steam-crypto-rs
Steam encryption and cryptographic utilities for Rust.
This crate provides the cryptographic primitives required for Steam protocol communication, including symmetric encryption (AES-256-CBC) and asymmetric key exchange (RSA-OAEP).
Overview
| Feature | Description |
|---|---|
| AES-256-CBC Encryption | Symmetric encryption with random or HMAC-derived IV |
| RSA-OAEP Key Exchange | Session key encryption using Steam's public key |
| Session Key Management | 32-byte key generation and management |
| HMAC-IV Mode | Integrity-verified encryption for TCP connections |
Installation
[]
= "0.1"
The crate is imported as steam_crypto in your code (the package name is steam-crypto-rs because steam-crypto is taken on crates.io).
Usage
Session Key Generation
use SessionKey;
// Generate a new random 32-byte session key
let key = generate;
// Access key components
let aes_key = key.aes_key; // First 16 bytes for AES
let hmac_key = key.hmac_key; // Last 16 bytes for HMAC
// Create from existing bytes
let bytes = ;
let key = from_bytes.unwrap;
Symmetric Encryption (AES-256-CBC)
use ;
let key = generate;
let plaintext = b"Hello, Steam!";
// Encrypt (prepends random 16-byte IV)
let encrypted = encrypt_message?;
// Decrypt (extracts IV from first 16 bytes)
let decrypted = decrypt_message?;
assert_eq!;
HMAC-IV Encryption (TCP Mode)
For TCP connections, Steam uses HMAC-derived IVs for message integrity:
use ;
let key = generate;
let plaintext = b"Secure message";
// Encrypt with HMAC-derived IV
let encrypted = encrypt_with_hmac_iv?;
// Decrypt and verify HMAC integrity
let decrypted = decrypt_with_hmac_iv?;
RSA Key Exchange (TCP Handshake)
During the TCP ChannelEncrypt handshake:
use ;
// Server sends a 16-byte nonce
let nonce: = /* from ChannelEncryptRequest */;
// Generate session key, XOR with nonce, encrypt with Steam's RSA key
let key_pair = generate_session_key?;
// Use the plain key for symmetric encryption
let session_key = key_pair.plain;
// Send encrypted key with CRC to server
let encrypted_key = key_pair.encrypted;
let crc = calculate_key_crc;
API Reference
Types
| Type | Description |
|---|---|
SessionKey |
32-byte symmetric key with AES and HMAC portions |
SessionKeyPair |
Contains both plain and RSA-encrypted session key |
CryptoError |
Error type for cryptographic operations |
Functions
| Function | Description |
|---|---|
encrypt_message(key, plaintext) |
AES-256-CBC encryption with random IV |
decrypt_message(key, ciphertext) |
AES-256-CBC decryption |
encrypt_with_hmac_iv(key, plaintext) |
Encryption with HMAC-derived IV (TCP mode) |
decrypt_with_hmac_iv(key, ciphertext) |
Decryption with HMAC verification (TCP mode) |
generate_session_key(nonce) |
Generate and RSA-encrypt a session key |
calculate_key_crc(encrypted_key) |
CRC32 checksum for handshake verification |
Error Variants
| Variant | Description |
|---|---|
InvalidKeyLength |
Key is not 32 bytes |
EncryptionFailed(String) |
AES or RSA encryption error |
DecryptionFailed(String) |
Decryption or padding error |
InvalidHmac |
HMAC-IV verification failed |
InvalidCrc |
CRC32 checksum mismatch |
Wire Formats
Standard Encryption
┌────────────────┬─────────────────────────────────┐
│ IV (16B) │ AES-256-CBC Encrypted Data │
└────────────────┴─────────────────────────────────┘
HMAC-IV Mode
The IV is derived from the plaintext for integrity verification:
IV = HMAC-SHA1(plaintext, hmac_key)[0..13] || random_bytes[0..3]
- First 13 bytes from HMAC provide integrity
- Last 3 bytes are random for additional entropy
- On decrypt, first 13 bytes of IV are verified against computed HMAC
Session Key Structure
┌─────────────────────────────────────────────────────────────────┐
│ SessionKey (32 bytes) │
├─────────────────────────────────┬───────────────────────────────┤
│ AES Key (16 bytes) │ HMAC Key (16 bytes) │
│ key[0..16] │ key[16..32] │
└─────────────────────────────────┴───────────────────────────────┘
TCP Handshake Flow
┌──────────┐ ┌──────────┐
│ Client │ │ Server │
└────┬─────┘ └────┬─────┘
│ │
│ ◄──── ChannelEncryptRequest ───── │
│ (16-byte nonce) │
│ │
│ ┌─────────────────────┐ │
│ │ 1. Generate 32B key │ │
│ │ 2. XOR key[0..16] │ │
│ │ with nonce │ │
│ │ 3. RSA-OAEP encrypt │ │
│ │ 4. Calculate CRC32 │ │
│ └─────────────────────┘ │
│ │
│ ──── ChannelEncryptResponse ────► │
│ (encrypted_key + CRC32 + zeroes) │
│ │
│ ◄──── ChannelEncryptResult ────── │
│ (EResult::OK) │
│ │
│ ═══ Encrypted Channel Active ═══ │
▼ ▼
Dependencies
| Crate | Purpose |
|---|---|
aes |
AES-256 block cipher |
cbc |
CBC mode encryption |
rand |
Cryptographic random number generation |
rsa |
RSA-OAEP encryption |
sha1 |
SHA1 for HMAC and OAEP |
hmac |
HMAC-SHA1 for IV derivation |
crc32fast |
CRC32 checksum calculation |
thiserror |
Error type derivation |
Security Notes
SessionKeyimplementsDebugto show"SessionKey([REDACTED])"to prevent accidental key logging- All random values are generated using cryptographically secure RNG (
rand::thread_rng()) - The RSA public key is hardcoded (Steam's official 1024-bit key)
Source
This crate is a Rust port of node-steam-crypto.
License
MIT