The trick is that each of those candidate sets has to contain the `real` value we're looking for, but the rest are just random false-positives.
padding with different char) and generate candidates for each set. We can create different plaintext sets (eg. We have no way of discriminating which candidates are valid and which are not.įortunately we are not limited to a single dataset. Now there is one last issue - there may be multiple candidates for a single byte! Print("Potential bytes on position %d are %s" % (index, str(potential)))Ĭandidates.append(integrate(i, ciphertexts)) We can do that because the property holds for each byte, so we don't need to guess whole `last_XOR_key`, we can work on single byte at a time: So that we arrive at the moment where the square property should hold. You can think of this as basically trying to invert: XOR all bytes we got and check if the sum is 0, if it is, then we might have guessed the `last_XOR_key` byte correctly, and we save this value as candidate Invert the `ba_block = self.substitute(ba_block)`Ħ. deXOR the `k-th` byte in every ciphertext we have with the guessed xor byteĥ. Test every possible value of `k-th` byte in `last_XOR_key`Ĥ. Generate set of ciphertexsts prepared as mentioned aboveģ. Once we established we have the square property, it becomes pretty clear what we can do here:ġ. The idea is that if we encrypt 256 plaintexts, which all differ only on a single byte, then given byte position in the output ciphertexsts will loop over every possible value. Let's modify the encryption function a bit, and remove:Ĭt = Ĭt.append(spn.enc_modified(pad(long_to_bytes(i), "A").encode("hex")))Ĭt = for x in ct] General approach would be to examine how input bits are moved around through the rounds, however we made an educated guess that low rounds number and setup of the task might hint at existence of square property there. Round keys are some randoms generated from static seed we don't know. Substitution is just a static sbox and so is the permutation. We don't care about the mode, because we will work only with single blocks.īa_block = tes_to_bitarray(block, self.BLOCK_SIZE*8)īa_block = self.keyXor(ba_block, self.keys)īa_block = self.keyXor(ba_block, last_XOR_key) We have SPN cipher with 8-bytes blocks and 4 rounds and only ECB mode. Our goal is to recover the last XOR key applied to the ciphertext. We can send as many payloads we want to the server and the server will return encrypted ciphertext. In the task we get access to a service running a custom ( ).