func TestMaskedEncrypt(t *testing.T) { cand, real := make([]byte, 16), make([]byte, 16) // Calculate the candidate output. constr, inputMask, outputMask := GenerateEncryptionKeys(key, seed, common.MatchingMasks{}) inputInv, _ := inputMask.Invert() outputInv, _ := outputMask.Invert() in := make([]byte, 16) copy(in, inputInv.Mul(matrix.Row(input))) // Apply input encoding. constr.Encrypt(cand, in) constr.Encrypt(cand, cand) copy(cand, outputInv.Mul(matrix.Row(cand))) // Remove output encoding. // Calculate the real output. c, _ := aes.NewCipher(key) c.Encrypt(real, input) c.Encrypt(real, real) if !bytes.Equal(real, cand) { t.Fatalf("Real disagrees with result! %x != %x", real, cand) } }
// 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() }
func TestDecrypt(t *testing.T) { for n, vec := range test_vectors.GetAESVectors(testing.Short()) { constr, inputMask, outputMask := GenerateDecryptionKeys( vec.Key, vec.Key, common.IndependentMasks{common.RandomMask, common.RandomMask}, ) inputInv, _ := inputMask.Invert() outputInv, _ := outputMask.Invert() in, out := make([]byte, 16), make([]byte, 16) copy(in, inputInv.Mul(matrix.Row(vec.Out))) // Apply input encoding. constr.Decrypt(out, in) copy(out, outputInv.Mul(matrix.Row(out))) // Remove output encoding. if !bytes.Equal(vec.In, out) { t.Fatalf("Real disagrees with result in test vector %v! %x != %x", n, vec.In, out) } } }
func (bm BlockMatrix) Get(i byte) (out [16]byte) { r := make([]byte, 16) r[bm.Position] = i res := bm.Linear.Mul(matrix.Row(r)) copy(out[:], res) for i, c := range bm.Constant { out[i] ^= c } return }
// Parse parses a byte array into a white-box construction. It returns an error if the byte slice isn't long enough. func Parse(in []byte) (constr Construction, err error) { if len(in) != fullSize { err = errors.New("Parsing the key failed.") return } for round := 0; round < 11; round++ { forwards := matrix.Matrix{} constant := [16]byte{} for row := 0; row < 128; row++ { forwards = append(forwards, matrix.Row(in[:16])) in = in[16:] } copy(constant[:], in[:16]) in = in[16:] constr[round] = encoding.NewBlockAffine(forwards, constant) } return }