This is Solidity library that implements FROST signature verification for EVM applications. In addition to Solidity, there is also Rust library for creating FROST signatures.
FROST is threshold signature scheme with n
participants and threshold t
. Each of n
participants has 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 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
This library can verify FROST signature t
of n
for
Learn more about FROST signatures with 📖 ZF FROST Book.
For usage examples, see test/examples/
.
For creating FROST signature, see offchain-signer/
.
Install with Foundry:
forge install StackOverflowExcept1on/frost-secp256k1-evm
frost-secp256k1-evm
├─ FROST - "Library for verifying `FROST-secp256k1-KECCAK256` signatures"
└─ TranspiledFROST - "Transpiled library for verifying `FROST-secp256k1-KECCAK256` signatures"
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:
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) { ... }
}
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.
// 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(address(this), nonce, newNumber));
// NOTE: `require(FROST.isValidPublicKey(...))` is checked in constructor
require(FROST.verifySignature(publicKeyX, publicKeyY, signatureRX, signatureRY, signatureZ, messageHash));
number = newNumber;
nonce++;
}
function increment(uint256 signatureRX, uint256 signatureRY, uint256 signatureZ) public {
uint256 newNumber = number + 1;
setNumber(newNumber, signatureRX, signatureRY, signatureZ);
}
}
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).
import {TranspiledFROST as FROST} from "frost-secp256k1-evm/TranspiledFROST.sol";
The project uses the Foundry toolchain. You can find installation instructions here.
Setup:
git clone https://github.com/StackOverflowExcept1on/frost-secp256k1-evm
cd frost-secp256k1-evm
forge install
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
orchallenge = 0
, then we cannot use math trick to verify signature viaecrecover
.ℹ️ Your application must simply re-generate the signature (it's different each time).
-
If group public key has
X >= Secp256k1.N
, then math trick withecrecover
will not work.⚠️ Before usingFROST.verifySignature(publicKeyX, publicKeyY, ...)
, checkFROST.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.
This library is licensed under the MIT LICENSE.