1use ciborium::value::Integer;
2use coset::{iana::CoapContentFormat, CborSerializable, CoseSign1};
3use serde::{de::DeserializeOwned, Serialize};
4
5use super::{
6 content_type, message::SerializedMessage, namespace, signing_key::SigningKey,
7 verifying_key::VerifyingKey, SigningNamespace,
8};
9use crate::{
10 cose::{CoseSerializable, SIGNING_NAMESPACE},
11 error::{EncodingError, SignatureError},
12 CryptoError,
13};
14
15#[derive(Clone, Debug)]
20pub struct SignedObject(pub(crate) CoseSign1);
21
22impl From<CoseSign1> for SignedObject {
23 fn from(cose_sign1: CoseSign1) -> Self {
24 SignedObject(cose_sign1)
25 }
26}
27
28impl SignedObject {
29 pub fn content_type(&self) -> Result<CoapContentFormat, CryptoError> {
32 content_type(&self.0.protected)
33 }
34
35 fn inner(&self) -> &CoseSign1 {
36 &self.0
37 }
38
39 fn namespace(&self) -> Result<SigningNamespace, CryptoError> {
40 namespace(&self.0.protected)
41 }
42
43 fn payload(&self) -> Result<Vec<u8>, CryptoError> {
44 self.0
45 .payload
46 .as_ref()
47 .ok_or(SignatureError::InvalidSignature.into())
48 .map(|payload| payload.to_vec())
49 }
50
51 pub fn verify_and_unwrap<Message: DeserializeOwned>(
54 &self,
55 verifying_key: &VerifyingKey,
56 namespace: &SigningNamespace,
57 ) -> Result<Message, CryptoError> {
58 SerializedMessage::from_bytes(
59 self.verify_and_unwrap_bytes(verifying_key, namespace)?,
60 self.content_type()?,
61 )
62 .decode()
63 .map_err(Into::into)
64 }
65
66 fn verify_and_unwrap_bytes(
69 &self,
70 verifying_key: &VerifyingKey,
71 namespace: &SigningNamespace,
72 ) -> Result<Vec<u8>, CryptoError> {
73 if self.inner().protected.header.alg.is_none() {
74 return Err(SignatureError::InvalidSignature.into());
75 }
76
77 if self.namespace()? != *namespace {
78 return Err(SignatureError::InvalidNamespace.into());
79 }
80
81 self.inner()
82 .verify_signature(&[], |sig, data| verifying_key.verify_raw(sig, data))?;
83 self.payload()
84 }
85}
86
87impl SigningKey {
88 fn sign_bytes(
92 &self,
93 serialized_message: &SerializedMessage,
94 namespace: &SigningNamespace,
95 ) -> Result<SignedObject, CryptoError> {
96 let cose_sign1 = coset::CoseSign1Builder::new()
97 .protected(
98 coset::HeaderBuilder::new()
99 .algorithm(self.cose_algorithm())
100 .key_id((&self.id).into())
101 .content_format(serialized_message.content_type())
102 .value(
103 SIGNING_NAMESPACE,
104 ciborium::Value::Integer(Integer::from(namespace.as_i64())),
105 )
106 .build(),
107 )
108 .payload(serialized_message.as_bytes().to_vec())
109 .create_signature(&[], |pt| self.sign_raw(pt))
110 .build();
111 Ok(SignedObject(cose_sign1))
112 }
113
114 pub fn sign<Message: Serialize>(
144 &self,
145 message: &Message,
146 namespace: &SigningNamespace,
147 ) -> Result<SignedObject, CryptoError> {
148 self.sign_bytes(&SerializedMessage::encode(message)?, namespace)
149 }
150}
151
152impl CoseSerializable for SignedObject {
153 fn from_cose(bytes: &[u8]) -> Result<Self, EncodingError> {
154 Ok(SignedObject(
155 CoseSign1::from_slice(bytes).map_err(|_| EncodingError::InvalidCoseEncoding)?,
156 ))
157 }
158
159 fn to_cose(&self) -> Vec<u8> {
160 self.0
161 .clone()
162 .to_vec()
163 .expect("SignedObject is always serializable")
164 }
165}
166
167#[cfg(test)]
168mod tests {
169 use serde::{Deserialize, Serialize};
170
171 use crate::{
172 CoseSerializable, CryptoError, SignatureAlgorithm, SignedObject, SigningKey,
173 SigningNamespace, VerifyingKey,
174 };
175
176 const VERIFYING_KEY: &[u8] = &[
177 166, 1, 1, 2, 80, 55, 131, 40, 191, 230, 137, 76, 182, 184, 139, 94, 152, 45, 63, 13, 71,
178 3, 39, 4, 129, 2, 32, 6, 33, 88, 32, 93, 213, 35, 177, 81, 219, 226, 241, 147, 140, 238,
179 32, 34, 183, 213, 107, 227, 92, 75, 84, 208, 47, 198, 80, 18, 188, 172, 145, 184, 154, 26,
180 170,
181 ];
182 const SIGNED_OBJECT: &[u8] = &[
183 132, 88, 30, 164, 1, 39, 3, 24, 60, 4, 80, 55, 131, 40, 191, 230, 137, 76, 182, 184, 139,
184 94, 152, 45, 63, 13, 71, 58, 0, 1, 56, 127, 32, 160, 85, 161, 102, 102, 105, 101, 108, 100,
185 49, 108, 84, 101, 115, 116, 32, 109, 101, 115, 115, 97, 103, 101, 88, 64, 206, 83, 177,
186 184, 37, 103, 128, 39, 120, 174, 61, 4, 29, 184, 68, 46, 47, 203, 47, 246, 108, 160, 169,
187 114, 7, 165, 119, 198, 3, 209, 52, 249, 89, 31, 156, 255, 212, 75, 224, 78, 183, 37, 174,
188 63, 112, 70, 219, 246, 19, 213, 17, 121, 249, 244, 23, 182, 36, 193, 175, 55, 250, 65, 250,
189 6,
190 ];
191
192 #[derive(Deserialize, Debug, PartialEq, Serialize)]
193 struct TestMessage {
194 field1: String,
195 }
196
197 #[test]
198 fn test_roundtrip_cose() {
199 let signed_object = SignedObject::from_cose(SIGNED_OBJECT).unwrap();
200 assert_eq!(
201 signed_object.content_type().unwrap(),
202 coset::iana::CoapContentFormat::Cbor
203 );
204 let cose_bytes = signed_object.to_cose();
205 assert_eq!(cose_bytes, SIGNED_OBJECT);
206 }
207
208 #[test]
209 fn test_verify_and_unwrap_testvector() {
210 let test_message = TestMessage {
211 field1: "Test message".to_string(),
212 };
213 let signed_object = SignedObject::from_cose(SIGNED_OBJECT).unwrap();
214 let verifying_key = VerifyingKey::from_cose(VERIFYING_KEY).unwrap();
215 let namespace = SigningNamespace::ExampleNamespace;
216 let payload: TestMessage = signed_object
217 .verify_and_unwrap(&verifying_key, &namespace)
218 .unwrap();
219 assert_eq!(payload, test_message);
220 }
221
222 #[test]
223 fn test_sign_verify_and_unwrap_roundtrip() {
224 let signing_key = SigningKey::make(SignatureAlgorithm::Ed25519);
225 let test_message = TestMessage {
226 field1: "Test message".to_string(),
227 };
228 let namespace = SigningNamespace::ExampleNamespace;
229 let signed_object = signing_key.sign(&test_message, &namespace).unwrap();
230 let verifying_key = signing_key.to_verifying_key();
231 let payload: TestMessage = signed_object
232 .verify_and_unwrap(&verifying_key, &namespace)
233 .unwrap();
234 assert_eq!(payload, test_message);
235 }
236
237 #[test]
238 fn test_fail_namespace_changed() {
239 let signing_key = SigningKey::make(SignatureAlgorithm::Ed25519);
240 let test_message = TestMessage {
241 field1: "Test message".to_string(),
242 };
243 let namespace = SigningNamespace::ExampleNamespace;
244 let signed_object = signing_key.sign(&test_message, &namespace).unwrap();
245 let verifying_key = signing_key.to_verifying_key();
246
247 let different_namespace = SigningNamespace::ExampleNamespace2;
248 let result: Result<TestMessage, CryptoError> =
249 signed_object.verify_and_unwrap(&verifying_key, &different_namespace);
250 assert!(result.is_err());
251 }
252}