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, ExpiryHours};
13

            
14
mod encode;
15
pub use encode::EncodedRsaCrosscert;
16

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

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

            
40
/// Compute the SHA256 digest of `c`, prefixed with PREFIX.
41
842
fn compute_digest(c: &[u8]) -> [u8; 32] {
42
842
    let mut d = ll::d::Sha256::new();
43
842
    d.update(PREFIX);
44
842
    d.update(c);
45
842
    d.finalize().into()
46
842
}
47

            
48
impl RsaCrosscert {
49
    /// Return the time at which this certificate becomes expired
50
562
    pub fn expiry(&self) -> std::time::SystemTime {
51
562
        self.exp_hours.into()
52
562
    }
53

            
54
    /// Return a reference to the digest.
55
    pub fn digest(&self) -> &[u8; 32] {
56
        &self.digest
57
    }
58

            
59
    /// Return true if the subject key in this certificate matches `other`
60
561
    pub fn subject_key_matches(&self, other: &ll::pk::ed25519::Ed25519Identity) -> bool {
61
561
        other == &self.subject_key
62
561
    }
63

            
64
    /// Return this certificate cert type.
65
    pub fn cert_type(&self) -> CertType {
66
        CertType::RSA_ID_V_IDENTITY
67
    }
68

            
69
    /// Decode a slice of bytes into an RSA crosscert.
70
631
    pub fn decode(bytes: &[u8]) -> tor_bytes::Result<UncheckedRsaCrosscert> {
71
631
        let mut r = Reader::from_slice(bytes);
72
631
        let signed_portion = r.peek(36)?; // TODO(nickm): a bit ugly.
73
631
        let subject_key = r.extract()?;
74
631
        let exp_hours = r.extract()?;
75
631
        let siglen = r.take_u8()?;
76
631
        let signature = r.take(siglen as usize)?.into();
77

            
78
631
        let digest = compute_digest(signed_portion);
79

            
80
631
        let cc = RsaCrosscert {
81
631
            subject_key,
82
631
            exp_hours,
83
631
            digest,
84
631
            signature,
85
631
        };
86

            
87
631
        Ok(UncheckedRsaCrosscert(cc))
88
631
    }
89
}
90

            
91
/// An RsaCrosscert whose signature has not been checked.
92
pub struct UncheckedRsaCrosscert(RsaCrosscert);
93

            
94
impl ExternallySigned<TimeRangeBound<RsaCrosscert>> for UncheckedRsaCrosscert {
95
    type Key = ll::pk::rsa::PublicKey;
96
    type KeyHint = ();
97
    type Error = tor_bytes::Error;
98

            
99
140
    fn key_is_correct(&self, _k: &Self::Key) -> Result<(), Self::KeyHint> {
100
        // there is no way to check except for trying to verify the signature
101
140
        Ok(())
102
140
    }
103

            
104
701
    fn is_well_signed(&self, k: &Self::Key) -> Result<(), Self::Error> {
105
701
        k.verify(&self.0.digest[..], &self.0.signature[..])
106
701
            .map_err(|_| {
107
140
                tor_bytes::Error::InvalidMessage(
108
140
                    "Invalid signature on RSA->Ed identity crosscert".into(),
109
140
                )
110
140
            })?;
111
561
        Ok(())
112
701
    }
113

            
114
561
    fn dangerously_assume_wellsigned(self) -> TimeRangeBound<RsaCrosscert> {
115
561
        let expiration = self.0.expiry();
116
561
        TimeRangeBound::new(self.0, ..expiration)
117
561
    }
118
}