密码学实验报告
本文最后更新于一年前或更久前,其中的信息可能已经有所发展或是发生改变。

Tips:此为⬛⬛⬛⬛⬛⬛大学⬛⬛⬛⬛⬛⬛⬛学院2019级⬛⬛⬛⬛⬛⬛实验班《密码学》课程中的部分课程作业题目解析和展示

第一次实验

任务1:破解一个Many Time Pad加密

思路:可以看注释~

(运行结果也一并放到代码里了,Shell部分就是)

__reference = 'https://github.com/Morrandir/Crypto001_Week1'

from string import ascii_letters
TARGET = '32510ba9babebbbefd001547a810e67149caee11d945cd7fc81a05e9f85aac650e9052ba6a8cd8257bf14d13e6f0a803b54fde9e77472dbff89d71b57bddef121336cb85ccb8f3315f4b52e301d16e9f52f904'
#使用TARGET与每条之前的MSGS进行异或
#原理:c(a)^c(b) == m(a) ^ m(b) ^ k ^ k == m(a) ^ m(b)
#以及空格和ascii中大小写字母异或的结果为反转其大小写
potential_str = ' ' + ascii_letters  #有意义的字符
MSGS = (
    '315c4eeaa8b5f8aaf9174145bf43e1784b8fa00dc71d885a804e5ee9fa40b16349c146fb778cdf2d3aff021dfff5b403b510d0d0455468aeb98622b137dae857553ccd8883a7bc37520e06e515d22c954eba5025b8cc57ee59418ce7dc6bc41556bdb36bbca3e8774301fbcaa3b83b220809560987815f65286764703de0f3d524400a19b159610b11ef3e',
    '234c02ecbbfbafa3ed18510abd11fa724fcda2018a1a8342cf064bbde548b12b07df44ba7191d9606ef4081ffde5ad46a5069d9f7f543bedb9c861bf29c7e205132eda9382b0bc2c5c4b45f919cf3a9f1cb74151f6d551f4480c82b2cb24cc5b028aa76eb7b4ab24171ab3cdadb8356f',
    '32510ba9a7b2bba9b8005d43a304b5714cc0bb0c8a34884dd91304b8ad40b62b07df44ba6e9d8a2368e51d04e0e7b207b70b9b8261112bacb6c866a232dfe257527dc29398f5f3251a0d47e503c66e935de81230b59b7afb5f41afa8d661cb',
    '32510ba9aab2a8a4fd06414fb517b5605cc0aa0dc91a8908c2064ba8ad5ea06a029056f47a8ad3306ef5021eafe1ac01a81197847a5c68a1b78769a37bc8f4575432c198ccb4ef63590256e305cd3a9544ee4160ead45aef520489e7da7d835402bca670bda8eb775200b8dabbba246b130f040d8ec6447e2c767f3d30ed81ea2e4c1404e1315a1010e7229be6636aaa',
    '3f561ba9adb4b6ebec54424ba317b564418fac0dd35f8c08d31a1fe9e24fe56808c213f17c81d9607cee021dafe1e001b21ade877a5e68bea88d61b93ac5ee0d562e8e9582f5ef375f0a4ae20ed86e935de81230b59b73fb4302cd95d770c65b40aaa065f2a5e33a5a0bb5dcaba43722130f042f8ec85b7c2070',
    '32510bfbacfbb9befd54415da243e1695ecabd58c519cd4bd2061bbde24eb76a19d84aba34d8de287be84d07e7e9a30ee714979c7e1123a8bd9822a33ecaf512472e8e8f8db3f9635c1949e640c621854eba0d79eccf52ff111284b4cc61d11902aebc66f2b2e436434eacc0aba938220b084800c2ca4e693522643573b2c4ce35050b0cf774201f0fe52ac9f26d71b6cf61a711cc229f77ace7aa88a2f19983122b11be87a59c355d25f8e4',
    '32510bfbacfbb9befd54415da243e1695ecabd58c519cd4bd90f1fa6ea5ba47b01c909ba7696cf606ef40c04afe1ac0aa8148dd066592ded9f8774b529c7ea125d298e8883f5e9305f4b44f915cb2bd05af51373fd9b4af511039fa2d96f83414aaaf261bda2e97b170fb5cce2a53e675c154c0d9681596934777e2275b381ce2e40582afe67650b13e72287ff2270abcf73bb028932836fbdecfecee0a3b894473c1bbeb6b4913a536ce4f9b13f1efff71ea313c8661dd9a4ce',
    '315c4eeaa8b5f8bffd11155ea506b56041c6a00c8a08854dd21a4bbde54ce56801d943ba708b8a3574f40c00fff9e00fa1439fd0654327a3bfc860b92f89ee04132ecb9298f5fd2d5e4b45e40ecc3b9d59e9417df7c95bba410e9aa2ca24c5474da2f276baa3ac325918b2daada43d6712150441c2e04f6565517f317da9d3',
    '271946f9bbb2aeadec111841a81abc300ecaa01bd8069d5cc91005e9fe4aad6e04d513e96d99de2569bc5e50eeeca709b50a8a987f4264edb6896fb537d0a716132ddc938fb0f836480e06ed0fcd6e9759f40462f9cf57f4564186a2c1778f1543efa270bda5e933421cbe88a4a52222190f471e9bd15f652b653b7071aec59a2705081ffe72651d08f822c9ed6d76e48b63ab15d0208573a7eef027',
    '466d06ece998b7a2fb1d464fed2ced7641ddaa3cc31c9941cf110abbf409ed39598005b3399ccfafb61d0315fca0a314be138a9f32503bedac8067f03adbf3575c3b8edc9ba7f537530541ab0f9f3cd04ff50d66f1d559ba520e89a2cb2a83'
)

CRACK = 'The secret message is: When using a stream cipher, never use the key more than once'
#根据异或结果,我们可以推断出,结果大概为:
#The secuet message is Whtn usinw a stream cipher never use the key more than once
#因为密钥为一串有意义的字符,我们可以推断出,其真正含义大概为:
#The secret message is When using a stream cipher never use the key more than once

def strxor(a, b):     # xor two strings of different lengths,因为源代码给的是py2的,这里修改了下改成了py3的
    if len(a) > len(b):
        return "".join([chr(x ^ y) for (x, y) in zip(a[:len(b)], b)])
    else:
        return "".join([chr(ord(x) ^ y) for (x, y) in zip(a, b[:len(a)])])

for MSG in MSGS:
    result = strxor(bytes.fromhex(MSG), bytes.fromhex(TARGET))
    for j in range(0, len(bytes.fromhex(TARGET))):
        if result[j] in potential_str:
            print(result[j],end='')
        else:
            print('*',end='')
    print('')

key = strxor(CRACK, bytes.fromhex(TARGET))

for MSG in MSGS:
    result = strxor(key, bytes.fromhex(MSG))
    print(result)
keyint = ''
for i in range(0,len(key)):
    keyint+=str(ord(key[i]))
Eighth = strxor(key,bytes.fromhex(MSGS[7]))
print(f"key={keyint}n8th message={Eighth}")
'''
shell:
**EC**C***T**S***EN**XE*HT******GQ*A****A*O********N**E*A*S*L**EF***O*O**ET***B**CT
***E*E****DM******L*S*N***NT***N*O*****E**E****E*IC****RAU**R*******N*O*******T*NNE
********E*H***S***U*SqE****QU**N*O****R***P******DE**V**NU**I**EAK**TM**EF*********
**********T***S***D***Dw**NAU******N******O*I*****I***E*O******EG******R*I****T***E
*******U*TW***S**EB***Aw******I**RAK***E**O*I*H**U****E*P***A***E*E*NM***A*********
***R*E***TT**S****SI*******T*****H***T**********R*I**V**E*S*E***T*E*A**R*R**A*O**C*
***R*E***TT**S****SI*******O*****Y*****E**A*I*****SN***Rg***R***N*E*OM********EO***
**EC**C*******S***N*SMH***NT**I**I****R***A***H***AN****GU**TT******TM********U***E
*HMP**********ZAG*N**CP**********EAS*****M*C*****ET***IRN***L*H*****C****ET********
t**ES*****S*E*****D**YT****R*SA*W*W*S*****N**P****T*E**RT**EA**EO*EYW****N*H*NRO***
We can factor the number 15 with quantum computers. We can also factor the number 1
Euler would probably enjoy that now his theorem becomes a corner stone of crypto -
The nice thing about Keeyloq is now we cryptographers can drive a lot of fancy cars
The ciphertext produced by a weak encryption algorithm looks as good as ciphertext
You don't want to buy a set of car keys from a guy who specializes in stealing cars
There are two types of cryptography - that which will keep secrets safe from your l
There are two types of cyptography: one that allows the Government to use brute for
We can see the point where the chip is unhappy if a wrong bit is sent and consumes
A (private-key)  encryption scheme states 3 algorithms, namely a procedure for gene
 The Concise OxfordDictionary (2006) deï¬nes crypto as the art of  writing o r sol
key=1025711013720121921620415211653422059914916461752061201701272374016012710720114141197111051765115425248170642615610911214312819210219999254240184972205216232220891169135119519317425223621315667581073813996191782406015497
8th message=We can see the point where the chip is unhappy if a wrong bit is sent and consumes
'''

任务2:破解维吉尼亚密码,和任务3思路很相似

思路:写在代码注释里~(还注释掉了一部分另一种思路的做法)

import string
import re
cipherlist = []
testkeys = []
for i in range(0x00,0xff):
    testkeys.append(i)
#加入所有可能密钥
potentialchars = test_chars = string.ascii_letters + string.digits + ',' + '.' + ' '
re__ = '[A-Za-z0-9,. ]'
def findindexkey(arr):
    global testchars
    global testkeys
    anskeys = list(testkeys)#不要使用anskeys =testkeys,python复习了属于是
    for i in testkeys:
        for s in arr:
            if chr(s ^ i) not in potentialchars:
                anskeys.remove(i)
                break
            #if re.search(re__,str(chr(s ^ i))):
            #    pass
            #else:
            #    anskeys.remove(i)
            #    break
            #使用正则表达式的实现方法
    return anskeys
#找出所有可能密钥,通过删除实现

text = 'F96DE8C227A259C87EE1DA2AED57C93FE5DA36ED4EC87EF2C63AAE5B9A7EFFD673BE4ACF7BE8923CAB1ECE7AF2DA3DA44FCF7AE29235A24C963FF0DF3CA3599A70E5DA36BF1ECE77F8DC34BE129A6CF4D126BF5B9A7CFEDF3EB850D37CF0C63AA2509A76FF9227A55B9A6FE3D720A850D97AB1DD35ED5FCE6BF0D138A84CC931B1F121B44ECE70F6C032BD56C33FF9D320ED5CDF7AFF9226BE5BDE3FF7DD21ED56CF71F5C036A94D963FF8D473A351CE3FE5DA3CB84DDB71F5C17FED51DC3FE8D732BF4D963FF3C727ED4AC87EF5DB27A451D47EFD9230BF47CA6BFEC12ABE4ADF72E29224A84CDF3FF5D720A459D47AF59232A35A9A7AE7D33FB85FCE7AF5923AA31EDB3FF7D33ABF52C33FF0D673A551D93FFCD33DA35BC831B1F43CBF1EDF67F0DF23A15B963FE5DA36ED68D378F4DC36BF5B9A7AFFD121B44ECE76FEDC73BE5DD27AFCD773BA5FC93FE5DA3CB859D26BB1C63CED5CDF3FE2D730B84CDF3FF7DD21ED5ADF7CF0D636BE1EDB79E5D721ED57CE3FE6D320ED57D469F4DC27A85A963FF3C727ED49DF3FFFDD24ED55D470E69E73AC50DE3FE5DA3ABE1EDF67F4C030A44DDF3FF5D73EA250C96BE3D327A84D963FE5DA32B91ED36BB1D132A31ED87AB1D021A255DF71B1C436BF479A7AF0C13AA14794'
for i in range(0,len(text),2):
    cipherlist.append(int(text[i:i+2],16))

for keylenth in range(1,12):
    for index in range(0,keylenth):
        modedlist = cipherlist[index::keylenth]
        anskeys = findindexkey(modedlist)
        #print(f"anskeys={anskeys},index is {index},and at this time the len is {keylenth}")
#此时发现只有7的时候有anskeys结果出现,结果为
'''
anskeys=[186],index is 0,and at this time the len is 7
anskeys=[31],index is 1,and at this time the len is 7
anskeys=[145],index is 2,and at this time the len is 7
anskeys=[178],index is 3,and at this time the len is 7
anskeys=[83],index is 4,and at this time the len is 7
anskeys=[205],index is 5,and at this time the len is 7
anskeys=[62],index is 6,and at this time the len is 7
'''
key = [186,31,145,178,83,205,62]
origin = ''
for i in range(0,len(cipherlist)):
    origin = origin + chr(cipherlist[i] ^ key[i%7])
print(origin)

运行结果:

任务3:给出一串用维吉尼亚密码加密后的密文,要求使用重合指数法获得明文。

http://www.cryptopals.com/sets/1

思路:先求密钥长度,看密钥可能的长度是多少,然后分片分成每个段的[0],[1]……的位置,依次进行试验,使得每个子段进行维吉尼亚解密后的结果处于正常英文的ASCII序号列表里,更好点的还可以通过进行字母出现频率判断来进一步缩小答案范围,最终范围只有一个。


import base64
import binascii

cipherbase64 = b'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'
cipher = base64.b64decode(cipherbase64)
cipher64 = binascii.b2a_hex(base64.b64decode(cipherbase64))
cipherlist = []
for i in range(0,len(cipher64),2):
    cipherlist.append(int(cipher64[i:i+2],16))
#print(cipherlist)
#cipherlist是一个list,里面每项为字符ASCII的int值
dict1 = {}
def HammingDistance(list1,list2):
    string = ''
    for i in range(0,len(list1)):
        a = bin(list1[i]^list2[i])
        string+=a[2:]
    return string.count('1')

def hamdistenceaverage(mlist):
  count = 0
  for j in mlist:
      for k in mlist:
        if j != k:
          count += HammingDistance(j,k) / i
  return count

for i in range(2,41):
    firstm = cipherlist[0:i]
    secondm = cipherlist[i:2*i]
    thirdm = cipherlist[2*i:3*i]
    fouthm = cipherlist[3*i:4*i]
    mlist = [firstm,secondm,thirdm,fouthm]
    count = hamdistenceaverage(mlist)
    dict1[f'{i}'] = count
#此部分用于找KEYSIZE

print(dict1)
list1= sorted(dict1.items(),key=lambda x:x[1])
print(list1)

OptionalKEYSIZE = [29,5,2,24]
'''
[('29', 32.96551724137932), ('5', 34.8), ('2', 36.0), ('24', 36.25), ('7', 36.85714285714286), ('6', 37.0), ('19', 37.1578947368421), ('20', 37.2), 
('3', 37.333333333333336), ('8', 37.5), ('28', 37.85714285714286), ('30', 38.13333333333333), ('34', 38.1764705882353), ('9', 38.22222222222222), ('39', 38.35897435897436), ('10', 38.4), ('17', 38.47058823529411), ('16', 38.5), ('40', 38.6), ('18', 39.0), ('26', 39.0), ('37', 39.027027027027025), ('32', 39.125), ('23', 39.130434782608695), ('38', 39.21052631578947), ('15', 39.33333333333333), ('33', 39.45454545454546), ('21', 39.61904761904762), ('35', 39.65714285714286), ('31', 39.74193548387096), ('25', 39.76), ('13', 39.84615384615385), ('4', 40.0), ('14', 40.142857142857146), ('27', 40.148148148148145), ('22', 40.54545454545455), ('36', 40.611111111111114), ('11', 41.09090909090909), ('12', 41.5)]
'''

def singlekey_xor(cipher):
    '''get the most probable key byte
    cipher: encrypted by the same byte of key(bytes)
    return: the byte of key(bytes)'''
    result = -1
    for i in range(256):
        tempmsg = bytes([i ^ x for x in cipher])
        #tempresult = score(tempmsg.decode('latin1'))
        tempresult = ChiSquared(tempmsg.decode('latin1') , 1)
        if tempresult > result:#比较如果score更大则几率更大
            result = tempresult
            key = bytes([i])
    return key
def break_key_reuse_xor(msg):#显示所有可用的keysize对应密文,词典形式排序
    '''msg: bytes
    return a dic {keysize : key},keysize is int, key is bytes'''
    key_size = OptionalKEYSIZE
    key = []
    for i in key_size:
        tempkey = b''
        for j in range(i):
            tempkey += singlekey_xor(msg[j::i])
        key.append(tempkey)
    return key

def ChiSquared(msg , mod):#重要!使用这个函数进行score排序,不然返回的将是一大堆列表,都可以
    '''Chi-squared Statistic
    msg: str
    mod: 1 is simply plus, 0 is multiple'''
    standard_letter_frequency = {  # From https://en.wikipedia.org/wiki/Letter_frequency.
    'e': 0.12702,'t': 0.09056,'a': 0.08167,'o': 0.07507,'i': 0.06966,'n': 0.06749,
    's': 0.06327,'h': 0.06094,'r': 0.05987,'d': 0.04253,'l': 0.04025,'c': 0.02782,
    'u': 0.02758,'m': 0.02406,'w': 0.02360,'f': 0.02228,'g': 0.02015,'y': 0.01974,
    'p': 0.01929,'b': 0.01492,'v': 0.00978,'k': 0.00772,'j': 0.00153,'x': 0.00150,
    'q': 0.00095,'z': 0.00074,' ': 0.25000}
    score = 0
    msg = msg.lower()
    if mod:
        for i in msg:
            if i in standard_letter_frequency:
                score += standard_letter_frequency[i]
    else:
        msg = msg.lower()
        for i in standard_letter_frequency:
            temp = standard_letter_frequency[i] * len(msg)
            score += (msg.count(i)- temp)**2 / temp
    return score

#均以bytes类型,防止串类型做不出来
XOR = lambda s1 , s2 : bytes([x1 ^ x2 for x1 , x2 in zip(s1 , s2)])
key = break_key_reuse_xor(cipher)
print(key)#可以看出key[0]有意义其余无意义
key = key[0]*len(cipher)
print(XOR(key , cipher))

运行结果:

image-20220108142426507

任务4:用户输入密码所使用的按键,告诉你密码的长度和密码SHA1之后的值,让你求密码的值。

MTC3 Cracking SHA1- HashedPasswords
https://www.mysterytwisterc3.org/en/challenges/level-2/cracking-sha1-hashed-passwords

思想:就是暴力枚举,看哪个算出来SHA1和目标SHA1一样就行了

#coding:utf-8
import hashlib
import itertools
import datetime

hash1="67ae1a64661ac8b4494666f58c4822408dd0a3e4"
str1="QqWw%58(=0Ii*+nN"
str2=[['Q', 'q'],[ 'W', 'w'],[ '%', '5'], ['8', '('],[ '=', '0'], ['I', 'i'], ['*', '+'], ['n', 'N']]
def sha_encrypt(str):
    hash = hashlib.sha1()
    hash.update(str.encode('utf-8'))
    encrypts = hash.hexdigest()
    return encrypts
st3="0"*8
str4=""
str3=list(st3)
starttime = datetime.datetime.now()
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))

运行结果:

image-20220108142712352

第二次实验

任务1:根据错误的Padding获得信息

PA2 option
AES decrypt a challenge cipher text generated using AES in CBC-mode with PKCS#5 padding.
connect(('128.8.130.16', 49101)))

友情提示:下面这个是python2,因为python3对于某些字符的识别和python2差别比较大,python3进行识别总是报错,最终无奈选择了python2

# -*- coding: cp936 -*-
from oracle import *
from Crypto.Util import strxor
import re

C = '9F0B13944841A832B2421B9EAF6D9836813EC9D944A5C8347A7CA69AA34D8DC0DF70E343C4000A2AE35874CE75E64C31'
BLOCK = 2
div = len(C) / (BLOCK + 1)
C = re.findall('.{' + str(div) + '}', C)

Oracle_Connect()
M = []
IVALUE = []
for b in range(BLOCK): #对 2 组密文分别求解
    print '[*] Detecting Block',b+1
    IV = C[b]
    Ivalue = []
    iv = '00000000000000000000000000000000' #初始化 iv
    iv = re.findall('.{2}', iv)[::-1]
    padding = 1

    for l in range(16):
        print "  [+] Detecting IVALUE's last", l + 1 , 'block'
        for ll in range(l):
            iv[ll] = hex(int(Ivalue[ll], 16) ^ padding)[2:].zfill(2) #更新 iv

        for n in range(256): #遍历 0x00-0xFF
            iv[l] = hex(n)[2:].zfill(2)
            data = ''.join(iv[::-1]) + C[b + 1]

            ctext = [(int(data[i:i + 2], 16)) for i in range(0, len(data), 2)]
            rc = Oracle_Send(ctext, 2)

            if str(rc) == '1': #Padding 正确时, 记录 Ivalue, 结束爆破
                Ivalue += [hex(n ^ padding)[2:].zfill(2)]
                break

        print '    [-]', ''.join(iv[::-1])
        print '    [-]', ''.join(Ivalue[::-1])

        padding += 1

    Ivalue = ''.join(Ivalue[::-1])
    IVALUE += [Ivalue]

    #IV 与 Ivalue 异或求密文
    m = re.findall('[0-9a-f]+', str(hex(int(IV, 16) ^ int(''.join(Ivalue), 16))))[1].decode('hex')
    M += [m]

    print '[#] Detecting Block', b + 1 ,'-- Done!'
    print '[#]', 'The IValue' + str(b + 1), 'is:', Ivalue
    print '[#]', 'The M' + str(b + 1) , 'is:', m
    print '-' * 50

Oracle_Disconnect()

print '[!] The Intermediary Value is:', ''.join(IVALUE)
print '[!] The M is:', ''.join(M)

运行结果:

任务2:Crptopals Sets2

Question 2 cryptopals
http://www.cryptopals.com/sets/2

  1. Implement PKCS#7 padding
  2. Implement CBC mode
  3. An ECB/CBC detection oracle4. Byte-at-a-time ECB decryption
    (Simple)
  4. ECB cut-and-paste
  5. Byte-at-a-time ECB decryption
    (Harder)
  6. PKCS#7 padding validation8. CBC bit flipping attacks
    上传6,7,8的代码截图

6.破解ECB,通过在秘密前面加上特定数量的字节,将秘密的第一个未知字节恰好“顶”到一块的最后一个字节,的到一个加密串。另一个则根据已经求出的秘密前缀将暴力猜测的字节“顶”到同样的位置,保证本字节之前的字节和上一个串是一样的。这样通过比较本块的加密结果是否一样就可以暴出这一字节。

from base64 import b64decode
from typing_extensions import ParamSpecKwargs
from S2C10 import aes_ecb_encrypt
from S2C09 import pkcs7_unpad
from random import randint
from Crypto import Random
from S1C08 import count_aes_ecb_repetitions
from S2C12 import find_block_length, ECBOracle

class HarderECBOracle(ECBOracle):

    def __init__(self, secret_padding):
        super(HarderECBOracle, self).__init__(secret_padding)
        self._random_prefix = Random.new().read(randint(0, 255))

    def encrypt(self, data):
        """Encrypts with AES-128-ECB mode, after prepending a fixed (randomly-generated) string
        and appending a fixed (given) string to every plaintext.
        使用AES-128-ECB模式进行加密
        """
        return aes_ecb_encrypt(self._random_prefix + data + self._secret_padding, self._key)

def get_next_byte(prefix_length, block_length, curr_decrypted_message, encryption_oracle):
    """Finds the next byte of the mysterious message that the oracle
    is appending to our plaintext.
    寻找下一块密文并且将预言加到明文上
    """

    # Compute the number of characters that we want to use as input to have the first unknown
    # character of the mysterious message at the end of a block
    length_to_use = (block_length - prefix_length - (1 + len(curr_decrypted_message))) % block_length
    my_input = b'A' * length_to_use

    # Compute the number of bytes that we will take from the fake and from the real ciphertexts
    # to compare them. We will ignore everything is beyond the byte we are trying to discover.
    cracking_length = prefix_length + length_to_use + len(curr_decrypted_message) + 1

    # Compute the real ciphertext that the oracle would output with my input
    real_ciphertext = encryption_oracle.encrypt(my_input)

    # For each possible character
    for i in range(256):

        # Compute our fake ciphertext, trying to obtain the same as the real ciphertext
        fake_ciphertext = encryption_oracle.encrypt(my_input + curr_decrypted_message + bytes([i]))

        # If we found a character that, used in our fake input, let us obtain the same ciphertext
        if fake_ciphertext[:cracking_length] == real_ciphertext[:cracking_length]:

            # Return that character as the next byte of the message
            return bytes([i])

    # If there was no match (most likely due to padding), return empty byte
    return b''

def has_equal_block(ciphertext, block_length):
    """Checks if the given ciphertext contains two consecutive equal blocks
    查看给予的密文有没有两个相同的块,这里是一串一串一位一位的算"""
    for i in range(0, len(ciphertext) - 1, block_length):
        if ciphertext[i:i+block_length] == ciphertext[i+block_length:i+2*block_length]:
            return True

    return False

def find_prefix_length(encryption_oracle, block_length):
    """Finds the length of the randomly generated prefix that the encryption oracle
    adds to every plaintext before encrypting. First, the block where the prefix ends
    is searched; then the precise index where the prefix ends is searched.
    """

    # To find the index of the block where the prefix ends, we use the oracle to encrypt
    # an empty message and a 1 character message. Then we use them to find it as explained below.
    ciphertext1 = encryption_oracle.encrypt(b'')
    ciphertext2 = encryption_oracle.encrypt(b'a')

    # The first block where the two ciphertexts differ will be the block where the
    # prefix (which was the same for both the inputs) ended.
    prefix_length = 0
    for i in range(0, len(ciphertext2), block_length):
        if ciphertext1[i:i+block_length] != ciphertext2[i:i+block_length]:
            prefix_length = i
            break

    # Now, to find the precise index where the prefix ended, we will encrypt identical bytes,
    # in a number equal to two block_lengths, and we will increase this amount by an incremental
    # offset to see when those bytes will be shifted to be autonomous blocks (thus encrypted the same way)
    for i in range(block_length):
        fake_input = bytes([0] * (2 * block_length + i))
        ciphertext = encryption_oracle.encrypt(fake_input)

        # If the bytes have shifted enough, we can compute the precise index where the prefix ends
        # inside its last block, which is going to be equal to block_length - i
        if has_equal_block(ciphertext, block_length):
            return prefix_length + block_length - i if i != 0 else prefix_length

    raise Exception('The oracle is not using ECB')

def byte_at_a_time_ecb_decryption_harder(encryption_oracle):
    """Performs the byte-at-a-time ECB decryption attack to discover the secret padding used by the oracle."""

    # Find the block length
    block_length = find_block_length(encryption_oracle)

    # To detect if the oracle encrypts with ECB mode, we can encrypt a big enough (more
    # than three block sizes) plaintext of identical bytes. If the ciphertext presents
    # repeated blocks then we can deduct that it is very likely using ECB.
    ciphertext = encryption_oracle.encrypt(bytes([0] * 64))
    assert count_aes_ecb_repetitions(ciphertext) > 0

    # The number of bytes that we have to decrypt by breaking the encryption oracle
    # will be equal to the length of the ciphertext when we encrypt an empty message
    # subtracted by the length of the prefix (which we have to find).
    prefix_length = find_prefix_length(encryption_oracle, block_length)
    mysterious_text_length = len(encryption_oracle.encrypt(b'')) - prefix_length

    # At this point we have all the information that we need to crack the ECB
    # encryption oracle byte by byte.
    secret_padding = b''
    for i in range(mysterious_text_length):
        secret_padding += get_next_byte(prefix_length, block_length, secret_padding, encryption_oracle)

    # Return the complete padding as bytes
    return secret_padding

def main():
    """Approach:
    1) Find the block_length and the encryption mode (as in S2C12)
    2) Find the prefix length
    3) Decrypt byte-by-byte the mysterious message (similar to S2C12)
    方法:我们通过控制AAAAA*n字符串的长度,慢慢更改A的长度,使得密文中每个字符慢慢暴露出来在另一个块
    中,这样就能一次一个字符的进行爆破,大大降低了计算量
    """
    secret_padding = b64decode("Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWctdG9wIGRvd24gc28gbXkgaGF"
                               "pciBjYW4gYmxvdwpUaGUgZ2lybGllcyBvbiBzdGFuZGJ5IHdhdmluZyBqdXN0IH"
                               "RvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5vLCBJIGp1c3QgZHJvdmUgYnkK")
    oracle = HarderECBOracle(secret_padding)
    discovered_secret_padding = byte_at_a_time_ecb_decryption_harder(oracle)

    # Check if the attack works correctly
    print(secret_padding)
    print(discovered_secret_padding)
    print(pkcs7_unpad(discovered_secret_padding))
    assert pkcs7_unpad(discovered_secret_padding) == secret_padding

if __name__ == '__main__':
    main()

7.一个简单的Padding Check

def check_padding(s: bytes):
    padding = s[-s[-1]:]
    for i in padding:
        if not i == len(padding):
            return False
    return True

print(check_padding(b'ICE ICE BABYx04x04x04x04'))
print(check_padding(b'ICE ICE BABYx05x05x05x05'))
print(check_padding(b'ICE ICE BABYx01x02x03x04'))
print(check_padding(b'ICE ICE BABY'))

8.CBC的比特翻转攻击,通过添加很长的data构造出一个纯没有意义的块,然后异或这一块的某个密文字节,这样下一块的明文字节在解密时就会被篡改。

from S2C10 import aes_cbc_encrypt, aes_cbc_decrypt
from Crypto import Random
from Crypto.Cipher import AES

class Oracle:

    def __init__(self):
        self._key = Random.new().read(AES.key_size[0])
        self._iv = Random.new().read(AES.block_size)
        self._prefix = "comment1=cooking%20MCs;userdata="
        self._suffix = ";comment2=%20like%20a%20pound%20of%20bacon"

    def encrypt(self, data):
        """Adds the prefix and the suffix specified in the challenge and encrypts the data with AES-128-CBC"""
        data = data.replace(';', '').replace('=', '')  # Remove special characters to avoid injection
        plaintext = (self._prefix + data + self._suffix).encode()
        return aes_cbc_encrypt(plaintext, self._key, self._iv)

    def decrypt_and_check_admin(self, ciphertext):
        """Decrypts the string and returns whether the characters ";admin=true;" are in the string"""
        data = aes_cbc_decrypt(ciphertext, self._key, self._iv)
        return b';admin=true;' in data

def find_block_length(encryption_oracle):
    """Returns the length of a block for the block cipher used by the encryption_oracle.
    To find the length of a block, we encrypt increasingly longer plaintexts until the size of the
    output ciphertext increases too. When this happens, we can then easily compute the length of a block
    as the difference between this new length of the ciphertext and its initial one.
    """
    my_text = ''
    ciphertext = encryption_oracle(my_text)
    initial_len = len(ciphertext)
    new_len = initial_len

    while new_len == initial_len:
        my_text += 'A'
        ciphertext = encryption_oracle(my_text)
        new_len = len(ciphertext)

    return new_len - initial_len

def find_prefix_length(encryption_oracle, block_length):
    """Returns the length of the prefix that the encryption oracle prepends to every plaintext."""

    # Encrypt two different ciphertexts
    ciphertext_a = encryption_oracle('A')
    ciphertext_b = encryption_oracle('B')

    # Find their common length
    common_len = 0
    while ciphertext_a[common_len] == ciphertext_b[common_len]:
        common_len += 1

    # Make sure that the common length is multiple of the block length
    common_len = int(common_len / block_length) * block_length

    # Try to add an increasing number of common bytes to the plaintext till they until
    # the two ciphertexts will have one extra identical block
    for i in range(1, block_length + 1):
        ciphertext_a = encryption_oracle('A' * i + 'X')
        ciphertext_b = encryption_oracle('A' * i + 'Y')

        # If there is one more identical block, it will mean that by adding i bytes
        # we made the common input (including prefix) to the same length multiple of
        # a block size. Then we can easily get the length of the prefix.
        if ciphertext_a[common_len:common_len + block_length] == ciphertext_b[common_len:common_len + block_length]:
            return common_len + (block_length - i)

def cbc_bit_flip(encryption_oracle):
    """Performs a CBC bit flipping attack to accomplish admin privileges in the decrypted data."""

    # Get the length of a block and the length of the prefix
    block_length = find_block_length(encryption_oracle.encrypt)
    prefix_length = find_prefix_length(encryption_oracle.encrypt, block_length)

    # Compute the number of bytes to add to the prefix to make its length a multiple of block_length
    additional_prefix_bytes = (block_length - (prefix_length % block_length)) % block_length
    total_prefix_length = prefix_length + additional_prefix_bytes

    # Compute the number of bytes to add to the plaintext to make its length a multiple of block length
    plaintext = "?admin?true"
    #此处输入?admin?true绕过plaintext对于=和?的过滤
    additional_plaintext_bytes = (block_length - (len(plaintext) % block_length)) % block_length

    # Make the plaintext long one block_length and encrypt it
    final_plaintext = additional_plaintext_bytes * '?' + plaintext
    ciphertext = encryption_oracle.encrypt(additional_prefix_bytes * '?' + final_plaintext)

    # Because XORing a byte with itself produces zero, we can produce the byte that we want
    # by changing the bytes of the block before the plaintext
    semicolon = ciphertext[total_prefix_length - 11] ^ ord('?') ^ ord(';')
    equals = ciphertext[total_prefix_length - 5] ^ ord('?') ^ ord('=')
    # 因为plainchar就是?,? ^ plainchar ^ ; = ;就把?变成;了
    #同理把?变成;

    # Put the pieces of our forged ciphertext together to generate the full ciphertext
    forced_ciphertext = ciphertext[:total_prefix_length - 11] + bytes([semicolon]) + 
                        ciphertext[total_prefix_length - 10: total_prefix_length - 5] + 
                        bytes([equals]) + ciphertext[total_prefix_length - 4:]

    return forced_ciphertext

def main():
    encryption_oracle = Oracle()
    forced_ciphertext = cbc_bit_flip(encryption_oracle)

    # Check if the ciphertext was forced properly
    assert encryption_oracle.decrypt_and_check_admin(forced_ciphertext)

if __name__ == '__main__':
    main()

第三次实验

大作业

尝试第一届全国高校密码学挑战赛,目的为练习RSA算法常见漏洞的利用。

Alice 正在使用一个使用RSA加密的通信程序。她发了21条信息,但其中有些是相同的,还有些参数不是很合适。现在已经拦截了所有21条信息,告诉你密文的格式是各64bit的n、e、c拼接,明文的格式是64bit的标识符、32bit的序列号、若干个0和64bit的明文,总共521个bit。要求你尽可能多地破解Alice的信息。

利用四种常见的RSA破解方法进行破解:

  1. 共模攻击

​ 如果一对消息的模数和明文相同,而指数互质,则可以找到有x,y使得,则有

代码:

def common_mod(n, e1, c1, e2, c2):
    # 共模攻击
    x, y = exgcd(e1, e2)
    return pow(c1, x, n) * pow(c2, y, n) % n
  1. 广播攻击

​ 如果若干个消息明文和指数相同,指数和明文个数相同,而且模数互质,则有方程组

成立。可以利用中国剩余定理解出m^e<n1·n2·…·ne,从而可以直接解出明文

代码:

def broadcast(a, m, e):
    c = crt(a, m)
    l, r = 1, c
    while l + 1 < r:
        md = (l + r) // 2
        if md ** e < c:
            l = md
        else:
            r = md
    if l ** e == c:
        return l
    if r ** e == c:
        return r
    return 0
  1. 公因数攻击

​ 如果两个不同的模数n1=p·q1,n2=p·q2,其中q相同,那么可以直接求出p=gcd(n1,n2)。毕竟,求gcd的时间复杂度是远小于大数分解的,导致可以分解出模数的因子,进而求得密钥。

代码:

def common_factor(n1, e1, c1, n2, e2, c2):
    # 公因数攻击
    p = gcd(n1, n2)
    q1 = n1 // p
    q2 = n2 // p
    phi1 = (p - 1) * (q1 - 1)
    phi2 = (p - 1) * (q2 - 1)
    d1 = inverse(e1, phi1)
    d2 = inverse(e2, phi2)
    return pow(c1, d1, n1), pow(c2, d2, n2)
  1. p-1分解

​ 选取一个数B,使得k=B!,如果B足够大,那么就有(p-1)|k,则由于2^(p-1) ≡ 1 (mod p)可知p|(2^k – 1),又有p|n,所以p|a,a ≡ 2^k – 1 mod n。将次数与n取gcd即可分解模数。

代码:

def p_1(n, e, c, b):
    # p-1 分解
    k = 1
    for i in range(b):
        k *= i + 1
    p = gcd(pow(2, k, n) - 1, n)
    if p == 1 or p == n:
        return 0
    q = n // p
    if p * q != n:
        return 0
    phi = (p - 1) * (q - 1)
    d = inverse(e, phi)
    return pow(c, d, n)
return 0

执行以上算法可以求出第0 1 2 4 18 19帧,也就是明文的第0 1 5 6 10 11块,内容为 My secre t is a finstein. That ism A to B . Imagin

from Crypto.Util.number import inverse
from random import randint

def int2str(a):
    s = ''
    for i in range(8):
        s += chr(a % 256)
        a >>= 8

    s = s[::-1]
    a >>= 352

    return (a & ((1 << 32) - 1)), s

def check(a):
    a >>= 64
    a = a & ((1 << 352) - 1)
    if a == 0:
        return 1
    else:
        return 0

def gcd(a, b):
    if b == 0:
        return a
    return gcd(b, a % b)

def exgcd(a, b):
    if b == 0:
        return 1, 0
    x, y = exgcd(b, a % b)
    return y, x - a // b * y

def common_mod(n, e1, c1, e2, c2):
    # 共模攻击
    x, y = exgcd(e1, e2)
    return pow(c1, x, n) * pow(c2, y, n) % n

def common_factor(n1, e1, c1, n2, e2, c2):
    # 公因数攻击
    p = gcd(n1, n2)
    q1 = n1 // p
    q2 = n2 // p
    phi1 = (p - 1) * (q1 - 1)
    phi2 = (p - 1) * (q2 - 1)
    d1 = inverse(e1, phi1)
    d2 = inverse(e2, phi2)
    return pow(c1, d1, n1), pow(c2, d2, n2)

def crt(a, m):
    M = 1
    for i in m:
        M *= i

    res = 0
    for i in range(len(m)):
        res = (res + a[i] * M // m[i] * inverse(M // m[i], m[i])) % M

    return res

def broadcast(a, m, e):
    c = crt(a, m)
    l, r = 1, c
    while l + 1 < r:
        md = (l + r) // 2
        if md ** e < c:
            l = md
        else:
            r = md

    if l ** e == c:
        return l
    if r ** e == c:
        return r

    return 0

def p_1(n, e, c, b):
    # p-1 分解
    k = 1
    for i in range(b):
        k *= i + 1

    p = gcd(pow(2, k, n) - 1, n)
    if p == 1 or p == n:
        return 0
    q = n // p
    if p * q != n:
        return 0
    phi = (p - 1) * (q - 1)
    d = inverse(e, phi)
    return pow(c, d, n)

    return 0

def main():
    n = []
    e = []
    c = []

    for i in range(21):
        f = open('./data/Frame' + str(i))
        s = f.read()
        n.append(int(s[:256], 16))
        e.append(int(s[256:512], 16))
        c.append(int(s[512:], 16))

    m = [0 for i in range(21)]

    # 寻找共模攻击
    print("Common mod:")
    for i in range(21):
        for j in range(i):
            if n[i] == n[j]:
                # print(i, j, gcd(e[i], e[j]))
                m[i] = common_mod(n[i], e[i], c[i], e[j], c[j])
                m[j] = m[i]
                print(i, j, ":", int2str(m[i]))

    # 寻找公因数攻击
    print("Common factor:")
    for i in range(21):
        for j in range(i):
            if gcd(n[i], n[j]) > 1 and n[i] != n[j]:
                m[i], m[j] = common_factor(n[i], e[i], c[i], n[j], e[j], c[j])
                print(i, ":", int2str(m[i]))
                print(j, ":", int2str(m[j]))

    # 广播攻击
    print("Broadcast:")
    id = [3, 8, 12, 16, 20]
    temp = broadcast([c[i] for i in id], [n[i] for i in id], 5)
    for i in id:
        m[i] = temp
    print(int2str(temp))

    # p-1 分解
    print("P-1:")
    for i in range(1, 21):
        if m[i] > 0:
            continue
        print("Attempt:", i)
        temp = p_1(n[i], e[i], c[i], 10000)
        if temp > 0:
            print(i, ":", int2str(temp))

if __name__ == "__main__":
    main()

运行结果:

testforlast

根据以上结果已经可以求得部分明文。

运行还可以发现使用p-1算法对Frame6破解可以得到其明文信息为"Logic

之后继续尝试破解,这里使用费马分解法。

原理是:如果 21 个分片中,存在由两个比较接近的素数相乘得到的模数,那么,费 马分解法将很快可以破解它。根据费马分解法的原理,我们用 C 语言开发平台编写程序,对所有 21 个分片的模数 N 进行攻击尝试。主要代码如下:(伪代码)

lo1

根据该原理进行破解,可以得到Frame10的明文will get

根据以上得信息可以大概得到明文为My secret is a f ---------------- instein. That is "Logic will get --------- m A to B. Imagin -------

其中,m A to B这样的句式并不多见,考虑到to,我们可以猜测为from A to B。

通过搜集资料,可以找到爱因斯坦的一句名言Logic will get you from A to B. Imagination will take you everywhere. 这句名言很明显符合要求。

通过进行加密验证和暴力破解遍历,验证成功。我们可以得到5、9、12、13、14、15块得内容。考虑到instein.这样的单词很为少见,同时爱因斯坦名字叫做”Albert Einstein”自然可以考虑4的明文为”Albert E”,通过验证也成功。

现在已知的明文为:My secret is a f --------- Albert Einstein. That is "Logic will get you from A to B. Imagination will take you everywhere."

根据已知的明文,我们可以大胆猜测,这个f可能是 “famous” 或者 “from”,如果是”famous”则符合语境的短语有:”famous quotes”, “famous saying”, “famous nots”等,通过继续用加密密钥进行猜测明文攻击,验证了分块3为”amous sa”,所以4很明显为”ying”

My secret is a famous saying --- Albert Einstein. That is "Logic will get you from A to B. Imagination will take you everywhere."

根据这样的一个语法结构和剩余位置只有4个(还必须包括两个空格),可以继续猜测剩下的单词为of,进行加密验证,猜想正确。

所以全部明文为

My secret is a famous saying of Albert Einstein. That is "Logic will get you from A to B. Imagination will take you everywhere."

这样一想,还真是”Imagination will take you everywhere.”啊。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇