Просмотр исходного кода

wgengine/wgcfg: convert to use new node key type.

Updates #3206

Signed-off-by: David Anderson <[email protected]>
David Anderson 4 лет назад
Родитель
Сommit
a9c78910bd

+ 13 - 0
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 {

+ 2 - 2
util/deephash/deephash_test.go

@@ -19,7 +19,7 @@ import (
 	"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{},
 				},
 			},
 		},

+ 7 - 13
wgengine/bench/wg.go

@@ -17,9 +17,9 @@ import (
 
 	"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 @@ import (
 
 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},
 		})
 

+ 5 - 4
wgengine/magicsock/magicsock_test.go

@@ -24,6 +24,7 @@ import (
 	"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")},
 			},

+ 18 - 18
wgengine/userspace.go

@@ -42,7 +42,6 @@ import (
 	"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)
 			}
 		}
 	}

+ 10 - 4
wgengine/userspace_test.go

@@ -18,7 +18,6 @@ import (
 	"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),
 				},

+ 3 - 3
wgengine/wgcfg/clone.go

@@ -10,7 +10,7 @@ package wgcfg
 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

+ 4 - 4
wgengine/wgcfg/config.go

@@ -8,7 +8,7 @@ package wgcfg
 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 @@ import (
 // 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

+ 1 - 1
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
 }

+ 10 - 13
wgengine/wgcfg/device_test.go

@@ -20,29 +20,26 @@ import (
 	"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)

+ 4 - 3
wgengine/wgcfg/nmcfg/nmcfg.go

@@ -10,12 +10,13 @@ import (
 	"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]

+ 27 - 27
wgengine/wgcfg/parser.go

@@ -15,6 +15,7 @@ import (
 
 	"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
 }

+ 6 - 9
wgengine/wgcfg/parser_test.go

@@ -13,7 +13,7 @@ import (
 	"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},
 	}
 

+ 6 - 6
wgengine/wgcfg/writer.go

@@ -10,7 +10,7 @@ import (
 	"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.

+ 6 - 26
wgengine/wglog/wglog.go

@@ -12,8 +12,8 @@ import (
 	"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 @@ import (
 // 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)
-}

+ 5 - 4
wgengine/wglog/wglog_test.go

@@ -8,8 +8,9 @@ import (
 	"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 @@ func TestLogger(t *testing.T) {
 	}
 
 	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
 }