pub struct EncryptionService { /* private fields */ }Expand description
Manages encryption keys and provides XChaCha20-Poly1305 encryption/decryption
This implements the security model described in the README:
- Files are encrypted using XChaCha20-Poly1305 for authenticated encryption
- Chunked format enables random-access decryption for efficient range reads
Implementations§
Source§impl EncryptionService
impl EncryptionService
Sourcepub fn new(stored_key: &str) -> Result<EncryptionService, EncryptionError>
pub fn new(stored_key: &str) -> Result<EncryptionService, EncryptionError>
Create an encryption service from a serialized keyring.
Sourcepub fn from_key(key: [u8; 32]) -> EncryptionService
pub fn from_key(key: [u8; 32]) -> EncryptionService
Create a new encryption service from a raw 32-byte key.
pub fn from_key_at_generation( generation: u64, key: [u8; 32], ) -> EncryptionService
pub fn from_keyring( keys: impl IntoIterator<Item = (u64, [u8; 32])>, ) -> Result<EncryptionService, EncryptionError>
pub fn current_generation(&self) -> u64
Sourcepub fn key_count(&self) -> usize
pub fn key_count(&self) -> usize
How many keys this keyring holds. Two keys at the same generation count as two — the count grows only when a genuinely new key is folded in.
pub fn keyring_entries(&self) -> Vec<(u64, [u8; 32])>
Sourcepub fn merged_with(&self, other: &EncryptionService) -> EncryptionService
pub fn merged_with(&self, other: &EncryptionService) -> EncryptionService
Union this keyring with other: every key either holds. A key already
present (same fingerprint) keeps its existing entry, so a merge never
drops a key already adopted and never rewrites one. This is how adoption
folds an incoming keyring in — keyrings merge, they never replace.
pub fn to_keyring_string(&self) -> Result<String, EncryptionError>
pub fn to_keyring_payload(&self) -> Result<Vec<u8>, EncryptionError>
pub fn from_keyring_payload( plaintext: Vec<u8>, ) -> Result<EncryptionService, EncryptionError>
Sourcepub fn key_for_fingerprint(
&self,
fingerprint: &[u8; 8],
) -> Result<[u8; 32], EncryptionError>
pub fn key_for_fingerprint( &self, fingerprint: &[u8; 8], ) -> Result<[u8; 32], EncryptionError>
The key with fingerprint fingerprint, if this keyring holds it. A sealed
object names its sealing key this way, so decryption resolves the key by
identity rather than by a generation number that a fork could reuse.
pub fn service_for_fingerprint( &self, fingerprint: &[u8; 8], ) -> Result<EncryptionService, EncryptionError>
pub fn with_appended_generation( &self, generation: u64, key: [u8; 32], ) -> Result<EncryptionService, EncryptionError>
Sourcepub fn fingerprint(&self) -> String
pub fn fingerprint(&self) -> String
SHA-256 fingerprint of the seal key, first 8 bytes hex-encoded (16 hex chars). Short enough to display in UI, long enough to detect wrong keys.
Sourcepub fn seal_fingerprint(&self) -> [u8; 8]
pub fn seal_fingerprint(&self) -> [u8; 8]
The seal key’s 8-byte fingerprint — what a sealed object records so a later read resolves the exact key, whatever the keyring has become since.
pub fn seal_key_fingerprint(&self) -> KeyFingerprint
Sourcepub fn encrypt(&self, plaintext: &[u8], aad_context: &[u8]) -> Vec<u8> ⓘ
pub fn encrypt(&self, plaintext: &[u8], aad_context: &[u8]) -> Vec<u8> ⓘ
Encrypt data using chunked XChaCha20-Poly1305 format. Returns: [base_nonce: 24 bytes][ciphertext with auth tags] For small data (single chunk), this is equivalent to standard AEAD. For large data, each chunk is independently encrypted for random-access.
Sourcepub fn decrypt(
&self,
encrypted_data: &[u8],
aad_context: &[u8],
) -> Result<Vec<u8>, EncryptionError>
pub fn decrypt( &self, encrypted_data: &[u8], aad_context: &[u8], ) -> Result<Vec<u8>, EncryptionError>
Decrypt data in chunked format: [nonce (24 bytes)][ciphertext chunks…]
Sourcepub fn sealer(&self, plaintext_len: u64, aad_context: &[u8]) -> ChunkSealer
pub fn sealer(&self, plaintext_len: u64, aad_context: &[u8]) -> ChunkSealer
A streaming sealer over this service’s key, for encrypting a blob
chunk-by-chunk straight into an upload. See [ChunkSealer].
Sourcepub fn decrypt_chunk(
&self,
ciphertext: &[u8],
chunk_index: usize,
aad_context: &[u8],
) -> Result<Vec<u8>, EncryptionError>
pub fn decrypt_chunk( &self, ciphertext: &[u8], chunk_index: usize, aad_context: &[u8], ) -> Result<Vec<u8>, EncryptionError>
Decrypt a specific chunk from chunked encrypted data. Enables random-access decryption without reading preceding chunks.
Sourcepub fn decrypt_range_with_offset(
&self,
nonce: &[u8],
encrypted_chunks: &[u8],
first_chunk_index: u64,
plaintext_start: u64,
plaintext_end: u64,
source_size: u64,
aad_context: &[u8],
) -> Result<Vec<u8>, EncryptionError>
pub fn decrypt_range_with_offset( &self, nonce: &[u8], encrypted_chunks: &[u8], first_chunk_index: u64, plaintext_start: u64, plaintext_end: u64, source_size: u64, aad_context: &[u8], ) -> Result<Vec<u8>, EncryptionError>
Decrypt a plaintext byte range using nonce from DB and partial chunk data.
This is the efficient method for encrypted range requests:
nonce: 24-byte nonce stored in DB at import timeencrypted_chunks: Raw encrypted chunk bytes (NO nonce prefix)first_chunk_index: Which chunk index the encrypted_chunks starts atplaintext_start,plaintext_end: Absolute byte positions in original file
Example: To read plaintext bytes 500,000-600,000:
- Calculate needed chunks:
encrypted_chunk_range(500000, 600000)-> chunks 7-9 - Fetch encrypted bytes from cloud at those positions
- Call
decrypt_range_with_offset(nonce, chunks, 7, 500000, 600000, source_size, aad_context)
Sourcepub fn derive_scoped(&self, scope_id: &str) -> EncryptionService
pub fn derive_scoped(&self, scope_id: &str) -> EncryptionService
Derive a scoped encryption service.
Uses HKDF: master_key + “coven-scope-v1:{scope_id}” -> 32-byte key. Deterministic: same master + scope_id always gives the same key.
pub fn derive_scoped_for_fingerprint( &self, fingerprint: &[u8; 8], scope_id: &str, ) -> Result<EncryptionService, EncryptionError>
Sourcepub fn derive_key(&self, info: &str) -> [u8; 32]
pub fn derive_key(&self, info: &str) -> [u8; 32]
Derive a 32-byte key using HKDF-SHA256 with the given info label.
The derivation is deterministic: same master key + same info string always produces the same derived key.
- Salt: the constant
"coven-hkdf-salt-v1"(RFC 5869 permits a fixed, non-secret salt) - IKM: master key
- Info: caller-provided label
Sourcepub fn seal_app_data(&self, plaintext: &[u8], aad: &[u8]) -> Vec<u8> ⓘ
pub fn seal_app_data(&self, plaintext: &[u8], aad: &[u8]) -> Vec<u8> ⓘ
Seal plaintext for storage in a host’s own rows, under this keyring’s
seal key:
[0] version = APP_DATA_SEAL_VERSION
[1..9] the seal key's 8-byte fingerprint
[9..] the chunked ciphertext `encrypt` produces under that keyNaming the key by fingerprint is what keeps the payload openable across
any number of later rotations and forks — Self::open_app_data resolves
whichever key the payload names, not the current one, and a key once held
is never dropped. aad binds the ciphertext to its context (the owning
row’s primary key, say) and must be presented unchanged to open it.
The body is the existing chunked format, so a large payload streams the same way a blob does; there is no size cliff and no second cipher.
Sourcepub fn open_app_data(
&self,
sealed: &[u8],
aad: &[u8],
) -> Result<Vec<u8>, SealError>
pub fn open_app_data( &self, sealed: &[u8], aad: &[u8], ) -> Result<Vec<u8>, SealError>
Open a payload Self::seal_app_data produced, under whichever key it
names — so a keyring that has rotated or merged a fork since still opens
everything it sealed before. A version this build does not read, or a key
this keyring does not hold, is a typed error; a wrong aad or a tampered
payload surfaces the AEAD failure through SealError::Crypto.
Trait Implementations§
Source§impl Clone for EncryptionService
impl Clone for EncryptionService
Source§fn clone(&self) -> EncryptionService
fn clone(&self) -> EncryptionService
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for EncryptionService
impl Debug for EncryptionService
Source§impl From<EncryptionService> for MasterKeyring
impl From<EncryptionService> for MasterKeyring
Source§fn from(service: EncryptionService) -> MasterKeyring
fn from(service: EncryptionService) -> MasterKeyring
Source§impl From<MasterKeyring> for EncryptionService
impl From<MasterKeyring> for EncryptionService
Source§fn from(keyring: MasterKeyring) -> EncryptionService
fn from(keyring: MasterKeyring) -> EncryptionService
Auto Trait Implementations§
impl Freeze for EncryptionService
impl RefUnwindSafe for EncryptionService
impl Send for EncryptionService
impl Sync for EncryptionService
impl Unpin for EncryptionService
impl UnsafeUnpin for EncryptionService
impl UnwindSafe for EncryptionService
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more