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