Fixed copy function as receiver does not mutate...

This commit is contained in:
David Vennik
2022-12-13 09:47:08 +01:00
parent aeb44fe48b
commit 7898c65d69
7 changed files with 155 additions and 173 deletions

View File

@@ -1,9 +1,6 @@
package ifc
import (
"reflect"
"unsafe"
"github.com/Indra-Labs/indra"
"github.com/Indra-Labs/indra/pkg/slice"
log2 "github.com/cybriq/proc/pkg/log"
@@ -15,89 +12,6 @@ var (
)
type Transport interface {
Send(b Bytes)
Receive() <-chan Bytes
}
type Bytes []byte
func ToBytes(b []byte) (msg Bytes) { return b }
func (m Bytes) ToBytes() []byte { return m }
func (m Bytes) Len() int { return len(m) }
func (m Bytes) Copy(start, end *slice.Cursor, bytes Bytes) {
copy(m[*start:*end], bytes[:*end-*start])
}
type U64Slice []uint64
func (u U64Slice) Copy() (o U64Slice) {
o = make(U64Slice, len(u))
copy(o, u)
return
}
// ToU64Slice converts the message with zero allocations if the slice capacity
// was already 8 plus the modulus of the length and 8, otherwise this function
// will trigger a stack allocation, or heap, if the bytes are very long. This is
// intended to be used with short byte slices like cipher nonces and hashes, so
// it usually won't trigger allocations off stack and very often won't trigger
// a copy on stack, saving a lot of time in a short, oft repeated operations.
func (m Bytes) ToU64Slice() (u U64Slice) {
mLen := uint64(len(m))
uLen := int(mLen / 8)
mMod := mLen % 8
if mMod != 0 {
uLen++
}
// Either extend if there is capacity or this will trigger a copy
for i := uint64(0); i < 8-mMod+8; i++ {
// We could use make with mMod+8 length to extend and ... but
// this does the same thing in practice.
m = append(m, 0)
}
u = make([]uint64, 0, 0)
// With the slice now long enough to be safely converted to []uint64
// plus an extra uint64 to store the original length we can coerce the
// type using unsafe.
//
// First we convert our empty []uint64 header
header := (*reflect.SliceHeader)(unsafe.Pointer(&u))
// then we point its memory location to the extended byte slice data
header.Data = (*reflect.SliceHeader)(unsafe.Pointer(&m)).Data
// Update the element length and capacity
header.Len = uLen
header.Cap = uLen
// store the original byte length
u = append(u, mLen)
return
}
// XOR the U64Slice with the provided slice. Panics if slices are different
// length. The receiver value is mutated in this operation.
func (u U64Slice) XOR(v U64Slice) {
// This should only trigger if the programmer is not XORing same size.
if u[len(u)-1] != v[len(v)-1] {
panic("programmer error, trying to XOR slices of different size")
}
for i := range u[:len(u)-1] {
u[i] ^= v[i]
}
}
func (u U64Slice) ToMessage() (m Bytes) {
// length is encoded into the last element
mLen := int(u[len(u)-1])
m = make(Bytes, 0, 0)
// With the slice now long enough to be safely converted to []uint64
// plus an extra uint64 to store the original length we can coerce the
// type using unsafe.
//
// First we convert our empty []uint64 header
header := (*reflect.SliceHeader)(unsafe.Pointer(&m))
// then we point its memory location to the extended byte slice data
header.Data = (*reflect.SliceHeader)(unsafe.Pointer(&u)).Data
// lastly, change the element length
header.Len = mLen
header.Cap = mLen
return m
Send(b slice.Bytes)
Receive() <-chan slice.Bytes
}

View File

@@ -1,4 +1,4 @@
package ifc
package slice
import (
"bytes"
@@ -56,15 +56,3 @@ func TestU64Slice_XOR(t *testing.T) {
t.FailNow()
}
}
func TestBytes_Copy(t *testing.T) {
var e error
var msg Bytes
if msg, _, e = testutils.GenerateTestMessage(33); check(e) {
t.Error(e)
t.FailNow()
}
buf := make(Bytes, 65)
buf.Copy(10, 20, msg)
log.I.S(msg, buf)
}

View File

@@ -7,9 +7,10 @@ package slice
import (
"crypto/rand"
"encoding/binary"
"reflect"
"unsafe"
"github.com/Indra-Labs/indra"
"github.com/Indra-Labs/indra/pkg/ifc"
"github.com/Indra-Labs/indra/pkg/sha256"
log2 "github.com/cybriq/proc/pkg/log"
)
@@ -104,10 +105,10 @@ const Uint32Len = 4
const Uint24Len = 3
const Uint16Len = 2
func NewUint64() ifc.Bytes { return make(ifc.Bytes, Uint64Len) }
func NewUint32() ifc.Bytes { return make(ifc.Bytes, Uint32Len) }
func NewUint24() ifc.Bytes { return make(ifc.Bytes, Uint24Len) }
func NewUint16() ifc.Bytes { return make(ifc.Bytes, Uint16Len) }
func NewUint64() Bytes { return make(Bytes, Uint64Len) }
func NewUint32() Bytes { return make(Bytes, Uint32Len) }
func NewUint24() Bytes { return make(Bytes, Uint24Len) }
func NewUint16() Bytes { return make(Bytes, Uint16Len) }
func NoisePad(l int) (noise []byte) {
var seed sha256.Hash
@@ -132,7 +133,87 @@ func NoisePad(l int) (noise []byte) {
type Cursor int
func (c *Cursor) Inc(v int) *Cursor {
func (c *Cursor) Inc(v int) Cursor {
*c += Cursor(v)
return c
return *c
}
type Bytes []byte
func ToBytes(b []byte) (msg Bytes) { return b }
func (m Bytes) ToBytes() []byte { return m }
func (m Bytes) Len() int { return len(m) }
type U64Slice []uint64
func (u U64Slice) Copy() (o U64Slice) {
o = make(U64Slice, len(u))
copy(o, u)
return
}
// ToU64Slice converts the message with zero allocations if the slice capacity
// was already 8 plus the modulus of the length and 8, otherwise this function
// will trigger a stack allocation, or heap, if the bytes are very long. This is
// intended to be used with short byte slices like cipher nonces and hashes, so
// it usually won't trigger allocations off stack and very often won't trigger
// a copy on stack, saving a lot of time in a short, oft repeated operations.
func (m Bytes) ToU64Slice() (u U64Slice) {
mLen := uint64(len(m))
uLen := int(mLen / 8)
mMod := mLen % 8
if mMod != 0 {
uLen++
}
// Either extend if there is capacity or this will trigger a copy
for i := uint64(0); i < 8-mMod+8; i++ {
// We could use make with mMod+8 length to extend and ... but
// this does the same thing in practice.
m = append(m, 0)
}
u = make([]uint64, 0, 0)
// With the slice now long enough to be safely converted to []uint64
// plus an extra uint64 to store the original length we can coerce the
// type using unsafe.
//
// First we convert our empty []uint64 header
header := (*reflect.SliceHeader)(unsafe.Pointer(&u))
// then we point its memory location to the extended byte slice data
header.Data = (*reflect.SliceHeader)(unsafe.Pointer(&m)).Data
// Update the element length and capacity
header.Len = uLen
header.Cap = uLen
// store the original byte length
u = append(u, mLen)
return
}
// XOR the U64Slice with the provided slice. Panics if slices are different
// length. The receiver value is mutated in this operation.
func (u U64Slice) XOR(v U64Slice) {
// This should only trigger if the programmer is not XORing same size.
if u[len(u)-1] != v[len(v)-1] {
panic("programmer error, trying to XOR slices of different size")
}
for i := range u[:len(u)-1] {
u[i] ^= v[i]
}
}
func (u U64Slice) ToMessage() (m Bytes) {
// length is encoded into the last element
mLen := int(u[len(u)-1])
m = make(Bytes, 0, 0)
// With the slice now long enough to be safely converted to []uint64
// plus an extra uint64 to store the original length we can coerce the
// type using unsafe.
//
// First we convert our empty []uint64 header
header := (*reflect.SliceHeader)(unsafe.Pointer(&m))
// then we point its memory location to the extended byte slice data
header.Data = (*reflect.SliceHeader)(unsafe.Pointer(&u)).Data
// lastly, change the element length
header.Len = mLen
header.Cap = mLen
return m
}

View File

@@ -1,16 +1,16 @@
package transport
import (
"github.com/Indra-Labs/indra/pkg/ifc"
"github.com/Indra-Labs/indra/pkg/slice"
)
type Sim chan ifc.Bytes
type Sim chan slice.Bytes
func (d Sim) Send(b ifc.Bytes) {
func (d Sim) Send(b slice.Bytes) {
d <- b
}
func (d Sim) Receive() <-chan ifc.Bytes {
func (d Sim) Receive() <-chan slice.Bytes {
return d
}

View File

@@ -1,16 +1,16 @@
package transport
import (
"github.com/Indra-Labs/indra/pkg/ifc"
"github.com/Indra-Labs/indra/pkg/slice"
)
type Dispatcher chan ifc.Bytes
type Dispatcher chan slice.Bytes
func (d Dispatcher) Send(b ifc.Bytes) {
func (d Dispatcher) Send(b slice.Bytes) {
d <- b
}
func (d Dispatcher) Receive() <-chan ifc.Bytes {
func (d Dispatcher) Receive() <-chan slice.Bytes {
return d
}

View File

@@ -6,7 +6,6 @@ import (
"github.com/Indra-Labs/indra"
"github.com/Indra-Labs/indra/pkg/ciph"
"github.com/Indra-Labs/indra/pkg/ifc"
"github.com/Indra-Labs/indra/pkg/key/address"
"github.com/Indra-Labs/indra/pkg/key/prv"
"github.com/Indra-Labs/indra/pkg/key/pub"
@@ -26,20 +25,20 @@ var (
const MagicLen = 3
type Onion interface {
Encode(o ifc.Bytes, c *slice.Cursor)
Encode(o slice.Bytes, c *slice.Cursor)
Len() int
}
var (
ForwardMagic = ifc.Bytes("fwd")
ExitMagic = ifc.Bytes("exi")
ReturnMagic = ifc.Bytes("rtn")
CipherMagic = ifc.Bytes("cif")
PurchaseMagic = ifc.Bytes("prc")
SessionMagic = ifc.Bytes("ses")
AcknowledgementMagic = ifc.Bytes("ack")
ResponseMagic = ifc.Bytes("res")
TokenMagic = ifc.Bytes("tok")
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")
)
// Message is the generic top level wrapper for an Onion. All following messages
@@ -48,7 +47,7 @@ type Message struct {
To *address.Sender
From *prv.Key
// The following field is only populated in the outermost layer.
ifc.Bytes
slice.Bytes
Onion
}
@@ -86,7 +85,7 @@ type Message struct {
// the layers for their length values.
func NewOnion(msg *Message) (o Onion, c *slice.Cursor) {
c = new(slice.Cursor)
msg.Bytes = make(ifc.Bytes, msg.Len())
msg.Bytes = make(slice.Bytes, msg.Len())
return msg, c
}
@@ -94,7 +93,7 @@ const OnionHeaderLen = 4 + nonce.IVLen + address.Len + pub.KeyLen
func (on *Message) Len() int { return MagicLen + OnionHeaderLen + on.Onion.Len() }
func (on *Message) Encode(o ifc.Bytes, c *slice.Cursor) {
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.
@@ -106,10 +105,10 @@ func (on *Message) Encode(o ifc.Bytes, c *slice.Cursor) {
checkEnd := checkStart + 4
// Generate a new nonce and copy it in.
n := nonce.New()
o.Copy(c.Inc(4), c.Inc(nonce.IVLen), n[:])
copy(o[c.Inc(4):c.Inc(nonce.IVLen)], n[:])
// Derive the cloaked key and copy it in.
to := on.To.GetCloak()
o.Copy(c, c.Inc(address.Len), to[:])
copy(o[*c:c.Inc(address.Len)], to[:])
// Call the tree of onions to perform their encoding.
on.Onion.Encode(o, c)
// Then we can encrypt the message segment
@@ -136,10 +135,10 @@ type Forward struct {
func (fw *Forward) Len() int { return MagicLen + len(fw.IP) + 1 + fw.Onion.Len() }
func (fw *Forward) Encode(o ifc.Bytes, c *slice.Cursor) {
o.Copy(c, c.Inc(MagicLen), ForwardMagic)
func (fw *Forward) Encode(o slice.Bytes, c *slice.Cursor) {
copy(o[*c:c.Inc(MagicLen)], ForwardMagic)
o[*c] = byte(len(fw.IP))
o.Copy(c.Inc(1), c.Inc(len(fw.IP)), ifc.Bytes(fw.IP))
copy(o[c.Inc(1):c.Inc(len(fw.IP))], fw.IP)
fw.Onion.Encode(o, c)
}
@@ -158,7 +157,7 @@ type Exit struct {
// given order over the reply message from the service.
Cipher [3]sha256.Hash
// Bytes are the message to be passed to the exit service.
ifc.Bytes
slice.Bytes
// Return is the encoded message with the three hops using the Return
// keys for the relevant relays, encrypted progressively. Note that this
// message uses a different Cipher than the one above
@@ -170,18 +169,18 @@ func (ex *Exit) Len() int {
ex.Return.Len()
}
func (ex *Exit) Encode(o ifc.Bytes, c *slice.Cursor) {
o.Copy(c, c.Inc(MagicLen), ExitMagic)
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))
o.Copy(c, c.Inc(slice.Uint16Len), port)
o.Copy(c, c.Inc(sha256.Len), ex.Cipher[0][:])
o.Copy(c, c.Inc(sha256.Len), ex.Cipher[1][:])
o.Copy(c, c.Inc(sha256.Len), ex.Cipher[1][:])
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))
o.Copy(c, c.Inc(slice.Uint32Len), bytesLen)
o.Copy(c, c.Inc(len(ex.Bytes)), ex.Bytes)
copy(o[*c:c.Inc(slice.Uint32Len)], bytesLen)
copy(o[*c:c.Inc(len(ex.Bytes))], ex.Bytes)
ex.Return.Encode(o, c)
}
@@ -198,10 +197,10 @@ type Return struct {
func (rt *Return) Len() int { return MagicLen + len(rt.IP) + 1 + rt.Onion.Len() }
func (rt *Return) Encode(o ifc.Bytes, c *slice.Cursor) {
o.Copy(c, c.Inc(MagicLen), ReturnMagic)
func (rt *Return) Encode(o slice.Bytes, c *slice.Cursor) {
copy(o[*c:c.Inc(MagicLen)], ReturnMagic)
o[*c] = byte(len(rt.IP))
o.Copy(c.Inc(1), c.Inc(len(rt.IP)), ifc.Bytes(rt.IP))
copy(o[c.Inc(1):c.Inc(len(rt.IP))], rt.IP)
rt.Onion.Encode(o, c)
}
@@ -217,10 +216,10 @@ type Cipher struct {
func (ci *Cipher) Len() int { return MagicLen + pub.KeyLen + ci.Forward.Len() }
func (ci *Cipher) Encode(o ifc.Bytes, c *slice.Cursor) {
o.Copy(c, c.Inc(nonce.IDLen), ci.ID[:])
o.Copy(c, c.Inc(MagicLen), CipherMagic)
o.Copy(c.Inc(1), c.Inc(sha256.Len), ci.Key[:])
func (ci *Cipher) Encode(o slice.Bytes, c *slice.Cursor) {
copy(o[*c:c.Inc(nonce.IDLen)], ci.ID[:])
copy(o[*c:c.Inc(MagicLen)], CipherMagic)
copy(o[c.Inc(1):c.Inc(sha256.Len)], ci.Key[:])
ci.Forward.Encode(o, c)
}
@@ -243,11 +242,11 @@ func (pr *Purchase) Len() int {
return MagicLen + slice.Uint64Len + pr.Return.Len()
}
func (pr *Purchase) Encode(o ifc.Bytes, c *slice.Cursor) {
o.Copy(c, c.Inc(MagicLen), PurchaseMagic)
func (pr *Purchase) Encode(o slice.Bytes, c *slice.Cursor) {
copy(o[*c:c.Inc(MagicLen)], PurchaseMagic)
value := slice.NewUint64()
slice.EncodeUint64(value, pr.Value)
o.Copy(c, c.Inc(slice.Uint64Len), value)
copy(o[*c:c.Inc(slice.Uint64Len)], value)
pr.Return.Encode(o, c)
}
@@ -263,10 +262,10 @@ type Session struct {
Return
}
func (se *Session) Encode(o ifc.Bytes, c *slice.Cursor) {
o.Copy(c, c.Inc(MagicLen), SessionMagic)
o.Copy(c, c.Inc(pub.KeyLen), se.ForwardKey[:])
o.Copy(c, c.Inc(pub.KeyLen), se.ReturnKey[:])
func (se *Session) Encode(o slice.Bytes, c *slice.Cursor) {
copy(o[*c:c.Inc(MagicLen)], SessionMagic)
copy(o[*c:c.Inc(pub.KeyLen)], se.ForwardKey[:])
copy(o[*c:c.Inc(pub.KeyLen)], se.ReturnKey[:])
se.Return.Encode(o, c)
}
@@ -278,28 +277,28 @@ type Acknowledgement struct {
func (ak *Acknowledgement) Len() int { return MagicLen + nonce.IDLen }
func (ak *Acknowledgement) Encode(o ifc.Bytes, c *slice.Cursor) {
o.Copy(c, c.Inc(MagicLen), AcknowledgementMagic)
o.Copy(c, c.Inc(pub.KeyLen), ak.ID[:])
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[:])
}
type Response ifc.Bytes
type Response slice.Bytes
func (rs Response) Len() int { return MagicLen + len(rs) + 4 }
func (rs Response) Encode(o ifc.Bytes, c *slice.Cursor) {
o.Copy(c, c.Inc(MagicLen), ResponseMagic)
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))
o.Copy(c, c.Inc(slice.Uint32Len), bytesLen)
o.Copy(c, c.Inc(len(rs)), ifc.Bytes(rs))
copy(o[*c:c.Inc(slice.Uint32Len)], bytesLen)
copy(o[*c:c.Inc(len(rs))], rs)
}
type Token sha256.Hash
func (tk Token) Len() int { return MagicLen + sha256.Len }
func (tk Token) Encode(o ifc.Bytes, c *slice.Cursor) {
o.Copy(c, c.Inc(MagicLen), TokenMagic)
o.Copy(c, c.Inc(sha256.Len), tk[:])
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

@@ -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 = "af876fa30c7b1c97ffe9c3e13e213bbdc7275460"
ParentGitCommit = "e9bb22630f78c0706079c2f8761ca9a2630bc3e2"
// BuildTime stores the time when the current binary was built.
BuildTime = "2022-12-13T09:23:51+01:00"
BuildTime = "2022-12-13T09:47:08+01:00"
// SemVer lists the (latest) git tag on the build.
SemVer = "v0.0.183"
SemVer = "v0.0.184"
// 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 = 183
Patch = 184
)
// Version returns a pretty printed version information string.