Derive a unique encryption key from a password, then use it to wrap the key.
crypto_generichash() is BLAKE2b with a variable-length output that defaults to 256 bits (32 bytes), as implemented in libsodium.
long2bytes() converts a 64-bit unsigned integer into a sequence of 8 octets (big-endian byte order).
int2bytes() converts a 32-bit unsigned integer into a sequence of 4 octets (big-endian byte order).
Algorithms: PBKDF2, HMAC-SHA384, AES-256-CTR, SHA-384
The header h
will depend on the exact version and mode being used
(with a trailing period). It will be one of
(k1.local-pw.
, k1.secret-pw.
, k3.local-pw.
, k3.secret-pw.
).
Given a plaintext key (ptk
), password (pw
), and iteration
count (i
, defaults to 100,000):
- Generate a random 256-bit (32 byte) salt (
s
). - Derive the 256-bit (32 byte) pre-key
k
from the password and salt.k = PBKDF2-SHA384(pw, s, i)
- Derive the encryption key (
Ek
) fromSHA-384(0xFF || k)
, truncated to the 32 most significant bytes (Ek = hash[0:31]
). - Derive the authentication key (
Ak
) fromSHA-384(0xFE || k)
. - Generate a random 128-bit nonce (
n
). - Encrypt the plaintext key
ptk
withEk
andn
to obtain the encrypted data keyedk
.edk = AES-256-CTR(msg=ptk, key=Ek, nonce=n)
- Calculate the authentication tag
t
overh
,s
,i
,n
, andedk
.t = HMAC-SHA-384( msg = h || s || int2bytes(i) || n || edk, key = Ak )
- Return
h
,s
,i
,n
,edk
,t
.
Given a password (pw
), salt (s
), iteration count (i
), nonce (n
),
encrypted data key (edk
), and authentication tag t
:
- Assert that the header
h
is correct for the expected version of the wrapped key. - Derive the pre-key
k
from the password and salt.k = PBKDF2-SHA384(pw, s, i)
- Derive the authentication key (
Ak
) fromSHA-384(0xFE || k)
. - Recalculate the authentication tag
t2
overh
,s
,i
,n
, andedk
.t2 = HMAC-SHA-384( msg = h || s || int2bytes(i) || n || edk, key = Ak )
- Compare
t
witht2
using a constant-time string comparison function. If it fails, abort. - Derive the encryption key (
Ek
) fromSHA-384(0xFF || k)
. - Decrypt the encrypted key (
edk
) withEk
andn
to obtain the plaintext keyptk
.ptk = AES-256-CTR(msg=edk, key=Ek, nonce=n)
- Return
ptk
Algorithms: Argon2id, BLAKE2b, XChaCha20
The header h
will depend on the exact version and mode being used
(with a trailing period). It will be one of
(k2.local-pw.
, k2.secret-pw.
, k4.local-pw.
, k4.secret-pw.
).
Given a plaintext key (ptk
), password (pw
), memory cost (mem
),
time cost (time
), and parallelism degree (para
):
- Generate a random 128-bit (16 byte) salt (
s
). - Derive the 256-bit (32 byte) pre-key
k
from the password and salt.k = Argon2id(pw, s, mem, time, para)
- Derive the encryption key (
Ek
) fromcrypto_generichash(0xFF || k)
. - Derive the authentication key (
Ak
) fromcrypto_generichash(0xFE || k)
. - Generate a random 192-bit (24 byte) nonce (
n
). - Encrypt the plaintext key (
ptk
) withEk
andn
to obtain the encrypted data keyedk
.edk = XChaCha20(msg=ptk, key=Ek, nonce=n)
- Calculate the authentication tag
t
overh
,s
,mem
,time
,para
,n
, andedk
.t = crypto_generichash( msg = h || s || long2bytes(mem) || int2bytes(time) || int2bytes(para) || n || edk, key = Ak, length = 32 # 32 bytes, 256 bits )
- Return
h
,s
,mem
,time
,para
,n
,edk
,t
.
Given a password (pw
), salt (s
), memory cost (mem
),
time cost (time
), parallelism degree (para
), nonce (n
),
encrypted data key (edk
), and authentication tag t
:
- Assert that the header
h
is correct for the expected version of the wrapped key. - Derive the pre-key
k
from the password and salt.k = Argon2id(pw, s, mem, time, para)
- Derive the authentication key (
Ak
) fromcrypto_generichash(0xFE || k)
. - Recalculate the authentication tag
t2
overh
,s
,mem
,time
,para
,n
, andedk
.t2 = crypto_generichash( msg = h || s || long2bytes(mem) || int2bytes(time) || int2bytes(para) || n || edk, key = Ak, length = 32 # 32 bytes, 256 bits )
- Compare
t
witht2
using a constant-time string comparison function. If it fails, abort. - Derive the encryption key (
Ek
) fromcrypto_generichash(0xFF || k)
. - Decrypt the encrypted key (
edk
) withEk
andn
to obtain the plaintext keyptk
.ptk = XChaCha20(msg=edk, key=Ek, nonce=n)
- Return
ptk
.