From ff16e58d234503ce5dc125fa4c4a1185688aea73 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 29 Oct 2021 16:02:38 -0700 Subject: [PATCH] tailcfg: move NodeKey type to types/key. This leaves behind a type alias and associated constructor, to allow for gradual switchover. Updates #3206. Signed-off-by: David Anderson --- tailcfg/tailcfg.go | 33 ++++----------- tailcfg/tailcfg_test.go | 55 ++----------------------- types/key/node.go | 7 ++++ types/key/node_legacy.go | 28 +++++++++++++ types/key/node_legacy_test.go | 75 +++++++++++++++++++++++++++++++++++ 5 files changed, 121 insertions(+), 77 deletions(-) create mode 100644 types/key/node_legacy.go create mode 100644 types/key/node_legacy_test.go diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go index 2f00e4b3a..e825bf974 100644 --- a/tailcfg/tailcfg.go +++ b/tailcfg/tailcfg.go @@ -78,8 +78,11 @@ func (u StableNodeID) IsZero() bool { return u == "" } -// NodeKey is the curve25519 public key for a node. -type NodeKey [32]byte +// NodeKey is the WireGuard public key for a node. +// +// Deprecated: prefer to use key.NodePublic instead. If you must have +// a NodeKey, use NodePublic.AsNodeKey. +type NodeKey = key.NodeKey // NodeKeyFromNodePublic returns k converted to a NodeKey. // @@ -87,14 +90,14 @@ type NodeKey [32]byte // gets removed from the codebase. Do not introduce new uses that // aren't related to #3206. func NodeKeyFromNodePublic(k key.NodePublic) NodeKey { - return k.Raw32() + return k.AsNodeKey() } // DiscoKey is the curve25519 public key for path discovery key. // It's never written to disk or reused between network start-ups. type DiscoKey [32]byte -// NodeKeyFromNodePublic returns k converted to a DiscoKey. +// DiscoKeyFromNodePublic returns k converted to a DiscoKey. // // Deprecated: exists only as a compatibility bridge while DiscoKey // gets removed from the codebase. Do not introduce new uses that @@ -1145,28 +1148,6 @@ func keyMarshalText(prefix string, k [32]byte) []byte { return appendKey(nil, prefix, k) } -func (k NodeKey) ShortString() string { return key.NodePublicFromRaw32(mem.B(k[:])).ShortString() } - -func (k NodeKey) String() string { return fmt.Sprintf("nodekey:%x", k[:]) } -func (k NodeKey) MarshalText() ([]byte, error) { - nk := key.NodePublicFromRaw32(mem.B(k[:])) - return nk.MarshalText() -} -func (k *NodeKey) UnmarshalText(text []byte) error { - var nk key.NodePublic - if err := nk.UnmarshalText(text); err != nil { - return err - } - nk.AppendTo(k[:0]) - return nil -} -func (k NodeKey) AsNodePublic() key.NodePublic { - return key.NodePublicFromRaw32(mem.B(k[:])) -} - -// IsZero reports whether k is the zero value. -func (k NodeKey) IsZero() bool { return k == NodeKey{} } - func (k DiscoKey) String() string { return fmt.Sprintf("discokey:%x", k[:]) } func (k DiscoKey) MarshalText() ([]byte, error) { dk := key.DiscoPublicFromRaw32(mem.B(k[:])) diff --git a/tailcfg/tailcfg_test.go b/tailcfg/tailcfg_test.go index 9d86ef403..edd730d2b 100644 --- a/tailcfg/tailcfg_test.go +++ b/tailcfg/tailcfg_test.go @@ -5,7 +5,6 @@ package tailcfg import ( - "bytes" "encoding" "encoding/json" "reflect" @@ -265,13 +264,13 @@ func TestNodeEqual(t *testing.T) { true, }, { - &Node{Key: NodeKeyFromNodePublic(n1)}, - &Node{Key: NodeKeyFromNodePublic(key.NewNode().Public())}, + &Node{Key: n1.AsNodeKey()}, + &Node{Key: key.NewNode().Public().AsNodeKey()}, false, }, { - &Node{Key: NodeKeyFromNodePublic(n1)}, - &Node{Key: NodeKeyFromNodePublic(n1)}, + &Node{Key: n1.AsNodeKey()}, + &Node{Key: n1.AsNodeKey()}, true, }, { @@ -408,52 +407,6 @@ func TestNetInfoFields(t *testing.T) { } } -func TestNodeKeyMarshal(t *testing.T) { - var k1, k2 NodeKey - for i := range k1 { - k1[i] = byte(i) - } - testKey(t, "nodekey:", k1, &k2) -} - -func TestNodeKeyRoundTrip(t *testing.T) { - serialized := `{ - "Pub":"nodekey:50d20b455ecf12bc453f83c2cfdb2a24925d06cf2598dcaa54e91af82ce9f765" - }` - - // Carefully check that the expected serialized data decodes and - // re-encodes to the expected keys. These types are serialized to - // disk all over the place and need to be stable. - pub := NodeKey{ - 0x50, 0xd2, 0xb, 0x45, 0x5e, 0xcf, 0x12, 0xbc, 0x45, 0x3f, 0x83, - 0xc2, 0xcf, 0xdb, 0x2a, 0x24, 0x92, 0x5d, 0x6, 0xcf, 0x25, 0x98, - 0xdc, 0xaa, 0x54, 0xe9, 0x1a, 0xf8, 0x2c, 0xe9, 0xf7, 0x65, - } - - type key struct { - Pub NodeKey - } - - var a key - if err := json.Unmarshal([]byte(serialized), &a); err != nil { - t.Fatal(err) - } - if a.Pub != pub { - t.Errorf("wrong deserialization of public key, got %#v want %#v", a.Pub, pub) - } - - bs, err := json.MarshalIndent(a, "", " ") - if err != nil { - t.Fatal(err) - } - - var b bytes.Buffer - json.Indent(&b, []byte(serialized), "", " ") - if got, want := string(bs), b.String(); got != want { - t.Error("json serialization doesn't roundtrip") - } -} - func TestDiscoKeyMarshal(t *testing.T) { var k1, k2 DiscoKey for i := range k1 { diff --git a/types/key/node.go b/types/key/node.go index c83aba15a..bf7088c3d 100644 --- a/types/key/node.go +++ b/types/key/node.go @@ -307,3 +307,10 @@ func (k NodePublic) WireGuardGoString() string { b[second+3] = b64((k.k[31] << 2) & 63) return string(b) } + +// AsNodeKey returns k converted to a NodeKey. +// +// Cross-compatibility shim as part of #3206. +func (k NodePublic) AsNodeKey() NodeKey { + return k.Raw32() +} diff --git a/types/key/node_legacy.go b/types/key/node_legacy.go new file mode 100644 index 000000000..7fc1944cf --- /dev/null +++ b/types/key/node_legacy.go @@ -0,0 +1,28 @@ +// Copyright (c) 2021 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 key + +import ( + "go4.org/mem" +) + +// NodeKey is the legacy form of NodePublic. +// See #3206 for removal effort. +type NodeKey [32]byte + +func (k NodeKey) ShortString() string { return k.AsNodePublic().ShortString() } +func (k NodeKey) String() string { return k.AsNodePublic().String() } +func (k NodeKey) MarshalText() ([]byte, error) { return k.AsNodePublic().MarshalText() } +func (k NodeKey) AsNodePublic() NodePublic { return NodePublicFromRaw32(mem.B(k[:])) } +func (k NodeKey) IsZero() bool { return k == NodeKey{} } + +func (k *NodeKey) UnmarshalText(text []byte) error { + var nk NodePublic + if err := nk.UnmarshalText(text); err != nil { + return err + } + *k = nk.AsNodeKey() + return nil +} diff --git a/types/key/node_legacy_test.go b/types/key/node_legacy_test.go new file mode 100644 index 000000000..e70143b10 --- /dev/null +++ b/types/key/node_legacy_test.go @@ -0,0 +1,75 @@ +// Copyright (c) 2021 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 key + +import ( + "bytes" + "encoding/json" + "strings" + "testing" +) + +func TestNodeKeyMarshal(t *testing.T) { + var k1, k2 NodeKey + for i := range k1 { + k1[i] = byte(i) + } + + const prefix = "nodekey:" + got, err := k1.MarshalText() + if err != nil { + t.Fatal(err) + } + if err := k2.UnmarshalText(got); err != nil { + t.Fatal(err) + } + if s := k1.String(); string(got) != s { + t.Errorf("MarshalText = %q != String %q", got, s) + } + if !strings.HasPrefix(string(got), prefix) { + t.Errorf("%q didn't start with prefix %q", got, prefix) + } + if k2 != k1 { + t.Errorf("mismatch after unmarshal") + } +} + +func TestNodeKeyRoundTrip(t *testing.T) { + serialized := `{ + "Pub":"nodekey:50d20b455ecf12bc453f83c2cfdb2a24925d06cf2598dcaa54e91af82ce9f765" + }` + + // Carefully check that the expected serialized data decodes and + // re-encodes to the expected keys. These types are serialized to + // disk all over the place and need to be stable. + pub := NodeKey{ + 0x50, 0xd2, 0xb, 0x45, 0x5e, 0xcf, 0x12, 0xbc, 0x45, 0x3f, 0x83, + 0xc2, 0xcf, 0xdb, 0x2a, 0x24, 0x92, 0x5d, 0x6, 0xcf, 0x25, 0x98, + 0xdc, 0xaa, 0x54, 0xe9, 0x1a, 0xf8, 0x2c, 0xe9, 0xf7, 0x65, + } + + type key struct { + Pub NodeKey + } + + var a key + if err := json.Unmarshal([]byte(serialized), &a); err != nil { + t.Fatal(err) + } + if a.Pub != pub { + t.Errorf("wrong deserialization of public key, got %#v want %#v", a.Pub, pub) + } + + bs, err := json.MarshalIndent(a, "", " ") + if err != nil { + t.Fatal(err) + } + + var b bytes.Buffer + json.Indent(&b, []byte(serialized), "", " ") + if got, want := string(bs), b.String(); got != want { + t.Error("json serialization doesn't roundtrip") + } +}