Finish challenge set 6, original challenges completed
This commit is contained in:
55
README.md
55
README.md
@@ -1,3 +1,58 @@
|
|||||||
# cryptopals
|
# cryptopals
|
||||||
|
|
||||||
My solutions to the [cryptopals](https://cryptopals.com/) challenges.
|
My solutions to the [cryptopals](https://cryptopals.com/) challenges.
|
||||||
|
|
||||||
|
As of February 2023, after 123 FocusMate sessions, I have completed the
|
||||||
|
(original) six challenge sets. I consider this project to be complete.
|
||||||
|
|
||||||
|
```
|
||||||
|
[okay] Challenge 1: SSdtIGtpbGxpbmcgeW91ciBicmFpbiBsaWtlIGEgcG9pc29ub3VzIG11c2hyb29t
|
||||||
|
[okay] Challenge 2: 746865206b696420646f6e277420706c6179
|
||||||
|
[okay] Challenge 3: Cooking MC's like a pound of bacon
|
||||||
|
[okay] Challenge 4: Now that the party is jumping
|
||||||
|
[okay] Challenge 5: 0b3637272a2b2e63622c2e69692a23693a2a3c6324202d623d...
|
||||||
|
[okay] Challenge 6: I'm back and I'm rin...
|
||||||
|
[okay] Challenge 7: I'm back and I'm rin...
|
||||||
|
[okay] Challenge 8: Cipher 132 [d880619740...] with rating 2398 (average = 2873.8186) is the solution.
|
||||||
|
[okay] Challenge 9: "YELLOW SUBMARINE\u{4}\u{4}\u{4}\u{4}"
|
||||||
|
[okay] Challenge 10: I'm back and I'm
|
||||||
|
[okay] Challenge 11: [10 / 10]
|
||||||
|
[okay] Challenge 12: Rollin' in my 5.0
|
||||||
|
[okay] Challenge 13: role=admin
|
||||||
|
[okay] Challenge 14: Rollin' in my 5.0
|
||||||
|
[okay] Challenge 15: PKCS7 works
|
||||||
|
[okay] Challenge 16: admin=true
|
||||||
|
[okay] Challenge 17: 000003Cooking MC's like a pound of bacon
|
||||||
|
[okay] Challenge 18: Yo, VIP Let's kick it Ice, Ice, baby Ice, Ice, baby
|
||||||
|
[okay] Challenge 19: i have met them at close of day
|
||||||
|
[okay] Challenge 20: I'm rated "R"...this is a warning, ya better void / P
|
||||||
|
[okay] Challenge 21: implemented MT19937
|
||||||
|
[okay] Challenge 22: cracked MT19937 seed
|
||||||
|
[okay] Challenge 23: MT19937 RNG successfully cloned from output
|
||||||
|
[okay] Challenge 24: MT19937 stream cipher implemented and cracked
|
||||||
|
[okay] Challenge 25: recovered AES CTR plaintext via edit
|
||||||
|
[okay] Challenge 26: admin=true
|
||||||
|
[okay] Challenge 27: recovered key successfully
|
||||||
|
[okay] Challenge 28: implemented SHA-1
|
||||||
|
[okay] Challenge 29: extended SHA-1 keyed message successfully
|
||||||
|
[okay] Challenge 30: implemented and extended MD4 successfully
|
||||||
|
[okay] Challenge 31: recovered HMAC-SHA1 via timing attack
|
||||||
|
[okay] Challenge 32: recovered HMAC-SHA1 with slightly less artificial timing leak
|
||||||
|
[okay] Challenge 33: implemented Diffie-Hellman
|
||||||
|
[okay] Challenge 34: implement MITM key-fixing attack on DH
|
||||||
|
[okay] Challenge 35: implement MITM with malicious g attack on DH
|
||||||
|
[okay] Challenge 36: implement secure remote password
|
||||||
|
[okay] Challenge 37: break SRP with zero key
|
||||||
|
[okay] Challenge 38: offline dictionary attack on SRP
|
||||||
|
[okay] Challenge 39: implement RSA
|
||||||
|
[okay] Challenge 40: implement an e=3 RSA Broadcast attack
|
||||||
|
[okay] Challenge 41: implement unpadded message recovery oracle
|
||||||
|
[okay] Challenge 42: Bleichenbacher's e=3 RSA Attack
|
||||||
|
[okay] Challenge 43: DSA key recovery from nonce
|
||||||
|
[okay] Challenge 44: DSA nonce recovery from repeated nonce
|
||||||
|
[okay] Challenge 45: DSA parameter tampering
|
||||||
|
[okay] Challenge 46: RSA parity oracle
|
||||||
|
[okay] Challenge 47: Bleichenbacher's PKCS 1.5 Padding Oracle (Simple Case)
|
||||||
|
[okay] Challenge 48: Bleichenbacher's PKCS 1.5 Padding Oracle (Complex Case)
|
||||||
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,16 @@ import sys
|
|||||||
from math import log, ceil, floor
|
from math import log, ceil, floor
|
||||||
from fractions import Fraction
|
from fractions import Fraction
|
||||||
|
|
||||||
|
# Case for which 2.b does not execute (Simple Case)
|
||||||
e = 3
|
e = 3
|
||||||
d = 49245850836238243386848117224834103046337172957950760944544575720018018155267
|
d = 49245850836238243386848117224834103046337172957950760944544575720018018155267
|
||||||
n = 73868776254357365080272175837251154570062235041494422684546086388903725268033
|
n = 73868776254357365080272175837251154570062235041494422684546086388903725268033
|
||||||
|
|
||||||
|
# Case for which 2.b does execute (Complex Case)
|
||||||
|
e = 3
|
||||||
|
d = 7436226431632429054084263734954342197144411188982219770498201348078527629483
|
||||||
|
n = 11154339647448643581126395602431513296069677965376957820612905826953621832839
|
||||||
|
|
||||||
|
|
||||||
def bytes_needed(n: int) -> int:
|
def bytes_needed(n: int) -> int:
|
||||||
if n == 0:
|
if n == 0:
|
||||||
@@ -63,10 +68,6 @@ def test():
|
|||||||
padded_m = 5300541194335152988749892502228755547482451611528547105226896651010982723
|
padded_m = 5300541194335152988749892502228755547482451611528547105226896651010982723
|
||||||
assert(padded_m == add_padding(m, k))
|
assert(padded_m == add_padding(m, k))
|
||||||
|
|
||||||
c_new = encrypt(padded_m)
|
|
||||||
c = 71554147358804792877798821486588152314859921438911236615156507964101619628630
|
|
||||||
assert(c == c_new)
|
|
||||||
|
|
||||||
assert(m == remove_padding(add_padding(m, k), k))
|
assert(m == remove_padding(add_padding(m, k), k))
|
||||||
print('[tests passed]')
|
print('[tests passed]')
|
||||||
|
|
||||||
@@ -98,7 +99,9 @@ def main():
|
|||||||
s += 1
|
s += 1
|
||||||
elif len(m) > 1:
|
elif len(m) > 1:
|
||||||
# Step 2.b: Searching with more than one interval left.
|
# Step 2.b: Searching with more than one interval left.
|
||||||
raise Exception("Not implemented")
|
s += 1
|
||||||
|
while not oracle(c * pow(s, e, n) % n):
|
||||||
|
s += 1
|
||||||
elif len(m) == 1:
|
elif len(m) == 1:
|
||||||
# Step 2.c: Searching with one interval left.
|
# Step 2.c: Searching with one interval left.
|
||||||
a, b = m[0]
|
a, b = m[0]
|
||||||
@@ -145,8 +148,7 @@ def main():
|
|||||||
assert(m == 5300541194335152988749892502228755547482451611528547105226896651010982723)
|
assert(m == 5300541194335152988749892502228755547482451611528547105226896651010982723)
|
||||||
m = remove_padding(m, k)
|
m = remove_padding(m, k)
|
||||||
assert(m == m_orig)
|
assert(m == m_orig)
|
||||||
print("[okay] Challenge 47: Bleichenbacher's PKCS 1.5 Padding Oracle (Simple Case)")
|
print("[okay] Challenge 48: Bleichenbacher's PKCS 1.5 Padding Oracle (Complex Case)")
|
||||||
|
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|||||||
19
src/main.rs
19
src/main.rs
@@ -1,10 +1,10 @@
|
|||||||
#![warn(clippy::pedantic)]
|
#![warn(clippy::pedantic)]
|
||||||
|
#![warn(clippy::nursery)]
|
||||||
#![allow(clippy::cast_possible_truncation)]
|
#![allow(clippy::cast_possible_truncation)]
|
||||||
#![allow(clippy::cast_precision_loss)]
|
#![allow(clippy::cast_precision_loss)]
|
||||||
#![allow(clippy::items_after_statements)]
|
#![allow(clippy::items_after_statements)]
|
||||||
#![allow(clippy::many_single_char_names)]
|
#![allow(clippy::many_single_char_names)]
|
||||||
#![allow(clippy::module_name_repetitions)]
|
#![allow(clippy::module_name_repetitions)]
|
||||||
#![feature(string_remove_matches)]
|
|
||||||
mod bytes;
|
mod bytes;
|
||||||
mod bytes_base64;
|
mod bytes_base64;
|
||||||
mod cbc;
|
mod cbc;
|
||||||
@@ -28,7 +28,7 @@ mod srp;
|
|||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
const RUN_ALL: bool = false;
|
const RUN_ALL: bool = true;
|
||||||
if RUN_ALL {
|
if RUN_ALL {
|
||||||
set1::challenge1();
|
set1::challenge1();
|
||||||
set1::challenge2();
|
set1::challenge2();
|
||||||
@@ -70,12 +70,13 @@ fn main() {
|
|||||||
set5::challenge38().unwrap_or_else(|| println!("[fail] challenge 38"));
|
set5::challenge38().unwrap_or_else(|| println!("[fail] challenge 38"));
|
||||||
set5::challenge39().unwrap_or_else(|| println!("[fail] challenge 39"));
|
set5::challenge39().unwrap_or_else(|| println!("[fail] challenge 39"));
|
||||||
set5::challenge40().unwrap_or_else(|| println!("[fail] challenge 40"));
|
set5::challenge40().unwrap_or_else(|| println!("[fail] challenge 40"));
|
||||||
|
set6::challenge41().unwrap_or_else(|_| println!("[fail] challenge 41"));
|
||||||
|
set6::challenge42().unwrap_or_else(|_| println!("[fail] challenge 42"));
|
||||||
|
set6::challenge43().unwrap_or_else(|| println!("[fail] challenge 43"));
|
||||||
|
set6::challenge44().unwrap_or_else(|| println!("[fail] challenge 44"));
|
||||||
|
set6::challenge45().unwrap_or_else(|| println!("[fail] challenge 45"));
|
||||||
|
set6::challenge46().unwrap_or_else(|_| println!("[fail] challenge 46"));
|
||||||
|
set6::challenge47().unwrap_or_else(|_| println!("[fail] challenge 47"));
|
||||||
|
set6::challenge48().unwrap_or_else(|_| println!("[fail] challenge 48"));
|
||||||
}
|
}
|
||||||
set6::challenge41().unwrap_or_else(|_| println!("[fail] challenge 41"));
|
|
||||||
set6::challenge42().unwrap_or_else(|_| println!("[fail] challenge 42"));
|
|
||||||
set6::challenge43().unwrap_or_else(|| println!("[fail] challenge 43"));
|
|
||||||
set6::challenge44().unwrap_or_else(|| println!("[fail] challenge 44"));
|
|
||||||
set6::challenge45().unwrap_or_else(|| println!("[fail] challenge 45"));
|
|
||||||
set6::challenge46().unwrap_or_else(|_| println!("[fail] challenge 46"));
|
|
||||||
set6::challenge47().unwrap_or_else(|_| println!("[fail] challenge 47"));
|
|
||||||
}
|
}
|
||||||
|
|||||||
227
src/set6.rs
227
src/set6.rs
@@ -2,10 +2,8 @@ use crate::bytes::Bytes;
|
|||||||
use crate::bytes_base64::BytesBase64;
|
use crate::bytes_base64::BytesBase64;
|
||||||
use crate::dsa;
|
use crate::dsa;
|
||||||
use crate::rsa;
|
use crate::rsa;
|
||||||
use crate::utils::bnclone;
|
use crate::utils::{bn, bnclone, cube_root, div_ceil, div_floor};
|
||||||
use num_bigint::BigUint;
|
use openssl::bn::{BigNum, BigNumContext};
|
||||||
use openssl::bn::BigNum;
|
|
||||||
use openssl::bn::BigNumContext;
|
|
||||||
use openssl::error::ErrorStack;
|
use openssl::error::ErrorStack;
|
||||||
use openssl::sha::sha256;
|
use openssl::sha::sha256;
|
||||||
|
|
||||||
@@ -61,12 +59,6 @@ pub fn challenge42() -> Result<(), ErrorStack> {
|
|||||||
"RSA verify does not work"
|
"RSA verify does not work"
|
||||||
);
|
);
|
||||||
|
|
||||||
fn cube_root(n: &BigNum) -> Result<BigNum, ErrorStack> {
|
|
||||||
let b = BigUint::from_bytes_be(&n.to_vec());
|
|
||||||
let b = b.nth_root(3);
|
|
||||||
BigNum::from_slice(&b.to_bytes_be())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn _cube(n: &BigNum) -> BigNum {
|
fn _cube(n: &BigNum) -> BigNum {
|
||||||
n * &(n * n)
|
n * &(n * n)
|
||||||
}
|
}
|
||||||
@@ -82,7 +74,7 @@ pub fn challenge42() -> Result<(), ErrorStack> {
|
|||||||
// Add one to the cube root to ensure that when the number is
|
// Add one to the cube root to ensure that when the number is
|
||||||
// cubed again it contains the desired signature.
|
// cubed again it contains the desired signature.
|
||||||
let sig_cubed = BigNum::from_slice(&v)?;
|
let sig_cubed = BigNum::from_slice(&v)?;
|
||||||
let mut sig = cube_root(&sig_cubed)?;
|
let mut sig = cube_root(&sig_cubed);
|
||||||
sig.add_word(1)?;
|
sig.add_word(1)?;
|
||||||
|
|
||||||
Ok(sig)
|
Ok(sig)
|
||||||
@@ -192,12 +184,12 @@ pub mod challenge44 {
|
|||||||
let file = std::fs::File::open("data/44.txt").unwrap();
|
let file = std::fs::File::open("data/44.txt").unwrap();
|
||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
let mut lines: Vec<String> = BufReader::new(file).lines().map(|l| l.unwrap()).collect();
|
let mut lines: Vec<String> = BufReader::new(file).lines().map(|l| l.unwrap()).collect();
|
||||||
// each message cosists of four lines: msg, s, r, m (sha1 hash of msg)
|
|
||||||
for line in lines.chunks_mut(4) {
|
for line in lines.chunks_mut(4) {
|
||||||
line[0].remove_matches("msg: ");
|
// each message cosists of four lines: msg, s, r, m (sha1 hash of msg)
|
||||||
line[1].remove_matches("s: ");
|
line[0] = line[0][5..].to_string();
|
||||||
line[2].remove_matches("r: ");
|
line[1] = line[1][3..].to_string();
|
||||||
line[3].remove_matches("m: ");
|
line[2] = line[2][3..].to_string();
|
||||||
|
line[3] = line[3][3..].to_string();
|
||||||
let msg = Bytes::from_utf8(&line[0]);
|
let msg = Bytes::from_utf8(&line[0]);
|
||||||
let s = BigNum::from_dec_str(&line[1]).unwrap();
|
let s = BigNum::from_dec_str(&line[1]).unwrap();
|
||||||
let r = BigNum::from_dec_str(&line[2]).unwrap();
|
let r = BigNum::from_dec_str(&line[2]).unwrap();
|
||||||
@@ -205,7 +197,7 @@ pub mod challenge44 {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
dsa::h(&msg).unwrap(),
|
dsa::h(&msg).unwrap(),
|
||||||
m,
|
m,
|
||||||
"Message hash from data/44.txt does not match"
|
"hashes in data/44.txt do not match"
|
||||||
);
|
);
|
||||||
let d = DsaSignedMsg { msg, r, s, m };
|
let d = DsaSignedMsg { msg, r, s, m };
|
||||||
result.push(d);
|
result.push(d);
|
||||||
@@ -407,20 +399,136 @@ pub fn challenge46() -> Result<(), ErrorStack> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod challenge47 {
|
||||||
|
use crate::rsa;
|
||||||
|
use crate::utils::{bn, bnclone, div_ceil, div_floor};
|
||||||
|
use openssl::bn::{BigNum, BigNumContext};
|
||||||
|
use std::cmp;
|
||||||
|
|
||||||
|
pub fn bleichenbachers_pkcs15_padding_oracle_attack(
|
||||||
|
c: &BigNum,
|
||||||
|
e: &BigNum,
|
||||||
|
d: &BigNum,
|
||||||
|
n: &BigNum,
|
||||||
|
) -> BigNum {
|
||||||
|
#![allow(non_snake_case)]
|
||||||
|
let mut ctx = BigNumContext::new().unwrap();
|
||||||
|
|
||||||
|
let public_key = rsa::RsaPublicKey {
|
||||||
|
e: bnclone(e),
|
||||||
|
n: bnclone(n),
|
||||||
|
};
|
||||||
|
|
||||||
|
let private_key = rsa::RsaPrivateKey {
|
||||||
|
d: bnclone(d),
|
||||||
|
n: bnclone(n),
|
||||||
|
};
|
||||||
|
|
||||||
|
// k is number of bytes of n as specified in Bleichenbacher's paper
|
||||||
|
let n_bytes = n.num_bytes();
|
||||||
|
let k: u32 = n_bytes.try_into().unwrap();
|
||||||
|
|
||||||
|
let oracle = |cipher: &BigNum| -> bool {
|
||||||
|
let cleartext: BigNum = rsa::rsa_decrypt_unpadded(cipher, &private_key).unwrap();
|
||||||
|
let v = cleartext.to_vec_padded(n_bytes).unwrap();
|
||||||
|
return v[0] == 0x0 && v[1] == 0x2;
|
||||||
|
};
|
||||||
|
|
||||||
|
let mul = |s: &BigNum| -> BigNum {
|
||||||
|
let mut ctx = BigNumContext::new().unwrap();
|
||||||
|
let mut f = BigNum::new().unwrap();
|
||||||
|
f.mod_exp(s, &public_key.e, &n, &mut ctx).unwrap();
|
||||||
|
&(c * &f) % n
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut B = BigNum::new().unwrap();
|
||||||
|
// B = 2^(8(k−2))
|
||||||
|
B.exp(&bn(2), &(&bn(8) * &(&bn(k) - &bn(2))), &mut ctx)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Step 1: Blinding (not necessary because already PKCS conforming).
|
||||||
|
let mut i: u32 = 1;
|
||||||
|
let mut s: BigNum = bn(1);
|
||||||
|
let mut m: Vec<(BigNum, BigNum)> = vec![(&bn(2) * &B, &(&bn(3) * &B) - &bn(1))];
|
||||||
|
let solution: BigNum;
|
||||||
|
|
||||||
|
// Step 2: Searching for PKCS conforming messages.
|
||||||
|
loop {
|
||||||
|
if i == 1 {
|
||||||
|
// Step 2.a: Starting the search.
|
||||||
|
s = div_ceil(&n, &(&bn(3) * &B));
|
||||||
|
while !oracle(&mul(&s)) {
|
||||||
|
s.add_word(1).unwrap();
|
||||||
|
}
|
||||||
|
} else if m.len() > 1 {
|
||||||
|
// Step 2.b: Searching with more than one interval left.
|
||||||
|
s.add_word(1).unwrap();
|
||||||
|
while !oracle(&mul(&s)) {
|
||||||
|
s.add_word(1).unwrap();
|
||||||
|
}
|
||||||
|
} else if m.len() == 1 {
|
||||||
|
// Step 2.c: Searching with one interval left.
|
||||||
|
let (a, b) = (bnclone(&m[0].0), bnclone(&m[0].1));
|
||||||
|
let mut found = false;
|
||||||
|
let mut r = div_ceil(&(&bn(2) * &(&(&b * &s) - &(&bn(2) * &(B)))), &n);
|
||||||
|
while !found {
|
||||||
|
let mut s_lower = div_ceil(&(&(&bn(2) * &B) + &(&r * n)), &b);
|
||||||
|
let s_upper = div_ceil(&(&(&bn(3) * &B) + &(&r * n)), &a);
|
||||||
|
while s_lower < s_upper {
|
||||||
|
if oracle(&mul(&s_lower)) {
|
||||||
|
found = true;
|
||||||
|
s = s_lower;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
s_lower.add_word(1).unwrap();
|
||||||
|
}
|
||||||
|
r.add_word(1).unwrap();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic!("step 2 -- no more intervals?");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: Narrowing the set of solutions.
|
||||||
|
let mut m_new: Vec<(BigNum, BigNum)> = vec![];
|
||||||
|
for (a, b) in m {
|
||||||
|
let lower_ceil = div_ceil(&(&(&a * &s) - &(&(&bn(3) * &B) + &bn(1))), &n);
|
||||||
|
let mut upper_ceil = div_ceil(&(&(&b * &s) - &(&bn(2) * &B)), &n);
|
||||||
|
let upper_floor = div_floor(&(&(&b * &s) - &(&bn(2) * &B)), &n);
|
||||||
|
if upper_floor == upper_ceil {
|
||||||
|
upper_ceil.add_word(1).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut r = lower_ceil;
|
||||||
|
while r < upper_ceil {
|
||||||
|
let a_new = cmp::max(bnclone(&a), div_ceil(&(&(&bn(2) * &B) + &(&r * n)), &s));
|
||||||
|
let b_new = cmp::min(
|
||||||
|
bnclone(&b),
|
||||||
|
div_floor(&(&(&(&bn(3) * &B) - &bn(1)) + &(&r * n)), &s),
|
||||||
|
);
|
||||||
|
m_new.push((a_new, b_new));
|
||||||
|
r.add_word(1).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m = m_new;
|
||||||
|
|
||||||
|
// Step 4: Computing the solutions.
|
||||||
|
if m.len() == 1 && m[0].0 == m[0].1 {
|
||||||
|
solution = rsa::rsa_padding_remove_pkcs1(&m[0].0, n_bytes).unwrap();
|
||||||
|
return solution;
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn challenge47() -> Result<(), ErrorStack> {
|
pub fn challenge47() -> Result<(), ErrorStack> {
|
||||||
// Generate a 256 bit keypair (that is, p and q will each be 128 bit primes), [n, e, d].
|
// Generate a 256 bit keypair (that is, p and q will each be 128 bit primes), [n, e, d].
|
||||||
let (public_key, private_key) = rsa::rsa_gen_keys_with_size(128, 128)?;
|
let (public_key, private_key) = rsa::rsa_gen_keys_with_size(128, 128)?;
|
||||||
println!("e={:?}", public_key.e);
|
|
||||||
println!("d={:?}", private_key.d);
|
|
||||||
println!("n={:?}", private_key.n);
|
|
||||||
|
|
||||||
// PKCS1.5-pad a short message, like "kick it, CC", and call it "m". Encrypt to to get "c".
|
// PKCS1.5-pad a short message, like "kick it, CC", and call it "m". Encrypt to to get "c".
|
||||||
let m = Bytes::from_utf8("kick it, CC");
|
let m = BigNum::from_slice(&"kick it, CC".as_bytes())?;
|
||||||
let m = BigNum::from_slice(&m.0)?;
|
let c = rsa::rsa_encrypt(&m, &public_key)?;
|
||||||
let n = bnclone(&public_key.n);
|
let n_bytes = private_key.n.num_bytes();
|
||||||
let n_bytes = n.num_bytes();
|
|
||||||
println!("m={:?}", m);
|
|
||||||
println!("n_bytes={}", n_bytes);
|
|
||||||
|
|
||||||
// Build an oracle function, just like you did in the last exercise, but have it check for
|
// Build an oracle function, just like you did in the last exercise, but have it check for
|
||||||
// plaintext[0] == 0 and plaintext[1] == 2.
|
// plaintext[0] == 0 and plaintext[1] == 2.
|
||||||
@@ -430,24 +538,57 @@ pub fn challenge47() -> Result<(), ErrorStack> {
|
|||||||
return v[0] == 0x0 && v[1] == 0x2;
|
return v[0] == 0x0 && v[1] == 0x2;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Decrypt "c" using your padding oracle.
|
assert!(oracle(&c), "c is padded");
|
||||||
let c_unpadded = rsa::rsa_encrypt_unpadded(&m, &public_key)?;
|
assert!(div_floor(&bn(3), &bn(2)) == bn(1));
|
||||||
let c = rsa::rsa_encrypt(&m, &public_key)?;
|
assert!(div_ceil(&bn(3), &bn(2)) == bn(2));
|
||||||
println!("c={:?}", c);
|
assert!(div_floor(&bn(4), &bn(2)) == bn(2));
|
||||||
|
assert!(div_ceil(&bn(4), &bn(2)) == bn(2));
|
||||||
|
|
||||||
assert!(!oracle(&c_unpadded), "oracle wrongly thinks unpadded message is padded");
|
let solution = challenge47::bleichenbachers_pkcs15_padding_oracle_attack(
|
||||||
assert!(oracle(&c), "oracle wrongly thinks padded message is not padded");
|
&c,
|
||||||
|
&public_key.e,
|
||||||
|
&private_key.d,
|
||||||
|
&private_key.n,
|
||||||
|
);
|
||||||
|
|
||||||
// B = 2^(8(k−2)); k is the length of n in bytes;
|
assert_eq!(m, solution, "Bleichenbacher's attack failed");
|
||||||
// let mut ctx = BigNumContext::new()?;
|
println!("[okay] Challenge 47: Bleichenbacher's PKCS 1.5 Padding Oracle (Simple Case)");
|
||||||
// let mut p = BigNum::new()?;
|
Ok(())
|
||||||
// let k = BigNum::from_u32(n_bytes.try_into().unwrap())?;
|
}
|
||||||
// p.checked_sub(&k, &BigNum::from_u32(2)?);
|
|
||||||
// p = &p * BigNum::from_u32(8);
|
pub fn challenge48() -> Result<(), ErrorStack> {
|
||||||
|
// Set yourself up the way you did in #47, but this time generate a 768 bit modulus.
|
||||||
// b.exp(&BigNum::from_u32(2)?, &BigNum::from_u32(8)? * &(&n_bytes - &BigNum::from_u32(2)), &mut ctx);
|
|
||||||
|
// Use e, d, and n that execute step 2.b. (not all 768 bit modulus do)
|
||||||
|
let public_key = rsa::RsaPublicKey {
|
||||||
println!("[xxxx] Challenge 47: Bleichenbacher's PKCS 1.5 Padding Oracle (Simple Case)");
|
e: BigNum::from_dec_str("3").unwrap(),
|
||||||
|
n: BigNum::from_dec_str(
|
||||||
|
"11154339647448643581126395602431513296069677965376957820612905826953621832839",
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let private_key = rsa::RsaPrivateKey {
|
||||||
|
d: BigNum::from_dec_str(
|
||||||
|
"7436226431632429054084263734954342197144411188982219770498201348078527629483",
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
n: BigNum::from_dec_str(
|
||||||
|
"11154339647448643581126395602431513296069677965376957820612905826953621832839",
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let m = BigNum::from_slice(&"kick it, CC".as_bytes())?;
|
||||||
|
let c = rsa::rsa_encrypt(&m, &public_key)?;
|
||||||
|
let solution = challenge47::bleichenbachers_pkcs15_padding_oracle_attack(
|
||||||
|
&c,
|
||||||
|
&public_key.e,
|
||||||
|
&private_key.d,
|
||||||
|
&private_key.n,
|
||||||
|
);
|
||||||
|
assert_eq!(m, solution, "Bleichenbacher's attack failed");
|
||||||
|
|
||||||
|
println!("[okay] Challenge 48: Bleichenbacher's PKCS 1.5 Padding Oracle (Complex Case)");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
35
src/utils.rs
35
src/utils.rs
@@ -1,5 +1,6 @@
|
|||||||
use crate::{bytes::Bytes, bytes_base64::BytesBase64};
|
use crate::{bytes::Bytes, bytes_base64::BytesBase64};
|
||||||
use openssl::bn::BigNum;
|
use num_bigint::BigUint;
|
||||||
|
use openssl::bn::{BigNum, BigNumContext};
|
||||||
use std::{
|
use std::{
|
||||||
io::{BufRead, BufReader},
|
io::{BufRead, BufReader},
|
||||||
time::{SystemTime, UNIX_EPOCH},
|
time::{SystemTime, UNIX_EPOCH},
|
||||||
@@ -55,3 +56,35 @@ pub fn xor(a: &[u8], b: &[u8]) -> Vec<u8> {
|
|||||||
pub fn bnclone(b: &BigNum) -> BigNum {
|
pub fn bnclone(b: &BigNum) -> BigNum {
|
||||||
BigNum::from_slice(&b.to_vec()).unwrap()
|
BigNum::from_slice(&b.to_vec()).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn cube_root(n: &BigNum) -> BigNum {
|
||||||
|
let b = BigUint::from_bytes_be(&n.to_vec());
|
||||||
|
let b = b.nth_root(3);
|
||||||
|
BigNum::from_slice(&b.to_bytes_be()).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bn(n: u32) -> BigNum {
|
||||||
|
BigNum::from_u32(n).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn div_rem(a: &BigNum, b: &BigNum) -> (BigNum, BigNum) {
|
||||||
|
// kReturns the divider and remainder of a / b.
|
||||||
|
let mut ctx = BigNumContext::new().unwrap();
|
||||||
|
let mut div = BigNum::new().unwrap();
|
||||||
|
let mut rem = BigNum::new().unwrap();
|
||||||
|
div.div_rem(&mut rem, a, b, &mut ctx).unwrap();
|
||||||
|
(div, rem)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn div_floor(a: &BigNum, b: &BigNum) -> BigNum {
|
||||||
|
let (div, _) = div_rem(a, b);
|
||||||
|
div
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn div_ceil(a: &BigNum, b: &BigNum) -> BigNum {
|
||||||
|
let (mut div, rem) = div_rem(a, b);
|
||||||
|
if rem.num_bits() != 0 {
|
||||||
|
div.add_word(1).unwrap();
|
||||||
|
}
|
||||||
|
div
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user