'use strict'; const common = require('../common'); if (!common.hasCrypto) common.skip('missing crypto'); if (common.hasOpenSSL3) common.skip('temporarily skipping for OpenSSL 3.0-alpha15'); const assert = require('assert'); const { subtle, getRandomValues } = require('crypto').webcrypto; const kTests = [ { namedCurve: 'P-521', pkcs8: '3081ee020100301006072a8648ce3d020106052b810400230481d63081d302010' + '1044201a67ed321915a64aa359b7d648ddc2618fa8e8d1867e8f71830b10d25ed' + '2891faf12f3c7e75421a2ea264f9a915320d274fe1470742b984e96b98912081f' + 'acd478da18189038186000400209d483f28666881c6641f3a126f400f51e46511' + '70fe678c75e85712e2868adc850824997bebf0bc82b43028a6d2ec1777ca45279' + 'f7206a3ea8b5cd2073f493e45000cb54c3a5acaa268c56710428878d98b8afbf6' + '8a612153632846d807e92672698f1b9c611de7d38e34cd6c73889092c56e52d68' + '0f1dfd092b87ac8ef9ff3c8fb48', spki: '30819b301006072a8648ce3d020106052b81040023038186000400ee69f94715d7' + '01e9e2011333d4f4f96cba7d91f88b112baf75cf09cc1f8aca97618da9389822d2' + '9b6fe9996a61203ef752b771e8958fc4677bb3778565ab60d6ed00deab6761895b' + '935e3ad325fb8549e56f13786aa73f88a2ecfe40933473d8aef240c4dfd7d506f2' + '2cdd0e55558f3fbf05ebf7efef7a72d78f46469b8448f26e2712', result: '009c2bce57be80adab3b07385b8e5990eb7d6fdebdb01bf35371a4f6075e9d28', }, { namedCurve: 'P-384', pkcs8: '3081b6020100301006072a8648ce3d020106052b8104002204819e30819b02010' + '10430f871a5666589c14a5747263ef85b319cc023db6e35676c3d781eef8b055f' + 'cfbe86fa0d06d056b5195fb1323af8de25b3a16403620004f11965df7dd4594d0' + '419c5086482a3b826b9797f9be0bd0d109c9e1e9989c1b9a92b8f269f98e17ad1' + '84ba73c1f79762af45af8141602642da271a6bb0ffeb0cb4478fcf707e661aa6d' + '6cdf51549c88c3f130be9e8201f6f6a09f4185aaf95c4', spki: '3076301006072a8648ce3d020106052b810400220362000491822dc2af59c18f5b' + '67f80df61a2603c2a8f0b3c0af822d63c279701a824560404401dde9a56ee52757' + 'ea8bc748d4c82b5337b48d7b65583a3d572438880036bac6730f42ca5278966bd5' + 'f21e86e21d30c5a6d0463ec513dd509ffcdcaf1ff5', result: 'e0bd6bce0aef8ca48838a6e2fcc57e67b9c5e8860c5f0be9dabec53e454e18a0', }, ]; async function prepareKeys() { const keys = {}; await Promise.all( kTests.map(async ({ namedCurve, size, pkcs8, spki, result }) => { const [ privateKey, publicKey, ] = await Promise.all([ subtle.importKey( 'pkcs8', Buffer.from(pkcs8, 'hex'), { name: 'ECDH', namedCurve }, true, ['deriveKey', 'deriveBits']), subtle.importKey( 'spki', Buffer.from(spki, 'hex'), { name: 'ECDH', namedCurve }, true, ['deriveKey', 'deriveBits']), ]); keys[namedCurve] = { privateKey, publicKey, size, result, }; })); return keys; } (async function() { const keys = await prepareKeys(); const otherArgs = [ { name: 'HMAC', hash: 'SHA-256', length: 256 }, true, ['sign', 'verify']]; await Promise.all( Object.keys(keys).map(async (namedCurve) => { const { result, privateKey, publicKey } = keys[namedCurve]; { // Good parameters const key = await subtle.deriveKey({ name: 'ECDH', public: publicKey }, privateKey, ...otherArgs); const raw = await subtle.exportKey('raw', key); assert.strictEqual(Buffer.from(raw).toString('hex'), result); } { // Case insensitivity const key = await subtle.deriveKey({ name: 'eCdH', public: publicKey }, privateKey, { name: 'HmAc', hash: 'SHA-256', length: 256 }, true, ['sign', 'verify']); const raw = await subtle.exportKey('raw', key); assert.strictEqual(Buffer.from(raw).toString('hex'), result); } })); // Error tests { // Missing public property await assert.rejects( subtle.deriveKey( { name: 'ECDH' }, keys['P-384'].privateKey, ...otherArgs), { code: 'ERR_INVALID_ARG_TYPE' }); } { // The public property is not a CryptoKey await assert.rejects( subtle.deriveKey( { name: 'ECDH', public: { message: 'Not a CryptoKey' } }, keys['P-384'].privateKey, ...otherArgs), { code: 'ERR_INVALID_ARG_TYPE' }); } { // Mismatched named curves await assert.rejects( subtle.deriveKey( { name: 'ECDH', public: keys['P-384'].publicKey }, keys['P-521'].privateKey, ...otherArgs), { message: /Named curve mismatch/ }); } { // Incorrect public key algorithm const { publicKey } = await subtle.generateKey( { name: 'ECDSA', namedCurve: 'P-521' }, false, ['verify']); await assert.rejects( subtle.deriveKey( { name: 'ECDH', public: publicKey }, keys['P-521'].privateKey, ...otherArgs), { message: /Keys must be ECDH keys/ }); } { // Private key does not have correct usages const privateKey = await subtle.importKey( 'pkcs8', Buffer.from(kTests[0].pkcs8, 'hex'), { name: 'ECDH', namedCurve: 'P-521' }, false, ['deriveBits']); await assert.rejects( subtle.deriveKey( { name: 'ECDH', public: keys['P-521'].publicKey, }, privateKey, ...otherArgs), { message: /baseKey does not have deriveKey usage/ }); } { // Base key is not a private key await assert.rejects( subtle.deriveKey( { name: 'ECDH', public: keys['P-521'].publicKey }, keys['P-521'].publicKey, ...otherArgs), { message: /baseKey must be a private key/ }); } { // Base key is not a private key await assert.rejects( subtle.deriveKey( { name: 'ECDH', public: keys['P-521'].privateKey }, keys['P-521'].publicKey, ...otherArgs), { message: /algorithm\.public must be a public key/ }); } { // Public is a secret key const keyData = getRandomValues(new Uint8Array(32)); const key = await subtle.importKey( 'raw', keyData, { name: 'AES-CBC', length: 256 }, false, ['encrypt']); await assert.rejects( subtle.deriveKey( { name: 'ECDH', public: key }, keys['P-521'].publicKey, ...otherArgs), { message: /algorithm\.public must be a public key/ }); } })().then(common.mustCall());