11cb0ef41Sopenharmony_ci'use strict';
21cb0ef41Sopenharmony_ci
31cb0ef41Sopenharmony_ciconst {
41cb0ef41Sopenharmony_ci  ArrayBufferIsView,
51cb0ef41Sopenharmony_ci  ArrayBufferPrototypeSlice,
61cb0ef41Sopenharmony_ci  ArrayFrom,
71cb0ef41Sopenharmony_ci  ArrayPrototypeIncludes,
81cb0ef41Sopenharmony_ci  ArrayPrototypePush,
91cb0ef41Sopenharmony_ci  MathFloor,
101cb0ef41Sopenharmony_ci  SafeSet,
111cb0ef41Sopenharmony_ci  TypedArrayPrototypeSlice,
121cb0ef41Sopenharmony_ci} = primordials;
131cb0ef41Sopenharmony_ci
141cb0ef41Sopenharmony_ciconst {
151cb0ef41Sopenharmony_ci  AESCipherJob,
161cb0ef41Sopenharmony_ci  KeyObjectHandle,
171cb0ef41Sopenharmony_ci  kCryptoJobAsync,
181cb0ef41Sopenharmony_ci  kKeyVariantAES_CTR_128,
191cb0ef41Sopenharmony_ci  kKeyVariantAES_CBC_128,
201cb0ef41Sopenharmony_ci  kKeyVariantAES_GCM_128,
211cb0ef41Sopenharmony_ci  kKeyVariantAES_KW_128,
221cb0ef41Sopenharmony_ci  kKeyVariantAES_CTR_192,
231cb0ef41Sopenharmony_ci  kKeyVariantAES_CBC_192,
241cb0ef41Sopenharmony_ci  kKeyVariantAES_GCM_192,
251cb0ef41Sopenharmony_ci  kKeyVariantAES_KW_192,
261cb0ef41Sopenharmony_ci  kKeyVariantAES_CTR_256,
271cb0ef41Sopenharmony_ci  kKeyVariantAES_CBC_256,
281cb0ef41Sopenharmony_ci  kKeyVariantAES_GCM_256,
291cb0ef41Sopenharmony_ci  kKeyVariantAES_KW_256,
301cb0ef41Sopenharmony_ci  kWebCryptoCipherDecrypt,
311cb0ef41Sopenharmony_ci  kWebCryptoCipherEncrypt,
321cb0ef41Sopenharmony_ci} = internalBinding('crypto');
331cb0ef41Sopenharmony_ci
341cb0ef41Sopenharmony_ciconst {
351cb0ef41Sopenharmony_ci  hasAnyNotIn,
361cb0ef41Sopenharmony_ci  jobPromise,
371cb0ef41Sopenharmony_ci  validateByteLength,
381cb0ef41Sopenharmony_ci  validateKeyOps,
391cb0ef41Sopenharmony_ci  validateMaxBufferLength,
401cb0ef41Sopenharmony_ci  kAesKeyLengths,
411cb0ef41Sopenharmony_ci  kHandle,
421cb0ef41Sopenharmony_ci  kKeyObject,
431cb0ef41Sopenharmony_ci} = require('internal/crypto/util');
441cb0ef41Sopenharmony_ci
451cb0ef41Sopenharmony_ciconst {
461cb0ef41Sopenharmony_ci  lazyDOMException,
471cb0ef41Sopenharmony_ci  promisify,
481cb0ef41Sopenharmony_ci} = require('internal/util');
491cb0ef41Sopenharmony_ci
501cb0ef41Sopenharmony_ciconst { PromiseReject } = primordials;
511cb0ef41Sopenharmony_ci
521cb0ef41Sopenharmony_ciconst {
531cb0ef41Sopenharmony_ci  InternalCryptoKey,
541cb0ef41Sopenharmony_ci  SecretKeyObject,
551cb0ef41Sopenharmony_ci  createSecretKey,
561cb0ef41Sopenharmony_ci} = require('internal/crypto/keys');
571cb0ef41Sopenharmony_ci
581cb0ef41Sopenharmony_ciconst {
591cb0ef41Sopenharmony_ci  generateKey: _generateKey,
601cb0ef41Sopenharmony_ci} = require('internal/crypto/keygen');
611cb0ef41Sopenharmony_ci
621cb0ef41Sopenharmony_ciconst kMaxCounterLength = 128;
631cb0ef41Sopenharmony_ciconst kTagLengths = [32, 64, 96, 104, 112, 120, 128];
641cb0ef41Sopenharmony_ciconst generateKey = promisify(_generateKey);
651cb0ef41Sopenharmony_ci
661cb0ef41Sopenharmony_cifunction getAlgorithmName(name, length) {
671cb0ef41Sopenharmony_ci  switch (name) {
681cb0ef41Sopenharmony_ci    case 'AES-CBC': return `A${length}CBC`;
691cb0ef41Sopenharmony_ci    case 'AES-CTR': return `A${length}CTR`;
701cb0ef41Sopenharmony_ci    case 'AES-GCM': return `A${length}GCM`;
711cb0ef41Sopenharmony_ci    case 'AES-KW': return `A${length}KW`;
721cb0ef41Sopenharmony_ci  }
731cb0ef41Sopenharmony_ci}
741cb0ef41Sopenharmony_ci
751cb0ef41Sopenharmony_cifunction validateKeyLength(length) {
761cb0ef41Sopenharmony_ci  if (length !== 128 && length !== 192 && length !== 256)
771cb0ef41Sopenharmony_ci    throw lazyDOMException('Invalid key length', 'DataError');
781cb0ef41Sopenharmony_ci}
791cb0ef41Sopenharmony_ci
801cb0ef41Sopenharmony_cifunction getVariant(name, length) {
811cb0ef41Sopenharmony_ci  switch (name) {
821cb0ef41Sopenharmony_ci    case 'AES-CBC':
831cb0ef41Sopenharmony_ci      switch (length) {
841cb0ef41Sopenharmony_ci        case 128: return kKeyVariantAES_CBC_128;
851cb0ef41Sopenharmony_ci        case 192: return kKeyVariantAES_CBC_192;
861cb0ef41Sopenharmony_ci        case 256: return kKeyVariantAES_CBC_256;
871cb0ef41Sopenharmony_ci      }
881cb0ef41Sopenharmony_ci      break;
891cb0ef41Sopenharmony_ci    case 'AES-CTR':
901cb0ef41Sopenharmony_ci      switch (length) {
911cb0ef41Sopenharmony_ci        case 128: return kKeyVariantAES_CTR_128;
921cb0ef41Sopenharmony_ci        case 192: return kKeyVariantAES_CTR_192;
931cb0ef41Sopenharmony_ci        case 256: return kKeyVariantAES_CTR_256;
941cb0ef41Sopenharmony_ci      }
951cb0ef41Sopenharmony_ci      break;
961cb0ef41Sopenharmony_ci    case 'AES-GCM':
971cb0ef41Sopenharmony_ci      switch (length) {
981cb0ef41Sopenharmony_ci        case 128: return kKeyVariantAES_GCM_128;
991cb0ef41Sopenharmony_ci        case 192: return kKeyVariantAES_GCM_192;
1001cb0ef41Sopenharmony_ci        case 256: return kKeyVariantAES_GCM_256;
1011cb0ef41Sopenharmony_ci      }
1021cb0ef41Sopenharmony_ci      break;
1031cb0ef41Sopenharmony_ci    case 'AES-KW':
1041cb0ef41Sopenharmony_ci      switch (length) {
1051cb0ef41Sopenharmony_ci        case 128: return kKeyVariantAES_KW_128;
1061cb0ef41Sopenharmony_ci        case 192: return kKeyVariantAES_KW_192;
1071cb0ef41Sopenharmony_ci        case 256: return kKeyVariantAES_KW_256;
1081cb0ef41Sopenharmony_ci      }
1091cb0ef41Sopenharmony_ci      break;
1101cb0ef41Sopenharmony_ci  }
1111cb0ef41Sopenharmony_ci}
1121cb0ef41Sopenharmony_ci
1131cb0ef41Sopenharmony_cifunction asyncAesCtrCipher(mode, key, data, { counter, length }) {
1141cb0ef41Sopenharmony_ci  validateByteLength(counter, 'algorithm.counter', 16);
1151cb0ef41Sopenharmony_ci  // The length must specify an integer between 1 and 128. While
1161cb0ef41Sopenharmony_ci  // there is no default, this should typically be 64.
1171cb0ef41Sopenharmony_ci  if (length === 0 || length > kMaxCounterLength) {
1181cb0ef41Sopenharmony_ci    throw lazyDOMException(
1191cb0ef41Sopenharmony_ci      'AES-CTR algorithm.length must be between 1 and 128',
1201cb0ef41Sopenharmony_ci      'OperationError');
1211cb0ef41Sopenharmony_ci  }
1221cb0ef41Sopenharmony_ci
1231cb0ef41Sopenharmony_ci  return jobPromise(() => new AESCipherJob(
1241cb0ef41Sopenharmony_ci    kCryptoJobAsync,
1251cb0ef41Sopenharmony_ci    mode,
1261cb0ef41Sopenharmony_ci    key[kKeyObject][kHandle],
1271cb0ef41Sopenharmony_ci    data,
1281cb0ef41Sopenharmony_ci    getVariant('AES-CTR', key.algorithm.length),
1291cb0ef41Sopenharmony_ci    counter,
1301cb0ef41Sopenharmony_ci    length));
1311cb0ef41Sopenharmony_ci}
1321cb0ef41Sopenharmony_ci
1331cb0ef41Sopenharmony_cifunction asyncAesCbcCipher(mode, key, data, { iv }) {
1341cb0ef41Sopenharmony_ci  validateByteLength(iv, 'algorithm.iv', 16);
1351cb0ef41Sopenharmony_ci  return jobPromise(() => new AESCipherJob(
1361cb0ef41Sopenharmony_ci    kCryptoJobAsync,
1371cb0ef41Sopenharmony_ci    mode,
1381cb0ef41Sopenharmony_ci    key[kKeyObject][kHandle],
1391cb0ef41Sopenharmony_ci    data,
1401cb0ef41Sopenharmony_ci    getVariant('AES-CBC', key.algorithm.length),
1411cb0ef41Sopenharmony_ci    iv));
1421cb0ef41Sopenharmony_ci}
1431cb0ef41Sopenharmony_ci
1441cb0ef41Sopenharmony_cifunction asyncAesKwCipher(mode, key, data) {
1451cb0ef41Sopenharmony_ci  return jobPromise(() => new AESCipherJob(
1461cb0ef41Sopenharmony_ci    kCryptoJobAsync,
1471cb0ef41Sopenharmony_ci    mode,
1481cb0ef41Sopenharmony_ci    key[kKeyObject][kHandle],
1491cb0ef41Sopenharmony_ci    data,
1501cb0ef41Sopenharmony_ci    getVariant('AES-KW', key.algorithm.length)));
1511cb0ef41Sopenharmony_ci}
1521cb0ef41Sopenharmony_ci
1531cb0ef41Sopenharmony_cifunction asyncAesGcmCipher(
1541cb0ef41Sopenharmony_ci  mode,
1551cb0ef41Sopenharmony_ci  key,
1561cb0ef41Sopenharmony_ci  data,
1571cb0ef41Sopenharmony_ci  { iv, additionalData, tagLength = 128 }) {
1581cb0ef41Sopenharmony_ci  if (!ArrayPrototypeIncludes(kTagLengths, tagLength)) {
1591cb0ef41Sopenharmony_ci    return PromiseReject(lazyDOMException(
1601cb0ef41Sopenharmony_ci      `${tagLength} is not a valid AES-GCM tag length`,
1611cb0ef41Sopenharmony_ci      'OperationError'));
1621cb0ef41Sopenharmony_ci  }
1631cb0ef41Sopenharmony_ci
1641cb0ef41Sopenharmony_ci  validateMaxBufferLength(iv, 'algorithm.iv');
1651cb0ef41Sopenharmony_ci
1661cb0ef41Sopenharmony_ci  if (additionalData !== undefined) {
1671cb0ef41Sopenharmony_ci    validateMaxBufferLength(additionalData, 'algorithm.additionalData');
1681cb0ef41Sopenharmony_ci  }
1691cb0ef41Sopenharmony_ci
1701cb0ef41Sopenharmony_ci  const tagByteLength = MathFloor(tagLength / 8);
1711cb0ef41Sopenharmony_ci  let tag;
1721cb0ef41Sopenharmony_ci  switch (mode) {
1731cb0ef41Sopenharmony_ci    case kWebCryptoCipherDecrypt: {
1741cb0ef41Sopenharmony_ci      const slice = ArrayBufferIsView(data) ?
1751cb0ef41Sopenharmony_ci        TypedArrayPrototypeSlice : ArrayBufferPrototypeSlice;
1761cb0ef41Sopenharmony_ci      tag = slice(data, -tagByteLength);
1771cb0ef41Sopenharmony_ci
1781cb0ef41Sopenharmony_ci      // Refs: https://www.w3.org/TR/WebCryptoAPI/#aes-gcm-operations
1791cb0ef41Sopenharmony_ci      //
1801cb0ef41Sopenharmony_ci      // > If *plaintext* has a length less than *tagLength* bits, then `throw`
1811cb0ef41Sopenharmony_ci      // > an `OperationError`.
1821cb0ef41Sopenharmony_ci      if (tagByteLength > tag.byteLength) {
1831cb0ef41Sopenharmony_ci        return PromiseReject(lazyDOMException(
1841cb0ef41Sopenharmony_ci          'The provided data is too small.',
1851cb0ef41Sopenharmony_ci          'OperationError'));
1861cb0ef41Sopenharmony_ci      }
1871cb0ef41Sopenharmony_ci
1881cb0ef41Sopenharmony_ci      data = slice(data, 0, -tagByteLength);
1891cb0ef41Sopenharmony_ci      break;
1901cb0ef41Sopenharmony_ci    }
1911cb0ef41Sopenharmony_ci    case kWebCryptoCipherEncrypt:
1921cb0ef41Sopenharmony_ci      tag = tagByteLength;
1931cb0ef41Sopenharmony_ci      break;
1941cb0ef41Sopenharmony_ci  }
1951cb0ef41Sopenharmony_ci
1961cb0ef41Sopenharmony_ci  return jobPromise(() => new AESCipherJob(
1971cb0ef41Sopenharmony_ci    kCryptoJobAsync,
1981cb0ef41Sopenharmony_ci    mode,
1991cb0ef41Sopenharmony_ci    key[kKeyObject][kHandle],
2001cb0ef41Sopenharmony_ci    data,
2011cb0ef41Sopenharmony_ci    getVariant('AES-GCM', key.algorithm.length),
2021cb0ef41Sopenharmony_ci    iv,
2031cb0ef41Sopenharmony_ci    tag,
2041cb0ef41Sopenharmony_ci    additionalData));
2051cb0ef41Sopenharmony_ci}
2061cb0ef41Sopenharmony_ci
2071cb0ef41Sopenharmony_cifunction aesCipher(mode, key, data, algorithm) {
2081cb0ef41Sopenharmony_ci  switch (algorithm.name) {
2091cb0ef41Sopenharmony_ci    case 'AES-CTR': return asyncAesCtrCipher(mode, key, data, algorithm);
2101cb0ef41Sopenharmony_ci    case 'AES-CBC': return asyncAesCbcCipher(mode, key, data, algorithm);
2111cb0ef41Sopenharmony_ci    case 'AES-GCM': return asyncAesGcmCipher(mode, key, data, algorithm);
2121cb0ef41Sopenharmony_ci    case 'AES-KW': return asyncAesKwCipher(mode, key, data);
2131cb0ef41Sopenharmony_ci  }
2141cb0ef41Sopenharmony_ci}
2151cb0ef41Sopenharmony_ci
2161cb0ef41Sopenharmony_ciasync function aesGenerateKey(algorithm, extractable, keyUsages) {
2171cb0ef41Sopenharmony_ci  const { name, length } = algorithm;
2181cb0ef41Sopenharmony_ci  if (!ArrayPrototypeIncludes(kAesKeyLengths, length)) {
2191cb0ef41Sopenharmony_ci    throw lazyDOMException(
2201cb0ef41Sopenharmony_ci      'AES key length must be 128, 192, or 256 bits',
2211cb0ef41Sopenharmony_ci      'OperationError');
2221cb0ef41Sopenharmony_ci  }
2231cb0ef41Sopenharmony_ci
2241cb0ef41Sopenharmony_ci  const checkUsages = ['wrapKey', 'unwrapKey'];
2251cb0ef41Sopenharmony_ci  if (name !== 'AES-KW')
2261cb0ef41Sopenharmony_ci    ArrayPrototypePush(checkUsages, 'encrypt', 'decrypt');
2271cb0ef41Sopenharmony_ci
2281cb0ef41Sopenharmony_ci  const usagesSet = new SafeSet(keyUsages);
2291cb0ef41Sopenharmony_ci  if (hasAnyNotIn(usagesSet, checkUsages)) {
2301cb0ef41Sopenharmony_ci    throw lazyDOMException(
2311cb0ef41Sopenharmony_ci      'Unsupported key usage for an AES key',
2321cb0ef41Sopenharmony_ci      'SyntaxError');
2331cb0ef41Sopenharmony_ci  }
2341cb0ef41Sopenharmony_ci
2351cb0ef41Sopenharmony_ci  const key = await generateKey('aes', { length }).catch((err) => {
2361cb0ef41Sopenharmony_ci    throw lazyDOMException(
2371cb0ef41Sopenharmony_ci      'The operation failed for an operation-specific reason' +
2381cb0ef41Sopenharmony_ci      `[${err.message}]`,
2391cb0ef41Sopenharmony_ci      { name: 'OperationError', cause: err });
2401cb0ef41Sopenharmony_ci  });
2411cb0ef41Sopenharmony_ci
2421cb0ef41Sopenharmony_ci  return new InternalCryptoKey(
2431cb0ef41Sopenharmony_ci    key,
2441cb0ef41Sopenharmony_ci    { name, length },
2451cb0ef41Sopenharmony_ci    ArrayFrom(usagesSet),
2461cb0ef41Sopenharmony_ci    extractable);
2471cb0ef41Sopenharmony_ci}
2481cb0ef41Sopenharmony_ci
2491cb0ef41Sopenharmony_ciasync function aesImportKey(
2501cb0ef41Sopenharmony_ci  algorithm,
2511cb0ef41Sopenharmony_ci  format,
2521cb0ef41Sopenharmony_ci  keyData,
2531cb0ef41Sopenharmony_ci  extractable,
2541cb0ef41Sopenharmony_ci  keyUsages) {
2551cb0ef41Sopenharmony_ci  const { name } = algorithm;
2561cb0ef41Sopenharmony_ci  const checkUsages = ['wrapKey', 'unwrapKey'];
2571cb0ef41Sopenharmony_ci  if (name !== 'AES-KW')
2581cb0ef41Sopenharmony_ci    ArrayPrototypePush(checkUsages, 'encrypt', 'decrypt');
2591cb0ef41Sopenharmony_ci
2601cb0ef41Sopenharmony_ci  const usagesSet = new SafeSet(keyUsages);
2611cb0ef41Sopenharmony_ci  if (hasAnyNotIn(usagesSet, checkUsages)) {
2621cb0ef41Sopenharmony_ci    throw lazyDOMException(
2631cb0ef41Sopenharmony_ci      'Unsupported key usage for an AES key',
2641cb0ef41Sopenharmony_ci      'SyntaxError');
2651cb0ef41Sopenharmony_ci  }
2661cb0ef41Sopenharmony_ci
2671cb0ef41Sopenharmony_ci  let keyObject;
2681cb0ef41Sopenharmony_ci  let length;
2691cb0ef41Sopenharmony_ci  switch (format) {
2701cb0ef41Sopenharmony_ci    case 'raw': {
2711cb0ef41Sopenharmony_ci      validateKeyLength(keyData.byteLength * 8);
2721cb0ef41Sopenharmony_ci      keyObject = createSecretKey(keyData);
2731cb0ef41Sopenharmony_ci      break;
2741cb0ef41Sopenharmony_ci    }
2751cb0ef41Sopenharmony_ci    case 'jwk': {
2761cb0ef41Sopenharmony_ci      if (!keyData.kty)
2771cb0ef41Sopenharmony_ci        throw lazyDOMException('Invalid keyData', 'DataError');
2781cb0ef41Sopenharmony_ci
2791cb0ef41Sopenharmony_ci      if (keyData.kty !== 'oct')
2801cb0ef41Sopenharmony_ci        throw lazyDOMException('Invalid JWK "kty" Parameter', 'DataError');
2811cb0ef41Sopenharmony_ci
2821cb0ef41Sopenharmony_ci      if (usagesSet.size > 0 &&
2831cb0ef41Sopenharmony_ci          keyData.use !== undefined &&
2841cb0ef41Sopenharmony_ci          keyData.use !== 'enc') {
2851cb0ef41Sopenharmony_ci        throw lazyDOMException('Invalid JWK "use" Parameter', 'DataError');
2861cb0ef41Sopenharmony_ci      }
2871cb0ef41Sopenharmony_ci
2881cb0ef41Sopenharmony_ci      validateKeyOps(keyData.key_ops, usagesSet);
2891cb0ef41Sopenharmony_ci
2901cb0ef41Sopenharmony_ci      if (keyData.ext !== undefined &&
2911cb0ef41Sopenharmony_ci          keyData.ext === false &&
2921cb0ef41Sopenharmony_ci          extractable === true) {
2931cb0ef41Sopenharmony_ci        throw lazyDOMException(
2941cb0ef41Sopenharmony_ci          'JWK "ext" Parameter and extractable mismatch',
2951cb0ef41Sopenharmony_ci          'DataError');
2961cb0ef41Sopenharmony_ci      }
2971cb0ef41Sopenharmony_ci
2981cb0ef41Sopenharmony_ci      const handle = new KeyObjectHandle();
2991cb0ef41Sopenharmony_ci      handle.initJwk(keyData);
3001cb0ef41Sopenharmony_ci
3011cb0ef41Sopenharmony_ci      ({ length } = handle.keyDetail({ }));
3021cb0ef41Sopenharmony_ci      validateKeyLength(length);
3031cb0ef41Sopenharmony_ci
3041cb0ef41Sopenharmony_ci      if (keyData.alg !== undefined) {
3051cb0ef41Sopenharmony_ci        if (keyData.alg !== getAlgorithmName(algorithm.name, length))
3061cb0ef41Sopenharmony_ci          throw lazyDOMException(
3071cb0ef41Sopenharmony_ci            'JWK "alg" does not match the requested algorithm',
3081cb0ef41Sopenharmony_ci            'DataError');
3091cb0ef41Sopenharmony_ci      }
3101cb0ef41Sopenharmony_ci
3111cb0ef41Sopenharmony_ci      keyObject = new SecretKeyObject(handle);
3121cb0ef41Sopenharmony_ci      break;
3131cb0ef41Sopenharmony_ci    }
3141cb0ef41Sopenharmony_ci    default:
3151cb0ef41Sopenharmony_ci      throw lazyDOMException(
3161cb0ef41Sopenharmony_ci        `Unable to import AES key with format ${format}`,
3171cb0ef41Sopenharmony_ci        'NotSupportedError');
3181cb0ef41Sopenharmony_ci  }
3191cb0ef41Sopenharmony_ci
3201cb0ef41Sopenharmony_ci  if (length === undefined) {
3211cb0ef41Sopenharmony_ci    ({ length } = keyObject[kHandle].keyDetail({ }));
3221cb0ef41Sopenharmony_ci    validateKeyLength(length);
3231cb0ef41Sopenharmony_ci  }
3241cb0ef41Sopenharmony_ci
3251cb0ef41Sopenharmony_ci  return new InternalCryptoKey(
3261cb0ef41Sopenharmony_ci    keyObject,
3271cb0ef41Sopenharmony_ci    { name, length },
3281cb0ef41Sopenharmony_ci    keyUsages,
3291cb0ef41Sopenharmony_ci    extractable);
3301cb0ef41Sopenharmony_ci}
3311cb0ef41Sopenharmony_ci
3321cb0ef41Sopenharmony_cimodule.exports = {
3331cb0ef41Sopenharmony_ci  aesCipher,
3341cb0ef41Sopenharmony_ci  aesGenerateKey,
3351cb0ef41Sopenharmony_ci  aesImportKey,
3361cb0ef41Sopenharmony_ci  getAlgorithmName,
3371cb0ef41Sopenharmony_ci};
338