From c107f891d29246b219f0eb77a846b6a57ac4c381 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 16 Feb 2020 23:09:24 +0000 Subject: [PATCH 1/7] Implement pubkeys in API functions --- src/crypto/crypto.go | 10 ++++++++++ src/yggdrasil/conn.go | 8 +++++--- src/yggdrasil/dialer.go | 38 ++++++++++++++++++++++++++++++++------ src/yggdrasil/listener.go | 4 ++-- 4 files changed, 49 insertions(+), 11 deletions(-) diff --git a/src/crypto/crypto.go b/src/crypto/crypto.go index 3a6e02d1..92adf890 100644 --- a/src/crypto/crypto.go +++ b/src/crypto/crypto.go @@ -194,6 +194,16 @@ type BoxSharedKey [BoxSharedKeyLen]byte // BoxNonce is the nonce used in NaCl-like crypto "box" operations (curve25519+xsalsa20+poly1305), and must not be reused for different messages encrypted using the same BoxSharedKey. type BoxNonce [BoxNonceLen]byte +// String returns a string representation of the "box" key. +func (k BoxPubKey) String() string { + return hex.EncodeToString(k[:]) +} + +// Network returns "pubkey" for "box" keys. +func (n BoxPubKey) Network() string { + return "pubkey" +} + // NewBoxKeys generates a new pair of public/private crypto box keys. func NewBoxKeys() (*BoxPubKey, *BoxPrivKey) { pubBytes, privBytes, err := box.GenerateKey(rand.Reader) diff --git a/src/yggdrasil/conn.go b/src/yggdrasil/conn.go index f6229036..078dca35 100644 --- a/src/yggdrasil/conn.go +++ b/src/yggdrasil/conn.go @@ -353,13 +353,15 @@ func (c *Conn) Close() (err error) { // LocalAddr returns the complete node ID of the local side of the connection. // This is always going to return your own node's node ID. func (c *Conn) LocalAddr() net.Addr { - return crypto.GetNodeID(&c.core.boxPub) + return c.core.boxPub } // RemoteAddr returns the complete node ID of the remote side of the connection. func (c *Conn) RemoteAddr() net.Addr { - // RemoteAddr is set during the dial or accept, and isn't changed, so it's safe to access directly - return c.nodeID + if c.session != nil { + return c.session.theirPermPub + } + return nil } // SetDeadline is equivalent to calling both SetReadDeadline and diff --git a/src/yggdrasil/dialer.go b/src/yggdrasil/dialer.go index 490502b4..293f6d0b 100644 --- a/src/yggdrasil/dialer.go +++ b/src/yggdrasil/dialer.go @@ -17,19 +17,32 @@ type Dialer struct { core *Core } -// Dial opens a session to the given node. The first parameter should be "nodeid" -// and the second parameter should contain a hexadecimal representation of the -// target node ID. It uses DialContext internally. +// Dial opens a session to the given node. The first parameter should be +// "pubkey" or "nodeid" and the second parameter should contain a hexadecimal +// representation of the target. It uses DialContext internally. func (d *Dialer) Dial(network, address string) (net.Conn, error) { return d.DialContext(nil, network, address) } -// DialContext is used internally by Dial, and should only be used with a context that includes a timeout. It uses DialByNodeIDandMask internally. +// DialContext is used internally by Dial, and should only be used with a +// context that includes a timeout. It uses DialByNodeIDandMask internally when +// the network is "nodeid", or DialByPublicKey when the network is "pubkey". func (d *Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) { var nodeID crypto.NodeID var nodeMask crypto.NodeID // Process switch network { + case "pubkey": + dest, err := hex.DecodeString(address) + if err != nil { + return nil, err + } + if len(dest) != crypto.BoxPubKeyLen { + return nil, errors.New("invalid key length supplied") + } + var pubKey crypto.BoxPubKey + copy(pubKey[:], dest) + return d.DialByPublicKey(ctx, &pubKey) case "nodeid": // A node ID was provided - we don't need to do anything special with it if tokens := strings.Split(address, "/"); len(tokens) == 2 { @@ -62,8 +75,9 @@ func (d *Dialer) DialContext(ctx context.Context, network, address string) (net. } } -// DialByNodeIDandMask opens a session to the given node based on raw -// NodeID parameters. If ctx is nil or has no timeout, then a default timeout of 6 seconds will apply, beginning *after* the search finishes. +// DialByNodeIDandMask opens a session to the given node based on raw NodeID +// parameters. If ctx is nil or has no timeout, then a default timeout of 6 +// seconds will apply, beginning *after* the search finishes. func (d *Dialer) DialByNodeIDandMask(ctx context.Context, nodeID, nodeMask *crypto.NodeID) (net.Conn, error) { startDial := time.Now() conn := newConn(d.core, nodeID, nodeMask, nil) @@ -92,3 +106,15 @@ func (d *Dialer) DialByNodeIDandMask(ctx context.Context, nodeID, nodeMask *cryp return nil, errors.New("session handshake timeout") } } + +// DialByPublicKey opens a session to the given node based on the public key. If +// ctx is nil or has no timeout, then a default timeout of 6 seconds will apply, +// beginning *after* the search finishes. +func (d *Dialer) DialByPublicKey(ctx context.Context, pubKey *crypto.BoxPubKey) (net.Conn, error) { + nodeID := crypto.GetNodeID(pubKey) + var nodeMask crypto.NodeID + for i := range nodeMask { + nodeMask[i] = 0xFF + } + return d.DialByNodeIDandMask(ctx, nodeID, &nodeMask) +} diff --git a/src/yggdrasil/listener.go b/src/yggdrasil/listener.go index 63830970..1b908b42 100644 --- a/src/yggdrasil/listener.go +++ b/src/yggdrasil/listener.go @@ -39,7 +39,7 @@ func (l *Listener) Close() (err error) { return nil } -// Addr is not implemented for this type yet +// Addr returns the address of the listener func (l *Listener) Addr() net.Addr { - return nil + return l.core.boxPub } From 63936c11b500bb06285980591aeba590f86661d6 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 16 Feb 2020 23:21:58 +0000 Subject: [PATCH 2/7] Update tuntap module, return pointers --- src/tuntap/tun.go | 3 ++- src/yggdrasil/conn.go | 4 ++-- src/yggdrasil/listener.go | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/tuntap/tun.go b/src/tuntap/tun.go index 53a17466..656ecca7 100644 --- a/src/tuntap/tun.go +++ b/src/tuntap/tun.go @@ -251,7 +251,8 @@ func (tun *TunAdapter) _wrap(conn *yggdrasil.Conn) (c *tunConn, err error) { } c = &s // Get the remote address and subnet of the other side - remoteNodeID := conn.RemoteAddr().(*crypto.NodeID) + remotePubKey := conn.RemoteAddr().(*crypto.BoxPubKey) + remoteNodeID := crypto.GetNodeID(remotePubKey) s.addr = *address.AddrForNodeID(remoteNodeID) s.snet = *address.SubnetForNodeID(remoteNodeID) // Work out if this is already a destination we already know about diff --git a/src/yggdrasil/conn.go b/src/yggdrasil/conn.go index 078dca35..d18f196d 100644 --- a/src/yggdrasil/conn.go +++ b/src/yggdrasil/conn.go @@ -353,13 +353,13 @@ func (c *Conn) Close() (err error) { // LocalAddr returns the complete node ID of the local side of the connection. // This is always going to return your own node's node ID. func (c *Conn) LocalAddr() net.Addr { - return c.core.boxPub + return &c.core.boxPub } // RemoteAddr returns the complete node ID of the remote side of the connection. func (c *Conn) RemoteAddr() net.Addr { if c.session != nil { - return c.session.theirPermPub + return &c.session.theirPermPub } return nil } diff --git a/src/yggdrasil/listener.go b/src/yggdrasil/listener.go index 1b908b42..74ef3e88 100644 --- a/src/yggdrasil/listener.go +++ b/src/yggdrasil/listener.go @@ -41,5 +41,5 @@ func (l *Listener) Close() (err error) { // Addr returns the address of the listener func (l *Listener) Addr() net.Addr { - return l.core.boxPub + return &l.core.boxPub } From d16505e4177926b6dd85bd36b9c3525efeda70a0 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 16 Feb 2020 23:26:18 +0000 Subject: [PATCH 3/7] Update CKR --- src/tuntap/conn.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/tuntap/conn.go b/src/tuntap/conn.go index aa149063..24ea5ef3 100644 --- a/src/tuntap/conn.go +++ b/src/tuntap/conn.go @@ -92,8 +92,7 @@ func (s *tunConn) _read(bs []byte) (err error) { // The destination address isn't in our CKR allowed range skip = true } else if key, err := s.tun.ckr.getPublicKeyForAddress(srcAddr, addrlen); err == nil { - srcNodeID := crypto.GetNodeID(&key) - if *s.conn.RemoteAddr().(*crypto.NodeID) == *srcNodeID { + if *s.conn.RemoteAddr().(*crypto.BoxPubKey) == key { // This is the one allowed CKR case, where source and destination addresses are both good } else { // The CKR key associated with this address doesn't match the sender's NodeID @@ -169,8 +168,7 @@ func (s *tunConn) _write(bs []byte) (err error) { // The source address isn't in our CKR allowed range skip = true } else if key, err := s.tun.ckr.getPublicKeyForAddress(dstAddr, addrlen); err == nil { - dstNodeID := crypto.GetNodeID(&key) - if *s.conn.RemoteAddr().(*crypto.NodeID) == *dstNodeID { + if *s.conn.RemoteAddr().(*crypto.BoxPubKey) == key { // This is the one allowed CKR case, where source and destination addresses are both good } else { // The CKR key associated with this address doesn't match the sender's NodeID From 6b0b7046453ddecdd22da86bc1f98e8435f0d1ec Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 16 Feb 2020 23:30:47 +0000 Subject: [PATCH 4/7] Update comments --- src/yggdrasil/conn.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/yggdrasil/conn.go b/src/yggdrasil/conn.go index d18f196d..37049c73 100644 --- a/src/yggdrasil/conn.go +++ b/src/yggdrasil/conn.go @@ -350,13 +350,14 @@ func (c *Conn) Close() (err error) { return } -// LocalAddr returns the complete node ID of the local side of the connection. -// This is always going to return your own node's node ID. +// LocalAddr returns the complete public key of the local side of the +// connection. This is always going to return your own node's node ID. func (c *Conn) LocalAddr() net.Addr { return &c.core.boxPub } -// RemoteAddr returns the complete node ID of the remote side of the connection. +// RemoteAddr returns the complete public key of the remote side of the +// connection. func (c *Conn) RemoteAddr() net.Addr { if c.session != nil { return &c.session.theirPermPub From 429189d11dff8f4709ea1910f81f1e3420474037 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 16 Feb 2020 23:44:20 +0000 Subject: [PATCH 5/7] Use 'curve25519' instead of 'pubkey' --- src/crypto/crypto.go | 4 ++-- src/yggdrasil/dialer.go | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/crypto/crypto.go b/src/crypto/crypto.go index 92adf890..211a0e54 100644 --- a/src/crypto/crypto.go +++ b/src/crypto/crypto.go @@ -199,9 +199,9 @@ func (k BoxPubKey) String() string { return hex.EncodeToString(k[:]) } -// Network returns "pubkey" for "box" keys. +// Network returns "curve25519" for "box" keys. func (n BoxPubKey) Network() string { - return "pubkey" + return "curve25519" } // NewBoxKeys generates a new pair of public/private crypto box keys. diff --git a/src/yggdrasil/dialer.go b/src/yggdrasil/dialer.go index 293f6d0b..9f58d305 100644 --- a/src/yggdrasil/dialer.go +++ b/src/yggdrasil/dialer.go @@ -18,21 +18,21 @@ type Dialer struct { } // Dial opens a session to the given node. The first parameter should be -// "pubkey" or "nodeid" and the second parameter should contain a hexadecimal -// representation of the target. It uses DialContext internally. +// "curve25519" or "nodeid" and the second parameter should contain a +// hexadecimal representation of the target. It uses DialContext internally. func (d *Dialer) Dial(network, address string) (net.Conn, error) { return d.DialContext(nil, network, address) } // DialContext is used internally by Dial, and should only be used with a // context that includes a timeout. It uses DialByNodeIDandMask internally when -// the network is "nodeid", or DialByPublicKey when the network is "pubkey". +// the network is "nodeid", or DialByPublicKey when the network is "curve25519". func (d *Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) { var nodeID crypto.NodeID var nodeMask crypto.NodeID // Process switch network { - case "pubkey": + case "curve25519": dest, err := hex.DecodeString(address) if err != nil { return nil, err From 6c731c4efc9827115f6404c7ebcdf0c1a5d496c1 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 16 Feb 2020 23:45:11 +0000 Subject: [PATCH 6/7] Fix comment on LocalAddr --- src/yggdrasil/conn.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yggdrasil/conn.go b/src/yggdrasil/conn.go index 37049c73..eef57683 100644 --- a/src/yggdrasil/conn.go +++ b/src/yggdrasil/conn.go @@ -351,7 +351,7 @@ func (c *Conn) Close() (err error) { } // LocalAddr returns the complete public key of the local side of the -// connection. This is always going to return your own node's node ID. +// connection. This is always going to return your own node's public key. func (c *Conn) LocalAddr() net.Addr { return &c.core.boxPub } From 471fcd7fdfbb316922c0bdbb7b020d54218abcd2 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 16 Feb 2020 23:57:05 +0000 Subject: [PATCH 7/7] Update doc.go dial example --- src/yggdrasil/doc.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/yggdrasil/doc.go b/src/yggdrasil/doc.go index 44d39b66..9b9dd738 100644 --- a/src/yggdrasil/doc.go +++ b/src/yggdrasil/doc.go @@ -113,9 +113,9 @@ a Dialer: // ... } -You can then dial using the 16-byte node ID in hexadecimal format, for example: +You can then dial using the node's public key in hexadecimal format, for example: - conn, err := dialer.Dial("nodeid", "24a58cfce691ec016b0f698f7be1bee983cea263781017e99ad3ef62b4ef710a45d6c1a072c5ce46131bd574b78818c9957042cafeeed13966f349e94eb771bf") + conn, err := dialer.Dial("curve25519", "55071be281f50d0abbda63aadc59755624280c44b2f1f47684317aa4e0325604") if err != nil { // ... }