mirror of
https://github.com/luxfi/precompile.git
synced 2026-09-24 00:26:21 +00:00
175 lines
5.2 KiB
Go
175 lines
5.2 KiB
Go
// Copyright (C) 2025, Lux Industries, Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package mldsa
|
|
|
|
import (
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/cloudflare/circl/sign/mldsa/mldsa65"
|
|
"github.com/luxfi/geth/common"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
var addr0 = common.Address{}
|
|
|
|
// --- Edge Cases ---
|
|
|
|
func TestDeep_NilInput(t *testing.T) {
|
|
_, _, err := MLDSAVerifyPrecompile.Run(nil, addr0, ContractMLDSAVerifyAddress, nil, 100000, true)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestDeep_EmptyInput(t *testing.T) {
|
|
_, _, err := MLDSAVerifyPrecompile.Run(nil, addr0, ContractMLDSAVerifyAddress, []byte{}, 100000, true)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestDeep_SingleByte(t *testing.T) {
|
|
_, _, err := MLDSAVerifyPrecompile.Run(nil, addr0, ContractMLDSAVerifyAddress, []byte{0x00}, 100000, true)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestDeep_AllZeros(t *testing.T) {
|
|
input := make([]byte, 2+1952+2+32+3293)
|
|
gas := MLDSAVerifyPrecompile.RequiredGas(input)
|
|
ret, _, err := MLDSAVerifyPrecompile.Run(nil, addr0, ContractMLDSAVerifyAddress, input, gas+1_000_000, true)
|
|
if err == nil {
|
|
require.Equal(t, byte(0), ret[31], "all zeros must not verify")
|
|
}
|
|
}
|
|
|
|
// --- Cryptographic Correctness ---
|
|
|
|
// buildMLDSAInput builds the correct input format:
|
|
// mode(1) + pubkey(pubKeySize) + msg_len(32 as uint256) + sig(sigSize) + msg
|
|
func buildMLDSAInput(t *testing.T, mode byte, pubBytes, msg, sigBytes []byte) []byte {
|
|
t.Helper()
|
|
msgLenBytes := make([]byte, 32)
|
|
msgLenBytes[31] = byte(len(msg))
|
|
if len(msg) > 255 {
|
|
msgLenBytes[30] = byte(len(msg) >> 8)
|
|
}
|
|
|
|
input := make([]byte, 0, 1+len(pubBytes)+32+len(sigBytes)+len(msg))
|
|
input = append(input, mode)
|
|
input = append(input, pubBytes...)
|
|
input = append(input, msgLenBytes...)
|
|
input = append(input, sigBytes...)
|
|
input = append(input, msg...)
|
|
return input
|
|
}
|
|
|
|
func TestDeep_ValidSignature(t *testing.T) {
|
|
pub, priv, err := mldsa65.GenerateKey(nil)
|
|
require.NoError(t, err)
|
|
|
|
msg := []byte("test message for ML-DSA-65")
|
|
sigBytes := make([]byte, mldsa65.SignatureSize)
|
|
err = mldsa65.SignTo(priv, msg, precompileCtx, false, sigBytes)
|
|
require.NoError(t, err)
|
|
|
|
pubBytes, _ := pub.MarshalBinary()
|
|
input := buildMLDSAInput(t, ModeMLDSA65, pubBytes, msg, sigBytes)
|
|
|
|
gas := MLDSAVerifyPrecompile.RequiredGas(input)
|
|
ret, _, err := MLDSAVerifyPrecompile.Run(nil, addr0, ContractMLDSAVerifyAddress, input, gas+1_000_000, true)
|
|
require.NoError(t, err)
|
|
require.Equal(t, byte(1), ret[31], "valid ML-DSA-65 signature must verify")
|
|
}
|
|
|
|
func TestDeep_WrongMessage(t *testing.T) {
|
|
pub, priv, err := mldsa65.GenerateKey(nil)
|
|
require.NoError(t, err)
|
|
|
|
msg := []byte("original")
|
|
sigBytes := make([]byte, mldsa65.SignatureSize)
|
|
err = mldsa65.SignTo(priv, msg, precompileCtx, false, sigBytes)
|
|
require.NoError(t, err)
|
|
|
|
pubBytes, _ := pub.MarshalBinary()
|
|
input := buildMLDSAInput(t, ModeMLDSA65, pubBytes, []byte("tampered"), sigBytes)
|
|
|
|
gas := MLDSAVerifyPrecompile.RequiredGas(input)
|
|
ret, _, err := MLDSAVerifyPrecompile.Run(nil, addr0, ContractMLDSAVerifyAddress, input, gas+1_000_000, true)
|
|
require.NoError(t, err)
|
|
require.Equal(t, byte(0), ret[31])
|
|
}
|
|
|
|
func TestDeep_BitFlipSig(t *testing.T) {
|
|
pub, priv, err := mldsa65.GenerateKey(nil)
|
|
require.NoError(t, err)
|
|
|
|
msg := []byte("bit flip ML-DSA")
|
|
sigBytes := make([]byte, mldsa65.SignatureSize)
|
|
err = mldsa65.SignTo(priv, msg, precompileCtx, false, sigBytes)
|
|
require.NoError(t, err)
|
|
sigBytes[0] ^= 0x01
|
|
|
|
pubBytes, _ := pub.MarshalBinary()
|
|
input := buildMLDSAInput(t, ModeMLDSA65, pubBytes, msg, sigBytes)
|
|
|
|
gas := MLDSAVerifyPrecompile.RequiredGas(input)
|
|
ret, _, err := MLDSAVerifyPrecompile.Run(nil, addr0, ContractMLDSAVerifyAddress, input, gas+1_000_000, true)
|
|
require.NoError(t, err)
|
|
require.Equal(t, byte(0), ret[31])
|
|
}
|
|
|
|
// --- Gas Accounting ---
|
|
|
|
func TestDeep_GasInsufficient(t *testing.T) {
|
|
input := make([]byte, 100)
|
|
gas := MLDSAVerifyPrecompile.RequiredGas(input)
|
|
if gas > 0 {
|
|
_, _, err := MLDSAVerifyPrecompile.Run(nil, addr0, ContractMLDSAVerifyAddress, input, gas-1, true)
|
|
require.Error(t, err)
|
|
}
|
|
}
|
|
|
|
func TestDeep_GasZero(t *testing.T) {
|
|
input := make([]byte, 100)
|
|
_, _, err := MLDSAVerifyPrecompile.Run(nil, addr0, ContractMLDSAVerifyAddress, input, 0, true)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
// --- Concurrency ---
|
|
|
|
func TestDeep_Concurrent(t *testing.T) {
|
|
pub, priv, _ := mldsa65.GenerateKey(nil)
|
|
msg := []byte("concurrent ML-DSA")
|
|
sigBytes := make([]byte, mldsa65.SignatureSize)
|
|
_ = mldsa65.SignTo(priv, msg, precompileCtx, false, sigBytes)
|
|
pubBytes, _ := pub.MarshalBinary()
|
|
input := buildMLDSAInput(t, ModeMLDSA65, pubBytes, msg, sigBytes)
|
|
gas := MLDSAVerifyPrecompile.RequiredGas(input)
|
|
|
|
var wg sync.WaitGroup
|
|
for range 20 {
|
|
wg.Go(func() {
|
|
ret, _, err := MLDSAVerifyPrecompile.Run(nil, addr0, ContractMLDSAVerifyAddress, input, gas+1_000_000, true)
|
|
require.NoError(t, err)
|
|
require.Equal(t, byte(1), ret[31])
|
|
})
|
|
}
|
|
wg.Wait()
|
|
}
|
|
|
|
// --- Fuzz ---
|
|
|
|
func FuzzMLDSA(f *testing.F) {
|
|
f.Add([]byte{0x00, 0x00, 0x20})
|
|
f.Add([]byte{0x01})
|
|
f.Add([]byte{})
|
|
|
|
f.Fuzz(func(t *testing.T, input []byte) {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
t.Errorf("panic: %v", r)
|
|
}
|
|
}()
|
|
gas := MLDSAVerifyPrecompile.RequiredGas(input)
|
|
MLDSAVerifyPrecompile.Run(nil, addr0, ContractMLDSAVerifyAddress, input, gas+1_000_000, true)
|
|
})
|
|
}
|