Source

cipher-text.ts

import { ComprModeType } from './compr-mode-type'
import { INVALID_CIPHER_CONSTRUCTOR_OPTIONS } from './constants'
import { Context } from './context'
import { Exception, SealError } from './exception'
import { MemoryPoolHandle } from './memory-pool-handle'
import { ParmsIdType, ParmsIdTypeConstructorOptions } from './parms-id-type'
import { Instance, Library, LoaderOptions } from './seal'
import { VectorConstructorOptions } from './vector'
export type CipherTextDependencyOptions = {
  readonly Exception: Exception
  readonly ComprModeType: ComprModeType
  readonly ParmsIdType: ParmsIdTypeConstructorOptions
  readonly MemoryPoolHandle: MemoryPoolHandle
  readonly Vector: VectorConstructorOptions
}

export type CipherTextDependencies = {
  ({
    Exception,
    ComprModeType,
    ParmsIdType,
    MemoryPoolHandle,
    Vector
  }: CipherTextDependencyOptions): CipherTextConstructorOptions
}

export type CipherTextConstructorOptions = {
  ({
    context,
    parmsId,
    sizeCapacity,
    pool
  }?: {
    context?: Context
    parmsId?: ParmsIdType
    sizeCapacity?: number
    pool?: MemoryPoolHandle
  }): CipherText
}

export type CipherText = {
  readonly instance: Instance
  readonly unsafeInject: (instance: Instance) => void
  readonly delete: () => void
  readonly reserve: (context: Context, capacity: number) => void
  readonly resize: (size: number) => void
  readonly release: () => void
  readonly coeffModulusSize: number
  readonly polyModulusDegree: number
  readonly size: number
  readonly sizeCapacity: number
  readonly isTransparent: boolean
  readonly isNttForm: boolean
  readonly parmsId: ParmsIdType
  readonly scale: number
  readonly setScale: (scale: number) => void
  readonly pool: MemoryPoolHandle
  readonly save: (compression?: ComprModeType) => string
  readonly saveArray: (compression?: ComprModeType) => Uint8Array
  readonly load: (context: Context, encoded: string) => void
  readonly loadArray: (context: Context, array: Uint8Array) => void
  readonly copy: (cipher: CipherText) => void
  readonly clone: () => CipherText
  readonly move: (cipher: CipherText) => void
}

const CipherTextConstructor =
  (library: Library): CipherTextDependencies =>
  ({
    Exception,
    ComprModeType,
    ParmsIdType,
    MemoryPoolHandle,
    Vector
  }: CipherTextDependencyOptions): CipherTextConstructorOptions =>
  ({
    context,
    parmsId,
    sizeCapacity,
    pool = MemoryPoolHandle.global
  } = {}): CipherText => {
    // Static methods
    const Constructor = library.Ciphertext

    let _instance = construct({
      context,
      parmsId,
      sizeCapacity,
      pool
    })

    function construct({
      context,
      parmsId,
      sizeCapacity,
      pool = MemoryPoolHandle.global
    }: {
      context?: Context
      parmsId?: ParmsIdType
      sizeCapacity?: number
      pool?: MemoryPoolHandle
    }) {
      try {
        if (!context && !parmsId && sizeCapacity === undefined) {
          return new Constructor(pool)
        } else if (context && !parmsId && sizeCapacity === undefined) {
          return new Constructor(context.instance, pool)
        } else if (context && parmsId && sizeCapacity === undefined) {
          return new Constructor(context.instance, parmsId.instance, pool)
        } else if (context && parmsId && sizeCapacity !== undefined) {
          return new Constructor(
            context.instance,
            parmsId.instance,
            sizeCapacity,
            pool
          )
        } else {
          throw new Error(INVALID_CIPHER_CONSTRUCTOR_OPTIONS)
        }
      } catch (e) {
        throw Exception.safe(e as SealError)
      }
    }
    /**
     * @implements CipherText
     */

    /**
     * @interface CipherText
     */
    return {
      /**
       * Get the underlying WASM instance
       *
       * @private
       * @readonly
       * @name CipherText#instance
       * @type {Instance}
       */
      get instance() {
        return _instance
      },

      /**
       * Inject this object with a raw WASM instance. No type checking is performed.
       *
       * @private
       * @function
       * @name CipherText#unsafeInject
       * @param {Instance} instance WASM instance
       */
      unsafeInject(instance: Instance) {
        if (_instance) {
          _instance.delete()
          _instance = undefined
        }
        _instance = instance
      },

      /**
       * Delete the underlying WASM instance.
       *
       * Should be called before dereferencing this object to prevent the
       * WASM heap from growing indefinitely.
       * @function
       * @name CipherText#delete
       */
      delete() {
        if (_instance) {
          _instance.delete()
          _instance = undefined
        }
      },

      /**
       * Allocates enough memory to accommodate the backing array of a ciphertext
       * with given capacity. In addition to the capacity, the allocation size is
       * determined by the current encryption parameters.
       *
       * @function
       * @name CipherText#reserve
       * @param {Context} context The SEAL Context
       * @param {number} capacity The capacity to reserve
       */
      reserve(context: Context, capacity: number) {
        try {
          return _instance.reserve(context.instance, capacity)
        } catch (e) {
          throw Exception.safe(e as SealError)
        }
      },

      /**
       * Resizes the CipherText to given size, reallocating if the capacity
       * of the CipherText is too small.
       *
       * This function is mainly intended for internal use and is called
       * automatically by functions such as Evaluator.multiply and
       * Evaluator.relinearize. A normal user should never have a reason
       * to manually resize a CipherText.
       *
       * @function
       * @name CipherText#resize
       * @param {number} size The new size
       */
      resize(size: number) {
        try {
          return _instance.resize(size)
        } catch (e) {
          throw Exception.safe(e as SealError)
        }
      },

      /**
       * Resets the CipherText. This function releases any memory allocated
       * by the CipherText, returning it to the memory pool. It also sets all
       * encryption parameter specific size information to zero.
       *
       * @function
       * @name CipherText#release
       */
      release() {
        _instance.release()
      },

      /**
       * The number of primes in the coefficient modulus of the
       * associated encryption parameters. This directly affects the
       * allocation size of the CipherText.
       *
       * @readonly
       * @name CipherText#coeffModulusSize
       * @type {number}
       */
      get coeffModulusSize() {
        return _instance.coeffModulusSize()
      },

      /**
       * The degree of the polynomial modulus of the associated
       * encryption parameters. This directly affects the allocation size
       * of the CipherText.
       *
       * @readonly
       * @name CipherText#polyModulusDegree
       * @type {number}
       */
      get polyModulusDegree() {
        return _instance.polyModulusDegree()
      },

      /**
       * The size of the CipherText.
       *
       * @readonly
       * @name CipherText#size
       * @type {number}
       */
      get size() {
        return _instance.size()
      },

      /**
       * The capacity of the allocation. This means the largest size
       * of the CipherText that can be stored in the current allocation with
       * the current encryption parameters.
       *
       * @readonly
       * @name CipherText#sizeCapacity
       * @type {number}
       */
      get sizeCapacity() {
        return _instance.sizeCapacity()
      },

      /**
       * Whether the current CipherText is transparent, i.e. does not require
       * a secret key to decrypt. In typical security models such transparent
       * CipherTexts would not be considered to be valid. Starting from the second
       * polynomial in the current CipherText, this function returns true if all
       * following coefficients are identically zero. Otherwise, returns false.
       *
       * @readonly
       * @name CipherText#isTransparent
       * @type {boolean}
       */
      get isTransparent() {
        return _instance.isTransparent()
      },

      /**
       * Whether the CipherText is in NTT form.
       *
       * @readonly
       * @name CipherText#isNttForm
       * @type {boolean}
       */
      get isNttForm() {
        return _instance.isNttForm()
      },

      /**
       * The reference to parmsId.
       * @see {@link EncryptionParameters} for more information about parmsId.
       *
       * @readonly
       * @name CipherText#parmsId
       * @type {ParmsIdType}
       */
      get parmsId() {
        const parms = ParmsIdType()
        parms.inject(_instance.parmsId())
        return parms
      },

      /**
       * The reference to the scale. This is only needed when using the
       * CKKS encryption scheme. The user should have little or no reason to ever
       * change the scale by hand.
       *
       * @readonly
       * @name CipherText#scale
       * @type {number}
       */
      get scale() {
        return _instance.scale()
      },

      /**
       * Sets the CipherText scale. This is only needed when using the
       * CKKS encryption scheme. The user should have little or no reason to ever
       * change the scale by hand.
       *
       * @function
       * @name CipherText#setScale
       * @param {number} scale The scale to set
       */
      setScale(scale: number) {
        _instance.setScale(scale)
      },

      /**
       * The currently used MemoryPoolHandle.
       *
       * @readonly
       * @name CipherText#pool
       * @type {MemoryPoolHandle}
       */
      get pool() {
        return _instance.pool()
      },

      /**
       * Save the CipherText to a base64 string
       *
       * @function
       * @name CipherText#save
       * @param {ComprModeType} [compression={@link ComprModeType.zstd}] The compression mode to use
       * @returns {string} Base64 encoded string
       */
      save(compression: ComprModeType = ComprModeType.zstd): string {
        return _instance.saveToString(compression)
      },

      /**
       * Save the CipherText as a binary Uint8Array
       *
       * @function
       * @name CipherText#saveArray
       * @param {ComprModeType} [compression={@link ComprModeType.zstd}] The compression mode to use
       * @returns {Uint8Array} A byte array containing the CipherText in binary form
       */
      saveArray(compression: ComprModeType = ComprModeType.zstd): Uint8Array {
        const tempVect = Vector()
        const instance = _instance.saveToArray(compression)
        tempVect.unsafeInject(instance)
        tempVect.setType('Uint8Array')
        const tempArr = tempVect.toArray() as Uint8Array
        tempVect.delete()
        return tempArr
      },

      /**
       * Load a CipherText from a base64 string
       *
       * @function
       * @name CipherText#load
       * @param {Context} context Encryption context to enforce
       * @param {string} encoded Base64 encoded string
       */
      load(context: Context, encoded: string) {
        try {
          _instance.loadFromString(context.instance, encoded)
        } catch (e) {
          throw Exception.safe(e as SealError)
        }
      },

      /**
       * Load a CipherText from an Uint8Array holding binary data
       *
       * @function
       * @name CipherText#loadArray
       * @param {Context} context Encryption context to enforce
       * @param {Uint8Array} array TypedArray containing binary data
       */
      loadArray(context: Context, array: Uint8Array) {
        try {
          _instance.loadFromArray(context.instance, array)
        } catch (e) {
          throw Exception.safe(e as SealError)
        }
      },

      /**
       * Copy an existing CipherText and overwrite this instance
       *
       * @function
       * @name CipherText#copy
       * @param {CipherText} cipher CipherText to copy
       * @example
       * const cipherTextA = seal.CipherText()
       * // ... after encoding some data ...
       * const cipherTextB = seal.CipherText()
       * cipherTextB.copy(cipherTextA)
       * // cipherTextB holds a copy of cipherTextA
       */
      copy(cipher: CipherText) {
        try {
          _instance.copy(cipher.instance)
        } catch (e) {
          throw Exception.safe(e as SealError)
        }
      },

      /**
       * Clone and return a new instance of this CipherText
       *
       * @function
       * @name CipherText#clone
       * @returns {CipherText}
       * @example
       * const cipherTextA = seal.CipherText()
       * // ... after encoding some data ...
       * const cipherTextB = cipherTextA.clone()
       * // cipherTextB holds a copy of cipherTextA
       */
      clone(): CipherText {
        try {
          const clonedInstance = _instance.clone()
          const cipher = CipherTextConstructor(library)({
            Exception,
            ComprModeType,
            ParmsIdType,
            MemoryPoolHandle,
            Vector
          })()
          cipher.unsafeInject(clonedInstance)
          return cipher
        } catch (e) {
          throw Exception.safe(e as SealError)
        }
      },

      /**
       * Move a CipherText into this one and delete the old reference
       *
       * @function
       * @name CipherText#move
       * @param {CipherText} cipher CipherText to move
       * @example
       * const cipherTextA = seal.CipherText()
       * // ... after encoding some data ...
       * const cipherTextB = seal.CipherText()
       * cipherTextB.move(cipherTextA)
       * // cipherTextB holds a the instance of cipherTextA.
       * // cipherTextA no longer holds an instance
       */
      move(cipher: CipherText) {
        try {
          _instance.move(cipher.instance)
          // TODO: find optimization
          // This method results in a copy instead of a real move.
          // Therefore, we need to delete the old instance.
          cipher.delete()
        } catch (e) {
          throw Exception.safe(e as SealError)
        }
      }
    }
  }

export const CipherTextInit = ({
  loader
}: LoaderOptions): CipherTextDependencies => {
  const library: Library = loader.library
  return CipherTextConstructor(library)
}