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