// GenerateEncryptionKeys creates a white-boxed version of AES with given key for encryption, with any non-determinism // generated by seed. Opts specifies what type of input and output masks we put on the construction and should be in // common.{IndependentMasks, SameMasks, MatchingMasks}. func GenerateEncryptionKeys(key, seed []byte, opts common.KeyGenerationOpts) (out Construction, inputMask, outputMask matrix.Matrix) { rs := random.NewSource("Chow Encryption", seed) constr := saes.Construction{key} roundKeys := constr.StretchedKey() // Apply ShiftRows to round keys 0 to 9. for k := 0; k < 10; k++ { constr.ShiftRows(roundKeys[k]) } skinny := func(pos int) table.Byte { return common.TBox{constr, roundKeys[9][pos], roundKeys[10][pos]} } wide := func(round, pos int) table.Word { return table.ComposedToWord{ common.TBox{Constr: constr, KeyByte1: roundKeys[round][pos]}, common.TyiTable(pos % 4), } } generateKeys(&rs, opts, &out, &inputMask, &outputMask, common.ShiftRows, skinny, wide) return }
func TestRecoverEncodings(t *testing.T) { constr, key := testConstruction() fastConstr := fastTestConstruction() baseConstr := saes.Construction{key} roundKeys := baseConstr.StretchedKey() outAff := getOutputAffineEncoding(constr, fastConstr, 1, 0) // Manually recover the output encoding. Q, Ps := RecoverEncodings(fastConstr, 1, 0) if fmt.Sprintf("%x %v", outAff.Linear, outAff.Constant) != fmt.Sprintf("%x %v", Q.Linear, Q.Constant) { t.Fatalf("RecoverEncodings recovered the wrong output encoding!") } // Verify that all Ps composed with their corresponding output encoding equals XOR by a key byte. id := matrix.GenerateIdentity(8) for pos, P := range Ps { outAff := getOutputAffineEncoding(constr, fastConstr, 0, unshiftRows(pos)) A, b := DecomposeAffineEncoding(encoding.ComposedBytes{outAff, P}) if fmt.Sprintf("%x", id) != fmt.Sprintf("%x", A) { t.Fatalf("Linear part of encoding was not identity!") } if roundKeys[1][unshiftRows(pos)] != b { t.Fatalf("Constant part of encoding was not key byte!") } } }
// GenerateDecryptionKeys creates a white-boxed version of AES with given key for decryption, with any non-determinism // generated by seed. Opts specifies what type of input and output masks we put on the construction and should be in // common.{IndependentMasks, SameMasks, MatchingMasks}. func GenerateDecryptionKeys(key, seed []byte, opts common.KeyGenerationOpts) (out Construction, inputMask, outputMask matrix.Matrix) { rs := random.NewSource("Chow Decryption", seed) constr := saes.Construction{key} roundKeys := constr.StretchedKey() // Last key needs to be unshifted for decryption to work right. constr.UnShiftRows(roundKeys[10]) skinny := func(pos int) table.Byte { return common.InvTBox{constr, 0x00, roundKeys[0][pos]} } wide := func(round, pos int) table.Word { if round == 0 { return table.ComposedToWord{ common.InvTBox{Constr: constr, KeyByte1: roundKeys[10][pos], KeyByte2: roundKeys[9][pos]}, common.InvTyiTable(pos % 4), } } else { return table.ComposedToWord{ common.InvTBox{Constr: constr, KeyByte2: roundKeys[9-round][pos]}, common.InvTyiTable(pos % 4), } } } generateKeys(&rs, opts, &out, &inputMask, &outputMask, common.UnShiftRows, skinny, wide) return }
// GenerateDecryptionKeys creates a white-boxed version of the AES key `key` for decryption, with any non-determinism // generated by `seed`. The `opts` argument works the same as above. func GenerateDecryptionKeys(key, seed []byte, opts KeyGenerationOpts) (out Construction, inputMask, outputMask matrix.Matrix) { constr := saes.Construction{key} roundKeys := constr.StretchedKey() // Last key needs to be unshifted for decryption to work right. constr.UnShiftRows(roundKeys[10]) skinny := func(pos int) table.Byte { return InvTBox{constr, 0x00, roundKeys[0][pos]} } wide := func(round, pos int) table.Word { if round == 0 { return table.ComposedToWord{ InvTBox{constr, roundKeys[10][pos], roundKeys[9][pos]}, InvTyiTable(pos % 4), } } else { return table.ComposedToWord{ InvTBox{constr, 0x00, roundKeys[9-round][pos]}, InvTyiTable(pos % 4), } } } generateKeys(seed, opts, &out, &inputMask, &outputMask, unshiftRows, skinny, wide) return }
func TestBackOneRound(t *testing.T) { _, key := testConstruction() baseConstr := saes.Construction{key} roundKeys := baseConstr.StretchedKey() for round := 1; round < 11; round++ { a, b := roundKeys[round-1], BackOneRound(roundKeys[round], round) if bytes.Compare(a, b) != 0 { t.Fatalf("Failed to move back one round on round %v!\nReal: %x\nCand: %x\n", round, a, b) } } }
// GenerateDecryptionKeys creates a white-boxed version of the AES key `key` for decryption, with any non-determinism // generated by `seed`. func GenerateDecryptionKeys(key, seed []byte, opts common.KeyGenerationOpts) (out Construction, inputMask, outputMask matrix.Matrix) { rs := random.NewSource("Xiao Decryption", seed) constr := saes.Construction{key} roundKeys := constr.StretchedKey() // Apply UnShiftRows to round keys 10. constr.UnShiftRows(roundKeys[10]) hidden := func(round, pos int) table.DoubleToWord { if round == 0 { return tBoxMixCol{ [2]table.Byte{ common.InvTBox{constr, roundKeys[10][pos+0], roundKeys[9][pos+0]}, common.InvTBox{constr, roundKeys[10][pos+1], roundKeys[9][pos+1]}, }, unMixColumns, sideFromPos(pos), } } else if 0 < round && round < 9 { return tBoxMixCol{ [2]table.Byte{ common.InvTBox{constr, 0x00, roundKeys[9-round][pos+0]}, common.InvTBox{constr, 0x00, roundKeys[9-round][pos+1]}, }, unMixColumns, sideFromPos(pos), } } else { return tBox{ [2]table.Byte{ common.InvTBox{constr, 0x00, roundKeys[0][pos+0]}, common.InvTBox{constr, 0x00, roundKeys[0][pos+1]}, }, sideFromPos(pos), } } } common.GenerateMasks(&rs, opts, &inputMask, &outputMask) generateRoundMaterial(&rs, &out, hidden) generateBarriers(&rs, &out, &inputMask, &outputMask, &unShiftRows) return out, inputMask, outputMask }
// GenerateKeys creates a white-boxed version of the AES key `key`, with any non-determinism generated by `seed`. func GenerateKeys(key, seed []byte) (out Construction, inputMask, outputMask encoding.BlockAffine) { rs := random.NewSource("Ful Construction", seed) // Generate two completely random affine transformations, to be put on input and output of SPN. input, output := generateAffineMasks(&rs) // Steal key schedule logic from the standard AES construction. contr := saes.Construction{key} roundKeys := contr.StretchedKey() // Generate an SPN which has the input and output masks, but is otherwise un-obfuscated. out[0] = decomposition[0].compose(&blockAffine{ linear: matrix.GenerateIdentity(128), constant: matrix.Row(roundKeys[0]), }).compose(input) copy(out[1:5], decomposition[1:5]) for i := 1; i < 10; i++ { out[4*i+0] = decomposition[0].compose(&blockAffine{ linear: round, constant: matrix.Row(roundKeys[i]).Add(subBytesConst), }).compose(out[4*i+0]) copy(out[4*i+1:4*i+5], decomposition[1:5]) } out[40] = output.compose(&blockAffine{ linear: lastRound, constant: matrix.Row(roundKeys[10]).Add(subBytesConst), }).compose(out[40]) // Sample self-equivalences of the S-box layer and mix them into adjacent affine layers. label := make([]byte, 16) copy(label, []byte("Self-Eq")) r := rs.Stream(label) for i := 0; i < 40; i++ { a, bInv := generateSelfEquivalence(r, stateSize[i%4], compressSize[i%4]) out[i] = a.compose(out[i]) out[i+1] = out[i+1].compose(bInv) } return out, input.BlockAffine(), output.BlockAffine() }
// GenerateKeys creates a white-boxed version of the AES key `key`, with any non-determinism generated by `seed`. func GenerateKeys(key, seed []byte) (out Construction, inputMask, outputMask encoding.BlockAffine) { rs := random.NewSource("Toy Construction", seed) // Generate two completely random affine transformations, to be put on input and output of SPN. inputMask, outputMask = generateAffineMasks(&rs) // Steal key schedule logic from the standard AES construction. constr := saes.Construction{key} roundKeys := constr.StretchedKey() // Generate an SPN which has the input and output masks, but is otherwise un-obfuscated. out[0] = inputMask encoding.XOR(out[0].BlockAdditive[:], out[0].BlockAdditive[:], roundKeys[0]) for i := 1; i < 10; i++ { out[i] = encoding.BlockAffine{ BlockLinear: encoding.BlockLinear{round, unRound}, BlockAdditive: shiftRoundKey(roundKeys[i]), } } out[10] = encoding.BlockAffine{ BlockLinear: encoding.BlockLinear{lastRound, firstRound}, BlockAdditive: shiftRoundKey(roundKeys[10]), } out[10], _ = encoding.DecomposeBlockAffine(encoding.ComposedBlocks{out[10], outputMask}) // Sample a self-equivalences of the S-box layer and mix them into adjacent affine layers. label := make([]byte, 16) copy(label, []byte("Self-Eq")) r := rs.Stream(label) for i := 1; i < 11; i++ { a, bInv := generateSelfEquivalence(r) out[i-1], _ = encoding.DecomposeBlockAffine(encoding.ComposedBlocks{out[i-1], a}) out[i], _ = encoding.DecomposeBlockAffine(encoding.ComposedBlocks{bInv, out[i]}) } return }
// GenerateEncryptionKeys creates a white-boxed version of the AES key `key` for encryption, with any non-determinism // generated by `seed`. func GenerateEncryptionKeys(key, seed []byte, opts common.KeyGenerationOpts) (out Construction, inputMask, outputMask matrix.Matrix) { rs := random.NewSource("Xiao Encryption", seed) constr := saes.Construction{key} roundKeys := constr.StretchedKey() // Apply ShiftRows to round keys 0 to 9. for k := 0; k < 10; k++ { constr.ShiftRows(roundKeys[k]) } hidden := func(round, pos int) table.DoubleToWord { if round == 9 { return tBox{ [2]table.Byte{ common.TBox{constr, roundKeys[9][pos+0], roundKeys[10][pos+0]}, common.TBox{constr, roundKeys[9][pos+1], roundKeys[10][pos+1]}, }, sideFromPos(pos), } } else { return tBoxMixCol{ [2]table.Byte{ common.TBox{constr, roundKeys[round][pos+0], 0x00}, common.TBox{constr, roundKeys[round][pos+1], 0x00}, }, mixColumns, sideFromPos(pos), } } } common.GenerateMasks(&rs, opts, &inputMask, &outputMask) generateRoundMaterial(&rs, &out, hidden) generateBarriers(&rs, &out, &inputMask, &outputMask, &shiftRows) return out, inputMask, outputMask }
// GenerateEncryptionKeys creates a white-boxed version of the AES key `key` for encryption, with any non-determinism // generated by `seed`. The `opts` specifies what type of input and output masks we put on the construction and should // be either IndependentMasks, SameMasks, or MatchingMasks. func GenerateEncryptionKeys(key, seed []byte, opts KeyGenerationOpts) (out Construction, inputMask, outputMask matrix.Matrix) { constr := saes.Construction{key} roundKeys := constr.StretchedKey() // Apply ShiftRows to round keys 0 to 9. for k := 0; k < 10; k++ { constr.ShiftRows(roundKeys[k]) } skinny := func(pos int) table.Byte { return TBox{constr, roundKeys[9][pos], roundKeys[10][pos]} } wide := func(round, pos int) table.Word { return table.ComposedToWord{ TBox{constr, roundKeys[round][pos], 0x00}, TyiTable(pos % 4), } } generateKeys(seed, opts, &out, &inputMask, &outputMask, shiftRows, skinny, wide) return }