From f99bb789b9ef7be6341811a3bfcb1b19e5b1e176 Mon Sep 17 00:00:00 2001 From: arnaucube Date: Sun, 31 Oct 2021 01:31:04 +0200 Subject: [PATCH] Add utils (bits) --- go-keccak256-bits-impl/README.md | 2 + go-keccak256-bits-impl/go.mod | 13 +++ go-keccak256-bits-impl/go.sum | 19 ++++ go-keccak256-bits-impl/utils.go | 136 +++++++++++++++++++++++++++ go-keccak256-bits-impl/utils_test.go | 104 ++++++++++++++++++++ 5 files changed, 274 insertions(+) create mode 100644 go-keccak256-bits-impl/README.md create mode 100644 go-keccak256-bits-impl/go.mod create mode 100644 go-keccak256-bits-impl/go.sum create mode 100644 go-keccak256-bits-impl/utils.go create mode 100644 go-keccak256-bits-impl/utils_test.go diff --git a/go-keccak256-bits-impl/README.md b/go-keccak256-bits-impl/README.md new file mode 100644 index 0000000..f4cf2e0 --- /dev/null +++ b/go-keccak256-bits-impl/README.md @@ -0,0 +1,2 @@ +# go-keccak256-bits-impl +Implementation of keccak256 hash function in Go, working with bits instead of bytes&uint64, in order to recreate the Circom circuit behaviour. diff --git a/go-keccak256-bits-impl/go.mod b/go-keccak256-bits-impl/go.mod new file mode 100644 index 0000000..07b6ad5 --- /dev/null +++ b/go-keccak256-bits-impl/go.mod @@ -0,0 +1,13 @@ +module keccak + +go 1.17 + +require github.com/frankban/quicktest v1.14.0 + +require ( + github.com/google/go-cmp v0.5.6 // indirect + github.com/kr/pretty v0.3.0 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/rogpeppe/go-internal v1.6.1 // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect +) diff --git a/go-keccak256-bits-impl/go.sum b/go-keccak256-bits-impl/go.sum new file mode 100644 index 0000000..fdd33fa --- /dev/null +++ b/go-keccak256-bits-impl/go.sum @@ -0,0 +1,19 @@ +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss= +github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= diff --git a/go-keccak256-bits-impl/utils.go b/go-keccak256-bits-impl/utils.go new file mode 100644 index 0000000..2f06e65 --- /dev/null +++ b/go-keccak256-bits-impl/utils.go @@ -0,0 +1,136 @@ +package keccak + +import ( + "encoding/binary" + "fmt" + "math" +) + +func bytesToU64(v []byte) uint64 { + return uint64(v[0]) | + uint64(v[1])<<8 | + uint64(v[2])<<16 | + uint64(v[3])<<24 | + uint64(v[4])<<32 | + uint64(v[5])<<40 | + uint64(v[6])<<48 | + uint64(v[7])<<56 +} +func u64ToBytes(x uint64) []byte { + v := make([]byte, 8) + v[0] = byte(x) + v[1] = byte(x >> 8) + v[2] = byte(x >> 16) + v[3] = byte(x >> 24) + v[4] = byte(x >> 32) + v[5] = byte(x >> 40) + v[6] = byte(x >> 48) + v[7] = byte(x >> 56) + return v +} + +func bitsToU64Array(b []bool) []uint64 { + r := make([]uint64, len(b)/64) + for i := 0; i < len(b)/64; i++ { + r[i] = bitsToU64(b[i*64 : i*64+64]) + } + return r +} +func u64ArrayToBits(u []uint64) []bool { + r := make([]bool, len(u)*64) + for i := 0; i < len(u); i++ { + copy(r[i*64:i*64+64], u64ToBits(u[i])) + } + return r +} +func bitsToU64(b []bool) uint64 { + if len(b) != 64 { + panic(fmt.Errorf("len(b)=%d, max=64", len(b))) + } + by := bitsToBytes(b) + return binary.LittleEndian.Uint64(by) +} +func u64ToBits(u uint64) []bool { + by := u64ToBytes(u) + return bytesToBits(by) +} +func bytesToBits(b []byte) []bool { + var bits []bool + for i := 0; i < len(b); i++ { + for j := 0; j < 8; j++ { + bits = append(bits, b[i]&(1< 0) + } + } + return bits +} +func bitsToBytes(bits []bool) []byte { + bytesLen := int(math.Ceil(float64(len(bits)) / 8)) + b := make([]byte, bytesLen) + for i := 0; i < len(bits); i++ { + if bits[i] { + b[i/8] |= 1 << (i % 8) + } + } + return b +} + +func leftShift(a []bool, n int) []bool { + c := make([]bool, len(a)) + copy(c[n:], a[:]) + // c[0] = a[0] + // for i := 1; i < len(a); i++ { + // if i < n { + // c[i] = a[i] + // } else { + // c[i] = a[i-1] + // } + // } + return c +} + +func rightShift(a []bool, n int) []bool { + c := make([]bool, len(a)) + copy(c[:], a[n:]) + // for i := len(a) - 1 - n; i >= 0; i-- { + // c[i] = a[i+1] + // } + return c +} + +// TODO add unit tests +func xorSingle(a []bool) []bool { + c := make([]bool, len(a)) + for i := 0; i < len(a); i++ { + c[i] = !a[i] + } + return c +} +func xor(a, b []bool) []bool { + c := make([]bool, len(a)) + for i := 0; i < len(a); i++ { + if a[i] != b[i] { // XOR + c[i] = true + } + } + return c +} +func or(a, b []bool) []bool { + c := make([]bool, len(a)) + for i := 0; i < len(a); i++ { + if a[i] == false && b[i] == false { // OR + c[i] = false + } else { + c[i] = true + } + } + return c +} +func and(a, b []bool) []bool { + c := make([]bool, len(a)) + for i := 0; i < len(a); i++ { + if a[i] == true && b[i] == true { + c[i] = true + } + } + return c +} diff --git a/go-keccak256-bits-impl/utils_test.go b/go-keccak256-bits-impl/utils_test.go new file mode 100644 index 0000000..f78edbe --- /dev/null +++ b/go-keccak256-bits-impl/utils_test.go @@ -0,0 +1,104 @@ +package keccak + +import ( + "encoding/binary" + "testing" + + qt "github.com/frankban/quicktest" +) + +func TestUtilsBytesToBool(t *testing.T) { + b := []byte{0, 1, 2, 3, 255, 254, 253} + bits := bytesToBits(b) + // for i := 0; i < len(b); i++ { + // fmt.Println(i*8, i*8+8, b[i], bits[i*8:i*8+8]) + // } + qt.Assert(t, bits[0:8], qt.DeepEquals, + []bool{false, false, false, false, false, false, false, false}) + qt.Assert(t, bits[8:16], qt.DeepEquals, + []bool{true, false, false, false, false, false, false, false}) + qt.Assert(t, bits[16:24], qt.DeepEquals, + []bool{false, true, false, false, false, false, false, false}) + qt.Assert(t, bits[24:32], qt.DeepEquals, + []bool{true, true, false, false, false, false, false, false}) + qt.Assert(t, bits[32:40], qt.DeepEquals, + []bool{true, true, true, true, true, true, true, true}) + qt.Assert(t, bits[40:48], qt.DeepEquals, + []bool{false, true, true, true, true, true, true, true}) + qt.Assert(t, bits[48:56], qt.DeepEquals, + []bool{true, false, true, true, true, true, true, true}) +} + +func TestUtilsBitsToBytes(t *testing.T) { + b := []byte{0, 1, 2, 3, 255, 254, 253} + bits := bytesToBits(b) + b2 := bitsToBytes(bits) + qt.Assert(t, b2, qt.DeepEquals, b) +} + +func TestUtilsU64(t *testing.T) { + u := uint64(100) + b := make([]byte, 8) + binary.LittleEndian.PutUint64(b, u) + + // toUint64 + // with bytes + uRes := bytesToU64(b) + qt.Assert(t, uRes, qt.Equals, u) + + // with bits + uRes = bitsToU64(bytesToBits(b)) + qt.Assert(t, uRes, qt.Equals, u) + + // fromUint64 + // with bytes + bRes := u64ToBytes(u) + qt.Assert(t, bRes, qt.DeepEquals, b) + + // with bits + bResBits := u64ToBits(u) + qt.Assert(t, bResBits, qt.DeepEquals, bytesToBits(b)) +} + +func TestUtilsLeftShift(t *testing.T) { + u := uint64(2) + bits := u64ToBits(u) + r := leftShift(bits, 1) + qt.Assert(t, bitsToU64(r), qt.Equals, u<<1) + + u = uint64(9) + bits = u64ToBits(u) + r = leftShift(bits, 1) + qt.Assert(t, bitsToU64(r), qt.Equals, u<<1) + + u = uint64(14) + bits = u64ToBits(u) + r = leftShift(bits, 63) + qt.Assert(t, bitsToU64(r), qt.Equals, u<<63) + + u = uint64(123456) + bits = u64ToBits(u) + r = leftShift(bits, 1) + qt.Assert(t, bitsToU64(r), qt.Equals, u<<1) +} +func TestUtilsRightShift(t *testing.T) { + u := uint64(2) + bits := u64ToBits(u) + r := rightShift(bits, 1) + qt.Assert(t, bitsToU64(r), qt.Equals, u>>1) + + u = uint64(9) + bits = u64ToBits(u) + r = rightShift(bits, 1) + qt.Assert(t, bitsToU64(r), qt.Equals, u>>1) + + u = uint64(14) + bits = u64ToBits(u) + r = rightShift(bits, 63) + qt.Assert(t, bitsToU64(r), qt.Equals, u>>63) + + u = uint64(123456) + bits = u64ToBits(u) + r = rightShift(bits, 1) + qt.Assert(t, bitsToU64(r), qt.Equals, u>>1) +}