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.credential,
84 std::slice::from_ref(issuer.as_ref()),
85 options,
86 fail_fast,
87 )?;
88
89 Ok(credential_token)
90 }
91
92 pub fn verify_signature<DOC, T>(
109 &self,
110 credential: &Jwt,
111 trusted_issuers: &[DOC],
112 options: &JwsVerificationOptions,
113 ) -> Result<DecodedJwtCredential<T>, JwtValidationError>
114 where
115 T: Clone + serde::Serialize + serde::de::DeserializeOwned,
116 DOC: AsRef<CoreDocument>,
117 {
118 Self::verify_signature_with_verifiers(&self.0, &self.1, credential, trusted_issuers, options)
119 }
120
121 pub(crate) fn parse_composite_pk<'a, 'i, DOC>(
122 jws: &JwsValidationItem<'a>,
123 trusted_issuers: &'i [DOC],
124 options: &JwsVerificationOptions,
125 ) -> Result<(&'a CompositeJwk, DIDUrl), JwtValidationError>
126 where
127 DOC: AsRef<CoreDocument>,
128 'i: 'a,
129 {
130 let nonce: Option<&str> = options.nonce.as_deref();
131 if jws.nonce() != nonce {
133 return Err(JwtValidationError::JwsDecodingError(
134 identity_verification::jose::error::Error::InvalidParam("invalid nonce value"),
135 ));
136 }
137
138 let method_id: DIDUrl =
141 match &options.method_id {
142 Some(method_id) => method_id.clone(),
143 None => {
144 let kid: &str = jws.protected_header().and_then(|header| header.kid()).ok_or(
145 JwtValidationError::MethodDataLookupError {
146 source: None,
147 message: "could not extract kid from protected header",
148 signer_ctx: SignerContext::Issuer,
149 },
150 )?;
151
152 DIDUrl::parse(kid).map_err(|err| JwtValidationError::MethodDataLookupError {
154 source: Some(err.into()),
155 message: "could not parse kid as a DID Url",
156 signer_ctx: SignerContext::Issuer,
157 })?
158 }
159 };
160
161 let issuer: &CoreDocument = trusted_issuers
163 .iter()
164 .map(AsRef::as_ref)
165 .find(|issuer_doc| <CoreDocument>::id(issuer_doc) == method_id.did())
166 .ok_or(JwtValidationError::DocumentMismatch(SignerContext::Issuer))?;
167
168 issuer
170 .resolve_method(&method_id, options.method_scope)
171 .and_then(|method| method.data().composite_public_key())
172 .ok_or_else(|| JwtValidationError::MethodDataLookupError {
173 source: None,
174 message: "could not extract CompositePublicKey from a method identified by kid",
175 signer_ctx: SignerContext::Issuer,
176 })
177 .map(move |c: &CompositeJwk| (c, method_id))
178 }
179
180 fn verify_signature_with_verifiers<DOC, T>(
182 traditional_signature_verifier: &TRV,
183 pq_signature_verifier: &PQV,
184 credential: &Jwt,
185 trusted_issuers: &[DOC],
186 options: &JwsVerificationOptions,
187 ) -> Result<DecodedJwtCredential<T>, JwtValidationError>
188 where
189 T: Clone + serde::Serialize + serde::de::DeserializeOwned,
190 DOC: AsRef<CoreDocument>,
191 {
192 let decoded: JwsValidationItem<'_> = JwtCredentialValidator::<TRV>::decode(credential.as_str())?;
198
199 let (composite, method_id) = Self::parse_composite_pk(&decoded, trusted_issuers, options)?;
200
201 let credential_token = Self::verify_decoded_signature(
202 decoded,
203 composite.traditional_public_key(),
204 composite.pq_public_key(),
205 traditional_signature_verifier,
206 pq_signature_verifier,
207 )?;
208
209 let issuer_id: CoreDID = JwtCredentialValidatorUtils::extract_issuer(&credential_token.credential)?;
212 if &issuer_id != method_id.did() {
213 return Err(JwtValidationError::IdentifierMismatch {
214 signer_ctx: SignerContext::Issuer,
215 });
216 };
217 Ok(credential_token)
218 }
219
220 pub(crate) fn verify_signature_raw<'a>(
221 decoded: JwsValidationItem<'a>,
222 traditional_pk: &TraditionalJwk,
223 pq_pk: &PostQuantumJwk,
224 traditional_verifier: &TRV,
225 pq_verifier: &PQV,
226 ) -> Result<DecodedJws<'a>, JwtValidationError> {
227 decoded
228 .verify_hybrid(traditional_verifier, pq_verifier, traditional_pk, pq_pk)
229 .map_err(|err| JwtValidationError::Signature {
230 source: err,
231 signer_ctx: SignerContext::Issuer,
232 })
233 }
234
235 fn verify_decoded_signature<T>(
237 decoded: JwsValidationItem<'_>,
238 traditional_pk: &TraditionalJwk,
239 pq_pk: &PostQuantumJwk,
240 traditional_verifier: &TRV,
241 pq_verifier: &PQV,
242 ) -> Result<DecodedJwtCredential<T>, JwtValidationError>
243 where
244 T: Clone + serde::Serialize + serde::de::DeserializeOwned,
245 {
246 let DecodedJws { protected, claims, .. } =
248 Self::verify_signature_raw(decoded, traditional_pk, pq_pk, traditional_verifier, pq_verifier)?;
249
250 let credential_claims: CredentialJwtClaims<'_, T> =
251 CredentialJwtClaims::from_json_slice(&claims).map_err(|err| {
252 JwtValidationError::CredentialStructure(crate::Error::JwtClaimsSetDeserializationError(err.into()))
253 })?;
254
255 let custom_claims = credential_claims.custom.clone();
256
257 let credential: Credential<T> = credential_claims
259 .try_into_credential()
260 .map_err(JwtValidationError::CredentialStructure)?;
261
262 Ok(DecodedJwtCredential {
263 credential,
264 header: Box::new(protected),
265 custom_claims,
266 })
267 }
268}