1use std::fmt;
10
11use sha2::{Digest, Sha384};
12
13use crate::cbor::{self, Value};
14use crate::node_key::{carried_key_well_formed, verify, KeyError, NodeKey};
15use crate::profile::Profile;
16
17#[derive(Debug, Clone, PartialEq, Eq)]
20pub enum ObjectError {
21 Malformed,
25 SignatureInvalid,
28 AlgMismatch,
30 FieldKeyNotText,
32 DuplicateField(String),
34 Key(KeyError),
36}
37
38impl fmt::Display for ObjectError {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 match self {
41 ObjectError::Malformed => f.write_str("malformed signed object"),
42 ObjectError::SignatureInvalid => {
43 f.write_str("the signed object's signature does not verify")
44 }
45 ObjectError::AlgMismatch => {
46 f.write_str("the signed object names another profile's algorithm")
47 }
48 ObjectError::FieldKeyNotText => f.write_str("a field key that is not text"),
49 ObjectError::DuplicateField(name) => write!(f, "two fields named {name:?}"),
50 ObjectError::Key(e) => write!(f, "{e}"),
51 }
52 }
53}
54
55impl std::error::Error for ObjectError {}
56
57#[derive(Debug, Clone, PartialEq, Eq)]
59pub struct Object {
60 pub key: Vec<u8>,
61 pub tbs: Vec<u8>,
62 pub signature: Vec<u8>,
63}
64
65#[derive(Debug, Clone, PartialEq, Eq)]
67pub struct HeldObject {
68 pub tbs: Vec<u8>,
69 pub signature: Vec<u8>,
70}
71
72#[derive(Debug, Clone, PartialEq)]
75pub struct VerifiedObject {
76 pub key: Vec<u8>,
77 pub tbs: Vec<u8>,
78 pub fields: Value,
79}
80
81impl Object {
82 pub fn to_value(&self) -> Value {
84 Value::Map(vec![
85 (Value::text("key"), Value::Bytes(self.key.clone())),
86 (Value::text("tbs"), Value::Bytes(self.tbs.clone())),
87 (
88 Value::text("signature"),
89 Value::Bytes(self.signature.clone()),
90 ),
91 ])
92 }
93
94 pub fn from_value(value: &Value) -> Result<Object, ObjectError> {
97 let [key, tbs, signature] = exact_byte_fields(value, ["key", "tbs", "signature"])?;
98 Ok(Object {
99 key,
100 tbs,
101 signature,
102 })
103 }
104}
105
106impl HeldObject {
107 pub fn to_value(&self) -> Value {
109 Value::Map(vec![
110 (Value::text("tbs"), Value::Bytes(self.tbs.clone())),
111 (
112 Value::text("signature"),
113 Value::Bytes(self.signature.clone()),
114 ),
115 ])
116 }
117
118 pub fn from_value(value: &Value) -> Result<HeldObject, ObjectError> {
121 let [tbs, signature] = exact_byte_fields(value, ["tbs", "signature"])?;
122 Ok(HeldObject { tbs, signature })
123 }
124}
125
126pub fn sign_object(
129 label: &str,
130 fields: &[(Value, Value)],
131 key: &NodeKey,
132) -> Result<Object, ObjectError> {
133 let carried = key.public_key();
134 let tbs = object_tbs(fields, key.profile())?;
135 let signature = key
136 .sign(&object_signed_bytes(label, &carried, &tbs))
137 .map_err(ObjectError::Key)?;
138 Ok(Object {
139 key: carried,
140 tbs,
141 signature,
142 })
143}
144
145pub fn sign_held_object(
147 label: &str,
148 fields: &[(Value, Value)],
149 key: &NodeKey,
150) -> Result<HeldObject, ObjectError> {
151 let object = sign_object(label, fields, key)?;
152 Ok(HeldObject {
153 tbs: object.tbs,
154 signature: object.signature,
155 })
156}
157
158pub fn verify_object(
164 label: &str,
165 value: &Value,
166 profile: Profile,
167) -> Result<VerifiedObject, ObjectError> {
168 let object = Object::from_value(value)?;
169 if !carried_key_well_formed(&object.key, profile) {
170 return Err(ObjectError::Malformed);
171 }
172 verified(label, object.key, object.tbs, &object.signature, profile)
173}
174
175pub fn verify_held_object(
178 label: &str,
179 value: &Value,
180 key: &[u8],
181 profile: Profile,
182) -> Result<VerifiedObject, ObjectError> {
183 let held = HeldObject::from_value(value)?;
184 verified(label, key.to_vec(), held.tbs, &held.signature, profile)
185}
186
187fn verified(
188 label: &str,
189 key: Vec<u8>,
190 tbs: Vec<u8>,
191 signature: &[u8],
192 profile: Profile,
193) -> Result<VerifiedObject, ObjectError> {
194 if !verify(
195 &object_signed_bytes(label, &key, &tbs),
196 signature,
197 &key,
198 profile,
199 ) {
200 return Err(ObjectError::SignatureInvalid);
201 }
202 let fields = cbor::decode(&tbs).map_err(|_| ObjectError::Malformed)?;
203 let alg = match (&fields, fields.get("alg")) {
204 (Value::Map(_), Some(Value::Text(alg))) => alg.clone(),
205 _ => return Err(ObjectError::Malformed),
206 };
207 if alg != profile.sig_alg() {
208 return Err(ObjectError::AlgMismatch);
209 }
210 Ok(VerifiedObject { key, tbs, fields })
211}
212
213fn object_tbs(fields: &[(Value, Value)], profile: Profile) -> Result<Vec<u8>, ObjectError> {
215 let mut seen = std::collections::HashSet::with_capacity(fields.len());
216 let mut with_alg = Vec::with_capacity(fields.len() + 1);
217 for (key, value) in fields {
218 let Value::Text(name) = key else {
219 return Err(ObjectError::FieldKeyNotText);
220 };
221 if seen.contains(name.as_str()) {
222 return Err(ObjectError::DuplicateField(name.clone()));
223 }
224 if name == "alg" {
225 continue;
226 }
227 seen.insert(name.as_str());
228 with_alg.push((key.clone(), value.clone()));
229 }
230 with_alg.push((Value::text("alg"), Value::text(profile.sig_alg())));
231 cbor::encode(&Value::Map(with_alg)).map_err(|_| ObjectError::Malformed)
232}
233
234fn object_signed_bytes(label: &str, key: &[u8], tbs: &[u8]) -> Vec<u8> {
237 let mut out = Vec::with_capacity(label.len() + 1 + 48 + tbs.len());
238 out.extend_from_slice(label.as_bytes());
239 out.push(0);
240 out.extend_from_slice(&Sha384::digest(key));
241 out.extend_from_slice(tbs);
242 out
243}
244
245fn exact_byte_fields<const N: usize>(
248 value: &Value,
249 names: [&str; N],
250) -> Result<[Vec<u8>; N], ObjectError> {
251 let Value::Map(pairs) = value else {
252 return Err(ObjectError::Malformed);
253 };
254 if pairs.len() != N {
255 return Err(ObjectError::Malformed);
256 }
257 let mut out: [Vec<u8>; N] = std::array::from_fn(|_| Vec::new());
258 for (slot, name) in out.iter_mut().zip(names) {
259 match value.get(name) {
260 Some(Value::Bytes(b)) => *slot = b.clone(),
261 _ => return Err(ObjectError::Malformed),
262 }
263 }
264 Ok(out)
265}