11cb0ef41Sopenharmony_ci'use strict';
21cb0ef41Sopenharmony_ci
31cb0ef41Sopenharmony_ciconst common = require('../common');
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ciif (!common.hasCrypto)
61cb0ef41Sopenharmony_ci  common.skip('missing crypto');
71cb0ef41Sopenharmony_ci
81cb0ef41Sopenharmony_ciconst assert = require('assert');
91cb0ef41Sopenharmony_ciconst { subtle } = require('crypto').webcrypto;
101cb0ef41Sopenharmony_ci
111cb0ef41Sopenharmony_ciconst kWrappingData = {
121cb0ef41Sopenharmony_ci  'RSA-OAEP': {
131cb0ef41Sopenharmony_ci    generate: {
141cb0ef41Sopenharmony_ci      modulusLength: 4096,
151cb0ef41Sopenharmony_ci      publicExponent: new Uint8Array([1, 0, 1]),
161cb0ef41Sopenharmony_ci      hash: 'SHA-256',
171cb0ef41Sopenharmony_ci    },
181cb0ef41Sopenharmony_ci    wrap: { label: new Uint8Array(8) },
191cb0ef41Sopenharmony_ci    pair: true
201cb0ef41Sopenharmony_ci  },
211cb0ef41Sopenharmony_ci  'AES-CTR': {
221cb0ef41Sopenharmony_ci    generate: { length: 128 },
231cb0ef41Sopenharmony_ci    wrap: { counter: new Uint8Array(16), length: 64 },
241cb0ef41Sopenharmony_ci    pair: false
251cb0ef41Sopenharmony_ci  },
261cb0ef41Sopenharmony_ci  'AES-CBC': {
271cb0ef41Sopenharmony_ci    generate: { length: 128 },
281cb0ef41Sopenharmony_ci    wrap: { iv: new Uint8Array(16) },
291cb0ef41Sopenharmony_ci    pair: false
301cb0ef41Sopenharmony_ci  },
311cb0ef41Sopenharmony_ci  'AES-GCM': {
321cb0ef41Sopenharmony_ci    generate: { length: 128 },
331cb0ef41Sopenharmony_ci    wrap: {
341cb0ef41Sopenharmony_ci      iv: new Uint8Array(16),
351cb0ef41Sopenharmony_ci      additionalData: new Uint8Array(16),
361cb0ef41Sopenharmony_ci      tagLength: 64
371cb0ef41Sopenharmony_ci    },
381cb0ef41Sopenharmony_ci    pair: false
391cb0ef41Sopenharmony_ci  },
401cb0ef41Sopenharmony_ci  'AES-KW': {
411cb0ef41Sopenharmony_ci    generate: { length: 128 },
421cb0ef41Sopenharmony_ci    wrap: { },
431cb0ef41Sopenharmony_ci    pair: false
441cb0ef41Sopenharmony_ci  }
451cb0ef41Sopenharmony_ci};
461cb0ef41Sopenharmony_ci
471cb0ef41Sopenharmony_cifunction generateWrappingKeys() {
481cb0ef41Sopenharmony_ci  return Promise.all(Object.keys(kWrappingData).map(async (name) => {
491cb0ef41Sopenharmony_ci    const keys = await subtle.generateKey(
501cb0ef41Sopenharmony_ci      { name, ...kWrappingData[name].generate },
511cb0ef41Sopenharmony_ci      true,
521cb0ef41Sopenharmony_ci      ['wrapKey', 'unwrapKey']);
531cb0ef41Sopenharmony_ci    if (kWrappingData[name].pair) {
541cb0ef41Sopenharmony_ci      kWrappingData[name].wrappingKey = keys.publicKey;
551cb0ef41Sopenharmony_ci      kWrappingData[name].unwrappingKey = keys.privateKey;
561cb0ef41Sopenharmony_ci    } else {
571cb0ef41Sopenharmony_ci      kWrappingData[name].wrappingKey = keys;
581cb0ef41Sopenharmony_ci      kWrappingData[name].unwrappingKey = keys;
591cb0ef41Sopenharmony_ci    }
601cb0ef41Sopenharmony_ci  }));
611cb0ef41Sopenharmony_ci}
621cb0ef41Sopenharmony_ci
631cb0ef41Sopenharmony_ciasync function generateKeysToWrap() {
641cb0ef41Sopenharmony_ci  const parameters = [
651cb0ef41Sopenharmony_ci    {
661cb0ef41Sopenharmony_ci      algorithm: {
671cb0ef41Sopenharmony_ci        name: 'RSASSA-PKCS1-v1_5',
681cb0ef41Sopenharmony_ci        modulusLength: 1024,
691cb0ef41Sopenharmony_ci        publicExponent: new Uint8Array([1, 0, 1]),
701cb0ef41Sopenharmony_ci        hash: 'SHA-256'
711cb0ef41Sopenharmony_ci      },
721cb0ef41Sopenharmony_ci      privateUsages: ['sign'],
731cb0ef41Sopenharmony_ci      publicUsages: ['verify'],
741cb0ef41Sopenharmony_ci      pair: true,
751cb0ef41Sopenharmony_ci    },
761cb0ef41Sopenharmony_ci    {
771cb0ef41Sopenharmony_ci      algorithm: {
781cb0ef41Sopenharmony_ci        name: 'RSA-PSS',
791cb0ef41Sopenharmony_ci        modulusLength: 1024,
801cb0ef41Sopenharmony_ci        publicExponent: new Uint8Array([1, 0, 1]),
811cb0ef41Sopenharmony_ci        hash: 'SHA-256'
821cb0ef41Sopenharmony_ci      },
831cb0ef41Sopenharmony_ci      privateUsages: ['sign'],
841cb0ef41Sopenharmony_ci      publicUsages: ['verify'],
851cb0ef41Sopenharmony_ci      pair: true,
861cb0ef41Sopenharmony_ci    },
871cb0ef41Sopenharmony_ci    {
881cb0ef41Sopenharmony_ci      algorithm: {
891cb0ef41Sopenharmony_ci        name: 'RSA-OAEP',
901cb0ef41Sopenharmony_ci        modulusLength: 1024,
911cb0ef41Sopenharmony_ci        publicExponent: new Uint8Array([1, 0, 1]),
921cb0ef41Sopenharmony_ci        hash: 'SHA-256'
931cb0ef41Sopenharmony_ci      },
941cb0ef41Sopenharmony_ci      privateUsages: ['decrypt'],
951cb0ef41Sopenharmony_ci      publicUsages: ['encrypt'],
961cb0ef41Sopenharmony_ci      pair: true,
971cb0ef41Sopenharmony_ci    },
981cb0ef41Sopenharmony_ci    {
991cb0ef41Sopenharmony_ci      algorithm: {
1001cb0ef41Sopenharmony_ci        name: 'ECDSA',
1011cb0ef41Sopenharmony_ci        namedCurve: 'P-384'
1021cb0ef41Sopenharmony_ci      },
1031cb0ef41Sopenharmony_ci      privateUsages: ['sign'],
1041cb0ef41Sopenharmony_ci      publicUsages: ['verify'],
1051cb0ef41Sopenharmony_ci      pair: true,
1061cb0ef41Sopenharmony_ci    },
1071cb0ef41Sopenharmony_ci    {
1081cb0ef41Sopenharmony_ci      algorithm: {
1091cb0ef41Sopenharmony_ci        name: 'ECDH',
1101cb0ef41Sopenharmony_ci        namedCurve: 'P-384'
1111cb0ef41Sopenharmony_ci      },
1121cb0ef41Sopenharmony_ci      privateUsages: ['deriveBits'],
1131cb0ef41Sopenharmony_ci      publicUsages: [],
1141cb0ef41Sopenharmony_ci      pair: true,
1151cb0ef41Sopenharmony_ci    },
1161cb0ef41Sopenharmony_ci    {
1171cb0ef41Sopenharmony_ci      algorithm: {
1181cb0ef41Sopenharmony_ci        name: 'Ed25519',
1191cb0ef41Sopenharmony_ci      },
1201cb0ef41Sopenharmony_ci      privateUsages: ['sign'],
1211cb0ef41Sopenharmony_ci      publicUsages: ['verify'],
1221cb0ef41Sopenharmony_ci      pair: true,
1231cb0ef41Sopenharmony_ci    },
1241cb0ef41Sopenharmony_ci    {
1251cb0ef41Sopenharmony_ci      algorithm: {
1261cb0ef41Sopenharmony_ci        name: 'Ed448',
1271cb0ef41Sopenharmony_ci      },
1281cb0ef41Sopenharmony_ci      privateUsages: ['sign'],
1291cb0ef41Sopenharmony_ci      publicUsages: ['verify'],
1301cb0ef41Sopenharmony_ci      pair: true,
1311cb0ef41Sopenharmony_ci    },
1321cb0ef41Sopenharmony_ci    {
1331cb0ef41Sopenharmony_ci      algorithm: {
1341cb0ef41Sopenharmony_ci        name: 'X25519',
1351cb0ef41Sopenharmony_ci      },
1361cb0ef41Sopenharmony_ci      privateUsages: ['deriveBits'],
1371cb0ef41Sopenharmony_ci      publicUsages: [],
1381cb0ef41Sopenharmony_ci      pair: true,
1391cb0ef41Sopenharmony_ci    },
1401cb0ef41Sopenharmony_ci    {
1411cb0ef41Sopenharmony_ci      algorithm: {
1421cb0ef41Sopenharmony_ci        name: 'X448',
1431cb0ef41Sopenharmony_ci      },
1441cb0ef41Sopenharmony_ci      privateUsages: ['deriveBits'],
1451cb0ef41Sopenharmony_ci      publicUsages: [],
1461cb0ef41Sopenharmony_ci      pair: true,
1471cb0ef41Sopenharmony_ci    },
1481cb0ef41Sopenharmony_ci    {
1491cb0ef41Sopenharmony_ci      algorithm: {
1501cb0ef41Sopenharmony_ci        name: 'AES-CTR',
1511cb0ef41Sopenharmony_ci        length: 128
1521cb0ef41Sopenharmony_ci      },
1531cb0ef41Sopenharmony_ci      usages: ['encrypt', 'decrypt'],
1541cb0ef41Sopenharmony_ci      pair: false,
1551cb0ef41Sopenharmony_ci    },
1561cb0ef41Sopenharmony_ci    {
1571cb0ef41Sopenharmony_ci      algorithm: {
1581cb0ef41Sopenharmony_ci        name: 'AES-CBC',
1591cb0ef41Sopenharmony_ci        length: 128
1601cb0ef41Sopenharmony_ci      },
1611cb0ef41Sopenharmony_ci      usages: ['encrypt', 'decrypt'],
1621cb0ef41Sopenharmony_ci      pair: false,
1631cb0ef41Sopenharmony_ci    },
1641cb0ef41Sopenharmony_ci    {
1651cb0ef41Sopenharmony_ci      algorithm: {
1661cb0ef41Sopenharmony_ci        name: 'AES-GCM', length: 128
1671cb0ef41Sopenharmony_ci      },
1681cb0ef41Sopenharmony_ci      usages: ['encrypt', 'decrypt'],
1691cb0ef41Sopenharmony_ci      pair: false,
1701cb0ef41Sopenharmony_ci    },
1711cb0ef41Sopenharmony_ci    {
1721cb0ef41Sopenharmony_ci      algorithm: {
1731cb0ef41Sopenharmony_ci        name: 'AES-KW',
1741cb0ef41Sopenharmony_ci        length: 128
1751cb0ef41Sopenharmony_ci      },
1761cb0ef41Sopenharmony_ci      usages: ['wrapKey', 'unwrapKey'],
1771cb0ef41Sopenharmony_ci      pair: false,
1781cb0ef41Sopenharmony_ci    },
1791cb0ef41Sopenharmony_ci    {
1801cb0ef41Sopenharmony_ci      algorithm: {
1811cb0ef41Sopenharmony_ci        name: 'HMAC',
1821cb0ef41Sopenharmony_ci        length: 128,
1831cb0ef41Sopenharmony_ci        hash: 'SHA-256'
1841cb0ef41Sopenharmony_ci      },
1851cb0ef41Sopenharmony_ci      usages: ['sign', 'verify'],
1861cb0ef41Sopenharmony_ci      pair: false,
1871cb0ef41Sopenharmony_ci    },
1881cb0ef41Sopenharmony_ci  ];
1891cb0ef41Sopenharmony_ci
1901cb0ef41Sopenharmony_ci  const allkeys = await Promise.all(parameters.map(async (params) => {
1911cb0ef41Sopenharmony_ci    const usages = 'usages' in params ?
1921cb0ef41Sopenharmony_ci      params.usages :
1931cb0ef41Sopenharmony_ci      params.publicUsages.concat(params.privateUsages);
1941cb0ef41Sopenharmony_ci
1951cb0ef41Sopenharmony_ci    const keys = await subtle.generateKey(params.algorithm, true, usages);
1961cb0ef41Sopenharmony_ci
1971cb0ef41Sopenharmony_ci    if (params.pair) {
1981cb0ef41Sopenharmony_ci      return [
1991cb0ef41Sopenharmony_ci        {
2001cb0ef41Sopenharmony_ci          algorithm: params.algorithm,
2011cb0ef41Sopenharmony_ci          usages: params.publicUsages,
2021cb0ef41Sopenharmony_ci          key: keys.publicKey,
2031cb0ef41Sopenharmony_ci        },
2041cb0ef41Sopenharmony_ci        {
2051cb0ef41Sopenharmony_ci          algorithm: params.algorithm,
2061cb0ef41Sopenharmony_ci          usages: params.privateUsages,
2071cb0ef41Sopenharmony_ci          key: keys.privateKey,
2081cb0ef41Sopenharmony_ci        },
2091cb0ef41Sopenharmony_ci      ];
2101cb0ef41Sopenharmony_ci    }
2111cb0ef41Sopenharmony_ci
2121cb0ef41Sopenharmony_ci    return [{
2131cb0ef41Sopenharmony_ci      algorithm: params.algorithm,
2141cb0ef41Sopenharmony_ci      usages: params.usages,
2151cb0ef41Sopenharmony_ci      key: keys,
2161cb0ef41Sopenharmony_ci    }];
2171cb0ef41Sopenharmony_ci  }));
2181cb0ef41Sopenharmony_ci
2191cb0ef41Sopenharmony_ci  return allkeys.flat();
2201cb0ef41Sopenharmony_ci}
2211cb0ef41Sopenharmony_ci
2221cb0ef41Sopenharmony_cifunction getFormats(key) {
2231cb0ef41Sopenharmony_ci  switch (key.key.type) {
2241cb0ef41Sopenharmony_ci    case 'secret': return ['raw', 'jwk'];
2251cb0ef41Sopenharmony_ci    case 'public': return ['spki', 'jwk'];
2261cb0ef41Sopenharmony_ci    case 'private': return ['pkcs8', 'jwk'];
2271cb0ef41Sopenharmony_ci  }
2281cb0ef41Sopenharmony_ci}
2291cb0ef41Sopenharmony_ci
2301cb0ef41Sopenharmony_ci// If the wrapping algorithm is AES-KW, the exported key
2311cb0ef41Sopenharmony_ci// material length must be a multiple of 8.
2321cb0ef41Sopenharmony_ci// If the wrapping algorithm is RSA-OAEP, the exported key
2331cb0ef41Sopenharmony_ci// material maximum length is a factor of the modulusLength
2341cb0ef41Sopenharmony_ci//
2351cb0ef41Sopenharmony_ci// As per the NOTE in step 13 https://w3c.github.io/webcrypto/#SubtleCrypto-method-wrapKey
2361cb0ef41Sopenharmony_ci// we're padding AES-KW wrapped JWK to make sure it is always a multiple of 8 bytes
2371cb0ef41Sopenharmony_ci// in length
2381cb0ef41Sopenharmony_ciasync function wrappingIsPossible(name, exported) {
2391cb0ef41Sopenharmony_ci  if ('byteLength' in exported) {
2401cb0ef41Sopenharmony_ci    switch (name) {
2411cb0ef41Sopenharmony_ci      case 'AES-KW':
2421cb0ef41Sopenharmony_ci        return exported.byteLength % 8 === 0;
2431cb0ef41Sopenharmony_ci      case 'RSA-OAEP':
2441cb0ef41Sopenharmony_ci        return exported.byteLength <= 446;
2451cb0ef41Sopenharmony_ci    }
2461cb0ef41Sopenharmony_ci  } else if ('kty' in exported && name === 'RSA-OAEP') {
2471cb0ef41Sopenharmony_ci    return JSON.stringify(exported).length <= 478;
2481cb0ef41Sopenharmony_ci  }
2491cb0ef41Sopenharmony_ci  return true;
2501cb0ef41Sopenharmony_ci}
2511cb0ef41Sopenharmony_ci
2521cb0ef41Sopenharmony_ciasync function testWrap(wrappingKey, unwrappingKey, key, wrap, format) {
2531cb0ef41Sopenharmony_ci  const exported = await subtle.exportKey(format, key.key);
2541cb0ef41Sopenharmony_ci  if (!(await wrappingIsPossible(wrappingKey.algorithm.name, exported)))
2551cb0ef41Sopenharmony_ci    return;
2561cb0ef41Sopenharmony_ci
2571cb0ef41Sopenharmony_ci  const wrapped =
2581cb0ef41Sopenharmony_ci    await subtle.wrapKey(
2591cb0ef41Sopenharmony_ci      format,
2601cb0ef41Sopenharmony_ci      key.key,
2611cb0ef41Sopenharmony_ci      wrappingKey,
2621cb0ef41Sopenharmony_ci      { name: wrappingKey.algorithm.name, ...wrap });
2631cb0ef41Sopenharmony_ci  const unwrapped =
2641cb0ef41Sopenharmony_ci    await subtle.unwrapKey(
2651cb0ef41Sopenharmony_ci      format,
2661cb0ef41Sopenharmony_ci      wrapped,
2671cb0ef41Sopenharmony_ci      unwrappingKey,
2681cb0ef41Sopenharmony_ci      { name: wrappingKey.algorithm.name, ...wrap },
2691cb0ef41Sopenharmony_ci      key.algorithm,
2701cb0ef41Sopenharmony_ci      true,
2711cb0ef41Sopenharmony_ci      key.usages);
2721cb0ef41Sopenharmony_ci  assert(unwrapped.extractable);
2731cb0ef41Sopenharmony_ci
2741cb0ef41Sopenharmony_ci  const exportedAgain = await subtle.exportKey(format, unwrapped);
2751cb0ef41Sopenharmony_ci  assert.deepStrictEqual(exported, exportedAgain);
2761cb0ef41Sopenharmony_ci}
2771cb0ef41Sopenharmony_ci
2781cb0ef41Sopenharmony_cifunction testWrapping(name, keys) {
2791cb0ef41Sopenharmony_ci  const variations = [];
2801cb0ef41Sopenharmony_ci
2811cb0ef41Sopenharmony_ci  const {
2821cb0ef41Sopenharmony_ci    wrappingKey,
2831cb0ef41Sopenharmony_ci    unwrappingKey,
2841cb0ef41Sopenharmony_ci    wrap
2851cb0ef41Sopenharmony_ci  } = kWrappingData[name];
2861cb0ef41Sopenharmony_ci
2871cb0ef41Sopenharmony_ci  keys.forEach((key) => {
2881cb0ef41Sopenharmony_ci    getFormats(key).forEach((format) => {
2891cb0ef41Sopenharmony_ci      variations.push(testWrap(wrappingKey, unwrappingKey, key, wrap, format));
2901cb0ef41Sopenharmony_ci    });
2911cb0ef41Sopenharmony_ci  });
2921cb0ef41Sopenharmony_ci
2931cb0ef41Sopenharmony_ci  return variations;
2941cb0ef41Sopenharmony_ci}
2951cb0ef41Sopenharmony_ci
2961cb0ef41Sopenharmony_ci(async function() {
2971cb0ef41Sopenharmony_ci  await generateWrappingKeys();
2981cb0ef41Sopenharmony_ci  const keys = await generateKeysToWrap();
2991cb0ef41Sopenharmony_ci  const variations = [];
3001cb0ef41Sopenharmony_ci  Object.keys(kWrappingData).forEach((name) => {
3011cb0ef41Sopenharmony_ci    variations.push(...testWrapping(name, keys));
3021cb0ef41Sopenharmony_ci  });
3031cb0ef41Sopenharmony_ci  await Promise.all(variations);
3041cb0ef41Sopenharmony_ci})().then(common.mustCall());
305