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