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 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
121pub 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}