Skip to main content

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}