Best key lengths are estimated using the hamming distance of blocks of keylength bytes. Ciphertext is then transposed in such a way that it can be solved by sequences of SingleByteXor (one for each offset in the key).
min_keylength
and max_keylength
are limits as to what key lengths are tested.
considered_keylength_count
is the number of best key lengths that are kept to be
analysed by the SingleByteXor frequency analysis.
# Attempts to find the key by using frequency analysis on the resulting plaintexts.
# Best key lengths are estimated using the hamming distance of blocks of keylength bytes.
# Ciphertext is then transposed in such a way that it can be solved by sequences of
# SingleByteXor (one for each offset in the key).
#
# `min_keylength` and `max_keylength` are limits as to what key lengths are tested.
# `considered_keylength_count` is the number of best key lengths that are kept to be
# analysed by the SingleByteXor frequency analysis.
fun find_key(min_keylength, max_keylength, considered_keylength_count: nullable Int) do
# Set default values
if min_keylength == null then min_keylength = 2
if max_keylength == null then max_keylength = 40
if considered_keylength_count == null then considered_keylength_count = 3
# Find the best candidate key lengths based on the normalized hamming distances
var best_sizes = get_normalized_hamming_distances(min_keylength, max_keylength, considered_keylength_count)
var best = 0.0
var best_key: nullable Bytes = null
for ks in best_sizes do
# Rearrange ciphertext to be in SingleByteXORable blocks
var transposed = transpose_ciphertext(ks)
var key = new Bytes.empty
for slot in transposed do
var sbx = new SingleByteXorCipher
sbx.ciphertext = slot
sbx.find_key
key.add sbx.key
end
# Evaluate the resulting plaintext based on english frequency analysis
var eng_score = ciphertext.xorcipher(key).to_s.english_scoring
if eng_score > best then
best_key = key
best = eng_score
end
assert best_key != null
self.key = best_key
end
end
lib/crapto/xor.nit:52,2--96,4