use crate::cms::signed_data::{
AlgorithmIdentifier, EncapContentInfo, SignedData, SignerIdentifier, SignerInfo,
};
pub fn encode_signed_data_der(signed_data: &SignedData) -> Result<Vec<u8>, DerError> {
let inner = encode_signed_data_inner(signed_data)?;
let content_type_oid_bytes = oid_to_der(&[1, 2, 840, 113549, 1, 7, 2]);
let mut explicit_content = Vec::new();
explicit_content.push(0xA0); explicit_content.extend_from_slice(&encode_length(inner.len()));
explicit_content.extend_from_slice(&inner);
let mut seq_body = Vec::new();
seq_body.extend_from_slice(&content_type_oid_bytes);
seq_body.extend_from_slice(&explicit_content);
let mut out = Vec::new();
out.push(0x30); out.extend_from_slice(&encode_length(seq_body.len()));
out.extend_from_slice(&seq_body);
Ok(out)
}
fn encode_signed_data_inner(sd: &SignedData) -> Result<Vec<u8>, DerError> {
let mut body = Vec::new();
body.extend_from_slice(&integer_to_der(sd.version as i64));
let mut digest_algs = Vec::new();
for alg in &sd.digest_algorithms {
digest_algs.extend_from_slice(&encode_algorithm_identifier(alg));
}
body.extend_from_slice(&wrap_der(0x31, &digest_algs));
body.extend_from_slice(&encode_encap_content_info(&sd.encap_content_info)?);
if !sd.certificates.is_empty() {
let mut certs_body = Vec::new();
for cert in &sd.certificates {
certs_body.extend_from_slice(cert); }
body.push(0xA0);
body.extend_from_slice(&encode_length(certs_body.len()));
body.extend_from_slice(&certs_body);
}
let mut signer_infos = Vec::new();
for si in &sd.signer_infos {
signer_infos.extend_from_slice(&encode_signer_info(si)?);
}
body.extend_from_slice(&wrap_der(0x31, &signer_infos));
Ok(wrap_der(0x30, &body)) }
fn encode_encap_content_info(eci: &EncapContentInfo) -> Result<Vec<u8>, DerError> {
let oid_bytes = oid_from_string(&eci.content_type)?;
let mut body = Vec::new();
body.extend_from_slice(&oid_bytes);
if let Some(content) = &eci.content {
let octet = wrap_der(0x04, content);
body.push(0xA0);
body.extend_from_slice(&encode_length(octet.len()));
body.extend_from_slice(&octet);
}
Ok(wrap_der(0x30, &body))
}
fn encode_algorithm_identifier(alg: &AlgorithmIdentifier) -> Vec<u8> {
let oid_bytes = match oid_from_string(&alg.oid) {
Ok(b) => b,
Err(_) => return Vec::new(),
};
let mut body = oid_bytes;
if let Some(params) = &alg.parameters {
body.extend_from_slice(params);
} else {
body.extend_from_slice(&[0x05, 0x00]);
}
wrap_der(0x30, &body)
}
fn encode_signer_info(si: &SignerInfo) -> Result<Vec<u8>, DerError> {
let mut body = Vec::new();
body.extend_from_slice(&integer_to_der(si.version as i64));
match &si.sid {
SignerIdentifier::IssuerAndSerialNumber {
issuer_der,
serial_number,
} => {
let mut seq = Vec::new();
seq.extend_from_slice(issuer_der);
seq.extend_from_slice(&integer_bytes_to_der(serial_number));
body.extend_from_slice(&wrap_der(0x30, &seq));
}
SignerIdentifier::SubjectKeyIdentifier { key_identifier } => {
let octet = wrap_der(0x04, key_identifier);
body.push(0x80);
body.extend_from_slice(&encode_length(octet.len()));
body.extend_from_slice(&octet);
}
}
body.extend_from_slice(&encode_algorithm_identifier(&si.digest_algorithm));
body.extend_from_slice(&encode_algorithm_identifier(&si.signature_algorithm));
body.extend_from_slice(&wrap_der(0x04, &si.signature));
Ok(wrap_der(0x30, &body))
}
#[derive(Debug, thiserror::Error)]
pub enum DerError {
#[error("invalid OID: {0}")]
InvalidOid(String),
#[error("value too large: {0}")]
TooLarge(String),
}
fn oid_to_der(arcs: &[u64]) -> Vec<u8> {
let first = (40 * arcs.first().copied().unwrap_or(0) + arcs.get(1).copied().unwrap_or(0)) as u8;
let mut out = vec![first];
for arc in arcs.iter().skip(2) {
out.extend_from_slice(&encode_base128(*arc));
}
wrap_der(0x06, &out)
}
fn oid_from_string(s: &str) -> Result<Vec<u8>, DerError> {
let arcs: Vec<u64> = s
.split('.')
.map(|a| a.parse::<u64>().map_err(|_| DerError::InvalidOid(s.into())))
.collect::<Result<Vec<_>, _>>()?;
if arcs.len() < 2 {
return Err(DerError::InvalidOid("OID must have >= 2 arcs".into()));
}
Ok(oid_to_der(&arcs))
}
fn encode_base128(n: u64) -> Vec<u8> {
let mut bytes = Vec::new();
let mut n = n;
loop {
bytes.insert(0, (n & 0x7F) as u8);
n >>= 7;
if n == 0 {
break;
}
}
let last = bytes.len() - 1;
for b in &mut bytes[..last] {
*b |= 0x80;
}
bytes
}
fn integer_to_der(n: i64) -> Vec<u8> {
if (0..=127).contains(&n) {
return vec![0x02, 0x01, n as u8];
}
let bytes = n.to_be_bytes();
let mut start = 0;
while start < bytes.len() - 1 && bytes[start] == 0 && (bytes[start + 1] & 0x80) == 0 {
start += 1;
}
wrap_der(0x02, &bytes[start..])
}
fn integer_bytes_to_der(bytes: &[u8]) -> Vec<u8> {
let mut value = bytes.to_vec();
if value.first().map(|b| b & 0x80 != 0).unwrap_or(false) {
value.insert(0, 0);
}
wrap_der(0x02, &value)
}
fn encode_length(len: usize) -> Vec<u8> {
if len < 128 {
return vec![len as u8];
}
let mut bytes = Vec::new();
let mut l = len;
while l > 0 {
bytes.insert(0, (l & 0xFF) as u8);
l >>= 8;
}
let mut out = vec![0x80 | bytes.len() as u8];
out.extend_from_slice(&bytes);
out
}
fn wrap_der(tag: u8, body: &[u8]) -> Vec<u8> {
let mut out = vec![tag];
out.extend_from_slice(&encode_length(body.len()));
out.extend_from_slice(body);
out
}
#[cfg(test)]
mod tests {
use super::*;
use crate::cms::signed_data::{EncapContentInfo, SignerIdentifier, SignerInfo};
#[test]
fn encode_length_short_form() {
assert_eq!(encode_length(5), vec![5]);
assert_eq!(encode_length(127), vec![127]);
}
#[test]
fn encode_length_long_form() {
assert_eq!(encode_length(128), vec![0x81, 0x80]);
assert_eq!(encode_length(256), vec![0x82, 0x01, 0x00]);
}
#[test]
fn integer_short() {
assert_eq!(integer_to_der(0), vec![0x02, 0x01, 0x00]);
assert_eq!(integer_to_der(42), vec![0x02, 0x01, 0x2A]);
assert_eq!(integer_to_der(127), vec![0x02, 0x01, 0x7F]);
}
#[test]
fn oid_sha256() {
let bytes = oid_from_string("2.16.840.1.101.3.4.2.1").unwrap();
assert_eq!(bytes[0], 0x06);
assert_eq!(bytes[1], 0x09);
assert_eq!(bytes[2], 96); assert_eq!(bytes[3], 0x86);
assert_eq!(bytes[4], 0x48);
}
#[test]
fn oid_invalid_arcs() {
assert!(oid_from_string("not-a-number").is_err());
assert!(oid_from_string("1").is_err());
}
#[test]
fn base128_single_byte() {
assert_eq!(encode_base128(0), vec![0]);
assert_eq!(encode_base128(127), vec![127]);
}
#[test]
fn base128_multi_byte() {
assert_eq!(encode_base128(840), vec![0x86, 0x48]);
}
#[test]
fn encode_signed_data_minimal() {
let sd = SignedData {
version: 1,
digest_algorithms: vec![AlgorithmIdentifier {
oid: "2.16.840.1.101.3.4.2.1".into(),
parameters: None,
}],
encap_content_info: EncapContentInfo {
content_type: "1.2.840.113549.1.7.1".into(),
content: None,
},
certificates: vec![],
signer_infos: vec![SignerInfo {
version: 1,
sid: SignerIdentifier::SubjectKeyIdentifier {
key_identifier: vec![0xAA; 20],
},
digest_algorithm: AlgorithmIdentifier {
oid: "2.16.840.1.101.3.4.2.1".into(),
parameters: None,
},
signed_attrs: vec![],
signature_algorithm: AlgorithmIdentifier {
oid: "1.2.840.113549.1.1.11".into(),
parameters: None,
},
signature: vec![0u8; 256],
unsigned_attrs: vec![],
}],
};
let der = encode_signed_data_der(&sd).unwrap();
assert_eq!(der[0], 0x30); assert!(der.len() > 50);
}
}