Skip to content

Repository files navigation

VeriPQ

Solidity library for verifying post-quantum digital signatures on the EVM.

It implements ML-DSA-65 signature verification, per FIPS 204, along with two non-standard variants that keep the lattice arithmetic identical and only swap the extendable output function (XOF), trading interoperability for gas cost.

Library XOF
MLDSA65 SHAKE128/256, i.e. FIPS 204 as specified
TurboMLDSA65 TurboSHAKE128/256, i.e. Keccak-f[1600] reduced to 12 rounds, as specified in RFC 9861
Keccak256XofCtrMLDSA65 KECCAK256 EVM opcode, run in counter mode

Warning

This is experimental and unaudited. It accompanies one of my blog posts https://itzmeanjan.in/pages/verifying-ml-dsa-signatures-on-evm.html.

Prerequisites

  • You will need docker, a C++20 compiler (gcc or clang), python3 and make.
  • You have to fetch third-party dependencies, which are managed as git submodules.
git clone --recurse-submodules git@github.com:itzmeanjan/veripq.git
# or inside cloned directory
git submodule update --init --recursive lib

Testing

Ensure functional correctness and conformance of cryptographic components such as hashing and ML-DSA signature verfication using known answer test (KAT) vectors.

make test

Test vectors are generated from NIST ACVP. Regenerate them all with following command.

make gen-kat

Benchmarking

A gas probe meters the cost of verifying a signature with a 2793-byte message with a 183-byte context, for all variants of ML-DSA.

Variant verify gas
ML-DSA-65 15,432,417
TurboML-DSA-65 12,366,017
Keccak256XofCtr-ML-DSA-65 8,792,483

To pinpoint the cost of only hashing:

make profile-hashing
Variant Total Hashing Share
ML-DSA-65 15,386,915 8,674,229 56.4%
TurboML-DSA-65 12,320,452 5,602,105 45.5%
Keccak256XofCtr-ML-DSA-65 8,802,894 2,118,015 24.1%

Note

The lattice arithmetic is a constant ~6.7M gas across all three variants.

Usage

Install it as a Foundry dependency:

forge install itzmeanjan/veripq

Then verify a signature.

import {MLDSA65} from "veripq/mldsa/MLDSA65.sol";

// pk is 1952 bytes, sig is 3309 bytes
bool ok = MLDSA65.verify(pk, message, sig);
// or, domain separate with a context string of < 256 bytes
bool okInCtx = MLDSA65.verify(pk, message, sig, ctx);

TurboMLDSA65 and Keccak256XofCtrMLDSA65 expose exactly the same interface. See examples/MLDSA65Example.sol which demonstrates guarding a value update behind "Keccak256XofCtr variant of ML-DSA-65" signature verification.

About

Post-Quantum Digital Signature Verification on Ethereum Virtual Machine

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages