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