Implement RSA OAEP Padding in Python
Implements OAEP-style padding with MGF1 using SHA-256 from the Python standard library for RSA encryption prep.
Python code
36 linesimport os
import hashlib
def mgf1(seed, length, hash_func=hashlib.sha256):
"""MGF1 mask generation function."""
output = b""
counter = 0
while len(output) < length:
c = counter.to_bytes(4, "big")
output += hash_func(seed + c).digest()
counter += 1
return output[:length]
def oaep_pad(message, k=128):
"""Simple OAEP-style padding (mock) for RSA encryption."""
h_len = 32
if len(message) > k - 2 * h_len - 2:
raise ValueError("Message too long")
label_hash = hashlib.sha256(b"").digest()
ps = b"\x00" * (k - len(message) - 2 * h_len - 2)
db = label_hash + ps + b"\x01" + message
seed = os.urandom(h_len)
db_mask = mgf1(seed, k - h_len - 1)
masked_db = bytes(a ^ b for a, b in zip(db, db_mask))
seed_mask = mgf1(masked_db, h_len)
masked_seed = bytes(a ^ b for a, b in zip(seed, seed_mask))
return b"\x00" + masked_seed + masked_db
if __name__ == "__main__":
data = b"small secret"
padded = oaep_pad(data, k=128)
print(f"Padded length: {len(padded)} bytes")
print(f"Padded (hex): {padded.hex()}")
Output
Padded length: 128 bytes
Padded (hex): e.g. 0076a1b2... (random hex string of 256 characters)
How it works
The mgf1 function generates a mask of arbitrary length by repeatedly hashing the seed with an incrementing 4-byte counter, following the MGF1 spec. oaep_pad constructs the data block from a label hash, zero padding, a delimiter, and the message, then applies two rounds of XOR masking with MGF outputs to hide the seed and the data block. The final output is prefixed with a zero byte to mark the OAEP version (EME-OAEP). Using os.urandom for the seed ensures each encryption is randomized, which is critical for security. This implementation is a teaching mock; real RSA encryption should use a library like cryptography to avoid subtle errors.
Common mistakes
- Forgetting to keep the message length under k - 2*h_len - 2, causing a ValueError
- Using a fixed seed instead of `os.urandom`, making padding predictable and insecure
- Not validating that the output length equals the modulus byte length k
- Implementing MGF1 with wrong endianness or counter size (must be 4 bytes big-endian)
Variations
- Use SHA-1 instead of SHA-256 for legacy compatibility (not recommended)
- Use the `cryptography` library's `padding.OAEP` for production-ready RSA encryption
Real-world use cases
- Preparing plaintext for RSA encryption of small secrets like symmetric keys in hybrid encryption schemes.
- Teaching or auditing secure padding schemes in a cryptography course or security review.
- Mocking OAEP behavior in test harnesses or prototypes before integrating a full crypto stack.
Sponsored
More from Auth & security at scale
- ACME LetsEncrypt Mock Challenge Server in Python medium
- AES GCM encryption and decryption in Python medium
- Build a Mock OIDC Userinfo Endpoint in Python with Flask easy
- ChaCha20-Poly1305 mock in Python medium
- ECDH key agreement in Python with cryptography medium
- Enforce TLS 1.2 Minimum in Python easy
Keep learning
Related tutorials and quizzes for this topic.