mirror of
https://github.com/tailscale/tailscale.git
synced 2025-04-19 21:23:58 +00:00
tka,types/key: remove dependency for tailcfg & types/ packages on tka
Following the pattern elsewhere, we create a new tka-specific types package for the types that need to couple between the serialized structure types, and tka. Signed-off-by: Tom DNetto <tom@tailscale.com>
This commit is contained in:
parent
a9f6cd41fd
commit
f50043f6cb
@ -1,13 +1,9 @@
|
|||||||
tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/depaware)
|
tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/depaware)
|
||||||
|
|
||||||
filippo.io/edwards25519 from github.com/hdevalence/ed25519consensus
|
|
||||||
filippo.io/edwards25519/field from filippo.io/edwards25519
|
|
||||||
W 💣 github.com/alexbrainman/sspi from github.com/alexbrainman/sspi/negotiate+
|
W 💣 github.com/alexbrainman/sspi from github.com/alexbrainman/sspi/negotiate+
|
||||||
W github.com/alexbrainman/sspi/internal/common from github.com/alexbrainman/sspi/negotiate
|
W github.com/alexbrainman/sspi/internal/common from github.com/alexbrainman/sspi/negotiate
|
||||||
W 💣 github.com/alexbrainman/sspi/negotiate from tailscale.com/net/tshttpproxy
|
W 💣 github.com/alexbrainman/sspi/negotiate from tailscale.com/net/tshttpproxy
|
||||||
github.com/fxamacker/cbor/v2 from tailscale.com/tka
|
|
||||||
github.com/golang/groupcache/lru from tailscale.com/net/dnscache
|
github.com/golang/groupcache/lru from tailscale.com/net/dnscache
|
||||||
github.com/hdevalence/ed25519consensus from tailscale.com/tka
|
|
||||||
L github.com/josharian/native from github.com/mdlayher/netlink+
|
L github.com/josharian/native from github.com/mdlayher/netlink+
|
||||||
L 💣 github.com/jsimonetti/rtnetlink from tailscale.com/net/interfaces
|
L 💣 github.com/jsimonetti/rtnetlink from tailscale.com/net/interfaces
|
||||||
L github.com/jsimonetti/rtnetlink/internal/unix from github.com/jsimonetti/rtnetlink
|
L github.com/jsimonetti/rtnetlink/internal/unix from github.com/jsimonetti/rtnetlink
|
||||||
@ -30,7 +26,6 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
|
|||||||
github.com/tailscale/goupnp/ssdp from github.com/tailscale/goupnp
|
github.com/tailscale/goupnp/ssdp from github.com/tailscale/goupnp
|
||||||
github.com/tcnksm/go-httpstat from tailscale.com/net/netcheck
|
github.com/tcnksm/go-httpstat from tailscale.com/net/netcheck
|
||||||
github.com/toqueteos/webbrowser from tailscale.com/cmd/tailscale/cli
|
github.com/toqueteos/webbrowser from tailscale.com/cmd/tailscale/cli
|
||||||
github.com/x448/float16 from github.com/fxamacker/cbor/v2
|
|
||||||
💣 go4.org/mem from tailscale.com/derp+
|
💣 go4.org/mem from tailscale.com/derp+
|
||||||
go4.org/netipx from tailscale.com/wgengine/filter
|
go4.org/netipx from tailscale.com/wgengine/filter
|
||||||
W 💣 golang.zx2c4.com/wireguard/windows/tunnel/winipcfg from tailscale.com/net/interfaces+
|
W 💣 golang.zx2c4.com/wireguard/windows/tunnel/winipcfg from tailscale.com/net/interfaces+
|
||||||
@ -73,7 +68,6 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
|
|||||||
tailscale.com/safesocket from tailscale.com/cmd/tailscale/cli+
|
tailscale.com/safesocket from tailscale.com/cmd/tailscale/cli+
|
||||||
tailscale.com/syncs from tailscale.com/net/netcheck+
|
tailscale.com/syncs from tailscale.com/net/netcheck+
|
||||||
tailscale.com/tailcfg from tailscale.com/cmd/tailscale/cli+
|
tailscale.com/tailcfg from tailscale.com/cmd/tailscale/cli+
|
||||||
tailscale.com/tka from tailscale.com/types/key
|
|
||||||
W tailscale.com/tsconst from tailscale.com/net/interfaces
|
W tailscale.com/tsconst from tailscale.com/net/interfaces
|
||||||
💣 tailscale.com/tstime/mono from tailscale.com/tstime/rate
|
💣 tailscale.com/tstime/mono from tailscale.com/tstime/rate
|
||||||
tailscale.com/tstime/rate from tailscale.com/wgengine/filter
|
tailscale.com/tstime/rate from tailscale.com/wgengine/filter
|
||||||
@ -89,6 +83,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
|
|||||||
tailscale.com/types/persist from tailscale.com/ipn
|
tailscale.com/types/persist from tailscale.com/ipn
|
||||||
tailscale.com/types/preftype from tailscale.com/cmd/tailscale/cli+
|
tailscale.com/types/preftype from tailscale.com/cmd/tailscale/cli+
|
||||||
tailscale.com/types/structs from tailscale.com/ipn+
|
tailscale.com/types/structs from tailscale.com/ipn+
|
||||||
|
tailscale.com/types/tkatype from tailscale.com/types/key
|
||||||
tailscale.com/types/views from tailscale.com/tailcfg+
|
tailscale.com/types/views from tailscale.com/tailcfg+
|
||||||
tailscale.com/util/clientmetric from tailscale.com/net/netcheck+
|
tailscale.com/util/clientmetric from tailscale.com/net/netcheck+
|
||||||
tailscale.com/util/cloudenv from tailscale.com/net/dnscache+
|
tailscale.com/util/cloudenv from tailscale.com/net/dnscache+
|
||||||
@ -102,9 +97,8 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
|
|||||||
tailscale.com/version from tailscale.com/cmd/tailscale/cli+
|
tailscale.com/version from tailscale.com/cmd/tailscale/cli+
|
||||||
tailscale.com/version/distro from tailscale.com/cmd/tailscale/cli+
|
tailscale.com/version/distro from tailscale.com/cmd/tailscale/cli+
|
||||||
tailscale.com/wgengine/filter from tailscale.com/types/netmap
|
tailscale.com/wgengine/filter from tailscale.com/types/netmap
|
||||||
golang.org/x/crypto/argon2 from tailscale.com/tka
|
golang.org/x/crypto/blake2b from golang.org/x/crypto/nacl/box
|
||||||
golang.org/x/crypto/blake2b from golang.org/x/crypto/nacl/box+
|
golang.org/x/crypto/blake2s from tailscale.com/control/controlbase
|
||||||
golang.org/x/crypto/blake2s from tailscale.com/control/controlbase+
|
|
||||||
golang.org/x/crypto/chacha20 from golang.org/x/crypto/chacha20poly1305
|
golang.org/x/crypto/chacha20 from golang.org/x/crypto/chacha20poly1305
|
||||||
golang.org/x/crypto/chacha20poly1305 from crypto/tls+
|
golang.org/x/crypto/chacha20poly1305 from crypto/tls+
|
||||||
golang.org/x/crypto/cryptobyte from crypto/ecdsa+
|
golang.org/x/crypto/cryptobyte from crypto/ecdsa+
|
||||||
@ -162,7 +156,6 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
|
|||||||
embed from tailscale.com/cmd/tailscale/cli+
|
embed from tailscale.com/cmd/tailscale/cli+
|
||||||
encoding from encoding/json+
|
encoding from encoding/json+
|
||||||
encoding/asn1 from crypto/x509+
|
encoding/asn1 from crypto/x509+
|
||||||
encoding/base32 from tailscale.com/tka
|
|
||||||
encoding/base64 from encoding/json+
|
encoding/base64 from encoding/json+
|
||||||
encoding/binary from compress/gzip+
|
encoding/binary from compress/gzip+
|
||||||
encoding/hex from crypto/x509+
|
encoding/hex from crypto/x509+
|
||||||
|
@ -244,7 +244,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
|||||||
tailscale.com/syncs from tailscale.com/net/netcheck+
|
tailscale.com/syncs from tailscale.com/net/netcheck+
|
||||||
tailscale.com/tailcfg from tailscale.com/client/tailscale/apitype+
|
tailscale.com/tailcfg from tailscale.com/client/tailscale/apitype+
|
||||||
LD tailscale.com/tempfork/gliderlabs/ssh from tailscale.com/ssh/tailssh
|
LD tailscale.com/tempfork/gliderlabs/ssh from tailscale.com/ssh/tailssh
|
||||||
tailscale.com/tka from tailscale.com/types/key+
|
tailscale.com/tka from tailscale.com/ipn/ipnlocal+
|
||||||
W tailscale.com/tsconst from tailscale.com/net/interfaces
|
W tailscale.com/tsconst from tailscale.com/net/interfaces
|
||||||
tailscale.com/tstime from tailscale.com/wgengine/magicsock
|
tailscale.com/tstime from tailscale.com/wgengine/magicsock
|
||||||
💣 tailscale.com/tstime/mono from tailscale.com/net/tstun+
|
💣 tailscale.com/tstime/mono from tailscale.com/net/tstun+
|
||||||
@ -263,6 +263,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
|||||||
tailscale.com/types/persist from tailscale.com/control/controlclient+
|
tailscale.com/types/persist from tailscale.com/control/controlclient+
|
||||||
tailscale.com/types/preftype from tailscale.com/ipn+
|
tailscale.com/types/preftype from tailscale.com/ipn+
|
||||||
tailscale.com/types/structs from tailscale.com/control/controlclient+
|
tailscale.com/types/structs from tailscale.com/control/controlclient+
|
||||||
|
tailscale.com/types/tkatype from tailscale.com/tka+
|
||||||
tailscale.com/types/views from tailscale.com/ipn/ipnlocal+
|
tailscale.com/types/views from tailscale.com/ipn/ipnlocal+
|
||||||
tailscale.com/util/clientmetric from tailscale.com/control/controlclient+
|
tailscale.com/util/clientmetric from tailscale.com/control/controlclient+
|
||||||
tailscale.com/util/cloudenv from tailscale.com/net/dns/resolver+
|
tailscale.com/util/cloudenv from tailscale.com/net/dns/resolver+
|
||||||
|
13
tka/aum.go
13
tka/aum.go
@ -12,15 +12,12 @@ import (
|
|||||||
|
|
||||||
"github.com/fxamacker/cbor/v2"
|
"github.com/fxamacker/cbor/v2"
|
||||||
"golang.org/x/crypto/blake2s"
|
"golang.org/x/crypto/blake2s"
|
||||||
|
"tailscale.com/types/tkatype"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AUMHash represents the BLAKE2s digest of an Authority Update Message (AUM).
|
// AUMHash represents the BLAKE2s digest of an Authority Update Message (AUM).
|
||||||
type AUMHash [blake2s.Size]byte
|
type AUMHash [blake2s.Size]byte
|
||||||
|
|
||||||
// AUMSigHash represents the BLAKE2s digest of an Authority Update
|
|
||||||
// Message (AUM), sans any signatures.
|
|
||||||
type AUMSigHash [blake2s.Size]byte
|
|
||||||
|
|
||||||
// AUMKind describes valid AUM types.
|
// AUMKind describes valid AUM types.
|
||||||
type AUMKind uint8
|
type AUMKind uint8
|
||||||
|
|
||||||
@ -100,7 +97,7 @@ type AUM struct {
|
|||||||
|
|
||||||
// KeyID references a public key which is part of the key authority.
|
// KeyID references a public key which is part of the key authority.
|
||||||
// This field is used for RemoveKey and UpdateKey AUMs.
|
// This field is used for RemoveKey and UpdateKey AUMs.
|
||||||
KeyID KeyID `cbor:"4,keyasint,omitempty"`
|
KeyID tkatype.KeyID `cbor:"4,keyasint,omitempty"`
|
||||||
|
|
||||||
// State describes the full state of the key authority.
|
// State describes the full state of the key authority.
|
||||||
// This field is used for Checkpoint AUMs.
|
// This field is used for Checkpoint AUMs.
|
||||||
@ -118,7 +115,7 @@ type AUM struct {
|
|||||||
|
|
||||||
// Signatures lists the signatures over this AUM.
|
// Signatures lists the signatures over this AUM.
|
||||||
// CBOR key 23 is the last key which can be encoded as a single byte.
|
// CBOR key 23 is the last key which can be encoded as a single byte.
|
||||||
Signatures []Signature `cbor:"23,keyasint,omitempty"`
|
Signatures []tkatype.Signature `cbor:"23,keyasint,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// StaticValidate returns a nil error if the AUM is well-formed.
|
// StaticValidate returns a nil error if the AUM is well-formed.
|
||||||
@ -230,7 +227,7 @@ func (a *AUM) Hash() AUMHash {
|
|||||||
// This is identical to Hash() except the Signatures are not
|
// This is identical to Hash() except the Signatures are not
|
||||||
// serialized. Without this, the hash used for signatures
|
// serialized. Without this, the hash used for signatures
|
||||||
// would be circularly dependent on the signatures.
|
// would be circularly dependent on the signatures.
|
||||||
func (a AUM) SigHash() AUMSigHash {
|
func (a AUM) SigHash() tkatype.AUMSigHash {
|
||||||
dupe := a
|
dupe := a
|
||||||
dupe.Signatures = nil
|
dupe.Signatures = nil
|
||||||
return blake2s.Sum256(dupe.Serialize())
|
return blake2s.Sum256(dupe.Serialize())
|
||||||
@ -250,7 +247,7 @@ func (a *AUM) sign25519(priv ed25519.PrivateKey) {
|
|||||||
key := Key{Kind: Key25519, Public: priv.Public().(ed25519.PublicKey)}
|
key := Key{Kind: Key25519, Public: priv.Public().(ed25519.PublicKey)}
|
||||||
sigHash := a.SigHash()
|
sigHash := a.SigHash()
|
||||||
|
|
||||||
a.Signatures = append(a.Signatures, Signature{
|
a.Signatures = append(a.Signatures, tkatype.Signature{
|
||||||
KeyID: key.ID(),
|
KeyID: key.ID(),
|
||||||
Signature: ed25519.Sign(priv, sigHash[:]),
|
Signature: ed25519.Sign(priv, sigHash[:]),
|
||||||
})
|
})
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/fxamacker/cbor/v2"
|
"github.com/fxamacker/cbor/v2"
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
"golang.org/x/crypto/blake2s"
|
"golang.org/x/crypto/blake2s"
|
||||||
|
"tailscale.com/types/tkatype"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSerialization(t *testing.T) {
|
func TestSerialization(t *testing.T) {
|
||||||
@ -137,7 +138,7 @@ func TestSerialization(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Signature",
|
"Signature",
|
||||||
AUM{MessageKind: AUMAddKey, Signatures: []Signature{{KeyID: []byte{1}}}},
|
AUM{MessageKind: AUMAddKey, Signatures: []tkatype.Signature{{KeyID: []byte{1}}}},
|
||||||
[]byte{
|
[]byte{
|
||||||
0xa3, // major type 5 (map), 3 items
|
0xa3, // major type 5 (map), 3 items
|
||||||
0x01, // |- major type 0 (int), value 1 (first key, MessageKind)
|
0x01, // |- major type 0 (int), value 1 (first key, MessageKind)
|
||||||
@ -198,7 +199,7 @@ func TestAUMWeight(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"Key unknown",
|
"Key unknown",
|
||||||
AUM{
|
AUM{
|
||||||
Signatures: []Signature{{KeyID: fakeKeyID[:]}},
|
Signatures: []tkatype.Signature{{KeyID: fakeKeyID[:]}},
|
||||||
},
|
},
|
||||||
State{},
|
State{},
|
||||||
0,
|
0,
|
||||||
@ -206,7 +207,7 @@ func TestAUMWeight(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"Unary key",
|
"Unary key",
|
||||||
AUM{
|
AUM{
|
||||||
Signatures: []Signature{{KeyID: key.ID()}},
|
Signatures: []tkatype.Signature{{KeyID: key.ID()}},
|
||||||
},
|
},
|
||||||
State{
|
State{
|
||||||
Keys: []Key{key},
|
Keys: []Key{key},
|
||||||
@ -216,7 +217,7 @@ func TestAUMWeight(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"Multiple keys",
|
"Multiple keys",
|
||||||
AUM{
|
AUM{
|
||||||
Signatures: []Signature{{KeyID: key.ID()}, {KeyID: key2.ID()}},
|
Signatures: []tkatype.Signature{{KeyID: key.ID()}, {KeyID: key2.ID()}},
|
||||||
},
|
},
|
||||||
State{
|
State{
|
||||||
Keys: []Key{key, key2},
|
Keys: []Key{key, key2},
|
||||||
@ -226,7 +227,7 @@ func TestAUMWeight(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"Double use",
|
"Double use",
|
||||||
AUM{
|
AUM{
|
||||||
Signatures: []Signature{{KeyID: key.ID()}, {KeyID: key.ID()}},
|
Signatures: []tkatype.Signature{{KeyID: key.ID()}, {KeyID: key.ID()}},
|
||||||
},
|
},
|
||||||
State{
|
State{
|
||||||
Keys: []Key{key},
|
Keys: []Key{key},
|
||||||
@ -255,7 +256,7 @@ func TestAUMHashes(t *testing.T) {
|
|||||||
sigHash1 := aum.SigHash()
|
sigHash1 := aum.SigHash()
|
||||||
aumHash1 := aum.Hash()
|
aumHash1 := aum.Hash()
|
||||||
|
|
||||||
aum.Signatures = []Signature{{KeyID: []byte{1, 2, 3, 4}}}
|
aum.Signatures = []tkatype.Signature{{KeyID: []byte{1, 2, 3, 4}}}
|
||||||
sigHash2 := aum.SigHash()
|
sigHash2 := aum.SigHash()
|
||||||
aumHash2 := aum.Hash()
|
aumHash2 := aum.Hash()
|
||||||
if len(aum.Signatures) != 1 {
|
if len(aum.Signatures) != 1 {
|
||||||
|
@ -6,11 +6,13 @@ package tka
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"tailscale.com/types/tkatype"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Types implementing Signer can sign update messages.
|
// Types implementing Signer can sign update messages.
|
||||||
type Signer interface {
|
type Signer interface {
|
||||||
SignAUM(*AUM) error
|
SignAUM(tkatype.AUMSigHash) ([]tkatype.Signature, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateBuilder implements a builder for changes to the tailnet
|
// UpdateBuilder implements a builder for changes to the tailnet
|
||||||
@ -34,9 +36,11 @@ func (b *UpdateBuilder) mkUpdate(update AUM) error {
|
|||||||
update.PrevAUMHash = prevHash
|
update.PrevAUMHash = prevHash
|
||||||
|
|
||||||
if b.signer != nil {
|
if b.signer != nil {
|
||||||
if err := b.signer.SignAUM(&update); err != nil {
|
sigs, err := b.signer.SignAUM(update.SigHash())
|
||||||
|
if err != nil {
|
||||||
return fmt.Errorf("signing failed: %v", err)
|
return fmt.Errorf("signing failed: %v", err)
|
||||||
}
|
}
|
||||||
|
update.Signatures = append(update.Signatures, sigs...)
|
||||||
}
|
}
|
||||||
if err := update.StaticValidate(); err != nil {
|
if err := update.StaticValidate(); err != nil {
|
||||||
return fmt.Errorf("generated update was invalid: %v", err)
|
return fmt.Errorf("generated update was invalid: %v", err)
|
||||||
@ -61,7 +65,7 @@ func (b *UpdateBuilder) AddKey(key Key) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RemoveKey removes a key from the authority.
|
// RemoveKey removes a key from the authority.
|
||||||
func (b *UpdateBuilder) RemoveKey(keyID KeyID) error {
|
func (b *UpdateBuilder) RemoveKey(keyID tkatype.KeyID) error {
|
||||||
if _, err := b.state.GetKey(keyID); err != nil {
|
if _, err := b.state.GetKey(keyID); err != nil {
|
||||||
return fmt.Errorf("failed reading key %x: %v", keyID, err)
|
return fmt.Errorf("failed reading key %x: %v", keyID, err)
|
||||||
}
|
}
|
||||||
@ -69,7 +73,7 @@ func (b *UpdateBuilder) RemoveKey(keyID KeyID) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetKeyVote updates the number of votes of an existing key.
|
// SetKeyVote updates the number of votes of an existing key.
|
||||||
func (b *UpdateBuilder) SetKeyVote(keyID KeyID, votes uint) error {
|
func (b *UpdateBuilder) SetKeyVote(keyID tkatype.KeyID, votes uint) error {
|
||||||
if _, err := b.state.GetKey(keyID); err != nil {
|
if _, err := b.state.GetKey(keyID); err != nil {
|
||||||
return fmt.Errorf("failed reading key %x: %v", keyID, err)
|
return fmt.Errorf("failed reading key %x: %v", keyID, err)
|
||||||
}
|
}
|
||||||
@ -80,7 +84,7 @@ func (b *UpdateBuilder) SetKeyVote(keyID KeyID, votes uint) error {
|
|||||||
//
|
//
|
||||||
// TODO(tom): Provide an API to update specific values rather than the whole
|
// TODO(tom): Provide an API to update specific values rather than the whole
|
||||||
// map.
|
// map.
|
||||||
func (b *UpdateBuilder) SetKeyMeta(keyID KeyID, meta map[string]string) error {
|
func (b *UpdateBuilder) SetKeyMeta(keyID tkatype.KeyID, meta map[string]string) error {
|
||||||
if _, err := b.state.GetKey(keyID); err != nil {
|
if _, err := b.state.GetKey(keyID); err != nil {
|
||||||
return fmt.Errorf("failed reading key %x: %v", keyID, err)
|
return fmt.Errorf("failed reading key %x: %v", keyID, err)
|
||||||
}
|
}
|
||||||
|
@ -9,13 +9,19 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"tailscale.com/types/tkatype"
|
||||||
)
|
)
|
||||||
|
|
||||||
type signer25519 ed25519.PrivateKey
|
type signer25519 ed25519.PrivateKey
|
||||||
|
|
||||||
func (s signer25519) SignAUM(update *AUM) error {
|
func (s signer25519) SignAUM(sigHash tkatype.AUMSigHash) ([]tkatype.Signature, error) {
|
||||||
update.sign25519(ed25519.PrivateKey(s))
|
priv := ed25519.PrivateKey(s)
|
||||||
return nil
|
key := Key{Kind: Key25519, Public: priv.Public().(ed25519.PublicKey)}
|
||||||
|
|
||||||
|
return []tkatype.Signature{{
|
||||||
|
KeyID: key.ID(),
|
||||||
|
Signature: ed25519.Sign(priv, sigHash[:]),
|
||||||
|
}}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAuthorityBuilderAddKey(t *testing.T) {
|
func TestAuthorityBuilderAddKey(t *testing.T) {
|
||||||
|
@ -15,6 +15,7 @@ import (
|
|||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
"github.com/google/go-cmp/cmp/cmpopts"
|
"github.com/google/go-cmp/cmp/cmpopts"
|
||||||
|
"tailscale.com/types/tkatype"
|
||||||
)
|
)
|
||||||
|
|
||||||
// chaintest_test.go implements test helpers for concisely describing
|
// chaintest_test.go implements test helpers for concisely describing
|
||||||
@ -265,7 +266,7 @@ func (c *testChain) makeAUM(v *testchainNode) AUM {
|
|||||||
|
|
||||||
sigHash := aum.SigHash()
|
sigHash := aum.SigHash()
|
||||||
for _, key := range c.SignAllKeys {
|
for _, key := range c.SignAllKeys {
|
||||||
aum.Signatures = append(aum.Signatures, Signature{
|
aum.Signatures = append(aum.Signatures, tkatype.Signature{
|
||||||
KeyID: c.Key[key].ID(),
|
KeyID: c.Key[key].ID(),
|
||||||
Signature: ed25519.Sign(c.KeyPrivs[key], sigHash[:]),
|
Signature: ed25519.Sign(c.KeyPrivs[key], sigHash[:]),
|
||||||
})
|
})
|
||||||
@ -274,7 +275,7 @@ func (c *testChain) makeAUM(v *testchainNode) AUM {
|
|||||||
// If the aum was specified as being signed by some key, then
|
// If the aum was specified as being signed by some key, then
|
||||||
// sign it using that key.
|
// sign it using that key.
|
||||||
if key := v.SignedWith; key != "" {
|
if key := v.SignedWith; key != "" {
|
||||||
aum.Signatures = append(aum.Signatures, Signature{
|
aum.Signatures = append(aum.Signatures, tkatype.Signature{
|
||||||
KeyID: c.Key[key].ID(),
|
KeyID: c.Key[key].ID(),
|
||||||
Signature: ed25519.Sign(c.KeyPrivs[key], sigHash[:]),
|
Signature: ed25519.Sign(c.KeyPrivs[key], sigHash[:]),
|
||||||
})
|
})
|
||||||
|
19
tka/key.go
19
tka/key.go
@ -10,6 +10,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/hdevalence/ed25519consensus"
|
"github.com/hdevalence/ed25519consensus"
|
||||||
|
"tailscale.com/types/tkatype"
|
||||||
)
|
)
|
||||||
|
|
||||||
// KeyKind describes the different varieties of a Key.
|
// KeyKind describes the different varieties of a Key.
|
||||||
@ -73,12 +74,12 @@ func (k Key) Clone() Key {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k Key) ID() KeyID {
|
func (k Key) ID() tkatype.KeyID {
|
||||||
switch k.Kind {
|
switch k.Kind {
|
||||||
// Because 25519 public keys are so short, we just use the 32-byte
|
// Because 25519 public keys are so short, we just use the 32-byte
|
||||||
// public as their 'key ID'.
|
// public as their 'key ID'.
|
||||||
case Key25519:
|
case Key25519:
|
||||||
return KeyID(k.Public)
|
return tkatype.KeyID(k.Public)
|
||||||
default:
|
default:
|
||||||
panic("unsupported key kind")
|
panic("unsupported key kind")
|
||||||
}
|
}
|
||||||
@ -112,21 +113,9 @@ func (k Key) StaticValidate() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// KeyID references a verification key stored in the key authority.
|
|
||||||
//
|
|
||||||
// For 25519 keys: The 32-byte public key.
|
|
||||||
type KeyID []byte
|
|
||||||
|
|
||||||
// Signature describes a signature over an AUM, which can be verified
|
|
||||||
// using the key referenced by KeyID.
|
|
||||||
type Signature struct {
|
|
||||||
KeyID KeyID `cbor:"1,keyasint"`
|
|
||||||
Signature []byte `cbor:"2,keyasint"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify returns a nil error if the signature is valid over the
|
// Verify returns a nil error if the signature is valid over the
|
||||||
// provided AUM BLAKE2s digest, using the given key.
|
// provided AUM BLAKE2s digest, using the given key.
|
||||||
func (s *Signature) Verify(aumDigest AUMSigHash, key Key) error {
|
func signatureVerify(s *tkatype.Signature, aumDigest tkatype.AUMSigHash, key Key) error {
|
||||||
// NOTE(tom): Even if we can compute the public from the KeyID,
|
// NOTE(tom): Even if we can compute the public from the KeyID,
|
||||||
// its possible for the KeyID to be attacker-controlled
|
// its possible for the KeyID to be attacker-controlled
|
||||||
// so we should use the public contained in the state machine.
|
// so we should use the public contained in the state machine.
|
||||||
|
@ -10,6 +10,8 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"tailscale.com/types/tkatype"
|
||||||
)
|
)
|
||||||
|
|
||||||
// returns a random source based on the test name + extraSeed.
|
// returns a random source based on the test name + extraSeed.
|
||||||
@ -41,24 +43,24 @@ func TestVerify25519(t *testing.T) {
|
|||||||
MessageKind: AUMRemoveKey,
|
MessageKind: AUMRemoveKey,
|
||||||
KeyID: []byte{1, 2, 3, 4},
|
KeyID: []byte{1, 2, 3, 4},
|
||||||
// Signatures is set to crap so we are sure its ignored in the sigHash computation.
|
// Signatures is set to crap so we are sure its ignored in the sigHash computation.
|
||||||
Signatures: []Signature{{KeyID: []byte{45, 42}}},
|
Signatures: []tkatype.Signature{{KeyID: []byte{45, 42}}},
|
||||||
}
|
}
|
||||||
sigHash := aum.SigHash()
|
sigHash := aum.SigHash()
|
||||||
aum.Signatures = []Signature{
|
aum.Signatures = []tkatype.Signature{
|
||||||
{
|
{
|
||||||
KeyID: key.ID(),
|
KeyID: key.ID(),
|
||||||
Signature: ed25519.Sign(priv, sigHash[:]),
|
Signature: ed25519.Sign(priv, sigHash[:]),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := aum.Signatures[0].Verify(aum.SigHash(), key); err != nil {
|
if err := signatureVerify(&aum.Signatures[0], aum.SigHash(), key); err != nil {
|
||||||
t.Errorf("signature verification failed: %v", err)
|
t.Errorf("signature verification failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure it fails with a different public key.
|
// Make sure it fails with a different public key.
|
||||||
pub2, _ := testingKey25519(t, 2)
|
pub2, _ := testingKey25519(t, 2)
|
||||||
key2 := Key{Kind: Key25519, Public: pub2}
|
key2 := Key{Kind: Key25519, Public: pub2}
|
||||||
if err := aum.Signatures[0].Verify(aum.SigHash(), key2); err == nil {
|
if err := signatureVerify(&aum.Signatures[0], aum.SigHash(), key2); err == nil {
|
||||||
t.Error("signature verification with different key did not fail")
|
t.Error("signature verification with different key did not fail")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -204,7 +204,7 @@ func TestScenarioHelpers(t *testing.T) {
|
|||||||
if _, ok := n.AUMs["L3"]; !ok {
|
if _, ok := n.AUMs["L3"]; !ok {
|
||||||
t.Errorf("node n is missing %s", "L3")
|
t.Errorf("node n is missing %s", "L3")
|
||||||
}
|
}
|
||||||
if err := n.AUMs["L3"].Signatures[0].Verify(n.AUMs["L3"].SigHash(), *s.defaultKey); err != nil {
|
if err := signatureVerify(&n.AUMs["L3"].Signatures[0], n.AUMs["L3"].SigHash(), *s.defaultKey); err != nil {
|
||||||
t.Errorf("chained AUM was not signed: %v", err)
|
t.Errorf("chained AUM was not signed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/fxamacker/cbor/v2"
|
"github.com/fxamacker/cbor/v2"
|
||||||
"github.com/hdevalence/ed25519consensus"
|
"github.com/hdevalence/ed25519consensus"
|
||||||
"golang.org/x/crypto/blake2s"
|
"golang.org/x/crypto/blake2s"
|
||||||
|
"tailscale.com/types/tkatype"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SigKind describes valid NodeKeySignature types.
|
// SigKind describes valid NodeKeySignature types.
|
||||||
@ -67,7 +68,7 @@ func (s NodeKeySignature) sigHash() [blake2s.Size]byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Serialize returns the given NKS in a serialized format.
|
// Serialize returns the given NKS in a serialized format.
|
||||||
func (s *NodeKeySignature) Serialize() []byte {
|
func (s *NodeKeySignature) Serialize() tkatype.MarshaledSignature {
|
||||||
out := bytes.NewBuffer(make([]byte, 0, 128)) // 64byte sig + 32byte keyID + 32byte headroom
|
out := bytes.NewBuffer(make([]byte, 0, 128)) // 64byte sig + 32byte keyID + 32byte headroom
|
||||||
encoder, err := cbor.CTAP2EncOptions().EncMode()
|
encoder, err := cbor.CTAP2EncOptions().EncMode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"golang.org/x/crypto/argon2"
|
"golang.org/x/crypto/argon2"
|
||||||
|
"tailscale.com/types/tkatype"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrNoSuchKey is returned if the key referenced by a KeyID does not exist.
|
// ErrNoSuchKey is returned if the key referenced by a KeyID does not exist.
|
||||||
@ -40,7 +41,7 @@ type State struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetKey returns the trusted key with the specified KeyID.
|
// GetKey returns the trusted key with the specified KeyID.
|
||||||
func (s State) GetKey(key KeyID) (Key, error) {
|
func (s State) GetKey(key tkatype.KeyID) (Key, error) {
|
||||||
for _, k := range s.Keys {
|
for _, k := range s.Keys {
|
||||||
if bytes.Equal(k.ID(), key) {
|
if bytes.Equal(k.ID(), key) {
|
||||||
return k, nil
|
return k, nil
|
||||||
|
@ -13,6 +13,7 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/fxamacker/cbor/v2"
|
"github.com/fxamacker/cbor/v2"
|
||||||
|
"tailscale.com/types/tkatype"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Authority is a Tailnet Key Authority. This type is the main coupling
|
// Authority is a Tailnet Key Authority. This type is the main coupling
|
||||||
@ -416,7 +417,7 @@ func aumVerify(aum AUM, state State, isGenesisAUM bool) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("bad keyID on signature %d: %v", i, err)
|
return fmt.Errorf("bad keyID on signature %d: %v", i, err)
|
||||||
}
|
}
|
||||||
if err := sig.Verify(sigHash, key); err != nil {
|
if err := signatureVerify(&sig, sigHash, key); err != nil {
|
||||||
return fmt.Errorf("signature %d: %v", i, err)
|
return fmt.Errorf("signature %d: %v", i, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -485,9 +486,11 @@ func Create(storage Chonk, state State, signer Signer) (*Authority, AUM, error)
|
|||||||
// This serves as an easy way to validate the given state.
|
// This serves as an easy way to validate the given state.
|
||||||
return nil, AUM{}, fmt.Errorf("invalid state: %v", err)
|
return nil, AUM{}, fmt.Errorf("invalid state: %v", err)
|
||||||
}
|
}
|
||||||
if err := signer.SignAUM(&genesis); err != nil {
|
sigs, err := signer.SignAUM(genesis.SigHash())
|
||||||
|
if err != nil {
|
||||||
return nil, AUM{}, fmt.Errorf("signing failed: %v", err)
|
return nil, AUM{}, fmt.Errorf("signing failed: %v", err)
|
||||||
}
|
}
|
||||||
|
genesis.Signatures = append(genesis.Signatures, sigs...)
|
||||||
|
|
||||||
a, err := Bootstrap(storage, genesis)
|
a, err := Bootstrap(storage, genesis)
|
||||||
return a, genesis, err
|
return a, genesis, err
|
||||||
@ -591,7 +594,7 @@ func (a *Authority) Inform(updates []AUM) error {
|
|||||||
|
|
||||||
// VerifySignature returns true if the provided nodeKeySignature is signed
|
// VerifySignature returns true if the provided nodeKeySignature is signed
|
||||||
// correctly by a trusted key.
|
// correctly by a trusted key.
|
||||||
func (a *Authority) VerifySignature(nodeKeySignature []byte) error {
|
func (a *Authority) VerifySignature(nodeKeySignature tkatype.MarshaledSignature) error {
|
||||||
var decoded NodeKeySignature
|
var decoded NodeKeySignature
|
||||||
if err := cbor.Unmarshal(nodeKeySignature, &decoded); err != nil {
|
if err := cbor.Unmarshal(nodeKeySignature, &decoded); err != nil {
|
||||||
return fmt.Errorf("unmarshal: %v", err)
|
return fmt.Errorf("unmarshal: %v", err)
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"tailscale.com/types/tkatype"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestComputeChainCandidates(t *testing.T) {
|
func TestComputeChainCandidates(t *testing.T) {
|
||||||
@ -232,7 +233,7 @@ func TestOpenAuthority(t *testing.T) {
|
|||||||
|
|
||||||
i2, i2H := fakeAUM(t, 2, &i1H)
|
i2, i2H := fakeAUM(t, 2, &i1H)
|
||||||
i3, i3H := fakeAUM(t, 5, &i2H)
|
i3, i3H := fakeAUM(t, 5, &i2H)
|
||||||
l2, l2H := fakeAUM(t, AUM{MessageKind: AUMNoOp, KeyID: []byte{7}, Signatures: []Signature{{KeyID: key.ID()}}}, &i3H)
|
l2, l2H := fakeAUM(t, AUM{MessageKind: AUMNoOp, KeyID: []byte{7}, Signatures: []tkatype.Signature{{KeyID: key.ID()}}}, &i3H)
|
||||||
l3, l3H := fakeAUM(t, 4, &i3H)
|
l3, l3H := fakeAUM(t, 4, &i3H)
|
||||||
|
|
||||||
g2, g2H := fakeAUM(t, 8, nil)
|
g2, g2H := fakeAUM(t, 8, nil)
|
||||||
|
@ -9,8 +9,8 @@ import (
|
|||||||
"crypto/subtle"
|
"crypto/subtle"
|
||||||
|
|
||||||
"go4.org/mem"
|
"go4.org/mem"
|
||||||
"tailscale.com/tka"
|
|
||||||
"tailscale.com/types/structs"
|
"tailscale.com/types/structs"
|
||||||
|
"tailscale.com/types/tkatype"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -68,23 +68,26 @@ func (k NLPrivate) Public() NLPublic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// KeyID returns an identifier for this key.
|
// KeyID returns an identifier for this key.
|
||||||
func (k NLPrivate) KeyID() tka.KeyID {
|
func (k NLPrivate) KeyID() tkatype.KeyID {
|
||||||
pub := k.Public()
|
// The correct way to compute this is:
|
||||||
return tka.Key{
|
// return tka.Key{
|
||||||
Kind: tka.Key25519,
|
// Kind: tka.Key25519,
|
||||||
Public: pub.k[:],
|
// Public: pub.k[:],
|
||||||
}.ID()
|
// }.ID()
|
||||||
|
//
|
||||||
|
// However, under the hood the key id for a 25519
|
||||||
|
// key is just the public key, so we avoid the
|
||||||
|
// dependency on tka by just doing this ourselves.
|
||||||
|
pub := k.Public().k
|
||||||
|
return pub[:]
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignAUM implements tka.UpdateSigner.
|
// SignAUM implements tka.UpdateSigner.
|
||||||
func (k NLPrivate) SignAUM(a *tka.AUM) error {
|
func (k NLPrivate) SignAUM(sigHash tkatype.AUMSigHash) ([]tkatype.Signature, error) {
|
||||||
sigHash := a.SigHash()
|
return []tkatype.Signature{{
|
||||||
|
|
||||||
a.Signatures = append(a.Signatures, tka.Signature{
|
|
||||||
KeyID: k.KeyID(),
|
KeyID: k.KeyID(),
|
||||||
Signature: ed25519.Sign(k.k[:], sigHash[:]),
|
Signature: ed25519.Sign(ed25519.PrivateKey(k.k[:]), sigHash[:]),
|
||||||
})
|
}}, nil
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NLPublic is the public portion of a a NLPrivate.
|
// NLPublic is the public portion of a a NLPrivate.
|
||||||
|
@ -6,6 +6,7 @@ package key
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/ed25519"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"tailscale.com/tka"
|
"tailscale.com/tka"
|
||||||
@ -55,7 +56,14 @@ func TestNLPrivate(t *testing.T) {
|
|||||||
if got, want := len(aum.Signatures), 1; got != want {
|
if got, want := len(aum.Signatures), 1; got != want {
|
||||||
t.Fatalf("len(signatures) = %d, want %d", got, want)
|
t.Fatalf("len(signatures) = %d, want %d", got, want)
|
||||||
}
|
}
|
||||||
if err := aum.Signatures[0].Verify(aum.SigHash(), k); err != nil {
|
sigHash := aum.SigHash()
|
||||||
t.Errorf("signature did not verify: %v", err)
|
if ok := ed25519.Verify(pub.Verifier(), sigHash[:], aum.Signatures[0].Signature); !ok {
|
||||||
|
t.Error("signature did not verify")
|
||||||
|
}
|
||||||
|
|
||||||
|
// We manually compute the keyID, so make sure its consistent with
|
||||||
|
// tka.Key.ID().
|
||||||
|
if !bytes.Equal(k.ID(), p.KeyID()) {
|
||||||
|
t.Errorf("private.KeyID() & tka KeyID differ: %x != %x", k.ID(), p.KeyID())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
34
types/tkatype/tkatype.go
Normal file
34
types/tkatype/tkatype.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package tkatype defines types for working with the tka package.
|
||||||
|
//
|
||||||
|
// Do not add extra dependencies to this package unless they are tiny,
|
||||||
|
// because this package encodes wire types that should be lightweight to use.
|
||||||
|
package tkatype
|
||||||
|
|
||||||
|
// KeyID references a verification key stored in the key authority. A keyID
|
||||||
|
// uniquely identifies a key. KeyIDs are all 32 bytes.
|
||||||
|
//
|
||||||
|
// For 25519 keys: We just use the 32-byte public key.
|
||||||
|
//
|
||||||
|
// Even though this is a 32-byte value, we use a byte slice because
|
||||||
|
// CBOR-encoded byte slices have a different prefix to CBOR-encoded arrays.
|
||||||
|
// Encoding as a byte slice allows us to change the size in the future if we
|
||||||
|
// ever need to.
|
||||||
|
type KeyID []byte
|
||||||
|
|
||||||
|
// MarshaledSignature represents a marshaled tka.NodeKeySignature.
|
||||||
|
type MarshaledSignature []byte
|
||||||
|
|
||||||
|
// AUMSigHash represents the BLAKE2s digest of an Authority Update
|
||||||
|
// Message (AUM), sans any signatures.
|
||||||
|
type AUMSigHash [32]byte
|
||||||
|
|
||||||
|
// Signature describes a signature over an AUM, which can be verified
|
||||||
|
// using the key referenced by KeyID.
|
||||||
|
type Signature struct {
|
||||||
|
KeyID KeyID `cbor:"1,keyasint"`
|
||||||
|
Signature []byte `cbor:"2,keyasint"`
|
||||||
|
}
|
17
types/tkatype/tkatype_test.go
Normal file
17
types/tkatype/tkatype_test.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package tkatype
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/crypto/blake2s"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSigHashSize(t *testing.T) {
|
||||||
|
var sigHash AUMSigHash
|
||||||
|
if len(sigHash) != blake2s.Size {
|
||||||
|
t.Errorf("AUMSigHash is wrong size: got %d, want %d", len(sigHash), blake2s.Size)
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user