jar_claims.rs 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. //! Fuzz the JAR (request object) claims envelope plus the PAR/JAR
  2. //! mandatory-rule dispatch.
  3. //!
  4. //! The target splits the input into a faux `token_endpoint_auth_method` and a
  5. //! compact-JWT-shaped claims envelope. The envelope parser (three
  6. //! base64url segments, JSON header/payload objects) must never panic;
  7. //! `require_par_or_jar` pins the confidential-client dispatch. Full
  8. //! signature verification needs keys and stays under unit/proptest cover.
  9. #![no_main]
  10. use d_oauth::db::Client;
  11. use d_oauth::jose::jar::require_par_or_jar;
  12. use libfuzzer_sys::fuzz_target;
  13. fn envelope_invariants(compact: &str) {
  14. let mut parts = compact.split('.');
  15. let (Some(header_b64), Some(payload_b64), Some(sig_b64), None) =
  16. (parts.next(), parts.next(), parts.next(), parts.next())
  17. else {
  18. return;
  19. };
  20. if sig_b64.is_empty() {
  21. return;
  22. }
  23. use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD};
  24. let Ok(header_raw) = URL_SAFE_NO_PAD.decode(header_b64) else {
  25. return;
  26. };
  27. let Ok(payload_raw) = URL_SAFE_NO_PAD.decode(payload_b64) else {
  28. return;
  29. };
  30. let Ok(header) = serde_json::from_slice::<serde_json::Value>(&header_raw) else {
  31. return;
  32. };
  33. let Ok(payload) = serde_json::from_slice::<serde_json::Value>(&payload_raw) else {
  34. return;
  35. };
  36. // Header and payload are JSON objects; a request object names its
  37. // algorithm and carries claims. Presence (not validity) is the envelope
  38. // property the fuzzer pins.
  39. let _ = header.is_object() && payload.is_object();
  40. }
  41. fn client_with_method(method: &str) -> Client {
  42. Client {
  43. id: uuid::Uuid::nil(),
  44. client_id: "fuzz-client".to_owned(),
  45. client_secret_expires_at: None,
  46. client_id_issued_at: time::OffsetDateTime::UNIX_EPOCH,
  47. client_name: "fuzz".to_owned(),
  48. grant_types: vec!["authorization_code".to_owned()],
  49. response_types: vec!["code".to_owned()],
  50. scope: String::new(),
  51. token_endpoint_auth_method: method.to_owned(),
  52. jwks: None,
  53. jwks_uri: None,
  54. tls_client_auth_subject_dn: None,
  55. tls_client_auth_cert_thumbprint: None,
  56. request_uris: None,
  57. contacts: None,
  58. software_id: None,
  59. active: true,
  60. created_at: time::OffsetDateTime::UNIX_EPOCH,
  61. updated_at: time::OffsetDateTime::UNIX_EPOCH,
  62. }
  63. }
  64. fuzz_target!(|data: &[u8]| {
  65. let Ok(text) = core::str::from_utf8(data) else {
  66. return;
  67. };
  68. let (method, compact) = match text.split_once('\n') {
  69. Some((method, compact)) => (method, compact),
  70. None => ("none", text),
  71. };
  72. envelope_invariants(compact);
  73. let client = client_with_method(method);
  74. let _ = require_par_or_jar(&client);
  75. });