xtax_encryption/lib.rs
1//! # xtax-encryption
2//!
3//! Trait-only encryption provider interface — no backend, no storage, no I/O
4//! decisions. Implement [`EncryptionProvider`] to plug any encryption scheme
5//! into crates like `xtax-blob-storage`.
6//!
7//! ## Crate architecture
8//!
9//! ```text
10//! xtax-encryption ← this crate (trait + error types only)
11//! ↑
12//! xtax-blob-storage ← re-exports and uses the trait
13//! ```
14//!
15//! ## Usage
16//!
17//! ```rust,no_run
18//! use async_trait::async_trait;
19//! use tokio::io::{AsyncRead, AsyncWrite};
20//! use xtax_encryption::{EncryptionProvider, EncryptionResult};
21//!
22//! struct NoopEncryption;
23//!
24//! #[async_trait]
25//! impl EncryptionProvider for NoopEncryption {
26//! async fn encrypt_stream(
27//! &self,
28//! _input: &mut (dyn AsyncRead + Send + Unpin),
29//! _output: &mut (dyn AsyncWrite + Send + Unpin),
30//! ) -> EncryptionResult<Vec<u8>> {
31//! Ok(vec![])
32//! }
33//!
34//! async fn decrypt_stream(
35//! &self,
36//! _input: &mut (dyn AsyncRead + Send + Unpin),
37//! _output: &mut (dyn AsyncWrite + Send + Unpin),
38//! _header_bytes: &[u8],
39//! ) -> EncryptionResult<()> {
40//! Ok(())
41//! }
42//!
43//! async fn rekey_header(&self, _header_bytes: &[u8]) -> EncryptionResult<Option<Vec<u8>>> {
44//! Ok(None)
45//! }
46//! }
47//! ```
48//!
49//! ## Feature flags
50//!
51//! This crate has no features — it's a minimal dependency.
52
53use async_trait::async_trait;
54use tokio::io::{AsyncRead, AsyncWrite};
55
56// ---------------------------------------------------------------------------
57// Error type
58// ---------------------------------------------------------------------------
59
60/// An error returned by [`EncryptionProvider`] methods.
61#[derive(Debug, thiserror::Error)]
62#[non_exhaustive]
63pub enum EncryptionError {
64 /// Encrypt or decrypt operation failed.
65 #[error("encryption operation failed: {message}")]
66 Operation {
67 /// Human-readable description of the failure.
68 message: String,
69 /// Optional underlying cause.
70 #[source]
71 source: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
72 },
73
74 /// Invalid or corrupted header data.
75 #[error("invalid encryption header: {0}")]
76 InvalidHeader(String),
77
78 /// I/O error during encryption or decryption.
79 #[error(transparent)]
80 Io(#[from] std::io::Error),
81}
82
83/// Convenience alias for `Result<T, EncryptionError>`.
84pub type EncryptionResult<T> = Result<T, EncryptionError>;
85
86// ---------------------------------------------------------------------------
87// EncryptionProvider trait
88// ---------------------------------------------------------------------------
89
90/// Encryption provider — abstracts the encryption operations needed
91/// by encrypted storage layers.
92///
93/// This trait allows any crate to work with a pluggable encryption backend
94/// that supports detached-header stream encryption.
95///
96/// # Implementations
97///
98/// - Must be [`Send`] + [`Sync`] (required by async storage layers).
99/// - The [`encrypt_stream`](EncryptionProvider::encrypt_stream) method
100/// **must** flush the output stream before returning.
101/// - The returned header bytes are stored separately from the encrypted data
102/// and later passed back to [`decrypt_stream`](EncryptionProvider::decrypt_stream).
103#[async_trait]
104pub trait EncryptionProvider: Send + Sync {
105 /// Encrypt data from `input` and write the encrypted stream to `output`.
106 ///
107 /// Returns the serialisable encryption header that must be stored
108 /// alongside the data (e.g. as a separate blob).
109 async fn encrypt_stream(
110 &self,
111 input: &mut (dyn AsyncRead + Send + Unpin),
112 output: &mut (dyn AsyncWrite + Send + Unpin),
113 ) -> EncryptionResult<Vec<u8>>;
114
115 /// Decrypt data from `input` using the previously stored `header_bytes`
116 /// and write plaintext to `output`.
117 async fn decrypt_stream(
118 &self,
119 input: &mut (dyn AsyncRead + Send + Unpin),
120 output: &mut (dyn AsyncWrite + Send + Unpin),
121 header_bytes: &[u8],
122 ) -> EncryptionResult<()>;
123
124 /// Try to re-key (re-wrap) an existing encryption header with the
125 /// current master key.
126 ///
127 /// - Returns `None` if the header is already using the current key.
128 /// - Returns `Some(new_header_bytes)` if the header was re-wrapped.
129 async fn rekey_header(&self, header_bytes: &[u8]) -> EncryptionResult<Option<Vec<u8>>>;
130}