blob: ed4c0ae4ecdef6dfead4c2ea1674137bcf69b6d2 [file] [log] [blame]
package verify
import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"io"
"testing"
"time"
"github.com/flynn/go-tuf/data"
"github.com/flynn/go-tuf/sign"
"golang.org/x/crypto/ed25519"
. "gopkg.in/check.v1"
)
// Hook up gocheck into the "go test" runner.
func Test(t *testing.T) { TestingT(t) }
type VerifySuite struct{}
var _ = Suite(&VerifySuite{})
type ecdsaSigner struct {
*ecdsa.PrivateKey
}
func (s ecdsaSigner) PublicData() *data.Key {
pub := s.Public().(*ecdsa.PublicKey)
return &data.Key{
Type: data.KeyTypeECDSA_SHA2_P256,
Scheme: data.KeySchemeECDSA_SHA2_P256,
Algorithms: data.KeyAlgorithms,
Value: data.KeyValue{Public: elliptic.Marshal(pub.Curve, pub.X, pub.Y)},
}
}
func (s ecdsaSigner) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) ([]byte, error) {
hash := sha256.Sum256(msg)
return s.PrivateKey.Sign(rand, hash[:], crypto.SHA256)
}
func (s ecdsaSigner) IDs() []string {
return s.PublicData().IDs()
}
func (s ecdsaSigner) ContainsID(id string) bool {
return s.PublicData().ContainsID(id)
}
func (ecdsaSigner) Type() string {
return data.KeyTypeECDSA_SHA2_P256
}
func (ecdsaSigner) Scheme() string {
return data.KeySchemeECDSA_SHA2_P256
}
func (VerifySuite) Test(c *C) {
type test struct {
name string
keys []*data.Key
roles map[string]*data.Role
s *data.Signed
ver int
exp *time.Time
typ string
role string
err error
mut func(*test)
}
expiredTime := time.Now().Add(-time.Hour)
minVer := 10
tests := []test{
{
name: "no signatures",
mut: func(t *test) { t.s.Signatures = []data.Signature{} },
err: ErrNoSignatures,
},
{
name: "unknown role",
role: "foo",
err: ErrUnknownRole{"foo"},
},
{
name: "signature wrong length",
mut: func(t *test) { t.s.Signatures[0].Signature = []byte{0} },
err: ErrInvalid,
},
{
name: "key missing from role",
mut: func(t *test) { t.roles["root"].KeyIDs = nil },
err: ErrRoleThreshold{1, 0},
},
{
name: "invalid signature",
mut: func(t *test) { t.s.Signatures[0].Signature = make([]byte, ed25519.SignatureSize) },
err: ErrInvalid,
},
{
name: "not enough signatures",
mut: func(t *test) { t.roles["root"].Threshold = 2 },
err: ErrRoleThreshold{2, 1},
},
{
name: "exactly enough signatures",
},
{
name: "more than enough signatures",
mut: func(t *test) {
k, _ := sign.GenerateEd25519Key()
sign.Sign(t.s, k.Signer())
t.keys = append(t.keys, k.PublicData())
t.roles["root"].KeyIDs = append(t.roles["root"].KeyIDs, k.PublicData().IDs()...)
},
},
{
name: "duplicate key id",
mut: func(t *test) {
t.roles["root"].Threshold = 2
t.s.Signatures = append(t.s.Signatures, t.s.Signatures[0])
},
err: ErrRoleThreshold{2, 1},
},
{
name: "unknown key",
mut: func(t *test) {
k, _ := sign.GenerateEd25519Key()
sign.Sign(t.s, k.Signer())
},
},
{
name: "unknown key below threshold",
mut: func(t *test) {
k, _ := sign.GenerateEd25519Key()
sign.Sign(t.s, k.Signer())
t.roles["root"].Threshold = 2
},
err: ErrRoleThreshold{2, 1},
},
{
name: "unknown keys in db",
mut: func(t *test) {
k, _ := sign.GenerateEd25519Key()
sign.Sign(t.s, k.Signer())
t.keys = append(t.keys, k.PublicData())
},
},
{
name: "unknown keys in db below threshold",
mut: func(t *test) {
k, _ := sign.GenerateEd25519Key()
sign.Sign(t.s, k.Signer())
t.keys = append(t.keys, k.PublicData())
t.roles["root"].Threshold = 2
},
err: ErrRoleThreshold{2, 1},
},
{
name: "wrong type",
typ: "bar",
err: ErrWrongMetaType,
},
{
name: "low version",
ver: minVer - 1,
err: ErrLowVersion{minVer - 1, minVer},
},
{
name: "expired",
exp: &expiredTime,
err: ErrExpired{expiredTime},
},
{
name: "valid ecdsa signature",
mut: func(t *test) {
k, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
s := ecdsaSigner{k}
sign.Sign(t.s, s)
t.s.Signatures = t.s.Signatures[1:]
t.keys = []*data.Key{s.PublicData()}
t.roles["root"].KeyIDs = s.PublicData().IDs()
},
},
{
name: "invalid ecdsa signature",
mut: func(t *test) {
k, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
s := ecdsaSigner{k}
sign.Sign(t.s, s)
t.s.Signatures[1].Signature[0]++
t.keys = append(t.keys, s.PublicData())
t.roles["root"].KeyIDs = append(t.roles["root"].KeyIDs, s.PublicData().IDs()...)
},
err: ErrInvalid,
},
}
for _, t := range tests {
if t.role == "" {
t.role = "root"
}
if t.ver == 0 {
t.ver = minVer
}
if t.exp == nil {
expires := time.Now().Add(time.Hour)
t.exp = &expires
}
if t.typ == "" {
t.typ = t.role
}
if t.keys == nil && t.s == nil {
k, _ := sign.GenerateEd25519Key()
t.s, _ = sign.Marshal(&signedMeta{Type: t.typ, Version: t.ver, Expires: *t.exp}, k.Signer())
t.keys = []*data.Key{k.PublicData()}
}
if t.roles == nil {
t.roles = map[string]*data.Role{
"root": {
KeyIDs: t.keys[0].IDs(),
Threshold: 1,
},
}
}
if t.mut != nil {
t.mut(&t)
}
db := NewDB()
for _, k := range t.keys {
for _, id := range k.IDs() {
err := db.AddKey(id, k)
c.Assert(err, IsNil)
}
}
for n, r := range t.roles {
err := db.AddRole(n, r)
c.Assert(err, IsNil)
}
err := db.Verify(t.s, t.role, minVer)
if e, ok := t.err.(ErrExpired); ok {
assertErrExpired(c, err, e)
} else {
c.Assert(err, DeepEquals, t.err, Commentf("name = %s", t.name))
}
}
}
func assertErrExpired(c *C, err error, expected ErrExpired) {
actual, ok := err.(ErrExpired)
if !ok {
c.Fatalf("expected err to have type ErrExpired, got %T", err)
}
c.Assert(actual.Expired.Unix(), Equals, expected.Expired.Unix())
}