blob: f5ef43397cfeb779e3c91a4013fcd1a5291d6649 [file] [log] [blame]
// Copyright 2015 The Vanadium 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 crypto
import (
"bytes"
"crypto/rand"
"net"
"testing"
"testing/quick"
"v.io/x/ref/profiles/internal/lib/iobuf"
)
func quickTest(t *testing.T, e Encrypter, d Decrypter) {
f := func(plaintext []byte) bool {
plainslice := iobuf.NewSlice(plaintext)
cipherslice, err := e.Encrypt(plainslice)
if err != nil {
t.Error(err)
return false
}
plainslice, err = d.Decrypt(cipherslice)
if err != nil {
t.Error(err)
return false
}
defer plainslice.Release()
return bytes.Equal(plainslice.Contents, plaintext)
}
if err := quick.Check(f, nil); err != nil {
t.Error(err)
}
}
func TestNull(t *testing.T) {
crypter := NewNullCrypter()
quickTest(t, crypter, crypter)
crypter.String() // Only to test that String does not crash.
}
func testSimple(t *testing.T, c1, c2 Crypter) {
// Execute String just to check that it does not crash.
c1.String()
c2.String()
if t.Failed() {
return
}
quickTest(t, c1, c2)
quickTest(t, c2, c1)
// Log the byte overhead of encryption, just so that test output has a
// record.
var overhead [10]int
for i := 0; i < len(overhead); i++ {
size := 1 << uint(i)
slice, err := c1.Encrypt(iobuf.NewSlice(make([]byte, size)))
overhead[i] = slice.Size() - size
slice.Release()
if err != nil {
t.Fatalf("%d: %v", i, err)
}
}
t.Logf("Byte overhead of encryption: %v", overhead)
}
func TestTLS(t *testing.T) {
server, client := net.Pipe()
c1, c2 := tlsCrypters(t, server, client)
testSimple(t, c1, c2)
}
func TestBox(t *testing.T) {
c1, c2 := boxCrypters(t, nil, nil)
testSimple(t, c1, c2)
}
// testChannelBinding attempts to ensure that:
// (a) ChannelBinding returns the same value for both ends of a Crypter
// (b) ChannelBindings are unique
// For (b), we simply test many times and check that no two instances have the same ChannelBinding value.
// Yes, this test isn't exhaustive. If you have ideas, please share.
func testChannelBinding(t *testing.T, factory func(testing.TB, net.Conn, net.Conn) (Crypter, Crypter)) {
values := make([][]byte, 100)
for i := 0; i < len(values); i++ {
conn1, conn2 := net.Pipe()
c1, c2 := factory(t, conn1, conn2)
if !bytes.Equal(c1.ChannelBinding(), c2.ChannelBinding()) {
t.Fatalf("Two ends of the crypter ended up with different channel bindings (iteration #%d)", i)
}
values[i] = c1.ChannelBinding()
}
for i := 0; i < len(values); i++ {
for j := i + 1; j < len(values); j++ {
if bytes.Equal(values[i], values[j]) {
t.Fatalf("Same ChannelBinding seen on multiple channels (%d and %d)", i, j)
}
}
}
}
func TestChannelBindingTLS(t *testing.T) { testChannelBinding(t, tlsCrypters) }
func TestChannelBindingBox(t *testing.T) { testChannelBinding(t, boxCrypters) }
func TestTLSNil(t *testing.T) {
conn1, conn2 := net.Pipe()
c1, c2 := tlsCrypters(t, conn1, conn2)
if t.Failed() {
return
}
cipher, err := c1.Encrypt(iobuf.NewSlice(nil))
if err != nil {
t.Fatal(err)
}
plain, err := c2.Decrypt(cipher)
if err != nil {
t.Fatal(err)
}
if plain.Size() != 0 {
t.Fatalf("Decryption produced non-empty data (%d)", plain.Size())
}
}
func TestTLSFragmentedPlaintext(t *testing.T) {
// Form RFC 5246, Section 6.2.1, the maximum length of a TLS record is
// 16K (it is represented by a uint16).
// http://tools.ietf.org/html/rfc5246#section-6.2.1
const dataLen = 16384 + 1
conn1, conn2 := net.Pipe()
enc, dec := tlsCrypters(t, conn1, conn2)
cipher, err := enc.Encrypt(iobuf.NewSlice(make([]byte, dataLen)))
if err != nil {
t.Fatal(err)
}
plain, err := dec.Decrypt(cipher)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(plain.Contents, make([]byte, dataLen)) {
t.Errorf("Got %d bytes, want %d bytes of zeroes", plain.Size(), dataLen)
}
}
type factory func(t testing.TB, server, client net.Conn) (Crypter, Crypter)
func tlsCrypters(t testing.TB, serverConn, clientConn net.Conn) (Crypter, Crypter) {
crypters := make(chan Crypter)
go func() {
server, err := NewTLSServer(serverConn, serverConn.LocalAddr(), serverConn.RemoteAddr(), iobuf.NewPool(0))
if err != nil {
t.Fatal(err)
}
crypters <- server
}()
go func() {
client, err := NewTLSClient(clientConn, clientConn.LocalAddr(), clientConn.RemoteAddr(), TLSClientSessionCache{}, iobuf.NewPool(0))
if err != nil {
t.Fatal(err)
}
crypters <- client
}()
c1 := <-crypters
c2 := <-crypters
return c1, c2
}
func boxCrypters(t testing.TB, _, _ net.Conn) (Crypter, Crypter) {
server, client := make(chan *BoxKey, 1), make(chan *BoxKey, 1)
clientExchanger := func(pubKey *BoxKey) (*BoxKey, error) {
client <- pubKey
return <-server, nil
}
serverExchanger := func(pubKey *BoxKey) (*BoxKey, error) {
server <- pubKey
return <-client, nil
}
crypters := make(chan Crypter)
for _, ex := range []BoxKeyExchanger{clientExchanger, serverExchanger} {
go func(exchanger BoxKeyExchanger) {
crypter, err := NewBoxCrypter(exchanger, iobuf.NewPool(0))
if err != nil {
t.Fatal(err)
}
crypters <- crypter
}(ex)
}
return <-crypters, <-crypters
}
func benchmarkEncrypt(b *testing.B, crypters factory, size int) {
plaintext := make([]byte, size)
if _, err := rand.Read(plaintext); err != nil {
b.Fatal(err)
}
conn1, conn2 := net.Pipe()
defer conn1.Close()
defer conn2.Close()
e, _ := crypters(b, conn1, conn2)
b.SetBytes(int64(size))
b.ResetTimer()
for i := 0; i < b.N; i++ {
cipher, err := e.Encrypt(iobuf.NewSlice(plaintext))
if err != nil {
b.Fatal(err)
}
cipher.Release()
}
}
func BenchmarkTLSEncrypt_1B(b *testing.B) { benchmarkEncrypt(b, tlsCrypters, 1) }
func BenchmarkTLSEncrypt_1K(b *testing.B) { benchmarkEncrypt(b, tlsCrypters, 1<<10) }
func BenchmarkTLSEncrypt_10K(b *testing.B) { benchmarkEncrypt(b, tlsCrypters, 10<<10) }
func BenchmarkTLSEncrypt_1M(b *testing.B) { benchmarkEncrypt(b, tlsCrypters, 1<<20) }
func BenchmarkTLSEncrypt_5M(b *testing.B) { benchmarkEncrypt(b, tlsCrypters, 5<<20) }
func BenchmarkBoxEncrypt_1B(b *testing.B) { benchmarkEncrypt(b, boxCrypters, 1) }
func BenchmarkBoxEncrypt_1K(b *testing.B) { benchmarkEncrypt(b, boxCrypters, 1<<10) }
func BenchmarkBoxEncrypt_10K(b *testing.B) { benchmarkEncrypt(b, boxCrypters, 10<<10) }
func BenchmarkBoxEncrypt_1M(b *testing.B) { benchmarkEncrypt(b, boxCrypters, 1<<20) }
func BenchmarkBoxEncrypt_5M(b *testing.B) { benchmarkEncrypt(b, boxCrypters, 5<<20) }
func benchmarkRoundTrip(b *testing.B, crypters factory, size int) {
plaintext := make([]byte, size)
if _, err := rand.Read(plaintext); err != nil {
b.Fatal(err)
}
conn1, conn2 := net.Pipe()
defer conn1.Close()
defer conn2.Close()
e, d := crypters(b, conn1, conn2)
b.SetBytes(int64(size))
b.ResetTimer()
for i := 0; i < b.N; i++ {
cipherslice, err := e.Encrypt(iobuf.NewSlice(plaintext))
if err != nil {
b.Fatal(err)
}
plainslice, err := d.Decrypt(cipherslice)
if err != nil {
b.Fatal(err)
}
plainslice.Release()
}
}
func BenchmarkTLSRoundTrip_1B(b *testing.B) { benchmarkRoundTrip(b, tlsCrypters, 1) }
func BenchmarkTLSRoundTrip_1K(b *testing.B) { benchmarkRoundTrip(b, tlsCrypters, 1<<10) }
func BenchmarkTLSRoundTrip_10K(b *testing.B) { benchmarkRoundTrip(b, tlsCrypters, 10<<10) }
func BenchmarkTLSRoundTrip_1M(b *testing.B) { benchmarkRoundTrip(b, tlsCrypters, 1<<20) }
func BenchmarkTLSRoundTrip_5M(b *testing.B) { benchmarkRoundTrip(b, tlsCrypters, 5<<20) }
func BenchmarkBoxRoundTrip_1B(b *testing.B) { benchmarkRoundTrip(b, boxCrypters, 1) }
func BenchmarkBoxRoundTrip_1K(b *testing.B) { benchmarkRoundTrip(b, boxCrypters, 1<<10) }
func BenchmarkBoxRoundTrip_10K(b *testing.B) { benchmarkRoundTrip(b, boxCrypters, 10<<10) }
func BenchmarkBoxRoundTrip_1M(b *testing.B) { benchmarkRoundTrip(b, boxCrypters, 1<<20) }
func BenchmarkBoxRoundTrip_5M(b *testing.B) { benchmarkRoundTrip(b, boxCrypters, 5<<20) }
func benchmarkSetup(b *testing.B, crypters factory) {
for i := 0; i < b.N; i++ {
conn1, conn2 := net.Pipe()
crypters(b, conn1, conn2)
conn1.Close()
conn2.Close()
}
}
func BenchmarkTLSSetup(b *testing.B) { benchmarkSetup(b, tlsCrypters) }
func BenchmarkBoxSetup(b *testing.B) { benchmarkSetup(b, boxCrypters) }