XOR cipher where the key is repeated to match the length of the message.

Introduced properties

fun find_key(min_keylength: nullable Int, max_keylength: nullable Int, considered_keylength_count: nullable Int)

crypto :: RepeatingKeyXorCipher :: find_key

Attempts to find the key by using frequency analysis on the resulting plaintexts.
fun key: Bytes

crypto :: RepeatingKeyXorCipher :: key

Cryptographic key used in encryption and decryption.
protected fun key=(key: Bytes)

crypto :: RepeatingKeyXorCipher :: key=

Cryptographic key used in encryption and decryption.

Redefined properties

redef type SELF: RepeatingKeyXorCipher

crypto $ RepeatingKeyXorCipher :: SELF

Type of this instance, automatically specialized in every class
redef fun decrypt

crypto $ RepeatingKeyXorCipher :: decrypt

Decrypt ciphertext and populate self.plaintext
redef fun encrypt

crypto $ RepeatingKeyXorCipher :: encrypt

Encrypt plaintext and populate self.ciphertext

All properties

fun !=(other: nullable Object): Bool

core :: Object :: !=

Have self and other different values?
fun ==(other: nullable Object): Bool

core :: Object :: ==

Have self and other the same value?
type CLASS: Class[SELF]

core :: Object :: CLASS

The type of the class of self.
type SELF: Object

core :: Object :: SELF

Type of this instance, automatically specialized in every class
fun ciphertext: Bytes

crypto :: Cipher :: ciphertext

Encrypted data
fun ciphertext=(ciphertext: Bytes)

crypto :: Cipher :: ciphertext=

Encrypted data
protected fun class_factory(name: String): CLASS

core :: Object :: class_factory

Implementation used by get_class to create the specific class.
fun class_name: String

core :: Object :: class_name

The class name of the object.
abstract fun decrypt

crypto :: Cipher :: decrypt

Decrypt ciphertext and populate self.plaintext
abstract fun encrypt

crypto :: Cipher :: encrypt

Encrypt plaintext and populate self.ciphertext
fun find_key(min_keylength: nullable Int, max_keylength: nullable Int, considered_keylength_count: nullable Int)

crypto :: RepeatingKeyXorCipher :: find_key

Attempts to find the key by using frequency analysis on the resulting plaintexts.
fun get_class: CLASS

core :: Object :: get_class

The meta-object representing the dynamic type of self.
fun hash: Int

core :: Object :: hash

The hash code of the object.
init init

core :: Object :: init

fun inspect: String

core :: Object :: inspect

Developer readable representation of self.
protected fun inspect_head: String

core :: Object :: inspect_head

Return "CLASSNAME:#OBJECTID".
intern fun is_same_instance(other: nullable Object): Bool

core :: Object :: is_same_instance

Return true if self and other are the same instance (i.e. same identity).
fun is_same_serialized(other: nullable Object): Bool

core :: Object :: is_same_serialized

Is self the same as other in a serialization context?
intern fun is_same_type(other: Object): Bool

core :: Object :: is_same_type

Return true if self and other have the same dynamic type.
fun key: Bytes

crypto :: RepeatingKeyXorCipher :: key

Cryptographic key used in encryption and decryption.
protected fun key=(key: Bytes)

crypto :: RepeatingKeyXorCipher :: key=

Cryptographic key used in encryption and decryption.
intern fun object_id: Int

core :: Object :: object_id

An internal hash code for the object based on its identity.
fun output

core :: Object :: output

Display self on stdout (debug only).
intern fun output_class_name

core :: Object :: output_class_name

Display class name on stdout (debug only).
fun plaintext: Bytes

crypto :: Cipher :: plaintext

Unencrypted data
fun plaintext=(plaintext: Bytes)

crypto :: Cipher :: plaintext=

Unencrypted data
fun serialization_hash: Int

core :: Object :: serialization_hash

Hash value use for serialization
intern fun sys: Sys

core :: Object :: sys

Return the global sys object, the only instance of the Sys class.
abstract fun to_jvalue(env: JniEnv): JValue

core :: Object :: to_jvalue

fun to_s: String

core :: Object :: to_s

User readable representation of self.
package_diagram crypto::RepeatingKeyXorCipher RepeatingKeyXorCipher crypto::Cipher Cipher crypto::RepeatingKeyXorCipher->crypto::Cipher core::Object Object crypto::Cipher->core::Object ...core::Object ... ...core::Object->core::Object

Ancestors

interface Object

core :: Object

The root of the class hierarchy.

Parents

abstract class Cipher

crypto :: Cipher

Base class to modelize cryptographic ciphers

Class definitions

crypto $ RepeatingKeyXorCipher
# XOR cipher where the key is repeated to match the length of the message.
class RepeatingKeyXorCipher
	super Cipher

	# Cryptographic key used in encryption and decryption.
	var key = new Bytes.empty

	redef fun encrypt do
		assert key.length > 0
		ciphertext = plaintext.xorcipher(key)
	end

	redef fun decrypt do
		assert key.length > 0
		plaintext = ciphertext.xorcipher(key)
	end
end
lib/crypto/xor_ciphers.nit:87,1--103,3

crapto :: xor $ RepeatingKeyXorCipher
redef class RepeatingKeyXorCipher
	# 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

	# Computes the normalized hamming distances between blocks of ciphertext of length between `min_length` and `max_length`.
	# The `considered_keylength_count` smallest results are returned
	private fun get_normalized_hamming_distances(min_keylength, max_keylength, considered_keylength_count: Int): Array[Int] do

		var normalized_distances = new HashMap[Float, Int]

		# Iterate over all given key lengths
		for ks in [min_keylength .. max_keylength[ do

			# Initialize the blocks of size `ks`
			var blocks = new Array[Bytes]
			while (blocks.length + 1) * ks < ciphertext.length do
				blocks.add(ciphertext.slice(blocks.length * ks, ks))
			end

			# Compute the normalized hamming distance with all block combinations
			var pairs = new CombinationCollection[Bytes](blocks, 2)
			var hamming_dists = new Array[Float]
			for p in pairs do
				hamming_dists.add(p[0].hamming_distance(p[1]).to_f / ks.to_f)
			end

			# Normalize the results based on the number of blocks
			var normalized = 0.0
			for dist in hamming_dists do normalized += dist
			normalized /= hamming_dists.length.to_f
			normalized_distances[normalized] = ks

		end

		# Collect the best candidates
		var distances = normalized_distances.keys.to_a
		default_comparator.sort(distances)
		var best_distances = distances.subarray(0, considered_keylength_count)
		var best_sizes = [for d in best_distances do normalized_distances[d]]

		return best_sizes
	end

	# Returns a rearranged format of the ciphertext where every byte of a slot will be XORed with the same offset of a key of length `keylength`.
	private fun transpose_ciphertext(keylength: Int): Array[Bytes] do
		var transposed = new Array[Bytes]
		for i in [0 .. keylength[ do
			transposed.add(new Bytes.empty)
		end

		for byte_idx in [0 .. ciphertext.length[ do
			transposed[byte_idx % keylength].add(ciphertext[byte_idx])
		end

		return transposed
	end
end
lib/crapto/xor.nit:51,1--150,3