Skip to main content

EncryptionService

Struct EncryptionService 

Source
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

Source

pub fn new(stored_key: &str) -> Result<EncryptionService, EncryptionError>

Create an encryption service from a serialized keyring.

Source

pub fn from_key(key: [u8; 32]) -> EncryptionService

Create a new encryption service from a raw 32-byte key.

Source

pub fn from_key_at_generation( generation: u64, key: [u8; 32], ) -> EncryptionService

Source

pub fn from_keyring( keys: impl IntoIterator<Item = (u64, [u8; 32])>, ) -> Result<EncryptionService, EncryptionError>

Source

pub fn current_generation(&self) -> u64

Source

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.

Source

pub fn keyring_entries(&self) -> Vec<(u64, [u8; 32])>

Source

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.

Source

pub fn to_keyring_string(&self) -> Result<String, EncryptionError>

Source

pub fn to_keyring_payload(&self) -> Result<Vec<u8>, EncryptionError>

Source

pub fn from_keyring_payload( plaintext: Vec<u8>, ) -> Result<EncryptionService, EncryptionError>

Source

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.

Source

pub fn service_for_fingerprint( &self, fingerprint: &[u8; 8], ) -> Result<EncryptionService, EncryptionError>

Source

pub fn with_appended_generation( &self, generation: u64, key: [u8; 32], ) -> Result<EncryptionService, EncryptionError>

Source

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.

Source

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.

Source

pub fn seal_key_fingerprint(&self) -> KeyFingerprint

Source

pub fn key_bytes(&self) -> [u8; 32]

Return the raw 32-byte seal key.

Source

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.

Source

pub fn decrypt( &self, encrypted_data: &[u8], aad_context: &[u8], ) -> Result<Vec<u8>, EncryptionError>

Decrypt data in chunked format: [nonce (24 bytes)][ciphertext chunks…]

Source

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].

Source

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.

Source

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 time
  • encrypted_chunks: Raw encrypted chunk bytes (NO nonce prefix)
  • first_chunk_index: Which chunk index the encrypted_chunks starts at
  • plaintext_start, plaintext_end: Absolute byte positions in original file

Example: To read plaintext bytes 500,000-600,000:

  1. Calculate needed chunks: encrypted_chunk_range(500000, 600000) -> chunks 7-9
  2. Fetch encrypted bytes from cloud at those positions
  3. Call decrypt_range_with_offset(nonce, chunks, 7, 500000, 600000, source_size, aad_context)
Source

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.

Source

pub fn derive_scoped_for_fingerprint( &self, fingerprint: &[u8; 8], scope_id: &str, ) -> Result<EncryptionService, EncryptionError>

Source

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
Source

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 key

Naming 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.

Source

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

Source§

fn clone(&self) -> EncryptionService

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for EncryptionService

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl From<EncryptionService> for MasterKeyring

Source§

fn from(service: EncryptionService) -> MasterKeyring

Converts to this type from the input type.
Source§

impl From<MasterKeyring> for EncryptionService

Source§

fn from(keyring: MasterKeyring) -> EncryptionService

Converts to this type from the input type.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> FromRef<T> for T
where T: Clone,

§

fn from_ref(input: &T) -> T

Converts to this type from a reference to the input type.
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
§

impl<Unshared, Shared> IntoShared<Shared> for Unshared
where Shared: FromUnshared<Unshared>,

§

fn into_shared(self) -> Shared

Creates a shared type from an unshared type.
§

impl<T> PolicyExt for T
where T: ?Sized,

§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] only if self and other return Action::Follow. Read more
§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,