1'use strict'; 2 3const { 4 FunctionPrototypeCall, 5} = primordials; 6 7const { 8 HKDFJob, 9 kCryptoJobAsync, 10 kCryptoJobSync, 11} = internalBinding('crypto'); 12 13const { 14 validateFunction, 15 validateInteger, 16 validateString, 17} = require('internal/validators'); 18 19const { kMaxLength } = require('buffer'); 20 21const { 22 normalizeHashName, 23 toBuf, 24 validateByteSource, 25 kKeyObject, 26} = require('internal/crypto/util'); 27 28const { 29 createSecretKey, 30 isKeyObject, 31} = require('internal/crypto/keys'); 32 33const { 34 lazyDOMException, 35 promisify, 36} = require('internal/util'); 37 38const { 39 isAnyArrayBuffer, 40 isArrayBufferView, 41} = require('internal/util/types'); 42 43const { 44 codes: { 45 ERR_INVALID_ARG_TYPE, 46 ERR_OUT_OF_RANGE, 47 }, 48 hideStackFrames, 49} = require('internal/errors'); 50 51const validateParameters = hideStackFrames((hash, key, salt, info, length) => { 52 validateString(hash, 'digest'); 53 key = prepareKey(key); 54 salt = validateByteSource(salt, 'salt'); 55 info = validateByteSource(info, 'info'); 56 57 validateInteger(length, 'length', 0, kMaxLength); 58 59 if (info.byteLength > 1024) { 60 throw ERR_OUT_OF_RANGE( 61 'info', 62 'must not contain more than 1024 bytes', 63 info.byteLength); 64 } 65 66 return { 67 hash, 68 key, 69 salt, 70 info, 71 length, 72 }; 73}); 74 75function prepareKey(key) { 76 if (isKeyObject(key)) 77 return key; 78 79 if (isAnyArrayBuffer(key)) 80 return createSecretKey(key); 81 82 key = toBuf(key); 83 84 if (!isArrayBufferView(key)) { 85 throw new ERR_INVALID_ARG_TYPE( 86 'ikm', 87 [ 88 'string', 89 'SecretKeyObject', 90 'ArrayBuffer', 91 'TypedArray', 92 'DataView', 93 'Buffer', 94 ], 95 key); 96 } 97 98 return createSecretKey(key); 99} 100 101function hkdf(hash, key, salt, info, length, callback) { 102 ({ 103 hash, 104 key, 105 salt, 106 info, 107 length, 108 } = validateParameters(hash, key, salt, info, length)); 109 110 validateFunction(callback, 'callback'); 111 112 const job = new HKDFJob(kCryptoJobAsync, hash, key, salt, info, length); 113 114 job.ondone = (error, bits) => { 115 if (error) return FunctionPrototypeCall(callback, job, error); 116 FunctionPrototypeCall(callback, job, null, bits); 117 }; 118 119 job.run(); 120} 121 122function hkdfSync(hash, key, salt, info, length) { 123 ({ 124 hash, 125 key, 126 salt, 127 info, 128 length, 129 } = validateParameters(hash, key, salt, info, length)); 130 131 const job = new HKDFJob(kCryptoJobSync, hash, key, salt, info, length); 132 const { 0: err, 1: bits } = job.run(); 133 if (err !== undefined) 134 throw err; 135 136 return bits; 137} 138 139const hkdfPromise = promisify(hkdf); 140async function hkdfDeriveBits(algorithm, baseKey, length) { 141 const { hash, salt, info } = algorithm; 142 143 if (length === 0) 144 throw lazyDOMException('length cannot be zero', 'OperationError'); 145 if (length === null) 146 throw lazyDOMException('length cannot be null', 'OperationError'); 147 if (length % 8) { 148 throw lazyDOMException( 149 'length must be a multiple of 8', 150 'OperationError'); 151 } 152 153 try { 154 return await hkdfPromise( 155 normalizeHashName(hash.name), baseKey[kKeyObject], salt, info, length / 8, 156 ); 157 } catch (err) { 158 throw lazyDOMException( 159 'The operation failed for an operation-specific reason', 160 { name: 'OperationError', cause: err }); 161 } 162} 163 164module.exports = { 165 hkdf, 166 hkdfSync, 167 hkdfDeriveBits, 168}; 169