// generateSelfEquivalence returns a random self-equivalence of the S-box layer, so that \zeta(x) = bInv(\zeta(a(x))). func generateSelfEquivalence(r io.Reader) (a, bInv encoding.Block) { // Sample a byte-wise permutation to apply to the input. p := &permutation{encoding.GenerateShuffle(r)} // Sample one non-zero scalar for each byte. Each byte of the input is multiplied by this scalar. buff := make([]byte, 1) scalars := encoding.ConcatenatedBlock{} for pos := 0; pos < 16; { r.Read(buff) if buff[0] != 0x00 { scalars[pos] = encoding.NewByteMultiplication(number.ByteFieldElem(buff[0])) pos++ } } // Sample a random value in [0, 8) for each byte. This is the number of times to apply the Frobenius. frobs := encoding.ConcatenatedBlock{} for pos := 0; pos < 16; pos++ { r.Read(buff) frobs[pos] = frobenius(buff[0] & 0x7) } return encoding.ComposedBlocks{ frobs, scalars, p, }, encoding.ComposedBlocks{ encoding.InverseBlock{p}, scalars, encoding.InverseBlock{frobs}, } }
// stripScalars gets rid of unknown scalars in each block of the affine layer. It leaves it exactly equal to MixColumns, // but there is an unknown scalar in each block that will move into the S-box layers. func (al *affineLayer) stripScalars() (encoding.ConcatenatedBlock, encoding.ConcatenatedBlock) { input, output := [16]encoding.ByteLinear{}, [16]encoding.ByteLinear{} for pos := 0; pos < 16; pos += 4 { found := false for guess := 1; guess < 256 && !found; guess++ { // Take a guess for the input scalar on the first column. input[pos], _ = encoding.DecomposeByteLinear(encoding.NewByteMultiplication(number.ByteFieldElem(guess))) // Given input scalar on first column, calculate output scalars on all rows. for i := pos; i < pos+4; i++ { mc, _ := mixColumns[i%4][0].Invert() output[i] = encoding.NewByteLinear( al.getBlock(i, pos).Compose(input[pos].Backwards).Compose(mc), ) } // Given output scalar on each row, calculate input scalars on all columns. for i := pos + 1; i < pos+4; i++ { mc, _ := mixColumns[0][i%4].Invert() input[i] = encoding.NewByteLinear( al.getBlock(pos, i).Compose(output[pos].Backwards).Compose(mc), ) } // Verify that guess is consistent. found = true for i := pos; i < pos+4 && found; i++ { for j := pos; j < pos+4 && found; j++ { cand := al.getBlock(i, j).Compose(output[i].Backwards).Compose(input[j].Backwards) real := mixColumns[i%4][j%4] if !cand.Equals(real) { found = false } } } } if !found { panic("Failed to disambiguate block affine layer!") } } in, out := encoding.ConcatenatedBlock{}, encoding.ConcatenatedBlock{} for pos := 0; pos < 16; pos++ { in[pos], out[pos] = input[pos], output[pos] } return in, out }