Remove composition, add fast XOR, added notes about return secondary keys

This commit is contained in:
David Vennik
2022-12-10 09:03:35 +01:00
parent f4b5dfffc1
commit 27ff7bd09c
5 changed files with 23 additions and 243 deletions

View File

@@ -4,9 +4,6 @@ package ciph
import (
"crypto/aes"
"crypto/cipher"
"fmt"
"reflect"
"unsafe"
"github.com/Indra-Labs/indra"
"github.com/Indra-Labs/indra/pkg/key/ecdh"
@@ -37,70 +34,3 @@ func Encipher(blk cipher.Block, n nonce.IV, b []byte) {
cipher.NewCTR(blk, n).XORKeyStream(b, b)
}
}
const SecretLength = 32
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 {
e = fmt.Errorf("unable to combine ciphers, cipher %d is"+
" length %d, expected %d",
i, len(secrets[i]), SecretLength)
}
}
combined = make([]byte, SecretLength)
for i := range combined {
for j := range secrets {
combined[i] ^= secrets[j][i]
}
}
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]
}
}

View File

@@ -1,156 +1 @@
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)
}

View File

@@ -22,9 +22,20 @@ type Message []byte
func ToMessage(b []byte) (msg Message) { return b }
func (m Message) ToBytes() []byte { return m }
func (m Message) Copy() (o Message) {
o = make(Message, len(m))
copy(o, m)
return
}
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
@@ -53,13 +64,8 @@ func (m Message) ToU64Slice() (u U64Slice) {
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
// Update the element length and capacity
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)
@@ -79,7 +85,6 @@ func (u U64Slice) XOR(v U64Slice) {
}
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)
@@ -93,11 +98,6 @@ func (u U64Slice) ToMessage() (m Message) {
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
}

View File

@@ -3,6 +3,7 @@ package ifc
import (
"testing"
"github.com/Indra-Labs/indra/pkg/sha256"
"github.com/Indra-Labs/indra/pkg/testutils"
)
@@ -26,7 +27,7 @@ func TestU64Slice_XOR(t *testing.T) {
t.Error(e)
t.FailNow()
}
log.I.S(msg1)
hash1 := sha256.Single(msg1)
uMsg1 := msg1.ToU64Slice()
var msg2 Message
if msg2, _, e = testutils.GenerateTestMessage(33); check(e) {
@@ -46,5 +47,9 @@ func TestU64Slice_XOR(t *testing.T) {
uMsg1.XOR(uMsg3)
uMsg1.XOR(uMsg2)
uMsg1.XOR(uMsg3)
log.I.S(uMsg1.ToMessage())
hash2 := sha256.Single(uMsg1.ToMessage())
if !hash1.Equals(hash2) {
t.Error("XOR failed")
t.FailNow()
}
}

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 = "dbe8d115254dc3f5f9857685b56935fe4eb2f51f"
ParentGitCommit = "1c947633bcf8c49fff106151da27e45c4df635c2"
// BuildTime stores the time when the current binary was built.
BuildTime = "2022-12-09T18:22:47+01:00"
BuildTime = "2022-12-10T09:03:35+01:00"
// SemVer lists the (latest) git tag on the build.
SemVer = "v0.0.164"
SemVer = "v0.0.165"
// 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 = 164
Patch = 165
)
// Version returns a pretty printed version information string.