nstdlib-nightly
Version:
Node.js standard library converted to runtime-agnostic ES modules.
182 lines (158 loc) • 4.76 kB
JavaScript
// Source: https://github.com/nodejs/node/blob/65eff1eb/lib/internal/crypto/mac.js
import {
HmacJob,
KeyObjectHandle,
kCryptoJobAsync,
kSignJobModeSign,
kSignJobModeVerify,
} from "nstdlib/stub/binding/crypto";
import {
getBlockSize,
hasAnyNotIn,
jobPromise,
normalizeHashName,
validateBitLength,
validateKeyOps,
kHandle,
kKeyObject,
} from "nstdlib/lib/internal/crypto/util";
import { lazyDOMException, promisify } from "nstdlib/lib/internal/util";
import { generateKey as _generateKey } from "nstdlib/lib/internal/crypto/keygen";
import {
InternalCryptoKey,
SecretKeyObject,
createSecretKey,
} from "nstdlib/lib/internal/crypto/keys";
const generateKey = promisify(_generateKey);
async function hmacGenerateKey(algorithm, extractable, keyUsages) {
const { hash, name } = algorithm;
let { length } = algorithm;
if (length === undefined) length = getBlockSize(hash.name);
validateBitLength(length, "algorithm.length", true);
const usageSet = new Set(keyUsages);
if (hasAnyNotIn(usageSet, ["sign", "verify"])) {
throw lazyDOMException(
"Unsupported key usage for an HMAC key",
"SyntaxError",
);
}
const key = await generateKey("hmac", { length }).catch((err) => {
throw lazyDOMException(
"The operation failed for an operation-specific reason",
{ name: "OperationError", cause: err },
);
});
return new InternalCryptoKey(
key,
{ name, length, hash: { name: hash.name } },
Array.from(usageSet),
extractable,
);
}
function getAlgorithmName(hash) {
switch (hash) {
case "SHA-1": // Fall through
case "SHA-256": // Fall through
case "SHA-384": // Fall through
case "SHA-512": // Fall through
return `HS${hash.slice(4)}`;
default:
throw lazyDOMException("Unsupported digest algorithm", "DataError");
}
}
async function hmacImportKey(
format,
keyData,
algorithm,
extractable,
keyUsages,
) {
const usagesSet = new Set(keyUsages);
if (hasAnyNotIn(usagesSet, ["sign", "verify"])) {
throw lazyDOMException(
"Unsupported key usage for an HMAC key",
"SyntaxError",
);
}
let keyObject;
switch (format) {
case "raw": {
const checkLength = keyData.byteLength * 8;
if (checkLength === 0 || algorithm.length === 0)
throw lazyDOMException("Zero-length key is not supported", "DataError");
// The Web Crypto spec allows for key lengths that are not multiples of
// 8. We don't. Our check here is stricter than that defined by the spec
// in that we require that algorithm.length match keyData.length * 8 if
// algorithm.length is specified.
if (algorithm.length !== undefined && algorithm.length !== checkLength) {
throw lazyDOMException("Invalid key length", "DataError");
}
keyObject = createSecretKey(keyData);
break;
}
case "jwk": {
if (!keyData.kty) throw lazyDOMException("Invalid keyData", "DataError");
if (keyData.kty !== "oct")
throw lazyDOMException('Invalid JWK "kty" Parameter', "DataError");
if (
usagesSet.size > 0 &&
keyData.use !== undefined &&
keyData.use !== "sig"
) {
throw lazyDOMException('Invalid JWK "use" Parameter', "DataError");
}
validateKeyOps(keyData.key_ops, usagesSet);
if (
keyData.ext !== undefined &&
keyData.ext === false &&
extractable === true
) {
throw lazyDOMException(
'JWK "ext" Parameter and extractable mismatch',
"DataError",
);
}
if (keyData.alg !== undefined) {
if (keyData.alg !== getAlgorithmName(algorithm.hash.name))
throw lazyDOMException(
'JWK "alg" does not match the requested algorithm',
"DataError",
);
}
const handle = new KeyObjectHandle();
handle.initJwk(keyData);
keyObject = new SecretKeyObject(handle);
break;
}
default:
throw lazyDOMException(`Unable to import HMAC key with format ${format}`);
}
const { length } = keyObject[kHandle].keyDetail({});
return new InternalCryptoKey(
keyObject,
{
name: "HMAC",
hash: algorithm.hash,
length,
},
keyUsages,
extractable,
);
}
function hmacSignVerify(key, data, algorithm, signature) {
const mode = signature === undefined ? kSignJobModeSign : kSignJobModeVerify;
return jobPromise(
() =>
new HmacJob(
kCryptoJobAsync,
mode,
normalizeHashName(key.algorithm.hash.name),
key[kKeyObject][kHandle],
data,
signature,
),
);
}
export { hmacImportKey };
export { hmacGenerateKey };
export { hmacSignVerify };