1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
use tor_bytes::Reader;
use tor_checkable::{timed::TimerangeBound, ExternallySigned};
use tor_llcrypto as ll;
use digest::Digest;
#[must_use]
pub struct RsaCrosscert {
subject_key: ll::pk::ed25519::PublicKey,
exp_hours: u32,
digest: [u8; 32],
signature: Vec<u8>,
}
impl RsaCrosscert {
pub fn expiry(&self) -> std::time::SystemTime {
let d = std::time::Duration::new(u64::from(self.exp_hours) * 3600, 0);
std::time::SystemTime::UNIX_EPOCH + d
}
pub fn subject_key_matches(&self, other: &ll::pk::ed25519::PublicKey) -> bool {
&self.subject_key == other
}
pub fn decode(bytes: &[u8]) -> tor_bytes::Result<UncheckedRsaCrosscert> {
let mut r = Reader::from_slice(bytes);
let signed_portion = r.peek(36)?;
let subject_key = r.extract()?;
let exp_hours = r.take_u32()?;
let siglen = r.take_u8()?;
let signature = r.take(siglen as usize)?.into();
let mut d = ll::d::Sha256::new();
d.update(&b"Tor TLS RSA/Ed25519 cross-certificate"[..]);
d.update(signed_portion);
let digest = d.finalize().into();
let cc = RsaCrosscert {
subject_key,
exp_hours,
digest,
signature,
};
Ok(UncheckedRsaCrosscert(cc))
}
}
pub struct UncheckedRsaCrosscert(RsaCrosscert);
impl ExternallySigned<TimerangeBound<RsaCrosscert>> for UncheckedRsaCrosscert {
type Key = ll::pk::rsa::PublicKey;
type KeyHint = ();
type Error = tor_bytes::Error;
fn key_is_correct(&self, _k: &Self::Key) -> Result<(), Self::KeyHint> {
Ok(())
}
fn is_well_signed(&self, k: &Self::Key) -> Result<(), Self::Error> {
k.verify(&self.0.digest[..], &self.0.signature[..])
.map_err(|_| {
tor_bytes::Error::BadMessage("Invalid signature on RSA->Ed identity crosscert")
})?;
Ok(())
}
fn dangerously_assume_wellsigned(self) -> TimerangeBound<RsaCrosscert> {
let expiration = self.0.expiry();
TimerangeBound::new(self.0, ..expiration)
}
}