Skip to content

Commit

Permalink
feat: add TFHE decryption utils
Browse files Browse the repository at this point in the history
  • Loading branch information
dartdart26 committed Jun 11, 2024
1 parent c1ce5fc commit b45babb
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 15 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,14 @@ There are two ways to contribute to the Zama fhEVM:

- you can open issues to report bugs or typos, or to suggest new ideas
- you can ask to become an official contributor by emailing [email protected]. (becoming an approved contributor involves signing our Contributor License Agreement (CLA))
Only approved contributors can send pull requests, so please make sure to get in touch before you do!
Only approved contributors can send pull requests, so please make sure to get in touch before you do!

## Credits

This library uses several dependencies and we would like to thank the contributors of those libraries.

## Need support?

<a target="_blank" href="https://community.zama.ai">
<img src="https://github.com/zama-ai/fhevmjs/assets/1384478/4fc4e460-ca1d-4910-8bc2-cd1d50c7d020">
</a>
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "fhevmjs",
"version": "0.5.0-0",
"version": "0.5.0-1",
"description": "fhEVM SDK for blockchain using TFHE",
"main": "lib/node.cjs",
"types": "lib/node.d.ts",
Expand Down
1 change: 1 addition & 0 deletions src/node.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//@ts-check
export * from './sdk';
export * from './tfhe';
export { clientKeyDecryptor } from './utils';
8 changes: 4 additions & 4 deletions src/sdk/decrypt.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ describe('decrypt', () => {

it('decrypts an address Uint8Array value bigger than 160 bits', async () => {
const keypair = sodium.crypto_box_keypair();
const address = '0x9b8a8ba1f109551bd432803012645ac136ddd64dba72'
const address = '0x9b8a8ba1f109551bd432803012645ac136ddd64dba72';
// Must truncate to 40-digit
const expected = '0x8ba1f109551bd432803012645ac136ddd64dba72'
const expected = '0x8ba1f109551bd432803012645ac136ddd64dba72';
const value = BigInt(address);
const ciphertext = sodium.crypto_box_seal(
bigIntToBytes(value),
Expand All @@ -61,9 +61,9 @@ describe('decrypt', () => {

it('decrypts an address Uint8Array value lower than 160 bits', async () => {
const keypair = sodium.crypto_box_keypair();
const address = '0x8ba1f109551bd432803012645ac136ddd64d'
const address = '0x8ba1f109551bd432803012645ac136ddd64d';
// Must add padding until to 40-digit
const expected = '0x00008ba1f109551bd432803012645ac136ddd64d'
const expected = '0x00008ba1f109551bd432803012645ac136ddd64d';
const value = BigInt(address);
const ciphertext = sodium.crypto_box_seal(
bigIntToBytes(value),
Expand Down
16 changes: 8 additions & 8 deletions src/sdk/decrypt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ export const decryptAddress = (
keypair.privateKey,
);

let hexString = bytesToHex(decrypted);
// Ensure hexString forms a valid 40-digit Ethereum address.
// Truncate or pad with leading zeros as necessary to correct length issues.
if (hexString.length > 40) {
hexString = hexString.substring(hexString.length - 40);
} else {
hexString = hexString.slice(2).padStart(40, '0');
}
let hexString = bytesToHex(decrypted);
// Ensure hexString forms a valid 40-digit Ethereum address.
// Truncate or pad with leading zeros as necessary to correct length issues.
if (hexString.length > 40) {
hexString = hexString.substring(hexString.length - 40);
} else {
hexString = hexString.slice(2).padStart(40, '0');
}
return getAddress(hexString);
};
9 changes: 9 additions & 0 deletions src/sdk/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,15 @@ export const getPublicKeyCallParams = () => ({
data: '0xd9d47bb001',
});

export const getCiphertextCallParams = (handle: bigint) => {
let hex = handle.toString(16);
hex = hex.padStart(64, '0');
return {
to: '0x000000000000000000000000000000000000005d',
data: '0xff627e77' + hex,
};
};

export const createInstance = async (
params: FhevmInstanceParams,
): Promise<FhevmInstance> => {
Expand Down
97 changes: 96 additions & 1 deletion src/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,31 @@
import { bigIntToBytes, bytesToBigInt } from './utils';
import {
bigIntToBytes,
bytesToBigInt,
clientKeyDecryptor,
toHexString,
} from './utils';
import { createTfheKeypair } from './tfhe';
import {
FheBool,
FheUint4,
FheUint8,
FheUint16,
FheUint32,
FheUint64,
FheUint160,
TfheCompactPublicKey,
} from 'node-tfhe';

describe('decrypt', () => {
let clientKeySer: Uint8Array;
let compactPublicKey: TfheCompactPublicKey;

beforeAll(async () => {
const { clientKey, publicKey } = createTfheKeypair();
clientKeySer = clientKey.serialize();
compactPublicKey = publicKey;
});

it('converts a number to bytes', async () => {
const value = BigInt(28482);
const bytes = bigIntToBytes(value);
Expand All @@ -24,4 +49,74 @@ describe('decrypt', () => {
const bigint0 = bytesToBigInt(value0);
expect(bigint0.toString()).toBe('0');
});

it('decryptor bool', async () => {
const d = clientKeyDecryptor(clientKeySer);
const c = FheBool.encrypt_with_compact_public_key(
true,
compactPublicKey,
).serialize();
const v = await d.decryptBool(toHexString(c));
expect(v).toBe(true);
});

it('decryptor 4', async () => {
const d = clientKeyDecryptor(clientKeySer);
const c = FheUint4.encrypt_with_compact_public_key(
4,
compactPublicKey,
).serialize();
const v = await d.decrypt4(toHexString(c));
expect(v).toBe(4);
});

it('decryptor 8', async () => {
const d = clientKeyDecryptor(clientKeySer);
const c = FheUint8.encrypt_with_compact_public_key(
67,
compactPublicKey,
).serialize();
const v = await d.decrypt8(toHexString(c));
expect(v).toBe(67);
});

it('decryptor 16', async () => {
const d = clientKeyDecryptor(clientKeySer);
const c = FheUint16.encrypt_with_compact_public_key(
1700,
compactPublicKey,
).serialize();
const v = await d.decrypt16(toHexString(c));
expect(v).toBe(1700);
});

it('decryptor 32', async () => {
const d = clientKeyDecryptor(clientKeySer);
const c = FheUint32.encrypt_with_compact_public_key(
77662,
compactPublicKey,
).serialize();
const v = await d.decrypt32(toHexString(c));
expect(v).toBe(77662);
});

it('decryptor 64', async () => {
const d = clientKeyDecryptor(clientKeySer);
const c = FheUint64.encrypt_with_compact_public_key(
BigInt(11200),
compactPublicKey,
).serialize();
const v = await d.decrypt64(toHexString(c));
expect(v).toBe(BigInt(11200));
});

it('decryptor address', async () => {
const d = clientKeyDecryptor(clientKeySer);
const c = FheUint160.encrypt_with_compact_public_key(
BigInt('0x8ba1f109551bd432803012645ac136ddd64dba72'),
compactPublicKey,
).serialize();
const v = await d.decryptAddress(toHexString(c));
expect(v).toBe('0x8ba1f109551bd432803012645ac136ddd64dba72');
});
});
37 changes: 37 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
import { toBigIntBE, toBufferBE } from 'bigint-buffer';
import {
FheBool,
FheUint4,
FheUint8,
FheUint16,
FheUint32,
FheUint64,
FheUint160,
TfheClientKey,
} from 'node-tfhe';

export const fromHexString = (hexString: string): Uint8Array => {
const arr = hexString.replace(/^(0x)/, '').match(/.{1,2}/g);
Expand Down Expand Up @@ -39,3 +49,30 @@ export const isAddress = function (address: string) {
}
return false;
};

export const clientKeyDecryptor = (clientKeySer: Uint8Array) => {
const clientKey = TfheClientKey.deserialize(clientKeySer);
return {
decryptBool: (ciphertext: string) =>
FheBool.deserialize(fromHexString(ciphertext)).decrypt(clientKey),
decrypt4: (ciphertext: string) =>
FheUint4.deserialize(fromHexString(ciphertext)).decrypt(clientKey),
decrypt8: (ciphertext: string) =>
FheUint8.deserialize(fromHexString(ciphertext)).decrypt(clientKey),
decrypt16: (ciphertext: string) =>
FheUint16.deserialize(fromHexString(ciphertext)).decrypt(clientKey),
decrypt32: (ciphertext: string) =>
FheUint32.deserialize(fromHexString(ciphertext)).decrypt(clientKey),
decrypt64: (ciphertext: string) =>
FheUint64.deserialize(fromHexString(ciphertext)).decrypt(clientKey),
decryptAddress: (ciphertext: string) => {
let hex = FheUint160.deserialize(fromHexString(ciphertext))
.decrypt(clientKey)
.toString(16);
while (hex.length < 40) {
hex = '0' + hex;
}
return '0x' + hex;
},
};
};

0 comments on commit b45babb

Please sign in to comment.