Skip to main content

rtc_crypto/providers/
ring.rs

1use crate::common;
2use crate::common::check_tag_len;
3use crate::{
4    ActiveKeyExchange, AeadAlgorithm, AeadCipher, BlockCipherAlgorithm, CbcAlgorithm, CbcCipher,
5    CryptoAlgorithm, CryptoError, HashAlgorithm, HmacAlgorithm, KeyExchangeAlgorithm, Mac,
6    PublicKey, PublicKeyEncoding, RTCCrypto, RTCCryptoProvider, RTCRandom, SecretVec,
7    SignatureScheme, SigningKey, StreamCipher, StreamCipherAlgorithm, constant_time_eq,
8};
9use ::hmac::Mac as RustCryptoMac;
10use ring::aead;
11use ring::agreement;
12use ring::digest;
13use ring::hmac;
14use ring::rand::SystemRandom;
15use ring::signature::{self, KeyPair};
16use sha1::Sha1;
17use std::sync::Arc;
18
19/// The built-in Ring provider bundle.
20#[derive(Default)]
21pub struct RingProvider {
22    crypto: RingCrypto,
23    random: RingRandom,
24}
25
26impl RingProvider {
27    /// Creates a Ring provider.
28    #[must_use]
29    pub const fn new() -> Self {
30        Self {
31            crypto: RingCrypto,
32            random: RingRandom,
33        }
34    }
35}
36
37impl RTCCryptoProvider for RingProvider {
38    fn name(&self) -> &'static str {
39        "ring"
40    }
41
42    fn crypto(&self) -> &dyn RTCCrypto {
43        &self.crypto
44    }
45
46    fn random(&self) -> &dyn RTCRandom {
47        &self.random
48    }
49}
50
51/// Ring-backed cryptographic operations, with documented RustCrypto fallbacks for primitives Ring
52/// does not expose (MD5, AES block/CTR/CBC, and AES-CCM).
53#[derive(Default)]
54pub struct RingCrypto;
55
56/// Ring's operating-system-backed secure random source.
57#[derive(Default)]
58pub struct RingRandom;
59
60impl RTCRandom for RingRandom {
61    fn fill(&self, output: &mut [u8]) -> Result<(), CryptoError> {
62        common::fill_random(output)
63    }
64}
65
66impl RTCCrypto for RingCrypto {
67    fn supports(&self, algorithm: CryptoAlgorithm) -> bool {
68        matches!(
69            algorithm,
70            CryptoAlgorithm::Hash(HashAlgorithm::Md5 | HashAlgorithm::Sha256)
71                | CryptoAlgorithm::Hmac(HmacAlgorithm::Sha1 | HmacAlgorithm::Sha256)
72                | CryptoAlgorithm::Aead(
73                    AeadAlgorithm::Aes128Gcm
74                        | AeadAlgorithm::Aes256Gcm
75                        | AeadAlgorithm::Aes128Ccm
76                        | AeadAlgorithm::Aes128Ccm8
77                        | AeadAlgorithm::ChaCha20Poly1305,
78                )
79                | CryptoAlgorithm::StreamCipher(
80                    StreamCipherAlgorithm::Aes128Ctr | StreamCipherAlgorithm::Aes256Ctr,
81                )
82                | CryptoAlgorithm::BlockCipher(
83                    BlockCipherAlgorithm::Aes128 | BlockCipherAlgorithm::Aes256,
84                )
85                | CryptoAlgorithm::Cbc(CbcAlgorithm::Aes256Cbc)
86                | CryptoAlgorithm::KeyExchange(
87                    KeyExchangeAlgorithm::P256
88                        | KeyExchangeAlgorithm::P384
89                        | KeyExchangeAlgorithm::X25519,
90                )
91                | CryptoAlgorithm::Signature(
92                    SignatureScheme::Ed25519
93                        | SignatureScheme::EcdsaP256Sha256
94                        | SignatureScheme::EcdsaP384Sha384
95                        | SignatureScheme::RsaPkcs1Sha1
96                        | SignatureScheme::RsaPkcs1Sha256
97                        | SignatureScheme::RsaPkcs1Sha384
98                        | SignatureScheme::RsaPkcs1Sha512,
99                )
100                | CryptoAlgorithm::SigningKeyGeneration(
101                    SignatureScheme::Ed25519 | SignatureScheme::EcdsaP256Sha256,
102                )
103                | CryptoAlgorithm::SigningKeyImport(
104                    SignatureScheme::Ed25519
105                        | SignatureScheme::EcdsaP256Sha256
106                        | SignatureScheme::RsaPkcs1Sha256,
107                )
108        )
109    }
110
111    fn hash(&self, algorithm: HashAlgorithm, data: &[u8]) -> Result<Vec<u8>, CryptoError> {
112        match algorithm {
113            HashAlgorithm::Md5 => Ok(common::md5(data)),
114            HashAlgorithm::Sha256 => Ok(digest::digest(&digest::SHA256, data).as_ref().to_vec()),
115        }
116    }
117
118    fn new_hmac(&self, algorithm: HmacAlgorithm, key: &[u8]) -> Result<Box<dyn Mac>, CryptoError> {
119        match algorithm {
120            // ring's SHA-1 is a software implementation and measures 3.3x slower than
121            // RustCrypto's; see common::RustCryptoHmacSha1. SHA-256 stays on ring, which uses
122            // the hardware instructions.
123            HmacAlgorithm::Sha1 => Ok(Box::new(RustCryptoHmacSha1::new(key))),
124            HmacAlgorithm::Sha256 => Ok(Box::new(RingHmac {
125                key: hmac::Key::new(hmac_algorithm(algorithm), key),
126                output_len: algorithm.output_len(),
127            })),
128        }
129    }
130
131    fn block_encrypt(
132        &self,
133        algorithm: BlockCipherAlgorithm,
134        key: &[u8],
135        block: &mut [u8],
136    ) -> Result<(), CryptoError> {
137        common::block_encrypt(algorithm, key, block)
138    }
139
140    fn new_stream_cipher(
141        &self,
142        algorithm: StreamCipherAlgorithm,
143        key: &[u8],
144    ) -> Result<Box<dyn StreamCipher>, CryptoError> {
145        common::new_stream_cipher(algorithm, key)
146    }
147
148    fn new_aead(
149        &self,
150        algorithm: AeadAlgorithm,
151        key: &[u8],
152    ) -> Result<Box<dyn AeadCipher>, CryptoError> {
153        match algorithm {
154            AeadAlgorithm::Aes128Ccm | AeadAlgorithm::Aes128Ccm8 => common::new_ccm(algorithm, key),
155            AeadAlgorithm::Aes128Gcm => RingAead::create(&aead::AES_128_GCM, key),
156            AeadAlgorithm::Aes256Gcm => RingAead::create(&aead::AES_256_GCM, key),
157            AeadAlgorithm::ChaCha20Poly1305 => RingAead::create(&aead::CHACHA20_POLY1305, key),
158        }
159    }
160
161    fn new_cbc(
162        &self,
163        algorithm: CbcAlgorithm,
164        key: &[u8],
165    ) -> Result<Box<dyn CbcCipher>, CryptoError> {
166        common::new_cbc(algorithm, key)
167    }
168
169    fn start_key_exchange(
170        &self,
171        algorithm: KeyExchangeAlgorithm,
172    ) -> Result<Box<dyn ActiveKeyExchange>, CryptoError> {
173        RingKeyExchange::start(algorithm)
174    }
175
176    fn generate_signing_key(
177        &self,
178        scheme: SignatureScheme,
179    ) -> Result<Arc<dyn SigningKey>, CryptoError> {
180        RingSigningKey::generate(scheme)
181    }
182
183    fn import_signing_key(
184        &self,
185        scheme: SignatureScheme,
186        pkcs8_der: &[u8],
187    ) -> Result<Arc<dyn SigningKey>, CryptoError> {
188        RingSigningKey::import(scheme, pkcs8_der)
189    }
190
191    fn verify_signature(
192        &self,
193        scheme: SignatureScheme,
194        public_key: PublicKey<'_>,
195        message: &[u8],
196        signature: &[u8],
197    ) -> Result<(), CryptoError> {
198        verify_public_key_encoding(scheme, public_key.encoding)?;
199        signature::UnparsedPublicKey::new(verification_algorithm(scheme), public_key.bytes)
200            .verify(message, signature)
201            .map_err(|_| CryptoError::InvalidSignature)
202    }
203}
204
205/// A keyed HMAC holding a `ring::hmac::Key`.
206///
207/// `Key::new` performs the ipad/opad derivation; `Context::with_key` only clones the resulting
208/// state. Keeping the key here is what moves that derivation off the per-packet path.
209struct RingHmac {
210    key: hmac::Key,
211    output_len: usize,
212}
213
214impl Mac for RingHmac {
215    fn output_len(&self) -> usize {
216        self.output_len
217    }
218
219    fn sign(&mut self, input: &[&[u8]], output: &mut [u8]) -> Result<(), CryptoError> {
220        common::check_tag_len(self.output_len, output.len())?;
221        let mut context = hmac::Context::with_key(&self.key);
222        for part in input {
223            context.update(part);
224        }
225        output.copy_from_slice(context.sign().as_ref());
226        Ok(())
227    }
228
229    fn verify(&mut self, input: &[&[u8]], expected: &[u8]) -> Result<(), CryptoError> {
230        common::check_tag_len(self.output_len, expected.len())?;
231        let mut actual = vec![0; self.output_len];
232        self.sign(input, &mut actual)?;
233        if constant_time_eq(&actual, expected) {
234            Ok(())
235        } else {
236            Err(CryptoError::AuthenticationFailed)
237        }
238    }
239}
240
241fn hmac_algorithm(algorithm: HmacAlgorithm) -> hmac::Algorithm {
242    match algorithm {
243        HmacAlgorithm::Sha1 => hmac::HMAC_SHA1_FOR_LEGACY_USE_ONLY,
244        HmacAlgorithm::Sha256 => hmac::HMAC_SHA256,
245    }
246}
247
248struct RingAead {
249    key: aead::LessSafeKey,
250}
251
252impl RingAead {
253    fn create(
254        algorithm: &'static aead::Algorithm,
255        key: &[u8],
256    ) -> Result<Box<dyn AeadCipher>, CryptoError> {
257        common::check_key_len(algorithm.key_len(), key.len())?;
258        let key = aead::UnboundKey::new(algorithm, key)
259            .map(aead::LessSafeKey::new)
260            .map_err(|_| CryptoError::InvalidPrivateKey)?;
261        Ok(Box::new(Self { key }))
262    }
263}
264
265impl AeadCipher for RingAead {
266    fn tag_len(&self) -> usize {
267        16
268    }
269
270    fn seal_in_place(
271        &mut self,
272        nonce: &[u8],
273        aad: &[u8],
274        plaintext_and_ciphertext: &mut [u8],
275        tag_out: &mut [u8],
276    ) -> Result<(), CryptoError> {
277        common::check_nonce_len(12, nonce.len())?;
278        common::check_tag_len(self.tag_len(), tag_out.len())?;
279        let nonce = aead::Nonce::try_assume_unique_for_key(nonce).map_err(|_| {
280            CryptoError::InvalidNonceLength {
281                expected: 12,
282                actual: nonce.len(),
283            }
284        })?;
285        let tag = self
286            .key
287            .seal_in_place_separate_tag(nonce, aead::Aad::from(aad), plaintext_and_ciphertext)
288            .map_err(|_| CryptoError::AuthenticationFailed)?;
289        tag_out.copy_from_slice(tag.as_ref());
290        Ok(())
291    }
292
293    fn open_in_place(
294        &mut self,
295        nonce: &[u8],
296        aad: &[u8],
297        ciphertext_and_plaintext: &mut [u8],
298        tag: &[u8],
299    ) -> Result<(), CryptoError> {
300        common::check_nonce_len(12, nonce.len())?;
301        common::check_tag_len(self.tag_len(), tag.len())?;
302        let nonce = aead::Nonce::try_assume_unique_for_key(nonce).map_err(|_| {
303            CryptoError::InvalidNonceLength {
304                expected: 12,
305                actual: nonce.len(),
306            }
307        })?;
308        let tag = aead::Tag::try_from(tag).map_err(|_| CryptoError::InvalidTagLength {
309            expected: self.tag_len(),
310            actual: tag.len(),
311        })?;
312        self.key
313            .open_in_place_separate_tag(
314                nonce,
315                aead::Aad::from(aad),
316                tag,
317                ciphertext_and_plaintext,
318                0..,
319            )
320            .map(|_| ())
321            .map_err(|_| CryptoError::AuthenticationFailed)
322    }
323}
324
325struct RingKeyExchange {
326    algorithm: KeyExchangeAlgorithm,
327    backend_algorithm: &'static agreement::Algorithm,
328    private_key: agreement::EphemeralPrivateKey,
329    public_key: Vec<u8>,
330}
331
332impl RingKeyExchange {
333    fn start(algorithm: KeyExchangeAlgorithm) -> Result<Box<dyn ActiveKeyExchange>, CryptoError> {
334        let backend_algorithm = agreement_algorithm(algorithm);
335        let private_key =
336            agreement::EphemeralPrivateKey::generate(backend_algorithm, &SystemRandom::new())
337                .map_err(|_| CryptoError::RandomnessFailed)?;
338        let public_key = private_key
339            .compute_public_key()
340            .map_err(|_| CryptoError::Provider("key exchange public-key generation failed".into()))?
341            .as_ref()
342            .to_vec();
343        Ok(Box::new(Self {
344            algorithm,
345            backend_algorithm,
346            private_key,
347            public_key,
348        }))
349    }
350}
351
352impl ActiveKeyExchange for RingKeyExchange {
353    fn algorithm(&self) -> KeyExchangeAlgorithm {
354        self.algorithm
355    }
356
357    fn public_key(&self) -> &[u8] {
358        &self.public_key
359    }
360
361    fn complete(self: Box<Self>, peer_public_key: &[u8]) -> Result<SecretVec, CryptoError> {
362        let peer = agreement::UnparsedPublicKey::new(self.backend_algorithm, peer_public_key);
363        agreement::agree_ephemeral(self.private_key, &peer, |secret| {
364            SecretVec::new(secret.to_vec())
365        })
366        .map_err(|_| CryptoError::InvalidPublicKey)
367    }
368}
369
370enum RingSigningKeyKind {
371    Ed25519(signature::Ed25519KeyPair),
372    EcdsaP256(signature::EcdsaKeyPair),
373    Rsa(signature::RsaKeyPair),
374}
375
376struct RingSigningKey {
377    scheme: SignatureScheme,
378    kind: RingSigningKeyKind,
379    public_key: Vec<u8>,
380    public_key_encoding: PublicKeyEncoding,
381    pkcs8_der: SecretVec,
382}
383
384impl RingSigningKey {
385    fn generate(scheme: SignatureScheme) -> Result<Arc<dyn SigningKey>, CryptoError> {
386        let rng = SystemRandom::new();
387        let pkcs8 = match scheme {
388            SignatureScheme::Ed25519 => signature::Ed25519KeyPair::generate_pkcs8(&rng),
389            SignatureScheme::EcdsaP256Sha256 => signature::EcdsaKeyPair::generate_pkcs8(
390                &signature::ECDSA_P256_SHA256_ASN1_SIGNING,
391                &rng,
392            ),
393            _ => {
394                return Err(CryptoError::UnsupportedAlgorithm(
395                    CryptoAlgorithm::SigningKeyGeneration(scheme),
396                ));
397            }
398        }
399        .map_err(|_| CryptoError::RandomnessFailed)?;
400        Self::import(scheme, pkcs8.as_ref())
401    }
402
403    fn import(
404        scheme: SignatureScheme,
405        pkcs8_der: &[u8],
406    ) -> Result<Arc<dyn SigningKey>, CryptoError> {
407        let rng = SystemRandom::new();
408        let (kind, public_key, public_key_encoding) = match scheme {
409            SignatureScheme::Ed25519 => {
410                let key = signature::Ed25519KeyPair::from_pkcs8_maybe_unchecked(pkcs8_der)
411                    .map_err(|_| CryptoError::InvalidPrivateKey)?;
412                let public = key.public_key().as_ref().to_vec();
413                (
414                    RingSigningKeyKind::Ed25519(key),
415                    public,
416                    PublicKeyEncoding::Ed25519Raw,
417                )
418            }
419            SignatureScheme::EcdsaP256Sha256 => {
420                let key = signature::EcdsaKeyPair::from_pkcs8(
421                    &signature::ECDSA_P256_SHA256_ASN1_SIGNING,
422                    pkcs8_der,
423                    &rng,
424                )
425                .map_err(|_| CryptoError::InvalidPrivateKey)?;
426                let public = key.public_key().as_ref().to_vec();
427                (
428                    RingSigningKeyKind::EcdsaP256(key),
429                    public,
430                    PublicKeyEncoding::EcUncompressedPoint,
431                )
432            }
433            SignatureScheme::RsaPkcs1Sha256 => {
434                let key = signature::RsaKeyPair::from_pkcs8(pkcs8_der)
435                    .map_err(|_| CryptoError::InvalidPrivateKey)?;
436                let public = key.public().as_ref().to_vec();
437                (
438                    RingSigningKeyKind::Rsa(key),
439                    public,
440                    PublicKeyEncoding::RsaPkcs1Der,
441                )
442            }
443            _ => {
444                return Err(CryptoError::UnsupportedAlgorithm(
445                    CryptoAlgorithm::SigningKeyImport(scheme),
446                ));
447            }
448        };
449        Ok(Arc::new(Self {
450            scheme,
451            kind,
452            public_key,
453            public_key_encoding,
454            pkcs8_der: SecretVec::new(pkcs8_der.to_vec()),
455        }))
456    }
457}
458
459impl SigningKey for RingSigningKey {
460    fn supports(&self, scheme: SignatureScheme) -> bool {
461        self.scheme == scheme
462    }
463
464    fn public_key(&self) -> PublicKey<'_> {
465        PublicKey {
466            encoding: self.public_key_encoding,
467            bytes: &self.public_key,
468        }
469    }
470
471    fn sign(&self, scheme: SignatureScheme, message: &[u8]) -> Result<Vec<u8>, CryptoError> {
472        if !self.supports(scheme) {
473            return Err(CryptoError::UnsupportedAlgorithm(
474                CryptoAlgorithm::Signature(scheme),
475            ));
476        }
477        match &self.kind {
478            RingSigningKeyKind::Ed25519(key) => Ok(key.sign(message).as_ref().to_vec()),
479            RingSigningKeyKind::EcdsaP256(key) => key
480                .sign(&SystemRandom::new(), message)
481                .map(|signature| signature.as_ref().to_vec())
482                .map_err(|_| CryptoError::Provider("signature generation failed".into())),
483            RingSigningKeyKind::Rsa(key) => {
484                let mut signature = vec![0; key.public().modulus_len()];
485                key.sign(
486                    &signature::RSA_PKCS1_SHA256,
487                    &SystemRandom::new(),
488                    message,
489                    &mut signature,
490                )
491                .map_err(|_| CryptoError::Provider("signature generation failed".into()))?;
492                Ok(signature)
493            }
494        }
495    }
496
497    fn to_pkcs8_der(&self) -> Result<Option<SecretVec>, CryptoError> {
498        Ok(Some(self.pkcs8_der.clone()))
499    }
500}
501
502fn agreement_algorithm(algorithm: KeyExchangeAlgorithm) -> &'static agreement::Algorithm {
503    match algorithm {
504        KeyExchangeAlgorithm::P256 => &agreement::ECDH_P256,
505        KeyExchangeAlgorithm::P384 => &agreement::ECDH_P384,
506        KeyExchangeAlgorithm::X25519 => &agreement::X25519,
507    }
508}
509
510fn verification_algorithm(
511    scheme: SignatureScheme,
512) -> &'static dyn signature::VerificationAlgorithm {
513    match scheme {
514        SignatureScheme::Ed25519 => &signature::ED25519,
515        SignatureScheme::EcdsaP256Sha256 => &signature::ECDSA_P256_SHA256_ASN1,
516        SignatureScheme::EcdsaP384Sha384 => &signature::ECDSA_P384_SHA384_ASN1,
517        SignatureScheme::RsaPkcs1Sha1 => &signature::RSA_PKCS1_1024_8192_SHA1_FOR_LEGACY_USE_ONLY,
518        SignatureScheme::RsaPkcs1Sha256 => {
519            &signature::RSA_PKCS1_1024_8192_SHA256_FOR_LEGACY_USE_ONLY
520        }
521        SignatureScheme::RsaPkcs1Sha384 => &signature::RSA_PKCS1_2048_8192_SHA384,
522        SignatureScheme::RsaPkcs1Sha512 => {
523            &signature::RSA_PKCS1_1024_8192_SHA512_FOR_LEGACY_USE_ONLY
524        }
525    }
526}
527
528fn verify_public_key_encoding(
529    scheme: SignatureScheme,
530    encoding: PublicKeyEncoding,
531) -> Result<(), CryptoError> {
532    let valid = matches!(
533        (scheme, encoding),
534        (SignatureScheme::Ed25519, PublicKeyEncoding::Ed25519Raw)
535            | (
536                SignatureScheme::EcdsaP256Sha256 | SignatureScheme::EcdsaP384Sha384,
537                PublicKeyEncoding::EcUncompressedPoint
538            )
539            | (
540                SignatureScheme::RsaPkcs1Sha1
541                    | SignatureScheme::RsaPkcs1Sha256
542                    | SignatureScheme::RsaPkcs1Sha384
543                    | SignatureScheme::RsaPkcs1Sha512,
544                PublicKeyEncoding::RsaPkcs1Der
545            )
546    );
547    if valid {
548        Ok(())
549    } else {
550        Err(CryptoError::InvalidPublicKey)
551    }
552}
553
554type HmacSha1 = ::hmac::Hmac<Sha1>;
555
556/// HMAC-SHA1 backed by RustCrypto, keyed once.
557///
558/// `ring` exposes SHA-1 only as `HMAC_SHA1_FOR_LEGACY_USE_ONLY` and does not use the ARMv8 SHA-1
559/// instructions, measuring 4469 ns against RustCrypto's 1373 ns over a 1212-byte SRTP packet —
560/// 3.3x, and the whole of the SRTP AES-CM/HMAC regression. The built-in providers are already
561/// composite (AES-CTR, CCM, CBC and MD5 come from RustCrypto too), so HMAC-SHA1 is composed the
562/// same way rather than making the slower backend the default. `aws-lc-rs` has fast SHA-1 and
563/// keeps its own.
564pub(crate) struct RustCryptoHmacSha1 {
565    keyed: HmacSha1,
566}
567
568impl RustCryptoHmacSha1 {
569    pub(crate) fn new(key: &[u8]) -> Self {
570        Self {
571            // HMAC accepts any key length: it hashes longer keys and zero-pads shorter ones.
572            keyed: <HmacSha1 as RustCryptoMac>::new_from_slice(key)
573                .expect("HMAC accepts keys of any length"),
574        }
575    }
576}
577
578impl Mac for RustCryptoHmacSha1 {
579    fn output_len(&self) -> usize {
580        20
581    }
582
583    fn sign(&mut self, input: &[&[u8]], output: &mut [u8]) -> Result<(), CryptoError> {
584        check_tag_len(20, output.len())?;
585        let mut mac = self.keyed.clone();
586        for part in input {
587            mac.update(part);
588        }
589        output.copy_from_slice(&mac.finalize().into_bytes());
590        Ok(())
591    }
592
593    fn verify(&mut self, input: &[&[u8]], expected: &[u8]) -> Result<(), CryptoError> {
594        check_tag_len(20, expected.len())?;
595        let mut actual = [0u8; 20];
596        self.sign(input, &mut actual)?;
597        if crate::constant_time_eq(&actual, expected) {
598            Ok(())
599        } else {
600            Err(CryptoError::AuthenticationFailed)
601        }
602    }
603}