diff --git a/.github/workflows/nss-pk12util-test.yml b/.github/workflows/nss-pk12util-test.yml new file mode 100644 index 00000000..ec9c3f93 --- /dev/null +++ b/.github/workflows/nss-pk12util-test.yml @@ -0,0 +1,370 @@ +name: wolfPKCS11 NSS pk12util Test + +on: + push: + branches: [ main, master, nss ] + pull_request: + branches: [ main, master, nss ] + workflow_dispatch: + +env: + NSPR_VERSION: NSPR_4_36_BRANCH + NSS_VERSION: NSS_3_112_RTM + WOLFSSL_VERSION: v5.8.0-stable + NSS_DEBUG_PKCS11_MODULE: wolfPKCS11 + NSPR_LOG_MODULES: all:5 + NSPR_LOG_FILE: /logs/nss.log + NSS_OUTPUT_FILE: /logs/stats.log + NSS_STRICT_NOFORK: 1 + NSS_DEBUG: all + +jobs: + nss-pk12util-test: + runs-on: ubuntu-22.04 + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install system dependencies + run: | + sudo apt-get update + sudo DEBIAN_FRONTEND=noninteractive apt-get install -y \ + mercurial \ + python3 \ + python-is-python3 \ + python3-pip \ + gyp \ + ninja-build \ + build-essential \ + automake \ + libtool \ + git \ + pkg-config \ + poppler-utils \ + wget \ + enscript \ + ghostscript \ + gdb \ + vim \ + hexedit \ + openssl \ + ca-certificates + + - name: Cache NSPR + id: cache-nspr + uses: actions/cache@v4 + with: + path: /tmp/src/nspr + key: nspr-${{ env.NSPR_VERSION }} + + - name: Clone and build NSPR + if: steps.cache-nspr.outputs.cache-hit != 'true' + run: | + mkdir -p /tmp/src + cd /tmp/src + hg clone https://hg.mozilla.org/projects/nspr -r ${{ env.NSPR_VERSION }} + + - name: Cache NSS source and patches + id: cache-nss-source + uses: actions/cache@v4 + with: + path: | + /tmp/src/nss + /tmp/src/osp + key: nss-source-${{ env.NSS_VERSION }}-latest + + - name: Cache NSS build artifacts + id: cache-nss-build + uses: actions/cache@v4 + with: + path: /tmp/src/dist + key: nss-build-${{ env.NSS_VERSION }}-latest + + - name: Clone NSS and apply wolfSSL patches + if: steps.cache-nss-source.outputs.cache-hit != 'true' + run: | + mkdir -p /tmp/src + cd /tmp/src + + # Clone official Mozilla NSS with specific tag + hg clone https://hg.mozilla.org/projects/nss -r ${{ env.NSS_VERSION }} + + # Clone wolfSSL OSP repository for patches + git clone https://github.com/wolfSSL/osp.git + + cd nss + + # Apply patches from wolfSSL/osp/nss directory + echo "Applying wolfSSL NSS patches..." + if [ -d "../osp/nss" ]; then + for patch in ../osp/nss/*.patch; do + if [ -f "$patch" ]; then + echo "Applying patch: $(basename $patch)" + patch -p1 < "$patch" || { + echo "Warning: Patch $(basename $patch) failed to apply cleanly" + echo "Attempting to apply with --reject-file option..." + patch -p1 --reject-file=/tmp/$(basename $patch).rej < "$patch" || true + } + fi + done + else + echo "No patches found in wolfSSL/osp/nss directory" + fi + + - name: Build NSS + if: steps.cache-nss-build.outputs.cache-hit != 'true' + run: | + cd /tmp/src/nss + + # Set NSS build environment + export USE_64=1 + export NSS_ENABLE_WERROR=0 + export BUILD_OPT=0 + + # Build NSS with debug mode enabled + ./build.sh -v + + - name: Copy NSS headers and libraries + run: | + # Create directories for headers + sudo mkdir -p /usr/local/include/nss + sudo mkdir -p /usr/local/include/nspr + sudo mkdir -p /usr/local/lib + + # Copy NSS headers from dist directory + sudo cp -r /tmp/src/dist/public/nss/* /usr/local/include/nss/ + + # Copy NSS library and headers + sudo cp -r /tmp/src/dist/Debug/* /usr/local/ + + # Copy NSS and NSPR libraries + sudo find /tmp/src/nspr/Debug -name "*.so" -exec cp {} /usr/local/lib/ \; + + # Update library cache + sudo ldconfig + + - name: Cache wolfSSL + id: cache-wolfssl + uses: actions/cache@v4 + with: + path: /tmp/wolfssl + key: wolfssl-${{ env.WOLFSSL_VERSION }} + + - name: Clone and build wolfSSL + if: steps.cache-wolfssl.outputs.cache-hit != 'true' + run: | + cd /tmp + git clone https://github.com/wolfSSL/wolfssl.git --branch ${{ env.WOLFSSL_VERSION }} --depth 1 + cd wolfssl + ./autogen.sh + ./configure --enable-aescfb --enable-cryptocb --enable-rsapss --enable-keygen --enable-pwdbased --enable-scrypt --enable-cmac --enable-aesctr --enable-aesccm --enable-md5 C_EXTRA_FLAGS="-DWOLFSSL_PUBLIC_MP -DWC_RSA_DIRECT -DHAVE_AES_ECB -D_GNU_SOURCE" + make + + - name: Install wolfSSL + run: | + cd /tmp/wolfssl + sudo make install + sudo ldconfig + + - name: Build wolfPKCS11 + run: | + ./autogen.sh + ./configure --enable-debug --enable-nss --enable-aesecb --enable-aesctr --enable-aesccm --enable-aescmac CFLAGS="-D_GNU_SOURCE" + make + sudo make install + sudo ldconfig + + - name: Configure NSS database + run: | + sudo mkdir -p /nss-test/nssdb + sudo chmod -R 777 /nss-test + sudo mkdir -p /logs + + # Configure NSS to use wolfPKCS11 + cat > /nss-test/pkcs11.txt << 'EOF' + library=/usr/local/lib/libwolfpkcs11.so + name=wolfPKCS11 + NSS=Flags=internal,critical,fips cipherOrder=100 slotParams={0x00000001=[slotFlags=ECC,RSA,DSA,DH,RC2,RC4,DES,RANDOM,SHA1,MD5,MD2,SSL,TLS,AES,Camellia,SEED,SHA256,SHA512] } + EOF + + # Initialize NSS database + certutil -N -d /nss-test/nssdb/ --empty-password + + - name: Run NSS pk12util tests + run: | + cd /nss-test + set -e + + echo "=== NSS pk12util Test Script ===" + echo "NSS Database location: /nss-test/nssdb" + echo + + # Create test data + echo "1. Creating test data file:" + echo "This is test data for CMS signing and encryption" > test-data.txt + cat test-data.txt + echo + + # Generate a test certificate and key + echo "2. Generating CA and user certificates:" + + # Step 1: Create a CA certificate + echo " Creating CA certificate..." + cat > ca-openssl.conf << 'CAEOF' + [req] + distinguished_name = req_distinguished_name + req_extensions = v3_ca + prompt = no + + [req_distinguished_name] + CN = Test CA + O = NSS Test CA + C = US + + [v3_ca] + keyUsage = critical, keyCertSign, cRLSign + basicConstraints = critical, CA:true + subjectKeyIdentifier = hash + authorityKeyIdentifier = keyid:always,issuer + CAEOF + + # Create CA certificate and key + openssl req -x509 -newkey rsa:2048 -keyout ca-key.pem -out ca-cert.pem -days 365 -nodes \ + -config ca-openssl.conf -extensions v3_ca + + # Import CA certificate into NSS database + certutil -A -n "TestCA" -i ca-cert.pem -t "CT,C,C" -d /nss-test/nssdb + + # Step 2: Create user certificate signed by CA + echo " Creating user certificate signed by CA..." + cat > user-openssl.conf << 'USEREOF' + [req] + distinguished_name = req_distinguished_name + prompt = no + + [req_distinguished_name] + CN = Test User + O = NSS Test + C = US + emailAddress = test@example.com + + [v3_user] + keyUsage = critical, digitalSignature, keyEncipherment + extendedKeyUsage = critical, emailProtection + basicConstraints = critical, CA:false + subjectKeyIdentifier = hash + subjectAltName = email:test@example.com + USEREOF + + # Create user certificate request (without authority key identifier) + openssl req -new -newkey rsa:2048 -keyout user-key.pem -out user-req.pem -nodes \ + -config user-openssl.conf + + # Create signing config with authority key identifier + cat > signing.conf << 'SIGNEOF' + [v3_user_sign] + keyUsage = critical, digitalSignature, keyEncipherment + extendedKeyUsage = critical, emailProtection + basicConstraints = critical, CA:false + subjectKeyIdentifier = hash + authorityKeyIdentifier = keyid:always,issuer:always + subjectAltName = email:test@example.com + SIGNEOF + + # Sign user certificate with CA + openssl x509 -req -in user-req.pem -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial \ + -out user-cert.pem -days 365 -extensions v3_user_sign -extfile signing.conf + + # Convert user certificate to PKCS#12 format + openssl pkcs12 -export -in user-cert.pem -inkey user-key.pem -out user-cert.p12 \ + -name "testcert" -passout pass: + + # Import user certificate into NSS database + echo " Importing user certificate into NSS database..." + pk12util -i user-cert.p12 -d /nss-test/nssdb -W "" + + # Set proper trust attributes + certutil -M -n "testcert" -t "u,u,u" -d /nss-test/nssdb + + echo " ✓ CA and user certificates created successfully" + + echo "3. Listing certificates in NSS database:" + certutil -L -d /nss-test/nssdb + echo + echo "Private keys in NSS database:" + certutil -K -d /nss-test/nssdb + echo + echo "Certificate details:" + if certutil -L -n "testcert" -d /nss-test/nssdb >/dev/null 2>&1; then + echo "User certificate 'testcert':" + certutil -L -n "testcert" -d /nss-test/nssdb + echo + fi + if certutil -L -n "TestCA" -d /nss-test/nssdb >/dev/null 2>&1; then + echo "CA certificate 'TestCA':" + certutil -L -n "TestCA" -d /nss-test/nssdb + fi + + echo "4. Testing CMS operations with cmsutil:" + + # Test CMS signing with additional options to handle trust + echo " a) Signing data with CMS:" + cmsutil -S -N "testcert" -i test-data.txt -o signed-data.p7s -d /nss-test/nssdb -p "" -G + + if [ -f signed-data.p7s ]; then + echo " ✓ CMS signing successful - created signed-data.p7s" + ls -la signed-data.p7s + else + echo " ✗ CMS signing failed" + fi + + # Test CMS verification + echo " b) Verifying CMS signature:" + openssl smime -verify -in signed-data.p7s -CAfile test-cert.pem -inform DER -noverify 2>/dev/null && echo " ✓ OpenSSL verification successful" + + # Test CMS encryption (envelope) + echo " c) Creating CMS encrypted envelope:" + cmsutil -E -r "testcert" -i test-data.txt -o encrypted-data.p7e -d /nss-test/nssdb + if [ -f encrypted-data.p7e ]; then + echo " ✓ CMS encryption successful - created encrypted-data.p7e" + ls -la encrypted-data.p7e + else + echo " ✗ CMS encryption failed" + fi + + # Test CMS decryption + echo " d) Decrypting CMS envelope:" + cmsutil -D -i encrypted-data.p7e -o decrypted-data.txt -d /nss-test/nssdb -p "" + if [ -f decrypted-data.txt ]; then + echo " ✓ CMS decryption successful" + echo " Original data:" + cat test-data.txt + echo " Decrypted data:" + cat decrypted-data.txt + echo " Data match:" $(cmp -s test-data.txt decrypted-data.txt && echo "YES" || echo "NO") + else + echo " ✗ CMS decryption failed" + fi + + echo + echo "=== pk12util Test Complete ===" + echo "Files created:" + ls -la *.p7s *.p7e *.txt *.pem *.p12 2>/dev/null || echo "No files found" + + # Create tar archive with all test artifacts + sudo mkdir -p /tmp/artifacts + sudo cp -r /logs /tmp/artifacts/ 2>/dev/null || true + sudo cp -r /nss-test /tmp/artifacts/ 2>/dev/null || true + sudo tar -czf /tmp/nss-pk12util-test-artifacts.tar.gz -C /tmp/artifacts . 2>/dev/null || true + + # Fix permissions for artifact upload + sudo chown $USER:$USER /tmp/nss-pk12util-test-artifacts.tar.gz 2>/dev/null || true + + - name: Upload test artifacts + if: failure() + uses: actions/upload-artifact@v4 + with: + name: nss-pk12util-test-artifacts + path: /tmp/nss-pk12util-test-artifacts.tar.gz + retention-days: 5 diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 00b87079..f11f56a1 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -86,6 +86,10 @@ jobs: uses: ./.github/workflows/build-workflow.yml with: config: --enable-nss + pbkdf2: + uses: ./.github/workflows/build-workflow.yml + with: + config: --enable-pbkdf2 --with-pbkdf2-iterations=1000 debug: uses: ./.github/workflows/build-workflow.yml diff --git a/configure.ac b/configure.ac index b0c9a1ef..7e9792a9 100644 --- a/configure.ac +++ b/configure.ac @@ -332,6 +332,26 @@ then AM_CFLAGS="$AM_CFLAGS -DWOLFPKCS11_HKDF" fi +AC_ARG_ENABLE([pbkdf2], + [AS_HELP_STRING([--enable-pbkdf2],[Enable internal PBKDF2 for PIN hashing (default: disabled)])], + [ ENABLED_PBKDF2=$enableval ], + [ ENABLED_PBKDF2=no ] + ) +if test "$ENABLED_PBKDF2" = "yes" +then + AM_CFLAGS="$AM_CFLAGS -DWOLFPKCS11_PBKDF2" +fi + +AC_ARG_WITH([pbkdf2-iterations], + [AS_HELP_STRING([--with-pbkdf2-iterations=NUM],[Set PBKDF2 iterations (default: 600000)])], + [ PBKDF2_ITERATIONS=$withval ], + [ PBKDF2_ITERATIONS=600000 ] + ) +if test "$ENABLED_PBKDF2" = "yes" +then + AM_CFLAGS="$AM_CFLAGS -DPBKDF2_ITERATIONS=$PBKDF2_ITERATIONS" +fi + AC_ARG_ENABLE([md5], [AS_HELP_STRING([--enable-md5],[Enable MD5 (default: enabled)])], [ ENABLED_MD5=$enableval ], @@ -449,6 +469,8 @@ then AM_CFLAGS="$AM_CFLAGS -DWOLFPKCS11_NSS" fi +AM_CONDITIONAL([ENABLED_NSS],[test "x$enable_nss" = "xyes"]) + AC_ARG_WITH([default-token-path], [AS_HELP_STRING([--with-default-token-path=PATH],[Set default token storage path (default: none)])], [ WOLFPKCS11_DEFAULT_TOKEN_PATH=$withval ], diff --git a/examples/include.am b/examples/include.am index 34035d9f..572ed8e4 100644 --- a/examples/include.am +++ b/examples/include.am @@ -45,6 +45,12 @@ noinst_PROGRAMS += examples/token_info examples_token_info_SOURCES = examples/token_info.c examples_token_info_LDADD = +if ENABLED_NSS +noinst_PROGRAMS += examples/nss_pkcs12_pbe_example +examples_nss_pkcs12_pbe_example_SOURCES = examples/nss_pkcs12_pbe_example.c +examples_nss_pkcs12_pbe_example_LDADD = +endif + dist_noinst_SCRIPTS += examples/examples.test EXTRA_DIST += examples/rsa-2048.der @@ -60,5 +66,7 @@ examples_mech_info_LDADD += src/libwolfpkcs11.la examples_obj_list_LDADD += src/libwolfpkcs11.la examples_slot_info_LDADD += src/libwolfpkcs11.la examples_token_info_LDADD += src/libwolfpkcs11.la +if ENABLED_NSS +examples_nss_pkcs12_pbe_example_LDADD += src/libwolfpkcs11.la +endif endif - diff --git a/examples/nss_pkcs12_pbe_example.c b/examples/nss_pkcs12_pbe_example.c new file mode 100644 index 00000000..58404726 --- /dev/null +++ b/examples/nss_pkcs12_pbe_example.c @@ -0,0 +1,617 @@ +/* nss_pkcs12_pbe_example.c + * + * Example demonstrating NSS PKCS#12 PBE SHA-256 HMAC key generation + * + * Copyright (C) 2006-2025 wolfSSL Inc. + * + * This file is part of wolfPKCS11. + * + * wolfPKCS11 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPKCS11 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#ifndef WOLFSSL_USER_SETTINGS + #include +#endif +#include +#include + +#ifndef WOLFPKCS11_USER_SETTINGS + #include +#endif +#include + +#ifndef HAVE_PKCS11_STATIC +#include +#endif + +#include +#include +#include + +#ifdef DEBUG_WOLFPKCS11 + #define CHECK_CKR(rv, op) \ + fprintf(stderr, "%s: %ld\n", op, rv) +#else + #define CHECK_CKR(rv, op) \ + if (rv != CKR_OK) \ + fprintf(stderr, "%s: %ld\n", op, rv) +#endif + +/* DLL Location and slot */ +#ifndef WOLFPKCS11_DLL_FILENAME + #ifdef __MACH__ + #define WOLFPKCS11_DLL_FILENAME "./src/.libs/libwolfpkcs11.dylib" + #else + #define WOLFPKCS11_DLL_FILENAME "./src/.libs/libwolfpkcs11.so" + #endif +#endif +#ifndef WOLFPKCS11_DLL_SLOT + #define WOLFPKCS11_DLL_SLOT 1 +#endif + +#ifndef HAVE_PKCS11_STATIC +static void* dlib; +#endif +static CK_FUNCTION_LIST* funcList; +static CK_SLOT_ID slot = WOLFPKCS11_DLL_SLOT; + +static byte* userPin = (byte*)"wolfpkcs11-test"; +static CK_ULONG userPinLen; + +/* Load and initialize PKCS#11 library by name. + * + * library Name of library file. + * session Session handle to be opened. + * return CKR_OK on success, other value on failure. + */ +static CK_RV pkcs11_init(const char* library, CK_SESSION_HANDLE* session) +{ + CK_RV ret = CKR_OK; +#ifndef HAVE_PKCS11_STATIC + void* func; + + dlib = dlopen(library, RTLD_NOW | RTLD_LOCAL); + if (dlib == NULL) { + fprintf(stderr, "dlopen error: %s\n", dlerror()); + ret = -1; + } + + if (ret == CKR_OK) { + func = (void*)(CK_C_GetFunctionList)dlsym(dlib, "C_GetFunctionList"); + if (func == NULL) { + fprintf(stderr, "Failed to get function list function\n"); + ret = -1; + } + } + + if (ret == CKR_OK) { + ret = ((CK_C_GetFunctionList)func)(&funcList); + CHECK_CKR(ret, "Get Function List call"); + } + + if (ret != CKR_OK && dlib != NULL) + dlclose(dlib); + +#else + ret = C_GetFunctionList(&funcList); + (void)library; +#endif + + if (ret == CKR_OK) { + ret = funcList->C_Initialize(NULL); + CHECK_CKR(ret, "Initialize"); + } + + if (ret == CKR_OK) { + ret = funcList->C_OpenSession(slot, CKF_SERIAL_SESSION | CKF_RW_SESSION, + NULL, NULL, session); + CHECK_CKR(ret, "Open Session"); + } + + if (ret == CKR_OK) { + userPinLen = (CK_ULONG)XSTRLEN((char*)userPin); + ret = funcList->C_Login(*session, CKU_USER, userPin, userPinLen); + if (ret != CKR_OK) { + CHECK_CKR(ret, "Login"); + printf("Note: Login failed, continuing without authentication\n"); + printf(" Some operations may not work without proper " + "login\n"); + /* Don't fail completely - some operations might work without login */ + ret = CKR_OK; + } + } + + return ret; +} + +/* Finalize and close PKCS#11 library. + */ +static void pkcs11_final(CK_SESSION_HANDLE session) +{ + funcList->C_CloseSession(session); + funcList->C_Finalize(NULL); +#ifndef HAVE_PKCS11_STATIC + dlclose(dlib); +#endif +} + +#ifdef WOLFPKCS11_NSS + +static void print_hex(const char* label, const CK_BYTE* data, CK_ULONG len) +{ + printf("%s: ", label); + for (CK_ULONG i = 0; i < len; i++) { + printf("%02X", data[i]); + } + printf("\n"); +} + +static int demonstrate_pkcs12_pbe_key_generation(const char* library) +{ + CK_RV rv; + CK_SESSION_HANDLE session; + CK_OBJECT_HANDLE key; + /* Example password and salt (in real use, these should be secure) */ + CK_BYTE password[] = "MySecurePassword2024"; + CK_BYTE salt[] = { + 0x8A, 0x2F, 0x3E, 0x91, 0x45, 0x67, 0xBC, 0xDE, + 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0 + }; + CK_ULONG iterationCount = 100000; /* Modern recommended minimum */ + CK_ULONG keyLength = 32; /* 256-bit AES key */ + CK_PBE_PARAMS pbeParams; + CK_MECHANISM mechanism; + CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY; + CK_KEY_TYPE keyType = CKK_GENERIC_SECRET; + CK_BBOOL ckTrue = CK_TRUE; + CK_BBOOL ckFalse = CK_FALSE; + CK_BYTE keyLabel[] = "PKCS12-PBE-Generated-Key"; + CK_ATTRIBUTE keyTemplate[] = { + {CKA_CLASS, &keyClass, sizeof(keyClass)}, + {CKA_KEY_TYPE, &keyType, sizeof(keyType)}, + {CKA_VALUE_LEN, &keyLength, sizeof(keyLength)}, + {CKA_LABEL, keyLabel, sizeof(keyLabel) - 1}, + {CKA_ENCRYPT, &ckTrue, sizeof(ckTrue)}, + {CKA_DECRYPT, &ckTrue, sizeof(ckTrue)}, + {CKA_EXTRACTABLE, &ckTrue, sizeof(ckTrue)}, + {CKA_TOKEN, &ckFalse, sizeof(ckFalse)} + }; + CK_ULONG keyTemplateCount = sizeof(keyTemplate) / sizeof(keyTemplate[0]); + CK_BYTE keyValue[64]; + CK_ULONG keyValueLen = sizeof(keyValue); + CK_ATTRIBUTE getTemplate[] = { + {CKA_VALUE, keyValue, keyValueLen} + }; + struct { + CK_ULONG length; + const char* purpose; + } keyLengths[] = { + {16, "AES-128 encryption"}, + {24, "AES-192 encryption"}, + {32, "AES-256 encryption"}, + {64, "HMAC-SHA512 authentication"} + }; + CK_OBJECT_HANDLE testKey; + CK_ULONG testLen; + CK_OBJECT_HANDLE prodKey; + CK_BBOOL sensitive = CK_TRUE; + CK_BBOOL nonExtractable = CK_FALSE; /* Set to CK_TRUE for production */ + CK_BYTE prodLabel[] = "Production-PKCS12-Key"; + CK_ATTRIBUTE prodTemplate[] = { + {CKA_CLASS, &keyClass, sizeof(keyClass)}, + {CKA_KEY_TYPE, &keyType, sizeof(keyType)}, + {CKA_VALUE_LEN, &keyLength, sizeof(keyLength)}, + {CKA_LABEL, prodLabel, sizeof(prodLabel) - 1}, + {CKA_ENCRYPT, &ckTrue, sizeof(ckTrue)}, + {CKA_DECRYPT, &ckTrue, sizeof(ckTrue)}, + {CKA_SENSITIVE, &sensitive, sizeof(sensitive)}, + {CKA_EXTRACTABLE, &nonExtractable, sizeof(nonExtractable)}, + {CKA_TOKEN, &ckFalse, sizeof(ckFalse)} + }; + CK_ULONG prodTemplateCount = sizeof(prodTemplate) / sizeof(prodTemplate[0]); + CK_OBJECT_HANDLE aesKey; + CK_OBJECT_CLASS aesKeyClass = CKO_SECRET_KEY; + CK_KEY_TYPE aesKeyType = CKK_AES; + CK_ULONG aesKeyLength = 32; /* 256-bit AES key */ + CK_BYTE aesKeyLabel[] = "PKCS12-PBE-AES-Key"; + CK_ATTRIBUTE aesKeyTemplate[] = { + {CKA_CLASS, &aesKeyClass, sizeof(aesKeyClass)}, + {CKA_KEY_TYPE, &aesKeyType, sizeof(aesKeyType)}, + {CKA_VALUE_LEN, &aesKeyLength, sizeof(aesKeyLength)}, + {CKA_LABEL, aesKeyLabel, sizeof(aesKeyLabel) - 1}, + {CKA_ENCRYPT, &ckTrue, sizeof(ckTrue)}, + {CKA_DECRYPT, &ckTrue, sizeof(ckTrue)}, + {CKA_EXTRACTABLE, &ckTrue, sizeof(ckTrue)}, + {CKA_TOKEN, &ckFalse, sizeof(ckFalse)} + }; + CK_ULONG aesKeyTemplateCount = sizeof(aesKeyTemplate) / + sizeof(aesKeyTemplate[0]); + CK_BYTE plaintext[] = "Hello, PKCS#12 PBE!"; + CK_BYTE ciphertext[256]; + CK_ULONG ciphertextLen = sizeof(ciphertext); + CK_BYTE decrypted[256]; + CK_ULONG decryptedLen = sizeof(decrypted); + CK_BYTE iv[16] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}; + CK_MECHANISM encMech = { CKM_AES_CBC_PAD, iv, sizeof(iv) }; + CK_ULONG plaintextLen; + CK_BYTE extractedKeyValue[32]; + CK_ULONG extractedKeyValueLen = sizeof(extractedKeyValue); + CK_ATTRIBUTE extractTemplate[] = { + {CKA_VALUE, extractedKeyValue, extractedKeyValueLen} + }; + CK_OBJECT_HANDLE newAesKey; + CK_ATTRIBUTE newKeyTemplate[] = { + {CKA_CLASS, &aesKeyClass, sizeof(aesKeyClass)}, + {CKA_KEY_TYPE, &aesKeyType, sizeof(aesKeyType)}, + {CKA_VALUE, extractedKeyValue, 0}, /* Will be set later */ + {CKA_ENCRYPT, &ckTrue, sizeof(ckTrue)}, + {CKA_DECRYPT, &ckTrue, sizeof(ckTrue)}, + {CKA_TOKEN, &ckFalse, sizeof(ckFalse)} + }; + + printf("=== NSS PKCS#12 PBE SHA-256 HMAC Key Generation Example ===\n\n"); + + /* Initialize PKCS#11 and open session */ + rv = pkcs11_init(library, &session); + if (rv != CKR_OK) { + printf("ERROR: PKCS#11 initialization failed: 0x%08lX\n", rv); + return -1; + } + + /* Example 1: Basic key generation */ + printf("1. Basic Key Generation\n"); + printf(" Purpose: Generate a 256-bit encryption key from password\n\n"); + + printf(" Password: %s\n", (char*)password); + print_hex(" Salt", salt, sizeof(salt)); + printf(" Iterations: %lu\n", iterationCount); + printf(" Key Length: %lu bytes (%lu bits)\n\n", keyLength, keyLength * 8); + + /* Set up PBE parameters */ + pbeParams.pInitVector = NULL; + pbeParams.pPassword = password; + pbeParams.ulPasswordLen = strlen((char*)password); + pbeParams.pSalt = salt; + pbeParams.ulSaltLen = sizeof(salt); + pbeParams.ulIteration = iterationCount; + + mechanism.mechanism = CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN; + mechanism.pParameter = &pbeParams; + mechanism.ulParameterLen = sizeof(pbeParams); + + /* Generate the key */ + printf(" Generating key...\n"); + rv = funcList->C_GenerateKey(session, &mechanism, keyTemplate, + keyTemplateCount, &key); + if (rv != CKR_OK) { + printf("ERROR: C_GenerateKey failed: 0x%08lX\n", rv); + if (rv == CKR_MECHANISM_INVALID) { + printf(" CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN mechanism " + "not supported\n"); + printf(" This indicates the NSS extension is not available " + "in this build\n"); + } else if (rv == CKR_USER_NOT_LOGGED_IN) { + printf(" User not logged in - authentication may be " + "required\n"); + } else if (rv == CKR_FUNCTION_NOT_SUPPORTED) { + printf(" Function not supported by this PKCS#11 " + "implementation\n"); + } + pkcs11_final(session); + return -1; + } + + printf(" + Key generated successfully (handle: %lu)\n\n", key); + + /* Retrieve and display key information */ + + rv = funcList->C_GetAttributeValue(session, key, getTemplate, 1); + if (rv == CKR_OK) { + printf(" Generated Key Material:\n"); + print_hex(" Value", keyValue, getTemplate[0].ulValueLen); + printf("\n"); + } else if (rv == CKR_ATTRIBUTE_SENSITIVE) { + printf(" Key value is marked as sensitive (cannot extract)\n\n"); + } else { + printf(" Could not retrieve key value: 0x%08lX\n\n", rv); + } + + /* Example 2: Different key lengths for different use cases */ + printf("2. Multiple Key Lengths Example\n"); + printf(" Purpose: Generate keys for different cryptographic needs\n\n"); + + { + int keyLengthIndex; + for (keyLengthIndex = 0; keyLengthIndex < 4; keyLengthIndex++) { + testLen = keyLengths[keyLengthIndex].length; + + keyTemplate[2].pValue = &testLen; /* Update CKA_VALUE_LEN */ + + printf(" Generating %lu-byte key for %s...\n", + testLen, keyLengths[keyLengthIndex].purpose); + + rv = funcList->C_GenerateKey(session, &mechanism, keyTemplate, + keyTemplateCount, &testKey); + if (rv == CKR_OK) { + printf(" + Success (handle: %lu)\n", testKey); + } else { + printf(" - Failed: 0x%08lX\n", rv); + } + } + } + printf("\n"); + + /* Example 3: Security considerations */ + printf("3. Security Best Practices\n"); + printf(" - Use strong, unique passwords\n"); + printf(" - Generate cryptographically random salts\n"); + printf(" - Use sufficient iteration counts (100,000+ " + "recommended)\n"); + printf(" - Clear sensitive data from memory after use\n"); + printf(" - Store keys securely (mark as non-extractable for " + "production)\n\n"); + + /* Example 4: Production-ready key (non-extractable) */ + printf("4. Production Key Generation\n"); + printf(" Purpose: Generate a non-extractable key for production use\n\n"); + + rv = funcList->C_GenerateKey(session, &mechanism, prodTemplate, + prodTemplateCount, &prodKey); + if (rv == CKR_OK) { + printf(" + Production key generated (handle: %lu)\n", prodKey); + printf(" Key is marked as sensitive and non-extractable\n\n"); + } else { + printf(" - Production key generation failed: 0x%08lX\n\n", rv); + } + + /* Example 5: Generate proper AES key and test encryption */ + printf("5. AES Key Generation and Usage Test\n"); + printf(" Purpose: Generate an AES key using PBE and test encryption\n\n"); + + printf(" Generating AES-256 key using PBE...\n"); + rv = funcList->C_GenerateKey(session, &mechanism, aesKeyTemplate, + aesKeyTemplateCount, &aesKey); + if (rv != CKR_OK) { + printf(" - AES key generation failed: 0x%08lX\n", rv); + printf(" Skipping encryption test\n\n"); + } else { + printf(" + AES key generated successfully (handle: %lu)\n", + aesKey); + + /* Test encryption/decryption with the AES key */ + printf(" Plaintext: %s\n", plaintext); + print_hex(" IV", iv, sizeof(iv)); + printf(" Testing AES-CBC-PAD encryption...\n"); + + /* Use original plaintext without manual padding since CBC_PAD + * handles it */ + plaintextLen = strlen((char*)plaintext); + + rv = funcList->C_EncryptInit(session, &encMech, aesKey); + if (rv == CKR_OK) { + rv = funcList->C_Encrypt(session, plaintext, plaintextLen, + ciphertext, &ciphertextLen); + } + + if (rv == CKR_OK) { + printf(" + Encryption successful\n"); + print_hex(" Ciphertext", ciphertext, ciphertextLen); + + printf(" Testing AES-CBC-PAD decryption...\n"); + rv = funcList->C_DecryptInit(session, &encMech, aesKey); + if (rv == CKR_OK) { + rv = funcList->C_Decrypt(session, ciphertext, ciphertextLen, + decrypted, &decryptedLen); + } + + if (rv == CKR_OK && decryptedLen == plaintextLen && + memcmp(plaintext, decrypted, plaintextLen) == 0) { + printf(" + Decryption successful - plaintext recovered!\n"); + printf(" Original: %.*s\n", (int)plaintextLen, decrypted); + } else { + printf(" - Decryption failed or data mismatch " + "(rv=0x%08lX, len=%lu)\n", rv, decryptedLen); + if (rv == CKR_OK && decryptedLen > 0) { + printf(" Decrypted: %.*s\n", (int)decryptedLen, decrypted); + } + } + } else { + printf(" - Encryption failed: 0x%08lX\n", rv); + printf(" Error details: "); + if (rv == CKR_KEY_INDIGESTIBLE) { + printf("CKR_KEY_INDIGESTIBLE - Key format incompatible\n"); + } else if (rv == CKR_KEY_TYPE_INCONSISTENT) { + printf("CKR_KEY_TYPE_INCONSISTENT - Key type mismatch\n"); + } else if (rv == CKR_MECHANISM_INVALID) { + printf("CKR_MECHANISM_INVALID - Mechanism not supported\n"); + } else if (rv == CKR_KEY_HANDLE_INVALID) { + printf("CKR_KEY_HANDLE_INVALID - Invalid key handle\n"); + } else { + printf("Unknown error\n"); + } + + printf(" Trying to extract and re-import key...\n"); + + /* Try to extract the PBE-generated key value and create a new + * AES key */ + + rv = funcList->C_GetAttributeValue(session, aesKey, + extractTemplate, 1); + if (rv == CKR_OK) { + printf(" Extracted key value, creating new AES key " + "object...\n"); + + /* Create a new AES key from the extracted value */ + newKeyTemplate[2].ulValueLen = extractTemplate[0].ulValueLen; + + rv = funcList->C_CreateObject(session, newKeyTemplate, + sizeof(newKeyTemplate)/ + sizeof(newKeyTemplate[0]), + &newAesKey); + if (rv == CKR_OK) { + printf(" New AES key created, retrying encryption...\n"); + + rv = funcList->C_EncryptInit(session, &encMech, + newAesKey); + if (rv == CKR_OK) { + rv = funcList->C_Encrypt(session, plaintext, + plaintextLen, ciphertext, + &ciphertextLen); + if (rv == CKR_OK) { + printf(" + Encryption successful with " + "re-imported key!\n"); + print_hex(" Ciphertext", ciphertext, ciphertextLen); + } else { + printf(" - Encryption still failed: " + "0x%08lX\n", rv); + } + } + } else { + printf(" Failed to create new AES key: 0x%08lX\n", rv); + } + + /* Clear sensitive key material */ + memset(extractedKeyValue, 0, sizeof(extractedKeyValue)); + } else { + printf(" Could not extract key value: 0x%08lX\n", rv); + } + } + } + printf("\n"); + + /* Clean up */ + printf("6. Cleanup\n"); + + /* Clear sensitive data */ + memset(password, 0, sizeof(password)); + memset(&pbeParams, 0, sizeof(pbeParams)); + + pkcs11_final(session); + + printf(" + Session closed and library finalized\n"); + printf(" + Sensitive data cleared from memory\n\n"); + + printf("=== Example completed successfully ===\n"); + return 0; +} + +/* Match the command line argument with the string. + * + * arg Command line argument. + * str String to check for. + * return 1 if the command line argument matches the string, 0 otherwise. + */ +static int string_matches(const char* arg, const char* str) +{ + int len = (int)XSTRLEN(str) + 1; + return XSTRNCMP(arg, str, len) == 0; +} + +/* Display the usage options of the program. */ +static void Usage(void) +{ + printf("nss_pkcs12_pbe_example\n"); + printf("-? Help, print this usage\n"); + printf("-lib PKCS#11 library to test\n"); + printf("-slot Slot number to use\n"); +} + +#ifndef NO_MAIN_DRIVER +int main(int argc, char* argv[]) +#else +int nss_pkcs12_pbe_example(int argc, char* argv[]) +#endif +{ + const char* libName = WOLFPKCS11_DLL_FILENAME; + +#ifndef WOLFPKCS11_NO_ENV + if (!XGETENV("WOLFPKCS11_TOKEN_PATH")) { + XSETENV("WOLFPKCS11_TOKEN_PATH", "./store", 1); + } +#endif + + argc--; + argv++; + while (argc > 0) { + if (string_matches(*argv, "-?")) { + Usage(); + return 0; + } + else if (string_matches(*argv, "-lib")) { + argc--; + argv++; + if (argc == 0) { + fprintf(stderr, "Library name not supplied\n"); + return 1; + } + libName = *argv; + } + else if (string_matches(*argv, "-slot")) { + argc--; + argv++; + if (argc == 0) { + fprintf(stderr, "Slot number not supplied\n"); + return 1; + } + slot = atoi(*argv); + } + else { + fprintf(stderr, "Unrecognized command line argument\n %s\n", + argv[0]); + return 1; + } + + argc--; + argv++; + } + + printf("NSS PKCS#12 PBE SHA-256 HMAC Key Generation Example\n"); + printf("This example demonstrates how to use the " + "CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN\n"); + printf("mechanism to generate cryptographic keys from passwords using " + "PBKDF2.\n\n"); + + if (demonstrate_pkcs12_pbe_key_generation(libName) != 0) { + printf("Example failed!\n"); + return 1; + } + + printf("\nFor more information, see:\n"); + printf("- PKCS#11 v2.40 specification\n"); + printf("- RFC 2898 (PKCS #5 v2.0: PBKDF2)\n"); + printf("- wolfPKCS11 documentation\n"); + + return 0; +} + +#else /* WOLFPKCS11_NSS */ + +#ifndef NO_MAIN_DRIVER +int main(void) +#else +int nss_pkcs12_pbe_example(void) +#endif +{ + printf("NSS PKCS#12 PBE support not compiled in.\n"); + printf("Build with WOLFPKCS11_NSS defined to enable this feature.\n"); + return 0; +} + +#endif /* WOLFPKCS11_NSS */ diff --git a/src/crypto.c b/src/crypto.c index 7e4ee71e..a4cd83b8 100644 --- a/src/crypto.c +++ b/src/crypto.c @@ -19,7 +19,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ - #ifdef HAVE_CONFIG_H #include #endif @@ -234,6 +233,7 @@ static AttributeType attrType[] = { { CKA_TRUST_EMAIL_PROTECTION, ATTR_TYPE_ULONG }, { CKA_TRUST_CODE_SIGNING, ATTR_TYPE_ULONG }, { CKA_TRUST_STEP_UP_APPROVED, ATTR_TYPE_BOOL }, + { CKA_NSS_DB, ATTR_TYPE_DATA }, #endif }; /* Count of elements in attribute type list. */ @@ -6295,6 +6295,31 @@ CK_RV C_GenerateKey(CK_SESSION_HANDLE hSession, case CKM_GENERIC_SECRET_KEY_GEN: keyType = CKK_GENERIC_SECRET; break; +#ifndef NO_HMAC + case CKM_PKCS5_PBKD2: + { + CK_ATTRIBUTE *keyTypeAttr = NULL; + keyType = CKK_GENERIC_SECRET; + FindAttributeType(pTemplate, ulCount, CKA_KEY_TYPE, &keyTypeAttr); + if (keyTypeAttr != NULL && keyTypeAttr->pValue != NULL && + keyTypeAttr->ulValueLen == sizeof(CK_ULONG)) { + CK_KEY_TYPE templateKeyType = *(CK_KEY_TYPE*)keyTypeAttr->pValue; + if (templateKeyType == CKK_AES || + templateKeyType == CKK_HKDF) { + keyType = templateKeyType; + } + } + } + break; +#ifdef WOLFPKCS11_NSS + case CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN: + case CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN: + case CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN: + case CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN: + keyType = CKK_GENERIC_SECRET; + break; +#endif +#endif default: rv = CKR_MECHANISM_INVALID; break; @@ -6302,6 +6327,254 @@ CK_RV C_GenerateKey(CK_SESSION_HANDLE hSession, if (rv == CKR_OK) { CK_ATTRIBUTE *lenAttr = NULL; + +#ifndef NO_HMAC + /* PKCS#5 PBKDF2 key generation */ + if (pMechanism->mechanism == CKM_PKCS5_PBKD2) { + CK_BYTE* derivedKey; + CK_ULONG derivedKeyLen; + WP11_Object* pbkdf2Key; + unsigned char* secretKeyData[2] = { NULL, NULL }; + CK_ULONG secretKeyLen[2] = { 0, 0 }; + int ret; + int hashType; + CK_ULONG pwLen; + CK_PKCS5_PBKD2_PARAMS2* params; + + CK_ULONG size1 = sizeof(CK_PKCS5_PBKD2_PARAMS2); + CK_ULONG size2 = sizeof(CK_PKCS5_PBKD2_PARAMS); + CK_ULONG paramLen = pMechanism->ulParameterLen; + + if (pMechanism->pParameter == NULL) { + return CKR_MECHANISM_PARAM_INVALID; + } + + /* Avoid a clang-tidy warning that we are comparing the same thing + * on platforms where these two structs are the same size. + */ + if (size1 != size2) { + if (paramLen != size1 && paramLen != size2) { + return CKR_MECHANISM_PARAM_INVALID; + } + } + else if (paramLen != size1) { + return CKR_MECHANISM_PARAM_INVALID; + } + + params = (CK_PKCS5_PBKD2_PARAMS2*)pMechanism->pParameter; + + if (params->saltSource != CKZ_SALT_SPECIFIED || + params->pSaltSourceData == NULL || + params->ulSaltSourceDataLen == 0) { + return CKR_MECHANISM_PARAM_INVALID; + } + + if (params->iterations == 0) { + return CKR_MECHANISM_PARAM_INVALID; + } + + /* Assume older pointer type if password is too long */ + if (params->ulPasswordLen > CK_PKCS5_PBKD2_PARAMS_MAX_PWD_LEN) { + pwLen = *((CK_PKCS5_PBKD2_PARAMS*) + pMechanism->pParameter)->ulPasswordLen; + } + else { + pwLen = params->ulPasswordLen; + } + + switch (params->prf) { +#ifndef NO_SHA + case CKP_PKCS5_PBKD2_HMAC_SHA1: + hashType = WC_SHA; + break; +#endif +#ifdef WOLFSSL_SHA224 + case CKP_PKCS5_PBKD2_HMAC_SHA224: + hashType = WC_SHA224; + break; +#endif +#ifndef NO_SHA256 + case CKP_PKCS5_PBKD2_HMAC_SHA256: + hashType = WC_SHA256; + break; +#endif +#ifdef WOLFSSL_SHA384 + case CKP_PKCS5_PBKD2_HMAC_SHA384: + hashType = WC_SHA384; + break; +#endif +#ifdef WOLFSSL_SHA512 + case CKP_PKCS5_PBKD2_HMAC_SHA512: + hashType = WC_SHA512; + break; +#endif +#ifndef WOLFSSL_NOSHA512_224 + case CKP_PKCS5_PBKD2_HMAC_SHA512_224: + hashType = WC_SHA512_224; + break; +#endif +#ifndef WOLFSSL_NOSHA512_256 + case CKP_PKCS5_PBKD2_HMAC_SHA512_256: + hashType = WC_SHA512_256; + break; +#endif + default: + return CKR_MECHANISM_PARAM_INVALID; + } + + /* Get desired key length from template */ + FindAttributeType(pTemplate, ulCount, CKA_VALUE_LEN, &lenAttr); + if (lenAttr == NULL) + return CKR_TEMPLATE_INCOMPLETE; + if (lenAttr->pValue == NULL) + return CKR_ATTRIBUTE_VALUE_INVALID; + if (lenAttr->ulValueLen != sizeof(CK_ULONG)) + return CKR_ATTRIBUTE_VALUE_INVALID; + derivedKeyLen = *(CK_ULONG*)lenAttr->pValue; + if (derivedKeyLen == 0) + return CKR_ATTRIBUTE_VALUE_INVALID; + + derivedKey = (CK_BYTE*)XMALLOC(derivedKeyLen, NULL, + DYNAMIC_TYPE_TMP_BUFFER); + if (derivedKey == NULL) + return CKR_HOST_MEMORY; + + ret = WP11_PBKDF2(derivedKey, + params->pPassword, (int)pwLen, + (const byte*)params->pSaltSourceData, + (int)params->ulSaltSourceDataLen, + (int)params->iterations, + (int)derivedKeyLen, + hashType); + + if (ret != 0) { + XFREE(derivedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return CKR_FUNCTION_FAILED; + } + + rv = NewObject(session, keyType, CKO_SECRET_KEY, pTemplate, ulCount, + &pbkdf2Key); + if (rv == CKR_OK) { + /* Set the derived key material */ + secretKeyData[0] = (unsigned char*)&derivedKeyLen; + secretKeyLen[0] = sizeof(CK_ULONG); + secretKeyData[1] = derivedKey; + secretKeyLen[1] = derivedKeyLen; + + ret = WP11_Object_SetSecretKey(pbkdf2Key, secretKeyData, secretKeyLen); + if (ret == 0) { + rv = AddObject(session, pbkdf2Key, pTemplate, ulCount, phKey); + if (rv != CKR_OK) { + WP11_Object_Free(pbkdf2Key); + } + } else { + WP11_Object_Free(pbkdf2Key); + rv = CKR_FUNCTION_FAILED; + } + } + + XFREE(derivedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return rv; + } +#ifdef WOLFPKCS11_NSS + /* NSS PKCS#12 PBE requires parameters, others don't */ + else if (pMechanism->mechanism == CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN || + pMechanism->mechanism == CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN || + pMechanism->mechanism == CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN || + pMechanism->mechanism == CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN) { + CK_BYTE* derivedKey; + CK_ULONG derivedKeyLen; + WP11_Object* pbeKey; + unsigned char* secretKeyData[2] = { NULL, NULL }; + CK_ULONG secretKeyLen[2] = { 0, 0 }; + int ret; + int hashType; + CK_BYTE_PTR password = NULL; + CK_ULONG passwordLen = 0; + CK_BYTE_PTR salt = NULL; + CK_ULONG saltLen = 0; + CK_ULONG iterationCount = 0; + CK_PBE_PARAMS* params; + + /* Determine hash type and key length based on mechanism */ + switch (pMechanism->mechanism) { + case CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN: + hashType = WC_SHA224; + derivedKeyLen = WC_SHA224_DIGEST_SIZE; + break; + case CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN: + hashType = WC_SHA256; + derivedKeyLen = WC_SHA256_DIGEST_SIZE; + break; + case CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN: + hashType = WC_SHA384; + derivedKeyLen = WC_SHA384_DIGEST_SIZE; + break; + case CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN: + hashType = WC_SHA512; + derivedKeyLen = WC_SHA512_DIGEST_SIZE; + break; + default: + return CKR_MECHANISM_INVALID; + } + + if (pMechanism->pParameter == NULL || + pMechanism->ulParameterLen != sizeof(CK_PBE_PARAMS)) { + return CKR_MECHANISM_PARAM_INVALID; + } + params = (CK_PBE_PARAMS*)pMechanism->pParameter; + password = params->pPassword; + passwordLen = params->ulPasswordLen; + salt = params->pSalt; + saltLen = params->ulSaltLen; + iterationCount = params->ulIteration; + + if (salt == NULL || saltLen == 0 || + iterationCount == 0) { + return CKR_MECHANISM_PARAM_INVALID; + } + + derivedKey = (CK_BYTE*)XMALLOC(derivedKeyLen, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (derivedKey == NULL) + return CKR_HOST_MEMORY; + + ret = WP11_PKCS12_PBKDF(derivedKey, password, (int)passwordLen, + salt, (int)saltLen, (int)iterationCount, + (int)derivedKeyLen, hashType); + + if (ret != 0) { + XFREE(derivedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return CKR_FUNCTION_FAILED; + } + + /* Create the key object */ + rv = NewObject(session, keyType, CKO_SECRET_KEY, pTemplate, ulCount, &pbeKey); + if (rv == CKR_OK) { + /* Set the derived key material */ + secretKeyData[0] = (unsigned char*)&derivedKeyLen; + secretKeyLen[0] = sizeof(CK_ULONG); + secretKeyData[1] = derivedKey; + secretKeyLen[1] = derivedKeyLen; + + ret = WP11_Object_SetSecretKey(pbeKey, secretKeyData, secretKeyLen); + if (ret == 0) { + rv = AddObject(session, pbeKey, pTemplate, ulCount, phKey); + if (rv != CKR_OK) { + WP11_Object_Free(pbeKey); + } + } else { + WP11_Object_Free(pbeKey); + rv = CKR_FUNCTION_FAILED; + } + } + + XFREE(derivedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return rv; + } +#endif +#endif + + /* Standard key generation for non-PBE mechanisms */ if (pMechanism->pParameter != NULL || pMechanism->ulParameterLen != 0) { return CKR_MECHANISM_PARAM_INVALID; diff --git a/src/internal.c b/src/internal.c index 5dc9fa07..b3a317ca 100644 --- a/src/internal.c +++ b/src/internal.c @@ -5739,10 +5739,14 @@ int WP11_Slot_HasSession(WP11_Slot* slot) * -ve on failure. */ static int HashPIN(char* pin, int pinLen, byte* seed, int seedLen, byte* hash, - int hashLen) + int hashLen, WP11_Slot* slot) { -#ifdef HAVE_SCRYPT +#ifdef WOLFPKCS11_PBKDF2 + return wc_PBKDF2_ex(hash, (byte*)pin, pinLen, seed, seedLen, + PBKDF2_ITERATIONS, hashLen, WC_SHA256, NULL, slot->devId); +#elif defined(HAVE_SCRYPT) /* Convert PIN into secret using scrypt algorithm. */ + (void)slot; return wc_scrypt(hash, (byte*)pin, pinLen, seed, seedLen, WP11_HASH_PIN_COST, WP11_HASH_PIN_BLOCKSIZE, WP11_HASH_PIN_PARALLEL, hashLen); @@ -5750,6 +5754,7 @@ static int HashPIN(char* pin, int pinLen, byte* seed, int seedLen, byte* hash, /* fallback to simple SHA2-256 hash of pin */ (void)seed; (void)seedLen; + (void)slot; XMEMSET(hash, 0, hashLen); return wc_Sha256Hash((const byte*)pin, pinLen, hash); #else @@ -5759,6 +5764,7 @@ static int HashPIN(char* pin, int pinLen, byte* seed, int seedLen, byte* hash, (void)seedLen; (void)hash; (void)hashLen; + (void)slot; return NOT_COMPILED_IN; #endif } @@ -5824,7 +5830,7 @@ int WP11_Slot_CheckSOPin(WP11_Slot* slot, char* pin, int pinLen) /* Costly Operation done out of lock. */ ret = HashPIN(pin, pinLen, token->soPinSeed, sizeof(token->soPinSeed), - hash, sizeof(hash)); + hash, sizeof(hash), slot); WP11_Lock_LockRO(&slot->lock); } @@ -5864,7 +5870,7 @@ int WP11_Slot_CheckUserPin(WP11_Slot* slot, char* pin, int pinLen) /* Costly Operation done out of lock. */ ret = HashPIN(pin, pinLen, token->userPinSeed, - sizeof(token->userPinSeed), hash, sizeof(hash)); + sizeof(token->userPinSeed), hash, sizeof(hash), slot); WP11_Lock_LockRO(&slot->lock); } @@ -6028,7 +6034,7 @@ int WP11_Slot_UserLogin(WP11_Slot* slot, char* pin, int pinLen) #ifndef WOLFPKCS11_NO_STORE if (ret == 0) { ret = HashPIN(pin, pinLen, token->seed, sizeof(token->seed), - token->key, sizeof(token->key)); + token->key, sizeof(token->key), slot); } #endif WP11_Lock_LockRW(&slot->lock); @@ -6100,7 +6106,7 @@ int WP11_Slot_SetSOPin(WP11_Slot* slot, char* pin, int pinLen) /* Costly Operation done out of lock. */ ret = HashPIN(pin, pinLen, token->soPinSeed, sizeof(token->soPinSeed), token->soPin, - sizeof(token->soPin)); + sizeof(token->soPin), slot); WP11_Lock_LockRW(&slot->lock); } if (ret == 0) { @@ -6149,11 +6155,11 @@ int WP11_Slot_SetUserPin(WP11_Slot* slot, char* pin, int pinLen) token->userPinEmpty = 0; ret = HashPIN(pin, pinLen, token->userPinSeed, sizeof(token->userPinSeed), token->userPin, - sizeof(token->userPin)); + sizeof(token->userPin), slot); #ifndef WOLFPKCS11_NO_STORE if (ret == 0) { ret = HashPIN(pin, pinLen, token->seed, sizeof(token->seed), - token->key, sizeof(token->key)); + token->key, sizeof(token->key), slot); } #endif WP11_Lock_LockRW(&slot->lock); @@ -9276,6 +9282,10 @@ int WP11_Object_SetAttr(WP11_Object* object, CK_ATTRIBUTE_TYPE type, byte* data, case CKA_TRUST_CODE_SIGNING: case CKA_TRUST_STEP_UP_APPROVED: /* Handled in WP11_Object_SetTrust */ +#ifdef WOLFPKCS11_NSS + case CKA_NSS_DB: + /* Ignored legacy field */ +#endif #endif case CKA_CERTIFICATE_TYPE: /* Handled in WP11_Object_SetCert */ @@ -12656,7 +12666,35 @@ int WP11_Digest_Single(unsigned char* data, word32 dataLen, return ret; } +int WP11_PBKDF2(byte* output, const byte* passwd, int pLen, + const byte* salt, int sLen, int iterations, int kLen, int hashType) +{ + return wc_PBKDF2(output, passwd, pLen, salt, sLen, iterations, kLen, + hashType); +} + +int WP11_PKCS12_PBKDF(byte* output, const byte* passwd, int pLen, + const byte* salt, int sLen, int iterations, int kLen, int hashType) +{ + /* For PKCS#12 MAC key derivation, purpose should be 3 */ + return wc_PKCS12_PBKDF(output, passwd, pLen, salt, sLen, iterations, + kLen, hashType, 3); +} + #ifndef NO_HMAC +/** + * Return the length of a signature in bytes. + * + * @param session [in] Session object. + * @return Length of HMAC signature in bytes. + */ +int WP11_Hmac_SigLen(WP11_Session* session) +{ + WP11_Hmac* hmac = &session->params.hmac; + + return hmac->hmacSz; +} + /** * Convert the HMAC mechanism to a wolfCrypt hash type. * @@ -12708,20 +12746,6 @@ static int wp11_hmac_hash_type(CK_MECHANISM_TYPE hmacMech, int* hashType) return ret; } -/** - * Return the length of a signature in bytes. - * - * @param session [in] Session object. - * @return Length of HMAC signature in bytes. - */ -int WP11_Hmac_SigLen(WP11_Session* session) -{ - WP11_Hmac* hmac = &session->params.hmac; - - return hmac->hmacSz; -} - - /** * Initialize the HMAC operation. * diff --git a/src/slot.c b/src/slot.c index 7d59ecbc..5e466a20 100644 --- a/src/slot.c +++ b/src/slot.c @@ -460,11 +460,16 @@ static CK_MECHANISM_TYPE mechanismList[] = { #ifdef WOLFPKCS11_NSS /* Only advertise CKM_SSL3_MASTER_KEY_DERIVE. Not implemented. */ CKM_SSL3_MASTER_KEY_DERIVE, + CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN, + CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN, + CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN, + CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN, #endif #ifdef WOLFSSL_HAVE_PRF CKM_TLS_MAC, #endif CKM_GENERIC_SECRET_KEY_GEN, + CKM_PKCS5_PBKD2 }; /* Count of mechanisms in list. */ @@ -650,6 +655,18 @@ static CK_MECHANISM_INFO nssTls12MasterKeyDeriveDhInfo = { static CK_MECHANISM_INFO nssTls12MasterKeyDeriveInfo = { 48, 128, CKF_DERIVE }; +static CK_MECHANISM_INFO nssPkcs12PbeSha224HmacKeyGenMechInfo = { + 224, 224, CKF_GENERATE +}; +static CK_MECHANISM_INFO nssPkcs12PbeSha256HmacKeyGenMechInfo = { + 256, 256, CKF_GENERATE +}; +static CK_MECHANISM_INFO nssPkcs12PbeSha384HmacKeyGenMechInfo = { + 384, 384, CKF_GENERATE +}; +static CK_MECHANISM_INFO nssPkcs12PbeSha512HmacKeyGenMechInfo = { + 512, 512, CKF_GENERATE +}; #endif #endif #ifdef WOLFPKCS11_NSS @@ -801,6 +818,10 @@ static CK_MECHANISM_INFO genSecKeyGenMechInfo = { 1, 32, CKF_GENERATE }; +static CK_MECHANISM_INFO pkcs5Pbkdf2MechInfo = { + 1, 256, CKF_GENERATE +}; + /** * Get information on a mechanism. * @@ -1114,6 +1135,22 @@ CK_RV C_GetMechanismInfo(CK_SLOT_ID slotID, CK_MECHANISM_TYPE type, XMEMCPY(pInfo, &nssTls12MasterKeyDeriveDhInfo, sizeof(CK_MECHANISM_INFO)); break; + case CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN: + XMEMCPY(pInfo, &nssPkcs12PbeSha224HmacKeyGenMechInfo, + sizeof(CK_MECHANISM_INFO)); + break; + case CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN: + XMEMCPY(pInfo, &nssPkcs12PbeSha256HmacKeyGenMechInfo, + sizeof(CK_MECHANISM_INFO)); + break; + case CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN: + XMEMCPY(pInfo, &nssPkcs12PbeSha384HmacKeyGenMechInfo, + sizeof(CK_MECHANISM_INFO)); + break; + case CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN: + XMEMCPY(pInfo, &nssPkcs12PbeSha512HmacKeyGenMechInfo, + sizeof(CK_MECHANISM_INFO)); + break; #endif #endif #ifdef WOLFPKCS11_NSS @@ -1133,6 +1170,10 @@ CK_RV C_GetMechanismInfo(CK_SLOT_ID slotID, CK_MECHANISM_TYPE type, XMEMCPY(pInfo, &genSecKeyGenMechInfo, sizeof(CK_MECHANISM_INFO)); break; + case CKM_PKCS5_PBKD2: + XMEMCPY(pInfo, &pkcs5Pbkdf2MechInfo, + sizeof(CK_MECHANISM_INFO)); + break; default: return CKR_MECHANISM_INVALID; } diff --git a/tests/pkcs11test.c b/tests/pkcs11test.c index 7cbfba9a..547a2d75 100644 --- a/tests/pkcs11test.c +++ b/tests/pkcs11test.c @@ -415,22 +415,22 @@ static CK_RV test_nss_config_string_parsing(void* args) CK_RV ret; CK_C_INITIALIZE_ARGS initArgs; CK_CHAR_PTR nssConfigStr = (CK_CHAR_PTR)"configdir='' certPrefix='' keyPrefix='' secmod='' flags=readOnly,noCertDB,noModDB,forceOpen,optimizeSpace updatedir='' updateCertPrefix='' updateKeyPrefix='' updateid='' updateTokenDescription=''"; - + (void)args; - + /* Test with the problematic NSS config string that has unquoted flags */ XMEMSET(&initArgs, 0x00, sizeof(initArgs)); initArgs.flags = CKF_OS_LOCKING_OK; initArgs.LibraryParameters = (CK_CHAR_PTR *)nssConfigStr; - + /* This should succeed - the parser should handle unquoted flag values */ ret = funcList->C_Initialize(&initArgs); CHECK_CKR(ret, "Initialize with NSS config string"); - + if (ret == CKR_OK) { funcList->C_Finalize(NULL); } - + return ret; } @@ -439,22 +439,22 @@ static CK_RV test_nss_config_string_mixed_values(void* args) CK_RV ret; CK_C_INITIALIZE_ARGS initArgs; CK_CHAR_PTR nssConfigStr = (CK_CHAR_PTR)"configdir='/tmp/test' certPrefix='' keyPrefix=cert flags=readOnly,noCertDB updatedir='' updateid=test123"; - + (void)args; - + /* Test with mixed quoted and unquoted values */ XMEMSET(&initArgs, 0x00, sizeof(initArgs)); initArgs.flags = CKF_OS_LOCKING_OK; initArgs.LibraryParameters = (CK_CHAR_PTR *)nssConfigStr; - + /* This should succeed - the parser should handle mixed quoted/unquoted values */ ret = funcList->C_Initialize(&initArgs); CHECK_CKR(ret, "Initialize with mixed NSS config string"); - + if (ret == CKR_OK) { funcList->C_Finalize(NULL); } - + return ret; } #endif @@ -1432,6 +1432,475 @@ static CK_RV test_nss_trust_object_token_storage(void* args) return ret; } + +#ifndef NO_HMAC +/* Test NSS PKCS#12 PBE SHA224 HMAC key generation */ +static CK_RV test_nss_pkcs12_pbe_sha224_hmac_key_gen(void* args) +{ + CK_SESSION_HANDLE session = *(CK_SESSION_HANDLE*)args; + CK_RV ret = CKR_OK; + CK_OBJECT_HANDLE key = CK_INVALID_HANDLE; + + /* Test parameters */ + CK_BYTE password[] = "TestPassword224"; + CK_BYTE salt[] = { + 0x8A, 0x2F, 0x3E, 0x91, 0x45, 0x67, 0xBC, 0xDE, + 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0 + }; + CK_ULONG iterations = 10000; + CK_ULONG keyLength = 32; /* 256-bit key */ + + CK_PBE_PARAMS pbeParams = { + NULL, + password, + strlen((char*)password), + salt, + sizeof(salt), + iterations + }; + + CK_MECHANISM mechanism = { + CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN, + &pbeParams, + sizeof(pbeParams) + }; + + CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY; + CK_KEY_TYPE keyType = CKK_GENERIC_SECRET; + CK_BYTE keyLabel[] = "Test-NSS-PKCS12-PBE-SHA224-Key"; + + CK_ATTRIBUTE keyTemplate[] = { + {CKA_CLASS, &keyClass, sizeof(keyClass)}, + {CKA_KEY_TYPE, &keyType, sizeof(keyType)}, + {CKA_VALUE_LEN, &keyLength, sizeof(keyLength)}, + {CKA_LABEL, keyLabel, sizeof(keyLabel)-1} + }; + + ret = funcList->C_GenerateKey(session, &mechanism, keyTemplate, + sizeof(keyTemplate)/sizeof(CK_ATTRIBUTE), &key); + CHECK_CKR(ret, "NSS PKCS#12 PBE SHA224 HMAC Key Generation"); + + /* Test with invalid parameters */ + if (ret == CKR_OK) { + CK_PBE_PARAMS invalidParams = pbeParams; + CK_MECHANISM invalidMech = mechanism; + invalidParams.ulIteration = 0; + invalidMech.pParameter = &invalidParams; + + ret = funcList->C_GenerateKey(session, &invalidMech, keyTemplate, + sizeof(keyTemplate)/sizeof(CK_ATTRIBUTE), &key); + CHECK_CKR_FAIL(ret, CKR_MECHANISM_PARAM_INVALID, + "NSS PKCS#12 PBE SHA224 with zero iterations"); + ret = CKR_OK; /* Reset for next test */ + } + + return ret; +} + +/* Test NSS PKCS#12 PBE SHA256 HMAC key generation */ +static CK_RV test_nss_pkcs12_pbe_sha256_hmac_key_gen(void* args) +{ + CK_SESSION_HANDLE session = *(CK_SESSION_HANDLE*)args; + CK_RV ret = CKR_OK; + CK_OBJECT_HANDLE key = CK_INVALID_HANDLE; + + /* Test parameters */ + CK_BYTE password[] = "TestPassword256"; + CK_BYTE salt[] = { + 0x8A, 0x2F, 0x3E, 0x91, 0x45, 0x67, 0xBC, 0xDE, + 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0 + }; + CK_ULONG iterations = 10000; + CK_ULONG keyLength = 32; /* 256-bit key */ + + CK_PBE_PARAMS pbeParams = { + NULL, + password, + strlen((char*)password), + salt, + sizeof(salt), + iterations + }; + + CK_MECHANISM mechanism = { + CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN, + &pbeParams, + sizeof(pbeParams) + }; + + CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY; + CK_KEY_TYPE keyType = CKK_GENERIC_SECRET; + CK_BYTE keyLabel[] = "Test-NSS-PKCS12-PBE-SHA256-Key"; + + CK_ATTRIBUTE keyTemplate[] = { + {CKA_CLASS, &keyClass, sizeof(keyClass)}, + {CKA_KEY_TYPE, &keyType, sizeof(keyType)}, + {CKA_VALUE_LEN, &keyLength, sizeof(keyLength)}, + {CKA_LABEL, keyLabel, sizeof(keyLabel)-1} + }; + + ret = funcList->C_GenerateKey(session, &mechanism, keyTemplate, + sizeof(keyTemplate)/sizeof(CK_ATTRIBUTE), &key); + CHECK_CKR(ret, "NSS PKCS#12 PBE SHA256 HMAC Key Generation"); + + /* Test with invalid salt */ + if (ret == CKR_OK) { + CK_PBE_PARAMS invalidParams = pbeParams; + CK_MECHANISM invalidMech = mechanism; + invalidParams.pSalt = NULL; + invalidMech.pParameter = &invalidParams; + + ret = funcList->C_GenerateKey(session, &invalidMech, keyTemplate, + sizeof(keyTemplate)/sizeof(CK_ATTRIBUTE), &key); + CHECK_CKR_FAIL(ret, CKR_MECHANISM_PARAM_INVALID, + "NSS PKCS#12 PBE SHA256 with NULL salt"); + ret = CKR_OK; /* Reset for next test */ + } + + return ret; +} + +/* Test NSS PKCS#12 PBE SHA384 HMAC key generation */ +static CK_RV test_nss_pkcs12_pbe_sha384_hmac_key_gen(void* args) +{ + CK_SESSION_HANDLE session = *(CK_SESSION_HANDLE*)args; + CK_RV ret = CKR_OK; + CK_OBJECT_HANDLE key = CK_INVALID_HANDLE; + + /* Test parameters */ + CK_BYTE password[] = "TestPassword384"; + CK_BYTE salt[] = { + 0x8A, 0x2F, 0x3E, 0x91, 0x45, 0x67, 0xBC, 0xDE, + 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0 + }; + CK_ULONG iterations = 10000; + CK_ULONG keyLength = 48; /* 384-bit key */ + + CK_PBE_PARAMS pbeParams = { + NULL, + password, + strlen((char*)password), + salt, + sizeof(salt), + iterations + }; + + CK_MECHANISM mechanism = { + CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN, + &pbeParams, + sizeof(pbeParams) + }; + + CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY; + CK_KEY_TYPE keyType = CKK_GENERIC_SECRET; + CK_BYTE keyLabel[] = "Test-NSS-PKCS12-PBE-SHA384-Key"; + + CK_ATTRIBUTE keyTemplate[] = { + {CKA_CLASS, &keyClass, sizeof(keyClass)}, + {CKA_KEY_TYPE, &keyType, sizeof(keyType)}, + {CKA_VALUE_LEN, &keyLength, sizeof(keyLength)}, + {CKA_LABEL, keyLabel, sizeof(keyLabel)-1} + }; + + ret = funcList->C_GenerateKey(session, &mechanism, keyTemplate, + sizeof(keyTemplate)/sizeof(CK_ATTRIBUTE), &key); + CHECK_CKR(ret, "NSS PKCS#12 PBE SHA384 HMAC Key Generation"); + + return ret; +} + +/* Test NSS PKCS#12 PBE SHA512 HMAC key generation */ +static CK_RV test_nss_pkcs12_pbe_sha512_hmac_key_gen(void* args) +{ + CK_SESSION_HANDLE session = *(CK_SESSION_HANDLE*)args; + CK_RV ret = CKR_OK; + CK_OBJECT_HANDLE key = CK_INVALID_HANDLE; + + /* Test parameters */ + CK_BYTE password[] = "TestPassword512"; + CK_BYTE salt[] = { + 0x8A, 0x2F, 0x3E, 0x91, 0x45, 0x67, 0xBC, 0xDE, + 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0 + }; + CK_ULONG iterations = 10000; + CK_ULONG keyLength = 64; /* 512-bit key */ + + CK_PBE_PARAMS pbeParams = { + NULL, + password, + strlen((char*)password), + salt, + sizeof(salt), + iterations + }; + + CK_MECHANISM mechanism = { + CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN, + &pbeParams, + sizeof(pbeParams) + }; + + CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY; + CK_KEY_TYPE keyType = CKK_GENERIC_SECRET; + CK_BYTE keyLabel[] = "Test-NSS-PKCS12-PBE-SHA512-Key"; + + CK_ATTRIBUTE keyTemplate[] = { + {CKA_CLASS, &keyClass, sizeof(keyClass)}, + {CKA_KEY_TYPE, &keyType, sizeof(keyType)}, + {CKA_VALUE_LEN, &keyLength, sizeof(keyLength)}, + {CKA_LABEL, keyLabel, sizeof(keyLabel)-1} + }; + + ret = funcList->C_GenerateKey(session, &mechanism, keyTemplate, + sizeof(keyTemplate)/sizeof(CK_ATTRIBUTE), &key); + CHECK_CKR(ret, "NSS PKCS#12 PBE SHA512 HMAC Key Generation"); + + /* Test with wrong mechanism parameter size */ + if (ret == CKR_OK) { + CK_MECHANISM invalidMech = mechanism; + invalidMech.ulParameterLen = sizeof(pbeParams) - 1; /* Wrong size */ + + ret = funcList->C_GenerateKey(session, &invalidMech, keyTemplate, + sizeof(keyTemplate)/sizeof(CK_ATTRIBUTE), &key); + CHECK_CKR_FAIL(ret, CKR_MECHANISM_PARAM_INVALID, + "NSS PKCS#12 PBE SHA512 with wrong parameter size"); + ret = CKR_OK; /* Reset for next test */ + } + + return ret; +} + +/* Test NSS PKCS#12 PBE key generation with different key sizes */ +static CK_RV test_nss_pkcs12_pbe_key_sizes(void* args) +{ + CK_SESSION_HANDLE session = *(CK_SESSION_HANDLE*)args; + CK_RV ret = CKR_OK; + CK_OBJECT_HANDLE key = CK_INVALID_HANDLE; + + /* Test parameters */ + CK_BYTE password[] = "TestPasswordSizes"; + CK_BYTE salt[] = { + 0x8A, 0x2F, 0x3E, 0x91, 0x45, 0x67, 0xBC, 0xDE, + 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0 + }; + CK_ULONG iterations = 10000; + + CK_PBE_PARAMS pbeParams = { + NULL, + password, + strlen((char*)password), + salt, + sizeof(salt), + iterations + }; + + CK_MECHANISM mechanism = { + CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN, + &pbeParams, + sizeof(pbeParams) + }; + + CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY; + CK_KEY_TYPE keyType = CKK_GENERIC_SECRET; + CK_BYTE keyLabel[] = "Test-NSS-PKCS12-PBE-KeySize"; + + /* Test different key sizes */ + CK_ULONG keySizes[] = {16, 24, 32, 48, 64}; /* 128, 192, 256, 384, 512 bits */ + CK_ULONG numSizes = sizeof(keySizes) / sizeof(CK_ULONG); + CK_ULONG i; + + for (i = 0; i < numSizes && ret == CKR_OK; i++) { + CK_ATTRIBUTE keyTemplate[] = { + {CKA_CLASS, &keyClass, sizeof(keyClass)}, + {CKA_KEY_TYPE, &keyType, sizeof(keyType)}, + {CKA_VALUE_LEN, &keySizes[i], sizeof(CK_ULONG)}, + {CKA_LABEL, keyLabel, sizeof(keyLabel)-1} + }; + + ret = funcList->C_GenerateKey(session, &mechanism, keyTemplate, + sizeof(keyTemplate)/sizeof(CK_ATTRIBUTE), &key); + CHECK_CKR(ret, "NSS PKCS#12 PBE with different key sizes"); + } + + return ret; +} +#endif +#endif + +#ifndef NO_HMAC +/* Test PKCS#5 PBKDF2 key generation */ +static CK_RV test_pkcs5_pbkdf2_key_gen(void* args) +{ + CK_SESSION_HANDLE session = *(CK_SESSION_HANDLE*)args; + CK_RV ret = CKR_OK; + CK_OBJECT_HANDLE key = CK_INVALID_HANDLE; + + /* Test parameters */ + CK_BYTE password[] = "TestPassword123"; + CK_ULONG passwordLen = sizeof(password) - 1; + CK_BYTE salt[] = { + 0x8A, 0x2F, 0x3E, 0x91, 0x45, 0x67, 0xBC, 0xDE, + 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0 + }; + CK_ULONG iterations = 10000; + CK_ULONG keyLength = 32; /* 256-bit key */ + + CK_PKCS5_PBKD2_PARAMS pbkdf2Params = { + CKZ_SALT_SPECIFIED, /* saltSource */ + salt, /* pSaltSourceData */ + sizeof(salt), /* ulSaltSourceDataLen */ + iterations, /* iterations */ + CKP_PKCS5_PBKD2_HMAC_SHA256, /* prf */ + NULL, /* pPrfData */ + 0, /* ulPrfDataLen */ + password, /* pPassword */ + &passwordLen /* ulPasswordLen */ + }; + + CK_MECHANISM mechanism = { + CKM_PKCS5_PBKD2, + &pbkdf2Params, + sizeof(pbkdf2Params) + }; + + CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY; + CK_KEY_TYPE keyType = CKK_GENERIC_SECRET; + CK_BYTE keyLabel[] = "Test-PKCS5-PBKDF2-Key"; + + CK_ATTRIBUTE keyTemplate[] = { + {CKA_CLASS, &keyClass, sizeof(keyClass)}, + {CKA_KEY_TYPE, &keyType, sizeof(keyType)}, + {CKA_VALUE_LEN, &keyLength, sizeof(keyLength)}, + {CKA_LABEL, keyLabel, sizeof(keyLabel)-1} + }; + + ret = funcList->C_GenerateKey(session, &mechanism, keyTemplate, + sizeof(keyTemplate)/sizeof(CK_ATTRIBUTE), &key); + CHECK_CKR(ret, "PKCS#5 PBKDF2 Key Generation"); + + /* Test with invalid salt source */ + if (ret == CKR_OK) { + CK_PKCS5_PBKD2_PARAMS invalidParams = pbkdf2Params; + CK_MECHANISM invalidMech = mechanism; + invalidParams.saltSource = 0; /* Invalid salt source */ + invalidMech.pParameter = &invalidParams; + + ret = funcList->C_GenerateKey(session, &invalidMech, keyTemplate, + sizeof(keyTemplate)/sizeof(CK_ATTRIBUTE), &key); + CHECK_CKR_FAIL(ret, CKR_MECHANISM_PARAM_INVALID, + "PKCS#5 PBKDF2 with invalid salt source"); + ret = CKR_OK; /* Reset for next test */ + } + + /* Test with zero iterations */ + if (ret == CKR_OK) { + CK_PKCS5_PBKD2_PARAMS invalidParams = pbkdf2Params; + CK_MECHANISM invalidMech = mechanism; + invalidParams.iterations = 0; + invalidMech.pParameter = &invalidParams; + + ret = funcList->C_GenerateKey(session, &invalidMech, keyTemplate, + sizeof(keyTemplate)/sizeof(CK_ATTRIBUTE), &key); + CHECK_CKR_FAIL(ret, CKR_MECHANISM_PARAM_INVALID, + "PKCS#5 PBKDF2 with zero iterations"); + ret = CKR_OK; /* Reset for next test */ + } + + /* Test with different key types from template */ + if (ret == CKR_OK) { + CK_KEY_TYPE testAesKeyType = CKK_AES; + CK_ATTRIBUTE aesKeyTemplate[] = { + {CKA_CLASS, &keyClass, sizeof(keyClass)}, + {CKA_KEY_TYPE, &testAesKeyType, sizeof(testAesKeyType)}, + {CKA_VALUE_LEN, &keyLength, sizeof(keyLength)}, + {CKA_LABEL, keyLabel, sizeof(keyLabel)-1} + }; + + ret = funcList->C_GenerateKey(session, &mechanism, aesKeyTemplate, + sizeof(aesKeyTemplate)/sizeof(CK_ATTRIBUTE), &key); + CHECK_CKR(ret, "PKCS#5 PBKDF2 Key Generation with CKK_AES"); + + /* Verify the generated key has the correct type */ + if (ret == CKR_OK && key != CK_INVALID_HANDLE) { + CK_KEY_TYPE retrievedKeyType; + CK_ULONG retrievedLen = sizeof(retrievedKeyType); + ret = funcList->C_GetAttributeValue(session, key, + &(CK_ATTRIBUTE){CKA_KEY_TYPE, &retrievedKeyType, retrievedLen}, + 1); + CHECK_CKR(ret, "Get attribute value"); + if (ret == CKR_OK && retrievedKeyType != CKK_AES) { + ret = CKR_GENERAL_ERROR; + CHECK_CKR(ret, "Key type AES"); + } + } + + if (key != CK_INVALID_HANDLE) { + funcList->C_DestroyObject(session, key); + key = CK_INVALID_HANDLE; + } + } + + /* Test with HKDF key type from template */ + if (ret == CKR_OK) { + CK_KEY_TYPE testHkdfKeyType = CKK_HKDF; + CK_ATTRIBUTE hkdfKeyTemplate[] = { + {CKA_CLASS, &keyClass, sizeof(keyClass)}, + {CKA_KEY_TYPE, &testHkdfKeyType, sizeof(testHkdfKeyType)}, + {CKA_VALUE_LEN, &keyLength, sizeof(keyLength)}, + {CKA_LABEL, keyLabel, sizeof(keyLabel)-1} + }; + + ret = funcList->C_GenerateKey(session, &mechanism, hkdfKeyTemplate, + sizeof(hkdfKeyTemplate)/sizeof(CK_ATTRIBUTE), &key); + CHECK_CKR(ret, "PKCS#5 PBKDF2 Key Generation with CKK_HKDF"); + + if (key != CK_INVALID_HANDLE) { + funcList->C_DestroyObject(session, key); + key = CK_INVALID_HANDLE; + } + } + + /* Test with NULL salt */ + if (ret == CKR_OK) { + CK_PKCS5_PBKD2_PARAMS invalidParams = pbkdf2Params; + CK_MECHANISM invalidMech = mechanism; + invalidParams.pSaltSourceData = NULL; + invalidMech.pParameter = &invalidParams; + + ret = funcList->C_GenerateKey(session, &invalidMech, keyTemplate, + sizeof(keyTemplate)/sizeof(CK_ATTRIBUTE), &key); + CHECK_CKR_FAIL(ret, CKR_MECHANISM_PARAM_INVALID, + "PKCS#5 PBKDF2 with NULL salt"); + ret = CKR_OK; /* Reset for next test */ + } + +#ifndef NO_SHA + /* Test different hash algorithms */ + if (ret == CKR_OK) { + CK_PKCS5_PBKD2_PARAMS sha1Params = pbkdf2Params; + CK_MECHANISM sha1Mech = mechanism; + sha1Params.prf = CKP_PKCS5_PBKD2_HMAC_SHA1; + sha1Mech.pParameter = &sha1Params; + + ret = funcList->C_GenerateKey(session, &sha1Mech, keyTemplate, + sizeof(keyTemplate)/sizeof(CK_ATTRIBUTE), &key); + CHECK_CKR(ret, "PKCS#5 PBKDF2 with SHA1"); + } +#endif + + if (ret == CKR_OK) { + CK_PKCS5_PBKD2_PARAMS sha512Params = pbkdf2Params; + CK_MECHANISM sha512Mech = mechanism; + sha512Params.prf = CKP_PKCS5_PBKD2_HMAC_SHA512; + sha512Mech.pParameter = &sha512Params; + + ret = funcList->C_GenerateKey(session, &sha512Mech, keyTemplate, + sizeof(keyTemplate)/sizeof(CK_ATTRIBUTE), &key); + CHECK_CKR(ret, "PKCS#5 PBKDF2 with SHA512"); + } + + return ret; +} #endif static CK_RV test_op_state_fail(void* args) @@ -3661,7 +4130,7 @@ static CK_RV test_data_object_null_value(void* args) /* Test getting CKA_VALUE individually to confirm it's NULL/empty */ if (ret == CKR_OK) { CK_ATTRIBUTE singleAttr = { CKA_VALUE, NULL, 0 }; - + ret = funcList->C_GetAttributeValue(session, dataObj, &singleAttr, 1); CHECK_CKR(ret, "Get single CKA_VALUE attribute for NULL value"); @@ -15012,13 +15481,13 @@ static CK_RV test_nss_email_attribute(void* args) CK_RV ret = CKR_OK; CK_OBJECT_HANDLE obj = CK_INVALID_HANDLE; CK_CERTIFICATE_TYPE certType = CKC_X_509; - + /* Test email address */ static CK_UTF8CHAR test_email[] = "test@wolfssl.com"; static CK_UTF8CHAR label[] = "NSS Email Test Certificate"; static CK_BYTE subject[] = "CN=Test User,O=wolfSSL,C=US"; static CK_BYTE id[] = {0x01, 0x02, 0x03, 0x04, 0x05}; - + /* Minimal X.509 certificate data for testing */ static CK_BYTE certificate[] = { 0x30, 0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, @@ -15026,7 +15495,7 @@ static CK_RV test_nss_email_attribute(void* args) 0x82, 0x01, 0x0F, 0x00, 0x30, 0x82, 0x01, 0x0A, 0x02, 0x82, 0x01, 0x01, 0x00, 0xC0, 0x95, 0x08, 0xE1, 0x57, 0x41, 0xF2 }; - + /* Template for creating the certificate object with NSS email attribute */ CK_ATTRIBUTE tmpl[] = { { CKA_CLASS, &certificateClass, sizeof(certificateClass) }, @@ -15039,23 +15508,23 @@ static CK_RV test_nss_email_attribute(void* args) { CKA_NSS_EMAIL, test_email, sizeof(test_email)-1 } }; CK_ULONG tmplCnt = sizeof(tmpl) / sizeof(*tmpl); - + /* Buffer to retrieve the email attribute */ CK_BYTE emailBuffer[64]; CK_ATTRIBUTE getEmailAttr = { CKA_NSS_EMAIL, emailBuffer, sizeof(emailBuffer) }; - + /* Create the certificate object with NSS email attribute */ ret = funcList->C_CreateObject(session, tmpl, tmplCnt, &obj); CHECK_CKR(ret, "Create Certificate Object with NSS Email"); - + /* Verify the NSS_EMAIL attribute can be retrieved */ if (ret == CKR_OK) { ret = funcList->C_GetAttributeValue(session, obj, &getEmailAttr, 1); CHECK_CKR(ret, "Get NSS_EMAIL attribute"); } - + /* Verify the email value matches what was set */ if (ret == CKR_OK) { if (getEmailAttr.ulValueLen != sizeof(test_email)-1 || @@ -15064,25 +15533,25 @@ static CK_RV test_nss_email_attribute(void* args) CHECK_CKR(ret, "NSS_EMAIL attribute value incorrect"); } } - + /* Test getting the attribute length first (NULL buffer) */ if (ret == CKR_OK) { CK_ATTRIBUTE getLenAttr = { CKA_NSS_EMAIL, NULL, 0 }; ret = funcList->C_GetAttributeValue(session, obj, &getLenAttr, 1); CHECK_CKR(ret, "Get NSS_EMAIL attribute length"); - + if (ret == CKR_OK && getLenAttr.ulValueLen != sizeof(test_email)-1) { ret = -1; CHECK_CKR(ret, "NSS_EMAIL attribute length incorrect"); } } - + /* Clean up - destroy the object */ if (ret == CKR_OK) { ret = funcList->C_DestroyObject(session, obj); CHECK_CKR(ret, "Destroy NSS Email Certificate Object"); } - + return ret; } #endif @@ -15528,6 +15997,16 @@ static TEST_FUNC testFunc[] = { PKCS11TEST_FUNC_SESS_DECL(test_nss_trust_object_token_storage), PKCS11TEST_FUNC_SESS_DECL(test_nss_derive_tls12_master_key), PKCS11TEST_FUNC_SESS_DECL(test_nss_email_attribute), +#ifndef NO_HMAC + PKCS11TEST_FUNC_SESS_DECL(test_nss_pkcs12_pbe_sha224_hmac_key_gen), + PKCS11TEST_FUNC_SESS_DECL(test_nss_pkcs12_pbe_sha256_hmac_key_gen), + PKCS11TEST_FUNC_SESS_DECL(test_nss_pkcs12_pbe_sha384_hmac_key_gen), + PKCS11TEST_FUNC_SESS_DECL(test_nss_pkcs12_pbe_sha512_hmac_key_gen), + PKCS11TEST_FUNC_SESS_DECL(test_nss_pkcs12_pbe_key_sizes), +#endif +#endif +#ifndef NO_HMAC + PKCS11TEST_FUNC_SESS_DECL(test_pkcs5_pbkdf2_key_gen), #endif #endif #ifndef NO_SHA diff --git a/wolfpkcs11/internal.h b/wolfpkcs11/internal.h index 97f37d15..a712c0ed 100644 --- a/wolfpkcs11/internal.h +++ b/wolfpkcs11/internal.h @@ -41,6 +41,12 @@ extern "C" { #ifdef HAVE_FIPS #define NO_MD5 + #define WOLFPKCS11_PBKDF2 +#endif + +/* FIPS has no scrypt and SHA256 is not strong enough */ +#ifndef PBKDF2_ITERATIONS + #define PBKDF2_ITERATIONS 600000 #endif #ifdef WOLFPKCS11_NO_MD5 @@ -611,6 +617,11 @@ WP11_LOCAL int WP11_Aes_Cmac_Verify(unsigned char* data, word32 dataLen, WP11_LOCAL int WP11_Aes_Cmac_Verify_Final(unsigned char* sig, word32 sigLen, int* stat, WP11_Session* session); +WP11_LOCAL int WP11_PBKDF2(byte* output, const byte* passwd, int pLen, + const byte* salt, int sLen, int iterations, int kLen, int hashType); +WP11_LOCAL int WP11_PKCS12_PBKDF(byte* output, const byte* passwd, int pLen, + const byte* salt, int sLen, int iterations, int kLen, int hashType); + WP11_LOCAL int WP11_Hmac_SigLen(WP11_Session* session); WP11_LOCAL int WP11_Hmac_Init(CK_MECHANISM_TYPE mechanism, WP11_Object* secret, WP11_Session* session, CK_ULONG digestSize); diff --git a/wolfpkcs11/pkcs11.h b/wolfpkcs11/pkcs11.h index 48a976f7..334d1f6d 100644 --- a/wolfpkcs11/pkcs11.h +++ b/wolfpkcs11/pkcs11.h @@ -252,6 +252,9 @@ extern "C" { #define CKA_TRUST_STEP_UP_APPROVED (CKA_TRUST + 16) #define CKA_CERT_SHA1_HASH (CKA_TRUST + 100) #define CKA_CERT_MD5_HASH (CKA_TRUST + 101) + +/* A legacy attribute we need to know exists, but skip */ +#define CKA_NSS_DB 0xD5A0DB00L #endif #define CKM_RSA_PKCS_KEY_PAIR_GEN 0x00000000UL @@ -294,6 +297,7 @@ extern "C" { #define CKM_GENERIC_SECRET_KEY_GEN 0x00000350UL #define CKM_SSL3_MASTER_KEY_DERIVE 0x00000371UL #define CKM_TLS_PRF 0x00000378UL +#define CKM_PKCS5_PBKD2 0x000003B0UL #define CKM_TLS12_MASTER_KEY_DERIVE 0x000003E0UL #define CKM_TLS12_KEY_AND_MAC_DERIVE 0x000003E1UL #define CKM_TLS12_MASTER_KEY_DERIVE_DH 0x000003E2UL @@ -328,6 +332,11 @@ extern "C" { #define CKM_NSS_TLS_PRF_GENERAL_SHA256 (CKM_NSS + 21) #define CKM_NSS_TLS_EXTENDED_MASTER_KEY_DERIVE (CKM_NSS + 25) #define CKM_NSS_TLS_EXTENDED_MASTER_KEY_DERIVE_DH (CKM_NSS + 26) + +#define CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN (CKM_NSS + 29) +#define CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN (CKM_NSS + 30) +#define CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN (CKM_NSS + 31) +#define CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN (CKM_NSS + 32) #endif #define CKR_OK 0x00000000UL @@ -428,6 +437,7 @@ extern "C" { #define CKG_MGF1_SHA512 0x00000004UL #define CKZ_DATA_SPECIFIED 0x00000001UL +#define CKZ_SALT_SPECIFIED 0x00000001UL #define CKC_X_509 0x00000000UL #define CKC_X_509_ATTR_CERT 0x00000001UL @@ -716,6 +726,52 @@ typedef struct CK_TLS12_KEY_MAT_PARAMS { CK_MECHANISM_TYPE prfHashMechanism; } CK_TLS12_KEY_MAT_PARAMS; +typedef CK_ULONG CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE; +typedef CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE* CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE_PTR; + +#define CKP_PKCS5_PBKD2_HMAC_SHA1 0x00000001UL +#define CKP_PKCS5_PBKD2_HMAC_GOSTR3411 0x00000002UL /* Not implemented */ +#define CKP_PKCS5_PBKD2_HMAC_SHA224 0x00000003UL +#define CKP_PKCS5_PBKD2_HMAC_SHA256 0x00000004UL +#define CKP_PKCS5_PBKD2_HMAC_SHA384 0x00000005UL +#define CKP_PKCS5_PBKD2_HMAC_SHA512 0x00000006UL +#define CKP_PKCS5_PBKD2_HMAC_SHA512_224 0x00000007UL +#define CKP_PKCS5_PBKD2_HMAC_SHA512_256 0x00000008UL + +typedef CK_ULONG CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE; +typedef CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE* CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE_PTR; + +typedef struct CK_PKCS5_PBKD2_PARAMS { + CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE saltSource; + CK_VOID_PTR pSaltSourceData; + CK_ULONG ulSaltSourceDataLen; + CK_ULONG iterations; + CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE prf; + CK_VOID_PTR pPrfData; + CK_ULONG ulPrfDataLen; + CK_UTF8CHAR_PTR pPassword; + CK_ULONG_PTR ulPasswordLen; +} CK_PKCS5_PBKD2_PARAMS; + +/* ulPasswordLen was an error in the 2.10 spec which was corrected with the + * below in the 2.40 spec. Both need to be supported. */ + +typedef struct CK_PKCS5_PBKD2_PARAMS2 { + CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE saltSource; + CK_VOID_PTR pSaltSourceData; + CK_ULONG ulSaltSourceDataLen; + CK_ULONG iterations; + CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE prf; + CK_VOID_PTR pPrfData; + CK_ULONG ulPrfDataLen; + CK_UTF8CHAR_PTR pPassword; + CK_ULONG ulPasswordLen; +} CK_PKCS5_PBKD2_PARAMS2; + +/* If len is greater than this we assume it is a pointer, and therefore + * CK_PKCS5_PBKD2_PARAMS (NSS does this too) */ +#define CK_PKCS5_PBKD2_PARAMS_MAX_PWD_LEN 8192 + #ifdef WOLFPKCS11_NSS typedef struct CK_NSS_TLS_EXTENDED_MASTER_KEY_DERIVE_PARAMS { CK_MECHANISM_TYPE prfHashMechanism; @@ -723,8 +779,17 @@ typedef struct CK_NSS_TLS_EXTENDED_MASTER_KEY_DERIVE_PARAMS { CK_ULONG ulSessionHashLen; CK_VERSION_PTR pVersion; } CK_NSS_TLS_EXTENDED_MASTER_KEY_DERIVE_PARAMS; -#endif +typedef struct CK_PBE_PARAMS { + CK_BYTE_PTR pInitVector; + CK_UTF8CHAR_PTR pPassword; + CK_ULONG ulPasswordLen; + CK_BYTE_PTR pSalt; + CK_ULONG ulSaltLen; + CK_ULONG ulIteration; +} CK_PBE_PARAMS; +typedef CK_PBE_PARAMS* CK_PBE_PARAMS_PTR; +#endif typedef struct CK_TLS_MAC_PARAMS { CK_MECHANISM_TYPE prfHashMechanism; CK_ULONG ulMacLength;