ssi_data_integrity_core/signing/
jws.rs1use 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 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}