You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
DateKeys/profile/registry.go

89 lines
2.5 KiB

package profile
import (
"crypto/sha256"
"encoding/hex"
"fmt"
datekeys "g.activething.com/go/DateKeys"
)
// Registry resolves a profile_id to a locally trusted Provider Profile. The
// client MUST NOT accept a profile or key supplied by the endpoint that
// delivers the release (spec §13); a Registry is built by the caller from
// pinned data only.
type Registry interface {
// Lookup returns a copy of the pinned profile for id.
Lookup(id string) (*Profile, bool)
}
// Pin is a profile together with the profile_hash the caller expects it to
// have (spec §13: the profile hash is known in advance).
type Pin struct {
Profile *Profile
Hash [32]byte
}
type pinned struct {
profile *Profile
hash [32]byte
}
type registry struct {
m map[string]pinned
}
// NewRegistry validates every profile, checks it against its expected
// profile_hash and returns an immutable registry holding private copies.
//
// Each profile goes through the rules of spec §12.1 in their order, as its
// encoding would: it is encoded and decoded again (rules 1 to 3, with the
// codes profile.Decode reports), and only then compared with its pinned
// profile_hash (rule 4).
func NewRegistry(pins ...Pin) (Registry, error) {
r := &registry{m: make(map[string]pinned, len(pins))}
for _, pin := range pins {
if pin.Profile == nil {
return nil, fmt.Errorf("profile: nil profile in registry: %w", datekeys.ErrUnknownProfile)
}
b, err := pin.Profile.CanonicalCBOR()
if err != nil {
return nil, err
}
p, err := Decode(b)
if err != nil {
return nil, err
}
h := sha256.Sum256(b)
if h != pin.Hash {
return nil, fmt.Errorf("profile %s: profile_hash %x does not match the pinned %x: %w",
p.ID, h, pin.Hash, datekeys.ErrProfileMismatch)
}
if _, dup := r.m[p.ID]; dup {
return nil, fmt.Errorf("profile %s: pinned twice", p.ID)
}
r.m[p.ID] = pinned{profile: p, hash: h}
}
return r, nil
}
func (r *registry) Lookup(id string) (*Profile, bool) {
e, ok := r.m[id]
if !ok {
return nil, false
}
return e.profile.Clone(), true
}
// Default returns the default registry, which contains only the Quicknet
// profile checked against QuicknetProfileHash.
func Default() (Registry, error) {
var h [32]byte
b, err := hex.DecodeString(QuicknetProfileHash)
if err != nil || len(b) != len(h) {
return nil, fmt.Errorf("profile: invalid pinned Quicknet profile hash: %w", datekeys.ErrProfileMismatch)
}
copy(h[:], b)
return NewRegistry(Pin{Profile: Quicknet(), Hash: h})
}

Powered by TurnKey Linux.