diff --git a/src/crypto/crypto.go b/src/crypto/crypto.go index 3a6e02d1..211a0e54 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 "curve25519" for "box" keys. +func (n BoxPubKey) Network() string { + return "curve25519" +} + // 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/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 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 f6229036..eef57683 100644 --- a/src/yggdrasil/conn.go +++ b/src/yggdrasil/conn.go @@ -350,16 +350,19 @@ 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 public key. 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. +// RemoteAddr returns the complete public key 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..9f58d305 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 +// "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. +// 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 "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 "curve25519": + 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/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 { // ... } diff --git a/src/yggdrasil/listener.go b/src/yggdrasil/listener.go index 63830970..74ef3e88 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 }