Merge branch 'protocol'
This commit is contained in:
@@ -21,13 +21,11 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Len is the length of the signatures used in Indra, compact keys that can have
|
// Len is the length of the signatures used in Indra, compact keys that can have
|
||||||
// the public key extracted from them, thus eliminating the need to separately
|
// the public key extracted from them.
|
||||||
// specify it in messages.
|
|
||||||
const Len = 65
|
const Len = 65
|
||||||
|
|
||||||
// Bytes is an ECDSA BIP62 formatted compact signature which allows the recovery
|
// Bytes is an ECDSA BIP62 formatted compact signature which allows the recovery
|
||||||
// of the public key from the signature. This allows messages to avoid adding
|
// of the public key from the signature.
|
||||||
// extra bytes to also specify the public key of the signer.
|
|
||||||
type Bytes [Len]byte
|
type Bytes [Len]byte
|
||||||
|
|
||||||
// Sign produces an ECDSA BIP62 compact signature.
|
// Sign produces an ECDSA BIP62 compact signature.
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ package hiddenservice
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"git-indra.lan/indra-labs/indra"
|
"git-indra.lan/indra-labs/indra"
|
||||||
"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/nonce"
|
||||||
"git-indra.lan/indra-labs/indra/pkg/crypto/sha256"
|
"git-indra.lan/indra-labs/indra/pkg/crypto/sha256"
|
||||||
|
"git-indra.lan/indra-labs/indra/pkg/messages/intro"
|
||||||
"git-indra.lan/indra-labs/indra/pkg/messages/magicbytes"
|
"git-indra.lan/indra-labs/indra/pkg/messages/magicbytes"
|
||||||
log2 "git-indra.lan/indra-labs/indra/pkg/proc/log"
|
log2 "git-indra.lan/indra-labs/indra/pkg/proc/log"
|
||||||
"git-indra.lan/indra-labs/indra/pkg/splice"
|
"git-indra.lan/indra-labs/indra/pkg/splice"
|
||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
MagicString = "hs"
|
MagicString = "hs"
|
||||||
Len = magicbytes.Len + nonce.IDLen + pub.KeyLen +
|
Len = magicbytes.Len + nonce.IDLen + intro.Len +
|
||||||
3*sha256.Len + nonce.IVLen*3
|
3*sha256.Len + nonce.IVLen*3
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -30,11 +30,7 @@ var (
|
|||||||
// header for any client that requests it.
|
// header for any client that requests it.
|
||||||
type Layer struct {
|
type Layer struct {
|
||||||
nonce.ID
|
nonce.ID
|
||||||
// Identity is a public key identifying the hidden service. It is encoded
|
intro.Layer
|
||||||
// into Bech32 encoding to function like an IP address, with a 2 byte
|
|
||||||
// truncated hash check suffix to eliminate possible human input errors and
|
|
||||||
// ending in ".indra" to indicate it is an indra hidden service.
|
|
||||||
Identity *pub.Key
|
|
||||||
// Ciphers is a set of 3 symmetric ciphers that are to be used in their
|
// Ciphers is a set of 3 symmetric ciphers that are to be used in their
|
||||||
// given order over the reply message from the service.
|
// given order over the reply message from the service.
|
||||||
Ciphers [3]sha256.Hash
|
Ciphers [3]sha256.Hash
|
||||||
@@ -56,7 +52,9 @@ func (x *Layer) Encode(b slice.Bytes, c *slice.Cursor) {
|
|||||||
splice.Splice(b, c).
|
splice.Splice(b, c).
|
||||||
Magic(Magic).
|
Magic(Magic).
|
||||||
ID(x.ID).
|
ID(x.ID).
|
||||||
Pubkey(x.Identity).
|
Pubkey(x.Key).
|
||||||
|
AddrPort(x.AddrPort).
|
||||||
|
Signature(x.Bytes).
|
||||||
Hash(x.Ciphers[0]).Hash(x.Ciphers[1]).Hash(x.Ciphers[2]).
|
Hash(x.Ciphers[0]).Hash(x.Ciphers[1]).Hash(x.Ciphers[2]).
|
||||||
IV(x.Nonces[0]).IV(x.Nonces[1]).IV(x.Nonces[2])
|
IV(x.Nonces[0]).IV(x.Nonces[1]).IV(x.Nonces[2])
|
||||||
}
|
}
|
||||||
@@ -67,7 +65,9 @@ func (x *Layer) Decode(b slice.Bytes, c *slice.Cursor) (e error) {
|
|||||||
}
|
}
|
||||||
splice.Splice(b, c).
|
splice.Splice(b, c).
|
||||||
ReadID(&x.ID).
|
ReadID(&x.ID).
|
||||||
ReadPubkey(&x.Identity).
|
ReadPubkey(&x.Layer.Key).
|
||||||
|
ReadAddrPort(&x.Layer.AddrPort).
|
||||||
|
ReadSignature(&x.Layer.Bytes).
|
||||||
ReadHash(&x.Ciphers[0]).ReadHash(&x.Ciphers[1]).ReadHash(&x.Ciphers[2]).
|
ReadHash(&x.Ciphers[0]).ReadHash(&x.Ciphers[1]).ReadHash(&x.Ciphers[2]).
|
||||||
ReadIV(&x.Nonces[0]).ReadIV(&x.Nonces[1]).ReadIV(&x.Nonces[2])
|
ReadIV(&x.Nonces[0]).ReadIV(&x.Nonces[1]).ReadIV(&x.Nonces[2])
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -4,17 +4,27 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
|
||||||
|
"git-indra.lan/indra-labs/indra"
|
||||||
|
"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/pub"
|
||||||
|
"git-indra.lan/indra-labs/indra/pkg/crypto/key/sig"
|
||||||
|
"git-indra.lan/indra-labs/indra/pkg/crypto/sha256"
|
||||||
"git-indra.lan/indra-labs/indra/pkg/messages/magicbytes"
|
"git-indra.lan/indra-labs/indra/pkg/messages/magicbytes"
|
||||||
|
log2 "git-indra.lan/indra-labs/indra/pkg/proc/log"
|
||||||
"git-indra.lan/indra-labs/indra/pkg/splice"
|
"git-indra.lan/indra-labs/indra/pkg/splice"
|
||||||
"git-indra.lan/indra-labs/indra/pkg/types"
|
"git-indra.lan/indra-labs/indra/pkg/types"
|
||||||
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
log = log2.GetLogger(indra.PathBase)
|
||||||
|
check = log.E.Chk
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
MagicString = "in"
|
MagicString = "in"
|
||||||
AddrLen = net.IPv6len + 3
|
AddrLen = net.IPv6len + 3
|
||||||
Len = magicbytes.Len + pub.KeyLen + AddrLen
|
Len = magicbytes.Len + pub.KeyLen + AddrLen + sig.Len
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -22,19 +32,60 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Layer struct {
|
type Layer struct {
|
||||||
*pub.Key
|
Key *pub.Key
|
||||||
*netip.AddrPort
|
AddrPort *netip.AddrPort
|
||||||
|
Bytes sig.Bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(key *prv.Key, ap *netip.AddrPort) (in *Layer) {
|
||||||
|
pk := pub.Derive(key)
|
||||||
|
bap, _ := ap.MarshalBinary()
|
||||||
|
pkb := pk.ToBytes()
|
||||||
|
hash := sha256.Single(append(pkb[:], bap...))
|
||||||
|
var e error
|
||||||
|
var sign sig.Bytes
|
||||||
|
if sign, e = sig.Sign(key, hash); check(e) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
in = &Layer{
|
||||||
|
Key: pk,
|
||||||
|
AddrPort: ap,
|
||||||
|
Bytes: sign,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (im *Layer) Validate() bool {
|
||||||
|
bap, _ := im.AddrPort.MarshalBinary()
|
||||||
|
pkb := im.Key.ToBytes()
|
||||||
|
hash := sha256.Single(append(pkb[:], bap...))
|
||||||
|
key, e := im.Bytes.Recover(hash)
|
||||||
|
if check(e) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
kb := key.ToBytes()
|
||||||
|
if kb.Equals(pkb) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (im *Layer) Insert(o types.Onion) {}
|
func (im *Layer) Insert(o types.Onion) {}
|
||||||
func (im *Layer) Len() int { return Len }
|
func (im *Layer) Len() int { return Len }
|
||||||
|
|
||||||
func (im *Layer) Encode(b slice.Bytes, c *slice.Cursor) {
|
func (im *Layer) Encode(b slice.Bytes, c *slice.Cursor) {
|
||||||
splice.Splice(b, c).Magic(Magic).Pubkey(im.Key).AddrPort(im.AddrPort)
|
splice.Splice(b, c).
|
||||||
|
Magic(Magic).
|
||||||
|
Pubkey(im.Key).
|
||||||
|
AddrPort(im.AddrPort).
|
||||||
|
Signature(im.Bytes)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (im *Layer) Decode(b slice.Bytes, c *slice.Cursor) (e error) {
|
func (im *Layer) Decode(b slice.Bytes, c *slice.Cursor) (e error) {
|
||||||
splice.Splice(b, c).ReadPubkey(&im.Key).ReadAddrPort(&im.AddrPort)
|
splice.Splice(b, c).
|
||||||
|
ReadPubkey(&im.Key).
|
||||||
|
ReadAddrPort(&im.AddrPort).
|
||||||
|
ReadSignature(&im.Bytes)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
22
pkg/messages/intro/intro-message_test.go
Normal file
22
pkg/messages/intro/intro-message_test.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package intro
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git-indra.lan/indra-labs/indra/pkg/crypto/key/prv"
|
||||||
|
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLayer_Validate(t *testing.T) {
|
||||||
|
addr := slice.GenerateRandomAddrPortIPv4()
|
||||||
|
var e error
|
||||||
|
var idPrv *prv.Key
|
||||||
|
if idPrv, e = prv.GenerateKey(); check(e) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
im := New(idPrv, addr)
|
||||||
|
log.I.S(im)
|
||||||
|
if !im.Validate() {
|
||||||
|
t.Error("failed to validate")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -44,7 +44,7 @@ var (
|
|||||||
// is concealed to the hops except for the encryption crypt they decrypt using
|
// is concealed to the hops except for the encryption crypt they decrypt using
|
||||||
// their Payload key, delivered in this message.
|
// their Payload key, delivered in this message.
|
||||||
type Layer struct {
|
type Layer struct {
|
||||||
nonce.ID
|
nonce.ID // only used by a node
|
||||||
Hop byte // only used by a node
|
Hop byte // only used by a node
|
||||||
Header, Payload *prv.Key
|
Header, Payload *prv.Key
|
||||||
types.Onion
|
types.Onion
|
||||||
|
|||||||
157
pkg/relay/balance.go
Normal file
157
pkg/relay/balance.go
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
package relay
|
||||||
|
|
||||||
|
import (
|
||||||
|
"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/messages/balance"
|
||||||
|
"git-indra.lan/indra-labs/indra/pkg/messages/crypt"
|
||||||
|
"git-indra.lan/indra-labs/indra/pkg/messages/getbalance"
|
||||||
|
"git-indra.lan/indra-labs/indra/pkg/types"
|
||||||
|
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (eng *Engine) balance(on *balance.Layer,
|
||||||
|
b slice.Bytes, c *slice.Cursor, prev types.Onion) {
|
||||||
|
|
||||||
|
local := eng.GetLocalNodeAddress()
|
||||||
|
pending := eng.PendingResponses.Find(on.ID)
|
||||||
|
if pending != nil {
|
||||||
|
for i := range pending.Billable {
|
||||||
|
s := eng.FindSession(pending.Billable[i])
|
||||||
|
if s != nil {
|
||||||
|
switch {
|
||||||
|
case i < 2:
|
||||||
|
in := s.RelayRate * lnwire.MilliSatoshi(
|
||||||
|
pending.SentSize) / 1024 / 1024
|
||||||
|
eng.DecSession(s.ID, in, true, "reverse")
|
||||||
|
case i == 2:
|
||||||
|
in := s.RelayRate * lnwire.MilliSatoshi(
|
||||||
|
pending.SentSize/2) / 1024 / 1024
|
||||||
|
out := s.RelayRate * lnwire.MilliSatoshi(
|
||||||
|
len(b)/2) / 1024 / 1024
|
||||||
|
eng.DecSession(s.ID, in+out, true, "getbalance")
|
||||||
|
case i > 2:
|
||||||
|
out := s.RelayRate * lnwire.MilliSatoshi(
|
||||||
|
len(b)) / 1024 / 1024
|
||||||
|
eng.DecSession(s.ID, out, true, "reverse")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var se *Session
|
||||||
|
eng.IterateSessions(func(s *Session) bool {
|
||||||
|
if s.ID == on.ID {
|
||||||
|
log.D.F("%s received balance %s for session %s %s was %s",
|
||||||
|
local,
|
||||||
|
on.MilliSatoshi, on.ID, on.ConfID, s.Remaining)
|
||||||
|
se = s
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
eng.PendingResponses.Delete(pending.ID, nil)
|
||||||
|
if se != nil {
|
||||||
|
log.D.F("got %v, expected %v", se.Remaining, on.MilliSatoshi)
|
||||||
|
se.Remaining = on.MilliSatoshi
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBalance sends out a request in a similar way to SendExit except the node
|
||||||
|
// being queried can be any of the 5.
|
||||||
|
func GetBalance(id, confID 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 retNonces [3]nonce.IV
|
||||||
|
copy(retNonces[:], 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).
|
||||||
|
GetBalance(id, confID, prvs, pubs, retNonces).
|
||||||
|
ReverseCrypt(s[3], prvs[0], n[3], 0).
|
||||||
|
ReverseCrypt(s[4], prvs[1], n[4], 0).
|
||||||
|
ReverseCrypt(client, prvs[2], n[5], 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (eng *Engine) SendGetBalance(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)
|
||||||
|
confID := nonce.NewID()
|
||||||
|
o := GetBalance(target.ID, confID, se[5], c, eng.KeySet)
|
||||||
|
log.D.Ln("sending out getbalance onion")
|
||||||
|
res := eng.PostAcctOnion(o)
|
||||||
|
eng.SendWithOneHook(c[0].AddrPort, res, hook)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (eng *Engine) getbalance(on *getbalance.Layer,
|
||||||
|
b slice.Bytes, c *slice.Cursor, prev types.Onion) {
|
||||||
|
|
||||||
|
log.T.S(on)
|
||||||
|
var found bool
|
||||||
|
var bal *balance.Layer
|
||||||
|
eng.IterateSessions(func(s *Session) bool {
|
||||||
|
if s.ID == on.ID {
|
||||||
|
bal = &balance.Layer{
|
||||||
|
ID: on.ID,
|
||||||
|
ConfID: on.ConfID,
|
||||||
|
MilliSatoshi: s.Remaining,
|
||||||
|
}
|
||||||
|
found = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
if !found {
|
||||||
|
log.E.Ln("session not found", on.ID)
|
||||||
|
log.D.S(eng.Sessions)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
header := b[*c:c.Inc(crypt.ReverseHeaderLen)]
|
||||||
|
rb := FormatReply(header,
|
||||||
|
Encode(bal), on.Ciphers, on.Nonces)
|
||||||
|
rb = append(rb, slice.NoisePad(714-len(rb))...)
|
||||||
|
switch on1 := prev.(type) {
|
||||||
|
case *crypt.Layer:
|
||||||
|
sess := eng.FindSessionByHeader(on1.ToPriv)
|
||||||
|
if sess != nil {
|
||||||
|
in := sess.RelayRate *
|
||||||
|
lnwire.MilliSatoshi(len(b)) / 2 / 1024 / 1024
|
||||||
|
out := sess.RelayRate *
|
||||||
|
lnwire.MilliSatoshi(len(rb)) / 2 / 1024 / 1024
|
||||||
|
eng.DecSession(sess.ID, in+out, false, "getbalance")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
eng.IterateSessions(func(s *Session) bool {
|
||||||
|
if s.ID == on.ID {
|
||||||
|
bal = &balance.Layer{
|
||||||
|
ID: on.ID,
|
||||||
|
ConfID: on.ConfID,
|
||||||
|
MilliSatoshi: s.Remaining,
|
||||||
|
}
|
||||||
|
found = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
rb = FormatReply(header,
|
||||||
|
Encode(bal), on.Ciphers, on.Nonces)
|
||||||
|
rb = append(rb, slice.NoisePad(714-len(rb))...)
|
||||||
|
eng.handleMessage(rb, on)
|
||||||
|
}
|
||||||
@@ -10,9 +10,9 @@ import (
|
|||||||
"go.uber.org/atomic"
|
"go.uber.org/atomic"
|
||||||
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/crypto/key/prv"
|
"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/nonce"
|
||||||
"git-indra.lan/indra-labs/indra/pkg/crypto/sha256"
|
"git-indra.lan/indra-labs/indra/pkg/crypto/sha256"
|
||||||
|
"git-indra.lan/indra-labs/indra/pkg/messages/intro"
|
||||||
log2 "git-indra.lan/indra-labs/indra/pkg/proc/log"
|
log2 "git-indra.lan/indra-labs/indra/pkg/proc/log"
|
||||||
"git-indra.lan/indra-labs/indra/pkg/service"
|
"git-indra.lan/indra-labs/indra/pkg/service"
|
||||||
"git-indra.lan/indra-labs/indra/pkg/transport"
|
"git-indra.lan/indra-labs/indra/pkg/transport"
|
||||||
@@ -290,10 +290,10 @@ func TestClient_HiddenService(t *testing.T) {
|
|||||||
t.Error(e)
|
t.Error(e)
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
identPub := pub.Derive(identPrv)
|
|
||||||
id := nonce.NewID()
|
id := nonce.NewID()
|
||||||
|
il := intro.New(identPrv, clients[0].GetLocalNodeAddress())
|
||||||
clients[0].SendIntro(id, clients[0].Sessions[i+returns],
|
clients[0].SendIntro(id, clients[0].Sessions[i+returns],
|
||||||
identPub, func(id nonce.ID, b slice.Bytes) {
|
il, func(id nonce.ID, b slice.Bytes) {
|
||||||
log.I.Ln("success")
|
log.I.Ln("success")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -302,8 +302,9 @@ func TestClient_HiddenService(t *testing.T) {
|
|||||||
v.Shutdown()
|
v.Shutdown()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClient_HiddenServiceBroadcast(t *testing.T) {
|
func TestClient_HiddenServiceBroadcast(t *testing.T) {
|
||||||
log2.SetLogLevel(log2.Trace)
|
log2.SetLogLevel(log2.Info)
|
||||||
var clients []*Engine
|
var clients []*Engine
|
||||||
var e error
|
var e error
|
||||||
const returns = 2
|
const returns = 2
|
||||||
@@ -340,10 +341,12 @@ func TestClient_HiddenServiceBroadcast(t *testing.T) {
|
|||||||
t.Error(e)
|
t.Error(e)
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
identPub := pub.Derive(identPrv)
|
log2.SetLogLevel(log2.Trace)
|
||||||
|
// identPub := pub.Derive(identPrv)
|
||||||
id := nonce.NewID()
|
id := nonce.NewID()
|
||||||
|
il := intro.New(identPrv, clients[0].GetLocalNodeAddress())
|
||||||
clients[0].SendIntro(id, clients[0].Sessions[returns],
|
clients[0].SendIntro(id, clients[0].Sessions[returns],
|
||||||
identPub, func(id nonce.ID, b slice.Bytes) {
|
il, func(id nonce.ID, b slice.Bytes) {
|
||||||
log.I.Ln("success")
|
log.I.Ln("success")
|
||||||
})
|
})
|
||||||
time.Sleep(time.Second * 5)
|
time.Sleep(time.Second * 5)
|
||||||
|
|||||||
140
pkg/relay/exit.go
Normal file
140
pkg/relay/exit.go
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
package relay
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git-indra.lan/indra-labs/lnd/lnd/lnwire"
|
|
||||||
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/messages/balance"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/types"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (eng *Engine) balance(on *balance.Layer,
|
|
||||||
b slice.Bytes, c *slice.Cursor, prev types.Onion) {
|
|
||||||
|
|
||||||
local := eng.GetLocalNodeAddress()
|
|
||||||
pending := eng.PendingResponses.Find(on.ID)
|
|
||||||
if pending != nil {
|
|
||||||
for i := range pending.Billable {
|
|
||||||
s := eng.FindSession(pending.Billable[i])
|
|
||||||
if s != nil {
|
|
||||||
switch {
|
|
||||||
case i < 2:
|
|
||||||
in := s.RelayRate * lnwire.MilliSatoshi(
|
|
||||||
pending.SentSize) / 1024 / 1024
|
|
||||||
eng.DecSession(s.ID, in, true, "reverse")
|
|
||||||
case i == 2:
|
|
||||||
in := s.RelayRate * lnwire.MilliSatoshi(
|
|
||||||
pending.SentSize/2) / 1024 / 1024
|
|
||||||
out := s.RelayRate * lnwire.MilliSatoshi(
|
|
||||||
len(b)/2) / 1024 / 1024
|
|
||||||
eng.DecSession(s.ID, in+out, true, "getbalance")
|
|
||||||
case i > 2:
|
|
||||||
out := s.RelayRate * lnwire.MilliSatoshi(
|
|
||||||
len(b)) / 1024 / 1024
|
|
||||||
eng.DecSession(s.ID, out, true, "reverse")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var se *Session
|
|
||||||
eng.IterateSessions(func(s *Session) bool {
|
|
||||||
if s.ID == on.ID {
|
|
||||||
log.D.F("%s received balance %s for session %s %s was %s",
|
|
||||||
local,
|
|
||||||
on.MilliSatoshi, on.ID, on.ConfID, s.Remaining)
|
|
||||||
se = s
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
eng.PendingResponses.Delete(pending.ID, nil)
|
|
||||||
if se != nil {
|
|
||||||
log.D.F("got %v, expected %v", se.Remaining, on.MilliSatoshi)
|
|
||||||
se.Remaining = on.MilliSatoshi
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package relay
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/messages/confirm"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/types"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (eng *Engine) confirm(on *confirm.Layer,
|
|
||||||
b slice.Bytes, c *slice.Cursor, prev types.Onion) {
|
|
||||||
|
|
||||||
// When a confirmation arrives check if it is registered for and run the
|
|
||||||
// hook that was registered with it.
|
|
||||||
eng.PendingResponses.Delete(on.ID, nil)
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
package relay
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/messages/crypt"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/messages/magicbytes"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/messages/session"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/types"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (eng *Engine) crypt(on *crypt.Layer, b slice.Bytes,
|
|
||||||
c *slice.Cursor, prev types.Onion) {
|
|
||||||
|
|
||||||
// this is probably an encrypted crypt for us.
|
|
||||||
hdr, _, _, identity := eng.FindCloaked(on.Cloak)
|
|
||||||
if hdr == nil {
|
|
||||||
log.T.Ln("no matching key found from cloaked key")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
on.ToPriv = hdr
|
|
||||||
on.Decrypt(hdr, b, c)
|
|
||||||
if identity {
|
|
||||||
if string(b[*c:][:magicbytes.Len]) != session.MagicString {
|
|
||||||
log.T.Ln("dropping message due to identity key with" +
|
|
||||||
" no following session")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
eng.handleMessage(BudgeUp(b, *c), on)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
eng.handleMessage(BudgeUp(b, *c), on)
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package relay
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/messages/delay"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/types"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (eng *Engine) delay(on *delay.Layer, b slice.Bytes,
|
|
||||||
c *slice.Cursor, prev types.Onion) {
|
|
||||||
|
|
||||||
// this is a message to hold the message in the buffer until a duration
|
|
||||||
// elapses. The accounting for the remainder of the message adds a
|
|
||||||
// factor to the effective byte consumption in accordance with the time
|
|
||||||
// to be stored.
|
|
||||||
// todo: accounting
|
|
||||||
select {
|
|
||||||
case <-time.After(on.Duration):
|
|
||||||
}
|
|
||||||
eng.handleMessage(BudgeUp(b, *c), on)
|
|
||||||
}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
package relay
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"git-indra.lan/indra-labs/lnd/lnd/lnwire"
|
|
||||||
|
|
||||||
"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"
|
|
||||||
)
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
package relay
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git-indra.lan/indra-labs/lnd/lnd/lnwire"
|
|
||||||
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/messages/crypt"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/messages/forward"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/types"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (eng *Engine) forward(on *forward.Layer, b slice.Bytes,
|
|
||||||
c *slice.Cursor, prev types.Onion) {
|
|
||||||
|
|
||||||
// forward the whole buffer received onwards. Usually there will be a
|
|
||||||
// crypt.Layer under this which will be unwrapped by the receiver.
|
|
||||||
if on.AddrPort.String() == eng.GetLocalNodeAddress().String() {
|
|
||||||
// it is for us, we want to unwrap the next part.
|
|
||||||
eng.handleMessage(BudgeUp(b, *c), on)
|
|
||||||
} else {
|
|
||||||
switch on1 := prev.(type) {
|
|
||||||
case *crypt.Layer:
|
|
||||||
sess := eng.FindSessionByHeader(on1.ToPriv)
|
|
||||||
if sess != nil {
|
|
||||||
eng.DecSession(sess.ID,
|
|
||||||
eng.GetLocalNodeRelayRate()*lnwire.MilliSatoshi(len(b))/1024/1024,
|
|
||||||
false, "forward")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// we need to forward this message onion.
|
|
||||||
eng.Send(on.AddrPort, BudgeUp(b, *c))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
package relay
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git-indra.lan/indra-labs/lnd/lnd/lnwire"
|
|
||||||
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/messages/balance"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/messages/crypt"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/messages/getbalance"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/types"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (eng *Engine) getbalance(on *getbalance.Layer,
|
|
||||||
b slice.Bytes, c *slice.Cursor, prev types.Onion) {
|
|
||||||
|
|
||||||
log.T.S(on)
|
|
||||||
var found bool
|
|
||||||
var bal *balance.Layer
|
|
||||||
eng.IterateSessions(func(s *Session) bool {
|
|
||||||
if s.ID == on.ID {
|
|
||||||
bal = &balance.Layer{
|
|
||||||
ID: on.ID,
|
|
||||||
ConfID: on.ConfID,
|
|
||||||
MilliSatoshi: s.Remaining,
|
|
||||||
}
|
|
||||||
found = true
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
if !found {
|
|
||||||
log.E.Ln("session not found", on.ID)
|
|
||||||
log.D.S(eng.Sessions)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
header := b[*c:c.Inc(crypt.ReverseHeaderLen)]
|
|
||||||
rb := FormatReply(header,
|
|
||||||
Encode(bal), on.Ciphers, on.Nonces)
|
|
||||||
rb = append(rb, slice.NoisePad(714-len(rb))...)
|
|
||||||
switch on1 := prev.(type) {
|
|
||||||
case *crypt.Layer:
|
|
||||||
sess := eng.FindSessionByHeader(on1.ToPriv)
|
|
||||||
if sess != nil {
|
|
||||||
in := sess.RelayRate *
|
|
||||||
lnwire.MilliSatoshi(len(b)) / 2 / 1024 / 1024
|
|
||||||
out := sess.RelayRate *
|
|
||||||
lnwire.MilliSatoshi(len(rb)) / 2 / 1024 / 1024
|
|
||||||
eng.DecSession(sess.ID, in+out, false, "getbalance")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
eng.IterateSessions(func(s *Session) bool {
|
|
||||||
if s.ID == on.ID {
|
|
||||||
bal = &balance.Layer{
|
|
||||||
ID: on.ID,
|
|
||||||
ConfID: on.ConfID,
|
|
||||||
MilliSatoshi: s.Remaining,
|
|
||||||
}
|
|
||||||
found = true
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
rb = FormatReply(header,
|
|
||||||
Encode(bal), on.Ciphers, on.Nonces)
|
|
||||||
rb = append(rb, slice.NoisePad(714-len(rb))...)
|
|
||||||
eng.handleMessage(rb, on)
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
package relay
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/messages/hiddenservice"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/types"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (eng *Engine) hiddenservice(hs *hiddenservice.Layer, b slice.Bytes,
|
|
||||||
c *slice.Cursor, prev types.Onion) {
|
|
||||||
|
|
||||||
log.D.F("%s adding introduction for key %s", eng.GetLocalNodeAddress(),
|
|
||||||
hs.Identity.ToBase32())
|
|
||||||
eng.Introductions.AddIntro(hs.Identity, b[*c:])
|
|
||||||
log.I.Ln("stored new introduction, starting broadcast")
|
|
||||||
go eng.hiddenserviceBroadcaster(hs.Identity)
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
package relay
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/messages/intro"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/types"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (eng *Engine) intro(intr *intro.Layer, b slice.Bytes,
|
|
||||||
c *slice.Cursor, prev types.Onion) {
|
|
||||||
|
|
||||||
log.D.F("sending out intro to %s at %s to all known peers",
|
|
||||||
intr.Key.ToBase32(), intr.AddrPort.String())
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
package relay
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git-indra.lan/indra-labs/lnd/lnd/lnwire"
|
|
||||||
|
|
||||||
"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"
|
|
||||||
)
|
|
||||||
|
|
||||||
// response is a payload from an exit message.
|
|
||||||
func (eng *Engine) response(on *response.Layer, b slice.Bytes,
|
|
||||||
cur *slice.Cursor, prev types.Onion) {
|
|
||||||
|
|
||||||
pending := eng.PendingResponses.Find(on.ID)
|
|
||||||
log.T.F("searching for pending ID %x", on.ID)
|
|
||||||
if pending != nil {
|
|
||||||
for i := range pending.Billable {
|
|
||||||
s := eng.FindSession(pending.Billable[i])
|
|
||||||
if s != nil {
|
|
||||||
typ := "response"
|
|
||||||
relayRate := s.RelayRate
|
|
||||||
dataSize := len(b)
|
|
||||||
switch i {
|
|
||||||
case 0, 1:
|
|
||||||
dataSize = pending.SentSize
|
|
||||||
case 2:
|
|
||||||
for j := range s.Services {
|
|
||||||
if s.Services[j].Port == on.Port {
|
|
||||||
relayRate = s.Services[j].RelayRate / 2
|
|
||||||
typ = "exit"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
eng.DecSession(s.ID, relayRate*lnwire.
|
|
||||||
MilliSatoshi(dataSize)/1024/1024, true, typ)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
eng.PendingResponses.Delete(on.ID, on.Bytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
package relay
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git-indra.lan/indra-labs/lnd/lnd/lnwire"
|
|
||||||
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/crypto/ciph"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/messages/crypt"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/messages/reverse"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/types"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (eng *Engine) reverse(on *reverse.Layer, b slice.Bytes,
|
|
||||||
c *slice.Cursor, prev types.Onion) {
|
|
||||||
|
|
||||||
var e error
|
|
||||||
var on2 types.Onion
|
|
||||||
if on.AddrPort.String() == eng.GetLocalNodeAddress().String() {
|
|
||||||
if on2, e = Peel(b, c); check(e) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
switch on1 := on2.(type) {
|
|
||||||
case *crypt.Layer:
|
|
||||||
start := *c - crypt.ReverseLayerLen
|
|
||||||
first := *c
|
|
||||||
second := first + crypt.ReverseLayerLen
|
|
||||||
last := second + crypt.ReverseLayerLen
|
|
||||||
log.T.Ln("searching for reverse crypt keys")
|
|
||||||
hdr, pld, _, _ := eng.FindCloaked(on1.Cloak)
|
|
||||||
if hdr == nil || pld == nil {
|
|
||||||
log.E.F("failed to find key for %s",
|
|
||||||
eng.GetLocalNodeAddress().String())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// We need to find the PayloadPub to match.
|
|
||||||
on1.ToPriv = hdr
|
|
||||||
blk := ciph.GetBlock(on1.ToPriv, on1.FromPub)
|
|
||||||
// Decrypt using the Payload key and header nonce.
|
|
||||||
ciph.Encipher(blk, on1.Nonce,
|
|
||||||
b[*c:c.Inc(2*crypt.ReverseLayerLen)])
|
|
||||||
blk = ciph.GetBlock(pld, on1.FromPub)
|
|
||||||
ciph.Encipher(blk, on1.Nonce, b[*c:])
|
|
||||||
// shift the header segment upwards and pad the
|
|
||||||
// remainder.
|
|
||||||
copy(b[start:first], b[first:second])
|
|
||||||
copy(b[first:second], b[second:last])
|
|
||||||
copy(b[second:last], slice.NoisePad(crypt.ReverseLayerLen))
|
|
||||||
if b[start:start+2].String() != reverse.MagicString {
|
|
||||||
// It's for us!
|
|
||||||
log.T.Ln("handling response")
|
|
||||||
eng.handleMessage(BudgeUp(b, last), on1)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
sess := eng.FindSessionByHeader(hdr)
|
|
||||||
if sess != nil {
|
|
||||||
eng.DecSession(sess.ID,
|
|
||||||
eng.GetLocalNodeRelayRate()*lnwire.
|
|
||||||
MilliSatoshi(len(b))/1024/1024, false, "reverse")
|
|
||||||
eng.handleMessage(BudgeUp(b, start), on1)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// If a reverse is not followed by an onion crypt the
|
|
||||||
// message is incorrectly formed, just drop it.
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else if prev != nil {
|
|
||||||
// we need to forward this message onion.
|
|
||||||
log.T.Ln("forwarding reverse")
|
|
||||||
eng.Send(on.AddrPort, b)
|
|
||||||
} else {
|
|
||||||
log.E.Ln("we do not forward nonsense! scoff! snort!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
package relay
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/messages/session"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/types"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (eng *Engine) session(on *session.Layer, b slice.Bytes,
|
|
||||||
c *slice.Cursor, prev types.Onion) {
|
|
||||||
|
|
||||||
log.D.Ln(prev == nil)
|
|
||||||
log.T.F("incoming session %s", on.ID)
|
|
||||||
pi := eng.FindPendingPreimage(on.PreimageHash())
|
|
||||||
if pi != nil {
|
|
||||||
// We need to delete this first in case somehow two such messages arrive
|
|
||||||
// at the same time, and we end up with duplicate sessions.
|
|
||||||
eng.DeletePendingPayment(pi.Preimage)
|
|
||||||
log.D.F("Adding session %s to %s", pi.ID, eng.GetLocalNodeAddress())
|
|
||||||
eng.AddSession(NewSession(pi.ID,
|
|
||||||
eng.GetLocalNode(), pi.Amount, on.Header, on.Payload, on.Hop))
|
|
||||||
eng.handleMessage(BudgeUp(b, *c), on)
|
|
||||||
} else {
|
|
||||||
log.E.Ln("dropping session message without payment")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
179
pkg/relay/handlers.go
Normal file
179
pkg/relay/handlers.go
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
package relay
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git-indra.lan/indra-labs/lnd/lnd/lnwire"
|
||||||
|
|
||||||
|
"git-indra.lan/indra-labs/indra/pkg/crypto/ciph"
|
||||||
|
"git-indra.lan/indra-labs/indra/pkg/messages/confirm"
|
||||||
|
"git-indra.lan/indra-labs/indra/pkg/messages/crypt"
|
||||||
|
"git-indra.lan/indra-labs/indra/pkg/messages/delay"
|
||||||
|
"git-indra.lan/indra-labs/indra/pkg/messages/forward"
|
||||||
|
"git-indra.lan/indra-labs/indra/pkg/messages/magicbytes"
|
||||||
|
"git-indra.lan/indra-labs/indra/pkg/messages/response"
|
||||||
|
"git-indra.lan/indra-labs/indra/pkg/messages/reverse"
|
||||||
|
"git-indra.lan/indra-labs/indra/pkg/messages/session"
|
||||||
|
"git-indra.lan/indra-labs/indra/pkg/types"
|
||||||
|
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (eng *Engine) confirm(on *confirm.Layer,
|
||||||
|
b slice.Bytes, c *slice.Cursor, prev types.Onion) {
|
||||||
|
|
||||||
|
// When a confirmation arrives check if it is registered for and run the
|
||||||
|
// hook that was registered with it.
|
||||||
|
eng.PendingResponses.Delete(on.ID, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (eng *Engine) crypt(on *crypt.Layer, b slice.Bytes,
|
||||||
|
c *slice.Cursor, prev types.Onion) {
|
||||||
|
|
||||||
|
// this is probably an encrypted crypt for us.
|
||||||
|
hdr, _, _, identity := eng.FindCloaked(on.Cloak)
|
||||||
|
if hdr == nil {
|
||||||
|
log.T.Ln("no matching key found from cloaked key")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
on.ToPriv = hdr
|
||||||
|
on.Decrypt(hdr, b, c)
|
||||||
|
if identity {
|
||||||
|
if string(b[*c:][:magicbytes.Len]) != session.MagicString {
|
||||||
|
log.T.Ln("dropping message due to identity key with" +
|
||||||
|
" no following session")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
eng.handleMessage(BudgeUp(b, *c), on)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
eng.handleMessage(BudgeUp(b, *c), on)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (eng *Engine) delay(on *delay.Layer, b slice.Bytes,
|
||||||
|
c *slice.Cursor, prev types.Onion) {
|
||||||
|
|
||||||
|
// this is a message to hold the message in the buffer until a duration
|
||||||
|
// elapses. The accounting for the remainder of the message adds a
|
||||||
|
// factor to the effective byte consumption in accordance with the time
|
||||||
|
// to be stored.
|
||||||
|
// todo: accounting
|
||||||
|
select {
|
||||||
|
case <-time.After(on.Duration):
|
||||||
|
}
|
||||||
|
eng.handleMessage(BudgeUp(b, *c), on)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (eng *Engine) forward(on *forward.Layer, b slice.Bytes,
|
||||||
|
c *slice.Cursor, prev types.Onion) {
|
||||||
|
|
||||||
|
// forward the whole buffer received onwards. Usually there will be a
|
||||||
|
// crypt.Layer under this which will be unwrapped by the receiver.
|
||||||
|
if on.AddrPort.String() == eng.GetLocalNodeAddress().String() {
|
||||||
|
// it is for us, we want to unwrap the next part.
|
||||||
|
eng.handleMessage(BudgeUp(b, *c), on)
|
||||||
|
} else {
|
||||||
|
switch on1 := prev.(type) {
|
||||||
|
case *crypt.Layer:
|
||||||
|
sess := eng.FindSessionByHeader(on1.ToPriv)
|
||||||
|
if sess != nil {
|
||||||
|
eng.DecSession(sess.ID,
|
||||||
|
eng.GetLocalNodeRelayRate()*lnwire.MilliSatoshi(len(b))/1024/1024,
|
||||||
|
false, "forward")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// we need to forward this message onion.
|
||||||
|
eng.Send(on.AddrPort, BudgeUp(b, *c))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// response is a payload from an exit message.
|
||||||
|
func (eng *Engine) response(on *response.Layer, b slice.Bytes,
|
||||||
|
cur *slice.Cursor, prev types.Onion) {
|
||||||
|
|
||||||
|
pending := eng.PendingResponses.Find(on.ID)
|
||||||
|
log.T.F("searching for pending ID %x", on.ID)
|
||||||
|
if pending != nil {
|
||||||
|
for i := range pending.Billable {
|
||||||
|
s := eng.FindSession(pending.Billable[i])
|
||||||
|
if s != nil {
|
||||||
|
typ := "response"
|
||||||
|
relayRate := s.RelayRate
|
||||||
|
dataSize := len(b)
|
||||||
|
switch i {
|
||||||
|
case 0, 1:
|
||||||
|
dataSize = pending.SentSize
|
||||||
|
case 2:
|
||||||
|
for j := range s.Services {
|
||||||
|
if s.Services[j].Port == on.Port {
|
||||||
|
relayRate = s.Services[j].RelayRate / 2
|
||||||
|
typ = "exit"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
eng.DecSession(s.ID, relayRate*lnwire.MilliSatoshi(dataSize)/1024/1024, true, typ)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
eng.PendingResponses.Delete(on.ID, on.Bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (eng *Engine) reverse(on *reverse.Layer, b slice.Bytes,
|
||||||
|
c *slice.Cursor, prev types.Onion) {
|
||||||
|
|
||||||
|
var e error
|
||||||
|
var on2 types.Onion
|
||||||
|
if on.AddrPort.String() == eng.GetLocalNodeAddress().String() {
|
||||||
|
if on2, e = Peel(b, c); check(e) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch on1 := on2.(type) {
|
||||||
|
case *crypt.Layer:
|
||||||
|
start := *c - crypt.ReverseLayerLen
|
||||||
|
first := *c
|
||||||
|
second := first + crypt.ReverseLayerLen
|
||||||
|
last := second + crypt.ReverseLayerLen
|
||||||
|
log.T.Ln("searching for reverse crypt keys")
|
||||||
|
hdr, pld, _, _ := eng.FindCloaked(on1.Cloak)
|
||||||
|
if hdr == nil || pld == nil {
|
||||||
|
log.E.F("failed to find key for %s",
|
||||||
|
eng.GetLocalNodeAddress().String())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// We need to find the PayloadPub to match.
|
||||||
|
on1.ToPriv = hdr
|
||||||
|
blk := ciph.GetBlock(on1.ToPriv, on1.FromPub)
|
||||||
|
// Decrypt using the Payload key and header nonce.
|
||||||
|
ciph.Encipher(blk, on1.Nonce,
|
||||||
|
b[*c:c.Inc(2*crypt.ReverseLayerLen)])
|
||||||
|
blk = ciph.GetBlock(pld, on1.FromPub)
|
||||||
|
ciph.Encipher(blk, on1.Nonce, b[*c:])
|
||||||
|
// shift the header segment upwards and pad the
|
||||||
|
// remainder.
|
||||||
|
copy(b[start:first], b[first:second])
|
||||||
|
copy(b[first:second], b[second:last])
|
||||||
|
copy(b[second:last], slice.NoisePad(crypt.ReverseLayerLen))
|
||||||
|
if b[start:start+2].String() != reverse.MagicString {
|
||||||
|
// It's for us!
|
||||||
|
log.T.Ln("handling response")
|
||||||
|
eng.handleMessage(BudgeUp(b, last), on1)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
sess := eng.FindSessionByHeader(hdr)
|
||||||
|
if sess != nil {
|
||||||
|
eng.DecSession(sess.ID,
|
||||||
|
eng.GetLocalNodeRelayRate()*lnwire.MilliSatoshi(len(b))/1024/1024, false, "reverse")
|
||||||
|
eng.handleMessage(BudgeUp(b, start), on1)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// If a reverse is not followed by an onion crypt the
|
||||||
|
// message is incorrectly formed, just drop it.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else if prev != nil {
|
||||||
|
// we need to forward this message onion.
|
||||||
|
log.T.Ln("forwarding reverse")
|
||||||
|
eng.Send(on.AddrPort, b)
|
||||||
|
} else {
|
||||||
|
log.E.Ln("we do not forward nonsense! scoff! snort!")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
package relay
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"git-indra.lan/indra-labs/lnd/lnd/lnwire"
|
|
||||||
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/crypto/nonce"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/messages/session"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/util/cryptorand"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
|
||||||
)
|
|
||||||
|
|
||||||
// BuyNewSessions performs the initial purchase of 5 sessions as well as adding
|
|
||||||
// different hop numbers to relays with existing sessions. Note that all 5 of
|
|
||||||
// the sessions will be paid the amount specified, not divided up.
|
|
||||||
func (eng *Engine) BuyNewSessions(amount lnwire.MilliSatoshi,
|
|
||||||
hook func()) (e error) {
|
|
||||||
|
|
||||||
var nodes [5]*Node
|
|
||||||
nodes = eng.SessionManager.SelectUnusedCircuit()
|
|
||||||
for i := range nodes {
|
|
||||||
if nodes[i] == nil {
|
|
||||||
e = fmt.Errorf("failed to find nodes %d", i)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Get a random return hop session (index 5).
|
|
||||||
var returnSession *Session
|
|
||||||
returnHops := eng.SessionManager.GetSessionsAtHop(5)
|
|
||||||
if len(returnHops) > 1 {
|
|
||||||
cryptorand.Shuffle(len(returnHops), func(i, j int) {
|
|
||||||
returnHops[i], returnHops[j] = returnHops[j], returnHops[i]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// There must be at least one, and if there was more than one the first
|
|
||||||
// index of returnHops will be a randomly selected one.
|
|
||||||
returnSession = returnHops[0]
|
|
||||||
conf := nonce.NewID()
|
|
||||||
var s [5]*session.Layer
|
|
||||||
for i := range s {
|
|
||||||
s[i] = session.New(byte(i))
|
|
||||||
}
|
|
||||||
var confirmChans [5]chan bool
|
|
||||||
var pendingConfirms int
|
|
||||||
for i := range nodes {
|
|
||||||
confirmChans[i] = nodes[i].
|
|
||||||
PaymentChan.Send(amount, s[i])
|
|
||||||
pendingConfirms++
|
|
||||||
}
|
|
||||||
var success bool
|
|
||||||
for pendingConfirms > 0 {
|
|
||||||
// The confirmation channels will signal upon success or failure
|
|
||||||
// according to the LN payment send protocol once either the HTLCs
|
|
||||||
// confirm on the way back or the path fails.
|
|
||||||
select {
|
|
||||||
case success = <-confirmChans[0]:
|
|
||||||
if success {
|
|
||||||
pendingConfirms--
|
|
||||||
}
|
|
||||||
case success = <-confirmChans[1]:
|
|
||||||
if success {
|
|
||||||
pendingConfirms--
|
|
||||||
}
|
|
||||||
case success = <-confirmChans[2]:
|
|
||||||
if success {
|
|
||||||
pendingConfirms--
|
|
||||||
}
|
|
||||||
case success = <-confirmChans[3]:
|
|
||||||
if success {
|
|
||||||
pendingConfirms--
|
|
||||||
}
|
|
||||||
case success = <-confirmChans[4]:
|
|
||||||
if success {
|
|
||||||
pendingConfirms--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// todo: handle payment failures!
|
|
||||||
o := SendKeys(conf, s, returnSession, nodes[:], eng.KeySet)
|
|
||||||
res := eng.PostAcctOnion(o)
|
|
||||||
eng.SendWithOneHook(nodes[0].AddrPort, res, func(id nonce.ID, b slice.Bytes) {
|
|
||||||
eng.SessionManager.Lock()
|
|
||||||
defer eng.SessionManager.Unlock()
|
|
||||||
var sessions [5]*Session
|
|
||||||
for i := range nodes {
|
|
||||||
log.D.F("confirming and storing session at hop %d %s for %s with"+
|
|
||||||
" %v initial"+
|
|
||||||
" balance", i, s[i].ID, nodes[i].AddrPort.String(), amount)
|
|
||||||
sessions[i] = NewSession(s[i].ID, nodes[i], amount,
|
|
||||||
s[i].Header, s[i].Payload, byte(i))
|
|
||||||
eng.SessionManager.Add(sessions[i])
|
|
||||||
eng.Sessions = append(eng.Sessions, sessions[i])
|
|
||||||
eng.SessionManager.PendingPayments.Delete(s[i].PreimageHash())
|
|
||||||
}
|
|
||||||
hook()
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
package relay
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/netip"
|
|
||||||
"runtime"
|
|
||||||
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Send a message to a peer via their AddrPort.
|
|
||||||
func (eng *Engine) Send(addr *netip.AddrPort, b slice.Bytes) {
|
|
||||||
// first search if we already have the node available with connection
|
|
||||||
// open.
|
|
||||||
as := addr.String()
|
|
||||||
eng.ForEachNode(func(n *Node) bool {
|
|
||||||
if as == n.AddrPort.String() {
|
|
||||||
_, f, l, _ := runtime.Caller(1)
|
|
||||||
log.T.F("%s sending message to %v %s:%d",
|
|
||||||
eng.GetLocalNode().AddrPort.String(), addr, f, l)
|
|
||||||
n.Transport.Send(b)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
package relay
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/crypto/nonce"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
|
||||||
)
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package relay
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/crypto/nonce"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (eng *Engine) SendGetBalance(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)
|
|
||||||
confID := nonce.NewID()
|
|
||||||
o := GetBalance(target.ID, confID, se[5], c, eng.KeySet)
|
|
||||||
log.D.Ln("sending out getbalance onion")
|
|
||||||
res := eng.PostAcctOnion(o)
|
|
||||||
eng.SendWithOneHook(c[0].AddrPort, res, hook)
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
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)) {
|
|
||||||
|
|
||||||
log.I.Ln(target.Hop)
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
@@ -1,104 +0,0 @@
|
|||||||
package relay
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/netip"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"git-indra.lan/indra-labs/lnd/lnd/lnwire"
|
|
||||||
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/crypto/nonce"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/messages/balance"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/messages/confirm"
|
|
||||||
"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/forward"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/messages/getbalance"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/messages/reverse"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (eng *Engine) SendOnion(ap *netip.AddrPort, o Skins,
|
|
||||||
responseHook func(id nonce.ID, b slice.Bytes), timeout time.Duration) {
|
|
||||||
|
|
||||||
if timeout == 0 {
|
|
||||||
timeout = DefaultTimeout
|
|
||||||
}
|
|
||||||
b := Encode(o.Assemble())
|
|
||||||
var billable []nonce.ID
|
|
||||||
var ret nonce.ID
|
|
||||||
var last nonce.ID
|
|
||||||
var port uint16
|
|
||||||
var postAcct []func()
|
|
||||||
var sessions Sessions
|
|
||||||
// do client accounting
|
|
||||||
skip := false
|
|
||||||
for i := range o {
|
|
||||||
if skip {
|
|
||||||
skip = false
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
switch on := o[i].(type) {
|
|
||||||
case *crypt.Layer:
|
|
||||||
s := eng.FindSessionByHeaderPub(on.ToHeaderPub)
|
|
||||||
if s == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
sessions = append(sessions, s)
|
|
||||||
// The last hop needs no accounting as it's us!
|
|
||||||
if i == len(o)-1 {
|
|
||||||
// The session used for the last hop is stored, however.
|
|
||||||
ret = s.ID
|
|
||||||
billable = append(billable, s.ID)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
switch on2 := o[i+1].(type) {
|
|
||||||
case *forward.Layer:
|
|
||||||
billable = append(billable, s.ID)
|
|
||||||
postAcct = append(postAcct,
|
|
||||||
func() {
|
|
||||||
eng.DecSession(s.ID,
|
|
||||||
s.RelayRate*
|
|
||||||
lnwire.MilliSatoshi(len(b))/1024/1024, true,
|
|
||||||
"forward")
|
|
||||||
})
|
|
||||||
case *reverse.Layer:
|
|
||||||
billable = append(billable, s.ID)
|
|
||||||
case *exit.Layer:
|
|
||||||
for j := range s.Services {
|
|
||||||
if s.Services[j].Port != on2.Port {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
port = on2.Port
|
|
||||||
postAcct = append(postAcct,
|
|
||||||
func() {
|
|
||||||
eng.DecSession(s.ID,
|
|
||||||
s.Services[j].RelayRate*
|
|
||||||
lnwire.MilliSatoshi(len(b)/2)/1024/1024,
|
|
||||||
true, "exit")
|
|
||||||
})
|
|
||||||
break
|
|
||||||
}
|
|
||||||
billable = append(billable, s.ID)
|
|
||||||
last = on2.ID
|
|
||||||
skip = true
|
|
||||||
case *getbalance.Layer:
|
|
||||||
last = s.ID
|
|
||||||
billable = append(billable, s.ID)
|
|
||||||
skip = true
|
|
||||||
}
|
|
||||||
case *confirm.Layer:
|
|
||||||
last = on.ID
|
|
||||||
case *balance.Layer:
|
|
||||||
last = on.ID
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if responseHook == nil {
|
|
||||||
responseHook = func(_ nonce.ID, _ slice.Bytes) {
|
|
||||||
log.D.Ln("nil response hook")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
eng.PendingResponses.Add(last, len(b), sessions, billable, ret, port,
|
|
||||||
responseHook, postAcct)
|
|
||||||
log.T.Ln("sending out onion")
|
|
||||||
eng.Send(ap, b)
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
package relay
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/crypto/nonce"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (eng *Engine) SendPing(c Circuit, hook Callback) {
|
|
||||||
hops := []byte{0, 1, 2, 3, 4, 5}
|
|
||||||
s := make(Sessions, len(hops))
|
|
||||||
copy(s, c[:])
|
|
||||||
se := eng.SelectHops(hops, s)
|
|
||||||
copy(c[:], se)
|
|
||||||
confID := nonce.NewID()
|
|
||||||
o := Ping(confID, se[len(se)-1], c, eng.KeySet)
|
|
||||||
res := eng.PostAcctOnion(o)
|
|
||||||
eng.SendWithOneHook(c[0].AddrPort, res, hook)
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
package relay
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/cybriq/qu"
|
|
||||||
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/crypto/key/pub"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/messages/intro"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/util/cryptorand"
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Referrers map[pub.Bytes][]pub.Bytes
|
|
||||||
|
|
||||||
func (eng *Engine) hiddenserviceBroadcaster(hsk *pub.Key) {
|
|
||||||
log.D.F("propagating hidden service introduction for %x", hsk.ToBytes())
|
|
||||||
done := qu.T()
|
|
||||||
me := eng.GetLocalNodeAddress()
|
|
||||||
intr := &intro.Layer{
|
|
||||||
Key: hsk, AddrPort: me,
|
|
||||||
}
|
|
||||||
msg := make(slice.Bytes, intro.Len)
|
|
||||||
c := slice.NewCursor()
|
|
||||||
intr.Encode(msg, c)
|
|
||||||
nPeers := eng.NodesLen()
|
|
||||||
peerIndices := make([]int, nPeers)
|
|
||||||
for i := 0; i < nPeers; i++ {
|
|
||||||
peerIndices[i] = i
|
|
||||||
}
|
|
||||||
cryptorand.Shuffle(nPeers, func(i, j int) {
|
|
||||||
peerIndices[i], peerIndices[j] = peerIndices[j], peerIndices[i]
|
|
||||||
})
|
|
||||||
// Since relays will also gossip this information, we will start a ticker
|
|
||||||
// that sends out the hidden service introduction once a second until it
|
|
||||||
// runs out of known relays to gossip to.
|
|
||||||
ticker := time.NewTicker(time.Second)
|
|
||||||
var cursor int
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-eng.C.Wait():
|
|
||||||
return
|
|
||||||
case <-done:
|
|
||||||
return
|
|
||||||
case <-ticker.C:
|
|
||||||
n := eng.FindNodeByIndex(peerIndices[cursor])
|
|
||||||
n.Transport.Send(msg)
|
|
||||||
cursor++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
89
pkg/relay/hiddenservice.go
Normal file
89
pkg/relay/hiddenservice.go
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
package relay
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/cybriq/qu"
|
||||||
|
|
||||||
|
"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/messages/hiddenservice"
|
||||||
|
"git-indra.lan/indra-labs/indra/pkg/messages/intro"
|
||||||
|
"git-indra.lan/indra-labs/indra/pkg/types"
|
||||||
|
"git-indra.lan/indra-labs/indra/pkg/util/cryptorand"
|
||||||
|
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Referrers map[pub.Bytes][]pub.Bytes
|
||||||
|
|
||||||
|
func (eng *Engine) hiddenserviceBroadcaster(hs *intro.Layer) {
|
||||||
|
log.D.F("propagating hidden service introduction for %x", hs.Key.ToBytes())
|
||||||
|
done := qu.T()
|
||||||
|
intr := &intro.Layer{
|
||||||
|
Key: hs.Key, AddrPort: hs.AddrPort, Bytes: hs.Bytes,
|
||||||
|
}
|
||||||
|
msg := make(slice.Bytes, intro.Len)
|
||||||
|
c := slice.NewCursor()
|
||||||
|
intr.Encode(msg, c)
|
||||||
|
nPeers := eng.NodesLen()
|
||||||
|
peerIndices := make([]int, nPeers)
|
||||||
|
for i := 0; i < nPeers; i++ {
|
||||||
|
peerIndices[i] = i
|
||||||
|
}
|
||||||
|
cryptorand.Shuffle(nPeers, func(i, j int) {
|
||||||
|
peerIndices[i], peerIndices[j] = peerIndices[j], peerIndices[i]
|
||||||
|
})
|
||||||
|
// Since relays will also gossip this information, we will start a ticker
|
||||||
|
// that sends out the hidden service introduction once a second until it
|
||||||
|
// runs out of known relays to gossip to.
|
||||||
|
ticker := time.NewTicker(time.Second)
|
||||||
|
var cursor int
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-eng.C.Wait():
|
||||||
|
return
|
||||||
|
case <-done:
|
||||||
|
return
|
||||||
|
case <-ticker.C:
|
||||||
|
n := eng.FindNodeByIndex(peerIndices[cursor])
|
||||||
|
n.Transport.Send(msg)
|
||||||
|
cursor++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func HiddenService(id nonce.ID, il *intro.Layer, 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).
|
||||||
|
HiddenService(id, il, 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (eng *Engine) hiddenservice(hs *hiddenservice.Layer, b slice.Bytes,
|
||||||
|
c *slice.Cursor, prev types.Onion) {
|
||||||
|
|
||||||
|
log.D.F("%s adding introduction for key %s", eng.GetLocalNodeAddress(),
|
||||||
|
hs.Layer.Key.ToBase32())
|
||||||
|
eng.Introductions.AddIntro(hs.Layer.Key, b[*c:])
|
||||||
|
log.I.Ln("stored new introduction, starting broadcast")
|
||||||
|
go eng.hiddenserviceBroadcaster(&hs.Layer)
|
||||||
|
}
|
||||||
@@ -5,6 +5,8 @@ import (
|
|||||||
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/crypto/key/pub"
|
"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/nonce"
|
||||||
|
"git-indra.lan/indra-labs/indra/pkg/messages/intro"
|
||||||
|
"git-indra.lan/indra-labs/indra/pkg/types"
|
||||||
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -65,3 +67,27 @@ func (in *Introductions) AddNotified(nodeID nonce.ID, ident pub.Bytes) {
|
|||||||
}
|
}
|
||||||
in.Unlock()
|
in.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (eng *Engine) SendIntro(id nonce.ID, target *Session, intr *intro.Layer,
|
||||||
|
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, intr, 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (eng *Engine) intro(intr *intro.Layer, b slice.Bytes,
|
||||||
|
c *slice.Cursor, prev types.Onion) {
|
||||||
|
|
||||||
|
if intr.Validate() {
|
||||||
|
log.D.F("sending out intro to %s at %s to all known peers",
|
||||||
|
intr.Key.ToBase32(), intr.AddrPort.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
package relay
|
|
||||||
|
|
||||||
import (
|
|
||||||
"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"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetBalance sends out a request in a similar way to SendExit except the node
|
|
||||||
// being queried can be any of the 5.
|
|
||||||
func GetBalance(id, confID 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 retNonces [3]nonce.IV
|
|
||||||
copy(retNonces[:], 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).
|
|
||||||
GetBalance(id, confID, prvs, pubs, retNonces).
|
|
||||||
ReverseCrypt(s[3], prvs[0], n[3], 0).
|
|
||||||
ReverseCrypt(s[4], prvs[1], n[4], 0).
|
|
||||||
ReverseCrypt(client, prvs[2], n[5], 0)
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
package relay
|
|
||||||
|
|
||||||
import (
|
|
||||||
"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"
|
|
||||||
)
|
|
||||||
|
|
||||||
func HiddenService(id nonce.ID, ident *pub.Key, 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).
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
package relay
|
|
||||||
|
|
||||||
import (
|
|
||||||
"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/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)
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
package relay
|
|
||||||
|
|
||||||
import (
|
|
||||||
"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/messages/session"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SendKeys provides a pair of private keys that will be used to generate the
|
|
||||||
// Purchase header bytes and to generate the ciphers provided in the Purchase
|
|
||||||
// message to encrypt the Session that is returned.
|
|
||||||
//
|
|
||||||
// The OnionSkin key, its cloaked public key counterpart used in the ToHeaderPub
|
|
||||||
// field of the Purchase message preformed header bytes, but the Ciphers
|
|
||||||
// provided in the Purchase message, for encrypting the Session to be returned,
|
|
||||||
// uses the Payload key, along with the public key found in the encrypted crypt
|
|
||||||
// of the header for the Reverse relay.
|
|
||||||
//
|
|
||||||
// This message's last crypt is a Confirmation, which allows the client to know
|
|
||||||
// that the keys were successfully delivered.
|
|
||||||
//
|
|
||||||
// This is the only onion that uses the node identity keys. The payment preimage
|
|
||||||
// hash must be available or the relay should not forward the remainder of the
|
|
||||||
// packet.
|
|
||||||
//
|
|
||||||
// If hdr/pld cipher keys are nil there must be a HeaderPub available on the
|
|
||||||
// session for the hop. This allows this function to send keys to any number of
|
|
||||||
// hops, but the very first SendKeys must have all in order to create the first
|
|
||||||
// set of sessions. This is by way of indicating to not use the IdentityPub but
|
|
||||||
// the HeaderPub instead. Not allowing free relay at all prevents spam attacks.
|
|
||||||
func SendKeys(id nonce.ID, s [5]*session.Layer,
|
|
||||||
client *Session, hop []*Node, ks *signer.KeySet) Skins {
|
|
||||||
|
|
||||||
n := GenNonces(6)
|
|
||||||
sk := Skins{}
|
|
||||||
for i := range s {
|
|
||||||
if i == 0 {
|
|
||||||
sk = sk.Crypt(hop[i].IdentityPub, nil, ks.Next(),
|
|
||||||
n[i], 0).Session(s[i])
|
|
||||||
} else {
|
|
||||||
sk = sk.ForwardSession(hop[i], ks.Next(), n[i], s[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return sk.
|
|
||||||
ForwardCrypt(client, ks.Next(), n[5]).
|
|
||||||
Confirmation(id, 0)
|
|
||||||
}
|
|
||||||
@@ -26,3 +26,15 @@ func Ping(id nonce.ID, client *Session, s Circuit,
|
|||||||
ForwardCrypt(client, ks.Next(), n[5]).
|
ForwardCrypt(client, ks.Next(), n[5]).
|
||||||
Confirmation(id, 0)
|
Confirmation(id, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (eng *Engine) SendPing(c Circuit, hook Callback) {
|
||||||
|
hops := []byte{0, 1, 2, 3, 4, 5}
|
||||||
|
s := make(Sessions, len(hops))
|
||||||
|
copy(s, c[:])
|
||||||
|
se := eng.SelectHops(hops, s)
|
||||||
|
copy(c[:], se)
|
||||||
|
confID := nonce.NewID()
|
||||||
|
o := Ping(confID, se[len(se)-1], c, eng.KeySet)
|
||||||
|
res := eng.PostAcctOnion(o)
|
||||||
|
eng.SendWithOneHook(c[0].AddrPort, res, hook)
|
||||||
|
}
|
||||||
@@ -1,8 +1,42 @@
|
|||||||
package relay
|
package relay
|
||||||
|
|
||||||
import (
|
import "git-indra.lan/indra-labs/indra/pkg/util/cryptorand"
|
||||||
"git-indra.lan/indra-labs/indra/pkg/util/cryptorand"
|
|
||||||
)
|
// SelectUnusedCircuit accepts an array of 5 Node entries where all or some are
|
||||||
|
// empty and picks nodes for the remainder that do not have a hop at that
|
||||||
|
// position.
|
||||||
|
func (sm *SessionManager) SelectUnusedCircuit() (c [5]*Node) {
|
||||||
|
sm.Lock()
|
||||||
|
defer sm.Unlock()
|
||||||
|
// Create a shuffled slice of Nodes to randomise the selection process.
|
||||||
|
nodeList := make([]*Node, len(sm.nodes)-1)
|
||||||
|
copy(nodeList, sm.nodes[1:])
|
||||||
|
for i := range nodeList {
|
||||||
|
if _, ok := sm.SessionCache[nodeList[i].ID]; !ok {
|
||||||
|
log.T.F("adding session cache entry for node %s", nodeList[i].ID)
|
||||||
|
sm.SessionCache[nodeList[i].ID] = &Circuit{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var counter int
|
||||||
|
out:
|
||||||
|
for counter < 5 {
|
||||||
|
for i := range sm.SessionCache {
|
||||||
|
if counter == 5 {
|
||||||
|
break out
|
||||||
|
}
|
||||||
|
if sm.SessionCache[i][counter] == nil {
|
||||||
|
for j := range nodeList {
|
||||||
|
if nodeList[j].ID == i {
|
||||||
|
c[counter] = nodeList[j]
|
||||||
|
counter++
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (sm *SessionManager) SelectHops(hops []byte,
|
func (sm *SessionManager) SelectHops(hops []byte,
|
||||||
alreadyHave Sessions) (so Sessions) {
|
alreadyHave Sessions) (so Sessions) {
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
package relay
|
|
||||||
|
|
||||||
// SelectUnusedCircuit accepts an array of 5 Node entries where all or some are
|
|
||||||
// empty and picks nodes for the remainder that do not have a hop at that
|
|
||||||
// position.
|
|
||||||
func (sm *SessionManager) SelectUnusedCircuit() (c [5]*Node) {
|
|
||||||
sm.Lock()
|
|
||||||
defer sm.Unlock()
|
|
||||||
// Create a shuffled slice of Nodes to randomise the selection process.
|
|
||||||
nodeList := make([]*Node, len(sm.nodes)-1)
|
|
||||||
copy(nodeList, sm.nodes[1:])
|
|
||||||
for i := range nodeList {
|
|
||||||
if _, ok := sm.SessionCache[nodeList[i].ID]; !ok {
|
|
||||||
log.T.F("adding session cache entry for node %s", nodeList[i].ID)
|
|
||||||
sm.SessionCache[nodeList[i].ID] = &Circuit{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var counter int
|
|
||||||
out:
|
|
||||||
for counter < 5 {
|
|
||||||
for i := range sm.SessionCache {
|
|
||||||
if counter == 5 {
|
|
||||||
break out
|
|
||||||
}
|
|
||||||
if sm.SessionCache[i][counter] == nil {
|
|
||||||
for j := range nodeList {
|
|
||||||
if nodeList[j].ID == i {
|
|
||||||
c[counter] = nodeList[j]
|
|
||||||
counter++
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
@@ -2,11 +2,29 @@ package relay
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
"git-indra.lan/indra-labs/indra/pkg/crypto/nonce"
|
"git-indra.lan/indra-labs/indra/pkg/crypto/nonce"
|
||||||
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Send a message to a peer via their AddrPort.
|
||||||
|
func (eng *Engine) Send(addr *netip.AddrPort, b slice.Bytes) {
|
||||||
|
// first search if we already have the node available with connection
|
||||||
|
// open.
|
||||||
|
as := addr.String()
|
||||||
|
eng.ForEachNode(func(n *Node) bool {
|
||||||
|
if as == n.AddrPort.String() {
|
||||||
|
_, f, l, _ := runtime.Caller(1)
|
||||||
|
log.T.F("%s sending message to %v %s:%d",
|
||||||
|
eng.GetLocalNode().AddrPort.String(), addr, f, l)
|
||||||
|
n.Transport.Send(b)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// SendWithOneHook is used for onions with only one confirmation hook. Usually
|
// SendWithOneHook is used for onions with only one confirmation hook. Usually
|
||||||
// as returned from PostAcctOnion this is the last, confirmation or response
|
// as returned from PostAcctOnion this is the last, confirmation or response
|
||||||
// layer in an onion.Skins.
|
// layer in an onion.Skins.
|
||||||
@@ -2,13 +2,19 @@ package relay
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"git-indra.lan/indra-labs/lnd/lnd/lnwire"
|
"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/prv"
|
||||||
"git-indra.lan/indra-labs/indra/pkg/crypto/key/pub"
|
"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/nonce"
|
||||||
"git-indra.lan/indra-labs/indra/pkg/crypto/sha256"
|
"git-indra.lan/indra-labs/indra/pkg/crypto/sha256"
|
||||||
|
"git-indra.lan/indra-labs/indra/pkg/messages/session"
|
||||||
|
"git-indra.lan/indra-labs/indra/pkg/types"
|
||||||
|
"git-indra.lan/indra-labs/indra/pkg/util/cryptorand"
|
||||||
|
"git-indra.lan/indra-labs/indra/pkg/util/slice"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A Session keeps track of a connection session. It specifically maintains the
|
// A Session keeps track of a connection session. It specifically maintains the
|
||||||
@@ -111,3 +117,149 @@ func (c Circuit) String() (o string) {
|
|||||||
|
|
||||||
// Sessions are arbitrary length lists of sessions.
|
// Sessions are arbitrary length lists of sessions.
|
||||||
type Sessions []*Session
|
type Sessions []*Session
|
||||||
|
|
||||||
|
func (eng *Engine) session(on *session.Layer, b slice.Bytes,
|
||||||
|
c *slice.Cursor, prev types.Onion) {
|
||||||
|
|
||||||
|
log.D.Ln(prev == nil)
|
||||||
|
log.T.F("incoming session %x", on.PreimageHash())
|
||||||
|
pi := eng.FindPendingPreimage(on.PreimageHash())
|
||||||
|
if pi != nil {
|
||||||
|
// We need to delete this first in case somehow two such messages arrive
|
||||||
|
// at the same time, and we end up with duplicate sessions.
|
||||||
|
eng.DeletePendingPayment(pi.Preimage)
|
||||||
|
log.D.F("Adding session %s to %s", pi.ID, eng.GetLocalNodeAddress())
|
||||||
|
eng.AddSession(NewSession(pi.ID,
|
||||||
|
eng.GetLocalNode(), pi.Amount, on.Header, on.Payload, on.Hop))
|
||||||
|
eng.handleMessage(BudgeUp(b, *c), on)
|
||||||
|
} else {
|
||||||
|
log.E.Ln("dropping session message without payment")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuyNewSessions performs the initial purchase of 5 sessions as well as adding
|
||||||
|
// different hop numbers to relays with existing sessions. Note that all 5 of
|
||||||
|
// the sessions will be paid the amount specified, not divided up.
|
||||||
|
func (eng *Engine) BuyNewSessions(amount lnwire.MilliSatoshi,
|
||||||
|
hook func()) (e error) {
|
||||||
|
|
||||||
|
var nodes [5]*Node
|
||||||
|
nodes = eng.SessionManager.SelectUnusedCircuit()
|
||||||
|
for i := range nodes {
|
||||||
|
if nodes[i] == nil {
|
||||||
|
e = fmt.Errorf("failed to find nodes %d", i)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Get a random return hop session (index 5).
|
||||||
|
var returnSession *Session
|
||||||
|
returnHops := eng.SessionManager.GetSessionsAtHop(5)
|
||||||
|
if len(returnHops) > 1 {
|
||||||
|
cryptorand.Shuffle(len(returnHops), func(i, j int) {
|
||||||
|
returnHops[i], returnHops[j] = returnHops[j], returnHops[i]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// There must be at least one, and if there was more than one the first
|
||||||
|
// index of returnHops will be a randomly selected one.
|
||||||
|
returnSession = returnHops[0]
|
||||||
|
conf := nonce.NewID()
|
||||||
|
var s [5]*session.Layer
|
||||||
|
for i := range s {
|
||||||
|
s[i] = session.New(byte(i))
|
||||||
|
}
|
||||||
|
var confirmChans [5]chan bool
|
||||||
|
var pendingConfirms int
|
||||||
|
for i := range nodes {
|
||||||
|
confirmChans[i] = nodes[i].
|
||||||
|
PaymentChan.Send(amount, s[i])
|
||||||
|
pendingConfirms++
|
||||||
|
}
|
||||||
|
var success bool
|
||||||
|
for pendingConfirms > 0 {
|
||||||
|
// The confirmation channels will signal upon success or failure
|
||||||
|
// according to the LN payment send protocol once either the HTLCs
|
||||||
|
// confirm on the way back or the path fails.
|
||||||
|
select {
|
||||||
|
case success = <-confirmChans[0]:
|
||||||
|
if success {
|
||||||
|
pendingConfirms--
|
||||||
|
}
|
||||||
|
case success = <-confirmChans[1]:
|
||||||
|
if success {
|
||||||
|
pendingConfirms--
|
||||||
|
}
|
||||||
|
case success = <-confirmChans[2]:
|
||||||
|
if success {
|
||||||
|
pendingConfirms--
|
||||||
|
}
|
||||||
|
case success = <-confirmChans[3]:
|
||||||
|
if success {
|
||||||
|
pendingConfirms--
|
||||||
|
}
|
||||||
|
case success = <-confirmChans[4]:
|
||||||
|
if success {
|
||||||
|
pendingConfirms--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// todo: handle payment failures!
|
||||||
|
o := SendSessions(conf, s, returnSession, nodes[:], eng.KeySet)
|
||||||
|
res := eng.PostAcctOnion(o)
|
||||||
|
eng.SendWithOneHook(nodes[0].AddrPort, res, func(id nonce.ID, b slice.Bytes) {
|
||||||
|
eng.SessionManager.Lock()
|
||||||
|
defer eng.SessionManager.Unlock()
|
||||||
|
var sessions [5]*Session
|
||||||
|
for i := range nodes {
|
||||||
|
log.D.F("confirming and storing session at hop %d %s for %s with"+
|
||||||
|
" %v initial"+
|
||||||
|
" balance", i, s[i].ID, nodes[i].AddrPort.String(), amount)
|
||||||
|
sessions[i] = NewSession(s[i].ID, nodes[i], amount,
|
||||||
|
s[i].Header, s[i].Payload, byte(i))
|
||||||
|
eng.SessionManager.Add(sessions[i])
|
||||||
|
eng.Sessions = append(eng.Sessions, sessions[i])
|
||||||
|
eng.SessionManager.PendingPayments.Delete(s[i].PreimageHash())
|
||||||
|
}
|
||||||
|
hook()
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendSessions provides a pair of private keys that will be used to generate the
|
||||||
|
// Purchase header bytes and to generate the ciphers provided in the Purchase
|
||||||
|
// message to encrypt the Session that is returned.
|
||||||
|
//
|
||||||
|
// The OnionSkin key, its cloaked public key counterpart used in the ToHeaderPub
|
||||||
|
// field of the Purchase message preformed header bytes, but the Ciphers
|
||||||
|
// provided in the Purchase message, for encrypting the Session to be returned,
|
||||||
|
// uses the Payload key, along with the public key found in the encrypted crypt
|
||||||
|
// of the header for the Reverse relay.
|
||||||
|
//
|
||||||
|
// This message's last crypt is a Confirmation, which allows the client to know
|
||||||
|
// that the keys were successfully delivered.
|
||||||
|
//
|
||||||
|
// This is the only onion that uses the node identity keys. The payment preimage
|
||||||
|
// hash must be available or the relay should not forward the remainder of the
|
||||||
|
// packet.
|
||||||
|
//
|
||||||
|
// If hdr/pld cipher keys are nil there must be a HeaderPub available on the
|
||||||
|
// session for the hop. This allows this function to send keys to any number of
|
||||||
|
// hops, but the very first SendSessions must have all in order to create the first
|
||||||
|
// set of sessions. This is by way of indicating to not use the IdentityPub but
|
||||||
|
// the HeaderPub instead. Not allowing free relay at all prevents spam attacks.
|
||||||
|
func SendSessions(id nonce.ID, s [5]*session.Layer,
|
||||||
|
client *Session, hop []*Node, ks *signer.KeySet) Skins {
|
||||||
|
|
||||||
|
n := GenNonces(6)
|
||||||
|
sk := Skins{}
|
||||||
|
for i := range s {
|
||||||
|
if i == 0 {
|
||||||
|
sk = sk.Crypt(hop[i].IdentityPub, nil, ks.Next(),
|
||||||
|
n[i], 0).Session(s[i])
|
||||||
|
} else {
|
||||||
|
sk = sk.ForwardSession(hop[i], ks.Next(), n[i], s[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sk.
|
||||||
|
ForwardCrypt(client, ks.Next(), n[5]).
|
||||||
|
Confirmation(id, 0)
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
"git-indra.lan/indra-labs/indra/pkg/messages/forward"
|
"git-indra.lan/indra-labs/indra/pkg/messages/forward"
|
||||||
"git-indra.lan/indra-labs/indra/pkg/messages/getbalance"
|
"git-indra.lan/indra-labs/indra/pkg/messages/getbalance"
|
||||||
"git-indra.lan/indra-labs/indra/pkg/messages/hiddenservice"
|
"git-indra.lan/indra-labs/indra/pkg/messages/hiddenservice"
|
||||||
|
"git-indra.lan/indra-labs/indra/pkg/messages/intro"
|
||||||
"git-indra.lan/indra-labs/indra/pkg/messages/noop"
|
"git-indra.lan/indra-labs/indra/pkg/messages/noop"
|
||||||
"git-indra.lan/indra-labs/indra/pkg/messages/response"
|
"git-indra.lan/indra-labs/indra/pkg/messages/response"
|
||||||
"git-indra.lan/indra-labs/indra/pkg/messages/reverse"
|
"git-indra.lan/indra-labs/indra/pkg/messages/reverse"
|
||||||
@@ -113,11 +114,11 @@ func (o Skins) GetBalance(id, confID nonce.ID, prvs [3]*prv.Key,
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o Skins) HiddenService(id nonce.ID, addr *pub.Key, prvs [3]*prv.Key,
|
func (o Skins) HiddenService(id nonce.ID, intr *intro.Layer, prvs [3]*prv.Key,
|
||||||
pubs [3]*pub.Key, nonces [3]nonce.IV) Skins {
|
pubs [3]*pub.Key, nonces [3]nonce.IV) Skins {
|
||||||
return append(o, &hiddenservice.Layer{
|
return append(o, &hiddenservice.Layer{
|
||||||
ID: id,
|
ID: id,
|
||||||
Identity: addr,
|
Layer: *intr,
|
||||||
Ciphers: GenCiphers(prvs, pubs),
|
Ciphers: GenCiphers(prvs, pubs),
|
||||||
Nonces: nonces,
|
Nonces: nonces,
|
||||||
})
|
})
|
||||||
@@ -133,7 +134,7 @@ func (o Skins) Response(id nonce.ID, res slice.Bytes, port uint16) Skins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (o Skins) Session(sess *session.Layer) Skins {
|
func (o Skins) Session(sess *session.Layer) Skins {
|
||||||
// SendKeys can apply to from 1 to 5 nodes, if either key is nil then
|
// SendSessions can apply to from 1 to 5 nodes, if either key is nil then
|
||||||
// this crypt just doesn't get added in the serialization process.
|
// this crypt just doesn't get added in the serialization process.
|
||||||
if sess.Header == nil || sess.Payload == nil {
|
if sess.Header == nil || sess.Payload == nil {
|
||||||
return o
|
return o
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"git-indra.lan/indra-labs/indra/pkg/crypto/key/cloak"
|
"git-indra.lan/indra-labs/indra/pkg/crypto/key/cloak"
|
||||||
"git-indra.lan/indra-labs/indra/pkg/crypto/key/prv"
|
"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/pub"
|
||||||
|
"git-indra.lan/indra-labs/indra/pkg/crypto/key/sig"
|
||||||
"git-indra.lan/indra-labs/indra/pkg/crypto/nonce"
|
"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/crypto/sha256"
|
||||||
"git-indra.lan/indra-labs/indra/pkg/messages/magicbytes"
|
"git-indra.lan/indra-labs/indra/pkg/messages/magicbytes"
|
||||||
@@ -177,4 +178,14 @@ func (s *Splicer) Bytes(b []byte) *Splicer {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Splicer) Signature(sb sig.Bytes) *Splicer {
|
||||||
|
copy(s.b[*s.c:s.c.Inc(sig.Len)], sb[:])
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Splicer) ReadSignature(sb *sig.Bytes) *Splicer {
|
||||||
|
copy(sb[:], s.b[*s.c:s.c.Inc(sig.Len)])
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Splicer) Done() {}
|
func (s *Splicer) Done() {}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ type Sim chan slice.Bytes
|
|||||||
|
|
||||||
func NewSim(bufs int) Sim { return make(Sim, bufs) }
|
func NewSim(bufs int) Sim { return make(Sim, bufs) }
|
||||||
func (d Sim) Send(b slice.Bytes) {
|
func (d Sim) Send(b slice.Bytes) {
|
||||||
log.D.Ln("sim transport sending")
|
|
||||||
d <- b
|
d <- b
|
||||||
}
|
}
|
||||||
func (d Sim) Receive() <-chan slice.Bytes {
|
func (d Sim) Receive() <-chan slice.Bytes {
|
||||||
|
|||||||
Reference in New Issue
Block a user