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.
82 lines
2.2 KiB
82 lines
2.2 KiB
package profile
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"fmt"
|
|
|
|
datekeys "github.com/datekeys/datekeys-go"
|
|
)
|
|
|
|
// 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.
|
|
func NewRegistry(pins ...Pin) (Registry, error) {
|
|
r := ®istry{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)
|
|
}
|
|
p := pin.Profile.Clone()
|
|
if err := p.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
h, err := p.Hash()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
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})
|
|
}
|