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