mirror of
https://github.com/luxfi/crypto.git
synced 2026-07-27 01:54:50 +00:00
fix: gofmt -s across repo (CI format check)
This commit is contained in:
@@ -71,14 +71,14 @@ type SignatureConfig struct {
|
||||
PreferredType SignatureType `json:"preferredType"`
|
||||
|
||||
// Enable specific signature types
|
||||
EnableBLS bool `json:"enableBLS"`
|
||||
EnableCorona bool `json:"enableCorona"`
|
||||
EnableCGGMP21 bool `json:"enableCGGMP21"`
|
||||
EnableBLS bool `json:"enableBLS"`
|
||||
EnableCorona bool `json:"enableCorona"`
|
||||
EnableCGGMP21 bool `json:"enableCGGMP21"`
|
||||
|
||||
// Fee configuration (in nLUX - nano LUX)
|
||||
BLSFee uint64 `json:"blsFee"` // 0 = free
|
||||
CoronaFee uint64 `json:"coronaFee"` // Premium for enhanced privacy
|
||||
CGGMP21Fee uint64 `json:"cggmp21Fee"` // Premium for threshold signatures
|
||||
BLSFee uint64 `json:"blsFee"` // 0 = free
|
||||
CoronaFee uint64 `json:"coronaFee"` // Premium for enhanced privacy
|
||||
CGGMP21Fee uint64 `json:"cggmp21Fee"` // Premium for threshold signatures
|
||||
|
||||
// Performance settings
|
||||
ParallelAggregation bool `json:"parallelAggregation"`
|
||||
|
||||
@@ -188,4 +188,3 @@ func TestEnvOverride(t *testing.T) {
|
||||
t.Errorf("env override: Default() = %v; want Vanilla", got)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,10 +22,10 @@ type kat struct {
|
||||
Key string `json:"key"`
|
||||
ContextString string `json:"context_string"`
|
||||
Cases []struct {
|
||||
InputLen int `json:"input_len"`
|
||||
Hash string `json:"hash"`
|
||||
KeyedHash string `json:"keyed_hash"`
|
||||
DeriveKey string `json:"derive_key"`
|
||||
InputLen int `json:"input_len"`
|
||||
Hash string `json:"hash"`
|
||||
KeyedHash string `json:"keyed_hash"`
|
||||
DeriveKey string `json:"derive_key"`
|
||||
} `json:"cases"`
|
||||
}
|
||||
|
||||
|
||||
@@ -68,8 +68,8 @@ type rngState struct {
|
||||
}
|
||||
|
||||
var (
|
||||
rngMu sync.Mutex
|
||||
activeRNG *rngState
|
||||
rngMu sync.Mutex
|
||||
activeRNG *rngState
|
||||
)
|
||||
|
||||
// installRNG takes the cgo lock and registers `r` as the entropy
|
||||
@@ -92,12 +92,12 @@ func clearRNG() error {
|
||||
return s.err
|
||||
}
|
||||
|
||||
//export hqcGoRandombytes
|
||||
//
|
||||
// Called from randombytes_shim.c. Reads exactly `n` bytes from the
|
||||
// installed RNG into the buffer at `buf`. Returns 0 on full read,
|
||||
// non-zero on partial read or reader error (PQClean checks the
|
||||
// return value of randombytes() == 0).
|
||||
//
|
||||
//export hqcGoRandombytes
|
||||
func hqcGoRandombytes(buf *C.uint8_t, n C.size_t) C.int {
|
||||
if activeRNG == nil {
|
||||
// Misuse — no RNG installed. Fail loudly via non-zero return
|
||||
|
||||
+8
-7
@@ -34,10 +34,11 @@ import (
|
||||
// amortises the cgo crossover across slots.
|
||||
//
|
||||
// Measured on Apple M1 Max (HQC-128 encaps):
|
||||
// 1-item batch: accel = 35 us, direct = 30 us (direct wins)
|
||||
// 4-item batch: accel = 110 us, direct = 120 us (parity)
|
||||
// 16-item batch: accel = 410 us, direct = 480 us (accel ~17% win)
|
||||
// 128-item batch: accel = 1.7 ms, direct = 3.8 ms (accel ~2.2x)
|
||||
//
|
||||
// 1-item batch: accel = 35 us, direct = 30 us (direct wins)
|
||||
// 4-item batch: accel = 110 us, direct = 120 us (parity)
|
||||
// 16-item batch: accel = 410 us, direct = 480 us (accel ~17% win)
|
||||
// 128-item batch: accel = 1.7 ms, direct = 3.8 ms (accel ~2.2x)
|
||||
//
|
||||
// The exact threshold is workload-dependent. We pick 4 (the first
|
||||
// batch size where accel matches direct) so the API remains useful
|
||||
@@ -53,9 +54,9 @@ const batchSizeThreshold = 4
|
||||
// OpenMP at the C++ level. That parallelism is independent of whether
|
||||
// a "real" GPU device is present — the kernels share the same C++
|
||||
// host loop. We therefore key dispatch on:
|
||||
// 1. backend.IsVanilla() is false — i.e. the caller did NOT explicitly
|
||||
// pick the pure-Go fallback.
|
||||
// 2. The batch is large enough to amortise the cgo crossover.
|
||||
// 1. backend.IsVanilla() is false — i.e. the caller did NOT explicitly
|
||||
// pick the pure-Go fallback.
|
||||
// 2. The batch is large enough to amortise the cgo crossover.
|
||||
//
|
||||
// We do NOT consult a session handle here — accel/ops/code parallelises via
|
||||
// OpenMP at the C++ level whether or not a Metal/CUDA device exists, so the
|
||||
|
||||
+3
-3
@@ -153,10 +153,10 @@ func karatsuba(o, a, b []uint64, size int, stack []uint64) {
|
||||
ah := a[sizeL:]
|
||||
bh := b[sizeL:]
|
||||
|
||||
karatsuba(o, a, b, sizeL, rest) // o[0:2*sizeL] = a_L * b_L
|
||||
karatsuba(tmp2, ah, bh, sizeH, rest) // tmp2 = a_H * b_H
|
||||
karatsuba(o, a, b, sizeL, rest) // o[0:2*sizeL] = a_L * b_L
|
||||
karatsuba(tmp2, ah, bh, sizeH, rest) // tmp2 = a_H * b_H
|
||||
karatsubaAdd1(alh, blh, a, b, sizeL, sizeH)
|
||||
karatsuba(tmp1, alh, blh, sizeL, rest) // tmp1 = (a_L+a_H)*(b_L+b_H)
|
||||
karatsuba(tmp1, alh, blh, sizeL, rest) // tmp1 = (a_L+a_H)*(b_L+b_H)
|
||||
karatsubaAdd2(o, tmp1, tmp2, sizeL, sizeH)
|
||||
}
|
||||
|
||||
|
||||
+5
-5
@@ -18,11 +18,11 @@ import (
|
||||
// CPU PQClean reference. The two paths share the same canonical
|
||||
// kernels via accel/ops/code so the test asserts that:
|
||||
//
|
||||
// 1. The GPU batch path returns valid (ct, ss) pairs (decap-able by
|
||||
// the canonical PQClean reference).
|
||||
// 2. The byte layout matches what PQClean produces with the same
|
||||
// entropy stream — there is no "shifted" or "padded" encoding
|
||||
// difference between the two paths.
|
||||
// 1. The GPU batch path returns valid (ct, ss) pairs (decap-able by
|
||||
// the canonical PQClean reference).
|
||||
// 2. The byte layout matches what PQClean produces with the same
|
||||
// entropy stream — there is no "shifted" or "padded" encoding
|
||||
// difference between the two paths.
|
||||
//
|
||||
// We can't compare against per-item GPU vs per-item CPU directly
|
||||
// because the batch surface takes a flat seed buffer while the
|
||||
|
||||
+17
-17
@@ -21,17 +21,17 @@
|
||||
//
|
||||
// Backends:
|
||||
//
|
||||
// 1. PQClean cgo (default for non-stub builds). Build with
|
||||
// `-tags=hqc_pqclean`. Wires the vendored PQClean HQC reference
|
||||
// implementation under crypto/hqc/pqclean/ (public domain). NIST
|
||||
// KAT vectors verify byte-for-byte — see
|
||||
// crypto/hqc/backend_pqclean_test.go's TestKAT_HQC{128,192,256}.
|
||||
// 2. Cloudflare CIRCL Go (placeholder; build tag `hqc_circl`).
|
||||
// Activates once CIRCL ships HQC support.
|
||||
// 3. Stub (default). When no backend tag is set, every operation
|
||||
// returns ErrBackendNotWired so the package still compiles on
|
||||
// hosts without cgo. The wire format and parameter constants are
|
||||
// fixed by NIST IR 8528 §4.1 and remain stable across backends.
|
||||
// 1. PQClean cgo (default for non-stub builds). Build with
|
||||
// `-tags=hqc_pqclean`. Wires the vendored PQClean HQC reference
|
||||
// implementation under crypto/hqc/pqclean/ (public domain). NIST
|
||||
// KAT vectors verify byte-for-byte — see
|
||||
// crypto/hqc/backend_pqclean_test.go's TestKAT_HQC{128,192,256}.
|
||||
// 2. Cloudflare CIRCL Go (placeholder; build tag `hqc_circl`).
|
||||
// Activates once CIRCL ships HQC support.
|
||||
// 3. Stub (default). When no backend tag is set, every operation
|
||||
// returns ErrBackendNotWired so the package still compiles on
|
||||
// hosts without cgo. The wire format and parameter constants are
|
||||
// fixed by NIST IR 8528 §4.1 and remain stable across backends.
|
||||
package hqc
|
||||
|
||||
import (
|
||||
@@ -58,12 +58,12 @@ const (
|
||||
|
||||
// Params returns the fixed sizes for the parameter set.
|
||||
type Params struct {
|
||||
Mode Mode
|
||||
PublicKeySize int
|
||||
PrivateKeySize int
|
||||
CiphertextSize int
|
||||
SharedSecretSize int
|
||||
SecurityLevelBits int // NIST PQ category mapped to ≈ AES key bits
|
||||
Mode Mode
|
||||
PublicKeySize int
|
||||
PrivateKeySize int
|
||||
CiphertextSize int
|
||||
SharedSecretSize int
|
||||
SecurityLevelBits int // NIST PQ category mapped to ≈ AES key bits
|
||||
}
|
||||
|
||||
// MustParamsFor returns the canonical Params for `mode` or panics on
|
||||
|
||||
@@ -16,9 +16,9 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
once sync.Once
|
||||
sess *accel.Session
|
||||
initErr error
|
||||
once sync.Once
|
||||
sess *accel.Session
|
||||
initErr error
|
||||
available bool
|
||||
)
|
||||
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
// - vanilla: golang.org/x/crypto/sha3.NewLegacyKeccak256 (pure Go)
|
||||
// - cgo: uses libluxcrypto's optimised C implementation when CGO is on
|
||||
// - gpu: batch-routes through github.com/luxfi/accel for batches that
|
||||
// exceed the BatchThreshold cutoff
|
||||
// exceed the BatchThreshold cutoff
|
||||
//
|
||||
// The dispatcher honours backend.Default(); see github.com/luxfi/crypto/backend.
|
||||
package keccak256
|
||||
|
||||
+9
-9
@@ -26,12 +26,12 @@ var concurrentBatchThreshold = 8
|
||||
//
|
||||
// Dispatch ladder:
|
||||
//
|
||||
// 1. GPU substrate (accel.LatticeOps.MLDSAVerifyBatch) when n >= BatchThreshold
|
||||
// AND a backend plugin is loaded AND mode is in {44, 65, 87}.
|
||||
// 2. Goroutine-parallel CPU verify when n >= concurrentBatchThreshold.
|
||||
// Scales to GOMAXPROCS without GPU substrate. Useful on hosts that
|
||||
// have CPU cores but no GPU plugin (Linux CI, no-Metal Macs, etc).
|
||||
// 3. Serial CPU verify as the floor.
|
||||
// 1. GPU substrate (accel.LatticeOps.MLDSAVerifyBatch) when n >= BatchThreshold
|
||||
// AND a backend plugin is loaded AND mode is in {44, 65, 87}.
|
||||
// 2. Goroutine-parallel CPU verify when n >= concurrentBatchThreshold.
|
||||
// Scales to GOMAXPROCS without GPU substrate. Useful on hosts that
|
||||
// have CPU cores but no GPU plugin (Linux CI, no-Metal Macs, etc).
|
||||
// 3. Serial CPU verify as the floor.
|
||||
//
|
||||
// All paths are byte-equal: each path ends in cloudflare/circl's
|
||||
// FIPS 204-conformant Verify. The dispatch choice only affects throughput,
|
||||
@@ -131,9 +131,9 @@ var ErrBatchLength = errors.New("mldsa: batch input slices have inconsistent len
|
||||
//
|
||||
// Dispatch ladder mirrors BatchVerify:
|
||||
//
|
||||
// 1. GPU substrate (accel.LatticeOps.MLDSASignBatch).
|
||||
// 2. Goroutine-parallel CPU sign.
|
||||
// 3. Serial CPU sign.
|
||||
// 1. GPU substrate (accel.LatticeOps.MLDSASignBatch).
|
||||
// 2. Goroutine-parallel CPU sign.
|
||||
// 3. Serial CPU sign.
|
||||
//
|
||||
// For deterministic-mode signing (FIPS 204 § Algorithm 2 with rnd=0^256),
|
||||
// callers can pass nil for randSource. The CPU path passes deterministic=true
|
||||
|
||||
@@ -29,8 +29,6 @@ import (
|
||||
|
||||
"github.com/cloudflare/circl/sign/mldsa/mldsa44"
|
||||
"github.com/luxfi/crypto/backend"
|
||||
|
||||
|
||||
)
|
||||
|
||||
// genBatch44 produces n ML-DSA-44 keypairs + signs distinct messages with
|
||||
@@ -223,9 +221,9 @@ func TestMLDSA44_BatchSign_RoundTrip(t *testing.T) {
|
||||
// BenchmarkMLDSA44_BatchVerify measures throughput of the ML-DSA-44 batch
|
||||
// verify dispatch ladder. Run with -bench=. to compare:
|
||||
//
|
||||
// BenchmarkMLDSA44_BatchVerify/serial-1
|
||||
// BenchmarkMLDSA44_BatchVerify/concurrent-8
|
||||
// BenchmarkMLDSA44_BatchVerify/concurrent-32
|
||||
// BenchmarkMLDSA44_BatchVerify/serial-1
|
||||
// BenchmarkMLDSA44_BatchVerify/concurrent-8
|
||||
// BenchmarkMLDSA44_BatchVerify/concurrent-32
|
||||
func BenchmarkMLDSA44_BatchVerify(b *testing.B) {
|
||||
for _, size := range []int{1, 8, 32, 64} {
|
||||
b.Run(fmt.Sprintf("n=%d", size), func(b *testing.B) {
|
||||
|
||||
@@ -21,8 +21,6 @@ import (
|
||||
|
||||
"github.com/cloudflare/circl/sign/mldsa/mldsa87"
|
||||
"github.com/luxfi/crypto/backend"
|
||||
|
||||
|
||||
)
|
||||
|
||||
func genBatch87(t testing.TB, n int) (pubs []*PublicKey, msgs [][]byte, sigs [][]byte) {
|
||||
|
||||
+6
-6
@@ -50,12 +50,12 @@ func (t DispatchTier) String() string {
|
||||
// Provenance is the auditable evidence record for the ML-DSA package.
|
||||
// See slhdsa.Provenance — same shape, same purpose.
|
||||
type Provenance struct {
|
||||
Tier DispatchTier
|
||||
AccelInitialised bool
|
||||
DeviceAvailable bool
|
||||
PluginStrongSymbol bool
|
||||
BatchThresholdN int
|
||||
ConcurrentThresholdN int
|
||||
Tier DispatchTier
|
||||
AccelInitialised bool
|
||||
DeviceAvailable bool
|
||||
PluginStrongSymbol bool
|
||||
BatchThresholdN int
|
||||
ConcurrentThresholdN int
|
||||
}
|
||||
|
||||
// GetProvenance returns the current dispatch provenance.
|
||||
|
||||
@@ -81,27 +81,27 @@ func mlkem_decaps_ct_setup() C.int {
|
||||
return 0
|
||||
}
|
||||
|
||||
//export mlkem_decaps_ct_pool_size
|
||||
//
|
||||
// Returns the per-sample input width (FIPS 203 ML-KEM-768 ciphertext
|
||||
// is 1088 bytes). Named "pool_size" for harness API compatibility.
|
||||
//
|
||||
//export mlkem_decaps_ct_pool_size
|
||||
func mlkem_decaps_ct_pool_size() C.size_t {
|
||||
return C.size_t(dMlkemCTSize)
|
||||
}
|
||||
|
||||
//export mlkem_decaps_ct_input_size
|
||||
//
|
||||
// Same as pool_size — full ciphertext bytes per sample.
|
||||
//
|
||||
//export mlkem_decaps_ct_input_size
|
||||
func mlkem_decaps_ct_input_size() C.size_t {
|
||||
return C.size_t(dMlkemCTSize)
|
||||
}
|
||||
|
||||
//export mlkem_decaps_ct_copy_valid_ct
|
||||
//
|
||||
// Copies the class-A reference ciphertext into the caller-supplied C
|
||||
// buffer (must be at least mlkem_decaps_ct_input_size() bytes). Returns
|
||||
// 0 on success. Avoids passing a Go pointer back through cgo (which
|
||||
// would trip Go 1.26's cgocheck=1 unpinned-pointer panic).
|
||||
//
|
||||
//export mlkem_decaps_ct_copy_valid_ct
|
||||
func mlkem_decaps_ct_copy_valid_ct(dst *C.uint8_t) C.int {
|
||||
if len(dMlkemValidCT) == 0 || dst == nil {
|
||||
return 1
|
||||
@@ -110,8 +110,6 @@ func mlkem_decaps_ct_copy_valid_ct(dst *C.uint8_t) C.int {
|
||||
return 0
|
||||
}
|
||||
|
||||
//export mlkem_decaps_ct
|
||||
//
|
||||
// One dudect sample: decapsulate the per-sample bytes. Class A → valid
|
||||
// reference ciphertext (FO-accept branch). Class B → caller-random
|
||||
// bytes (FO-reject branch via implicit rejection). Output is discarded;
|
||||
@@ -120,6 +118,8 @@ func mlkem_decaps_ct_copy_valid_ct(dst *C.uint8_t) C.int {
|
||||
// Critical: this function MUST be branchless in its dispatch — only
|
||||
// the Decaps call may differ between samples. Any data-dependent
|
||||
// branch here would surface as a harness artifact.
|
||||
//
|
||||
//export mlkem_decaps_ct
|
||||
func mlkem_decaps_ct(data *C.uint8_t) {
|
||||
if dMlkemSK == nil {
|
||||
return
|
||||
|
||||
@@ -33,9 +33,9 @@ import (
|
||||
|
||||
const kKeygenRandSize = 64 // ML-KEM-768 keygen tape per FIPS 203
|
||||
|
||||
//export mlkem_keygen_ct_setup
|
||||
//
|
||||
// Returns 0 on success.
|
||||
//
|
||||
//export mlkem_keygen_ct_setup
|
||||
func mlkem_keygen_ct_setup() C.int {
|
||||
// Smoke test: ensure the underlying API is functional with a
|
||||
// zero tape (the class-A input).
|
||||
@@ -47,16 +47,16 @@ func mlkem_keygen_ct_setup() C.int {
|
||||
return 0
|
||||
}
|
||||
|
||||
//export mlkem_keygen_ct_input_size
|
||||
//
|
||||
// Returns the per-sample input width: 64 bytes (the keygen tape).
|
||||
//
|
||||
//export mlkem_keygen_ct_input_size
|
||||
func mlkem_keygen_ct_input_size() C.size_t {
|
||||
return C.size_t(kKeygenRandSize)
|
||||
}
|
||||
|
||||
//export mlkem_keygen_ct
|
||||
//
|
||||
// One dudect measurement sample. data points to a 64-byte tape.
|
||||
//
|
||||
//export mlkem_keygen_ct
|
||||
func mlkem_keygen_ct(data *C.uint8_t) {
|
||||
src := unsafe.Slice((*byte)(unsafe.Pointer(data)), kKeygenRandSize)
|
||||
r := bytes.NewReader(src)
|
||||
|
||||
+10
-10
@@ -111,10 +111,10 @@ func buildValidInput(randSrc *[kProofBodyLen + kPubLen]byte) []byte {
|
||||
return out
|
||||
}
|
||||
|
||||
//export p3q_verify_ct_setup
|
||||
//
|
||||
// Initialise the per-startup pool of valid inputs. Returns 0 on
|
||||
// success, non-zero on failure.
|
||||
//
|
||||
//export p3q_verify_ct_setup
|
||||
func p3q_verify_ct_setup() C.int {
|
||||
// Register a backend verifier that always accepts. This pins the
|
||||
// CT measurement on the precompile dispatch + backend invocation
|
||||
@@ -134,24 +134,24 @@ func p3q_verify_ct_setup() C.int {
|
||||
return 0
|
||||
}
|
||||
|
||||
//export p3q_verify_ct_input_size
|
||||
//
|
||||
// Returns the per-sample input size for the C harness scratch buffer.
|
||||
//
|
||||
//export p3q_verify_ct_input_size
|
||||
func p3q_verify_ct_input_size() C.size_t {
|
||||
return C.size_t(kInputLen)
|
||||
}
|
||||
|
||||
//export p3q_verify_ct_pool_size
|
||||
//
|
||||
// Returns the number of valid inputs in the per-startup pool.
|
||||
//
|
||||
//export p3q_verify_ct_pool_size
|
||||
func p3q_verify_ct_pool_size() C.size_t {
|
||||
return C.size_t(kValidPool)
|
||||
}
|
||||
|
||||
//export p3q_verify_ct_copy_pool
|
||||
//
|
||||
// Copies validPool[idx] into the caller-supplied dst buffer
|
||||
// (kInputLen bytes). Returns 0 on success, non-zero on bounds violation.
|
||||
//
|
||||
//export p3q_verify_ct_copy_pool
|
||||
func p3q_verify_ct_copy_pool(idx C.size_t, dst *C.uint8_t) C.int {
|
||||
i := int(idx)
|
||||
if i < 0 || i >= kValidPool || validPool[i] == nil {
|
||||
@@ -168,8 +168,6 @@ var (
|
||||
supplied = uint64(1 << 30) // far above required for kInputLen
|
||||
)
|
||||
|
||||
//export p3q_verify_ct
|
||||
//
|
||||
// One dudect measurement sample.
|
||||
//
|
||||
// data points to kInputLen bytes of caller-controlled wire calldata.
|
||||
@@ -180,6 +178,8 @@ var (
|
||||
// The function MUST be branchless on `data` — any data-dependent
|
||||
// branch we introduce here pollutes the measurement before Run()
|
||||
// even starts. We only copy data into a fresh slice and dispatch.
|
||||
//
|
||||
//export p3q_verify_ct
|
||||
func p3q_verify_ct(data *C.uint8_t) {
|
||||
inputSlice := unsafe.Slice((*byte)(unsafe.Pointer(data)), kInputLen)
|
||||
input := append([]byte{}, inputSlice...)
|
||||
|
||||
@@ -56,9 +56,9 @@ func NewGenerators(rng io.Reader) (*Generators, error) {
|
||||
// SHA-256 and then dispatched to NewGeneratorsFromSeed, so the actual on-curve
|
||||
// derivation is identical to the C++/Metal/CUDA/WGSL canonical:
|
||||
//
|
||||
// key = SHA-256(seed)
|
||||
// G = HashToG1(key || u64_le(0), DST=PEDERSEN_SEEDED_GEN_V1)
|
||||
// H = HashToG1(key || u64_le(1), DST=PEDERSEN_SEEDED_GEN_V1)
|
||||
// key = SHA-256(seed)
|
||||
// G = HashToG1(key || u64_le(0), DST=PEDERSEN_SEEDED_GEN_V1)
|
||||
// H = HashToG1(key || u64_le(1), DST=PEDERSEN_SEEDED_GEN_V1)
|
||||
//
|
||||
// This collapses LP-137 issue N3 (RED-FINAL §2.4): the legacy two-DST
|
||||
// PEDERSEN_G_V1 / PEDERSEN_H_V1 path is removed; everything now flows through
|
||||
|
||||
@@ -8,14 +8,15 @@
|
||||
//
|
||||
// - Schoolbook O(n^2) -- always correct, fastest below n=64
|
||||
// - NTT-based O(n log n) for the negacyclic convolution that lattice
|
||||
// cryptography (ML-KEM, ML-DSA, Corona) uses
|
||||
// cryptography (ML-KEM, ML-DSA, Corona) uses
|
||||
//
|
||||
// Q = 998244353 is the canonical FFT-friendly prime used by AtCoder, Cyclone-FFT,
|
||||
// the SPOJ POLYMUL reference, and most competitive-programming NTT references.
|
||||
// Its 2-adicity is 23, so the NTT supports lengths up to N = 2^23.
|
||||
//
|
||||
// Negacyclic convolution: c[k] = sum_{i+j=k} a[i]*b[j]
|
||||
// - sum_{i+j=k+n} a[i]*b[j]
|
||||
// - sum_{i+j=k+n} a[i]*b[j]
|
||||
//
|
||||
// over Z_Q[X]/(X^n + 1) with q the prime above. This is the exact shape
|
||||
// used by lattice schemes; cyclic convolution is recovered by padding.
|
||||
//
|
||||
|
||||
@@ -27,9 +27,9 @@ func lcg(seed uint64, n int) []uint64 {
|
||||
// that C++ and Metal layers MUST match.
|
||||
func TestKATs(t *testing.T) {
|
||||
type kat struct {
|
||||
name string
|
||||
n int
|
||||
sA, sB uint64
|
||||
name string
|
||||
n int
|
||||
sA, sB uint64
|
||||
sum, first, last uint64
|
||||
}
|
||||
kats := []kat{
|
||||
|
||||
@@ -15,11 +15,11 @@ import (
|
||||
var poseidon2KATJSON []byte
|
||||
|
||||
type kat struct {
|
||||
Width int `json:"width"`
|
||||
FullRounds int `json:"full_rounds"`
|
||||
PartialRounds int `json:"partial_rounds"`
|
||||
SBox int `json:"sbox"`
|
||||
Cases []katCase `json:"kat"`
|
||||
Width int `json:"width"`
|
||||
FullRounds int `json:"full_rounds"`
|
||||
PartialRounds int `json:"partial_rounds"`
|
||||
SBox int `json:"sbox"`
|
||||
Cases []katCase `json:"kat"`
|
||||
}
|
||||
|
||||
type katCase struct {
|
||||
|
||||
@@ -160,4 +160,3 @@ func (e *cpuEngine) verifyBatch(mode Mode, msgs, sigs, pks [][]byte) ([]bool, er
|
||||
})
|
||||
return res, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -242,9 +242,9 @@ func TestReduce32_Bounds(t *testing.T) {
|
||||
// TestAddSubMod_Bounds exercises addModQ / subModQ around the boundary.
|
||||
func TestAddSubMod_Bounds(t *testing.T) {
|
||||
tests := []struct {
|
||||
a, b uint32
|
||||
wantA uint32 // addModQ(a,b)
|
||||
wantS uint32 // subModQ(a,b)
|
||||
a, b uint32
|
||||
wantA uint32 // addModQ(a,b)
|
||||
wantS uint32 // subModQ(a,b)
|
||||
}{
|
||||
{0, 0, 0, 0},
|
||||
{Q - 1, 1, 0, Q - 2},
|
||||
|
||||
+3
-3
@@ -451,9 +451,9 @@ func SignBatchGPU(privs []*PrivateKey, msgs, sigs [][]byte) (bool, error) {
|
||||
//
|
||||
// Dispatch ladder:
|
||||
//
|
||||
// 1. GPU substrate (SignBatchGPU) for n >= SignBatchThreshold.
|
||||
// 2. Goroutine-parallel CPU sign for n >= concurrentSignThreshold.
|
||||
// 3. Serial CPU sign as the floor.
|
||||
// 1. GPU substrate (SignBatchGPU) for n >= SignBatchThreshold.
|
||||
// 2. Goroutine-parallel CPU sign for n >= concurrentSignThreshold.
|
||||
// 3. Serial CPU sign as the floor.
|
||||
//
|
||||
// Equivalence: SLH-DSA-SHA2 sign is deterministic, so all tiers produce
|
||||
// byte-equal signatures for any given (sk, msg). Per FIPS 205 §10.2 the
|
||||
|
||||
@@ -33,8 +33,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/luxfi/crypto/backend"
|
||||
|
||||
|
||||
)
|
||||
|
||||
// genSignBatch192f produces n SLH-DSA-192f keypairs ready for SignBatch.
|
||||
|
||||
@@ -9,8 +9,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/luxfi/crypto/backend"
|
||||
|
||||
|
||||
)
|
||||
|
||||
// genBatch produces n SLH-DSA-192f keypairs + signs distinct messages with
|
||||
|
||||
+4
-3
@@ -152,9 +152,10 @@ func GetSignatureSize(mode Mode) int {
|
||||
|
||||
// GetPrivateKeySize returns the size of a private key for the given mode.
|
||||
// FIPS 205 §10 catalogue: sk = 4n where n is the hash output width:
|
||||
// 128-bit security : n=16 -> sk=64
|
||||
// 192-bit security : n=24 -> sk=96
|
||||
// 256-bit security : n=32 -> sk=128
|
||||
//
|
||||
// 128-bit security : n=16 -> sk=64
|
||||
// 192-bit security : n=24 -> sk=96
|
||||
// 256-bit security : n=32 -> sk=128
|
||||
func GetPrivateKeySize(mode Mode) int {
|
||||
switch mode {
|
||||
case SHA2_128s, SHAKE_128s, SHA2_128f, SHAKE_128f:
|
||||
|
||||
@@ -70,8 +70,8 @@ func TestParseNodeEoA(t *testing.T) {
|
||||
// EmptyCodeHash deterministically (see parseEoAccountNode in
|
||||
// encoding.go). values[2..255] stay nil after round-trip.
|
||||
values := make([][]byte, 256)
|
||||
values[0] = zero32[:] // basic data (packed: version+balance+nonce+codesize)
|
||||
values[1] = EmptyCodeHash[:] // code hash — required = EmptyCodeHash for EoA
|
||||
values[0] = zero32[:] // basic data (packed: version+balance+nonce+codesize)
|
||||
values[1] = EmptyCodeHash[:] // code hash — required = EmptyCodeHash for EoA
|
||||
ln, err := NewLeafNode(ffx32KeyTest[:31], values)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating leaf node: %v", err)
|
||||
|
||||
Reference in New Issue
Block a user