wgengine/magicsock: cache precomputed nacl/box shared keys

Updates #483
This commit is contained in:
Brad Fitzpatrick 2020-06-29 14:26:25 -07:00
parent a975e86bb8
commit a83ca9e734
2 changed files with 24 additions and 7 deletions

View File

@ -95,6 +95,7 @@ type Conn struct {
discoOfNode map[tailcfg.NodeKey]tailcfg.DiscoKey
endpointOfDisco map[tailcfg.DiscoKey]*discoEndpoint
sharedDiscoKey map[tailcfg.DiscoKey]*[32]byte // nacl/box precomputed key
// addrsByUDP is a map of every remote ip:port to a priority
// list of endpoint addresses for a peer.
@ -250,6 +251,7 @@ func newConn() *Conn {
derpStarted: make(chan struct{}),
peerLastDerp: make(map[key.Public]int),
endpointOfDisco: make(map[tailcfg.DiscoKey]*discoEndpoint),
sharedDiscoKey: make(map[tailcfg.DiscoKey]*[32]byte),
}
c.endpointsUpdateWaiter = sync.NewCond(&c.mu)
return c
@ -499,6 +501,11 @@ func (c *Conn) SetNetInfoCallback(fn func(*tailcfg.NetInfo)) {
func (c *Conn) SetDiscoPrivateKey(k key.Private) {
c.mu.Lock()
defer c.mu.Unlock()
if !c.discoPrivate.IsZero() && c.discoPrivate != k {
// TODO: support changing a key at runtime; need to
// clear a bunch of maps at least
panic("unsupported")
}
c.discoPrivate = k
c.logf("magicsock: disco key set; public: %x", k.Public())
}
@ -1348,7 +1355,7 @@ func (c *Conn) handleDiscoMessage(msg []byte, addr *net.UDPAddr) bool {
var nonce [nonceLen]byte
copy(nonce[:], msg[len(magic)+len(key.Public{}):])
sealedBox := msg[headerLen:]
payload, ok := box.Open(nil, sealedBox, &nonce, key.Public(sender).B32(), c.discoPrivate.B32())
payload, ok := box.OpenAfterPrecomputation(nil, sealedBox, &nonce, c.sharedDiscoKeyLocked(sender))
if !ok {
c.logf("magicsock: failed to open disco message box purportedly from %s (disco key %x)", senderNode.Key.ShortString(), sender[:])
return false
@ -1358,6 +1365,16 @@ func (c *Conn) handleDiscoMessage(msg []byte, addr *net.UDPAddr) bool {
return true
}
func (c *Conn) sharedDiscoKeyLocked(k tailcfg.DiscoKey) *[32]byte {
if v, ok := c.sharedDiscoKey[k]; ok {
return v
}
shared := new([32]byte)
box.Precompute(shared, key.Public(k).B32(), c.discoPrivate.B32())
c.sharedDiscoKey[k] = shared
return shared
}
// SetPrivateKey sets the connection's private key.
//
// This is only used to be able prove our identity when connecting to
@ -1491,6 +1508,7 @@ func (c *Conn) SetNetworkMap(nm *controlclient.NetworkMap) {
if _, ok := c.nodeOfDisco[dk]; !ok {
de.cleanup()
delete(c.endpointOfDisco, dk)
delete(c.sharedDiscoKey, dk)
}
}

View File

@ -841,12 +841,11 @@ func TestDiscoMessage(t *testing.T) {
peer1Priv := key.NewPrivate()
peer1Pub := peer1Priv.Public()
c := &Conn{
logf: t.Logf,
discoPrivate: key.NewPrivate(),
nodeOfDisco: map[tailcfg.DiscoKey]*tailcfg.Node{
tailcfg.DiscoKey(peer1Pub): &tailcfg.Node{Key: tailcfg.NodeKey{1: 1}},
},
c := newConn()
c.logf = t.Logf
c.SetDiscoPrivateKey(key.NewPrivate())
c.nodeOfDisco = map[tailcfg.DiscoKey]*tailcfg.Node{
tailcfg.DiscoKey(peer1Pub): &tailcfg.Node{Key: tailcfg.NodeKey{1: 1}},
}
const payload = "why hello"