1mod commit;
23mod proof;
24
25use std::fmt;
26
27use serde::{Deserialize, Serialize};
28use tls_core::{
29 msgs::{
30 codec::Codec,
31 enums::NamedGroup,
32 handshake::{DigitallySignedStruct, ServerECDHParams},
33 },
34 verify::ServerCertVerifier as _,
35};
36use web_time::{Duration, UNIX_EPOCH};
37
38use crate::{hash::impl_domain_separator, CryptoProvider};
39
40pub use commit::{ServerCertCommitment, ServerCertOpening};
41pub use proof::{ServerIdentityProof, ServerIdentityProofError};
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
45#[serde(rename_all = "snake_case")]
46pub enum TlsVersion {
47 V1_2,
49 V1_3,
51}
52
53impl TryFrom<tls_core::msgs::enums::ProtocolVersion> for TlsVersion {
54 type Error = &'static str;
55
56 fn try_from(value: tls_core::msgs::enums::ProtocolVersion) -> Result<Self, Self::Error> {
57 Ok(match value {
58 tls_core::msgs::enums::ProtocolVersion::TLSv1_2 => TlsVersion::V1_2,
59 tls_core::msgs::enums::ProtocolVersion::TLSv1_3 => TlsVersion::V1_3,
60 _ => return Err("unsupported TLS version"),
61 })
62 }
63}
64
65#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
67pub struct ServerName(String);
68
69impl ServerName {
70 pub fn new(name: String) -> Self {
72 Self(name)
73 }
74
75 pub fn as_str(&self) -> &str {
77 &self.0
78 }
79}
80
81impl From<&str> for ServerName {
82 fn from(name: &str) -> Self {
83 Self(name.to_string())
84 }
85}
86
87impl AsRef<str> for ServerName {
88 fn as_ref(&self) -> &str {
89 &self.0
90 }
91}
92
93impl fmt::Display for ServerName {
94 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95 write!(f, "{}", self.0)
96 }
97}
98
99#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
101#[serde(rename_all = "lowercase")]
102#[non_exhaustive]
103#[allow(non_camel_case_types)]
104pub enum KeyType {
105 SECP256R1 = 0x0017,
107}
108
109#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
111#[serde(rename_all = "lowercase")]
112#[allow(non_camel_case_types, missing_docs)]
113pub enum SignatureScheme {
114 RSA_PKCS1_SHA1 = 0x0201,
115 ECDSA_SHA1_Legacy = 0x0203,
116 RSA_PKCS1_SHA256 = 0x0401,
117 ECDSA_NISTP256_SHA256 = 0x0403,
118 RSA_PKCS1_SHA384 = 0x0501,
119 ECDSA_NISTP384_SHA384 = 0x0503,
120 RSA_PKCS1_SHA512 = 0x0601,
121 ECDSA_NISTP521_SHA512 = 0x0603,
122 RSA_PSS_SHA256 = 0x0804,
123 RSA_PSS_SHA384 = 0x0805,
124 RSA_PSS_SHA512 = 0x0806,
125 ED25519 = 0x0807,
126}
127
128impl TryFrom<tls_core::msgs::enums::SignatureScheme> for SignatureScheme {
129 type Error = &'static str;
130
131 fn try_from(value: tls_core::msgs::enums::SignatureScheme) -> Result<Self, Self::Error> {
132 use tls_core::msgs::enums::SignatureScheme as Core;
133 use SignatureScheme::*;
134 Ok(match value {
135 Core::RSA_PKCS1_SHA1 => RSA_PKCS1_SHA1,
136 Core::ECDSA_SHA1_Legacy => ECDSA_SHA1_Legacy,
137 Core::RSA_PKCS1_SHA256 => RSA_PKCS1_SHA256,
138 Core::ECDSA_NISTP256_SHA256 => ECDSA_NISTP256_SHA256,
139 Core::RSA_PKCS1_SHA384 => RSA_PKCS1_SHA384,
140 Core::ECDSA_NISTP384_SHA384 => ECDSA_NISTP384_SHA384,
141 Core::RSA_PKCS1_SHA512 => RSA_PKCS1_SHA512,
142 Core::ECDSA_NISTP521_SHA512 => ECDSA_NISTP521_SHA512,
143 Core::RSA_PSS_SHA256 => RSA_PSS_SHA256,
144 Core::RSA_PSS_SHA384 => RSA_PSS_SHA384,
145 Core::RSA_PSS_SHA512 => RSA_PSS_SHA512,
146 Core::ED25519 => ED25519,
147 _ => return Err("unsupported signature scheme"),
148 })
149 }
150}
151
152impl From<SignatureScheme> for tls_core::msgs::enums::SignatureScheme {
153 fn from(value: SignatureScheme) -> Self {
154 use tls_core::msgs::enums::SignatureScheme::*;
155 match value {
156 SignatureScheme::RSA_PKCS1_SHA1 => RSA_PKCS1_SHA1,
157 SignatureScheme::ECDSA_SHA1_Legacy => ECDSA_SHA1_Legacy,
158 SignatureScheme::RSA_PKCS1_SHA256 => RSA_PKCS1_SHA256,
159 SignatureScheme::ECDSA_NISTP256_SHA256 => ECDSA_NISTP256_SHA256,
160 SignatureScheme::RSA_PKCS1_SHA384 => RSA_PKCS1_SHA384,
161 SignatureScheme::ECDSA_NISTP384_SHA384 => ECDSA_NISTP384_SHA384,
162 SignatureScheme::RSA_PKCS1_SHA512 => RSA_PKCS1_SHA512,
163 SignatureScheme::ECDSA_NISTP521_SHA512 => ECDSA_NISTP521_SHA512,
164 SignatureScheme::RSA_PSS_SHA256 => RSA_PSS_SHA256,
165 SignatureScheme::RSA_PSS_SHA384 => RSA_PSS_SHA384,
166 SignatureScheme::RSA_PSS_SHA512 => RSA_PSS_SHA512,
167 SignatureScheme::ED25519 => ED25519,
168 }
169 }
170}
171
172#[derive(Debug, Clone, Serialize, Deserialize)]
174pub struct Certificate(pub Vec<u8>);
175
176impl From<tls_core::key::Certificate> for Certificate {
177 fn from(cert: tls_core::key::Certificate) -> Self {
178 Self(cert.0)
179 }
180}
181
182#[derive(Debug, Clone, Serialize, Deserialize)]
184pub struct ServerSignature {
185 pub scheme: SignatureScheme,
187 pub sig: Vec<u8>,
189}
190
191#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
193pub struct ServerEphemKey {
194 #[serde(rename = "type")]
196 pub typ: KeyType,
197 pub key: Vec<u8>,
199}
200
201impl_domain_separator!(ServerEphemKey);
202
203impl ServerEphemKey {
204 pub(crate) fn kx_params(&self) -> Vec<u8> {
206 let group = match self.typ {
207 KeyType::SECP256R1 => NamedGroup::secp256r1,
208 };
209
210 let mut kx_params = Vec::new();
211 ServerECDHParams::new(group, &self.key).encode(&mut kx_params);
212
213 kx_params
214 }
215}
216
217impl TryFrom<tls_core::key::PublicKey> for ServerEphemKey {
218 type Error = &'static str;
219
220 fn try_from(value: tls_core::key::PublicKey) -> Result<Self, Self::Error> {
221 let tls_core::msgs::enums::NamedGroup::secp256r1 = value.group else {
222 return Err("unsupported key type");
223 };
224
225 Ok(ServerEphemKey {
226 typ: KeyType::SECP256R1,
227 key: value.key,
228 })
229 }
230}
231
232#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
234pub struct ConnectionInfo {
235 pub time: u64,
237 pub version: TlsVersion,
239 pub transcript_length: TranscriptLength,
241}
242
243impl_domain_separator!(ConnectionInfo);
244
245#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
247pub struct TranscriptLength {
248 pub sent: u32,
250 pub received: u32,
252}
253
254#[derive(Debug, Clone, Serialize, Deserialize)]
256pub struct HandshakeDataV1_2 {
257 pub client_random: [u8; 32],
259 pub server_random: [u8; 32],
261 pub server_ephemeral_key: ServerEphemKey,
263}
264
265#[derive(Debug, Clone, Serialize, Deserialize)]
267#[serde(rename_all = "snake_case")]
268#[non_exhaustive]
269pub enum HandshakeData {
270 V1_2(HandshakeDataV1_2),
272}
273
274impl_domain_separator!(HandshakeData);
275
276#[derive(Debug, Clone, Serialize, Deserialize)]
278pub struct ServerCertData {
279 pub certs: Vec<Certificate>,
281 pub sig: ServerSignature,
283 pub handshake: HandshakeData,
285}
286
287impl ServerCertData {
288 pub fn verify_with_provider(
297 &self,
298 provider: &CryptoProvider,
299 time: u64,
300 server_ephemeral_key: &ServerEphemKey,
301 server_name: &ServerName,
302 ) -> Result<(), CertificateVerificationError> {
303 #[allow(irrefutable_let_patterns)]
304 let HandshakeData::V1_2(HandshakeDataV1_2 {
305 client_random,
306 server_random,
307 server_ephemeral_key: expected_server_ephemeral_key,
308 }) = &self.handshake
309 else {
310 unreachable!("only TLS 1.2 is implemented")
311 };
312
313 if server_ephemeral_key != expected_server_ephemeral_key {
314 return Err(CertificateVerificationError::InvalidServerEphemeralKey);
315 }
316
317 let server_name = tls_core::dns::ServerName::try_from(server_name.as_ref())
319 .map_err(|_| CertificateVerificationError::InvalidIdentity(server_name.clone()))?;
320
321 let cert_chain = self
323 .certs
324 .clone()
325 .into_iter()
326 .map(|cert| tls_core::key::Certificate(cert.0))
327 .collect::<Vec<_>>();
328
329 let (end_entity, intermediates) = cert_chain
330 .split_first()
331 .ok_or(CertificateVerificationError::MissingCerts)?;
332
333 provider
336 .cert
337 .verify_server_cert(
338 end_entity,
339 intermediates,
340 &server_name,
341 &mut [].into_iter(),
342 &[],
343 UNIX_EPOCH + Duration::from_secs(time),
344 )
345 .map_err(|_| CertificateVerificationError::InvalidCert)?;
346
347 let mut message = Vec::new();
349 message.extend_from_slice(client_random);
350 message.extend_from_slice(server_random);
351 message.extend_from_slice(&server_ephemeral_key.kx_params());
352
353 let dss = DigitallySignedStruct::new(self.sig.scheme.into(), self.sig.sig.clone());
354
355 provider
356 .cert
357 .verify_tls12_signature(&message, end_entity, &dss)
358 .map_err(|_| CertificateVerificationError::InvalidServerSignature)?;
359
360 Ok(())
361 }
362}
363
364#[derive(Debug, thiserror::Error)]
366#[allow(missing_docs)]
367pub enum CertificateVerificationError {
368 #[error("invalid server identity: {0}")]
369 InvalidIdentity(ServerName),
370 #[error("missing server certificates")]
371 MissingCerts,
372 #[error("invalid server certificate")]
373 InvalidCert,
374 #[error("invalid server signature")]
375 InvalidServerSignature,
376 #[error("invalid server ephemeral key")]
377 InvalidServerEphemeralKey,
378}
379
380#[cfg(test)]
381mod tests {
382 use super::*;
383 use crate::{
384 fixtures::ConnectionFixture, provider::default_cert_verifier, transcript::Transcript,
385 };
386
387 use hex::FromHex;
388 use rstest::*;
389 use tls_core::verify::WebPkiVerifier;
390 use tlsn_data_fixtures::http::{request::GET_WITH_HEADER, response::OK_JSON};
391
392 #[fixture]
393 #[once]
394 fn crypto_provider() -> CryptoProvider {
395 let mut store = default_cert_verifier().root_store().clone();
396
397 let cert = tls_core::key::Certificate(
399 appliedzkp()
400 .server_cert_data
401 .certs
402 .last()
403 .expect("chain is valid")
404 .0
405 .clone(),
406 );
407
408 store.add(&cert).unwrap();
409
410 CryptoProvider {
411 hash: Default::default(),
412 cert: WebPkiVerifier::new(store, None),
413 signer: Default::default(),
414 signature: Default::default(),
415 }
416 }
417
418 fn tlsnotary() -> ConnectionFixture {
419 ConnectionFixture::tlsnotary(Transcript::new(GET_WITH_HEADER, OK_JSON).length())
420 }
421
422 fn appliedzkp() -> ConnectionFixture {
423 ConnectionFixture::appliedzkp(Transcript::new(GET_WITH_HEADER, OK_JSON).length())
424 }
425
426 #[rstest]
428 #[case::tlsnotary(tlsnotary())]
429 #[case::appliedzkp(appliedzkp())]
430 fn test_verify_cert_chain_sucess_ca_implicit(
431 crypto_provider: &CryptoProvider,
432 #[case] mut data: ConnectionFixture,
433 ) {
434 data.server_cert_data.certs.pop();
436
437 assert!(data
438 .server_cert_data
439 .verify_with_provider(
440 crypto_provider,
441 data.connection_info.time,
442 data.server_ephemeral_key(),
443 &ServerName::from(data.server_name.as_ref()),
444 )
445 .is_ok());
446 }
447
448 #[rstest]
451 #[case::tlsnotary(tlsnotary())]
452 #[case::appliedzkp(appliedzkp())]
453 fn test_verify_cert_chain_success_ca_explicit(
454 crypto_provider: &CryptoProvider,
455 #[case] data: ConnectionFixture,
456 ) {
457 assert!(data
458 .server_cert_data
459 .verify_with_provider(
460 crypto_provider,
461 data.connection_info.time,
462 data.server_ephemeral_key(),
463 &ServerName::from(data.server_name.as_ref()),
464 )
465 .is_ok());
466 }
467
468 #[rstest]
470 #[case::tlsnotary(tlsnotary())]
471 #[case::appliedzkp(appliedzkp())]
472 fn test_verify_cert_chain_fail_bad_time(
473 crypto_provider: &CryptoProvider,
474 #[case] data: ConnectionFixture,
475 ) {
476 let bad_time: u64 = 1571465711;
478
479 let err = data.server_cert_data.verify_with_provider(
480 crypto_provider,
481 bad_time,
482 data.server_ephemeral_key(),
483 &ServerName::from(data.server_name.as_ref()),
484 );
485
486 assert!(matches!(
487 err.unwrap_err(),
488 CertificateVerificationError::InvalidCert
489 ));
490 }
491
492 #[rstest]
494 #[case::tlsnotary(tlsnotary())]
495 #[case::appliedzkp(appliedzkp())]
496 fn test_verify_cert_chain_fail_no_interm_cert(
497 crypto_provider: &CryptoProvider,
498 #[case] mut data: ConnectionFixture,
499 ) {
500 data.server_cert_data.certs.pop();
502 data.server_cert_data.certs.pop();
504
505 let err = data.server_cert_data.verify_with_provider(
506 crypto_provider,
507 data.connection_info.time,
508 data.server_ephemeral_key(),
509 &ServerName::from(data.server_name.as_ref()),
510 );
511
512 assert!(matches!(
513 err.unwrap_err(),
514 CertificateVerificationError::InvalidCert
515 ));
516 }
517
518 #[rstest]
521 #[case::tlsnotary(tlsnotary())]
522 #[case::appliedzkp(appliedzkp())]
523 fn test_verify_cert_chain_fail_no_interm_cert_with_ca_cert(
524 crypto_provider: &CryptoProvider,
525 #[case] mut data: ConnectionFixture,
526 ) {
527 data.server_cert_data.certs.remove(1);
529
530 let err = data.server_cert_data.verify_with_provider(
531 crypto_provider,
532 data.connection_info.time,
533 data.server_ephemeral_key(),
534 &ServerName::from(data.server_name.as_ref()),
535 );
536
537 assert!(matches!(
538 err.unwrap_err(),
539 CertificateVerificationError::InvalidCert
540 ));
541 }
542
543 #[rstest]
545 #[case::tlsnotary(tlsnotary())]
546 #[case::appliedzkp(appliedzkp())]
547 fn test_verify_cert_chain_fail_bad_ee_cert(
548 crypto_provider: &CryptoProvider,
549 #[case] mut data: ConnectionFixture,
550 ) {
551 let ee: &[u8] = include_bytes!("./fixtures/data/unknown/ee.der");
552
553 data.server_cert_data.certs[0] = Certificate(ee.to_vec());
555
556 let err = data.server_cert_data.verify_with_provider(
557 crypto_provider,
558 data.connection_info.time,
559 data.server_ephemeral_key(),
560 &ServerName::from(data.server_name.as_ref()),
561 );
562
563 assert!(matches!(
564 err.unwrap_err(),
565 CertificateVerificationError::InvalidCert
566 ));
567 }
568
569 #[rstest]
571 #[case::tlsnotary(tlsnotary())]
572 #[case::appliedzkp(appliedzkp())]
573 fn test_verify_sig_ke_params_fail_bad_client_random(
574 crypto_provider: &CryptoProvider,
575 #[case] mut data: ConnectionFixture,
576 ) {
577 let HandshakeData::V1_2(HandshakeDataV1_2 { client_random, .. }) =
578 &mut data.server_cert_data.handshake;
579 client_random[31] = client_random[31].wrapping_add(1);
580
581 let err = data.server_cert_data.verify_with_provider(
582 crypto_provider,
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_verify_sig_ke_params_fail_bad_sig(
599 crypto_provider: &CryptoProvider,
600 #[case] mut data: ConnectionFixture,
601 ) {
602 data.server_cert_data.sig.sig[31] = data.server_cert_data.sig.sig[31].wrapping_add(1);
603
604 let err = data.server_cert_data.verify_with_provider(
605 crypto_provider,
606 data.connection_info.time,
607 data.server_ephemeral_key(),
608 &ServerName::from(data.server_name.as_ref()),
609 );
610
611 assert!(matches!(
612 err.unwrap_err(),
613 CertificateVerificationError::InvalidServerSignature
614 ));
615 }
616
617 #[rstest]
619 #[case::tlsnotary(tlsnotary())]
620 #[case::appliedzkp(appliedzkp())]
621 fn test_check_dns_name_present_in_cert_fail_bad_host(
622 crypto_provider: &CryptoProvider,
623 #[case] data: ConnectionFixture,
624 ) {
625 let bad_name = ServerName::from("badhost.com");
626
627 let err = data.server_cert_data.verify_with_provider(
628 crypto_provider,
629 data.connection_info.time,
630 data.server_ephemeral_key(),
631 &bad_name,
632 );
633
634 assert!(matches!(
635 err.unwrap_err(),
636 CertificateVerificationError::InvalidCert
637 ));
638 }
639
640 #[rstest]
642 #[case::tlsnotary(tlsnotary())]
643 #[case::appliedzkp(appliedzkp())]
644 fn test_invalid_ephemeral_key(
645 crypto_provider: &CryptoProvider,
646 #[case] data: ConnectionFixture,
647 ) {
648 let wrong_ephemeral_key = ServerEphemKey {
649 typ: KeyType::SECP256R1,
650 key: Vec::<u8>::from_hex(include_bytes!("./fixtures/data/unknown/pubkey")).unwrap(),
651 };
652
653 let err = data.server_cert_data.verify_with_provider(
654 crypto_provider,
655 data.connection_info.time,
656 &wrong_ephemeral_key,
657 &ServerName::from(data.server_name.as_ref()),
658 );
659
660 assert!(matches!(
661 err.unwrap_err(),
662 CertificateVerificationError::InvalidServerEphemeralKey
663 ));
664 }
665
666 #[rstest]
668 #[case::tlsnotary(tlsnotary())]
669 #[case::appliedzkp(appliedzkp())]
670 fn test_verify_cert_chain_fail_no_cert(
671 crypto_provider: &CryptoProvider,
672 #[case] mut data: ConnectionFixture,
673 ) {
674 data.server_cert_data.certs = Vec::new();
676
677 let err = data.server_cert_data.verify_with_provider(
678 crypto_provider,
679 data.connection_info.time,
680 data.server_ephemeral_key(),
681 &ServerName::from(data.server_name.as_ref()),
682 );
683
684 assert!(matches!(
685 err.unwrap_err(),
686 CertificateVerificationError::MissingCerts
687 ));
688 }
689}