pub struct KeyRing { /* private fields */ }Expand description
Holds the (versioned) key material used for field protection.
- RSA public key: enough to write encrypted fields; the private key is only needed to read them back, so a log-producing service can run without it.
- HMAC key: required to write and search
FieldProtection::Hmacfields (FieldProtection::Sha256needs no key). A keyed MAC is what stops an attacker with disk access from recovering low-entropy values (SSNs, phone numbers, …) by brute-forcing the digest; without the key the stored digests are useless.
Rotation. Each key carries a KeyVersion; the highest version of
each kind is active and protects every new write, and the version is
recorded on the stored value itself (StoredValue::Hmac /
StoredValue::Rsa) and on persisted index tokens. Older versions stay
in the ring as read-side material: decrypt picks the
private key the record names, and searches probe HMAC digests under every
held version, so rotating keys never silently severs old data. The
version-less builders (with_hmac_key, …) install version 1 — a ring
that never rotates needs no version bookkeeping.
Implementations§
Source§impl KeyRing
impl KeyRing
pub fn new() -> Self
pub fn with_public_pem(self, pem: &str) -> Result<Self>
pub fn with_private_pem(self, pem: &str) -> Result<Self>
Sourcepub fn with_hmac_key(self, key: impl AsRef<[u8]>) -> Self
pub fn with_hmac_key(self, key: impl AsRef<[u8]>) -> Self
Set the secret key for searchable-hash (HMAC-SHA-256) fields (version 1).
Sourcepub fn with_public_pem_version(
self,
version: KeyVersion,
pem: &str,
) -> Result<Self>
pub fn with_public_pem_version( self, version: KeyVersion, pem: &str, ) -> Result<Self>
Add the RSA public key of one key version.
Sourcepub fn with_private_pem_version(
self,
version: KeyVersion,
pem: &str,
) -> Result<Self>
pub fn with_private_pem_version( self, version: KeyVersion, pem: &str, ) -> Result<Self>
Add the RSA private key of one key version (the matching public key is derived if it was not given separately).
Sourcepub fn with_hmac_key_version(
self,
version: KeyVersion,
key: impl AsRef<[u8]>,
) -> Result<Self>
pub fn with_hmac_key_version( self, version: KeyVersion, key: impl AsRef<[u8]>, ) -> Result<Self>
Add the HMAC key of one key version.
Sourcepub fn with_generated_rsa(
self,
version: KeyVersion,
bits: usize,
) -> Result<Self>
pub fn with_generated_rsa( self, version: KeyVersion, bits: usize, ) -> Result<Self>
Add a freshly generated in-memory RSA keypair under version (tests,
demos and rotation drills — production should load persistent keys).
Sourcepub fn with_generated_hmac(self, version: KeyVersion) -> Result<Self>
pub fn with_generated_hmac(self, version: KeyVersion) -> Result<Self>
Add a freshly generated random HMAC key under version.
Sourcepub fn generate_ephemeral(bits: usize) -> Result<Self>
pub fn generate_ephemeral(bits: usize) -> Result<Self>
Generate an in-memory RSA keypair and a random HMAC key, both version 1 (useful for tests and demos — production should load persistent keys).
Sourcepub fn active_hmac_version(&self) -> Option<KeyVersion>
pub fn active_hmac_version(&self) -> Option<KeyVersion>
Version of the HMAC key new writes are digested with (the highest one).
Sourcepub fn active_rsa_version(&self) -> Option<KeyVersion>
pub fn active_rsa_version(&self) -> Option<KeyVersion>
Version of the RSA key new writes are encrypted with (the highest one).
Sourcepub fn hmac_versions(&self) -> Vec<KeyVersion> ⓘ
pub fn hmac_versions(&self) -> Vec<KeyVersion> ⓘ
Every held HMAC key version, newest first — the probe order for multi-version digest matching (recent keys hit most often).
Sourcepub fn has_rsa_private(&self, version: KeyVersion) -> bool
pub fn has_rsa_private(&self, version: KeyVersion) -> bool
Whether version’s RSA private key is held (re-key needs every
version still referenced by stored values).
Sourcepub fn hmac_hex(&self, data: &[u8]) -> Result<(KeyVersion, String)>
pub fn hmac_hex(&self, data: &[u8]) -> Result<(KeyVersion, String)>
Keyed digest under the active HMAC key, used when writing HMAC-protected fields. Returns the version the digest was made with, which the caller persists next to the digest.
Sourcepub fn hmac_hex_with(&self, version: KeyVersion, data: &[u8]) -> Result<String>
pub fn hmac_hex_with(&self, version: KeyVersion, data: &[u8]) -> Result<String>
Keyed digest under one specific HMAC key version — the probe side of multi-version search.
Sourcepub fn index_token_digest(
&self,
field: &str,
protection: FieldProtection,
token: &str,
) -> Result<(KeyVersion, String)>
pub fn index_token_digest( &self, field: &str, protection: FieldProtection, token: &str, ) -> Result<(KeyVersion, String)>
Digest of one blind-index token (crate::schema::FieldIndex) under
the active key. Returns the key version used (KEYLESS for
keyless protections), which the caller persists next to the digests.
The input is domain-separated ("idx:" + field + NUL + token) so an
index digest can never collide with — or be replayed as — the field’s
own stored digest. The digest function follows the field’s protection:
keyless protections (None/Sha256) use SHA-256, keyed ones (Hmac/Rsa)
use the HMAC key, so the tokens of an encrypted field cannot be
brute-forced offline any more than the field itself.
Sourcepub fn index_token_digest_with(
&self,
version: KeyVersion,
field: &str,
protection: FieldProtection,
token: &str,
) -> Result<String>
pub fn index_token_digest_with( &self, version: KeyVersion, field: &str, protection: FieldProtection, token: &str, ) -> Result<String>
index_token_digest under one specific
key version — the probe side of multi-version search. version is
ignored for keyless protections.
Sourcepub fn protect(
&self,
value: &Value,
protection: FieldProtection,
) -> Result<StoredValue>
pub fn protect( &self, value: &Value, protection: FieldProtection, ) -> Result<StoredValue>
Apply a schema-declared protection to a plain value, under the active key of the relevant kind. The key version is recorded on the stored value so reads outlive rotations.
Sourcepub fn can_sign(&self) -> bool
pub fn can_sign(&self) -> bool
Whether this ring can produce signatures (i.e. the active RSA version holds its private key). Write-only deployments (public keys only) cannot sign — checkpointing keys off this.
Sourcepub fn sign(&self, data: &[u8]) -> Result<(KeyVersion, Vec<u8>)>
pub fn sign(&self, data: &[u8]) -> Result<(KeyVersion, Vec<u8>)>
RSA PKCS#1 v1.5 signature over SHA-256(data) with the active private key; returns the key version so verifiers can pick the matching public key after a rotation. PKCS#1 v1.5 (not PSS) because it is deterministic: re-signing identical checkpoint bytes yields identical signatures, which keeps externally anchored copies byte-comparable.
Sourcepub fn verify_signature(
&self,
key_version: KeyVersion,
data: &[u8],
signature: &[u8],
) -> Result<()>
pub fn verify_signature( &self, key_version: KeyVersion, data: &[u8], signature: &[u8], ) -> Result<()>
Verify a signature produced by sign under the named
key version. Needs only that version’s public key, so an auditor can
verify without decryption capability.
Sourcepub fn decrypt(&self, stored: &StoredValue) -> Result<Vec<u8>>
pub fn decrypt(&self, stored: &StoredValue) -> Result<Vec<u8>>
Recover the canonical bytes of an RSA-protected value, with the private key of the version the value was written under.
Sourcepub fn rewrap(&self, stored: &StoredValue) -> Result<StoredValue>
pub fn rewrap(&self, stored: &StoredValue) -> Result<StoredValue>
Re-wrap the data key of an RSA-protected value under the active public key: the old version’s private key unwraps the DEK, the active public key re-wraps it. Nonce and ciphertext are untouched (the DEK itself never changed), so this is cheap and cannot corrupt the payload. Values already at the active version pass through unchanged.