1use core::str;
5use std::borrow::Cow;
6
7use crate::error::Error;
8use crate::error::Result;
9use crate::jwk::CompositeAlgId;
10use crate::jwk::Jwk;
11use crate::jwk::PostQuantumJwk;
12use crate::jwk::TraditionalJwk;
13use crate::jws::JwsAlgorithm;
14use crate::jws::JwsHeader;
15use crate::jwu::create_message;
16use crate::jwu::decode_b64;
17use crate::jwu::decode_b64_json;
18use crate::jwu::filter_non_empty_bytes;
19use crate::jwu::parse_utf8;
20use crate::jwu::validate_jws_headers;
21
22use super::JwsVerifier;
23use super::VerificationInput;
24
25#[derive(Clone, Debug, PartialEq, Eq)]
29#[non_exhaustive]
30pub struct DecodedJws<'a> {
31 pub protected: JwsHeader,
33 pub unprotected: Option<Box<JwsHeader>>,
35 pub claims: Cow<'a, [u8]>,
37}
38
39enum DecodedHeaders {
40 Protected(JwsHeader),
41 Unprotected(JwsHeader),
42 Both {
43 protected: JwsHeader,
44 unprotected: Box<JwsHeader>,
46 },
47}
48
49impl DecodedHeaders {
50 fn new(protected: Option<JwsHeader>, unprotected: Option<JwsHeader>) -> Result<Self> {
51 match (protected, unprotected) {
52 (Some(protected), Some(unprotected)) => Ok(Self::Both {
53 protected,
54 unprotected: Box::new(unprotected),
55 }),
56 (Some(protected), None) => Ok(Self::Protected(protected)),
57 (None, Some(unprotected)) => Ok(Self::Unprotected(unprotected)),
58 (None, None) => Err(Error::MissingHeader("no headers were decoded")),
59 }
60 }
61
62 fn protected_header(&self) -> Option<&JwsHeader> {
63 match self {
64 DecodedHeaders::Protected(ref header) => Some(header),
65 DecodedHeaders::Both { ref protected, .. } => Some(protected),
66 DecodedHeaders::Unprotected(_) => None,
67 }
68 }
69
70 fn unprotected_header(&self) -> Option<&JwsHeader> {
71 match self {
72 DecodedHeaders::Unprotected(ref header) => Some(header),
73 DecodedHeaders::Both { ref unprotected, .. } => Some(unprotected.as_ref()),
74 DecodedHeaders::Protected(_) => None,
75 }
76 }
77}
78
79pub struct JwsValidationItem<'a> {
83 headers: DecodedHeaders,
84 signing_input: Box<[u8]>,
85 decoded_signature: Box<[u8]>,
86 claims: Cow<'a, [u8]>,
87}
88impl<'a> JwsValidationItem<'a> {
89 pub fn protected_header(&self) -> Option<&JwsHeader> {
91 self.headers.protected_header()
92 }
93
94 pub fn nonce(&self) -> Option<&str> {
96 self.protected_header().and_then(|header| header.nonce())
97 }
98
99 pub fn kid(&self) -> Option<&str> {
101 self.protected_header().and_then(|header| header.kid())
102 }
103
104 pub fn unprotected_header(&self) -> Option<&JwsHeader> {
106 self.headers.unprotected_header()
107 }
108
109 pub fn alg(&self) -> Option<JwsAlgorithm> {
111 self.protected_header().and_then(|protected| protected.alg())
112 }
113
114 pub fn claims(&self) -> &[u8] {
116 &self.claims
117 }
118
119 pub fn signing_input(&self) -> &[u8] {
124 &self.signing_input
125 }
126
127 pub fn decoded_signature(&self) -> &[u8] {
129 &self.decoded_signature
130 }
131
132 pub fn verify<T>(self, verifier: &T, public_key: &Jwk) -> Result<DecodedJws<'a>>
144 where
145 T: JwsVerifier,
146 {
147 let JwsValidationItem {
149 headers,
150 claims,
151 signing_input,
152 decoded_signature,
153 } = self;
154 let (protected, unprotected): (JwsHeader, Option<Box<JwsHeader>>) = match headers {
155 DecodedHeaders::Protected(protected) => (protected, None),
156 DecodedHeaders::Both { protected, unprotected } => (protected, Some(unprotected)),
157 DecodedHeaders::Unprotected(_) => return Err(Error::MissingHeader("missing protected header")),
158 };
159
160 let alg: JwsAlgorithm = protected.alg().ok_or(Error::ProtectedHeaderWithoutAlg)?;
162 public_key.check_alg(alg.name())?;
163
164 let input = VerificationInput {
166 alg,
167 signing_input,
168 decoded_signature,
169 };
170 verifier
172 .verify(input, public_key)
173 .map_err(Error::SignatureVerificationError)?;
174
175 Ok(DecodedJws {
176 protected,
177 unprotected,
178 claims,
179 })
180 }
181
182 pub fn verify_hybrid<TRV, PQV>(
184 self,
185 traditional_verifier: &TRV,
186 pq_verifier: &PQV,
187 traditional_pk: &TraditionalJwk,
188 pq_pk: &PostQuantumJwk,
189 ) -> Result<DecodedJws<'a>>
190 where
191 TRV: JwsVerifier,
192 PQV: JwsVerifier,
193 {
194 let JwsValidationItem {
196 headers,
197 claims,
198 signing_input,
199 decoded_signature,
200 } = self;
201
202 let (protected, unprotected): (JwsHeader, Option<Box<JwsHeader>>) = match headers {
203 DecodedHeaders::Protected(protected) => (protected, None),
204 DecodedHeaders::Both { protected, unprotected } => (protected, Some(unprotected)),
205 DecodedHeaders::Unprotected(_) => return Err(Error::MissingHeader("missing protected header")),
206 };
207
208 let alg: JwsAlgorithm = protected.alg().ok_or(Error::ProtectedHeaderWithoutAlg)?;
210
211 let domain = match alg {
212 JwsAlgorithm::IdMldsa44Ed25519 => CompositeAlgId::IdMldsa44Ed25519.domain(),
213 JwsAlgorithm::IdMldsa65Ed25519 => CompositeAlgId::IdMldsa65Ed25519.domain(),
214 _ => return Err(Error::JwsAlgorithmParsingError),
215 };
216
217 let mut input = CompositeAlgId::COMPOSITE_SIGNATURE_PREFIX.to_vec();
220
221 input.extend_from_slice(domain);
223
224 input.push(0x00);
226
227 input.extend(signing_input);
229
230 traditional_pk.check_alg(JwsAlgorithm::EdDSA.name())?;
231
232 let (extracted_signature_t, extracted_signature_pq) =
233 decoded_signature.split_at(crypto::signatures::ed25519::Signature::LENGTH);
234
235 let input1 = VerificationInput {
237 alg: JwsAlgorithm::EdDSA,
238 signing_input: input.clone().into(),
239 decoded_signature: extracted_signature_t.into(),
240 };
241
242 traditional_verifier
244 .verify(input1, traditional_pk)
245 .map_err(Error::SignatureVerificationError)?;
246
247 let input2 = VerificationInput {
248 alg,
249 signing_input: input.into(),
250 decoded_signature: extracted_signature_pq.into(),
251 };
252
253 pq_verifier
255 .verify(input2, pq_pk)
256 .map_err(Error::SignatureVerificationError)?;
257
258 Ok(DecodedJws {
259 protected,
260 unprotected,
261 claims,
262 })
263 }
264}
265
266#[derive(serde::Deserialize)]
270#[serde(deny_unknown_fields)]
271struct JwsSignature<'a> {
272 header: Option<JwsHeader>,
273 protected: Option<&'a str>,
274 signature: &'a str,
275}
276
277#[derive(serde::Deserialize)]
278#[serde(deny_unknown_fields)]
279struct General<'a> {
280 payload: Option<&'a str>,
281 signatures: Vec<JwsSignature<'a>>,
282}
283
284#[derive(serde::Deserialize)]
285#[serde(deny_unknown_fields)]
286struct Flatten<'a> {
287 payload: Option<&'a str>,
288 #[serde(flatten)]
289 signature: JwsSignature<'a>,
290}
291
292#[derive(Debug, Clone)]
298pub struct Decoder;
299
300impl Decoder {
301 pub fn new() -> Decoder {
303 Self
304 }
305
306 pub fn decode_compact_serialization<'b>(
314 &self,
315 jws_bytes: &'b [u8],
316 detached_payload: Option<&'b [u8]>,
317 ) -> Result<JwsValidationItem<'b>> {
318 let mut segments = jws_bytes.split(|byte| *byte == b'.');
319
320 let (Some(protected), Some(payload), Some(signature), None) =
321 (segments.next(), segments.next(), segments.next(), segments.next())
322 else {
323 return Err(Error::InvalidContent("invalid segments count"));
324 };
325
326 let signature: JwsSignature<'_> = JwsSignature {
327 header: None,
328 protected: Some(parse_utf8(protected)?),
329 signature: parse_utf8(signature)?,
330 };
331
332 let payload = Self::expand_payload(detached_payload, Some(payload))?;
333
334 self.decode_signature(payload, signature)
335 }
336
337 pub fn decode_flattened_serialization<'b>(
344 &self,
345 jws_bytes: &'b [u8],
346 detached_payload: Option<&'b [u8]>,
347 ) -> Result<JwsValidationItem<'b>> {
348 let data: Flatten<'_> = serde_json::from_slice(jws_bytes).map_err(Error::InvalidJson)?;
349 let payload = Self::expand_payload(detached_payload, data.payload)?;
350 let signature = data.signature;
351 self.decode_signature(payload, signature)
352 }
353
354 fn decode_signature<'a, 'b>(
355 &self,
356 payload: &'b [u8],
357 jws_signature: JwsSignature<'a>,
358 ) -> Result<JwsValidationItem<'b>> {
359 let JwsSignature {
360 header: unprotected_header,
361 protected,
362 signature,
363 } = jws_signature;
364
365 let protected_header: Option<JwsHeader> = protected.map(decode_b64_json).transpose()?;
366 validate_jws_headers(protected_header.as_ref(), unprotected_header.as_ref())?;
367
368 let protected_bytes: &[u8] = protected.map(str::as_bytes).unwrap_or_default();
369 let signing_input: Box<[u8]> = create_message(protected_bytes, payload).into();
370 let decoded_signature: Box<[u8]> = decode_b64(signature)?.into();
371
372 let claims: Cow<'b, [u8]> = if protected_header.as_ref().and_then(|value| value.b64()).unwrap_or(true) {
373 Cow::Owned(decode_b64(payload)?)
374 } else {
375 Cow::Borrowed(payload)
376 };
377
378 Ok(JwsValidationItem {
379 headers: DecodedHeaders::new(protected_header, unprotected_header)?,
380 signing_input,
381 decoded_signature,
382 claims,
383 })
384 }
385
386 fn expand_payload<'b>(
387 detached_payload: Option<&'b [u8]>,
388 parsed_payload: Option<&'b (impl AsRef<[u8]> + ?Sized)>,
389 ) -> Result<&'b [u8]> {
390 match (detached_payload, filter_non_empty_bytes(parsed_payload)) {
391 (Some(payload), None) => Ok(payload),
392 (None, Some(payload)) => Ok(payload),
393 (Some(_), Some(_)) => Err(Error::InvalidContent("multiple payloads")),
394 (None, None) => Err(Error::InvalidContent("missing payload")),
395 }
396 }
397}
398
399pub struct JwsValidationIter<'decoder, 'payload, 'signatures> {
406 decoder: &'decoder Decoder,
407 signatures: std::vec::IntoIter<JwsSignature<'signatures>>,
408 payload: &'payload [u8],
409}
410
411impl<'payload> Iterator for JwsValidationIter<'_, 'payload, '_> {
412 type Item = Result<JwsValidationItem<'payload>>;
413
414 fn next(&mut self) -> Option<Self::Item> {
415 self
416 .signatures
417 .next()
418 .map(|signature| self.decoder.decode_signature(self.payload, signature))
419 }
420}
421
422impl Decoder {
423 pub fn decode_general_serialization<'decoder, 'data>(
430 &'decoder self,
431 jws_bytes: &'data [u8],
432 detached_payload: Option<&'data [u8]>,
433 ) -> Result<JwsValidationIter<'decoder, 'data, 'data>> {
434 let data: General<'data> = serde_json::from_slice(jws_bytes).map_err(Error::InvalidJson)?;
435
436 let payload = Self::expand_payload(detached_payload, data.payload)?;
437 let signatures = data.signatures;
438
439 Ok(JwsValidationIter {
440 decoder: self,
441 payload,
442 signatures: signatures.into_iter(),
443 })
444 }
445}
446
447impl Default for Decoder {
448 fn default() -> Self {
449 Self::new()
450 }
451}
452
453#[cfg(test)]
454mod tests {
455 use crate::jwt::JwtClaims;
456
457 use super::*;
458
459 const RFC_7515_APPENDIX_EXAMPLE_CLAIMS: &str = r#"
460 {
461 "iss":"joe",
462 "exp":1300819380,
463 "http://example.com/is_root":true
464 }
465 "#;
466
467 const SIGNING_INPUT_ES256_RFC_7515_APPENDIX_EXAMPLE: &[u8] = &[
468 101, 121, 74, 104, 98, 71, 99, 105, 79, 105, 74, 70, 85, 122, 73, 49, 78, 105, 74, 57, 46, 101, 121, 74, 112, 99,
469 51, 77, 105, 79, 105, 74, 113, 98, 50, 85, 105, 76, 65, 48, 75, 73, 67, 74, 108, 101, 72, 65, 105, 79, 106, 69,
470 122, 77, 68, 65, 52, 77, 84, 107, 122, 79, 68, 65, 115, 68, 81, 111, 103, 73, 109, 104, 48, 100, 72, 65, 54, 76,
471 121, 57, 108, 101, 71, 70, 116, 99, 71, 120, 108, 76, 109, 78, 118, 98, 83, 57, 112, 99, 49, 57, 121, 98, 50, 57,
472 48, 73, 106, 112, 48, 99, 110, 86, 108, 102, 81,
473 ];
474
475 #[test]
477 fn rfc7515_appendix_a_6() {
478 let general_jws_json_serialized: &str = r#"
479 {
480 "payload": "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ",
481 "signatures": [
482 {
483 "protected": "eyJhbGciOiJSUzI1NiJ9",
484 "header": {
485 "kid": "2010-12-29"
486 },
487 "signature": "cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqvhJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrBp0igcN_IoypGlUPQGe77Rw"
488 },
489 {
490 "protected": "eyJhbGciOiJFUzI1NiJ9",
491 "header": {
492 "kid": "e9bc097a-ce51-4036-9562-d2ade882db0d"
493 },
494 "signature": "DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSApmWQxfKTUJqPP3-Kg6NU1Q"
495 }
496 ]
497 }"#;
498
499 let claims: JwtClaims<serde_json::Value> = serde_json::from_str(RFC_7515_APPENDIX_EXAMPLE_CLAIMS).unwrap();
500
501 let decoder = Decoder::new();
502
503 let mut signature_iter = decoder
504 .decode_general_serialization(general_jws_json_serialized.as_bytes(), None)
505 .unwrap()
506 .filter_map(|decoded| decoded.ok());
507
508 let first_signature_decoding = signature_iter.next().unwrap();
510 let second_signature_decoding = signature_iter.next().unwrap();
511 drop(signature_iter);
512
513 assert_eq!(first_signature_decoding.alg().unwrap(), JwsAlgorithm::RS256);
515 assert_eq!(
516 first_signature_decoding
517 .unprotected_header()
518 .and_then(|value| value.kid())
519 .unwrap(),
520 "2010-12-29"
521 );
522 let decoded_claims: JwtClaims<serde_json::Value> =
523 serde_json::from_slice(first_signature_decoding.claims()).unwrap();
524 assert_eq!(claims, decoded_claims);
525
526 assert_eq!(second_signature_decoding.alg().unwrap(), JwsAlgorithm::ES256);
528 assert_eq!(
529 second_signature_decoding
530 .unprotected_header()
531 .and_then(|value| value.kid())
532 .unwrap(),
533 "e9bc097a-ce51-4036-9562-d2ade882db0d"
534 );
535
536 let decoded_claims: JwtClaims<serde_json::Value> =
537 serde_json::from_slice(second_signature_decoding.claims()).unwrap();
538 assert_eq!(decoded_claims, claims);
539 assert_eq!(
540 SIGNING_INPUT_ES256_RFC_7515_APPENDIX_EXAMPLE,
541 second_signature_decoding.signing_input()
542 );
543 }
544
545 #[test]
547 fn rfc7515_appendix_a_7() {
548 let flattened_jws_json_serialized: &str = r#"
549 {
550 "payload": "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ",
551 "protected":"eyJhbGciOiJFUzI1NiJ9",
552 "header": {"kid":"e9bc097a-ce51-4036-9562-d2ade882db0d"},
553 "signature": "DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSApmWQxfKTUJqPP3-Kg6NU1Q"
554 }
555 "#;
556
557 let claims: JwtClaims<serde_json::Value> = serde_json::from_str(RFC_7515_APPENDIX_EXAMPLE_CLAIMS).unwrap();
558 let decoder = Decoder::new();
559 let decoded = decoder
560 .decode_flattened_serialization(flattened_jws_json_serialized.as_bytes(), None)
561 .unwrap();
562 assert_eq!(decoded.alg().unwrap(), JwsAlgorithm::ES256);
563 assert_eq!(
564 decoded.unprotected_header().and_then(|value| value.kid()).unwrap(),
565 "e9bc097a-ce51-4036-9562-d2ade882db0d"
566 );
567
568 assert_eq!(decoded.signing_input(), SIGNING_INPUT_ES256_RFC_7515_APPENDIX_EXAMPLE);
569 let decoded_claims: JwtClaims<serde_json::Value> = serde_json::from_slice(decoded.claims()).unwrap();
570 assert_eq!(decoded_claims, claims);
571 }
572}