mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-29 04:55:31 +00:00
tka: support rotating node-keys in node-key signatures
Signed-off-by: Tom DNetto <tom@tailscale.com>
This commit is contained in:
parent
b3cc719add
commit
a78f8fa701
@ -11,6 +11,7 @@
|
|||||||
"math/rand"
|
"math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"tailscale.com/types/key"
|
||||||
"tailscale.com/types/tkatype"
|
"tailscale.com/types/tkatype"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -64,3 +65,34 @@ func TestVerify25519(t *testing.T) {
|
|||||||
t.Error("signature verification with different key did not fail")
|
t.Error("signature verification with different key did not fail")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNLPrivate(t *testing.T) {
|
||||||
|
p := key.NewNLPrivate()
|
||||||
|
pub := p.Public()
|
||||||
|
|
||||||
|
// Test that key.NLPrivate implements Signer by making a new
|
||||||
|
// authority.
|
||||||
|
k := Key{Kind: Key25519, Public: pub.Verifier(), Votes: 1}
|
||||||
|
_, aum, err := Create(&Mem{}, State{
|
||||||
|
Keys: []Key{k},
|
||||||
|
DisablementSecrets: [][]byte{bytes.Repeat([]byte{1}, 32)},
|
||||||
|
}, p)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Create() failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the generated genesis AUM was signed.
|
||||||
|
if got, want := len(aum.Signatures), 1; got != want {
|
||||||
|
t.Fatalf("len(signatures) = %d, want %d", got, want)
|
||||||
|
}
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
95
tka/sig.go
95
tka/sig.go
@ -13,6 +13,7 @@
|
|||||||
"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/key"
|
||||||
"tailscale.com/types/tkatype"
|
"tailscale.com/types/tkatype"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -21,9 +22,17 @@
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
SigInvalid SigKind = iota
|
SigInvalid SigKind = iota
|
||||||
// SigDirect describes a signature over a specific node key, using
|
// SigDirect describes a signature over a specific node key, signed
|
||||||
// the keyID specified.
|
// by a key in the tailnet key authority referenced by the specified keyID.
|
||||||
SigDirect
|
SigDirect
|
||||||
|
// SigRotation describes a signature over a specific node key, signed
|
||||||
|
// by the rotation key authorized by a nested NodeKeySignature structure.
|
||||||
|
//
|
||||||
|
// While it is possible to nest rotations multiple times up to the CBOR
|
||||||
|
// nesting limit, it is intended that nodes simply regenerate their outer
|
||||||
|
// SigRotation signature and sign it again with their rotation key. That
|
||||||
|
// way, SigRotation nesting should only be 2 deep in the common case.
|
||||||
|
SigRotation
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s SigKind) String() string {
|
func (s SigKind) String() string {
|
||||||
@ -32,6 +41,8 @@ func (s SigKind) String() string {
|
|||||||
return "invalid"
|
return "invalid"
|
||||||
case SigDirect:
|
case SigDirect:
|
||||||
return "direct"
|
return "direct"
|
||||||
|
case SigRotation:
|
||||||
|
return "rotation"
|
||||||
default:
|
default:
|
||||||
return fmt.Sprintf("Sig?<%d>", int(s))
|
return fmt.Sprintf("Sig?<%d>", int(s))
|
||||||
}
|
}
|
||||||
@ -42,7 +53,7 @@ func (s SigKind) String() string {
|
|||||||
type NodeKeySignature struct {
|
type NodeKeySignature struct {
|
||||||
// SigKind identifies the variety of signature.
|
// SigKind identifies the variety of signature.
|
||||||
SigKind SigKind `cbor:"1,keyasint"`
|
SigKind SigKind `cbor:"1,keyasint"`
|
||||||
// Pubkey identifies the public key which is being certified.
|
// Pubkey identifies the public key which is being authorized.
|
||||||
Pubkey []byte `cbor:"2,keyasint"`
|
Pubkey []byte `cbor:"2,keyasint"`
|
||||||
|
|
||||||
// KeyID identifies which key in the tailnet key authority should
|
// KeyID identifies which key in the tailnet key authority should
|
||||||
@ -50,9 +61,39 @@ type NodeKeySignature struct {
|
|||||||
// SigCredential signature kinds.
|
// SigCredential signature kinds.
|
||||||
KeyID []byte `cbor:"3,keyasint,omitempty"`
|
KeyID []byte `cbor:"3,keyasint,omitempty"`
|
||||||
|
|
||||||
// Signature is the packed (R, S) ed25519 signature over the rest
|
// Signature is the packed (R, S) ed25519 signature over all other
|
||||||
// of the structure.
|
// fields of the structure.
|
||||||
Signature []byte `cbor:"4,keyasint,omitempty"`
|
Signature []byte `cbor:"4,keyasint,omitempty"`
|
||||||
|
|
||||||
|
// Nested describes a NodeKeySignature which authorizes the node-key
|
||||||
|
// used as Pubkey. Only used for SigRotation signatures.
|
||||||
|
Nested *NodeKeySignature `cbor:"5,keyasint,omitempty"`
|
||||||
|
|
||||||
|
// RotationPubkey specifies the ed25519 public key which may sign a
|
||||||
|
// SigRotation signature, which embeds this one.
|
||||||
|
//
|
||||||
|
// Intermediate SigRotation signatures may omit this value to use the
|
||||||
|
// parent one.
|
||||||
|
RotationPubkey []byte `cbor:"6,keyasint,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// rotationPublic returns the public key which must sign a SigRotation
|
||||||
|
// signature that embeds this signature, if any.
|
||||||
|
func (s NodeKeySignature) rotationPublic() (pub ed25519.PublicKey, ok bool) {
|
||||||
|
if len(s.RotationPubkey) > 0 {
|
||||||
|
return ed25519.PublicKey(s.RotationPubkey), true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch s.SigKind {
|
||||||
|
case SigRotation:
|
||||||
|
if s.Nested == nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return s.Nested.rotationPublic()
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SigHash returns the cryptographic digest which a signature
|
// SigHash returns the cryptographic digest which a signature
|
||||||
@ -97,10 +138,44 @@ func (s *NodeKeySignature) Unserialize(data []byte) error {
|
|||||||
return dec.Unmarshal(data, s)
|
return dec.Unmarshal(data, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// verifySignature checks that the NodeKeySignature is authentic and certified
|
// verifySignature checks that the NodeKeySignature is authentic, certified
|
||||||
// by the given verificationKey.
|
// by the given verificationKey, and authorizes the given nodeKey.
|
||||||
func (s *NodeKeySignature) verifySignature(verificationKey Key) error {
|
func (s *NodeKeySignature) verifySignature(nodeKey key.NodePublic, verificationKey Key) error {
|
||||||
|
nodeBytes, err := nodeKey.MarshalBinary()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("marshalling pubkey: %v", err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(nodeBytes, s.Pubkey) {
|
||||||
|
return errors.New("signature does not authorize nodeKey")
|
||||||
|
}
|
||||||
|
|
||||||
sigHash := s.SigHash()
|
sigHash := s.SigHash()
|
||||||
|
switch s.SigKind {
|
||||||
|
case SigRotation:
|
||||||
|
if s.Nested == nil {
|
||||||
|
return errors.New("nested signatures must nest a signature")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the signature using the nested rotation key.
|
||||||
|
verifyPub, ok := s.Nested.rotationPublic()
|
||||||
|
if !ok {
|
||||||
|
return errors.New("missing rotation key")
|
||||||
|
}
|
||||||
|
if !ed25519.Verify(ed25519.PublicKey(verifyPub[:]), sigHash[:], s.Signature) {
|
||||||
|
return errors.New("invalid signature")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recurse to verify the signature on the nested structure.
|
||||||
|
var nestedPub key.NodePublic
|
||||||
|
if err := nestedPub.UnmarshalBinary(s.Nested.Pubkey); err != nil {
|
||||||
|
return fmt.Errorf("nested pubkey: %v", err)
|
||||||
|
}
|
||||||
|
if err := s.Nested.verifySignature(nestedPub, verificationKey); err != nil {
|
||||||
|
return fmt.Errorf("nested: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case SigDirect:
|
||||||
switch verificationKey.Kind {
|
switch verificationKey.Kind {
|
||||||
case Key25519:
|
case Key25519:
|
||||||
if ed25519consensus.Verify(ed25519.PublicKey(verificationKey.Public), sigHash[:], s.Signature) {
|
if ed25519consensus.Verify(ed25519.PublicKey(verificationKey.Public), sigHash[:], s.Signature) {
|
||||||
@ -111,4 +186,8 @@ func (s *NodeKeySignature) verifySignature(verificationKey Key) error {
|
|||||||
default:
|
default:
|
||||||
return fmt.Errorf("unhandled key type: %v", verificationKey.Kind)
|
return fmt.Errorf("unhandled key type: %v", verificationKey.Kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unhandled signature type: %v", s.SigKind)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
153
tka/sig_test.go
153
tka/sig_test.go
@ -9,18 +9,20 @@
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"tailscale.com/types/key"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSigDirect(t *testing.T) {
|
func TestSigDirect(t *testing.T) {
|
||||||
nodeKeyPub := []byte{1, 2, 3, 4}
|
node := key.NewNode()
|
||||||
|
nodeKeyPub, _ := node.Public().MarshalBinary()
|
||||||
|
|
||||||
// Verification key (the key used to sign)
|
// Verification key (the key used to sign)
|
||||||
pub, priv := testingKey25519(t, 1)
|
pub, priv := testingKey25519(t, 1)
|
||||||
key := Key{Kind: Key25519, Public: pub, Votes: 2}
|
k := Key{Kind: Key25519, Public: pub, Votes: 2}
|
||||||
|
|
||||||
sig := NodeKeySignature{
|
sig := NodeKeySignature{
|
||||||
SigKind: SigDirect,
|
SigKind: SigDirect,
|
||||||
KeyID: key.ID(),
|
KeyID: k.ID(),
|
||||||
Pubkey: nodeKeyPub,
|
Pubkey: nodeKeyPub,
|
||||||
}
|
}
|
||||||
sigHash := sig.SigHash()
|
sigHash := sig.SigHash()
|
||||||
@ -30,9 +32,147 @@ func TestSigDirect(t *testing.T) {
|
|||||||
t.Errorf("sigHash changed after signing: %x != %x", sig.SigHash(), sigHash)
|
t.Errorf("sigHash changed after signing: %x != %x", sig.SigHash(), sigHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := sig.verifySignature(key); err != nil {
|
if err := sig.verifySignature(node.Public(), k); err != nil {
|
||||||
t.Fatalf("verifySignature() failed: %v", err)
|
t.Fatalf("verifySignature() failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test verification fails when verifying for a different node
|
||||||
|
if err := sig.verifySignature(key.NewNode().Public(), k); err == nil {
|
||||||
|
t.Error("verifySignature() did not error for different nodekey")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test verification fails if the wrong verification key is provided
|
||||||
|
copy(k.Public, []byte{1, 2, 3, 4})
|
||||||
|
if err := sig.verifySignature(node.Public(), k); err == nil {
|
||||||
|
t.Error("verifySignature() did not error for wrong verification key")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSigNested(t *testing.T) {
|
||||||
|
// Network-lock key (the key used to sign the nested sig)
|
||||||
|
pub, priv := testingKey25519(t, 1)
|
||||||
|
k := Key{Kind: Key25519, Public: pub, Votes: 2}
|
||||||
|
// Rotation key (the key used to sign the outer sig)
|
||||||
|
rPub, rPriv := testingKey25519(t, 2)
|
||||||
|
// The old node key which is being rotated out
|
||||||
|
oldNode := key.NewNode()
|
||||||
|
oldPub, _ := oldNode.Public().MarshalBinary()
|
||||||
|
// The new node key that is being rotated in
|
||||||
|
node := key.NewNode()
|
||||||
|
nodeKeyPub, _ := node.Public().MarshalBinary()
|
||||||
|
|
||||||
|
// The original signature for the old node key, signed by
|
||||||
|
// the network-lock key.
|
||||||
|
nestedSig := NodeKeySignature{
|
||||||
|
SigKind: SigDirect,
|
||||||
|
KeyID: k.ID(),
|
||||||
|
Pubkey: oldPub,
|
||||||
|
RotationPubkey: rPub,
|
||||||
|
}
|
||||||
|
sigHash := nestedSig.SigHash()
|
||||||
|
nestedSig.Signature = ed25519.Sign(priv, sigHash[:])
|
||||||
|
if err := nestedSig.verifySignature(oldNode.Public(), k); err != nil {
|
||||||
|
t.Fatalf("verifySignature(oldNode) failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The signature authorizing the rotation, signed by the
|
||||||
|
// rotation key & embedding the original signature.
|
||||||
|
sig := NodeKeySignature{
|
||||||
|
SigKind: SigRotation,
|
||||||
|
KeyID: k.ID(),
|
||||||
|
Pubkey: nodeKeyPub,
|
||||||
|
Nested: &nestedSig,
|
||||||
|
}
|
||||||
|
sigHash = sig.SigHash()
|
||||||
|
sig.Signature = ed25519.Sign(rPriv, sigHash[:])
|
||||||
|
|
||||||
|
if err := sig.verifySignature(node.Public(), k); err != nil {
|
||||||
|
t.Fatalf("verifySignature(node) failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test verification fails if the wrong verification key is provided
|
||||||
|
kBad := Key{Kind: Key25519, Public: []byte{1, 2, 3, 4}, Votes: 2}
|
||||||
|
if err := sig.verifySignature(node.Public(), kBad); err == nil {
|
||||||
|
t.Error("verifySignature() did not error for wrong verification key")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test verification fails if the inner signature is invalid
|
||||||
|
tmp := make([]byte, ed25519.SignatureSize)
|
||||||
|
copy(tmp, nestedSig.Signature)
|
||||||
|
copy(nestedSig.Signature, []byte{1, 2, 3, 4})
|
||||||
|
if err := sig.verifySignature(node.Public(), k); err == nil {
|
||||||
|
t.Error("verifySignature(node) succeeded with bad inner signature")
|
||||||
|
}
|
||||||
|
copy(nestedSig.Signature, tmp)
|
||||||
|
|
||||||
|
// Test verification fails if the outer signature is invalid
|
||||||
|
copy(sig.Signature, []byte{1, 2, 3, 4})
|
||||||
|
if err := sig.verifySignature(node.Public(), k); err == nil {
|
||||||
|
t.Error("verifySignature(node) succeeded with bad outer signature")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSigNested_DeepNesting(t *testing.T) {
|
||||||
|
// Network-lock key (the key used to sign the nested sig)
|
||||||
|
pub, priv := testingKey25519(t, 1)
|
||||||
|
k := Key{Kind: Key25519, Public: pub, Votes: 2}
|
||||||
|
// Rotation key (the key used to sign the outer sig)
|
||||||
|
rPub, rPriv := testingKey25519(t, 2)
|
||||||
|
// The old node key which is being rotated out
|
||||||
|
oldNode := key.NewNode()
|
||||||
|
oldPub, _ := oldNode.Public().MarshalBinary()
|
||||||
|
|
||||||
|
// The original signature for the old node key, signed by
|
||||||
|
// the network-lock key.
|
||||||
|
nestedSig := NodeKeySignature{
|
||||||
|
SigKind: SigDirect,
|
||||||
|
KeyID: k.ID(),
|
||||||
|
Pubkey: oldPub,
|
||||||
|
RotationPubkey: rPub,
|
||||||
|
}
|
||||||
|
sigHash := nestedSig.SigHash()
|
||||||
|
nestedSig.Signature = ed25519.Sign(priv, sigHash[:])
|
||||||
|
if err := nestedSig.verifySignature(oldNode.Public(), k); err != nil {
|
||||||
|
t.Fatalf("verifySignature(oldNode) failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
outer := nestedSig
|
||||||
|
var lastNodeKey key.NodePrivate
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
lastNodeKey = key.NewNode()
|
||||||
|
nodeKeyPub, _ := lastNodeKey.Public().MarshalBinary()
|
||||||
|
|
||||||
|
tmp := outer
|
||||||
|
sig := NodeKeySignature{
|
||||||
|
SigKind: SigRotation,
|
||||||
|
KeyID: k.ID(),
|
||||||
|
Pubkey: nodeKeyPub,
|
||||||
|
Nested: &tmp,
|
||||||
|
}
|
||||||
|
sigHash = sig.SigHash()
|
||||||
|
sig.Signature = ed25519.Sign(rPriv, sigHash[:])
|
||||||
|
|
||||||
|
outer = sig
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := outer.verifySignature(lastNodeKey.Public(), k); err != nil {
|
||||||
|
t.Fatalf("verifySignature(lastNodeKey) failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test verification fails if the inner signature is invalid
|
||||||
|
tmp := make([]byte, ed25519.SignatureSize)
|
||||||
|
copy(tmp, nestedSig.Signature)
|
||||||
|
copy(nestedSig.Signature, []byte{1, 2, 3, 4})
|
||||||
|
if err := outer.verifySignature(lastNodeKey.Public(), k); err == nil {
|
||||||
|
t.Error("verifySignature(lastNodeKey) succeeded with bad inner signature")
|
||||||
|
}
|
||||||
|
copy(nestedSig.Signature, tmp)
|
||||||
|
|
||||||
|
// Test verification fails if an intermediate signature is invalid
|
||||||
|
copy(outer.Nested.Nested.Signature, []byte{1, 2, 3, 4})
|
||||||
|
if err := outer.verifySignature(lastNodeKey.Public(), k); err == nil {
|
||||||
|
t.Error("verifySignature(lastNodeKey) succeeded with bad outer signature")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSigSerializeUnserialize(t *testing.T) {
|
func TestSigSerializeUnserialize(t *testing.T) {
|
||||||
@ -43,6 +183,11 @@ func TestSigSerializeUnserialize(t *testing.T) {
|
|||||||
SigKind: SigDirect,
|
SigKind: SigDirect,
|
||||||
KeyID: key.ID(),
|
KeyID: key.ID(),
|
||||||
Pubkey: nodeKeyPub,
|
Pubkey: nodeKeyPub,
|
||||||
|
Nested: &NodeKeySignature{
|
||||||
|
SigKind: SigDirect,
|
||||||
|
KeyID: key.ID(),
|
||||||
|
Pubkey: nodeKeyPub,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
sigHash := sig.SigHash()
|
sigHash := sig.SigHash()
|
||||||
sig.Signature = ed25519.Sign(priv, sigHash[:])
|
sig.Signature = ed25519.Sign(priv, sigHash[:])
|
||||||
|
11
tka/tka.go
11
tka/tka.go
@ -13,6 +13,7 @@
|
|||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/fxamacker/cbor/v2"
|
"github.com/fxamacker/cbor/v2"
|
||||||
|
"tailscale.com/types/key"
|
||||||
"tailscale.com/types/tkatype"
|
"tailscale.com/types/tkatype"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -23,7 +24,7 @@
|
|||||||
TagsMd: cbor.TagsForbidden,
|
TagsMd: cbor.TagsForbidden,
|
||||||
|
|
||||||
// Arbitrarily-chosen maximums.
|
// Arbitrarily-chosen maximums.
|
||||||
MaxNestedLevels: 8,
|
MaxNestedLevels: 16, // Most likely to be hit for SigRotation sigs.
|
||||||
MaxArrayElements: 4096,
|
MaxArrayElements: 4096,
|
||||||
MaxMapPairs: 1024,
|
MaxMapPairs: 1024,
|
||||||
}
|
}
|
||||||
@ -604,9 +605,9 @@ func (a *Authority) Inform(updates []AUM) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifySignature returns true if the provided nodeKeySignature is signed
|
// NodeKeyAuthorized checks if the provided nodeKeySignature authorizes
|
||||||
// correctly by a trusted key.
|
// the given node key.
|
||||||
func (a *Authority) VerifySignature(nodeKeySignature tkatype.MarshaledSignature) error {
|
func (a *Authority) NodeKeyAuthorized(nodeKey key.NodePublic, nodeKeySignature tkatype.MarshaledSignature) error {
|
||||||
var decoded NodeKeySignature
|
var decoded NodeKeySignature
|
||||||
if err := decoded.Unserialize(nodeKeySignature); err != nil {
|
if err := decoded.Unserialize(nodeKeySignature); err != nil {
|
||||||
return fmt.Errorf("unserialize: %v", err)
|
return fmt.Errorf("unserialize: %v", err)
|
||||||
@ -616,7 +617,7 @@ func (a *Authority) VerifySignature(nodeKeySignature tkatype.MarshaledSignature)
|
|||||||
return fmt.Errorf("key: %v", err)
|
return fmt.Errorf("key: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return decoded.verifySignature(key)
|
return decoded.verifySignature(nodeKey, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// KeyTrusted returns true if the given keyID is trusted by the tailnet
|
// KeyTrusted returns true if the given keyID is trusted by the tailnet
|
||||||
|
@ -6,10 +6,7 @@
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/ed25519"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"tailscale.com/tka"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNLPrivate(t *testing.T) {
|
func TestNLPrivate(t *testing.T) {
|
||||||
@ -40,30 +37,4 @@ func TestNLPrivate(t *testing.T) {
|
|||||||
if !bytes.Equal(decodedPub.k[:], pub.k[:]) {
|
if !bytes.Equal(decodedPub.k[:], pub.k[:]) {
|
||||||
t.Error("decoded and generated NLPublic bytes differ")
|
t.Error("decoded and generated NLPublic bytes differ")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that NLPrivate implements tka.Signer by making a new
|
|
||||||
// authority.
|
|
||||||
k := tka.Key{Kind: tka.Key25519, Public: pub.Verifier(), Votes: 1}
|
|
||||||
_, aum, err := tka.Create(&tka.Mem{}, tka.State{
|
|
||||||
Keys: []tka.Key{k},
|
|
||||||
DisablementSecrets: [][]byte{bytes.Repeat([]byte{1}, 32)},
|
|
||||||
}, p)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("tka.Create() failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure the generated genesis AUM was signed.
|
|
||||||
if got, want := len(aum.Signatures), 1; got != want {
|
|
||||||
t.Fatalf("len(signatures) = %d, want %d", got, want)
|
|
||||||
}
|
|
||||||
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())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user