1//! Certificate related types and functions for an arti relay.
23use std::time::SystemTime;
45use 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};
910use crate::pk::{RelayIdentityKeypair, RelayLinkSigningKeypair, RelaySigningKeypair};
1112// 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.
1718/// 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<RelayLinkSigningKeyCert, CertEncodeError> {
25 Ed25519Cert::constructor()
26 .cert_type(RelayLinkSigningKeyCert::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(RelayLinkSigningKeyCert::from)
32}
3334/// 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}
4950/// Certificate for the medium-term relay signing key (`K_relaysign_ed`).
51///
52/// This is an ed25519 certificate encoded in Tor's
53/// [certificate format](https://spec.torproject.org/cert-spec.html#ed-certs)
54/// with [`CERT_KEY_TYPE`](https://spec.torproject.org/cert-spec.html#list-key-types)
55/// set to `ed25519` (`01`),
56/// and the [`CERT_TYPE`](https://spec.torproject.org/cert-spec.html#list-cert-types)
57/// set to `IDENTITY_V_SIGNING` (`04`).
58///
59/// The signing key is the relay identity key (`K_relayid_ed`)`).
60#[derive(Debug, Clone, PartialEq, derive_more::From)]
61pub struct RelaySigningKeyCert(EncodedEd25519Cert);
6263impl RelaySigningKeyCert {
64/// Return the `CertType` of this cert.
65fn cert_type() -> CertType {
66 CertType::IDENTITY_V_SIGNING
67 }
68}
6970/// Certificate for the short-term signing keypair for link authentication.
71///
72/// This is an ed25519 certificate encoded in Tor's
73/// [certificate format](https://spec.torproject.org/cert-spec.html#ed-certs)
74/// with [`CERT_KEY_TYPE`](https://spec.torproject.org/cert-spec.html#list-key-types)
75/// set to `ed25519` (`01`),
76/// and the [`CERT_TYPE`](https://spec.torproject.org/cert-spec.html#list-cert-types)
77/// set to `SIGNING_V_LINK_AUTH` (`06`).
78///
79/// The signing key is the relay identity key (`K_relayid_ed`)`).
80#[derive(Debug, Clone, PartialEq, derive_more::From)]
81pub struct RelayLinkSigningKeyCert(EncodedEd25519Cert);
8283impl RelayLinkSigningKeyCert {
84/// Return the `CertType` of this cert.
85fn cert_type() -> CertType {
86 CertType::SIGNING_V_LINK_AUTH
87 }
88}
8990impl ToEncodableCert<RelaySigningKeypair> for RelaySigningKeyCert {
91type ParsedCert = ParsedEd25519Cert;
92type EncodableCert = EncodedEd25519Cert;
93type SigningKey = RelayIdentityKeypair;
9495fn validate(
96 cert: Self::ParsedCert,
97 subject: &RelaySigningKeypair,
98 signed_with: &Self::SigningKey,
99 ) -> Result<Self, InvalidCertError> {
100// TODO: take the time/time provider as an arg?
101let now = SystemTime::now();
102 validate_ed25519_cert(
103 cert,
104&subject.public().into(),
105&signed_with.public().into(),
106Self::cert_type(),
107&now,
108 )
109 .map(RelaySigningKeyCert::from)
110 }
111112fn to_encodable_cert(self) -> Self::EncodableCert {
113self.0
114}
115}
116117impl ToEncodableCert<RelayLinkSigningKeypair> for RelayLinkSigningKeyCert {
118type ParsedCert = ParsedEd25519Cert;
119type EncodableCert = EncodedEd25519Cert;
120type SigningKey = RelaySigningKeypair;
121122fn validate(
123 cert: Self::ParsedCert,
124 subject: &RelayLinkSigningKeypair,
125 signed_with: &Self::SigningKey,
126 ) -> Result<Self, InvalidCertError> {
127// TODO: take the time/time provider as an arg?
128let now = SystemTime::now();
129 validate_ed25519_cert(
130 cert,
131&subject.public().into(),
132&signed_with.public().into(),
133Self::cert_type(),
134&now,
135 )
136 .map(RelayLinkSigningKeyCert::from)
137 }
138139fn to_encodable_cert(self) -> Self::EncodableCert {
140self.0
141}
142}
143144/// Validate the specified `cert`, checking that
145/// * its [`CertType`] is `cert_type, and
146/// * its subject key is `subject`, and
147/// * it is signed with the `signed_with` key, and
148/// * it is timely (it is not expired or not yet valid at the specified `ts`)
149fn validate_ed25519_cert(
150 cert: ParsedEd25519Cert,
151 subject: &ed25519::PublicKey,
152 signed_with: &ed25519::PublicKey,
153 cert_type: CertType,
154 ts: &SystemTime,
155) -> Result<EncodedEd25519Cert, InvalidCertError> {
156let cert = cert
157 .should_be_signed_with(&Ed25519Identity::from(signed_with))?
158.check_signature()?;
159160let cert = cert.check_valid_at(ts)?;
161let subject = Ed25519Identity::from(subject);
162163if subject != *cert.subject_key()? {
164return Err(InvalidCertError::SubjectKeyMismatch);
165 }
166167let actual_cert_type = cert.as_ref().cert_type();
168if actual_cert_type != cert_type {
169return Err(InvalidCertError::CertType(actual_cert_type));
170 }
171172Ok(cert.into_encoded())
173}