title: How to break repeating-key XOR Challenge using Single-byte XOR cipher
body: <p>This Question is about <a href="https://cryptopals.com/sets/1/challenges/6" rel="nofollow noreferrer">challenge number 6</a> in <a href="https://cryptopals.com/sets/1" rel="nofollow noreferrer">set number 1</a> in the challenges of <a href="https://cryptopals.com/" rel="nofollow noreferrer">&quot;the cryptopals crypto challenges&quot;</a>.</p>
  <hr />
  <p>The challenge is:</p>
  <blockquote>
  <p><a href="https://cryptopals.com/static/challenge-data/6.txt" rel="nofollow noreferrer">There's a file</a> here. It's been base64'd after being encrypted with repeating-key XOR.</p>
  <p>Decrypt it.</p>
  </blockquote>
  <p>After that there's a description of steps to decrypt the file, There is total of 8 steps. You can find them in <a href="https://cryptopals.com/sets/1/challenges/6" rel="nofollow noreferrer">the site</a>.</p>
  <hr />
  <p>I have been trying to solve this challenge for a while and I am struggling with the final two steps. Even though I've solved challenge number 3, and it contains the solution for these steps.</p>
  <p>Note: It is, of course, possible that there is a mistake in the first 6 steps but they seems to work well after looking at the <code>print</code> after every step.</p>
  <hr />
  <p>My code:</p>
  <p>Written in <code>Python 3.6</code>.</p>
  <p>In order to not deal with web requests, and since it is not the purpose of this challenge. I just copied the content of the file to a string in the begging, You can do this as well before running the code.</p>
  <pre><code>import base64
  
  # Encoding the file from base64 to binary
  file = base64.b64decode(&quot;&quot;&quot;HUIfTQsP...JwwRTWM=&quot;&quot;&quot;)
  print(file)
  print()
  
  # Step 1 - guess key size
  KEYSIZE = 4
  
  
  # Step 2 - find hamming distance - number of differing bits
  def hamming2(s1, s2):
      &quot;&quot;&quot;Calculate the Hamming distance between two bit strings&quot;&quot;&quot;
      assert len(s1) == len(s2)
      return sum(c1 != c2 for c1, c2 in zip(s1, s2))
  
  
  def distance(a, b):  # Hamming distance
      calc = 0
      for ca, cb in [(a[i], b[i]) for i in range(len(a))]:
          bina = '{:08b}'.format(int(ca))
          binb = '{:08b}'.format(int(cb))
          calc += hamming2(bina, binb)
      return calc
  
  # Test step 2
  print(&quot;distance: 'this is a test' and 'wokka wokka!!!' =&quot;, distance([ord(c) for c in &quot;this is a test&quot;], [ord(c) for c in &quot;wokka wokka!!!&quot;]))  # 37 - Working
  print()
  
  
  # Step 3
  key_sizes = []
  # For each key size
  for KEYSIZE in range(2, 41):
      # take the first KEYSIZE worth of bytes, and the second KEYSIZE worth of bytes -
      # file[0:KEYSIZE], file[KEYSIZE:2*KEYSIZE]
      # and find the edit distance between them
      # Normalize this result by dividing by KEYSIZE
      key_sizes.append((distance(file[0:KEYSIZE], file[KEYSIZE:2*KEYSIZE]) / KEYSIZE, KEYSIZE))
  key_sizes.sort(key=lambda a: a[0])
  
  
  # Step 4
  for val, key in key_sizes:
      print(key, &quot;:&quot;, val)
  KEYSIZE = key_sizes[0][1]
  print()
  
  
  # Step 5 + 6
  # Each line is a list of all the bytes in that index
  splited_file = [[] for i in range(KEYSIZE)]
  counter = 0
  for char in file:
      splited_file[counter].append(char)
      counter += 1
      counter %= KEYSIZE
  for line in splited_file:
      print(line)
  print()
  
  
  # Step 7
  # Code from another level
  # Gets a string and a single char
  # Doing a single-byte XOR over it
  def single_char_string(a, b):
      final = &quot;&quot;
      for c in a:
          final += chr(c ^ b)
      return final
  
  
  # Going over all the bytes and listing the result arter the XOR by number of bytes
  def find_single_byte(in_string):
      helper_list = []
      for num in range(256):
          helper_list.append((single_char_string(in_string, num), num))
      helper_list.sort(key=lambda a: a[0].count(' '), reverse=True)
      return helper_list[0]
  
  # Step 8
  final_key = &quot;&quot;
  key_list = []
  for line in splited_file:
      result = find_single_byte(line)
      print(result)
      final_key += chr(result[1])
      key_list.append(result[1])
  print(final_key)
  print(key_list)
  </code></pre>
  <p>Output:</p>
  <pre><code>b'\x1dB\x1fM\x0b\x0f\x02\x1fO\x13N&lt;\x1aie\x1fI...\x08VA;R\x1d\x06\x06TT\x0e\x10N\x05\x16I\x1e\x10\'\x0c\x11Mc'
  
  distance: 'this is a test' and 'wokka wokka!!!' = 37
  
  5 : 1.2
  3 : 2.0
  2 : 2.5
  .
  .
  .
  26 : 3.5
  28 : 3.5357142857142856
  9 : 3.5555555555555554
  22 : 3.727272727272727
  6 : 4.0
  
  [29, 15, 78, 31, 19, 27, 0, 32, ... 17, 26, 78, 38, 28, 2, 1, 65, 6, 78, 16, 99]
  [66, 2, 60, 73, 1, 1, 30, 3, 13, ... 26, 14, 0, 26, 79, 99, 8, 79, 11, 4, 82, 59, 84, 5, 39]
  [31, 31, 19, 26, 79, 47, 17, 28, ... 71, 89, 12, 1, 16, 45, 78, 3, 120, 11, 42, 82, 84, 22, 12]
  [77, 79, 105, 14, 7, 69, 73, 29, 101, ... 54, 70, 78, 55, 7, 79, 31, 88, 10, 69, 65, 8, 29, 14, 73, 17]
  [11, 19, 101, 78, 78, 54, 100, 67, 82, ... 1, 76, 26, 1, 2, 73, 21, 72, 73, 49, 27, 86, 6, 16, 30, 77]
  
  ('=/n?3; \x00\x13&amp;-,&gt;1...r1:n\x06&lt;&quot;!a&amp;n0C', 32)
  ('b&quot;\x1ci!!&gt;ts es(ogg ...5i&lt;% tc:. :oC(o+$r\x1bt%\x07', 32)
  ('??:&lt;+6!=ngm2i4\x0byD...&amp;h9&amp;2:-)sm.a)u\x06&amp;=\x0ct&amp;~n +=&amp;*4X:&lt;(3:o\x0f1&lt;mE gy,!0\rn#X+\nrt6,', 32)
  ('moI.\'ei=Et\'\x1c:l ...6k=\x1b m~t*\x155\x1ei+=+ts/e*9$sgl0\'\x02\x16fn\x17\'o?x*ea(=.i1', 32)
  ('+3Enn\x16Dcr&lt;$,)\x01...i5\x01,hi\x11;v&amp;0&gt;m', 32)
   
  [32, 32, 32, 32, 32]
  </code></pre>
  <p>Notice that in the printing of the key as string you cannot see it but there is 5 chars in there.</p>
  <hr />
  <p>It is not the correct answer since you can see that in the forth part - after the XOR, the results do not look like words... Probably a problem in the last two functions but I couldn't figure it out.</p>
  <p>I've also tried some other lengths and It does not seems to be the problem.</p>
  <hr />
  <p>So what I'm asking is not to fix my code, I want to solve this challenge by myself :). I would like you to tell me where I am wrong? why? and how should I continue?</p>
  <p>Thank you for your help.</p>