11cb0ef41Sopenharmony_ci'use strict';
21cb0ef41Sopenharmony_ci
31cb0ef41Sopenharmony_ciconst {
41cb0ef41Sopenharmony_ci  ArrayPrototypeIncludes,
51cb0ef41Sopenharmony_ci  ObjectKeys,
61cb0ef41Sopenharmony_ci  SafeSet,
71cb0ef41Sopenharmony_ci} = primordials;
81cb0ef41Sopenharmony_ci
91cb0ef41Sopenharmony_ciconst {
101cb0ef41Sopenharmony_ci  ECKeyExportJob,
111cb0ef41Sopenharmony_ci  KeyObjectHandle,
121cb0ef41Sopenharmony_ci  SignJob,
131cb0ef41Sopenharmony_ci  kCryptoJobAsync,
141cb0ef41Sopenharmony_ci  kKeyTypePrivate,
151cb0ef41Sopenharmony_ci  kSignJobModeSign,
161cb0ef41Sopenharmony_ci  kSignJobModeVerify,
171cb0ef41Sopenharmony_ci  kSigEncP1363,
181cb0ef41Sopenharmony_ci} = internalBinding('crypto');
191cb0ef41Sopenharmony_ci
201cb0ef41Sopenharmony_ciconst {
211cb0ef41Sopenharmony_ci  getUsagesUnion,
221cb0ef41Sopenharmony_ci  hasAnyNotIn,
231cb0ef41Sopenharmony_ci  jobPromise,
241cb0ef41Sopenharmony_ci  normalizeHashName,
251cb0ef41Sopenharmony_ci  validateKeyOps,
261cb0ef41Sopenharmony_ci  kHandle,
271cb0ef41Sopenharmony_ci  kKeyObject,
281cb0ef41Sopenharmony_ci  kNamedCurveAliases,
291cb0ef41Sopenharmony_ci} = require('internal/crypto/util');
301cb0ef41Sopenharmony_ci
311cb0ef41Sopenharmony_ciconst {
321cb0ef41Sopenharmony_ci  lazyDOMException,
331cb0ef41Sopenharmony_ci  promisify,
341cb0ef41Sopenharmony_ci} = require('internal/util');
351cb0ef41Sopenharmony_ci
361cb0ef41Sopenharmony_ciconst {
371cb0ef41Sopenharmony_ci  generateKeyPair: _generateKeyPair,
381cb0ef41Sopenharmony_ci} = require('internal/crypto/keygen');
391cb0ef41Sopenharmony_ci
401cb0ef41Sopenharmony_ciconst {
411cb0ef41Sopenharmony_ci  InternalCryptoKey,
421cb0ef41Sopenharmony_ci  PrivateKeyObject,
431cb0ef41Sopenharmony_ci  PublicKeyObject,
441cb0ef41Sopenharmony_ci  createPrivateKey,
451cb0ef41Sopenharmony_ci  createPublicKey,
461cb0ef41Sopenharmony_ci} = require('internal/crypto/keys');
471cb0ef41Sopenharmony_ci
481cb0ef41Sopenharmony_ciconst generateKeyPair = promisify(_generateKeyPair);
491cb0ef41Sopenharmony_ci
501cb0ef41Sopenharmony_cifunction verifyAcceptableEcKeyUse(name, isPublic, usages) {
511cb0ef41Sopenharmony_ci  let checkSet;
521cb0ef41Sopenharmony_ci  switch (name) {
531cb0ef41Sopenharmony_ci    case 'ECDH':
541cb0ef41Sopenharmony_ci      checkSet = isPublic ? [] : ['deriveKey', 'deriveBits'];
551cb0ef41Sopenharmony_ci      break;
561cb0ef41Sopenharmony_ci    case 'ECDSA':
571cb0ef41Sopenharmony_ci      checkSet = isPublic ? ['verify'] : ['sign'];
581cb0ef41Sopenharmony_ci      break;
591cb0ef41Sopenharmony_ci    default:
601cb0ef41Sopenharmony_ci      throw lazyDOMException(
611cb0ef41Sopenharmony_ci        'The algorithm is not supported', 'NotSupportedError');
621cb0ef41Sopenharmony_ci  }
631cb0ef41Sopenharmony_ci  if (hasAnyNotIn(usages, checkSet)) {
641cb0ef41Sopenharmony_ci    throw lazyDOMException(
651cb0ef41Sopenharmony_ci      `Unsupported key usage for a ${name} key`,
661cb0ef41Sopenharmony_ci      'SyntaxError');
671cb0ef41Sopenharmony_ci  }
681cb0ef41Sopenharmony_ci}
691cb0ef41Sopenharmony_ci
701cb0ef41Sopenharmony_cifunction createECPublicKeyRaw(namedCurve, keyData) {
711cb0ef41Sopenharmony_ci  const handle = new KeyObjectHandle();
721cb0ef41Sopenharmony_ci
731cb0ef41Sopenharmony_ci  if (!handle.initECRaw(kNamedCurveAliases[namedCurve], keyData)) {
741cb0ef41Sopenharmony_ci    throw lazyDOMException('Invalid keyData', 'DataError');
751cb0ef41Sopenharmony_ci  }
761cb0ef41Sopenharmony_ci
771cb0ef41Sopenharmony_ci  return new PublicKeyObject(handle);
781cb0ef41Sopenharmony_ci}
791cb0ef41Sopenharmony_ci
801cb0ef41Sopenharmony_ciasync function ecGenerateKey(algorithm, extractable, keyUsages) {
811cb0ef41Sopenharmony_ci  const { name, namedCurve } = algorithm;
821cb0ef41Sopenharmony_ci
831cb0ef41Sopenharmony_ci  if (!ArrayPrototypeIncludes(ObjectKeys(kNamedCurveAliases), namedCurve)) {
841cb0ef41Sopenharmony_ci    throw lazyDOMException(
851cb0ef41Sopenharmony_ci      'Unrecognized namedCurve',
861cb0ef41Sopenharmony_ci      'NotSupportedError');
871cb0ef41Sopenharmony_ci  }
881cb0ef41Sopenharmony_ci
891cb0ef41Sopenharmony_ci  const usageSet = new SafeSet(keyUsages);
901cb0ef41Sopenharmony_ci  switch (name) {
911cb0ef41Sopenharmony_ci    case 'ECDSA':
921cb0ef41Sopenharmony_ci      if (hasAnyNotIn(usageSet, ['sign', 'verify'])) {
931cb0ef41Sopenharmony_ci        throw lazyDOMException(
941cb0ef41Sopenharmony_ci          'Unsupported key usage for an ECDSA key',
951cb0ef41Sopenharmony_ci          'SyntaxError');
961cb0ef41Sopenharmony_ci      }
971cb0ef41Sopenharmony_ci      break;
981cb0ef41Sopenharmony_ci    case 'ECDH':
991cb0ef41Sopenharmony_ci      if (hasAnyNotIn(usageSet, ['deriveKey', 'deriveBits'])) {
1001cb0ef41Sopenharmony_ci        throw lazyDOMException(
1011cb0ef41Sopenharmony_ci          'Unsupported key usage for an ECDH key',
1021cb0ef41Sopenharmony_ci          'SyntaxError');
1031cb0ef41Sopenharmony_ci      }
1041cb0ef41Sopenharmony_ci      // Fall through
1051cb0ef41Sopenharmony_ci  }
1061cb0ef41Sopenharmony_ci
1071cb0ef41Sopenharmony_ci  const keypair = await generateKeyPair('ec', { namedCurve }).catch((err) => {
1081cb0ef41Sopenharmony_ci    throw lazyDOMException(
1091cb0ef41Sopenharmony_ci      'The operation failed for an operation-specific reason',
1101cb0ef41Sopenharmony_ci      { name: 'OperationError', cause: err });
1111cb0ef41Sopenharmony_ci  });
1121cb0ef41Sopenharmony_ci
1131cb0ef41Sopenharmony_ci  let publicUsages;
1141cb0ef41Sopenharmony_ci  let privateUsages;
1151cb0ef41Sopenharmony_ci  switch (name) {
1161cb0ef41Sopenharmony_ci    case 'ECDSA':
1171cb0ef41Sopenharmony_ci      publicUsages = getUsagesUnion(usageSet, 'verify');
1181cb0ef41Sopenharmony_ci      privateUsages = getUsagesUnion(usageSet, 'sign');
1191cb0ef41Sopenharmony_ci      break;
1201cb0ef41Sopenharmony_ci    case 'ECDH':
1211cb0ef41Sopenharmony_ci      publicUsages = [];
1221cb0ef41Sopenharmony_ci      privateUsages = getUsagesUnion(usageSet, 'deriveKey', 'deriveBits');
1231cb0ef41Sopenharmony_ci      break;
1241cb0ef41Sopenharmony_ci  }
1251cb0ef41Sopenharmony_ci
1261cb0ef41Sopenharmony_ci  const keyAlgorithm = { name, namedCurve };
1271cb0ef41Sopenharmony_ci
1281cb0ef41Sopenharmony_ci  const publicKey =
1291cb0ef41Sopenharmony_ci    new InternalCryptoKey(
1301cb0ef41Sopenharmony_ci      keypair.publicKey,
1311cb0ef41Sopenharmony_ci      keyAlgorithm,
1321cb0ef41Sopenharmony_ci      publicUsages,
1331cb0ef41Sopenharmony_ci      true);
1341cb0ef41Sopenharmony_ci
1351cb0ef41Sopenharmony_ci  const privateKey =
1361cb0ef41Sopenharmony_ci    new InternalCryptoKey(
1371cb0ef41Sopenharmony_ci      keypair.privateKey,
1381cb0ef41Sopenharmony_ci      keyAlgorithm,
1391cb0ef41Sopenharmony_ci      privateUsages,
1401cb0ef41Sopenharmony_ci      extractable);
1411cb0ef41Sopenharmony_ci
1421cb0ef41Sopenharmony_ci  return { publicKey, privateKey };
1431cb0ef41Sopenharmony_ci}
1441cb0ef41Sopenharmony_ci
1451cb0ef41Sopenharmony_cifunction ecExportKey(key, format) {
1461cb0ef41Sopenharmony_ci  return jobPromise(() => new ECKeyExportJob(
1471cb0ef41Sopenharmony_ci    kCryptoJobAsync,
1481cb0ef41Sopenharmony_ci    format,
1491cb0ef41Sopenharmony_ci    key[kKeyObject][kHandle]));
1501cb0ef41Sopenharmony_ci}
1511cb0ef41Sopenharmony_ci
1521cb0ef41Sopenharmony_ciasync function ecImportKey(
1531cb0ef41Sopenharmony_ci  format,
1541cb0ef41Sopenharmony_ci  keyData,
1551cb0ef41Sopenharmony_ci  algorithm,
1561cb0ef41Sopenharmony_ci  extractable,
1571cb0ef41Sopenharmony_ci  keyUsages) {
1581cb0ef41Sopenharmony_ci
1591cb0ef41Sopenharmony_ci  const { name, namedCurve } = algorithm;
1601cb0ef41Sopenharmony_ci
1611cb0ef41Sopenharmony_ci  if (!ArrayPrototypeIncludes(ObjectKeys(kNamedCurveAliases), namedCurve)) {
1621cb0ef41Sopenharmony_ci    throw lazyDOMException(
1631cb0ef41Sopenharmony_ci      'Unrecognized namedCurve',
1641cb0ef41Sopenharmony_ci      'NotSupportedError');
1651cb0ef41Sopenharmony_ci  }
1661cb0ef41Sopenharmony_ci
1671cb0ef41Sopenharmony_ci  let keyObject;
1681cb0ef41Sopenharmony_ci  const usagesSet = new SafeSet(keyUsages);
1691cb0ef41Sopenharmony_ci  switch (format) {
1701cb0ef41Sopenharmony_ci    case 'spki': {
1711cb0ef41Sopenharmony_ci      verifyAcceptableEcKeyUse(name, true, usagesSet);
1721cb0ef41Sopenharmony_ci      try {
1731cb0ef41Sopenharmony_ci        keyObject = createPublicKey({
1741cb0ef41Sopenharmony_ci          key: keyData,
1751cb0ef41Sopenharmony_ci          format: 'der',
1761cb0ef41Sopenharmony_ci          type: 'spki',
1771cb0ef41Sopenharmony_ci        });
1781cb0ef41Sopenharmony_ci      } catch (err) {
1791cb0ef41Sopenharmony_ci        throw lazyDOMException(
1801cb0ef41Sopenharmony_ci          'Invalid keyData', { name: 'DataError', cause: err });
1811cb0ef41Sopenharmony_ci      }
1821cb0ef41Sopenharmony_ci      break;
1831cb0ef41Sopenharmony_ci    }
1841cb0ef41Sopenharmony_ci    case 'pkcs8': {
1851cb0ef41Sopenharmony_ci      verifyAcceptableEcKeyUse(name, false, usagesSet);
1861cb0ef41Sopenharmony_ci      try {
1871cb0ef41Sopenharmony_ci        keyObject = createPrivateKey({
1881cb0ef41Sopenharmony_ci          key: keyData,
1891cb0ef41Sopenharmony_ci          format: 'der',
1901cb0ef41Sopenharmony_ci          type: 'pkcs8',
1911cb0ef41Sopenharmony_ci        });
1921cb0ef41Sopenharmony_ci      } catch (err) {
1931cb0ef41Sopenharmony_ci        throw lazyDOMException(
1941cb0ef41Sopenharmony_ci          'Invalid keyData', { name: 'DataError', cause: err });
1951cb0ef41Sopenharmony_ci      }
1961cb0ef41Sopenharmony_ci      break;
1971cb0ef41Sopenharmony_ci    }
1981cb0ef41Sopenharmony_ci    case 'jwk': {
1991cb0ef41Sopenharmony_ci      if (!keyData.kty)
2001cb0ef41Sopenharmony_ci        throw lazyDOMException('Invalid keyData', 'DataError');
2011cb0ef41Sopenharmony_ci      if (keyData.kty !== 'EC')
2021cb0ef41Sopenharmony_ci        throw lazyDOMException('Invalid JWK "kty" Parameter', 'DataError');
2031cb0ef41Sopenharmony_ci      if (keyData.crv !== namedCurve)
2041cb0ef41Sopenharmony_ci        throw lazyDOMException(
2051cb0ef41Sopenharmony_ci          'JWK "crv" does not match the requested algorithm',
2061cb0ef41Sopenharmony_ci          'DataError');
2071cb0ef41Sopenharmony_ci
2081cb0ef41Sopenharmony_ci      verifyAcceptableEcKeyUse(
2091cb0ef41Sopenharmony_ci        name,
2101cb0ef41Sopenharmony_ci        keyData.d === undefined,
2111cb0ef41Sopenharmony_ci        usagesSet);
2121cb0ef41Sopenharmony_ci
2131cb0ef41Sopenharmony_ci      if (usagesSet.size > 0 && keyData.use !== undefined) {
2141cb0ef41Sopenharmony_ci        const checkUse = name === 'ECDH' ? 'enc' : 'sig';
2151cb0ef41Sopenharmony_ci        if (keyData.use !== checkUse)
2161cb0ef41Sopenharmony_ci          throw lazyDOMException('Invalid JWK "use" Parameter', 'DataError');
2171cb0ef41Sopenharmony_ci      }
2181cb0ef41Sopenharmony_ci
2191cb0ef41Sopenharmony_ci      validateKeyOps(keyData.key_ops, usagesSet);
2201cb0ef41Sopenharmony_ci
2211cb0ef41Sopenharmony_ci      if (keyData.ext !== undefined &&
2221cb0ef41Sopenharmony_ci          keyData.ext === false &&
2231cb0ef41Sopenharmony_ci          extractable === true) {
2241cb0ef41Sopenharmony_ci        throw lazyDOMException(
2251cb0ef41Sopenharmony_ci          'JWK "ext" Parameter and extractable mismatch',
2261cb0ef41Sopenharmony_ci          'DataError');
2271cb0ef41Sopenharmony_ci      }
2281cb0ef41Sopenharmony_ci
2291cb0ef41Sopenharmony_ci      if (algorithm.name === 'ECDSA' && keyData.alg !== undefined) {
2301cb0ef41Sopenharmony_ci        let algNamedCurve;
2311cb0ef41Sopenharmony_ci        switch (keyData.alg) {
2321cb0ef41Sopenharmony_ci          case 'ES256': algNamedCurve = 'P-256'; break;
2331cb0ef41Sopenharmony_ci          case 'ES384': algNamedCurve = 'P-384'; break;
2341cb0ef41Sopenharmony_ci          case 'ES512': algNamedCurve = 'P-521'; break;
2351cb0ef41Sopenharmony_ci        }
2361cb0ef41Sopenharmony_ci        if (algNamedCurve !== namedCurve)
2371cb0ef41Sopenharmony_ci          throw lazyDOMException(
2381cb0ef41Sopenharmony_ci            'JWK "alg" does not match the requested algorithm',
2391cb0ef41Sopenharmony_ci            'DataError');
2401cb0ef41Sopenharmony_ci      }
2411cb0ef41Sopenharmony_ci
2421cb0ef41Sopenharmony_ci      const handle = new KeyObjectHandle();
2431cb0ef41Sopenharmony_ci      const type = handle.initJwk(keyData, namedCurve);
2441cb0ef41Sopenharmony_ci      if (type === undefined)
2451cb0ef41Sopenharmony_ci        throw lazyDOMException('Invalid JWK', 'DataError');
2461cb0ef41Sopenharmony_ci      keyObject = type === kKeyTypePrivate ?
2471cb0ef41Sopenharmony_ci        new PrivateKeyObject(handle) :
2481cb0ef41Sopenharmony_ci        new PublicKeyObject(handle);
2491cb0ef41Sopenharmony_ci      break;
2501cb0ef41Sopenharmony_ci    }
2511cb0ef41Sopenharmony_ci    case 'raw': {
2521cb0ef41Sopenharmony_ci      verifyAcceptableEcKeyUse(name, true, usagesSet);
2531cb0ef41Sopenharmony_ci      keyObject = createECPublicKeyRaw(namedCurve, keyData);
2541cb0ef41Sopenharmony_ci      break;
2551cb0ef41Sopenharmony_ci    }
2561cb0ef41Sopenharmony_ci  }
2571cb0ef41Sopenharmony_ci
2581cb0ef41Sopenharmony_ci  switch (algorithm.name) {
2591cb0ef41Sopenharmony_ci    case 'ECDSA':
2601cb0ef41Sopenharmony_ci      // Fall through
2611cb0ef41Sopenharmony_ci    case 'ECDH':
2621cb0ef41Sopenharmony_ci      if (keyObject.asymmetricKeyType !== 'ec')
2631cb0ef41Sopenharmony_ci        throw lazyDOMException('Invalid key type', 'DataError');
2641cb0ef41Sopenharmony_ci      break;
2651cb0ef41Sopenharmony_ci  }
2661cb0ef41Sopenharmony_ci
2671cb0ef41Sopenharmony_ci  if (!keyObject[kHandle].checkEcKeyData()) {
2681cb0ef41Sopenharmony_ci    throw lazyDOMException('Invalid keyData', 'DataError');
2691cb0ef41Sopenharmony_ci  }
2701cb0ef41Sopenharmony_ci
2711cb0ef41Sopenharmony_ci  const {
2721cb0ef41Sopenharmony_ci    namedCurve: checkNamedCurve,
2731cb0ef41Sopenharmony_ci  } = keyObject[kHandle].keyDetail({});
2741cb0ef41Sopenharmony_ci  if (kNamedCurveAliases[namedCurve] !== checkNamedCurve)
2751cb0ef41Sopenharmony_ci    throw lazyDOMException('Named curve mismatch', 'DataError');
2761cb0ef41Sopenharmony_ci
2771cb0ef41Sopenharmony_ci  return new InternalCryptoKey(
2781cb0ef41Sopenharmony_ci    keyObject,
2791cb0ef41Sopenharmony_ci    { name, namedCurve },
2801cb0ef41Sopenharmony_ci    keyUsages,
2811cb0ef41Sopenharmony_ci    extractable);
2821cb0ef41Sopenharmony_ci}
2831cb0ef41Sopenharmony_ci
2841cb0ef41Sopenharmony_cifunction ecdsaSignVerify(key, data, { name, hash }, signature) {
2851cb0ef41Sopenharmony_ci  const mode = signature === undefined ? kSignJobModeSign : kSignJobModeVerify;
2861cb0ef41Sopenharmony_ci  const type = mode === kSignJobModeSign ? 'private' : 'public';
2871cb0ef41Sopenharmony_ci
2881cb0ef41Sopenharmony_ci  if (key.type !== type)
2891cb0ef41Sopenharmony_ci    throw lazyDOMException(`Key must be a ${type} key`, 'InvalidAccessError');
2901cb0ef41Sopenharmony_ci
2911cb0ef41Sopenharmony_ci  const hashname = normalizeHashName(hash.name);
2921cb0ef41Sopenharmony_ci
2931cb0ef41Sopenharmony_ci  return jobPromise(() => new SignJob(
2941cb0ef41Sopenharmony_ci    kCryptoJobAsync,
2951cb0ef41Sopenharmony_ci    mode,
2961cb0ef41Sopenharmony_ci    key[kKeyObject][kHandle],
2971cb0ef41Sopenharmony_ci    undefined,
2981cb0ef41Sopenharmony_ci    undefined,
2991cb0ef41Sopenharmony_ci    undefined,
3001cb0ef41Sopenharmony_ci    data,
3011cb0ef41Sopenharmony_ci    hashname,
3021cb0ef41Sopenharmony_ci    undefined,  // Salt length, not used with ECDSA
3031cb0ef41Sopenharmony_ci    undefined,  // PSS Padding, not used with ECDSA
3041cb0ef41Sopenharmony_ci    kSigEncP1363,
3051cb0ef41Sopenharmony_ci    signature));
3061cb0ef41Sopenharmony_ci}
3071cb0ef41Sopenharmony_ci
3081cb0ef41Sopenharmony_cimodule.exports = {
3091cb0ef41Sopenharmony_ci  ecExportKey,
3101cb0ef41Sopenharmony_ci  ecImportKey,
3111cb0ef41Sopenharmony_ci  ecGenerateKey,
3121cb0ef41Sopenharmony_ci  ecdsaSignVerify,
3131cb0ef41Sopenharmony_ci};
314