1'use strict';
2
3const common = require('../common');
4
5if (!common.hasCrypto)
6  common.skip('missing crypto');
7
8const assert = require('assert');
9const { subtle } = require('crypto').webcrypto;
10
11const {
12  passing
13} = require('../fixtures/crypto/rsa')();
14
15async function importVectorKey(
16  publicKeyBuffer,
17  privateKeyBuffer,
18  name,
19  hash,
20  publicUsages,
21  privateUsages) {
22  const [publicKey, privateKey] = await Promise.all([
23    subtle.importKey(
24      'spki', publicKeyBuffer, { name, hash }, false, publicUsages),
25    subtle.importKey(
26      'pkcs8', privateKeyBuffer, { name, hash }, false, privateUsages),
27  ]);
28
29  return { publicKey, privateKey };
30}
31
32async function testDecryption({ ciphertext,
33                                algorithm,
34                                plaintext,
35                                hash,
36                                publicKeyBuffer,
37                                privateKeyBuffer }) {
38  if (ciphertext === undefined)
39    return;
40
41  const {
42    privateKey
43  } = await importVectorKey(
44    publicKeyBuffer,
45    privateKeyBuffer,
46    algorithm.name,
47    hash,
48    ['encrypt'],
49    ['decrypt']);
50
51  const encodedPlaintext = Buffer.from(plaintext).toString('hex');
52  const result = await subtle.decrypt(algorithm, privateKey, ciphertext);
53
54  assert.strictEqual(
55    Buffer.from(result).toString('hex'),
56    encodedPlaintext);
57
58  const ciphercopy = Buffer.from(ciphertext);
59
60  // Modifying the ciphercopy after calling decrypt should just work
61  const result2 = await subtle.decrypt(algorithm, privateKey, ciphercopy);
62  ciphercopy[0] = 255 - ciphercopy[0];
63
64  assert.strictEqual(
65    Buffer.from(result2).toString('hex'),
66    encodedPlaintext);
67}
68
69async function testEncryption(
70  {
71    ciphertext,
72    algorithm,
73    plaintext,
74    hash,
75    publicKeyBuffer,
76    privateKeyBuffer
77  },
78  modify = false) {
79  const {
80    publicKey,
81    privateKey
82  } = await importVectorKey(
83    publicKeyBuffer,
84    privateKeyBuffer,
85    algorithm.name,
86    hash,
87    ['encrypt'],
88    ['decrypt']);
89
90  if (modify)
91    plaintext = Buffer.from(plaintext);  // make a copy
92
93  const encodedPlaintext = Buffer.from(plaintext).toString('hex');
94
95  const result = await subtle.encrypt(algorithm, publicKey, plaintext);
96  if (modify)
97    plaintext[0] = 255 - plaintext[0];
98
99  assert.strictEqual(
100    result.byteLength * 8,
101    privateKey.algorithm.modulusLength);
102
103  const out = await subtle.decrypt(algorithm, privateKey, result);
104
105  assert.strictEqual(
106    Buffer.from(out).toString('hex'),
107    encodedPlaintext);
108}
109
110async function testEncryptionLongPlaintext({ algorithm,
111                                             plaintext,
112                                             hash,
113                                             publicKeyBuffer,
114                                             privateKeyBuffer }) {
115  const {
116    publicKey,
117  } = await importVectorKey(
118    publicKeyBuffer,
119    privateKeyBuffer,
120    algorithm.name,
121    hash,
122    ['encrypt'],
123    ['decrypt']);
124  const newplaintext = new Uint8Array(plaintext.byteLength + 1);
125  newplaintext.set(plaintext, 0);
126  newplaintext[plaintext.byteLength] = 32;
127
128  return assert.rejects(
129    subtle.encrypt(algorithm, publicKey, newplaintext), {
130      name: 'OperationError'
131    });
132}
133
134async function testEncryptionWrongKey({ algorithm,
135                                        plaintext,
136                                        hash,
137                                        publicKeyBuffer,
138                                        privateKeyBuffer }) {
139  const {
140    privateKey,
141  } = await importVectorKey(
142    publicKeyBuffer,
143    privateKeyBuffer,
144    algorithm.name,
145    hash,
146    ['encrypt'],
147    ['decrypt']);
148  return assert.rejects(
149    subtle.encrypt(algorithm, privateKey, plaintext), {
150      message: /The requested operation is not valid/
151    });
152}
153
154async function testEncryptionBadUsage({ algorithm,
155                                        plaintext,
156                                        hash,
157                                        publicKeyBuffer,
158                                        privateKeyBuffer }) {
159  const {
160    publicKey,
161  } = await importVectorKey(
162    publicKeyBuffer,
163    privateKeyBuffer,
164    algorithm.name,
165    hash,
166    ['wrapKey'],
167    ['decrypt']);
168  return assert.rejects(
169    subtle.encrypt(algorithm, publicKey, plaintext), {
170      message: /The requested operation is not valid/
171    });
172}
173
174async function testDecryptionWrongKey({ ciphertext,
175                                        algorithm,
176                                        hash,
177                                        publicKeyBuffer,
178                                        privateKeyBuffer }) {
179  if (ciphertext === undefined)
180    return;
181
182  const {
183    publicKey
184  } = await importVectorKey(
185    publicKeyBuffer,
186    privateKeyBuffer,
187    algorithm.name,
188    hash,
189    ['encrypt'],
190    ['decrypt']);
191
192  return assert.rejects(
193    subtle.decrypt(algorithm, publicKey, ciphertext), {
194      message: /The requested operation is not valid/
195    });
196}
197
198async function testDecryptionBadUsage({ ciphertext,
199                                        algorithm,
200                                        hash,
201                                        publicKeyBuffer,
202                                        privateKeyBuffer }) {
203  if (ciphertext === undefined)
204    return;
205
206  const {
207    publicKey
208  } = await importVectorKey(
209    publicKeyBuffer,
210    privateKeyBuffer,
211    algorithm.name,
212    hash,
213    ['encrypt'],
214    ['unwrapKey']);
215
216  return assert.rejects(
217    subtle.decrypt(algorithm, publicKey, ciphertext), {
218      message: /The requested operation is not valid/
219    });
220}
221
222(async function() {
223  const variations = [];
224
225  // Test decryption
226  passing.forEach(async (vector) => {
227    variations.push(testDecryption(vector));
228    variations.push(testDecryptionWrongKey(vector));
229    variations.push(testDecryptionBadUsage(vector));
230    variations.push(testEncryption(vector));
231    variations.push(testEncryption(vector, true));
232    variations.push(testEncryptionLongPlaintext(vector));
233    variations.push(testEncryptionWrongKey(vector));
234    variations.push(testEncryptionBadUsage(vector));
235  });
236
237  await Promise.all(variations);
238})().then(common.mustCall());
239