• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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    name: 'X25519',
15    size: 32,
16    pkcs8: '302e020100300506032b656e04220420c8838e76d057dfb7d8c95a69e138160ad' +
17           'd6373fd71a4d276bb56e3a81b64ff61',
18    spki: '302a300506032b656e0321001cf2b1e6022ec537371ed7f53e54fa1154d83e98eb' +
19          '64ea51fae5b3307cfe9706',
20    result: '2768409dfab99ec23b8c89b93ff5880295f76176088f89e43dfebe7ea1950008'
21  },
22  {
23    name: 'X448',
24    size: 56,
25    pkcs8: '3046020100300506032b656f043a043858c7d29a3eb519b29d00cfb191bb64fc6' +
26           'd8a42d8f17176272b89f2272d1819295c6525c0829671b052ef0727530f188e31' +
27           'd0cc53bf26929e',
28    spki: '3042300506032b656f033900b604a1d1a5cd1d9426d561ef630a9eb16cbe69d5b9' +
29          'ca615edc53633efb52ea31e6e6a0a1dbacc6e76cbce6482d7e4ba3d55d9e802765' +
30          'ce6f',
31    result: 'f0f6c5f17f94f4291eab7178866d37ec8906dd6c514143dc85be7cf28deff39b' +
32            '726e0f6dcf810eb594dca97b4882bd44c43ea7dc67f49a4e',
33  },
34];
35
36async function prepareKeys() {
37  const keys = {};
38  await Promise.all(
39    kTests.map(async ({ name, size, pkcs8, spki, result }) => {
40      const [
41        privateKey,
42        publicKey,
43      ] = await Promise.all([
44        subtle.importKey(
45          'pkcs8',
46          Buffer.from(pkcs8, 'hex'),
47          { name },
48          true,
49          ['deriveKey', 'deriveBits']),
50        subtle.importKey(
51          'spki',
52          Buffer.from(spki, 'hex'),
53          { name },
54          true,
55          []),
56      ]);
57      keys[name] = {
58        privateKey,
59        publicKey,
60        size,
61        result,
62      };
63    }));
64  return keys;
65}
66
67(async function() {
68  const keys = await prepareKeys();
69
70  await Promise.all(
71    Object.keys(keys).map(async (name) => {
72      const { size, result, privateKey, publicKey } = keys[name];
73
74      {
75        // Good parameters
76        const bits = await subtle.deriveBits({
77          name,
78          public: publicKey
79        }, privateKey, 8 * size);
80
81        assert(bits instanceof ArrayBuffer);
82        assert.strictEqual(Buffer.from(bits).toString('hex'), result);
83      }
84
85      {
86        // Case insensitivity
87        const bits = await subtle.deriveBits({
88          name: name.toLowerCase(),
89          public: publicKey
90        }, privateKey, 8 * size);
91
92        assert.strictEqual(Buffer.from(bits).toString('hex'), result);
93      }
94
95      {
96        // Null length
97        const bits = await subtle.deriveBits({
98          name,
99          public: publicKey
100        }, privateKey, null);
101
102        assert.strictEqual(Buffer.from(bits).toString('hex'), result);
103      }
104
105      {
106        // Short Result
107        const bits = await subtle.deriveBits({
108          name,
109          public: publicKey
110        }, privateKey, 8 * size - 32);
111
112        assert.strictEqual(
113          Buffer.from(bits).toString('hex'),
114          result.slice(0, -8));
115      }
116
117      {
118        // Too long result
119        await assert.rejects(subtle.deriveBits({
120          name,
121          public: publicKey
122        }, privateKey, 8 * size + 8), {
123          message: /derived bit length is too small/
124        });
125      }
126
127      {
128        // Non-multiple of 8
129        const bits = await subtle.deriveBits({
130          name,
131          public: publicKey
132        }, privateKey, 8 * size - 11);
133
134        assert.strictEqual(
135          Buffer.from(bits).toString('hex'),
136          result.slice(0, -2));
137      }
138    }));
139
140  // Error tests
141  {
142    // Missing public property
143    await assert.rejects(
144      subtle.deriveBits(
145        { name: 'X448' },
146        keys.X448.privateKey,
147        8 * keys.X448.size),
148      { code: 'ERR_MISSING_OPTION' });
149  }
150
151  {
152    // The public property is not a CryptoKey
153    await assert.rejects(
154      subtle.deriveBits(
155        {
156          name: 'X448',
157          public: { message: 'Not a CryptoKey' }
158        },
159        keys.X448.privateKey,
160        8 * keys.X448.size),
161      { code: 'ERR_INVALID_ARG_TYPE' });
162  }
163
164  {
165    // Mismatched types
166    await assert.rejects(
167      subtle.deriveBits(
168        {
169          name: 'X448',
170          public: keys.X25519.publicKey
171        },
172        keys.X448.privateKey,
173        8 * keys.X448.size),
174      { message: 'The public and private keys must be of the same type' });
175  }
176
177  {
178    // Base key is not a private key
179    await assert.rejects(subtle.deriveBits({
180      name: 'X448',
181      public: keys.X448.publicKey
182    }, keys.X448.publicKey, null), {
183      name: 'InvalidAccessError'
184    });
185  }
186
187  {
188    // Base key is not a private key
189    await assert.rejects(subtle.deriveBits({
190      name: 'X448',
191      public: keys.X448.privateKey
192    }, keys.X448.publicKey, null), {
193      name: 'InvalidAccessError'
194    });
195  }
196
197  {
198    // Public is a secret key
199    const keyData = webcrypto.getRandomValues(new Uint8Array(32));
200    const key = await subtle.importKey(
201      'raw',
202      keyData,
203      { name: 'AES-CBC', length: 256 },
204      false, ['encrypt']);
205
206    await assert.rejects(subtle.deriveBits({
207      name: 'X448',
208      public: key
209    }, keys.X448.publicKey, null), {
210      name: 'InvalidAccessError'
211    });
212  }
213})().then(common.mustCall());
214