Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- const paillier = require('paillier-bigint')
- const { Seal } = require('node-seal')
- const { performance } = require('perf_hooks')
- ;(async function () {
- const benchmark = create()
- await benchmark.performanceTest()
- })()
- function create() {
- let seal = null
- function randomIntInc(low, high) {
- return Math.floor(Math.random() * (high - low + 1) + low)
- }
- async function performanceTest() {
- // Run paillier-bigint
- await paillierPerformanceTest(paillier)
- // Run node-seal
- await sealPerformanceTest(seal)
- }
- async function paillierPerformanceTest(paillier) {
- let timeStart = 0
- let timeEnd = 0
- process.stdout.write('Generating private/public keys: ')
- timeStart = performance.now()
- const { privateKey, publicKey } = await paillier.generateRandomKeys(3072)
- timeEnd = performance.now()
- process.stdout.write(
- `Done [${Math.round((timeEnd - timeStart) * 1000)} microseconds]\r\n`
- )
- let timeEncryptSum = 0
- let timeDecryptSum = 0
- let timeAddSum = 0
- let timeMultiplyPlainSum = 0
- /*
- How many times to run the test?
- */
- const count = 50
- /*
- This number represents is the range ceiling for the values we will encrypt
- */
- const plainNumber = 786433
- process.stdout.write('Running tests ')
- for (let i = 0; i < count; i++) {
- const randomNumber = BigInt(
- Math.floor(randomIntInc(0, plainNumber) % plainNumber)
- )
- /*
- [Encryption]
- */
- timeStart = performance.now()
- const cipher = publicKey.encrypt(randomNumber)
- timeEnd = performance.now()
- timeEncryptSum += timeEnd - timeStart
- /*
- [Decryption]
- */
- timeStart = performance.now()
- const plain = privateKey.decrypt(cipher)
- timeEnd = performance.now()
- timeDecryptSum += timeEnd - timeStart
- if (plain !== randomNumber) {
- throw new Error('Decryption failed!')
- }
- /*
- [Add]
- */
- timeStart = performance.now()
- const sum = publicKey.addition(cipher, cipher)
- timeEnd = performance.now()
- timeAddSum += timeEnd - timeStart
- if (privateKey.decrypt(sum) !== randomNumber * 2n) {
- throw new Error('Addition failed!')
- }
- /*
- [Multiply Plain]
- */
- timeStart = performance.now()
- const product = publicKey.multiply(cipher, randomNumber)
- timeEnd = performance.now()
- timeMultiplyPlainSum += timeEnd - timeStart
- if (privateKey.decrypt(product) !== randomNumber * randomNumber) {
- throw new Error('Addition failed!')
- }
- process.stdout.write('.')
- }
- process.stdout.write(' Done\r\n\r\n')
- const avgEncrypt = Math.round((timeEncryptSum * 1000) / count)
- const avgDecrypt = Math.round((timeDecryptSum * 1000) / count)
- const avgAdd = Math.round((timeAddSum * 1000) / count)
- const avgMultiplyPlain = Math.round((timeMultiplyPlainSum * 1000) / count)
- console.log(`Average encrypt: ${avgEncrypt} microseconds`)
- console.log(`Average decrypt: ${avgDecrypt} microseconds`)
- console.log(`Average add: ${avgAdd} microseconds`)
- console.log(`Average multiply plain: ${avgMultiplyPlain} microseconds`)
- console.log('')
- }
- async function sealPerformanceTest() {
- const seal = await Seal()
- const parms = seal.EncryptionParameters(seal.SchemeType.BFV)
- let polyModulusDegree = 4096
- let smallModulus = seal.SmallModulus('786433')
- let coeffModulus = seal.CoeffModulus.BFVDefault(polyModulusDegree)
- parms.setPolyModulusDegree(polyModulusDegree)
- parms.setCoeffModulus(coeffModulus)
- parms.setPlainModulus(smallModulus)
- let context = seal.Context(parms)
- let timeStart = 0
- let timeEnd = 0
- console.log(context.toHuman())
- const plainModulus = parms.plainModulus
- process.stdout.write('Generating secret/public keys: ')
- timeStart = performance.now()
- const keyGenerator = seal.KeyGenerator(context)
- timeEnd = performance.now()
- process.stdout.write(
- `Done [${Math.round((timeEnd - timeStart) * 1000)} microseconds]\r\n`
- )
- const secretKey = keyGenerator.getSecretKey()
- const publicKey = keyGenerator.getPublicKey()
- const encryptor = seal.Encryptor(context, publicKey)
- const decryptor = seal.Decryptor(context, secretKey)
- const evaluator = seal.Evaluator(context)
- const batchEncoder = seal.BatchEncoder(context)
- /*
- These will hold the total times used by each operation.
- */
- let timeBatchSum = 0
- let timeUnbatchSum = 0
- let timeEncryptSum = 0
- let timeDecryptSum = 0
- let timeAddSum = 0
- let timeMultiplySum = 0
- let timeMultiplyPlainSum = 0
- /*
- How many times to run the test?
- */
- const count = 50
- /*
- Populate a vector of values to batch.
- */
- const slotCount = batchEncoder.slotCount
- const array = new Uint32Array(slotCount)
- const plainNumber = Number(plainModulus.value)
- for (let i = 0; i < slotCount; i++) {
- array[i] = Math.floor(randomIntInc(0, plainNumber) % plainNumber)
- }
- process.stdout.write('Running tests ')
- for (let i = 0; i < count; i++) {
- /*
- [Batching]
- There is nothing unusual here. We batch our random plaintext matrix
- into the polynomial. Note how the plaintext we create is of the exactly
- right size so unnecessary reallocations are avoided.
- */
- const plain = seal.PlainText({
- capacity: parms.polyModulusDegree,
- coeffCount: 0
- })
- plain.reserve(polyModulusDegree)
- timeStart = performance.now()
- batchEncoder.encode(array, plain)
- timeEnd = performance.now()
- timeBatchSum += timeEnd - timeStart
- /*
- [Unbatching]
- We unbatch what we just batched.
- */
- timeStart = performance.now()
- const unbatched = batchEncoder.decode(plain, false)
- timeEnd = performance.now()
- timeUnbatchSum += timeEnd - timeStart
- if (JSON.stringify(unbatched) !== JSON.stringify(array)) {
- throw new Error('Batch/unbatch failed. Something is wrong.')
- }
- /*
- [Encryption]
- We make sure our ciphertext is already allocated and large enough
- to hold the encryption with these encryption parameters. We encrypt
- our random batched matrix here.
- */
- const encrypted = seal.CipherText({ context })
- timeStart = performance.now()
- encryptor.encrypt(plain, encrypted)
- timeEnd = performance.now()
- timeEncryptSum += timeEnd - timeStart
- /*
- [Decryption]
- We decrypt what we just encrypted.
- */
- const plain2 = seal.PlainText({
- capacity: parms.polyModulusDegree,
- coeffCount: 0
- })
- plain2.reserve(polyModulusDegree)
- timeStart = performance.now()
- decryptor.decrypt(encrypted, plain2)
- timeEnd = performance.now()
- timeDecryptSum += timeEnd - timeStart
- if (plain2.toPolynomial() !== plain.toPolynomial()) {
- throw new Error('Encrypt/decrypt failed. Something is wrong.')
- }
- /*
- [Add]
- We create two ciphertexts and perform a few additions with them.
- */
- const encrypted1 = seal.CipherText({ context })
- const encrypted2 = seal.CipherText({ context })
- const plain3 = batchEncoder.encode(Int32Array.from([i]))
- const plain4 = batchEncoder.encode(Int32Array.from([i + 1]))
- encryptor.encrypt(plain3, encrypted1)
- encryptor.encrypt(plain4, encrypted2)
- timeStart = performance.now()
- evaluator.add(encrypted1, encrypted1, encrypted1)
- evaluator.add(encrypted2, encrypted2, encrypted2)
- evaluator.add(encrypted1, encrypted2, encrypted1)
- timeEnd = performance.now()
- timeAddSum += timeEnd - timeStart
- /*
- [Multiply]
- We multiply two ciphertexts. Since the size of the result will be 3,
- and will overwrite the first argument, we reserve first enough memory
- to avoid reallocating during multiplication.
- */
- encrypted1.reserve(context, 3)
- timeStart = performance.now()
- evaluator.multiply(encrypted1, encrypted2, encrypted1)
- timeEnd = performance.now()
- timeMultiplySum += timeEnd - timeStart
- /*
- [Multiply Plain]
- We multiply a ciphertext with a random plaintext. Recall that
- multiplyPlain does not change the size of the ciphertext so we use
- encrypted2 here.
- */
- timeStart = performance.now()
- evaluator.multiplyPlain(encrypted2, plain, encrypted2)
- timeEnd = performance.now()
- timeMultiplyPlainSum += timeEnd - timeStart
- // Cleanup
- plain.delete()
- plain2.delete()
- plain3.delete()
- plain4.delete()
- encrypted.delete()
- encrypted1.delete()
- encrypted2.delete()
- process.stdout.write('.')
- }
- process.stdout.write(' Done\r\n\r\n')
- const avgBatch = Math.round((timeBatchSum * 1000) / count)
- const avgUnbatch = Math.round((timeUnbatchSum * 1000) / count)
- const avgEncrypt = Math.round((timeEncryptSum * 1000) / count)
- const avgDecrypt = Math.round((timeDecryptSum * 1000) / count)
- const avgAdd = Math.round((timeAddSum * 1000) / (3 * count))
- const avgMultiply = Math.round((timeMultiplySum * 1000) / count)
- const avgMultiplyPlain = Math.round((timeMultiplyPlainSum * 1000) / count)
- console.log(`Average batch: ${avgBatch} microseconds`)
- console.log(`Average unbatch: ${avgUnbatch} microseconds`)
- console.log(`Average encrypt: ${avgEncrypt} microseconds`)
- console.log(`Average decrypt: ${avgDecrypt} microseconds`)
- console.log(`Average add: ${avgAdd} microseconds`)
- console.log(`Average multiply: ${avgMultiply} microseconds`)
- console.log(`Average multiply plain: ${avgMultiplyPlain} microseconds`)
- console.log('')
- // Cleanup
- parms.delete()
- plainModulus.delete()
- context.delete()
- keyGenerator.delete()
- secretKey.delete()
- publicKey.delete()
- evaluator.delete()
- batchEncoder.delete()
- encryptor.delete()
- decryptor.delete()
- }
- return {
- performanceTest
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement