alterion_encrypt/lib.rs
1// SPDX-License-Identifier: GPL-3.0
2//! # alterion-encrypt
3//!
4//! End-to-end encrypted request/response pipeline for Actix-web services.
5//!
6//! ## Architecture
7//!
8//! The library is built in three layers:
9//!
10//! | Layer | Crate path | Responsibility |
11//! |-------|-----------|----------------|
12//! | Key management | `alterion-ecdh` (re-exported) | Rotating X25519 key store, ephemeral handshakes |
13//! | Crypto primitives | `tools::crypt` | AES-256-GCM encrypt/decrypt, Argon2id password hashing, pepper store |
14//! | Wire format | `tools::serializer` | JSON → deflate → msgpack → AES-GCM → ECDH wrap → [`Request`](tools::serializer::Request) |
15//! | Middleware | `interceptor` | Actix-web [`Transform`](actix_web::dev::Transform) that applies the pipeline transparently |
16//!
17//! ## Security properties
18//!
19//! - **Forward secrecy** — each request uses a fresh ephemeral X25519 keypair; compromise of the
20//! server's long-term key does not expose past traffic.
21//! - **Payload confidentiality** — AES-256-GCM with a randomly-generated per-request key; the key
22//! itself is wrapped with the ECDH-derived wrap key so only the server can recover it.
23//! - **Response integrity** — HMAC-SHA256 over the ciphertext, keyed with a key domain-separated
24//! from the encryption key via HKDF-SHA256 (`"alterion-response-mac"` label).
25//! - **Replay protection** — each packet carries a Unix timestamp validated within ±30 seconds; an
26//! optional Redis store (`replay_store`) rejects duplicate `wrapped_key` values.
27//! - **Password security** — Argon2id (65 536 KiB, 3 passes, 4 lanes) with a randomly-generated
28//! pepper stored in the OS keyring and rotatable without invalidating existing hashes.
29//!
30//! ## Typical server setup
31//!
32//! The primary entry point is [`interceptor::Interceptor`]: mount it as an Actix-web middleware
33//! and every encrypted request is transparently decrypted, and every response re-encrypted,
34//! using the X25519 ECDH + AES-256-GCM + HMAC-SHA256 pipeline.
35//!
36//! ## Example
37//!
38//! ```rust,no_run
39//! use alterion_encrypt::{init_key_store, init_handshake_store, start_rotation};
40//! use alterion_encrypt::interceptor::{Interceptor, DecryptedBody};
41//! use actix_web::{web, App, HttpServer, HttpRequest, HttpMessage, HttpResponse, post, get};
42//!
43//! #[post("/api/example")]
44//! async fn example_handler(req: HttpRequest) -> HttpResponse {
45//! let body = match req.extensions().get::<DecryptedBody>().cloned() {
46//! Some(b) => b,
47//! None => return HttpResponse::BadRequest().body("missing encrypted body"),
48//! };
49//! // body.0 is the raw decrypted bytes — deserialise however you like
50//! HttpResponse::Ok().json(serde_json::json!({ "ok": true }))
51//! }
52//!
53//! #[actix_web::main]
54//! async fn main() -> std::io::Result<()> {
55//! // Rotate ECDH keys every hour; keep the previous key live for 5 minutes.
56//! let store = init_key_store(3600);
57//! let hs = init_handshake_store();
58//! start_rotation(store.clone(), 3600, hs.clone());
59//!
60//! HttpServer::new(move || {
61//! App::new()
62//! .wrap(Interceptor { key_store: store.clone(), handshake_store: hs.clone(), replay_store: None })
63//! .service(example_handler)
64//! })
65//! .bind("0.0.0.0:8080")?
66//! .run()
67//! .await
68//! }
69//! ```
70
71pub mod interceptor;
72pub mod tools;
73
74pub use alterion_ecdh::{
75 KeyStore, KeyEntry, EcdhError, HandshakeStore,
76 init_key_store, init_handshake_store,
77 start_rotation, get_current_public_key,
78 ecdh, init_handshake, ecdh_ephemeral, prune_handshakes,
79};