// FindPartialEncoding takes an affine encoded F and finds the values that strip its output encoding. It returns the // parameters it finds and the input encoding of f up to a key byte. func FindPartialEncoding(constr *chow.Construction, f table.Byte, L, AtildeInv matrix.Matrix) (number.ByteFieldElem, byte, encoding.ByteAffine) { fInv := table.Invert(f) id := encoding.ByteLinear(matrix.GenerateIdentity(8)) SInv := table.InvertibleTable(chow.InvTBox{saes.Construction{}, 0x00, 0x00}) S := table.Invert(SInv) // Brute force the constant part of the output encoding and the beta in Atilde = A_i <- D(beta) for c := 0; c < 256; c++ { for d := 1; d < 256; d++ { cand := encoding.ComposedBytes{ TableAsEncoding{f, fInv}, encoding.ByteAffine{id, byte(c)}, encoding.ByteLinear(AtildeInv), encoding.ByteMultiplication(byte(d)), // D below TableAsEncoding{SInv, S}, } if isAffine(cand) { a, b := DecomposeAffineEncoding(cand) return number.ByteFieldElem(d), byte(c), encoding.ByteAffine{encoding.ByteLinear(a), byte(b)} } } } panic("Failed to strip output encodings!") }
// RecoverEncodings returns the full affine output encoding of affine-encoded f at the given position, as well as the // input affine encodings for all neighboring bytes up to a key byte. Returns (out, []in) func RecoverEncodings(constr *chow.Construction, round, pos int) (encoding.ByteAffine, []encoding.ByteAffine) { Ps := make([]encoding.ByteAffine, 4) // Approximate input encodings. Ds := make([]number.ByteFieldElem, 4) // Array of gamma * MC coefficient q := byte(0x00) // The constant part of the output encoding. L := RecoverL(constr, round, pos) Atilde := FindAtilde(constr, L) AtildeInv, _ := Atilde.Invert() for i := 0; i < 4; i++ { j := pos/4*4 + i inEnc, _ := RecoverAffineEncoded(constr, encoding.IdentityByte{}, round-1, unshiftRows(j), unshiftRows(j)) _, f := RecoverAffineEncoded(constr, inEnc, round, j, pos) var c byte Ds[i], c, Ps[i] = FindPartialEncoding(constr, f, L, AtildeInv) q ^= c if i == 0 { q ^= f.Get(0x00) } } DInv, _ := DecomposeAffineEncoding(encoding.ByteMultiplication(FindDuplicate(Ds).Invert())) A := Atilde.Compose(DInv) return encoding.ByteAffine{encoding.ByteLinear(A), q}, Ps }
func TestFindCharacteristic(t *testing.T) { M := matrix.GenerateRandom(rand.Reader, 8) A := matrix.GenerateRandom(rand.Reader, 8) AInv, _ := A.Invert() N, _ := DecomposeAffineEncoding(encoding.ComposedBytes{ encoding.ByteLinear(A), encoding.ByteLinear(M), encoding.ByteLinear(AInv), }) if FindCharacteristic(M) != FindCharacteristic(N) { t.Fatalf("FindCharacteristic was not invariant!\nM = %2.2x\nA = %2.2x\nN = %2.2x, ", M, A, N) } }
func TestDecomposeAffineEncoding(t *testing.T) { ae := encoding.ByteAffine{ encoding.ByteLinear(matrix.Matrix{ matrix.Row{0xA4}, matrix.Row{0x49}, matrix.Row{0x92}, matrix.Row{0x25}, matrix.Row{0x4a}, matrix.Row{0x94}, matrix.Row{0x29}, matrix.Row{0x52}, }), 0x63, } m, c := DecomposeAffineEncoding(ae) for i := 0; i < 8; i++ { if ae.Linear[i][0] != m[i][0] { t.Fatalf("Row %v in the linear part is wrong! %v != %v", i, ae.Linear[i][0], m[i][0]) } } if ae.Constant != c { t.Fatalf("The affine part is wrong! %v != %v", ae.Constant, c) } }
func getOutputAffineEncoding(constr, fastConstr *chow.Construction, round, pos int) encoding.ByteAffine { _, out, _ := exposeRound(constr, round, pos, pos) outEncTilde, _ := RecoverAffineEncoded(fastConstr, encoding.IdentityByte{}, round, pos, pos) outAff := encoding.ComposedBytes{out, encoding.InverseByte{outEncTilde}} A, b := DecomposeAffineEncoding(outAff) return encoding.ByteAffine{encoding.ByteLinear(A), b} }
// FindCharacteristic finds the characteristic number of a matrix. This number is invariant to matrix similarity. func FindCharacteristic(A matrix.Matrix) (a byte) { AkEnc := encoding.ComposedBytes{} for k := uint(0); k < 8; k++ { AkEnc = append(AkEnc, encoding.ByteLinear(A)) Ak, _ := DecomposeAffineEncoding(AkEnc) a ^= Ak.Trace() << k } return }
func verifyIsAffine(t *testing.T, aff encoding.Byte, err string) { m, c := DecomposeAffineEncoding(aff) test := encoding.ByteAffine{encoding.ByteLinear(m), c} for i := 0; i < 256; i++ { a, b := aff.Encode(byte(i)), test.Encode(byte(i)) if a != b { t.Fatalf(err, i, a, b) } } }
func BenchmarkDecomposeAffineEncoding(b *testing.B) { aff := encoding.ByteAffine{ encoding.ByteLinear(matrix.GenerateRandom(rand.Reader, 8)), 0x60, } b.ResetTimer() for i := 0; i < b.N; i++ { DecomposeAffineEncoding(aff) } }
// RecoverAffineRel returns the affine relationship that maps y_i to y_j (instances of F with affine output encodings), // both taking input in the (inPos)th position and outputting in the (outPos1)th and (outPos2)th position, respectively. func RecoverAffineRel(constr *chow.Construction, round, inPos, outPos1, outPos2 int) encoding.ByteAffine { _, y_i := RecoverAffineEncoded(constr, encoding.IdentityByte{}, round, inPos, outPos1) _, y_j := RecoverAffineEncoded(constr, encoding.IdentityByte{}, round, inPos, outPos2) RelEnc := encoding.ComposedBytes{ TableAsEncoding{table.Invert(y_i), nil}, TableAsEncoding{y_j, nil}, } L, c := DecomposeAffineEncoding(RelEnc) return encoding.ByteAffine{encoding.ByteLinear(L), c} }
// isAffine returns true if the given encoding is affine and false if not. func isAffine(aff encoding.Byte) bool { m, c := DecomposeAffineEncoding(aff) test := encoding.ByteAffine{encoding.ByteLinear(m), c} for i := 0; i < 256; i++ { a, b := aff.Encode(byte(i)), test.Encode(byte(i)) if a != b { return false } } return true }
func TestRecoverL(t *testing.T) { MC := [][]number.ByteFieldElem{ []number.ByteFieldElem{0x02, 0x01, 0x01, 0x03}, []number.ByteFieldElem{0x03, 0x02, 0x01, 0x01}, } constr, _ := testConstruction() fastConstr := fastTestConstruction() for i := 0; i < 16; i++ { L := RecoverL(fastConstr, 1, i) outAff := getOutputAffineEncoding(constr, fastConstr, 1, i) // L is supposed to equal A_i <- D(beta) <- A_i^(-1) // We strip the conjugation by A_i and check that D(beta) is multiplication by an element of GF(2^8). DEnc := encoding.ComposedBytes{ outAff, encoding.ByteLinear(L), encoding.InverseByte{outAff}, } D, _ := DecomposeAffineEncoding(DEnc) Dstr := fmt.Sprintf("%x", D) // Calculate what beta should be. pos0, pos1 := i%4, (i+1)%4 beta := MC[0][pos0].Mul(MC[1][pos1]).Mul(MC[0][pos1].Mul(MC[1][pos0]).Invert()) // Calculate the matrix of multiplication by beta and check that it equals what we derived in D. E, _ := DecomposeAffineEncoding(encoding.ByteMultiplication(beta)) Estr := fmt.Sprintf("%x", E) if Dstr != Estr { t.Fatalf("A_i^(-1) * L * A_i doesn't equal D(beta)! i = %v\nL = %2.2x\nD = %2.2x\nE = %2.2x\n", i, L, D, E) } } }
func generateKeys(seed []byte, opts KeyGenerationOpts, out *Construction, inputMask, outputMask *matrix.Matrix, shift func(int) int, skinny func(int) table.Byte, wide func(int, int) table.Word) { // Generate input and output encodings. switch opts.(type) { case IndependentMasks: *inputMask = GenerateMask(opts.(IndependentMasks).Input, seed, Inside) *outputMask = GenerateMask(opts.(IndependentMasks).Output, seed, Outside) case SameMasks: mask := GenerateMask(MaskType(opts.(SameMasks)), seed, Inside) *inputMask, *outputMask = mask, mask case MatchingMasks: mask := GenerateMask(RandomMask, seed, Inside) *inputMask = mask *outputMask, _ = mask.Invert() default: panic("Unrecognized key generation options!") } // Generate the Input Mask table and the 10th T-Box/Output Mask table for pos := 0; pos < 16; pos++ { out.InputMask[pos] = encoding.BlockTable{ encoding.IdentityByte{}, BlockMaskEncoding(seed, pos, Inside, shift), MaskTable{*inputMask, pos}, } out.TBoxOutputMask[pos] = encoding.BlockTable{ encoding.ComposedBytes{ encoding.ByteLinear(MixingBijection(seed, 8, 8, pos)), ByteRoundEncoding(seed, 8, pos, Outside), }, BlockMaskEncoding(seed, pos, Outside, shift), table.ComposedToBlock{ skinny(pos), MaskTable{*outputMask, pos}, }, } } // Generate the XOR Tables for the Input and Output Masks. out.InputXORTable = blockXORTables(seed, Inside, shift) out.OutputXORTable = blockXORTables(seed, Outside, shift) // Generate round material. for round := 0; round < 9; round++ { for pos := 0; pos < 16; pos++ { // Generate a word-sized mixing bijection and stick it on the end of the T-Box/Tyi Table. mb := MixingBijection(seed, 32, round, pos/4) // Build the T-Box and Tyi Table for this round and position in the state matrix. out.TBoxTyiTable[round][pos] = encoding.WordTable{ encoding.ComposedBytes{ encoding.ByteLinear(MixingBijection(seed, 8, round-1, pos)), encoding.ConcatenatedByte{ RoundEncoding(seed, round-1, 2*pos+0, Outside), RoundEncoding(seed, round-1, 2*pos+1, Outside), }, }, encoding.ComposedWords{ encoding.ConcatenatedWord{ encoding.ByteLinear(MixingBijection(seed, 8, round, shift(pos/4*4+0))), encoding.ByteLinear(MixingBijection(seed, 8, round, shift(pos/4*4+1))), encoding.ByteLinear(MixingBijection(seed, 8, round, shift(pos/4*4+2))), encoding.ByteLinear(MixingBijection(seed, 8, round, shift(pos/4*4+3))), }, encoding.WordLinear(mb), WordStepEncoding(seed, round, pos, Inside), }, wide(round, pos), } // Encode the inverse of the mixing bijection from above in the MB^(-1) table for this round and position. mbInv, _ := mb.Invert() out.MBInverseTable[round][pos] = encoding.WordTable{ encoding.ConcatenatedByte{ RoundEncoding(seed, round, 2*pos+0, Inside), RoundEncoding(seed, round, 2*pos+1, Inside), }, WordStepEncoding(seed, round, pos, Outside), MBInverseTable{mbInv, uint(pos) % 4}, } } } // Generate the High and Low XOR Tables for reach round. out.HighXORTable = xorTables(seed, Inside, shift) out.LowXORTable = xorTables(seed, Outside, shift) }