diff options
author | Russ Cox <rsc@golang.org> | 2017-09-12 16:19:24 -0400 |
---|---|---|
committer | Russ Cox <rsc@golang.org> | 2017-09-18 00:26:15 +0000 |
commit | c9e2d9eb06d2c57cb2a78707fb60a639a94efb42 (patch) | |
tree | c2a9285f4c6ad710ebed6f3568965b5d5ac9d87d /src/crypto/rsa/boring_test.go | |
parent | e773ea9aa33a574796c256930870af9e84dbfd5a (diff) | |
download | go-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.go | 220 |
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) +} |