Stubbed in new Decode method in types.Onion

This commit is contained in:
David Vennik
2022-12-22 10:47:28 +00:00
parent ca954b424a
commit f502f8483a
20 changed files with 557 additions and 343 deletions

15
pkg/types/types.go Normal file
View File

@@ -0,0 +1,15 @@
package types
import (
"github.com/Indra-Labs/indra/pkg/slice"
)
// Onion is an interface for the layers of messages each encrypted inside a
// Message, which provides the cipher for the inner layers inside it.
type Onion interface {
Encode(b slice.Bytes, c *slice.Cursor)
Decode(b slice.Bytes, c *slice.Cursor) (in interface{}, e error)
Len() int
Inner() Onion
Insert(on Onion)
}

47
pkg/wire/cipher.go Normal file
View File

@@ -0,0 +1,47 @@
package wire
import (
"github.com/Indra-Labs/indra/pkg/key/prv"
"github.com/Indra-Labs/indra/pkg/key/pub"
"github.com/Indra-Labs/indra/pkg/slice"
"github.com/Indra-Labs/indra/pkg/types"
)
// Cipher delivers a public key to be used in association with a Return
// specifically in the situation of a node bootstrapping that doesn't have
// sessions yet. The Forward key will appear in the pre-formed header, but the
// cipher provided to the seller will correspond with
type Cipher struct {
Header, Payload *prv.Key
types.Onion
}
var (
CipherMagic = slice.Bytes("cif")
_ types.Onion = &Cipher{}
)
func (x *Cipher) Inner() types.Onion { return x.Onion }
func (x *Cipher) Insert(o types.Onion) { x.Onion = o }
func (x *Cipher) Len() int {
return MagicLen + pub.KeyLen + x.Onion.Len()
}
func (x *Cipher) Encode(o slice.Bytes, c *slice.Cursor) {
copy(o[*c:c.Inc(MagicLen)], CipherMagic)
hdr, pld := x.Header.ToBytes(), x.Payload.ToBytes()
copy(o[c.Inc(1):c.Inc(prv.KeyLen)], hdr[:])
copy(o[c.Inc(1):c.Inc(prv.KeyLen)], pld[:])
x.Onion.Encode(o, c)
}
func (x *Cipher) Decode(b slice.Bytes, c *slice.Cursor) (in interface{},
e error) {
magic := CipherMagic
if !CheckMagic(b, magic) {
return ReturnError(ErrWrongMagic, x, b, magic)
}
return
}

51
pkg/wire/confirmation.go Normal file
View File

@@ -0,0 +1,51 @@
package wire
import (
"github.com/Indra-Labs/indra/pkg/nonce"
"github.com/Indra-Labs/indra/pkg/slice"
"github.com/Indra-Labs/indra/pkg/types"
)
// 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 {
nonce.ID
}
var (
ConfirmationMagic = slice.Bytes("cnf")
_ types.Onion = &Confirmation{}
)
func (x *Confirmation) Inner() types.Onion { return nil }
func (x *Confirmation) Insert(o types.Onion) {}
func (x *Confirmation) Len() int {
return MagicLen + nonce.IDLen
}
func (x *Confirmation) Encode(o slice.Bytes, c *slice.Cursor) {
copy(o[*c:c.Inc(MagicLen)], ConfirmationMagic)
// Copy in the ID.
copy(o[*c:c.Inc(nonce.IDLen)], x.ID[:])
}
func (x *Confirmation) Decode(b slice.Bytes, c *slice.Cursor) (in interface{},
e error) {
magic := ConfirmationMagic
if !CheckMagic(b, magic) {
return ReturnError(ErrWrongMagic, x, b, magic)
}
return
}

View File

@@ -1 +0,0 @@
package wire

View File

@@ -1 +0,0 @@
package wire

65
pkg/wire/exit.go Normal file
View File

@@ -0,0 +1,65 @@
package wire
import (
"github.com/Indra-Labs/indra/pkg/sha256"
"github.com/Indra-Labs/indra/pkg/slice"
"github.com/Indra-Labs/indra/pkg/types"
)
// Exit messages are the layer of a message after two Forward packets that
// provides an exit address and
type Exit struct {
// Port identifies the type of service as well as being the port used by
// the service to be relayed to. Notice there is no IP address, this is
// because Indranet only forwards to exits of decentralised services
// also running on the same machine. This service could be a proxy, of
// course, if configured this way. This could be done by tunneling from
// a local Socks5 proxy into Indranet and the exit node also having
// this.
Port uint16
// Cipher is a set of 3 symmetric ciphers that are to be used in their
// given order over the reply message from the service.
Cipher [3]sha256.Hash
// Bytes are the message to be passed to the exit service.
slice.Bytes
types.Onion
}
var (
ExitMagic = slice.Bytes("exi")
_ types.Onion = &Exit{}
)
func (x *Exit) Inner() types.Onion { return x.Onion }
func (x *Exit) Insert(o types.Onion) { x.Onion = o }
func (x *Exit) Len() int {
return MagicLen + slice.Uint16Len + 3*sha256.Len + x.Bytes.Len() +
x.Onion.Len()
}
func (x *Exit) Encode(o slice.Bytes, c *slice.Cursor) {
copy(o[*c:c.Inc(MagicLen)], ExitMagic)
port := slice.NewUint16()
slice.EncodeUint16(port, int(x.Port))
copy(o[*c:c.Inc(slice.Uint16Len)], port)
copy(o[*c:c.Inc(sha256.Len)], x.Cipher[0][:])
copy(o[*c:c.Inc(sha256.Len)], x.Cipher[1][:])
copy(o[*c:c.Inc(sha256.Len)], x.Cipher[1][:])
bytesLen := slice.NewUint32()
slice.EncodeUint32(bytesLen, len(x.Bytes))
copy(o[*c:c.Inc(slice.Uint32Len)], bytesLen)
copy(o[*c:c.Inc(len(x.Bytes))], x.Bytes)
x.Onion.Encode(o, c)
}
func (x *Exit) Decode(b slice.Bytes, c *slice.Cursor) (in interface{},
e error) {
magic := ExitMagic
if !CheckMagic(b, magic) {
return ReturnError(ErrWrongMagic, x, b, magic)
}
return
}

43
pkg/wire/forward.go Normal file
View File

@@ -0,0 +1,43 @@
package wire
import (
"net"
"github.com/Indra-Labs/indra/pkg/slice"
"github.com/Indra-Labs/indra/pkg/types"
)
// Forward is just an IP address and a wrapper for another message.
type Forward struct {
net.IP
types.Onion
}
var (
ForwardMagic = slice.Bytes("fwd")
_ types.Onion = &Forward{}
)
func (x *Forward) Inner() types.Onion { return x.Onion }
func (x *Forward) Insert(o types.Onion) { x.Onion = o }
func (x *Forward) Len() int {
return MagicLen + len(x.IP) + 1 + x.Onion.Len()
}
func (x *Forward) Encode(o slice.Bytes, c *slice.Cursor) {
copy(o[*c:c.Inc(MagicLen)], ForwardMagic)
o[*c] = byte(len(x.IP))
copy(o[c.Inc(1):c.Inc(len(x.IP))], x.IP)
x.Onion.Encode(o, c)
}
func (x *Forward) Decode(b slice.Bytes, c *slice.Cursor) (in interface{},
e error) {
magic := ForwardMagic
if !CheckMagic(b, magic) {
return ReturnError(ErrWrongMagic, x, b, magic)
}
return
}

View File

@@ -9,9 +9,10 @@ import (
"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"
)
type OnionSkins []Onion
type OnionSkins []types.Onion
func (o OnionSkins) Header(to *address.Sender, from *prv.Key) OnionSkins {
return append(o, &Message{To: to, From: from})
@@ -52,7 +53,7 @@ func (o OnionSkins) Token(tok sha256.Hash) OnionSkins {
// contains the second, second contains the third, and so on, and then returns
// the first onion, on which you can then call Encode and generate the wire
// message form of the onion.
func (o OnionSkins) Assemble() (on Onion) {
func (o OnionSkins) Assemble() (on types.Onion) {
// First item is the outer layer.
on = o[0]
// Iterate through the remaining layers.

View File

@@ -1 +0,0 @@
package wire

79
pkg/wire/message.go Normal file
View File

@@ -0,0 +1,79 @@
package wire
import (
"crypto/cipher"
"github.com/Indra-Labs/indra/pkg/ciph"
"github.com/Indra-Labs/indra/pkg/key/address"
"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"
"github.com/Indra-Labs/indra/pkg/slice"
"github.com/Indra-Labs/indra/pkg/types"
)
// Message is the generic top level wrapper for an Onion. 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.
type Message struct {
To *address.Sender
From *prv.Key
// The following field is only populated in the outermost layer.
slice.Bytes
types.Onion
}
const OnionHeaderLen = 4 + nonce.IVLen + address.Len + pub.KeyLen
var _ types.Onion = &Message{}
func (x *Message) Inner() types.Onion { return x.Onion }
func (x *Message) Insert(o types.Onion) { x.Onion = o }
func (x *Message) Len() int {
return MagicLen + OnionHeaderLen + x.Onion.Len()
}
func (x *Message) Encode(o slice.Bytes, c *slice.Cursor) {
// The first level message contains the Bytes, but the inner layers do
// not. The inner layers will be passed this buffer, but the first needs
// to have it copied from its original location.
if o == nil {
o = x.Bytes
}
// We write the checksum last so save the cursor position here.
checkStart := *c
checkEnd := checkStart + 4
// Generate a new nonce and copy it in.
n := nonce.New()
copy(o[c.Inc(4):c.Inc(nonce.IVLen)], n[:])
// Derive the cloaked key and copy it in.
to := x.To.GetCloak()
copy(o[*c:c.Inc(address.Len)], to[:])
// Derive the public key from the From key and copy in.
pubKey := pub.Derive(x.From).ToBytes()
copy(o[*c:c.Inc(pub.KeyLen)], pubKey[:])
// Call the tree of onions to perform their encoding.
x.Onion.Encode(o, c)
// Then we can encrypt the message segment
var e error
var blk cipher.Block
if blk = ciph.GetBlock(x.From, x.To.Key); check(e) {
panic(e)
}
ciph.Encipher(blk, n, o[checkEnd:])
// Get the hash of the message and truncate it to the checksum at the
// start of the message. Every layer of the onion has a Header and an
// onion inside it, the Header takes care of the encryption. This saves
// x complications as every layer is header first, message after, with
// wrapped messages inside each message afterwards.
hash := sha256.Single(o[checkEnd:])
copy(o[checkStart:checkEnd], hash[:4])
}
func (x *Message) Decode(b slice.Bytes, c *slice.Cursor) (in interface{},
e error) {
return
}

View File

@@ -9,6 +9,7 @@ import (
"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"
)
// Ping is a message which checks the liveness of relays by ensuring they are
@@ -22,7 +23,7 @@ import (
// 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) Onion {
set signer.KeySet) types.Onion {
return OnionSkins{}.
Header(address.FromPubKey(hop[0].HeaderKey), set.Next()).
@@ -50,7 +51,7 @@ func Ping(id nonce.ID, client node.Node, hop [3]node.Node,
// that the key was successfully delivered to the Return 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) Onion {
client node.Node, hop [5]node.Node, set signer.KeySet) types.Onion {
return OnionSkins{}.
Header(address.FromPubKey(hop[0].HeaderKey), set.Next()).
@@ -84,7 +85,7 @@ func SendKeys(id nonce.ID, hdr, pld *prv.Key,
// remainder with noise, so it always looks like the first hop,
// indistinguishable.
func SendPurchase(nBytes uint64, client node.Node,
hop [5]node.Node, set signer.KeySet) Onion {
hop [5]node.Node, set signer.KeySet) types.Onion {
var rtns [3]*prv.Key
for i := range rtns {
@@ -136,7 +137,7 @@ func SendPurchase(nBytes uint64, client node.Node,
// remainder with noise, so it always looks like the first hop,
// indistinguishable.
func SendExit(payload slice.Bytes, port uint16, client node.Node,
hop [5]node.Node, set signer.KeySet) Onion {
hop [5]node.Node, set signer.KeySet) types.Onion {
var rtns [3]*prv.Key
for i := range rtns {

View File

@@ -1 +0,0 @@
package wire

View File

@@ -1,22 +1,16 @@
package wire
import (
"crypto/cipher"
"net"
"fmt"
"reflect"
"github.com/Indra-Labs/indra"
"github.com/Indra-Labs/indra/pkg/ciph"
"github.com/Indra-Labs/indra/pkg/key/address"
"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"
"github.com/Indra-Labs/indra/pkg/slice"
log2 "github.com/cybriq/proc/pkg/log"
. "github.com/cybriq/proc/pkg/log"
)
var (
log = log2.GetLogger(indra.PathBase)
log = GetLogger(indra.PathBase)
check = log.E.Chk
)
@@ -24,326 +18,24 @@ var (
// valid Magic string as listed below.
const MagicLen = 3
var (
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")
)
const ErrWrongMagic = "expected '%v', got '%v': type: %v"
// Onion is an interface for the layers of messages each encrypted inside a
// Message, which provides the cipher for the inner layers inside it.
type Onion interface {
Encode(o slice.Bytes, c *slice.Cursor)
Len() int
Inner() Onion
Insert(on Onion)
}
// Message is the generic top level wrapper for an Onion. 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.
type Message struct {
To *address.Sender
From *prv.Key
// The following field is only populated in the outermost layer.
slice.Bytes
Onion
}
const OnionHeaderLen = 4 + nonce.IVLen + address.Len + pub.KeyLen
var _ Onion = &Message{}
func (on *Message) Inner() Onion { return on.Onion }
func (on *Message) Insert(o Onion) { on.Onion = o }
func (on *Message) Len() int {
return MagicLen + OnionHeaderLen + on.Onion.Len()
}
func (on *Message) Encode(o slice.Bytes, c *slice.Cursor) {
// The first level message contains the Bytes, but the inner layers do
// not. The inner layers will be passed this buffer, but the first needs
// to have it copied from its original location.
if o == nil {
o = on.Bytes
func CheckMagic(b slice.Bytes, m slice.Bytes) (match bool) {
header := b[:MagicLen]
match = true
for i := range header {
if header[i] != m[i] {
match = false
break
}
}
// We write the checksum last so save the cursor position here.
checkStart := *c
checkEnd := checkStart + 4
// Generate a new nonce and copy it in.
n := nonce.New()
copy(o[c.Inc(4):c.Inc(nonce.IVLen)], n[:])
// Derive the cloaked key and copy it in.
to := on.To.GetCloak()
copy(o[*c:c.Inc(address.Len)], to[:])
// Derive the public key from the From key and copy in.
pubKey := pub.Derive(on.From).ToBytes()
copy(o[*c:c.Inc(pub.KeyLen)], pubKey[:])
// Call the tree of onions to perform their encoding.
on.Onion.Encode(o, c)
// Then we can encrypt the message segment
var e error
var blk cipher.Block
if blk = ciph.GetBlock(on.From, on.To.Key); check(e) {
panic(e)
}
ciph.Encipher(blk, n, o[checkEnd:])
// Get the hash of the message and truncate it to the checksum at the
// start of the message. Every layer of the onion has a Header and an
// onion inside it, the Header takes care of the encryption. This saves
// on complications as every layer is header first, message after, with
// wrapped messages inside each message afterwards.
hash := sha256.Single(o[checkEnd:])
copy(o[checkStart:checkEnd], hash[:4])
return
}
// 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 {
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)
// Copy in the ID.
copy(o[*c:c.Inc(nonce.IDLen)], cf.ID[:])
}
// Forward is just an IP address and a wrapper for another message.
type Forward struct {
net.IP
Onion
}
var _ Onion = &Forward{}
func (fw *Forward) Inner() Onion { return fw.Onion }
func (fw *Forward) Insert(o Onion) { fw.Onion = o }
func (fw *Forward) Len() int {
return MagicLen + len(fw.IP) + 1 + fw.Onion.Len()
}
func (fw *Forward) Encode(o slice.Bytes, c *slice.Cursor) {
copy(o[*c:c.Inc(MagicLen)], ForwardMagic)
o[*c] = byte(len(fw.IP))
copy(o[c.Inc(1):c.Inc(len(fw.IP))], fw.IP)
fw.Onion.Encode(o, c)
}
// Return messages are distinct from Forward messages in that the header
// encryption uses a different secret than the payload. The magic bytes signal
// this to the relay that receives this, which then looks up the Return key
// matching the To address in the message header.
type Return struct {
// IP is the address of the next relay in the return leg of a circuit.
net.IP
Onion
}
var _ Onion = &Return{}
func (rt *Return) Inner() Onion { return rt.Onion }
func (rt *Return) Insert(o Onion) { rt.Onion = o }
func (rt *Return) Len() int {
return MagicLen + len(rt.IP) + 1 + rt.Onion.Len()
}
func (rt *Return) Encode(o slice.Bytes, c *slice.Cursor) {
copy(o[*c:c.Inc(MagicLen)], ReturnMagic)
o[*c] = byte(len(rt.IP))
copy(o[c.Inc(1):c.Inc(len(rt.IP))], rt.IP)
rt.Onion.Encode(o, c)
}
// Exit messages are the layer of a message after two Forward packets that
// provides an exit address and
type Exit struct {
// Port identifies the type of service as well as being the port used by
// the service to be relayed to. Notice there is no IP address, this is
// because Indranet only forwards to exits of decentralised services
// also running on the same machine. This service could be a proxy, of
// course, if configured this way. This could be done by tunneling from
// a local Socks5 proxy into Indranet and the exit node also having
// this.
Port uint16
// Cipher is a set of 3 symmetric ciphers that are to be used in their
// given order over the reply message from the service.
Cipher [3]sha256.Hash
// Bytes are the message to be passed to the exit service.
slice.Bytes
Onion
}
var _ Onion = &Exit{}
func (ex *Exit) Inner() Onion { return ex.Onion }
func (ex *Exit) Insert(o Onion) { ex.Onion = o }
func (ex *Exit) Len() int {
return MagicLen + slice.Uint16Len + 3*sha256.Len + ex.Bytes.Len() +
ex.Onion.Len()
}
func (ex *Exit) Encode(o slice.Bytes, c *slice.Cursor) {
copy(o[*c:c.Inc(MagicLen)], ExitMagic)
port := slice.NewUint16()
slice.EncodeUint16(port, int(ex.Port))
copy(o[*c:c.Inc(slice.Uint16Len)], port)
copy(o[*c:c.Inc(sha256.Len)], ex.Cipher[0][:])
copy(o[*c:c.Inc(sha256.Len)], ex.Cipher[1][:])
copy(o[*c:c.Inc(sha256.Len)], ex.Cipher[1][:])
bytesLen := slice.NewUint32()
slice.EncodeUint32(bytesLen, len(ex.Bytes))
copy(o[*c:c.Inc(slice.Uint32Len)], bytesLen)
copy(o[*c:c.Inc(len(ex.Bytes))], ex.Bytes)
ex.Onion.Encode(o, c)
func ReturnError(errString string, t interface{}, found,
magic slice.Bytes) (in interface{}, e error) {
e = fmt.Errorf(errString,
found[:MagicLen], magic, reflect.TypeOf(t))
return
}
// Cipher delivers a public key to be used in association with a Return
// specifically in the situation of a node bootstrapping that doesn't have
// sessions yet. The Forward key will appear in the pre-formed header, but the
// cipher provided to the seller will correspond with
type Cipher struct {
Header, Payload *prv.Key
Onion
}
var _ Onion = &Cipher{}
func (ci *Cipher) Inner() Onion { return ci.Onion }
func (ci *Cipher) Insert(o Onion) { ci.Onion = o }
func (ci *Cipher) Len() int {
return MagicLen + pub.KeyLen + ci.Onion.Len()
}
func (ci *Cipher) Encode(o slice.Bytes, c *slice.Cursor) {
copy(o[*c:c.Inc(MagicLen)], CipherMagic)
hdr, pld := ci.Header.ToBytes(), ci.Payload.ToBytes()
copy(o[c.Inc(1):c.Inc(prv.KeyLen)], hdr[:])
copy(o[c.Inc(1):c.Inc(prv.KeyLen)], pld[:])
ci.Onion.Encode(o, c)
}
// Purchase is a message that is sent after first forwarding a Lighting payment
// of an amount corresponding to the number of bytes requested based on the
// price advertised for Exit traffic by a relay.
//
// The Return bytes contain the message header that is prepended to a Session
// message which contains the pair of keys associated with the Session that is
// purchased.
type Purchase struct {
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
Onion
}
var _ Onion = &Purchase{}
func (pr *Purchase) Inner() Onion { return pr.Onion }
func (pr *Purchase) Insert(o Onion) { pr.Onion = o }
func (pr *Purchase) Len() int {
return MagicLen + slice.Uint64Len + pr.Onion.Len()
}
func (pr *Purchase) Encode(o slice.Bytes, c *slice.Cursor) {
copy(o[*c:c.Inc(MagicLen)], PurchaseMagic)
value := slice.NewUint64()
slice.EncodeUint64(value, pr.NBytes)
pr.Onion.Encode(o, c)
}
// Session is a message containing two public keys which identify to a relay how
// to decrypt the header in a Return message, using the HeaderKey, and the
// payload, which uses the PayloadKey. There is two keys in order to prevent the
// Exit node from being able to decrypt the header, but enable it to encrypt the
// payload, and Return relay hops have these key pairs and identify the
// HeaderKey and then know they can unwrap their layer of the payload using the
// PayloadKey.
//
// Clients use the HeaderKey, cloaked, in their messages for the seller relay,
// in the header, and use the PayloadKey as the public key half with ECDH and
// their generated private key which produces the public key that is placed in
// the header.
type Session struct {
HeaderKey *pub.Key
PayloadKey *pub.Key
Onion
}
var _ Onion = &Session{}
func (se *Session) Inner() Onion { return se.Onion }
func (se *Session) Insert(o Onion) { se.Onion = o }
func (se *Session) Len() int {
return MagicLen + pub.KeyLen*2 + se.Onion.Len()
}
func (se *Session) Encode(o slice.Bytes, c *slice.Cursor) {
hdr, pld := se.HeaderKey.ToBytes(), se.PayloadKey.ToBytes()
copy(o[*c:c.Inc(MagicLen)], SessionMagic)
copy(o[*c:c.Inc(pub.KeyLen)], hdr[:])
copy(o[*c:c.Inc(pub.KeyLen)], pld[:])
se.Onion.Encode(o, c)
}
// 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.
// Response messages are what are carried back via Return messages from an Exit.
type Response slice.Bytes
var _ Onion = Response{}
func (rs Response) Inner() Onion { return nil }
func (rs Response) Insert(_ Onion) {}
func (rs Response) Len() int { return MagicLen + len(rs) + 4 }
func (rs Response) Encode(o slice.Bytes, c *slice.Cursor) {
copy(o[*c:c.Inc(MagicLen)], ResponseMagic)
bytesLen := slice.NewUint32()
slice.EncodeUint32(bytesLen, len(rs))
copy(o[*c:c.Inc(slice.Uint32Len)], bytesLen)
copy(o[*c:c.Inc(len(rs))], rs)
}
// A Token is a 32 byte value.
type Token sha256.Hash
var _ Onion = Token{}
func (tk Token) Inner() Onion { return nil }
func (tk Token) Insert(_ Onion) {}
func (tk Token) Len() int { return MagicLen + sha256.Len }
func (tk Token) Encode(o slice.Bytes, c *slice.Cursor) {
copy(o[*c:c.Inc(MagicLen)], TokenMagic)
copy(o[*c:c.Inc(sha256.Len)], tk[:])
}

View File

@@ -1 +0,0 @@
package wire

51
pkg/wire/purchase.go Normal file
View File

@@ -0,0 +1,51 @@
package wire
import (
"github.com/Indra-Labs/indra/pkg/sha256"
"github.com/Indra-Labs/indra/pkg/slice"
"github.com/Indra-Labs/indra/pkg/types"
)
// Purchase is a message that is sent after first forwarding a Lighting payment
// of an amount corresponding to the number of bytes requested based on the
// price advertised for Exit traffic by a relay.
//
// The Return bytes contain the message header that is prepended to a Session
// message which contains the pair of keys associated with the Session that is
// purchased.
type Purchase struct {
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
types.Onion
}
var (
PurchaseMagic = slice.Bytes("prc")
_ types.Onion = &Purchase{}
)
func (x *Purchase) Inner() types.Onion { return x.Onion }
func (x *Purchase) Insert(o types.Onion) { x.Onion = o }
func (x *Purchase) Len() int {
return MagicLen + slice.Uint64Len + x.Onion.Len()
}
func (x *Purchase) Encode(o slice.Bytes, c *slice.Cursor) {
copy(o[*c:c.Inc(MagicLen)], PurchaseMagic)
value := slice.NewUint64()
slice.EncodeUint64(value, x.NBytes)
x.Onion.Encode(o, c)
}
func (x *Purchase) Decode(b slice.Bytes, c *slice.Cursor) (in interface{},
e error) {
magic := PurchaseMagic
if !CheckMagic(b, magic) {
return ReturnError(ErrWrongMagic, x, b, magic)
}
return
}

37
pkg/wire/response.go Normal file
View File

@@ -0,0 +1,37 @@
package wire
import (
"github.com/Indra-Labs/indra/pkg/slice"
"github.com/Indra-Labs/indra/pkg/types"
)
// Response messages are what are carried back via Return messages from an Exit.
type Response slice.Bytes
var (
ResponseMagic = slice.Bytes("res")
_ types.Onion = Response{}
)
func (x Response) Inner() types.Onion { return nil }
func (x Response) Insert(_ types.Onion) {}
func (x Response) Len() int { return MagicLen + len(x) + 4 }
func (x Response) Encode(o slice.Bytes, c *slice.Cursor) {
copy(o[*c:c.Inc(MagicLen)], ResponseMagic)
bytesLen := slice.NewUint32()
slice.EncodeUint32(bytesLen, len(x))
copy(o[*c:c.Inc(slice.Uint32Len)], bytesLen)
copy(o[*c:c.Inc(len(x))], x)
}
func (x Response) Decode(b slice.Bytes, c *slice.Cursor) (in interface{},
e error) {
magic := ResponseMagic
if !CheckMagic(b, magic) {
return ReturnError(ErrWrongMagic, x, b, magic)
}
return
}

47
pkg/wire/return.go Normal file
View File

@@ -0,0 +1,47 @@
package wire
import (
"net"
"github.com/Indra-Labs/indra/pkg/slice"
"github.com/Indra-Labs/indra/pkg/types"
)
// Return messages are distinct from Forward messages in that the header
// encryption uses a different secret than the payload. The magic bytes signal
// this to the relay that receives this, which then looks up the Return key
// matching the To address in the message header.
type Return struct {
// IP is the address of the next relay in the return leg of a circuit.
net.IP
types.Onion
}
var (
ReturnMagic = slice.Bytes("rtn")
_ types.Onion = &Return{}
)
func (x *Return) Inner() types.Onion { return x.Onion }
func (x *Return) Insert(o types.Onion) { x.Onion = o }
func (x *Return) Len() int {
return MagicLen + len(x.IP) + 1 + x.Onion.Len()
}
func (x *Return) Encode(o slice.Bytes, c *slice.Cursor) {
copy(o[*c:c.Inc(MagicLen)], ReturnMagic)
o[*c] = byte(len(x.IP))
copy(o[c.Inc(1):c.Inc(len(x.IP))], x.IP)
x.Onion.Encode(o, c)
}
func (x *Return) Decode(b slice.Bytes, c *slice.Cursor) (in interface{},
e error) {
magic := ReturnMagic
if !CheckMagic(b, magic) {
return ReturnError(ErrWrongMagic, x, b, magic)
}
return
}

55
pkg/wire/session.go Normal file
View File

@@ -0,0 +1,55 @@
package wire
import (
"github.com/Indra-Labs/indra/pkg/key/pub"
"github.com/Indra-Labs/indra/pkg/slice"
"github.com/Indra-Labs/indra/pkg/types"
)
// Session is a message containing two public keys which identify to a relay how
// to decrypt the header in a Return message, using the HeaderKey, and the
// payload, which uses the PayloadKey. There is two keys in order to prevent the
// Exit node from being able to decrypt the header, but enable it to encrypt the
// payload, and Return relay hops have these key pairs and identify the
// HeaderKey and then know they can unwrap their layer of the payload using the
// PayloadKey.
//
// Clients use the HeaderKey, cloaked, in their messages for the seller relay,
// in the header, and use the PayloadKey as the public key half with ECDH and
// their generated private key which produces the public key that is placed in
// the header.
type Session struct {
HeaderKey *pub.Key
PayloadKey *pub.Key
types.Onion
}
var (
SessionMagic = slice.Bytes("ses")
_ types.Onion = &Session{}
)
func (x *Session) Inner() types.Onion { return x.Onion }
func (x *Session) Insert(o types.Onion) { x.Onion = o }
func (x *Session) Len() int {
return MagicLen + pub.KeyLen*2 + x.Onion.Len()
}
func (x *Session) Encode(o slice.Bytes, c *slice.Cursor) {
hdr, pld := x.HeaderKey.ToBytes(), x.PayloadKey.ToBytes()
copy(o[*c:c.Inc(MagicLen)], SessionMagic)
copy(o[*c:c.Inc(pub.KeyLen)], hdr[:])
copy(o[*c:c.Inc(pub.KeyLen)], pld[:])
x.Onion.Encode(o, c)
}
func (x *Session) Decode(b slice.Bytes, c *slice.Cursor) (in interface{},
e error) {
magic := SessionMagic
if !CheckMagic(b, magic) {
return ReturnError(ErrWrongMagic, x, b, magic)
}
return
}

35
pkg/wire/token.go Normal file
View File

@@ -0,0 +1,35 @@
package wire
import (
"github.com/Indra-Labs/indra/pkg/sha256"
"github.com/Indra-Labs/indra/pkg/slice"
"github.com/Indra-Labs/indra/pkg/types"
)
// A Token is a 32 byte value.
type Token sha256.Hash
var (
TokenMagic = slice.Bytes("tok")
_ types.Onion = Token{}
)
func (x Token) Inner() types.Onion { return nil }
func (x Token) Insert(_ types.Onion) {}
func (x Token) Len() int { return MagicLen + sha256.Len }
func (x Token) Encode(o slice.Bytes, c *slice.Cursor) {
copy(o[*c:c.Inc(MagicLen)], TokenMagic)
copy(o[*c:c.Inc(sha256.Len)], x[:])
}
func (x Token) Decode(b slice.Bytes, c *slice.Cursor) (in interface{},
e error) {
magic := TokenMagic
if !CheckMagic(b, magic) {
return ReturnError(ErrWrongMagic, x, b, magic)
}
return
}

View File

@@ -11,13 +11,13 @@ var (
// URL is the git URL for the repository.
URL = "github.com/Indra-Labs/indra"
// GitRef is the gitref, as in refs/heads/branchname.
GitRef = "refs/heads/ind-bootstrap"
GitRef = "refs/heads/main"
// ParentGitCommit is the commit hash of the parent HEAD.
ParentGitCommit = "8c361507bd145da860c9753d1dfb8951223ffc39"
ParentGitCommit = "30f1e7ed49f9a8a9445787a90e7205598edca81d"
// BuildTime stores the time when the current binary was built.
BuildTime = "2022-12-21T19:14:06Z"
BuildTime = "2022-12-22T10:47:28Z"
// SemVer lists the (latest) git tag on the build.
SemVer = "v0.0.212"
SemVer = "v0.0.213"
// 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 = 212
Patch = 213
)
// Version returns a pretty printed version information string.