From a9c78910bd4008aa4e0e7ef9f4c0946b73e003f0 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Wed, 27 Oct 2021 17:42:33 -0700 Subject: [PATCH] wgengine/wgcfg: convert to use new node key type. Updates #3206 Signed-off-by: David Anderson --- types/key/node.go | 13 +++++++ util/deephash/deephash_test.go | 4 +-- wgengine/bench/wg.go | 20 ++++------- wgengine/magicsock/magicsock_test.go | 9 ++--- wgengine/userspace.go | 36 +++++++++---------- wgengine/userspace_test.go | 14 +++++--- wgengine/wgcfg/clone.go | 6 ++-- wgengine/wgcfg/config.go | 8 ++--- wgengine/wgcfg/device.go | 2 +- wgengine/wgcfg/device_test.go | 23 ++++++------ wgengine/wgcfg/nmcfg/nmcfg.go | 7 ++-- wgengine/wgcfg/parser.go | 54 ++++++++++++++-------------- wgengine/wgcfg/parser_test.go | 15 ++++---- wgengine/wgcfg/writer.go | 12 +++---- wgengine/wglog/wglog.go | 32 ++++------------- wgengine/wglog/wglog_test.go | 9 ++--- 16 files changed, 127 insertions(+), 137 deletions(-) diff --git a/types/key/node.go b/types/key/node.go index 6618e8c5e..0542c147a 100644 --- a/types/key/node.go +++ b/types/key/node.go @@ -54,6 +54,19 @@ func NewNode() NodePrivate { return ret } +// NodePrivateFromRaw32 parses a 32-byte raw value as a NodePrivate. +// +// Deprecated: only needed to cast from legacy node private key types, +// do not add more uses unrelated to #3206. +func NodePrivateFromRaw32(raw mem.RO) NodePrivate { + if raw.Len() != 32 { + panic("input has wrong size") + } + var ret NodePrivate + raw.Copy(ret.k[:]) + return ret +} + func ParseNodePrivateUntyped(raw mem.RO) (NodePrivate, error) { var ret NodePrivate if err := parseHex(ret.k[:], raw, mem.B(nil)); err != nil { diff --git a/util/deephash/deephash_test.go b/util/deephash/deephash_test.go index ca9b5464d..3319d0b3f 100644 --- a/util/deephash/deephash_test.go +++ b/util/deephash/deephash_test.go @@ -19,7 +19,7 @@ "tailscale.com/tailcfg" "tailscale.com/types/dnstype" "tailscale.com/types/ipproto" - "tailscale.com/types/wgkey" + "tailscale.com/types/key" "tailscale.com/util/dnsname" "tailscale.com/version" "tailscale.com/wgengine/filter" @@ -138,7 +138,7 @@ func getVal() []interface{} { Addresses: []netaddr.IPPrefix{netaddr.IPPrefixFrom(netaddr.IPFrom16([16]byte{3: 3}), 5)}, Peers: []wgcfg.Peer{ { - PublicKey: wgkey.Key{}, + PublicKey: key.NodePublic{}, }, }, }, diff --git a/wgengine/bench/wg.go b/wgengine/bench/wg.go index ad05ed848..2d8c83540 100644 --- a/wgengine/bench/wg.go +++ b/wgengine/bench/wg.go @@ -17,9 +17,9 @@ "tailscale.com/net/dns" "tailscale.com/tailcfg" + "tailscale.com/types/key" "tailscale.com/types/logger" "tailscale.com/types/netmap" - "tailscale.com/types/wgkey" "tailscale.com/wgengine" "tailscale.com/wgengine/filter" "tailscale.com/wgengine/router" @@ -28,10 +28,7 @@ func setupWGTest(b *testing.B, logf logger.Logf, traf *TrafficGen, a1, a2 netaddr.IPPrefix) { l1 := logger.WithPrefix(logf, "e1: ") - k1, err := wgkey.NewPrivate() - if err != nil { - log.Fatalf("e1 NewPrivateKey: %v", err) - } + k1 := key.NewNode() c1 := wgcfg.Config{ Name: "e1", @@ -56,10 +53,7 @@ func setupWGTest(b *testing.B, logf logger.Logf, traf *TrafficGen, a1, a2 netadd } l2 := logger.WithPrefix(logf, "e2: ") - k2, err := wgkey.NewPrivate() - if err != nil { - log.Fatalf("e2 NewPrivateKey: %v", err) - } + k2 := key.NewNode() c2 := wgcfg.Config{ Name: "e2", PrivateKey: k2, @@ -111,8 +105,8 @@ func setupWGTest(b *testing.B, logf logger.Logf, traf *TrafficGen, a1, a2 netadd Endpoints: eps, } e2.SetNetworkMap(&netmap.NetworkMap{ - NodeKey: tailcfg.NodeKey(k2), - PrivateKey: wgkey.Private(k2), + NodeKey: tailcfg.NodeKeyFromNodePublic(k2.Public()), + PrivateKey: k2.AsWGPrivate(), Peers: []*tailcfg.Node{&n}, }) @@ -148,8 +142,8 @@ func setupWGTest(b *testing.B, logf logger.Logf, traf *TrafficGen, a1, a2 netadd Endpoints: eps, } e1.SetNetworkMap(&netmap.NetworkMap{ - NodeKey: tailcfg.NodeKey(k1), - PrivateKey: wgkey.Private(k1), + NodeKey: tailcfg.NodeKeyFromNodePublic(k1.Public()), + PrivateKey: k1.AsWGPrivate(), Peers: []*tailcfg.Node{&n}, }) diff --git a/wgengine/magicsock/magicsock_test.go b/wgengine/magicsock/magicsock_test.go index 486af23be..7803d579c 100644 --- a/wgengine/magicsock/magicsock_test.go +++ b/wgengine/magicsock/magicsock_test.go @@ -24,6 +24,7 @@ "time" "unsafe" + "go4.org/mem" "golang.org/x/crypto/nacl/box" "golang.zx2c4.com/wireguard/device" "golang.zx2c4.com/wireguard/tun/tuntest" @@ -1017,11 +1018,11 @@ func testTwoDevicePing(t *testing.T, d *devices) { m1cfg := &wgcfg.Config{ Name: "peer1", - PrivateKey: m1.privateKey, + PrivateKey: key.NodePrivateFromRaw32(mem.B(m1.privateKey[:])), Addresses: []netaddr.IPPrefix{netaddr.MustParseIPPrefix("1.0.0.1/32")}, Peers: []wgcfg.Peer{ wgcfg.Peer{ - PublicKey: m2.privateKey.Public(), + PublicKey: key.NodePrivateFromRaw32(mem.B(m2.privateKey[:])).Public(), DiscoKey: m2.conn.DiscoPublicKey(), AllowedIPs: []netaddr.IPPrefix{netaddr.MustParseIPPrefix("1.0.0.2/32")}, }, @@ -1029,11 +1030,11 @@ func testTwoDevicePing(t *testing.T, d *devices) { } m2cfg := &wgcfg.Config{ Name: "peer2", - PrivateKey: m2.privateKey, + PrivateKey: key.NodePrivateFromRaw32(mem.B(m2.privateKey[:])), Addresses: []netaddr.IPPrefix{netaddr.MustParseIPPrefix("1.0.0.2/32")}, Peers: []wgcfg.Peer{ wgcfg.Peer{ - PublicKey: m1.privateKey.Public(), + PublicKey: key.NodePrivateFromRaw32(mem.B(m1.privateKey[:])).Public(), DiscoKey: m1.conn.DiscoPublicKey(), AllowedIPs: []netaddr.IPPrefix{netaddr.MustParseIPPrefix("1.0.0.1/32")}, }, diff --git a/wgengine/userspace.go b/wgengine/userspace.go index 79ca15d40..b54c9a1df 100644 --- a/wgengine/userspace.go +++ b/wgengine/userspace.go @@ -42,7 +42,6 @@ "tailscale.com/types/key" "tailscale.com/types/logger" "tailscale.com/types/netmap" - "tailscale.com/types/wgkey" "tailscale.com/util/deephash" "tailscale.com/version" "tailscale.com/wgengine/filter" @@ -128,7 +127,7 @@ type userspaceEngine struct { netMap *netmap.NetworkMap // or nil closing bool // Close was called (even if we're still closing) statusCallback StatusCallback - peerSequence []wgkey.Key + peerSequence []tailcfg.NodeKey endpoints []tailcfg.Endpoint pendOpen map[flowtrack.Tuple]*pendingOpenFlow // see pendopen.go networkMapCallbacks map[*someHandle]NetworkMapCallback @@ -648,27 +647,28 @@ func (e *userspaceEngine) maybeReconfigWireguardLocked(discoChanged map[tailcfg. needRemoveStep := false for i := range full.Peers { p := &full.Peers[i] - nk := tailcfg.NodeKey(p.PublicKey) + nk := p.PublicKey + tnk := tailcfg.NodeKeyFromNodePublic(nk) if !isTrimmablePeer(p, len(full.Peers)) { min.Peers = append(min.Peers, *p) - if discoChanged[nk] { + if discoChanged[tnk] { needRemoveStep = true } continue } - trackNodes = append(trackNodes, nk) + trackNodes = append(trackNodes, tnk) recentlyActive := false for _, cidr := range p.AllowedIPs { trackIPs = append(trackIPs, cidr.IP()) - recentlyActive = recentlyActive || e.isActiveSinceLocked(nk, cidr.IP(), activeCutoff) + recentlyActive = recentlyActive || e.isActiveSinceLocked(tnk, cidr.IP(), activeCutoff) } if recentlyActive { min.Peers = append(min.Peers, *p) - if discoChanged[tailcfg.NodeKey(p.PublicKey)] { + if discoChanged[tnk] { needRemoveStep = true } } else { - trimmedNodes[tailcfg.NodeKey(p.PublicKey)] = true + trimmedNodes[tnk] = true } } e.lastNMinPeers = len(min.Peers) @@ -687,7 +687,7 @@ func (e *userspaceEngine) maybeReconfigWireguardLocked(discoChanged map[tailcfg. minner.Peers = nil numRemove := 0 for _, p := range min.Peers { - if discoChanged[tailcfg.NodeKey(p.PublicKey)] { + if discoChanged[tailcfg.NodeKeyFromNodePublic(p.PublicKey)] { numRemove++ continue } @@ -807,8 +807,8 @@ func (e *userspaceEngine) Reconfig(cfg *wgcfg.Config, routerCfg *router.Config, e.mu.Lock() e.peerSequence = e.peerSequence[:0] for _, p := range cfg.Peers { - e.peerSequence = append(e.peerSequence, wgkey.Key(p.PublicKey)) - peerSet[key.Public(p.PublicKey)] = struct{}{} + e.peerSequence = append(e.peerSequence, tailcfg.NodeKeyFromNodePublic(p.PublicKey)) + peerSet[p.PublicKey.AsPublic()] = struct{}{} } e.mu.Unlock() @@ -845,7 +845,7 @@ func (e *userspaceEngine) Reconfig(cfg *wgcfg.Config, routerCfg *router.Config, prevEP := make(map[tailcfg.NodeKey]tailcfg.DiscoKey) for i := range e.lastCfgFull.Peers { if p := &e.lastCfgFull.Peers[i]; !p.DiscoKey.IsZero() { - prevEP[tailcfg.NodeKey(p.PublicKey)] = p.DiscoKey + prevEP[tailcfg.NodeKeyFromNodePublic(p.PublicKey)] = p.DiscoKey } } for i := range cfg.Peers { @@ -853,7 +853,7 @@ func (e *userspaceEngine) Reconfig(cfg *wgcfg.Config, routerCfg *router.Config, if p.DiscoKey.IsZero() { continue } - pub := tailcfg.NodeKey(p.PublicKey) + pub := tailcfg.NodeKeyFromNodePublic(p.PublicKey) if old, ok := prevEP[pub]; ok && old != p.DiscoKey { discoChanged[pub] = true e.logf("wgengine: Reconfig: %s changed from %q to %q", pub.ShortString(), old, p.DiscoKey) @@ -867,7 +867,7 @@ func (e *userspaceEngine) Reconfig(cfg *wgcfg.Config, routerCfg *router.Config, // (which is needed by DERP) before wgdev gets it, as wgdev // will start trying to handshake, which we want to be able to // go over DERP. - if err := e.magicConn.SetPrivateKey(wgkey.Private(cfg.PrivateKey)); err != nil { + if err := e.magicConn.SetPrivateKey(cfg.PrivateKey.AsWGPrivate()); err != nil { e.logf("wgengine: Reconfig: SetPrivateKey: %v", err) } e.magicConn.UpdatePeers(peerSet) @@ -978,7 +978,7 @@ func (e *userspaceEngine) getStatus() (*Status, error) { errc <- err }() - pp := make(map[wgkey.Key]ipnstate.PeerStatusLite) + pp := make(map[tailcfg.NodeKey]ipnstate.PeerStatusLite) var p ipnstate.PeerStatusLite var hst1, hst2, n int64 @@ -1012,7 +1012,7 @@ func (e *userspaceEngine) getStatus() (*Status, error) { return nil, fmt.Errorf("IpcGetOperation: invalid key in line %q", line) } if !p.NodeKey.IsZero() { - pp[wgkey.Key(p.NodeKey)] = p + pp[p.NodeKey] = p } p = ipnstate.PeerStatusLite{NodeKey: tailcfg.NodeKey(pk)} case "rx_bytes": @@ -1043,7 +1043,7 @@ func (e *userspaceEngine) getStatus() (*Status, error) { } } if !p.NodeKey.IsZero() { - pp[wgkey.Key(p.NodeKey)] = p + pp[p.NodeKey] = p } if err := <-errc; err != nil { return nil, fmt.Errorf("IpcGetOperation: %v", err) @@ -1464,7 +1464,7 @@ func (e *userspaceEngine) peerForIP(ip netaddr.IP) (n *tailcfg.Node, isSelf bool } if best.IsZero() || cidr.Bits() > best.Bits() { best = cidr - bestKey = tailcfg.NodeKey(p.PublicKey) + bestKey = tailcfg.NodeKeyFromNodePublic(p.PublicKey) } } } diff --git a/wgengine/userspace_test.go b/wgengine/userspace_test.go index bda88ee3c..c2777f53d 100644 --- a/wgengine/userspace_test.go +++ b/wgengine/userspace_test.go @@ -18,7 +18,6 @@ "tailscale.com/tstime/mono" "tailscale.com/types/key" "tailscale.com/types/netmap" - "tailscale.com/types/wgkey" "tailscale.com/wgengine/router" "tailscale.com/wgengine/wgcfg" ) @@ -105,10 +104,14 @@ func TestUserspaceEngineReconfig(t *testing.T) { }, }, } + nk, err := key.ParseNodePublicUntyped(mem.S(nodeHex)) + if err != nil { + t.Fatal(err) + } cfg := &wgcfg.Config{ Peers: []wgcfg.Peer{ { - PublicKey: wgkey.Key(nkFromHex(nodeHex)), + PublicKey: nk, AllowedIPs: []netaddr.IPPrefix{ netaddr.IPPrefixFrom(netaddr.IPv4(100, 100, 99, 1), 32), }, @@ -161,11 +164,14 @@ func TestUserspaceEnginePortReconfig(t *testing.T) { t.Cleanup(ue.Close) startingPort := ue.magicConn.LocalPort() - nodeKey := nkFromHex("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + nodeKey, err := key.ParseNodePublicUntyped(mem.S("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")) + if err != nil { + t.Fatal(err) + } cfg := &wgcfg.Config{ Peers: []wgcfg.Peer{ { - PublicKey: wgkey.Key(nodeKey), + PublicKey: nodeKey, AllowedIPs: []netaddr.IPPrefix{ netaddr.IPPrefixFrom(netaddr.IPv4(100, 100, 99, 1), 32), }, diff --git a/wgengine/wgcfg/clone.go b/wgengine/wgcfg/clone.go index 8cdab2b22..63e39ff5b 100644 --- a/wgengine/wgcfg/clone.go +++ b/wgengine/wgcfg/clone.go @@ -10,7 +10,7 @@ import ( "inet.af/netaddr" "tailscale.com/tailcfg" - "tailscale.com/types/wgkey" + "tailscale.com/types/key" ) // Clone makes a deep copy of Config. @@ -33,7 +33,7 @@ func (src *Config) Clone() *Config { // A compilation failure here means this code must be regenerated, with the command at the top of this file. var _ConfigCloneNeedsRegeneration = Config(struct { Name string - PrivateKey wgkey.Private + PrivateKey key.NodePrivate Addresses []netaddr.IPPrefix MTU uint16 DNS []netaddr.IP @@ -54,7 +54,7 @@ func (src *Peer) Clone() *Peer { // A compilation failure here means this code must be regenerated, with the command at the top of this file. var _PeerCloneNeedsRegeneration = Peer(struct { - PublicKey wgkey.Key + PublicKey key.NodePublic DiscoKey tailcfg.DiscoKey AllowedIPs []netaddr.IPPrefix PersistentKeepalive uint16 diff --git a/wgengine/wgcfg/config.go b/wgengine/wgcfg/config.go index 3e22aef22..653c28b11 100644 --- a/wgengine/wgcfg/config.go +++ b/wgengine/wgcfg/config.go @@ -8,7 +8,7 @@ import ( "inet.af/netaddr" "tailscale.com/tailcfg" - "tailscale.com/types/wgkey" + "tailscale.com/types/key" ) //go:generate go run tailscale.com/cmd/cloner -type=Config,Peer -output=clone.go @@ -17,7 +17,7 @@ // It only supports the set of things Tailscale uses. type Config struct { Name string - PrivateKey wgkey.Private + PrivateKey key.NodePrivate Addresses []netaddr.IPPrefix MTU uint16 DNS []netaddr.IP @@ -25,14 +25,14 @@ type Config struct { } type Peer struct { - PublicKey wgkey.Key + PublicKey key.NodePublic DiscoKey tailcfg.DiscoKey // present only so we can handle restarts within wgengine, not passed to WireGuard AllowedIPs []netaddr.IPPrefix PersistentKeepalive uint16 } // PeerWithKey returns the Peer with key k and reports whether it was found. -func (config Config) PeerWithKey(k wgkey.Key) (Peer, bool) { +func (config Config) PeerWithKey(k key.NodePublic) (Peer, bool) { for _, p := range config.Peers { if p.PublicKey == k { return p, true diff --git a/wgengine/wgcfg/device.go b/wgengine/wgcfg/device.go index b28416216..526f0e92c 100644 --- a/wgengine/wgcfg/device.go +++ b/wgengine/wgcfg/device.go @@ -29,7 +29,7 @@ func DeviceConfig(d *device.Device) (*Config, error) { return nil, err } sort.Slice(cfg.Peers, func(i, j int) bool { - return cfg.Peers[i].PublicKey.LessThan(&cfg.Peers[j].PublicKey) + return cfg.Peers[i].PublicKey.Less(cfg.Peers[j].PublicKey) }) return cfg, nil } diff --git a/wgengine/wgcfg/device_test.go b/wgengine/wgcfg/device_test.go index c636785ce..617235711 100644 --- a/wgengine/wgcfg/device_test.go +++ b/wgengine/wgcfg/device_test.go @@ -20,29 +20,26 @@ "golang.zx2c4.com/wireguard/tun" "inet.af/netaddr" "tailscale.com/tailcfg" - "tailscale.com/types/wgkey" + "tailscale.com/types/key" ) func TestDeviceConfig(t *testing.T) { - newPrivateKey := func() (wgkey.Key, wgkey.Private) { + newK := func() (key.NodePublic, key.NodePrivate) { t.Helper() - pk, err := wgkey.NewPrivate() - if err != nil { - t.Fatal(err) - } - return wgkey.Key(pk.Public()), wgkey.Private(pk) + k := key.NewNode() + return k.Public(), k } - k1, pk1 := newPrivateKey() + k1, pk1 := newK() ip1 := netaddr.MustParseIPPrefix("10.0.0.1/32") - k2, pk2 := newPrivateKey() + k2, pk2 := newK() ip2 := netaddr.MustParseIPPrefix("10.0.0.2/32") - k3, _ := newPrivateKey() + k3, _ := newK() ip3 := netaddr.MustParseIPPrefix("10.0.0.3/32") cfg1 := &Config{ - PrivateKey: wgkey.Private(pk1), + PrivateKey: pk1, Peers: []Peer{{ PublicKey: k2, AllowedIPs: []netaddr.IPPrefix{ip2}, @@ -50,7 +47,7 @@ func TestDeviceConfig(t *testing.T) { } cfg2 := &Config{ - PrivateKey: wgkey.Private(pk2), + PrivateKey: pk2, Peers: []Peer{{ PublicKey: k1, AllowedIPs: []netaddr.IPPrefix{ip1}, @@ -149,7 +146,7 @@ func TestDeviceConfig(t *testing.T) { AllowedIPs: []netaddr.IPPrefix{ip3}, }) sort.Slice(cfg1.Peers, func(i, j int) bool { - return cfg1.Peers[i].PublicKey.LessThan(&cfg1.Peers[j].PublicKey) + return cfg1.Peers[i].PublicKey.Less(cfg1.Peers[j].PublicKey) }) origCfg, err := DeviceConfig(device1) diff --git a/wgengine/wgcfg/nmcfg/nmcfg.go b/wgengine/wgcfg/nmcfg/nmcfg.go index 469fbd1c8..40a1e44e4 100644 --- a/wgengine/wgcfg/nmcfg/nmcfg.go +++ b/wgengine/wgcfg/nmcfg/nmcfg.go @@ -10,12 +10,13 @@ "fmt" "strings" + "go4.org/mem" "inet.af/netaddr" "tailscale.com/net/tsaddr" "tailscale.com/tailcfg" + "tailscale.com/types/key" "tailscale.com/types/logger" "tailscale.com/types/netmap" - "tailscale.com/types/wgkey" "tailscale.com/wgengine/wgcfg" ) @@ -54,7 +55,7 @@ func cidrIsSubnet(node *tailcfg.Node, cidr netaddr.IPPrefix) bool { func WGCfg(nm *netmap.NetworkMap, logf logger.Logf, flags netmap.WGConfigFlags, exitNode tailcfg.StableNodeID) (*wgcfg.Config, error) { cfg := &wgcfg.Config{ Name: "tailscale", - PrivateKey: wgkey.Private(nm.PrivateKey), + PrivateKey: key.NodePrivateFromRaw32(mem.B(nm.PrivateKey[:])), Addresses: nm.Addresses, Peers: make([]wgcfg.Peer, 0, len(nm.Peers)), } @@ -72,7 +73,7 @@ func WGCfg(nm *netmap.NetworkMap, logf logger.Logf, flags netmap.WGConfigFlags, continue } cfg.Peers = append(cfg.Peers, wgcfg.Peer{ - PublicKey: wgkey.Key(peer.Key), + PublicKey: key.NodePublicFromRaw32(mem.B(peer.Key[:])), DiscoKey: peer.DiscoKey, }) cpeer := &cfg.Peers[len(cfg.Peers)-1] diff --git a/wgengine/wgcfg/parser.go b/wgengine/wgcfg/parser.go index b17ae40f2..c781d57a8 100644 --- a/wgengine/wgcfg/parser.go +++ b/wgengine/wgcfg/parser.go @@ -15,6 +15,7 @@ "go4.org/mem" "inet.af/netaddr" + "tailscale.com/types/key" "tailscale.com/types/wgkey" ) @@ -128,71 +129,70 @@ func parseKeyHex(s []byte, dst []byte) error { return nil } -func (cfg *Config) handleDeviceLine(key, value mem.RO, valueBytes []byte) error { +func (cfg *Config) handleDeviceLine(k, value mem.RO, valueBytes []byte) error { switch { - case key.EqualString("private_key"): + case k.EqualString("private_key"): // wireguard-go guarantees not to send zero value; private keys are already clamped. - if err := parseKeyHex(valueBytes, cfg.PrivateKey[:]); err != nil { + var err error + cfg.PrivateKey, err = key.ParseNodePrivateUntyped(value) + if err != nil { return err } - case key.EqualString("listen_port") || key.EqualString("fwmark"): + case k.EqualString("listen_port") || k.EqualString("fwmark"): // ignore default: - return fmt.Errorf("unexpected IpcGetOperation key: %q", key.StringCopy()) + return fmt.Errorf("unexpected IpcGetOperation key: %q", k.StringCopy()) } return nil } func (cfg *Config) handlePublicKeyLine(valueBytes []byte) (*Peer, error) { p := Peer{} - if err := parseKeyHex(valueBytes, p.PublicKey[:]); err != nil { + var err error + p.PublicKey, err = key.ParseNodePublicUntyped(mem.B(valueBytes)) + if err != nil { return nil, err } cfg.Peers = append(cfg.Peers, p) return &cfg.Peers[len(cfg.Peers)-1], nil } -func (cfg *Config) handlePeerLine(peer *Peer, key, value mem.RO, valueBytes []byte) error { +func (cfg *Config) handlePeerLine(peer *Peer, k, value mem.RO, valueBytes []byte) error { switch { - case key.EqualString("endpoint"): - // TODO: our key types are all over the place, and this - // particular one can't parse a mem.RO or a []byte without - // allocating. We don't reconfigure wireguard often though, so - // this is okay. - s := value.StringCopy() - k, err := wgkey.ParseHex(s) + case k.EqualString("endpoint"): + nk, err := key.ParseNodePublicUntyped(value) if err != nil { - return fmt.Errorf("invalid endpoint %q for peer %q, expected a hex public key", s, peer.PublicKey.ShortString()) + return fmt.Errorf("invalid endpoint %q for peer %q, expected a hex public key", value.StringCopy(), peer.PublicKey.ShortString()) } - if k != peer.PublicKey { - return fmt.Errorf("unexpected endpoint %q for peer %q, expected the peer's public key", s, peer.PublicKey.ShortString()) + if nk != peer.PublicKey { + return fmt.Errorf("unexpected endpoint %q for peer %q, expected the peer's public key", value.StringCopy(), peer.PublicKey.ShortString()) } - case key.EqualString("persistent_keepalive_interval"): + case k.EqualString("persistent_keepalive_interval"): n, err := mem.ParseUint(value, 10, 16) if err != nil { return err } peer.PersistentKeepalive = uint16(n) - case key.EqualString("allowed_ip"): + case k.EqualString("allowed_ip"): ipp := netaddr.IPPrefix{} err := ipp.UnmarshalText(valueBytes) if err != nil { return err } peer.AllowedIPs = append(peer.AllowedIPs, ipp) - case key.EqualString("protocol_version"): + case k.EqualString("protocol_version"): if !value.EqualString("1") { return fmt.Errorf("invalid protocol version: %q", value.StringCopy()) } - case key.EqualString("replace_allowed_ips") || - key.EqualString("preshared_key") || - key.EqualString("last_handshake_time_sec") || - key.EqualString("last_handshake_time_nsec") || - key.EqualString("tx_bytes") || - key.EqualString("rx_bytes"): + case k.EqualString("replace_allowed_ips") || + k.EqualString("preshared_key") || + k.EqualString("last_handshake_time_sec") || + k.EqualString("last_handshake_time_nsec") || + k.EqualString("tx_bytes") || + k.EqualString("rx_bytes"): // ignore default: - return fmt.Errorf("unexpected IpcGetOperation key: %q", key.StringCopy()) + return fmt.Errorf("unexpected IpcGetOperation key: %q", k.StringCopy()) } return nil } diff --git a/wgengine/wgcfg/parser_test.go b/wgengine/wgcfg/parser_test.go index 59f448702..156dab14f 100644 --- a/wgengine/wgcfg/parser_test.go +++ b/wgengine/wgcfg/parser_test.go @@ -13,7 +13,7 @@ "testing" "inet.af/netaddr" - "tailscale.com/types/wgkey" + "tailscale.com/types/key" ) func noError(t *testing.T, err error) bool { @@ -61,15 +61,12 @@ func TestParseEndpoint(t *testing.T) { } func BenchmarkFromUAPI(b *testing.B) { - newPrivateKey := func() (wgkey.Key, wgkey.Private) { + newK := func() (key.NodePublic, key.NodePrivate) { b.Helper() - pk, err := wgkey.NewPrivate() - if err != nil { - b.Fatal(err) - } - return wgkey.Key(pk.Public()), wgkey.Private(pk) + k := key.NewNode() + return k.Public(), k } - k1, pk1 := newPrivateKey() + k1, pk1 := newK() ip1 := netaddr.MustParseIPPrefix("10.0.0.1/32") peer := Peer{ @@ -77,7 +74,7 @@ func BenchmarkFromUAPI(b *testing.B) { AllowedIPs: []netaddr.IPPrefix{ip1}, } cfg1 := &Config{ - PrivateKey: wgkey.Private(pk1), + PrivateKey: pk1, Peers: []Peer{peer, peer, peer, peer}, } diff --git a/wgengine/wgcfg/writer.go b/wgengine/wgcfg/writer.go index e83dfccd2..c7be7d024 100644 --- a/wgengine/wgcfg/writer.go +++ b/wgengine/wgcfg/writer.go @@ -10,7 +10,7 @@ "strconv" "inet.af/netaddr" - "tailscale.com/types/wgkey" + "tailscale.com/types/key" ) // ToUAPI writes cfg in UAPI format to w. @@ -32,15 +32,15 @@ func (cfg *Config) ToUAPI(w io.Writer, prev *Config) error { set(key, strconv.FormatUint(uint64(value), 10)) } setPeer := func(peer Peer) { - set("public_key", peer.PublicKey.HexString()) + set("public_key", peer.PublicKey.UntypedHexString()) } // Device config. - if prev.PrivateKey != cfg.PrivateKey { - set("private_key", cfg.PrivateKey.HexString()) + if !prev.PrivateKey.Equal(cfg.PrivateKey) { + set("private_key", cfg.PrivateKey.UntypedHexString()) } - old := make(map[wgkey.Key]Peer) + old := make(map[key.NodePublic]Peer) for _, p := range prev.Peers { old[p.PublicKey] = p } @@ -55,7 +55,7 @@ func (cfg *Config) ToUAPI(w io.Writer, prev *Config) error { // to WireGuard, because doing so generates a bit more work in // calling magicsock's ParseEndpoint for effectively a no-op. if !wasPresent { - set("endpoint", p.PublicKey.HexString()) + set("endpoint", p.PublicKey.UntypedHexString()) } // TODO: replace_allowed_ips is expensive. diff --git a/wgengine/wglog/wglog.go b/wgengine/wglog/wglog.go index 8b27e05d5..eef14e46e 100644 --- a/wgengine/wglog/wglog.go +++ b/wgengine/wglog/wglog.go @@ -12,8 +12,8 @@ "sync/atomic" "golang.zx2c4.com/wireguard/device" + "tailscale.com/types/key" "tailscale.com/types/logger" - "tailscale.com/types/wgkey" "tailscale.com/wgengine/wgcfg" ) @@ -21,9 +21,9 @@ // It can be modified at run time to adjust to new wireguard-go configurations. type Logger struct { DeviceLogger *device.Logger - replace atomic.Value // of map[string]string - mu sync.Mutex // protects strs - strs map[wgkey.Key]*strCache // cached strs used to populate replace + replace atomic.Value // of map[string]string + mu sync.Mutex // protects strs + strs map[key.NodePublic]*strCache // cached strs used to populate replace } // strCache holds a wireguard-go and a Tailscale style peer string. @@ -84,7 +84,7 @@ func NewLogger(logf logger.Logf) *Logger { Verbosef: logger.WithPrefix(wrapper, "[v2] "), Errorf: wrapper, } - ret.strs = make(map[wgkey.Key]*strCache) + ret.strs = make(map[key.NodePublic]*strCache) return ret } @@ -98,7 +98,7 @@ func (x *Logger) SetPeers(peers []wgcfg.Peer) { for _, peer := range peers { c, ok := x.strs[peer.PublicKey] // look up cached strs if !ok { - wg := wireguardGoString(peer.PublicKey) + wg := peer.PublicKey.WireGuardGoString() ts := peer.PublicKey.ShortString() c = &strCache{wg: wg, ts: ts} x.strs[peer.PublicKey] = c @@ -117,23 +117,3 @@ func (x *Logger) SetPeers(peers []wgcfg.Peer) { } x.replace.Store(replace) } - -// wireguardGoString prints k in the same format used by wireguard-go. -func wireguardGoString(k wgkey.Key) string { - src := k - b64 := func(input byte) byte { - return input + 'A' + byte(((25-int(input))>>8)&6) - byte(((51-int(input))>>8)&75) - byte(((61-int(input))>>8)&15) + byte(((62-int(input))>>8)&3) - } - b := []byte("peer(____…____)") - const first = len("peer(") - const second = len("peer(____…") - b[first+0] = b64((src[0] >> 2) & 63) - b[first+1] = b64(((src[0] << 4) | (src[1] >> 4)) & 63) - b[first+2] = b64(((src[1] << 2) | (src[2] >> 6)) & 63) - b[first+3] = b64(src[2] & 63) - b[second+0] = b64(src[29] & 63) - b[second+1] = b64((src[30] >> 2) & 63) - b[second+2] = b64(((src[30] << 4) | (src[31] >> 4)) & 63) - b[second+3] = b64((src[31] << 2) & 63) - return string(b) -} diff --git a/wgengine/wglog/wglog_test.go b/wgengine/wglog/wglog_test.go index f2aad667a..937d86e2a 100644 --- a/wgengine/wglog/wglog_test.go +++ b/wgengine/wglog/wglog_test.go @@ -8,8 +8,9 @@ "fmt" "testing" + "go4.org/mem" + "tailscale.com/types/key" "tailscale.com/types/logger" - "tailscale.com/types/wgkey" "tailscale.com/wgengine/wgcfg" "tailscale.com/wgengine/wglog" ) @@ -41,7 +42,7 @@ type log struct { } x := wglog.NewLogger(logf) - key, err := wgkey.ParseHex("20c4c1ae54e1fd37cab6e9a532ca20646aff496796cc41d4519560e5e82bee53") + key, err := key.ParseNodePublicUntyped(mem.S("20c4c1ae54e1fd37cab6e9a532ca20646aff496796cc41d4519560e5e82bee53")) if err != nil { t.Fatal(err) } @@ -92,9 +93,9 @@ func genPeers(n int) []wgcfg.Peer { } peers := make([]wgcfg.Peer, n) for i := range peers { - var k wgkey.Key + var k [32]byte k[n] = byte(n) - peers[i].PublicKey = k + peers[i].PublicKey = key.NodePublicFromRaw32(mem.B(k[:])) } return peers }