Skip to main content

tor_relay_crypto/
certs.rs

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