wire_e2e_identity/
pki.rs

1// TODO: we're allowing unused code here because the E2EI parts haven't been
2// coupled yet; once they are, remove this.
3#![allow(unused)]
4use std::sync::Arc;
5
6use async_lock::{RwLock, RwLockReadGuard};
7use openmls_traits::{
8    authentication_service::{CredentialAuthenticationStatus, CredentialRef},
9    crypto::OpenMlsCrypto,
10    types::SignatureScheme,
11};
12use spki::{SignatureAlgorithmIdentifier, der::referenced::RefToOwned};
13
14use crate::error::{E2eIdentityError, E2eIdentityResult};
15
16pub struct Ed25519PkiSignature(ed25519_dalek::Signature);
17impl spki::SignatureBitStringEncoding for Ed25519PkiSignature {
18    fn to_bitstring(&self) -> spki::der::Result<spki::der::asn1::BitString> {
19        spki::der::asn1::BitString::new(0, self.0.to_vec())
20    }
21}
22
23#[derive(Debug, Clone)]
24pub struct Ed25519PkiKeypair(ed25519_dalek::SigningKey);
25
26impl Ed25519PkiKeypair {
27    pub fn keypair_bytes(&self) -> Vec<u8> {
28        self.0.to_keypair_bytes().to_vec()
29    }
30}
31
32impl spki::SignatureAlgorithmIdentifier for Ed25519PkiKeypair {
33    type Params = spki::der::AnyRef<'static>;
34    const SIGNATURE_ALGORITHM_IDENTIFIER: spki::AlgorithmIdentifier<Self::Params> = ed25519_dalek::pkcs8::ALGORITHM_ID;
35}
36
37impl signature::Keypair for Ed25519PkiKeypair {
38    type VerifyingKey = <ed25519_dalek::SigningKey as signature::Keypair>::VerifyingKey;
39    fn verifying_key(&self) -> Self::VerifyingKey {
40        self.0.verifying_key()
41    }
42}
43
44impl signature::Signer<Ed25519PkiSignature> for Ed25519PkiKeypair {
45    fn try_sign(&self, message: &[u8]) -> Result<Ed25519PkiSignature, ed25519_dalek::SignatureError> {
46        self.0.try_sign(message).map(Ed25519PkiSignature)
47    }
48}
49
50#[derive(Clone)]
51pub struct P521PkiVerifyingKey(ecdsa::VerifyingKey<p521::NistP521>);
52impl From<ecdsa::VerifyingKey<p521::NistP521>> for P521PkiVerifyingKey {
53    fn from(k: ecdsa::VerifyingKey<p521::NistP521>) -> Self {
54        Self(k)
55    }
56}
57
58impl std::ops::Deref for P521PkiVerifyingKey {
59    type Target = ecdsa::VerifyingKey<p521::NistP521>;
60    fn deref(&self) -> &Self::Target {
61        &self.0
62    }
63}
64
65impl p521::pkcs8::EncodePublicKey for P521PkiVerifyingKey {
66    fn to_public_key_der(&self) -> spki::Result<spki::Document> {
67        self.0.to_public_key_der()
68    }
69}
70
71#[derive(Clone)]
72pub struct P521PkiKeypair(ecdsa::SigningKey<p521::NistP521>);
73
74impl spki::SignatureAlgorithmIdentifier for P521PkiKeypair {
75    type Params = spki::ObjectIdentifier;
76    const SIGNATURE_ALGORITHM_IDENTIFIER: spki::AlgorithmIdentifier<Self::Params> = spki::AlgorithmIdentifier {
77        oid: ecdsa::ECDSA_SHA512_OID,
78        parameters: None,
79    };
80}
81
82impl signature::Keypair for P521PkiKeypair {
83    type VerifyingKey = P521PkiVerifyingKey;
84    fn verifying_key(&self) -> Self::VerifyingKey {
85        (*self.0.verifying_key()).into()
86    }
87}
88
89impl signature::Signer<p521::ecdsa::DerSignature> for P521PkiKeypair {
90    fn try_sign(&self, message: &[u8]) -> Result<p521::ecdsa::DerSignature, p521::ecdsa::Error> {
91        let sk = p521::ecdsa::SigningKey::from(self.0.clone());
92        Ok(sk.try_sign(message)?.to_der())
93    }
94}
95
96#[derive(Clone)]
97pub enum PkiKeypair {
98    P256(p256::ecdsa::SigningKey),
99    P384(p384::ecdsa::SigningKey),
100    P521(P521PkiKeypair),
101    Ed25519(Ed25519PkiKeypair),
102}
103
104impl std::fmt::Debug for PkiKeypair {
105    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106        f.debug_struct("PkiKeypair")
107            .field(
108                "type",
109                &match self {
110                    Self::P256(_k) => "P256",
111                    Self::P384(_k) => "P384",
112                    Self::P521(_k) => "P521",
113                    Self::Ed25519(_k) => "Ed25519",
114                },
115            )
116            .field("key", &"[REDACTED]")
117            .finish()
118    }
119}
120
121impl PkiKeypair {
122    pub fn signing_key_bytes(&self) -> Vec<u8> {
123        match self {
124            Self::P256(sk) => sk.to_bytes().to_vec(),
125            Self::P384(sk) => sk.to_bytes().to_vec(),
126            Self::P521(sk) => sk.0.to_bytes().to_vec(),
127            Self::Ed25519(sk) => sk.0.to_bytes().to_vec(),
128        }
129    }
130
131    pub fn public_key_bytes(&self) -> Vec<u8> {
132        match self {
133            Self::P256(sk) => sk.verifying_key().to_sec1_bytes().to_vec(),
134            Self::P384(sk) => sk.verifying_key().to_sec1_bytes().to_vec(),
135            Self::P521(sk) => sk.0.verifying_key().to_sec1_bytes().to_vec(),
136            Self::Ed25519(sk) => sk.0.verifying_key().to_bytes().to_vec(),
137        }
138    }
139
140    pub fn public_key_identifier(&self) -> Vec<u8> {
141        use sha1::Digest as _;
142        sha1::Sha1::digest(self.public_key_bytes()).to_vec()
143    }
144}
145
146pub use x509_cert::builder::Profile as CertProfile;
147
148pub struct CertificateGenerationArgs<'a> {
149    pub signature_scheme: SignatureScheme,
150    pub profile: CertProfile,
151    pub serial: u64,
152    /// Duration since UNIX EPOCH
153    pub validity_start: Option<std::time::Duration>,
154    /// Duration relative to `validity_start` if present. Otherwise relative to now
155    pub validity_from_start: std::time::Duration,
156    pub org: &'a str,
157    pub common_name: Option<&'a str>,
158    pub alternative_names: Option<&'a [&'a str]>,
159    pub domain: Option<&'a str>,
160    pub crl_dps: Option<&'a [&'a str]>,
161    pub signer: Option<&'a PkiKeypair>,
162    pub is_ca: bool,
163    pub is_root: bool,
164}
165
166fn get_extended_keyusage(is_ca: bool) -> x509_cert::ext::pkix::ExtendedKeyUsage {
167    let mut ext_keyusages = vec![];
168    if !is_ca {
169        ext_keyusages.push(x509_cert::der::oid::db::rfc5280::ID_KP_CLIENT_AUTH);
170    }
171
172    x509_cert::ext::pkix::ExtendedKeyUsage(ext_keyusages)
173}
174
175macro_rules! impl_certgen {
176    (
177        $signer:expr, $signer_keypair:expr, $sig_type:path,
178        $profile:expr, $own_spki:expr, $serial:expr,
179        $subject:expr, $org:expr, $domain:expr, $validity:expr, $alt_names:expr,
180        $crl_dps:expr, $is_ca:expr, $is_root:expr
181    ) => {{
182        let add_akid = $is_ca && $profile == x509_cert::builder::Profile::Root;
183
184        let mut builder = x509_cert::builder::CertificateBuilder::new(
185            $profile,
186            $serial,
187            $validity,
188            $subject,
189            $own_spki,
190            $signer_keypair,
191        )
192        .map_err(|_| E2eIdentityError::CertificateGenerationError)?;
193
194        if add_akid {
195            builder
196                .add_extension(&$signer.akid()?)
197                .map_err(|_| E2eIdentityError::CertificateGenerationError)?;
198        }
199
200        builder
201            .add_extension(&get_extended_keyusage($is_ca))
202            .map_err(|_| E2eIdentityError::CertificateGenerationError)?;
203
204        if !$is_ca {
205            if let Some(alt_names) = $alt_names {
206                let mut alt_names_list = vec![];
207                for alt_name in alt_names {
208                    alt_names_list.push(x509_cert::ext::pkix::name::GeneralName::UniformResourceIdentifier(
209                        alt_name
210                            .to_string()
211                            .try_into()
212                            .map_err(|_| E2eIdentityError::CertificateGenerationError)?,
213                    ));
214                }
215
216                builder
217                    .add_extension(&x509_cert::ext::pkix::SubjectAltName(alt_names_list))
218                    .map_err(|_| E2eIdentityError::CertificateGenerationError)?;
219            }
220        } else {
221            let mut permitted_subtrees = vec![
222                x509_cert::ext::pkix::name::GeneralName::UniformResourceIdentifier(
223                    format!(".{}", $org)
224                        .try_into()
225                        .map_err(|_| E2eIdentityError::CertificateGenerationError)?,
226                ),
227                x509_cert::ext::pkix::name::GeneralName::UniformResourceIdentifier(
228                    format!("{}", $org)
229                        .try_into()
230                        .map_err(|_| E2eIdentityError::CertificateGenerationError)?,
231                ),
232            ];
233
234            if let Some(domain) = $domain {
235                // Add Domain DNS SAN
236                builder
237                    .add_extension(&x509_cert::ext::pkix::SubjectAltName(vec![
238                        x509_cert::ext::pkix::name::GeneralName::DnsName(
239                            domain
240                                .to_string()
241                                .try_into()
242                                .map_err(|_| E2eIdentityError::CertificateGenerationError)?,
243                        ),
244                    ]))
245                    .map_err(|_| E2eIdentityError::CertificateGenerationError)?;
246
247                permitted_subtrees.push(x509_cert::ext::pkix::name::GeneralName::DnsName(
248                    domain
249                        .to_string()
250                        .try_into()
251                        .map_err(|_| E2eIdentityError::CertificateGenerationError)?,
252                ));
253            }
254
255            if !$is_root {
256                builder
257                    .add_extension(&x509_cert::ext::pkix::NameConstraints {
258                        permitted_subtrees: Some(
259                            permitted_subtrees
260                                .into_iter()
261                                .map(|base| x509_cert::ext::pkix::constraints::name::GeneralSubtree {
262                                    base,
263                                    minimum: 0,
264                                    maximum: None,
265                                })
266                                .collect(),
267                        ),
268
269                        excluded_subtrees: None,
270                    })
271                    .map_err(|_| E2eIdentityError::CertificateGenerationError)?;
272            }
273        }
274
275        if let Some(crl_dps) = $crl_dps {
276            let mut crl_distribution_points = vec![];
277            for dp in crl_dps {
278                crl_distribution_points.push(x509_cert::ext::pkix::crl::dp::DistributionPoint {
279                    distribution_point: Some(x509_cert::ext::pkix::name::DistributionPointName::FullName(vec![
280                        x509_cert::ext::pkix::name::GeneralName::UniformResourceIdentifier(
281                            dp.to_string()
282                                .try_into()
283                                .map_err(|_| E2eIdentityError::CertificateGenerationError)?,
284                        ),
285                    ])),
286                    crl_issuer: None,
287                    reasons: None,
288                });
289            }
290            builder
291                .add_extension(&x509_cert::ext::pkix::CrlDistributionPoints(crl_distribution_points))
292                .map_err(|_| E2eIdentityError::CertificateGenerationError)?;
293        }
294
295        builder
296            .build::<$sig_type>()
297            .map_err(|_| E2eIdentityError::CertificateGenerationError)?
298    }};
299}
300
301impl PkiKeypair {
302    pub fn new(signature_scheme: SignatureScheme, sk: Vec<u8>) -> E2eIdentityResult<Self> {
303        match signature_scheme {
304            SignatureScheme::ECDSA_SECP256R1_SHA256 => Ok(PkiKeypair::P256(
305                p256::ecdsa::SigningKey::from_slice(sk.as_slice())
306                    .map_err(|_| E2eIdentityError::CertificateGenerationError)?,
307            )),
308            SignatureScheme::ECDSA_SECP384R1_SHA384 => Ok(PkiKeypair::P384(
309                p384::ecdsa::SigningKey::from_slice(sk.as_slice())
310                    .map_err(|_| E2eIdentityError::CertificateGenerationError)?,
311            )),
312            SignatureScheme::ECDSA_SECP521R1_SHA512 => Ok(PkiKeypair::P521(P521PkiKeypair(
313                ecdsa::SigningKey::<p521::NistP521>::from_slice(sk.as_slice())
314                    .map_err(|_| E2eIdentityError::CertificateGenerationError)?,
315            ))),
316            SignatureScheme::ED25519 => Ok(PkiKeypair::Ed25519(Ed25519PkiKeypair(
317                ed25519_dalek::SigningKey::from_bytes(
318                    sk.as_slice()
319                        .try_into()
320                        .expect("private key must be exactly {ed25519_dalek::SECRET_KEY_LENGTH} bytes"),
321                ),
322            ))),
323            _ => Err(E2eIdentityError::UnsupportedSignatureScheme),
324        }
325    }
326
327    pub fn signature_algorithm(&self) -> spki::AlgorithmIdentifierRef<'_> {
328        match self {
329            Self::P256(_) => p256::ecdsa::SigningKey::SIGNATURE_ALGORITHM_IDENTIFIER,
330            Self::P384(_) => p384::ecdsa::SigningKey::SIGNATURE_ALGORITHM_IDENTIFIER,
331            Self::P521(_) => spki::AlgorithmIdentifierRef {
332                oid: ecdsa::ECDSA_SHA512_OID,
333                parameters: None,
334            },
335            Self::Ed25519(_) => ed25519_dalek::pkcs8::ALGORITHM_ID,
336        }
337    }
338
339    pub fn spki(&self) -> E2eIdentityResult<spki::SubjectPublicKeyInfoOwned> {
340        match self {
341            Self::P256(sk) => Ok(spki::SubjectPublicKeyInfoOwned::from_key(*sk.verifying_key())
342                .map_err(|_| E2eIdentityError::CertificateGenerationError)?),
343            Self::P384(sk) => Ok(spki::SubjectPublicKeyInfoOwned::from_key(*sk.verifying_key())
344                .map_err(|_| E2eIdentityError::CertificateGenerationError)?),
345            Self::P521(sk) => Ok(spki::SubjectPublicKeyInfoOwned::from_key(*sk.0.verifying_key())
346                .map_err(|_| E2eIdentityError::CertificateGenerationError)?),
347            Self::Ed25519(sk) => Ok(spki::SubjectPublicKeyInfoOwned::from_key(sk.0.verifying_key())
348                .map_err(|_| E2eIdentityError::CertificateGenerationError)?),
349        }
350    }
351
352    pub fn akid(&self) -> E2eIdentityResult<x509_cert::ext::pkix::AuthorityKeyIdentifier> {
353        Ok(x509_cert::ext::pkix::AuthorityKeyIdentifier {
354            key_identifier: Some(
355                spki::der::asn1::OctetString::new(self.public_key_identifier())
356                    .map_err(|_| E2eIdentityError::CertificateGenerationError)?,
357            ),
358            authority_cert_issuer: None,
359            authority_cert_serial_number: None,
360        })
361    }
362
363    pub fn revoke_certs(
364        &self,
365        issuer_cert: &x509_cert::Certificate,
366        revoked_cert_serial_numbers: Vec<Vec<u8>>,
367    ) -> E2eIdentityResult<x509_cert::crl::CertificateList> {
368        let signature_algorithm = self.signature_algorithm();
369        let now = web_time::SystemTime::now()
370            .duration_since(web_time::UNIX_EPOCH)
371            .map_err(|_| E2eIdentityError::CertificateGenerationError)?;
372        let now = x509_cert::der::asn1::GeneralizedTime::from_unix_duration(now)
373            .map_err(|_| E2eIdentityError::CertificateGenerationError)?;
374        let now = x509_cert::time::Time::GeneralTime(now);
375
376        let revoked_certificates = revoked_cert_serial_numbers
377            .into_iter()
378            .map(|serial_number| x509_cert::crl::RevokedCert {
379                serial_number: x509_cert::serial_number::SerialNumber::new(&serial_number)
380                    .expect("Non-positive serial number"),
381                revocation_date: now,
382                crl_entry_extensions: None,
383            })
384            .collect();
385
386        let tbs_cert_list = x509_cert::crl::TbsCertList {
387            version: x509_cert::Version::V3,
388            signature: signature_algorithm.ref_to_owned(),
389            issuer: issuer_cert.tbs_certificate.subject.clone(),
390            this_update: now,
391            next_update: None,
392            revoked_certificates: Some(revoked_certificates),
393            crl_extensions: None,
394        };
395
396        use spki::der::Encode as _;
397
398        let tbs = tbs_cert_list
399            .to_der()
400            .map_err(|_| E2eIdentityError::CertificateGenerationError)?;
401
402        use signature::Signer as _;
403
404        let signature: Vec<u8> = match self {
405            PkiKeypair::P256(sk) => signature::Signer::<p256::ecdsa::DerSignature>::try_sign(sk, &tbs)?
406                .to_der()
407                .map_err(|_| E2eIdentityError::CertificateGenerationError),
408            PkiKeypair::P384(sk) => signature::Signer::<p384::ecdsa::DerSignature>::try_sign(sk, &tbs)?
409                .to_der()
410                .map_err(|_| E2eIdentityError::CertificateGenerationError),
411            PkiKeypair::P521(sk) => {
412                let sk = p521::ecdsa::SigningKey::from(sk.0.clone());
413                let signature: p521::ecdsa::DerSignature = sk.try_sign(&tbs)?.to_der();
414
415                signature
416                    .to_der()
417                    .map_err(|_| E2eIdentityError::CertificateGenerationError)
418            }
419            PkiKeypair::Ed25519(sk) => Ok(sk.try_sign(&tbs)?.0.to_vec()),
420        }?;
421
422        let signature =
423            spki::der::asn1::BitString::new(0, signature).map_err(|_| E2eIdentityError::CertificateGenerationError)?;
424
425        Ok(x509_cert::crl::CertificateList {
426            tbs_cert_list,
427            signature_algorithm: signature_algorithm.ref_to_owned(),
428            signature,
429        })
430    }
431
432    pub fn generate_cert(&self, args: CertificateGenerationArgs) -> E2eIdentityResult<x509_cert::Certificate> {
433        use std::str::FromStr as _;
434
435        use x509_cert::builder::Builder as _;
436        let mut subject_fmt = format!("O={}", args.org);
437        if let Some(cn) = args.common_name {
438            subject_fmt.push_str(&format!(",CN={cn}"));
439        }
440
441        let subject =
442            x509_cert::name::Name::from_str(&subject_fmt).map_err(|_| E2eIdentityError::CertificateGenerationError)?;
443
444        let validity_start = if let Some(validity_start) = args.validity_start {
445            validity_start
446        } else {
447            web_time::SystemTime::now()
448                .duration_since(web_time::UNIX_EPOCH)
449                .map_err(|_| E2eIdentityError::CertificateGenerationError)?
450        } - std::time::Duration::from_secs(1); // to prevent time clipping
451
452        let validity = {
453            let not_before = x509_cert::der::asn1::GeneralizedTime::from_unix_duration(validity_start)
454                .map_err(|_| E2eIdentityError::CertificateGenerationError)?
455                .into();
456            let not_after =
457                x509_cert::der::asn1::GeneralizedTime::from_unix_duration(validity_start + args.validity_from_start)
458                    .map_err(|_| E2eIdentityError::CertificateGenerationError)?
459                    .into();
460            x509_cert::time::Validity { not_before, not_after }
461        };
462
463        let serial_number = x509_cert::serial_number::SerialNumber::from(args.serial);
464        let spki = self.spki()?;
465
466        let signer = args.signer.unwrap_or(self);
467
468        let cert = match signer {
469            PkiKeypair::P256(kp) => {
470                impl_certgen!(
471                    signer,
472                    kp,
473                    p256::ecdsa::DerSignature,
474                    args.profile,
475                    spki,
476                    serial_number,
477                    subject,
478                    args.org,
479                    args.domain,
480                    validity,
481                    args.alternative_names,
482                    args.crl_dps,
483                    args.is_ca,
484                    args.is_root
485                )
486            }
487            PkiKeypair::P384(kp) => {
488                impl_certgen!(
489                    signer,
490                    kp,
491                    p384::ecdsa::DerSignature,
492                    args.profile,
493                    spki,
494                    serial_number,
495                    subject,
496                    args.org,
497                    args.domain,
498                    validity,
499                    args.alternative_names,
500                    args.crl_dps,
501                    args.is_ca,
502                    args.is_root
503                )
504            }
505            PkiKeypair::P521(kp) => {
506                impl_certgen!(
507                    signer,
508                    kp,
509                    p521::ecdsa::DerSignature,
510                    args.profile,
511                    spki,
512                    serial_number,
513                    subject,
514                    args.org,
515                    args.domain,
516                    validity,
517                    args.alternative_names,
518                    args.crl_dps,
519                    args.is_ca,
520                    args.is_root
521                )
522            }
523            PkiKeypair::Ed25519(kp) => {
524                impl_certgen!(
525                    signer,
526                    kp,
527                    Ed25519PkiSignature,
528                    args.profile,
529                    spki,
530                    serial_number,
531                    subject,
532                    args.org,
533                    args.domain,
534                    validity,
535                    args.alternative_names,
536                    args.crl_dps,
537                    args.is_ca,
538                    args.is_root
539                )
540            }
541        };
542
543        Ok(cert)
544    }
545
546    pub fn rand(alg: SignatureScheme, crypto: &impl OpenMlsCrypto) -> super::E2eIdentityResult<Self> {
547        Self::new(
548            alg,
549            crypto
550                .signature_key_gen(alg)
551                .map_err(|_| super::E2eIdentityError::SignatureKeyGenerationFailed)?
552                .0,
553        )
554    }
555}