From 1b531539cdfc00ca29dbc25d86ad8e272052bb47 Mon Sep 17 00:00:00 2001 From: David Vennik Date: Mon, 19 Dec 2022 13:27:32 +0000 Subject: [PATCH] Added assemble function for OnionSkins and the necessary Insert function to enable it. --- pkg/wire/{onions_test.go => decode.go} | 0 pkg/wire/decode_test.go | 1 + pkg/wire/{onions.go => layers.go} | 56 +++--------- pkg/wire/layers_test.go | 1 + pkg/wire/onion.go | 1 + pkg/wire/onion_test.go | 1 + pkg/wire/onionskins.go | 120 ++++++++++++++++++------- version.go | 8 +- 8 files changed, 110 insertions(+), 78 deletions(-) rename pkg/wire/{onions_test.go => decode.go} (100%) create mode 100644 pkg/wire/decode_test.go rename pkg/wire/{onions.go => layers.go} (73%) create mode 100644 pkg/wire/layers_test.go create mode 100644 pkg/wire/onion.go create mode 100644 pkg/wire/onion_test.go diff --git a/pkg/wire/onions_test.go b/pkg/wire/decode.go similarity index 100% rename from pkg/wire/onions_test.go rename to pkg/wire/decode.go diff --git a/pkg/wire/decode_test.go b/pkg/wire/decode_test.go new file mode 100644 index 00000000..4048932c --- /dev/null +++ b/pkg/wire/decode_test.go @@ -0,0 +1 @@ +package wire diff --git a/pkg/wire/onions.go b/pkg/wire/layers.go similarity index 73% rename from pkg/wire/onions.go rename to pkg/wire/layers.go index 5df17acb..bb2c93ac 100644 --- a/pkg/wire/onions.go +++ b/pkg/wire/layers.go @@ -13,83 +13,55 @@ import ( type OnionSkins []Onion -func NewOnion() (o OnionSkins) { return o } - func (o OnionSkins) Message(to *address.Sender, from *prv.Key) OnionSkins { return append(o, &Message{To: to, From: from}) } - func (o OnionSkins) Forward(ip net.IP) OnionSkins { return append(o, &Forward{IP: ip}) } - func (o OnionSkins) Exit(port uint16, ciphers [3]sha256.Hash, payload slice.Bytes) OnionSkins { return append(o, &Exit{Port: port, Cipher: ciphers, Bytes: payload}) } - func (o OnionSkins) Return(ip net.IP, fwd, rtn pub.Key) OnionSkins { return append(o, &Return{IP: ip, Forward: fwd, Return: rtn}) } - func (o OnionSkins) Cipher(id nonce.ID, key pub.Key) OnionSkins { return append(o, &Cipher{ID: id, Key: key.ToBytes()}) } - func (o OnionSkins) Purchase(value uint64) OnionSkins { return append(o, &Purchase{Value: value}) } - func (o OnionSkins) Session(fwd, rtn pub.Key) OnionSkins { return append(o, &Session{ ForwardKey: fwd.ToBytes(), ReturnKey: rtn.ToBytes(), }) } - func (o OnionSkins) Acknowledgement(id nonce.ID) OnionSkins { return append(o, &Acknowledgement{ID: id}) } - func (o OnionSkins) Response(res slice.Bytes) OnionSkins { return append(o, Response(res)) } - func (o OnionSkins) Token(tok sha256.Hash) OnionSkins { return append(o, Token(tok)) } +// Assemble inserts the slice of Onion s inside each other so the first then +// 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) { - for i := range o { - oc := o[len(o)-i-1] - switch oc.(type) { - case *Message: - - case *Forward: - - case *Exit: - - case *Return: - - case *Cipher: - - case *Purchase: - - case *Session: - - case *Acknowledgement: - - case *Response: - - case *Token: - - } + // First item is the outer layer. + on = o[0] + // Iterate through the remaining layers. + for _, oc := range o[1:] { + on.Insert(oc) + // Next step we are inserting inside the one we just inserted. + on = oc } - return + // At the end, the first element contains references to every element + // inside it. + return o[0] } - -/* -func (o OnionSkins) () OnionSkins { - return append(o, &{}) -} -*/ diff --git a/pkg/wire/layers_test.go b/pkg/wire/layers_test.go new file mode 100644 index 00000000..4048932c --- /dev/null +++ b/pkg/wire/layers_test.go @@ -0,0 +1 @@ +package wire diff --git a/pkg/wire/onion.go b/pkg/wire/onion.go new file mode 100644 index 00000000..4048932c --- /dev/null +++ b/pkg/wire/onion.go @@ -0,0 +1 @@ +package wire diff --git a/pkg/wire/onion_test.go b/pkg/wire/onion_test.go new file mode 100644 index 00000000..4048932c --- /dev/null +++ b/pkg/wire/onion_test.go @@ -0,0 +1 @@ +package wire diff --git a/pkg/wire/onionskins.go b/pkg/wire/onionskins.go index 086acf69..faf3815e 100644 --- a/pkg/wire/onionskins.go +++ b/pkg/wire/onionskins.go @@ -20,15 +20,10 @@ var ( check = log.E.Chk ) -// MagicLen is 3 to make it nearly impossible that the wrong cipher will yield a +// MagicLen is 3 to make it infeasible that the wrong cipher will yield a // valid Magic string as listed below. const MagicLen = 3 -type Onion interface { - Encode(o slice.Bytes, c *slice.Cursor) - Len() int -} - var ( ForwardMagic = slice.Bytes("fwd") ExitMagic = slice.Bytes("exi") @@ -41,8 +36,18 @@ var ( TokenMagic = slice.Bytes("tok") ) +// 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. +// 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 @@ -51,11 +56,13 @@ type Message struct { Onion } -var _ Onion = &Message{} - const OnionHeaderLen = 4 + nonce.IVLen + address.Len + pub.KeyLen -func (on *Message) Len() int { return MagicLen + OnionHeaderLen + on.Onion.Len() } +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 @@ -97,7 +104,11 @@ type Forward struct { Onion } -func (fw *Forward) Len() int { return MagicLen + len(fw.IP) + 1 + fw.Onion.Len() } +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) @@ -122,15 +133,16 @@ type Exit struct { Cipher [3]sha256.Hash // Bytes are the message to be passed to the exit service. 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 - Return Onion + 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.Return.Len() + ex.Onion.Len() } func (ex *Exit) Encode(o slice.Bytes, c *slice.Cursor) { @@ -145,7 +157,7 @@ func (ex *Exit) Encode(o slice.Bytes, c *slice.Cursor) { 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.Return.Encode(o, c) + ex.Onion.Encode(o, c) } @@ -156,11 +168,21 @@ func (ex *Exit) Encode(o slice.Bytes, c *slice.Cursor) { type Return struct { // IP is the address of the next relay in the return leg of a circuit. net.IP - Forward, Return pub.Key + // The Key here should be the Return key matching the IP of the relay. + // The header provided in a previous Exit message uses the Forward key + // so that the Exit node cannot decrypt the header and discover the + // return path. + pub.Key Onion } -func (rt *Return) Len() int { return MagicLen + len(rt.IP) + 1 + rt.Onion.Len() } +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) @@ -175,17 +197,21 @@ func (rt *Return) Encode(o slice.Bytes, c *slice.Cursor) { // Purchase. type Cipher struct { nonce.ID - Key pub.Bytes - Forward Onion + Key pub.Bytes + Onion } -func (ci *Cipher) Len() int { return MagicLen + pub.KeyLen + ci.Forward.Len() } +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(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) + ci.Onion.Encode(o, c) } // Purchase is a message that is sent after first forwarding a Lighting payment @@ -199,12 +225,16 @@ func (ci *Cipher) Encode(o slice.Bytes, c *slice.Cursor) { // // Purchases have an ID created by the client. type Purchase struct { - Value uint64 - Return Onion + Value uint64 + 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.Return.Len() + return MagicLen + slice.Uint64Len + pr.Onion.Len() } func (pr *Purchase) Encode(o slice.Bytes, c *slice.Cursor) { @@ -212,7 +242,7 @@ func (pr *Purchase) Encode(o slice.Bytes, c *slice.Cursor) { value := slice.NewUint64() slice.EncodeUint64(value, pr.Value) copy(o[*c:c.Inc(slice.Uint64Len)], value) - pr.Return.Encode(o, c) + pr.Onion.Encode(o, c) } // Session is a message containing two public keys which identify to a relay the @@ -224,32 +254,53 @@ func (pr *Purchase) Encode(o slice.Bytes, c *slice.Cursor) { type Session struct { ForwardKey pub.Bytes ReturnKey pub.Bytes - Return + 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) { 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) + se.Onion.Encode(o, c) } +// The remaining methods are terminals, all constructed Onion structures +// should have one of these as the last element otherwise the second last call +// to Encode will panic with a nil. + // Acknowledgement messages just contain a nonce ID, these are used to terminate // ping and Cipher onion messages that confirm relaying was successful. type Acknowledgement struct { nonce.ID } -func (ak *Acknowledgement) Len() int { return MagicLen + nonce.IDLen } +var _ Onion = &Acknowledgement{} + +func (ak *Acknowledgement) Inner() Onion { return nil } +func (ak *Acknowledgement) Insert(_ Onion) {} +func (ak *Acknowledgement) Len() int { return MagicLen + nonce.IDLen } func (ak *Acknowledgement) Encode(o slice.Bytes, c *slice.Cursor) { copy(o[*c:c.Inc(MagicLen)], AcknowledgementMagic) copy(o[*c:c.Inc(pub.KeyLen)], ak.ID[:]) } +// Response messages are what are carried back via Return messages from an Exit. type Response slice.Bytes -func (rs Response) Len() int { return MagicLen + len(rs) + 4 } +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) @@ -259,9 +310,14 @@ func (rs Response) Encode(o slice.Bytes, c *slice.Cursor) { copy(o[*c:c.Inc(len(rs))], rs) } +// A Token is a 32 byte value. TODO: not sure we need this? type Token sha256.Hash -func (tk Token) Len() int { return MagicLen + sha256.Len } +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) diff --git a/version.go b/version.go index aa47c760..3cf3ce14 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 = "f11a3d7d23febb46ed83359c800a1a605b023b58" + ParentGitCommit = "feabf7f2666ba523f71a346b30611e00e0b639dd" // BuildTime stores the time when the current binary was built. - BuildTime = "2022-12-19T11:16:58Z" + BuildTime = "2022-12-19T13:27:32Z" // SemVer lists the (latest) git tag on the build. - SemVer = "v0.0.188" + SemVer = "v0.0.189" // 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 = 188 + Patch = 189 ) // Version returns a pretty printed version information string.