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
use crate::CertType;
13

            
14
#[cfg(feature = "encode")]
15
mod encode;
16
#[cfg(feature = "encode")]
17
pub use encode::EncodedRsaCrosscert;
18

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

            
39
/// Number of seconds in an hour.
40
const SECS_PER_HOUR: u64 = 3600;
41

            
42
/// Prefix appended when generating a digest for an RsaCrosscert
43
const PREFIX: &[u8] = b"Tor TLS RSA/Ed25519 cross-certificate";
44

            
45
/// Compute the SHA256 digest of `c`, prefixed with PREFIX.
46
238
fn compute_digest(c: &[u8]) -> [u8; 32] {
47
238
    let mut d = ll::d::Sha256::new();
48
238
    d.update(PREFIX);
49
238
    d.update(c);
50
238
    d.finalize().into()
51
238
}
52

            
53
impl RsaCrosscert {
54
    /// Return the time at which this certificate becomes expired
55
199
    pub fn expiry(&self) -> std::time::SystemTime {
56
199
        let d = std::time::Duration::new(u64::from(self.exp_hours) * SECS_PER_HOUR, 0);
57
199
        std::time::SystemTime::UNIX_EPOCH + d
58
199
    }
59

            
60
    /// Return a reference to the digest.
61
    pub fn digest(&self) -> &[u8; 32] {
62
        &self.digest
63
    }
64

            
65
    /// Return true if the subject key in this certificate matches `other`
66
197
    pub fn subject_key_matches(&self, other: &ll::pk::ed25519::Ed25519Identity) -> bool {
67
197
        other == &self.subject_key
68
197
    }
69

            
70
    /// Return this certificate cert type.
71
    pub fn cert_type(&self) -> CertType {
72
        CertType::RSA_ID_V_IDENTITY
73
    }
74

            
75
    /// Decode a slice of bytes into an RSA crosscert.
76
236
    pub fn decode(bytes: &[u8]) -> tor_bytes::Result<UncheckedRsaCrosscert> {
77
236
        let mut r = Reader::from_slice(bytes);
78
236
        let signed_portion = r.peek(36)?; // TODO(nickm): a bit ugly.
79
236
        let subject_key = r.extract()?;
80
236
        let exp_hours = r.take_u32()?;
81
236
        let siglen = r.take_u8()?;
82
236
        let signature = r.take(siglen as usize)?.into();
83

            
84
236
        let digest = compute_digest(signed_portion);
85

            
86
236
        let cc = RsaCrosscert {
87
236
            subject_key,
88
236
            exp_hours,
89
236
            digest,
90
236
            signature,
91
236
        };
92

            
93
236
        Ok(UncheckedRsaCrosscert(cc))
94
236
    }
95
}
96

            
97
/// An RsaCrosscert whose signature has not been checked.
98
pub struct UncheckedRsaCrosscert(RsaCrosscert);
99

            
100
impl ExternallySigned<TimerangeBound<RsaCrosscert>> for UncheckedRsaCrosscert {
101
    type Key = ll::pk::rsa::PublicKey;
102
    type KeyHint = ();
103
    type Error = tor_bytes::Error;
104

            
105
78
    fn key_is_correct(&self, _k: &Self::Key) -> Result<(), Self::KeyHint> {
106
        // there is no way to check except for trying to verify the signature
107
78
        Ok(())
108
78
    }
109

            
110
275
    fn is_well_signed(&self, k: &Self::Key) -> Result<(), Self::Error> {
111
275
        k.verify(&self.0.digest[..], &self.0.signature[..])
112
333
            .map_err(|_| {
113
134
                tor_bytes::Error::InvalidMessage(
114
134
                    "Invalid signature on RSA->Ed identity crosscert".into(),
115
134
                )
116
136
            })?;
117
197
        Ok(())
118
275
    }
119

            
120
197
    fn dangerously_assume_wellsigned(self) -> TimerangeBound<RsaCrosscert> {
121
197
        let expiration = self.0.expiry();
122
197
        TimerangeBound::new(self.0, ..expiration)
123
197
    }
124
}