blob: e9df0d5865fcca86b768d9b17ef9e9ee4e24006a [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 plain_test
import (
"bytes"
"crypto/rand"
"fmt"
"log"
"strings"
"testing"
"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.PlainPack
)
func TestRegister(t *testing.T) {
p := pack.Lookup(upspin.PlainPack)
if p == nil {
t.Fatal("Lookup failed")
}
if p.Packing() != upspin.PlainPack {
t.Fatalf("expected plain pack 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 TestPack(t *testing.T) {
const (
user upspin.UserName = "joe@upspin.io"
name = upspin.PathName(user + "/file/of/user")
text = "this is some text"
)
cfg, packer := setup(user)
testPackAndUnpack(t, cfg, packer, name, []byte(text))
}
func benchmarkPlainPack(b *testing.B, fileSize int) {
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: got %d, expected %d", n, fileSize)
}
data = data[:n]
name := upspin.PathName(fmt.Sprintf("%s/file/of/user.%d", user, packing))
cfg, packer := setup(user)
// TODO Consider running this once as a Test. Then the Benchmark version
// doesn't need any error checking in the loop, and certainly no bytes.Equal.
// Change eeintegrity_test.go similarly.
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)
}
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")
}
}
}
func BenchmarkPlainPack_1byte(b *testing.B) { benchmarkPlainPack(b, 1) }
func BenchmarkPlainPack_1kbyte(b *testing.B) { benchmarkPlainPack(b, 1024) }
func BenchmarkPlainPack_1Mbyte(b *testing.B) { benchmarkPlainPack(b, 1024*1024) }
func TestMultiBlockRoundTrip(t *testing.T) {
const userName = upspin.UserName("aly@upspin.io")
cfg, packer := setup(userName)
packtest.TestMultiBlockRoundTrip(t, cfg, packer, userName)
}
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
}