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)] #![allow(clippy::collapsible_if)] #![deny(clippy::unused_async)]
47mod err;
50pub mod rsa;
51
52#[cfg(feature = "x509")]
53pub use tor_cert_x509 as x509;
54
55use caret::caret_int;
56use tor_bytes::{Error as BytesError, Result as BytesResult};
57use tor_bytes::{Readable, Reader};
58use tor_llcrypto::pk::*;
59
60use std::time;
61
62pub use err::CertError;
63
64#[cfg(feature = "encode")]
65mod encode;
66#[cfg(feature = "encode")]
67pub use encode::EncodedEd25519Cert;
68#[cfg(feature = "encode")]
69pub use err::CertEncodeError;
70
71type CertResult<T> = std::result::Result<T, CertError>;
73
74caret_int! {
75 pub struct CertType(u8) {
86 TLS_LINK_X509 = 0x01,
88 RSA_ID_X509 = 0x02,
90 LINK_AUTH_X509 = 0x03,
93
94 IDENTITY_V_SIGNING = 0x04,
96
97 SIGNING_V_TLS_CERT = 0x05,
99
100 SIGNING_V_LINK_AUTH = 0x06,
102
103 RSA_ID_V_IDENTITY = 0x07,
106
107 HS_BLINDED_ID_V_SIGNING = 0x08,
111
112 HS_IP_V_SIGNING = 0x09,
127
128 NTOR_CC_IDENTITY = 0x0A,
131
132 HS_IP_CC_SIGNING = 0x0B,
141
142 FAMILY_V_IDENTITY = 0x0C,
145 }
146}
147
148caret_int! {
149 pub struct ExtType(u8) {
151 SIGNED_WITH_ED25519_KEY = 0x04,
155 }
156}
157
158caret_int! {
159 pub struct KeyType(u8) {
161 ED25519_KEY = 0x01,
163 SHA256_OF_RSA = 0x02,
165 SHA256_OF_X509 = 0x03,
167 }
168}
169
170#[derive(Debug, Clone)]
173#[cfg_attr(feature = "encode", derive(derive_builder::Builder))]
174#[cfg_attr(
175 feature = "encode",
176 builder(name = "Ed25519CertConstructor", build_fn(skip))
177)]
178pub struct Ed25519Cert {
179 #[cfg_attr(feature = "encode", builder(setter(custom)))]
181 exp_hours: u32,
182 cert_type: CertType,
184 cert_key: CertifiedKey,
186 #[allow(unused)]
188 #[cfg_attr(feature = "encode", builder(setter(custom)))]
189 extensions: Vec<CertExt>,
190 #[cfg_attr(feature = "encode", builder(setter(custom)))]
196 signed_with: Option<ed25519::Ed25519Identity>,
197}
198
199#[derive(Debug, Clone)]
201#[non_exhaustive]
202pub enum CertifiedKey {
203 Ed25519(ed25519::Ed25519Identity),
205 RsaSha256Digest([u8; 32]),
207 X509Sha256Digest([u8; 32]),
209 Unrecognized(UnrecognizedKey),
211}
212
213#[derive(Debug, Clone)]
215pub struct UnrecognizedKey {
216 key_type: KeyType,
218 key_digest: [u8; 32],
220}
221
222impl CertifiedKey {
223 pub fn key_type(&self) -> KeyType {
225 match self {
226 CertifiedKey::Ed25519(_) => KeyType::ED25519_KEY,
227 CertifiedKey::RsaSha256Digest(_) => KeyType::SHA256_OF_RSA,
228 CertifiedKey::X509Sha256Digest(_) => KeyType::SHA256_OF_X509,
229
230 CertifiedKey::Unrecognized(u) => u.key_type,
231 }
232 }
233 pub fn as_bytes(&self) -> &[u8] {
236 match self {
237 CertifiedKey::Ed25519(k) => k.as_bytes(),
238 CertifiedKey::RsaSha256Digest(k) => &k[..],
239 CertifiedKey::X509Sha256Digest(k) => &k[..],
240 CertifiedKey::Unrecognized(u) => &u.key_digest[..],
241 }
242 }
243 pub fn as_ed25519(&self) -> Option<&ed25519::Ed25519Identity> {
246 match self {
247 CertifiedKey::Ed25519(k) => Some(k),
248 _ => None,
249 }
250 }
251 fn from_reader(key_type: KeyType, r: &mut Reader<'_>) -> BytesResult<Self> {
254 Ok(match key_type {
255 KeyType::ED25519_KEY => CertifiedKey::Ed25519(r.extract()?),
256 KeyType::SHA256_OF_RSA => CertifiedKey::RsaSha256Digest(r.extract()?),
257 KeyType::SHA256_OF_X509 => CertifiedKey::X509Sha256Digest(r.extract()?),
258 _ => CertifiedKey::Unrecognized(UnrecognizedKey {
259 key_type,
260 key_digest: r.extract()?,
261 }),
262 })
263 }
264}
265
266#[derive(Debug, Clone)]
268enum CertExt {
269 SignedWithEd25519(SignedWithEd25519Ext),
271 Unrecognized(UnrecognizedExt),
273}
274
275#[derive(Debug, Clone)]
277#[allow(unused)]
278struct UnrecognizedExt {
279 affects_validation: bool,
282 ext_type: ExtType,
284 body: Vec<u8>,
286}
287
288impl CertExt {
289 fn ext_id(&self) -> ExtType {
291 match self {
292 CertExt::SignedWithEd25519(_) => ExtType::SIGNED_WITH_ED25519_KEY,
293 CertExt::Unrecognized(u) => u.ext_type,
294 }
295 }
296}
297
298#[derive(Debug, Clone)]
300struct SignedWithEd25519Ext {
301 pk: ed25519::Ed25519Identity,
303}
304
305impl Readable for CertExt {
306 fn take_from(b: &mut Reader<'_>) -> BytesResult<Self> {
307 let len = b.take_u16()?;
308 let ext_type: ExtType = b.take_u8()?.into();
309 let flags = b.take_u8()?;
310 let body = b.take(len as usize)?;
311
312 Ok(match ext_type {
313 ExtType::SIGNED_WITH_ED25519_KEY => CertExt::SignedWithEd25519(SignedWithEd25519Ext {
314 pk: ed25519::Ed25519Identity::from_bytes(body).ok_or_else(|| {
315 BytesError::InvalidMessage("wrong length on Ed25519 key".into())
316 })?,
317 }),
318 _ => {
319 if (flags & 1) != 0 {
320 return Err(BytesError::InvalidMessage(
321 "unrecognized certificate extension, with 'affects_validation' flag set."
322 .into(),
323 ));
324 }
325 CertExt::Unrecognized(UnrecognizedExt {
326 affects_validation: false,
327 ext_type,
328 body: body.into(),
329 })
330 }
331 })
332 }
333}
334
335impl Ed25519Cert {
336 pub fn decode(cert: &[u8]) -> BytesResult<KeyUnknownCert> {
345 let mut r = Reader::from_slice(cert);
346 let v = r.take_u8()?;
347 if v != 1 {
348 return Err(BytesError::InvalidMessage(
351 "Unrecognized certificate version".into(),
352 ));
353 }
354 let cert_type = r.take_u8()?.into();
355 let exp_hours = r.take_u32()?;
356 let mut cert_key_type = r.take_u8()?.into();
357
358 if cert_type == CertType::SIGNING_V_TLS_CERT && cert_key_type == KeyType::ED25519_KEY {
362 cert_key_type = KeyType::SHA256_OF_X509;
363 }
364
365 let cert_key = CertifiedKey::from_reader(cert_key_type, &mut r)?;
366 let n_exts = r.take_u8()?;
367 let mut extensions = Vec::new();
368 for _ in 0..n_exts {
369 let e: CertExt = r.extract()?;
370 extensions.push(e);
371 }
372
373 let sig_offset = r.consumed();
374 let signature: ed25519::Signature = r.extract()?;
375 r.should_be_exhausted()?;
376
377 let keyext = extensions
378 .iter()
379 .find(|e| e.ext_id() == ExtType::SIGNED_WITH_ED25519_KEY);
380
381 let included_pkey = match keyext {
382 Some(CertExt::SignedWithEd25519(s)) => Some(s.pk),
383 _ => None,
384 };
385
386 Ok(KeyUnknownCert {
387 cert: UncheckedCert {
388 cert: Ed25519Cert {
389 exp_hours,
390 cert_type,
391 cert_key,
392 extensions,
393
394 signed_with: included_pkey,
395 },
396 text: cert[0..sig_offset].into(),
397 signature,
398 },
399 })
400 }
401
402 pub fn expiry(&self) -> std::time::SystemTime {
404 let d = std::time::Duration::new(u64::from(self.exp_hours) * 3600, 0);
405 std::time::SystemTime::UNIX_EPOCH + d
406 }
407
408 pub fn is_expired_at(&self, when: std::time::SystemTime) -> bool {
410 when >= self.expiry()
411 }
412
413 pub fn subject_key(&self) -> &CertifiedKey {
416 &self.cert_key
417 }
418
419 pub fn signing_key(&self) -> Option<&ed25519::Ed25519Identity> {
421 self.signed_with.as_ref()
422 }
423
424 pub fn cert_type(&self) -> CertType {
426 self.cert_type
427 }
428}
429
430#[derive(Clone, Debug)]
439pub struct KeyUnknownCert {
440 cert: UncheckedCert,
442}
443
444impl KeyUnknownCert {
445 pub fn peek_cert_type(&self) -> CertType {
447 self.cert.cert.cert_type
448 }
449 pub fn peek_subject_key(&self) -> &CertifiedKey {
451 &self.cert.cert.cert_key
452 }
453
454 #[deprecated(
462 since = "0.7.1",
463 note = "Use should_have_signing_key or should_be_signed_with instead."
464 )]
465 pub fn check_key(self, pkey: Option<&ed25519::Ed25519Identity>) -> CertResult<UncheckedCert> {
466 match pkey {
467 Some(wanted) => self.should_be_signed_with(wanted),
468 None => self.should_have_signing_key(),
469 }
470 }
471
472 pub fn should_have_signing_key(self) -> CertResult<UncheckedCert> {
479 let real_key = match &self.cert.cert.signed_with {
480 Some(a) => *a,
481 None => return Err(CertError::MissingPubKey),
482 };
483
484 Ok(UncheckedCert {
485 cert: Ed25519Cert {
486 signed_with: Some(real_key),
487 ..self.cert.cert
488 },
489 ..self.cert
490 })
491 }
492
493 pub fn should_be_signed_with(
499 self,
500 pkey: &ed25519::Ed25519Identity,
501 ) -> CertResult<UncheckedCert> {
502 let real_key = match &self.cert.cert.signed_with {
503 Some(a) if a == pkey => *pkey,
504 None => *pkey,
505 Some(_) => return Err(CertError::KeyMismatch),
506 };
507
508 Ok(UncheckedCert {
509 cert: Ed25519Cert {
510 signed_with: Some(real_key),
511 ..self.cert.cert
512 },
513 ..self.cert
514 })
515 }
516}
517
518#[derive(Debug, Clone)]
521pub struct UncheckedCert {
522 cert: Ed25519Cert,
525
526 text: Vec<u8>,
531
532 signature: ed25519::Signature,
534}
535
536pub struct SigCheckedCert {
539 cert: Ed25519Cert,
541}
542
543impl UncheckedCert {
544 pub fn dangerously_split(
547 self,
548 ) -> CertResult<(SigCheckedCert, ed25519::ValidatableEd25519Signature)> {
549 use tor_checkable::SelfSigned;
550 let signing_key = self.cert.signed_with.ok_or(CertError::MissingPubKey)?;
551 let signing_key = signing_key
552 .try_into()
553 .map_err(|_| CertError::BadSignature)?;
554 let signature =
555 ed25519::ValidatableEd25519Signature::new(signing_key, self.signature, &self.text[..]);
556 Ok((self.dangerously_assume_wellsigned(), signature))
557 }
558
559 pub fn peek_subject_key(&self) -> &CertifiedKey {
561 &self.cert.cert_key
562 }
563 pub fn peek_signing_key(&self) -> &ed25519::Ed25519Identity {
565 self.cert
566 .signed_with
567 .as_ref()
568 .expect("Made an UncheckedCert without a signing key")
569 }
570}
571
572impl tor_checkable::SelfSigned<SigCheckedCert> for UncheckedCert {
573 type Error = CertError;
574
575 fn is_well_signed(&self) -> CertResult<()> {
576 let pubkey = &self.cert.signed_with.ok_or(CertError::MissingPubKey)?;
577 let pubkey: ed25519::PublicKey = pubkey.try_into().map_err(|_| CertError::BadSignature)?;
578
579 pubkey
580 .verify(&self.text[..], &self.signature)
581 .map_err(|_| CertError::BadSignature)?;
582
583 Ok(())
584 }
585
586 fn dangerously_assume_wellsigned(self) -> SigCheckedCert {
587 SigCheckedCert { cert: self.cert }
588 }
589}
590
591impl tor_checkable::Timebound<Ed25519Cert> for Ed25519Cert {
592 type Error = tor_checkable::TimeValidityError;
593
594 fn is_valid_at(&self, t: &time::SystemTime) -> Result<(), Self::Error> {
595 if self.is_expired_at(*t) {
596 let expiry = self.expiry();
597 Err(Self::Error::Expired(
598 t.duration_since(expiry)
599 .expect("certificate expiry time inconsistent"),
600 ))
601 } else {
602 Ok(())
603 }
604 }
605
606 fn dangerously_assume_timely(self) -> Ed25519Cert {
607 self
608 }
609}
610
611impl tor_checkable::Timebound<Ed25519Cert> for SigCheckedCert {
612 type Error = tor_checkable::TimeValidityError;
613 fn is_valid_at(&self, t: &time::SystemTime) -> std::result::Result<(), Self::Error> {
614 self.cert.is_valid_at(t)
615 }
616
617 fn dangerously_assume_timely(self) -> Ed25519Cert {
618 self.cert.dangerously_assume_timely()
619 }
620}
621
622#[cfg(test)]
623mod test {
624 #![allow(clippy::bool_assert_comparison)]
626 #![allow(clippy::clone_on_copy)]
627 #![allow(clippy::dbg_macro)]
628 #![allow(clippy::mixed_attributes_style)]
629 #![allow(clippy::print_stderr)]
630 #![allow(clippy::print_stdout)]
631 #![allow(clippy::single_char_pattern)]
632 #![allow(clippy::unwrap_used)]
633 #![allow(clippy::unchecked_time_subtraction)]
634 #![allow(clippy::useless_vec)]
635 #![allow(clippy::needless_pass_by_value)]
636 use super::*;
638 use hex_literal::hex;
639
640 #[test]
641 fn parse_unrecognized_ext() -> BytesResult<()> {
642 let b = hex!("0009 99 10 657874656e73696f6e");
644 let mut r = Reader::from_slice(&b);
645 let e: CertExt = r.extract()?;
646 r.should_be_exhausted()?;
647
648 assert_eq!(e.ext_id(), 0x99.into());
649
650 let b = hex!("0009 99 11 657874656e73696f6e");
653 let mut r = Reader::from_slice(&b);
654 let e: Result<CertExt, BytesError> = r.extract();
655 assert!(e.is_err());
656 assert_eq!(
657 e.err().unwrap(),
658 BytesError::InvalidMessage(
659 "unrecognized certificate extension, with 'affects_validation' flag set.".into()
660 )
661 );
662
663 Ok(())
664 }
665
666 #[test]
667 fn certified_key() -> BytesResult<()> {
668 let b =
669 hex!("4c27616d6f757220756e6974206365757820717527656e636861c3ae6e616974206c6520666572");
670 let mut r = Reader::from_slice(&b);
671
672 let ck = CertifiedKey::from_reader(KeyType::SHA256_OF_RSA, &mut r)?;
673 assert_eq!(ck.as_bytes(), &b[..32]);
674 assert_eq!(ck.key_type(), KeyType::SHA256_OF_RSA);
675 assert_eq!(r.remaining(), 7);
676
677 let mut r = Reader::from_slice(&b);
678 let ck = CertifiedKey::from_reader(42.into(), &mut r)?;
679 assert_eq!(ck.as_bytes(), &b[..32]);
680 assert_eq!(ck.key_type(), 42.into());
681 assert_eq!(r.remaining(), 7);
682
683 Ok(())
684 }
685}