1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
/* Copyright (c) Fortanix, Inc.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

//! PKIMessage type

use bit_vec::BitVec;
use yasna::{ASN1Result, BERDecodable, BERReader, DERWriter, Tag};

use crate::{x509::GenericCertificate, DerWrite};

use super::{body::PkiBody, header::PkiHeader};

derive_sequence! {
    /// The `PKIMessage` type is defined in [RFC 4210 Section 5.1].
    ///
    /// ```text
    /// PKIMessage ::= SEQUENCE {
    ///     header           PKIHeader,
    ///     body             PKIBody,
    ///     protection   [0] PKIProtection OPTIONAL,
    ///     extraCerts   [1] SEQUENCE SIZE (1..MAX) OF CMPCertificate
    ///     OPTIONAL }
    /// ```
    ///
    /// [RFC 4210 Section 5.1]: https://datatracker.ietf.org/doc/html/rfc4210#section-5.1
    ///
    /// Tags are EXPLICIT TAG in default according to [rfc4210#appendix-F](https://datatracker.ietf.org/doc/html/rfc4210#appendix-F).
    PkiMessage<'a> {
        header:      [_] UNTAGGED REQUIRED: PkiHeader<'a>,
        body:        [_] UNTAGGED REQUIRED: PkiBody,
        protection:  [0] EXPLICIT OPTIONAL: Option<PkiProtection>,
        extra_certs: [1] EXPLICIT OPTIONAL: Option<CmpCertificates>,
    }
}

/// The `PKIProtection` type is defined in [RFC 4210 Section 5.1.3].
///
/// ```text
///  PKIProtection ::= BIT STRING
/// ```
///
/// [RFC 4210 Section 5.1.3]: https://www.rfc-editor.org/rfc/rfc4210#section-5.1.3
pub type PkiProtection = BitVec;

/// The `CMPCertificate` type is defined in [RFC 4210 Appendix F]
///
/// ```text
///  CMPCertificate ::= CHOICE { x509v3PKCert Certificate, ... }
/// ```
///
/// [RFC 4210 Appendix F]: https://www.rfc-editor.org/rfc/rfc4210#appendix-F
pub type CmpCertificate = GenericCertificate;

derive_sequence_of!{
    /// Represents: SEQUENCE SIZE (1..MAX) OF CMPCertificate
    CmpCertificate => CmpCertificates
}

derive_sequence! {
    /// The `ProtectedPart` type is defined in [RFC 4210 Section 5.1.3].
    ///
    /// ```text
    /// ProtectedPart ::= SEQUENCE {
    ///     header    PKIHeader,
    ///     body      PKIBody }
    /// ```
    ///
    /// [RFC 4210 Section 5.1.3]: https://www.rfc-editor.org/rfc/rfc4210#section-5.1.3
    ProtectedPart<'a> {
        header:      PkiHeader<'a>,
        body:        PkiBody,
    }
}


#[cfg(test)]
mod test {
    use crate::{FromDer, ToDer};
    use b64_ct::{FromBase64, ToBase64, STANDARD};

    use super::*;

    #[test]
    fn test_ftx_pki_msg_decode_encode() {
        let pki_msg_base64 = include_str!("../../tests/data/ftx_test_pki_msg.b64");
        check_pki_msg_decode_encode(pki_msg_base64);
    }

    /// Test encode/decode PKIMessage generated from openssl.
    /// Test data is generated from following command using openssl version 3.X:
    /// ```bash
    /// openssl genrsa 2048 > subjectkey.pem
    /// openssl req -x509 -days 3650 -new -newkey rsa:2048 -keyout senderkey.pem -subj /CN=sender -out sendercert.pem -nodes
    /// openssl cmp -cmd cr -reqout cr.der -newkey subjectkey.pem -subject "/CN=subject" -certout /dev/null -rspin /dev/null -recipient "/CN=recipient" -key senderkey.pem -cert sendercert.pem -extracerts sendercert.pem
    /// base64 < cr.der
    /// ```
    #[test]
    fn test_openssl_pki_msg_decode_encode() {
        let pki_msg_base64 = include_str!("../../tests/data/openssl_pki_msg.b64");
        check_pki_msg_decode_encode(pki_msg_base64);
    }

    fn check_pki_msg_decode_encode(pki_msg_base64: &str) {
        let pki_msg_data = pki_msg_base64.trim().from_base64().expect("base64 decode test pki msg");
        // decode from source
        let pki_msg = PkiMessage::from_der(&pki_msg_data).expect("DER decode test pki msg");
        // re-encode
        let pki_msg_back_to_der = pki_msg.to_der();
        // re-encoded DER should be equal to source
        assert!(
            pki_msg_data == pki_msg_back_to_der,
            "{}",
            pki_msg_back_to_der.to_base64(STANDARD)
        );
        // re-decoded object should be equal to previous decoded object
        let pki_msg2 = PkiMessage::from_der(&pki_msg_back_to_der).expect("DER decode re-encoded pki msg");
        assert_eq!(pki_msg, pki_msg2);
    }
}