mod dss;
mod dss_read;
mod ess;
mod level;
mod ts_attr;
pub use dss::append_dss;
pub use dss_read::{parse_dss, read_dss};
pub use ess::build_signing_certificate_v2;
pub use level::{classify_pades_level, has_document_timestamp, vri_key};
pub use ts_attr::build_signature_timestamp_attr;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[non_exhaustive]
pub enum PadesLevel {
BB,
BT,
BLt,
BLta,
}
impl PadesLevel {
pub fn code(self) -> i32 {
match self {
PadesLevel::BB => 0,
PadesLevel::BT => 1,
PadesLevel::BLt => 2,
PadesLevel::BLta => 3,
}
}
pub fn from_code(code: i32) -> Option<PadesLevel> {
match code {
0 => Some(PadesLevel::BB),
1 => Some(PadesLevel::BT),
2 => Some(PadesLevel::BLt),
3 => Some(PadesLevel::BLta),
_ => None,
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
#[non_exhaustive]
pub struct RevocationMaterial {
pub certificates: Vec<Vec<u8>>,
pub crls: Vec<Vec<u8>>,
pub ocsp_responses: Vec<Vec<u8>>,
}
impl RevocationMaterial {
pub fn is_empty(&self) -> bool {
self.certificates.is_empty() && self.crls.is_empty() && self.ocsp_responses.is_empty()
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
#[non_exhaustive]
pub struct VriEntry {
pub signature_digest: String,
pub certificates: Vec<Vec<u8>>,
pub crls: Vec<Vec<u8>>,
pub ocsp_responses: Vec<Vec<u8>>,
pub timestamp: Option<String>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
#[non_exhaustive]
pub struct DocumentSecurityStore {
pub certificates: Vec<Vec<u8>>,
pub crls: Vec<Vec<u8>>,
pub ocsp_responses: Vec<Vec<u8>>,
pub vri: Vec<VriEntry>,
}
impl DocumentSecurityStore {
pub fn is_empty(&self) -> bool {
self.certificates.is_empty()
&& self.crls.is_empty()
&& self.ocsp_responses.is_empty()
&& self.vri.is_empty()
}
pub fn vri_for(&self, signature_digest: &str) -> Option<&VriEntry> {
self.vri
.iter()
.find(|e| e.signature_digest == signature_digest)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn level_codes_are_frozen_and_round_trip() {
assert_eq!(PadesLevel::BB.code(), 0);
assert_eq!(PadesLevel::BT.code(), 1);
assert_eq!(PadesLevel::BLt.code(), 2);
assert_eq!(PadesLevel::BLta.code(), 3);
for lvl in [
PadesLevel::BB,
PadesLevel::BT,
PadesLevel::BLt,
PadesLevel::BLta,
] {
assert_eq!(PadesLevel::from_code(lvl.code()), Some(lvl));
}
assert_eq!(PadesLevel::from_code(4), None);
assert_eq!(PadesLevel::from_code(-1), None);
}
#[test]
fn level_ordering_is_superset_chain() {
assert!(PadesLevel::BB < PadesLevel::BT);
assert!(PadesLevel::BT < PadesLevel::BLt);
assert!(PadesLevel::BLt < PadesLevel::BLta);
}
#[test]
fn revocation_material_is_empty() {
assert!(RevocationMaterial::default().is_empty());
let m = RevocationMaterial {
certificates: vec![vec![0x30, 0x82]],
..RevocationMaterial::default()
};
assert!(!m.is_empty());
}
#[test]
fn dss_empty_and_vri_lookup() {
let mut dss = DocumentSecurityStore::default();
assert!(dss.is_empty());
assert!(dss.vri_for("ABCD").is_none());
dss.vri.push(VriEntry {
signature_digest: "ABCD1234".to_string(),
..VriEntry::default()
});
assert!(!dss.is_empty());
assert_eq!(dss.vri_for("ABCD1234").map(|e| e.signature_digest.as_str()), Some("ABCD1234"));
assert!(dss.vri_for("nope").is_none());
}
}