1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![doc = include_str!("../README.md")]
3#![allow(renamed_and_removed_lints)] #![allow(unknown_lints)] #![warn(missing_docs)]
7#![warn(noop_method_call)]
8#![warn(unreachable_pub)]
9#![warn(clippy::all)]
10#![deny(clippy::await_holding_lock)]
11#![deny(clippy::cargo_common_metadata)]
12#![deny(clippy::cast_lossless)]
13#![deny(clippy::checked_conversions)]
14#![warn(clippy::cognitive_complexity)]
15#![deny(clippy::debug_assert_with_mut_call)]
16#![deny(clippy::exhaustive_enums)]
17#![deny(clippy::exhaustive_structs)]
18#![deny(clippy::expl_impl_clone_on_copy)]
19#![deny(clippy::fallible_impl_from)]
20#![deny(clippy::implicit_clone)]
21#![deny(clippy::large_stack_arrays)]
22#![warn(clippy::manual_ok_or)]
23#![deny(clippy::missing_docs_in_private_items)]
24#![warn(clippy::needless_borrow)]
25#![warn(clippy::needless_pass_by_value)]
26#![warn(clippy::option_option)]
27#![deny(clippy::print_stderr)]
28#![deny(clippy::print_stdout)]
29#![warn(clippy::rc_buffer)]
30#![deny(clippy::ref_option_ref)]
31#![warn(clippy::semicolon_if_nothing_returned)]
32#![warn(clippy::trait_duplication_in_bounds)]
33#![deny(clippy::unchecked_time_subtraction)]
34#![deny(clippy::unnecessary_wraps)]
35#![warn(clippy::unseparated_literal_suffix)]
36#![deny(clippy::unwrap_used)]
37#![deny(clippy::mod_module_files)]
38#![allow(clippy::let_unit_value)] #![allow(clippy::uninlined_format_args)]
40#![allow(clippy::significant_drop_in_scrutinee)] #![allow(clippy::result_large_err)] #![allow(clippy::needless_raw_string_hashes)] #![allow(clippy::needless_lifetimes)] #![allow(mismatched_lifetime_syntaxes)] #![deny(clippy::unused_async)]
46mod err;
49pub mod rsa;
50
51#[cfg(feature = "x509")]
52pub use tor_cert_x509 as x509;
53
54use caret::caret_int;
55use tor_bytes::{Error as BytesError, Result as BytesResult};
56use tor_bytes::{Readable, Reader};
57use tor_llcrypto::pk::*;
58
59use std::time;
60
61pub use err::CertError;
62
63#[cfg(feature = "encode")]
64mod encode;
65#[cfg(feature = "encode")]
66pub use encode::EncodedEd25519Cert;
67#[cfg(feature = "encode")]
68pub use err::CertEncodeError;
69
70type CertResult<T> = std::result::Result<T, CertError>;
72
73caret_int! {
74 pub struct CertType(u8) {
85 TLS_LINK_X509 = 0x01,
87 RSA_ID_X509 = 0x02,
89 LINK_AUTH_X509 = 0x03,
92
93 IDENTITY_V_SIGNING = 0x04,
95
96 SIGNING_V_TLS_CERT = 0x05,
98
99 SIGNING_V_LINK_AUTH = 0x06,
101
102 RSA_ID_V_IDENTITY = 0x07,
105
106 HS_BLINDED_ID_V_SIGNING = 0x08,
110
111 HS_IP_V_SIGNING = 0x09,
126
127 NTOR_CC_IDENTITY = 0x0A,
130
131 HS_IP_CC_SIGNING = 0x0B,
140
141 FAMILY_V_IDENTITY = 0x0C,
144 }
145}
146
147caret_int! {
148 pub struct ExtType(u8) {
150 SIGNED_WITH_ED25519_KEY = 0x04,
154 }
155}
156
157caret_int! {
158 pub struct KeyType(u8) {
160 ED25519_KEY = 0x01,
162 SHA256_OF_RSA = 0x02,
164 SHA256_OF_X509 = 0x03,
166 }
167}
168
169#[derive(Debug, Clone)]
172#[cfg_attr(feature = "encode", derive(derive_builder::Builder))]
173#[cfg_attr(
174 feature = "encode",
175 builder(name = "Ed25519CertConstructor", build_fn(skip))
176)]
177pub struct Ed25519Cert {
178 #[cfg_attr(feature = "encode", builder(setter(custom)))]
180 exp_hours: u32,
181 cert_type: CertType,
183 cert_key: CertifiedKey,
185 #[allow(unused)]
187 #[cfg_attr(feature = "encode", builder(setter(custom)))]
188 extensions: Vec<CertExt>,
189 #[cfg_attr(feature = "encode", builder(setter(custom)))]
195 signed_with: Option<ed25519::Ed25519Identity>,
196}
197
198#[derive(Debug, Clone)]
200#[non_exhaustive]
201pub enum CertifiedKey {
202 Ed25519(ed25519::Ed25519Identity),
204 RsaSha256Digest([u8; 32]),
206 X509Sha256Digest([u8; 32]),
208 Unrecognized(UnrecognizedKey),
210}
211
212#[derive(Debug, Clone)]
214pub struct UnrecognizedKey {
215 key_type: KeyType,
217 key_digest: [u8; 32],
219}
220
221impl CertifiedKey {
222 pub fn key_type(&self) -> KeyType {
224 match self {
225 CertifiedKey::Ed25519(_) => KeyType::ED25519_KEY,
226 CertifiedKey::RsaSha256Digest(_) => KeyType::SHA256_OF_RSA,
227 CertifiedKey::X509Sha256Digest(_) => KeyType::SHA256_OF_X509,
228
229 CertifiedKey::Unrecognized(u) => u.key_type,
230 }
231 }
232 pub fn as_bytes(&self) -> &[u8] {
235 match self {
236 CertifiedKey::Ed25519(k) => k.as_bytes(),
237 CertifiedKey::RsaSha256Digest(k) => &k[..],
238 CertifiedKey::X509Sha256Digest(k) => &k[..],
239 CertifiedKey::Unrecognized(u) => &u.key_digest[..],
240 }
241 }
242 pub fn as_ed25519(&self) -> Option<&ed25519::Ed25519Identity> {
245 match self {
246 CertifiedKey::Ed25519(k) => Some(k),
247 _ => None,
248 }
249 }
250 fn from_reader(key_type: KeyType, r: &mut Reader<'_>) -> BytesResult<Self> {
253 Ok(match key_type {
254 KeyType::ED25519_KEY => CertifiedKey::Ed25519(r.extract()?),
255 KeyType::SHA256_OF_RSA => CertifiedKey::RsaSha256Digest(r.extract()?),
256 KeyType::SHA256_OF_X509 => CertifiedKey::X509Sha256Digest(r.extract()?),
257 _ => CertifiedKey::Unrecognized(UnrecognizedKey {
258 key_type,
259 key_digest: r.extract()?,
260 }),
261 })
262 }
263}
264
265#[derive(Debug, Clone)]
267enum CertExt {
268 SignedWithEd25519(SignedWithEd25519Ext),
270 Unrecognized(UnrecognizedExt),
272}
273
274#[derive(Debug, Clone)]
276#[allow(unused)]
277struct UnrecognizedExt {
278 affects_validation: bool,
281 ext_type: ExtType,
283 body: Vec<u8>,
285}
286
287impl CertExt {
288 fn ext_id(&self) -> ExtType {
290 match self {
291 CertExt::SignedWithEd25519(_) => ExtType::SIGNED_WITH_ED25519_KEY,
292 CertExt::Unrecognized(u) => u.ext_type,
293 }
294 }
295}
296
297#[derive(Debug, Clone)]
299struct SignedWithEd25519Ext {
300 pk: ed25519::Ed25519Identity,
302}
303
304impl Readable for CertExt {
305 fn take_from(b: &mut Reader<'_>) -> BytesResult<Self> {
306 let len = b.take_u16()?;
307 let ext_type: ExtType = b.take_u8()?.into();
308 let flags = b.take_u8()?;
309 let body = b.take(len as usize)?;
310
311 Ok(match ext_type {
312 ExtType::SIGNED_WITH_ED25519_KEY => CertExt::SignedWithEd25519(SignedWithEd25519Ext {
313 pk: ed25519::Ed25519Identity::from_bytes(body).ok_or_else(|| {
314 BytesError::InvalidMessage("wrong length on Ed25519 key".into())
315 })?,
316 }),
317 _ => {
318 if (flags & 1) != 0 {
319 return Err(BytesError::InvalidMessage(
320 "unrecognized certificate extension, with 'affects_validation' flag set."
321 .into(),
322 ));
323 }
324 CertExt::Unrecognized(UnrecognizedExt {
325 affects_validation: false,
326 ext_type,
327 body: body.into(),
328 })
329 }
330 })
331 }
332}
333
334impl Ed25519Cert {
335 pub fn decode(cert: &[u8]) -> BytesResult<KeyUnknownCert> {
344 let mut r = Reader::from_slice(cert);
345 let v = r.take_u8()?;
346 if v != 1 {
347 return Err(BytesError::InvalidMessage(
350 "Unrecognized certificate version".into(),
351 ));
352 }
353 let cert_type = r.take_u8()?.into();
354 let exp_hours = r.take_u32()?;
355 let mut cert_key_type = r.take_u8()?.into();
356
357 if cert_type == CertType::SIGNING_V_TLS_CERT && cert_key_type == KeyType::ED25519_KEY {
361 cert_key_type = KeyType::SHA256_OF_X509;
362 }
363
364 let cert_key = CertifiedKey::from_reader(cert_key_type, &mut r)?;
365 let n_exts = r.take_u8()?;
366 let mut extensions = Vec::new();
367 for _ in 0..n_exts {
368 let e: CertExt = r.extract()?;
369 extensions.push(e);
370 }
371
372 let sig_offset = r.consumed();
373 let signature: ed25519::Signature = r.extract()?;
374 r.should_be_exhausted()?;
375
376 let keyext = extensions
377 .iter()
378 .find(|e| e.ext_id() == ExtType::SIGNED_WITH_ED25519_KEY);
379
380 let included_pkey = match keyext {
381 Some(CertExt::SignedWithEd25519(s)) => Some(s.pk),
382 _ => None,
383 };
384
385 Ok(KeyUnknownCert {
386 cert: UncheckedCert {
387 cert: Ed25519Cert {
388 exp_hours,
389 cert_type,
390 cert_key,
391 extensions,
392
393 signed_with: included_pkey,
394 },
395 text: cert[0..sig_offset].into(),
396 signature,
397 },
398 })
399 }
400
401 pub fn expiry(&self) -> std::time::SystemTime {
403 let d = std::time::Duration::new(u64::from(self.exp_hours) * 3600, 0);
404 std::time::SystemTime::UNIX_EPOCH + d
405 }
406
407 pub fn is_expired_at(&self, when: std::time::SystemTime) -> bool {
409 when >= self.expiry()
410 }
411
412 pub fn subject_key(&self) -> &CertifiedKey {
415 &self.cert_key
416 }
417
418 pub fn signing_key(&self) -> Option<&ed25519::Ed25519Identity> {
420 self.signed_with.as_ref()
421 }
422
423 pub fn cert_type(&self) -> CertType {
425 self.cert_type
426 }
427}
428
429#[derive(Clone, Debug)]
438pub struct KeyUnknownCert {
439 cert: UncheckedCert,
441}
442
443impl KeyUnknownCert {
444 pub fn peek_cert_type(&self) -> CertType {
446 self.cert.cert.cert_type
447 }
448 pub fn peek_subject_key(&self) -> &CertifiedKey {
450 &self.cert.cert.cert_key
451 }
452
453 #[deprecated(
461 since = "0.7.1",
462 note = "Use should_have_signing_key or should_be_signed_with instead."
463 )]
464 pub fn check_key(self, pkey: Option<&ed25519::Ed25519Identity>) -> CertResult<UncheckedCert> {
465 match pkey {
466 Some(wanted) => self.should_be_signed_with(wanted),
467 None => self.should_have_signing_key(),
468 }
469 }
470
471 pub fn should_have_signing_key(self) -> CertResult<UncheckedCert> {
478 let real_key = match &self.cert.cert.signed_with {
479 Some(a) => *a,
480 None => return Err(CertError::MissingPubKey),
481 };
482
483 Ok(UncheckedCert {
484 cert: Ed25519Cert {
485 signed_with: Some(real_key),
486 ..self.cert.cert
487 },
488 ..self.cert
489 })
490 }
491
492 pub fn should_be_signed_with(
498 self,
499 pkey: &ed25519::Ed25519Identity,
500 ) -> CertResult<UncheckedCert> {
501 let real_key = match &self.cert.cert.signed_with {
502 Some(a) if a == pkey => *pkey,
503 None => *pkey,
504 Some(_) => return Err(CertError::KeyMismatch),
505 };
506
507 Ok(UncheckedCert {
508 cert: Ed25519Cert {
509 signed_with: Some(real_key),
510 ..self.cert.cert
511 },
512 ..self.cert
513 })
514 }
515}
516
517#[derive(Debug, Clone)]
520pub struct UncheckedCert {
521 cert: Ed25519Cert,
524
525 text: Vec<u8>,
530
531 signature: ed25519::Signature,
533}
534
535pub struct SigCheckedCert {
538 cert: Ed25519Cert,
540}
541
542impl UncheckedCert {
543 pub fn dangerously_split(
546 self,
547 ) -> CertResult<(SigCheckedCert, ed25519::ValidatableEd25519Signature)> {
548 use tor_checkable::SelfSigned;
549 let signing_key = self.cert.signed_with.ok_or(CertError::MissingPubKey)?;
550 let signing_key = signing_key
551 .try_into()
552 .map_err(|_| CertError::BadSignature)?;
553 let signature =
554 ed25519::ValidatableEd25519Signature::new(signing_key, self.signature, &self.text[..]);
555 Ok((self.dangerously_assume_wellsigned(), signature))
556 }
557
558 pub fn peek_subject_key(&self) -> &CertifiedKey {
560 &self.cert.cert_key
561 }
562 pub fn peek_signing_key(&self) -> &ed25519::Ed25519Identity {
564 self.cert
565 .signed_with
566 .as_ref()
567 .expect("Made an UncheckedCert without a signing key")
568 }
569}
570
571impl tor_checkable::SelfSigned<SigCheckedCert> for UncheckedCert {
572 type Error = CertError;
573
574 fn is_well_signed(&self) -> CertResult<()> {
575 let pubkey = &self.cert.signed_with.ok_or(CertError::MissingPubKey)?;
576 let pubkey: ed25519::PublicKey = pubkey.try_into().map_err(|_| CertError::BadSignature)?;
577
578 pubkey
579 .verify(&self.text[..], &self.signature)
580 .map_err(|_| CertError::BadSignature)?;
581
582 Ok(())
583 }
584
585 fn dangerously_assume_wellsigned(self) -> SigCheckedCert {
586 SigCheckedCert { cert: self.cert }
587 }
588}
589
590impl tor_checkable::Timebound<Ed25519Cert> for Ed25519Cert {
591 type Error = tor_checkable::TimeValidityError;
592
593 fn is_valid_at(&self, t: &time::SystemTime) -> Result<(), Self::Error> {
594 if self.is_expired_at(*t) {
595 let expiry = self.expiry();
596 Err(Self::Error::Expired(
597 t.duration_since(expiry)
598 .expect("certificate expiry time inconsistent"),
599 ))
600 } else {
601 Ok(())
602 }
603 }
604
605 fn dangerously_assume_timely(self) -> Ed25519Cert {
606 self
607 }
608}
609
610impl tor_checkable::Timebound<Ed25519Cert> for SigCheckedCert {
611 type Error = tor_checkable::TimeValidityError;
612 fn is_valid_at(&self, t: &time::SystemTime) -> std::result::Result<(), Self::Error> {
613 self.cert.is_valid_at(t)
614 }
615
616 fn dangerously_assume_timely(self) -> Ed25519Cert {
617 self.cert.dangerously_assume_timely()
618 }
619}
620
621#[cfg(test)]
622mod test {
623 #![allow(clippy::bool_assert_comparison)]
625 #![allow(clippy::clone_on_copy)]
626 #![allow(clippy::dbg_macro)]
627 #![allow(clippy::mixed_attributes_style)]
628 #![allow(clippy::print_stderr)]
629 #![allow(clippy::print_stdout)]
630 #![allow(clippy::single_char_pattern)]
631 #![allow(clippy::unwrap_used)]
632 #![allow(clippy::unchecked_time_subtraction)]
633 #![allow(clippy::useless_vec)]
634 #![allow(clippy::needless_pass_by_value)]
635 use super::*;
637 use hex_literal::hex;
638
639 #[test]
640 fn parse_unrecognized_ext() -> BytesResult<()> {
641 let b = hex!("0009 99 10 657874656e73696f6e");
643 let mut r = Reader::from_slice(&b);
644 let e: CertExt = r.extract()?;
645 r.should_be_exhausted()?;
646
647 assert_eq!(e.ext_id(), 0x99.into());
648
649 let b = hex!("0009 99 11 657874656e73696f6e");
652 let mut r = Reader::from_slice(&b);
653 let e: Result<CertExt, BytesError> = r.extract();
654 assert!(e.is_err());
655 assert_eq!(
656 e.err().unwrap(),
657 BytesError::InvalidMessage(
658 "unrecognized certificate extension, with 'affects_validation' flag set.".into()
659 )
660 );
661
662 Ok(())
663 }
664
665 #[test]
666 fn certified_key() -> BytesResult<()> {
667 let b =
668 hex!("4c27616d6f757220756e6974206365757820717527656e636861c3ae6e616974206c6520666572");
669 let mut r = Reader::from_slice(&b);
670
671 let ck = CertifiedKey::from_reader(KeyType::SHA256_OF_RSA, &mut r)?;
672 assert_eq!(ck.as_bytes(), &b[..32]);
673 assert_eq!(ck.key_type(), KeyType::SHA256_OF_RSA);
674 assert_eq!(r.remaining(), 7);
675
676 let mut r = Reader::from_slice(&b);
677 let ck = CertifiedKey::from_reader(42.into(), &mut r)?;
678 assert_eq!(ck.as_bytes(), &b[..32]);
679 assert_eq!(ck.key_type(), 42.into());
680 assert_eq!(r.remaining(), 7);
681
682 Ok(())
683 }
684}