Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 29 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,21 @@ Builds input text by encrypting the plaintext and signing it.

- `inputText`: An object of the form { "ciphertext": { "value": int[] }, "signature": bytes[] }

### 5. `decryptUint(ciphertext: bigint, userKey: string)`
### 5.`buildAddressInputText((plaintext: string, sender: { wallet: BaseWallet; userKey: string }, contractAddress: string, functionSelector: string)`

Builds input text by encrypting the plaintext and signing it.
**Parameters:**

- `plaintext`: The plaintext string message.
- `sender`: The sender's wallet and userKey.
- `contractAddress`: The contract address.
- `functionSelector`: The function signature.

**Returns:**

- `inputText`: An object of the form { "ciphertext": { "ct1": int, "ct2": int, "ct3": int }, "signature1": bytes, "signature2": bytes, "signature3": bytes }

### 6. `decryptUint(ciphertext: bigint, userKey: string)`

Decrypts a value stored in a contract using a user key.

Expand All @@ -213,7 +227,7 @@ Decrypts a value stored in a contract using a user key.

- `result`: The decrypted value.

### 6. `decryptString(ciphertext: Array<bigint>, userKey: string)`
### 7. `decryptString(ciphertext: Array<bigint>, userKey: string)`

Decrypts a value stored in a contract using a user key.

Expand All @@ -226,6 +240,19 @@ Decrypts a value stored in a contract using a user key.

- `result`: The decrypted string.

### 8. `decryptAddress(ciphertext: Array<bigint>, userKey: string)`

Decrypts an address stored in a contract and encrypted using a user key.

**Parameters:**

- `ciphertext`: An object of the form { "ct1": int, "ct2": int, "ct3": int } where each cell holds a portion of the address encrypted
- `userKey`: The user's AES key.

**Returns:**

- `result`: The decrypted address.

# ether_utils.ts

This TypeScript library, `ethers_utils.ts`, provides ethers functionality to interact with the COTI-v2 network. Below is
Expand Down
67 changes: 65 additions & 2 deletions src/crypto_utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import forge from 'node-forge'
import {BaseWallet, ethers, getBytes, SigningKey, solidityPackedKeccak256} from "ethers"
import {BaseWallet, ethers, getAddress, getBytes, isAddress, SigningKey, solidityPackedKeccak256} from "ethers"

const BLOCK_SIZE = 16 // AES block size in bytes
const HEX_BASE = 16
Expand Down Expand Up @@ -189,6 +189,49 @@ export function buildStringInputText(
return inputText
}

export function buildAddressInputText(
plaintext: string,
sender: { wallet: BaseWallet; userKey: string },
contractAddress: string,
functionSelector: string
) {
if (!isAddress(plaintext)) {
throw new Error("Plaintext must be a valid address.")
}

const it1 = buildInputText(
BigInt("0x" + plaintext.substring(2, 18)), // bytes 1 - 8
sender,
contractAddress,
functionSelector
)
const it2 = buildInputText(
BigInt("0x" + plaintext.substring(18, 34)), // bytes 9 - 16
sender,
contractAddress,
functionSelector
)
const it3 = buildInputText(
BigInt("0x" + plaintext.substring(34, 42)), // bytes 17 - 20
sender,
contractAddress,
functionSelector
)

const inputText = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think we should enhance it to a datatype of some ciphered address to manage the 3 parts away from the user

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean something like this?

ciphertext: {
ct1: it1.ciphertext,
ct2: it2.ciphertext,
ct3: it3.ciphertext
},
signature1: it1.signature,
signature2: it2.signature,
signature3: it3.signature
}

return inputText
}

export function decryptUint(ciphertext: bigint, userKey: string): bigint {
// Convert ciphertext to Uint8Array
let ctArray = new Uint8Array()
Expand Down Expand Up @@ -217,7 +260,7 @@ export function decryptString(ciphertext: { value: bigint[] }, userKey: string):
let encodedStr = new Uint8Array()

for (let i = 0; i < ciphertext.value.length; i++) {
const decrypted = decryptUint(BigInt(ciphertext.value[i]), userKey)
const decrypted = decryptUint(ciphertext.value[i], userKey)

encodedStr = new Uint8Array([...encodedStr, ...encodeUint(decrypted)])
}
Expand All @@ -229,6 +272,26 @@ export function decryptString(ciphertext: { value: bigint[] }, userKey: string):
.replace(/\0/g, '')
}

export function decryptAddress(ciphertext: { ct1: bigint, ct2: bigint, ct3: bigint }, userKey: string): string {
let addr = '0x'

let decrypted: bigint

decrypted = decryptUint(ciphertext.ct1, userKey)

addr += decrypted.toString(16).padStart(16, '0') // 8 bytes is 16 characters

decrypted = decryptUint(ciphertext.ct2, userKey)

addr += decrypted.toString(16).padStart(16, '0') // 8 bytes is 16 characters

decrypted = decryptUint(ciphertext.ct3, userKey)

addr += decrypted.toString(16).padStart(8, '0') // 4 bytes is 8 characters

return getAddress(addr)
}

export function generateAesKey(): string {
return forge.random.getBytesSync(BLOCK_SIZE)
}
Expand Down
65 changes: 65 additions & 0 deletions tests/crypto_utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { Wallet } from 'ethers'
import {
buildAddressInputText,
buildInputText,
buildStringInputText,
decodeUint,
decrypt,
decryptAddress,
decryptRSA,
decryptString,
decryptUint,
Expand Down Expand Up @@ -429,4 +431,67 @@ describe('crypto_utils', () => {

expect(plaintext).toEqual(PLAINTEXT)
})

test('buildAddressInputText - build input text from an arbitrary address', () => {
const PRIVATE_KEY = '0x526c9f9fe2fc41fb30fd0dbba1d4d76d774030166ef9f819b361046f5a5b4a34'
const USER_KEY = '4b0418c1543dbe70f215175bcddfac42'
const CONTRACT_ADDRESS = '0x0000000000000000000000000000000000000001'
const FUNCTION_SELECTOR = '0x11223344'
const PLAINTEXT = '0xc0ffee254729296a45a3885639AC7E10F9d54979'
const CIPHERTEXT = {
ct1: 57746566665648186615896636477407589926815064820721185638918731479700437094224n,
ct2: 57746566665648186612828207168301157696292953115220848200251829797438968713040n,
ct3: 57746566665648186614314868401766501073179498565984802815167605437801327120208n,
}
const SIGNATURE_1 = new Uint8Array([
199, 62, 88, 96, 21, 229, 59, 251, 139, 82, 97,
58, 80, 170, 180, 202, 33, 4, 191, 0, 243, 73,
80, 190, 185, 212, 218, 92, 26, 21, 3, 96, 59,
81, 66, 165, 248, 67, 213, 67, 121, 188, 208, 66,
8, 165, 133, 113, 66, 51, 103, 21, 205, 41, 101,
134, 212, 148, 214, 192, 229, 127, 101, 139, 0
])
const SIGNATURE_2 = new Uint8Array([
183, 184, 157, 140, 192, 49, 223, 89, 22, 36, 188,
247, 95, 12, 106, 27, 181, 184, 177, 225, 203, 18,
182, 141, 9, 5, 153, 106, 205, 121, 109, 217, 17,
14, 15, 82, 5, 130, 182, 140, 7, 58, 227, 159,
79, 28, 83, 57, 192, 142, 7, 70, 253, 114, 10,
219, 187, 2, 117, 114, 72, 7, 4, 235, 1
])
const SIGNATURE_3 = new Uint8Array([
168, 150, 6, 165, 30, 219, 206, 10, 188, 110, 127,
254, 247, 100, 165, 255, 205, 41, 191, 112, 101, 182,
138, 77, 180, 79, 23, 74, 93, 118, 75, 138, 82,
185, 175, 209, 115, 33, 182, 29, 60, 90, 174, 90,
96, 195, 196, 216, 89, 140, 155, 149, 136, 30, 181,
79, 217, 152, 25, 141, 175, 81, 228, 34, 0
])

const {ciphertext, signature1, signature2, signature3 } = buildAddressInputText(
PLAINTEXT,
{ wallet: new Wallet(PRIVATE_KEY), userKey: USER_KEY },
CONTRACT_ADDRESS,
FUNCTION_SELECTOR
)

expect(ciphertext).toEqual(CIPHERTEXT)
expect(signature1).toEqual(SIGNATURE_1)
expect(signature2).toEqual(SIGNATURE_2)
expect(signature3).toEqual(SIGNATURE_3)
})

test('decryptAddress - decrypt ciphertext of an arbitrary address', () => {
const USER_KEY = '4b0418c1543dbe70f215175bcddfac42'
const CIPHERTEXT = {
ct1: 57746566665648186615896636477407589926815064820721185638918731479700437094224n,
ct2: 57746566665648186612828207168301157696292953115220848200251829797438968713040n,
ct3: 57746566665648186614314868401766501073179498565984802815167605437801327120208n,
}
const PLAINTEXT = '0xc0ffee254729296a45a3885639AC7E10F9d54979'

const plaintext = decryptAddress(CIPHERTEXT, USER_KEY)

expect(plaintext).toEqual(PLAINTEXT)
})
})