11cb0ef41Sopenharmony_ci// Flags: --expose-internals --no-warnings
21cb0ef41Sopenharmony_ci'use strict';
31cb0ef41Sopenharmony_ci
41cb0ef41Sopenharmony_ciconst common = require('../common');
51cb0ef41Sopenharmony_ci
61cb0ef41Sopenharmony_ciif (!common.hasCrypto)
71cb0ef41Sopenharmony_ci  common.skip('missing crypto');
81cb0ef41Sopenharmony_ci
91cb0ef41Sopenharmony_ciconst assert = require('assert');
101cb0ef41Sopenharmony_ciconst { webcrypto: { subtle }, KeyObject } = require('crypto');
111cb0ef41Sopenharmony_ci
121cb0ef41Sopenharmony_ci// This is only a partial test. The WebCrypto Web Platform Tests
131cb0ef41Sopenharmony_ci// will provide much greater coverage.
141cb0ef41Sopenharmony_ci
151cb0ef41Sopenharmony_ci// Test ECDH key derivation
161cb0ef41Sopenharmony_ci{
171cb0ef41Sopenharmony_ci  async function test(namedCurve) {
181cb0ef41Sopenharmony_ci    const [alice, bob] = await Promise.all([
191cb0ef41Sopenharmony_ci      subtle.generateKey({ name: 'ECDH', namedCurve }, true, ['deriveKey']),
201cb0ef41Sopenharmony_ci      subtle.generateKey({ name: 'ECDH', namedCurve }, true, ['deriveKey']),
211cb0ef41Sopenharmony_ci    ]);
221cb0ef41Sopenharmony_ci
231cb0ef41Sopenharmony_ci    const [secret1, secret2] = await Promise.all([
241cb0ef41Sopenharmony_ci      subtle.deriveKey({
251cb0ef41Sopenharmony_ci        name: 'ECDH', namedCurve, public: alice.publicKey
261cb0ef41Sopenharmony_ci      }, bob.privateKey, {
271cb0ef41Sopenharmony_ci        name: 'AES-CBC',
281cb0ef41Sopenharmony_ci        length: 256
291cb0ef41Sopenharmony_ci      }, true, ['encrypt']),
301cb0ef41Sopenharmony_ci      subtle.deriveKey({
311cb0ef41Sopenharmony_ci        name: 'ECDH', namedCurve, public: bob.publicKey
321cb0ef41Sopenharmony_ci      }, alice.privateKey, {
331cb0ef41Sopenharmony_ci        name: 'AES-CBC',
341cb0ef41Sopenharmony_ci        length: 256
351cb0ef41Sopenharmony_ci      }, true, ['encrypt']),
361cb0ef41Sopenharmony_ci    ]);
371cb0ef41Sopenharmony_ci
381cb0ef41Sopenharmony_ci    const [raw1, raw2] = await Promise.all([
391cb0ef41Sopenharmony_ci      subtle.exportKey('raw', secret1),
401cb0ef41Sopenharmony_ci      subtle.exportKey('raw', secret2),
411cb0ef41Sopenharmony_ci    ]);
421cb0ef41Sopenharmony_ci
431cb0ef41Sopenharmony_ci    assert.deepStrictEqual(raw1, raw2);
441cb0ef41Sopenharmony_ci  }
451cb0ef41Sopenharmony_ci
461cb0ef41Sopenharmony_ci  test('P-521').then(common.mustCall());
471cb0ef41Sopenharmony_ci}
481cb0ef41Sopenharmony_ci
491cb0ef41Sopenharmony_ci// Test HKDF key derivation
501cb0ef41Sopenharmony_ci{
511cb0ef41Sopenharmony_ci  async function test(pass, info, salt, hash, expected) {
521cb0ef41Sopenharmony_ci    const ec = new TextEncoder();
531cb0ef41Sopenharmony_ci    const key = await subtle.importKey(
541cb0ef41Sopenharmony_ci      'raw',
551cb0ef41Sopenharmony_ci      ec.encode(pass),
561cb0ef41Sopenharmony_ci      { name: 'HKDF', hash },
571cb0ef41Sopenharmony_ci      false, ['deriveKey']);
581cb0ef41Sopenharmony_ci
591cb0ef41Sopenharmony_ci    const secret = await subtle.deriveKey({
601cb0ef41Sopenharmony_ci      name: 'HKDF',
611cb0ef41Sopenharmony_ci      hash,
621cb0ef41Sopenharmony_ci      salt: ec.encode(salt),
631cb0ef41Sopenharmony_ci      info: ec.encode(info)
641cb0ef41Sopenharmony_ci    }, key, {
651cb0ef41Sopenharmony_ci      name: 'AES-CTR',
661cb0ef41Sopenharmony_ci      length: 256
671cb0ef41Sopenharmony_ci    }, true, ['encrypt']);
681cb0ef41Sopenharmony_ci
691cb0ef41Sopenharmony_ci    const raw = await subtle.exportKey('raw', secret);
701cb0ef41Sopenharmony_ci
711cb0ef41Sopenharmony_ci    assert.strictEqual(Buffer.from(raw).toString('hex'), expected);
721cb0ef41Sopenharmony_ci  }
731cb0ef41Sopenharmony_ci
741cb0ef41Sopenharmony_ci  const kTests = [
751cb0ef41Sopenharmony_ci    ['hello', 'there', 'my friend', 'SHA-256',
761cb0ef41Sopenharmony_ci     '14d93b0ccd99d4f2cbd9fbfe9c830b5b8a43e3e45e32941ef21bdeb0fa87b6b6'],
771cb0ef41Sopenharmony_ci    ['hello', 'there', 'my friend', 'SHA-384',
781cb0ef41Sopenharmony_ci     'e36cf2cf943d8f3a88adb80f478745c336ac811b1a86d03a7d10eb0b6b52295c'],
791cb0ef41Sopenharmony_ci  ];
801cb0ef41Sopenharmony_ci
811cb0ef41Sopenharmony_ci  const tests = Promise.all(kTests.map((args) => test(...args)));
821cb0ef41Sopenharmony_ci
831cb0ef41Sopenharmony_ci  tests.then(common.mustCall());
841cb0ef41Sopenharmony_ci}
851cb0ef41Sopenharmony_ci
861cb0ef41Sopenharmony_ci// Test PBKDF2 key derivation
871cb0ef41Sopenharmony_ci{
881cb0ef41Sopenharmony_ci  async function test(pass, salt, iterations, hash, expected) {
891cb0ef41Sopenharmony_ci    const ec = new TextEncoder();
901cb0ef41Sopenharmony_ci    const key = await subtle.importKey(
911cb0ef41Sopenharmony_ci      'raw',
921cb0ef41Sopenharmony_ci      ec.encode(pass),
931cb0ef41Sopenharmony_ci      { name: 'PBKDF2', hash },
941cb0ef41Sopenharmony_ci      false, ['deriveKey']);
951cb0ef41Sopenharmony_ci    const secret = await subtle.deriveKey({
961cb0ef41Sopenharmony_ci      name: 'PBKDF2',
971cb0ef41Sopenharmony_ci      hash,
981cb0ef41Sopenharmony_ci      salt: ec.encode(salt),
991cb0ef41Sopenharmony_ci      iterations,
1001cb0ef41Sopenharmony_ci    }, key, {
1011cb0ef41Sopenharmony_ci      name: 'AES-CTR',
1021cb0ef41Sopenharmony_ci      length: 256
1031cb0ef41Sopenharmony_ci    }, true, ['encrypt']);
1041cb0ef41Sopenharmony_ci
1051cb0ef41Sopenharmony_ci    const raw = await subtle.exportKey('raw', secret);
1061cb0ef41Sopenharmony_ci
1071cb0ef41Sopenharmony_ci    assert.strictEqual(Buffer.from(raw).toString('hex'), expected);
1081cb0ef41Sopenharmony_ci  }
1091cb0ef41Sopenharmony_ci
1101cb0ef41Sopenharmony_ci  const kTests = [
1111cb0ef41Sopenharmony_ci    ['hello', 'there', 10, 'SHA-256',
1121cb0ef41Sopenharmony_ci     'f72d1cf4853fffbd16a42751765d11f8dc7939498ee7b7ce7678b4cb16fad880'],
1131cb0ef41Sopenharmony_ci    ['hello', 'there', 5, 'SHA-384',
1141cb0ef41Sopenharmony_ci     '201509b012c9cd2fbe7ea938f0c509b36ecb140f38bf9130e96923f55f46756d'],
1151cb0ef41Sopenharmony_ci  ];
1161cb0ef41Sopenharmony_ci
1171cb0ef41Sopenharmony_ci  const tests = Promise.all(kTests.map((args) => test(...args)));
1181cb0ef41Sopenharmony_ci
1191cb0ef41Sopenharmony_ci  tests.then(common.mustCall());
1201cb0ef41Sopenharmony_ci}
1211cb0ef41Sopenharmony_ci
1221cb0ef41Sopenharmony_ci// Test default key lengths
1231cb0ef41Sopenharmony_ci{
1241cb0ef41Sopenharmony_ci  const vectors = [
1251cb0ef41Sopenharmony_ci    ['PBKDF2', 'deriveKey', 528],
1261cb0ef41Sopenharmony_ci    ['HKDF', 'deriveKey', 528],
1271cb0ef41Sopenharmony_ci    [{ name: 'HMAC', hash: 'SHA-1' }, 'sign', 512],
1281cb0ef41Sopenharmony_ci    [{ name: 'HMAC', hash: 'SHA-256' }, 'sign', 512],
1291cb0ef41Sopenharmony_ci    // Not long enough secret generated by ECDH
1301cb0ef41Sopenharmony_ci    // [{ name: 'HMAC', hash: 'SHA-384' }, 'sign', 1024],
1311cb0ef41Sopenharmony_ci    // [{ name: 'HMAC', hash: 'SHA-512' }, 'sign', 1024],
1321cb0ef41Sopenharmony_ci  ];
1331cb0ef41Sopenharmony_ci
1341cb0ef41Sopenharmony_ci  (async () => {
1351cb0ef41Sopenharmony_ci    const keyPair = await subtle.generateKey({ name: 'ECDH', namedCurve: 'P-521' }, false, ['deriveKey']);
1361cb0ef41Sopenharmony_ci    for (const [derivedKeyAlgorithm, usage, expected] of vectors) {
1371cb0ef41Sopenharmony_ci      const derived = await subtle.deriveKey(
1381cb0ef41Sopenharmony_ci        { name: 'ECDH', public: keyPair.publicKey },
1391cb0ef41Sopenharmony_ci        keyPair.privateKey,
1401cb0ef41Sopenharmony_ci        derivedKeyAlgorithm,
1411cb0ef41Sopenharmony_ci        false,
1421cb0ef41Sopenharmony_ci        [usage]);
1431cb0ef41Sopenharmony_ci
1441cb0ef41Sopenharmony_ci      if (derived.algorithm.name === 'HMAC') {
1451cb0ef41Sopenharmony_ci        assert.strictEqual(derived.algorithm.length, expected);
1461cb0ef41Sopenharmony_ci      } else {
1471cb0ef41Sopenharmony_ci        // KDFs cannot be exportable and do not indicate their length
1481cb0ef41Sopenharmony_ci        const secretKey = KeyObject.from(derived);
1491cb0ef41Sopenharmony_ci        assert.strictEqual(secretKey.symmetricKeySize, expected / 8);
1501cb0ef41Sopenharmony_ci      }
1511cb0ef41Sopenharmony_ci    }
1521cb0ef41Sopenharmony_ci  })().then(common.mustCall());
1531cb0ef41Sopenharmony_ci}
1541cb0ef41Sopenharmony_ci
1551cb0ef41Sopenharmony_ci{
1561cb0ef41Sopenharmony_ci  const vectors = [
1571cb0ef41Sopenharmony_ci    [{ name: 'HMAC', hash: 'SHA-1' }, 'sign', 512],
1581cb0ef41Sopenharmony_ci    [{ name: 'HMAC', hash: 'SHA-256' }, 'sign', 512],
1591cb0ef41Sopenharmony_ci    [{ name: 'HMAC', hash: 'SHA-384' }, 'sign', 1024],
1601cb0ef41Sopenharmony_ci    [{ name: 'HMAC', hash: 'SHA-512' }, 'sign', 1024],
1611cb0ef41Sopenharmony_ci  ];
1621cb0ef41Sopenharmony_ci
1631cb0ef41Sopenharmony_ci  (async () => {
1641cb0ef41Sopenharmony_ci    for (const [derivedKeyAlgorithm, usage, expected] of vectors) {
1651cb0ef41Sopenharmony_ci      const derived = await subtle.deriveKey(
1661cb0ef41Sopenharmony_ci        { name: 'PBKDF2', salt: new Uint8Array([]), hash: 'SHA-256', iterations: 20 },
1671cb0ef41Sopenharmony_ci        await subtle.importKey('raw', new Uint8Array([]), { name: 'PBKDF2' }, false, ['deriveKey']),
1681cb0ef41Sopenharmony_ci        derivedKeyAlgorithm,
1691cb0ef41Sopenharmony_ci        false,
1701cb0ef41Sopenharmony_ci        [usage]);
1711cb0ef41Sopenharmony_ci
1721cb0ef41Sopenharmony_ci      assert.strictEqual(derived.algorithm.length, expected);
1731cb0ef41Sopenharmony_ci    }
1741cb0ef41Sopenharmony_ci  })().then(common.mustCall());
1751cb0ef41Sopenharmony_ci}
1761cb0ef41Sopenharmony_ci
1771cb0ef41Sopenharmony_ci// Test X25519 and X448 key derivation
1781cb0ef41Sopenharmony_ci{
1791cb0ef41Sopenharmony_ci  async function test(name) {
1801cb0ef41Sopenharmony_ci    const [alice, bob] = await Promise.all([
1811cb0ef41Sopenharmony_ci      subtle.generateKey({ name }, true, ['deriveKey']),
1821cb0ef41Sopenharmony_ci      subtle.generateKey({ name }, true, ['deriveKey']),
1831cb0ef41Sopenharmony_ci    ]);
1841cb0ef41Sopenharmony_ci
1851cb0ef41Sopenharmony_ci    const [secret1, secret2] = await Promise.all([
1861cb0ef41Sopenharmony_ci      subtle.deriveKey({
1871cb0ef41Sopenharmony_ci        name, public: alice.publicKey
1881cb0ef41Sopenharmony_ci      }, bob.privateKey, {
1891cb0ef41Sopenharmony_ci        name: 'AES-CBC',
1901cb0ef41Sopenharmony_ci        length: 256
1911cb0ef41Sopenharmony_ci      }, true, ['encrypt']),
1921cb0ef41Sopenharmony_ci      subtle.deriveKey({
1931cb0ef41Sopenharmony_ci        name, public: bob.publicKey
1941cb0ef41Sopenharmony_ci      }, alice.privateKey, {
1951cb0ef41Sopenharmony_ci        name: 'AES-CBC',
1961cb0ef41Sopenharmony_ci        length: 256
1971cb0ef41Sopenharmony_ci      }, true, ['encrypt']),
1981cb0ef41Sopenharmony_ci    ]);
1991cb0ef41Sopenharmony_ci
2001cb0ef41Sopenharmony_ci    const [raw1, raw2] = await Promise.all([
2011cb0ef41Sopenharmony_ci      subtle.exportKey('raw', secret1),
2021cb0ef41Sopenharmony_ci      subtle.exportKey('raw', secret2),
2031cb0ef41Sopenharmony_ci    ]);
2041cb0ef41Sopenharmony_ci
2051cb0ef41Sopenharmony_ci    assert.deepStrictEqual(raw1, raw2);
2061cb0ef41Sopenharmony_ci  }
2071cb0ef41Sopenharmony_ci
2081cb0ef41Sopenharmony_ci  test('X25519').then(common.mustCall());
2091cb0ef41Sopenharmony_ci  test('X448').then(common.mustCall());
2101cb0ef41Sopenharmony_ci}
211