Skip to content

Commit

Permalink
Key generation for threshold RSA (safe primes).
Browse files Browse the repository at this point in the history
  • Loading branch information
armfazh committed Jul 19, 2023
1 parent c6e3661 commit 099d313
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 0 deletions.
34 changes: 34 additions & 0 deletions math/primes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package math

import (
"crypto/rand"
"io"
"math/big"
)

// IsSafePrime reports whether p is (probably) a safe prime.
// The prime p=2*q+1 is safe prime if both p and q are primes.
// Note that ProbablyPrime is not suitable for judging primes
// that an adversary may have crafted to fool the test.
func IsSafePrime(p *big.Int) bool {
pdiv2 := new(big.Int).Rsh(p, 1)
return p.ProbablyPrime(20) && pdiv2.ProbablyPrime(20)
}

// SafePrime returns a number of the given bit length that is a safe prime with high probability.
// The number returned p=2*q+1 is a safe prime if both p and q are primes.
// SafePrime will return error for any error returned by rand.Read or if bits < 2.
func SafePrime(random io.Reader, bits int) (*big.Int, error) {
one := big.NewInt(1)
p := new(big.Int)
for {
q, err := rand.Prime(random, bits-1)
if err != nil {
return nil, err
}
p.Lsh(q, 1).Add(p, one)
if p.ProbablyPrime(20) {
return p, nil
}
}
}
46 changes: 46 additions & 0 deletions math/primes_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package math

import (
"crypto/rand"
"fmt"
"math/big"
"testing"

"github.com/cloudflare/circl/internal/test"
)

func TestSafePrime(t *testing.T) {
firstSafePrimes := []int64{
5, 7, 11, 23, 47, 59, 83, 107, 167, 179, 227, 263, 347, 359, 383, 467,
479, 503, 563, 587, 719, 839, 863, 887, 983, 1019, 1187, 1283, 1307,
1319, 1367, 1439, 1487, 1523, 1619, 1823, 1907, 2027, 2039, 2063, 2099,
2207, 2447, 2459, 2579, 2819, 2879, 2903, 2963, 2999, 3023, 3119, 3167,
3203, 3467, 3623, 3779, 3803, 3863, 3947, 4007, 4079, 4127, 4139, 4259,
4283, 4547, 4679, 4703, 4787, 4799, 4919, 5087, 5099, 5387, 5399, 5483,
5507, 5639, 5807, 5879, 5927, 5939, 6047, 6599, 6659, 6719, 6779, 6827,
6899, 6983, 7079, 7187, 7247, 7523, 7559, 7607, 7643, 7703, 7727,
}

p := new(big.Int)
for _, pi := range firstSafePrimes {
p.SetInt64(pi)
test.CheckOk(IsSafePrime(p), fmt.Sprintf("it should be a safe prime p=%v", p), t)
}
}

func TestIsSafePrime(t *testing.T) {
for i := 1; i < 5; i++ {
bits := 128 * i
t.Run(fmt.Sprint(bits), func(t *testing.T) {
p, err := SafePrime(rand.Reader, bits)
test.CheckNoErr(t, err, "safeprime failed")
test.CheckOk(IsSafePrime(p), fmt.Sprintf("it should be a safe prime p=%v", p), t)
})
}
}

func BenchmarkSafePrime(b *testing.B) {
for i := 0; i < b.N; i++ {
_, _ = SafePrime(rand.Reader, 256)
}
}
1 change: 1 addition & 0 deletions tss/doc.go
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
// Package tss provides threshold signature schemes.
package tss
60 changes: 60 additions & 0 deletions tss/rsa/rsa_threshold.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
// Package rsa provides RSA threshold signature scheme.
//
// This package implements the Protocol 1 of "Practical Threshold Signatures"
// by Victor Shoup [1].
//
// # References
//
// [1] https://www.iacr.org/archive/eurocrypt2000/1807/18070209-new.pdf
package rsa

import (
Expand All @@ -9,8 +17,60 @@ import (
"io"
"math"
"math/big"

cmath "github.com/cloudflare/circl/math"
)

// GenerateKey generates a RSA keypair for its use in RSA threshold signatures.
// Internally, the modulus is the product of two safe primes. The time
// consumed by this function is relatively longer than the regular
// GenerateKey function from the crypto/rsa package.
func GenerateKey(random io.Reader, bits int) (*rsa.PrivateKey, error) {
p, err := cmath.SafePrime(random, bits/2)
if err != nil {
return nil, err
}

var q *big.Int
n := new(big.Int)
found := false
for !found {
q, err = cmath.SafePrime(random, bits-p.BitLen())
if err != nil {
return nil, err
}

// check for different primes.
if p.Cmp(q) != 0 {
n.Mul(p, q)
// check n has the desired bitlength.
if n.BitLen() == bits {
found = true
}
}
}

one := big.NewInt(1)
pminus1 := new(big.Int).Sub(p, one)
qminus1 := new(big.Int).Sub(q, one)
totient := new(big.Int).Mul(pminus1, qminus1)

priv := new(rsa.PrivateKey)
priv.Primes = []*big.Int{p, q}
priv.N = n
priv.E = 65537
priv.D = new(big.Int)
e := big.NewInt(int64(priv.E))
ok := priv.D.ModInverse(e, totient)
if ok == nil {
return nil, errors.New("public key is not coprime to phi(n)")
}

priv.Precompute()

return priv, nil
}

// l or `Players`, the total number of Players.
// t, the number of corrupted Players.
// k=t+1 or `Threshold`, the number of signature shares needed to obtain a signature.
Expand Down
11 changes: 11 additions & 0 deletions tss/rsa/rsa_threshold_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,24 @@ import (
"crypto/rsa"
_ "crypto/sha256"
"errors"
"fmt"
"io"
"math/big"
"testing"

"github.com/cloudflare/circl/internal/test"
)

var ONE = big.NewInt(1)

func TestGenerateKey(t *testing.T) {
// [Warning]: this is only for tests, use a secure bitlen above 2048 bits.
bitlen := 128
key, err := GenerateKey(rand.Reader, bitlen)
test.CheckNoErr(t, err, "failed to create key")
test.CheckOk(key.Validate() == nil, fmt.Sprintf("key is not valid: %v", key), t)
}

func createPrivateKey(p, q *big.Int, e int) *rsa.PrivateKey {
return &rsa.PrivateKey{
PublicKey: rsa.PublicKey{
Expand Down

0 comments on commit 099d313

Please sign in to comment.