diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go index 4d585fcba..879e7b582 100644 --- a/tailcfg/tailcfg.go +++ b/tailcfg/tailcfg.go @@ -94,6 +94,15 @@ func NodeKeyFromNodePublic(k key.NodePublic) NodeKey { // It's never written to disk or reused between network start-ups. type DiscoKey [32]byte +// NodeKeyFromNodePublic 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 +// aren't related to #3206. +func DiscoKeyFromDiscoPublic(k key.DiscoPublic) DiscoKey { + return k.Raw32() +} + // User is an IPN user. // // A user can have multiple logins associated with it (e.g. gmail and github oauth). diff --git a/types/key/disco.go b/types/key/disco.go index 04f3e2e82..cf3fb1fca 100644 --- a/types/key/disco.go +++ b/types/key/disco.go @@ -92,6 +92,14 @@ func (k DiscoPublic) IsZero() bool { return k == DiscoPublic{} } +// Raw32 returns k encoded as 32 raw bytes. +// +// Deprecated: only needed for a temporary compat shim in tailcfg, do +// not add more uses. +func (k DiscoPublic) Raw32() [32]byte { + return k.k +} + // ShortString returns the Tailscale conventional debug representation // of a disco key. func (k DiscoPublic) ShortString() string { diff --git a/wgengine/magicsock/magicsock.go b/wgengine/magicsock/magicsock.go index 060886db4..192c340e9 100644 --- a/wgengine/magicsock/magicsock.go +++ b/wgengine/magicsock/magicsock.go @@ -28,7 +28,6 @@ import ( "time" "go4.org/mem" - "golang.org/x/crypto/nacl/box" "golang.zx2c4.com/wireguard/conn" "inet.af/netaddr" "tailscale.com/control/controlclient" @@ -98,14 +97,14 @@ type peerMap struct { // nodesOfDisco are contains the set of nodes that are using a // DiscoKey. Usually those sets will be just one node. - nodesOfDisco map[tailcfg.DiscoKey]map[tailcfg.NodeKey]bool + nodesOfDisco map[key.DiscoPublic]map[tailcfg.NodeKey]bool } func newPeerMap() peerMap { return peerMap{ byNodeKey: map[tailcfg.NodeKey]*peerInfo{}, byIPPort: map[netaddr.IPPort]*peerInfo{}, - nodesOfDisco: map[tailcfg.DiscoKey]map[tailcfg.NodeKey]bool{}, + nodesOfDisco: map[key.DiscoPublic]map[tailcfg.NodeKey]bool{}, } } @@ -116,7 +115,7 @@ func (m *peerMap) nodeCount() int { // anyEndpointForDiscoKey reports whether there exists any // peers in the netmap with dk as their DiscoKey. -func (m *peerMap) anyEndpointForDiscoKey(dk tailcfg.DiscoKey) bool { +func (m *peerMap) anyEndpointForDiscoKey(dk key.DiscoPublic) bool { return len(m.nodesOfDisco[dk]) > 0 } @@ -150,7 +149,7 @@ func (m *peerMap) forEachEndpoint(f func(ep *endpoint)) { // forEachEndpointWithDiscoKey invokes f on every endpoint in m // that has the provided DiscoKey. -func (m *peerMap) forEachEndpointWithDiscoKey(dk tailcfg.DiscoKey, f func(ep *endpoint)) { +func (m *peerMap) forEachEndpointWithDiscoKey(dk key.DiscoPublic, f func(ep *endpoint)) { for nk := range m.nodesOfDisco[dk] { pi, ok := m.byNodeKey[nk] if !ok { @@ -356,15 +355,15 @@ type Conn struct { // discoPrivate is the private naclbox key used for active // discovery traffic. It's created once near (but not during) // construction. - discoPrivate key.Private - discoPublic tailcfg.DiscoKey // public of discoPrivate - discoShort string // ShortString of discoPublic (to save logging work later) + discoPrivate key.DiscoPrivate + discoPublic key.DiscoPublic // public of discoPrivate + discoShort string // ShortString of discoPublic (to save logging work later) // nodeOfDisco tracks the networkmap Node entity for each peer // discovery key. peerMap peerMap // discoInfo is the state for an active DiscoKey. - discoInfo map[tailcfg.DiscoKey]*discoInfo + discoInfo map[key.DiscoPublic]*discoInfo // netInfoFunc is a callback that provides a tailcfg.NetInfo when // discovered network conditions change. @@ -529,7 +528,7 @@ func newConn() *Conn { derpStarted: make(chan struct{}), peerLastDerp: make(map[key.NodePublic]int), peerMap: newPeerMap(), - discoInfo: make(map[tailcfg.DiscoKey]*discoInfo), + discoInfo: make(map[key.DiscoPublic]*discoInfo), } c.bind = &connBind{Conn: c, closed: true} c.muCond = sync.NewCond(&c.mu) @@ -931,13 +930,13 @@ func (c *Conn) derpRegionCodeLocked(regionID int) string { } // DiscoPublicKey returns the discovery public key. -func (c *Conn) DiscoPublicKey() tailcfg.DiscoKey { +func (c *Conn) DiscoPublicKey() key.DiscoPublic { c.mu.Lock() defer c.mu.Unlock() if c.discoPrivate.IsZero() { - priv := key.NewPrivate() + priv := key.NewDisco() c.discoPrivate = priv - c.discoPublic = tailcfg.DiscoKey(priv.Public()) + c.discoPublic = priv.Public() c.discoShort = c.discoPublic.ShortString() c.logf("magicsock: disco key = %v", c.discoShort) } @@ -1747,7 +1746,7 @@ const ( // // The dstKey should only be non-zero if the dstDisco key // unambiguously maps to exactly one peer. -func (c *Conn) sendDiscoMessage(dst netaddr.IPPort, dstKey tailcfg.NodeKey, dstDisco tailcfg.DiscoKey, m disco.Message, logLevel discoLogLevel) (sent bool, err error) { +func (c *Conn) sendDiscoMessage(dst netaddr.IPPort, dstKey tailcfg.NodeKey, dstDisco key.DiscoPublic, m disco.Message, logLevel discoLogLevel) (sent bool, err error) { c.mu.Lock() if c.closed { c.mu.Unlock() @@ -1759,12 +1758,12 @@ func (c *Conn) sendDiscoMessage(dst netaddr.IPPort, dstKey tailcfg.NodeKey, dstD } pkt := make([]byte, 0, 512) // TODO: size it correctly? pool? if it matters. pkt = append(pkt, disco.Magic...) - pkt = append(pkt, c.discoPublic[:]...) - pkt = append(pkt, nonce[:]...) + pkt = c.discoPublic.AppendTo(pkt) di := c.discoInfoLocked(dstDisco) c.mu.Unlock() - pkt = box.SealAfterPrecomputation(pkt, m.AppendMarshal(nil), &nonce, di.sharedKey) + box := di.sharedKey.Seal(m.AppendMarshal(nil)) + pkt = append(pkt, box...) sent, err = c.sendAddr(dst, key.NodePublicFromRaw32(mem.B(dstKey[:])), pkt) if sent { if logLevel == discoLog || (logLevel == discoVerboseLog && debugDisco) { @@ -1799,7 +1798,7 @@ func (c *Conn) sendDiscoMessage(dst netaddr.IPPort, dstKey tailcfg.NodeKey, dstD // it was received from at the DERP layer. derpNodeSrc is zero when received // over UDP. func (c *Conn) handleDiscoMessage(msg []byte, src netaddr.IPPort, derpNodeSrc tailcfg.NodeKey) (isDiscoMsg bool) { - const headerLen = len(disco.Magic) + len(tailcfg.DiscoKey{}) + disco.NonceLen + headerLen := len(disco.Magic) + key.DiscoPublic{}.RawLen() if len(msg) < headerLen || string(msg[:len(disco.Magic)]) != disco.Magic { return false } @@ -1810,8 +1809,7 @@ func (c *Conn) handleDiscoMessage(msg []byte, src netaddr.IPPort, derpNodeSrc ta // Use naked returns for all following paths. isDiscoMsg = true - var sender tailcfg.DiscoKey - copy(sender[:], msg[len(disco.Magic):]) + sender := key.DiscoPublicFromRaw32(mem.B(msg[len(disco.Magic) : len(disco.Magic)+key.DiscoPublic{}.RawLen()])) c.mu.Lock() defer c.mu.Unlock() @@ -1848,10 +1846,8 @@ func (c *Conn) handleDiscoMessage(msg []byte, src netaddr.IPPort, derpNodeSrc ta di := c.discoInfoLocked(sender) - var nonce [disco.NonceLen]byte - copy(nonce[:], msg[len(disco.Magic)+key.NodePublic{}.RawLen():]) sealedBox := msg[headerLen:] - payload, ok := box.OpenAfterPrecomputation(nil, sealedBox, &nonce, di.sharedKey) + payload, ok := di.sharedKey.Open(sealedBox) if !ok { // This might be have been intended for a previous // disco key. When we restart we get a new disco key @@ -1931,7 +1927,7 @@ func (c *Conn) handleDiscoMessage(msg []byte, src netaddr.IPPort, derpNodeSrc ta // derpNodeSrc is non-zero if the disco ping arrived via DERP. // // c.mu must be held. -func (c *Conn) unambiguousNodeKeyOfPingLocked(dm *disco.Ping, dk tailcfg.DiscoKey, derpNodeSrc tailcfg.NodeKey) (nk tailcfg.NodeKey, ok bool) { +func (c *Conn) unambiguousNodeKeyOfPingLocked(dm *disco.Ping, dk key.DiscoPublic, derpNodeSrc tailcfg.NodeKey) (nk tailcfg.NodeKey, ok bool) { if !derpNodeSrc.IsZero() { if ep, ok := c.peerMap.endpointForNodeKey(derpNodeSrc); ok && ep.discoKey == dk { return derpNodeSrc, true @@ -2070,15 +2066,14 @@ func (c *Conn) enqueueCallMeMaybe(derpAddr netaddr.IPPort, de *endpoint) { // discoInfoLocked returns the previous or new discoInfo for k. // // c.mu must be held. -func (c *Conn) discoInfoLocked(k tailcfg.DiscoKey) *discoInfo { +func (c *Conn) discoInfoLocked(k key.DiscoPublic) *discoInfo { di, ok := c.discoInfo[k] if !ok { di = &discoInfo{ discoKey: k, discoShort: k.ShortString(), - sharedKey: new([32]byte), + sharedKey: c.discoPrivate.Shared(k), } - box.Precompute(di.sharedKey, key.Public(k).B32(), c.discoPrivate.B32()) c.discoInfo[k] = di } return di @@ -2274,7 +2269,7 @@ func (c *Conn) SetNetworkMap(nm *netmap.NetworkMap) { endpointState: map[netaddr.IPPort]*endpointState{}, } if !n.DiscoKey.IsZero() { - ep.discoKey = n.DiscoKey + ep.discoKey = key.DiscoPublicFromRaw32(mem.B(n.DiscoKey[:])) ep.discoShort = n.DiscoKey.ShortString() } ep.wgEndpoint = key.NodePublicFromRaw32(mem.B(n.Key[:])).UntypedHexString() @@ -3146,8 +3141,8 @@ type endpoint struct { // mu protects all following fields. mu sync.Mutex // Lock ordering: Conn.mu, then endpoint.mu - discoKey tailcfg.DiscoKey // for discovery messages. IsZero() if peer can't disco. - discoShort string // ShortString of discoKey. Empty if peer can't disco. + discoKey key.DiscoPublic // for discovery messages. IsZero() if peer can't disco. + discoShort string // ShortString of discoKey. Empty if peer can't disco. heartBeatTimer *time.Timer // nil when idle lastSend mono.Time // last time there was outgoing packets sent to this peer (from wireguard-go) @@ -3615,9 +3610,10 @@ func (de *endpoint) updateFromNode(n *tailcfg.Node) { de.mu.Lock() defer de.mu.Unlock() - if de.discoKey != n.DiscoKey { + tnk := key.DiscoPublicFromRaw32(mem.B(n.DiscoKey[:])) + if de.discoKey != tnk { de.c.logf("[v1] magicsock: disco: node %s changed from discokey %s to %s", de.publicKey.ShortString(), de.discoKey, n.DiscoKey) - de.discoKey = n.DiscoKey + de.discoKey = tnk de.discoShort = de.discoKey.ShortString() de.resetLocked() } @@ -3915,7 +3911,7 @@ func (de *endpoint) stopAndReset() { de.mu.Lock() defer de.mu.Unlock() - de.c.logf("[v1] magicsock: doing cleanup for discovery key %x", de.discoKey[:]) + de.c.logf("[v1] magicsock: doing cleanup for discovery key %s", de.discoKey.ShortString()) de.resetLocked() if de.heartBeatTimer != nil { @@ -3968,17 +3964,17 @@ type discoInfo struct { // discoKey is the same as the Conn.discoInfo map key, // just so you can pass around a *discoInfo alone. // Not modifed once initiazed. - discoKey tailcfg.DiscoKey + discoKey key.DiscoPublic // discoShort is discoKey.ShortString(). // Not modifed once initiazed; discoShort string - // sharedKey is the precomputed nacl/box key for - // communication with the peer that has the DiscoKey - // used to look up this *discoInfo in Conn.discoInfo. + // sharedKey is the precomputed key for communication with the + // peer that has the DiscoKey used to look up this *discoInfo in + // Conn.discoInfo. // Not modifed once initialized. - sharedKey *[32]byte + sharedKey key.DiscoShared // Mutable fields follow, owned by Conn.mu: diff --git a/wgengine/magicsock/magicsock_test.go b/wgengine/magicsock/magicsock_test.go index 9944888e6..2f99ef9aa 100644 --- a/wgengine/magicsock/magicsock_test.go +++ b/wgengine/magicsock/magicsock_test.go @@ -25,7 +25,6 @@ import ( "unsafe" "go4.org/mem" - "golang.org/x/crypto/nacl/box" "golang.zx2c4.com/wireguard/device" "golang.zx2c4.com/wireguard/tun/tuntest" "inet.af/netaddr" @@ -260,7 +259,7 @@ func meshStacks(logf logger.Logf, mutateNetmap func(idx int, nm *netmap.NetworkM ID: tailcfg.NodeID(i + 1), Name: fmt.Sprintf("node%d", i+1), Key: tailcfg.NodeKeyFromNodePublic(peer.privateKey.Public()), - DiscoKey: peer.conn.DiscoPublicKey(), + DiscoKey: tailcfg.DiscoKeyFromDiscoPublic(peer.conn.DiscoPublicKey()), Addresses: addrs, AllowedIPs: addrs, Endpoints: epStrings(eps[i]), @@ -668,7 +667,7 @@ func TestDiscokeyChange(t *testing.T) { // Start with some random discoKey that isn't actually m1's key, // to simulate m2 coming up with knowledge of an old, expired // discokey. We'll switch to the correct one later in the test. - m1DiscoKey = tailcfg.DiscoKey(key.NewPrivate().Public()) + m1DiscoKey = key.NewDisco().Public() ) setm1Key := func(idx int, nm *netmap.NetworkMap) { if idx != 1 { @@ -681,7 +680,7 @@ func TestDiscokeyChange(t *testing.T) { } mu.Lock() defer mu.Unlock() - nm.Peers[0].DiscoKey = m1DiscoKey + nm.Peers[0].DiscoKey = tailcfg.DiscoKeyFromDiscoPublic(m1DiscoKey) } cleanupMesh := meshStacks(t.Logf, setm1Key, m1, m2) @@ -1138,11 +1137,11 @@ func TestDiscoMessage(t *testing.T) { peer1Priv := c.discoPrivate n := &tailcfg.Node{ Key: tailcfg.NodeKey(key.NewPrivate().Public()), - DiscoKey: peer1Pub, + DiscoKey: tailcfg.DiscoKeyFromDiscoPublic(peer1Pub), } c.peerMap.upsertEndpoint(&endpoint{ publicKey: n.Key, - discoKey: n.DiscoKey, + discoKey: key.DiscoPublicFromRaw32(mem.B(n.DiscoKey[:])), }) const payload = "why hello" @@ -1150,10 +1149,10 @@ func TestDiscoMessage(t *testing.T) { var nonce [24]byte crand.Read(nonce[:]) - pkt := append([]byte("TS💬"), peer1Pub[:]...) - pkt = append(pkt, nonce[:]...) + pkt := peer1Pub.AppendTo([]byte("TS💬")) - pkt = box.Seal(pkt, []byte(payload), &nonce, c.discoPrivate.Public().B32(), peer1Priv.B32()) + box := peer1Priv.Shared(c.discoPrivate.Public()).Seal([]byte(payload)) + pkt = append(pkt, box...) got := c.handleDiscoMessage(pkt, netaddr.IPPort{}, tailcfg.NodeKey{}) if !got { t.Error("failed to open it") @@ -1224,18 +1223,18 @@ func newTestConn(t testing.TB) *Conn { // addTestEndpoint sets conn's network map to a single peer expected // to receive packets from sendConn (or DERP), and returns that peer's // nodekey and discokey. -func addTestEndpoint(tb testing.TB, conn *Conn, sendConn net.PacketConn) (tailcfg.NodeKey, tailcfg.DiscoKey) { +func addTestEndpoint(tb testing.TB, conn *Conn, sendConn net.PacketConn) (tailcfg.NodeKey, key.DiscoPublic) { // Give conn just enough state that it'll recognize sendConn as a // valid peer and not fall through to the legacy magicsock // codepath. - discoKey := tailcfg.DiscoKey{31: 1} + discoKey := key.DiscoPublicFromRaw32(mem.B([]byte{31: 1})) nodeKey := key.NodePublicFromRaw32(mem.B([]byte{0: 'N', 1: 'K', 31: 0})) tnk := tailcfg.NodeKeyFromNodePublic(nodeKey) conn.SetNetworkMap(&netmap.NetworkMap{ Peers: []*tailcfg.Node{ { Key: tnk, - DiscoKey: discoKey, + DiscoKey: tailcfg.DiscoKeyFromDiscoPublic(discoKey), Endpoints: []string{sendConn.LocalAddr().String()}, }, }, @@ -1405,7 +1404,7 @@ func TestSetNetworkMapChangingNodeKey(t *testing.T) { conn.SetPrivateKey(key.NodePrivateFromRaw32(mem.B([]byte{0: 1, 31: 0}))) - discoKey := tailcfg.DiscoKey{31: 1} + discoKey := key.DiscoPublicFromRaw32(mem.B([]byte{31: 1})) nodeKey1 := tailcfg.NodeKey{0: 'N', 1: 'K', 2: '1'} nodeKey2 := tailcfg.NodeKey{0: 'N', 1: 'K', 2: '2'} @@ -1413,7 +1412,7 @@ func TestSetNetworkMapChangingNodeKey(t *testing.T) { Peers: []*tailcfg.Node{ { Key: nodeKey1, - DiscoKey: discoKey, + DiscoKey: tailcfg.DiscoKeyFromDiscoPublic(discoKey), Endpoints: []string{"192.168.1.2:345"}, }, }, @@ -1428,7 +1427,7 @@ func TestSetNetworkMapChangingNodeKey(t *testing.T) { Peers: []*tailcfg.Node{ { Key: nodeKey2, - DiscoKey: discoKey, + DiscoKey: tailcfg.DiscoKeyFromDiscoPublic(discoKey), Endpoints: []string{"192.168.1.2:345"}, }, }, diff --git a/wgengine/userspace.go b/wgengine/userspace.go index 77e8bee7f..53d1e3eba 100644 --- a/wgengine/userspace.go +++ b/wgengine/userspace.go @@ -331,7 +331,7 @@ func NewUserspaceEngine(logf logger.Logf, conf Config) (_ Engine, reterr error) closePool.add(e.magicConn) e.magicConn.SetNetworkUp(e.linkMon.InterfaceState().AnyInterfaceUp()) - tsTUNDev.SetDiscoKey(e.magicConn.DiscoPublicKey()) + tsTUNDev.SetDiscoKey(tailcfg.DiscoKeyFromDiscoPublic(e.magicConn.DiscoPublicKey())) if conf.RespondToPing { e.tundev.PostFilterIn = echoRespondToAll @@ -842,7 +842,7 @@ func (e *userspaceEngine) Reconfig(cfg *wgcfg.Config, routerCfg *router.Config, // and a second time with it. discoChanged := make(map[tailcfg.NodeKey]bool) { - prevEP := make(map[tailcfg.NodeKey]tailcfg.DiscoKey) + prevEP := make(map[tailcfg.NodeKey]key.DiscoPublic) for i := range e.lastCfgFull.Peers { if p := &e.lastCfgFull.Peers[i]; !p.DiscoKey.IsZero() { prevEP[tailcfg.NodeKeyFromNodePublic(p.PublicKey)] = p.DiscoKey @@ -1232,7 +1232,7 @@ func (e *userspaceEngine) SetNetworkMap(nm *netmap.NetworkMap) { } func (e *userspaceEngine) DiscoPublicKey() tailcfg.DiscoKey { - return e.magicConn.DiscoPublicKey() + return tailcfg.DiscoKeyFromDiscoPublic(e.magicConn.DiscoPublicKey()) } func (e *userspaceEngine) UpdateStatus(sb *ipnstate.StatusBuilder) { diff --git a/wgengine/wgcfg/clone.go b/wgengine/wgcfg/clone.go index 63e39ff5b..efd280a00 100644 --- a/wgengine/wgcfg/clone.go +++ b/wgengine/wgcfg/clone.go @@ -9,7 +9,6 @@ package wgcfg import ( "inet.af/netaddr" - "tailscale.com/tailcfg" "tailscale.com/types/key" ) @@ -55,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 key.NodePublic - DiscoKey tailcfg.DiscoKey + DiscoKey key.DiscoPublic AllowedIPs []netaddr.IPPrefix PersistentKeepalive uint16 }{}) diff --git a/wgengine/wgcfg/config.go b/wgengine/wgcfg/config.go index 653c28b11..6d64502ed 100644 --- a/wgengine/wgcfg/config.go +++ b/wgengine/wgcfg/config.go @@ -7,7 +7,6 @@ package wgcfg import ( "inet.af/netaddr" - "tailscale.com/tailcfg" "tailscale.com/types/key" ) @@ -26,7 +25,7 @@ type Config struct { type Peer struct { PublicKey key.NodePublic - DiscoKey tailcfg.DiscoKey // present only so we can handle restarts within wgengine, not passed to WireGuard + DiscoKey key.DiscoPublic // present only so we can handle restarts within wgengine, not passed to WireGuard AllowedIPs []netaddr.IPPrefix PersistentKeepalive uint16 } diff --git a/wgengine/wgcfg/device_test.go b/wgengine/wgcfg/device_test.go index 617235711..beb9a2fcd 100644 --- a/wgengine/wgcfg/device_test.go +++ b/wgengine/wgcfg/device_test.go @@ -15,11 +15,11 @@ import ( "sync" "testing" + "go4.org/mem" "golang.zx2c4.com/wireguard/conn" "golang.zx2c4.com/wireguard/device" "golang.zx2c4.com/wireguard/tun" "inet.af/netaddr" - "tailscale.com/tailcfg" "tailscale.com/types/key" ) @@ -125,7 +125,7 @@ func TestDeviceConfig(t *testing.T) { }) t.Run("device1 modify peer", func(t *testing.T) { - cfg1.Peers[0].DiscoKey = tailcfg.DiscoKey{1} + cfg1.Peers[0].DiscoKey = key.DiscoPublicFromRaw32(mem.B([]byte{0: 1, 31: 0})) if err := ReconfigDevice(device1, cfg1, t.Logf); err != nil { t.Fatal(err) } @@ -133,7 +133,7 @@ func TestDeviceConfig(t *testing.T) { }) t.Run("device1 replace endpoint", func(t *testing.T) { - cfg1.Peers[0].DiscoKey = tailcfg.DiscoKey{2} + cfg1.Peers[0].DiscoKey = key.DiscoPublicFromRaw32(mem.B([]byte{0: 2, 31: 0})) if err := ReconfigDevice(device1, cfg1, t.Logf); err != nil { t.Fatal(err) } diff --git a/wgengine/wgcfg/nmcfg/nmcfg.go b/wgengine/wgcfg/nmcfg/nmcfg.go index e7fea2fa7..58e66e34d 100644 --- a/wgengine/wgcfg/nmcfg/nmcfg.go +++ b/wgengine/wgcfg/nmcfg/nmcfg.go @@ -74,7 +74,7 @@ func WGCfg(nm *netmap.NetworkMap, logf logger.Logf, flags netmap.WGConfigFlags, } cfg.Peers = append(cfg.Peers, wgcfg.Peer{ PublicKey: key.NodePublicFromRaw32(mem.B(peer.Key[:])), - DiscoKey: peer.DiscoKey, + DiscoKey: key.DiscoPublicFromRaw32(mem.B(peer.DiscoKey[:])), }) cpeer := &cfg.Peers[len(cfg.Peers)-1] if peer.KeepAlive {