1#![cfg_attr(docsrs, feature(doc_auto_cfg, 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_duration_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)] mod err;
47pub mod rsa;
48
49use caret::caret_int;
50use tor_bytes::{Error as BytesError, Result as BytesResult};
51use tor_bytes::{Readable, Reader};
52use tor_llcrypto::pk::*;
53
54use std::time;
55
56pub use err::CertError;
57
58#[cfg(feature = "encode")]
59mod encode;
60#[cfg(feature = "encode")]
61pub use encode::EncodedEd25519Cert;
62#[cfg(feature = "encode")]
63pub use err::CertEncodeError;
64
65type CertResult<T> = std::result::Result<T, CertError>;
67
68caret_int! {
69 pub struct CertType(u8) {
80 TLS_LINK_X509 = 0x01,
82 RSA_ID_X509 = 0x02,
84 LINK_AUTH_X509 = 0x03,
87
88 IDENTITY_V_SIGNING = 0x04,
90
91 SIGNING_V_TLS_CERT = 0x05,
93
94 SIGNING_V_LINK_AUTH = 0x06,
96
97 RSA_ID_V_IDENTITY = 0x07,
100
101 HS_BLINDED_ID_V_SIGNING = 0x08,
105
106 HS_IP_V_SIGNING = 0x09,
121
122 NTOR_CC_IDENTITY = 0x0A,
125
126 HS_IP_CC_SIGNING = 0x0B,
135
136 FAMILY_V_IDENTITY = 0x0C,
139 }
140}
141
142caret_int! {
143 pub struct ExtType(u8) {
145 SIGNED_WITH_ED25519_KEY = 0x04,
149 }
150}
151
152caret_int! {
153 pub struct KeyType(u8) {
155 ED25519_KEY = 0x01,
157 SHA256_OF_RSA = 0x02,
159 SHA256_OF_X509 = 0x03,
161 }
162}
163
164#[derive(Debug, Clone)]
167#[cfg_attr(feature = "encode", derive(derive_builder::Builder))]
168#[cfg_attr(
169 feature = "encode",
170 builder(name = "Ed25519CertConstructor", build_fn(skip))
171)]
172pub struct Ed25519Cert {
173 #[cfg_attr(feature = "encode", builder(setter(custom)))]
175 exp_hours: u32,
176 cert_type: CertType,
178 cert_key: CertifiedKey,
180 #[allow(unused)]
182 #[cfg_attr(feature = "encode", builder(setter(custom)))]
183 extensions: Vec<CertExt>,
184 #[cfg_attr(feature = "encode", builder(setter(custom)))]
190 signed_with: Option<ed25519::Ed25519Identity>,
191}
192
193#[derive(Debug, Clone)]
195#[non_exhaustive]
196pub enum CertifiedKey {
197 Ed25519(ed25519::Ed25519Identity),
199 RsaSha256Digest([u8; 32]),
201 X509Sha256Digest([u8; 32]),
203 Unrecognized(UnrecognizedKey),
205}
206
207#[derive(Debug, Clone)]
209pub struct UnrecognizedKey {
210 key_type: KeyType,
212 key_digest: [u8; 32],
214}
215
216impl CertifiedKey {
217 pub fn key_type(&self) -> KeyType {
219 match self {
220 CertifiedKey::Ed25519(_) => KeyType::ED25519_KEY,
221 CertifiedKey::RsaSha256Digest(_) => KeyType::SHA256_OF_RSA,
222 CertifiedKey::X509Sha256Digest(_) => KeyType::SHA256_OF_X509,
223
224 CertifiedKey::Unrecognized(u) => u.key_type,
225 }
226 }
227 pub fn as_bytes(&self) -> &[u8] {
230 match self {
231 CertifiedKey::Ed25519(k) => k.as_bytes(),
232 CertifiedKey::RsaSha256Digest(k) => &k[..],
233 CertifiedKey::X509Sha256Digest(k) => &k[..],
234 CertifiedKey::Unrecognized(u) => &u.key_digest[..],
235 }
236 }
237 pub fn as_ed25519(&self) -> Option<&ed25519::Ed25519Identity> {
240 match self {
241 CertifiedKey::Ed25519(k) => Some(k),
242 _ => None,
243 }
244 }
245 fn from_reader(key_type: KeyType, r: &mut Reader<'_>) -> BytesResult<Self> {
248 Ok(match key_type {
249 KeyType::ED25519_KEY => CertifiedKey::Ed25519(r.extract()?),
250 KeyType::SHA256_OF_RSA => CertifiedKey::RsaSha256Digest(r.extract()?),
251 KeyType::SHA256_OF_X509 => CertifiedKey::X509Sha256Digest(r.extract()?),
252 _ => CertifiedKey::Unrecognized(UnrecognizedKey {
253 key_type,
254 key_digest: r.extract()?,
255 }),
256 })
257 }
258}
259
260#[derive(Debug, Clone)]
262enum CertExt {
263 SignedWithEd25519(SignedWithEd25519Ext),
265 Unrecognized(UnrecognizedExt),
267}
268
269#[derive(Debug, Clone)]
271#[allow(unused)]
272struct UnrecognizedExt {
273 affects_validation: bool,
276 ext_type: ExtType,
278 body: Vec<u8>,
280}
281
282impl CertExt {
283 fn ext_id(&self) -> ExtType {
285 match self {
286 CertExt::SignedWithEd25519(_) => ExtType::SIGNED_WITH_ED25519_KEY,
287 CertExt::Unrecognized(u) => u.ext_type,
288 }
289 }
290}
291
292#[derive(Debug, Clone)]
294struct SignedWithEd25519Ext {
295 pk: ed25519::Ed25519Identity,
297}
298
299impl Readable for CertExt {
300 fn take_from(b: &mut Reader<'_>) -> BytesResult<Self> {
301 let len = b.take_u16()?;
302 let ext_type: ExtType = b.take_u8()?.into();
303 let flags = b.take_u8()?;
304 let body = b.take(len as usize)?;
305
306 Ok(match ext_type {
307 ExtType::SIGNED_WITH_ED25519_KEY => CertExt::SignedWithEd25519(SignedWithEd25519Ext {
308 pk: ed25519::Ed25519Identity::from_bytes(body).ok_or_else(|| {
309 BytesError::InvalidMessage("wrong length on Ed25519 key".into())
310 })?,
311 }),
312 _ => {
313 if (flags & 1) != 0 {
314 return Err(BytesError::InvalidMessage(
315 "unrecognized certificate extension, with 'affects_validation' flag set."
316 .into(),
317 ));
318 }
319 CertExt::Unrecognized(UnrecognizedExt {
320 affects_validation: false,
321 ext_type,
322 body: body.into(),
323 })
324 }
325 })
326 }
327}
328
329impl Ed25519Cert {
330 pub fn decode(cert: &[u8]) -> BytesResult<KeyUnknownCert> {
339 let mut r = Reader::from_slice(cert);
340 let v = r.take_u8()?;
341 if v != 1 {
342 return Err(BytesError::InvalidMessage(
345 "Unrecognized certificate version".into(),
346 ));
347 }
348 let cert_type = r.take_u8()?.into();
349 let exp_hours = r.take_u32()?;
350 let mut cert_key_type = r.take_u8()?.into();
351
352 if cert_type == CertType::SIGNING_V_TLS_CERT && cert_key_type == KeyType::ED25519_KEY {
356 cert_key_type = KeyType::SHA256_OF_X509;
357 }
358
359 let cert_key = CertifiedKey::from_reader(cert_key_type, &mut r)?;
360 let n_exts = r.take_u8()?;
361 let mut extensions = Vec::new();
362 for _ in 0..n_exts {
363 let e: CertExt = r.extract()?;
364 extensions.push(e);
365 }
366
367 let sig_offset = r.consumed();
368 let signature: ed25519::Signature = r.extract()?;
369 r.should_be_exhausted()?;
370
371 let keyext = extensions
372 .iter()
373 .find(|e| e.ext_id() == ExtType::SIGNED_WITH_ED25519_KEY);
374
375 let included_pkey = match keyext {
376 Some(CertExt::SignedWithEd25519(s)) => Some(s.pk),
377 _ => None,
378 };
379
380 Ok(KeyUnknownCert {
381 cert: UncheckedCert {
382 cert: Ed25519Cert {
383 exp_hours,
384 cert_type,
385 cert_key,
386 extensions,
387
388 signed_with: included_pkey,
389 },
390 text: cert[0..sig_offset].into(),
391 signature,
392 },
393 })
394 }
395
396 pub fn expiry(&self) -> std::time::SystemTime {
398 let d = std::time::Duration::new(u64::from(self.exp_hours) * 3600, 0);
399 std::time::SystemTime::UNIX_EPOCH + d
400 }
401
402 pub fn is_expired_at(&self, when: std::time::SystemTime) -> bool {
404 when >= self.expiry()
405 }
406
407 pub fn subject_key(&self) -> &CertifiedKey {
410 &self.cert_key
411 }
412
413 pub fn signing_key(&self) -> Option<&ed25519::Ed25519Identity> {
415 self.signed_with.as_ref()
416 }
417
418 pub fn cert_type(&self) -> CertType {
420 self.cert_type
421 }
422}
423
424#[derive(Clone, Debug)]
433pub struct KeyUnknownCert {
434 cert: UncheckedCert,
436}
437
438impl KeyUnknownCert {
439 pub fn peek_cert_type(&self) -> CertType {
441 self.cert.cert.cert_type
442 }
443 pub fn peek_subject_key(&self) -> &CertifiedKey {
445 &self.cert.cert.cert_key
446 }
447
448 #[deprecated(
456 since = "0.7.1",
457 note = "Use should_have_signing_key or should_be_signed_with instead."
458 )]
459 pub fn check_key(self, pkey: Option<&ed25519::Ed25519Identity>) -> CertResult<UncheckedCert> {
460 match pkey {
461 Some(wanted) => self.should_be_signed_with(wanted),
462 None => self.should_have_signing_key(),
463 }
464 }
465
466 pub fn should_have_signing_key(self) -> CertResult<UncheckedCert> {
473 let real_key = match &self.cert.cert.signed_with {
474 Some(a) => *a,
475 None => return Err(CertError::MissingPubKey),
476 };
477
478 Ok(UncheckedCert {
479 cert: Ed25519Cert {
480 signed_with: Some(real_key),
481 ..self.cert.cert
482 },
483 ..self.cert
484 })
485 }
486
487 pub fn should_be_signed_with(
493 self,
494 pkey: &ed25519::Ed25519Identity,
495 ) -> CertResult<UncheckedCert> {
496 let real_key = match &self.cert.cert.signed_with {
497 Some(a) if a == pkey => *pkey,
498 None => *pkey,
499 Some(_) => return Err(CertError::KeyMismatch),
500 };
501
502 Ok(UncheckedCert {
503 cert: Ed25519Cert {
504 signed_with: Some(real_key),
505 ..self.cert.cert
506 },
507 ..self.cert
508 })
509 }
510}
511
512#[derive(Debug, Clone)]
515pub struct UncheckedCert {
516 cert: Ed25519Cert,
519
520 text: Vec<u8>,
525
526 signature: ed25519::Signature,
528}
529
530pub struct SigCheckedCert {
533 cert: Ed25519Cert,
535}
536
537impl UncheckedCert {
538 pub fn dangerously_split(
541 self,
542 ) -> CertResult<(SigCheckedCert, ed25519::ValidatableEd25519Signature)> {
543 use tor_checkable::SelfSigned;
544 let signing_key = self.cert.signed_with.ok_or(CertError::MissingPubKey)?;
545 let signing_key = signing_key
546 .try_into()
547 .map_err(|_| CertError::BadSignature)?;
548 let signature =
549 ed25519::ValidatableEd25519Signature::new(signing_key, self.signature, &self.text[..]);
550 Ok((self.dangerously_assume_wellsigned(), signature))
551 }
552
553 pub fn peek_subject_key(&self) -> &CertifiedKey {
555 &self.cert.cert_key
556 }
557 pub fn peek_signing_key(&self) -> &ed25519::Ed25519Identity {
559 self.cert
560 .signed_with
561 .as_ref()
562 .expect("Made an UncheckedCert without a signing key")
563 }
564}
565
566impl tor_checkable::SelfSigned<SigCheckedCert> for UncheckedCert {
567 type Error = CertError;
568
569 fn is_well_signed(&self) -> CertResult<()> {
570 let pubkey = &self.cert.signed_with.ok_or(CertError::MissingPubKey)?;
571 let pubkey: ed25519::PublicKey = pubkey.try_into().map_err(|_| CertError::BadSignature)?;
572
573 pubkey
574 .verify(&self.text[..], &self.signature)
575 .map_err(|_| CertError::BadSignature)?;
576
577 Ok(())
578 }
579
580 fn dangerously_assume_wellsigned(self) -> SigCheckedCert {
581 SigCheckedCert { cert: self.cert }
582 }
583}
584
585impl tor_checkable::Timebound<Ed25519Cert> for Ed25519Cert {
586 type Error = tor_checkable::TimeValidityError;
587
588 fn is_valid_at(&self, t: &time::SystemTime) -> Result<(), Self::Error> {
589 if self.is_expired_at(*t) {
590 let expiry = self.expiry();
591 Err(Self::Error::Expired(
592 t.duration_since(expiry)
593 .expect("certificate expiry time inconsistent"),
594 ))
595 } else {
596 Ok(())
597 }
598 }
599
600 fn dangerously_assume_timely(self) -> Ed25519Cert {
601 self
602 }
603}
604
605impl tor_checkable::Timebound<Ed25519Cert> for SigCheckedCert {
606 type Error = tor_checkable::TimeValidityError;
607 fn is_valid_at(&self, t: &time::SystemTime) -> std::result::Result<(), Self::Error> {
608 self.cert.is_valid_at(t)
609 }
610
611 fn dangerously_assume_timely(self) -> Ed25519Cert {
612 self.cert.dangerously_assume_timely()
613 }
614}
615
616#[cfg(test)]
617mod test {
618 #![allow(clippy::bool_assert_comparison)]
620 #![allow(clippy::clone_on_copy)]
621 #![allow(clippy::dbg_macro)]
622 #![allow(clippy::mixed_attributes_style)]
623 #![allow(clippy::print_stderr)]
624 #![allow(clippy::print_stdout)]
625 #![allow(clippy::single_char_pattern)]
626 #![allow(clippy::unwrap_used)]
627 #![allow(clippy::unchecked_duration_subtraction)]
628 #![allow(clippy::useless_vec)]
629 #![allow(clippy::needless_pass_by_value)]
630 use super::*;
632 use hex_literal::hex;
633
634 #[test]
635 fn parse_unrecognized_ext() -> BytesResult<()> {
636 let b = hex!("0009 99 10 657874656e73696f6e");
638 let mut r = Reader::from_slice(&b);
639 let e: CertExt = r.extract()?;
640 r.should_be_exhausted()?;
641
642 assert_eq!(e.ext_id(), 0x99.into());
643
644 let b = hex!("0009 99 11 657874656e73696f6e");
647 let mut r = Reader::from_slice(&b);
648 let e: Result<CertExt, BytesError> = r.extract();
649 assert!(e.is_err());
650 assert_eq!(
651 e.err().unwrap(),
652 BytesError::InvalidMessage(
653 "unrecognized certificate extension, with 'affects_validation' flag set.".into()
654 )
655 );
656
657 Ok(())
658 }
659
660 #[test]
661 fn certified_key() -> BytesResult<()> {
662 let b =
663 hex!("4c27616d6f757220756e6974206365757820717527656e636861c3ae6e616974206c6520666572");
664 let mut r = Reader::from_slice(&b);
665
666 let ck = CertifiedKey::from_reader(KeyType::SHA256_OF_RSA, &mut r)?;
667 assert_eq!(ck.as_bytes(), &b[..32]);
668 assert_eq!(ck.key_type(), KeyType::SHA256_OF_RSA);
669 assert_eq!(r.remaining(), 7);
670
671 let mut r = Reader::from_slice(&b);
672 let ck = CertifiedKey::from_reader(42.into(), &mut r)?;
673 assert_eq!(ck.as_bytes(), &b[..32]);
674 assert_eq!(ck.key_type(), 42.into());
675 assert_eq!(r.remaining(), 7);
676
677 Ok(())
678 }
679}