Skip to main content

confium_pki/cms/
signed_data.rs

1//! CMS SignedData structure (RFC 5652).
2//!
3//! Provides idiomatic Rust types for CMS SignedData. Wire format is DER
4//! (handled by the consumer via `der` crate when serializing for real
5//! PKCS#7 compatibility). This crate provides the semantic model and
6//! a simplified JSON serialization for testing.
7
8use chrono::{DateTime, Utc};
9use serde::{Deserialize, Serialize};
10
11/// SignedData structure as defined in RFC 5652 §5.1.
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct SignedData {
14    /// CMS version (typically 1 for typical SignedData).
15    pub version: u32,
16    /// Digest algorithms used by signerInfos.
17    pub digest_algorithms: Vec<AlgorithmIdentifier>,
18    /// The encapsulated content (the payload being signed).
19    pub encap_content_info: EncapContentInfo,
20    /// Certificates (X.509) associated with the signers.
21    pub certificates: Vec<Vec<u8>>,
22    /// Signer info entries — one per signer.
23    pub signer_infos: Vec<SignerInfo>,
24}
25
26/// Encapsulated content (the payload).
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct EncapContentInfo {
29    /// ContentType OID (e.g., "1.2.840.113549.1.7.1" for data).
30    pub content_type: String,
31    /// Optional content (absent for detached signatures).
32    #[serde(default)]
33    pub content: Option<Vec<u8>>,
34}
35
36/// Algorithm identifier (OID + optional parameters).
37#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct AlgorithmIdentifier {
39    /// Algorithm OID.
40    pub oid: String,
41    /// Optional parameters (raw bytes).
42    #[serde(default)]
43    pub parameters: Option<Vec<u8>>,
44}
45
46/// Signer information per RFC 5652 §5.3.
47#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct SignerInfo {
49    /// CMS version (typically 1).
50    pub version: u32,
51    /// Signer identifier (typically issuerAndSerialNumber or subjectKeyIdentifier).
52    pub sid: SignerIdentifier,
53    /// Digest algorithm used.
54    pub digest_algorithm: AlgorithmIdentifier,
55    /// Signed attributes (optional).
56    #[serde(default)]
57    pub signed_attrs: Vec<Attribute>,
58    /// Signature algorithm.
59    pub signature_algorithm: AlgorithmIdentifier,
60    /// The signature bytes.
61    pub signature: Vec<u8>,
62    /// Unsigned attributes (optional).
63    #[serde(default)]
64    pub unsigned_attrs: Vec<Attribute>,
65}
66
67/// Signer identifier variants.
68#[derive(Debug, Clone, Serialize, Deserialize)]
69#[serde(tag = "kind", rename_all = "snake_case")]
70pub enum SignerIdentifier {
71    /// Issuer name + serial number.
72    IssuerAndSerialNumber {
73        /// Issuer name (DER-encoded).
74        issuer_der: Vec<u8>,
75        /// Certificate serial number.
76        serial_number: Vec<u8>,
77    },
78    /// Subject key identifier.
79    SubjectKeyIdentifier {
80        /// Key identifier bytes.
81        key_identifier: Vec<u8>,
82    },
83}
84
85/// CMS attribute (OID + values).
86#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct Attribute {
88    /// Attribute OID.
89    pub oid: String,
90    /// Attribute values (SET OF ANY).
91    pub values: Vec<Vec<u8>>,
92}
93
94impl SignedData {
95    /// Construct a new SignedData with the given encapsulated content.
96    pub fn new(content_type: impl Into<String>, content: Option<Vec<u8>>) -> Self {
97        Self {
98            version: 1,
99            digest_algorithms: Vec::new(),
100            encap_content_info: EncapContentInfo {
101                content_type: content_type.into(),
102                content,
103            },
104            certificates: Vec::new(),
105            signer_infos: Vec::new(),
106        }
107    }
108
109    /// Add a signer.
110    pub fn add_signer(&mut self, signer_info: SignerInfo) {
111        // Add the digest algorithm to digest_algorithms if not present.
112        let oid = &signer_info.digest_algorithm.oid;
113        if !self.digest_algorithms.iter().any(|a| &a.oid == oid) {
114            self.digest_algorithms
115                .push(signer_info.digest_algorithm.clone());
116        }
117        self.signer_infos.push(signer_info);
118    }
119
120    /// Add a certificate (DER bytes).
121    pub fn add_certificate(&mut self, cert_der: Vec<u8>) {
122        self.certificates.push(cert_der);
123    }
124
125    /// Number of signers.
126    pub fn signer_count(&self) -> usize {
127        self.signer_infos.len()
128    }
129
130    /// When was this signed? (uses the first signer's signing time if available)
131    pub fn signing_time(&self) -> Option<DateTime<Utc>> {
132        self.signer_infos.first().and_then(|s| {
133            s.signed_attrs.iter().find_map(|a| {
134                if a.oid == "1.2.840.113549.1.9.5" {
135                    // signingTime attribute — values[0] is UTCTime/GeneralizedTime DER
136                    // For simplicity, return None; real impl would parse.
137                    None
138                } else {
139                    None
140                }
141            })
142        })
143    }
144}
145
146#[cfg(test)]
147mod tests {
148    use super::*;
149
150    #[test]
151    fn signed_data_construction() {
152        let mut sd = SignedData::new("1.2.840.113549.1.7.1", Some(b"hello".to_vec()));
153        let signer = SignerInfo {
154            version: 1,
155            sid: SignerIdentifier::SubjectKeyIdentifier {
156                key_identifier: vec![1, 2, 3],
157            },
158            digest_algorithm: AlgorithmIdentifier {
159                oid: "2.16.840.1.101.3.4.2.1".into(), // SHA-256
160                parameters: None,
161            },
162            signed_attrs: Vec::new(),
163            signature_algorithm: AlgorithmIdentifier {
164                oid: "1.2.840.113549.1.1.11".into(), // sha256WithRSAEncryption
165                parameters: None,
166            },
167            signature: vec![0u8; 256],
168            unsigned_attrs: Vec::new(),
169        };
170        sd.add_signer(signer);
171        sd.add_certificate(vec![0u8; 100]);
172        assert_eq!(sd.signer_count(), 1);
173        assert_eq!(sd.digest_algorithms.len(), 1);
174        assert_eq!(sd.certificates.len(), 1);
175    }
176}