CipherChatGitHub ↗

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.

SM4-CBCHMAC-SHA256PBKDF2TCP SocketsPython

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.

FieldTypeDescription
ivbytes (16)Random initialisation vector (CBC)
ciphertextbytes (var.)SM4-CBC encrypted payload
tagbytes (32)HMAC-SHA256 over IV + ciphertext
usernamestringDisplay 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.txt

Terminal 1 — Sender

1
Run python main.py and select sender mode.
2
Accept the default host (127.0.0.1) and port (5000).
3
Enter a username.
4
Copy the auto-generated password — you'll share it with the receiver.

Terminal 2 — Receiver

1
Run python main.py and select receiver mode.
2
Use the same host and port as the sender.
3
Enter a username.
4
Paste the same password so both sides derive matching keys.

/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.