| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137 |
- // Copyright (c) Tailscale Inc & AUTHORS
- // SPDX-License-Identifier: BSD-3-Clause
- package tka
- import (
- "crypto/ed25519"
- "errors"
- "fmt"
- "tailscale.com/types/tkatype"
- )
- // KeyKind describes the different varieties of a Key.
- type KeyKind uint8
- // Valid KeyKind values.
- const (
- KeyInvalid KeyKind = iota
- Key25519
- )
- func (k KeyKind) String() string {
- switch k {
- case KeyInvalid:
- return "invalid"
- case Key25519:
- return "25519"
- default:
- return fmt.Sprintf("Key?<%d>", int(k))
- }
- }
- // Key describes the public components of a key known to network-lock.
- type Key struct {
- Kind KeyKind `cbor:"1,keyasint"`
- // Votes describes the weight applied to signatures using this key.
- // Weighting is used to deterministically resolve branches in the AUM
- // chain (i.e. forks, where two AUMs exist with the same parent).
- Votes uint `cbor:"2,keyasint"`
- // Public encodes the public key of the key. For 25519 keys,
- // this is simply the point on the curve representing the public
- // key.
- Public []byte `cbor:"3,keyasint"`
- // Meta describes arbitrary metadata about the key. This could be
- // used to store the name of the key, for instance.
- Meta map[string]string `cbor:"12,keyasint,omitempty"`
- }
- // Clone makes an independent copy of Key.
- //
- // NOTE: There is a difference between a nil slice and an empty slice for encoding purposes,
- // so an implementation of Clone() must take care to preserve this.
- func (k Key) Clone() Key {
- out := k
- if k.Public != nil {
- out.Public = make([]byte, len(k.Public))
- copy(out.Public, k.Public)
- }
- if k.Meta != nil {
- out.Meta = make(map[string]string, len(k.Meta))
- for k, v := range k.Meta {
- out.Meta[k] = v
- }
- }
- return out
- }
- // MustID returns the KeyID of the key, panicking if an error is
- // encountered. This must only be used for tests.
- func (k Key) MustID() tkatype.KeyID {
- id, err := k.ID()
- if err != nil {
- panic(err)
- }
- return id
- }
- // ID returns the KeyID of the key.
- func (k Key) ID() (tkatype.KeyID, error) {
- switch k.Kind {
- // Because 25519 public keys are so short, we just use the 32-byte
- // public as their 'key ID'.
- case Key25519:
- return tkatype.KeyID(k.Public), nil
- default:
- return nil, fmt.Errorf("unknown key kind: %v", k.Kind)
- }
- }
- // Ed25519 returns the ed25519 public key encoded by Key. An error is
- // returned for keys which do not represent ed25519 public keys.
- func (k Key) Ed25519() (ed25519.PublicKey, error) {
- switch k.Kind {
- case Key25519:
- return ed25519.PublicKey(k.Public), nil
- default:
- return nil, fmt.Errorf("key is of type %v, not ed25519", k.Kind)
- }
- }
- const maxMetaBytes = 512
- func (k Key) StaticValidate() error {
- if k.Votes > 4096 {
- return fmt.Errorf("excessive key weight: %d > 4096", k.Votes)
- }
- if k.Votes == 0 {
- return errors.New("key votes must be non-zero")
- }
- // We have an arbitrary upper limit on the amount
- // of metadata that can be associated with a key, so
- // people don't start using it as a key-value store and
- // causing pathological cases due to the number + size of
- // AUMs.
- var metaBytes uint
- for k, v := range k.Meta {
- metaBytes += uint(len(k) + len(v))
- }
- if metaBytes > maxMetaBytes {
- return fmt.Errorf("key metadata too big (%d > %d)", metaBytes, maxMetaBytes)
- }
- switch k.Kind {
- case Key25519:
- default:
- return fmt.Errorf("unrecognized key kind: %v", k.Kind)
- }
- return nil
- }
|