RGP
Relatively Good Privacy
Usage
let = generate_fingerprint;
let = generate_exchange_keys;
let mut pub_keys = vec!;
// 8mb
let content = vec!;
// 20,000 recipients
for _ in 0..20_000
let mut encrypted_content = encrypt
.unwrap;
extract_content_for_key_position
.unwrap;
let decrypted_content = decrypt
.unwrap;
assert_eq!;
Disable Multi-threading
The "multi-thread" feature is enabled by default and utilizes the Rayon crate. It only impacts the content::encrypt function, but can be disabled by setting default-features to false.
# Cargo.toml
[]
= { = "x.x.x", = false }
Process
- Generate one-time and ephemeral components
- nonce
- one-time content key
- ephemeral private key
- one-time public key
- Sign plaintext to generate content signature
- Encrypt plaintext and content signature with one-time content key
- Encrypt one-time content key for all recipients
- Generate shared secret with recipient public key and ephemeral private key
- Encrypt one-time content key with shared secret
Ciphersuite
- Ed25519 for signatures
- XChaCha20Poly1305 for content
- X25519 for Diffie-Hellman shared secret generation
- XChaCha20 for one-time content key encryption
Performance
For the 8mb example with 20,000 recipients, on my M1 MacBook Pro
| Operation | Time |
|---|---|
| encrypt (multi-thread) | 97.186 ms |
| encrypt (single-thread) | 764.00 ms |
| extract | 328.30 µs |
| decrypt | 44.729 ms |
Doing the equivalent operation for just 1 recipient on 8mb is
| Operation | Time |
|---|---|
| encrypt (multi-thread) | 61.212 ms |
| encrypt (single-thread) | 61.314 ms |
When benchmarked in isolation, the signing operation (internal to the encrypt function) and verifying operation (internal to the decrypt function), take 28.469 ms and 14.209 ms, respectively.
To check performance on your machine, run cargo bench (or cargo bench --no-default-features to disable multi-threading). You can also view the latest benches in the GitHub CI workflow under job/Benchmark or job/Benchmark (single threaded).
NOTE: in multi-threaded mode the content signing/encryption logic is done in a separate thread from the per-recipient content key encryption, and the content key encryption work is done in a Rayon par_chunks_mut for loop. There is likely an opportunity for further parallelization in the content encryption and signing step.
Encrypted Format
- nonce = 24 bytes
- one-time public key = 32 bytes
- keys count (1-9 bytes)
- int size = 2 bits (0 for u8+63 | 1 for u16+63 | 2 for u32+63 | 3 for u64+63)
- count
- numbers 0-63 = 6 bits
- numbers >63 = 1-8 bytes (big endian int)
- encrypted keys = pub_keys.len() * 32 bytes
- encrypted content = content.len()
- signature = 64 bytes (encrypted along with the content to preserve deniability)
- Poly1305 MAC = 16 bytes
License
Security
THIS CODE HAS NOT BEEN AUDITED OR REVIEWED. USE AT YOUR OWN RISK.