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

            
6
use tor_bytes::Reader;
7
use tor_checkable::{ExternallySigned, timed::TimerangeBound};
8
use tor_llcrypto as ll;
9

            
10
use digest::Digest;
11

            
12
/// A RSA->Ed25519 cross-certificate
13
///
14
/// This kind of certificate is used in the channel handshake to prove
15
/// that the Ed25519 identity key speaks on behalf of the RSA identity key.
16
///
17
/// (There is no converse type for certifying Ed25519 identity keys with
18
/// RSA identity keys, since the RSA identity keys are too weak to trust.)
19
#[must_use]
20
pub struct RsaCrosscert {
21
    /// The key that is being certified
22
    subject_key: ll::pk::ed25519::Ed25519Identity,
23
    /// The expiration time of this certificate, in hours since the
24
    /// unix epoch.
25
    exp_hours: u32,
26
    /// The digest of the signed part of the certificate (for checking)
27
    digest: [u8; 32],
28
    /// The (alleged) signature on the certificate.
29
    signature: Vec<u8>,
30
}
31

            
32
impl RsaCrosscert {
33
    /// Return the time at which this certificate becomes expired
34
195
    pub fn expiry(&self) -> std::time::SystemTime {
35
195
        let d = std::time::Duration::new(u64::from(self.exp_hours) * 3600, 0);
36
195
        std::time::SystemTime::UNIX_EPOCH + d
37
195
    }
38

            
39
    /// Return a reference to the digest.
40
78
    pub fn digest(&self) -> &[u8; 32] {
41
78
        &self.digest
42
78
    }
43

            
44
    /// Return true if the subject key in this certificate matches `other`
45
195
    pub fn subject_key_matches(&self, other: &ll::pk::ed25519::Ed25519Identity) -> bool {
46
195
        other == &self.subject_key
47
195
    }
48

            
49
    /// Decode a slice of bytes into an RSA crosscert.
50
234
    pub fn decode(bytes: &[u8]) -> tor_bytes::Result<UncheckedRsaCrosscert> {
51
234
        let mut r = Reader::from_slice(bytes);
52
234
        let signed_portion = r.peek(36)?; // TODO(nickm): a bit ugly.
53
234
        let subject_key = r.extract()?;
54
234
        let exp_hours = r.take_u32()?;
55
234
        let siglen = r.take_u8()?;
56
234
        let signature = r.take(siglen as usize)?.into();
57

            
58
234
        let mut d = ll::d::Sha256::new();
59
234
        d.update(&b"Tor TLS RSA/Ed25519 cross-certificate"[..]);
60
234
        d.update(signed_portion);
61
234
        let digest = d.finalize().into();
62

            
63
234
        let cc = RsaCrosscert {
64
234
            subject_key,
65
234
            exp_hours,
66
234
            digest,
67
234
            signature,
68
234
        };
69

            
70
234
        Ok(UncheckedRsaCrosscert(cc))
71
234
    }
72
}
73

            
74
/// An RsaCrosscert whose signature has not been checked.
75
pub struct UncheckedRsaCrosscert(RsaCrosscert);
76

            
77
impl ExternallySigned<TimerangeBound<RsaCrosscert>> for UncheckedRsaCrosscert {
78
    type Key = ll::pk::rsa::PublicKey;
79
    type KeyHint = ();
80
    type Error = tor_bytes::Error;
81

            
82
78
    fn key_is_correct(&self, _k: &Self::Key) -> Result<(), Self::KeyHint> {
83
        // there is no way to check except for trying to verify the signature
84
78
        Ok(())
85
78
    }
86

            
87
273
    fn is_well_signed(&self, k: &Self::Key) -> Result<(), Self::Error> {
88
273
        k.verify(&self.0.digest[..], &self.0.signature[..])
89
329
            .map_err(|_| {
90
132
                tor_bytes::Error::InvalidMessage(
91
132
                    "Invalid signature on RSA->Ed identity crosscert".into(),
92
132
                )
93
134
            })?;
94
195
        Ok(())
95
273
    }
96

            
97
195
    fn dangerously_assume_wellsigned(self) -> TimerangeBound<RsaCrosscert> {
98
195
        let expiration = self.0.expiry();
99
195
        TimerangeBound::new(self.0, ..expiration)
100
195
    }
101
}