DownloadPaseto Version 2
Encrypt
Given a message m , key k , and optional footer f .
-
Set header `h` to `v2.local.`
-
Generate 24 random bytes from the OS's CSPRNG.
-
Calculate BLAKE2b of the message `m` with the output of step 2 as the key,
with an output length of 24. This will be our 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.
-
Pack `h`, `n`, and `f` together (in that order) using
PAE.
We'll call this `preAuth`.
-
Encrypt the message using XChaCha20-Poly1305, using an AEAD interface such as
the one provided in libsodium.
c = crypto_aead_xchacha20poly1305_encrypt(
message = m
aad = preAuth
nonce = n
key = k
);
-
If `f` is:
* Empty: return h || b64(n || c)
* Non-empty: return h || b64(n || c) || `.` || base64url(f)
* ...where || means "concatenate"
Decrypt
Given a message m , key k , and optional footer f .
-
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
constant-time string compare function.
-
Verify that the message begins with `v2.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 base64url to raw binary. Set:
* `n` to the leftmost 24 bytes
* `c` to the middle remainder of the payload, excluding `n`.
-
Pack `h`, `n`, and `f` together (in that order) using
PAE.
We'll call this `preAuth`
-
Decrypt `c` using `XChaCha20-Poly1305`, store the result in `p`.
p = crypto_aead_xchacha20poly1305_decrypt(
ciphertext = c
aad = preAuth
nonce = n
key = k
);
-
If decryption failed, throw an exception. Otherwise, return `p`.
Sign
Given a message m , Ed25519 secret key sk , and
optional footer f (which defaults to empty string):
-
Set `h` to `v2.public.`
-
Pack `h`, `m`, and `f` together using
PAE
(pre-authentication encoding). We'll call this `m2`.
-
Sign `m2` using Ed25519 `sk`. We'll call this `sig`.
sig = crypto_sign_detached(
message = m2,
private_key = sk
);
-
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.
Verify
Given a signed message sm , 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
constant-time string compare function.
-
Verify that the message begins with `v2.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 base64url to raw binary. Set:
* `s` to the rightmost 64 bytes
* `m` to the leftmost remainder of the payload, excluding `s`
-
Pack `h`, `m`, and `f` together using
PAE.
We'll call this `m2`.
-
Use Ed25519 to verify that the signature is valid for the message:
valid = crypto_sign_verify_detached(
signature = s,
message = m2,
public_key = pk
);
-
If the signature is valid, return `m`. Otherwise, throw an exception.
|