PASETO Version 1 is deprecated. Implementations SHOULD migrate to Version 3.
Given a message (m
) and a nonce (n
):
- Calculate HMAC-SHA384 of the message
m
withn
as the key. - Return the leftmost 32 bytes of step 1.
Given a message m
, key k
, and optional footer f
(which defaults to empty string):
- Before encrypting, first assert that the key being used is intended for use
with
v1.local
tokens, and has a length of 256 bits (32 bytes). See Algorithm Lucidity for more information. - Set header
h
tov1.local.
Note: This includes the trailing period. - Generate 32 random bytes from the OS's CSPRNG,
b
. - Calculate
GetNonce()
ofm
and theb
to get the nonce,n
.- This step is to ensure that an RNG failure does not result in a nonce-misuse 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 ofn
as the HKDF salt:Ek = hkdf_sha384( len = 32 ikm = k, info = "paseto-encryption-key", salt = n[0:16] ); Ak = hkdf_sha384( len = 32 ikm = k, info = "paseto-auth-key-for-aead", salt = n[0:16] );
- Encrypt the message using
AES-256-CTR
, usingEk
as the key and the rightmost 16 bytes ofn
as the nonce. We'll call thisc
:c = aes256ctr_encrypt( plaintext = m, nonce = n[16:] key = Ek );
- Pack
h
,n
,c
, andf
together using PAE (pre-authentication encoding). We'll call thispreAuth
- Calculate HMAC-SHA384 of the output of
preAuth
, usingAk
as the authentication key. We'll call thist
. - If
f
is:- Empty: return "
h
|| base64url(n
||c
||t
)" - Non-empty: return "
h
|| base64url(n
||c
||t
) ||.
|| base64url(f
)" - ...where || means "concatenate"
- Note:
base64url()
means Base64url from RFC 4648 without=
padding.
- Empty: return "
Given a message m
, key k
, and optional footer f
(which defaults to empty string):
-
Before decrypting, first assert that the key being used is intended for use with
v1.local
tokens, and has a length of 256 bits (32 bytes). See Algorithm Lucidity for more information. -
If
f
is not empty, implementations MAY verify that the value appended to the token matches some expected stringf
, provided they do so using a constant-time string compare function. -
Verify that the message begins with
v1.local.
, otherwise throw an exception. This constant will be referred to ash
.
Note: This includes the trailing period. -
Decode the payload (
m
sansh
,f
, and the optional trailing period betweenm
andf
) from base64url to raw binary. Set:n
to the leftmost 32 bytest
to the rightmost 48 bytesc
to the middle remainder of the payload, excludingn
andt
-
Split the key (
k
) into an Encryption key (Ek
) and an Authentication key (Ak
), using the leftmost 16 bytes ofn
as the HKDF salt.- For encryption keys, the info parameter for HKDF MUST be set to paseto-encryption-key.
- For authentication keys, the info parameter for HKDF MUST be set to paseto-auth-key-for-aead.
- The output length MUST be 32 for both keys.
Ek = hkdf_sha384( len = 32 ikm = k, info = "paseto-encryption-key", salt = n[0:16] ); Ak = hkdf_sha384( len = 32 ikm = k, info = "paseto-auth-key-for-aead", salt = n[0:16] );
-
Pack
h
,n
,c
, andf
together (in that order) using PAE. We'll call thispreAuth
. -
Recalculate HMAC-SHA-384 of
preAuth
usingAk
as the key. We'll call thist2
. -
Compare
t
witht2
using a constant-time string compare function. If they are not identical, throw an exception. -
Decrypt
c
usingAES-256-CTR
, usingEk
as the key and the rightmost 16 bytes ofn
as the nonce, and return this value.return aes256ctr_decrypt( cipherext = c, nonce = n[16:] key = Ek );
Given a message m
, 2048-bit RSA secret key sk
, and
optional footer f
(which defaults to empty string):
- Before signing, first assert that the key being used is intended for use
with
v1.public
tokens, and is the secret key of the intended keypair. See Algorithm Lucidity for more information. - Set
h
tov1.public.
Note: This includes the trailing period. - Pack
h
,m
, andf
together using PAE (pre-authentication encoding). We'll call thism2
. - Sign
m2
using RSA with the private keysk
. We'll call thissig
.Only the above parameters are supported. PKCS1v1.5 is explicitly forbidden.sig = crypto_sign_rsa( message = m2, private_key = sk, padding_mode = "pss", public_exponent = 65537, hash = "sha384" mgf = "mgf1+sha384" );
- If
f
is:- Empty: return "
h
|| base64url(m
||sig
)" - Non-empty: return "
h
|| base64url(m
||sig
) ||.
|| base64url(f
)" - ...where || means "concatenate"
- Note:
base64url()
means Base64url from RFC 4648 without=
padding.
- Empty: return "
Given a signed message sm
, RSA public key pk
, and optional
footer f
(which defaults to empty string):
- Before verifying, first assert that the key being used is intended for use
with
v1.public
tokens, and is the public key of the intended keypair. See Algorithm Lucidity for more information. - If
f
is not empty, implementations MAY verify that the value appended to the token matches some expected stringf
, provided they do so using a constant-time string compare function. - Verify that the message begins with
v1.public.
, otherwise throw an exception. This constant will be referred to ash
.
Note: This includes the trailing period. - Decode the payload (
sm
sansh
,f
, and the optional trailing period betweenm
andf
) from base64url to raw binary. Set:s
to the rightmost 256 bytesm
to the leftmost remainder of the payload, excludings
- Pack
h
,m
, andf
together (in that order) using PAE (see PAE. We'll call thism2
. - 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.