Skip to content

Security: inspikalu/cipherkit

Security

SECURITY.md

Security Threat Model: Encrypt FHE & Ika 2PC-MPC

This document outlines the threat models specific to building confidential, cross-chain applications on Solana using Encrypt (FHE) and Ika (2PC-MPC).

CipherKit was built specifically with the Adevar Labs security requirements in mind. By providing a strict static analysis engine (cipherkit analyze), we aim to prevent the most common architectural vulnerabilities that emerge when bridging on-chain transparency with off-chain cryptography.


1. Encrypt (FHE) Threat Model

Encrypt provides Fully Homomorphic Encryption (FHE) via its encrypt-dsl and encrypt-anchor SDKs. While the cryptography itself is robust, smart contract logic can easily mishandle the FHE state.

1.1 FHE Ciphertext Exposure (The "msg!() Leak")

The Threat: Solana's ledger is radically public. If a developer logs an FHE type (like EUint64 or EBool) using msg!() or sol_log!(), the ciphertext itself becomes permanently etched into the public ledger. Even though the value is encrypted, exposing the ciphertext can lead to side-channel or future decryption vulnerabilities if the network encryption keys are ever compromised or updated. CipherKit Defense: Rule fhe-log-leak statically detects msg! or sol_log tokens that enclose known FHE types, flagging them as critical.

1.2 Public Struct Field Exposure

The Threat: In Anchor, #[account] structs are serialized into the account data buffer. If a developer stores a raw FHE type pub value: EUint64 directly inside a public struct (instead of simply storing the Pubkey of the standalone ciphertext account), the raw ciphertext state is continuously exposed to all network observers. CipherKit Defense: Rule fhe-pub-field flags any pub field in a struct whose type resolves to an FHE struct (e.g., EBool, EUint*).

1.3 Irreversible make_public() Calls

The Threat: The Encrypt SDK exposes a .make_public() function which effectively nullifies the privacy of an EncryptedAccount by setting its authorized array to all zeroes. If called unconditionally or in the wrong context, it permanently destroys the confidentiality of the state. CipherKit Defense: Rule fhe-make-public issues a high-severity warning wherever this method is invoked, forcing the developer to manually audit the authorization context.

1.4 Time-of-Check to Time-of-Use (TOCTOU) on Decryption

The Threat: Decryption in Encrypt is a two-step CPI process:

  1. request_decryption() returns a digest of the current ciphertext at that exact moment.
  2. The executor decrypts it off-chain.
  3. read_decrypted_verified(digest) is called to ensure the plaintext corresponds to the exact state that was requested. If a developer calls request_decryption but ignores or drops the returned digest (failing to save it to their state account), a malicious actor could mutate the FHE ciphertext between the request and the read, causing the contract to ingest spoofed plaintext. CipherKit Defense: Rule fhe-missing-digest-store tracks assignments of the request_decryption() return value. If it's dropped via let _ = or implicit discarding, a high-severity alert is raised.

1.5 Unsafe .unwrap() on Decrypted Results

The Threat: Decryption requests can fail if the MPC threshold nodes reject the auth packet. Calling .unwrap() directly on the read_decrypted result can cause the Solana program to panic entirely, potentially locking the protocol state and creating a denial-of-service vector. CipherKit Defense: Rule fhe-unwrap-on-decrypt detects .unwrap or .expect chained onto decryption read calls.


2. Ika (dWallet) Threat Model

Ika brings 2PC-MPC (Two-Party Computation) to Solana, allowing programs to natively sign transactions for Ethereum, Bitcoin, etc., without a traditional bridge.

2.1 Unverified Message Approval (The Missing Signer)

The Threat: approve_message() is the CPI that commands the Ika dWallet network to sign a hash. If this is called in an instruction that does not enforce is_signer checks on the user authorizing the action, anyone could trigger a signature on behalf of the DAO or protocol. CipherKit Defense: Rule ika-missing-signer heuristically checks if is_signer() is present in the instruction handler or if an Anchor Signer<'info> type is utilized before approve_message is invoked.

2.2 Permanent dWallet Authority Transfer

The Threat: A dWallet's authority dictates who can trigger its signatures. The transfer_dwallet(new_authority) CPI permanently moves this control. If transferred to [0u8; 32] or a malicious actor, the entire cross-chain treasury is permanently locked or stolen. CipherKit Defense: Rule ika-authority-transfer flags all authority transfers, elevating to critical if the new authority is heuristically identified as a zero-address.


3. Known Limitations of the Static Analyzer

The cipherkit analyze engine uses Abstract Syntax Tree (AST) visitor patterns without running a full Rust borrow-checker or trait resolution engine.

  • Aliasing & Shadowing: If a user renames EUint64 to MyCustomNum via type MyCustomNum = EUint64; in another file, the local file scanner will not currently catch msg!("{}", MyCustomNum::new()).
  • Complex Control Flow: The TOCTOU rule (fhe-missing-digest-store) checks for immediate assignment. If the digest is passed through multiple functional layers before being stored, the scanner may issue a false positive.
  • Macro Expansion: It scans pre-expansion tokens. If another macro is generating the leak, CipherKit will not see the leak until cargo expand is run natively.

Despite these limitations, the analyzer immediately catches the most common, catastrophic beginner mistakes when migrating from transparent Web3 development to FHE/MPC contexts.

There aren't any published security advisories