implementation of CKM_RSA_AES_KEY_WRAP mechanism#480
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds PKCS#11 v3.1 support for the CKM_RSA_AES_KEY_WRAP composite wrapping mechanism (RSA-OAEP wrap of a temporary AES key + AES-KWP wrap of the target key), along with test vectors, warnings for weak wrapping strength, and FIPS indicators/changelog updates.
Changes:
- Implemented
CKM_RSA_AES_KEY_WRAPin the RSA mechanism layer and registered it in the mechanism table. - Added unit tests plus an OpenSSL-generated interoperability test vector (JSON) for unwrap validation.
- Added weak-wrap warning utilities and a FIPS indicators table entry; updated the changelog.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| testdata/rsa_aes_key_wrap.json | Adds an OpenSSL-generated interop vector for RSA-AES key wrap framing and unwrap validation. |
| src/tests/rsa.rs | Adds wrap/unwrap round-trip tests and an interoperability unwrap test using the JSON vector. |
| src/rsa.rs | Implements and registers the CKM_RSA_AES_KEY_WRAP mechanism, including ephemeral AES key generation and composite wrap/unwrap logic. |
| src/misc.rs | Adds helper logic to detect/log weak key wrap strength relative to AES key size. |
| src/fips/indicators.rs | Extends the FIPS mechanism indicators table to include CKM_RSA_AES_KEY_WRAP. |
| CHANGELOG.md | Notes the new mechanism support under Unreleased. |
Comments suppressed due to low confidence (1)
CHANGELOG.md:11
- The Unreleased entry is currently listed before the "### What Changed" section, leaving that section empty. This is inconsistent with the formatting used in the rest of the changelog (bullets are under "### What Changed").
## [Unreleased]
* Added support for CKM_RSA_AES_KEY_WRAP mechanism
### What Changed
| return Err(CKR_WRAPPING_KEY_TYPE_INCONSISTENT)?; | ||
| } | ||
|
|
||
| let params = mech.get_parameters::<CK_RSA_AES_KEY_WRAP_PARAMS>()?; |
There was a problem hiding this comment.
I think this is a fix for the get_parameters() function, rather than this place.
| return Err(CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT)?; | ||
| } | ||
|
|
||
| let params = mech.get_parameters::<CK_RSA_AES_KEY_WRAP_PARAMS>()?; |
|
|
||
| /* AES-KWP output length (C2), per NIST SP800-38F: the input padded to | ||
| * an 8-byte boundary plus an 8-byte integrity check block */ | ||
| let c2_len = ((keydata.len() + AES_BLOCK_SIZE - 1) / 8) * 8; |
|
@simo5 I didn't realize there was a similar PR in the queue, although it seems to be hanging for a while now. I'm fine either way, provided that support for this mechanism is eventually merged into the code base. Let me know of your pref, Regards |
|
The #448 did not get update for months so we would better get one active merged than none. |
|
|
||
| /// Implements the RSA AES key wrap operation (Wrap) | ||
| /// | ||
| /// [RSA AES KEY WRAP](https://docs.oasis-open.org/pkcs11/pkcs11-spec/v3.1/os/pkcs11-spec-v3.1-os.html#_Toc111203524) |
There was a problem hiding this comment.
I think the 3.2 is already out so I would reference the latest issue here:
https://docs.oasis-open.org/pkcs11/pkcs11-spec/v3.2/pkcs11-spec-v3.2.html#_Toc234407237
| return Err(CKR_WRAPPING_KEY_TYPE_INCONSISTENT)?; | ||
| } | ||
|
|
||
| let params = mech.get_parameters::<CK_RSA_AES_KEY_WRAP_PARAMS>()?; |
There was a problem hiding this comment.
I think this is a fix for the get_parameters() function, rather than this place.
| | CKF_DECRYPT | ||
| | CKF_WRAP | ||
| | CKF_UNWRAP | ||
| | CKF_DERIVE, |
There was a problem hiding this comment.
We have the CKF_DERIVE on other wrapping mechanisms, but not on the output AES key itself, see the AES key definition above in FipsKeyType.
If I see right, the only AES Derive operation that exists in specs are CKM_AES_CBC_ENCRYPT_DATA and CKM_AES_ECB_ENCRYPT_DATA, which are not FIPS methods so I think the genflags should not contain the DERIVE. Otherwise the derve should be also on the AES key to be consistent. This is more to @simo5 to clarify FIPS considerations here.
| (CKA_SENSITIVE, true), | ||
| (CKA_UNWRAP, true), | ||
| (CKA_DECRYPT, true), | ||
| (CKA_EXTRACTABLE, true), |
There was a problem hiding this comment.
does the wrapping key need to be extractable for this test?
| (CKA_EXPONENT_2, exponent_2.as_slice()), | ||
| (CKA_COEFFICIENT, coefficient.as_slice()), | ||
| ], | ||
| &[(CKA_UNWRAP, true), (CKA_DECRYPT, true)], |
There was a problem hiding this comment.
Is the explicit DECRYPT flag on the private key needed for the unwrapping operation? I guess this is due to the reuse of the decrypt operation, which does the flag check. I wonder if we should not bypass that in this case (and do the checks only externally to low-level encryption/decryption functions).
I know the usage could be limited with the ALLOWED_MECHANISMS, but I would consider limiting the usage also on the attributes level to either wrapping or encryption without the need for the other one.
| assert!(!is_weak_key_wrap(112, 128)); | ||
| assert!(!is_weak_key_wrap(128, 128)); | ||
| assert!(!is_weak_key_wrap(80, 128)); |
There was a problem hiding this comment.
nit: could these be sorted by size?
| /// Mechanism object implementing the CKM_RSA_AES_KEY_WRAP composite key | ||
| /// wrapping mechanism. | ||
| static RSA_AES_KW_MECH: LazyLock<Box<dyn Mechanism>> = LazyLock::new(|| { | ||
| Box::new(RsaAesKeyWrapMechanism { | ||
| info: CK_MECHANISM_INFO { | ||
| ulMinKeySize: CK_ULONG::try_from(MIN_RSA_SIZE_BITS).unwrap(), | ||
| ulMaxKeySize: CK_ULONG::try_from(MAX_RSA_SIZE_BITS).unwrap(), | ||
| flags: CKF_WRAP | CKF_UNWRAP, | ||
| }, | ||
| }) | ||
| }); | ||
|
|
There was a problem hiding this comment.
nit: why not include this mechanism into the RSA_MECHS table? I do not have a strong opinion either way, but we have all the others. Not sure if there is some performance difference in having one or more LazyLock, boxes.
| ulParameterLen: CK_ULONG::try_from(std::mem::size_of::< | ||
| CK_RSA_PKCS_OAEP_PARAMS, |
There was a problem hiding this comment.
could we use the convenience macro sizeof! here from misc.rs?
| ulParameterLen: CK_ULONG::try_from(std::mem::size_of::< | |
| CK_RSA_PKCS_OAEP_PARAMS, | |
| ulParameterLen: sizeof!(CK_RSA_PKCS_OAEP_PARAMS), |
Description
This PR adds support for
CKM_RSA_AES_KEY_WRAPwrapping mechanism, that combines in one go RSA wrapping of a temporary AES key, then AES wrapping of any extractable key.Checklist
Reviewer's checklist: