Skip to content

Commit f68d46d

Browse files
committed
docs(changelog): add second-audit security fixes
Documents the three findings from the follow-up audit that we shipped in this release: - Symmetric set_no_expiration_danger_acknowledged on the parser side (previously asymmetric; parser silently accepted tokens with no exp). - Removed panic-on-bad-input in Key::<N>::from(&[u8]) and PasetoAsymmetricPrivateKey::<V2|V4, Public>::from(&[u8]); both replaced by fallible TryFrom that returns PasetoError::InvalidKey. Also added a "Breaking changes" section listing the three behavioural breaks: stricter default exp enforcement, Key TryFrom replacing From, and PasetoAsymmetricPrivateKey TryFrom replacing From for V2/V4. All three are mechanical migrations for downstream callers.
1 parent c20e130 commit f68d46d

1 file changed

Lines changed: 28 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ crate against the PASETO specification and current advisory database.
1515
torsion components and non-canonical R encodings, eliminating ed25519
1616
signature malleability. All 56 default tests and the official PASETO v2/v4
1717
public test vectors continue to pass.
18+
- **Default parser now rejects tokens missing the `exp` claim.** A second-look
19+
audit highlighted an asymmetry: the builder required an explicit
20+
`set_no_expiration_danger_acknowledged()` to mint non-expiring tokens, but
21+
the parser silently accepted them. The parser is now symmetric — an absent,
22+
null, or non-string `exp` returns `PasetoClaimError::Missing("exp")`. Use
23+
the new `PasetoParser::set_no_expiration_danger_acknowledged()` to opt in
24+
when you genuinely want to accept non-expiring tokens.
1825
- **Token and footer size limits.** New constants `Paseto::MAX_TOKEN_SIZE`
1926
(64 KiB) and `Paseto::MAX_FOOTER_SIZE` (1024 bytes, per PASETO spec
2027
recommendation) are enforced in both `parse_raw_token` and
@@ -27,6 +34,12 @@ crate against the PASETO specification and current advisory database.
2734
`Zeroize` + `ZeroizeOnDrop`. The root `PasetoSymmetricKey` (`Key<N>`) was
2835
already zeroized; this closes the gap for per-message Ek/Ak material derived
2936
via HKDF (v1/v3) and BLAKE2b (v4).
37+
- **Eliminated two public-API panic-on-bad-input paths.**
38+
`Key::<N>::from(&[u8])` and `PasetoAsymmetricPrivateKey::<V2|V4, Public>::from(&[u8])`
39+
previously called `copy_from_slice` into fixed-size buffers, panicking the
40+
thread on size mismatch. Both now use `TryFrom<&[u8]>` returning
41+
`PasetoError::InvalidKey`. The infallible array-typed `From` impls remain
42+
for callers that already have a correctly-sized array.
3043

3144
### Dependencies
3245

@@ -40,6 +53,21 @@ crate against the PASETO specification and current advisory database.
4053
RFC 7519 §4.1.5. Previously the strict `<=` check rejected tokens for one
4154
instant at the boundary.
4255

56+
### Breaking changes
57+
58+
- `PasetoParser::default()` now rejects tokens missing `exp` by default;
59+
callers that want non-expiring tokens must add
60+
`.set_no_expiration_danger_acknowledged()` on the parser (mirroring the
61+
existing builder method).
62+
- `Key::<N>::from(&[u8])` removed. Callers using `Key::<N>::from(slice)`
63+
with a slice (rather than an array) must switch to
64+
`Key::<N>::try_from(slice)?`. The array-typed `From<[u8; N]>` and
65+
`From<&[u8; N]>` impls are unchanged.
66+
- `PasetoAsymmetricPrivateKey::<V2|V4, Public>::from(&[u8])` is now
67+
`TryFrom<&[u8]>`. Callers using `from(slice)` must switch to
68+
`try_from(slice)?`. V1 (RSA, arbitrary-length) and V3 (Key<48>-typed)
69+
are unaffected.
70+
4371
### Spec compliance
4472

4573
- **PAE `le64`** now masks the high bit (`n &= 0x7FFF_FFFF_FFFF_FFFF`) for

0 commit comments

Comments
 (0)