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.
pip install cryptography
Python code
57 linesimport 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
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
- Replace MockKMS with `boto3.client('kms')` and use `generate_data_key` and `decrypt` for real AWS calls
- 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
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.