tka: refer consistently to "DisablementValues"
This avoids putting "DisablementSecrets" in the JSON output from `tailscale lock log`, which is potentially scary to somebody who doesn't understand the distinction. AUMs are stored and transmitted in CBOR-encoded format, which uses an integer rather than a string key, so this doesn't break already-created TKAs. Fixes #19189 Change-Id: I15b4e81a7cef724a450bafcfa0b938da223c78c9 Signed-off-by: Alex Chan <alexc@tailscale.com>
This commit is contained in:
@@ -28,8 +28,6 @@ func (lc *Client) NetworkLockStatus(ctx context.Context) (*ipnstate.NetworkLockS
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NetworkLockInit initializes the tailnet key authority.
|
// NetworkLockInit initializes the tailnet key authority.
|
||||||
//
|
|
||||||
// TODO(tom): Plumb through disablement secrets.
|
|
||||||
func (lc *Client) NetworkLockInit(ctx context.Context, keys []tka.Key, disablementValues [][]byte, supportDisablement []byte) (*ipnstate.NetworkLockStatus, error) {
|
func (lc *Client) NetworkLockInit(ctx context.Context, keys []tka.Key, disablementValues [][]byte, supportDisablement []byte) (*ipnstate.NetworkLockStatus, error) {
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
type initRequest struct {
|
type initRequest struct {
|
||||||
|
|||||||
@@ -76,8 +76,8 @@ func toLogMessageV1(aum tka.AUM, update ipnstate.NetworkLockUpdate) logMessageV1
|
|||||||
if h := state.LastAUMHash; h != nil {
|
if h := state.LastAUMHash; h != nil {
|
||||||
expandedState.LastAUMHash = h.String()
|
expandedState.LastAUMHash = h.String()
|
||||||
}
|
}
|
||||||
for _, secret := range state.DisablementSecrets {
|
for _, secret := range state.DisablementValues {
|
||||||
expandedState.DisablementSecrets = append(expandedState.DisablementSecrets, fmt.Sprintf("%x", secret))
|
expandedState.DisablementValues = append(expandedState.DisablementValues, fmt.Sprintf("%x", secret))
|
||||||
}
|
}
|
||||||
for _, key := range state.Keys {
|
for _, key := range state.Keys {
|
||||||
expandedState.Keys = append(expandedState.Keys, toTKAKeyV1(&key))
|
expandedState.Keys = append(expandedState.Keys, toTKAKeyV1(&key))
|
||||||
@@ -180,9 +180,13 @@ type expandedStateV1 struct {
|
|||||||
// LastAUMHash is the blake2s digest of the last-applied AUM.
|
// LastAUMHash is the blake2s digest of the last-applied AUM.
|
||||||
LastAUMHash string `json:"LastAUMHash,omitzero"`
|
LastAUMHash string `json:"LastAUMHash,omitzero"`
|
||||||
|
|
||||||
// DisablementSecrets are KDF-derived values which can be used
|
// DisablementValues are KDF-derived values used to verify that a caller
|
||||||
// to turn off the TKA in the event of a consensus-breaking bug.
|
// possesses a valid DisablementSecret. These values are used during the
|
||||||
DisablementSecrets []string
|
// Tailnet Lock deactivation process.
|
||||||
|
//
|
||||||
|
// These are safe to share publicly or store in the clear. They cannot be
|
||||||
|
// used to derive the original DisablementSecret.
|
||||||
|
DisablementValues []string
|
||||||
|
|
||||||
// Keys are the public keys of either:
|
// Keys are the public keys of either:
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -672,7 +672,7 @@ func nlDescribeUpdate(update ipnstate.NetworkLockUpdate, color bool) (string, er
|
|||||||
|
|
||||||
case tka.AUMCheckpoint.String():
|
case tka.AUMCheckpoint.String():
|
||||||
fmt.Fprintln(&stanza, "Disablement values:")
|
fmt.Fprintln(&stanza, "Disablement values:")
|
||||||
for _, v := range aum.State.DisablementSecrets {
|
for _, v := range aum.State.DisablementValues {
|
||||||
fmt.Fprintf(&stanza, " - %x\n", v)
|
fmt.Fprintf(&stanza, " - %x\n", v)
|
||||||
}
|
}
|
||||||
fmt.Fprintln(&stanza, "Keys:")
|
fmt.Fprintln(&stanza, "Keys:")
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ func TestNetworkLockLogOutput(t *testing.T) {
|
|||||||
Meta: map[string]string{"en": "one", "de": "eins", "es": "uno"},
|
Meta: map[string]string{"en": "one", "de": "eins", "es": "uno"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
DisablementSecrets: [][]byte{
|
DisablementValues: [][]byte{
|
||||||
{1, 2, 3},
|
{1, 2, 3},
|
||||||
{4, 5, 6},
|
{4, 5, 6},
|
||||||
{7, 8, 9},
|
{7, 8, 9},
|
||||||
@@ -125,7 +125,7 @@ KeyID: tlpub:0202
|
|||||||
"MessageKind": "checkpoint",
|
"MessageKind": "checkpoint",
|
||||||
"PrevAUMHash": "BKVVXHOVBW7Y7YXYTLVVLMNSYG6DS5GVRVSYZLASNU3AQKA732XQ",
|
"PrevAUMHash": "BKVVXHOVBW7Y7YXYTLVVLMNSYG6DS5GVRVSYZLASNU3AQKA732XQ",
|
||||||
"State": {
|
"State": {
|
||||||
"DisablementSecrets": [
|
"DisablementValues": [
|
||||||
"010203",
|
"010203",
|
||||||
"040506",
|
"040506",
|
||||||
"070809"
|
"070809"
|
||||||
|
|||||||
@@ -654,15 +654,10 @@ func (b *LocalBackend) NetworkLockInit(keys []tka.Key, disablementValues [][]byt
|
|||||||
// the filesystem until we've finished the initialization sequence,
|
// the filesystem until we've finished the initialization sequence,
|
||||||
// just in case something goes wrong.
|
// just in case something goes wrong.
|
||||||
_, genesisAUM, err := tka.Create(tka.ChonkMem(), tka.State{
|
_, genesisAUM, err := tka.Create(tka.ChonkMem(), tka.State{
|
||||||
Keys: keys,
|
Keys: keys,
|
||||||
// TODO(tom): s/tka.State.DisablementSecrets/tka.State.DisablementValues
|
DisablementValues: disablementValues,
|
||||||
// This will center on consistent nomenclature:
|
StateID1: binary.LittleEndian.Uint64(entropy[:8]),
|
||||||
// - DisablementSecret: value needed to disable.
|
StateID2: binary.LittleEndian.Uint64(entropy[8:]),
|
||||||
// - DisablementValue: the KDF of the disablement secret, a public value.
|
|
||||||
DisablementSecrets: disablementValues,
|
|
||||||
|
|
||||||
StateID1: binary.LittleEndian.Uint64(entropy[:8]),
|
|
||||||
StateID2: binary.LittleEndian.Uint64(entropy[8:]),
|
|
||||||
}, nlPriv)
|
}, nlPriv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("tka.Create: %v", err)
|
return fmt.Errorf("tka.Create: %v", err)
|
||||||
|
|||||||
@@ -104,8 +104,8 @@ func TestTKAEnablementFlow(t *testing.T) {
|
|||||||
key := tka.Key{Kind: tka.Key25519, Public: nlPriv.Public().Verifier(), Votes: 2}
|
key := tka.Key{Kind: tka.Key25519, Public: nlPriv.Public().Verifier(), Votes: 2}
|
||||||
chonk := tka.ChonkMem()
|
chonk := tka.ChonkMem()
|
||||||
a1, genesisAUM, err := tka.Create(chonk, tka.State{
|
a1, genesisAUM, err := tka.Create(chonk, tka.State{
|
||||||
Keys: []tka.Key{key},
|
Keys: []tka.Key{key},
|
||||||
DisablementSecrets: [][]byte{bytes.Repeat([]byte{0xa5}, 32)},
|
DisablementValues: [][]byte{bytes.Repeat([]byte{0xa5}, 32)},
|
||||||
}, nlPriv)
|
}, nlPriv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("tka.Create() failed: %v", err)
|
t.Fatalf("tka.Create() failed: %v", err)
|
||||||
@@ -195,8 +195,8 @@ func TestTKADisablementFlow(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
authority, _, err := tka.Create(chonk, tka.State{
|
authority, _, err := tka.Create(chonk, tka.State{
|
||||||
Keys: []tka.Key{key},
|
Keys: []tka.Key{key},
|
||||||
DisablementSecrets: [][]byte{tka.DisablementKDF(disablementSecret)},
|
DisablementValues: [][]byte{tka.DisablementKDF(disablementSecret)},
|
||||||
}, nlPriv)
|
}, nlPriv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("tka.Create() failed: %v", err)
|
t.Fatalf("tka.Create() failed: %v", err)
|
||||||
@@ -368,8 +368,8 @@ func TestTKASync(t *testing.T) {
|
|||||||
key := tka.Key{Kind: tka.Key25519, Public: nlPriv.Public().Verifier(), Votes: 2}
|
key := tka.Key{Kind: tka.Key25519, Public: nlPriv.Public().Verifier(), Votes: 2}
|
||||||
controlStorage := tka.ChonkMem()
|
controlStorage := tka.ChonkMem()
|
||||||
controlAuthority, bootstrap, err := tka.Create(controlStorage, tka.State{
|
controlAuthority, bootstrap, err := tka.Create(controlStorage, tka.State{
|
||||||
Keys: []tka.Key{key, someKey},
|
Keys: []tka.Key{key, someKey},
|
||||||
DisablementSecrets: [][]byte{tka.DisablementKDF(disablementSecret)},
|
DisablementValues: [][]byte{tka.DisablementKDF(disablementSecret)},
|
||||||
}, nlPriv)
|
}, nlPriv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("tka.Create() failed: %v", err)
|
t.Fatalf("tka.Create() failed: %v", err)
|
||||||
@@ -478,8 +478,8 @@ func TestTKASyncTriggersCompact(t *testing.T) {
|
|||||||
controlStorage := tka.ChonkMem()
|
controlStorage := tka.ChonkMem()
|
||||||
controlStorage.SetClock(clock)
|
controlStorage.SetClock(clock)
|
||||||
controlAuthority, bootstrap, err := tka.Create(controlStorage, tka.State{
|
controlAuthority, bootstrap, err := tka.Create(controlStorage, tka.State{
|
||||||
Keys: []tka.Key{key, someKey},
|
Keys: []tka.Key{key, someKey},
|
||||||
DisablementSecrets: [][]byte{tka.DisablementKDF(disablementSecret)},
|
DisablementValues: [][]byte{tka.DisablementKDF(disablementSecret)},
|
||||||
}, nlPriv)
|
}, nlPriv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("tka.Create() failed: %v", err)
|
t.Fatalf("tka.Create() failed: %v", err)
|
||||||
@@ -608,8 +608,8 @@ func TestTKAFilterNetmap(t *testing.T) {
|
|||||||
nlKey := tka.Key{Kind: tka.Key25519, Public: nlPriv.Public().Verifier(), Votes: 2}
|
nlKey := tka.Key{Kind: tka.Key25519, Public: nlPriv.Public().Verifier(), Votes: 2}
|
||||||
storage := tka.ChonkMem()
|
storage := tka.ChonkMem()
|
||||||
authority, _, err := tka.Create(storage, tka.State{
|
authority, _, err := tka.Create(storage, tka.State{
|
||||||
Keys: []tka.Key{nlKey},
|
Keys: []tka.Key{nlKey},
|
||||||
DisablementSecrets: [][]byte{bytes.Repeat([]byte{0xa5}, 32)},
|
DisablementValues: [][]byte{bytes.Repeat([]byte{0xa5}, 32)},
|
||||||
}, nlPriv)
|
}, nlPriv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("tka.Create() failed: %v", err)
|
t.Fatalf("tka.Create() failed: %v", err)
|
||||||
@@ -771,8 +771,8 @@ func TestTKADisable(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
authority, _, err := tka.Create(chonk, tka.State{
|
authority, _, err := tka.Create(chonk, tka.State{
|
||||||
Keys: []tka.Key{key},
|
Keys: []tka.Key{key},
|
||||||
DisablementSecrets: [][]byte{tka.DisablementKDF(disablementSecret)},
|
DisablementValues: [][]byte{tka.DisablementKDF(disablementSecret)},
|
||||||
}, nlPriv)
|
}, nlPriv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("tka.Create() failed: %v", err)
|
t.Fatalf("tka.Create() failed: %v", err)
|
||||||
@@ -859,8 +859,8 @@ func TestTKASign(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
authority, _, err := tka.Create(chonk, tka.State{
|
authority, _, err := tka.Create(chonk, tka.State{
|
||||||
Keys: []tka.Key{key},
|
Keys: []tka.Key{key},
|
||||||
DisablementSecrets: [][]byte{tka.DisablementKDF(disablementSecret)},
|
DisablementValues: [][]byte{tka.DisablementKDF(disablementSecret)},
|
||||||
}, nlPriv)
|
}, nlPriv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("tka.Create() failed: %v", err)
|
t.Fatalf("tka.Create() failed: %v", err)
|
||||||
@@ -918,8 +918,8 @@ func TestTKAForceDisable(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
authority, genesis, err := tka.Create(chonk, tka.State{
|
authority, genesis, err := tka.Create(chonk, tka.State{
|
||||||
Keys: []tka.Key{key},
|
Keys: []tka.Key{key},
|
||||||
DisablementSecrets: [][]byte{tka.DisablementKDF(disablementSecret)},
|
DisablementValues: [][]byte{tka.DisablementKDF(disablementSecret)},
|
||||||
}, nlPriv)
|
}, nlPriv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("tka.Create() failed: %v", err)
|
t.Fatalf("tka.Create() failed: %v", err)
|
||||||
@@ -1006,8 +1006,8 @@ func TestTKAAffectedSigs(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
authority, _, err := tka.Create(chonk, tka.State{
|
authority, _, err := tka.Create(chonk, tka.State{
|
||||||
Keys: []tka.Key{tkaKey},
|
Keys: []tka.Key{tkaKey},
|
||||||
DisablementSecrets: [][]byte{tka.DisablementKDF(disablementSecret)},
|
DisablementValues: [][]byte{tka.DisablementKDF(disablementSecret)},
|
||||||
}, nlPriv)
|
}, nlPriv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("tka.Create() failed: %v", err)
|
t.Fatalf("tka.Create() failed: %v", err)
|
||||||
@@ -1135,8 +1135,8 @@ func TestTKARecoverCompromisedKeyFlow(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
authority, _, err := tka.Create(chonk, tka.State{
|
authority, _, err := tka.Create(chonk, tka.State{
|
||||||
Keys: []tka.Key{key, compromisedKey, cosignKey},
|
Keys: []tka.Key{key, compromisedKey, cosignKey},
|
||||||
DisablementSecrets: [][]byte{tka.DisablementKDF(disablementSecret)},
|
DisablementValues: [][]byte{tka.DisablementKDF(disablementSecret)},
|
||||||
}, nlPriv)
|
}, nlPriv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("tka.Create() failed: %v", err)
|
t.Fatalf("tka.Create() failed: %v", err)
|
||||||
|
|||||||
+2
-2
@@ -104,7 +104,7 @@ func TestSerialization(t *testing.T) {
|
|||||||
},
|
},
|
||||||
bytes.Repeat([]byte{0}, 32)...),
|
bytes.Repeat([]byte{0}, 32)...),
|
||||||
[]byte{
|
[]byte{
|
||||||
0x02, // |- major type 0 (int), value 2 (second key, DisablementSecrets)
|
0x02, // |- major type 0 (int), value 2 (second key, DisablementValues)
|
||||||
0xf6, // |- major type 7 (val), value null (second value, nil)
|
0xf6, // |- major type 7 (val), value null (second value, nil)
|
||||||
0x03, // |- major type 0 (int), value 3 (third key, Keys)
|
0x03, // |- major type 0 (int), value 3 (third key, Keys)
|
||||||
0x81, // |- major type 4 (array), value 1 (one item in array)
|
0x81, // |- major type 4 (array), value 1 (one item in array)
|
||||||
@@ -182,7 +182,7 @@ func TestDeserializeExistingAUMs(t *testing.T) {
|
|||||||
Want: AUM{
|
Want: AUM{
|
||||||
MessageKind: AUMCheckpoint,
|
MessageKind: AUMCheckpoint,
|
||||||
State: &State{
|
State: &State{
|
||||||
DisablementSecrets: [][]byte{
|
DisablementValues: [][]byte{
|
||||||
fromBase64("jSwtotIRlTdbkNPV0bZZifOMIGvi1e1VsJPYu8D0tLo="),
|
fromBase64("jSwtotIRlTdbkNPV0bZZifOMIGvi1e1VsJPYu8D0tLo="),
|
||||||
fromBase64("EIcFRg4lBkYrtz+t4LnGf/KLY7dg18pPjgY24eYlsdQ="),
|
fromBase64("EIcFRg4lBkYrtz+t4LnGf/KLY7dg18pPjgY24eYlsdQ="),
|
||||||
fromBase64("5VU4oRQiMoq5qK00McfpwtmjcheVammLCRwzdp2Zje8="),
|
fromBase64("5VU4oRQiMoq5qK00McfpwtmjcheVammLCRwzdp2Zje8="),
|
||||||
|
|||||||
+14
-14
@@ -30,8 +30,8 @@ func TestAuthorityBuilderAddKey(t *testing.T) {
|
|||||||
|
|
||||||
storage := ChonkMem()
|
storage := ChonkMem()
|
||||||
a, _, err := Create(storage, State{
|
a, _, err := Create(storage, State{
|
||||||
Keys: []Key{key},
|
Keys: []Key{key},
|
||||||
DisablementSecrets: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
DisablementValues: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
||||||
}, signer25519(priv))
|
}, signer25519(priv))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Create() failed: %v", err)
|
t.Fatalf("Create() failed: %v", err)
|
||||||
@@ -64,8 +64,8 @@ func TestAuthorityBuilderMaxKey(t *testing.T) {
|
|||||||
|
|
||||||
storage := ChonkMem()
|
storage := ChonkMem()
|
||||||
a, _, err := Create(storage, State{
|
a, _, err := Create(storage, State{
|
||||||
Keys: []Key{key},
|
Keys: []Key{key},
|
||||||
DisablementSecrets: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
DisablementValues: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
||||||
}, signer25519(priv))
|
}, signer25519(priv))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Create() failed: %v", err)
|
t.Fatalf("Create() failed: %v", err)
|
||||||
@@ -111,8 +111,8 @@ func TestAuthorityBuilderRemoveKey(t *testing.T) {
|
|||||||
|
|
||||||
storage := ChonkMem()
|
storage := ChonkMem()
|
||||||
a, _, err := Create(storage, State{
|
a, _, err := Create(storage, State{
|
||||||
Keys: []Key{key, key2},
|
Keys: []Key{key, key2},
|
||||||
DisablementSecrets: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
DisablementValues: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
||||||
}, signer25519(priv))
|
}, signer25519(priv))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Create() failed: %v", err)
|
t.Fatalf("Create() failed: %v", err)
|
||||||
@@ -157,8 +157,8 @@ func TestAuthorityBuilderSetKeyVote(t *testing.T) {
|
|||||||
|
|
||||||
storage := ChonkMem()
|
storage := ChonkMem()
|
||||||
a, _, err := Create(storage, State{
|
a, _, err := Create(storage, State{
|
||||||
Keys: []Key{key},
|
Keys: []Key{key},
|
||||||
DisablementSecrets: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
DisablementValues: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
||||||
}, signer25519(priv))
|
}, signer25519(priv))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Create() failed: %v", err)
|
t.Fatalf("Create() failed: %v", err)
|
||||||
@@ -193,8 +193,8 @@ func TestAuthorityBuilderSetKeyMeta(t *testing.T) {
|
|||||||
|
|
||||||
storage := ChonkMem()
|
storage := ChonkMem()
|
||||||
a, _, err := Create(storage, State{
|
a, _, err := Create(storage, State{
|
||||||
Keys: []Key{key},
|
Keys: []Key{key},
|
||||||
DisablementSecrets: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
DisablementValues: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
||||||
}, signer25519(priv))
|
}, signer25519(priv))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Create() failed: %v", err)
|
t.Fatalf("Create() failed: %v", err)
|
||||||
@@ -229,8 +229,8 @@ func TestAuthorityBuilderMultiple(t *testing.T) {
|
|||||||
|
|
||||||
storage := ChonkMem()
|
storage := ChonkMem()
|
||||||
a, _, err := Create(storage, State{
|
a, _, err := Create(storage, State{
|
||||||
Keys: []Key{key},
|
Keys: []Key{key},
|
||||||
DisablementSecrets: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
DisablementValues: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
||||||
}, signer25519(priv))
|
}, signer25519(priv))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Create() failed: %v", err)
|
t.Fatalf("Create() failed: %v", err)
|
||||||
@@ -277,8 +277,8 @@ func TestAuthorityBuilderCheckpointsAfterXUpdates(t *testing.T) {
|
|||||||
|
|
||||||
storage := ChonkMem()
|
storage := ChonkMem()
|
||||||
a, _, err := Create(storage, State{
|
a, _, err := Create(storage, State{
|
||||||
Keys: []Key{key},
|
Keys: []Key{key},
|
||||||
DisablementSecrets: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
DisablementValues: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
||||||
}, signer25519(priv))
|
}, signer25519(priv))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Create() failed: %v", err)
|
t.Fatalf("Create() failed: %v", err)
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ func TestGenerateDeeplink(t *testing.T) {
|
|||||||
G1.template = genesis
|
G1.template = genesis
|
||||||
`,
|
`,
|
||||||
optTemplate("genesis", AUM{MessageKind: AUMCheckpoint, State: &State{
|
optTemplate("genesis", AUM{MessageKind: AUMCheckpoint, State: &State{
|
||||||
Keys: []Key{key},
|
Keys: []Key{key},
|
||||||
DisablementSecrets: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
DisablementValues: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
||||||
}}),
|
}}),
|
||||||
)
|
)
|
||||||
a, _ := Open(c.Chonk())
|
a, _ := Open(c.Chonk())
|
||||||
|
|||||||
+2
-2
@@ -73,8 +73,8 @@ func TestNLPrivate(t *testing.T) {
|
|||||||
// authority.
|
// authority.
|
||||||
k := Key{Kind: Key25519, Public: pub.Verifier(), Votes: 1}
|
k := Key{Kind: Key25519, Public: pub.Verifier(), Votes: 1}
|
||||||
_, aum, err := Create(ChonkMem(), State{
|
_, aum, err := Create(ChonkMem(), State{
|
||||||
Keys: []Key{k},
|
Keys: []Key{k},
|
||||||
DisablementSecrets: [][]byte{bytes.Repeat([]byte{1}, 32)},
|
DisablementValues: [][]byte{bytes.Repeat([]byte{1}, 32)},
|
||||||
}, p)
|
}, p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Create() failed: %v", err)
|
t.Fatalf("Create() failed: %v", err)
|
||||||
|
|||||||
@@ -148,8 +148,8 @@ func testScenario(t *testing.T, sharedChain string, sharedOptions ...testchainOp
|
|||||||
key := Key{Kind: Key25519, Public: pub, Votes: 1}
|
key := Key{Kind: Key25519, Public: pub, Votes: 1}
|
||||||
sharedOptions = append(sharedOptions,
|
sharedOptions = append(sharedOptions,
|
||||||
optTemplate("genesis", AUM{MessageKind: AUMCheckpoint, State: &State{
|
optTemplate("genesis", AUM{MessageKind: AUMCheckpoint, State: &State{
|
||||||
Keys: []Key{key},
|
Keys: []Key{key},
|
||||||
DisablementSecrets: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
DisablementValues: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
||||||
}}),
|
}}),
|
||||||
optKey("key", key, priv),
|
optKey("key", key, priv),
|
||||||
optSignAllUsing("key"))
|
optSignAllUsing("key"))
|
||||||
|
|||||||
+4
-4
@@ -175,8 +175,8 @@ func TestSigNested_DeepNesting(t *testing.T) {
|
|||||||
// Test this works with our public API
|
// Test this works with our public API
|
||||||
a, _ := Open(newTestchain(t, "G1\nG1.template = genesis",
|
a, _ := Open(newTestchain(t, "G1\nG1.template = genesis",
|
||||||
optTemplate("genesis", AUM{MessageKind: AUMCheckpoint, State: &State{
|
optTemplate("genesis", AUM{MessageKind: AUMCheckpoint, State: &State{
|
||||||
Keys: []Key{k},
|
Keys: []Key{k},
|
||||||
DisablementSecrets: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
DisablementValues: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
||||||
}})).Chonk())
|
}})).Chonk())
|
||||||
if err := a.NodeKeyAuthorized(lastNodeKey.Public(), outer.Serialize()); err != nil {
|
if err := a.NodeKeyAuthorized(lastNodeKey.Public(), outer.Serialize()); err != nil {
|
||||||
t.Errorf("NodeKeyAuthorized(lastNodeKey) failed: %v", err)
|
t.Errorf("NodeKeyAuthorized(lastNodeKey) failed: %v", err)
|
||||||
@@ -240,8 +240,8 @@ func TestSigCredential(t *testing.T) {
|
|||||||
// Test someone can't misuse our public API for verifying node-keys
|
// Test someone can't misuse our public API for verifying node-keys
|
||||||
a, _ := Open(newTestchain(t, "G1\nG1.template = genesis",
|
a, _ := Open(newTestchain(t, "G1\nG1.template = genesis",
|
||||||
optTemplate("genesis", AUM{MessageKind: AUMCheckpoint, State: &State{
|
optTemplate("genesis", AUM{MessageKind: AUMCheckpoint, State: &State{
|
||||||
Keys: []Key{k},
|
Keys: []Key{k},
|
||||||
DisablementSecrets: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
DisablementValues: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
||||||
}})).Chonk())
|
}})).Chonk())
|
||||||
if err := a.NodeKeyAuthorized(node.Public(), nestedSig.Serialize()); err == nil {
|
if err := a.NodeKeyAuthorized(node.Public(), nestedSig.Serialize()); err == nil {
|
||||||
t.Error("NodeKeyAuthorized(SigCredential, node) did not fail")
|
t.Error("NodeKeyAuthorized(SigCredential, node) did not fail")
|
||||||
|
|||||||
+18
-14
@@ -29,9 +29,13 @@ type State struct {
|
|||||||
// is the same as the LastAUMHash.
|
// is the same as the LastAUMHash.
|
||||||
LastAUMHash *AUMHash `cbor:"1,keyasint"`
|
LastAUMHash *AUMHash `cbor:"1,keyasint"`
|
||||||
|
|
||||||
// DisablementSecrets are KDF-derived values which can be used
|
// DisablementValues are KDF-derived values used to verify that a caller
|
||||||
// to turn off the TKA in the event of a consensus-breaking bug.
|
// possesses a valid DisablementSecret. These values are used during the
|
||||||
DisablementSecrets [][]byte `cbor:"2,keyasint"`
|
// Tailnet Lock deactivation process.
|
||||||
|
//
|
||||||
|
// These are safe to share publicly or store in the clear. They cannot be
|
||||||
|
// used to derive the original DisablementSecret.
|
||||||
|
DisablementValues [][]byte `cbor:"2,keyasint"`
|
||||||
|
|
||||||
// Keys are the public keys of either:
|
// Keys are the public keys of either:
|
||||||
//
|
//
|
||||||
@@ -79,11 +83,11 @@ func (s State) Clone() State {
|
|||||||
out.LastAUMHash = &dupe
|
out.LastAUMHash = &dupe
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.DisablementSecrets != nil {
|
if s.DisablementValues != nil {
|
||||||
out.DisablementSecrets = make([][]byte, len(s.DisablementSecrets))
|
out.DisablementValues = make([][]byte, len(s.DisablementValues))
|
||||||
for i := range s.DisablementSecrets {
|
for i := range s.DisablementValues {
|
||||||
out.DisablementSecrets[i] = make([]byte, len(s.DisablementSecrets[i]))
|
out.DisablementValues[i] = make([]byte, len(s.DisablementValues[i]))
|
||||||
copy(out.DisablementSecrets[i], s.DisablementSecrets[i])
|
copy(out.DisablementValues[i], s.DisablementValues[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +118,7 @@ var disablementSalt = []byte("tailscale network-lock disablement salt")
|
|||||||
// key authority, but cannot be reversed to find the input secret.
|
// key authority, but cannot be reversed to find the input secret.
|
||||||
//
|
//
|
||||||
// When the output of this function is stored in tka state (i.e. in
|
// When the output of this function is stored in tka state (i.e. in
|
||||||
// tka.State.DisablementSecrets) a call to Authority.ValidDisablement()
|
// tka.State.DisablementValues) a call to Authority.ValidDisablement()
|
||||||
// with the input of this function as the argument will return true.
|
// with the input of this function as the argument will return true.
|
||||||
func DisablementKDF(secret []byte) []byte {
|
func DisablementKDF(secret []byte) []byte {
|
||||||
// time = 4 (3 recommended, booped to 4 to compensate for less memory)
|
// time = 4 (3 recommended, booped to 4 to compensate for less memory)
|
||||||
@@ -127,7 +131,7 @@ func DisablementKDF(secret []byte) []byte {
|
|||||||
// checkDisablement returns true for a valid disablement secret.
|
// checkDisablement returns true for a valid disablement secret.
|
||||||
func (s State) checkDisablement(secret []byte) bool {
|
func (s State) checkDisablement(secret []byte) bool {
|
||||||
derived := DisablementKDF(secret)
|
derived := DisablementKDF(secret)
|
||||||
for _, candidate := range s.DisablementSecrets {
|
for _, candidate := range s.DisablementValues {
|
||||||
if subtle.ConstantTimeCompare(derived, candidate) == 1 {
|
if subtle.ConstantTimeCompare(derived, candidate) == 1 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -254,17 +258,17 @@ func (s *State) staticValidateCheckpoint() error {
|
|||||||
if s.LastAUMHash != nil {
|
if s.LastAUMHash != nil {
|
||||||
return errors.New("cannot specify a parent AUM")
|
return errors.New("cannot specify a parent AUM")
|
||||||
}
|
}
|
||||||
if len(s.DisablementSecrets) == 0 {
|
if len(s.DisablementValues) == 0 {
|
||||||
return errors.New("at least one disablement secret required")
|
return errors.New("at least one disablement secret required")
|
||||||
}
|
}
|
||||||
if numDS := len(s.DisablementSecrets); numDS > maxDisablementSecrets {
|
if numDS := len(s.DisablementValues); numDS > maxDisablementSecrets {
|
||||||
return fmt.Errorf("too many disablement secrets (%d, max %d)", numDS, maxDisablementSecrets)
|
return fmt.Errorf("too many disablement secrets (%d, max %d)", numDS, maxDisablementSecrets)
|
||||||
}
|
}
|
||||||
for i, ds := range s.DisablementSecrets {
|
for i, ds := range s.DisablementValues {
|
||||||
if len(ds) != disablementLength {
|
if len(ds) != disablementLength {
|
||||||
return fmt.Errorf("disablement[%d]: invalid length (got %d, want %d)", i, len(ds), disablementLength)
|
return fmt.Errorf("disablement[%d]: invalid length (got %d, want %d)", i, len(ds), disablementLength)
|
||||||
}
|
}
|
||||||
for j, ds2 := range s.DisablementSecrets {
|
for j, ds2 := range s.DisablementValues {
|
||||||
if i == j {
|
if i == j {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-10
@@ -36,26 +36,26 @@ func TestCloneState(t *testing.T) {
|
|||||||
State State
|
State State
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"Empty",
|
Name: "Empty",
|
||||||
State{},
|
State: State{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Key",
|
Name: "Key",
|
||||||
State{
|
State: State{
|
||||||
Keys: []Key{{Kind: Key25519, Votes: 2, Public: []byte{5, 6, 7, 8}, Meta: map[string]string{"a": "b"}}},
|
Keys: []Key{{Kind: Key25519, Votes: 2, Public: []byte{5, 6, 7, 8}, Meta: map[string]string{"a": "b"}}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"StateID",
|
Name: "StateID",
|
||||||
State{
|
State: State{
|
||||||
StateID1: 42,
|
StateID1: 42,
|
||||||
StateID2: 22,
|
StateID2: 22,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"DisablementSecrets",
|
Name: "DisablementValues",
|
||||||
State{
|
State: State{
|
||||||
DisablementSecrets: [][]byte{
|
DisablementValues: [][]byte{
|
||||||
{1, 2, 3, 4},
|
{1, 2, 3, 4},
|
||||||
{5, 6, 7, 8},
|
{5, 6, 7, 8},
|
||||||
},
|
},
|
||||||
@@ -155,7 +155,7 @@ func TestApplyUpdatesChain(t *testing.T) {
|
|||||||
Keys: []Key{{Kind: Key25519, Public: []byte{1, 2, 3, 4}}},
|
Keys: []Key{{Kind: Key25519, Public: []byte{1, 2, 3, 4}}},
|
||||||
}, PrevAUMHash: fromHex("f09bda3bb7cf6756ea9adc25770aede4b3ca8142949d6ef5ca0add29af912fd4")},
|
}, PrevAUMHash: fromHex("f09bda3bb7cf6756ea9adc25770aede4b3ca8142949d6ef5ca0add29af912fd4")},
|
||||||
},
|
},
|
||||||
State{DisablementSecrets: [][]byte{{1, 2, 3, 4}}},
|
State{DisablementValues: [][]byte{{1, 2, 3, 4}}},
|
||||||
State{
|
State{
|
||||||
Keys: []Key{{Kind: Key25519, Public: []byte{1, 2, 3, 4}}},
|
Keys: []Key{{Kind: Key25519, Public: []byte{1, 2, 3, 4}}},
|
||||||
LastAUMHash: hashFromHex("57343671da5eea3cfb502954e976e8028bffd3540b50a043b2a65a8d8d8217d0"),
|
LastAUMHash: hashFromHex("57343671da5eea3cfb502954e976e8028bffd3540b50a043b2a65a8d8d8217d0"),
|
||||||
|
|||||||
+2
-2
@@ -340,8 +340,8 @@ func TestSyncSimpleE2E(t *testing.T) {
|
|||||||
G1.template = genesis
|
G1.template = genesis
|
||||||
`,
|
`,
|
||||||
optTemplate("genesis", AUM{MessageKind: AUMCheckpoint, State: &State{
|
optTemplate("genesis", AUM{MessageKind: AUMCheckpoint, State: &State{
|
||||||
Keys: []Key{key},
|
Keys: []Key{key},
|
||||||
DisablementSecrets: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
DisablementValues: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
||||||
}}),
|
}}),
|
||||||
optKey("key", key, priv),
|
optKey("key", key, priv),
|
||||||
optSignAllUsing("key"))
|
optSignAllUsing("key"))
|
||||||
|
|||||||
@@ -316,8 +316,8 @@ func TestMarkDescendantAUMs(t *testing.T) {
|
|||||||
|
|
||||||
func TestMarkAncestorIntersectionAUMs(t *testing.T) {
|
func TestMarkAncestorIntersectionAUMs(t *testing.T) {
|
||||||
fakeState := &State{
|
fakeState := &State{
|
||||||
Keys: []Key{{Kind: Key25519, Votes: 1}},
|
Keys: []Key{{Kind: Key25519, Votes: 1}},
|
||||||
DisablementSecrets: [][]byte{bytes.Repeat([]byte{1}, 32)},
|
DisablementValues: [][]byte{bytes.Repeat([]byte{1}, 32)},
|
||||||
}
|
}
|
||||||
|
|
||||||
tcs := []struct {
|
tcs := []struct {
|
||||||
@@ -542,8 +542,8 @@ func cloneMem(src, dst *Mem) {
|
|||||||
|
|
||||||
func TestCompact(t *testing.T) {
|
func TestCompact(t *testing.T) {
|
||||||
fakeState := &State{
|
fakeState := &State{
|
||||||
Keys: []Key{{Kind: Key25519, Votes: 1}},
|
Keys: []Key{{Kind: Key25519, Votes: 1}},
|
||||||
DisablementSecrets: [][]byte{bytes.Repeat([]byte{1}, 32)},
|
DisablementValues: [][]byte{bytes.Repeat([]byte{1}, 32)},
|
||||||
}
|
}
|
||||||
|
|
||||||
// A & B are deleted because the new lastActiveAncestor advances beyond them.
|
// A & B are deleted because the new lastActiveAncestor advances beyond them.
|
||||||
@@ -610,8 +610,8 @@ func TestCompactLongButYoung(t *testing.T) {
|
|||||||
|
|
||||||
storage := ChonkMem()
|
storage := ChonkMem()
|
||||||
auth, _, err := Create(storage, State{
|
auth, _, err := Create(storage, State{
|
||||||
Keys: []Key{ourKey, someOtherKey},
|
Keys: []Key{ourKey, someOtherKey},
|
||||||
DisablementSecrets: [][]byte{DisablementKDF(bytes.Repeat([]byte{0xa5}, 32))},
|
DisablementValues: [][]byte{DisablementKDF(bytes.Repeat([]byte{0xa5}, 32))},
|
||||||
}, ourPriv)
|
}, ourPriv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("tka.Create() failed: %v", err)
|
t.Fatalf("tka.Create() failed: %v", err)
|
||||||
|
|||||||
+19
-19
@@ -305,8 +305,8 @@ func TestAuthorityValidDisablement(t *testing.T) {
|
|||||||
G1.template = genesis
|
G1.template = genesis
|
||||||
`,
|
`,
|
||||||
optTemplate("genesis", AUM{MessageKind: AUMCheckpoint, State: &State{
|
optTemplate("genesis", AUM{MessageKind: AUMCheckpoint, State: &State{
|
||||||
Keys: []Key{key},
|
Keys: []Key{key},
|
||||||
DisablementSecrets: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
DisablementValues: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
||||||
}}),
|
}}),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -321,8 +321,8 @@ func TestCreateBootstrapAuthority(t *testing.T) {
|
|||||||
key := Key{Kind: Key25519, Public: pub, Votes: 2}
|
key := Key{Kind: Key25519, Public: pub, Votes: 2}
|
||||||
|
|
||||||
a1, genesisAUM, err := Create(ChonkMem(), State{
|
a1, genesisAUM, err := Create(ChonkMem(), State{
|
||||||
Keys: []Key{key},
|
Keys: []Key{key},
|
||||||
DisablementSecrets: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
DisablementValues: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
||||||
}, signer25519(priv))
|
}, signer25519(priv))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Create() failed: %v", err)
|
t.Fatalf("Create() failed: %v", err)
|
||||||
@@ -353,8 +353,8 @@ func TestBootstrapChonkMustBeEmpty(t *testing.T) {
|
|||||||
pub, priv := testingKey25519(t, 1)
|
pub, priv := testingKey25519(t, 1)
|
||||||
key := Key{Kind: Key25519, Public: pub, Votes: 2}
|
key := Key{Kind: Key25519, Public: pub, Votes: 2}
|
||||||
state := State{
|
state := State{
|
||||||
Keys: []Key{key},
|
Keys: []Key{key},
|
||||||
DisablementSecrets: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
DisablementValues: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bootstrap our chonk for the first time, which should succeed.
|
// Bootstrap our chonk for the first time, which should succeed.
|
||||||
@@ -420,8 +420,8 @@ func TestAuthorityInformNonLinear(t *testing.T) {
|
|||||||
L4.hashSeed = 2
|
L4.hashSeed = 2
|
||||||
`,
|
`,
|
||||||
optTemplate("genesis", AUM{MessageKind: AUMCheckpoint, State: &State{
|
optTemplate("genesis", AUM{MessageKind: AUMCheckpoint, State: &State{
|
||||||
Keys: []Key{key},
|
Keys: []Key{key},
|
||||||
DisablementSecrets: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
DisablementValues: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
||||||
}}),
|
}}),
|
||||||
optKey("key", key, priv),
|
optKey("key", key, priv),
|
||||||
optSignAllUsing("key"))
|
optSignAllUsing("key"))
|
||||||
@@ -465,8 +465,8 @@ func TestAuthorityInformLinear(t *testing.T) {
|
|||||||
G1.template = genesis
|
G1.template = genesis
|
||||||
`,
|
`,
|
||||||
optTemplate("genesis", AUM{MessageKind: AUMCheckpoint, State: &State{
|
optTemplate("genesis", AUM{MessageKind: AUMCheckpoint, State: &State{
|
||||||
Keys: []Key{key},
|
Keys: []Key{key},
|
||||||
DisablementSecrets: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
DisablementValues: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
||||||
}}),
|
}}),
|
||||||
optKey("key", key, priv),
|
optKey("key", key, priv),
|
||||||
optSignAllUsing("key"))
|
optSignAllUsing("key"))
|
||||||
@@ -517,7 +517,7 @@ func TestInteropWithNLKey(t *testing.T) {
|
|||||||
Public: pub2.KeyID(),
|
Public: pub2.KeyID(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
DisablementSecrets: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
DisablementValues: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
||||||
}, priv1)
|
}, priv1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("tka.Create: %v", err)
|
t.Errorf("tka.Create: %v", err)
|
||||||
@@ -546,12 +546,12 @@ func TestAuthorityCompact(t *testing.T) {
|
|||||||
C.template = checkpoint2
|
C.template = checkpoint2
|
||||||
`,
|
`,
|
||||||
optTemplate("genesis", AUM{MessageKind: AUMCheckpoint, State: &State{
|
optTemplate("genesis", AUM{MessageKind: AUMCheckpoint, State: &State{
|
||||||
Keys: []Key{key},
|
Keys: []Key{key},
|
||||||
DisablementSecrets: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
DisablementValues: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
||||||
}}),
|
}}),
|
||||||
optTemplate("checkpoint2", AUM{MessageKind: AUMCheckpoint, State: &State{
|
optTemplate("checkpoint2", AUM{MessageKind: AUMCheckpoint, State: &State{
|
||||||
Keys: []Key{key},
|
Keys: []Key{key},
|
||||||
DisablementSecrets: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
DisablementValues: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
||||||
}}),
|
}}),
|
||||||
optKey("key", key, priv),
|
optKey("key", key, priv),
|
||||||
optSignAllUsing("key"))
|
optSignAllUsing("key"))
|
||||||
@@ -603,8 +603,8 @@ func TestFindParentForRewrite(t *testing.T) {
|
|||||||
D.template = remove2
|
D.template = remove2
|
||||||
`,
|
`,
|
||||||
optTemplate("genesis", AUM{MessageKind: AUMCheckpoint, State: &State{
|
optTemplate("genesis", AUM{MessageKind: AUMCheckpoint, State: &State{
|
||||||
Keys: []Key{k1},
|
Keys: []Key{k1},
|
||||||
DisablementSecrets: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
DisablementValues: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
||||||
}}),
|
}}),
|
||||||
optTemplate("add2", AUM{MessageKind: AUMAddKey, Key: &k2}),
|
optTemplate("add2", AUM{MessageKind: AUMAddKey, Key: &k2}),
|
||||||
optTemplate("add3", AUM{MessageKind: AUMAddKey, Key: &k3}),
|
optTemplate("add3", AUM{MessageKind: AUMAddKey, Key: &k3}),
|
||||||
@@ -672,8 +672,8 @@ func TestMakeRetroactiveRevocation(t *testing.T) {
|
|||||||
D.template = add3
|
D.template = add3
|
||||||
`,
|
`,
|
||||||
optTemplate("genesis", AUM{MessageKind: AUMCheckpoint, State: &State{
|
optTemplate("genesis", AUM{MessageKind: AUMCheckpoint, State: &State{
|
||||||
Keys: []Key{k1},
|
Keys: []Key{k1},
|
||||||
DisablementSecrets: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
DisablementValues: [][]byte{DisablementKDF([]byte{1, 2, 3})},
|
||||||
}}),
|
}}),
|
||||||
optTemplate("add2", AUM{MessageKind: AUMAddKey, Key: &k2}),
|
optTemplate("add2", AUM{MessageKind: AUMAddKey, Key: &k2}),
|
||||||
optTemplate("add3", AUM{MessageKind: AUMAddKey, Key: &k3}))
|
optTemplate("add3", AUM{MessageKind: AUMAddKey, Key: &k3}))
|
||||||
|
|||||||
Reference in New Issue
Block a user