1use std::fmt;
4
5use rustls_pki_types as webpki_types;
6use serde::{Deserialize, Serialize};
7use tls_core::msgs::{codec::Codec, enums::NamedGroup, handshake::ServerECDHParams};
8
9use crate::webpki::{CertificateDer, ServerCertVerifier, ServerCertVerifierError};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
13#[serde(rename_all = "snake_case")]
14pub enum TlsVersion {
15 V1_2,
17 V1_3,
19}
20
21impl TryFrom<tls_core::msgs::enums::ProtocolVersion> for TlsVersion {
22 type Error = &'static str;
23
24 fn try_from(value: tls_core::msgs::enums::ProtocolVersion) -> Result<Self, Self::Error> {
25 Ok(match value {
26 tls_core::msgs::enums::ProtocolVersion::TLSv1_2 => TlsVersion::V1_2,
27 tls_core::msgs::enums::ProtocolVersion::TLSv1_3 => TlsVersion::V1_3,
28 _ => return Err("unsupported TLS version"),
29 })
30 }
31}
32
33#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
35pub enum ServerName {
36 Dns(DnsName),
38}
39
40impl ServerName {
41 pub(crate) fn to_webpki(&self) -> webpki_types::ServerName<'static> {
42 match self {
43 ServerName::Dns(name) => webpki_types::ServerName::DnsName(
44 webpki_types::DnsName::try_from(name.0.as_str())
45 .expect("name was validated")
46 .to_owned(),
47 ),
48 }
49 }
50}
51
52impl fmt::Display for ServerName {
53 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54 match self {
55 ServerName::Dns(name) => write!(f, "{name}"),
56 }
57 }
58}
59
60#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
62#[serde(try_from = "String")]
63pub struct DnsName(String);
64
65impl DnsName {
66 pub fn as_str(&self) -> &str {
68 self.0.as_str()
69 }
70}
71
72impl fmt::Display for DnsName {
73 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74 write!(f, "{}", self.0)
75 }
76}
77
78impl AsRef<str> for DnsName {
79 fn as_ref(&self) -> &str {
80 &self.0
81 }
82}
83
84#[derive(Debug, thiserror::Error)]
86#[error("invalid DNS name")]
87pub struct InvalidDnsNameError {}
88
89impl TryFrom<&str> for DnsName {
90 type Error = InvalidDnsNameError;
91
92 fn try_from(value: &str) -> Result<Self, Self::Error> {
93 match webpki_types::DnsName::try_from_str(value) {
95 Ok(_) => Ok(DnsName(value.to_string())),
96 Err(_) => Err(InvalidDnsNameError {}),
97 }
98 }
99}
100
101impl TryFrom<String> for DnsName {
102 type Error = InvalidDnsNameError;
103
104 fn try_from(value: String) -> Result<Self, Self::Error> {
105 Self::try_from(value.as_str())
106 }
107}
108
109#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
111#[serde(rename_all = "lowercase")]
112#[non_exhaustive]
113#[allow(non_camel_case_types)]
114pub enum KeyType {
115 SECP256R1 = 0x0017,
117}
118
119#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
121#[serde(rename_all = "lowercase")]
122#[allow(non_camel_case_types, missing_docs)]
123pub enum SignatureScheme {
124 RSA_PKCS1_SHA1 = 0x0201,
125 ECDSA_SHA1_Legacy = 0x0203,
126 RSA_PKCS1_SHA256 = 0x0401,
127 ECDSA_NISTP256_SHA256 = 0x0403,
128 RSA_PKCS1_SHA384 = 0x0501,
129 ECDSA_NISTP384_SHA384 = 0x0503,
130 RSA_PKCS1_SHA512 = 0x0601,
131 ECDSA_NISTP521_SHA512 = 0x0603,
132 RSA_PSS_SHA256 = 0x0804,
133 RSA_PSS_SHA384 = 0x0805,
134 RSA_PSS_SHA512 = 0x0806,
135 ED25519 = 0x0807,
136}
137
138impl fmt::Display for SignatureScheme {
139 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140 match self {
141 SignatureScheme::RSA_PKCS1_SHA1 => write!(f, "RSA_PKCS1_SHA1"),
142 SignatureScheme::ECDSA_SHA1_Legacy => write!(f, "ECDSA_SHA1_Legacy"),
143 SignatureScheme::RSA_PKCS1_SHA256 => write!(f, "RSA_PKCS1_SHA256"),
144 SignatureScheme::ECDSA_NISTP256_SHA256 => write!(f, "ECDSA_NISTP256_SHA256"),
145 SignatureScheme::RSA_PKCS1_SHA384 => write!(f, "RSA_PKCS1_SHA384"),
146 SignatureScheme::ECDSA_NISTP384_SHA384 => write!(f, "ECDSA_NISTP384_SHA384"),
147 SignatureScheme::RSA_PKCS1_SHA512 => write!(f, "RSA_PKCS1_SHA512"),
148 SignatureScheme::ECDSA_NISTP521_SHA512 => write!(f, "ECDSA_NISTP521_SHA512"),
149 SignatureScheme::RSA_PSS_SHA256 => write!(f, "RSA_PSS_SHA256"),
150 SignatureScheme::RSA_PSS_SHA384 => write!(f, "RSA_PSS_SHA384"),
151 SignatureScheme::RSA_PSS_SHA512 => write!(f, "RSA_PSS_SHA512"),
152 SignatureScheme::ED25519 => write!(f, "ED25519"),
153 }
154 }
155}
156
157impl TryFrom<tls_core::msgs::enums::SignatureScheme> for SignatureScheme {
158 type Error = &'static str;
159
160 fn try_from(value: tls_core::msgs::enums::SignatureScheme) -> Result<Self, Self::Error> {
161 use tls_core::msgs::enums::SignatureScheme as Core;
162 use SignatureScheme::*;
163 Ok(match value {
164 Core::RSA_PKCS1_SHA1 => RSA_PKCS1_SHA1,
165 Core::ECDSA_SHA1_Legacy => ECDSA_SHA1_Legacy,
166 Core::RSA_PKCS1_SHA256 => RSA_PKCS1_SHA256,
167 Core::ECDSA_NISTP256_SHA256 => ECDSA_NISTP256_SHA256,
168 Core::RSA_PKCS1_SHA384 => RSA_PKCS1_SHA384,
169 Core::ECDSA_NISTP384_SHA384 => ECDSA_NISTP384_SHA384,
170 Core::RSA_PKCS1_SHA512 => RSA_PKCS1_SHA512,
171 Core::ECDSA_NISTP521_SHA512 => ECDSA_NISTP521_SHA512,
172 Core::RSA_PSS_SHA256 => RSA_PSS_SHA256,
173 Core::RSA_PSS_SHA384 => RSA_PSS_SHA384,
174 Core::RSA_PSS_SHA512 => RSA_PSS_SHA512,
175 Core::ED25519 => ED25519,
176 _ => return Err("unsupported signature scheme"),
177 })
178 }
179}
180
181impl From<SignatureScheme> for tls_core::msgs::enums::SignatureScheme {
182 fn from(value: SignatureScheme) -> Self {
183 use tls_core::msgs::enums::SignatureScheme::*;
184 match value {
185 SignatureScheme::RSA_PKCS1_SHA1 => RSA_PKCS1_SHA1,
186 SignatureScheme::ECDSA_SHA1_Legacy => ECDSA_SHA1_Legacy,
187 SignatureScheme::RSA_PKCS1_SHA256 => RSA_PKCS1_SHA256,
188 SignatureScheme::ECDSA_NISTP256_SHA256 => ECDSA_NISTP256_SHA256,
189 SignatureScheme::RSA_PKCS1_SHA384 => RSA_PKCS1_SHA384,
190 SignatureScheme::ECDSA_NISTP384_SHA384 => ECDSA_NISTP384_SHA384,
191 SignatureScheme::RSA_PKCS1_SHA512 => RSA_PKCS1_SHA512,
192 SignatureScheme::ECDSA_NISTP521_SHA512 => ECDSA_NISTP521_SHA512,
193 SignatureScheme::RSA_PSS_SHA256 => RSA_PSS_SHA256,
194 SignatureScheme::RSA_PSS_SHA384 => RSA_PSS_SHA384,
195 SignatureScheme::RSA_PSS_SHA512 => RSA_PSS_SHA512,
196 SignatureScheme::ED25519 => ED25519,
197 }
198 }
199}
200
201#[derive(Debug, Clone, Serialize, Deserialize)]
203pub struct ServerSignature {
204 pub scheme: SignatureScheme,
206 pub sig: Vec<u8>,
208}
209
210#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
212pub struct ServerEphemKey {
213 #[serde(rename = "type")]
215 pub typ: KeyType,
216 pub key: Vec<u8>,
218}
219
220impl ServerEphemKey {
221 pub(crate) fn kx_params(&self) -> Vec<u8> {
223 let group = match self.typ {
224 KeyType::SECP256R1 => NamedGroup::secp256r1,
225 };
226
227 let mut kx_params = Vec::new();
228 ServerECDHParams::new(group, &self.key).encode(&mut kx_params);
229
230 kx_params
231 }
232}
233
234impl TryFrom<tls_core::key::PublicKey> for ServerEphemKey {
235 type Error = &'static str;
236
237 fn try_from(value: tls_core::key::PublicKey) -> Result<Self, Self::Error> {
238 let tls_core::msgs::enums::NamedGroup::secp256r1 = value.group else {
239 return Err("unsupported key type");
240 };
241
242 Ok(ServerEphemKey {
243 typ: KeyType::SECP256R1,
244 key: value.key,
245 })
246 }
247}
248
249#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
251pub struct ConnectionInfo {
252 pub time: u64,
254 pub version: TlsVersion,
256 pub transcript_length: TranscriptLength,
258}
259
260#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
262pub struct TranscriptLength {
263 pub sent: u32,
265 pub received: u32,
267}
268
269#[derive(Debug, Clone, Serialize, Deserialize)]
271pub struct CertBindingV1_2 {
272 pub client_random: [u8; 32],
274 pub server_random: [u8; 32],
276 pub server_ephemeral_key: ServerEphemKey,
278}
279
280#[derive(Debug, Clone, Serialize, Deserialize)]
287#[serde(rename_all = "snake_case")]
288#[non_exhaustive]
289pub enum CertBinding {
290 V1_2(CertBindingV1_2),
292}
293
294#[derive(Debug, Clone, Serialize, Deserialize)]
296pub struct VerifyData {
297 pub client_finished: Vec<u8>,
299 pub server_finished: Vec<u8>,
301}
302
303#[derive(Debug, Clone, Serialize, Deserialize)]
305pub struct HandshakeData {
306 pub certs: Vec<CertificateDer>,
308 pub sig: ServerSignature,
310 pub binding: CertBinding,
312}
313
314impl HandshakeData {
315 pub fn verify(
324 &self,
325 verifier: &ServerCertVerifier,
326 time: u64,
327 server_ephemeral_key: &ServerEphemKey,
328 server_name: &ServerName,
329 ) -> Result<(), HandshakeVerificationError> {
330 #[allow(irrefutable_let_patterns)]
331 let CertBinding::V1_2(CertBindingV1_2 {
332 client_random,
333 server_random,
334 server_ephemeral_key: expected_server_ephemeral_key,
335 }) = &self.binding
336 else {
337 unreachable!("only TLS 1.2 is implemented")
338 };
339
340 if server_ephemeral_key != expected_server_ephemeral_key {
341 return Err(HandshakeVerificationError::InvalidServerEphemeralKey);
342 }
343
344 let (end_entity, intermediates) = self
345 .certs
346 .split_first()
347 .ok_or(HandshakeVerificationError::MissingCerts)?;
348
349 verifier
352 .verify_server_cert(end_entity, intermediates, server_name, time)
353 .map_err(HandshakeVerificationError::ServerCert)?;
354
355 let mut message = Vec::new();
357 message.extend_from_slice(client_random);
358 message.extend_from_slice(server_random);
359 message.extend_from_slice(&server_ephemeral_key.kx_params());
360
361 use webpki::ring as alg;
362 let sig_alg = match self.sig.scheme {
363 SignatureScheme::RSA_PKCS1_SHA256 => alg::RSA_PKCS1_2048_8192_SHA256,
364 SignatureScheme::RSA_PKCS1_SHA384 => alg::RSA_PKCS1_2048_8192_SHA384,
365 SignatureScheme::RSA_PKCS1_SHA512 => alg::RSA_PKCS1_2048_8192_SHA512,
366 SignatureScheme::RSA_PSS_SHA256 => alg::RSA_PSS_2048_8192_SHA256_LEGACY_KEY,
367 SignatureScheme::RSA_PSS_SHA384 => alg::RSA_PSS_2048_8192_SHA384_LEGACY_KEY,
368 SignatureScheme::RSA_PSS_SHA512 => alg::RSA_PSS_2048_8192_SHA512_LEGACY_KEY,
369 SignatureScheme::ECDSA_NISTP256_SHA256 => alg::ECDSA_P256_SHA256,
370 SignatureScheme::ECDSA_NISTP384_SHA384 => alg::ECDSA_P384_SHA384,
371 SignatureScheme::ED25519 => alg::ED25519,
372 scheme => {
373 return Err(HandshakeVerificationError::UnsupportedSignatureScheme(
374 scheme,
375 ))
376 }
377 };
378
379 let end_entity = webpki_types::CertificateDer::from(end_entity.0.as_slice());
380 let end_entity = webpki::EndEntityCert::try_from(&end_entity)
381 .map_err(|_| HandshakeVerificationError::InvalidEndEntityCertificate)?;
382
383 end_entity
384 .verify_signature(sig_alg, &message, &self.sig.sig)
385 .map_err(|_| HandshakeVerificationError::InvalidServerSignature)?;
386
387 Ok(())
388 }
389}
390
391#[derive(Debug, thiserror::Error)]
393#[allow(missing_docs)]
394pub enum HandshakeVerificationError {
395 #[error("invalid end entity certificate")]
396 InvalidEndEntityCertificate,
397 #[error("missing server certificates")]
398 MissingCerts,
399 #[error("invalid server signature")]
400 InvalidServerSignature,
401 #[error("invalid server ephemeral key")]
402 InvalidServerEphemeralKey,
403 #[error("server certificate verification failed: {0}")]
404 ServerCert(ServerCertVerifierError),
405 #[error("unsupported signature scheme: {0}")]
406 UnsupportedSignatureScheme(SignatureScheme),
407}
408
409#[cfg(test)]
410mod tests {
411 use super::*;
412 use crate::{fixtures::ConnectionFixture, transcript::Transcript, webpki::RootCertStore};
413
414 use hex::FromHex;
415 use rstest::*;
416 use tlsn_data_fixtures::http::{request::GET_WITH_HEADER, response::OK_JSON};
417
418 #[fixture]
419 #[once]
420 fn verifier() -> ServerCertVerifier {
421 let mut root_store = RootCertStore {
422 roots: webpki_root_certs::TLS_SERVER_ROOT_CERTS
423 .iter()
424 .map(|c| CertificateDer(c.to_vec()))
425 .collect(),
426 };
427
428 root_store.roots.push(
430 appliedzkp()
431 .server_cert_data
432 .certs
433 .last()
434 .expect("chain is valid")
435 .clone(),
436 );
437
438 ServerCertVerifier::new(&root_store).unwrap()
439 }
440
441 fn tlsnotary() -> ConnectionFixture {
442 ConnectionFixture::tlsnotary(Transcript::new(GET_WITH_HEADER, OK_JSON).length())
443 }
444
445 fn appliedzkp() -> ConnectionFixture {
446 ConnectionFixture::appliedzkp(Transcript::new(GET_WITH_HEADER, OK_JSON).length())
447 }
448
449 #[rstest]
451 #[case::tlsnotary(tlsnotary())]
452 #[case::appliedzkp(appliedzkp())]
453 fn test_verify_cert_chain_sucess_ca_implicit(
454 verifier: &ServerCertVerifier,
455 #[case] mut data: ConnectionFixture,
456 ) {
457 data.server_cert_data.certs.pop();
459
460 assert!(data
461 .server_cert_data
462 .verify(
463 verifier,
464 data.connection_info.time,
465 data.server_ephemeral_key(),
466 &data.server_name,
467 )
468 .is_ok());
469 }
470
471 #[rstest]
474 #[case::tlsnotary(tlsnotary())]
475 #[case::appliedzkp(appliedzkp())]
476 fn test_verify_cert_chain_success_ca_explicit(
477 verifier: &ServerCertVerifier,
478 #[case] data: ConnectionFixture,
479 ) {
480 assert!(data
481 .server_cert_data
482 .verify(
483 verifier,
484 data.connection_info.time,
485 data.server_ephemeral_key(),
486 &data.server_name,
487 )
488 .is_ok());
489 }
490
491 #[rstest]
493 #[case::tlsnotary(tlsnotary())]
494 #[case::appliedzkp(appliedzkp())]
495 fn test_verify_cert_chain_fail_bad_time(
496 verifier: &ServerCertVerifier,
497 #[case] data: ConnectionFixture,
498 ) {
499 let bad_time: u64 = 1571465711;
501
502 let err = data.server_cert_data.verify(
503 verifier,
504 bad_time,
505 data.server_ephemeral_key(),
506 &data.server_name,
507 );
508
509 assert!(matches!(
510 err.unwrap_err(),
511 HandshakeVerificationError::ServerCert(_)
512 ));
513 }
514
515 #[rstest]
517 #[case::tlsnotary(tlsnotary())]
518 #[case::appliedzkp(appliedzkp())]
519 fn test_verify_cert_chain_fail_no_interm_cert(
520 verifier: &ServerCertVerifier,
521 #[case] mut data: ConnectionFixture,
522 ) {
523 data.server_cert_data.certs.pop();
525 data.server_cert_data.certs.pop();
527
528 let err = data.server_cert_data.verify(
529 verifier,
530 data.connection_info.time,
531 data.server_ephemeral_key(),
532 &data.server_name,
533 );
534
535 assert!(matches!(
536 err.unwrap_err(),
537 HandshakeVerificationError::ServerCert(_)
538 ));
539 }
540
541 #[rstest]
544 #[case::tlsnotary(tlsnotary())]
545 #[case::appliedzkp(appliedzkp())]
546 fn test_verify_cert_chain_fail_no_interm_cert_with_ca_cert(
547 verifier: &ServerCertVerifier,
548 #[case] mut data: ConnectionFixture,
549 ) {
550 data.server_cert_data.certs.remove(1);
552
553 let err = data.server_cert_data.verify(
554 verifier,
555 data.connection_info.time,
556 data.server_ephemeral_key(),
557 &data.server_name,
558 );
559
560 assert!(matches!(
561 err.unwrap_err(),
562 HandshakeVerificationError::ServerCert(_)
563 ));
564 }
565
566 #[rstest]
568 #[case::tlsnotary(tlsnotary())]
569 #[case::appliedzkp(appliedzkp())]
570 fn test_verify_cert_chain_fail_bad_ee_cert(
571 verifier: &ServerCertVerifier,
572 #[case] mut data: ConnectionFixture,
573 ) {
574 let ee: &[u8] = include_bytes!("./fixtures/data/unknown/ee.der");
575
576 data.server_cert_data.certs[0] = CertificateDer(ee.to_vec());
578
579 let err = data.server_cert_data.verify(
580 verifier,
581 data.connection_info.time,
582 data.server_ephemeral_key(),
583 &data.server_name,
584 );
585
586 assert!(matches!(
587 err.unwrap_err(),
588 HandshakeVerificationError::ServerCert(_)
589 ));
590 }
591
592 #[rstest]
594 #[case::tlsnotary(tlsnotary())]
595 #[case::appliedzkp(appliedzkp())]
596 fn test_verify_sig_ke_params_fail_bad_client_random(
597 verifier: &ServerCertVerifier,
598 #[case] mut data: ConnectionFixture,
599 ) {
600 let CertBinding::V1_2(CertBindingV1_2 { client_random, .. }) =
601 &mut data.server_cert_data.binding;
602 client_random[31] = client_random[31].wrapping_add(1);
603
604 let err = data.server_cert_data.verify(
605 verifier,
606 data.connection_info.time,
607 data.server_ephemeral_key(),
608 &data.server_name,
609 );
610
611 assert!(matches!(
612 err.unwrap_err(),
613 HandshakeVerificationError::InvalidServerSignature
614 ));
615 }
616
617 #[rstest]
619 #[case::tlsnotary(tlsnotary())]
620 #[case::appliedzkp(appliedzkp())]
621 fn test_verify_sig_ke_params_fail_bad_sig(
622 verifier: &ServerCertVerifier,
623 #[case] mut data: ConnectionFixture,
624 ) {
625 data.server_cert_data.sig.sig[31] = data.server_cert_data.sig.sig[31].wrapping_add(1);
626
627 let err = data.server_cert_data.verify(
628 verifier,
629 data.connection_info.time,
630 data.server_ephemeral_key(),
631 &data.server_name,
632 );
633
634 assert!(matches!(
635 err.unwrap_err(),
636 HandshakeVerificationError::InvalidServerSignature
637 ));
638 }
639
640 #[rstest]
642 #[case::tlsnotary(tlsnotary())]
643 #[case::appliedzkp(appliedzkp())]
644 fn test_check_dns_name_present_in_cert_fail_bad_host(
645 verifier: &ServerCertVerifier,
646 #[case] data: ConnectionFixture,
647 ) {
648 let bad_name = ServerName::Dns(DnsName::try_from("badhost.com").unwrap());
649
650 let err = data.server_cert_data.verify(
651 verifier,
652 data.connection_info.time,
653 data.server_ephemeral_key(),
654 &bad_name,
655 );
656
657 assert!(matches!(
658 err.unwrap_err(),
659 HandshakeVerificationError::ServerCert(_)
660 ));
661 }
662
663 #[rstest]
665 #[case::tlsnotary(tlsnotary())]
666 #[case::appliedzkp(appliedzkp())]
667 fn test_invalid_ephemeral_key(verifier: &ServerCertVerifier, #[case] data: ConnectionFixture) {
668 let wrong_ephemeral_key = ServerEphemKey {
669 typ: KeyType::SECP256R1,
670 key: Vec::<u8>::from_hex(include_bytes!("./fixtures/data/unknown/pubkey")).unwrap(),
671 };
672
673 let err = data.server_cert_data.verify(
674 verifier,
675 data.connection_info.time,
676 &wrong_ephemeral_key,
677 &data.server_name,
678 );
679
680 assert!(matches!(
681 err.unwrap_err(),
682 HandshakeVerificationError::InvalidServerEphemeralKey
683 ));
684 }
685
686 #[rstest]
688 #[case::tlsnotary(tlsnotary())]
689 #[case::appliedzkp(appliedzkp())]
690 fn test_verify_cert_chain_fail_no_cert(
691 verifier: &ServerCertVerifier,
692 #[case] mut data: ConnectionFixture,
693 ) {
694 data.server_cert_data.certs = Vec::new();
696
697 let err = data.server_cert_data.verify(
698 verifier,
699 data.connection_info.time,
700 data.server_ephemeral_key(),
701 &data.server_name,
702 );
703
704 assert!(matches!(
705 err.unwrap_err(),
706 HandshakeVerificationError::MissingCerts
707 ));
708 }
709}