1'use strict';
2
3const common = require('../common');
4
5if (!common.hasCrypto)
6  common.skip('missing crypto');
7
8const { kMaxLength } = require('buffer');
9const assert = require('assert');
10const {
11  createSecretKey,
12  hkdf,
13  hkdfSync,
14  getHashes
15} = require('crypto');
16
17{
18  assert.throws(() => hkdf(), {
19    code: 'ERR_INVALID_ARG_TYPE',
20    message: /The "digest" argument must be of type string/
21  });
22
23  [1, {}, [], false, Infinity].forEach((i) => {
24    assert.throws(() => hkdf(i, 'a'), {
25      code: 'ERR_INVALID_ARG_TYPE',
26      message: /^The "digest" argument must be of type string/
27    });
28    assert.throws(() => hkdfSync(i, 'a'), {
29      code: 'ERR_INVALID_ARG_TYPE',
30      message: /^The "digest" argument must be of type string/
31    });
32  });
33
34  [1, {}, [], false, Infinity].forEach((i) => {
35    assert.throws(() => hkdf('sha256', i), {
36      code: 'ERR_INVALID_ARG_TYPE',
37      message: /^The "ikm" argument must be /
38    });
39    assert.throws(() => hkdfSync('sha256', i), {
40      code: 'ERR_INVALID_ARG_TYPE',
41      message: /^The "ikm" argument must be /
42    });
43  });
44
45  [1, {}, [], false, Infinity].forEach((i) => {
46    assert.throws(() => hkdf('sha256', 'secret', i), {
47      code: 'ERR_INVALID_ARG_TYPE',
48      message: /^The "salt" argument must be /
49    });
50    assert.throws(() => hkdfSync('sha256', 'secret', i), {
51      code: 'ERR_INVALID_ARG_TYPE',
52      message: /^The "salt" argument must be /
53    });
54  });
55
56  [1, {}, [], false, Infinity].forEach((i) => {
57    assert.throws(() => hkdf('sha256', 'secret', 'salt', i), {
58      code: 'ERR_INVALID_ARG_TYPE',
59      message: /^The "info" argument must be /
60    });
61    assert.throws(() => hkdfSync('sha256', 'secret', 'salt', i), {
62      code: 'ERR_INVALID_ARG_TYPE',
63      message: /^The "info" argument must be /
64    });
65  });
66
67  ['test', {}, [], false].forEach((i) => {
68    assert.throws(() => hkdf('sha256', 'secret', 'salt', 'info', i), {
69      code: 'ERR_INVALID_ARG_TYPE',
70      message: /^The "length" argument must be of type number/
71    });
72    assert.throws(() => hkdfSync('sha256', 'secret', 'salt', 'info', i), {
73      code: 'ERR_INVALID_ARG_TYPE',
74      message: /^The "length" argument must be of type number/
75    });
76  });
77
78  assert.throws(() => hkdf('sha256', 'secret', 'salt', 'info', -1), {
79    code: 'ERR_OUT_OF_RANGE'
80  });
81  assert.throws(() => hkdfSync('sha256', 'secret', 'salt', 'info', -1), {
82    code: 'ERR_OUT_OF_RANGE'
83  });
84  assert.throws(() => hkdf('sha256', 'secret', 'salt', 'info',
85                           kMaxLength + 1), {
86    code: 'ERR_OUT_OF_RANGE'
87  });
88  assert.throws(() => hkdfSync('sha256', 'secret', 'salt', 'info',
89                               kMaxLength + 1), {
90    code: 'ERR_OUT_OF_RANGE'
91  });
92
93  assert.throws(() => hkdfSync('unknown', 'a', '', '', 10), {
94    code: 'ERR_CRYPTO_INVALID_DIGEST'
95  });
96
97  assert.throws(() => hkdf('unknown', 'a', '', '', 10, common.mustNotCall()), {
98    code: 'ERR_CRYPTO_INVALID_DIGEST'
99  });
100
101  assert.throws(() => hkdf('unknown', 'a', '', Buffer.alloc(1025), 10,
102                           common.mustNotCall()), {
103    code: 'ERR_OUT_OF_RANGE'
104  });
105
106  assert.throws(() => hkdfSync('unknown', 'a', '', Buffer.alloc(1025), 10), {
107    code: 'ERR_OUT_OF_RANGE'
108  });
109
110  assert.throws(
111    () => hkdf('sha512', 'a', '', '', 64 * 255 + 1, common.mustNotCall()), {
112      code: 'ERR_CRYPTO_INVALID_KEYLEN'
113    });
114
115  assert.throws(
116    () => hkdfSync('sha512', 'a', '', '', 64 * 255 + 1), {
117      code: 'ERR_CRYPTO_INVALID_KEYLEN'
118    });
119}
120
121const algorithms = [
122  ['sha256', 'secret', 'salt', 'info', 10],
123  ['sha256', '', '', '', 10],
124  ['sha256', '', 'salt', '', 10],
125  ['sha512', 'secret', 'salt', '', 15],
126];
127if (!common.hasOpenSSL3)
128  algorithms.push(['whirlpool', 'secret', '', 'info', 20]);
129
130algorithms.forEach(([ hash, secret, salt, info, length ]) => {
131  {
132    const syncResult = hkdfSync(hash, secret, salt, info, length);
133    assert(syncResult instanceof ArrayBuffer);
134    let is_async = false;
135    hkdf(hash, secret, salt, info, length,
136         common.mustSucceed((asyncResult) => {
137           assert(is_async);
138           assert(asyncResult instanceof ArrayBuffer);
139           assert.deepStrictEqual(syncResult, asyncResult);
140         }));
141    // Keep this after the hkdf call above. This verifies
142    // that the callback is invoked asynchronously.
143    is_async = true;
144  }
145
146  {
147    const buf_secret = Buffer.from(secret);
148    const buf_salt = Buffer.from(salt);
149    const buf_info = Buffer.from(info);
150
151    const syncResult = hkdfSync(hash, buf_secret, buf_salt, buf_info, length);
152    hkdf(hash, buf_secret, buf_salt, buf_info, length,
153         common.mustSucceed((asyncResult) => {
154           assert.deepStrictEqual(syncResult, asyncResult);
155         }));
156  }
157
158  {
159    const key_secret = createSecretKey(Buffer.from(secret));
160    const buf_salt = Buffer.from(salt);
161    const buf_info = Buffer.from(info);
162
163    const syncResult = hkdfSync(hash, key_secret, buf_salt, buf_info, length);
164    hkdf(hash, key_secret, buf_salt, buf_info, length,
165         common.mustSucceed((asyncResult) => {
166           assert.deepStrictEqual(syncResult, asyncResult);
167         }));
168  }
169
170  {
171    const ta_secret = new Uint8Array(Buffer.from(secret));
172    const ta_salt = new Uint16Array(Buffer.from(salt));
173    const ta_info = new Uint32Array(Buffer.from(info));
174
175    const syncResult = hkdfSync(hash, ta_secret, ta_salt, ta_info, length);
176    hkdf(hash, ta_secret, ta_salt, ta_info, length,
177         common.mustSucceed((asyncResult) => {
178           assert.deepStrictEqual(syncResult, asyncResult);
179         }));
180  }
181
182  {
183    const ta_secret = new Uint8Array(Buffer.from(secret));
184    const ta_salt = new Uint16Array(Buffer.from(salt));
185    const ta_info = new Uint32Array(Buffer.from(info));
186
187    const syncResult = hkdfSync(
188      hash,
189      ta_secret.buffer,
190      ta_salt.buffer,
191      ta_info.buffer,
192      length);
193    hkdf(hash, ta_secret, ta_salt, ta_info, length,
194         common.mustSucceed((asyncResult) => {
195           assert.deepStrictEqual(syncResult, asyncResult);
196         }));
197  }
198
199  {
200    const ta_secret = new Uint8Array(Buffer.from(secret));
201    const sa_salt = new SharedArrayBuffer(0);
202    const sa_info = new SharedArrayBuffer(1);
203
204    const syncResult = hkdfSync(
205      hash,
206      ta_secret.buffer,
207      sa_salt,
208      sa_info,
209      length);
210    hkdf(hash, ta_secret, sa_salt, sa_info, length,
211         common.mustSucceed((asyncResult) => {
212           assert.deepStrictEqual(syncResult, asyncResult);
213         }));
214  }
215});
216
217
218if (!common.hasOpenSSL3) {
219  const kKnownUnsupported = ['shake128', 'shake256'];
220  getHashes()
221    .filter((hash) => !kKnownUnsupported.includes(hash))
222    .forEach((hash) => {
223      assert(hkdfSync(hash, 'key', 'salt', 'info', 5));
224    });
225}
226