Skip to main content

hickory_proto/rr/rdata/
cert.rs

1// Copyright 2024 Brian Taber <btaber@zsd.systems>
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// https://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// https://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8//! CERT record type for storing certificates in DNS
9use alloc::string::String;
10use alloc::vec::Vec;
11use core::fmt;
12use core::str::FromStr;
13
14#[cfg(feature = "serde")]
15use serde::{Deserialize, Serialize};
16
17use crate::{
18    error::ProtoResult,
19    rr::{RData, RecordData, RecordDataDecodable, RecordType},
20    serialize::{
21        binary::{
22            BinDecodable, BinDecoder, BinEncodable, BinEncoder, DecodeError, RDataEncoding,
23            Restrict, RestrictedMath,
24        },
25        txt::ParseError,
26    },
27};
28
29/// [RFC 4398, Storing Certificates in DNS, November 1987](https://tools.ietf.org/html/rfc4398#section-2.1)
30///
31/// ```text
32/// [2.1](https://datatracker.ietf.org/doc/html/rfc4398#section-2.1).  Certificate Type Values
33///
34///    The following values are defined or reserved:
35///
36///          Value  Mnemonic  Certificate Type
37///          -----  --------  ----------------
38///              0            Reserved
39///              1  PKIX      X.509 as per PKIX
40///              2  SPKI      SPKI certificate
41///              3  PGP       OpenPGP packet
42///              4  IPKIX     The URL of an X.509 data object
43///              5  ISPKI     The URL of an SPKI certificate
44///              6  IPGP      The fingerprint and URL of an OpenPGP packet
45///              7  ACPKIX    Attribute Certificate
46///              8  IACPKIX   The URL of an Attribute Certificate
47///          9-252            Available for IANA assignment
48///            253  URI       URI private
49///            254  OID       OID private
50///            255            Reserved
51///      256-65279            Available for IANA assignment
52///    65280-65534            Experimental
53///          65535            Reserved
54/// ```
55#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
56#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
57pub enum CertType {
58    /// 0            Reserved
59    Reserved0,
60
61    /// 1  PKIX      X.509 as per PKIX
62    PKIX,
63
64    /// 2  SPKI      SPKI certificate
65    SPKI,
66
67    /// 3  PGP       OpenPGP packet
68    PGP,
69
70    /// 4  IPKIX     The URL of an X.509 data object
71    IPKIX,
72
73    /// 5  ISPKI     The URL of an SPKI certificate
74    ISPKI,
75
76    /// 6  IPGP      The fingerprint and URL of an OpenPGP packet
77    IPGP,
78
79    /// 7  ACPKIX    Attribute Certificate
80    ACPKIX,
81
82    /// 8  IACPKIX   The URL of an Attribute Certificate
83    IACPKIX,
84
85    /// 253  URI       URI private
86    URI,
87
88    /// 254  OID       OID private
89    OID,
90
91    /// 255          Reserved
92    Reserved255,
93
94    /// 9-252, 256-65279            Available for IANA assignment
95    Unassigned(u16),
96
97    /// 65280-65534            Experimental
98    Experimental(u16),
99
100    /// 65535        Reserved
101    Reserved65535,
102}
103
104impl From<u16> for CertType {
105    fn from(cert_type: u16) -> Self {
106        match cert_type {
107            0 => Self::Reserved0,
108            1 => Self::PKIX,
109            2 => Self::SPKI,
110            3 => Self::PGP,
111            4 => Self::IPKIX,
112            5 => Self::ISPKI,
113            6 => Self::IPGP,
114            7 => Self::ACPKIX,
115            8 => Self::IACPKIX,
116            9_u16..=252_u16 => Self::Unassigned(cert_type),
117            253 => Self::URI,
118            254 => Self::OID,
119            255 => Self::Reserved255,
120            256_u16..=65279_u16 => Self::Unassigned(cert_type),
121            65280_u16..=65534_u16 => Self::Experimental(cert_type),
122            65535 => Self::Reserved65535,
123        }
124    }
125}
126
127impl From<CertType> for u16 {
128    fn from(cert_type: CertType) -> Self {
129        match cert_type {
130            CertType::Reserved0 => 0,
131            CertType::PKIX => 1,
132            CertType::SPKI => 2,
133            CertType::PGP => 3,
134            CertType::IPKIX => 4,
135            CertType::ISPKI => 5,
136            CertType::IPGP => 6,
137            CertType::ACPKIX => 7,
138            CertType::IACPKIX => 8,
139            CertType::URI => 253,
140            CertType::OID => 254,
141            CertType::Reserved255 => 255,
142            CertType::Unassigned(cert_type) => cert_type,
143            CertType::Experimental(cert_type) => cert_type,
144            CertType::Reserved65535 => 65535,
145        }
146    }
147}
148
149impl<'r> BinDecodable<'r> for CertType {
150    fn read(decoder: &mut BinDecoder<'r>) -> Result<Self, DecodeError> {
151        let algorithm_id = decoder
152            .read_u16()?
153            .unverified(/*CertType is verified as safe in processing this*/);
154        Ok(Self::from(algorithm_id))
155    }
156}
157
158impl fmt::Display for CertType {
159    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
160        write!(f, "{self:?}")
161    }
162}
163
164/// [RFC 4398, Storing Certificates in DNS, November 1987](https://tools.ietf.org/html/rfc4398#section-2.2)
165///
166/// ```text
167///
168/// [2.2](https://datatracker.ietf.org/doc/html/rfc4398#section-2.2).  Text Representation of CERT RRs
169///
170///    The RDATA portion of a CERT RR has the type field as an unsigned
171///    decimal integer or as a mnemonic symbol as listed in [Section 2.1](https://datatracker.ietf.org/doc/html/rfc4398#section-2.1),
172///    above.
173///
174///    The key tag field is represented as an unsigned decimal integer.
175///
176///    The algorithm field is represented as an unsigned decimal integer or
177///    a mnemonic symbol as listed in [[12](https://datatracker.ietf.org/doc/html/rfc4398#ref-12)].
178///
179/// [12]  Arends, R., Austein, R., Larson, M., Massey, D., and S. Rose,
180/// "Resource Records for the DNS Security Extensions", RFC 4034,
181/// March 2005.
182///
183///
184/// [RFC 4034, Resource Records for the DNS Security Extensions, March 2005][rfc4034]
185/// https://tools.ietf.org/html/rfc4034#appendix-A.1
186///
187/// [A.1](https://datatracker.ietf.org/doc/html/rfc4034#appendix-A.1).  DNSSEC Algorithm Types
188///
189///    The DNSKEY, RRSIG, and DS RRs use an 8-bit number to identify the
190///    security algorithm being used.  These values are stored in the
191///    "Algorithm number" field in the resource record RDATA.
192///
193///    Some algorithms are usable only for zone signing (DNSSEC), some only
194///    for transaction security mechanisms (SIG(0) and TSIG), and some for
195///    both.  Those usable for zone signing may appear in DNSKEY, RRSIG, and
196///    DS RRs.  Those usable for transaction security would be present in
197///    SIG(0) and KEY RRs, as described in [RFC2931].
198///
199///                                 Zone
200///    Value Algorithm [Mnemonic]  Signing  References   Status
201///    ----- -------------------- --------- ----------  ---------
202///      0   reserved
203///      1   RSA/MD5 [RSAMD5]         n      [RFC2537]  NOT RECOMMENDED
204///      2   Diffie-Hellman [DH]      n      [RFC2539]   -
205///      3   DSA/SHA-1 [DSA]          y      [RFC2536]  OPTIONAL
206///      4   Elliptic Curve [ECC]              TBA       -
207///      5   RSA/SHA-1 [RSASHA1]      y      [RFC3110]  MANDATORY
208///    252   Indirect [INDIRECT]      n                  -
209///    253   Private [PRIVATEDNS]     y      see below  OPTIONAL
210///    254   Private [PRIVATEOID]     y      see below  OPTIONAL
211///    255   reserved
212///
213///    6 - 251  Available for assignment by IETF Standards Action.
214///
215/// (RFC Required) Domain Name System Security (DNSSEC) Algorithm Numbers
216/// Created: 2003-11-03, Last Updated: 2024-04-16
217/// https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.txt
218///
219///                                                              Zone
220///     Value  Algorithm [Mnemonic]                            Signing    References
221///     -----  --------------------                           ---------   ----------
222///       6    DSA-NSEC3-SHA1 [DSA-NSEC3-SHA1]                    Y       [RFC5155][proposed standard]
223///       7    RSASHA1-NSEC3-SHA1 [RSASHA1-NSEC3-SHA1]            Y       [RFC5155][proposed standard]
224///       8    RSA/SHA-256 [RSASHA256]                            Y       [RFC5702][proposed standard]
225///       9    reserved
226///      10    RSA/SHA-512 [RSASHA512]                            Y       [RFC5702][proposed standard]
227///      11    reserved
228///      12    GOST R 34.10-2001 [ECC-GOST]                       Y       [RFC5933][proposed standard]
229///      13    ECDSA Curve P-256 with SHA-256 [ECDSAP256SHA256]   Y       [RFC6605][proposed standard]
230///      14    ECDSA Curve P-384 with SHA-384 [ECDSAP384SHA384]   Y       [RFC6605][proposed standard]
231///      15    Ed25519 [ED25519]                                  Y       [RFC8080][proposed standard]
232///      16    Ed448 [ED448]                                      Y       [RFC8080][proposed standard]
233///      17    SM2 signing with SM3 hashing [SM2SM3]              Y       [RFC-cuiling-dnsop-sm2-alg-15][informational]
234///   18-22    Unassigned
235///      23    GOST R 34.10-2012 [ECC-GOST12]                     Y       [RFC9558][informational]
236///  24-122    Unassigned
237/// 123-251    reserved
238/// ```
239#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
240#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
241pub enum Algorithm {
242    /// 0, 9, 11, 123-251, 255   reserved
243    Reserved(u8),
244
245    /// 1   RSA/MD5 ([RFC 2537](https://tools.ietf.org/html/rfc2537))
246    RSAMD5,
247
248    /// 2   Diffie-Hellman ([RFC 2539](https://tools.ietf.org/html/rfc2539))
249    DH,
250
251    /// 3   DSA/SHA-1 ([RFC 2536](https://tools.ietf.org/html/rfc2536))
252    DSA,
253
254    /// 4   Elliptic Curve
255    ECC,
256
257    /// 5   RSA/SHA-1 ([RFC 3110](https://tools.ietf.org/html/rfc3110))
258    RSASHA1,
259
260    /// 252   Indirect
261    INDIRECT,
262
263    /// 253   Private
264    PRIVATEDNS,
265
266    /// 254   Private
267    PRIVATEOID,
268
269    /// 6    DSA-NSEC3-SHA1 ([RFC 5155](https://tools.ietf.org/html/rfc5155))
270    DSANSEC3SHA1,
271
272    /// 7    RSASHA1-NSEC3-SHA1 (RFC5155)
273    RSASHA1NSEC3SHA1,
274
275    /// 8    RSA/SHA-256 ([RFC 5702](https://tools.ietf.org/html/rfc5702))
276    RSASHA256,
277
278    /// 10    RSA/SHA-512 ([RFC 5702](https://tools.ietf.org/html/rfc5702))
279    RSASHA512,
280
281    /// 12    GOST R 34.10-2001 ([RFC 5933](https://tools.ietf.org/html/rfc5933))
282    ECCGOST,
283
284    /// 13    ECDSA Curve P-256 with SHA-256 ([RFC 6605](https://tools.ietf.org/html/rfc6605))
285    ECDSAP256SHA256,
286
287    /// 14    ECDSA Curve P-384 with SHA-384 ([RFC 6605](https://tools.ietf.org/html/rfc6605))
288    ECDSAP384SHA384,
289
290    /// 15    Ed25519 ([RFC 8080](https://tools.ietf.org/html/rfc8080))
291    ED25519,
292
293    /// 16    Ed448 ([RFC 8080](https://tools.ietf.org/html/rfc8080))
294    ED448,
295
296    /// 17    SM2 signing with SM3 hashing (RFC-cuiling-dnsop-sm2-alg-15)
297    SM2SM3,
298
299    /// 23    GOST R 34.10-2012 ([RFC 9558](https://tools.ietf.org/html/rfc9558))
300    ECCGOST12,
301
302    ///   18-22, 24-122    Unassigned
303    Unassigned(u8),
304}
305
306impl From<u8> for Algorithm {
307    fn from(algorithm: u8) -> Self {
308        match algorithm {
309            0 => Self::Reserved(0),
310            1 => Self::RSAMD5,
311            2 => Self::DH,
312            3 => Self::DSA,
313            4 => Self::ECC,
314            5 => Self::RSASHA1,
315            6 => Self::DSANSEC3SHA1,
316            7 => Self::RSASHA1NSEC3SHA1,
317            8 => Self::RSASHA256,
318            9 => Self::Reserved(9),
319            10 => Self::RSASHA512,
320            11 => Self::Reserved(11),
321            12 => Self::ECCGOST,
322            13 => Self::ECDSAP256SHA256,
323            14 => Self::ECDSAP384SHA384,
324            15 => Self::ED25519,
325            16 => Self::ED448,
326            17 => Self::SM2SM3,
327            18..=22 => Self::Unassigned(algorithm),
328            23 => Self::ECCGOST12,
329            24..=122 => Self::Unassigned(algorithm),
330            252 => Self::INDIRECT,
331            253 => Self::PRIVATEDNS,
332            254 => Self::PRIVATEOID,
333            _ => Self::Unassigned(algorithm),
334        }
335    }
336}
337
338impl From<Algorithm> for u8 {
339    fn from(algorithm: Algorithm) -> Self {
340        match algorithm {
341            Algorithm::Reserved(value) if value == 0 => value,
342            Algorithm::RSAMD5 => 1,
343            Algorithm::DH => 2,
344            Algorithm::DSA => 3,
345            Algorithm::ECC => 4,
346            Algorithm::RSASHA1 => 5,
347            Algorithm::DSANSEC3SHA1 => 6,
348            Algorithm::RSASHA1NSEC3SHA1 => 7,
349            Algorithm::RSASHA256 => 8,
350            Algorithm::Reserved(value) if value == 9 => value,
351            Algorithm::RSASHA512 => 10,
352            Algorithm::Reserved(value) if value == 11 => value,
353            Algorithm::ECCGOST => 12,
354            Algorithm::ECDSAP256SHA256 => 13,
355            Algorithm::ECDSAP384SHA384 => 14,
356            Algorithm::ED25519 => 15,
357            Algorithm::ED448 => 16,
358            Algorithm::SM2SM3 => 17,
359            Algorithm::Unassigned(value) if (18..=22).contains(&value) => value,
360            Algorithm::ECCGOST12 => 23,
361            Algorithm::Unassigned(value) if (24..=122).contains(&value) => value,
362            Algorithm::INDIRECT => 252,
363            Algorithm::PRIVATEDNS => 253,
364            Algorithm::PRIVATEOID => 254,
365            Algorithm::Unassigned(value) => value,
366            Algorithm::Reserved(value) => value,
367        }
368    }
369}
370
371impl<'r> BinDecodable<'r> for Algorithm {
372    // https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml
373    fn read(decoder: &mut BinDecoder<'r>) -> Result<Self, DecodeError> {
374        let algorithm_id = decoder
375            .read_u8()?
376            .unverified(/*Algorithm is verified as safe in processing this*/);
377        Ok(Self::from(algorithm_id))
378    }
379}
380
381impl fmt::Display for Algorithm {
382    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
383        write!(f, "{self:?}")
384    }
385}
386
387/// [RFC 4398, Storing Certificates in DNS, November 1987](https://tools.ietf.org/html/rfc4398)
388///
389/// ```text
390///
391/// [2](https://datatracker.ietf.org/doc/html/rfc4398#section-2).  The CERT Resource Record
392///
393///    The CERT resource record (RR) has the structure given below.  Its RR
394///    type code is 37.
395///
396///       1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
397///    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
398///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
399///    |             type              |             key tag           |
400///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
401///    |   algorithm   |                                               /
402///    +---------------+            certificate or CRL                 /
403///    /                                                               /
404///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
405/// ```
406#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
407#[derive(Debug, PartialEq, Eq, Hash, Clone)]
408#[non_exhaustive]
409pub struct CERT {
410    /// The CERT type
411    pub cert_type: CertType,
412
413    /// The CERT key tag
414    pub key_tag: u16,
415
416    /// The CERT algorithm
417    pub algorithm: Algorithm,
418
419    /// The CERT record data
420    ///
421    /// The data stored here is **not** base64-encoded. Use the `cert_base64()` function
422    /// if you need the data in base64-encoded format.
423    pub cert_data: Vec<u8>,
424}
425
426impl CERT {
427    /// Construct a new CERT RData
428    pub const fn new(
429        cert_type: CertType,
430        key_tag: u16,
431        algorithm: Algorithm,
432        cert_data: Vec<u8>,
433    ) -> Self {
434        Self {
435            cert_type,
436            key_tag,
437            algorithm,
438            cert_data,
439        }
440    }
441
442    /// Parse the RData from a set of Tokens
443    pub(crate) fn from_tokens<'i, I: Iterator<Item = &'i str>>(
444        tokens: I,
445    ) -> Result<Self, ParseError> {
446        let mut iter = tokens;
447
448        let token = iter
449            .next()
450            .ok_or(ParseError::Message("CERT cert type field missing"))?;
451        let cert_type = CertType::from(
452            u16::from_str(token)
453                .map_err(|_| ParseError::Message("Invalid digit found in cert_type token"))?,
454        );
455
456        let token = iter
457            .next()
458            .ok_or(ParseError::Message("CERT key tag field missing"))?;
459        let key_tag = u16::from_str(token)
460            .map_err(|_| ParseError::Message("Invalid digit found in key_tag token"))?;
461
462        let token = iter
463            .next()
464            .ok_or(ParseError::Message("CERT algorithm field missing"))?;
465        let algorithm = Algorithm::from(
466            u8::from_str(token)
467                .map_err(|_| ParseError::Message("Invalid digit found in algorithm token"))?,
468        );
469
470        let token = iter
471            .next()
472            .ok_or(ParseError::Message("CERT data missing"))?;
473
474        let cert_data = data_encoding::BASE64
475            .decode(token.as_bytes())
476            .map_err(|_| ParseError::Message("Invalid base64 CERT data"))?;
477
478        Ok(Self::new(cert_type, key_tag, algorithm, cert_data))
479    }
480
481    /// Returns the CERT (Base64)
482    pub fn cert_base64(&self) -> String {
483        data_encoding::BASE64.encode(&self.cert_data).clone()
484    }
485}
486
487impl TryFrom<&[u8]> for CERT {
488    type Error = DecodeError;
489
490    fn try_from(cert_record: &[u8]) -> Result<Self, Self::Error> {
491        let mut decoder = BinDecoder::new(cert_record);
492        let length = Restrict::new(cert_record.len() as u16); // You can use the full length here
493        Self::read_data(&mut decoder, length) // Reuse the read_data method for parsing
494    }
495}
496
497impl BinEncodable for CERT {
498    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
499        let mut encoder = encoder.with_rdata_behavior(RDataEncoding::Other);
500        encoder.emit_u16(self.cert_type.into())?;
501        encoder.emit_u16(self.key_tag)?;
502        encoder.emit_u8(self.algorithm.into())?;
503        encoder.emit_vec(&self.cert_data)?;
504
505        Ok(())
506    }
507}
508
509impl<'r> RecordDataDecodable<'r> for CERT {
510    fn read_data(decoder: &mut BinDecoder<'r>, length: Restrict<u16>) -> Result<Self, DecodeError> {
511        let rdata_length = length.map(|u| u as usize).unverified(/*used only as length safely*/);
512
513        if rdata_length <= 5 {
514            return Err(DecodeError::IncorrectRDataLengthRead {
515                read: rdata_length,
516                len: 6,
517            });
518        }
519
520        let start_idx = decoder.index();
521
522        // let cert_type = decoder.read_u16()?.unverified(/*valid as any u16*/);
523        let cert_type = CertType::read(decoder)?;
524        let key_tag = decoder.read_u16()?.unverified(/*valid as any u16*/);
525        let algorithm = Algorithm::read(decoder)?;
526
527        let cert_len = length
528            .map(|u| u as usize)
529            .checked_sub(decoder.index() - start_idx)
530            .map_err(|len| DecodeError::IncorrectRDataLengthRead { read: decoder.index() - start_idx, len })?
531            .unverified(/*used only as length safely*/);
532
533        let cert_data = decoder.read_vec(cert_len)?.unverified(/*will fail in usage if invalid*/);
534
535        Ok(Self {
536            cert_type,
537            key_tag,
538            algorithm,
539            cert_data,
540        })
541    }
542}
543
544impl RecordData for CERT {
545    fn try_borrow(data: &RData) -> Option<&Self> {
546        match data {
547            RData::CERT(data) => Some(data),
548            _ => None,
549        }
550    }
551
552    fn record_type(&self) -> RecordType {
553        RecordType::CERT
554    }
555
556    fn into_rdata(self) -> RData {
557        RData::CERT(self)
558    }
559}
560
561/// [RFC 4398, Storing Certificates in DNS, November 1987](https://tools.ietf.org/html/rfc4398#section-2.2)
562///
563/// ```text
564///
565/// [2.2](https://datatracker.ietf.org/doc/html/rfc4398#section-2.2).  Text Representation of CERT RRs
566///
567///    The RDATA portion of a CERT RR has the type field as an unsigned
568///    decimal integer or as a mnemonic symbol as listed in [Section 2.1](https://datatracker.ietf.org/doc/html/rfc4398#section-2.1),
569///    above.
570///
571///    The key tag field is represented as an unsigned decimal integer.
572///
573///    The algorithm field is represented as an unsigned decimal integer or
574///    a mnemonic symbol as listed in [[12](https://datatracker.ietf.org/doc/html/rfc4398#ref-12)].
575///
576///    The certificate/CRL portion is represented in base 64 [[16](https://datatracker.ietf.org/doc/html/rfc4398#ref-16)] and may be
577///    divided into any number of white-space-separated substrings, down to
578///    single base-64 digits, which are concatenated to obtain the full
579///    signature.  These substrings can span lines using the standard
580///    parenthesis.
581///
582///    Note that the certificate/CRL portion may have internal sub-fields,
583///    but these do not appear in the master file representation.  For
584///    example, with type 254, there will be an OID size, an OID, and then
585///    the certificate/CRL proper.  However, only a single logical base-64
586///    string will appear in the text representation.
587///
588/// ```
589impl fmt::Display for CERT {
590    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
591        let cert_data = &data_encoding::BASE64.encode(&self.cert_data);
592
593        write!(
594            f,
595            "{cert_type} {key_tag} {algorithm} {cert_data}",
596            cert_type = self.cert_type,
597            key_tag = &self.key_tag,
598            algorithm = self.algorithm,
599            cert_data = &cert_data
600        )?;
601
602        Ok(())
603    }
604}
605
606#[cfg(test)]
607mod tests {
608    #![allow(clippy::dbg_macro, clippy::print_stdout)]
609
610    use super::*;
611
612    #[test]
613    fn test_cert_type() {
614        assert_eq!(CertType::Reserved0, CertType::from(0));
615        assert_eq!(CertType::PKIX, CertType::from(1));
616        assert_eq!(CertType::SPKI, CertType::from(2));
617        assert_eq!(CertType::PGP, CertType::from(3));
618        assert_eq!(CertType::IPKIX, CertType::from(4));
619        assert_eq!(CertType::ISPKI, CertType::from(5));
620        assert_eq!(CertType::IPGP, CertType::from(6));
621        assert_eq!(CertType::ACPKIX, CertType::from(7));
622        assert_eq!(CertType::IACPKIX, CertType::from(8));
623        assert_eq!(CertType::URI, CertType::from(253));
624        assert_eq!(CertType::OID, CertType::from(254));
625        assert_eq!(CertType::Reserved255, CertType::from(255));
626        assert_eq!(CertType::Unassigned(9), CertType::from(9));
627        assert_eq!(CertType::Unassigned(90), CertType::from(90));
628        assert_eq!(CertType::Experimental(65280), CertType::from(65280));
629        assert_eq!(CertType::Experimental(65390), CertType::from(65390));
630        assert_eq!(CertType::Reserved65535, CertType::from(65535));
631
632        let cert_type_iana_9 = CertType::Unassigned(9);
633        let cert_type_iana_90 = CertType::Unassigned(90);
634        let cert_type_experimental_80 = CertType::Experimental(65280);
635        let cert_type_experimental_90 = CertType::Experimental(65290);
636
637        assert_eq!(u16::from(CertType::Reserved0), 0);
638        assert_eq!(u16::from(CertType::PKIX), 1);
639        assert_eq!(u16::from(CertType::SPKI), 2);
640        assert_eq!(u16::from(CertType::PGP), 3);
641        assert_eq!(u16::from(CertType::IPKIX), 4);
642        assert_eq!(u16::from(CertType::ISPKI), 5);
643        assert_eq!(u16::from(CertType::IPGP), 6);
644        assert_eq!(u16::from(CertType::ACPKIX), 7);
645        assert_eq!(u16::from(CertType::IACPKIX), 8);
646        assert_eq!(u16::from(cert_type_iana_9), 9);
647        assert_eq!(u16::from(cert_type_iana_90), 90);
648        assert_eq!(u16::from(CertType::URI), 253);
649        assert_eq!(u16::from(CertType::OID), 254);
650        assert_eq!(u16::from(CertType::Reserved255), 255);
651        assert_eq!(u16::from(cert_type_experimental_80), 65280);
652        assert_eq!(u16::from(cert_type_experimental_90), 65290);
653        assert_eq!(u16::from(CertType::Reserved65535), 65535);
654    }
655
656    #[test]
657    fn test_algorithm() {
658        assert_eq!(Algorithm::Reserved(0), Algorithm::from(0));
659        assert_eq!(Algorithm::DH, Algorithm::from(2));
660        assert_eq!(Algorithm::DSA, Algorithm::from(3));
661        assert_eq!(Algorithm::ECC, Algorithm::from(4));
662        assert_eq!(Algorithm::RSASHA1, Algorithm::from(5));
663        assert_eq!(Algorithm::DSANSEC3SHA1, Algorithm::from(6));
664        assert_eq!(Algorithm::RSASHA1NSEC3SHA1, Algorithm::from(7));
665        assert_eq!(Algorithm::RSASHA256, Algorithm::from(8));
666        assert_eq!(Algorithm::Reserved(9), Algorithm::from(9));
667        assert_eq!(Algorithm::RSASHA512, Algorithm::from(10));
668        assert_eq!(Algorithm::Reserved(11), Algorithm::from(11));
669        assert_eq!(Algorithm::ECCGOST, Algorithm::from(12));
670        assert_eq!(Algorithm::ECDSAP256SHA256, Algorithm::from(13));
671        assert_eq!(Algorithm::ECDSAP384SHA384, Algorithm::from(14));
672        assert_eq!(Algorithm::ED25519, Algorithm::from(15));
673        assert_eq!(Algorithm::ED448, Algorithm::from(16));
674        assert_eq!(Algorithm::SM2SM3, Algorithm::from(17));
675        assert_eq!(Algorithm::Unassigned(18), Algorithm::from(18));
676        assert_eq!(Algorithm::Unassigned(20), Algorithm::from(20));
677        assert_eq!(Algorithm::ECCGOST12, Algorithm::from(23));
678        assert_eq!(Algorithm::INDIRECT, Algorithm::from(252));
679        assert_eq!(Algorithm::PRIVATEDNS, Algorithm::from(253));
680        assert_eq!(Algorithm::PRIVATEOID, Algorithm::from(254));
681
682        let algorithm_reserved_0 = Algorithm::Reserved(0);
683        let algorithm_reserved_9 = Algorithm::Reserved(9);
684
685        assert_eq!(u8::from(algorithm_reserved_0), 0);
686        assert_eq!(u8::from(Algorithm::DH), 2);
687
688        assert_eq!(u8::from(Algorithm::DSA), 3);
689        assert_eq!(u8::from(Algorithm::ECC), 4);
690        assert_eq!(u8::from(Algorithm::RSASHA1), 5);
691        assert_eq!(u8::from(Algorithm::DSANSEC3SHA1), 6);
692        assert_eq!(u8::from(Algorithm::RSASHA1NSEC3SHA1), 7);
693        assert_eq!(u8::from(Algorithm::RSASHA256), 8);
694        assert_eq!(u8::from(Algorithm::Reserved(9)), 9);
695        assert_eq!(u8::from(Algorithm::RSASHA512), 10);
696        assert_eq!(u8::from(Algorithm::Reserved(11)), 11);
697        assert_eq!(u8::from(Algorithm::ECCGOST), 12);
698        assert_eq!(u8::from(Algorithm::ECDSAP256SHA256), 13);
699        assert_eq!(u8::from(Algorithm::ECDSAP384SHA384), 14);
700        assert_eq!(u8::from(Algorithm::ED25519), 15);
701        assert_eq!(u8::from(Algorithm::ED448), 16);
702        assert_eq!(u8::from(Algorithm::SM2SM3), 17);
703        assert_eq!(u8::from(Algorithm::Unassigned(18)), 18);
704        assert_eq!(u8::from(Algorithm::Unassigned(20)), 20);
705        assert_eq!(u8::from(Algorithm::ECCGOST12), 23);
706        assert_eq!(u8::from(Algorithm::INDIRECT), 252);
707        assert_eq!(u8::from(Algorithm::PRIVATEDNS), 253);
708        assert_eq!(u8::from(Algorithm::PRIVATEOID), 254);
709
710        assert_eq!(u8::from(algorithm_reserved_9), 9);
711    }
712
713    #[test]
714    fn test_valid_cert_data_length() {
715        let valid_cert_data = [1, 2, 3, 4, 5, 6]; // At least 6 bytes
716        let result = CERT::try_from(&valid_cert_data[..]);
717        assert!(
718            result.is_ok(),
719            "Expected a valid result with sufficient cert_data length"
720        );
721    }
722
723    #[test]
724    fn test_cert_creation() {
725        // Sample values
726        let cert_type = CertType::PKIX;
727        let key_tag = 12345;
728        let algorithm = Algorithm::RSASHA256; // Replace with an actual variant from Algorithm
729        let cert_data = [1, 2, 3, 4, 5];
730
731        // Create an instance of the CERT struct
732        let cert = CERT {
733            cert_type,
734            key_tag,
735            algorithm,
736            cert_data: cert_data.to_vec(),
737        };
738
739        // Assert that the fields are correctly set
740        assert_eq!(cert.cert_type, cert_type);
741        assert_eq!(cert.key_tag, key_tag);
742        assert_eq!(cert.algorithm, algorithm);
743        assert_eq!(cert.cert_data, cert_data);
744    }
745
746    #[test]
747    fn test_cert_empty_cert_data() {
748        let cert_type = CertType::PKIX;
749        let key_tag = 12345;
750        let algorithm = Algorithm::RSASHA256;
751        let cert_data = Vec::new(); // Empty cert_data
752
753        // Create an instance of the CERT struct
754        let cert = CERT {
755            cert_type,
756            key_tag,
757            algorithm,
758            cert_data,
759        };
760
761        // Assert that cert_data is empty and other fields are correctly set
762        assert_eq!(cert.cert_type, cert_type);
763        assert_eq!(cert.key_tag, key_tag);
764        assert_eq!(cert.algorithm, algorithm);
765        assert!(cert.cert_data.is_empty());
766    }
767
768    #[test]
769    fn test_valid_cert_record() {
770        // Create a mock cert_data with 5 initial bytes + valid Base64 string for the rest
771        let valid_cert_record = [
772            0x00, 0x01, // cert_type: 1 (PKIX)
773            0x30, 0x39, // key_tag: 12345
774            0x08, // algorithm: 8 (e.g., RSASHA256)
775            65, 81, 73, 68, // "AQID" = [1, 2, 3]
776        ];
777
778        let cert = CERT::try_from(&valid_cert_record[..]);
779        assert!(cert.is_ok(), "Expected valid cert_record");
780
781        let cert = cert.unwrap();
782        assert_eq!(cert.cert_type, CertType::PKIX);
783        assert_eq!(cert.key_tag, 12345);
784        assert_eq!(cert.algorithm, Algorithm::RSASHA256); // Assuming this is algorithm 8
785        assert_eq!(cert.cert_data, [65, 81, 73, 68]);
786    }
787
788    #[test]
789    fn test_invalid_cert_record_length() {
790        let invalid_cert_record = [1, 2, 3, 4]; // Less than 5 bytes
791
792        let result = CERT::try_from(&invalid_cert_record[..]);
793        assert!(
794            matches!(
795                result,
796                Err(DecodeError::IncorrectRDataLengthRead { read: 4, len: 6 })
797            ),
798            "Expected error due to invalid cert_record length, got {result:?}"
799        );
800    }
801
802    #[test]
803    fn test_valid_cert_data() {
804        // Base64-encoded dummy certificate data.
805        let tokens = vec!["1", "123", "3", "Q2VydGlmaWNhdGUgZGF0YQ=="].into_iter();
806
807        let result = CERT::from_tokens(tokens);
808
809        assert!(result.is_ok());
810
811        let cert = result.unwrap();
812        assert_eq!(cert.cert_type, CertType::from(1));
813        assert_eq!(cert.key_tag, 123);
814        assert_eq!(cert.algorithm, Algorithm::from(3));
815        assert_eq!(cert.cert_data, b"Certificate data".to_vec()); // Decoded base64 data.
816    }
817
818    #[test]
819    fn test_invalid_base64_data() {
820        // Invalid base64 data (contains invalid characters).
821        let tokens = vec!["1", "123", "3", "Invalid_base64"].into_iter();
822
823        let result = CERT::from_tokens(tokens);
824
825        // Expecting an error for invalid base64 data.
826        assert!(result.is_err());
827        let err = result.unwrap_err();
828        assert_eq!(format!("{err}"), "Invalid base64 CERT data");
829    }
830
831    #[test]
832    fn test_invalid_token_digit() {
833        // Missing cert_type (first token) will try to decode cert leading to invalid digit
834        let tokens = vec!["123", "3", "Q2VydGlmaWNhdGUgZGF0YQ=="].into_iter();
835
836        let result = CERT::from_tokens(tokens);
837
838        // Expecting an error due to missing cert type.
839        assert!(result.is_err());
840        let err = result.unwrap_err();
841        assert_eq!(format!("{err}"), "Invalid digit found in algorithm token");
842    }
843
844    #[test]
845    fn test_missing_cert_data() {
846        // Missing cert_data (last token)
847        let tokens = vec!["1", "123", "3"].into_iter();
848
849        let result = CERT::from_tokens(tokens);
850
851        // Expecting an error due to missing cert data.
852        assert!(result.is_err());
853        let err = result.unwrap_err();
854        assert_eq!(format!("{err}"), "CERT data missing");
855    }
856}