{"version":3,"file":"encryption.mjs","sourceRoot":"","sources":["../../../src/shared/encryption/encryption.ts"],"names":[],"mappings":"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","sourcesContent":["import { gcm } from '@noble/ciphers/aes';\nimport { randomBytes } from '@noble/ciphers/webcrypto';\nimport { scryptAsync } from '@noble/hashes/scrypt';\nimport { sha256 } from '@noble/hashes/sha256';\nimport { utf8ToBytes, concatBytes, bytesToHex } from '@noble/hashes/utils';\n\nimport {\n  getCachedKeyBySalt,\n  getCachedKeyGeneratedWithSharedSalt,\n  setCachedKey,\n} from './cache';\nimport {\n  ALGORITHM_KEY_SIZE,\n  ALGORITHM_NONCE_SIZE,\n  MAX_KDF_PROMISE_CACHE_SIZE,\n  SCRYPT_N,\n  SCRYPT_p,\n  SCRYPT_r,\n  SCRYPT_SALT_SIZE,\n  SHARED_SALT,\n} from './constants';\nimport {\n  base64ToByteArray,\n  byteArrayToBase64,\n  bytesToUtf8,\n  stringToByteArray,\n} from './utils';\nimport type { NativeScrypt } from '../types/encryption';\n\nexport type EncryptedPayload = {\n  // version\n  v: '1';\n\n  // key derivation function algorithm - scrypt\n  t: 'scrypt';\n\n  // data\n  d: string;\n\n  // encryption options - scrypt\n  o: {\n    N: number;\n    r: number;\n    p: number;\n    dkLen: number;\n  };\n\n  // Salt options\n  saltLen: number;\n};\n\nclass EncryptorDecryptor {\n  // Promise cache for ongoing KDF operations to prevent duplicate work\n  readonly #kdfPromiseCache = new Map<\n    string,\n    Promise<{ key: Uint8Array; salt: Uint8Array }>\n  >();\n\n  async encryptString(\n    plaintext: string,\n    password: string,\n    nativeScryptCrypto?: NativeScrypt,\n  ): Promise<string> {\n    try {\n      return await this.#encryptStringV1(\n        plaintext,\n        password,\n        nativeScryptCrypto,\n      );\n    } catch (e) {\n      const errorMessage = e instanceof Error ? e.message : JSON.stringify(e);\n      throw new Error(`Unable to encrypt string - ${errorMessage}`);\n    }\n  }\n\n  async decryptString(\n    encryptedDataStr: string,\n    password: string,\n    nativeScryptCrypto?: NativeScrypt,\n  ): Promise<string> {\n    try {\n      const encryptedData: EncryptedPayload = JSON.parse(encryptedDataStr);\n      if (encryptedData.v === '1') {\n        if (encryptedData.t === 'scrypt') {\n          return await this.#decryptStringV1(\n            encryptedData,\n            password,\n            nativeScryptCrypto,\n          );\n        }\n      }\n      throw new Error(\n        `Unsupported encrypted data payload - ${encryptedDataStr}`,\n      );\n    } catch (e) {\n      const errorMessage = e instanceof Error ? e.message : JSON.stringify(e);\n      throw new Error(`Unable to decrypt string - ${errorMessage}`);\n    }\n  }\n\n  async #encryptStringV1(\n    plaintext: string,\n    password: string,\n    nativeScryptCrypto?: NativeScrypt,\n  ): Promise<string> {\n    const { key, salt } = await this.#getOrGenerateScryptKey(\n      password,\n      {\n        N: SCRYPT_N,\n        r: SCRYPT_r,\n        p: SCRYPT_p,\n        dkLen: ALGORITHM_KEY_SIZE,\n      },\n      undefined,\n      nativeScryptCrypto,\n    );\n\n    // Encrypt and prepend salt.\n    const plaintextRaw = utf8ToBytes(plaintext);\n    const ciphertextAndNonceAndSalt = concatBytes(\n      salt,\n      this.#encrypt(plaintextRaw, key),\n    );\n\n    // Convert to Base64\n    const encryptedData = byteArrayToBase64(ciphertextAndNonceAndSalt);\n\n    const encryptedPayload: EncryptedPayload = {\n      v: '1',\n      t: 'scrypt',\n      d: encryptedData,\n      o: {\n        N: SCRYPT_N,\n        r: SCRYPT_r,\n        p: SCRYPT_p,\n        dkLen: ALGORITHM_KEY_SIZE,\n      },\n      saltLen: SCRYPT_SALT_SIZE,\n    };\n\n    return JSON.stringify(encryptedPayload);\n  }\n\n  async #decryptStringV1(\n    data: EncryptedPayload,\n    password: string,\n    nativeScryptCrypto?: NativeScrypt,\n  ): Promise<string> {\n    const { o, d: base64CiphertextAndNonceAndSalt, saltLen } = data;\n\n    // Decode the base64.\n    const ciphertextAndNonceAndSalt = base64ToByteArray(\n      base64CiphertextAndNonceAndSalt,\n    );\n\n    // Create buffers of salt and ciphertextAndNonce.\n    const salt = ciphertextAndNonceAndSalt.slice(0, saltLen);\n    const ciphertextAndNonce = ciphertextAndNonceAndSalt.slice(\n      saltLen,\n      ciphertextAndNonceAndSalt.length,\n    );\n\n    // Derive the key.\n    const { key } = await this.#getOrGenerateScryptKey(\n      password,\n      {\n        N: o.N,\n        r: o.r,\n        p: o.p,\n        dkLen: o.dkLen,\n      },\n      salt,\n      nativeScryptCrypto,\n    );\n\n    // Decrypt and return result.\n    return bytesToUtf8(this.#decrypt(ciphertextAndNonce, key));\n  }\n\n  getSalt(encryptedDataStr: string) {\n    try {\n      const encryptedData: EncryptedPayload = JSON.parse(encryptedDataStr);\n      if (encryptedData.v === '1') {\n        if (encryptedData.t === 'scrypt') {\n          const { d: base64CiphertextAndNonceAndSalt, saltLen } = encryptedData;\n\n          // Decode the base64.\n          const ciphertextAndNonceAndSalt = base64ToByteArray(\n            base64CiphertextAndNonceAndSalt,\n          );\n\n          // Create buffers of salt and ciphertextAndNonce.\n          const salt = ciphertextAndNonceAndSalt.slice(0, saltLen);\n          return salt;\n        }\n      }\n      throw new Error(\n        `Unsupported encrypted data payload - ${encryptedDataStr}`,\n      );\n    } catch (e) {\n      const errorMessage = e instanceof Error ? e.message : JSON.stringify(e);\n      throw new Error(`Unable to get salt - ${errorMessage}`);\n    }\n  }\n\n  getIfEntriesHaveDifferentSalts(entries: string[]): boolean {\n    const salts = entries\n      .map((e) => {\n        try {\n          return this.getSalt(e);\n        } catch {\n          return undefined;\n        }\n      })\n      .filter((s): s is Uint8Array => s !== undefined);\n\n    const strSet = new Set(salts.map((arr) => arr.toString()));\n    return strSet.size === salts.length;\n  }\n\n  #encrypt(plaintext: Uint8Array, key: Uint8Array): Uint8Array {\n    const nonce = randomBytes(ALGORITHM_NONCE_SIZE);\n\n    // Encrypt and prepend nonce.\n    const ciphertext = gcm(key, nonce).encrypt(plaintext);\n\n    return concatBytes(nonce, ciphertext);\n  }\n\n  #decrypt(ciphertextAndNonce: Uint8Array, key: Uint8Array): Uint8Array {\n    // Create buffers of nonce and ciphertext.\n    const nonce = ciphertextAndNonce.slice(0, ALGORITHM_NONCE_SIZE);\n    const ciphertext = ciphertextAndNonce.slice(\n      ALGORITHM_NONCE_SIZE,\n      ciphertextAndNonce.length,\n    );\n\n    // Decrypt and return result.\n    return gcm(key, nonce).decrypt(ciphertext);\n  }\n\n  async #getOrGenerateScryptKey(\n    password: string,\n    o: EncryptedPayload['o'],\n    salt?: Uint8Array,\n    nativeScryptCrypto?: NativeScrypt,\n  ) {\n    const hashedPassword = createSHA256Hash(password);\n\n    // Check if we already have the key cached\n    const cachedKey = salt\n      ? getCachedKeyBySalt(hashedPassword, salt)\n      : getCachedKeyGeneratedWithSharedSalt(hashedPassword);\n\n    if (cachedKey) {\n      return {\n        key: cachedKey.key,\n        salt: cachedKey.salt,\n      };\n    }\n\n    // Create a unique cache key for this KDF operation\n    const newSalt = salt ?? SHARED_SALT;\n    const cacheKey = this.#createKdfCacheKey(\n      hashedPassword,\n      o,\n      newSalt,\n      nativeScryptCrypto,\n    );\n\n    // Check if there's already an ongoing KDF operation with the same parameters\n    const existingPromise = this.#kdfPromiseCache.get(cacheKey);\n    if (existingPromise) {\n      return existingPromise;\n    }\n\n    // Limit cache size to prevent unbounded growth\n    if (this.#kdfPromiseCache.size >= MAX_KDF_PROMISE_CACHE_SIZE) {\n      // Remove the oldest entry (first inserted)\n      const firstKey = this.#kdfPromiseCache.keys().next().value;\n      if (firstKey) {\n        this.#kdfPromiseCache.delete(firstKey);\n      }\n    }\n\n    // Create and cache the promise for the KDF operation\n    const kdfPromise = this.#performKdfOperation(\n      password,\n      o,\n      newSalt,\n      hashedPassword,\n      nativeScryptCrypto,\n    );\n\n    // Cache the promise and set up cleanup\n    this.#kdfPromiseCache.set(cacheKey, kdfPromise);\n\n    // Clean up the cache after completion (both success and failure)\n    // eslint-disable-next-line no-void\n    void kdfPromise.finally(() => {\n      this.#kdfPromiseCache.delete(cacheKey);\n    });\n\n    return kdfPromise;\n  }\n\n  #createKdfCacheKey(\n    hashedPassword: string,\n    o: EncryptedPayload['o'],\n    salt: Uint8Array,\n    nativeScryptCrypto?: NativeScrypt,\n  ): string {\n    const saltStr = byteArrayToBase64(salt);\n    const hasNative = Boolean(nativeScryptCrypto);\n    return `${hashedPassword}:${o.N}:${o.r}:${o.p}:${o.dkLen}:${saltStr}:${hasNative}`;\n  }\n\n  async #performKdfOperation(\n    password: string,\n    o: EncryptedPayload['o'],\n    salt: Uint8Array,\n    hashedPassword: string,\n    nativeScryptCrypto?: NativeScrypt,\n  ): Promise<{ key: Uint8Array; salt: Uint8Array }> {\n    let newKey: Uint8Array;\n\n    if (nativeScryptCrypto) {\n      newKey = await nativeScryptCrypto(\n        stringToByteArray(password),\n        salt,\n        o.N,\n        o.r,\n        o.p,\n        o.dkLen,\n      );\n    } else {\n      newKey = await scryptAsync(password, salt, {\n        N: o.N,\n        r: o.r,\n        p: o.p,\n        dkLen: o.dkLen,\n      });\n    }\n\n    setCachedKey(hashedPassword, salt, newKey);\n\n    return {\n      key: newKey,\n      salt,\n    };\n  }\n}\n\nconst encryption = new EncryptorDecryptor();\nexport default encryption;\n\n/**\n * Receive a SHA256 hash from a given string\n *\n * @param data - input\n * @returns sha256 hash\n */\nexport function createSHA256Hash(data: string): string {\n  const hashedData = sha256(data);\n  return bytesToHex(hashedData);\n}\n"]}