diff --git a/src/lib/SoftHSM.cpp b/src/lib/SoftHSM.cpp index 1a69b68cd..362995069 100644 --- a/src/lib/SoftHSM.cpp +++ b/src/lib/SoftHSM.cpp @@ -127,6 +127,28 @@ std::auto_ptr* SoftHSM::instance = new std::auto_ptr(); #endif +// Key types that are handled as generic secret key objects +static bool isGenericSecretKeyType(CK_KEY_TYPE keyType) +{ + switch (keyType) + { + case CKK_GENERIC_SECRET: + case CKK_MD5_HMAC: + case CKK_SHA_1_HMAC: + case CKK_SHA224_HMAC: + case CKK_SHA256_HMAC: + case CKK_SHA384_HMAC: + case CKK_SHA512_HMAC: + case CKK_SHA3_224_HMAC: + case CKK_SHA3_256_HMAC: + case CKK_SHA3_384_HMAC: + case CKK_SHA3_512_HMAC: + return true; + default: + return false; + } +} + static CK_RV newP11Object(CK_OBJECT_CLASS objClass, CK_KEY_TYPE keyType, CK_CERTIFICATE_TYPE certType, P11Object **p11object) { switch(objClass) { @@ -191,17 +213,7 @@ static CK_RV newP11Object(CK_OBJECT_CLASS objClass, CK_KEY_TYPE keyType, CK_CERT return CKR_ATTRIBUTE_VALUE_INVALID; break; case CKO_SECRET_KEY: - if ((keyType == CKK_GENERIC_SECRET) || - (keyType == CKK_MD5_HMAC) || - (keyType == CKK_SHA_1_HMAC) || - (keyType == CKK_SHA224_HMAC) || - (keyType == CKK_SHA256_HMAC) || - (keyType == CKK_SHA384_HMAC) || - (keyType == CKK_SHA512_HMAC) || - (keyType == CKK_SHA3_224_HMAC) || - (keyType == CKK_SHA3_256_HMAC) || - (keyType == CKK_SHA3_384_HMAC) || - (keyType == CKK_SHA3_512_HMAC)) + if (isGenericSecretKeyType(keyType)) { P11GenericSecretKeyObj* key = new P11GenericSecretKeyObj(); *p11object = key; @@ -1291,27 +1303,22 @@ CK_RV SoftHSM::C_GetMechanismInfo(CK_SLOT_ID slotID, CK_MECHANISM_TYPE type, CK_ pInfo->flags = CKF_GENERATE; break; #ifndef WITH_FIPS - case CKM_DES_CBC_PAD: - /* FALLTHROUGH */ -#endif - case CKM_DES3_CBC_PAD: - pInfo->flags = CKF_WRAP | CKF_UNWRAP; - /* FALLTHROUGH */ -#ifndef WITH_FIPS - /* FALLTHROUGH - extra needed due to gcc issue. */ case CKM_DES_ECB: - /* FALLTHROUGH */ case CKM_DES_CBC: - /* FALLTHROUGH */ + case CKM_DES_CBC_PAD: + // Key size is not in use + pInfo->ulMinKeySize = 0; + pInfo->ulMaxKeySize = 0; + pInfo->flags = CKF_ENCRYPT | CKF_DECRYPT; + break; #endif - case CKM_DES3_CBC: - pInfo->flags |= CKF_WRAP; - /* FALLTHROUGH */ case CKM_DES3_ECB: + case CKM_DES3_CBC: + case CKM_DES3_CBC_PAD: // 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 @@ -7219,14 +7226,23 @@ CK_RV SoftHSM::WrapKeySym algo = SymAlgo::AES; break; + case CKM_DES3_ECB: case CKM_DES3_CBC: + // [PKCS#11 v2.40, General block cipher ECB and CBC] + // The key value is padded on the trailing end with up to + // block size minus one null bytes. + blocksize = 8; + wrappedlen = RFC3394Pad(keydata); algo = SymAlgo::DES3; + // DES key length is counted without the parity bits + bb = 7; break; case CKM_DES3_CBC_PAD: blocksize = 8; wrappedlen = RFC5652Pad(keydata, blocksize); algo = SymAlgo::DES3; + bb = 7; break; default: @@ -7254,12 +7270,16 @@ CK_RV SoftHSM::WrapKeySym case CKM_AES_CBC: case CKM_AES_CBC_PAD: + case CKM_DES3_ECB: case CKM_DES3_CBC: case CKM_DES3_CBC_PAD: - iv.resize(blocksize); - memcpy(&iv[0], pMechanism->pParameter, blocksize); + if (pMechanism->mechanism != CKM_DES3_ECB) + { + iv.resize(blocksize); + memcpy(&iv[0], pMechanism->pParameter, blocksize); + } - if (!cipher->encryptInit(wrappingkey, SymMode::CBC, iv, false)) + if (!cipher->encryptInit(wrappingkey, pMechanism->mechanism == CKM_DES3_ECB ? SymMode::ECB : SymMode::CBC, iv, false)) { cipher->recycleKey(wrappingkey); CryptoFactory::i()->recycleSymmetricAlgorithm(cipher); @@ -7552,6 +7572,17 @@ CK_RV SoftHSM::C_WrapKey pMechanism->ulParameterLen != 16) return CKR_ARGUMENTS_BAD; break; + case CKM_DES3_ECB: + if (pMechanism->pParameter != NULL_PTR || + pMechanism->ulParameterLen != 0) + return CKR_ARGUMENTS_BAD; + 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; } @@ -7592,8 +7623,9 @@ 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)) + if ((pMechanism->mechanism == CKM_DES3_ECB || pMechanism->mechanism == CKM_DES3_CBC || pMechanism->mechanism == CKM_DES3_CBC_PAD) && + 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 @@ -7634,6 +7666,9 @@ CK_RV SoftHSM::C_WrapKey // CKM_RSA_PKCS and CKM_RSA_PKCS_OAEP can be used only on SECRET keys: PKCS#11 2.40 draft 2 section 2.1.6 PKCS #1 v1.5 RSA & section 2.1.8 PKCS #1 RSA OAEP if ((pMechanism->mechanism == CKM_RSA_PKCS || pMechanism->mechanism == CKM_RSA_PKCS_OAEP) && keyClass != CKO_SECRET_KEY) return CKR_KEY_NOT_WRAPPABLE; + // Block cipher ECB and CBC without padding can wrap only SECRET keys: PKCS#11 2.40 General block cipher ECB and CBC + if ((pMechanism->mechanism == CKM_DES3_ECB || pMechanism->mechanism == CKM_DES3_CBC) && keyClass != CKO_SECRET_KEY) + return CKR_KEY_NOT_WRAPPABLE; // Verify the wrap template attribute if (wrapKey->attributeExists(CKA_WRAP_TEMPLATE)) @@ -7847,9 +7882,13 @@ CK_RV SoftHSM::UnwrapKeySym blocksize = 16; break; + case CKM_DES3_ECB: + case CKM_DES3_CBC: case CKM_DES3_CBC_PAD: algo = SymAlgo::DES3; blocksize = 8; + // DES key length is counted without the parity bits + bb = 7; break; default: @@ -7878,10 +7917,15 @@ CK_RV SoftHSM::UnwrapKeySym switch(pMechanism->mechanism) { case CKM_AES_CBC: - iv.resize(blocksize); - memcpy(&iv[0], pMechanism->pParameter, blocksize); + case CKM_DES3_ECB: + case CKM_DES3_CBC: + if (pMechanism->mechanism != CKM_DES3_ECB) + { + iv.resize(blocksize); + memcpy(&iv[0], pMechanism->pParameter, blocksize); + } - if (!cipher->decryptInit(unwrappingkey, SymMode::CBC, iv, false)) + if (!cipher->decryptInit(unwrappingkey, pMechanism->mechanism == CKM_DES3_ECB ? SymMode::ECB : SymMode::CBC, iv, false)) { cipher->recycleKey(unwrappingkey); CryptoFactory::i()->recycleSymmetricAlgorithm(cipher); @@ -7901,7 +7945,8 @@ CK_RV SoftHSM::UnwrapKeySym return CKR_GENERAL_ERROR; } keydata += decryptedFinal; - // No unpadding for CKM_AES_CBC - returns raw decrypted data + // No unpadding - returns raw decrypted data, trailing null + // bytes are truncated by the caller based on the key type break; case CKM_AES_CBC_PAD: @@ -7934,7 +7979,7 @@ CK_RV SoftHSM::UnwrapKeySym { cipher->recycleKey(unwrappingkey); CryptoFactory::i()->recycleSymmetricAlgorithm(cipher); - return CKR_GENERAL_ERROR; // TODO should be another error + return CKR_WRAPPED_KEY_INVALID; } break; @@ -8236,14 +8281,25 @@ CK_RV SoftHSM::C_UnwrapKey case CKM_AES_CBC: case CKM_AES_CBC_PAD: - // TODO check block length + if ((ulWrappedKeyLen == 0) || ((ulWrappedKeyLen % 16) != 0)) + return CKR_WRAPPED_KEY_LEN_RANGE; if (pMechanism->pParameter == NULL_PTR || pMechanism->ulParameterLen != 16) return CKR_ARGUMENTS_BAD; break; + case CKM_DES3_ECB: + if ((ulWrappedKeyLen == 0) || ((ulWrappedKeyLen % 8) != 0)) + return CKR_WRAPPED_KEY_LEN_RANGE; + if (pMechanism->pParameter != NULL_PTR || + pMechanism->ulParameterLen != 0) + return CKR_ARGUMENTS_BAD; + break; + + case CKM_DES3_CBC: case CKM_DES3_CBC_PAD: - // TODO check block length + if ((ulWrappedKeyLen == 0) || ((ulWrappedKeyLen % 8) != 0)) + return CKR_WRAPPED_KEY_LEN_RANGE; if (pMechanism->pParameter == NULL_PTR || pMechanism->ulParameterLen != 8) return CKR_ARGUMENTS_BAD; @@ -8289,9 +8345,10 @@ CK_RV SoftHSM::C_UnwrapKey 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_DES3_ECB || pMechanism->mechanism == CKM_DES3_CBC || pMechanism->mechanism == CKM_DES3_CBC_PAD) && + 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) @@ -8318,6 +8375,9 @@ CK_RV SoftHSM::C_UnwrapKey // Report errors and/or unexpected usage. if (objClass != CKO_SECRET_KEY && objClass != CKO_PRIVATE_KEY) return CKR_ATTRIBUTE_VALUE_INVALID; + // Block cipher ECB and CBC without padding can unwrap only SECRET keys + if ((pMechanism->mechanism == CKM_DES3_ECB || pMechanism->mechanism == CKM_DES3_CBC) && objClass != CKO_SECRET_KEY) + return CKR_TEMPLATE_INCONSISTENT; // Key type will be handled at object creation // Check authorization @@ -8342,6 +8402,12 @@ CK_RV SoftHSM::C_UnwrapKey }; CK_ULONG secretAttribsCount = 4; + // Block cipher ECB and CBC without padding use CKA_VALUE_LEN, if the + // key type supports it, to truncate the unwrapped key value + bool isUnpadded = pMechanism->mechanism == CKM_DES3_ECB || pMechanism->mechanism == CKM_DES3_CBC; + CK_ULONG valueLen = 0; + bool haveValueLen = false; + // Add the additional if (ulCount > (maxAttribs - secretAttribsCount)) return CKR_TEMPLATE_INCONSISTENT; @@ -8354,6 +8420,17 @@ CK_RV SoftHSM::C_UnwrapKey case CKA_PRIVATE: case CKA_KEY_TYPE: continue; + case CKA_VALUE_LEN: + if (isUnpadded && isGenericSecretKeyType(keyType)) + { + if (pTemplate[i].pValue == NULL_PTR || pTemplate[i].ulValueLen != sizeof(CK_ULONG)) + return CKR_ATTRIBUTE_VALUE_INVALID; + valueLen = *(CK_ULONG*)pTemplate[i].pValue; + haveValueLen = true; + continue; + } + secretAttribs[secretAttribsCount++] = pTemplate[i]; + break; default: secretAttribs[secretAttribsCount++] = pTemplate[i]; } @@ -8428,6 +8505,36 @@ CK_RV SoftHSM::C_UnwrapKey return rv; } + // Block cipher ECB and CBC without padding: truncate the trailing + // null bytes added when wrapping, based on the key type or CKA_VALUE_LEN + if (isUnpadded) + { + size_t keyLen = keydata.size(); + switch (keyType) + { + case CKK_DES: + keyLen = 8; + break; + case CKK_DES2: + keyLen = 16; + break; + case CKK_DES3: + keyLen = 24; + break; + default: + if (haveValueLen) + { + if (valueLen == 0 || valueLen > keydata.size()) + return CKR_TEMPLATE_INCONSISTENT; + keyLen = valueLen; + } + break; + } + if (keyLen > keydata.size()) + return CKR_WRAPPED_KEY_INVALID; + keydata.resize(keyLen); + } + // Create the secret object using C_CreateObject rv = this->CreateObject(hSession, secretAttribs, secretAttribsCount, hKey, OBJECT_OP_UNWRAP); @@ -8459,6 +8566,8 @@ CK_RV SoftHSM::C_UnwrapKey else value = keydata; bOK = bOK && osobject->setAttribute(CKA_VALUE, value); + if (osobject->attributeExists(CKA_VALUE_LEN)) + bOK = bOK && osobject->setAttribute(CKA_VALUE_LEN, (unsigned long)keydata.size()); } else if (keyType == CKK_RSA) { diff --git a/src/lib/test/SymmetricAlgorithmTests.cpp b/src/lib/test/SymmetricAlgorithmTests.cpp index 46f717b30..64e717714 100644 --- a/src/lib/test/SymmetricAlgorithmTests.cpp +++ b/src/lib/test/SymmetricAlgorithmTests.cpp @@ -648,7 +648,7 @@ std::vector aesCBCWrappedKeys { std::vector desCBCWrappedKeys { #ifndef WITH_FIPS - rsa2048underdes56, + // rsa2048underdes56 is not included, single DES does not support unwrapping rsa2048underdes112, #endif rsa2048underdes168, @@ -1319,6 +1319,77 @@ inline void SymmetricAlgorithmTests::desWrapUnwrapRsa(CK_MECHANISM_TYPE mechanis wrapUnwrapRsa(mechanismType, hSession, hKey); } +// Wrap a secret key with a DES2/DES3 key, compare the result against +// expectedWrapped (if not empty), unwrap it again and check the value. +void SymmetricAlgorithmTests::des3WrapUnwrapSecret(CK_MECHANISM_TYPE mechanismType, + CK_SESSION_HANDLE hSession, + CK_OBJECT_HANDLE hKey, + CK_KEY_TYPE keyType, + const Bytes &keyValue, + const Bytes &expectedWrapped, + const Bytes &expectedUnwrapped) +{ + CK_BYTE iv[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }; + CK_MECHANISM mechanism = { mechanismType, NULL_PTR, 0 }; + if (mechanismType != CKM_DES3_ECB) + { + mechanism.pParameter = iv; + mechanism.ulParameterLen = sizeof(iv); + } + CK_BBOOL bFalse = CK_FALSE; + CK_BBOOL bTrue = CK_TRUE; + CK_OBJECT_CLASS secretClass = CKO_SECRET_KEY; + CK_ATTRIBUTE attribs[] = { + { CKA_CLASS, &secretClass, sizeof(secretClass) }, + { CKA_KEY_TYPE, &keyType, sizeof(keyType) }, + { CKA_TOKEN, &bFalse, sizeof(bFalse) }, + { CKA_PRIVATE, &bTrue, sizeof(bTrue) }, + { CKA_SENSITIVE, &bFalse, sizeof(bFalse) }, + { CKA_EXTRACTABLE, &bTrue, sizeof(bTrue) }, + { CKA_VALUE, const_cast(keyValue.data()), static_cast(keyValue.size()) } + }; + + CK_OBJECT_HANDLE hSecret = CK_INVALID_HANDLE; + CK_RV rv = CRYPTOKI_F_PTR( C_CreateObject(hSession, attribs, sizeof(attribs)/sizeof(CK_ATTRIBUTE), &hSecret) ); + CPPUNIT_ASSERT(rv == CKR_OK); + + // Wrapped length is the key padded to the DES block size + const CK_ULONG keyLen = static_cast(keyValue.size()); + CK_ULONG expectedLen = (keyLen + 7) & ~7UL; + if (mechanismType == CKM_DES3_CBC_PAD) + expectedLen = keyLen + 8 - (keyLen % 8); + + CK_ULONG wrappedLen = 0UL; + rv = CRYPTOKI_F_PTR( C_WrapKey(hSession, &mechanism, hKey, hSecret, NULL_PTR, &wrappedLen) ); + CPPUNIT_ASSERT(rv == CKR_OK); + CPPUNIT_ASSERT(wrappedLen == expectedLen); + + Bytes wrapped(wrappedLen); + rv = CRYPTOKI_F_PTR( C_WrapKey(hSession, &mechanism, hKey, hSecret, wrapped.data(), &wrappedLen) ); + CPPUNIT_ASSERT(rv == CKR_OK); + CPPUNIT_ASSERT(wrappedLen == expectedLen); + if (!expectedWrapped.empty()) + CPPUNIT_ASSERT(wrapped == expectedWrapped); + + rv = CRYPTOKI_F_PTR( C_DestroyObject(hSession, hSecret) ); + CPPUNIT_ASSERT(rv == CKR_OK); + + CK_OBJECT_HANDLE hNew = CK_INVALID_HANDLE; + rv = CRYPTOKI_F_PTR( C_UnwrapKey(hSession, &mechanism, hKey, wrapped.data(), wrappedLen, attribs, sizeof(attribs)/sizeof(CK_ATTRIBUTE) - 1, &hNew) ); + CPPUNIT_ASSERT(rv == CKR_OK); + CPPUNIT_ASSERT(hNew != CK_INVALID_HANDLE); + + Bytes unwrapped(wrappedLen); + CK_ATTRIBUTE valueAttrib = { CKA_VALUE, unwrapped.data(), static_cast(unwrapped.size()) }; + rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSession, hNew, &valueAttrib, 1) ); + CPPUNIT_ASSERT(rv == CKR_OK); + unwrapped.resize(valueAttrib.ulValueLen); + CPPUNIT_ASSERT(unwrapped == expectedUnwrapped); + + rv = CRYPTOKI_F_PTR( C_DestroyObject(hSession, hNew) ); + CPPUNIT_ASSERT(rv == CKR_OK); +} + void SymmetricAlgorithmTests::wrapUnwrapRsa(CK_MECHANISM_TYPE mechanismType, CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hKey) { @@ -1897,27 +1968,276 @@ void SymmetricAlgorithmTests::testDesWrapUnwrap() rv = CRYPTOKI_F_PTR( C_Login(hSession,CKU_USER,m_userPin1,m_userPin1Length) ); CPPUNIT_ASSERT(rv==CKR_OK); - std::array hKey { CK_INVALID_HANDLE, CK_INVALID_HANDLE, CK_INVALID_HANDLE }; + const CK_MECHANISM_TYPE mechanisms[] = { CKM_DES3_ECB, CKM_DES3_CBC, CKM_DES3_CBC_PAD }; + // All 3DES block cipher mechanisms can wrap and unwrap + CK_MECHANISM_INFO mechInfo; + for (auto mechanismType : mechanisms) + { + rv = CRYPTOKI_F_PTR( C_GetMechanismInfo(m_initializedTokenSlotID, mechanismType, &mechInfo) ); + CPPUNIT_ASSERT(rv == CKR_OK); + CPPUNIT_ASSERT(mechInfo.flags & CKF_WRAP); + CPPUNIT_ASSERT(mechInfo.flags & CKF_UNWRAP); + } #ifndef WITH_FIPS - rv = generateDesKey(hSession,IN_SESSION,IS_PUBLIC,hKey[0]); - CPPUNIT_ASSERT(rv == CKR_OK); - rv = generateDes2Key(hSession,IN_SESSION,IS_PUBLIC,hKey[1]); + // Single DES does not support wrapping + rv = CRYPTOKI_F_PTR( C_GetMechanismInfo(m_initializedTokenSlotID, CKM_DES_CBC, &mechInfo) ); CPPUNIT_ASSERT(rv == CKR_OK); + CPPUNIT_ASSERT((mechInfo.flags & (CKF_WRAP | CKF_UNWRAP)) == 0); #endif - rv = generateDes3Key(hSession,IN_SESSION,IS_PUBLIC,hKey[2]); + + // Keying option 1 key from NIST SP 800-67 + const Bytes des3Key { + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, + 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23 + }; + const Bytes des2Key { + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10 + }; + const Bytes des3Value { + 0x8a, 0x7b, 0x6c, 0x5d, 0x4e, 0x3f, 0x2a, 0x1b, + 0x1a, 0x2b, 0x3c, 0x4d, 0x5e, 0x6f, 0x7a, 0x8b, + 0xc1, 0xd2, 0xe3, 0xf4, 0xa5, 0xb6, 0xc7, 0xd8 + }; + const Bytes des2Value(des3Key.begin(), des3Key.begin() + 16); + const Bytes aesValue { + 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c + }; + // Not a multiple of the DES block size + const Bytes secretValue { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13 + }; + Bytes secretValuePadded(secretValue); + secretValuePadded.resize(24, 0x00); + + CK_OBJECT_HANDLE hDes3 = CK_INVALID_HANDLE; + rv = importDes3Key(hSession, IN_SESSION, IS_PUBLIC, hDes3, des3Key); CPPUNIT_ASSERT(rv == CKR_OK); + std::vector wrappingKeys { hDes3 }; +#ifndef WITH_FIPS + CK_OBJECT_HANDLE hDes2 = CK_INVALID_HANDLE; + rv = importDes2Key(hSession, IN_SESSION, IS_PUBLIC, hDes2, des2Key); + CPPUNIT_ASSERT(rv == CKR_OK); + wrappingKeys.push_back(hDes2); +#endif + // Known answers, computed independently with OpenSSL's des-ede3 and des-ede ciphers. + // ECB and CBC pad the key value with null bytes, which are kept on unwrap + // when the key type does not define the length. + des3WrapUnwrapSecret(CKM_DES3_ECB, hSession, hDes3, CKK_GENERIC_SECRET, secretValue, + { + 0x30, 0x32, 0x92, 0x53, 0xbd, 0x29, 0x65, 0x40, + 0x2e, 0xa4, 0x37, 0xbe, 0x92, 0x66, 0x17, 0x8c, + 0x12, 0x3e, 0x17, 0x52, 0xb2, 0x69, 0x1a, 0x27 + }, + secretValuePadded); + des3WrapUnwrapSecret(CKM_DES3_CBC_PAD, hSession, hDes3, CKK_GENERIC_SECRET, secretValue, + { + 0x4e, 0xba, 0x73, 0x9c, 0x99, 0x8b, 0xcb, 0x60, + 0x20, 0x12, 0xc4, 0x74, 0x5e, 0xe8, 0x66, 0x10, + 0x80, 0xcd, 0x65, 0xa4, 0x39, 0x64, 0x1d, 0xe4 + }, + secretValue); #ifndef WITH_FIPS - desWrapUnwrapRsa(CKM_DES_CBC_PAD, hSession, hKey[0]); - desWrapUnwrapRsa(CKM_DES3_CBC_PAD, hSession, hKey[1]); + des3WrapUnwrapSecret(CKM_DES3_CBC, hSession, hDes2, CKK_DES3, des3Value, + { + 0x0c, 0xd8, 0x00, 0x36, 0xee, 0xf3, 0xe3, 0xa4, + 0x8c, 0x07, 0x25, 0x77, 0x20, 0xe5, 0xf3, 0xe0, + 0x83, 0x2d, 0x1e, 0xcd, 0x2e, 0x88, 0xef, 0x53 + }, + des3Value); + des3WrapUnwrapSecret(CKM_DES3_ECB, hSession, hDes2, CKK_DES2, des2Value, + { + 0x1a, 0x4d, 0x67, 0x2d, 0xca, 0x6c, 0xb3, 0x35, + 0x38, 0x84, 0xbf, 0x7e, 0x7a, 0xd7, 0xa2, 0xde + }, + des2Value); #endif - desWrapUnwrapRsa(CKM_DES3_CBC_PAD, hSession, hKey[2]); - for ( auto &wrapped : desCBCWrappedKeys ) + // Round trips with every mechanism and wrapping key + for (auto hKey : wrappingKeys) { - unwrapKnownKey(hSession, wrapped); - } + for (auto mechanismType : mechanisms) + { + des3WrapUnwrapSecret(mechanismType, hSession, hKey, CKK_DES2, des2Value, {}, des2Value); + des3WrapUnwrapSecret(mechanismType, hSession, hKey, CKK_DES3, des3Value, {}, des3Value); + des3WrapUnwrapSecret(mechanismType, hSession, hKey, CKK_AES, aesValue, {}, aesValue); + } + des3WrapUnwrapSecret(CKM_DES3_CBC_PAD, hSession, hKey, CKK_GENERIC_SECRET, secretValue, {}, secretValue); + + // Private keys can be wrapped only with padding + desWrapUnwrapRsa(CKM_DES3_CBC_PAD, hSession, hKey); + } + + CK_BYTE iv[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }; + CK_BYTE longIv[16] = { 0 }; + CK_MECHANISM ecbMechanism = { CKM_DES3_ECB, NULL_PTR, 0 }; + CK_MECHANISM cbcMechanism = { CKM_DES3_CBC, iv, sizeof(iv) }; + CK_MECHANISM cbcPadMechanism = { CKM_DES3_CBC_PAD, iv, sizeof(iv) }; + CK_BBOOL bFalse = CK_FALSE; + CK_BBOOL bTrue = CK_TRUE; + CK_OBJECT_CLASS secretClass = CKO_SECRET_KEY; + CK_OBJECT_CLASS privateClass = CKO_PRIVATE_KEY; + CK_KEY_TYPE des2Type = CKK_DES2; + CK_KEY_TYPE des3Type = CKK_DES3; + CK_KEY_TYPE rsaType = CKK_RSA; + CK_ATTRIBUTE des3Attribs[] = { + { CKA_CLASS, &secretClass, sizeof(secretClass) }, + { CKA_KEY_TYPE, &des3Type, sizeof(des3Type) }, + { CKA_TOKEN, &bFalse, sizeof(bFalse) }, + { CKA_PRIVATE, &bTrue, sizeof(bTrue) }, + { CKA_SENSITIVE, &bFalse, sizeof(bFalse) }, + { CKA_EXTRACTABLE, &bTrue, sizeof(bTrue) }, + { CKA_VALUE, const_cast(des3Value.data()), static_cast(des3Value.size()) } + }; + const CK_ULONG des3AttribsCount = sizeof(des3Attribs)/sizeof(CK_ATTRIBUTE); + CK_ATTRIBUTE des2Attribs[] = { + { CKA_CLASS, &secretClass, sizeof(secretClass) }, + { CKA_KEY_TYPE, &des2Type, sizeof(des2Type) }, + { CKA_TOKEN, &bFalse, sizeof(bFalse) }, + { CKA_PRIVATE, &bTrue, sizeof(bTrue) }, + { CKA_SENSITIVE, &bFalse, sizeof(bFalse) }, + { CKA_EXTRACTABLE, &bTrue, sizeof(bTrue) } + }; + CK_ATTRIBUTE rsaAttribs[] = { + { CKA_CLASS, &privateClass, sizeof(privateClass) }, + { CKA_KEY_TYPE, &rsaType, sizeof(rsaType) }, + { CKA_TOKEN, &bFalse, sizeof(bFalse) }, + { CKA_PRIVATE, &bTrue, sizeof(bTrue) } + }; + + CK_OBJECT_HANDLE hSecret = CK_INVALID_HANDLE; + rv = CRYPTOKI_F_PTR( C_CreateObject(hSession, des3Attribs, des3AttribsCount, &hSecret) ); + CPPUNIT_ASSERT(rv == CKR_OK); + CK_BYTE wrapped[32]; + CK_ULONG wrappedLen = sizeof(wrapped); + CK_OBJECT_HANDLE hNew = CK_INVALID_HANDLE; + + // Invalid mechanism parameters + CK_MECHANISM badMechanism = { CKM_DES3_ECB, iv, sizeof(iv) }; + rv = CRYPTOKI_F_PTR( C_WrapKey(hSession, &badMechanism, hDes3, hSecret, wrapped, &wrappedLen) ); + CPPUNIT_ASSERT(rv == CKR_ARGUMENTS_BAD); + badMechanism = { CKM_DES3_CBC, NULL_PTR, 0 }; + rv = CRYPTOKI_F_PTR( C_WrapKey(hSession, &badMechanism, hDes3, hSecret, wrapped, &wrappedLen) ); + CPPUNIT_ASSERT(rv == CKR_ARGUMENTS_BAD); + badMechanism = { CKM_DES3_CBC_PAD, longIv, sizeof(longIv) }; + rv = CRYPTOKI_F_PTR( C_WrapKey(hSession, &badMechanism, hDes3, hSecret, wrapped, &wrappedLen) ); + CPPUNIT_ASSERT(rv == CKR_ARGUMENTS_BAD); + rv = CRYPTOKI_F_PTR( C_UnwrapKey(hSession, &badMechanism, hDes3, wrapped, 24, des2Attribs, sizeof(des2Attribs)/sizeof(CK_ATTRIBUTE), &hNew) ); + CPPUNIT_ASSERT(rv == CKR_ARGUMENTS_BAD); + + // The wrapping key must be a DES2 or DES3 key + CK_OBJECT_HANDLE hAes = CK_INVALID_HANDLE; + rv = importAesKey(hSession, IN_SESSION, IS_PUBLIC, hAes, aesValue); + CPPUNIT_ASSERT(rv == CKR_OK); + rv = CRYPTOKI_F_PTR( C_WrapKey(hSession, &cbcPadMechanism, hAes, hSecret, wrapped, &wrappedLen) ); + CPPUNIT_ASSERT(rv == CKR_WRAPPING_KEY_TYPE_INCONSISTENT); + rv = CRYPTOKI_F_PTR( C_UnwrapKey(hSession, &cbcPadMechanism, hAes, wrapped, 24, des2Attribs, sizeof(des2Attribs)/sizeof(CK_ATTRIBUTE), &hNew) ); + CPPUNIT_ASSERT(rv == CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT); + + // The wrapped key length must be a non-zero multiple of the block size + for (auto mechanism : { ecbMechanism, cbcMechanism, cbcPadMechanism }) + { + rv = CRYPTOKI_F_PTR( C_UnwrapKey(hSession, &mechanism, hDes3, wrapped, 20, des2Attribs, sizeof(des2Attribs)/sizeof(CK_ATTRIBUTE), &hNew) ); + CPPUNIT_ASSERT(rv == CKR_WRAPPED_KEY_LEN_RANGE); + rv = CRYPTOKI_F_PTR( C_UnwrapKey(hSession, &mechanism, hDes3, wrapped, 0, des2Attribs, sizeof(des2Attribs)/sizeof(CK_ATTRIBUTE), &hNew) ); + CPPUNIT_ASSERT(rv == CKR_WRAPPED_KEY_LEN_RANGE); + } + + // ECB and CBC truncate the unwrapped value to the length of the key type + wrappedLen = sizeof(wrapped); + rv = CRYPTOKI_F_PTR( C_WrapKey(hSession, &ecbMechanism, hDes3, hSecret, wrapped, &wrappedLen) ); + CPPUNIT_ASSERT(rv == CKR_OK); + CPPUNIT_ASSERT(wrappedLen == 24); + rv = CRYPTOKI_F_PTR( C_UnwrapKey(hSession, &ecbMechanism, hDes3, wrapped, wrappedLen, des2Attribs, sizeof(des2Attribs)/sizeof(CK_ATTRIBUTE), &hNew) ); + CPPUNIT_ASSERT(rv == CKR_OK); + CK_BYTE value[24]; + CK_ATTRIBUTE valueAttrib = { CKA_VALUE, value, sizeof(value) }; + rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSession, hNew, &valueAttrib, 1) ); + CPPUNIT_ASSERT(rv == CKR_OK); + CPPUNIT_ASSERT(valueAttrib.ulValueLen == 16); + CPPUNIT_ASSERT(memcmp(value, des3Value.data(), 16) == 0); + rv = CRYPTOKI_F_PTR( C_DestroyObject(hSession, hNew) ); + CPPUNIT_ASSERT(rv == CKR_OK); + // ...and fail if the unwrapped value is too short for the key type + rv = CRYPTOKI_F_PTR( C_UnwrapKey(hSession, &ecbMechanism, hDes3, wrapped, 16, des3Attribs, des3AttribsCount - 1, &hNew) ); + CPPUNIT_ASSERT(rv == CKR_WRAPPED_KEY_INVALID); + + // ECB and CBC truncate a generic secret to CKA_VALUE_LEN of the template + const CK_BYTE secretWrapped[] = { + 0x30, 0x32, 0x92, 0x53, 0xbd, 0x29, 0x65, 0x40, + 0x2e, 0xa4, 0x37, 0xbe, 0x92, 0x66, 0x17, 0x8c, + 0x12, 0x3e, 0x17, 0x52, 0xb2, 0x69, 0x1a, 0x27 + }; + CK_KEY_TYPE genericType = CKK_GENERIC_SECRET; + CK_KEY_TYPE aesType = CKK_AES; + CK_ULONG secretLen = static_cast(secretValue.size()); + CK_ATTRIBUTE secretAttribs[] = { + { CKA_CLASS, &secretClass, sizeof(secretClass) }, + { CKA_KEY_TYPE, &genericType, sizeof(genericType) }, + { CKA_TOKEN, &bFalse, sizeof(bFalse) }, + { CKA_PRIVATE, &bTrue, sizeof(bTrue) }, + { CKA_SENSITIVE, &bFalse, sizeof(bFalse) }, + { CKA_EXTRACTABLE, &bTrue, sizeof(bTrue) }, + { CKA_VALUE_LEN, &secretLen, sizeof(secretLen) } + }; + const CK_ULONG secretAttribsCount = sizeof(secretAttribs)/sizeof(CK_ATTRIBUTE); + rv = CRYPTOKI_F_PTR( C_UnwrapKey(hSession, &ecbMechanism, hDes3, const_cast(secretWrapped), sizeof(secretWrapped), secretAttribs, secretAttribsCount, &hNew) ); + CPPUNIT_ASSERT(rv == CKR_OK); + CK_ULONG unwrappedLen = 0; + CK_ATTRIBUTE secretCheck[] = { + { CKA_VALUE, value, sizeof(value) }, + { CKA_VALUE_LEN, &unwrappedLen, sizeof(unwrappedLen) } + }; + rv = CRYPTOKI_F_PTR( C_GetAttributeValue(hSession, hNew, secretCheck, 2) ); + CPPUNIT_ASSERT(rv == CKR_OK); + CPPUNIT_ASSERT(secretCheck[0].ulValueLen == secretLen); + CPPUNIT_ASSERT(memcmp(value, secretValue.data(), secretLen) == 0); + CPPUNIT_ASSERT(unwrappedLen == secretLen); + rv = CRYPTOKI_F_PTR( C_DestroyObject(hSession, hNew) ); + CPPUNIT_ASSERT(rv == CKR_OK); + // ...and fail if CKA_VALUE_LEN is longer than the unwrapped value + secretLen = sizeof(secretWrapped) + 8; + rv = CRYPTOKI_F_PTR( C_UnwrapKey(hSession, &ecbMechanism, hDes3, const_cast(secretWrapped), sizeof(secretWrapped), secretAttribs, secretAttribsCount, &hNew) ); + CPPUNIT_ASSERT(rv == CKR_TEMPLATE_INCONSISTENT); + // CKA_VALUE_LEN is still not allowed when unwrapping an AES key + secretLen = 16; + secretAttribs[1].pValue = &aesType; + rv = CRYPTOKI_F_PTR( C_UnwrapKey(hSession, &ecbMechanism, hDes3, const_cast(secretWrapped), sizeof(secretWrapped), secretAttribs, secretAttribsCount, &hNew) ); + CPPUNIT_ASSERT(rv == CKR_ATTRIBUTE_READ_ONLY); + + // Invalid padding: the plaintext block is all zeros + const CK_BYTE badPadding[] = { 0x30, 0x32, 0x92, 0x53, 0xbd, 0x29, 0x65, 0x40 }; + rv = CRYPTOKI_F_PTR( C_UnwrapKey(hSession, &cbcPadMechanism, hDes3, const_cast(badPadding), sizeof(badPadding), des2Attribs, sizeof(des2Attribs)/sizeof(CK_ATTRIBUTE), &hNew) ); + CPPUNIT_ASSERT(rv == CKR_WRAPPED_KEY_INVALID); + + // ECB and CBC can not wrap or unwrap private keys + CK_OBJECT_HANDLE hPrk = CK_INVALID_HANDLE; + rv = generateRsaPrivateKey(hSession, CK_TRUE, CK_TRUE, hPrk); + CPPUNIT_ASSERT(rv == CKR_OK); + for (auto mechanism : { ecbMechanism, cbcMechanism }) + { + wrappedLen = sizeof(wrapped); + rv = CRYPTOKI_F_PTR( C_WrapKey(hSession, &mechanism, hDes3, hPrk, wrapped, &wrappedLen) ); + CPPUNIT_ASSERT(rv == CKR_KEY_NOT_WRAPPABLE); + rv = CRYPTOKI_F_PTR( C_UnwrapKey(hSession, &mechanism, hDes3, wrapped, 24, rsaAttribs, sizeof(rsaAttribs)/sizeof(CK_ATTRIBUTE), &hNew) ); + CPPUNIT_ASSERT(rv == CKR_TEMPLATE_INCONSISTENT); + } + rv = CRYPTOKI_F_PTR( C_DestroyObject(hSession, hPrk) ); + CPPUNIT_ASSERT(rv == CKR_OK); + rv = CRYPTOKI_F_PTR( C_DestroyObject(hSession, hSecret) ); + CPPUNIT_ASSERT(rv == CKR_OK); + + // Keys wrapped by another PKCS#11 token + for ( auto &material : desCBCWrappedKeys ) + { + unwrapKnownKey(hSession, material); + } } void SymmetricAlgorithmTests::testNullTemplate() diff --git a/src/lib/test/SymmetricAlgorithmTests.h b/src/lib/test/SymmetricAlgorithmTests.h index 7d60fb576..e7368cb43 100644 --- a/src/lib/test/SymmetricAlgorithmTests.h +++ b/src/lib/test/SymmetricAlgorithmTests.h @@ -47,6 +47,7 @@ class SymmetricAlgorithmTests : public TestsBase #ifdef HAVE_AES_KEY_WRAP CPPUNIT_TEST(testAesWrapUnwrap); #endif + CPPUNIT_TEST(testDesWrapUnwrap); CPPUNIT_TEST(testNullTemplate); CPPUNIT_TEST(testNonModifiableDesKeyGeneration); // TODO: check CKA_CHECK_VALUE error @@ -91,6 +92,13 @@ class SymmetricAlgorithmTests : public TestsBase void aesWrapUnwrapNonModifiableGeneric(CK_MECHANISM_TYPE mechanismType, CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hKey); void aesWrapUnwrapRsa(CK_MECHANISM_TYPE mechanismType, CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hKey); void desWrapUnwrapRsa(CK_MECHANISM_TYPE mechanismType, CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hKey); + void des3WrapUnwrapSecret(CK_MECHANISM_TYPE mechanismType, + CK_SESSION_HANDLE hSession, + CK_OBJECT_HANDLE hKey, + CK_KEY_TYPE keyType, + const Bytes &keyValue, + const Bytes &expectedWrapped, + const Bytes &expectedUnwrapped); CK_RV generateRsaPrivateKey(CK_SESSION_HANDLE hSession, CK_BBOOL bToken, CK_BBOOL bPrivate, CK_OBJECT_HANDLE &hKey); #ifdef WITH_GOST void aesWrapUnwrapGost(CK_MECHANISM_TYPE mechanismType, CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hKey);