identity_credential/validator/jwt_credential_validation/
jwt_credential_validator_hybrid.rs1use identity_core::convert::FromJson;
5use identity_did::CoreDID;
6use identity_did::DIDUrl;
7use identity_document::document::CoreDocument;
8use identity_document::verifiable::JwsVerificationOptions;
9use identity_verification::jwk::CompositeJwk;
10use identity_verification::jwk::PostQuantumJwk;
11use identity_verification::jwk::TraditionalJwk;
12use identity_verification::jws::DecodedJws;
13use identity_verification::jws::JwsValidationItem;
14use identity_verification::jws::JwsVerifier;
15
16use super::CompoundCredentialValidationError;
17use super::DecodedJwtCredential;
18use super::JwtCredentialValidationOptions;
19use super::JwtCredentialValidatorUtils;
20use super::JwtValidationError;
21use super::SignerContext;
22use crate::credential::Credential;
23use crate::credential::CredentialJwtClaims;
24use crate::credential::Jwt;
25use crate::validator::FailFast;
26use crate::validator::JwtCredentialValidator;
27
28pub struct JwtCredentialValidatorHybrid<TRV, PQV>(TRV, PQV);
30
31impl<TRV: JwsVerifier, PQV: JwsVerifier> JwtCredentialValidatorHybrid<TRV, PQV> {
32 pub fn with_signature_verifiers(traditional_signature_verifier: TRV, pq_signature_verifier: PQV) -> Self {
35 Self(traditional_signature_verifier, pq_signature_verifier)
36 }
37
38 pub fn validate<DOC, T>(
62 &self,
63 credential_jwt: &Jwt,
64 issuer: &DOC,
65 options: &JwtCredentialValidationOptions,
66 fail_fast: FailFast,
67 ) -> Result<DecodedJwtCredential<T>, CompoundCredentialValidationError>
68 where
69 T: Clone + serde::Serialize + serde::de::DeserializeOwned,
70 DOC: AsRef<CoreDocument>,
71 {
72 let credential_token = self
73 .verify_signature(
74 credential_jwt,
75 std::slice::from_ref(issuer.as_ref()),
76 &options.verification_options,
77 )
78 .map_err(|err| CompoundCredentialValidationError {
79 validation_errors: [err].into(),
80 })?;
81
82 JwtCredentialValidator::<TRV>::validate_decoded_credential(
83 credential_token,
84 std::slice::from_ref(issuer.as_ref()),
85 options,
86 fail_fast,
87 )
88 }
89
90 pub fn verify_signature<DOC, T>(
107 &self,
108 credential: &Jwt,
109 trusted_issuers: &[DOC],
110 options: &JwsVerificationOptions,
111 ) -> Result<DecodedJwtCredential<T>, JwtValidationError>
112 where
113 T: Clone + serde::Serialize + serde::de::DeserializeOwned,
114 DOC: AsRef<CoreDocument>,
115 {
116 Self::verify_signature_with_verifiers(&self.0, &self.1, credential, trusted_issuers, options)
117 }
118
119 pub(crate) fn parse_composite_pk<'a, 'i, DOC>(
120 jws: &JwsValidationItem<'a>,
121 trusted_issuers: &'i [DOC],
122 options: &JwsVerificationOptions,
123 ) -> Result<(&'a CompositeJwk, DIDUrl), JwtValidationError>
124 where
125 DOC: AsRef<CoreDocument>,
126 'i: 'a,
127 {
128 let nonce: Option<&str> = options.nonce.as_deref();
129 if jws.nonce() != nonce {
131 return Err(JwtValidationError::JwsDecodingError(
132 identity_verification::jose::error::Error::InvalidParam("invalid nonce value"),
133 ));
134 }
135
136 let method_id: DIDUrl =
139 match &options.method_id {
140 Some(method_id) => method_id.clone(),
141 None => {
142 let kid: &str = jws.protected_header().and_then(|header| header.kid()).ok_or(
143 JwtValidationError::MethodDataLookupError {
144 source: None,
145 message: "could not extract kid from protected header",
146 signer_ctx: SignerContext::Issuer,
147 },
148 )?;
149
150 DIDUrl::parse(kid).map_err(|err| JwtValidationError::MethodDataLookupError {
152 source: Some(err.into()),
153 message: "could not parse kid as a DID Url",
154 signer_ctx: SignerContext::Issuer,
155 })?
156 }
157 };
158
159 let issuer: &CoreDocument = trusted_issuers
161 .iter()
162 .map(AsRef::as_ref)
163 .find(|issuer_doc| <CoreDocument>::id(issuer_doc) == method_id.did())
164 .ok_or(JwtValidationError::DocumentMismatch(SignerContext::Issuer))?;
165
166 issuer
168 .resolve_method(&method_id, options.method_scope)
169 .and_then(|method| method.data().composite_public_key())
170 .ok_or_else(|| JwtValidationError::MethodDataLookupError {
171 source: None,
172 message: "could not extract CompositePublicKey from a method identified by kid",
173 signer_ctx: SignerContext::Issuer,
174 })
175 .map(move |c: &CompositeJwk| (c, method_id))
176 }
177
178 fn verify_signature_with_verifiers<DOC, T>(
180 traditional_signature_verifier: &TRV,
181 pq_signature_verifier: &PQV,
182 credential: &Jwt,
183 trusted_issuers: &[DOC],
184 options: &JwsVerificationOptions,
185 ) -> Result<DecodedJwtCredential<T>, JwtValidationError>
186 where
187 T: Clone + serde::Serialize + serde::de::DeserializeOwned,
188 DOC: AsRef<CoreDocument>,
189 {
190 let decoded: JwsValidationItem<'_> = JwtCredentialValidator::<TRV>::decode(credential.as_str())?;
196
197 let (composite, method_id) = Self::parse_composite_pk(&decoded, trusted_issuers, options)?;
198
199 let credential_token = Self::verify_decoded_signature(
200 decoded,
201 composite.traditional_public_key(),
202 composite.pq_public_key(),
203 traditional_signature_verifier,
204 pq_signature_verifier,
205 )?;
206
207 let issuer_id: CoreDID = JwtCredentialValidatorUtils::extract_issuer(&credential_token.credential)?;
210 if &issuer_id != method_id.did() {
211 return Err(JwtValidationError::IdentifierMismatch {
212 signer_ctx: SignerContext::Issuer,
213 });
214 };
215 Ok(credential_token)
216 }
217
218 pub(crate) fn verify_signature_raw<'a>(
219 decoded: JwsValidationItem<'a>,
220 traditional_pk: &TraditionalJwk,
221 pq_pk: &PostQuantumJwk,
222 traditional_verifier: &TRV,
223 pq_verifier: &PQV,
224 ) -> Result<DecodedJws<'a>, JwtValidationError> {
225 decoded
226 .verify_hybrid(traditional_verifier, pq_verifier, traditional_pk, pq_pk)
227 .map_err(|err| JwtValidationError::Signature {
228 source: err,
229 signer_ctx: SignerContext::Issuer,
230 })
231 }
232
233 fn verify_decoded_signature<T>(
235 decoded: JwsValidationItem<'_>,
236 traditional_pk: &TraditionalJwk,
237 pq_pk: &PostQuantumJwk,
238 traditional_verifier: &TRV,
239 pq_verifier: &PQV,
240 ) -> Result<DecodedJwtCredential<T>, JwtValidationError>
241 where
242 T: Clone + serde::Serialize + serde::de::DeserializeOwned,
243 {
244 let DecodedJws { protected, claims, .. } =
246 Self::verify_signature_raw(decoded, traditional_pk, pq_pk, traditional_verifier, pq_verifier)?;
247
248 let credential_claims: CredentialJwtClaims<'_, T> =
249 CredentialJwtClaims::from_json_slice(&claims).map_err(|err| {
250 JwtValidationError::CredentialStructure(crate::Error::JwtClaimsSetDeserializationError(err.into()))
251 })?;
252
253 let custom_claims = credential_claims.custom.clone();
254
255 let credential: Credential<T> = credential_claims
257 .try_into_credential()
258 .map_err(JwtValidationError::CredentialStructure)?;
259
260 Ok(DecodedJwtCredential {
261 credential,
262 header: Box::new(protected),
263 custom_claims,
264 })
265 }
266}