Source

key-generator.ts

import { Context } from './context'
import { Exception, SealError } from './exception'
import { GaloisKeys, GaloisKeysConstructorOptions } from './galois-keys'
import { PublicKey, PublicKeyConstructorOptions } from './public-key'
import { RelinKeys, RelinKeysConstructorOptions } from './relin-keys'
import { Instance, Library, LoaderOptions } from './seal'
import { SecretKey, SecretKeyConstructorOptions } from './secret-key'
import { Serializable, SerializableConstructorOptions } from './serializable'

export type KeyGeneratorDependencyOptions = {
  readonly Exception: Exception
  readonly PublicKey: PublicKeyConstructorOptions
  readonly SecretKey: SecretKeyConstructorOptions
  readonly RelinKeys: RelinKeysConstructorOptions
  readonly GaloisKeys: GaloisKeysConstructorOptions
  readonly Serializable: SerializableConstructorOptions
}

export type KeyGeneratorDependencies = {
  ({
    Exception,
    PublicKey,
    SecretKey,
    RelinKeys,
    GaloisKeys,
    Serializable
  }: KeyGeneratorDependencyOptions): KeyGeneratorConstructorOptions
}

export type KeyGeneratorConstructorOptions = {
  (context: Context, secretKey?: SecretKey): KeyGenerator
}

export type KeyGenerator = {
  readonly instance: Instance
  readonly unsafeInject: (instance: Instance) => void
  readonly delete: () => void
  readonly secretKey: () => SecretKey
  readonly createPublicKeySerializable: () => Serializable
  readonly createPublicKey: () => PublicKey
  readonly createRelinKeysSerializable: () => Serializable
  readonly createRelinKeys: () => RelinKeys
  readonly createGaloisKeysSerializable: (steps?: Int32Array) => Serializable
  readonly createGaloisKeys: (steps?: Int32Array) => GaloisKeys
}

const KeyGeneratorConstructor =
  (library: Library): KeyGeneratorDependencies =>
  ({
    Exception,
    PublicKey,
    SecretKey,
    RelinKeys,
    GaloisKeys,
    Serializable
  }: KeyGeneratorDependencyOptions): KeyGeneratorConstructorOptions =>
  (context, secretKey): KeyGenerator => {
    const Constructor = library.KeyGenerator
    let _instance = constructInstance(context, secretKey)

    function constructInstance(context: Context, secretKey?: SecretKey) {
      try {
        if (secretKey) {
          return new Constructor(context.instance, secretKey.instance)
        }
        return new Constructor(context.instance)
      } catch (e) {
        throw Exception.safe(e as SealError)
      }
    }
    /**
     * @implements KeyGenerator
     */

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

      /**
       * Inject this object with a raw WASM instance. No type checking is performed.
       *
       * @private
       * @function
       * @name KeyGenerator#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 KeyGenerator#delete
       */
      delete() {
        if (_instance) {
          _instance.delete()
          _instance = undefined
        }
      },

      /**
       * Return the generated SecretKey
       *
       * @function
       * @name KeyGenerator#secretKey
       * @returns {SecretKey} The secret key that was generated upon instantiation of this KeyGenerator
       */
      secretKey(): SecretKey {
        try {
          const key = SecretKey()
          const instance = _instance.secretKey()
          key.inject(instance)
          return key
        } catch (e) {
          throw Exception.safe(e as SealError)
        }
      },

      /**
       * Create a new PublicKey instance
       *
       * @function
       * @name KeyGenerator#createPublicKey
       * @returns {PublicKey} A new PublicKey instance
       */
      createPublicKey(): PublicKey {
        try {
          const key = PublicKey()
          _instance.createPublicKey(key.instance)
          return key
        } catch (e) {
          throw Exception.safe(e as SealError)
        }
      },

      /**
       * Create a new, Serializable PublicKey instance
       *
       * @function
       * @name KeyGenerator#createPublicKeySerializable
       * @returns {Serializable<PublicKey>} A new, serializable, PublicKey instance
       */
      createPublicKeySerializable(): Serializable {
        try {
          const serialized = Serializable()
          const instance = _instance.createPublicKeySerializable()
          serialized.unsafeInject(instance)
          return serialized
        } catch (e) {
          throw Exception.safe(e as SealError)
        }
      },

      /**
       * Generates and returns relinearization keys. This function returns
       * relinearization keys in a fully expanded form and is meant to be used
       * primarily for demo, testing, and debugging purposes.
       *
       * @function
       * @name KeyGenerator#createRelinKeys
       * @returns {RelinKeys} New RelinKeys from the KeyGenerator's internal secret key
       */
      createRelinKeys(): RelinKeys {
        try {
          const keys = RelinKeys()
          _instance.createRelinKeys(keys.instance)
          return keys
        } catch (e) {
          throw Exception.safe(e as SealError)
        }
      },

      /**
       * Generates and returns relinearization keys as a serializable object.
       *
       * Half of the key data is pseudo-randomly generated from a seed to reduce
       * the object size. The resulting serializable object cannot be used
       * directly and is meant to be serialized for the size reduction to have an
       * impact.
       *
       *
       * @function
       * @name KeyGenerator#createRelinKeysSerializable
       * @returns {Serializable<RelinKeys>} New, serializable RelinKeys from the KeyGenerator's internal secret key
       */
      createRelinKeysSerializable(): Serializable {
        try {
          const serialized = Serializable()
          const instance = _instance.createRelinKeysSerializable()
          serialized.unsafeInject(instance)
          return serialized
        } catch (e) {
          throw Exception.safe(e as SealError)
        }
      },

      /**
       * Generates and returns Galois keys. This function returns Galois keys in
       * a fully expanded form and is meant to be used primarily for demo, testing,
       * and debugging purposes. The user can optionally give an input a vector of desired
       * Galois rotation step counts, where negative step counts correspond to
       * rotations to the right and positive step counts correspond to rotations to
       * the left. A step count of zero can be used to indicate a column rotation
       * in the BFV scheme complex conjugation in the CKKS scheme.
       *
       * @function
       * @name KeyGenerator#createGaloisKeys
       * @param {Int32Array} [steps=Int32Array.from([])] Specific Galois Elements to generate
       * @returns {GaloisKeys} New GaloisKeys from the KeyGenerator's internal secret key
       */
      createGaloisKeys(steps: Int32Array = Int32Array.from([])): GaloisKeys {
        try {
          const keys = GaloisKeys()
          _instance.createGaloisKeys(steps, keys.instance)
          return keys
        } catch (e) {
          throw Exception.safe(e as SealError)
        }
      },

      /**
       * Generates and returns Galois keys as a serializable object. This function
       * creates specific Galois keys that can be used to apply specific Galois
       * automorphisms on encrypted data. The user can optionally give an input a vector
       * of desired Galois rotation step counts, where negative step counts
       * correspond to rotations to the right and positive step counts correspond
       * to rotations to the left. A step count of zero can be used to indicate
       * a column rotation in the BFV scheme complex conjugation in the CKKS scheme.
       * Half of the key data is pseudo-randomly generated from a seed to reduce
       * the object size. The resulting serializable object cannot be used
       * directly and is meant to be serialized for the size reduction to have an
       * impact.
       *
       * @function
       * @name KeyGenerator#createGaloisKeysSerializable
       * @param {Int32Array} [steps=Int32Array.from([])] Specific Galois Elements to generate
       * @returns {Serializable<GaloisKeys>} Base64 encoded string
       */
      createGaloisKeysSerializable(
        steps: Int32Array = Int32Array.from([])
      ): Serializable {
        try {
          const serialized = Serializable()
          const instance = _instance.createGaloisKeysSerializable(steps)
          serialized.unsafeInject(instance)
          return serialized
        } catch (e) {
          throw Exception.safe(e as SealError)
        }
      }
    }
  }

export const KeyGeneratorInit = ({
  loader
}: LoaderOptions): KeyGeneratorDependencies => {
  const library: Library = loader.library
  return KeyGeneratorConstructor(library)
}