Skip to content

Commit 3d36f40

Browse files
authored
Merge pull request #1 from AriajSarkar/dev
Feat: v2
2 parents b262a20 + 6c60d47 commit 3d36f40

23 files changed

Lines changed: 3248 additions & 86 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ fuzz/artifacts/
1717
*.swo
1818
*~
1919
.DS_Store
20+
copilot-instructions.md
2021

2122
# Testing
2223
*.profraw

CHANGELOG.md

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,90 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
- **Streaming AEAD Encryption** - High-performance streaming encryption for large files
12+
- `Aes256GcmStreamEncryptor` and `Aes256GcmStreamDecryptor` for AES-256-GCM
13+
- `ChaCha20Poly1305StreamEncryptor` and `ChaCha20Poly1305StreamDecryptor`
14+
- Uses RustCrypto's `aead::stream` module (STREAM construction from RFC)
15+
- Implements EncryptorBE32/DecryptorBE32 with proper nonce derivation
16+
- Auto-generates 7-byte nonces (12-byte AEAD nonce - 5 bytes for counter/flag)
17+
- Methods: `new()`, `nonce()`, `encrypt_next()`, `encrypt_last()`, `from_nonce()`, `decrypt_next()`, `decrypt_last()`
18+
- Proper ownership semantics (encrypt_last/decrypt_last consume self)
19+
- Each chunk independently authenticated with nonce-reuse resistance
20+
- Maximum 2^32 chunks per stream
21+
- Default chunk size: 64 KB, max chunk size: 1 MB
22+
- 5 comprehensive tests covering roundtrip, large data, authentication failures, and edge cases
23+
- Total test count increased to 108 tests (from 103)
24+
25+
- **Comprehensive RFC/NIST Test Vectors** - 13 new test cases covering 28 test vectors from 8 standards
26+
- NIST CAVP vectors for AES-128/256-GCM (3 test cases)
27+
- RFC 7539 vectors for ChaCha20-Poly1305 (2 test cases)
28+
- RFC 4634 vectors for SHA-256/512 (7 test vectors)
29+
- RFC 4231 vectors for HMAC-SHA256/512 (10 test vectors)
30+
- RFC 6070 vectors for PBKDF2 (4 test cases, adapted for security minimums)
31+
- RFC 5869 vectors for HKDF (2 test cases)
32+
33+
- **Constant-Time Operations Audit** - Complete review and documentation
34+
- Added `subtle` crate v2.6 for constant-time comparisons
35+
- Updated `constant_time_eq()` to use industry-standard `subtle::ConstantTimeEq`
36+
- Created comprehensive `CONSTANT_TIME_AUDIT.md` documentation
37+
- Verified all MAC and AEAD operations use constant-time tag verification
38+
- Added security notes to MAC verification functions
39+
- Documented guidelines for when to use constant-time comparisons
40+
41+
- **Serde Serialization Support** (behind `serde-support` feature flag)
42+
- Full serialization/deserialization for `Ciphertext` (encrypted data)
43+
- Serialization for Ed25519 public keys and signatures
44+
- Support for JSON, TOML, bincode, and all serde formats
45+
- Comprehensive example demonstrating usage patterns
46+
- Binary serialization ~55% more compact than JSON
47+
- Perfect for storing encrypted data and cryptographic keys
48+
49+
- **Dependency Updates** - Updated 21 packages to latest stable versions
50+
- RustCrypto: aes-gcm 0.10.3, chacha20poly1305 0.10.1, pbkdf2 0.12.2, argon2 0.5.3
51+
- Asymmetric: ed25519-dalek 2.2.0 (from 2.1), x25519-dalek 2.0.1
52+
- Security: zeroize 1.8.2, subtle 2.6.1 (new), pkcs8 0.10.2 (new)
53+
- Encoding: base64 0.22.1
54+
- Dev deps: proptest 1.9.0 (from 1.5.0), hex-literal 1.1.0, serde_json 1.0.141, serde_bytes 0.11.19
55+
- All tests passing (193 with all features: 103 unit + 6 integration + 7 interop + 13 RFC vectors + 64 doc tests)
56+
- Avoided breaking changes: getrandom stays on 0.2.x, rand_core on 0.6.x for RustCrypto compatibility
57+
58+
- **PKCS#8 Key Import/Export** (RFC 5208, RFC 5958, RFC 8410)
59+
- Full PKCS#8 DER/PEM support for Ed25519 and X25519 private keys
60+
- SPKI (SubjectPublicKeyInfo) DER/PEM support for public keys
61+
- Standards-compliant implementation using `pkcs8` crate v0.10.2
62+
- OpenSSL-compatible key formats
63+
- Algorithm OIDs: Ed25519 (1.3.101.112), X25519 (1.3.101.110)
64+
- 8 new methods: `to_pkcs8_der/pem`, `from_pkcs8_der/pem` for keypairs
65+
- 8 new methods: `to_public_key_der/pem`, `from_public_key_der/pem` for public keys
66+
- Comprehensive example in `examples/pkcs8_example.rs`
67+
- All doc tests passing with roundtrip verification
68+
1069
### Planned
11-
- RSA encryption and signatures (optional feature)
12-
- PKCS#8 key import/export
1370
- Streaming encryption API
1471
- SHA-3 and BLAKE2 support
72+
- Property-based testing with proptest
73+
74+
## [0.2.0] - 2025-10-29
75+
76+
### Added
77+
- **RSA Support** (behind `rsa-support` feature flag)
78+
- RSA-OAEP encryption with SHA-256
79+
- RSA-PSS signatures with SHA-256
80+
- 2048-bit and 4096-bit key generation
81+
- PEM/DER import/export (PKCS#8)
82+
- Complete test suite with RFC test vectors
83+
- Benchmarks for RSA operations
84+
- Example code demonstrating usage
85+
86+
### Security
87+
- ⚠️ **IMPORTANT**: RSA implementation has known vulnerability (RUSTSEC-2023-0071 - Marvin timing attack)
88+
- Added prominent security warnings in documentation
89+
- Recommend Ed25519 for signatures and X25519+AEAD for encryption unless RSA is required for compatibility
90+
91+
### Changed
92+
- Excluded development files from published crate (`.github/`, `fuzz/`, `TODOs.md`, etc.)
93+
- Updated documentation to include RSA examples and security warnings
1594

1695
## [0.1.0] - 2025-10-28
1796

Cargo.toml

Lines changed: 56 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "crabgraph"
3-
version = "0.1.1"
3+
version = "0.2.0"
44
authors = ["Raj Sarkar <ariajsarkar@gmail.com>"]
55
edition = "2021"
66
rust-version = "1.70"
@@ -12,55 +12,73 @@ documentation = "https://docs.rs/crabgraph"
1212
license = "MIT OR Apache-2.0"
1313
keywords = ["cryptography", "crypto", "encryption", "security", "authentication"]
1414
categories = ["cryptography", "authentication", "encoding", "no-std"]
15+
exclude = [
16+
".github/",
17+
".gitignore",
18+
"fuzz/",
19+
"target/",
20+
"TODOs.md",
21+
"rustfmt.toml",
22+
"Cargo.lock",
23+
"docs/",
24+
]
1525

1626
[dependencies]
17-
# AEAD ciphers
18-
aes-gcm = { version = "0.10", features = ["std"] }
19-
chacha20poly1305 = { version = "0.10", features = ["std"] }
27+
# AEAD ciphers - RustCrypto audited implementations
28+
aes-gcm = { version = "0.10.3", features = ["std", "stream"] }
29+
chacha20poly1305 = { version = "0.10.1", features = ["std", "stream"] }
2030

21-
# Key derivation
22-
pbkdf2 = { version = "0.12", features = ["simple"] }
23-
argon2 = { version = "0.5", features = ["std"] }
24-
hkdf = "0.12"
31+
# Key derivation functions
32+
pbkdf2 = { version = "0.12.2", features = ["simple"] }
33+
argon2 = { version = "0.5.3", features = ["std"] }
34+
hkdf = "0.12.4"
2535

26-
# Hashing
27-
sha2 = { version = "0.10", features = ["oid"] }
28-
sha3 = { version = "0.10", optional = true }
29-
blake2 = { version = "0.10", optional = true }
36+
# Hashing algorithms
37+
sha2 = { version = "0.10.9", features = ["oid"] }
38+
sha3 = { version = "0.10.8", optional = true }
39+
blake2 = { version = "0.10.6", optional = true }
3040

31-
# MAC
32-
hmac = "0.12"
41+
# Message Authentication Codes
42+
hmac = "0.12.1"
3343

3444
# Asymmetric cryptography
35-
ed25519-dalek = { version = "2.1", features = ["rand_core"] }
36-
x25519-dalek = { version = "2.0", features = ["static_secrets"] }
37-
rsa = { version = "0.9", optional = true, features = ["sha2"] }
45+
ed25519-dalek = { version = "2.2", features = ["rand_core", "pkcs8", "pem"] }
46+
x25519-dalek = { version = "2.0.1", features = ["static_secrets"] }
47+
rsa = { version = "0.9.8", optional = true, features = ["sha2"] }
48+
pkcs8 = { version = "0.10.2", features = ["pem", "alloc"] }
3849

3950
# Random number generation
40-
getrandom = "0.2"
41-
rand_core = "0.6"
51+
getrandom = "0.2.16"
52+
rand_core = "0.6.4"
4253

4354
# Secure memory handling
44-
zeroize = { version = "1.7", features = ["derive"] }
55+
zeroize = { version = "1.8.2", features = ["derive"] }
4556

46-
# Encoding
47-
base64 = "0.21"
48-
hex = "0.4"
57+
# Constant-time operations
58+
subtle = { version = "2.6.1", default-features = false }
59+
60+
# Encoding utilities
61+
base64 = "0.22.1"
62+
hex = "0.4.3"
4963

5064
# Error handling
51-
thiserror = "1.0"
65+
thiserror = "1.0.69"
5266

5367
# Serialization (optional)
54-
serde = { version = "1.0", features = ["derive"], optional = true }
68+
serde = { version = "1.0.215", features = ["derive"], optional = true }
5569

5670
# Zero-copy optimizations (optional)
57-
bytes = { version = "1.5", optional = true }
71+
bytes = { version = "1.9.0", optional = true }
5872

5973
[dev-dependencies]
60-
criterion = { version = "0.5", features = ["html_reports"] }
61-
proptest = "1.4"
62-
quickcheck = "1.0"
63-
hex-literal = "0.4"
74+
criterion = { version = "0.7.0", features = ["html_reports"] }
75+
proptest = "1.9.0"
76+
quickcheck = "1.0.3"
77+
hex-literal = "1.1.0"
78+
serde_json = "1.0.141"
79+
bincode = "1.3.3"
80+
toml = "0.8.23"
81+
serde_bytes = "0.11.19"
6482

6583
[features]
6684
default = ["std"]
@@ -73,6 +91,14 @@ serde-support = ["serde"]
7391
zero-copy = ["bytes"]
7492
wasm = ["getrandom/js"]
7593

94+
[[example]]
95+
name = "rsa_example"
96+
required-features = ["rsa-support"]
97+
98+
[[example]]
99+
name = "serde_example"
100+
required-features = ["serde-support"]
101+
76102
[[bench]]
77103
name = "aead_bench"
78104
harness = false

README.md

Lines changed: 87 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@ For security issues, please see [SECURITY.md](SECURITY.md).
1919
## ✨ Features
2020

2121
- 🔒 **Authenticated Encryption (AEAD)**: AES-GCM, ChaCha20-Poly1305
22-
- 🔑 **Key Derivation**: PBKDF2, Argon2, HKDF
23-
- ✍️ **Digital Signatures**: Ed25519
22+
-**Streaming Encryption**: Process large files chunk-by-chunk with STREAM construction
23+
- �🔑 **Key Derivation**: PBKDF2, Argon2, HKDF
24+
- ✍️ **Digital Signatures**: Ed25519, (optional: RSA-PSS)
2425
- 🤝 **Key Exchange**: X25519 (Elliptic Curve Diffie-Hellman)
2526
- 🔐 **Message Authentication**: HMAC (SHA-256, SHA-512)
2627
- #️⃣ **Hashing**: SHA-256, SHA-512, (optional: SHA-3, BLAKE2)
28+
- 🔒 **Optional RSA Support**: RSA-OAEP encryption & RSA-PSS signatures (⚠️ opt-in only, not recommended)
2729
- 🎲 **Secure Random**: Cryptographically secure RNG wrapper
2830
- 🧹 **Memory Safety**: Automatic zeroization of sensitive data
2931
- 🌐 **Interoperability**: Helpers for CryptoJS compatibility
@@ -101,6 +103,77 @@ fn main() -> CrabResult<()> {
101103
}
102104
```
103105

106+
### Serialization (Serde)
107+
108+
```rust
109+
use crabgraph::{aead::AesGcm256, asym::Ed25519KeyPair, CrabResult};
110+
111+
fn main() -> CrabResult<()> {
112+
// Encrypt data
113+
let key = AesGcm256::generate_key()?;
114+
let cipher = AesGcm256::new(&key)?;
115+
let ciphertext = cipher.encrypt(b"Secret message", None)?;
116+
117+
// Serialize to JSON
118+
let json = serde_json::to_string(&ciphertext)?;
119+
println!("Ciphertext JSON: {}", json);
120+
121+
// Deserialize and decrypt
122+
let restored: crabgraph::aead::Ciphertext = serde_json::from_str(&json)?;
123+
let plaintext = cipher.decrypt(&restored, None)?;
124+
125+
// Works with keys and signatures too
126+
let keypair = Ed25519KeyPair::generate()?;
127+
let pubkey_json = serde_json::to_string(&keypair.public_key())?;
128+
129+
Ok(())
130+
}
131+
```
132+
133+
### Streaming Encryption for Large Files
134+
135+
```rust
136+
use crabgraph::{
137+
aead::stream::{Aes256GcmStreamEncryptor, Aes256GcmStreamDecryptor},
138+
rand::secure_bytes,
139+
CrabResult
140+
};
141+
142+
fn main() -> CrabResult<()> {
143+
// Generate a 32-byte key for AES-256-GCM
144+
let key = secure_bytes(32)?;
145+
146+
// Create stream encryptor (auto-generates 7-byte nonce)
147+
let mut encryptor = Aes256GcmStreamEncryptor::new(&key)?;
148+
let nonce = encryptor.nonce().to_vec(); // Save nonce for decryption
149+
150+
// Encrypt chunks (64 KB default chunk size)
151+
let chunk1 = b"First chunk of data...";
152+
let chunk2 = b"Second chunk of data...";
153+
let chunk3 = b"Final chunk of data!";
154+
155+
let encrypted1 = encryptor.encrypt_next(chunk1)?;
156+
let encrypted2 = encryptor.encrypt_next(chunk2)?;
157+
let encrypted3 = encryptor.encrypt_last(chunk3)?; // Consumes encryptor
158+
159+
// Decrypt using saved nonce
160+
let mut decryptor = Aes256GcmStreamDecryptor::from_nonce(&key, &nonce)?;
161+
162+
let decrypted1 = decryptor.decrypt_next(&encrypted1)?;
163+
let decrypted2 = decryptor.decrypt_next(&encrypted2)?;
164+
let decrypted3 = decryptor.decrypt_last(&encrypted3)?; // Consumes decryptor
165+
166+
assert_eq!(decrypted1, chunk1);
167+
assert_eq!(decrypted2, chunk2);
168+
assert_eq!(decrypted3, chunk3);
169+
170+
Ok(())
171+
}
172+
```
173+
174+
See `examples/serde_example.rs` for JSON, TOML, and binary serialization examples.
175+
```
176+
104177
### HMAC Authentication
105178
106179
```rust
@@ -167,11 +240,22 @@ cargo audit
167240
- `alloc`: Allocation support without full std
168241
- `no_std`: Embedded/bare-metal support
169242
- `extended-hashes`: SHA-3 and BLAKE2 support
170-
- `rsa-support`: RSA encryption/signatures (large dependency)
243+
- `rsa-support`: RSA encryption/signatures (⚠️ **NOT enabled by default** - opt-in only, has known vulnerability RUSTSEC-2023-0071)
171244
- `serde-support`: Serialization for keys and ciphertexts
172245
- `zero-copy`: `bytes` crate integration for high-performance scenarios
173246
- `wasm`: WebAssembly support
174247

248+
### Enabling RSA Support
249+
250+
RSA is **not included by default** due to security concerns. To use RSA:
251+
252+
```toml
253+
[dependencies]
254+
crabgraph = { version = "0.2", features = ["rsa-support"] }
255+
```
256+
257+
⚠️ **Security Warning**: RSA has a known timing attack vulnerability (RUSTSEC-2023-0071). Use Ed25519 for signatures and X25519+AEAD for encryption unless RSA is specifically required for legacy compatibility.
258+
175259
## 🤝 Contributing
176260

177261
Contributions are welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) and our [Code of Conduct](CODE_OF_CONDUCT.md).

0 commit comments

Comments
 (0)