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