From c05c22ddbc771e7713849cae40f9d91bfafa0503 Mon Sep 17 00:00:00 2001 From: Rasmus Dahlberg Date: Thu, 25 Feb 2021 14:36:35 +0100 Subject: major refactor based on README.md and TODOs Updated types, improved units tests, isolated most test data to have it in one place, renamed and created new files to improve readability, and fixed a bunch of minor TODOs. --- namespace/namespace.go | 150 ------------------------------ namespace/namespace_test.go | 218 -------------------------------------------- namespace/testdata/data.go | 21 ----- 3 files changed, 389 deletions(-) delete mode 100644 namespace/namespace.go delete mode 100644 namespace/namespace_test.go delete mode 100644 namespace/testdata/data.go (limited to 'namespace') diff --git a/namespace/namespace.go b/namespace/namespace.go deleted file mode 100644 index f5c56cf..0000000 --- a/namespace/namespace.go +++ /dev/null @@ -1,150 +0,0 @@ -// Package namespace provides namespace functionality. A namespace refers to a -// particular verification key and signing algorithm that can be serialized with -// TLS 1.2 notation, see RFC 5246 (ยง4). Only Ed25519 is supported at this time. -// -// For example, this is how a serialized Ed25519 namespace looks like: -// -// 0 2 3 35 (byte index) -// +----+-----------------------+ -// | 1 + 32 + Verification key + -// +----+-----------------------+ -package namespace - -import ( - "fmt" - - "crypto/ed25519" - - "github.com/google/certificate-transparency-go/tls" -) - -// NamespaceFormat defines a particular namespace type that is versioend -type NamespaceFormat tls.Enum - -const ( - NamespaceFormatReserved NamespaceFormat = 0 - NamespaceFormatEd25519V1 NamespaceFormat = 1 -) - -// Namespace references a versioned namespace based on a given format specifier -type Namespace struct { - Format NamespaceFormat `tls:"maxval:65535"` - NamespaceEd25519V1 *NamespaceEd25519V1 `tls:"selector:Format,val:1"` -} - -// NamespaceEd25519V1 uses an Ed25519 verification key as namespace. Encoding, -// signing, and verification operations are defined by RFC 8032. -type NamespaceEd25519V1 struct { - Namespace []byte `tls:"minlen:32,maxlen:32"` -} - -// String returns a human-readable representation of a namespace. -func (n Namespace) String() string { - switch n.Format { - case NamespaceFormatEd25519V1: - return fmt.Sprintf("%x", n.NamespaceEd25519V1.Namespace) - default: - return "reserved" - } -} - -// NewNamespaceEd25519V1 returns an new Ed25519V1 namespace based on a -// verification key. -func NewNamespaceEd25519V1(vk []byte) (*Namespace, error) { - if len(vk) != 32 { - return nil, fmt.Errorf("invalid verification key: must be 32 bytes") - } - return &Namespace{ - Format: NamespaceFormatEd25519V1, - NamespaceEd25519V1: &NamespaceEd25519V1{ - Namespace: vk, - }, - }, nil -} - -// Verify checks that signature is valid over message for this namespace -func (ns *Namespace) Verify(message, signature []byte) error { - switch ns.Format { - case NamespaceFormatEd25519V1: - if !ed25519.Verify(ed25519.PublicKey(ns.NamespaceEd25519V1.Namespace), message, signature) { - return fmt.Errorf("ed25519 signature verification failed") - } - default: - return fmt.Errorf("namespace not supported: %v", ns.Format) - } - return nil -} - -func (ns *Namespace) Marshal() ([]byte, error) { - serialized, err := tls.Marshal(*ns) - if err != nil { - return nil, fmt.Errorf("marshaled failed for namespace(%v): %v", ns.Format, err) - } - return serialized, err -} - -func (ns *Namespace) Unmarshal(serialized []byte) error { - extra, err := tls.Unmarshal(serialized, ns) - if err != nil { - return fmt.Errorf("unmarshal failed for namespace: %v", err) - } else if len(extra) > 0 { - return fmt.Errorf("unmarshal found extra data for namespace(%v): %v", ns.Format, err) - } - return nil -} - -// NamespacePool is a pool of namespaces that contain complete verification keys -type NamespacePool struct { - pool map[string]*Namespace - list []*Namespace - // If we need to update this structure without a restart => add mutex. -} - -// NewNameSpacePool creates a new namespace pool from a list of namespaces. An -// error is returned if there are duplicate namespaces or namespaces without a -// complete verification key. The latter is determined by namespaceWithKey(). -func NewNamespacePool(namespaces []*Namespace) (*NamespacePool, error) { - np := &NamespacePool{ - pool: make(map[string]*Namespace), - list: make([]*Namespace, 0), - } - for _, namespace := range namespaces { - if !namespaceWithKey(namespace.Format) { - return nil, fmt.Errorf("need verification key in namespace pool: %v", namespace.Format) - } - if _, ok := np.pool[namespace.String()]; ok { - return nil, fmt.Errorf("duplicate namespace: %v", namespace.String()) - } - np.pool[namespace.String()] = namespace - np.list = append(np.list, namespace) - } - return np, nil -} - -// Find checks if namespace is a member of the namespace pool. -func (np *NamespacePool) Find(namespace *Namespace) (*Namespace, bool) { - if _, ok := np.pool[namespace.String()]; !ok { - return nil, false - } - // If the passed namespace is a key fingerprint the actual key needs to be - // attached before returning. Not applicable for Ed25519. Docdoc later. - return namespace, true -} - -// List returns a copied list of namespaces that is used by this pool. -func (np *NamespacePool) List() []*Namespace { - namespaces := make([]*Namespace, len(np.list)) - copy(namespaces, np.list) - return namespaces -} - -// namespaceWithKey returns true if a namespace format contains a complete -// verification key. I.e., some formats might have a key fingerprint instead. -func namespaceWithKey(format NamespaceFormat) bool { - switch format { - case NamespaceFormatEd25519V1: - return true - default: - return false - } -} diff --git a/namespace/namespace_test.go b/namespace/namespace_test.go deleted file mode 100644 index 10c0bf0..0000000 --- a/namespace/namespace_test.go +++ /dev/null @@ -1,218 +0,0 @@ -package namespace - -import ( - "bytes" - "testing" - - "crypto/ed25519" - - "github.com/system-transparency/stfe/namespace/testdata" -) - -func TestNewNamespaceEd25519V1(t *testing.T) { - for _, table := range []struct { - description string - vk []byte - wantErr bool - }{ - { - description: "invalid", - vk: append(testdata.Ed25519Vk, 0x00), - wantErr: true, - }, - { - description: "valid", - vk: testdata.Ed25519Vk, - }, - } { - n, err := NewNamespaceEd25519V1(table.vk) - if got, want := err != nil, table.wantErr; got != want { - t.Errorf("got error %v but wanted %v in test %q: %v", got, want, table.description, err) - } - if err != nil { - continue - } - if got, want := n.Format, NamespaceFormatEd25519V1; got != want { - t.Errorf("got namespace format %v but wanted %v in test %q", got, want, table.description) - continue - } - if got, want := n.NamespaceEd25519V1.Namespace, table.vk; !bytes.Equal(got, want) { - t.Errorf("got namespace %X but wanted %X in test %q", got, want, table.description) - } - } -} - -func TestVerify(t *testing.T) { - testMsg := []byte("msg") - for _, table := range []struct { - description string - namespace *Namespace - msg, sig []byte - wantErr bool - }{ - { - description: "invalid: unsupported namespace", - namespace: &Namespace{Format: NamespaceFormatReserved}, - msg: testMsg, - sig: []byte("sig"), - wantErr: true, - }, - { - description: "invalid: bad ed25519 verification key", - namespace: mustNewNamespaceEd25519V1(t, testdata.Ed25519Sk[:32]), - msg: testMsg, - sig: ed25519.Sign(ed25519.PrivateKey(testdata.Ed25519Sk), testMsg), - wantErr: true, - }, - { - description: "invalid: ed25519 signature is not over message", - namespace: mustNewNamespaceEd25519V1(t, testdata.Ed25519Vk), - msg: append(testMsg, 0x00), - sig: ed25519.Sign(ed25519.PrivateKey(testdata.Ed25519Sk), testMsg), - wantErr: true, - }, - { - description: "valid: ed25519", - namespace: mustNewNamespaceEd25519V1(t, testdata.Ed25519Vk), - msg: testMsg, - sig: ed25519.Sign(ed25519.PrivateKey(testdata.Ed25519Sk), testMsg), - }, - } { - err := table.namespace.Verify(table.msg, table.sig) - if got, want := err != nil, table.wantErr; got != want { - t.Errorf("got error=%v but wanted %v in test %q: %v", got, want, table.description, err) - } - } -} - -func TestMarshal(t *testing.T) { - for _, table := range []struct { - description string - namespace *Namespace - wantErr bool - wantBytes []byte - }{ - { - description: "invalid ed25519: namespace size too small", - namespace: &Namespace{ - Format: NamespaceFormatEd25519V1, - NamespaceEd25519V1: &NamespaceEd25519V1{ - Namespace: testdata.Ed25519Vk[:len(testdata.Ed25519Vk)-1], - }, - }, - wantErr: true, - }, - { - description: "invalid ed25519: namespace size too large", - namespace: &Namespace{ - Format: NamespaceFormatEd25519V1, - NamespaceEd25519V1: &NamespaceEd25519V1{ - Namespace: append(testdata.Ed25519Vk, 0x00), - }, - }, - wantErr: true, - }, - { - description: "valid: ed25519", - namespace: mustNewNamespaceEd25519V1(t, testdata.Ed25519Vk), - // TODO: wantBytes - }, - } { - _, err := table.namespace.Marshal() - if got, want := err != nil, table.wantErr; got != want { - t.Errorf("got error %v but wanted %v in test %q: %v", got, want, table.description, err) - } - if err != nil { - continue - } - // TODO: add check that we got the bytes we wanted also - } -} - -func TestUnmarshal(t *testing.T) { - // TODO -} - -func TestNewNamespacePool(t *testing.T) { - ns1, _ := NewNamespaceEd25519V1(testdata.Ed25519Vk) - ns2, _ := NewNamespaceEd25519V1(make([]byte, 32)) - nsr := &Namespace{Format: NamespaceFormatReserved} - for _, table := range []struct { - description string - namespaces []*Namespace - wantErr bool - }{ - { - description: "invalid: duplicate namespace", - namespaces: []*Namespace{ns1, ns1, ns2}, - wantErr: true, - }, - { - description: "invalid: namespace without key", - namespaces: []*Namespace{ns1, nsr, ns2}, - wantErr: true, - }, - { - description: "valid: empty", - namespaces: []*Namespace{}, - }, - { - description: "valid: one namespace", - namespaces: []*Namespace{ns1}, - }, - { - description: "valid: two namespaces", - namespaces: []*Namespace{ns1, ns2}, - }, - } { - _, err := NewNamespacePool(table.namespaces) - if got, want := err != nil, table.wantErr; got != want { - t.Errorf("got error %v but wanted %v in test %q: %v", got, want, table.description, err) - } - } -} - -func TestFind(t *testing.T) { - ns1 := mustNewNamespaceEd25519V1(t, testdata.Ed25519Vk) - ns2 := mustNewNamespaceEd25519V1(t, make([]byte, 32)) - pool, _ := NewNamespacePool(nil) - _, got := pool.Find(ns1) - if want := false; got != want { - t.Errorf("got %v but wanted %v in test %q", got, want, "empty pool") - } - - pool, _ = NewNamespacePool([]*Namespace{ns1}) - _, got = pool.Find(ns1) - if want := true; got != want { - t.Errorf("got %v but wanted %v in test %q", got, want, "non-empty pool: looking for member") - } - _, got = pool.Find(ns2) - if want := false; got != want { - t.Errorf("got %v but wanted %v in test %q", got, want, "non-empty pool: looking for non-member") - } -} - -func TestList(t *testing.T) { - ns1 := mustNewNamespaceEd25519V1(t, testdata.Ed25519Vk) - ns2 := mustNewNamespaceEd25519V1(t, make([]byte, 32)) - namespaces := []*Namespace{ns1, ns2} - pool, _ := NewNamespacePool(namespaces) - l1 := pool.List() - if got, want := len(l1), len(namespaces); got != want { - t.Errorf("got len %v but wanted %v", got, want) - } - - l1[0] = ns2 - l2 := pool.List() - if bytes.Equal(l1[0].NamespaceEd25519V1.Namespace, l2[0].NamespaceEd25519V1.Namespace) { - t.Errorf("returned list is not a copy") - } -} - -func mustNewNamespaceEd25519V1(t *testing.T, vk []byte) *Namespace { - namespace, err := NewNamespaceEd25519V1(vk) - if err != nil { - t.Fatalf("must make ed25519 namespace: %v", err) - } - return namespace -} diff --git a/namespace/testdata/data.go b/namespace/testdata/data.go deleted file mode 100644 index 5f3f4df..0000000 --- a/namespace/testdata/data.go +++ /dev/null @@ -1,21 +0,0 @@ -package testdata - -import ( - "encoding/base64" -) - -var ( - Ed25519Vk = mustDecodeB64("HOQFUkKNWpjYAhNKTyWCzahlI7RDtf5123kHD2LACj0=") - Ed25519Sk = mustDecodeB64("Zaajc50Xt1tNpTj6WYkljzcVjLXL2CcQcHFT/xZqYEcc5AVSQo1amNgCE0pPJYLNqGUjtEO1/nXbeQcPYsAKPQ==") - - Ed25519Vk2 = mustDecodeB64("LqrWb9JwQUTk/SwTNDdMH8aRmy3mbmhwEepO5WSgb+A=") - Ed25519Sk2 = mustDecodeB64("fDkSq4cWvG72yMhUyHVcZ72QKerZ66msgyVqDvfufZQuqtZv0nBBROT9LBM0N0wfxpGbLeZuaHAR6k7lZKBv4A==") - - Ed25519Vk3 = mustDecodeB64("Icd1U1oY0z+5iAwgCQZyGI+pycGs6GI2rQO8gAzT7Y0=") - Ed25519Sk3 = mustDecodeB64("Q6uNL7VKv9flaBIXBXy/NyhNOicLYKKmfuJ6tLReMvQhx3VTWhjTP7mIDCAJBnIYj6nJwazoYjatA7yADNPtjQ==") -) - -func mustDecodeB64(s string) []byte { - b, _ := base64.StdEncoding.DecodeString(s) - return b -} -- cgit v1.2.3