Skip to content

Commit

Permalink
add support for ctAddress
Browse files Browse the repository at this point in the history
  • Loading branch information
spencer-xyz committed Sep 29, 2024
1 parent f263c5d commit 0183765
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 4 deletions.
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 = {
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)
})
})

0 comments on commit 0183765

Please sign in to comment.