• 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
12async function testEncrypt({ keyBuffer, algorithm, plaintext, result }) {
13  // Using a copy of plaintext to prevent tampering of the original
14  plaintext = Buffer.from(plaintext);
15
16  const key = await subtle.importKey(
17    'raw',
18    keyBuffer,
19    { name: algorithm.name },
20    false,
21    ['encrypt', 'decrypt']);
22
23  const output = await subtle.encrypt(algorithm, key, plaintext);
24  plaintext[0] = 255 - plaintext[0];
25
26  assert.strictEqual(
27    Buffer.from(output).toString('hex'),
28    Buffer.from(result).toString('hex'));
29
30  // Converting the returned ArrayBuffer into a Buffer right away,
31  // so that the next line works
32  const check = Buffer.from(await subtle.decrypt(algorithm, key, output));
33  check[0] = 255 - check[0];
34
35  assert.strictEqual(
36    Buffer.from(check).toString('hex'),
37    Buffer.from(plaintext).toString('hex'));
38}
39
40async function testEncryptNoEncrypt({ keyBuffer, algorithm, plaintext }) {
41  const key = await subtle.importKey(
42    'raw',
43    keyBuffer,
44    { name: algorithm.name },
45    false,
46    ['decrypt']);
47
48  return assert.rejects(subtle.encrypt(algorithm, key, plaintext), {
49    message: /The requested operation is not valid for the provided key/
50  });
51}
52
53async function testEncryptNoDecrypt({ keyBuffer, algorithm, plaintext }) {
54  const key = await subtle.importKey(
55    'raw',
56    keyBuffer,
57    { name: algorithm.name },
58    false,
59    ['encrypt']);
60
61  const output = await subtle.encrypt(algorithm, key, plaintext);
62
63  return assert.rejects(subtle.decrypt(algorithm, key, output), {
64    message: /The requested operation is not valid for the provided key/
65  });
66}
67
68async function testEncryptWrongAlg({ keyBuffer, algorithm, plaintext }, alg) {
69  assert.notStrictEqual(algorithm.name, alg);
70  const key = await subtle.importKey(
71    'raw',
72    keyBuffer,
73    { name: alg },
74    false,
75    ['encrypt']);
76
77  return assert.rejects(subtle.encrypt(algorithm, key, plaintext), {
78    message: /The requested operation is not valid for the provided key/
79  });
80}
81
82async function testDecrypt({ keyBuffer, algorithm, result }) {
83  const key = await subtle.importKey(
84    'raw',
85    keyBuffer,
86    { name: algorithm.name },
87    false,
88    ['encrypt', 'decrypt']);
89
90  await subtle.decrypt(algorithm, key, result);
91}
92
93// Test aes-cbc vectors
94{
95  const {
96    passing,
97    failing,
98    decryptionFailing
99  } = require('../fixtures/crypto/aes_cbc')();
100
101  (async function() {
102    const variations = [];
103
104    passing.forEach((vector) => {
105      variations.push(testEncrypt(vector));
106      variations.push(testEncryptNoEncrypt(vector));
107      variations.push(testEncryptNoDecrypt(vector));
108      variations.push(testEncryptWrongAlg(vector, 'AES-CTR'));
109    });
110
111    failing.forEach((vector) => {
112      variations.push(assert.rejects(testEncrypt(vector), {
113        message: /algorithm\.iv must contain exactly 16 bytes/
114      }));
115      variations.push(assert.rejects(testDecrypt(vector), {
116        message: /algorithm\.iv must contain exactly 16 bytes/
117      }));
118    });
119
120    decryptionFailing.forEach((vector) => {
121      variations.push(assert.rejects(testDecrypt(vector), {
122        name: 'OperationError'
123      }));
124    });
125
126    await Promise.all(variations);
127  })().then(common.mustCall());
128}
129
130// Test aes-ctr vectors
131{
132  const {
133    passing,
134    failing,
135    decryptionFailing
136  } = require('../fixtures/crypto/aes_ctr')();
137
138  (async function() {
139    const variations = [];
140
141    passing.forEach((vector) => {
142      variations.push(testEncrypt(vector));
143      variations.push(testEncryptNoEncrypt(vector));
144      variations.push(testEncryptNoDecrypt(vector));
145      variations.push(testEncryptWrongAlg(vector, 'AES-CBC'));
146    });
147
148    // TODO(@jasnell): These fail for different reasons. Need to
149    // make them consistent
150    failing.forEach((vector) => {
151      variations.push(assert.rejects(testEncrypt(vector), {
152        message: /.*/
153      }));
154      variations.push(assert.rejects(testDecrypt(vector), {
155        message: /.*/
156      }));
157    });
158
159    decryptionFailing.forEach((vector) => {
160      variations.push(assert.rejects(testDecrypt(vector), {
161        name: 'OperationError'
162      }));
163    });
164
165    await Promise.all(variations);
166  })().then(common.mustCall());
167}
168
169// Test aes-gcm vectors
170{
171  const {
172    passing,
173    failing,
174    decryptionFailing
175  } = require('../fixtures/crypto/aes_gcm')();
176
177  (async function() {
178    const variations = [];
179
180    passing.forEach((vector) => {
181      variations.push(testEncrypt(vector));
182      variations.push(testEncryptNoEncrypt(vector));
183      variations.push(testEncryptNoDecrypt(vector));
184      variations.push(testEncryptWrongAlg(vector, 'AES-CBC'));
185    });
186
187    failing.forEach((vector) => {
188      variations.push(assert.rejects(testEncrypt(vector), {
189        message: /is not a valid AES-GCM tag length/
190      }));
191      variations.push(assert.rejects(testDecrypt(vector), {
192        message: /is not a valid AES-GCM tag length/
193      }));
194    });
195
196    decryptionFailing.forEach((vector) => {
197      variations.push(assert.rejects(testDecrypt(vector), {
198        name: 'OperationError'
199      }));
200    });
201
202    await Promise.all(variations);
203  })().then(common.mustCall());
204}
205
206{
207  (async function() {
208    const secretKey = await subtle.generateKey(
209      {
210        name: 'AES-GCM',
211        length: 256,
212      },
213      false,
214      ['encrypt', 'decrypt'],
215    );
216
217    const iv = webcrypto.getRandomValues(new Uint8Array(12));
218    const aad = webcrypto.getRandomValues(new Uint8Array(32));
219
220    const encrypted = await subtle.encrypt(
221      {
222        name: 'AES-GCM',
223        iv,
224        additionalData: aad,
225        tagLength: 128
226      },
227      secretKey,
228      webcrypto.getRandomValues(new Uint8Array(32))
229    );
230
231    await subtle.decrypt(
232      {
233        name: 'AES-GCM',
234        iv,
235        additionalData: aad,
236        tagLength: 128,
237      },
238      secretKey,
239      new Uint8Array(encrypted),
240    );
241  })().then(common.mustCall());
242}
243