blob: 06f018fbee706485bdc54dfc3e4d34d40038b299 [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 serialization_test
import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"fmt"
"io"
"io/ioutil"
mrand "math/rand"
"os"
"reflect"
"strings"
"testing"
"v.io/v23/security"
"v.io/x/ref/lib/security/serialization"
"v.io/x/ref/test"
"v.io/x/ref/test/testutil"
)
// We call our own TestMain here because v23 test generate causes an import cycle
// in this package.
func TestMain(m *testing.M) {
test.Init()
os.Exit(m.Run())
}
type bufferCloser struct {
bytes.Buffer
}
func (*bufferCloser) Close() error {
return nil
}
func signingWrite(d, s io.WriteCloser, signer serialization.Signer, writeList [][]byte, opts *serialization.Options) error {
swc, err := serialization.NewSigningWriteCloser(d, s, signer, opts)
if err != nil {
return fmt.Errorf("NewSigningWriteCloser failed: %s", err)
}
for _, b := range writeList {
if _, err := swc.Write(b); err != nil {
return fmt.Errorf("signingWriteCloser.Write failed: %s", err)
}
}
if err := swc.Close(); err != nil {
return fmt.Errorf("signingWriteCloser.Close failed: %s", err)
}
return nil
}
func verifyingRead(d, s io.Reader, key security.PublicKey) ([]byte, error) {
vr, err := serialization.NewVerifyingReader(d, s, key)
if err != nil {
return nil, fmt.Errorf("NewVerifyingReader failed: %s", err)
}
return ioutil.ReadAll(vr)
}
func newSigner() serialization.Signer {
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
panic(err)
}
p, err := security.CreatePrincipal(security.NewInMemoryECDSASigner(key), nil, nil)
if err != nil {
panic(err)
}
return p
}
func matchesErrorPattern(err error, pattern string) bool {
if (len(pattern) == 0) != (err == nil) {
return false
}
return err == nil || strings.Index(err.Error(), pattern) >= 0
}
func TestRoundTrip(t *testing.T) {
signer := newSigner()
d, s := &bufferCloser{}, &bufferCloser{}
testdata := []struct {
writeList [][]byte
opts *serialization.Options
}{
{[][]byte{testutil.RandomBytes(1)}, nil},
{[][]byte{testutil.RandomBytes(100)}, nil},
{[][]byte{testutil.RandomBytes(100)}, &serialization.Options{ChunkSizeBytes: 10}},
{[][]byte{testutil.RandomBytes(25), testutil.RandomBytes(15), testutil.RandomBytes(60), testutil.RandomBytes(5)}, &serialization.Options{ChunkSizeBytes: 7}},
}
for _, test := range testdata {
d.Reset()
s.Reset()
if err := signingWrite(d, s, signer, test.writeList, test.opts); err != nil {
t.Errorf("signingWrite(_, _, %v, %v) failed: %s", test.writeList, test.opts, err)
continue
}
dataRead, err := verifyingRead(d, s, signer.PublicKey())
if err != nil {
t.Errorf("verifyingRead failed: %s", err)
continue
}
dataWritten := bytes.Join(test.writeList, nil)
if !reflect.DeepEqual(dataRead, dataWritten) {
t.Errorf("Read-Write mismatch: data read: %v, data written: %v", dataRead, dataWritten)
continue
}
}
}
func TestIntegrityAndAuthenticity(t *testing.T) {
tamper := func(b []byte) []byte {
c := make([]byte, len(b))
copy(c, b)
c[mrand.Int()%len(b)] += 1
return c
}
signer := newSigner()
d, s := &bufferCloser{}, &bufferCloser{}
if err := signingWrite(d, s, signer, [][]byte{testutil.RandomBytes(100)}, &serialization.Options{ChunkSizeBytes: 7}); err != nil {
t.Fatalf("signingWrite failed: %s", err)
}
// copy the data and signature bytes written.
dataBytes := d.Bytes()
sigBytes := s.Bytes()
// Test that any tampering of the data bytes, or any change
// to the signer causes a verifyingRead to fail.
testdata := []struct {
dataBytes, sigBytes []byte
key security.PublicKey
wantErr string
}{
{dataBytes, sigBytes, signer.PublicKey(), ""},
{dataBytes, sigBytes, newSigner().PublicKey(), "signature verification failed"},
{tamper(dataBytes), sigBytes, signer.PublicKey(), "data has been modified"},
}
for _, test := range testdata {
if _, err := verifyingRead(&bufferCloser{*bytes.NewBuffer(test.dataBytes)}, &bufferCloser{*bytes.NewBuffer(test.sigBytes)}, test.key); !matchesErrorPattern(err, test.wantErr) {
t.Errorf("verifyingRead: got error: %s, want to match: %v", err, test.wantErr)
}
}
}
func TestEdgeCases(t *testing.T) {
var d, s io.ReadWriteCloser
var signer serialization.Signer
var key security.PublicKey
for i := 0; i < 3; i++ {
d, s = &bufferCloser{}, &bufferCloser{}
signer = newSigner()
key = signer.PublicKey()
switch i {
case 0:
d = nil
case 1:
s = nil
case 2:
signer = nil
key = nil
}
matchErr := "cannot be nil"
if _, err := serialization.NewSigningWriteCloser(d, s, signer, nil); !matchesErrorPattern(err, matchErr) {
t.Errorf("NewSigningWriter(%p, %p, %p, ...) returned: %v, want to match: %v", d, s, signer, err, matchErr)
}
if _, err := serialization.NewVerifyingReader(d, s, key); !matchesErrorPattern(err, matchErr) {
t.Errorf("NewVerifyingReader(%p, %p, %p) returned: %v, want to match: %v", d, s, key, err, matchErr)
}
}
}