Remove composition, add fast XOR, added notes about return secondary keys
This commit is contained in:
@@ -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]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user