Skip to main content

ssi_cose/
verification.rs

1use coset::{CoseKey, Header, ProtectedHeader};
2use ssi_claims_core::{
3    ClaimsValidity, InvalidProof, ProofValidationError, ProofValidity, ResolverProvider,
4    ValidateClaims, ValidateProof, VerifiableClaims, Verification,
5};
6use ssi_crypto::VerificationError;
7
8use crate::{
9    algorithm::instantiate_algorithm,
10    key::{CoseKeyDecode, CoseKeyResolver, KeyDecodingError},
11    CoseSignatureBytes, DecodedCoseSign1, UnsignedCoseSign1,
12};
13
14impl<T> DecodedCoseSign1<T> {
15    /// Verify.
16    pub async fn verify<P>(&self, params: P) -> Result<Verification, ProofValidationError>
17    where
18        T: ValidateCoseHeader<P> + ValidateClaims<P, CoseSignatureBytes>,
19        P: ResolverProvider<Resolver: CoseKeyResolver>,
20    {
21        VerifiableClaims::verify(self, params).await
22    }
23}
24
25impl<T> VerifiableClaims for DecodedCoseSign1<T> {
26    type Claims = UnsignedCoseSign1<T>;
27    type Proof = CoseSignatureBytes;
28
29    fn claims(&self) -> &Self::Claims {
30        &self.signing_bytes
31    }
32
33    fn proof(&self) -> &Self::Proof {
34        &self.signature
35    }
36}
37
38pub trait ValidateCoseHeader<P> {
39    fn validate_cose_headers(
40        &self,
41        _params: &P,
42        _protected: &ProtectedHeader,
43        _unprotected: &Header,
44    ) -> ClaimsValidity {
45        Ok(())
46    }
47}
48
49impl<P> ValidateCoseHeader<P> for () {}
50
51impl<E, T> ValidateClaims<E, CoseSignatureBytes> for UnsignedCoseSign1<T>
52where
53    T: ValidateClaims<E, CoseSignatureBytes> + ValidateCoseHeader<E>,
54{
55    fn validate_claims(&self, params: &E, signature: &CoseSignatureBytes) -> ClaimsValidity {
56        self.payload
57            .validate_cose_headers(params, &self.protected, &self.unprotected)?;
58        self.payload.validate_claims(params, signature)
59    }
60}
61
62impl<P, T> ValidateProof<P, UnsignedCoseSign1<T>> for CoseSignatureBytes
63where
64    P: ResolverProvider<Resolver: CoseKeyResolver>,
65{
66    async fn validate_proof<'a>(
67        &'a self,
68        params: &'a P,
69        claims: &'a UnsignedCoseSign1<T>,
70    ) -> Result<ProofValidity, ProofValidationError> {
71        let key = params
72            .resolver()
73            .fetch_public_cose_key(Some(&claims.protected.header.key_id))
74            .await?;
75
76        let signing_bytes = claims.tbs_data(&[]);
77
78        verify_bytes(
79            claims
80                .protected
81                .header
82                .alg
83                .as_ref()
84                .ok_or(ProofValidationError::MissingAlgorithm)?,
85            &key,
86            &signing_bytes,
87            &self.0,
88        )
89        .map(|b| {
90            if b {
91                Ok(())
92            } else {
93                Err(InvalidProof::Signature)
94            }
95        })
96        .map_err(Into::into)
97    }
98}
99
100#[derive(Debug, thiserror::Error)]
101pub enum CoseVerificationError {
102    #[error("unsupported COSE algorithm")]
103    UnsupportedAlgorithm(coset::Algorithm),
104
105    #[error(transparent)]
106    PublicKey(#[from] KeyDecodingError),
107
108    #[error(transparent)]
109    Verification(#[from] VerificationError),
110}
111
112impl From<CoseVerificationError> for ProofValidationError {
113    fn from(value: CoseVerificationError) -> Self {
114        match value {
115            CoseVerificationError::PublicKey(_) => Self::InvalidKey,
116            e => ProofValidationError::other(e),
117        }
118    }
119}
120
121/// Verify a signature using a COSE key and algorithm.
122pub fn verify_bytes(
123    algorithm: &coset::Algorithm,
124    key: &CoseKey,
125    signing_bytes: &[u8],
126    signature_bytes: &[u8],
127) -> Result<bool, CoseVerificationError> {
128    let instance = instantiate_algorithm(algorithm)
129        .ok_or_else(|| CoseVerificationError::UnsupportedAlgorithm(algorithm.clone()))?;
130    let public_key = key.decode_public()?;
131
132    public_key
133        .verify(instance, signing_bytes, signature_bytes)
134        .map_err(Into::into)
135}