aboutsummaryrefslogtreecommitdiff
path: root/src/crypto/rsa/boring_test.go
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2017-09-12 16:19:24 -0400
committerRuss Cox <rsc@golang.org>2017-09-18 00:26:15 +0000
commitc9e2d9eb06d2c57cb2a78707fb60a639a94efb42 (patch)
treec2a9285f4c6ad710ebed6f3568965b5d5ac9d87d /src/crypto/rsa/boring_test.go
parente773ea9aa33a574796c256930870af9e84dbfd5a (diff)
downloadgo-c9e2d9eb06d2c57cb2a78707fb60a639a94efb42.tar.gz
go-c9e2d9eb06d2c57cb2a78707fb60a639a94efb42.zip
[dev.boringcrypto] crypto/rsa: add test for, fix observable reads from custom randomness
In routines like GenerateKey, where bits from the randomness source have a visible effect on the output, we bypass BoringCrypto if given a non-standard randomness source (and also assert that this happens only during tests). In the decryption paths, the randomness source is only for blinding and has no effect on the output, so we unconditionally invoke BoringCrypto, letting it use its own randomness source as it sees fit. This in turn lets us verify that the non-BoringCrypto decryption function is never called, not even in tests. Unfortunately, while the randomness source has no visible effect on the decrypt operation, the decrypt operation does have a visible effect on the randomness source. If decryption doesn't use the randomness source, and it's a synthetic stream, then a future operation will read a different position in the stream and may produce different output. This happens in tests more often than you'd hope. To keep behavior of those future operations unchanged while still ensuring that the original decrypt is never called, this CL adds a simulation of the blinding preparation, to discard the right amount from the random source before invoking BoringCrypto. Change-Id: If2f87b856c811b59b536187c93efa99a97721419 Reviewed-on: https://go-review.googlesource.com/63912 Reviewed-by: Adam Langley <agl@golang.org>
Diffstat (limited to 'src/crypto/rsa/boring_test.go')
-rw-r--r--src/crypto/rsa/boring_test.go220
1 files changed, 220 insertions, 0 deletions
diff --git a/src/crypto/rsa/boring_test.go b/src/crypto/rsa/boring_test.go
index 290fe10a79..0b19e92d74 100644
--- a/src/crypto/rsa/boring_test.go
+++ b/src/crypto/rsa/boring_test.go
@@ -2,13 +2,21 @@
// 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 (
+ "bytes"
"crypto"
"crypto/rand"
+ "crypto/sha1"
+ "crypto/sha256"
"encoding/asn1"
+ "encoding/hex"
"reflect"
+ "sync"
"testing"
"unsafe"
)
@@ -65,3 +73,215 @@ func TestBoringVerify(t *testing.T) {
t.Errorf("sha1: %v", err)
}
}
+
+// The goal for BoringCrypto is to be indistinguishable from standard Go crypto.
+// Test that when routines are passed a not-actually-random reader, they
+// consume and potentially expose the expected bits from that reader.
+// This is awful but it makes sure that golden tests based on deterministic
+// "randomness" sources are unchanged by BoringCrypto.
+//
+// For decryption and signing, r is only used for blinding,
+// so we can and do still use BoringCrypto with its own true
+// randomness source, but we must be careful to consume
+// from r as if we'd used it for blinding.
+
+type testRandReader struct {
+ t *testing.T
+ offset int64
+ seq [8]byte
+ data []byte
+ buf [32]byte
+}
+
+func (r *testRandReader) Read(b []byte) (int, error) {
+ if len(r.data) == 0 && len(b) > 0 {
+ for i := range r.seq {
+ r.seq[i]++
+ if r.seq[i] != 0 {
+ break
+ }
+ }
+ r.buf = sha256.Sum256(r.seq[:])
+ r.data = r.buf[:]
+ }
+ n := copy(b, r.data)
+ r.data = r.data[n:]
+ r.offset += int64(n)
+ return n, nil
+}
+
+func (r *testRandReader) checkOffset(offset int64) {
+ r.t.Helper()
+ if r.offset != offset {
+ r.t.Fatalf("r.offset = %d, expected %d", r.offset, offset)
+ }
+}
+
+func testRand(t *testing.T) *testRandReader {
+ return &testRandReader{t: t}
+}
+
+var testKeyCache struct {
+ once sync.Once
+ k *PrivateKey
+}
+
+func testKey(t *testing.T) *PrivateKey {
+ testKeyCache.once.Do(func() {
+ // Note: Key must be 2048 bits in order to trigger
+ // BoringCrypto code paths.
+ k, err := GenerateKey(testRand(t), 2048)
+ if err != nil {
+ t.Fatal(err)
+ }
+ testKeyCache.k = k
+ })
+ return testKeyCache.k
+}
+
+func bytesFromHex(t *testing.T, x string) []byte {
+ b, err := hex.DecodeString(x)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return b
+}
+
+func TestBoringRandGenerateKey(t *testing.T) {
+ r := testRand(t)
+ k, err := GenerateKey(r, 2048) // 2048 is smallest size BoringCrypto might kick in for
+ if err != nil {
+ t.Fatal(err)
+ }
+ n := bigFromHex("b2e9c4c8b1c0f03ba6994fe1e715a3e598f0571f4676da420615b7b997d431ea7535ceb98e6b52172fe0d2fccfc5f696d1b34144f7d19d85633fcbf56daff805a66457b360b1b0f40ec18fb83f4c9b86f1b5fe26b209cdfff26911a95047df797210969693226423915c9be53ff1c06f86fe2d228273ef25970b90a3c70979f9d68458d5dd38f6700436f7cd5939c04be3e1f2ff52272513171540a685c9e8c8e20694e529cc3e0cc13d2fb91ac499d44b920a03e42be89a15e7ca73c29f2e2a1a8a7d9be57516ccb95e878db6ce6096e386a793cccc19eba15a37cc0f1234b7a25ee7c87569bc74c7ef3d6ad8d84a5ddb1e8901ae593f945523fe5e0ed451a5")
+ if k.N.Cmp(n) != 0 {
+ t.Fatalf("GenerateKey: wrong N\nhave %x\nwant %x", k.N, n)
+ }
+ r.checkOffset(35200)
+}
+
+func TestBoringRandGenerateMultiPrimeKey(t *testing.T) {
+ r := testRand(t)
+ k, err := GenerateMultiPrimeKey(r, 2, 2048)
+ if err != nil {
+ t.Fatal(err)
+ }
+ n := bigFromHex("b2e9c4c8b1c0f03ba6994fe1e715a3e598f0571f4676da420615b7b997d431ea7535ceb98e6b52172fe0d2fccfc5f696d1b34144f7d19d85633fcbf56daff805a66457b360b1b0f40ec18fb83f4c9b86f1b5fe26b209cdfff26911a95047df797210969693226423915c9be53ff1c06f86fe2d228273ef25970b90a3c70979f9d68458d5dd38f6700436f7cd5939c04be3e1f2ff52272513171540a685c9e8c8e20694e529cc3e0cc13d2fb91ac499d44b920a03e42be89a15e7ca73c29f2e2a1a8a7d9be57516ccb95e878db6ce6096e386a793cccc19eba15a37cc0f1234b7a25ee7c87569bc74c7ef3d6ad8d84a5ddb1e8901ae593f945523fe5e0ed451a5")
+ if k.N.Cmp(n) != 0 {
+ t.Fatalf("GenerateKey: wrong N\nhave %x\nwant %x", k.N, n)
+ }
+ r.checkOffset(35200)
+}
+
+func TestBoringRandEncryptPKCS1v15(t *testing.T) {
+ r := testRand(t)
+ k := testKey(t)
+ enc, err := EncryptPKCS1v15(r, &k.PublicKey, []byte("hello world"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ want := bytesFromHex(t, "a8c8c0d248e669942a140c1184e1112afbf794b7427d9ac966bd2dbb4c05a2fee76f311f7feec743b8a8715e34bf741b0d0c4226559daf4de258ff712178e3f25fecb7d3eee90251e8ae4b4b7b907cd2763948cc9da34ce83c69934b523830545a536c1ba4d3740f4687e877acee9c768bcd8e88d472ba5d905493121f4830d95dcea36ef0f1223ffb0a9008eddfc53aca36877328924a2c631dce4b67e745564301fe51ab2c768b39e525bda1e1a08e029b58c53a0b92285f734592d2deebda957bcfd29c697aee263fce5c5023c7d3495b6a9114a8ac691aa661721cf45973b68678bb1e15d6605b9040951163d5b6df0d7f0b20dcefa251a7a8947a090f4b")
+ if !bytes.Equal(enc, want) {
+ t.Fatalf("EncryptPKCS1v15: wrong enc\nhave %x\nwant %x", enc, want)
+ }
+ r.checkOffset(242)
+}
+
+func TestBoringRandDecryptPKCS1v15(t *testing.T) {
+ r := testRand(t)
+ k := testKey(t)
+ enc := bytesFromHex(t, "a8c8c0d248e669942a140c1184e1112afbf794b7427d9ac966bd2dbb4c05a2fee76f311f7feec743b8a8715e34bf741b0d0c4226559daf4de258ff712178e3f25fecb7d3eee90251e8ae4b4b7b907cd2763948cc9da34ce83c69934b523830545a536c1ba4d3740f4687e877acee9c768bcd8e88d472ba5d905493121f4830d95dcea36ef0f1223ffb0a9008eddfc53aca36877328924a2c631dce4b67e745564301fe51ab2c768b39e525bda1e1a08e029b58c53a0b92285f734592d2deebda957bcfd29c697aee263fce5c5023c7d3495b6a9114a8ac691aa661721cf45973b68678bb1e15d6605b9040951163d5b6df0d7f0b20dcefa251a7a8947a090f4b")
+ dec, err := DecryptPKCS1v15(r, k, enc)
+ if err != nil {
+ t.Fatal(err)
+ }
+ want := []byte("hello world")
+ if !bytes.Equal(dec, want) {
+ t.Fatalf("DecryptPKCS1v15: wrong dec\nhave %x\nwant %x", dec, want)
+ }
+ r.checkOffset(256)
+}
+
+func TestBoringRandDecryptPKCS1v15SessionKey(t *testing.T) {
+ r := testRand(t)
+ k := testKey(t)
+ enc := bytesFromHex(t, "a8c8c0d248e669942a140c1184e1112afbf794b7427d9ac966bd2dbb4c05a2fee76f311f7feec743b8a8715e34bf741b0d0c4226559daf4de258ff712178e3f25fecb7d3eee90251e8ae4b4b7b907cd2763948cc9da34ce83c69934b523830545a536c1ba4d3740f4687e877acee9c768bcd8e88d472ba5d905493121f4830d95dcea36ef0f1223ffb0a9008eddfc53aca36877328924a2c631dce4b67e745564301fe51ab2c768b39e525bda1e1a08e029b58c53a0b92285f734592d2deebda957bcfd29c697aee263fce5c5023c7d3495b6a9114a8ac691aa661721cf45973b68678bb1e15d6605b9040951163d5b6df0d7f0b20dcefa251a7a8947a090f4b")
+ dec := make([]byte, 11)
+ err := DecryptPKCS1v15SessionKey(r, k, enc, dec)
+ if err != nil {
+ t.Fatal(err)
+ }
+ want := []byte("hello world")
+ if !bytes.Equal(dec, want) {
+ t.Fatalf("DecryptPKCS1v15SessionKey: wrong dec\nhave %x\nwant %x", dec, want)
+ }
+ r.checkOffset(256)
+}
+
+func TestBoringRandSignPKCS1v15(t *testing.T) {
+ r := testRand(t)
+ k := testKey(t)
+ sum := sha1.Sum([]byte("hello"))
+ sig, err := SignPKCS1v15(r, k, crypto.SHA1, sum[:])
+ if err != nil {
+ t.Fatal(err)
+ }
+ want := bytesFromHex(t, "4a8da3c0c41af2b8a93d011d4e11f4da9b2d52641c6c3d78d863987e857295adcedfae0e0d3ec00352bd134dc3fbb93b23a1fbe3718775762d78165bbbd37c6ef8e07bfa44e16ed2f1b05ebc04ba7bd60162d8689edb8709349e06bc281d34c2a3ee75d3454bfd95053cbb27c10515fb9132290a6ecc858e0c003201a9e100aac7f66af967364a1176e4ed9ef672d41481c59580f98bb82f205f712153fd5e3035a811da9d6e56e50609d1d604857f6d8e958bb84f354cfa28e0b8bcbb1261f929382d431454f07cbf60c18ff1243b11c6b552f3a0aa7e936f45cded40688ee53b1b630f944139f4f51baae49cd039b57b2b82f58f5589335137f4b09bd315f5")
+ if !bytes.Equal(sig, want) {
+ t.Fatalf("SignPKCS1v15(hash=SHA1): wrong sig\nhave %x\nwant %x", sig, want)
+ }
+
+ sig, err = SignPKCS1v15(r, k, 0, sum[:])
+ if err != nil {
+ t.Fatal(err)
+ }
+ want = bytesFromHex(t, "5d3d34495ffade926adab2de0545aaf1f22a03def949b69e1c91d34a2f0c7f2d682af46034151a1b67aa22cb9c1a8cc24c1358fce9ac6a2141879bbe107371b14faa97b12494260d9602ed1355f22ab3495b0bb7c137bc6801c1113fc2bdc00d4c250bbd8fa17e4ff86f71544b30a78e9d62c0b949afd1159760282c2700ec8be24cd884efd585ec55b45506d90e66cc3c5911baaea961e6c4e8018c4b4feb04afdd71880e3d8eff120288e53289a1bfb9fe7a3b9aca1d4549f133063647bfd4c6f4c0f4038f1bbcb4d112aa601f1b15402595076adfdbefb1bb64d3193bafb0305145bb536cd949a03ebe0470c6a155369f784afab2e25e9d5c03d8e13dcf1a")
+ if !bytes.Equal(sig, want) {
+ t.Fatalf("SignPKCS1v15(hash=0): wrong sig\nhave %x\nwant %x", sig, want)
+ }
+ r.checkOffset(768)
+}
+
+func TestBoringRandSignPSS(t *testing.T) {
+ r := testRand(t)
+ k := testKey(t)
+ sum := sha1.Sum([]byte("hello"))
+ sig, err := SignPSS(r, k, crypto.SHA1, sum[:], nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ want := bytesFromHex(t, "a0de84c9654c2e78e33c899090f8dc0590046fda4ee29d133340800596401ae0df61bf8aa5689df3f873ad13cf55df5209c3a8c6450918b74c2017f87c2d588809740622c7752e3153a26d04bd3e9d9f6daa676e8e5e65a8a11d4fbd271d4693ab6a303652328dc1c923b484fa179fd6d9e8b523da74f3a307531c0dd75f243a041f7df22414dfdb83b3a241fe73e7af0f95cb6b60831bdd46dc05618e5cb3653476eb7d5405fa5ca98dad8f787ca86179055f305daa87eb424671878a93965e47d3002e2774be311d696b42e5691eddb2f788cd35246b408eb5d045c891ba1d57ce4c6fc935ceec90f7999406252f6266957cce4e7f12cf0ec94af358aeefa7")
+ if !bytes.Equal(sig, want) {
+ t.Fatalf("SignPSS: wrong sig\nhave %x\nwant %x", sig, want)
+ }
+ r.checkOffset(490)
+}
+
+func TestBoringRandEncryptOAEP(t *testing.T) {
+ r := testRand(t)
+ k := testKey(t)
+ enc, err := EncryptOAEP(sha256.New(), r, &k.PublicKey, []byte("hello"), []byte("label"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ want := bytesFromHex(t, "55dc7b590a511c2d249232ecbb70040e8e0ec03206caae5ec0a401a0ad8013209ef546870f93d0946b9845ace092d456d092403f76f12ee65c2b8759731a25589d8a7e857407d09cfbe36ae36fc4daeb514ac597b1de2f7dc8450ab78a9e420c9b5dbbae3e402c8f378bd35505a47d556b705ab8985707a22e3583c172ef5730f05fd0845880d67c1ddd3c1525aa4c2c4e162bd6435a485609f6bd76c8ff73a7b5d043e4724458594703245fabdb479ef2786c757b35932a645399f2703647785b59b971970e6bccef3e6cd6fae39f9f135203eb104f0db20cf48e461cb7d824889c0d5d6a47cd0bf213c2f7acb3ddbd3effefebb4f60458ffc8b6ff1e4cc447")
+ if !bytes.Equal(enc, want) {
+ t.Fatalf("EncryptOAEP: wrong enc\nhave %x\nwant %x", enc, want)
+ }
+ r.checkOffset(32)
+}
+
+func TestBoringRandDecryptOAEP(t *testing.T) {
+ r := testRand(t)
+ k := testKey(t)
+ enc := bytesFromHex(t, "55dc7b590a511c2d249232ecbb70040e8e0ec03206caae5ec0a401a0ad8013209ef546870f93d0946b9845ace092d456d092403f76f12ee65c2b8759731a25589d8a7e857407d09cfbe36ae36fc4daeb514ac597b1de2f7dc8450ab78a9e420c9b5dbbae3e402c8f378bd35505a47d556b705ab8985707a22e3583c172ef5730f05fd0845880d67c1ddd3c1525aa4c2c4e162bd6435a485609f6bd76c8ff73a7b5d043e4724458594703245fabdb479ef2786c757b35932a645399f2703647785b59b971970e6bccef3e6cd6fae39f9f135203eb104f0db20cf48e461cb7d824889c0d5d6a47cd0bf213c2f7acb3ddbd3effefebb4f60458ffc8b6ff1e4cc447")
+ dec, err := DecryptOAEP(sha256.New(), r, k, enc, []byte("label"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ want := []byte("hello")
+ if !bytes.Equal(dec, want) {
+ t.Fatalf("DecryptOAEP: wrong dec\nhave %x\nwant %x", dec, want)
+ }
+ r.checkOffset(256)
+}