密码学大作业

0930

1.题目:

Many Time Pad

Let us see what goes wrong when a stream cipher key is used more than once.  Below are eleven hex-encoded ciphertexts that are the result of encrypting eleven plaintexts with a stream cipher, all with the same stream cipher key.  Your goal is to decrypt the last ciphertext, and submit the secret message within it as solution.

Hint: XOR the ciphertexts together, and consider what happens when a space is XORed with a character in [a-zA-Z].

ciphertext #1:

315c4eeaa8b5f8aaf9174145bf43e1784b8fa00dc71d885a804e5ee9fa40b16349c146fb778cdf2d3aff021dfff5b403b510d0d0455468aeb98622b137dae857553ccd8883a7bc37520e06e515d22c954eba5025b8cc57ee59418ce7dc6bc41556bdb36bbca3e8774301fbcaa3b83b220809560987815f65286764703de0f3d524400a19b159610b11ef3e

ciphertext #2:

  234c02ecbbfbafa3ed18510abd11fa724fcda2018a1a8342cf064bbde548b12b07df44ba7191d9606ef4081ffde5ad46a5069d9f7f543bedb9c861bf29c7e205132eda9382b0bc2c5c4b45f919cf3a9f1cb74151f6d551f4480c82b2cb24cc5b028aa76eb7b4ab24171ab3cdadb8356f

ciphertext #3:

  32510ba9a7b2bba9b8005d43a304b5714cc0bb0c8a34884dd91304b8ad40b62b07df44ba6e9d8a2368e51d04e0e7b207b70b9b8261112bacb6c866a232dfe257527dc29398f5f3251a0d47e503c66e935de81230b59b7afb5f41afa8d661cb

ciphertext #4:

   32510ba9aab2a8a4fd06414fb517b5605cc0aa0dc91a8908c2064ba8ad5ea06a029056f47a8ad3306ef5021eafe1ac01a81197847a5c68a1b78769a37bc8f4575432c198ccb4ef63590256e305cd3a9544ee4160ead45aef520489e7da7d835402bca670bda8eb775200b8dabbba246b130f040d8ec6447e2c767f3d30ed81ea2e4c1404e1315a1010e7229be6636aaa

ciphertext #5:

   3f561ba9adb4b6ebec54424ba317b564418fac0dd35f8c08d31a1fe9e24fe56808c213f17c81d9607cee021dafe1e001b21ade877a5e68bea88d61b93ac5ee0d562e8e9582f5ef375f0a4ae20ed86e935de81230b59b73fb4302cd95d770c65b40aaa065f2a5e33a5a0bb5dcaba43722130f042f8ec85b7c2070

ciphertext #6:

    32510bfbacfbb9befd54415da243e1695ecabd58c519cd4bd2061bbde24eb76a19d84aba34d8de287be84d07e7e9a30ee714979c7e1123a8bd9822a33ecaf512472e8e8f8db3f9635c1949e640c621854eba0d79eccf52ff111284b4cc61d11902aebc66f2b2e436434eacc0aba938220b084800c2ca4e693522643573b2c4ce35050b0cf774201f0fe52ac9f26d71b6cf61a711cc229f77ace7aa88a2f19983122b11be87a59c355d25f8e4

ciphertext #7:

  32510bfbacfbb9befd54415da243e1695ecabd58c519cd4bd90f1fa6ea5ba47b01c909ba7696cf606ef40c04afe1ac0aa8148dd066592ded9f8774b529c7ea125d298e8883f5e9305f4b44f915cb2bd05af51373fd9b4af511039fa2d96f83414aaaf261bda2e97b170fb5cce2a53e675c154c0d9681596934777e2275b381ce2e40582afe67650b13e72287ff2270abcf73bb028932836fbdecfecee0a3b894473c1bbeb6b4913a536ce4f9b13f1efff71ea313c8661dd9a4ce

ciphertext #8:

   315c4eeaa8b5f8bffd11155ea506b56041c6a00c8a08854dd21a4bbde54ce56801d943ba708b8a3574f40c00fff9e00fa1439fd0654327a3bfc860b92f89ee04132ecb9298f5fd2d5e4b45e40ecc3b9d59e9417df7c95bba410e9aa2ca24c5474da2f276baa3ac325918b2daada43d6712150441c2e04f6565517f317da9d3

ciphertext #9:

  271946f9bbb2aeadec111841a81abc300ecaa01bd8069d5cc91005e9fe4aad6e04d513e96d99de2569bc5e50eeeca709b50a8a987f4264edb6896fb537d0a716132ddc938fb0f836480e06ed0fcd6e9759f40462f9cf57f4564186a2c1778f1543efa270bda5e933421cbe88a4a52222190f471e9bd15f652b653b7071aec59a2705081ffe72651d08f822c9ed6d76e48b63ab15d0208573a7eef027

ciphertext #10:

   466d06ece998b7a2fb1d464fed2ced7641ddaa3cc31c9941cf110abbf409ed39598005b3399ccfafb61d0315fca0a314be138a9f32503bedac8067f03adbf3575c3b8edc9ba7f537530541ab0f9f3cd04ff50d66f1d559ba520e89a2cb2a83

target ciphertext (decrypt this one):

32510ba9babebbbefd001547a810e67149caee11d945cd7fc81a05e9f85aac650e9052ba6a8cd8257bf14d13e6f0a803b54fde9e77472dbff89d71b57bddef121336cb85ccb8f3315f4b52e301d16e9f52f904

思路:

流密码的多次一密是不满足CPA语义安全的,无法抵抗CPA攻击。因为流密码加密满足以下原理:

mi⊕ki=ci

因此在多次一密的情况下,满足以下公式:

mi⊕mj=ci⊕cj

由ASCII码的性质可知,空格与字母异或得到的是字母的大小写转换,字母之间异或得到的不是字母。因此可以将11个密文分别做两两xor操作,然后通过结果判断不同明文中可能存在空格的位置,然后将对应位置上的密文和space做xor操作,就可得到对应位置的密钥信息,当获取足够多的密钥信息后,即可对目标密文进行解密。

代码:

import sys  

 

ciphertexts=[  

"315c4eeaa8b5f8aaf9174145bf43e1784b8fa00dc71d885a804e5ee9fa40b16349c146fb778cdf2d3aff021dfff5b403b510d0d0455468aeb98622b137dae857553ccd8883a7bc37520e06e515d22c954eba5025b8cc57ee59418ce7dc6bc41556bdb36bbca3e8774301fbcaa3b83b220809560987815f65286764703de0f3d524400a19b159610b11ef3e",  

"234c02ecbbfbafa3ed18510abd11fa724fcda2018a1a8342cf064bbde548b12b07df44ba7191d9606ef4081ffde5ad46a5069d9f7f543bedb9c861bf29c7e205132eda9382b0bc2c5c4b45f919cf3a9f1cb74151f6d551f4480c82b2cb24cc5b028aa76eb7b4ab24171ab3cdadb8356f",  

"32510ba9a7b2bba9b8005d43a304b5714cc0bb0c8a34884dd91304b8ad40b62b07df44ba6e9d8a2368e51d04e0e7b207b70b9b8261112bacb6c866a232dfe257527dc29398f5f3251a0d47e503c66e935de81230b59b7afb5f41afa8d661cb",  

"32510ba9aab2a8a4fd06414fb517b5605cc0aa0dc91a8908c2064ba8ad5ea06a029056f47a8ad3306ef5021eafe1ac01a81197847a5c68a1b78769a37bc8f4575432c198ccb4ef63590256e305cd3a9544ee4160ead45aef520489e7da7d835402bca670bda8eb775200b8dabbba246b130f040d8ec6447e2c767f3d30ed81ea2e4c1404e1315a1010e7229be6636aaa",  

"3f561ba9adb4b6ebec54424ba317b564418fac0dd35f8c08d31a1fe9e24fe56808c213f17c81d9607cee021dafe1e001b21ade877a5e68bea88d61b93ac5ee0d562e8e9582f5ef375f0a4ae20ed86e935de81230b59b73fb4302cd95d770c65b40aaa065f2a5e33a5a0bb5dcaba43722130f042f8ec85b7c2070",  

"32510bfbacfbb9befd54415da243e1695ecabd58c519cd4bd2061bbde24eb76a19d84aba34d8de287be84d07e7e9a30ee714979c7e1123a8bd9822a33ecaf512472e8e8f8db3f9635c1949e640c621854eba0d79eccf52ff111284b4cc61d11902aebc66f2b2e436434eacc0aba938220b084800c2ca4e693522643573b2c4ce35050b0cf774201f0fe52ac9f26d71b6cf61a711cc229f77ace7aa88a2f19983122b11be87a59c355d25f8e4",  

"32510bfbacfbb9befd54415da243e1695ecabd58c519cd4bd90f1fa6ea5ba47b01c909ba7696cf606ef40c04afe1ac0aa8148dd066592ded9f8774b529c7ea125d298e8883f5e9305f4b44f915cb2bd05af51373fd9b4af511039fa2d96f83414aaaf261bda2e97b170fb5cce2a53e675c154c0d9681596934777e2275b381ce2e40582afe67650b13e72287ff2270abcf73bb028932836fbdecfecee0a3b894473c1bbeb6b4913a536ce4f9b13f1efff71ea313c8661dd9a4ce",  

"315c4eeaa8b5f8bffd11155ea506b56041c6a00c8a08854dd21a4bbde54ce56801d943ba708b8a3574f40c00fff9e00fa1439fd0654327a3bfc860b92f89ee04132ecb9298f5fd2d5e4b45e40ecc3b9d59e9417df7c95bba410e9aa2ca24c5474da2f276baa3ac325918b2daada43d6712150441c2e04f6565517f317da9d3",  

"271946f9bbb2aeadec111841a81abc300ecaa01bd8069d5cc91005e9fe4aad6e04d513e96d99de2569bc5e50eeeca709b50a8a987f4264edb6896fb537d0a716132ddc938fb0f836480e06ed0fcd6e9759f40462f9cf57f4564186a2c1778f1543efa270bda5e933421cbe88a4a52222190f471e9bd15f652b653b7071aec59a2705081ffe72651d08f822c9ed6d76e48b63ab15d0208573a7eef027",  

"466d06ece998b7a2fb1d464fed2ced7641ddaa3cc31c9941cf110abbf409ed39598005b3399ccfafb61d0315fca0a314be138a9f32503bedac8067f03adbf3575c3b8edc9ba7f537530541ab0f9f3cd04ff50d66f1d559ba520e89a2cb2a83",  

"32510ba9babebbbefd001547a810e67149caee11d945cd7fc81a05e9f85aac650e9052ba6a8cd8257bf14d13e6f0a803b54fde9e77472dbff89d71b57bddef121336cb85ccb8f3315f4b52e301d16e9f52f904"  

]  





NUM_CIPHER=len(ciphertexts)

THRESHOLD_VALUE=6





def strxor(a, b):

    """xor two strings of different lengths"""  

    if len(a) > len(b):  

        return "".join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a[:len(b)], b)])  

    else:  

        return "".join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a, b[:len(a)])])  





def letter_position(s):

    """ Return the position of letters in the given string """  

    position=[]

    for idx in range(len(s)):  

        if (s[idx]>='A' and s[idx]<='Z') or (s[idx]>='a' and s[idx]<='z'):  

            position.append(idx)  

    return position  

 

def find_space(cipher):

    """Find the position of space"""

    space_position={}

    space_possible={}  

    for cipher_idx_1 in range(NUM_CIPHER):  

        space_xor=[]  

        for cipher_idx_2 in range(NUM_CIPHER):  

            plain_xor=strxor(cipher[cipher_idx_1].decode('hex'),cipher[cipher_idx_2].decode('hex'))

            if cipher_idx_2!=cipher_idx_1:  

                space_xor.append(letter_position(plain_xor)) # record the possible value of space

        space_possible[cipher_idx_1]=space_xor

    for cipher_idx_1 in range(NUM_CIPHER):  

        spa=[]  

        for position in range(100):  

            count=0

            for cipher_idx_2 in range(NUM_CIPHER-1):  

                if position in space_possible[cipher_idx_1][cipher_idx_2]:  

                    count+=1  

            if count>THRESHOLD_VALUE: # if possible position value appear more than THRESHOLD_VALUE times,

                                      # we consider it as a space position

                spa.append(position)

        space_position[cipher_idx_1]=spa;  

    return space_position





def calculate_key(cipher):

    key=[0]*100

    space=find_space(cipher)  

    for cipher_idx_1 in range(NUM_CIPHER):  

        for position in range(len(space[cipher_idx_1])):  

            idx=space[cipher_idx_1][position]*2; # ciphertext is hex-encoded, so its scale times two

            key[space[cipher_idx_1][position]]=ord((ciphertexts[cipher_idx_1][idx]+ciphertexts[cipher_idx_1][idx+1]).decode('hex'))^ord(' '); #derive key  

    key_str="";  

    for k in key:  

        key_str+=chr(k);

    return key_str  

 

result="";

key=calculate_key(ciphertexts)

for i in range(11):

    for letter in strxor(ciphertexts[i].decode('hex'),key): # decrypt the target cipher

        if (letter>='a' and letter<='z') or (letter>='A' and letter<='Z'):  

            result+=letter;  

        elif letter==' ':  

            result+=letter;  

        else:  

            result+='0';

    print result;

    print '\n'

result=''

得到第八组明文为:

A 0privfte0key0  encrypti0n schwme0 tates 0 algorithms0 namely a procedure for generatWng ke0s00C 0p

    因为截获的密文组较少,因此有一定偏差,认为校正后为:

A (private-key)  encryption scheme states 3 algorithms, namely a procedure for generating keys, a procedure for encrypting, and a procedure for decrypting.?

    与密文异或得到key:

66396e89c9dbd8cc9874352acd6395102eafce78aa7fed28a07f6bc98d29c50b69b0339a19f8aa401a9c6d708f80c066c763fef0123148cdd8e802d05ba98777335daefcecd59c433a6b268b60bf4ef03c9a611098bb3e9a3161edc7b804a33522cfd202d2c68c57376edba8c2ca50027c61246ce2a12b0c4502175010c0a1ba4625786d911100797d8a47e98b0204c4ef06c867a950f11ac989dea88fd1dbf16748749ed4c6f45b384c9d96c4

2.题目

Write a program that allows you to "crack" ciphertexts generated using a Vigenere-like cipher, where byte-wise XOR is used instead of addition modulo 26.

Specifically, the ciphertext

F96DE8C227A259C87EE1DA2AED57C93FE5DA36ED4EC87EF2C63AAE5B9A7EFFD673BE4ACF7BE8923CAB1ECE7AF2DA3DA44FCF7AE29235A24C963FF0DF3CA3599A70E5DA36BF1ECE77F8DC34BE129A6CF4D126BF5B9A7CFEDF3EB850D37CF0C63AA2509A76FF9227A55B9A6FE3D720A850D97AB1DD35ED5FCE6BF0D138A84CC931B1F121B44ECE70F6C032BD56C33FF9D320ED5CDF7AFF9226BE5BDE3FF7DD21ED56CF71F5C036A94D963FF8D473A351CE3FE5DA3CB84DDB71F5C17FED51DC3FE8D732BF4D963FF3C727ED4AC87EF5DB27A451D47EFD9230BF47CA6BFEC12ABE4ADF72E29224A84CDF3FF5D720A459D47AF59232A35A9A7AE7D33FB85FCE7AF5923AA31EDB3FF7D33ABF52C33FF0D673A551D93FFCD33DA35BC831B1F43CBF1EDF67F0DF23A15B963FE5DA36ED68D378F4DC36BF5B9A7AFFD121B44ECE76FEDC73BE5DD27AFCD773BA5FC93FE5DA3CB859D26BB1C63CED5CDF3FE2D730B84CDF3FF7DD21ED5ADF7CF0D636BE1EDB79E5D721ED57CE3FE6D320ED57D469F4DC27A85A963FF3C727ED49DF3FFFDD24ED55D470E69E73AC50DE3FE5DA3ABE1EDF67F4C030A44DDF3FF5D73EA250C96BE3D327A84D963FE5DA32B91ED36BB1D132A31ED87AB1D021A255DF71B1C436BF479A7AF0C13AA14794

   

思路:

设密钥的长度为x,密文为c,则c[i],c[i+x],c[i+2x]……都是明文与同一个字符异或加密的,因此可以遍历密钥的每一个字符的每一个ASCII码可能值,直到对应的明文都是字母或标点为止

代码:


import string
def findindexkey2(subarr):#再造一个函数筛选密钥
    test_chars=string.ascii_letters+string.digits+','+'.'+' '#将检查的字符改为英文+数字+逗号+句号+空格
    #print(test_chars)
    test_keys=[]#用于测试密钥
    ans_keys=[]#用于结果的返回
    for x in range(0x00,0xFF):# 枚举密钥里所有的值
        test_keys.append(x)
        ans_keys.append(x)
    for i in test_keys:#对于0x00~0xFF里的每一个数i和substr里的每个值s异或
        for s in subarr:
            if chr(s^i) not in test_chars:#用i解密s,如果解密后不是英文、数字、逗号、句号、空格,说明i不是密钥
                ans_keys.remove(i)#去掉ans_keys里测试失败的密钥
                break
    #print(ans_keys)
    return ans_keys

strmi='F96DE8C227A259C87EE1DA2AED57C93FE5DA36ED4EC87EF2C63AAE5B9A7EFFD673BE4ACF7BE8923C\
AB1ECE7AF2DA3DA44FCF7AE29235A24C963FF0DF3CA3599A70E5DA36BF1ECE77F8DC34BE129A6CF4D126BF\
5B9A7CFEDF3EB850D37CF0C63AA2509A76FF9227A55B9A6FE3D720A850D97AB1DD35ED5FCE6BF0D138A84C\
C931B1F121B44ECE70F6C032BD56C33FF9D320ED5CDF7AFF9226BE5BDE3FF7DD21ED56CF71F5C036A94D96\
3FF8D473A351CE3FE5DA3CB84DDB71F5C17FED51DC3FE8D732BF4D963FF3C727ED4AC87EF5DB27A451D47E\
FD9230BF47CA6BFEC12ABE4ADF72E29224A84CDF3FF5D720A459D47AF59232A35A9A7AE7D33FB85FCE7AF5\
923AA31EDB3FF7D33ABF52C33FF0D673A551D93FFCD33DA35BC831B1F43CBF1EDF67F0DF23A15B963FE5DA\
36ED68D378F4DC36BF5B9A7AFFD121B44ECE76FEDC73BE5DD27AFCD773BA5FC93FE5DA3CB859D26BB1C63C\
ED5CDF3FE2D730B84CDF3FF7DD21ED5ADF7CF0D636BE1EDB79E5D721ED57CE3FE6D320ED57D469F4DC27A8\
5A963FF3C727ED49DF3FFFDD24ED55D470E69E73AC50DE3FE5DA3ABE1EDF67F4C030A44DDF3FF5D73EA250\
C96BE3D327A84D963FE5DA32B91ED36BB1D132A31ED87AB1D021A255DF71B1C436BF479A7AF0C13AA14794'
arr=[]#密文,每个元素为字符的ascii码
for x in range(0,len(strmi),2):
    arr.append(int(strmi[x:2+x],16))

vigenerekeys=[]#维基尼尔密码的密钥
for index in range(0,7):#已经知道密钥长度是7
    subarr=arr[index::7]
    vigenerekeys.append(findindexkey2(subarr))

ming=''
for i in range(0,len(arr)):
    ming=ming+chr(arr[i]^vigenerekeys[i%7][0])
print(ming)

结果:

Cryptography is the practice and study of techniques for, among other things, secure communication in the presence of attackers. Cryptography has been used for hundreds, if not thousands, of years, but traditional cryptosystems were designed and evaluated in a fairly ad hoc manner. For example, the Vigenere encryption scheme was thought to be secure for decades after it was invented, but we now know, and this exercise demonstrates, that it can be broken very easily.

3.题目:

Challenge 6 Set 1 - The Cryptopals Crypto Challenges

思路:

遍历所有可能的秘钥长度,针对计算所有假设出来的秘钥长度,计算对应这些秘钥长度的密文的汉明距离,汉明距离最小的即为正确的秘钥长度。

随后根据秘钥长度,将密文分成大小相等的组,每组对应位置的密文再分别合并成组,因为他们是由相同的秘钥加密而来。

根据英文中出现概率最高的几个字母分别为“etaoin shrdlu”,来确定秘钥的每一位,最终得出秘钥,进而得出明文。

代码:

from subprocess import check_output as run

from base64 import b64decode

def key_sizes():

  """Generate a list of possible key sizes."""

  start = 2

  end = 40

  return list(range(start, end + 1))

def ascii_to_bytes(text):

  """Convert ASCII text to bytes."""

  return bytearray.fromhex(text.encode('utf-8').hex())

def xor_matching(a, b):

  """XOR two sets of bytes with matching lengths."""

  assert len(a) == len(b), 'attempting to XOR with elements of different lengths'

  return [a[i] ^ b[i] for i, x in enumerate(a)]

def hamming_distance(a, b):

  """Compute the Hamming distance between two inputs."""

  xor_bytes = xor_matching(a, b)

  binary_bytes = [bin(i)[2:] for i in xor_bytes]

  binary_string = ''.join(binary_bytes)

  binary = list(map(int, list(binary_string)))

  count = sum(binary)

  return count

assert hamming_distance(ascii_to_bytes('this is a test'), ascii_to_bytes('wokka wokka!!!')) == 37, "incorrect Hamming distance calculation"

def split_chunks(iterable, chunk_size):

  """Split an iterable into chunks of a specified size"""

  chunks = [

    iterable[i:i + chunk_size]

    for i

    in range(0, len(iterable), chunk_size)

    if i < len(iterable) - chunk_size

  ]

  return chunks

def normalized_hamming_distance(text, key_size):

  """Given a key size, compute the normalized hamming distance for two strings."""

  assert key_size < len(text) / 2, 'text is too short to provide two blocks at this key size'

  bytelist = b64decode(text)

  assert isinstance(bytelist, (bytes, bytearray)), 'hamming distance must be calculated with raw bytes'

  # break cipher text into chunks

  chunks = split_chunks(bytelist, key_size)

  # select two leading blocks

  blocks = [

    bytelist[0:key_size],

    bytelist[key_size:key_size * 2]

  ]

  # quadratic nested comprehensions

  hamming_distances = [

    [hamming_distance(block, chunk) for chunk in chunks]

    for block

    in blocks

  ][0] # this results in a nested array, so pull out the meaningful element

  # average all Hamming distances

  mean = sum(hamming_distances) / len(hamming_distances)

  # normalize by key size to further constrain deviations in the mean

  normalized = mean / key_size

  return normalized

def smallest(values):

  """Find the key sizes corresponding to the smallest Hamming distances in a list."""

  sorted_values = sorted(values, key=lambda x: x.get('distance'))

  return sorted_values[0].get('key_size')

def remote():

  """Retrieve ciphertext from the Cryptopals site."""

  url = "https://cryptopals.com/static/challenge-data/6.txt"

  return run(['curl', '--silent', url]).decode('ascii')

def find_key_size(text):

  """Find the most likely key size for a piece of encrypted text."""

  # compute hamming distance

  normalized_hamming_distances = [

    {

      'key_size': key_size,

      'distance': normalized_hamming_distance(text, key_size)

    }

    for key_size

    in key_sizes()

  ]

  # choose the smallest key size

  keys = smallest(normalized_hamming_distances)

  return keys

def transpose(text, size):

  """Transpose input text bytes by a specified size."""

  bytelist = b64decode(text)

  chunks = split_chunks(bytelist, size)

  transposed = list(zip(*chunks))

  # check that transposition worked as expected

  assert chunks[0][0] == transposed[0][0], 'matrix transposition failed'

  assert chunks[0][1] == transposed[1][0], 'matrix transposition failed'

  assert chunks[0][2] == transposed[2][0], 'matrix transposition failed'

  return transposed

def xor_single(bytelist, key):

  """XOR a set of bytes against a single key."""

  return [b ^ key for b in bytelist]

def ascii():

  """Generate ASCII characters."""

  return [chr(x) for x in range(128)]

def detect_key(strings):

  """Guess a likely key given a set of inputs."""

  common = list('etaoin shrdlu')

  counts = [

    sum([string.count(character) for character in common])

    for string in strings

  ]

  maximum = max(counts)

  index = counts.index(maximum)

  return chr(index)

def find_xor_key(bytelist):

  """For a set of XOR encrypted input bytes, statistically determine the single most likely key."""

  xor_bytes = [xor_single(bytelist, ord(character)) for character in ascii()]

  xor_strings = [''.join(list(map(chr, integer))) for integer in xor_bytes]

  key = detect_key(xor_strings)

  return key

def find_vignere_key(text):

  """Statistically determine the Vignere cipher key that was used to XOR encrypt an input text."""

  key_size = find_key_size(text)

  transposed_bytes = transpose(text, key_size)

  vignere_key = ''.join([find_xor_key(x) for x in transposed_bytes])

  return vignere_key

def decrypt_vignere(ciphertext, key):

  """Given a ciphertext and a key as input, decrypt with a Vignere cipher."""

  bytes_text = b64decode(ciphertext)

  bytes_key = ascii_to_bytes(key)

  decrypted_bytes = [b ^ bytes_key[i % len(bytes_key)] for i, b in enumerate(bytes_text)]

  decrypted_characters = [chr(b) for b in decrypted_bytes]

  decrypted_text = ''.join(decrypted_characters)

  return decrypted_text

def test():

  """Test challenge 6."""

  print('Challenge 6')

  ciphertext = remote()

  key = find_vignere_key(ciphertext)

  assert key == 'Terminator X: Bring the noise', 'incorrect key'

  message = decrypt_vignere(ciphertext, key)

  print(key)

  print(message)

  return (key, message)

if __name__ == "__main__":

  test()

结果:

4.题目

Level II Challenges | MysteryTwister — The Cipher Contest

代码:

import re

from Crypto.Hash import SHA

import hashlib

import itertools

import datetime

starttime = datetime.datetime.now()

hash1="67ae1a64661ac8b4494666f58c4822408dd0a3e4"

str1="QqWw%58(=0Ii*+nN"

str2=[['Q', 'q'],[ 'W', 'w'],[ '%', '5'], ['8', '('],[ '=', '0'], ['I', 'i'], ['*', '+'], ['n', 'N']]

def sha_encrypt(str):

    sha = hashlib.sha1(str)

    encrypts = sha.hexdigest()

    return encrypts

st3="0"*8

str4=""

str3=list(st3)

for a in range(0,2):

    str3[0]=str2[0][a]

    for b in range(0,2):

        str3[1]=str2[1][b]

        for c in range(0,2):

            str3[2]=str2[2][c]

            for d in range(0,2):

               str3[3] = str2[3][d]

               for e in range(0,2):

                   str3[4] = str2[4][e]

                   for f in range(0,2):

                       str3[5] = str2[5][f]

                       for g in range(0,2):

                           str3[6] = str2[6][g]

                           for h in range(0,2):

                               str3[7] = str2[7][h]

                               newS="".join(str3)

                               for i in itertools.permutations(newS, 8):

                                   str4 = sha_encrypt("".join(i))

                                   if str4==hash1:

                                       print "".join(i)

                                       endtime = datetime.datetime.now()

                                       print (endtime - starttime).seconds

                                       exit(0)

结果:

1024

1.题目:

Programming Assignment AES

In this assignment, you must decrypt a challenge ciphertext generated using AES in CBC-mode with PKCS #5 padding. (Note: technically this is PKCS #7 padding, since the block size of AES is 16 bytes. But the padding is done in exactly the same way as PKCS #5 padding.) To do so, you will be given access to a server that will decrypt any ciphertexts you send it (using the same key that was used to generate the challenge ciphertext)...but that will only tell you whether or not decryption results in an error!

All the files needed for this assignment are available here, including a README file that should explain everything.

Note that this assignment requires the ability to perform basic networking. Because we do not assume students necessarily know this, we have provided stub code for doing basic networking in C, Java, Ruby, and Python, but you are welcome to use any language of your choice as long as you are able to write code for basic networking functionality in that language. (Students may feel free to post stub code in other languages for the networking component on the discussion boards.)

The first step in this project is to send the challenge ciphertext to the server, and verify that you receive back a "no error" message. Once you can do that, the rest is "just" crypto...

The plaintext, when converted to ASCII, is readable English text, and so you should be able to tell once you have been successful. Once you have successfully recovered the plaintext (in ASCII).

代码:

#include "oracle.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
unsigned char ciphertext[3][16]={
  {159,11,19,148,72,65,168,50,178,66,27,158,175,109,152,54},
  {129,62,201,217,68,165,200,52,122,124,166,154,163,77,141,192},
  {223,112,227,67,196,0,10,42,227,88,116,206,117,230,76,49}
};
unsigned char ctext[32], plaintext[48], xor_iv[16];
void solve(int p) {
  int i, j, k, ret;
  memset(ctext, 0, sizeof(ctext) );
  for (i=0; i<16; ++i) ctext[16+i] = ciphertext[p][i];
  for (i=0; i<16; ++i) {
    for (j=0; j<i; ++j) ctext[15-j] = xor_iv[15-j] ^ (i+1);
    for (j=0; j<256; ++j) {
      ctext[15-i] = j;
      ret = Oracle_Send(ctext, 2);
      if (ret == 1) {
        xor_iv[15-i] = j ^ (i+1);
        plaintext[16*p+15-i] = xor_iv[15-i] ^ ciphertext[p-1][15-i];
        break;
      }
    }
  }
}
int main(int argc, char *argv[]) {
  int i, ret;
  Oracle_Connect();
  for (i=1; i<3; ++i) solve(i);
  freopen("plaintext.txt", "w", stdout);
  for (i=16; i<48; ++i) printf("%c", plaintext[i]);
  fclose(stdout);
  Oracle_Disconnect();
}

结果:

Yay! You get an A. =)

 2.题目:

Challenge 14 Set 2 - The Cryptopals Crypto Challenges

代码:

from base64 import b64decode
from Crypto import Random 
from Crypto.Cipher import AES
from random import Random as rand

UNKNOWN_STRING = b"""
Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWctdG9wIGRvd24gc28gbXkg
aGFpciBjYW4gYmxvdwpUaGUgZ2lybGllcyBvbiBzdGFuZGJ5IHdhdmluZyBq
dXN0IHRvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5vLCBJIGp1c3QgZHJvdmUg
YnkK"""
# b"Rollin' in my 5.0\nWith my rag-top down so my hair can blow\nThe girlies on standby waving just to say hi\nDid you stop? No, I just drove by\n" 

KEY = Random.new().read(16)
prefix_length = rand().randint(1, 3 * AES.block_size) # can occupy at most three blocks (this is arbitrary)
PREFIX = Random.new().read(prefix_length)

def pad(msg):
    """prepends the `msg` with `your_string` and then, applies PKCS#7 padding
    Args:
        your_string (bytes): the byte-string to prepend to `msg`
        msg (bytes): a byte-string that is to be padded
    Returns:
        bytes: the padded byte-string
    """
    paddedMsg = msg

    size = 16
    length = len(paddedMsg)
    if length % size == 0:
        return paddedMsg
    
    # PKCS#7 padding if the plain-text after padding isn't a multiple of AES.BLOCK_SIZE
    padding = size - (length % size)
    padValue = bytes([padding])
    paddedMsg += padValue * padding

    return paddedMsg
    

def encryption_oracle(your_string):
    """encrypts `your_string` + msg` + `UNKNOWN_STRING` using AES-ECB-128
    Args:
        your_string (bytes): byte-string used to prepend
    Returns:
        [bytes]: the byte-string of encrypted text
    """
    msg = bytes('The unknown string given to you was:\n', 'ascii')
    # append the `UNKNOWN_STRING` given to us to the `msg`, prepend `your_string` and then, the `PREFIX`
    plaintext = PREFIX + your_string + msg + b64decode(UNKNOWN_STRING)
    # Apply `PKCS#7` padding to correct size
    paddedPlaintext= pad(plaintext)

    cipher = AES.new(KEY, AES.MODE_ECB)
    ciphertext = cipher.encrypt(paddedPlaintext)

    return ciphertext


def detect_block_size():
    """detects the `block_size` used by the encryption_oracle()
    Returns:
        int: the `block_size` used by the encryption_oracle
    """
    feed = b"A"
    length = 0
    while True:
        cipher = encryption_oracle(feed)
        # on every iteration, add one more character
        feed  += feed
        # if the length of the ciphertext increases by more than 1,
        # PKCS#7 padding must have been added to make the size of plaintext == block_size
        # increase in the size gives the value of block_size
        if not length == 0 and len(cipher) - length > 1:
            return len(cipher) - length
        length = len(cipher)
        

def detect_mode(cipher):
    """detects whether the cipher-text was encrypted in ECB or not
    Args:
        cipher (bytes): byte-string of cipher-text
    Returns:
        str: "ECB" | "not ECB"
    """
    chunkSize = 16
    chunks = []
    for i in range(0, len(cipher), chunkSize):
        chunks.append(cipher[i:i+chunkSize])

    uniqueChunks = set(chunks)
    if len(chunks) > len(uniqueChunks):
        return "ECB"
    return "not ECB"


def detect_prefix_length():
    """Detects the length of the prefix used in the oracle
    Returns:
        int: the length of the prefix
    """
    block_size = detect_block_size()

    # first find number of integer blocks occupied
    test_case_1 = encryption_oracle(b'a')
    test_case_2 = encryption_oracle(b'b')

    length1 = len(test_case_1)
    length2 = len(test_case_2)

    blocks = 0
    min_length = min(length1, length2)
    # if the any of the blocks (starting from the left) are the same,
    # these blocks are occupied by the `PREFIX`
    for i in range(0, min_length, block_size):
        if test_case_1[i:i+block_size] != test_case_2[i:i+block_size]:
            break
        blocks += 1

    # now calculate the residual number of bytes and add to total size 
    test_input = b''
    length = blocks * block_size
    # if adding an extra `?` does not change the current block of cipher-text
    # we've reached the end of that block, and so,
    # we've found the number of extra characters needed to complete the block with some prefix characters
    for extra in range(block_size):
        test_input += b'?'
        curr = encryption_oracle(test_input)[length: length+block_size]
        next = encryption_oracle(test_input + b'?')[length: length+block_size] 
        if curr == next:
            break
    
    residue = block_size - len(test_input)
    length += residue
    return length 


def ecb_decrypt(block_size):
    """decrypts the plaintext (without key) using byte-at-a-time attack (simple)
    Args:
        block_size (int): the `block_size` used by the `encryption_oracle()` for encryption
    """
    # common = lower_cases + upper_cases + space + numbers
    # to optimize brute-force approach
    common = list(range(ord('a'), ord('z'))) + list(range(ord('A'), ord('Z'))) + [ord(' ')] + list(range(ord('0'), ord('9')))
    rare = [i for i in range(256) if i not in common]
    possibilities = bytes(common + rare)
    
    plaintext = b'' # holds the entire plaintext = sum of `found_block`'s
    check_length = block_size
    
    prefix_len = detect_prefix_length()
    print(f"Calculated Length of Prefix = { prefix_len }")
    check_begin = (prefix_len // block_size) * block_size
    residue = prefix_len % block_size

    while True:
        # as more characters in the block are found, the number of A's to prepend decreases
        prepend = b'A' * (block_size - 1 - (len(plaintext) + residue) % block_size)
        actual = encryption_oracle(prepend)[check_begin: check_begin+check_length]

        found = False
        for byte in possibilities:
            value = bytes([byte])
            your_string = prepend + plaintext + value
            produced = encryption_oracle(your_string)[check_begin: check_begin+check_length]
            if actual == produced:
                plaintext += value
                found = True
                break
        
        if not found:
            print(f'Possible end of plaintext: No matches found.')
            print(f"Plaintext: \n{ plaintext.decode('ascii') }")
            return
        
        if (len(plaintext) + residue) % block_size == 0: 
            check_length += block_size
    

def main():

    # detect block size
    block_size = detect_block_size()
    print(f"Block Size is { block_size }")

    # detect the mode (should be ECB)
    repeated_plaintext = b"A" * 50
    cipher = encryption_oracle(repeated_plaintext)
    mode = detect_mode(cipher)
    print(f"Mode of encryption is { mode }")

    # actual length of prefix
    print(f"Actual size of prefix = { len(PREFIX) }")

    # decrypt the plaintext inside `encryption_oracle()`
    ecb_decrypt(block_size)


if __name__ == "__main__":
    main()

结果:

 

3.题目: 

Challenge 16 Set 2 - The Cryptopals Crypto Challenges

 代码:

from Crypto.Cipher import AES
from os import urandom

BLOCK_SIZE = 16
KEY_SIZE = 16
IV_SIZE = 16

key = urandom(KEY_SIZE)
iv = urandom(IV_SIZE)

prefix = b"comment1=cooking%20MCs;userdata="
suffix = b";comment2=%20like%20a%20pound%20of%20bacon" 

def filter_and_pad(pt):
	pt = pt.replace(b";",b"%").replace(b"=",b"%")
	return prefix + pt + suffix

def PKCS7_padding(s):
	if len(s)%BLOCK_SIZE != 0:
		return s + chr((BLOCK_SIZE*(len(s)//BLOCK_SIZE)+BLOCK_SIZE)-len(s)).encode()*((BLOCK_SIZE*(len(s)//BLOCK_SIZE + 1))-len(s))
	return s

def encrypt(pt):
	pt = PKCS7_padding(pt)
	aes = AES.new(key, AES.MODE_CBC, iv)
	ct = aes.encrypt(pt)
	return ct

def cbc_decrypt(ct):
	aes = AES.new(key, AES.MODE_CBC, iv)
	dec = aes.decrypt(ct)
	if b";admin=true;" in dec:
		return True
	return False

def CBC_bitflipping_attack(ct):
	semicolon = ct[len(prefix)-16] ^ ord("%") ^ ord(";")
	equals = ct[len(prefix)-10] ^ ord("%") ^ ord("=")
	return ct[:len(prefix) - 16] + bytes([semicolon]) + ct[len(prefix)-15:len(prefix) - 10] + bytes([equals]) + ct[len(prefix) - 9:]

def main():
	pt = b";admin=true"
	ct = encrypt(filter_and_pad(pt))
	alt_ct = (CBC_bitflipping_attack(ct))
	print (cbc_decrypt(alt_ct))

if __name__ == "__main__":
	main()

 结果:

4.题目: 

MTC3 AES key —— encoded in the machine readable zone of a European ePassport

思路:

  1. 首先根据参考文章,16.1-16.1.5,可以算出?处的值
  2. 把参考文章中的MRZ_information的SHA-1三列中第最高有效位16字节作为kseed
  3. 吧kseed和c级联,sha-1后生成d,d的前十六位是ka,后十六位是kb
  4. Ka和kb奇偶校验生成key
  5. 把密文basee64解码,用得到的key解密

 代码:

from Crypto.Cipher import AES
from hashlib import sha1
import codecs
import binascii
import base64
#奇偶校验位的判断
def jiaoyan(x):
    k = []
    a = bin(int(x,16))[2:]
    for i in range(0,len(a),8):
        if (a[i:i+7].count("1"))%2 == 0:
            k.append(a[i:i+7])
            k.append('1')
        else :
            k.append(a[i:i+7])
            k.append('0')      
    a1 = hex(int(''.join(k),2))
    #print("this is " + x + "---" +a1)
    return a1[2:]
a = [1,1,1,1,1,6]
b = [7,3,1,7,3,1]
c = 0
for i in range(0,6):
    c = c + a[i]*b[i]
    res = c % 10
print (res)
#res = 7

information="1234567808000111018201111167"
H_information = sha1(information.encode()).hexdigest()
#十进制先变为二进制散列再换成十六进制
K_seed = H_information[:32]# 取前16位
c = '00000001'
#0x00000001
d = K_seed + c
#print(d)
H_d = sha1(codecs.decode(d,"hex")).hexdigest()
#十六进制先变为二进制散列再换成十六进制
#print(H_d)
ka = sha1(codecs.decode(d,"hex")).hexdigest()[:16]
kb = sha1(codecs.decode(d,"hex")).hexdigest()[16:32]
k_1 = jiaoyan(ka)
k_2 = jiaoyan(kb)  
key = k_1 + k_2
key="ea8645d97ff725a898942aa280c43179"
print(key)
IV='0'*32
cipher="9MgYwmuPrjiecPMx61O6zIuy3MtIXQQ0E59T3xB6u0Gyf1gYs2i3K9Jxaa0zj4gTMazJuApwd6+jdyeI5iGHvhQyDHGVlAuYTgJrbFDrfB22Fpil2NfNnWFBTXyf7SDI"
temp = base64.b64decode(cipher)
cipher=temp
m=AES.new(binascii.unhexlify(key),AES.MODE_CBC,binascii.unhexlify(IV))
print(m.decrypt(cipher))

 结果:

 

参考:https://blog.csdn.net/binghuang1999/article/details/102566698

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值