密码学大作业

密码学大作业

第一次大作业(0930)

题目1

题目内容

题目大致意思就是给你多个使用同一密钥进行流密码加密的密文,解密出来给定密文的信息。

思路

由ASCII码的性质可以知道,空格与字母异或会使字母大小写翻转,字母异或得到的非字母。故将密文分别两两异或,通过结果判断不同明文中可能存在空格的位置,将对应位置上的密文和空格异或,得到对应位置密钥,并最终对目标密文进行解密。
先前已经解出了最后一组明文,只需要将明文与对应的密文16进制异或,得到密钥再与给定的第八组明文进行异或即可。

源码
plaintext = "The secret message is: When using a stream cipher, never use the key more than once"
ciphertext = "32510ba9babebbbefd001547a810e67149caee11d945cd7fc81a05e9f85aac650e9052ba6a8cd8257bf14d13e6f0a803b54fde9e77472dbff89d71b57bddef121336cb85ccb8f3315f4b52e301d16e9f52f904"
ciphertext8 = "315c4eeaa8b5f8bffd11155ea506b56041c6a00c8a08854dd21a4bbde54ce56801d943ba708b8a3574f40c00fff9e00fa1439fd0654327a3bfc860b92f89ee04132ecb9298f5fd2d5e4b45e40ecc3b9d59e9417df7c95bba410e9aa2ca24c5474da2f276baa3ac325918b2daada43d6712150441c2e04f6565517f317da9d3"


def charListToHexList(charList):
    plainHexList = []
    for i in charList:
        plainHexList.append(hex(ord(i)))
    return plainHexList


if __name__ == '__main__':
    keyList = []
    plainList8 = []
    plaintext8 = ""
    plainList = list(plaintext)
    plainHexList = (charListToHexList(plainList))
    cipherHex = bytes.fromhex(ciphertext)
    cipherHex8 = bytes.fromhex(ciphertext8)

    for i in range(0, len(plainHexList)):
        keyList.append(cipherHex[i] ^ int(plainHexList[i], 16))
        plainList8.append(keyList[i] ^ cipherHex8[i])
    for i in range(0, len(plainList8)):
        plaintext8 += chr(plainList8[i])

    print("密钥(10进制ASCII码表示):", keyList)
    print("第八组明文:" + plaintext8)

结果
密钥(10进制ASCII码表示): [102, 57, 110, 137, 201, 219, 216, 204, 152, 116, 53, 42, 205, 99, 149, 16, 46, 175, 206, 120, 170, 127, 237, 40, 160, 127, 107, 201, 141, 41, 197, 11, 105, 176, 51, 154, 25, 248, 170, 64, 26, 156, 109, 112, 143, 128, 192, 102, 199, 99, 254, 240, 18, 49, 72, 205, 216, 232, 2, 208, 91, 169, 135, 119, 51, 93, 174, 252, 236, 213, 156, 67, 58, 107, 38, 139, 96, 191, 78, 240, 60, 154, 97]
第八组明文:We can see the point where the chip is unhappy if a wrong bit is sent and consumes 

题目2

题目内容

给定类维吉尼亚密码的流密码加密密文(不是相加模26,而是异或),解密出明文信息。

思路

设密钥的长度为x,密文为c,则c[i]、c[i+x]、c[i+2x]……都是明文与同一字符异或得到,所以我们可以猜测密钥长度,遍历密钥的每一个字符的每一个ASCII码可能值,直到对应的明文都是字母或标点为止,这样就能得到密钥和明文。

源码
import string

# text
ciphertext = "F96DE8C227A259C87EE1DA2AED57C93FE5DA36ED4EC87EF2C63AAE5B9A7EFFD673BE4ACF7BE8923CAB1ECE7AF2DA3DA44FCF7AE29235A24C963FF0DF3CA3599A70E5DA36BF1ECE77F8DC34BE129A6CF4D126BF5B9A7CFEDF3EB850D37CF0C63AA2509A76FF9227A55B9A6FE3D720A850D97AB1DD35ED5FCE6BF0D138A84CC931B1F121B44ECE70F6C032BD56C33FF9D320ED5CDF7AFF9226BE5BDE3FF7DD21ED56CF71F5C036A94D963FF8D473A351CE3FE5DA3CB84DDB71F5C17FED51DC3FE8D732BF4D963FF3C727ED4AC87EF5DB27A451D47EFD9230BF47CA6BFEC12ABE4ADF72E29224A84CDF3FF5D720A459D47AF59232A35A9A7AE7D33FB85FCE7AF5923AA31EDB3FF7D33ABF52C33FF0D673A551D93FFCD33DA35BC831B1F43CBF1EDF67F0DF23A15B963FE5DA36ED68D378F4DC36BF5B9A7AFFD121B44ECE76FEDC73BE5DD27AFCD773BA5FC93FE5DA3CB859D26BB1C63CED5CDF3FE2D730B84CDF3FF7DD21ED5ADF7CF0D636BE1EDB79E5D721ED57CE3FE6D320ED57D469F4DC27A85A963FF3C727ED49DF3FFFDD24ED55D470E69E73AC50DE3FE5DA3ABE1EDF67F4C030A44DDF3FF5D73EA250C96BE3D327A84D963FE5DA32B91ED36BB1D132A31ED87AB1D021A255DF71B1C436BF479A7AF0C13AA14794"


# 将16进制长串转为10进制ASCII码
def ciphertextToList(ciphertext):
    cipherList = []
    for i in range(0, len(ciphertext), 2):
        cipherList.append(int(ciphertext[i:2 + i], 16))
    return cipherList


# 选取密钥
def keySelect(subCipher):
    charList = string.ascii_letters + " " + "," + "."  # 这仨不能用string.punctuation,问就是乱码
    KeyTest = []
    KeyFin = []
    for i in range(0x00, 0xFF):
        KeyTest.append(i)
        KeyFin.append(i)
    for i in KeyTest:
        for j in subCipher:
            if chr(i ^ j) not in charList:
                KeyFin.remove(i)
                break
    return KeyFin


# main
def Decryption(cipherList):
    KeyFin = []
    KeyLenFin = 0
    for keyLen in range(1, 14):
        alternateKey = []
        for i in range(0, keyLen):
            subArray = cipherList[i::keyLen]
            keyAlter = keySelect(subArray)
            if not keyAlter:
                break
            else:
                alternateKey.insert(i, keyAlter)
        if alternateKey:
            KeyFin = alternateKey
            KeyLenFin = keyLen
            print("长度:", KeyLenFin)
            print("密钥(十进制ASCII码):", KeyFin)
            plaintext = ''
    for i in range(0, len(cipherList)):
        plaintext = plaintext + chr(cipherList[i] ^ KeyFin[i % len(KeyFin)][0])
    print('明文:', plaintext)
    return 0


cipherList = ciphertextToList(ciphertext)

Decryption(cipherList)
结果
长度: 7
密钥(十进制ASCII码): [[186], [31], [145], [178], [83], [205], [62]]
明文: 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

题目内容

Break repeating-key XOR - The Cryptopals Crypto Challenges

思路
  1. 猜测密钥长度KeySize,如从2-30
  2. 计算两个字符串之间的汉明距离
  3. 每次尝试KeySize时,从文件开头截取四个块,两两组合计算其汉明距离
  4. 选取3个最小的汉明距离所对应的KeySize作为密钥长度备选
  5. 知道KeySize大小后,将密文按KeySize大小切片,每片取第一个、第二个……组成新的块
  6. 对新的块而言,解密思路类似作业第2题
  7. 将每个单字符异或的Key拼接起来即为所要的最终的Key,将明文拼接即为完整明文
源码
import base64
from functools import partial
import itertools

letterFreqs = \
    {'a': 0.0651738, 'b': 0.0124248, 'c': 0.0217339,
     'd': 0.0349835, 'e': 0.1041442, 'f': 0.0197881,
     'g': 0.0158610, 'h': 0.0492888, 'i': 0.0558094,
     'j': 0.0009033, 'k': 0.0050529, 'l': 0.0331490,
     'm': 0.0202124, 'n': 0.0564513, 'o': 0.0596302,
     'p': 0.0137645, 'q': 0.0008606, 'r': 0.0497563,
     's': 0.0515760, 't': 0.0729357, 'u': 0.0225134,
     'v': 0.0082903, 'w': 0.0171272, 'x': 0.0013692,
     'y': 0.0145984, 'z': 0.0007836, ' ': 0.1918182}  # 字母频率


def GetScore(ib):  # 计算输入文本的分值
    score = 0
    for b in ib:
        score += letterFreqs.get(chr(b).lower(), 0)
    return score


def SingleXOR(ib, KeyValue):  # 对每个字符和Key异或
    output = b""
    for ch in ib:
        output += bytes([ch ^ KeyValue])
    return output


def SingleXorBreak(ciphertext):  # 破解单字符异或
    choices = []
    for KeyChoice in range(256):
        PlaintextChoice = SingleXOR(ciphertext, KeyChoice)
        ScoreChoice = GetScore(PlaintextChoice)
        result = {
            "key": KeyChoice,
            "score": ScoreChoice,
            "plaintext": PlaintextChoice
        }
        choices.append(result)
    return sorted(choices, key=lambda c: c['score'], reverse=True)[0]


def HammingDistance(bstr1, bstr2):  # 汉明距离
    distance = 0
    for b1, b2 in zip(bstr1, bstr2):
        dis = b1 ^ b2
        distance += sum([1 for bit in bin(dis) if bit == "1"])
    return distance


def KeyXOR(plaintext, Key):
    ciphertext = b""
    i = 0
    for b in plaintext:
        ciphertext += bytes([b ^ Key[i]])
        i = i + 1 if i < len(Key) - 1 else 0
    return ciphertext


def KeyBreak(text):
    NorDistances = {}
    for KeySize in range(2, 30):
        Slices = [text[i:i + KeySize] for i in range(0, len(text), KeySize)][:4]
        TempDistance = 0
        SlicePair = itertools.combinations(Slices, 2)
        for (a, b) in SlicePair:
            TempDistance += HammingDistance(a, b)
        TempDistance /= 6
        NorDistance = TempDistance / KeySize  # 对汉明距离进行归一化
        NorDistances[KeySize] = NorDistance
    PossibleKeySize = sorted(NorDistances, key=NorDistances.get)[:3]  # 取最小的3个汉明距离所对应的KeySize
    PossiblePlaintext = []
    for i in PossibleKeySize:
        TempKey = b""
        for j in range(i):
            block = b""
            for k in range(j, len(text), i):
                block += bytes([text[k]])
            TempKey += bytes([SingleXorBreak(block)["key"]])
        PossiblePlaintext.append((KeyXOR(text, TempKey), TempKey))
    return max(PossiblePlaintext, key=lambda k: GetScore(k[0]))  # 选取分数最高,即最有可能是正确明文的那一组


if __name__ == '__main__':
    with open("H3.txt") as fp:
        text = base64.b64decode(fp.read())
    result = KeyBreak(text)
    print("Key=", result[1].decode())
    print("----------result----------")
    print(result[0].decode().rstrip())


结果
Key= Terminator X: Bring the noise
----------result----------
I'm back and I'm ringin' the bell 
A rockin' on the mike while the fly girls yell 
In ecstasy in the back of me 
Well that's my DJ Deshay cuttin' all them Z's 
Hittin' hard and the girlies goin' crazy 
Vanilla's on the mike, man I'm not lazy. 

I'm lettin' my drug kick in 
It controls my mouth and I begin 
To just let it flow, let my concepts go 
My posse's to the side yellin', Go Vanilla Go! 

Smooth 'cause that's the way I will be 
And if you don't give a damn, then 
Why you starin' at me 
So get off 'cause I control the stage 
There's no dissin' allowed 
I'm in my own phase 
The girlies sa y they love me and that is ok 
And I can dance better than any kid n' play 

Stage 2 -- Yea the one ya' wanna listen to 
It's off my head so let the beat play through 
So I can funk it up and make it sound good 
1-2-3 Yo -- Knock on some wood 
For good luck, I like my rhymes atrocious 
Supercalafragilisticexpialidocious 
I'm an effect and that you can bet 
I can take a fly girl and make her wet. 

I'm like Samson -- Samson to Delilah 
There's no denyin', You can try to hang 
But you'll keep tryin' to get my style 
Over and over, practice makes perfect 
But not if you're a loafer. 

You'll get nowhere, no place, no time, no girls 
Soon -- Oh my God, homebody, you probably eat 
Spaghetti with a spoon! Come on and say it! 

VIP. Vanilla Ice yep, yep, I'm comin' hard like a rhino 
Intoxicating so you stagger like a wino 
So punks stop trying and girl stop cryin' 
Vanilla Ice is sellin' and you people are buyin' 
'Cause why the freaks are jockin' like Crazy Glue 
Movin' and groovin' trying to sing along 
All through the ghetto groovin' this here song 
Now you're amazed by the VIP posse. 

Steppin' so hard like a German Nazi 
Startled by the bases hittin' ground 
There's no trippin' on mine, I'm just gettin' down 
Sparkamatic, I'm hangin' tight like a fanatic 
You trapped me once and I thought that 
You might have it 
So step down and lend me your ear 
'89 in my time! You, '90 is my year. 

You're weakenin' fast, YO! and I can tell it 
Your body's gettin' hot, so, so I can smell it 
So don't be mad and don't be sad 
'Cause the lyrics belong to ICE, You can call me Dad 
You're pitchin' a fit, so step back and endure 
Let the witch doctor, Ice, do the dance to cure 
So come up close and don't be square 
You wanna battle me -- Anytime, anywhere 

You thought that I was weak, Boy, you're dead wrong 
So come on, everybody and sing this song 

Say -- Play that funky music Say, go white boy, go white boy go 
play that funky music Go white boy, go white boy, go 
Lay down and boogie and play that funky music till you die. 

Play that funky music Come on, Come on, let me hear 
Play that funky music white boy you say it, say it 
Play that funky music A little louder now 
Play that funky music, white boy Come on, Come on, Come on 
Play that funky music

进程已结束,退出代码0

题目4

题目内容

Cracking SHA1-Hashed Passwords

源码
import hashlib
import itertools
import datetime

starttime = datetime.datetime.now()
hash1 = "67ae1a64661ac8b4494666f58c4822408dd0a3e4"
searchList = [['Q', 'q'], ['W', 'w'], ['I', 'i'], ['N', 'n'], ['5', '%'], ['8', '('], ['0', '='], ['*', '+']]


def encryptSha(str):
    sha = hashlib.sha1(str.encode("utf-8"))
    encrypts = sha.hexdigest()
    return encrypts


if __name__ == '__main__':
    zeroStr = "00000000"
    str4 = ""
    zeroList = list(zeroStr)
    for a in range(0, 2):
        zeroList[0] = searchList[0][a]
        for b in range(0, 2):
            zeroList[1] = searchList[1][b]
            for c in range(0, 2):
                zeroList[2] = searchList[2][c]
                for d in range(0, 2):
                    zeroList[3] = searchList[3][d]
                    for e in range(0, 2):
                        zeroList[4] = searchList[4][e]
                        for f in range(0, 2):
                            zeroList[5] = searchList[5][f]
                            for g in range(0, 2):
                                zeroList[6] = searchList[6][g]
                                for h in range(0, 2):
                                    zeroList[7] = searchList[7][h]
                                    finStr = "".join(zeroList)
                                    for i in itertools.permutations(finStr, 8):
                                        str4 = encryptSha("".join(i))
                                        if str4 == hash1:
                                            print("".join(i))
                                            endtime = datetime.datetime.now()
                                            print("time: " + str(endtime - starttime))

结果
(Q=win*5
time: 0:00:06.105986

第二次大作业(1024)

题目1

题目内容

大致意思就是说解密CBC模式下使用PKCS #5填充的密文,通过与服务器进行交互来得到是否没有错误的信息。

思路
源码
# -*- coding: cp936 -*-

from oracle import *
from Crypto.Util import strxor
import re


def main():
    CipherText = "9F0B13944841A832B2421B9EAF6D9836813EC9D944A5C8347A7CA69AA34D8DC0" \
                 "DF70E343C4000A2AE35874CE75E64C31"
    BlockCount = 2
    Division = len(CipherText) / (BlockCount + 1)
    CipherTextList = re.findall(".{" + str(Division) + "}", CipherText)

    Oracle_Connect()
    PlainText = []
    IVALUE = []
    for bc in range(0, BlockCount):  # 对两组密文分别求解
        print "-" * 60
        print "Now Detecting Block " + str(bc + 1) + "."
        IV = CipherTextList[bc]
        ivalue = []  # 将解密后的ciphertext初始化
        TempIV = "0" * 32  # 初始化 IV
        TempIV = re.findall('.{2}', TempIV)[::-1]
        padding = 1
        for l in range(16):
            print "-" * 60
            print "Now Detecting Last " + str(l + 1) + ' Byte In Block.'
            for ll in range(l):
                TempIV[ll] = hex(int(ivalue[ll], 16) ^ padding)[2:].zfill(2)  # 更新 TempIV

            for n in range(0, 256):  # 从0x00-0xFF遍历寻找符合条件的字节
                TempIV[l] = hex(n)[2:].zfill(2)
                data = "".join(TempIV[::-1]) + CipherTextList[bc + 1]
                print data
                ctext = [(int(data[i:i + 2], 16)) for i in range(0, len(data), 2)]
                Feedback = Oracle_Send(ctext, 2)
                if str(Feedback) == "1":
                    ivalue += [hex(n ^ padding)[2:].zfill(2)]
                    break
            print "-" * 60
            print "Now The Value of IV is: ", "".join(TempIV[::-1])
            print "The Temporary Ciphertext after Decryption is: ", "".join(ivalue[::-1])

            padding += 1
        ivalue = "".join(ivalue[::-1])
        IVALUE += [ivalue]
        # IV与CADe异或求明文
        plaintext = re.findall("[0-9a-f]+", str(hex(int(IV, 16) ^ int("".join(ivalue), 16))))[1].decode("hex")
        PlainText += [plaintext]
        print "Detecting Block", bc + 1, "Finished."
        print "The ivalue" + str(bc + 1) + " is:", ivalue
        print "The Plaintext" + str(bc + 1) + "is:", plaintext
        print "-" * 60
    Oracle_Disconnect()
    print "The Ciphertext after Decryption is: ", "".join(IVALUE)
    print "The Plaintext is:", "".join(PlainText)


if __name__ == '__main__':
    main()
结果

在这里插入图片描述

题目2

题目内容

Set2 - The Cryptopals Crypto Challenges

源码
c7.py
from Crypto.Cipher import AES
from c9 import *


def decrypt_aes_128_ecb(data, key):
    cipher = AES.new(key, AES.MODE_ECB)
    return pkcs7_unpad(cipher.decrypt(data))
c8.py
from Crypto.Cipher.AES import block_size


def count_aes_ecb_repetitions(ciphertext):
    chunks = [ciphertext[i:i + block_size] for i in range(0, len(ciphertext), block_size)]
    number_of_duplicates = len(chunks) - len(set(chunks))
    return number_of_duplicates


def detect_ecb_encrypted_ciphertext(ciphertexts):
    best = (-1, 0)
    for i in range(len(ciphertexts)):
        repetitions = count_aes_ecb_repetitions(ciphertexts[i])
        best = max(best, (i, repetitions), key = lambda t: t[1])
    return best


def main():
    ciphertexts = [bytes.fromhex(line.strip()) for line in open("8.txt")]
    result = detect_ecb_encrypted_ciphertext(ciphertexts)
    print("The   ",result[0])
    print("the",result[1])
    print(result[0])
    assert result[0] == 132

if __name__ == '__main__':
    main()
c9.py
def pkcs7_padding(message,block_size):
    if len(message) == block_size:
        return message
    padding_thing = block_size - (len(message) % block_size)  # 填充内容、长度计算
    return message + bytes([padding_thing] * padding_thing)


def is_pkcs7_padded(bin_data):
    padding = bin_data[-bin_data[-1]:]
    return all(padding[b] == len(padding) for b in range(0, len(padding)))


def pkcs7_unpad(padded_message):
    if len(padded_message) == 0:
        raise Exception("the input must contain at least one byte!")
    if not is_pkcs7_padded(padded_message):
        return padded_message
    padded_len = padded_message[len(padded_message) - 1]
    return padded_message[:-padded_len]


def c9_main():
    message=b"YELLOW SUBMARINE"
    padded_message = pkcs7_padding(message, 20)
    print(padded_message)
    assert pkcs7_unpad(padded_message) == message


if __name__ == '__main__':
    c9_main()
c10.py
from Crypto.Cipher.AES import block_size
from base64 import b64decode
from c7 import *
from c9 import *


def encrypt_aes_128_ecb(message, key):
    cipher_message = AES.new(key, AES.MODE_ECB)
    return cipher_message.encrypt(pkcs7_padding(message, AES.block_size))


def ez_xor(bin_data1, bin_data2):
    return bytes([b1 ^ b2 for b1, b2 in zip(bin_data1, bin_data2)])


def encrypt_aes_128_cbc(message, key, iv):
    ciphertext = b""
    p = iv
    for i in range(0, len(message), AES.block_size):
        temp_plaintext_block = pkcs7_padding(message[i: i + AES.block_size], AES.block_size)
        block_ciphertext_in = ez_xor(temp_plaintext_block, p)
        encrypted_block = encrypt_aes_128_ecb(block_ciphertext_in, key)
        ciphertext += encrypted_block
        p = encrypted_block

    return ciphertext


def decrypt_aes_128_cbc(data, key, iv, unpad=True):
    plaintext = b""
    p = iv
    for i in range(0, len(data), AES.block_size):
        temp_ciphertext_block = data[i:i + AES.block_size]
        decrypted_blcok = decrypt_aes_128_ecb(temp_ciphertext_block, key)
        plaintext += ez_xor(p, decrypted_blcok)
        p = temp_ciphertext_block

    return pkcs7_unpad(plaintext) if unpad else plaintext


def main():
    iv = b'\x00' * AES.block_size
    key = b"YELLOW SUBMARINE"
    with open("10.txt") as f:
        bin_data = b64decode(f.read())
    print(decrypt_aes_128_cbc(bin_data, key, iv).decode().rstrip())


if __name__ == '__main__':
    main()

c12.py
from Crypto import Random

from c10 import *


class ECBOracle:
    def __init__(self, secret_padding):
        self._key = Random.new().read(AES.key_size[0])
        self._secret_padding = secret_padding

    def encrypt(self, data):
        return encrypt_aes_128_ecb(data + self._secret_padding, self._key)


def find_block_length(encryption_oracle):
    my_text = b""
    ciphertext = encryption_oracle.encrypt(my_text)
    initial_len = len(ciphertext)
    new_len = initial_len

    while new_len == initial_len:
        my_text += b"A"
        ciphertext = encryption_oracle.encrypt(my_text)
        new_len = len(ciphertext)

    return new_len - initial_len

c14.py(第6题)
from random import randint

from c12 import *
from c8 import *


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):
        return encrypt_aes_128_ecb(self._random_prefix + data + self._secret_padding, self._key)


def get_next_byte(prefix_length, block_length, curr_decrypted_message, encryption_oracle):
    length_to_use = (block_length - prefix_length - (1 + len(curr_decrypted_message))) % block_length
    my_input = b"A" * length_to_use
    cracking_length = prefix_length + length_to_use + len(curr_decrypted_message) + 1
    real_ciphertext = encryption_oracle.encrypt(my_input)
    for i in range(256):
        fake_ciphertext = encryption_oracle.encrypt(my_input + curr_decrypted_message + bytes([i]))
        if fake_ciphertext[:cracking_length] == real_ciphertext[:cracking_length]:
            return bytes([i])
    return b""


def has_equal_block(ciphertext, block_length):
    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):
    ciphertext1 = encryption_oracle.encrypt(b"")
    ciphertext2 = encryption_oracle.encrypt(b"a")
    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
    for i in range(block_length):
        fake_input = bytes([0] * (2 * block_length + i))
        ciphertext = encryption_oracle.encrypt(fake_input)
        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_harder_decryption(encryption_oracle):
    block_length = find_block_length(encryption_oracle)
    ciphertext = encryption_oracle.encrypt(bytes([0] * 64))
    assert count_aes_ecb_repetitions(ciphertext) > 0
    prefix_length = find_prefix_length(encryption_oracle, block_length)
    mysterious_text_length = len(encryption_oracle.encrypt(b"")) - prefix_length
    secret_padding = b""
    for i in range(mysterious_text_length):
        secret_padding += get_next_byte(prefix_length, block_length, secret_padding, encryption_oracle)
    return secret_padding


def main():
    secret_padding = b64decode("Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWctdG9wIGRvd24gc28gbXkg"
                               "aGFpciBjYW4gYmxvdwpUaGUgZ2lybGllcyBvbiBzdGFuZGJ5IHdhdmluZyBq"
                               "dXN0IHRvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5vLCBJIGp1c3QgZHJvdmUg"
                               "YnkK")
    oracle = HarderECBOracle(secret_padding)
    discovered_secret_padding = byte_at_a_time_ecb_harder_decryption(oracle)
    print(pkcs7_unpad(discovered_secret_padding))


if __name__ == '__main__':
    main()
c15.py(第7题)
def is_pkcs7_padded(bin_data):
    padding = bin_data[-bin_data[-1]:]
    return all(padding[b] == len(padding) for b in range(0, len(padding)))
c16.py(第8题)
from Crypto.Cipher import AES
from Crypto import Random
from c10 import *


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):
        data = data.replace(";", "").replace("=", "")
        plaintext = (self._prefix + data + self._suffix).encode()
        return encrypt_aes_128_cbc(plaintext, self._key, self._iv)

    def decrypt_and_check_admin(self, ciphertext):
        data = decrypt_aes_128_cbc(ciphertext, self._key, self._iv)
        return b";admin=true" in data


def find_block_length(encryption_oracle):
    text = ""
    ciphertext = encryption_oracle(text)
    ordi_len = len(ciphertext)
    new_len = ordi_len
    while new_len == ordi_len:
        text += "A"
        ciphertext = encryption_oracle(text)
        new_len = len(ciphertext)
    return new_len - ordi_len


def find_prefix_length(encryption_oracle, block_length):
    ciphertext_a = encryption_oracle("A")
    ciphertext_b = encryption_oracle("B")
    common_len = 0
    while ciphertext_a[common_len] == ciphertext_b[common_len]:
        common_len += 1
    common_len = int(common_len / block_length) * block_length
    for i in range(1, block_length + 1):
        ciphertext_a = encryption_oracle("A" * i + "X")
        ciphertext_b = encryption_oracle("A" * i + "Y")
        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_atk(encryption_oracle):
    block_length = find_block_length(encryption_oracle.encrypt)
    prefix_length = find_prefix_length(encryption_oracle.encrypt, block_length)
    addition_prefix_bytes = (block_length - (prefix_length % block_length)) % block_length
    total_prefix_length = prefix_length + addition_prefix_bytes
    plaintext = "?admin?true"
    additional_plaintext_bytes = (block_length - (len(plaintext) % block_length)) % block_length
    final_palintext = additional_plaintext_bytes * "?" + plaintext
    ciphertext = encryption_oracle.encrypt(addition_prefix_bytes * "?" + final_palintext)
    semicolon = ciphertext[total_prefix_length - 11] ^ ord("?") ^ ord(";")
    equals = ciphertext[total_prefix_length - 5] ^ ord("?") ^ ord("=")
    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_atk(encryption_oracle)
    print(encryption_oracle.decrypt_and_check_admin(forced_ciphertext))


if __name__ == '__main__':
    main()
结果
c14(第6题)
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"
c16(第8题)
True

题目3

题目内容

MTC3 - AES key

思路
  1. 根据参考资料,16.1-16.1.5,可以算出?的值
  2. 根据文件得到kseed
  3. 把kseed和c相连,计算d的sha1,前十六位是ka,后十六位是kb
  4. Ka和kb奇偶校验生成key
  5. 把密文以base64解码,用key解密
源码
# coding=utf-8


import base64
import re

from Crypto.Cipher import AES
from Crypto.Hash import SHA


def odd_even_verify(ka):  # 调整奇偶校验位
    k = []
    for i in ka:
        if bin(int(i, 16) >> 1).count('1') % 2 == 0:  # 若1的个数为偶数
            k += [hex(1 + (int(i, 16) >> 1 << 1))[2:].zfill(2)]  # 调整为奇数
        else:
            k += [hex((int(i, 16) >> 1 << 1))[2:].zfill(2)]  # 不动
    return ''.join(k)


def get_sha1(d):
    h = SHA.new()
    h.update(d)  # 计算d的sha1
    return h.hexdigest()[:32]  # 获取sha1的前32位


def main():
    c = '9MgYwmuPrjiecPMx61O6zIuy3MtIXQQ0E59T3xB6u0Gyf1gYs2i3K9Jxaa0zj4gTMazJuApwd6' \
        '+jdyeI5iGHvhQyDHGVlAuYTgJrbFDrfB22Fpil2NfNnWFBTXyf7SDI '  # C由文档中获取
    c = base64.b64decode(c)
    code = '12345678<8<<<1110182<1111167<<<<<<<<<<<<<<<4'
    code_no = code[:9]
    code_verify = code[9]
    nation = code[10:13]
    birth = code[13:19]
    birth_verify = code[19]
    sex = code[20]
    date_end = code[21:27]
    date_end_verify = code[27]
    other = code[28:]
    # 按照文件提取code中机读区对应的信息
    info = code_no + code_verify + birth + birth_verify + date_end + date_end_verify  # 得到机读区信息
    k_seed = get_sha1(info)  # 获取k_seed
    d = (k_seed + '0' * 7 + '1').decode('hex')  # 连接k_seed和c
    key = get_sha1(d)  # 获取key的sha1
    k1 = odd_even_verify(re.findall('.{2}', key[:16]))  # 对前半后半分别进行奇偶校验位的调整
    k2 = odd_even_verify(re.findall('.{2}', key[16:]))
    key = k1 + k2  # 再将密钥key拼接起来
    print 'The key is:', key
    ciphertext = AES.new(key.decode('hex'), AES.MODE_CBC, ('0' * 32).decode('hex'))
    print 'The M is:', ciphertext.decrypt(c)


if __name__ == '__main__':
    main()
结果
The key is: ea8645d97ff725a898942aa280c43179
The M is: Herzlichen Glueckwunsch. Sie haben die Nuss geknackt. Das Codewort lautet: Kryptographie!      

第三次大作业(RSA大礼包)

题目

题目内容

这次的题目是2016年密码挑战赛的第三题,大意上是说现在有一个RSA的加解密软件,并且有一些明密文对和对应参数,同时截获了一些密文。现在利用多种针对RSA的攻击,对明密文对和对应的参数,以及截获的密文进行分析和挖掘。从加密数据中,恢复通关密语和RSA的体制参数,以及对应的明文。

思路

利用多种针对RSA的攻击,如RSA共模攻击、低指数攻击、p-1分解法、公因数攻击、Fermat攻击等,尽可能多得在所截获的数据中,挖掘出明文和参数。

源码
共模攻击

如果一对消息的模数和明文相同,而指数互质,那么可以找到 x , y x,y x,y 使
x e 1 + y e 2 = 1 xe_1+ye_2=1 xe1+ye2=1 那么有 c 1 x + c 2 y ≡ m   m o d   n c_1^x+c_2^y≡m ~mod~n c1x+c2ym mod n
Frame0、Frame4采用该种攻击方法

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


# 共模攻击
def sameMod():
    index1 = 0
    index2 = 0
    for i in range(21):
        for j in range(i + 1, 21):
            if F[i] == F[j]:
                index1, index2 = i, j
    n = int(F[index1], 16)
    e1 = int(T[index1], 16)
    e2 = int(T[index2], 16)
    c1 = int(S[index1], 16)
    c2 = int(S[index2], 16)
    s = egcd(e1, e2)
    s1 = s[1]
    s2 = s[2]
    if s1 < 0:
        s1 = - s1
        c1 = gmpy2.invert(c1, n)
    elif s2 < 0:
        s2 = - s2
        c2 = gmpy2.invert(c2, n)

    m = pow(c1, s1, n) * pow(c2, s2, n) % n

    result = binascii.a2b_hex(hex(m)[-16:]).decode('ascii')

    return result
公因数攻击

Frame1、Frame18采用该种攻击方法

def sameFactor():
    index = []
    for i in range(21):
        for j in range(i + 1, 21):
            if int(F[i], 16) == int(F[j], 16):
                continue
            prime = gmpy2.gcd(int(F[i], 16), int(F[j], 16))
            if prime != 1:
                index.append(i)
                index.append(j)
                frameP = prime
    frameQ1 = int(F[index[0]], 16) // frameP
    frameQ18 = int(F[index[1]], 16) // frameP

    framePhi1 = (frameP - 1) * (frameQ1 - 1)
    framePhi18 = (frameP - 1) * (frameQ18 - 1)

    frameD1 = gmpy2.invert(int(T[index[0]], 16), framePhi1)
    frameD18 = gmpy2.invert(int(T[index[1]], 16), framePhi18)

    plaintext1 = gmpy2.powmod(int(S[index[0]], 16), frameD1, int(F[index[0]], 16))
    plaintext18 = gmpy2.powmod(int(S[index[1]], 16), frameD18, int(F[index[1]], 16))

    fin1 = binascii.a2b_hex(hex(plaintext1)[-16:]).decode('ascii')
    fin18 = binascii.a2b_hex(hex(plaintext18)[-16:]).decode('ascii')

    print('Frame', index[0], ':', fin1, sep='')
    print('Frame', index[1], ':', fin18, sep='')
    return 0

低指数攻击

若多个消息明文和指数相同,指数和明文个数相同,而且模数互质,那么可以列出方程组 { m e ≡ c 1   m o d   n 1 m e ≡ c 2   m o d   n 2 m e ≡ c 3   m o d   n 3 . . . m e ≡ c e   m o d   n e \left\{\begin{matrix} m^e≡c_1~mod~n_1\\m^e≡c_2~mod~n_2\\m^e≡c_3~mod~n_3\\...\\m^e≡c_ e~mod~n_e \end{matrix}\right. mec1 mod n1mec2 mod n2mec3 mod n3...mece mod ne
利用中国剩余定理可以解出 m e < n 1 n 2 n 3 . . . n e m^e<n_1n_2n_3...n_e me<n1n2n3...ne ,从而直接开根解出明文

Frame3、Frame8、Frame12、Frame16、Frame20采用该种攻击方法

def lowE():
    sessions = [{"c": int(S[3], 16), "n": int(F[3], 16)},
                {"c": int(S[8], 16), "n": int(F[8], 16)},
                {"c": int(S[12], 16), "n": int(F[12], 16)},
                {"c": int(S[16], 16), "n": int(F[16], 16)},
                {"c": int(S[20], 16), "n": int(F[20], 16)}]
    data = []
    for session in sessions:
        data += [(session['c'], session['n'])]
    x, y = ChineseRemainder(data)

    pt = gmpy2.iroot(gmpy2.mpz(x), 5)
    print(binascii.a2b_hex(hex(pt[0])[-16:]).decode('ascii'))

    return binascii.a2b_hex(hex(pt[0])[2:])
Pollard p-1分解法

选取一个数 B B B,并令 k = B ! k=B! k=B!,如果 B B B足够大,则有 p − 1 ∣ k p-1|k p1k。由
2 p − 1 ≡ 1   m o d   n 2^{p-1}≡1~mod~n 2p11 mod n,有 p ∣ 2 k − 1 p|2^k-1 p2k1。又有 p ∣ n p|n pn,所以有 p ∣ a p|a pa a ≡ 2 k − 1   m o d   n 。 a≡2^k-1~mod~n。 a2k1 mod n将幂与 n n n取最大公因数即可分解模数。

Frame2、Frame6、Frame19均采用该种攻击方法

def p1(n):
    B = pow(2,20)
    a = 2
    for i in range(2, B + 1):
        a = pow(a, i, n)
        d = gmpy2.gcd(a - 1, n)
        if (d >= 2) and (d <= (n - 1)):
            q = n // d
            n = q * d
    return d


def pollardResolve():
    index_list = [2, 6, 19]
    plaintext = []
    for i in range(3):
        N = int(F[index_list[i]], 16)
        c = int(S[index_list[i]], 16)
        e = int(T[index_list[i]], 16)
        p = p1(N)
        q = N // p
        framePhi = (p - 1) * (q - 1)
        d = gmpy2.invert(e, framePhi)
        m = gmpy2.powmod(c, d, N)
        plaintext.append(binascii.a2b_hex(hex(m)[2:]))
        print(f"Frame{index_list[i]}:{(binascii.a2b_hex(hex(m)[-16:])).decode('ascii')}")
    return plaintext
费马分解法

p、q比较接近时,可以使用这种攻击方法
Frame10采用该种攻击方法

def fermat(n):
    B = math.factorial(2 ** 14)
    v = 0
    i = 0
    u0 = gmpy2.iroot(n, 2)[0] + 1
    while i <= (B - 1):
        u = (u0 + i) * (u0 + i) - n
        if gmpy2.is_square(u):
            v = gmpy2.isqrt(u)
            break
        i += 1
    p = u0 + i + v
    return p


def fermatResolve():
    for i in range(10, 14):
        N = int(F[i], 16)
        p = fermat(N)
结果
共模攻击
My secre
公因数攻击
Frame1:. Imagin
Frame18:m A to B
低指数攻击
t is a f
Pollard p-1 分解法
Frame2: That is
Frame6: "Logic 
Frame19:instein.
费马分解法
Frame10:will get

通过上边的攻击我们可以破解出Frame0、1、2、3、4、6、8、10、12、16、18、19、20,再通过某些科学手段(百度)得到以下结果

"My secret is a famous saying of Albert Einstein. That is "Logic will get you from A to B. Imagination will take you everywhere.""
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值