Skip to main content

CryptoError

Enum CryptoError 

Source
#[non_exhaustive]
pub enum CryptoError {
Show 41 variants Io(Error), InputPath, InvalidInput(String), InvalidFormat(FormatDefect), UnsupportedVersion(UnsupportedVersion), InvalidKdfParams(InvalidKdfParams), KdfResourceCapExceeded { mem_cost_kib: u32, local_cap_kib: u32, }, KdfTimeCostCapExceeded { time_cost: u32, local_cap: u32, }, KdfLanesCapExceeded { lanes: u32, local_cap: u32, }, KdfBelowWriteFloor { mem_cost_kib: u32, floor_kib: u32, }, HeaderLenCapExceeded { header_len: u32, local_cap: u32, }, RecipientCountCapExceeded { count: u16, local_cap: u16, }, RecipientBodyCapExceeded { body_len: u32, local_cap: u32, }, RecipientStringCapExceeded { input_chars: u32, local_cap: u32, }, PrivateKeyWrappedSecretCapExceeded { wrapped_secret_len: u32, local_cap: u32, }, KeyFileUnlockFailed, HeaderTampered, HeaderMacFailedAfterUnwrap { type_name: String, }, RecipientUnwrapFailed { type_name: String, }, UnknownCriticalRecipient { type_name: String, }, NoSupportedRecipient, DecryptorModeMismatch { expected: UnauthenticatedRecipientMode, found: UnauthenticatedRecipientMode, }, EmptyRecipientList, IncompatibleRecipients { type_name: String, policy: MixingPolicy, }, PayloadTampered, PayloadTruncated, ExtraDataAfterPayload, PayloadChunkCountExceeded, MalformedArchive { reason: &'static str, }, UnsafeArchivePath { path: String, reason: &'static str, }, InvalidArchiveTree { path: String, reason: &'static str, }, ArchiveEntryCountCapExceeded { entry_count: u32, local_cap: u32, }, ArchiveTotalBytesCapExceeded { total_bytes: u64, local_cap: u64, }, ArchiveManifestLenCapExceeded { manifest_len: u64, local_cap: u32, }, ArchivePathBytesCapExceeded { path_bytes: u32, local_cap: u32, path: Option<String>, }, ArchivePathDepthCapExceeded { depth: u32, local_cap: u32, path: String, }, ArchiveExtLenCapExceeded { ext_len: u64, local_cap: u32, }, ArchiveEntryExtLenCapExceeded { ext_len: u64, local_cap: u32, path: Option<String>, }, ArchiveTotalEntryExtCapExceeded { total_ext_bytes: u64, local_cap: u64, }, InternalInvariant(&'static str), InternalCryptoFailure(&'static str),
}
Expand description

Errors that can occur during key generation, encryption, or decryption.

All Display messages are short, user-facing, and free of internal type names so that consumers can surface them directly without additional mapping.

§Design: identity-only where possible

Most variants are identity-only: they carry no per-operation context (no paths, no byte offsets, no wrapped error text), because that context belongs at the caller, not inside the error. A CLI frontend can prepend the file path if it wants to; a GUI can elide it; a server can log structured fields. The library stays agnostic.

Variants that do carry data carry typed structured data, not heap-allocated strings:

Consumers can pattern-match on these shapes without substring comparisons.

§The one escape hatch: CryptoError::InvalidInput

One variant — CryptoError::InvalidInput — carries a free-form String. It is the designated heterogeneous caller-input bucket for fail-closed rejections whose only useful context is a path or short token that has to be echoed back to the user. Concretely it covers:

  • encrypt-side source-tree problems: “Input is a symlink: path”, “Source is no longer a regular file: name”, “Unsupported file type: path”. The source tree belongs to the caller’s environment, so these are caller-input rejections, not format defects. Every embedded path or name — caller-supplied top-level paths included — is sanitized via sanitize_for_display / sanitize_path_for_display before embedding.
  • Bech32 recipient parser: reports a malformed recipient string, unexpected recipient prefix, or uppercase input. Callers pass recipient strings through as opaque values, so the parser echoes sanitized, truncated input to help the user spot a typo.
  • Caller-invocation path conflicts and shape rejections: “Output already exists: path”, “Key file already exists: path”, “Invalid recipient public key”. These surface which user-supplied path or value triggered the rejection so operators can fix it without extra debugging. The conflict messages render the parent directory raw (it is the caller’s trust boundary) and escape the final component, which can be attacker-influenced.
  • Caller-supplied config values outside the valid range: “KDF memory limit overflow: N MiB”, “Passphrase must not be empty”.

Rejections of a parsed archive payload — a malicious or corrupt FCA inside the decrypted stream — are not InvalidInput: they surface as CryptoError::MalformedArchive, CryptoError::UnsafeArchivePath, CryptoError::InvalidArchiveTree, or the Archive*CapExceeded family, because they describe the file, not the caller.

Library consumers treat InvalidInput as an opaque string and surface it via Display; the CLI and desktop frontends do exactly that.

Variants (Non-exhaustive)§

This enum is marked as non-exhaustive
Non-exhaustive enums could have additional variants added in future. Therefore, when matching against variants of non-exhaustive enums, an extra wildcard arm must be added to account for any future variants.
§

Io(Error)

Filesystem or stream I/O failure.

§

InputPath

Input file or directory does not exist.

§

InvalidInput(String)

Invalid caller input with a human-readable explanation. See the type-level docs for the design rationale.

§

InvalidFormat(FormatDefect)

Encrypted file or key-file structure is invalid, truncated, or corrupted at the format level (not a crypto authentication issue).

§

UnsupportedVersion(UnsupportedVersion)

Encrypted file or key-file version is outside the range this release can read.

§

InvalidKdfParams(InvalidKdfParams)

KDF parameters read from an untrusted header are outside safe structural bounds.

§

KdfResourceCapExceeded

Argon2id memory cost from a header exceeds the caller-configured local resource cap. Per FORMAT.md §3.2, exceeding a local cap produces a distinct resource-cap error rather than a generic malformed-file error. Distinct from InvalidKdfParams (structurally invalid params): here the params are well-formed but cost more than the caller is willing to spend.

Fields

§mem_cost_kib: u32

Memory cost requested by the untrusted header, in KiB.

§local_cap_kib: u32

Maximum memory cost accepted by the caller’s local policy, in KiB.

§

KdfTimeCostCapExceeded

Argon2id time cost (iteration count) from a header exceeds the caller-configured local cap. The time-dimension counterpart of Self::KdfResourceCapExceeded: the value is structurally valid (within the v1 maximum) but asks for more iterations than the policy allows. The default cap is the v1 maximum, so this variant is returned only when a caller tightens KdfLimit below that maximum.

Fields

§time_cost: u32

Time cost (iteration count) requested by the untrusted header.

§local_cap: u32

Maximum time cost accepted by the caller’s local policy.

§

KdfLanesCapExceeded

Argon2id lane count (parallelism) from a header exceeds the caller-configured local cap. The parallelism-dimension counterpart of Self::KdfResourceCapExceeded; like Self::KdfTimeCostCapExceeded, the default cap is the v1 maximum, so this variant is returned only when a caller tightens KdfLimit below that maximum.

Fields

§lanes: u32

Lane count (parallelism) requested by the untrusted header.

§local_cap: u32

Maximum lane count accepted by the caller’s local policy.

§

KdfBelowWriteFloor

Writer-side Argon2id memory cost is below the production floor. Raised only when writing a .fcr or private.key (passphrase encryption / key sealing), so a caller cannot accidentally seal an artefact with weak Argon2id memory. The floor is a writer policy only: readers accept any structurally valid, within-cap parameters, so a file written before the floor existed always decrypts.

Fields

§mem_cost_kib: u32

Memory cost the caller requested, in KiB.

§floor_kib: u32

Minimum memory cost the writer accepts, in KiB.

§

HeaderLenCapExceeded

header_len exceeds the caller-configured local cap. The structural max (HEADER_LEN_MAX = 16 MiB per FORMAT.md §3.1) is much higher; this fires when the header would exceed the caller’s resource policy. Distinct from FormatDefect::OversizedHeader (above structural max) per FORMAT.md §3.2.

Fields

§header_len: u32

Header length declared by the .fcr prefix, in bytes.

§local_cap: u32

Maximum header length accepted by local policy, in bytes.

§

RecipientCountCapExceeded

recipient_count exceeds the caller-configured local cap. The structural range (1..=4096 per FORMAT.md §3.2) is much wider; this fires when the count would exceed the caller’s resource policy. Distinct from FormatDefect::RecipientCountOutOfRange (above structural max).

Fields

§count: u16

Recipient count declared by the header or requested by the writer.

§local_cap: u16

Maximum recipient count accepted by local policy.

§

RecipientBodyCapExceeded

A recipient entry’s body_len exceeds the local resource cap. The structural max (BODY_LEN_MAX = 16 MiB per FORMAT.md §3.3) is much higher; this fires when the body would exceed the caller-configured local cap (FORMAT.md §3.2 recommends 8 KiB for untrusted input). Distinct from FormatDefect::MalformedRecipientEntry: the file is structurally valid; the reader’s resource policy is the constraint, and callers may raise the cap for trusted input.

Fields

§body_len: u32

Recipient body length declared by the entry, in bytes.

§local_cap: u32

Maximum per-recipient body length accepted by local policy, in bytes.

§

RecipientStringCapExceeded

Bech32 recipient string exceeds the caller-configured local length cap.

Distinct from malformed public-key input: the string may be structurally valid, but the reader’s resource policy rejected it. The v1 structural ceiling is 20,000 ASCII characters (FORMAT.md §7); the recommended local default is smaller. For valid recipient strings, byte length and character count are the same because the encoding is ASCII.

Fields

§input_chars: u32

Number of characters in the supplied recipient string.

§local_cap: u32

Maximum recipient-string length accepted by local policy.

§

PrivateKeyWrappedSecretCapExceeded

A private.key file’s wrapped_secret_len exceeds the local resource cap. The structural max (16 MiB per FORMAT.md §8) is much higher; this fires when the wrapped secret would exceed the reader’s resource policy (4 KiB by default — every v1 native key type needs only 48 bytes). Distinct from FormatDefect::MalformedPrivateKey: the file may be structurally valid for a future key type; the local policy is the constraint.

Fields

§wrapped_secret_len: u32

Wrapped-secret length declared by the private.key header, in bytes.

§local_cap: u32

Maximum wrapped-secret length accepted by local policy, in bytes.

§

KeyFileUnlockFailed

Unlocking the private.key file failed AEAD authentication. The key file is structurally valid, but either the supplied passphrase does not decrypt it, or its cleartext fields have been tampered with after the file was written. The AEAD primitive cannot distinguish the two cases — the associated-data binding introduced in the v1 private.key format catches tampering cryptographically, but both failure modes surface as the same error by design. The Display wording reflects both causes.

§

HeaderTampered

The header MAC failed after the passphrase recipient unwrapped a candidate file_key.

Per FORMAT.md §3.7, a recipient unwrap is not accepted until the candidate key verifies the header MAC. The failed MAC shows the MAC-covered bytes changed after the file was written, but cannot distinguish deliberate tampering from storage corruption. This variant is the passphrase-mode verdict; the public-key decrypt path reports the same condition as Self::HeaderMacFailedAfterUnwrap, whatever the recipient count.

§

HeaderMacFailedAfterUnwrap

In a public-key decrypt, a recipient slot unwrapped a file_key, but the resulting header_key did not verify the header MAC.

The unwrap is not final until the MAC verifies. The decrypt loop still visits every supported slot; when at least one slot unwrapped and none verified, this variant is the final verdict — including for a single-recipient public-key file. The passphrase counterpart is Self::HeaderTampered, and both render the same message. The type_name field records which recipient type produced the failed candidate; it is not shown in the message.

Fields

§type_name: String

Recipient type name whose candidate key failed header-MAC verification.

§

RecipientUnwrapFailed

No supported recipient slot opened with the supplied credential. This means the credential is wrong or a recipient body was modified; the AEAD result cannot distinguish those cases. This is the FORMAT.md §12 wrong-passphrase/key class, separate from Self::NoSupportedRecipient (a file with no supported recipient type at all). An opened slot is not final until the header MAC verifies.

The rendered message follows type_name: wrong passphrase for argon2id; for public-key recipients, no matching recipient or a modified file. The type_name is not shown.

Fields

§type_name: String

Recipient type name whose body failed to unwrap.

§

UnknownCriticalRecipient

The recipient list contains a recipient_flags.critical = 1 entry whose type_name is unknown to this implementation. Per FORMAT.md §3.4 unknown critical entries must cause file rejection (vs unknown non-critical, which are skipped).

Fields

§type_name: String

Unknown recipient type name that carried the critical flag.

§

NoSupportedRecipient

The file holds no recipient of a type this build can process — only unknown non-critical entries. Detected while classifying the recipient list, before any unwrap. A supported recipient whose unwrap fails is Self::RecipientUnwrapFailed instead; FORMAT.md §12 lists the two as separate failure classes.

§

DecryptorModeMismatch

The decryptor variant the caller chose does not match the file’s recipient mode (e.g. a passphrase decryptor invoked against a file sealed to public-key recipients, or vice versa).

The public API routes through crate::Decryptor::open, which inspects the file structurally and hands back the matching variant — so callers using the public surface cannot reach this error. It is reserved for internal callers and any future plugin-style API where a caller drives protocol::decrypt directly with a chosen credential scheme.

expected is the mode the decryptor expected (its credential-scheme mode); found is the mode classified from the file’s recipient list. Distinct from Self::NoSupportedRecipient, which means “the file’s recipient list contains no entry I can unlock,” not “I’m the wrong tool for this file.”

Fields

§expected: UnauthenticatedRecipientMode

Decryptor mode selected by the caller.

§found: UnauthenticatedRecipientMode

Recipient mode classified from the .fcr header.

§

EmptyRecipientList

The caller provided no encryption recipients.

Encryptor::with_public_keys requires at least one public recipient; otherwise there is no recipient entry to wrap the per-file file_key.

§

IncompatibleRecipients

The recipient list contains an entry whose mixing rule forbids the combination. The most common v1 trigger is an MixingPolicy::Exclusive native type (today only argon2id) sharing a file with any other entry — per FORMAT.md §4.1 such types must appear alone, and readers must reject the mix structurally before running any KDF.

type_name identifies which entry triggered the rejection; policy carries the MixingPolicy projection the offending rule declared, so callers can pattern-match without parsing the message. Future native types whose compatibility class differs from the two fixed shorthand classes surface as MixingPolicy::Custom { compatibility_class }, with the class identifier preserved in the variant payload. The same variant surfaces from both the decrypt-side mixing enforcement (run before any KDF) and the encrypt-side preflight (run before any output bytes are written), with identical wording in both directions.

Fields

§type_name: String

Recipient type name whose mixing policy rejected the list.

§policy: MixingPolicy

Mixing policy associated with the offending recipient type.

§

PayloadTampered

An encrypted payload chunk failed AEAD authentication during streaming decryption. The ciphertext was modified or corrupted after the header was authenticated.

§

PayloadTruncated

The encrypted stream ends before the final-flag chunk. Usually caused by a truncated file or an aborted download.

§

ExtraDataAfterPayload

Bytes remain after the final-flag chunk has been successfully decrypted. The file has unexpected trailing data.

§

PayloadChunkCountExceeded

The encrypted payload exceeds the 2^32-chunk cap mandated by FORMAT.md §5. Surfaces from the writer-side cap (refuses to emit the over-cap chunk) and the reader-side cap (refuses to consume one).

§

MalformedArchive

Inner FCA archive header or manifest is structurally invalid: bad magic, reserved flags set, no entries declared, truncated or over-declared regions, a length-field overflow, trailing manifest or content bytes, an unknown entry kind, a directory entry declaring a size, an out-of-range mode word, a non-UTF-8 or empty entry path, or a declared total that does not match the entry sizes. Fires inside the encrypted payload after the outer container is accepted, and from the writer gate when a caller hands the archive writer data it could never read back. The reason names the violated rule. Per FORMAT.md §9.

Fields

§reason: &'static str

The specific FORMAT.md §9 rule the payload violated.

§

UnsafeArchivePath

An archive entry path violates the FORMAT.md §9.6 grammar (absolute, traversal, separator abuse, control bytes, Windows-reserved names, and similar path-safety violations). Fires on read for a malicious or corrupt archive and on write for a source tree whose names FCA cannot represent.

Fields

§path: String

Offending entry path, sanitized for display (control and non-ASCII characters escaped, long input truncated).

§reason: &'static str

The specific FORMAT.md §9.6 rule the path violated.

§

InvalidArchiveTree

The archive manifest violates the FORMAT.md §9.7/§9.8 tree rules: duplicate entries (exact or ASCII-case-insensitive), multiple top-level roots, a missing parent or root entry, or a child under a file path.

Fields

§path: String

Entry path that exposed the violation, sanitized for display.

§reason: &'static str

The specific tree rule the manifest violated.

§

ArchiveEntryCountCapExceeded

Archive entry count exceeds the configured ArchiveLimits::max_entry_count cap (FORMAT.md §9.12). Like every Archive*CapExceeded variant, this is a resource-policy rejection, not a format defect, and is enforced identically on encrypt and decrypt.

Fields

§entry_count: u32

Entry count declared by the archive or discovered by the writer.

§local_cap: u32

Maximum entry count accepted by local policy.

§

ArchiveTotalBytesCapExceeded

Total plaintext bytes exceed the configured ArchiveLimits::max_total_plaintext_bytes cap (FORMAT.md §9.12).

Fields

§total_bytes: u64

Running plaintext total that crossed the cap, in bytes.

§local_cap: u64

Maximum total plaintext accepted by local policy, in bytes.

§

ArchiveManifestLenCapExceeded

Serialized manifest length exceeds the configured ArchiveLimits::max_manifest_bytes cap (FORMAT.md §9.12).

Fields

§manifest_len: u64

Manifest length declared by the archive header or computed by the writer, in bytes.

§local_cap: u32

Maximum manifest length accepted by local policy, in bytes.

§

ArchivePathBytesCapExceeded

An entry path’s UTF-8 byte length exceeds the configured ArchiveLimits::max_path_bytes cap (FORMAT.md §9.12). path is None when the cap fires on the declared length before the path bytes have been parsed.

Fields

§path_bytes: u32

Declared or measured path length, in bytes.

§local_cap: u32

Maximum path length accepted by local policy, in bytes.

§path: Option<String>

Offending entry path, sanitized for display, when available.

§

ArchivePathDepthCapExceeded

An entry path’s component depth exceeds the configured ArchiveLimits::max_path_depth cap (FORMAT.md §9.12).

Fields

§depth: u32

Component count of the offending path.

§local_cap: u32

Maximum path depth accepted by local policy.

§path: String

Offending entry path, sanitized for display.

§

ArchiveExtLenCapExceeded

The archive-level extension region exceeds the configured ArchiveLimits::max_archive_ext_bytes cap (FORMAT.md §9.12).

Fields

§ext_len: u64

Declared archive-extension length, in bytes.

§local_cap: u32

Maximum archive-extension length accepted by local policy, in bytes.

§

ArchiveEntryExtLenCapExceeded

A per-entry extension region exceeds the configured ArchiveLimits::max_entry_ext_bytes cap (FORMAT.md §9.12). path is None when the cap fires on the declared length before the entry’s path has been parsed.

Fields

§ext_len: u64

Declared entry-extension length, in bytes.

§local_cap: u32

Maximum per-entry extension length accepted by local policy, in bytes.

§path: Option<String>

Offending entry path, sanitized for display, when available.

§

ArchiveTotalEntryExtCapExceeded

The summed per-entry extension regions exceed the configured ArchiveLimits::max_total_entry_ext_bytes cap (FORMAT.md §9.12).

Fields

§total_ext_bytes: u64

Running entry-extension total that crossed the cap, in bytes.

§local_cap: u64

Maximum summed entry-extension bytes accepted by local policy.

§

InternalInvariant(&'static str)

A non-cryptographic invariant that should hold by construction did not hold. Triggered by state-machine misuse (e.g. using a stream after it was finalized), impossible-size checks, or internal encoding failures. If this fires, it indicates a library bug.

§

InternalCryptoFailure(&'static str)

A cryptographic primitive (AEAD encryption, HKDF expansion) returned an error even though the inputs were well-formed. Unreachable in practice for valid data; indicates either a library bug or a very rare underlying-crate failure.

Trait Implementations§

Source§

impl Debug for CryptoError

Source§

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

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

impl Display for CryptoError

Source§

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

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

impl Error for CryptoError

Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0:

use the Display impl or to_string()

1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0:

replaced by Error::source, which can support downcasting

Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<Error> for CryptoError

Source§

fn from(e: Error) -> Self

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> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

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> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. 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.