Warning

Code in this repository is not audited and may contain serious security holes. Use at your own risk.

Safe + Corona

This repository brings Corona post-quantum threshold signatures to the Safe smart account on the Lux C-Chain EVM. Corona is a Ring-LWE (RLWE) two-round t-of-n threshold signature (eprint.iacr.org/2024/1113 (lattice / Learning-With-Errors)).

Unlike safe-frost — which ships a self-contained secp256k1 Schnorr verifier in Solidity — Corona verification is performed by the on-chain coronaThreshold precompile at 0x012206 (part of the Lux LP-4200 unified PQCrypto block). The Solidity in this repository is a thin, decomplected adapter: it frames the precompile's exact calldata, staticcalls it under a strict success-word check, and binds the verified key to a Safe owner.

safe-corona supports the same three use cases as safe-frost:

  • as a Safe signer (an EIP-1271 owner — CoronaSigner)
  • as a Safe co-signer (a transaction guard — SafeCoronaCoSigner)
  • as an ERC-4337 account (CoronaAccount)

Precompile

Field Value
Address 0x012206
Verify ABI (raw wire format) threshold:uint32(4) ‖ totalParties:uint32(4) ‖ msgHash:bytes32(32) ‖ signature
Domain-separation context lux-evm-precompile-corona-v1
Return value 32-byte word: bytes32(1) iff valid, bytes32(0) otherwise (never reverts on a cryptographic false)
Reference implementation github.com/luxfi/precompile/corona

The "message" for every Safe use is the 32-byte Safe-transaction hash (or the ERC-4337 userOpHash), which is EIP-712-bound to the Safe address, chain id and nonce — so a signature authorises exactly one Safe / chain / nonce. The precompile additionally binds the domain-separation context above, so a Corona signature cannot be replayed against any other precompile in the PQCrypto block.

Important

The precompile reads its calldata as a raw packed layout, not an ABI-encoded function call — there is no 4-byte selector. Corona.sol frames the bytes exactly as the Go Run() parses them. Sending an ABI-encoded call (with a selector) would misalign every field.

Contracts

  • contracts/Corona.sol — the verifier library. One function, verify(...), that frames a (threshold, totalParties) pair (advisory metadata; soundness comes from the embedded group key), the public key / signature and the 32-byte hash into the precompile's wire format and strict-staticcalls 0x012206. Holds no state and enforces no policy (decomplected: dispatch only).
  • contracts/SafeCoronaSigner.sol — an EIP-1271 validator that makes a single Corona key a first-class Safe owner. Commit the key once via addOwnerWithThreshold; the Safe's checkNSignatures validates this owner's slice through the v = 0 contract-signature path. M-of-N thresholds mixing ECDSA, secp256r1 and Corona owners work natively.
  • contracts/SafeCoronaCoSigner.sol — a Safe transaction guard requiring every execTransaction to additionally carry a valid Corona co-signature.
  • contracts/CoronaAccount.sol — an ERC-4337 account authorised by a committed Corona key.

Key binding

Corona signatures are a multi-kilobyte serialized lattice signature that EMBEDS its own group key (matrix A, rounded public key b̃). An owner / account therefore commits a keccak256 of the stable group-key region of the serialized signature (the trailing 64 of 80 polynomials) at construction (immutable, so the validators are stateless view functions and side-step the Safe's state-changing-validator guard). At verify time the supplied key is re-hashed against the commitment, closing the "attacker supplies an arbitrary key" attack via second-preimage resistance.

Signature payload

The contract-signature / userOp signature payload is the raw serialized Corona signature bytes (the group key is embedded in it; there is no separate public-key argument).

Testing

forge test

The tests prove verify() against a real known-answer-test (KAT) vector — 163840-byte 2-of-3 serialized threshold signature produced by a genuine Corona ceremony. The KAT was generated by lux/precompile/cmd/safekatdump and independently proven to verify against the real Go precompile by lux/precompile/cmd/safekatverify, so the bytes the Solidity frames are known to be accepted on-chain.

Because the precompile is a luxd-native Go precompile (absent from a stock Foundry EVM), the tests install a mock at 0x012206 via vm.etch whose sole job is to assert calldata-framing conformance: it returns the success word iff the incoming calldata is byte-for-byte identical to the proven KAT input. Any drift in the wire format (field order, length-prefix widths, an accidental ABI selector) changes the calldata and the test fails. The headline test is Corona.t.sol::test_CalldataFramingMatchesPrecompileABI.

The fixture lives at tests/kat.json.

Integration with the Lux ecosystem

This package is part of the Lux blockchain ecosystem.

S
Description
Safe smart-wallet variant: Corona Ring-LWE threshold signatures (verifier + EIP-1271 signer + ERC-4337 account) via the Lux coronaThreshold precompile 0x012206
Readme BSD-3-Clause
1.7 MiB
Languages
Solidity 98.2%
Python 1.8%