1use ciborium::value::Integer;
2use coset::{iana::CoapContentFormat, CborSerializable, CoseSign1};
3use serde::Serialize;
4
5use super::{
6 content_type, message::SerializedMessage, namespace, signing_key::SigningKey, SigningNamespace,
7 VerifyingKey,
8};
9use crate::{
10 content_format::CoseSign1ContentFormat,
11 cose::{CoseSerializable, SIGNING_NAMESPACE},
12 error::{EncodingError, SignatureError},
13 CoseSign1Bytes, CryptoError,
14};
15
16pub struct Signature(CoseSign1);
20
21impl From<CoseSign1> for Signature {
22 fn from(cose_sign1: CoseSign1) -> Self {
23 Signature(cose_sign1)
24 }
25}
26
27impl Signature {
28 fn inner(&self) -> &CoseSign1 {
29 &self.0
30 }
31
32 fn namespace(&self) -> Result<SigningNamespace, CryptoError> {
33 namespace(&self.0.protected)
34 }
35
36 pub fn content_type(&self) -> Result<CoapContentFormat, CryptoError> {
39 content_type(&self.0.protected)
40 }
41
42 pub fn verify(
49 &self,
50 serialized_message_bytes: &[u8],
51 verifying_key: &VerifyingKey,
52 namespace: &SigningNamespace,
53 ) -> bool {
54 if self.inner().protected.header.alg.is_none() {
55 return false;
56 }
57
58 if self.namespace().ok().as_ref() != Some(namespace) {
59 return false;
60 }
61
62 self.inner()
63 .verify_detached_signature(serialized_message_bytes, &[], |sig, data| {
64 verifying_key.verify_raw(sig, data)
65 })
66 .is_ok()
67 }
68}
69
70impl SigningKey {
71 pub fn sign_detached<Message: Serialize>(
101 &self,
102 message: &Message,
103 namespace: &SigningNamespace,
104 ) -> Result<(Signature, SerializedMessage), CryptoError> {
105 let serialized_message = SerializedMessage::encode(message)?;
106 Ok((
107 self.sign_detached_bytes(&serialized_message, namespace),
108 serialized_message,
109 ))
110 }
111
112 pub fn counter_sign_detached(
129 &self,
130 serialized_message_bytes: Vec<u8>,
131 initial_signature: &Signature,
132 namespace: &SigningNamespace,
133 ) -> Result<Signature, CryptoError> {
134 if initial_signature.namespace()? != *namespace {
137 return Err(SignatureError::InvalidNamespace.into());
138 }
139
140 Ok(self.sign_detached_bytes(
141 &SerializedMessage::from_bytes(
142 serialized_message_bytes,
143 initial_signature.content_type()?,
144 ),
145 namespace,
146 ))
147 }
148
149 fn sign_detached_bytes(
153 &self,
154 message: &SerializedMessage,
155 namespace: &SigningNamespace,
156 ) -> Signature {
157 Signature::from(
158 coset::CoseSign1Builder::new()
159 .protected(
160 coset::HeaderBuilder::new()
161 .algorithm(self.cose_algorithm())
162 .key_id((&self.id).into())
163 .content_format(message.content_type())
164 .value(
165 SIGNING_NAMESPACE,
166 ciborium::Value::Integer(Integer::from(namespace.as_i64())),
167 )
168 .build(),
169 )
170 .create_detached_signature(message.as_bytes(), &[], |pt| self.sign_raw(pt))
171 .build(),
172 )
173 }
174}
175
176impl CoseSerializable<CoseSign1ContentFormat> for Signature {
177 fn from_cose(bytes: &CoseSign1Bytes) -> Result<Self, EncodingError> {
178 let cose_sign1 = CoseSign1::from_slice(bytes.as_ref())
179 .map_err(|_| EncodingError::InvalidCoseEncoding)?;
180 Ok(Signature(cose_sign1))
181 }
182
183 fn to_cose(&self) -> CoseSign1Bytes {
184 self.0
185 .clone()
186 .to_vec()
187 .expect("Signature is always serializable")
188 .into()
189 }
190}
191
192#[cfg(test)]
193mod tests {
194 use super::*;
195 use crate::{CoseKeyBytes, SignatureAlgorithm};
196
197 const VERIFYING_KEY: &[u8] = &[
198 166, 1, 1, 2, 80, 55, 131, 40, 191, 230, 137, 76, 182, 184, 139, 94, 152, 45, 63, 13, 71,
199 3, 39, 4, 129, 2, 32, 6, 33, 88, 32, 93, 213, 35, 177, 81, 219, 226, 241, 147, 140, 238,
200 32, 34, 183, 213, 107, 227, 92, 75, 84, 208, 47, 198, 80, 18, 188, 172, 145, 184, 154, 26,
201 170,
202 ];
203 const SIGNATURE: &[u8] = &[
204 132, 88, 30, 164, 1, 39, 3, 24, 60, 4, 80, 55, 131, 40, 191, 230, 137, 76, 182, 184, 139,
205 94, 152, 45, 63, 13, 71, 58, 0, 1, 56, 127, 32, 160, 246, 88, 64, 206, 83, 177, 184, 37,
206 103, 128, 39, 120, 174, 61, 4, 29, 184, 68, 46, 47, 203, 47, 246, 108, 160, 169, 114, 7,
207 165, 119, 198, 3, 209, 52, 249, 89, 31, 156, 255, 212, 75, 224, 78, 183, 37, 174, 63, 112,
208 70, 219, 246, 19, 213, 17, 121, 249, 244, 23, 182, 36, 193, 175, 55, 250, 65, 250, 6,
209 ];
210 const SERIALIZED_MESSAGE: &[u8] = &[
211 161, 102, 102, 105, 101, 108, 100, 49, 108, 84, 101, 115, 116, 32, 109, 101, 115, 115, 97,
212 103, 101,
213 ];
214
215 #[test]
216 fn test_cose_roundtrip_encode_signature() {
217 let signature = Signature::from_cose(&CoseSign1Bytes::from(SIGNATURE)).unwrap();
218 let cose_bytes = signature.to_cose();
219 let decoded_signature = Signature::from_cose(&cose_bytes).unwrap();
220 assert_eq!(signature.inner(), decoded_signature.inner());
221 }
222
223 #[test]
224 fn test_verify_testvector() {
225 let verifying_key = VerifyingKey::from_cose(&CoseKeyBytes::from(VERIFYING_KEY)).unwrap();
226 let signature = Signature::from_cose(&CoseSign1Bytes::from(SIGNATURE)).unwrap();
227 let serialized_message =
228 SerializedMessage::from_bytes(SERIALIZED_MESSAGE.to_vec(), CoapContentFormat::Cbor);
229
230 let namespace = SigningNamespace::ExampleNamespace;
231
232 assert!(signature.verify(serialized_message.as_ref(), &verifying_key, &namespace));
233 }
234
235 #[test]
236 fn test_sign_detached_roundtrip() {
237 let signing_key = SigningKey::make(SignatureAlgorithm::Ed25519);
238 let message = "Test message";
239 let namespace = SigningNamespace::ExampleNamespace;
240
241 let (signature, serialized_message) =
242 signing_key.sign_detached(&message, &namespace).unwrap();
243
244 let verifying_key = signing_key.to_verifying_key();
245 assert!(signature.verify(serialized_message.as_ref(), &verifying_key, &namespace));
246 }
247
248 #[test]
249 fn test_countersign_detatched() {
250 let signing_key = SigningKey::make(SignatureAlgorithm::Ed25519);
251 let message = "Test message";
252 let namespace = SigningNamespace::ExampleNamespace;
253
254 let (signature, serialized_message) =
255 signing_key.sign_detached(&message, &namespace).unwrap();
256
257 let countersignature = signing_key
258 .counter_sign_detached(
259 serialized_message.as_bytes().to_vec(),
260 &signature,
261 &namespace,
262 )
263 .unwrap();
264
265 let verifying_key = signing_key.to_verifying_key();
266 assert!(countersignature.verify(serialized_message.as_ref(), &verifying_key, &namespace));
267 }
268
269 #[test]
270 fn test_fail_namespace_changed() {
271 let signing_key = SigningKey::make(SignatureAlgorithm::Ed25519);
272 let message = "Test message";
273 let namespace = SigningNamespace::ExampleNamespace;
274
275 let (signature, serialized_message) =
276 signing_key.sign_detached(&message, &namespace).unwrap();
277
278 let different_namespace = SigningNamespace::ExampleNamespace2;
279 let verifying_key = signing_key.to_verifying_key();
280
281 assert!(!signature.verify(
282 serialized_message.as_ref(),
283 &verifying_key,
284 &different_namespace
285 ));
286 }
287}