1use std::sync::Arc;
20
21use rustls::pki_types::CertificateDer;
22
23pub fn ed25519_pubkey_from_cert(der: &[u8]) -> Result<[u8; 32], String> {
27 let (_, cert) =
28 x509_parser::parse_x509_certificate(der).map_err(|e| format!("parse cert: {e}"))?;
29 let spki = cert.public_key();
30 let alg_oid = &spki.algorithm.algorithm;
31 if alg_oid.to_id_string() != "1.3.101.112" {
32 return Err(format!(
33 "expected Ed25519 SPKI, got OID {}",
34 alg_oid.to_id_string()
35 ));
36 }
37 let pk = spki.subject_public_key.data.as_ref();
38 pk.try_into()
39 .map_err(|_| format!("Ed25519 pubkey must be 32 bytes, got {}", pk.len()))
40}
41
42#[derive(Debug)]
49pub struct PubkeyPinVerifier {
50 pinned: [u8; 32],
51 crypto: Arc<rustls::crypto::CryptoProvider>,
52}
53
54impl PubkeyPinVerifier {
55 pub fn new(pinned_pubkey: [u8; 32]) -> Self {
56 Self {
57 pinned: pinned_pubkey,
58 crypto: Arc::new(rustls::crypto::ring::default_provider()),
59 }
60 }
61}
62
63impl rustls::client::danger::ServerCertVerifier for PubkeyPinVerifier {
64 fn verify_server_cert(
65 &self,
66 end_entity: &CertificateDer<'_>,
67 _intermediates: &[CertificateDer<'_>],
68 _server_name: &rustls::pki_types::ServerName<'_>,
69 _ocsp_response: &[u8],
70 _now: rustls::pki_types::UnixTime,
71 ) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
72 let presented = ed25519_pubkey_from_cert(end_entity.as_ref())
73 .map_err(|e| rustls::Error::General(format!("pubkey extract: {e}")))?;
74
75 if presented == self.pinned {
76 Ok(rustls::client::danger::ServerCertVerified::assertion())
77 } else {
78 Err(rustls::Error::General(format!(
79 "pubkey mismatch: pinned={} presented={}",
80 hex(&self.pinned),
81 hex(&presented)
82 )))
83 }
84 }
85
86 fn verify_tls12_signature(
87 &self,
88 _message: &[u8],
89 _cert: &CertificateDer<'_>,
90 _dss: &rustls::DigitallySignedStruct,
91 ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
92 Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
96 }
97
98 fn verify_tls13_signature(
99 &self,
100 message: &[u8],
101 cert: &CertificateDer<'_>,
102 dss: &rustls::DigitallySignedStruct,
103 ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
104 rustls::crypto::verify_tls13_signature(
109 message,
110 cert,
111 dss,
112 &self.crypto.signature_verification_algorithms,
113 )
114 }
115
116 fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
117 self.crypto
118 .signature_verification_algorithms
119 .supported_schemes()
120 }
121}
122
123#[derive(Debug)]
129pub struct SkipServerVerification(Arc<rustls::crypto::CryptoProvider>);
130
131impl SkipServerVerification {
132 pub fn new() -> Self {
133 Self(Arc::new(rustls::crypto::ring::default_provider()))
134 }
135}
136
137impl Default for SkipServerVerification {
138 fn default() -> Self {
139 Self::new()
140 }
141}
142
143impl rustls::client::danger::ServerCertVerifier for SkipServerVerification {
144 fn verify_server_cert(
145 &self,
146 _end_entity: &CertificateDer<'_>,
147 _intermediates: &[CertificateDer<'_>],
148 _server_name: &rustls::pki_types::ServerName<'_>,
149 _ocsp_response: &[u8],
150 _now: rustls::pki_types::UnixTime,
151 ) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
152 Ok(rustls::client::danger::ServerCertVerified::assertion())
153 }
154
155 fn verify_tls12_signature(
156 &self,
157 _message: &[u8],
158 _cert: &CertificateDer<'_>,
159 _dss: &rustls::DigitallySignedStruct,
160 ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
161 Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
162 }
163
164 fn verify_tls13_signature(
165 &self,
166 _message: &[u8],
167 _cert: &CertificateDer<'_>,
168 _dss: &rustls::DigitallySignedStruct,
169 ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
170 Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
171 }
172
173 fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
174 self.0.signature_verification_algorithms.supported_schemes()
175 }
176}
177
178fn hex(bytes: &[u8]) -> String {
179 bytes.iter().map(|b| format!("{b:02x}")).collect()
180}
181
182#[cfg(test)]
183mod tests {
184 use super::*;
185 use rustls::client::danger::ServerCertVerifier;
186
187 #[test]
188 fn ed25519_pubkey_extraction_rejects_garbage_der() {
189 assert!(ed25519_pubkey_from_cert(b"not a certificate").is_err());
190 }
191
192 #[test]
193 fn pinned_verifier_stores_the_exact_bytes_given() {
194 let pin = [0x42u8; 32];
195 let verifier = PubkeyPinVerifier::new(pin);
196 assert_eq!(verifier.pinned, pin);
197 }
198
199 fn synthetic_ed25519_cert() -> (rustls::pki_types::CertificateDer<'static>, [u8; 32]) {
208 let key_pair = rcgen::KeyPair::generate_for(&rcgen::PKCS_ED25519).expect("keygen");
209 let params = rcgen::CertificateParams::new(Vec::<String>::new()).expect("params");
210 let cert = params.self_signed(&key_pair).expect("self-sign");
211 let der = rustls::pki_types::CertificateDer::from(cert.der().to_vec());
212 let pubkey =
213 ed25519_pubkey_from_cert(der.as_ref()).expect("our own synthetic cert must parse");
214 (der, pubkey)
215 }
216
217 fn fake_server_name() -> rustls::pki_types::ServerName<'static> {
218 rustls::pki_types::ServerName::try_from("station.example").expect("valid server name")
219 }
220
221 #[test]
222 fn extracts_the_real_pubkey_from_a_synthetic_cert() {
223 let (_der, pubkey) = synthetic_ed25519_cert();
228 assert_ne!(
229 pubkey, [0u8; 32],
230 "a real generated key should not be all-zero"
231 );
232 }
233
234 #[test]
235 fn verify_server_cert_accepts_the_pinned_key() {
236 let (der, pubkey) = synthetic_ed25519_cert();
237 let verifier = PubkeyPinVerifier::new(pubkey);
238 let result = verifier.verify_server_cert(
239 &der,
240 &[],
241 &fake_server_name(),
242 &[],
243 rustls::pki_types::UnixTime::now(),
244 );
245 assert!(
246 result.is_ok(),
247 "pinning the cert's real key must succeed: {result:?}"
248 );
249 }
250
251 #[test]
252 fn verify_server_cert_rejects_a_mismatched_key() {
253 let (der, pubkey) = synthetic_ed25519_cert();
254 let mut wrong = pubkey;
255 wrong[0] ^= 0xFF;
256 let verifier = PubkeyPinVerifier::new(wrong);
257 let result = verifier.verify_server_cert(
258 &der,
259 &[],
260 &fake_server_name(),
261 &[],
262 rustls::pki_types::UnixTime::now(),
263 );
264 assert!(result.is_err(), "pinning the WRONG key must fail closed");
265 }
266
267 #[test]
268 fn skip_verification_accepts_anything() {
269 let (der, _pubkey) = synthetic_ed25519_cert();
270 let verifier = SkipServerVerification::new();
271 let result = verifier.verify_server_cert(
272 &der,
273 &[],
274 &fake_server_name(),
275 &[],
276 rustls::pki_types::UnixTime::now(),
277 );
278 assert!(
279 result.is_ok(),
280 "insecure mode must accept any cert, by design"
281 );
282 }
283}