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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
|
//! Certificate related types and functions for an arti relay.
use std::time::SystemTime;
use tor_cert::{CertEncodeError, CertType, CertifiedKey, Ed25519Cert, EncodedEd25519Cert};
use tor_checkable::{SelfSigned, Timebound};
use tor_key_forge::{InvalidCertError, ParsedEd25519Cert, ToEncodableCert};
use tor_llcrypto::pk::ed25519::{self, Ed25519Identity};
use crate::pk::{RelayIdentityKeypair, RelayLinkSigningKeypair, RelaySigningKeypair};
// TODO: maybe we can eventually unify the 2 `gen_*_cert` functions
// into a single one taking a `K: HasCertType` generic param and returning `Result<K>`.
// That way, we could call `K::cert_type()` to get the cert type,
// making it impossible for the `gen_*_cert function to accidentally use
// a different cert type than the validation function.
/// Generate the relay signing certificate from the given relay identity keypair and the relay
/// signing keypair.
pub fn gen_signing_cert(
kp_relay_id: &RelayIdentityKeypair,
kp_relaysign_id: &RelaySigningKeypair,
expiry: SystemTime,
) -> Result<RelayLinkSigningKeyCert, CertEncodeError> {
Ed25519Cert::constructor()
.cert_type(RelayLinkSigningKeyCert::cert_type())
.expiration(expiry)
.signing_key(kp_relay_id.to_ed25519_id())
.cert_key(CertifiedKey::Ed25519(kp_relaysign_id.to_ed25519_id()))
.encode_and_sign(kp_relay_id)
.map(RelayLinkSigningKeyCert::from)
}
/// Generate the relay link certificate from the given relay signing keypair and the relay
/// link keypair.
pub fn gen_link_cert(
kp_relaysign_id: &RelaySigningKeypair,
kp_link_id: &RelayLinkSigningKeypair,
expiry: SystemTime,
) -> Result<RelayLinkSigningKeyCert, CertEncodeError> {
Ed25519Cert::constructor()
.cert_type(RelayLinkSigningKeyCert::cert_type())
.expiration(expiry)
.signing_key(kp_relaysign_id.to_ed25519_id())
.cert_key(CertifiedKey::Ed25519(kp_link_id.to_ed25519_id()))
.encode_and_sign(kp_relaysign_id)
.map(RelayLinkSigningKeyCert::from)
}
/// Certificate for the medium-term relay signing key (`K_relaysign_ed`).
///
/// This is an ed25519 certificate encoded in Tor's
/// [certificate format](https://spec.torproject.org/cert-spec.html#ed-certs)
/// with [`CERT_KEY_TYPE`](https://spec.torproject.org/cert-spec.html#list-key-types)
/// set to `ed25519` (`01`),
/// and the [`CERT_TYPE`](https://spec.torproject.org/cert-spec.html#list-cert-types)
/// set to `IDENTITY_V_SIGNING` (`04`).
///
/// The signing key is the relay identity key (`K_relayid_ed`)`).
#[derive(Debug, Clone, PartialEq, derive_more::From)]
pub struct RelaySigningKeyCert(EncodedEd25519Cert);
impl RelaySigningKeyCert {
/// Return the `CertType` of this cert.
fn cert_type() -> CertType {
CertType::IDENTITY_V_SIGNING
}
}
/// Certificate for the short-term signing keypair for link authentication.
///
/// This is an ed25519 certificate encoded in Tor's
/// [certificate format](https://spec.torproject.org/cert-spec.html#ed-certs)
/// with [`CERT_KEY_TYPE`](https://spec.torproject.org/cert-spec.html#list-key-types)
/// set to `ed25519` (`01`),
/// and the [`CERT_TYPE`](https://spec.torproject.org/cert-spec.html#list-cert-types)
/// set to `SIGNING_V_LINK_AUTH` (`06`).
///
/// The signing key is the relay identity key (`K_relayid_ed`)`).
#[derive(Debug, Clone, PartialEq, derive_more::From)]
pub struct RelayLinkSigningKeyCert(EncodedEd25519Cert);
impl RelayLinkSigningKeyCert {
/// Return the `CertType` of this cert.
fn cert_type() -> CertType {
CertType::SIGNING_V_LINK_AUTH
}
}
impl ToEncodableCert<RelaySigningKeypair> for RelaySigningKeyCert {
type ParsedCert = ParsedEd25519Cert;
type EncodableCert = EncodedEd25519Cert;
type SigningKey = RelayIdentityKeypair;
fn validate(
cert: Self::ParsedCert,
subject: &RelaySigningKeypair,
signed_with: &Self::SigningKey,
) -> Result<Self, InvalidCertError> {
// TODO: take the time/time provider as an arg?
let now = SystemTime::now();
validate_ed25519_cert(
cert,
&subject.public().into(),
&signed_with.public().into(),
Self::cert_type(),
&now,
)
.map(RelaySigningKeyCert::from)
}
fn to_encodable_cert(self) -> Self::EncodableCert {
self.0
}
}
impl ToEncodableCert<RelayLinkSigningKeypair> for RelayLinkSigningKeyCert {
type ParsedCert = ParsedEd25519Cert;
type EncodableCert = EncodedEd25519Cert;
type SigningKey = RelaySigningKeypair;
fn validate(
cert: Self::ParsedCert,
subject: &RelayLinkSigningKeypair,
signed_with: &Self::SigningKey,
) -> Result<Self, InvalidCertError> {
// TODO: take the time/time provider as an arg?
let now = SystemTime::now();
validate_ed25519_cert(
cert,
&subject.public().into(),
&signed_with.public().into(),
Self::cert_type(),
&now,
)
.map(RelayLinkSigningKeyCert::from)
}
fn to_encodable_cert(self) -> Self::EncodableCert {
self.0
}
}
/// Validate the specified `cert`, checking that
/// * its [`CertType`] is `cert_type, and
/// * its subject key is `subject`, and
/// * it is signed with the `signed_with` key, and
/// * it is timely (it is not expired or not yet valid at the specified `ts`)
fn validate_ed25519_cert(
cert: ParsedEd25519Cert,
subject: &ed25519::PublicKey,
signed_with: &ed25519::PublicKey,
cert_type: CertType,
ts: &SystemTime,
) -> Result<EncodedEd25519Cert, InvalidCertError> {
let cert = cert
.should_be_signed_with(&Ed25519Identity::from(signed_with))?
.check_signature()?;
let cert = cert.check_valid_at(ts)?;
let subject = Ed25519Identity::from(subject);
if subject != *cert.subject_key()? {
return Err(InvalidCertError::SubjectKeyMismatch);
}
let actual_cert_type = cert.as_ref().cert_type();
if actual_cert_type != cert_type {
return Err(InvalidCertError::CertType(actual_cert_type));
}
Ok(cert.into_encoded())
}
|