1use base64::{Engine as _, engine::general_purpose::STANDARD};
11use sfv::{
12 BareItem, DictSerializer, Dictionary, Integer, KeyRef, ListEntry, ListSerializer, Parser,
13 StringRef, key_ref,
14};
15
16use crate::profile::{Component, SIGNATURE_LABEL};
17
18pub const MIN_NONCE_BYTES: usize = 16;
20
21const CREATED: &KeyRef = key_ref("created");
22const NONCE: &KeyRef = key_ref("nonce");
23const ALG: &KeyRef = key_ref("alg");
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum Param {
28 Created,
30 Nonce,
32 Alg,
34}
35
36impl Param {
37 pub const SIGNING_ORDER: [Self; 3] = [Self::Created, Self::Nonce, Self::Alg];
39
40 const fn name(self) -> &'static str {
41 match self {
42 Self::Created => "created",
43 Self::Nonce => "nonce",
44 Self::Alg => "alg",
45 }
46 }
47}
48
49#[derive(Debug, Clone, PartialEq, Eq)]
53pub struct SignatureParams {
54 components: Vec<Component>,
55 created: Integer,
56 nonce: sfv::String,
57 alg: sfv::String,
58 order: [Param; 3],
59}
60
61#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
63pub enum SignatureInputError {
64 #[error("signature-input is not a structured field dictionary")]
66 Syntax,
67 #[error("signature-input must hold exactly one member labelled `integrity`")]
69 Label,
70 #[error("signature-input member is not a non-empty inner list")]
72 NotInnerList,
73 #[error("covered components must be plain strings")]
75 ComponentForm,
76 #[error("covered component `{0}` appears twice")]
78 DuplicateComponent(Component),
79 #[error("covered component is not part of the profile")]
81 UnsupportedComponent,
82 #[error("unexpected signature parameter")]
84 UnexpectedParameter,
85 #[error("missing signature parameter `{}`", .0.name())]
87 MissingParameter(Param),
88 #[error("invalid signature parameter `{}`", .0.name())]
90 InvalidParameter(Param),
91}
92
93impl SignatureParams {
94 pub fn new(created: i64, nonce: &str, alg: &str) -> Result<Self, SignatureInputError> {
101 let invalid = SignatureInputError::InvalidParameter;
102 if created <= 0 {
103 return Err(invalid(Param::Created));
104 }
105 if nonce.is_empty() {
106 return Err(invalid(Param::Nonce));
107 }
108 if alg.is_empty() {
109 return Err(invalid(Param::Alg));
110 }
111
112 Ok(Self {
113 components: Component::ALL.to_vec(),
114 created: Integer::try_from(created).map_err(|_| invalid(Param::Created))?,
115 nonce: sfv::String::from_string(nonce.to_owned()).map_err(|_| invalid(Param::Nonce))?,
116 alg: sfv::String::from_string(alg.to_owned()).map_err(|_| invalid(Param::Alg))?,
117 order: Param::SIGNING_ORDER,
118 })
119 }
120
121 pub fn parse(field: &str) -> Result<Self, SignatureInputError> {
127 let dictionary: Dictionary = Parser::new(field)
128 .parse()
129 .map_err(|_| SignatureInputError::Syntax)?;
130 let member = only_integrity_member(&dictionary).ok_or(SignatureInputError::Label)?;
131 let ListEntry::InnerList(inner_list) = member else {
132 return Err(SignatureInputError::NotInnerList);
133 };
134 if inner_list.items.is_empty() {
135 return Err(SignatureInputError::NotInnerList);
136 }
137
138 let mut components = Vec::with_capacity(inner_list.items.len());
139 for item in &inner_list.items {
140 let BareItem::String(identifier) = &item.bare_item else {
141 return Err(SignatureInputError::ComponentForm);
142 };
143 if !item.params.is_empty() {
144 return Err(SignatureInputError::ComponentForm);
145 }
146 let component = Component::from_identifier(identifier.as_str())
147 .ok_or(SignatureInputError::UnsupportedComponent)?;
148 if components.contains(&component) {
149 return Err(SignatureInputError::DuplicateComponent(component));
150 }
151 components.push(component);
152 }
153
154 let mut created = None;
155 let mut nonce = None;
156 let mut alg = None;
157 let mut order = Vec::with_capacity(3);
158 for (key, value) in &inner_list.params {
159 let param = match key.as_str() {
160 "created" => Param::Created,
161 "nonce" => Param::Nonce,
162 "alg" => Param::Alg,
163 _ => return Err(SignatureInputError::UnexpectedParameter),
164 };
165 let invalid = SignatureInputError::InvalidParameter(param);
166 match (param, value) {
167 (Param::Created, BareItem::Integer(value)) if i64::from(*value) > 0 => {
168 created = Some(*value);
169 }
170 (Param::Nonce, BareItem::String(value)) if !value.as_str().is_empty() => {
171 nonce = Some(value.clone());
172 }
173 (Param::Alg, BareItem::String(value)) if !value.as_str().is_empty() => {
174 alg = Some(value.clone());
175 }
176 _ => return Err(invalid),
177 }
178 order.push(param);
179 }
180
181 let missing = SignatureInputError::MissingParameter;
182 Ok(Self {
183 components,
184 created: created.ok_or(missing(Param::Created))?,
185 nonce: nonce.ok_or(missing(Param::Nonce))?,
186 alg: alg.ok_or(missing(Param::Alg))?,
187 order: order
189 .try_into()
190 .expect("created, nonce and alg are each present once"),
191 })
192 }
193
194 #[must_use]
196 pub fn components(&self) -> &[Component] {
197 &self.components
198 }
199
200 #[must_use]
202 pub fn created(&self) -> i64 {
203 i64::from(self.created)
204 }
205
206 #[must_use]
208 pub fn nonce(&self) -> &str {
209 self.nonce.as_str()
210 }
211
212 #[must_use]
214 pub fn alg(&self) -> &str {
215 self.alg.as_str()
216 }
217
218 #[must_use]
223 pub fn serialize(&self) -> String {
224 let mut list = ListSerializer::new();
225 {
226 let mut inner = list.inner_list();
227 for component in &self.components {
228 let identifier = StringRef::from_str(component.identifier())
229 .expect("component identifiers are printable ASCII");
230 let _ = inner.bare_item(identifier);
231 }
232 let mut params = inner.finish();
233 for param in self.order {
234 params = match param {
235 Param::Created => params.parameter(CREATED, self.created),
236 Param::Nonce => params.parameter(NONCE, &*self.nonce),
237 Param::Alg => params.parameter(ALG, &*self.alg),
238 };
239 }
240 }
241 list.finish().expect("a list with one member serializes")
242 }
243
244 #[must_use]
246 pub fn to_field(&self) -> String {
247 format!("{SIGNATURE_LABEL}={}", self.serialize())
248 }
249}
250
251#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
253pub enum SignatureFieldError {
254 #[error("signature is not a structured field dictionary")]
256 Syntax,
257 #[error("signature must hold exactly one member labelled `integrity`")]
259 Label,
260 #[error("signature member is not a non-empty byte sequence")]
262 NotByteSequence,
263}
264
265pub fn parse_signature(field: &str) -> Result<Vec<u8>, SignatureFieldError> {
271 let dictionary: Dictionary = Parser::new(field)
272 .parse()
273 .map_err(|_| SignatureFieldError::Syntax)?;
274 let member = only_integrity_member(&dictionary).ok_or(SignatureFieldError::Label)?;
275 match member {
276 ListEntry::Item(item) if item.params.is_empty() => match &item.bare_item {
277 BareItem::ByteSequence(bytes) if !bytes.is_empty() => Ok(bytes.clone()),
278 _ => Err(SignatureFieldError::NotByteSequence),
279 },
280 _ => Err(SignatureFieldError::NotByteSequence),
281 }
282}
283
284#[must_use]
286pub fn signature_field(signature: &[u8]) -> String {
287 let mut dictionary = DictSerializer::new();
288 let _ = dictionary.bare_item(key_ref(SIGNATURE_LABEL), signature);
289 dictionary
290 .finish()
291 .expect("a dictionary with one member serializes")
292}
293
294fn only_integrity_member(dictionary: &Dictionary) -> Option<&ListEntry> {
298 match dictionary.iter().collect::<Vec<_>>().as_slice() {
299 [(label, member)] if label.as_str() == SIGNATURE_LABEL => Some(member),
300 _ => None,
301 }
302}
303
304#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
306pub enum NonceError {
307 #[error("nonce is not canonical padded base64")]
309 NotCanonicalBase64,
310 #[error("nonce decodes to fewer than {MIN_NONCE_BYTES} bytes")]
312 TooShort,
313}
314
315pub fn validate_nonce(nonce: &str) -> Result<(), NonceError> {
323 let decoded = STANDARD
324 .decode(nonce)
325 .map_err(|_| NonceError::NotCanonicalBase64)?;
326 if STANDARD.encode(&decoded) != nonce {
327 return Err(NonceError::NotCanonicalBase64);
328 }
329 if decoded.len() < MIN_NONCE_BYTES {
330 return Err(NonceError::TooShort);
331 }
332 Ok(())
333}
334
335pub fn generate_nonce() -> Result<String, getrandom::Error> {
341 let mut bytes = [0u8; MIN_NONCE_BYTES];
342 getrandom::fill(&mut bytes)?;
343 Ok(STANDARD.encode(bytes))
344}