Merge branch 'main' into ind-bootstrap
# Conflicts: # version.go
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
// Package ciph manages encryption ciphers and encrypting blobs of data.
|
||||
// Package ciph manages encryption ciphers and encrypting blobs of data. Keys
|
||||
// are generated using ECDH from a public and private secp256k1 combined, as
|
||||
// well as directly from a 32 byte secret in the form of a static array as used
|
||||
// in most cryptographic hash function implementations in Go.
|
||||
package ciph
|
||||
|
||||
import (
|
||||
@@ -27,8 +30,9 @@ func GetBlock(from *prv.Key, to *pub.Key) (block cipher.Block) {
|
||||
return
|
||||
}
|
||||
|
||||
// BlockFromHash creates an AES block cipher from an sha256.Hash
|
||||
// BlockFromHash creates an AES block cipher from an sha256.Hash.
|
||||
func BlockFromHash(h sha256.Hash) (block cipher.Block) {
|
||||
// We can ignore the error because sha256.Hash is a valid key size.
|
||||
block, _ = aes.NewCipher(h[:])
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
package ciph
|
||||
@@ -1,6 +1,8 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"net/netip"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -17,7 +19,6 @@ import (
|
||||
"github.com/indra-labs/indra/pkg/nonce"
|
||||
"github.com/indra-labs/indra/pkg/session"
|
||||
"github.com/indra-labs/indra/pkg/slice"
|
||||
"github.com/indra-labs/indra/pkg/wire"
|
||||
"github.com/indra-labs/indra/pkg/wire/confirm"
|
||||
"github.com/indra-labs/indra/pkg/wire/layer"
|
||||
"github.com/indra-labs/indra/pkg/wire/response"
|
||||
@@ -39,8 +40,6 @@ var (
|
||||
type Client struct {
|
||||
*node.Node
|
||||
node.Nodes
|
||||
// *address.SendCache
|
||||
// *address.ReceiveCache
|
||||
session.Sessions
|
||||
PendingSessions []nonce.ID
|
||||
*confirm.Confirms
|
||||
@@ -55,8 +54,8 @@ func New(tpt ifc.Transport, hdrPrv *prv.Key, no *node.Node,
|
||||
nodes node.Nodes) (c *Client, e error) {
|
||||
|
||||
no.Transport = tpt
|
||||
no.HeaderPrv = hdrPrv
|
||||
no.HeaderPub = pub.Derive(hdrPrv)
|
||||
no.IdentityPrv = hdrPrv
|
||||
no.IdentityPub = pub.Derive(hdrPrv)
|
||||
var ks *signer.KeySet
|
||||
if _, ks, e = signer.New(); check(e) {
|
||||
return
|
||||
@@ -65,20 +64,21 @@ func New(tpt ifc.Transport, hdrPrv *prv.Key, no *node.Node,
|
||||
Confirms: confirm.NewConfirms(),
|
||||
Node: no,
|
||||
Nodes: nodes,
|
||||
// ReceiveCache: address.NewReceiveCache(),
|
||||
KeySet: ks,
|
||||
C: qu.T(),
|
||||
KeySet: ks,
|
||||
C: qu.T(),
|
||||
}
|
||||
// c.ReceiveCache.Add(address.NewReceiver(hdrPrv))
|
||||
// A new client requires a Session for receiving responses. This session
|
||||
// should have its keys changed periodically, or at least once on
|
||||
// startup.
|
||||
c.Sessions = c.Sessions.Add(session.New(no.ID, no, math.MaxUint64, 0, 0))
|
||||
return
|
||||
}
|
||||
|
||||
// Start a single thread of the Client.
|
||||
func (cl *Client) Start() {
|
||||
out:
|
||||
for {
|
||||
if cl.runner() {
|
||||
break out
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -93,13 +93,17 @@ func (cl *Client) RegisterConfirmation(hook confirm.Hook,
|
||||
})
|
||||
}
|
||||
|
||||
// FindCloaked searches the client identity key and the Sessions for a match.
|
||||
func (cl *Client) FindCloaked(clk cloak.PubKey) (hdr *prv.Key, pld *prv.Key) {
|
||||
// FindCloaked searches the client identity key and the Sessions for a match. It
|
||||
// returns the session as well, though not all users of this function will need
|
||||
// this.
|
||||
func (cl *Client) FindCloaked(clk cloak.PubKey) (hdr *prv.Key, pld *prv.Key,
|
||||
sess *session.Session) {
|
||||
|
||||
var b cloak.Blinder
|
||||
copy(b[:], clk[:cloak.BlindLen])
|
||||
hash := cloak.Cloak(b, cl.Node.HeaderBytes)
|
||||
hash := cloak.Cloak(b, cl.Node.IdentityBytes)
|
||||
if hash == clk {
|
||||
hdr = cl.Node.HeaderPrv
|
||||
hdr = cl.Node.IdentityPrv
|
||||
// there is no payload key for the node, only in sessions.
|
||||
return
|
||||
}
|
||||
@@ -108,44 +112,82 @@ func (cl *Client) FindCloaked(clk cloak.PubKey) (hdr *prv.Key, pld *prv.Key) {
|
||||
if hash == clk {
|
||||
hdr = cl.Sessions[i].HeaderPrv
|
||||
pld = cl.Sessions[i].PayloadPrv
|
||||
sess = cl.Sessions[i]
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cl *Client) SendKeys(nodeID nonce.ID,
|
||||
hook func(cf nonce.ID)) (confirmation nonce.ID, hdr, pld *prv.Key,
|
||||
// SendKeys is the delivery of a ...
|
||||
//
|
||||
// todo: this function should be requiring input of keys, and a related prior
|
||||
//
|
||||
// function that generates the preimage for an LN AMP, from the hash of the
|
||||
// keys.
|
||||
func (cl *Client) SendKeys(nodeID []nonce.ID, hook confirm.Hook) (conf nonce.ID,
|
||||
e error) {
|
||||
|
||||
// var hdrPub, pldPub *pub.Key
|
||||
if hdr, e = prv.GenerateKey(); check(e) {
|
||||
return
|
||||
var n []*node.Node
|
||||
for i := range nodeID {
|
||||
no := cl.Nodes.FindByID(nodeID[i])
|
||||
if no != nil {
|
||||
n = append(n, no)
|
||||
}
|
||||
}
|
||||
// hdrPub = pub.Derive(hdr)
|
||||
if pld, e = prv.GenerateKey(); check(e) {
|
||||
return
|
||||
if len(n) > 5 {
|
||||
e = fmt.Errorf("SendKeys maximum 5 keys %d given", len(nodeID))
|
||||
}
|
||||
ln := len(n)
|
||||
hdrPrv, pldPrv := make([]*prv.Key, ln), make([]*prv.Key, ln)
|
||||
hdrPub, pldPub := make([]*pub.Key, ln), make([]*pub.Key, ln)
|
||||
for i := range n {
|
||||
if hdrPrv[i], e = prv.GenerateKey(); check(e) {
|
||||
return
|
||||
}
|
||||
hdrPub[i] = pub.Derive(hdrPrv[i])
|
||||
if pldPrv[i], e = prv.GenerateKey(); check(e) {
|
||||
return
|
||||
}
|
||||
pldPub[i] = pub.Derive(pldPrv[i])
|
||||
}
|
||||
// pldPub = pub.Derive(pld)
|
||||
|
||||
n := cl.Nodes.FindByID(nodeID)
|
||||
selected := cl.Nodes.Select(SimpleSelector, n, 4)
|
||||
var hop [5]*node.Node
|
||||
hop[0], hop[1], hop[2], hop[3], hop[4] =
|
||||
selected[0], selected[1], selected[2], selected[3], cl.Node
|
||||
confirmation = nonce.NewID()
|
||||
os := wire.SendKeys(confirmation, hdr, pld, cl.Node, hop, cl.KeySet)
|
||||
cl.RegisterConfirmation(hook, os[len(os)-1].(*confirm.OnionSkin).ID)
|
||||
o := os.Assemble()
|
||||
b := wire.EncodeOnion(o)
|
||||
cl.Send(hop[0].AddrPort, b)
|
||||
// selected := cl.Nodes.Select(SimpleSelector, n, 4)
|
||||
// if len(selected) < 4 {
|
||||
// e = fmt.Errorf("not enough nodes known to form circuit")
|
||||
// return
|
||||
// }
|
||||
// hop := [5]*node.Node{
|
||||
// selected[0], selected[1], n, selected[2], selected[3],
|
||||
// }
|
||||
// conf = nonce.NewID()
|
||||
// os := wire.SendKeys(conf, hdrPrv, pldPrv, cl.Node, hop, cl.KeySet)
|
||||
// cl.RegisterConfirmation(hook, os[len(os)-1].(*confirm.OnionSkin).ID)
|
||||
// cl.Sessions.Add(&session.Session{
|
||||
// ID: n.ID,
|
||||
// Remaining: 1 << 16,
|
||||
// IdentityPub: hdrPub,
|
||||
// IdentityBytes: hdrPub.ToBytes(),
|
||||
// PayloadPub: pldPub,
|
||||
// PayloadBytes: pldPub.ToBytes(),
|
||||
// IdentityPrv: hdrPrv,
|
||||
// PayloadPrv: pldPrv,
|
||||
// Deadline: time.Now().Add(DefaultDeadline),
|
||||
// })
|
||||
// o := os.Assemble()
|
||||
// b := wire.EncodeOnion(o)
|
||||
// cl.Send(hop[0].AddrPort, b)
|
||||
return
|
||||
}
|
||||
|
||||
// Cleanup closes and flushes any resources the client opened that require sync
|
||||
// in order to reopen correctly.
|
||||
func (cl *Client) Cleanup() {
|
||||
// Do cleanup stuff before shutdown.
|
||||
}
|
||||
|
||||
// Shutdown triggers the shutdown of the client and the Cleanup before
|
||||
// finishing.
|
||||
func (cl *Client) Shutdown() {
|
||||
if cl.Bool.Load() {
|
||||
return
|
||||
@@ -155,6 +197,7 @@ func (cl *Client) Shutdown() {
|
||||
cl.C.Q()
|
||||
}
|
||||
|
||||
// Send a message to a peer via their AddrPort.
|
||||
func (cl *Client) Send(addr *netip.AddrPort, b slice.Bytes) {
|
||||
// first search if we already have the node available with connection
|
||||
// open.
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
)
|
||||
|
||||
func TestPing(t *testing.T) {
|
||||
const nTotal = 4
|
||||
const nTotal = 6
|
||||
clients := make([]*Client, nTotal)
|
||||
var e error
|
||||
if clients, e = CreateMockCircuitClients(nTotal); check(e) {
|
||||
@@ -29,17 +29,18 @@ func TestPing(t *testing.T) {
|
||||
for _, v := range clients {
|
||||
go v.Start()
|
||||
}
|
||||
pn := nonce.NewID()
|
||||
conf := nonce.NewID()
|
||||
var ks *signer.KeySet
|
||||
if _, ks, e = signer.New(); check(e) {
|
||||
t.Error(e)
|
||||
t.FailNow()
|
||||
}
|
||||
var hop [nTotal - 1]*node.Node
|
||||
for i := range clients[0].Nodes {
|
||||
hop[i] = clients[0].Nodes[i]
|
||||
var sessions session.Sessions
|
||||
for _, v := range clients[1:] {
|
||||
sessions = append(sessions, v.Sessions[0])
|
||||
}
|
||||
os := wire.Ping(pn, clients[0].Node, hop, ks)
|
||||
sessions = append(sessions, clients[0].Sessions[0])
|
||||
os := wire.Ping(conf, sessions, ks)
|
||||
quit := qu.T()
|
||||
log.I.S("sending ping with ID", os[len(os)-1].(*confirm.OnionSkin))
|
||||
clients[0].RegisterConfirmation(func(cf nonce.ID) {
|
||||
@@ -48,85 +49,44 @@ func TestPing(t *testing.T) {
|
||||
}, os[len(os)-1].(*confirm.OnionSkin).ID)
|
||||
o := os.Assemble()
|
||||
b := wire.EncodeOnion(o)
|
||||
hop[0].Send(b)
|
||||
// go func() {
|
||||
// time.Sleep(time.Second)
|
||||
// quit.Q()
|
||||
// t.Error("ping got stuck")
|
||||
// }()
|
||||
<-quit.Wait()
|
||||
for _, v := range clients {
|
||||
v.Shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendKeys(t *testing.T) {
|
||||
const nTotal = 6
|
||||
clients := make([]*Client, nTotal)
|
||||
var e error
|
||||
if clients, e = CreateMockCircuitClients(nTotal); check(e) {
|
||||
t.Error(e)
|
||||
t.FailNow()
|
||||
}
|
||||
// Start up the clients.
|
||||
for _, v := range clients {
|
||||
go v.Start()
|
||||
}
|
||||
quit := qu.T()
|
||||
clients[0].SendKeys(clients[0].Nodes[0].ID, func(cf nonce.ID) {
|
||||
log.I.S("received sendkeys confirmation ID", cf)
|
||||
clients[0].Send(clients[1].AddrPort, b)
|
||||
go func() {
|
||||
select {
|
||||
case <-time.After(time.Second):
|
||||
t.Error("ping got stuck")
|
||||
case <-quit:
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
quit.Q()
|
||||
})
|
||||
}()
|
||||
<-quit.Wait()
|
||||
for _, v := range clients {
|
||||
v.Shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendPurchase(t *testing.T) {
|
||||
const nTotal = 6
|
||||
clients := make([]*Client, nTotal)
|
||||
var e error
|
||||
if clients, e = CreateMockCircuitClients(nTotal); check(e) {
|
||||
t.Error(e)
|
||||
t.FailNow()
|
||||
}
|
||||
var ks *signer.KeySet
|
||||
if _, ks, e = signer.New(); check(e) {
|
||||
t.Error(e)
|
||||
t.FailNow()
|
||||
}
|
||||
var sess [3]*session.Session
|
||||
for i := range sess {
|
||||
sess[i] = session.NewSession(nonce.NewID(), 203230230, time.Hour)
|
||||
}
|
||||
// clients[4].ReceiveCache.Add(address.NewReceiver(sess[0].HeaderPrv))
|
||||
// clients[5].ReceiveCache.Add(address.NewReceiver(sess[1].HeaderPrv))
|
||||
// clients[0].ReceiveCache.Add(address.NewReceiver(sess[2].HeaderPrv))
|
||||
clients[4].Sessions = clients[4].Sessions.Add(sess[0])
|
||||
clients[5].Sessions = clients[5].Sessions.Add(sess[1])
|
||||
clients[0].Sessions = clients[0].Sessions.Add(sess[2])
|
||||
|
||||
// Start up the clients.
|
||||
for _, v := range clients {
|
||||
go v.Start()
|
||||
}
|
||||
var hop [nTotal - 1]*node.Node
|
||||
for i := range clients[0].Nodes {
|
||||
hop[i] = clients[0].Nodes[i]
|
||||
}
|
||||
const nBytes = 2342342
|
||||
id := nonce.NewID()
|
||||
os := wire.SendPurchase(id, nBytes, clients[0].Node, hop, sess, ks)
|
||||
clients[0].PendingSessions = append(clients[0].PendingSessions, id)
|
||||
o := os.Assemble()
|
||||
b := wire.EncodeOnion(o)
|
||||
hop[0].Send(b)
|
||||
<-clients[0].Wait()
|
||||
for _, v := range clients {
|
||||
v.Shutdown()
|
||||
}
|
||||
}
|
||||
// func TestSendKeys(t *testing.T) {
|
||||
// const nTotal = 6
|
||||
// clients := make([]*Client, nTotal)
|
||||
// var e error
|
||||
// if clients, e = CreateMockCircuitClients(nTotal); check(e) {
|
||||
// t.Error(e)
|
||||
// t.FailNow()
|
||||
// }
|
||||
// // Start up the clients.
|
||||
// for _, v := range clients {
|
||||
// go v.Start()
|
||||
// }
|
||||
// quit := qu.T()
|
||||
// clients[0].SendKeys(clients[0].Nodes[0].ID, func(cf nonce.ID) {
|
||||
// log.I.S("received sendkeys confirmation ID", cf)
|
||||
// quit.Q()
|
||||
// })
|
||||
// <-quit.Wait()
|
||||
// for _, v := range clients {
|
||||
// v.Shutdown()
|
||||
// }
|
||||
// }
|
||||
|
||||
func TestSendExit(t *testing.T) {
|
||||
const nTotal = 6
|
||||
@@ -142,12 +102,9 @@ func TestSendExit(t *testing.T) {
|
||||
t.FailNow()
|
||||
}
|
||||
var sess [3]*session.Session
|
||||
for i := range sess {
|
||||
sess[i] = session.NewSession(nonce.NewID(), 203230230, time.Hour)
|
||||
}
|
||||
// clients[4].ReceiveCache.Add(address.NewReceiver(sess[0].HeaderPrv))
|
||||
// clients[5].ReceiveCache.Add(address.NewReceiver(sess[1].HeaderPrv))
|
||||
// clients[0].ReceiveCache.Add(address.NewReceiver(sess[2].HeaderPrv))
|
||||
sess[0] = clients[4].Sessions.Find(clients[4].ID)
|
||||
sess[1] = clients[5].Sessions.Find(clients[5].ID)
|
||||
sess[2] = clients[0].Sessions.Find(clients[0].ID)
|
||||
clients[4].Sessions = clients[4].Sessions.Add(sess[0])
|
||||
clients[5].Sessions = clients[5].Sessions.Add(sess[1])
|
||||
clients[0].Sessions = clients[0].Sessions.Add(sess[2])
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/indra-labs/indra/pkg/key/prv"
|
||||
"github.com/indra-labs/indra/pkg/nonce"
|
||||
)
|
||||
|
||||
func TestPurchaseFlow(t *testing.T) {
|
||||
@@ -26,23 +22,22 @@ func TestPurchaseFlow(t *testing.T) {
|
||||
// quit.Q()
|
||||
// }()
|
||||
// <-quit.Wait()
|
||||
selected := clients[0].Nodes.Select(SimpleSelector, clients[1].Node, 4)
|
||||
// next to send out keys for the return hops
|
||||
returnNodes := selected[2:]
|
||||
var rtnHdr, rtnPld [2]*prv.Key
|
||||
var confirmation [2]nonce.ID
|
||||
var wait sync.WaitGroup
|
||||
for i := range returnNodes {
|
||||
wait.Add(1)
|
||||
confirmation[i], rtnHdr[i], rtnPld[i], e = clients[0].
|
||||
SendKeys(returnNodes[i].ID, func(cf nonce.ID) {
|
||||
log.I.S("confirmed", cf)
|
||||
wait.Done()
|
||||
})
|
||||
}
|
||||
log.I.S(confirmation)
|
||||
wait.Wait()
|
||||
// now to do the purchase
|
||||
// selected := clients[0].Nodes.Select(SimpleSelector, clients[1].Node, 4)
|
||||
// // next to send out keys for the return hops
|
||||
// returnNodes := selected[2:]
|
||||
// var confirmation [2]nonce.ID
|
||||
// var wait sync.WaitGroup
|
||||
// for i := range returnNodes {
|
||||
// wait.Add(1)
|
||||
// confirmation[i], e = clients[0].
|
||||
// SendKeys(returnNodes[i].ID, func(cf nonce.ID) {
|
||||
// log.I.S("confirmed", cf)
|
||||
// wait.Done()
|
||||
// })
|
||||
// }
|
||||
// log.I.S(confirmation)
|
||||
// wait.Wait()
|
||||
// // now to do the purchase
|
||||
|
||||
for _, v := range clients {
|
||||
v.Shutdown()
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/indra-labs/indra/pkg/ciph"
|
||||
session2 "github.com/indra-labs/indra/pkg/session"
|
||||
"github.com/indra-labs/indra/pkg/sha256"
|
||||
"github.com/indra-labs/indra/pkg/slice"
|
||||
"github.com/indra-labs/indra/pkg/types"
|
||||
@@ -16,7 +15,6 @@ import (
|
||||
"github.com/indra-labs/indra/pkg/wire/forward"
|
||||
"github.com/indra-labs/indra/pkg/wire/layer"
|
||||
"github.com/indra-labs/indra/pkg/wire/noop"
|
||||
"github.com/indra-labs/indra/pkg/wire/purchase"
|
||||
"github.com/indra-labs/indra/pkg/wire/response"
|
||||
"github.com/indra-labs/indra/pkg/wire/reverse"
|
||||
"github.com/indra-labs/indra/pkg/wire/session"
|
||||
@@ -52,8 +50,6 @@ func (cl *Client) runner() (out bool) {
|
||||
cl.layer(on, b, c)
|
||||
case *noop.OnionSkin:
|
||||
cl.noop(on, b, c)
|
||||
case *purchase.OnionSkin:
|
||||
cl.purchase(on, b, c)
|
||||
case *reverse.OnionSkin:
|
||||
cl.reverse(on, b, c)
|
||||
case *response.OnionSkin:
|
||||
@@ -139,7 +135,7 @@ func (cl *Client) forward(on *forward.OnionSkin, b slice.Bytes,
|
||||
|
||||
func (cl *Client) layer(on *layer.OnionSkin, b slice.Bytes, c *slice.Cursor) {
|
||||
// this is probably an encrypted layer for us.
|
||||
hdr, _ := cl.FindCloaked(on.Cloak)
|
||||
hdr, _, _ := cl.FindCloaked(on.Cloak)
|
||||
if hdr == nil {
|
||||
log.I.Ln("no matching key found from cloaked key")
|
||||
return
|
||||
@@ -153,31 +149,6 @@ func (cl *Client) noop(on *noop.OnionSkin, b slice.Bytes, c *slice.Cursor) {
|
||||
// this won't happen normally
|
||||
}
|
||||
|
||||
func (cl *Client) purchase(on *purchase.OnionSkin, b slice.Bytes, c *slice.Cursor) {
|
||||
// Create a new Session.
|
||||
s := session2.NewSession(on.ID, on.NBytes, DefaultDeadline)
|
||||
se := &session.OnionSkin{
|
||||
ID: s.ID,
|
||||
HeaderKey: s.HeaderPub,
|
||||
PayloadKey: s.PayloadPub,
|
||||
Onion: &noop.OnionSkin{},
|
||||
}
|
||||
cl.Mutex.Lock()
|
||||
cl.Sessions.Add(s)
|
||||
cl.Mutex.Unlock()
|
||||
header := b[*c:c.Inc(ReverseHeaderLen)]
|
||||
rb := make(slice.Bytes, ReverseHeaderLen+session.Len)
|
||||
cur := slice.NewCursor()
|
||||
copy(rb[*cur:cur.Inc(ReverseHeaderLen)], header[:ReverseHeaderLen])
|
||||
start := *cur
|
||||
se.Encode(rb, cur)
|
||||
for i := range on.Ciphers {
|
||||
blk := ciph.BlockFromHash(on.Ciphers[i])
|
||||
ciph.Encipher(blk, on.Nonces[2-i], rb[start:])
|
||||
}
|
||||
cl.Node.Send(rb)
|
||||
}
|
||||
|
||||
func (cl *Client) reverse(on *reverse.OnionSkin, b slice.Bytes,
|
||||
c *slice.Cursor) {
|
||||
|
||||
@@ -193,9 +164,8 @@ func (cl *Client) reverse(on *reverse.OnionSkin, b slice.Bytes,
|
||||
first := *c
|
||||
second := first + ReverseLayerLen
|
||||
last := second + ReverseLayerLen
|
||||
hdr, pld := cl.FindCloaked(on1.Cloak)
|
||||
hdr, pld, _ := cl.FindCloaked(on1.Cloak)
|
||||
// We need to find the PayloadPub to match.
|
||||
// ses := cl.Sessions.FindPub(hdr.Pub)
|
||||
hdrPrv := hdr
|
||||
hdrPub := on1.FromPub
|
||||
blk := ciph.GetBlock(hdrPrv, hdrPub)
|
||||
@@ -230,7 +200,9 @@ func (cl *Client) response(on *response.OnionSkin, b slice.Bytes, cur *slice.Cur
|
||||
cl.ExitHooks.Find(on.Hash)
|
||||
}
|
||||
|
||||
func (cl *Client) session(s *session.OnionSkin, b slice.Bytes, cur *slice.Cursor) {
|
||||
func (cl *Client) session(s *session.OnionSkin, b slice.Bytes,
|
||||
cur *slice.Cursor) {
|
||||
|
||||
// Session is returned from a Purchase message in the reverse layers.
|
||||
//
|
||||
// Session has a nonce.ID that is given in the last layer of a LN sphinx
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
|
||||
"github.com/indra-labs/indra/pkg/node"
|
||||
"github.com/indra-labs/indra/pkg/slice"
|
||||
)
|
||||
|
||||
// SimpleSelector is a pure random shuffle selection algorithm for producing
|
||||
// a list of nodes for an onion.
|
||||
//
|
||||
// This function will return nil if there isn't enough
|
||||
func SimpleSelector(n node.Nodes, exit *node.Node,
|
||||
count int) (selected node.Nodes) {
|
||||
|
||||
// For the purposes of this simple selector algorithm we require unique
|
||||
// nodes for each hop.
|
||||
if len(n) < count+1 {
|
||||
log.E.F("not enough nodes, have %d, need %d", len(n), count+1)
|
||||
return
|
||||
}
|
||||
// Remove the exit from the list of options.
|
||||
var nCandidates node.Nodes
|
||||
if exit != nil {
|
||||
for i := range n {
|
||||
if n[i].ID != exit.ID {
|
||||
nCandidates = append(nCandidates, n[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
// Shuffle the list we made
|
||||
rand.Seed(slice.GetCryptoRandSeed())
|
||||
rand.Shuffle(len(nCandidates), func(i, j int) {
|
||||
nCandidates[i], nCandidates[j] = nCandidates[j], nCandidates[i]
|
||||
})
|
||||
return nCandidates[:count]
|
||||
}
|
||||
@@ -31,7 +31,7 @@ func CreateMockCircuitClients(nTotal int) (clients []*Client, e error) {
|
||||
clients[i].AddrPort = nodes[i].AddrPort
|
||||
}
|
||||
// add each node to each other's Nodes except itself.
|
||||
for i := range nodes {
|
||||
for i := range clients {
|
||||
for j := range nodes {
|
||||
if i == j {
|
||||
continue
|
||||
@@ -39,5 +39,6 @@ func CreateMockCircuitClients(nTotal int) (clients []*Client, e error) {
|
||||
clients[i].Nodes = append(clients[i].Nodes, nodes[j])
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -16,28 +16,26 @@ var (
|
||||
)
|
||||
|
||||
// ParseCLIArgs reads a command line argument slice (presumably from os.Args),
|
||||
// identifies the command to run and
|
||||
// identifies the command to run and sets options provided.
|
||||
//
|
||||
// Rules for constructing CLI args:
|
||||
//
|
||||
// - Commands are identified by name, and must appear in their hierarchic
|
||||
// order to invoke subcommands. They are matched as normalised to lower
|
||||
// case.
|
||||
// Commands are identified by name, and must appear in their hierarchic order to
|
||||
// invoke subcommands. They are matched as normalised to lower case.
|
||||
//
|
||||
// - Options can be preceded by "--" or "-", and the full name, or the
|
||||
// alias, normalised to lower case for matching, and if there is an "=" after
|
||||
// it, the value is after this, otherwise, the next element in the args is the
|
||||
// value, except booleans, which default to true unless set to false. For these,
|
||||
// the prefix "no" or similar indicates the semantics of the true value.
|
||||
// Options can be preceded by "--" or "-", and the full name, or the alias,
|
||||
// normalised to lower case for matching, and if there is an "=" after it, the
|
||||
// value is after this, otherwise, the next element in the args is the value,
|
||||
// except booleans, which default to true unless set to false. For these, the
|
||||
// prefix "no" or similar indicates the semantics of the true value.
|
||||
//
|
||||
// - Options only match when preceded by their relevant Command, except for
|
||||
// the root Command, and these options must precede any other command
|
||||
// options.
|
||||
// Options only match when preceded by their relevant Command, except for the
|
||||
// root Command, and these options must precede any other Command.
|
||||
//
|
||||
// - If no command is selected, the root Command.Default is selected. This
|
||||
// can optionally be used for subcommands as well, though it is unlikely
|
||||
// needed, if found, the Default of the tip of the Command branch selected
|
||||
// by the CLI if there is one, otherwise the Command itself.
|
||||
// If no command is selected, the root Command.Default is selected. This can
|
||||
// optionally be used for subcommands as well, though it is unlikely needed, if
|
||||
// found, the Default of the tip of the Command branch selected by the CLI if
|
||||
// there is one, otherwise the Command itself.
|
||||
func (c *Command) ParseCLIArgs(a []string) (run *Command, runArgs []string,
|
||||
e error) {
|
||||
|
||||
|
||||
@@ -187,7 +187,7 @@ func Init(c *Command, p path.Path) (cmd *Command, err error) {
|
||||
if p == nil {
|
||||
p = path.Path{c.Name}
|
||||
}
|
||||
c.Path = p // .Parent()
|
||||
c.Path = p
|
||||
for i := range c.Configs {
|
||||
c.Configs[i].SetPath(p)
|
||||
}
|
||||
@@ -208,7 +208,7 @@ func Init(c *Command, p path.Path) (cmd *Command, err error) {
|
||||
return c, err
|
||||
}
|
||||
|
||||
// GetOpt returns the option at a requested path
|
||||
// GetOpt returns the option at a requested path.
|
||||
func (c *Command) GetOpt(path path.Path) (o config.Option) {
|
||||
p := make([]string, len(path))
|
||||
for i := range path {
|
||||
@@ -239,24 +239,6 @@ func (c *Command) GetOpt(path path.Path) (o config.Option) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Command) GetCommand(p string) (o *Command) {
|
||||
pp := strings.Split(p, " ")
|
||||
path := path.Path(pp)
|
||||
// log.I.F("%v == %v", path, c.Path)
|
||||
if path.Equal(c.Path) {
|
||||
// log.I.Ln("found", c.Path)
|
||||
return c
|
||||
}
|
||||
for i := range c.Commands {
|
||||
// log.I.Ln(c.Commands[i].Path)
|
||||
o = c.Commands[i].GetCommand(p)
|
||||
if o != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Command) GetValue(key string) config.Concrete {
|
||||
return c.Configs[key].Value()
|
||||
}
|
||||
@@ -270,7 +252,7 @@ func (c *Command) GetTextValue(key string) string {
|
||||
}
|
||||
|
||||
// ForEach runs a closure on every node in the Commands tree, stopping if the
|
||||
// closure returns false
|
||||
// closure returns false.
|
||||
func (c *Command) ForEach(cl func(*Command, int) bool, hereDepth,
|
||||
hereDist int, cmd *Command) (ocl func(*Command, int) bool, depth,
|
||||
dist int, cm *Command) {
|
||||
|
||||
@@ -6,13 +6,11 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
log2 "github.com/indra-labs/indra/pkg/log"
|
||||
"github.com/indra-labs/indra/pkg/opts/config"
|
||||
"github.com/indra-labs/indra/pkg/path"
|
||||
)
|
||||
|
||||
func TestCommand_Foreach(t *testing.T) {
|
||||
log2.SetLogLevel(log2.Info)
|
||||
cm, _ := Init(GetExampleCommands(), nil)
|
||||
log.I.Ln("spewing only droptxindex")
|
||||
cm.ForEach(func(cmd *Command, _ int) bool {
|
||||
@@ -23,7 +21,6 @@ func TestCommand_Foreach(t *testing.T) {
|
||||
}, 0, 0, cm)
|
||||
log.I.Ln("printing name of all commands found on search")
|
||||
cm.ForEach(func(cmd *Command, depth int) bool {
|
||||
log.I.Ln()
|
||||
log.I.F("%s%s #(%d)", path.GetIndent(depth), cmd.Path, depth)
|
||||
for i := range cmd.Configs {
|
||||
log.I.F("%s%s -%s %v #%v (%d)", path.GetIndent(depth),
|
||||
@@ -34,10 +31,7 @@ func TestCommand_Foreach(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCommand_MarshalText(t *testing.T) {
|
||||
|
||||
log2.SetLogLevel(log2.Info)
|
||||
o, _ := Init(GetExampleCommands(), nil)
|
||||
// log.I.S(o)
|
||||
conf, err := o.MarshalText()
|
||||
if log.E.Chk(err) {
|
||||
t.FailNow()
|
||||
@@ -46,7 +40,6 @@ func TestCommand_MarshalText(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCommand_UnmarshalText(t *testing.T) {
|
||||
log2.SetLogLevel(log2.Info)
|
||||
o, _ := Init(GetExampleCommands(), nil)
|
||||
var conf []byte
|
||||
var err error
|
||||
@@ -61,7 +54,6 @@ func TestCommand_UnmarshalText(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCommand_GetEnvs(t *testing.T) {
|
||||
log2.SetLogLevel(log2.Info)
|
||||
o, _ := Init(GetExampleCommands(), nil)
|
||||
envs := o.GetEnvs()
|
||||
var out []string
|
||||
@@ -80,7 +72,6 @@ func TestCommand_GetEnvs(t *testing.T) {
|
||||
var testSeparator = fmt.Sprintf("%s\n", strings.Repeat("-", 72))
|
||||
|
||||
func TestCommand_Help(t *testing.T) {
|
||||
log2.SetLogLevel(log2.Debug)
|
||||
ex := GetExampleCommands()
|
||||
ex.AddCommand(Help())
|
||||
o, _ := Init(ex, nil)
|
||||
@@ -207,7 +198,6 @@ func TestCommand_Help(t *testing.T) {
|
||||
|
||||
}
|
||||
func TestCommand_LogToFile(t *testing.T) {
|
||||
log2.SetLogLevel(log2.Trace)
|
||||
ex := GetExampleCommands()
|
||||
ex.AddCommand(Help())
|
||||
ex, _ = Init(ex, nil)
|
||||
|
||||
@@ -16,6 +16,8 @@ type Env struct {
|
||||
|
||||
type Envs []Env
|
||||
|
||||
// ForEach runs a closure on each element of an Env item in an Envs and halts if
|
||||
// one returns an error.
|
||||
func (e Envs) ForEach(fn func(env string, opt config.Option) (err error)) (err error) {
|
||||
for i := range e {
|
||||
var name []string
|
||||
@@ -30,6 +32,8 @@ func (e Envs) ForEach(fn func(env string, opt config.Option) (err error)) (err e
|
||||
return
|
||||
}
|
||||
|
||||
// LoadFromEnvironment walks the Envs and reads the values found in the
|
||||
// environment variables.
|
||||
func (e Envs) LoadFromEnvironment() (err error) {
|
||||
err = e.ForEach(func(env string, opt config.Option) (err error) {
|
||||
v, exists := os.LookupEnv(env)
|
||||
@@ -44,10 +48,12 @@ func (e Envs) LoadFromEnvironment() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (e Envs) Len() int {
|
||||
return len(e)
|
||||
}
|
||||
// Len returns the number items in an Envs. This is a sort.Sorter interface
|
||||
// implementation.
|
||||
func (e Envs) Len() int { return len(e) }
|
||||
|
||||
// Less compares two items and returns whether the first is lesser than the
|
||||
// second. This is a sort.Sorter interface implementation.
|
||||
func (e Envs) Less(i, j int) (res bool) {
|
||||
li, lj := len(e[i].Name), len(e[j].Name)
|
||||
if li < lj {
|
||||
@@ -68,9 +74,9 @@ func (e Envs) Less(i, j int) (res bool) {
|
||||
return
|
||||
}
|
||||
|
||||
func (e Envs) Swap(i, j int) {
|
||||
e[i], e[j] = e[j], e[i]
|
||||
}
|
||||
// Swap switches the position of two elements of an Envs. This is a sort.Sorter
|
||||
// interface implementation.
|
||||
func (e Envs) Swap(i, j int) { e[i], e[j] = e[j], e[i] }
|
||||
|
||||
// GetEnvs walks a Command tree and returns a slice containing all environment
|
||||
// variable names and the related config.Option.
|
||||
|
||||
@@ -25,8 +25,8 @@ fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
|
||||
culpa qui officia deserunt mollit anim id est laborum.`
|
||||
|
||||
// GetExampleCommands returns available subcommands in hypothetical Parallelcoin
|
||||
// Pod example for testing (derived from btcd and btcwallet plus
|
||||
// parallelcoin kopach miner)
|
||||
// Pod example for testing (derived from btcd and btcwallet plus parallelcoin
|
||||
// GUI and kopach miner).
|
||||
func GetExampleCommands() (c *Command) {
|
||||
c = &Command{
|
||||
Name: "pod123",
|
||||
|
||||
165
pkg/cmds/help.go
165
pkg/cmds/help.go
@@ -13,23 +13,27 @@ import (
|
||||
"github.com/indra-labs/indra/pkg/util"
|
||||
)
|
||||
|
||||
// Help is a default top level command that subsequent
|
||||
// Help is a default top level command that is injected into a Command at the
|
||||
// top level to provide an interface to finding the documentation embedded in
|
||||
// the options and commands definitions.
|
||||
func Help() (h *Command) {
|
||||
h = &Command{
|
||||
Path: nil,
|
||||
Name: "help",
|
||||
Description: "Print help information, optionally multiple keywords" +
|
||||
" can be given and will be searched to generate output",
|
||||
Documentation: "Uses partial matching, if result is ambiguous, prints general top " +
|
||||
Description: "Print help information, optionally multiple " +
|
||||
"keywords can be given and will be searched to " +
|
||||
"generate output",
|
||||
Documentation: "Uses partial matching, if result is " +
|
||||
"ambiguous, prints general top " +
|
||||
"level\nhelp and the list of partial match terms.\n\n" +
|
||||
"If single term exactly matches a command, the command help will" +
|
||||
" be printed.\n\n" +
|
||||
"If single term exactly matches a command, the command " +
|
||||
"help will be printed.\n\n" +
|
||||
"Otherwise, a list showing all items, their paths, and " +
|
||||
"description are shown.\n\n" +
|
||||
"Use their full path and the full " +
|
||||
"documentation for the item will be shown.\n\n" +
|
||||
"Note that in all cases, options are only recognised after their\n" +
|
||||
"related subcommand.",
|
||||
"Note that in all cases, options are only recognised " +
|
||||
"after their\nrelated subcommand.",
|
||||
Entrypoint: HelpEntrypoint,
|
||||
Parent: nil,
|
||||
Commands: nil,
|
||||
@@ -40,8 +44,8 @@ func Help() (h *Command) {
|
||||
return
|
||||
}
|
||||
|
||||
// IndentTextBlock adds an arbitrary number of tabs to the front of a text
|
||||
// block that is presumed to already be flowed to ~80 columns.
|
||||
// IndentTextBlock adds an arbitrary number of tabs to the front of a text block
|
||||
// that is presumed to already be flowed to ~80 columns.
|
||||
func IndentTextBlock(s string, tabs int) (o string) {
|
||||
s = strings.TrimSpace(s)
|
||||
split := strings.Split(s, strings.Repeat("\n", tabs))
|
||||
@@ -57,54 +61,64 @@ type CommandInfo struct {
|
||||
|
||||
type CommandInfos []*CommandInfo
|
||||
|
||||
// Sorter for CommandInfos.
|
||||
|
||||
func (c CommandInfos) Len() int { return len(c) }
|
||||
func (c CommandInfos) Less(i, j int) bool { return c[i].name < c[j].name }
|
||||
func (c CommandInfos) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
|
||||
|
||||
func HelpEntrypoint(c *Command, args []string) (err error) {
|
||||
if args == nil {
|
||||
// no args given, just print top level general help
|
||||
// no args given, just print top level general help.
|
||||
return
|
||||
}
|
||||
|
||||
foundCommands := &[]*Command{}
|
||||
foundCmds := &[]*Command{}
|
||||
fops := make(map[string]config.Option)
|
||||
foundOptions := &fops
|
||||
foundCommandWhole := false
|
||||
foundOptionWhole := false
|
||||
foundOpts := &fops
|
||||
var foundCmdWhole, foundOptWhole bool
|
||||
c.ForEach(func(cm *Command, depth int) bool {
|
||||
for i := range args {
|
||||
// check for match of current command name
|
||||
if strings.Contains(util.Norm(cm.Name), util.Norm(args[i])) {
|
||||
if util.Norm(cm.Name) == util.Norm(args[i]) {
|
||||
if len(args) == 1 {
|
||||
foundCommandWhole = true
|
||||
*foundCommands = append(*foundCommands, cm)
|
||||
break
|
||||
}
|
||||
if util.Norm(cm.Name) == util.Norm(args[i]) {
|
||||
if len(args) == 1 {
|
||||
foundCmdWhole = true
|
||||
*foundCmds = append(*foundCmds, cm)
|
||||
break
|
||||
}
|
||||
*foundCommands = append(*foundCommands, cm)
|
||||
}
|
||||
// or partial match.
|
||||
if strings.Contains(util.Norm(cm.Name),
|
||||
util.Norm(args[i])) {
|
||||
|
||||
*foundCmds = append(*foundCmds, cm)
|
||||
}
|
||||
// check for matches on configs
|
||||
for ops := range cm.Configs {
|
||||
// log.I.Ln(ops, cm.Name, Norm(ops), Norm(args[i]))
|
||||
if strings.Contains(util.Norm(ops), util.Norm(args[i])) {
|
||||
// in the case of specifying a command and an option
|
||||
// and the option is from the command, and there is
|
||||
// only two args, and the option is fully named, not
|
||||
// just partial matched, clear found options and
|
||||
// break to return one command one option,
|
||||
// which later is recognised to indicate show detail
|
||||
if len(args) == 2 && len(*foundCommands) == 1 &&
|
||||
util.Norm(ops) == util.Norm(args[i]) {
|
||||
if cm.Configs[ops].Path().Equal(cm.Path) {
|
||||
*foundOptions = make(map[string]config.Option)
|
||||
(*foundOptions)[ops] = cm.Configs[ops]
|
||||
foundOptionWhole = true
|
||||
return false
|
||||
}
|
||||
if strings.Contains(util.Norm(ops),
|
||||
util.Norm(args[i])) {
|
||||
// in the case of specifying a command
|
||||
// and an option and the option is from
|
||||
// the command, and there is only two
|
||||
// args, and the option is fully named,
|
||||
// not just partial matched, clear found
|
||||
// options and break to return one
|
||||
// command one option, which later is
|
||||
// recognised to indicate show detail
|
||||
if len(args) == 2 &&
|
||||
len(*foundCmds) == 1 &&
|
||||
util.Norm(ops) ==
|
||||
util.Norm(args[i]) &&
|
||||
cm.Configs[ops].Path().
|
||||
Equal(cm.Path) {
|
||||
|
||||
*foundOpts = make(map[string]config.Option)
|
||||
(*foundOpts)[ops] = cm.Configs[ops]
|
||||
foundOptWhole = true
|
||||
return false
|
||||
} else {
|
||||
(*foundOptions)[ops] = cm.Configs[ops]
|
||||
(*foundOpts)[ops] =
|
||||
cm.Configs[ops]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,10 +131,9 @@ func HelpEntrypoint(c *Command, args []string) (err error) {
|
||||
w := new(tabwriter.Writer)
|
||||
// minwidth, tabwidth, padding, padchar, flags
|
||||
w.Init(&b, 8, 8, 0, '\t', 0)
|
||||
// log.I.S(bufWriter.String())
|
||||
switch {
|
||||
case foundCommandWhole && len(args) == 1:
|
||||
cm := (*foundCommands)[0]
|
||||
case foundCmdWhole && len(args) == 1:
|
||||
cm := (*foundCmds)[0]
|
||||
// Print command help information
|
||||
var outs CommandInfos
|
||||
for i := range cm.Commands {
|
||||
@@ -131,7 +144,6 @@ func HelpEntrypoint(c *Command, args []string) (err error) {
|
||||
})
|
||||
}
|
||||
sort.Sort(outs)
|
||||
// out += fmt.Sprintf("\n%s - %s\n\n", cm.Path, cm.Description)
|
||||
out += fmt.Sprintf(
|
||||
"Help information for command '%s':\n\n",
|
||||
args[0])
|
||||
@@ -150,7 +162,8 @@ func HelpEntrypoint(c *Command, args []string) (err error) {
|
||||
outs[i].name, outs[i].description,
|
||||
def); e != nil {
|
||||
|
||||
_, _ = fmt.Fprintln(os.Stderr, "error printing columns")
|
||||
_, _ = fmt.Fprintln(os.Stderr,
|
||||
"error printing columns")
|
||||
} else {
|
||||
w.Flush()
|
||||
out += b.String()
|
||||
@@ -158,18 +171,23 @@ func HelpEntrypoint(c *Command, args []string) (err error) {
|
||||
}
|
||||
}
|
||||
if cm.Default != nil {
|
||||
out += "\n\t\t* indicates default if no subcommand given\n\n"
|
||||
out += "\n\t\t* indicates default if no " +
|
||||
"subcommand given\n\n"
|
||||
} else {
|
||||
out += "\n"
|
||||
}
|
||||
out += fmt.Sprintf("for more information on subcommands:\n\n")
|
||||
out += fmt.Sprintf("\t%s help <subcommand>\n\n", os.Args[0])
|
||||
out += fmt.Sprintf(
|
||||
"for more information on subcommands:\n\n")
|
||||
out += fmt.Sprintf(
|
||||
"\t%s help <subcommand>\n\n", os.Args[0])
|
||||
}
|
||||
if len(cm.Configs) > 0 {
|
||||
out += "Available configuration options on this command:\n\n"
|
||||
out += fmt.Sprintf("\t-%s %v - %s (default: '%s')\n",
|
||||
"flag", "[alias1 alias2]", "description", "default")
|
||||
out += "\t\t(prefix '-' can also be '--', value can follow after space or with '=' and no space)\n\n"
|
||||
"flag", "[alias1 alias2]", "description",
|
||||
"default")
|
||||
out += "\t\t(prefix '-' can also be '--', value can " +
|
||||
"follow after space or with '=' and no space)\n\n"
|
||||
var opts []string
|
||||
for i := range c.Configs {
|
||||
opts = append(opts, i)
|
||||
@@ -193,15 +211,15 @@ func HelpEntrypoint(c *Command, args []string) (err error) {
|
||||
"\nUse 'help %s <option>' to get details on option.\n",
|
||||
cm.Name)
|
||||
}
|
||||
case len(*foundOptions) == 1 &&
|
||||
(len(*foundCommands) == 0 ||
|
||||
foundOptionWhole):
|
||||
case len(*foundOpts) == 1 &&
|
||||
(len(*foundCmds) == 0 ||
|
||||
foundOptWhole):
|
||||
|
||||
// For this case there is only one option, and one match, so we
|
||||
// will print the full details as it is unambiguous.
|
||||
for i := range *foundOptions {
|
||||
for i := range *foundOpts {
|
||||
// there is only one but a range statement grabs it
|
||||
op := (*foundOptions)[i]
|
||||
op := (*foundOpts)[i]
|
||||
om := op.Meta()
|
||||
path := op.Path().TrimPrefix().String()
|
||||
if len(path) > 0 {
|
||||
@@ -210,13 +228,16 @@ func HelpEntrypoint(c *Command, args []string) (err error) {
|
||||
search := strings.Join(args, " ")
|
||||
if len(args) > 1 {
|
||||
out += fmt.Sprintf(
|
||||
"Help information for search terms '%s':\n\n", search)
|
||||
"Help information for search terms '%s':\n\n",
|
||||
search)
|
||||
} else {
|
||||
out += fmt.Sprintf("Help information for option '%s'\n\n",
|
||||
out += fmt.Sprintf(
|
||||
"Help information for option '%s'\n\n",
|
||||
i)
|
||||
}
|
||||
if len(path) > 1 {
|
||||
out += fmt.Sprintf("Command Path:\n\n\t%s\n\n", path)
|
||||
out += fmt.Sprintf("Command Path:\n\n\t%s\n\n",
|
||||
path)
|
||||
}
|
||||
out += fmt.Sprintf("%s [-%s]\n\n", i, strings.ToLower(i))
|
||||
out += fmt.Sprintf("\t%s\n\n", om.Description())
|
||||
@@ -225,7 +246,7 @@ func HelpEntrypoint(c *Command, args []string) (err error) {
|
||||
out += fmt.Sprintf("Documentation:\n\n%s\n\n",
|
||||
IndentTextBlock(om.Documentation(), 1))
|
||||
}
|
||||
case len(*foundCommands) > 0 || len(*foundOptions) > 0:
|
||||
case len(*foundCmds) > 0 || len(*foundOpts) > 0:
|
||||
// if the text was not a command and one of its options, just
|
||||
// show all partial matches of both commands and options in
|
||||
// summary with their relevant paths
|
||||
@@ -237,26 +258,27 @@ func HelpEntrypoint(c *Command, args []string) (err error) {
|
||||
out += fmt.Sprintf(
|
||||
"Help information for search term%s '%s':\n\n",
|
||||
plural, search)
|
||||
if len(*foundCommands)+len(*foundOptions) > 1 {
|
||||
if len(*foundCmds)+len(*foundOpts) > 1 {
|
||||
out += "Multiple matches found:\n\n"
|
||||
}
|
||||
if len(*foundCommands) > 0 {
|
||||
if len(*foundCmds) > 0 {
|
||||
plural := ""
|
||||
if len(*foundCommands) > 1 {
|
||||
if len(*foundCmds) > 1 {
|
||||
plural = "s"
|
||||
}
|
||||
out += fmt.Sprintf("Command%s\n\n", plural)
|
||||
for i := range *foundCommands {
|
||||
cm := (*foundCommands)[i]
|
||||
for i := range *foundCmds {
|
||||
cm := (*foundCmds)[i]
|
||||
fmt.Fprintf(w, "\t%s\t %s\n",
|
||||
strings.ToLower(cm.Name), cm.Description)
|
||||
strings.ToLower(cm.Name),
|
||||
cm.Description)
|
||||
}
|
||||
out += "\n"
|
||||
}
|
||||
if len(*foundOptions) > 0 {
|
||||
if len(*foundOpts) > 0 {
|
||||
out += fmt.Sprintf("Options:\n\n")
|
||||
for i := range *foundOptions {
|
||||
op := (*foundOptions)[i]
|
||||
for i := range *foundOpts {
|
||||
op := (*foundOpts)[i]
|
||||
om := op.Meta()
|
||||
path := op.Path().TrimPrefix().String()
|
||||
if len(path) > 0 {
|
||||
@@ -288,7 +310,6 @@ func HelpEntrypoint(c *Command, args []string) (err error) {
|
||||
})
|
||||
}
|
||||
sort.Sort(outs)
|
||||
// log.I.S(outs)
|
||||
plural := ""
|
||||
pluralVerb := "is"
|
||||
if len(c.Commands) > 1 {
|
||||
@@ -348,14 +369,16 @@ func HelpEntrypoint(c *Command, args []string) (err error) {
|
||||
fmt.Fprint(w, "\n\tFormat of configuration items:\n\n")
|
||||
fmt.Fprintf(w, "\t\t-%s\t%v\t\n",
|
||||
"flag [alias1 alias2]", "description (default: )")
|
||||
fmt.Fprint(w, "\t\t(prefix '-' can also be '--', value can follow after space or with '=' and no space)\n\n")
|
||||
fmt.Fprint(w, "\t\t(prefix '-' can also be '--', value can "+
|
||||
"follow after space or with '=' and no space)\n\n")
|
||||
w.Flush()
|
||||
out += b.String()
|
||||
b.Reset()
|
||||
out += fmt.Sprintf("For more information:\n\n")
|
||||
out += fmt.Sprintf("\t%s help <subcommand>\n\n", c.Name)
|
||||
out += "\tUse 'help <option>' to get details on option.\n"
|
||||
out += "\tUse 'help help' to learn more about the help function.\n\n"
|
||||
out += "\tUse 'help help' to learn more about the help " +
|
||||
"function.\n\n"
|
||||
|
||||
}
|
||||
fmt.Print(out)
|
||||
|
||||
@@ -95,7 +95,8 @@ func (c *Command) MarshalText() (text []byte, err error) {
|
||||
current = current.Parent
|
||||
}
|
||||
text = append(text,
|
||||
[]byte("# "+cmdPath+cmd.Name+": "+cmd.Description+"\n")...)
|
||||
[]byte("# "+cmdPath+cmd.Name+": "+
|
||||
cmd.Description+"\n")...)
|
||||
text = append(text,
|
||||
[]byte("["+cmdPath+cmd.Name+"]"+"\n\n")...)
|
||||
}
|
||||
@@ -141,32 +142,32 @@ func (c *Command) UnmarshalText(t []byte) (err error) {
|
||||
switch op.Type() {
|
||||
case meta.Bool:
|
||||
v := op.(*toggle.Opt)
|
||||
nv, ok := oo[i].value.(bool)
|
||||
if ok {
|
||||
if nv, ok := oo[i].value.(bool); ok {
|
||||
v.FromValue(nv)
|
||||
log.T.Ln("setting value of", oo[i].path,
|
||||
"to", nv)
|
||||
}
|
||||
log.T.Ln("setting value of", oo[i].path, "to", nv)
|
||||
case meta.Duration:
|
||||
v := op.(*duration.Opt)
|
||||
nv, ok := oo[i].value.(time.Duration)
|
||||
if ok {
|
||||
if nv, ok := oo[i].value.(time.Duration); ok {
|
||||
v.FromValue(nv)
|
||||
log.T.Ln("setting value of", oo[i].path,
|
||||
"to", nv)
|
||||
}
|
||||
log.T.Ln("setting value of", oo[i].path, "to", nv)
|
||||
case meta.Float:
|
||||
v := op.(*float.Opt)
|
||||
nv, ok := oo[i].value.(float64)
|
||||
if ok {
|
||||
if nv, ok := oo[i].value.(float64); ok {
|
||||
v.FromValue(nv)
|
||||
log.T.Ln("setting value of", oo[i].path,
|
||||
"to", nv)
|
||||
}
|
||||
log.T.Ln("setting value of", oo[i].path, "to", nv)
|
||||
case meta.Integer:
|
||||
v := op.(*integer.Opt)
|
||||
nv, ok := oo[i].value.(int64)
|
||||
if ok {
|
||||
if nv, ok := oo[i].value.(int64); ok {
|
||||
v.FromValue(nv)
|
||||
log.T.Ln("setting value of", oo[i].path,
|
||||
"to", nv)
|
||||
}
|
||||
log.T.Ln("setting value of", oo[i].path, "to", nv)
|
||||
case meta.List:
|
||||
v := op.(*list.Opt)
|
||||
nv, ok := oo[i].value.([]interface{})
|
||||
@@ -177,16 +178,18 @@ func (c *Command) UnmarshalText(t []byte) (err error) {
|
||||
if ok {
|
||||
v.FromValue(strings)
|
||||
}
|
||||
log.T.Ln("setting value of", oo[i].path, "to", nv)
|
||||
log.T.Ln("setting value of", oo[i].path, "to",
|
||||
nv)
|
||||
case meta.Text:
|
||||
v := op.(*text.Opt)
|
||||
nv, ok := oo[i].value.(string)
|
||||
if ok {
|
||||
if nv, ok := oo[i].value.(string); ok {
|
||||
v.FromValue(nv)
|
||||
log.T.Ln("setting value of", oo[i].path,
|
||||
"to", nv)
|
||||
}
|
||||
log.T.Ln("setting value of", oo[i].path, "to", nv)
|
||||
default:
|
||||
log.E.Ln("option type unknown:", oo[i].path, op.Type())
|
||||
log.E.Ln("option type unknown:",
|
||||
oo[i].path, op.Type())
|
||||
}
|
||||
} else {
|
||||
log.D.Ln("option not found:", oo[i].path)
|
||||
@@ -199,7 +202,8 @@ func (c *Command) LoadConfig() (err error) {
|
||||
var file io.Reader
|
||||
if file, err = os.Open(cfgFile.Expanded()); err != nil {
|
||||
log.T.F("creating config file at path: '%s'", cfgFile.Expanded())
|
||||
// If no config found, create data dir and drop the default in place
|
||||
// If no config found, create data dir and drop the default in
|
||||
// place
|
||||
return c.SaveConfig()
|
||||
} else {
|
||||
var all []byte
|
||||
@@ -213,8 +217,8 @@ func (c *Command) LoadConfig() (err error) {
|
||||
}
|
||||
|
||||
func (c *Command) SaveConfig() (err error) {
|
||||
datadir := c.GetOpt(path2.Path{c.Name, "DataDir"})
|
||||
if err = os.MkdirAll(datadir.Expanded(), 0700); log.E.Chk(err) {
|
||||
dd := c.GetOpt(path2.Path{c.Name, "DataDir"})
|
||||
if err = os.MkdirAll(dd.Expanded(), 0700); log.E.Chk(err) {
|
||||
return err
|
||||
}
|
||||
var f *os.File
|
||||
|
||||
@@ -4,11 +4,6 @@ import (
|
||||
"github.com/indra-labs/indra/pkg/slice"
|
||||
)
|
||||
|
||||
// var (
|
||||
// log = log2.GetLogger(indra.PathBase)
|
||||
// check = log.E.Chk
|
||||
// )
|
||||
|
||||
type Transport interface {
|
||||
Send(b slice.Bytes)
|
||||
Receive() <-chan slice.Bytes
|
||||
|
||||
@@ -9,18 +9,25 @@ import (
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/indra-labs/indra"
|
||||
log2 "github.com/indra-labs/indra/pkg/log"
|
||||
"go.uber.org/atomic"
|
||||
|
||||
"github.com/kardianos/osext"
|
||||
)
|
||||
|
||||
var (
|
||||
log = log2.GetLogger(indra.PathBase)
|
||||
check = log.E.Chk
|
||||
)
|
||||
|
||||
type HandlerWithSource struct {
|
||||
Source string
|
||||
Fn func()
|
||||
}
|
||||
|
||||
var (
|
||||
Restart bool // = true
|
||||
Restart bool
|
||||
requested atomic.Bool
|
||||
// ch is used to receive SIGINT (Ctrl+C) signals.
|
||||
ch chan os.Signal
|
||||
@@ -132,7 +139,7 @@ out:
|
||||
|
||||
// AddHandler adds a handler to call when a SIGINT (Ctrl+C) is received.
|
||||
func AddHandler(handler func()) {
|
||||
// Create the channel and start the main interrupt handler which invokes
|
||||
// Create the channel and start the main interrupt handler that invokes
|
||||
// all other callbacks and exits if not already done.
|
||||
_, loc, line, _ := runtime.Caller(1)
|
||||
msg := fmt.Sprintf("%s:%d", loc, line)
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
package interrupt
|
||||
|
||||
import (
|
||||
"github.com/indra-labs/indra"
|
||||
log2 "github.com/indra-labs/indra/pkg/log"
|
||||
)
|
||||
|
||||
var log = log2.GetLogger(indra.PathBase)
|
||||
@@ -25,21 +25,21 @@ func (c PubKey) CopyBlinder() (blinder Blinder) {
|
||||
return
|
||||
}
|
||||
|
||||
// PubKey is the blinded hash of a public key used to conceal a message
|
||||
// public key from attackers.
|
||||
// PubKey is the blinded hash of a public key used to conceal a message public
|
||||
// key from attackers.
|
||||
type PubKey [Len]byte
|
||||
|
||||
type Blinder [BlindLen]byte
|
||||
type Hash [HashLen]byte
|
||||
|
||||
// GetCloak returns a value which a receiver with the private key can
|
||||
// identify the association of a message with the peer in order to retrieve the
|
||||
// private key to generate the message cipher.
|
||||
// GetCloak returns a value which a receiver with the private key can identify
|
||||
// the association of a message with the peer in order to retrieve the private
|
||||
// key to generate the message cipher.
|
||||
//
|
||||
// The three byte blinding factor concatenated in front of the public key
|
||||
// generates the 5 bytes at the end of the PubKey code. In this way the
|
||||
// source public key it relates to is hidden to any who don't have this public
|
||||
// key, which only the parties know.
|
||||
// generates the 5 bytes at the end of the PubKey code. In this way the source
|
||||
// public key it relates to is hidden to any who don't have this public key,
|
||||
// which only the parties know.
|
||||
func GetCloak(s *pub.Key) (c PubKey) {
|
||||
var blinder Blinder
|
||||
var n int
|
||||
@@ -58,54 +58,12 @@ func Cloak(b Blinder, key pub.Bytes) (c PubKey) {
|
||||
return
|
||||
}
|
||||
|
||||
// Match uses the cached public key and the provided blinding factor to
|
||||
// match the source public key so the packet address field is only recognisable
|
||||
// to the intended recipient.
|
||||
// Match uses the cached public key and the provided blinding factor to match
|
||||
// the source public key so the packet address field is only recognisable to the
|
||||
// intended recipient.
|
||||
func Match(r PubKey, k pub.Bytes) bool {
|
||||
var b Blinder
|
||||
copy(b[:], r[:BlindLen])
|
||||
hash := Cloak(b, k)
|
||||
return r == hash
|
||||
}
|
||||
|
||||
// // Receiver wraps a private key with pre-generated public key used to recognise
|
||||
// // and associate messages from a specific peer, the public key is sent in a
|
||||
// // previous message inside the encrypted payload and this structure is cached to
|
||||
// // identify the correct key to decrypt the message.
|
||||
// type Receiver struct {
|
||||
// *prv.Key
|
||||
// Pub *pub.Key
|
||||
// pub.Bytes
|
||||
// }
|
||||
//
|
||||
// // NewReceiver takes a private key and generates a Receiver for the address
|
||||
// // cache.
|
||||
// func NewReceiver(k *prv.Key) (a *Receiver) {
|
||||
// a = &Receiver{
|
||||
// Key: k,
|
||||
// Pub: pub.Derive(k),
|
||||
// }
|
||||
// a.Bytes = a.Pub.ToBytes()
|
||||
// return
|
||||
// }
|
||||
//
|
||||
|
||||
// // Sender is the raw bytes of a public key received in the metadata of a
|
||||
// // message.
|
||||
// type Sender struct {
|
||||
// *pub.Key
|
||||
// }
|
||||
//
|
||||
// // FromPub creates a Sender from a public key.
|
||||
// func FromPub(k *pub.Key) (s *Sender) {
|
||||
// s = &Sender{Key: k}
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// // FromBytes creates a Sender from a received public key bytes.
|
||||
// func FromBytes(pkb pub.Bytes) (s *Sender, e error) {
|
||||
// var pk *pub.Key
|
||||
// pk, e = pub.FromBytes(pkb[:])
|
||||
// s = &Sender{Key: pk}
|
||||
// return
|
||||
// }
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/indra-labs/indra/pkg/sha256"
|
||||
)
|
||||
|
||||
// Compute computes an elliptic curve diffie hellman shared secret that can be
|
||||
// Compute computes an Elliptic Curve Diffie-Hellman shared secret that can be
|
||||
// decrypted by the holder of the private key matching the public key provided.
|
||||
func Compute(prv *prv.Key, pub *pub.Key) sha256.Hash {
|
||||
return sha256.Single(
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
package sig
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
|
||||
"github.com/indra-labs/indra"
|
||||
@@ -31,23 +29,6 @@ const Len = 65
|
||||
// extra bytes to also specify the public key of the signer.
|
||||
type Bytes [Len]byte
|
||||
|
||||
func New() Bytes { return Bytes{} }
|
||||
|
||||
// IsValid checks that the signature is the correct length. This avoids needing
|
||||
// to copy into a static array. Static arrays save on this code because they
|
||||
// automatically must be correct.
|
||||
func (sig Bytes) IsValid() (e error) {
|
||||
if len(sig) == Len {
|
||||
return
|
||||
}
|
||||
return fmt.Errorf(
|
||||
"signature incorrect length, expect %d, got %d",
|
||||
Len, len(sig))
|
||||
}
|
||||
|
||||
// FromBytes checks if signature bytes are the correct length to be a signature.
|
||||
func FromBytes(sig Bytes) (e error) { return sig.IsValid() }
|
||||
|
||||
// Sign produces an ECDSA BIP62 compact signature.
|
||||
func Sign(prv *prv.Key, hash sha256.Hash) (sig Bytes, e error) {
|
||||
copy(sig[:],
|
||||
|
||||
@@ -144,7 +144,7 @@ var (
|
||||
// allSubsystems stores all package subsystem names found in the current
|
||||
// application.
|
||||
allSubsystems []string
|
||||
codeLoc = false
|
||||
codeLoc = true
|
||||
)
|
||||
|
||||
func GetAllSubsystems() (o []string) {
|
||||
@@ -287,52 +287,51 @@ func logPrint(
|
||||
printFunc func() string,
|
||||
) func() {
|
||||
return func() {
|
||||
if level > Off && level <= logLevel {
|
||||
formatString := "%v%s%s%-6v %s\n"
|
||||
loc := ""
|
||||
tsf := timeStampFormat
|
||||
if codeLoc {
|
||||
formatString = "%-58v%s%s%-6v %s\n"
|
||||
loc = GetLoc(3, subsystem)
|
||||
tsf = LocTimeStampFormat
|
||||
}
|
||||
var app string
|
||||
if len(App) > 0 {
|
||||
fmt.Sprint(" [" + App + "]")
|
||||
}
|
||||
s := fmt.Sprintf(
|
||||
formatString,
|
||||
loc,
|
||||
color.Gray.Sprint(getTimeText(tsf)),
|
||||
app,
|
||||
LevelSpecs[level].Colorizer(
|
||||
" "+LevelSpecs[level].Name+" ",
|
||||
),
|
||||
printFunc(),
|
||||
)
|
||||
writerMx.Lock()
|
||||
defer writerMx.Unlock()
|
||||
fmt.Fprintf(writer, s)
|
||||
if level <= Off && level > logLevel {
|
||||
return
|
||||
}
|
||||
formatString := "%v%s%s%-6v %s\n"
|
||||
loc := ""
|
||||
tsf := timeStampFormat
|
||||
if codeLoc {
|
||||
formatString = "%-58v%s%s%-6v %s\n"
|
||||
loc = GetLoc(3, subsystem)
|
||||
tsf = LocTimeStampFormat
|
||||
}
|
||||
var app string
|
||||
if len(App) > 0 {
|
||||
fmt.Sprint(" [" + App + "]")
|
||||
}
|
||||
s := fmt.Sprintf(
|
||||
formatString,
|
||||
loc,
|
||||
color.Gray.Sprint(getTimeText(tsf)),
|
||||
app,
|
||||
LevelSpecs[level].Colorizer(
|
||||
" "+LevelSpecs[level].Name+" ",
|
||||
),
|
||||
printFunc(),
|
||||
)
|
||||
writerMx.Lock()
|
||||
defer writerMx.Unlock()
|
||||
fmt.Fprintf(writer, s)
|
||||
}
|
||||
}
|
||||
|
||||
// sortSubsystemsList sorts the list of subsystems, to keep the data read-only,
|
||||
// call this function right at the top of the main, which runs after
|
||||
// declarations and main/init. Really this is just here to alert the reader.
|
||||
func sortSubsystemsList() {
|
||||
sort.Strings(allSubsystems)
|
||||
}
|
||||
func sortSubsystemsList() { sort.Strings(allSubsystems) }
|
||||
|
||||
// Add adds a subsystem to the list of known subsystems and returns the
|
||||
// string so it is nice and neat in the package logg.go file
|
||||
func Add(pathBase string) (subsystem string) {
|
||||
var ok bool
|
||||
var file string
|
||||
_, file, _, ok = runtime.Caller(2)
|
||||
var fh string
|
||||
_, fh, _, ok = runtime.Caller(2)
|
||||
if ok {
|
||||
r := strings.Split(file, pathBase)
|
||||
fromRoot := filepath.Base(file)
|
||||
r := strings.Split(fh, pathBase)
|
||||
fromRoot := filepath.Base(fh)
|
||||
if len(r) > 1 {
|
||||
fromRoot = r[1]
|
||||
}
|
||||
@@ -349,7 +348,6 @@ func Add(pathBase string) (subsystem string) {
|
||||
// GetLogger returns a set of LevelPrinter with their subsystem preloaded
|
||||
func GetLogger(pathBase string) (l *Logger) {
|
||||
ss := Add(pathBase)
|
||||
// fmt.Println("subsystems:", allSubsystems)
|
||||
return &Logger{
|
||||
getOnePrinter(Fatal, ss),
|
||||
getOnePrinter(Error, ss),
|
||||
|
||||
@@ -5,6 +5,7 @@ package node
|
||||
import (
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"time"
|
||||
|
||||
"github.com/indra-labs/indra"
|
||||
"github.com/indra-labs/indra/pkg/ifc"
|
||||
@@ -26,11 +27,13 @@ var (
|
||||
// this except when the netip.AddrPort is known via the packet sender address.
|
||||
type Node struct {
|
||||
nonce.ID
|
||||
Addr string
|
||||
AddrPort *netip.AddrPort
|
||||
HeaderPub *pub.Key
|
||||
HeaderBytes pub.Bytes
|
||||
HeaderPrv *prv.Key
|
||||
Addr string
|
||||
AddrPort *netip.AddrPort
|
||||
IdentityPub *pub.Key
|
||||
IdentityBytes pub.Bytes
|
||||
IdentityPrv *prv.Key
|
||||
PingCount int
|
||||
LastSeen time.Time
|
||||
Services
|
||||
ifc.Transport
|
||||
}
|
||||
@@ -42,17 +45,18 @@ func New(addr *netip.AddrPort, hdr *pub.Key, hdrPrv *prv.Key,
|
||||
|
||||
id = nonce.NewID()
|
||||
n = &Node{
|
||||
ID: id,
|
||||
Addr: addr.String(),
|
||||
AddrPort: addr,
|
||||
Transport: tpt,
|
||||
HeaderPub: hdr,
|
||||
HeaderBytes: hdr.ToBytes(),
|
||||
HeaderPrv: hdrPrv,
|
||||
ID: id,
|
||||
Addr: addr.String(),
|
||||
AddrPort: addr,
|
||||
Transport: tpt,
|
||||
IdentityPub: hdr,
|
||||
IdentityBytes: hdr.ToBytes(),
|
||||
IdentityPrv: hdrPrv,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SendTo delivers a message to a service identified by its port.
|
||||
func (n *Node) SendTo(port uint16, b slice.Bytes) (e error) {
|
||||
e = fmt.Errorf("port not registered %d", port)
|
||||
for i := range n.Services {
|
||||
@@ -65,6 +69,7 @@ func (n *Node) SendTo(port uint16, b slice.Bytes) (e error) {
|
||||
return
|
||||
}
|
||||
|
||||
// ReceiveFrom returns the channel that receives messages for a given port.
|
||||
func (n *Node) ReceiveFrom(port uint16) (b <-chan slice.Bytes) {
|
||||
for i := range n.Services {
|
||||
if n.Services[i].Port == port {
|
||||
@@ -81,14 +86,11 @@ type Nodes []*Node
|
||||
// NewNodes creates an empty Nodes
|
||||
func NewNodes() (n Nodes) { return Nodes{} }
|
||||
|
||||
func (n Nodes) Len() int {
|
||||
return len(n)
|
||||
}
|
||||
// Len returns the length of a Nodes.
|
||||
func (n Nodes) Len() int { return len(n) }
|
||||
|
||||
// Add a Node to a Nodes.
|
||||
func (n Nodes) Add(nn *Node) Nodes {
|
||||
return append(n, nn)
|
||||
}
|
||||
func (n Nodes) Add(nn *Node) Nodes { return append(n, nn) }
|
||||
|
||||
// FindByID searches for a Node by ID.
|
||||
func (n Nodes) FindByID(i nonce.ID) (no *Node) {
|
||||
@@ -114,21 +116,18 @@ func (n Nodes) FindByAddrPort(id *netip.AddrPort) (no *Node) {
|
||||
|
||||
// DeleteByID deletes a node identified by an ID.
|
||||
func (n Nodes) DeleteByID(ii nonce.ID) (nn Nodes, e error) {
|
||||
e = fmt.Errorf("id %x not found", ii)
|
||||
e, nn = fmt.Errorf("id %x not found", ii), n
|
||||
for i := range n {
|
||||
if n[i].ID == ii {
|
||||
n = append(n[:i], n[i+1:]...)
|
||||
e = nil
|
||||
break
|
||||
return append(n[:i], n[i+1:]...), nil
|
||||
}
|
||||
}
|
||||
return n, e
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteByAddrPort deletes a node identified by a netip.AddrPort.
|
||||
func (n Nodes) DeleteByAddrPort(ip *netip.AddrPort) (nn Nodes, e error) {
|
||||
e = fmt.Errorf("node with ip %v not found", ip)
|
||||
nn = n
|
||||
e, nn = fmt.Errorf("node with ip %v not found", ip), n
|
||||
for i := range n {
|
||||
if n[i].AddrPort.String() == ip.String() {
|
||||
nn = append(n[:i], n[i+1:]...)
|
||||
@@ -138,13 +137,3 @@ func (n Nodes) DeleteByAddrPort(ip *netip.AddrPort) (nn Nodes, e error) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type Selector func(n Nodes, exit *Node, count int) (selected Nodes)
|
||||
|
||||
func (n Nodes) Select(selector Selector, exit *Node, count int) (selected Nodes) {
|
||||
if selector == nil {
|
||||
log.E.Ln("no selector function given")
|
||||
return
|
||||
}
|
||||
return selector(n, exit, count)
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
package node
|
||||
@@ -8,20 +8,20 @@ import (
|
||||
|
||||
const IDLen = 8
|
||||
|
||||
// ID is a value generated by a hash chain that renews at first use and every
|
||||
// time it generates 65536 new ID's.
|
||||
type ID [IDLen]byte
|
||||
|
||||
var seed sha256.Hash
|
||||
var counter uint16
|
||||
|
||||
func reseed() {
|
||||
var c int
|
||||
var e error
|
||||
if c, e = rand.Read(seed[:]); check(e) && c != IDLen {
|
||||
panic(e)
|
||||
if c, e := rand.Read(seed[:]); check(e) && c != IDLen {
|
||||
}
|
||||
counter++
|
||||
}
|
||||
|
||||
// NewID returns a random 8 byte nonce to be used as identifiers.
|
||||
func NewID() (t ID) {
|
||||
if counter == 0 {
|
||||
reseed()
|
||||
|
||||
@@ -22,7 +22,7 @@ type IV [IVLen]byte
|
||||
|
||||
// New reads a nonce from a cryptographically secure random number source
|
||||
func New() (n IV) {
|
||||
if _, e := rand.Read(n[:]); check(e) {
|
||||
if c, e := rand.Read(n[:]); check(e) && c != IDLen {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
package integer
|
||||
|
||||
import (
|
||||
"github.com/indra-labs/indra"
|
||||
log2 "github.com/indra-labs/indra/pkg/log"
|
||||
)
|
||||
|
||||
var log = log2.GetLogger(indra.PathBase)
|
||||
@@ -4,12 +4,19 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/indra-labs/indra"
|
||||
log2 "github.com/indra-labs/indra/pkg/log"
|
||||
"github.com/indra-labs/indra/pkg/opts/config"
|
||||
"github.com/indra-labs/indra/pkg/opts/meta"
|
||||
"github.com/indra-labs/indra/pkg/path"
|
||||
"go.uber.org/atomic"
|
||||
)
|
||||
|
||||
var (
|
||||
log = log2.GetLogger(indra.PathBase)
|
||||
check = log.E.Chk
|
||||
)
|
||||
|
||||
type Opt struct {
|
||||
p path.Path
|
||||
m meta.Metadata
|
||||
@@ -53,8 +60,7 @@ func (o *Opt) FromValue(v int64) *Opt {
|
||||
func (o *Opt) FromString(s string) (e error) {
|
||||
s = strings.TrimSpace(s)
|
||||
var p int64
|
||||
p, e = strconv.ParseInt(s, 10, 64)
|
||||
if e != nil {
|
||||
if p, e = strconv.ParseInt(s, 10, 64); check(e) {
|
||||
return e
|
||||
}
|
||||
o.v.Store(p)
|
||||
@@ -70,7 +76,7 @@ func (o *Opt) Expanded() (s string) { return o.String() }
|
||||
|
||||
func (o *Opt) SetExpanded(s string) {
|
||||
err := o.FromString(s)
|
||||
log.E.Chk(err)
|
||||
check(err)
|
||||
}
|
||||
|
||||
func (o *Opt) Value() (c config.Concrete) {
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
package duration
|
||||
|
||||
import (
|
||||
"github.com/indra-labs/indra"
|
||||
log2 "github.com/indra-labs/indra/pkg/log"
|
||||
)
|
||||
|
||||
var log = log2.GetLogger(indra.PathBase)
|
||||
@@ -5,12 +5,19 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/indra-labs/indra"
|
||||
log2 "github.com/indra-labs/indra/pkg/log"
|
||||
"github.com/indra-labs/indra/pkg/opts/config"
|
||||
"github.com/indra-labs/indra/pkg/opts/meta"
|
||||
"github.com/indra-labs/indra/pkg/path"
|
||||
"go.uber.org/atomic"
|
||||
)
|
||||
|
||||
var (
|
||||
log = log2.GetLogger(indra.PathBase)
|
||||
check = log.E.Chk
|
||||
)
|
||||
|
||||
type Opt struct {
|
||||
p path.Path
|
||||
m meta.Metadata
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
package float
|
||||
|
||||
import (
|
||||
"github.com/indra-labs/indra"
|
||||
log2 "github.com/indra-labs/indra/pkg/log"
|
||||
)
|
||||
|
||||
var log = log2.GetLogger(indra.PathBase)
|
||||
@@ -4,12 +4,19 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/indra-labs/indra"
|
||||
log2 "github.com/indra-labs/indra/pkg/log"
|
||||
"github.com/indra-labs/indra/pkg/opts/config"
|
||||
"github.com/indra-labs/indra/pkg/opts/meta"
|
||||
"github.com/indra-labs/indra/pkg/path"
|
||||
"go.uber.org/atomic"
|
||||
)
|
||||
|
||||
var (
|
||||
log = log2.GetLogger(indra.PathBase)
|
||||
check = log.E.Chk
|
||||
)
|
||||
|
||||
type Opt struct {
|
||||
p path.Path
|
||||
m meta.Metadata
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
package list
|
||||
|
||||
import (
|
||||
"github.com/indra-labs/indra"
|
||||
log2 "github.com/indra-labs/indra/pkg/log"
|
||||
)
|
||||
|
||||
var log = log2.GetLogger(indra.PathBase)
|
||||
@@ -3,6 +3,8 @@ package list
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/indra-labs/indra"
|
||||
log2 "github.com/indra-labs/indra/pkg/log"
|
||||
"github.com/indra-labs/indra/pkg/opts/config"
|
||||
"github.com/indra-labs/indra/pkg/opts/meta"
|
||||
normalize2 "github.com/indra-labs/indra/pkg/opts/normalize"
|
||||
@@ -10,6 +12,11 @@ import (
|
||||
"go.uber.org/atomic"
|
||||
)
|
||||
|
||||
var (
|
||||
log = log2.GetLogger(indra.PathBase)
|
||||
check = log.E.Chk
|
||||
)
|
||||
|
||||
type Opt struct {
|
||||
p path.Path
|
||||
m meta.Metadata
|
||||
@@ -54,11 +61,9 @@ func (o *Opt) FromValue(v []string) *Opt {
|
||||
func (o *Opt) FromString(s string) (e error) {
|
||||
s = strings.TrimSpace(s)
|
||||
split := strings.Split(s, ",")
|
||||
|
||||
if len(split) == 1 && split[0] == "" {
|
||||
split = make([]string, 0)
|
||||
}
|
||||
|
||||
o.v.Store(split)
|
||||
e = o.RunHooks()
|
||||
return
|
||||
|
||||
@@ -3,6 +3,14 @@ package normalize
|
||||
import (
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"github.com/indra-labs/indra"
|
||||
log2 "github.com/indra-labs/indra/pkg/log"
|
||||
)
|
||||
|
||||
var (
|
||||
log = log2.GetLogger(indra.PathBase)
|
||||
check = log.E.Chk
|
||||
)
|
||||
|
||||
// Address returns addr with the passed default port appended if there is not
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
package normalize
|
||||
|
||||
import (
|
||||
"github.com/indra-labs/indra"
|
||||
log2 "github.com/indra-labs/indra/pkg/log"
|
||||
)
|
||||
|
||||
var log = log2.GetLogger(indra.PathBase)
|
||||
@@ -15,13 +15,13 @@ func ResolvePath(input, appName string, abs bool) (cleaned string, e error) {
|
||||
input = strings.Replace(input, "~", homeDir, 1)
|
||||
cleaned = filepath.Clean(input)
|
||||
} else {
|
||||
|
||||
if abs {
|
||||
if cleaned, e = filepath.Abs(cleaned); log.E.Chk(e) {
|
||||
return
|
||||
}
|
||||
// if the path is relative, either ./ or not starting with a / then
|
||||
// we assume the path is relative to the app data directory
|
||||
// if the path is relative, either ./ or not starting
|
||||
// with a / then we assume the path is relative to the
|
||||
// app data directory
|
||||
} else if !strings.HasPrefix(string(os.PathSeparator), cleaned) ||
|
||||
strings.HasPrefix("."+string(os.PathSeparator), cleaned) {
|
||||
|
||||
@@ -37,9 +37,8 @@ func getHomeDir() (homeDir string) {
|
||||
if usr, e = user.Current(); !log.E.Chk(e) {
|
||||
homeDir = usr.HomeDir
|
||||
}
|
||||
// Fall back to standard HOME environment variable that
|
||||
// works for most POSIX OSes if the directory from the
|
||||
// Go standard lib failed.
|
||||
// Fall back to standard HOME environment variable that works for most
|
||||
// POSIX OSes if the directory from the Go standard lib failed.
|
||||
if e != nil || homeDir == "" {
|
||||
homeDir = os.Getenv("HOME")
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
package text
|
||||
|
||||
import (
|
||||
"github.com/indra-labs/indra"
|
||||
log2 "github.com/indra-labs/indra/pkg/log"
|
||||
)
|
||||
|
||||
var log = log2.GetLogger(indra.PathBase)
|
||||
@@ -3,6 +3,8 @@ package text
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/indra-labs/indra"
|
||||
log2 "github.com/indra-labs/indra/pkg/log"
|
||||
"github.com/indra-labs/indra/pkg/opts/config"
|
||||
"github.com/indra-labs/indra/pkg/opts/meta"
|
||||
normalize2 "github.com/indra-labs/indra/pkg/opts/normalize"
|
||||
@@ -10,6 +12,11 @@ import (
|
||||
"go.uber.org/atomic"
|
||||
)
|
||||
|
||||
var (
|
||||
log = log2.GetLogger(indra.PathBase)
|
||||
check = log.E.Chk
|
||||
)
|
||||
|
||||
type Opt struct {
|
||||
p path.Path
|
||||
m meta.Metadata
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
package toggle
|
||||
|
||||
import (
|
||||
"github.com/indra-labs/indra"
|
||||
log2 "github.com/indra-labs/indra/pkg/log"
|
||||
)
|
||||
|
||||
var log = log2.GetLogger(indra.PathBase)
|
||||
@@ -5,12 +5,19 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/indra-labs/indra"
|
||||
log2 "github.com/indra-labs/indra/pkg/log"
|
||||
"github.com/indra-labs/indra/pkg/opts/config"
|
||||
"github.com/indra-labs/indra/pkg/opts/meta"
|
||||
"github.com/indra-labs/indra/pkg/path"
|
||||
"go.uber.org/atomic"
|
||||
)
|
||||
|
||||
var (
|
||||
log = log2.GetLogger(indra.PathBase)
|
||||
check = log.E.Chk
|
||||
)
|
||||
|
||||
type Opt struct {
|
||||
p path.Path
|
||||
m meta.Metadata
|
||||
|
||||
@@ -105,7 +105,8 @@ func Encode(ep EP) (pkt []byte, e error) {
|
||||
slice.EncodeUint32(Length, ep.Length)
|
||||
Deadline := slice.NewUint64()
|
||||
slice.EncodeUint64(Deadline, uint64(ep.Deadline.Unix()))
|
||||
pkt = make([]byte, slice.SumLen(Seq, Length, Deadline, ep.Data)+1+Overhead)
|
||||
pkt = make([]byte, slice.SumLen(Seq, Length, Deadline,
|
||||
ep.Data)+1+Overhead)
|
||||
// Append pubkey used for encryption key derivation.
|
||||
k := pub.Derive(ep.From).ToBytes()
|
||||
// Copy nonce, address and key over top of the header.
|
||||
@@ -136,8 +137,8 @@ func Encode(ep EP) (pkt []byte, e error) {
|
||||
func GetKeys(d []byte) (from *pub.Key, e error) {
|
||||
pktLen := len(d)
|
||||
if pktLen < Overhead {
|
||||
// If this isn't checked the slice operations later can
|
||||
// hit bounds errors.
|
||||
// If this isn't checked the slice operations later can hit
|
||||
// bounds errors.
|
||||
e = fmt.Errorf("packet too small, min %d, got %d",
|
||||
Overhead, pktLen)
|
||||
log.E.Ln(e)
|
||||
@@ -167,8 +168,8 @@ func GetKeys(d []byte) (from *pub.Key, e error) {
|
||||
func Decode(d []byte, from *pub.Key, to *prv.Key) (f *Packet, e error) {
|
||||
pktLen := len(d)
|
||||
if pktLen < Overhead {
|
||||
// If this isn't checked the slice operations later can
|
||||
// hit bounds errors.
|
||||
// If this isn't checked the slice operations later can hit
|
||||
// bounds errors.
|
||||
e = fmt.Errorf("packet too small, min %d, got %d",
|
||||
Overhead, pktLen)
|
||||
log.E.Ln(e)
|
||||
|
||||
@@ -17,16 +17,14 @@ func TestSplitJoin(t *testing.T) {
|
||||
var e error
|
||||
var payload []byte
|
||||
var pHash sha256.Hash
|
||||
|
||||
if payload, pHash, e = testutils.GenerateTestMessage(msgSize); check(e) {
|
||||
t.FailNow()
|
||||
}
|
||||
var sp, rp, Rp *prv.Key
|
||||
var sP, rP, RP *pub.Key
|
||||
if sp, rp, sP, rP, e = testutils.GenerateTestKeyPairs(); check(e) {
|
||||
var sp, rp *prv.Key
|
||||
var rP *pub.Key
|
||||
if sp, rp, _, rP, e = testutils.GenerateTestKeyPairs(); check(e) {
|
||||
t.FailNow()
|
||||
}
|
||||
_, _, _, _ = sP, Rp, RP, rp
|
||||
addr := rP
|
||||
params := EP{
|
||||
To: addr,
|
||||
@@ -77,17 +75,14 @@ func BenchmarkSplit(b *testing.B) {
|
||||
segSize := 1382
|
||||
var e error
|
||||
var payload []byte
|
||||
var hash sha256.Hash
|
||||
if payload, hash, e = testutils.GenerateTestMessage(msgSize); check(e) {
|
||||
if payload, _, e = testutils.GenerateTestMessage(msgSize); check(e) {
|
||||
b.Error(e)
|
||||
}
|
||||
_ = hash
|
||||
var sp, rp, Rp *prv.Key
|
||||
var sP, rP *pub.Key
|
||||
if sp, rp, sP, rP, e = testutils.GenerateTestKeyPairs(); check(e) {
|
||||
var sp *prv.Key
|
||||
var rP *pub.Key
|
||||
if sp, _, _, rP, e = testutils.GenerateTestKeyPairs(); check(e) {
|
||||
b.FailNow()
|
||||
}
|
||||
_, _, _ = sP, Rp, rp
|
||||
addr := rP
|
||||
for n := 0; n < b.N; n++ {
|
||||
params := EP{
|
||||
@@ -103,6 +98,17 @@ func BenchmarkSplit(b *testing.B) {
|
||||
}
|
||||
_ = splitted
|
||||
}
|
||||
|
||||
// Example benchmark results show about 10Mb/s/thread throughput
|
||||
// handling 64Kb messages.
|
||||
//
|
||||
// goos: linux
|
||||
// goarch: amd64
|
||||
// pkg: github.com/indra-labs/indra/pkg/packet
|
||||
// cpu: AMD Ryzen 7 5800H with Radeon Graphics
|
||||
// BenchmarkSplit
|
||||
// BenchmarkSplit-16 157 7670080 ns/op
|
||||
// PASS
|
||||
}
|
||||
|
||||
func TestRemovePacket(t *testing.T) {
|
||||
@@ -144,7 +150,9 @@ func TestSplitJoinFEC(t *testing.T) {
|
||||
var payload []byte
|
||||
var pHash sha256.Hash
|
||||
|
||||
if payload, pHash, e = testutils.GenerateTestMessage(msgSize); check(e) {
|
||||
if payload, pHash, e = testutils.GenerateTestMessage(
|
||||
msgSize); check(e) {
|
||||
|
||||
t.FailNow()
|
||||
}
|
||||
var punctures []int
|
||||
@@ -173,9 +181,11 @@ func TestSplitJoinFEC(t *testing.T) {
|
||||
t.FailNow()
|
||||
}
|
||||
overhead := ep.GetOverhead()
|
||||
segMap := NewSegments(len(ep.Data), segSize, overhead, ep.Parity)
|
||||
segMap := NewSegments(len(ep.Data), segSize, overhead,
|
||||
ep.Parity)
|
||||
for segs := range segMap {
|
||||
start, end := segMap[segs].DStart, segMap[segs].PEnd
|
||||
start := segMap[segs].DStart
|
||||
end := segMap[segs].PEnd
|
||||
cnt := end - start
|
||||
par := segMap[segs].PEnd - segMap[segs].DEnd
|
||||
a := make([][]byte, cnt)
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
package path
|
||||
|
||||
import (
|
||||
"github.com/indra-labs/indra"
|
||||
log2 "github.com/indra-labs/indra/pkg/log"
|
||||
)
|
||||
|
||||
var log = log2.GetLogger(indra.PathBase)
|
||||
@@ -3,9 +3,16 @@ package path
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/indra-labs/indra"
|
||||
log2 "github.com/indra-labs/indra/pkg/log"
|
||||
"github.com/indra-labs/indra/pkg/util"
|
||||
)
|
||||
|
||||
var (
|
||||
log = log2.GetLogger(indra.PathBase)
|
||||
check = log.E.Chk
|
||||
)
|
||||
|
||||
type Path []string
|
||||
|
||||
func (p Path) TrimPrefix() Path {
|
||||
@@ -15,14 +22,9 @@ func (p Path) TrimPrefix() Path {
|
||||
return p[:0]
|
||||
}
|
||||
|
||||
func (p Path) String() string {
|
||||
return strings.Join(p, " ")
|
||||
}
|
||||
func (p Path) String() string { return strings.Join(p, " ") }
|
||||
|
||||
func From(s string) (p Path) {
|
||||
p = strings.Split(s, " ")
|
||||
return
|
||||
}
|
||||
func From(s string) (p Path) { return strings.Split(s, " ") }
|
||||
|
||||
func (p Path) Parent() (p1 Path) {
|
||||
if len(p) > 0 {
|
||||
@@ -31,11 +33,7 @@ func (p Path) Parent() (p1 Path) {
|
||||
return
|
||||
}
|
||||
|
||||
func (p Path) Child(child string) (p1 Path) {
|
||||
p1 = append(p, child)
|
||||
// log.I.Ln(p, p1)
|
||||
return
|
||||
}
|
||||
func (p Path) Child(child string) (p1 Path) { return append(p, child) }
|
||||
|
||||
func (p Path) Common(p2 Path) (o Path) {
|
||||
for i := range p {
|
||||
@@ -61,6 +59,4 @@ func (p Path) Equal(p2 Path) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func GetIndent(d int) string {
|
||||
return strings.Repeat("\t", d)
|
||||
}
|
||||
func GetIndent(d int) string { return strings.Repeat("\t", d) }
|
||||
|
||||
@@ -26,49 +26,16 @@ type Session struct {
|
||||
HeaderPub, PayloadPub *pub.Key
|
||||
HeaderBytes, PayloadBytes pub.Bytes
|
||||
HeaderPrv, PayloadPrv *prv.Key
|
||||
Depth int8
|
||||
Deadline time.Time
|
||||
}
|
||||
|
||||
type Sessions []*Session
|
||||
|
||||
func (s Sessions) Len() int { return len(s) }
|
||||
|
||||
func (s Sessions) Add(se *Session) Sessions { return append(s, se) }
|
||||
|
||||
func (s Sessions) Delete(se *Session) Sessions {
|
||||
for i := range s {
|
||||
if s[i] == se {
|
||||
return append(s[:i], s[i:]...)
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s Sessions) Find(t nonce.ID) (se *Session) {
|
||||
for i := range s {
|
||||
if s[i].ID == t {
|
||||
se = s[i]
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s Sessions) FindPub(pubKey *pub.Key) (se *Session) {
|
||||
for i := range s {
|
||||
if s[i].HeaderPub.Equals(pubKey) {
|
||||
se = s[i]
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// NewSession creates a new Session.
|
||||
// New creates a new Session.
|
||||
//
|
||||
// Purchasing a session the seller returns a token, based on a requested data
|
||||
// allocation.
|
||||
func NewSession(id nonce.ID, rem uint64, deadline time.Duration) (s *Session) {
|
||||
func New(id nonce.ID, node *node.Node, rem uint64, deadline time.Duration,
|
||||
depth int8) (s *Session) {
|
||||
|
||||
var e error
|
||||
var hdrPrv, pldPrv *prv.Key
|
||||
@@ -83,6 +50,7 @@ func NewSession(id nonce.ID, rem uint64, deadline time.Duration) (s *Session) {
|
||||
|
||||
s = &Session{
|
||||
ID: id,
|
||||
Node: node,
|
||||
Remaining: rem,
|
||||
HeaderPub: hdrPub,
|
||||
HeaderBytes: hdrPub.ToBytes(),
|
||||
@@ -91,6 +59,7 @@ func NewSession(id nonce.ID, rem uint64, deadline time.Duration) (s *Session) {
|
||||
HeaderPrv: hdrPrv,
|
||||
PayloadPrv: pldPrv,
|
||||
Deadline: time.Now().Add(deadline),
|
||||
Depth: depth,
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -109,3 +78,45 @@ func (s *Session) SubtractBytes(b uint64) bool {
|
||||
s.Remaining -= b
|
||||
return true
|
||||
}
|
||||
|
||||
type Sessions []*Session
|
||||
|
||||
func (s Sessions) Len() int { return len(s) }
|
||||
|
||||
func (s Sessions) Add(se *Session) Sessions { return append(s, se) }
|
||||
|
||||
func (s Sessions) Delete(se *Session) Sessions {
|
||||
for i := range s {
|
||||
if s[i] == se {
|
||||
return append(s[:i], s[i+1:]...)
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s Sessions) DeleteByID(id nonce.ID) Sessions {
|
||||
for i := range s {
|
||||
if s[i].ID == id {
|
||||
return append(s[:i], s[i+1:]...)
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s Sessions) Find(t nonce.ID) (se *Session) {
|
||||
for i := range s {
|
||||
if s[i].ID == t {
|
||||
return s[i]
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s Sessions) FindPub(pubKey *pub.Key) (se *Session) {
|
||||
for i := range s {
|
||||
if s[i].HeaderPub.Equals(pubKey) {
|
||||
return s[i]
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -59,12 +59,14 @@ func Cat(chunks ...[]byte) (pkt []byte) {
|
||||
return
|
||||
}
|
||||
|
||||
var put64 = binary.LittleEndian.PutUint64
|
||||
var get64 = binary.LittleEndian.Uint64
|
||||
var put32 = binary.LittleEndian.PutUint32
|
||||
var get32 = binary.LittleEndian.Uint32
|
||||
var put16 = binary.LittleEndian.PutUint16
|
||||
var get16 = binary.LittleEndian.Uint16
|
||||
var (
|
||||
put64 = binary.LittleEndian.PutUint64
|
||||
get64 = binary.LittleEndian.Uint64
|
||||
put32 = binary.LittleEndian.PutUint32
|
||||
get32 = binary.LittleEndian.Uint32
|
||||
put16 = binary.LittleEndian.PutUint16
|
||||
get16 = binary.LittleEndian.Uint16
|
||||
)
|
||||
|
||||
// DecodeUint64 returns an int containing the little endian encoded 64-bit value
|
||||
// stored in a 4 byte long slice
|
||||
@@ -102,10 +104,12 @@ func DecodeUint16(b []byte) int { return int(get16(b)) }
|
||||
// EncodeUint16 puts an int into a uint32 and then into 2 byte long slice.
|
||||
func EncodeUint16(b []byte, n int) { put16(b, uint16(n)) }
|
||||
|
||||
const Uint64Len = 8
|
||||
const Uint32Len = 4
|
||||
const Uint24Len = 3
|
||||
const Uint16Len = 2
|
||||
const (
|
||||
Uint64Len = 8
|
||||
Uint32Len = 4
|
||||
Uint24Len = 3
|
||||
Uint16Len = 2
|
||||
)
|
||||
|
||||
func NewUint64() Bytes { return make(Bytes, Uint64Len) }
|
||||
func NewUint32() Bytes { return make(Bytes, Uint32Len) }
|
||||
|
||||
@@ -15,11 +15,8 @@ type Sim chan slice.Bytes
|
||||
|
||||
func NewSim(bufs int) Sim { return make(Sim, bufs) }
|
||||
func (d Sim) Send(b slice.Bytes) {
|
||||
// log.I.S("sending", b.ToBytes())
|
||||
// log.I.S(runtime.Caller(1))
|
||||
d <- b
|
||||
}
|
||||
func (d Sim) Receive() <-chan slice.Bytes {
|
||||
// log.I.Ln("receiving")
|
||||
return d
|
||||
}
|
||||
|
||||
@@ -22,8 +22,9 @@ var (
|
||||
_ types.Onion = &OnionSkin{}
|
||||
)
|
||||
|
||||
// OnionSkin cipher delivers a pair of private keys to be used in association with a
|
||||
// reply.Type specifically in the situation of a node bootstrapping sessions.
|
||||
// OnionSkin cipher delivers a pair of private keys to be used in association
|
||||
// with a reply.Type specifically in the situation of a node bootstrapping
|
||||
// sessions.
|
||||
//
|
||||
// After ~10 seconds these can be purged from the cache as they are otherwise a
|
||||
// DoS vector buffer flooding.
|
||||
@@ -35,7 +36,6 @@ type OnionSkin struct {
|
||||
func (x *OnionSkin) Inner() types.Onion { return x.Onion }
|
||||
func (x *OnionSkin) Insert(o types.Onion) { x.Onion = o }
|
||||
func (x *OnionSkin) Len() int { return Len + x.Onion.Len() }
|
||||
|
||||
func (x *OnionSkin) Encode(b slice.Bytes, c *slice.Cursor) {
|
||||
copy(b[*c:c.Inc(magicbytes.Len)], Magic)
|
||||
hdr := x.Header.ToBytes()
|
||||
@@ -44,8 +44,6 @@ func (x *OnionSkin) Encode(b slice.Bytes, c *slice.Cursor) {
|
||||
copy(b[*c:c.Inc(pub.KeyLen)], pld[:])
|
||||
x.Onion.Encode(b, c)
|
||||
}
|
||||
|
||||
// Decode unwraps a cipher.OnionSkin message.
|
||||
func (x *OnionSkin) Decode(b slice.Bytes, c *slice.Cursor) (e error) {
|
||||
if len(b[*c:]) < Len-magicbytes.Len {
|
||||
return magicbytes.TooShort(len(b[*c:]),
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
"github.com/indra-labs/indra/pkg/wire/forward"
|
||||
"github.com/indra-labs/indra/pkg/wire/layer"
|
||||
"github.com/indra-labs/indra/pkg/wire/magicbytes"
|
||||
"github.com/indra-labs/indra/pkg/wire/purchase"
|
||||
"github.com/indra-labs/indra/pkg/wire/response"
|
||||
"github.com/indra-labs/indra/pkg/wire/reverse"
|
||||
"github.com/indra-labs/indra/pkg/wire/session"
|
||||
@@ -72,12 +71,6 @@ func PeelOnion(b slice.Bytes, c *slice.Cursor) (on types.Onion, e error) {
|
||||
return
|
||||
}
|
||||
on = &o
|
||||
case purchase.MagicString:
|
||||
o := &purchase.OnionSkin{}
|
||||
if e = o.Decode(b, c); check(e) {
|
||||
return
|
||||
}
|
||||
on = o
|
||||
case reverse.MagicString:
|
||||
o := &reverse.OnionSkin{}
|
||||
if e = o.Decode(b, c); check(e) {
|
||||
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
"github.com/indra-labs/indra/pkg/wire/exit"
|
||||
"github.com/indra-labs/indra/pkg/wire/forward"
|
||||
"github.com/indra-labs/indra/pkg/wire/layer"
|
||||
"github.com/indra-labs/indra/pkg/wire/purchase"
|
||||
"github.com/indra-labs/indra/pkg/wire/response"
|
||||
"github.com/indra-labs/indra/pkg/wire/reverse"
|
||||
"github.com/indra-labs/indra/pkg/wire/session"
|
||||
@@ -243,46 +242,6 @@ func TestOnionSkins_Layer(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestOnionSkins_Purchase(t *testing.T) {
|
||||
|
||||
var e error
|
||||
prvs, pubs := GetCipherSet(t)
|
||||
ciphers := GenCiphers(prvs, pubs)
|
||||
p := rand.Uint64()
|
||||
n3 := Gen3Nonces()
|
||||
n := nonce.NewID()
|
||||
on := OnionSkins{}.
|
||||
Purchase(n, p, prvs, pubs, n3).
|
||||
Assemble()
|
||||
onb := EncodeOnion(on)
|
||||
c := slice.NewCursor()
|
||||
var onex types.Onion
|
||||
if onex, e = PeelOnion(onb, c); check(e) {
|
||||
t.FailNow()
|
||||
}
|
||||
pr := &purchase.OnionSkin{}
|
||||
var ok bool
|
||||
if pr, ok = onex.(*purchase.OnionSkin); !ok {
|
||||
t.Error("did not unwrap expected type")
|
||||
t.FailNow()
|
||||
}
|
||||
if pr.NBytes != p {
|
||||
t.Error("NBytes did not unwrap correctly")
|
||||
t.FailNow()
|
||||
}
|
||||
if pr.ID != n {
|
||||
t.Errorf("id %v did not unwrap correctly, expected %v",
|
||||
pr.ID, n)
|
||||
t.FailNow()
|
||||
}
|
||||
for i := range pr.Ciphers {
|
||||
if pr.Ciphers[i] != ciphers[i] {
|
||||
t.Errorf("cipher %d did not unwrap correctly", i)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestOnionSkins_Reply(t *testing.T) {
|
||||
|
||||
var e error
|
||||
|
||||
@@ -47,13 +47,11 @@ func (x *OnionSkin) String() string {
|
||||
func (x *OnionSkin) Inner() types.Onion { return nil }
|
||||
func (x *OnionSkin) Insert(o types.Onion) {}
|
||||
func (x *OnionSkin) Len() int { return Len }
|
||||
|
||||
func (x *OnionSkin) Encode(b slice.Bytes, c *slice.Cursor) {
|
||||
copy(b[*c:c.Inc(magicbytes.Len)], Magic)
|
||||
// Copy in the ID.
|
||||
copy(b[*c:c.Inc(nonce.IDLen)], x.ID[:])
|
||||
}
|
||||
|
||||
func (x *OnionSkin) Decode(b slice.Bytes, c *slice.Cursor) (e error) {
|
||||
if len(b[*c:]) < Len-magicbytes.Len {
|
||||
return magicbytes.TooShort(len(b[*c:]),
|
||||
|
||||
@@ -13,7 +13,7 @@ type Callback struct {
|
||||
nonce.ID
|
||||
time.Time
|
||||
Onion *OnionSkin
|
||||
Hook Hook
|
||||
Hook
|
||||
}
|
||||
|
||||
type Confirms struct {
|
||||
@@ -21,12 +21,7 @@ type Confirms struct {
|
||||
Cnf []Callback
|
||||
}
|
||||
|
||||
func NewConfirms() *Confirms {
|
||||
cn := Confirms{
|
||||
Cnf: make([]Callback, 0),
|
||||
}
|
||||
return &cn
|
||||
}
|
||||
func NewConfirms() *Confirms { return &Confirms{Cnf: make([]Callback, 0)} }
|
||||
|
||||
func (cn *Confirms) Add(cb *Callback) {
|
||||
cn.Lock()
|
||||
|
||||
@@ -38,7 +38,6 @@ func (x *OnionSkin) String() string {
|
||||
func (x *OnionSkin) Inner() types.Onion { return x.Onion }
|
||||
func (x *OnionSkin) Insert(o types.Onion) { x.Onion = o }
|
||||
func (x *OnionSkin) Len() int { return Len + x.Onion.Len() }
|
||||
|
||||
func (x *OnionSkin) Encode(b slice.Bytes, c *slice.Cursor) {
|
||||
copy(b[*c:c.Inc(magicbytes.Len)], Magic)
|
||||
var ap []byte
|
||||
@@ -50,7 +49,6 @@ func (x *OnionSkin) Encode(b slice.Bytes, c *slice.Cursor) {
|
||||
copy(b[c.Inc(1):c.Inc(Len-magicbytes.Len-1)], ap)
|
||||
x.Onion.Encode(b, c)
|
||||
}
|
||||
|
||||
func (x *OnionSkin) Decode(b slice.Bytes, c *slice.Cursor) (e error) {
|
||||
if len(b[*c:]) < Len-magicbytes.Len {
|
||||
return magicbytes.TooShort(len(b[*c:]), Len-magicbytes.Len, string(Magic))
|
||||
|
||||
@@ -31,7 +31,7 @@ var (
|
||||
// OnionSkin message is the generic top level wrapper for an OnionSkin. All
|
||||
// following messages are wrapped inside this. This type provides the encryption
|
||||
// for each layer, and a header which a relay uses to determine what cipher to
|
||||
// use.
|
||||
// use. Everything in a message after this message is encrypted as specified.
|
||||
type OnionSkin struct {
|
||||
To *pub.Key
|
||||
From *prv.Key
|
||||
@@ -58,7 +58,6 @@ func (x *OnionSkin) Insert(o types.Onion) { x.Onion = o }
|
||||
func (x *OnionSkin) Len() int {
|
||||
return Len + x.Onion.Len()
|
||||
}
|
||||
|
||||
func (x *OnionSkin) Encode(b slice.Bytes, c *slice.Cursor) {
|
||||
copy(b[*c:c.Inc(magicbytes.Len)], Magic)
|
||||
copy(b[*c:c.Inc(nonce.IVLen)], x.Nonce[:])
|
||||
@@ -79,9 +78,6 @@ func (x *OnionSkin) Encode(b slice.Bytes, c *slice.Cursor) {
|
||||
}
|
||||
ciph.Encipher(blk, x.Nonce, b[start:])
|
||||
}
|
||||
|
||||
// Decode decodes a received OnionSkin. The entire remainder of the message is
|
||||
// encrypted by this layer.
|
||||
func (x *OnionSkin) Decode(b slice.Bytes, c *slice.Cursor) (e error) {
|
||||
if len(b[*c:]) < Len-magicbytes.Len {
|
||||
return magicbytes.TooShort(len(b[*c:]), Len-magicbytes.Len, "message")
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"net/netip"
|
||||
"time"
|
||||
|
||||
"github.com/indra-labs/indra/pkg/key/ecdh"
|
||||
"github.com/indra-labs/indra/pkg/key/prv"
|
||||
"github.com/indra-labs/indra/pkg/key/pub"
|
||||
"github.com/indra-labs/indra/pkg/nonce"
|
||||
@@ -18,39 +17,22 @@ import (
|
||||
"github.com/indra-labs/indra/pkg/wire/forward"
|
||||
"github.com/indra-labs/indra/pkg/wire/layer"
|
||||
"github.com/indra-labs/indra/pkg/wire/noop"
|
||||
"github.com/indra-labs/indra/pkg/wire/purchase"
|
||||
"github.com/indra-labs/indra/pkg/wire/response"
|
||||
"github.com/indra-labs/indra/pkg/wire/reverse"
|
||||
"github.com/indra-labs/indra/pkg/wire/session"
|
||||
"github.com/indra-labs/indra/pkg/wire/token"
|
||||
)
|
||||
|
||||
func GenCiphers(prvs [3]*prv.Key, pubs [3]*pub.Key) (ciphers [3]sha256.Hash) {
|
||||
for i := range prvs {
|
||||
ciphers[2-i] = ecdh.Compute(prvs[i], pubs[i])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Gen3Nonces() (n [3]nonce.IV) {
|
||||
for i := range n {
|
||||
n[i] = nonce.New()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GenPingNonces() (n [4]nonce.IV) {
|
||||
for i := range n {
|
||||
n[i] = nonce.New()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type OnionSkins []types.Onion
|
||||
|
||||
var os = &noop.OnionSkin{}
|
||||
|
||||
func (o OnionSkins) Cipher(hdr, pld *prv.Key) OnionSkins {
|
||||
// SendKeys can apply to from 1 to 5 nodes, if either key is nil then
|
||||
// this layer just doesn't get added in the serialization process.
|
||||
if hdr == nil || pld == nil {
|
||||
return o
|
||||
}
|
||||
return append(o, &cipher.OnionSkin{
|
||||
Header: hdr,
|
||||
Payload: pld,
|
||||
@@ -92,26 +74,16 @@ func (o OnionSkins) OnionSkin(to *pub.Key, from *prv.Key,
|
||||
Onion: os,
|
||||
})
|
||||
}
|
||||
func (o OnionSkins) Purchase(id nonce.ID, nBytes uint64, prvs [3]*prv.Key,
|
||||
pubs [3]*pub.Key, n [3]nonce.IV) OnionSkins {
|
||||
|
||||
oo := append(o, &purchase.OnionSkin{
|
||||
ID: id,
|
||||
NBytes: nBytes,
|
||||
Ciphers: GenCiphers(prvs, pubs),
|
||||
Nonces: n,
|
||||
Onion: os,
|
||||
})
|
||||
|
||||
return oo
|
||||
}
|
||||
func (o OnionSkins) Reverse(ip *netip.AddrPort) OnionSkins {
|
||||
return append(o, &reverse.OnionSkin{AddrPort: ip, Onion: os})
|
||||
}
|
||||
|
||||
func (o OnionSkins) Response(hash sha256.Hash, res slice.Bytes) OnionSkins {
|
||||
rs := response.OnionSkin{Hash: hash, Bytes: res}
|
||||
return append(o, &rs)
|
||||
}
|
||||
|
||||
func (o OnionSkins) Session(hdr, pld *pub.Key) OnionSkins {
|
||||
return append(o, &session.OnionSkin{
|
||||
HeaderKey: hdr,
|
||||
@@ -119,6 +91,7 @@ func (o OnionSkins) Session(hdr, pld *pub.Key) OnionSkins {
|
||||
Onion: os,
|
||||
})
|
||||
}
|
||||
|
||||
func (o OnionSkins) Token(tok sha256.Hash) OnionSkins {
|
||||
return append(o, (*token.OnionSkin)(&tok))
|
||||
}
|
||||
|
||||
@@ -11,10 +11,10 @@ type OnionSkin struct {
|
||||
func (x *OnionSkin) Inner() types.Onion { return nil }
|
||||
func (x *OnionSkin) Insert(o types.Onion) {}
|
||||
func (x *OnionSkin) Len() int { return 0 }
|
||||
|
||||
func (x *OnionSkin) Encode(b slice.Bytes, c *slice.Cursor) {
|
||||
func (x *OnionSkin) Encode(b slice.Bytes,
|
||||
c *slice.Cursor) {
|
||||
}
|
||||
|
||||
func (x *OnionSkin) Decode(b slice.Bytes, c *slice.Cursor) (e error) {
|
||||
func (x *OnionSkin) Decode(b slice.Bytes,
|
||||
c *slice.Cursor) (e error) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -15,24 +15,30 @@ import (
|
||||
// last hop as the key to narrow the number of elements to search through to
|
||||
// find the matching cipher and reveal the contained ID inside it.
|
||||
//
|
||||
// The pending ping records keep the identifiers of the three nodes that were in
|
||||
// The pending ping records keep the identifiers of the 5 nodes that were in
|
||||
// a ping onion and when the Confirmation is correctly received these nodes get
|
||||
// an increment of their liveness score. By using this scheme, when nodes are
|
||||
// offline their scores will fall to zero after a time whereas live nodes will
|
||||
// have steadily increasing scores from successful pings.
|
||||
func Ping(id nonce.ID, client *node.Node, hop [3]*node.Node,
|
||||
set *signer.KeySet) OnionSkins {
|
||||
|
||||
func Ping(id nonce.ID, s session.Sessions, ks *signer.KeySet) OnionSkins {
|
||||
if len(s) != 6 {
|
||||
log.E.F("Ping requires 6 sessions, received %d", len(s))
|
||||
return nil
|
||||
}
|
||||
n := GenPingNonces()
|
||||
return OnionSkins{}.
|
||||
Forward(hop[0].AddrPort).
|
||||
OnionSkin(hop[0].HeaderPub, set.Next(), n[0]).
|
||||
Forward(hop[1].AddrPort).
|
||||
OnionSkin(hop[1].HeaderPub, set.Next(), n[1]).
|
||||
Forward(hop[2].AddrPort).
|
||||
OnionSkin(hop[2].HeaderPub, set.Next(), n[2]).
|
||||
Forward(client.AddrPort).
|
||||
OnionSkin(client.HeaderPub, set.Next(), n[3]).
|
||||
Forward(s[0].AddrPort).
|
||||
OnionSkin(s[0].HeaderPub, ks.Next(), n[0]).
|
||||
Forward(s[1].AddrPort).
|
||||
OnionSkin(s[1].HeaderPub, ks.Next(), n[1]).
|
||||
Forward(s[2].AddrPort).
|
||||
OnionSkin(s[2].HeaderPub, ks.Next(), n[2]).
|
||||
Forward(s[3].AddrPort).
|
||||
OnionSkin(s[3].HeaderPub, ks.Next(), n[3]).
|
||||
Forward(s[4].AddrPort).
|
||||
OnionSkin(s[4].HeaderPub, ks.Next(), n[3]).
|
||||
Forward(s[5].AddrPort).
|
||||
OnionSkin(s[5].HeaderPub, ks.Next(), n[3]).
|
||||
Confirmation(id)
|
||||
}
|
||||
|
||||
@@ -47,73 +53,32 @@ func Ping(id nonce.ID, client *node.Node, hop [3]*node.Node,
|
||||
// the Reverse relay.
|
||||
//
|
||||
// This message's last layer is a Confirmation, which allows the client to know
|
||||
// that the key was successfully delivered to the Reverse relays that will be
|
||||
// used in the Purchase.
|
||||
func SendKeys(id nonce.ID, hdr, pld *prv.Key,
|
||||
client *node.Node, hop [5]*node.Node, set *signer.KeySet) OnionSkins {
|
||||
// that the keys were successfully delivered.
|
||||
func SendKeys(id nonce.ID, hdr, pld []*prv.Key,
|
||||
client *node.Node, hop []*node.Node, set *signer.KeySet) OnionSkins {
|
||||
|
||||
n0 := Gen3Nonces()
|
||||
n1 := Gen3Nonces()
|
||||
n := GenNonces(6)
|
||||
return OnionSkins{}.
|
||||
Forward(hop[0].AddrPort).
|
||||
OnionSkin(hop[0].HeaderPub, set.Next(), n0[0]).
|
||||
OnionSkin(hop[0].IdentityPub, set.Next(), n[0]).
|
||||
Cipher(hdr[0], pld[0]).
|
||||
Forward(hop[1].AddrPort).
|
||||
OnionSkin(hop[1].HeaderPub, set.Next(), n0[1]).
|
||||
OnionSkin(hop[1].IdentityPub, set.Next(), n[1]).
|
||||
Cipher(hdr[1], pld[1]).
|
||||
Forward(hop[2].AddrPort).
|
||||
OnionSkin(hop[2].HeaderPub, set.Next(), n0[2]).
|
||||
Cipher(hdr, pld).
|
||||
OnionSkin(hop[2].IdentityPub, set.Next(), n[2]).
|
||||
Cipher(hdr[2], pld[2]).
|
||||
Forward(hop[3].AddrPort).
|
||||
OnionSkin(hop[3].HeaderPub, set.Next(), n1[0]).
|
||||
OnionSkin(hop[3].IdentityPub, set.Next(), n[3]).
|
||||
Cipher(hdr[3], pld[3]).
|
||||
Forward(hop[4].AddrPort).
|
||||
OnionSkin(hop[4].HeaderPub, set.Next(), n1[1]).
|
||||
OnionSkin(hop[4].IdentityPub, set.Next(), n[4]).
|
||||
Cipher(hdr[4], pld[4]).
|
||||
Forward(client.AddrPort).
|
||||
OnionSkin(client.HeaderPub, set.Next(), n1[2]).
|
||||
OnionSkin(client.IdentityPub, set.Next(), n[5]).
|
||||
Confirmation(id)
|
||||
}
|
||||
|
||||
// SendPurchase delivers a request for keys for a relaying session with a given
|
||||
// router (in this case, hop 2). It is almost identical to an Exit except the
|
||||
// payload is always just a 64-bit unsigned integer.
|
||||
//
|
||||
// The response, which will be two public keys that identify the session and
|
||||
// form the basis of the cloaked "To" keys, 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 layer header to the top and pads the
|
||||
// remainder with noise, so it always looks like the first hop,
|
||||
// indistinguishable.
|
||||
func SendPurchase(n nonce.ID, nBytes uint64, client *node.Node,
|
||||
hop [5]*node.Node, sess [3]*session.Session,
|
||||
set *signer.KeySet) OnionSkins {
|
||||
|
||||
var prvs [3]*prv.Key
|
||||
for i := range prvs {
|
||||
prvs[i] = set.Next()
|
||||
}
|
||||
n0, n1 := Gen3Nonces(), Gen3Nonces()
|
||||
var pubs [3]*pub.Key
|
||||
pubs[0] = sess[0].PayloadPub
|
||||
pubs[1] = sess[1].PayloadPub
|
||||
pubs[2] = sess[2].PayloadPub
|
||||
return OnionSkins{}.
|
||||
Forward(hop[0].AddrPort).
|
||||
OnionSkin(hop[0].HeaderPub, set.Next(), n0[0]).
|
||||
Forward(hop[1].AddrPort).
|
||||
OnionSkin(hop[1].HeaderPub, set.Next(), n0[1]).
|
||||
Forward(hop[2].AddrPort).
|
||||
OnionSkin(hop[2].HeaderPub, set.Next(), n0[2]).
|
||||
Purchase(n, nBytes, prvs, pubs, n1).
|
||||
Reverse(hop[3].AddrPort).
|
||||
OnionSkin(sess[0].HeaderPub, prvs[0], n1[0]).
|
||||
Reverse(hop[4].AddrPort).
|
||||
OnionSkin(sess[1].HeaderPub, prvs[1], n1[1]).
|
||||
Reverse(client.AddrPort).
|
||||
OnionSkin(sess[2].HeaderPub, prvs[2], n1[2])
|
||||
}
|
||||
|
||||
// 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.
|
||||
@@ -145,11 +110,11 @@ func SendExit(payload slice.Bytes, port uint16, client *node.Node,
|
||||
pubs[2] = sess[2].PayloadPub
|
||||
return OnionSkins{}.
|
||||
Forward(hop[0].AddrPort).
|
||||
OnionSkin(hop[0].HeaderPub, set.Next(), n0[0]).
|
||||
OnionSkin(hop[0].IdentityPub, set.Next(), n0[0]).
|
||||
Forward(hop[1].AddrPort).
|
||||
OnionSkin(hop[1].HeaderPub, set.Next(), n0[1]).
|
||||
OnionSkin(hop[1].IdentityPub, set.Next(), n0[1]).
|
||||
Forward(hop[2].AddrPort).
|
||||
OnionSkin(hop[2].HeaderPub, set.Next(), n0[2]).
|
||||
OnionSkin(hop[2].IdentityPub, set.Next(), n0[2]).
|
||||
Exit(port, prvs, pubs, n1, payload).
|
||||
Reverse(hop[3].AddrPort).
|
||||
OnionSkin(sess[0].HeaderPub, prvs[0], n1[0]).
|
||||
|
||||
@@ -1,532 +0,0 @@
|
||||
package wire
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/indra-labs/indra/pkg/key/pub"
|
||||
"github.com/indra-labs/indra/pkg/key/signer"
|
||||
log2 "github.com/indra-labs/indra/pkg/log"
|
||||
"github.com/indra-labs/indra/pkg/node"
|
||||
"github.com/indra-labs/indra/pkg/nonce"
|
||||
"github.com/indra-labs/indra/pkg/session"
|
||||
"github.com/indra-labs/indra/pkg/sha256"
|
||||
"github.com/indra-labs/indra/pkg/slice"
|
||||
"github.com/indra-labs/indra/pkg/testutils"
|
||||
"github.com/indra-labs/indra/pkg/types"
|
||||
"github.com/indra-labs/indra/pkg/wire/cipher"
|
||||
"github.com/indra-labs/indra/pkg/wire/confirm"
|
||||
"github.com/indra-labs/indra/pkg/wire/exit"
|
||||
"github.com/indra-labs/indra/pkg/wire/forward"
|
||||
"github.com/indra-labs/indra/pkg/wire/layer"
|
||||
"github.com/indra-labs/indra/pkg/wire/purchase"
|
||||
"github.com/indra-labs/indra/pkg/wire/reverse"
|
||||
)
|
||||
|
||||
func PeelForward(t *testing.T, b slice.Bytes,
|
||||
c *slice.Cursor) (fwd *forward.OnionSkin) {
|
||||
|
||||
var ok bool
|
||||
var on types.Onion
|
||||
var e error
|
||||
if on, e = PeelOnion(b, c); check(e) {
|
||||
t.Error(e)
|
||||
}
|
||||
if fwd, ok = on.(*forward.OnionSkin); !ok {
|
||||
t.Error("did not unwrap expected type", reflect.TypeOf(fwd))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func PeelOnionSkin(t *testing.T, b slice.Bytes,
|
||||
c *slice.Cursor) (l *layer.OnionSkin) {
|
||||
|
||||
var ok bool
|
||||
var on types.Onion
|
||||
var e error
|
||||
if on, e = PeelOnion(b, c); check(e) {
|
||||
t.Error(e)
|
||||
}
|
||||
if l, ok = on.(*layer.OnionSkin); !ok {
|
||||
t.Error("did not unwrap expected type", reflect.TypeOf(l))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func PeelConfirmation(t *testing.T, b slice.Bytes,
|
||||
c *slice.Cursor) (cn *confirm.OnionSkin) {
|
||||
|
||||
var ok bool
|
||||
var e error
|
||||
var on types.Onion
|
||||
if on, e = PeelOnion(b, c); check(e) {
|
||||
t.Error(e)
|
||||
}
|
||||
if cn, ok = on.(*confirm.OnionSkin); !ok {
|
||||
t.Error("did not unwrap expected type", reflect.TypeOf(on))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func PeelPurchase(t *testing.T, b slice.Bytes,
|
||||
c *slice.Cursor) (pr *purchase.OnionSkin) {
|
||||
|
||||
var ok bool
|
||||
var e error
|
||||
var on types.Onion
|
||||
if on, e = PeelOnion(b, c); check(e) {
|
||||
t.Error(e)
|
||||
}
|
||||
if pr, ok = on.(*purchase.OnionSkin); !ok {
|
||||
t.Error("did not unwrap expected type", reflect.TypeOf(on))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func PeelReverse(t *testing.T, b slice.Bytes,
|
||||
c *slice.Cursor) (rp *reverse.OnionSkin) {
|
||||
|
||||
var ok bool
|
||||
var e error
|
||||
var on types.Onion
|
||||
if on, e = PeelOnion(b, c); check(e) {
|
||||
t.Error(e)
|
||||
}
|
||||
if rp, ok = on.(*reverse.OnionSkin); !ok {
|
||||
t.Error("did not unwrap expected type", reflect.TypeOf(on))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func PeelExit(t *testing.T, b slice.Bytes,
|
||||
c *slice.Cursor) (ex *exit.OnionSkin) {
|
||||
|
||||
var ok bool
|
||||
var e error
|
||||
var on types.Onion
|
||||
if on, e = PeelOnion(b, c); check(e) {
|
||||
t.Error(e)
|
||||
t.FailNow()
|
||||
}
|
||||
if ex, ok = on.(*exit.OnionSkin); !ok {
|
||||
t.Error("did not unwrap expected type", reflect.TypeOf(on))
|
||||
t.FailNow()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func TestPing(t *testing.T) {
|
||||
|
||||
_, ks, e := signer.New()
|
||||
if check(e) {
|
||||
t.Error(e)
|
||||
t.FailNow()
|
||||
}
|
||||
var hop [3]*node.Node
|
||||
for i := range hop {
|
||||
prv1, _ := GetTwoPrvKeys(t)
|
||||
pub1 := pub.Derive(prv1)
|
||||
var n nonce.ID
|
||||
hop[i], n = node.New(slice.GenerateRandomAddrPortIPv4(),
|
||||
pub1, prv1, nil)
|
||||
_ = n
|
||||
}
|
||||
cprv1, _ := GetTwoPrvKeys(t)
|
||||
cpub1 := pub.Derive(cprv1)
|
||||
var n nonce.ID
|
||||
var client *node.Node
|
||||
client, n = node.New(slice.GenerateRandomAddrPortIPv4(),
|
||||
cpub1, cprv1, nil)
|
||||
|
||||
on := Ping(n, client, hop, ks)
|
||||
b := EncodeOnion(on.Assemble())
|
||||
c := slice.NewCursor()
|
||||
|
||||
// Forward(hop[0].AddrPort).
|
||||
f0 := PeelForward(t, b, c)
|
||||
if hop[0].AddrPort.String() != f0.AddrPort.String() {
|
||||
t.Errorf("failed to unwrap; expected: '%s', got: '%s'",
|
||||
hop[0].AddrPort.String(), f0.AddrPort.String())
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// OnionSkin(address.FromPubKey(hop[0].HeaderPub), set.Next()).
|
||||
PeelOnionSkin(t, b, c).Decrypt(hop[0].HeaderPrv, b, c)
|
||||
|
||||
// Forward(hop[1].AddrPort).
|
||||
f1 := PeelForward(t, b, c)
|
||||
if hop[1].AddrPort.String() != f1.AddrPort.String() {
|
||||
t.Errorf("failed to unwrap; expected: '%s', got: '%s'",
|
||||
hop[1].AddrPort.String(), f1.AddrPort.String())
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// OnionSkin(address.FromPubKey(hop[1].HeaderPub), set.Next()).
|
||||
PeelOnionSkin(t, b, c).Decrypt(hop[1].HeaderPrv, b, c)
|
||||
|
||||
// Forward(hop[2].AddrPort).
|
||||
f2 := PeelForward(t, b, c)
|
||||
if hop[2].AddrPort.String() != f2.AddrPort.String() {
|
||||
t.Errorf("failed to unwrap; expected: '%s', got: '%s'",
|
||||
hop[2].AddrPort.String(), f2.AddrPort.String())
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// OnionSkin(address.FromPubKey(hop[2].HeaderPub), set.Next()).
|
||||
PeelOnionSkin(t, b, c).Decrypt(hop[2].HeaderPrv, b, c)
|
||||
|
||||
// Forward(client.AddrPort).
|
||||
f3 := PeelForward(t, b, c)
|
||||
if client.AddrPort.String() != f3.AddrPort.String() {
|
||||
t.Errorf("failed to unwrap; expected: '%s', got: '%s'",
|
||||
client.AddrPort.String(), f3.AddrPort.String())
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// OnionSkin(address.FromPubKey(client.HeaderPub), set.Next()).
|
||||
PeelOnionSkin(t, b, c).Decrypt(client.HeaderPrv, b, c)
|
||||
|
||||
// Confirmation(id).
|
||||
co := PeelConfirmation(t, b, c)
|
||||
if co.ID != n {
|
||||
t.Error("did not unwrap expected confirmation nonce")
|
||||
t.FailNow()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendKeys(t *testing.T) {
|
||||
|
||||
_, ks, e := signer.New()
|
||||
if check(e) {
|
||||
t.Error(e)
|
||||
t.FailNow()
|
||||
}
|
||||
var hop [5]*node.Node
|
||||
for i := range hop {
|
||||
prv1, _ := GetTwoPrvKeys(t)
|
||||
pub1 := pub.Derive(prv1)
|
||||
hop[i], _ = node.New(slice.GenerateRandomAddrPortIPv4(),
|
||||
pub1, prv1, nil)
|
||||
}
|
||||
cprv1, _ := GetTwoPrvKeys(t)
|
||||
cpub1 := pub.Derive(cprv1)
|
||||
var n nonce.ID
|
||||
var client *node.Node
|
||||
client, n = node.New(slice.GenerateRandomAddrPortIPv4(),
|
||||
cpub1, cprv1, nil)
|
||||
ciprv1, ciprv2 := GetTwoPrvKeys(t)
|
||||
// cipub1, cipub2 := pub.Derive(ciprv1), pub.Derive(ciprv2)
|
||||
|
||||
on := SendKeys(n, ciprv1, ciprv2, client, hop, ks)
|
||||
b := EncodeOnion(on.Assemble())
|
||||
c := slice.NewCursor()
|
||||
var ok bool
|
||||
|
||||
// Forward(hop[0].AddrPort).
|
||||
f0 := PeelForward(t, b, c)
|
||||
if hop[0].AddrPort.String() != f0.AddrPort.String() {
|
||||
t.Errorf("failed to unwrap expected: '%s', got '%s'",
|
||||
hop[0].AddrPort.String(), f0.AddrPort.String())
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// OnionSkin(address.FromPubKey(hop[0].HeaderPub), set.Next()).
|
||||
PeelOnionSkin(t, b, c).Decrypt(hop[0].HeaderPrv, b, c)
|
||||
|
||||
// Forward(hop[1].AddrPort).
|
||||
f1 := PeelForward(t, b, c)
|
||||
if hop[1].AddrPort.String() != f1.AddrPort.String() {
|
||||
t.Errorf("failed to unwrap expected: '%s', got '%s'",
|
||||
hop[1].AddrPort.String(), f1.AddrPort.String())
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// OnionSkin(address.FromPubKey(hop[1].HeaderPub), set.Next()).
|
||||
PeelOnionSkin(t, b, c).Decrypt(hop[1].HeaderPrv, b, c)
|
||||
|
||||
// Forward(hop[2].AddrPort).
|
||||
f2 := PeelForward(t, b, c)
|
||||
if hop[2].AddrPort.String() != f2.AddrPort.String() {
|
||||
t.Errorf("failed to unwrap expected: '%s', got '%s'",
|
||||
hop[2].AddrPort.String(), f2.AddrPort.String())
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// OnionSkin(address.FromPubKey(hop[2].HeaderPub), set.Next()).
|
||||
PeelOnionSkin(t, b, c).Decrypt(hop[2].HeaderPrv, b, c)
|
||||
|
||||
// Cipher(hdr, pld).
|
||||
var onc types.Onion
|
||||
if onc, e = PeelOnion(b, c); check(e) {
|
||||
t.Error(e)
|
||||
t.FailNow()
|
||||
}
|
||||
var ci *cipher.OnionSkin
|
||||
if ci, ok = onc.(*cipher.OnionSkin); !ok {
|
||||
t.Error("did not unwrap expected type", reflect.TypeOf(onc))
|
||||
t.FailNow()
|
||||
}
|
||||
if !ci.Header.Key.Equals(&ciprv1.Key) {
|
||||
t.Error("did not unwrap header key")
|
||||
t.FailNow()
|
||||
}
|
||||
if !ci.Payload.Key.Equals(&ciprv2.Key) {
|
||||
t.Error("did not unwrap payload key")
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// Forward(hop[3].AddrPort).
|
||||
f3 := PeelForward(t, b, c)
|
||||
if hop[3].AddrPort.String() != f3.AddrPort.String() {
|
||||
t.Errorf("failed to unwrap expected: '%s', got '%s'",
|
||||
hop[3].AddrPort.String(), f3.AddrPort.String())
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// OnionSkin(address.FromPubKey(hop[3].HeaderPub), set.Next()).
|
||||
PeelOnionSkin(t, b, c).Decrypt(hop[3].HeaderPrv, b, c)
|
||||
|
||||
// Forward(hop[4].AddrPort).
|
||||
f4 := PeelForward(t, b, c)
|
||||
if hop[4].AddrPort.String() != f4.AddrPort.String() {
|
||||
t.Errorf("failed to unwrap expected: '%s', got '%s'",
|
||||
hop[3].AddrPort.String(), f4.AddrPort.String())
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// OnionSkin(address.FromPubKey(hop[4].HeaderPub), set.Next()).
|
||||
PeelOnionSkin(t, b, c).Decrypt(hop[4].HeaderPrv, b, c)
|
||||
|
||||
// Forward(client.AddrPort).
|
||||
f5 := PeelForward(t, b, c)
|
||||
if client.AddrPort.String() != f5.AddrPort.String() {
|
||||
t.Errorf("failed to unwrap expected: '%s', got '%s'",
|
||||
client.AddrPort.String(), f5.AddrPort.String())
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// OnionSkin(address.FromPubKey(client.HeaderPub), set.Next()).
|
||||
PeelOnionSkin(t, b, c).Decrypt(client.HeaderPrv, b, c)
|
||||
|
||||
// Confirmation(id).
|
||||
co := PeelConfirmation(t, b, c)
|
||||
if co.ID != n {
|
||||
t.Error("did not unwrap expected confirmation nonce")
|
||||
t.FailNow()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestSendPurchase(t *testing.T) {
|
||||
|
||||
log2.SetLogLevel(log2.Trace)
|
||||
_, ks, e := signer.New()
|
||||
if check(e) {
|
||||
t.Error(e)
|
||||
t.FailNow()
|
||||
}
|
||||
var hop [5]*node.Node
|
||||
for i := range hop {
|
||||
prv1, _ := GetTwoPrvKeys(t)
|
||||
pub1 := pub.Derive(prv1)
|
||||
hop[i], _ = node.New(slice.GenerateRandomAddrPortIPv4(),
|
||||
pub1, prv1, nil)
|
||||
}
|
||||
cprv1, _ := GetTwoPrvKeys(t)
|
||||
cpub1 := pub.Derive(cprv1)
|
||||
var client *node.Node
|
||||
client, _ = node.New(slice.GenerateRandomAddrPortIPv4(),
|
||||
cpub1, cprv1, nil)
|
||||
var sess [3]*session.Session
|
||||
for i := range sess {
|
||||
sess[i] = session.NewSession(nonce.NewID(), 203230230, time.Hour)
|
||||
}
|
||||
nBytes := rand.Uint64()
|
||||
n := nonce.NewID()
|
||||
on := SendPurchase(n, nBytes, client, hop, sess, ks)
|
||||
b := EncodeOnion(on.Assemble())
|
||||
c := slice.NewCursor()
|
||||
|
||||
// Forward(hop[0].AddrPort).
|
||||
f0 := PeelForward(t, b, c)
|
||||
if hop[0].AddrPort.String() != f0.AddrPort.String() {
|
||||
t.Errorf("failed to unwrap expected: '%s', got '%s'",
|
||||
hop[0].AddrPort.String(), f0.AddrPort.String())
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// OnionSkin(address.FromPubKey(hop[0].HeaderPub), set.Next()).
|
||||
PeelOnionSkin(t, b, c).Decrypt(hop[0].HeaderPrv, b, c)
|
||||
|
||||
// Forward(hop[1].AddrPort).
|
||||
f1 := PeelForward(t, b, c)
|
||||
if hop[1].AddrPort.String() != f1.AddrPort.String() {
|
||||
t.Errorf("failed to unwrap expected: '%s', got '%s'",
|
||||
hop[0].AddrPort.String(), f1.AddrPort.String())
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// OnionSkin(address.FromPubKey(hop[1].HeaderPub), set.Next()).
|
||||
PeelOnionSkin(t, b, c).Decrypt(hop[1].HeaderPrv, b, c)
|
||||
|
||||
// Forward(hop[2].AddrPort).
|
||||
f2 := PeelForward(t, b, c)
|
||||
if hop[2].AddrPort.String() != f2.AddrPort.String() {
|
||||
t.Errorf("failed to unwrap expected: '%s', got '%s'",
|
||||
hop[1].AddrPort.String(), f1.AddrPort.String())
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// OnionSkin(address.FromPubKey(hop[2].HeaderPub), set.Next()).
|
||||
PeelOnionSkin(t, b, c).Decrypt(hop[2].HeaderPrv, b, c)
|
||||
|
||||
// Purchase(nBytes, prvs, pubs).
|
||||
pr := PeelPurchase(t, b, c)
|
||||
if pr.NBytes != nBytes {
|
||||
t.Errorf("failed to retrieve original purchase nBytes")
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// Reverse(hop[3].AddrPort).
|
||||
rp1 := PeelReverse(t, b, c)
|
||||
if rp1.AddrPort.String() != hop[3].AddrPort.String() {
|
||||
t.Errorf("failed to retrieve first reply hop")
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// OnionSkin(address.FromPubKey(hop[3].HeaderPub), replies[0]).
|
||||
PeelOnionSkin(t, b, c).Decrypt(sess[0].HeaderPrv, b, c)
|
||||
|
||||
// Reverse(hop[4].AddrPort).
|
||||
rp2 := PeelReverse(t, b, c)
|
||||
if rp2.AddrPort.String() != hop[4].AddrPort.String() {
|
||||
t.Errorf("failed to retrieve second reply hop")
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// OnionSkin(address.FromPubKey(hop[4].HeaderPub), replies[1]).
|
||||
PeelOnionSkin(t, b, c).Decrypt(sess[1].HeaderPrv, b, c)
|
||||
|
||||
// Reverse(client.AddrPort).
|
||||
rp3 := PeelReverse(t, b, c)
|
||||
if rp3.AddrPort.String() != client.AddrPort.String() {
|
||||
t.Errorf("failed to retrieve third reply hop")
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// OnionSkin(address.FromPubKey(client.HeaderPub), replies[2]).
|
||||
PeelOnionSkin(t, b, c).Decrypt(sess[2].HeaderPrv, b, c)
|
||||
|
||||
}
|
||||
|
||||
func TestSendExit(t *testing.T) {
|
||||
|
||||
_, ks, e := signer.New()
|
||||
if check(e) {
|
||||
t.Error(e)
|
||||
t.FailNow()
|
||||
}
|
||||
var hop [5]*node.Node
|
||||
for i := range hop {
|
||||
prv1, _ := GetTwoPrvKeys(t)
|
||||
pub1 := pub.Derive(prv1)
|
||||
hop[i], _ = node.New(slice.GenerateRandomAddrPortIPv4(),
|
||||
pub1, prv1, nil)
|
||||
}
|
||||
cprv1, _ := GetTwoPrvKeys(t)
|
||||
cpub1 := pub.Derive(cprv1)
|
||||
var client *node.Node
|
||||
client, _ = node.New(slice.GenerateRandomAddrPortIPv4(),
|
||||
cpub1, cprv1, nil)
|
||||
port := uint16(rand.Uint32())
|
||||
var message slice.Bytes
|
||||
var hash sha256.Hash
|
||||
message, hash, e = testutils.GenerateTestMessage(2502)
|
||||
var sess [3]*session.Session
|
||||
for i := range sess {
|
||||
sess[i] = session.NewSession(nonce.NewID(), 203230230, time.Hour)
|
||||
}
|
||||
on := SendExit(message, port, client, hop, sess, ks)
|
||||
b := EncodeOnion(on.Assemble())
|
||||
c := slice.NewCursor()
|
||||
|
||||
// Forward(hop[0].AddrPort).
|
||||
f0 := PeelForward(t, b, c)
|
||||
if hop[0].AddrPort.String() != f0.AddrPort.String() {
|
||||
t.Errorf("failed to unwrap expected: '%s', got '%s'",
|
||||
hop[0].AddrPort.String(), f0.AddrPort.String())
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// OnionSkin(address.FromPubKey(hop[0].HeaderPub), set.Next()).
|
||||
PeelOnionSkin(t, b, c).Decrypt(hop[0].HeaderPrv, b, c)
|
||||
|
||||
// Forward(hop[1].AddrPort).
|
||||
f1 := PeelForward(t, b, c)
|
||||
if hop[1].AddrPort.String() != f1.AddrPort.String() {
|
||||
t.Errorf("failed to unwrap expected: '%s', got '%s'",
|
||||
hop[0].AddrPort.String(), f1.AddrPort.String())
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// OnionSkin(address.FromPubKey(hop[1].HeaderPub), set.Next()).
|
||||
PeelOnionSkin(t, b, c).Decrypt(hop[1].HeaderPrv, b, c)
|
||||
|
||||
// Forward(hop[2].AddrPort).
|
||||
f2 := PeelForward(t, b, c)
|
||||
if hop[2].AddrPort.String() != f2.AddrPort.String() {
|
||||
t.Errorf("failed to unwrap expected: '%s', got '%s'",
|
||||
hop[1].AddrPort.String(), f1.AddrPort.String())
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// OnionSkin(address.FromPubKey(hop[2].HeaderPub), set.Next()).
|
||||
PeelOnionSkin(t, b, c).Decrypt(hop[2].HeaderPrv, b, c)
|
||||
|
||||
// Exit(port, prvs, pubs, payload).
|
||||
pr := PeelExit(t, b, c)
|
||||
if pr.Port != port {
|
||||
t.Errorf("failed to retrieve original purchase nBytes")
|
||||
t.FailNow()
|
||||
}
|
||||
mh := sha256.Single(pr.Bytes)
|
||||
if mh != hash {
|
||||
t.Errorf("exit message not correctly decoded")
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// Reverse(hop[3].AddrPort).
|
||||
rp1 := PeelReverse(t, b, c)
|
||||
if rp1.AddrPort.String() != hop[3].AddrPort.String() {
|
||||
t.Errorf("failed to retrieve first reply hop")
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// OnionSkin(address.FromPubKey(hop[3].HeaderPub), replies[0]).
|
||||
PeelOnionSkin(t, b, c).Decrypt(sess[0].HeaderPrv, b, c)
|
||||
|
||||
// Reverse(hop[4].AddrPort).
|
||||
rp2 := PeelReverse(t, b, c)
|
||||
if rp2.AddrPort.String() != hop[4].AddrPort.String() {
|
||||
t.Errorf("failed to retrieve second reply hop")
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// OnionSkin(address.FromPubKey(hop[4].HeaderPub), replies[1]).
|
||||
PeelOnionSkin(t, b, c).Decrypt(sess[1].HeaderPrv, b, c)
|
||||
|
||||
// Reverse(client.AddrPort).
|
||||
rp3 := PeelReverse(t, b, c)
|
||||
if rp3.AddrPort.String() != client.AddrPort.String() {
|
||||
t.Errorf("failed to retrieve third reply hop")
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// OnionSkin(address.FromPubKey(client.HeaderPub), replies[2]).
|
||||
PeelOnionSkin(t, b, c).Decrypt(sess[2].HeaderPrv, b, c)
|
||||
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
package purchase
|
||||
|
||||
import (
|
||||
"github.com/indra-labs/indra"
|
||||
log2 "github.com/indra-labs/indra/pkg/log"
|
||||
"github.com/indra-labs/indra/pkg/nonce"
|
||||
"github.com/indra-labs/indra/pkg/sha256"
|
||||
"github.com/indra-labs/indra/pkg/slice"
|
||||
"github.com/indra-labs/indra/pkg/types"
|
||||
"github.com/indra-labs/indra/pkg/wire/magicbytes"
|
||||
)
|
||||
|
||||
const (
|
||||
MagicString = "pc"
|
||||
Len = magicbytes.Len + slice.Uint64Len + sha256.Len*3 +
|
||||
nonce.IVLen*3 + nonce.IDLen
|
||||
)
|
||||
|
||||
var (
|
||||
log = log2.GetLogger(indra.PathBase)
|
||||
check = log.E.Chk
|
||||
Magic = slice.Bytes(MagicString)
|
||||
_ types.Onion = &OnionSkin{}
|
||||
)
|
||||
|
||||
// OnionSkin purchase is a message that requests a session key, which will
|
||||
// activate when a payment for it has been done, or it will time out after some
|
||||
// period to allow unused codes to be flushed.
|
||||
type OnionSkin struct {
|
||||
nonce.ID
|
||||
NBytes uint64
|
||||
// Ciphers is a set of 3 symmetric ciphers that are to be used in their
|
||||
// given order over the reply message from the service.
|
||||
Ciphers [3]sha256.Hash
|
||||
// Nonces are the nonces to use with the cipher when creating the
|
||||
// encryption for the reply message.
|
||||
Nonces [3]nonce.IV
|
||||
types.Onion
|
||||
}
|
||||
|
||||
func (x *OnionSkin) Inner() types.Onion { return x.Onion }
|
||||
func (x *OnionSkin) Insert(o types.Onion) { x.Onion = o }
|
||||
func (x *OnionSkin) Len() int {
|
||||
return Len + x.Onion.Len()
|
||||
}
|
||||
|
||||
func (x *OnionSkin) Encode(b slice.Bytes, c *slice.Cursor) {
|
||||
copy(b[*c:c.Inc(magicbytes.Len)], Magic)
|
||||
copy(b[*c:c.Inc(nonce.IDLen)], x.ID[:])
|
||||
value := slice.NewUint64()
|
||||
slice.EncodeUint64(value, x.NBytes)
|
||||
copy(b[*c:c.Inc(slice.Uint64Len)], value)
|
||||
copy(b[*c:c.Inc(sha256.Len)], x.Ciphers[0][:])
|
||||
copy(b[*c:c.Inc(sha256.Len)], x.Ciphers[1][:])
|
||||
copy(b[*c:c.Inc(sha256.Len)], x.Ciphers[2][:])
|
||||
copy(b[*c:c.Inc(nonce.IVLen)], x.Nonces[0][:])
|
||||
copy(b[*c:c.Inc(nonce.IVLen)], x.Nonces[1][:])
|
||||
copy(b[*c:c.Inc(nonce.IVLen)], x.Nonces[2][:])
|
||||
x.Onion.Encode(b, c)
|
||||
}
|
||||
|
||||
func (x *OnionSkin) Decode(b slice.Bytes, c *slice.Cursor) (e error) {
|
||||
if len(b[*c:]) < Len-magicbytes.Len {
|
||||
return magicbytes.TooShort(len(b[*c:]),
|
||||
Len-magicbytes.Len, MagicString)
|
||||
}
|
||||
copy(x.ID[:], b[*c:c.Inc(nonce.IDLen)])
|
||||
x.NBytes = slice.DecodeUint64(
|
||||
b[*c:c.Inc(slice.Uint64Len)])
|
||||
for i := range x.Ciphers {
|
||||
bytes := b[*c:c.Inc(sha256.Len)]
|
||||
copy(x.Ciphers[i][:], bytes)
|
||||
bytes.Zero()
|
||||
}
|
||||
for i := range x.Nonces {
|
||||
bytes := b[*c:c.Inc(nonce.IVLen)]
|
||||
copy(x.Nonces[i][:], bytes)
|
||||
bytes.Zero()
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,18 +1,21 @@
|
||||
package response
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/indra-labs/indra/pkg/sha256"
|
||||
)
|
||||
|
||||
type Hook struct {
|
||||
sha256.Hash
|
||||
Callback func()
|
||||
time.Time
|
||||
}
|
||||
|
||||
type Hooks []Hook
|
||||
|
||||
func (h Hooks) Add(hash sha256.Hash, fn func()) (hh Hooks) {
|
||||
return append(h, Hook{Hash: hash, Callback: fn})
|
||||
return append(h, Hook{Hash: hash, Callback: fn, Time: time.Now()})
|
||||
}
|
||||
|
||||
func (h Hooks) Find(hash sha256.Hash) (hh Hooks) {
|
||||
|
||||
@@ -35,7 +35,6 @@ func New() *OnionSkin {
|
||||
func (x *OnionSkin) Inner() types.Onion { return nil }
|
||||
func (x *OnionSkin) Insert(_ types.Onion) {}
|
||||
func (x *OnionSkin) Len() int { return Len + len(x.Bytes) }
|
||||
|
||||
func (x *OnionSkin) Encode(b slice.Bytes, c *slice.Cursor) {
|
||||
copy(b[*c:c.Inc(magicbytes.Len)], Magic)
|
||||
copy(b[*c:c.Inc(sha256.Len)], x.Hash[:])
|
||||
@@ -44,7 +43,6 @@ func (x *OnionSkin) Encode(b slice.Bytes, c *slice.Cursor) {
|
||||
copy(b[*c:c.Inc(slice.Uint32Len)], bytesLen)
|
||||
copy(b[*c:c.Inc(len(x.Bytes))], x.Bytes)
|
||||
}
|
||||
|
||||
func (x *OnionSkin) Decode(b slice.Bytes, c *slice.Cursor) (e error) {
|
||||
if len(b[*c:]) < Len-magicbytes.Len {
|
||||
return magicbytes.TooShort(len(b[*c:]),
|
||||
|
||||
@@ -39,7 +39,6 @@ type OnionSkin struct {
|
||||
func (x *OnionSkin) Inner() types.Onion { return x.Onion }
|
||||
func (x *OnionSkin) Insert(o types.Onion) { x.Onion = o }
|
||||
func (x *OnionSkin) Len() int { return Len + x.Onion.Len() }
|
||||
|
||||
func (x *OnionSkin) Encode(b slice.Bytes, c *slice.Cursor) {
|
||||
copy(b[*c:c.Inc(magicbytes.Len)], Magic)
|
||||
var ap []byte
|
||||
@@ -51,7 +50,6 @@ func (x *OnionSkin) Encode(b slice.Bytes, c *slice.Cursor) {
|
||||
copy(b[c.Inc(1):c.Inc(Len-magicbytes.Len-1)], ap)
|
||||
x.Onion.Encode(b, c)
|
||||
}
|
||||
|
||||
func (x *OnionSkin) Decode(b slice.Bytes, c *slice.Cursor) (e error) {
|
||||
if len(b[*c:]) < Len-magicbytes.Len {
|
||||
return magicbytes.TooShort(len(b[*c:]), Len-magicbytes.Len, string(Magic))
|
||||
|
||||
@@ -43,7 +43,6 @@ type OnionSkin struct {
|
||||
func (x *OnionSkin) Inner() types.Onion { return x.Onion }
|
||||
func (x *OnionSkin) Insert(o types.Onion) { x.Onion = o }
|
||||
func (x *OnionSkin) Len() int { return Len + x.Onion.Len() }
|
||||
|
||||
func (x *OnionSkin) Encode(b slice.Bytes, c *slice.Cursor) {
|
||||
hdr, pld := x.HeaderKey.ToBytes(), x.PayloadKey.ToBytes()
|
||||
copy(b[*c:c.Inc(magicbytes.Len)], Magic)
|
||||
@@ -52,7 +51,6 @@ func (x *OnionSkin) Encode(b slice.Bytes, c *slice.Cursor) {
|
||||
copy(b[*c:c.Inc(pub.KeyLen)], pld[:])
|
||||
x.Onion.Encode(b, c)
|
||||
}
|
||||
|
||||
func (x *OnionSkin) Decode(b slice.Bytes, c *slice.Cursor) (e error) {
|
||||
if len(b[*c:]) < Len-magicbytes.Len {
|
||||
return magicbytes.TooShort(len(b[*c:]), Len-magicbytes.Len, string(Magic))
|
||||
|
||||
@@ -29,12 +29,10 @@ func NewOnionSkin() *OnionSkin {
|
||||
func (x *OnionSkin) Inner() types.Onion { return nil }
|
||||
func (x *OnionSkin) Insert(_ types.Onion) {}
|
||||
func (x *OnionSkin) Len() int { return MinLen }
|
||||
|
||||
func (x *OnionSkin) Encode(b slice.Bytes, c *slice.Cursor) {
|
||||
copy(b[*c:c.Inc(magicbytes.Len)], Magic)
|
||||
copy(b[*c:c.Inc(sha256.Len)], x[:sha256.Len])
|
||||
}
|
||||
|
||||
func (x *OnionSkin) Decode(b slice.Bytes, c *slice.Cursor) (e error) {
|
||||
if len(b[*c:]) < MinLen-magicbytes.Len {
|
||||
return magicbytes.TooShort(len(b[*c:]), MinLen-magicbytes.Len,
|
||||
|
||||
38
pkg/wire/util.go
Normal file
38
pkg/wire/util.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package wire
|
||||
|
||||
import (
|
||||
"github.com/indra-labs/indra/pkg/key/ecdh"
|
||||
"github.com/indra-labs/indra/pkg/key/prv"
|
||||
"github.com/indra-labs/indra/pkg/key/pub"
|
||||
"github.com/indra-labs/indra/pkg/nonce"
|
||||
"github.com/indra-labs/indra/pkg/sha256"
|
||||
)
|
||||
|
||||
func GenCiphers(prvs [3]*prv.Key, pubs [3]*pub.Key) (ciphers [3]sha256.Hash) {
|
||||
for i := range prvs {
|
||||
ciphers[2-i] = ecdh.Compute(prvs[i], pubs[i])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Gen3Nonces() (n [3]nonce.IV) {
|
||||
for i := range n {
|
||||
n[i] = nonce.New()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GenNonces(count int) (n []nonce.IV) {
|
||||
n = make([]nonce.IV, count)
|
||||
for i := range n {
|
||||
n[i] = nonce.New()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GenPingNonces() (n [4]nonce.IV) {
|
||||
for i := range n {
|
||||
n[i] = nonce.New()
|
||||
}
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user