crypto_demo.py

Download
python 348 lines 12.3 KB
  1"""
  2Cryptography Fundamentals Demo
  3==============================
  4
  5Educational demonstration of core cryptographic primitives:
  6- AES-GCM symmetric encryption/decryption
  7- RSA key generation and hybrid encryption
  8- Digital signatures with Ed25519
  9- Key exchange simulation with X25519
 10- Fallback implementations using hashlib/hmac
 11
 12Requirements:
 13    pip install cryptography   (optional - fallback provided)
 14
 15This is a DEFENSIVE/EDUCATIONAL example. All operations demonstrate
 16how to properly use cryptographic libraries for data protection.
 17"""
 18
 19import os
 20import hashlib
 21import hmac
 22import base64
 23import struct
 24import json
 25
 26# ============================================================
 27# Section 1: Check for cryptography library availability
 28# ============================================================
 29
 30try:
 31    from cryptography.hazmat.primitives.ciphers.aead import AESGCM
 32    from cryptography.hazmat.primitives.asymmetric import rsa, padding, ed25519, x25519
 33    from cryptography.hazmat.primitives import hashes, serialization
 34    HAS_CRYPTOGRAPHY = True
 35except ImportError:
 36    HAS_CRYPTOGRAPHY = False
 37
 38print("=" * 65)
 39print("  Cryptography Fundamentals Demo")
 40print("=" * 65)
 41print(f"\n  cryptography library available: {HAS_CRYPTOGRAPHY}")
 42print()
 43
 44
 45# ============================================================
 46# Section 2: AES-GCM Symmetric Encryption
 47# ============================================================
 48
 49print("-" * 65)
 50print("  Section 2: AES-GCM Symmetric Encryption")
 51print("-" * 65)
 52
 53if HAS_CRYPTOGRAPHY:
 54    # Generate a random 256-bit key
 55    key = AESGCM.generate_key(bit_length=256)
 56    print(f"\n  AES-256 Key (hex):  {key.hex()[:32]}...")
 57
 58    # AES-GCM provides both confidentiality and authenticity
 59    aesgcm = AESGCM(key)
 60    nonce = os.urandom(12)  # 96-bit nonce recommended for GCM
 61    plaintext = b"Sensitive data: credit card 4111-1111-1111-1111"
 62    associated_data = b"metadata:user_id=42"
 63
 64    # Encrypt with authenticated associated data (AAD)
 65    ciphertext = aesgcm.encrypt(nonce, plaintext, associated_data)
 66    print(f"  Nonce (hex):        {nonce.hex()}")
 67    print(f"  Plaintext:          {plaintext.decode()}")
 68    print(f"  Ciphertext (hex):   {ciphertext.hex()[:48]}...")
 69    print(f"  Ciphertext length:  {len(ciphertext)} bytes")
 70    print(f"    (plaintext {len(plaintext)} + auth tag 16 = {len(plaintext) + 16})")
 71
 72    # Decrypt
 73    decrypted = aesgcm.decrypt(nonce, ciphertext, associated_data)
 74    print(f"  Decrypted:          {decrypted.decode()}")
 75    print(f"  Match:              {decrypted == plaintext}")
 76
 77    # Demonstrate tamper detection
 78    tampered = bytearray(ciphertext)
 79    tampered[0] ^= 0xFF  # Flip bits in first byte
 80    try:
 81        aesgcm.decrypt(nonce, bytes(tampered), associated_data)
 82        print("  Tamper detection:   FAILED (should not reach here)")
 83    except Exception:
 84        print("  Tamper detection:   Authentication tag mismatch detected!")
 85else:
 86    print("\n  [cryptography not installed - showing fallback]")
 87
 88print()
 89
 90# --- Fallback: XOR stream cipher concept with HMAC auth ---
 91print("  -- Fallback: HMAC-authenticated encryption concept --")
 92
 93def fallback_encrypt(key_bytes: bytes, plaintext: bytes) -> dict:
 94    """
 95    Educational fallback using HMAC for authentication.
 96    NOT production-ready - use AES-GCM via cryptography library.
 97    """
 98    # Derive separate keys for encryption and MAC
 99    enc_key = hashlib.sha256(key_bytes + b"enc").digest()
100    mac_key = hashlib.sha256(key_bytes + b"mac").digest()
101
102    # Simple XOR stream (educational only)
103    iv = os.urandom(16)
104    stream = hashlib.sha256(enc_key + iv).digest()
105    # Extend stream for longer messages
106    extended = stream
107    while len(extended) < len(plaintext):
108        extended += hashlib.sha256(enc_key + extended[-32:]).digest()
109    ct = bytes(p ^ s for p, s in zip(plaintext, extended))
110
111    # HMAC for authentication
112    tag = hmac.new(mac_key, iv + ct, hashlib.sha256).digest()
113    return {"iv": iv, "ciphertext": ct, "tag": tag}
114
115
116def fallback_decrypt(key_bytes: bytes, bundle: dict) -> bytes:
117    """Decrypt and verify the fallback encryption."""
118    enc_key = hashlib.sha256(key_bytes + b"enc").digest()
119    mac_key = hashlib.sha256(key_bytes + b"mac").digest()
120
121    # Verify HMAC first (encrypt-then-MAC pattern)
122    expected_tag = hmac.new(
123        mac_key, bundle["iv"] + bundle["ciphertext"], hashlib.sha256
124    ).digest()
125    if not hmac.compare_digest(expected_tag, bundle["tag"]):
126        raise ValueError("Authentication failed - data tampered!")
127
128    # Decrypt
129    stream = hashlib.sha256(enc_key + bundle["iv"]).digest()
130    extended = stream
131    while len(extended) < len(bundle["ciphertext"]):
132        extended += hashlib.sha256(enc_key + extended[-32:]).digest()
133    return bytes(c ^ s for c, s in zip(bundle["ciphertext"], extended))
134
135
136fb_key = os.urandom(32)
137fb_plain = b"Hello, fallback encryption!"
138fb_bundle = fallback_encrypt(fb_key, fb_plain)
139fb_decrypted = fallback_decrypt(fb_key, fb_bundle)
140print(f"  Plaintext:          {fb_plain.decode()}")
141print(f"  IV (hex):           {fb_bundle['iv'].hex()}")
142print(f"  Ciphertext (hex):   {fb_bundle['ciphertext'].hex()}")
143print(f"  HMAC tag (hex):     {fb_bundle['tag'].hex()[:32]}...")
144print(f"  Decrypted:          {fb_decrypted.decode()}")
145print(f"  Match:              {fb_decrypted == fb_plain}")
146print()
147
148
149# ============================================================
150# Section 3: RSA Key Generation and Hybrid Encryption
151# ============================================================
152
153print("-" * 65)
154print("  Section 3: RSA Key Generation & Hybrid Encryption")
155print("-" * 65)
156
157if HAS_CRYPTOGRAPHY:
158    # Generate RSA key pair
159    private_key = rsa.generate_private_key(
160        public_exponent=65537,
161        key_size=2048,
162    )
163    public_key = private_key.public_key()
164
165    pub_pem = public_key.public_bytes(
166        encoding=serialization.Encoding.PEM,
167        format=serialization.PublicFormat.SubjectPublicKeyInfo,
168    )
169    print(f"\n  RSA-2048 public key generated")
170    print(f"  PEM format (first line): {pub_pem.decode().split(chr(10))[0]}")
171
172    # Hybrid encryption: RSA encrypts an AES key, AES encrypts the data
173    # Step 1: Generate random AES session key
174    session_key = os.urandom(32)
175
176    # Step 2: RSA-encrypt the session key
177    encrypted_session_key = public_key.encrypt(
178        session_key,
179        padding.OAEP(
180            mgf=padding.MGF1(algorithm=hashes.SHA256()),
181            algorithm=hashes.SHA256(),
182            label=None,
183        ),
184    )
185    print(f"  Session key (hex):          {session_key.hex()[:24]}...")
186    print(f"  RSA-encrypted key length:   {len(encrypted_session_key)} bytes")
187
188    # Step 3: AES-GCM encrypt the actual data with the session key
189    aesgcm_hybrid = AESGCM(session_key)
190    hybrid_nonce = os.urandom(12)
191    hybrid_data = b"Large payload encrypted with AES, key protected by RSA"
192    hybrid_ct = aesgcm_hybrid.encrypt(hybrid_nonce, hybrid_data, None)
193    print(f"  Hybrid ciphertext length:   {len(hybrid_ct)} bytes")
194
195    # Receiver: RSA-decrypt session key, then AES-decrypt data
196    recovered_key = private_key.decrypt(
197        encrypted_session_key,
198        padding.OAEP(
199            mgf=padding.MGF1(algorithm=hashes.SHA256()),
200            algorithm=hashes.SHA256(),
201            label=None,
202        ),
203    )
204    recovered_plain = AESGCM(recovered_key).decrypt(hybrid_nonce, hybrid_ct, None)
205    print(f"  Decrypted payload:          {recovered_plain.decode()}")
206    print(f"  Key recovery match:         {recovered_key == session_key}")
207else:
208    print("\n  [cryptography not installed]")
209    print("  RSA hybrid encryption requires the cryptography library.")
210    print("  Install with: pip install cryptography")
211print()
212
213
214# ============================================================
215# Section 4: Digital Signatures with Ed25519
216# ============================================================
217
218print("-" * 65)
219print("  Section 4: Digital Signatures (Ed25519)")
220print("-" * 65)
221
222if HAS_CRYPTOGRAPHY:
223    # Ed25519: fast, secure, small signatures
224    signing_key = ed25519.Ed25519PrivateKey.generate()
225    verify_key = signing_key.public_key()
226
227    message = b"Transfer $1000 from Alice to Bob"
228    signature = signing_key.sign(message)
229
230    print(f"\n  Message:            {message.decode()}")
231    print(f"  Signature (hex):    {signature.hex()[:48]}...")
232    print(f"  Signature length:   {len(signature)} bytes")
233
234    # Verify valid signature
235    try:
236        verify_key.verify(signature, message)
237        print("  Verification:       VALID")
238    except Exception:
239        print("  Verification:       INVALID")
240
241    # Verify tampered message
242    tampered_msg = b"Transfer $9999 from Alice to Bob"
243    try:
244        verify_key.verify(signature, tampered_msg)
245        print("  Tampered verify:    VALID (should not happen!)")
246    except Exception:
247        print("  Tampered verify:    INVALID (tamper detected!)")
248else:
249    print("\n  [cryptography not installed]")
250    print("  Ed25519 requires the cryptography library.")
251print()
252
253# --- Fallback: HMAC-based message authentication ---
254print("  -- Fallback: HMAC-based message authentication --")
255secret = os.urandom(32)
256msg = b"Important message to authenticate"
257mac_tag = hmac.new(secret, msg, hashlib.sha256).digest()
258print(f"  Message:            {msg.decode()}")
259print(f"  HMAC-SHA256 (hex):  {mac_tag.hex()}")
260verify_ok = hmac.compare_digest(
261    mac_tag, hmac.new(secret, msg, hashlib.sha256).digest()
262)
263print(f"  Verification:       {'VALID' if verify_ok else 'INVALID'}")
264print()
265
266
267# ============================================================
268# Section 5: Key Exchange with X25519
269# ============================================================
270
271print("-" * 65)
272print("  Section 5: Key Exchange (X25519 / Diffie-Hellman)")
273print("-" * 65)
274
275if HAS_CRYPTOGRAPHY:
276    # X25519 Diffie-Hellman key exchange
277    alice_private = x25519.X25519PrivateKey.generate()
278    alice_public = alice_private.public_key()
279
280    bob_private = x25519.X25519PrivateKey.generate()
281    bob_public = bob_private.public_key()
282
283    # Both sides compute the same shared secret
284    alice_shared = alice_private.exchange(bob_public)
285    bob_shared = bob_private.exchange(alice_public)
286
287    print(f"\n  Alice's shared secret: {alice_shared.hex()[:32]}...")
288    print(f"  Bob's shared secret:   {bob_shared.hex()[:32]}...")
289    print(f"  Secrets match:         {alice_shared == bob_shared}")
290
291    # Derive an encryption key from the shared secret
292    derived_key = hashlib.sha256(alice_shared).digest()
293    print(f"  Derived AES key:       {derived_key.hex()[:32]}...")
294else:
295    print("\n  [cryptography not installed]")
296    print("  X25519 requires the cryptography library.")
297
298print()
299
300# --- Fallback: Simplified DH concept with small numbers ---
301print("  -- Fallback: Diffie-Hellman concept (small numbers) --")
302# Educational only - real DH uses much larger primes
303p = 23  # Small prime (real: 2048+ bits)
304g = 5   # Generator
305
306alice_secret = 6   # Alice's private key
307bob_secret = 15    # Bob's private key
308
309alice_pub = pow(g, alice_secret, p)  # g^a mod p
310bob_pub = pow(g, bob_secret, p)      # g^b mod p
311
312alice_computed = pow(bob_pub, alice_secret, p)   # (g^b)^a mod p
313bob_computed = pow(alice_pub, bob_secret, p)     # (g^a)^b mod p
314
315print(f"  Parameters:  p={p}, g={g}")
316print(f"  Alice:  secret={alice_secret}, public={alice_pub}")
317print(f"  Bob:    secret={bob_secret}, public={bob_pub}")
318print(f"  Alice computes shared: {alice_computed}")
319print(f"  Bob computes shared:   {bob_computed}")
320print(f"  Secrets match:         {alice_computed == bob_computed}")
321print()
322
323
324# ============================================================
325# Section 6: Summary
326# ============================================================
327
328print("=" * 65)
329print("  Summary of Cryptographic Primitives")
330print("=" * 65)
331print("""
332  Primitive        | Use Case                  | Key Size
333  -----------------+---------------------------+----------
334  AES-GCM          | Symmetric encryption      | 128/256-bit
335  RSA-OAEP         | Asymmetric / hybrid enc   | 2048+ bit
336  Ed25519          | Digital signatures         | 256-bit
337  X25519           | Key exchange (ECDH)        | 256-bit
338  HMAC-SHA256      | Message authentication     | 256-bit
339  PBKDF2/Argon2    | Password hashing           | N/A
340
341  Best Practices:
342  - Never implement your own crypto primitives
343  - Use authenticated encryption (AES-GCM, not AES-CBC alone)
344  - Use hybrid encryption for large data (RSA + AES)
345  - Rotate keys regularly
346  - Use constant-time comparison for MACs/signatures
347""")