diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2e7ec8ab8..8b0f90a59 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -75,6 +75,44 @@ jobs: run: | make check || (find . -name test-suite.log -exec cat {} \; && false) + linux_ossl_35: + name: Linux with OpenSSL 3.5.4 + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + - name: Prepare + env: + OPENSSL_VERSION: 3.5.4 + OPENSSL_INSTALL_DIR: /usr/local/openssl-3.5 + LDFLAGS: "-Wl,-rpath,/usr/local/openssl-3.5/lib64 -L/usr/local/openssl-3.5/lib64" + PKG_CONFIG_PATH: "/usr/local/openssl-3.5/lib64/pkgconfig" + run: | + sudo apt-get update -qq + sudo apt-get install -y libcppunit-dev p11-kit build-essential checkinstall zlib1g-dev sudo autoconf libtool git + # Install OpenSSL 3.5 + curl -L -O https://github.com/openssl/openssl/releases/download/openssl-${{ env.OPENSSL_VERSION }}/openssl-${{ env.OPENSSL_VERSION }}.tar.gz + tar -xf openssl-${{ env.OPENSSL_VERSION }}.tar.gz + cd openssl-${{ env.OPENSSL_VERSION }} + ./config shared zlib no-ssl3 no-weak-ssl-ciphers --prefix=${{ env.OPENSSL_INSTALL_DIR }} --openssldir=${{ env.OPENSSL_INSTALL_DIR }} + make -j$(nproc) > build.log + sudo make install > install.log + cd ${{ env.OPENSSL_INSTALL_DIR }} + sudo ln -sf lib64 lib + - name: Build + env: + # Once all OpenSSL deprecations fixed, uncomment this + # CXXFLAGS: -Werror + OPENSSL_INSTALL_DIR: /usr/local/openssl-3.5 + LDFLAGS: "-Wl,-rpath,/usr/local/openssl-3.5/lib64 -L/usr/local/openssl-3.5/lib64" + PKG_CONFIG_PATH: "/usr/local/openssl-3.5/lib64/pkgconfig" + run: | + ./autogen.sh + ./configure --with-crypto-backend=openssl --with-openssl=${{ env.OPENSSL_INSTALL_DIR }} + make -j$(nproc) + - name: Test + run: | + make check || (find . -name test-suite.log -exec cat {} \; && false) + macos: name: macOS (${{ matrix.backend }}) runs-on: macos-14 diff --git a/CMAKE-NOTES.md b/CMAKE-NOTES.md index 0ed35b8de..b355c41f1 100644 --- a/CMAKE-NOTES.md +++ b/CMAKE-NOTES.md @@ -11,6 +11,7 @@ Some options (more can be found in CMakeLists.txt): -DBUILD_TESTS=ON Compile tests along with libraries -DDISABLE_NON_PAGED_MEMORY=ON Disable non-paged memory for secure storage -DENABLE_EDDSA=ON Enable support for EDDSA + -DENABLE_MLDSA=ON Enable support for ML-DSA -DWITH_MIGRATE=ON Build migration tool -DWITH_CRYPTO_BACKEND=openssl Select crypto backend (openssl|botan) diff --git a/CMAKE-WIN-NOTES.md b/CMAKE-WIN-NOTES.md index 3076a801a..a2f271666 100644 --- a/CMAKE-WIN-NOTES.md +++ b/CMAKE-WIN-NOTES.md @@ -52,6 +52,7 @@ Some options (more can be found in CMakeLists.txt): -DBUILD_TESTS=ON Compile tests along with libraries -DENABLE_EDDSA=ON Enable support for EDDSA + -DENABLE_MLDSA=ON Enable support for ML-DSA -DWITH_MIGRATE=ON Build migration tool -DWITH_CRYPTO_BACKEND= Select crypto backend (openssl|botan) -DDISABLE_NON_PAGED_MEMORY=ON Disable non-paged memory for secure storage diff --git a/CMakeLists.txt b/CMakeLists.txt index b55419146..645c28746 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,7 @@ option(DISABLE_VISIBILITY "Disables and unsets -fvisibility=hidden" OFF) option(ENABLE_64bit "Enable 64-bit compiling" OFF) option(ENABLE_ECC "Enable support for ECC" ON) option(ENABLE_EDDSA "Enable support for EDDSA" ON) +option(ENABLE_MLDSA "Enable support for ML-DSA" OFF) option(ENABLE_GOST "Enable support for GOST" OFF) option(ENABLE_FIPS "Enable support for FIPS 140-2 mode" OFF) option(ENABLE_P11_KIT "Enable p11-kit integration" ON) diff --git a/README.md b/README.md index 0ea5a8f6c..3c9f31aff 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,7 @@ Options: --enable-ecc Enable support for ECC (default detect) --enable-gost Enable support for GOST (default detect) --enable-eddsa Enable support for EDDSA (default detect) + --enable-mldsa Enable support for ML-DSA (default detect) --disable-visibility Disable hidden visibilty link mode [enabled] --with-crypto-backend Select crypto backend (openssl|botan) --with-openssl=PATH Specify prefix of path of OpenSSL diff --git a/config.h.in.cmake b/config.h.in.cmake index 4f7f2a6c5..95fb83516 100644 --- a/config.h.in.cmake +++ b/config.h.in.cmake @@ -151,6 +151,9 @@ /* Compile with EDDSA support */ #cmakedefine WITH_EDDSA @WITH_EDDSA@ +/* Compile with ML-DSA support */ +#cmakedefine WITH_ML_DSA @WITH_ML_DSA@ + /* Compile with FIPS 140-2 mode */ #cmakedefine WITH_FIPS @WITH_FIPS@ diff --git a/m4/acx_crypto_backend.m4 b/m4/acx_crypto_backend.m4 index fe59ca97f..57a951a52 100644 --- a/m4/acx_crypto_backend.m4 +++ b/m4/acx_crypto_backend.m4 @@ -28,6 +28,16 @@ AC_DEFUN([ACX_CRYPTO_BACKEND],[ [enable_eddsa="detect"] ) + # Add ML-DSA check + + AC_ARG_ENABLE(mldsa, + AS_HELP_STRING([--enable-mldsa], + [Enable support for ML-DSA (default detect)] + ), + [enable_mldsa="${enableval}"], + [enable_mldsa="detect"] + ) + # Second check for the FIPS 140-2 mode AC_ARG_ENABLE(fips, @@ -100,6 +110,15 @@ AC_DEFUN([ACX_CRYPTO_BACKEND],[ detect*-no*) enable_eddsa="no";; esac + case "${enable_mldsa}" in + yes|detect) ACX_OPENSSL_MLDSA;; + esac + case "${enable_mldsa}-${have_lib_openssl_mldsa_support}" in + yes-no) AC_MSG_ERROR([OpenSSL library has no ML-DSA support]);; + detect-yes) enable_mldsa="yes";; + detect-no) enable_mldsa="no";; + esac + case "${enable_gost}-${enable_fips}" in yes-yes) AC_MSG_ERROR([GOST is not FIPS approved]);; yes-no|detect-no) ACX_OPENSSL_GOST;; @@ -166,6 +185,10 @@ AC_DEFUN([ACX_CRYPTO_BACKEND],[ detect-*) enable_eddsa="${have_lib_botan_eddsa_support}";; esac + if test "x${enable_mldsa}" = "xyes"; then + AC_MSG_ERROR([Botan does not support ML-DSA]) + fi + case "${enable_gost}" in yes|detect) ACX_BOTAN_GOST;; esac @@ -231,6 +254,19 @@ AC_DEFUN([ACX_CRYPTO_BACKEND],[ fi AM_CONDITIONAL([WITH_EDDSA], [test "x${enable_eddsa}" = "xyes"]) + AC_MSG_CHECKING(for ML-DSA support) + if test "x${enable_mldsa}" = "xyes"; then + AC_MSG_RESULT(yes) + AC_DEFINE_UNQUOTED( + [WITH_ML_DSA], + [], + [Compile with ML-DSA support] + ) + else + AC_MSG_RESULT(no) + fi + AM_CONDITIONAL([WITH_ML_DSA], [test "x${enable_mldsa}" = "xyes"]) + AC_SUBST(CRYPTO_INCLUDES) AC_SUBST(CRYPTO_LIBS) diff --git a/m4/acx_openssl_mldsa.m4 b/m4/acx_openssl_mldsa.m4 new file mode 100644 index 000000000..22378d013 --- /dev/null +++ b/m4/acx_openssl_mldsa.m4 @@ -0,0 +1,43 @@ +AC_DEFUN([ACX_OPENSSL_MLDSA],[ + AC_MSG_CHECKING(for OpenSSL ML-DSA support) + + tmp_CPPFLAGS=$CPPFLAGS + tmp_LIBS=$LIBS + + CPPFLAGS="$CPPFLAGS $CRYPTO_INCLUDES" + LIBS="$CRYPTO_LIBS $LIBS" + + AC_LANG_PUSH([C]) + AC_CACHE_VAL([acx_cv_lib_openssl_mldsa_support],[ + acx_cv_lib_openssl_mldsa_support=no + AC_RUN_IFELSE([ + AC_LANG_SOURCE([[ + #include + #include + int main() + { + EVP_PKEY_CTX *pctx = + EVP_PKEY_CTX_new_from_name(NULL, "ML-DSA-44", NULL); + if (pctx == NULL) + return 1; + return 0; + } + ]]) + ],[ + AC_MSG_RESULT([yes]) + acx_cv_lib_openssl_mldsa_support=yes + ],[ + AC_MSG_RESULT([no]) + acx_cv_lib_openssl_mldsa_support=no + ],[ + AC_MSG_WARN([Cannot test, ML-DSA]) + acx_cv_lib_openssl_mldsa_support=no + ]) + ]) + + AC_LANG_POP([C]) + + CPPFLAGS=$tmp_CPPFLAGS + LIBS=$tmp_LIBS + have_lib_openssl_mldsa_support="${acx_cv_lib_openssl_mldsa_support}" +]) diff --git a/src/bin/dump/tables.h b/src/bin/dump/tables.h index e6db51697..115d6ecb7 100644 --- a/src/bin/dump/tables.h +++ b/src/bin/dump/tables.h @@ -150,6 +150,8 @@ void fill_CKA_table(std::map &t) t[CKA_OS_TOKENFLAGS] = "CKA_OS_TOKENFLAGS"; t[CKA_OS_SOPIN] = "CKA_OS_SOPIN"; t[CKA_OS_USERPIN] = "CKA_OS_USERPIN"; + t[CKA_PARAMETER_SET] = "CKA_PARAMETER_SET"; + t[CKA_SEED] = "CKA_SEED"; } void fill_CKM_table(std::map &t) @@ -478,6 +480,8 @@ void fill_CKM_table(std::map &t) t[CKM_RSA_PKCS_OAEP_TPM_1_1] = "CKM_RSA_PKCS_OAEP_TPM_1_1"; t[CKM_EC_EDWARDS_KEY_PAIR_GEN] = "CKM_EC_EDWARDS_KEY_PAIR_GEN"; t[CKM_EDDSA] = "CKM_EDDSA"; + t[CKM_ML_DSA_KEY_PAIR_GEN] = "CKM_ML_DSA_KEY_PAIR_GEN"; + t[CKM_ML_DSA] = "CKM_ML_DSA"; } void fill_CKO_table(std::map &t) @@ -544,6 +548,7 @@ void fill_CKK_table(std::map &t) t[CKK_GOSTR3411] = "CKK_GOSTR3411"; t[CKK_GOST28147] = "CKK_GOST28147"; t[CKK_EC_EDWARDS] = "CKK_EC_EDWARDS"; + t[CKK_ML_DSA] = "CKK_ML_DSA"; } void fill_CKC_table(std::map &t) diff --git a/src/lib/P11Attributes.cpp b/src/lib/P11Attributes.cpp index 937f043fb..c9603bf9c 100644 --- a/src/lib/P11Attributes.cpp +++ b/src/lib/P11Attributes.cpp @@ -2227,6 +2227,38 @@ bool P11AttrEcPoint::setDefault() return osobject->setAttribute(type, attr); } +/***************************************** + * CKA_PARAMETER_SET + *****************************************/ + +// Set default value +bool P11AttrParameterSet::setDefault() +{ + OSAttribute attr((unsigned long)0); + return osobject->setAttribute(type, attr); +} + +// Update the value if allowed +CK_RV P11AttrParameterSet::updateAttr(Token* /*token*/, bool /*isPrivate*/, CK_VOID_PTR pValue, CK_ULONG ulValueLen, int op) +{ + // Attribute specific checks + if (op != OBJECT_OP_GENERATE && op != OBJECT_OP_CREATE) + { + return CKR_ATTRIBUTE_READ_ONLY; + } + + if (ulValueLen != sizeof(CK_ULONG)) + { + return CKR_ATTRIBUTE_VALUE_INVALID; + } + + // Store data + + osobject->setAttribute(type, *(CK_ULONG*)pValue); + + return CKR_OK; +} + /***************************************** * CKA_GOSTR3410_PARAMS *****************************************/ @@ -2523,3 +2555,42 @@ CK_RV P11AttrAllowedMechanisms::updateAttr(Token* /*token*/, bool /*isPrivate*/, osobject->setAttribute(type, OSAttribute(data)); return CKR_OK; } + +/***************************************** + * CKA_SEED + *****************************************/ + +// Set default value +bool P11AttrSeed::setDefault() +{ + OSAttribute attr(ByteString("")); + return osobject->setAttribute(type, attr); +} + +// Update the value if allowed +CK_RV P11AttrSeed::updateAttr(Token *token, bool isPrivate, CK_VOID_PTR pValue, CK_ULONG ulValueLen, int /*op*/) +{ + ByteString plaintext((unsigned char*)pValue, ulValueLen); + ByteString value; + + // Encrypt + + if (isPrivate) + { + if (!token->encrypt(plaintext, value)) + return CKR_GENERAL_ERROR; + } + else + value = plaintext; + + // Attribute specific checks + + if (value.size() < ulValueLen) + return CKR_GENERAL_ERROR; + + // Store data + + osobject->setAttribute(type, value); + + return CKR_OK; +} \ No newline at end of file diff --git a/src/lib/P11Attributes.h b/src/lib/P11Attributes.h index 6fd31f8d0..ed8bc05fc 100644 --- a/src/lib/P11Attributes.h +++ b/src/lib/P11Attributes.h @@ -1144,6 +1144,24 @@ class P11AttrEcPoint : public P11Attribute virtual bool setDefault(); }; +/***************************************** + * CKA_PARAMETER_SET + *****************************************/ + +class P11AttrParameterSet : public P11Attribute +{ +public: + // Constructor + P11AttrParameterSet(OSObject* inobject, CK_ULONG inchecks = 0) : P11Attribute(inobject) { type = CKA_PARAMETER_SET; size = sizeof(CK_ULONG); checks = ck1|inchecks; } + +protected: + // Set the default value of the attribute + virtual bool setDefault(); + + // Update the value if allowed + virtual CK_RV updateAttr(Token *token, bool isPrivate, CK_VOID_PTR pValue, CK_ULONG ulValueLen, int op); +}; + /***************************************** * CKA_GOSTR3410_PARAMS *****************************************/ @@ -1261,4 +1279,22 @@ class P11AttrAllowedMechanisms : public P11Attribute virtual CK_RV updateAttr(Token *token, bool isPrivate, CK_VOID_PTR pValue, CK_ULONG ulValueLen, int op); }; +/***************************************** + * CKA_SEED + *****************************************/ + +class P11AttrSeed : public P11Attribute +{ +public: + // Constructor + P11AttrSeed(OSObject* inobject) : P11Attribute(inobject) { type = CKA_SEED; checks = ck4|ck6|ck7; } + +protected: + // Set the default value of the attribute + virtual bool setDefault(); + + // Update the value if allowed + virtual CK_RV updateAttr(Token *token, bool isPrivate, CK_VOID_PTR pValue, CK_ULONG ulValueLen, int op); +}; + #endif // !_SOFTHSM_V2_P11ATTRIBUTES_H diff --git a/src/lib/P11Objects.cpp b/src/lib/P11Objects.cpp index bbd021047..d3ecf6db2 100644 --- a/src/lib/P11Objects.cpp +++ b/src/lib/P11Objects.cpp @@ -930,6 +930,51 @@ bool P11EDPublicKeyObj::init(OSObject *inobject) return true; } +// Constructor +P11MLDSAPublicKeyObj::P11MLDSAPublicKeyObj() +{ + initialized = false; +} + +// Add attributes +bool P11MLDSAPublicKeyObj::init(OSObject *inobject) +{ + if (initialized) return true; + if (inobject == NULL) return false; + + if (!inobject->attributeExists(CKA_KEY_TYPE) || inobject->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED) != CKK_ML_DSA) { + OSAttribute setKeyType((unsigned long)CKK_ML_DSA); + inobject->setAttribute(CKA_KEY_TYPE, setKeyType); + } + + // Create parent + if (!P11PublicKeyObj::init(inobject)) return false; + + // Create attributes + P11Attribute* attrParameterSet = new P11AttrParameterSet(osobject,P11Attribute::ck3); + P11Attribute* attrValue = new P11AttrValue(osobject,P11Attribute::ck1|P11Attribute::ck4); + + // Initialize the attributes + if + ( + !attrParameterSet->init() || + !attrValue->init() + ) + { + ERROR_MSG("Could not initialize the attribute"); + delete attrParameterSet; + delete attrValue; + return false; + } + + // Add them to the map + attributes[attrParameterSet->getType()] = attrParameterSet; + attributes[attrValue->getType()] = attrValue; + + initialized = true; + return true; +} + // Constructor P11DHPublicKeyObj::P11DHPublicKeyObj() { @@ -1335,6 +1380,55 @@ bool P11EDPrivateKeyObj::init(OSObject *inobject) return true; } +// Constructor +P11MLDSAPrivateKeyObj::P11MLDSAPrivateKeyObj() +{ + initialized = false; +} + +// Add attributes +bool P11MLDSAPrivateKeyObj::init(OSObject *inobject) +{ + if (initialized) return true; + if (inobject == NULL) return false; + + if (!inobject->attributeExists(CKA_KEY_TYPE) || inobject->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED) != CKK_ML_DSA) { + OSAttribute setKeyType((unsigned long)CKK_ML_DSA); + inobject->setAttribute(CKA_KEY_TYPE, setKeyType); + } + + // Create parent + if (!P11PrivateKeyObj::init(inobject)) return false; + + // Create attributes + P11Attribute* attrParameterSet = new P11AttrParameterSet(osobject,P11Attribute::ck4|P11Attribute::ck6); + P11Attribute* attrSeed = new P11AttrSeed(osobject); + P11Attribute* attrValue = new P11AttrValue(osobject,P11Attribute::ck1|P11Attribute::ck4|P11Attribute::ck6|P11Attribute::ck7); + + // Initialize the attributes + if + ( + !attrParameterSet->init() || + !attrValue->init() || + !attrSeed->init() + ) + { + ERROR_MSG("Could not initialize the attribute"); + delete attrParameterSet; + delete attrSeed; + delete attrValue; + return false; + } + + // Add them to the map + attributes[attrParameterSet->getType()] = attrParameterSet; + attributes[attrSeed->getType()] = attrSeed; + attributes[attrValue->getType()] = attrValue; + + initialized = true; + return true; +} + // Constructor P11DHPrivateKeyObj::P11DHPrivateKeyObj() { diff --git a/src/lib/P11Objects.h b/src/lib/P11Objects.h index 9d223ac01..6ed7dbcfe 100644 --- a/src/lib/P11Objects.h +++ b/src/lib/P11Objects.h @@ -198,6 +198,19 @@ class P11EDPublicKeyObj : public P11PublicKeyObj bool initialized; }; +class P11MLDSAPublicKeyObj : public P11PublicKeyObj +{ +public: + // Constructor + P11MLDSAPublicKeyObj(); + + // Add attributes + virtual bool init(OSObject *inobject); + +protected: + bool initialized; +}; + class P11DHPublicKeyObj : public P11PublicKeyObj { public: @@ -287,6 +300,19 @@ class P11EDPrivateKeyObj : public P11PrivateKeyObj bool initialized; }; +class P11MLDSAPrivateKeyObj : public P11PrivateKeyObj +{ +public: + // Constructor + P11MLDSAPrivateKeyObj(); + + // Add attributes + virtual bool init(OSObject *inobject); + +protected: + bool initialized; +}; + class P11DHPrivateKeyObj : public P11PrivateKeyObj { public: diff --git a/src/lib/SoftHSM.cpp b/src/lib/SoftHSM.cpp index 62ddd72c3..7b8ae455c 100644 --- a/src/lib/SoftHSM.cpp +++ b/src/lib/SoftHSM.cpp @@ -62,6 +62,12 @@ #include "DHPrivateKey.h" #include "GOSTPublicKey.h" #include "GOSTPrivateKey.h" +#ifdef WITH_ML_DSA +#include "MLDSAParameters.h" +#include "MLDSAPublicKey.h" +#include "MLDSAPrivateKey.h" +#include "MLDSAUtil.h" +#endif #include "cryptoki.h" #include "SoftHSM.h" #include "osmutex.h" @@ -140,6 +146,10 @@ static CK_RV newP11Object(CK_OBJECT_CLASS objClass, CK_KEY_TYPE keyType, CK_CERT *p11object = new P11GOSTPublicKeyObj(); else if (keyType == CKK_EC_EDWARDS) *p11object = new P11EDPublicKeyObj(); +#ifdef WITH_ML_DSA + else if (keyType == CKK_ML_DSA) + *p11object = new P11MLDSAPublicKeyObj(); +#endif else return CKR_ATTRIBUTE_VALUE_INVALID; break; @@ -157,6 +167,10 @@ static CK_RV newP11Object(CK_OBJECT_CLASS objClass, CK_KEY_TYPE keyType, CK_CERT *p11object = new P11GOSTPrivateKeyObj(); else if (keyType == CKK_EC_EDWARDS) *p11object = new P11EDPrivateKeyObj(); +#ifdef WITH_ML_DSA + else if (keyType == CKK_ML_DSA) + *p11object = new P11MLDSAPrivateKeyObj(); +#endif else return CKR_ATTRIBUTE_VALUE_INVALID; break; @@ -824,6 +838,10 @@ void SoftHSM::prepareSupportedMechanisms(std::maprecycleAsymmetricAlgorithm(eddsa); +#endif +#ifdef WITH_ML_DSA + AsymmetricAlgorithm* mldsa = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::MLDSA); + if (mldsa != NULL) + { + mldsaMinSize = mldsa->getMinKeySize(); + mldsaMaxSize = mldsa->getMaxKeySize(); + } + else + { + return CKR_GENERAL_ERROR; + } + CryptoFactory::i()->recycleAsymmetricAlgorithm(mldsa); #endif pInfo->flags = 0; // initialize flags switch (type) @@ -1117,7 +1151,7 @@ CK_RV SoftHSM::C_GetMechanismInfo(CK_SLOT_ID slotID, CK_MECHANISM_TYPE type, CK_ break; case CKM_GENERIC_SECRET_KEY_GEN: pInfo->ulMinKeySize = 1; - pInfo->ulMaxKeySize = 0x80000000; + pInfo->ulMaxKeySize = 0x8000000; pInfo->flags = CKF_GENERATE; break; #ifndef WITH_FIPS @@ -1316,6 +1350,18 @@ CK_RV SoftHSM::C_GetMechanismInfo(CK_SLOT_ID slotID, CK_MECHANISM_TYPE type, CK_ pInfo->ulMaxKeySize = eddsaMaxSize; pInfo->flags = CKF_SIGN | CKF_VERIFY; break; +#endif +#ifdef WITH_ML_DSA + case CKM_ML_DSA_KEY_PAIR_GEN: + pInfo->ulMinKeySize = mldsaMinSize; + pInfo->ulMaxKeySize = mldsaMaxSize; + pInfo->flags = CKF_GENERATE_KEY_PAIR; + break; + case CKM_ML_DSA: + pInfo->ulMinKeySize = mldsaMinSize; + pInfo->ulMaxKeySize = mldsaMaxSize; + pInfo->flags = CKF_SIGN | CKF_VERIFY; + break; #endif case CKM_CONCATENATE_DATA_AND_BASE: case CKM_CONCATENATE_BASE_AND_DATA: @@ -4155,6 +4201,10 @@ CK_RV SoftHSM::AsymSignInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechan #endif #ifdef WITH_EDDSA bool isEDDSA = false; +#endif +#ifdef WITH_ML_DSA + bool isMLDSA = false; + SIGN_ADDITIONAL_CONTEXT additionalContext = {}; #endif switch(pMechanism->mechanism) { case CKM_RSA_PKCS: @@ -4421,6 +4471,58 @@ CK_RV SoftHSM::AsymSignInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechan bAllowMultiPartOp = false; isEDDSA = true; break; +#endif +#ifdef WITH_ML_DSA + case CKM_ML_DSA: + mechanism = AsymMech::MLDSA; + bAllowMultiPartOp = false; + isMLDSA = true; + if (pMechanism->pParameter != NULL_PTR) { + if(pMechanism->ulParameterLen != sizeof(CK_SIGN_ADDITIONAL_CONTEXT)) + { + ERROR_MSG("Invalid parameters"); + return CKR_ARGUMENTS_BAD; + } + else + { + const CK_SIGN_ADDITIONAL_CONTEXT* ckSignAdditionalContext = (const CK_SIGN_ADDITIONAL_CONTEXT*) pMechanism->pParameter; + if (ckSignAdditionalContext->ulContextLen > 255) + { + ERROR_MSG("ML-DSA: Invalid parameters, context length > 255"); + return CKR_ARGUMENTS_BAD; + } + + // Always initialize context fields + additionalContext.contextAsChar = NULL; + additionalContext.contextLength = 0; + if (ckSignAdditionalContext->ulContextLen > 0) + { + if (ckSignAdditionalContext->pContext == NULL) + { + ERROR_MSG("ML-DSA: Invalid parameters, pContext is NULL"); + return CKR_ARGUMENTS_BAD; + } + additionalContext.contextAsChar = (unsigned char*) ckSignAdditionalContext->pContext; + additionalContext.contextLength = ckSignAdditionalContext->ulContextLen; + } + switch (ckSignAdditionalContext->hedgeVariant) { + case CKH_HEDGE_REQUIRED: + additionalContext.hedgeType = Hedge::HEDGE_REQUIRED; + break; + case CKH_DETERMINISTIC_REQUIRED: + additionalContext.hedgeType = Hedge::DETERMINISTIC_REQUIRED; + break; + case CKH_HEDGE_PREFERRED: + // Per PKCS11v3.2 section 6.67.5 + // "If no parameter is supplied the hedgeVariant will be CKH_HEDGE_PREFERRED" + default: + additionalContext.hedgeType = Hedge::HEDGE_PREFERRED; + } + param = &additionalContext; + paramLen = sizeof(SIGN_ADDITIONAL_CONTEXT); + } + } + break; #endif default: return CKR_MECHANISM_INVALID; @@ -4507,6 +4609,27 @@ CK_RV SoftHSM::AsymSignInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechan return CKR_GENERAL_ERROR; } } +#endif +#ifdef WITH_ML_DSA + else if (isMLDSA) + { + asymCrypto = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::MLDSA); + if (asymCrypto == NULL) return CKR_MECHANISM_INVALID; + + privateKey = asymCrypto->newPrivateKey(); + if (privateKey == NULL) + { + CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto); + return CKR_HOST_MEMORY; + } + + if (MLDSAUtil::getMLDSAPrivateKey((MLDSAPrivateKey*)privateKey, token, key) != CKR_OK) + { + asymCrypto->recyclePrivateKey(privateKey); + CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto); + return CKR_GENERAL_ERROR; + } + } #endif else { @@ -5158,6 +5281,10 @@ CK_RV SoftHSM::AsymVerifyInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMech #endif #ifdef WITH_EDDSA bool isEDDSA = false; +#endif +#ifdef WITH_ML_DSA + bool isMLDSA = false; + SIGN_ADDITIONAL_CONTEXT additionalContext = {}; #endif switch(pMechanism->mechanism) { case CKM_RSA_PKCS: @@ -5422,6 +5549,56 @@ CK_RV SoftHSM::AsymVerifyInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMech bAllowMultiPartOp = false; isEDDSA = true; break; +#endif +#ifdef WITH_ML_DSA + case CKM_ML_DSA: + mechanism = AsymMech::MLDSA; + bAllowMultiPartOp = false; + isMLDSA = true; + if (pMechanism->pParameter != NULL_PTR) { + if(pMechanism->ulParameterLen != sizeof(CK_SIGN_ADDITIONAL_CONTEXT)) + { + ERROR_MSG("Invalid parameters"); + return CKR_ARGUMENTS_BAD; + } + else + { + const CK_SIGN_ADDITIONAL_CONTEXT* ckSignAdditionalContext = (const CK_SIGN_ADDITIONAL_CONTEXT*) pMechanism->pParameter; + if (ckSignAdditionalContext->ulContextLen > 255) { + ERROR_MSG("ML-DSA: Invalid parameters, context length > 255"); + return CKR_ARGUMENTS_BAD; + } + // Always initialize context fields + additionalContext.contextAsChar = NULL; + additionalContext.contextLength = 0; + if (ckSignAdditionalContext->ulContextLen > 0) { + if (ckSignAdditionalContext->pContext == NULL) + { + ERROR_MSG("ML-DSA: Invalid parameters, pContext is NULL"); + return CKR_ARGUMENTS_BAD; + } + additionalContext.contextAsChar = (unsigned char*) ckSignAdditionalContext->pContext; + additionalContext.contextLength = ckSignAdditionalContext->ulContextLen; + } + + switch (ckSignAdditionalContext->hedgeVariant) { + case CKH_HEDGE_REQUIRED: + additionalContext.hedgeType = Hedge::HEDGE_REQUIRED; + break; + case CKH_DETERMINISTIC_REQUIRED: + additionalContext.hedgeType = Hedge::DETERMINISTIC_REQUIRED; + break; + // Per PKCS11v3.2 section 6.67.5 + // "If no parameter is supplied the hedgeVariant will be CKH_HEDGE_PREFERRED" + case CKH_HEDGE_PREFERRED: + default: + additionalContext.hedgeType = Hedge::HEDGE_PREFERRED; + } + param = &additionalContext; + paramLen = sizeof(SIGN_ADDITIONAL_CONTEXT); + } + } + break; #endif default: return CKR_MECHANISM_INVALID; @@ -5508,6 +5685,27 @@ CK_RV SoftHSM::AsymVerifyInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMech return CKR_GENERAL_ERROR; } } +#endif +#ifdef WITH_ML_DSA + else if (isMLDSA) + { + asymCrypto = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::MLDSA); + if (asymCrypto == NULL) return CKR_MECHANISM_INVALID; + + publicKey = asymCrypto->newPublicKey(); + if (publicKey == NULL) + { + CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto); + return CKR_HOST_MEMORY; + } + + if (MLDSAUtil::getMLDSAPublicKey((MLDSAPublicKey*)publicKey, token, key) != CKR_OK) + { + asymCrypto->recyclePublicKey(publicKey); + CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto); + return CKR_GENERAL_ERROR; + } + } #endif else { @@ -5627,6 +5825,7 @@ static CK_RV AsymVerify(Session* session, CK_BYTE_PTR pData, CK_ULONG ulDataLen, if (ulSignatureLen != size) { ERROR_MSG("The size of the signature differs from the size of the mechanism"); + DEBUG_MSG("size: %lu, ulSignatureLen: %lu", size, ulSignatureLen); session->resetOp(); return CKR_SIGNATURE_LEN_RANGE; } @@ -5810,6 +6009,7 @@ static CK_RV AsymVerifyFinal(Session* session, CK_BYTE_PTR pSignature, CK_ULONG if (ulSignatureLen != size) { ERROR_MSG("The size of the signature differs from the size of the mechanism"); + DEBUG_MSG("size: %lu, ulSignatureLen: %lu", size, ulSignatureLen); session->resetOp(); return CKR_SIGNATURE_LEN_RANGE; } @@ -6118,6 +6318,11 @@ CK_RV SoftHSM::C_GenerateKeyPair case CKM_EC_EDWARDS_KEY_PAIR_GEN: keyType = CKK_EC_EDWARDS; break; +#endif +#ifdef WITH_ML_DSA + case CKM_ML_DSA_KEY_PAIR_GEN: + keyType = CKK_ML_DSA; + break; #endif default: return CKR_MECHANISM_INVALID; @@ -6146,6 +6351,8 @@ CK_RV SoftHSM::C_GenerateKeyPair return CKR_TEMPLATE_INCONSISTENT; if (pMechanism->mechanism == CKM_EC_EDWARDS_KEY_PAIR_GEN && keyType != CKK_EC_EDWARDS) return CKR_TEMPLATE_INCONSISTENT; + if (pMechanism->mechanism == CKM_ML_DSA_KEY_PAIR_GEN && keyType != CKK_ML_DSA) + return CKR_TEMPLATE_INCONSISTENT; // Extract information from the private key template that is needed to create the object. CK_OBJECT_CLASS privateKeyClass = CKO_PRIVATE_KEY; @@ -6169,6 +6376,8 @@ CK_RV SoftHSM::C_GenerateKeyPair return CKR_TEMPLATE_INCONSISTENT; if (pMechanism->mechanism == CKM_EC_EDWARDS_KEY_PAIR_GEN && keyType != CKK_EC_EDWARDS) return CKR_TEMPLATE_INCONSISTENT; + if (pMechanism->mechanism == CKM_ML_DSA_KEY_PAIR_GEN && keyType != CKK_ML_DSA) + return CKR_TEMPLATE_INCONSISTENT; // Check user credentials CK_RV rv = haveWrite(session->getState(), ispublicKeyToken || isprivateKeyToken, ispublicKeyPrivate || isprivateKeyPrivate); @@ -6242,6 +6451,16 @@ CK_RV SoftHSM::C_GenerateKeyPair ispublicKeyToken, ispublicKeyPrivate, isprivateKeyToken, isprivateKeyPrivate); } +#ifdef WITH_ML_DSA + if (pMechanism->mechanism == CKM_ML_DSA_KEY_PAIR_GEN) + { + return this->generateMLDSA(hSession, + pPublicKeyTemplate, ulPublicKeyAttributeCount, + pPrivateKeyTemplate, ulPrivateKeyAttributeCount, + phPublicKey, phPrivateKey, + ispublicKeyToken, ispublicKeyPrivate, isprivateKeyToken, isprivateKeyPrivate); + } +#endif return CKR_GENERAL_ERROR; } @@ -6354,23 +6573,23 @@ CK_RV SoftHSM::WrapKeySym case CKM_AES_CBC: algo = SymAlgo::AES; break; - + case CKM_AES_CBC_PAD: blocksize = 16; wrappedlen = RFC5652Pad(keydata, blocksize); algo = SymAlgo::AES; break; - + case CKM_DES3_CBC: algo = SymAlgo::DES3; break; - + case CKM_DES3_CBC_PAD: blocksize = 8; wrappedlen = RFC5652Pad(keydata, blocksize); algo = SymAlgo::DES3; break; - + default: return CKR_MECHANISM_INVALID; } @@ -6400,7 +6619,7 @@ CK_RV SoftHSM::WrapKeySym case CKM_DES3_CBC_PAD: iv.resize(blocksize); memcpy(&iv[0], pMechanism->pParameter, blocksize); - + if (!cipher->encryptInit(wrappingkey, SymMode::CBC, iv, false)) { cipher->recycleKey(wrappingkey); @@ -6820,14 +7039,19 @@ CK_RV SoftHSM::C_WrapKey break; #endif #ifdef WITH_EDDSA - case CKK_EC_EDWARDS: - alg = AsymAlgo::EDDSA; + case CKK_EC_EDWARDS: + alg = AsymAlgo::EDDSA; break; #endif #ifdef WITH_GOST case CKK_GOSTR3410: alg = AsymAlgo::GOST; break; +#endif +#ifdef WITH_ML_DSA + case CKK_ML_DSA: + alg = AsymAlgo::MLDSA; + break; #endif default: return CKR_KEY_NOT_WRAPPABLE; @@ -6859,7 +7083,7 @@ CK_RV SoftHSM::C_WrapKey break; #endif #ifdef WITH_EDDSA - case CKK_EC_EDWARDS: + case CKK_EC_EDWARDS: rv = getEDPrivateKey((EDPrivateKey*)privateKey, token, key); break; #endif @@ -6867,6 +7091,11 @@ CK_RV SoftHSM::C_WrapKey case CKK_GOSTR3410: rv = getGOSTPrivateKey((GOSTPrivateKey*)privateKey, token, key); break; +#endif +#ifdef WITH_ML_DSA + case CKK_ML_DSA: + rv = MLDSAUtil::getMLDSAPrivateKey((MLDSAPrivateKey*)privateKey, token, key); + break; #endif } if (rv != CKR_OK) @@ -6929,7 +7158,7 @@ CK_RV SoftHSM::UnwrapKeySym SymWrap::Type mode = SymWrap::Unknown; size_t bb = 8; size_t blocksize = 0; - + switch(pMechanism->mechanism) { #ifdef HAVE_AES_KEY_WRAP case CKM_AES_KEY_WRAP: @@ -6947,12 +7176,12 @@ CK_RV SoftHSM::UnwrapKeySym algo = SymAlgo::AES; blocksize = 16; break; - + case CKM_DES3_CBC_PAD: algo = SymAlgo::DES3; blocksize = 8; break; - + default: return CKR_MECHANISM_INVALID; } @@ -6975,14 +7204,14 @@ CK_RV SoftHSM::UnwrapKeySym ByteString iv; ByteString decryptedFinal; CK_RV rv = CKR_OK; - + 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); @@ -7011,7 +7240,7 @@ CK_RV SoftHSM::UnwrapKeySym return CKR_GENERAL_ERROR; // TODO should be another error } break; - + default: // Unwrap the key rv = CKR_OK; @@ -7302,7 +7531,7 @@ CK_RV SoftHSM::C_UnwrapKey pMechanism->ulParameterLen != 8) return CKR_ARGUMENTS_BAD; break; - + default: return CKR_MECHANISM_INVALID; } @@ -7346,7 +7575,7 @@ CK_RV SoftHSM::C_UnwrapKey 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; - + // Check if the unwrapping key can be used for unwrapping if (unwrapKey->getBooleanValue(CKA_UNWRAP, false) == false) return CKR_KEY_FUNCTION_NOT_PERMITTED; @@ -7543,6 +7772,12 @@ CK_RV SoftHSM::C_UnwrapKey { bOK = bOK && setGOSTPrivateKey(osobject, keydata, token, isPrivate != CK_FALSE); } +#endif +#ifdef WITH_ML_DSA + else if (keyType == CKK_ML_DSA) + { + bOK = bOK && MLDSAUtil::setMLDSAPrivateKey(osobject, keydata, token, isPrivate != CK_FALSE); + } #endif else bOK = false; @@ -8151,11 +8386,11 @@ CK_RV SoftHSM::generateAES if (rv == CKR_OK) { OSObject* osobject = (OSObject*)handleManager->getObject(*phKey); - if (osobject == NULL_PTR || !osobject->isValid()) + if (osobject == NULL_PTR || !osobject->isValid()) { rv = CKR_FUNCTION_FAILED; - } - else if (osobject->startTransaction()) + } + else if (osobject->startTransaction()) { bool bOK = true; @@ -9935,6 +10170,266 @@ CK_RV SoftHSM::generateED return rv; } +#ifdef WITH_ML_DSA +// Generate an MLDSA key pair +CK_RV SoftHSM::generateMLDSA +(CK_SESSION_HANDLE hSession, + CK_ATTRIBUTE_PTR pPublicKeyTemplate, + CK_ULONG ulPublicKeyAttributeCount, + CK_ATTRIBUTE_PTR pPrivateKeyTemplate, + CK_ULONG ulPrivateKeyAttributeCount, + CK_OBJECT_HANDLE_PTR phPublicKey, + CK_OBJECT_HANDLE_PTR phPrivateKey, + CK_BBOOL isPublicKeyOnToken, + CK_BBOOL isPublicKeyPrivate, + CK_BBOOL isPrivateKeyOnToken, + CK_BBOOL isPrivateKeyPrivate) +{ + *phPublicKey = CK_INVALID_HANDLE; + *phPrivateKey = CK_INVALID_HANDLE; + + // Get the session + Session* session = (Session*)handleManager->getSession(hSession); + if (session == NULL) + return CKR_SESSION_HANDLE_INVALID; + + // Get the token + Token* token = session->getToken(); + if (token == NULL) + return CKR_GENERAL_ERROR; + + // Extract desired key information + CK_ULONG paramSet = 0; + for (CK_ULONG i = 0; i < ulPublicKeyAttributeCount; i++) + { + switch (pPublicKeyTemplate[i].type) + { + case CKA_PARAMETER_SET: + if (pPublicKeyTemplate[i].ulValueLen != sizeof(CK_ULONG)) { + INFO_MSG("CKA_PARAMETER_SET must be sizeof(CK_ULONG)"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + paramSet = *(CK_ULONG*)pPublicKeyTemplate[i].pValue; + break; + default: + break; + } + } + + // The parameters must be specified to be able to generate a key pair. + if (paramSet == 0) { + INFO_MSG("Missing parameter(s) in pPublicKeyTemplate"); + return CKR_TEMPLATE_INCOMPLETE; + } + + if (paramSet != CKP_ML_DSA_44 && paramSet != CKP_ML_DSA_65 && paramSet != CKP_ML_DSA_87) { + INFO_MSG("Unsupported parameter set: %lu", (unsigned long)paramSet); + return CKR_PARAMETER_SET_NOT_SUPPORTED; + } + + // Set the parameters + MLDSAParameters p; + p.setParameterSet(paramSet); + + // Generate key pair + AsymmetricKeyPair* kp = NULL; + AsymmetricAlgorithm* mldsa = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::MLDSA); + if (mldsa == NULL) return CKR_GENERAL_ERROR; + if (!mldsa->generateKeyPair(&kp, &p)) + { + ERROR_MSG("Could not generate key pair"); + CryptoFactory::i()->recycleAsymmetricAlgorithm(mldsa); + return CKR_GENERAL_ERROR; + } + + MLDSAPublicKey* pub = (MLDSAPublicKey*) kp->getPublicKey(); + MLDSAPrivateKey* priv = (MLDSAPrivateKey*) kp->getPrivateKey(); + + CK_RV rv = CKR_OK; + + // Create a public key using C_CreateObject + if (rv == CKR_OK) + { + const CK_ULONG maxAttribs = 32; + CK_OBJECT_CLASS publicKeyClass = CKO_PUBLIC_KEY; + CK_KEY_TYPE publicKeyType = CKK_ML_DSA; + CK_ATTRIBUTE publicKeyAttribs[maxAttribs] = { + { CKA_CLASS, &publicKeyClass, sizeof(publicKeyClass) }, + { CKA_TOKEN, &isPublicKeyOnToken, sizeof(isPublicKeyOnToken) }, + { CKA_PRIVATE, &isPublicKeyPrivate, sizeof(isPublicKeyPrivate) }, + { CKA_KEY_TYPE, &publicKeyType, sizeof(publicKeyType) }, + }; + CK_ULONG publicKeyAttribsCount = 4; + + // Add the additional + if (ulPublicKeyAttributeCount > (maxAttribs - publicKeyAttribsCount)) + rv = CKR_TEMPLATE_INCONSISTENT; + for (CK_ULONG i=0; i < ulPublicKeyAttributeCount && rv == CKR_OK; ++i) + { + switch (pPublicKeyTemplate[i].type) + { + case CKA_CLASS: + case CKA_TOKEN: + case CKA_PRIVATE: + case CKA_KEY_TYPE: + continue; + default: + publicKeyAttribs[publicKeyAttribsCount++] = pPublicKeyTemplate[i]; + } + } + + if (rv == CKR_OK) + rv = this->CreateObject(hSession,publicKeyAttribs,publicKeyAttribsCount,phPublicKey,OBJECT_OP_GENERATE); + + // Store the attributes that are being supplied by the key generation to the object + if (rv == CKR_OK) + { + OSObject* osobject = (OSObject*)handleManager->getObject(*phPublicKey); + if (osobject == NULL_PTR || !osobject->isValid()) { + rv = CKR_FUNCTION_FAILED; + } else if (osobject->startTransaction()) { + bool bOK = true; + + // Common Key Attributes + bOK = bOK && osobject->setAttribute(CKA_LOCAL,true); + CK_ULONG ulKeyGenMechanism = (CK_ULONG)CKM_ML_DSA_KEY_PAIR_GEN; + bOK = bOK && osobject->setAttribute(CKA_KEY_GEN_MECHANISM,ulKeyGenMechanism); + + // ML-DSA Public Key Attributes + ByteString value; + if (isPublicKeyPrivate) + { + token->encrypt(pub->getValue(), value); + } + else + { + value = pub->getValue(); + } + bOK = bOK && osobject->setAttribute(CKA_PARAMETER_SET, pub->getParameterSet()); + bOK = bOK && osobject->setAttribute(CKA_VALUE, value); + + if (bOK) + bOK = osobject->commitTransaction(); + else + osobject->abortTransaction(); + + if (!bOK) + rv = CKR_FUNCTION_FAILED; + } else + rv = CKR_FUNCTION_FAILED; + } + } + + // Create a private key using C_CreateObject + if (rv == CKR_OK) + { + const CK_ULONG maxAttribs = 32; + CK_OBJECT_CLASS privateKeyClass = CKO_PRIVATE_KEY; + CK_KEY_TYPE privateKeyType = CKK_ML_DSA; + CK_ATTRIBUTE privateKeyAttribs[maxAttribs] = { + { CKA_CLASS, &privateKeyClass, sizeof(privateKeyClass) }, + { CKA_TOKEN, &isPrivateKeyOnToken, sizeof(isPrivateKeyOnToken) }, + { CKA_PRIVATE, &isPrivateKeyPrivate, sizeof(isPrivateKeyPrivate) }, + { CKA_KEY_TYPE, &privateKeyType, sizeof(privateKeyType) }, + }; + CK_ULONG privateKeyAttribsCount = 4; + if (ulPrivateKeyAttributeCount > (maxAttribs - privateKeyAttribsCount)) + rv = CKR_TEMPLATE_INCONSISTENT; + for (CK_ULONG i=0; i < ulPrivateKeyAttributeCount && rv == CKR_OK; ++i) + { + switch (pPrivateKeyTemplate[i].type) + { + case CKA_CLASS: + case CKA_TOKEN: + case CKA_PRIVATE: + case CKA_KEY_TYPE: + continue; + default: + privateKeyAttribs[privateKeyAttribsCount++] = pPrivateKeyTemplate[i]; + } + } + + if (rv == CKR_OK) + rv = this->CreateObject(hSession,privateKeyAttribs,privateKeyAttribsCount,phPrivateKey,OBJECT_OP_GENERATE); + + // Store the attributes that are being supplied by the key generation to the object + if (rv == CKR_OK) + { + OSObject* osobject = (OSObject*)handleManager->getObject(*phPrivateKey); + if (osobject == NULL_PTR || !osobject->isValid()) { + rv = CKR_FUNCTION_FAILED; + } else if (osobject->startTransaction()) { + bool bOK = true; + + // Common Key Attributes + bOK = bOK && osobject->setAttribute(CKA_LOCAL,true); + CK_ULONG ulKeyGenMechanism = (CK_ULONG)CKM_ML_DSA_KEY_PAIR_GEN; + bOK = bOK && osobject->setAttribute(CKA_KEY_GEN_MECHANISM,ulKeyGenMechanism); + + // Common Private Key Attributes + bool bAlwaysSensitive = osobject->getBooleanValue(CKA_SENSITIVE, false); + bOK = bOK && osobject->setAttribute(CKA_ALWAYS_SENSITIVE,bAlwaysSensitive); + bool bNeverExtractable = osobject->getBooleanValue(CKA_EXTRACTABLE, false) == false; + bOK = bOK && osobject->setAttribute(CKA_NEVER_EXTRACTABLE, bNeverExtractable); + + // MLDSA Private Key Attributes + ByteString value; + ByteString seed; + if (isPrivateKeyPrivate) + { + token->encrypt(priv->getValue(), value); + token->encrypt(priv->getSeed(), seed); + } + else + { + value = priv->getValue(); + seed = priv->getSeed(); + } + + bOK = bOK && osobject->setAttribute(CKA_PARAMETER_SET, priv->getParameterSet()); + bOK = bOK && osobject->setAttribute(CKA_VALUE, value); + bOK = bOK && osobject->setAttribute(CKA_SEED, seed); + + if (bOK) + bOK = osobject->commitTransaction(); + else + osobject->abortTransaction(); + + if (!bOK) + rv = CKR_FUNCTION_FAILED; + } else + rv = CKR_FUNCTION_FAILED; + } + } + + // Clean up + mldsa->recycleKeyPair(kp); + CryptoFactory::i()->recycleAsymmetricAlgorithm(mldsa); + + // Remove keys that may have been created already when the function fails. + if (rv != CKR_OK) + { + if (*phPrivateKey != CK_INVALID_HANDLE) + { + OSObject* ospriv = (OSObject*)handleManager->getObject(*phPrivateKey); + handleManager->destroyObject(*phPrivateKey); + if (ospriv) ospriv->destroyObject(); + *phPrivateKey = CK_INVALID_HANDLE; + } + + if (*phPublicKey != CK_INVALID_HANDLE) + { + OSObject* ospub = (OSObject*)handleManager->getObject(*phPublicKey); + handleManager->destroyObject(*phPublicKey); + if (ospub) ospub->destroyObject(); + *phPublicKey = CK_INVALID_HANDLE; + } + } + + return rv; +} +#endif + // Generate a DH key pair CK_RV SoftHSM::generateDH (CK_SESSION_HANDLE hSession, @@ -12664,6 +13159,8 @@ CK_RV SoftHSM::getEDPublicKey(EDPublicKey* publicKey, Token* token, OSObject* ke return CKR_OK; } + + CK_RV SoftHSM::getDHPrivateKey(DHPrivateKey* privateKey, Token* token, OSObject* key) { if (privateKey == NULL) return CKR_ARGUMENTS_BAD; diff --git a/src/lib/SoftHSM.h b/src/lib/SoftHSM.h index ba0b7c62e..ab8ac3cc2 100644 --- a/src/lib/SoftHSM.h +++ b/src/lib/SoftHSM.h @@ -371,6 +371,22 @@ class SoftHSM CK_BBOOL isOnToken, CK_BBOOL isPrivate ); +#ifdef WITH_ML_DSA + CK_RV generateMLDSA + ( + CK_SESSION_HANDLE hSession, + CK_ATTRIBUTE_PTR pPublicKeyTemplate, + CK_ULONG ulPublicKeyAttributeCount, + CK_ATTRIBUTE_PTR pPrivateKeyTemplate, + CK_ULONG ulPrivateKeyAttributeCount, + CK_OBJECT_HANDLE_PTR phPublicKey, + CK_OBJECT_HANDLE_PTR phPrivateKey, + CK_BBOOL isPublicKeyOnToken, + CK_BBOOL isPublicKeyPrivate, + CK_BBOOL isPrivateKeyOnToken, + CK_BBOOL isPrivateKeyPrivate + ); +#endif #ifdef WITH_ECC CK_RV deriveECDH ( diff --git a/src/lib/crypto/AsymmetricAlgorithm.h b/src/lib/crypto/AsymmetricAlgorithm.h index 52519db65..41eedb44f 100644 --- a/src/lib/crypto/AsymmetricAlgorithm.h +++ b/src/lib/crypto/AsymmetricAlgorithm.h @@ -53,7 +53,8 @@ struct AsymAlgo ECDH, ECDSA, GOST, - EDDSA + EDDSA, + MLDSA }; }; @@ -92,7 +93,8 @@ struct AsymMech ECDSA_SHA512, GOST, GOST_GOST, - EDDSA + EDDSA, + MLDSA }; }; @@ -116,6 +118,40 @@ struct RSA_PKCS_PSS_PARAMS size_t sLen; }; +struct Hedge +{ + enum Type + { + HEDGE_PREFERRED, + HEDGE_REQUIRED, + DETERMINISTIC_REQUIRED + }; +}; + +struct SIGN_ADDITIONAL_CONTEXT +{ + Hedge::Type hedgeType; + const unsigned char* contextAsChar; + size_t contextLength; + + // Prevent shallow copies (Session::setParameters handles deep-copy) + SIGN_ADDITIONAL_CONTEXT(const SIGN_ADDITIONAL_CONTEXT&) = delete; + SIGN_ADDITIONAL_CONTEXT& operator=(const SIGN_ADDITIONAL_CONTEXT&) = delete; + + SIGN_ADDITIONAL_CONTEXT(): + hedgeType(Hedge::Type::HEDGE_PREFERRED), + contextAsChar(NULL), + contextLength(0){} + SIGN_ADDITIONAL_CONTEXT(Hedge::Type hedgeType): + hedgeType(hedgeType), + contextAsChar(NULL), + contextLength(0){} + SIGN_ADDITIONAL_CONTEXT(Hedge::Type hedgeType, const unsigned char* contextAsChar, size_t contextLength): + hedgeType(hedgeType), + contextAsChar(contextAsChar), + contextLength(contextLength){} +}; + class AsymmetricAlgorithm { public: diff --git a/src/lib/crypto/CMakeLists.txt b/src/lib/crypto/CMakeLists.txt index 4b7c22e65..26061966f 100644 --- a/src/lib/crypto/CMakeLists.txt +++ b/src/lib/crypto/CMakeLists.txt @@ -28,6 +28,10 @@ set(SOURCES AESKey.cpp GOSTPublicKey.cpp HashAlgorithm.cpp MacAlgorithm.cpp + MLDSAParameters.cpp + MLDSAPrivateKey.cpp + MLDSAPublicKey.cpp + MLDSAUtil.cpp RSAParameters.cpp RSAPrivateKey.cpp RSAPublicKey.cpp @@ -69,6 +73,10 @@ if(WITH_OPENSSL) OSSLGOSTR3411.cpp OSSLHMAC.cpp OSSLMD5.cpp + OSSLMLDSA.cpp + OSSLMLDSAKeyPair.cpp + OSSLMLDSAPrivateKey.cpp + OSSLMLDSAPublicKey.cpp OSSLRNG.cpp OSSLRSA.cpp OSSLRSAKeyPair.cpp diff --git a/src/lib/crypto/MLDSAParameters.cpp b/src/lib/crypto/MLDSAParameters.cpp new file mode 100644 index 000000000..bd992d0ae --- /dev/null +++ b/src/lib/crypto/MLDSAParameters.cpp @@ -0,0 +1,51 @@ +/***************************************************************************** + MLDSAParameters.cpp + + ML-DSA parameters (only used for key generation) + *****************************************************************************/ + +#include "config.h" +#include "log.h" +#include "MLDSAParameters.h" +#include + +// The type +/*static*/ const char* MLDSAParameters::type = "ML-DSA parameters"; + +// Set the parameter set +void MLDSAParameters::setParameterSet(const unsigned long inParameterSet) +{ + parameterSet = inParameterSet; +} + +// Get the parameter set +unsigned long MLDSAParameters::getParameterSet() const +{ + return parameterSet; +} + +// Are the parameters of the given type? +bool MLDSAParameters::areOfType(const char* inType) +{ + return (strcmp(type, inType) == 0); +} + +// Serialisation +ByteString MLDSAParameters::serialise() const +{ + return ByteString(getParameterSet()); +} + +bool MLDSAParameters::deserialise(ByteString& serialised) +{ + + if (serialised.size() == 0) + { + return false; + } + + setParameterSet(serialised.long_val()); + + return true; +} + diff --git a/src/lib/crypto/MLDSAParameters.h b/src/lib/crypto/MLDSAParameters.h new file mode 100644 index 000000000..15d6937ff --- /dev/null +++ b/src/lib/crypto/MLDSAParameters.h @@ -0,0 +1,74 @@ +/***************************************************************************** + MLDSAParameters.h + + ML-DSA parameters (only used for key generation) + *****************************************************************************/ + +#ifndef _SOFTHSM_V2_MLDSAPARAMETERS_H +#define _SOFTHSM_V2_MLDSAPARAMETERS_H + +#include +#include +#include "config.h" +#include "ByteString.h" +#include "AsymmetricParameters.h" + + +class MLDSAParameters : public AsymmetricParameters +{ +public: + // The type + static const char* type; + + // Get the ML-DSA parameter set + virtual unsigned long getParameterSet() const; + + // Setters for the ML-DSA parameter set + virtual void setParameterSet(const unsigned long parameterSet); + + // Are the parameters of the given type? + virtual bool areOfType(const char* inType); + + // Serialisation + virtual ByteString serialise() const; + virtual bool deserialise(ByteString& serialised); + + /* ML-DSA values for CKA_PARAMETER_SETS + typedef CK_ULONG CK_ML_DSA_PARAMETER_SET_TYPE; + #define CKP_ML_DSA_44 0x00000001UL + #define CKP_ML_DSA_65 0x00000002UL + #define CKP_ML_DSA_87 0x00000003UL + */ + static const unsigned long ML_DSA_44_PARAMETER_SET = CKP_ML_DSA_44; + static const unsigned long ML_DSA_65_PARAMETER_SET = CKP_ML_DSA_65; + static const unsigned long ML_DSA_87_PARAMETER_SET = CKP_ML_DSA_87; + + /* + From https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.204.pdf + Table 2. Sizes (in bytes) of keys and signatures of ML-DSA + Private Key | Public Key | Signature Size + ML-DSA-44 2560 | 1312 | 2420 + ML-DSA-65 4032 | 1952 | 3309 + ML-DSA-87 4896 | 2592 | 4627 + */ + + static const unsigned long ML_DSA_44_PRIV_LENGTH = 2560; + static const unsigned long ML_DSA_65_PRIV_LENGTH = 4032; + static const unsigned long ML_DSA_87_PRIV_LENGTH = 4896; + + static const unsigned long ML_DSA_44_PUB_LENGTH = 1312; + static const unsigned long ML_DSA_65_PUB_LENGTH = 1952; + static const unsigned long ML_DSA_87_PUB_LENGTH = 2592; + + static const unsigned long ML_DSA_44_SIGNATURE_LENGTH = 2420; + static const unsigned long ML_DSA_65_SIGNATURE_LENGTH = 3309; + static const unsigned long ML_DSA_87_SIGNATURE_LENGTH = 4627; + + +private: + unsigned long parameterSet; + +}; + +#endif // !_SOFTHSM_V2_MLDSAPARAMETERS_H + diff --git a/src/lib/crypto/MLDSAPrivateKey.cpp b/src/lib/crypto/MLDSAPrivateKey.cpp new file mode 100644 index 000000000..0a17ed6e6 --- /dev/null +++ b/src/lib/crypto/MLDSAPrivateKey.cpp @@ -0,0 +1,98 @@ +/***************************************************************************** + MLDSAPrivateKey.cpp + + ML-DSA private key class + *****************************************************************************/ + +#include "config.h" +#include "log.h" +#include "MLDSAParameters.h" +#include "MLDSAPrivateKey.h" +#include + +// Set the type +/*static*/ const char* MLDSAPrivateKey::type = "Abstract ML-DSA private key"; + +// Check if the key is of the given type +bool MLDSAPrivateKey::isOfType(const char* inType) +{ + return !strcmp(type, inType); +} + +unsigned long MLDSAPrivateKey::getBitLength() const +{ + return getValue().bits(); +} + +// Get the parameter set +unsigned long MLDSAPrivateKey::getParameterSet() const +{ + switch(value.size()) { + case MLDSAParameters::ML_DSA_44_PRIV_LENGTH: + return MLDSAParameters::ML_DSA_44_PARAMETER_SET; + case MLDSAParameters::ML_DSA_65_PRIV_LENGTH: + return MLDSAParameters::ML_DSA_65_PARAMETER_SET; + case MLDSAParameters::ML_DSA_87_PRIV_LENGTH: + return MLDSAParameters::ML_DSA_87_PARAMETER_SET; + } + return 0UL; +} + +// Get the signatureLength length +unsigned long MLDSAPrivateKey::getOutputLength() const +{ + switch(value.size()) { + case MLDSAParameters::ML_DSA_44_PRIV_LENGTH: + return MLDSAParameters::ML_DSA_44_SIGNATURE_LENGTH; + case MLDSAParameters::ML_DSA_65_PRIV_LENGTH: + return MLDSAParameters::ML_DSA_65_SIGNATURE_LENGTH; + case MLDSAParameters::ML_DSA_87_PRIV_LENGTH: + return MLDSAParameters::ML_DSA_87_SIGNATURE_LENGTH; + } + return 0UL; +} + +void MLDSAPrivateKey::setValue(const ByteString& inValue) +{ + value = inValue; +} + +const ByteString& MLDSAPrivateKey::getValue() const +{ + return value; +} + +void MLDSAPrivateKey::setSeed(const ByteString& inSeed) +{ + seed = inSeed; +} + +const ByteString& MLDSAPrivateKey::getSeed() const +{ + return seed; +} + +// Serialisation +ByteString MLDSAPrivateKey::serialise() const +{ + return seed.serialise() + + value.serialise(); +} + +bool MLDSAPrivateKey::deserialise(ByteString& serialised) +{ + ByteString deserializedSeed = ByteString::chainDeserialise(serialised); + ByteString deserializedValue = ByteString::chainDeserialise(serialised); + + if ((deserializedSeed.size() == 0) || + (deserializedValue.size() == 0)) + { + return false; + } + + setSeed(deserializedSeed); + setValue(deserializedValue); + + return true; +} + diff --git a/src/lib/crypto/MLDSAPrivateKey.h b/src/lib/crypto/MLDSAPrivateKey.h new file mode 100644 index 000000000..ba6c823f3 --- /dev/null +++ b/src/lib/crypto/MLDSAPrivateKey.h @@ -0,0 +1,50 @@ +/***************************************************************************** + MLDSAPrivateKey.h + + ML-DSA private key class + *****************************************************************************/ + +#ifndef _SOFTHSM_V2_MLDSAPRIVATEKEY_H +#define _SOFTHSM_V2_MLDSAPRIVATEKEY_H + +#include "config.h" +#include "PrivateKey.h" + +class MLDSAPrivateKey : public PrivateKey +{ + public: + // The type + static const char* type; + + // Check if the key is of the given type + virtual bool isOfType(const char* inType); + + // Get the ML-DSA parameter set + virtual unsigned long getParameterSet() const; + + // Get the signature length + virtual unsigned long getOutputLength() const; + + // Get the bit length + virtual unsigned long getBitLength() const; + + // Setters for the ML-DSA private key components + virtual void setValue(const ByteString& value); + virtual void setSeed(const ByteString& seed); + + // Getters for the ML-DSA private key components + virtual const ByteString& getValue() const; + virtual const ByteString& getSeed() const; + + // Serialisation + virtual ByteString serialise() const; + virtual bool deserialise(ByteString& serialised); + +protected: + + ByteString value; + ByteString seed; +}; + +#endif // !_SOFTHSM_V2_MLDSAPRIVATEKEY_H + diff --git a/src/lib/crypto/MLDSAPublicKey.cpp b/src/lib/crypto/MLDSAPublicKey.cpp new file mode 100644 index 000000000..2358770d9 --- /dev/null +++ b/src/lib/crypto/MLDSAPublicKey.cpp @@ -0,0 +1,84 @@ +/***************************************************************************** + MLDSAPublicKey.cpp + + ML-DSA public key class + *****************************************************************************/ + +#include "config.h" +#include "log.h" +#include "MLDSAParameters.h" +#include "MLDSAPublicKey.h" +#include + +// Set the type +/*static*/ const char* MLDSAPublicKey::type = "Abstract ML-DSA public key"; + +// Check if the key is of the given type +bool MLDSAPublicKey::isOfType(const char* inType) +{ + return !strcmp(type, inType); +} + +unsigned long MLDSAPublicKey::getBitLength() const +{ + return getValue().bits(); +} + +// Get the bit length +unsigned long MLDSAPublicKey::getParameterSet() const +{ + switch(value.size()) { + case MLDSAParameters::ML_DSA_44_PUB_LENGTH: + return MLDSAParameters::ML_DSA_44_PARAMETER_SET; + case MLDSAParameters::ML_DSA_65_PUB_LENGTH: + return MLDSAParameters::ML_DSA_65_PARAMETER_SET; + case MLDSAParameters::ML_DSA_87_PUB_LENGTH: + return MLDSAParameters::ML_DSA_87_PARAMETER_SET; + } + return 0UL; +} + +// Get the signatureLength length +unsigned long MLDSAPublicKey::getOutputLength() const +{ + switch(value.size()) { + case MLDSAParameters::ML_DSA_44_PUB_LENGTH: + return MLDSAParameters::ML_DSA_44_SIGNATURE_LENGTH; + case MLDSAParameters::ML_DSA_65_PUB_LENGTH: + return MLDSAParameters::ML_DSA_65_SIGNATURE_LENGTH; + case MLDSAParameters::ML_DSA_87_PUB_LENGTH: + return MLDSAParameters::ML_DSA_87_SIGNATURE_LENGTH; + } + return 0UL; +} + +const ByteString& MLDSAPublicKey::getValue() const +{ + return value; +} + +void MLDSAPublicKey::setValue(const ByteString& inValue) +{ + value = inValue; +} + +// Serialisation +ByteString MLDSAPublicKey::serialise() const +{ + return value.serialise(); +} + +bool MLDSAPublicKey::deserialise(ByteString& serialised) +{ + ByteString deserializedValue = ByteString::chainDeserialise(serialised); + + if ((deserializedValue.size() == 0)) + { + return false; + } + + setValue(deserializedValue); + + return true; +} + diff --git a/src/lib/crypto/MLDSAPublicKey.h b/src/lib/crypto/MLDSAPublicKey.h new file mode 100644 index 000000000..3fa1dadb5 --- /dev/null +++ b/src/lib/crypto/MLDSAPublicKey.h @@ -0,0 +1,48 @@ +/***************************************************************************** + MLDSAPublicKey.h + + ML-DSA public key class + *****************************************************************************/ + +#ifndef _SOFTHSM_V2_MLDSAPUBLICKEY_H +#define _SOFTHSM_V2_MLDSAPUBLICKEY_H + +#include "config.h" +#include "PublicKey.h" +#include "ByteString.h" + +class MLDSAPublicKey : public PublicKey +{ +public: + // The type + static const char* type; + + // Check if the key is of the given type + virtual bool isOfType(const char* inType); + + // Get the parameter set + virtual unsigned long getParameterSet() const; + + // Get the signature length + virtual unsigned long getOutputLength() const; + + // Get the bit length + virtual unsigned long getBitLength() const; + + virtual void setValue(const ByteString& value); + + // Getters for the ML-DSA public key components + virtual const ByteString& getValue() const; + + // Serialisation + virtual ByteString serialise() const; + virtual bool deserialise(ByteString& serialised); + +protected: + + // Public components + ByteString value; +}; + +#endif // !_SOFTHSM_V2_MLDSAPUBLICKEY_H + diff --git a/src/lib/crypto/MLDSAUtil.cpp b/src/lib/crypto/MLDSAUtil.cpp new file mode 100644 index 000000000..374678f9b --- /dev/null +++ b/src/lib/crypto/MLDSAUtil.cpp @@ -0,0 +1,113 @@ +/***************************************************************************** + MLDSAUtil.cpp + + ML-DSA convenience functions + *****************************************************************************/ + +#include "config.h" +#ifdef WITH_ML_DSA +#include "MLDSAUtil.h" + +/*static*/ CK_RV MLDSAUtil::getMLDSAPrivateKey(MLDSAPrivateKey* privateKey, Token* token, OSObject* key) +{ + if (privateKey == NULL) return CKR_ARGUMENTS_BAD; + if (token == NULL) return CKR_ARGUMENTS_BAD; + if (key == NULL) return CKR_ARGUMENTS_BAD; + + // Get the CKA_PRIVATE attribute, when the attribute is not present use default false + bool isKeyPrivate = key->getBooleanValue(CKA_PRIVATE, false); + + // ML-DSA Private Key Attributes + ByteString value; + ByteString seed; + if (isKeyPrivate) + { + bool bOK = true; + bOK = bOK && token->decrypt(key->getByteStringValue(CKA_VALUE), value); + bOK = bOK && token->decrypt(key->getByteStringValue(CKA_SEED), seed); + if (!bOK) + return CKR_GENERAL_ERROR; + } + else + { + value = key->getByteStringValue(CKA_VALUE); + seed = key->getByteStringValue(CKA_SEED); + } + + privateKey->setValue(value); + privateKey->setSeed(seed); + + return CKR_OK; +} + +/*static*/ CK_RV MLDSAUtil::getMLDSAPublicKey(MLDSAPublicKey* publicKey, Token* token, OSObject* key) +{ + if (publicKey == NULL) return CKR_ARGUMENTS_BAD; + if (token == NULL) return CKR_ARGUMENTS_BAD; + if (key == NULL) return CKR_ARGUMENTS_BAD; + + // Get the CKA_PRIVATE attribute, when the attribute is not present use default false + bool isKeyPrivate = key->getBooleanValue(CKA_PRIVATE, false); + + // EC Public Key Attributes + ByteString value; + if (isKeyPrivate) + { + bool bOK = true; + bOK = bOK && token->decrypt(key->getByteStringValue(CKA_VALUE), value); + if (!bOK) + return CKR_GENERAL_ERROR; + } + else + { + value = key->getByteStringValue(CKA_VALUE); + } + + publicKey->setValue(value); + + return CKR_OK; +} + +/*static*/ bool MLDSAUtil::setMLDSAPrivateKey(OSObject* key, const ByteString &ber, Token* token, bool isPrivate) +{ + AsymmetricAlgorithm* mldsa = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::MLDSA); + if (mldsa == NULL) + return false; + PrivateKey* priv = mldsa->newPrivateKey(); + if (priv == NULL) + { + CryptoFactory::i()->recycleAsymmetricAlgorithm(mldsa); + return false; + } + if (!priv->PKCS8Decode(ber)) + { + mldsa->recyclePrivateKey(priv); + CryptoFactory::i()->recycleAsymmetricAlgorithm(mldsa); + return false; + } + // ML-DSA Private Key Attributes + ByteString parameterSet; + ByteString seed; + ByteString value; + if (isPrivate) + { + token->encrypt(((MLDSAPrivateKey*)priv)->getSeed(), seed); + token->encrypt(((MLDSAPrivateKey*)priv)->getValue(), value); + } + else + { + seed = ((MLDSAPrivateKey*)priv)->getSeed(); + value = ((MLDSAPrivateKey*)priv)->getValue(); + } + bool bOK = true; + bOK = bOK && key->setAttribute(CKA_PARAMETER_SET, ((MLDSAPrivateKey*)priv)->getParameterSet()); + bOK = bOK && key->setAttribute(CKA_SEED, seed); + bOK = bOK && key->setAttribute(CKA_VALUE, value); + + mldsa->recyclePrivateKey(priv); + CryptoFactory::i()->recycleAsymmetricAlgorithm(mldsa); + + return bOK; +} + +#endif \ No newline at end of file diff --git a/src/lib/crypto/MLDSAUtil.h b/src/lib/crypto/MLDSAUtil.h new file mode 100644 index 000000000..74958ccba --- /dev/null +++ b/src/lib/crypto/MLDSAUtil.h @@ -0,0 +1,30 @@ +/***************************************************************************** + MLDSAUtil.h + + ML-DSA convenience functions + *****************************************************************************/ + +#ifndef _SOFTHSM_V2_MLDSAUTIL_H +#define _SOFTHSM_V2_MLDSAUTIL_H + +#include "config.h" +#ifdef WITH_ML_DSA +#include "MLDSAPrivateKey.h" +#include "MLDSAPublicKey.h" +#include "AsymmetricAlgorithm.h" +#include "CryptoFactory.h" +#include "ByteString.h" +#include "Token.h" +#include "OSObject.h" + +class MLDSAUtil +{ +public: + static CK_RV getMLDSAPrivateKey(MLDSAPrivateKey* privateKey, Token* token, OSObject* key); + static CK_RV getMLDSAPublicKey(MLDSAPublicKey* publicKey, Token* token, OSObject* key); + + static bool setMLDSAPrivateKey(OSObject* key, const ByteString &ber, Token* token, bool isPrivate); +}; + +#endif // WITH_ML_DSA +#endif // !_SOFTHSM_V2_MLDSAUTIL_H \ No newline at end of file diff --git a/src/lib/crypto/Makefile.am b/src/lib/crypto/Makefile.am index e23848fa2..8c21965ba 100644 --- a/src/lib/crypto/Makefile.am +++ b/src/lib/crypto/Makefile.am @@ -3,6 +3,8 @@ MAINTAINERCLEANFILES = $(srcdir)/Makefile.in AM_CPPFLAGS = -I$(srcdir)/.. \ -I$(srcdir)/../common \ -I$(srcdir)/../data_mgr \ + -I$(srcdir)/../slot_mgr \ + -I$(srcdir)/../object_store \ -I$(srcdir)/../pkcs11 \ @CRYPTO_INCLUDES@ @@ -28,6 +30,10 @@ libsofthsm_crypto_la_SOURCES = AESKey.cpp \ GOSTPrivateKey.cpp \ HashAlgorithm.cpp \ MacAlgorithm.cpp \ + MLDSAParameters.cpp \ + MLDSAPrivateKey.cpp \ + MLDSAPublicKey.cpp \ + MLDSAUtil.cpp \ RSAParameters.cpp \ RSAPrivateKey.cpp \ RSAPublicKey.cpp \ @@ -76,6 +82,10 @@ libsofthsm_crypto_la_SOURCES += OSSLAES.cpp \ OSSLCMAC.cpp \ OSSLHMAC.cpp \ OSSLMD5.cpp \ + OSSLMLDSA.cpp \ + OSSLMLDSAKeyPair.cpp \ + OSSLMLDSAPrivateKey.cpp \ + OSSLMLDSAPublicKey.cpp \ OSSLRNG.cpp \ OSSLRSA.cpp \ OSSLRSAKeyPair.cpp \ diff --git a/src/lib/crypto/OSSLComp.cpp b/src/lib/crypto/OSSLComp.cpp index ede710baf..efd2d1148 100644 --- a/src/lib/crypto/OSSLComp.cpp +++ b/src/lib/crypto/OSSLComp.cpp @@ -25,7 +25,7 @@ */ /***************************************************************************** - OSSLUtil.cpp + OSSLComp.cpp Adding OpenSSL forward-compatible code as suggested by OpenSSL *****************************************************************************/ diff --git a/src/lib/crypto/OSSLCryptoFactory.cpp b/src/lib/crypto/OSSLCryptoFactory.cpp index db22998ad..d37abafc0 100644 --- a/src/lib/crypto/OSSLCryptoFactory.cpp +++ b/src/lib/crypto/OSSLCryptoFactory.cpp @@ -58,6 +58,9 @@ #ifdef WITH_EDDSA #include "OSSLEDDSA.h" #endif +#ifdef WITH_ML_DSA +#include "OSSLMLDSA.h" +#endif #include #include @@ -347,6 +350,10 @@ AsymmetricAlgorithm* OSSLCryptoFactory::getAsymmetricAlgorithm(AsymAlgo::Type al #ifdef WITH_EDDSA case AsymAlgo::EDDSA: return new OSSLEDDSA(); +#endif +#ifdef WITH_ML_DSA + case AsymAlgo::MLDSA: + return new OSSLMLDSA(); #endif default: break; diff --git a/src/lib/crypto/OSSLMLDSA.cpp b/src/lib/crypto/OSSLMLDSA.cpp new file mode 100644 index 000000000..f5d689dbc --- /dev/null +++ b/src/lib/crypto/OSSLMLDSA.cpp @@ -0,0 +1,568 @@ +/***************************************************************************** + OSSLMLDSA.cpp + + OpenSSL ML-DSA asymmetric algorithm implementation + *****************************************************************************/ + +#include "config.h" +#ifdef WITH_ML_DSA +#include "log.h" +#include "OSSLMLDSA.h" +#include "CryptoFactory.h" +#include "MLDSAParameters.h" +#include "OSSLMLDSAKeyPair.h" +#include "OSSLComp.h" +#include "OSSLUtil.h" +#include +#include +#include +#include +#include +#include + +int OSSLMLDSA::OSSL_RANDOM = 0; +int OSSLMLDSA::OSSL_DETERMINISTIC = 1; + +// Signing functions +bool OSSLMLDSA::sign(PrivateKey *privateKey, const ByteString &dataToSign, + ByteString &signature, const AsymMech::Type mechanism, + const void * param /* = NULL*/, const size_t paramLen /* = 0 */) +{ + if (mechanism != AsymMech::MLDSA) + { + ERROR_MSG("Invalid mechanism supplied (%i)", mechanism); + return false; + } + + if (privateKey == NULL) + { + ERROR_MSG("No private key supplied"); + return false; + } + + // Check if the private key is the right type + if (!privateKey->isOfType(OSSLMLDSAPrivateKey::type)) + { + ERROR_MSG("Invalid key type supplied"); + + return false; + } + + OSSLMLDSAPrivateKey *pk = (OSSLMLDSAPrivateKey *)privateKey; + + EVP_PKEY *pkey = pk->getOSSLKey(); + + if (pkey == NULL) + { + ERROR_MSG("Could not get the OpenSSL private key"); + + return false; + } + + // Perform the signature operation + size_t len = pk->getOutputLength(); + if (len == 0) + { + ERROR_MSG("Could not get the signature length"); + return false; + } + signature.resize(len); + memset(&signature[0], 0, len); + + if (param != NULL && paramLen != sizeof(SIGN_ADDITIONAL_CONTEXT)) + { + ERROR_MSG("Invalid parameters supplied"); + + return false; + } + + OSSL_PARAM params[4], *p = params; + SIGN_ADDITIONAL_CONTEXT* additionalContext = (SIGN_ADDITIONAL_CONTEXT*) param; + if (additionalContext != NULL) { + Hedge::Type type = additionalContext->hedgeType; + size_t contextSize = additionalContext->contextLength; + if (contextSize > 0) { + if (contextSize > 255) { + ERROR_MSG("Invalid parameters, context length > 255"); + return false; + } + if (additionalContext->contextAsChar == NULL) { + ERROR_MSG("Invalid parameters, context pointer is NULL"); + return false; + } + const unsigned char* contextAsChars = additionalContext->contextAsChar; + *p++ = OSSL_PARAM_construct_octet_string(OSSL_SIGNATURE_PARAM_CONTEXT_STRING, (unsigned char*)contextAsChars, contextSize); + } + switch (type) { + case Hedge::Type::DETERMINISTIC_REQUIRED: + *p++ = OSSL_PARAM_construct_int(OSSL_SIGNATURE_PARAM_DETERMINISTIC, &OSSL_DETERMINISTIC); + break; + default: + *p++ = OSSL_PARAM_construct_int(OSSL_SIGNATURE_PARAM_DETERMINISTIC, &OSSL_RANDOM); + break; + } + *p = OSSL_PARAM_construct_end(); + } + + EVP_PKEY_CTX *sctx = EVP_PKEY_CTX_new_from_pkey(NULL, pkey, NULL); + if (sctx == NULL) + { + ERROR_MSG("ML-DSA sign sctx alloc failed"); + return false; + } + + unsigned long parameterSet = pk->getParameterSet(); + const char* name = OSSL::mldsaParameterSet2Name(parameterSet); + if (name == NULL) + { + ERROR_MSG("Unknown ML-DSA parameter set (%lu)", parameterSet); + EVP_PKEY_CTX_free(sctx); + return false; + } + + EVP_SIGNATURE *sig_alg = EVP_SIGNATURE_fetch(NULL, name, NULL); + if (sig_alg == NULL) { + ERROR_MSG("ML-DSA EVP_SIGNATURE_fetch failed (0x%08X)", ERR_get_error()); + EVP_PKEY_CTX_free(sctx); + return false; + } + int initRv; + if (param != NULL) { + initRv = EVP_PKEY_sign_message_init(sctx, sig_alg, params); + } + else + { + initRv = EVP_PKEY_sign_message_init(sctx, sig_alg, NULL); + } + if (!initRv) { + ERROR_MSG("ML-DSA sign_message_init failed (0x%08X)", ERR_get_error()); + EVP_SIGNATURE_free(sig_alg); + EVP_PKEY_CTX_free(sctx); + return false; + } + /* Calculate the required size for the signature by passing a NULL buffer. */ + if (EVP_PKEY_sign(sctx, NULL, &len, dataToSign.const_byte_str(), dataToSign.size()) <= 0) { + ERROR_MSG("ML-DSA sign size query failed (0x%08X)", ERR_get_error()); + EVP_SIGNATURE_free(sig_alg); + EVP_PKEY_CTX_free(sctx); + return false; + } + signature.resize(len); + if (EVP_PKEY_sign(sctx, &signature[0], &len, dataToSign.const_byte_str(), dataToSign.size()) <= 0) { + ERROR_MSG("ML-DSA sign failed (0x%08X)", ERR_get_error()); + EVP_SIGNATURE_free(sig_alg); + EVP_PKEY_CTX_free(sctx); + return false; + } + + EVP_SIGNATURE_free(sig_alg); + EVP_PKEY_CTX_free(sctx); + + return true; +} + +bool OSSLMLDSA::signInit(PrivateKey * /*privateKey*/, const AsymMech::Type /*mechanism*/, + const void * /* param = NULL */, const size_t /* paramLen = 0 */) +{ + ERROR_MSG("ML-DSA does not support multi part signing"); + + return false; +} + +bool OSSLMLDSA::signUpdate(const ByteString & /*dataToSign*/) +{ + ERROR_MSG("ML-DSA does not support multi part signing"); + + return false; +} + +bool OSSLMLDSA::signFinal(ByteString & /*signature*/) +{ + ERROR_MSG("ML-DSA does not support multi part signing"); + + return false; +} + +// Verification functions +bool OSSLMLDSA::verify(PublicKey *publicKey, const ByteString &originalData, + const ByteString &signature, const AsymMech::Type mechanism, + const void * param /* = NULL */, const size_t paramLen /* = 0 */) +{ + if (mechanism != AsymMech::MLDSA) + { + ERROR_MSG("Invalid mechanism supplied (%i)", mechanism); + return false; + } + + if (publicKey == NULL) + { + ERROR_MSG("No public key supplied"); + return false; + } + + // Check if the public key is the right type + if (!publicKey->isOfType(OSSLMLDSAPublicKey::type)) + { + ERROR_MSG("Invalid key type supplied"); + + return false; + } + + OSSLMLDSAPublicKey *pk = (OSSLMLDSAPublicKey *)publicKey; + EVP_PKEY *pkey = pk->getOSSLKey(); + + if (pkey == NULL) + { + ERROR_MSG("Could not get the OpenSSL public key"); + + return false; + } + + // Perform the verify operation + size_t len = pk->getOutputLength(); + if (len == 0) + { + ERROR_MSG("Could not get the signature length"); + return false; + } + if (signature.size() != len) + { + ERROR_MSG("Invalid buffer length"); + return false; + } + + if (param != NULL && paramLen != sizeof(SIGN_ADDITIONAL_CONTEXT)) + { + ERROR_MSG("Invalid parameters supplied"); + + return false; + } + + OSSL_PARAM params[4], *p = params; + SIGN_ADDITIONAL_CONTEXT* additionalContext = (SIGN_ADDITIONAL_CONTEXT*) param; + if (additionalContext != NULL) { + Hedge::Type type = additionalContext->hedgeType; + size_t contextSize = additionalContext->contextLength; + if (contextSize > 0) { + if (contextSize > 255) { + ERROR_MSG("Invalid parameters, context length > 255"); + return false; + } + if (additionalContext->contextAsChar == NULL) { + ERROR_MSG("Invalid parameters, context pointer is NULL"); + return false; + } + const unsigned char* contextAsChars = additionalContext->contextAsChar; + *p++ = OSSL_PARAM_construct_octet_string(OSSL_SIGNATURE_PARAM_CONTEXT_STRING, (unsigned char*)contextAsChars, contextSize); + } + switch (type) { + case Hedge::Type::DETERMINISTIC_REQUIRED: + *p++ = OSSL_PARAM_construct_int(OSSL_SIGNATURE_PARAM_DETERMINISTIC, &OSSL_DETERMINISTIC); + break; + default: + *p++ = OSSL_PARAM_construct_int(OSSL_SIGNATURE_PARAM_DETERMINISTIC, &OSSL_RANDOM); + break; + } + *p = OSSL_PARAM_construct_end(); + } + + EVP_PKEY_CTX *vctx = NULL; + EVP_SIGNATURE *sig_alg = NULL; + + vctx = EVP_PKEY_CTX_new_from_pkey(NULL, pkey, NULL); + if (vctx == NULL) { + ERROR_MSG("ML-DSA EVP_PKEY_CTX_new_from_pkey failed (0x%08X)", ERR_get_error()); + return false; + } + + unsigned long parameterSet = pk->getParameterSet(); + const char* name = OSSL::mldsaParameterSet2Name(parameterSet); + + if (name == NULL) + { + ERROR_MSG("Unknown ML-DSA parameter set (%lu)", parameterSet); + EVP_PKEY_CTX_free(vctx); + return false; + } + + sig_alg = EVP_SIGNATURE_fetch(NULL, name, NULL); + if (sig_alg == NULL) { + ERROR_MSG("ML-DSA EVP_SIGNATURE_fetch failed (0x%08X)", ERR_get_error()); + EVP_PKEY_CTX_free(vctx); + return false; + } + + int initRv; + if (param != NULL) { + initRv = EVP_PKEY_verify_message_init(vctx, sig_alg, params); + } + else + { + initRv = EVP_PKEY_verify_message_init(vctx, sig_alg, NULL); + } + + if (!initRv) { + ERROR_MSG("ML-DSA verify init failed (0x%08X)", ERR_get_error()); + EVP_PKEY_CTX_free(vctx); + EVP_SIGNATURE_free(sig_alg); + return false; + } + int verifyRV = EVP_PKEY_verify(vctx, signature.const_byte_str(), signature.size(), + originalData.const_byte_str(), originalData.size()); + EVP_PKEY_CTX_free(vctx); + EVP_SIGNATURE_free(sig_alg); + if (verifyRV != 1) + { + if (verifyRV != 0) + { + ERROR_MSG("ML-DSA verify error (0x%08X)", ERR_get_error()); + } + return false; + } + return true; +} + +bool OSSLMLDSA::verifyInit(PublicKey * /*publicKey*/, const AsymMech::Type /*mechanism*/, + const void * /* param = NULL */, const size_t /* paramLen = 0 */) +{ + ERROR_MSG("ML-DSA does not support multi part verifying"); + + return false; +} + +bool OSSLMLDSA::verifyUpdate(const ByteString & /*originalData*/) +{ + ERROR_MSG("ML-DSA does not support multi part verifying"); + + return false; +} + +bool OSSLMLDSA::verifyFinal(const ByteString & /*signature*/) +{ + ERROR_MSG("ML-DSA does not support multi part verifying"); + + return false; +} + +// Encryption functions +bool OSSLMLDSA::encrypt(PublicKey * /*publicKey*/, const ByteString & /*data*/, + ByteString & /*encryptedData*/, const AsymMech::Type /*padding*/) +{ + ERROR_MSG("ML-DSA does not support encryption"); + + return false; +} + +// Decryption functions +bool OSSLMLDSA::decrypt(PrivateKey * /*privateKey*/, const ByteString & /*encryptedData*/, + ByteString & /*data*/, const AsymMech::Type /*padding*/) +{ + ERROR_MSG("ML-DSA does not support decryption"); + + return false; +} + +unsigned long OSSLMLDSA::getMinKeySize() +{ + return MLDSAParameters::ML_DSA_44_PRIV_LENGTH; +} + +unsigned long OSSLMLDSA::getMaxKeySize() +{ + return MLDSAParameters::ML_DSA_87_PRIV_LENGTH; +} + +bool OSSLMLDSA::checkEncryptedDataSize(PrivateKey * /* privateKey*/, const ByteString & /*encryptedData*/, int * /* errorCode*/) +{ + ERROR_MSG("ML-DSA does not support encryption"); + + return false; +} + +// Key factory +bool OSSLMLDSA::generateKeyPair(AsymmetricKeyPair **ppKeyPair, AsymmetricParameters *parameters, RNG * /*rng = NULL */) +{ + // Check parameters + if ((ppKeyPair == NULL) || + (parameters == NULL)) + { + return false; + } + + if (!parameters->areOfType(MLDSAParameters::type)) + { + ERROR_MSG("Invalid parameters supplied for ML-DSA key generation"); + + return false; + } + + MLDSAParameters *params = (MLDSAParameters *)parameters; + unsigned long parameterSet = params->getParameterSet(); + const char* name = OSSL::mldsaParameterSet2Name(parameterSet); + + if (name == NULL) + { + ERROR_MSG("Unknown ML-DSA parameter set (%lu)", parameterSet); + return false; + } + + EVP_PKEY_CTX *ctx = NULL; + EVP_PKEY *pkey = NULL; + ctx = EVP_PKEY_CTX_new_from_name(NULL, name, NULL); + if (ctx == NULL) { + ERROR_MSG("ML-DSA keygen context failed (0x%08X)", ERR_get_error()); + return false; + } + int initRV = EVP_PKEY_keygen_init(ctx); + if (initRV <= 0) { + ERROR_MSG("ML-DSA keygen init failed (0x%08X)", ERR_get_error()); + EVP_PKEY_CTX_free(ctx); + return false; + } + + int keygenRV = EVP_PKEY_generate(ctx, &pkey); + if (keygenRV <= 0) { + ERROR_MSG("ML-DSA keygen failed (0x%08X)", ERR_get_error()); + EVP_PKEY_CTX_free(ctx); + return false; + } + // Create an asymmetric key-pair object to return + OSSLMLDSAKeyPair *kp = new OSSLMLDSAKeyPair(); + + // bump refcount for each wrapper + EVP_PKEY_up_ref(pkey); + ((OSSLMLDSAPrivateKey*)kp->getPrivateKey())->setFromOSSL(pkey); + EVP_PKEY_up_ref(pkey); + ((OSSLMLDSAPublicKey*) kp->getPublicKey())->setFromOSSL(pkey); + + *ppKeyPair = kp; + + // Release the context + EVP_PKEY_CTX_free(ctx); + // Release the key + EVP_PKEY_free(pkey); + + return true; +} + +bool OSSLMLDSA::reconstructKeyPair(AsymmetricKeyPair **ppKeyPair, ByteString &serialisedData) +{ + // Check input + if ((ppKeyPair == NULL) || + (serialisedData.size() == 0)) + { + return false; + } + + ByteString dPub = ByteString::chainDeserialise(serialisedData); + ByteString dPriv = ByteString::chainDeserialise(serialisedData); + + OSSLMLDSAKeyPair *kp = new OSSLMLDSAKeyPair(); + + bool rv = true; + + if (!((MLDSAPublicKey *)kp->getPublicKey())->deserialise(dPub)) + { + rv = false; + } + + if (!((MLDSAPrivateKey *)kp->getPrivateKey())->deserialise(dPriv)) + { + rv = false; + } + + if (!rv) + { + delete kp; + + return false; + } + + *ppKeyPair = kp; + + return true; +} + +bool OSSLMLDSA::reconstructPublicKey(PublicKey **ppPublicKey, ByteString &serialisedData) +{ + // Check input + if ((ppPublicKey == NULL) || + (serialisedData.size() == 0)) + { + return false; + } + + OSSLMLDSAPublicKey *pub = new OSSLMLDSAPublicKey(); + + if (!pub->deserialise(serialisedData)) + { + delete pub; + + return false; + } + + *ppPublicKey = pub; + + return true; +} + +bool OSSLMLDSA::reconstructPrivateKey(PrivateKey **ppPrivateKey, ByteString &serialisedData) +{ + // Check input + if ((ppPrivateKey == NULL) || + (serialisedData.size() == 0)) + { + return false; + } + + OSSLMLDSAPrivateKey *priv = new OSSLMLDSAPrivateKey(); + + if (!priv->deserialise(serialisedData)) + { + delete priv; + + return false; + } + + *ppPrivateKey = priv; + + return true; +} + +PublicKey *OSSLMLDSA::newPublicKey() +{ + return (PublicKey *)new OSSLMLDSAPublicKey(); +} + +PrivateKey *OSSLMLDSA::newPrivateKey() +{ + return (PrivateKey *)new OSSLMLDSAPrivateKey(); +} + +AsymmetricParameters *OSSLMLDSA::newParameters() +{ + return (AsymmetricParameters *)new MLDSAParameters(); +} + +bool OSSLMLDSA::reconstructParameters(AsymmetricParameters **ppParams, ByteString &serialisedData) +{ + // Check input parameters + if ((ppParams == NULL) || (serialisedData.size() == 0)) + { + return false; + } + + MLDSAParameters *params = new MLDSAParameters(); + + if (!params->deserialise(serialisedData)) + { + delete params; + + return false; + } + + *ppParams = params; + + return true; +} +#endif diff --git a/src/lib/crypto/OSSLMLDSA.h b/src/lib/crypto/OSSLMLDSA.h new file mode 100644 index 000000000..b9e02a71a --- /dev/null +++ b/src/lib/crypto/OSSLMLDSA.h @@ -0,0 +1,57 @@ +/***************************************************************************** + OSSLMLDSA.h + + OpenSSL ML-DSA asymmetric algorithm implementation + *****************************************************************************/ + +#ifndef _SOFTHSM_V2_OSSLMLDSA_H +#define _SOFTHSM_V2_OSSLMLDSA_H + +#include "config.h" +#include "AsymmetricAlgorithm.h" +#include + +class OSSLMLDSA : public AsymmetricAlgorithm +{ +public: + // Destructor + virtual ~OSSLMLDSA() { } + + // Signing functions + virtual bool sign(PrivateKey *privateKey, const ByteString &dataToSign, ByteString &signature, const AsymMech::Type mechanism, const void *param = NULL, const size_t paramLen = 0); + virtual bool signInit(PrivateKey* privateKey, const AsymMech::Type mechanism, const void* param = NULL, const size_t paramLen = 0); + virtual bool signUpdate(const ByteString& dataToSign); + virtual bool signFinal(ByteString& signature); + + // Verification functions + virtual bool verify(PublicKey* publicKey, const ByteString& originalData, const ByteString& signature, const AsymMech::Type mechanism, const void* param = NULL, const size_t paramLen = 0); + virtual bool verifyInit(PublicKey* publicKey, const AsymMech::Type mechanism, const void* param = NULL, const size_t paramLen = 0); + virtual bool verifyUpdate(const ByteString& originalData); + virtual bool verifyFinal(const ByteString& signature); + + // Encryption functions + virtual bool encrypt(PublicKey* publicKey, const ByteString& data, ByteString& encryptedData, const AsymMech::Type padding); + + // Decryption functions + virtual bool checkEncryptedDataSize(PrivateKey* privateKey, const ByteString& encryptedData, int* errorCode); + virtual bool decrypt(PrivateKey* privateKey, const ByteString& encryptedData, ByteString& data, const AsymMech::Type padding); + virtual unsigned long getMinKeySize(); + virtual unsigned long getMaxKeySize(); + + // Key factory + virtual bool generateKeyPair(AsymmetricKeyPair** ppKeyPair, AsymmetricParameters* parameters, RNG* rng = NULL); + virtual bool reconstructKeyPair(AsymmetricKeyPair** ppKeyPair, ByteString& serialisedData); + virtual bool reconstructPublicKey(PublicKey** ppPublicKey, ByteString& serialisedData); + virtual bool reconstructPrivateKey(PrivateKey** ppPrivateKey, ByteString& serialisedData); + virtual bool reconstructParameters(AsymmetricParameters** ppParams, ByteString& serialisedData); + virtual PublicKey* newPublicKey(); + virtual PrivateKey* newPrivateKey(); + virtual AsymmetricParameters* newParameters(); + +private: + static int OSSL_RANDOM; + static int OSSL_DETERMINISTIC; +}; + +#endif // !_SOFTHSM_V2_OSSLMLDSA_H + diff --git a/src/lib/crypto/OSSLMLDSAKeyPair.cpp b/src/lib/crypto/OSSLMLDSAKeyPair.cpp new file mode 100644 index 000000000..1fb5e5bc8 --- /dev/null +++ b/src/lib/crypto/OSSLMLDSAKeyPair.cpp @@ -0,0 +1,48 @@ +/***************************************************************************** + OSSLMLDSAKeyPair.cpp + + OpenSSL ML-DSA key-pair class + *****************************************************************************/ + +#include "config.h" +#ifdef WITH_ML_DSA +#include "log.h" +#include "OSSLMLDSAKeyPair.h" + +// Set the public key +void OSSLMLDSAKeyPair::setPublicKey(OSSLMLDSAPublicKey& publicKey) +{ + // Copy only the public material; avoid sharing OpenSSL handles + pubKey.setValue(publicKey.getValue()); +} + +// Set the private key +void OSSLMLDSAKeyPair::setPrivateKey(OSSLMLDSAPrivateKey& privateKey) +{ + // Copy only the raw material; avoid sharing OpenSSL handles + privKey.setSeed(privateKey.getSeed()); + privKey.setValue(privateKey.getValue()); +} + +// Return the public key +PublicKey* OSSLMLDSAKeyPair::getPublicKey() +{ + return &pubKey; +} + +const PublicKey* OSSLMLDSAKeyPair::getConstPublicKey() const +{ + return &pubKey; +} + +// Return the private key +PrivateKey* OSSLMLDSAKeyPair::getPrivateKey() +{ + return &privKey; +} + +const PrivateKey* OSSLMLDSAKeyPair::getConstPrivateKey() const +{ + return &privKey; +} +#endif diff --git a/src/lib/crypto/OSSLMLDSAKeyPair.h b/src/lib/crypto/OSSLMLDSAKeyPair.h new file mode 100644 index 000000000..5a89b6b51 --- /dev/null +++ b/src/lib/crypto/OSSLMLDSAKeyPair.h @@ -0,0 +1,42 @@ +/***************************************************************************** + OSSLMLDSAKeyPair.h + + OpenSSL ML-DSA key-pair class + *****************************************************************************/ + +#ifndef _SOFTHSM_V2_OSSLMLDSAKEYPAIR_H +#define _SOFTHSM_V2_OSSLMLDSAKEYPAIR_H + +#include "config.h" +#ifdef WITH_ML_DSA +#include "AsymmetricKeyPair.h" +#include "OSSLMLDSAPublicKey.h" +#include "OSSLMLDSAPrivateKey.h" + +class OSSLMLDSAKeyPair : public AsymmetricKeyPair +{ +public: + // Set the public key + void setPublicKey(OSSLMLDSAPublicKey& publicKey); + + // Set the private key + void setPrivateKey(OSSLMLDSAPrivateKey& privateKey); + + // Return the public key + virtual PublicKey* getPublicKey(); + virtual const PublicKey* getConstPublicKey() const; + + // Return the private key + virtual PrivateKey* getPrivateKey(); + virtual const PrivateKey* getConstPrivateKey() const; + +private: + // The public key + OSSLMLDSAPublicKey pubKey; + + // The private key + OSSLMLDSAPrivateKey privKey; +}; + +#endif // WITH_ML_DSA +#endif // !_SOFTHSM_V2_OSSLMLDSAKEYPAIR_H diff --git a/src/lib/crypto/OSSLMLDSAPrivateKey.cpp b/src/lib/crypto/OSSLMLDSAPrivateKey.cpp new file mode 100644 index 000000000..e52e27e9c --- /dev/null +++ b/src/lib/crypto/OSSLMLDSAPrivateKey.cpp @@ -0,0 +1,212 @@ +/***************************************************************************** + OSSLMLDSAPrivateKey.cpp + + OpenSSL ML-DSA private key class + *****************************************************************************/ + +#include "config.h" +#ifdef WITH_ML_DSA +#include "log.h" +#include "OSSLMLDSAPrivateKey.h" +#include "MLDSAParameters.h" +#include "OSSLUtil.h" +#include +#include +#include + +// Constructors +OSSLMLDSAPrivateKey::OSSLMLDSAPrivateKey() +{ + pkey = NULL; +} + +OSSLMLDSAPrivateKey::OSSLMLDSAPrivateKey(const EVP_PKEY* inMLDSAKEY) +{ + pkey = NULL; + + setFromOSSL(inMLDSAKEY); +} + +// Destructor +OSSLMLDSAPrivateKey::~OSSLMLDSAPrivateKey() +{ + if (pkey != NULL) + { + EVP_PKEY_free(pkey); + pkey = NULL; + } +} + +OSSLMLDSAPrivateKey::OSSLMLDSAPrivateKey(OSSLMLDSAPrivateKey&& other) noexcept + : MLDSAPrivateKey(std::move(other)), pkey(other.pkey) +{ + other.pkey = NULL; +} + +OSSLMLDSAPrivateKey& OSSLMLDSAPrivateKey::operator=(OSSLMLDSAPrivateKey&& other) noexcept +{ + if (this != &other) + { + MLDSAPrivateKey::operator=(std::move(other)); + if (pkey) EVP_PKEY_free(pkey); + pkey = other.pkey; + other.pkey = NULL; + } + return *this; +} + +// The type +const char* OSSLMLDSAPrivateKey::type = "OpenSSL ML-DSA Private Key"; + +// Set from OpenSSL representation +void OSSLMLDSAPrivateKey::setFromOSSL(const EVP_PKEY* inMLDSAKEY) +{ + uint8_t seed[32]; + size_t seed_len; + int rv = EVP_PKEY_get_octet_string_param(inMLDSAKEY, OSSL_PKEY_PARAM_ML_DSA_SEED, + seed, sizeof(seed), &seed_len); + if(rv && seed_len == 32) { + // seed is not mandatory for OSSL key reconstruction + ByteString seedBS = ByteString(seed, seed_len); + setSeed(seedBS); + } + + // let's use max priv length + uint8_t priv[MLDSAParameters::ML_DSA_87_PRIV_LENGTH]; + size_t priv_len; + rv = EVP_PKEY_get_octet_string_param(inMLDSAKEY, OSSL_PKEY_PARAM_PRIV_KEY, + priv, sizeof(priv), &priv_len); + if(!rv) { + ERROR_MSG("Could not get private key private, rv: %d", rv); + return; + } + + ByteString valueBS = ByteString(priv, priv_len); + + setValue(valueBS); +} + +// Check if the key is of the given type +bool OSSLMLDSAPrivateKey::isOfType(const char* inType) +{ + return !strcmp(type, inType); +} + +void OSSLMLDSAPrivateKey::setValue(const ByteString& inValue) +{ + MLDSAPrivateKey::setValue(inValue); + if (pkey != NULL) + { + EVP_PKEY_free(pkey); + pkey = NULL; + } +} + +void OSSLMLDSAPrivateKey::setSeed(const ByteString& inSeed) +{ + MLDSAPrivateKey::setSeed(inSeed); + if (pkey != NULL) + { + EVP_PKEY_free(pkey); + pkey = NULL; + } +} + +// Encode into PKCS#8 DER +ByteString OSSLMLDSAPrivateKey::PKCS8Encode() +{ + ByteString der; + EVP_PKEY* key = getOSSLKey(); + if (key == NULL) return der; + PKCS8_PRIV_KEY_INFO* p8inf = EVP_PKEY2PKCS8(key); + if (p8inf == NULL) return der; + int len = i2d_PKCS8_PRIV_KEY_INFO(p8inf, NULL); + if (len < 0) + { + PKCS8_PRIV_KEY_INFO_free(p8inf); + return der; + } + der.resize(len); + unsigned char* priv = &der[0]; + int len2 = i2d_PKCS8_PRIV_KEY_INFO(p8inf, &priv); + PKCS8_PRIV_KEY_INFO_free(p8inf); + if (len2 != len) der.wipe(); + return der; +} + +// Decode from PKCS#8 BER +bool OSSLMLDSAPrivateKey::PKCS8Decode(const ByteString& ber) +{ + int len = ber.size(); + if (len <= 0) return false; + const unsigned char* priv = ber.const_byte_str(); + PKCS8_PRIV_KEY_INFO* p8 = d2i_PKCS8_PRIV_KEY_INFO(NULL, &priv, len); + if (p8 == NULL) return false; + EVP_PKEY* localPKey = EVP_PKCS82PKEY(p8); + PKCS8_PRIV_KEY_INFO_free(p8); + if (localPKey == NULL) return false; + setFromOSSL(localPKey); + EVP_PKEY_free(localPKey); + return true; +} + +// Retrieve the OpenSSL representation of the key +EVP_PKEY* OSSLMLDSAPrivateKey::getOSSLKey() +{ + if (pkey == NULL) createOSSLKey(); + + return pkey; +} + +// Create the OpenSSL representation of the key +void OSSLMLDSAPrivateKey::createOSSLKey() +{ + if (pkey != NULL) return; + + ByteString localValue = getValue(); + + const char* name = OSSL::mldsaParameterSet2Name(getParameterSet()); + if (name == NULL) + { + ERROR_MSG("Unknown ML-DSA parameter set (value length: %zu)", localValue.size()); + return; + } + if (localValue.size() == 0) + { + ERROR_MSG("Empty ML-DSA private key value; cannot create EVP_PKEY"); + return; + } + + int selection = 0; + EVP_PKEY_CTX *ctx = NULL; + OSSL_PARAM params[3], *p = params; + + *p++ = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, + localValue.byte_str(), localValue.size()); + selection = OSSL_KEYMGMT_SELECT_PRIVATE_KEY; + + *p = OSSL_PARAM_construct_end(); + + ctx = EVP_PKEY_CTX_new_from_name(NULL, name, NULL); + if (ctx == NULL) { + ERROR_MSG("Could not create context"); + return; + } + int rv = EVP_PKEY_fromdata_init(ctx); + if (!rv) { + ERROR_MSG("Could not EVP_PKEY_fromdata_init:%d", rv); + EVP_PKEY_CTX_free(ctx); + return; + } + rv = EVP_PKEY_fromdata(ctx, &pkey, selection, params); + if (!rv) { + ERROR_MSG("Could not EVP_PKEY_fromdata:%d", rv); + EVP_PKEY_CTX_free(ctx); + return; + } + + EVP_PKEY_CTX_free(ctx); + +} + +#endif diff --git a/src/lib/crypto/OSSLMLDSAPrivateKey.h b/src/lib/crypto/OSSLMLDSAPrivateKey.h new file mode 100644 index 000000000..6ec6b67fc --- /dev/null +++ b/src/lib/crypto/OSSLMLDSAPrivateKey.h @@ -0,0 +1,69 @@ +/***************************************************************************** + OSSLMLDSAPrivateKey.h + + OpenSSL ML-DSA private key class + *****************************************************************************/ + +#ifndef _SOFTHSM_V2_OSSLMLDSAPRIVATEKEY_H +#define _SOFTHSM_V2_OSSLMLDSAPRIVATEKEY_H + +#include "config.h" +#ifdef WITH_ML_DSA +#include "MLDSAParameters.h" +#include "MLDSAPrivateKey.h" +#include +#include + +class OSSLMLDSAPrivateKey : public MLDSAPrivateKey +{ +public: + // Constructors + OSSLMLDSAPrivateKey(); + + OSSLMLDSAPrivateKey(const EVP_PKEY* inMLDSAKEY); + + // Destructor + virtual ~OSSLMLDSAPrivateKey(); + + // Non-copyable (raw ownership of EVP_PKEY) + OSSLMLDSAPrivateKey(const OSSLMLDSAPrivateKey&) = delete; + OSSLMLDSAPrivateKey& operator=(const OSSLMLDSAPrivateKey&) = delete; + + // Movable + OSSLMLDSAPrivateKey(OSSLMLDSAPrivateKey&&) noexcept; + OSSLMLDSAPrivateKey& operator=(OSSLMLDSAPrivateKey&&) noexcept; + + // The type + static const char* type; + + // Check if the key is of the given type + virtual bool isOfType(const char* inType); + + // Setters for the ML-DSA private key components + virtual void setValue(const ByteString& value); + virtual void setSeed(const ByteString& seed); + + // Encode into PKCS#8 DER + virtual ByteString PKCS8Encode(); + + // Decode from PKCS#8 BER + virtual bool PKCS8Decode(const ByteString& ber); + + // Set from OpenSSL representation + virtual void setFromOSSL(const EVP_PKEY* inMLDSAKEY); + + // Retrieve the OpenSSL representation of the key + EVP_PKEY* getOSSLKey(); + +private: + // The internal OpenSSL representation + EVP_PKEY* pkey; + + // Create the OpenSSL representation of the key + void createOSSLKey(); + +}; + +#endif // WITH_ML_DSA +#endif // !_SOFTHSM_V2_OSSLECPRIVATEKEY_H + diff --git a/src/lib/crypto/OSSLMLDSAPublicKey.cpp b/src/lib/crypto/OSSLMLDSAPublicKey.cpp new file mode 100644 index 000000000..b4b2715ff --- /dev/null +++ b/src/lib/crypto/OSSLMLDSAPublicKey.cpp @@ -0,0 +1,147 @@ +/***************************************************************************** + OSSLMLDSAPublicKey.cpp + + OpenSSL ML-DSA public key class + *****************************************************************************/ + +#include "config.h" +#ifdef WITH_ML_DSA +#include "log.h" +#include "OSSLMLDSAPublicKey.h" +#include "MLDSAParameters.h" +#include "OSSLUtil.h" +#include +#include +#include + +// Constructors +OSSLMLDSAPublicKey::OSSLMLDSAPublicKey() +{ + pkey = NULL; +} + +OSSLMLDSAPublicKey::OSSLMLDSAPublicKey(const EVP_PKEY* inEVPPKEY) +{ + pkey = NULL; + + setFromOSSL(inEVPPKEY); +} + +// Destructor +OSSLMLDSAPublicKey::~OSSLMLDSAPublicKey() +{ + if (pkey != NULL) + { + EVP_PKEY_free(pkey); + pkey = NULL; + } +} + +OSSLMLDSAPublicKey::OSSLMLDSAPublicKey(OSSLMLDSAPublicKey&& other) noexcept + : MLDSAPublicKey(std::move(other)), pkey(other.pkey) +{ + other.pkey = NULL; +} + +OSSLMLDSAPublicKey& OSSLMLDSAPublicKey::operator=(OSSLMLDSAPublicKey&& other) noexcept +{ + if (this != &other) + { + // move base + MLDSAPublicKey::operator=(std::move(other)); + // release current + if (pkey) { EVP_PKEY_free(pkey); } + // steal + pkey = other.pkey; + other.pkey = NULL; + } + return *this; +} + +// The type +/*static*/ const char* OSSLMLDSAPublicKey::type = "OpenSSL ML-DSA Public Key"; + +// Set from OpenSSL representation +void OSSLMLDSAPublicKey::setFromOSSL(const EVP_PKEY* inEVPPKEY) +{ + // let's use max pub length + uint8_t localPub[MLDSAParameters::ML_DSA_87_PUB_LENGTH]; + size_t pub_len; + int rv = EVP_PKEY_get_octet_string_param(inEVPPKEY, OSSL_PKEY_PARAM_PUB_KEY, + localPub, sizeof(localPub), &pub_len); + + if(!rv) { + ERROR_MSG("Could not get ML-DSA public key, rv: %d", rv); + return; + } + + ByteString pubBS = ByteString(localPub, pub_len); + setValue(pubBS); + +} + +// Check if the key is of the given type +bool OSSLMLDSAPublicKey::isOfType(const char* inType) +{ + return !strcmp(OSSLMLDSAPublicKey::type, inType) || MLDSAPublicKey::isOfType(inType); +} + +void OSSLMLDSAPublicKey::setValue(const ByteString& inValue) +{ + MLDSAPublicKey::setValue(inValue); + if (pkey) + { + EVP_PKEY_free(pkey); + pkey = NULL; + } +} + +// Retrieve the OpenSSL representation of the key +EVP_PKEY* OSSLMLDSAPublicKey::getOSSLKey() +{ + if (pkey == NULL) createOSSLKey(); + + return pkey; +} + +// Create the OpenSSL representation of the key +void OSSLMLDSAPublicKey::createOSSLKey() +{ + if (pkey != NULL) return; + + ByteString localValue = getValue(); + + const char* name = OSSL::mldsaParameterSet2Name(getParameterSet()); + + int selection = 0; + EVP_PKEY_CTX *ctx = NULL; + OSSL_PARAM params[3], *p = params; + + *p++ = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PUB_KEY, + localValue.byte_str(), localValue.size()); + selection = OSSL_KEYMGMT_SELECT_PUBLIC_KEY; + + *p = OSSL_PARAM_construct_end(); + + ctx = EVP_PKEY_CTX_new_from_name(NULL, name, NULL); + if (ctx == NULL) { + ERROR_MSG("Could not create context"); + return; + } + int rv = EVP_PKEY_fromdata_init(ctx); + if (!rv) { + ERROR_MSG("Could not EVP_PKEY_fromdata_init:%d", rv); + EVP_PKEY_CTX_free(ctx); + return; + } + rv = EVP_PKEY_fromdata(ctx, &pkey, selection, params); + if (!rv) { + ERROR_MSG("Could not EVP_PKEY_fromdata:%d", rv); + EVP_PKEY_CTX_free(ctx); + return; + } + + EVP_PKEY_CTX_free(ctx); + +} +#endif diff --git a/src/lib/crypto/OSSLMLDSAPublicKey.h b/src/lib/crypto/OSSLMLDSAPublicKey.h new file mode 100644 index 000000000..171563a94 --- /dev/null +++ b/src/lib/crypto/OSSLMLDSAPublicKey.h @@ -0,0 +1,59 @@ +/***************************************************************************** + OSSLMLDSAPublicKey.h + + OpenSSL ML-DSA public key class + *****************************************************************************/ + +#ifndef _SOFTHSM_V2_OSSLMLDSAPUBLICKEY_H +#define _SOFTHSM_V2_OSSLMLDSAPUBLICKEY_H + +#include "config.h" +#ifdef WITH_ML_DSA +#include "MLDSAParameters.h" +#include "MLDSAPublicKey.h" +#include + +class OSSLMLDSAPublicKey : public MLDSAPublicKey +{ +public: + // Constructors + OSSLMLDSAPublicKey(); + + OSSLMLDSAPublicKey(const EVP_PKEY* inMLDSAKEY); + + // Destructor + virtual ~OSSLMLDSAPublicKey(); + + // Non-copyable (raw ownership of EVP_PKEY) + OSSLMLDSAPublicKey(const OSSLMLDSAPublicKey&) = delete; + OSSLMLDSAPublicKey& operator=(const OSSLMLDSAPublicKey&) = delete; + + // Movable + OSSLMLDSAPublicKey(OSSLMLDSAPublicKey&&) noexcept; + OSSLMLDSAPublicKey& operator=(OSSLMLDSAPublicKey&&) noexcept; + + // The type + static const char* type; + + // Check if the key is of the given type + virtual bool isOfType(const char* inType); + + virtual void setValue(const ByteString& value); + + // Set from OpenSSL representation + virtual void setFromOSSL(const EVP_PKEY* inMLDSAKEY); + + // Retrieve the OpenSSL representation of the key + EVP_PKEY* getOSSLKey(); + +private: + // The internal OpenSSL representation + EVP_PKEY* pkey; + + // Create the OpenSSL representation of the key + void createOSSLKey(); +}; + +#endif // WITH_ML_DSA +#endif // !_SOFTHSM_V2_OSSLMLDSAPUBLICKEY_H + diff --git a/src/lib/crypto/OSSLUtil.cpp b/src/lib/crypto/OSSLUtil.cpp index 794f80e3e..70c154674 100644 --- a/src/lib/crypto/OSSLUtil.cpp +++ b/src/lib/crypto/OSSLUtil.cpp @@ -34,10 +34,22 @@ #include "log.h" #include "DerUtil.h" #include "OSSLUtil.h" +#ifdef WITH_ML_DSA +#include "MLDSAParameters.h" +#include +#endif #include #include #include +#ifdef WITH_ML_DSA +static const std::map mldsaAlgNameFromParameterSet { + {MLDSAParameters::ML_DSA_44_PARAMETER_SET, "ML-DSA-44"}, + {MLDSAParameters::ML_DSA_65_PARAMETER_SET, "ML-DSA-65"}, + {MLDSAParameters::ML_DSA_87_PARAMETER_SET, "ML-DSA-87"} +}; +#endif + // Convert an OpenSSL BIGNUM to a ByteString ByteString OSSL::bn2ByteString(const BIGNUM* bn) { @@ -209,3 +221,16 @@ int OSSL::byteString2oid(const ByteString& byteString) return NID_undef; } #endif + +#ifdef WITH_ML_DSA +const char* OSSL::mldsaParameterSet2Name(unsigned long parameterSet) { + + std::map::const_iterator it = mldsaAlgNameFromParameterSet.find(parameterSet); + + if (it != mldsaAlgNameFromParameterSet.end()) { + return it->second; + } + + return NULL; +} +#endif \ No newline at end of file diff --git a/src/lib/crypto/OSSLUtil.h b/src/lib/crypto/OSSLUtil.h index 7b86c50e8..5f41e9fde 100644 --- a/src/lib/crypto/OSSLUtil.h +++ b/src/lib/crypto/OSSLUtil.h @@ -72,6 +72,10 @@ namespace OSSL // Convert a ByteString to an OpenSSL NID int byteString2oid(const ByteString& byteString); #endif + +#ifdef WITH_ML_DSA + const char* mldsaParameterSet2Name(unsigned long parameterSet); +#endif } #endif // !_SOFTHSM_V2_OSSLUTIL_H diff --git a/src/lib/crypto/test/CMakeLists.txt b/src/lib/crypto/test/CMakeLists.txt index f952c847b..b77f9bc05 100644 --- a/src/lib/crypto/test/CMakeLists.txt +++ b/src/lib/crypto/test/CMakeLists.txt @@ -22,6 +22,7 @@ set(SOURCES cryptotest.cpp GOSTTests.cpp HashTests.cpp MacTests.cpp + MLDSATests.cpp RNGTests.cpp RSATests.cpp chisq.c diff --git a/src/lib/crypto/test/MLDSATests.cpp b/src/lib/crypto/test/MLDSATests.cpp new file mode 100644 index 000000000..a5d0edded --- /dev/null +++ b/src/lib/crypto/test/MLDSATests.cpp @@ -0,0 +1,520 @@ +/***************************************************************************** + MLDSATests.cpp + + Contains test cases to test the ML-DSA class + *****************************************************************************/ + +#include +#include +#include +#include +#include "MLDSATests.h" +#include "CryptoFactory.h" +#include "RNG.h" +#include "AsymmetricKeyPair.h" +#include "AsymmetricAlgorithm.h" +#ifdef WITH_ML_DSA +#include "MLDSAParameters.h" +#include "MLDSAPublicKey.h" +#include "MLDSAPrivateKey.h" + +CPPUNIT_TEST_SUITE_REGISTRATION(MLDSATests); + +static const std::vector allParameterSets = { + CKP_ML_DSA_44, CKP_ML_DSA_65, CKP_ML_DSA_87}; + +void MLDSATests::setUp() +{ + mldsa = NULL; + + mldsa = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::MLDSA); + + // Check the MLDSA object + CPPUNIT_ASSERT(mldsa != NULL); +} + +void MLDSATests::tearDown() +{ + if (mldsa != NULL) + { + CryptoFactory::i()->recycleAsymmetricAlgorithm(mldsa); + } + + fflush(stdout); +} + +void MLDSATests::testKeyGeneration() +{ + for (const unsigned long parameterSet : allParameterSets) + { + // Set domain parameters + MLDSAParameters *p = new MLDSAParameters(); + p->setParameterSet(parameterSet); + + // Generate key-pair + AsymmetricKeyPair *kp; + CPPUNIT_ASSERT(mldsa->generateKeyPair(&kp, p)); + + MLDSAPublicKey *pub = (MLDSAPublicKey *)kp->getPublicKey(); + MLDSAPrivateKey *priv = (MLDSAPrivateKey *)kp->getPrivateKey(); + + CPPUNIT_ASSERT(pub->getParameterSet() == parameterSet); + CPPUNIT_ASSERT(priv->getParameterSet() == parameterSet); + + mldsa->recycleParameters(p); + mldsa->recycleKeyPair(kp); + } +} + +void MLDSATests::testSerialisation() +{ + for (const unsigned long parameterSet : allParameterSets) + { + // Get domain parameters + MLDSAParameters *p = new MLDSAParameters(); + p->setParameterSet(parameterSet); + + // Serialise the parameters + ByteString serialisedParams = p->serialise(); + + // Deserialise the parameters + AsymmetricParameters *dMLDSA; + + CPPUNIT_ASSERT(mldsa->reconstructParameters(&dMLDSA, serialisedParams)); + + CPPUNIT_ASSERT(dMLDSA->areOfType(MLDSAParameters::type)); + + MLDSAParameters *ddMLDSA = (MLDSAParameters *)dMLDSA; + + CPPUNIT_ASSERT(p->getParameterSet() == ddMLDSA->getParameterSet()); + + // Generate a key-pair + AsymmetricKeyPair *kp; + + CPPUNIT_ASSERT(mldsa->generateKeyPair(&kp, dMLDSA)); + + // Serialise the key-pair + ByteString serialisedKP = kp->serialise(); + + // Deserialise the key-pair + AsymmetricKeyPair *dKP; + + CPPUNIT_ASSERT(mldsa->reconstructKeyPair(&dKP, serialisedKP)); + + // Check the deserialised key-pair + MLDSAPrivateKey *privKey = (MLDSAPrivateKey *)kp->getPrivateKey(); + MLDSAPublicKey *pubKey = (MLDSAPublicKey *)kp->getPublicKey(); + + MLDSAPrivateKey *dPrivKey = (MLDSAPrivateKey *)dKP->getPrivateKey(); + MLDSAPublicKey *dPubKey = (MLDSAPublicKey *)dKP->getPublicKey(); + + CPPUNIT_ASSERT(privKey->getParameterSet() == dPrivKey->getParameterSet()); + CPPUNIT_ASSERT(privKey->getValue() == dPrivKey->getValue()); + CPPUNIT_ASSERT(privKey->getSeed() == dPrivKey->getSeed()); + + CPPUNIT_ASSERT(pubKey->getParameterSet() == dPubKey->getParameterSet()); + CPPUNIT_ASSERT(pubKey->getValue() == dPubKey->getValue()); + + mldsa->recycleParameters(p); + mldsa->recycleParameters(dMLDSA); + mldsa->recycleKeyPair(kp); + mldsa->recycleKeyPair(dKP); + } +} + +void MLDSATests::testPKCS8() +{ + for (const unsigned long parameterSet : allParameterSets) + { + // Get domain parameters + MLDSAParameters *p = new MLDSAParameters(); + p->setParameterSet(parameterSet); + + // Generate a key-pair + AsymmetricKeyPair *kp; + + CPPUNIT_ASSERT(mldsa->generateKeyPair(&kp, p)); + CPPUNIT_ASSERT(kp != NULL); + + MLDSAPrivateKey *priv = (MLDSAPrivateKey *)kp->getPrivateKey(); + CPPUNIT_ASSERT(priv != NULL); + + MLDSAPublicKey *pub = (MLDSAPublicKey *)kp->getPublicKey(); + CPPUNIT_ASSERT(pub != NULL); + + // Encode and decode the private key + ByteString pkcs8 = priv->PKCS8Encode(); + CPPUNIT_ASSERT(pkcs8.size() != 0); + + MLDSAPrivateKey *dPriv = (MLDSAPrivateKey *)mldsa->newPrivateKey(); + CPPUNIT_ASSERT(dPriv != NULL); + + CPPUNIT_ASSERT(dPriv->PKCS8Decode(pkcs8)); + + CPPUNIT_ASSERT(priv->getParameterSet() == dPriv->getParameterSet()); + CPPUNIT_ASSERT(priv->getValue() == dPriv->getValue()); + + mldsa->recycleParameters(p); + mldsa->recyclePrivateKey(dPriv); + mldsa->recycleKeyPair(kp); + } +} + +void MLDSATests::testSigningVerifying() +{ + for (const unsigned long parameterSet : allParameterSets) + { + // Get domain parameters + MLDSAParameters *p = new MLDSAParameters(); + CPPUNIT_ASSERT(p != NULL); + p->setParameterSet(parameterSet); + + // Generate key-pair + AsymmetricKeyPair *kp; + CPPUNIT_ASSERT(mldsa->generateKeyPair(&kp, p)); + + // Generate some data to sign + ByteString dataToSign; + + RNG *rng = CryptoFactory::i()->getRNG(); + CPPUNIT_ASSERT(rng != NULL); + + CPPUNIT_ASSERT(rng->generateRandom(dataToSign, 567)); + + // Sign the data + ByteString sig; + CPPUNIT_ASSERT(mldsa->sign(kp->getPrivateKey(), dataToSign, sig, AsymMech::MLDSA)); + + // And verify it + CPPUNIT_ASSERT(mldsa->verify(kp->getPublicKey(), dataToSign, sig, AsymMech::MLDSA)); + + mldsa->recycleKeyPair(kp); + mldsa->recycleParameters(p); + } +} + +void MLDSATests::testSigningVerifyingHedgePreferred() +{ + + // Get domain parameters + MLDSAParameters *p = new MLDSAParameters(); + CPPUNIT_ASSERT(p != NULL); + p->setParameterSet(CKP_ML_DSA_44); + + // Generate key-pair + AsymmetricKeyPair *kp; + CPPUNIT_ASSERT(mldsa->generateKeyPair(&kp, p)); + + // Generate some data to sign + ByteString dataToSign; + + RNG *rng = CryptoFactory::i()->getRNG(); + CPPUNIT_ASSERT(rng != NULL); + + CPPUNIT_ASSERT(rng->generateRandom(dataToSign, 567)); + + SIGN_ADDITIONAL_CONTEXT context = { + Hedge::Type::HEDGE_PREFERRED + }; + + // Sign the data + ByteString sig; + CPPUNIT_ASSERT(mldsa->sign(kp->getPrivateKey(), dataToSign, sig, AsymMech::MLDSA, &context, sizeof(context))); + + // And verify it + CPPUNIT_ASSERT(mldsa->verify(kp->getPublicKey(), dataToSign, sig, AsymMech::MLDSA, &context, sizeof(context))); + + mldsa->recycleKeyPair(kp); + mldsa->recycleParameters(p); +} + +void MLDSATests::testSigningVerifyingHedgePreferredWithContext() +{ + // Get domain parameters + MLDSAParameters *p = new MLDSAParameters(); + CPPUNIT_ASSERT(p != NULL); + p->setParameterSet(CKP_ML_DSA_44); + + // Generate key-pair + AsymmetricKeyPair *kp; + CPPUNIT_ASSERT(mldsa->generateKeyPair(&kp, p)); + + // Generate some data to sign + ByteString dataToSign; + + RNG *rng = CryptoFactory::i()->getRNG(); + CPPUNIT_ASSERT(rng != NULL); + + CPPUNIT_ASSERT(rng->generateRandom(dataToSign, 567)); + + std::string contextStr = std::string("HEDGE_PREFERRED"); + + SIGN_ADDITIONAL_CONTEXT context = { + Hedge::Type::HEDGE_PREFERRED, + (const unsigned char *)(contextStr.c_str()), + contextStr.size() + }; + + // Sign the data + ByteString sig; + CPPUNIT_ASSERT(mldsa->sign(kp->getPrivateKey(), dataToSign, sig, AsymMech::MLDSA, &context, sizeof(context))); + + // And verify it + CPPUNIT_ASSERT(mldsa->verify(kp->getPublicKey(), dataToSign, sig, AsymMech::MLDSA, &context, sizeof(context))); + + mldsa->recycleKeyPair(kp); + mldsa->recycleParameters(p); +} + +void MLDSATests::testSigningVerifyingHedgePreferredWithContextTooLong() +{ + // Get domain parameters + MLDSAParameters *p = new MLDSAParameters(); + CPPUNIT_ASSERT(p != NULL); + p->setParameterSet(CKP_ML_DSA_44); + + // Generate key-pair + AsymmetricKeyPair *kp; + CPPUNIT_ASSERT(mldsa->generateKeyPair(&kp, p)); + + // Generate some data to sign + ByteString dataToSign; + + RNG *rng = CryptoFactory::i()->getRNG(); + CPPUNIT_ASSERT(rng != NULL); + + CPPUNIT_ASSERT(rng->generateRandom(dataToSign, 567)); + + std::string contextStr = std::string("HEDGE_PREFERREDHEDGE_PREFERREDHEDGE_PREFERREDHEDGE_PREFERREDHEDGE_PREFERREDHEDGE_PREFERREDHEDGE_PREFERREDHEDGE_PREFERREDHEDGE_PREFERREDHEDGE_PREFERREDHEDGE_PREFERREDHEDGE_PREFERREDHEDGE_PREFERREDHEDGE_PREFERREDHEDGE_PREFERREDHEDGE_PREFERREDHEDGE_PREFERREDHEDGE_PREFERRED"); + + SIGN_ADDITIONAL_CONTEXT context = { + Hedge::Type::HEDGE_PREFERRED, + (const unsigned char *)(contextStr.c_str()), + contextStr.size() + }; + + // Sign the data + ByteString sig; + CPPUNIT_ASSERT_EQUAL(false, mldsa->sign(kp->getPrivateKey(), dataToSign, sig, AsymMech::MLDSA, &context, sizeof(context))); + + mldsa->recycleKeyPair(kp); + mldsa->recycleParameters(p); +} + +void MLDSATests::testSigningVerifyingHedgeRequired() +{ + // Get domain parameters + MLDSAParameters *p = new MLDSAParameters(); + CPPUNIT_ASSERT(p != NULL); + p->setParameterSet(CKP_ML_DSA_44); + + // Generate key-pair + AsymmetricKeyPair *kp; + CPPUNIT_ASSERT(mldsa->generateKeyPair(&kp, p)); + + // Generate some data to sign + ByteString dataToSign; + + RNG *rng = CryptoFactory::i()->getRNG(); + CPPUNIT_ASSERT(rng != NULL); + + CPPUNIT_ASSERT(rng->generateRandom(dataToSign, 567)); + + SIGN_ADDITIONAL_CONTEXT context = { + Hedge::Type::HEDGE_REQUIRED + }; + + // Sign the data + ByteString sig; + CPPUNIT_ASSERT(mldsa->sign(kp->getPrivateKey(), dataToSign, sig, AsymMech::MLDSA, &context, sizeof(context))); + + // And verify it + CPPUNIT_ASSERT(mldsa->verify(kp->getPublicKey(), dataToSign, sig, AsymMech::MLDSA, &context, sizeof(context))); + + mldsa->recycleKeyPair(kp); + mldsa->recycleParameters(p); +} + +void MLDSATests::testSigningVerifyingHedgeRequiredWithContext() +{ + // Get domain parameters + MLDSAParameters *p = new MLDSAParameters(); + CPPUNIT_ASSERT(p != NULL); + p->setParameterSet(CKP_ML_DSA_44); + + // Generate key-pair + AsymmetricKeyPair *kp; + CPPUNIT_ASSERT(mldsa->generateKeyPair(&kp, p)); + + // Generate some data to sign + ByteString dataToSign; + + RNG *rng = CryptoFactory::i()->getRNG(); + CPPUNIT_ASSERT(rng != NULL); + + CPPUNIT_ASSERT(rng->generateRandom(dataToSign, 567)); + + std::string contextStr = std::string("HEDGE_REQUIRED"); + + SIGN_ADDITIONAL_CONTEXT context = { + Hedge::Type::HEDGE_REQUIRED, + (const unsigned char *)(contextStr.c_str()), + contextStr.size() + }; + + // Sign the data + ByteString sig; + CPPUNIT_ASSERT(mldsa->sign(kp->getPrivateKey(), dataToSign, sig, AsymMech::MLDSA, &context, sizeof(context))); + + // And verify it + CPPUNIT_ASSERT(mldsa->verify(kp->getPublicKey(), dataToSign, sig, AsymMech::MLDSA, &context, sizeof(context))); + + mldsa->recycleKeyPair(kp); + mldsa->recycleParameters(p); +} + +void MLDSATests::testSigningVerifyingHedgeRequiredWithContextTooLong() +{ + // Get domain parameters + MLDSAParameters *p = new MLDSAParameters(); + CPPUNIT_ASSERT(p != NULL); + p->setParameterSet(CKP_ML_DSA_44); + + // Generate key-pair + AsymmetricKeyPair *kp; + CPPUNIT_ASSERT(mldsa->generateKeyPair(&kp, p)); + + // Generate some data to sign + ByteString dataToSign; + + RNG *rng = CryptoFactory::i()->getRNG(); + CPPUNIT_ASSERT(rng != NULL); + + CPPUNIT_ASSERT(rng->generateRandom(dataToSign, 567)); + + std::string contextStr = std::string("HEDGE_REQUIREDHEDGE_REQUIREDHEDGE_REQUIREDHEDGE_REQUIREDHEDGE_REQUIREDHEDGE_REQUIREDHEDGE_REQUIREDHEDGE_REQUIREDHEDGE_REQUIREDHEDGE_REQUIREDHEDGE_REQUIREDHEDGE_REQUIREDHEDGE_REQUIREDHEDGE_REQUIREDHEDGE_REQUIREDHEDGE_REQUIREDHEDGE_REQUIREDHEDGE_REQUIREDHEDGE_REQUIRED"); + + SIGN_ADDITIONAL_CONTEXT context = { + Hedge::Type::HEDGE_REQUIRED, + (const unsigned char *)(contextStr.c_str()), + contextStr.size() + }; + + // Sign the data + ByteString sig; + CPPUNIT_ASSERT(!mldsa->sign(kp->getPrivateKey(), dataToSign, sig, AsymMech::MLDSA, &context, sizeof(context))); + + mldsa->recycleKeyPair(kp); + mldsa->recycleParameters(p); +} + +void MLDSATests::testSigningVerifyingDeterministic() +{ + // Get domain parameters + MLDSAParameters *p = new MLDSAParameters(); + CPPUNIT_ASSERT(p != NULL); + p->setParameterSet(CKP_ML_DSA_44); + + // Generate key-pair + AsymmetricKeyPair *kp; + CPPUNIT_ASSERT(mldsa->generateKeyPair(&kp, p)); + + // Generate some data to sign + ByteString dataToSign; + + RNG *rng = CryptoFactory::i()->getRNG(); + CPPUNIT_ASSERT(rng != NULL); + + CPPUNIT_ASSERT(rng->generateRandom(dataToSign, 567)); + + SIGN_ADDITIONAL_CONTEXT context = { + Hedge::Type::DETERMINISTIC_REQUIRED, + NULL, + 0 + }; + + // Sign the data + ByteString sig; + CPPUNIT_ASSERT(mldsa->sign(kp->getPrivateKey(), dataToSign, sig, AsymMech::MLDSA, &context, sizeof(context))); + + // And verify it + CPPUNIT_ASSERT(mldsa->verify(kp->getPublicKey(), dataToSign, sig, AsymMech::MLDSA, &context, sizeof(context))); + + mldsa->recycleKeyPair(kp); + mldsa->recycleParameters(p); +} + +void MLDSATests::testSigningVerifyingDeterministicWithContext() +{ + // Get domain parameters + MLDSAParameters *p = new MLDSAParameters(); + CPPUNIT_ASSERT(p != NULL); + p->setParameterSet(CKP_ML_DSA_44); + + // Generate key-pair + AsymmetricKeyPair *kp; + CPPUNIT_ASSERT(mldsa->generateKeyPair(&kp, p)); + + // Generate some data to sign + ByteString dataToSign; + + RNG *rng = CryptoFactory::i()->getRNG(); + CPPUNIT_ASSERT(rng != NULL); + + CPPUNIT_ASSERT(rng->generateRandom(dataToSign, 567)); + + std::string contextStr = std::string("DETERMINISTIC_REQUIRED"); + + SIGN_ADDITIONAL_CONTEXT context = { + Hedge::Type::DETERMINISTIC_REQUIRED, + (const unsigned char *)(contextStr.c_str()), + contextStr.size() + }; + + // Sign the data + ByteString sig; + CPPUNIT_ASSERT(mldsa->sign(kp->getPrivateKey(), dataToSign, sig, AsymMech::MLDSA, &context, sizeof(context))); + + // And verify it + CPPUNIT_ASSERT(mldsa->verify(kp->getPublicKey(), dataToSign, sig, AsymMech::MLDSA, &context, sizeof(context))); + + mldsa->recycleKeyPair(kp); + mldsa->recycleParameters(p); +} + +void MLDSATests::testSigningVerifyingDeterministicWithContextTooLong() +{ + // Get domain parameters + MLDSAParameters *p = new MLDSAParameters(); + CPPUNIT_ASSERT(p != NULL); + p->setParameterSet(CKP_ML_DSA_44); + + // Generate key-pair + AsymmetricKeyPair *kp; + CPPUNIT_ASSERT(mldsa->generateKeyPair(&kp, p)); + + // Generate some data to sign + ByteString dataToSign; + + RNG *rng = CryptoFactory::i()->getRNG(); + CPPUNIT_ASSERT(rng != NULL); + + CPPUNIT_ASSERT(rng->generateRandom(dataToSign, 567)); + + std::string contextStr = std::string("DETERMINISTIC_REQUIREDDETERMINISTIC_REQUIREDDETERMINISTIC_REQUIREDDETERMINISTIC_REQUIREDDETERMINISTIC_REQUIREDDETERMINISTIC_REQUIREDDETERMINISTIC_REQUIREDDETERMINISTIC_REQUIREDDETERMINISTIC_REQUIREDDETERMINISTIC_REQUIREDDETERMINISTIC_REQUIREDDETERMINISTIC_REQUIRED"); + + SIGN_ADDITIONAL_CONTEXT context = { + Hedge::Type::DETERMINISTIC_REQUIRED, + (const unsigned char *)(contextStr.c_str()), + contextStr.size() + }; + + // Sign the data + ByteString sig; + CPPUNIT_ASSERT(!mldsa->sign(kp->getPrivateKey(), dataToSign, sig, AsymMech::MLDSA, &context, sizeof(context))); + + mldsa->recycleKeyPair(kp); + mldsa->recycleParameters(p); +} + +#endif diff --git a/src/lib/crypto/test/MLDSATests.h b/src/lib/crypto/test/MLDSATests.h new file mode 100644 index 000000000..c02a89a80 --- /dev/null +++ b/src/lib/crypto/test/MLDSATests.h @@ -0,0 +1,55 @@ +/***************************************************************************** + MLDSATests.h + + Contains test cases to test the MLDSA class + *****************************************************************************/ + +#ifndef _SOFTHSM_V2_MLDSATESTS_H +#define _SOFTHSM_V2_MLDSATESTS_H + +#include +#include "AsymmetricAlgorithm.h" + +class MLDSATests : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE(MLDSATests); + CPPUNIT_TEST(testKeyGeneration); + CPPUNIT_TEST(testSerialisation); + CPPUNIT_TEST(testPKCS8); + CPPUNIT_TEST(testSigningVerifying); + CPPUNIT_TEST(testSigningVerifyingHedgePreferred); + CPPUNIT_TEST(testSigningVerifyingHedgePreferredWithContext); + CPPUNIT_TEST(testSigningVerifyingHedgePreferredWithContextTooLong); + CPPUNIT_TEST(testSigningVerifyingHedgeRequired); + CPPUNIT_TEST(testSigningVerifyingHedgeRequiredWithContext); + CPPUNIT_TEST(testSigningVerifyingHedgeRequiredWithContextTooLong); + CPPUNIT_TEST(testSigningVerifyingDeterministic); + CPPUNIT_TEST(testSigningVerifyingDeterministicWithContext); + CPPUNIT_TEST(testSigningVerifyingDeterministicWithContextTooLong); + CPPUNIT_TEST_SUITE_END(); + +public: + void testKeyGeneration(); + void testSerialisation(); + void testPKCS8(); + void testSigningVerifying(); + void testSigningVerifyingHedgePreferred(); + void testSigningVerifyingHedgePreferredWithContext(); + void testSigningVerifyingHedgePreferredWithContextTooLong(); + void testSigningVerifyingHedgeRequired(); + void testSigningVerifyingHedgeRequiredWithContext(); + void testSigningVerifyingHedgeRequiredWithContextTooLong(); + void testSigningVerifyingDeterministic(); + void testSigningVerifyingDeterministicWithContext(); + void testSigningVerifyingDeterministicWithContextTooLong(); + + void setUp(); + void tearDown(); + +private: + // MLDSA instance + AsymmetricAlgorithm* mldsa; +}; + +#endif // !_SOFTHSM_V2_MLDSATESTS_H + diff --git a/src/lib/crypto/test/Makefile.am b/src/lib/crypto/test/Makefile.am index 4065ef9ce..3833ffa87 100644 --- a/src/lib/crypto/test/Makefile.am +++ b/src/lib/crypto/test/Makefile.am @@ -24,6 +24,7 @@ cryptotest_SOURCES = cryptotest.cpp \ GOSTTests.cpp \ HashTests.cpp \ MacTests.cpp \ + MLDSATests.cpp \ RNGTests.cpp \ RSATests.cpp \ chisq.c \ diff --git a/src/lib/object_store/DBObject.cpp b/src/lib/object_store/DBObject.cpp index a29417343..38edb0f50 100644 --- a/src/lib/object_store/DBObject.cpp +++ b/src/lib/object_store/DBObject.cpp @@ -519,6 +519,9 @@ static AttributeKind attributeKind(CK_ATTRIBUTE_TYPE type) case CKA_OS_SOPIN: return akBinary; case CKA_OS_USERPIN: return akBinary; + case CKA_PARAMETER_SET: return akInteger; + case CKA_SEED: return akBinary; + default: return akUnknown; } } diff --git a/src/lib/session_mgr/Session.cpp b/src/lib/session_mgr/Session.cpp index 5db36fdfd..da15892a4 100644 --- a/src/lib/session_mgr/Session.cpp +++ b/src/lib/session_mgr/Session.cpp @@ -352,6 +352,41 @@ void Session::setParameters(void* inParam, size_t inParamLen) paramLen = 0; } + // For ML-DSA, deep-copy struct + context bytes into one buffer and fix the pointer + // Suggested by CodeRabbit to avoid memory leak + if (mechanism == AsymMech::MLDSA && inParamLen == sizeof(SIGN_ADDITIONAL_CONTEXT)) { + const SIGN_ADDITIONAL_CONTEXT* additionalContext = static_cast(inParam); + const size_t ctxLen = additionalContext->contextLength; + if (ctxLen > 0 && additionalContext->contextAsChar == NULL) { + // Invalid input; leave parameters unset + return; + } + + // total = struct + optional context bytes + const size_t total = sizeof(SIGN_ADDITIONAL_CONTEXT) + ctxLen; + unsigned char* buf = static_cast(malloc(total)); + if (!buf) { + // Out of memory; leave parameters unset + return; + } + + // Copy the struct first + memcpy(buf, additionalContext, sizeof(SIGN_ADDITIONAL_CONTEXT)); + auto* stored = reinterpret_cast(buf); + + // Copy context bytes (if present) and repoint inside the stored struct + if (ctxLen) { + unsigned char* payload = buf + sizeof(SIGN_ADDITIONAL_CONTEXT); + memcpy(payload, additionalContext->contextAsChar, ctxLen); + stored->contextAsChar = payload; + } else { + stored->contextAsChar = NULL; + } + param = buf; + paramLen = sizeof(SIGN_ADDITIONAL_CONTEXT); + return; + } + param = malloc(inParamLen); if (param != NULL) { diff --git a/src/lib/test/SignVerifyTests.cpp b/src/lib/test/SignVerifyTests.cpp index ac9d46543..eaa76149a 100644 --- a/src/lib/test/SignVerifyTests.cpp +++ b/src/lib/test/SignVerifyTests.cpp @@ -228,12 +228,55 @@ CK_RV SignVerifyTests::generateED(const char* curve, CK_SESSION_HANDLE hSession, } #endif +#ifdef WITH_ML_DSA +CK_RV SignVerifyTests::generateMLDSA(CK_ULONG parameterSet, CK_SESSION_HANDLE hSession, CK_BBOOL bTokenPuk, CK_BBOOL bPrivatePuk, CK_BBOOL bTokenPrk, CK_BBOOL bPrivatePrk, CK_OBJECT_HANDLE &hPuk, CK_OBJECT_HANDLE &hPrk) +{ + CK_MECHANISM mechanism = { CKM_ML_DSA_KEY_PAIR_GEN, NULL_PTR, 0 }; + CK_KEY_TYPE keyType = CKK_ML_DSA; + CK_BYTE label[] = { 0x12, 0x34 }; // dummy + CK_BYTE id[] = { 123 } ; // dummy + CK_BBOOL bFalse = CK_FALSE; + CK_BBOOL bTrue = CK_TRUE; + + CK_ATTRIBUTE pukAttribs[] = { + { CKA_PARAMETER_SET, ¶meterSet, sizeof(parameterSet) }, + { CKA_LABEL, &label[0], sizeof(label) }, + { CKA_ID, &id[0], sizeof(id) }, + { CKA_KEY_TYPE, &keyType, sizeof(keyType) }, + { CKA_VERIFY, &bTrue, sizeof(bTrue) }, + { CKA_ENCRYPT, &bFalse, sizeof(bFalse) }, + { CKA_WRAP, &bFalse, sizeof(bFalse) }, + { CKA_TOKEN, &bTokenPuk, sizeof(bTokenPuk) }, + { CKA_PRIVATE, &bPrivatePuk, sizeof(bPrivatePuk) } + }; + CK_ATTRIBUTE prkAttribs[] = { + { CKA_LABEL, &label[0], sizeof(label) }, + { CKA_ID, &id[0], sizeof(id) }, + { CKA_KEY_TYPE, &keyType, sizeof(keyType) }, + { CKA_SIGN, &bTrue, sizeof(bTrue) }, + { CKA_DECRYPT, &bFalse, sizeof(bFalse) }, + { CKA_UNWRAP, &bFalse, sizeof(bFalse) }, + { CKA_SENSITIVE, &bTrue, sizeof(bTrue) }, + { CKA_TOKEN, &bTokenPrk, sizeof(bTokenPrk) }, + { CKA_PRIVATE, &bPrivatePrk, sizeof(bPrivatePrk) }, + { CKA_EXTRACTABLE, &bFalse, sizeof(bFalse) } + }; + + hPuk = CK_INVALID_HANDLE; + hPrk = CK_INVALID_HANDLE; + return CRYPTOKI_F_PTR( C_GenerateKeyPair(hSession, &mechanism, + pukAttribs, sizeof(pukAttribs)/sizeof(CK_ATTRIBUTE), + prkAttribs, sizeof(prkAttribs)/sizeof(CK_ATTRIBUTE), + &hPuk, &hPrk) ); +} +#endif + void SignVerifyTests::signVerifySingle(CK_MECHANISM_TYPE mechanismType, CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hPublicKey, CK_OBJECT_HANDLE hPrivateKey, CK_VOID_PTR param /* = NULL_PTR */, CK_ULONG paramLen /* = 0 */) { CK_RV rv; CK_MECHANISM mechanism = { mechanismType, param, paramLen }; CK_BYTE data[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,0x0C, 0x0D, 0x0F }; - CK_BYTE signature[256]; + CK_BYTE signature[64 * 1024]; CK_ULONG ulSignatureLen = 0; rv = CRYPTOKI_F_PTR( C_SignInit(hSession,&mechanism,hPrivateKey) ); @@ -803,6 +846,97 @@ void SignVerifyTests::testEdSignVerify(const char* curve) } #endif +#ifdef WITH_ML_DSA +void SignVerifyTests::testMLDSASignVerify(CK_ULONG parameterSet) +{ + CK_RV rv; + CK_SESSION_HANDLE hSessionRO; + CK_SESSION_HANDLE hSessionRW; + + // Just make sure that we finalize any previous tests + CRYPTOKI_F_PTR( C_Finalize(NULL_PTR) ); + + // Open read-only session on when the token is not initialized should fail + rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, &hSessionRO) ); + CPPUNIT_ASSERT(rv == CKR_CRYPTOKI_NOT_INITIALIZED); + + // Initialize the library and start the test. + rv = CRYPTOKI_F_PTR( C_Initialize(NULL_PTR) ); + CPPUNIT_ASSERT(rv == CKR_OK); + + // Open read-only session + rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, &hSessionRO) ); + CPPUNIT_ASSERT(rv == CKR_OK); + + // Open read-write session + rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL_PTR, NULL_PTR, &hSessionRW) ); + CPPUNIT_ASSERT(rv == CKR_OK); + + // Login USER into the sessions so we can create a private objects + rv = CRYPTOKI_F_PTR( C_Login(hSessionRO,CKU_USER,m_userPin1,m_userPin1Length) ); + CPPUNIT_ASSERT(rv==CKR_OK); + + CK_OBJECT_HANDLE hPuk = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hPrk = CK_INVALID_HANDLE; + + CK_BYTE data[] = "context-context-context"; + CK_ULONG dataSize = (CK_ULONG)(sizeof(data) - 1); // exclude trailing NUL + + CK_SIGN_ADDITIONAL_CONTEXT params[] = { + { CKH_HEDGE_PREFERRED, NULL, 0 }, + { CKH_HEDGE_PREFERRED, data, dataSize }, + { CKH_HEDGE_REQUIRED, NULL, 0 }, + { CKH_HEDGE_REQUIRED, data, dataSize }, + { CKH_DETERMINISTIC_REQUIRED, NULL, 0 }, + { CKH_DETERMINISTIC_REQUIRED, data, dataSize }, + }; + + // Public Session keys + rv = generateMLDSA(parameterSet,hSessionRW,IN_SESSION,IS_PUBLIC,IN_SESSION,IS_PUBLIC,hPuk,hPrk); + CPPUNIT_ASSERT(rv == CKR_OK); + signVerifySingle(CKM_ML_DSA, hSessionRO, hPuk,hPrk); + signVerifySingle(CKM_ML_DSA, hSessionRO, hPuk,hPrk, ¶ms[0], sizeof(params[0])); + signVerifySingle(CKM_ML_DSA, hSessionRO, hPuk,hPrk, ¶ms[1], sizeof(params[1])); + signVerifySingle(CKM_ML_DSA, hSessionRO, hPuk,hPrk, ¶ms[2], sizeof(params[2])); + signVerifySingle(CKM_ML_DSA, hSessionRO, hPuk,hPrk, ¶ms[3], sizeof(params[3])); + signVerifySingle(CKM_ML_DSA, hSessionRO, hPuk,hPrk, ¶ms[4], sizeof(params[4])); + signVerifySingle(CKM_ML_DSA, hSessionRO, hPuk,hPrk, ¶ms[5], sizeof(params[5])); + + // Private Session Keys + rv = generateMLDSA(parameterSet,hSessionRW,IN_SESSION,IS_PRIVATE,IN_SESSION,IS_PRIVATE,hPuk,hPrk); + CPPUNIT_ASSERT(rv == CKR_OK); + signVerifySingle(CKM_ML_DSA, hSessionRO, hPuk,hPrk); + signVerifySingle(CKM_ML_DSA, hSessionRO, hPuk,hPrk, ¶ms[0], sizeof(params[0])); + signVerifySingle(CKM_ML_DSA, hSessionRO, hPuk,hPrk, ¶ms[1], sizeof(params[1])); + signVerifySingle(CKM_ML_DSA, hSessionRO, hPuk,hPrk, ¶ms[2], sizeof(params[2])); + signVerifySingle(CKM_ML_DSA, hSessionRO, hPuk,hPrk, ¶ms[3], sizeof(params[3])); + signVerifySingle(CKM_ML_DSA, hSessionRO, hPuk,hPrk, ¶ms[4], sizeof(params[4])); + signVerifySingle(CKM_ML_DSA, hSessionRO, hPuk,hPrk, ¶ms[5], sizeof(params[5])); + + // Public Token Keys + rv = generateMLDSA(parameterSet,hSessionRW,ON_TOKEN,IS_PUBLIC,ON_TOKEN,IS_PUBLIC,hPuk,hPrk); + CPPUNIT_ASSERT(rv == CKR_OK); + signVerifySingle(CKM_ML_DSA, hSessionRO, hPuk,hPrk); + signVerifySingle(CKM_ML_DSA, hSessionRO, hPuk,hPrk, ¶ms[0], sizeof(params[0])); + signVerifySingle(CKM_ML_DSA, hSessionRO, hPuk,hPrk, ¶ms[1], sizeof(params[1])); + signVerifySingle(CKM_ML_DSA, hSessionRO, hPuk,hPrk, ¶ms[2], sizeof(params[2])); + signVerifySingle(CKM_ML_DSA, hSessionRO, hPuk,hPrk, ¶ms[3], sizeof(params[3])); + signVerifySingle(CKM_ML_DSA, hSessionRO, hPuk,hPrk, ¶ms[4], sizeof(params[4])); + signVerifySingle(CKM_ML_DSA, hSessionRO, hPuk,hPrk, ¶ms[5], sizeof(params[5])); + + // Private Token Keys + rv = generateMLDSA(parameterSet, hSessionRW,ON_TOKEN,IS_PRIVATE,ON_TOKEN,IS_PRIVATE,hPuk,hPrk); + CPPUNIT_ASSERT(rv == CKR_OK); + signVerifySingle(CKM_ML_DSA, hSessionRO, hPuk,hPrk); + signVerifySingle(CKM_ML_DSA, hSessionRO, hPuk,hPrk, ¶ms[0], sizeof(params[0])); + signVerifySingle(CKM_ML_DSA, hSessionRO, hPuk,hPrk, ¶ms[1], sizeof(params[1])); + signVerifySingle(CKM_ML_DSA, hSessionRO, hPuk,hPrk, ¶ms[2], sizeof(params[2])); + signVerifySingle(CKM_ML_DSA, hSessionRO, hPuk,hPrk, ¶ms[3], sizeof(params[3])); + signVerifySingle(CKM_ML_DSA, hSessionRO, hPuk,hPrk, ¶ms[4], sizeof(params[4])); + signVerifySingle(CKM_ML_DSA, hSessionRO, hPuk,hPrk, ¶ms[5], sizeof(params[5])); +} +#endif + CK_RV SignVerifyTests::generateKey(CK_SESSION_HANDLE hSession, CK_KEY_TYPE keyType, CK_BBOOL bToken, CK_BBOOL bPrivate, CK_OBJECT_HANDLE &hKey) { #ifndef WITH_BOTAN diff --git a/src/lib/test/SignVerifyTests.h b/src/lib/test/SignVerifyTests.h index d7acbf91a..c2b4ada78 100644 --- a/src/lib/test/SignVerifyTests.h +++ b/src/lib/test/SignVerifyTests.h @@ -49,6 +49,9 @@ class SignVerifyTests : public TestsBase CPPUNIT_TEST_PARAMETERIZED(testEdSignVerify, {"Ed25519", "Ed448"}); #endif CPPUNIT_TEST(testMacSignVerify); +#ifdef WITH_ML_DSA + CPPUNIT_TEST_PARAMETERIZED(testMLDSASignVerify, {CKP_ML_DSA_44, CKP_ML_DSA_65, CKP_ML_DSA_87}); +#endif CPPUNIT_TEST_SUITE_END(); public: @@ -60,6 +63,9 @@ class SignVerifyTests : public TestsBase void testEdSignVerify(const char* curve); #endif void testMacSignVerify(); +#ifdef WITH_ML_DSA + void testMLDSASignVerify(CK_ULONG parameterSet); +#endif protected: CK_RV generateRSA(CK_SESSION_HANDLE hSession, CK_BBOOL bTokenPuk, CK_BBOOL bPrivatePuk, CK_BBOOL bTokenPrk, CK_BBOOL bPrivatePrk, CK_OBJECT_HANDLE &hPuk, CK_OBJECT_HANDLE &hPrk); @@ -68,6 +74,9 @@ class SignVerifyTests : public TestsBase #endif #ifdef WITH_EDDSA CK_RV generateED(const char* curve, CK_SESSION_HANDLE hSession, CK_BBOOL bTokenPuk, CK_BBOOL bPrivatePuk, CK_BBOOL bTokenPrk, CK_BBOOL bPrivatePrk, CK_OBJECT_HANDLE &hPuk, CK_OBJECT_HANDLE &hPrk); +#endif +#ifdef WITH_ML_DSA + CK_RV generateMLDSA(CK_ULONG parameterSet, CK_SESSION_HANDLE hSession, CK_BBOOL bTokenPuk, CK_BBOOL bPrivatePuk, CK_BBOOL bTokenPrk, CK_BBOOL bPrivatePrk, CK_OBJECT_HANDLE &hPuk, CK_OBJECT_HANDLE &hPrk); #endif void signVerifySingle(CK_MECHANISM_TYPE mechanismType, CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hPublicKey, CK_OBJECT_HANDLE hPrivateKey, CK_VOID_PTR param = NULL_PTR, CK_ULONG paramLen = 0); void signVerifySingleData(size_t dataSize, CK_MECHANISM_TYPE mechanismType, CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hPublicKey, CK_OBJECT_HANDLE hPrivateKey, CK_VOID_PTR param = NULL_PTR, CK_ULONG paramLen = 0);