n, } = require('internal/util'); function pbkdf2(password, salt, iterations, keylen, digest, callback) { if (typeof digest === 'function') { callback = digest; digest = undefined; } ({ password, salt, iterations, keylen, digest } = check(password, salt, iterations, keylen, digest)); validateFunction(callback, 'callback'); const job = new PBKDF2Job( kCryptoJobAsync, password, salt, iterations, keylen, digest); const encoding = getDefaultEncoding(); job.ondone = (err, result) => { if (err !== undefined) return FunctionPrototypeCall(callback, job, err); const buf = Buffer.from(result); if (encoding === 'buffer') return FunctionPrototypeCall(callback, job, null, buf); FunctionPrototypeCall(callback, job, null, buf.toString(encoding)); }; job.run(); } function pbkdf2Sync(password, salt, iterations, keylen, digest) { ({ password, salt, iterations, keylen, digest } = check(password, salt, iterations, keylen, digest)); const job = new PBKDF2Job( kCryptoJobSync, password, salt, iterations, keylen, digest); const { 0: err, 1: result } = job.run(); if (err !== undefined) throw err; const buf = Buffer.from(result); const encoding = getDefaultEncoding(); return encoding === 'buffer' ? buf : buf.toString(encoding); } function check(password, salt, iterations, keylen, digest) { validateString(digest, 'digest'); password = getArrayBufferOrView(password, 'password'); salt = getArrayBufferOrView(salt, 'salt'); validateUint32(iterations, 'iterations', true); validateUint32(keylen, 'keylen'); return { password, salt, iterations, keylen, digest }; } async function pbkdf2DeriveBits(algorithm, baseKey, length) { validateUint32(length, 'length'); const { iterations } = algorithm; let { hash } = algorithm; const salt = getArrayBufferOrView(algorithm.salt, 'algorithm.salt'); if (hash === undefined) throw new ERR_MISSING_OPTION('algorithm.hash'); validateInteger(iterations, 'algorithm.iterations', 1); hash = normalizeHashName(hash.name); const raw = baseKey[kKeyObject].export(); let byteLength = 64; // the default if (length !== undefined) { if (length === 0) throw lazyDOMException('length cannot be zero', 'OperationError'); if (length % 8) { throw lazyDOMException( 'length must be a multiple of 8', 'OperationError'); } byteLength = length / 8; } return new Promise((resolve, reject) => { pbkdf2(raw, salt, iterations, byteLength, hash, (err, result) => { if (err) return reject(err); resolve(result.buffer); }); }); } module.exports = { pbkdf2, pbkdf2Sync, pbkdf2DeriveBits, }; 'use strict'; const { ArrayFrom, Promise, SafeSet, } = primordials; const { HmacJob, KeyObjectHandle, kCryptoJobAsync, kSignJobModeSign, kSignJobModeVerify, } = internalBinding('crypto'); const { getHashLength, hasAnyNotIn, jobPromise, normalizeHashName, validateBitLength, validateKeyOps, kHandle, kKeyObject, } = require('internal/crypto/util'); const { lazyDOMException, } = require('internal/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, };