func exposeRound(constr *chow.Construction, round, inPos, outPos int) (encoding.Byte, encoding.Byte, table.InvertibleTable) { // Actual input and output encoding for round 1 in position i. in := constr.TBoxTyiTable[round][inPos].(encoding.WordTable).In out := constr.TBoxTyiTable[round+1][common.ShiftRows(outPos)].(encoding.WordTable).In f := F{constr, round, inPos, outPos, 0x00} // R corresponds to an exposed round. // Marginally more intuitive way to write it: V(x) := out.Decode(f.Get(in.Encode(x))) R := table.InvertibleTable(encoding.ByteTable{ encoding.InverseByte{in}, encoding.InverseByte{out}, f, }) return in, out, R }
// RecoverKey returns the AES key used to generate the given white-box construction. func RecoverKey(constr *chow.Construction) []byte { round1, round2 := round{ construction: constr, round: 1, }, round{ construction: constr, round: 2, } // Decomposition Phase constr1 := aspn.DecomposeSPN(round1, cspn.SAS) constr2 := aspn.DecomposeSPN(round2, cspn.SAS) var ( leading, middle, trailing sboxLayer left, right = affineLayer(constr1[1].(encoding.BlockAffine)), affineLayer(constr2[1].(encoding.BlockAffine)) ) for pos := 0; pos < 16; pos++ { leading[pos] = constr1[0].(encoding.ConcatenatedBlock)[pos] middle[pos] = encoding.ComposedBytes{ constr1[2].(encoding.ConcatenatedBlock)[pos], constr2[0].(encoding.ConcatenatedBlock)[common.ShiftRows(pos)], } trailing[pos] = constr2[2].(encoding.ConcatenatedBlock)[pos] } // Disambiguation Phase // Disambiguate the affine layer. lin, lout := left.clean() rin, rout := right.clean() leading.rightCompose(lin, common.NoShift) middle.leftCompose(lout, common.NoShift).rightCompose(rin, common.ShiftRows) trailing.leftCompose(rout, common.NoShift) // The SPN decomposition naturally leaves the affine layers without a constant part. // We would push it into the S-boxes here if that wasn't the case. // Move the constant off of the input and output of the S-boxes. mcin, mcout := middle.cleanConstant() mcin, mcout = left.Decode(mcin), right.Encode(mcout) leading.rightCompose(encoding.DecomposeConcatenatedBlock(encoding.BlockAdditive(mcin)), common.NoShift) trailing.leftCompose(encoding.DecomposeConcatenatedBlock(encoding.BlockAdditive(mcout)), common.NoShift) // Move the multiplication off of the input and output of the middle S-boxes. mlin, mlout := middle.cleanLinear() leading.rightCompose(mlin, common.NoShift) trailing.leftCompose(mlout, common.NoShift) // fmt.Println(encoding.ProbablyEquivalentBlocks( // encoding.ComposedBlocks{aspn.Encoding{round1}, ShiftRows{}, aspn.Encoding{round2}}, // encoding.ComposedBlocks{leading, left, middle, ShiftRows{}, right, trailing}, // )) // Output: true // Extract the key from the leading S-boxes. key := [16]byte{} for pos := 0; pos < 16; pos++ { for guess := 0; guess < 256; guess++ { cand := encoding.ComposedBytes{ leading[pos], encoding.ByteAdditive(guess), encoding.InverseByte{sbox{}}, } if isAS(cand) { key[pos] = byte(guess) break } } } key = left.Encode(key) return backOneRound(backOneRound(key[:], 2), 1) }