blob: 5fe468f8749811f0702233d2269bd74d1a7d89fe [file] [log] [blame]
// Copyright 2016 The Upspin 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 ei
import (
"bytes"
"crypto/rand"
"fmt"
"log"
"strings"
"testing"
"upspin.io/bind"
"upspin.io/config"
"upspin.io/errors"
"upspin.io/factotum"
"upspin.io/pack"
"upspin.io/pack/internal/packtest"
"upspin.io/test/testfixtures"
"upspin.io/test/testutil"
"upspin.io/upspin"
)
const (
packing = upspin.EEIntegrityPack
)
func TestRegister(t *testing.T) {
p := pack.Lookup(upspin.EEIntegrityPack)
if p == nil {
t.Fatal("Lookup failed")
}
if p.Packing() != upspin.EEIntegrityPack {
t.Fatalf("expected EEIntegrityPack, got %q", p)
}
}
// packBlob packs text according to the parameters and returns the cipher.
func packBlob(t *testing.T, cfg upspin.Config, packer upspin.Packer, d *upspin.DirEntry, text []byte) []byte {
d.Packing = packer.Packing()
bp, err := packer.Pack(cfg, d)
if err != nil {
t.Fatal("packBlob:", err)
}
cipher, err := bp.Pack(text)
if err != nil {
t.Fatal("packBlob:", err)
}
bp.SetLocation(upspin.Location{Reference: "dummy"})
if err := bp.Close(); err != nil {
t.Fatal("packBlob:", err)
}
return cipher
}
// unpackBlob unpacks cipher according to the parameters and returns the plain text.
func unpackBlob(t *testing.T, cfg upspin.Config, packer upspin.Packer, d *upspin.DirEntry, cipher []byte) []byte {
bp, err := packer.Unpack(cfg, d)
if err != nil {
t.Fatal("unpackBlob:", err)
}
if _, ok := bp.NextBlock(); !ok {
t.Fatal("unpackBlob: no next block")
}
text, err := bp.Unpack(cipher)
if err != nil {
t.Fatal("unpackBlob:", err)
}
return text
}
func testPackAndUnpack(t *testing.T, cfg upspin.Config, packer upspin.Packer, name upspin.PathName, text []byte) {
// First pack.
d := &upspin.DirEntry{
Name: name,
SignedName: name,
Writer: cfg.UserName(),
}
cipher := packBlob(t, cfg, packer, d, text)
// Now unpack.
clear := unpackBlob(t, cfg, packer, d, cipher)
if !bytes.Equal(text, clear) {
t.Errorf("text: expected %q; got %q", text, clear)
}
if d.SignedName != d.Name {
t.Errorf("SignedName: expected %q; got %q", d.Name, d.SignedName)
}
}
func testPackNameAndUnpack(t *testing.T, cfg upspin.Config, packer upspin.Packer, name, newName upspin.PathName, text []byte) {
// First pack.
d := &upspin.DirEntry{
Name: name,
SignedName: name,
Writer: cfg.UserName(),
}
cipher := packBlob(t, cfg, packer, d, text)
// Name to newName.
if err := packer.Name(cfg, d, newName); err != nil {
t.Errorf("Name failed: %s", err)
}
if d.Name != newName {
t.Errorf("Name failed to set the name")
}
// Now unpack.
clear := unpackBlob(t, cfg, packer, d, cipher)
if !bytes.Equal(text, clear) {
t.Errorf("text: expected %q; got %q", text, clear)
}
}
func TestPack256(t *testing.T) {
const (
user upspin.UserName = "joe@upspin.io"
name = upspin.PathName(user + "/file/of/user.256")
text = "this is some text 256"
)
cfg, packer := setup(user)
testPackAndUnpack(t, cfg, packer, name, []byte(text))
}
func TestName256(t *testing.T) {
const (
user upspin.UserName = "joe@upspin.io"
name = upspin.PathName(user + "/file/of/user.256")
newName = upspin.PathName(user + "/file/of/user.256.2")
text = "this is some text 256"
)
cfg, packer := setup(user)
testPackNameAndUnpack(t, cfg, packer, name, newName, []byte(text))
}
func benchmarkPack(b *testing.B, curveName string, fileSize int, unpack bool) {
b.SetBytes(int64(fileSize))
const user upspin.UserName = "joe@upspin.io"
data := make([]byte, fileSize)
n, err := rand.Read(data)
if err != nil {
b.Fatal(err)
}
if n != fileSize {
b.Fatalf("Not enough random bytes read: %d", n)
}
data = data[:n]
name := upspin.PathName(fmt.Sprintf("%s/file/of/user.%d", user, packing))
cfg, packer := setup(user)
for i := 0; i < b.N; i++ {
d := &upspin.DirEntry{
Name: name,
SignedName: name,
Writer: cfg.UserName(),
Packing: packer.Packing(),
}
bp, err := packer.Pack(cfg, d)
if err != nil {
b.Fatal(err)
}
cipher, err := bp.Pack(data)
if err != nil {
b.Fatal(err)
}
bp.SetLocation(upspin.Location{Reference: "dummy"})
if err := bp.Close(); err != nil {
b.Fatal(err)
}
if !unpack {
continue
}
bu, err := packer.Unpack(cfg, d)
if err != nil {
b.Fatal(err)
}
if _, ok := bu.NextBlock(); !ok {
b.Fatal("no next block")
}
clear, err := bu.Unpack(cipher)
if err != nil {
b.Fatal(err)
}
if !bytes.Equal(clear, data) {
b.Fatal("cleartext mismatch")
}
}
}
const unpack = true
func BenchmarkPack256_1byte(b *testing.B) { benchmarkPack(b, "p256", 1, !unpack) }
func BenchmarkPack256_1kbyte(b *testing.B) { benchmarkPack(b, "p256", 1024, !unpack) }
func BenchmarkPack256_1Mbyte(b *testing.B) { benchmarkPack(b, "p256", 1024*1024, !unpack) }
func BenchmarkPackUnpack256_1byte(b *testing.B) { benchmarkPack(b, "p256", 1, unpack) }
func BenchmarkPackUnpack256_1kbyte(b *testing.B) { benchmarkPack(b, "p256", 1024, unpack) }
func BenchmarkPackUnpack256_1Mbyte(b *testing.B) {
benchmarkPack(b, "p256", 1024*1024, unpack)
}
func TestSharing(t *testing.T) {
// joe@google.com is the owner of a file that is shared with bob@foo.com.
const (
joesUserName upspin.UserName = "joe@google.com"
pathName = upspin.PathName(joesUserName + "/secret_file_shared_with_bob")
bobsUserName upspin.UserName = "bob@foo.com"
text = "bob, here's the secret file. Sincerely, The Joe."
)
// Set up Joe as the creator/owner.
joecfg, packer := setup(joesUserName)
// Set up a mock user service that knows about Joe's public keys (for checking signature during unpack).
mockKey := &dummyKey{
userToMatch: []upspin.UserName{joesUserName},
keyToReturn: []upspin.PublicKey{joecfg.Factotum().PublicKey()},
}
bind.RegisterKeyServer(upspin.InProcess, mockKey)
joecfg = config.SetKeyEndpoint(joecfg, upspin.Endpoint{Transport: upspin.InProcess})
d := &upspin.DirEntry{
Name: pathName,
SignedName: pathName,
}
d.Writer = joecfg.UserName()
cipher := packBlob(t, joecfg, packer, d, []byte(text))
// Now load Bob as the current user.
bobcfg, packer := setup(bobsUserName)
bobcfg = config.SetKeyEndpoint(bobcfg, upspin.Endpoint{Transport: upspin.InProcess})
clear := unpackBlob(t, bobcfg, packer, d, cipher)
if string(clear) != text {
t.Errorf("Expected %s, got %s", text, clear)
}
// Finally, check that unpack looked up Joe's public key, to verify the signature.
if mockKey.returnedKeys != 1 {
t.Fatal("Packer failed to request dude's public key")
}
}
func setup(name upspin.UserName) (upspin.Config, upspin.Packer) {
cfg := config.SetUserName(config.New(), name)
packer := pack.Lookup(packing)
j := strings.IndexByte(string(name), '@')
if j < 0 {
log.Fatalf("malformed username %s", name)
}
f, err := factotum.NewFromDir(testutil.Repo("key", "testdata", string(name[:j])))
if err != nil {
log.Fatalf("unable to initialize factotum for %s", string(name[:j]))
}
cfg = config.SetFactotum(cfg, f)
return cfg, packer
}
// dummyKey is a User service that returns a key for a given user.
type dummyKey struct {
testfixtures.DummyKey
// The two slices go together
userToMatch []upspin.UserName
keyToReturn []upspin.PublicKey
returnedKeys int
}
var _ upspin.KeyServer = (*dummyKey)(nil)
func (d *dummyKey) Lookup(userName upspin.UserName) (*upspin.User, error) {
const op errors.Op = "pack/ei.dummyKey.Lookup"
for i, u := range d.userToMatch {
if u == userName {
d.returnedKeys++
user := &upspin.User{
Name: userName,
PublicKey: d.keyToReturn[i],
}
return user, nil
}
}
return nil, errors.E(op, userName, errors.NotExist, "user not found")
}
func (d *dummyKey) Dial(cc upspin.Config, e upspin.Endpoint) (upspin.Service, error) {
return d, nil
}
func TestMultiBlockRoundTrip(t *testing.T) {
const userName = upspin.UserName("aly@upspin.io")
cfg, packer := setup(userName)
packtest.TestMultiBlockRoundTrip(t, cfg, packer, userName)
}