Implement RSA OAEP Padding in Python

Implements OAEP-style padding with MGF1 using SHA-256 from the Python standard library for RSA encryption prep.

Medium Python 3.9+ Aug 9, 2026 Auth & security at scale 12 views 0 copies

Python code

36 lines
Python 3.9+
import 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

stdout
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

  1. Use SHA-1 instead of SHA-256 for legacy compatibility (not recommended)
  2. 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

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Auth & security at scale

Related tutorials and quizzes for this topic.