certs.rs
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 }