πŸ” Hybrid Cryptography Diagrams

Hybrid Cryptography Diagrams

1️⃣ Hybrid Cryptography Overview

SENDER Step 1: Generate Session Key session_key = RANDOM(32 bytes) Step 2: Asymmetric Encryption X25519 + Sealed Box enc_session_key = Encrypt(session_key, receiver_public_key) Step 3: Symmetric Encryption ChaCha20-Poly1305 AEAD ciphertext+tag = Encrypt(message, session_key) Step 4: Send to Receiver Secure Transmission mTLS RECEIVER Step 1: Receive Encrypted Message Step 2: Asymmetric Decryption X25519 + Sealed Box session_key = Decrypt(enc_session_key, private_key) Step 3: Verify and Decrypt ChaCha20-Poly1305 AEAD 1. Verify Tag (Authentication) 2. Decrypt(ciphertext, session_key) Step 4: Original Message Recovered βœ“
Asymmetric Encryption (X25519)
Symmetric Encryption (ChaCha20)
Data Transmission

2️⃣ Detailed Encryption Flow (Sender)

Input Plaintext: “Hello World” 1. Generate Session Key session_key = os.urandom(32) 256-bit random key 2. Encrypt Session Key X25519 + Sealed Box sealed_box = SealedBox(public_key) enc_session_key = sealed_box.encrypt() 3. Generate Nonce nonce = os.urandom(12) 96-bit random nonce 4. ChaCha20 Encryption cipher = ChaCha20Poly1305(session_key) result = cipher.encrypt(nonce, plaintext) ↓ result = ciphertext || tag (n bytes) || (16 bytes) πŸ”‘ Key Usage Receiver Public Key (X25519) 32 bytes – for Session Key encryption Session Key (ChaCha20) 32 bytes – for main message encryption Nonce (ChaCha20) 12 bytes – unique for each message πŸ“¦ Final Output (JSON) message_id: “sender-abc-receiver” enc_session_key: “base64…” nonce: “base64…” ciphertext: “base64…” ← includes tag sent_time: “2025-10-15T…”
Important Note: Session Key is used only for one message and then discarded (Forward Secrecy).

3️⃣ Detailed Decryption Flow (Receiver)

πŸ“¬ Receive Encrypted Message enc_session_key: “base64…” nonce: “base64…” ciphertext: “base64…” (includes tag) 1. Decrypt Session Key sealed_box = SealedBox(private_key) session_key = sealed_box.decrypt(enc_session_key) Using X25519 private key 2. Automatic Tag Separation βš™οΈ Happens inside cipher.decrypt(): ciphertext = data[:-16] tag_received = data[-16:] ⚠️ You don’t do anything! Library handles it 3. Verify and Decrypt cipher = ChaCha20Poly1305(session_key) plaintext = cipher.decrypt(nonce, ciphertext_with_tag) βœ“ Tag verified, message is valid πŸ” Tag Verification Details 1. Compute tag_computed from ciphertext tag_computed = Poly1305(ciphertext) 2. Compare with tag_received if tag_computed == tag_received βœ“ Valid Decrypt βœ— Invalid Reject
⚠️ Key Note: In version 2.0, you don’t write any additional code for tag separation. The cryptography library handles everything automatically and securely.

4️⃣ Detailed Ciphertext + Tag Structure

Byte Structure of cipher.encrypt() Output Ciphertext n bytes (length of original message) Tag 16 bytes (fixed) 0 n-1 n n+15 Example: Message “HELLO” (5 bytes) A3 F2 C1 D4 E5 5 bytes ciphertext 7A 8B 3F 2C 16 bytes tag Total Length: 21 bytes (5 + 16)
# Example in Python: plaintext = b”HELLO” # 5 bytes cipher = ChaCha20Poly1305(session_key) result = cipher.encrypt(nonce, plaintext, None) print(f”Plaintext length: {len(plaintext)}”) # 5 print(f”Result length: {len(result)}”) # 21 print(f”Ciphertext: {result[:-16].hex()}”) # first 5 bytes print(f”Tag: {result[-16:].hex()}”) # last 16 bytes

5️⃣ Complete Message Flow from Sender to Receiver

1 Public Key of Receiver from Server 2 Session Key Random 32 bytes Generate 3 Encrypt Session Key with Public Key Message with Session Key 4 Send via TLS to Server 5 Decrypt Session Key with Private Key Message with Session Key
# Complete Encryption Flow in Sender.py # 1. Retrieve receiver’s public key with open(f”keys/{receiver_id}_public.key”, “r”) as f: public_key_b64 = f.read().strip() # 2. Create sealed box for asymmetric encryption public_key = PublicKey(b64decode(public_key_b64)) sealed_box = SealedBox(public_key) # 3. Generate random session key session_key = os.urandom(32) # 256-bit # 4. Encrypt session key with public key enc_session_key = sealed_box.encrypt(session_key) # 5. Generate random nonce nonce = os.urandom(12) # 96-bit # 6. Encrypt message with ChaCha20-Poly1305 cipher = ChaCha20Poly1305(session_key) message_bytes = message[“content”].encode(‘utf-8’) ciphertext_with_tag = cipher.encrypt(nonce, message_bytes, None) # ↑ includes ciphertext + tag (16 bytes) # 7. Construct final message encrypted_message = { “message_id”: f”{sender_id}-{correlation_id}-{receiver_id}”, “receiver_client_id”: receiver_id, “enc_session_key”: b64encode(enc_session_key).decode(‘utf-8’), “nonce”: b64encode(nonce).decode(‘utf-8’), “ciphertext”: b64encode(ciphertext_with_tag).decode(‘utf-8’), “sent_time”: datetime.now(timezone.utc).isoformat() } # 8. Send to server command = f”publish {exchange_name} {routing_key} {json.dumps(encrypted_message)}\n” writer.write(command.encode(‘utf-8’))
# Complete Decryption Flow in Receiver.py # 1. Receive encrypted message message_data = json.loads(message_str) # 2. Decrypt session key with private key enc_session_key = b64decode(message_data[“enc_session_key”]) sealed_box = SealedBox(PRIVATE_KEY) session_key = sealed_box.decrypt(enc_session_key) # 3. Extract nonce and ciphertext+tag nonce = b64decode(message_data[“nonce”]) ciphertext_with_tag = b64decode(message_data[“ciphertext”]) # 4. Decrypt and verify message cipher = ChaCha20Poly1305(session_key) try: plaintext_bytes = cipher.decrypt(nonce, ciphertext_with_tag, None) # ↑ Inside this function: # – Tag is separated # – Tag is verified # – If valid, decrypts plaintext = plaintext_bytes.decode(‘utf-8’) print(f”Message received: {plaintext}”) except InvalidTag: print(“❌ Message tampered or corrupted!”)
Version 1.x vs 2.0 Comparison ❌ Version 1.x (Non-standard) Sender result = cipher.encrypt(…) tag = result[-16:] # ❌ Manual ciphertext = result[:-16] # ❌ Manual Send tag separately in JSON “tag”: “…”, “ciphertext”: “…” ⚠️ Dangerous and unnecessary Receiver tag = b64decode(msg[“tag”]) # ❌ Manual ct = b64decode(msg[“ciphertext”]) combined = ct + tag # ❌ Recombine plaintext = cipher.decrypt(nonce, combined) ⚠️ Complex and error-prone 3 lines of unnecessary code βœ… Version 2.0 (Standard) Sender result = cipher.encrypt(…) # βœ… Includes tag Send together in JSON “ciphertext”: “…” (includes tag) βœ“ Simple and secure βœ“ RFC 7539 standard Receiver data = b64decode(msg[“ciphertext”]) plaintext = cipher.decrypt(nonce, data) # βœ… Library handles tag βœ“ Less code βœ“ Zero error probability βœ“ Constant-time operations β†’
Enhanced data and fully compliant with international standards.