From 41971434d1ac17d3a978a651e619d43e266ddbba Mon Sep 17 00:00:00 2001 From: Adam Langley Date: Wed, 6 Apr 2011 14:11:56 -0400 Subject: [PATCH] crypto/rsa: add 3-prime support. R=golang-dev, rsc1 CC=golang-dev https://golang.org/cl/4365041 --- src/pkg/crypto/rsa/rsa.go | 124 ++++++++++++++++++++++++++++++--- src/pkg/crypto/rsa/rsa_test.go | 47 ++++++++++++- 2 files changed, 160 insertions(+), 11 deletions(-) diff --git a/src/pkg/crypto/rsa/rsa.go b/src/pkg/crypto/rsa/rsa.go index 57ef7be626..b3b212c206 100644 --- a/src/pkg/crypto/rsa/rsa.go +++ b/src/pkg/crypto/rsa/rsa.go @@ -92,19 +92,21 @@ type PublicKey struct { type PrivateKey struct { PublicKey // public part. D *big.Int // private exponent - P, Q *big.Int // prime factors of N + P, Q, R *big.Int // prime factors of N (R may be nil) - rwMutex sync.RWMutex // protects the following - dP, dQ *big.Int // D mod (P-1) (or mod Q-1) - qInv *big.Int // q^-1 mod p + rwMutex sync.RWMutex // protects the following + dP, dQ, dR *big.Int // D mod (P-1) (or mod Q-1 etc) + qInv *big.Int // q^-1 mod p + pq *big.Int // P*Q + tr *big.Int // pq·tr ≡ 1 mod r } // Validate performs basic sanity checks on the key. // It returns nil if the key is valid, or else an os.Error describing a problem. func (priv *PrivateKey) Validate() os.Error { - // Check that p and q are prime. Note that this is just a sanity - // check. Since the random witnesses chosen by ProbablyPrime are + // Check that p, q and, maybe, r are prime. Note that this is just a + // sanity check. Since the random witnesses chosen by ProbablyPrime are // deterministic, given the candidate number, it's easy for an attack // to generate composites that pass this test. if !big.ProbablyPrime(priv.P, 20) { @@ -113,16 +115,26 @@ func (priv *PrivateKey) Validate() os.Error { if !big.ProbablyPrime(priv.Q, 20) { return os.ErrorString("Q is composite") } + if priv.R != nil && !big.ProbablyPrime(priv.R, 20) { + return os.ErrorString("R is composite") + } - // Check that p*q == n. + // Check that p*q*r == n. modulus := new(big.Int).Mul(priv.P, priv.Q) + if priv.R != nil { + modulus.Mul(modulus, priv.R) + } if modulus.Cmp(priv.N) != 0 { return os.ErrorString("invalid modulus") } - // Check that e and totient(p, q) are coprime. + // Check that e and totient(p, q, r) are coprime. pminus1 := new(big.Int).Sub(priv.P, bigOne) qminus1 := new(big.Int).Sub(priv.Q, bigOne) totient := new(big.Int).Mul(pminus1, qminus1) + if priv.R != nil { + rminus1 := new(big.Int).Sub(priv.R, bigOne) + totient.Mul(totient, rminus1) + } e := big.NewInt(int64(priv.E)) gcd := new(big.Int) x := new(big.Int) @@ -131,7 +143,7 @@ func (priv *PrivateKey) Validate() os.Error { if gcd.Cmp(bigOne) != 0 { return os.ErrorString("invalid public exponent E") } - // Check that de ≡ 1 (mod totient(p, q)) + // Check that de ≡ 1 (mod totient(p, q, r)) de := new(big.Int).Mul(priv.D, e) de.Mod(de, totient) if de.Cmp(bigOne) != 0 { @@ -140,7 +152,7 @@ func (priv *PrivateKey) Validate() os.Error { return nil } -// GenerateKeyPair generates an RSA keypair of the given bit size. +// GenerateKey generates an RSA keypair of the given bit size. func GenerateKey(rand io.Reader, bits int) (priv *PrivateKey, err os.Error) { priv = new(PrivateKey) // Smaller public exponents lead to faster public key @@ -196,6 +208,77 @@ func GenerateKey(rand io.Reader, bits int) (priv *PrivateKey, err os.Error) { return } +// Generate3PrimeKey generates a 3-prime RSA keypair of the given bit size, as +// suggested in [1]. Although the public keys are compatible (actually, +// indistinguishable) from the 2-prime case, the private keys are not. Thus it +// may not be possible to export 3-prime private keys in certain formats or to +// subsequently import them into other code. +// +// Table 1 in [2] suggests that size should be >= 1024 when using 3 primes. +// +// [1] US patent 4405829 (1972, expired) +// [2] http://www.cacr.math.uwaterloo.ca/techreports/2006/cacr2006-16.pdf +func Generate3PrimeKey(rand io.Reader, bits int) (priv *PrivateKey, err os.Error) { + priv = new(PrivateKey) + priv.E = 3 + + pminus1 := new(big.Int) + qminus1 := new(big.Int) + rminus1 := new(big.Int) + totient := new(big.Int) + + for { + p, err := randomPrime(rand, bits/3) + if err != nil { + return nil, err + } + + todo := bits - p.BitLen() + q, err := randomPrime(rand, todo/2) + if err != nil { + return nil, err + } + + todo -= q.BitLen() + r, err := randomPrime(rand, todo) + if err != nil { + return nil, err + } + + if p.Cmp(q) == 0 || + q.Cmp(r) == 0 || + r.Cmp(p) == 0 { + continue + } + + n := new(big.Int).Mul(p, q) + n.Mul(n, r) + pminus1.Sub(p, bigOne) + qminus1.Sub(q, bigOne) + rminus1.Sub(r, bigOne) + totient.Mul(pminus1, qminus1) + totient.Mul(totient, rminus1) + + g := new(big.Int) + priv.D = new(big.Int) + y := new(big.Int) + e := big.NewInt(int64(priv.E)) + big.GcdInt(g, priv.D, y, e, totient) + + if g.Cmp(bigOne) == 0 { + priv.D.Add(priv.D, totient) + priv.P = p + priv.Q = q + priv.R = r + priv.N = n + + break + } + } + + return +} + // incCounter increments a four byte, big-endian counter. func incCounter(c *[4]byte) { if c[3]++; c[3] != 0 { @@ -336,6 +419,14 @@ func (priv *PrivateKey) precompute() { priv.dQ.Mod(priv.D, priv.dQ) priv.qInv = new(big.Int).ModInverse(priv.Q, priv.P) + + if priv.R != nil { + priv.dR = new(big.Int).Sub(priv.R, bigOne) + priv.dR.Mod(priv.D, priv.dR) + + priv.pq = new(big.Int).Mul(priv.P, priv.Q) + priv.tr = new(big.Int).ModInverse(priv.pq, priv.R) + } } // decrypt performs an RSA decryption, resulting in a plaintext integer. If a @@ -402,6 +493,19 @@ func decrypt(rand io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int, err os.E m.Mod(m, priv.P) m.Mul(m, priv.Q) m.Add(m, m2) + + if priv.dR != nil { + // 3-prime CRT. + m2.Exp(c, priv.dR, priv.R) + m2.Sub(m2, m) + m2.Mul(m2, priv.tr) + m2.Mod(m2, priv.R) + if m2.Sign() < 0 { + m2.Add(m2, priv.R) + } + m2.Mul(m2, priv.pq) + m.Add(m, m2) + } } priv.rwMutex.RUnlock() diff --git a/src/pkg/crypto/rsa/rsa_test.go b/src/pkg/crypto/rsa/rsa_test.go index 87fe6db9a5..d8a936eb68 100644 --- a/src/pkg/crypto/rsa/rsa_test.go +++ b/src/pkg/crypto/rsa/rsa_test.go @@ -21,15 +21,37 @@ func TestKeyGeneration(t *testing.T) { if err != nil { t.Errorf("failed to generate key") } + testKeyBasics(t, priv) +} + +func Test3PrimeKeyGeneration(t *testing.T) { + if testing.Short() { + return + } + + size := 768 + priv, err := Generate3PrimeKey(rand.Reader, size) + if err != nil { + t.Errorf("failed to generate key") + } + testKeyBasics(t, priv) +} + +func testKeyBasics(t *testing.T, priv *PrivateKey) { + if err := priv.Validate(); err != nil { + t.Errorf("Validate() failed: %s", err) + } + pub := &priv.PublicKey m := big.NewInt(42) c := encrypt(new(big.Int), pub, m) m2, err := decrypt(nil, priv, c) if err != nil { t.Errorf("error while decrypting: %s", err) + return } if m.Cmp(m2) != 0 { - t.Errorf("got:%v, want:%v (%s)", m2, m, priv) + t.Errorf("got:%v, want:%v (%+v)", m2, m, priv) } m3, err := decrypt(rand.Reader, priv, c) @@ -69,6 +91,29 @@ func BenchmarkRSA2048Decrypt(b *testing.B) { } } +func Benchmark3PrimeRSA2048Decrypt(b *testing.B) { + b.StopTimer() + priv := &PrivateKey{ + PublicKey: PublicKey{ + N: fromBase10("16346378922382193400538269749936049106320265317511766357599732575277382844051791096569333808598921852351577762718529818072849191122419410612033592401403764925096136759934497687765453905884149505175426053037420486697072448609022753683683718057795566811401938833367954642951433473337066311978821180526439641496973296037000052546108507805269279414789035461158073156772151892452251106173507240488993608650881929629163465099476849643165682709047462010581308719577053905787496296934240246311806555924593059995202856826239801816771116902778517096212527979497399966526283516447337775509777558018145573127308919204297111496233"), + E: 3, + }, + D: fromBase10("10897585948254795600358846499957366070880176878341177571733155050184921896034527397712889205732614568234385175145686545381899460748279607074689061600935843283397424506622998458510302603922766336783617368686090042765718290914099334449154829375179958369993407724946186243249568928237086215759259909861748642124071874879861299389874230489928271621259294894142840428407196932444474088857746123104978617098858619445675532587787023228852383149557470077802718705420275739737958953794088728369933811184572620857678792001136676902250566845618813972833750098806496641114644760255910789397593428910198080271317419213080834885003"), + P: fromBase10("1025363189502892836833747188838978207017355117492483312747347695538428729137306368764177201532277413433182799108299960196606011786562992097313508180436744488171474690412562218914213688661311117337381958560443"), + Q: fromBase10("3467903426626310123395340254094941045497208049900750380025518552334536945536837294961497712862519984786362199788654739924501424784631315081391467293694361474867825728031147665777546570788493758372218019373"), + R: fromBase10("4597024781409332673052708605078359346966325141767460991205742124888960305710298765592730135879076084498363772408626791576005136245060321874472727132746643162385746062759369754202494417496879741537284589047"), + } + priv.precompute() + + c := fromBase10("1000") + + b.StartTimer() + + for i := 0; i < b.N; i++ { + decrypt(nil, priv, c) + } +} + type testEncryptOAEPMessage struct { in []byte seed []byte