UNPKG

@coti-io/coti-sdk-typescript

Version:

A library for encryption, decryption and cryptographic utilities for the COTI blockchain.

426 lines (425 loc) 20.1 kB
import { BaseWallet } from 'ethers'; import { BuildItUint256WithSignerParams, CtUint256Like, ctInt256, ctString, ctUint, ctUint256, itInt256, itString, itUint, itUint256, itUint256Signed, SerializableCtUint, SerializableCtUint256 } from './types'; /** * Low-level AES block encryption used by the COTI garbled-circuit scheme. * * Encrypts up to 16 bytes of plaintext by XORing with AES-ECB(random `r`). * Returns the 16-byte ciphertext block and the random `r` used (also 16 bytes). * * @param key - 128-bit AES key (16 bytes). * @param plaintext - Up to 16 bytes; shorter inputs are zero-padded on the left. * @returns The ciphertext block and random `r` (each 16 bytes). * @throws RangeError if `plaintext` exceeds 16 bytes or `key` is not 16 bytes. */ export declare function encrypt(key: Uint8Array, plaintext: Uint8Array): { ciphertext: Uint8Array; r: Uint8Array; }; /** * Low-level AES block decryption. Inverts {@link encrypt}. * * Decrypts one 16-byte block. When `r2` and `ciphertext2` are provided, decrypts * a second block and returns the 32-byte concatenation of both plaintext blocks. * * @param key - 128-bit AES key (16 bytes). * @param r - 16-byte random value from encryption. * @param ciphertext - 16-byte ciphertext block. * @param r2 - Optional second-block random value (must pair with `ciphertext2`). * @param ciphertext2 - Optional second-block ciphertext (must pair with `r2`). * @returns 16-byte plaintext, or 32 bytes when a second block is supplied. * @throws RangeError if any input has an invalid length or block pairs are mismatched. */ export declare function decrypt(key: Uint8Array, r: Uint8Array, ciphertext: Uint8Array, r2?: Uint8Array | null, ciphertext2?: Uint8Array | null): Uint8Array; /** * Generates a 2048-bit RSA key pair in DER encoding. * * @returns Public and private keys as `Uint8Array` DER blobs. */ export declare function generateRSAKeyPair(): { publicKey: Uint8Array; privateKey: Uint8Array; }; /** * Decrypts an RSA-OAEP (SHA-256) ciphertext with a DER-encoded private key. * * @param privateKey - RSA private key as a DER `Uint8Array`. * @param ciphertext - Hex-encoded RSA ciphertext (no `0x` prefix required by forge). * @returns Decrypted payload as a lowercase hex string. * @throws Error if the key or ciphertext format is invalid. */ export declare function decryptRSA(privateKey: Uint8Array, ciphertext: string): string; /** * Recovers a user's AES key by XORing two RSA-decrypted key shares. * * Each share is RSA-OAEP encrypted; after decryption both shares are parsed as * hex AES keys and XORed to produce the final 128-bit user key. * * @param privateKey - RSA private key used to decrypt both shares. * @param encryptedKeyShare0 - Hex RSA ciphertext of the first share. * @param encryptedKeyShare1 - Hex RSA ciphertext of the second share. * @returns Recovered AES key as a 32-character lowercase hex string. * @throws Error if RSA decryption or key parsing fails. */ export declare function recoverUserKey(privateKey: Uint8Array, encryptedKeyShare0: string, encryptedKeyShare1: string): string; /** * Signs a message hash with an Ethereum secp256k1 private key. * * @param message - 32-byte message digest as a hex string (e.g. from `solidityPackedKeccak256`). * @param privateKey - Signer's private key (hex string). * @returns 65-byte signature (`r` || `s` || `v`) as a `Uint8Array`. */ export declare function sign(message: string, privateKey: string): Uint8Array; /** * Signs a ctUint ciphertext for COTI input-text (IT) submission. * * Hashes `(signer, contract, selector, ciphertext)` with `solidityPackedKeccak256` * and signs the digest with the sender's wallet private key. * * @param sender - Wallet and user AES key (key is not used for signing). * @param contractAddress - Target contract address. * @param functionSelector - 4-byte function selector (e.g. `"0x11223344"`). * @param ct - Encrypted value ({@link ctUint} bigint) being signed. * @returns 65-byte signature as a `Uint8Array`. */ export declare function signInputText(sender: { wallet: BaseWallet; userKey: string; }, contractAddress: string, functionSelector: string, ct: bigint): Uint8Array; /** * @deprecated Use {@link prepareIT} for unsigned integer input-text values. This * legacy helper is limited to 64-bit plaintexts and will be removed in a * future major version. * * Encrypts a plaintext, signs the ciphertext, and returns an {@link itUint} * ready for smart contract submission. * * @param plaintext - Unsigned value up to 64 bits. * @param sender - Wallet and user AES key. * @param contractAddress - Target contract address. * @param functionSelector - 4-byte function selector. * @returns Signed input text with {@link ctUint} ciphertext. * @throws RangeError if `plaintext` exceeds 64 bits or is negative. */ export declare function buildInputText(plaintext: bigint, sender: { wallet: BaseWallet; userKey: string; }, contractAddress: string, functionSelector: string): itUint; /** * Builds signed input text for a UTF-8 string. * * Encodes the string in 8-byte chunks (each stored as a {@link ctUint}), * encrypts and signs each chunk independently, and returns an {@link itString}. * * @param plaintext - UTF-8 string to encrypt. * @param sender - Wallet and user AES key. * @param contractAddress - Target contract address. * @param functionSelector - 4-byte function selector. * @returns Signed string input text with one ciphertext/signature pair per chunk. * @throws RangeError if any chunk exceeds 64 bits after encoding. */ export declare function buildStringInputText(plaintext: string, sender: { wallet: BaseWallet; userKey: string; }, contractAddress: string, functionSelector: string): itString; /** * Decrypts a ctUint ciphertext using the user's AES key. * * `ctUint` is stored on-chain as a bigint packing 32 bytes (16-byte AES * ciphertext + 16-byte random `r`). This function decrypts to a plaintext * of up to 64 bits. For 128-bit input-text values use `prepareIT` instead. * * - A zero ciphertext is short-circuited to `0n` without validating the key, * since it represents uninitialized/empty on-chain storage. This allows DApps * to read empty balances before the user has configured their AES key. * Note: this means `decryptUint(0n, invalidKey)` returns `0n` without throwing. * - For non-zero ciphertexts, key validation is performed by `encodeKey` * (strips "0x", lowercases, enforces 128-bit hex). Invalid keys throw * rather than producing garbage. * * @param ciphertext - The ctUint value (32-byte wire format as a bigint). * @param userKey - The AES key (32 hex chars, optionally "0x"-prefixed). * @returns The decrypted plaintext as a bigint (up to 64 bits). * @throws Error if the key is invalid (null, wrong length, non-hex) and ciphertext is non-zero. */ export declare function decryptUint(ciphertext: ctUint, userKey: string): bigint; /** * Decrypts a canonical {@link ctUint256} ciphertext using the user's AES key. * * @param ciphertext - Object with `ciphertextHigh` and `ciphertextLow` parts. * @param userKey - AES key (32 hex chars, optionally `0x`-prefixed). * @returns Decrypted plaintext as a bigint (up to 256 bits). * @throws Error if the key is invalid. */ export declare function decryptUint256(ciphertext: ctUint256, userKey: string): bigint; /** * Decrypts a {@link ctString} ciphertext back to a UTF-8 string. * * Each chunk is decrypted via {@link decryptUint}; trailing zero padding added * during {@link buildStringInputText} is trimmed before decoding. * * @param ciphertext - String ciphertext with an array of {@link ctUint} chunks. * @param userKey - AES key (32 hex chars, optionally `0x`-prefixed). * @returns Decrypted UTF-8 string. * @throws Error if the key is invalid for any non-zero chunk. */ export declare function decryptString(ciphertext: ctString, userKey: string): string; /** * Generates 16 bytes of cryptographically random AES key material as a * node-forge binary string (each character's code point is one byte 0–255). * * @returns 16-character forge binary string (not hex). * @example * ```ts * const hexKey = Buffer.from(generateRandomAesKeyBinaryString(), "binary").toString("hex") * ``` */ export declare function generateRandomAesKeyBinaryString(): string; /** * @deprecated Use {@link generateRandomAesKeyBinaryString} instead. Despite the * name, the return value is a 16-byte binary string, not a numeric or hex value. */ export declare function generateRandomAesKeySizeNumber(): string; /** * Converts a node-forge binary string into a `Uint8Array`. * * In forge binary strings each character's Unicode code point represents one * byte (0–255). This is **not** UTF-8 encoding of human-readable text. * * @param binaryString - Forge binary string (one byte per character code point). * @returns Byte array with one entry per character. */ export declare function binaryStringToBytes(binaryString: string): Uint8Array; /** * @deprecated Use {@link binaryStringToBytes} instead. Despite the name, this does * not UTF-8-encode a string; it converts a forge binary string to bytes. */ export declare function encodeString(str: string): Uint8Array; /** * Validates and normalizes an AES key: ensures it is present, strips the "0x" * prefix, and lowercases it. COTI uses a 128-bit AES key, so only 32-character * hex strings are accepted. * * @param aesKey - The AES key, optionally prefixed with "0x". * @returns The normalized lowercase hex string. * @throws Error if the key is empty/null/undefined, contains non-hex characters, or is not 32 hex characters. */ export declare function normalizeAesKey(aesKey: string | null | undefined): string; /** * Parses and validates a user AES key hex string into a 16-byte `Uint8Array`. * * Delegates to {@link normalizeAesKey} for validation (strips `0x`, lowercases, * enforces 128-bit length) before hex decoding. * * @param userKey - AES key as a 32-character hex string (optional `0x` prefix). * @returns 128-bit key as 16 bytes. * @throws Error if the key is missing, wrong length, or contains non-hex characters. */ export declare function encodeKey(userKey: string): Uint8Array; /** * Encodes an unsigned integer as a 16-byte big-endian byte array. * * @param plaintext - Integer value (typically up to 128 bits). * @returns 16-byte big-endian representation. */ export declare function encodeUint(plaintext: bigint): Uint8Array; /** * Decodes a big-endian byte array into an unsigned bigint. * * @param plaintextBytes - Byte array (commonly 16 bytes from {@link encodeUint}). * @returns Decoded unsigned integer. * @throws SyntaxError if `plaintextBytes` is empty. */ export declare function decodeUint(plaintextBytes: Uint8Array): bigint; /** * AES-ECB encrypts a 16-byte random value (the `r` component of COTI encryption). * * @param r - Random value as a forge binary string or 16-byte `Uint8Array`. * @param key - 128-bit AES key (16 bytes). * @returns Encrypted 16-byte block. * @throws RangeError if `key` is not 16 bytes. */ export declare function encryptNumber(r: string | Uint8Array, key: Uint8Array): Uint8Array; /** * Prepares a signed input text ({@link itUint}) for an unsigned integer. * * Encrypts up to **128-bit** plaintext, signs the resulting {@link ctUint}, * and returns the ciphertext/signature pair for smart contract submission. * For 256-bit values use {@link prepareIT256} instead. * * @param plaintext - Unsigned value up to 128 bits. * @param sender - Wallet and user AES key. * @param contractAddress - Target contract address. * @param functionSelector - 4-byte function selector. * @returns Signed input text. * @throws RangeError if `plaintext` exceeds 128 bits or is negative. */ export declare function prepareIT(plaintext: bigint, sender: { wallet: BaseWallet; userKey: string; }, contractAddress: string, functionSelector: string): itUint; /** * Encrypts an unsigned value into a {@link ctUint} without building an IT signature. * * Plaintext must fit in **64 bits**. The resulting {@link ctUint} bigint packs * 32 bytes on the wire (16-byte ciphertext + 16-byte random `r`). For 128-bit * input-text values use {@link prepareIT} instead. * * @param plaintext - Unsigned value up to 64 bits. * @param userKey - AES key (32 hex chars, optionally `0x`-prefixed). * @returns Encrypted ctUint ciphertext as a bigint. * @throws RangeError if `plaintext` exceeds 64 bits or is negative. * @throws Error if the key is invalid. */ export declare function encryptUint(plaintext: bigint, userKey: string): ctUint; /** * Encrypts an unsigned value into a {@link ctUint256} without building an IT signature. * * Values up to 128 bits use compact encoding; larger values up to 256 bits use * full two-block encoding. For signed submission use {@link prepareIT256}, * {@link prepareSignedIT256}, or {@link buildItUint256WithSigner} instead. * * @param plaintext - Unsigned value up to 256 bits. * @param userKey - AES key (32 hex chars, optionally `0x`-prefixed). * @returns Encrypted ctUint256 ciphertext. * @throws RangeError if `plaintext` exceeds 256 bits or is negative. * @throws Error if the key is invalid. */ export declare function encryptUint256(plaintext: bigint, userKey: string): ctUint256; /** * Strict coercion for app/JSON payloads into canonical ctUint types. * * - `'ctUint64'`: coerces string/number/bigint to {@link ctUint}. * - `'ctUint256'`: accepts flat `{ ciphertextHigh, ciphertextLow }` or * `[high, low]` tuples only. * * For nested on-chain shapes use {@link decryptCtUint256} instead. * * @param value - Serializable payload from JSON/RPC. * @param type - Target ciphertext type discriminator. * @returns Canonical `bigint` or {@link ctUint256}. * @throws Error if the payload cannot be coerced. */ export declare function normalizeCtPayload(value: SerializableCtUint, type: 'ctUint64'): ctUint; export declare function normalizeCtPayload(value: SerializableCtUint256, type: 'ctUint256'): ctUint256; export declare function normalizeCtPayload(value: SerializableCtUint | SerializableCtUint256, type: 'ctUint64' | 'ctUint256'): ctUint | ctUint256; /** * Returns whether `value` matches a supported {@link ctUint256} wire shape. * * Accepts flat objects, two-element tuples, and nested four-limb objects as * defined by {@link CtUint256Like}. Does not validate ciphertext correctness. * * @param value - Value to inspect (e.g. from RPC or contract return data). * @returns `true` if the shape is recognized. */ export declare function isCtUint256Shape(value: unknown): value is CtUint256Like; /** * Returns whether a ctUint256 value represents zero / uninitialized storage. * * Handles scalar zero, flat `{ ciphertextHigh, ciphertextLow }`, tuple, and * nested four-limb shapes. Returns `false` for unrecognized shapes. * * @param ciphertext - Ciphertext value in any supported wire format. * @returns `true` if all ciphertext limbs are zero. */ export declare function isZeroCtUint256(ciphertext: unknown): boolean; /** * Decrypts a ctUint256 from any supported wire shape. * * Unlike {@link decryptUint256}, accepts flat, tuple, and nested on-chain/RPC * formats (see {@link isCtUint256Shape}). For strict JSON coercion use * {@link normalizeCtPayload} first. * * @param ciphertext - Flat, tuple, or nested ctUint256 payload. * @param userKey - AES key (32 hex chars, optionally `0x`-prefixed). * @returns Decrypted plaintext as a bigint (up to 256 bits). * @throws Error if the shape is invalid or the key is invalid. */ export declare function decryptCtUint256(ciphertext: unknown, userKey: string): bigint; /** * Builds a signed {@link itUint256Signed} using an external wallet signer. * * Encrypts the value, then signs an ABI-packed payload of * `(signer, contract, selector, ciphertextHigh, ciphertextLow)`. Intended for * browser wallets — **not** interchangeable with {@link prepareIT256}, which * signs raw ciphertext bytes for private-key callers. * * @param params - Value, AES key, addresses, selector, and async sign callback. * @returns Encrypted ctUint256 and hex signature string from the wallet. * @throws RangeError if `value` exceeds 256 bits or is negative. * @throws Error if the AES key is invalid. */ export declare function buildItUint256WithSigner({ value, aesKey, signerAddress, contractAddress, functionSelector, signMessage }: BuildItUint256WithSignerParams): Promise<itUint256Signed>; /** * Prepares a signed 256-bit input text ({@link itUint256}) for smart contract submission. * * Encrypts the plaintext (up to 256 bits), signs the raw 64-byte ciphertext bytes * with the sender's private key, and returns the ciphertext/signature pair. * For browser-wallet signing use {@link buildItUint256WithSigner} instead. * * @param plaintext - Unsigned value up to 256 bits. * @param sender - Wallet and user AES key. * @param contractAddress - Target contract address. * @param functionSelector - 4-byte function selector. * @returns Signed 256-bit input text. * @throws RangeError if `plaintext` exceeds 256 bits or is negative. */ export declare function prepareIT256(plaintext: bigint, sender: { wallet: BaseWallet; userKey: string; }, contractAddress: string, functionSelector: string): itUint256; /** * Encrypts a signed int256 into a {@link ctInt256} without building an IT signature. * * Negative values are encoded as 256-bit two's complement, then encrypted with * the same wire format as {@link encryptUint256}. * * @param plaintext - Signed value in `[-(2^255), 2^255 - 1]`. * @param userKey - AES key (32 hex chars, optionally `0x`-prefixed). * @returns Encrypted ctInt256 ciphertext. * @throws RangeError if `plaintext` is outside int256 range. * @throws Error if the key is invalid. */ export declare function encryptInt256(plaintext: bigint, userKey: string): ctInt256; /** * Decrypts a {@link ctInt256} ciphertext and returns the signed plaintext. * * @param ciphertext - Encrypted int256 (same wire shape as ctUint256). * @param userKey - AES key (32 hex chars, optionally `0x`-prefixed). * @returns Signed plaintext in int256 range. * @throws Error if the key is invalid. */ export declare function decryptInt256(ciphertext: ctInt256, userKey: string): bigint; /** * Prepares a signed int256 input text ({@link itInt256}) for smart contract submission. * * Encodes `plaintext` as 256-bit two's complement, then delegates to * {@link prepareIT256} for encrypt + private-key IT signature. * * @param plaintext - Signed value in `[-(2^255), 2^255 - 1]`. * @param sender - Wallet and user AES key. * @param contractAddress - Target contract address. * @param functionSelector - 4-byte function selector. * @returns Signed 256-bit input text. * @throws RangeError if `plaintext` is outside int256 range. */ export declare function prepareSignedIT256(plaintext: bigint, sender: { wallet: BaseWallet; userKey: string; }, contractAddress: string, functionSelector: string): itInt256; /** * Builds a COTI input-text (IT) signature over (signer, contract, selector, ciphertext). * * @deprecated Use `signInputText` with an ethers `Wallet` for private-key * signing. For browser signers, use `buildItUint256WithSigner` for 256-bit * values. This private-key convenience wrapper will be removed in a future * major version. * * @param signerAddress - Address of the signer; must match the address derived from privateKey. * @param contractAddress - Target contract address. * @param functionSelector - 4-byte function selector (e.g. "0x11223344"). * @param ciphertext - The encrypted value being signed. * @param privateKey - Signer's private key. * @returns The 65-byte signature as a hex string. * @throws Error if signerAddress does not match the address derived from privateKey. */ export declare function buildItSignature(signerAddress: string, contractAddress: string, functionSelector: string, ciphertext: bigint, privateKey: string): string;