s0l0ist / node-seal

Homomorphic Encryption for TypeScript or JavaScript - Microsoft SEAL
https://s0l0ist.github.io/node-seal/
MIT License
192 stars 24 forks source link

The example code with 1024 bit polymodulus seems to fail (incorrect output) #132

Closed amlwwalker closed 2 years ago

amlwwalker commented 2 years ago

Hi, Taking the example from here https://github.com/morfix-io/node-seal/blob/main/FULL-EXAMPLE.md

I have changed the code so that instead of specifying the parameters I do:

  let coeffModulus = seal.CoeffModulus.BFVDefault(polyModulusDegree)
  // Create a suitable set of CoeffModulus primes
  parms.setCoeffModulus(
      coeffModulus
  )

If I set the polyModulusDegree to 2048 I get

decodedArray Int32Array(2048) [
  2, 4, 6, 8, 10, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0,
  ... 1948 more items
]

4096 I get

decodedArray Int32Array(4096) [
  2, 4, 6, 8, 10, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0,
  ... 3996 more items
]

but adjusting it for 1024 bit and using the default function for coeff modulus seems to cause the output to fail: 1024

decodedArray Int32Array(1024) [
   105844, -318666,  353194,  308340,  -22466, -280593, -121865,
    93239,  161666,   56413, -429374,  274691,  214325,  150437,
  -262277, -435923,  175173,  180207, -250189, -183222, -302924,
   493734,  -39478,  507789, -123179,  -10467, -247445,  111902,
   446943, -463523,  490017,   45863, -455228,  -32028, -308374,
   161534, -216214,  -57172,  453304,    2499,  266728,  -17369,
   287119,   20179,  211342,  479885,  442275,   46353,   22520,
  -432398,  436056,  -26115, -103722,  264451,  256323,  308170,
   198784, -325548,  205138, -371934,   56778,  372592,  -79392,
    59866,  409560, -329666, -188138, -473686,  194220, -251112,
   199224,  227037,  117366, -311811, -249313,  -96853,  293575,
  -325482,  -27145, -389919,    1070, -414144,  486298, -375258,
  -436172, -100284,  -60636,  211476, -268699,  444525,   68176,
  -465590, -276743,   69281, -471714,  143503, -443269,  357831,
  -348976,   85036,
  ... 924 more items
]

Any ideas whats going on and how to fix it? My use case is I want the smallest output size data I can get all the while using homomorphic encryption (I need to send the data over HTTP with the smallest payload).

Full code snippet:

;(async () => {
  // Using CommonJS for RunKit
  const SEAL = require('node-seal')
  const seal = await SEAL()
  const schemeType = seal.SchemeType.bfv
  const securityLevel = seal.SecurityLevel.tc128
  const polyModulusDegree = 1024    
  const bitSize = 20
  const parms = seal.EncryptionParameters(schemeType)
    // Set the PolyModulusDegree
  parms.setPolyModulusDegree(polyModulusDegree)
  let coeffModulus = seal.CoeffModulus.BFVDefault(polyModulusDegree)
  // Create a suitable set of CoeffModulus primes
  parms.setCoeffModulus(
      coeffModulus
  )

  // Set the PlainModulus to a prime of bitSize 20.
  parms.setPlainModulus(
    seal.PlainModulus.Batching(polyModulusDegree, bitSize)
  )
  const context = seal.Context(
    parms, // Encryption Parameters
    true, // ExpandModChain
    securityLevel // Enforce a security level
  )

  // Set the PolyModulusDegree
  parms.setPolyModulusDegree(polyModulusDegree)

  if (!context.parametersSet()) {
    throw new Error(
      'Could not set the parameters in the given context. Please try different encryption parameters.'
    )
  }

  const encoder = seal.BatchEncoder(context)
  const keyGenerator = seal.KeyGenerator(context)
  const publicKey = keyGenerator.createPublicKey()
  const secretKey = keyGenerator.secretKey()
  const encryptor = seal.Encryptor(context, publicKey)
  const decryptor = seal.Decryptor(context, secretKey)
  const evaluator = seal.Evaluator(context)

  // Create data to be encrypted
  const array = Int32Array.from([1, 2, 3, 4, 5])

  // Encode the Array
  const plainText = encoder.encode(array)

  // Encrypt the PlainText
  const cipherText = encryptor.encrypt(plainText)

  // Add the CipherText to itself and store it in the destination parameter (itself)
  evaluator.add(cipherText, cipherText, cipherText) // Op (A), Op (B), Op (Dest)

  // Or create return a new cipher with the result (omitting destination parameter)
  // const cipher2x = evaluator.add(cipherText, cipherText)

  // Decrypt the CipherText
  const decryptedPlainText = decryptor.decrypt(cipherText)

  // Decode the PlainText
  const decodedArray = encoder.decode(decryptedPlainText)

  console.log('decodedArray', decodedArray)
})()
s0l0ist commented 2 years ago

Hey @amlwwalker, you'll need to lower the bitSize:

const bitSize = 16