1use coset::{
2 Algorithm, CborSerializable, CoseSign1, Header, ProtectedHeader, TaggedCborSerializable,
3};
4use ssi_claims_core::SignatureError;
5
6use crate::{CosePayload, CoseSign1BytesBuf, TYP_LABEL};
7
8pub struct CoseSignerInfo {
10 pub algorithm: Option<Algorithm>,
12
13 pub key_id: Vec<u8>,
15}
16
17pub trait CoseSigner {
21 #[allow(async_fn_in_trait)]
25 async fn fetch_info(&self) -> Result<CoseSignerInfo, SignatureError>;
26
27 #[allow(async_fn_in_trait)]
29 async fn sign_bytes(&self, signing_bytes: &[u8]) -> Result<Vec<u8>, SignatureError>;
30
31 #[allow(async_fn_in_trait)]
36 async fn sign(
37 &self,
38 payload: &(impl ?Sized + CosePayload),
39 additional_data: Option<&[u8]>,
40 tagged: bool,
41 ) -> Result<CoseSign1BytesBuf, SignatureError> {
42 let info = self.fetch_info().await?;
43
44 let mut result = CoseSign1 {
45 protected: ProtectedHeader {
46 header: Header {
47 alg: info.algorithm,
48 key_id: info.key_id,
49 content_type: payload.content_type(),
50 rest: match payload.typ() {
51 Some(typ) => vec![(TYP_LABEL, typ.into())],
52 None => Vec::new(),
53 },
54 ..Default::default()
55 },
56 ..Default::default()
57 },
58 unprotected: Header::default(),
59 payload: Some(payload.payload_bytes().into_owned()),
60 signature: Vec::new(),
61 };
62
63 let tbs = result.tbs_data(additional_data.unwrap_or_default());
64
65 result.signature = self.sign_bytes(&tbs).await?;
66
67 Ok(if tagged {
68 result.to_tagged_vec().unwrap().into()
69 } else {
70 result.to_vec().unwrap().into()
71 })
72 }
73}
74
75impl<T: CoseSigner> CoseSigner for &T {
76 async fn fetch_info(&self) -> Result<CoseSignerInfo, SignatureError> {
77 T::fetch_info(*self).await
78 }
79
80 async fn sign_bytes(&self, signing_bytes: &[u8]) -> Result<Vec<u8>, SignatureError> {
81 T::sign_bytes(*self, signing_bytes).await
82 }
83
84 async fn sign(
85 &self,
86 payload: &(impl ?Sized + CosePayload),
87 additional_data: Option<&[u8]>,
88 tagged: bool,
89 ) -> Result<CoseSign1BytesBuf, SignatureError> {
90 T::sign(*self, payload, additional_data, tagged).await
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use crate::{key::CoseKeyGenerate, CosePayload, DecodedCoseSign1};
97 use coset::CoseKey;
98 use ssi_claims_core::VerificationParameters;
99
100 async fn sign_with(key: &CoseKey, tagged: bool) {
101 let bytes = b"PAYLOAD".sign(key, tagged).await.unwrap();
102 let decoded: DecodedCoseSign1 = bytes.decode(tagged).unwrap();
103
104 assert_eq!(decoded.signing_bytes.payload.as_bytes(), b"PAYLOAD");
105
106 let params = VerificationParameters::from_resolver(key);
107 assert_eq!(decoded.verify(params).await.unwrap(), Ok(()));
108 }
109
110 #[cfg(feature = "ed25519")]
111 #[async_std::test]
112 async fn sign_ed25519() {
113 sign_with(&CoseKey::generate_ed25519(), false).await
114 }
115
116 #[cfg(feature = "ed25519")]
117 #[async_std::test]
118 async fn sign_ed25519_tagged() {
119 sign_with(&CoseKey::generate_ed25519(), true).await
120 }
121
122 #[cfg(feature = "secp256k1")]
123 #[async_std::test]
124 async fn sign_secp256k1() {
125 sign_with(&CoseKey::generate_secp256k1(), false).await
126 }
127
128 #[cfg(feature = "secp256k1")]
129 #[async_std::test]
130 async fn sign_secp256k1_tagged() {
131 sign_with(&CoseKey::generate_secp256k1(), true).await
132 }
133
134 #[cfg(feature = "secp256r1")]
135 #[async_std::test]
136 async fn sign_p256() {
137 sign_with(&CoseKey::generate_p256(), false).await
138 }
139
140 #[cfg(feature = "secp256r1")]
141 #[async_std::test]
142 async fn sign_p256_tagged() {
143 sign_with(&CoseKey::generate_p256(), true).await
144 }
145
146 #[cfg(feature = "secp384r1")]
147 #[async_std::test]
148 async fn sign_p384() {
149 sign_with(&CoseKey::generate_p384(), false).await
150 }
151
152 #[cfg(feature = "secp384r1")]
153 #[async_std::test]
154 async fn sign_p384_tagged() {
155 sign_with(&CoseKey::generate_p384(), true).await
156 }
157}