blob: 9a3234f1ad5d5763f318e68f0be749fb324ff501 [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 security
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"io/ioutil"
"os"
"path"
"reflect"
"testing"
"v.io/v23/security"
"v.io/v23/verror"
)
func TestLoadPersistentPrincipal(t *testing.T) {
// If the directory does not exist want os.IsNotExists.
_, err := LoadPersistentPrincipal("/tmp/fake/path/", nil)
if !os.IsNotExist(err) {
t.Errorf("invalid path should return does not exist error, instead got %v", err)
}
// If the key file exists and is unencrypted we should succeed.
dir := generatePEMFile(nil)
if _, err = LoadPersistentPrincipal(dir, nil); err != nil {
t.Errorf("unencrypted LoadPersistentPrincipal should have succeeded: %v", err)
}
os.RemoveAll(dir)
// If the private key file exists and is encrypted we should succeed with correct passphrase.
passphrase := []byte("passphrase")
incorrect_passphrase := []byte("incorrect_passphrase")
dir = generatePEMFile(passphrase)
if _, err = LoadPersistentPrincipal(dir, passphrase); err != nil {
t.Errorf("encrypted LoadPersistentPrincipal should have succeeded: %v", err)
}
// and fail with an incorrect passphrase.
if _, err = LoadPersistentPrincipal(dir, incorrect_passphrase); err == nil {
t.Errorf("encrypted LoadPersistentPrincipal with incorrect passphrase should fail")
}
// and return ErrBadPassphrase if the passphrase is nil.
if _, err = LoadPersistentPrincipal(dir, nil); verror.ErrorID(err) != ErrBadPassphrase.ID {
t.Errorf("encrypted LoadPersistentPrincipal with nil passphrase should return ErrBadPassphrase: %v", err)
}
os.RemoveAll(dir)
}
func TestCreatePersistentPrincipal(t *testing.T) {
tests := []struct {
Message, Passphrase []byte
}{
{[]byte("unencrypted"), nil},
{[]byte("encrypted"), []byte("passphrase")},
}
for _, test := range tests {
testCreatePersistentPrincipal(t, test.Message, test.Passphrase)
}
}
func testCreatePersistentPrincipal(t *testing.T, message, passphrase []byte) {
// Persistence of the BlessingRoots and BlessingStore objects is
// tested in other files. Here just test the persistence of the key.
dir, err := ioutil.TempDir("", "TestCreatePersistentPrincipal")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
p, err := CreatePersistentPrincipal(dir, passphrase)
if err != nil {
t.Fatal(err)
}
_, err = CreatePersistentPrincipal(dir, passphrase)
if err == nil {
t.Error("CreatePersistentPrincipal passed unexpectedly")
}
sig, err := p.Sign(message)
if err != nil {
t.Fatal(err)
}
p2, err := LoadPersistentPrincipal(dir, passphrase)
if err != nil {
t.Fatalf("%s failed: %v", message, err)
}
if !sig.Verify(p2.PublicKey(), message) {
t.Errorf("%s failed: p.PublicKey=%v, p2.PublicKey=%v", message, p.PublicKey(), p2.PublicKey())
}
}
func generatePEMFile(passphrase []byte) (dir string) {
dir, err := ioutil.TempDir("", "TestLoadPersistentPrincipal")
if err != nil {
panic(err)
}
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
panic(err)
}
f, err := os.Create(path.Join(dir, privateKeyFile))
if err != nil {
panic(err)
}
defer f.Close()
if err = SavePEMKey(f, key, passphrase); err != nil {
panic(err)
}
return dir
}
func TestPrincipalBlessingsByName(t *testing.T) {
var p1, p2, p3 security.Principal
var err error
if p1, err = NewPrincipal(); err != nil {
t.Fatal(err)
}
if p2, err = NewPrincipal(); err != nil {
t.Fatal(err)
}
alice, err := p1.BlessSelf("alice")
if err != nil {
t.Fatal(err)
}
p2.AddToRoots(alice)
var aliceworkfriend, alicegymfriend, aliceworkboss security.Blessings
if aliceworkfriend, err = p1.Bless(p2.PublicKey(), alice, "work/friend", security.UnconstrainedUse()); err != nil {
t.Errorf("Bless(work/friend) failed: %v", err)
}
p2.BlessingStore().Set(aliceworkfriend, "alice/work/friend")
if alicegymfriend, err = p1.Bless(p2.PublicKey(), alice, "gym/friend", security.UnconstrainedUse()); err != nil {
t.Errorf("Bless(gym/friend) failed: %v", err)
}
p2.BlessingStore().Set(alicegymfriend, "alice/gym/friend")
if aliceworkboss, err = p1.Bless(p2.PublicKey(), alice, "work/boss", security.UnconstrainedUse()); err != nil {
t.Errorf("Bless(work/friend) failed: %v", err)
}
p2.BlessingStore().Set(aliceworkboss, "alice/work/boss")
// Blessing from an untrusted principal that should never be returned
if p3, err = NewPrincipal(); err != nil {
t.Fatal(err)
}
fake, err := p3.BlessSelf("alice")
if err != nil {
t.Fatal(err)
}
fakefriend, err := p3.Bless(p2.PublicKey(), fake, "work/friend", security.UnconstrainedUse())
if err != nil {
t.Errorf("Bless(work/friend) failed: %v", err)
}
_, err = p2.BlessingStore().Set(fakefriend, "fake/work/friend")
tests := []struct {
matched []security.Blessings
pattern security.BlessingPattern
}{
{
matched: []security.Blessings{aliceworkfriend, aliceworkboss},
pattern: "alice/work",
},
{
matched: []security.Blessings{aliceworkfriend},
pattern: "alice/work/friend",
},
{
matched: []security.Blessings{alicegymfriend},
pattern: "alice/gym/friend",
},
{
matched: []security.Blessings{aliceworkfriend, alicegymfriend, aliceworkboss},
pattern: "alice",
},
{
matched: []security.Blessings{aliceworkfriend, alicegymfriend, aliceworkboss},
pattern: security.AllPrincipals,
},
{
matched: nil,
pattern: "alice/school",
},
}
for _, test := range tests {
matched := p2.BlessingsByName(test.pattern)
if len(matched) != len(test.matched) {
t.Errorf("BlessingsByName(%s) did not return expected number of matches wanted:%d got:%d", test.pattern, len(test.matched), len(matched))
}
for _, m := range matched {
found := false
for _, tm := range test.matched {
if reflect.DeepEqual(m, tm) {
found = true
break
}
}
if !found {
t.Errorf("Invalid blessing was returned as a match:%v for pattern:%s", m, test.pattern)
}
}
}
}