Added Confirmation message and removed Acknowledgement

This commit is contained in:
David Vennik
2022-12-20 09:52:26 +00:00
parent cb4e2ca813
commit 4acda76427
6 changed files with 78 additions and 88 deletions

View File

@@ -1,10 +1,6 @@
package client
import (
crand "crypto/rand"
"encoding/binary"
"fmt"
"math/rand"
"net"
"github.com/Indra-Labs/indra"
@@ -13,7 +9,6 @@ import (
"github.com/Indra-Labs/indra/pkg/key/prv"
"github.com/Indra-Labs/indra/pkg/key/pub"
"github.com/Indra-Labs/indra/pkg/node"
"github.com/Indra-Labs/indra/pkg/nonce"
log2 "github.com/cybriq/proc/pkg/log"
"github.com/cybriq/qu"
)
@@ -75,43 +70,3 @@ func (c *Client) Cleanup() {
func (c *Client) Shutdown() {
c.C.Q()
}
const CircuitLen = 5
const CircuitExit = 2
const ReturnLen = 3
func (c *Client) GeneratePath(length, exit int) (ci *Circuit, e error) {
if len(c.Sessions) < 5 {
e = fmt.Errorf("insufficient Sessions to form a circuit, "+
"5 required, %d available", len(c.Sessions))
return
}
nodesLen := len(c.Nodes)
s := make(node.Nodes, nodesLen)
for i := range s {
s[i] = c.Nodes[i]
}
randBytes := make([]byte, 8)
// use crypto/rand to seed PRNG to avoid possible timing attacks.
var n int
if n, e = crand.Read(randBytes); check(e) && n != 8 {
return
}
rand.Seed(int64(binary.LittleEndian.Uint64(randBytes)))
rand.Shuffle(nodesLen, func(i, j int) { s[i], s[j] = s[j], s[i] })
ci = &Circuit{
ID: nonce.NewID(),
Hops: s[:length],
Exit: exit,
}
c.Circuits = c.Circuits.Add(ci)
return
}
func (c *Client) GenerateCircuit() (ci *Circuit, e error) {
return c.GeneratePath(CircuitLen, CircuitExit)
}
func (c *Client) GenerateReturn() (ci *Circuit, e error) {
return c.GeneratePath(ReturnLen, CircuitExit)
}

View File

@@ -11,10 +11,10 @@ import (
// with new credit, and the current state of the encryption.
type Session struct {
nonce.ID
Remaining uint64
SendEntry *address.SendEntry
ReceiveEntry *address.ReceiveEntry
KeyRoller *signer.KeySet
Remaining uint64
Forward, Return *address.SendEntry
ReceiveEntry *address.ReceiveEntry
KeyRoller *signer.KeySet
}
type Sessions []*Session
@@ -49,13 +49,13 @@ func (s Sessions) Find(t nonce.ID) (se *Session) {
//
// Purchasing a session the seller returns a token, based on a requested data
// allocation,
func NewSession(id nonce.ID, rem uint64, se *address.SendEntry,
func NewSession(id nonce.ID, rem uint64, fwd, rtn *address.SendEntry,
re *address.ReceiveEntry, kr *signer.KeySet) (s *Session) {
s = &Session{
ID: id,
Remaining: rem,
SendEntry: se,
Forward: fwd,
ReceiveEntry: re,
KeyRoller: kr,
}
@@ -80,7 +80,7 @@ func (s *Session) SubtractBytes(b uint64) bool {
}
func (s *Session) SetSendEntry(se *address.SendEntry) {
s.SendEntry = se
s.Forward = se
}
func (s *Session) SetReceiveEntry(re *address.ReceiveEntry) {

View File

@@ -16,6 +16,9 @@ type OnionSkins []Onion
func (o OnionSkins) Message(to *address.Sender, from *prv.Key) OnionSkins {
return append(o, &Message{To: to, From: from})
}
func (o OnionSkins) Confirmation(ciph sha256.Hash, id nonce.ID) OnionSkins {
return append(o, &Confirmation{Cipher: ciph, ID: id})
}
func (o OnionSkins) Forward(ip net.IP) OnionSkins {
return append(o, &Forward{IP: ip})
}
@@ -38,9 +41,6 @@ func (o OnionSkins) Session(fwd, rtn pub.Key) OnionSkins {
ForwardKey: fwd.ToBytes(), ReturnKey: rtn.ToBytes(),
})
}
func (o OnionSkins) Acknowledgement(id nonce.ID) OnionSkins {
return append(o, &Acknowledgement{ID: id})
}
func (o OnionSkins) Response(res slice.Bytes) OnionSkins {
return append(o, Response(res))
}

View File

@@ -4,9 +4,23 @@ import (
"github.com/Indra-Labs/indra/pkg/key/address"
"github.com/Indra-Labs/indra/pkg/key/signer"
"github.com/Indra-Labs/indra/pkg/node"
"github.com/Indra-Labs/indra/pkg/nonce"
"github.com/Indra-Labs/indra/pkg/sha256"
)
func Ping(nodes [3]node.Node, set signer.KeySet) Onion {
// Ping is a message which checks the liveness of relays by ensuring they are
// correctly relaying messages. Pending pings are stored in a table with the
// 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
// 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(ciph sha256.Hash, id nonce.ID, nodes [3]node.Node,
set signer.KeySet) Onion {
return OnionSkins{}.
Message(address.FromPubKey(nodes[0].Forward), set.Next()).
Forward(nodes[0].IP).
@@ -14,5 +28,6 @@ func Ping(nodes [3]node.Node, set signer.KeySet) Onion {
Forward(nodes[1].IP).
Message(address.FromPubKey(nodes[2].Forward), set.Next()).
Forward(nodes[2].IP).
Confirmation(ciph, id).
Assemble()
}

View File

@@ -1,6 +1,7 @@
package wire
import (
"crypto/aes"
"crypto/cipher"
"net"
@@ -25,15 +26,15 @@ var (
const MagicLen = 3
var (
ForwardMagic = slice.Bytes("fwd")
ExitMagic = slice.Bytes("exi")
ReturnMagic = slice.Bytes("rtn")
CipherMagic = slice.Bytes("cif")
PurchaseMagic = slice.Bytes("prc")
SessionMagic = slice.Bytes("ses")
AcknowledgementMagic = slice.Bytes("ack")
ResponseMagic = slice.Bytes("res")
TokenMagic = slice.Bytes("tok")
ConfirmationMagic = slice.Bytes("cnf")
ForwardMagic = slice.Bytes("fwd")
ExitMagic = slice.Bytes("exi")
ReturnMagic = slice.Bytes("rtn")
CipherMagic = slice.Bytes("cif")
PurchaseMagic = slice.Bytes("prc")
SessionMagic = slice.Bytes("ses")
ResponseMagic = slice.Bytes("res")
TokenMagic = slice.Bytes("tok")
)
// Onion is an interface for the layers of messages each encrypted inside a
@@ -100,6 +101,42 @@ func (on *Message) Encode(o slice.Bytes, c *slice.Cursor) {
copy(o[checkStart:checkEnd], hash[:4])
}
// Confirmation is an encryption layer for messages returned to the client on
// the inside of an onion, for Ping and Cipher messages, providing a
// confirmation of the transit of the onion through its encoded route.
//
// It is encrypted because otherwise internal identifiers could be leaked and
// potentially reveal something about the entropy of a client/relay.
//
// In order to speed up recognition, the key of the table of pending Ping and
// Cipher messages will include the last hop that will deliver this layer of the
// onion - there can be more than one up in the air at a time, but they are
// randomly selected, so they will generally be a much smaller subset versus the
// current full set of Session s currently open.
type Confirmation struct {
Cipher sha256.Hash
nonce.ID
}
var _ Onion = &Confirmation{}
func (cf *Confirmation) Inner() Onion { return nil }
func (cf *Confirmation) Insert(o Onion) {}
func (cf *Confirmation) Len() int {
return MagicLen + nonce.IDLen
}
func (cf *Confirmation) Encode(o slice.Bytes, c *slice.Cursor) {
copy(o[*c:c.Inc(MagicLen)], ConfirmationMagic)
// Generate block cipher from confirmation Cipher.
block, _ := aes.NewCipher(cf.Cipher[:])
start, end := *c, c.Inc(nonce.IDLen)
// Copy in the ID.
copy(o[start:end], cf.ID[:])
// Encrypt the ID to the cipher.
ciph.Encipher(block, nonce.New(), o[start:end])
}
// Forward is just an IP address and a wrapper for another message.
type Forward struct {
net.IP
@@ -278,27 +315,10 @@ func (se *Session) Encode(o slice.Bytes, c *slice.Cursor) {
se.Onion.Encode(o, c)
}
// The remaining methods are terminals, all constructed Onion structures
// The remaining types are terminals, all constructed Onion structures
// should have one of these as the last element otherwise the second last call
// to Encode will panic with a nil.
// Acknowledgement messages just contain a nonce ID, these are used to terminate
// ping and Cipher onion messages that confirm relaying was successful.
type Acknowledgement struct {
nonce.ID
}
var _ Onion = &Acknowledgement{}
func (ak *Acknowledgement) Inner() Onion { return nil }
func (ak *Acknowledgement) Insert(_ Onion) {}
func (ak *Acknowledgement) Len() int { return MagicLen + nonce.IDLen }
func (ak *Acknowledgement) Encode(o slice.Bytes, c *slice.Cursor) {
copy(o[*c:c.Inc(MagicLen)], AcknowledgementMagic)
copy(o[*c:c.Inc(pub.KeyLen)], ak.ID[:])
}
// Response messages are what are carried back via Return messages from an Exit.
type Response slice.Bytes
@@ -316,7 +336,7 @@ func (rs Response) Encode(o slice.Bytes, c *slice.Cursor) {
copy(o[*c:c.Inc(len(rs))], rs)
}
// A Token is a 32 byte value. TODO: not sure we need this?
// A Token is a 32 byte value.
type Token sha256.Hash
var _ Onion = Token{}

View File

@@ -13,11 +13,11 @@ var (
// GitRef is the gitref, as in refs/heads/branchname.
GitRef = "refs/heads/main"
// ParentGitCommit is the commit hash of the parent HEAD.
ParentGitCommit = "6f024585b196a19c99cd28a80613c3a5a6b8ada1"
ParentGitCommit = "6c414cd9e34291f25632a39ee739435a56ce7fa4"
// BuildTime stores the time when the current binary was built.
BuildTime = "2022-12-19T22:23:36Z"
BuildTime = "2022-12-20T09:52:26Z"
// SemVer lists the (latest) git tag on the build.
SemVer = "v0.0.191"
SemVer = "v0.0.192"
// PathBase is the path base returned from runtime caller.
PathBase = "/home/loki/src/github.com/Indra-Labs/indra/"
// Major is the major number from the tag.
@@ -25,7 +25,7 @@ var (
// Minor is the minor number from the tag.
Minor = 0
// Patch is the patch version number from the tag.
Patch = 191
Patch = 192
)
// Version returns a pretty printed version information string.