diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 11310f7..0ffad89 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -25,7 +25,7 @@ import ( "github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/admin" "github.com/yggdrasil-network/yggdrasil-go/src/config" - "github.com/yggdrasil-network/yggdrasil-go/src/crypto" + "github.com/yggdrasil-network/yggdrasil-go/src/module" "github.com/yggdrasil-network/yggdrasil-go/src/multicast" "github.com/yggdrasil-network/yggdrasil-go/src/tuntap" @@ -220,23 +220,23 @@ func main() { return } // Have we been asked for the node address yet? If so, print it and then stop. - getNodeID := func() *crypto.NodeID { - // TODO: curve + getNodeKey := func() ed25519.PublicKey { + if pubkey, err := hex.DecodeString(cfg.PublicKey); err == nil { + return ed25519.PublicKey(pubkey) + } return nil } switch { case *getaddr: - if nodeid := getNodeID(); nodeid != nil { - panic("TODO") - addr := new(address.Address) //*address.AddrForNodeID(nodeid) + if key := getNodeKey(); key != nil { + addr := address.AddrForKey(key) ip := net.IP(addr[:]) fmt.Println(ip.String()) } return case *getsnet: - if nodeid := getNodeID(); nodeid != nil { - panic("TODO") - snet := new(address.Address) //*address.SubnetForNodeID(nodeid) + if key := getNodeKey(); key != nil { + snet := address.SubnetForKey(key) ipnet := net.IPNet{ IP: append(snet[:], 0, 0, 0, 0, 0, 0, 0, 0), Mask: net.CIDRMask(len(snet)*8, 128), diff --git a/src/config/config.go b/src/config/config.go index 49cc8a2..073e159 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -17,10 +17,10 @@ configuration option that is not provided. package config import ( + "crypto/ed25519" "encoding/hex" "sync" - "github.com/yggdrasil-network/yggdrasil-go/src/crypto" "github.com/yggdrasil-network/yggdrasil-go/src/defaults" ) @@ -90,7 +90,10 @@ type SessionFirewall struct { // using -autoconf. func GenerateConfig() *NodeConfig { // Generate encryption keys. - spub, spriv := crypto.NewSigKeys() + spub, spriv, err := ed25519.GenerateKey(nil) + if err != nil { + panic(err) + } // Create a node configuration and populate it. cfg := NodeConfig{} cfg.Listen = []string{} @@ -116,7 +119,10 @@ func GenerateConfig() *NodeConfig { // signing keypair. The signing keys are used by the switch to derive the // structure of the spanning tree. func (cfg *NodeConfig) NewKeys() { - spub, spriv := crypto.NewSigKeys() + spub, spriv, err := ed25519.GenerateKey(nil) + if err != nil { + panic(err) + } cfg.PublicKey = hex.EncodeToString(spub[:]) cfg.PrivateKey = hex.EncodeToString(spriv[:]) } diff --git a/src/crypto/crypto.go b/src/crypto/crypto.go deleted file mode 100644 index dcedb2c..0000000 --- a/src/crypto/crypto.go +++ /dev/null @@ -1,311 +0,0 @@ -// Package crypto is a wrapper around packages under golang.org/x/crypto/, particulaly curve25519, ed25519, and nacl/box. -// This is used to avoid explicitly importing and using these packages throughout yggdrasil. -// It also includes the all-important NodeID and TreeID types, which are used to identify nodes in the DHT and in the spanning tree's root selection algorithm, respectively. -package crypto - -/* - -This part of the package wraps crypto operations needed elsewhere - -In particular, it exposes key generation for ed25519 and nacl box - -It also defines NodeID and TreeID as hashes of keys, and wraps hash functions - -*/ - -import ( - "crypto/rand" - "crypto/sha512" - "encoding/hex" - "sync" - - "golang.org/x/crypto/curve25519" - "golang.org/x/crypto/ed25519" - "golang.org/x/crypto/nacl/box" -) - -//////////////////////////////////////////////////////////////////////////////// - -// NodeID and TreeID - -// NodeIDLen is the length (in bytes) of a NodeID. -const NodeIDLen = sha512.Size - -// TreeIDLen is the length (in bytes) of a TreeID. -const TreeIDLen = sha512.Size - -// handleLen is the length (in bytes) of a Handle. -const handleLen = 8 - -// NodeID is how a yggdrasil node is identified in the DHT, and is used to derive IPv6 addresses and subnets in the main executable. It is a sha512sum hash of the node's BoxPubKey -type NodeID [NodeIDLen]byte - -// TreeID is how a yggdrasil node is identified in the root selection algorithm used to construct the spanning tree. -type TreeID [TreeIDLen]byte - -type Handle [handleLen]byte - -func (n *NodeID) String() string { - return hex.EncodeToString(n[:]) -} - -// Network returns "nodeid" nearly always right now. -func (n *NodeID) Network() string { - return "nodeid" -} - -// PrefixLength returns the number of bits set in a masked NodeID. -func (n *NodeID) PrefixLength() int { - var len int - for i, v := range *n { - _, _ = i, v - if v == 0xff { - len += 8 - continue - } - for v&0x80 != 0 { - len++ - v <<= 1 - } - if v != 0 { - return -1 - } - for i++; i < NodeIDLen; i++ { - if n[i] != 0 { - return -1 - } - } - break - } - return len -} - -// GetNodeID returns the NodeID associated with a BoxPubKey. -func GetNodeID(pub *BoxPubKey) *NodeID { - h := sha512.Sum512(pub[:]) - return (*NodeID)(&h) -} - -// GetTreeID returns the TreeID associated with a BoxPubKey -func GetTreeID(pub *SigPubKey) *TreeID { - h := sha512.Sum512(pub[:]) - return (*TreeID)(&h) -} - -// NewHandle returns a new (cryptographically random) Handle, used by the session code to identify which session an incoming packet is associated with. -func NewHandle() *Handle { - var h Handle - _, err := rand.Read(h[:]) - if err != nil { - panic(err) - } - return &h -} - -//////////////////////////////////////////////////////////////////////////////// - -// Signatures - -// SigPubKeyLen is the length of a SigPubKey in bytes. -const SigPubKeyLen = ed25519.PublicKeySize - -// SigPrivKeyLen is the length of a SigPrivKey in bytes. -const SigPrivKeyLen = ed25519.PrivateKeySize - -// SigLen is the length of SigBytes. -const SigLen = ed25519.SignatureSize - -// SigPubKey is a public ed25519 signing key. -type SigPubKey [SigPubKeyLen]byte - -// SigPrivKey is a private ed25519 signing key. -type SigPrivKey [SigPrivKeyLen]byte - -// SigBytes is an ed25519 signature. -type SigBytes [SigLen]byte - -// NewSigKeys generates a public/private ed25519 key pair. -func NewSigKeys() (*SigPubKey, *SigPrivKey) { - var pub SigPubKey - var priv SigPrivKey - pubSlice, privSlice, err := ed25519.GenerateKey(rand.Reader) - if err != nil { - panic(err) - } - copy(pub[:], pubSlice) - copy(priv[:], privSlice) - return &pub, &priv -} - -// Sign returns the SigBytes signing a message. -func Sign(priv *SigPrivKey, msg []byte) *SigBytes { - var sig SigBytes - sigSlice := ed25519.Sign(priv[:], msg) - copy(sig[:], sigSlice) - return &sig -} - -// Verify returns true if the provided signature matches the key and message. -func Verify(pub *SigPubKey, msg []byte, sig *SigBytes) bool { - // Should sig be an array instead of a slice?... - // It's fixed size, but - return ed25519.Verify(pub[:], msg, sig[:]) -} - -// Public returns the SigPubKey associated with this SigPrivKey. -func (p SigPrivKey) Public() SigPubKey { - priv := make(ed25519.PrivateKey, ed25519.PrivateKeySize) - copy(priv[:], p[:]) - pub := priv.Public().(ed25519.PublicKey) - var sigPub SigPubKey - copy(sigPub[:], pub[:]) - return sigPub -} - -//////////////////////////////////////////////////////////////////////////////// - -// NaCl-like crypto "box" (curve25519+xsalsa20+poly1305) - -// BoxPubKeyLen is the length of a BoxPubKey in bytes. -const BoxPubKeyLen = 32 - -// BoxPrivKeyLen is the length of a BoxPrivKey in bytes. -const BoxPrivKeyLen = 32 - -// BoxSharedKeyLen is the length of a BoxSharedKey in bytes. -const BoxSharedKeyLen = 32 - -// BoxNonceLen is the length of a BoxNonce in bytes. -const BoxNonceLen = 24 - -// BoxOverhead is the length of the overhead from boxing something. -const BoxOverhead = box.Overhead - -// BoxPubKey is a NaCl-like "box" public key (curve25519+xsalsa20+poly1305). -type BoxPubKey [BoxPubKeyLen]byte - -// BoxPrivKey is a NaCl-like "box" private key (curve25519+xsalsa20+poly1305). -type BoxPrivKey [BoxPrivKeyLen]byte - -// BoxSharedKey is a NaCl-like "box" shared key (curve25519+xsalsa20+poly1305). -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) - if err != nil { - panic(err) - } - pub := (*BoxPubKey)(pubBytes) - priv := (*BoxPrivKey)(privBytes) - return pub, priv -} - -// GetSharedKey returns the shared key derived from your private key and the destination's public key. -func GetSharedKey(myPrivKey *BoxPrivKey, - othersPubKey *BoxPubKey) *BoxSharedKey { - var shared [BoxSharedKeyLen]byte - priv := (*[BoxPrivKeyLen]byte)(myPrivKey) - pub := (*[BoxPubKeyLen]byte)(othersPubKey) - box.Precompute(&shared, pub, priv) - return (*BoxSharedKey)(&shared) -} - -// pool is used internally by BoxOpen and BoxSeal to avoid allocating temporary space -var pool = sync.Pool{New: func() interface{} { return []byte(nil) }} - -// BoxOpen returns a message and true if it successfully opens a crypto box using the provided shared key and nonce. -// The boxed input slice's backing array is reused for the unboxed output when possible. -func BoxOpen(shared *BoxSharedKey, - boxed []byte, - nonce *BoxNonce) ([]byte, bool) { - s := (*[BoxSharedKeyLen]byte)(shared) - n := (*[BoxNonceLen]byte)(nonce) - temp := append(pool.Get().([]byte), boxed...) - unboxed, success := box.OpenAfterPrecomputation(boxed[:0], temp, n, s) - pool.Put(temp[:0]) - return unboxed, success -} - -// BoxSeal seals a crypto box using the provided shared key, returning the box and the nonce needed to decrypt it. -// If nonce is nil, a random BoxNonce will be used and returned. -// If nonce is non-nil, then nonce.Increment() will be called before using it, and the incremented BoxNonce is what is returned. -// The unboxed input slice's backing array is reused for the boxed output when possible. -func BoxSeal(shared *BoxSharedKey, unboxed []byte, nonce *BoxNonce) ([]byte, *BoxNonce) { - if nonce == nil { - nonce = NewBoxNonce() - } - nonce.Increment() - s := (*[BoxSharedKeyLen]byte)(shared) - n := (*[BoxNonceLen]byte)(nonce) - temp := append(pool.Get().([]byte), unboxed...) - boxed := box.SealAfterPrecomputation(unboxed[:0], temp, n, s) - pool.Put(temp[:0]) - return boxed, nonce -} - -// NewBoxNonce generates a (cryptographically) random BoxNonce. -func NewBoxNonce() *BoxNonce { - var nonce BoxNonce - _, err := rand.Read(nonce[:]) - for ; err == nil && nonce[0] == 0xff; _, err = rand.Read(nonce[:]) { - // Make sure nonce isn't too high - // This is just to make rollover unlikely to happen - // Rollover is fine, but it may kill the session and force it to reopen - } - if err != nil { - panic(err) - } - return &nonce -} - -// Increment adds 2 to a BoxNonce, which is useful if one node intends to send only with odd BoxNonce values, and the other only with even BoxNonce values. -func (n *BoxNonce) Increment() { - oldNonce := *n - n[len(n)-1] += 2 - for i := len(n) - 2; i >= 0; i-- { - if n[i+1] < oldNonce[i+1] { - n[i]++ - } - } -} - -// Public returns the BoxPubKey associated with this BoxPrivKey. -func (p BoxPrivKey) Public() BoxPubKey { - var boxPub [BoxPubKeyLen]byte - var boxPriv [BoxPrivKeyLen]byte - copy(boxPriv[:BoxPrivKeyLen], p[:BoxPrivKeyLen]) - curve25519.ScalarBaseMult(&boxPub, &boxPriv) - return boxPub -} - -// Minus is the result of subtracting the provided BoNonce from this BoxNonce, bounded at +- 64. -// It's primarily used to determine if a new BoxNonce is higher than the last known BoxNonce from a crypto session, and by how much. -// This is used in the machinery that makes sure replayed packets can't keep a session open indefinitely or stuck using old/bad information about a node. -func (n *BoxNonce) Minus(m *BoxNonce) int64 { - diff := int64(0) - for idx := range n { - diff *= 256 - diff += int64(n[idx]) - int64(m[idx]) - if diff > 64 { - diff = 64 - } - if diff < -64 { - diff = -64 - } - } - return diff -} diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index 29b7340..b22a59c 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -15,12 +15,13 @@ import ( "time" "github.com/yggdrasil-network/yggdrasil-go/src/address" - "github.com/yggdrasil-network/yggdrasil-go/src/crypto" "github.com/yggdrasil-network/yggdrasil-go/src/util" "golang.org/x/net/proxy" //"github.com/Arceliar/phony" // TODO? use instead of mutexes ) +type keyArray [ed25519.PublicKeySize]byte + type links struct { core *Core mutex sync.RWMutex // protects links below @@ -32,7 +33,7 @@ type links struct { // linkInfo is used as a map key type linkInfo struct { - key crypto.SigPubKey + key keyArray linkType string // Type of link, e.g. TCP, AWDL local string // Local name or address remote string // Remote name or address @@ -50,7 +51,7 @@ type link struct { } type linkOptions struct { - pinnedEd25519Keys map[crypto.SigPubKey]struct{} + pinnedEd25519Keys map[keyArray]struct{} } func (l *links) init(c *Core) error { @@ -80,10 +81,10 @@ func (l *links) call(uri string, sintf string) error { pathtokens := strings.Split(strings.Trim(u.Path, "/"), "/") tcpOpts := tcpOptions{} if pubkeys, ok := u.Query()["ed25519"]; ok && len(pubkeys) > 0 { - tcpOpts.pinnedEd25519Keys = make(map[crypto.SigPubKey]struct{}) + tcpOpts.pinnedEd25519Keys = make(map[keyArray]struct{}) for _, pubkey := range pubkeys { if sigPub, err := hex.DecodeString(pubkey); err == nil { - var sigPubKey crypto.SigPubKey + var sigPubKey keyArray copy(sigPubKey[:], sigPub) tcpOpts.pinnedEd25519Keys[sigPubKey] = struct{}{} } @@ -200,7 +201,7 @@ func (intf *link) handler() (chan struct{}, error) { // Check if the remote side matches the keys we expected. This is a bit of a weak // check - in future versions we really should check a signature or something like that. if pinned := intf.options.pinnedEd25519Keys; pinned != nil { - var key crypto.SigPubKey + var key keyArray copy(key[:], meta.key) if _, allowed := pinned[key]; !allowed { intf.links.core.log.Errorf("Failed to connect to node: %q sent ed25519 key that does not match pinned keys", intf.name)