Implement Bleichenbacher PKCS padding oracle in Python

This commit is contained in:
2023-02-04 17:41:10 -05:00
parent edbe2144ae
commit fcb67889b9
4 changed files with 167 additions and 17 deletions

View File

@@ -1,3 +1,3 @@
# cryptopals
My solutions to the cryptopals challenges.
My solutions to the [cryptopals](https://cryptopals.com/) challenges.

140
data/set6c47.py Normal file
View File

@@ -0,0 +1,140 @@
from math import log, ceil, floor
e = 3
d = 49245850836238243386848117224834103046337172957950760944544575720018018155267
n = 73868776254357365080272175837251154570062235041494422684546086388903725268033
def bytes_needed(n: int) -> int:
if n == 0:
return 1
return int(log(n, 256)) + 1
def add_padding(m: int, k: int) -> int:
padding_size = 11
from_len = bytes_needed(m)
assert(from_len + padding_size <= k)
padding_str_len = k - 3 - from_len
r = [0x0, 0x2]
for _ in range(2, padding_str_len + 2):
r.append(0xff)
r.append(0x0)
r += m.to_bytes(from_len, 'big')
r = int.from_bytes(r, byteorder='big')
return r
def remove_padding(m: int, k: int) -> int:
v = m.to_bytes(k, 'big')
assert(v[0] == 0x0 and v[1] == 0x2)
i = 2
while v[i] == 0xff:
i = i + 1
assert(v[i] == 0x0)
return int.from_bytes(v[i + 1:], 'big')
def oracle(c: int) -> bool:
m = decrypt(c)
v = m.to_bytes(bytes_needed(n), 'big')
return v[0] == 0x0 and v[1] == 0x2
def encrypt(m: int) -> int:
return pow(m, e, n)
def decrypt(c: int) -> int:
return pow(c, d, n)
def test():
m = int.from_bytes(b"kick it, CC", byteorder='big')
m = 129852745126415640677073731
assert(m == 129852745126415640677073731)
k = bytes_needed(n)
assert(k == 32)
padded_m = 5300541194335152988749892502228755547482451611528547105226896651010982723
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))
print('[tests passed]')
def main():
test()
k = bytes_needed(n)
m = int.from_bytes(b"kick it, CC", byteorder='big')
c = encrypt(add_padding(m, k))
assert(m == remove_padding(decrypt(c), k))
B = pow(2, 8 * (k - 2))
# Step 1: Blinding (not necessary because already PKCS conforming).
i = 1
s = 1
m = [(2 * B, 3 * B - 1)]
assert(oracle(c))
# Step 2: Searching for PKCS conforming messages.
while True:
print(f"{i=}\n{s=}\n{m=}")
if i == 1:
# Step 2.a: Starting the search.
s = ceil(n / (3 * B))
while not oracle(c * pow(s, e, n) % n):
s += 1
elif len(m) > 1:
# Step 2.b: Searching with more than one interval left.
raise Exception("Not implemented")
elif len(m) == 1:
# Step 2.c: Searching with one interval left.
a, b = m[0]
found = False
r = ceil(2 * ((b * s - 2 * B) / n))
while not found:
s_lower = ceil((2 * B + r * n) / b)
s_upper = ceil((3 * B + r * n) / a)
assert(s_lower < s_upper + 1)
for s in range(s_lower, s_upper):
if oracle(c * pow(s, e, n) % n):
found = True
break
r += 1
else:
raise Exception("No intervals?")
# Step 3: Narrowing the set of solutions.
m_new = []
for (a, b) in m:
lower = ceil((a * s - 3 * B + 1) / n)
upper = ceil((b * s - 2 * B) / n)
for r in range(lower, upper):
a_new = max(a, ceil((2 * B + r * n) / s))
b_new = min(b, floor((3 * B - 1 + r * n) / s))
m_new.append((a_new, b_new))
m = m_new
# Step 4: Computing the solutions.
if len(m) == 1 and m[0][0] == m[0][1]:
m = a * pow(s0, -1, n) % n
break
i = i + 1
print(m)
main()

View File

@@ -62,15 +62,15 @@ fn main() {
set4::challenge30();
set4::challenge31();
set4::challenge32();
set5::challenge33();
set5::challenge34();
set5::challenge35();
set5::challenge36().unwrap_or_else(|| println!("[fail] challenge 36"));
set5::challenge37().unwrap_or_else(|| println!("[fail] challenge 37"));
set5::challenge38().unwrap_or_else(|| println!("[fail] challenge 38"));
set5::challenge39().unwrap_or_else(|| println!("[fail] challenge 39"));
set5::challenge40().unwrap_or_else(|| println!("[fail] challenge 40"));
}
set5::challenge33();
set5::challenge34();
set5::challenge35();
set5::challenge36().unwrap_or_else(|| println!("[fail] challenge 36"));
set5::challenge37().unwrap_or_else(|| println!("[fail] challenge 37"));
set5::challenge38().unwrap_or_else(|| println!("[fail] challenge 38"));
set5::challenge39().unwrap_or_else(|| println!("[fail] challenge 39"));
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"));

View File

@@ -410,12 +410,17 @@ pub fn challenge46() -> 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].
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".
let m = Bytes::from_utf8("kick it, CC");
let m = BigNum::from_slice(&m.0)?;
let n = bnclone(&public_key.n);
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
// plaintext[0] == 0 and plaintext[1] == 2.
@@ -428,15 +433,20 @@ pub fn challenge47() -> Result<(), ErrorStack> {
// Decrypt "c" using your padding oracle.
let c_unpadded = rsa::rsa_encrypt_unpadded(&m, &public_key)?;
let c = rsa::rsa_encrypt(&m, &public_key)?;
println!("c={:?}", c);
assert!(!oracle(&c_unpadded), "oracle wrongly thinks unpadded message is padded");
assert!(oracle(&c), "oracle wrongly thinks padded message is not padded");
// B = 2^(8(k2)); k is the length of n in bytes;
// let mut ctx = BigNumContext::new()?;
// let mut p = BigNum::new()?;
// let k = BigNum::from_u32(n_bytes.try_into().unwrap())?;
// p.checked_sub(&k, &BigNum::from_u32(2)?);
// p = &p * BigNum::from_u32(8);
// b.exp(&BigNum::from_u32(2)?, &BigNum::from_u32(8)? * &(&n_bytes - &BigNum::from_u32(2)), &mut ctx);
assert!(
!oracle(&c_unpadded),
"oracle wrongly thinks unpadded message is padded"
);
assert!(
oracle(&c),
"oracle wrongly thinks padded message is not padded"
);
println!("[xxxx] Challenge 47: Bleichenbacher's PKCS 1.5 Padding Oracle (Simple Case)");
Ok(())