Update
This commit is contained in:
49
vendor/tailscale.com/tka/tka.go
generated
vendored
49
vendor/tailscale.com/tka/tka.go
generated
vendored
@@ -1,7 +1,9 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Package tka (WIP) implements the Tailnet Key Authority.
|
||||
//go:build !ts_omit_tailnetlock
|
||||
|
||||
// Package tka implements the Tailnet Key Authority (TKA) for Tailnet Lock.
|
||||
package tka
|
||||
|
||||
import (
|
||||
@@ -92,7 +94,7 @@ func computeChainCandidates(storage Chonk, lastKnownOldest *AUMHash, maxIter int
|
||||
|
||||
// candidates.Oldest needs to be computed by working backwards from
|
||||
// head as far as we can.
|
||||
iterAgain := true // if theres still work to be done.
|
||||
iterAgain := true // if there's still work to be done.
|
||||
for i := 0; iterAgain; i++ {
|
||||
if i >= maxIter {
|
||||
return nil, fmt.Errorf("iteration limit exceeded (%d)", maxIter)
|
||||
@@ -100,14 +102,14 @@ func computeChainCandidates(storage Chonk, lastKnownOldest *AUMHash, maxIter int
|
||||
|
||||
iterAgain = false
|
||||
for j := range candidates {
|
||||
parent, hasParent := candidates[j].Oldest.Parent()
|
||||
parentHash, hasParent := candidates[j].Oldest.Parent()
|
||||
if hasParent {
|
||||
parent, err := storage.AUM(parent)
|
||||
parent, err := storage.AUM(parentHash)
|
||||
if err != nil {
|
||||
if err == os.ErrNotExist {
|
||||
continue
|
||||
}
|
||||
return nil, fmt.Errorf("reading parent: %v", err)
|
||||
return nil, fmt.Errorf("reading parent %s: %v", parentHash, err)
|
||||
}
|
||||
candidates[j].Oldest = parent
|
||||
if lastKnownOldest != nil && *lastKnownOldest == parent.Hash() {
|
||||
@@ -208,7 +210,7 @@ func fastForwardWithAdvancer(
|
||||
}
|
||||
nextAUM, err := storage.AUM(*startState.LastAUMHash)
|
||||
if err != nil {
|
||||
return AUM{}, State{}, fmt.Errorf("reading next: %v", err)
|
||||
return AUM{}, State{}, fmt.Errorf("reading next (%v): %v", *startState.LastAUMHash, err)
|
||||
}
|
||||
|
||||
curs := nextAUM
|
||||
@@ -293,9 +295,9 @@ func computeStateAt(storage Chonk, maxIter int, wantHash AUMHash) (State, error)
|
||||
}
|
||||
|
||||
// If we got here, the current state is dependent on the previous.
|
||||
// Keep iterating backwards till thats not the case.
|
||||
// Keep iterating backwards till that's not the case.
|
||||
if curs, err = storage.AUM(parent); err != nil {
|
||||
return State{}, fmt.Errorf("reading parent: %v", err)
|
||||
return State{}, fmt.Errorf("reading parent (%v): %v", parent, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,7 +324,7 @@ func computeStateAt(storage Chonk, maxIter int, wantHash AUMHash) (State, error)
|
||||
return curs.Hash() == wantHash
|
||||
})
|
||||
// fastForward only terminates before the done condition if it
|
||||
// doesnt have any later AUMs to process. This cant be the case
|
||||
// doesn't have any later AUMs to process. This can't be the case
|
||||
// as we've already iterated through them above so they must exist,
|
||||
// but we check anyway to be super duper sure.
|
||||
if err == nil && *state.LastAUMHash != wantHash {
|
||||
@@ -334,13 +336,13 @@ func computeStateAt(storage Chonk, maxIter int, wantHash AUMHash) (State, error)
|
||||
// computeActiveAncestor determines which ancestor AUM to use as the
|
||||
// ancestor of the valid chain.
|
||||
//
|
||||
// If all the chains end up having the same ancestor, then thats the
|
||||
// If all the chains end up having the same ancestor, then that's the
|
||||
// only possible ancestor, ezpz. However if there are multiple distinct
|
||||
// ancestors, that means there are distinct chains, and we need some
|
||||
// hint to choose what to use. For that, we rely on the chainsThroughActive
|
||||
// bit, which signals to us that that ancestor was part of the
|
||||
// chain in a previous run.
|
||||
func computeActiveAncestor(storage Chonk, chains []chain) (AUMHash, error) {
|
||||
func computeActiveAncestor(chains []chain) (AUMHash, error) {
|
||||
// Dedupe possible ancestors, tracking if they were part of
|
||||
// the active chain on a previous run.
|
||||
ancestors := make(map[AUMHash]bool, len(chains))
|
||||
@@ -355,7 +357,7 @@ func computeActiveAncestor(storage Chonk, chains []chain) (AUMHash, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Theres more than one, so we need to use the ancestor that was
|
||||
// There's more than one, so we need to use the ancestor that was
|
||||
// part of the active chain in a previous iteration.
|
||||
// Note that there can only be one distinct ancestor that was
|
||||
// formerly part of the active chain, because AUMs can only have
|
||||
@@ -389,8 +391,12 @@ func computeActiveChain(storage Chonk, lastKnownOldest *AUMHash, maxIter int) (c
|
||||
return chain{}, fmt.Errorf("computing candidates: %v", err)
|
||||
}
|
||||
|
||||
if len(chains) == 0 {
|
||||
return chain{}, errors.New("no chain candidates in AUM storage")
|
||||
}
|
||||
|
||||
// Find the right ancestor.
|
||||
oldestHash, err := computeActiveAncestor(storage, chains)
|
||||
oldestHash, err := computeActiveAncestor(chains)
|
||||
if err != nil {
|
||||
return chain{}, fmt.Errorf("computing ancestor: %v", err)
|
||||
}
|
||||
@@ -440,6 +446,13 @@ func aumVerify(aum AUM, state State, isGenesisAUM bool) error {
|
||||
return fmt.Errorf("signature %d: %v", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
if aum.MessageKind == AUMRemoveKey && len(state.Keys) == 1 {
|
||||
if kid, err := state.Keys[0].ID(); err == nil && bytes.Equal(aum.KeyID, kid) {
|
||||
return errors.New("cannot remove the last key in the state")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -466,7 +479,7 @@ func (a *Authority) Head() AUMHash {
|
||||
// Open initializes an existing TKA from the given tailchonk.
|
||||
//
|
||||
// Only use this if the current node has initialized an Authority before.
|
||||
// If a TKA exists on other nodes but theres nothing locally, use Bootstrap().
|
||||
// If a TKA exists on other nodes but there's nothing locally, use Bootstrap().
|
||||
// If no TKA exists anywhere and you are creating it for the first
|
||||
// time, use New().
|
||||
func Open(storage Chonk) (*Authority, error) {
|
||||
@@ -579,14 +592,14 @@ func (a *Authority) InformIdempotent(storage Chonk, updates []AUM) (Authority, e
|
||||
toCommit := make([]AUM, 0, len(updates))
|
||||
prevHash := a.Head()
|
||||
|
||||
// The state at HEAD is the current state of the authority. Its likely
|
||||
// The state at HEAD is the current state of the authority. It's likely
|
||||
// to be needed, so we prefill it rather than computing it.
|
||||
stateAt[prevHash] = a.state
|
||||
|
||||
// Optimization: If the set of updates is a chain building from
|
||||
// the current head, EG:
|
||||
// <a.Head()> ==> updates[0] ==> updates[1] ...
|
||||
// Then theres no need to recompute the resulting state from the
|
||||
// Then there's no need to recompute the resulting state from the
|
||||
// stored ancestor, because the last state computed during iteration
|
||||
// is the new state. This should be the common case.
|
||||
// isHeadChain keeps track of this.
|
||||
@@ -766,8 +779,8 @@ func (a *Authority) findParentForRewrite(storage Chonk, removeKeys []tkatype.Key
|
||||
}
|
||||
}
|
||||
if !keyTrusted {
|
||||
// Success: the revoked keys are not trusted!
|
||||
// Lets check that our key was trusted to ensure
|
||||
// Success: the revoked keys are not trusted.
|
||||
// Check that our key was trusted to ensure
|
||||
// we can sign a fork from here.
|
||||
if _, err := state.GetKey(ourKey); err == nil {
|
||||
break
|
||||
|
||||
Reference in New Issue
Block a user