Skip to main content

ssi_cose/
signature.rs

1use coset::{
2    Algorithm, CborSerializable, CoseSign1, Header, ProtectedHeader, TaggedCborSerializable,
3};
4use ssi_claims_core::SignatureError;
5
6use crate::{CosePayload, CoseSign1BytesBuf, TYP_LABEL};
7
8/// COSE signer information.
9pub struct CoseSignerInfo {
10    /// Signature algorithm.
11    pub algorithm: Option<Algorithm>,
12
13    /// Signing key identifier.
14    pub key_id: Vec<u8>,
15}
16
17/// COSE signer.
18///
19/// Any type with the ability to sign a COSE payload.
20pub trait CoseSigner {
21    /// Fetches the information about the signing key.
22    ///
23    /// This information will be included in the COSE header.
24    #[allow(async_fn_in_trait)]
25    async fn fetch_info(&self) -> Result<CoseSignerInfo, SignatureError>;
26
27    /// Signs the given bytes.
28    #[allow(async_fn_in_trait)]
29    async fn sign_bytes(&self, signing_bytes: &[u8]) -> Result<Vec<u8>, SignatureError>;
30
31    /// Signs the given payload.
32    ///
33    /// Returns a serialized `COSE_Sign1` object, tagged or not according to
34    /// `tagged`.
35    #[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}