// isBlockOfInverse takes a candidate solution for the given block of the matrix and returns true if it is valid and // false if it isn't. func (al *affineLayer) isBlockOfInverse(block int, cand matrix.Matrix) bool { // Pad matrix. inv := matrix.GenerateEmpty(32*block, 32) for _, row := range cand { inv = append(inv, row) } for row := 0; row < 96-32*block; row++ { inv = append(inv, matrix.NewRow(32)) } // Test if this is consistent with inverse. res := (*al).BlockLinear.Forwards.Compose(inv).Transpose() for i := 0; i < 4; i++ { row, pos := res[8*i], blockPos[4*block+i] if h := row.Height(); !(16*pos <= h && h < 16*(pos+1)) { return false } if !row[2*(pos+1):].IsZero() { return false } } return true }
// cleanLeft gets the last affine layer back to a matrix with 16-by-16 blocks along the diagonal, times ShiftRows, times // MixColumns and returns the matrix on the input encoding that it used to do this. func (al *affineLayer) cleanLeft() encoding.Block { inverse := matrix.GenerateEmpty(128, 128) mixcols := matrix.GenerateEmpty(128, 128) // Combine individual blocks of the inverse matrix into the full inverse matrix. Also build the matrix corresponding // to the full-block MixColumns operation. for block := 0; block < 4; block++ { inv := al.findBlockOfInverse(block) for row := 0; row < 32; row++ { copy(inverse[32*block+row][4*block:], inv[row]) copy(mixcols[32*block+row][4*block:], mixColumn[row]) } } out := encoding.NewBlockLinear(inverse.Compose(mixcols)) al.leftCompose(out) return encoding.InverseBlock{out} }
// generateSelfEquivalence returns a random self-equivalence of the S-box layer, defined by stateSize and compressSize. func generateSelfEquivalence(r io.Reader, stateSize, compressSize int) (a, bInv *blockAffine) { inSize, outSize := 8*(stateSize+compressSize), 8*stateSize in := &blockAffine{ linear: matrix.GenerateEmpty(inSize, inSize), constant: matrix.NewRow(inSize), } out := &blockAffine{ linear: matrix.GenerateEmpty(outSize, outSize), constant: matrix.NewRow(outSize), } // The S-box portion of the self-equivalence. forwards, backwards := generatePermutation(r, 8*compressSize) for i := 0; i < 8*compressSize; i++ { selfEq := generateSboxSelfEq(r) in.linear[2*forwards[i]+0].SetBit(2*i+0, selfEq[0]) in.linear[2*forwards[i]+0].SetBit(2*i+1, selfEq[1]) in.linear[2*forwards[i]+1].SetBit(2*i+0, selfEq[2]) in.linear[2*forwards[i]+1].SetBit(2*i+1, selfEq[3]) in.constant.SetBit(2*forwards[i]+0, selfEq[4]) in.constant.SetBit(2*forwards[i]+1, selfEq[5]) out.linear[backwards[i]].SetBit(i, true) } // The open portion of the self-equivalence. Fill it with a random, invertible matrix. ignoreAll := func(_ int) bool { return true } dense, denseInv := matrix.GenerateRandomPartial(r, 8*(stateSize-compressSize), matrix.IgnoreNoBytes, ignoreAll) for i := 0; i < 8*(stateSize-compressSize); i++ { copy(in.linear[8*2*compressSize+i][2*compressSize:], dense[i]) copy(out.linear[8*compressSize+i][compressSize:], denseInv[i]) } return in, out }
func maskSwap(rs *random.Source, size, round int) (out matrix.Matrix) { out = matrix.GenerateEmpty(128, 128) for row := 0; row < 128; row += size { col := row / 8 m := common.MixingBijection(rs, size, round, row/size) for subRow := 0; subRow < size; subRow++ { copy(out[row+subRow][col:], m[subRow]) } } return }
// blockOfInverse computes a block of the something something something. func blockOfInverse(swap [2]int, eqs [4]int) matrix.Matrix { unmixcol := unMixColumn.Dup() // Swap chosen rows. for i, ok := range swap { if ok == 1 { for row := 16 * i; row < 16*i+8; row++ { unmixcol[row], unmixcol[8+row] = unmixcol[8+row], unmixcol[row] } } } // Generate matrix corresponding to self-equivalence noise from S-box. noise := matrix.GenerateEmpty(32, 32) for i, eq := range eqs { for row := 0; row < 8; row++ { noise[8*i+row][i] = equivalences[eq][row][0] } } return noise.Compose(unmixcol) }