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

Evaluator.add() result #43

Closed miaaaahmed97 closed 4 years ago

miaaaahmed97 commented 4 years ago

Hey. I'm trying to add two 'vectors' :

const v1 = [0, 0, 1]
const v2 = [1, 0, 0]

which in ciphertext are ciphertext1 and ciphertext1, respectively.

This is the code use to add them:

const evaluator = Morfix.Evaluator({
    context: context
  })

  const cipherTextResult = Morfix.CipherText()
  evaluator.add({
    a: cipherText1,
    b: cipherText2,
    destination: cipherTextResult
  })

  // Create a new plainText variable to store the decrypted cipherText
  const decryptedPlainText = Morfix.PlainText()
  decryptor.decrypt({
    cipherText: cipherTextResult,
    plainText: decryptedPlainText
  })

  // Create a c++ vector to store the decoded result
  const decodeVector = Morfix.Vector({array: new Int32Array(3) })

  // Decode the plaintext to the c++ vector
  encoder.decodeVectorInt32({
    plainText: decryptedPlainText,
    vector: decodeVector
  })

  console.log('decodeVector.size', decodeVector.size)

  // Convert the vector to a JS array
  const decryptedArray = decodeVector.toArray()
  console.log('decryptedArray', decryptedArray)

which outputs:

decodeVector.size 4099
decryptedArray Int32Array [
  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, 0,
  0, 0, 0, 0,
  ... 3999 more items
]

However, i require the result in the form [1, 0, 1]. How do I get that ?

s0l0ist commented 4 years ago

I'm assuming you're using the BatchEncoder. Using the morfix.io site, I was able to generate some example code:

(async () => {
    // Pick one for your environment
    // npm install node-seal
    // yarn add node-seal
    //
    // ES6 or CommonJS
    // import { Seal } from 'node-seal'
    const { Seal } = require('node-seal')

    // Wait for the web assembly to fully initialize
    const Morfix = await Seal

    ////////////////////////
    // Encryption Parameters
    ////////////////////////

    // Create a new EncryptionParameters
    const encParms = Morfix.EncryptionParameters({
      schemeType: Morfix.SchemeType.BFV
    })

    // Assign Poly Modulus Degree
    encParms.setPolyModulusDegree({
      polyModulusDegree: 4096
    })

    // Create a C++ vector from JS string of integers representing bit-lengths of primes
    const bitSizesVector = Morfix.Vector({
      array: new Int32Array([36,36,37])
    })

    // Create a suitable set of CoeffModulus primes from the vector
    const coeffModulusVector = Morfix.CoeffModulus.Create({
      polyModulusDegree: 4096,
      bitSizes: bitSizesVector,
      securityLevel: Morfix.SecurityLevel.tc128
    })

    // Assign Coefficient Modulus
    encParms.setCoeffModulus({
      coeffModulus: coeffModulusVector
    })

    // Assign a PlainModulus (only for BFV scheme type)
    encParms.setPlainModulus({
      plainModulus: Morfix.PlainModulus.Batching({
        polyModulusDegree: 4096,
        bitSize: 20,
      })
    })

    ////////////////////////
    // Context
    ////////////////////////

    // Create a new Context
    const context = Morfix.Context({
      encryptionParams: encParms,
      expandModChain: true,
      securityLevel: Morfix.SecurityLevel.tc128
    })

    // Helper to check if the Context was created successfully
    if (!context.parametersSet) {
      throw new Error('Could not set the parameters in the given context. Please try different encryption parameters.')
    }

    ////////////////////////
    // Keys
    ////////////////////////

    // Create a new KeyGenerator (use uploaded keys if applicable)
    const keyGenerator = Morfix.KeyGenerator({ 
      context
    })

    // Get the SecretKey from the keyGenerator
    const Keypair_A_sec = keyGenerator.getSecretKey()

    // Get the PublicKey from the keyGenerator
    const Keypair_A_pub = keyGenerator.getPublicKey()

    ////////////////////////
    // Variables
    ////////////////////////

    // Create the PlainText(s) 
    const Plain_A_pt = Morfix.PlainText()
    const Plain_B_pt = Morfix.PlainText()
    const Plain_Result_pt = Morfix.PlainText()

    // Create the CipherText(s) 
    const Cipher_A_ct = Morfix.CipherText()
    const Cipher_B_ct = Morfix.CipherText()
    const Cipher_Result_ct = Morfix.CipherText()

    ////////////////////////
    // Instances
    ////////////////////////

    // Create an Evaluator
    const evaluator = Morfix.Evaluator({ context })

    // Create a BatchEncoder (only BFV SchemeType)
    const batchEncoder = Morfix.BatchEncoder({ context })

    // Create an Encryptor
    const encryptor = Morfix.Encryptor({
      context,
      publicKey: Keypair_A_pub
    })

    // Create a Decryptor
    const decryptor = Morfix.Decryptor({
      context,
      secretKey: Keypair_A_sec
    })

    ////////////////////////
    // Homomorphic Functions
    ////////////////////////

    // Encode data to a PlainText
    batchEncoder.encodeVectorInt32({
      vector: Morfix.Vector({ array: new Int32Array([0,0,1]) }),
      plainText: Plain_A_pt
    })

    // Encode data to a PlainText
    batchEncoder.encodeVectorInt32({
      vector: Morfix.Vector({ array: new Int32Array([1,0,0]) }),
      plainText: Plain_B_pt
    })

    // Encrypt a PlainText
    encryptor.encrypt({
      plainText: Plain_A_pt,
      cipherText: Cipher_A_ct
    })    

    // Encrypt a PlainText
    encryptor.encrypt({
      plainText: Plain_B_pt,
      cipherText: Cipher_B_ct
    })    

    // Add CipherText B to CipherText A and store the sum in a destination CipherText
    evaluator.add({
      a: Cipher_A_ct,
      b: Cipher_B_ct,
      destination: Cipher_Result_ct
    })    

    // Decrypt a CipherText
    decryptor.decrypt({
      cipherText: Cipher_Result_ct,
      plainText: Plain_Result_pt
    })    

    // Decode data from a PlainText
    // Create a vector to store the decoded PlainText
    const destinationVector_Plain_Result_pt = Morfix.Vector({ array: new Int32Array() })

    batchEncoder.decodeVectorInt32({
      plainText: Plain_Result_pt,
      vector: destinationVector_Plain_Result_pt
    })

    console.log('size', destinationVector_Plain_Result_pt.size)
    // 4096

    console.log('decoded', destinationVector_Plain_Result_pt.toArray() )
    // yields [ 1, 0, 1, 0, 0, 0, ..., 0] 

})()

Tested on RunKit and it works. Keep in mind the batching will always yield a vector size equal to the PolyModulusDegree used for the EncryptionParameters when using BFV scheme. In your case, this appears to be 4096 elements. When creating a vector from an array with only 3 elements, this is internally extended and filled with zeros to 4096 (PolyModulusDegree) when encoding to a PlainText.

You would simply extract the first three elements from the output array.

s0l0ist commented 4 years ago

I’m closing this for now as it appears to not be a library issue.