mirror of
https://github.com/luxfi/threshold.git
synced 2026-10-30 15:15:53 +00:00
Item5, re-targeted onto SHIPPED threshold v1.12.1 (cloud go.mod pin), not the stale on-disk main. The LSS Dilithium adapter (dilithium.go: sampleSecret, sampleError, sampleMask) and the true-threshold ML-DSA Shamir adapter (mldsa_threshold.go: sampleMaskingVector) sampled secret-key, error, and masking-polynomial coefficients with math/rand, a non-cryptographic PRNG. A predictable mask y is the lattice-signature analogue of ECDSA nonce reuse and can lead to key recovery. Both adapters are gated out of the production Corona/LSS path (factory_corona_prod.go) and unreferenced by any consumer, but the footgun is loaded for whoever wires them in next. Route all such sampling through a single crypto/rand-backed randIntn helper (csprng.go). crypto/rand.Int already does unbiased rejection sampling internally, so this is the one and only sampling path. Regression tests (csprng_test.go): randIntn bounds/panic behavior, freshness (non-determinism) of sampled secrets/masks across independent DKG and signing runs, and an AST-based source scan (TestPackage_NoMath RandForSecrets) that fails the build if math/rand (v1 or v2) is imported into this package again. No go.mod/go.sum change.
38 lines
1.5 KiB
Go
38 lines
1.5 KiB
Go
// Package adapters - shared CSPRNG helper for LSS signature adapters.
|
|
package adapters
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"fmt"
|
|
"math/big"
|
|
)
|
|
|
|
// randIntn returns a cryptographically secure uniform random integer in the
|
|
// half-open range [0, n).
|
|
//
|
|
// Every Dilithium/ML-DSA secret polynomial, error polynomial, and masking
|
|
// polynomial (y) coefficient sampled anywhere in this package MUST go
|
|
// through this function — never through math/rand or math/rand/v2, which
|
|
// are not cryptographically secure PRNGs. A predictable or low-entropy mask
|
|
// y is exactly the ECDSA-style nonce-reuse footgun for lattice signatures:
|
|
// leaking (or being able to guess) even a few bits of y across signatures
|
|
// enables the same class of key-recovery attacks nonce reuse gives against
|
|
// Schnorr/ECDSA (Bleichenbacher-style lattice attacks on biased nonces).
|
|
//
|
|
// crypto/rand.Int already performs its own unbiased rejection sampling
|
|
// against crypto/rand.Reader internally, so no additional rejection loop
|
|
// is needed here — this is intentionally the one and only sampling path.
|
|
func randIntn(n int) int {
|
|
if n <= 0 {
|
|
panic(fmt.Sprintf("adapters: randIntn requires n > 0, got %d", n))
|
|
}
|
|
v, err := rand.Int(rand.Reader, big.NewInt(int64(n)))
|
|
if err != nil {
|
|
// crypto/rand.Reader failing means the OS entropy source is broken.
|
|
// There is no safe fallback when sampling secret/nonce material —
|
|
// fail loudly rather than silently degrade to a weaker source.
|
|
panic(fmt.Sprintf("adapters: crypto/rand.Reader failed: %v", err))
|
|
}
|
|
return int(v.Int64())
|
|
}
|