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