From 89f45baa0aa2588f3c6771d474f35f23517b2b44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D1=85=D0=B5=D1=80=D0=B5=D1=82=D0=B8=D0=BA?= Date: Thu, 23 Feb 2023 11:23:36 +0000 Subject: [PATCH] Added hidden service intro message and started test for it --- pkg/onion/hiddenservice/hiddenservice.go | 3 ++ pkg/relay/engine_test.go | 59 ++++++++++++++++++++++-- pkg/relay/gen/main.go | 6 ++- pkg/relay/handlemessage.go | 8 ++++ pkg/relay/handler-hiddenservice.go | 12 +++++ pkg/relay/helper-postacct.go | 7 ++- pkg/relay/helper-sendexit.go | 27 ++++------- pkg/relay/helper-sendintroduction.go | 21 +++++++++ pkg/relay/onion-hiddenservice.go | 4 +- pkg/relay/peel.go | 6 +++ pkg/relay/senders.go | 10 ++-- pkg/relay/skins.go | 3 +- 12 files changed, 137 insertions(+), 29 deletions(-) create mode 100644 pkg/relay/handler-hiddenservice.go create mode 100644 pkg/relay/helper-sendintroduction.go diff --git a/pkg/onion/hiddenservice/hiddenservice.go b/pkg/onion/hiddenservice/hiddenservice.go index 1eebab2f..a0768e23 100644 --- a/pkg/onion/hiddenservice/hiddenservice.go +++ b/pkg/onion/hiddenservice/hiddenservice.go @@ -28,6 +28,7 @@ var ( // Layer exit messages are the crypt of a message after two Forward packets // that provides an exit address and type Layer struct { + nonce.ID // Identity is a public key identifying the hidden service. It is encoded // into Bech32 encoding to function like an IP address, with a 2 byte // truncated hash check suffix to eliminate possible human input errors and @@ -53,6 +54,7 @@ func (x *Layer) Len() int { func (x *Layer) Encode(b slice.Bytes, c *slice.Cursor) { splice.Splice(b, c). Magic(Magic). + ID(x.ID). Hash(x.Ciphers[0]).Hash(x.Ciphers[1]).Hash(x.Ciphers[2]). IV(x.Nonces[0]).IV(x.Nonces[1]).IV(x.Nonces[2]) } @@ -62,6 +64,7 @@ func (x *Layer) Decode(b slice.Bytes, c *slice.Cursor) (e error) { return magicbytes.TooShort(len(b[*c:]), Len-magicbytes.Len, string(Magic)) } splice.Splice(b, c). + ReadID(&x.ID). ReadHash(&x.Ciphers[0]).ReadHash(&x.Ciphers[1]).ReadHash(&x.Ciphers[2]). ReadIV(&x.Nonces[0]).ReadIV(&x.Nonces[1]).ReadIV(&x.Nonces[2]) return diff --git a/pkg/relay/engine_test.go b/pkg/relay/engine_test.go index e4ace3ea..1e89ba3d 100644 --- a/pkg/relay/engine_test.go +++ b/pkg/relay/engine_test.go @@ -5,10 +5,12 @@ import ( "sync" "testing" "time" - + "github.com/cybriq/qu" "go.uber.org/atomic" - + + "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/nonce" "git-indra.lan/indra-labs/indra/pkg/crypto/sha256" log2 "git-indra.lan/indra-labs/indra/pkg/proc/log" @@ -138,7 +140,7 @@ out: } log.I.F("success\n\n") wg.Done() - }, 0) + }) bb := <-clients[3].ReceiveToLocalNode(port) log.T.S(bb.ToBytes()) if e = clients[3].SendFromLocalNode(port, respMsg); check(e) { @@ -248,3 +250,54 @@ out: v.Shutdown() } } + +func TestClient_HiddenService(t *testing.T) { + log2.SetLogLevel(log2.Trace) + var clients []*Engine + var e error + if clients, e = CreateNMockCircuits(true, 2, 2); check(e) { + t.Error(e) + t.FailNow() + } + // Start up the clients. + for _, v := range clients { + go v.Start() + } + quit := qu.T() + // var wg sync.WaitGroup + // go func() { + // select { + // case <-time.After(time.Second): + // case <-quit: + // return + // } + // quit.Q() + // t.Error("SendExit test failed") + // }() + var identPrv *prv.Key + if identPrv, e = prv.GenerateKey(); check(e) { + t.Error(e) + t.FailNow() + } + identPub := pub.Derive(identPrv) +out: + for i := 3; i < len(clients[0].Sessions)-1; i++ { + // wg.Add(1) + id := nonce.NewID() + clients[0].SendIntro(id, clients[0].Sessions[i], + identPub, func(id nonce.ID, b slice.Bytes) { + log.I.Ln("success") + // wg.Done() + }) + select { + case <-quit: + break out + default: + } + // wg.Wait() + } + quit.Q() + for _, v := range clients { + v.Shutdown() + } +} diff --git a/pkg/relay/gen/main.go b/pkg/relay/gen/main.go index 68c51d00..f50223df 100644 --- a/pkg/relay/gen/main.go +++ b/pkg/relay/gen/main.go @@ -31,7 +31,8 @@ func (p handlemessages) Swap(i, j int) { p[i], p[j] = p[j], p[i] } func main() { typesList := []string{"balance", "confirm", "crypt", "delay", "dxresponse", - "exit", "forward", "getbalance", "reverse", "response", "session"} + "exit", "forward", "getbalance", "hiddenservice", "reverse", + "response", "session"} sort.Strings(typesList) tpl := `package relay @@ -48,6 +49,7 @@ import ( "git-indra.lan/indra-labs/indra/pkg/onion/exit" "git-indra.lan/indra-labs/indra/pkg/onion/forward" "git-indra.lan/indra-labs/indra/pkg/onion/getbalance" + "git-indra.lan/indra-labs/indra/pkg/onion/hiddenservice" "git-indra.lan/indra-labs/indra/pkg/onion/magicbytes" "git-indra.lan/indra-labs/indra/pkg/onion/response" "git-indra.lan/indra-labs/indra/pkg/onion/reverse" @@ -97,6 +99,7 @@ func Peel(b slice.Bytes, c *slice.Cursor) (on types.Onion, e error) { {"exit", true}, {"forward", true}, {"getbalance", true}, + {"hiddenservice", true}, {"reverse", false}, {"response", true}, {"session", true}, @@ -115,6 +118,7 @@ import ( "git-indra.lan/indra-labs/indra/pkg/onion/exit" "git-indra.lan/indra-labs/indra/pkg/onion/forward" "git-indra.lan/indra-labs/indra/pkg/onion/getbalance" + "git-indra.lan/indra-labs/indra/pkg/onion/hiddenservice" "git-indra.lan/indra-labs/indra/pkg/onion/response" "git-indra.lan/indra-labs/indra/pkg/onion/reverse" "git-indra.lan/indra-labs/indra/pkg/onion/session" diff --git a/pkg/relay/handlemessage.go b/pkg/relay/handlemessage.go index 3233e7a7..a96ab1a2 100644 --- a/pkg/relay/handlemessage.go +++ b/pkg/relay/handlemessage.go @@ -10,6 +10,7 @@ import ( "git-indra.lan/indra-labs/indra/pkg/onion/exit" "git-indra.lan/indra-labs/indra/pkg/onion/forward" "git-indra.lan/indra-labs/indra/pkg/onion/getbalance" + "git-indra.lan/indra-labs/indra/pkg/onion/hiddenservice" "git-indra.lan/indra-labs/indra/pkg/onion/response" "git-indra.lan/indra-labs/indra/pkg/onion/reverse" "git-indra.lan/indra-labs/indra/pkg/onion/session" @@ -71,6 +72,13 @@ func (eng *Engine) handleMessage(b slice.Bytes, prev types.Onion) { } log.T.C(recLog(on, b, eng)) eng.getbalance(on, b, c, prev) + case *hiddenservice.Layer: + if prev == nil { + log.E.Ln(reflect.TypeOf(on), "requests from outside? absurd!") + return + } + log.T.C(recLog(on, b, eng)) + eng.hiddenservice(on, b, c, prev) case *response.Layer: if prev == nil { log.E.Ln(reflect.TypeOf(on), "requests from outside? absurd!") diff --git a/pkg/relay/handler-hiddenservice.go b/pkg/relay/handler-hiddenservice.go new file mode 100644 index 00000000..347d2d1a --- /dev/null +++ b/pkg/relay/handler-hiddenservice.go @@ -0,0 +1,12 @@ +package relay + +import ( + "git-indra.lan/indra-labs/indra/pkg/onion/hiddenservice" + "git-indra.lan/indra-labs/indra/pkg/types" + "git-indra.lan/indra-labs/indra/pkg/util/slice" +) + +func (eng *Engine) hiddenservice(ex *hiddenservice.Layer, b slice.Bytes, + c *slice.Cursor, prev types.Onion) { + +} diff --git a/pkg/relay/helper-postacct.go b/pkg/relay/helper-postacct.go index d171e98c..14e180d3 100644 --- a/pkg/relay/helper-postacct.go +++ b/pkg/relay/helper-postacct.go @@ -2,7 +2,7 @@ package relay import ( "git-indra.lan/indra-labs/lnd/lnd/lnwire" - + "git-indra.lan/indra-labs/indra/pkg/crypto/nonce" "git-indra.lan/indra-labs/indra/pkg/onion/balance" "git-indra.lan/indra-labs/indra/pkg/onion/confirm" @@ -10,6 +10,7 @@ import ( "git-indra.lan/indra-labs/indra/pkg/onion/exit" "git-indra.lan/indra-labs/indra/pkg/onion/forward" "git-indra.lan/indra-labs/indra/pkg/onion/getbalance" + "git-indra.lan/indra-labs/indra/pkg/onion/hiddenservice" "git-indra.lan/indra-labs/indra/pkg/onion/reverse" "git-indra.lan/indra-labs/indra/pkg/util/slice" ) @@ -59,6 +60,10 @@ func (eng *Engine) PostAcctOnion(o Skins) (res SendData) { lnwire.MilliSatoshi(len(res.b))/1024/1024, true, "forward") }) + case *hiddenservice.Layer: + res.last = on2.ID + res.billable = append(res.billable, s.ID) + skip = true case *reverse.Layer: res.billable = append(res.billable, s.ID) case *exit.Layer: diff --git a/pkg/relay/helper-sendexit.go b/pkg/relay/helper-sendexit.go index b9222bc5..821eb77b 100644 --- a/pkg/relay/helper-sendexit.go +++ b/pkg/relay/helper-sendexit.go @@ -1,45 +1,38 @@ package relay import ( - "time" - "git-indra.lan/indra-labs/indra/pkg/crypto/nonce" "git-indra.lan/indra-labs/indra/pkg/util/slice" ) -func (eng *Engine) SendExit(port uint16, message slice.Bytes, id nonce.ID, - target *Session, hook func(id nonce.ID, b slice.Bytes), - timeout time.Duration) { - +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, message, id, se[len(se)-1], c, eng.KeySet) + 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, message slice.Bytes, id nonce.ID, - target *Session) (c Circuit, - o Skins) { - +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] = target + s[2] = exit se := eng.SelectHops(hops, s) copy(c[:], se) - o = SendExit(port, message, id, se[len(se)-1], c, eng.KeySet) + o = SendExit(port, msg, id, se[len(se)-1], c, eng.KeySet) return } -func (eng *Engine) SendExitNew(c Circuit, - o Skins, hook func(id nonce.ID, b slice.Bytes), - timeout time.Duration) { - +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) diff --git a/pkg/relay/helper-sendintroduction.go b/pkg/relay/helper-sendintroduction.go new file mode 100644 index 00000000..45d138e9 --- /dev/null +++ b/pkg/relay/helper-sendintroduction.go @@ -0,0 +1,21 @@ +package relay + +import ( + "git-indra.lan/indra-labs/indra/pkg/crypto/key/pub" + "git-indra.lan/indra-labs/indra/pkg/crypto/nonce" + "git-indra.lan/indra-labs/indra/pkg/util/slice" +) + +func (eng *Engine) SendIntro(id nonce.ID, target *Session, ident *pub.Key, + hook func(id nonce.ID, b slice.Bytes)) { + 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 := HiddenService(id, ident, se[len(se)-1], c, eng.KeySet) + log.D.Ln("sending out intro onion") + res := eng.PostAcctOnion(o) + eng.SendWithOneHook(c[0].AddrPort, res, hook) +} diff --git a/pkg/relay/onion-hiddenservice.go b/pkg/relay/onion-hiddenservice.go index 53d5006c..6497f25f 100644 --- a/pkg/relay/onion-hiddenservice.go +++ b/pkg/relay/onion-hiddenservice.go @@ -7,7 +7,7 @@ import ( "git-indra.lan/indra-labs/indra/pkg/crypto/nonce" ) -func HiddenService(ident *pub.Key, client *Session, s Circuit, +func HiddenService(id nonce.ID, ident *pub.Key, client *Session, s Circuit, ks *signer.KeySet) Skins { var prvs [3]*prv.Key @@ -25,7 +25,7 @@ func HiddenService(ident *pub.Key, client *Session, s Circuit, ReverseCrypt(s[0], ks.Next(), n[0], 3). ReverseCrypt(s[1], ks.Next(), n[1], 2). ReverseCrypt(s[2], ks.Next(), n[2], 1). - HiddenService(ident, prvs, pubs, returnNonces). + HiddenService(id, ident, prvs, pubs, returnNonces). ReverseCrypt(s[3], prvs[0], n[3], 3). ReverseCrypt(s[4], prvs[1], n[4], 2). ReverseCrypt(client, prvs[2], n[5], 1) diff --git a/pkg/relay/peel.go b/pkg/relay/peel.go index 538287b8..b256fa18 100644 --- a/pkg/relay/peel.go +++ b/pkg/relay/peel.go @@ -13,6 +13,7 @@ import ( "git-indra.lan/indra-labs/indra/pkg/onion/exit" "git-indra.lan/indra-labs/indra/pkg/onion/forward" "git-indra.lan/indra-labs/indra/pkg/onion/getbalance" + "git-indra.lan/indra-labs/indra/pkg/onion/hiddenservice" "git-indra.lan/indra-labs/indra/pkg/onion/magicbytes" "git-indra.lan/indra-labs/indra/pkg/onion/response" "git-indra.lan/indra-labs/indra/pkg/onion/reverse" @@ -63,6 +64,11 @@ func Peel(b slice.Bytes, c *slice.Cursor) (on types.Onion, e error) { if e = on.Decode(b, c); check(e) { return } + case hiddenservice.MagicString: + on = &hiddenservice.Layer{} + if e = on.Decode(b, c); check(e) { + return + } case response.MagicString: on = &response.Layer{} if e = on.Decode(b, c); check(e) { diff --git a/pkg/relay/senders.go b/pkg/relay/senders.go index 0f44d5c5..b742b158 100644 --- a/pkg/relay/senders.go +++ b/pkg/relay/senders.go @@ -2,7 +2,7 @@ package relay import ( "net/netip" - + "git-indra.lan/indra-labs/indra/pkg/crypto/nonce" "git-indra.lan/indra-labs/indra/pkg/util/slice" ) @@ -10,14 +10,16 @@ import ( // SendWithOneHook is used for onions with only one confirmation hook. Usually // as returned from PostAcctOnion this is the last, confirmation or response // layer in an onion.Skins. -func (eng *Engine) SendWithOneHook(ap *netip.AddrPort, res SendData, responseHook func(id nonce.ID, b slice.Bytes)) { - +func (eng *Engine) SendWithOneHook(ap *netip.AddrPort, res SendData, + responseHook Callback) { + if responseHook == nil { responseHook = func(_ nonce.ID, _ slice.Bytes) { log.D.Ln("nil response hook") } } - eng.PendingResponses.Add(res.last, len(res.b), res.sessions, res.billable, res.ret, res.port, responseHook, res.postAcct) + eng.PendingResponses.Add(res.last, len(res.b), res.sessions, res.billable, + res.ret, res.port, responseHook, res.postAcct) log.T.Ln("sending out onion") eng.Send(ap, res.b) } diff --git a/pkg/relay/skins.go b/pkg/relay/skins.go index 1fa86ec9..0879906b 100644 --- a/pkg/relay/skins.go +++ b/pkg/relay/skins.go @@ -113,9 +113,10 @@ func (o Skins) GetBalance(id, confID nonce.ID, prvs [3]*prv.Key, }) } -func (o Skins) HiddenService(addr *pub.Key, prvs [3]*prv.Key, +func (o Skins) HiddenService(id nonce.ID, addr *pub.Key, prvs [3]*prv.Key, pubs [3]*pub.Key, nonces [3]nonce.IV) Skins { return append(o, &hiddenservice.Layer{ + ID: id, Identity: addr, Ciphers: GenCiphers(prvs, pubs), Nonces: nonces,