RiK's Blog - cryptopals
type: channel
link: https://pwnerik.cn/tag/cryptopals/
items: 西电网安实验班 现代密码学 实验三
items: 西电网安实验班 现代密码学 实验二
items: 西电网安实验班 现代密码学 实验一

西电网安实验班 现代密码学 实验三
type: item
date: 2025-11-25T11:01:00+00:00
description: 题目一Project Euler RSA Encryption Problem 182链接：https://projecteuler.net/problem=182RSA 有可能存在加密但没有加密的情况。起初遍历所有可能的消息并逐个加密来计算 unconsealed messages count。后来发现有以下公式：$$ N = (1 + \gcd(e-1, p-1)) \times (1 + \gcd(e-1, q-1)) $$from gmpy2 import gcd p = 1009 q = 3643 phi = (p - 1) * (q - 1) min_unconcealed = p * q candidates = [] for e in range(3, phi, 2): if gcd(e, phi) != 1: continue count = (1 + gcd(e - 1, p - 1)) * (1 + gcd(e - 1, q - 1)) if count < min_unconcealed: min_unconcealed = count candidates = [e] elif count == min_unconcealed: candidates.append(e) print(f"min_unconcealed: {min_unconcealed}") print("e:", end=" ") for e in candidates[:50]: print(f'{e} ', end='') print()题目二Implement RSA链接：https://www.cryptopals.com/sets/5/challenges/39只是实现一个简单的 RSA，要求手写 exgcd 和 modinv，那就手写吧。from gmpy2 import next_prime, powmod from secrets import randbits from Crypto.Util.number import bytes_to_long def gcd(a, b): while b != 0: a, b = b, a % b return a def ex_gcd(a, b): if b == 0: return 1, 0 else: x1, y1 = ex_gcd(b, a % b) x = y1 y = x1 - (a // b) * y1 return x, y def inverse(a, m): x, _ = ex_gcd(a, m) return (x % m + m) % m e = 3 while True: p = next_prime(randbits(64)) q = next_prime(randbits(64)) n = p * q phi = (p - 1) * (q - 1) if gcd(e, phi) == 1: break m = bytes_to_long(b'pwnerik.cn') assert 0 <= m < n c = powmod(m, e, n) d = inverse(e, phi) msg = powmod(c, d, n) assert m == msg这个实现有很多问题，例如 e、p、q 太小。

西电网安实验班 现代密码学 实验二
type: item
date: 2025-11-25T03:47:00+00:00
description: 题目一地址：https://mysterytwister.org/challenges/level-2/aes-key--encoded-in-the-machine-readable-zone-of-a-european-epassport俄罗斯的电子护照编号编码了一个 AES 密钥，我们需要解码出这个密钥然后解密 AES CBC 模式密文。题目说 IV 初始化为 0，那么我们只需要得到密钥。目标密文（base64）：9MgYwmuPrjiecPMx61O6zIuy3MtIXQQ0E59T3xB6u0Gyf1gYs2i3K9Jxaa0zj4gTMazJuApwd6+jdyeI5iGHvhQyDHGVlAuYTgJrbFDrfB22Fpil2NfNnWFBTXyf7SDI题目已经过期了，现在的 ICAO 文件描述的护照编号格式连长度都不一样。这怎么做？只能抄题解了……题目二地址：https://cryptopals.com/sets/2Implement PKCS#7 padding不需要特别考虑 content 恰好和块等长的情况，相当于再附加一个只有 padding 的块。def pkcs_7(content: bytes) -> bytes: padlen = 16 - (len(content) % 16) return content + bytes([padlen] * padlen) pkcs_7("YELLOW SUBMARINE", 20) # "YELLOW SUBMARINE\x04\x04\x04\x04"Implement CBC modefrom base64 import b64decode from Crypto.Cipher import AES def aes_ecb_dec(block: bytes, key: bytes) -> bytes: assert len(block) % 16 == 0 cipher = AES.new(key, AES.MODE_ECB) return cipher.decrypt(block) def bytesxor(a: bytes, b: bytes) -> bytes: if len(a) > len(b): return bytes([x ^ y for (x, y) in zip(a[:len(b)], b)]) else: return bytes([x ^ y for (x, y) in zip(a, b[:len(a)])]) def depkcs_7(content: bytes) -> bytes: return content[:len(content) - content[-1]] def aes_cbc_dec(cipher: bytes, key: bytes, iv: bytes) -> bytes: assert len(cipher) % 16 == 0 result = bytes() prev_block = iv for i in range(0, len(cipher), 16): block = cipher[i:i+16] decrypted = aes_ecb_dec(block, key) result += bytesxor(decrypted, prev_block) prev_block = block return depkcs_7(result) key = b"YELLOW SUBMARINE" with open('10.txt', 'r') as f: encrypted_b64 = f.read() cipher = b64decode(encrypted_b64) print(aes_cbc_dec(cipher, key, b'\x00' * 16))An ECB/CBC detection oracle通过检查是否有重复块来判断是否是 ECB 模式，用户输入需要有较长的重复 pattern 才能成功。def aes_ecb_enc(data: bytes, key: bytes) -> bytes: assert len(data) % 16 == 0 cipher = AES.new(key, AES.MODE_ECB) return cipher.encrypt(data) def pkcs_7(content: bytes) -> bytes: padlen = 16 - (len(content) % 16) return content + bytes([padlen] * padlen) def aes_cbc_enc(plain: bytes, key: bytes,…

西电网安实验班 现代密码学 实验一
type: item
date: 2025-11-13T10:15:00+00:00
description: 题目一地址：https://www.coursera.org/learn/crypto/assignment-submission/KZ9js/week-1-programming-assignment-optional/attempt​ 题目使用同一个流密码密钥加密得到 10 个密文，加密方式是逐字节异或，目标是通过这 10 个密文得到密钥以解出第 11 个密文。​ 已知消息 M、密钥 K，异或加密的过程是$$ C=M\oplus K $$得到密文 C。异或是自反的运算，所以解密与加密过程相同$$ M=C\oplus K $$​ 对于同一个密钥 K 加密的两个密文 C_1、C_2，如果将两者异或可以得到$$ B=C_1\oplus C_2=M_1\oplus K\oplus M_2\oplus K=M_1\oplus M_2 $$字节流B是两消息逐字节异或结果。​ 我们首先导入这些密文和加密方法msg_1 = bytes.fromhex('315c4eeaa8b5f8aaf9174145bf43e1784b8fa00dc71d885a804e5ee9fa40b16349c146fb778cdf2d3aff021dfff5b403b510d0d0455468aeb98622b137dae857553ccd8883a7bc37520e06e515d22c954eba5025b8cc57ee59418ce7dc6bc41556bdb36bbca3e8774301fbcaa3b83b220809560987815f65286764703de0f3d524400a19b159610b11ef3e') msg_2 = bytes.fromhex('234c02ecbbfbafa3ed18510abd11fa724fcda2018a1a8342cf064bbde548b12b07df44ba7191d9606ef4081ffde5ad46a5069d9f7f543bedb9c861bf29c7e205132eda9382b0bc2c5c4b45f919cf3a9f1cb74151f6d551f4480c82b2cb24cc5b028aa76eb7b4ab24171ab3cdadb8356f') msg_3 = bytes.fromhex('32510ba9a7b2bba9b8005d43a304b5714cc0bb0c8a34884dd91304b8ad40b62b07df44ba6e9d8a2368e51d04e0e7b207b70b9b8261112bacb6c866a232dfe257527dc29398f5f3251a0d47e503c66e935de81230b59b7afb5f41afa8d661cb') msg_4 = bytes.fromhex('32510ba9aab2a8a4fd06414fb517b5605cc0aa0dc91a8908c2064ba8ad5ea06a029056f47a8ad3306ef5021eafe1ac01a81197847a5c68a1b78769a37bc8f4575432c198ccb4ef63590256e305cd3a9544ee4160ead45aef520489e7da7d835402bca670bda8eb775200b8dabbba246b130f040d8ec6447e2c767f3d30ed81ea2e4c1404e1315a1010e7229be6636aaa') msg_5 = bytes.fromhex('3f561ba9adb4b6ebec54424ba317b564418fac0dd35f8c08d31a1fe9e24fe56808c213f17c81d9607cee021dafe1e001b21ade877a5e68bea88d61b93ac5ee0d562e8e9582f5ef375f0a4ae20ed86e935de81230b59b73fb4302cd95d770c65b40aaa065f2a5e33a5a0bb5dcaba43722130f042f8ec85b7c2070') msg_6 = bytes.fromhex('32510bfbacfbb9befd54415da243e1695ecabd58c519cd4bd2061bbde24eb76a19d84aba34d8de287be84d07e7e9a30ee714979c7e1123a8bd9822a33ecaf512472e8e8f8db3f9635c1949e640c621854eba0d79eccf52ff111284b4cc61d11902aebc66f2b2e436434eacc0aba…