// 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 } }