1
//! Certificate related types and functions for an arti relay.
2

            
3
use std::time::SystemTime;
4

            
5
use tor_cert::{CertEncodeError, CertType, CertifiedKey, Ed25519Cert, EncodedEd25519Cert};
6
use tor_checkable::{SelfSigned, Timebound};
7
use tor_key_forge::{InvalidCertError, ParsedEd25519Cert, ToEncodableCert};
8
use tor_llcrypto::pk::ed25519::{self, Ed25519Identity};
9

            
10
use 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.
20
pub 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
}
33

            
34
/// Generate the relay link certificate from the given relay signing keypair and the relay
35
/// link keypair.
36
pub 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
/// 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)]
61
pub struct RelaySigningKeyCert(EncodedEd25519Cert);
62

            
63
impl RelaySigningKeyCert {
64
    /// Return the `CertType` of this cert.
65
    fn cert_type() -> CertType {
66
        CertType::IDENTITY_V_SIGNING
67
    }
68
}
69

            
70
/// 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)]
81
pub struct RelayLinkSigningKeyCert(EncodedEd25519Cert);
82

            
83
impl RelayLinkSigningKeyCert {
84
    /// Return the `CertType` of this cert.
85
    fn cert_type() -> CertType {
86
        CertType::SIGNING_V_LINK_AUTH
87
    }
88
}
89

            
90
impl ToEncodableCert<RelaySigningKeypair> for RelaySigningKeyCert {
91
    type ParsedCert = ParsedEd25519Cert;
92
    type EncodableCert = EncodedEd25519Cert;
93
    type SigningKey = RelayIdentityKeypair;
94

            
95
    fn 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?
101
        let now = SystemTime::now();
102
        validate_ed25519_cert(
103
            cert,
104
            &subject.public().into(),
105
            &signed_with.public().into(),
106
            Self::cert_type(),
107
            &now,
108
        )
109
        .map(RelaySigningKeyCert::from)
110
    }
111

            
112
    fn to_encodable_cert(self) -> Self::EncodableCert {
113
        self.0
114
    }
115
}
116

            
117
impl ToEncodableCert<RelayLinkSigningKeypair> for RelayLinkSigningKeyCert {
118
    type ParsedCert = ParsedEd25519Cert;
119
    type EncodableCert = EncodedEd25519Cert;
120
    type SigningKey = RelaySigningKeypair;
121

            
122
    fn 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?
128
        let now = SystemTime::now();
129
        validate_ed25519_cert(
130
            cert,
131
            &subject.public().into(),
132
            &signed_with.public().into(),
133
            Self::cert_type(),
134
            &now,
135
        )
136
        .map(RelayLinkSigningKeyCert::from)
137
    }
138

            
139
    fn to_encodable_cert(self) -> Self::EncodableCert {
140
        self.0
141
    }
142
}
143

            
144
/// 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`)
149
fn 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> {
156
    let cert = cert
157
        .should_be_signed_with(&Ed25519Identity::from(signed_with))?
158
        .check_signature()?;
159

            
160
    let cert = cert.check_valid_at(ts)?;
161
    let subject = Ed25519Identity::from(subject);
162

            
163
    if subject != *cert.subject_key()? {
164
        return Err(InvalidCertError::SubjectKeyMismatch);
165
    }
166

            
167
    let actual_cert_type = cert.as_ref().cert_type();
168
    if actual_cert_type != cert_type {
169
        return Err(InvalidCertError::CertType(actual_cert_type));
170
    }
171

            
172
    Ok(cert.into_encoded())
173
}