https://github.com/stackoverflowexcept1on/frost-secp256k1-evm
frost-secp256k1-evm verifier in solidity
https://github.com/stackoverflowexcept1on/frost-secp256k1-evm
cryptography elliptic-curves schnorr schnorr-signatures solidity threshold-cryptography
Last synced: 4 months ago
JSON representation
frost-secp256k1-evm verifier in solidity
- Host: GitHub
- URL: https://github.com/stackoverflowexcept1on/frost-secp256k1-evm
- Owner: StackOverflowExcept1on
- License: mit
- Created: 2024-10-01T13:50:39.000Z (10 months ago)
- Default Branch: master
- Last Pushed: 2024-10-24T06:23:18.000Z (9 months ago)
- Last Synced: 2024-10-25T01:13:38.261Z (9 months ago)
- Topics: cryptography, elliptic-curves, schnorr, schnorr-signatures, solidity, threshold-cryptography
- Language: Solidity
- Homepage: https://github.com/ZcashFoundation/frost/pull/749
- Size: 38.1 KB
- Stars: 2
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# frost-secp256k1-evm — cheap threshold signature scheme for EVM
[](https://github.com/StackOverflowExcept1on/frost-secp256k1-evm/actions/workflows/ci.yml)
[](https://crates.io/crates/frost-secp256k1-evm)This is Solidity library that implements [FROST signature](https://github.com/ZcashFoundation/frost) verification for
EVM applications. In addition to Solidity, there is also [Rust library](https://crates.io/crates/frost-secp256k1-evm)
for creating FROST signatures.FROST is threshold signature scheme with `n` participants and threshold `t`. Each of `n` participants has $\frac{1}{n}$
of group private key and participant's public key. The participants' public keys can be aggregated into group public
key (representing group of `n` participants with threshold `t`). The private key is generated by `n` participants
through distributed key generation (DKG). In this case, private key never appears in memory completely in any of
participants. Instead, each participant has only $\frac{1}{n}$ of group private key. Alternatively, private key can be
generated by a trusted dealer who distributes to each participant $\frac{1}{n}$ of group private key via Shamir secret
sharing scheme, but in this case dealer can know group private key and sign any messages. The trusted dealer scheme is
intended only for tests or for cases when trusted dealer himself wants to split his key into parts (e.g., one part is
stored on paper, second on flash drive, and third in safe deposit box). Participants can create FROST signature if at
least `t` (or more than `t`) of `n` participants sign message. The size of such signature will be 65 bytes in compressed
form, and it can be verified in $O(1)$ time, and verification is done by group public key, message and signature.This library can verify FROST signature `t` of `n` for $\approx 4200$ gas on any EVM network. ECDSA signatures would
require at least $3000 \cdot t$ gas, i.e. $O(t)$ time instead of $O(1)$. Threshold signatures are suitable for creating
multi-signature wallets, decentralized orgs, oracles, and many other EVM applications. Since threshold signature scheme
uses group public key, participants remain completely anonymous, and they can generate their signature in asynchronous
network using wrapper over FROST called [ROAST](https://github.com/StackOverflowExcept1on/roast). Only group public
key (64 bytes) needs to be stored onchain, signatures are created offchain in asynchronous network.Learn more about FROST signatures with [:book: ZF FROST Book](https://frost.zfnd.org/frost.html).
For usage examples, see [`test/examples/`](./test/examples).
For creating FROST signature, see [`offchain-signer/`](./offchain-signer) and [`FROSTOffchain`](#frostoffchain).
## Installation
Install with [Foundry](https://getfoundry.sh):
```bash
forge install StackOverflowExcept1on/frost-secp256k1-evm
```## Libraries
```ml
frost-secp256k1-evm
├─ FROST - "Library for verifying `FROST-secp256k1-KECCAK256` signatures"
├─ FROSTOffchain - "Library for creating `FROST-secp256k1-KECCAK256` signatures"
└─ TranspiledFROST - "Transpiled library for verifying `FROST-secp256k1-KECCAK256` signatures"
```### FROST
Library for verifying `FROST-secp256k1-KECCAK256` signatures.
It's recommended to compile this library with the `--via-ir` flag (`via_ir = true` in `foundry.toml`) to avoid
possible `"Stack too deep"` error and to reduce gas consumption.Library provides the following API:
```solidity
library FROST {
function isValidPublicKey(uint256 publicKeyX, uint256 publicKeyY) internal pure returns (bool) { ... }function verifySignature(
uint256 publicKeyX,
uint256 publicKeyY,
uint256 signatureRX,
uint256 signatureRY,
uint256 signatureZ,
bytes32 messageHash
) internal view returns (bool) { ... }
}
```#### Example usage
Here is an example `FROSTCounter` contract. This is same `Counter` contract that is usually written when learning
Solidity, but with one difference: if at least `t` (or more than `t`) of `n` participants sign
`setNumber(uint256 newNumber)`, then `setNumber` is executed. The group key of participants is also stored in contract.```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;import {FROST} from "frost-secp256k1-evm/FROST.sol";
contract FROSTCounter {
uint256 public publicKeyX;
uint256 public publicKeyY;uint256 public nonce;
uint256 public number;
constructor(uint256 _publicKeyX, uint256 _publicKeyY) {
require(FROST.isValidPublicKey(_publicKeyX, _publicKeyY));
publicKeyX = _publicKeyX;
publicKeyY = _publicKeyY;
}function setNumber(uint256 newNumber, uint256 signatureRX, uint256 signatureRY, uint256 signatureZ) public {
bytes32 messageHash =
keccak256(abi.encodePacked(block.chainid, uint256(uint160(address(this))), nonce, newNumber));
nonce++;
// NOTE: `require(FROST.isValidPublicKey(...))` is checked in constructor
require(FROST.verifySignature(publicKeyX, publicKeyY, signatureRX, signatureRY, signatureZ, messageHash));
number = newNumber;
}function increment(uint256 signatureRX, uint256 signatureRY, uint256 signatureZ) public {
uint256 newNumber = number + 1;
setNumber(newNumber, signatureRX, signatureRY, signatureZ);
}
}
```### FROSTOffchain
Library for creating `FROST-secp256k1-KECCAK256` signatures.
This is useful for testing contracts such as `FROSTCounter`. It allows generating `SigningKey`, which can then be split
into `n` participants with threshold `t` by function like [`frost_secp256k1_evm::keys::generate_with_dealer()`
](https://docs.rs/frost-secp256k1-evm/latest/frost_secp256k1_evm/keys/fn.generate_with_dealer.html). However, when
`SigningKey` is split via `frost_secp256k1_evm::keys::generate_with_dealer()`, it is removed (centralized dealer
must do this so as not to know group private key and not to affect value in `FROSTCounter` contract). In this library,
`SigningKey` is not removed, and you can use group private key to create signature that will allow to change values in
`FROSTCounter` contract.#### Example usage
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;import {Test, Vm, console} from "forge-std/Test.sol";
import {FROST} from "frost-secp256k1-evm/FROST.sol";
import {SigningKey, FROSTOffchain} from "frost-secp256k1-evm/FROSTOffchain.sol";contract MyContractTest is Test {
using FROSTOffchain for SigningKey;function test_ExampleUsage() public {
SigningKey signingKey = FROSTOffchain.newSigningKey();Vm.Wallet memory wallet = vm.createWallet(signingKey.asScalar());
uint256 publicKeyX = wallet.publicKeyX;
uint256 publicKeyY = wallet.publicKeyY;/* set `publicKeyX` and `publicKeyY` in contract such as `FROSTCounter` */
bytes32 messageHash = 0x4141414141414141414141414141414141414141414141414141414141414141;
(uint256 signatureRX, uint256 signatureRY, uint256 signatureZ) = signingKey.createSignature(messageHash);/* pass `signatureRX`, `signatureRY`, `signatureZ` to contract such as `FROSTCounter` */
assertTrue(FROST.verifySignature(publicKeyX, publicKeyY, signatureRX, signatureRY, signatureZ, messageHash));
}
}
```### TranspiledFROST
Transpiled library for verifying `FROST-secp256k1-KECCAK256` signatures.
Use `TranspiledFROST` instead of `FROST` if `--via-ir` is not available for some reason. It consists of inline
assembly (language close to Ethereum Virtual Machine).#### Example usage
```solidity
import {TranspiledFROST as FROST} from "frost-secp256k1-evm/TranspiledFROST.sol";
```## Contributing
The project uses the Foundry toolchain. You can find installation instructions [here](https://getfoundry.sh).
Setup:
```bash
git clone https://github.com/StackOverflowExcept1on/frost-secp256k1-evm
cd frost-secp256k1-evm
forge install
```## Safety
This is **experimental software** and is provided on an "as is" and "as available" basis.
There is currently no audit, but each file has comments explaining cryptography that is used to verify FROST signatures.
Known edge cases with FROST signature verification:
- If `signatureZ = 0` or `challenge = 0`, then we cannot use math trick to verify signature via `ecrecover`.
:information_source: Your application must simply re-generate the signature (it's different each time).
- If group public key has `X >= Secp256k1.N`, then math trick with `ecrecover` will not work.
:warning: Before using `FROST.verifySignature(publicKeyX, publicKeyY, ...)`, check
`FROST.isValidPublicKey(publicKeyX, publicKeyY)`.Both edge cases are very rare, but you should keep them in mind.
We **do not give any warranties** and **will not be liable for any loss** incurred through any use of this codebase.
## License
This library is licensed under the [MIT LICENSE](./LICENSE).