pub enum SseError {
Show 24 variants
KeyFileIo {
path: PathBuf,
source: Error,
},
BadKeyLength {
got: usize,
},
TooShort {
got: usize,
},
BadMagic {
got: [u8; 4],
},
UnsupportedAlgo {
tag: u8,
},
KeyNotInKeyring {
id: u16,
},
DecryptFailed,
WrongCustomerKey,
InvalidCustomerKey {
reason: &'static str,
},
CustomerKeyAlgorithmUnsupported {
algo: String,
},
CustomerKeyRequired,
CustomerKeyUnexpected,
KmsAsyncRequired,
KmsFrameTooShort {
got: usize,
min: usize,
},
KmsFrameFieldOob {
what: &'static str,
},
KmsKeyIdNotUtf8,
KmsWrappedDekMismatch {
supplied: String,
stored: String,
},
KmsRequired,
KmsBackend(KmsError),
ChunkAuthFailed {
chunk_index: u32,
},
ChunkSizeInvalid,
ChunkFrameTruncated {
what: &'static str,
},
ChunkCountTooLarge {
got: u32,
max: u32,
},
ChunkFrameTooLarge {
details: &'static str,
},
}Variants§
KeyFileIo
BadKeyLength
TooShort
BadMagic
UnsupportedAlgo
KeyNotInKeyring
DecryptFailed
WrongCustomerKey
The MD5 fingerprint stored in the S4E3 frame doesn’t match the
MD5 of the customer key the client supplied. This is the
“wrong customer key on GET” signal — distinct from
DecryptFailed so service.rs can map it to AWS S3’s
403 AccessDenied (S3 returns AccessDenied when the supplied
SSE-C key doesn’t match the one used at PUT time).
InvalidCustomerKey
parse_customer_key_headers saw a malformed input. reason is
a short human string (“base64 decode of key”, “key length”,
“md5 length”, “md5 mismatch”) for operator log lines — never
echoed to the client (would leak crypto details).
CustomerKeyAlgorithmUnsupported
Client asked for an SSE-C algorithm the gateway doesn’t speak.
AWS S3 only ever defines AES256 here; surfacing the offending
string lets us 400 with a useful message.
CustomerKeyRequired
S4E3 body lacks an SSE-C key — caller passed SseSource::Keyring
when decrypting an SSE-C-encrypted object. service.rs should
translate this into the same “missing customer key” 400 that
AWS S3 returns when SSE-C headers are absent on a GET.
CustomerKeyUnexpected
Inverse: client sent SSE-C headers on a GET for an object stored without SSE-C. The supplied key has no role in decryption, but AWS S3 actually 400s in this case (“expected an unencrypted object” / “extraneous SSE-C headers”), so we mirror that.
KmsAsyncRequired
decrypt (sync) was handed an S4E4 body. SSE-KMS unwrap is
async (it round-trips to the KMS backend), so callers must
peek the magic with peek_magic and dispatch S4E4 frames to
decrypt_with_kms instead. service.rs’s GET handler does
this; tests / direct callers may hit this if they forget.
KmsFrameTooShort
S4E4 frame is shorter than the minimum-possible header (38
bytes for an empty key_id + empty wrapped_dek, which is
itself impossible — we just sanity-check the floor).
KmsFrameFieldOob
S4E4 declared a key_id_len or wrapped_dek_len that runs
past the end of the body. Almost certainly truncation /
corruption rather than tampering (tampering would fail the
AES-GCM tag instead).
KmsKeyIdNotUtf8
key_id field of an S4E4 frame is not valid UTF-8. We require
UTF-8 because LocalKms uses the basename of a .kek file
(which is OS-string-but-typically-UTF-8) and AWS KMS uses ARNs
(which are ASCII).
KmsWrappedDekMismatch
service.rs handed decrypt_with_kms a WrappedDek whose
key_id doesn’t match the one stored in the S4E4 frame. This
is an integration bug (caller is meant to pull the wrapped
DEK from the frame, not from somewhere else), surface as a
distinct error so it shows up in tests rather than silently
failing the AES-GCM tag.
KmsRequired
SSE-KMS path got a non-Kms SseSource for an S4E4 body. The
async dispatch in decrypt_with_kms re-derives the source
internally so this can only happen if a future caller passes
SseSource::Keyring / CustomerKey to a path that expected
Kms — kept around for symmetry with the other “wrong source”
errors.
KmsBackend(KmsError)
Pass-through for crate::kms::KmsError surfaced from
KmsBackend::decrypt_dek — boxed so the variant stays small.
ChunkAuthFailed
AES-GCM auth tag verify failed on chunk chunk_index of an
S4E5 body. Distinct from the all-or-nothing
SseError::DecryptFailed because the streaming GET may
have already emitted earlier chunks to the client by the
time chunk N fails — operators need the chunk index in audit
logs to triangulate which byte range was tampered with (or
which disk sector flipped).
ChunkSizeInvalid
Caller asked encrypt_v2_chunked to use a chunk size of 0
— nonsensical (would loop forever). Surfaced as an error
rather than panicking so service.rs can map a bad
--sse-chunk-size 0 configuration to a clear startup error.
ChunkFrameTruncated
S4E5 frame is shorter than the fixed header or declares a
(chunk_count × per-chunk-bytes) total that overruns the
body. Almost certainly truncation / corruption — tampering
with the per-chunk ciphertext or tag would surface as
SseError::ChunkAuthFailed instead.
ChunkCountTooLarge
S4E6 chunk_index is encoded as a 24-bit big-endian field in
the per-chunk nonce, capping chunk_count at
2^24 - 1 = 16_777_215. At the default 1 MiB chunk size that
is ~16 PiB per object — well past S3’s 5 GiB single-object
ceiling. Surface as a distinct error so a misconfiguration
(--sse-chunk-size 1 on a multi-GiB object, say) shows up at
PUT time with a clear cause rather than a panic at the u32 →
u24 cast.
ChunkFrameTooLarge
parse_chunked_header rejected an S4E5 / S4E6 frame because
the declared chunk_size × chunk_count (or the on-disk total
after adding per-chunk tag overhead and the fixed header) is
either:
- arithmetically nonsensical (the multiplication / addition overflows u64 on a 64-bit host), or
- larger than the gateway’s configured
max_body_bytes(default 5 GiB — AWS S3’s single-object PUT ceiling).
This is the DoS guard for the chunked path: without it, a
24-byte malicious header that claims chunk_size = u32::MAX
and chunk_count = u32::MAX would have caused the buffered
decrypt path to attempt a multi-PB Vec::with_capacity (or
integer-overflow into a tiny alloc + later out-of-bounds
panic) before any cryptographic work happened. Surface as a
distinct variant — never echo the offending sizes back to the
client (kept as a &'static str details field for operator
audit logs only) so the response isn’t a tampering oracle.
Trait Implementations§
Source§impl Error for SseError
impl Error for SseError
Source§fn source(&self) -> Option<&(dyn Error + 'static)>
fn source(&self) -> Option<&(dyn Error + 'static)>
1.0.0 · Source§fn description(&self) -> &str
fn description(&self) -> &str
use the Display impl or to_string()