1use crate::TlsError;
2use crate::config::{Certificate, TlsCipherSuite, TlsClock, TlsVerifier};
3#[cfg(feature = "p384")]
4use crate::der_certificate::ECDSA_SHA384;
5#[cfg(feature = "ed25519")]
6use crate::der_certificate::ED25519;
7use crate::der_certificate::{
8 DecodedCertificate, ECDSA_SHA256, HOSTNAME_MAXLEN, MAX_SAN_DNS_NAMES, Time,
9 extract_common_name, extract_san_dns_names,
10};
11#[cfg(feature = "rsa")]
12use crate::der_certificate::{RSA_PKCS1_SHA256, RSA_PKCS1_SHA384, RSA_PKCS1_SHA512};
13use crate::extensions::extension_data::signature_algorithms::SignatureScheme;
14use crate::handshake::{
15 certificate::{
16 Certificate as OwnedCertificate, CertificateEntryRef, CertificateRef as ServerCertificate,
17 },
18 certificate_verify::CertificateVerifyRef,
19};
20use crate::parse_buffer::ParseError;
21use core::marker::PhantomData;
22#[cfg(feature = "defmt")]
23use defmt::Debug2Format;
24use der::Decode;
25use digest::Digest;
26use heapless::Vec;
27
28pub struct CertificateNames {
29 pub common_name: Option<heapless::String<HOSTNAME_MAXLEN>>,
30 pub san_dns_names: heapless::Vec<heapless::String<HOSTNAME_MAXLEN>, MAX_SAN_DNS_NAMES>,
31}
32
33pub struct CertificateChain<'a> {
34 prev: &'a CertificateEntryRef<'a>,
35 chain: &'a ServerCertificate<'a>,
36 idx: isize,
37}
38
39impl<'a> CertificateChain<'a> {
40 pub fn new(ca: &'a CertificateEntryRef, chain: &'a ServerCertificate<'a>) -> Self {
41 Self {
42 prev: ca,
43 chain,
44 idx: chain.entries.len() as isize - 1,
45 }
46 }
47}
48
49impl<'a> Iterator for CertificateChain<'a> {
50 type Item = (&'a CertificateEntryRef<'a>, &'a CertificateEntryRef<'a>);
51
52 fn next(&mut self) -> Option<Self::Item> {
53 if self.idx < 0 {
54 return None;
55 }
56
57 let cur = &self.chain.entries[self.idx as usize];
58 let out = (self.prev, cur);
59
60 self.prev = cur;
61 self.idx -= 1;
62
63 Some(out)
64 }
65}
66
67pub struct CertVerifier<'a, CipherSuite, Clock, const CERT_SIZE: usize>
68where
69 Clock: TlsClock,
70 CipherSuite: TlsCipherSuite,
71{
72 ca: Certificate<&'a [u8]>,
73 host: Option<heapless::String<64>>,
74 certificate_transcript: Option<CipherSuite::Hash>,
75 certificate: Option<OwnedCertificate<CERT_SIZE>>,
76 _clock: PhantomData<Clock>,
77}
78
79impl<'a, CipherSuite, Clock, const CERT_SIZE: usize> CertVerifier<'a, CipherSuite, Clock, CERT_SIZE>
80where
81 Clock: TlsClock,
82 CipherSuite: TlsCipherSuite,
83{
84 #[must_use]
85 pub fn new(ca: Certificate<&'a [u8]>) -> Self {
86 Self {
87 ca,
88 host: None,
89 certificate_transcript: None,
90 certificate: None,
91 _clock: PhantomData,
92 }
93 }
94}
95
96impl<CipherSuite, Clock, const CERT_SIZE: usize> TlsVerifier<CipherSuite>
97 for CertVerifier<'_, CipherSuite, Clock, CERT_SIZE>
98where
99 CipherSuite: TlsCipherSuite,
100 Clock: TlsClock,
101{
102 fn set_hostname_verification(&mut self, hostname: &str) -> Result<(), TlsError> {
103 self.host.replace(
104 heapless::String::try_from(hostname).map_err(|_| TlsError::InsufficientSpace)?,
105 );
106 Ok(())
107 }
108
109 fn verify_certificate(
110 &mut self,
111 transcript: &CipherSuite::Hash,
112 cert: ServerCertificate,
113 ) -> Result<(), TlsError> {
114 let mut names = CertificateNames {
115 common_name: None,
116 san_dns_names: heapless::Vec::new(),
117 };
118
119 for (p, q) in CertificateChain::new(&(&self.ca).into(), &cert) {
120 names = verify_certificate(p, q, Clock::now())?;
121 }
122
123 if !tls_hostname_match(&names, &self.host) {
124 error!(
125 "Hostname ({:?}) does not match certificate names (CN={:?}, SANs={:?})",
126 self.host, names.common_name, names.san_dns_names
127 );
128 return Err(TlsError::InvalidCertificate);
129 }
130
131 self.certificate.replace(cert.try_into()?);
132 self.certificate_transcript.replace(transcript.clone());
133 Ok(())
134 }
135
136 fn verify_signature(&mut self, verify: CertificateVerifyRef) -> Result<(), TlsError> {
137 let handshake_hash = unwrap!(self.certificate_transcript.take());
138 let ctx_str = b"TLS 1.3, server CertificateVerify\x00";
139 let mut msg: Vec<u8, 146> = Vec::new();
140 msg.resize(64, 0x20).map_err(|_| TlsError::EncodeError)?;
141 msg.extend_from_slice(ctx_str)
142 .map_err(|_| TlsError::EncodeError)?;
143 msg.extend_from_slice(&handshake_hash.finalize())
144 .map_err(|_| TlsError::EncodeError)?;
145
146 let certificate = unwrap!(self.certificate.as_ref()).try_into()?;
147 verify_signature(&msg[..], &certificate, &verify)?;
148 Ok(())
149 }
150}
151
152fn verify_signature(
153 message: &[u8],
154 certificate: &ServerCertificate,
155 verify: &CertificateVerifyRef,
156) -> Result<(), TlsError> {
157 let verified;
158
159 let certificate =
160 if let Some(CertificateEntryRef::X509(certificate)) = certificate.entries.first() {
161 certificate
162 } else {
163 return Err(TlsError::DecodeError);
164 };
165
166 let certificate =
167 DecodedCertificate::from_der(certificate).map_err(|_| TlsError::DecodeError)?;
168
169 let public_key = certificate
170 .tbs_certificate
171 .subject_public_key_info
172 .public_key
173 .as_bytes()
174 .ok_or(TlsError::DecodeError)?;
175
176 match verify.signature_scheme {
177 SignatureScheme::EcdsaSecp256r1Sha256 => {
178 use p256::ecdsa::{Signature, VerifyingKey, signature::Verifier};
179 let verifying_key =
180 VerifyingKey::from_sec1_bytes(public_key).map_err(|_| TlsError::DecodeError)?;
181 let signature =
182 Signature::from_der(&verify.signature).map_err(|_| TlsError::DecodeError)?;
183 verified = verifying_key.verify(message, &signature).is_ok();
184 }
185 #[cfg(feature = "p384")]
186 SignatureScheme::EcdsaSecp384r1Sha384 => {
187 use p384::ecdsa::{Signature, VerifyingKey, signature::Verifier};
188 let verifying_key =
189 VerifyingKey::from_sec1_bytes(public_key).map_err(|_| TlsError::DecodeError)?;
190 let signature =
191 Signature::from_der(&verify.signature).map_err(|_| TlsError::DecodeError)?;
192 verified = verifying_key.verify(message, &signature).is_ok();
193 }
194 #[cfg(feature = "ed25519")]
195 SignatureScheme::Ed25519 => {
196 use ed25519_dalek::{Signature, Verifier, VerifyingKey};
197 let verifying_key: VerifyingKey =
198 VerifyingKey::from_bytes(public_key.try_into().unwrap())
199 .map_err(|_| TlsError::DecodeError)?;
200 let signature =
201 Signature::try_from(verify.signature).map_err(|_| TlsError::DecodeError)?;
202 verified = verifying_key.verify(message, &signature).is_ok();
203 }
204 #[cfg(feature = "rsa")]
205 SignatureScheme::RsaPssRsaeSha256 => {
206 use rsa::{
207 RsaPublicKey,
208 pkcs1::DecodeRsaPublicKey,
209 pss::{Signature, VerifyingKey},
210 signature::Verifier,
211 };
212 use sha2::Sha256;
213
214 let der_pubkey = RsaPublicKey::from_pkcs1_der(public_key).unwrap();
215 let verifying_key = VerifyingKey::<Sha256>::from(der_pubkey);
216
217 let signature =
218 Signature::try_from(verify.signature).map_err(|_| TlsError::DecodeError)?;
219 verified = verifying_key.verify(message, &signature).is_ok();
220 }
221 #[cfg(feature = "rsa")]
222 SignatureScheme::RsaPssRsaeSha384 => {
223 use rsa::{
224 RsaPublicKey,
225 pkcs1::DecodeRsaPublicKey,
226 pss::{Signature, VerifyingKey},
227 signature::Verifier,
228 };
229 use sha2::Sha384;
230
231 let der_pubkey =
232 RsaPublicKey::from_pkcs1_der(public_key).map_err(|_| TlsError::DecodeError)?;
233 let verifying_key = VerifyingKey::<Sha384>::from(der_pubkey);
234
235 let signature =
236 Signature::try_from(verify.signature).map_err(|_| TlsError::DecodeError)?;
237 verified = verifying_key.verify(message, &signature).is_ok();
238 }
239 #[cfg(feature = "rsa")]
240 SignatureScheme::RsaPssRsaeSha512 => {
241 use rsa::{
242 RsaPublicKey,
243 pkcs1::DecodeRsaPublicKey,
244 pss::{Signature, VerifyingKey},
245 signature::Verifier,
246 };
247 use sha2::Sha512;
248
249 let der_pubkey =
250 RsaPublicKey::from_pkcs1_der(public_key).map_err(|_| TlsError::DecodeError)?;
251 let verifying_key = VerifyingKey::<Sha512>::from(der_pubkey);
252
253 let signature =
254 Signature::try_from(verify.signature).map_err(|_| TlsError::DecodeError)?;
255 verified = verifying_key.verify(message, &signature).is_ok();
256 }
257 _ => {
258 error!(
259 "InvalidSignatureScheme: {:?} Are you missing a feature?",
260 verify.signature_scheme
261 );
262 return Err(TlsError::InvalidSignatureScheme);
263 }
264 }
265
266 if !verified {
267 return Err(TlsError::InvalidSignature);
268 }
269 Ok(())
270}
271
272fn get_certificate_tlv_bytes<'a>(input: &[u8]) -> der::Result<&[u8]> {
273 use der::{Decode, Reader, SliceReader};
274
275 let mut reader = SliceReader::new(input)?;
276 let top_header = der::Header::decode(&mut reader)?;
277 top_header.tag().assert_eq(der::Tag::Sequence)?;
278
279 let header = der::Header::peek(&mut reader)?;
280 header.tag().assert_eq(der::Tag::Sequence)?;
281
282 reader.tlv_bytes()
283}
284
285fn get_cert_time(time: Time) -> u64 {
286 match time {
287 Time::UtcTime(utc_time) => utc_time.to_unix_duration().as_secs(),
288 Time::GeneralTime(generalized_time) => generalized_time.to_unix_duration().as_secs(),
289 }
290}
291
292fn verify_certificate(
293 verifier: &CertificateEntryRef,
294 certificate: &CertificateEntryRef,
295 now: Option<u64>,
296) -> Result<CertificateNames, TlsError> {
297 let mut verified = false;
298 let mut common_name = None;
299 let mut san_dns_names = heapless::Vec::new();
300
301 let ca_certificate = if let CertificateEntryRef::X509(verifier) = verifier {
302 DecodedCertificate::from_der(verifier).map_err(|_| TlsError::DecodeError)?
303 } else {
304 return Err(TlsError::DecodeError);
305 };
306
307 if let CertificateEntryRef::X509(certificate) = certificate {
308 let parsed_certificate =
309 DecodedCertificate::from_der(certificate).map_err(|_| TlsError::DecodeError)?;
310
311 let ca_public_key = ca_certificate
312 .tbs_certificate
313 .subject_public_key_info
314 .public_key
315 .as_bytes()
316 .ok_or(TlsError::DecodeError)?;
317
318 common_name = extract_common_name(&parsed_certificate.tbs_certificate)
319 .map_err(|_| TlsError::DecodeError)?;
320 debug!("CommonName: {:?}", common_name);
321
322 san_dns_names = extract_san_dns_names(&parsed_certificate.tbs_certificate)
323 .map_err(|_| TlsError::DecodeError)?;
324 debug!("SANs: {:?}", san_dns_names);
325
326 if let Some(now) = now {
327 if get_cert_time(parsed_certificate.tbs_certificate.validity.not_before) > now
328 || get_cert_time(parsed_certificate.tbs_certificate.validity.not_after) < now
329 {
330 return Err(TlsError::InvalidCertificate);
331 }
332 debug!("Epoch is {} and certificate is valid!", now)
333 }
334
335 let certificate_data =
336 get_certificate_tlv_bytes(certificate).map_err(|_| TlsError::DecodeError)?;
337
338 match parsed_certificate.signature_algorithm {
339 ECDSA_SHA256 => {
340 use p256::ecdsa::{Signature, VerifyingKey, signature::Verifier};
341 let verifying_key = VerifyingKey::from_sec1_bytes(ca_public_key)
342 .map_err(|_| TlsError::DecodeError)?;
343
344 let signature = Signature::from_der(
345 parsed_certificate
346 .signature
347 .as_bytes()
348 .ok_or(TlsError::ParseError(ParseError::InvalidData))?,
349 )
350 .map_err(|_| TlsError::ParseError(ParseError::InvalidData))?;
351
352 verified = verifying_key.verify(&certificate_data, &signature).is_ok();
353 }
354 #[cfg(feature = "p384")]
355 ECDSA_SHA384 => {
356 use p384::ecdsa::{Signature, VerifyingKey, signature::Verifier};
357 let verifying_key = VerifyingKey::from_sec1_bytes(ca_public_key)
358 .map_err(|_| TlsError::DecodeError)?;
359
360 let signature = Signature::from_der(
361 parsed_certificate
362 .signature
363 .as_bytes()
364 .ok_or(TlsError::ParseError(ParseError::InvalidData))?,
365 )
366 .map_err(|_| TlsError::ParseError(ParseError::InvalidData))?;
367
368 verified = verifying_key.verify(&certificate_data, &signature).is_ok();
369 }
370 #[cfg(feature = "ed25519")]
371 ED25519 => {
372 use ed25519_dalek::{Signature, Verifier, VerifyingKey};
373 let verifying_key: VerifyingKey =
374 VerifyingKey::from_bytes(ca_public_key.try_into().unwrap())
375 .map_err(|_| TlsError::DecodeError)?;
376
377 let signature = Signature::try_from(
378 parsed_certificate
379 .signature
380 .as_bytes()
381 .ok_or(TlsError::ParseError(ParseError::InvalidData))?,
382 )
383 .map_err(|_| TlsError::ParseError(ParseError::InvalidData))?;
384
385 verified = verifying_key.verify(certificate_data, &signature).is_ok();
386 }
387 #[cfg(feature = "rsa")]
388 a if a == RSA_PKCS1_SHA256 => {
389 use rsa::{
390 pkcs1::DecodeRsaPublicKey,
391 pkcs1v15::{Signature, VerifyingKey},
392 signature::Verifier,
393 };
394 use sha2::Sha256;
395
396 let verifying_key =
397 VerifyingKey::<Sha256>::from_pkcs1_der(ca_public_key).map_err(|e| {
398 #[cfg(feature = "defmt")]
399 error!("VerifyingKey: {:?}", Debug2Format(&e));
400 #[cfg(not(feature = "defmt"))]
401 error!("VerifyingKey: {}", e);
402 TlsError::DecodeError
403 })?;
404
405 let signature = Signature::try_from(
406 parsed_certificate
407 .signature
408 .as_bytes()
409 .ok_or(TlsError::ParseError(ParseError::InvalidData))?,
410 )
411 .map_err(|e| {
412 #[cfg(feature = "defmt")]
413 error!("Signature: {:?}", Debug2Format(&e));
414 #[cfg(not(feature = "defmt"))]
415 error!("Signature: {}", e);
416 TlsError::ParseError(ParseError::InvalidData)
417 })?;
418
419 verified = verifying_key.verify(certificate_data, &signature).is_ok();
420 }
421 #[cfg(feature = "rsa")]
422 a if a == RSA_PKCS1_SHA384 => {
423 use rsa::{
424 pkcs1::DecodeRsaPublicKey,
425 pkcs1v15::{Signature, VerifyingKey},
426 signature::Verifier,
427 };
428 use sha2::Sha384;
429
430 let verifying_key = VerifyingKey::<Sha384>::from_pkcs1_der(ca_public_key)
431 .map_err(|_| TlsError::DecodeError)?;
432
433 let signature = Signature::try_from(
434 parsed_certificate
435 .signature
436 .as_bytes()
437 .ok_or(TlsError::ParseError(ParseError::InvalidData))?,
438 )
439 .map_err(|_| TlsError::ParseError(ParseError::InvalidData))?;
440
441 verified = verifying_key.verify(certificate_data, &signature).is_ok();
442 }
443 #[cfg(feature = "rsa")]
444 a if a == RSA_PKCS1_SHA512 => {
445 use rsa::{
446 pkcs1::DecodeRsaPublicKey,
447 pkcs1v15::{Signature, VerifyingKey},
448 signature::Verifier,
449 };
450 use sha2::Sha512;
451
452 let verifying_key = VerifyingKey::<Sha512>::from_pkcs1_der(ca_public_key)
453 .map_err(|_| TlsError::DecodeError)?;
454
455 let signature = Signature::try_from(
456 parsed_certificate
457 .signature
458 .as_bytes()
459 .ok_or(TlsError::ParseError(ParseError::InvalidData))?,
460 )
461 .map_err(|_| TlsError::ParseError(ParseError::InvalidData))?;
462
463 verified = verifying_key.verify(certificate_data, &signature).is_ok();
464 }
465 _ => {
466 error!(
467 "Unsupported signature alg: {:?}",
468 parsed_certificate.signature_algorithm
469 );
470 return Err(TlsError::InvalidSignatureScheme);
471 }
472 }
473 }
474
475 if !verified {
476 return Err(TlsError::InvalidCertificate);
477 }
478
479 Ok(CertificateNames {
480 common_name,
481 san_dns_names,
482 })
483}
484
485fn tls_hostname_match(
491 names: &CertificateNames,
492 hostname: &Option<heapless::String<HOSTNAME_MAXLEN>>,
493) -> bool {
494 let hostname = match hostname.as_ref() {
495 Some(h) => h,
496 None => {
497 return names.common_name.is_none() && names.san_dns_names.is_empty();
498 }
499 };
500
501 for san in &names.san_dns_names {
502 if tls_hostname_match_impl(san.as_bytes(), hostname.as_bytes()) {
503 return true;
504 }
505 }
506
507 match names.common_name.as_ref() {
508 Some(cn) => tls_hostname_match_impl(cn.as_bytes(), hostname.as_bytes()),
509 None => false,
510 }
511}
512
513fn tls_hostname_match_impl(cn: &[u8], host: &[u8]) -> bool {
514 let mut cn_labels = 1;
515 let mut host_labels = 1;
516 let mut stars = 0;
517
518 for &b in cn {
519 match b {
520 b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'-' | b'.' | b'*' => {}
521 _ => return false,
522 }
523 if b == b'.' {
524 cn_labels += 1;
525 }
526 if b == b'*' {
527 stars += 1;
528 }
529 }
530
531 for &b in host {
532 match b {
533 b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'-' | b'.' => {}
534 _ => return false,
535 }
536 if b == b'.' {
537 host_labels += 1;
538 }
539 }
540
541 if stars == 0 {
542 if cn.len() != host.len() {
543 return false;
544 }
545 for i in 0..cn.len() {
546 if cn[i].to_ascii_lowercase() != host[i].to_ascii_lowercase() {
547 return false;
548 }
549 }
550 return true;
551 }
552
553 if stars != 1 {
555 return false;
556 }
557 if !cn.starts_with(b"*.") {
558 return false;
559 }
560 if cn_labels < 3 {
561 return false;
562 }
563 if cn_labels != host_labels {
564 return false;
565 }
566
567 let suffix = &cn[2..];
568 let mut dot_idx = None;
569 for i in 0..host.len() {
570 if host[i] == b'.' {
571 dot_idx = Some(i);
572 break;
573 }
574 }
575 let dot_idx = match dot_idx {
576 Some(i) => i,
577 None => return false,
578 };
579 let host_suffix = &host[dot_idx + 1..];
580
581 if suffix.len() != host_suffix.len() {
582 return false;
583 }
584
585 for i in 0..suffix.len() {
586 if suffix[i].to_ascii_lowercase() != host_suffix[i].to_ascii_lowercase() {
587 return false;
588 }
589 }
590
591 true
592}
593
594#[cfg(test)]
595mod tests {
596 use super::tls_hostname_match_impl;
597
598 #[test]
599 fn exact_match() {
600 assert!(tls_hostname_match_impl(b"example.com", b"example.com"));
601 assert!(tls_hostname_match_impl(b"EXAMPLE.COM", b"example.com"));
602 assert!(tls_hostname_match_impl(b"example.com", b"EXAMPLE.COM"));
603 }
604
605 #[test]
606 fn exact_mismatch() {
607 assert!(!tls_hostname_match_impl(b"example.com", b"example.org"));
608 assert!(!tls_hostname_match_impl(b"example.com", b"sub.example.com"));
609 }
610
611 #[test]
612 fn valid_wildcard_match() {
613 assert!(tls_hostname_match_impl(
614 b"*.example.com",
615 b"api.example.com"
616 ));
617 assert!(tls_hostname_match_impl(
618 b"*.example.com",
619 b"WWW.example.com"
620 ));
621 }
622
623 #[test]
624 fn wildcard_single_label_only() {
625 assert!(!tls_hostname_match_impl(
626 b"*.example.com",
627 b"a.b.example.com"
628 ));
629 }
630
631 #[test]
632 fn wildcard_requires_same_label_count() {
633 assert!(!tls_hostname_match_impl(b"*.example.com", b"example.com"));
634 assert!(!tls_hostname_match_impl(
635 b"*.example.com",
636 b"deep.api.example.com"
637 ));
638 }
639
640 #[test]
641 fn wildcard_must_be_leftmost_label() {
642 assert!(!tls_hostname_match_impl(
643 b"api.*.example.com",
644 b"api.test.example.com"
645 ));
646 assert!(!tls_hostname_match_impl(
647 b"foo*.example.xx",
648 b"foobar.example.xx"
649 ));
650 }
651
652 #[test]
653 fn wildcard_requires_minimum_three_labels() {
654 assert!(!tls_hostname_match_impl(b"*.com", b"example.com"));
655 assert!(!tls_hostname_match_impl(b"*.org", b"test.org"));
656 }
657
658 #[test]
659 fn multiple_wildcards_rejected() {
660 assert!(!tls_hostname_match_impl(
661 b"*.*.example.com",
662 b"a.b.example.com"
663 ));
664 assert!(!tls_hostname_match_impl(
665 b"**.example.com",
666 b"api.example.com"
667 ));
668 }
669
670 #[test]
671 fn idna_a_label_supported() {
672 assert!(tls_hostname_match_impl(
673 b"xn--bcher-kva.example",
674 b"xn--bcher-kva.example"
675 ));
676
677 assert!(tls_hostname_match_impl(
678 b"*.xn--bcher-kva.example",
679 b"api.xn--bcher-kva.example"
680 ));
681 }
682
683 #[test]
684 fn unicode_rejected() {
685 assert!(!tls_hostname_match_impl(
686 "bücher.example".as_bytes(),
687 "bücher.example".as_bytes()
688 ));
689 assert!(!tls_hostname_match_impl(
690 "*.bücher.example".as_bytes(),
691 "api.bücher.example".as_bytes()
692 ));
693 }
694
695 #[test]
696 fn invalid_characters_rejected() {
697 assert!(!tls_hostname_match_impl(b"example!.com", b"example!.com"));
698 assert!(!tls_hostname_match_impl(b"example.com", b"exa mple.com"));
699 }
700}