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 namedCurve: 'P-521', 15 pkcs8: '3081ee020100301006072a8648ce3d020106052b810400230481d63081d302010' + 16 '1044201a67ed321915a64aa359b7d648ddc2618fa8e8d1867e8f71830b10d25ed' + 17 '2891faf12f3c7e75421a2ea264f9a915320d274fe1470742b984e96b98912081f' + 18 'acd478da18189038186000400209d483f28666881c6641f3a126f400f51e46511' + 19 '70fe678c75e85712e2868adc850824997bebf0bc82b43028a6d2ec1777ca45279' + 20 'f7206a3ea8b5cd2073f493e45000cb54c3a5acaa268c56710428878d98b8afbf6' + 21 '8a612153632846d807e92672698f1b9c611de7d38e34cd6c73889092c56e52d68' + 22 '0f1dfd092b87ac8ef9ff3c8fb48', 23 spki: '30819b301006072a8648ce3d020106052b81040023038186000400ee69f94715d7' + 24 '01e9e2011333d4f4f96cba7d91f88b112baf75cf09cc1f8aca97618da9389822d2' + 25 '9b6fe9996a61203ef752b771e8958fc4677bb3778565ab60d6ed00deab6761895b' + 26 '935e3ad325fb8549e56f13786aa73f88a2ecfe40933473d8aef240c4dfd7d506f2' + 27 '2cdd0e55558f3fbf05ebf7efef7a72d78f46469b8448f26e2712', 28 result: '009c2bce57be80adab3b07385b8e5990eb7d6fdebdb01bf35371a4f6075e9d28', 29 }, 30 { 31 namedCurve: 'P-384', 32 pkcs8: '3081b6020100301006072a8648ce3d020106052b8104002204819e30819b02010' + 33 '10430f871a5666589c14a5747263ef85b319cc023db6e35676c3d781eef8b055f' + 34 'cfbe86fa0d06d056b5195fb1323af8de25b3a16403620004f11965df7dd4594d0' + 35 '419c5086482a3b826b9797f9be0bd0d109c9e1e9989c1b9a92b8f269f98e17ad1' + 36 '84ba73c1f79762af45af8141602642da271a6bb0ffeb0cb4478fcf707e661aa6d' + 37 '6cdf51549c88c3f130be9e8201f6f6a09f4185aaf95c4', 38 spki: '3076301006072a8648ce3d020106052b810400220362000491822dc2af59c18f5b' + 39 '67f80df61a2603c2a8f0b3c0af822d63c279701a824560404401dde9a56ee52757' + 40 'ea8bc748d4c82b5337b48d7b65583a3d572438880036bac6730f42ca5278966bd5' + 41 'f21e86e21d30c5a6d0463ec513dd509ffcdcaf1ff5', 42 result: 'e0bd6bce0aef8ca48838a6e2fcc57e67b9c5e8860c5f0be9dabec53e454e18a0', 43 }, 44]; 45 46async function prepareKeys() { 47 const keys = {}; 48 await Promise.all( 49 kTests.map(async ({ namedCurve, size, pkcs8, spki, result }) => { 50 const [ 51 privateKey, 52 publicKey, 53 ] = await Promise.all([ 54 subtle.importKey( 55 'pkcs8', 56 Buffer.from(pkcs8, 'hex'), 57 { 58 name: 'ECDH', 59 namedCurve 60 }, 61 true, 62 ['deriveKey', 'deriveBits']), 63 subtle.importKey( 64 'spki', 65 Buffer.from(spki, 'hex'), 66 { 67 name: 'ECDH', 68 namedCurve 69 }, 70 true, 71 []), 72 ]); 73 keys[namedCurve] = { 74 privateKey, 75 publicKey, 76 size, 77 result, 78 }; 79 })); 80 return keys; 81} 82 83(async function() { 84 const keys = await prepareKeys(); 85 const otherArgs = [ 86 { name: 'HMAC', hash: 'SHA-256', length: 256 }, 87 true, 88 ['sign', 'verify']]; 89 90 await Promise.all( 91 Object.keys(keys).map(async (namedCurve) => { 92 const { result, privateKey, publicKey } = keys[namedCurve]; 93 94 { 95 // Good parameters 96 const key = await subtle.deriveKey({ 97 name: 'ECDH', 98 public: publicKey 99 }, privateKey, ...otherArgs); 100 101 const raw = await subtle.exportKey('raw', key); 102 103 assert.strictEqual(Buffer.from(raw).toString('hex'), result); 104 } 105 106 { 107 // Case insensitivity 108 const key = await subtle.deriveKey({ 109 name: 'eCdH', 110 public: publicKey 111 }, privateKey, { 112 name: 'HmAc', 113 hash: 'SHA-256', 114 length: 256 115 }, true, ['sign', 'verify']); 116 117 const raw = await subtle.exportKey('raw', key); 118 119 assert.strictEqual(Buffer.from(raw).toString('hex'), result); 120 } 121 })); 122 123 // Error tests 124 { 125 // Missing public property 126 await assert.rejects( 127 subtle.deriveKey( 128 { name: 'ECDH' }, 129 keys['P-384'].privateKey, 130 ...otherArgs), 131 { code: 'ERR_MISSING_OPTION' }); 132 } 133 134 { 135 // The public property is not a CryptoKey 136 await assert.rejects( 137 subtle.deriveKey( 138 { 139 name: 'ECDH', 140 public: { message: 'Not a CryptoKey' } 141 }, 142 keys['P-384'].privateKey, 143 ...otherArgs), 144 { code: 'ERR_INVALID_ARG_TYPE' }); 145 } 146 147 { 148 // Mismatched named curves 149 await assert.rejects( 150 subtle.deriveKey( 151 { 152 name: 'ECDH', 153 public: keys['P-384'].publicKey 154 }, 155 keys['P-521'].privateKey, 156 ...otherArgs), 157 { message: /Named curve mismatch/ }); 158 } 159 160 { 161 // Incorrect public key algorithm 162 const { publicKey } = await subtle.generateKey( 163 { 164 name: 'ECDSA', 165 namedCurve: 'P-521' 166 }, 167 false, 168 ['sign', 'verify']); 169 170 await assert.rejects( 171 subtle.deriveKey( 172 { 173 name: 'ECDH', 174 public: publicKey 175 }, 176 keys['P-521'].privateKey, 177 ...otherArgs), 178 { message: /Keys must be ECDH, X25519, or X448 keys/ }); 179 } 180 181 { 182 // Private key does not have correct usages 183 const privateKey = await subtle.importKey( 184 'pkcs8', 185 Buffer.from(kTests[0].pkcs8, 'hex'), 186 { 187 name: 'ECDH', 188 namedCurve: 'P-521' 189 }, false, ['deriveBits']); 190 191 await assert.rejects( 192 subtle.deriveKey( 193 { 194 name: 'ECDH', 195 public: keys['P-521'].publicKey, 196 }, 197 privateKey, 198 ...otherArgs), 199 { message: /baseKey does not have deriveKey usage/ }); 200 } 201 202 { 203 // Base key is not a private key 204 await assert.rejects( 205 subtle.deriveKey( 206 { 207 name: 'ECDH', 208 public: keys['P-521'].publicKey 209 }, 210 keys['P-521'].publicKey, 211 ...otherArgs), 212 { name: 'InvalidAccessError' }); 213 } 214 215 { 216 // Base key is not a private key 217 await assert.rejects( 218 subtle.deriveKey( 219 { 220 name: 'ECDH', 221 public: keys['P-521'].privateKey 222 }, 223 keys['P-521'].publicKey, 224 ...otherArgs), 225 { name: 'InvalidAccessError' }); 226 } 227 228 { 229 // Public is a secret key 230 const keyData = webcrypto.getRandomValues(new Uint8Array(32)); 231 const key = await subtle.importKey( 232 'raw', 233 keyData, 234 { name: 'AES-CBC', length: 256 }, 235 false, ['encrypt']); 236 237 await assert.rejects( 238 subtle.deriveKey( 239 { 240 name: 'ECDH', 241 public: key 242 }, 243 keys['P-521'].publicKey, 244 ...otherArgs), 245 { name: 'InvalidAccessError' }); 246 } 247})().then(common.mustCall()); 248