From 00d4acc160bde5f401da9fc6fcf352780699938a Mon Sep 17 00:00:00 2001 From: Varunram Date: Fri, 23 Nov 2018 12:12:28 +0530 Subject: [PATCH 1/3] connect to remote BOLT nodes this is a combination of earlier PRs #434 and #435 with newer changes. With this PR, you must be able to 1. Connect to a remote lnd node and observe connection success 2. Connect remote lnd node to lit node for 15s --- cmd/lit-af/lit-af.go | 77 ++++++++++------ db/lnbolt/peerdb.go | 14 +-- lit.go | 35 +++++-- litrpc/netcmds.go | 13 +-- lncore/peers.go | 38 +------- lndc/conn.go | 63 ++++++++++--- lndc/listener.go | 32 ++++++- lndc/noise.go | 213 ++++++++++++++++++++++++++++++------------- lndc/noise_test.go | 5 +- lnp2p/listen.go | 56 ++++++++++-- lnp2p/msgproc.go | 3 +- lnp2p/peermgr.go | 190 +++++++++++++++++++------------------- lnp2p/util.go | 10 +- qln/init.go | 2 +- qln/netio.go | 15 +-- qln/remotecontrol.go | 9 +- uspv/header.go | 4 + 17 files changed, 478 insertions(+), 301 deletions(-) diff --git a/cmd/lit-af/lit-af.go b/cmd/lit-af/lit-af.go index f79acb555..b5468c45d 100644 --- a/cmd/lit-af/lit-af.go +++ b/cmd/lit-af/lit-af.go @@ -1,6 +1,7 @@ package main import ( + "encoding/hex" "fmt" "os" "path/filepath" @@ -65,7 +66,7 @@ func newConfigParser(conf *litAfConfig, options flags.Options) *flags.Parser { return parser } -func (lc *litAfClient) litAfSetup(conf litAfConfig) { +func (lc *litAfClient) litAfSetup(conf litAfConfig) error { var err error // create home directory if it does not exist @@ -89,42 +90,60 @@ func (lc *litAfClient) litAfSetup(conf litAfConfig) { } logging.SetLogLevel(logLevel) // defaults to zero + // we don't know whether the passed address is a remotePKH or a remotePK + // so we need to detect that here and then take steps accordingly + // so the first part involved here would be either dealing with raw pubkeys or + // dealing with pk hashes + // another question here is how do we deal with connecting to other nodes? + // if we need that in, we need to hcange our overall architecture to host + // pks as well as pk hashes adr, host, port := lnutil.ParseAdrStringWithPort(conf.Con) - logging.Infof("Adr: %s, Host: %s, Port: %d", adr, host, port) - if litrpc.LndcRpcCanConnectLocallyWithHomeDir(defaultDir) && adr == "" && (host == "localhost" || host == "127.0.0.1") { + // now we've split the address, check if pkh, if not, route straight to noise_xk + if len(adr) == 0 { + // so the user didn't provide us with an address to connect to + // we need to connect to the locally running lit-af instance lc.RPCClient, err = litrpc.NewLocalLndcRpcClientWithHomeDirAndPort(defaultDir, port) if err != nil { logging.Fatal(err.Error()) } - } else { - if !lnutil.LitAdrOK(adr) { - logging.Fatal("lit address passed in -con parameter is not valid") - } + return nil + } - keyFilePath := filepath.Join(defaultDir, "lit-af-key.hex") - privKey, err := lnutil.ReadKeyFile(keyFilePath) - if err != nil { - logging.Fatal(err.Error()) - } - key, _ := koblitz.PrivKeyFromBytes(koblitz.S256(), privKey[:]) - - if adr != "" && strings.HasPrefix(adr, "ln1") && host == "" { - ipv4, _, err := lnutil.Lookup(adr, conf.Tracker, "") - if err != nil { - logging.Fatalf("Error looking up address on the tracker: %s", err) - } else { - adr = fmt.Sprintf("%s@%s", adr, ipv4) - } - } else { - adr = fmt.Sprintf("%s@%s:%d", adr, host, port) - } + keyFilePath := filepath.Join(defaultDir, "lit-af-key.hex") + privKey, err := lnutil.ReadKeyFile(keyFilePath) + if err != nil { + logging.Fatal(err.Error()) + } + key, _ := koblitz.PrivKeyFromBytes(koblitz.S256(), privKey[:]) + pubkey := key.PubKey().SerializeCompressed() // this is in bytes + fmt.Printf("The pubkey of this lit-af instance is: %s\n", hex.EncodeToString(pubkey)) + var temp [33]byte + copy(temp[:], pubkey[:33]) + fmt.Printf("The pkh of this lit-af instance is: %s\n", lnutil.LitAdrFromPubkey(temp)) + + if len(adr) == 44 && !lnutil.LitAdrOK(adr) { + logging.Fatal("lit address passed in -con parameter is not valid") + } - lc.RPCClient, err = litrpc.NewLndcRpcClient(adr, key) + if host == "" { + ipv4, _, err := lnutil.Lookup(adr, conf.Tracker, "") if err != nil { - logging.Fatal(err.Error()) + logging.Fatalf("Error looking up address on the tracker: %s", err) } + adr = fmt.Sprintf("%s@%s", adr, ipv4) + } else { + // host is non empty or address is remotePK, doesn't matter since NewLndcRpcClient will take acre of it for us + adr = fmt.Sprintf("%s@%s:%d", adr, host, port) } + + fmt.Printf("Remote Host: %s, Port: %d\n", host, port) + lc.RPCClient, err = litrpc.NewLndcRpcClient(adr, key) + if err != nil { + logging.Fatal(err.Error()) + } + + return nil } // for now just testing how to connect and get messages back and forth @@ -137,7 +156,11 @@ func main() { Dir: defaultDir, Tracker: defaultTracker, } - lc.litAfSetup(conf) // setup lit-af to start + err = lc.litAfSetup(conf) // setup lit-af to start + if err != nil { + logging.Error(err) + return + } rl, err := readline.NewEx(&readline.Config{ Prompt: lnutil.Prompt("lit-af") + lnutil.White("# "), diff --git a/db/lnbolt/peerdb.go b/db/lnbolt/peerdb.go index 2ebb81501..c1ef6e35f 100644 --- a/db/lnbolt/peerdb.go +++ b/db/lnbolt/peerdb.go @@ -55,13 +55,7 @@ func (pdb *peerboltdb) GetPeerAddrs() ([]lncore.LnAddr, error) { if k == nil { break } - lnaddr, err := lncore.ParseLnAddr(string(k)) - if err != nil { - logging.Warnf("lnbolt/peerdb: found invalid key in DB as lnaddr: %s (error: %s)", string(k), err.Error()) - continue - } - - atmp = append(atmp, lnaddr) + atmp = append(atmp, lncore.LnAddr(string(k))) } // Now that we have the final array return it. @@ -132,11 +126,7 @@ func (pdb *peerboltdb) GetPeerInfos() (map[lncore.LnAddr]lncore.PeerInfo, error) return err2 } - ka, err2 := lncore.ParseLnAddr(string(k)) - if err2 != nil { - logging.Warnf("lnbolt/peerdb: found invalid key in DB as lnaddr: %s (error: %s)", string(k), err.Error()) - continue - } + ka := lncore.LnAddr(string(k)) mtmp[ka] = pi } diff --git a/lit.go b/lit.go index cc79ebecf..6dd0d475c 100644 --- a/lit.go +++ b/lit.go @@ -6,6 +6,7 @@ import ( "runtime" "syscall" "time" + "encoding/hex" "github.com/mit-dci/lit/logging" @@ -55,11 +56,12 @@ type litConfig struct { // define a struct for usage with go-flags Rpcport uint16 `short:"p" long:"rpcport" description:"Set RPC port to connect to"` Rpchost string `long:"rpchost" description:"Set RPC host to listen to"` // auto config - AutoReconnect bool `long:"autoReconnect" description:"Attempts to automatically reconnect to known peers periodically."` - AutoReconnectInterval int64 `long:"autoReconnectInterval" description:"The interval (in seconds) the reconnect logic should be executed"` - AutoReconnectOnlyConnectedCoins bool `long:"autoReconnectOnlyConnectedCoins" description:"Only reconnect to peers that we have channels with in a coin whose coin daemon is available"` - AutoListenPort int `long:"autoListenPort" description:"When auto reconnect enabled, starts listening on this port"` - NoAutoListen bool `long:"noautolisten" description:"Don't automatically listen on any ports."` + AutoReconnect bool `long:"autoReconnect" description:"Attempts to automatically reconnect to known peers periodically."` + AutoReconnectInterval int64 `long:"autoReconnectInterval" description:"The interval (in seconds) the reconnect logic should be executed"` + AutoReconnectOnlyConnectedCoins bool `long:"autoReconnectOnlyConnectedCoins" description:"Only reconnect to peers that we have channels with in a coin whose coin daemon is available"` + AutoListenPort int `long:"autoListenPort" description:"When auto reconnect enabled, starts listening on this port"` + NoAutoListen bool `long:"noautolisten" description:"Don't automatically listen on any ports."` + Whitelist string `long:"whitelist" description:"Whitelist a single address so that you can enable a remote peer to login for the first time and authenticate others"` Params *coinparam.Params } @@ -293,12 +295,33 @@ func main() { // if we don't link wallet, we can still continue, no worries. logging.Error(err) } - + logging.Info("Starting lit node") rpcl := new(litrpc.LitRPC) rpcl.Node = node rpcl.OffButton = make(chan bool, 1) node.RPC = rpcl + Authorization := new(qln.RemoteControlAuthorization) + Authorization.UnansweredRequest = false + Authorization.Allowed = true + + // check for whitelisted addresses here + if conf.Whitelist != "" && len(conf.Whitelist) == 66 { // the length of a standard LNAddr + // pass the pubkey here, which is ugly + // we could pass the pkh, have the peer dial us and we could get + // the pubkey during the second round of noise, but maybe overkill? + // we need to decode this hex string into a byte slice + addr, _ := hex.DecodeString(conf.Whitelist) + var temp [33]byte + copy(temp[:33], addr) + logging.Info("Whitelisting address as requested: ",addr) + err = rpcl.Node.SaveRemoteControlAuthorization(temp, Authorization) + if err != nil { + logging.Errorf("Error whitelisting address: %s", err.Error()) + // don't fatal since this doesn't affect program flow + } + } + if conf.UnauthRPC { go litrpc.RPCListen(rpcl, conf.Rpchost, conf.Rpcport) } diff --git a/litrpc/netcmds.go b/litrpc/netcmds.go index c82a7c0a3..cca98b71b 100644 --- a/litrpc/netcmds.go +++ b/litrpc/netcmds.go @@ -8,6 +8,7 @@ import ( "github.com/mit-dci/lit/bech32" "github.com/mit-dci/lit/crypto/koblitz" "github.com/mit-dci/lit/lncore" + "github.com/mit-dci/lit/lnp2p" "github.com/mit-dci/lit/lnutil" "github.com/mit-dci/lit/logging" "github.com/mit-dci/lit/qln" @@ -65,6 +66,8 @@ func (r *LitRPC) Connect(args ConnectArgs, reply *ConnectReply) error { // first, see if the peer to connect to is referenced by peer index. var connectAdr string // check if a peer number was supplied instead of a pubkeyhash + // accept either an string or a pubkey (raw) + // so args.LNAddr passed here contains blah@host:ip peerIdxint, err := strconv.Atoi(args.LNAddr) // number will mean no error if err == nil { @@ -81,7 +84,6 @@ func (r *LitRPC) Connect(args ConnectArgs, reply *ConnectReply) error { // use string as is, try to convert to ln address connectAdr = args.LNAddr } - err = r.Node.DialPeer(connectAdr) if err != nil { return err @@ -94,13 +96,8 @@ func (r *LitRPC) Connect(args ConnectArgs, reply *ConnectReply) error { paddr = strings.SplitN(paddr, "@", 2)[0] } - pm := r.Node.PeerMan - lnaddr, err := lncore.ParseLnAddr(paddr) - if err != nil { - return err - } - - p := pm.GetPeer(lnaddr) + var pm *lnp2p.PeerManager = r.Node.PeerMan + p := pm.GetPeer(lncore.LnAddr(paddr)) if p == nil { return fmt.Errorf("couldn't find peer in manager after connecting") } diff --git a/lncore/peers.go b/lncore/peers.go index 567c84f45..6657557d6 100644 --- a/lncore/peers.go +++ b/lncore/peers.go @@ -1,46 +1,9 @@ package lncore -import ( - "fmt" - - "github.com/mit-dci/lit/bech32" -) - // LnAddr is just a bech32-encoded pubkey. // TODO Move this to another package so it's more obviously not *just* IO-related. type LnAddr string -// ParseLnAddr will verify that the string passed is a valid LN address, as in -// ln1pmclh89haeswrw0unf8awuyqeu4t2uell58nea. -func ParseLnAddr(m string) (LnAddr, error) { - - prefix, raw, err := bech32.Decode(m) - - // Check it's valid bech32. - if err != nil { - return "", err - } - - // Check it has the right prefix. - if prefix != "ln" { - return "", fmt.Errorf("prefix is not 'ln'") - } - - // Check the length of the content bytes is right. - if len(raw) > 20 { - return "", fmt.Errorf("address too long to be pubkey") - } - - return LnAddr(m), nil // should be the only place we cast to this type - -} - -// ToString returns the LnAddr as a string. Right now it just unwraps it but it -// might do something more eventually. -func (lnaddr LnAddr) ToString() string { - return string(lnaddr) -} - // LitPeerStorage is storage for peer data. type LitPeerStorage interface { GetPeerAddrs() ([]LnAddr, error) @@ -59,6 +22,7 @@ type PeerInfo struct { LnAddr *LnAddr `json:"lnaddr"` Nickname *string `json:"name"` NetAddr *string `json:"netaddr"` // ip address, port, I guess + Pubkey *string `json:pubkey` // TEMP This is again, for adapting to the old system. PeerIdx uint32 `json:"hint_peeridx"` diff --git a/lndc/conn.go b/lndc/conn.go index 3e0ccd514..edb071c6f 100644 --- a/lndc/conn.go +++ b/lndc/conn.go @@ -2,6 +2,7 @@ package lndc import ( "bytes" + "encoding/hex" "fmt" "io" "math" @@ -29,28 +30,53 @@ type Conn struct { // A compile-time assertion to ensure that Conn meets the net.Conn interface. var _ net.Conn = (*Conn)(nil) +var Noise_XK bool // Dial attempts to establish an encrypted+authenticated connection with the // remote peer located at address which has remotePub as its long-term static // public key. In the case of a handshake failure, the connection is closed and // a non-nil error is returned. -func Dial(localPriv *koblitz.PrivateKey, ipAddr string, remotePKH string, +func Dial(localPriv *koblitz.PrivateKey, ipAddr string, remoteAddress string, dialer func(string, string) (net.Conn, error)) (*Conn, error) { + var remotePKH string + var remotePK [33]byte + if remoteAddress[0:3] == "ln1" { // its a remote PKH + remotePKH = remoteAddress + } else if len(remoteAddress) == 66 { // hex encoded remotePK + temp, _ := hex.DecodeString(remoteAddress) + copy(remotePK[:], temp) + logging.Info("Got remote PK: ", remotePK, ", using noise_xk to connect") + SetXKConsts() + } var conn net.Conn var err error conn, err = dialer("tcp", ipAddr) - logging.Info("ipAddr is", ipAddr) + logging.Debug("ipAddr is: ", ipAddr) if err != nil { return nil, err } - b := &Conn{ - conn: conn, - noise: NewNoiseMachine(true, localPriv), + b := new(Conn) + if Noise_XK { + // we need to convert the raw PK to a koblitz public key + remotePub, err := koblitz.ParsePubKey(remotePK[:], koblitz.S256()) + if err != nil { + logging.Debug(err) + return nil, err + } + b = &Conn{ + conn: conn, + noise: NewNoiseXKMachine(true, localPriv, remotePub), + } + } else { + b = &Conn{ + conn: conn, + noise: NewNoiseXXMachine(true, localPriv), + } } // Initiate the handshake by sending the first act to the receiver. - actOne, err := b.noise.GenActOne() + actOne, err := b.noise.GenActOne(remotePK) if err != nil { b.conn.Close() return nil, err @@ -59,7 +85,6 @@ func Dial(localPriv *koblitz.PrivateKey, ipAddr string, remotePKH string, b.conn.Close() return nil, err } - // We'll ensure that we get ActTwo from the remote peer in a timely // manner. If they don't respond within 1s, then we'll kill the // connection. @@ -69,22 +94,30 @@ func Dial(localPriv *koblitz.PrivateKey, ipAddr string, remotePKH string, // remotePub), then read the second act after which we'll be able to // send our static public key to the remote peer with strong forward // secrecy. - var actTwo [ActTwoSize]byte + actTwo := make([]byte, ActTwoSize) if _, err := io.ReadFull(conn, actTwo[:]); err != nil { b.conn.Close() return nil, err } - s, err := b.noise.RecvActTwo(actTwo) - if err != nil { - b.conn.Close() - return nil, err + if !Noise_XK { + remotePK, err = b.noise.RecvActTwo(actTwo) + if err != nil { + b.conn.Close() + return nil, err + } + } else { + if _, err := b.noise.RecvActTwo(actTwo); err != nil { + b.conn.Close() + return nil, err + } } - logging.Info("Received pubkey", s) - if lnutil.LitAdrFromPubkey(s) != remotePKH { + logging.Infoln("Received pubkey: ", remotePK) + if lnutil.LitAdrFromPubkey(remotePK) != remotePKH && !Noise_XK { + // for noise_XK dont check PKH and PK because we'd have already checked this + // the last time we connected to this guy return nil, fmt.Errorf("Remote PKH doesn't match. Quitting!") } - logging.Infof("Received PKH %s matches", lnutil.LitAdrFromPubkey(s)) // Finally, complete the handshake by sending over our encrypted static // key and execute the final ECDH operation. diff --git a/lndc/listener.go b/lndc/listener.go index b8e676b1d..88d7609f1 100644 --- a/lndc/listener.go +++ b/lndc/listener.go @@ -1,6 +1,7 @@ package lndc import ( + "fmt" "errors" "io" "net" @@ -8,6 +9,7 @@ import ( "time" "github.com/mit-dci/lit/crypto/koblitz" + "github.com/mit-dci/lit/logging" ) // defaultHandshakes is the maximum number of handshakes that can be done in @@ -102,9 +104,12 @@ func (l *Listener) doHandshake(conn net.Conn) { default: } + // TODO: the listener here defaults to a noise_XK machine, we should add a condition + // here so that we can use a noise_XX machine for lit specific connections + // at this point, we really don't know who (noiseXX or noiseXK) is connecting to + // us, so we need to init hte noise part later lndcConn := &Conn{ - conn: conn, - noise: NewNoiseMachine(false, l.localStatic), + conn: conn, } // We'll ensure that we get ActOne from the remote peer in a timely @@ -115,12 +120,29 @@ func (l *Listener) doHandshake(conn net.Conn) { // Attempt to carry out the first act of the handshake protocol. If the // connecting node doesn't know our long-term static public key, then // this portion will fail with a non-nil error. - var actOne [ActOneSize]byte + actOne := make([]byte, ActOneSize) if _, err := io.ReadFull(conn, actOne[:]); err != nil { lndcConn.conn.Close() l.rejectConn(err) return } + + if actOne[0] == 0 { + // remote node wants to connect via XK + SetXKConsts() + lndcConn.noise = NewNoiseXKMachine(false, l.localStatic, nil) + logging.Infof("remote node wants to connect via noise_xk") + } else if actOne[0] == 1 { + // no need to init the constants here since defaults are set to noise_XX + logging.Infof("Remote Node requesting connection via noise_xx") + lndcConn.noise = NewNoiseXXMachine(false, l.localStatic) + } else { + logging.Info("Unknown version byte received, dropping connection") + lndcConn.conn.Close() + l.rejectConn(fmt.Errorf("Unknown version byte received, dropping connection")) + return + } + if err := lndcConn.noise.RecvActOne(actOne); err != nil { lndcConn.conn.Close() l.rejectConn(err) @@ -128,7 +150,7 @@ func (l *Listener) doHandshake(conn net.Conn) { } // Next, progress the handshake processes by sending over our ephemeral // key for the session along with an authenticating tag. - actTwo, err := lndcConn.noise.GenActTwo() + actTwo, err := lndcConn.noise.GenActTwo(HandshakeVersion) if err != nil { lndcConn.conn.Close() l.rejectConn(err) @@ -154,7 +176,7 @@ func (l *Listener) doHandshake(conn net.Conn) { // Finally, finish the handshake processes by reading and decrypting // the connection peer's static public key. If this succeeds then both // sides have mutually authenticated each other. - var actThree [ActThreeSize]byte + actThree := make([]byte, ActThreeSize) if _, err := io.ReadFull(conn, actThree[:]); err != nil { lndcConn.conn.Close() l.rejectConn(err) diff --git a/lndc/noise.go b/lndc/noise.go index 59c14c55f..181c0f352 100644 --- a/lndc/noise.go +++ b/lndc/noise.go @@ -4,8 +4,8 @@ import ( "crypto/cipher" "crypto/sha256" "encoding/binary" - "fmt" "errors" + "fmt" "io" "math" "time" @@ -17,13 +17,6 @@ import ( ) const ( - // protocolName is the precise instantiation of the Noise protocol - // This value will be used as part of the prologue. If the initiator - // and responder aren't using the exact same string for this value, - // along with prologue of the Bitcoin network, then the initial - // handshake will fail. - protocolName = "Noise_XX_secp256k1_ChaChaPoly_SHA256" - // macSize is the length in bytes of the tags generated by poly1305. macSize = 16 @@ -262,10 +255,10 @@ type handshakeState struct { remoteEphemeral *koblitz.PublicKey } -// newHandshakeState returns a new instance of the handshake state initialized +// newXXHandshakeState returns a new instance of the handshake state initialized // with the prologue and protocol name. If this is the responder's handshake // state, then the remotePub can be nil. -func newHandshakeState(initiator bool, prologue []byte, +func newXXHandshakeState(initiator bool, prologue []byte, localStatic *koblitz.PrivateKey) handshakeState { h := handshakeState{ @@ -282,6 +275,32 @@ func newHandshakeState(initiator bool, prologue []byte, return h } +// newXXHandshakeState returns a new instance of the handshake state initialized +// with the prologue and protocol name. If this is the responder's handshake +// state, then the remotePub can be nil. +func newXKHandshakeState(initiator bool, prologue []byte, + localStatic *koblitz.PrivateKey, remoteStatic *koblitz.PublicKey) handshakeState { + + h := handshakeState{ + initiator: initiator, + localStatic: localStatic, + remoteStatic: remoteStatic, + } + + // Set the current chaining key and handshake digest to the hash of the + // protocol name, and additionally mix in the prologue. If either sides + // disagree about the prologue or protocol name, then the handshake + // will fail. + h.InitializeSymmetric([]byte("Noise_XK_secp256k1_ChaChaPoly_SHA256")) + h.mixHash(prologue) + if initiator { + h.mixHash(remoteStatic.SerializeCompressed()) + } else { + h.mixHash(localStatic.PubKey().SerializeCompressed()) + } + return h +} + // EphemeralGenerator is a functional option that allows callers to substitute // a custom function for use when generating ephemeral keys for ActOne or // ActTwo. The function closure return by this function can be passed into @@ -316,10 +335,19 @@ func EphemeralGenerator(gen func() (*koblitz.PrivateKey, error)) func(*Machine) // INITIATOR -> e RESPONDER // INITIATOR <- e, ee, s, es RESPONDER // INITIATOR -> s, se RESPONDER +// The protocol has the following steps involved: +// XK(s, rs): +// INITIATOR <- s +// INITIATOR -> e, es RESPONDER +// INITIATOR <- e, ee RESPONDER +// INITIATOR -> s, se RESPONDER // s refers to the static key (or public key) belonging to an entity // e refers to the ephemeral key // e, ee, es refer to a DH exchange between the initiator's key pair and the // responder's key pair. The letters e and s hold the same meaning as before. +// lit uses Noise_XX to connect with nodes that it does not know of and uses +// Noise_XK for nodes that it has previously connected to. This saves 33 bytes +// in Act Two. type Machine struct { sendCipher cipherState @@ -350,34 +378,48 @@ type Machine struct { // string "lightning" as the prologue. The last parameter is a set of variadic // arguments for adding additional options to the lndc Machine // initialization. -func NewNoiseMachine(initiator bool, localStatic *koblitz.PrivateKey, - options ...func(*Machine)) *Machine { +func NewNoiseXKMachine(initiator bool, localStatic *koblitz.PrivateKey, + remotePub *koblitz.PublicKey) *Machine { - handshake := newHandshakeState(initiator, []byte("lit"), localStatic) - // TODO: if we're sending messages of type XK, set it back to - // "lightning" which is what BOLT uses + handshake := newXKHandshakeState(initiator, []byte("lightning"), localStatic, remotePub) + // "lightning" is what BOLT uses, "lit" is used for XX handshakes m := &Machine{handshakeState: handshake} - // With the initial base machine created, we'll assign our default // version of the ephemeral key generator. m.ephemeralGen = func() (*koblitz.PrivateKey, error) { return koblitz.NewPrivateKey(koblitz.S256()) } - // With the default options established, we'll now process all the - // options passed in as parameters. - for _, option := range options { - option(m) + + return m +} + +func NewNoiseXXMachine(initiator bool, localStatic *koblitz.PrivateKey) *Machine { + + handshake := newXXHandshakeState(initiator, []byte("lit"), localStatic) + + m := &Machine{handshakeState: handshake} + + // With the initial base machine created, we'll assign our default + // version of the ephemeral key generator. + m.ephemeralGen = func() (*koblitz.PrivateKey, error) { + return koblitz.NewPrivateKey(koblitz.S256()) } return m } -const ( +var ( + // protocolName is the precise instantiation of the Noise protocol + // This value will be used as part of the prologue. If the initiator + // and responder aren't using the exact same string for this value, + // along with prologue of the Bitcoin network, then the initial + // handshake will fail. + protocolName = "Noise_XX_secp256k1_ChaChaPoly_SHA256" // HandshakeVersion is the expected version of the lndc handshake. // Any messages that carry a different version will cause the handshake // to abort immediately. - HandshakeVersion = byte(1) // TODO: add support for noise_XK (brontide) as well + HandshakeVersion = byte(1) // ActOneSize is the size of the packet sent from initiator to // responder in ActOne. The packet consists of a handshake version, an @@ -403,33 +445,55 @@ const ( ActThreeSize = 66 ) +func SetXKConsts() { + Noise_XK = true + // Noise_XK's hadnshake version + HandshakeVersion = byte(0) + + // ActTwoSize is the size the packet sent from responder to initiator + // in ActTwo. The packet consists of a handshake version, an ephemeral + // key in compressed format and a 16-byte poly1305 tag. + // <- e, ee + // 1 + 33 + 16 + ActTwoSize = 50 + // protocolName is the precise instantiation of the Noise protocol + // This value will be used as part of the prologue. If the initiator + // and responder aren't using the exact same string for this value, + // along with prologue of the Bitcoin network, then the initial + // handshake will fail. + protocolName = "Noise_XK_secp256k1_ChaChaPoly_SHA256" +} + // GenActOne generates the initial packet (act one) to be sent from initiator // to responder. During act one the initiator generates an ephemeral key and // hashes it into the handshake digest. Future payloads are encrypted with a key // derived from this result. // -> e -func (b *Machine) GenActOne() ([ActOneSize]byte, error) { - var ( - err error - actOne [ActOneSize]byte - ) - +func (b *Machine) GenActOne(remotePK [33]byte) ([]byte, error) { + // TODO: remove this remotePK passed since its not needed + var err error + actOne := make([]byte, ActOneSize) // Generate e b.localEphemeral, err = b.ephemeralGen() if err != nil { return actOne, err } - // Compress e e := b.localEphemeral.PubKey().SerializeCompressed() // Hash it into the handshake digest b.mixHash(e) + if Noise_XK { + // es + s := ecdh(b.remoteStatic, b.localEphemeral) + b.mixKey(s[:]) + } authPayload := b.EncryptAndHash([]byte{}) actOne[0] = HandshakeVersion copy(actOne[1:34], e) copy(actOne[34:], authPayload) + _, err = b.DecryptAndHash(authPayload[:]) return actOne, nil } @@ -437,7 +501,10 @@ func (b *Machine) GenActOne() ([ActOneSize]byte, error) { // executes the mirrored actions to that of the initiator extending the // handshake digest and deriving a new shared secret based on an ECDH with the // initiator's ephemeral key and responder's static key. -func (b *Machine) RecvActOne(actOne [ActOneSize]byte) error { +func (b *Machine) RecvActOne(actOne []byte) error { + // before we call this function, we already know whether its noise_XX or + // noise_XK and have set the appropriate constants on the listener side + // since we check for the first byte received var ( err error e [33]byte @@ -446,7 +513,7 @@ func (b *Machine) RecvActOne(actOne [ActOneSize]byte) error { // If the handshake version is unknown, then the handshake fails // immediately. - if actOne[0] != HandshakeVersion { + if !(actOne[0] == 0 || actOne[0] == 1) { return fmt.Errorf("Act One: invalid handshake version: %v, "+ "only %v is valid, msg=%x", actOne[0], HandshakeVersion, actOne[:]) @@ -454,7 +521,6 @@ func (b *Machine) RecvActOne(actOne [ActOneSize]byte) error { copy(e[:], actOne[1:34]) copy(p[:], actOne[34:]) - // e b.remoteEphemeral, err = koblitz.ParsePubKey(e[:], koblitz.S256()) if err != nil { @@ -462,6 +528,11 @@ func (b *Machine) RecvActOne(actOne [ActOneSize]byte) error { } b.mixHash(b.remoteEphemeral.SerializeCompressed()) + if actOne[0] == 0 { + // es + es := ecdh(b.remoteEphemeral, b.localStatic) + b.mixKey(es) + } _, err = b.DecryptAndHash(p[:]) return err // nil means Act one completed successfully } @@ -469,12 +540,9 @@ func (b *Machine) RecvActOne(actOne [ActOneSize]byte) error { // GenActTwo generates the second packet (act two) to be sent from the // responder to the initiator // <- e, ee, s, es -func (b *Machine) GenActTwo() ([ActTwoSize]byte, error) { - var ( - err error - actTwo [ActTwoSize]byte - ) - +func (b *Machine) GenActTwo(HandshakeVersion byte) ([]byte, error) { + var err error + actTwo := make([]byte, ActTwoSize) // e b.localEphemeral, err = b.ephemeralGen() if err != nil { @@ -488,19 +556,28 @@ func (b *Machine) GenActTwo() ([ActTwoSize]byte, error) { ee := ecdh(b.remoteEphemeral, b.localEphemeral) b.mixKey(ee) - // s - s := b.localStatic.PubKey().SerializeCompressed() - b.mixHash(s) - - // es - es := ecdh(b.remoteEphemeral, b.localStatic) - b.mixKey(es) + if HandshakeVersion == 1 { + // s + s := b.localStatic.PubKey().SerializeCompressed() + b.mixHash(s) + + // es + es := ecdh(b.remoteEphemeral, b.localStatic) + b.mixKey(es) + + authPayload := b.EncryptAndHash([]byte{}) + actTwo[0] = HandshakeVersion + copy(actTwo[1:34], e) + copy(actTwo[34:67], s) + copy(actTwo[67:], authPayload) + // add additional stuff based on what we need + return actTwo, nil + } authPayload := b.EncryptAndHash([]byte{}) actTwo[0] = HandshakeVersion copy(actTwo[1:34], e) - copy(actTwo[34:67], s) - copy(actTwo[67:], authPayload) + copy(actTwo[34:], authPayload) // add additional stuff based on what we need return actTwo, nil } @@ -508,7 +585,7 @@ func (b *Machine) GenActTwo() ([ActTwoSize]byte, error) { // RecvActTwo processes the second packet (act two) sent from the responder to // the initiator. A successful processing of this packet authenticates the // initiator to the responder. -func (b *Machine) RecvActTwo(actTwo [ActTwoSize]byte) ([33]byte, error) { +func (b *Machine) RecvActTwo(actTwo []byte) ([33]byte, error) { var ( err error e [33]byte @@ -524,9 +601,14 @@ func (b *Machine) RecvActTwo(actTwo [ActTwoSize]byte) ([33]byte, error) { actTwo[:]) } - copy(e[:], actTwo[1:34]) - copy(s[:], actTwo[34:67]) - copy(p[:], actTwo[67:]) + if HandshakeVersion == 0 { + copy(e[:], actTwo[1:34]) + copy(p[:], actTwo[34:]) + } else { + copy(e[:], actTwo[1:34]) + copy(s[:], actTwo[34:67]) + copy(p[:], actTwo[67:]) + } // e b.remoteEphemeral, err = koblitz.ParsePubKey(e[:], koblitz.S256()) @@ -539,17 +621,18 @@ func (b *Machine) RecvActTwo(actTwo [ActTwoSize]byte) ([33]byte, error) { ee := ecdh(b.remoteEphemeral, b.localEphemeral) b.mixKey(ee) - // s - b.remoteStatic, err = koblitz.ParsePubKey(s[:], koblitz.S256()) - if err != nil { - return empty, err - } - b.mixHash(b.remoteStatic.SerializeCompressed()) - - // es - es := ecdh(b.remoteStatic, b.localEphemeral) - b.mixKey(es) + if HandshakeVersion == 1 { + // s + b.remoteStatic, err = koblitz.ParsePubKey(s[:], koblitz.S256()) + if err != nil { + return empty, err + } + b.mixHash(b.remoteStatic.SerializeCompressed()) + // es + es := ecdh(b.remoteStatic, b.localEphemeral) + b.mixKey(es) + } _, err = b.DecryptAndHash(p[:]) return s, err } @@ -560,9 +643,9 @@ func (b *Machine) RecvActTwo(actTwo [ActTwoSize]byte) ([33]byte, error) { // the responder. This act also includes the final ECDH operation which yields // the final session. // -> s, se -func (b *Machine) GenActThree() ([ActThreeSize]byte, error) { - var actThree [ActThreeSize]byte - +func (b *Machine) GenActThree() ([]byte, error) { + //var actThree [ActThreeSize]byte + actThree := make([]byte, ActThreeSize) // s s := b.localStatic.PubKey().SerializeCompressed() encryptedS := b.EncryptAndHash(s) @@ -587,7 +670,7 @@ func (b *Machine) GenActThree() ([ActThreeSize]byte, error) { // the responder. After processing this act, the responder learns of the // initiator's static public key. Decryption of the static key serves to // authenticate the initiator to the responder. -func (b *Machine) RecvActThree(actThree [ActThreeSize]byte) error { +func (b *Machine) RecvActThree(actThree []byte) error { var ( err error s [49]byte diff --git a/lndc/noise_test.go b/lndc/noise_test.go index 861c8c851..724955cfe 100644 --- a/lndc/noise_test.go +++ b/lndc/noise_test.go @@ -342,7 +342,8 @@ func TestBolt0008TestVectors(t *testing.T) { // act one. This should consist of exactly 50 bytes. We'll assert that // the payload return is _exactly_ the same as what's specified within // the test vectors. - actOne, err := initiator.GenActOne() + var empty [33]byte + actOne, err := initiator.GenActOne(empty) if err != nil { t.Fatalf("unable to generate act one: %v", err) } @@ -367,7 +368,7 @@ func TestBolt0008TestVectors(t *testing.T) { // its contribution to the crypto handshake. We'll also verify that we // produce the _exact_ same byte stream as advertised within the spec's // test vectors. - actTwo, err := responder.GenActTwo() + actTwo, err := responder.GenActTwo(byte(1)) if err != nil { t.Fatalf("unable to generate act two: %v", err) } diff --git a/lnp2p/listen.go b/lnp2p/listen.go index 954462dca..6a974c88b 100644 --- a/lnp2p/listen.go +++ b/lnp2p/listen.go @@ -2,6 +2,7 @@ package lnp2p import ( "github.com/mit-dci/lit/eventbus" + "github.com/mit-dci/lit/lncore" "github.com/mit-dci/lit/lndc" "github.com/mit-dci/lit/logging" ) @@ -47,23 +48,58 @@ func acceptConnections(listener *lndc.Listener, port int, pm *PeerManager) { rlitaddr := convertPubkeyToLitAddr(rpk) rnetaddr := lndcConn.RemoteAddr() - // Make sure we can't let ourself connect to ourself. - if string(rlitaddr) == pm.GetExternalAddress() { - logging.Infof("peermgr: Got a connection from ourselves? Dropping.") - lndcConn.Close() + logging.Infof("New connection from %s at %s\n", rlitaddr, rnetaddr.String()) + + // Read the peer info from the DB. + pi, err := pm.peerdb.GetPeerInfo(rlitaddr) + if err != nil { + logging.Warnf("problem loading peer info in DB (maybe this is ok?): %s\n", err.Error()) + netConn.Close() continue } - logging.Infof("peermgr: New connection from %s at %s\n", rlitaddr, rnetaddr.String()) + // Create the actual peer object. + npeer := &Peer{ + lnaddr: rlitaddr, + nickname: nil, + conn: lndcConn, + idpubkey: rpk, - p, err := pm.handleNewConnection(lndcConn, rlitaddr) - if err != nil { - logging.Warnf("%s\n", err.Error()) - continue + // TEMP + idx: nil, } + // Add the peer data to the DB if we don't have it. + if pi == nil { + raddr := rnetaddr.String() + pidx, err := pm.peerdb.GetUniquePeerIdx() + if err != nil { + logging.Errorf("problem getting unique peeridx: %s\n", err.Error()) + } + pi = &lncore.PeerInfo{ + LnAddr: &rlitaddr, + Nickname: nil, + NetAddr: &raddr, + PeerIdx: pidx, + } + err = pm.peerdb.AddPeer(rlitaddr, *pi) + npeer.idx = &pidx + if err != nil { + // don't close it, I guess + logging.Errorf("problem saving peer info to DB: %s\n", err.Error()) + } + } else { + npeer.nickname = pi.Nickname + // TEMP + npeer.idx = &pi.PeerIdx + } + + // Don't do any locking here since registerPeer takes a lock and Go's + // mutex isn't reentrant. + pm.registerPeer(npeer) + // Start a goroutine to process inbound traffic for this peer. - go processConnectionInboundTraffic(p, pm) + go processConnectionInboundTraffic(npeer, pm) } diff --git a/lnp2p/msgproc.go b/lnp2p/msgproc.go index 3897a9580..b1207c65f 100644 --- a/lnp2p/msgproc.go +++ b/lnp2p/msgproc.go @@ -74,7 +74,8 @@ func (mp *MessageProcessor) HandleMessage(peer *Peer, buf []byte) error { } var err error - + // TODO: Change wire protocol + logging.Debug("Received a message from peer: ", buf) // First see if we have handlers defined for this message type. mtype := buf[0] h := mp.handlers[mtype] diff --git a/lnp2p/peermgr.go b/lnp2p/peermgr.go index f367ddd83..15e6230e0 100644 --- a/lnp2p/peermgr.go +++ b/lnp2p/peermgr.go @@ -3,8 +3,10 @@ package lnp2p //"crypto/ecdsa" // TODO Use ecdsa not koblitz import ( "crypto/ecdsa" + "encoding/hex" "fmt" "net" + "strconv" "sync" "time" @@ -30,11 +32,10 @@ const MaxNodeCount = 1024 type PeerManager struct { // Biographical. - idkey privkey - peerdb lncore.LitPeerStorage - ebus *eventbus.EventBus - mproc MessageProcessor - netsettings *NetSettings + idkey privkey + peerdb lncore.LitPeerStorage + ebus *eventbus.EventBus + mproc MessageProcessor // Peer tracking. peers []lncore.LnAddr // compatibility @@ -66,7 +67,7 @@ type NetSettings struct { } // NewPeerManager creates a peer manager from a root key -func NewPeerManager(rootkey *hdkeychain.ExtendedKey, pdb lncore.LitPeerStorage, trackerURL string, bus *eventbus.EventBus, ns *NetSettings) (*PeerManager, error) { +func NewPeerManager(rootkey *hdkeychain.ExtendedKey, pdb lncore.LitPeerStorage, trackerURL string, bus *eventbus.EventBus) (*PeerManager, error) { k, err := computeIdentKeyFromRoot(rootkey) if err != nil { return nil, err @@ -77,7 +78,6 @@ func NewPeerManager(rootkey *hdkeychain.ExtendedKey, pdb lncore.LitPeerStorage, peerdb: pdb, ebus: bus, mproc: NewMessageProcessor(), - netsettings: ns, peers: make([]lncore.LnAddr, MaxNodeCount), peerMap: map[lncore.LnAddr]*Peer{}, listeningPorts: map[int]*listeningthread{}, @@ -103,6 +103,11 @@ func (pm *PeerManager) GetExternalAddress() string { return string(addr) } +func (pm *PeerManager) GetExternalPubkeyString() string { + c := koblitz.PublicKey(ecdsa.PublicKey(pm.idkey.PublicKey)) + return hex.EncodeToString(c.SerializeCompressed()) +} + func computeIdentKeyFromRoot(rootkey *hdkeychain.ExtendedKey) (privkey, error) { var kg portxo.KeyGen kg.Depth = 5 @@ -129,6 +134,7 @@ func (pm *PeerManager) GetPeerIdx(peer *Peer) uint32 { // GetPeer returns the peer with the given lnaddr. func (pm *PeerManager) GetPeer(lnaddr lncore.LnAddr) *Peer { p, ok := pm.peerMap[lnaddr] + logging.Errorf("%v -> %v (%t)\n", lnaddr, p, ok) if !ok { return nil } @@ -144,93 +150,110 @@ func (pm *PeerManager) GetPeerByIdx(id int32) *Peer { } // TryConnectAddress attempts to connect to the specified LN address. -func (pm *PeerManager) TryConnectAddress(addr string) (*Peer, error) { +func (pm *PeerManager) TryConnectAddress(addr string, settings *NetSettings) error { + // the address we're dialing can either be of the following two types: + // 1. pkhash@ip:port + // 2. pk@ip:port (where pk is in hex and is a compressed public key) // Figure out who we're trying to connect to. - who, where := splitAdrString(addr) + pkOrpkHash, where := splitAdrString(addr) if where == "" { ipv4, _, err := lnutil.Lookup(addr, pm.trackerURL, "") if err != nil { - return nil, err + return err } where = fmt.Sprintf("%s:2448", ipv4) } + who := lncore.LnAddr(pkOrpkHash) + return pm.tryConnectPeer(&who, where, settings) +} - lnwho, err := lncore.ParseLnAddr(who) - if err != nil { - return nil, err - } +// tryConnectPeer tries to dial to the passed addr at where along with the passed +// settings. Returns an error +func (pm *PeerManager) tryConnectPeer(addr *lncore.LnAddr, where string, settings *NetSettings) (error) { - x, y := pm.tryConnectPeer(where, &lnwho) - return x, y + // lnaddr check, to make sure that we do the right thing. + if addr == nil { + return fmt.Errorf("connection to a peer with unknown addr not supported yet") + } -} + // Do NAT setup stuff. + if settings != nil && settings.NatMode != nil { -func (pm *PeerManager) tryConnectPeer(netaddr string, lnaddr *lncore.LnAddr) (*Peer, error) { + // Do some type juggling. + x, err := strconv.Atoi(where[1:]) + if err != nil { + return err + } + lisPort := uint16(x) // if only Atoi could infer which type we wanted to parse as! - // lnaddr check, to make sure that we do the right thing. - if lnaddr == nil { - return nil, fmt.Errorf("connection to a peer with unknown lnaddr not supported yet") + // Actually figure out what we're going to do. + if *settings.NatMode == "upnp" { + // Universal Plug-n-Play + logging.Infof("Attempting port forwarding via UPnP...") + err = nat.SetupUpnp(lisPort) + if err != nil { + return err + } + } else if *settings.NatMode == "pmp" { + // NAT Port Mapping Protocol + timeout := time.Duration(10 * time.Second) + logging.Infof("Attempting port forwarding via PMP...") + _, err = nat.SetupPmp(timeout, lisPort) + if err != nil { + return err + } + } else { + return fmt.Errorf("invalid NAT type: %s", *settings.NatMode) + } } dialer := net.Dial // Use a proxy server if applicable. - ns := pm.netsettings - if ns != nil && ns.ProxyAddr != nil { - d, err := connectToProxyTCP(*ns.ProxyAddr, ns.ProxyAuth) + if settings != nil && settings.ProxyAddr != nil { + d, err := connectToProxyTCP(*settings.ProxyAddr, settings.ProxyAuth) if err != nil { - return nil, err + return err } dialer = d } - // Create the connection. - lndcconn, err := lndc.Dial(pm.idkey, netaddr, string(*lnaddr), dialer) - if err != nil { - return nil, err + var remotePK *string + var lndcconn *lndc.Conn + x, err := pm.peerdb.GetPeerInfo(*addr) + if x != nil { + if *(x.LnAddr) == *addr { + // we have some entry in the db, we can use noise_xk + remotePK = x.Pubkey + // Set up the connection. + lndcconn, err = lndc.Dial(pm.idkey, where, *remotePK, dialer) + if err != nil { + return err + } + } + } else { + // Set up the connection. + lndcconn, err = lndc.Dial(pm.idkey, where, string(*addr), dialer) + if err != nil { + return err + } } - // Try to set up the new connection. - p, err := pm.handleNewConnection(lndcconn, *lnaddr) + pi, err := pm.peerdb.GetPeerInfo(*addr) if err != nil { - return nil, err + logging.Errorf("Problem loading peer info from DB: %s\n", err.Error()) + // don't kill the connection? } - // Now start listening for inbound traffic. - // (it *also* took me a while to realize I forgot *this*) - go processConnectionInboundTraffic(p, pm) - - // Return - return p, nil - -} - -func (pm *PeerManager) handleNewConnection(conn *lndc.Conn, expectedAddr lncore.LnAddr) (*Peer, error) { - // Now that we've got the connection, actually create the peer object. - pk := pubkey(conn.RemotePub()) + pk := pubkey(lndcconn.RemotePub()) rlitaddr := convertPubkeyToLitAddr(pk) - - if rlitaddr != expectedAddr { - conn.Close() - return nil, fmt.Errorf("peermgr: Connection init error, expected addr %s got addr %s", expectedAddr, rlitaddr) - } - p := &Peer{ lnaddr: rlitaddr, nickname: nil, - conn: conn, + conn: lndcconn, idpubkey: pk, - - // TEMP - idx: nil, - } - - pi, err := pm.peerdb.GetPeerInfo(expectedAddr) - if err != nil { - logging.Errorf("peermgr: Problem loading peer info from DB: %s\n", err.Error()) - // don't kill the connection? } if pi == nil { @@ -240,12 +263,18 @@ func (pm *PeerManager) handleNewConnection(conn *lndc.Conn, expectedAddr lncore. } else { p.idx = &pidx } - raddr := conn.RemoteAddr().String() + raddr := lndcconn.RemoteAddr().String() + // before we store the pubkey, we need to convert it to a hex encoded string + convertedPubKey := (*koblitz.PublicKey)(pk) + pStore := convertedPubKey.SerializeCompressed() // now we have a byte string + pStore2 := hex.EncodeToString(pStore) + logging.Infof("SOTREING PK OF REMOTE PEER", pStore2) pi = &lncore.PeerInfo{ LnAddr: &rlitaddr, Nickname: nil, NetAddr: &raddr, PeerIdx: pidx, + Pubkey: &pStore2, } err = pm.peerdb.AddPeer(p.GetLnAddr(), *pi) if err != nil { @@ -261,8 +290,12 @@ func (pm *PeerManager) handleNewConnection(conn *lndc.Conn, expectedAddr lncore. // (it took me a while to realize I forgot this) pm.registerPeer(p) - // Now actually return the peer. - return p, nil + // Now start listening for inbound traffic. + // (it *also* took me a while to realize I forgot *this*) + go processConnectionInboundTraffic(p, pm) + + // Return + return nil } @@ -335,34 +368,6 @@ func (pm *PeerManager) DisconnectPeer(peer *Peer) error { // ListenOnPort attempts to start a goroutine lisening on the port. func (pm *PeerManager) ListenOnPort(port int) error { - // Do NAT setup stuff. - ns := pm.netsettings - if ns != nil && ns.NatMode != nil { - - // Do some type juggling. - lisPort := uint16(port) - - // Actually figure out what we're going to do. - if *ns.NatMode == "upnp" { - // Universal Plug-n-Play - logging.Infof("Attempting port forwarding via UPnP...") - err := nat.SetupUpnp(lisPort) - if err != nil { - return err - } - } else if *ns.NatMode == "pmp" { - // NAT Port Mapping Protocol - timeout := time.Duration(10 * time.Second) - logging.Infof("Attempting port forwarding via PMP...") - _, err := nat.SetupPmp(timeout, lisPort) - if err != nil { - return err - } - } else { - return fmt.Errorf("invalid NAT type: %s", *ns.NatMode) - } - } - threadobj := &listeningthread{ listener: nil, } @@ -377,8 +382,9 @@ func (pm *PeerManager) ListenOnPort(port int) error { return fmt.Errorf("listen cancelled by event handler") } + // TODO UPnP and PMP NAT traversal. + // Try to start listening. - // TODO Listen on proxy if possible? logging.Info("PORT: ", port) listener, err := lndc.NewListener(pm.idkey, port) if err != nil { diff --git a/lnp2p/util.go b/lnp2p/util.go index 0af8df4df..49e09f30c 100644 --- a/lnp2p/util.go +++ b/lnp2p/util.go @@ -32,15 +32,7 @@ func splitAdrString(adr string) (string, string) { func convertPubkeyToLitAddr(pk pubkey) lncore.LnAddr { b := (*koblitz.PublicKey)(pk).SerializeCompressed() doubleSha := fastsha256.Sum256(b[:]) - - lnaddr, e := lncore.ParseLnAddr(bech32.Encode("ln", doubleSha[:20])) - - // Should never happen. - if e != nil { - panic("this should never happen, lit addr gen code has a bug: " + e.Error()) - } - - return lnaddr + return lncore.LnAddr(bech32.Encode("ln", doubleSha[:20])) } func connectToProxyTCP(addr string, auth *string) (func(string, string) (net.Conn, error), error) { diff --git a/qln/init.go b/qln/init.go index fe9cee9f0..a730dc78d 100644 --- a/qln/init.go +++ b/qln/init.go @@ -60,7 +60,7 @@ func NewLitNode(privKey *[32]byte, path string, trackerURL string, proxyURL stri nd.Events = &ebus // Peer manager - nd.PeerMan, err = lnp2p.NewPeerManager(rootPrivKey, nd.NewLitDB.GetPeerDB(), trackerURL, &ebus, nil) // TODO proxy/nat stuff + nd.PeerMan, err = lnp2p.NewPeerManager(rootPrivKey, nd.NewLitDB.GetPeerDB(), trackerURL, &ebus) if err != nil { return nil, err } diff --git a/qln/netio.go b/qln/netio.go index 39a021ae4..b0a1a7d2f 100644 --- a/qln/netio.go +++ b/qln/netio.go @@ -2,11 +2,12 @@ package qln import ( "fmt" + "strings" + "github.com/mit-dci/lit/crypto/koblitz" "github.com/mit-dci/lit/lncore" "github.com/mit-dci/lit/lnutil" "github.com/mit-dci/lit/logging" - "strings" ) // GetLisAddressAndPorts . @@ -20,12 +21,7 @@ func (nd *LitNode) GetLisAddressAndPorts() (string, []string) { // TODO Remove this function. func (nd *LitNode) FindPeerIndexByAddress(lnAdr string) (uint32, error) { pm := nd.PeerMan - lnaddr, err := lncore.ParseLnAddr(lnAdr) - if err != nil { - return 0, err - } - - p := pm.GetPeer(lnaddr) + p := pm.GetPeer(lncore.LnAddr(lnAdr)) if p != nil { return p.GetIdx(), nil } @@ -45,6 +41,7 @@ func (nd *LitNode) TCPListener(port int) (string, error) { lnaddr := nd.PeerMan.GetExternalAddress() + logging.Infof("My raw hex Public Key is: %s", nd.PeerMan.GetExternalPubkeyString()) logging.Infof("Listening with ln address: %s \n", lnaddr) // Don't announce on the tracker if we are communicating via SOCKS proxy @@ -85,13 +82,11 @@ func splitAdrString(adr string) (string, string) { // TODO Remove this. func (nd *LitNode) DialPeer(connectAdr string) error { - _, err := nd.PeerMan.TryConnectAddress(connectAdr) + err := nd.PeerMan.TryConnectAddress(connectAdr, nil) if err != nil { return err } - // TEMP The event handler handles actually setting up the peer in the LitNode - return nil } diff --git a/qln/remotecontrol.go b/qln/remotecontrol.go index fa9bfffb2..b754bda9a 100644 --- a/qln/remotecontrol.go +++ b/qln/remotecontrol.go @@ -84,6 +84,11 @@ func (nd *LitNode) RemoteControlRequestHandler(msg lnutil.RemoteControlRpcReques // If i'm not authorized, and it's not a whitelisted method then we fail the // request with an 'unauthorized' error + // while this is good, if you're the first person to connect to lit + // using the --con option, you're out of luck. + // Because nobody has authenticated you and neither can you authenticate yourself. + // In order to solve this, we need to pass a cli option to lit which can allow for + // authentication before the first peer logs in. if !auth.Allowed && !whitelisted { err = fmt.Errorf("Received remote control request from unauthorized peer: %x", pubKey) logging.Errorf(err.Error()) @@ -259,7 +264,7 @@ func (nd *LitNode) SaveRemoteControlAuthorization(pub [33]byte, auth *RemoteCont }) } -// GetRemoteControlAuthorization retrieves the remote controlauthorizzation for +// GetRemoteControlAuthorization retrieves the remote control authorization for // a specific pubkey from the database. func (nd *LitNode) GetRemoteControlAuthorization(pub [33]byte) (*RemoteControlAuthorization, error) { r := new(RemoteControlAuthorization) @@ -267,6 +272,7 @@ func (nd *LitNode) GetRemoteControlAuthorization(pub [33]byte) (*RemoteControlAu // If the client uses our default remote control key (derived from our root priv) // then it has access to our private key (file) and is most likely running from our // localhost. So we always accept this. + logging.Debug("Fetching remote control authorization for pubkey: ", pub[:], nd.DefaultRemoteControlKey.SerializeCompressed()) if bytes.Equal(pub[:], nd.DefaultRemoteControlKey.SerializeCompressed()) { r.Allowed = true return r, nil @@ -278,6 +284,7 @@ func (nd *LitNode) GetRemoteControlAuthorization(pub [33]byte) (*RemoteControlAu // serialize state b := cbk.Get(pub[:]) r = RemoteControlAuthorizationFromBytes(b, pub) + logging.Debug("Fetched pubkey ", r," from the database") return nil }) return r, err diff --git a/uspv/header.go b/uspv/header.go index 8861cb079..1e9e0345e 100644 --- a/uspv/header.go +++ b/uspv/header.go @@ -312,6 +312,10 @@ func CheckHeaderChain( // reorg is go, snip to attach height reorgDepth := height - attachHeight + if reorgDepth > numheaders { + logging.Info("Reorg depth is greater than the number of headers received, exiting!") + return 0, fmt.Errorf("Reorg depth is greater than the number of headers received, exiting!") + } oldHeaders = oldHeaders[:numheaders-reorgDepth] } From 89e3025437510310fd0566fdc969edb514b91f2f Mon Sep 17 00:00:00 2001 From: Varunram Date: Wed, 5 Dec 2018 09:35:33 -0500 Subject: [PATCH 2/3] remove comments --- cmd/lit-af/lit-af.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/cmd/lit-af/lit-af.go b/cmd/lit-af/lit-af.go index b5468c45d..d4d46a755 100644 --- a/cmd/lit-af/lit-af.go +++ b/cmd/lit-af/lit-af.go @@ -92,16 +92,11 @@ func (lc *litAfClient) litAfSetup(conf litAfConfig) error { // we don't know whether the passed address is a remotePKH or a remotePK // so we need to detect that here and then take steps accordingly - // so the first part involved here would be either dealing with raw pubkeys or - // dealing with pk hashes - // another question here is how do we deal with connecting to other nodes? - // if we need that in, we need to hcange our overall architecture to host - // pks as well as pk hashes adr, host, port := lnutil.ParseAdrStringWithPort(conf.Con) // now we've split the address, check if pkh, if not, route straight to noise_xk if len(adr) == 0 { - // so the user didn't provide us with an address to connect to + // so the user didn't provide us with an address to connect to and // we need to connect to the locally running lit-af instance lc.RPCClient, err = litrpc.NewLocalLndcRpcClientWithHomeDirAndPort(defaultDir, port) if err != nil { From c618cee8e440c09385001d812f9bc8bafed1abaf Mon Sep 17 00:00:00 2001 From: Varunram Date: Wed, 5 Dec 2018 10:21:01 -0500 Subject: [PATCH 3/3] make tests work --- lndc/noise.go | 17 +++++++++-------- lndc/noise_test.go | 18 +++++++++--------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/lndc/noise.go b/lndc/noise.go index 181c0f352..652b29e44 100644 --- a/lndc/noise.go +++ b/lndc/noise.go @@ -304,10 +304,10 @@ func newXKHandshakeState(initiator bool, prologue []byte, // EphemeralGenerator is a functional option that allows callers to substitute // a custom function for use when generating ephemeral keys for ActOne or // ActTwo. The function closure return by this function can be passed into -// NewNoiseMachine as a function option parameter. +// NewNoiseMachine(XK or XX) as a function option parameter. func EphemeralGenerator(gen func() (*koblitz.PrivateKey, error)) func(*Machine) { return func(m *Machine) { - m.ephemeralGen = gen + m.EphemeralGen = gen } } @@ -353,7 +353,8 @@ type Machine struct { sendCipher cipherState recvCipher cipherState - ephemeralGen func() (*koblitz.PrivateKey, error) + EphemeralGen func() (*koblitz.PrivateKey, error) + // this is exported in order for noise_test to define a static ephemeral pubkey handshakeState @@ -372,7 +373,7 @@ type Machine struct { nextCipherText [math.MaxUint16 + macSize]byte } -// NewNoiseMachine creates a new instance of the lndc state-machine. If +// NewNoiseXKMachine creates a new instance of the lndc state-machine. If // the responder (listener) is creating the object, then the remotePub should // be nil. The handshake state within lndc is initialized using the ascii // string "lightning" as the prologue. The last parameter is a set of variadic @@ -387,7 +388,7 @@ func NewNoiseXKMachine(initiator bool, localStatic *koblitz.PrivateKey, m := &Machine{handshakeState: handshake} // With the initial base machine created, we'll assign our default // version of the ephemeral key generator. - m.ephemeralGen = func() (*koblitz.PrivateKey, error) { + m.EphemeralGen = func() (*koblitz.PrivateKey, error) { return koblitz.NewPrivateKey(koblitz.S256()) } @@ -402,7 +403,7 @@ func NewNoiseXXMachine(initiator bool, localStatic *koblitz.PrivateKey) *Machine // With the initial base machine created, we'll assign our default // version of the ephemeral key generator. - m.ephemeralGen = func() (*koblitz.PrivateKey, error) { + m.EphemeralGen = func() (*koblitz.PrivateKey, error) { return koblitz.NewPrivateKey(koblitz.S256()) } @@ -475,7 +476,7 @@ func (b *Machine) GenActOne(remotePK [33]byte) ([]byte, error) { var err error actOne := make([]byte, ActOneSize) // Generate e - b.localEphemeral, err = b.ephemeralGen() + b.localEphemeral, err = b.EphemeralGen() if err != nil { return actOne, err } @@ -544,7 +545,7 @@ func (b *Machine) GenActTwo(HandshakeVersion byte) ([]byte, error) { var err error actTwo := make([]byte, ActTwoSize) // e - b.localEphemeral, err = b.ephemeralGen() + b.localEphemeral, err = b.EphemeralGen() if err != nil { return actTwo, err } diff --git a/lndc/noise_test.go b/lndc/noise_test.go index 724955cfe..ec8479a0e 100644 --- a/lndc/noise_test.go +++ b/lndc/noise_test.go @@ -310,7 +310,7 @@ func TestBolt0008TestVectors(t *testing.T) { // EphemeralGenerator function for the state machine to ensure that the // initiator and responder both generate the ephemeral public key // defined within the test vectors. - initiatorEphemeral := EphemeralGenerator(func() (*koblitz.PrivateKey, error) { + initiatorEphemeral := func() (*koblitz.PrivateKey, error) { e := "121212121212121212121212121212121212121212121212121212" + "1212121212" eBytes, err := hex.DecodeString(e) @@ -320,8 +320,8 @@ func TestBolt0008TestVectors(t *testing.T) { priv, _ := koblitz.PrivKeyFromBytes(koblitz.S256(), eBytes) return priv, nil - }) - responderEphemeral := EphemeralGenerator(func() (*koblitz.PrivateKey, error) { + } + responderEphemeral := func() (*koblitz.PrivateKey, error) { e := "222222222222222222222222222222222222222222222222222" + "2222222222222" eBytes, err := hex.DecodeString(e) @@ -331,12 +331,14 @@ func TestBolt0008TestVectors(t *testing.T) { priv, _ := koblitz.PrivKeyFromBytes(koblitz.S256(), eBytes) return priv, nil - }) + } // Finally, we'll create both brontide state machines, so we can begin // our test. - initiator := NewNoiseMachine(true, initiatorPriv, initiatorEphemeral) - responder := NewNoiseMachine(false, responderPriv, responderEphemeral) + initiator := NewNoiseXXMachine(true, initiatorPriv) + initiator.EphemeralGen = initiatorEphemeral // needed for locking the ephemeral key + responder := NewNoiseXXMachine(false, responderPriv) + responder.EphemeralGen = responderEphemeral // needed for locking the ephemeral key // We'll start with the initiator generating the initial payload for // act one. This should consist of exactly 50 bytes. We'll assert that @@ -347,9 +349,7 @@ func TestBolt0008TestVectors(t *testing.T) { if err != nil { t.Fatalf("unable to generate act one: %v", err) } - expectedActOne, err := hex.DecodeString("01036360e856310ce5d294e" + - "8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f71432d5611e91" + - "ffea67c17e8d5ae0cbb3") + expectedActOne, err := hex.DecodeString("01036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f71432d5611e91ffea67c17e8d5ae0cbb3") if err != nil { t.Fatalf("unable to parse expected act one: %v", err) }