aboutsummaryrefslogtreecommitdiff
path: root/types/namespace.go
diff options
context:
space:
mode:
Diffstat (limited to 'types/namespace.go')
-rw-r--r--types/namespace.go135
1 files changed, 135 insertions, 0 deletions
diff --git a/types/namespace.go b/types/namespace.go
new file mode 100644
index 0000000..8a2ad17
--- /dev/null
+++ b/types/namespace.go
@@ -0,0 +1,135 @@
+package types
+
+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"`
+ Ed25519V1 *Ed25519V1 `tls:"selector:Format,val:1"`
+}
+
+// Ed25519V1 uses an Ed25519 verification key as namespace. Encoding,
+// signing, and verification operations are defined by RFC 8032.
+type Ed25519V1 struct {
+ Namespace [32]byte
+}
+
+func (f NamespaceFormat) String() string {
+ switch f {
+ case NamespaceFormatReserved:
+ return "reserved"
+ case NamespaceFormatEd25519V1:
+ return "ed25519_v1"
+ default:
+ return fmt.Sprintf("unknown NamespaceFormat: %d", f)
+ }
+}
+
+func (n Namespace) String() string {
+ switch n.Format {
+ case NamespaceFormatReserved:
+ return fmt.Sprintf("Format(%s)", n.Format)
+ case NamespaceFormatEd25519V1:
+ return fmt.Sprintf("Format(%s): %+v", n.Format, n.Ed25519V1)
+ default:
+ return fmt.Sprintf("unknown Namespace: %v", n.Format)
+ }
+}
+
+// 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")
+ }
+
+ var ed25519v1 Ed25519V1
+ copy(ed25519v1.Namespace[:], vk)
+ return &Namespace{
+ Format: NamespaceFormatEd25519V1,
+ Ed25519V1: &ed25519v1,
+ }, 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.Ed25519V1.Namespace[:]), message, signature) {
+ return fmt.Errorf("ed25519 signature verification failed")
+ }
+ default:
+ return fmt.Errorf("namespace not supported: %v", ns.Format)
+ }
+ 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
+ }
+}