Skip to content

Commit 099d313

Browse files
committed
Key generation for threshold RSA (safe primes).
1 parent c6e3661 commit 099d313

File tree

5 files changed

+152
-0
lines changed

5 files changed

+152
-0
lines changed

math/primes.go

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package math
2+
3+
import (
4+
"crypto/rand"
5+
"io"
6+
"math/big"
7+
)
8+
9+
// IsSafePrime reports whether p is (probably) a safe prime.
10+
// The prime p=2*q+1 is safe prime if both p and q are primes.
11+
// Note that ProbablyPrime is not suitable for judging primes
12+
// that an adversary may have crafted to fool the test.
13+
func IsSafePrime(p *big.Int) bool {
14+
pdiv2 := new(big.Int).Rsh(p, 1)
15+
return p.ProbablyPrime(20) && pdiv2.ProbablyPrime(20)
16+
}
17+
18+
// SafePrime returns a number of the given bit length that is a safe prime with high probability.
19+
// The number returned p=2*q+1 is a safe prime if both p and q are primes.
20+
// SafePrime will return error for any error returned by rand.Read or if bits < 2.
21+
func SafePrime(random io.Reader, bits int) (*big.Int, error) {
22+
one := big.NewInt(1)
23+
p := new(big.Int)
24+
for {
25+
q, err := rand.Prime(random, bits-1)
26+
if err != nil {
27+
return nil, err
28+
}
29+
p.Lsh(q, 1).Add(p, one)
30+
if p.ProbablyPrime(20) {
31+
return p, nil
32+
}
33+
}
34+
}

math/primes_test.go

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package math
2+
3+
import (
4+
"crypto/rand"
5+
"fmt"
6+
"math/big"
7+
"testing"
8+
9+
"github.com/cloudflare/circl/internal/test"
10+
)
11+
12+
func TestSafePrime(t *testing.T) {
13+
firstSafePrimes := []int64{
14+
5, 7, 11, 23, 47, 59, 83, 107, 167, 179, 227, 263, 347, 359, 383, 467,
15+
479, 503, 563, 587, 719, 839, 863, 887, 983, 1019, 1187, 1283, 1307,
16+
1319, 1367, 1439, 1487, 1523, 1619, 1823, 1907, 2027, 2039, 2063, 2099,
17+
2207, 2447, 2459, 2579, 2819, 2879, 2903, 2963, 2999, 3023, 3119, 3167,
18+
3203, 3467, 3623, 3779, 3803, 3863, 3947, 4007, 4079, 4127, 4139, 4259,
19+
4283, 4547, 4679, 4703, 4787, 4799, 4919, 5087, 5099, 5387, 5399, 5483,
20+
5507, 5639, 5807, 5879, 5927, 5939, 6047, 6599, 6659, 6719, 6779, 6827,
21+
6899, 6983, 7079, 7187, 7247, 7523, 7559, 7607, 7643, 7703, 7727,
22+
}
23+
24+
p := new(big.Int)
25+
for _, pi := range firstSafePrimes {
26+
p.SetInt64(pi)
27+
test.CheckOk(IsSafePrime(p), fmt.Sprintf("it should be a safe prime p=%v", p), t)
28+
}
29+
}
30+
31+
func TestIsSafePrime(t *testing.T) {
32+
for i := 1; i < 5; i++ {
33+
bits := 128 * i
34+
t.Run(fmt.Sprint(bits), func(t *testing.T) {
35+
p, err := SafePrime(rand.Reader, bits)
36+
test.CheckNoErr(t, err, "safeprime failed")
37+
test.CheckOk(IsSafePrime(p), fmt.Sprintf("it should be a safe prime p=%v", p), t)
38+
})
39+
}
40+
}
41+
42+
func BenchmarkSafePrime(b *testing.B) {
43+
for i := 0; i < b.N; i++ {
44+
_, _ = SafePrime(rand.Reader, 256)
45+
}
46+
}

tss/doc.go

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
// Package tss provides threshold signature schemes.
12
package tss

tss/rsa/rsa_threshold.go

+60
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
// Package rsa provides RSA threshold signature scheme.
2+
//
3+
// This package implements the Protocol 1 of "Practical Threshold Signatures"
4+
// by Victor Shoup [1].
5+
//
6+
// # References
7+
//
8+
// [1] https://www.iacr.org/archive/eurocrypt2000/1807/18070209-new.pdf
19
package rsa
210

311
import (
@@ -9,8 +17,60 @@ import (
917
"io"
1018
"math"
1119
"math/big"
20+
21+
cmath "github.com/cloudflare/circl/math"
1222
)
1323

24+
// GenerateKey generates a RSA keypair for its use in RSA threshold signatures.
25+
// Internally, the modulus is the product of two safe primes. The time
26+
// consumed by this function is relatively longer than the regular
27+
// GenerateKey function from the crypto/rsa package.
28+
func GenerateKey(random io.Reader, bits int) (*rsa.PrivateKey, error) {
29+
p, err := cmath.SafePrime(random, bits/2)
30+
if err != nil {
31+
return nil, err
32+
}
33+
34+
var q *big.Int
35+
n := new(big.Int)
36+
found := false
37+
for !found {
38+
q, err = cmath.SafePrime(random, bits-p.BitLen())
39+
if err != nil {
40+
return nil, err
41+
}
42+
43+
// check for different primes.
44+
if p.Cmp(q) != 0 {
45+
n.Mul(p, q)
46+
// check n has the desired bitlength.
47+
if n.BitLen() == bits {
48+
found = true
49+
}
50+
}
51+
}
52+
53+
one := big.NewInt(1)
54+
pminus1 := new(big.Int).Sub(p, one)
55+
qminus1 := new(big.Int).Sub(q, one)
56+
totient := new(big.Int).Mul(pminus1, qminus1)
57+
58+
priv := new(rsa.PrivateKey)
59+
priv.Primes = []*big.Int{p, q}
60+
priv.N = n
61+
priv.E = 65537
62+
priv.D = new(big.Int)
63+
e := big.NewInt(int64(priv.E))
64+
ok := priv.D.ModInverse(e, totient)
65+
if ok == nil {
66+
return nil, errors.New("public key is not coprime to phi(n)")
67+
}
68+
69+
priv.Precompute()
70+
71+
return priv, nil
72+
}
73+
1474
// l or `Players`, the total number of Players.
1575
// t, the number of corrupted Players.
1676
// k=t+1 or `Threshold`, the number of signature shares needed to obtain a signature.

tss/rsa/rsa_threshold_test.go

+11
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,24 @@ import (
77
"crypto/rsa"
88
_ "crypto/sha256"
99
"errors"
10+
"fmt"
1011
"io"
1112
"math/big"
1213
"testing"
14+
15+
"github.com/cloudflare/circl/internal/test"
1316
)
1417

1518
var ONE = big.NewInt(1)
1619

20+
func TestGenerateKey(t *testing.T) {
21+
// [Warning]: this is only for tests, use a secure bitlen above 2048 bits.
22+
bitlen := 128
23+
key, err := GenerateKey(rand.Reader, bitlen)
24+
test.CheckNoErr(t, err, "failed to create key")
25+
test.CheckOk(key.Validate() == nil, fmt.Sprintf("key is not valid: %v", key), t)
26+
}
27+
1728
func createPrivateKey(p, q *big.Int, e int) *rsa.PrivateKey {
1829
return &rsa.PrivateKey{
1930
PublicKey: rsa.PublicKey{

0 commit comments

Comments
 (0)