mirror of
https://github.com/tailscale/tailscale.git
synced 2024-12-01 14:05:39 +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)
|
||||
|
||||
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/internal/common from github.com/alexbrainman/sspi/negotiate
|
||||
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/hdevalence/ed25519consensus from tailscale.com/tka
|
||||
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/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/tcnksm/go-httpstat from tailscale.com/net/netcheck
|
||||
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/netipx from tailscale.com/wgengine/filter
|
||||
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/syncs from tailscale.com/net/netcheck+
|
||||
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
|
||||
💣 tailscale.com/tstime/mono from tailscale.com/tstime/rate
|
||||
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/preftype from tailscale.com/cmd/tailscale/cli+
|
||||
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/util/clientmetric from tailscale.com/net/netcheck+
|
||||
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/distro from tailscale.com/cmd/tailscale/cli+
|
||||
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/blake2s from tailscale.com/control/controlbase+
|
||||
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/chacha20 from golang.org/x/crypto/chacha20poly1305
|
||||
golang.org/x/crypto/chacha20poly1305 from crypto/tls+
|
||||
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+
|
||||
encoding from encoding/json+
|
||||
encoding/asn1 from crypto/x509+
|
||||
encoding/base32 from tailscale.com/tka
|
||||
encoding/base64 from encoding/json+
|
||||
encoding/binary from compress/gzip+
|
||||
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/tailcfg from tailscale.com/client/tailscale/apitype+
|
||||
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
|
||||
tailscale.com/tstime from tailscale.com/wgengine/magicsock
|
||||
💣 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/preftype from tailscale.com/ipn+
|
||||
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/util/clientmetric from tailscale.com/control/controlclient+
|
||||
tailscale.com/util/cloudenv from tailscale.com/net/dns/resolver+
|
||||
|
13
tka/aum.go
13
tka/aum.go
@ -12,15 +12,12 @@
|
||||
|
||||
"github.com/fxamacker/cbor/v2"
|
||||
"golang.org/x/crypto/blake2s"
|
||||
"tailscale.com/types/tkatype"
|
||||
)
|
||||
|
||||
// AUMHash represents the BLAKE2s digest of an Authority Update Message (AUM).
|
||||
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.
|
||||
type AUMKind uint8
|
||||
|
||||
@ -100,7 +97,7 @@ type AUM struct {
|
||||
|
||||
// KeyID references a public key which is part of the key authority.
|
||||
// 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.
|
||||
// This field is used for Checkpoint AUMs.
|
||||
@ -118,7 +115,7 @@ type AUM struct {
|
||||
|
||||
// Signatures lists the signatures over this AUM.
|
||||
// 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.
|
||||
@ -230,7 +227,7 @@ func (a *AUM) Hash() AUMHash {
|
||||
// This is identical to Hash() except the Signatures are not
|
||||
// serialized. Without this, the hash used for signatures
|
||||
// would be circularly dependent on the signatures.
|
||||
func (a AUM) SigHash() AUMSigHash {
|
||||
func (a AUM) SigHash() tkatype.AUMSigHash {
|
||||
dupe := a
|
||||
dupe.Signatures = nil
|
||||
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)}
|
||||
sigHash := a.SigHash()
|
||||
|
||||
a.Signatures = append(a.Signatures, Signature{
|
||||
a.Signatures = append(a.Signatures, tkatype.Signature{
|
||||
KeyID: key.ID(),
|
||||
Signature: ed25519.Sign(priv, sigHash[:]),
|
||||
})
|
||||
|
@ -11,6 +11,7 @@
|
||||
"github.com/fxamacker/cbor/v2"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"golang.org/x/crypto/blake2s"
|
||||
"tailscale.com/types/tkatype"
|
||||
)
|
||||
|
||||
func TestSerialization(t *testing.T) {
|
||||
@ -137,7 +138,7 @@ func TestSerialization(t *testing.T) {
|
||||
},
|
||||
{
|
||||
"Signature",
|
||||
AUM{MessageKind: AUMAddKey, Signatures: []Signature{{KeyID: []byte{1}}}},
|
||||
AUM{MessageKind: AUMAddKey, Signatures: []tkatype.Signature{{KeyID: []byte{1}}}},
|
||||
[]byte{
|
||||
0xa3, // major type 5 (map), 3 items
|
||||
0x01, // |- major type 0 (int), value 1 (first key, MessageKind)
|
||||
@ -198,7 +199,7 @@ func TestAUMWeight(t *testing.T) {
|
||||
{
|
||||
"Key unknown",
|
||||
AUM{
|
||||
Signatures: []Signature{{KeyID: fakeKeyID[:]}},
|
||||
Signatures: []tkatype.Signature{{KeyID: fakeKeyID[:]}},
|
||||
},
|
||||
State{},
|
||||
0,
|
||||
@ -206,7 +207,7 @@ func TestAUMWeight(t *testing.T) {
|
||||
{
|
||||
"Unary key",
|
||||
AUM{
|
||||
Signatures: []Signature{{KeyID: key.ID()}},
|
||||
Signatures: []tkatype.Signature{{KeyID: key.ID()}},
|
||||
},
|
||||
State{
|
||||
Keys: []Key{key},
|
||||
@ -216,7 +217,7 @@ func TestAUMWeight(t *testing.T) {
|
||||
{
|
||||
"Multiple keys",
|
||||
AUM{
|
||||
Signatures: []Signature{{KeyID: key.ID()}, {KeyID: key2.ID()}},
|
||||
Signatures: []tkatype.Signature{{KeyID: key.ID()}, {KeyID: key2.ID()}},
|
||||
},
|
||||
State{
|
||||
Keys: []Key{key, key2},
|
||||
@ -226,7 +227,7 @@ func TestAUMWeight(t *testing.T) {
|
||||
{
|
||||
"Double use",
|
||||
AUM{
|
||||
Signatures: []Signature{{KeyID: key.ID()}, {KeyID: key.ID()}},
|
||||
Signatures: []tkatype.Signature{{KeyID: key.ID()}, {KeyID: key.ID()}},
|
||||
},
|
||||
State{
|
||||
Keys: []Key{key},
|
||||
@ -255,7 +256,7 @@ func TestAUMHashes(t *testing.T) {
|
||||
sigHash1 := aum.SigHash()
|
||||
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()
|
||||
aumHash2 := aum.Hash()
|
||||
if len(aum.Signatures) != 1 {
|
||||
|
@ -6,11 +6,13 @@
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"tailscale.com/types/tkatype"
|
||||
)
|
||||
|
||||
// Types implementing Signer can sign update messages.
|
||||
type Signer interface {
|
||||
SignAUM(*AUM) error
|
||||
SignAUM(tkatype.AUMSigHash) ([]tkatype.Signature, error)
|
||||
}
|
||||
|
||||
// UpdateBuilder implements a builder for changes to the tailnet
|
||||
@ -34,9 +36,11 @@ func (b *UpdateBuilder) mkUpdate(update AUM) error {
|
||||
update.PrevAUMHash = prevHash
|
||||
|
||||
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)
|
||||
}
|
||||
update.Signatures = append(update.Signatures, sigs...)
|
||||
}
|
||||
if err := update.StaticValidate(); err != nil {
|
||||
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.
|
||||
func (b *UpdateBuilder) RemoveKey(keyID KeyID) error {
|
||||
func (b *UpdateBuilder) RemoveKey(keyID tkatype.KeyID) error {
|
||||
if _, err := b.state.GetKey(keyID); err != nil {
|
||||
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.
|
||||
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 {
|
||||
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
|
||||
// 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 {
|
||||
return fmt.Errorf("failed reading key %x: %v", keyID, err)
|
||||
}
|
||||
|
@ -9,13 +9,19 @@
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"tailscale.com/types/tkatype"
|
||||
)
|
||||
|
||||
type signer25519 ed25519.PrivateKey
|
||||
|
||||
func (s signer25519) SignAUM(update *AUM) error {
|
||||
update.sign25519(ed25519.PrivateKey(s))
|
||||
return nil
|
||||
func (s signer25519) SignAUM(sigHash tkatype.AUMSigHash) ([]tkatype.Signature, error) {
|
||||
priv := ed25519.PrivateKey(s)
|
||||
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) {
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"tailscale.com/types/tkatype"
|
||||
)
|
||||
|
||||
// chaintest_test.go implements test helpers for concisely describing
|
||||
@ -265,7 +266,7 @@ func (c *testChain) makeAUM(v *testchainNode) AUM {
|
||||
|
||||
sigHash := aum.SigHash()
|
||||
for _, key := range c.SignAllKeys {
|
||||
aum.Signatures = append(aum.Signatures, Signature{
|
||||
aum.Signatures = append(aum.Signatures, tkatype.Signature{
|
||||
KeyID: c.Key[key].ID(),
|
||||
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
|
||||
// sign it using that key.
|
||||
if key := v.SignedWith; key != "" {
|
||||
aum.Signatures = append(aum.Signatures, Signature{
|
||||
aum.Signatures = append(aum.Signatures, tkatype.Signature{
|
||||
KeyID: c.Key[key].ID(),
|
||||
Signature: ed25519.Sign(c.KeyPrivs[key], sigHash[:]),
|
||||
})
|
||||
|
19
tka/key.go
19
tka/key.go
@ -10,6 +10,7 @@
|
||||
"fmt"
|
||||
|
||||
"github.com/hdevalence/ed25519consensus"
|
||||
"tailscale.com/types/tkatype"
|
||||
)
|
||||
|
||||
// KeyKind describes the different varieties of a Key.
|
||||
@ -73,12 +74,12 @@ func (k Key) Clone() Key {
|
||||
return out
|
||||
}
|
||||
|
||||
func (k Key) ID() KeyID {
|
||||
func (k Key) ID() tkatype.KeyID {
|
||||
switch k.Kind {
|
||||
// Because 25519 public keys are so short, we just use the 32-byte
|
||||
// public as their 'key ID'.
|
||||
case Key25519:
|
||||
return KeyID(k.Public)
|
||||
return tkatype.KeyID(k.Public)
|
||||
default:
|
||||
panic("unsupported key kind")
|
||||
}
|
||||
@ -112,21 +113,9 @@ func (k Key) StaticValidate() error {
|
||||
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
|
||||
// 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,
|
||||
// its possible for the KeyID to be attacker-controlled
|
||||
// so we should use the public contained in the state machine.
|
||||
|
@ -10,6 +10,8 @@
|
||||
"encoding/binary"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"tailscale.com/types/tkatype"
|
||||
)
|
||||
|
||||
// returns a random source based on the test name + extraSeed.
|
||||
@ -41,24 +43,24 @@ func TestVerify25519(t *testing.T) {
|
||||
MessageKind: AUMRemoveKey,
|
||||
KeyID: []byte{1, 2, 3, 4},
|
||||
// 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()
|
||||
aum.Signatures = []Signature{
|
||||
aum.Signatures = []tkatype.Signature{
|
||||
{
|
||||
KeyID: key.ID(),
|
||||
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)
|
||||
}
|
||||
|
||||
// Make sure it fails with a different public key.
|
||||
pub2, _ := testingKey25519(t, 2)
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
@ -204,7 +204,7 @@ func TestScenarioHelpers(t *testing.T) {
|
||||
if _, ok := n.AUMs["L3"]; !ok {
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
"github.com/fxamacker/cbor/v2"
|
||||
"github.com/hdevalence/ed25519consensus"
|
||||
"golang.org/x/crypto/blake2s"
|
||||
"tailscale.com/types/tkatype"
|
||||
)
|
||||
|
||||
// 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.
|
||||
func (s *NodeKeySignature) Serialize() []byte {
|
||||
func (s *NodeKeySignature) Serialize() tkatype.MarshaledSignature {
|
||||
out := bytes.NewBuffer(make([]byte, 0, 128)) // 64byte sig + 32byte keyID + 32byte headroom
|
||||
encoder, err := cbor.CTAP2EncOptions().EncMode()
|
||||
if err != nil {
|
||||
|
@ -10,6 +10,7 @@
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/crypto/argon2"
|
||||
"tailscale.com/types/tkatype"
|
||||
)
|
||||
|
||||
// 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.
|
||||
func (s State) GetKey(key KeyID) (Key, error) {
|
||||
func (s State) GetKey(key tkatype.KeyID) (Key, error) {
|
||||
for _, k := range s.Keys {
|
||||
if bytes.Equal(k.ID(), key) {
|
||||
return k, nil
|
||||
|
@ -13,6 +13,7 @@
|
||||
"sort"
|
||||
|
||||
"github.com/fxamacker/cbor/v2"
|
||||
"tailscale.com/types/tkatype"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@ -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.
|
||||
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)
|
||||
}
|
||||
genesis.Signatures = append(genesis.Signatures, sigs...)
|
||||
|
||||
a, err := Bootstrap(storage, genesis)
|
||||
return a, genesis, err
|
||||
@ -591,7 +594,7 @@ func (a *Authority) Inform(updates []AUM) error {
|
||||
|
||||
// VerifySignature returns true if the provided nodeKeySignature is signed
|
||||
// correctly by a trusted key.
|
||||
func (a *Authority) VerifySignature(nodeKeySignature []byte) error {
|
||||
func (a *Authority) VerifySignature(nodeKeySignature tkatype.MarshaledSignature) error {
|
||||
var decoded NodeKeySignature
|
||||
if err := cbor.Unmarshal(nodeKeySignature, &decoded); err != nil {
|
||||
return fmt.Errorf("unmarshal: %v", err)
|
||||
|
@ -9,6 +9,7 @@
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"tailscale.com/types/tkatype"
|
||||
)
|
||||
|
||||
func TestComputeChainCandidates(t *testing.T) {
|
||||
@ -232,7 +233,7 @@ func TestOpenAuthority(t *testing.T) {
|
||||
|
||||
i2, i2H := fakeAUM(t, 2, &i1H)
|
||||
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)
|
||||
|
||||
g2, g2H := fakeAUM(t, 8, nil)
|
||||
|
@ -9,8 +9,8 @@
|
||||
"crypto/subtle"
|
||||
|
||||
"go4.org/mem"
|
||||
"tailscale.com/tka"
|
||||
"tailscale.com/types/structs"
|
||||
"tailscale.com/types/tkatype"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -68,23 +68,26 @@ func (k NLPrivate) Public() NLPublic {
|
||||
}
|
||||
|
||||
// KeyID returns an identifier for this key.
|
||||
func (k NLPrivate) KeyID() tka.KeyID {
|
||||
pub := k.Public()
|
||||
return tka.Key{
|
||||
Kind: tka.Key25519,
|
||||
Public: pub.k[:],
|
||||
}.ID()
|
||||
func (k NLPrivate) KeyID() tkatype.KeyID {
|
||||
// The correct way to compute this is:
|
||||
// return tka.Key{
|
||||
// Kind: tka.Key25519,
|
||||
// Public: pub.k[:],
|
||||
// }.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.
|
||||
func (k NLPrivate) SignAUM(a *tka.AUM) error {
|
||||
sigHash := a.SigHash()
|
||||
|
||||
a.Signatures = append(a.Signatures, tka.Signature{
|
||||
func (k NLPrivate) SignAUM(sigHash tkatype.AUMSigHash) ([]tkatype.Signature, error) {
|
||||
return []tkatype.Signature{{
|
||||
KeyID: k.KeyID(),
|
||||
Signature: ed25519.Sign(k.k[:], sigHash[:]),
|
||||
})
|
||||
return nil
|
||||
Signature: ed25519.Sign(ed25519.PrivateKey(k.k[:]), sigHash[:]),
|
||||
}}, nil
|
||||
}
|
||||
|
||||
// NLPublic is the public portion of a a NLPrivate.
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ed25519"
|
||||
"testing"
|
||||
|
||||
"tailscale.com/tka"
|
||||
@ -55,7 +56,14 @@ func TestNLPrivate(t *testing.T) {
|
||||
if got, want := len(aum.Signatures), 1; got != want {
|
||||
t.Fatalf("len(signatures) = %d, want %d", got, want)
|
||||
}
|
||||
if err := aum.Signatures[0].Verify(aum.SigHash(), k); err != nil {
|
||||
t.Errorf("signature did not verify: %v", err)
|
||||
sigHash := aum.SigHash()
|
||||
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…
Reference in New Issue
Block a user