CipherChat
SM4-CBC encrypted two-way chat over TCP with HMAC-SHA256 integrity verification.
A cryptography mini-project exploring authenticated encryption with the Chinese national standard SM4 cipher.
Protocol
Packet Format
Every message is framed as a JSON object with a 4-byte big-endian length prefix. Binary fields are base64-encoded for transport.
| Field | Type | Description |
|---|---|---|
| iv | bytes (16) | Random initialisation vector (CBC) |
| ciphertext | bytes (var.) | SM4-CBC encrypted payload |
| tag | bytes (32) | HMAC-SHA256 over IV + ciphertext |
| username | string | Display name of the sender |
Wire Layout
┌─────────────────────────────────────────────┐
│ 4-byte length (big-endian) │
├─────────────────────────────────────────────┤
│ JSON payload │
│ { │
│ "iv": "<base64>", │
│ "ciphertext": "<base64>", │
│ "tag": "<base64>", │
│ "username": "<string>" │
│ } │
└─────────────────────────────────────────────┘Cryptography
Key Derivation
A shared password is stretched into two keys using PBKDF2-HMAC-SHA256 with 100,000 iterations:
sm4_key = PBKDF2(password, salt, iterations=100_000)[:16]
hmac_key = PBKDF2(password, salt, iterations=100_000)[16:48]Encryption
Each message is encrypted with SM4 in CBC mode. Every transmission uses a fresh random 16-byte IV.
iv = os.urandom(16)
padded = pkcs7_pad(plaintext, block_size=16)
ciphertext = SM4_CBC_encrypt(sm4_key, iv, padded)Authentication
An HMAC-SHA256 tag authenticates the IV and ciphertext. The receiver verifies the tag before attempting decryption.
tag = HMAC_SHA256(hmac_key, iv + ciphertext)
# On receive:
assert constant_time_compare(tag, expected)
plaintext = SM4_CBC_decrypt(sm4_key, iv, ciphertext)
plaintext = pkcs7_unpad(plaintext)Usage
Setup
pip install -r requirements.txtTerminal 1 — Sender
Terminal 2 — Receiver
/quit — Type this at any time to end the session gracefully.
Security Notes
✓ Authenticated Encryption
HMAC-SHA256 is computed over IV + ciphertext. The receiver verifies the MAC before decryption, preventing chosen-ciphertext attacks.
✓ Random IV Per Message
A fresh 16-byte IV from os.urandom() ensures the same plaintext never produces the same ciphertext.
⚠ Limitations
This is an educational prototype. Notable caveats: fixed PBKDF2 salt (should be per-user random), no forward secrecy, basic plaintext password exchange, and no protection against replay attacks.
🔬 Cipher Details
SM4 is the Chinese national standard for symmetric encryption (GB/T 32907-2016), using a 128-bit key and 128-bit block size in a 32-round Feistel network.