• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Flags: --expose-internals --no-warnings
2'use strict';
3
4const common = require('../common');
5
6if (!common.hasCrypto)
7  common.skip('missing crypto');
8
9const assert = require('assert');
10const { webcrypto: { subtle }, KeyObject } = require('crypto');
11
12// This is only a partial test. The WebCrypto Web Platform Tests
13// will provide much greater coverage.
14
15// Test ECDH key derivation
16{
17  async function test(namedCurve) {
18    const [alice, bob] = await Promise.all([
19      subtle.generateKey({ name: 'ECDH', namedCurve }, true, ['deriveKey']),
20      subtle.generateKey({ name: 'ECDH', namedCurve }, true, ['deriveKey']),
21    ]);
22
23    const [secret1, secret2] = await Promise.all([
24      subtle.deriveKey({
25        name: 'ECDH', namedCurve, public: alice.publicKey
26      }, bob.privateKey, {
27        name: 'AES-CBC',
28        length: 256
29      }, true, ['encrypt']),
30      subtle.deriveKey({
31        name: 'ECDH', namedCurve, public: bob.publicKey
32      }, alice.privateKey, {
33        name: 'AES-CBC',
34        length: 256
35      }, true, ['encrypt']),
36    ]);
37
38    const [raw1, raw2] = await Promise.all([
39      subtle.exportKey('raw', secret1),
40      subtle.exportKey('raw', secret2),
41    ]);
42
43    assert.deepStrictEqual(raw1, raw2);
44  }
45
46  test('P-521').then(common.mustCall());
47}
48
49// Test HKDF key derivation
50{
51  async function test(pass, info, salt, hash, expected) {
52    const ec = new TextEncoder();
53    const key = await subtle.importKey(
54      'raw',
55      ec.encode(pass),
56      { name: 'HKDF', hash },
57      false, ['deriveKey']);
58
59    const secret = await subtle.deriveKey({
60      name: 'HKDF',
61      hash,
62      salt: ec.encode(salt),
63      info: ec.encode(info)
64    }, key, {
65      name: 'AES-CTR',
66      length: 256
67    }, true, ['encrypt']);
68
69    const raw = await subtle.exportKey('raw', secret);
70
71    assert.strictEqual(Buffer.from(raw).toString('hex'), expected);
72  }
73
74  const kTests = [
75    ['hello', 'there', 'my friend', 'SHA-256',
76     '14d93b0ccd99d4f2cbd9fbfe9c830b5b8a43e3e45e32941ef21bdeb0fa87b6b6'],
77    ['hello', 'there', 'my friend', 'SHA-384',
78     'e36cf2cf943d8f3a88adb80f478745c336ac811b1a86d03a7d10eb0b6b52295c'],
79  ];
80
81  const tests = Promise.all(kTests.map((args) => test(...args)));
82
83  tests.then(common.mustCall());
84}
85
86// Test PBKDF2 key derivation
87{
88  async function test(pass, salt, iterations, hash, expected) {
89    const ec = new TextEncoder();
90    const key = await subtle.importKey(
91      'raw',
92      ec.encode(pass),
93      { name: 'PBKDF2', hash },
94      false, ['deriveKey']);
95    const secret = await subtle.deriveKey({
96      name: 'PBKDF2',
97      hash,
98      salt: ec.encode(salt),
99      iterations,
100    }, key, {
101      name: 'AES-CTR',
102      length: 256
103    }, true, ['encrypt']);
104
105    const raw = await subtle.exportKey('raw', secret);
106
107    assert.strictEqual(Buffer.from(raw).toString('hex'), expected);
108  }
109
110  const kTests = [
111    ['hello', 'there', 10, 'SHA-256',
112     'f72d1cf4853fffbd16a42751765d11f8dc7939498ee7b7ce7678b4cb16fad880'],
113    ['hello', 'there', 5, 'SHA-384',
114     '201509b012c9cd2fbe7ea938f0c509b36ecb140f38bf9130e96923f55f46756d'],
115  ];
116
117  const tests = Promise.all(kTests.map((args) => test(...args)));
118
119  tests.then(common.mustCall());
120}
121
122// Test default key lengths
123{
124  const vectors = [
125    ['PBKDF2', 'deriveKey', 528],
126    ['HKDF', 'deriveKey', 528],
127    [{ name: 'HMAC', hash: 'SHA-1' }, 'sign', 512],
128    [{ name: 'HMAC', hash: 'SHA-256' }, 'sign', 512],
129    // Not long enough secret generated by ECDH
130    // [{ name: 'HMAC', hash: 'SHA-384' }, 'sign', 1024],
131    // [{ name: 'HMAC', hash: 'SHA-512' }, 'sign', 1024],
132  ];
133
134  (async () => {
135    const keyPair = await subtle.generateKey({ name: 'ECDH', namedCurve: 'P-521' }, false, ['deriveKey']);
136    for (const [derivedKeyAlgorithm, usage, expected] of vectors) {
137      const derived = await subtle.deriveKey(
138        { name: 'ECDH', public: keyPair.publicKey },
139        keyPair.privateKey,
140        derivedKeyAlgorithm,
141        false,
142        [usage]);
143
144      if (derived.algorithm.name === 'HMAC') {
145        assert.strictEqual(derived.algorithm.length, expected);
146      } else {
147        // KDFs cannot be exportable and do not indicate their length
148        const secretKey = KeyObject.from(derived);
149        assert.strictEqual(secretKey.symmetricKeySize, expected / 8);
150      }
151    }
152  })().then(common.mustCall());
153}
154
155{
156  const vectors = [
157    [{ name: 'HMAC', hash: 'SHA-1' }, 'sign', 512],
158    [{ name: 'HMAC', hash: 'SHA-256' }, 'sign', 512],
159    [{ name: 'HMAC', hash: 'SHA-384' }, 'sign', 1024],
160    [{ name: 'HMAC', hash: 'SHA-512' }, 'sign', 1024],
161  ];
162
163  (async () => {
164    for (const [derivedKeyAlgorithm, usage, expected] of vectors) {
165      const derived = await subtle.deriveKey(
166        { name: 'PBKDF2', salt: new Uint8Array([]), hash: 'SHA-256', iterations: 20 },
167        await subtle.importKey('raw', new Uint8Array([]), { name: 'PBKDF2' }, false, ['deriveKey']),
168        derivedKeyAlgorithm,
169        false,
170        [usage]);
171
172      assert.strictEqual(derived.algorithm.length, expected);
173    }
174  })().then(common.mustCall());
175}
176
177// Test X25519 and X448 key derivation
178{
179  async function test(name) {
180    const [alice, bob] = await Promise.all([
181      subtle.generateKey({ name }, true, ['deriveKey']),
182      subtle.generateKey({ name }, true, ['deriveKey']),
183    ]);
184
185    const [secret1, secret2] = await Promise.all([
186      subtle.deriveKey({
187        name, public: alice.publicKey
188      }, bob.privateKey, {
189        name: 'AES-CBC',
190        length: 256
191      }, true, ['encrypt']),
192      subtle.deriveKey({
193        name, public: bob.publicKey
194      }, alice.privateKey, {
195        name: 'AES-CBC',
196        length: 256
197      }, true, ['encrypt']),
198    ]);
199
200    const [raw1, raw2] = await Promise.all([
201      subtle.exportKey('raw', secret1),
202      subtle.exportKey('raw', secret2),
203    ]);
204
205    assert.deepStrictEqual(raw1, raw2);
206  }
207
208  test('X25519').then(common.mustCall());
209  test('X448').then(common.mustCall());
210}
211