141 lines
4.4 KiB
Go
141 lines
4.4 KiB
Go
package relay
|
|
|
|
import (
|
|
"time"
|
|
|
|
"git-indra.lan/indra-labs/lnd/lnd/lnwire"
|
|
|
|
"git-indra.lan/indra-labs/indra/pkg/crypto/key/prv"
|
|
"git-indra.lan/indra-labs/indra/pkg/crypto/key/pub"
|
|
"git-indra.lan/indra-labs/indra/pkg/crypto/key/signer"
|
|
"git-indra.lan/indra-labs/indra/pkg/crypto/nonce"
|
|
"git-indra.lan/indra-labs/indra/pkg/crypto/sha256"
|
|
"git-indra.lan/indra-labs/indra/pkg/messages/crypt"
|
|
"git-indra.lan/indra-labs/indra/pkg/messages/exit"
|
|
"git-indra.lan/indra-labs/indra/pkg/messages/response"
|
|
"git-indra.lan/indra-labs/indra/pkg/types"
|
|
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
|
)
|
|
|
|
// SendExit constructs a message containing an arbitrary payload to a node (3rd
|
|
// hop) with a set of 3 ciphers derived from the hidden PayloadPub of the return
|
|
// hops that are layered progressively after the Exit message.
|
|
//
|
|
// The Exit node forwards the packet it receives to the local port specified in
|
|
// the Exit message, and then uses the ciphers to encrypt the reply with the
|
|
// three ciphers provided, which don't enable it to decrypt the header, only to
|
|
// encrypt the payload.
|
|
//
|
|
// The response is encrypted with the given layers, the ciphers are already
|
|
// given in reverse order, so they are decoded in given order to create the
|
|
// correct payload encryption to match the PayloadPub combined with the header's
|
|
// given public From key.
|
|
//
|
|
// The header remains a constant size and each node in the Reverse trims off
|
|
// their section at the top, moves the next crypt header to the top and pads the
|
|
// remainder with noise, so it always looks like the first hop.
|
|
func SendExit(port uint16, payload slice.Bytes, id nonce.ID,
|
|
client *Session, s Circuit, ks *signer.KeySet) Skins {
|
|
|
|
var prvs [3]*prv.Key
|
|
for i := range prvs {
|
|
prvs[i] = ks.Next()
|
|
}
|
|
n := GenNonces(6)
|
|
var returnNonces [3]nonce.IV
|
|
copy(returnNonces[:], n[3:])
|
|
var pubs [3]*pub.Key
|
|
pubs[0] = s[3].PayloadPub
|
|
pubs[1] = s[4].PayloadPub
|
|
pubs[2] = client.PayloadPub
|
|
return Skins{}.
|
|
ReverseCrypt(s[0], ks.Next(), n[0], 3).
|
|
ReverseCrypt(s[1], ks.Next(), n[1], 2).
|
|
ReverseCrypt(s[2], ks.Next(), n[2], 1).
|
|
Exit(port, prvs, pubs, returnNonces, id, payload).
|
|
ReverseCrypt(s[3], prvs[0], n[3], 3).
|
|
ReverseCrypt(s[4], prvs[1], n[4], 2).
|
|
ReverseCrypt(client, prvs[2], n[5], 1)
|
|
}
|
|
|
|
func (eng *Engine) exit(ex *exit.Layer, b slice.Bytes,
|
|
c *slice.Cursor, prev types.Onion) {
|
|
|
|
// payload is forwarded to a local port and the result is forwarded
|
|
// back with a reverse header.
|
|
var e error
|
|
var result slice.Bytes
|
|
h := sha256.Single(ex.Bytes)
|
|
log.T.S(h)
|
|
log.T.F("%s received exit id %x", eng.GetLocalNodeAddress(), ex.ID)
|
|
if e = eng.SendFromLocalNode(ex.Port, ex.Bytes); check(e) {
|
|
return
|
|
}
|
|
timer := time.NewTicker(time.Second)
|
|
select {
|
|
case result = <-eng.ReceiveToLocalNode(ex.Port):
|
|
case <-timer.C:
|
|
}
|
|
// We need to wrap the result in a message crypt.
|
|
res := Encode(&response.Layer{
|
|
ID: ex.ID,
|
|
Port: ex.Port,
|
|
Load: byte(eng.Load.Load()),
|
|
Bytes: result,
|
|
})
|
|
rb := FormatReply(b[*c:c.Inc(crypt.ReverseHeaderLen)],
|
|
res, ex.Ciphers, ex.Nonces)
|
|
switch on := prev.(type) {
|
|
case *crypt.Layer:
|
|
sess := eng.FindSessionByHeader(on.ToPriv)
|
|
if sess == nil {
|
|
break
|
|
}
|
|
for i := range sess.Services {
|
|
if ex.Port != sess.Services[i].Port {
|
|
continue
|
|
}
|
|
in := sess.Services[i].RelayRate *
|
|
lnwire.MilliSatoshi(len(b)) / 2 / 1024 / 1024
|
|
out := sess.Services[i].RelayRate *
|
|
lnwire.MilliSatoshi(len(rb)) / 2 / 1024 / 1024
|
|
eng.DecSession(sess.ID, in+out, false, "exit")
|
|
break
|
|
}
|
|
}
|
|
eng.handleMessage(rb, ex)
|
|
}
|
|
|
|
func (eng *Engine) SendExit(port uint16, msg slice.Bytes, id nonce.ID,
|
|
target *Session, hook Callback) {
|
|
|
|
hops := []byte{0, 1, 2, 3, 4, 5}
|
|
s := make(Sessions, len(hops))
|
|
s[2] = target
|
|
se := eng.SelectHops(hops, s)
|
|
var c Circuit
|
|
copy(c[:], se)
|
|
o := SendExit(port, msg, id, se[len(se)-1], c, eng.KeySet)
|
|
log.D.Ln("sending out exit onion")
|
|
res := eng.PostAcctOnion(o)
|
|
eng.SendWithOneHook(c[0].AddrPort, res, hook)
|
|
}
|
|
|
|
func (eng *Engine) MakeExit(port uint16, msg slice.Bytes, id nonce.ID,
|
|
exit *Session) (c Circuit, o Skins) {
|
|
|
|
hops := []byte{0, 1, 2, 3, 4, 5}
|
|
s := make(Sessions, len(hops))
|
|
s[2] = exit
|
|
se := eng.SelectHops(hops, s)
|
|
copy(c[:], se)
|
|
o = SendExit(port, msg, id, se[len(se)-1], c, eng.KeySet)
|
|
return
|
|
}
|
|
|
|
func (eng *Engine) SendExitNew(c Circuit, o Skins, hook Callback) {
|
|
log.D.Ln("sending out exit onion")
|
|
res := eng.PostAcctOnion(o)
|
|
eng.SendWithOneHook(c[0].AddrPort, res, hook)
|
|
}
|