Added unsafe []byte to []uint64 conversion and XOR
This commit is contained in:
@@ -5,6 +5,8 @@ import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
|
||||
"github.com/Indra-Labs/indra"
|
||||
"github.com/Indra-Labs/indra/pkg/key/ecdh"
|
||||
@@ -38,7 +40,7 @@ func Encipher(blk cipher.Block, n nonce.IV, b []byte) {
|
||||
|
||||
const SecretLength = 32
|
||||
|
||||
func CombineCiphers(secrets [][]byte) (combined []byte, e error) {
|
||||
func CombineCiphers(secrets ...[]byte) (combined []byte, e error) {
|
||||
// All ciphers must be the same length, 32 bytes for AES-CTR 256
|
||||
for i := range secrets {
|
||||
if len(secrets[i]) != SecretLength {
|
||||
@@ -55,3 +57,50 @@ func CombineCiphers(secrets [][]byte) (combined []byte, e error) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type CipherKeys struct {
|
||||
From *prv.Key
|
||||
To *pub.Key
|
||||
}
|
||||
|
||||
func GenerateCompositeCipher(ck ...*CipherKeys) (secret []byte, e error) {
|
||||
// var secret64 []uint64
|
||||
var secrets [][]byte
|
||||
for i := range ck {
|
||||
var sec []byte
|
||||
sec = ecdh.Compute(ck[i].From, ck[i].To)
|
||||
secrets = append(secrets, sec)
|
||||
// sec64 := touint64(sec)
|
||||
// if i == 0 {
|
||||
// secret64 = sec64
|
||||
// } else {
|
||||
// xor64(secret64, sec64)
|
||||
// }
|
||||
}
|
||||
return CombineCiphers(secrets...)
|
||||
}
|
||||
|
||||
// touint64 assumes len(x)%8 == 0
|
||||
func touint64(x []byte) []uint64 {
|
||||
xx := make([]uint64, 0, 0)
|
||||
hdrp := (*reflect.SliceHeader)(unsafe.Pointer(&xx))
|
||||
hdrp.Data = (*reflect.SliceHeader)(unsafe.Pointer(&x)).Data
|
||||
hdrp.Len = len(x) / 8
|
||||
hdrp.Cap = len(x) / 8
|
||||
return xx
|
||||
}
|
||||
|
||||
func tobytes(x []uint64) []byte {
|
||||
xx := make([]byte, 0, 0)
|
||||
hdrp := (*reflect.SliceHeader)(unsafe.Pointer(&xx))
|
||||
hdrp.Data = (*reflect.SliceHeader)(unsafe.Pointer(&x)).Data
|
||||
hdrp.Len = len(x) * 8
|
||||
hdrp.Cap = len(x) * 8
|
||||
return xx
|
||||
}
|
||||
|
||||
func xor64(a, b []uint64) {
|
||||
for i := range a {
|
||||
a[i] ^= b[i]
|
||||
}
|
||||
}
|
||||
|
||||
156
pkg/ciph/cipher_test.go
Normal file
156
pkg/ciph/cipher_test.go
Normal file
@@ -0,0 +1,156 @@
|
||||
package ciph
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"testing"
|
||||
|
||||
"github.com/Indra-Labs/indra/pkg/key/ecdh"
|
||||
"github.com/Indra-Labs/indra/pkg/key/prv"
|
||||
"github.com/Indra-Labs/indra/pkg/key/pub"
|
||||
nonce2 "github.com/Indra-Labs/indra/pkg/nonce"
|
||||
"github.com/Indra-Labs/indra/pkg/sha256"
|
||||
"github.com/Indra-Labs/indra/pkg/testutils"
|
||||
)
|
||||
|
||||
func TestGenerateCompositeCipher(t *testing.T) {
|
||||
var cifkeys []*CipherKeys
|
||||
var e error
|
||||
for i := 0; i < 3; i++ {
|
||||
var p1, p2 *prv.Key
|
||||
if p1, e = prv.GenerateKey(); check(e) {
|
||||
t.Error(e)
|
||||
t.FailNow()
|
||||
}
|
||||
pub1 := pub.Derive(p1)
|
||||
if p2, e = prv.GenerateKey(); check(e) {
|
||||
t.Error(e)
|
||||
t.FailNow()
|
||||
}
|
||||
cifkeys = append(cifkeys, &CipherKeys{p2, pub1})
|
||||
}
|
||||
var m, original []byte
|
||||
m, _, e = testutils.GenerateTestMessage(1024)
|
||||
log.I.S(sha256.Single(m))
|
||||
original = make([]byte, len(m))
|
||||
copy(original, m)
|
||||
var compositeSecret []byte
|
||||
if compositeSecret, e =
|
||||
GenerateCompositeCipher(cifkeys...); check(e) {
|
||||
|
||||
t.Error(e)
|
||||
t.FailNow()
|
||||
}
|
||||
var block cipher.Block
|
||||
if block, e = aes.NewCipher(compositeSecret); check(e) {
|
||||
t.Error(e)
|
||||
t.FailNow()
|
||||
}
|
||||
nonce := nonce2.New()
|
||||
Encipher(block, nonce, m)
|
||||
log.I.S(sha256.Single(m))
|
||||
// for i := range cifkeys {
|
||||
// var blk cipher.Block
|
||||
// if blk, e = GetBlock(cifkeys[i].From,
|
||||
// cifkeys[i].To); check(e) {
|
||||
// t.Error(e)
|
||||
// t.FailNow()
|
||||
// }
|
||||
// Encipher(blk, nonce, m)
|
||||
// }
|
||||
// log.I.S(sha256.Single(m), sha256.Single(original))
|
||||
for i := range cifkeys {
|
||||
var blk cipher.Block
|
||||
if blk, e = GetBlock(cifkeys[i].From,
|
||||
cifkeys[i].To); check(e) {
|
||||
t.Error(e)
|
||||
t.FailNow()
|
||||
}
|
||||
Encipher(blk, nonce, original)
|
||||
}
|
||||
log.I.S(sha256.Single(original))
|
||||
|
||||
}
|
||||
func TestCompositing(t *testing.T) {
|
||||
var cifkeys []*CipherKeys
|
||||
var e error
|
||||
for i := 0; i < 3; i++ {
|
||||
var p1, p2 *prv.Key
|
||||
if p1, e = prv.GenerateKey(); check(e) {
|
||||
t.Error(e)
|
||||
t.FailNow()
|
||||
}
|
||||
pub1 := pub.Derive(p1)
|
||||
if p2, e = prv.GenerateKey(); check(e) {
|
||||
t.Error(e)
|
||||
t.FailNow()
|
||||
}
|
||||
cifkeys = append(cifkeys, &CipherKeys{p2, pub1})
|
||||
}
|
||||
var secrets [][]byte
|
||||
for i := range cifkeys {
|
||||
secrets = append(secrets, ecdh.Compute(cifkeys[i].
|
||||
From, cifkeys[i].To))
|
||||
}
|
||||
log.I.S(secrets)
|
||||
var cs []byte
|
||||
if cs, e = CombineCiphers(secrets...); check(e) {
|
||||
t.Error(e)
|
||||
t.FailNow()
|
||||
}
|
||||
log.I.S(cs)
|
||||
|
||||
// this proves we can retreive the third cipher if we know
|
||||
// the first two
|
||||
for _, i := range secrets[:2] {
|
||||
for j := range cs {
|
||||
cs[j] ^= i[j]
|
||||
}
|
||||
}
|
||||
log.I.S(cs, secrets[2])
|
||||
|
||||
testWords := []byte("this is a test")
|
||||
testBytes1 := make([]byte, len(testWords))
|
||||
copy(testBytes1, testWords)
|
||||
testBytes2 := make([]byte, len(testWords))
|
||||
copy(testBytes2, testWords)
|
||||
log.I.S(testWords, testBytes1, testBytes2)
|
||||
|
||||
// regenerate the combined cipher
|
||||
if cs, e = CombineCiphers(secrets...); check(e) {
|
||||
t.Error(e)
|
||||
t.FailNow()
|
||||
}
|
||||
var nonces []nonce2.IV
|
||||
for i := 0; i < 3; i++ {
|
||||
nonces = append(nonces, nonce2.New())
|
||||
}
|
||||
log.I.S(nonces)
|
||||
n := nonce2.New()
|
||||
tmp := make(nonce2.IV, nonce2.IVLen)
|
||||
copy(tmp, nonces[0])
|
||||
nt := touint64(tmp)
|
||||
for i := range nonces {
|
||||
if i != 0 {
|
||||
u64 := touint64(nonces[i])
|
||||
xor64(nt, u64)
|
||||
}
|
||||
}
|
||||
n = tobytes(nt)
|
||||
log.I.S(n)
|
||||
var cb cipher.Block
|
||||
if cb, e = aes.NewCipher(cs); !check(e) {
|
||||
}
|
||||
Encipher(cb, n, testBytes1)
|
||||
log.I.S(testBytes1)
|
||||
for i := range cifkeys {
|
||||
var blk cipher.Block
|
||||
if blk, e = GetBlock(cifkeys[i].From,
|
||||
cifkeys[i].To); check(e) {
|
||||
t.Error(e)
|
||||
t.FailNow()
|
||||
}
|
||||
Encipher(blk, nonces[i], testBytes2)
|
||||
}
|
||||
log.I.S(testBytes2)
|
||||
}
|
||||
103
pkg/ifc/interfaces.go
Normal file
103
pkg/ifc/interfaces.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package ifc
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
|
||||
"github.com/Indra-Labs/indra"
|
||||
log2 "github.com/cybriq/proc/pkg/log"
|
||||
)
|
||||
|
||||
var (
|
||||
log = log2.GetLogger(indra.PathBase)
|
||||
check = log.E.Chk
|
||||
)
|
||||
|
||||
type Transport interface {
|
||||
Send(b Message)
|
||||
Receive() <-chan Message
|
||||
}
|
||||
|
||||
type Message []byte
|
||||
|
||||
func ToMessage(b []byte) (msg Message) { return b }
|
||||
func (m Message) ToBytes() []byte { return m }
|
||||
|
||||
type U64Slice []uint64
|
||||
|
||||
// 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 Message) 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
|
||||
// lastly, change the element length
|
||||
header.Len = uLen
|
||||
// vh := reflect.ValueOf(header)
|
||||
// vh.SetLen(len(m) / 8)
|
||||
// This safely pins the total capacity to the length, regardless of what
|
||||
// it was before. Simply setting the value could cause the GC confusion.
|
||||
// vh.SetCap(header.Len)
|
||||
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 Message) {
|
||||
// log.I.S(u)
|
||||
// length is encoded into the last element
|
||||
mLen := int(u[len(u)-1])
|
||||
m = make(Message, 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
|
||||
// vh := reflect.ValueOf(header)
|
||||
// vh.SetLen(len(m) / 8)
|
||||
// This safely pins the total capacity to the length, regardless of what
|
||||
// it was before. Simply setting the value could cause the GC confusion.
|
||||
// vh.SetCap(header.Len)
|
||||
header.Cap = mLen
|
||||
return m
|
||||
}
|
||||
50
pkg/ifc/interfaces_test.go
Normal file
50
pkg/ifc/interfaces_test.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package ifc
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/Indra-Labs/indra/pkg/testutils"
|
||||
)
|
||||
|
||||
func TestMessage_ToU64Slice(t *testing.T) {
|
||||
var e error
|
||||
var msg1 Message
|
||||
if msg1, _, e = testutils.GenerateTestMessage(33); check(e) {
|
||||
t.Error(e)
|
||||
t.FailNow()
|
||||
}
|
||||
log.I.S(msg1)
|
||||
uMsg1 := msg1.ToU64Slice()
|
||||
umsg1 := uMsg1.ToMessage()
|
||||
log.I.S(umsg1)
|
||||
}
|
||||
|
||||
func TestU64Slice_XOR(t *testing.T) {
|
||||
var e error
|
||||
var msg1 Message
|
||||
if msg1, _, e = testutils.GenerateTestMessage(33); check(e) {
|
||||
t.Error(e)
|
||||
t.FailNow()
|
||||
}
|
||||
log.I.S(msg1)
|
||||
uMsg1 := msg1.ToU64Slice()
|
||||
var msg2 Message
|
||||
if msg2, _, e = testutils.GenerateTestMessage(33); check(e) {
|
||||
t.Error(e)
|
||||
t.FailNow()
|
||||
}
|
||||
// log.I.S(msg2)
|
||||
uMsg2 := msg2.ToU64Slice()
|
||||
var msg3 Message
|
||||
if msg3, _, e = testutils.GenerateTestMessage(33); check(e) {
|
||||
t.Error(e)
|
||||
t.FailNow()
|
||||
}
|
||||
// log.I.S(msg3)
|
||||
uMsg3 := msg3.ToU64Slice()
|
||||
uMsg1.XOR(uMsg2)
|
||||
uMsg1.XOR(uMsg3)
|
||||
uMsg1.XOR(uMsg2)
|
||||
uMsg1.XOR(uMsg3)
|
||||
log.I.S(uMsg1.ToMessage())
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package ifc
|
||||
|
||||
type Transport interface {
|
||||
Send(b Message)
|
||||
Receive() <-chan Message
|
||||
}
|
||||
|
||||
type Message []byte
|
||||
|
||||
func ToMessage(b []byte) (msg Message) { return b }
|
||||
func (msg Message) ToBytes() []byte { return msg }
|
||||
@@ -13,7 +13,9 @@ import (
|
||||
// Compute computes an elliptic curve diffie hellman shared secret that can be
|
||||
// decrypted by the holder of the private key matching the public key provided.
|
||||
func Compute(prv *prv.Key, pub *pub.Key) sha256.Hash {
|
||||
return secp256k1.GenerateSharedSecret(
|
||||
(*secp256k1.PrivateKey)(prv), (*secp256k1.PublicKey)(pub),
|
||||
return sha256.Single(
|
||||
secp256k1.GenerateSharedSecret(
|
||||
(*secp256k1.PrivateKey)(prv), (*secp256k1.PublicKey)(pub),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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 = "0afe0959a4ae4a82d16f47e58886272acca75623"
|
||||
ParentGitCommit = "dbe8d115254dc3f5f9857685b56935fe4eb2f51f"
|
||||
// BuildTime stores the time when the current binary was built.
|
||||
BuildTime = "2022-12-09T11:56:56+01:00"
|
||||
BuildTime = "2022-12-09T18:22:47+01:00"
|
||||
// SemVer lists the (latest) git tag on the build.
|
||||
SemVer = "v0.0.163"
|
||||
SemVer = "v0.0.164"
|
||||
// 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 = 163
|
||||
Patch = 164
|
||||
)
|
||||
|
||||
// Version returns a pretty printed version information string.
|
||||
|
||||
Reference in New Issue
Block a user