1use super::der::*;
8use crate::crypto::ellipticcurve::Curve;
9use crate::crypto::ellipticcurve::Point;
10use crate::crypto::ellipticcurve::PublicKey;
11
12use ibig::IBig;
13
14use super::{bytes, bytes::str_to_u16, log};
15use crate::crypto::ellipticcurve::Signature;
16use std::collections::HashMap;
17use std::fmt::Display;
18use std::time::{SystemTime, UNIX_EPOCH};
19
20#[derive(Debug)]
21pub enum ParseError {
22 Algorithms,
23 SubjectPublicKeyInfo,
24 UTF8StringTimestamp,
25 NameGet,
26 NameAddValue,
27 TBSBuilder,
28 DerParseType,
29 DerParseLen,
30 Version,
31 UTF8String,
32 TimeStamp,
33 MissingObjectIdentifier,
34 NotYetImplemented,
35}
36
37#[derive(Debug)]
38pub struct UtcTime {
39 pub year: u16,
40 pub month: u16,
41 pub day: u16,
42 pub hour: u16,
43 pub minute: u16,
44 pub second: u16,
45}
46
47impl UtcTime {
48 fn from_utc_timestamp(t: &str) -> Option<UtcTime> {
49 if t.len() < 12 {
50 return None;
51 }
52 let (mut year, month, day, hour, minute, second) = (
53 str_to_u16(&t[0..]),
54 str_to_u16(&t[2..]),
55 str_to_u16(&t[4..]),
56 str_to_u16(&t[6..]),
57 str_to_u16(&t[8..]),
58 str_to_u16(&t[10..]),
59 );
60 if year < 50 {
61 year += 2000;
62 } else {
63 year += 1900;
64 }
65 Some(UtcTime {
66 year,
67 month,
68 day,
69 hour,
70 minute,
71 second,
72 })
73 }
74 pub fn get_unix_timestamp(&self) -> u64 {
75 let days_since_year_start = [0_u64, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
76 let year = self.year as u64;
77
78 let leap_years =
79 ((year - 1) - 1968) / 4 - ((year - 1) - 1900) / 100 + ((year - 1) - 1600) / 400;
80 let mut days = (year - 1970) * 365 + leap_years;
81 days += days_since_year_start[(self.month - 1) as usize];
82 days += (self.day - 1) as u64;
83
84 let mut res = 0;
85 res += days * 86400;
86 res += ((self.hour) as u64) * 3600;
87 res += ((self.minute) as u64) * 60;
88 res += (self.second) as u64;
89 res
90 }
91}
92impl Display for UtcTime {
93 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94 write!(
96 f,
97 "{:0>2}:{:0>2}:{:0>2} {:0>2}/{:0>2}/{}",
98 self.hour, self.minute, self.second, self.month, self.day, self.year
99 )
100 }
101}
102
103pub struct Validity {
104 not_before: Option<UtcTime>,
105 not_after: Option<UtcTime>,
106}
107impl Validity {
108 fn new() -> Validity {
109 Validity {
110 not_before: None,
111 not_after: None,
112 }
113 }
114 pub fn is_valid(&self) -> bool {
115 let current_time = SystemTime::now();
116 let since_the_epoch = current_time
117 .duration_since(UNIX_EPOCH)
118 .expect("Time went backwards");
119 let current = since_the_epoch.as_secs();
120
121 if let Some(not_before) = self.not_before.as_ref() {
122 if current < not_before.get_unix_timestamp() {
123 return false;
124 }
125 log::debug!(" start date: {not_before}");
126 }
127
128 if let Some(not_after) = self.not_after.as_ref() {
129 if current > not_after.get_unix_timestamp() {
130 return false;
131 }
132 log::debug!(" end date: {not_after}");
133 }
134
135 true
136 }
137}
138pub type Oid = [u8; 10];
139pub type OctetString = Vec<u8>;
140pub struct Extension {
141 pub id: Oid,
142 pub critical: Option<bool>,
143 pub value: OctetString,
144}
145
146#[derive(Debug, PartialEq)]
147pub enum Algorithms {
148 EcdsaWithSha256,
149 EcdsaWithSha384,
150 EcPublicKey,
151 Sha256WithRSAEncryption,
152}
153impl Algorithms {
154 pub fn new(s: &str) -> Result<Algorithms, ParseError> {
155 Ok(match s {
156 "ecdsaWithSHA256" => Algorithms::EcdsaWithSha256,
157 "ecdsaWithSHA384" => Algorithms::EcdsaWithSha384,
158 "ecPublicKey" => Algorithms::EcPublicKey,
159 "sha256WithRSAEncryption" => Algorithms::Sha256WithRSAEncryption,
160 e => {
161 log::error!("Add algorithm: {e} (x509.rs)");
162 return Err(ParseError::Algorithms);
163 }
164 })
165 }
166}
167#[derive(Debug)]
168pub struct AlgorithmIdentifier {
169 pub algorithm: Algorithms,
170 pub parameters: Option<String>,
171}
172impl AlgorithmIdentifier {
173 pub fn new(algorithm: Algorithms) -> AlgorithmIdentifier {
174 AlgorithmIdentifier {
175 algorithm,
176 parameters: None,
177 }
178 }
179}
180pub struct SubjectPublicKeyInfo {
181 pub algorithm: AlgorithmIdentifier,
182 pub subject_public_key: BitString,
183}
184
185struct SubjectPublicKeyInfoBuilder {
186 algorithm: Option<AlgorithmIdentifier>,
187 subject_public_key: Option<BitString>,
188}
189impl SubjectPublicKeyInfoBuilder {
190 fn new() -> Self {
191 Self {
192 algorithm: None,
193 subject_public_key: None,
194 }
195 }
196 fn build(self) -> Result<SubjectPublicKeyInfo, ParseError> {
197 if self.algorithm.is_none() {
198 return Err(ParseError::SubjectPublicKeyInfo);
199 }
200 Ok(SubjectPublicKeyInfo {
201 algorithm: self.algorithm.unwrap(),
202 subject_public_key: self.subject_public_key.unwrap(),
203 })
204 }
205}
206fn parse_object_identifier(id: &[u8]) -> Result<String, ParseError> {
207 let id = bytes::to_u128_be_fill(id);
208 Ok(match id {
209 0x550406 => "countryName".to_string(),
210 0x550408 => "stateOrProvinceName".to_string(),
211 0x55040a => "organizationName".to_string(),
212 0x550403 => "commonName".to_string(),
213 0x2a8648ce3d040302 => "ecdsaWithSHA256".to_string(),
214 0x2a8648ce3d040303 => "ecdsaWithSHA384".to_string(),
215 0x2a8648ce3d030107 => "prime256v1".to_string(),
216 0x2a8648ce3d0201 => "ecPublicKey".to_string(),
217 0x2a864886f70d010901 => "emailAddress".to_string(),
218 0x2a864886f70d01010b => "sha256WithRSAEncryption".to_string(),
219 0x2a864886f70d010101 => "sha256WithRSAEncryption".to_string(),
220 0x550407 => "localityName".to_string(),
221 0x55040B => "organizationalUnitName".to_string(),
222 _ => {
223 log::error!("Missing ObjectIdentifier: = {id:#x} (x509.rs)");
224 "UNKOWN".to_string()
225 }
227 })
228}
229
230pub struct BitString(Vec<u8>);
231
232impl BitString {
233 pub fn as_slice(&self) -> &[u8] {
234 &self.0
235 }
236}
237
238pub struct Extensions(Vec<Extension>);
239
240pub struct Name(HashMap<String, String>, Option<String>);
241
242impl Name {
243 pub fn new() -> Self {
244 Self(HashMap::new(), None)
245 }
246 #[allow(dead_code)]
247 pub fn get(&self, key: &str) -> Result<String, ParseError> {
248 if let Some(value) = self.0.get(key) {
249 return Ok(value.to_string());
250 }
251 Err(ParseError::NameGet)
252 }
253 pub fn add_object_identifier(&mut self, id: &[u8]) -> Result<(), ParseError> {
254 self.1 = Some(parse_object_identifier(id)?);
255 Ok(())
256 }
257 pub fn add_value(&mut self, value: String) -> Result<(), ParseError> {
258 if let Some(name) = self.1.as_ref() {
259 self.0.insert(name.to_string(), value);
260 self.1 = None;
261 return Ok(());
262 }
263 Err(ParseError::NameAddValue)
264 }
265}
266
267impl Default for Name {
268 fn default() -> Self {
269 Self::new()
270 }
271}
272
273impl Display for Name {
274 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
275 let empty = "".to_string();
276 let c = self.0.get("countryName").unwrap_or(&empty);
277 let st = self.0.get("stateOrProvinceName").unwrap_or(&empty);
278 let o = self.0.get("organizationName").unwrap_or(&empty);
279 let cn = self.0.get("commonName").unwrap_or(&empty);
280 write!(f, "C={}; ST={}; O={}; CN={}", c, st, o, cn)
281 }
282}
283
284pub struct TBSCertificate {
285 pub version: Option<Version>,
286 pub serial_number: IBig,
287 pub signature: AlgorithmIdentifier,
288 pub issuer: Name,
289 pub validity: Validity,
290 pub subject: Name,
291 pub subject_public_key_info: SubjectPublicKeyInfo,
292 pub extension: Option<Extensions>,
295 pub raw_data: Option<Vec<u8>>,
296}
297pub struct TBSCertificateBuilder {
298 version: Option<Version>,
299 serial_number: Option<IBig>,
300 signature: Option<AlgorithmIdentifier>,
301 issuer: Name,
302 validity: Validity,
303 subject: Name,
304 subject_public_key_info: SubjectPublicKeyInfoBuilder,
305 extension: Option<Extensions>,
308 raw_data: Option<Vec<u8>>,
309}
310
311impl TBSCertificateBuilder {
312 pub fn new() -> TBSCertificateBuilder {
313 TBSCertificateBuilder {
314 version: None,
315 serial_number: None,
316 signature: None,
317 issuer: Name::new(),
318 validity: Validity::new(),
319 subject: Name::new(),
320 subject_public_key_info: SubjectPublicKeyInfoBuilder::new(),
321 extension: None,
322 raw_data: None,
323 }
324 }
325 pub fn build(self) -> Result<TBSCertificate, ParseError> {
326 if self.serial_number.is_none() || self.signature.is_none() {
327 return Err(ParseError::TBSBuilder);
328 }
329 Ok(TBSCertificate {
330 version: self.version,
331 serial_number: self.serial_number.as_ref().unwrap().clone(),
332 signature: self.signature.unwrap(),
333 issuer: self.issuer,
334 validity: self.validity,
335 subject: self.subject,
336 subject_public_key_info: self.subject_public_key_info.build()?,
337 extension: self.extension,
338 raw_data: self.raw_data,
339 })
340 }
341}
342
343impl Default for TBSCertificateBuilder {
344 fn default() -> Self {
345 Self::new()
346 }
347}
348
349pub struct X509 {
350 pub tbs_certificate: TBSCertificate,
351 pub tbs_certificate_size: usize,
352 pub signature_algorithm: Algorithms,
353 pub signature: Option<Signature>,
354}
355impl X509 {
356 pub fn from_raw(data: &[u8]) -> Result<X509, ParseError> {
357 let mut res = X509Builder::new();
358 let mut consumed = 0;
359 log::debug!("Start parsing X509 certificate");
360 parse(&mut res, ParsingState::Init, data, &mut consumed)?;
361 res.build()
362 }
363 pub fn get_public_key(&self) -> Option<PublicKey> {
364 if self
365 .tbs_certificate
366 .subject_public_key_info
367 .algorithm
368 .algorithm
369 != Algorithms::EcPublicKey
370 {
371 return None;
372 }
373
374 let curve = match self
375 .tbs_certificate
376 .subject_public_key_info
377 .algorithm
378 .parameters
379 .as_ref()?
380 .as_str()
381 {
382 "prime256v1" => Curve::secp256r1(),
383 _ => return None,
384 };
385
386 let pubkey = self
387 .tbs_certificate
388 .subject_public_key_info
389 .subject_public_key
390 .as_slice();
391
392 if pubkey[0] != 0x4 {
393 todo!("Handle compressed points");
394 }
395
396 let x = bytes::to_ibig_be(&pubkey[1..33]);
397 let y = bytes::to_ibig_be(&pubkey[33..65]);
398
399 Some(PublicKey::new(Point::new(x, y), curve))
400 }
401}
402pub struct X509Builder {
403 tbs_certificate: TBSCertificateBuilder,
404 tbs_certificate_size: usize,
405 signature_algorithm: Option<Algorithms>,
406 signature: (Option<IBig>, Option<IBig>),
407}
408impl X509Builder {
409 fn new() -> X509Builder {
410 X509Builder {
411 tbs_certificate: TBSCertificateBuilder::new(),
412 tbs_certificate_size: 0,
413 signature_algorithm: None,
414 signature: (None, None),
415 }
416 }
417 fn build(self) -> Result<X509, ParseError> {
418 let mut signature = None;
419
420 if self.signature.0.is_some() && self.signature.1.is_some() {
421 signature = Some(Signature::new(
422 self.signature.1.unwrap(),
423 self.signature.0.unwrap(),
424 ));
425 }
426
427 Ok(X509 {
428 tbs_certificate: self.tbs_certificate.build()?,
429 tbs_certificate_size: self.tbs_certificate_size,
430 signature_algorithm: self.signature_algorithm.unwrap(),
431 signature,
432 })
433 }
434}
435
436#[derive(PartialEq, Debug, Clone)]
437enum ParsingState {
438 Init,
439 InBitString,
440 InSet,
441}
442
443#[derive(Debug, Clone)]
444pub enum Version {
445 V1 = 0,
446 V2 = 1,
447 V3 = 2,
448}
449impl Version {
450 fn parse(data: &[u8]) -> Result<Version, ParseError> {
451 if data.len() == 1 {
452 return Ok(match data[0] {
453 0 => Version::V1,
454 1 => Version::V2,
455 2 => Version::V3,
456 _ => return Err(ParseError::Version),
457 });
458 }
459 Err(ParseError::Version)
460 }
461}
462fn parse(
463 res: &mut X509Builder,
464 state: ParsingState,
465 data: &[u8],
466 consumed: &mut usize,
467) -> Result<(), ParseError> {
468 let (size, der) = der_parse(consumed, data)?;
469 let body = &data[*consumed..*consumed + size];
470
471 match der {
472 EncodedForm::Constructed(cons) => match cons {
473 DerType::Sequence => {
474 if res.tbs_certificate_size == 0 {
475 res.tbs_certificate_size = 1; } else if res.tbs_certificate_size == 1 {
477 res.tbs_certificate_size = size + 4;
478 }
479 let size_should = size + *consumed;
480 while size_should > *consumed {
481 parse(res, state.clone(), data, consumed)?;
482 }
483 }
484 DerType::ContextSpecific(tag) => {
485 match tag {
486 0x00 => {
487 res.tbs_certificate.version = Some(Version::parse(&body[2..])?);
488 }
489 0x03 => log::fixme!("TODO: certificate extenstions"),
490 _ => (),
491 }
492 *consumed += size;
493 }
494 DerType::Set => {
495 parse(res, ParsingState::InSet, data, consumed)?;
496 }
498 _ => {
499 todo!("cons={cons:?}");
500 }
501 },
502 EncodedForm::Primitive(prim) => match prim {
503 DerType::Integer => {
504 let int = bytes::to_ibig_be(body);
505 *consumed += size;
506 if res.tbs_certificate.serial_number.is_none() {
507 res.tbs_certificate.serial_number = Some(int);
508 } else if res.signature.0.is_none() {
509 res.signature.0 = Some(int);
510 } else {
511 res.signature.1 = Some(int);
512 }
513 }
514 DerType::ObjectIdentifier => match state {
515 ParsingState::InSet => {
516 if res.tbs_certificate.validity.not_after.is_none() {
517 res.tbs_certificate.issuer.add_object_identifier(body)?;
518 } else {
519 res.tbs_certificate.subject.add_object_identifier(body)?;
520 }
521 *consumed += size;
522 }
523 ParsingState::Init => {
524 let oji = parse_object_identifier(body)?;
525 if res.tbs_certificate.signature.is_none() {
526 res.tbs_certificate.signature =
527 Some(AlgorithmIdentifier::new(Algorithms::new(&oji)?));
528 } else if res
529 .tbs_certificate
530 .subject_public_key_info
531 .algorithm
532 .is_none()
533 {
534 res.tbs_certificate.subject_public_key_info.algorithm =
535 Some(AlgorithmIdentifier::new(Algorithms::new(&oji)?));
536 } else if res
537 .tbs_certificate
538 .subject_public_key_info
539 .algorithm
540 .as_ref()
541 .unwrap()
542 .parameters
543 .is_none()
544 {
545 res.tbs_certificate
546 .subject_public_key_info
547 .algorithm
548 .as_mut()
549 .unwrap()
550 .parameters = Some(oji);
551 } else if res.signature_algorithm.is_none() {
552 res.signature_algorithm = Some(Algorithms::new(&oji)?);
553 }
554 *consumed += size;
555 }
556 _ => todo!("ObjectIdentifier {state:?}"),
557 },
558 DerType::PrintableString | DerType::UTF8String => {
559 let string = match String::from_utf8(body.to_vec()) {
560 Ok(e) => e,
561 Err(_) => return Err(ParseError::UTF8String),
562 };
563 if res.tbs_certificate.validity.not_after.is_none() {
564 res.tbs_certificate.issuer.add_value(string)?;
565 } else {
566 res.tbs_certificate.subject.add_value(string)?;
567 }
568 *consumed += size;
569 }
570 DerType::OctetString => {
571 *consumed += size;
572 todo!("GOT OctetString");
573 }
574 DerType::UTCTime => {
575 let timestamp = match String::from_utf8(body.to_vec()) {
576 Ok(e) => e,
577 Err(_) => return Err(ParseError::UTF8StringTimestamp),
578 };
579 let timestamp = UtcTime::from_utc_timestamp(×tamp);
580 if timestamp.is_none() {
581 return Err(ParseError::TimeStamp);
582 }
583 if res.tbs_certificate.validity.not_before.is_none() {
584 res.tbs_certificate.validity.not_before = timestamp;
585 } else {
586 res.tbs_certificate.validity.not_after = timestamp;
587 }
588 *consumed += size;
589 }
590 DerType::BitString => {
591 if data[*consumed + 1] == 0x30 {
592 *consumed += 1;
593 let size_should = size + *consumed - 1;
594 while size_should > *consumed {
595 parse(res, ParsingState::InBitString, data, consumed)?;
596 }
597 } else {
598 let number_of_unused_bits = data[*consumed];
599 if number_of_unused_bits != 0 {
600 todo!("Number of unused bits is not 0");
601 }
602
603 if res
604 .tbs_certificate
605 .subject_public_key_info
606 .subject_public_key
607 .is_none()
608 {
609 res.tbs_certificate
610 .subject_public_key_info
611 .subject_public_key = Some(BitString(body[1..].to_vec()));
612 }
613 *consumed += size;
614 }
615 }
616 DerType::Null => {
617 todo!("Null");
618 }
619 _ => {
620 log::error!("not yet implemented: prim={prim:?}");
621 return Err(ParseError::NotYetImplemented);
622 }
623 },
624 }
625 Ok(())
626}
627
628#[cfg(test)]
629mod tests {
630
631 use crate::utils::x509::{Algorithms, X509};
632
633 #[test]
634 fn test_parse_x509_client() {
635 let data = super::bytes::from_hex("308201f53082019b02145608da598885be938e65547ce4999f58fee38580300a06082a8648ce3d040302306c310b3009060355040613024445310d300b06035504080c044d6f6f6e31193017060355040a0c10416e6f746865724341207365637572653112301006035504030c09416e6f746865724341311f301d06092a864886f70d010901161073656375726974794074736d722e6575301e170d3233303331363138303335325a170d3234303331353138303335325a30818d310b3009060355040613024445310d300b06035504080c044d6f6f6e310e300c06035504070c05537061636531173015060355040a0c0e416e6f74686572436f6d70616e793111300f060355040b0c087a65637572697479310e300c06035504030c056f74736d723123302106092a864886f70d010901161474736d7240616e6f746865722e636f6d70616e793059301306072a8648ce3d020106082a8648ce3d03010703420004e206723a9057980587346a8dc604d729cb78eb2a26569bc6ef63d39bb004c4dbb8f3eb5ad2bcd70adfb182248bc32052dd9a58a0ba4578bb3e7aab71b6e4cbe3300a06082a8648ce3d040302034800304502201136f044a0c91932cc7c5f5ae3e6c13fafef1332f0fa2ebc413ec361c19a9ef10221009acdbf76f32fe17fc0b2d6acaf6c61d2f632cbfdb26a4cbeb53d5e30ca4aefea");
636
637 let x509 = match X509::from_raw(&data) {
638 Ok(e) => e,
639 Err(err) => {
640 panic!("ParseError = {err:?}");
641 }
642 };
643
644 assert_eq!(x509.signature_algorithm, Algorithms::EcdsaWithSha256);
645 assert_eq!(
646 x509.tbs_certificate.issuer.get("commonName").unwrap(),
647 "AnotherCA"
648 );
649 assert_eq!(
650 x509.tbs_certificate.subject.get("commonName").unwrap(),
651 "otsmr"
652 );
653 assert_eq!(
654 x509.tbs_certificate.issuer.get("organizationName").unwrap(),
655 "AnotherCA secure"
656 );
657 }
658 #[test]
659 fn test_parse_x509_webpage() {
660 let data = super::bytes::from_hex("30820215308201bba0030201020214612fe409659dfe39c6a2d685db2c71fbbfbec7d4300a06082a8648ce3d0403023060310b30090603550406130244453113301106035504080c0a536f6d652d53746174653121301f060355040a0c18496e7465726e6574205769646769747320507479204c74643119301706035504030c10616e6f74686572746c732e6c6f63616c301e170d3233303330393130313831325a170d3234303330383130313831325a3060310b30090603550406130244453113301106035504080c0a536f6d652d53746174653121301f060355040a0c18496e7465726e6574205769646769747320507479204c74643119301706035504030c10616e6f74686572746c732e6c6f63616c3059301306072a8648ce3d020106082a8648ce3d030107034200046eed2bfee88db30f90001b34f24ad85a96aedc7c01d175f81d6da2a3d96f29c86d5e14735b63f2d579e067b503bb42c1d934f5b7316fa57a13d0454577d194e5a3533051301d0603551d0e04160414a9d135118bc5c3b7076c6248169e0087b5ed5c00301f0603551d23041830168014a9d135118bc5c3b7076c6248169e0087b5ed5c00300f0603551d130101ff040530030101ff300a06082a8648ce3d04030203480030450220372c31a1401b8dce99a61cd3ac7f83d4aec628085ecab625093ac72e628fd1d4022100a28383292dc8d73f114f2c1694e4dffc51791aceca226cc83699b9467bbb78fc");
661
662 let x509 = match X509::from_raw(&data) {
663 Ok(e) => e,
664 Err(_) => {
665 panic!("TLS ERROR");
666 }
667 };
668
669 assert_eq!(x509.signature_algorithm, Algorithms::EcdsaWithSha256);
670 assert_eq!(
671 x509.tbs_certificate.issuer.get("commonName").unwrap(),
672 "anothertls.local"
673 );
674 assert_eq!(
675 x509.tbs_certificate.issuer.get("organizationName").unwrap(),
676 "Internet Widgits Pty Ltd"
677 );
678 }
679}