11cb0ef41Sopenharmony_ci'use strict';
21cb0ef41Sopenharmony_ci
31cb0ef41Sopenharmony_ciconst {
41cb0ef41Sopenharmony_ci  SafeSet,
51cb0ef41Sopenharmony_ci  Uint8Array,
61cb0ef41Sopenharmony_ci} = primordials;
71cb0ef41Sopenharmony_ci
81cb0ef41Sopenharmony_ciconst {
91cb0ef41Sopenharmony_ci  KeyObjectHandle,
101cb0ef41Sopenharmony_ci  RSACipherJob,
111cb0ef41Sopenharmony_ci  RSAKeyExportJob,
121cb0ef41Sopenharmony_ci  SignJob,
131cb0ef41Sopenharmony_ci  kCryptoJobAsync,
141cb0ef41Sopenharmony_ci  kSignJobModeSign,
151cb0ef41Sopenharmony_ci  kSignJobModeVerify,
161cb0ef41Sopenharmony_ci  kKeyVariantRSA_SSA_PKCS1_v1_5,
171cb0ef41Sopenharmony_ci  kKeyVariantRSA_PSS,
181cb0ef41Sopenharmony_ci  kKeyVariantRSA_OAEP,
191cb0ef41Sopenharmony_ci  kKeyTypePrivate,
201cb0ef41Sopenharmony_ci  kWebCryptoCipherEncrypt,
211cb0ef41Sopenharmony_ci  RSA_PKCS1_PSS_PADDING,
221cb0ef41Sopenharmony_ci} = internalBinding('crypto');
231cb0ef41Sopenharmony_ci
241cb0ef41Sopenharmony_ciconst {
251cb0ef41Sopenharmony_ci  validateInt32,
261cb0ef41Sopenharmony_ci} = require('internal/validators');
271cb0ef41Sopenharmony_ci
281cb0ef41Sopenharmony_ciconst {
291cb0ef41Sopenharmony_ci  bigIntArrayToUnsignedInt,
301cb0ef41Sopenharmony_ci  getUsagesUnion,
311cb0ef41Sopenharmony_ci  hasAnyNotIn,
321cb0ef41Sopenharmony_ci  jobPromise,
331cb0ef41Sopenharmony_ci  normalizeHashName,
341cb0ef41Sopenharmony_ci  validateKeyOps,
351cb0ef41Sopenharmony_ci  validateMaxBufferLength,
361cb0ef41Sopenharmony_ci  kHandle,
371cb0ef41Sopenharmony_ci  kKeyObject,
381cb0ef41Sopenharmony_ci} = require('internal/crypto/util');
391cb0ef41Sopenharmony_ci
401cb0ef41Sopenharmony_ciconst {
411cb0ef41Sopenharmony_ci  lazyDOMException,
421cb0ef41Sopenharmony_ci  promisify,
431cb0ef41Sopenharmony_ci} = require('internal/util');
441cb0ef41Sopenharmony_ci
451cb0ef41Sopenharmony_ciconst {
461cb0ef41Sopenharmony_ci  InternalCryptoKey,
471cb0ef41Sopenharmony_ci  PrivateKeyObject,
481cb0ef41Sopenharmony_ci  PublicKeyObject,
491cb0ef41Sopenharmony_ci  createPublicKey,
501cb0ef41Sopenharmony_ci  createPrivateKey,
511cb0ef41Sopenharmony_ci} = require('internal/crypto/keys');
521cb0ef41Sopenharmony_ci
531cb0ef41Sopenharmony_ciconst {
541cb0ef41Sopenharmony_ci  generateKeyPair: _generateKeyPair,
551cb0ef41Sopenharmony_ci} = require('internal/crypto/keygen');
561cb0ef41Sopenharmony_ci
571cb0ef41Sopenharmony_ciconst kRsaVariants = {
581cb0ef41Sopenharmony_ci  'RSASSA-PKCS1-v1_5': kKeyVariantRSA_SSA_PKCS1_v1_5,
591cb0ef41Sopenharmony_ci  'RSA-PSS': kKeyVariantRSA_PSS,
601cb0ef41Sopenharmony_ci  'RSA-OAEP': kKeyVariantRSA_OAEP,
611cb0ef41Sopenharmony_ci};
621cb0ef41Sopenharmony_ciconst generateKeyPair = promisify(_generateKeyPair);
631cb0ef41Sopenharmony_ci
641cb0ef41Sopenharmony_cifunction verifyAcceptableRsaKeyUse(name, isPublic, usages) {
651cb0ef41Sopenharmony_ci  let checkSet;
661cb0ef41Sopenharmony_ci  switch (name) {
671cb0ef41Sopenharmony_ci    case 'RSA-OAEP':
681cb0ef41Sopenharmony_ci      checkSet = isPublic ? ['encrypt', 'wrapKey'] : ['decrypt', 'unwrapKey'];
691cb0ef41Sopenharmony_ci      break;
701cb0ef41Sopenharmony_ci    case 'RSA-PSS':
711cb0ef41Sopenharmony_ci      // Fall through
721cb0ef41Sopenharmony_ci    case 'RSASSA-PKCS1-v1_5':
731cb0ef41Sopenharmony_ci      checkSet = isPublic ? ['verify'] : ['sign'];
741cb0ef41Sopenharmony_ci      break;
751cb0ef41Sopenharmony_ci    default:
761cb0ef41Sopenharmony_ci      throw lazyDOMException(
771cb0ef41Sopenharmony_ci        'The algorithm is not supported', 'NotSupportedError');
781cb0ef41Sopenharmony_ci  }
791cb0ef41Sopenharmony_ci  if (hasAnyNotIn(usages, checkSet)) {
801cb0ef41Sopenharmony_ci    throw lazyDOMException(
811cb0ef41Sopenharmony_ci      `Unsupported key usage for an ${name} key`,
821cb0ef41Sopenharmony_ci      'SyntaxError');
831cb0ef41Sopenharmony_ci  }
841cb0ef41Sopenharmony_ci}
851cb0ef41Sopenharmony_ci
861cb0ef41Sopenharmony_cifunction rsaOaepCipher(mode, key, data, { label }) {
871cb0ef41Sopenharmony_ci  const type = mode === kWebCryptoCipherEncrypt ? 'public' : 'private';
881cb0ef41Sopenharmony_ci  if (key.type !== type) {
891cb0ef41Sopenharmony_ci    throw lazyDOMException(
901cb0ef41Sopenharmony_ci      'The requested operation is not valid for the provided key',
911cb0ef41Sopenharmony_ci      'InvalidAccessError');
921cb0ef41Sopenharmony_ci  }
931cb0ef41Sopenharmony_ci  if (label !== undefined) {
941cb0ef41Sopenharmony_ci    validateMaxBufferLength(label, 'algorithm.label');
951cb0ef41Sopenharmony_ci  }
961cb0ef41Sopenharmony_ci
971cb0ef41Sopenharmony_ci  return jobPromise(() => new RSACipherJob(
981cb0ef41Sopenharmony_ci    kCryptoJobAsync,
991cb0ef41Sopenharmony_ci    mode,
1001cb0ef41Sopenharmony_ci    key[kKeyObject][kHandle],
1011cb0ef41Sopenharmony_ci    data,
1021cb0ef41Sopenharmony_ci    kKeyVariantRSA_OAEP,
1031cb0ef41Sopenharmony_ci    normalizeHashName(key.algorithm.hash.name),
1041cb0ef41Sopenharmony_ci    label));
1051cb0ef41Sopenharmony_ci}
1061cb0ef41Sopenharmony_ci
1071cb0ef41Sopenharmony_ciasync function rsaKeyGenerate(
1081cb0ef41Sopenharmony_ci  algorithm,
1091cb0ef41Sopenharmony_ci  extractable,
1101cb0ef41Sopenharmony_ci  keyUsages) {
1111cb0ef41Sopenharmony_ci
1121cb0ef41Sopenharmony_ci  const {
1131cb0ef41Sopenharmony_ci    name,
1141cb0ef41Sopenharmony_ci    modulusLength,
1151cb0ef41Sopenharmony_ci    publicExponent,
1161cb0ef41Sopenharmony_ci    hash,
1171cb0ef41Sopenharmony_ci  } = algorithm;
1181cb0ef41Sopenharmony_ci
1191cb0ef41Sopenharmony_ci  const usageSet = new SafeSet(keyUsages);
1201cb0ef41Sopenharmony_ci
1211cb0ef41Sopenharmony_ci  const publicExponentConverted = bigIntArrayToUnsignedInt(publicExponent);
1221cb0ef41Sopenharmony_ci  if (publicExponentConverted === undefined) {
1231cb0ef41Sopenharmony_ci    throw lazyDOMException(
1241cb0ef41Sopenharmony_ci      'The publicExponent must be equivalent to an unsigned 32-bit value',
1251cb0ef41Sopenharmony_ci      'OperationError');
1261cb0ef41Sopenharmony_ci  }
1271cb0ef41Sopenharmony_ci
1281cb0ef41Sopenharmony_ci  switch (name) {
1291cb0ef41Sopenharmony_ci    case 'RSA-OAEP':
1301cb0ef41Sopenharmony_ci      if (hasAnyNotIn(usageSet,
1311cb0ef41Sopenharmony_ci                      ['encrypt', 'decrypt', 'wrapKey', 'unwrapKey'])) {
1321cb0ef41Sopenharmony_ci        throw lazyDOMException(
1331cb0ef41Sopenharmony_ci          'Unsupported key usage for a RSA key',
1341cb0ef41Sopenharmony_ci          'SyntaxError');
1351cb0ef41Sopenharmony_ci      }
1361cb0ef41Sopenharmony_ci      break;
1371cb0ef41Sopenharmony_ci    default:
1381cb0ef41Sopenharmony_ci      if (hasAnyNotIn(usageSet, ['sign', 'verify'])) {
1391cb0ef41Sopenharmony_ci        throw lazyDOMException(
1401cb0ef41Sopenharmony_ci          'Unsupported key usage for a RSA key',
1411cb0ef41Sopenharmony_ci          'SyntaxError');
1421cb0ef41Sopenharmony_ci      }
1431cb0ef41Sopenharmony_ci  }
1441cb0ef41Sopenharmony_ci
1451cb0ef41Sopenharmony_ci  const keypair = await generateKeyPair('rsa', {
1461cb0ef41Sopenharmony_ci    modulusLength,
1471cb0ef41Sopenharmony_ci    publicExponent: publicExponentConverted,
1481cb0ef41Sopenharmony_ci  }).catch((err) => {
1491cb0ef41Sopenharmony_ci    throw lazyDOMException(
1501cb0ef41Sopenharmony_ci      'The operation failed for an operation-specific reason',
1511cb0ef41Sopenharmony_ci      { name: 'OperationError', cause: err });
1521cb0ef41Sopenharmony_ci  });
1531cb0ef41Sopenharmony_ci
1541cb0ef41Sopenharmony_ci  const keyAlgorithm = {
1551cb0ef41Sopenharmony_ci    name,
1561cb0ef41Sopenharmony_ci    modulusLength,
1571cb0ef41Sopenharmony_ci    publicExponent,
1581cb0ef41Sopenharmony_ci    hash: { name: hash.name },
1591cb0ef41Sopenharmony_ci  };
1601cb0ef41Sopenharmony_ci
1611cb0ef41Sopenharmony_ci  let publicUsages;
1621cb0ef41Sopenharmony_ci  let privateUsages;
1631cb0ef41Sopenharmony_ci  switch (name) {
1641cb0ef41Sopenharmony_ci    case 'RSA-OAEP': {
1651cb0ef41Sopenharmony_ci      publicUsages = getUsagesUnion(usageSet, 'encrypt', 'wrapKey');
1661cb0ef41Sopenharmony_ci      privateUsages = getUsagesUnion(usageSet, 'decrypt', 'unwrapKey');
1671cb0ef41Sopenharmony_ci      break;
1681cb0ef41Sopenharmony_ci    }
1691cb0ef41Sopenharmony_ci    default: {
1701cb0ef41Sopenharmony_ci      publicUsages = getUsagesUnion(usageSet, 'verify');
1711cb0ef41Sopenharmony_ci      privateUsages = getUsagesUnion(usageSet, 'sign');
1721cb0ef41Sopenharmony_ci      break;
1731cb0ef41Sopenharmony_ci    }
1741cb0ef41Sopenharmony_ci  }
1751cb0ef41Sopenharmony_ci
1761cb0ef41Sopenharmony_ci  const publicKey =
1771cb0ef41Sopenharmony_ci    new InternalCryptoKey(
1781cb0ef41Sopenharmony_ci      keypair.publicKey,
1791cb0ef41Sopenharmony_ci      keyAlgorithm,
1801cb0ef41Sopenharmony_ci      publicUsages,
1811cb0ef41Sopenharmony_ci      true);
1821cb0ef41Sopenharmony_ci
1831cb0ef41Sopenharmony_ci  const privateKey =
1841cb0ef41Sopenharmony_ci    new InternalCryptoKey(
1851cb0ef41Sopenharmony_ci      keypair.privateKey,
1861cb0ef41Sopenharmony_ci      keyAlgorithm,
1871cb0ef41Sopenharmony_ci      privateUsages,
1881cb0ef41Sopenharmony_ci      extractable);
1891cb0ef41Sopenharmony_ci
1901cb0ef41Sopenharmony_ci  return { publicKey, privateKey };
1911cb0ef41Sopenharmony_ci}
1921cb0ef41Sopenharmony_ci
1931cb0ef41Sopenharmony_cifunction rsaExportKey(key, format) {
1941cb0ef41Sopenharmony_ci  return jobPromise(() => new RSAKeyExportJob(
1951cb0ef41Sopenharmony_ci    kCryptoJobAsync,
1961cb0ef41Sopenharmony_ci    format,
1971cb0ef41Sopenharmony_ci    key[kKeyObject][kHandle],
1981cb0ef41Sopenharmony_ci    kRsaVariants[key.algorithm.name]));
1991cb0ef41Sopenharmony_ci}
2001cb0ef41Sopenharmony_ci
2011cb0ef41Sopenharmony_ciasync function rsaImportKey(
2021cb0ef41Sopenharmony_ci  format,
2031cb0ef41Sopenharmony_ci  keyData,
2041cb0ef41Sopenharmony_ci  algorithm,
2051cb0ef41Sopenharmony_ci  extractable,
2061cb0ef41Sopenharmony_ci  keyUsages) {
2071cb0ef41Sopenharmony_ci  const usagesSet = new SafeSet(keyUsages);
2081cb0ef41Sopenharmony_ci  let keyObject;
2091cb0ef41Sopenharmony_ci  switch (format) {
2101cb0ef41Sopenharmony_ci    case 'spki': {
2111cb0ef41Sopenharmony_ci      verifyAcceptableRsaKeyUse(algorithm.name, true, usagesSet);
2121cb0ef41Sopenharmony_ci      try {
2131cb0ef41Sopenharmony_ci        keyObject = createPublicKey({
2141cb0ef41Sopenharmony_ci          key: keyData,
2151cb0ef41Sopenharmony_ci          format: 'der',
2161cb0ef41Sopenharmony_ci          type: 'spki',
2171cb0ef41Sopenharmony_ci        });
2181cb0ef41Sopenharmony_ci      } catch (err) {
2191cb0ef41Sopenharmony_ci        throw lazyDOMException(
2201cb0ef41Sopenharmony_ci          'Invalid keyData', { name: 'DataError', cause: err });
2211cb0ef41Sopenharmony_ci      }
2221cb0ef41Sopenharmony_ci      break;
2231cb0ef41Sopenharmony_ci    }
2241cb0ef41Sopenharmony_ci    case 'pkcs8': {
2251cb0ef41Sopenharmony_ci      verifyAcceptableRsaKeyUse(algorithm.name, false, usagesSet);
2261cb0ef41Sopenharmony_ci      try {
2271cb0ef41Sopenharmony_ci        keyObject = createPrivateKey({
2281cb0ef41Sopenharmony_ci          key: keyData,
2291cb0ef41Sopenharmony_ci          format: 'der',
2301cb0ef41Sopenharmony_ci          type: 'pkcs8',
2311cb0ef41Sopenharmony_ci        });
2321cb0ef41Sopenharmony_ci      } catch (err) {
2331cb0ef41Sopenharmony_ci        throw lazyDOMException(
2341cb0ef41Sopenharmony_ci          'Invalid keyData', { name: 'DataError', cause: err });
2351cb0ef41Sopenharmony_ci      }
2361cb0ef41Sopenharmony_ci      break;
2371cb0ef41Sopenharmony_ci    }
2381cb0ef41Sopenharmony_ci    case 'jwk': {
2391cb0ef41Sopenharmony_ci      if (!keyData.kty)
2401cb0ef41Sopenharmony_ci        throw lazyDOMException('Invalid keyData', 'DataError');
2411cb0ef41Sopenharmony_ci
2421cb0ef41Sopenharmony_ci      if (keyData.kty !== 'RSA')
2431cb0ef41Sopenharmony_ci        throw lazyDOMException('Invalid JWK "kty" Parameter', 'DataError');
2441cb0ef41Sopenharmony_ci
2451cb0ef41Sopenharmony_ci      verifyAcceptableRsaKeyUse(
2461cb0ef41Sopenharmony_ci        algorithm.name,
2471cb0ef41Sopenharmony_ci        keyData.d === undefined,
2481cb0ef41Sopenharmony_ci        usagesSet);
2491cb0ef41Sopenharmony_ci
2501cb0ef41Sopenharmony_ci      if (usagesSet.size > 0 && keyData.use !== undefined) {
2511cb0ef41Sopenharmony_ci        const checkUse = algorithm.name === 'RSA-OAEP' ? 'enc' : 'sig';
2521cb0ef41Sopenharmony_ci        if (keyData.use !== checkUse)
2531cb0ef41Sopenharmony_ci          throw lazyDOMException('Invalid JWK "use" Parameter', 'DataError');
2541cb0ef41Sopenharmony_ci      }
2551cb0ef41Sopenharmony_ci
2561cb0ef41Sopenharmony_ci      validateKeyOps(keyData.key_ops, usagesSet);
2571cb0ef41Sopenharmony_ci
2581cb0ef41Sopenharmony_ci      if (keyData.ext !== undefined &&
2591cb0ef41Sopenharmony_ci          keyData.ext === false &&
2601cb0ef41Sopenharmony_ci          extractable === true) {
2611cb0ef41Sopenharmony_ci        throw lazyDOMException(
2621cb0ef41Sopenharmony_ci          'JWK "ext" Parameter and extractable mismatch',
2631cb0ef41Sopenharmony_ci          'DataError');
2641cb0ef41Sopenharmony_ci      }
2651cb0ef41Sopenharmony_ci
2661cb0ef41Sopenharmony_ci      if (keyData.alg !== undefined) {
2671cb0ef41Sopenharmony_ci        const hash =
2681cb0ef41Sopenharmony_ci          normalizeHashName(keyData.alg, normalizeHashName.kContextWebCrypto);
2691cb0ef41Sopenharmony_ci        if (hash !== algorithm.hash.name)
2701cb0ef41Sopenharmony_ci          throw lazyDOMException(
2711cb0ef41Sopenharmony_ci            'JWK "alg" does not match the requested algorithm',
2721cb0ef41Sopenharmony_ci            'DataError');
2731cb0ef41Sopenharmony_ci      }
2741cb0ef41Sopenharmony_ci
2751cb0ef41Sopenharmony_ci      const handle = new KeyObjectHandle();
2761cb0ef41Sopenharmony_ci      const type = handle.initJwk(keyData);
2771cb0ef41Sopenharmony_ci      if (type === undefined)
2781cb0ef41Sopenharmony_ci        throw lazyDOMException('Invalid JWK', 'DataError');
2791cb0ef41Sopenharmony_ci
2801cb0ef41Sopenharmony_ci      keyObject = type === kKeyTypePrivate ?
2811cb0ef41Sopenharmony_ci        new PrivateKeyObject(handle) :
2821cb0ef41Sopenharmony_ci        new PublicKeyObject(handle);
2831cb0ef41Sopenharmony_ci
2841cb0ef41Sopenharmony_ci      break;
2851cb0ef41Sopenharmony_ci    }
2861cb0ef41Sopenharmony_ci    default:
2871cb0ef41Sopenharmony_ci      throw lazyDOMException(
2881cb0ef41Sopenharmony_ci        `Unable to import RSA key with format ${format}`,
2891cb0ef41Sopenharmony_ci        'NotSupportedError');
2901cb0ef41Sopenharmony_ci  }
2911cb0ef41Sopenharmony_ci
2921cb0ef41Sopenharmony_ci  if (keyObject.asymmetricKeyType !== 'rsa') {
2931cb0ef41Sopenharmony_ci    throw lazyDOMException('Invalid key type', 'DataError');
2941cb0ef41Sopenharmony_ci  }
2951cb0ef41Sopenharmony_ci
2961cb0ef41Sopenharmony_ci  const {
2971cb0ef41Sopenharmony_ci    modulusLength,
2981cb0ef41Sopenharmony_ci    publicExponent,
2991cb0ef41Sopenharmony_ci  } = keyObject[kHandle].keyDetail({});
3001cb0ef41Sopenharmony_ci
3011cb0ef41Sopenharmony_ci  return new InternalCryptoKey(keyObject, {
3021cb0ef41Sopenharmony_ci    name: algorithm.name,
3031cb0ef41Sopenharmony_ci    modulusLength,
3041cb0ef41Sopenharmony_ci    publicExponent: new Uint8Array(publicExponent),
3051cb0ef41Sopenharmony_ci    hash: algorithm.hash,
3061cb0ef41Sopenharmony_ci  }, keyUsages, extractable);
3071cb0ef41Sopenharmony_ci}
3081cb0ef41Sopenharmony_ci
3091cb0ef41Sopenharmony_cifunction rsaSignVerify(key, data, { saltLength }, signature) {
3101cb0ef41Sopenharmony_ci  let padding;
3111cb0ef41Sopenharmony_ci  if (key.algorithm.name === 'RSA-PSS') {
3121cb0ef41Sopenharmony_ci    padding = RSA_PKCS1_PSS_PADDING;
3131cb0ef41Sopenharmony_ci    // TODO(@jasnell): Validate maximum size of saltLength
3141cb0ef41Sopenharmony_ci    // based on the key size:
3151cb0ef41Sopenharmony_ci    //   Math.ceil((keySizeInBits - 1)/8) - digestSizeInBytes - 2
3161cb0ef41Sopenharmony_ci    validateInt32(saltLength, 'algorithm.saltLength', -2);
3171cb0ef41Sopenharmony_ci  }
3181cb0ef41Sopenharmony_ci
3191cb0ef41Sopenharmony_ci  const mode = signature === undefined ? kSignJobModeSign : kSignJobModeVerify;
3201cb0ef41Sopenharmony_ci  const type = mode === kSignJobModeSign ? 'private' : 'public';
3211cb0ef41Sopenharmony_ci
3221cb0ef41Sopenharmony_ci  if (key.type !== type)
3231cb0ef41Sopenharmony_ci    throw lazyDOMException(`Key must be a ${type} key`, 'InvalidAccessError');
3241cb0ef41Sopenharmony_ci
3251cb0ef41Sopenharmony_ci  return jobPromise(() => new SignJob(
3261cb0ef41Sopenharmony_ci    kCryptoJobAsync,
3271cb0ef41Sopenharmony_ci    signature === undefined ? kSignJobModeSign : kSignJobModeVerify,
3281cb0ef41Sopenharmony_ci    key[kKeyObject][kHandle],
3291cb0ef41Sopenharmony_ci    undefined,
3301cb0ef41Sopenharmony_ci    undefined,
3311cb0ef41Sopenharmony_ci    undefined,
3321cb0ef41Sopenharmony_ci    data,
3331cb0ef41Sopenharmony_ci    normalizeHashName(key.algorithm.hash.name),
3341cb0ef41Sopenharmony_ci    saltLength,
3351cb0ef41Sopenharmony_ci    padding,
3361cb0ef41Sopenharmony_ci    undefined,
3371cb0ef41Sopenharmony_ci    signature));
3381cb0ef41Sopenharmony_ci}
3391cb0ef41Sopenharmony_ci
3401cb0ef41Sopenharmony_ci
3411cb0ef41Sopenharmony_cimodule.exports = {
3421cb0ef41Sopenharmony_ci  rsaCipher: rsaOaepCipher,
3431cb0ef41Sopenharmony_ci  rsaExportKey,
3441cb0ef41Sopenharmony_ci  rsaImportKey,
3451cb0ef41Sopenharmony_ci  rsaKeyGenerate,
3461cb0ef41Sopenharmony_ci  rsaSignVerify,
3471cb0ef41Sopenharmony_ci};
348