From 7898c65d69b64f202ed71783545107fd31497de1 Mon Sep 17 00:00:00 2001 From: David Vennik Date: Tue, 13 Dec 2022 09:47:08 +0100 Subject: [PATCH] Fixed copy function as receiver does not mutate... --- pkg/ifc/interfaces.go | 90 +--------------------- pkg/{ifc => slice}/interfaces_test.go | 14 +--- pkg/slice/slice.go | 95 +++++++++++++++++++++-- pkg/transport/sim.go | 8 +- pkg/transport/transport.go | 8 +- pkg/wire/onionskins.go | 105 +++++++++++++------------- version.go | 8 +- 7 files changed, 155 insertions(+), 173 deletions(-) rename pkg/{ifc => slice}/interfaces_test.go (83%) diff --git a/pkg/ifc/interfaces.go b/pkg/ifc/interfaces.go index 38acb725..b245b583 100644 --- a/pkg/ifc/interfaces.go +++ b/pkg/ifc/interfaces.go @@ -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 } diff --git a/pkg/ifc/interfaces_test.go b/pkg/slice/interfaces_test.go similarity index 83% rename from pkg/ifc/interfaces_test.go rename to pkg/slice/interfaces_test.go index c9bbbdaa..1763d344 100644 --- a/pkg/ifc/interfaces_test.go +++ b/pkg/slice/interfaces_test.go @@ -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) -} diff --git a/pkg/slice/slice.go b/pkg/slice/slice.go index 632a454e..8d4a8ee1 100644 --- a/pkg/slice/slice.go +++ b/pkg/slice/slice.go @@ -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 } diff --git a/pkg/transport/sim.go b/pkg/transport/sim.go index 8059b493..6b160c1d 100644 --- a/pkg/transport/sim.go +++ b/pkg/transport/sim.go @@ -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 } diff --git a/pkg/transport/transport.go b/pkg/transport/transport.go index 923f5a3c..bb26e122 100644 --- a/pkg/transport/transport.go +++ b/pkg/transport/transport.go @@ -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 } diff --git a/pkg/wire/onionskins.go b/pkg/wire/onionskins.go index dfe8f5dd..c583799f 100644 --- a/pkg/wire/onionskins.go +++ b/pkg/wire/onionskins.go @@ -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[:]) } diff --git a/version.go b/version.go index fe994328..cd88247a 100644 --- a/version.go +++ b/version.go @@ -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.