-
Notifications
You must be signed in to change notification settings - Fork 388
Add support for DES3 wrap/unwrap #812
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
f80dc12
7efd531
5d712e3
5cd5a55
2245eb1
045d4fb
00c91b3
e86110b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1138,13 +1138,12 @@ CK_RV SoftHSM::C_GetMechanismInfo(CK_SLOT_ID slotID, CK_MECHANISM_TYPE type, CK_ | |
| /* FALLTHROUGH */ | ||
| #endif | ||
| case CKM_DES3_CBC: | ||
| pInfo->flags |= CKF_WRAP; | ||
| /* FALLTHROUGH */ | ||
| case CKM_DES3_ECB: | ||
| // Key size is not in use | ||
| pInfo->ulMinKeySize = 0; | ||
| pInfo->ulMaxKeySize = 0; | ||
| pInfo->flags |= CKF_ENCRYPT | CKF_DECRYPT; | ||
| pInfo->flags |= CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP; | ||
| break; | ||
| case CKM_DES3_CMAC: | ||
| // Key size is not in use | ||
|
|
@@ -6269,6 +6268,7 @@ CK_RV SoftHSM::WrapKeySym | |
| // Get the symmetric algorithm matching the mechanism | ||
| SymAlgo::Type algo = SymAlgo::Unknown; | ||
| SymWrap::Type mode = SymWrap::Unknown; | ||
| SymMode::Type sym_mode = SymMode::Unknown; | ||
| size_t bb = 8; | ||
| size_t blocksize = 0; | ||
| auto wrappedlen = keydata.size(); | ||
|
|
@@ -6291,22 +6291,44 @@ CK_RV SoftHSM::WrapKeySym | |
| #endif | ||
| case CKM_AES_CBC: | ||
| algo = SymAlgo::AES; | ||
| sym_mode = SymMode::CBC; | ||
| blocksize = 16; | ||
| if ((wrappedlen % 16) != 0) | ||
| return CKR_KEY_SIZE_RANGE; | ||
| break; | ||
|
|
||
| case CKM_AES_CBC_PAD: | ||
| blocksize = 16; | ||
| wrappedlen = RFC5652Pad(keydata, blocksize); | ||
| algo = SymAlgo::AES; | ||
| break; | ||
|
|
||
| case CKM_DES3_CBC: | ||
| algo = SymAlgo::DES3; | ||
| sym_mode = SymMode::CBC; | ||
| break; | ||
|
|
||
| case CKM_DES3_CBC_PAD: | ||
| blocksize = 8; | ||
| blocksize = 8; | ||
| wrappedlen = RFC5652Pad(keydata, blocksize); | ||
| algo = SymAlgo::DES3; | ||
| sym_mode = SymMode::CBC; | ||
| bb = 7; | ||
| break; | ||
|
|
||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| case CKM_DES3_CBC: | ||
| if ((wrappedlen % 8) != 0) | ||
| return CKR_KEY_SIZE_RANGE; | ||
| blocksize = 8; | ||
| algo = SymAlgo::DES3; | ||
| sym_mode = SymMode::CBC; | ||
| bb = 7; | ||
| break; | ||
|
|
||
| case CKM_DES3_ECB: | ||
| // ECB mode has no IV; keep blocksize=0 so iv remains empty | ||
| if ((wrappedlen % 8) != 0) | ||
| return CKR_KEY_SIZE_RANGE; | ||
| blocksize = 0; | ||
| algo = SymAlgo::DES3; | ||
| sym_mode = SymMode::ECB; | ||
| bb = 7; | ||
| break; | ||
|
Comment on lines
6293
to
6332
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainAdd targeted tests for new 3DES ECB wrap/unwrap and template enforcement
I can draft unit tests exercising these cases if helpful. Also applies to: 6923-6944, 7256-7312 🏁 Script executed: #!/bin/bash
# Sanity: ensure the new paths are compiled-in and referenced
rg -n 'CKM_DES3_ECB' -C3 src/lib/SoftHSM.cpp
rg -n 'CKA_VALUE_LEN' -C2 src/lib/SoftHSM.cppLength of output: 13141 Add tests for 3DES ECB wrap/unwrap and CKA_VALUE_LEN enforcement Please add unit tests covering the new DES3 ECB code paths and the template‐length checks: • Wrap (SoftHSM.cpp ~6323–6332) • Unwrap (SoftHSM.cpp ~7306–7312) • Secret-key template enforcement (SoftHSM.cpp ~7408–7510) Let me know if you’d like me to draft those unit tests. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @coderabbitai generate unit tests
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the test should be part of this PR rather than a different PR generated by Code Rabbit. Those tests should be also cleaned up and verified. The reason is that this should be tested in CI before merging. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
|
||
| default: | ||
|
|
@@ -6333,13 +6355,18 @@ CK_RV SoftHSM::WrapKeySym | |
| switch(pMechanism->mechanism) { | ||
|
|
||
| case CKM_AES_CBC: | ||
| case CKM_AES_CBC_PAD: | ||
| case CKM_AES_CBC_PAD: | ||
| case CKM_DES3_CBC: | ||
| case CKM_DES3_CBC_PAD: | ||
| iv.resize(blocksize); | ||
| memcpy(&iv[0], pMechanism->pParameter, blocksize); | ||
|
|
||
| if (!cipher->encryptInit(wrappingkey, SymMode::CBC, iv, false)) | ||
| case CKM_DES3_CBC_PAD: | ||
| case CKM_DES3_ECB: | ||
| if (blocksize > 0) { | ||
| iv.resize(blocksize); | ||
| memcpy(&iv[0], pMechanism->pParameter, blocksize); | ||
| } else { | ||
| iv.wipe(); | ||
| } | ||
|
|
||
| if (!cipher->encryptInit(wrappingkey, sym_mode, iv, false)) | ||
| { | ||
| cipher->recycleKey(wrappingkey); | ||
| CryptoFactory::i()->recycleSymmetricAlgorithm(cipher); | ||
|
|
@@ -6612,6 +6639,15 @@ CK_RV SoftHSM::C_WrapKey | |
| pMechanism->ulParameterLen != 16) | ||
| return CKR_ARGUMENTS_BAD; | ||
| break; | ||
| case CKM_DES3_ECB: | ||
| if (pMechanism->ulParameterLen != 0) | ||
| return CKR_ARGUMENTS_BAD; // no IV for ECB; allow NULL or non-NULL pointer when length==0 | ||
| break; | ||
| case CKM_DES3_CBC: | ||
| case CKM_DES3_CBC_PAD: | ||
| if (pMechanism->pParameter == NULL_PTR || pMechanism->ulParameterLen != 8) | ||
| return CKR_ARGUMENTS_BAD; | ||
| break; | ||
| default: | ||
| return CKR_MECHANISM_INVALID; | ||
| } | ||
|
|
@@ -6652,9 +6688,11 @@ CK_RV SoftHSM::C_WrapKey | |
| return CKR_WRAPPING_KEY_TYPE_INCONSISTENT; | ||
| if ((pMechanism->mechanism == CKM_AES_CBC || pMechanism->mechanism == CKM_AES_CBC_PAD) && wrapKey->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED) != CKK_AES) | ||
| return CKR_WRAPPING_KEY_TYPE_INCONSISTENT; | ||
| if (pMechanism->mechanism == CKM_DES3_CBC && (wrapKey->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED) != CKK_DES2 || | ||
| wrapKey->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED) != CKK_DES3)) | ||
| return CKR_WRAPPING_KEY_TYPE_INCONSISTENT; | ||
| if ((pMechanism->mechanism == CKM_DES3_CBC || pMechanism->mechanism == CKM_DES3_CBC_PAD || pMechanism->mechanism == CKM_DES3_ECB) && | ||
| ((wrapKey->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED) != CKK_DES2 && | ||
| wrapKey->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED) != CKK_DES3))) { | ||
| return CKR_WRAPPING_KEY_TYPE_INCONSISTENT; | ||
| } | ||
|
|
||
| // Check if the wrapping key can be used for wrapping | ||
| if (wrapKey->getBooleanValue(CKA_WRAP, false) == false) | ||
|
|
@@ -6865,6 +6903,7 @@ CK_RV SoftHSM::UnwrapKeySym | |
| // Get the symmetric algorithm matching the mechanism | ||
| SymAlgo::Type algo = SymAlgo::Unknown; | ||
| SymWrap::Type mode = SymWrap::Unknown; | ||
| SymMode::Type sym_mode = SymMode::Unknown; | ||
| size_t bb = 8; | ||
| size_t blocksize = 0; | ||
|
|
||
|
|
@@ -6881,16 +6920,28 @@ CK_RV SoftHSM::UnwrapKeySym | |
| mode = SymWrap::AES_KEYWRAP_PAD; | ||
| break; | ||
| #endif | ||
| case CKM_AES_CBC_PAD: | ||
| case CKM_AES_CBC_PAD: | ||
| algo = SymAlgo::AES; | ||
| sym_mode = SymMode::CBC; | ||
| blocksize = 16; | ||
| break; | ||
|
|
||
| case CKM_DES3_CBC_PAD: | ||
|
|
||
| case CKM_DES3_CBC_PAD: | ||
| case CKM_DES3_CBC: | ||
| algo = SymAlgo::DES3; | ||
| sym_mode = SymMode::CBC; | ||
| blocksize = 8; | ||
| break; | ||
|
|
||
| bb = 7; | ||
| break; | ||
|
|
||
| case CKM_DES3_ECB: | ||
| algo = SymAlgo::DES3; | ||
| sym_mode = SymMode::ECB; | ||
| // ECB mode has no IV; keep blocksize=0 so iv remains empty | ||
| blocksize = 0; | ||
| bb = 7; | ||
| break; | ||
|
|
||
| default: | ||
| return CKR_MECHANISM_INVALID; | ||
| } | ||
|
|
@@ -6916,39 +6967,43 @@ CK_RV SoftHSM::UnwrapKeySym | |
|
|
||
| switch(pMechanism->mechanism) { | ||
|
|
||
| case CKM_AES_CBC_PAD: | ||
| case CKM_DES3_CBC_PAD: | ||
| iv.resize(blocksize); | ||
| memcpy(&iv[0], pMechanism->pParameter, blocksize); | ||
|
|
||
| if (!cipher->decryptInit(unwrappingkey, SymMode::CBC, iv, false)) | ||
| { | ||
| cipher->recycleKey(unwrappingkey); | ||
| CryptoFactory::i()->recycleSymmetricAlgorithm(cipher); | ||
| return CKR_MECHANISM_INVALID; | ||
| } | ||
| if (!cipher->decryptUpdate(wrapped, keydata)) | ||
| { | ||
| cipher->recycleKey(unwrappingkey); | ||
| CryptoFactory::i()->recycleSymmetricAlgorithm(cipher); | ||
| return CKR_GENERAL_ERROR; | ||
| } | ||
| // Finalize encryption | ||
| if (!cipher->decryptFinal(decryptedFinal)) | ||
| { | ||
| cipher->recycleKey(unwrappingkey); | ||
| CryptoFactory::i()->recycleSymmetricAlgorithm(cipher); | ||
| return CKR_GENERAL_ERROR; | ||
| } | ||
| keydata += decryptedFinal; | ||
| case CKM_AES_CBC_PAD: | ||
| case CKM_DES3_CBC_PAD: | ||
| case CKM_DES3_CBC: | ||
| case CKM_DES3_ECB: | ||
| if (blocksize > 0) { | ||
| iv.resize(blocksize); | ||
| memcpy(&iv[0], pMechanism->pParameter, blocksize); | ||
| } else { | ||
| iv.wipe(); | ||
| } | ||
|
|
||
| if(!RFC5652Unpad(keydata,blocksize)) | ||
| { | ||
| cipher->recycleKey(unwrappingkey); | ||
| CryptoFactory::i()->recycleSymmetricAlgorithm(cipher); | ||
| return CKR_GENERAL_ERROR; // TODO should be another error | ||
| } | ||
| break; | ||
| if (!cipher->decryptInit(unwrappingkey, sym_mode, iv, false)) { | ||
| cipher->recycleKey(unwrappingkey); | ||
| CryptoFactory::i()->recycleSymmetricAlgorithm(cipher); | ||
| return CKR_MECHANISM_INVALID; | ||
| } | ||
| if (!cipher->decryptUpdate(wrapped, keydata)) { | ||
| cipher->recycleKey(unwrappingkey); | ||
| CryptoFactory::i()->recycleSymmetricAlgorithm(cipher); | ||
| return CKR_GENERAL_ERROR; | ||
| } | ||
| // Finalize decryption | ||
| if (!cipher->decryptFinal(decryptedFinal)) { | ||
| cipher->recycleKey(unwrappingkey); | ||
| CryptoFactory::i()->recycleSymmetricAlgorithm(cipher); | ||
| return CKR_WRAPPED_KEY_INVALID; | ||
| } | ||
| keydata += decryptedFinal; | ||
|
|
||
| if (pMechanism->mechanism == CKM_AES_CBC_PAD || pMechanism->mechanism == CKM_DES3_CBC_PAD) { | ||
| if (!RFC5652Unpad(keydata, blocksize)) { | ||
| cipher->recycleKey(unwrappingkey); | ||
| CryptoFactory::i()->recycleSymmetricAlgorithm(cipher); | ||
| return CKR_WRAPPED_KEY_INVALID; | ||
| } | ||
| } | ||
| break; | ||
|
|
||
| default: | ||
| // Unwrap the key | ||
|
|
@@ -7227,20 +7282,34 @@ CK_RV SoftHSM::C_UnwrapKey | |
| return rv; | ||
| break; | ||
|
|
||
| case CKM_AES_CBC_PAD: | ||
| // TODO check block length | ||
| if (pMechanism->pParameter == NULL_PTR || | ||
| pMechanism->ulParameterLen != 16) | ||
| case CKM_AES_CBC_PAD: | ||
| if (pMechanism->pParameter == NULL_PTR || pMechanism->ulParameterLen != 16) | ||
| return CKR_ARGUMENTS_BAD; | ||
| if (ulWrappedKeyLen == 0 || (ulWrappedKeyLen % 16) != 0) | ||
| return CKR_WRAPPED_KEY_LEN_RANGE; | ||
| break; | ||
|
|
||
| case CKM_DES3_CBC_PAD: | ||
| // TODO check block length | ||
| if (pMechanism->pParameter == NULL_PTR || | ||
| pMechanism->ulParameterLen != 8) | ||
| case CKM_DES3_CBC_PAD: | ||
| if (pMechanism->pParameter == NULL_PTR || pMechanism->ulParameterLen != 8) | ||
| return CKR_ARGUMENTS_BAD; | ||
| if (ulWrappedKeyLen == 0 || (ulWrappedKeyLen % 8) != 0) | ||
| return CKR_WRAPPED_KEY_LEN_RANGE; | ||
| break; | ||
|
|
||
| case CKM_DES3_CBC: | ||
| if (pMechanism->pParameter == NULL_PTR || pMechanism->ulParameterLen != 8) | ||
| return CKR_ARGUMENTS_BAD; | ||
| if ((ulWrappedKeyLen % 8) != 0) | ||
| return CKR_WRAPPED_KEY_LEN_RANGE; | ||
| break; | ||
|
|
||
|
|
||
| case CKM_DES3_ECB: | ||
| if (pMechanism->ulParameterLen != 0) | ||
| return CKR_ARGUMENTS_BAD; // ECB takes no IV; allow NULL or non-NULL pointer when length==0 | ||
| if ((ulWrappedKeyLen % 8) != 0) | ||
| return CKR_WRAPPED_KEY_LEN_RANGE; | ||
| break; | ||
|
|
||
| default: | ||
| return CKR_MECHANISM_INVALID; | ||
| } | ||
|
|
@@ -7279,12 +7348,14 @@ CK_RV SoftHSM::C_UnwrapKey | |
| if ((pMechanism->mechanism == CKM_RSA_PKCS || pMechanism->mechanism == CKM_RSA_PKCS_OAEP || pMechanism->mechanism == CKM_RSA_AES_KEY_WRAP) && | ||
| unwrapKey->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED) != CKK_RSA) | ||
| return CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT; | ||
| if ((pMechanism->mechanism == CKM_AES_CBC || pMechanism->mechanism == CKM_AES_CBC_PAD) && unwrapKey->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED) != CKK_AES) | ||
| return CKR_WRAPPING_KEY_TYPE_INCONSISTENT; | ||
| if (pMechanism->mechanism == CKM_DES3_CBC && (unwrapKey->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED) != CKK_DES2 || | ||
| unwrapKey->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED) != CKK_DES3)) | ||
| return CKR_WRAPPING_KEY_TYPE_INCONSISTENT; | ||
|
|
||
| if ((pMechanism->mechanism == CKM_AES_CBC || pMechanism->mechanism == CKM_AES_CBC_PAD) && | ||
| unwrapKey->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED) != CKK_AES) | ||
| return CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT; | ||
| if ((pMechanism->mechanism == CKM_DES3_CBC_PAD || pMechanism->mechanism == CKM_DES3_CBC || pMechanism->mechanism == CKM_DES3_ECB) && | ||
| (unwrapKey->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED) != CKK_DES2 && | ||
| unwrapKey->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED) != CKK_DES3)) | ||
| return CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT; | ||
|
|
||
| // Check if the unwrapping key can be used for unwrapping | ||
| if (unwrapKey->getBooleanValue(CKA_UNWRAP, false) == false) | ||
| return CKR_KEY_FUNCTION_NOT_PERMITTED; | ||
|
|
@@ -7334,7 +7405,11 @@ CK_RV SoftHSM::C_UnwrapKey | |
| }; | ||
| CK_ULONG secretAttribsCount = 4; | ||
|
|
||
| // Add the additional | ||
| // Capture optional CKA_VALUE_LEN from the template (for compatibility) | ||
| CK_ULONG requestedValueLen = 0; | ||
| bool haveRequestedValueLen = false; | ||
|
|
||
| // Add the additional attributes, skipping read-only for unwrap and capturing CKA_VALUE_LEN | ||
| if (ulCount > (maxAttribs - secretAttribsCount)) | ||
| return CKR_TEMPLATE_INCONSISTENT; | ||
| for (CK_ULONG i = 0; i < ulCount; ++i) | ||
|
|
@@ -7346,6 +7421,14 @@ CK_RV SoftHSM::C_UnwrapKey | |
| case CKA_PRIVATE: | ||
| case CKA_KEY_TYPE: | ||
| continue; | ||
| case CKA_VALUE_LEN: | ||
| // Accept a caller-provided length for compatibility, but do not set it here; we will verify after unwrap | ||
| if (pTemplate[i].ulValueLen != sizeof(CK_ULONG)) { | ||
| return CKR_TEMPLATE_INCONSISTENT; | ||
| } | ||
| requestedValueLen = *(CK_ULONG *) pTemplate[i].pValue; | ||
| haveRequestedValueLen = true; | ||
| continue; | ||
| default: | ||
| secretAttribs[secretAttribsCount++] = pTemplate[i]; | ||
| } | ||
|
|
@@ -7420,6 +7503,12 @@ CK_RV SoftHSM::C_UnwrapKey | |
| return rv; | ||
| } | ||
|
|
||
| // If provided, enforce CKA_VALUE_LEN only for secret keys | ||
| if (objClass == CKO_SECRET_KEY && haveRequestedValueLen && keydata.size() != requestedValueLen) | ||
| { | ||
| return CKR_TEMPLATE_INCONSISTENT; | ||
| } | ||
|
|
||
| // Create the secret object using C_CreateObject | ||
| rv = this->CreateObject(hSession, secretAttribs, secretAttribsCount, hKey, OBJECT_OP_UNWRAP); | ||
|
|
||
|
|
@@ -12044,7 +12133,7 @@ CK_RV SoftHSM::deriveSymmetric | |
| // attributes set to CK_TRUE | ||
| bool bNeverExtractable = baseKey->getBooleanValue(CKA_NEVER_EXTRACTABLE, false) && | ||
| otherKey->getBooleanValue(CKA_NEVER_EXTRACTABLE, false); | ||
| bOK = bOK && osobject->setAttribute(CKA_ALWAYS_SENSITIVE, bNeverExtractable); | ||
| bOK = bOK && osobject->setAttribute(CKA_NEVER_EXTRACTABLE, bNeverExtractable); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is an unrelated change which should be in its own PR with a test. |
||
| } | ||
| else if (pMechanism->mechanism == CKM_CONCATENATE_BASE_AND_DATA || | ||
| pMechanism->mechanism == CKM_CONCATENATE_DATA_AND_BASE) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is already being fixed in #728