Skip to main content

rtc_dtls/crypto/
mod.rs

1//! Cryptographic primitives for DTLS.
2//!
3//! The record ciphers ([`crypto_gcm`](crate::crypto::crypto_gcm), [`crypto_ccm`](crate::crypto::crypto_ccm), [`crypto_chacha20`](crate::crypto::crypto_chacha20), [`crypto_cbc`](crate::crypto::crypto_cbc)), plus
4//! the certificate and signature handling the handshake needs. Which cipher is used is decided by
5//! the negotiated cipher suite, so a caller normally reaches these only through
6//! [`CipherSuite`](crate::cipher_suite::CipherSuite).
7//!
8//! Certificates here are usually self-signed: WebRTC authenticates a peer by comparing the
9//! certificate fingerprint against the one signalled in SDP, not by validating a CA chain.
10#[cfg(test)]
11mod crypto_test;
12
13/// AES-CBC with a separate HMAC, for the older CBC suites.
14pub mod crypto_cbc;
15/// AES-CCM authenticated encryption.
16pub mod crypto_ccm;
17/// ChaCha20-Poly1305 authenticated encryption.
18pub mod crypto_chacha20;
19/// AES-GCM authenticated encryption.
20pub mod crypto_gcm;
21/// Block-cipher padding for the CBC suites.
22pub mod padding;
23
24use std::convert::TryFrom;
25use std::sync::Arc;
26
27use der_parser::oid;
28use der_parser::oid::Oid;
29
30use rustls::client::danger::ServerCertVerifier;
31use rustls::pki_types::{CertificateDer, ServerName};
32use rustls::server::danger::ClientCertVerifier;
33
34use rcgen::{CertifiedKey, KeyPair, generate_simple_self_signed};
35use ring::rand::SystemRandom;
36use ring::signature::{EcdsaKeyPair, Ed25519KeyPair};
37
38use crate::curve::named_curve::*;
39use crate::record_layer::record_layer_header::*;
40use crate::signature_hash_algorithm::{HashAlgorithm, SignatureAlgorithm, SignatureHashAlgorithm};
41use shared::error::*;
42
43/// A X.509 certificate(s) used to authenticate a DTLS connection.
44#[derive(Clone, PartialEq, Debug)]
45pub struct Certificate {
46    /// DER-encoded certificates.
47    pub certificate: Vec<CertificateDer<'static>>,
48    /// Private key.
49    pub private_key: CryptoPrivateKey,
50}
51
52impl Certificate {
53    /// Generate a self-signed certificate.
54    ///
55    /// See [`rcgen::generate_simple_self_signed`].
56    pub fn generate_self_signed(subject_alt_names: impl Into<Vec<String>>) -> Result<Self> {
57        let CertifiedKey { cert, signing_key } = generate_simple_self_signed(subject_alt_names)?;
58        Ok(Certificate {
59            certificate: vec![cert.der().to_owned()],
60            private_key: CryptoPrivateKey::try_from(&signing_key)?,
61        })
62    }
63
64    /// Generate a self-signed certificate with the given algorithm.
65    ///
66    /// See `rcgen::Certificate::self_signed`.
67    pub fn generate_self_signed_with_alg(
68        subject_alt_names: impl Into<Vec<String>>,
69        alg: &'static rcgen::SignatureAlgorithm,
70    ) -> Result<Self> {
71        let params = rcgen::CertificateParams::new(subject_alt_names).unwrap();
72        let key_pair = rcgen::KeyPair::generate_for(alg).unwrap();
73        let cert = params.self_signed(&key_pair).unwrap();
74
75        Ok(Certificate {
76            certificate: vec![cert.der().to_owned()],
77            private_key: CryptoPrivateKey::try_from(&key_pair)?,
78        })
79    }
80
81    /// Parses a certificate from the ASCII PEM format.
82    #[cfg(feature = "pem")]
83    pub fn from_pem(pem_str: &str) -> Result<Self> {
84        let mut pems = pem::parse_many(pem_str).map_err(|e| Error::InvalidPEM(e.to_string()))?;
85        if pems.len() < 2 {
86            return Err(Error::InvalidPEM(format!(
87                "expected at least two PEM blocks, got {}",
88                pems.len()
89            )));
90        }
91        if pems[0].tag() != "PRIVATE_KEY" {
92            return Err(Error::InvalidPEM(format!(
93                "invalid tag (expected: 'PRIVATE_KEY', got: '{}')",
94                pems[0].tag()
95            )));
96        }
97
98        let keypair = KeyPair::try_from(pems[0].contents())
99            .map_err(|e| Error::InvalidPEM(format!("can't decode keypair: {e}")))?;
100
101        let mut rustls_certs = Vec::new();
102        for p in pems.drain(1..) {
103            if p.tag() != "CERTIFICATE" {
104                return Err(Error::InvalidPEM(format!(
105                    "invalid tag (expected: 'CERTIFICATE', got: '{}')",
106                    p.tag()
107                )));
108            }
109            rustls_certs.push(CertificateDer::from(p.contents().to_vec()));
110        }
111
112        Ok(Certificate {
113            certificate: rustls_certs,
114            private_key: CryptoPrivateKey::try_from(&keypair)?,
115        })
116    }
117
118    /// Serializes the certificate (including the private key) in PKCS#8 format in PEM.
119    #[cfg(feature = "pem")]
120    pub fn serialize_pem(&self) -> String {
121        let mut data = vec![pem::Pem::new(
122            "PRIVATE_KEY".to_string(),
123            self.private_key.serialized_der.clone(),
124        )];
125        for rustls_cert in &self.certificate {
126            data.push(pem::Pem::new(
127                "CERTIFICATE".to_string(),
128                rustls_cert.as_ref(),
129            ));
130        }
131        pem::encode_many(&data)
132    }
133}
134
135pub(crate) fn value_key_message(
136    client_random: &[u8],
137    server_random: &[u8],
138    public_key: &[u8],
139    named_curve: NamedCurve,
140) -> Vec<u8> {
141    let mut server_ecdh_params = vec![0u8; 4];
142    server_ecdh_params[0] = 3; // named curve
143    server_ecdh_params[1..3].copy_from_slice(&(named_curve as u16).to_be_bytes());
144    server_ecdh_params[3] = public_key.len() as u8;
145
146    let mut plaintext = vec![];
147    plaintext.extend_from_slice(client_random);
148    plaintext.extend_from_slice(server_random);
149    plaintext.extend_from_slice(&server_ecdh_params);
150    plaintext.extend_from_slice(public_key);
151
152    plaintext
153}
154
155/// Trait for delegating signing to an external service (e.g., HSM, TPM, cloud KMS).
156///
157/// Implementations must be thread-safe and cloneable. Each DTLS handshake may
158/// clone the signer, so `clone_box` must return a fresh instance that signs
159/// with the same key.
160pub trait CustomSigner: Send + Sync + std::fmt::Debug {
161    /// Sign the given message and return the raw signature bytes.
162    fn sign(&self, message: &[u8]) -> std::result::Result<Vec<u8>, String>;
163
164    /// Clone this signer into a new boxed instance.
165    fn clone_box(&self) -> Box<dyn CustomSigner>;
166}
167
168/// Either ED25519, ECDSA, RSA keypair, or a custom external signer.
169#[derive(Debug)]
170pub enum CryptoPrivateKeyKind {
171    /// An Ed25519 key pair.
172    Ed25519(Ed25519KeyPair),
173    /// An ECDSA key pair over NIST P-256.
174    Ecdsa256(EcdsaKeyPair),
175    /// An RSA key pair used with SHA-256.
176    Rsa256(ring::rsa::KeyPair),
177    /// Delegate signing to an external provider. The signer receives the raw
178    /// message bytes and must return a signature in the format expected by the
179    /// negotiated signature algorithm (e.g., ASN.1 DER for ECDSA).
180    Custom(Box<dyn CustomSigner>),
181}
182
183/// Private key.
184#[derive(Debug)]
185pub struct CryptoPrivateKey {
186    /// Keypair.
187    pub kind: CryptoPrivateKeyKind,
188    /// DER-encoded keypair.
189    pub serialized_der: Vec<u8>,
190}
191
192impl PartialEq for CryptoPrivateKey {
193    fn eq(&self, other: &Self) -> bool {
194        if self.serialized_der != other.serialized_der {
195            return false;
196        }
197
198        matches!(
199            (&self.kind, &other.kind),
200            (
201                CryptoPrivateKeyKind::Rsa256(_),
202                CryptoPrivateKeyKind::Rsa256(_)
203            ) | (
204                CryptoPrivateKeyKind::Ecdsa256(_),
205                CryptoPrivateKeyKind::Ecdsa256(_)
206            ) | (
207                CryptoPrivateKeyKind::Ed25519(_),
208                CryptoPrivateKeyKind::Ed25519(_)
209            ) | (
210                CryptoPrivateKeyKind::Custom(_),
211                CryptoPrivateKeyKind::Custom(_)
212            )
213        )
214    }
215}
216
217impl Clone for CryptoPrivateKey {
218    fn clone(&self) -> Self {
219        match self.kind {
220            CryptoPrivateKeyKind::Ed25519(_) => CryptoPrivateKey {
221                kind: CryptoPrivateKeyKind::Ed25519(
222                    Ed25519KeyPair::from_pkcs8_maybe_unchecked(&self.serialized_der).unwrap(),
223                ),
224                serialized_der: self.serialized_der.clone(),
225            },
226            CryptoPrivateKeyKind::Ecdsa256(_) => CryptoPrivateKey {
227                kind: CryptoPrivateKeyKind::Ecdsa256(
228                    EcdsaKeyPair::from_pkcs8(
229                        &ring::signature::ECDSA_P256_SHA256_ASN1_SIGNING,
230                        &self.serialized_der,
231                        #[cfg(feature = "ring")]
232                        &SystemRandom::new(),
233                    )
234                    .unwrap(),
235                ),
236                serialized_der: self.serialized_der.clone(),
237            },
238            CryptoPrivateKeyKind::Rsa256(_) => CryptoPrivateKey {
239                kind: CryptoPrivateKeyKind::Rsa256(
240                    ring::rsa::KeyPair::from_pkcs8(&self.serialized_der).unwrap(),
241                ),
242                serialized_der: self.serialized_der.clone(),
243            },
244            CryptoPrivateKeyKind::Custom(ref signer) => CryptoPrivateKey {
245                kind: CryptoPrivateKeyKind::Custom(signer.clone_box()),
246                serialized_der: self.serialized_der.clone(),
247            },
248        }
249    }
250}
251
252impl TryFrom<&KeyPair> for CryptoPrivateKey {
253    type Error = Error;
254
255    fn try_from(key_pair: &KeyPair) -> Result<Self> {
256        Self::from_key_pair(key_pair)
257    }
258}
259
260impl CryptoPrivateKey {
261    /// Derives the signature scheme that matches `key_pair`.
262    ///
263    /// # Errors
264    ///
265    /// Fails if the key type has no supported scheme.
266    pub fn from_key_pair(key_pair: &KeyPair) -> Result<Self> {
267        let serialized_der = key_pair.serialize_der();
268        if key_pair.is_compatible(&rcgen::PKCS_ED25519) {
269            Ok(CryptoPrivateKey {
270                kind: CryptoPrivateKeyKind::Ed25519(
271                    Ed25519KeyPair::from_pkcs8_maybe_unchecked(&serialized_der)
272                        .map_err(|e| Error::Other(e.to_string()))?,
273                ),
274                serialized_der,
275            })
276        } else if key_pair.is_compatible(&rcgen::PKCS_ECDSA_P256_SHA256) {
277            Ok(CryptoPrivateKey {
278                kind: CryptoPrivateKeyKind::Ecdsa256(
279                    EcdsaKeyPair::from_pkcs8(
280                        &ring::signature::ECDSA_P256_SHA256_ASN1_SIGNING,
281                        &serialized_der,
282                        #[cfg(feature = "ring")]
283                        &SystemRandom::new(),
284                    )
285                    .map_err(|e| Error::Other(e.to_string()))?,
286                ),
287                serialized_der,
288            })
289        } else if key_pair.is_compatible(&rcgen::PKCS_RSA_SHA256) {
290            Ok(CryptoPrivateKey {
291                kind: CryptoPrivateKeyKind::Rsa256(
292                    ring::rsa::KeyPair::from_pkcs8(&serialized_der)
293                        .map_err(|e| Error::Other(e.to_string()))?,
294                ),
295                serialized_der,
296            })
297        } else {
298            Err(Error::Other("Unsupported key_pair".to_owned()))
299        }
300    }
301}
302
303// If the client provided a "signature_algorithms" extension, then all
304// certificates provided by the server MUST be signed by a
305// hash/signature algorithm pair that appears in that extension
306//
307// https://tools.ietf.org/html/rfc5246#section-7.4.2
308pub(crate) fn generate_key_signature(
309    client_random: &[u8],
310    server_random: &[u8],
311    public_key: &[u8],
312    named_curve: NamedCurve,
313    private_key: &CryptoPrivateKey, /*, hash_algorithm: HashAlgorithm*/
314) -> Result<Vec<u8>> {
315    let msg = value_key_message(client_random, server_random, public_key, named_curve);
316    let signature = match &private_key.kind {
317        CryptoPrivateKeyKind::Ed25519(kp) => kp.sign(&msg).as_ref().to_vec(),
318        CryptoPrivateKeyKind::Ecdsa256(kp) => {
319            let system_random = SystemRandom::new();
320            kp.sign(&system_random, &msg)
321                .map_err(|e| Error::Other(e.to_string()))?
322                .as_ref()
323                .to_vec()
324        }
325        CryptoPrivateKeyKind::Rsa256(kp) => {
326            let system_random = SystemRandom::new();
327            #[cfg(feature = "ring")]
328            let mut signature = vec![0; kp.public().modulus_len()];
329            #[cfg(feature = "aws-lc-rs")]
330            let mut signature = vec![0; kp.public_modulus_len()];
331            kp.sign(
332                &ring::signature::RSA_PKCS1_SHA256,
333                &system_random,
334                &msg,
335                &mut signature,
336            )
337            .map_err(|e| Error::Other(e.to_string()))?;
338
339            signature
340        }
341        CryptoPrivateKeyKind::Custom(signer) => signer.sign(&msg).map_err(Error::Other)?,
342    };
343
344    Ok(signature)
345}
346
347// add OID_ED25519 which is not defined in x509_parser
348/// The X.509 algorithm OID for Ed25519.
349pub const OID_ED25519: Oid<'static> = oid!(1.3.101.112);
350/// The X.509 algorithm OID for ECDSA with a named curve.
351pub const OID_ECDSA: Oid<'static> = oid!(1.2.840.10045.2.1);
352
353fn verify_signature(
354    message: &[u8],
355    hash_algorithm: &SignatureHashAlgorithm,
356    remote_key_signature: &[u8],
357    raw_certificates: &[Vec<u8>],
358    insecure_verification: bool,
359) -> Result<()> {
360    if raw_certificates.is_empty() {
361        return Err(Error::ErrLengthMismatch);
362    }
363
364    let (_, certificate) = x509_parser::parse_x509_certificate(&raw_certificates[0])
365        .map_err(|e| Error::Other(e.to_string()))?;
366
367    let verify_alg: &dyn ring::signature::VerificationAlgorithm = match hash_algorithm.signature {
368        SignatureAlgorithm::Ed25519 => &ring::signature::ED25519,
369        SignatureAlgorithm::Ecdsa if hash_algorithm.hash == HashAlgorithm::Sha256 => {
370            &ring::signature::ECDSA_P256_SHA256_ASN1
371        }
372        SignatureAlgorithm::Ecdsa if hash_algorithm.hash == HashAlgorithm::Sha384 => {
373            &ring::signature::ECDSA_P384_SHA384_ASN1
374        }
375        SignatureAlgorithm::Rsa if hash_algorithm.hash == HashAlgorithm::Sha1 => {
376            &ring::signature::RSA_PKCS1_1024_8192_SHA1_FOR_LEGACY_USE_ONLY
377        }
378        SignatureAlgorithm::Rsa if (hash_algorithm.hash == HashAlgorithm::Sha256) => {
379            if remote_key_signature.len() < 256 && insecure_verification {
380                &ring::signature::RSA_PKCS1_1024_8192_SHA256_FOR_LEGACY_USE_ONLY
381            } else {
382                &ring::signature::RSA_PKCS1_2048_8192_SHA256
383            }
384        }
385        SignatureAlgorithm::Rsa if hash_algorithm.hash == HashAlgorithm::Sha384 => {
386            &ring::signature::RSA_PKCS1_2048_8192_SHA384
387        }
388        SignatureAlgorithm::Rsa if hash_algorithm.hash == HashAlgorithm::Sha512 => {
389            if remote_key_signature.len() < 256 && insecure_verification {
390                &ring::signature::RSA_PKCS1_1024_8192_SHA512_FOR_LEGACY_USE_ONLY
391            } else {
392                &ring::signature::RSA_PKCS1_2048_8192_SHA512
393            }
394        }
395        _ => return Err(Error::ErrKeySignatureVerifyUnimplemented),
396    };
397
398    log::trace!("Picked an algorithm {verify_alg:?}");
399
400    let public_key = ring::signature::UnparsedPublicKey::new(
401        verify_alg,
402        certificate
403            .tbs_certificate
404            .subject_pki
405            .subject_public_key
406            .data,
407    );
408
409    public_key
410        .verify(message, remote_key_signature)
411        .map_err(|e| Error::Other(e.to_string()))?;
412
413    Ok(())
414}
415
416pub(crate) fn verify_key_signature(
417    message: &[u8],
418    hash_algorithm: &SignatureHashAlgorithm,
419    remote_key_signature: &[u8],
420    raw_certificates: &[Vec<u8>],
421    insecure_verification: bool,
422) -> Result<()> {
423    verify_signature(
424        message,
425        hash_algorithm,
426        remote_key_signature,
427        raw_certificates,
428        insecure_verification,
429    )
430}
431
432// If the server has sent a CertificateRequest message, the client MUST send the Certificate
433// message.  The ClientKeyExchange message is now sent, and the content
434// of that message will depend on the public key algorithm selected
435// between the ClientHello and the ServerHello.  If the client has sent
436// a certificate with signing ability, a digitally-signed
437// CertificateVerify message is sent to explicitly verify possession of
438// the private key in the certificate.
439// https://tools.ietf.org/html/rfc5246#section-7.3
440pub(crate) fn generate_certificate_verify(
441    handshake_bodies: &[u8],
442    private_key: &CryptoPrivateKey, /*, hashAlgorithm hashAlgorithm*/
443) -> Result<Vec<u8>> {
444    let signature = match &private_key.kind {
445        CryptoPrivateKeyKind::Ed25519(kp) => kp.sign(handshake_bodies).as_ref().to_vec(),
446        CryptoPrivateKeyKind::Ecdsa256(kp) => {
447            let system_random = SystemRandom::new();
448            kp.sign(&system_random, handshake_bodies)
449                .map_err(|e| Error::Other(e.to_string()))?
450                .as_ref()
451                .to_vec()
452        }
453        CryptoPrivateKeyKind::Rsa256(kp) => {
454            let system_random = SystemRandom::new();
455            #[cfg(feature = "ring")]
456            let mut signature = vec![0; kp.public().modulus_len()];
457            #[cfg(feature = "aws-lc-rs")]
458            let mut signature = vec![0; kp.public_modulus_len()];
459            kp.sign(
460                &ring::signature::RSA_PKCS1_SHA256,
461                &system_random,
462                handshake_bodies,
463                &mut signature,
464            )
465            .map_err(|e| Error::Other(e.to_string()))?;
466
467            signature
468        }
469        CryptoPrivateKeyKind::Custom(signer) => {
470            signer.sign(handshake_bodies).map_err(Error::Other)?
471        }
472    };
473
474    Ok(signature)
475}
476
477pub(crate) fn verify_certificate_verify(
478    handshake_bodies: &[u8],
479    hash_algorithm: &SignatureHashAlgorithm,
480    remote_key_signature: &[u8],
481    raw_certificates: &[Vec<u8>],
482    insecure_verification: bool,
483) -> Result<()> {
484    verify_signature(
485        handshake_bodies,
486        hash_algorithm,
487        remote_key_signature,
488        raw_certificates,
489        insecure_verification,
490    )
491}
492
493pub(crate) fn load_certs(raw_certificates: &[Vec<u8>]) -> Result<Vec<CertificateDer<'static>>> {
494    if raw_certificates.is_empty() {
495        return Err(Error::ErrLengthMismatch);
496    }
497
498    let mut certs = vec![];
499    for raw_cert in raw_certificates {
500        let cert = CertificateDer::from(raw_cert.to_vec());
501        certs.push(cert);
502    }
503
504    Ok(certs)
505}
506
507pub(crate) fn verify_client_cert(
508    raw_certificates: &[Vec<u8>],
509    cert_verifier: &Arc<dyn ClientCertVerifier>,
510) -> Result<Vec<CertificateDer<'static>>> {
511    let chains = load_certs(raw_certificates)?;
512
513    let (end_entity, intermediates) = chains
514        .split_first()
515        .ok_or(Error::ErrClientCertificateRequired)?;
516
517    match cert_verifier.verify_client_cert(
518        end_entity,
519        intermediates,
520        rustls::pki_types::UnixTime::now(),
521    ) {
522        Ok(_) => {}
523        Err(err) => return Err(Error::Other(err.to_string())),
524    };
525
526    Ok(chains)
527}
528
529pub(crate) fn verify_server_cert(
530    raw_certificates: &[Vec<u8>],
531    cert_verifier: &Arc<dyn ServerCertVerifier>,
532    server_name: &str,
533) -> Result<Vec<CertificateDer<'static>>> {
534    let chains = load_certs(raw_certificates)?;
535    let server_name = match ServerName::try_from(server_name) {
536        Ok(server_name) => server_name,
537        Err(err) => return Err(Error::Other(err.to_string())),
538    };
539
540    let (end_entity, intermediates) = chains
541        .split_first()
542        .ok_or(Error::ErrServerMustHaveCertificate)?;
543    match cert_verifier.verify_server_cert(
544        end_entity,
545        intermediates,
546        &server_name,
547        &[],
548        rustls::pki_types::UnixTime::now(),
549    ) {
550        Ok(_) => {}
551        Err(err) => return Err(Error::Other(err.to_string())),
552    };
553
554    Ok(chains)
555}
556
557pub(crate) fn generate_aead_additional_data(h: &RecordLayerHeader, payload_len: usize) -> [u8; 13] {
558    let mut additional_data = [0u8; 13];
559    // SequenceNumber MUST be set first
560    // we only want uint48, clobbering an extra 2 (using uint64, rust doesn't have uint48)
561    additional_data[..8].copy_from_slice(&h.sequence_number.to_be_bytes());
562    additional_data[..2].copy_from_slice(&h.epoch.to_be_bytes());
563    additional_data[8] = h.content_type as u8;
564    additional_data[9] = h.protocol_version.major;
565    additional_data[10] = h.protocol_version.minor;
566    additional_data[11..].copy_from_slice(&(payload_len as u16).to_be_bytes());
567
568    additional_data
569}
570
571#[cfg(test)]
572mod test {
573    #[cfg(feature = "pem")]
574    use super::*;
575
576    #[cfg(feature = "pem")]
577    #[test]
578    fn test_certificate_serialize_pem_and_from_pem() -> Result<()> {
579        let cert = Certificate::generate_self_signed(vec!["webrtc.rs".to_owned()])?;
580
581        let pem = cert.serialize_pem();
582        let loaded_cert = Certificate::from_pem(&pem)?;
583
584        assert_eq!(loaded_cert, cert);
585
586        Ok(())
587    }
588}