Kudelski Security flagged that pre-fix bnb-chain/tss-lib keygen generated the RSA modulus $\tilde N$ in ecdsa/keygen/round_1.go via Go’s rsa.GenerateMultiPrimeKey, which returns ordinary RSA primes, not safe primes. However, the helper that later derives the DLN bases (common.GetRandomGeneratorOfTheQuadraticResidue) required $\tilde N$ to be a product of safe primes for its output to land in the prime-order QR subgroup (source):
1// FILE: ecdsa/keygen/round_1.go — bnb-chain/tss-lib @ a2c27b4 (vulnerable)
2// 5-7. generate auxiliary RSA primes for ZKPs later on
3go func(ch chan<- *rsa.PrivateKey) {
4 pk, err := rsa.GenerateMultiPrimeKey(rand.Reader, 2, RSAModulusLen)
5 if err != nil {
6 common.Logger.Errorf("RSA generation error: %s", err)
7 ch <- nil
8 }
9 ch <- pk
10}(rsaCh)
The fix introduced by PR #68 moved $\tilde N$ generation into a new ecdsa/keygen/prepare.go backed by a GermainSafePrime generator (source):
1// FILE: ecdsa/keygen/prepare.go — bnb-chain/tss-lib (post-PR #68, fixed)
2// 5-7. generate safe primes for ZKPs used later on
3go func(ch chan<- []*common.GermainSafePrime) {
4 sgps, err := common.GetRandomSafePrimesConcurrent(safePrimeBitLen, 2, timeout, concurrency/2)
5 if err != nil {
6 ch <- nil
7 return
8 }
9 ch <- sgps
10}(sgpCh)
11// ...
12NTildei, h1i, h2i, err := crypto.GenerateNTildei([2]*big.Int{sgps[0].SafePrime(), sgps[1].SafePrime()})
A later commit (769ccf744f) added sanity checks on the generator’s output and stored $p = (P-1)/2$, $q = (Q-1)/2$ as witnesses for the DLN proofs over $\tilde N$.