1use coset::{CborSerializable, CoseSign1, CoseSign1Builder, HeaderBuilder, iana};
27use serde::{Serialize, de::DeserializeOwned};
28
29#[derive(Debug, Clone)]
31pub struct CoseSigned<T> {
32 inner: CoseSign1,
33 _marker: std::marker::PhantomData<T>,
34}
35
36#[derive(Debug, thiserror::Error)]
38pub enum CoseError {
39 #[error("CBOR serialization failed: {0}")]
41 CborSerialize(String),
42
43 #[error("CBOR deserialization failed: {0}")]
45 CborDeserialize(String),
46
47 #[error("COSE serialization failed: {0}")]
49 CoseSerialize(String),
50
51 #[error("COSE deserialization failed: {0}")]
53 CoseDeserialize(String),
54
55 #[error("Signature verification failed")]
57 VerificationFailed,
58
59 #[error("Missing payload")]
61 MissingPayload,
62
63 #[error("Invalid key: {0}")]
65 InvalidKey(String),
66
67 #[error("Invalid signature length")]
69 InvalidSignatureLength,
70
71 #[error("Algorithm mismatch: expected {expected}, got {got}")]
73 AlgorithmMismatch {
74 expected: String,
76 got: String,
78 },
79}
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq)]
83pub enum SigningAlgorithm {
84 EdDSA,
86 ES256,
88 ES384,
90}
91
92impl std::fmt::Display for SigningAlgorithm {
93 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94 match self {
95 SigningAlgorithm::EdDSA => write!(f, "EdDSA"),
96 SigningAlgorithm::ES256 => write!(f, "ES256"),
97 SigningAlgorithm::ES384 => write!(f, "ES384"),
98 }
99 }
100}
101
102impl SigningAlgorithm {
103 fn to_iana(self) -> iana::Algorithm {
104 match self {
105 SigningAlgorithm::EdDSA => iana::Algorithm::EdDSA,
106 SigningAlgorithm::ES256 => iana::Algorithm::ES256,
107 SigningAlgorithm::ES384 => iana::Algorithm::ES384,
108 }
109 }
110}
111
112impl<T> CoseSigned<T>
113where
114 T: Serialize + DeserializeOwned,
115{
116 pub fn kid(&self) -> Option<String> {
121 let kid = &self.inner.protected.header.key_id;
122 if kid.is_empty() {
123 None
124 } else {
125 String::from_utf8(kid.clone()).ok()
126 }
127 }
128
129 pub fn algorithm(&self) -> Option<SigningAlgorithm> {
131 match self.inner.protected.header.alg {
132 Some(coset::RegisteredLabelWithPrivate::Assigned(iana::Algorithm::EdDSA)) => {
133 Some(SigningAlgorithm::EdDSA)
134 }
135 Some(coset::RegisteredLabelWithPrivate::Assigned(iana::Algorithm::ES256)) => {
136 Some(SigningAlgorithm::ES256)
137 }
138 Some(coset::RegisteredLabelWithPrivate::Assigned(iana::Algorithm::ES384)) => {
139 Some(SigningAlgorithm::ES384)
140 }
141 _ => None,
142 }
143 }
144
145 pub fn to_bytes(&self) -> Result<Vec<u8>, CoseError> {
150 self.inner
151 .clone()
152 .to_vec()
153 .map_err(|e| CoseError::CoseSerialize(e.to_string()))
154 }
155
156 pub fn from_bytes(bytes: &[u8]) -> Result<Self, CoseError> {
158 let inner =
159 CoseSign1::from_slice(bytes).map_err(|e| CoseError::CoseDeserialize(e.to_string()))?;
160 Ok(Self {
161 inner,
162 _marker: std::marker::PhantomData,
163 })
164 }
165
166 pub fn payload_unverified(&self) -> Result<T, CoseError> {
171 let payload = self
172 .inner
173 .payload
174 .as_ref()
175 .ok_or(CoseError::MissingPayload)?;
176
177 ciborium::from_reader(payload.as_slice())
178 .map_err(|e| CoseError::CborDeserialize(e.to_string()))
179 }
180
181 pub fn sign_with<F>(
185 payload: &T,
186 kid: &str,
187 alg: SigningAlgorithm,
188 sign_fn: F,
189 ) -> Result<Self, CoseError>
190 where
191 F: FnOnce(&[u8]) -> Result<Vec<u8>, CoseError>,
192 {
193 let mut cbor_payload = Vec::new();
194 ciborium::into_writer(payload, &mut cbor_payload)
195 .map_err(|e| CoseError::CborSerialize(e.to_string()))?;
196
197 let protected = HeaderBuilder::new()
198 .algorithm(alg.to_iana())
199 .key_id(kid.as_bytes().to_vec())
200 .build();
201
202 let sign1 = CoseSign1Builder::new()
203 .protected(protected)
204 .payload(cbor_payload)
205 .try_create_signature(&[], sign_fn)?
206 .build();
207
208 Ok(Self {
209 inner: sign1,
210 _marker: std::marker::PhantomData,
211 })
212 }
213
214 pub fn verify_with<F>(&self, verify_fn: F) -> Result<T, CoseError>
218 where
219 F: FnOnce(&[u8], &[u8]) -> Result<(), CoseError>,
220 {
221 self.inner
222 .verify_signature(&[], |sig, data| verify_fn(data, sig))?;
223
224 let payload = self
225 .inner
226 .payload
227 .as_ref()
228 .ok_or(CoseError::MissingPayload)?;
229
230 ciborium::from_reader(payload.as_slice())
231 .map_err(|e| CoseError::CborDeserialize(e.to_string()))
232 }
233
234 pub fn check_algorithm(&self, expected: SigningAlgorithm) -> Result<(), CoseError> {
236 let actual = self.algorithm();
237 if actual != Some(expected) {
238 return Err(CoseError::AlgorithmMismatch {
239 expected: expected.to_string(),
240 got: actual
241 .map(|a| a.to_string())
242 .unwrap_or_else(|| "None".to_string()),
243 });
244 }
245 Ok(())
246 }
247}
248
249impl From<coset::CoseError> for CoseError {
250 fn from(e: coset::CoseError) -> Self {
251 CoseError::CoseSerialize(format!("{:?}", e))
252 }
253}
254
255#[cfg(feature = "ed25519")]
256mod ed25519_impl {
257 use super::*;
258 use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
259
260 impl<T> CoseSigned<T>
261 where
262 T: Serialize + DeserializeOwned,
263 {
264 pub fn sign_ed25519(
266 payload: &T,
267 kid: &str,
268 signing_key: &SigningKey,
269 ) -> Result<Self, CoseError> {
270 Self::sign_with(payload, kid, SigningAlgorithm::EdDSA, |data| {
271 let sig = signing_key.sign(data);
272 Ok(sig.to_bytes().to_vec())
273 })
274 }
275
276 pub fn verify_ed25519(&self, verifying_key: &VerifyingKey) -> Result<T, CoseError> {
278 self.check_algorithm(SigningAlgorithm::EdDSA)?;
279
280 self.verify_with(|data, sig| {
281 let signature =
282 Signature::from_slice(sig).map_err(|_| CoseError::InvalidSignatureLength)?;
283 verifying_key
284 .verify(data, &signature)
285 .map_err(|_| CoseError::VerificationFailed)
286 })
287 }
288 }
289}
290
291#[cfg(feature = "p256")]
292mod p256_impl {
293 use super::*;
294 use p256::ecdsa::{
295 Signature, SigningKey, VerifyingKey, signature::Signer, signature::Verifier,
296 };
297
298 impl<T> CoseSigned<T>
299 where
300 T: Serialize + DeserializeOwned,
301 {
302 pub fn sign_p256(
304 payload: &T,
305 kid: &str,
306 signing_key: &SigningKey,
307 ) -> Result<Self, CoseError> {
308 Self::sign_with(payload, kid, SigningAlgorithm::ES256, |data| {
309 let sig: Signature = signing_key.sign(data);
310 Ok(sig.to_bytes().to_vec())
311 })
312 }
313
314 pub fn verify_p256(&self, verifying_key: &VerifyingKey) -> Result<T, CoseError> {
316 self.check_algorithm(SigningAlgorithm::ES256)?;
317
318 self.verify_with(|data, sig| {
319 let signature =
320 Signature::from_slice(sig).map_err(|_| CoseError::InvalidSignatureLength)?;
321 verifying_key
322 .verify(data, &signature)
323 .map_err(|_| CoseError::VerificationFailed)
324 })
325 }
326 }
327}
328
329#[cfg(feature = "p384")]
330mod p384_impl {
331 use super::*;
332 use p384::ecdsa::{
333 Signature, SigningKey, VerifyingKey, signature::Signer, signature::Verifier,
334 };
335
336 impl<T> CoseSigned<T>
337 where
338 T: Serialize + DeserializeOwned,
339 {
340 pub fn sign_p384(
342 payload: &T,
343 kid: &str,
344 signing_key: &SigningKey,
345 ) -> Result<Self, CoseError> {
346 Self::sign_with(payload, kid, SigningAlgorithm::ES384, |data| {
347 let sig: Signature = signing_key.sign(data);
348 Ok(sig.to_bytes().to_vec())
349 })
350 }
351
352 pub fn verify_p384(&self, verifying_key: &VerifyingKey) -> Result<T, CoseError> {
354 self.check_algorithm(SigningAlgorithm::ES384)?;
355
356 self.verify_with(|data, sig| {
357 let signature =
358 Signature::from_slice(sig).map_err(|_| CoseError::InvalidSignatureLength)?;
359 verifying_key
360 .verify(data, &signature)
361 .map_err(|_| CoseError::VerificationFailed)
362 })
363 }
364 }
365}