Skip to main content

tor_relay_crypto/
certs.rs

1//! Certificate related types and functions for an arti relay.
2
3use std::time::SystemTime;
4
5use tor_cert::{CertEncodeError, CertType, CertifiedKey, Ed25519Cert, EncodedEd25519Cert};
6use tor_checkable::{SelfSigned, Timebound};
7use tor_key_forge::{InvalidCertError, ParsedEd25519Cert, ToEncodableCert};
8use tor_llcrypto::pk::ed25519::{self, Ed25519Identity};
9
10use crate::pk::{RelayIdentityKeypair, RelayLinkSigningKeypair, RelaySigningKeypair};
11
12// TODO: maybe we can eventually unify the 2 `gen_*_cert` functions
13// into a single one taking a `K: HasCertType` generic param and returning `Result<K>`.
14// That way, we could call `K::cert_type()` to get the cert type,
15// making it impossible for the `gen_*_cert function to accidentally use
16// a different cert type than the validation function.
17
18/// Generate the relay signing certificate from the given relay identity keypair and the relay
19/// signing keypair.
20pub fn gen_signing_cert(
21    kp_relay_id: &RelayIdentityKeypair,
22    kp_relaysign_id: &RelaySigningKeypair,
23    expiry: SystemTime,
24) -> Result<RelaySigningKeyCert, CertEncodeError> {
25    Ed25519Cert::constructor()
26        .cert_type(RelaySigningKeyCert::cert_type())
27        .expiration(expiry)
28        .signing_key(kp_relay_id.to_ed25519_id())
29        .cert_key(CertifiedKey::Ed25519(kp_relaysign_id.to_ed25519_id()))
30        .encode_and_sign(kp_relay_id)
31        .map(RelaySigningKeyCert::from)
32}
33
34/// Generate the relay link certificate from the given relay signing keypair and the relay
35/// link keypair.
36pub fn gen_link_cert(
37    kp_relaysign_id: &RelaySigningKeypair,
38    kp_link_id: &RelayLinkSigningKeypair,
39    expiry: SystemTime,
40) -> Result<RelayLinkSigningKeyCert, CertEncodeError> {
41    Ed25519Cert::constructor()
42        .cert_type(RelayLinkSigningKeyCert::cert_type())
43        .expiration(expiry)
44        .signing_key(kp_relaysign_id.to_ed25519_id())
45        .cert_key(CertifiedKey::Ed25519(kp_link_id.to_ed25519_id()))
46        .encode_and_sign(kp_relaysign_id)
47        .map(RelayLinkSigningKeyCert::from)
48}
49
50/// Generate the signed TLS certificate from the given relay signing keypair and the TLS cert
51/// digest.
52pub fn gen_tls_cert(
53    kp_relaysign_id: &RelaySigningKeypair,
54    tls_digest: [u8; 32],
55    expiry: SystemTime,
56) -> Result<EncodedEd25519Cert, CertEncodeError> {
57    Ed25519Cert::constructor()
58        .cert_type(CertType::SIGNING_V_TLS_CERT)
59        .expiration(expiry)
60        .signing_key(kp_relaysign_id.to_ed25519_id())
61        .cert_key(CertifiedKey::X509Sha256Digest(tls_digest))
62        .encode_and_sign(kp_relaysign_id)
63}
64
65/// Certificate for the medium-term relay signing key (`K_relaysign_ed`).
66///
67/// This is an ed25519 certificate encoded in Tor's
68/// [certificate format](https://spec.torproject.org/cert-spec.html#ed-certs)
69/// with [`CERT_KEY_TYPE`](https://spec.torproject.org/cert-spec.html#list-key-types)
70/// set to `ed25519` (`01`),
71/// and the [`CERT_TYPE`](https://spec.torproject.org/cert-spec.html#list-cert-types)
72/// set to `IDENTITY_V_SIGNING` (`04`).
73///
74/// The signing key is the relay identity key (`K_relayid_ed`)`).
75#[derive(Debug, Clone, PartialEq, derive_more::From)]
76pub struct RelaySigningKeyCert(EncodedEd25519Cert);
77
78impl RelaySigningKeyCert {
79    /// Return the `CertType` of this cert.
80    fn cert_type() -> CertType {
81        CertType::IDENTITY_V_SIGNING
82    }
83}
84
85/// Certificate for the short-term signing keypair for link authentication.
86///
87/// This is an ed25519 certificate encoded in Tor's
88/// [certificate format](https://spec.torproject.org/cert-spec.html#ed-certs)
89/// with [`CERT_KEY_TYPE`](https://spec.torproject.org/cert-spec.html#list-key-types)
90/// set to `ed25519` (`01`),
91/// and the [`CERT_TYPE`](https://spec.torproject.org/cert-spec.html#list-cert-types)
92/// set to `SIGNING_V_LINK_AUTH` (`06`).
93///
94/// The signing key is the relay identity key (`K_relayid_ed`)`).
95#[derive(Debug, Clone, PartialEq, derive_more::From)]
96pub struct RelayLinkSigningKeyCert(EncodedEd25519Cert);
97
98impl RelayLinkSigningKeyCert {
99    /// Return the `CertType` of this cert.
100    fn cert_type() -> CertType {
101        CertType::SIGNING_V_LINK_AUTH
102    }
103}
104
105impl ToEncodableCert<RelaySigningKeypair> for RelaySigningKeyCert {
106    type ParsedCert = ParsedEd25519Cert;
107    type EncodableCert = EncodedEd25519Cert;
108    type SigningKey = RelayIdentityKeypair;
109
110    fn validate(
111        cert: Self::ParsedCert,
112        subject: &RelaySigningKeypair,
113        signed_with: &Self::SigningKey,
114    ) -> Result<Self, InvalidCertError> {
115        // TODO: take the time/time provider as an arg?
116        let now = SystemTime::now();
117        validate_ed25519_cert(
118            cert,
119            &subject.public().into(),
120            &signed_with.public().into(),
121            Self::cert_type(),
122            &now,
123        )
124        .map(RelaySigningKeyCert::from)
125    }
126
127    fn to_encodable_cert(self) -> Self::EncodableCert {
128        self.0
129    }
130}
131
132impl ToEncodableCert<RelayLinkSigningKeypair> for RelayLinkSigningKeyCert {
133    type ParsedCert = ParsedEd25519Cert;
134    type EncodableCert = EncodedEd25519Cert;
135    type SigningKey = RelaySigningKeypair;
136
137    fn validate(
138        cert: Self::ParsedCert,
139        subject: &RelayLinkSigningKeypair,
140        signed_with: &Self::SigningKey,
141    ) -> Result<Self, InvalidCertError> {
142        // TODO: take the time/time provider as an arg?
143        let now = SystemTime::now();
144        validate_ed25519_cert(
145            cert,
146            &subject.public().into(),
147            &signed_with.public().into(),
148            Self::cert_type(),
149            &now,
150        )
151        .map(RelayLinkSigningKeyCert::from)
152    }
153
154    fn to_encodable_cert(self) -> Self::EncodableCert {
155        self.0
156    }
157}
158
159/// Validate the specified `cert`, checking that
160///    * its [`CertType`] is `cert_type, and
161///    * its subject key is `subject`, and
162///    * it is signed with the `signed_with` key, and
163///    * it is timely (it is not expired or not yet valid at the specified `ts`)
164fn validate_ed25519_cert(
165    cert: ParsedEd25519Cert,
166    subject: &ed25519::PublicKey,
167    signed_with: &ed25519::PublicKey,
168    cert_type: CertType,
169    ts: &SystemTime,
170) -> Result<EncodedEd25519Cert, InvalidCertError> {
171    let cert = cert
172        .should_be_signed_with(&Ed25519Identity::from(signed_with))?
173        .check_signature()?;
174
175    let cert = cert.check_valid_at(ts)?;
176    let subject = Ed25519Identity::from(subject);
177
178    if subject != *cert.subject_key()? {
179        return Err(InvalidCertError::SubjectKeyMismatch);
180    }
181
182    let actual_cert_type = cert.as_ref().cert_type();
183    if actual_cert_type != cert_type {
184        return Err(InvalidCertError::CertType(actual_cert_type));
185    }
186
187    Ok(cert.into_encoded())
188}