uselesskey-ring 0.8.0

ring 0.17 adapter traits for uselesskey RSA/ECDSA/Ed25519 fixtures.
Documentation
#![forbid(unsafe_code)]

//! Integration between uselesskey test fixtures and `ring` 0.17.
//!
//! This crate provides extension traits that convert uselesskey fixtures into
//! `ring` native signing key types, making it easy to use test fixtures with
//! code that depends on `ring` directly.
//!
//! # Features
//!
//! Enable the key types you need:
//!
//! - `rsa` - RSA keypairs -> `ring::rsa::KeyPair`
//! - `ecdsa` - ECDSA keypairs -> `ring::signature::EcdsaKeyPair`
//! - `ed25519` - Ed25519 keypairs -> `ring::signature::Ed25519KeyPair`
//! - `all` - All of the above
//!
//! # Examples
//!
//! Convert an RSA fixture to a `ring` key pair (requires `rsa` feature):
//!
#![cfg_attr(feature = "rsa", doc = "```")]
#![cfg_attr(not(feature = "rsa"), doc = "```ignore")]
//! use uselesskey_core::Factory;
//! use uselesskey_rsa::{RsaFactoryExt, RsaSpec};
//! use uselesskey_ring::RingRsaKeyPairExt;
//!
//! let fx = Factory::random();
//! let kp = fx.rsa("test", RsaSpec::rs256());
//! let ring_pair = kp.rsa_key_pair_ring();
//! assert!(ring_pair.public().modulus_len() > 0);
//! ```

// =========================================================================
// RSA
// =========================================================================

/// Extension trait to convert uselesskey RSA fixtures into `ring::rsa::KeyPair`.
#[cfg(feature = "rsa")]
pub trait RingRsaKeyPairExt {
    /// Convert the RSA private key to a `ring::rsa::KeyPair`.
    fn rsa_key_pair_ring(&self) -> ring::rsa::KeyPair;
}

#[cfg(feature = "rsa")]
impl RingRsaKeyPairExt for uselesskey_rsa::RsaKeyPair {
    fn rsa_key_pair_ring(&self) -> ring::rsa::KeyPair {
        ring::rsa::KeyPair::from_pkcs8(self.private_key_pkcs8_der()).expect("valid RSA PKCS#8 DER")
    }
}

// =========================================================================
// ECDSA
// =========================================================================

#[cfg(feature = "ecdsa")]
use ring::signature::{
    ECDSA_P256_SHA256_ASN1_SIGNING, ECDSA_P384_SHA384_ASN1_SIGNING,
    EcdsaKeyPair as RingEcdsaKeyPair, EcdsaSigningAlgorithm,
};

/// Extension trait to convert uselesskey ECDSA fixtures into `ring::signature::EcdsaKeyPair`.
#[cfg(feature = "ecdsa")]
pub trait RingEcdsaKeyPairExt {
    /// Convert the ECDSA private key to a `ring::signature::EcdsaKeyPair`.
    ///
    /// The correct signing algorithm is chosen based on the curve (P-256 -> ECDSA_P256_SHA256_ASN1_SIGNING,
    /// P-384 -> ECDSA_P384_SHA384_ASN1_SIGNING).
    fn ecdsa_key_pair_ring(&self) -> RingEcdsaKeyPair;
}

#[cfg(feature = "ecdsa")]
impl RingEcdsaKeyPairExt for uselesskey_ecdsa::EcdsaKeyPair {
    fn ecdsa_key_pair_ring(&self) -> RingEcdsaKeyPair {
        let alg: &'static EcdsaSigningAlgorithm = match self.spec() {
            uselesskey_ecdsa::EcdsaSpec::Es256 => &ECDSA_P256_SHA256_ASN1_SIGNING,
            uselesskey_ecdsa::EcdsaSpec::Es384 => &ECDSA_P384_SHA384_ASN1_SIGNING,
        };
        RingEcdsaKeyPair::from_pkcs8(
            alg,
            self.private_key_pkcs8_der(),
            &ring::rand::SystemRandom::new(),
        )
        .expect("valid ECDSA PKCS#8 DER")
    }
}

// =========================================================================
// Ed25519
// =========================================================================

/// Extension trait to convert uselesskey Ed25519 fixtures into `ring::signature::Ed25519KeyPair`.
#[cfg(feature = "ed25519")]
pub trait RingEd25519KeyPairExt {
    /// Convert the Ed25519 private key to a `ring::signature::Ed25519KeyPair`.
    fn ed25519_key_pair_ring(&self) -> ring::signature::Ed25519KeyPair;
}

#[cfg(feature = "ed25519")]
impl RingEd25519KeyPairExt for uselesskey_ed25519::Ed25519KeyPair {
    fn ed25519_key_pair_ring(&self) -> ring::signature::Ed25519KeyPair {
        ring::signature::Ed25519KeyPair::from_pkcs8_maybe_unchecked(self.private_key_pkcs8_der())
            .expect("valid Ed25519 PKCS#8 DER")
    }
}

// =========================================================================
// Tests
// =========================================================================

#[cfg(test)]
mod tests {
    use std::sync::OnceLock;
    use uselesskey_core::{Factory, Seed};

    static FX: OnceLock<Factory> = OnceLock::new();

    fn fx() -> Factory {
        FX.get_or_init(|| {
            let seed = Seed::from_env_value("uselesskey-ring-inline-test-seed-v1")
                .expect("test seed should always parse");
            Factory::deterministic(seed)
        })
        .clone()
    }

    #[cfg(feature = "rsa")]
    mod rsa_tests {
        use crate::RingRsaKeyPairExt;
        use ring::signature;
        use uselesskey_rsa::{RsaFactoryExt, RsaSpec};

        #[test]
        fn test_rsa_sign_verify() {
            let fx = super::fx();
            let rsa = fx.rsa("test", RsaSpec::rs256());
            let ring_kp = rsa.rsa_key_pair_ring();

            let msg = b"test message";
            let rng = ring::rand::SystemRandom::new();
            let mut sig = vec![0u8; ring_kp.public().modulus_len()];
            ring_kp
                .sign(&signature::RSA_PKCS1_SHA256, &rng, msg, &mut sig)
                .expect("sign");

            let public_key_bytes = ring_kp.public().as_ref();
            let public_key = signature::UnparsedPublicKey::new(
                &signature::RSA_PKCS1_2048_8192_SHA256,
                public_key_bytes,
            );
            public_key.verify(msg, &sig).expect("verify");
        }
    }

    #[cfg(feature = "rsa")]
    mod rsa_deterministic_tests {
        use crate::RingRsaKeyPairExt;
        use ring::signature;
        use uselesskey_core::{Factory, Seed};
        use uselesskey_rsa::{RsaFactoryExt, RsaSpec};

        #[test]
        fn test_rsa_deterministic_sign_verify() {
            let seed = Seed::from_env_value("test-seed").unwrap();
            let fx = Factory::deterministic(seed);
            let rsa = fx.rsa("det-test", RsaSpec::rs256());
            let ring_kp = rsa.rsa_key_pair_ring();

            let msg = b"deterministic message";
            let rng = ring::rand::SystemRandom::new();
            let mut sig = vec![0u8; ring_kp.public().modulus_len()];
            ring_kp
                .sign(&signature::RSA_PKCS1_SHA256, &rng, msg, &mut sig)
                .expect("sign");

            let public_key_bytes = ring_kp.public().as_ref();
            let public_key = signature::UnparsedPublicKey::new(
                &signature::RSA_PKCS1_2048_8192_SHA256,
                public_key_bytes,
            );
            public_key.verify(msg, &sig).expect("verify");
        }
    }

    #[cfg(feature = "ecdsa")]
    mod ecdsa_tests {
        use crate::RingEcdsaKeyPairExt;
        use ring::signature::{self, KeyPair};
        use uselesskey_core::Factory;
        use uselesskey_ecdsa::{EcdsaFactoryExt, EcdsaSpec};

        #[test]
        fn test_ecdsa_p256_sign_verify() {
            let fx = Factory::random();
            let ecdsa = fx.ecdsa("test", EcdsaSpec::es256());
            let ring_kp = ecdsa.ecdsa_key_pair_ring();

            let msg = b"test message";
            let rng = ring::rand::SystemRandom::new();
            let sig = ring_kp.sign(&rng, msg).expect("sign");

            let public_key_bytes = ring_kp.public_key().as_ref();
            let public_key = signature::UnparsedPublicKey::new(
                &signature::ECDSA_P256_SHA256_ASN1,
                public_key_bytes,
            );
            public_key.verify(msg, sig.as_ref()).expect("verify");
        }

        #[test]
        fn test_ecdsa_p384_sign_verify() {
            let fx = Factory::random();
            let ecdsa = fx.ecdsa("test", EcdsaSpec::es384());
            let ring_kp = ecdsa.ecdsa_key_pair_ring();

            let msg = b"test message";
            let rng = ring::rand::SystemRandom::new();
            let sig = ring_kp.sign(&rng, msg).expect("sign");

            let public_key_bytes = ring_kp.public_key().as_ref();
            let public_key = signature::UnparsedPublicKey::new(
                &signature::ECDSA_P384_SHA384_ASN1,
                public_key_bytes,
            );
            public_key.verify(msg, sig.as_ref()).expect("verify");
        }
    }

    #[cfg(feature = "ed25519")]
    mod ed25519_tests {
        use crate::RingEd25519KeyPairExt;
        use ring::signature::{self, KeyPair};
        use uselesskey_core::Factory;
        use uselesskey_ed25519::{Ed25519FactoryExt, Ed25519Spec};

        #[test]
        fn test_ed25519_sign_verify() {
            let fx = Factory::random();
            let ed = fx.ed25519("test", Ed25519Spec::new());
            let ring_kp = ed.ed25519_key_pair_ring();

            let msg = b"test message";
            let sig = ring_kp.sign(msg);

            let public_key_bytes = ring_kp.public_key().as_ref();
            let public_key =
                signature::UnparsedPublicKey::new(&signature::ED25519, public_key_bytes);
            public_key.verify(msg, sig.as_ref()).expect("verify");
        }
    }
}