state.go 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. //go:build !ts_omit_tailnetlock
  4. package tka
  5. import (
  6. "bytes"
  7. "errors"
  8. "fmt"
  9. "golang.org/x/crypto/argon2"
  10. "tailscale.com/types/tkatype"
  11. )
  12. // ErrNoSuchKey is returned if the key referenced by a KeyID does not exist.
  13. var ErrNoSuchKey = errors.New("key not found")
  14. // State describes Tailnet Key Authority state at an instant in time.
  15. //
  16. // State is mutated by applying Authority Update Messages (AUMs), resulting
  17. // in a new State.
  18. type State struct {
  19. // LastAUMHash is the blake2s digest of the last-applied AUM.
  20. // Because AUMs are strictly ordered and form a hash chain, we
  21. // check the previous AUM hash in an update we are applying
  22. // is the same as the LastAUMHash.
  23. LastAUMHash *AUMHash `cbor:"1,keyasint"`
  24. // DisablementSecrets are KDF-derived values which can be used
  25. // to turn off the TKA in the event of a consensus-breaking bug.
  26. DisablementSecrets [][]byte `cbor:"2,keyasint"`
  27. // Keys are the public keys of either:
  28. //
  29. // 1. The signing nodes currently trusted by the TKA.
  30. // 2. Ephemeral keys that were used to generate pre-signed auth keys.
  31. Keys []Key `cbor:"3,keyasint"`
  32. // StateID's are nonce's, generated on enablement and fixed for
  33. // the lifetime of the Tailnet Key Authority. We generate 16-bytes
  34. // worth of keyspace here just in case we come up with a cool future
  35. // use for this.
  36. StateID1 uint64 `cbor:"4,keyasint,omitempty"`
  37. StateID2 uint64 `cbor:"5,keyasint,omitempty"`
  38. }
  39. // GetKey returns the trusted key with the specified KeyID.
  40. func (s State) GetKey(key tkatype.KeyID) (Key, error) {
  41. for _, k := range s.Keys {
  42. keyID, err := k.ID()
  43. if err != nil {
  44. return Key{}, err
  45. }
  46. if bytes.Equal(keyID, key) {
  47. return k, nil
  48. }
  49. }
  50. return Key{}, ErrNoSuchKey
  51. }
  52. // Clone makes an independent copy of State.
  53. //
  54. // NOTE: There is a difference between a nil slice and an empty
  55. // slice for encoding purposes, so an implementation of Clone()
  56. // must take care to preserve this.
  57. func (s State) Clone() State {
  58. out := State{
  59. StateID1: s.StateID1,
  60. StateID2: s.StateID2,
  61. }
  62. if s.LastAUMHash != nil {
  63. dupe := *s.LastAUMHash
  64. out.LastAUMHash = &dupe
  65. }
  66. if s.DisablementSecrets != nil {
  67. out.DisablementSecrets = make([][]byte, len(s.DisablementSecrets))
  68. for i := range s.DisablementSecrets {
  69. out.DisablementSecrets[i] = make([]byte, len(s.DisablementSecrets[i]))
  70. copy(out.DisablementSecrets[i], s.DisablementSecrets[i])
  71. }
  72. }
  73. if s.Keys != nil {
  74. out.Keys = make([]Key, len(s.Keys))
  75. for i := range s.Keys {
  76. out.Keys[i] = s.Keys[i].Clone()
  77. }
  78. }
  79. return out
  80. }
  81. // cloneForUpdate is like Clone, except LastAUMHash is set based
  82. // on the hash of the given update.
  83. func (s State) cloneForUpdate(update *AUM) State {
  84. out := s.Clone()
  85. aumHash := update.Hash()
  86. out.LastAUMHash = &aumHash
  87. return out
  88. }
  89. const disablementLength = 32
  90. var disablementSalt = []byte("tailscale network-lock disablement salt")
  91. // DisablementKDF computes a public value which can be stored in a
  92. // key authority, but cannot be reversed to find the input secret.
  93. //
  94. // When the output of this function is stored in tka state (i.e. in
  95. // tka.State.DisablementSecrets) a call to Authority.ValidDisablement()
  96. // with the input of this function as the argument will return true.
  97. func DisablementKDF(secret []byte) []byte {
  98. // time = 4 (3 recommended, booped to 4 to compensate for less memory)
  99. // memory = 16 (32 recommended)
  100. // threads = 4
  101. // keyLen = 32 (256 bits)
  102. return argon2.Key(secret, disablementSalt, 4, 16*1024, 4, disablementLength)
  103. }
  104. // checkDisablement returns true for a valid disablement secret.
  105. func (s State) checkDisablement(secret []byte) bool {
  106. derived := DisablementKDF(secret)
  107. for _, candidate := range s.DisablementSecrets {
  108. if bytes.Equal(derived, candidate) {
  109. return true
  110. }
  111. }
  112. return false
  113. }
  114. // parentMatches returns true if an AUM can chain to (be applied)
  115. // to the current state.
  116. //
  117. // Specifically, the rules are:
  118. // - The last AUM hash must match (transitively, this implies that this
  119. // update follows the last update message applied to the state machine)
  120. // - Or, the state machine knows no parent (it's brand new).
  121. func (s State) parentMatches(update AUM) bool {
  122. if s.LastAUMHash == nil {
  123. return true
  124. }
  125. return bytes.Equal(s.LastAUMHash[:], update.PrevAUMHash)
  126. }
  127. // applyVerifiedAUM computes a new state based on the update provided.
  128. //
  129. // The provided update MUST be verified: That is, the AUM must be well-formed
  130. // (as defined by StaticValidate()), and signatures over the AUM must have
  131. // been verified.
  132. func (s State) applyVerifiedAUM(update AUM) (State, error) {
  133. // Validate that the update message has the right parent.
  134. if !s.parentMatches(update) {
  135. return State{}, errors.New("parent AUMHash mismatch")
  136. }
  137. switch update.MessageKind {
  138. case AUMNoOp:
  139. out := s.cloneForUpdate(&update)
  140. return out, nil
  141. case AUMCheckpoint:
  142. if update.State == nil {
  143. return State{}, errors.New("missing checkpoint state")
  144. }
  145. id1Match, id2Match := update.State.StateID1 == s.StateID1, update.State.StateID2 == s.StateID2
  146. if !id1Match || !id2Match {
  147. return State{}, errors.New("checkpointed state has an incorrect stateID")
  148. }
  149. return update.State.cloneForUpdate(&update), nil
  150. case AUMAddKey:
  151. if update.Key == nil {
  152. return State{}, errors.New("no key to add provided")
  153. }
  154. keyID, err := update.Key.ID()
  155. if err != nil {
  156. return State{}, err
  157. }
  158. if _, err := s.GetKey(keyID); err == nil {
  159. return State{}, errors.New("key already exists")
  160. }
  161. out := s.cloneForUpdate(&update)
  162. out.Keys = append(out.Keys, *update.Key)
  163. return out, nil
  164. case AUMUpdateKey:
  165. k, err := s.GetKey(update.KeyID)
  166. if err != nil {
  167. return State{}, err
  168. }
  169. if update.Votes != nil {
  170. k.Votes = *update.Votes
  171. }
  172. if update.Meta != nil {
  173. k.Meta = update.Meta
  174. }
  175. if err := k.StaticValidate(); err != nil {
  176. return State{}, fmt.Errorf("updated key fails validation: %v", err)
  177. }
  178. out := s.cloneForUpdate(&update)
  179. for i := range out.Keys {
  180. keyID, err := out.Keys[i].ID()
  181. if err != nil {
  182. return State{}, err
  183. }
  184. if bytes.Equal(keyID, update.KeyID) {
  185. out.Keys[i] = k
  186. }
  187. }
  188. return out, nil
  189. case AUMRemoveKey:
  190. idx := -1
  191. for i := range s.Keys {
  192. keyID, err := s.Keys[i].ID()
  193. if err != nil {
  194. return State{}, err
  195. }
  196. if bytes.Equal(update.KeyID, keyID) {
  197. idx = i
  198. break
  199. }
  200. }
  201. if idx < 0 {
  202. return State{}, ErrNoSuchKey
  203. }
  204. out := s.cloneForUpdate(&update)
  205. out.Keys = append(out.Keys[:idx], out.Keys[idx+1:]...)
  206. return out, nil
  207. default:
  208. // An AUM with an unknown message kind was received! That means
  209. // that a future version of tailscaled added some feature we don't
  210. // understand.
  211. //
  212. // The future-compatibility contract for AUM message types is that
  213. // they must only add new features, not change the semantics of existing
  214. // mechanisms or features. As such, old clients can safely ignore them.
  215. out := s.cloneForUpdate(&update)
  216. return out, nil
  217. }
  218. }
  219. // Upper bound on checkpoint elements, chosen arbitrarily. Intended to
  220. // cap out insanely large AUMs.
  221. const (
  222. maxDisablementSecrets = 32
  223. maxKeys = 512
  224. )
  225. // staticValidateCheckpoint validates that the state is well-formed for
  226. // inclusion in a checkpoint AUM.
  227. func (s *State) staticValidateCheckpoint() error {
  228. if s.LastAUMHash != nil {
  229. return errors.New("cannot specify a parent AUM")
  230. }
  231. if len(s.DisablementSecrets) == 0 {
  232. return errors.New("at least one disablement secret required")
  233. }
  234. if numDS := len(s.DisablementSecrets); numDS > maxDisablementSecrets {
  235. return fmt.Errorf("too many disablement secrets (%d, max %d)", numDS, maxDisablementSecrets)
  236. }
  237. for i, ds := range s.DisablementSecrets {
  238. if len(ds) != disablementLength {
  239. return fmt.Errorf("disablement[%d]: invalid length (got %d, want %d)", i, len(ds), disablementLength)
  240. }
  241. for j, ds2 := range s.DisablementSecrets {
  242. if i == j {
  243. continue
  244. }
  245. if bytes.Equal(ds, ds2) {
  246. return fmt.Errorf("disablement[%d]: duplicates disablement[%d]", i, j)
  247. }
  248. }
  249. }
  250. if len(s.Keys) == 0 {
  251. return errors.New("at least one key is required")
  252. }
  253. if numKeys := len(s.Keys); numKeys > maxKeys {
  254. return fmt.Errorf("too many keys (%d, max %d)", numKeys, maxKeys)
  255. }
  256. for i, k := range s.Keys {
  257. if err := k.StaticValidate(); err != nil {
  258. return fmt.Errorf("key[%d]: %v", i, err)
  259. }
  260. }
  261. // NOTE: The max number of keys is constrained (512), so
  262. // O(n^2) is fine.
  263. for i, k := range s.Keys {
  264. for j, k2 := range s.Keys {
  265. if i == j {
  266. continue
  267. }
  268. id1, err := k.ID()
  269. if err != nil {
  270. return fmt.Errorf("key[%d]: %w", i, err)
  271. }
  272. id2, err := k2.ID()
  273. if err != nil {
  274. return fmt.Errorf("key[%d]: %w", j, err)
  275. }
  276. if bytes.Equal(id1, id2) {
  277. return fmt.Errorf("key[%d]: duplicates key[%d]", i, j)
  278. }
  279. }
  280. }
  281. return nil
  282. }