#[non_exhaustive]pub enum WrapScheme {
CmsAes256Gcm,
RsaOaepSha256,
RsaAesKeyWrapSha256,
Aes128Kw,
Aes256Kw,
Aes128Kwp,
Aes256Kwp,
}Expand description
The wrapping scheme a step requests, or a backend reports.
A scheme names a container and the parts of it Rite fixes. What varies
with the recipient is recorded in WrapDescription instead.
Variants (Non-exhaustive)§
This enum is marked as non-exhaustive
CmsAes256Gcm
CMS AuthEnvelopedData (RFC 5083) with AES-256-GCM content
encryption. Output: CMS ContentInfo DER.
The encapsulation follows the recipient key: RSA takes key transport, EC takes the RFC 5753 key-agreement path, and a KEM recipient takes the RFC 9629 key-encapsulation path.
CMS-RSA-CBC, the EnvelopedData variant with AES-256-CBC, was
removed. It carried no integrity protection, so unwrapping imported
whatever decrypted; PCI PIN v3.1 requirement 18-3 bans unauthenticated
key encryption. Restoring it would take an authenticated construction
around it, which is what the deployments that do use CBC (X9 TR-34,
AD CS key archival) wrap it in.
RsaOaepSha256
RSAES-OAEP with SHA-256, as raw ciphertext (RFC 8017).
Output is exactly the modulus size and describes nothing about itself.
The payload ceiling is k - 2*hLen - 2: 190 bytes under RSA-2048, 446
under RSA-4096, which fits a symmetric key or an EC private key and
never fits an RSA one. Self::RsaAesKeyWrapSha256 exists for that.
RsaAesKeyWrapSha256
RSA-AES key wrap with SHA-256 (PKCS#11 CKM_RSA_AES_KEY_WRAP).
An ephemeral AES key under RSAES-OAEP, concatenated with the payload under AES-KWP (RFC 5649). The OAEP part comes first and is exactly the modulus size, which is how a recipient splits the two.
This is the shape cloud KMS import expects, and it carries no size
ceiling. The digest is in the name because it is not recoverable from
the bytes and recipients differ: AWS names both RSA_AES_KEY_WRAP_SHA_256
and RSA_AES_KEY_WRAP_SHA_1, and Azure BYOK specifies SHA-1.
Aes128Kw
AES Key Wrap under a 128-bit KEK (RFC 3394, PKCS#11
CKM_AES_KEY_WRAP).
The payload must be a multiple of 8 bytes and at least 16, which a
PKCS#8 private key is not, so this carries another symmetric key and
little else. Self::Aes128Kwp has no such rule.
Aes256Kw
AES Key Wrap under a 256-bit KEK (RFC 3394).
Aes128Kwp
AES Key Wrap with Padding under a 128-bit KEK (RFC 5649, PKCS#11
CKM_AES_KEY_WRAP_KWP).
Carries a payload of any length. This is what Thales recommends over
CBC for wrapping, and what OpenStack Barbican moved to in 2024 after
its hardcoded CKM_AES_CBC_PAD broke against Luna firmware in FIPS
mode.
Aes256Kwp
AES Key Wrap with Padding under a 256-bit KEK (RFC 5649).
Implementations§
Source§impl WrapScheme
impl WrapScheme
Sourcepub fn fixed_description(self) -> Option<WrapDescription>
pub fn fixed_description(self) -> Option<WrapDescription>
The description this scheme fixes, where it fixes one.
None for a self-describing scheme: what a CMS wrap did follows the
recipient key, so it is read out of the artifact rather than known in
advance. A raw mechanism has no such freedom, which is the same fact
that makes its description an assertion.
This is the one place that says what a raw scheme produces. A backend
builds its description from here rather than restating the OIDs, and
permits compares against it, so the two cannot
disagree.
Sourcepub fn required_kek(self) -> Option<KeyAlgorithm>
pub fn required_kek(self) -> Option<KeyAlgorithm>
The one key algorithm this scheme wraps under, where it names one.
use rite_sdk::{KeyAlgorithm, WrapScheme};
assert_eq!(WrapScheme::Aes256Kwp.required_kek(), Some(KeyAlgorithm::Aes256));
// CMS admits a family, so it names none.
assert_eq!(WrapScheme::CmsAes256Gcm.required_kek(), None);None where the scheme admits a family rather than a single algorithm.
The AES schemes name a KEK size, because the RFC 3394 and RFC 5649
object identifiers are per-size and fixed_description
has only the scheme to go on. Naming it is also what makes a ceremony
that expects a 256-bit KEK refuse a 128-bit one instead of wrapping
under it in silence.
Sourcepub fn accepts_recipient(self, algorithm: KeyAlgorithm) -> bool
pub fn accepts_recipient(self, algorithm: KeyAlgorithm) -> bool
Whether a key of this algorithm can receive a wrap under this scheme.
Narrower than KeyAlgorithm::can_receive_wrap, which answers for CMS,
where the recipient key selects the encapsulation. The RSA mechanisms
accept nothing but RSA, and the AES mechanisms nothing but the KEK size
in their name.
A symmetric key passes here and still cannot arrive as recipient:,
since that path takes a public key and a symmetric key has none. An
AES scheme is reachable only under wrapping_key:.
Sourcepub fn permits(self, description: &WrapDescription) -> bool
pub fn permits(self, description: &WrapDescription) -> bool
Whether description is one this scheme admits.
The check is what keeps a recorded scheme from contradicting the artifact it labels. For a self-describing scheme it is a structural test against what was read out of the bytes; for a raw mechanism the description is fixed, so equality is the whole test.
Sourcepub fn is_self_describing(self) -> bool
pub fn is_self_describing(self) -> bool
Whether a verifier can re-derive the wrap’s algorithms from the bytes.
This is the line between evidence and assertion. A CMS artifact carries
its own algorithm identifiers, so rite verify reads them back and
compares them against the transcript; raw mechanism output carries
nothing, so the recorded algorithms are what the backend reports having
invoked and no offline check can confirm them.
Matched exhaustively on purpose: a scheme added without answering this would otherwise default into being treated as evidence.
Sourcepub fn carries_content(self) -> bool
pub fn carries_content(self) -> bool
Whether the scheme carries content of any length, as opposed to a key.
A container encrypts the content under its own key and addresses only that key to the recipient, so what goes in is arbitrary. A raw mechanism encrypts the payload directly under the recipient’s key, which bounds it to roughly a key’s size.
A separate question from is_self_describing,
which draws the same line today. That one asks whether a verifier can
re-derive the algorithms, and this one asks what may be put in. Keeping
them apart is what lets them diverge honestly when a second container
lands.
Matched exhaustively on purpose: a scheme added without answering this would otherwise default into accepting a file.