PASETO v1 — Legacy RSA Compatibility
Why PASETO v1 exists, what it uses (AES-256-CTR + HMAC-SHA384, RSA-PSS-2048), and why new systems should never choose v1.
Detailed Explanation
v1 was the original PASETO version and exists today purely as a legacy compatibility option. New systems should not use v1; if you're inspecting a v1 token, it's almost always part of a long-running service that pre-dates the v3/v4 designs.
Cryptography:
v1.local: AES-256-CTR for encryption, HMAC-SHA384 for authentication. The 48-byte trailing tag is the HMAC.v1.public: RSA-PSS with a 2048-bit key, MGF1-SHA384, salt length 48. The signature is 256 bytes.
Why v1 was designed this way:
When PASETO was first specified (2018), the goal was a JWT-equivalent that worked in legacy environments — places where ChaCha20 wasn't available, or where existing PKI infrastructure was already RSA. AES-CTR + HMAC was the maximally portable AEAD; RSA-PSS was the most widely-supported signature scheme. v1 traded modern primitives for compatibility.
Why v1 is now legacy:
Three problems: (1) AES-CTR + HMAC is encrypt-then-MAC, which is fine but slower and more error-prone to implement than a built-in AEAD like XChaCha20-Poly1305 or XChaCha20+BLAKE2b. (2) RSA-PSS-2048 signatures are 256 bytes — 4x larger than Ed25519's 64 bytes — and signing/verifying is much slower. (3) RSA key management is harder than Ed25519 (key generation is slow, keys are bulky).
Recognizing v1 tokens:
The header prefix v1.local or v1.public is the only reliable indicator. v1.public tokens are noticeably longer than v3/v4 tokens because of the 256-byte RSA signature. If a Base64url-decoded payload is at least ~340 characters longer than the same JSON would be plain, it's probably v1.public.
Migration to v3/v4:
Don't try to convert v1 tokens in place. Issue new v4 tokens via your normal auth flow, dual-accept v1 + v4 during a transition window, then stop issuing v1. Track v1 issuance and verification separately so you can confirm when the v1 path is dead before removing the legacy code.
Use Case
An ops team inspects a PASETO token from a 2018-era service and confirms it's v1.public — flagging it for migration to v4.public as part of the next platform refresh.