iroh_net/tls/
certificate.rs1use std::sync::Arc;
9
10use der::{asn1::OctetStringRef, Decode, Encode, Sequence};
11use x509_parser::prelude::*;
12
13use crate::key::{PublicKey, SecretKey, Signature};
14
15const P2P_EXT_OID: [u64; 9] = [1, 3, 6, 1, 4, 1, 53594, 1, 1];
19
20const P2P_SIGNING_PREFIX: [u8; 21] = *b"libp2p-tls-handshake:";
26
27static P2P_SIGNATURE_ALGORITHM: &rcgen::SignatureAlgorithm = &rcgen::PKCS_ECDSA_P256_SHA256;
30
31#[derive(Debug)]
32pub(crate) struct AlwaysResolvesCert(Arc<rustls::sign::CertifiedKey>);
33
34impl AlwaysResolvesCert {
35 pub(crate) fn new(
36 cert: rustls::pki_types::CertificateDer<'static>,
37 key: &rustls::pki_types::PrivateKeyDer<'_>,
38 ) -> Result<Self, rustls::Error> {
39 let certified_key = rustls::sign::CertifiedKey::new(
40 vec![cert],
41 rustls::crypto::ring::sign::any_ecdsa_type(key)?,
42 );
43 Ok(Self(Arc::new(certified_key)))
44 }
45}
46
47impl rustls::client::ResolvesClientCert for AlwaysResolvesCert {
48 fn resolve(
49 &self,
50 _root_hint_subjects: &[&[u8]],
51 _sigschemes: &[rustls::SignatureScheme],
52 ) -> Option<Arc<rustls::sign::CertifiedKey>> {
53 Some(Arc::clone(&self.0))
54 }
55
56 fn has_certs(&self) -> bool {
57 true
58 }
59}
60
61impl rustls::server::ResolvesServerCert for AlwaysResolvesCert {
62 fn resolve(
63 &self,
64 _client_hello: rustls::server::ClientHello<'_>,
65 ) -> Option<Arc<rustls::sign::CertifiedKey>> {
66 Some(Arc::clone(&self.0))
67 }
68}
69
70#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
73struct SignedKey<'a> {
74 public_key: OctetStringRef<'a>,
75 signature: OctetStringRef<'a>,
76}
77
78pub fn generate(
81 identity_secret_key: &SecretKey,
82) -> Result<
83 (
84 rustls::pki_types::CertificateDer<'static>,
85 rustls::pki_types::PrivateKeyDer<'static>,
86 ),
87 GenError,
88> {
89 let certificate_keypair = rcgen::KeyPair::generate(P2P_SIGNATURE_ALGORITHM)?;
95 let rustls_key =
96 rustls::pki_types::PrivateKeyDer::try_from(certificate_keypair.serialize_der()).unwrap();
97 let certificate = {
98 let mut params = rcgen::CertificateParams::new(vec![]);
99 params.distinguished_name = rcgen::DistinguishedName::new();
100 params.custom_extensions.push(make_libp2p_extension(
101 identity_secret_key,
102 &certificate_keypair,
103 )?);
104 params.alg = P2P_SIGNATURE_ALGORITHM;
105 params.key_pair = Some(certificate_keypair);
106 rcgen::Certificate::from_params(params)?
107 };
108
109 let rustls_certificate = rustls::pki_types::CertificateDer::from(certificate.serialize_der()?);
110
111 Ok((rustls_certificate, rustls_key))
112}
113
114pub fn parse<'a>(
119 certificate: &'a rustls::pki_types::CertificateDer<'_>,
120) -> Result<P2pCertificate<'a>, ParseError> {
121 let certificate = parse_unverified(certificate.as_ref())?;
122
123 certificate.verify()?;
124
125 Ok(certificate)
126}
127
128#[derive(Debug)]
131pub struct P2pCertificate<'a> {
132 certificate: X509Certificate<'a>,
133 extension: P2pExtension,
137}
138
139#[derive(Debug)]
142pub struct P2pExtension {
143 public_key: crate::key::PublicKey,
144 signature: crate::key::Signature,
147}
148
149#[derive(Debug, thiserror::Error)]
151#[error(transparent)]
152pub struct GenError(#[from] rcgen::Error);
153
154#[derive(Debug, thiserror::Error)]
156#[error(transparent)]
157pub struct ParseError(#[from] pub(crate) webpki::Error);
158
159#[derive(Debug, thiserror::Error)]
161#[error(transparent)]
162pub struct VerificationError(#[from] pub(crate) webpki::Error);
163
164fn parse_unverified(der_input: &[u8]) -> Result<P2pCertificate, webpki::Error> {
168 let x509 = X509Certificate::from_der(der_input)
169 .map(|(_rest_input, x509)| x509)
170 .map_err(|_| webpki::Error::BadDer)?;
171
172 let p2p_ext_oid = der_parser::oid::Oid::from(&P2P_EXT_OID)
173 .expect("This is a valid OID of p2p extension; qed");
174
175 let mut libp2p_extension = None;
176
177 for ext in x509.extensions() {
178 let oid = &ext.oid;
179 if oid == &p2p_ext_oid && libp2p_extension.is_some() {
180 return Err(webpki::Error::BadDer);
182 }
183
184 if oid == &p2p_ext_oid {
185 let signed_key =
186 SignedKey::from_der(ext.value).map_err(|_| webpki::Error::ExtensionValueInvalid)?;
187 let public_key_raw = signed_key.public_key.as_bytes();
188 let public_key =
189 PublicKey::try_from(public_key_raw).map_err(|_| webpki::Error::UnknownIssuer)?;
190
191 let signature = Signature::from_slice(signed_key.signature.as_bytes())
192 .map_err(|_| webpki::Error::UnknownIssuer)?;
193 let ext = P2pExtension {
194 public_key,
195 signature,
196 };
197 libp2p_extension = Some(ext);
198 continue;
199 }
200
201 if ext.critical {
202 return Err(webpki::Error::UnsupportedCriticalExtension);
205 }
206
207 }
209
210 let extension = libp2p_extension.ok_or(webpki::Error::BadDer)?;
213
214 let certificate = P2pCertificate {
215 certificate: x509,
216 extension,
217 };
218
219 Ok(certificate)
220}
221
222fn make_libp2p_extension(
223 identity_secret_key: &SecretKey,
224 certificate_keypair: &rcgen::KeyPair,
225) -> Result<rcgen::CustomExtension, rcgen::Error> {
226 let signature = {
230 let mut msg = vec![];
231 msg.extend(P2P_SIGNING_PREFIX);
232 msg.extend(certificate_keypair.public_key_der());
233
234 identity_secret_key.sign(&msg)
235 };
236
237 let public_key = identity_secret_key.public();
238 let public_key_ref = OctetStringRef::new(&public_key.as_bytes()[..])
239 .map_err(|_| rcgen::Error::CouldNotParseKeyPair)?;
240 let signature = signature.to_bytes();
241 let signature_ref =
242 OctetStringRef::new(&signature).map_err(|_| rcgen::Error::CouldNotParseCertificate)?;
243 let key = SignedKey {
244 public_key: public_key_ref,
245 signature: signature_ref,
246 };
247
248 let mut extension_content = Vec::new();
249 key.encode_to_vec(&mut extension_content).expect("vec");
250
251 let mut ext = rcgen::CustomExtension::from_oid_content(&P2P_EXT_OID, extension_content);
253 ext.set_criticality(true);
254
255 Ok(ext)
256}
257
258impl P2pCertificate<'_> {
259 pub fn peer_id(&self) -> PublicKey {
261 self.extension.public_key
262 }
263
264 pub fn verify_signature(
267 &self,
268 signature_scheme: rustls::SignatureScheme,
269 message: &[u8],
270 signature: &[u8],
271 ) -> Result<(), VerificationError> {
272 let pk = self.public_key(signature_scheme)?;
273 pk.verify(message, signature)
274 .map_err(|_| webpki::Error::InvalidSignatureForPublicKey)?;
275
276 Ok(())
277 }
278
279 fn public_key(
283 &self,
284 signature_scheme: rustls::SignatureScheme,
285 ) -> Result<ring::signature::UnparsedPublicKey<&[u8]>, webpki::Error> {
286 use ring::signature;
287 use rustls::SignatureScheme::*;
288
289 let current_signature_scheme = self.signature_scheme()?;
290 if signature_scheme != current_signature_scheme {
291 return Err(webpki::Error::UnsupportedSignatureAlgorithmForPublicKey);
293 }
294
295 let verification_algorithm: &dyn signature::VerificationAlgorithm = match signature_scheme {
296 ECDSA_NISTP256_SHA256 => &signature::ECDSA_P256_SHA256_ASN1,
297 ECDSA_NISTP384_SHA384 => &signature::ECDSA_P384_SHA384_ASN1,
298 ECDSA_NISTP521_SHA512 => {
299 return Err(webpki::Error::UnsupportedSignatureAlgorithm);
301 }
302 ED25519 => &signature::ED25519,
303 ED448 => {
304 return Err(webpki::Error::UnsupportedSignatureAlgorithm);
306 }
307 RSA_PKCS1_SHA256 | RSA_PKCS1_SHA384 | RSA_PKCS1_SHA512 | RSA_PSS_SHA256
309 | RSA_PSS_SHA384 | RSA_PSS_SHA512 => {
310 return Err(webpki::Error::UnsupportedSignatureAlgorithm)
311 }
312 RSA_PKCS1_SHA1 => return Err(webpki::Error::UnsupportedSignatureAlgorithm),
316 ECDSA_SHA1_Legacy => return Err(webpki::Error::UnsupportedSignatureAlgorithm),
317 Unknown(_) => return Err(webpki::Error::UnsupportedSignatureAlgorithm),
318 _ => return Err(webpki::Error::UnsupportedSignatureAlgorithm),
319 };
320 let spki = &self.certificate.tbs_certificate.subject_pki;
321 let key = signature::UnparsedPublicKey::new(
322 verification_algorithm,
323 spki.subject_public_key.as_ref(),
324 );
325
326 Ok(key)
327 }
328
329 fn verify(&self) -> Result<(), webpki::Error> {
337 use webpki::Error;
338
339 if !self.certificate.validity().is_valid() {
342 return Err(Error::InvalidCertValidity);
343 }
344
345 let signature_scheme = self.signature_scheme()?;
351 let raw_certificate = self.certificate.tbs_certificate.as_ref();
354 let signature = self.certificate.signature_value.as_ref();
355 self.verify_signature(signature_scheme, raw_certificate, signature)
357 .map_err(|_| Error::SignatureAlgorithmMismatch)?;
358
359 let subject_pki = self.certificate.public_key().raw;
360
361 let mut msg = vec![];
365 msg.extend(P2P_SIGNING_PREFIX);
366 msg.extend(subject_pki);
367
368 let user_owns_sk = self
373 .extension
374 .public_key
375 .verify(&msg, &self.extension.signature)
376 .is_ok();
377 if !user_owns_sk {
378 return Err(Error::UnknownIssuer);
379 }
380
381 Ok(())
382 }
383
384 fn signature_scheme(&self) -> Result<rustls::SignatureScheme, webpki::Error> {
388 use oid_registry::*;
391 use rustls::SignatureScheme::*;
392
393 let signature_algorithm = &self.certificate.signature_algorithm;
394 let pki_algorithm = &self.certificate.tbs_certificate.subject_pki.algorithm;
395
396 if pki_algorithm.algorithm == OID_KEY_TYPE_EC_PUBLIC_KEY {
397 let signature_param = pki_algorithm
398 .parameters
399 .as_ref()
400 .ok_or(webpki::Error::BadDer)?
401 .as_oid()
402 .map_err(|_| webpki::Error::BadDer)?;
403 if signature_param == OID_EC_P256
404 && signature_algorithm.algorithm == OID_SIG_ECDSA_WITH_SHA256
405 {
406 return Ok(ECDSA_NISTP256_SHA256);
407 }
408 if signature_param == OID_NIST_EC_P384
409 && signature_algorithm.algorithm == OID_SIG_ECDSA_WITH_SHA384
410 {
411 return Ok(ECDSA_NISTP384_SHA384);
412 }
413 if signature_param == OID_NIST_EC_P521
414 && signature_algorithm.algorithm == OID_SIG_ECDSA_WITH_SHA512
415 {
416 return Ok(ECDSA_NISTP521_SHA512);
417 }
418 return Err(webpki::Error::UnsupportedSignatureAlgorithm);
419 }
420
421 if signature_algorithm.algorithm == OID_SIG_ED25519 {
422 return Ok(ED25519);
423 }
424 if signature_algorithm.algorithm == OID_SIG_ED448 {
425 return Ok(ED448);
426 }
427
428 Err(webpki::Error::UnsupportedSignatureAlgorithm)
429 }
430}
431
432#[cfg(test)]
433mod tests {
434 use super::*;
435
436 #[test]
437 fn sanity_check() {
438 let secret_key = SecretKey::generate();
439
440 let (cert, _) = generate(&secret_key).unwrap();
441 let parsed_cert = parse(&cert).unwrap();
442
443 assert!(parsed_cert.verify().is_ok());
444 assert_eq!(secret_key.public(), parsed_cert.extension.public_key);
445 }
446}