feat: 实现多个挑战并改进测试
- 重写p1实现纯Rust的hex到base64转换 - 完成p13 ECB剪切粘贴攻击和破解脚本 - 实现p33 Diffie-Hellman密钥交换算法 - 修复p9的PKCS#7测试用例 - 在common库添加gen_random_key和pkcs7_unpadding函数 - 更新workspace依赖管理
This commit is contained in:
115
Cargo.lock
generated
115
Cargo.lock
generated
@@ -8,18 +8,18 @@ version = "1.0.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.1"
|
||||
@@ -31,6 +31,7 @@ name = "common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -39,13 +40,12 @@ version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.3"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
|
||||
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
@@ -61,12 +61,46 @@ version = "0.2.174"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
|
||||
dependencies = [
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.46"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "p1"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"hex",
|
||||
"anyhow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -100,10 +134,20 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "p13"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"common",
|
||||
"hex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "p14"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
"common",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "p15"
|
||||
@@ -139,6 +183,17 @@ dependencies = [
|
||||
"hex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "p33"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "p4"
|
||||
version = "0.1.0"
|
||||
@@ -184,6 +239,9 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "p9"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
@@ -212,27 +270,22 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "5.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.2"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.9.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
@@ -240,9 +293,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.9.3"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
@@ -266,21 +319,9 @@ checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.14.2+wasi-0.2.4"
|
||||
version = "0.11.1+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
|
||||
dependencies = [
|
||||
"wit-bindgen-rt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.39.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
|
||||
@@ -10,4 +10,7 @@ members = ["problems/*", "common"]
|
||||
hex = "0.4.3"
|
||||
base64 = "0.22.1"
|
||||
anyhow = "1.0.98"
|
||||
rand = "0.9.2"
|
||||
rand = "0.8"
|
||||
num-bigint = { version = "0.4", features = ["rand"] }
|
||||
num-traits = "0.2"
|
||||
once_cell = "1.21"
|
||||
|
||||
@@ -4,4 +4,5 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.98"
|
||||
anyhow = { workspace = true }
|
||||
rand = { workspace = true }
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use anyhow::{Result, anyhow};
|
||||
use rand::prelude::*;
|
||||
|
||||
const SBOX: [u8; 256] = [
|
||||
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
|
||||
@@ -396,6 +397,22 @@ pub fn pkcs7_padding(data: &mut Vec<u8>, block_size: usize) {
|
||||
data.extend(vec![padding_length as u8; padding_length]);
|
||||
}
|
||||
|
||||
pub fn pkcs7_unpadding(input: &[u8]) -> Result<Vec<u8>> {
|
||||
if input.is_empty() {
|
||||
return Err(anyhow!("Input cannot be empty"));
|
||||
}
|
||||
let padding_length = *input.last().unwrap();
|
||||
if padding_length == 0 || padding_length > input.len() as u8 {
|
||||
return Err(anyhow!("Invalid PKCS#7 padding"));
|
||||
}
|
||||
for &byte in input.iter().rev().take(padding_length as usize) {
|
||||
if byte != padding_length {
|
||||
return Err(anyhow!("Invalid PKCS#7 padding"));
|
||||
}
|
||||
}
|
||||
Ok(input[..input.len() - padding_length as usize].to_vec())
|
||||
}
|
||||
|
||||
pub fn is_ecb(cipher: &[u8]) -> bool {
|
||||
// Check if the input is a valid ECB encrypted data
|
||||
let mut seen_blocks = HashSet::new();
|
||||
@@ -458,3 +475,10 @@ pub fn xor_with_key(input: &[u8], key: &[u8]) -> Result<Vec<u8>> {
|
||||
.map(|(&a, &b)| a ^ b)
|
||||
.collect())
|
||||
}
|
||||
|
||||
pub fn gen_random_key() -> [u8; 16] {
|
||||
let mut rng = rand::rng();
|
||||
let mut key = [0u8; 16];
|
||||
rng.fill(&mut key);
|
||||
key
|
||||
}
|
||||
|
||||
@@ -4,5 +4,4 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
hex = { workspace = true }
|
||||
base64 = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
|
||||
@@ -1,13 +1,106 @@
|
||||
use base64::{Engine as _, engine::general_purpose::STANDARD};
|
||||
use anyhow::{Ok, Result, anyhow};
|
||||
|
||||
fn hex_to_base64(hex_str: &str) -> String {
|
||||
let bytes = hex::decode(hex_str).expect("解码失败");
|
||||
STANDARD.encode(&bytes)
|
||||
const BASE_TABLE: [char; 64] = [
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
|
||||
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
|
||||
'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4',
|
||||
'5', '6', '7', '8', '9', '+', '/',
|
||||
];
|
||||
|
||||
fn hex_to_base64(hex_str: &str) -> Result<String> {
|
||||
if hex_str.is_empty() {
|
||||
return Ok("".to_string());
|
||||
}
|
||||
let mut hex_str = hex_str.to_lowercase();
|
||||
if hex_str.len() % 2 == 1 {
|
||||
hex_str.insert(0, '0');
|
||||
}
|
||||
let mut hex_bytes: Vec<u8> = Vec::new();
|
||||
|
||||
let mut result = String::new();
|
||||
|
||||
for i in hex_str.as_bytes().chunks(2) {
|
||||
let high = match i[0] {
|
||||
b'0'..=b'9' => i[0] - b'0',
|
||||
b'a'..=b'f' => i[0] - b'a' + 10,
|
||||
b'A'..=b'F' => i[0] - b'A' + 10,
|
||||
_ => return Err(anyhow!("Invalid hex character: {}", i[0] as char)),
|
||||
};
|
||||
let low = match i[1] {
|
||||
b'0'..=b'9' => i[1] - b'0',
|
||||
b'a'..=b'f' => i[1] - b'a' + 10,
|
||||
b'A'..=b'F' => i[1] - b'A' + 10,
|
||||
_ => return Err(anyhow!("Invalid hex character: {}", i[0] as char)),
|
||||
};
|
||||
|
||||
hex_bytes.push(high << 4 | low);
|
||||
}
|
||||
|
||||
for base_bytes in hex_bytes.chunks(3) {
|
||||
match base_bytes.len() {
|
||||
1 => {
|
||||
let index_1 = base_bytes[0] >> 2 & 0x3F;
|
||||
let index_2 = (base_bytes[0] & 0x03) << 4;
|
||||
|
||||
result.push(BASE_TABLE[index_1 as usize]);
|
||||
result.push(BASE_TABLE[index_2 as usize]);
|
||||
result.push('=');
|
||||
result.push('=');
|
||||
}
|
||||
2 => {
|
||||
let index_1 = base_bytes[0] >> 2 & 0x3F;
|
||||
let index_2 = (base_bytes[0] & 0x03) << 4 | (base_bytes[1] >> 4) & 0x0f;
|
||||
let index_3 = (base_bytes[1] & 0x0F) << 2;
|
||||
result.push(BASE_TABLE[index_1 as usize]);
|
||||
result.push(BASE_TABLE[index_2 as usize]);
|
||||
result.push(BASE_TABLE[index_3 as usize]);
|
||||
result.push('=');
|
||||
}
|
||||
3 => {
|
||||
let index_1 = base_bytes[0] >> 2 & 0x3F;
|
||||
let index_2 = (base_bytes[0] & 0x03) << 4 | (base_bytes[1] >> 4) & 0x0f;
|
||||
let index_3 = (base_bytes[1] & 0x0F) << 2 | (base_bytes[2] >> 6) & 0x03;
|
||||
let index_4 = base_bytes[2] & 0x3F;
|
||||
result.push(BASE_TABLE[index_1 as usize]);
|
||||
result.push(BASE_TABLE[index_2 as usize]);
|
||||
result.push(BASE_TABLE[index_3 as usize]);
|
||||
result.push(BASE_TABLE[index_4 as usize]);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
fn main() -> Result<()> {
|
||||
// 从十六进制字符串解码为原始字节
|
||||
let hex_str = "49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d";
|
||||
let base64_str = hex_to_base64(hex_str);
|
||||
let base64_str = hex_to_base64(hex_str)?;
|
||||
println!("十六进制字符串: {base64_str}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*; // 导入上层的函数
|
||||
#[test]
|
||||
fn test_hex_to_base64_empty() {
|
||||
let hex_str = "";
|
||||
let expected_base64 = "";
|
||||
assert_eq!(hex_to_base64(hex_str).unwrap(), expected_base64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hex_to_base64_single_byte() {
|
||||
let hex_str = "4a";
|
||||
let expected_base64 = "Sg==";
|
||||
assert_eq!(hex_to_base64(hex_str).unwrap(), expected_base64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hex_to_base64_all_bytes() {
|
||||
let hex_str = "000102ff";
|
||||
let expected_base64 = "AAEC/w==";
|
||||
assert_eq!(hex_to_base64(hex_str).unwrap(), expected_base64);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
use common::{aes_cbc_enc, aes_ecb_enc, is_ecb, pkcs7_padding};
|
||||
use common::{aes_cbc_enc, aes_ecb_enc, gen_random_key, is_ecb, pkcs7_padding};
|
||||
use rand::prelude::*;
|
||||
|
||||
fn gen_random_key() -> [u8; 16] {
|
||||
let mut rng = rand::rng();
|
||||
let mut key = [0u8; 16];
|
||||
rng.fill(&mut key);
|
||||
key
|
||||
}
|
||||
|
||||
fn encryption_oracle(plaintext: &[u8]) -> Vec<u8> {
|
||||
let key = gen_random_key();
|
||||
let mut rng = rand::rng();
|
||||
|
||||
@@ -2,15 +2,7 @@ use std::process::exit;
|
||||
|
||||
use anyhow::Result;
|
||||
use base64::{Engine, engine::general_purpose::STANDARD};
|
||||
use common::{aes_ecb_enc, pkcs7_padding};
|
||||
use rand::{prelude::*, rand_core::block};
|
||||
|
||||
fn gen_random_key() -> [u8; 16] {
|
||||
let mut rng = rand::rng();
|
||||
let mut key = [0u8; 16];
|
||||
rng.fill(&mut key);
|
||||
key
|
||||
}
|
||||
use common::{aes_ecb_enc, gen_random_key, pkcs7_padding};
|
||||
|
||||
fn oracle(controlled_input: &[u8], key: &[u8; 16], unknown_string: &[u8]) -> Vec<u8> {
|
||||
let mut data = Vec::new();
|
||||
|
||||
@@ -4,3 +4,5 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
common = { path = "../../common" }
|
||||
hex = { workspace = true }
|
||||
|
||||
42
problems/p13/crack.py
Normal file
42
problems/p13/crack.py
Normal file
@@ -0,0 +1,42 @@
|
||||
from pwn import *
|
||||
|
||||
# context.log_level = "debug"
|
||||
|
||||
p = process("../../target/x86_64-unknown-linux-musl/debug/p13")
|
||||
p.recvuntil(b"> ") # 等待提示符
|
||||
|
||||
|
||||
p.sendline(b"1")
|
||||
p.recvuntil(b"email: ")
|
||||
p.sendline(b"foo@bar.com12")
|
||||
cipher = p.recvline().strip()
|
||||
print(f"Cipher: {cipher}")
|
||||
|
||||
role_cipher = cipher[32:64]
|
||||
|
||||
p.sendline(b"1")
|
||||
p.recvuntil(b"email: ")
|
||||
p.sendline(b"foo@bar.co" + b"admin" + b"\x0b" * 11)
|
||||
cipher = p.recvline().strip()
|
||||
# admin_cipher = cipher[32:64]
|
||||
print(f"Cipher: {cipher}")
|
||||
|
||||
cracked_cipher = cipher[0:32] + role_cipher + cipher[32:]
|
||||
|
||||
p.recvuntil(b"> ")
|
||||
p.sendline(b"2")
|
||||
p.recvuntil(b"cipher: ")
|
||||
p.sendline(cracked_cipher)
|
||||
|
||||
profile = p.recvline().strip()
|
||||
print(f"Profile: {profile}")
|
||||
|
||||
json = p.recvline().strip()
|
||||
print(f"Json: {json}")
|
||||
|
||||
if b"Cracked!" in json:
|
||||
print("Success!")
|
||||
|
||||
|
||||
p.recvuntil(b"> ")
|
||||
# p.interactive()
|
||||
@@ -1,3 +1,130 @@
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
use common::{aes_ecb_dec, aes_ecb_enc, gen_random_key, pkcs7_padding, pkcs7_unpadding};
|
||||
use std::fmt::Write as FmtWrite;
|
||||
use std::io::Write as IoWrite;
|
||||
|
||||
use std::io::{stdin, stdout};
|
||||
|
||||
fn kv_to_json(input: &str) -> String {
|
||||
let mut json = String::new();
|
||||
if input.is_empty() {
|
||||
return json;
|
||||
}
|
||||
|
||||
json.push('{');
|
||||
|
||||
for kv in input.split('&') {
|
||||
if let Some((key, value)) = kv.split_once('=') {
|
||||
if value.contains('=') {
|
||||
todo!("Ignore here");
|
||||
}
|
||||
if json.contains(key) {
|
||||
todo!("Ignore here")
|
||||
}
|
||||
match value.parse::<i32>() {
|
||||
Ok(num) => write!(json, "{key}: {num}, ").unwrap(),
|
||||
Err(_) => write!(json, "{key}: \'{value}\', ").unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
json.pop();
|
||||
json.pop();
|
||||
json.push('}');
|
||||
json
|
||||
}
|
||||
|
||||
/// Generates a user profile string from an email address.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// let profile = profile_for("foo@bar.com");
|
||||
/// assert_eq!(profile, "email=foo@bar.com&uid=10&role=user");
|
||||
/// ```
|
||||
fn profile_for(email: &str) -> String {
|
||||
let email = email.replace(['=', '&'], "");
|
||||
format!("email={email}&uid=10&role=user")
|
||||
}
|
||||
|
||||
fn enc_profile(profile: &str, key: &[u8; 16]) -> Vec<u8> {
|
||||
let mut profile = profile.as_bytes().to_vec();
|
||||
pkcs7_padding(&mut profile, 16);
|
||||
|
||||
aes_ecb_enc(&profile, key).unwrap()
|
||||
}
|
||||
|
||||
fn dec_profile(cipher: &[u8], key: &[u8; 16]) -> Vec<u8> {
|
||||
let profile = aes_ecb_dec(cipher, key).unwrap();
|
||||
pkcs7_unpadding(&profile).unwrap()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let key = gen_random_key();
|
||||
println!("input 1 for enc, input 2 for dec");
|
||||
loop {
|
||||
print!("> ");
|
||||
stdout().flush().unwrap(); // 强制刷新输出缓冲区
|
||||
|
||||
let mut option = String::new();
|
||||
stdin().read_line(&mut option).unwrap();
|
||||
let option = option.trim().parse::<i32>();
|
||||
match option {
|
||||
Ok(1) => {
|
||||
print!("email: ");
|
||||
stdout().flush().unwrap();
|
||||
let mut email = String::new();
|
||||
stdin().read_line(&mut email).unwrap();
|
||||
let email = email.trim();
|
||||
let profile = profile_for(email);
|
||||
let cipher = enc_profile(&profile, &key);
|
||||
for i in cipher {
|
||||
print!("{i:02x}");
|
||||
}
|
||||
println!();
|
||||
}
|
||||
Ok(2) => {
|
||||
print!("cipher: ");
|
||||
stdout().flush().unwrap();
|
||||
let mut cipher = String::new();
|
||||
stdin().read_line(&mut cipher).unwrap();
|
||||
let cipher = match hex::decode(cipher.trim()) {
|
||||
Ok(bytes) => bytes,
|
||||
Err(_) => {
|
||||
println!("invalid cipher");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let profile = String::from_utf8(dec_profile(&cipher, &key)).unwrap();
|
||||
println!("{profile}");
|
||||
let json = kv_to_json(&profile);
|
||||
if json.contains("role: 'admin'") {
|
||||
println!("Cracked!");
|
||||
} else {
|
||||
println!("{json}");
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
println!("input 1 for enc, input 2 for dec");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_kv_to_json() {
|
||||
let test = kv_to_json("foo=bar&baz=qux&zap=zazzle");
|
||||
println!("{test}");
|
||||
let test = kv_to_json("foo=bar&baz=qux&zap=123");
|
||||
println!("{test}");
|
||||
let test = kv_to_json("foo=bar&baz=qux&zap=123a");
|
||||
println!("{test}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_profile_for() {
|
||||
let test = profile_for("1@2");
|
||||
println!("u{test}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,6 @@ fn main() -> Result<(), String> {
|
||||
|
||||
// 将结果转换为十六进制字符串
|
||||
let hex_result = hex::encode(&xor_string);
|
||||
println!("{}", hex_result);
|
||||
println!("{hex_result}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ fn main() {
|
||||
if let Ok(text) = std::str::from_utf8(&plaintext) {
|
||||
// 只有当转换成功时才调用is_valid_english
|
||||
if is_valid_english(text, None) {
|
||||
println!("Found valid sentence with key {}: {}", key, text);
|
||||
println!("Found valid sentence with key {key}: {text}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
11
problems/p33/Cargo.toml
Normal file
11
problems/p33/Cargo.toml
Normal file
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "p33"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
num-bigint = { workspace = true }
|
||||
num-traits = { workspace = true }
|
||||
rand = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
once_cell = { workspace = true }
|
||||
209
problems/p33/src/main.rs
Normal file
209
problems/p33/src/main.rs
Normal file
@@ -0,0 +1,209 @@
|
||||
use anyhow::{Ok, Result, anyhow};
|
||||
use num_bigint::{BigUint, RandBigInt};
|
||||
use num_traits::{One, Zero};
|
||||
use once_cell::sync::Lazy;
|
||||
use rand::thread_rng;
|
||||
|
||||
static NIST_P: Lazy<BigUint> = Lazy::new(|| {
|
||||
BigUint::parse_bytes(b"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca237327ffffffffffffffff",16).unwrap()
|
||||
});
|
||||
|
||||
static NIST_G: Lazy<BigUint> = Lazy::new(|| BigUint::from(2u8));
|
||||
|
||||
fn mod_exp(base: &BigUint, exp: &BigUint, modulus: &BigUint) -> Result<BigUint> {
|
||||
if modulus.is_zero() {
|
||||
return Err(anyhow!("modulus should greater than 0"));
|
||||
}
|
||||
let mut base = base % modulus;
|
||||
if exp.is_zero() {
|
||||
return Ok(BigUint::one());
|
||||
}
|
||||
if base.is_zero() {
|
||||
return Ok(base);
|
||||
}
|
||||
let mut result = BigUint::one();
|
||||
let mut exp = exp.clone();
|
||||
while !exp.is_zero() {
|
||||
if exp.bit(0) {
|
||||
result = (&result * &base) % modulus;
|
||||
}
|
||||
base = (&base * &base) % modulus;
|
||||
exp >>= 1;
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
type PublicKey = BigUint;
|
||||
type PrivateKey = BigUint;
|
||||
|
||||
fn gen_dh_keypair() -> Result<(PrivateKey, PublicKey)> {
|
||||
let mut rng = thread_rng();
|
||||
let private_key = rng.gen_biguint_range(&BigUint::zero(), &NIST_P);
|
||||
let public_key = mod_exp(&NIST_G, &private_key, &NIST_P)?;
|
||||
Ok((private_key, public_key))
|
||||
}
|
||||
|
||||
fn gen_secret(public_key: &BigUint, privite_key: &BigUint) -> Result<BigUint> {
|
||||
mod_exp(public_key, privite_key, &NIST_P)
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let (sk_alice, pk_alice) = gen_dh_keypair()?;
|
||||
let (sk_bob, pk_bob) = gen_dh_keypair()?;
|
||||
let secret_alice = gen_secret(&pk_bob, &sk_alice)?;
|
||||
let secret_bob = gen_secret(&pk_alice, &sk_bob)?;
|
||||
if secret_alice == secret_bob {
|
||||
println!("We share the same secret");
|
||||
} else {
|
||||
println!("Secret incorrect");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use num_bigint::BigUint;
|
||||
use num_traits::{One, Zero};
|
||||
|
||||
#[test]
|
||||
fn test_basic_modexp() {
|
||||
// 3^4 mod 5 = 81 mod 5 = 1
|
||||
let result = mod_exp(
|
||||
&BigUint::from(3u32),
|
||||
&BigUint::from(4u32),
|
||||
&BigUint::from(5u32),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(result, BigUint::from(1u32));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_large_numbers() {
|
||||
// 2^100 mod 17
|
||||
let result = mod_exp(
|
||||
&BigUint::from(2u32),
|
||||
&BigUint::from(100u32),
|
||||
&BigUint::from(17u32),
|
||||
)
|
||||
.unwrap();
|
||||
// 2^100 mod 17 = 16 (can verify with smaller calculation)
|
||||
assert_eq!(result, BigUint::from(16u32));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zero_exponent() {
|
||||
// Any number^0 mod m = 1 (except 0^0)
|
||||
let result = mod_exp(
|
||||
&BigUint::from(123u32),
|
||||
&BigUint::zero(),
|
||||
&BigUint::from(456u32),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(result, BigUint::one());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zero_base() {
|
||||
// 0^n mod m = 0 (for n > 0)
|
||||
let result = mod_exp(&BigUint::zero(), &BigUint::from(5u32), &BigUint::from(7u32)).unwrap();
|
||||
assert_eq!(result, BigUint::zero());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_one_base() {
|
||||
// 1^n mod m = 1
|
||||
let result = mod_exp(
|
||||
&BigUint::one(),
|
||||
&BigUint::from(999u32),
|
||||
&BigUint::from(123u32),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(result, BigUint::one());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_modulus_one() {
|
||||
// Any number mod 1 = 0
|
||||
let result = mod_exp(
|
||||
&BigUint::from(123u32),
|
||||
&BigUint::from(456u32),
|
||||
&BigUint::one(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(result, BigUint::zero());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rsa_example() {
|
||||
// RSA-like calculation: 42^17 mod 77
|
||||
let result = mod_exp(
|
||||
&BigUint::from(42u32),
|
||||
&BigUint::from(17u32),
|
||||
&BigUint::from(77u32),
|
||||
)
|
||||
.unwrap();
|
||||
// Can verify this manually or with known RSA test vectors
|
||||
assert_eq!(result, BigUint::from(70u32));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fermat_little_theorem() {
|
||||
// p = 7 (prime), a = 3
|
||||
// 3^6 mod 7 should equal 1 (Fermat's Little Theorem: a^(p-1) ≡ 1 mod p)
|
||||
let result = mod_exp(
|
||||
&BigUint::from(3u32),
|
||||
&BigUint::from(6u32),
|
||||
&BigUint::from(7u32),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(result, BigUint::one());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_very_large_numbers() {
|
||||
// Test with very large numbers
|
||||
let base = BigUint::parse_bytes(b"123456789012345678901234567890", 10).unwrap();
|
||||
let exp = BigUint::from(1000u32);
|
||||
let modulus = BigUint::parse_bytes(b"987654321098765432109876543210987654321", 10).unwrap();
|
||||
|
||||
let result = mod_exp(&base, &exp, &modulus);
|
||||
assert!(result.is_ok());
|
||||
// Result should be less than modulus
|
||||
assert!(
|
||||
result.unwrap()
|
||||
< BigUint::parse_bytes(b"987654321098765432109876543210987654321", 10).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_zero_modulus() {
|
||||
// Division by zero should return error
|
||||
let result = mod_exp(&BigUint::from(5u32), &BigUint::from(3u32), &BigUint::zero());
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zero_zero_case() {
|
||||
// 0^0 is mathematically undefined, should handle appropriately
|
||||
let result = mod_exp(&BigUint::zero(), &BigUint::zero(), &BigUint::from(5u32));
|
||||
// Depending on your implementation, this might return 1 or error
|
||||
// Common convention is 0^0 = 1 in many contexts
|
||||
assert!(result.is_ok() || result.is_err()); // Just ensure it's handled
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_equal_base_modulus() {
|
||||
// base == modulus, so base mod modulus = 0
|
||||
// 0^exp mod modulus = 0 (for exp > 0)
|
||||
let result = mod_exp(
|
||||
&BigUint::from(7u32),
|
||||
&BigUint::from(3u32),
|
||||
&BigUint::from(7u32),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(result, BigUint::zero());
|
||||
}
|
||||
}
|
||||
@@ -4,3 +4,4 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
anyhow = { workspace = true }
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
use anyhow::{Result, anyhow};
|
||||
use core::panic;
|
||||
|
||||
fn pkcs7_padding(data: &mut Vec<u8>, block_size: usize) {
|
||||
if block_size == 0 {
|
||||
panic!("Block size must be greater than zero");
|
||||
@@ -12,6 +15,22 @@ fn pkcs7_padding(data: &mut Vec<u8>, block_size: usize) {
|
||||
data.extend(vec![padding_length as u8; padding_length]);
|
||||
}
|
||||
|
||||
fn pkcs7_unpadding(input: &[u8]) -> Result<Vec<u8>> {
|
||||
if input.is_empty() {
|
||||
return Err(anyhow!("Input cannot be empty"));
|
||||
}
|
||||
let padding_length = *input.last().unwrap();
|
||||
if padding_length == 0 || padding_length > input.len() as u8 {
|
||||
return Err(anyhow!("Invalid PKCS#7 padding"));
|
||||
}
|
||||
for &byte in input.iter().rev().take(padding_length as usize) {
|
||||
if byte != padding_length {
|
||||
return Err(anyhow!("Invalid PKCS#7 padding"));
|
||||
}
|
||||
}
|
||||
Ok(input[..input.len() - padding_length as usize].to_vec())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut plain_text = b"YELLOW SUBMARINE".to_vec();
|
||||
pkcs7_padding(&mut plain_text, 20);
|
||||
@@ -76,4 +95,43 @@ mod tests {
|
||||
assert_eq!(data.len(), 255);
|
||||
assert_eq!(&data[1..], vec![254; 254]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pkcs7_unpadding_valid() {
|
||||
let result = pkcs7_unpadding(&[65, 66, 67, 4, 4, 4, 4]).unwrap();
|
||||
assert_eq!(result, vec![65, 66, 67]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pkcs7_unpadding_single_byte() {
|
||||
let result = pkcs7_unpadding(&[65, 66, 67, 68, 69, 70, 1]).unwrap();
|
||||
assert_eq!(result, vec![65, 66, 67, 68, 69, 70]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pkcs7_unpadding_full_padding_block() {
|
||||
let result = pkcs7_unpadding(&[8, 8, 8, 8, 8, 8, 8, 8]).unwrap();
|
||||
assert_eq!(result, vec![]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pkcs7_unpadding_invalid_padding_byte_zero() {
|
||||
assert!(pkcs7_unpadding(&[65, 66, 67, 0]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pkcs7_unpadding_invalid_padding_too_large() {
|
||||
assert!(pkcs7_unpadding(&[65, 66, 67, 5]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pkcs7_unpadding_inconsistent_padding() {
|
||||
assert!(pkcs7_unpadding(&[65, 66, 67, 3, 3, 2]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Input cannot be empty")]
|
||||
fn test_pkcs7_unpadding_empty_data() {
|
||||
pkcs7_unpadding(&[]).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user