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.
