Skip to content

Commit

Permalink
Add Paillier HE example.
Browse files Browse the repository at this point in the history
  • Loading branch information
msinkec committed Oct 24, 2023
1 parent 1e0881c commit 544e546
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 1 deletion.
66 changes: 66 additions & 0 deletions src/contracts/paillierHE.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { SmartContract, assert, method, prop } from 'scrypt-ts'

export class PaillierHE extends SmartContract {
// max # of bits for e = ceil(log2(n))
// priv key: 2048 bits
// n: 2048*2 = 4096 bits
static readonly N = 4096

@prop()
nSquare: bigint

@prop(true)
x: bigint

constructor(nSquare: bigint, x: bigint) {
super(...arguments)
this.nSquare = nSquare
this.x = x
}

@method()
public add(toAdd: bigint) {
this.x = PaillierHE.addCT(this.x, toAdd, this.nSquare)
assert(true)
}

@method()
public mul(factor: bigint) {
this.x = PaillierHE.mulCT(this.x, factor, this.nSquare)
assert(true)
}

@method()
static addCT(ct0: bigint, ct1: bigint, nSquare: bigint): bigint {
return (ct0 * ct1) % nSquare
}

@method()
static mulCT(ct0: bigint, k: bigint, nSquare: bigint): bigint {
return PaillierHE.modExp(ct0, k, nSquare)
}

// x^y % m
@method()
static modExp(x: bigint, y: bigint, m: bigint): bigint {
let res: bigint = 1n
x = x % m

if (x != 0n) {
for (let i = 0; i < PaillierHE.N; i++) {
if (y >= 0n) {
// If y is odd, multiply x with result
if (y % 2n) res = (res * x) % m

// y >> 1
y = y / 2n
x = (x * x) % m
}
}
} else {
res = 0n
}

return res
}
}
2 changes: 1 addition & 1 deletion tests/elGamalHE.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ function decrypt(ct: CT, k: bigint): Point {
return SECP256K1.addPoints(ct.c2, kNegc1)
}

describe('Test SmartContract `PartialHE`', () => {
describe('Test SmartContract `ElGamalHE`', () => {
// For mG maps mG.x to m
let lookupTable: Map<bigint, bigint>

Expand Down
77 changes: 77 additions & 0 deletions tests/paillierHE.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { expect, use } from 'chai'
import * as paillierBigint from 'paillier-bigint'

Check failure on line 2 in tests/paillierHE.test.ts

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

Cannot find module 'paillier-bigint' or its corresponding type declarations.
import { PaillierHE } from '../src/contracts/paillierHE'

import chaiAsPromised from 'chai-as-promised'
import { getDefaultSigner } from './utils/helper'
import { MethodCallOptions } from 'scrypt-ts'
use(chaiAsPromised)

describe('Test SmartContract `PaillierHE`', () => {
before(() => {
PaillierHE.loadArtifact()
})

it('should pass the public method unit test successfully.', async () => {
// (asynchronous) creation of a random private, public key pair for the Paillier cryptosystem
const { publicKey, privateKey } =
await paillierBigint.generateRandomKeys(2048)

const x = publicKey.encrypt(0n)
const instance = new PaillierHE(publicKey._n2, x)
await instance.connect(getDefaultSigner())

await instance.deploy(1)

// Set current instance to be the deployed one.
let currentInstance = instance

for (let i = 0; i < 5; ++i) {
const nextInstance = currentInstance.next()

// Add encrypted amount (100) to the contract commulative value.
const toAdd = publicKey.encrypt(100n)
nextInstance.x = PaillierHE.addCT(
currentInstance.x,
toAdd,
publicKey._n2
)

// Call add method.
const callContractAdd = async () =>
currentInstance.methods.add(toAdd, {
next: {
instance: nextInstance,
balance: 1,
},
} as MethodCallOptions<PaillierHE>)
await expect(callContractAdd()).not.rejected

currentInstance = nextInstance

// Multiply encrypted amount.
const k = 5n
nextInstance.x = PaillierHE.mulCT(
currentInstance.x,
k,
publicKey._n2
)

// Call mul method.
const callContractMul = async () =>
currentInstance.methods.mul(k, {
next: {
instance: nextInstance,
balance: 1,
},
} as MethodCallOptions<PaillierHE>)
await expect(callContractMul()).not.rejected

currentInstance = nextInstance
}

// Decrypt and check end result.
const m = privateKey.decrypt(currentInstance.x)
await expect(m == 390500n).to.be.true
})
})

0 comments on commit 544e546

Please sign in to comment.