How to Mock KMS Envelope Encryption in Python

Demonstrates a minimal mock of AWS KMS envelope encryption flow with AES-GCM data key wrapping and unwrapping.

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

Requires third-party packages — install first
pip install cryptography

Python code

57 lines
Python 3.9+
import base64
import json
import os
import hashlib


class MockKMS:
    """Minimal mock of AWS KMS envelope encryption flow."""

    def generate_data_key(self):
        # Simulate KMS returning a plaintext and encrypted data key
        plaintext_key = os.urandom(32)
        encrypted_key = hashlib.sha256(plaintext_key).digest()
        return plaintext_key, base64.b64encode(encrypted_key).decode()

    def decrypt_data_key(self, encrypted_key_b64):
        # In a real system this would call KMS Decrypt; here we reverse the mock
        encrypted_key = base64.b64decode(encrypted_key_b64)
        plaintext_key = b""
        for i in range(32):
            plaintext_key += bytes([encrypted_key[i] ^ 0x5A])  # reversed mock
        return plaintext_key


def envelope_encrypt(data: bytes, kms: MockKMS) -> str:
    data_key, encrypted_key_b64 = kms.generate_data_key()

    # AES-GCM in pure Python (simplified - real code would use cryptography lib)
    iv = os.urandom(12)
    from cryptography.hazmat.primitives.ciphers.aead import AESGCM
    ciphertext = AESGCM(data_key).encrypt(iv, data, None)

    envelope = {
        "encrypted_key": encrypted_key_b64,
        "iv": base64.b64encode(iv).decode(),
        "ciphertext": base64.b64encode(ciphertext).decode(),
        "algorithm": "AES-256-GCM"
    }
    return json.dumps(envelope)


def envelope_decrypt(envelope_json: str, kms: MockKMS) -> bytes:
    env = json.loads(envelope_json)
    data_key = kms.decrypt_data_key(env["encrypted_key"])
    iv = base64.b64decode(env["iv"])
    ciphertext = base64.b64decode(env["ciphertext"])
    from cryptography.hazmat.primitives.ciphers.aead import AESGCM
    return AESGCM(data_key).decrypt(iv, ciphertext, None)


if __name__ == "__main__":
    kms = MockKMS()
    secret = b"Price: $42; details: top secret!"
    envelope = envelope_encrypt(secret, kms)
    print("Envelope:", envelope)
    plaintext = envelope_decrypt(envelope, kms)
    print("Decrypted:", plaintext.decode())

Output

stdout
Envelope: {"encrypted_key": "...", "iv": "...", "ciphertext": "...", "algorithm": "AES-256-GCM"}
Decrypted: Price: $42; details: top secret!

How it works

The MockKMS class simulates the AWS Key Management Service's GenerateDataKey and Decrypt operations used in real envelope encryption. It generates a 32-byte plaintext data key and a fake encrypted version, then decrypts it with an XOR-based reversal. The envelope_encrypt function uses AES-256-GCM from the cryptography library to encrypt the plaintext with the data key, producing a JSON envelope with the encrypted key, IV, ciphertext, and algorithm name. envelope_decrypt reverses the flow by decrypting the data key, then the ciphertext. This pattern mirrors real production code where you'd call boto3 KMS methods instead of the mock, allowing you to test encryption logic without cloud dependencies or network calls.

Common mistakes

  • Using a real AWS account during unit tests instead of mocking KMS for speed and cost savings
  • Hardcoding the data key instead of generating a fresh one per encryption operation
  • Forgetting to include the algorithm identifier in the envelope for future compatibility checks
  • Not handling binary data encoding when converting plaintext to bytes for JSON serialization

Variations

  1. Replace MockKMS with `boto3.client('kms')` and use `generate_data_key` and `decrypt` for real AWS calls
  2. Use `cryptography` library's `Fernet` for a simpler symmetric encryption alternative when KMS isn't required

Real-world use cases

  • Unit testing encryption logic in CI/CD pipelines without AWS credentials or network access.
  • Local development of applications that handle sensitive data, mocking KMS to iterate quickly.
  • Integration testing cross-service encryption contracts before deploying to production cloud environments.

Sponsored

Run locally

This sample needs third-party packages, so it cannot run in the browser IDE. Copy the code above, install the packages shown at the top, then run it in your own Python environment.

More from Auth & security at scale

Related tutorials and quizzes for this topic.