Use these test vectors to verify that your implementation produces correct output at each step. All vectors are derived from RFC 9474 Appendix A.1 (RSABSSA-SHA384-PSS-Randomized).
To reproduce blindedMsg exactly, your blind() implementation must accept
an injectable inv parameter (the modular inverse of the blinding factor
r). See RFC 9474 §4.2.
In production, inv is always generated randomly — only fix it for test
vector verification.
Use this public key for all test vector verification. Do not use in production.
The RFC 9474 test vectors reference raw key components (n, e, modLen) directly. Our docs use publicKey as the argument to blind() and finalize() — these components can be extracted from the public key PEM above using any standard RSA library.
These values are extracted from the test public key above and needed if your implementation works with raw key components rather than a PEM file.
blind()RSABSSA-SHA384-PSS-Randomized prepends a random 32-byte msg_prefix to the message before PSS encoding (prepared_msg = msg_prefix || msg).
Persona’s protocol does not use msg_prefix in production — the nonce in tokenInput already provides per-request randomness. The msg_prefix here is included solely to match the RFC 9474 Appendix A.1 test vectors exactly. It is straightforward to pass through to your blind() call for verification purposes.
Inputs
Construct preparedMsg by concatenating msg_prefix || msg, then call:
Expected output
finalize()Inputs
Call finalize with the same preparedMsg from Test 1:
Expected output
buildTokenInput()Parse this fixed WWW-Authenticate header and combine with the fixed nonce to produce the expected tokenInput.
Inputs
The decoded challenge JSON:
Extract mac from the decoded challenge and token-key-id from the header. Build tokenInput as:
Expected output