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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
|
//! RSA cross-cert generation
use web_time_compat::SystemTime;
use derive_more::{AsRef, Deref, Into};
use tor_bytes::Writer as _;
use tor_llcrypto::pk::{ed25519, rsa};
use crate::{CertEncodeError, EncodedCert, ExpiryHours};
/// An RSA cross certificate certificate,
/// created using [`EncodedRsaCrosscert::encode_and_sign`].
///
/// It corresponds to the type of certificate parsed with
/// [`RsaCrosscert`](super::RsaCrosscert).
/// It is used to prove that an Ed25519 identity speaks
/// on behalf of an RSA identity.
///
/// The certificate is encoded in the format specified
/// in Tor's [certificate specification](https://spec.torproject.org/cert-spec.html#rsa-cross-cert)
///
/// This certificate has already been validated.
#[derive(Clone, Debug, PartialEq, Into, AsRef, Deref)]
pub struct EncodedRsaCrosscert(Vec<u8>);
impl EncodedRsaCrosscert {
/// Create a new [`EncodedRsaCrosscert`] certifying `ed_identity` as
/// speaking on behalf of `rsa_identity`.
///
/// The certificate will expire no earlier than `expiration`,
/// and no more than one hour later.
/// (Expiration times in these certificates have a one-hour granularity.)
pub fn encode_and_sign(
rsa_identity: &rsa::KeyPair,
ed_identity: &ed25519::Ed25519Identity,
expiration: SystemTime,
) -> Result<Self, CertEncodeError> {
let mut cert = Vec::new();
cert.write(ed_identity)?;
let exp_hours = ExpiryHours::try_from_systemtime_ceil(expiration)?;
cert.write(&exp_hours)?;
{
let signature = rsa_identity
.sign(&super::compute_digest(&cert))
.map_err(|_| CertEncodeError::RsaSignatureFailed)?;
let mut inner = cert.write_nested_u8len();
inner.write_and_consume(signature)?;
inner.finish()?;
}
Ok(EncodedRsaCrosscert(cert))
}
}
impl EncodedCert for EncodedRsaCrosscert {
fn cert_type(&self) -> crate::CertType {
crate::CertType::RSA_ID_V_IDENTITY
}
fn encoded(&self) -> &[u8] {
&self.0
}
}
#[cfg(test)]
mod test {
// @@ begin test lint list maintained by maint/add_warning @@
#![allow(clippy::bool_assert_comparison)]
#![allow(clippy::clone_on_copy)]
#![allow(clippy::dbg_macro)]
#![allow(clippy::mixed_attributes_style)]
#![allow(clippy::print_stderr)]
#![allow(clippy::print_stdout)]
#![allow(clippy::single_char_pattern)]
#![allow(clippy::unwrap_used)]
#![allow(clippy::unchecked_time_subtraction)]
#![allow(clippy::useless_vec)]
#![allow(clippy::needless_pass_by_value)]
#![allow(clippy::string_slice)] // See arti#2571
//! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
use std::time::Duration;
use tor_basic_utils::test_rng::testing_rng;
use tor_checkable::{ExternallySigned, TimeBound};
use web_time_compat::SystemTimeExt;
use crate::SEC_PER_HOUR;
use crate::rsa::RsaCrosscert;
use super::*;
#[test]
fn generate() {
let mut rng = testing_rng();
let keypair = rsa::KeyPair::generate(&mut rng).unwrap();
let ed_id =
ed25519::Ed25519Identity::from_base64("dGhhdW1hdHVyZ3kgaXMgc3RvcmVkIGluIHRoZSBvcmI")
.unwrap();
let now = SystemTime::get();
let expiry = now + Duration::from_secs(24 * SEC_PER_HOUR);
let cert = EncodedRsaCrosscert::encode_and_sign(&keypair, &ed_id, expiry).unwrap();
let parsed = RsaCrosscert::decode(cert.as_ref()).unwrap();
let parsed = parsed
.check_signature(&keypair.to_public_key())
.unwrap()
.if_valid_at(&now)
.unwrap();
assert!(parsed.subject_key_matches(&ed_id));
assert_eq!(parsed.subject_key, ed_id);
let parsed_expiry = parsed.expiry();
assert!(parsed_expiry >= expiry);
assert!(parsed_expiry < expiry + Duration::new(3600, 0));
}
}
|