diff --git a/pkg/codec/onion/crypt/crypt.go b/pkg/codec/onion/crypt/crypt.go index 9da7a705..dc966279 100644 --- a/pkg/codec/onion/crypt/crypt.go +++ b/pkg/codec/onion/crypt/crypt.go @@ -1,6 +1,6 @@ // Package crypt is an onion message layer which specifies that subsequent content will be encrypted. // -// The cloaked receiver key, and the ephemeral per-message/per-packet "from" keys are intended to be single use only (generated via scalar multiplication with pairs of secrets). +// The cloaked receiver key, and the ephemeral per-message/per-packet "from" keys are intended to be single use only (generated via scalar sum with pairs of randomly generated secrets). // // todo: note reference of this algorithm. package crypt @@ -30,7 +30,7 @@ var ( ) const ( - CryptMagic = "cryp" + Magic = "cryp" ) // Crypt is an encrypted message, and forms the "skins" of the onions. @@ -75,6 +75,7 @@ func (x *Crypt) Account(res *sess.Data, sm *sess.Manager, s *sessions.Data, return } res.Sessions = append(res.Sessions, sd) + // The last hop needs no accounting as it's us! if last { res.Ret = sd.Header.Bytes @@ -86,19 +87,24 @@ func (x *Crypt) Account(res *sess.Data, sm *sess.Manager, s *sessions.Data, // Decode a splice.Splice's next bytes into a Crypt. func (x *Crypt) Decode(s *splice.Splice) (e error) { if e = magic.TooShort(s.Remaining(), consts.CryptLen-magic.Len, - CryptMagic); fails(e) { + Magic); fails(e) { return } - s.ReadIV(&x.IV).ReadCloak(&x.Cloak).ReadPubkey(&x.FromPub) + s. + ReadIV(&x.IV). + ReadCloak(&x.Cloak). + ReadPubkey(&x.FromPub) return } // Decrypt requires the prv.Pub to be located from the Cloak, using the FromPub // key to derive the shared secret, and then decrypts the rest of the message. func (x *Crypt) Decrypt(prk *crypto.Prv, s *splice.Splice) { - ciph.Encipher(ciph.GetBlock(prk, x.FromPub, "decrypt crypt header"), - x.IV, s.GetRest()) + + c := ciph.GetBlock(prk, x.FromPub) + + ciph.Encipher(c, x.IV, s.GetRest()) } // Encode a Crypt into a splice.Splice's next bytes. @@ -106,24 +112,31 @@ func (x *Crypt) Decrypt(prk *crypto.Prv, s *splice.Splice) { // The crypt renders the inner contents first and once complete returns and // encrypts everything after the Crypt header. func (x *Crypt) Encode(s *splice.Splice) (e error) { + log.T.F("encoding %s %s %x %x", reflect.TypeOf(x), x.ToHeaderPub, x.From.ToBytes(), x.IV, ) + if x.ToHeaderPub == nil || x.From == nil { s.Advance(consts.CryptLen, "crypt") return } - s.Magic(CryptMagic). - IV(x.IV).Cloak(x.ToHeaderPub).Pubkey(crypto.DerivePub(x.From)) + + s.Magic(Magic). + IV(x.IV). + Cloak(x.ToHeaderPub). + Pubkey(crypto.DerivePub(x.From)) + // Then we can encrypt the message segment var blk cipher.Block - if blk = ciph.GetBlock(x.From, x.ToHeaderPub, - "crypt header"); fails(e) { + if blk = ciph.GetBlock(x.From, x.ToHeaderPub); fails(e) { panic(e) } + start := s.GetCursor() end := s.Len() + switch { case x.Depth == 0: case x.Depth > 0: @@ -137,9 +150,9 @@ func (x *Crypt) Encode(s *splice.Splice) (e error) { } } ciph.Encipher(blk, x.IV, s.GetRange(start, end)) + if end != s.Len() { - if blk = ciph.GetBlock(x.From, x.ToPayloadPub, - "crypt payload"); fails(e) { + if blk = ciph.GetBlock(x.From, x.ToPayloadPub); fails(e) { return } ciph.Encipher(blk, x.IV, s.GetFrom(end)) @@ -184,7 +197,7 @@ func (x *Crypt) Len() int { } // Magic bytes that identify this message -func (x *Crypt) Magic() string { return CryptMagic } +func (x *Crypt) Magic() string { return Magic } // Wrap inserts an onion inside a Crypt. func (x *Crypt) Wrap(inner ont.Onion) { x.Onion = inner } @@ -205,4 +218,4 @@ func New(toHdr, toPld *crypto.Pub, from *crypto.Prv, iv nonce.IV, // Gen is a factory function to generate an Crypt. func Gen() codec.Codec { return &Crypt{} } -func init() { reg.Register(CryptMagic, Gen) } +func init() { reg.Register(Magic, Gen) } diff --git a/pkg/codec/onion/hidden/route/route.go b/pkg/codec/onion/hidden/route/route.go index 592282ce..c19e63f8 100644 --- a/pkg/codec/onion/hidden/route/route.go +++ b/pkg/codec/onion/hidden/route/route.go @@ -125,7 +125,7 @@ func (x *Route) Decode(s *splice.Splice) (e error) { // Decrypt decrypts the rest of a message after the Route segment if the // recipient has the hidden service private key. func (x *Route) Decrypt(prk *crypto.Prv, s *splice.Splice) { - ciph.Encipher(ciph.GetBlock(prk, x.SenderPub, "route decrypt"), x.IV, + ciph.Encipher(ciph.GetBlock(prk, x.SenderPub), x.IV, s.GetRest()) // And now we can see the reply field for the return trip. s.ReadID(&x.ID).ReadCiphers(&x.Ciphers).ReadNonces(&x.Nonces) @@ -149,7 +149,7 @@ func (x *Route) Encode(s *splice.Splice) (e error) { } var blk cipher.Block // Encrypt the message! - if blk = ciph.GetBlock(x.Sender, x.HiddenService, "route"); fails(e) { + if blk = ciph.GetBlock(x.Sender, x.HiddenService); fails(e) { return } ciph.Encipher(blk, x.IV, s.GetFrom(start)) @@ -197,11 +197,14 @@ func (x *Route) Handle(s *splice.Splice, p ont.Onion, ng ont.Ngin) (e error) { } rh := []ont.Onion{ reverse.New(addrs[0]), - crypt.New(rt.Sessions[0].Header.Pub, rt.Sessions[0].Payload.Pub, rt.Keys[0], rt.Nonces[0], 3), + crypt.New(rt.Sessions[0].Header.Pub, rt.Sessions[0].Payload.Pub, + rt.Keys[0], rt.Nonces[0], 3), reverse.New(addrs[1]), - crypt.New(rt.Sessions[1].Header.Pub, rt.Sessions[1].Payload.Pub, rt.Keys[1], rt.Nonces[1], 2), + crypt.New(rt.Sessions[1].Header.Pub, rt.Sessions[1].Payload.Pub, + rt.Keys[1], rt.Nonces[1], 2), reverse.New(addrs[2]), - crypt.New(rt.Sessions[2].Header.Pub, rt.Sessions[2].Payload.Pub, rt.Keys[2], rt.Nonces[2], 1), + crypt.New(rt.Sessions[2].Header.Pub, rt.Sessions[2].Payload.Pub, + rt.Keys[2], rt.Nonces[2], 1), } // .RoutingHeader(rt) rHdr := codec.Encode(ont.Assemble(rh)) @@ -216,9 +219,11 @@ func (x *Route) Handle(s *splice.Splice, p ont.Onion, ng ont.Ngin) (e error) { } mr := []ont.Onion{ forward.New(addrs[3]), - crypt.New(ss[3].Header.Pub, ss[3].Payload.Pub, ng.Keyset().Next(), n[3], 0), + crypt.New(ss[3].Header.Pub, ss[3].Payload.Pub, ng.Keyset().Next(), + n[3], 0), forward.New(addrs[4]), - crypt.New(ss[4].Header.Pub, ss[4].Payload.Pub, ng.Keyset().Next(), n[4], 0), + crypt.New(ss[4].Header.Pub, ss[4].Payload.Pub, ng.Keyset().Next(), + n[4], 0), ready.New(x.ID, x.HiddenService, x.RoutingHeaderBytes, hidden.GetRoutingHeaderFromCursor(rHdr), diff --git a/pkg/codec/onion/reverse/reverse.go b/pkg/codec/onion/reverse/reverse.go index cbab42e7..1c132688 100644 --- a/pkg/codec/onion/reverse/reverse.go +++ b/pkg/codec/onion/reverse/reverse.go @@ -93,7 +93,7 @@ func (x *Reverse) Handle(s *splice.Splice, p ont.Onion, ng ont.Ngin) (e error) { if e = in.Decode(s); fails(e) { return e } - if in.Magic() != crypt.CryptMagic { + if in.Magic() != crypt.Magic { return e } on := in.(*crypt.Crypt) @@ -111,7 +111,7 @@ func (x *Reverse) Handle(s *splice.Splice, p ont.Onion, ng ont.Ngin) (e error) { on.ToPriv = hdr // Decrypt using the Payload key and header nonce. c := s.GetCursor() - ciph.Encipher(ciph.GetBlock(on.ToPriv, on.FromPub, "reverse header"), + ciph.Encipher(ciph.GetBlock(on.ToPriv, on.FromPub), on.IV, s.GetRange(c, c+2*consts.ReverseCryptLen)) // shift the header segment upwards and pad the // remainder. @@ -119,7 +119,7 @@ func (x *Reverse) Handle(s *splice.Splice, p ont.Onion, ng ont.Ngin) (e error) { s.CopyRanges(first, second, second, last) s.CopyIntoRange(slice.NoisePad(consts.ReverseCryptLen), second, last) if last != s.Len() { - ciph.Encipher(ciph.GetBlock(pld, on.FromPub, "reverse payload"), + ciph.Encipher(ciph.GetBlock(pld, on.FromPub), on.IV, s.GetFrom(last)) } if string(s.GetRange(start, start+magic.Len)) != Magic { diff --git a/pkg/crypto/ciph/cipher.go b/pkg/crypto/ciph/cipher.go index ffe59453..3522f48e 100644 --- a/pkg/crypto/ciph/cipher.go +++ b/pkg/crypto/ciph/cipher.go @@ -20,6 +20,7 @@ var ( // BlockFromHash creates an AES block cipher from an sha256.Hash. func BlockFromHash(h sha256.Hash) (block cipher.Block) { + // We can ignore the error because sha256.Hash is a valid key size. block, _ = aes.NewCipher(h[:]) return @@ -29,20 +30,23 @@ func BlockFromHash(h sha256.Hash) (block cipher.Block) { // and decrypts encrypted data. If the cipher.Block is nil, it panics (this // should never happen). func Encipher(blk cipher.Block, n nonce.IV, b []byte) { + if blk == nil { panic("Encipher called without a block cipher provided") + } else { cipher.NewCTR(blk, n[:]).XORKeyStream(b, b) + } } // GetBlock returns a block cipher with a secret generated from the provided // keys using ECDH. -func GetBlock(from *crypto.Prv, to *crypto.Pub, note string) (block cipher.Block) { +func GetBlock(from *crypto.Prv, to *crypto.Pub) (block cipher.Block) { + secret := crypto.ComputeSharedSecret(from, to) - // fb := from.ToBytes() - // log.T.Ln(note, "secret", color.Red.Sprint(enc(secret[:])[:52]), "<-", - // color.Blue.Sprint(enc(fb[:])[:52]), "+", to.ToBased32()) + block, _ = aes.NewCipher(secret[:]) + return } diff --git a/pkg/engine/packet/packet.go b/pkg/engine/packet/packet.go index ae4d17b0..d00cc66d 100644 --- a/pkg/engine/packet/packet.go +++ b/pkg/engine/packet/packet.go @@ -126,7 +126,7 @@ func DecodePacket(d []byte, from *crypto.Pub, to *crypto.Prv, c := new(slice.Cursor) copy(iv[:], d[c.Inc(4+crypto.PubKeyLen+crypto.CloakLen):c.Inc(nonce.IVLen)]) var blk cipher.Block - if blk = ciph.GetBlock(to, from, "packet decode"); fails(e) { + if blk = ciph.GetBlock(to, from); fails(e) { return } // This decrypts the rest of the packet. @@ -151,7 +151,7 @@ func DecodePacket(d []byte, from *crypto.Pub, to *crypto.Prv, // signature to the end. func EncodePacket(ep *PacketParams) (pkt []byte, e error) { var blk cipher.Block - if blk = ciph.GetBlock(ep.From, ep.To, "packet encode"); fails(e) { + if blk = ciph.GetBlock(ep.From, ep.To); fails(e) { return } nonc := nonce.New() @@ -191,7 +191,8 @@ func EncodePacket(ep *PacketParams) (pkt []byte, e error) { // found, it is combined with the public key to generate the cipher and the // entire packet should then be decrypted, and the DecodePacket function will // then decode a OnionSkin. -func GetKeysFromPacket(d []byte) (from *crypto.Pub, to crypto.CloakedPubKey, iv nonce.IV, +func GetKeysFromPacket(d []byte) (from *crypto.Pub, to crypto.CloakedPubKey, + iv nonce.IV, e error) { pktLen := len(d) if pktLen < Overhead { diff --git a/pkg/util/splice/splice.go b/pkg/util/splice/splice.go index 5b678073..3ebf9b7f 100644 --- a/pkg/util/splice/splice.go +++ b/pkg/util/splice/splice.go @@ -349,6 +349,38 @@ func (s *Splice) Bytes(b []byte) *Splice { return s } +const MaxUint24 = 1<<24 - 1 + +// Offset writes a 24 bit value into the Splice. Used with the Crypt payload +// cipher offset. A value over MaxUint24 is not recoverable so it will panic. +func (s *Splice) Offset(o uint32) *Splice { + + // This offset value being over MaxUint24 is a programmer error. + if o > MaxUint24 { + panic(fmt.Sprintf("payload cipher offset cannot be over %d", MaxUint24)) + } + index := s.GetCursor() + + // little endian, smallest byte first, bigger bytes after. + s.b[index] = byte(o) + s.b[index+1] = byte(o >> 8) + s.b[index+2] = byte(o >> 16) + s.Advance(3, "payload cipher offset") + return s +} + +// ReadOffset decodes the offset and fills the integer with the uint24 value. +func (s *Splice) ReadOffset(o *int) *Splice { + + index := s.GetCursor() + // little endian, smallest byte first, bigger bytes after. + *o = int(s.b[index]) + *o += int(s.b[index+1]) << 8 + *o += int(s.b[index+2]) << 16 + s.Advance(3, "payload cipher offset") + return s +} + func (s *Splice) ReadBytes(b *slice.Bytes) *Splice { bytesLen := slice.DecodeUint32(s.b[*s.c:s.c.Inc(slice.Uint32Len)]) s.Segments = append(s.Segments, diff --git a/version.go b/version.go index 36cd5fb0..c8fa2946 100644 --- a/version.go +++ b/version.go @@ -19,11 +19,11 @@ const ( // URL is the git URL for the repository. URL = "git.indra-labs.org/dev/ind" // GitRef is the gitref, as in refs/heads/branchname. - GitRef = "refs/heads/peerstore" + GitRef = "refs/heads/15" // ParentGitCommit is the commit hash of the parent HEAD. - ParentGitCommit = "ccc276f67b42f2eaeeb10d8274d677749ffc7436" + ParentGitCommit = "742a44445cc6862042a76fdb02e64236fd4340bb" // BuildTime stores the time when the current binary was built. - BuildTime = "2023-07-23T21:23:39+01:00" + BuildTime = "2023-08-09T21:04:58+01:00" // SemVer lists the (latest) git tag on the release. SemVer = "v0.1.20" // Major is the major number from the tag.