Skip to content

Commit 5e5b9d3

Browse files
authored
Update: Use WebCrypto API for PBKDF2 (#92)
1 parent c327c04 commit 5e5b9d3

File tree

1 file changed

+106
-4
lines changed

1 file changed

+106
-4
lines changed

lib/algorithms/pbes2.js

+106-4
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,63 @@ function pbes2EncryptFN(hmac, kw) {
7070
return promise;
7171
};
7272

73-
// NOTE: WebCrypto API missing until there's better support
74-
var webcrypto = null;
73+
var webcrypto = function(key, pdata, props) {
74+
var salt = util.asBuffer(props.p2s || new Buffer(0), "base64url"),
75+
itrs = props.p2c || 0;
76+
77+
if (0 >= itrs) {
78+
return Promise.reject(new Error("invalid iteration count"));
79+
}
80+
if (8 > salt.length) {
81+
return Promise.reject(new Error("salt too small"));
82+
}
83+
salt = fixSalt(hmac, kw, salt);
84+
85+
var promise;
86+
87+
// STEP 1: derive shared key
88+
var hash = hmac.replace("HS", "SHA-");
89+
promise = Promise.resolve(key);
90+
promise = promise.then(function(keyval) {
91+
return helpers.subtleCrypto.importKey("raw", keyval, "PBKDF2", false, ["deriveKey"]);
92+
});
93+
promise = promise.then(function(key) {
94+
var mainAlgo = {
95+
name: "PBKDF2",
96+
salt: salt,
97+
iterations: itrs,
98+
hash: hash
99+
};
100+
var deriveAlgo = {
101+
name: "AES-KW",
102+
length: keyLen * 8
103+
};
104+
105+
return helpers.subtleCrypto.deriveKey(mainAlgo, key, deriveAlgo, true, ["wrapKey", "unwrapKey"]);
106+
});
107+
// STEP 2: encrypt cek
108+
promise = promise.then(function(dk) {
109+
// assume subtleCrypto for keywrap
110+
return Promise.all([
111+
helpers.subtleCrypto.importKey("raw", pdata, { name: "HMAC", hash: "SHA-256" }, true, ["sign"]),
112+
dk
113+
]);
114+
});
115+
promise = promise.then(function(keys) {
116+
return helpers.subtleCrypto.wrapKey("raw",
117+
keys[0], // key
118+
keys[1], // wrappingKey
119+
"AES-KW");
120+
});
121+
promise = promise.then(function(result) {
122+
result = new Buffer(result);
123+
124+
return {
125+
data: result
126+
};
127+
});
128+
return promise;
129+
};
75130

76131
var nodejs = function(key, pdata, props) {
77132
if (6 > helpers.nodeCrypto.pbkdf2.length) {
@@ -165,8 +220,55 @@ function pbes2DecryptFN(hmac, kw) {
165220
return promise;
166221
};
167222

168-
// NOTE: WebCrypto API missing until there's better support
169-
var webcrypto = null;
223+
var webcrypto = function(key, cdata, props) {
224+
props = props || {};
225+
226+
var salt = util.asBuffer(props.p2s || new Buffer(0), "base64url"),
227+
itrs = props.p2c || 0;
228+
229+
if (0 >= itrs) {
230+
return Promise.reject(new Error("invalid iteration count"));
231+
}
232+
233+
if (8 > salt.length) {
234+
return Promise.reject(new Error("salt too small"));
235+
}
236+
salt = fixSalt(hmac, kw, salt);
237+
238+
var hash = hmac.replace("HS", "SHA-");
239+
var promise;
240+
promise = Promise.resolve(key);
241+
promise = promise.then(function(keyval) {
242+
return helpers.subtleCrypto.importKey("raw", keyval, "PBKDF2", false, ["deriveKey"]);
243+
});
244+
promise = promise.then(function(key) {
245+
var mainAlgo = {
246+
name: "PBKDF2",
247+
salt: salt,
248+
iterations: itrs,
249+
hash: hash
250+
};
251+
var deriveAlgo = {
252+
name: "AES-KW",
253+
length: keyLen * 8
254+
};
255+
256+
return helpers.subtleCrypto.deriveKey(mainAlgo, key, deriveAlgo, true, ["wrapKey", "unwrapKey"]);
257+
});
258+
// STEP 2: decrypt cek
259+
promise = promise.then(function(key) {
260+
return helpers.subtleCrypto.unwrapKey("raw", cdata, key, "AES-KW", {name: "HMAC", hash: "SHA-256"}, true, ["sign"]);
261+
});
262+
promise = promise.then(function(result) {
263+
// unwrapped CryptoKey -- extract raw
264+
return helpers.subtleCrypto.exportKey("raw", result);
265+
});
266+
promise = promise.then(function(result) {
267+
result = new Buffer(result);
268+
return result;
269+
});
270+
return promise;
271+
};
170272

171273
var nodejs = function(key, cdata, props) {
172274
if (6 > helpers.nodeCrypto.pbkdf2.length) {

0 commit comments

Comments
 (0)