xs/vendor/blitter.com/go/groestl/round.go

218 lines
5.2 KiB
Go

package groestl
import (
"encoding/binary"
"fmt"
)
func buildColumns(data []byte, cols chan uint64) {
for i, l := 8, len(data); i <= l; i += 8 {
cols <- binary.BigEndian.Uint64(data[i-8 : i])
}
close(cols)
}
// Performs compression function. Returns nil on success, error otherwise.
func (d *digest) transform(data []byte) error {
if (len(data) % d.BlockSize()) != 0 {
return fmt.Errorf("data len in transform is not a multiple of BlockSize")
}
cols := make(chan uint64)
go buildColumns(data, cols)
eb := d.blocks + uint64(len(data)/d.BlockSize())
for d.blocks < eb {
m := make([]uint64, d.columns)
hxm := make([]uint64, d.columns)
for i := 0; i < d.columns; i++ {
m[i] = <-cols
hxm[i] = d.chaining[i] ^ m[i]
}
if VERBOSE {
fmt.Println("\n========================================\n")
fmt.Println("Block Contents:")
printUintSlice(m)
fmt.Println()
}
hxm = round(d, hxm, 'P')
m = round(d, m, 'Q')
for i := 0; i < d.columns; i++ {
d.chaining[i] ^= hxm[i] ^ m[i]
}
d.blocks += 1
if VERBOSE {
fmt.Println("P(h+m) + Q(m) + h =")
printUintSlice(d.chaining[:d.columns])
fmt.Println()
}
}
return nil
}
// Performs last compression. After this function, data
// is ready for truncation.
func (d *digest) finalTransform() {
h := make([]uint64, d.columns)
for i := 0; i < d.columns; i++ {
h[i] = d.chaining[i]
}
if VERBOSE {
fmt.Println("\n========================================\n")
fmt.Println("Output transformation:\n")
}
h = round(d, h, 'P')
for i := 0; i < d.columns; i++ {
d.chaining[i] ^= h[i]
}
d.blocks += 1
if VERBOSE {
fmt.Println("P(h) + h =")
printUintSlice(d.chaining[:d.columns])
fmt.Println("\n---------------------------------------\n")
}
}
// Performs whole set of rounds on data provided in x. Variant denotes type
// of permutation being performed. P and Q are for groestl-512
// and lowercase are for groestl-256
func round(d *digest, x []uint64, variant rune) []uint64 {
if VERBOSE {
fmt.Println(":: BEGIN " + string(variant))
defer fmt.Println(":: END " + string(variant) + "\n")
fmt.Println("Input:")
printUintSlice(x)
}
if d.BlockSize() == 64 {
// for smaller blocksize change variant to lowercase letter
variant += 0x20
}
for i := 0; i < d.rounds; i++ {
x = addRoundConstant(x, i, variant)
if VERBOSE {
fmt.Printf("t=%d (AddRoundConstant):\n", i)
printUintSlice(x)
}
x = subBytes(x)
if VERBOSE {
fmt.Printf("t=%d (SubBytes):\n", i)
printUintSlice(x)
}
x = shiftBytes(x, variant)
if VERBOSE {
fmt.Printf("t=%d (ShiftBytes):\n", i)
printUintSlice(x)
}
x = mixBytes(x)
if VERBOSE {
fmt.Printf("t=%d (MixBytes):\n", i)
printUintSlice(x)
}
}
return x
}
// AddRoundConstant transformation for data provided in x. Variant denotes type
// of permutation being performed. P and Q are for groestl-512
// and lowercase are for groestl-256
func addRoundConstant(x []uint64, r int, variant rune) []uint64 {
switch variant {
case 'P', 'p':
for i, l := 0, len(x); i < l; i++ {
// byte from row 0: ((col >> (8*7)) & 0xFF)
// we want to xor the byte below with row 0
// therefore we have to shift it by 8*7 bits
x[i] ^= uint64((i<<4)^r) << (8 * 7)
}
case 'Q', 'q':
for i, l := 0, len(x); i < l; i++ {
x[i] ^= ^uint64(0) ^ uint64((i<<4)^r)
}
}
return x
}
// SubBytes transformation for data provided in x.
func subBytes(x []uint64) []uint64 {
var newCol [8]byte
for i, l := 0, len(x); i < l; i++ {
for j := 0; j < 8; j++ {
newCol[j] = sbox[pickRow(x[i], j)]
}
x[i] = binary.BigEndian.Uint64(newCol[:])
}
return x
}
// ShiftBytes transformation for data provided in x. Variant denotes type
// of permutation being performed. P and Q are for groestl-512
// and lowercase are for groestl-256
func shiftBytes(x []uint64, variant rune) []uint64 {
var shiftVector [8]int
switch variant {
case 'p':
shiftVector = [8]int{0, 1, 2, 3, 4, 5, 6, 7}
case 'P':
shiftVector = [8]int{0, 1, 2, 3, 4, 5, 6, 11}
case 'q':
shiftVector = [8]int{1, 3, 5, 7, 0, 2, 4, 6}
case 'Q':
shiftVector = [8]int{1, 3, 5, 11, 0, 2, 4, 6}
}
l := len(x)
ret := make([]uint64, l)
for i := 0; i < l; i++ {
ret[i] = uint64(pickRow(x[(i+shiftVector[0])%l], 0))
for j := 1; j < 8; j++ {
ret[i] <<= 8
ret[i] ^= uint64(pickRow(x[(i+shiftVector[j])%l], j))
}
}
return ret
}
// MixBytes transformation for data provided in x.
func mixBytes(x []uint64) []uint64 {
// this part is tricky
// so here comes yet another rough translation straight from reference implementation
mul2 := func(b uint8) uint8 { return uint8((b << 1) ^ (0x1B * ((b >> 7) & 1))) }
mul3 := func(b uint8) uint8 { return (mul2(b) ^ (b)) }
mul4 := func(b uint8) uint8 { return mul2(mul2(b)) }
mul5 := func(b uint8) uint8 { return (mul4(b) ^ (b)) }
mul7 := func(b uint8) uint8 { return (mul4(b) ^ mul2(b) ^ (b)) }
var temp [8]uint8
for i, l := 0, len(x); i < l; i++ {
for j := 0; j < 8; j++ {
temp[j] =
mul2(pickRow(x[i], (j+0)%8)) ^
mul2(pickRow(x[i], (j+1)%8)) ^
mul3(pickRow(x[i], (j+2)%8)) ^
mul4(pickRow(x[i], (j+3)%8)) ^
mul5(pickRow(x[i], (j+4)%8)) ^
mul3(pickRow(x[i], (j+5)%8)) ^
mul5(pickRow(x[i], (j+6)%8)) ^
mul7(pickRow(x[i], (j+7)%8))
}
x[i] = binary.BigEndian.Uint64(temp[:])
}
return x
}