rittenhop-dev/versions/5.94.2/node_modules/node-jose/lib/algorithms/pbes2.js

262 lines
6.2 KiB
JavaScript
Raw Normal View History

2024-09-23 19:40:12 -04:00
/*!
* algorithms/pbes2.js - Password-Based Encryption (v2) Algorithms
*
* Copyright (c) 2015 Cisco Systems, Inc. See LICENSE file.
*/
"use strict";
var forge = require("../deps/forge.js"),
merge = require("../util/merge.js"),
util = require("../util"),
helpers = require("./helpers.js"),
CONSTANTS = require("./constants.js"),
KW = require("./aes-kw.js");
var NULL_BUFFER = Buffer.from([0]);
var DEFAULT_ITERATIONS = 8192;
var DEFAULT_SALT_LENGTH = 16;
function fixSalt(hmac, kw, salt) {
var alg = "PBES2-" + hmac + "+" + kw;
var output = [
Buffer.from(alg, "utf8"),
NULL_BUFFER,
salt
];
return Buffer.concat(output);
}
function pbkdf2Fn(hash) {
function prepareProps(props) {
props = props || {};
var keyLen = props.length || 0;
var salt = util.asBuffer(props.salt || Buffer.alloc(0), "base64u4l"),
itrs = props.iterations || 0;
if (0 >= keyLen) {
throw new Error("invalid key length");
}
if (0 >= itrs) {
throw new Error("invalid iteration count");
}
props.length = keyLen;
props.salt = salt;
props.iterations = itrs;
return props;
}
var fallback = function(key, props) {
try {
props = prepareProps(props);
} catch (err) {
return Promise.reject(err);
}
var keyLen = props.length,
salt = props.salt,
itrs = props.iterations;
var promise = new Promise(function(resolve, reject) {
var md = forge.md[hash.replace("-", "").toLowerCase()].create();
var cb = function(err, dk) {
if (err) {
reject(err);
} else {
dk = Buffer.from(dk, "binary");
resolve(dk);
}
};
forge.pkcs5.pbkdf2(key.toString("binary"),
salt.toString("binary"),
itrs,
keyLen,
md,
cb);
});
return promise;
};
var webcrypto = function(key, props) {
try {
props = prepareProps(props);
} catch (err) {
return Promise.reject(err);
}
var keyLen = props.length,
salt = props.salt,
itrs = props.iterations;
var promise = Promise.resolve(key);
promise = promise.then(function(keyval) {
return helpers.subtleCrypto.importKey("raw", keyval, "PBKDF2", false, ["deriveBits"]);
});
promise = promise.then(function(key) {
var mainAlgo = {
name: "PBKDF2",
salt: new Uint8Array(salt),
iterations: itrs,
hash: hash
};
return helpers.subtleCrypto.deriveBits(mainAlgo, key, keyLen * 8);
});
promise = promise.then(function(result) {
return util.asBuffer(result);
});
return promise;
};
var nodejs = function(key, props) {
if (6 > helpers.nodeCrypto.pbkdf2.length) {
throw new Error("unsupported algorithm: PBKDF2-" + hash);
}
try {
props = prepareProps(props);
} catch (err) {
return Promise.reject(err);
}
var keyLen = props.length,
salt = props.salt,
itrs = props.iterations;
var md = hash.replace("-", "");
var promise = new Promise(function(resolve, reject) {
function cb(err, dk) {
if (err) {
reject(err);
} else {
resolve(dk);
}
}
helpers.nodeCrypto.pbkdf2(key, salt, itrs, keyLen, md, cb);
});
return promise;
};
return helpers.setupFallback(nodejs, webcrypto, fallback);
}
function pbes2EncryptFN(hmac, kw) {
var deriveAlg = "PBKDF2-" + hmac.replace("HS", "SHA-");
var keyLen = CONSTANTS.KEYLENGTH[kw] / 8;
return function(key, pdata, props) {
props = props || {};
var salt = util.asBuffer(props.p2s || Buffer.alloc(0), "base64url"),
itrs = props.p2c || DEFAULT_ITERATIONS;
if (0 >= itrs) {
throw new Error("invalid iteration count");
}
if (0 === salt.length) {
salt = util.randomBytes(DEFAULT_SALT_LENGTH);
} else if (8 > salt.length) {
throw new Error("salt too small");
}
var header = {
p2s: util.base64url.encode(salt),
p2c: itrs
};
salt = fixSalt(hmac, kw, salt);
props = merge(props, {
salt: salt,
iterations: itrs,
length: keyLen
});
var promise = Promise.resolve(key);
// STEP 1: derive shared key
promise = promise.then(function (key) {
return pbes2[deriveAlg].derive(key, props);
});
// STEP 2: encrypt cek
promise = promise.then(function (dk) {
return KW[kw].encrypt(dk, pdata);
});
// STEP 3: (re-)apply headers
promise = promise.then(function (results) {
results.header = merge(results.header || {}, header);
return results;
});
return promise;
};
}
function pbes2DecryptFN(hmac, kw) {
var deriveAlg = "PBKDF2-" + hmac.replace("HS", "SHA-");
var keyLen = CONSTANTS.KEYLENGTH[kw] / 8;
return function(key, cdata, props) {
props = props || {};
var salt = util.asBuffer(props.p2s || Buffer.alloc(0), "base64url"),
itrs = props.p2c || 0;
if (0 >= itrs) {
return Promise.reject(new Error("invalid iteration count"));
}
if (8 > salt.length) {
return Promise.reject(new Error("salt too small"));
}
salt = fixSalt(hmac, kw, salt);
props = merge(props, {
salt: salt,
iterations: itrs,
length: keyLen
});
var promise = Promise.resolve(key);
// STEP 1: derived shared key
promise = promise.then(function(key) {
return pbes2[deriveAlg].derive(key, props);
});
// STEP 2: decrypt cek
promise = promise.then(function(dk) {
return KW[kw].decrypt(dk, cdata);
});
return promise;
};
}
// ### Public API
var pbes2 = {};
// * [name].derive
[
"PBKDF2-SHA-256",
"PBKDF2-SHA-384",
"PBKDF2-SHA-512"
].forEach(function(alg) {
var hash = alg.replace("PBKDF2-", "");
pbes2[alg] = {
derive: pbkdf2Fn(hash)
};
});
// [name].encrypt
// [name].decrypt
[
"PBES2-HS256+A128KW",
"PBES2-HS384+A192KW",
"PBES2-HS512+A256KW"
].forEach(function(alg) {
var parts = /PBES2-(HS\d+)\+(A\d+KW)/g.exec(alg);
var hmac = parts[1],
kw = parts[2];
pbes2[alg] = {
encrypt: pbes2EncryptFN(hmac, kw),
decrypt: pbes2DecryptFN(hmac, kw)
};
});
module.exports = pbes2;