dpop_proof.rs 1.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940
  1. //! Fuzz the DPoP proof's embedded-JWK gate (`jose::jwk`).
  2. //!
  3. //! The proof JWK is self-asserted hostile input: [`decoding_key`] must refuse
  4. //! private/symmetric material, enforce the `alg`-to-key-type correspondence,
  5. //! and floor RSA moduli, while [`thumbprint`] canonicalizes (RFC 7638) only
  6. //! the public members. Both must never panic on any input; a JWK accepted by
  7. //! [`decoding_key`] always carries the members [`thumbprint`] needs.
  8. //! Signature verification and the `jti` replay claim need keys and Redis, so
  9. //! they stay under unit/proptest cover.
  10. #![no_main]
  11. use d_oauth::jose::jwk::{decoding_key, thumbprint};
  12. use jsonwebtoken::Algorithm;
  13. use libfuzzer_sys::fuzz_target;
  14. fuzz_target!(|data: &[u8]| {
  15. let Some((&selector, rest)) = data.split_first() else {
  16. return;
  17. };
  18. let alg = match selector % 3 {
  19. 0 => Algorithm::ES256,
  20. 1 => Algorithm::EdDSA,
  21. _ => Algorithm::RS256,
  22. };
  23. let Ok(text) = core::str::from_utf8(rest) else {
  24. return;
  25. };
  26. let Ok(jwk) = serde_json::from_str::<serde_json::Value>(text) else {
  27. return;
  28. };
  29. let key = decoding_key(alg, &jwk);
  30. let print = thumbprint(&jwk);
  31. // An accepted verification key always thumbprints: every path that builds
  32. // a `DecodingKey` already required the exact public members the canonical
  33. // form consumes.
  34. if key.is_ok() {
  35. debug_assert!(print.is_ok());
  36. }
  37. });