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 discoOfNode map[tailcfg.NodeKey]tailcfg.DiscoKey
endpointOfDisco map[tailcfg.DiscoKey]*discoEndpoint 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 // addrsByUDP is a map of every remote ip:port to a priority
// list of endpoint addresses for a peer. // list of endpoint addresses for a peer.
@ -250,6 +251,7 @@ func newConn() *Conn {
derpStarted: make(chan struct{}), derpStarted: make(chan struct{}),
peerLastDerp: make(map[key.Public]int), peerLastDerp: make(map[key.Public]int),
endpointOfDisco: make(map[tailcfg.DiscoKey]*discoEndpoint), endpointOfDisco: make(map[tailcfg.DiscoKey]*discoEndpoint),
sharedDiscoKey: make(map[tailcfg.DiscoKey]*[32]byte),
} }
c.endpointsUpdateWaiter = sync.NewCond(&c.mu) c.endpointsUpdateWaiter = sync.NewCond(&c.mu)
return c return c
@ -499,6 +501,11 @@ func (c *Conn) SetNetInfoCallback(fn func(*tailcfg.NetInfo)) {
func (c *Conn) SetDiscoPrivateKey(k key.Private) { func (c *Conn) SetDiscoPrivateKey(k key.Private) {
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() 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.discoPrivate = k
c.logf("magicsock: disco key set; public: %x", k.Public()) 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 var nonce [nonceLen]byte
copy(nonce[:], msg[len(magic)+len(key.Public{}):]) copy(nonce[:], msg[len(magic)+len(key.Public{}):])
sealedBox := msg[headerLen:] 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 { if !ok {
c.logf("magicsock: failed to open disco message box purportedly from %s (disco key %x)", senderNode.Key.ShortString(), sender[:]) c.logf("magicsock: failed to open disco message box purportedly from %s (disco key %x)", senderNode.Key.ShortString(), sender[:])
return false return false
@ -1358,6 +1365,16 @@ func (c *Conn) handleDiscoMessage(msg []byte, addr *net.UDPAddr) bool {
return true 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. // SetPrivateKey sets the connection's private key.
// //
// This is only used to be able prove our identity when connecting to // 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 { if _, ok := c.nodeOfDisco[dk]; !ok {
de.cleanup() de.cleanup()
delete(c.endpointOfDisco, dk) delete(c.endpointOfDisco, dk)
delete(c.sharedDiscoKey, dk)
} }
} }

View File

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