mirror of https://gogs.blitter.com/RLabs/xs
218 lines
5.2 KiB
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
|
|
}
|