1
0

feat: init books

This commit is contained in:
2025-08-31 16:03:40 +08:00
parent 2f0b6f10b1
commit fff4838b44
67 changed files with 1789 additions and 0 deletions

14
cryptopal_book/book.toml Normal file
View File

@@ -0,0 +1,14 @@
[book]
authors = ["sangge"]
language = "zh-cn"
src = "src"
title = "Cryptopal Book"
[output.html.print]
enable = false
[output.html.search]
enable = false
[output.html]
default-theme = "rust"
code.runnable = false

View File

@@ -0,0 +1,9 @@
# 开篇
本书主要用于记录cryptopal的做题思路。附带对题目进行简单的翻译。
构建方法
```bash
mdbook build
```

View File

@@ -0,0 +1,91 @@
# Summary
[Introduction](README.md)
# Set 1: Basics
- [Challenge 1: Convert hex to base64](./challenge_01.md)
- [Challenge 2: Fixed XOR](./challenge_02.md)
- [Challenge 3: Single-byte XOR cipher](./challenge_03.md)
- [Challenge 4: Detect single-character XOR](./challenge_04.md)
- [Challenge 5: Implement repeating-key XOR](./challenge_05.md)
- [Challenge 6: Break repeating-key XOR](./challenge_06.md)
- [Challenge 7: AES in ECB mode](./challenge_07.md)
- [Challenge 8: Detect AES in ECB mode](./challenge_08.md)
# Set 2: Block Crypto
- [Challenge 9: Implement PKCS#7 padding](./challenge_09.md)
- [Challenge 10: Implement CBC mode](./challenge_10.md)
- [Challenge 11: An ECB/CBC detection oracle](./challenge_11.md)
- [Challenge 12: Byte-at-a-time ECB decryption (Simple)](./challenge_12.md)
- [Challenge 13: ECB cut-and-paste](./challenge_13.md)
- [Challenge 14: Byte-at-a-time ECB decryption (Harder)](./challenge_14.md)
- [Challenge 15: PKCS#7 padding validation](./challenge_15.md)
- [Challenge 16: CBC bitflipping attacks](./challenge_16.md)
# Set 3: Block & Stream Crypto
- [Challenge 17: The CBC padding oracle](./challenge_17.md)
- [Challenge 18: Implement CTR, the stream cipher mode](./challenge_18.md)
- [Challenge 19: Break fixed-nonce CTR mode using substitutions](./challenge_19.md)
- [Challenge 20: Break fixed-nonce CTR statistically](./challenge_20.md)
- [Challenge 21: Implement the MT19937 Mersenne Twister RNG](./challenge_21.md)
- [Challenge 22: Crack an MT19937 seed](./challenge_22.md)
- [Challenge 23: Clone an MT19937 RNG from its output](./challenge_23.md)
- [Challenge 24: Create the MT19937 stream cipher and break it](./challenge_24.md)
# Set 4: Stream Crypto and Randomness
- [Challenge 25: Break "random access read/write" AES CTR](./challenge_25.md)
- [Challenge 26: CTR bitflipping](./challenge_26.md)
- [Challenge 27: Recover the key from CBC with IV=Key](./challenge_27.md)
- [Challenge 28: Implement a SHA-1 keyed MAC](./challenge_28.md)
- [Challenge 29: Break a SHA-1 keyed MAC using length extension](./challenge_29.md)
- [Challenge 30: Break an MD4 keyed MAC using length extension](./challenge_30.md)
- [Challenge 31: Implement and break HMAC-SHA1 with an artificial timing leak](./challenge_31.md)
- [Challenge 32: Break HMAC-SHA1 with a slightly less artificial timing leak](./challenge_32.md)
# Set 5: Diffie-Hellman and Friends
- [Challenge 33: Implement Diffie-Hellman](./challenge_33.md)
- [Challenge 34: Implement a MITM key-fixing attack on Diffie-Hellman with parameter injection](./challenge_34.md)
- [Challenge 35: Implement DH with negotiated groups, and break with malicious "g" parameters](./challenge_35.md)
- [Challenge 36: Implement Secure Remote Password (SRP)](./challenge_36.md)
- [Challenge 37: Break SRP with a zero key](./challenge_37.md)
- [Challenge 38: Offline dictionary attack on simplified SRP](./challenge_38.md)
- [Challenge 39: Implement RSA](./challenge_39.md)
- [Challenge 40: Implement an E=3 RSA Broadcast attack](./challenge_40.md)
# Set 6: RSA and DSA
- [Challenge 41: Implement unpadded message recovery oracle](./challenge_41.md)
- [Challenge 42: Bleichenbacher's e=3 RSA Attack](./challenge_42.md)
- [Challenge 43: DSA key recovery from nonce](./challenge_43.md)
- [Challenge 44: DSA nonce recovery from repeated nonce](./challenge_44.md)
- [Challenge 45: DSA parameter tampering](./challenge_45.md)
- [Challenge 46: RSA parity oracle](./challenge_46.md)
- [Challenge 47: Bleichenbacher's PKCS 1.5 Padding Oracle (Simple Case)](./challenge_47.md)
- [Challenge 48: Bleichenbacher's PKCS 1.5 Padding Oracle (Complete Case)](./challenge_48.md)
# Set 7: Hashes
- [Challenge 49: CBC-MAC Message Forgery](./challenge_49.md)
- [Challenge 50: Hashing with CBC-MAC](./challenge_50.md)
- [Challenge 51: Compression Ratio Side-Channel Attacks](./challenge_51.md)
- [Challenge 52: Iterated Hash Function Multicollisions](./challenge_52.md)
- [Challenge 53: Kelsey and Schneier's Expandable Messages](./challenge_53.md)
- [Challenge 54: Kelsey and Kohno's Nostradamus Attack](./challenge_54.md)
- [Challenge 55: MD4 Collisions](./challenge_55.md)
- [Challenge 56: RC4 Single-Byte Biases](./challenge_56.md)
# Set 8: Abstract Algebra
- [Challenge 57: Diffie-Hellman Revisited: Small Subgroup Confinement](./challenge_57.md)
- [Challenge 58: Pollard's Method for Catching Kangaroos](./challenge_58.md)
- [Challenge 59: Elliptic Curve Diffie-Hellman and Invalid-Curve Attacks](./challenge_59.md)
- [Challenge 60: Single-Coordinate Ladders and Insecure Twists](./challenge_60.md)
- [Challenge 61: Duplicate-Signature Key Selection in ECDSA (and RSA)](./challenge_61.md)
- [Challenge 62: Key-Recovery Attacks on ECDSA with Biased Nonces](./challenge_62.md)
- [Challenge 63: Key-Recovery Attacks on GCM with Repeated Nonces](./challenge_63.md)
- [Challenge 64: Key-Recovery Attacks on GCM with a Truncated Counter](./challenge_64.md)

View File

@@ -0,0 +1,76 @@
# 挑战一十六进制转Base64
## 题目描述
将十六进制字符串转换为Base64编码。
**输入字符串:**
```plain
49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d
```
**预期输出:**
```
SSdtIGtpbGxpbmcgeW91ciBicmFpbiBsaWtlIGEgcG9pc29ub3VzIG11c2hyb29t
```
## 核心规则
**Cryptopals黄金法则始终操作原始字节永远不要操作编码字符串。十六进制和Base64仅用于美化输出。**
## 什么是Base64
Base64是一种基于64个可打印字符来表示二进制数据个表示方法。
由于2^6=64所以每6个比特为一个单元对应某个可打印字符。
3个字节有24个比特对应于4个Base64单元即3个字节可由4个可打印字符来表示。
| Index | Binary | Char | Index | Binary | Char | Index | Binary | Char | Index | Binary | Char |
| ----- | ------ | ---- | ----- | ------ | ---- | ----- | ------ | ---- | ----- | ------ | ---- |
| 0 | 000000 | A | 16 | 010000 | Q | 32 | 100000 | g | 48 | 110000 | w |
| 1 | 000001 | B | 17 | 010001 | R | 33 | 100001 | h | 49 | 110001 | x |
| 2 | 000010 | C | 18 | 010010 | S | 34 | 100010 | i | 50 | 110010 | y |
| 3 | 000011 | D | 19 | 010011 | T | 35 | 100011 | j | 51 | 110011 | z |
| 4 | 000100 | E | 20 | 010100 | U | 36 | 100100 | k | 52 | 110100 | 0 |
| 5 | 000101 | F | 21 | 010101 | V | 37 | 100101 | l | 53 | 110101 | 1 |
| 6 | 000110 | G | 22 | 010110 | W | 38 | 100110 | m | 54 | 110110 | 2 |
| 7 | 000111 | H | 23 | 010111 | X | 39 | 100111 | n | 55 | 110111 | 3 |
| 8 | 001000 | I | 24 | 011000 | Y | 40 | 101000 | o | 56 | 111000 | 4 |
| 9 | 001001 | J | 25 | 011001 | Z | 41 | 101001 | p | 57 | 111001 | 5 |
| 10 | 001010 | K | 26 | 011010 | a | 42 | 101010 | q | 58 | 111010 | 6 |
| 11 | 001011 | L | 27 | 011011 | b | 43 | 101011 | r | 59 | 111011 | 7 |
| 12 | 001100 | M | 28 | 011100 | c | 44 | 101100 | s | 60 | 111100 | 8 |
| 13 | 001101 | N | 29 | 011101 | d | 45 | 101101 | t | 61 | 111101 | 9 |
| 14 | 001110 | O | 30 | 011110 | e | 46 | 101110 | u | 62 | 111110 | + |
| 15 | 001111 | P | 31 | 011111 | f | 47 | 101111 | v | 63 | 111111 | / |
Padding: =
例如编码一个"Hello World"字符串:
H -> 01001000
e -> 01100101
l -> 01101100
l -> 01101100
o -> 01101111
(空格) -> 00100000
W -> 01010111
o -> 01101111
r -> 01110010
l -> 01101100
d -> 01100100
连接后每6位划分就是
010010 000110 010101 101100 011011 000110 111100 100000
010101 110110 111101 110010 011011 000110 0100
此处对应最后一组不足6位因此填充两位0对齐至6位。
并在最终的编码末尾添加一个=表示填充了两位0。
对应的编码就是SGVsbG8gV29ybGQ=
如果最后一组填充了四位0则最终编码末尾添加两个=。
对于题目的16进制字符串则是将16进制转换为2进制表示并进行base编码即可。

View File

@@ -0,0 +1,40 @@
# 挑战二固定XOR运算
## 题目描述
写一个函数接受两个等长的缓冲区并产生它们的XOR组合。
**输入字符串:**
```
1c0111001f010100061a024b53535009181c
686974207468652062756c6c277320657965
```
**预期输出:**
```
746865206b696420646f6e277420706c6179
```
**实现要点:**
1. **长度校验**:确保两个缓冲区长度相等
2. **迭代器配对**:使用`zip()`将两个缓冲区的字节一一配对
3. **XOR运算**:对每对字节执行异或运算`b1 ^ b2`
4. **收集结果**将所有XOR结果收集为`Vec<u8>`
**执行流程:**
1. **解码输入**:将十六进制字符串转换为字节数组
2. **执行XOR**:调用`fixed_xor()`函数
3. **编码输出**:将结果重新编码为十六进制字符串
## XOR运算原理
XOR异或运算的特性
- `0 ⊕ 0 = 0`
- `0 ⊕ 1 = 1`
- `1 ⊕ 0 = 1`
- `1 ⊕ 1 = 0`

View File

@@ -0,0 +1,36 @@
# 挑战三单字节XOR密码
## 题目描述
十六进制编码的字符串已经使用单个字符进行了XOR加密。找到该密钥并解密消息。
**输入密文:**
```
1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736
```
**实现步骤:**
1. **解码密文**:将十六进制字符串转换为字节数组
2. **遍历所有可能的密钥**从0到2558位
3. **XOR解密**:对每个字节应用`byte ^ key`操作
4. **UTF-8验证**检查解密结果是否为有效UTF-8字符串
5. **英文验证**:使用`is_valid_english()`函数检查文本质量
## 解密原理
单字节XOR加密的特点
- **对称性**XOR运算具有自反性`A ⊕ B ⊕ B = A`
- **暴力破解**由于密钥空间很小只有256个可能可以尝试所有密钥
- **文本验证**:通过统计分析判断解密结果是否为有效英文
## 英文验证函数
`common::is_valid_english()`函数通过以下特征验证文本:
- 字符合法性(字母、数字、标点符号)
- 字母比例默认至少60%
- 无连续空格
- 无换行符

View File

@@ -0,0 +1,49 @@
# 挑战四检测单字符XOR
## 题目描述
在文件中有一个字符串被单字符XOR加密。找到它。
该文件包含60个字符的字符串其中一个已经被单字符XOR加密。
**数据文件:** `4.txt`
该文件包含多行60字符的十六进制字符串例如
```
0e3647e8592d35514a081243582536ed3de6734059001e3f535ce6271032
334b041de124f73c18011a50e608097ac308ecee501337ec3e100854201d
40e127f51c10031d0133590b1e490f3514e05a54143d08222c2a4071e351
...
```
**原始数据来源:** [https://cryptopals.com/static/challenge-data/4.txt](https://cryptopals.com/static/challenge-data/4.txt)
**文件处理:**
- 读取包含多行十六进制字符串的文件
- 将所有行连接成一个长字符串进行处理
### 3. 滑动窗口解密检测
**实现策略:**
1. **十六进制解码**:将连接后的字符串解码为字节数组
2. **遍历所有密钥**从0到255尝试每个可能的单字节密钥
3. **XOR解密**:对整个字节数组应用当前密钥
4. **滑动窗口检测**使用20字节的滑动窗口检查解密结果
5. **英文验证**:对每个窗口片段进行英文有效性检查
## 算法优化
### 滑动窗口技术
- **窗口大小**20字节足够检测有意义的英文文本片段
- **重叠扫描**:逐字节移动窗口,确保不遗漏任何有效文本
- **局部验证**:只需部分文本通过英文验证即可识别正确密钥
### 检测原理
- **统计分析**:通过`is_valid_english()`函数的统计特征识别
- **模式识别**:英文文本具有特定的字符分布和结构
- **窗口策略**:避免因文件中混合内容导致的误判

View File

@@ -0,0 +1,67 @@
# 挑战五实现重复密钥XOR
## 题目描述
实现重复密钥XOR加密。对于给定的明文和密钥使用重复的密钥进行XOR加密。
**输入明文:**
```
Burning 'em, if you ain't quick and nimble I go crazy when I hear a cymbal
```
**密钥:**
```
ICE
```
**实现要点:**
1. **空密钥检查**:确保密钥不为空
2. **循环迭代器**:使用`key.iter().cycle()`创建无限重复的密钥序列
3. **配对操作**`zip()`将输入字节与循环密钥配对
4. **XOR运算**:对每对字节执行`a ^ b`操作
5. **结果收集**将所有XOR结果收集为`Vec<u8>`
### 3. 主函数实现
```rust
fn main() -> Result<()> {
let plaintexts = r#"
Burning 'em, if you ain't quick and nimble I go crazy when I hear a cymbal
"#;
let key = b"ICE";
for plaintext in plaintexts.lines() {
let plaintext_bytes = plaintext.as_bytes();
let cipher = xor_with_key(plaintext_bytes, key)?;
println!("{}", hex::encode(cipher));
}
Ok(())
}
```
**执行流程:**
1. **文本处理**:定义多行明文字符串
2. **密钥定义**:使用字节字符串`b"ICE"`
3. **逐行处理**:对每行文本进行加密
4. **字节转换**:将文本转换为字节数组
5. **XOR加密**:调用`xor_with_key()`函数
6. **十六进制输出**:将加密结果编码为十六进制字符串
## 重复密钥XOR原理
### 密钥重复模式
- **循环使用**:密钥"ICE"会重复使用:`I-C-E-I-C-E-I-C-E...`
- **长度无关**:无论明文多长,密钥都会循环重复
- **对称加密**:同样的密钥可以用于加密和解密
### 安全性特点
- **比单字节XOR更安全**:密钥长度增加了破解难度
- **仍然脆弱**:存在重复模式,可通过频率分析攻击
- **历史意义**Vigenère密码的电子版本
这种实现展示了如何使用Rust的迭代器和函数式编程特性来优雅地处理密码学算法。

View File

@@ -0,0 +1,188 @@
# 挑战六破解重复密钥XOR
## 题目描述
破解重复密钥XOR加密。这是一个复杂的密码分析挑战需要使用Hamming距离来确定密钥长度然后使用频率分析来恢复密钥。
**数据文件:** `6.txt`
该文件包含Base64编码的加密数据例如
```
HUIfTQsPAh9PE048GmllH0kcDk4TAQsHThsBFkU2AB4BSWQgVB0dQzNTTmVS
BgBHVBwNRU0HBAxTEjwMHghJGgkRTxRMIRpHKwAFHUdZEQQJAGQmB1MANxYG
DBoXQR0BUlQwXwAgEwoFR08SSAhFTmU+Fgk4RQYFCBpGB08fWXh+amI2DB0P
...
```
**原始数据来源:** [https://cryptopals.com/static/challenge-data/6.txt](https://cryptopals.com/static/challenge-data/6.txt)
## 代码实现与解析
### 1. 依赖引入
```rust
use core::f32;
use std::fs;
use std::{cmp::Ordering, str::from_utf8};
use std::collections::HashMap;
use anyhow::{Result, anyhow};
use base64::{Engine, engine::general_purpose::STANDARD};
use common::xor_with_key;
```
### 2. Base64解码函数
```rust
fn base64_to_bytes(input: &str) -> Result<Vec<u8>> {
Ok(STANDARD.decode(input)?)
}
```
### 3. Hamming距离计算
```rust
fn hamming_distance(input_1: &[u8], input_2: &[u8]) -> Result<u32> {
if input_1.len() != input_2.len() {
return Err(anyhow!("Can't count hamming distance"));
}
let hamming_distance = input_1
.iter()
.zip(input_2.iter())
.map(|(a, b)| (a ^ b).count_ones())
.sum::<u32>();
Ok(hamming_distance)
}
```
**Hamming距离原理**
- 计算两个等长字节序列的位差异数量
- 使用XOR运算找出不同的位
- `count_ones()`统计结果中1的个数
### 4. 密钥长度推测
```rust
fn get_posible_keysize(cipher_bytes: &[u8]) -> Result<usize> {
let mut posible_keysizes: HashMap<usize, f32> = HashMap::new();
for keysize in 2..=40 {
if cipher_bytes.len() < 4 * keysize {
return Err(anyhow!("Wrong keysize"));
}
let mut total_distance = 0;
for i in 0..=(cipher_bytes.len() - 4 * keysize) {
let slice_1 = &cipher_bytes[i..i + keysize];
let slice_2 = &cipher_bytes[i + keysize..i + 2 * keysize];
let slice_3 = &cipher_bytes[i + 2 * keysize..i + 3 * keysize];
let slice_4 = &cipher_bytes[i + 3 * keysize..i + 4 * keysize];
total_distance += hamming_distance(slice_1, slice_2)?;
total_distance += hamming_distance(slice_1, slice_3)?;
total_distance += hamming_distance(slice_1, slice_4)?;
total_distance += hamming_distance(slice_2, slice_3)?;
total_distance += hamming_distance(slice_2, slice_4)?;
total_distance += hamming_distance(slice_3, slice_4)?;
}
let score = total_distance as f32 / keysize as f32;
posible_keysizes.insert(keysize, score);
}
let mut posible_keysizes: Vec<_> = posible_keysizes.into_iter().collect();
posible_keysizes.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(Ordering::Equal));
Ok(posible_keysizes.first().unwrap().0)
}
```
**密钥长度推测策略:**
1. **多块采样**取连续的4个密钥长度大小的块
2. **距离计算**计算所有块之间的Hamming距离
3. **归一化评分**:除以密钥长度进行归一化
4. **最小值选择**:选择归一化距离最小的密钥长度
### 5. 英文文本评分
```rust
fn score_english_text(input: &[u8]) -> f32 {
use std::collections::HashMap;
let mut score = 0_f32;
let freq: HashMap<u8, f32> = [
(b'a', 0.0817), (b'b', 0.0150), (b'c', 0.0278), (b'd', 0.0425),
(b'e', 0.1270), (b'f', 0.0223), (b'g', 0.0202), (b'h', 0.0609),
(b'i', 0.0697), (b'j', 0.0015), (b'k', 0.0077), (b'l', 0.0403),
(b'm', 0.0241), (b'n', 0.0675), (b'o', 0.0751), (b'p', 0.0193),
(b'q', 0.0010), (b'r', 0.0599), (b's', 0.0633), (b't', 0.0906),
(b'u', 0.0276), (b'v', 0.00981), (b'w', 0.0236), (b'x', 0.0015),
(b'y', 0.0197), (b'z', 0.0007), (b' ', 0.1300),
]
.iter()
.cloned()
.collect();
for c in input.iter().map(|b| b.to_ascii_lowercase()) {
if let Some(f) = freq.get(&c) {
score += f;
}
}
score
}
```
**频率分析原理:**
- 使用英文字母的标准频率分布
- 计算文本与标准频率的匹配程度
- 分数越高,越可能是有效英文文本
### 6. 主破解流程
```rust
fn main() -> Result<()> {
let cipher = fs::read_to_string("./problems/p6/6.txt")?;
let cipher: String = cipher.lines().collect();
let cipher_bytes = base64_to_bytes(&cipher)?;
let posible_keysize = get_posible_keysize(&cipher_bytes)?;
// 按密钥长度分组
let mut blocks: Vec<Vec<u8>> = vec![Vec::new(); posible_keysize];
for (i, &byte) in cipher_bytes.iter().enumerate() {
blocks[i % posible_keysize].push(byte);
}
let mut key_list: Vec<u8> = Vec::new();
for block in blocks {
let mut high_score = 0_f32;
let mut possible_key = 0_u8;
for key in 0..=255 {
let decrypted: Vec<u8> = block.iter().map(|&b| b ^ key).collect();
let score = score_english_text(&decrypted);
if score > high_score {
high_score = score;
possible_key = key;
}
}
key_list.push(possible_key);
}
let str_key = String::from_utf8(key_list.clone())?;
println!("key: {str_key}");
let plaintext = xor_with_key(&cipher_bytes, &key_list)?;
let str_plaintext = from_utf8(&plaintext)?;
println!("plaintext: {str_plaintext}");
Ok(())
}
```
**破解步骤:**
1. **读取密文**从Base64文件读取加密数据
2. **推测密钥长度**使用Hamming距离分析
3. **分组密文**:按密钥长度将密文分组
4. **恢复密钥**:对每组使用频率分析找到最佳密钥字节
5. **解密验证**:使用恢复的密钥解密整个密文
## 密码分析原理
这个挑战展示了经典的Vigenère密码分析技术
- **Hamming距离**:利用重复密钥的周期性特征
- **频率分析**:基于英文字母的统计分布
- **分组技术**将复杂问题分解为多个单字节XOR问题
这是密码学中从古典密码向现代密码过渡的重要里程碑。

View File

@@ -0,0 +1,19 @@
# 挑战七AES ECB模式解密
## 题目描述
使用AES-128-ECB模式解密给定的Base64编码数据。
**数据文件:** `7.txt`
该文件包含Base64编码的AES-ECB加密数据例如
```
CRIwqt4+szDbqkNY+I0qbDe3LQz0wiw0SuxBQtAM5TDdMbjCMD/venUDW9BL
PEXODbk6a48oMbAY6DDZsuLbc0uR9cp9hQ0QQGATyyCESq2NSsvhx5zKlLtz
dsnfK5ED5srKjK7Fz4Q38/ttd+stL/9WnDzlJvAo7WBsjI5YJc2gmAYayNfm
...
```
**密钥:** `YELLOW SUBMARINE`
**原始数据来源:** [https://cryptopals.com/static/challenge-data/7.txt](https://cryptopals.com/static/challenge-data/7.txt)

View File

@@ -0,0 +1,18 @@
# 挑战八检测AES ECB模式
## 题目描述
在给定的十六进制字符串中检测哪一个是使用AES-ECB模式加密的。
**数据文件:** `8.txt`
该文件包含多行十六进制字符串其中一行是使用AES-ECB模式加密的例如
```
8a10247f90d0a05538888ad6205882196f5f6d05c21ec8dca0cb0be02c3f8b09e382963f443aa514daa501257b09a36bf8c4c392d8ca1bf4395f0d5f2542148c7e5ff22237969874bf66cb85357ef99956accf13ba1af36ca7a91a50533c4d89b7353f908c5a166774293b0bf6247391df69c87dacc4125a99ec417221b58170e633381e3847c6b1c28dda2913c011e13fc4406f8fe73bbf78e803e1d995ce4d
bd20aad820c9e387ea57408566e5844c1e470e9d6fbbdba3a6b4df1dd85bce2208f1944f1827d015df9c46c22803f41d1052acb721977f0ccc13db95c970252091ea5e36e423ee6a2f2d12ef909fcadd42529885d221af1225e32161b85e6dc03465cf398c937846b18bac05e88820a567caac113762753dffbe6ece09823bab5aee947a682bb3156f42df1d8dc320a897ee79981cf937390b4ae93eb8657f6c
...
```
**检测原理:** ECB模式加密相同的明文块会产生相同的密文块因此可以通过检测重复的密文块来识别ECB模式。
**原始数据来源:** [https://cryptopals.com/static/challenge-data/8.txt](https://cryptopals.com/static/challenge-data/8.txt)

View File

@@ -0,0 +1 @@
# Challenge 9: Implement PKCS#7 padding

View File

@@ -0,0 +1 @@
# Challenge 10: Implement CBC mode

View File

@@ -0,0 +1 @@
# Challenge 11: An ECB/CBC detection oracle

View File

@@ -0,0 +1,50 @@
# Challenge 12: Byte-at-a-time ECB decryption (Simple)
## 攻击思路
1. 先填充随机字符,使得填充 + 未知明文刚好为16字节的倍数即1个block的长度
检测方法为逐步填充当填充进去后密文长度刚好比先前多16字节时填充是刚好的。
> [!NOTE]
> pkcs7的填充规则是如果数据长度正好是块大小的倍数仍然要添加一个完整的填充块。
> 此时最后一个块为全0x10
此时输入结构为:
```plaintext
[padding, unknown plaintext]
[16, 16, 16, ...,16]
```
2. 此时开头加上一个猜解字节,然后加上pkcs7的padding。填充多一个字符。
此时输入结构为:
```plaintext
[some byte, 15, 15, ...,15]
[padding, unknown plaintext]
[last byte of unknown plaintext, 15, 15, ...,15]
```
通过对比第一个block的密文和最后一个block的密文即可知道一个字节。
3. 对于第二个字节同样操作padding需要加多一byte
```plaintext
[some byte, known byte, 14, 14, ..., 14]
[padding, unknown plaintext]
[unknown byte, known byte, 14, 14, ..., 14]
```
4. 在破解完一个block之后需要利用到已破解的部分
```plaintext
[some byte, known byte]
[known byte, padding]
[padding, unknown byte]
[some byte, known byte]
[known byte, 15, 15, ..., 15 ]
```
此时比较第一个block和倒数第2个block
5. 在破解开始前,可通过总长度-填充长度,计算出未知明文的长度。
在破解长度与未知明文长度相同时,所得就是未知明文。

View File

@@ -0,0 +1,137 @@
# 挑战 13ECB 剪切粘贴攻击
## 背景介绍
ECBElectronic Codebook模式的一个严重弱点是相同的明文块会产生相同的密文块。
这使得攻击者可以通过"剪切粘贴"密文块来构造恶意的加密数据。
## 题目要求
### 步骤 1实现键值对解析器
编写一个键值对解析例程类似于解析结构化cookie的功能。该例程应该能够将
```plaintext
foo=bar&baz=qux&zap=zazzle
```
解析为:
```json
{
"foo": "bar",
"baz": "qux",
"zap": "zazzle"
}
```
### 步骤 2实现用户配置文件编码器
编写一个函数,根据邮箱地址编码用户配置文件。例如:
```rust
profile_for("foo@bar.com")
```
应该生成:
```json
{
"email": "foo@bar.com",
"uid": 10,
"role": "user"
}
```
编码后的字符串格式为:
```plaintext
email=foo@bar.com&uid=10&role=user
```
**重要安全要求**`profile_for` 函数不应允许编码元字符(`&``=`)。需要过滤、转义或以其他方式处理这些字符,防止用户将邮箱地址设置为 `"foo@bar.com&role=admin"` 这样的恶意输入。
### 步骤 3实现加密和解密函数
生成一个随机的 AES 密钥,然后实现:
1. **加密函数**:使用该密钥加密编码后的用户配置文件,并将密文"提供"给"攻击者"
2. **解密函数**:解密编码后的用户配置文件并解析
### 步骤 4实施 ECB 剪切粘贴攻击
**攻击目标**:仅使用 `profile_for()` 函数(作为预言机生成"有效"密文)和密文本身,构造一个 `role=admin` 的配置文件。
## 什么是json
JSON = JavaScript Object Notation
JSON是一种轻量级的数据交换格式用来存储和传输数据。
**基本语法**
- **对象**`{"key": "value"}` - 用花括号包围
- **字符串**`"hello"` - 必须用双引号
- **数字**`123`, `45.67` - 直接写数值
- **布尔值**`true`, `false`
- **数组**`[1, 2, 3]` - 用方括号
- **null**`null`
**例子对比**
```
原始格式foo=bar&baz=qux&zap=zazzle
JSON格式
{
"foo": "bar",
"baz": "qux",
"zap": "zazzle"
}
```
**实际用途**
- API数据传输
- 配置文件
- 数据存储
- 前后端通信
JSON就是把数据结构用文本方式表示出来方便程序之间交换数据。
## 问题分析
当我们随便输入一个邮箱foo@bar.com的时候处理流程是这样的:
```plaintext
email=foo@bar.com&uid=10&role=user
按照16字节分割填充 ->
email=foo@bar.co m&uid=10&role=us er\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e
```
假如我们的邮箱再长2字节则中间的block为
```plaintext
email=foo@bar.co m12&uid=10&role= user\x0c...
```
如果我们将中间block的密文复制多一份则解密时有
```
email=foo@bar.co m12&uid=10&role= m12&uid=10&role= user
```
此时我们获得了role=部分的密文片段。
然后再构造多一次
```plaintext
email=foo@bar.co admin&uid=10&rol e=user\x0a...
```
然后将密文片段粘贴到第三个block
```plaintext
email=foo@bar.co m12&uid=10&role= admin&uid=10&rol e=user\x0a...
->
email=foo@bar.com12&uid=10&role=admin&uid=10&role=user\x0a...
```

View File

@@ -0,0 +1 @@
# Challenge 14: Byte-at-a-time ECB decryption (Harder)

View File

@@ -0,0 +1 @@
# Challenge 15: PKCS#7 padding validation

View File

@@ -0,0 +1 @@
# Challenge 16: CBC bitflipping attacks

View File

@@ -0,0 +1 @@
# Challenge 17: The CBC padding oracle

View File

@@ -0,0 +1 @@
# Challenge 18: Implement CTR, the stream cipher mode

View File

@@ -0,0 +1,76 @@
# 挑战十九使用替换攻击固定随机数CTR模式
## 题目描述
使用你的CTR加密/解密函数将其随机数值固定为0。生成一个随机的AES密钥。
在*连续的加密操作中**不是*在一个大的连续CTR流中对以下base64解码后的每一行进行加密产生多个独立的密文
```
SSBoYXZlIG1ldCB0aGVtIGF0IGNsb3NlIG9mIGRheQ==
Q29taW5nIHdpdGggdml2aWQgZmFjZXM=
RnJvbSBjb3VudGVyIG9yIGRlc2sgYW1vbmcgZ3JleQ==
RWlnaHRlZW50aC1jZW50dXJ5IGhvdXNlcy4=
SSBoYXZlIHBhc3NlZCB3aXRoIGEgbm9kIG9mIHRoZSBoZWFk
T3IgcG9saXRlIG1lYW5pbmdsZXNzIHdvcmRzLA==
T3IgaGF2ZSBsaW5nZXJlZCBhd2hpbGUgYW5kIHNhaWQ=
UG9saXRlIG1lYW5pbmdsZXNzIHdvcmRzLA==
QW5kIHRob3VnaHQgYmVmb3JlIEkgaGFkIGRvbmU=
T2YgYSBtb2NraW5nIHRhbGUgb3IgYSBnaWJl
VG8gcGxlYXNlIGEgY29tcGFuaW9u
QXJvdW5kIHRoZSBmaXJlIGF0IHRoZSBjbHViLA==
QmVpbmcgY2VydGFpbiB0aGF0IHRoZXkgYW5kIEk=
QnV0IGxpdmVkIHdoZXJlIG1vdGxleSBpcyB3b3JuOg==
QWxsIGNoYW5nZWQsIGNoYW5nZWQgdXR0ZXJseTo=
QSB0ZXJyaWJsZSBiZWF1dHkgaXMgYm9ybi4=
VGhhdCB3b21hbidzIGRheXMgd2VyZSBzcGVudA==
SW4gaWdub3JhbnQgZ29vZCB3aWxsLA==
SGVyIG5pZ2h0cyBpbiBhcmd1bWVudA==
VW50aWwgaGVyIHZvaWNlIGdyZXcgc2hyaWxsLg==
V2hhdCB2b2ljZSBtb3JlIHN3ZWV0IHRoYW4gaGVycw==
V2hlbiB5b3VuZyBhbmQgYmVhdXRpZnVsLA==
U2hlIHJvZGUgdG8gaGFycmllcnM/
VGhpcyBtYW4gaGFkIGtlcHQgYSBzY2hvb2w=
QW5kIHJvZGUgb3VyIHdpbmdlZCBob3JzZS4=
VGhpcyBvdGhlciBoaXMgaGVscGVyIGFuZCBmcmllbmQ=
V2FzIGNvbWluZyBpbnRvIGhpcyBmb3JjZTs=
SGUgbWlnaHQgaGF2ZSB3b24gZmFtZSBpbiB0aGUgZW5kLA==
U28gc2Vuc2l0aXZlIGhpcyBuYXR1cmUgc2VlbWVkLA==
U28gZGFyaW5nIGFuZCBzd2VldCBoaXMgdGhvdWdodC4=
VGhpcyBvdGhlciBtYW4gSSBoYWQgZHJlYW1lZA==
QSBkcnVua2VuLCB2YWluLWdsb3Jpb3VzIGxvdXQu
SGUgaGFkIGRvbmUgbW9zdCBiaXR0ZXIgd3Jvbmc=
VG8gc29tZSB3aG8gYXJlIG5lYXIgbXkgaGVhcnQs
WWV0IEkgbnVtYmVyIGhpbSBpbiB0aGUgc29uZzs=
SGUsIHRvbywgaGFzIHJlc2lnbmVkIGhpcyBwYXJ0
SW4gdGhlIGNhc3VhbCBjb21lZHk7
SGUsIHRvbywgaGFzIGJlZW4gY2hhbmdlZCBpbiBoaXMgdHVybiw=
VHJhbnNmb3JtZWQgdXR0ZXJseTo=
QSB0ZXJyaWJsZSBiZWF1dHkgaXMgYm9ybi4=
```
这应该产生40个短的CTR加密密文
## 攻击原理
因为CTR随机数没有为每次加密随机化每个密文都使用相同的密钥流进行加密。这是非常糟糕的。
理解这一点后就像大多数流密码包括RC4以及任何在CTR模式下运行的块密码数据字节的实际"加密"归结为单个XOR操作很明显
```
密文字节 XOR 明文字节 = 密钥流字节
```
而且由于密钥流对每个密文都相同:
```
密文字节 XOR 密钥流字节 = 明文字节
```
## 攻击方法
逐步攻击这个密码系统:猜测字母,使用预期的英语语言频率来验证猜测,捕捉常见的英语三字组合等等。
## 注意
不要想得太复杂。自动化这个过程会得到加分,但我让你做这个的部分原因是我认为这种方法是次优的。

View File

@@ -0,0 +1,21 @@
# 挑战二十统计方法破解固定随机数CTR
## 题目描述
在[这个文件](../../../static/challenge-data/20.txt)中找到类似的一组Base64编码的明文。对它们执行与第一个完全相同的操作但用不同的方法解决问题。
## 解题方法
不要对已知明文进行点推测而是将密文集合视为与重复密钥XOR相同的方式处理。
显然CTR加密看起来与重复密钥XOR不同*但使用固定随机数时,它们实际上是同一回事。*
## 攻击步骤
要利用这一点:
1. 获取你的密文集合,将它们截断到公共长度(最小密文的长度即可)。
2. 将结果密文的串联视为重复密钥XOR来解决密钥大小等于你异或的密文长度。
## 关键洞察
CTR模式使用固定随机数本质上等同于重复密钥XOR可以使用相同的统计分析方法来破解。

View File

@@ -0,0 +1 @@
# Challenge 21: Implement the MT19937 Mersenne Twister RNG

View File

@@ -0,0 +1,18 @@
# 挑战二十二破解MT19937种子
## 题目描述
确保你的MT19937接受整数种子值。测试它验证给定种子能得到相同的输出序列
编写一个执行以下操作的例程:
1. 等待随机秒数比如40到1000之间
2. 用当前Unix时间戳为RNG设置种子
3. 再次等待随机秒数
4. 返回RNG的第一个32位输出
你懂的。在它运行时去喝杯咖啡。或者只是模拟时间的流逝,不过如果你这样做就失去了这个练习的一些乐趣。
## 任务
从32位RNG输出中发现种子。

View File

@@ -0,0 +1,29 @@
# 挑战二十三从输出克隆MT19937 RNG
## 题目描述
MT19937的内部状态由624个32位整数组成。
对于每批624个输出MT会置换该内部状态。通过定期置换状态MT19937实现了2^19937的周期这是一个巨大的数。
每次调用MT19937时其内部状态的一个元素会经过一个调质函数该函数通过结果扩散位。
调质函数是可逆的;你可以编写一个"逆调质"函数该函数接受MT19937输出并将其转换回MT19937状态数组的相应元素。
## 实现方法
要反转调质变换,请按相反顺序应用调质变换中每个操作的逆操作。调质变换中有两种操作,每种应用两次:
- 一种是与右移值进行XOR
- 另一种是与左移值AND魔数进行XOR
因此你需要代码来反转"右"和"左"操作。
## 攻击步骤
一旦你有了"逆调质"功能创建一个新的MT19937生成器调用它产生624个输出对每个进行逆调质以重建生成器的状态并将该状态拼接到MT19937生成器的新实例中。
新的"拼接"生成器应该能预测原始生成器的值。
## 思考题
停下来思考一秒钟。你会如何修改MT19937以使这种攻击变得困难如果你对每个调质后的输出进行加密哈希会发生什么

View File

@@ -0,0 +1,19 @@
# 挑战二十四创建MT19937流密码并破解它
## 题目描述
你可以用任何PRNG创建一个简单的流密码使用它生成一系列8位输出并称这些输出为密钥流。将明文的每个字节与密钥流的每个连续字节进行XOR。
使用16位种子为MT19937编写执行此操作的函数。验证你可以正确加密和解密。此代码应该看起来类似于你的CTR代码。
## 任务一:破解种子
使用你的函数加密已知明文比如连续的14个'A'字符),前面加上随机数量的随机字符。
从密文中恢复"密钥"16位种子
## 任务二:密码重置令牌
使用相同的思路用当前时间做种子的MT19937生成一个随机的"密码重置令牌"。
编写一个函数来检查任何给定的密码令牌是否实际上是由当前时间做种子的MT19937 PRNG的产物。

View File

@@ -0,0 +1,19 @@
# 挑战二十五:破解"随机访问读写"AES CTR
## 题目描述
回到CTR模式。使用随机密钥对[这个文件](../../../static/challenge-data/25.txt)中恢复的明文ECB练习进行CTR加密在这个练习中密钥对你应该是未知的但要保存它
现在,编写代码允许你"寻找"到密文中的位置,解密并用不同的明文重新加密。将其暴露为一个函数,比如*"edit(ciphertext, key, offset, newtext)"*。
想象一下,"edit"函数通过API调用暴露给攻击者但不泄露密钥或原始明文攻击者拥有密文并控制偏移量和"新文本"。
## 任务
恢复原始明文。
## 思考题
**值得思考的问题:**
CTR模式的一个民间传说中的好处是能够轻松地"向前寻找"到密文中要访问密文的第N个字节你只需要能够生成密钥流的第N个字节。想象一下如果你依赖这个建议来加密磁盘会怎样。

View File

@@ -0,0 +1,7 @@
# 挑战二十六CTR位翻转攻击
## 题目描述
世界上有些人相信CTR模式能够抵御CBC模式容易受到的位翻转攻击。
重新实现[之前的CBC位翻转练习](../../2/challenges/16.html)使用CTR模式而不是CBC模式。注入一个"admin=true"令牌。

View File

@@ -0,0 +1,33 @@
# 挑战二十七从IV=Key的CBC中恢复密钥
## 题目描述
取用你在[CBC练习](../../2/challenges/16.html)中的代码并修改它使其将CBC加密的密钥重新用作IV。
应用程序有时会将密钥用作IV理由是发送方和接收方都必须知道密钥通过同时用作密钥和IV可以节省一些空间。
将密钥用作IV是不安全的能够修改传输中密文的攻击者可以让接收方解密一个值该值将泄露密钥。
## 攻击步骤
练习16中的CBC代码加密一个URL字符串。验证明文的每个字节是否符合ASCII标准查找高ASCII值。不符合标准的消息应该引发异常或返回包含解密明文的错误在现实系统中这种情况经常发生
使用你的代码加密至少3个块长的消息
```
AES-CBC(P_1, P_2, P_3) -> C_1, C_2, C_3
```
修改消息(你现在是攻击者):
```
C_1, C_2, C_3 -> C_1, 0, C_1
```
解密消息你现在是接收方并在发现高ASCII时引发适当的错误。
作为攻击者,从错误中恢复明文,提取密钥:
```
P'_1 XOR P'_3
```

View File

@@ -0,0 +1,21 @@
# 挑战二十八实现SHA-1密钥MAC
## 题目描述
找到你所用编程语言的SHA-1实现。
## 重要提醒
**不要作弊。那样不会有效果。**
不要使用你的语言已经提供的SHA-1实现例如不要在Ruby中使用"Digest"库或调用OpenSSL在Ruby中你需要一个纯Ruby的SHA-1
## 实现要求
编写一个函数使用密钥前缀MAC在密钥下对消息进行身份验证这简单来说就是
```
SHA1(key || message)
```
验证你不能在不破坏你生成的MAC的情况下篡改消息并且你不能在不知道密钥的情况下生成新的MAC。

View File

@@ -0,0 +1,47 @@
# 挑战二十九使用长度扩展攻击破解SHA-1密钥MAC
## 题目描述
密钥前缀SHA-1 MAC很容易被破解。
对密钥前缀SHA1的攻击依赖于这样一个事实你可以获取SHA-1的输出并将其用作SHA-1的新起始点从而获取任意的SHA-1哈希并"给它提供更多数据"。
由于在密钥前缀中密钥位于数据之前你以这种方式提供给SHA-1哈希的任何额外数据都将看起来像是用密钥进行了哈希。
## 攻击原理
要执行攻击你需要考虑SHA-1使用消息的位长度进行"填充"的事实;你的伪造消息需要包含该填充。我们称之为"胶水填充"。你实际伪造的最终消息将是:
```
SHA1(key || original-message || glue-padding || new-message)
```
(其中整个构造消息的最终填充是隐含的)
注意,要生成胶水填充,你需要知道消息的原始位长度;消息本身对攻击者是已知的,但密钥不是,所以你需要猜测它。
这在实践中听起来比实际复杂。
## 实现步骤
要实现攻击首先编写计算任意消息MD填充的函数并验证你生成的填充与你的SHA-1实现使用的填充相同。这应该需要5-10分钟。
现在获取你要伪造的消息的SHA-1密钥前缀MAC——这只是一个SHA-1哈希——并将其分解为32位SHA-1寄存器SHA-1称它们为"a"、"b"、"c"等)。
修改你的SHA-1实现使调用者可以传入"a"、"b"、"c"等的新值(它们通常从魔数开始)。通过"固定"寄存器,对你要伪造的额外数据进行哈希。
## 攻击任务
使用此攻击,在密钥(从/usr/share/dict/words或类似地方选择一个随机单词下生成字符串的密钥前缀MAC
```
"comment1=cooking%20MCs;userdata=foo;comment2=%20like%20a%20pound%20of%20bacon"
```
伪造此消息的一个变体,以";admin=true"结尾。
## 实用性
**这是一个非常有用的攻击:**
例如Thai Duong和Juliano Rizzo比我们更早发现这种攻击他们用它破解了Flickr API。

View File

@@ -0,0 +1,11 @@
# 挑战三十使用长度扩展攻击破解MD4密钥MAC
## 题目描述
第二段与第一段相同但使用MD4而不是SHA-1。一旦对SHA-1进行了这种攻击MD4变体应该需要更少的时间主要是你花在Google搜索MD4实现上的时间。
## 背景说明
**你想,我们为什么要费心做这个?**
要怪Stripe。在他们的第二个CTF游戏中倒数第二个挑战涉及用SHA1破解H(k, m) MAC。这意味着SHA1代码在互联网上到处漂浮。MD4代码就没那么多了。

View File

@@ -0,0 +1,31 @@
# 挑战三十一实现并破解带有人工时序泄露的HMAC-SHA1
## 题目描述
Wikipedia上的伪代码应该就足够了。HMAC非常简单。
使用你选择的Web框架Sinatra、web.py等编写一个小应用程序它有一个URL接受"file"参数和"signature"参数,如下所示:
```
http://localhost:9000/test?file=foo&signature=46b4ec586117154dacd49d664e5d63fdc88efb51
```
让服务器生成HMAC密钥然后验证传入请求的"signature"对"file"是否有效,使用"=="运算符比较文件的有效MAC与"signature"参数换句话说按任何普通程序员验证HMAC的方式验证它
## 实现要求
编写一个函数,称之为"insecure_compare",通过逐字节比较和早期退出来实现==操作在第一个不匹配的字节处返回false
在"insecure_compare"的循环中添加50ms睡眠每个字节后睡眠50ms
使用你的"insecure_compare"函数验证传入请求的HMAC并测试整个装置是否工作。如果MAC无效则返回500如果正常则返回200。
## 攻击任务
使用此应用程序中的时序泄露编写一个程序来发现任何文件的有效MAC。
## 为什么使用人工延迟?
**为什么是人工延迟?**
早期退出字符串比较可能是密码学时序泄露最常见的来源但它们并不特别容易利用。事实上许多时序泄露例如C、C++、Ruby或Python中的任何泄露可能根本无法在广域网上利用。要玩攻击现实世界的时序泄露你必须开始编写低级时序代码。我们在这些挑战中保持密码学性。

View File

@@ -0,0 +1,7 @@
# 挑战三十二破解稍微不那么人工的时序泄露HMAC-SHA1
## 题目描述
减少你的"insecure_compare"中的睡眠时间直到你之前的解决方案失效。尝试从5ms开始。
现在再次破解它。

View File

@@ -0,0 +1,100 @@
# 挑战 33实现 Diffie-Hellman 密钥交换
## 背景介绍
Diffie-Hellman 密钥交换是密码学中最重要的算法之一,它允许两方在不安全的通信信道上安全地交换密钥。这个算法基于离散对数问题的难解性。
## 实现步骤
### 第一步:小数值测试
首先用小数值来理解算法的工作原理:
1. **设置参数**
- 设置 `p = 37`(素数)
- 设置 `g = 5`(生成元)
2. **Alice 的操作**
- 生成随机私钥 `a`0 < a < 37
- 计算公钥 `A = g^a mod p = 5^a mod 37`
3. **Bob 的操作**
- 生成随机私钥 `b`0 < b < 37
- 计算公钥 `B = g^b mod p = 5^b mod 37`
4. **密钥交换**
- Alice 和 Bob 交换公钥 `A``B`
- Alice 计算共享密钥:`s = B^a mod p`
- Bob 计算共享密钥:`s = A^b mod p`
- 验证两者得到相同的 `s`
5. **生成最终密钥**
- 将共享密钥 `s` 进行哈希处理生成实际使用的密钥
- 可以使用 SHA-256 生成 128 位密钥材料,或分别生成加密密钥和 MAC 密钥
### 第二步:实际参数实现
使用 NIST 推荐的实际参数重复上述过程:
**参数 p**1024位素数
```
ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024
e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd
3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec
6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f
24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361
c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552
bb9ed529077096966d670c354e4abc9804f1746c08ca237327fff
fffffffffffff
```
**参数 g**
```
g = 2
```
## 实现要点
### 模指数运算
**重要提示**:必须实现自己的模指数运算函数,因为直接计算 `a^(1024位数)` 会导致数值溢出。
推荐使用快速模指数算法(二进制方法):
### 大数运算
- 在 Rust 中可以使用 `num-bigint` crate 处理大整数运算
- Python、Ruby 等高级语言原生支持大整数,实现更容易
## 数学原理
Diffie-Hellman 的安全性基于:
- **离散对数问题**:给定 `g^x mod p`,求解 `x` 在计算上是困难的
- **共享密钥计算**`(g^a)^b mod p = (g^b)^a mod p = g^(ab) mod p`
## 实现提示
1. 确保正确实现模指数运算,避免数值溢出
2. 使用密码学安全的随机数生成器生成私钥
3. 验证小参数和大参数情况下都能得到正确结果
4. 注意处理边界情况和错误输入
## 算法工作流程示例
```
Alice Bob
----- -----
私钥: a (随机) 私钥: b (随机)
公钥: A = g^a mod p 公钥: B = g^b mod p
交换公钥 A, B
<----------->
共享密钥: s = B^a mod p 共享密钥: s = A^b mod p
最终密钥: hash(s) 最终密钥: hash(s)
```
这样Alice 和 Bob 就在不直接传输密钥的情况下,安全地协商出了相同的共享密钥。

View File

@@ -0,0 +1,57 @@
# 挑战三十四实现对Diffie-Hellman的参数注入MITM密钥固定攻击
## 协议设计
使用你刚刚编写的代码来构建协议和"回声"机器人。如果你不想的话,实际上不必做网络部分;只需模拟即可。协议是:
**A->B**
发送 "p", "g", "A"
**B->A**
发送 "B"
**A->B**
发送 AES-CBC(SHA1(s)[0:16], iv=random(16), msg) + iv
**B->A**
发送 AES-CBC(SHA1(s)[0:16], iv=random(16), A's msg) + iv
换句话说用SHA1从DH派生AES密钥在两个方向都使用它并使用随机IV进行CBCIV附加或前置到消息中
## MITM攻击实现
现在实现以下MITM攻击
**A->M**
发送 "p", "g", "A"
**M->B**
发送 "p", "g", "p"
**B->M**
发送 "B"
**M->A**
发送 "p"
**A->M**
发送 AES-CBC(SHA1(s)[0:16], iv=random(16), msg) + iv
**M->B**
中继那个到B
**B->M**
发送 AES-CBC(SHA1(s)[0:16], iv=random(16), A's msg) + iv
**M->A**
中继那个到A
## 攻击原理
M应该能够解密消息。协议中的"A"和"B"——公钥,通过网络——已被替换为"p"。快速做一下DH数学看看这对密钥的可预测性有什么影响。
当消息经过时从M的角度解密消息。
## 注意
注意实际上不必注入虚假参数来使这种攻击起作用你可以生成Ma、MA、Mb和MB作为有效的DH参数来进行通用MITM攻击。但要进行参数注入攻击它将再次出现。

View File

@@ -0,0 +1 @@
# Challenge 35: Implement DH with negotiated groups, and break with malicious "g" parameters

View File

@@ -0,0 +1 @@
# Challenge 36: Implement Secure Remote Password (SRP)

View File

@@ -0,0 +1 @@
# Challenge 37: Break SRP with a zero key

View File

@@ -0,0 +1 @@
# Challenge 38: Offline dictionary attack on simplified SRP

View File

@@ -0,0 +1 @@
# Challenge 39: Implement RSA

View File

@@ -0,0 +1 @@
# Challenge 40: Implement an E=3 RSA Broadcast attack

View File

@@ -0,0 +1,44 @@
# 挑战四十一:实现无填充消息恢复预言机
## 背景介绍
Nate Lawson说我们应该停止称其为"RSA填充",开始称其为"RSA装甲"。原因如下。
想象一个Web应用程序再次使用Javascript加密接收RSA加密的消息这些消息再次强调Javascript在加密之前根本没有填充。
你可以提交任意RSA数据块服务器将返回明文。但你不能两次提交相同的消息假设服务器在某个活跃时间间隔内保存先前消息的哈希并且消息嵌入了时间戳
```json
{
time: 1356304276,
social: '555-55-5555',
}
```
## 攻击任务
你想破解给定的密文。但你不能简单地将密文传递给服务器并获得明文。
但是RSA是同态的。也就是说
```
RSA(AB) = RSA(A) * RSA(B)
```
这意味着你可以生成任何明文P的密文如果
```
RSA(P) = RSA(S * C / S) = RSA(S) * RSA(C) / RSA(S)
```
也就是说你选择S比如2并发送
```
RSA(2) * C * RSA(2^(-1 mod n))
```
服务器将计算2 * P / 2这就是P。
## 实现要求
实现这个攻击。

View File

@@ -0,0 +1,34 @@
# 挑战四十二Bleichenbacher的e=3 RSA攻击
## 历史背景
**密码学旅游信息牌:**
这种攻击几年前破解了Firefox的TLS证书验证。你可以编写Python脚本为任何证书伪造RSA签名。我们每隔一年左右就会发现它的新实例。
## 攻击原理
使用加密指数为3的RSA很受欢迎因为它使RSA数学计算更快。
与所有的"textbook RSA"实现一样没有填充的RSA签名验证是不安全的。但PKCS#1v15填充的RSA签名验证也可能不安全
首先让我们谈谈PKCS#1签名填充。使用公钥指数e=3的RSA签名是这样进行的
1. 生成要签名的哈希H
2. 生成ASN.1 DigestInfo结构将哈希算法的OID与哈希值结合
3. 应用PKCS#1填充,产生"00 01 FF FF ... FF FF 00 ASN.1 HASH"
4. 取结果的立方根mod n生成签名
这是textbook RSA但应用了结构化填充。
验证工作是反过来的立方操作sig^3 mod n然后检查填充结构。
## 攻击实现
这种攻击基于实现错误。很多实现没有检查"FF FF ... FF FF 00"填充是否延续到数字的完整长度,而是在看到"00 ASN.1 HASH"后就停止了。
这是致命的:攻击者可以通过在第一部分放置正确的字节,然后添加任何垃圾来伪造签名,只要立方根是整数即可。
## 任务
实现RSA签名验证和伪造攻击。

View File

@@ -0,0 +1 @@
# Challenge 43: DSA key recovery from nonce

View File

@@ -0,0 +1 @@
# Challenge 44: DSA nonce recovery from repeated nonce

View File

@@ -0,0 +1 @@
# Challenge 45: DSA parameter tampering

View File

@@ -0,0 +1 @@
# Challenge 46: RSA parity oracle

View File

@@ -0,0 +1 @@
# Challenge 47: Bleichenbacher's PKCS 1.5 Padding Oracle (Simple Case)

View File

@@ -0,0 +1 @@
# Challenge 48: Bleichenbacher's PKCS 1.5 Padding Oracle (Complete Case)

View File

@@ -0,0 +1,82 @@
# 挑战四十九CBC-MAC消息伪造
## CBC-MAC原理
让我们谈谈CBC-MAC。
CBC-MAC是这样的
1. 取明文P
2. 使用密钥K在CBC下加密P产生密文C
3. 丢掉C的所有部分除了最后一块C[n]
4. C[n]就是MAC
## 场景设置
假设有一个在线银行应用程序它通过网络与API服务器通信来执行用户请求。每个请求看起来像这样
```
message || IV || MAC
```
消息看起来像这样:
```
from=#{from_id}&to=#{to_id}&amount=#{amount}
```
现在为它编写一个API服务器和Web前端。注意无需雄心勃勃地编写实际的服务器和Web应用程序。完全可以在这一个上走低保真路线。客户端和服务器应该共享一个密钥K来签名和验证消息。
## 第一次攻击
API服务器应该接受消息验证签名如果MAC有效则执行每个事务。它也是公开暴露的——攻击者可以自由提交消息假设他可以伪造正确的MAC。
Web客户端应该允许攻击者为他控制的账户生成有效消息。假设攻击者能够捕获和检查从客户端到API服务器的消息。
我们还没有讨论的一件事是IV。假设客户端生成每消息IV并将其与MAC一起发送。这就是CBC的工作方式对吧
**错误。**
对于在CBC-MAC下签名的消息攻击者控制的IV是一种负债。为什么因为它对消息的第一块产生完全控制。
使用这个事实生成一条消息将100万太空币从目标受害者的账户转移到你的账户。
## 第二次攻击
现在让我们稍微调整一下协议。
如我们现在所知你应该使用固定IV与CBC-MAC所以让我们这样做。为简单起见我们将其设置为0。这意味着IV从协议中消失
```
message || MAC
```
很简单,但我们也会调整消息。为了效率的目的,银行希望能够在单个请求中处理多个事务。所以消息现在看起来像这样:
```
from=#{from_id}&tx_list=#{transactions}
```
事务列表格式如下:
```
to:amount(;to:amount)*
```
这里仍然有一个弱点MAC容易受到长度扩展攻击。如何
CBC-MAC的输出是新消息的有效IV。
*"但我们不再控制IV了"*
通过对CBC的充分掌握我们可以伪造它。
你的任务从目标用户捕获有效消息。使用长度扩展添加向攻击者账户支付100万太空币的事务。
## 提示
如果你对消息的第一块有完全控制权,这会容易得多,对吧?也许你可以模拟这一点。
## 思考题
思考题:你会如何修改协议来防止这种情况?

View File

@@ -0,0 +1,27 @@
# 挑战五十使用CBC-MAC进行哈希
## 问题背景
有时人们试图使用CBC-MAC作为哈希函数。
这是一个坏想法。Matt Green解释
> 长话短说密码学哈希函数是公共函数没有密钥具有抗碰撞性很难找到具有相同哈希的两条消息。MAC是密钥函数通常提供消息不可伪造性——一个非常不同的属性。而且只有当密钥是秘密时它们才能保证这一点。
## 挑战任务
让我们尝试一个简单的练习。
哈希函数经常用于代码验证。这个JavaScript代码片段带换行符
```javascript
alert('MZA who was that?');
```
在CBC-MAC下哈希为 *296b8d7cb78a243dda4d0a61d33bbdd1*,使用密钥 *"YELLOW SUBMARINE"* 和0 IV。
伪造一个有效的JavaScript代码片段提示"Ayo, the Wu is back!"并哈希到相同值。确保它在浏览器中运行。
## 额外学分
编写JavaScript代码下载你的文件检查其CBC-MAC并且仅在匹配预期哈希时将其插入DOM。

View File

@@ -0,0 +1,69 @@
# 挑战五十一:压缩比率侧信道攻击
## 背景介绍
互联网流量通常被压缩以节省带宽。直到最近这包括HTTPS头部它仍然包括响应的内容。
这为什么重要?
嗯,如果你是一个攻击者,拥有:
1. 部分明文知识 **
2. 部分明文控制 **
3. 访问压缩预言机
你有很好的机会恢复任何额外的未知明文。
什么是压缩预言机?你给它一些输入,它告诉你完整消息的压缩效果如何,即结果输出的长度。
## 攻击场景
这有点类似于我们在第4组中进行的时序攻击我们利用偶然的侧信道而不是攻击密码学机制本身。
场景你正在运行MITM攻击目标是窃取安全会话cookie。你已经注入了恶意内容允许你生成任意请求并在传输中观察它们。具体细节并不十分重要继续下去。
## 预言机实现
所以!编写这个预言机:
```
oracle(P) -> length(encrypt(compress(format_request(P))))
```
格式化请求如下:
```
POST / HTTP/1.1
Host: hapless.com
Cookie: sessionid=TmV2ZXIgcmV2ZWFsIHRoZSBXdS1UYW5nIFNlY3JldCE=
Content-Length: ((len(P)))
((P))
```
假装你看不到那个会话id。你是攻击者。
使用zlib或其他什么压缩。
加密...对我们的目的来说实际上有点无关紧要,但要有体育精神。只使用某种流密码。自由选择。每次调用预言机时使用随机密钥/IV。
然后只返回字节长度。
## 攻击原理
现在,这里的想法是使用压缩库泄露信息。"sessionid=T"的有效载荷应该比"sessionid=S"压缩得稍微好一点。
有一个复杂因素。DEFLATE算法以单个位为单位操作但最终消息长度将以字节为单位。即使你确实找到了更好的压缩差异可能不会跨越字节边界。所以这是一个问题。
你也可能得到一些偶然的假阳性。
但别担心!我对你有充分的信心。
## 任务
使用压缩预言机恢复会话id。
我等着。
得到了吗?太好了。
现在将你的流密码换成CBC并再次执行。

View File

@@ -0,0 +1 @@
# Challenge 52: Iterated Hash Function Multicollisions

View File

@@ -0,0 +1 @@
# Challenge 53: Kelsey and Schneier's Expandable Messages

View File

@@ -0,0 +1 @@
# Challenge 54: Kelsey and Kohno's Nostradamus Attack

View File

@@ -0,0 +1 @@
# Challenge 55: MD4 Collisions

View File

@@ -0,0 +1 @@
# Challenge 56: RC4 Single-Byte Biases

View File

@@ -0,0 +1,78 @@
# 挑战五十七Diffie-Hellman重访小子群限制攻击
## 背景介绍
这个集合将专注于椭圆曲线。但在我们开始之前我们将用一些经典的Diffie-Hellman来启动事情。
相信我,这稍后会有意义的。
## 协议设置
让我们直接开始。首先构建你的典型Diffie-Hellman密钥协商Alice和Bob交换公钥并派生相同的共享秘密。然后Bob向Alice发送一些带有MAC的消息。轻而易举。
使用这些参数:
```
p = 7199773997391911030609999317773941274322764333428698921736339643928346453700085358802973900485592910475480089726140708102474957429903531369589969318716771
g = 4565356397095740655436854503483826832136106141639563487732438195343690437606117828318042418238184896212352329118608100083187535033402010599512641674644143
```
生成元g具有阶q
```
q = 236234353446506858198510045061214171961
```
"阶"是一个新词但它只是意味着g^q = 1 mod p。你可能注意到q是素数就像p一样。这不是巧合事实上我们一起选择了q和p使得q整除p-1群本身的阶或大小。这保证了阶为q的元素g将存在。事实上将有q-1个这样的元素。
## 攻击原理
回到协议。Alice和Bob应该选择他们的密钥作为模q的随机整数。选择模p没有意义由于g具有阶q数字在那之后只会开始重复。你可以通过验证对任何x和kg^x mod p = g^(x + k*q) mod p来证明这一点。
我们如何攻击这个协议记住我们之前说过的关于阶的话q整除p-1的事实保证了阶为q的元素的存在。如果p-1有更小的除数呢
剧透警告有的。我选择了j = (p-1) / q有许多小因子因为我希望你快乐。通过分解j来找到它们
```
j = 30477252323177606811760882179058908038824640750610513771646768011063128035873508507547741559514324673960576895059570
```
你不需要完全分解它。只需找到一堆小于2^16的因子。应该有很多。友好提示也许避免任何重复因子。它们只会使事情复杂化。
## Pohlig-Hellman攻击
得到了吗好。现在我们可以使用这些来使用Pohlig-Hellman算法恢复Bob的密钥进行离散对数。方法如下
1. **找到小阶元素**取小因子j之一。称之为r。我们想要找到阶为r的元素h
```
h := rand(1, p)^((p-1)/r) mod p
```
如果h = 1再试一次。
2. **发送无效公钥**你是Eve。将h作为你的公钥发送给Bob。注意h不是有效的公钥不存在x使得h = g^x mod p。但Bob不知道这一点。
3. **Bob计算共享密钥**Bob将计算
```
K := h^x mod p
```
其中x是他的密钥K是输出的共享秘密。然后Bob发回(m, t)
```
m := "crazy flamboyant for the rap enjoyment"
t := MAC(K, m)
```
4. **暴力搜索**我们Eve无法计算K因为h实际上不是有效的公钥。但我们还没有失败。
记住我们看到g^x当x > q时开始重复h对r具有相同的性质。这意味着Bob可能生成的K值只有r个可能值。我们可以通过对这些值进行暴力搜索来恢复K直到t = MAC(K, m)。
现在我们知道Bob的密钥x mod r。
5. **重复攻击**重复步骤1到4多次。最终你将知道
```
x = b1 mod r1
x = b2 mod r2
x = b3 mod r3
...
```
一旦(r1*r2*...*rn) > q你将有足够的信息使用中国剩余定理重新组装Bob的密钥。

View File

@@ -0,0 +1 @@
# Challenge 58: Pollard's Method for Catching Kangaroos

View File

@@ -0,0 +1,133 @@
# 挑战五十九椭圆曲线Diffie-Hellman和无效曲线攻击
## 椭圆曲线基础
我不打算给你展示任何图表——如果你想看一个,你可以在互联网上的其他每个椭圆曲线教程中找到它们。就个人而言,我从未能够从中获得太多洞察。
它们在ASCII中也很难绘制。
理解椭圆曲线的关键是它们是一个在许多方面类似于我们更熟悉的环境——模p的乘法整数。所以如果我们学习某些原始操作是如何定义的我们可以使用我们工具带中已有的很多工具来推理它们。
## 椭圆曲线方程
让我们深入研究。椭圆曲线E只是这样的方程
```
y^2 = x^3 + a*x + b
```
a和b系数的选择定义了曲线。
我们群中的元素将是满足曲线方程的(x, y)坐标。现在,曲线上有无限多对这样的点,但我们只想考虑其中一些。我们将通过在有限域的上下文中考虑曲线来减少我们的点集。
## 有限域和椭圆曲线
暂时,了解有限域是什么并不太重要。你基本上可以将其视为"模p的整数",具有你期望的所有常用操作:乘法、除法(通过模逆)、加法和减法。
我们将使用记号GF(p)来讨论大小为p的有限域。"GF"代表"伽罗瓦域"有限域的另一个名称。当我们在域GF(p)上取曲线E写作E(GF(p))我们所说的是只有x和y都在GF(p)中的点才有效。
## 椭圆曲线群操作
如果这些点要形成类似于模p乘法整数的群我们需要有一组类似的原始函数来处理它们。
1. **点加法**在模p乘法整数中我们通过将两个元素相乘并取模p余数来组合它们。
我们通过添加椭圆曲线点来组合它们。
2. **单位元**我们使用1作为乘法单位元y * 1 = y对所有y。
在椭圆曲线上我们定义单位元O为抽象的"无穷远点",它不映射到任何实际的(x, y)对。
在曲线上我们有简单的规则P + O = P对所有P。
3. **逆元**我们有modinv函数来反转模p的整数。这作为除法的替代。给定y它找到x使得y * x = 1。
椭圆曲线中的逆元更容易。只需翻转y上的符号
```
invert((x, y)) = (x, -y) = (x, p-y)
```
## 椭圆曲线点加法算法
这是加法:
```python
function add(P1, P2):
if P1 = O:
return P2
if P2 = O:
return P1
if P1 = invert(P2):
return O
x1, y1 := P1
x2, y2 := P2
if P1 = P2:
m := (3*x1^2 + a) / 2*y1
else:
m := (y2 - y1) / (x2 - x1)
x3 := m^2 - x1 - x2
y3 := m*(x1 - x3) - y1
return (x3, y3)
```
## 标量乘法
在椭圆曲线上,我们将使用标量乘法来表示重复加法:
```
P + P + P + P + P = 5*P
```
不要被共享记号搞混:标量乘法不类似于整数中的乘法。它类似于指数运算。
## 挑战任务
实现一套函数直到椭圆曲线标量乘法。使用这个曲线:
```
y^2 = x^3 - 95051*x + 11279326
```
在GF(233970423115425145524320034830162017933)上。使用这个基点:
```
(182, 85518893674295321206118380980485522083)
```
它的阶为29246302889428143187362802287225875743。
## 无效曲线攻击
我们能在这个设置中应用来自#57的子群限制攻击吗起初看来会很困难因为辅因子很小。我们可以通过发送阶为8的点来恢复大约三位但仅此而已。曲线上确实没有足够的小阶点。
曲线外的点呢?
等等,什么?是的,*不在*曲线上的点。仔细看看我们的组合函数。注意缺少什么曲线的b参数在任何地方都没有计算。这是因为我们有四个计算输入曲线参数(a, b)和点坐标(x, y)。给定任何三个,你可以计算第四个。
这里有一个危险的假设:即对等方将提交有效的(x, y)对。如果Eve可以提交无效对这真正为她打开了游戏现在她可以从仅在b参数上不同的任何曲线中选择点。她所要做的就是找到一些具有小子群的曲线并挑选一些小阶点。Alice将无意中在错误的曲线上计算共享密钥并在此过程中泄露她私钥的几位。
## 攻击实现
使用这些我为你生成的曲线:
```
y^2 = x^3 - 95051*x + 210
y^2 = x^3 - 95051*x + 504
y^2 = x^3 - 95051*x + 727
```
它们的阶为:
```
233970423115425145550826547352470124412
233970423115425145544350131142039591210
233970423115425145545378039958152057148
```
从无效曲线使用小阶点实现来自#57的密钥恢复攻击

View File

@@ -0,0 +1 @@
# Challenge 60: Single-Coordinate Ladders and Insecure Twists

View File

@@ -0,0 +1 @@
# Challenge 61: Duplicate-Signature Key Selection in ECDSA (and RSA)

View File

@@ -0,0 +1 @@
# Challenge 62: Key-Recovery Attacks on ECDSA with Biased Nonces

View File

@@ -0,0 +1 @@
# Challenge 63: Key-Recovery Attacks on GCM with Repeated Nonces

View File

@@ -0,0 +1 @@
# Challenge 64: Key-Recovery Attacks on GCM with a Truncated Counter