From-scratch RSAES-OAEP implementation in Python, with automatic key generation, OAEP padding, SHA-256, MGF1, encryption, and decryption through an interactive command-line interface.
This repository implements the main RSAES-OAEP flow: RSA with OAEP padding before applying the RSA encryption operation.
The project started with a textbook RSA version that encrypted ASCII characters directly. That version made it possible to demonstrate vulnerabilities such as determinism, dictionary attacks, and algebraic manipulation of ciphertexts.
The current implementation replaces that approach with a byte-based, block-based OAEP flow.
Warning
This project is not intended for production cryptography. Its purpose is to show how the RSAES-OAEP flow is built and which textbook RSA problems it addresses.
This repository contains:
- An interactive CLI for key generation, encryption, and decryption.
- RSA implementation with automatically generated 1024-bit keys.
- OAEP implementation with SHA-256 and MGF1.
- Large prime generation using Miller-Rabin.
- Explicit byte/integer conversion to connect OAEP with RSA.
- Documentation about the previous textbook RSA vulnerability.
- Documentation about the changes made to implement RSAES-OAEP.
Note
SHA-256 is provided by Python's standard library through hashlib. The OAEP random seed is also generated with Python's standard cryptographic randomness through secrets.token_bytes(...), instead of a from-scratch random generator. The project implements the RSAES-OAEP flow, MGF1 usage, block handling, and RSA operations; implementing SHA-256 or a cryptographic random generator itself is outside this repository's focus.
The current encryption flow is:
text -> UTF-8 bytes -> blocks -> OAEP encode -> integer -> RSA encrypt
The decryption flow is:
RSA decrypt -> integer -> OAEP decode -> bytes -> UTF-8 text
Main implementation decisions:
- OAEP as the padding scheme.
- SHA-256 as the hash function.
- MGF1 as the mask generation function.
e = 65537as the public exponent.- 1024 bits as the default key size.
- UTF-8 messages instead of encrypting ASCII characters one by one.
- Block encryption, because OAEP limits the maximum plaintext size per block.
With 1024-bit RSA and SHA-256:
key size = 128 bytes
hash length = 32 bytes
max OAEP block size = 128 - 2*32 - 2 = 62 bytes
Messages larger than 62 bytes are split into multiple RSAES-OAEP blocks.
- Python 3.12+
- uv optional, but recommended
Using uv:
uv run python main.pyWithout uv:
python3 main.pyNote
No external dependencies are required.
Technical documentation is available under docs/:
- Textbook RSA Vulnerability: explains the problems in the previous version without OAEP.
- RSAES-OAEP Implementation: summarizes what changed when moving from textbook RSA to RSAES-OAEP and why.