I am having an issue using PBKDF2 with Webcrypto. I would like to use PBKDF2 with the SHA-1 algorithm. Currently, I have,
const ENCODING = "utf-8";
const HMACSHA1 = {name: "HMAC", "hash" : "SHA-1"};
const PBKDF2SHA1 = {name: "PBKDF2", "hash": "SHA-1"};
// str2binb takes a string and outputs an ArrayBuffer
async function pbkdf2_generate_key_from_string(string) { // Working
return crypto.subtle.importKey(
"raw",
str2binb(string),
PBKDF2SHA1,
false,
["deriveKey", "deriveBits"],
);
}
async function pbkdf2_derive_salted_key(key, salt, iterations) { // Not working
return crypto.subtle.deriveKey(
{
"name": "PBKDF2",
"salt": salt,
"iterations": iterations,
"hash": "SHA-1",
"length": 160
},
key,
{
"name": "HMAC"
"hash": "SHA-1",
"length": 160
},
true,
[ "encrypt", "decrypt"]
);
}
However I know I must be using it wrong, because it is a supported key derivation algorithm, and according to Mozilla's fantastic docs, under derivedKeyAlgorithm
HMAC is supported as well as HMAC-SHA1. I also have it working with AES-GCM
fairly straight forwardly.
The error message I am getting, when I try,
salt = b64binb("QSXCR+Q6sek8bf92"); // ArrayBuffer
key = await pbkdf2_generate_key_from_string("pencil");
x = await pbkdf2_derive_salted_key(key, salt, 4096)
is Uncaught DOMException: Cannot create a key using the specified key usages.
Note: I understand SHA1 is no longer recommended, this is for legacy support.
Note 2: It works when I replaced the above with,
async function pbkdf2_derive_salted_key(key, data, salt, iterations) { // Not working
return crypto.subtle.deriveKey(
{
"name": "PBKDF2",
salt: salt,
"iterations": iterations,
"hash": "SHA-1",
},
key,
{
"name": "AES-GCM",
"length": 256
},
true,
[ "encrypt", "decrypt"]
);
}
Thank you.
In
deriveKey
, the parameterkeyUsages
must be changed to["sign", "verify"]
. Then the code works (sinceb64binb
,str2binb
were not posted, the following code uses appropriate substitutes):With this key,
window.crypto.subtle.sign
creates a signature using HMAC-SHA1.Update:
Since in SubtleCrypto the use of a key is generally specified,
["encrypt", "decrypt"]
must be applied askeyUsages
parameter for AES-GCM and["sign", "verify"]
for HMAC-SHA.Why
["encrypt", "decrypt"]
for AES-GCM? AES-GCM is used for encrypting / decrypting messages: AES describes a block cipher (allowing the encryption of a single block) and GCM the mode of operation (allowing the encryption of more than a single block). GCM provides confidentiality, authenticity and integrity.And why
["sign", "verify"]
for HMAC-SHA? An HMAC is used for signing / verifying messages: Some modes of operation such as CBC only provide confidentiality. To additionally supply authenticity and integrity, the message can be signed with a MAC (e.g. an HMAC which is a specific type of MAC based on a cryptographic hash function e.g. one of the SHA family). The MAC is typically calculated from the ciphertext and not the plaintext (Encrypt-then-MAC). With regard to the SHA1 vulnerabilities, see for a comparison of HMAC-SHA1 and HMAC-SHA256 here and here.