pkce.rs 1.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344
  1. //! Fuzz PKCE S256 derivation and verification (`crypto::pkce`).
  2. //!
  3. //! The input is a verifier line; anything after a newline is an arbitrary
  4. //! presented challenge. [`challenge_from_verifier`] accepts only the RFC 7636
  5. //! §4.1 verifier shape; when it derives a challenge, the same verifier must
  6. //! verify against it, and a one-character challenge tamper must fail.
  7. #![no_main]
  8. use d_oauth::crypto::pkce::{challenge_from_verifier, verify_s256};
  9. use libfuzzer_sys::fuzz_target;
  10. fuzz_target!(|data: &[u8]| {
  11. let Ok(text) = core::str::from_utf8(data) else {
  12. return;
  13. };
  14. let (verifier, presented) = match text.split_once('\n') {
  15. Some((verifier, presented)) => (verifier, Some(presented)),
  16. None => (text, None),
  17. };
  18. if let Ok(challenge) = challenge_from_verifier(verifier) {
  19. assert!(
  20. verify_s256(verifier, &challenge).is_ok(),
  21. "a derived S256 challenge did not verify"
  22. );
  23. // Flip one base64url character; the digest can no longer match.
  24. if let Some(first) = challenge.as_bytes().first().copied() {
  25. let replacement = if first == b'A' { "B" } else { "A" };
  26. let mut tampered = challenge.clone();
  27. tampered.replace_range(0..1, replacement);
  28. assert!(
  29. verify_s256(verifier, &tampered).is_err(),
  30. "a one-character tampered challenge verified"
  31. );
  32. }
  33. }
  34. // Exercise the verifier against an arbitrary presented challenge.
  35. if let Some(presented) = presented {
  36. let _ = verify_s256(verifier, presented);
  37. }
  38. });