1use tor_bytes::Reader;
7use tor_checkable::{ExternallySigned, timed::TimerangeBound};
8use tor_llcrypto as ll;
9
10use digest::Digest;
11
12use crate::CertType;
13
14#[cfg(feature = "encode")]
15mod encode;
16#[cfg(feature = "encode")]
17pub use encode::EncodedRsaCrosscert;
18
19#[must_use]
27pub struct RsaCrosscert {
28 subject_key: ll::pk::ed25519::Ed25519Identity,
30 exp_hours: u32,
33 digest: [u8; 32],
35 signature: Vec<u8>,
37}
38
39const SECS_PER_HOUR: u64 = 3600;
41
42const PREFIX: &[u8] = b"Tor TLS RSA/Ed25519 cross-certificate";
44
45fn compute_digest(c: &[u8]) -> [u8; 32] {
47 let mut d = ll::d::Sha256::new();
48 d.update(PREFIX);
49 d.update(c);
50 d.finalize().into()
51}
52
53impl RsaCrosscert {
54 pub fn expiry(&self) -> std::time::SystemTime {
56 let d = std::time::Duration::new(u64::from(self.exp_hours) * SECS_PER_HOUR, 0);
57 std::time::SystemTime::UNIX_EPOCH + d
58 }
59
60 pub fn digest(&self) -> &[u8; 32] {
62 &self.digest
63 }
64
65 pub fn subject_key_matches(&self, other: &ll::pk::ed25519::Ed25519Identity) -> bool {
67 other == &self.subject_key
68 }
69
70 pub fn cert_type(&self) -> CertType {
72 CertType::RSA_ID_V_IDENTITY
73 }
74
75 pub fn decode(bytes: &[u8]) -> tor_bytes::Result<UncheckedRsaCrosscert> {
77 let mut r = Reader::from_slice(bytes);
78 let signed_portion = r.peek(36)?; let subject_key = r.extract()?;
80 let exp_hours = r.take_u32()?;
81 let siglen = r.take_u8()?;
82 let signature = r.take(siglen as usize)?.into();
83
84 let digest = compute_digest(signed_portion);
85
86 let cc = RsaCrosscert {
87 subject_key,
88 exp_hours,
89 digest,
90 signature,
91 };
92
93 Ok(UncheckedRsaCrosscert(cc))
94 }
95}
96
97pub struct UncheckedRsaCrosscert(RsaCrosscert);
99
100impl ExternallySigned<TimerangeBound<RsaCrosscert>> for UncheckedRsaCrosscert {
101 type Key = ll::pk::rsa::PublicKey;
102 type KeyHint = ();
103 type Error = tor_bytes::Error;
104
105 fn key_is_correct(&self, _k: &Self::Key) -> Result<(), Self::KeyHint> {
106 Ok(())
108 }
109
110 fn is_well_signed(&self, k: &Self::Key) -> Result<(), Self::Error> {
111 k.verify(&self.0.digest[..], &self.0.signature[..])
112 .map_err(|_| {
113 tor_bytes::Error::InvalidMessage(
114 "Invalid signature on RSA->Ed identity crosscert".into(),
115 )
116 })?;
117 Ok(())
118 }
119
120 fn dangerously_assume_wellsigned(self) -> TimerangeBound<RsaCrosscert> {
121 let expiration = self.0.expiry();
122 TimerangeBound::new(self.0, ..expiration)
123 }
124}