'use strict'; const { ArrayFrom, Promise, SafeSet, } = primordials; const { HmacJob, KeyObjectHandle, kCryptoJobAsync, kSignJobModeSign, kSignJobModeVerify, } = internalBinding('crypto'); const { getHashLength, hasAnyNotIn, jobPromise, lazyDOMException, normalizeHashName, validateBitLength, validateKeyOps, kHandle, kKeyObject, } = require('internal/crypto/util'); const { codes: { ERR_MISSING_OPTION, ERR_INVALID_ARG_TYPE, } } = require('internal/errors'); const { generateKey, } = require('internal/crypto/keygen'); const { InternalCryptoKey, SecretKeyObject, createSecretKey, isKeyObject, } = require('internal/crypto/keys'); async function hmacGenerateKey(algorithm, extractable, keyUsages) { const { hash, name } = algorithm; let { length } = algorithm; if (hash === undefined) throw new ERR_MISSING_OPTION('algorithm.hash'); if (length === undefined) length = getHashLength(hash.name); validateBitLength(length, 'algorithm.length', true); const usageSet = new SafeSet(keyUsages); if (hasAnyNotIn(usageSet, ['sign', 'verify'])) { throw lazyDOMException( 'Unsupported key usage for an HMAC key', 'SyntaxError'); } return new Promise((resolve, reject) => { generateKey('hmac', { length }, (err, key) => { if (err) { return reject(lazyDOMException( 'The operation failed for an operation-specific reason', 'OperationError')); } resolve(new InternalCryptoKey( key, { name, length, hash: { name: hash.name } }, ArrayFrom(usageSet), extractable)); }); }); } async function hmacImportKey( format, keyData, algorithm, extractable, keyUsages) { const { hash } = algorithm; if (hash === undefined) throw new ERR_MISSING_OPTION('algorithm.hash'); const usagesSet = new SafeSet(keyUsages); if (hasAnyNotIn(usagesSet, ['sign', 'verify'])) { throw lazyDOMException( 'Unsupported key usage for an HMAC key', 'SyntaxError'); } let keyObject; switch (format) { case 'node.keyObject': { if (!isKeyObject(keyData)) throw new ERR_INVALID_ARG_TYPE('keyData', 'KeyObject', keyData); if (keyData.type !== 'secret') { throw lazyDOMException( `Unable to import HMAC key with format ${format}`); } keyObject = keyData; break; } 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 == null || typeof keyData !== 'object') throw lazyDOMException('Invalid JWK keyData', 'DataError'); if (keyData.kty !== 'oct') throw lazyDOMException('Invalid key type', 'DataError'); if (usagesSet.size > 0 && keyData.use !== undefined && keyData.use !== 'sig') { throw lazyDOMException('Invalid use type', 'DataError'); } validateKeyOps(keyData.key_ops, usagesSet); if (keyData.ext !== undefined && keyData.ext === false && extractable === true) { throw lazyDOMException('JWK is not extractable', 'DataError'); } if (keyData.alg !== undefined) { if (typeof keyData.alg !== 'string') throw lazyDOMException('Invalid alg', 'DataError'); switch (keyData.alg) { case 'HS1': if (algorithm.hash.name !== 'SHA-1') throw lazyDOMException('Digest algorithm mismatch', 'DataError'); break; case 'HS256': if (algorithm.hash.name !== 'SHA-256') throw lazyDOMException('Digest algorithm mismatch', 'DataError'); break; case 'HS384': if (algorithm.hash.name !== 'SHA-384') throw lazyDOMException('Digest algorithm mismatch', 'DataError'); break; case 'HS512': if (algorithm.hash.name !== 'SHA-512') throw lazyDOMException('Digest algorithm mismatch', 'DataError'); break; default: throw lazyDOMException('Unsupported digest 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)); } module.exports = { hmacImportKey, hmacGenerateKey, hmacSignVerify, };