anothertls/utils/
x509.rs

1/*
2 * Copyright (c) 2023, Tobias Müller <git@tsmr.eu>
3 *
4 *
5 */
6
7use 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        // 1678989832
95        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            // return Err(ParseError::MissingObjectIdentifier);
226        }
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    // issuer_unique_id:
293    // subject_unique_id:
294    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    // issuer_unique_id:
306    // subject_unique_id:
307    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; // Size of tbs_certificate + signature_algorithm + signature
476                } 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                // *consumed += size;
497            }
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(&timestamp);
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}