key.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package tka
  4. import (
  5. "crypto/ed25519"
  6. "errors"
  7. "fmt"
  8. "github.com/hdevalence/ed25519consensus"
  9. "tailscale.com/types/tkatype"
  10. )
  11. // KeyKind describes the different varieties of a Key.
  12. type KeyKind uint8
  13. // Valid KeyKind values.
  14. const (
  15. KeyInvalid KeyKind = iota
  16. Key25519
  17. )
  18. func (k KeyKind) String() string {
  19. switch k {
  20. case KeyInvalid:
  21. return "invalid"
  22. case Key25519:
  23. return "25519"
  24. default:
  25. return fmt.Sprintf("Key?<%d>", int(k))
  26. }
  27. }
  28. // Key describes the public components of a key known to network-lock.
  29. type Key struct {
  30. Kind KeyKind `cbor:"1,keyasint"`
  31. // Votes describes the weight applied to signatures using this key.
  32. // Weighting is used to deterministically resolve branches in the AUM
  33. // chain (i.e. forks, where two AUMs exist with the same parent).
  34. Votes uint `cbor:"2,keyasint"`
  35. // Public encodes the public key of the key. For 25519 keys,
  36. // this is simply the point on the curve representing the public
  37. // key.
  38. Public []byte `cbor:"3,keyasint"`
  39. // Meta describes arbitrary metadata about the key. This could be
  40. // used to store the name of the key, for instance.
  41. Meta map[string]string `cbor:"12,keyasint,omitempty"`
  42. }
  43. // Clone makes an independent copy of Key.
  44. //
  45. // NOTE: There is a difference between a nil slice and an empty slice for encoding purposes,
  46. // so an implementation of Clone() must take care to preserve this.
  47. func (k Key) Clone() Key {
  48. out := k
  49. if k.Public != nil {
  50. out.Public = make([]byte, len(k.Public))
  51. copy(out.Public, k.Public)
  52. }
  53. if k.Meta != nil {
  54. out.Meta = make(map[string]string, len(k.Meta))
  55. for k, v := range k.Meta {
  56. out.Meta[k] = v
  57. }
  58. }
  59. return out
  60. }
  61. // MustID returns the KeyID of the key, panicking if an error is
  62. // encountered. This must only be used for tests.
  63. func (k Key) MustID() tkatype.KeyID {
  64. id, err := k.ID()
  65. if err != nil {
  66. panic(err)
  67. }
  68. return id
  69. }
  70. // ID returns the KeyID of the key.
  71. func (k Key) ID() (tkatype.KeyID, error) {
  72. switch k.Kind {
  73. // Because 25519 public keys are so short, we just use the 32-byte
  74. // public as their 'key ID'.
  75. case Key25519:
  76. return tkatype.KeyID(k.Public), nil
  77. default:
  78. return nil, fmt.Errorf("unknown key kind: %v", k.Kind)
  79. }
  80. }
  81. // Ed25519 returns the ed25519 public key encoded by Key. An error is
  82. // returned for keys which do not represent ed25519 public keys.
  83. func (k Key) Ed25519() (ed25519.PublicKey, error) {
  84. switch k.Kind {
  85. case Key25519:
  86. return ed25519.PublicKey(k.Public), nil
  87. default:
  88. return nil, fmt.Errorf("key is of type %v, not ed25519", k.Kind)
  89. }
  90. }
  91. const maxMetaBytes = 512
  92. func (k Key) StaticValidate() error {
  93. if k.Votes > 4096 {
  94. return fmt.Errorf("excessive key weight: %d > 4096", k.Votes)
  95. }
  96. if k.Votes == 0 {
  97. return errors.New("key votes must be non-zero")
  98. }
  99. // We have an arbitrary upper limit on the amount
  100. // of metadata that can be associated with a key, so
  101. // people don't start using it as a key-value store and
  102. // causing pathological cases due to the number + size of
  103. // AUMs.
  104. var metaBytes uint
  105. for k, v := range k.Meta {
  106. metaBytes += uint(len(k) + len(v))
  107. }
  108. if metaBytes > maxMetaBytes {
  109. return fmt.Errorf("key metadata too big (%d > %d)", metaBytes, maxMetaBytes)
  110. }
  111. switch k.Kind {
  112. case Key25519:
  113. default:
  114. return fmt.Errorf("unrecognized key kind: %v", k.Kind)
  115. }
  116. return nil
  117. }
  118. // Verify returns a nil error if the signature is valid over the
  119. // provided AUM BLAKE2s digest, using the given key.
  120. func signatureVerify(s *tkatype.Signature, aumDigest tkatype.AUMSigHash, key Key) error {
  121. // NOTE(tom): Even if we can compute the public from the KeyID,
  122. // its possible for the KeyID to be attacker-controlled
  123. // so we should use the public contained in the state machine.
  124. switch key.Kind {
  125. case Key25519:
  126. if ed25519consensus.Verify(ed25519.PublicKey(key.Public), aumDigest[:], s.Signature) {
  127. return nil
  128. }
  129. return errors.New("invalid signature")
  130. default:
  131. return fmt.Errorf("unhandled key type: %v", key.Kind)
  132. }
  133. }