paseto icon indicating copy to clipboard operation
paseto copied to clipboard

v1.public tokens include base64-encoded 'null' footer when no footer is provided

Open rrrodzilla opened this issue 3 months ago • 0 comments

Hi! I maintain rusty_paseto, a Rust implementation of PASETO, and encountered an interoperability issue that appears to stem from footer handling in this library.

Issue Description

When generating v1.public tokens without a footer, this library produces 4-part tokens ending with .bnVsbA (base64url-encoded "null"):

v1.public.{payload}.{signature}.bnVsbA

According to the official PASETO v1 specification, tokens should be formatted as:

  • Empty footer: v1.public.{base64url(message||signature)} (3 parts)
  • Non-empty footer: v1.public.{base64url(message||signature)}.{base64url(footer)} (4 parts)

The spec explicitly states:

If f is: Empty: return "h || base64url(m || sig)"\n> If f is: Non-empty: return "h || base64url(m || sig) || ." || base64url(f)"

Reproduction

v1 := paseto.NewV1()
token, err := v1.Sign(privateKey, payloadJSON, nil)  // nil footer
// Results in: v1.public.{payload}.{signature}.bnVsbA

Impact

This creates interoperability issues with spec-compliant PASETO implementations that expect 3-part tokens when no footer is provided. The library does correctly verify tokens from other implementations (3-part tokens work fine), so this is specifically a generation issue.

Interoperability Test Results

I verified this bidirectionally between o1egl/paseto and rusty_paseto:

  • Go → Rust verification: ✗ Fails (spec-compliant implementations reject the extra footer component)
  • Rust → Go verification: ✓ Works (this library correctly handles 3-part tokens)

Suggested Fix

When the footer parameter is nil or empty, the library should generate 3-part tokens without appending the base64-encoded "null" footer component.

Happy to provide additional details or test cases if helpful. Thanks for maintaining this library!

rrrodzilla avatar Oct 05 '25 22:10 rrrodzilla