From 0f81038eb037a489cfe4999bc4fea9a1c0bb6f23 Mon Sep 17 00:00:00 2001 From: Varunram Date: Wed, 21 Nov 2018 23:26:37 +0530 Subject: [PATCH 01/10] rebase 307 upon current master --- litrpc/netcmds.go | 1 - lndc/conn.go | 44 +++++++++++---- lndc/listener.go | 14 +++-- lndc/noise.go | 132 ++++++++++++++++++++++++++++++--------------- lndc/noise_test.go | 5 +- qln/init.go | 13 +++++ qln/lndb.go | 1 + qln/netio.go | 42 ++++++++++++--- 8 files changed, 184 insertions(+), 68 deletions(-) diff --git a/litrpc/netcmds.go b/litrpc/netcmds.go index 6e760dba4..9aaf67cd4 100644 --- a/litrpc/netcmds.go +++ b/litrpc/netcmds.go @@ -82,7 +82,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 diff --git a/lndc/conn.go b/lndc/conn.go index 3e0ccd514..067116756 100644 --- a/lndc/conn.go +++ b/lndc/conn.go @@ -29,15 +29,30 @@ 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) == 33 { // remotePK + temp := []byte(remoteAddress) + copy(remotePK[:], temp) + } var conn net.Conn var err error + var empty [33]byte + if remotePK != empty { + logging.Info("Connecting via Noise_XK since we know remotePK") + Noise_XK = true + SetConsts() + } conn, err = dialer("tcp", ipAddr) logging.Info("ipAddr is", ipAddr) if err != nil { @@ -50,7 +65,7 @@ func Dial(localPriv *koblitz.PrivateKey, ipAddr string, remotePKH string, } // 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 @@ -69,22 +84,29 @@ 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..58c37474a 100644 --- a/lndc/listener.go +++ b/lndc/listener.go @@ -8,6 +8,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 @@ -115,12 +116,19 @@ 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) + logging.Info("Handshake Version", HandshakeVersion) 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 + HandshakeVersion = byte(0) + ActTwoSize = 50 + } // no need for else as default covers XX + if err := lndcConn.noise.RecvActOne(actOne); err != nil { lndcConn.conn.Close() l.rejectConn(err) @@ -128,7 +136,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 +162,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..e89122a60 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" @@ -316,10 +316,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 @@ -373,11 +382,11 @@ func NewNoiseMachine(initiator bool, localStatic *koblitz.PrivateKey, return m } -const ( +var ( // 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,18 +412,25 @@ const ( ActThreeSize = 66 ) +func SetConsts() { + HandshakeVersion = byte(0) // Noise_XK's hadnshake version + // 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 +} + // 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) { + var err error + actOne := make([]byte, ActOneSize) // Generate e b.localEphemeral, err = b.ephemeralGen() if err != nil { @@ -426,6 +442,16 @@ func (b *Machine) GenActOne() ([ActOneSize]byte, error) { // Hash it into the handshake digest b.mixHash(e) + if Noise_XK { + b.remoteStatic, err = koblitz.ParsePubKey(remotePK[:], koblitz.S256()) + if err != nil { + return nil, err + } + // es + s := ecdh(b.remoteStatic, b.localEphemeral) + b.mixKey(s[:]) + } + authPayload := b.EncryptAndHash([]byte{}) actOne[0] = HandshakeVersion copy(actOne[1:34], e) @@ -437,7 +463,7 @@ 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 { var ( err error e [33]byte @@ -446,7 +472,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[:]) @@ -462,6 +488,12 @@ 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 +501,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 +517,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 +546,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 +562,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 +582,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 +604,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 +631,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/qln/init.go b/qln/init.go index a730dc78d..4cb60ef91 100644 --- a/qln/init.go +++ b/qln/init.go @@ -136,6 +136,19 @@ func NewLitNode(privKey *[32]byte, path string, trackerURL string, proxyURL stri nd.PeerMap = map[*lnp2p.Peer]*RemotePeer{} nd.PeerMapMtx = &sync.Mutex{} + nd.KnownPubkeys = make(map[uint32][33]byte) + var empty [33]byte + i := uint32(0) + for { + pubKey, _ := nd.GetPubHostFromPeerIdx(4) + if pubKey == empty { + logging.Infof("Done, tried %d hosts, none matched\n", i+1) + break + } + nd.KnownPubkeys[i] = pubKey + i++ + } + return nd, nil } diff --git a/qln/lndb.go b/qln/lndb.go index 06a0ee11b..abd4cd85c 100644 --- a/qln/lndb.go +++ b/qln/lndb.go @@ -131,6 +131,7 @@ type LitNode struct { TrackerURL string ChannelMap map[[20]byte][]LinkDesc + KnownPubkeys map[uint32][33]byte ChannelMapMtx sync.Mutex AdvTimeout *time.Ticker diff --git a/qln/netio.go b/qln/netio.go index 36b89c662..77dab47d6 100644 --- a/qln/netio.go +++ b/qln/netio.go @@ -2,11 +2,14 @@ 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" + "github.com/mit-dci/lit/bech32" + "github.com/mit-dci/lit/crypto/fastsha256" ) // GetLisAddressAndPorts . @@ -80,13 +83,38 @@ func splitAdrString(adr string) (string, string) { // TODO Remove this. func (nd *LitNode) DialPeer(connectAdr string) error { - _, err := nd.PeerMan.TryConnectAddress(connectAdr, nil) - if err != nil { - return err + who, _ := splitAdrString(connectAdr) + logging.Info("COOLPEER!", who, nd.KnownPubkeys) + + var remotePK [33]byte + var noisexk bool + for _, pubkey := range nd.KnownPubkeys { + logging.Debug("Found pubkey in list of known pubkeys: ", pubkey) + idHash := fastsha256.Sum256(pubkey[:]) + adr := bech32.Encode("ln", idHash[:20]) + if adr == who { + remotePK = pubkey + noisexk = true + } + } + // get my private ID key + // idPriv := nd.IdKey() + // var newConn *lndc.Conn + // Assign remote connection + if noisexk { + var remotePKdup [33]byte + temp := []byte(string(remotePK[:])) + copy(remotePKdup[:], temp) + _, err := nd.PeerMan.TryConnectAddress(string(remotePK[:]), nil) + if err != nil { + return err + } + } else { + _, 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 } From 0540b370c5651a65099ce15dae6f614230588835 Mon Sep 17 00:00:00 2001 From: Varunram Date: Thu, 22 Nov 2018 00:36:52 +0530 Subject: [PATCH 02/10] use noise_xk to connect to already known peers --- lncore/peers.go | 1 + lndc/conn.go | 15 +++++++-------- lndc/listener.go | 2 +- lnp2p/peermgr.go | 31 ++++++++++++++++++++++++++----- qln/init.go | 13 ------------- qln/lndb.go | 1 - qln/netio.go | 37 ++++--------------------------------- 7 files changed, 39 insertions(+), 61 deletions(-) diff --git a/lncore/peers.go b/lncore/peers.go index e4930d662..6657557d6 100644 --- a/lncore/peers.go +++ b/lncore/peers.go @@ -22,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 067116756..1f128f2ca 100644 --- a/lndc/conn.go +++ b/lndc/conn.go @@ -2,6 +2,7 @@ package lndc import ( "bytes" + "encoding/hex" "fmt" "io" "math" @@ -41,18 +42,16 @@ func Dial(localPriv *koblitz.PrivateKey, ipAddr string, remoteAddress string, var remotePK [33]byte if remoteAddress[0:3] == "ln1" { // its a remote PKH remotePKH = remoteAddress - } else if len(remoteAddress) == 33 { // remotePK - temp := []byte(remoteAddress) + } else if len(remoteAddress) == 66 { // hex encoded remotePK + temp, _ := hex.DecodeString(remoteAddress) copy(remotePK[:], temp) - } - var conn net.Conn - var err error - var empty [33]byte - if remotePK != empty { - logging.Info("Connecting via Noise_XK since we know remotePK") + logging.Info("Got remote PK: ", remotePK, " using noise_xk to connect") Noise_XK = true SetConsts() } + var conn net.Conn + var err error + conn, err = dialer("tcp", ipAddr) logging.Info("ipAddr is", ipAddr) if err != nil { diff --git a/lndc/listener.go b/lndc/listener.go index 58c37474a..4574450bc 100644 --- a/lndc/listener.go +++ b/lndc/listener.go @@ -117,7 +117,6 @@ func (l *Listener) doHandshake(conn net.Conn) { // connecting node doesn't know our long-term static public key, then // this portion will fail with a non-nil error. actOne := make([]byte, ActOneSize) - logging.Info("Handshake Version", HandshakeVersion) if _, err := io.ReadFull(conn, actOne[:]); err != nil { lndcConn.conn.Close() l.rejectConn(err) @@ -127,6 +126,7 @@ func (l *Listener) doHandshake(conn net.Conn) { if actOne[0] == 0 { // remote node wants to connect via XK HandshakeVersion = byte(0) ActTwoSize = 50 + logging.Infof("remote node wants to connect via noise_xk") } // no need for else as default covers XX if err := lndcConn.noise.RecvActOne(actOne); err != nil { diff --git a/lnp2p/peermgr.go b/lnp2p/peermgr.go index b9c54774f..88748ed71 100644 --- a/lnp2p/peermgr.go +++ b/lnp2p/peermgr.go @@ -3,6 +3,7 @@ package lnp2p //"crypto/ecdsa" // TODO Use ecdsa not koblitz import ( "crypto/ecdsa" + "encoding/hex" "fmt" "net" "strconv" @@ -145,7 +146,6 @@ func (pm *PeerManager) GetPeerByIdx(id int32) *Peer { // TryConnectAddress attempts to connect to the specified LN address. func (pm *PeerManager) TryConnectAddress(addr string, settings *NetSettings) (*Peer, error) { - // Figure out who we're trying to connect to. who, where := splitAdrString(addr) if where == "" { @@ -211,10 +211,25 @@ func (pm *PeerManager) tryConnectPeer(netaddr string, lnaddr *lncore.LnAddr, set dialer = d } - // Set up 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(*lnaddr) + if x != nil { + if *(x.LnAddr) == *lnaddr { + // 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, netaddr, *remotePK, dialer) + if err != nil { + return nil, err + } + } + } else { + // Set up the connection. + lndcconn, err = lndc.Dial(pm.idkey, netaddr, string(*lnaddr), dialer) + if err != nil { + return nil, err + } } pi, err := pm.peerdb.GetPeerInfo(*lnaddr) @@ -241,11 +256,17 @@ func (pm *PeerManager) tryConnectPeer(netaddr string, lnaddr *lncore.LnAddr, set p.idx = &pidx } 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 { diff --git a/qln/init.go b/qln/init.go index 4cb60ef91..a730dc78d 100644 --- a/qln/init.go +++ b/qln/init.go @@ -136,19 +136,6 @@ func NewLitNode(privKey *[32]byte, path string, trackerURL string, proxyURL stri nd.PeerMap = map[*lnp2p.Peer]*RemotePeer{} nd.PeerMapMtx = &sync.Mutex{} - nd.KnownPubkeys = make(map[uint32][33]byte) - var empty [33]byte - i := uint32(0) - for { - pubKey, _ := nd.GetPubHostFromPeerIdx(4) - if pubKey == empty { - logging.Infof("Done, tried %d hosts, none matched\n", i+1) - break - } - nd.KnownPubkeys[i] = pubKey - i++ - } - return nd, nil } diff --git a/qln/lndb.go b/qln/lndb.go index abd4cd85c..06a0ee11b 100644 --- a/qln/lndb.go +++ b/qln/lndb.go @@ -131,7 +131,6 @@ type LitNode struct { TrackerURL string ChannelMap map[[20]byte][]LinkDesc - KnownPubkeys map[uint32][33]byte ChannelMapMtx sync.Mutex AdvTimeout *time.Ticker diff --git a/qln/netio.go b/qln/netio.go index 77dab47d6..9bc806a7a 100644 --- a/qln/netio.go +++ b/qln/netio.go @@ -8,8 +8,6 @@ import ( "github.com/mit-dci/lit/lncore" "github.com/mit-dci/lit/lnutil" "github.com/mit-dci/lit/logging" - "github.com/mit-dci/lit/bech32" - "github.com/mit-dci/lit/crypto/fastsha256" ) // GetLisAddressAndPorts . @@ -83,38 +81,11 @@ func splitAdrString(adr string) (string, string) { // TODO Remove this. func (nd *LitNode) DialPeer(connectAdr string) error { - who, _ := splitAdrString(connectAdr) - logging.Info("COOLPEER!", who, nd.KnownPubkeys) - - var remotePK [33]byte - var noisexk bool - for _, pubkey := range nd.KnownPubkeys { - logging.Debug("Found pubkey in list of known pubkeys: ", pubkey) - idHash := fastsha256.Sum256(pubkey[:]) - adr := bech32.Encode("ln", idHash[:20]) - if adr == who { - remotePK = pubkey - noisexk = true - } - } - // get my private ID key - // idPriv := nd.IdKey() - // var newConn *lndc.Conn - // Assign remote connection - if noisexk { - var remotePKdup [33]byte - temp := []byte(string(remotePK[:])) - copy(remotePKdup[:], temp) - _, err := nd.PeerMan.TryConnectAddress(string(remotePK[:]), nil) - if err != nil { - return err - } - } else { - _, err := nd.PeerMan.TryConnectAddress(connectAdr, nil) - if err != nil { - return err - } + _, err := nd.PeerMan.TryConnectAddress(connectAdr, nil) + if err != nil { + return err } + return nil } From a11f88a2362dd90715bd0a1e2692de4ca53c989a Mon Sep 17 00:00:00 2001 From: Varunram Date: Wed, 21 Nov 2018 16:15:15 +0530 Subject: [PATCH 03/10] allow for whitelisting of single address from the lit cli if you're the first person connecting to a node using --con, there is nobody who could have authenticated you, except via a local lit-af instance. This prevents two lit-af instances running on the same machine, as well as causing some sort of problem for people running multiple instances of lit on a single mahine --- cmd/lit-af/lit-af.go | 7 ++++++- lit.go | 35 +++++++++++++++++++++++++++++------ litrpc/netcmds.go | 3 +++ lnp2p/peermgr.go | 9 +++++---- qln/netio.go | 2 +- qln/remotecontrol.go | 9 ++++++++- 6 files changed, 52 insertions(+), 13 deletions(-) diff --git a/cmd/lit-af/lit-af.go b/cmd/lit-af/lit-af.go index f79acb555..5beb4de00 100644 --- a/cmd/lit-af/lit-af.go +++ b/cmd/lit-af/lit-af.go @@ -5,6 +5,7 @@ import ( "os" "path/filepath" "strings" + "encoding/hex" "github.com/chzyer/readline" "github.com/fatih/color" @@ -108,7 +109,11 @@ func (lc *litAfClient) litAfSetup(conf litAfConfig) { logging.Fatal(err.Error()) } key, _ := koblitz.PrivKeyFromBytes(koblitz.S256(), privKey[:]) - + pubkey := key.PubKey().SerializeCompressed() // this is in bytes + fmt.Println("The pubkey of this lit-af instance is:", hex.EncodeToString(pubkey)) + var temp [33]byte + copy(temp[:], pubkey[33:]) + fmt.Println("The pkh of this lit-af instance is:", lnutil.LitAdrFromPubkey(temp)) if adr != "" && strings.HasPrefix(adr, "ln1") && host == "" { ipv4, _, err := lnutil.Lookup(adr, conf.Tracker, "") if err != nil { 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 9aaf67cd4..0584955e3 100644 --- a/litrpc/netcmds.go +++ b/litrpc/netcmds.go @@ -66,6 +66,9 @@ 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 + fmt.Println("PASSED STUFF:", args.LNAddr) peerIdxint, err := strconv.Atoi(args.LNAddr) // number will mean no error if err == nil { diff --git a/lnp2p/peermgr.go b/lnp2p/peermgr.go index 88748ed71..4f5a7b53f 100644 --- a/lnp2p/peermgr.go +++ b/lnp2p/peermgr.go @@ -145,20 +145,21 @@ func (pm *PeerManager) GetPeerByIdx(id int32) *Peer { } // TryConnectAddress attempts to connect to the specified LN address. -func (pm *PeerManager) TryConnectAddress(addr string, settings *NetSettings) (*Peer, error) { +func (pm *PeerManager) TryConnectAddress(addr string, settings *NetSettings) (error) { + // Figure out who we're trying to connect to. who, 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) } lnwho := lncore.LnAddr(who) - x, y := pm.tryConnectPeer(where, &lnwho, settings) - return x, y + _, y := pm.tryConnectPeer(where, &lnwho, settings) + return y } diff --git a/qln/netio.go b/qln/netio.go index 9bc806a7a..1e33ef548 100644 --- a/qln/netio.go +++ b/qln/netio.go @@ -81,7 +81,7 @@ func splitAdrString(adr string) (string, string) { // TODO Remove this. func (nd *LitNode) DialPeer(connectAdr string) error { - _, err := nd.PeerMan.TryConnectAddress(connectAdr, nil) + err := nd.PeerMan.TryConnectAddress(connectAdr, nil) if err != nil { return err } 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 From bd19ba1cc66ad88525b297dfd6bdcf2e77492ca5 Mon Sep 17 00:00:00 2001 From: Varunram Date: Wed, 21 Nov 2018 18:38:36 +0530 Subject: [PATCH 04/10] connect to remote node using their pubkey as well bolt compatibility stuff --- litrpc/netcmds.go | 1 - lndc/conn.go | 7 +++++++ lnp2p/peermgr.go | 43 +++++++++++++++++++++++++------------------ qln/netio.go | 1 + uspv/header.go | 4 ++++ 5 files changed, 37 insertions(+), 19 deletions(-) diff --git a/litrpc/netcmds.go b/litrpc/netcmds.go index 0584955e3..cca98b71b 100644 --- a/litrpc/netcmds.go +++ b/litrpc/netcmds.go @@ -68,7 +68,6 @@ func (r *LitRPC) Connect(args ConnectArgs, reply *ConnectReply) error { // 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 - fmt.Println("PASSED STUFF:", args.LNAddr) peerIdxint, err := strconv.Atoi(args.LNAddr) // number will mean no error if err == nil { diff --git a/lndc/conn.go b/lndc/conn.go index 1f128f2ca..a02ee4891 100644 --- a/lndc/conn.go +++ b/lndc/conn.go @@ -100,10 +100,17 @@ func Dial(localPriv *koblitz.PrivateKey, ipAddr string, remoteAddress string, return nil, err } } +<<<<<<< HEAD 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 +======= + + logging.Info("Received pubkey", s) + logging.Debug("Received pubkey: ", lnutil.LitAdrFromPubkey(s), " have pubkey: ", remotePKH) + if lnutil.LitAdrFromPubkey(s) != remotePKH { +>>>>>>> connect to remote node using their pubkey as well return nil, fmt.Errorf("Remote PKH doesn't match. Quitting!") } diff --git a/lnp2p/peermgr.go b/lnp2p/peermgr.go index 4f5a7b53f..ffa5686ae 100644 --- a/lnp2p/peermgr.go +++ b/lnp2p/peermgr.go @@ -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 @@ -145,10 +150,13 @@ func (pm *PeerManager) GetPeerByIdx(id int32) *Peer { } // TryConnectAddress attempts to connect to the specified LN address. -func (pm *PeerManager) TryConnectAddress(addr string, settings *NetSettings) (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 { @@ -156,27 +164,26 @@ func (pm *PeerManager) TryConnectAddress(addr string, settings *NetSettings) (er } where = fmt.Sprintf("%s:2448", ipv4) } - - lnwho := lncore.LnAddr(who) - _, y := pm.tryConnectPeer(where, &lnwho, settings) - return y - + who := lncore.LnAddr(pkOrpkHash) + return pm.tryConnectPeer(&who, where, settings) } -func (pm *PeerManager) tryConnectPeer(netaddr string, lnaddr *lncore.LnAddr, settings *NetSettings) (*Peer, error) { +// 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) { // 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") + 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 { // Do some type juggling. - x, err := strconv.Atoi(netaddr[1:]) + x, err := strconv.Atoi(where[1:]) if err != nil { - return nil, err + return err } lisPort := uint16(x) // if only Atoi could infer which type we wanted to parse as! @@ -186,7 +193,7 @@ func (pm *PeerManager) tryConnectPeer(netaddr string, lnaddr *lncore.LnAddr, set logging.Infof("Attempting port forwarding via UPnP...") err = nat.SetupUpnp(lisPort) if err != nil { - return nil, err + return err } } else if *settings.NatMode == "pmp" { // NAT Port Mapping Protocol @@ -194,10 +201,10 @@ func (pm *PeerManager) tryConnectPeer(netaddr string, lnaddr *lncore.LnAddr, set logging.Infof("Attempting port forwarding via PMP...") _, err = nat.SetupPmp(timeout, lisPort) if err != nil { - return nil, err + return err } } else { - return nil, fmt.Errorf("invalid NAT type: %s", *settings.NatMode) + return fmt.Errorf("invalid NAT type: %s", *settings.NatMode) } } @@ -207,7 +214,7 @@ func (pm *PeerManager) tryConnectPeer(netaddr string, lnaddr *lncore.LnAddr, set if settings != nil && settings.ProxyAddr != nil { d, err := connectToProxyTCP(*settings.ProxyAddr, settings.ProxyAuth) if err != nil { - return nil, err + return err } dialer = d } @@ -233,7 +240,7 @@ func (pm *PeerManager) tryConnectPeer(netaddr string, lnaddr *lncore.LnAddr, set } } - pi, err := pm.peerdb.GetPeerInfo(*lnaddr) + pi, err := pm.peerdb.GetPeerInfo(*addr) if err != nil { logging.Errorf("Problem loading peer info from DB: %s\n", err.Error()) // don't kill the connection? @@ -288,7 +295,7 @@ func (pm *PeerManager) tryConnectPeer(netaddr string, lnaddr *lncore.LnAddr, set go processConnectionInboundTraffic(p, pm) // Return - return p, nil + return nil } diff --git a/qln/netio.go b/qln/netio.go index 1e33ef548..b0a1a7d2f 100644 --- a/qln/netio.go +++ b/qln/netio.go @@ -41,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 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 a7396abd59d5dd93bed3a631c7af5bdf449d9603 Mon Sep 17 00:00:00 2001 From: Varunram Date: Wed, 21 Nov 2018 19:07:57 +0530 Subject: [PATCH 05/10] add pubkey connecting support for lit-af still need to take care of other stuff to make sure that pubkey is used uniformly inside lit --- cmd/lit-af/lit-af.go | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/cmd/lit-af/lit-af.go b/cmd/lit-af/lit-af.go index 5beb4de00..931cb90b9 100644 --- a/cmd/lit-af/lit-af.go +++ b/cmd/lit-af/lit-af.go @@ -66,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 @@ -90,8 +90,28 @@ 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) + // now e've split the address, check if pkh, if not, convert to pkh + if len(adr) == 44 { + // remote PKH, do nothing + } else if len(adr) == 66 { + // remote PK, convert to remotePKH + addr, err := hex.DecodeString(adr) + if err != nil { + return fmt.Errorf("Unable to decode hex string") + } + var temp [33]byte + copy(temp[:33], addr) + adr = lnutil.LitAdrFromPubkey(temp) + } + fmt.Printf("Adr: %s, Host: %s, Port: %d\n", adr, host, port) if litrpc.LndcRpcCanConnectLocallyWithHomeDir(defaultDir) && adr == "" && (host == "localhost" || host == "127.0.0.1") { lc.RPCClient, err = litrpc.NewLocalLndcRpcClientWithHomeDirAndPort(defaultDir, port) @@ -110,10 +130,10 @@ func (lc *litAfClient) litAfSetup(conf litAfConfig) { } key, _ := koblitz.PrivKeyFromBytes(koblitz.S256(), privKey[:]) pubkey := key.PubKey().SerializeCompressed() // this is in bytes - fmt.Println("The pubkey of this lit-af instance is:", hex.EncodeToString(pubkey)) + fmt.Printf("The pubkey of this lit-af instance is: %s\n", hex.EncodeToString(pubkey)) var temp [33]byte - copy(temp[:], pubkey[33:]) - fmt.Println("The pkh of this lit-af instance is:", lnutil.LitAdrFromPubkey(temp)) + copy(temp[:], pubkey[:33]) + fmt.Printf("The pkh of this lit-af instance is: %s\n", lnutil.LitAdrFromPubkey(temp)) if adr != "" && strings.HasPrefix(adr, "ln1") && host == "" { ipv4, _, err := lnutil.Lookup(adr, conf.Tracker, "") if err != nil { @@ -130,6 +150,7 @@ func (lc *litAfClient) litAfSetup(conf litAfConfig) { logging.Fatal(err.Error()) } } + return nil } // for now just testing how to connect and get messages back and forth @@ -142,7 +163,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("# "), From de92ae828140677b8674f83026ca746d5e4ba0a2 Mon Sep 17 00:00:00 2001 From: Varunram Date: Thu, 22 Nov 2018 18:48:33 +0530 Subject: [PATCH 06/10] supprot for pubkeys + noise_xk --- lndc/conn.go | 8 +------- lnp2p/peermgr.go | 12 ++++++------ 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/lndc/conn.go b/lndc/conn.go index a02ee4891..dd6b446c0 100644 --- a/lndc/conn.go +++ b/lndc/conn.go @@ -100,17 +100,11 @@ func Dial(localPriv *koblitz.PrivateKey, ipAddr string, remoteAddress string, return nil, err } } -<<<<<<< HEAD + 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 -======= - - logging.Info("Received pubkey", s) - logging.Debug("Received pubkey: ", lnutil.LitAdrFromPubkey(s), " have pubkey: ", remotePKH) - if lnutil.LitAdrFromPubkey(s) != remotePKH { ->>>>>>> connect to remote node using their pubkey as well return nil, fmt.Errorf("Remote PKH doesn't match. Quitting!") } diff --git a/lnp2p/peermgr.go b/lnp2p/peermgr.go index ffa5686ae..15e6230e0 100644 --- a/lnp2p/peermgr.go +++ b/lnp2p/peermgr.go @@ -221,22 +221,22 @@ func (pm *PeerManager) tryConnectPeer(addr *lncore.LnAddr, where string, setting var remotePK *string var lndcconn *lndc.Conn - x, err := pm.peerdb.GetPeerInfo(*lnaddr) + x, err := pm.peerdb.GetPeerInfo(*addr) if x != nil { - if *(x.LnAddr) == *lnaddr { + 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, netaddr, *remotePK, dialer) + lndcconn, err = lndc.Dial(pm.idkey, where, *remotePK, dialer) if err != nil { - return nil, err + return err } } } else { // Set up the connection. - lndcconn, err = lndc.Dial(pm.idkey, netaddr, string(*lnaddr), dialer) + lndcconn, err = lndc.Dial(pm.idkey, where, string(*addr), dialer) if err != nil { - return nil, err + return err } } From 91ab1ddc9d28c8c63b30a3f0c6b4b710f2d24819 Mon Sep 17 00:00:00 2001 From: Varunram Date: Thu, 22 Nov 2018 22:13:49 +0530 Subject: [PATCH 07/10] two way dial works --- cmd/lit-af/lit-af.go | 84 +++++++++++++++----------- litrpc/lndcrpcclient.go | 9 +++ lndc/conn.go | 26 ++++++-- lndc/listener.go | 2 +- lndc/noise.go | 130 ++++++++++++++++++++++++++++++++-------- 5 files changed, 185 insertions(+), 66 deletions(-) diff --git a/cmd/lit-af/lit-af.go b/cmd/lit-af/lit-af.go index 931cb90b9..c87216cf5 100644 --- a/cmd/lit-af/lit-af.go +++ b/cmd/lit-af/lit-af.go @@ -1,11 +1,11 @@ package main import ( + "encoding/hex" "fmt" "os" "path/filepath" "strings" - "encoding/hex" "github.com/chzyer/readline" "github.com/fatih/color" @@ -66,7 +66,7 @@ func newConfigParser(conf *litAfConfig, options flags.Options) *flags.Parser { return parser } -func (lc *litAfClient) litAfSetup(conf litAfConfig) (error){ +func (lc *litAfClient) litAfSetup(conf litAfConfig) error { var err error // create home directory if it does not exist @@ -98,31 +98,52 @@ func (lc *litAfClient) litAfSetup(conf litAfConfig) (error){ // 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 e've split the address, check if pkh, if not, convert to pkh + // now we've split the address, check if pkh, if not, route straight to noise_xk if len(adr) == 44 { - // remote PKH, do nothing - } else if len(adr) == 66 { - // remote PK, convert to remotePKH - addr, err := hex.DecodeString(adr) - if err != nil { - return fmt.Errorf("Unable to decode hex string") - } - var temp [33]byte - copy(temp[:33], addr) - adr = lnutil.LitAdrFromPubkey(temp) - } - fmt.Printf("Adr: %s, Host: %s, Port: %d\n", adr, host, port) - if litrpc.LndcRpcCanConnectLocallyWithHomeDir(defaultDir) && adr == "" && (host == "localhost" || host == "127.0.0.1") { + // remote PKH, do standard stuff + fmt.Printf("Adr: %s, Host: %s, Port: %d\n", adr, host, port) + if litrpc.LndcRpcCanConnectLocallyWithHomeDir(defaultDir) && adr == "" && (host == "localhost" || host == "127.0.0.1") { - 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") - } + 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") + } + 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 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) + } + + lc.RPCClient, err = litrpc.NewLndcRpcClient(adr, key) + if err != nil { + logging.Fatal(err.Error()) + } + } + } else if len(adr) == 66 { + // remote PK, don't convert to remotePKH + // we have a pubkey, we should't bother checking addresses + // this is definitively a non-lit instance, so don't check for lit stuff here keyFilePath := filepath.Join(defaultDir, "lit-af-key.hex") privKey, err := lnutil.ReadKeyFile(keyFilePath) if err != nil { @@ -134,18 +155,11 @@ func (lc *litAfClient) litAfSetup(conf litAfConfig) (error){ var temp [33]byte copy(temp[:], pubkey[:33]) fmt.Printf("The pkh of this lit-af instance is: %s\n", lnutil.LitAdrFromPubkey(temp)) - 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) - } + // so this is a non lit node and hence, we don't need to lookup on the tracker (since we already have the pubkey) - lc.RPCClient, err = litrpc.NewLndcRpcClient(adr, key) + // initialize a new rpc instance with noise_xk based authentication here + adr = fmt.Sprintf("%s@%s:%d", adr, host, port) + err = litrpc.NewOtherRpcClient(adr, key) if err != nil { logging.Fatal(err.Error()) } diff --git a/litrpc/lndcrpcclient.go b/litrpc/lndcrpcclient.go index 7a508ee0b..1bd5ba073 100644 --- a/litrpc/lndcrpcclient.go +++ b/litrpc/lndcrpcclient.go @@ -107,6 +107,15 @@ func NewLocalLndcRpcClientWithHomeDirAndPort(litHomeDir string, port uint32) (*L return NewLndcRpcClient(adr, key) } +func NewOtherRpcClient(address string, key *koblitz.PrivateKey)(error) { + // Dial a connection to the lit node + who, where := lnutil.ParseAdrString(address) + _, err := lndc.Dial(key, where, who, net.Dial) + if err != nil { + return err + } + return nil +} // NewLndcRpcClient creates a new LNDC client using the given private key, which // is arbitrary. It will then connect to the lit node specified in address, and // can then exchange remote control calls with it. In order to succesfully diff --git a/lndc/conn.go b/lndc/conn.go index dd6b446c0..b714ec72d 100644 --- a/lndc/conn.go +++ b/lndc/conn.go @@ -51,16 +51,30 @@ func Dial(localPriv *koblitz.PrivateKey, ipAddr string, remoteAddress string, } var conn net.Conn var err error - + fmt.Println("CHKP1") conn, err = dialer("tcp", ipAddr) logging.Info("ipAddr is", ipAddr) if err != nil { return nil, err } - b := &Conn{ - conn: conn, - noise: NewNoiseMachine(true, localPriv), + b := new(Conn) + if Noise_XK { + fmt.Println("Setting up a new noise_xk machine") + // I need to convert the raw PK to a koblitz public key + remotePub, err := koblitz.ParsePubKey(remotePK[:], koblitz.S256()) + if err != nil { + return nil, err + } + b = &Conn{ + conn: conn, + noise: NewXKNoiseMachine(true, localPriv, remotePub), + } + } else { + b = &Conn{ + conn: conn, + noise: NewXXNoiseMachine(true, localPriv), + } } // Initiate the handshake by sending the first act to the receiver. @@ -73,7 +87,7 @@ func Dial(localPriv *koblitz.PrivateKey, ipAddr string, remoteAddress string, b.conn.Close() return nil, err } - + fmt.Println("CHKA2") // 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. @@ -85,9 +99,11 @@ func Dial(localPriv *koblitz.PrivateKey, ipAddr string, remoteAddress string, // secrecy. actTwo := make([]byte, ActTwoSize) if _, err := io.ReadFull(conn, actTwo[:]); err != nil { + fmt.Println("CHKmid", err) b.conn.Close() return nil, err } + fmt.Println("CHKA3") if !Noise_XK { remotePK, err = b.noise.RecvActTwo(actTwo) if err != nil { diff --git a/lndc/listener.go b/lndc/listener.go index 4574450bc..2024ba90e 100644 --- a/lndc/listener.go +++ b/lndc/listener.go @@ -105,7 +105,7 @@ func (l *Listener) doHandshake(conn net.Conn) { lndcConn := &Conn{ conn: conn, - noise: NewNoiseMachine(false, l.localStatic), + noise: NewXKNoiseMachine(false, l.localStatic, nil), } // We'll ensure that we get ActOne from the remote peer in a timely diff --git a/lndc/noise.go b/lndc/noise.go index e89122a60..15d29d567 100644 --- a/lndc/noise.go +++ b/lndc/noise.go @@ -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 @@ -227,6 +220,7 @@ func (s *symmetricState) EncryptAndHash(plaintext []byte) []byte { // ciphertext. When encrypting the handshake digest (h) is used as the // associated data to the AEAD cipher. func (s *symmetricState) DecryptAndHash(ciphertext []byte) ([]byte, error) { + fmt.Println("HANDSHAKE DIGEST:", s.handshakeDigest[:]) plaintext, err := s.Decrypt(s.handshakeDigest[:], nil, ciphertext) if err != nil { return nil, err @@ -241,6 +235,8 @@ func (s *symmetricState) DecryptAndHash(ciphertext []byte) ([]byte, error) { func (s *symmetricState) InitializeSymmetric(protocolName []byte) { var empty [32]byte s.handshakeDigest = sha256.Sum256(protocolName) + fmt.Println("SANITY CH ECK", sha256.Sum256([]byte("Noise_XK_secp256k1_ChaChaPoly_SHA256"))) + fmt.Println("SHA256SUYM is", s.handshakeDigest) s.chainingKey = s.handshakeDigest s.InitializeKey(empty) // init with empty key } @@ -282,6 +278,37 @@ func newHandshakeState(initiator bool, prologue []byte, return h } +// newHandshakeState 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, + } + // same fmt.Println("MIX HASH before newXKHandshakeState:", h.handshakeDigest[:]) + + // 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. + fmt.Println("MIX HASH 1 before INITSYMM in newXKHandshakeState:", h.handshakeDigest[:]) + fmt.Println("PROTOCL NAME is", []byte(protocolName), prologue) + fmt.Println("CHEDASDASD", protocolName, protocolName == "Noise_XK_secp256k1_ChaChaPoly_SHA256") + h.InitializeSymmetric([]byte("Noise_XK_secp256k1_ChaChaPoly_SHA256")) + fmt.Println("MIX HASH 1 before prologue in newXKHandshakeState:", h.handshakeDigest[:]) + h.mixHash(prologue) + fmt.Println("MIX HASH after prologue in newXKHandshakeState:", h.handshakeDigest[:]) + 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 @@ -359,10 +386,35 @@ 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, +func NewXKNoiseMachine(initiator bool, localStatic *koblitz.PrivateKey, remotePub *koblitz.PublicKey, + options ...func(*Machine)) *Machine { + + // so if it noise_XK, I need to hash the remote PK in here as well + handshake := newXKHandshakeState(initiator, []byte("lightning"), localStatic, remotePub) + // TODO: if we're sending messages of type XK, set it back to + // "lightning" which is what BOLT uses + + 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 NewXXNoiseMachine(initiator bool, localStatic *koblitz.PrivateKey, options ...func(*Machine)) *Machine { - handshake := newHandshakeState(initiator, []byte("lit"), localStatic) + // so if it noise_XK, I need to hash the remote PK in here as well + handshake := newHandshakeState(initiator, []byte("lightning"), localStatic) // TODO: if we're sending messages of type XK, set it back to // "lightning" which is what BOLT uses @@ -383,6 +435,12 @@ func NewNoiseMachine(initiator bool, localStatic *koblitz.PrivateKey, } 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. @@ -419,6 +477,7 @@ func SetConsts() { // key in compressed format and a 16-byte poly1305 tag. // <- e, ee // 1 + 33 + 16 + protocolName = "Noise_XK_secp256k1_ChaChaPoly_SHA256" ActTwoSize = 50 } @@ -429,6 +488,9 @@ func SetConsts() { // -> e func (b *Machine) GenActOne(remotePK [33]byte) ([]byte, error) { + // TODO: remove this remotePK passed since its not needed + fmt.Println("Using protocol name: ", protocolName) + fmt.Println("HANDSHAKE DIGEST IN GENACTONE:", b.handshakeDigest[:]) var err error actOne := make([]byte, ActOneSize) // Generate e @@ -437,26 +499,40 @@ func (b *Machine) GenActOne(remotePK [33]byte) ([]byte, error) { return actOne, err } - // Compress e - e := b.localEphemeral.PubKey().SerializeCompressed() - // Hash it into the handshake digest - b.mixHash(e) - if Noise_XK { - b.remoteStatic, err = koblitz.ParsePubKey(remotePK[:], koblitz.S256()) - if err != nil { - return nil, err - } + ephemeral := b.localEphemeral.PubKey().SerializeCompressed() + b.mixHash(ephemeral) // es s := ecdh(b.remoteStatic, b.localEphemeral) b.mixKey(s[:]) + authPayload := b.EncryptAndHash([]byte{}) + actOne[0] = HandshakeVersion + copy(actOne[1:34], ephemeral) + copy(actOne[34:], authPayload) + _, err = b.DecryptAndHash(authPayload[:]) + if err != nil { + fmt.Println("Decrypting Act One of Noise failed: ", err) + } + return actOne, nil + } else { + // Compress e + e := b.localEphemeral.PubKey().SerializeCompressed() + // Hash it into the handshake digest + fmt.Println("E IS:", e) + b.mixHash(e) + fmt.Println("SENDING E:", b.localEphemeral.PubKey().SerializeCompressed()) + authPayload := b.EncryptAndHash([]byte{}) + actOne[0] = HandshakeVersion + copy(actOne[1:34], e) + copy(actOne[34:], authPayload) + fmt.Println("Sending, ActOne: ", actOne) + _, err = b.DecryptAndHash(authPayload[:]) + if err != nil { + fmt.Println("Decrypting Act One of Noise failed: ", err) + } + return actOne, nil } - authPayload := b.EncryptAndHash([]byte{}) - actOne[0] = HandshakeVersion - copy(actOne[1:34], e) - copy(actOne[34:], authPayload) - return actOne, nil } // RecvActOne processes the act one packet sent by the initiator. The responder @@ -464,6 +540,7 @@ func (b *Machine) GenActOne(remotePK [33]byte) ([]byte, error) { // 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 []byte) error { + fmt.Println("Received, ActOne: ", actOne) var ( err error e [33]byte @@ -480,7 +557,7 @@ func (b *Machine) RecvActOne(actOne []byte) error { copy(e[:], actOne[1:34]) copy(p[:], actOne[34:]) - + //fmt.Println("INITIAL STATE:", b.handshakeDigest[:]) // e b.remoteEphemeral, err = koblitz.ParsePubKey(e[:], koblitz.S256()) if err != nil { @@ -493,8 +570,11 @@ func (b *Machine) RecvActOne(actOne []byte) error { es := ecdh(b.remoteEphemeral, b.localStatic) b.mixKey(es) } - + fmt.Println("CHK THIS OUT", b.handshakeDigest[:]) _, err = b.DecryptAndHash(p[:]) + if err != nil { + fmt.Println("Decrypting Act One ofin Noise failed: ", err) + } return err // nil means Act one completed successfully } From 9c7487619b0951557af6743b92703f88f3f70516 Mon Sep 17 00:00:00 2001 From: Varunram Date: Fri, 23 Nov 2018 11:13:22 +0530 Subject: [PATCH 08/10] Remove ugly log statements --- lndc/conn.go | 15 +++----- lndc/listener.go | 22 ++++++++--- lndc/noise.go | 95 ++++++++++++++++++------------------------------ 3 files changed, 56 insertions(+), 76 deletions(-) diff --git a/lndc/conn.go b/lndc/conn.go index b714ec72d..21a8c221c 100644 --- a/lndc/conn.go +++ b/lndc/conn.go @@ -45,22 +45,20 @@ func Dial(localPriv *koblitz.PrivateKey, ipAddr string, remoteAddress string, } 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") + logging.Info("Got remote PK: ", remotePK, ", using noise_xk to connect") Noise_XK = true - SetConsts() + SetXKConsts() } var conn net.Conn var err error - fmt.Println("CHKP1") conn, err = dialer("tcp", ipAddr) - logging.Info("ipAddr is", ipAddr) + logging.Debug("ipAddr is: ", ipAddr) if err != nil { return nil, err } b := new(Conn) if Noise_XK { - fmt.Println("Setting up a new noise_xk machine") // I need to convert the raw PK to a koblitz public key remotePub, err := koblitz.ParsePubKey(remotePK[:], koblitz.S256()) if err != nil { @@ -68,12 +66,12 @@ func Dial(localPriv *koblitz.PrivateKey, ipAddr string, remoteAddress string, } b = &Conn{ conn: conn, - noise: NewXKNoiseMachine(true, localPriv, remotePub), + noise: NewNoiseXKMachine(true, localPriv, remotePub), } } else { b = &Conn{ conn: conn, - noise: NewXXNoiseMachine(true, localPriv), + noise: NewNoiseXXMachine(true, localPriv), } } @@ -87,7 +85,6 @@ func Dial(localPriv *koblitz.PrivateKey, ipAddr string, remoteAddress string, b.conn.Close() return nil, err } - fmt.Println("CHKA2") // 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. @@ -99,11 +96,9 @@ func Dial(localPriv *koblitz.PrivateKey, ipAddr string, remoteAddress string, // secrecy. actTwo := make([]byte, ActTwoSize) if _, err := io.ReadFull(conn, actTwo[:]); err != nil { - fmt.Println("CHKmid", err) b.conn.Close() return nil, err } - fmt.Println("CHKA3") if !Noise_XK { remotePK, err = b.noise.RecvActTwo(actTwo) if err != nil { diff --git a/lndc/listener.go b/lndc/listener.go index 2024ba90e..2f2961319 100644 --- a/lndc/listener.go +++ b/lndc/listener.go @@ -103,9 +103,13 @@ 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: NewXKNoiseMachine(false, l.localStatic, nil), + conn: conn, } // We'll ensure that we get ActOne from the remote peer in a timely @@ -123,11 +127,17 @@ func (l *Listener) doHandshake(conn net.Conn) { return } - if actOne[0] == 0 { // remote node wants to connect via XK - HandshakeVersion = byte(0) - ActTwoSize = 50 + if actOne[0] == 0 { + // remote node wants to connect via XK + Noise_XK = true + SetXKConsts() + lndcConn.noise = NewNoiseXKMachine(false, l.localStatic, nil) logging.Infof("remote node wants to connect via noise_xk") - } // no need for else as default covers XX + } else { + // 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) + } if err := lndcConn.noise.RecvActOne(actOne); err != nil { lndcConn.conn.Close() diff --git a/lndc/noise.go b/lndc/noise.go index 15d29d567..14c0fe3bb 100644 --- a/lndc/noise.go +++ b/lndc/noise.go @@ -220,7 +220,6 @@ func (s *symmetricState) EncryptAndHash(plaintext []byte) []byte { // ciphertext. When encrypting the handshake digest (h) is used as the // associated data to the AEAD cipher. func (s *symmetricState) DecryptAndHash(ciphertext []byte) ([]byte, error) { - fmt.Println("HANDSHAKE DIGEST:", s.handshakeDigest[:]) plaintext, err := s.Decrypt(s.handshakeDigest[:], nil, ciphertext) if err != nil { return nil, err @@ -235,8 +234,6 @@ func (s *symmetricState) DecryptAndHash(ciphertext []byte) ([]byte, error) { func (s *symmetricState) InitializeSymmetric(protocolName []byte) { var empty [32]byte s.handshakeDigest = sha256.Sum256(protocolName) - fmt.Println("SANITY CH ECK", sha256.Sum256([]byte("Noise_XK_secp256k1_ChaChaPoly_SHA256"))) - fmt.Println("SHA256SUYM is", s.handshakeDigest) s.chainingKey = s.handshakeDigest s.InitializeKey(empty) // init with empty key } @@ -258,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{ @@ -278,30 +275,24 @@ func newHandshakeState(initiator bool, prologue []byte, return h } -// 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 newXKHandshakeState(initiator bool, prologue []byte, localStatic *koblitz.PrivateKey, remoteStatic *koblitz.PublicKey) handshakeState { h := handshakeState{ - initiator: initiator, - localStatic: localStatic, + initiator: initiator, + localStatic: localStatic, remoteStatic: remoteStatic, } - // same fmt.Println("MIX HASH before newXKHandshakeState:", h.handshakeDigest[:]) // 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. - fmt.Println("MIX HASH 1 before INITSYMM in newXKHandshakeState:", h.handshakeDigest[:]) - fmt.Println("PROTOCL NAME is", []byte(protocolName), prologue) - fmt.Println("CHEDASDASD", protocolName, protocolName == "Noise_XK_secp256k1_ChaChaPoly_SHA256") h.InitializeSymmetric([]byte("Noise_XK_secp256k1_ChaChaPoly_SHA256")) - fmt.Println("MIX HASH 1 before prologue in newXKHandshakeState:", h.handshakeDigest[:]) h.mixHash(prologue) - fmt.Println("MIX HASH after prologue in newXKHandshakeState:", h.handshakeDigest[:]) if initiator { h.mixHash(remoteStatic.SerializeCompressed()) } else { @@ -309,6 +300,7 @@ func newXKHandshakeState(initiator bool, prologue []byte, } 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 @@ -386,13 +378,12 @@ 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 NewXKNoiseMachine(initiator bool, localStatic *koblitz.PrivateKey, remotePub *koblitz.PublicKey, +func NewNoiseXKMachine(initiator bool, localStatic *koblitz.PrivateKey, remotePub *koblitz.PublicKey, options ...func(*Machine)) *Machine { // so if it noise_XK, I need to hash the remote PK in here as well handshake := newXKHandshakeState(initiator, []byte("lightning"), localStatic, remotePub) - // TODO: if we're sending messages of type XK, set it back to - // "lightning" which is what BOLT uses + // "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 @@ -409,12 +400,11 @@ func NewXKNoiseMachine(initiator bool, localStatic *koblitz.PrivateKey, remotePu return m } - -func NewXXNoiseMachine(initiator bool, localStatic *koblitz.PrivateKey, +func NewNoiseXXMachine(initiator bool, localStatic *koblitz.PrivateKey, options ...func(*Machine)) *Machine { // so if it noise_XK, I need to hash the remote PK in here as well - handshake := newHandshakeState(initiator, []byte("lightning"), localStatic) + handshake := newXXHandshakeState(initiator, []byte("lit"), localStatic) // TODO: if we're sending messages of type XK, set it back to // "lightning" which is what BOLT uses @@ -470,15 +460,22 @@ var ( ActThreeSize = 66 ) -func SetConsts() { - HandshakeVersion = byte(0) // Noise_XK's hadnshake version +func SetXKConsts() { + // 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 - protocolName = "Noise_XK_secp256k1_ChaChaPoly_SHA256" 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 @@ -489,8 +486,6 @@ func SetConsts() { func (b *Machine) GenActOne(remotePK [33]byte) ([]byte, error) { // TODO: remove this remotePK passed since its not needed - fmt.Println("Using protocol name: ", protocolName) - fmt.Println("HANDSHAKE DIGEST IN GENACTONE:", b.handshakeDigest[:]) var err error actOne := make([]byte, ActOneSize) // Generate e @@ -499,40 +494,21 @@ func (b *Machine) GenActOne(remotePK [33]byte) ([]byte, error) { return actOne, err } + e := b.localEphemeral.PubKey().SerializeCompressed() + // Hash it into the handshake digest + b.mixHash(e) + if Noise_XK { - ephemeral := b.localEphemeral.PubKey().SerializeCompressed() - b.mixHash(ephemeral) // es s := ecdh(b.remoteStatic, b.localEphemeral) b.mixKey(s[:]) - authPayload := b.EncryptAndHash([]byte{}) - actOne[0] = HandshakeVersion - copy(actOne[1:34], ephemeral) - copy(actOne[34:], authPayload) - _, err = b.DecryptAndHash(authPayload[:]) - if err != nil { - fmt.Println("Decrypting Act One of Noise failed: ", err) - } - return actOne, nil - } else { - // Compress e - e := b.localEphemeral.PubKey().SerializeCompressed() - // Hash it into the handshake digest - fmt.Println("E IS:", e) - b.mixHash(e) - fmt.Println("SENDING E:", b.localEphemeral.PubKey().SerializeCompressed()) - authPayload := b.EncryptAndHash([]byte{}) - actOne[0] = HandshakeVersion - copy(actOne[1:34], e) - copy(actOne[34:], authPayload) - fmt.Println("Sending, ActOne: ", actOne) - _, err = b.DecryptAndHash(authPayload[:]) - if err != nil { - fmt.Println("Decrypting Act One of Noise failed: ", err) - } - return actOne, nil } - + authPayload := b.EncryptAndHash([]byte{}) + actOne[0] = HandshakeVersion + copy(actOne[1:34], e) + copy(actOne[34:], authPayload) + _, err = b.DecryptAndHash(authPayload[:]) + return actOne, nil } // RecvActOne processes the act one packet sent by the initiator. The responder @@ -540,7 +516,6 @@ func (b *Machine) GenActOne(remotePK [33]byte) ([]byte, error) { // 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 []byte) error { - fmt.Println("Received, ActOne: ", actOne) var ( err error e [33]byte @@ -555,9 +530,13 @@ func (b *Machine) RecvActOne(actOne []byte) error { actOne[:]) } + if actOne[0] == 0 { + // this is needed in order to start the correct listener on our side + Noise_XK = true + fmt.Println("TRUE?", Noise_XK) + } copy(e[:], actOne[1:34]) copy(p[:], actOne[34:]) - //fmt.Println("INITIAL STATE:", b.handshakeDigest[:]) // e b.remoteEphemeral, err = koblitz.ParsePubKey(e[:], koblitz.S256()) if err != nil { @@ -570,11 +549,7 @@ func (b *Machine) RecvActOne(actOne []byte) error { es := ecdh(b.remoteEphemeral, b.localStatic) b.mixKey(es) } - fmt.Println("CHK THIS OUT", b.handshakeDigest[:]) _, err = b.DecryptAndHash(p[:]) - if err != nil { - fmt.Println("Decrypting Act One ofin Noise failed: ", err) - } return err // nil means Act one completed successfully } From b36a630dfb83bd171560d6505225f52af52a2ffd Mon Sep 17 00:00:00 2001 From: Varunram Date: Fri, 23 Nov 2018 11:21:16 +0530 Subject: [PATCH 09/10] listen to bolt nodes and connect to bolt nodes but we can't do anything more since the wire protocol and the rpc stuff hasn't been changed to deal with that yet --- lndc/conn.go | 4 ++-- lndc/listener.go | 12 ++++++++---- lndc/noise.go | 30 +++++++----------------------- 3 files changed, 17 insertions(+), 29 deletions(-) diff --git a/lndc/conn.go b/lndc/conn.go index 21a8c221c..edb071c6f 100644 --- a/lndc/conn.go +++ b/lndc/conn.go @@ -46,7 +46,6 @@ func Dial(localPriv *koblitz.PrivateKey, ipAddr string, remoteAddress string, temp, _ := hex.DecodeString(remoteAddress) copy(remotePK[:], temp) logging.Info("Got remote PK: ", remotePK, ", using noise_xk to connect") - Noise_XK = true SetXKConsts() } var conn net.Conn @@ -59,9 +58,10 @@ func Dial(localPriv *koblitz.PrivateKey, ipAddr string, remoteAddress string, b := new(Conn) if Noise_XK { - // I need to convert the raw PK to a koblitz public key + // 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{ diff --git a/lndc/listener.go b/lndc/listener.go index 2f2961319..88d7609f1 100644 --- a/lndc/listener.go +++ b/lndc/listener.go @@ -1,6 +1,7 @@ package lndc import ( + "fmt" "errors" "io" "net" @@ -103,8 +104,7 @@ func (l *Listener) doHandshake(conn net.Conn) { default: } - // TODO: - // the listener here defaults to a noise_XK machine, we should add a condition + // 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 @@ -129,14 +129,18 @@ func (l *Listener) doHandshake(conn net.Conn) { if actOne[0] == 0 { // remote node wants to connect via XK - Noise_XK = true SetXKConsts() lndcConn.noise = NewNoiseXKMachine(false, l.localStatic, nil) logging.Infof("remote node wants to connect via noise_xk") - } else { + } 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 { diff --git a/lndc/noise.go b/lndc/noise.go index 14c0fe3bb..181c0f352 100644 --- a/lndc/noise.go +++ b/lndc/noise.go @@ -378,10 +378,9 @@ 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 NewNoiseXKMachine(initiator bool, localStatic *koblitz.PrivateKey, remotePub *koblitz.PublicKey, - options ...func(*Machine)) *Machine { +func NewNoiseXKMachine(initiator bool, localStatic *koblitz.PrivateKey, + remotePub *koblitz.PublicKey) *Machine { - // so if it noise_XK, I need to hash the remote PK in here as well handshake := newXKHandshakeState(initiator, []byte("lightning"), localStatic, remotePub) // "lightning" is what BOLT uses, "lit" is used for XX handshakes @@ -391,22 +390,13 @@ func NewNoiseXKMachine(initiator bool, localStatic *koblitz.PrivateKey, remotePu 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, - options ...func(*Machine)) *Machine { +func NewNoiseXXMachine(initiator bool, localStatic *koblitz.PrivateKey) *Machine { - // so if it noise_XK, I need to hash the remote PK in here as well handshake := newXXHandshakeState(initiator, []byte("lit"), localStatic) - // TODO: if we're sending messages of type XK, set it back to - // "lightning" which is what BOLT uses m := &Machine{handshakeState: handshake} @@ -415,11 +405,6 @@ func NewNoiseXXMachine(initiator bool, localStatic *koblitz.PrivateKey, 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 } @@ -461,6 +446,7 @@ var ( ) func SetXKConsts() { + Noise_XK = true // Noise_XK's hadnshake version HandshakeVersion = byte(0) @@ -516,6 +502,9 @@ func (b *Machine) GenActOne(remotePK [33]byte) ([]byte, error) { // 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 []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 @@ -530,11 +519,6 @@ func (b *Machine) RecvActOne(actOne []byte) error { actOne[:]) } - if actOne[0] == 0 { - // this is needed in order to start the correct listener on our side - Noise_XK = true - fmt.Println("TRUE?", Noise_XK) - } copy(e[:], actOne[1:34]) copy(p[:], actOne[34:]) // e From 018afdd64162ddeb728997a9f8e8897cf7a50210 Mon Sep 17 00:00:00 2001 From: Varunram Date: Fri, 23 Nov 2018 11:36:08 +0530 Subject: [PATCH 10/10] add debug line --- lnp2p/msgproc.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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]