DownloadPaseto Version 1
GetNonce
Given a message (m ) and a nonce (n ):
 Calculate HMACSHA384 of the message
m with n as the key.
 Return the leftmost 32 bytes of step 1.
Encrypt
Given a message m , key k , and optional footer f
(which defaults to empty string):
 Set header
h to v1.local.
 Generate 32 random bytes from the OS's CSPRNG.
 Calculate
GetNonce() of m and the output of step 2
to get the nonce, n .
* This step is to ensure that an RNG failure does not result
in a noncemisuse condition that breaks the security of
our stream cipher.
 Split the key into an Encryption key (
Ek ) and Authentication key (Ak ),
using the leftmost 16 bytes of n as the HKDF salt:
`
Ek = hkdf_sha384(
len = 32
ikm = k,
info = "pasetoencryptionkey",
salt = n[0:16]
);
Ak = hkdf_sha384(
len = 32
ikm = k,
info = "pasetoauthkeyforaead",
salt = n[0:16]
);
`
 Encrypt the message using
AES256CTR , using Ek as the key and
the rightmost 16 bytes of n as the nonce. We'll call this c :
`
c = aes256ctr_encrypt(
plaintext = m,
nonce = n[16:]
key = Ek
);
`
 Pack
h , n , c , and f together using
PAE
(preauthentication encoding). We'll call this preAuth
 Calculate HMACSHA384 of the output of
preAuth , using Ak as the
authentication key. We'll call this t .
 If
f is:
* Empty: return "h  base64url(n  c  t )"
* Nonempty: return "h  base64url(n  c  t )  .  base64url(f )"
* ...where  means "concatenate"
* Note: base64url() means Base64url from RFC 4648 without = padding.
Decrypt
Given a message m , key k , and optional footer f
(which defaults to empty string):
 If
f is not empty, implementations MAY verify that the value appended
to the token matches some expected string f , provided they do so using a
constanttime string compare function.
 Verify that the message begins with
v1.local. , otherwise throw an
exception. This constant will be referred to as h .
 Decode the payload (
m sans h , f , and the optional trailing period
between m and f ) from b64 to raw binary. Set:
* n to the leftmost 32 bytes
* t to the rightmost 48 bytes
* c to the middle remainder of the payload, excluding n and t
Split the key (k ) into an Encryption key (Ek ) and an Authentication key
(Ak ), using the leftmost 16 bytes of n as the HKDF salt.
For encryption keys, theinfoparameter for HKDFMUST* be set to
pasetoencryptionkey.
For authentication keys, theinfoparameter for HKDFMUST* be set to
pasetoauthkeyforaead.
The output lengthMUST* be 32 for both keys.
`
Ek = hkdf_sha384(
len = 32
ikm = k,
info = "pasetoencryptionkey",
salt = n[0:16]
);
Ak = hkdf_sha384(
len = 32
ikm = k,
info = "pasetoauthkeyforaead",
salt = n[0:16]
);
`
 Pack
h , n , c , and f together (in that order) using
PAE.
We'll call this preAuth .
 Recalculate HMACSHA384 of
preAuth using Ak as the key. We'll call this
t2 .
 Compare
t with t2 using a constanttime string compare function. If they
are not identical, throw an exception.
 Decrypt
c using AES256CTR , using Ek as the key and the rightmost 16
bytes of n as the nonce, and return this value.
`
return aes256ctr_decrypt(
cipherext = c,
nonce = n[16:]
key = Ek
);
`
Sign
Given a message m , 2048bit RSA secret key sk , and
optional footer f (which defaults to empty string):
 Set
h to v1.public.
 Pack
h , m , and f together using
PAE
(preauthentication encoding). We'll call this m2 .
 Sign
m2 using RSA with the private key sk . We'll call this sig .
`
sig = crypto_sign_rsa(
message = m2,
private_key = sk,
padding_mode = "pss",
public_exponent = 65537,
hash = "sha384"
mgf = "mgf1+sha384"
);
`
Only the above parameters are supported. PKCS1v1.5 is explicitly forbidden.
 If
f is:
* Empty: return "h  base64url(m  sig )"
* Nonempty: return "h  base64url(m  sig )  .  base64url(f )"
* ...where  means "concatenate"
* Note: base64url() means Base64url from RFC 4648 without = padding.
Verify
Given a signed message sm , RSA public key pk , and optional
footer f (which defaults to empty string):
 If
f is not empty, implementations MAY verify that the value appended
to the token matches some expected string f , provided they do so using a
constanttime string compare function.
 Verify that the message begins with
v1.public. , otherwise throw an
exception. This constant will be referred to as h .
 Decode the payload (
sm sans h , f , and the optional trailing period
between m and f ) from b64 to raw binary. Set:
* s to the rightmost 256 bytes
* m to the leftmost remainder of the payload, excluding s
 Pack
h , m , and f together (in that order) using PAE (see
PAE.
We'll call this m2 .
 Use RSA to verify that the signature is valid for the message:
`
valid = crypto_sign_rsa_verify(
signature = s,
message = m2,
public_key = pk,
padding_mode = "pss",
public_exponent = 65537,
hash = "sha384"
mgf = "mgf1+sha384"
);
`
 If the signature is valid, return
m . Otherwise, throw an exception.
