pub struct SecretValue(/* private fields */);Expand description
A secret value whose memory is zeroed on drop.
Does not implement Debug, Display, or Clone to prevent accidental
leakage. Use as_bytes for comparisons in tests.
§Why not the secrecy crate?
The secrecy crate’s Secret<T> zeroes
the outer allocation on drop, which is the same guarantee Zeroizing<Vec<u8>>
provides here. For a plain byte buffer that guarantee would be sufficient,
and using secrecy would be reasonable.
The problem is extract_field and
extract_path_field. Secrets are often
stored as JSON objects ({"username":"alice","password":"s3cr3t"}), so
callers frequently need to pull a single string field out of the raw bytes.
The obvious implementation calls serde_json::from_slice, but serde_json
allocates every field value as a plain (non-Zeroizing) String. Even
after the parsed map is dropped, those allocations are not zeroed: the
password lingers in heap memory until the allocator reuses the page.
secrecy::Secret does not help here — it only zeroes what it directly owns.
These methods therefore use a hand-rolled JSON scanner that never allocates
non-target fields at all. Only the one requested value is placed into a
Zeroizing buffer; everything else is scanned and discarded in place.
Switching to secrecy would either require accepting that leak or keeping
the custom scanner anyway, gaining a dependency without simplifying the code.
Note on built-in backends: the network-backed backends (aws-sm, azure-kv,
doppler, gcp-sm, vault) use serde_json directly rather than these methods,
because in those backends the secret arrives in non-Zeroizing heap memory (a
reqwest response buffer or an SDK-owned String) before any parsing begins.
The custom scanner cannot retroactively zero memory it does not own, so using
it there would add complexity without improving the security boundary. These
methods are most useful when the SecretValue originates from a backend that
does not pre-leak — e.g. secretx-file or secretx-env — and the caller
needs to extract a JSON field while minimising additional unzeroed copies.
See the // ── JSON field extractor section below for the full rationale.
Implementations§
Source§impl SecretValue
impl SecretValue
Sourcepub fn new(bytes: Vec<u8>) -> Self
pub fn new(bytes: Vec<u8>) -> Self
Wrap raw bytes in a SecretValue.
The bytes are moved into a Zeroizing container and zeroed on drop.
Sourcepub fn into_bytes(self) -> Zeroizing<Vec<u8>>
pub fn into_bytes(self) -> Zeroizing<Vec<u8>>
Consume the SecretValue and return the inner Zeroizing<Vec<u8>>.
Sourcepub fn from_zeroizing(z: Zeroizing<Vec<u8>>) -> Self
pub fn from_zeroizing(z: Zeroizing<Vec<u8>>) -> Self
Construct from an already-zeroizing buffer without creating a
non-Zeroizing intermediate copy.
Sourcepub fn as_str(&self) -> Result<&str, SecretError>
pub fn as_str(&self) -> Result<&str, SecretError>
Decode as UTF-8 without copying. Fails if not valid UTF-8.
Sourcepub fn extract_field(&self, field: &str) -> Result<SecretValue, SecretError>
pub fn extract_field(&self, field: &str) -> Result<SecretValue, SecretError>
Parse as a JSON object and extract a single string field.
Common for secrets that bundle multiple values as JSON,
e.g. {"username":"foo","password":"bar"}.
Uses a hand-rolled JSON scanner so that only the requested field’s value is allocated. A full-tree parser (e.g. serde_json) would allocate copies of every field value, leaving other secret strings in unzeroized heap memory even after the parse result is dropped.
Sourcepub fn extract_path(&self, path: &[&str]) -> Result<SecretValue, SecretError>
pub fn extract_path(&self, path: &[&str]) -> Result<SecretValue, SecretError>
Navigate through nested JSON objects and return the raw bytes of the
value at path’s final key as a new SecretValue.
Each key in path must exist in the current JSON object. All
intermediate values (path[..path.len()-1]) must be JSON objects.
The final value may be any JSON type.
§Example (Vault KV v2)
Given {"data": {"data": {"password": "s3cret"}}, ...},
extract_path(&["data", "data"]) returns a SecretValue containing
the bytes of {"password": "s3cret"}. Call SecretValue::extract_field on
the result to retrieve a specific secret field.
Sourcepub fn extract_path_field(
&self,
path: &[&str],
field: &str,
) -> Result<SecretValue, SecretError>
pub fn extract_path_field( &self, path: &[&str], field: &str, ) -> Result<SecretValue, SecretError>
Navigate through nested JSON objects and extract a string field.
Equivalent to self.extract_path(path)?.extract_field(field) but
avoids an intermediate allocation: the nested object bytes are sliced
from the original input with no copy, and only the target field value
is placed in a Zeroizing buffer.