diff options
Diffstat (limited to 'src/crypto/rsa')
-rw-r--r-- | src/crypto/rsa/boring.go | 124 | ||||
-rw-r--r-- | src/crypto/rsa/boring_test.go | 125 | ||||
-rw-r--r-- | src/crypto/rsa/pkcs1v15.go | 86 | ||||
-rw-r--r-- | src/crypto/rsa/pkcs1v15_test.go | 2 | ||||
-rw-r--r-- | src/crypto/rsa/pss.go | 35 | ||||
-rw-r--r-- | src/crypto/rsa/pss_test.go | 3 | ||||
-rw-r--r-- | src/crypto/rsa/rsa.go | 68 | ||||
-rw-r--r-- | src/crypto/rsa/rsa_test.go | 87 |
8 files changed, 503 insertions, 27 deletions
diff --git a/src/crypto/rsa/boring.go b/src/crypto/rsa/boring.go new file mode 100644 index 0000000000..0f362a2f16 --- /dev/null +++ b/src/crypto/rsa/boring.go @@ -0,0 +1,124 @@ +// Copyright 2017 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/boring" + "math/big" + "sync/atomic" + "unsafe" +) + +// Cached conversions from Go PublicKey/PrivateKey to BoringCrypto. +// +// A new 'boring atomic.Value' field in both PublicKey and PrivateKey +// serves as a cache for the most recent conversion. The cache is an +// atomic.Value because code might reasonably set up a key and then +// (thinking it immutable) use it from multiple goroutines simultaneously. +// The first operation initializes the cache; if there are multiple simultaneous +// first operations, they will do redundant work but not step on each other. +// +// We could just assume that once used in a sign/verify/encrypt/decrypt operation, +// a particular key is never again modified, but that has not been a +// stated assumption before. Just in case there is any existing code that +// does modify the key between operations, we save the original values +// alongside the cached BoringCrypto key and check that the real key +// still matches before using the cached key. The theory is that the real +// operations are significantly more expensive than the comparison. + +type boringPub struct { + key *boring.PublicKeyRSA + orig PublicKey +} + +func boringPublicKey(pub *PublicKey) (*boring.PublicKeyRSA, error) { + b := (*boringPub)(atomic.LoadPointer(&pub.boring)) + if b != nil && publicKeyEqual(&b.orig, pub) { + return b.key, nil + } + + b = new(boringPub) + b.orig = copyPublicKey(pub) + key, err := boring.NewPublicKeyRSA(b.orig.N, big.NewInt(int64(b.orig.E))) + if err != nil { + return nil, err + } + b.key = key + atomic.StorePointer(&pub.boring, unsafe.Pointer(b)) + return key, nil +} + +type boringPriv struct { + key *boring.PrivateKeyRSA + orig PrivateKey +} + +func boringPrivateKey(priv *PrivateKey) (*boring.PrivateKeyRSA, error) { + b := (*boringPriv)(atomic.LoadPointer(&priv.boring)) + if b != nil && privateKeyEqual(&b.orig, priv) { + return b.key, nil + } + + b = new(boringPriv) + b.orig = copyPrivateKey(priv) + + var N, E, D, P, Q, Dp, Dq, Qinv *big.Int + N = b.orig.N + E = big.NewInt(int64(b.orig.E)) + D = b.orig.D + if len(b.orig.Primes) == 2 { + P = b.orig.Primes[0] + Q = b.orig.Primes[1] + Dp = b.orig.Precomputed.Dp + Dq = b.orig.Precomputed.Dq + Qinv = b.orig.Precomputed.Qinv + } + key, err := boring.NewPrivateKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv) + if err != nil { + return nil, err + } + b.key = key + atomic.StorePointer(&priv.boring, unsafe.Pointer(b)) + return key, nil +} + +func publicKeyEqual(k1, k2 *PublicKey) bool { + return k1.N != nil && + k1.N.Cmp(k2.N) == 0 && + k1.E == k2.E +} + +func copyPublicKey(k *PublicKey) PublicKey { + return PublicKey{ + N: new(big.Int).Set(k.N), + E: k.E, + } +} + +func privateKeyEqual(k1, k2 *PrivateKey) bool { + return publicKeyEqual(&k1.PublicKey, &k2.PublicKey) && + k1.D.Cmp(k2.D) == 0 +} + +func copyPrivateKey(k *PrivateKey) PrivateKey { + dst := PrivateKey{ + PublicKey: copyPublicKey(&k.PublicKey), + D: new(big.Int).Set(k.D), + } + dst.Primes = make([]*big.Int, len(k.Primes)) + for i, p := range k.Primes { + dst.Primes[i] = new(big.Int).Set(p) + } + if x := k.Precomputed.Dp; x != nil { + dst.Precomputed.Dp = new(big.Int).Set(x) + } + if x := k.Precomputed.Dq; x != nil { + dst.Precomputed.Dq = new(big.Int).Set(x) + } + if x := k.Precomputed.Qinv; x != nil { + dst.Precomputed.Qinv = new(big.Int).Set(x) + } + return dst +} diff --git a/src/crypto/rsa/boring_test.go b/src/crypto/rsa/boring_test.go new file mode 100644 index 0000000000..11dcdf88fd --- /dev/null +++ b/src/crypto/rsa/boring_test.go @@ -0,0 +1,125 @@ +// Copyright 2017 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. + +// Note: Can run these tests against the non-BoringCrypto +// version of the code by using "CGO_ENABLED=0 go test". + +package rsa + +import ( + "crypto" + "crypto/rand" + "encoding/asn1" + "reflect" + "runtime" + "runtime/debug" + "sync" + "sync/atomic" + "testing" + "unsafe" +) + +func TestBoringASN1Marshal(t *testing.T) { + k, err := GenerateKey(rand.Reader, 128) + if err != nil { + t.Fatal(err) + } + // This used to fail, because of the unexported 'boring' field. + // Now the compiler hides it [sic]. + _, err = asn1.Marshal(k.PublicKey) + if err != nil { + t.Fatal(err) + } +} + +func TestBoringDeepEqual(t *testing.T) { + k, err := GenerateKey(rand.Reader, 128) + if err != nil { + t.Fatal(err) + } + k.boring = nil // probably nil already but just in case + k2 := *k + k2.boring = unsafe.Pointer(k) // anything not nil, for this test + if !reflect.DeepEqual(k, &k2) { + // compiler should be hiding the boring field from reflection + t.Fatalf("DeepEqual compared boring fields") + } +} + +func TestBoringVerify(t *testing.T) { + // Check that signatures that lack leading zeroes don't verify. + key := &PublicKey{ + N: bigFromHex("c4fdf7b40a5477f206e6ee278eaef888ca73bf9128a9eef9f2f1ddb8b7b71a4c07cfa241f028a04edb405e4d916c61d6beabc333813dc7b484d2b3c52ee233c6a79b1eea4e9cc51596ba9cd5ac5aeb9df62d86ea051055b79d03f8a4fa9f38386f5bd17529138f3325d46801514ea9047977e0829ed728e68636802796801be1"), + E: 65537, + } + + hash := fromHex("019c5571724fb5d0e47a4260c940e9803ba05a44") + paddedHash := fromHex("3021300906052b0e03021a05000414019c5571724fb5d0e47a4260c940e9803ba05a44") + + // signature is one byte shorter than key.N. + sig := fromHex("5edfbeb6a73e7225ad3cc52724e2872e04260d7daf0d693c170d8c4b243b8767bc7785763533febc62ec2600c30603c433c095453ede59ff2fcabeb84ce32e0ed9d5cf15ffcbc816202b64370d4d77c1e9077d74e94a16fb4fa2e5bec23a56d7a73cf275f91691ae1801a976fcde09e981a2f6327ac27ea1fecf3185df0d56") + + err := VerifyPKCS1v15(key, 0, paddedHash, sig) + if err == nil { + t.Errorf("raw: expected verification error") + } + + err = VerifyPKCS1v15(key, crypto.SHA1, hash, sig) + if err == nil { + t.Errorf("sha1: expected verification error") + } +} + +func TestBoringGenerateKey(t *testing.T) { + k, err := GenerateKey(rand.Reader, 2048) // 2048 is smallest size BoringCrypto might kick in for + if err != nil { + t.Fatal(err) + } + + // Non-Boring GenerateKey always sets CRTValues to a non-nil (possibly empty) slice. + if k.Precomputed.CRTValues == nil { + t.Fatalf("GenerateKey: Precomputed.CRTValues = nil") + } +} + +func TestBoringFinalizers(t *testing.T) { + if runtime.GOOS == "nacl" || runtime.GOOS == "js" { + // Times out on nacl and js/wasm (without BoringCrypto) + // but not clear why - probably consuming rand.Reader too quickly + // and being throttled. Also doesn't really matter. + t.Skipf("skipping on %s/%s", runtime.GOOS, runtime.GOARCH) + } + + k, err := GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatal(err) + } + + // Run test with GOGC=10, to make bug more likely. + // Without the KeepAlives, the loop usually dies after + // about 30 iterations. + defer debug.SetGCPercent(debug.SetGCPercent(10)) + for n := 0; n < 200; n++ { + // Clear the underlying BoringCrypto object. + atomic.StorePointer(&k.boring, nil) + + // Race to create the underlying BoringCrypto object. + // The ones that lose the race are prime candidates for + // being GC'ed too early if the finalizers are not being + // used correctly. + var wg sync.WaitGroup + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + defer wg.Done() + sum := make([]byte, 32) + _, err := SignPKCS1v15(rand.Reader, k, crypto.SHA256, sum) + if err != nil { + panic(err) // usually caused by memory corruption, so hard stop + } + }() + } + wg.Wait() + } +} diff --git a/src/crypto/rsa/pkcs1v15.go b/src/crypto/rsa/pkcs1v15.go index 0cbd6d0045..213ddb4add 100644 --- a/src/crypto/rsa/pkcs1v15.go +++ b/src/crypto/rsa/pkcs1v15.go @@ -14,6 +14,8 @@ import ( "crypto/internal/randutil" ) +import "crypto/internal/boring" + // This file implements encryption and decryption using PKCS #1 v1.5 padding. // PKCS1v15DecrypterOpts is for passing options to PKCS #1 v1.5 decryption using @@ -36,8 +38,8 @@ type PKCS1v15DecryptOptions struct { // // WARNING: use of this function to encrypt plaintexts other than // session keys is dangerous. Use RSA OAEP in new protocols. -func EncryptPKCS1v15(rand io.Reader, pub *PublicKey, msg []byte) ([]byte, error) { - randutil.MaybeReadByte(rand) +func EncryptPKCS1v15(random io.Reader, pub *PublicKey, msg []byte) ([]byte, error) { + randutil.MaybeReadByte(random) if err := checkPub(pub); err != nil { return nil, err @@ -47,20 +49,37 @@ func EncryptPKCS1v15(rand io.Reader, pub *PublicKey, msg []byte) ([]byte, error) return nil, ErrMessageTooLong } + if boring.Enabled && random == boring.RandReader { + bkey, err := boringPublicKey(pub) + if err != nil { + return nil, err + } + return boring.EncryptRSAPKCS1(bkey, msg) + } + boring.UnreachableExceptTests() + // EM = 0x00 || 0x02 || PS || 0x00 || M em := make([]byte, k) em[1] = 2 ps, mm := em[2:len(em)-len(msg)-1], em[len(em)-len(msg):] - err := nonZeroRandomBytes(ps, rand) + err := nonZeroRandomBytes(ps, random) if err != nil { return nil, err } em[len(em)-len(msg)-1] = 0 copy(mm, msg) + if boring.Enabled { + var bkey *boring.PublicKeyRSA + bkey, err = boringPublicKey(pub) + if err != nil { + return nil, err + } + return boring.EncryptRSANoPadding(bkey, em) + } + m := new(big.Int).SetBytes(em) c := encrypt(new(big.Int), pub, m) - return c.FillBytes(em), nil } @@ -76,6 +95,19 @@ func DecryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) ([]byt if err := checkPub(&priv.PublicKey); err != nil { return nil, err } + + if boring.Enabled { + bkey, err := boringPrivateKey(priv) + if err != nil { + return nil, err + } + out, err := boring.DecryptRSAPKCS1(bkey, ciphertext) + if err != nil { + return nil, ErrDecryption + } + return out, nil + } + valid, out, index, err := decryptPKCS1v15(rand, priv, ciphertext) if err != nil { return nil, err @@ -143,13 +175,26 @@ func decryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (valid return } - c := new(big.Int).SetBytes(ciphertext) - m, err := decrypt(rand, priv, c) - if err != nil { - return + if boring.Enabled { + var bkey *boring.PrivateKeyRSA + bkey, err = boringPrivateKey(priv) + if err != nil { + return + } + em, err = boring.DecryptRSANoPadding(bkey, ciphertext) + if err != nil { + return + } + } else { + c := new(big.Int).SetBytes(ciphertext) + var m *big.Int + m, err = decrypt(rand, priv, c) + if err != nil { + return + } + em = m.FillBytes(make([]byte, k)) } - em = m.FillBytes(make([]byte, k)) firstByteIsZero := subtle.ConstantTimeByteEq(em[0], 0) secondByteIsTwo := subtle.ConstantTimeByteEq(em[1], 2) @@ -228,7 +273,7 @@ var hashPrefixes = map[crypto.Hash][]byte{ // messages is small, an attacker may be able to build a map from // messages to signatures and identify the signed messages. As ever, // signatures provide authenticity, not confidentiality. -func SignPKCS1v15(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) { +func SignPKCS1v15(random io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) { hashLen, prefix, err := pkcs1v15HashInfo(hash, len(hashed)) if err != nil { return nil, err @@ -240,6 +285,14 @@ func SignPKCS1v15(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []b return nil, ErrMessageTooLong } + if boring.Enabled { + bkey, err := boringPrivateKey(priv) + if err != nil { + return nil, err + } + return boring.SignRSAPKCS1v15(bkey, hash, hashed) + } + // EM = 0x00 || 0x01 || PS || 0x00 || T em := make([]byte, k) em[1] = 1 @@ -250,7 +303,7 @@ func SignPKCS1v15(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []b copy(em[k-hashLen:k], hashed) m := new(big.Int).SetBytes(em) - c, err := decryptAndCheck(rand, priv, m) + c, err := decryptAndCheck(random, priv, m) if err != nil { return nil, err } @@ -264,6 +317,17 @@ func SignPKCS1v15(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []b // returning a nil error. If hash is zero then hashed is used directly. This // isn't advisable except for interoperability. func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) error { + if boring.Enabled { + bkey, err := boringPublicKey(pub) + if err != nil { + return err + } + if err := boring.VerifyRSAPKCS1v15(bkey, hash, hashed, sig); err != nil { + return ErrVerification + } + return nil + } + hashLen, prefix, err := pkcs1v15HashInfo(hash, len(hashed)) if err != nil { return err diff --git a/src/crypto/rsa/pkcs1v15_test.go b/src/crypto/rsa/pkcs1v15_test.go index 26b8c5f26f..c5d825b42a 100644 --- a/src/crypto/rsa/pkcs1v15_test.go +++ b/src/crypto/rsa/pkcs1v15_test.go @@ -65,7 +65,7 @@ func TestDecryptPKCS1v15(t *testing.T) { for i, test := range decryptPKCS1v15Tests { out, err := decryptFunc(decodeBase64(test.in)) if err != nil { - t.Errorf("#%d error decrypting", i) + t.Errorf("#%d error decrypting: %v", i, err) } want := []byte(test.out) if !bytes.Equal(out, want) { diff --git a/src/crypto/rsa/pss.go b/src/crypto/rsa/pss.go index b2adbedb28..cda1eafd6e 100644 --- a/src/crypto/rsa/pss.go +++ b/src/crypto/rsa/pss.go @@ -15,6 +15,8 @@ import ( "math/big" ) +import "crypto/internal/boring" + // Per RFC 8017, Section 9.1 // // EM = MGF1 xor DB || H( 8*0x00 || mHash || salt ) || 0xbc @@ -213,6 +215,21 @@ func signPSSWithSalt(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed, if err != nil { return nil, err } + + if boring.Enabled { + bkey, err := boringPrivateKey(priv) + if err != nil { + return nil, err + } + // Note: BoringCrypto takes care of the "AndCheck" part of "decryptAndCheck". + // (It's not just decrypt.) + s, err := boring.DecryptRSANoPadding(bkey, em) + if err != nil { + return nil, err + } + return s, nil + } + m := new(big.Int).SetBytes(em) c, err := decryptAndCheck(rand, priv, m) if err != nil { @@ -274,6 +291,14 @@ func SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, digest []byte, saltLength = hash.Size() } + if boring.Enabled && rand == boring.RandReader { + bkey, err := boringPrivateKey(priv) + if err != nil { + return nil, err + } + return boring.SignRSAPSS(bkey, hash, digest, saltLength) + } + salt := make([]byte, saltLength) if _, err := io.ReadFull(rand, salt); err != nil { return nil, err @@ -288,6 +313,16 @@ func SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, digest []byte, // argument may be nil, in which case sensible defaults are used. opts.Hash is // ignored. func VerifyPSS(pub *PublicKey, hash crypto.Hash, digest []byte, sig []byte, opts *PSSOptions) error { + if boring.Enabled { + bkey, err := boringPublicKey(pub) + if err != nil { + return err + } + if err := boring.VerifyRSAPSS(bkey, hash, digest, sig, opts.saltLength()); err != nil { + return ErrVerification + } + return nil + } if len(sig) != pub.Size() { return ErrVerification } diff --git a/src/crypto/rsa/pss_test.go b/src/crypto/rsa/pss_test.go index dfa8d8bb5a..32f6f0c3aa 100644 --- a/src/crypto/rsa/pss_test.go +++ b/src/crypto/rsa/pss_test.go @@ -9,7 +9,6 @@ import ( "bytes" "compress/bzip2" "crypto" - _ "crypto/md5" "crypto/rand" "crypto/sha1" _ "crypto/sha256" @@ -211,7 +210,7 @@ func TestPSSSigning(t *testing.T) { {8, 8, true}, } - hash := crypto.MD5 + hash := crypto.SHA1 h := hash.New() h.Write([]byte("testing")) hashed := h.Sum(nil) diff --git a/src/crypto/rsa/rsa.go b/src/crypto/rsa/rsa.go index 178ade666a..eece385981 100644 --- a/src/crypto/rsa/rsa.go +++ b/src/crypto/rsa/rsa.go @@ -35,6 +35,11 @@ import ( "crypto/internal/randutil" ) +import ( + "crypto/internal/boring" + "unsafe" +) + var bigZero = big.NewInt(0) var bigOne = big.NewInt(1) @@ -42,6 +47,8 @@ var bigOne = big.NewInt(1) type PublicKey struct { N *big.Int // modulus E int // public exponent + + boring unsafe.Pointer } // Any methods implemented on PublicKey might need to also be implemented on @@ -105,6 +112,8 @@ type PrivateKey struct { // Precomputed contains precomputed values that speed up private // operations, if available. Precomputed PrecomputedValues + + boring unsafe.Pointer } // Public returns the public key corresponding to priv. @@ -256,6 +265,32 @@ func GenerateKey(random io.Reader, bits int) (*PrivateKey, error) { func GenerateMultiPrimeKey(random io.Reader, nprimes int, bits int) (*PrivateKey, error) { randutil.MaybeReadByte(random) + if boring.Enabled && random == boring.RandReader && nprimes == 2 && (bits == 2048 || bits == 3072) { + N, E, D, P, Q, Dp, Dq, Qinv, err := boring.GenerateKeyRSA(bits) + if err != nil { + return nil, err + } + e64 := E.Int64() + if !E.IsInt64() || int64(int(e64)) != e64 { + return nil, errors.New("crypto/rsa: generated key exponent too large") + } + key := &PrivateKey{ + PublicKey: PublicKey{ + N: N, + E: int(e64), + }, + D: D, + Primes: []*big.Int{P, Q}, + Precomputed: PrecomputedValues{ + Dp: Dp, + Dq: Dq, + Qinv: Qinv, + CRTValues: make([]CRTValue, 0), // non-nil, to match Precompute + }, + } + return key, nil + } + priv := new(PrivateKey) priv.E = 65537 @@ -385,6 +420,7 @@ func mgf1XOR(out []byte, hash hash.Hash, seed []byte) { var ErrMessageTooLong = errors.New("crypto/rsa: message too long for RSA public key size") func encrypt(c *big.Int, pub *PublicKey, m *big.Int) *big.Int { + boring.Unreachable() e := big.NewInt(int64(pub.E)) c.Exp(m, e, pub.N) return c @@ -417,6 +453,15 @@ func EncryptOAEP(hash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, l return nil, ErrMessageTooLong } + if boring.Enabled && random == boring.RandReader { + bkey, err := boringPublicKey(pub) + if err != nil { + return nil, err + } + return boring.EncryptRSAOAEP(hash, bkey, msg, label) + } + boring.UnreachableExceptTests() + hash.Write(label) lHash := hash.Sum(nil) hash.Reset() @@ -437,6 +482,15 @@ func EncryptOAEP(hash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, l mgf1XOR(db, hash, seed) mgf1XOR(seed, hash, db) + if boring.Enabled { + var bkey *boring.PublicKeyRSA + bkey, err = boringPublicKey(pub) + if err != nil { + return nil, err + } + return boring.EncryptRSANoPadding(bkey, em) + } + m := new(big.Int) m.SetBytes(em) c := encrypt(new(big.Int), pub, m) @@ -487,6 +541,9 @@ func (priv *PrivateKey) Precompute() { // decrypt performs an RSA decryption, resulting in a plaintext integer. If a // random source is given, RSA blinding is used. func decrypt(random io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int, err error) { + if len(priv.Primes) <= 2 { + boring.Unreachable() + } // TODO(agl): can we get away with reusing blinds? if c.Cmp(priv.N) > 0 { err = ErrDecryption @@ -603,6 +660,17 @@ func DecryptOAEP(hash hash.Hash, random io.Reader, priv *PrivateKey, ciphertext return nil, ErrDecryption } + if boring.Enabled { + bkey, err := boringPrivateKey(priv) + if err != nil { + return nil, err + } + out, err := boring.DecryptRSAOAEP(hash, bkey, ciphertext, label) + if err != nil { + return nil, ErrDecryption + } + return out, nil + } c := new(big.Int).SetBytes(ciphertext) m, err := decrypt(random, priv, c) diff --git a/src/crypto/rsa/rsa_test.go b/src/crypto/rsa/rsa_test.go index 84b167455f..766d9a954f 100644 --- a/src/crypto/rsa/rsa_test.go +++ b/src/crypto/rsa/rsa_test.go @@ -10,23 +10,27 @@ import ( "crypto/rand" "crypto/sha1" "crypto/sha256" + "fmt" "math/big" "testing" ) +import "crypto/internal/boring" + func TestKeyGeneration(t *testing.T) { - size := 1024 - if testing.Short() { - size = 128 - } - priv, err := GenerateKey(rand.Reader, size) - if err != nil { - t.Errorf("failed to generate key") - } - if bits := priv.N.BitLen(); bits != size { - t.Errorf("key too short (%d vs %d)", bits, size) + for _, size := range []int{128, 1024, 2048, 3072} { + priv, err := GenerateKey(rand.Reader, size) + if err != nil { + t.Errorf("GenerateKey(%d): %v", size, err) + } + if bits := priv.N.BitLen(); bits != size { + t.Errorf("key too short (%d vs %d)", bits, size) + } + testKeyBasics(t, priv) + if testing.Short() { + break + } } - testKeyBasics(t, priv) } func Test3PrimeKeyGeneration(t *testing.T) { @@ -110,6 +114,25 @@ func testKeyBasics(t *testing.T, priv *PrivateKey) { t.Errorf("private exponent too large") } + if boring.Enabled { + // Cannot call encrypt/decrypt directly. Test via PKCS1v15. + msg := []byte("hi!") + enc, err := EncryptPKCS1v15(rand.Reader, &priv.PublicKey, msg) + if err != nil { + t.Errorf("EncryptPKCS1v15: %v", err) + return + } + dec, err := DecryptPKCS1v15(rand.Reader, priv, enc) + if err != nil { + t.Errorf("DecryptPKCS1v15: %v", err) + return + } + if !bytes.Equal(dec, msg) { + t.Errorf("got:%x want:%x (%+v)", dec, msg, priv) + } + return + } + pub := &priv.PublicKey m := big.NewInt(42) c := encrypt(new(big.Int), pub, m) @@ -158,6 +181,10 @@ func init() { } func BenchmarkRSA2048Decrypt(b *testing.B) { + if boring.Enabled { + b.Skip("no raw decrypt in BoringCrypto") + } + b.StopTimer() c := fromBase10("8472002792838218989464636159316973636630013835787202418124758118372358261975764365740026024610403138425986214991379012696600761514742817632790916315594342398720903716529235119816755589383377471752116975374952783629225022962092351886861518911824745188989071172097120352727368980275252089141512321893536744324822590480751098257559766328893767334861211872318961900897793874075248286439689249972315699410830094164386544311554704755110361048571142336148077772023880664786019636334369759624917224888206329520528064315309519262325023881707530002540634660750469137117568199824615333883758410040459705787022909848740188613313") @@ -180,6 +207,10 @@ func BenchmarkRSA2048Sign(b *testing.B) { } func Benchmark3PrimeRSA2048Decrypt(b *testing.B) { + if boring.Enabled { + b.Skip("no raw decrypt in BoringCrypto") + } + b.StopTimer() priv := &PrivateKey{ PublicKey: PublicKey{ @@ -222,7 +253,7 @@ func TestEncryptOAEP(t *testing.T) { n := new(big.Int) for i, test := range testEncryptOAEPData { n.SetString(test.modulus, 16) - public := PublicKey{n, test.e} + public := PublicKey{N: n, E: test.e} for j, message := range test.msgs { randomSource := bytes.NewReader(message.seed) @@ -247,7 +278,7 @@ func TestDecryptOAEP(t *testing.T) { n.SetString(test.modulus, 16) d.SetString(test.d, 16) private := new(PrivateKey) - private.PublicKey = PublicKey{n, test.e} + private.PublicKey = PublicKey{N: n, E: test.e} private.D = d for j, message := range test.msgs { @@ -272,6 +303,36 @@ func TestDecryptOAEP(t *testing.T) { } } +func TestEncryptDecryptOAEP(t *testing.T) { + sha256 := sha256.New() + n := new(big.Int) + d := new(big.Int) + for i, test := range testEncryptOAEPData { + n.SetString(test.modulus, 16) + d.SetString(test.d, 16) + priv := new(PrivateKey) + priv.PublicKey = PublicKey{N: n, E: test.e} + priv.D = d + + for j, message := range test.msgs { + label := []byte(fmt.Sprintf("hi#%d", j)) + enc, err := EncryptOAEP(sha256, rand.Reader, &priv.PublicKey, message.in, label) + if err != nil { + t.Errorf("#%d,%d: EncryptOAEP: %v", i, j, err) + continue + } + dec, err := DecryptOAEP(sha256, rand.Reader, priv, enc, label) + if err != nil { + t.Errorf("#%d,%d: DecryptOAEP: %v", i, j, err) + continue + } + if !bytes.Equal(dec, message.in) { + t.Errorf("#%d,%d: round trip %q -> %q", i, j, message.in, dec) + } + } + } +} + // testEncryptOAEPData contains a subset of the vectors from RSA's "Test vectors for RSA-OAEP". var testEncryptOAEPData = []testEncryptOAEPStruct{ // Key 1 |