1'use strict';
2
3const common = require('../common');
4
5if (!common.hasCrypto)
6  common.skip('missing crypto');
7
8const assert = require('assert');
9const { webcrypto } = require('crypto');
10const { subtle } = webcrypto;
11
12const kTests = [
13  {
14    namedCurve: 'P-521',
15    pkcs8: '3081ee020100301006072a8648ce3d020106052b810400230481d63081d302010' +
16           '1044201a67ed321915a64aa359b7d648ddc2618fa8e8d1867e8f71830b10d25ed' +
17           '2891faf12f3c7e75421a2ea264f9a915320d274fe1470742b984e96b98912081f' +
18           'acd478da18189038186000400209d483f28666881c6641f3a126f400f51e46511' +
19           '70fe678c75e85712e2868adc850824997bebf0bc82b43028a6d2ec1777ca45279' +
20           'f7206a3ea8b5cd2073f493e45000cb54c3a5acaa268c56710428878d98b8afbf6' +
21           '8a612153632846d807e92672698f1b9c611de7d38e34cd6c73889092c56e52d68' +
22           '0f1dfd092b87ac8ef9ff3c8fb48',
23    spki: '30819b301006072a8648ce3d020106052b81040023038186000400ee69f94715d7' +
24          '01e9e2011333d4f4f96cba7d91f88b112baf75cf09cc1f8aca97618da9389822d2' +
25          '9b6fe9996a61203ef752b771e8958fc4677bb3778565ab60d6ed00deab6761895b' +
26          '935e3ad325fb8549e56f13786aa73f88a2ecfe40933473d8aef240c4dfd7d506f2' +
27          '2cdd0e55558f3fbf05ebf7efef7a72d78f46469b8448f26e2712',
28    result: '009c2bce57be80adab3b07385b8e5990eb7d6fdebdb01bf35371a4f6075e9d28',
29  },
30  {
31    namedCurve: 'P-384',
32    pkcs8: '3081b6020100301006072a8648ce3d020106052b8104002204819e30819b02010' +
33           '10430f871a5666589c14a5747263ef85b319cc023db6e35676c3d781eef8b055f' +
34           'cfbe86fa0d06d056b5195fb1323af8de25b3a16403620004f11965df7dd4594d0' +
35           '419c5086482a3b826b9797f9be0bd0d109c9e1e9989c1b9a92b8f269f98e17ad1' +
36           '84ba73c1f79762af45af8141602642da271a6bb0ffeb0cb4478fcf707e661aa6d' +
37           '6cdf51549c88c3f130be9e8201f6f6a09f4185aaf95c4',
38    spki: '3076301006072a8648ce3d020106052b810400220362000491822dc2af59c18f5b' +
39          '67f80df61a2603c2a8f0b3c0af822d63c279701a824560404401dde9a56ee52757' +
40          'ea8bc748d4c82b5337b48d7b65583a3d572438880036bac6730f42ca5278966bd5' +
41          'f21e86e21d30c5a6d0463ec513dd509ffcdcaf1ff5',
42    result: 'e0bd6bce0aef8ca48838a6e2fcc57e67b9c5e8860c5f0be9dabec53e454e18a0',
43  },
44];
45
46async function prepareKeys() {
47  const keys = {};
48  await Promise.all(
49    kTests.map(async ({ namedCurve, size, pkcs8, spki, result }) => {
50      const [
51        privateKey,
52        publicKey,
53      ] = await Promise.all([
54        subtle.importKey(
55          'pkcs8',
56          Buffer.from(pkcs8, 'hex'),
57          {
58            name: 'ECDH',
59            namedCurve
60          },
61          true,
62          ['deriveKey', 'deriveBits']),
63        subtle.importKey(
64          'spki',
65          Buffer.from(spki, 'hex'),
66          {
67            name: 'ECDH',
68            namedCurve
69          },
70          true,
71          []),
72      ]);
73      keys[namedCurve] = {
74        privateKey,
75        publicKey,
76        size,
77        result,
78      };
79    }));
80  return keys;
81}
82
83(async function() {
84  const keys = await prepareKeys();
85  const otherArgs = [
86    { name: 'HMAC', hash: 'SHA-256', length: 256 },
87    true,
88    ['sign', 'verify']];
89
90  await Promise.all(
91    Object.keys(keys).map(async (namedCurve) => {
92      const { result, privateKey, publicKey } = keys[namedCurve];
93
94      {
95        // Good parameters
96        const key = await subtle.deriveKey({
97          name: 'ECDH',
98          public: publicKey
99        }, privateKey, ...otherArgs);
100
101        const raw = await subtle.exportKey('raw', key);
102
103        assert.strictEqual(Buffer.from(raw).toString('hex'), result);
104      }
105
106      {
107        // Case insensitivity
108        const key = await subtle.deriveKey({
109          name: 'eCdH',
110          public: publicKey
111        }, privateKey, {
112          name: 'HmAc',
113          hash: 'SHA-256',
114          length: 256
115        }, true, ['sign', 'verify']);
116
117        const raw = await subtle.exportKey('raw', key);
118
119        assert.strictEqual(Buffer.from(raw).toString('hex'), result);
120      }
121    }));
122
123  // Error tests
124  {
125    // Missing public property
126    await assert.rejects(
127      subtle.deriveKey(
128        { name: 'ECDH' },
129        keys['P-384'].privateKey,
130        ...otherArgs),
131      { code: 'ERR_MISSING_OPTION' });
132  }
133
134  {
135    // The public property is not a CryptoKey
136    await assert.rejects(
137      subtle.deriveKey(
138        {
139          name: 'ECDH',
140          public: { message: 'Not a CryptoKey' }
141        },
142        keys['P-384'].privateKey,
143        ...otherArgs),
144      { code: 'ERR_INVALID_ARG_TYPE' });
145  }
146
147  {
148    // Mismatched named curves
149    await assert.rejects(
150      subtle.deriveKey(
151        {
152          name: 'ECDH',
153          public: keys['P-384'].publicKey
154        },
155        keys['P-521'].privateKey,
156        ...otherArgs),
157      { message: /Named curve mismatch/ });
158  }
159
160  {
161    // Incorrect public key algorithm
162    const { publicKey } = await subtle.generateKey(
163      {
164        name: 'ECDSA',
165        namedCurve: 'P-521'
166      },
167      false,
168      ['sign', 'verify']);
169
170    await assert.rejects(
171      subtle.deriveKey(
172        {
173          name: 'ECDH',
174          public: publicKey
175        },
176        keys['P-521'].privateKey,
177        ...otherArgs),
178      { message: /Keys must be ECDH, X25519, or X448 keys/ });
179  }
180
181  {
182    // Private key does not have correct usages
183    const privateKey = await subtle.importKey(
184      'pkcs8',
185      Buffer.from(kTests[0].pkcs8, 'hex'),
186      {
187        name: 'ECDH',
188        namedCurve: 'P-521'
189      }, false, ['deriveBits']);
190
191    await assert.rejects(
192      subtle.deriveKey(
193        {
194          name: 'ECDH',
195          public: keys['P-521'].publicKey,
196        },
197        privateKey,
198        ...otherArgs),
199      { message: /baseKey does not have deriveKey usage/ });
200  }
201
202  {
203    // Base key is not a private key
204    await assert.rejects(
205      subtle.deriveKey(
206        {
207          name: 'ECDH',
208          public: keys['P-521'].publicKey
209        },
210        keys['P-521'].publicKey,
211        ...otherArgs),
212      { name: 'InvalidAccessError' });
213  }
214
215  {
216    // Base key is not a private key
217    await assert.rejects(
218      subtle.deriveKey(
219        {
220          name: 'ECDH',
221          public: keys['P-521'].privateKey
222        },
223        keys['P-521'].publicKey,
224        ...otherArgs),
225      { name: 'InvalidAccessError' });
226  }
227
228  {
229    // Public is a secret key
230    const keyData = webcrypto.getRandomValues(new Uint8Array(32));
231    const key = await subtle.importKey(
232      'raw',
233      keyData,
234      { name: 'AES-CBC', length: 256 },
235      false, ['encrypt']);
236
237    await assert.rejects(
238      subtle.deriveKey(
239        {
240          name: 'ECDH',
241          public: key
242        },
243        keys['P-521'].publicKey,
244        ...otherArgs),
245      { name: 'InvalidAccessError' });
246  }
247})().then(common.mustCall());
248