1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (C) 2026 Matthew Jackson
//! Unit tests for the part of the `jwt` module that a caller outside the crate cannot reach.
//!
//! `JwtError` is returned by [`super::JwtConfig::sign_access_token`], which cannot fail for the
//! shapes this crate builds (a struct of strings and numbers always serializes, and ECDSA over a
//! loaded key always signs). The one path that CAN produce one is `unix_seconds`, which is
//! `pub(crate)`, so its refusal and its message are only testable from in here. The rest of the
//! module's surface is driven from `tests/jwt.rs` and `tests/jwt_key_identity.rs`.
//!
//! [`super::CompactJws::reject_unknown_crit`] is also driven directly from here, and for a
//! different reason: its three refusing arms are reached from four verifiers (DPoP proofs, client
//! assertions, RFC 9101 request objects), but only through headers those verifiers will build, so
//! the NON-ARRAY arm — a `crit` that is a string, a number, a null — is not reachable by any route
//! the end-to-end tests take. A check nothing exercises is a check nobody would notice being
//! deleted.
use *;
use Duration;
/// RFC 7519 section 2: a `NumericDate` counts seconds SINCE the epoch, so an instant before the
/// epoch has no representation at all. Refusing is the only correct answer; wrapping or saturating
/// would mint a token whose `iat` and `exp` are a fiction, and `exp` in particular is the only
/// thing standing between a leaked token and an unbounded lifetime.
/// One JWS with the given protected header, over a fixed payload and an unchecked signature.
///
/// `reject_unknown_crit` reads the header and nothing else, so a real signature would only make the
/// fixture harder to read: these tests are about what the parser refuses BEFORE anybody verifies.
/// RFC 7515 section 4.1.11 in full, arm by arm, because each arm is a separate refusal and the
/// interesting one is unreachable from any verifier's own tests.
///
/// The member's meaning is that the producer REQUIRES the recipient to understand the named header
/// parameters. This verifier implements no JWS extension, so:
///
/// - a `crit` naming anything is a refusal (RFC 8725 section 3.10 names this as an attack surface:
/// `"crit":["b64"]` with `"b64":false` means the payload was signed unencoded, so a verifier that
/// ignores the member verifies a different message from the one that was signed);
/// - an EMPTY `crit` is refused by 4.1.11 itself, whatever the recipient implements. A verifier that
/// only scanned for unrecognised names would accept it, having found none;
/// - a `crit` that is not an ARRAY is refused too. 4.1.11 fixes the type, and a header that gets it
/// wrong is one this server cannot evaluate the requirement of — which must mean refuse, not
/// ignore. `"crit":"b64"` is exactly what a producer writing the member by hand emits, and a
/// verifier matching only on the array shape would fall through it to a happy accept.