Skip to main content

tor_cert/
rsa.rs

1//! RSA->Ed25519 cross-certificates
2//!
3//! These are used in the Tor link handshake to prove that a given ed25519
4//! key speaks for a given (deprecated) RSA identity.
5
6use tor_bytes::Reader;
7use tor_checkable::{ExternallySigned, timed::TimerangeBound};
8use tor_llcrypto as ll;
9
10use digest::Digest;
11
12use crate::CertType;
13
14/// A RSA->Ed25519 cross-certificate
15///
16/// This kind of certificate is used in the channel handshake to prove
17/// that the Ed25519 identity key speaks on behalf of the RSA identity key.
18///
19/// (There is no converse type for certifying Ed25519 identity keys with
20/// RSA identity keys, since the RSA identity keys are too weak to trust.)
21#[must_use]
22pub struct RsaCrosscert {
23    /// The key that is being certified
24    subject_key: ll::pk::ed25519::Ed25519Identity,
25    /// The expiration time of this certificate, in hours since the
26    /// unix epoch.
27    exp_hours: u32,
28    /// The digest of the signed part of the certificate (for checking)
29    digest: [u8; 32],
30    /// The (alleged) signature on the certificate.
31    signature: Vec<u8>,
32}
33
34impl RsaCrosscert {
35    /// Return the time at which this certificate becomes expired
36    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    /// Return a reference to the digest.
42    pub fn digest(&self) -> &[u8; 32] {
43        &self.digest
44    }
45
46    /// Return true if the subject key in this certificate matches `other`
47    pub fn subject_key_matches(&self, other: &ll::pk::ed25519::Ed25519Identity) -> bool {
48        other == &self.subject_key
49    }
50
51    /// Return this certificate cert type.
52    pub fn cert_type(&self) -> CertType {
53        CertType::RSA_ID_V_IDENTITY
54    }
55
56    /// Decode a slice of bytes into an RSA crosscert.
57    pub fn decode(bytes: &[u8]) -> tor_bytes::Result<UncheckedRsaCrosscert> {
58        let mut r = Reader::from_slice(bytes);
59        let signed_portion = r.peek(36)?; // TODO(nickm): a bit ugly.
60        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
81/// An RsaCrosscert whose signature has not been checked.
82pub 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        // there is no way to check except for trying to verify the signature
91        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}