Skip to main content

ssi_data_integrity_core/signing/
jws.rs

1use std::{borrow::Cow, marker::PhantomData};
2
3use ssi_claims_core::{ProofValidationError, SignatureError};
4use ssi_crypto::algorithm::{SignatureAlgorithmInstance, SignatureAlgorithmType};
5use ssi_jwk::{Algorithm, JWK};
6use ssi_jws::{DecodedJws, JwsSignature, JwsString};
7use ssi_verification_methods::{MessageSigner, VerifyBytes, VerifyBytesWithRecoveryJwk};
8
9use crate::{
10    suite::standard::{
11        SignatureAlgorithm, SignatureAndVerificationAlgorithm, VerificationAlgorithm,
12    },
13    CryptographicSuite, ProofConfigurationRef, ProofRef,
14};
15
16use super::AlgorithmSelection;
17
18#[derive(
19    Debug,
20    Clone,
21    serde::Serialize,
22    serde::Deserialize,
23    linked_data::Serialize,
24    linked_data::Deserialize,
25)]
26#[ld(prefix("sec" = "https://w3id.org/security#"))]
27pub struct DetachedJwsSignature {
28    #[ld("sec:jws")]
29    pub jws: JwsString,
30}
31
32impl DetachedJwsSignature {
33    pub fn new(jws: JwsString) -> Self {
34        Self { jws }
35    }
36
37    /// Decodes the signature for the given message.
38    ///
39    /// Returns the signing bytes, the signature bytes and the signature algorithm.
40    pub fn decode(
41        &self,
42        message: &[u8],
43    ) -> Result<(Vec<u8>, JwsSignature, Algorithm), ProofValidationError> {
44        let DecodedJws {
45            signing_bytes: detached_signing_bytes,
46            signature,
47        } = self
48            .jws
49            .decode()
50            .map_err(|_| ProofValidationError::InvalidSignature)?;
51        let signing_bytes = detached_signing_bytes.header.encode_signing_bytes(message);
52        Ok((
53            signing_bytes,
54            signature,
55            detached_signing_bytes.header.algorithm,
56        ))
57    }
58
59    pub async fn sign_detached<A: SignatureAlgorithmType + Into<Algorithm>, S: MessageSigner<A>>(
60        payload: &[u8],
61        signer: S,
62        key_id: Option<String>,
63        algorithm_instance: A::Instance,
64    ) -> Result<Self, SignatureError> {
65        let header = ssi_jws::Header::new_unencoded(algorithm_instance.algorithm().into(), key_id);
66        let signing_bytes = header.encode_signing_bytes(payload);
67        let signature = signer.sign(algorithm_instance, &signing_bytes).await?;
68        let jws = ssi_jws::JwsString::encode_detached(header, &signature);
69        Ok(Self::new(jws))
70    }
71}
72
73impl AsRef<str> for DetachedJwsSignature {
74    fn as_ref(&self) -> &str {
75        self.jws.as_str()
76    }
77}
78
79impl super::AlterSignature for DetachedJwsSignature {
80    fn alter(&mut self) {
81        self.jws = JwsString::from_string(format!("ff{}", self.jws)).unwrap();
82    }
83}
84
85pub struct DetachedJwsSigning<A>(PhantomData<A>);
86
87impl<A> SignatureAndVerificationAlgorithm for DetachedJwsSigning<A> {
88    type Signature = DetachedJwsSignature;
89}
90
91impl<A, S, T> SignatureAlgorithm<S, T> for DetachedJwsSigning<A>
92where
93    S: CryptographicSuite,
94    S::PreparedClaims: AsRef<[u8]>,
95    A: SignatureAlgorithmType
96        + AlgorithmSelection<S::VerificationMethod, S::ProofOptions>
97        + Into<Algorithm>,
98    T: MessageSigner<A>,
99{
100    async fn sign(
101        verification_method: &S::VerificationMethod,
102        signer: T,
103        prepared_claims: S::PreparedClaims,
104        proof_configuration: ProofConfigurationRef<'_, S>,
105    ) -> Result<Self::Signature, SignatureError> {
106        DetachedJwsSignature::sign_detached(
107            prepared_claims.as_ref(),
108            signer,
109            None,
110            A::select_algorithm(verification_method, proof_configuration.options)?,
111        )
112        .await
113    }
114}
115
116impl<A, S> VerificationAlgorithm<S> for DetachedJwsSigning<A>
117where
118    S: CryptographicSuite<Signature = DetachedJwsSignature>,
119    S::PreparedClaims: AsRef<[u8]>,
120    S::VerificationMethod: VerifyBytes<A>,
121    A: TryFrom<Algorithm>,
122{
123    fn verify(
124        method: &S::VerificationMethod,
125        prepared_claims: S::PreparedClaims,
126        proof: ProofRef<S>,
127    ) -> Result<ssi_claims_core::ProofValidity, ProofValidationError> {
128        let DecodedJws {
129            signing_bytes: detached_signing_bytes,
130            signature,
131        } = proof
132            .signature
133            .jws
134            .decode()
135            .map_err(|_| ProofValidationError::InvalidSignature)?;
136
137        let signing_bytes = detached_signing_bytes
138            .header
139            .encode_signing_bytes(prepared_claims.as_ref());
140
141        let algorithm = detached_signing_bytes
142            .header
143            .algorithm
144            .try_into()
145            .map_err(|_| ProofValidationError::InvalidSignature)?;
146
147        method.verify_bytes(algorithm, &signing_bytes, &signature)
148    }
149}
150
151pub struct DetachedJwsRecoverySigning<A>(PhantomData<A>);
152
153impl<A> SignatureAndVerificationAlgorithm for DetachedJwsRecoverySigning<A> {
154    type Signature = DetachedJwsSignature;
155}
156
157impl<A, S, T> SignatureAlgorithm<S, T> for DetachedJwsRecoverySigning<A>
158where
159    S: CryptographicSuite,
160    S::PreparedClaims: AsRef<[u8]>,
161    S::ProofOptions: RecoverPublicJwk,
162    A: Clone + Into<Algorithm> + AlgorithmSelection<S::VerificationMethod, S::ProofOptions>,
163    T: MessageSigner<A>,
164{
165    async fn sign(
166        verification_method: &S::VerificationMethod,
167        signer: T,
168        prepared_claims: S::PreparedClaims,
169        proof_configuration: ProofConfigurationRef<'_, S>,
170    ) -> Result<Self::Signature, SignatureError> {
171        DetachedJwsSignature::sign_detached(
172            prepared_claims.as_ref(),
173            signer,
174            proof_configuration.options.public_jwk().key_id.clone(),
175            A::select_algorithm(verification_method, proof_configuration.options)?,
176        )
177        .await
178    }
179}
180
181impl<A, S> VerificationAlgorithm<S> for DetachedJwsRecoverySigning<A>
182where
183    S: CryptographicSuite<Signature = DetachedJwsSignature>,
184    S::PreparedClaims: AsRef<[u8]>,
185    S::ProofOptions: RecoverPublicJwk,
186    S::VerificationMethod: VerifyBytesWithRecoveryJwk<A>,
187    A: TryFrom<ssi_jwk::Algorithm>,
188{
189    fn verify(
190        verification_method: &S::VerificationMethod,
191        prepared_claims: S::PreparedClaims,
192        proof: ProofRef<S>,
193    ) -> Result<ssi_claims_core::ProofValidity, ProofValidationError> {
194        let DecodedJws {
195            signing_bytes: detached_signing_bytes,
196            signature,
197        } = proof
198            .signature
199            .jws
200            .decode()
201            .map_err(|_| ProofValidationError::InvalidSignature)?;
202
203        let signing_bytes = detached_signing_bytes
204            .header
205            .encode_signing_bytes(prepared_claims.as_ref());
206
207        let found_algorithm = detached_signing_bytes
208            .header
209            .algorithm
210            .try_into()
211            .map_err(|_| ProofValidationError::InvalidSignature)?;
212
213        verification_method.verify_bytes_with_public_jwk(
214            &proof.options.public_jwk(),
215            found_algorithm,
216            &signing_bytes,
217            &signature,
218        )
219    }
220}
221
222pub trait RecoverPublicJwk {
223    fn public_jwk(&'_ self) -> Cow<'_, JWK>;
224}