Skip to main content

confium_pki/cms/
der_encode.rs

1//! Real DER encoding for CMS structures (RFC 5652).
2//!
3//! Produces standard ASN.1 DER bytes verifiable by OpenSSL and other
4//! standards-compliant tools.
5//!
6//! DER encoding rules (X.690):
7//! - Tag (1 byte for common types)
8//! - Length (short form < 128, or long form for larger)
9//! - Value (TLV)
10//!
11//! This module implements the encoding by hand using `der` crate's
12//! primitive types. For complex structures, hand-rolling gives more
13//! control than `der::derive::Derive` for our semantic types.
14
15use crate::cms::signed_data::{
16    AlgorithmIdentifier, EncapContentInfo, SignedData, SignerIdentifier, SignerInfo,
17};
18
19/// Encode a `SignedData` into DER bytes.
20///
21/// Produces the outer `ContentInfo` wrapper per RFC 5652 §3:
22/// ```text
23/// ContentInfo ::= SEQUENCE {
24///     contentType OBJECT IDENTIFIER,  -- 1.2.840.113549.1.7.2 (signedData)
25///     content [0] EXPLICIT ANY DEFINED BY contentType }
26/// ```
27pub fn encode_signed_data_der(signed_data: &SignedData) -> Result<Vec<u8>, DerError> {
28    // Encode the inner SignedData first to get its length.
29    let inner = encode_signed_data_inner(signed_data)?;
30
31    // Wrap in ContentInfo: SEQUENCE { OID, [0] EXPLICIT inner }
32    let content_type_oid_bytes = oid_to_der(&[1, 2, 840, 113549, 1, 7, 2]);
33
34    let mut explicit_content = Vec::new();
35    explicit_content.push(0xA0); // [0] EXPLICIT context tag
36    explicit_content.extend_from_slice(&encode_length(inner.len()));
37    explicit_content.extend_from_slice(&inner);
38
39    let mut seq_body = Vec::new();
40    seq_body.extend_from_slice(&content_type_oid_bytes);
41    seq_body.extend_from_slice(&explicit_content);
42
43    let mut out = Vec::new();
44    out.push(0x30); // SEQUENCE
45    out.extend_from_slice(&encode_length(seq_body.len()));
46    out.extend_from_slice(&seq_body);
47    Ok(out)
48}
49
50fn encode_signed_data_inner(sd: &SignedData) -> Result<Vec<u8>, DerError> {
51    // SignedData ::= SEQUENCE {
52    //   version INTEGER,
53    //   digestAlgorithms SET OF AlgorithmIdentifier,
54    //   encapContentInfo EncapContentInfo,
55    //   certificates [0] IMPLICIT CertificateSet OPTIONAL,
56    //   signerInfos SET OF SignerInfo
57    // }
58    let mut body = Vec::new();
59    body.extend_from_slice(&integer_to_der(sd.version as i64));
60
61    let mut digest_algs = Vec::new();
62    for alg in &sd.digest_algorithms {
63        digest_algs.extend_from_slice(&encode_algorithm_identifier(alg));
64    }
65    body.extend_from_slice(&wrap_der(0x31, &digest_algs)); // SET OF
66
67    body.extend_from_slice(&encode_encap_content_info(&sd.encap_content_info)?);
68
69    // certificates [0] IMPLICIT — only if non-empty
70    if !sd.certificates.is_empty() {
71        let mut certs_body = Vec::new();
72        for cert in &sd.certificates {
73            certs_body.extend_from_slice(cert); // already DER
74        }
75        // [0] IMPLICIT context tag = 0xA0 (constructed)
76        body.push(0xA0);
77        body.extend_from_slice(&encode_length(certs_body.len()));
78        body.extend_from_slice(&certs_body);
79    }
80
81    let mut signer_infos = Vec::new();
82    for si in &sd.signer_infos {
83        signer_infos.extend_from_slice(&encode_signer_info(si)?);
84    }
85    body.extend_from_slice(&wrap_der(0x31, &signer_infos)); // SET OF
86
87    Ok(wrap_der(0x30, &body)) // SEQUENCE
88}
89
90fn encode_encap_content_info(eci: &EncapContentInfo) -> Result<Vec<u8>, DerError> {
91    let oid_bytes = oid_from_string(&eci.content_type)?;
92
93    let mut body = Vec::new();
94    body.extend_from_slice(&oid_bytes);
95
96    if let Some(content) = &eci.content {
97        // [0] EXPLICIT OCTET STRING
98        let octet = wrap_der(0x04, content);
99        body.push(0xA0);
100        body.extend_from_slice(&encode_length(octet.len()));
101        body.extend_from_slice(&octet);
102    }
103
104    Ok(wrap_der(0x30, &body))
105}
106
107fn encode_algorithm_identifier(alg: &AlgorithmIdentifier) -> Vec<u8> {
108    let oid_bytes = match oid_from_string(&alg.oid) {
109        Ok(b) => b,
110        Err(_) => return Vec::new(),
111    };
112    let mut body = oid_bytes;
113    if let Some(params) = &alg.parameters {
114        body.extend_from_slice(params);
115    } else {
116        // NULL parameters
117        body.extend_from_slice(&[0x05, 0x00]);
118    }
119    wrap_der(0x30, &body)
120}
121
122fn encode_signer_info(si: &SignerInfo) -> Result<Vec<u8>, DerError> {
123    let mut body = Vec::new();
124    body.extend_from_slice(&integer_to_der(si.version as i64));
125
126    // sid: SignerIdentifier
127    match &si.sid {
128        SignerIdentifier::IssuerAndSerialNumber {
129            issuer_der,
130            serial_number,
131        } => {
132            // SEQUENCE { Name, CertificateSerialNumber }
133            let mut seq = Vec::new();
134            seq.extend_from_slice(issuer_der);
135            seq.extend_from_slice(&integer_bytes_to_der(serial_number));
136            body.extend_from_slice(&wrap_der(0x30, &seq));
137        }
138        SignerIdentifier::SubjectKeyIdentifier { key_identifier } => {
139            // [0] IMPLICIT OCTET STRING
140            let octet = wrap_der(0x04, key_identifier);
141            body.push(0x80);
142            body.extend_from_slice(&encode_length(octet.len()));
143            body.extend_from_slice(&octet);
144        }
145    }
146
147    body.extend_from_slice(&encode_algorithm_identifier(&si.digest_algorithm));
148
149    // signedAttrs [0] IMPLICIT SET OF Attribute — optional, skip if empty
150    // signatureAlgorithm
151    body.extend_from_slice(&encode_algorithm_identifier(&si.signature_algorithm));
152
153    // signature OCTET STRING
154    body.extend_from_slice(&wrap_der(0x04, &si.signature));
155
156    // unsignedAttrs [1] IMPLICIT SET OF Attribute — optional, skip if empty
157
158    Ok(wrap_der(0x30, &body))
159}
160
161/// Errors during DER encoding.
162#[derive(Debug, thiserror::Error)]
163pub enum DerError {
164    /// Invalid OID format.
165    #[error("invalid OID: {0}")]
166    InvalidOid(String),
167    /// Value too large to encode.
168    #[error("value too large: {0}")]
169    TooLarge(String),
170}
171
172fn oid_to_der(arcs: &[u64]) -> Vec<u8> {
173    // First byte: 40 * arc[0] + arc[1]
174    let first = (40 * arcs.first().copied().unwrap_or(0) + arcs.get(1).copied().unwrap_or(0)) as u8;
175    let mut out = vec![first];
176    for arc in arcs.iter().skip(2) {
177        out.extend_from_slice(&encode_base128(*arc));
178    }
179    // Wrap as OID TLV: 0x06 <length> <value>
180    wrap_der(0x06, &out)
181}
182
183fn oid_from_string(s: &str) -> Result<Vec<u8>, DerError> {
184    let arcs: Vec<u64> = s
185        .split('.')
186        .map(|a| a.parse::<u64>().map_err(|_| DerError::InvalidOid(s.into())))
187        .collect::<Result<Vec<_>, _>>()?;
188    if arcs.len() < 2 {
189        return Err(DerError::InvalidOid("OID must have >= 2 arcs".into()));
190    }
191    Ok(oid_to_der(&arcs))
192}
193
194fn encode_base128(n: u64) -> Vec<u8> {
195    let mut bytes = Vec::new();
196    let mut n = n;
197    loop {
198        bytes.insert(0, (n & 0x7F) as u8);
199        n >>= 7;
200        if n == 0 {
201            break;
202        }
203    }
204    // Set high bit on all but last
205    let last = bytes.len() - 1;
206    for b in &mut bytes[..last] {
207        *b |= 0x80;
208    }
209    bytes
210}
211
212fn integer_to_der(n: i64) -> Vec<u8> {
213    if (0..=127).contains(&n) {
214        return vec![0x02, 0x01, n as u8];
215    }
216    let bytes = n.to_be_bytes();
217    // Strip leading zero bytes (for positive) but keep sign bit correct
218    let mut start = 0;
219    while start < bytes.len() - 1 && bytes[start] == 0 && (bytes[start + 1] & 0x80) == 0 {
220        start += 1;
221    }
222    wrap_der(0x02, &bytes[start..])
223}
224
225fn integer_bytes_to_der(bytes: &[u8]) -> Vec<u8> {
226    // Treat as unsigned, but ensure leading 0 if high bit set
227    let mut value = bytes.to_vec();
228    if value.first().map(|b| b & 0x80 != 0).unwrap_or(false) {
229        value.insert(0, 0);
230    }
231    wrap_der(0x02, &value)
232}
233
234fn encode_length(len: usize) -> Vec<u8> {
235    if len < 128 {
236        return vec![len as u8];
237    }
238    let mut bytes = Vec::new();
239    let mut l = len;
240    while l > 0 {
241        bytes.insert(0, (l & 0xFF) as u8);
242        l >>= 8;
243    }
244    let mut out = vec![0x80 | bytes.len() as u8];
245    out.extend_from_slice(&bytes);
246    out
247}
248
249fn wrap_der(tag: u8, body: &[u8]) -> Vec<u8> {
250    let mut out = vec![tag];
251    out.extend_from_slice(&encode_length(body.len()));
252    out.extend_from_slice(body);
253    out
254}
255
256#[cfg(test)]
257mod tests {
258    use super::*;
259    use crate::cms::signed_data::{EncapContentInfo, SignerIdentifier, SignerInfo};
260
261    #[test]
262    fn encode_length_short_form() {
263        assert_eq!(encode_length(5), vec![5]);
264        assert_eq!(encode_length(127), vec![127]);
265    }
266
267    #[test]
268    fn encode_length_long_form() {
269        assert_eq!(encode_length(128), vec![0x81, 0x80]);
270        assert_eq!(encode_length(256), vec![0x82, 0x01, 0x00]);
271    }
272
273    #[test]
274    fn integer_short() {
275        assert_eq!(integer_to_der(0), vec![0x02, 0x01, 0x00]);
276        assert_eq!(integer_to_der(42), vec![0x02, 0x01, 0x2A]);
277        assert_eq!(integer_to_der(127), vec![0x02, 0x01, 0x7F]);
278    }
279
280    #[test]
281    fn oid_sha256() {
282        // 2.16.840.1.101.3.4.2.1 (SHA-256)
283        let bytes = oid_from_string("2.16.840.1.101.3.4.2.1").unwrap();
284        // SHA-256 OID DER: 06 09 60 86 48 01 65 03 04 02 01
285        assert_eq!(bytes[0], 0x06);
286        assert_eq!(bytes[1], 0x09);
287        assert_eq!(bytes[2], 96); // 40*2 + 16
288        assert_eq!(bytes[3], 0x86);
289        assert_eq!(bytes[4], 0x48);
290    }
291
292    #[test]
293    fn oid_invalid_arcs() {
294        assert!(oid_from_string("not-a-number").is_err());
295        assert!(oid_from_string("1").is_err());
296    }
297
298    #[test]
299    fn base128_single_byte() {
300        assert_eq!(encode_base128(0), vec![0]);
301        assert_eq!(encode_base128(127), vec![127]);
302    }
303
304    #[test]
305    fn base128_multi_byte() {
306        // 840 = 6 * 128 + 72 → [0x86, 0x48]
307        assert_eq!(encode_base128(840), vec![0x86, 0x48]);
308    }
309
310    #[test]
311    fn encode_signed_data_minimal() {
312        let sd = SignedData {
313            version: 1,
314            digest_algorithms: vec![AlgorithmIdentifier {
315                oid: "2.16.840.1.101.3.4.2.1".into(),
316                parameters: None,
317            }],
318            encap_content_info: EncapContentInfo {
319                content_type: "1.2.840.113549.1.7.1".into(),
320                content: None,
321            },
322            certificates: vec![],
323            signer_infos: vec![SignerInfo {
324                version: 1,
325                sid: SignerIdentifier::SubjectKeyIdentifier {
326                    key_identifier: vec![0xAA; 20],
327                },
328                digest_algorithm: AlgorithmIdentifier {
329                    oid: "2.16.840.1.101.3.4.2.1".into(),
330                    parameters: None,
331                },
332                signed_attrs: vec![],
333                signature_algorithm: AlgorithmIdentifier {
334                    oid: "1.2.840.113549.1.1.11".into(),
335                    parameters: None,
336                },
337                signature: vec![0u8; 256],
338                unsigned_attrs: vec![],
339            }],
340        };
341        let der = encode_signed_data_der(&sd).unwrap();
342        assert_eq!(der[0], 0x30); // outer SEQUENCE
343        // Sanity: bytes should be reasonably long
344        assert!(der.len() > 50);
345    }
346}