From: Filippo Valsorda Date: Wed, 27 Nov 2024 17:17:28 +0000 (+0100) Subject: crypto/internal/fips140/rsa: add Miller-Rabin test X-Git-Tag: go1.24rc1~60 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=22b5c145fb85771cdc5d16cd6aa8294bcf010d46;p=gostls13.git crypto/internal/fips140/rsa: add Miller-Rabin test A following CL will move key generation to crypto/internal/fips140/rsa. Updates #69799 For #69536 Change-Id: Icdf9b8424da20453939c6587af7dc922aad9e0ca Reviewed-on: https://go-review.googlesource.com/c/go/+/632215 Auto-Submit: Filippo Valsorda Reviewed-by: Roland Shoemaker LUCI-TryBot-Result: Go LUCI Reviewed-by: Russ Cox Reviewed-by: Daniel McCarney --- diff --git a/src/crypto/internal/fips140/bigmod/nat.go b/src/crypto/internal/fips140/bigmod/nat.go index 13a4ba6e96..e640696729 100644 --- a/src/crypto/internal/fips140/bigmod/nat.go +++ b/src/crypto/internal/fips140/bigmod/nat.go @@ -244,6 +244,56 @@ func (x *Nat) IsZero() choice { return zero } +// IsOne returns 1 if x == 1, and 0 otherwise. +func (x *Nat) IsOne() choice { + // Eliminate bounds checks in the loop. + size := len(x.limbs) + xLimbs := x.limbs[:size] + + if len(xLimbs) == 0 { + return no + } + + one := ctEq(xLimbs[0], 1) + for i := 1; i < size; i++ { + one &= ctEq(xLimbs[i], 0) + } + return one +} + +// IsMinusOne returns 1 if x == -1 mod m, and 0 otherwise. +// +// The length of x must be the same as the modulus. x must already be reduced +// modulo m. +func (x *Nat) IsMinusOne(m *Modulus) choice { + minusOne := m.Nat() + minusOne.SubOne(m) + return x.Equal(minusOne) +} + +// IsOdd returns 1 if x is odd, and 0 otherwise. +func (x *Nat) IsOdd() choice { + if len(x.limbs) == 0 { + return no + } + return choice(x.limbs[0] & 1) +} + +// TrailingZeroBitsVarTime returns the number of trailing zero bits in x. +func (x *Nat) TrailingZeroBitsVarTime() uint { + var t uint + limbs := x.limbs + for _, l := range limbs { + if l == 0 { + t += _W + continue + } + t += uint(bits.TrailingZeros(l)) + break + } + return t +} + // cmpGeq returns 1 if x >= y, and 0 otherwise. // // Both operands must have the same announced length. @@ -308,6 +358,37 @@ func (x *Nat) sub(y *Nat) (c uint) { return } +// ShiftRightVarTime sets x = x >> n. +// +// The announced length of x is unchanged. +func (x *Nat) ShiftRightVarTime(n uint) *Nat { + // Eliminate bounds checks in the loop. + size := len(x.limbs) + xLimbs := x.limbs[:size] + + shift := int(n % _W) + shiftLimbs := int(n / _W) + + var shiftedLimbs []uint + if shiftLimbs < size { + shiftedLimbs = xLimbs[shiftLimbs:] + } + + for i := range xLimbs { + if i >= len(shiftedLimbs) { + xLimbs[i] = 0 + continue + } + + xLimbs[i] = shiftedLimbs[i] >> shift + if i+1 < len(shiftedLimbs) { + xLimbs[i] |= shiftedLimbs[i+1] << (_W - shift) + } + } + + return x +} + // Modulus is used for modular arithmetic, precomputing relevant constants. // // A Modulus can leak the exact number of bits needed to store its value @@ -403,7 +484,7 @@ func NewModulus(b []byte) (*Modulus, error) { return nil, errors.New("modulus must be > 0") } m.leading = _W - bitLen(m.nat.limbs[len(m.nat.limbs)-1]) - if m.nat.limbs[0]&1 == 1 { + if m.nat.IsOdd() == 1 { m.odd = true m.m0inv = minusInverseModW(m.nat.limbs[0]) m.rr = rr(m) @@ -435,9 +516,13 @@ func (m *Modulus) BitLen() int { return len(m.nat.limbs)*_W - int(m.leading) } -// Nat returns m as a Nat. The return value must not be written to. +// Nat returns m as a Nat. func (m *Modulus) Nat() *Nat { - return m.nat + // Make a copy so that the caller can't modify m.nat or alias it with + // another Nat in a modulus operation. + n := NewNat() + n.set(m.nat) + return n } // shiftIn calculates x = x << _W + y mod m. @@ -553,6 +638,16 @@ func (x *Nat) Sub(y *Nat, m *Modulus) *Nat { return x } +// SubOne computes x = x - 1 mod m. +// +// The length of x must be the same as the modulus. x must already be reduced +// modulo m. +func (x *Nat) SubOne(m *Modulus) *Nat { + one := NewNat().ExpandFor(m) + one.limbs[0] = 1 + return x.Sub(one, m) +} + // Add computes x = x + y mod m. // // The length of both operands must be the same as the modulus. Both operands diff --git a/src/crypto/internal/fips140/bigmod/nat_test.go b/src/crypto/internal/fips140/bigmod/nat_test.go index 6ee0dd48da..06fd20868d 100644 --- a/src/crypto/internal/fips140/bigmod/nat_test.go +++ b/src/crypto/internal/fips140/bigmod/nat_test.go @@ -31,6 +31,14 @@ func (x *Nat) setBig(n *big.Int) *Nat { return x } +func (n *Nat) asBig() *big.Int { + bits := make([]big.Word, len(n.limbs)) + for i := range n.limbs { + bits[i] = big.Word(n.limbs[i]) + } + return new(big.Int).SetBits(bits) +} + func (n *Nat) String() string { var limbs []string for i := range n.limbs { @@ -404,6 +412,98 @@ func testMul(t *testing.T, n int) { } } +func TestIs(t *testing.T) { + checkYes := func(c choice, err string) { + t.Helper() + if c != yes { + t.Error(err) + } + } + checkNot := func(c choice, err string) { + t.Helper() + if c != no { + t.Error(err) + } + } + + mFour := modulusFromBytes([]byte{4}) + n, err := NewNat().SetBytes([]byte{3}, mFour) + if err != nil { + t.Fatal(err) + } + checkYes(n.IsMinusOne(mFour), "3 is not -1 mod 4") + checkNot(n.IsZero(), "3 is zero") + checkNot(n.IsOne(), "3 is one") + checkYes(n.IsOdd(), "3 is not odd") + n.SubOne(mFour) + checkNot(n.IsMinusOne(mFour), "2 is -1 mod 4") + checkNot(n.IsZero(), "2 is zero") + checkNot(n.IsOne(), "2 is one") + checkNot(n.IsOdd(), "2 is odd") + n.SubOne(mFour) + checkNot(n.IsMinusOne(mFour), "1 is -1 mod 4") + checkNot(n.IsZero(), "1 is zero") + checkYes(n.IsOne(), "1 is not one") + checkYes(n.IsOdd(), "1 is not odd") + n.SubOne(mFour) + checkNot(n.IsMinusOne(mFour), "0 is -1 mod 4") + checkYes(n.IsZero(), "0 is not zero") + checkNot(n.IsOne(), "0 is one") + checkNot(n.IsOdd(), "0 is odd") + n.SubOne(mFour) + checkYes(n.IsMinusOne(mFour), "-1 is not -1 mod 4") + checkNot(n.IsZero(), "-1 is zero") + checkNot(n.IsOne(), "-1 is one") + checkYes(n.IsOdd(), "-1 mod 4 is not odd") + + mTwoLimbs := maxModulus(2) + n, err = NewNat().SetBytes([]byte{0x01}, mTwoLimbs) + if err != nil { + t.Fatal(err) + } + if n.IsOne() != 1 { + t.Errorf("1 is not one") + } +} + +func TestTrailingZeroBits(t *testing.T) { + nb := new(big.Int).SetBytes([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7e}) + nb.Lsh(nb, 128) + expected := 129 + for expected >= 0 { + n := NewNat().setBig(nb) + if n.TrailingZeroBitsVarTime() != uint(expected) { + t.Errorf("%d != %d", n.TrailingZeroBitsVarTime(), expected) + } + nb.Rsh(nb, 1) + expected-- + } +} + +func TestRightShift(t *testing.T) { + nb, err := cryptorand.Int(cryptorand.Reader, new(big.Int).Lsh(big.NewInt(1), 1024)) + if err != nil { + t.Fatal(err) + } + for _, shift := range []uint{1, 32, 64, 128, 1024 - 128, 1024 - 64, 1024 - 32, 1024 - 1} { + testShift := func(t *testing.T, shift uint) { + n := NewNat().setBig(nb) + oldLen := len(n.limbs) + n.ShiftRightVarTime(shift) + if len(n.limbs) != oldLen { + t.Errorf("len(n.limbs) = %d, want %d", len(n.limbs), oldLen) + } + exp := new(big.Int).Rsh(nb, shift) + if n.asBig().Cmp(exp) != 0 { + t.Errorf("%v != %v", n.asBig(), exp) + } + } + t.Run(fmt.Sprint(shift-1), func(t *testing.T) { testShift(t, shift-1) }) + t.Run(fmt.Sprint(shift), func(t *testing.T) { testShift(t, shift) }) + t.Run(fmt.Sprint(shift+1), func(t *testing.T) { testShift(t, shift+1) }) + } +} + func natBytes(n *Nat) []byte { return n.Bytes(maxModulus(uint(len(n.limbs)))) } diff --git a/src/crypto/internal/fips140/rsa/keygen.go b/src/crypto/internal/fips140/rsa/keygen.go new file mode 100644 index 0000000000..e06e4cf771 --- /dev/null +++ b/src/crypto/internal/fips140/rsa/keygen.go @@ -0,0 +1,161 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package rsa + +import ( + "crypto/internal/fips140/bigmod" + "crypto/internal/fips140/drbg" + "errors" +) + +// isPrime runs the Miller-Rabin Probabilistic Primality Test from +// FIPS 186-5, Appendix B.3.1. +// +// w must be a random odd integer greater than three in big-endian order. +// isPrime might return false positives for adversarially chosen values. +// +// isPrime is not constant-time. +func isPrime(w []byte) bool { + mr, err := millerRabinSetup(w) + if err != nil { + // w is zero, one, or even. + return false + } + + // iterations is the number of Miller-Rabin rounds, each with a + // randomly-selected base. + // + // The worst case false positive rate for a single iteration is 1/4 per + // https://eprint.iacr.org/2018/749, so if w were selected adversarially, we + // would need up to 64 iterations to get to a negligible (2⁻¹²⁸) chance of + // false positive. + // + // However, since this function is only used for randomly-selected w in the + // context of RSA key generation, we can use a smaller number of iterations. + // The exact number depends on the size of the prime (and the implied + // security level). See BoringSSL for the full formula. + // https://cs.opensource.google/boringssl/boringssl/+/master:crypto/fipsmodule/bn/prime.c.inc;l=208-283;drc=3a138e43 + bits := mr.w.BitLen() + var iterations int + switch { + case bits >= 3747: + iterations = 3 + case bits >= 1345: + iterations = 4 + case bits >= 476: + iterations = 5 + case bits >= 400: + iterations = 6 + case bits >= 347: + iterations = 7 + case bits >= 308: + iterations = 8 + case bits >= 55: + iterations = 27 + default: + iterations = 34 + } + + b := make([]byte, (bits+7)/8) + for { + drbg.Read(b) + if excess := len(b)*8 - bits; excess != 0 { + b[0] >>= excess + } + result, err := millerRabinIteration(mr, b) + if err != nil { + // b was rejected. + continue + } + if result == millerRabinCOMPOSITE { + return false + } + iterations-- + if iterations == 0 { + return true + } + } +} + +type millerRabin struct { + w *bigmod.Modulus + a uint + m []byte +} + +// millerRabinSetup prepares state that's reused across multiple iterations of +// the Miller-Rabin test. +func millerRabinSetup(w []byte) (*millerRabin, error) { + mr := &millerRabin{} + + // Check that w is odd, and precompute Montgomery parameters. + wm, err := bigmod.NewModulus(w) + if err != nil { + return nil, err + } + if wm.Nat().IsOdd() == 0 { + return nil, errors.New("candidate is even") + } + mr.w = wm + + // Compute m = (w-1)/2^a, where m is odd. + wMinus1 := mr.w.Nat().SubOne(mr.w) + if wMinus1.IsZero() == 1 { + return nil, errors.New("candidate is one") + } + mr.a = wMinus1.TrailingZeroBitsVarTime() + + // Store mr.m as a big-endian byte slice with leading zero bytes removed, + // for use with [bigmod.Nat.Exp]. + m := wMinus1.ShiftRightVarTime(mr.a) + mr.m = m.Bytes(mr.w) + for mr.m[0] == 0 { + mr.m = mr.m[1:] + } + + return mr, nil +} + +const millerRabinCOMPOSITE = false +const millerRabinPOSSIBLYPRIME = true + +func millerRabinIteration(mr *millerRabin, bb []byte) (bool, error) { + // Reject b ≤ 1 or b ≥ w − 1. + if len(bb) != (mr.w.BitLen()+7)/8 { + return false, errors.New("incorrect length") + } + b := bigmod.NewNat() + if _, err := b.SetBytes(bb, mr.w); err != nil { + return false, err + } + if b.IsZero() == 1 || b.IsOne() == 1 || b.IsMinusOne(mr.w) == 1 { + return false, errors.New("out-of-range candidate") + } + + // Compute b^(m*2^i) mod w for successive i. + // If b^m mod w = 1, b is a possible prime. + // If b^(m*2^i) mod w = -1 for some 0 <= i < a, b is a possible prime. + // Otherwise b is composite. + + // Start by computing and checking b^m mod w (also the i = 0 case). + z := bigmod.NewNat().Exp(b, mr.m, mr.w) + if z.IsOne() == 1 || z.IsMinusOne(mr.w) == 1 { + return millerRabinPOSSIBLYPRIME, nil + } + + // Check b^(m*2^i) mod w = -1 for 0 < i < a. + for range mr.a - 1 { + z.Mul(z, mr.w) + if z.IsMinusOne(mr.w) == 1 { + return millerRabinPOSSIBLYPRIME, nil + } + if z.IsOne() == 1 { + // Future squaring will not turn z == 1 into -1. + break + } + } + + return millerRabinCOMPOSITE, nil +} diff --git a/src/crypto/internal/fips140/rsa/keygen_test.go b/src/crypto/internal/fips140/rsa/keygen_test.go new file mode 100644 index 0000000000..7d613e6ddf --- /dev/null +++ b/src/crypto/internal/fips140/rsa/keygen_test.go @@ -0,0 +1,93 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package rsa + +import ( + "bufio" + "encoding/hex" + "fmt" + "os" + "strings" + "testing" +) + +func TestMillerRabin(t *testing.T) { + f, err := os.Open("testdata/miller_rabin_tests.txt") + if err != nil { + t.Fatal(err) + } + + var expected bool + var W, B string + var lineNum int + scanner := bufio.NewScanner(f) + for scanner.Scan() { + lineNum++ + line := scanner.Text() + if len(line) == 0 || line[0] == '#' { + continue + } + + k, v, _ := strings.Cut(line, " = ") + switch k { + case "Result": + switch v { + case "Composite": + expected = millerRabinCOMPOSITE + case "PossiblyPrime": + expected = millerRabinPOSSIBLYPRIME + default: + t.Fatalf("unknown result %q on line %d", v, lineNum) + } + case "W": + W = v + case "B": + B = v + + t.Run(fmt.Sprintf("line %d", lineNum), func(t *testing.T) { + if len(W)%2 != 0 { + W = "0" + W + } + for len(B) < len(W) { + B = "0" + B + } + + mr, err := millerRabinSetup(decodeHex(t, W)) + if err != nil { + t.Logf("W = %s", W) + t.Logf("B = %s", B) + t.Fatalf("failed to set up Miller-Rabin test: %v", err) + } + + result, err := millerRabinIteration(mr, decodeHex(t, B)) + if err != nil { + t.Logf("W = %s", W) + t.Logf("B = %s", B) + t.Fatalf("failed to run Miller-Rabin test: %v", err) + } + + if result != expected { + t.Logf("W = %s", W) + t.Logf("B = %s", B) + t.Fatalf("unexpected result: got %v, want %v", result, expected) + } + }) + default: + t.Fatalf("unknown key %q on line %d", k, lineNum) + } + } + if err := scanner.Err(); err != nil { + t.Fatal(err) + } +} + +func decodeHex(t *testing.T, s string) []byte { + t.Helper() + b, err := hex.DecodeString(s) + if err != nil { + t.Fatalf("failed to decode hex %q: %v", s, err) + } + return b +} diff --git a/src/crypto/internal/fips140/rsa/testdata/miller_rabin_tests.txt b/src/crypto/internal/fips140/rsa/testdata/miller_rabin_tests.txt new file mode 100644 index 0000000000..10e9685a61 --- /dev/null +++ b/src/crypto/internal/fips140/rsa/testdata/miller_rabin_tests.txt @@ -0,0 +1,344 @@ +# This file contains test vectors for whether B is a Miller-Rabin composite +# witness for W. W must be odd and B must satisfy 1 <= B <= W-1. +# +# It was copied from BoringSSL's crypto/fipsmodule/bn/test/miller_rabin_tests.txt, +# removing out-of-range candidates that we reject within the iteration function. + +# Exhaustively test a small prime. + +Result = PossiblyPrime +W = 7 +B = 2 + +Result = PossiblyPrime +W = 7 +B = 3 + +Result = PossiblyPrime +W = 7 +B = 4 + +Result = PossiblyPrime +W = 7 +B = 5 + + +# Random large inputs which try to cover a few cases. The nontrivial square root +# case appears to be difficult to hit randomly. + +# b^m = w-1 +Result = PossiblyPrime +W = d6b4ffc7cf70b2a2fc5d6023015875504d40e3dcce7c2e6b762c3de7bb806a5074144e7054198dabf53d23108679ccc541d5a99efeb1d1abaf89e0dbcead2a8b +B = fabbafdbec6494ddb5ea4bf458536e87082369b0e53a200ed413f3e64b2fddc7c57c565710fbe73fae5b188fce97d8dcca74c2b5d90906c96d3c2c358a735cd + +# b^m = w-1 +Result = PossiblyPrime +W = 52cc61c42b341ad56dc11495e7cb2fe31e506b9e99522efbf44cd7c28468d3833c5e360f3c77b0aa43c0495c4e14665ab0d7cee9294c722f0de47d4401828401 +B = 3bdc9639c0fc2e77ab48d46e0b4ac6529c11c900e8fe4d82d75767c0556feb23d3f42d4924d16876a743feb386b7b84c7fd16a6c252f662faf0024d19972e62f + +# b^m = w-1 +Result = PossiblyPrime +W = cff9897aa7dce0f2afad262b2de57d301305de717f3539c537c4ce062f8cb70df13fbc1eb4a3b9f0958a8810d1ca9042b4f23334b285a15fee3fc66498761d4b +B = 9ceb43132fddf9ee4104ea1cb3eb2253c1d7f803f05f0305de9e31a17dd75832f47b8bf189a9b7ca0905f2a7470d9c6349080f481ff1708696fa12d972e7d7ba + +# Some b^(m*2^j) = w-1 +Result = PossiblyPrime +W = 67d1825dad5344170e65247a87aef1634a1b32bdc22f2f04d9d2959767bb5a27610fba55cd607e0f9fdd9fbb0f7f98e40d5e1eb2f52318fb5be4dbfd30d38861 +B = 260fb14724ff80984736859d8755ee98b25bcb56db9fde1db001a1e1273374034c5b75fd60b3710c7a08ce7d390776f010f384d4e32943cf0c477497d53e9e05 + +# Some b^(m*2^j) = w-1 +Result = PossiblyPrime +W = ad0bc85b58aaa204177aa9431a40929beb1cbea2dd6f66a25cc54600013213b225ba881805661df43f4208965ada7aacc8095d07d3cbef1a7bbfaae8b745f731 +B = 3d9310f20e9c80269fa6830c7e1a6f02fc5c58646001a9ef6b8b3e496602ff22c3dcb2ddb6a221723fc1722ce237fb46f7a7bb2945e415c8839b15a972f076c9 + +# Some b^(m*2^j) = w-1 +Result = PossiblyPrime +W = b25c917f55f6c7b596921daba919f35039e5d805119c1587e99849dd7104460c86214f162a6f17aea847bc7f3859e59f2991d457059511972ef373d4bc75e309 +B = a1f10b261dee84619b0423201d46af19eef9ec0612cf947c4d5c36c0c4b28207f75967e69452eabad0a5dcd28f27f7a8a7ed9c8b3e5026c6e0ba5634d94c2d44 + +# b^m = 1 +Result = PossiblyPrime +W = d3eeb0eff05b6992e9fa61b02755e155f4aae28c6e45ddb874edd86acdd2d83d18a20e0e00d8b8bc94b92d14fc3f41ced6ababe8ac98c7730c075dbe0f699369 +B = 6b7717269c6225203681a1cacec87cacd83003ec6e9e3f04effcc4f86634770c0860e1f2770b8f303719a44949664a1094205a99d95a0856758fed66d690105e + +# b^m = 1 +Result = PossiblyPrime +W = 64561b8d9aa50340c3a01ccb3e6e17f5023513661c012be288f3900a3ca76890e67290b9560fa1d480f9d2aacccca581b5690636665f243fa13aff5d0bff12d3 +B = 1f5ff70d3d60671ebc5fbfca731898a04438053dbc3c841e6335f487e457d92d9efb5d506d5bef6872d58d12b9a41c950bfc38d12ed977c90eacdd6535b811a0 + +# b^m = 1 +Result = PossiblyPrime +W = 69c63fbf44df21b0ed0ee929a740c12d1f3f064da0dcd9d509f31fa45fa27d1a759ab5a9f6f1040d7ee90a0b1e68f779273c41ea1c1198fd547ff6bd70c7e787 +B = 5f7996a9bbfd8fd88e472220b70077bfdacdd63d88885134431f024c2acb7126827b174eb093eb5313f07bb5461de9b0feb7d77ca2c39c2a323a150f33ea525f + +# End of iteration +Result = Composite +W = 28cc3e08c44571c6dcb98a9ab8b4f3e2b16e1f884997d94a3188bcbb7f1b7cdaecdae8329c013ec8f75dc00004da0039943e4262cd080b16a42910102e00dddb +B = 512061ab1c69931c2fa0bb89d8d09f3c9209230bf927ddd6fb6a72075f967ed3c4dbb5f437bf4d31ca7344782b22011ad56609dc19aed65319bababfc13dd7 + +# End of iteration +Result = Composite +W = 4eeb7b4d371c45fe8586fee3b1efd792176b70f6cc2698dfa1dd028366626febe0199c3c5f77a5c3cad0057a04767383051d41965255d03681b2a37edad34a9b +B = 4afc2e85f84017b3fd6967a227eb74c8297b40ea02733d9513bff9b3f01081963f25872f4254afc4e9321eea35b2a1e42eadb186fcc84f2f30f4a994350b93b8 + +# End of iteration +Result = Composite +W = 8e35a959555dd2eb66c65cee3c264071d20671f159e1f9896f1d0ceb041905fcf053eacc189de317c3ee6f93901223cbf30d5b7ddbbdab981790e2f6397e6803 +B = 44c0153759309ec4e5b1e59d57c1b126545ef7ea302b6e43561df4d16068b922389d6924f01c945d9080d1f93a0732599bdedae72d6d590839dc0884dd860441 + + +# 0x6c1 = 1729 = 7 * 13 * 19 is a Fermat pseudoprime. + +# Found non-trivial square root +Result = Composite +W = 6c1 +B = b8 + +# End of iteration +Result = Composite +W = 6c1 +B = 111 + +# End of iteration +Result = Composite +W = 6c1 +B = 11d + +# Found non-trivial square root +Result = Composite +W = 6c1 +B = 19c + +# Found non-trivial square root +Result = Composite +W = 6c1 +B = 223 + +# End of iteration +Result = Composite +W = 6c1 +B = 3aa + +# Found non-trivial square root +Result = Composite +W = 6c1 +B = 653 + + +# 1729 has a number of false witnesses. + +# b^m = 1 +Result = PossiblyPrime +W = 6c1 +B = 78 + +# b^m = 1 +Result = PossiblyPrime +W = 6c1 +B = eb + +# b^m = w-1 +Result = PossiblyPrime +W = 6c1 +B = 178 + +# b^m = w-1 +Result = PossiblyPrime +W = 6c1 +B = 178 + +# b^m = w-1 +Result = PossiblyPrime +W = 6c1 +B = 1aa + +# b^m = 1 +Result = PossiblyPrime +W = 6c1 +B = 271 + +# b^m = 1 +Result = PossiblyPrime +W = 6c1 +B = 2b2 + + +# https://kconrad.math.uconn.edu/blurbs/ugradnumthy/millerrabin.pdf, examples +# 3.1 and 3.2 has a complete list of false witnesses for 65 = 0x41 and +# 85 = 0x55. + +# Some b^(m*2^j) = w-1 +Result = PossiblyPrime +W = 41 +B = 8 + +# Some b^(m*2^j) = w-1 +Result = PossiblyPrime +W = 41 +B = 12 + +# Some b^(m*2^j) = w-1 +Result = PossiblyPrime +W = 41 +B = 2f + +# Some b^(m*2^j) = w-1 +Result = PossiblyPrime +W = 41 +B = 39 + +# Some b^(m*2^j) = w-1 +Result = PossiblyPrime +W = 55 +B = d + +# Some b^(m*2^j) = w-1 +Result = PossiblyPrime +W = 55 +B = 26 + +# Some b^(m*2^j) = w-1 +Result = PossiblyPrime +W = 55 +B = 2f + +# Some b^(m*2^j) = w-1 +Result = PossiblyPrime +W = 55 +B = 48 + +# Other witnesses for 65 and 85 will report composite: + +# Found non-trivial square root +Result = Composite +W = 41 +B = 2c + +# End of iteration +Result = Composite +W = 41 +B = 16 + +# End of iteration +Result = Composite +W = 41 +B = 14 + +# End of iteration +Result = Composite +W = 41 +B = 2 + +# End of iteration +Result = Composite +W = 41 +B = 3a + +# End of iteration +Result = Composite +W = 55 +B = 40 + +# End of iteration +Result = Composite +W = 55 +B = 7 + +# End of iteration +Result = Composite +W = 55 +B = 23 + +# End of iteration +Result = Composite +W = 55 +B = 2e + +# End of iteration +Result = Composite +W = 55 +B = 2a + +# W below is composite, but it is one of the worst case scenarios for +# Miller-Rabin, from Wycheproof tests. 1/4 of witnesses report the value is +# prime. Test that we correctly classify false and true witnesses. + +# b^m = w-1 +Result = PossiblyPrime +W = 550fda19f97cdfbd13930911ef6e9e1cb2b7b5215a35c215d51ebffeb435642174cbe998f4451bde2d4bd2ce92ab5b9493b657f1d77d9ad4d348550247b903906109c608ecba7f88c239c76f0afc231e7f1ac1cee87b4c34448a16f7979ff4c18e65e05d5a86909615fe56587576962a2cb3ba467d9806445a0f039907601af77ba7d07578eff612364fbcac11d35e243734aa6d9a6cdcf912a2dd0a12ba7e87 +B = 379c6027f818b5164bc13dff5e996ec7210976f33570d5c60275918b8988d97a63bb6582af85682c45667a8b94b7acab4d919ede00f5bd2ba7abc8634d66f8875fd930f35ec8013d37b958e65f07de015c0574e64198d73aab5466f3a971b74830b7f1671cb9277fbc95c1ba8c29dc903d8cea1b74c22ab9164f9c438ab9ba7d9919f832e40c3e36faca7343e2314669b0104d9c4f2e1b011cdbd9c686baef0 + +# b^m = w-1 +Result = PossiblyPrime +W = 550fda19f97cdfbd13930911ef6e9e1cb2b7b5215a35c215d51ebffeb435642174cbe998f4451bde2d4bd2ce92ab5b9493b657f1d77d9ad4d348550247b903906109c608ecba7f88c239c76f0afc231e7f1ac1cee87b4c34448a16f7979ff4c18e65e05d5a86909615fe56587576962a2cb3ba467d9806445a0f039907601af77ba7d07578eff612364fbcac11d35e243734aa6d9a6cdcf912a2dd0a12ba7e87 +B = 3cc4b644965b2133caffc2bb6258b1ecd5b586b900a09b010382fcef709e4cd37ee3e3182bf8d393c1ab6f9a933d46338b3d960923d8c9607c2b2763d5680230a2bc0c91138e9d0ecb35e7154a06aaa902d34b9b14964b81f4d8232641492d83b22cd805a115e75ddd8e63b864c00e4c90ba36a41e7966e97e063a60a6a6cfd53e1f62a57852c7443e88dcf6245557a4b65494c3e88e466ad75316aaa9727def + +# b^m = 1 +Result = PossiblyPrime +W = 550fda19f97cdfbd13930911ef6e9e1cb2b7b5215a35c215d51ebffeb435642174cbe998f4451bde2d4bd2ce92ab5b9493b657f1d77d9ad4d348550247b903906109c608ecba7f88c239c76f0afc231e7f1ac1cee87b4c34448a16f7979ff4c18e65e05d5a86909615fe56587576962a2cb3ba467d9806445a0f039907601af77ba7d07578eff612364fbcac11d35e243734aa6d9a6cdcf912a2dd0a12ba7e87 +B = 40c03b6ba22bd62c0379b1c36dfccd34d61e3d15f7af1d5f6a60ab972a9d0e956e2bb9e275294e0f1c879eb7a4555443429c99a8d74f7bd359a1046ac30072c04b0e2cbd005be15ff4ce0c93276de2c513fbc5771b5059904a87f180530f6773498114b5aaf70da01967d8294742e451df6377dd5e64b2a8968f4ba61b51a154317d63958ff3788defbeeebee21af5027c2291e8c5df8c0b66770d91b683cffe + +# b^m = w-1 +Result = PossiblyPrime +W = 550fda19f97cdfbd13930911ef6e9e1cb2b7b5215a35c215d51ebffeb435642174cbe998f4451bde2d4bd2ce92ab5b9493b657f1d77d9ad4d348550247b903906109c608ecba7f88c239c76f0afc231e7f1ac1cee87b4c34448a16f7979ff4c18e65e05d5a86909615fe56587576962a2cb3ba467d9806445a0f039907601af77ba7d07578eff612364fbcac11d35e243734aa6d9a6cdcf912a2dd0a12ba7e87 +B = 3c7c71b84f0c6c3817f57511946315cec7d0120a9c30ceabda801fbaec329a8f10c7b9f0ae90a3dada9885bf73a3cabed86784af9682f3dea50a7817f65cfc9190cf997f12784223c4965ed6e52a1be26d4dde31741cd3d1a2e2f3a74040d0f3868eef849727aa855f66c94791194ad5d360298364e2de9ca9288e6423f644b01d52e1bd66a9f7f00bd7995a9ca2ed16f40e902852c6250a3b52bbbf5bfd33e8 + +# b^m = w-1 +Result = PossiblyPrime +W = 550fda19f97cdfbd13930911ef6e9e1cb2b7b5215a35c215d51ebffeb435642174cbe998f4451bde2d4bd2ce92ab5b9493b657f1d77d9ad4d348550247b903906109c608ecba7f88c239c76f0afc231e7f1ac1cee87b4c34448a16f7979ff4c18e65e05d5a86909615fe56587576962a2cb3ba467d9806445a0f039907601af77ba7d07578eff612364fbcac11d35e243734aa6d9a6cdcf912a2dd0a12ba7e87 +B = 36e6aa9acb399a50f52be0324dcef05f3cff3117f94538f6d0952b7d7be88ba4dc75d843ff7ff775e11f55c86ba6b2a6ddebd8850c33424b4d35c66321af426662e7074f0a2409a9ccf1c66ef7d823efc8240b8f3c7e9e8dd65a64e8a3ca5b26695ef17171ffe136c0593b179414c5b5ad0d66f2a25146c38b2f97e60b0472ed72de34bff1b6ac186f23645a1bbe909cdfc2b2d861eb44931568f1bb117d8a0c + +# End of iteration +Result = Composite +W = 550fda19f97cdfbd13930911ef6e9e1cb2b7b5215a35c215d51ebffeb435642174cbe998f4451bde2d4bd2ce92ab5b9493b657f1d77d9ad4d348550247b903906109c608ecba7f88c239c76f0afc231e7f1ac1cee87b4c34448a16f7979ff4c18e65e05d5a86909615fe56587576962a2cb3ba467d9806445a0f039907601af77ba7d07578eff612364fbcac11d35e243734aa6d9a6cdcf912a2dd0a12ba7e87 +B = 278f2215d3ab836043fbfa472216bbdcedb775a6a0ed711754d05aa75089a9e5d8201e113d68656f37381e44483cd365f5d383bdca5ae8d1f2e6575d7873851cfff0e12b1cfe100a04cb300cbd924353fcbd3307d01242cf6a5e86e752c6f4586bcabf48b018bb97e65c3ed409fd6f67f98987517356d88344b3c8945ccd753148a37b648dd2db44d19522a69a9ad8eb23edc55340e85a198abf179ad731db41 + +# End of iteration +Result = Composite +W = 550fda19f97cdfbd13930911ef6e9e1cb2b7b5215a35c215d51ebffeb435642174cbe998f4451bde2d4bd2ce92ab5b9493b657f1d77d9ad4d348550247b903906109c608ecba7f88c239c76f0afc231e7f1ac1cee87b4c34448a16f7979ff4c18e65e05d5a86909615fe56587576962a2cb3ba467d9806445a0f039907601af77ba7d07578eff612364fbcac11d35e243734aa6d9a6cdcf912a2dd0a12ba7e87 +B = afa1478bebbfe1157568f4ae53549b4c3a6a8771b816970bfac6ce5c8b962231db7a41da4d5f1d8bf504dcfe440325b54e1888bdae344eb969436a35e5c6ce5300d46313cb2fcb57fc83305f65f53d392de400e9231cbbc2ac8243defcaf7063c632b9601a81d83138274702ff336d727d3e82ccacce069843ac9c1c590c772c8c586b65c7085a1df5a47fc960d4098a22418b41f0062c77b5d55d17149d167 + +# End of iteration +Result = Composite +W = 550fda19f97cdfbd13930911ef6e9e1cb2b7b5215a35c215d51ebffeb435642174cbe998f4451bde2d4bd2ce92ab5b9493b657f1d77d9ad4d348550247b903906109c608ecba7f88c239c76f0afc231e7f1ac1cee87b4c34448a16f7979ff4c18e65e05d5a86909615fe56587576962a2cb3ba467d9806445a0f039907601af77ba7d07578eff612364fbcac11d35e243734aa6d9a6cdcf912a2dd0a12ba7e87 +B = 10f7030590b629e0313a61bdf46936a1f25db91b2b421f7ebb671f7844c22561b44b2f7699db61e5228ebb5817afad416325f9439eff7a82d8a630c504de12eaa44d97c79ee56e726ae74ee0b472f0d5fa8f20aee426e689cd33dd084f96bf4d928a21e815f7e8aaca4a5752f39c4a76bdfaa8227dc05d0dfa885d8b26d46fbcbf0d2e0d999d2c31ad84c306c9126539dbdf447f8dc707d29c7fa8021a767668 + +# End of iteration +Result = Composite +W = 550fda19f97cdfbd13930911ef6e9e1cb2b7b5215a35c215d51ebffeb435642174cbe998f4451bde2d4bd2ce92ab5b9493b657f1d77d9ad4d348550247b903906109c608ecba7f88c239c76f0afc231e7f1ac1cee87b4c34448a16f7979ff4c18e65e05d5a86909615fe56587576962a2cb3ba467d9806445a0f039907601af77ba7d07578eff612364fbcac11d35e243734aa6d9a6cdcf912a2dd0a12ba7e87 +B = 97dbb6a55c039ec926aaa5ff15a2917a2b4cafc3ca07c4c6b05f931d86c9bf60ee05cbbace194e5ca97682ec67c36394018d68c3536fbf13b50f8a7e31eaed87307759a0a48c6c58d21bc7c38b878c53db5d7a8e1fdd81abefc50470a3800852e74d76fdd1933e45f39ee97b8efb68837721890d867b32a894dd0ceb4c5844a05d384145865c10973ce748ccdd8fee73f1bf8611ce0535430b6b98fb36cad7a + +# End of iteration +Result = Composite +W = 550fda19f97cdfbd13930911ef6e9e1cb2b7b5215a35c215d51ebffeb435642174cbe998f4451bde2d4bd2ce92ab5b9493b657f1d77d9ad4d348550247b903906109c608ecba7f88c239c76f0afc231e7f1ac1cee87b4c34448a16f7979ff4c18e65e05d5a86909615fe56587576962a2cb3ba467d9806445a0f039907601af77ba7d07578eff612364fbcac11d35e243734aa6d9a6cdcf912a2dd0a12ba7e87 +B = 225f58add44ed2b0a64a1d8452866d0f3c0cd45c8375e1bb33c188915c77fa11b81250b920245dda7f6126e5e0c79e6f98f89dc15db86394cf81b44f0d801e613fa4d5c6fef66fa31f26cfe6153f2e8159aad6b0351dcc0e93f9a68f649b2a77cff747b605b542d22419166befebec6cde3201e3c0cacaa2bc9d87073b8d1f1aa2b114d61de45ac8b0ad2141b43434a629ef284cd999fd82b310db7c57cf5c81 + +# End of iteration +Result = Composite +W = 550fda19f97cdfbd13930911ef6e9e1cb2b7b5215a35c215d51ebffeb435642174cbe998f4451bde2d4bd2ce92ab5b9493b657f1d77d9ad4d348550247b903906109c608ecba7f88c239c76f0afc231e7f1ac1cee87b4c34448a16f7979ff4c18e65e05d5a86909615fe56587576962a2cb3ba467d9806445a0f039907601af77ba7d07578eff612364fbcac11d35e243734aa6d9a6cdcf912a2dd0a12ba7e87 +B = 2780926c9cf7c1eb2aaa935d90b6d4dea44eeefdfcf9ccd4a33feb215e3a1cb2d358136a490fed18403947f3d98807819737c66e12d42c3cc8c0e246b96b3c3b0795ab875fbaf668b81b5b05bf23e258ea00a0a140a790f76e04ab619800b7597f614ffc1a1c94be2f3f1a71d64eb47d98e4653d76eabedacff3a97ecf590e6a1fd55096b7bc9314629f698d0fbe9b01a1f2bc0bf3a2c097f99f1fd222b52ed2 + +# End of iteration +Result = Composite +W = 550fda19f97cdfbd13930911ef6e9e1cb2b7b5215a35c215d51ebffeb435642174cbe998f4451bde2d4bd2ce92ab5b9493b657f1d77d9ad4d348550247b903906109c608ecba7f88c239c76f0afc231e7f1ac1cee87b4c34448a16f7979ff4c18e65e05d5a86909615fe56587576962a2cb3ba467d9806445a0f039907601af77ba7d07578eff612364fbcac11d35e243734aa6d9a6cdcf912a2dd0a12ba7e87 +B = 129cc5b0d9f8001b3895f1fcb4833779763636aeeeb3f980e63ea506202e6bde868444b6a58ff1dca08625f025a7e95a5eaaf1a8899eee640e3f05fbdb2867e2483bdc27c87b58684416e521c107f3667ed8dd23f0381edab767c5205a4378118bc011947cb6bdfe3fa4af50b8de876b555c9a0b2b0dae01261847f63e1e0cac2d032530bf19d5da60a04dfe22ce6343f60defbb94ccf0bdf010f89a4029720 + +# b^m = 1 +Result = PossiblyPrime +W = 550fda19f97cdfbd13930911ef6e9e1cb2b7b5215a35c215d51ebffeb435642174cbe998f4451bde2d4bd2ce92ab5b9493b657f1d77d9ad4d348550247b903906109c608ecba7f88c239c76f0afc231e7f1ac1cee87b4c34448a16f7979ff4c18e65e05d5a86909615fe56587576962a2cb3ba467d9806445a0f039907601af77ba7d07578eff612364fbcac11d35e243734aa6d9a6cdcf912a2dd0a12ba7e87 +B = 4e2a47cf67c3331b1e9976f583f6339cf76a8d48682d01355c25b2aed90c5544e737ecfa849c17d27a64fad7e659ef48df9a3ac0410e5c7ca8d087fc3a3ba23e5a3f000be009fcc8227ead28158c5b5d66f2efb47111638ef61cea4984de42fbd476bc2236ad02154d3ce85805c45e49d16b496e313a4052a37d4b88a3b13e598d2074a3e36a37e90278601f2b2305e034f9bf3aea8e939c3ba274e8ff4d8a14 + +# b^m = 1 +Result = PossiblyPrime +W = 550fda19f97cdfbd13930911ef6e9e1cb2b7b5215a35c215d51ebffeb435642174cbe998f4451bde2d4bd2ce92ab5b9493b657f1d77d9ad4d348550247b903906109c608ecba7f88c239c76f0afc231e7f1ac1cee87b4c34448a16f7979ff4c18e65e05d5a86909615fe56587576962a2cb3ba467d9806445a0f039907601af77ba7d07578eff612364fbcac11d35e243734aa6d9a6cdcf912a2dd0a12ba7e87 +B = 2455c4ab826e2ae72708a8ff51348ce4821cb86fa89e298c751c1754211c63b2e9a712d40f0235f310606fcf296726a86973f19f890d571f5b90f026e8d24d07bc0478a3c1333171587387f1f7fe4a770b593216f2743318aabacb3320c40a4e52b9f409e1176fe8db099e93a7991eb8568168e2e486fa5aa228bb1dce9df3290ef13fd21c331479bb0f8b7a7e7f03c5211ae8cc46fa4d0f46e86b2dadeddd5b + +# End of iteration +Result = Composite +W = 550fda19f97cdfbd13930911ef6e9e1cb2b7b5215a35c215d51ebffeb435642174cbe998f4451bde2d4bd2ce92ab5b9493b657f1d77d9ad4d348550247b903906109c608ecba7f88c239c76f0afc231e7f1ac1cee87b4c34448a16f7979ff4c18e65e05d5a86909615fe56587576962a2cb3ba467d9806445a0f039907601af77ba7d07578eff612364fbcac11d35e243734aa6d9a6cdcf912a2dd0a12ba7e87 +B = 9951c2c02dd7deedce29bd0c78dd80066b1d69c0e6fe4a17f7d03c6a640d866d01fc8214bafb6737efd93d80a35b8993f5367ce287459b07954e9771ffbc72ccdd812d26a9bf4be0292a24eb5c3b56f09619b1c1b481f7566f7e50e65f69f5feb591bd107fec72a783429dbde6e2607f3db2c58d4b070a45b4d6b43537e19942ce890b04ae1e91069c04a96ed03ddb2f4fc456f136b98102c70a15700dbd911 + +# End of iteration +Result = Composite +W = 550fda19f97cdfbd13930911ef6e9e1cb2b7b5215a35c215d51ebffeb435642174cbe998f4451bde2d4bd2ce92ab5b9493b657f1d77d9ad4d348550247b903906109c608ecba7f88c239c76f0afc231e7f1ac1cee87b4c34448a16f7979ff4c18e65e05d5a86909615fe56587576962a2cb3ba467d9806445a0f039907601af77ba7d07578eff612364fbcac11d35e243734aa6d9a6cdcf912a2dd0a12ba7e87 +B = 4cb8217d229d5f95f6d94807a99363823655d6bba6bdafa4f0dbfe7a5c538aa79c918710aad4f55caaee5ab405ebdcef29dfb76cae99fca8d5a955b6315f71a3cb2d69a217ff45aed66ba87cdc5c0de5d512c6dd12e641e9fe6a2557dd2f03bf3a18650ff139efa179f0fbe69cbb4b54e50d13177bfe7bb90de36b548d5ccfef74b05d3c08a7e2a3bb4dc8d7eb338a7a1b068c433ea204d171eda5e7c6b6722c diff --git a/src/crypto/rsa/rsa.go b/src/crypto/rsa/rsa.go index eb6ce73e0f..3c9b98eae9 100644 --- a/src/crypto/rsa/rsa.go +++ b/src/crypto/rsa/rsa.go @@ -243,10 +243,6 @@ func (priv *PrivateKey) Validate() error { if err != nil { return fmt.Errorf("crypto/rsa: invalid private exponent: %v", err) } - one, err := bigmod.NewNat().SetUint(1, N) - if err != nil { - return fmt.Errorf("crypto/rsa: internal error: %v", err) - } Π := bigmod.NewNat().ExpandFor(N) for _, prime := range priv.Primes { @@ -254,7 +250,7 @@ func (priv *PrivateKey) Validate() error { if err != nil { return fmt.Errorf("crypto/rsa: invalid prime: %v", err) } - if p.IsZero() == 1 { + if p.IsZero() == 1 || p.IsOne() == 1 { return errors.New("crypto/rsa: invalid prime") } Π.Mul(p, N) @@ -265,11 +261,7 @@ func (priv *PrivateKey) Validate() error { // exponent(ℤ/nℤ). It also implies that a^de ≡ a mod p as a^(p-1) ≡ 1 // mod p. Thus a^de ≡ a mod n for all a coprime to n, as required. - p.Sub(one, N) - if p.IsZero() == 1 { - return errors.New("crypto/rsa: invalid prime") - } - pMinus1, err := bigmod.NewModulus(p.Bytes(N)) + pMinus1, err := bigmod.NewModulus(p.SubOne(N).Bytes(N)) if err != nil { return fmt.Errorf("crypto/rsa: internal error: %v", err) } @@ -278,16 +270,11 @@ func (priv *PrivateKey) Validate() error { if err != nil { return fmt.Errorf("crypto/rsa: invalid public exponent: %v", err) } - one, err := bigmod.NewNat().SetUint(1, pMinus1) - if err != nil { - return fmt.Errorf("crypto/rsa: internal error: %v", err) - } de := bigmod.NewNat() de.Mod(d, pMinus1) de.Mul(e, pMinus1) - de.Sub(one, pMinus1) - if de.IsZero() != 1 { + if de.IsOne() != 1 { return errors.New("crypto/rsa: invalid exponents") } }