// 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 ( "crypto" "crypto/rand" "crypto/rsa" "errors" "fmt" "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. func validateParams(players, threshold uint) error { if players <= 1 { return errors.New("rsa_threshold: Players (l) invalid: should be > 1") } if threshold < 1 || threshold > players { return fmt.Errorf("rsa_threshold: Threshold (k) invalid: %d < 1 || %d > %d", threshold, threshold, players) } return nil } // Deal takes in an existing RSA private key generated elsewhere. If cache is true, cached values are stored in KeyShare taking up more memory by reducing Sign time. // See KeyShare documentation. Multi-prime RSA keys are unsupported. func Deal(randSource io.Reader, players, threshold uint, key *rsa.PrivateKey, cache bool) ([]KeyShare, error) { err := validateParams(players, threshold) ONE := big.NewInt(1) if err != nil { return nil, err } if len(key.Primes) != 2 { return nil, errors.New("multiprime rsa keys are unsupported") } p := key.Primes[0] q := key.Primes[1] e := int64(key.E) // p = 2p' + 1 // q = 2q' + 1 // p' = (p - 1)/2 // q' = (q - 1)/2 // m = p'q' = (p - 1)(q - 1)/4 var pprime big.Int // p - 1 pprime.Sub(p, ONE) // q - 1 var m big.Int m.Sub(q, ONE) // (p - 1)(q - 1) m.Mul(&m, &pprime) // >> 2 == / 4 m.Rsh(&m, 2) // de ≡ 1 var d big.Int _d := d.ModInverse(big.NewInt(e), &m) if _d == nil { return nil, errors.New("rsa_threshold: no ModInverse for e in Z/Zm") } // a_0...a_{k-1} a := make([]*big.Int, threshold) // a_0 = d a[0] = &d // a_0...a_{k-1} = rand from {0, ..., m - 1} for i := uint(1); i <= threshold-1; i++ { a[i], err = rand.Int(randSource, &m) if err != nil { return nil, errors.New("rsa_threshold: unable to generate an int within [0, m)") } } shares := make([]KeyShare, players) // 1 <= i <= l for i := uint(1); i <= players; i++ { shares[i-1].Players = players shares[i-1].Threshold = threshold // Σ^{k-1}_{i=0} | a_i * X^i (mod m) poly := computePolynomial(threshold, a, i, &m) shares[i-1].si = poly shares[i-1].Index = i if cache { shares[i-1].get2DeltaSi(int64(players)) } } return shares, nil } func calcN(p, q *big.Int) big.Int { // n = pq var n big.Int n.Mul(p, q) return n } // f(X) = Σ^{k-1}_{i=0} | a_i * X^i (mod m) func computePolynomial(k uint, a []*big.Int, x uint, m *big.Int) *big.Int { // TODO: use Horner's method here. sum := big.NewInt(0) // Σ^{k-1}_{i=0} for i := uint(0); i <= k-1; i++ { // X^i // TODO optimize: we can compute x^{n+1} from the previous x^n xi := int64(math.Pow(float64(x), float64(i))) // a_i * X^i prod := big.Int{} prod.Mul(a[i], big.NewInt(xi)) // (mod m) prod.Mod(&prod, m) // while not in the spec, we are eventually modding m, so we can mod here for efficiency // Σ sum.Add(sum, &prod) } sum.Mod(sum, m) return sum } // PadHash MUST be called before signing a message func PadHash(padder Padder, hash crypto.Hash, pub *rsa.PublicKey, msg []byte) ([]byte, error) { // Sign(Pad(Hash(M))) hasher := hash.New() hasher.Write(msg) digest := hasher.Sum(nil) return padder.Pad(pub, hash, digest) } type Signature = []byte // CombineSignShares combines t SignShare's to produce a valid signature func CombineSignShares(pub *rsa.PublicKey, shares []SignShare, msg []byte) (Signature, error) { players := shares[0].Players threshold := shares[0].Threshold for i := range shares { if shares[i].Players != players { return nil, errors.New("rsa_threshold: shares didn't have consistent players") } if shares[i].Threshold != threshold { return nil, errors.New("rsa_threshold: shares didn't have consistent threshold") } } if uint(len(shares)) < threshold { return nil, errors.New("rsa_threshold: insufficient shares for the threshold") } w := big.NewInt(1) delta := calculateDelta(int64(players)) // i_1 ... i_k for _, share := range shares { // λ(S, 0, i) lambda, err := computeLambda(delta, shares, 0, int64(share.Index)) if err != nil { return nil, err } // 2λ var exp big.Int exp.Add(lambda, lambda) // faster than TWO * lambda // we need to handle negative λ's (aka inverse), so abs it, compare, and if necessary modinverse abslam := big.Int{} abslam.Abs(&exp) var tmp big.Int // x_i^{|2λ|} tmp.Exp(share.xi, &abslam, pub.N) if abslam.Cmp(&exp) == 1 { tmp.ModInverse(&tmp, pub.N) } // TODO first compute all the powers for the negative exponents (but don't invert yet); multiply these together and then invert all at once. This is ok since (ab)^-1 = a^-1 b^-1 w.Mul(w, &tmp).Mod(w, pub.N) } w.Mod(w, pub.N) // e′ = 4∆^2 eprime := big.Int{} eprime.Mul(delta, delta) // faster than delta^TWO eprime.Add(&eprime, &eprime) // faster than FOUR * eprime eprime.Add(&eprime, &eprime) // e′a + eb = 1 a := big.Int{} b := big.Int{} e := big.NewInt(int64(pub.E)) tmp := big.Int{} tmp.GCD(&a, &b, &eprime, e) // TODO You can compute a earlier and multiply a into the exponents used when computing w. // w^a wa := big.Int{} wa.Exp(w, &a, pub.N) // TODO justification // x^b x := big.Int{} x.SetBytes(msg) xb := big.Int{} xb.Exp(&x, &b, pub.N) // TODO justification // y = w^a * x^b y := big.Int{} y.Mul(&wa, &xb).Mod(&y, pub.N) // verify that signature is valid by checking x == y^e. ye := big.Int{} ye.Exp(&y, e, pub.N) if ye.Cmp(&x) != 0 { return nil, errors.New("rsa: internal error") } // ensure signature has the right size. sig := y.FillBytes(make([]byte, pub.Size())) return sig, nil } // computes lagrange Interpolation for the shares // i must be an id 0..l but not in S // j must be in S func computeLambda(delta *big.Int, S []SignShare, i, j int64) (*big.Int, error) { if i == j { return nil, errors.New("rsa_threshold: i and j can't be equal by precondition") } // these are just to check preconditions foundi := false foundj := false // λ(s, i, j) = ∆( ( π{j'∈S\{j}} (i - j') ) / ( π{j'∈S\{j}} (j - j') ) ) num := int64(1) den := int64(1) // ∈ S for _, s := range S { // j' jprime := int64(s.Index) // S\{j} if jprime == j { foundj = true continue } if jprime == i { foundi = false break } // (i - j') num *= i - jprime // (j - j') den *= j - jprime } // ∆ * (num/den) var lambda big.Int // (num/den) lambda.Div(big.NewInt(num), big.NewInt(den)) // ∆ * (num/den) lambda.Mul(delta, &lambda) if foundi { return nil, fmt.Errorf("rsa_threshold: i: %d should not be in S", i) } if !foundj { return nil, fmt.Errorf("rsa_threshold: j: %d should be in S", j) } return &lambda, nil }