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#[must_use]
22pub struct RsaCrosscert {
23 subject_key: ll::pk::ed25519::Ed25519Identity,
25 exp_hours: u32,
28 digest: [u8; 32],
30 signature: Vec<u8>,
32}
33
34impl RsaCrosscert {
35 pub fn expiry(&self) -> std::time::SystemTime {
37 let d = std::time::Duration::new(u64::from(self.exp_hours) * 3600, 0);
38 std::time::SystemTime::UNIX_EPOCH + d
39 }
40
41 pub fn digest(&self) -> &[u8; 32] {
43 &self.digest
44 }
45
46 pub fn subject_key_matches(&self, other: &ll::pk::ed25519::Ed25519Identity) -> bool {
48 other == &self.subject_key
49 }
50
51 pub fn cert_type(&self) -> CertType {
53 CertType::RSA_ID_V_IDENTITY
54 }
55
56 pub fn decode(bytes: &[u8]) -> tor_bytes::Result<UncheckedRsaCrosscert> {
58 let mut r = Reader::from_slice(bytes);
59 let signed_portion = r.peek(36)?; let subject_key = r.extract()?;
61 let exp_hours = r.take_u32()?;
62 let siglen = r.take_u8()?;
63 let signature = r.take(siglen as usize)?.into();
64
65 let mut d = ll::d::Sha256::new();
66 d.update(&b"Tor TLS RSA/Ed25519 cross-certificate"[..]);
67 d.update(signed_portion);
68 let digest = d.finalize().into();
69
70 let cc = RsaCrosscert {
71 subject_key,
72 exp_hours,
73 digest,
74 signature,
75 };
76
77 Ok(UncheckedRsaCrosscert(cc))
78 }
79}
80
81pub struct UncheckedRsaCrosscert(RsaCrosscert);
83
84impl ExternallySigned<TimerangeBound<RsaCrosscert>> for UncheckedRsaCrosscert {
85 type Key = ll::pk::rsa::PublicKey;
86 type KeyHint = ();
87 type Error = tor_bytes::Error;
88
89 fn key_is_correct(&self, _k: &Self::Key) -> Result<(), Self::KeyHint> {
90 Ok(())
92 }
93
94 fn is_well_signed(&self, k: &Self::Key) -> Result<(), Self::Error> {
95 k.verify(&self.0.digest[..], &self.0.signature[..])
96 .map_err(|_| {
97 tor_bytes::Error::InvalidMessage(
98 "Invalid signature on RSA->Ed identity crosscert".into(),
99 )
100 })?;
101 Ok(())
102 }
103
104 fn dangerously_assume_wellsigned(self) -> TimerangeBound<RsaCrosscert> {
105 let expiration = self.0.expiry();
106 TimerangeBound::new(self.0, ..expiration)
107 }
108}