codecs: add base64 URL encoding without paddin
What
Adds serializeBase64UrlNoPadding() methods to embed biscuit tokens in URL parameters without padding characters safely (=).
Why
Padding characters in base64-encoded tokens can cause issues when used in URLs. This was mentioned in code comments about needing custom encoders to prevent Java's default padding in URL contexts.
How
- Added serializeBase64UrlNoPadding() to Biscuit and UnverifiedBiscuit classes
- Uses Java's built-in Base64.getUrlEncoder().withoutPadding()
- Existing fromBase64Url() already handles both formats automatically
- Updated docs to clarify that padded/unpadded strings are both accepted
Example
String token = biscuit.serializeBase64UrlNoPadding();
String url = "https://api.example.com/auth?token=" + token;
// No special encoding needed - works directly in URLs
Fully backward compatible, no breaking changes.
We need to check the compatibility with the other library. https://github.com/eclipse-biscuit/biscuit-rust/issues/211 https://github.com/eclipse-biscuit/biscuit-rust/pull/213.
The implementation are not all coherent https://eprint.iacr.org/2022/361.pdf
@Korbik, I did not test all libraries, but biscuit-cli (so biscuit-rust) can decode it.
I really recommend reading the paper to get a good understanding of the issues. One thing we want to avoid is malleability (changing the base64 representation without changing the underlying data). This is an issue if intermediaries want to compare base64 contents directly. The most obvious way to do that is to manipulate padding characters. Another less obvious way is to manipulate unused bits (eg swapping a Q with an R, see the paper for how this works).
Another thing we want to avoid is breaking compatibility with other libraries.
To give you a quick summary:
- the spec mandates base64 url for text representations (but does not say anything about padding)
- currently, all (i think) implementations emit padded base64
rust
- the rust implementation uses
base64 0.13with theURL_SAFEconfig, which accepts both padded and unpadded versions when decoding - recent
base64versions (0.20+) mandate the presence of padding characters, so when we update it, unpadded versions will be rejected - there is also the stricter
base64-ctin the dependency tree, which does not treat padding as optional. we might want to use it directly to reduce the dependency footprint
Go
- the go implementation (last time i checked) does not treat padding as optional
haskell
- the haskell implementation uses a version of
base64with optional padding
conclusion
- malleability is not directly an issue for biscuits, since we use hex encoding for things where we need strict equality (pubkeys, revocation ids)
- The ecosystem seems to be moving towards explicit padding, to avoid malleability issues. We should not rely on libraries treating padding as optional.
- base64 is hopeless
- we could add encoding and decoding functions with explicit no_padding behaviour, but:
- for base64, the biscuit ecosystem is more or less set on padding
- an alternative to base64 with stricter semantics and no chance of overlap would be good