Skip to main content

ring_native_ossl/
lib.rs

1//! OpenSSL-backed implementation of a `ring`-compatible cryptography API.
2//!
3//! This crate mirrors the public surface of several `ring` modules so that
4//! code written against `ring` can be compiled against OpenSSL instead, without
5//! pulling in `ring` itself.  All cryptographic operations are delegated to
6//! OpenSSL through the `native-ossl` crate.
7//!
8//! # Modules mirrored
9//!
10//! | Module | `ring` counterpart |
11//! |---|---|
12//! | [`aead`] | `ring::aead` |
13//! | [`agreement`] | `ring::agreement` |
14//! | [`digest`] | `ring::digest` |
15//! | [`error`] | `ring::error` |
16//! | [`hkdf`] | `ring::hkdf` |
17//! | [`hmac`] | `ring::hmac` |
18//! | [`rand`] | `ring::rand` |
19//! | [`signature`] | `ring::signature` |
20//!
21//! The internal `spki` module is not public; it holds the shared
22//! `SubjectPublicKeyInfo` DER header constants used by `agreement` and `signature`.
23//!
24//! # What is not included
25//!
26//! This crate does not reproduce `ring`-internal sealed-trait hierarchies.  The
27//! [`rand::SecureRandom`] trait is defined in this crate and is used as a bound
28//! in [`agreement`] and [`signature`]; callers should use it in place of
29//! `ring::rand::SecureRandom`.
30//!
31//! RSA key generation is not implemented; RSA keys can be loaded from PKCS#8
32//! or PKCS#1 DER through the [`signature`] types.
33//!
34//! # Example
35//!
36//! ```rust
37//! use ring_native_ossl::{digest, hmac, rand, agreement};
38//!
39//! // One-shot digest
40//! let hash = digest::digest(&digest::SHA256, b"hello world");
41//! assert_eq!(hash.as_ref().len(), 32);
42//!
43//! // HMAC sign and verify
44//! let key = hmac::Key::new(hmac::HMAC_SHA256, b"my-key");
45//! let tag = hmac::sign(&key, b"data");
46//! hmac::verify(&key, b"data", tag.as_ref()).unwrap();
47//!
48//! // X25519 ephemeral key agreement
49//! let rng = rand::SystemRandom::new();
50//! let alice = agreement::EphemeralPrivateKey::generate(&agreement::X25519, &rng).unwrap();
51//! let alice_pub = alice.compute_public_key().unwrap();
52//! ```
53
54pub mod aead;
55pub mod agreement;
56pub mod digest;
57pub mod error;
58pub mod hkdf;
59pub mod hmac;
60pub mod rand;
61pub mod signature;
62pub(crate) mod spki;
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67
68    #[test]
69    fn sha256_digest_known_vector() {
70        // SHA-256("") = e3b0c44298fc1c149afb...
71        let d = digest::digest(&digest::SHA256, b"");
72        assert_eq!(d.as_ref().len(), 32);
73        assert_eq!(
74            d.as_ref(),
75            &[
76                0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f,
77                0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b,
78                0x78, 0x52, 0xb8, 0x55,
79            ]
80        );
81    }
82
83    #[test]
84    fn sha256_digest_incremental() {
85        let mut ctx = digest::Context::new(&digest::SHA256);
86        ctx.update(b"abc");
87        let d = ctx.finish();
88        // SHA-256("abc")
89        assert_eq!(
90            d.as_ref(),
91            &[
92                0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae,
93                0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61,
94                0xf2, 0x00, 0x15, 0xad,
95            ]
96        );
97    }
98
99    #[test]
100    fn hmac_sha256_sign_verify_roundtrip() {
101        let key = hmac::Key::new(hmac::HMAC_SHA256, b"my-secret-key");
102        let tag = hmac::sign(&key, b"hello world");
103        let result = hmac::verify(&key, b"hello world", tag.as_ref());
104        assert!(result.is_ok());
105    }
106
107    #[test]
108    fn hmac_sha256_wrong_data_rejected() {
109        let key = hmac::Key::new(hmac::HMAC_SHA256, b"my-secret-key");
110        let tag = hmac::sign(&key, b"hello world");
111        let result = hmac::verify(&key, b"hello WORLD", tag.as_ref());
112        assert!(result.is_err());
113    }
114
115    #[test]
116    fn hkdf_sha256_expand_known_length() {
117        struct Len32;
118        impl hkdf::KeyType for Len32 {
119            fn len(&self) -> usize {
120                32
121            }
122        }
123        let salt = hkdf::Salt::new(hkdf::HKDF_SHA256, b"test-salt");
124        let prk = salt.extract(b"input-keying-material");
125        let mut out = [0u8; 32];
126        prk.expand(&[b"info"], Len32)
127            .unwrap()
128            .fill(&mut out)
129            .unwrap();
130        assert_ne!(out, [0u8; 32]);
131    }
132
133    #[test]
134    fn x25519_agreement_roundtrip() {
135        let rng = rand::SystemRandom::new();
136        let alice = agreement::EphemeralPrivateKey::generate(&agreement::X25519, &rng).unwrap();
137        let alice_pub = alice.compute_public_key().unwrap();
138
139        let bob = agreement::EphemeralPrivateKey::generate(&agreement::X25519, &rng).unwrap();
140        let bob_pub = bob.compute_public_key().unwrap();
141
142        let alice_secret = agreement::agree_ephemeral(
143            alice,
144            &agreement::UnparsedPublicKey::new(&agreement::X25519, bob_pub.as_ref()),
145            (),
146            |s| Ok::<Vec<u8>, ()>(s.to_vec()),
147        )
148        .unwrap();
149
150        let bob_secret = agreement::agree_ephemeral(
151            bob,
152            &agreement::UnparsedPublicKey::new(&agreement::X25519, alice_pub.as_ref()),
153            (),
154            |s| Ok::<Vec<u8>, ()>(s.to_vec()),
155        )
156        .unwrap();
157
158        assert_eq!(alice_secret, bob_secret);
159        assert!(!alice_secret.is_empty());
160    }
161
162    #[test]
163    fn aes_128_gcm_seal_open_roundtrip() {
164        let key_bytes = [0u8; 16];
165        let key = aead::UnboundKey::new(&aead::AES_128_GCM, &key_bytes).unwrap();
166        let lsk = aead::LessSafeKey::new(key);
167        let nonce = aead::Nonce::assume_unique_for_key([0u8; 12]);
168        let aad = aead::Aad::from(b"additional data".as_ref());
169
170        let plaintext = b"hello aead world!";
171        let mut in_out = plaintext.to_vec();
172        lsk.seal_in_place_append_tag(nonce, &aad, &mut in_out)
173            .unwrap();
174        assert!(in_out.len() > plaintext.len());
175
176        let nonce2 = aead::Nonce::assume_unique_for_key([0u8; 12]);
177        let aad2 = aead::Aad::from(b"additional data".as_ref());
178        let result = lsk.open_in_place(nonce2, &aad2, &mut in_out).unwrap();
179        assert_eq!(result, plaintext);
180    }
181
182    #[test]
183    fn ed25519_sign_verify() {
184        let keypair = signature::Ed25519KeyPair::generate().unwrap();
185        let msg = b"sign this message";
186        let sig = keypair.sign(msg).expect("Ed25519 sign failed");
187
188        let pub_key_bytes = keypair.public_key();
189        let result = signature::verify(&signature::ED25519, pub_key_bytes, msg, sig.as_ref());
190        assert!(result.is_ok(), "Ed25519 verification failed");
191    }
192
193    #[test]
194    fn aead_tampered_ciphertext_fails() {
195        let key = aead::UnboundKey::new(&aead::AES_128_GCM, &[0u8; 16]).unwrap();
196        let lsk = aead::LessSafeKey::new(key);
197        let mut in_out = b"secret".to_vec();
198        lsk.seal_in_place_append_tag(
199            aead::Nonce::assume_unique_for_key([0u8; 12]),
200            &aead::Aad::from(b"".as_ref()),
201            &mut in_out,
202        )
203        .unwrap();
204        in_out[0] ^= 0xff; // corrupt one ciphertext byte
205        assert!(lsk
206            .open_in_place(
207                aead::Nonce::assume_unique_for_key([0u8; 12]),
208                &aead::Aad::from(b"".as_ref()),
209                &mut in_out,
210            )
211            .is_err());
212    }
213
214    #[test]
215    fn ecdsa_wrong_key_verify_fails() {
216        let rng = rand::SystemRandom::new();
217        let kp1 = signature::EcdsaKeyPair::generate(&signature::ECDSA_P256_SHA256_ASN1).unwrap();
218        let kp2 = signature::EcdsaKeyPair::generate(&signature::ECDSA_P256_SHA256_ASN1).unwrap();
219        let msg = b"test message";
220        let sig = kp1.sign(&rng, msg).unwrap();
221        assert!(signature::verify(
222            &signature::ECDSA_P256_SHA256_ASN1,
223            kp2.public_key(),
224            msg,
225            sig.as_ref(),
226        )
227        .is_err());
228    }
229
230    #[test]
231    fn agreement_wrong_length_peer_key_fails() {
232        let rng = rand::SystemRandom::new();
233        let alice = agreement::EphemeralPrivateKey::generate(&agreement::X25519, &rng).unwrap();
234        let result = agreement::agree_ephemeral(
235            alice,
236            &agreement::UnparsedPublicKey::new(&agreement::X25519, &[0u8; 31]),
237            (),
238            |_| Ok::<(), ()>(()),
239        );
240        assert!(result.is_err());
241    }
242
243    #[test]
244    fn agreement_ec_compressed_point_rejected() {
245        let rng = rand::SystemRandom::new();
246        let alice = agreement::EphemeralPrivateKey::generate(&agreement::ECDH_P256, &rng).unwrap();
247        let mut bad = [0u8; 65];
248        bad[0] = 0x02; // compressed EC point, not uncompressed 0x04
249        let result = agreement::agree_ephemeral(
250            alice,
251            &agreement::UnparsedPublicKey::new(&agreement::ECDH_P256, &bad),
252            (),
253            |_| Ok::<(), ()>(()),
254        );
255        assert!(result.is_err());
256    }
257}