1use std::fmt;
4
5use serde::{Deserialize, Serialize};
6use tls_core::{
7 msgs::{
8 codec::Codec,
9 enums::NamedGroup,
10 handshake::{DigitallySignedStruct, ServerECDHParams},
11 },
12 verify::{ServerCertVerifier as _, WebPkiVerifier},
13};
14use web_time::{Duration, UNIX_EPOCH};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
18#[serde(rename_all = "snake_case")]
19pub enum TlsVersion {
20 V1_2,
22 V1_3,
24}
25
26impl TryFrom<tls_core::msgs::enums::ProtocolVersion> for TlsVersion {
27 type Error = &'static str;
28
29 fn try_from(value: tls_core::msgs::enums::ProtocolVersion) -> Result<Self, Self::Error> {
30 Ok(match value {
31 tls_core::msgs::enums::ProtocolVersion::TLSv1_2 => TlsVersion::V1_2,
32 tls_core::msgs::enums::ProtocolVersion::TLSv1_3 => TlsVersion::V1_3,
33 _ => return Err("unsupported TLS version"),
34 })
35 }
36}
37
38#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
40pub struct ServerName(String);
41
42impl ServerName {
43 pub fn new(name: String) -> Self {
45 Self(name)
46 }
47
48 pub fn as_str(&self) -> &str {
50 &self.0
51 }
52}
53
54impl From<&str> for ServerName {
55 fn from(name: &str) -> Self {
56 Self(name.to_string())
57 }
58}
59
60impl AsRef<str> for ServerName {
61 fn as_ref(&self) -> &str {
62 &self.0
63 }
64}
65
66impl fmt::Display for ServerName {
67 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68 write!(f, "{}", self.0)
69 }
70}
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
74#[serde(rename_all = "lowercase")]
75#[non_exhaustive]
76#[allow(non_camel_case_types)]
77pub enum KeyType {
78 SECP256R1 = 0x0017,
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
84#[serde(rename_all = "lowercase")]
85#[allow(non_camel_case_types, missing_docs)]
86pub enum SignatureScheme {
87 RSA_PKCS1_SHA1 = 0x0201,
88 ECDSA_SHA1_Legacy = 0x0203,
89 RSA_PKCS1_SHA256 = 0x0401,
90 ECDSA_NISTP256_SHA256 = 0x0403,
91 RSA_PKCS1_SHA384 = 0x0501,
92 ECDSA_NISTP384_SHA384 = 0x0503,
93 RSA_PKCS1_SHA512 = 0x0601,
94 ECDSA_NISTP521_SHA512 = 0x0603,
95 RSA_PSS_SHA256 = 0x0804,
96 RSA_PSS_SHA384 = 0x0805,
97 RSA_PSS_SHA512 = 0x0806,
98 ED25519 = 0x0807,
99}
100
101impl TryFrom<tls_core::msgs::enums::SignatureScheme> for SignatureScheme {
102 type Error = &'static str;
103
104 fn try_from(value: tls_core::msgs::enums::SignatureScheme) -> Result<Self, Self::Error> {
105 use tls_core::msgs::enums::SignatureScheme as Core;
106 use SignatureScheme::*;
107 Ok(match value {
108 Core::RSA_PKCS1_SHA1 => RSA_PKCS1_SHA1,
109 Core::ECDSA_SHA1_Legacy => ECDSA_SHA1_Legacy,
110 Core::RSA_PKCS1_SHA256 => RSA_PKCS1_SHA256,
111 Core::ECDSA_NISTP256_SHA256 => ECDSA_NISTP256_SHA256,
112 Core::RSA_PKCS1_SHA384 => RSA_PKCS1_SHA384,
113 Core::ECDSA_NISTP384_SHA384 => ECDSA_NISTP384_SHA384,
114 Core::RSA_PKCS1_SHA512 => RSA_PKCS1_SHA512,
115 Core::ECDSA_NISTP521_SHA512 => ECDSA_NISTP521_SHA512,
116 Core::RSA_PSS_SHA256 => RSA_PSS_SHA256,
117 Core::RSA_PSS_SHA384 => RSA_PSS_SHA384,
118 Core::RSA_PSS_SHA512 => RSA_PSS_SHA512,
119 Core::ED25519 => ED25519,
120 _ => return Err("unsupported signature scheme"),
121 })
122 }
123}
124
125impl From<SignatureScheme> for tls_core::msgs::enums::SignatureScheme {
126 fn from(value: SignatureScheme) -> Self {
127 use tls_core::msgs::enums::SignatureScheme::*;
128 match value {
129 SignatureScheme::RSA_PKCS1_SHA1 => RSA_PKCS1_SHA1,
130 SignatureScheme::ECDSA_SHA1_Legacy => ECDSA_SHA1_Legacy,
131 SignatureScheme::RSA_PKCS1_SHA256 => RSA_PKCS1_SHA256,
132 SignatureScheme::ECDSA_NISTP256_SHA256 => ECDSA_NISTP256_SHA256,
133 SignatureScheme::RSA_PKCS1_SHA384 => RSA_PKCS1_SHA384,
134 SignatureScheme::ECDSA_NISTP384_SHA384 => ECDSA_NISTP384_SHA384,
135 SignatureScheme::RSA_PKCS1_SHA512 => RSA_PKCS1_SHA512,
136 SignatureScheme::ECDSA_NISTP521_SHA512 => ECDSA_NISTP521_SHA512,
137 SignatureScheme::RSA_PSS_SHA256 => RSA_PSS_SHA256,
138 SignatureScheme::RSA_PSS_SHA384 => RSA_PSS_SHA384,
139 SignatureScheme::RSA_PSS_SHA512 => RSA_PSS_SHA512,
140 SignatureScheme::ED25519 => ED25519,
141 }
142 }
143}
144
145#[derive(Debug, Clone, Serialize, Deserialize)]
147pub struct Certificate(pub Vec<u8>);
148
149impl From<tls_core::key::Certificate> for Certificate {
150 fn from(cert: tls_core::key::Certificate) -> Self {
151 Self(cert.0)
152 }
153}
154
155#[derive(Debug, Clone, Serialize, Deserialize)]
157pub struct ServerSignature {
158 pub scheme: SignatureScheme,
160 pub sig: Vec<u8>,
162}
163
164#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
166pub struct ServerEphemKey {
167 #[serde(rename = "type")]
169 pub typ: KeyType,
170 pub key: Vec<u8>,
172}
173
174impl ServerEphemKey {
175 pub(crate) fn kx_params(&self) -> Vec<u8> {
177 let group = match self.typ {
178 KeyType::SECP256R1 => NamedGroup::secp256r1,
179 };
180
181 let mut kx_params = Vec::new();
182 ServerECDHParams::new(group, &self.key).encode(&mut kx_params);
183
184 kx_params
185 }
186}
187
188impl TryFrom<tls_core::key::PublicKey> for ServerEphemKey {
189 type Error = &'static str;
190
191 fn try_from(value: tls_core::key::PublicKey) -> Result<Self, Self::Error> {
192 let tls_core::msgs::enums::NamedGroup::secp256r1 = value.group else {
193 return Err("unsupported key type");
194 };
195
196 Ok(ServerEphemKey {
197 typ: KeyType::SECP256R1,
198 key: value.key,
199 })
200 }
201}
202
203#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
205pub struct ConnectionInfo {
206 pub time: u64,
208 pub version: TlsVersion,
210 pub transcript_length: TranscriptLength,
212}
213
214#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
216pub struct TranscriptLength {
217 pub sent: u32,
219 pub received: u32,
221}
222
223#[derive(Debug, Clone, Serialize, Deserialize)]
225pub struct HandshakeDataV1_2 {
226 pub client_random: [u8; 32],
228 pub server_random: [u8; 32],
230 pub server_ephemeral_key: ServerEphemKey,
232}
233
234#[derive(Debug, Clone, Serialize, Deserialize)]
236#[serde(rename_all = "snake_case")]
237#[non_exhaustive]
238pub enum HandshakeData {
239 V1_2(HandshakeDataV1_2),
241}
242
243#[derive(Debug, Clone, Serialize, Deserialize)]
245pub struct VerifyData {
246 pub client_finished: Vec<u8>,
248 pub server_finished: Vec<u8>,
250}
251
252#[derive(Debug, Clone, Serialize, Deserialize)]
254pub struct ServerCertData {
255 pub certs: Vec<Certificate>,
257 pub sig: ServerSignature,
259 pub handshake: HandshakeData,
261}
262
263impl ServerCertData {
264 pub fn verify(
273 &self,
274 verifier: &WebPkiVerifier,
275 time: u64,
276 server_ephemeral_key: &ServerEphemKey,
277 server_name: &ServerName,
278 ) -> Result<(), CertificateVerificationError> {
279 #[allow(irrefutable_let_patterns)]
280 let HandshakeData::V1_2(HandshakeDataV1_2 {
281 client_random,
282 server_random,
283 server_ephemeral_key: expected_server_ephemeral_key,
284 }) = &self.handshake
285 else {
286 unreachable!("only TLS 1.2 is implemented")
287 };
288
289 if server_ephemeral_key != expected_server_ephemeral_key {
290 return Err(CertificateVerificationError::InvalidServerEphemeralKey);
291 }
292
293 let server_name = tls_core::dns::ServerName::try_from(server_name.as_ref())
295 .map_err(|_| CertificateVerificationError::InvalidIdentity(server_name.clone()))?;
296
297 let cert_chain = self
299 .certs
300 .clone()
301 .into_iter()
302 .map(|cert| tls_core::key::Certificate(cert.0))
303 .collect::<Vec<_>>();
304
305 let (end_entity, intermediates) = cert_chain
306 .split_first()
307 .ok_or(CertificateVerificationError::MissingCerts)?;
308
309 verifier
312 .verify_server_cert(
313 end_entity,
314 intermediates,
315 &server_name,
316 &mut [].into_iter(),
317 &[],
318 UNIX_EPOCH + Duration::from_secs(time),
319 )
320 .map_err(|_| CertificateVerificationError::InvalidCert)?;
321
322 let mut message = Vec::new();
324 message.extend_from_slice(client_random);
325 message.extend_from_slice(server_random);
326 message.extend_from_slice(&server_ephemeral_key.kx_params());
327
328 let dss = DigitallySignedStruct::new(self.sig.scheme.into(), self.sig.sig.clone());
329
330 verifier
331 .verify_tls12_signature(&message, end_entity, &dss)
332 .map_err(|_| CertificateVerificationError::InvalidServerSignature)?;
333
334 Ok(())
335 }
336}
337
338#[derive(Debug, thiserror::Error)]
340#[allow(missing_docs)]
341pub enum CertificateVerificationError {
342 #[error("invalid server identity: {0}")]
343 InvalidIdentity(ServerName),
344 #[error("missing server certificates")]
345 MissingCerts,
346 #[error("invalid server certificate")]
347 InvalidCert,
348 #[error("invalid server signature")]
349 InvalidServerSignature,
350 #[error("invalid server ephemeral key")]
351 InvalidServerEphemeralKey,
352}
353
354#[cfg(test)]
355mod tests {
356 use super::*;
357 use crate::{fixtures::ConnectionFixture, transcript::Transcript};
358
359 use hex::FromHex;
360 use rstest::*;
361 use tls_core::{
362 anchors::{OwnedTrustAnchor, RootCertStore},
363 verify::WebPkiVerifier,
364 };
365 use tlsn_data_fixtures::http::{request::GET_WITH_HEADER, response::OK_JSON};
366
367 #[fixture]
368 #[once]
369 fn verifier() -> WebPkiVerifier {
370 let mut root_store = RootCertStore::empty();
371 root_store.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.iter().map(|ta| {
372 OwnedTrustAnchor::from_subject_spki_name_constraints(
373 ta.subject.as_ref(),
374 ta.subject_public_key_info.as_ref(),
375 ta.name_constraints.as_ref().map(|nc| nc.as_ref()),
376 )
377 }));
378
379 let cert = tls_core::key::Certificate(
381 appliedzkp()
382 .server_cert_data
383 .certs
384 .last()
385 .expect("chain is valid")
386 .0
387 .clone(),
388 );
389
390 root_store.add(&cert).unwrap();
391
392 WebPkiVerifier::new(root_store, None)
393 }
394
395 fn tlsnotary() -> ConnectionFixture {
396 ConnectionFixture::tlsnotary(Transcript::new(GET_WITH_HEADER, OK_JSON).length())
397 }
398
399 fn appliedzkp() -> ConnectionFixture {
400 ConnectionFixture::appliedzkp(Transcript::new(GET_WITH_HEADER, OK_JSON).length())
401 }
402
403 #[rstest]
405 #[case::tlsnotary(tlsnotary())]
406 #[case::appliedzkp(appliedzkp())]
407 fn test_verify_cert_chain_sucess_ca_implicit(
408 verifier: &WebPkiVerifier,
409 #[case] mut data: ConnectionFixture,
410 ) {
411 data.server_cert_data.certs.pop();
413
414 assert!(data
415 .server_cert_data
416 .verify(
417 verifier,
418 data.connection_info.time,
419 data.server_ephemeral_key(),
420 &ServerName::from(data.server_name.as_ref()),
421 )
422 .is_ok());
423 }
424
425 #[rstest]
428 #[case::tlsnotary(tlsnotary())]
429 #[case::appliedzkp(appliedzkp())]
430 fn test_verify_cert_chain_success_ca_explicit(
431 verifier: &WebPkiVerifier,
432 #[case] data: ConnectionFixture,
433 ) {
434 assert!(data
435 .server_cert_data
436 .verify(
437 verifier,
438 data.connection_info.time,
439 data.server_ephemeral_key(),
440 &ServerName::from(data.server_name.as_ref()),
441 )
442 .is_ok());
443 }
444
445 #[rstest]
447 #[case::tlsnotary(tlsnotary())]
448 #[case::appliedzkp(appliedzkp())]
449 fn test_verify_cert_chain_fail_bad_time(
450 verifier: &WebPkiVerifier,
451 #[case] data: ConnectionFixture,
452 ) {
453 let bad_time: u64 = 1571465711;
455
456 let err = data.server_cert_data.verify(
457 verifier,
458 bad_time,
459 data.server_ephemeral_key(),
460 &ServerName::from(data.server_name.as_ref()),
461 );
462
463 assert!(matches!(
464 err.unwrap_err(),
465 CertificateVerificationError::InvalidCert
466 ));
467 }
468
469 #[rstest]
471 #[case::tlsnotary(tlsnotary())]
472 #[case::appliedzkp(appliedzkp())]
473 fn test_verify_cert_chain_fail_no_interm_cert(
474 verifier: &WebPkiVerifier,
475 #[case] mut data: ConnectionFixture,
476 ) {
477 data.server_cert_data.certs.pop();
479 data.server_cert_data.certs.pop();
481
482 let err = data.server_cert_data.verify(
483 verifier,
484 data.connection_info.time,
485 data.server_ephemeral_key(),
486 &ServerName::from(data.server_name.as_ref()),
487 );
488
489 assert!(matches!(
490 err.unwrap_err(),
491 CertificateVerificationError::InvalidCert
492 ));
493 }
494
495 #[rstest]
498 #[case::tlsnotary(tlsnotary())]
499 #[case::appliedzkp(appliedzkp())]
500 fn test_verify_cert_chain_fail_no_interm_cert_with_ca_cert(
501 verifier: &WebPkiVerifier,
502 #[case] mut data: ConnectionFixture,
503 ) {
504 data.server_cert_data.certs.remove(1);
506
507 let err = data.server_cert_data.verify(
508 verifier,
509 data.connection_info.time,
510 data.server_ephemeral_key(),
511 &ServerName::from(data.server_name.as_ref()),
512 );
513
514 assert!(matches!(
515 err.unwrap_err(),
516 CertificateVerificationError::InvalidCert
517 ));
518 }
519
520 #[rstest]
522 #[case::tlsnotary(tlsnotary())]
523 #[case::appliedzkp(appliedzkp())]
524 fn test_verify_cert_chain_fail_bad_ee_cert(
525 verifier: &WebPkiVerifier,
526 #[case] mut data: ConnectionFixture,
527 ) {
528 let ee: &[u8] = include_bytes!("./fixtures/data/unknown/ee.der");
529
530 data.server_cert_data.certs[0] = Certificate(ee.to_vec());
532
533 let err = data.server_cert_data.verify(
534 verifier,
535 data.connection_info.time,
536 data.server_ephemeral_key(),
537 &ServerName::from(data.server_name.as_ref()),
538 );
539
540 assert!(matches!(
541 err.unwrap_err(),
542 CertificateVerificationError::InvalidCert
543 ));
544 }
545
546 #[rstest]
548 #[case::tlsnotary(tlsnotary())]
549 #[case::appliedzkp(appliedzkp())]
550 fn test_verify_sig_ke_params_fail_bad_client_random(
551 verifier: &WebPkiVerifier,
552 #[case] mut data: ConnectionFixture,
553 ) {
554 let HandshakeData::V1_2(HandshakeDataV1_2 { client_random, .. }) =
555 &mut data.server_cert_data.handshake;
556 client_random[31] = client_random[31].wrapping_add(1);
557
558 let err = data.server_cert_data.verify(
559 verifier,
560 data.connection_info.time,
561 data.server_ephemeral_key(),
562 &ServerName::from(data.server_name.as_ref()),
563 );
564
565 assert!(matches!(
566 err.unwrap_err(),
567 CertificateVerificationError::InvalidServerSignature
568 ));
569 }
570
571 #[rstest]
573 #[case::tlsnotary(tlsnotary())]
574 #[case::appliedzkp(appliedzkp())]
575 fn test_verify_sig_ke_params_fail_bad_sig(
576 verifier: &WebPkiVerifier,
577 #[case] mut data: ConnectionFixture,
578 ) {
579 data.server_cert_data.sig.sig[31] = data.server_cert_data.sig.sig[31].wrapping_add(1);
580
581 let err = data.server_cert_data.verify(
582 verifier,
583 data.connection_info.time,
584 data.server_ephemeral_key(),
585 &ServerName::from(data.server_name.as_ref()),
586 );
587
588 assert!(matches!(
589 err.unwrap_err(),
590 CertificateVerificationError::InvalidServerSignature
591 ));
592 }
593
594 #[rstest]
596 #[case::tlsnotary(tlsnotary())]
597 #[case::appliedzkp(appliedzkp())]
598 fn test_check_dns_name_present_in_cert_fail_bad_host(
599 verifier: &WebPkiVerifier,
600 #[case] data: ConnectionFixture,
601 ) {
602 let bad_name = ServerName::from("badhost.com");
603
604 let err = data.server_cert_data.verify(
605 verifier,
606 data.connection_info.time,
607 data.server_ephemeral_key(),
608 &bad_name,
609 );
610
611 assert!(matches!(
612 err.unwrap_err(),
613 CertificateVerificationError::InvalidCert
614 ));
615 }
616
617 #[rstest]
619 #[case::tlsnotary(tlsnotary())]
620 #[case::appliedzkp(appliedzkp())]
621 fn test_invalid_ephemeral_key(verifier: &WebPkiVerifier, #[case] data: ConnectionFixture) {
622 let wrong_ephemeral_key = ServerEphemKey {
623 typ: KeyType::SECP256R1,
624 key: Vec::<u8>::from_hex(include_bytes!("./fixtures/data/unknown/pubkey")).unwrap(),
625 };
626
627 let err = data.server_cert_data.verify(
628 verifier,
629 data.connection_info.time,
630 &wrong_ephemeral_key,
631 &ServerName::from(data.server_name.as_ref()),
632 );
633
634 assert!(matches!(
635 err.unwrap_err(),
636 CertificateVerificationError::InvalidServerEphemeralKey
637 ));
638 }
639
640 #[rstest]
642 #[case::tlsnotary(tlsnotary())]
643 #[case::appliedzkp(appliedzkp())]
644 fn test_verify_cert_chain_fail_no_cert(
645 verifier: &WebPkiVerifier,
646 #[case] mut data: ConnectionFixture,
647 ) {
648 data.server_cert_data.certs = Vec::new();
650
651 let err = data.server_cert_data.verify(
652 verifier,
653 data.connection_info.time,
654 data.server_ephemeral_key(),
655 &ServerName::from(data.server_name.as_ref()),
656 );
657
658 assert!(matches!(
659 err.unwrap_err(),
660 CertificateVerificationError::MissingCerts
661 ));
662 }
663}