1use std::cmp::Ordering;
4
5use prikk_error::{PrikkError, Result};
6
7use crate::{CanonicalEncode, CanonicalWriter, ObjectId, ObjectType, Signature};
8
9#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
11pub struct SignatureEnvelopeIssues {
12 pub malformed_shape: bool,
14 pub duplicate: bool,
16 pub noncanonical_order: bool,
18}
19
20#[derive(Debug, Clone, PartialEq, Eq)]
22pub struct ObjectEnvelope {
23 pub object_type: ObjectType,
25 pub schema_version: u32,
27 pub canonical_payload: Vec<u8>,
29 pub signatures: Vec<Signature>,
31}
32
33impl ObjectEnvelope {
34 #[must_use]
36 pub fn unsigned(
37 object_type: ObjectType,
38 schema_version: u32,
39 canonical_payload: Vec<u8>,
40 ) -> Self {
41 Self {
42 object_type,
43 schema_version,
44 canonical_payload,
45 signatures: Vec::new(),
46 }
47 }
48
49 #[must_use]
51 pub fn object_id(&self) -> ObjectId {
52 ObjectId::from_canonical_payload(
53 self.object_type,
54 self.schema_version,
55 &self.canonical_payload,
56 )
57 }
58
59 pub fn validate(&self) -> Result<()> {
61 if self.schema_version == 0 {
62 return Err(PrikkError::UnsupportedFormatVersion(0));
63 }
64 for signature in &self.signatures {
65 signature.validate()?;
66 }
67 Ok(())
68 }
69
70 pub fn signature_issues(&self) -> Result<SignatureEnvelopeIssues> {
72 self.validate()?;
73 let mut issues = SignatureEnvelopeIssues {
74 malformed_shape: self
75 .signatures
76 .iter()
77 .any(|signature| signature.validate_shape().is_err()),
78 ..SignatureEnvelopeIssues::default()
79 };
80 let mut signatures_by_tuple = self.signatures.iter().collect::<Vec<_>>();
81 signatures_by_tuple.sort_unstable_by(|left, right| left.canonical_cmp(right));
82 issues.duplicate = signatures_by_tuple
83 .windows(2)
84 .any(|pair| matches!(pair, [left, right] if left.canonical_cmp(right).is_eq()));
85 for pair in self.signatures.windows(2) {
86 let [left, right] = pair else {
87 continue;
88 };
89 match left.canonical_cmp(right) {
90 Ordering::Equal => {}
91 Ordering::Greater => issues.noncanonical_order = true,
92 Ordering::Less => {}
93 }
94 }
95 Ok(issues)
96 }
97
98 pub fn validate_strict(&self) -> Result<()> {
100 let issues = self.signature_issues()?;
101 if issues.malformed_shape {
102 return Err(PrikkError::InvalidSignature(
103 "envelope contains a signature with malformed algorithm shape".to_string(),
104 ));
105 }
106 if issues.duplicate {
107 return Err(PrikkError::InvalidSignature(
108 "envelope contains a duplicate signature tuple".to_string(),
109 ));
110 }
111 if issues.noncanonical_order {
112 return Err(PrikkError::InvalidSignature(
113 "envelope signatures are not in canonical order".to_string(),
114 ));
115 }
116 Ok(())
117 }
118
119 pub fn add_signature(&mut self, signature: Signature) -> Result<()> {
121 self.validate_strict()?;
122 signature.validate()?;
123 signature.validate_shape()?;
124 match self
125 .signatures
126 .binary_search_by(|existing| existing.canonical_cmp(&signature))
127 {
128 Ok(_) => Err(PrikkError::InvalidSignature(
129 "envelope contains a duplicate signature tuple".to_string(),
130 )),
131 Err(index) => {
132 self.signatures.insert(index, signature);
133 Ok(())
134 }
135 }
136 }
137}
138
139impl CanonicalEncode for ObjectEnvelope {
140 fn encode_canonical(&self, writer: &mut CanonicalWriter) -> Result<()> {
141 self.validate_strict()?;
142 writer.field_u32(1, self.object_type.code() as u32)?;
143 writer.field_u32(2, self.schema_version)?;
144 writer.field_bytes(3, &self.canonical_payload)?;
145 writer.repeated_record(4, &self.signatures)?;
146 Ok(())
147 }
148}
149
150#[cfg(test)]
151mod tests;