Stubbed in new Decode method in types.Onion
This commit is contained in:
15
pkg/types/types.go
Normal file
15
pkg/types/types.go
Normal 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
47
pkg/wire/cipher.go
Normal 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
51
pkg/wire/confirmation.go
Normal 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
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
package wire
|
||||
@@ -1 +0,0 @@
|
||||
package wire
|
||||
65
pkg/wire/exit.go
Normal file
65
pkg/wire/exit.go
Normal 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
43
pkg/wire/forward.go
Normal 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
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
package wire
|
||||
79
pkg/wire/message.go
Normal file
79
pkg/wire/message.go
Normal 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
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
package wire
|
||||
@@ -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[:])
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
package wire
|
||||
51
pkg/wire/purchase.go
Normal file
51
pkg/wire/purchase.go
Normal 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
37
pkg/wire/response.go
Normal 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
47
pkg/wire/return.go
Normal 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
55
pkg/wire/session.go
Normal 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
35
pkg/wire/token.go
Normal 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
|
||||
}
|
||||
10
version.go
10
version.go
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user