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