summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/pk11wrap
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--security/nss/lib/pk11wrap/Makefile58
-rw-r--r--security/nss/lib/pk11wrap/debug_module.c3454
-rw-r--r--security/nss/lib/pk11wrap/dev3hack.c264
-rw-r--r--security/nss/lib/pk11wrap/dev3hack.h30
-rw-r--r--security/nss/lib/pk11wrap/exports.gyp41
-rw-r--r--security/nss/lib/pk11wrap/manifest.mn67
-rw-r--r--security/nss/lib/pk11wrap/pk11akey.c2679
-rw-r--r--security/nss/lib/pk11wrap/pk11auth.c814
-rw-r--r--security/nss/lib/pk11wrap/pk11cert.c2982
-rw-r--r--security/nss/lib/pk11wrap/pk11cxt.c1795
-rw-r--r--security/nss/lib/pk11wrap/pk11err.c141
-rw-r--r--security/nss/lib/pk11wrap/pk11func.h15
-rw-r--r--security/nss/lib/pk11wrap/pk11hpke.c1276
-rw-r--r--security/nss/lib/pk11wrap/pk11hpke.h82
-rw-r--r--security/nss/lib/pk11wrap/pk11kea.c141
-rw-r--r--security/nss/lib/pk11wrap/pk11list.c99
-rw-r--r--security/nss/lib/pk11wrap/pk11load.c715
-rw-r--r--security/nss/lib/pk11wrap/pk11mech.c1952
-rw-r--r--security/nss/lib/pk11wrap/pk11merge.c1437
-rw-r--r--security/nss/lib/pk11wrap/pk11nobj.c807
-rw-r--r--security/nss/lib/pk11wrap/pk11obj.c2285
-rw-r--r--security/nss/lib/pk11wrap/pk11pars.c2158
-rw-r--r--security/nss/lib/pk11wrap/pk11pbe.c1486
-rw-r--r--security/nss/lib/pk11wrap/pk11pk12.c829
-rw-r--r--security/nss/lib/pk11wrap/pk11pqg.c522
-rw-r--r--security/nss/lib/pk11wrap/pk11pqg.h135
-rw-r--r--security/nss/lib/pk11wrap/pk11priv.h210
-rw-r--r--security/nss/lib/pk11wrap/pk11pub.h1054
-rw-r--r--security/nss/lib/pk11wrap/pk11sdr.c437
-rw-r--r--security/nss/lib/pk11wrap/pk11sdr.h28
-rw-r--r--security/nss/lib/pk11wrap/pk11skey.c3047
-rw-r--r--security/nss/lib/pk11wrap/pk11slot.c2787
-rw-r--r--security/nss/lib/pk11wrap/pk11util.c1746
-rw-r--r--security/nss/lib/pk11wrap/pk11wrap.gyp71
-rw-r--r--security/nss/lib/pk11wrap/secmod.h194
-rw-r--r--security/nss/lib/pk11wrap/secmodi.h174
-rw-r--r--security/nss/lib/pk11wrap/secmodt.h448
-rw-r--r--security/nss/lib/pk11wrap/secmodti.h205
-rw-r--r--security/nss/lib/pk11wrap/secpkcs5.h61
39 files changed, 36726 insertions, 0 deletions
diff --git a/security/nss/lib/pk11wrap/Makefile b/security/nss/lib/pk11wrap/Makefile
new file mode 100644
index 0000000000..9d27595ca1
--- /dev/null
+++ b/security/nss/lib/pk11wrap/Makefile
@@ -0,0 +1,58 @@
+#! gmake
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#######################################################################
+# (1) Include initial platform-independent assignments (MANDATORY). #
+#######################################################################
+
+include manifest.mn
+
+#######################################################################
+# (2) Include "global" configuration information. (OPTIONAL) #
+#######################################################################
+
+include $(CORE_DEPTH)/coreconf/config.mk
+
+#######################################################################
+# (3) Include "component" configuration information. (OPTIONAL) #
+#######################################################################
+
+
+
+#######################################################################
+# (4) Include "local" platform-dependent assignments (OPTIONAL). #
+#######################################################################
+
+#######################################################################
+# (5) Execute "global" rules. (OPTIONAL) #
+#######################################################################
+
+include $(CORE_DEPTH)/coreconf/rules.mk
+
+#######################################################################
+# (6) Execute "component" rules. (OPTIONAL) #
+#######################################################################
+
+
+
+#######################################################################
+# (7) Execute "local" rules. (OPTIONAL). #
+#######################################################################
+
+
+$(OBJDIR)/pk11load$(OBJ_SUFFIX): debug_module.c
+
+# On AIX 4.3, IBM xlC_r compiler (version 3.6.6) cannot compile
+# pk11slot.c in 64-bit mode for unknown reasons. A workaround is
+# to compile it with optimizations turned on. (Bugzilla bug #63815)
+ifeq ($(OS_TARGET)$(OS_RELEASE),AIX4.3)
+ifeq ($(USE_64),1)
+ifndef BUILD_OPT
+$(OBJDIR)/pk11slot.o: pk11slot.c | $$(@D)/d
+ $(CC) -o $@ -c -O2 $(CFLAGS) $<
+endif
+endif
+endif
diff --git a/security/nss/lib/pk11wrap/debug_module.c b/security/nss/lib/pk11wrap/debug_module.c
new file mode 100644
index 0000000000..6f0662c7af
--- /dev/null
+++ b/security/nss/lib/pk11wrap/debug_module.c
@@ -0,0 +1,3454 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "prlog.h"
+#include <stdio.h>
+#include "cert.h" /* for CERT_DerNameToAscii & CERT_Hexify */
+
+static PRLogModuleInfo *modlog = NULL;
+
+static CK_FUNCTION_LIST_3_0_PTR module_functions;
+
+static CK_FUNCTION_LIST_3_0 debug_functions;
+
+static void print_final_statistics(void);
+
+#define STRING static const char
+STRING fmt_flags[] = " flags = 0x%x";
+STRING fmt_hKey[] = " hKey = 0x%x";
+STRING fmt_hObject[] = " hObject = 0x%x";
+STRING fmt_hSession[] = " hSession = 0x%x";
+STRING fmt_manufacturerID[] = " manufacturerID = \"%.32s\"";
+STRING fmt_pAssociatedData[] = " pAssociatedData = 0x%p";
+STRING fmt_pCiphertext[] = " pCiphertext = 0x%p";
+STRING fmt_pCiphertextPart[] = " pCiphertextPart = 0x%p";
+STRING fmt_pData[] = " pData = 0x%p";
+STRING fmt_pDigest[] = " pDigest = 0x%p";
+STRING fmt_pEncryptedData[] = " pEncryptedData = 0x%p";
+STRING fmt_pEncryptedPart[] = " pEncryptedPart = 0x%p";
+STRING fmt_pInfo[] = " pInfo = 0x%p";
+STRING fmt_pMechanism[] = " pMechanism = 0x%p";
+STRING fmt_pOperationState[] = " pOperationState = 0x%p";
+STRING fmt_pParameter[] = " pParameter = 0x%p";
+STRING fmt_pPart[] = " pPart = 0x%p";
+STRING fmt_pPlaintext[] = " pPlaintext = 0x%p";
+STRING fmt_pPlaintextPart[] = " pPlaintextPart = 0x%p";
+STRING fmt_pPin[] = " pPin = 0x%p";
+STRING fmt_pSignature[] = " pSignature = 0x%p";
+STRING fmt_pTemplate[] = " pTemplate = 0x%p";
+STRING fmt_pWrappedKey[] = " pWrappedKey = 0x%p";
+STRING fmt_phKey[] = " phKey = 0x%p";
+STRING fmt_phObject[] = " phObject = 0x%p";
+STRING fmt_pulCount[] = " pulCount = 0x%p";
+STRING fmt_pulCiphertextLen[] = " pulCiphertextLen = 0x%p";
+STRING fmt_pulCiphertextPartLen[] = " pulCiphertextPartLen = 0x%p";
+STRING fmt_pulDataLen[] = " pulDataLen = 0x%p";
+STRING fmt_pulDigestLen[] = " pulDigestLen = 0x%p";
+STRING fmt_pulEncryptedPartLen[] = " pulEncryptedPartLen = 0x%p";
+STRING fmt_pulPartLen[] = " pulPartLen = 0x%p";
+STRING fmt_pulPlaintextLen[] = " pulPlaintextLen = 0x%p";
+STRING fmt_pulPlaintextPartLen[] = " pulPlaintextPartLen = 0x%p";
+STRING fmt_pulSignatureLen[] = " pulSignatureLen = 0x%p";
+STRING fmt_slotID[] = " slotID = 0x%x";
+STRING fmt_sphKey[] = " *phKey = 0x%x";
+STRING fmt_spulCount[] = " *pulCount = 0x%x";
+STRING fmt_spulDataLen[] = " *pulDataLen = 0x%x";
+STRING fmt_spulDigestLen[] = " *pulDigestLen = 0x%x";
+STRING fmt_spulEncryptedPartLen[] = " *pulEncryptedPartLen = 0x%x";
+STRING fmt_spulPartLen[] = " *pulPartLen = 0x%x";
+STRING fmt_spulSignatureLen[] = " *pulSignatureLen = 0x%x";
+STRING fmt_ulAttributeCount[] = " ulAttributeCount = %d";
+STRING fmt_ulCiphertextLen[] = " ulCiphertextLen = %d";
+STRING fmt_ulCiphertextPartLen[] = " ulCiphertextPartLen = %d";
+STRING fmt_ulCount[] = " ulCount = %d";
+STRING fmt_ulDataLen[] = " ulDataLen = %d";
+STRING fmt_ulEncryptedPartLen[] = " ulEncryptedPartLen = %d";
+STRING fmt_ulAssociatedDataLen[] = " ulAssociatedDataLen = 0x%p";
+STRING fmt_ulParameterLen[] = " ulParameterLen = 0x%p";
+STRING fmt_ulPartLen[] = " ulPartLen = %d";
+STRING fmt_ulPlaintextLen[] = " ulPlaintextLen = 0x%p";
+STRING fmt_ulPlaintextPartLen[] = " ulPlaintextPartLen = 0x%p";
+STRING fmt_ulPinLen[] = " ulPinLen = %d";
+STRING fmt_ulSignatureLen[] = " ulSignatureLen = %d";
+
+STRING fmt_fwVersion[] = " firmware version: %d.%d";
+STRING fmt_hwVersion[] = " hardware version: %d.%d";
+STRING fmt_s_qsq_d[] = " %s = \"%s\" [%d]";
+STRING fmt_s_s_d[] = " %s = %s [%d]";
+STRING fmt_s_lu[] = " %s = %lu";
+STRING fmt_invalid_handle[] = " (CK_INVALID_HANDLE)";
+
+static void
+get_attr_type_str(CK_ATTRIBUTE_TYPE atype, char *str, int len)
+{
+#define CASE(attr) \
+ case attr: \
+ a = #attr; \
+ break
+
+ const char *a = NULL;
+
+ switch (atype) {
+ CASE(CKA_CLASS);
+ CASE(CKA_TOKEN);
+ CASE(CKA_PRIVATE);
+ CASE(CKA_LABEL);
+ CASE(CKA_APPLICATION);
+ CASE(CKA_VALUE);
+ CASE(CKA_OBJECT_ID);
+ CASE(CKA_CERTIFICATE_TYPE);
+ CASE(CKA_CERTIFICATE_CATEGORY);
+ CASE(CKA_ISSUER);
+ CASE(CKA_SERIAL_NUMBER);
+ CASE(CKA_AC_ISSUER);
+ CASE(CKA_OWNER);
+ CASE(CKA_ATTR_TYPES);
+ CASE(CKA_TRUSTED);
+ CASE(CKA_KEY_TYPE);
+ CASE(CKA_SUBJECT);
+ CASE(CKA_ID);
+ CASE(CKA_SENSITIVE);
+ CASE(CKA_ENCRYPT);
+ CASE(CKA_DECRYPT);
+ CASE(CKA_WRAP);
+ CASE(CKA_UNWRAP);
+ CASE(CKA_SIGN);
+ CASE(CKA_SIGN_RECOVER);
+ CASE(CKA_VERIFY);
+ CASE(CKA_VERIFY_RECOVER);
+ CASE(CKA_DERIVE);
+ CASE(CKA_START_DATE);
+ CASE(CKA_END_DATE);
+ CASE(CKA_MODULUS);
+ CASE(CKA_MODULUS_BITS);
+ CASE(CKA_PUBLIC_EXPONENT);
+ CASE(CKA_PRIVATE_EXPONENT);
+ CASE(CKA_PRIME_1);
+ CASE(CKA_PRIME_2);
+ CASE(CKA_EXPONENT_1);
+ CASE(CKA_EXPONENT_2);
+ CASE(CKA_COEFFICIENT);
+ CASE(CKA_PRIME);
+ CASE(CKA_SUBPRIME);
+ CASE(CKA_BASE);
+ CASE(CKA_PRIME_BITS);
+ CASE(CKA_SUBPRIME_BITS);
+ CASE(CKA_VALUE_BITS);
+ CASE(CKA_VALUE_LEN);
+ CASE(CKA_EXTRACTABLE);
+ CASE(CKA_LOCAL);
+ CASE(CKA_NEVER_EXTRACTABLE);
+ CASE(CKA_ALWAYS_SENSITIVE);
+ CASE(CKA_KEY_GEN_MECHANISM);
+ CASE(CKA_MODIFIABLE);
+ CASE(CKA_ECDSA_PARAMS);
+ CASE(CKA_EC_POINT);
+ CASE(CKA_SECONDARY_AUTH);
+ CASE(CKA_AUTH_PIN_FLAGS);
+ CASE(CKA_HW_FEATURE_TYPE);
+ CASE(CKA_RESET_ON_INIT);
+ CASE(CKA_HAS_RESET);
+ CASE(CKA_VENDOR_DEFINED);
+ CASE(CKA_PROFILE_ID);
+ CASE(CKA_NSS_URL);
+ CASE(CKA_NSS_EMAIL);
+ CASE(CKA_NSS_SMIME_INFO);
+ CASE(CKA_NSS_SMIME_TIMESTAMP);
+ CASE(CKA_NSS_PKCS8_SALT);
+ CASE(CKA_NSS_PASSWORD_CHECK);
+ CASE(CKA_NSS_EXPIRES);
+ CASE(CKA_NSS_KRL);
+ CASE(CKA_NSS_PQG_COUNTER);
+ CASE(CKA_NSS_PQG_SEED);
+ CASE(CKA_NSS_PQG_H);
+ CASE(CKA_NSS_PQG_SEED_BITS);
+ CASE(CKA_TRUST);
+ CASE(CKA_TRUST_DIGITAL_SIGNATURE);
+ CASE(CKA_TRUST_NON_REPUDIATION);
+ CASE(CKA_TRUST_KEY_ENCIPHERMENT);
+ CASE(CKA_TRUST_DATA_ENCIPHERMENT);
+ CASE(CKA_TRUST_KEY_AGREEMENT);
+ CASE(CKA_TRUST_KEY_CERT_SIGN);
+ CASE(CKA_TRUST_CRL_SIGN);
+ CASE(CKA_TRUST_SERVER_AUTH);
+ CASE(CKA_TRUST_CLIENT_AUTH);
+ CASE(CKA_TRUST_CODE_SIGNING);
+ CASE(CKA_TRUST_EMAIL_PROTECTION);
+ CASE(CKA_TRUST_IPSEC_END_SYSTEM);
+ CASE(CKA_TRUST_IPSEC_TUNNEL);
+ CASE(CKA_TRUST_IPSEC_USER);
+ CASE(CKA_TRUST_TIME_STAMPING);
+ CASE(CKA_CERT_SHA1_HASH);
+ CASE(CKA_CERT_MD5_HASH);
+ CASE(CKA_NSS_DB);
+ CASE(CKA_NSS_TRUST);
+ default:
+ break;
+ }
+ if (a)
+ PR_snprintf(str, len, "%s", a);
+ else
+ PR_snprintf(str, len, "0x%p", atype);
+}
+
+static void
+get_obj_class(CK_OBJECT_CLASS objClass, char *str, int len)
+{
+
+ const char *a = NULL;
+
+ switch (objClass) {
+ CASE(CKO_DATA);
+ CASE(CKO_CERTIFICATE);
+ CASE(CKO_PUBLIC_KEY);
+ CASE(CKO_PRIVATE_KEY);
+ CASE(CKO_SECRET_KEY);
+ CASE(CKO_HW_FEATURE);
+ CASE(CKO_DOMAIN_PARAMETERS);
+ CASE(CKO_PROFILE);
+ CASE(CKO_NSS_CRL);
+ CASE(CKO_NSS_SMIME);
+ CASE(CKO_NSS_TRUST);
+ CASE(CKO_NSS_BUILTIN_ROOT_LIST);
+ default:
+ break;
+ }
+ if (a)
+ PR_snprintf(str, len, "%s", a);
+ else
+ PR_snprintf(str, len, "0x%p", objClass);
+}
+
+static void
+get_profile_val(CK_PROFILE_ID profile, char *str, int len)
+{
+
+ const char *a = NULL;
+
+ switch (profile) {
+ CASE(CKP_INVALID_ID);
+ CASE(CKP_BASELINE_PROVIDER);
+ CASE(CKP_EXTENDED_PROVIDER);
+ CASE(CKP_AUTHENTICATION_TOKEN);
+ CASE(CKP_PUBLIC_CERTIFICATES_TOKEN);
+ default:
+ break;
+ }
+ if (a)
+ PR_snprintf(str, len, "%s", a);
+ else
+ PR_snprintf(str, len, "0x%p", profile);
+}
+
+static void
+get_trust_val(CK_TRUST trust, char *str, int len)
+{
+ const char *a = NULL;
+
+ switch (trust) {
+ CASE(CKT_NSS_TRUSTED);
+ CASE(CKT_NSS_TRUSTED_DELEGATOR);
+ CASE(CKT_NSS_NOT_TRUSTED);
+ CASE(CKT_NSS_MUST_VERIFY_TRUST);
+ CASE(CKT_NSS_TRUST_UNKNOWN);
+ CASE(CKT_NSS_VALID_DELEGATOR);
+ default:
+ break;
+ }
+ if (a)
+ PR_snprintf(str, len, "%s", a);
+ else
+ PR_snprintf(str, len, "0x%p", trust);
+}
+
+static void
+log_rv(CK_RV rv)
+{
+ const char *a = NULL;
+
+ switch (rv) {
+ CASE(CKR_OK);
+ CASE(CKR_CANCEL);
+ CASE(CKR_HOST_MEMORY);
+ CASE(CKR_SLOT_ID_INVALID);
+ CASE(CKR_GENERAL_ERROR);
+ CASE(CKR_FUNCTION_FAILED);
+ CASE(CKR_ARGUMENTS_BAD);
+ CASE(CKR_NO_EVENT);
+ CASE(CKR_NEED_TO_CREATE_THREADS);
+ CASE(CKR_CANT_LOCK);
+ CASE(CKR_ATTRIBUTE_READ_ONLY);
+ CASE(CKR_ATTRIBUTE_SENSITIVE);
+ CASE(CKR_ATTRIBUTE_TYPE_INVALID);
+ CASE(CKR_ATTRIBUTE_VALUE_INVALID);
+ CASE(CKR_DATA_INVALID);
+ CASE(CKR_DATA_LEN_RANGE);
+ CASE(CKR_DEVICE_ERROR);
+ CASE(CKR_DEVICE_MEMORY);
+ CASE(CKR_DEVICE_REMOVED);
+ CASE(CKR_ENCRYPTED_DATA_INVALID);
+ CASE(CKR_ENCRYPTED_DATA_LEN_RANGE);
+ CASE(CKR_FUNCTION_CANCELED);
+ CASE(CKR_FUNCTION_NOT_PARALLEL);
+ CASE(CKR_FUNCTION_NOT_SUPPORTED);
+ CASE(CKR_KEY_HANDLE_INVALID);
+ CASE(CKR_KEY_SIZE_RANGE);
+ CASE(CKR_KEY_TYPE_INCONSISTENT);
+ CASE(CKR_KEY_NOT_NEEDED);
+ CASE(CKR_KEY_CHANGED);
+ CASE(CKR_KEY_NEEDED);
+ CASE(CKR_KEY_INDIGESTIBLE);
+ CASE(CKR_KEY_FUNCTION_NOT_PERMITTED);
+ CASE(CKR_KEY_NOT_WRAPPABLE);
+ CASE(CKR_KEY_UNEXTRACTABLE);
+ CASE(CKR_MECHANISM_INVALID);
+ CASE(CKR_MECHANISM_PARAM_INVALID);
+ CASE(CKR_OBJECT_HANDLE_INVALID);
+ CASE(CKR_OPERATION_ACTIVE);
+ CASE(CKR_OPERATION_NOT_INITIALIZED);
+ CASE(CKR_PIN_INCORRECT);
+ CASE(CKR_PIN_INVALID);
+ CASE(CKR_PIN_LEN_RANGE);
+ CASE(CKR_PIN_EXPIRED);
+ CASE(CKR_PIN_LOCKED);
+ CASE(CKR_SESSION_CLOSED);
+ CASE(CKR_SESSION_COUNT);
+ CASE(CKR_SESSION_HANDLE_INVALID);
+ CASE(CKR_SESSION_PARALLEL_NOT_SUPPORTED);
+ CASE(CKR_SESSION_READ_ONLY);
+ CASE(CKR_SESSION_EXISTS);
+ CASE(CKR_SESSION_READ_ONLY_EXISTS);
+ CASE(CKR_SESSION_READ_WRITE_SO_EXISTS);
+ CASE(CKR_SIGNATURE_INVALID);
+ CASE(CKR_SIGNATURE_LEN_RANGE);
+ CASE(CKR_TEMPLATE_INCOMPLETE);
+ CASE(CKR_TEMPLATE_INCONSISTENT);
+ CASE(CKR_TOKEN_NOT_PRESENT);
+ CASE(CKR_TOKEN_NOT_RECOGNIZED);
+ CASE(CKR_TOKEN_WRITE_PROTECTED);
+ CASE(CKR_UNWRAPPING_KEY_HANDLE_INVALID);
+ CASE(CKR_UNWRAPPING_KEY_SIZE_RANGE);
+ CASE(CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT);
+ CASE(CKR_USER_ALREADY_LOGGED_IN);
+ CASE(CKR_USER_NOT_LOGGED_IN);
+ CASE(CKR_USER_PIN_NOT_INITIALIZED);
+ CASE(CKR_USER_TYPE_INVALID);
+ CASE(CKR_USER_ANOTHER_ALREADY_LOGGED_IN);
+ CASE(CKR_USER_TOO_MANY_TYPES);
+ CASE(CKR_WRAPPED_KEY_INVALID);
+ CASE(CKR_WRAPPED_KEY_LEN_RANGE);
+ CASE(CKR_WRAPPING_KEY_HANDLE_INVALID);
+ CASE(CKR_WRAPPING_KEY_SIZE_RANGE);
+ CASE(CKR_WRAPPING_KEY_TYPE_INCONSISTENT);
+ CASE(CKR_RANDOM_SEED_NOT_SUPPORTED);
+ CASE(CKR_RANDOM_NO_RNG);
+ CASE(CKR_DOMAIN_PARAMS_INVALID);
+ CASE(CKR_BUFFER_TOO_SMALL);
+ CASE(CKR_SAVED_STATE_INVALID);
+ CASE(CKR_INFORMATION_SENSITIVE);
+ CASE(CKR_STATE_UNSAVEABLE);
+ CASE(CKR_CRYPTOKI_NOT_INITIALIZED);
+ CASE(CKR_CRYPTOKI_ALREADY_INITIALIZED);
+ CASE(CKR_MUTEX_BAD);
+ CASE(CKR_MUTEX_NOT_LOCKED);
+ CASE(CKR_FUNCTION_REJECTED);
+ CASE(CKR_ACTION_PROHIBITED);
+ CASE(CKR_CURVE_NOT_SUPPORTED);
+ CASE(CKR_NEW_PIN_MODE);
+ CASE(CKR_NEXT_OTP);
+ CASE(CKR_EXCEEDED_MAX_ITERATIONS);
+ CASE(CKR_FIPS_SELF_TEST_FAILED);
+ CASE(CKR_LIBRARY_LOAD_FAILED);
+ CASE(CKR_PIN_TOO_WEAK);
+ CASE(CKR_TOKEN_RESOURCE_EXCEEDED);
+ CASE(CKR_OPERATION_CANCEL_FAILED);
+ default:
+ break;
+ }
+ if (a)
+ PR_LOG(modlog, 1, (" rv = %s\n", a));
+ else
+ PR_LOG(modlog, 1, (" rv = 0x%x\n", rv));
+}
+
+static void
+log_state(CK_STATE state)
+{
+ const char *a = NULL;
+
+ switch (state) {
+ CASE(CKS_RO_PUBLIC_SESSION);
+ CASE(CKS_RO_USER_FUNCTIONS);
+ CASE(CKS_RW_PUBLIC_SESSION);
+ CASE(CKS_RW_USER_FUNCTIONS);
+ CASE(CKS_RW_SO_FUNCTIONS);
+ default:
+ break;
+ }
+ if (a)
+ PR_LOG(modlog, 1, (" state = %s\n", a));
+ else
+ PR_LOG(modlog, 1, (" state = 0x%x\n", state));
+}
+
+static void
+log_handle(PRLogModuleLevel level, const char *format, CK_ULONG handle)
+{
+ char fmtBuf[80];
+ if (handle)
+ PR_LOG(modlog, level, (format, handle));
+ else {
+ PL_strncpyz(fmtBuf, format, sizeof fmtBuf);
+ PL_strcatn(fmtBuf, sizeof fmtBuf, fmt_invalid_handle);
+ PR_LOG(modlog, level, (fmtBuf, handle));
+ }
+}
+
+static void
+print_mechanism(CK_MECHANISM_PTR m)
+{
+
+ const char *a = NULL;
+
+ switch (m->mechanism) {
+ CASE(CKM_AES_CBC);
+ CASE(CKM_AES_CBC_ENCRYPT_DATA);
+ CASE(CKM_AES_CBC_PAD);
+ CASE(CKM_AES_CCM);
+ CASE(CKM_AES_CTR);
+ CASE(CKM_AES_CTS);
+ CASE(CKM_AES_GCM);
+ CASE(CKM_AES_ECB);
+ CASE(CKM_AES_ECB_ENCRYPT_DATA);
+ CASE(CKM_AES_KEY_GEN);
+ CASE(CKM_AES_MAC);
+ CASE(CKM_AES_MAC_GENERAL);
+ CASE(CKM_AES_CMAC);
+ CASE(CKM_AES_CMAC_GENERAL);
+ CASE(CKM_CAMELLIA_CBC);
+ CASE(CKM_CAMELLIA_CBC_ENCRYPT_DATA);
+ CASE(CKM_CAMELLIA_CBC_PAD);
+ CASE(CKM_CAMELLIA_ECB);
+ CASE(CKM_CAMELLIA_ECB_ENCRYPT_DATA);
+ CASE(CKM_CAMELLIA_KEY_GEN);
+ CASE(CKM_CAMELLIA_MAC);
+ CASE(CKM_CAMELLIA_MAC_GENERAL);
+ CASE(CKM_CHACHA20_KEY_GEN);
+ CASE(CKM_CHACHA20);
+ CASE(CKM_CDMF_CBC);
+ CASE(CKM_CDMF_CBC_PAD);
+ CASE(CKM_CDMF_ECB);
+ CASE(CKM_CDMF_KEY_GEN);
+ CASE(CKM_CDMF_MAC);
+ CASE(CKM_CDMF_MAC_GENERAL);
+ CASE(CKM_CMS_SIG);
+ CASE(CKM_CONCATENATE_BASE_AND_DATA);
+ CASE(CKM_CONCATENATE_BASE_AND_KEY);
+ CASE(CKM_CONCATENATE_DATA_AND_BASE);
+ CASE(CKM_DES2_KEY_GEN);
+ CASE(CKM_DES3_CBC);
+ CASE(CKM_DES3_CBC_ENCRYPT_DATA);
+ CASE(CKM_DES3_CBC_PAD);
+ CASE(CKM_DES3_ECB);
+ CASE(CKM_DES3_ECB_ENCRYPT_DATA);
+ CASE(CKM_DES3_KEY_GEN);
+ CASE(CKM_DES3_MAC);
+ CASE(CKM_DES3_MAC_GENERAL);
+ CASE(CKM_DES_CBC);
+ CASE(CKM_DES_CBC_ENCRYPT_DATA);
+ CASE(CKM_DES_CBC_PAD);
+ CASE(CKM_DES_CFB64);
+ CASE(CKM_DES_CFB8);
+ CASE(CKM_DES_ECB);
+ CASE(CKM_DES_ECB_ENCRYPT_DATA);
+ CASE(CKM_DES_KEY_GEN);
+ CASE(CKM_DES_MAC);
+ CASE(CKM_DES_MAC_GENERAL);
+ CASE(CKM_DES_OFB64);
+ CASE(CKM_DES_OFB8);
+ CASE(CKM_DH_PKCS_DERIVE);
+ CASE(CKM_DH_PKCS_KEY_PAIR_GEN);
+ CASE(CKM_DH_PKCS_PARAMETER_GEN);
+ CASE(CKM_DSA);
+ CASE(CKM_DSA_KEY_PAIR_GEN);
+ CASE(CKM_DSA_PARAMETER_GEN);
+ CASE(CKM_DSA_SHA1);
+ CASE(CKM_ECDH1_COFACTOR_DERIVE);
+ CASE(CKM_ECDH1_DERIVE);
+ CASE(CKM_ECDSA);
+ CASE(CKM_ECDSA_SHA1);
+ CASE(CKM_ECMQV_DERIVE);
+ CASE(CKM_EC_KEY_PAIR_GEN); /* also CASE(CKM_ECDSA_KEY_PAIR_GEN); */
+ CASE(CKM_EXTRACT_KEY_FROM_KEY);
+ CASE(CKM_ECDSA_SHA224);
+ CASE(CKM_ECDSA_SHA256);
+ CASE(CKM_ECDSA_SHA384);
+ CASE(CKM_ECDSA_SHA512);
+ CASE(CKM_EC_KEY_PAIR_GEN_W_EXTRA_BITS);
+ CASE(CKM_FASTHASH);
+ CASE(CKM_FORTEZZA_TIMESTAMP);
+ CASE(CKM_GENERIC_SECRET_KEY_GEN);
+ CASE(CKM_IDEA_CBC);
+ CASE(CKM_IDEA_CBC_PAD);
+ CASE(CKM_IDEA_ECB);
+ CASE(CKM_IDEA_KEY_GEN);
+ CASE(CKM_IDEA_MAC);
+ CASE(CKM_IDEA_MAC_GENERAL);
+ CASE(CKM_KEA_KEY_DERIVE);
+ CASE(CKM_KEA_KEY_PAIR_GEN);
+ CASE(CKM_KEY_WRAP_LYNKS);
+ CASE(CKM_KEY_WRAP_SET_OAEP);
+ CASE(CKM_MD2);
+ CASE(CKM_MD2_HMAC);
+ CASE(CKM_MD2_HMAC_GENERAL);
+ CASE(CKM_MD2_KEY_DERIVATION);
+ CASE(CKM_MD2_RSA_PKCS);
+ CASE(CKM_MD5);
+ CASE(CKM_MD5_HMAC);
+ CASE(CKM_MD5_HMAC_GENERAL);
+ CASE(CKM_MD5_KEY_DERIVATION);
+ CASE(CKM_MD5_RSA_PKCS);
+ CASE(CKM_PBA_SHA1_WITH_SHA1_HMAC);
+ CASE(CKM_PBE_MD2_DES_CBC);
+ CASE(CKM_PBE_MD5_DES_CBC);
+ CASE(CKM_PBE_SHA1_DES2_EDE_CBC);
+ CASE(CKM_PBE_SHA1_DES3_EDE_CBC);
+ CASE(CKM_PBE_SHA1_RC2_128_CBC);
+ CASE(CKM_PBE_SHA1_RC2_40_CBC);
+ CASE(CKM_PBE_SHA1_RC4_128);
+ CASE(CKM_PBE_SHA1_RC4_40);
+ CASE(CKM_PKCS5_PBKD2);
+ CASE(CKM_POLY1305_KEY_GEN);
+ CASE(CKM_POLY1305);
+ CASE(CKM_RC2_CBC);
+ CASE(CKM_RC2_CBC_PAD);
+ CASE(CKM_RC2_ECB);
+ CASE(CKM_RC2_KEY_GEN);
+ CASE(CKM_RC2_MAC);
+ CASE(CKM_RC2_MAC_GENERAL);
+ CASE(CKM_RC4);
+ CASE(CKM_RC4_KEY_GEN);
+ CASE(CKM_RC5_CBC);
+ CASE(CKM_RC5_CBC_PAD);
+ CASE(CKM_RC5_ECB);
+ CASE(CKM_RC5_KEY_GEN);
+ CASE(CKM_RC5_MAC);
+ CASE(CKM_RC5_MAC_GENERAL);
+ CASE(CKM_RIPEMD128);
+ CASE(CKM_RIPEMD128_HMAC);
+ CASE(CKM_RIPEMD128_HMAC_GENERAL);
+ CASE(CKM_RIPEMD128_RSA_PKCS);
+ CASE(CKM_RIPEMD160);
+ CASE(CKM_RIPEMD160_HMAC);
+ CASE(CKM_RIPEMD160_HMAC_GENERAL);
+ CASE(CKM_RIPEMD160_RSA_PKCS);
+ CASE(CKM_RSA_9796);
+ CASE(CKM_RSA_PKCS);
+ CASE(CKM_RSA_PKCS_KEY_PAIR_GEN);
+ CASE(CKM_RSA_PKCS_OAEP);
+ CASE(CKM_RSA_PKCS_PSS);
+ CASE(CKM_RSA_X9_31);
+ CASE(CKM_RSA_X9_31_KEY_PAIR_GEN);
+ CASE(CKM_RSA_X_509);
+ CASE(CKM_SHA1_KEY_DERIVATION);
+ CASE(CKM_SHA1_RSA_PKCS);
+ CASE(CKM_SHA1_RSA_PKCS_PSS);
+ CASE(CKM_SHA1_RSA_X9_31);
+ CASE(CKM_SHA224);
+ CASE(CKM_SHA224_HMAC);
+ CASE(CKM_SHA224_HMAC_GENERAL);
+ CASE(CKM_SHA224_KEY_DERIVATION);
+ CASE(CKM_SHA224_RSA_PKCS);
+ CASE(CKM_SHA224_RSA_PKCS_PSS);
+ CASE(CKM_SHA256);
+ CASE(CKM_SHA256_HMAC);
+ CASE(CKM_SHA256_HMAC_GENERAL);
+ CASE(CKM_SHA256_KEY_DERIVATION);
+ CASE(CKM_SHA256_RSA_PKCS);
+ CASE(CKM_SHA256_RSA_PKCS_PSS);
+ CASE(CKM_SHA384);
+ CASE(CKM_SHA384_HMAC);
+ CASE(CKM_SHA384_HMAC_GENERAL);
+ CASE(CKM_SHA384_KEY_DERIVATION);
+ CASE(CKM_SHA384_RSA_PKCS);
+ CASE(CKM_SHA384_RSA_PKCS_PSS);
+ CASE(CKM_SHA512);
+ CASE(CKM_SHA512_HMAC);
+ CASE(CKM_SHA512_HMAC_GENERAL);
+ CASE(CKM_SHA512_KEY_DERIVATION);
+ CASE(CKM_SHA512_RSA_PKCS);
+ CASE(CKM_SHA512_RSA_PKCS_PSS);
+ CASE(CKM_SHA_1);
+ CASE(CKM_SHA_1_HMAC);
+ CASE(CKM_SHA_1_HMAC_GENERAL);
+ CASE(CKM_SKIPJACK_CBC64);
+ CASE(CKM_SKIPJACK_CFB16);
+ CASE(CKM_SKIPJACK_CFB32);
+ CASE(CKM_SKIPJACK_CFB64);
+ CASE(CKM_SKIPJACK_CFB8);
+ CASE(CKM_SKIPJACK_ECB64);
+ CASE(CKM_SKIPJACK_KEY_GEN);
+ CASE(CKM_SKIPJACK_OFB64);
+ CASE(CKM_SKIPJACK_PRIVATE_WRAP);
+ CASE(CKM_SKIPJACK_RELAYX);
+ CASE(CKM_SKIPJACK_WRAP);
+ CASE(CKM_SSL3_KEY_AND_MAC_DERIVE);
+ CASE(CKM_SSL3_MASTER_KEY_DERIVE);
+ CASE(CKM_SSL3_MASTER_KEY_DERIVE_DH);
+ CASE(CKM_SSL3_MD5_MAC);
+ CASE(CKM_SSL3_PRE_MASTER_KEY_GEN);
+ CASE(CKM_SSL3_SHA1_MAC);
+ CASE(CKM_TLS_KEY_AND_MAC_DERIVE);
+ CASE(CKM_TLS_MASTER_KEY_DERIVE);
+ CASE(CKM_TLS_MASTER_KEY_DERIVE_DH);
+ CASE(CKM_TLS_PRE_MASTER_KEY_GEN);
+ CASE(CKM_TLS_PRF);
+ CASE(CKM_TWOFISH_CBC);
+ CASE(CKM_TWOFISH_KEY_GEN);
+ CASE(CKM_X9_42_DH_DERIVE);
+ CASE(CKM_X9_42_DH_HYBRID_DERIVE);
+ CASE(CKM_X9_42_DH_KEY_PAIR_GEN);
+ CASE(CKM_X9_42_DH_PARAMETER_GEN);
+ CASE(CKM_X9_42_MQV_DERIVE);
+ CASE(CKM_XOR_BASE_AND_DATA);
+ default:
+ break;
+ }
+ if (a)
+ PR_LOG(modlog, 4, (" mechanism = %s", a));
+ else
+ PR_LOG(modlog, 4, (" mechanism = 0x%p", m->mechanism));
+}
+
+static void
+get_key_type(CK_KEY_TYPE keyType, char *str, int len)
+{
+
+ const char *a = NULL;
+
+ switch (keyType) {
+ CASE(CKK_AES);
+ CASE(CKK_CAMELLIA);
+ CASE(CKK_CDMF);
+ CASE(CKK_DES);
+ CASE(CKK_DES2);
+ CASE(CKK_DES3);
+ CASE(CKK_DH);
+ CASE(CKK_DSA);
+ CASE(CKK_EC); /* also CASE(CKK_ECDSA); */
+ CASE(CKK_GENERIC_SECRET);
+ CASE(CKK_IDEA);
+ CASE(CKK_INVALID_KEY_TYPE);
+ CASE(CKK_KEA);
+ CASE(CKK_RC2);
+ CASE(CKK_RC4);
+ CASE(CKK_RC5);
+ CASE(CKK_RSA);
+ CASE(CKK_SKIPJACK);
+ CASE(CKK_TWOFISH);
+ CASE(CKK_X9_42_DH);
+ CASE(CKK_MD5_HMAC);
+ CASE(CKK_SHA_1_HMAC);
+ CASE(CKK_RIPEMD128_HMAC);
+ CASE(CKK_RIPEMD160_HMAC);
+ CASE(CKK_SHA256_HMAC);
+ CASE(CKK_SHA384_HMAC);
+ CASE(CKK_SHA512_HMAC);
+ CASE(CKK_SHA224_HMAC);
+ CASE(CKK_GOSTR3410);
+ CASE(CKK_GOSTR3411);
+ CASE(CKK_GOST28147);
+ CASE(CKK_CHACHA20);
+ CASE(CKK_POLY1305);
+ CASE(CKK_AES_XTS);
+ CASE(CKK_SHA3_224_HMAC);
+ CASE(CKK_SHA3_256_HMAC);
+ CASE(CKK_SHA3_384_HMAC);
+ CASE(CKK_SHA3_512_HMAC);
+ CASE(CKK_BLAKE2B_160_HMAC);
+ CASE(CKK_BLAKE2B_256_HMAC);
+ CASE(CKK_BLAKE2B_384_HMAC);
+ CASE(CKK_BLAKE2B_512_HMAC);
+ CASE(CKK_SALSA20);
+ CASE(CKK_X2RATCHET);
+ CASE(CKK_EC_EDWARDS);
+ CASE(CKK_EC_MONTGOMERY);
+ CASE(CKK_HKDF);
+ CASE(CKK_SHA512_224_HMAC);
+ CASE(CKK_SHA512_256_HMAC);
+ CASE(CKK_SHA512_T_HMAC);
+ default:
+ break;
+ }
+ if (a)
+ PR_snprintf(str, len, "%s", a);
+ else
+ PR_snprintf(str, len, "0x%p", keyType);
+}
+
+static void
+print_attr_value(CK_ATTRIBUTE_PTR attr)
+{
+ char atype[48];
+ char valstr[49];
+ int len;
+
+ get_attr_type_str(attr->type, atype, sizeof atype);
+ switch (attr->type) {
+ case CKA_ALWAYS_SENSITIVE:
+ case CKA_DECRYPT:
+ case CKA_DERIVE:
+ case CKA_ENCRYPT:
+ case CKA_EXTRACTABLE:
+ case CKA_LOCAL:
+ case CKA_MODIFIABLE:
+ case CKA_NEVER_EXTRACTABLE:
+ case CKA_PRIVATE:
+ case CKA_SENSITIVE:
+ case CKA_SIGN:
+ case CKA_SIGN_RECOVER:
+ case CKA_TOKEN:
+ case CKA_UNWRAP:
+ case CKA_VERIFY:
+ case CKA_VERIFY_RECOVER:
+ case CKA_WRAP:
+ if (attr->ulValueLen > 0 && attr->pValue) {
+ CK_BBOOL tf = *((CK_BBOOL *)attr->pValue);
+ PR_LOG(modlog, 4, (fmt_s_s_d, atype, tf ? "CK_TRUE" : "CK_FALSE", attr->ulValueLen));
+ break;
+ }
+ case CKA_CLASS:
+ if (attr->ulValueLen > 0 && attr->pValue) {
+ CK_OBJECT_CLASS objClass = *((CK_OBJECT_CLASS *)attr->pValue);
+ get_obj_class(objClass, valstr, sizeof valstr);
+ PR_LOG(modlog, 4, (fmt_s_s_d, atype, valstr, attr->ulValueLen));
+ break;
+ }
+ case CKA_TRUST_CLIENT_AUTH:
+ case CKA_TRUST_CODE_SIGNING:
+ case CKA_TRUST_EMAIL_PROTECTION:
+ case CKA_TRUST_SERVER_AUTH:
+ if (attr->ulValueLen > 0 && attr->pValue) {
+ CK_TRUST trust = *((CK_TRUST *)attr->pValue);
+ get_trust_val(trust, valstr, sizeof valstr);
+ PR_LOG(modlog, 4, (fmt_s_s_d, atype, valstr, attr->ulValueLen));
+ break;
+ }
+ case CKA_KEY_TYPE:
+ if (attr->ulValueLen > 0 && attr->pValue) {
+ CK_KEY_TYPE keyType = *((CK_KEY_TYPE *)attr->pValue);
+ get_key_type(keyType, valstr, sizeof valstr);
+ PR_LOG(modlog, 4, (fmt_s_s_d, atype, valstr, attr->ulValueLen));
+ break;
+ }
+ case CKA_PIXEL_X:
+ case CKA_PIXEL_Y:
+ case CKA_RESOLUTION:
+ case CKA_CHAR_ROWS:
+ case CKA_CHAR_COLUMNS:
+ case CKA_BITS_PER_PIXEL:
+ case CKA_CERTIFICATE_CATEGORY: /* should print as enum/string */
+ case CKA_JAVA_MIDP_SECURITY_DOMAIN: /* should print as enum/string */
+ case CKA_MODULUS_BITS:
+ case CKA_PRIME_BITS:
+ case CKA_SUBPRIME_BITS:
+ case CKA_VALUE_BITS:
+ case CKA_VALUE_LEN:
+ if (attr->ulValueLen > 0 && attr->pValue) {
+ CK_ULONG valueLen = *((CK_ULONG *)attr->pValue);
+ /* XXX check for the special value CK_UNAVAILABLE_INFORMATION */
+ PR_LOG(modlog, 4, (fmt_s_lu, atype, (PRUint32)valueLen));
+ break;
+ }
+ case CKA_LABEL:
+ case CKA_NSS_EMAIL:
+ case CKA_NSS_URL:
+ if (attr->ulValueLen > 0 && attr->pValue) {
+ len = PR_MIN(attr->ulValueLen + 1, sizeof valstr);
+ PR_snprintf(valstr, len, "%s", attr->pValue);
+ PR_LOG(modlog, 4, (fmt_s_qsq_d, atype, valstr, attr->ulValueLen));
+ break;
+ }
+ case CKA_PROFILE_ID:
+ if (attr->ulValueLen > 0 && attr->pValue) {
+ CK_PROFILE_ID profile = *((CK_PROFILE_ID *)attr->pValue);
+ get_profile_val(profile, valstr, sizeof valstr);
+ PR_LOG(modlog, 4, (fmt_s_s_d, atype, valstr, attr->ulValueLen));
+ break;
+ }
+ case CKA_ISSUER:
+ case CKA_SUBJECT:
+ if (attr->ulValueLen > 0 && attr->pValue) {
+ char *asciiName;
+ SECItem derName;
+ derName.type = siDERNameBuffer;
+ derName.data = attr->pValue;
+ derName.len = attr->ulValueLen;
+ asciiName = CERT_DerNameToAscii(&derName);
+ if (asciiName) {
+ PR_LOG(modlog, 4, (fmt_s_s_d, atype, asciiName, attr->ulValueLen));
+ PORT_Free(asciiName);
+ break;
+ }
+ /* else treat like a binary buffer */
+ goto binary_buffer;
+ }
+ case CKA_ID:
+ if (attr->ulValueLen > 0 && attr->pValue) {
+ unsigned char *pV = attr->pValue;
+ for (len = (int)attr->ulValueLen; len > 0; --len) {
+ unsigned int ch = *pV++;
+ if (ch >= 0x20 && ch < 0x7f)
+ continue;
+ if (!ch && len == 1) /* will ignore NUL if last character */
+ continue;
+ break;
+ }
+ if (!len) { /* entire string is printable */
+ len = PR_MIN(attr->ulValueLen + 1, sizeof valstr);
+ PR_snprintf(valstr, len, "%s", attr->pValue);
+ PR_LOG(modlog, 4, (fmt_s_qsq_d, atype, valstr, attr->ulValueLen));
+ break;
+ }
+ /* else fall through and treat like a binary buffer */
+ }
+ binary_buffer:
+ case CKA_SERIAL_NUMBER:
+ default:
+ if (attr->ulValueLen > 0 && attr->pValue) {
+ char *hexBuf;
+ SECItem attrBuf;
+ attrBuf.type = siDERNameBuffer;
+ attrBuf.data = attr->pValue;
+ attrBuf.len = PR_MIN(attr->ulValueLen, (sizeof valstr) / 2);
+
+ hexBuf = CERT_Hexify(&attrBuf, PR_FALSE);
+ if (hexBuf) {
+ PR_LOG(modlog, 4, (fmt_s_s_d, atype, hexBuf, attr->ulValueLen));
+ PORT_Free(hexBuf);
+ break;
+ }
+ /* else fall through and show only the address. :( */
+ }
+ PR_LOG(modlog, 4, (" %s = [0x%p] [%d]", atype, attr->pValue, attr->ulValueLen));
+ break;
+ }
+}
+
+static void
+print_template(CK_ATTRIBUTE_PTR templ, CK_ULONG tlen)
+{
+ CK_ULONG i;
+ for (i = 0; i < tlen; i++) {
+ print_attr_value(&templ[i]);
+ }
+}
+
+struct nssdbg_prof_str {
+ PRUint32 time;
+ PRUint32 calls;
+ char *function;
+};
+
+#define NSSDBG_DEFINE(func) \
+ { \
+ 0, 0, #func \
+ }
+
+struct nssdbg_prof_str nssdbg_prof_data[] = {
+#define FUNC_C_INITIALIZE 0
+ NSSDBG_DEFINE(C_Initialize),
+#define FUNC_C_FINALIZE 1
+ NSSDBG_DEFINE(C_Finalize),
+#define FUNC_C_GETINFO 2
+ NSSDBG_DEFINE(C_GetInfo),
+#define FUNC_C_GETFUNCITONLIST 3
+ NSSDBG_DEFINE(C_GetFunctionList),
+#define FUNC_C_GETSLOTLIST 4
+ NSSDBG_DEFINE(C_GetSlotList),
+#define FUNC_C_GETSLOTINFO 5
+ NSSDBG_DEFINE(C_GetSlotInfo),
+#define FUNC_C_GETTOKENINFO 6
+ NSSDBG_DEFINE(C_GetTokenInfo),
+#define FUNC_C_GETMECHANISMLIST 7
+ NSSDBG_DEFINE(C_GetMechanismList),
+#define FUNC_C_GETMECHANISMINFO 8
+ NSSDBG_DEFINE(C_GetMechanismInfo),
+#define FUNC_C_INITTOKEN 9
+ NSSDBG_DEFINE(C_InitToken),
+#define FUNC_C_INITPIN 10
+ NSSDBG_DEFINE(C_InitPIN),
+#define FUNC_C_SETPIN 11
+ NSSDBG_DEFINE(C_SetPIN),
+#define FUNC_C_OPENSESSION 12
+ NSSDBG_DEFINE(C_OpenSession),
+#define FUNC_C_CLOSESESSION 13
+ NSSDBG_DEFINE(C_CloseSession),
+#define FUNC_C_CLOSEALLSESSIONS 14
+ NSSDBG_DEFINE(C_CloseAllSessions),
+#define FUNC_C_GETSESSIONINFO 15
+ NSSDBG_DEFINE(C_GetSessionInfo),
+#define FUNC_C_GETOPERATIONSTATE 16
+ NSSDBG_DEFINE(C_GetOperationState),
+#define FUNC_C_SETOPERATIONSTATE 17
+ NSSDBG_DEFINE(C_SetOperationState),
+#define FUNC_C_LOGIN 18
+ NSSDBG_DEFINE(C_Login),
+#define FUNC_C_LOGOUT 19
+ NSSDBG_DEFINE(C_Logout),
+#define FUNC_C_CREATEOBJECT 20
+ NSSDBG_DEFINE(C_CreateObject),
+#define FUNC_C_COPYOBJECT 21
+ NSSDBG_DEFINE(C_CopyObject),
+#define FUNC_C_DESTROYOBJECT 22
+ NSSDBG_DEFINE(C_DestroyObject),
+#define FUNC_C_GETOBJECTSIZE 23
+ NSSDBG_DEFINE(C_GetObjectSize),
+#define FUNC_C_GETATTRIBUTEVALUE 24
+ NSSDBG_DEFINE(C_GetAttributeValue),
+#define FUNC_C_SETATTRIBUTEVALUE 25
+ NSSDBG_DEFINE(C_SetAttributeValue),
+#define FUNC_C_FINDOBJECTSINIT 26
+ NSSDBG_DEFINE(C_FindObjectsInit),
+#define FUNC_C_FINDOBJECTS 27
+ NSSDBG_DEFINE(C_FindObjects),
+#define FUNC_C_FINDOBJECTSFINAL 28
+ NSSDBG_DEFINE(C_FindObjectsFinal),
+#define FUNC_C_ENCRYPTINIT 29
+ NSSDBG_DEFINE(C_EncryptInit),
+#define FUNC_C_ENCRYPT 30
+ NSSDBG_DEFINE(C_Encrypt),
+#define FUNC_C_ENCRYPTUPDATE 31
+ NSSDBG_DEFINE(C_EncryptUpdate),
+#define FUNC_C_ENCRYPTFINAL 32
+ NSSDBG_DEFINE(C_EncryptFinal),
+#define FUNC_C_DECRYPTINIT 33
+ NSSDBG_DEFINE(C_DecryptInit),
+#define FUNC_C_DECRYPT 34
+ NSSDBG_DEFINE(C_Decrypt),
+#define FUNC_C_DECRYPTUPDATE 35
+ NSSDBG_DEFINE(C_DecryptUpdate),
+#define FUNC_C_DECRYPTFINAL 36
+ NSSDBG_DEFINE(C_DecryptFinal),
+#define FUNC_C_DIGESTINIT 37
+ NSSDBG_DEFINE(C_DigestInit),
+#define FUNC_C_DIGEST 38
+ NSSDBG_DEFINE(C_Digest),
+#define FUNC_C_DIGESTUPDATE 39
+ NSSDBG_DEFINE(C_DigestUpdate),
+#define FUNC_C_DIGESTKEY 40
+ NSSDBG_DEFINE(C_DigestKey),
+#define FUNC_C_DIGESTFINAL 41
+ NSSDBG_DEFINE(C_DigestFinal),
+#define FUNC_C_SIGNINIT 42
+ NSSDBG_DEFINE(C_SignInit),
+#define FUNC_C_SIGN 43
+ NSSDBG_DEFINE(C_Sign),
+#define FUNC_C_SIGNUPDATE 44
+ NSSDBG_DEFINE(C_SignUpdate),
+#define FUNC_C_SIGNFINAL 45
+ NSSDBG_DEFINE(C_SignFinal),
+#define FUNC_C_SIGNRECOVERINIT 46
+ NSSDBG_DEFINE(C_SignRecoverInit),
+#define FUNC_C_SIGNRECOVER 47
+ NSSDBG_DEFINE(C_SignRecover),
+#define FUNC_C_VERIFYINIT 48
+ NSSDBG_DEFINE(C_VerifyInit),
+#define FUNC_C_VERIFY 49
+ NSSDBG_DEFINE(C_Verify),
+#define FUNC_C_VERIFYUPDATE 50
+ NSSDBG_DEFINE(C_VerifyUpdate),
+#define FUNC_C_VERIFYFINAL 51
+ NSSDBG_DEFINE(C_VerifyFinal),
+#define FUNC_C_VERIFYRECOVERINIT 52
+ NSSDBG_DEFINE(C_VerifyRecoverInit),
+#define FUNC_C_VERIFYRECOVER 53
+ NSSDBG_DEFINE(C_VerifyRecover),
+#define FUNC_C_DIGESTENCRYPTUPDATE 54
+ NSSDBG_DEFINE(C_DigestEncryptUpdate),
+#define FUNC_C_DECRYPTDIGESTUPDATE 55
+ NSSDBG_DEFINE(C_DecryptDigestUpdate),
+#define FUNC_C_SIGNENCRYPTUPDATE 56
+ NSSDBG_DEFINE(C_SignEncryptUpdate),
+#define FUNC_C_DECRYPTVERIFYUPDATE 57
+ NSSDBG_DEFINE(C_DecryptVerifyUpdate),
+#define FUNC_C_GENERATEKEY 58
+ NSSDBG_DEFINE(C_GenerateKey),
+#define FUNC_C_GENERATEKEYPAIR 59
+ NSSDBG_DEFINE(C_GenerateKeyPair),
+#define FUNC_C_WRAPKEY 60
+ NSSDBG_DEFINE(C_WrapKey),
+#define FUNC_C_UNWRAPKEY 61
+ NSSDBG_DEFINE(C_UnWrapKey),
+#define FUNC_C_DERIVEKEY 62
+ NSSDBG_DEFINE(C_DeriveKey),
+#define FUNC_C_SEEDRANDOM 63
+ NSSDBG_DEFINE(C_SeedRandom),
+#define FUNC_C_GENERATERANDOM 64
+ NSSDBG_DEFINE(C_GenerateRandom),
+#define FUNC_C_GETFUNCTIONSTATUS 65
+ NSSDBG_DEFINE(C_GetFunctionStatus),
+#define FUNC_C_CANCELFUNCTION 66
+ NSSDBG_DEFINE(C_CancelFunction),
+#define FUNC_C_WAITFORSLOTEVENT 67
+ NSSDBG_DEFINE(C_WaitForSlotEvent),
+#define FUNC_C_GETINTERFACELIST 68
+ NSSDBG_DEFINE(C_GetInterfaceList),
+#define FUNC_C_GETINTERFACE 69
+ NSSDBG_DEFINE(C_GetInterface),
+#define FUNC_C_LOGINUSER 70
+ NSSDBG_DEFINE(C_LoginUser),
+#define FUNC_C_SESSIONCANCEL 71
+ NSSDBG_DEFINE(C_SessionCancel),
+#define FUNC_C_MESSAGEENCRYPTINIT 72
+ NSSDBG_DEFINE(C_MessageEncryptInit),
+#define FUNC_C_ENCRYPTMESSAGE 73
+ NSSDBG_DEFINE(C_EncryptMessage),
+#define FUNC_C_ENCRYPTMESSAGEBEGIN 74
+ NSSDBG_DEFINE(C_EncryptMessageBegin),
+#define FUNC_C_ENCRYPTMESSAGENEXT 75
+ NSSDBG_DEFINE(C_EncryptMessageNext),
+#define FUNC_C_MESSAGEENCRYPTFINAL 76
+ NSSDBG_DEFINE(C_MessageEncryptFinal),
+#define FUNC_C_MESSAGEDECRYPTINIT 77
+ NSSDBG_DEFINE(C_MessageDecryptInit),
+#define FUNC_C_DECRYPTMESSAGE 78
+ NSSDBG_DEFINE(C_DecryptMessage),
+#define FUNC_C_DECRYPTMESSAGEBEGIN 79
+ NSSDBG_DEFINE(C_DecryptMessageBegin),
+#define FUNC_C_DECRYPTMESSAGENEXT 80
+ NSSDBG_DEFINE(C_DecryptMessageNext),
+#define FUNC_C_MESSAGEDECRYPTFINAL 81
+ NSSDBG_DEFINE(C_MessageDecryptFinal),
+#define FUNC_C_MESSAGESIGNINIT 82
+ NSSDBG_DEFINE(C_MessageSignInit),
+#define FUNC_C_SIGNMESSAGE 83
+ NSSDBG_DEFINE(C_SignMessage),
+#define FUNC_C_SIGNMESSAGEBEGIN 84
+ NSSDBG_DEFINE(C_SignMessageBegin),
+#define FUNC_C_SIGNMESSAGENEXT 85
+ NSSDBG_DEFINE(C_SignMessageNext),
+#define FUNC_C_MESSAGESIGNFINAL 86
+ NSSDBG_DEFINE(C_MessageSignFinal),
+#define FUNC_C_MESSAGEVERIFYINIT 87
+ NSSDBG_DEFINE(C_MessageVerifyInit),
+#define FUNC_C_VERIFYMESSAGE 88
+ NSSDBG_DEFINE(C_VerifyMessage),
+#define FUNC_C_VERIFYMESSAGEBEGIN 89
+ NSSDBG_DEFINE(C_VerifyMessageBegin),
+#define FUNC_C_VERIFYMESSAGENEXT 90
+ NSSDBG_DEFINE(C_VerifyMessageNext),
+#define FUNC_C_MESSAGEVERIFYFINAL 91
+ NSSDBG_DEFINE(C_MessageVerifyFinal)
+};
+
+int nssdbg_prof_size = sizeof(nssdbg_prof_data) / sizeof(nssdbg_prof_data[0]);
+
+static void
+nssdbg_finish_time(PRInt32 fun_number, PRIntervalTime start)
+{
+ PRIntervalTime ival;
+ PRIntervalTime end = PR_IntervalNow();
+
+ ival = end - start;
+ /* sigh, lie to PRAtomic add and say we are using signed values */
+ PR_ATOMIC_ADD((PRInt32 *)&nssdbg_prof_data[fun_number].time, (PRInt32)ival);
+}
+
+static void
+nssdbg_start_time(PRInt32 fun_number, PRIntervalTime *start)
+{
+ PR_ATOMIC_INCREMENT((PRInt32 *)&nssdbg_prof_data[fun_number].calls);
+ *start = PR_IntervalNow();
+}
+
+#define COMMON_DEFINITIONS \
+ CK_RV rv; \
+ PRIntervalTime start
+
+CK_RV
+NSSDBGC_Initialize(
+ CK_VOID_PTR pInitArgs)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_Initialize"));
+ PR_LOG(modlog, 3, (" pInitArgs = 0x%p", pInitArgs));
+ nssdbg_start_time(FUNC_C_INITIALIZE, &start);
+ rv = module_functions->C_Initialize(pInitArgs);
+ nssdbg_finish_time(FUNC_C_INITIALIZE, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_Finalize(
+ CK_VOID_PTR pReserved)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_Finalize"));
+ PR_LOG(modlog, 3, (" pReserved = 0x%p", pReserved));
+ nssdbg_start_time(FUNC_C_FINALIZE, &start);
+ rv = module_functions->C_Finalize(pReserved);
+ nssdbg_finish_time(FUNC_C_FINALIZE, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_GetInfo(
+ CK_INFO_PTR pInfo)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_GetInfo"));
+ PR_LOG(modlog, 3, (fmt_pInfo, pInfo));
+ nssdbg_start_time(FUNC_C_GETINFO, &start);
+ rv = module_functions->C_GetInfo(pInfo);
+ nssdbg_finish_time(FUNC_C_GETINFO, start);
+ if (rv == CKR_OK) {
+ PR_LOG(modlog, 4, (" cryptoki version: %d.%d", pInfo->cryptokiVersion.major, pInfo->cryptokiVersion.minor));
+ PR_LOG(modlog, 4, (fmt_manufacturerID, pInfo->manufacturerID));
+ PR_LOG(modlog, 4, (" library description = \"%.32s\"", pInfo->libraryDescription));
+ PR_LOG(modlog, 4, (" library version: %d.%d", pInfo->libraryVersion.major, pInfo->libraryVersion.minor));
+ }
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_GetFunctionList(
+ CK_FUNCTION_LIST_PTR_PTR ppFunctionList)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_GetFunctionList"));
+ PR_LOG(modlog, 3, (" ppFunctionList = 0x%p", ppFunctionList));
+ nssdbg_start_time(FUNC_C_GETFUNCITONLIST, &start);
+ rv = module_functions->C_GetFunctionList(ppFunctionList);
+ nssdbg_finish_time(FUNC_C_GETFUNCITONLIST, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_GetSlotList(
+ CK_BBOOL tokenPresent,
+ CK_SLOT_ID_PTR pSlotList,
+ CK_ULONG_PTR pulCount)
+{
+ COMMON_DEFINITIONS;
+
+ CK_ULONG i;
+ PR_LOG(modlog, 1, ("C_GetSlotList"));
+ PR_LOG(modlog, 3, (" tokenPresent = 0x%x", tokenPresent));
+ PR_LOG(modlog, 3, (" pSlotList = 0x%p", pSlotList));
+ PR_LOG(modlog, 3, (fmt_pulCount, pulCount));
+ nssdbg_start_time(FUNC_C_GETSLOTLIST, &start);
+ rv = module_functions->C_GetSlotList(tokenPresent, pSlotList, pulCount);
+ nssdbg_finish_time(FUNC_C_GETSLOTLIST, start);
+ PR_LOG(modlog, 4, (fmt_spulCount, *pulCount));
+ if (pSlotList) {
+ for (i = 0; i < *pulCount; i++) {
+ PR_LOG(modlog, 4, (" slotID[%d] = %x", i, pSlotList[i]));
+ }
+ }
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_GetSlotInfo(
+ CK_SLOT_ID slotID,
+ CK_SLOT_INFO_PTR pInfo)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_GetSlotInfo"));
+ PR_LOG(modlog, 3, (fmt_slotID, slotID));
+ PR_LOG(modlog, 3, (fmt_pInfo, pInfo));
+ nssdbg_start_time(FUNC_C_GETSLOTINFO, &start);
+ rv = module_functions->C_GetSlotInfo(slotID, pInfo);
+ nssdbg_finish_time(FUNC_C_GETSLOTINFO, start);
+ if (rv == CKR_OK) {
+ PR_LOG(modlog, 4, (" slotDescription = \"%.64s\"", pInfo->slotDescription));
+ PR_LOG(modlog, 4, (fmt_manufacturerID, pInfo->manufacturerID));
+ PR_LOG(modlog, 4, (" flags = %s %s %s", pInfo->flags & CKF_HW_SLOT ? "CKF_HW_SLOT" : "", pInfo->flags & CKF_REMOVABLE_DEVICE ? "CKF_REMOVABLE_DEVICE" : "", pInfo->flags & CKF_TOKEN_PRESENT ? "CKF_TOKEN_PRESENT" : ""));
+ PR_LOG(modlog, 4, (fmt_hwVersion, pInfo->hardwareVersion.major, pInfo->hardwareVersion.minor));
+ PR_LOG(modlog, 4, (fmt_fwVersion, pInfo->firmwareVersion.major, pInfo->firmwareVersion.minor));
+ }
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_GetTokenInfo(
+ CK_SLOT_ID slotID,
+ CK_TOKEN_INFO_PTR pInfo)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_GetTokenInfo"));
+ PR_LOG(modlog, 3, (fmt_slotID, slotID));
+ PR_LOG(modlog, 3, (fmt_pInfo, pInfo));
+ nssdbg_start_time(FUNC_C_GETTOKENINFO, &start);
+ rv = module_functions->C_GetTokenInfo(slotID, pInfo);
+ nssdbg_finish_time(FUNC_C_GETTOKENINFO, start);
+ if (rv == CKR_OK) {
+ PR_LOG(modlog, 4, (" label = \"%.32s\"", pInfo->label));
+ PR_LOG(modlog, 4, (fmt_manufacturerID, pInfo->manufacturerID));
+ PR_LOG(modlog, 4, (" model = \"%.16s\"", pInfo->model));
+ PR_LOG(modlog, 4, (" serial = \"%.16s\"", pInfo->serialNumber));
+ PR_LOG(modlog, 4, (" flags = %s %s %s %s", pInfo->flags & CKF_RNG ? "CKF_RNG" : "", pInfo->flags & CKF_WRITE_PROTECTED ? "CKF_WRITE_PROTECTED" : "", pInfo->flags & CKF_LOGIN_REQUIRED ? "CKF_LOGIN_REQUIRED" : "", pInfo->flags & CKF_USER_PIN_INITIALIZED ? "CKF_USER_PIN_INIT" : ""));
+ PR_LOG(modlog, 4, (" maxSessions = %u, Sessions = %u", pInfo->ulMaxSessionCount, pInfo->ulSessionCount));
+ PR_LOG(modlog, 4, (" maxRwSessions = %u, RwSessions = %u", pInfo->ulMaxRwSessionCount, pInfo->ulRwSessionCount));
+ /* ignore Max & Min Pin Len, Public and Private Memory */
+ PR_LOG(modlog, 4, (fmt_hwVersion, pInfo->hardwareVersion.major, pInfo->hardwareVersion.minor));
+ PR_LOG(modlog, 4, (fmt_fwVersion, pInfo->firmwareVersion.major, pInfo->firmwareVersion.minor));
+ }
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_GetMechanismList(
+ CK_SLOT_ID slotID,
+ CK_MECHANISM_TYPE_PTR pMechanismList,
+ CK_ULONG_PTR pulCount)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_GetMechanismList"));
+ PR_LOG(modlog, 3, (fmt_slotID, slotID));
+ PR_LOG(modlog, 3, (" pMechanismList = 0x%p", pMechanismList));
+ PR_LOG(modlog, 3, (fmt_pulCount, pulCount));
+ nssdbg_start_time(FUNC_C_GETMECHANISMLIST, &start);
+ rv = module_functions->C_GetMechanismList(slotID,
+ pMechanismList,
+ pulCount);
+ nssdbg_finish_time(FUNC_C_GETMECHANISMLIST, start);
+ PR_LOG(modlog, 4, (fmt_spulCount, *pulCount));
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_GetMechanismInfo(
+ CK_SLOT_ID slotID,
+ CK_MECHANISM_TYPE type,
+ CK_MECHANISM_INFO_PTR pInfo)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_GetMechanismInfo"));
+ PR_LOG(modlog, 3, (fmt_slotID, slotID));
+ PR_LOG(modlog, 3, (" type = 0x%x", type));
+ PR_LOG(modlog, 3, (fmt_pInfo, pInfo));
+ nssdbg_start_time(FUNC_C_GETMECHANISMINFO, &start);
+ rv = module_functions->C_GetMechanismInfo(slotID,
+ type,
+ pInfo);
+ nssdbg_finish_time(FUNC_C_GETMECHANISMINFO, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_InitToken(
+ CK_SLOT_ID slotID,
+ CK_CHAR_PTR pPin,
+ CK_ULONG ulPinLen,
+ CK_CHAR_PTR pLabel)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_InitToken"));
+ PR_LOG(modlog, 3, (fmt_slotID, slotID));
+ PR_LOG(modlog, 3, (fmt_pPin, pPin));
+ PR_LOG(modlog, 3, (fmt_ulPinLen, ulPinLen));
+ PR_LOG(modlog, 3, (" pLabel = 0x%p", pLabel));
+ nssdbg_start_time(FUNC_C_INITTOKEN, &start);
+ rv = module_functions->C_InitToken(slotID,
+ pPin,
+ ulPinLen,
+ pLabel);
+ nssdbg_finish_time(FUNC_C_INITTOKEN, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_InitPIN(
+ CK_SESSION_HANDLE hSession,
+ CK_CHAR_PTR pPin,
+ CK_ULONG ulPinLen)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_InitPIN"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pPin, pPin));
+ PR_LOG(modlog, 3, (fmt_ulPinLen, ulPinLen));
+ nssdbg_start_time(FUNC_C_INITPIN, &start);
+ rv = module_functions->C_InitPIN(hSession,
+ pPin,
+ ulPinLen);
+ nssdbg_finish_time(FUNC_C_INITPIN, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_SetPIN(
+ CK_SESSION_HANDLE hSession,
+ CK_CHAR_PTR pOldPin,
+ CK_ULONG ulOldLen,
+ CK_CHAR_PTR pNewPin,
+ CK_ULONG ulNewLen)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_SetPIN"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (" pOldPin = 0x%p", pOldPin));
+ PR_LOG(modlog, 3, (" ulOldLen = %d", ulOldLen));
+ PR_LOG(modlog, 3, (" pNewPin = 0x%p", pNewPin));
+ PR_LOG(modlog, 3, (" ulNewLen = %d", ulNewLen));
+ nssdbg_start_time(FUNC_C_SETPIN, &start);
+ rv = module_functions->C_SetPIN(hSession,
+ pOldPin,
+ ulOldLen,
+ pNewPin,
+ ulNewLen);
+ nssdbg_finish_time(FUNC_C_SETPIN, start);
+ log_rv(rv);
+ return rv;
+}
+
+static PRUint32 numOpenSessions = 0;
+static PRUint32 maxOpenSessions = 0;
+
+CK_RV
+NSSDBGC_OpenSession(
+ CK_SLOT_ID slotID,
+ CK_FLAGS flags,
+ CK_VOID_PTR pApplication,
+ CK_NOTIFY Notify,
+ CK_SESSION_HANDLE_PTR phSession)
+{
+ COMMON_DEFINITIONS;
+
+ PR_ATOMIC_INCREMENT((PRInt32 *)&numOpenSessions);
+ maxOpenSessions = PR_MAX(numOpenSessions, maxOpenSessions);
+ PR_LOG(modlog, 1, ("C_OpenSession"));
+ PR_LOG(modlog, 3, (fmt_slotID, slotID));
+ PR_LOG(modlog, 3, (fmt_flags, flags));
+ PR_LOG(modlog, 3, (" pApplication = 0x%p", pApplication));
+ PR_LOG(modlog, 3, (" Notify = 0x%x", Notify));
+ PR_LOG(modlog, 3, (" phSession = 0x%p", phSession));
+ nssdbg_start_time(FUNC_C_OPENSESSION, &start);
+ rv = module_functions->C_OpenSession(slotID,
+ flags,
+ pApplication,
+ Notify,
+ phSession);
+ nssdbg_finish_time(FUNC_C_OPENSESSION, start);
+ log_handle(4, " *phSession = 0x%x", *phSession);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_CloseSession(
+ CK_SESSION_HANDLE hSession)
+{
+ COMMON_DEFINITIONS;
+
+ PR_ATOMIC_DECREMENT((PRInt32 *)&numOpenSessions);
+ PR_LOG(modlog, 1, ("C_CloseSession"));
+ log_handle(3, fmt_hSession, hSession);
+ nssdbg_start_time(FUNC_C_CLOSESESSION, &start);
+ rv = module_functions->C_CloseSession(hSession);
+ nssdbg_finish_time(FUNC_C_CLOSESESSION, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_CloseAllSessions(
+ CK_SLOT_ID slotID)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_CloseAllSessions"));
+ PR_LOG(modlog, 3, (fmt_slotID, slotID));
+ nssdbg_start_time(FUNC_C_CLOSEALLSESSIONS, &start);
+ rv = module_functions->C_CloseAllSessions(slotID);
+ nssdbg_finish_time(FUNC_C_CLOSEALLSESSIONS, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_GetSessionInfo(
+ CK_SESSION_HANDLE hSession,
+ CK_SESSION_INFO_PTR pInfo)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_GetSessionInfo"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pInfo, pInfo));
+ nssdbg_start_time(FUNC_C_GETSESSIONINFO, &start);
+ rv = module_functions->C_GetSessionInfo(hSession,
+ pInfo);
+ nssdbg_finish_time(FUNC_C_GETSESSIONINFO, start);
+ if (rv == CKR_OK) {
+ PR_LOG(modlog, 4, (fmt_slotID, pInfo->slotID));
+ log_state(pInfo->state);
+ PR_LOG(modlog, 4, (" flags = %s %s", pInfo->flags & CKF_RW_SESSION ? "CKF_RW_SESSION" : "", pInfo->flags & CKF_SERIAL_SESSION ? "CKF_SERIAL_SESSION" : ""));
+ PR_LOG(modlog, 4, (" deviceError = 0x%x", pInfo->ulDeviceError));
+ }
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_GetOperationState(
+ CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pOperationState,
+ CK_ULONG_PTR pulOperationStateLen)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_GetOperationState"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pOperationState, pOperationState));
+ PR_LOG(modlog, 3, (" pulOperationStateLen = 0x%p", pulOperationStateLen));
+ nssdbg_start_time(FUNC_C_GETOPERATIONSTATE, &start);
+ rv = module_functions->C_GetOperationState(hSession,
+ pOperationState,
+ pulOperationStateLen);
+ nssdbg_finish_time(FUNC_C_GETOPERATIONSTATE, start);
+ PR_LOG(modlog, 4, (" *pulOperationStateLen = 0x%x", *pulOperationStateLen));
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_SetOperationState(
+ CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pOperationState,
+ CK_ULONG ulOperationStateLen,
+ CK_OBJECT_HANDLE hEncryptionKey,
+ CK_OBJECT_HANDLE hAuthenticationKey)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_SetOperationState"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pOperationState, pOperationState));
+ PR_LOG(modlog, 3, (" ulOperationStateLen = %d", ulOperationStateLen));
+ log_handle(3, " hEncryptionKey = 0x%x", hEncryptionKey);
+ log_handle(3, " hAuthenticationKey = 0x%x", hAuthenticationKey);
+ nssdbg_start_time(FUNC_C_SETOPERATIONSTATE, &start);
+ rv = module_functions->C_SetOperationState(hSession,
+ pOperationState,
+ ulOperationStateLen,
+ hEncryptionKey,
+ hAuthenticationKey);
+ nssdbg_finish_time(FUNC_C_SETOPERATIONSTATE, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_Login(
+ CK_SESSION_HANDLE hSession,
+ CK_USER_TYPE userType,
+ CK_CHAR_PTR pPin,
+ CK_ULONG ulPinLen)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_Login"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (" userType = 0x%x", userType));
+ PR_LOG(modlog, 3, (fmt_pPin, pPin));
+ PR_LOG(modlog, 3, (fmt_ulPinLen, ulPinLen));
+ nssdbg_start_time(FUNC_C_LOGIN, &start);
+ rv = module_functions->C_Login(hSession,
+ userType,
+ pPin,
+ ulPinLen);
+ nssdbg_finish_time(FUNC_C_LOGIN, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_Logout(
+ CK_SESSION_HANDLE hSession)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_Logout"));
+ log_handle(3, fmt_hSession, hSession);
+ nssdbg_start_time(FUNC_C_LOGOUT, &start);
+ rv = module_functions->C_Logout(hSession);
+ nssdbg_finish_time(FUNC_C_LOGOUT, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_CreateObject(
+ CK_SESSION_HANDLE hSession,
+ CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount,
+ CK_OBJECT_HANDLE_PTR phObject)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_CreateObject"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pTemplate, pTemplate));
+ PR_LOG(modlog, 3, (fmt_ulCount, ulCount));
+ PR_LOG(modlog, 3, (fmt_phObject, phObject));
+ print_template(pTemplate, ulCount);
+ nssdbg_start_time(FUNC_C_CREATEOBJECT, &start);
+ rv = module_functions->C_CreateObject(hSession,
+ pTemplate,
+ ulCount,
+ phObject);
+ nssdbg_finish_time(FUNC_C_CREATEOBJECT, start);
+ log_handle(4, " *phObject = 0x%x", *phObject);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_CopyObject(
+ CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject,
+ CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount,
+ CK_OBJECT_HANDLE_PTR phNewObject)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_CopyObject"));
+ log_handle(3, fmt_hSession, hSession);
+ log_handle(3, fmt_hObject, hObject);
+ PR_LOG(modlog, 3, (fmt_pTemplate, pTemplate));
+ PR_LOG(modlog, 3, (fmt_ulCount, ulCount));
+ PR_LOG(modlog, 3, (" phNewObject = 0x%p", phNewObject));
+ print_template(pTemplate, ulCount);
+ nssdbg_start_time(FUNC_C_COPYOBJECT, &start);
+ rv = module_functions->C_CopyObject(hSession,
+ hObject,
+ pTemplate,
+ ulCount,
+ phNewObject);
+ nssdbg_finish_time(FUNC_C_COPYOBJECT, start);
+ log_handle(4, " *phNewObject = 0x%x", *phNewObject);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_DestroyObject(
+ CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_DestroyObject"));
+ log_handle(3, fmt_hSession, hSession);
+ log_handle(3, fmt_hObject, hObject);
+ nssdbg_start_time(FUNC_C_DESTROYOBJECT, &start);
+ rv = module_functions->C_DestroyObject(hSession,
+ hObject);
+ nssdbg_finish_time(FUNC_C_DESTROYOBJECT, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_GetObjectSize(
+ CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject,
+ CK_ULONG_PTR pulSize)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_GetObjectSize"));
+ log_handle(3, fmt_hSession, hSession);
+ log_handle(3, fmt_hObject, hObject);
+ PR_LOG(modlog, 3, (" pulSize = 0x%p", pulSize));
+ nssdbg_start_time(FUNC_C_GETOBJECTSIZE, &start);
+ rv = module_functions->C_GetObjectSize(hSession,
+ hObject,
+ pulSize);
+ nssdbg_finish_time(FUNC_C_GETOBJECTSIZE, start);
+ PR_LOG(modlog, 4, (" *pulSize = 0x%x", *pulSize));
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_GetAttributeValue(
+ CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject,
+ CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_GetAttributeValue"));
+ log_handle(3, fmt_hSession, hSession);
+ log_handle(3, fmt_hObject, hObject);
+ PR_LOG(modlog, 3, (fmt_pTemplate, pTemplate));
+ PR_LOG(modlog, 3, (fmt_ulCount, ulCount));
+ nssdbg_start_time(FUNC_C_GETATTRIBUTEVALUE, &start);
+ rv = module_functions->C_GetAttributeValue(hSession,
+ hObject,
+ pTemplate,
+ ulCount);
+ nssdbg_finish_time(FUNC_C_GETATTRIBUTEVALUE, start);
+ print_template(pTemplate, ulCount);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_SetAttributeValue(
+ CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject,
+ CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_SetAttributeValue"));
+ log_handle(3, fmt_hSession, hSession);
+ log_handle(3, fmt_hObject, hObject);
+ PR_LOG(modlog, 3, (fmt_pTemplate, pTemplate));
+ PR_LOG(modlog, 3, (fmt_ulCount, ulCount));
+ print_template(pTemplate, ulCount);
+ nssdbg_start_time(FUNC_C_SETATTRIBUTEVALUE, &start);
+ rv = module_functions->C_SetAttributeValue(hSession,
+ hObject,
+ pTemplate,
+ ulCount);
+ nssdbg_finish_time(FUNC_C_SETATTRIBUTEVALUE, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_FindObjectsInit(
+ CK_SESSION_HANDLE hSession,
+ CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_FindObjectsInit"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pTemplate, pTemplate));
+ PR_LOG(modlog, 3, (fmt_ulCount, ulCount));
+ print_template(pTemplate, ulCount);
+ nssdbg_start_time(FUNC_C_FINDOBJECTSINIT, &start);
+ rv = module_functions->C_FindObjectsInit(hSession,
+ pTemplate,
+ ulCount);
+ nssdbg_finish_time(FUNC_C_FINDOBJECTSINIT, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_FindObjects(
+ CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE_PTR phObject,
+ CK_ULONG ulMaxObjectCount,
+ CK_ULONG_PTR pulObjectCount)
+{
+ COMMON_DEFINITIONS;
+ CK_ULONG i;
+
+ PR_LOG(modlog, 1, ("C_FindObjects"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_phObject, phObject));
+ PR_LOG(modlog, 3, (" ulMaxObjectCount = %d", ulMaxObjectCount));
+ PR_LOG(modlog, 3, (" pulObjectCount = 0x%p", pulObjectCount));
+ nssdbg_start_time(FUNC_C_FINDOBJECTS, &start);
+ rv = module_functions->C_FindObjects(hSession,
+ phObject,
+ ulMaxObjectCount,
+ pulObjectCount);
+ nssdbg_finish_time(FUNC_C_FINDOBJECTS, start);
+ PR_LOG(modlog, 4, (" *pulObjectCount = 0x%x", *pulObjectCount));
+ for (i = 0; i < *pulObjectCount; i++) {
+ PR_LOG(modlog, 4, (" phObject[%d] = 0x%x%s", i, phObject[i], phObject[i] ? "" : fmt_invalid_handle));
+ }
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_FindObjectsFinal(
+ CK_SESSION_HANDLE hSession)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_FindObjectsFinal"));
+ log_handle(3, fmt_hSession, hSession);
+ nssdbg_start_time(FUNC_C_FINDOBJECTSFINAL, &start);
+ rv = module_functions->C_FindObjectsFinal(hSession);
+ nssdbg_finish_time(FUNC_C_FINDOBJECTSFINAL, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_EncryptInit(
+ CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hKey)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_EncryptInit"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pMechanism, pMechanism));
+ log_handle(3, fmt_hKey, hKey);
+ print_mechanism(pMechanism);
+ nssdbg_start_time(FUNC_C_ENCRYPTINIT, &start);
+ rv = module_functions->C_EncryptInit(hSession,
+ pMechanism,
+ hKey);
+ nssdbg_finish_time(FUNC_C_ENCRYPTINIT, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_Encrypt(
+ CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pData,
+ CK_ULONG ulDataLen,
+ CK_BYTE_PTR pEncryptedData,
+ CK_ULONG_PTR pulEncryptedDataLen)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_Encrypt"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pData, pData));
+ PR_LOG(modlog, 3, (fmt_ulDataLen, ulDataLen));
+ PR_LOG(modlog, 3, (fmt_pEncryptedData, pEncryptedData));
+ PR_LOG(modlog, 3, (" pulEncryptedDataLen = 0x%p", pulEncryptedDataLen));
+ nssdbg_start_time(FUNC_C_ENCRYPT, &start);
+ rv = module_functions->C_Encrypt(hSession,
+ pData,
+ ulDataLen,
+ pEncryptedData,
+ pulEncryptedDataLen);
+ nssdbg_finish_time(FUNC_C_ENCRYPT, start);
+ PR_LOG(modlog, 4, (" *pulEncryptedDataLen = 0x%x", *pulEncryptedDataLen));
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_EncryptUpdate(
+ CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pPart,
+ CK_ULONG ulPartLen,
+ CK_BYTE_PTR pEncryptedPart,
+ CK_ULONG_PTR pulEncryptedPartLen)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_EncryptUpdate"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pPart, pPart));
+ PR_LOG(modlog, 3, (fmt_ulPartLen, ulPartLen));
+ PR_LOG(modlog, 3, (fmt_pEncryptedPart, pEncryptedPart));
+ PR_LOG(modlog, 3, (fmt_pulEncryptedPartLen, pulEncryptedPartLen));
+ nssdbg_start_time(FUNC_C_ENCRYPTUPDATE, &start);
+ rv = module_functions->C_EncryptUpdate(hSession,
+ pPart,
+ ulPartLen,
+ pEncryptedPart,
+ pulEncryptedPartLen);
+ nssdbg_finish_time(FUNC_C_ENCRYPTUPDATE, start);
+ PR_LOG(modlog, 4, (fmt_spulEncryptedPartLen, *pulEncryptedPartLen));
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_EncryptFinal(
+ CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pLastEncryptedPart,
+ CK_ULONG_PTR pulLastEncryptedPartLen)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_EncryptFinal"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (" pLastEncryptedPart = 0x%p", pLastEncryptedPart));
+ PR_LOG(modlog, 3, (" pulLastEncryptedPartLen = 0x%p", pulLastEncryptedPartLen));
+ nssdbg_start_time(FUNC_C_ENCRYPTFINAL, &start);
+ rv = module_functions->C_EncryptFinal(hSession,
+ pLastEncryptedPart,
+ pulLastEncryptedPartLen);
+ nssdbg_finish_time(FUNC_C_ENCRYPTFINAL, start);
+ PR_LOG(modlog, 4, (" *pulLastEncryptedPartLen = 0x%x", *pulLastEncryptedPartLen));
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_DecryptInit(
+ CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hKey)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_DecryptInit"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pMechanism, pMechanism));
+ log_handle(3, fmt_hKey, hKey);
+ print_mechanism(pMechanism);
+ nssdbg_start_time(FUNC_C_DECRYPTINIT, &start);
+ rv = module_functions->C_DecryptInit(hSession,
+ pMechanism,
+ hKey);
+ nssdbg_finish_time(FUNC_C_DECRYPTINIT, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_Decrypt(
+ CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pEncryptedData,
+ CK_ULONG ulEncryptedDataLen,
+ CK_BYTE_PTR pData,
+ CK_ULONG_PTR pulDataLen)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_Decrypt"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pEncryptedData, pEncryptedData));
+ PR_LOG(modlog, 3, (" ulEncryptedDataLen = %d", ulEncryptedDataLen));
+ PR_LOG(modlog, 3, (fmt_pData, pData));
+ PR_LOG(modlog, 3, (fmt_pulDataLen, pulDataLen));
+ nssdbg_start_time(FUNC_C_DECRYPT, &start);
+ rv = module_functions->C_Decrypt(hSession,
+ pEncryptedData,
+ ulEncryptedDataLen,
+ pData,
+ pulDataLen);
+ nssdbg_finish_time(FUNC_C_DECRYPT, start);
+ PR_LOG(modlog, 4, (fmt_spulDataLen, *pulDataLen));
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_DecryptUpdate(
+ CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pEncryptedPart,
+ CK_ULONG ulEncryptedPartLen,
+ CK_BYTE_PTR pPart,
+ CK_ULONG_PTR pulPartLen)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_DecryptUpdate"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pEncryptedPart, pEncryptedPart));
+ PR_LOG(modlog, 3, (fmt_ulEncryptedPartLen, ulEncryptedPartLen));
+ PR_LOG(modlog, 3, (fmt_pPart, pPart));
+ PR_LOG(modlog, 3, (fmt_pulPartLen, pulPartLen));
+ nssdbg_start_time(FUNC_C_DECRYPTUPDATE, &start);
+ rv = module_functions->C_DecryptUpdate(hSession,
+ pEncryptedPart,
+ ulEncryptedPartLen,
+ pPart,
+ pulPartLen);
+ nssdbg_finish_time(FUNC_C_DECRYPTUPDATE, start);
+ PR_LOG(modlog, 4, (fmt_spulPartLen, *pulPartLen));
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_DecryptFinal(
+ CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pLastPart,
+ CK_ULONG_PTR pulLastPartLen)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_DecryptFinal"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (" pLastPart = 0x%p", pLastPart));
+ PR_LOG(modlog, 3, (" pulLastPartLen = 0x%p", pulLastPartLen));
+ nssdbg_start_time(FUNC_C_DECRYPTFINAL, &start);
+ rv = module_functions->C_DecryptFinal(hSession,
+ pLastPart,
+ pulLastPartLen);
+ nssdbg_finish_time(FUNC_C_DECRYPTFINAL, start);
+ PR_LOG(modlog, 4, (" *pulLastPartLen = 0x%x", *pulLastPartLen));
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_DigestInit(
+ CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_DigestInit"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pMechanism, pMechanism));
+ print_mechanism(pMechanism);
+ nssdbg_start_time(FUNC_C_DIGESTINIT, &start);
+ rv = module_functions->C_DigestInit(hSession,
+ pMechanism);
+ nssdbg_finish_time(FUNC_C_DIGESTINIT, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_Digest(
+ CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pData,
+ CK_ULONG ulDataLen,
+ CK_BYTE_PTR pDigest,
+ CK_ULONG_PTR pulDigestLen)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_Digest"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pData, pData));
+ PR_LOG(modlog, 3, (fmt_ulDataLen, ulDataLen));
+ PR_LOG(modlog, 3, (fmt_pDigest, pDigest));
+ PR_LOG(modlog, 3, (fmt_pulDigestLen, pulDigestLen));
+ nssdbg_start_time(FUNC_C_DIGEST, &start);
+ rv = module_functions->C_Digest(hSession,
+ pData,
+ ulDataLen,
+ pDigest,
+ pulDigestLen);
+ nssdbg_finish_time(FUNC_C_DIGEST, start);
+ PR_LOG(modlog, 4, (fmt_spulDigestLen, *pulDigestLen));
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_DigestUpdate(
+ CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pPart,
+ CK_ULONG ulPartLen)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_DigestUpdate"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pPart, pPart));
+ PR_LOG(modlog, 3, (fmt_ulPartLen, ulPartLen));
+ nssdbg_start_time(FUNC_C_DIGESTUPDATE, &start);
+ rv = module_functions->C_DigestUpdate(hSession,
+ pPart,
+ ulPartLen);
+ nssdbg_finish_time(FUNC_C_DIGESTUPDATE, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_DigestKey(
+ CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hKey)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_DigestKey"));
+ log_handle(3, fmt_hSession, hSession);
+ nssdbg_start_time(FUNC_C_DIGESTKEY, &start);
+ rv = module_functions->C_DigestKey(hSession,
+ hKey);
+ nssdbg_finish_time(FUNC_C_DIGESTKEY, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_DigestFinal(
+ CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pDigest,
+ CK_ULONG_PTR pulDigestLen)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_DigestFinal"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pDigest, pDigest));
+ PR_LOG(modlog, 3, (fmt_pulDigestLen, pulDigestLen));
+ nssdbg_start_time(FUNC_C_DIGESTFINAL, &start);
+ rv = module_functions->C_DigestFinal(hSession,
+ pDigest,
+ pulDigestLen);
+ nssdbg_finish_time(FUNC_C_DIGESTFINAL, start);
+ PR_LOG(modlog, 4, (fmt_spulDigestLen, *pulDigestLen));
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_SignInit(
+ CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hKey)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_SignInit"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pMechanism, pMechanism));
+ log_handle(3, fmt_hKey, hKey);
+ print_mechanism(pMechanism);
+ nssdbg_start_time(FUNC_C_SIGNINIT, &start);
+ rv = module_functions->C_SignInit(hSession,
+ pMechanism,
+ hKey);
+ nssdbg_finish_time(FUNC_C_SIGNINIT, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_Sign(
+ CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pData,
+ CK_ULONG ulDataLen,
+ CK_BYTE_PTR pSignature,
+ CK_ULONG_PTR pulSignatureLen)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_Sign"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pData, pData));
+ PR_LOG(modlog, 3, (fmt_ulDataLen, ulDataLen));
+ PR_LOG(modlog, 3, (fmt_pSignature, pSignature));
+ PR_LOG(modlog, 3, (fmt_pulSignatureLen, pulSignatureLen));
+ nssdbg_start_time(FUNC_C_SIGN, &start);
+ rv = module_functions->C_Sign(hSession,
+ pData,
+ ulDataLen,
+ pSignature,
+ pulSignatureLen);
+ nssdbg_finish_time(FUNC_C_SIGN, start);
+ PR_LOG(modlog, 4, (fmt_spulSignatureLen, *pulSignatureLen));
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_SignUpdate(
+ CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pPart,
+ CK_ULONG ulPartLen)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_SignUpdate"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pPart, pPart));
+ PR_LOG(modlog, 3, (fmt_ulPartLen, ulPartLen));
+ nssdbg_start_time(FUNC_C_SIGNUPDATE, &start);
+ rv = module_functions->C_SignUpdate(hSession,
+ pPart,
+ ulPartLen);
+ nssdbg_finish_time(FUNC_C_SIGNUPDATE, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_SignFinal(
+ CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pSignature,
+ CK_ULONG_PTR pulSignatureLen)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_SignFinal"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pSignature, pSignature));
+ PR_LOG(modlog, 3, (fmt_pulSignatureLen, pulSignatureLen));
+ nssdbg_start_time(FUNC_C_SIGNFINAL, &start);
+ rv = module_functions->C_SignFinal(hSession,
+ pSignature,
+ pulSignatureLen);
+ nssdbg_finish_time(FUNC_C_SIGNFINAL, start);
+ PR_LOG(modlog, 4, (fmt_spulSignatureLen, *pulSignatureLen));
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_SignRecoverInit(
+ CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hKey)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_SignRecoverInit"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pMechanism, pMechanism));
+ log_handle(3, fmt_hKey, hKey);
+ print_mechanism(pMechanism);
+ nssdbg_start_time(FUNC_C_SIGNRECOVERINIT, &start);
+ rv = module_functions->C_SignRecoverInit(hSession,
+ pMechanism,
+ hKey);
+ nssdbg_finish_time(FUNC_C_SIGNRECOVERINIT, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_SignRecover(
+ CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pData,
+ CK_ULONG ulDataLen,
+ CK_BYTE_PTR pSignature,
+ CK_ULONG_PTR pulSignatureLen)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_SignRecover"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pData, pData));
+ PR_LOG(modlog, 3, (fmt_ulDataLen, ulDataLen));
+ PR_LOG(modlog, 3, (fmt_pSignature, pSignature));
+ PR_LOG(modlog, 3, (fmt_pulSignatureLen, pulSignatureLen));
+ nssdbg_start_time(FUNC_C_SIGNRECOVER, &start);
+ rv = module_functions->C_SignRecover(hSession,
+ pData,
+ ulDataLen,
+ pSignature,
+ pulSignatureLen);
+ nssdbg_finish_time(FUNC_C_SIGNRECOVER, start);
+ PR_LOG(modlog, 4, (fmt_spulSignatureLen, *pulSignatureLen));
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_VerifyInit(
+ CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hKey)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_VerifyInit"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pMechanism, pMechanism));
+ log_handle(3, fmt_hKey, hKey);
+ print_mechanism(pMechanism);
+ nssdbg_start_time(FUNC_C_VERIFYINIT, &start);
+ rv = module_functions->C_VerifyInit(hSession,
+ pMechanism,
+ hKey);
+ nssdbg_finish_time(FUNC_C_VERIFYINIT, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_Verify(
+ CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pData,
+ CK_ULONG ulDataLen,
+ CK_BYTE_PTR pSignature,
+ CK_ULONG ulSignatureLen)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_Verify"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pData, pData));
+ PR_LOG(modlog, 3, (fmt_ulDataLen, ulDataLen));
+ PR_LOG(modlog, 3, (fmt_pSignature, pSignature));
+ PR_LOG(modlog, 3, (fmt_ulSignatureLen, ulSignatureLen));
+ nssdbg_start_time(FUNC_C_VERIFY, &start);
+ rv = module_functions->C_Verify(hSession,
+ pData,
+ ulDataLen,
+ pSignature,
+ ulSignatureLen);
+ nssdbg_finish_time(FUNC_C_VERIFY, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_VerifyUpdate(
+ CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pPart,
+ CK_ULONG ulPartLen)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_VerifyUpdate"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pPart, pPart));
+ PR_LOG(modlog, 3, (fmt_ulPartLen, ulPartLen));
+ nssdbg_start_time(FUNC_C_VERIFYUPDATE, &start);
+ rv = module_functions->C_VerifyUpdate(hSession,
+ pPart,
+ ulPartLen);
+ nssdbg_finish_time(FUNC_C_VERIFYUPDATE, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_VerifyFinal(
+ CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pSignature,
+ CK_ULONG ulSignatureLen)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_VerifyFinal"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pSignature, pSignature));
+ PR_LOG(modlog, 3, (fmt_ulSignatureLen, ulSignatureLen));
+ nssdbg_start_time(FUNC_C_VERIFYFINAL, &start);
+ rv = module_functions->C_VerifyFinal(hSession,
+ pSignature,
+ ulSignatureLen);
+ nssdbg_finish_time(FUNC_C_VERIFYFINAL, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_VerifyRecoverInit(
+ CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hKey)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_VerifyRecoverInit"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pMechanism, pMechanism));
+ log_handle(3, fmt_hKey, hKey);
+ print_mechanism(pMechanism);
+ nssdbg_start_time(FUNC_C_VERIFYRECOVERINIT, &start);
+ rv = module_functions->C_VerifyRecoverInit(hSession,
+ pMechanism,
+ hKey);
+ nssdbg_finish_time(FUNC_C_VERIFYRECOVERINIT, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_VerifyRecover(
+ CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pSignature,
+ CK_ULONG ulSignatureLen,
+ CK_BYTE_PTR pData,
+ CK_ULONG_PTR pulDataLen)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_VerifyRecover"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pSignature, pSignature));
+ PR_LOG(modlog, 3, (fmt_ulSignatureLen, ulSignatureLen));
+ PR_LOG(modlog, 3, (fmt_pData, pData));
+ PR_LOG(modlog, 3, (fmt_pulDataLen, pulDataLen));
+ nssdbg_start_time(FUNC_C_VERIFYRECOVER, &start);
+ rv = module_functions->C_VerifyRecover(hSession,
+ pSignature,
+ ulSignatureLen,
+ pData,
+ pulDataLen);
+ nssdbg_finish_time(FUNC_C_VERIFYRECOVER, start);
+ PR_LOG(modlog, 4, (fmt_spulDataLen, *pulDataLen));
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_DigestEncryptUpdate(
+ CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pPart,
+ CK_ULONG ulPartLen,
+ CK_BYTE_PTR pEncryptedPart,
+ CK_ULONG_PTR pulEncryptedPartLen)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_DigestEncryptUpdate"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pPart, pPart));
+ PR_LOG(modlog, 3, (fmt_ulPartLen, ulPartLen));
+ PR_LOG(modlog, 3, (fmt_pEncryptedPart, pEncryptedPart));
+ PR_LOG(modlog, 3, (fmt_pulEncryptedPartLen, pulEncryptedPartLen));
+ nssdbg_start_time(FUNC_C_DIGESTENCRYPTUPDATE, &start);
+ rv = module_functions->C_DigestEncryptUpdate(hSession,
+ pPart,
+ ulPartLen,
+ pEncryptedPart,
+ pulEncryptedPartLen);
+ nssdbg_finish_time(FUNC_C_DIGESTENCRYPTUPDATE, start);
+ PR_LOG(modlog, 4, (fmt_spulEncryptedPartLen, *pulEncryptedPartLen));
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_DecryptDigestUpdate(
+ CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pEncryptedPart,
+ CK_ULONG ulEncryptedPartLen,
+ CK_BYTE_PTR pPart,
+ CK_ULONG_PTR pulPartLen)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_DecryptDigestUpdate"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pEncryptedPart, pEncryptedPart));
+ PR_LOG(modlog, 3, (fmt_ulEncryptedPartLen, ulEncryptedPartLen));
+ PR_LOG(modlog, 3, (fmt_pPart, pPart));
+ PR_LOG(modlog, 3, (fmt_pulPartLen, pulPartLen));
+ nssdbg_start_time(FUNC_C_DECRYPTDIGESTUPDATE, &start);
+ rv = module_functions->C_DecryptDigestUpdate(hSession,
+ pEncryptedPart,
+ ulEncryptedPartLen,
+ pPart,
+ pulPartLen);
+ nssdbg_finish_time(FUNC_C_DECRYPTDIGESTUPDATE, start);
+ PR_LOG(modlog, 4, (fmt_spulPartLen, *pulPartLen));
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_SignEncryptUpdate(
+ CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pPart,
+ CK_ULONG ulPartLen,
+ CK_BYTE_PTR pEncryptedPart,
+ CK_ULONG_PTR pulEncryptedPartLen)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_SignEncryptUpdate"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pPart, pPart));
+ PR_LOG(modlog, 3, (fmt_ulPartLen, ulPartLen));
+ PR_LOG(modlog, 3, (fmt_pEncryptedPart, pEncryptedPart));
+ PR_LOG(modlog, 3, (fmt_pulEncryptedPartLen, pulEncryptedPartLen));
+ nssdbg_start_time(FUNC_C_SIGNENCRYPTUPDATE, &start);
+ rv = module_functions->C_SignEncryptUpdate(hSession,
+ pPart,
+ ulPartLen,
+ pEncryptedPart,
+ pulEncryptedPartLen);
+ nssdbg_finish_time(FUNC_C_SIGNENCRYPTUPDATE, start);
+ PR_LOG(modlog, 4, (fmt_spulEncryptedPartLen, *pulEncryptedPartLen));
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_DecryptVerifyUpdate(
+ CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pEncryptedPart,
+ CK_ULONG ulEncryptedPartLen,
+ CK_BYTE_PTR pPart,
+ CK_ULONG_PTR pulPartLen)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_DecryptVerifyUpdate"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pEncryptedPart, pEncryptedPart));
+ PR_LOG(modlog, 3, (fmt_ulEncryptedPartLen, ulEncryptedPartLen));
+ PR_LOG(modlog, 3, (fmt_pPart, pPart));
+ PR_LOG(modlog, 3, (fmt_pulPartLen, pulPartLen));
+ nssdbg_start_time(FUNC_C_DECRYPTVERIFYUPDATE, &start);
+ rv = module_functions->C_DecryptVerifyUpdate(hSession,
+ pEncryptedPart,
+ ulEncryptedPartLen,
+ pPart,
+ pulPartLen);
+ nssdbg_finish_time(FUNC_C_DECRYPTVERIFYUPDATE, start);
+ PR_LOG(modlog, 4, (fmt_spulPartLen, *pulPartLen));
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_GenerateKey(
+ CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount,
+ CK_OBJECT_HANDLE_PTR phKey)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_GenerateKey"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pMechanism, pMechanism));
+ PR_LOG(modlog, 3, (fmt_pTemplate, pTemplate));
+ PR_LOG(modlog, 3, (fmt_ulCount, ulCount));
+ PR_LOG(modlog, 3, (fmt_phKey, phKey));
+ print_template(pTemplate, ulCount);
+ print_mechanism(pMechanism);
+ nssdbg_start_time(FUNC_C_GENERATEKEY, &start);
+ rv = module_functions->C_GenerateKey(hSession,
+ pMechanism,
+ pTemplate,
+ ulCount,
+ phKey);
+ nssdbg_finish_time(FUNC_C_GENERATEKEY, start);
+ log_handle(4, fmt_sphKey, *phKey);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_GenerateKeyPair(
+ CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_ATTRIBUTE_PTR pPublicKeyTemplate,
+ CK_ULONG ulPublicKeyAttributeCount,
+ CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
+ CK_ULONG ulPrivateKeyAttributeCount,
+ CK_OBJECT_HANDLE_PTR phPublicKey,
+ CK_OBJECT_HANDLE_PTR phPrivateKey)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_GenerateKeyPair"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pMechanism, pMechanism));
+ PR_LOG(modlog, 3, (" pPublicKeyTemplate = 0x%p", pPublicKeyTemplate));
+ PR_LOG(modlog, 3, (" ulPublicKeyAttributeCount = %d", ulPublicKeyAttributeCount));
+ PR_LOG(modlog, 3, (" pPrivateKeyTemplate = 0x%p", pPrivateKeyTemplate));
+ PR_LOG(modlog, 3, (" ulPrivateKeyAttributeCount = %d", ulPrivateKeyAttributeCount));
+ PR_LOG(modlog, 3, (" phPublicKey = 0x%p", phPublicKey));
+ print_template(pPublicKeyTemplate, ulPublicKeyAttributeCount);
+ PR_LOG(modlog, 3, (" phPrivateKey = 0x%p", phPrivateKey));
+ print_template(pPrivateKeyTemplate, ulPrivateKeyAttributeCount);
+ print_mechanism(pMechanism);
+ nssdbg_start_time(FUNC_C_GENERATEKEYPAIR, &start);
+ rv = module_functions->C_GenerateKeyPair(hSession,
+ pMechanism,
+ pPublicKeyTemplate,
+ ulPublicKeyAttributeCount,
+ pPrivateKeyTemplate,
+ ulPrivateKeyAttributeCount,
+ phPublicKey,
+ phPrivateKey);
+ nssdbg_finish_time(FUNC_C_GENERATEKEYPAIR, start);
+ log_handle(4, " *phPublicKey = 0x%x", *phPublicKey);
+ log_handle(4, " *phPrivateKey = 0x%x", *phPrivateKey);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_WrapKey(
+ CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hWrappingKey,
+ CK_OBJECT_HANDLE hKey,
+ CK_BYTE_PTR pWrappedKey,
+ CK_ULONG_PTR pulWrappedKeyLen)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_WrapKey"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pMechanism, pMechanism));
+ log_handle(3, " hWrappingKey = 0x%x", hWrappingKey);
+ log_handle(3, fmt_hKey, hKey);
+ PR_LOG(modlog, 3, (fmt_pWrappedKey, pWrappedKey));
+ PR_LOG(modlog, 3, (" pulWrappedKeyLen = 0x%p", pulWrappedKeyLen));
+ print_mechanism(pMechanism);
+ nssdbg_start_time(FUNC_C_WRAPKEY, &start);
+ rv = module_functions->C_WrapKey(hSession,
+ pMechanism,
+ hWrappingKey,
+ hKey,
+ pWrappedKey,
+ pulWrappedKeyLen);
+ nssdbg_finish_time(FUNC_C_WRAPKEY, start);
+ PR_LOG(modlog, 4, (" *pulWrappedKeyLen = 0x%x", *pulWrappedKeyLen));
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_UnwrapKey(
+ CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hUnwrappingKey,
+ CK_BYTE_PTR pWrappedKey,
+ CK_ULONG ulWrappedKeyLen,
+ CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulAttributeCount,
+ CK_OBJECT_HANDLE_PTR phKey)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_UnwrapKey"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pMechanism, pMechanism));
+ log_handle(3, " hUnwrappingKey = 0x%x", hUnwrappingKey);
+ PR_LOG(modlog, 3, (fmt_pWrappedKey, pWrappedKey));
+ PR_LOG(modlog, 3, (" ulWrappedKeyLen = %d", ulWrappedKeyLen));
+ PR_LOG(modlog, 3, (fmt_pTemplate, pTemplate));
+ PR_LOG(modlog, 3, (fmt_ulAttributeCount, ulAttributeCount));
+ PR_LOG(modlog, 3, (fmt_phKey, phKey));
+ print_template(pTemplate, ulAttributeCount);
+ print_mechanism(pMechanism);
+ nssdbg_start_time(FUNC_C_UNWRAPKEY, &start);
+ rv = module_functions->C_UnwrapKey(hSession,
+ pMechanism,
+ hUnwrappingKey,
+ pWrappedKey,
+ ulWrappedKeyLen,
+ pTemplate,
+ ulAttributeCount,
+ phKey);
+ nssdbg_finish_time(FUNC_C_UNWRAPKEY, start);
+ log_handle(4, fmt_sphKey, *phKey);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_DeriveKey(
+ CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hBaseKey,
+ CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulAttributeCount,
+ CK_OBJECT_HANDLE_PTR phKey)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_DeriveKey"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pMechanism, pMechanism));
+ log_handle(3, " hBaseKey = 0x%x", hBaseKey);
+ PR_LOG(modlog, 3, (fmt_pTemplate, pTemplate));
+ PR_LOG(modlog, 3, (fmt_ulAttributeCount, ulAttributeCount));
+ PR_LOG(modlog, 3, (fmt_phKey, phKey));
+ print_template(pTemplate, ulAttributeCount);
+ print_mechanism(pMechanism);
+ nssdbg_start_time(FUNC_C_DERIVEKEY, &start);
+ rv = module_functions->C_DeriveKey(hSession,
+ pMechanism,
+ hBaseKey,
+ pTemplate,
+ ulAttributeCount,
+ phKey);
+ nssdbg_finish_time(FUNC_C_DERIVEKEY, start);
+ log_handle(4, fmt_sphKey, *phKey);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_SeedRandom(
+ CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pSeed,
+ CK_ULONG ulSeedLen)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_SeedRandom"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (" pSeed = 0x%p", pSeed));
+ PR_LOG(modlog, 3, (" ulSeedLen = %d", ulSeedLen));
+ nssdbg_start_time(FUNC_C_SEEDRANDOM, &start);
+ rv = module_functions->C_SeedRandom(hSession,
+ pSeed,
+ ulSeedLen);
+ nssdbg_finish_time(FUNC_C_SEEDRANDOM, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_GenerateRandom(
+ CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR RandomData,
+ CK_ULONG ulRandomLen)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_GenerateRandom"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (" RandomData = 0x%p", RandomData));
+ PR_LOG(modlog, 3, (" ulRandomLen = %d", ulRandomLen));
+ nssdbg_start_time(FUNC_C_GENERATERANDOM, &start);
+ rv = module_functions->C_GenerateRandom(hSession,
+ RandomData,
+ ulRandomLen);
+ nssdbg_finish_time(FUNC_C_GENERATERANDOM, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_GetFunctionStatus(
+ CK_SESSION_HANDLE hSession)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_GetFunctionStatus"));
+ log_handle(3, fmt_hSession, hSession);
+ nssdbg_start_time(FUNC_C_GETFUNCTIONSTATUS, &start);
+ rv = module_functions->C_GetFunctionStatus(hSession);
+ nssdbg_finish_time(FUNC_C_GETFUNCTIONSTATUS, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_CancelFunction(
+ CK_SESSION_HANDLE hSession)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_CancelFunction"));
+ log_handle(3, fmt_hSession, hSession);
+ nssdbg_start_time(FUNC_C_CANCELFUNCTION, &start);
+ rv = module_functions->C_CancelFunction(hSession);
+ nssdbg_finish_time(FUNC_C_CANCELFUNCTION, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_WaitForSlotEvent(
+ CK_FLAGS flags,
+ CK_SLOT_ID_PTR pSlot,
+ CK_VOID_PTR pRserved)
+{
+ COMMON_DEFINITIONS;
+
+ PR_LOG(modlog, 1, ("C_WaitForSlotEvent"));
+ PR_LOG(modlog, 3, (fmt_flags, flags));
+ PR_LOG(modlog, 3, (" pSlot = 0x%p", pSlot));
+ PR_LOG(modlog, 3, (" pRserved = 0x%p", pRserved));
+ nssdbg_start_time(FUNC_C_WAITFORSLOTEVENT, &start);
+ rv = module_functions->C_WaitForSlotEvent(flags,
+ pSlot,
+ pRserved);
+ nssdbg_finish_time(FUNC_C_WAITFORSLOTEVENT, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_GetInterfaceList(CK_INTERFACE_PTR interfaces,
+ CK_ULONG_PTR pulCount)
+{
+ COMMON_DEFINITIONS;
+ PR_LOG(modlog, 1, ("C_GetInterfaceList"));
+ PR_LOG(modlog, 3, (" interfaces = 0x%p", interfaces));
+ PR_LOG(modlog, 3, (" pulCount = %d", pulCount));
+ nssdbg_start_time(FUNC_C_GETINTERFACELIST, &start);
+ rv = module_functions->C_GetInterfaceList(interfaces,
+ pulCount);
+ nssdbg_finish_time(FUNC_C_GETINTERFACELIST, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_GetInterface(CK_UTF8CHAR_PTR pInterfaceName,
+ CK_VERSION_PTR pVersion,
+ CK_INTERFACE_PTR_PTR ppInterface,
+ CK_FLAGS flags)
+{
+ COMMON_DEFINITIONS;
+ PR_LOG(modlog, 1, ("C_GetInterface"));
+ PR_LOG(modlog, 3, (" pInterfaceName = 0x%p", pInterfaceName));
+ PR_LOG(modlog, 3, (" pVersion = 0x%p", pVersion));
+ PR_LOG(modlog, 3, (" ppInterface = 0x%p", ppInterface));
+ PR_LOG(modlog, 3, (fmt_flags, flags));
+ nssdbg_start_time(FUNC_C_GETINTERFACE, &start);
+ rv = module_functions->C_GetInterface(pInterfaceName,
+ pVersion,
+ ppInterface,
+ flags);
+ nssdbg_finish_time(FUNC_C_GETINTERFACE, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_LoginUser(CK_SESSION_HANDLE hSession,
+ CK_USER_TYPE userType,
+ CK_CHAR_PTR pPin,
+ CK_ULONG ulPinLen,
+ CK_UTF8CHAR_PTR pUsername,
+ CK_ULONG ulUsernameLen)
+{
+ COMMON_DEFINITIONS;
+ PR_LOG(modlog, 1, ("C_LoginUser"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (" userType = 0x%x", userType));
+ PR_LOG(modlog, 3, (fmt_pPin, pPin));
+ PR_LOG(modlog, 3, (fmt_ulPinLen, ulPinLen));
+ PR_LOG(modlog, 3, (" pUsername = 0x%p", pUsername));
+ PR_LOG(modlog, 3, (" ulUsernameLen = %d", ulUsernameLen));
+ nssdbg_start_time(FUNC_C_LOGINUSER, &start);
+ rv = module_functions->C_LoginUser(hSession,
+ userType,
+ pPin,
+ ulPinLen,
+ pUsername,
+ ulUsernameLen);
+ nssdbg_finish_time(FUNC_C_LOGINUSER, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_SessionCancel(CK_SESSION_HANDLE hSession,
+ CK_FLAGS flags)
+{
+ COMMON_DEFINITIONS;
+ PR_LOG(modlog, 1, ("C_SessionCancel"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_flags, flags));
+ nssdbg_start_time(FUNC_C_SESSIONCANCEL, &start);
+ rv = module_functions->C_SessionCancel(hSession,
+ flags);
+ nssdbg_finish_time(FUNC_C_SESSIONCANCEL, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_MessageEncryptInit(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hKey)
+{
+ COMMON_DEFINITIONS;
+ PR_LOG(modlog, 1, ("C_MessageEncryptInit"));
+ log_handle(3, fmt_hSession, hSession);
+ print_mechanism(pMechanism);
+ log_handle(3, fmt_hKey, hKey);
+ nssdbg_start_time(FUNC_C_MESSAGEENCRYPTINIT, &start);
+ rv = module_functions->C_MessageEncryptInit(hSession,
+ pMechanism,
+ hKey);
+ nssdbg_finish_time(FUNC_C_MESSAGEENCRYPTINIT, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_EncryptMessage(CK_SESSION_HANDLE hSession,
+ CK_VOID_PTR pParameter,
+ CK_ULONG ulParameterLen,
+ CK_BYTE_PTR pAssociatedData,
+ CK_ULONG ulAssociatedDataLen,
+ CK_BYTE_PTR pPlaintext,
+ CK_ULONG ulPlaintextLen,
+ CK_BYTE_PTR pCiphertext,
+ CK_ULONG_PTR pulCiphertextLen)
+{
+ COMMON_DEFINITIONS;
+ PR_LOG(modlog, 1, ("C_EncryptMessage"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pParameter, pParameter));
+ PR_LOG(modlog, 3, (fmt_ulParameterLen, ulParameterLen));
+ PR_LOG(modlog, 3, (fmt_pAssociatedData, pAssociatedData));
+ PR_LOG(modlog, 3, (fmt_ulAssociatedDataLen, ulAssociatedDataLen));
+ PR_LOG(modlog, 3, (fmt_pPlaintext, pPlaintext));
+ PR_LOG(modlog, 3, (fmt_ulPlaintextLen, ulPlaintextLen));
+ PR_LOG(modlog, 3, (fmt_pCiphertext, pCiphertext));
+ PR_LOG(modlog, 3, (fmt_pulCiphertextLen, pulCiphertextLen));
+ nssdbg_start_time(FUNC_C_ENCRYPTMESSAGE, &start);
+ rv = module_functions->C_EncryptMessage(hSession,
+ pParameter,
+ ulParameterLen,
+ pAssociatedData,
+ ulAssociatedDataLen,
+ pPlaintext,
+ ulPlaintextLen,
+ pCiphertext,
+ pulCiphertextLen);
+ nssdbg_finish_time(FUNC_C_ENCRYPTMESSAGE, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_EncryptMessageBegin(CK_SESSION_HANDLE hSession,
+ CK_VOID_PTR pParameter,
+ CK_ULONG ulParameterLen,
+ CK_BYTE_PTR pAssociatedData,
+ CK_ULONG ulAssociatedDataLen)
+{
+ COMMON_DEFINITIONS;
+ PR_LOG(modlog, 1, ("C_EncryptMessageBegin"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pParameter, pParameter));
+ PR_LOG(modlog, 3, (fmt_ulParameterLen, ulParameterLen));
+ PR_LOG(modlog, 3, (fmt_pAssociatedData, pAssociatedData));
+ PR_LOG(modlog, 3, (fmt_ulAssociatedDataLen, ulAssociatedDataLen));
+ nssdbg_start_time(FUNC_C_ENCRYPTMESSAGEBEGIN, &start);
+ rv = module_functions->C_EncryptMessageBegin(hSession,
+ pParameter,
+ ulParameterLen,
+ pAssociatedData,
+ ulAssociatedDataLen);
+ nssdbg_finish_time(FUNC_C_ENCRYPTMESSAGEBEGIN, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_EncryptMessageNext(CK_SESSION_HANDLE hSession,
+ CK_VOID_PTR pParameter,
+ CK_ULONG ulParameterLen,
+ CK_BYTE_PTR pPlaintextPart,
+ CK_ULONG ulPlaintextPartLen,
+ CK_BYTE_PTR pCiphertextPart,
+ CK_ULONG_PTR pulCiphertextPartLen,
+ CK_FLAGS flags)
+{
+ COMMON_DEFINITIONS;
+ PR_LOG(modlog, 1, ("C_EncryptMessageNext"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pParameter, pParameter));
+ PR_LOG(modlog, 3, (fmt_ulParameterLen, ulParameterLen));
+ PR_LOG(modlog, 3, (fmt_pPlaintextPart, pPlaintextPart));
+ PR_LOG(modlog, 3, (fmt_ulPlaintextPartLen, ulPlaintextPartLen));
+ PR_LOG(modlog, 3, (fmt_pCiphertextPart, pCiphertextPart));
+ PR_LOG(modlog, 3, (fmt_pulCiphertextPartLen, pulCiphertextPartLen));
+ nssdbg_start_time(FUNC_C_ENCRYPTMESSAGENEXT, &start);
+ rv = module_functions->C_EncryptMessageNext(hSession,
+ pParameter,
+ ulParameterLen,
+ pPlaintextPart,
+ ulPlaintextPartLen,
+ pCiphertextPart,
+ pulCiphertextPartLen,
+ flags);
+ nssdbg_finish_time(FUNC_C_ENCRYPTMESSAGENEXT, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_MessageEncryptFinal(CK_SESSION_HANDLE hSession)
+{
+ COMMON_DEFINITIONS;
+ PR_LOG(modlog, 1, ("C_MessageEncryptFinal"));
+ log_handle(3, fmt_hSession, hSession);
+ nssdbg_start_time(FUNC_C_MESSAGEENCRYPTFINAL, &start);
+ rv = module_functions->C_MessageEncryptFinal(hSession);
+ nssdbg_finish_time(FUNC_C_MESSAGEENCRYPTFINAL, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_MessageDecryptInit(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hKey)
+{
+ COMMON_DEFINITIONS;
+ PR_LOG(modlog, 1, ("C_MessageDecryptInit"));
+ log_handle(3, fmt_hSession, hSession);
+ print_mechanism(pMechanism);
+ log_handle(3, fmt_hKey, hKey);
+ nssdbg_start_time(FUNC_C_MESSAGEDECRYPTINIT, &start);
+ rv = module_functions->C_MessageDecryptInit(hSession,
+ pMechanism,
+ hKey);
+ nssdbg_finish_time(FUNC_C_MESSAGEDECRYPTINIT, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_DecryptMessage(CK_SESSION_HANDLE hSession,
+ CK_VOID_PTR pParameter,
+ CK_ULONG ulParameterLen,
+ CK_BYTE_PTR pAssociatedData,
+ CK_ULONG ulAssociatedDataLen,
+ CK_BYTE_PTR pCiphertext,
+ CK_ULONG ulCiphertextLen,
+ CK_BYTE_PTR pPlaintext,
+ CK_ULONG_PTR pulPlaintextLen)
+{
+ COMMON_DEFINITIONS;
+ PR_LOG(modlog, 1, ("C_DecryptMessage"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pParameter, pParameter));
+ PR_LOG(modlog, 3, (fmt_ulParameterLen, ulParameterLen));
+ PR_LOG(modlog, 3, (fmt_pAssociatedData, pAssociatedData));
+ PR_LOG(modlog, 3, (fmt_ulAssociatedDataLen, ulAssociatedDataLen));
+ PR_LOG(modlog, 3, (fmt_pCiphertext, pCiphertext));
+ PR_LOG(modlog, 3, (fmt_ulCiphertextLen, ulCiphertextLen));
+ PR_LOG(modlog, 3, (fmt_pPlaintext, pPlaintext));
+ PR_LOG(modlog, 3, (fmt_pulPlaintextLen, pulPlaintextLen));
+ nssdbg_start_time(FUNC_C_DECRYPTMESSAGE, &start);
+ rv = module_functions->C_DecryptMessage(hSession,
+ pParameter,
+ ulParameterLen,
+ pAssociatedData,
+ ulAssociatedDataLen,
+ pCiphertext,
+ ulCiphertextLen,
+ pPlaintext,
+ pulPlaintextLen);
+ nssdbg_finish_time(FUNC_C_DECRYPTMESSAGE, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_DecryptMessageBegin(CK_SESSION_HANDLE hSession,
+ CK_VOID_PTR pParameter,
+ CK_ULONG ulParameterLen,
+ CK_BYTE_PTR pAssociatedData,
+ CK_ULONG ulAssociatedDataLen)
+{
+ COMMON_DEFINITIONS;
+ PR_LOG(modlog, 1, ("C_DecryptMessageBegin"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pParameter, pParameter));
+ PR_LOG(modlog, 3, (fmt_ulParameterLen, ulParameterLen));
+ PR_LOG(modlog, 3, (fmt_pAssociatedData, pAssociatedData));
+ PR_LOG(modlog, 3, (fmt_ulAssociatedDataLen, ulAssociatedDataLen));
+ nssdbg_start_time(FUNC_C_DECRYPTMESSAGEBEGIN, &start);
+ rv = module_functions->C_DecryptMessageBegin(hSession,
+ pParameter,
+ ulParameterLen,
+ pAssociatedData,
+ ulAssociatedDataLen);
+ nssdbg_finish_time(FUNC_C_DECRYPTMESSAGEBEGIN, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_DecryptMessageNext(CK_SESSION_HANDLE hSession,
+ CK_VOID_PTR pParameter,
+ CK_ULONG ulParameterLen,
+ CK_BYTE_PTR pCiphertextPart,
+ CK_ULONG ulCiphertextPartLen,
+ CK_BYTE_PTR pPlaintextPart,
+ CK_ULONG_PTR pulPlaintextPartLen,
+ CK_FLAGS flags)
+{
+ COMMON_DEFINITIONS;
+ PR_LOG(modlog, 1, ("C_DecryptMessageNext"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pParameter, pParameter));
+ PR_LOG(modlog, 3, (fmt_ulParameterLen, ulParameterLen));
+ PR_LOG(modlog, 3, (fmt_pCiphertextPart, pCiphertextPart));
+ PR_LOG(modlog, 3, (fmt_ulCiphertextPartLen, ulCiphertextPartLen));
+ PR_LOG(modlog, 3, (fmt_pPlaintextPart, pPlaintextPart));
+ PR_LOG(modlog, 3, (fmt_pulPlaintextPartLen, pulPlaintextPartLen));
+ nssdbg_start_time(FUNC_C_DECRYPTMESSAGENEXT, &start);
+ rv = module_functions->C_DecryptMessageNext(hSession,
+ pParameter,
+ ulParameterLen,
+ pCiphertextPart,
+ ulCiphertextPartLen,
+ pPlaintextPart,
+ pulPlaintextPartLen,
+ flags);
+ nssdbg_finish_time(FUNC_C_DECRYPTMESSAGENEXT, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_MessageDecryptFinal(CK_SESSION_HANDLE hSession)
+{
+ COMMON_DEFINITIONS;
+ PR_LOG(modlog, 1, ("C_MessageDecryptFinal"));
+ log_handle(3, fmt_hSession, hSession);
+ nssdbg_start_time(FUNC_C_MESSAGEDECRYPTFINAL, &start);
+ rv = module_functions->C_MessageDecryptFinal(hSession);
+ nssdbg_finish_time(FUNC_C_MESSAGEDECRYPTFINAL, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_MessageSignInit(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hKey)
+{
+ COMMON_DEFINITIONS;
+ PR_LOG(modlog, 1, ("C_MessageSignInit"));
+ log_handle(3, fmt_hSession, hSession);
+ print_mechanism(pMechanism);
+ log_handle(3, fmt_hKey, hKey);
+ nssdbg_start_time(FUNC_C_MESSAGESIGNINIT, &start);
+ rv = module_functions->C_MessageSignInit(hSession,
+ pMechanism,
+ hKey);
+ nssdbg_finish_time(FUNC_C_MESSAGESIGNINIT, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_SignMessage(CK_SESSION_HANDLE hSession,
+ CK_VOID_PTR pParameter,
+ CK_ULONG ulParameterLen,
+ CK_BYTE_PTR pData,
+ CK_ULONG ulDataLen,
+ CK_BYTE_PTR pSignature,
+ CK_ULONG_PTR pulSignatureLen)
+{
+ COMMON_DEFINITIONS;
+ PR_LOG(modlog, 1, ("C_SignMessage"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pParameter, pParameter));
+ PR_LOG(modlog, 3, (fmt_ulParameterLen, ulParameterLen));
+ PR_LOG(modlog, 3, (fmt_pData, pData));
+ PR_LOG(modlog, 3, (fmt_ulDataLen, ulDataLen));
+ PR_LOG(modlog, 3, (fmt_pSignature, pSignature));
+ PR_LOG(modlog, 3, (fmt_pulSignatureLen, pulSignatureLen));
+ nssdbg_start_time(FUNC_C_SIGNMESSAGE, &start);
+ rv = module_functions->C_SignMessage(hSession,
+ pParameter,
+ ulParameterLen,
+ pData,
+ ulDataLen,
+ pSignature,
+ pulSignatureLen);
+ nssdbg_finish_time(FUNC_C_SIGNMESSAGE, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_SignMessageBegin(CK_SESSION_HANDLE hSession,
+ CK_VOID_PTR pParameter,
+ CK_ULONG ulParameterLen)
+{
+ COMMON_DEFINITIONS;
+ PR_LOG(modlog, 1, ("C_SignMessageBegin"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pParameter, pParameter));
+ PR_LOG(modlog, 3, (fmt_ulParameterLen, ulParameterLen));
+ nssdbg_start_time(FUNC_C_SIGNMESSAGEBEGIN, &start);
+ rv = module_functions->C_SignMessageBegin(hSession,
+ pParameter,
+ ulParameterLen);
+ nssdbg_finish_time(FUNC_C_SIGNMESSAGEBEGIN, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_SignMessageNext(CK_SESSION_HANDLE hSession,
+ CK_VOID_PTR pParameter,
+ CK_ULONG ulParameterLen,
+ CK_BYTE_PTR pData,
+ CK_ULONG ulDataLen,
+ CK_BYTE_PTR pSignature,
+ CK_ULONG_PTR pulSignatureLen)
+{
+ COMMON_DEFINITIONS;
+ PR_LOG(modlog, 1, ("C_SignMessageNext"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pParameter, pParameter));
+ PR_LOG(modlog, 3, (fmt_ulParameterLen, ulParameterLen));
+ PR_LOG(modlog, 3, (fmt_pData, pData));
+ PR_LOG(modlog, 3, (fmt_ulDataLen, ulDataLen));
+ PR_LOG(modlog, 3, (fmt_pSignature, pSignature));
+ PR_LOG(modlog, 3, (fmt_pulSignatureLen, pulSignatureLen));
+ nssdbg_start_time(FUNC_C_SIGNMESSAGENEXT, &start);
+ rv = module_functions->C_SignMessageNext(hSession,
+ pParameter,
+ ulParameterLen,
+ pData,
+ ulDataLen,
+ pSignature,
+ pulSignatureLen);
+ nssdbg_finish_time(FUNC_C_SIGNMESSAGENEXT, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_MessageSignFinal(CK_SESSION_HANDLE hSession)
+{
+ COMMON_DEFINITIONS;
+ PR_LOG(modlog, 1, ("C_MessageSignFinal"));
+ log_handle(3, fmt_hSession, hSession);
+ nssdbg_start_time(FUNC_C_MESSAGESIGNFINAL, &start);
+ rv = module_functions->C_MessageSignFinal(hSession);
+ nssdbg_finish_time(FUNC_C_MESSAGESIGNFINAL, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_MessageVerifyInit(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hKey)
+{
+ COMMON_DEFINITIONS;
+ PR_LOG(modlog, 1, ("C_MessageVerifyInit"));
+ log_handle(3, fmt_hSession, hSession);
+ print_mechanism(pMechanism);
+ log_handle(3, fmt_hKey, hKey);
+ nssdbg_start_time(FUNC_C_MESSAGEVERIFYINIT, &start);
+ rv = module_functions->C_MessageVerifyInit(hSession,
+ pMechanism,
+ hKey);
+ nssdbg_finish_time(FUNC_C_MESSAGEVERIFYINIT, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_VerifyMessage(CK_SESSION_HANDLE hSession,
+ CK_VOID_PTR pParameter,
+ CK_ULONG ulParameterLen,
+ CK_BYTE_PTR pData,
+ CK_ULONG ulDataLen,
+ CK_BYTE_PTR pSignature,
+ CK_ULONG ulSignatureLen)
+{
+ COMMON_DEFINITIONS;
+ PR_LOG(modlog, 1, ("C_VerifyMessage"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pParameter, pParameter));
+ PR_LOG(modlog, 3, (fmt_ulParameterLen, ulParameterLen));
+ PR_LOG(modlog, 3, (fmt_pData, pData));
+ PR_LOG(modlog, 3, (fmt_ulDataLen, ulDataLen));
+ PR_LOG(modlog, 3, (fmt_pSignature, pSignature));
+ PR_LOG(modlog, 3, (fmt_ulSignatureLen, ulSignatureLen));
+ nssdbg_start_time(FUNC_C_VERIFYMESSAGE, &start);
+ rv = module_functions->C_VerifyMessage(hSession,
+ pParameter,
+ ulParameterLen,
+ pData,
+ ulDataLen,
+ pSignature,
+ ulSignatureLen);
+ nssdbg_finish_time(FUNC_C_VERIFYMESSAGE, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_VerifyMessageBegin(CK_SESSION_HANDLE hSession,
+ CK_VOID_PTR pParameter,
+ CK_ULONG ulParameterLen)
+{
+ COMMON_DEFINITIONS;
+ PR_LOG(modlog, 1, ("C_VerifyMessageBegin"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pParameter, pParameter));
+ PR_LOG(modlog, 3, (fmt_ulParameterLen, ulParameterLen));
+ nssdbg_start_time(FUNC_C_VERIFYMESSAGEBEGIN, &start);
+ rv = module_functions->C_VerifyMessageBegin(hSession,
+ pParameter,
+ ulParameterLen);
+ nssdbg_finish_time(FUNC_C_VERIFYMESSAGEBEGIN, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_VerifyMessageNext(CK_SESSION_HANDLE hSession,
+ CK_VOID_PTR pParameter,
+ CK_ULONG ulParameterLen,
+ CK_BYTE_PTR pData,
+ CK_ULONG ulDataLen,
+ CK_BYTE_PTR pSignature,
+ CK_ULONG ulSignatureLen)
+{
+ COMMON_DEFINITIONS;
+ PR_LOG(modlog, 1, ("C_VerifyMessageNext"));
+ log_handle(3, fmt_hSession, hSession);
+ PR_LOG(modlog, 3, (fmt_pParameter, pParameter));
+ PR_LOG(modlog, 3, (fmt_ulParameterLen, ulParameterLen));
+ PR_LOG(modlog, 3, (fmt_pData, pData));
+ PR_LOG(modlog, 3, (fmt_ulDataLen, ulDataLen));
+ PR_LOG(modlog, 3, (fmt_pSignature, pSignature));
+ PR_LOG(modlog, 3, (fmt_ulSignatureLen, ulSignatureLen));
+ nssdbg_start_time(FUNC_C_VERIFYMESSAGENEXT, &start);
+ rv = module_functions->C_VerifyMessageNext(hSession,
+ pParameter,
+ ulParameterLen,
+ pData,
+ ulDataLen,
+ pSignature,
+ ulSignatureLen);
+ nssdbg_finish_time(FUNC_C_VERIFYMESSAGENEXT, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_RV
+NSSDBGC_MessageVerifyFinal(CK_SESSION_HANDLE hSession)
+{
+ COMMON_DEFINITIONS;
+ PR_LOG(modlog, 1, ("C_MessageVerifyFinal"));
+ log_handle(3, fmt_hSession, hSession);
+ nssdbg_start_time(FUNC_C_MESSAGEVERIFYFINAL, &start);
+ rv = module_functions->C_MessageVerifyFinal(hSession);
+ nssdbg_finish_time(FUNC_C_MESSAGEVERIFYFINAL, start);
+ log_rv(rv);
+ return rv;
+}
+
+CK_FUNCTION_LIST_3_0_PTR
+nss_InsertDeviceLog(
+ CK_FUNCTION_LIST_3_0_PTR devEPV)
+{
+ module_functions = devEPV;
+ debug_functions.version = devEPV->version;
+ modlog = PR_NewLogModule("nss_mod_log");
+ debug_functions.C_Initialize = NSSDBGC_Initialize;
+ debug_functions.C_Finalize = NSSDBGC_Finalize;
+ debug_functions.C_GetInfo = NSSDBGC_GetInfo;
+ debug_functions.C_GetFunctionList = NSSDBGC_GetFunctionList;
+ debug_functions.C_GetSlotList = NSSDBGC_GetSlotList;
+ debug_functions.C_GetSlotInfo = NSSDBGC_GetSlotInfo;
+ debug_functions.C_GetTokenInfo = NSSDBGC_GetTokenInfo;
+ debug_functions.C_GetMechanismList = NSSDBGC_GetMechanismList;
+ debug_functions.C_GetMechanismInfo = NSSDBGC_GetMechanismInfo;
+ debug_functions.C_InitToken = NSSDBGC_InitToken;
+ debug_functions.C_InitPIN = NSSDBGC_InitPIN;
+ debug_functions.C_SetPIN = NSSDBGC_SetPIN;
+ debug_functions.C_OpenSession = NSSDBGC_OpenSession;
+ debug_functions.C_CloseSession = NSSDBGC_CloseSession;
+ debug_functions.C_CloseAllSessions = NSSDBGC_CloseAllSessions;
+ debug_functions.C_GetSessionInfo = NSSDBGC_GetSessionInfo;
+ debug_functions.C_GetOperationState = NSSDBGC_GetOperationState;
+ debug_functions.C_SetOperationState = NSSDBGC_SetOperationState;
+ debug_functions.C_Login = NSSDBGC_Login;
+ debug_functions.C_Logout = NSSDBGC_Logout;
+ debug_functions.C_CreateObject = NSSDBGC_CreateObject;
+ debug_functions.C_CopyObject = NSSDBGC_CopyObject;
+ debug_functions.C_DestroyObject = NSSDBGC_DestroyObject;
+ debug_functions.C_GetObjectSize = NSSDBGC_GetObjectSize;
+ debug_functions.C_GetAttributeValue = NSSDBGC_GetAttributeValue;
+ debug_functions.C_SetAttributeValue = NSSDBGC_SetAttributeValue;
+ debug_functions.C_FindObjectsInit = NSSDBGC_FindObjectsInit;
+ debug_functions.C_FindObjects = NSSDBGC_FindObjects;
+ debug_functions.C_FindObjectsFinal = NSSDBGC_FindObjectsFinal;
+ debug_functions.C_EncryptInit = NSSDBGC_EncryptInit;
+ debug_functions.C_Encrypt = NSSDBGC_Encrypt;
+ debug_functions.C_EncryptUpdate = NSSDBGC_EncryptUpdate;
+ debug_functions.C_EncryptFinal = NSSDBGC_EncryptFinal;
+ debug_functions.C_DecryptInit = NSSDBGC_DecryptInit;
+ debug_functions.C_Decrypt = NSSDBGC_Decrypt;
+ debug_functions.C_DecryptUpdate = NSSDBGC_DecryptUpdate;
+ debug_functions.C_DecryptFinal = NSSDBGC_DecryptFinal;
+ debug_functions.C_DigestInit = NSSDBGC_DigestInit;
+ debug_functions.C_Digest = NSSDBGC_Digest;
+ debug_functions.C_DigestUpdate = NSSDBGC_DigestUpdate;
+ debug_functions.C_DigestKey = NSSDBGC_DigestKey;
+ debug_functions.C_DigestFinal = NSSDBGC_DigestFinal;
+ debug_functions.C_SignInit = NSSDBGC_SignInit;
+ debug_functions.C_Sign = NSSDBGC_Sign;
+ debug_functions.C_SignUpdate = NSSDBGC_SignUpdate;
+ debug_functions.C_SignFinal = NSSDBGC_SignFinal;
+ debug_functions.C_SignRecoverInit = NSSDBGC_SignRecoverInit;
+ debug_functions.C_SignRecover = NSSDBGC_SignRecover;
+ debug_functions.C_VerifyInit = NSSDBGC_VerifyInit;
+ debug_functions.C_Verify = NSSDBGC_Verify;
+ debug_functions.C_VerifyUpdate = NSSDBGC_VerifyUpdate;
+ debug_functions.C_VerifyFinal = NSSDBGC_VerifyFinal;
+ debug_functions.C_VerifyRecoverInit = NSSDBGC_VerifyRecoverInit;
+ debug_functions.C_VerifyRecover = NSSDBGC_VerifyRecover;
+ debug_functions.C_DigestEncryptUpdate = NSSDBGC_DigestEncryptUpdate;
+ debug_functions.C_DecryptDigestUpdate = NSSDBGC_DecryptDigestUpdate;
+ debug_functions.C_SignEncryptUpdate = NSSDBGC_SignEncryptUpdate;
+ debug_functions.C_DecryptVerifyUpdate = NSSDBGC_DecryptVerifyUpdate;
+ debug_functions.C_GenerateKey = NSSDBGC_GenerateKey;
+ debug_functions.C_GenerateKeyPair = NSSDBGC_GenerateKeyPair;
+ debug_functions.C_WrapKey = NSSDBGC_WrapKey;
+ debug_functions.C_UnwrapKey = NSSDBGC_UnwrapKey;
+ debug_functions.C_DeriveKey = NSSDBGC_DeriveKey;
+ debug_functions.C_SeedRandom = NSSDBGC_SeedRandom;
+ debug_functions.C_GenerateRandom = NSSDBGC_GenerateRandom;
+ debug_functions.C_GetFunctionStatus = NSSDBGC_GetFunctionStatus;
+ debug_functions.C_CancelFunction = NSSDBGC_CancelFunction;
+ debug_functions.C_WaitForSlotEvent = NSSDBGC_WaitForSlotEvent;
+ debug_functions.C_GetInterfaceList = NSSDBGC_GetInterfaceList;
+ debug_functions.C_GetInterface = NSSDBGC_GetInterface;
+ debug_functions.C_LoginUser = NSSDBGC_LoginUser;
+ debug_functions.C_SessionCancel = NSSDBGC_SessionCancel;
+ debug_functions.C_MessageEncryptInit = NSSDBGC_MessageEncryptInit;
+ debug_functions.C_EncryptMessage = NSSDBGC_EncryptMessage;
+ debug_functions.C_EncryptMessageBegin = NSSDBGC_EncryptMessageBegin;
+ debug_functions.C_EncryptMessageNext = NSSDBGC_EncryptMessageNext;
+ debug_functions.C_MessageEncryptFinal = NSSDBGC_MessageEncryptFinal;
+ debug_functions.C_MessageDecryptInit = NSSDBGC_MessageDecryptInit;
+ debug_functions.C_DecryptMessage = NSSDBGC_DecryptMessage;
+ debug_functions.C_DecryptMessageBegin = NSSDBGC_DecryptMessageBegin;
+ debug_functions.C_DecryptMessageNext = NSSDBGC_DecryptMessageNext;
+ debug_functions.C_MessageDecryptFinal = NSSDBGC_MessageDecryptFinal;
+ debug_functions.C_MessageSignInit = NSSDBGC_MessageSignInit;
+ debug_functions.C_SignMessage = NSSDBGC_SignMessage;
+ debug_functions.C_SignMessageBegin = NSSDBGC_SignMessageBegin;
+ debug_functions.C_SignMessageNext = NSSDBGC_SignMessageNext;
+ debug_functions.C_MessageSignFinal = NSSDBGC_MessageSignFinal;
+ debug_functions.C_MessageVerifyInit = NSSDBGC_MessageVerifyInit;
+ debug_functions.C_VerifyMessage = NSSDBGC_VerifyMessage;
+ debug_functions.C_VerifyMessageBegin = NSSDBGC_VerifyMessageBegin;
+ debug_functions.C_VerifyMessageNext = NSSDBGC_VerifyMessageNext;
+ debug_functions.C_MessageVerifyFinal = NSSDBGC_MessageVerifyFinal;
+ return &debug_functions;
+}
+
+/*
+ * scale the time factor up accordingly.
+ * This routine tries to keep at least 2 significant figures on output.
+ * If the time is 0, then indicate that with a 'z' for units.
+ * If the time is greater than 10 minutes, output the time in minutes.
+ * If the time is less than 10 minutes but greater than 10 seconds output
+ * the time in second.
+ * If the time is less than 10 seconds but greater than 10 milliseconds
+ * output * the time in millisecond.
+ * If the time is less than 10 milliseconds but greater than 0 ticks output
+ * the time in microsecond.
+ *
+ */
+static PRUint32
+getPrintTime(PRIntervalTime time, char **type)
+{
+ PRUint32 prTime;
+
+ /* detect a programming error by outputting 'bu' to the output stream
+ * rather than crashing */
+ *type = "bug";
+ if (time == 0) {
+ *type = "z";
+ return 0;
+ }
+
+ prTime = PR_IntervalToSeconds(time);
+
+ if (prTime >= 600) {
+ *type = "m";
+ return prTime / 60;
+ }
+ if (prTime >= 10) {
+ *type = "s";
+ return prTime;
+ }
+ prTime = PR_IntervalToMilliseconds(time);
+ if (prTime >= 10) {
+ *type = "ms";
+ return prTime;
+ }
+ *type = "us";
+ return PR_IntervalToMicroseconds(time);
+}
+
+static void
+print_final_statistics(void)
+{
+ int total_calls = 0;
+ PRIntervalTime total_time = 0;
+ PRUint32 pr_total_time;
+ char *type;
+ char *fname;
+ FILE *outfile = NULL;
+ int i;
+
+ fname = PR_GetEnvSecure("NSS_OUTPUT_FILE");
+ if (fname) {
+ /* need to add an optional process id to the filename */
+ outfile = fopen(fname, "w+");
+ }
+ if (!outfile) {
+ outfile = stdout;
+ }
+
+ fprintf(outfile, "%-25s %10s %12s %12s %10s\n", "Function", "# Calls",
+ "Time", "Avg.", "% Time");
+ fprintf(outfile, "\n");
+ for (i = 0; i < nssdbg_prof_size; i++) {
+ total_calls += nssdbg_prof_data[i].calls;
+ total_time += nssdbg_prof_data[i].time;
+ }
+ for (i = 0; i < nssdbg_prof_size; i++) {
+ PRIntervalTime time = nssdbg_prof_data[i].time;
+ PRUint32 usTime = PR_IntervalToMicroseconds(time);
+ PRUint32 prTime = 0;
+ PRUint32 calls = nssdbg_prof_data[i].calls;
+ /* don't print out functions that weren't even called */
+ if (calls == 0) {
+ continue;
+ }
+
+ prTime = getPrintTime(time, &type);
+
+ fprintf(outfile, "%-25s %10d %10d%2s ", nssdbg_prof_data[i].function,
+ calls, prTime, type);
+ /* for now always output the average in microseconds */
+ fprintf(outfile, "%10.2f%2s", (float)usTime / (float)calls, "us");
+ fprintf(outfile, "%10.2f%%", ((float)time / (float)total_time) * 100);
+ fprintf(outfile, "\n");
+ }
+ fprintf(outfile, "\n");
+
+ pr_total_time = getPrintTime(total_time, &type);
+
+ fprintf(outfile, "%25s %10d %10d%2s\n", "Totals", total_calls,
+ pr_total_time, type);
+ fprintf(outfile, "\n\nMaximum number of concurrent open sessions: %d\n\n",
+ maxOpenSessions);
+ fflush(outfile);
+ if (outfile != stdout) {
+ fclose(outfile);
+ }
+}
diff --git a/security/nss/lib/pk11wrap/dev3hack.c b/security/nss/lib/pk11wrap/dev3hack.c
new file mode 100644
index 0000000000..2d41a34d85
--- /dev/null
+++ b/security/nss/lib/pk11wrap/dev3hack.c
@@ -0,0 +1,264 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef PKIT_H
+#include "pkit.h"
+#endif /* PKIT_H */
+
+#ifndef DEVM_H
+#include "devm.h"
+#endif /* DEVM_H */
+
+#include "pki3hack.h"
+#include "dev3hack.h"
+#include "pkim.h"
+
+#ifndef BASE_H
+#include "base.h"
+#endif /* BASE_H */
+
+#include "pk11func.h"
+#include "secmodti.h"
+#include "secerr.h"
+
+NSS_IMPLEMENT nssSession *
+nssSession_ImportNSS3Session(NSSArena *arenaOpt,
+ CK_SESSION_HANDLE session,
+ PZLock *lock, PRBool rw)
+{
+ nssSession *rvSession = NULL;
+ if (session != CK_INVALID_HANDLE) {
+ rvSession = nss_ZNEW(arenaOpt, nssSession);
+ if (rvSession) {
+ rvSession->handle = session;
+ rvSession->lock = lock;
+ rvSession->ownLock = PR_FALSE;
+ rvSession->isRW = rw;
+ }
+ }
+ return rvSession;
+}
+
+NSS_IMPLEMENT nssSession *
+nssSlot_CreateSession(
+ NSSSlot *slot,
+ NSSArena *arenaOpt,
+ PRBool readWrite)
+{
+ nssSession *rvSession;
+
+ if (!readWrite) {
+ /* nss3hack version only returns rw swssions */
+ return NULL;
+ }
+ rvSession = nss_ZNEW(arenaOpt, nssSession);
+ if (!rvSession) {
+ return (nssSession *)NULL;
+ }
+
+ rvSession->handle = PK11_GetRWSession(slot->pk11slot);
+ if (rvSession->handle == CK_INVALID_HANDLE) {
+ nss_ZFreeIf(rvSession);
+ return NULL;
+ }
+ rvSession->isRW = PR_TRUE;
+ rvSession->slot = slot;
+ /*
+ * The session doesn't need its own lock. Here's why.
+ * 1. If we are reusing the default RW session of the slot,
+ * the slot lock is already locked to protect the session.
+ * 2. If the module is not thread safe, the slot (or rather
+ * module) lock is already locked.
+ * 3. If the module is thread safe and we are using a new
+ * session, no higher-level lock has been locked and we
+ * would need a lock for the new session. However, the
+ * current usage of the session is that it is always
+ * used and destroyed within the same function and never
+ * shared with another thread.
+ * So the session is either already protected by another
+ * lock or only used by one thread.
+ */
+ rvSession->lock = NULL;
+ rvSession->ownLock = PR_FALSE;
+ return rvSession;
+}
+
+NSS_IMPLEMENT PRStatus
+nssSession_Destroy(nssSession *s)
+{
+ PRStatus rv = PR_SUCCESS;
+ if (s) {
+ if (s->isRW) {
+ PK11_RestoreROSession(s->slot->pk11slot, s->handle);
+ }
+ rv = nss_ZFreeIf(s);
+ }
+ return rv;
+}
+
+static NSSSlot *
+nssSlot_CreateFromPK11SlotInfo(NSSTrustDomain *td, PK11SlotInfo *nss3slot)
+{
+ NSSSlot *rvSlot;
+ NSSArena *arena;
+ arena = nssArena_Create();
+ if (!arena) {
+ return NULL;
+ }
+ rvSlot = nss_ZNEW(arena, NSSSlot);
+ if (!rvSlot) {
+ nssArena_Destroy(arena);
+ return NULL;
+ }
+ rvSlot->base.refCount = 1;
+ rvSlot->base.lock = PZ_NewLock(nssILockOther);
+ rvSlot->base.arena = arena;
+ rvSlot->pk11slot = PK11_ReferenceSlot(nss3slot);
+ rvSlot->epv = nss3slot->functionList;
+ rvSlot->slotID = nss3slot->slotID;
+ /* Grab the slot name from the PKCS#11 fixed-length buffer */
+ rvSlot->base.name = nssUTF8_Duplicate(nss3slot->slot_name, td->arena);
+ rvSlot->lock = (nss3slot->isThreadSafe) ? NULL : nss3slot->sessionLock;
+ rvSlot->isPresentLock = PZ_NewLock(nssiLockOther);
+ rvSlot->isPresentCondition = PR_NewCondVar(rvSlot->isPresentLock);
+ rvSlot->isPresentThread = NULL;
+ rvSlot->lastTokenPingState = nssSlotLastPingState_Reset;
+ return rvSlot;
+}
+
+NSSToken *
+nssToken_CreateFromPK11SlotInfo(NSSTrustDomain *td, PK11SlotInfo *nss3slot)
+{
+ NSSToken *rvToken;
+ NSSArena *arena;
+
+ /* Don't create a token object for a disabled slot */
+ if (nss3slot->disabled) {
+ PORT_SetError(SEC_ERROR_NO_TOKEN);
+ return NULL;
+ }
+ arena = nssArena_Create();
+ if (!arena) {
+ return NULL;
+ }
+ rvToken = nss_ZNEW(arena, NSSToken);
+ if (!rvToken) {
+ nssArena_Destroy(arena);
+ return NULL;
+ }
+ rvToken->base.refCount = 1;
+ rvToken->base.lock = PZ_NewLock(nssILockOther);
+ if (!rvToken->base.lock) {
+ nssArena_Destroy(arena);
+ return NULL;
+ }
+ rvToken->base.arena = arena;
+ rvToken->pk11slot = PK11_ReferenceSlot(nss3slot);
+ rvToken->epv = nss3slot->functionList;
+ rvToken->defaultSession = nssSession_ImportNSS3Session(td->arena,
+ nss3slot->session,
+ nss3slot->sessionLock,
+ nss3slot->defRWSession);
+#if 0 /* we should do this instead of blindly continuing. */
+ if (!rvToken->defaultSession) {
+ PORT_SetError(SEC_ERROR_NO_TOKEN);
+ goto loser;
+ }
+#endif
+ if (!PK11_IsInternal(nss3slot) && PK11_IsHW(nss3slot)) {
+ rvToken->cache = nssTokenObjectCache_Create(rvToken,
+ PR_TRUE, PR_TRUE, PR_TRUE);
+ if (!rvToken->cache)
+ goto loser;
+ }
+ rvToken->trustDomain = td;
+ /* Grab the token name from the PKCS#11 fixed-length buffer */
+ rvToken->base.name = nssUTF8_Duplicate(nss3slot->token_name, td->arena);
+ rvToken->slot = nssSlot_CreateFromPK11SlotInfo(td, nss3slot);
+ if (!rvToken->slot) {
+ goto loser;
+ }
+ if (rvToken->defaultSession)
+ rvToken->defaultSession->slot = rvToken->slot;
+ return rvToken;
+loser:
+ PZ_DestroyLock(rvToken->base.lock);
+ nssArena_Destroy(arena);
+ return NULL;
+}
+
+NSS_IMPLEMENT void
+nssToken_UpdateName(NSSToken *token)
+{
+ if (!token) {
+ return;
+ }
+ token->base.name = nssUTF8_Duplicate(token->pk11slot->token_name, token->base.arena);
+}
+
+NSS_IMPLEMENT PRBool
+nssSlot_IsPermanent(NSSSlot *slot)
+{
+ return slot->pk11slot->isPerm;
+}
+
+NSS_IMPLEMENT PRBool
+nssSlot_IsFriendly(NSSSlot *slot)
+{
+ return PK11_IsFriendly(slot->pk11slot);
+}
+
+NSS_IMPLEMENT PRStatus
+nssToken_Refresh(NSSToken *token)
+{
+ PK11SlotInfo *nss3slot;
+
+ if (!token) {
+ return PR_SUCCESS;
+ }
+ nss3slot = token->pk11slot;
+ token->defaultSession =
+ nssSession_ImportNSS3Session(token->slot->base.arena,
+ nss3slot->session,
+ nss3slot->sessionLock,
+ nss3slot->defRWSession);
+ return token->defaultSession ? PR_SUCCESS : PR_FAILURE;
+}
+
+NSS_IMPLEMENT PRStatus
+nssToken_GetTrustOrder(NSSToken *tok)
+{
+ PK11SlotInfo *slot;
+ SECMODModule *module;
+ slot = tok->pk11slot;
+ module = PK11_GetModule(slot);
+ return module->trustOrder;
+}
+
+NSS_IMPLEMENT PRBool
+nssSlot_IsLoggedIn(NSSSlot *slot)
+{
+ if (!slot->pk11slot->needLogin) {
+ return PR_TRUE;
+ }
+ return PK11_IsLoggedIn(slot->pk11slot, NULL);
+}
+
+NSSTrustDomain *
+nssToken_GetTrustDomain(NSSToken *token)
+{
+ return token->trustDomain;
+}
+
+NSS_EXTERN PRStatus
+nssTrustDomain_RemoveTokenCertsFromCache(
+ NSSTrustDomain *td,
+ NSSToken *token);
+
+NSS_IMPLEMENT PRStatus
+nssToken_NotifyCertsNotVisible(
+ NSSToken *tok)
+{
+ return nssTrustDomain_RemoveTokenCertsFromCache(tok->trustDomain, tok);
+}
diff --git a/security/nss/lib/pk11wrap/dev3hack.h b/security/nss/lib/pk11wrap/dev3hack.h
new file mode 100644
index 0000000000..6105259105
--- /dev/null
+++ b/security/nss/lib/pk11wrap/dev3hack.h
@@ -0,0 +1,30 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef DEVNSS3HACK_H
+#define DEVNSS3HACK_H
+
+#include "cert.h"
+
+PR_BEGIN_EXTERN_C
+
+NSS_EXTERN NSSToken *
+nssToken_CreateFromPK11SlotInfo(NSSTrustDomain *td, PK11SlotInfo *nss3slot);
+
+NSS_EXTERN void
+nssToken_UpdateName(NSSToken *);
+
+NSS_EXTERN PRStatus
+nssToken_Refresh(NSSToken *);
+
+NSSTrustDomain *
+nssToken_GetTrustDomain(NSSToken *token);
+
+void PK11Slot_SetNSSToken(PK11SlotInfo *sl, NSSToken *nsst);
+
+NSSToken *PK11Slot_GetNSSToken(PK11SlotInfo *sl);
+
+PR_END_EXTERN_C
+
+#endif /* DEVNSS3HACK_H */
diff --git a/security/nss/lib/pk11wrap/exports.gyp b/security/nss/lib/pk11wrap/exports.gyp
new file mode 100644
index 0000000000..5067cade82
--- /dev/null
+++ b/security/nss/lib/pk11wrap/exports.gyp
@@ -0,0 +1,41 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+{
+ 'includes': [
+ '../../coreconf/config.gypi'
+ ],
+ 'targets': [
+ {
+ 'target_name': 'lib_pk11wrap_exports',
+ 'type': 'none',
+ 'copies': [
+ {
+ 'files': [
+ 'pk11func.h',
+ 'pk11hpke.h',
+ 'pk11pqg.h',
+ 'pk11priv.h',
+ 'pk11pub.h',
+ 'pk11sdr.h',
+ 'secmod.h',
+ 'secmodt.h',
+ 'secpkcs5.h'
+ ],
+ 'destination': '<(nss_public_dist_dir)/<(module)'
+ },
+ {
+ 'files': [
+ 'dev3hack.h',
+ 'secmodi.h',
+ 'secmodti.h'
+ ],
+ 'destination': '<(nss_private_dist_dir)/<(module)'
+ }
+ ]
+ }
+ ],
+ 'variables': {
+ 'module': 'nss'
+ }
+}
diff --git a/security/nss/lib/pk11wrap/manifest.mn b/security/nss/lib/pk11wrap/manifest.mn
new file mode 100644
index 0000000000..8f8a387b44
--- /dev/null
+++ b/security/nss/lib/pk11wrap/manifest.mn
@@ -0,0 +1,67 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+CORE_DEPTH = ../..
+
+EXPORTS = \
+ secmod.h \
+ secmodt.h \
+ secpkcs5.h \
+ pk11func.h \
+ pk11hpke.h \
+ pk11pub.h \
+ pk11priv.h \
+ pk11sdr.h \
+ pk11pqg.h \
+ $(NULL)
+
+PRIVATE_EXPORTS = \
+ secmodi.h \
+ secmodti.h \
+ dev3hack.h \
+ $(NULL)
+
+MODULE = nss
+
+CSRCS = \
+ dev3hack.c \
+ pk11akey.c \
+ pk11auth.c \
+ pk11cert.c \
+ pk11cxt.c \
+ pk11err.c \
+ pk11hpke.c \
+ pk11kea.c \
+ pk11list.c \
+ pk11load.c \
+ pk11mech.c \
+ pk11merge.c \
+ pk11nobj.c \
+ pk11obj.c \
+ pk11pars.c \
+ pk11pbe.c \
+ pk11pk12.c \
+ pk11pqg.c \
+ pk11sdr.c \
+ pk11skey.c \
+ pk11slot.c \
+ pk11util.c \
+ $(NULL)
+
+LIBRARY_NAME = pk11wrap
+SHARED_LIBRARY = $(NULL)
+
+NSS_LIBRARY_VERSION = 3
+SOFTOKEN_LIBRARY_VERSION = 3
+DEFINES += -DSHLIB_SUFFIX=\"$(DLL_SUFFIX)\" -DSHLIB_PREFIX=\"$(DLL_PREFIX)\" \
+ -DNSS_SHLIB_VERSION=\"$(NSS_LIBRARY_VERSION)\" \
+ -DSOFTOKEN_SHLIB_VERSION=\"$(SOFTOKEN_LIBRARY_VERSION)\"
+
+# only add module debugging in opt builds if DEBUG_PKCS11 is set
+ifdef DEBUG_PKCS11
+ DEFINES += -DDEBUG_MODULE -DFORCE_PR_LOG
+endif
+
+# This part of the code, including all sub-dirs, can be optimized for size
+export ALLOW_OPT_CODE_SIZE = 1
diff --git a/security/nss/lib/pk11wrap/pk11akey.c b/security/nss/lib/pk11wrap/pk11akey.c
new file mode 100644
index 0000000000..310d656627
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11akey.c
@@ -0,0 +1,2679 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/*
+ * This file contains functions to manage asymetric keys, (public and
+ * private keys).
+ */
+#include <stddef.h>
+
+#include "seccomon.h"
+#include "secmod.h"
+#include "secmodi.h"
+#include "secmodti.h"
+#include "pkcs11.h"
+#include "pkcs11t.h"
+#include "pk11func.h"
+#include "cert.h"
+#include "keyhi.h"
+#include "keyi.h"
+#include "secitem.h"
+#include "secasn1.h"
+#include "secoid.h"
+#include "secerr.h"
+#include "sechash.h"
+
+#include "secpkcs5.h"
+#include "blapit.h"
+
+static SECItem *
+pk11_MakeIDFromPublicKey(SECKEYPublicKey *pubKey)
+{
+ /* set the ID to the public key so we can find it again */
+ SECItem *pubKeyIndex = NULL;
+ switch (pubKey->keyType) {
+ case rsaKey:
+ pubKeyIndex = &pubKey->u.rsa.modulus;
+ break;
+ case dsaKey:
+ pubKeyIndex = &pubKey->u.dsa.publicValue;
+ break;
+ case dhKey:
+ pubKeyIndex = &pubKey->u.dh.publicValue;
+ break;
+ case ecKey:
+ pubKeyIndex = &pubKey->u.ec.publicValue;
+ break;
+ default:
+ return NULL;
+ }
+ PORT_Assert(pubKeyIndex != NULL);
+
+ return PK11_MakeIDFromPubKey(pubKeyIndex);
+}
+
+/*
+ * import a public key into the desired slot
+ *
+ * This function takes a public key structure and creates a public key in a
+ * given slot. If isToken is set, then a persistant public key is created.
+ *
+ * Note: it is possible for this function to return a handle for a key which
+ * is persistant, even if isToken is not set.
+ */
+CK_OBJECT_HANDLE
+PK11_ImportPublicKey(PK11SlotInfo *slot, SECKEYPublicKey *pubKey,
+ PRBool isToken)
+{
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_BBOOL ckfalse = CK_FALSE;
+ CK_OBJECT_CLASS keyClass = CKO_PUBLIC_KEY;
+ CK_KEY_TYPE keyType = CKK_GENERIC_SECRET;
+ CK_OBJECT_HANDLE objectID;
+ CK_ATTRIBUTE theTemplate[11];
+ CK_ATTRIBUTE *signedattr = NULL;
+ CK_ATTRIBUTE *attrs = theTemplate;
+ SECItem *ckaId = NULL;
+ SECItem *pubValue = NULL;
+ int signedcount = 0;
+ unsigned int templateCount = 0;
+ SECStatus rv;
+
+ /* if we already have an object in the desired slot, use it */
+ if (!isToken && pubKey->pkcs11Slot == slot) {
+ return pubKey->pkcs11ID;
+ }
+
+ /* free the existing key */
+ if (pubKey->pkcs11Slot != NULL) {
+ PK11SlotInfo *oSlot = pubKey->pkcs11Slot;
+ if (!PK11_IsPermObject(pubKey->pkcs11Slot, pubKey->pkcs11ID)) {
+ PK11_EnterSlotMonitor(oSlot);
+ (void)PK11_GETTAB(oSlot)->C_DestroyObject(oSlot->session,
+ pubKey->pkcs11ID);
+ PK11_ExitSlotMonitor(oSlot);
+ }
+ PK11_FreeSlot(oSlot);
+ pubKey->pkcs11Slot = NULL;
+ }
+ PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof(keyClass));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(keyType));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_TOKEN, isToken ? &cktrue : &ckfalse,
+ sizeof(CK_BBOOL));
+ attrs++;
+ if (isToken) {
+ ckaId = pk11_MakeIDFromPublicKey(pubKey);
+ if (ckaId == NULL) {
+ PORT_SetError(SEC_ERROR_BAD_KEY);
+ return CK_INVALID_HANDLE;
+ }
+ PK11_SETATTRS(attrs, CKA_ID, ckaId->data, ckaId->len);
+ attrs++;
+ }
+
+ /* now import the key */
+ {
+ switch (pubKey->keyType) {
+ case rsaKey:
+ keyType = CKK_RSA;
+ PK11_SETATTRS(attrs, CKA_WRAP, &cktrue, sizeof(CK_BBOOL));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_ENCRYPT, &cktrue,
+ sizeof(CK_BBOOL));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_VERIFY, &cktrue, sizeof(CK_BBOOL));
+ attrs++;
+ signedattr = attrs;
+ PK11_SETATTRS(attrs, CKA_MODULUS, pubKey->u.rsa.modulus.data,
+ pubKey->u.rsa.modulus.len);
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_PUBLIC_EXPONENT,
+ pubKey->u.rsa.publicExponent.data,
+ pubKey->u.rsa.publicExponent.len);
+ attrs++;
+ break;
+ case dsaKey:
+ keyType = CKK_DSA;
+ PK11_SETATTRS(attrs, CKA_VERIFY, &cktrue, sizeof(CK_BBOOL));
+ attrs++;
+ signedattr = attrs;
+ PK11_SETATTRS(attrs, CKA_PRIME, pubKey->u.dsa.params.prime.data,
+ pubKey->u.dsa.params.prime.len);
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_SUBPRIME, pubKey->u.dsa.params.subPrime.data,
+ pubKey->u.dsa.params.subPrime.len);
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_BASE, pubKey->u.dsa.params.base.data,
+ pubKey->u.dsa.params.base.len);
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_VALUE, pubKey->u.dsa.publicValue.data,
+ pubKey->u.dsa.publicValue.len);
+ attrs++;
+ break;
+ case fortezzaKey:
+ keyType = CKK_DSA;
+ PK11_SETATTRS(attrs, CKA_VERIFY, &cktrue, sizeof(CK_BBOOL));
+ attrs++;
+ signedattr = attrs;
+ PK11_SETATTRS(attrs, CKA_PRIME, pubKey->u.fortezza.params.prime.data,
+ pubKey->u.fortezza.params.prime.len);
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_SUBPRIME,
+ pubKey->u.fortezza.params.subPrime.data,
+ pubKey->u.fortezza.params.subPrime.len);
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_BASE, pubKey->u.fortezza.params.base.data,
+ pubKey->u.fortezza.params.base.len);
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_VALUE, pubKey->u.fortezza.DSSKey.data,
+ pubKey->u.fortezza.DSSKey.len);
+ attrs++;
+ break;
+ case dhKey:
+ keyType = CKK_DH;
+ PK11_SETATTRS(attrs, CKA_DERIVE, &cktrue, sizeof(CK_BBOOL));
+ attrs++;
+ signedattr = attrs;
+ PK11_SETATTRS(attrs, CKA_PRIME, pubKey->u.dh.prime.data,
+ pubKey->u.dh.prime.len);
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_BASE, pubKey->u.dh.base.data,
+ pubKey->u.dh.base.len);
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_VALUE, pubKey->u.dh.publicValue.data,
+ pubKey->u.dh.publicValue.len);
+ attrs++;
+ break;
+ case ecKey:
+ keyType = CKK_EC;
+ PK11_SETATTRS(attrs, CKA_VERIFY, &cktrue, sizeof(CK_BBOOL));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_DERIVE, &cktrue, sizeof(CK_BBOOL));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_EC_PARAMS,
+ pubKey->u.ec.DEREncodedParams.data,
+ pubKey->u.ec.DEREncodedParams.len);
+ attrs++;
+ if (PR_GetEnvSecure("NSS_USE_DECODED_CKA_EC_POINT")) {
+ PK11_SETATTRS(attrs, CKA_EC_POINT,
+ pubKey->u.ec.publicValue.data,
+ pubKey->u.ec.publicValue.len);
+ attrs++;
+ } else {
+ pubValue = SEC_ASN1EncodeItem(NULL, NULL,
+ &pubKey->u.ec.publicValue,
+ SEC_ASN1_GET(SEC_OctetStringTemplate));
+ if (pubValue == NULL) {
+ if (ckaId) {
+ SECITEM_FreeItem(ckaId, PR_TRUE);
+ }
+ return CK_INVALID_HANDLE;
+ }
+ PK11_SETATTRS(attrs, CKA_EC_POINT,
+ pubValue->data, pubValue->len);
+ attrs++;
+ }
+ break;
+ default:
+ if (ckaId) {
+ SECITEM_FreeItem(ckaId, PR_TRUE);
+ }
+ PORT_SetError(SEC_ERROR_BAD_KEY);
+ return CK_INVALID_HANDLE;
+ }
+ templateCount = attrs - theTemplate;
+ PORT_Assert(templateCount <= (sizeof(theTemplate) / sizeof(CK_ATTRIBUTE)));
+ if (pubKey->keyType != ecKey) {
+ PORT_Assert(signedattr);
+ signedcount = attrs - signedattr;
+ for (attrs = signedattr; signedcount; attrs++, signedcount--) {
+ pk11_SignedToUnsigned(attrs);
+ }
+ }
+ rv = PK11_CreateNewObject(slot, CK_INVALID_HANDLE, theTemplate,
+ templateCount, isToken, &objectID);
+ if (ckaId) {
+ SECITEM_FreeItem(ckaId, PR_TRUE);
+ }
+ if (pubValue) {
+ SECITEM_FreeItem(pubValue, PR_TRUE);
+ }
+ if (rv != SECSuccess) {
+ return CK_INVALID_HANDLE;
+ }
+ }
+
+ pubKey->pkcs11ID = objectID;
+ pubKey->pkcs11Slot = PK11_ReferenceSlot(slot);
+
+ return objectID;
+}
+
+/*
+ * take an attribute and copy it into a secitem
+ */
+static CK_RV
+pk11_Attr2SecItem(PLArenaPool *arena, const CK_ATTRIBUTE *attr, SECItem *item)
+{
+ item->data = NULL;
+
+ (void)SECITEM_AllocItem(arena, item, attr->ulValueLen);
+ if (item->data == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+ PORT_Memcpy(item->data, attr->pValue, item->len);
+ return CKR_OK;
+}
+
+/*
+ * get a curve length from a set of ecParams.
+ *
+ * We need this so we can reliably determine if the ecPoint passed to us
+ * was encoded or not. With out this, for many curves, we would incorrectly
+ * identify an unencoded curve as an encoded curve 1 in 65536 times, and for
+ * a few we would make that same mistake 1 in 32768 times. These are bad
+ * numbers since they are rare enough to pass tests, but common enough to
+ * be tripped over in the field.
+ *
+ * This function will only work for curves we recognized as of March 2009.
+ * The assumption is curves in use after March of 2009 would be supplied by
+ * PKCS #11 modules that already pass the correct encoding to us.
+ *
+ * Point length = (Roundup(curveLenInBits/8)*2+1)
+ */
+static int
+pk11_get_EC_PointLenInBytes(PLArenaPool *arena, const SECItem *ecParams,
+ PRBool *plain)
+{
+ SECItem oid;
+ SECOidTag tag;
+ SECStatus rv;
+
+ /* decode the OID tag */
+ rv = SEC_QuickDERDecodeItem(arena, &oid,
+ SEC_ASN1_GET(SEC_ObjectIDTemplate), ecParams);
+ if (rv != SECSuccess) {
+ /* could be explict curves, allow them to work if the
+ * PKCS #11 module support them. If we try to parse the
+ * explicit curve value in the future, we may return -1 here
+ * to indicate an invalid parameter if the explicit curve
+ * decode fails. */
+ return 0;
+ }
+
+ *plain = PR_FALSE;
+ tag = SECOID_FindOIDTag(&oid);
+ switch (tag) {
+ case SEC_OID_SECG_EC_SECP112R1:
+ case SEC_OID_SECG_EC_SECP112R2:
+ return 29; /* curve len in bytes = 14 bytes */
+ case SEC_OID_SECG_EC_SECT113R1:
+ case SEC_OID_SECG_EC_SECT113R2:
+ return 31; /* curve len in bytes = 15 bytes */
+ case SEC_OID_SECG_EC_SECP128R1:
+ case SEC_OID_SECG_EC_SECP128R2:
+ return 33; /* curve len in bytes = 16 bytes */
+ case SEC_OID_SECG_EC_SECT131R1:
+ case SEC_OID_SECG_EC_SECT131R2:
+ return 35; /* curve len in bytes = 17 bytes */
+ case SEC_OID_SECG_EC_SECP160K1:
+ case SEC_OID_SECG_EC_SECP160R1:
+ case SEC_OID_SECG_EC_SECP160R2:
+ return 41; /* curve len in bytes = 20 bytes */
+ case SEC_OID_SECG_EC_SECT163K1:
+ case SEC_OID_SECG_EC_SECT163R1:
+ case SEC_OID_SECG_EC_SECT163R2:
+ case SEC_OID_ANSIX962_EC_C2PNB163V1:
+ case SEC_OID_ANSIX962_EC_C2PNB163V2:
+ case SEC_OID_ANSIX962_EC_C2PNB163V3:
+ return 43; /* curve len in bytes = 21 bytes */
+ case SEC_OID_ANSIX962_EC_C2PNB176V1:
+ return 45; /* curve len in bytes = 22 bytes */
+ case SEC_OID_ANSIX962_EC_C2TNB191V1:
+ case SEC_OID_ANSIX962_EC_C2TNB191V2:
+ case SEC_OID_ANSIX962_EC_C2TNB191V3:
+ case SEC_OID_SECG_EC_SECP192K1:
+ case SEC_OID_ANSIX962_EC_PRIME192V1:
+ case SEC_OID_ANSIX962_EC_PRIME192V2:
+ case SEC_OID_ANSIX962_EC_PRIME192V3:
+ return 49; /*curve len in bytes = 24 bytes */
+ case SEC_OID_SECG_EC_SECT193R1:
+ case SEC_OID_SECG_EC_SECT193R2:
+ return 51; /*curve len in bytes = 25 bytes */
+ case SEC_OID_ANSIX962_EC_C2PNB208W1:
+ return 53; /*curve len in bytes = 26 bytes */
+ case SEC_OID_SECG_EC_SECP224K1:
+ case SEC_OID_SECG_EC_SECP224R1:
+ return 57; /*curve len in bytes = 28 bytes */
+ case SEC_OID_SECG_EC_SECT233K1:
+ case SEC_OID_SECG_EC_SECT233R1:
+ case SEC_OID_SECG_EC_SECT239K1:
+ case SEC_OID_ANSIX962_EC_PRIME239V1:
+ case SEC_OID_ANSIX962_EC_PRIME239V2:
+ case SEC_OID_ANSIX962_EC_PRIME239V3:
+ case SEC_OID_ANSIX962_EC_C2TNB239V1:
+ case SEC_OID_ANSIX962_EC_C2TNB239V2:
+ case SEC_OID_ANSIX962_EC_C2TNB239V3:
+ return 61; /*curve len in bytes = 30 bytes */
+ case SEC_OID_ANSIX962_EC_PRIME256V1:
+ case SEC_OID_SECG_EC_SECP256K1:
+ return 65; /*curve len in bytes = 32 bytes */
+ case SEC_OID_ANSIX962_EC_C2PNB272W1:
+ return 69; /*curve len in bytes = 34 bytes */
+ case SEC_OID_SECG_EC_SECT283K1:
+ case SEC_OID_SECG_EC_SECT283R1:
+ return 73; /*curve len in bytes = 36 bytes */
+ case SEC_OID_ANSIX962_EC_C2PNB304W1:
+ return 77; /*curve len in bytes = 38 bytes */
+ case SEC_OID_ANSIX962_EC_C2TNB359V1:
+ return 91; /*curve len in bytes = 45 bytes */
+ case SEC_OID_ANSIX962_EC_C2PNB368W1:
+ return 93; /*curve len in bytes = 46 bytes */
+ case SEC_OID_SECG_EC_SECP384R1:
+ return 97; /*curve len in bytes = 48 bytes */
+ case SEC_OID_SECG_EC_SECT409K1:
+ case SEC_OID_SECG_EC_SECT409R1:
+ return 105; /*curve len in bytes = 52 bytes */
+ case SEC_OID_ANSIX962_EC_C2TNB431R1:
+ return 109; /*curve len in bytes = 54 bytes */
+ case SEC_OID_SECG_EC_SECP521R1:
+ return 133; /*curve len in bytes = 66 bytes */
+ case SEC_OID_SECG_EC_SECT571K1:
+ case SEC_OID_SECG_EC_SECT571R1:
+ return 145; /*curve len in bytes = 72 bytes */
+ case SEC_OID_CURVE25519:
+ *plain = PR_TRUE;
+ return 32; /* curve len in bytes = 32 bytes (only X) */
+ /* unknown or unrecognized OIDs. return unknown length */
+ default:
+ break;
+ }
+ return 0;
+}
+
+/*
+ * returns the decoded point. In some cases the point may already be decoded.
+ * this function tries to detect those cases and return the point in
+ * publicKeyValue. In other cases it's DER encoded. In those cases the point
+ * is first decoded and returned. Space for the point is allocated out of
+ * the passed in arena.
+ */
+static CK_RV
+pk11_get_Decoded_ECPoint(PLArenaPool *arena, const SECItem *ecParams,
+ const CK_ATTRIBUTE *ecPoint, SECItem *publicKeyValue)
+{
+ SECItem encodedPublicValue;
+ SECStatus rv;
+ int keyLen;
+ PRBool plain = PR_FALSE;
+
+ if (ecPoint->ulValueLen == 0) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+ /*
+ * The PKCS #11 spec requires ecPoints to be encoded as a DER OCTET String.
+ * NSS has mistakenly passed unencoded values, and some PKCS #11 vendors
+ * followed that mistake. Now we need to detect which encoding we were
+ * passed in. The task is made more complicated by the fact the the
+ * DER encoding byte (SEC_ASN_OCTET_STRING) is the same as the
+ * EC_POINT_FORM_UNCOMPRESSED byte (0x04), so we can't use that to
+ * determine which curve we are using.
+ */
+
+ /* get the expected key length for the passed in curve.
+ * pk11_get_EC_PointLenInBytes only returns valid values for curves
+ * NSS has traditionally recognized. If the curve is not recognized,
+ * it will return '0', and we have to figure out if the key was
+ * encoded or not heuristically. If the ecParams are invalid, it
+ * will return -1 for the keyLen.
+ */
+ keyLen = pk11_get_EC_PointLenInBytes(arena, ecParams, &plain);
+ if (keyLen < 0) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+ /*
+ * Some curves are not encoded but we don't have the name here.
+ * Instead, pk11_get_EC_PointLenInBytes returns true plain if this is the
+ * case.
+ */
+ if (plain && ecPoint->ulValueLen == (unsigned int)keyLen) {
+ return pk11_Attr2SecItem(arena, ecPoint, publicKeyValue);
+ }
+
+ /* If the point is uncompressed and the lengths match, it
+ * must be an unencoded point */
+ if ((*((char *)ecPoint->pValue) == EC_POINT_FORM_UNCOMPRESSED) &&
+ (ecPoint->ulValueLen == (unsigned int)keyLen)) {
+ return pk11_Attr2SecItem(arena, ecPoint, publicKeyValue);
+ }
+
+ /* now assume the key passed to us was encoded and decode it */
+ if (*((char *)ecPoint->pValue) == SEC_ASN1_OCTET_STRING) {
+ /* OK, now let's try to decode it and see if it's valid */
+ encodedPublicValue.data = ecPoint->pValue;
+ encodedPublicValue.len = ecPoint->ulValueLen;
+ rv = SEC_QuickDERDecodeItem(arena, publicKeyValue,
+ SEC_ASN1_GET(SEC_OctetStringTemplate), &encodedPublicValue);
+
+ /* it coded correctly & we know the key length (and they match)
+ * then we are done, return the results. */
+ if (keyLen && rv == SECSuccess && publicKeyValue->len == (unsigned int)keyLen) {
+ return CKR_OK;
+ }
+
+ /* if we know the key length, one of the above tests should have
+ * succeded. If it doesn't the module gave us bad data */
+ if (keyLen) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+ /* We don't know the key length, so we don't know deterministically
+ * which encoding was used. We now will try to pick the most likely
+ * form that's correct, with a preference for the encoded form if we
+ * can't determine for sure. We do this by checking the key we got
+ * back from SEC_QuickDERDecodeItem for defects. If no defects are
+ * found, we assume the encoded parameter was was passed to us.
+ * our defect tests include:
+ * 1) it didn't decode.
+ * 2) The decode key had an invalid length (must be odd).
+ * 3) The decoded key wasn't an UNCOMPRESSED key.
+ * 4) The decoded key didn't include the entire encoded block
+ * except the DER encoding values. (fixing DER length to one
+ * particular value).
+ */
+ if ((rv != SECSuccess) || ((publicKeyValue->len & 1) != 1) ||
+ (publicKeyValue->data[0] != EC_POINT_FORM_UNCOMPRESSED) ||
+ (PORT_Memcmp(&encodedPublicValue.data[encodedPublicValue.len - publicKeyValue->len],
+ publicKeyValue->data,
+ publicKeyValue->len) != 0)) {
+ /* The decoded public key was flawed, the original key must have
+ * already been in decoded form. Do a quick sanity check then
+ * return the original key value.
+ */
+ if ((encodedPublicValue.len & 1) == 0) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+ return pk11_Attr2SecItem(arena, ecPoint, publicKeyValue);
+ }
+
+ /* as best we can figure, the passed in key was encoded, and we've
+ * now decoded it. Note: there is a chance this could be wrong if the
+ * following conditions hold:
+ * 1) The first byte or bytes of the X point looks like a valid length
+ * of precisely the right size (2*curveSize -1). this means for curves
+ * less than 512 bits (64 bytes), this will happen 1 in 256 times*.
+ * for curves between 512 and 1024, this will happen 1 in 65,536 times*
+ * for curves between 1024 and 256K this will happen 1 in 16 million*
+ * 2) The length of the 'DER length field' is odd
+ * (making both the encoded and decode
+ * values an odd length. this is true of all curves less than 512,
+ * as well as curves between 1024 and 256K).
+ * 3) The X[length of the 'DER length field'] == 0x04, 1 in 256.
+ *
+ * (* assuming all values are equally likely in the first byte,
+ * This isn't true if the curve length is not a multiple of 8. In these
+ * cases, if the DER length is possible, it's more likely,
+ * if it's not possible, then we have no false decodes).
+ *
+ * For reference here are the odds for the various curves we currently
+ * have support for (and the only curves SSL will negotiate at this
+ * time). NOTE: None of the supported curves will show up here
+ * because we return a valid length for all of these curves.
+ * The only way to get here is to have some application (not SSL)
+ * which supports some unknown curve and have some vendor supplied
+ * PKCS #11 module support that curve. NOTE: in this case, one
+ * presumes that that pkcs #11 module is likely to be using the
+ * correct encodings.
+ *
+ * Prime Curves (GFp):
+ * Bit False Odds of
+ * Size DER Len False Decode Positive
+ * 112 27 1 in 65536
+ * 128 31 1 in 65536
+ * 160 39 1 in 65536
+ * 192 47 1 in 65536
+ * 224 55 1 in 65536
+ * 239 59 1 in 32768 (top byte can only be 0-127)
+ * 256 63 1 in 65536
+ * 521 129,131 0 (decoded value would be even)
+ *
+ * Binary curves (GF2m).
+ * Bit False Odds of
+ * Size DER Len False Decode Positive
+ * 131 33 0 (top byte can only be 0-7)
+ * 163 41 0 (top byte can only be 0-7)
+ * 176 43 1 in 65536
+ * 191 47 1 in 32768 (top byte can only be 0-127)
+ * 193 49 0 (top byte can only be 0-1)
+ * 208 51 1 in 65536
+ * 233 59 0 (top byte can only be 0-1)
+ * 239 59 1 in 32768 (top byte can only be 0-127)
+ * 272 67 1 in 65536
+ * 283 71 0 (top byte can only be 0-7)
+ * 304 75 1 in 65536
+ * 359 89 1 in 32768 (top byte can only be 0-127)
+ * 368 91 1 in 65536
+ * 409 103 0 (top byte can only be 0-1)
+ * 431 107 1 in 32768 (top byte can only be 0-127)
+ * 571 129,143 0 (decoded value would be even)
+ *
+ */
+
+ return CKR_OK;
+ }
+
+ /* In theory, we should handle the case where the curve == 0 and
+ * the first byte is EC_POINT_FORM_UNCOMPRESSED, (which would be
+ * handled by doing a santity check on the key length and returning
+ * pk11_Attr2SecItem() to copy the ecPoint to the publicKeyValue).
+ *
+ * This test is unnecessary, however, due to the fact that
+ * EC_POINT_FORM_UNCOMPRESSED == SEC_ASIN1_OCTET_STRING, that case is
+ * handled in the above if. That means if we get here, the initial
+ * byte of our ecPoint value was invalid, so we can safely return.
+ * invalid attribute.
+ */
+
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+}
+
+/*
+ * extract a public key from a slot and id
+ */
+SECKEYPublicKey *
+PK11_ExtractPublicKey(PK11SlotInfo *slot, KeyType keyType, CK_OBJECT_HANDLE id)
+{
+ CK_OBJECT_CLASS keyClass = CKO_PUBLIC_KEY;
+ PLArenaPool *arena;
+ PLArenaPool *tmp_arena;
+ SECKEYPublicKey *pubKey;
+ unsigned int templateCount = 0;
+ CK_KEY_TYPE pk11KeyType;
+ CK_RV crv;
+ CK_ATTRIBUTE template[8];
+ CK_ATTRIBUTE *attrs = template;
+ CK_ATTRIBUTE *modulus, *exponent, *base, *prime, *subprime, *value;
+ CK_ATTRIBUTE *ecparams;
+
+ /* if we didn't know the key type, get it */
+ if (keyType == nullKey) {
+
+ pk11KeyType = PK11_ReadULongAttribute(slot, id, CKA_KEY_TYPE);
+ if (pk11KeyType == CK_UNAVAILABLE_INFORMATION) {
+ return NULL;
+ }
+ switch (pk11KeyType) {
+ case CKK_RSA:
+ keyType = rsaKey;
+ break;
+ case CKK_DSA:
+ keyType = dsaKey;
+ break;
+ case CKK_DH:
+ keyType = dhKey;
+ break;
+ case CKK_EC:
+ keyType = ecKey;
+ break;
+ default:
+ PORT_SetError(SEC_ERROR_BAD_KEY);
+ return NULL;
+ }
+ }
+
+ /* now we need to create space for the public key */
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL)
+ return NULL;
+ tmp_arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (tmp_arena == NULL) {
+ PORT_FreeArena(arena, PR_FALSE);
+ return NULL;
+ }
+
+ pubKey = (SECKEYPublicKey *)
+ PORT_ArenaZAlloc(arena, sizeof(SECKEYPublicKey));
+ if (pubKey == NULL) {
+ PORT_FreeArena(arena, PR_FALSE);
+ PORT_FreeArena(tmp_arena, PR_FALSE);
+ return NULL;
+ }
+
+ pubKey->arena = arena;
+ pubKey->keyType = keyType;
+ pubKey->pkcs11Slot = PK11_ReferenceSlot(slot);
+ pubKey->pkcs11ID = id;
+ PK11_SETATTRS(attrs, CKA_CLASS, &keyClass,
+ sizeof(keyClass));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_KEY_TYPE, &pk11KeyType,
+ sizeof(pk11KeyType));
+ attrs++;
+ switch (pubKey->keyType) {
+ case rsaKey:
+ modulus = attrs;
+ PK11_SETATTRS(attrs, CKA_MODULUS, NULL, 0);
+ attrs++;
+ exponent = attrs;
+ PK11_SETATTRS(attrs, CKA_PUBLIC_EXPONENT, NULL, 0);
+ attrs++;
+
+ templateCount = attrs - template;
+ PR_ASSERT(templateCount <= sizeof(template) / sizeof(CK_ATTRIBUTE));
+ crv = PK11_GetAttributes(tmp_arena, slot, id, template, templateCount);
+ if (crv != CKR_OK)
+ break;
+
+ if ((keyClass != CKO_PUBLIC_KEY) || (pk11KeyType != CKK_RSA)) {
+ crv = CKR_OBJECT_HANDLE_INVALID;
+ break;
+ }
+ crv = pk11_Attr2SecItem(arena, modulus, &pubKey->u.rsa.modulus);
+ if (crv != CKR_OK)
+ break;
+ crv = pk11_Attr2SecItem(arena, exponent, &pubKey->u.rsa.publicExponent);
+ if (crv != CKR_OK)
+ break;
+ break;
+ case dsaKey:
+ prime = attrs;
+ PK11_SETATTRS(attrs, CKA_PRIME, NULL, 0);
+ attrs++;
+ subprime = attrs;
+ PK11_SETATTRS(attrs, CKA_SUBPRIME, NULL, 0);
+ attrs++;
+ base = attrs;
+ PK11_SETATTRS(attrs, CKA_BASE, NULL, 0);
+ attrs++;
+ value = attrs;
+ PK11_SETATTRS(attrs, CKA_VALUE, NULL, 0);
+ attrs++;
+ templateCount = attrs - template;
+ PR_ASSERT(templateCount <= sizeof(template) / sizeof(CK_ATTRIBUTE));
+ crv = PK11_GetAttributes(tmp_arena, slot, id, template, templateCount);
+ if (crv != CKR_OK)
+ break;
+
+ if ((keyClass != CKO_PUBLIC_KEY) || (pk11KeyType != CKK_DSA)) {
+ crv = CKR_OBJECT_HANDLE_INVALID;
+ break;
+ }
+ crv = pk11_Attr2SecItem(arena, prime, &pubKey->u.dsa.params.prime);
+ if (crv != CKR_OK)
+ break;
+ crv = pk11_Attr2SecItem(arena, subprime, &pubKey->u.dsa.params.subPrime);
+ if (crv != CKR_OK)
+ break;
+ crv = pk11_Attr2SecItem(arena, base, &pubKey->u.dsa.params.base);
+ if (crv != CKR_OK)
+ break;
+ crv = pk11_Attr2SecItem(arena, value, &pubKey->u.dsa.publicValue);
+ if (crv != CKR_OK)
+ break;
+ break;
+ case dhKey:
+ prime = attrs;
+ PK11_SETATTRS(attrs, CKA_PRIME, NULL, 0);
+ attrs++;
+ base = attrs;
+ PK11_SETATTRS(attrs, CKA_BASE, NULL, 0);
+ attrs++;
+ value = attrs;
+ PK11_SETATTRS(attrs, CKA_VALUE, NULL, 0);
+ attrs++;
+ templateCount = attrs - template;
+ PR_ASSERT(templateCount <= sizeof(template) / sizeof(CK_ATTRIBUTE));
+ crv = PK11_GetAttributes(tmp_arena, slot, id, template, templateCount);
+ if (crv != CKR_OK)
+ break;
+
+ if ((keyClass != CKO_PUBLIC_KEY) || (pk11KeyType != CKK_DH)) {
+ crv = CKR_OBJECT_HANDLE_INVALID;
+ break;
+ }
+ crv = pk11_Attr2SecItem(arena, prime, &pubKey->u.dh.prime);
+ if (crv != CKR_OK)
+ break;
+ crv = pk11_Attr2SecItem(arena, base, &pubKey->u.dh.base);
+ if (crv != CKR_OK)
+ break;
+ crv = pk11_Attr2SecItem(arena, value, &pubKey->u.dh.publicValue);
+ if (crv != CKR_OK)
+ break;
+ break;
+ case ecKey:
+ pubKey->u.ec.size = 0;
+ ecparams = attrs;
+ PK11_SETATTRS(attrs, CKA_EC_PARAMS, NULL, 0);
+ attrs++;
+ value = attrs;
+ PK11_SETATTRS(attrs, CKA_EC_POINT, NULL, 0);
+ attrs++;
+ templateCount = attrs - template;
+ PR_ASSERT(templateCount <= sizeof(template) / sizeof(CK_ATTRIBUTE));
+ crv = PK11_GetAttributes(arena, slot, id, template, templateCount);
+ if (crv != CKR_OK)
+ break;
+
+ if ((keyClass != CKO_PUBLIC_KEY) || (pk11KeyType != CKK_EC)) {
+ crv = CKR_OBJECT_HANDLE_INVALID;
+ break;
+ }
+
+ crv = pk11_Attr2SecItem(arena, ecparams,
+ &pubKey->u.ec.DEREncodedParams);
+ if (crv != CKR_OK)
+ break;
+ pubKey->u.ec.encoding = ECPoint_Undefined;
+ crv = pk11_get_Decoded_ECPoint(arena,
+ &pubKey->u.ec.DEREncodedParams, value,
+ &pubKey->u.ec.publicValue);
+ break;
+ case fortezzaKey:
+ case nullKey:
+ default:
+ crv = CKR_OBJECT_HANDLE_INVALID;
+ break;
+ }
+
+ PORT_FreeArena(tmp_arena, PR_FALSE);
+
+ if (crv != CKR_OK) {
+ PORT_FreeArena(arena, PR_FALSE);
+ PK11_FreeSlot(slot);
+ PORT_SetError(PK11_MapError(crv));
+ return NULL;
+ }
+
+ return pubKey;
+}
+
+/*
+ * Build a Private Key structure from raw PKCS #11 information.
+ */
+SECKEYPrivateKey *
+PK11_MakePrivKey(PK11SlotInfo *slot, KeyType keyType,
+ PRBool isTemp, CK_OBJECT_HANDLE privID, void *wincx)
+{
+ PLArenaPool *arena;
+ SECKEYPrivateKey *privKey;
+ PRBool isPrivate;
+ SECStatus rv;
+
+ /* don't know? look it up */
+ if (keyType == nullKey) {
+ CK_KEY_TYPE pk11Type = CKK_RSA;
+
+ pk11Type = PK11_ReadULongAttribute(slot, privID, CKA_KEY_TYPE);
+ isTemp = (PRBool)!PK11_HasAttributeSet(slot, privID, CKA_TOKEN, PR_FALSE);
+ switch (pk11Type) {
+ case CKK_RSA:
+ keyType = rsaKey;
+ break;
+ case CKK_DSA:
+ keyType = dsaKey;
+ break;
+ case CKK_DH:
+ keyType = dhKey;
+ break;
+ case CKK_KEA:
+ keyType = fortezzaKey;
+ break;
+ case CKK_EC:
+ keyType = ecKey;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* if the key is private, make sure we are authenticated to the
+ * token before we try to use it */
+ isPrivate = (PRBool)PK11_HasAttributeSet(slot, privID, CKA_PRIVATE, PR_FALSE);
+ if (isPrivate) {
+ rv = PK11_Authenticate(slot, PR_TRUE, wincx);
+ if (rv != SECSuccess) {
+ return NULL;
+ }
+ }
+
+ /* now we need to create space for the private key */
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL)
+ return NULL;
+
+ privKey = (SECKEYPrivateKey *)
+ PORT_ArenaZAlloc(arena, sizeof(SECKEYPrivateKey));
+ if (privKey == NULL) {
+ PORT_FreeArena(arena, PR_FALSE);
+ return NULL;
+ }
+
+ privKey->arena = arena;
+ privKey->keyType = keyType;
+ privKey->pkcs11Slot = PK11_ReferenceSlot(slot);
+ privKey->pkcs11ID = privID;
+ privKey->pkcs11IsTemp = isTemp;
+ privKey->wincx = wincx;
+
+ return privKey;
+}
+
+PK11SlotInfo *
+PK11_GetSlotFromPrivateKey(SECKEYPrivateKey *key)
+{
+ PK11SlotInfo *slot = key->pkcs11Slot;
+ slot = PK11_ReferenceSlot(slot);
+ return slot;
+}
+
+/*
+ * Get the modulus length for raw parsing
+ */
+int
+PK11_GetPrivateModulusLen(SECKEYPrivateKey *key)
+{
+ CK_ATTRIBUTE theTemplate = { CKA_MODULUS, NULL, 0 };
+ PK11SlotInfo *slot = key->pkcs11Slot;
+ CK_RV crv;
+ int length;
+
+ switch (key->keyType) {
+ case rsaKey:
+ crv = PK11_GetAttributes(NULL, slot, key->pkcs11ID, &theTemplate, 1);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return -1;
+ }
+ if (theTemplate.pValue == NULL) {
+ PORT_SetError(PK11_MapError(CKR_ATTRIBUTE_VALUE_INVALID));
+ return -1;
+ }
+ length = theTemplate.ulValueLen;
+ if (*(unsigned char *)theTemplate.pValue == 0) {
+ length--;
+ }
+ PORT_Free(theTemplate.pValue);
+ return (int)length;
+
+ case fortezzaKey:
+ case dsaKey:
+ case dhKey:
+ default:
+ break;
+ }
+ if (theTemplate.pValue != NULL)
+ PORT_Free(theTemplate.pValue);
+ PORT_SetError(SEC_ERROR_INVALID_KEY);
+ return -1;
+}
+
+/*
+ * take a private key in one pkcs11 module and load it into another:
+ * NOTE: the source private key is a rare animal... it can't be sensitive.
+ * This is used to do a key gen using one pkcs11 module and storing the
+ * result into another.
+ */
+static SECKEYPrivateKey *
+pk11_loadPrivKeyWithFlags(PK11SlotInfo *slot, SECKEYPrivateKey *privKey,
+ SECKEYPublicKey *pubKey, PK11AttrFlags attrFlags)
+{
+ CK_ATTRIBUTE privTemplate[] = {
+ /* class must be first */
+ { CKA_CLASS, NULL, 0 },
+ { CKA_KEY_TYPE, NULL, 0 },
+ { CKA_ID, NULL, 0 },
+ /* RSA - the attributes below will be replaced for other
+ * key types.
+ */
+ { CKA_MODULUS, NULL, 0 },
+ { CKA_PRIVATE_EXPONENT, NULL, 0 },
+ { CKA_PUBLIC_EXPONENT, NULL, 0 },
+ { CKA_PRIME_1, NULL, 0 },
+ { CKA_PRIME_2, NULL, 0 },
+ { CKA_EXPONENT_1, NULL, 0 },
+ { CKA_EXPONENT_2, NULL, 0 },
+ { CKA_COEFFICIENT, NULL, 0 },
+ { CKA_DECRYPT, NULL, 0 },
+ { CKA_DERIVE, NULL, 0 },
+ { CKA_SIGN, NULL, 0 },
+ { CKA_SIGN_RECOVER, NULL, 0 },
+ { CKA_UNWRAP, NULL, 0 },
+ /* reserve space for the attributes that may be
+ * specified in attrFlags */
+ { CKA_TOKEN, NULL, 0 },
+ { CKA_PRIVATE, NULL, 0 },
+ { CKA_MODIFIABLE, NULL, 0 },
+ { CKA_SENSITIVE, NULL, 0 },
+ { CKA_EXTRACTABLE, NULL, 0 },
+#define NUM_RESERVED_ATTRS 5 /* number of reserved attributes above */
+ };
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_BBOOL ckfalse = CK_FALSE;
+ CK_ATTRIBUTE *attrs = NULL, *ap;
+ const int templateSize = sizeof(privTemplate) / sizeof(privTemplate[0]);
+ PLArenaPool *arena;
+ CK_OBJECT_HANDLE objectID;
+ int i, count = 0;
+ int extra_count = 0;
+ CK_RV crv;
+ SECStatus rv;
+ PRBool token = ((attrFlags & PK11_ATTR_TOKEN) != 0);
+
+ if (pk11_BadAttrFlags(attrFlags)) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ for (i = 0; i < templateSize; i++) {
+ if (privTemplate[i].type == CKA_MODULUS) {
+ attrs = &privTemplate[i];
+ count = i;
+ break;
+ }
+ }
+ PORT_Assert(attrs != NULL);
+ if (attrs == NULL) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return NULL;
+ }
+
+ ap = attrs;
+
+ switch (privKey->keyType) {
+ case rsaKey:
+ count = templateSize - NUM_RESERVED_ATTRS;
+ extra_count = count - (attrs - privTemplate);
+ break;
+ case dsaKey:
+ ap->type = CKA_PRIME;
+ ap++;
+ count++;
+ extra_count++;
+ ap->type = CKA_SUBPRIME;
+ ap++;
+ count++;
+ extra_count++;
+ ap->type = CKA_BASE;
+ ap++;
+ count++;
+ extra_count++;
+ ap->type = CKA_VALUE;
+ ap++;
+ count++;
+ extra_count++;
+ ap->type = CKA_SIGN;
+ ap++;
+ count++;
+ extra_count++;
+ break;
+ case dhKey:
+ ap->type = CKA_PRIME;
+ ap++;
+ count++;
+ extra_count++;
+ ap->type = CKA_BASE;
+ ap++;
+ count++;
+ extra_count++;
+ ap->type = CKA_VALUE;
+ ap++;
+ count++;
+ extra_count++;
+ ap->type = CKA_DERIVE;
+ ap++;
+ count++;
+ extra_count++;
+ break;
+ case ecKey:
+ ap->type = CKA_EC_PARAMS;
+ ap++;
+ count++;
+ extra_count++;
+ ap->type = CKA_VALUE;
+ ap++;
+ count++;
+ extra_count++;
+ ap->type = CKA_DERIVE;
+ ap++;
+ count++;
+ extra_count++;
+ ap->type = CKA_SIGN;
+ ap++;
+ count++;
+ extra_count++;
+ break;
+ default:
+ count = 0;
+ extra_count = 0;
+ break;
+ }
+
+ if (count == 0) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return NULL;
+ }
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL)
+ return NULL;
+ /*
+ * read out the old attributes.
+ */
+ crv = PK11_GetAttributes(arena, privKey->pkcs11Slot, privKey->pkcs11ID,
+ privTemplate, count);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ PORT_FreeArena(arena, PR_TRUE);
+ return NULL;
+ }
+
+ /* Set token, private, modifiable, sensitive, and extractable */
+ count += pk11_AttrFlagsToAttributes(attrFlags, &privTemplate[count],
+ &cktrue, &ckfalse);
+
+ /* Not everyone can handle zero padded key values, give
+ * them the raw data as unsigned. The exception is EC,
+ * where the values are encoded or zero-preserving
+ * per-RFC5915 */
+ if (privKey->keyType != ecKey) {
+ for (ap = attrs; extra_count; ap++, extra_count--) {
+ pk11_SignedToUnsigned(ap);
+ }
+ }
+
+ /* now Store the puppies */
+ rv = PK11_CreateNewObject(slot, CK_INVALID_HANDLE, privTemplate,
+ count, token, &objectID);
+ PORT_FreeArena(arena, PR_TRUE);
+ if (rv != SECSuccess) {
+ return NULL;
+ }
+
+ /* try loading the public key */
+ if (pubKey) {
+ PK11_ImportPublicKey(slot, pubKey, token);
+ if (pubKey->pkcs11Slot) {
+ PK11_FreeSlot(pubKey->pkcs11Slot);
+ pubKey->pkcs11Slot = NULL;
+ pubKey->pkcs11ID = CK_INVALID_HANDLE;
+ }
+ }
+
+ /* build new key structure */
+ return PK11_MakePrivKey(slot, privKey->keyType, !token,
+ objectID, privKey->wincx);
+}
+
+static SECKEYPrivateKey *
+pk11_loadPrivKey(PK11SlotInfo *slot, SECKEYPrivateKey *privKey,
+ SECKEYPublicKey *pubKey, PRBool token, PRBool sensitive)
+{
+ PK11AttrFlags attrFlags = 0;
+ if (token) {
+ attrFlags |= (PK11_ATTR_TOKEN | PK11_ATTR_PRIVATE);
+ } else {
+ attrFlags |= (PK11_ATTR_SESSION | PK11_ATTR_PUBLIC);
+ }
+ if (sensitive) {
+ attrFlags |= PK11_ATTR_SENSITIVE;
+ } else {
+ attrFlags |= PK11_ATTR_INSENSITIVE;
+ }
+ return pk11_loadPrivKeyWithFlags(slot, privKey, pubKey, attrFlags);
+}
+
+/*
+ * export this for PSM
+ */
+SECKEYPrivateKey *
+PK11_LoadPrivKey(PK11SlotInfo *slot, SECKEYPrivateKey *privKey,
+ SECKEYPublicKey *pubKey, PRBool token, PRBool sensitive)
+{
+ return pk11_loadPrivKey(slot, privKey, pubKey, token, sensitive);
+}
+
+/*
+ * Use the token to generate a key pair.
+ */
+SECKEYPrivateKey *
+PK11_GenerateKeyPairWithOpFlags(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
+ void *param, SECKEYPublicKey **pubKey, PK11AttrFlags attrFlags,
+ CK_FLAGS opFlags, CK_FLAGS opFlagsMask, void *wincx)
+{
+ /* we have to use these native types because when we call PKCS 11 modules
+ * we have to make sure that we are using the correct sizes for all the
+ * parameters. */
+ CK_BBOOL ckfalse = CK_FALSE;
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_ULONG modulusBits;
+ CK_BYTE publicExponent[4];
+ CK_ATTRIBUTE privTemplate[] = {
+ { CKA_SENSITIVE, NULL, 0 },
+ { CKA_TOKEN, NULL, 0 },
+ { CKA_PRIVATE, NULL, 0 },
+ { CKA_DERIVE, NULL, 0 },
+ { CKA_UNWRAP, NULL, 0 },
+ { CKA_SIGN, NULL, 0 },
+ { CKA_DECRYPT, NULL, 0 },
+ { CKA_EXTRACTABLE, NULL, 0 },
+ { CKA_MODIFIABLE, NULL, 0 },
+ };
+ CK_ATTRIBUTE rsaPubTemplate[] = {
+ { CKA_MODULUS_BITS, NULL, 0 },
+ { CKA_PUBLIC_EXPONENT, NULL, 0 },
+ { CKA_TOKEN, NULL, 0 },
+ { CKA_DERIVE, NULL, 0 },
+ { CKA_WRAP, NULL, 0 },
+ { CKA_VERIFY, NULL, 0 },
+ { CKA_VERIFY_RECOVER, NULL, 0 },
+ { CKA_ENCRYPT, NULL, 0 },
+ { CKA_MODIFIABLE, NULL, 0 },
+ };
+ CK_ATTRIBUTE dsaPubTemplate[] = {
+ { CKA_PRIME, NULL, 0 },
+ { CKA_SUBPRIME, NULL, 0 },
+ { CKA_BASE, NULL, 0 },
+ { CKA_TOKEN, NULL, 0 },
+ { CKA_DERIVE, NULL, 0 },
+ { CKA_WRAP, NULL, 0 },
+ { CKA_VERIFY, NULL, 0 },
+ { CKA_VERIFY_RECOVER, NULL, 0 },
+ { CKA_ENCRYPT, NULL, 0 },
+ { CKA_MODIFIABLE, NULL, 0 },
+ };
+ CK_ATTRIBUTE dhPubTemplate[] = {
+ { CKA_PRIME, NULL, 0 },
+ { CKA_BASE, NULL, 0 },
+ { CKA_TOKEN, NULL, 0 },
+ { CKA_DERIVE, NULL, 0 },
+ { CKA_WRAP, NULL, 0 },
+ { CKA_VERIFY, NULL, 0 },
+ { CKA_VERIFY_RECOVER, NULL, 0 },
+ { CKA_ENCRYPT, NULL, 0 },
+ { CKA_MODIFIABLE, NULL, 0 },
+ };
+ CK_ATTRIBUTE ecPubTemplate[] = {
+ { CKA_EC_PARAMS, NULL, 0 },
+ { CKA_TOKEN, NULL, 0 },
+ { CKA_DERIVE, NULL, 0 },
+ { CKA_WRAP, NULL, 0 },
+ { CKA_VERIFY, NULL, 0 },
+ { CKA_VERIFY_RECOVER, NULL, 0 },
+ { CKA_ENCRYPT, NULL, 0 },
+ { CKA_MODIFIABLE, NULL, 0 },
+ };
+ SECKEYECParams *ecParams;
+
+ /*CK_ULONG key_size = 0;*/
+ CK_ATTRIBUTE *pubTemplate;
+ int privCount = 0;
+ int pubCount = 0;
+ PK11RSAGenParams *rsaParams;
+ SECKEYPQGParams *dsaParams;
+ SECKEYDHParams *dhParams;
+ CK_MECHANISM mechanism;
+ CK_MECHANISM test_mech;
+ CK_MECHANISM test_mech2;
+ CK_SESSION_HANDLE session_handle;
+ CK_RV crv;
+ CK_OBJECT_HANDLE privID, pubID;
+ SECKEYPrivateKey *privKey;
+ KeyType keyType;
+ PRBool restore;
+ int peCount, i;
+ CK_ATTRIBUTE *attrs;
+ CK_ATTRIBUTE *privattrs;
+ CK_ATTRIBUTE setTemplate;
+ CK_MECHANISM_INFO mechanism_info;
+ CK_OBJECT_CLASS keyClass;
+ SECItem *cka_id;
+ PRBool haslock = PR_FALSE;
+ PRBool pubIsToken = PR_FALSE;
+ PRBool token = ((attrFlags & PK11_ATTR_TOKEN) != 0);
+ /* subset of attrFlags applicable to the public key */
+ PK11AttrFlags pubKeyAttrFlags = attrFlags &
+ (PK11_ATTR_TOKEN | PK11_ATTR_SESSION | PK11_ATTR_MODIFIABLE | PK11_ATTR_UNMODIFIABLE);
+
+ if (pk11_BadAttrFlags(attrFlags)) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ if (!param) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ /*
+ * The opFlags and opFlagMask parameters allow us to control the
+ * settings of the key usage attributes (CKA_ENCRYPT and friends).
+ * opFlagMask is set to one if the flag is specified in opFlags and
+ * zero if it is to take on a default value calculated by
+ * PK11_GenerateKeyPairWithOpFlags.
+ * opFlags specifies the actual value of the flag 1 or 0.
+ * Bits not corresponding to one bits in opFlagMask should be zero.
+ */
+
+ /* if we are trying to turn on a flag, it better be in the mask */
+ PORT_Assert((opFlags & ~opFlagsMask) == 0);
+ opFlags &= opFlagsMask;
+
+ PORT_Assert(slot != NULL);
+ if (slot == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MODULE);
+ return NULL;
+ }
+
+ /* if our slot really doesn't do this mechanism, Generate the key
+ * in our internal token and write it out */
+ if (!PK11_DoesMechanism(slot, type)) {
+ PK11SlotInfo *int_slot = PK11_GetInternalSlot();
+
+ /* don't loop forever looking for a slot */
+ if (slot == int_slot) {
+ PK11_FreeSlot(int_slot);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return NULL;
+ }
+
+ /* if there isn't a suitable slot, then we can't do the keygen */
+ if (int_slot == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MODULE);
+ return NULL;
+ }
+
+ /* generate the temporary key to load */
+ privKey = PK11_GenerateKeyPair(int_slot, type, param, pubKey, PR_FALSE,
+ PR_FALSE, wincx);
+ PK11_FreeSlot(int_slot);
+
+ /* if successful, load the temp key into the new token */
+ if (privKey != NULL) {
+ SECKEYPrivateKey *newPrivKey = pk11_loadPrivKeyWithFlags(slot,
+ privKey, *pubKey, attrFlags);
+ SECKEY_DestroyPrivateKey(privKey);
+ if (newPrivKey == NULL) {
+ SECKEY_DestroyPublicKey(*pubKey);
+ *pubKey = NULL;
+ }
+ return newPrivKey;
+ }
+ return NULL;
+ }
+
+ mechanism.mechanism = type;
+ mechanism.pParameter = NULL;
+ mechanism.ulParameterLen = 0;
+ test_mech.pParameter = NULL;
+ test_mech.ulParameterLen = 0;
+ test_mech2.mechanism = CKM_INVALID_MECHANISM;
+ test_mech2.pParameter = NULL;
+ test_mech2.ulParameterLen = 0;
+
+ /* set up the private key template */
+ privattrs = privTemplate;
+ privattrs += pk11_AttrFlagsToAttributes(attrFlags, privattrs,
+ &cktrue, &ckfalse);
+
+ /* set up the mechanism specific info */
+ switch (type) {
+ case CKM_RSA_PKCS_KEY_PAIR_GEN:
+ case CKM_RSA_X9_31_KEY_PAIR_GEN:
+ rsaParams = (PK11RSAGenParams *)param;
+ if (rsaParams->pe == 0) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+ modulusBits = rsaParams->keySizeInBits;
+ peCount = 0;
+
+ /* convert pe to a PKCS #11 string */
+ for (i = 0; i < 4; i++) {
+ if (peCount || (rsaParams->pe &
+ ((unsigned long)0xff000000L >> (i * 8)))) {
+ publicExponent[peCount] =
+ (CK_BYTE)((rsaParams->pe >> (3 - i) * 8) & 0xff);
+ peCount++;
+ }
+ }
+ PORT_Assert(peCount != 0);
+ attrs = rsaPubTemplate;
+ PK11_SETATTRS(attrs, CKA_MODULUS_BITS,
+ &modulusBits, sizeof(modulusBits));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_PUBLIC_EXPONENT,
+ publicExponent, peCount);
+ attrs++;
+ pubTemplate = rsaPubTemplate;
+ keyType = rsaKey;
+ test_mech.mechanism = CKM_RSA_PKCS;
+ break;
+ case CKM_DSA_KEY_PAIR_GEN:
+ dsaParams = (SECKEYPQGParams *)param;
+ attrs = dsaPubTemplate;
+ PK11_SETATTRS(attrs, CKA_PRIME, dsaParams->prime.data,
+ dsaParams->prime.len);
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_SUBPRIME, dsaParams->subPrime.data,
+ dsaParams->subPrime.len);
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_BASE, dsaParams->base.data,
+ dsaParams->base.len);
+ attrs++;
+ pubTemplate = dsaPubTemplate;
+ keyType = dsaKey;
+ test_mech.mechanism = CKM_DSA;
+ break;
+ case CKM_DH_PKCS_KEY_PAIR_GEN:
+ dhParams = (SECKEYDHParams *)param;
+ attrs = dhPubTemplate;
+ PK11_SETATTRS(attrs, CKA_PRIME, dhParams->prime.data,
+ dhParams->prime.len);
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_BASE, dhParams->base.data,
+ dhParams->base.len);
+ attrs++;
+ pubTemplate = dhPubTemplate;
+ keyType = dhKey;
+ test_mech.mechanism = CKM_DH_PKCS_DERIVE;
+ break;
+ case CKM_EC_KEY_PAIR_GEN:
+ ecParams = (SECKEYECParams *)param;
+ attrs = ecPubTemplate;
+ PK11_SETATTRS(attrs, CKA_EC_PARAMS, ecParams->data,
+ ecParams->len);
+ attrs++;
+ pubTemplate = ecPubTemplate;
+ keyType = ecKey;
+ /*
+ * ECC supports 2 different mechanism types (unlike RSA, which
+ * supports different usages with the same mechanism).
+ * We may need to query both mechanism types and or the results
+ * together -- but we only do that if either the user has
+ * requested both usages, or not specified any usages.
+ */
+ if ((opFlags & (CKF_SIGN | CKF_DERIVE)) == (CKF_SIGN | CKF_DERIVE)) {
+ /* We've explicitly turned on both flags, use both mechanism */
+ test_mech.mechanism = CKM_ECDH1_DERIVE;
+ test_mech2.mechanism = CKM_ECDSA;
+ } else if (opFlags & CKF_SIGN) {
+ /* just do signing */
+ test_mech.mechanism = CKM_ECDSA;
+ } else if (opFlags & CKF_DERIVE) {
+ /* just do ECDH */
+ test_mech.mechanism = CKM_ECDH1_DERIVE;
+ } else {
+ /* neither was specified default to both */
+ test_mech.mechanism = CKM_ECDH1_DERIVE;
+ test_mech2.mechanism = CKM_ECDSA;
+ }
+ break;
+ default:
+ PORT_SetError(SEC_ERROR_BAD_KEY);
+ return NULL;
+ }
+
+ /* now query the slot to find out how "good" a key we can generate */
+ if (!slot->isThreadSafe)
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_GetMechanismInfo(slot->slotID,
+ test_mech.mechanism, &mechanism_info);
+ /*
+ * EC keys are used in multiple different types of mechanism, if we
+ * are using dual use keys, we need to query the second mechanism
+ * as well.
+ */
+ if (test_mech2.mechanism != CKM_INVALID_MECHANISM) {
+ CK_MECHANISM_INFO mechanism_info2;
+ CK_RV crv2;
+
+ if (crv != CKR_OK) {
+ /* the first failed, make sure there is no trash in the
+ * mechanism flags when we or it below */
+ mechanism_info.flags = 0;
+ }
+ crv2 = PK11_GETTAB(slot)->C_GetMechanismInfo(slot->slotID,
+ test_mech2.mechanism, &mechanism_info2);
+ if (crv2 == CKR_OK) {
+ crv = CKR_OK; /* succeed if either mechnaism info succeeds */
+ /* combine the 2 sets of mechnanism flags */
+ mechanism_info.flags |= mechanism_info2.flags;
+ }
+ }
+ if (!slot->isThreadSafe)
+ PK11_ExitSlotMonitor(slot);
+ if ((crv != CKR_OK) || (mechanism_info.flags == 0)) {
+ /* must be old module... guess what it should be... */
+ switch (test_mech.mechanism) {
+ case CKM_RSA_PKCS:
+ mechanism_info.flags = (CKF_SIGN | CKF_DECRYPT |
+ CKF_WRAP | CKF_VERIFY_RECOVER | CKF_ENCRYPT | CKF_WRAP);
+ break;
+ case CKM_DSA:
+ mechanism_info.flags = CKF_SIGN | CKF_VERIFY;
+ break;
+ case CKM_DH_PKCS_DERIVE:
+ mechanism_info.flags = CKF_DERIVE;
+ break;
+ case CKM_ECDH1_DERIVE:
+ mechanism_info.flags = CKF_DERIVE;
+ if (test_mech2.mechanism == CKM_ECDSA) {
+ mechanism_info.flags |= CKF_SIGN | CKF_VERIFY;
+ }
+ break;
+ case CKM_ECDSA:
+ mechanism_info.flags = CKF_SIGN | CKF_VERIFY;
+ break;
+ default:
+ break;
+ }
+ }
+ /* now adjust our flags according to the user's key usage passed to us */
+ mechanism_info.flags = (mechanism_info.flags & (~opFlagsMask)) | opFlags;
+ /* set the public key attributes */
+ attrs += pk11_AttrFlagsToAttributes(pubKeyAttrFlags, attrs,
+ &cktrue, &ckfalse);
+ PK11_SETATTRS(attrs, CKA_DERIVE,
+ mechanism_info.flags & CKF_DERIVE ? &cktrue : &ckfalse,
+ sizeof(CK_BBOOL));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_WRAP,
+ mechanism_info.flags & CKF_WRAP ? &cktrue : &ckfalse,
+ sizeof(CK_BBOOL));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_VERIFY,
+ mechanism_info.flags & CKF_VERIFY ? &cktrue : &ckfalse,
+ sizeof(CK_BBOOL));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_VERIFY_RECOVER,
+ mechanism_info.flags & CKF_VERIFY_RECOVER ? &cktrue : &ckfalse,
+ sizeof(CK_BBOOL));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_ENCRYPT,
+ mechanism_info.flags & CKF_ENCRYPT ? &cktrue : &ckfalse,
+ sizeof(CK_BBOOL));
+ attrs++;
+ /* set the private key attributes */
+ PK11_SETATTRS(privattrs, CKA_DERIVE,
+ mechanism_info.flags & CKF_DERIVE ? &cktrue : &ckfalse,
+ sizeof(CK_BBOOL));
+ privattrs++;
+ PK11_SETATTRS(privattrs, CKA_UNWRAP,
+ mechanism_info.flags & CKF_UNWRAP ? &cktrue : &ckfalse,
+ sizeof(CK_BBOOL));
+ privattrs++;
+ PK11_SETATTRS(privattrs, CKA_SIGN,
+ mechanism_info.flags & CKF_SIGN ? &cktrue : &ckfalse,
+ sizeof(CK_BBOOL));
+ privattrs++;
+ PK11_SETATTRS(privattrs, CKA_DECRYPT,
+ mechanism_info.flags & CKF_DECRYPT ? &cktrue : &ckfalse,
+ sizeof(CK_BBOOL));
+ privattrs++;
+
+ if (token) {
+ session_handle = PK11_GetRWSession(slot);
+ haslock = PK11_RWSessionHasLock(slot, session_handle);
+ restore = PR_TRUE;
+ } else {
+ session_handle = slot->session;
+ if (session_handle != CK_INVALID_HANDLE)
+ PK11_EnterSlotMonitor(slot);
+ restore = PR_FALSE;
+ haslock = PR_TRUE;
+ }
+
+ if (session_handle == CK_INVALID_HANDLE) {
+ PORT_SetError(SEC_ERROR_BAD_DATA);
+ return NULL;
+ }
+ privCount = privattrs - privTemplate;
+ pubCount = attrs - pubTemplate;
+ crv = PK11_GETTAB(slot)->C_GenerateKeyPair(session_handle, &mechanism,
+ pubTemplate, pubCount, privTemplate, privCount, &pubID, &privID);
+
+ if (crv != CKR_OK) {
+ if (restore) {
+ PK11_RestoreROSession(slot, session_handle);
+ } else
+ PK11_ExitSlotMonitor(slot);
+ PORT_SetError(PK11_MapError(crv));
+ return NULL;
+ }
+ /* This locking code is dangerous and needs to be more thought
+ * out... the real problem is that we're holding the mutex open this long
+ */
+ if (haslock) {
+ PK11_ExitSlotMonitor(slot);
+ }
+
+ /* swap around the ID's for older PKCS #11 modules */
+ keyClass = PK11_ReadULongAttribute(slot, pubID, CKA_CLASS);
+ if (keyClass != CKO_PUBLIC_KEY) {
+ CK_OBJECT_HANDLE tmp = pubID;
+ pubID = privID;
+ privID = tmp;
+ }
+
+ *pubKey = PK11_ExtractPublicKey(slot, keyType, pubID);
+ if (*pubKey == NULL) {
+ if (restore) {
+ /* we may have to restore the mutex so it get's exited properly
+ * in RestoreROSession */
+ if (haslock)
+ PK11_EnterSlotMonitor(slot);
+ PK11_RestoreROSession(slot, session_handle);
+ }
+ PK11_DestroyObject(slot, pubID);
+ PK11_DestroyObject(slot, privID);
+ return NULL;
+ }
+
+ /* set the ID to the public key so we can find it again */
+ cka_id = pk11_MakeIDFromPublicKey(*pubKey);
+ pubIsToken = (PRBool)PK11_HasAttributeSet(slot, pubID, CKA_TOKEN, PR_FALSE);
+
+ PK11_SETATTRS(&setTemplate, CKA_ID, cka_id->data, cka_id->len);
+
+ if (haslock) {
+ PK11_EnterSlotMonitor(slot);
+ }
+ crv = PK11_GETTAB(slot)->C_SetAttributeValue(session_handle, privID,
+ &setTemplate, 1);
+
+ if (crv == CKR_OK && pubIsToken) {
+ crv = PK11_GETTAB(slot)->C_SetAttributeValue(session_handle, pubID,
+ &setTemplate, 1);
+ }
+
+ if (restore) {
+ PK11_RestoreROSession(slot, session_handle);
+ } else {
+ PK11_ExitSlotMonitor(slot);
+ }
+ SECITEM_FreeItem(cka_id, PR_TRUE);
+
+ if (crv != CKR_OK) {
+ PK11_DestroyObject(slot, pubID);
+ PK11_DestroyObject(slot, privID);
+ PORT_SetError(PK11_MapError(crv));
+ *pubKey = NULL;
+ return NULL;
+ }
+
+ privKey = PK11_MakePrivKey(slot, keyType, !token, privID, wincx);
+ if (privKey == NULL) {
+ SECKEY_DestroyPublicKey(*pubKey);
+ PK11_DestroyObject(slot, privID);
+ *pubKey = NULL;
+ return NULL;
+ }
+
+ return privKey;
+}
+
+SECKEYPrivateKey *
+PK11_GenerateKeyPairWithFlags(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
+ void *param, SECKEYPublicKey **pubKey, PK11AttrFlags attrFlags, void *wincx)
+{
+ return PK11_GenerateKeyPairWithOpFlags(slot, type, param, pubKey, attrFlags,
+ 0, 0, wincx);
+}
+
+/*
+ * Use the token to generate a key pair.
+ */
+SECKEYPrivateKey *
+PK11_GenerateKeyPair(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
+ void *param, SECKEYPublicKey **pubKey, PRBool token,
+ PRBool sensitive, void *wincx)
+{
+ PK11AttrFlags attrFlags = 0;
+
+ if (token) {
+ attrFlags |= PK11_ATTR_TOKEN;
+ } else {
+ attrFlags |= PK11_ATTR_SESSION;
+ }
+ if (sensitive) {
+ attrFlags |= (PK11_ATTR_SENSITIVE | PK11_ATTR_PRIVATE);
+ } else {
+ attrFlags |= (PK11_ATTR_INSENSITIVE | PK11_ATTR_PUBLIC);
+ }
+ return PK11_GenerateKeyPairWithFlags(slot, type, param, pubKey,
+ attrFlags, wincx);
+}
+
+/* build a public KEA key from the public value */
+SECKEYPublicKey *
+PK11_MakeKEAPubKey(unsigned char *keyData, int length)
+{
+ SECKEYPublicKey *pubk;
+ SECItem pkData;
+ SECStatus rv;
+ PLArenaPool *arena;
+
+ pkData.data = keyData;
+ pkData.len = length;
+ pkData.type = siBuffer;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL)
+ return NULL;
+
+ pubk = (SECKEYPublicKey *)PORT_ArenaZAlloc(arena, sizeof(SECKEYPublicKey));
+ if (pubk == NULL) {
+ PORT_FreeArena(arena, PR_FALSE);
+ return NULL;
+ }
+
+ pubk->arena = arena;
+ pubk->pkcs11Slot = 0;
+ pubk->pkcs11ID = CK_INVALID_HANDLE;
+ pubk->keyType = fortezzaKey;
+ rv = SECITEM_CopyItem(arena, &pubk->u.fortezza.KEAKey, &pkData);
+ if (rv != SECSuccess) {
+ PORT_FreeArena(arena, PR_FALSE);
+ return NULL;
+ }
+ return pubk;
+}
+
+SECStatus
+SECKEY_SetPublicValue(SECKEYPrivateKey *privKey, SECItem *publicValue)
+{
+ SECStatus rv;
+ SECKEYPublicKey pubKey;
+ PLArenaPool *arena;
+ PK11SlotInfo *slot;
+ CK_OBJECT_HANDLE privKeyID;
+
+ if (privKey == NULL || publicValue == NULL ||
+ publicValue->data == NULL || publicValue->len == 0) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ pubKey.arena = NULL;
+ pubKey.keyType = privKey->keyType;
+ pubKey.pkcs11Slot = NULL;
+ pubKey.pkcs11ID = CK_INVALID_HANDLE;
+ /* can't use PORT_InitCheapArena here becase SECKEY_DestroyPublic is used
+ * to free it, and it uses PORT_FreeArena which not only frees the
+ * underlying arena, it also frees the allocated arena struct. */
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ pubKey.arena = arena;
+ if (arena == NULL) {
+ return SECFailure;
+ }
+
+ slot = privKey->pkcs11Slot;
+ privKeyID = privKey->pkcs11ID;
+ rv = SECFailure;
+ switch (privKey->keyType) {
+ default:
+ /* error code already set to SECFailure */
+ break;
+ case rsaKey:
+ pubKey.u.rsa.modulus = *publicValue;
+ rv = PK11_ReadAttribute(slot, privKeyID, CKA_PUBLIC_EXPONENT,
+ arena, &pubKey.u.rsa.publicExponent);
+ break;
+ case dsaKey:
+ pubKey.u.dsa.publicValue = *publicValue;
+ rv = PK11_ReadAttribute(slot, privKeyID, CKA_PRIME,
+ arena, &pubKey.u.dsa.params.prime);
+ if (rv != SECSuccess) {
+ break;
+ }
+ rv = PK11_ReadAttribute(slot, privKeyID, CKA_SUBPRIME,
+ arena, &pubKey.u.dsa.params.subPrime);
+ if (rv != SECSuccess) {
+ break;
+ }
+ rv = PK11_ReadAttribute(slot, privKeyID, CKA_BASE,
+ arena, &pubKey.u.dsa.params.base);
+ break;
+ case dhKey:
+ pubKey.u.dh.publicValue = *publicValue;
+ rv = PK11_ReadAttribute(slot, privKeyID, CKA_PRIME,
+ arena, &pubKey.u.dh.prime);
+ if (rv != SECSuccess) {
+ break;
+ }
+ rv = PK11_ReadAttribute(slot, privKeyID, CKA_BASE,
+ arena, &pubKey.u.dh.base);
+ break;
+ case ecKey:
+ pubKey.u.ec.publicValue = *publicValue;
+ pubKey.u.ec.encoding = ECPoint_Undefined;
+ pubKey.u.ec.size = 0;
+ rv = PK11_ReadAttribute(slot, privKeyID, CKA_EC_PARAMS,
+ arena, &pubKey.u.ec.DEREncodedParams);
+ break;
+ }
+ if (rv == SECSuccess) {
+ rv = PK11_ImportPublicKey(slot, &pubKey, PR_TRUE);
+ }
+ /* Even though pubKey is stored on the stack, we've allocated
+ * some of it's data from the arena. SECKEY_DestroyPublicKey
+ * destroys keys by freeing the arena, so this will clean up all
+ * the data we allocated specifically for the key above. It will
+ * also free any slot references which we may have picked up in
+ * PK11_ImportPublicKey. It won't delete the underlying key if
+ * its a Token/Permanent key (which it will be if
+ * PK11_ImportPublicKey succeeds). */
+ SECKEY_DestroyPublicKey(&pubKey);
+
+ return rv;
+}
+
+/*
+ * NOTE: This function doesn't return a SECKEYPrivateKey struct to represent
+ * the new private key object. If it were to create a session object that
+ * could later be looked up by its nickname, it would leak a SECKEYPrivateKey.
+ * So isPerm must be true.
+ */
+SECStatus
+PK11_ImportEncryptedPrivateKeyInfo(PK11SlotInfo *slot,
+ SECKEYEncryptedPrivateKeyInfo *epki, SECItem *pwitem,
+ SECItem *nickname, SECItem *publicValue, PRBool isPerm,
+ PRBool isPrivate, KeyType keyType,
+ unsigned int keyUsage, void *wincx)
+{
+ if (!isPerm) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ return PK11_ImportEncryptedPrivateKeyInfoAndReturnKey(slot, epki,
+ pwitem, nickname, publicValue, isPerm, isPrivate, keyType,
+ keyUsage, NULL, wincx);
+}
+
+SECStatus
+PK11_ImportEncryptedPrivateKeyInfoAndReturnKey(PK11SlotInfo *slot,
+ SECKEYEncryptedPrivateKeyInfo *epki, SECItem *pwitem,
+ SECItem *nickname, SECItem *publicValue, PRBool isPerm,
+ PRBool isPrivate, KeyType keyType,
+ unsigned int keyUsage, SECKEYPrivateKey **privk,
+ void *wincx)
+{
+ CK_MECHANISM_TYPE pbeMechType;
+ SECItem *crypto_param = NULL;
+ PK11SymKey *key = NULL;
+ SECStatus rv = SECSuccess;
+ CK_MECHANISM_TYPE cryptoMechType;
+ SECKEYPrivateKey *privKey = NULL;
+ PRBool faulty3DES = PR_FALSE;
+ int usageCount = 0;
+ CK_KEY_TYPE key_type;
+ CK_ATTRIBUTE_TYPE *usage = NULL;
+ CK_ATTRIBUTE_TYPE rsaUsage[] = {
+ CKA_UNWRAP, CKA_DECRYPT, CKA_SIGN, CKA_SIGN_RECOVER
+ };
+ CK_ATTRIBUTE_TYPE dsaUsage[] = { CKA_SIGN };
+ CK_ATTRIBUTE_TYPE dhUsage[] = { CKA_DERIVE };
+ CK_ATTRIBUTE_TYPE ecUsage[] = { CKA_SIGN, CKA_DERIVE };
+ if ((epki == NULL) || (pwitem == NULL))
+ return SECFailure;
+
+ pbeMechType = PK11_AlgtagToMechanism(SECOID_FindOIDTag(
+ &epki->algorithm.algorithm));
+
+ switch (keyType) {
+ default:
+ case rsaKey:
+ key_type = CKK_RSA;
+ switch (keyUsage & (KU_KEY_ENCIPHERMENT | KU_DIGITAL_SIGNATURE)) {
+ case KU_KEY_ENCIPHERMENT:
+ usage = rsaUsage;
+ usageCount = 2;
+ break;
+ case KU_DIGITAL_SIGNATURE:
+ usage = &rsaUsage[2];
+ usageCount = 2;
+ break;
+ case KU_KEY_ENCIPHERMENT | KU_DIGITAL_SIGNATURE:
+ case 0: /* default to everything */
+ usage = rsaUsage;
+ usageCount = 4;
+ break;
+ }
+ break;
+ case dhKey:
+ key_type = CKK_DH;
+ usage = dhUsage;
+ usageCount = sizeof(dhUsage) / sizeof(dhUsage[0]);
+ break;
+ case dsaKey:
+ key_type = CKK_DSA;
+ usage = dsaUsage;
+ usageCount = sizeof(dsaUsage) / sizeof(dsaUsage[0]);
+ break;
+ case ecKey:
+ key_type = CKK_EC;
+ switch (keyUsage & (KU_DIGITAL_SIGNATURE | KU_KEY_AGREEMENT)) {
+ case KU_DIGITAL_SIGNATURE:
+ usage = ecUsage;
+ usageCount = 1;
+ break;
+ case KU_KEY_AGREEMENT:
+ usage = &ecUsage[1];
+ usageCount = 1;
+ break;
+ case KU_DIGITAL_SIGNATURE | KU_KEY_AGREEMENT:
+ default: /* default to everything */
+ usage = ecUsage;
+ usageCount = 2;
+ break;
+ }
+ break;
+ }
+
+try_faulty_3des:
+
+ key = PK11_PBEKeyGen(slot, &epki->algorithm, pwitem, faulty3DES, wincx);
+ if (key == NULL) {
+ rv = SECFailure;
+ goto done;
+ }
+ cryptoMechType = pk11_GetPBECryptoMechanism(&epki->algorithm,
+ &crypto_param, pwitem, faulty3DES);
+ if (cryptoMechType == CKM_INVALID_MECHANISM) {
+ rv = SECFailure;
+ goto done;
+ }
+
+ cryptoMechType = PK11_GetPadMechanism(cryptoMechType);
+
+ PORT_Assert(usage != NULL);
+ PORT_Assert(usageCount != 0);
+ privKey = PK11_UnwrapPrivKey(slot, key, cryptoMechType,
+ crypto_param, &epki->encryptedData,
+ nickname, publicValue, isPerm, isPrivate,
+ key_type, usage, usageCount, wincx);
+ if (privKey) {
+ rv = SECSuccess;
+ goto done;
+ }
+
+ /* if we are unable to import the key and the pbeMechType is
+ * CKM_NSS_PBE_SHA1_TRIPLE_DES_CBC, then it is possible that
+ * the encrypted blob was created with a buggy key generation method
+ * which is described in the PKCS 12 implementation notes. So we
+ * need to try importing via that method.
+ */
+ if ((pbeMechType == CKM_NSS_PBE_SHA1_TRIPLE_DES_CBC) && (!faulty3DES)) {
+ /* clean up after ourselves before redoing the key generation. */
+
+ PK11_FreeSymKey(key);
+ key = NULL;
+
+ if (crypto_param) {
+ SECITEM_ZfreeItem(crypto_param, PR_TRUE);
+ crypto_param = NULL;
+ }
+
+ faulty3DES = PR_TRUE;
+ goto try_faulty_3des;
+ }
+
+ /* key import really did fail */
+ rv = SECFailure;
+
+done:
+ if ((rv == SECSuccess) && isPerm) {
+ /* If we are importing a token object,
+ * create the corresponding public key.
+ * If this fails, just continue as the target
+ * token simply might not support persistant
+ * public keys. Such tokens are usable, but
+ * need to be authenticated before searching
+ * for user certs. */
+ (void)SECKEY_SetPublicValue(privKey, publicValue);
+ }
+
+ if (privKey) {
+ if (privk) {
+ *privk = privKey;
+ } else {
+ SECKEY_DestroyPrivateKey(privKey);
+ }
+ privKey = NULL;
+ }
+ if (crypto_param != NULL) {
+ SECITEM_ZfreeItem(crypto_param, PR_TRUE);
+ }
+
+ if (key != NULL) {
+ PK11_FreeSymKey(key);
+ }
+
+ return rv;
+}
+
+SECKEYPrivateKeyInfo *
+PK11_ExportPrivateKeyInfo(CERTCertificate *cert, void *wincx)
+{
+ SECKEYPrivateKeyInfo *pki = NULL;
+ SECKEYPrivateKey *pk = PK11_FindKeyByAnyCert(cert, wincx);
+ if (pk != NULL) {
+ pki = PK11_ExportPrivKeyInfo(pk, wincx);
+ SECKEY_DestroyPrivateKey(pk);
+ }
+ return pki;
+}
+
+/* V2 refers to PKCS #5 V2 here. If a PKCS #5 v1 or PKCS #12 pbe is passed
+ * for pbeTag, then encTag and hashTag are ignored. If pbe is an encryption
+ * algorithm, then PKCS #5 V2 is used with prfTag for the prf. If prfTag isn't
+ * supplied prf will be SEC_OID_HMAC_SHA1 */
+SECKEYEncryptedPrivateKeyInfo *
+PK11_ExportEncryptedPrivKeyInfoV2(
+ PK11SlotInfo *slot, /* optional, encrypt key in this slot */
+ SECOidTag pbeAlg, /* PBE algorithm to encrypt the with key */
+ SECOidTag encAlg, /* Encryption algorithm to Encrypt the key with */
+ SECOidTag prfAlg, /* Hash algorithm for PRF */
+ SECItem *pwitem, /* password for PBE encryption */
+ SECKEYPrivateKey *pk, /* encrypt this private key */
+ int iteration, /* interations for PBE alg */
+ void *pwArg) /* context for password callback */
+{
+ SECKEYEncryptedPrivateKeyInfo *epki = NULL;
+ PLArenaPool *arena = NULL;
+ SECAlgorithmID *algid;
+ SECOidTag pbeAlgTag = SEC_OID_UNKNOWN;
+ SECItem *crypto_param = NULL;
+ PK11SymKey *key = NULL;
+ SECKEYPrivateKey *tmpPK = NULL;
+ SECStatus rv = SECSuccess;
+ CK_RV crv;
+ CK_ULONG encBufLen;
+ CK_MECHANISM_TYPE pbeMechType;
+ CK_MECHANISM_TYPE cryptoMechType;
+ CK_MECHANISM cryptoMech;
+
+ if (!pwitem || !pk) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ algid = sec_pkcs5CreateAlgorithmID(pbeAlg, encAlg, prfAlg,
+ &pbeAlgTag, 0, NULL, iteration);
+ if (algid == NULL) {
+ return NULL;
+ }
+
+ arena = PORT_NewArena(2048);
+ if (arena)
+ epki = PORT_ArenaZNew(arena, SECKEYEncryptedPrivateKeyInfo);
+ if (epki == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+ epki->arena = arena;
+
+ /* if we didn't specify a slot, use the slot the private key was in */
+ if (!slot) {
+ slot = pk->pkcs11Slot;
+ }
+
+ /* if we specified a different slot, and the private key slot can do the
+ * pbe key gen, generate the key in the private key slot so we don't have
+ * to move it later */
+ pbeMechType = PK11_AlgtagToMechanism(pbeAlgTag);
+ if (slot != pk->pkcs11Slot) {
+ if (PK11_DoesMechanism(pk->pkcs11Slot, pbeMechType)) {
+ slot = pk->pkcs11Slot;
+ }
+ }
+ key = PK11_PBEKeyGen(slot, algid, pwitem, PR_FALSE, pwArg);
+ if (key == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ cryptoMechType = PK11_GetPBECryptoMechanism(algid, &crypto_param, pwitem);
+ if (cryptoMechType == CKM_INVALID_MECHANISM) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ cryptoMech.mechanism = PK11_GetPadMechanism(cryptoMechType);
+ cryptoMech.pParameter = crypto_param ? crypto_param->data : NULL;
+ cryptoMech.ulParameterLen = crypto_param ? crypto_param->len : 0;
+
+ /* If the key isn't in the private key slot, move it */
+ if (key->slot != pk->pkcs11Slot) {
+ PK11SymKey *newkey = pk11_CopyToSlot(pk->pkcs11Slot,
+ key->type, CKA_WRAP, key);
+ if (newkey == NULL) {
+ /* couldn't import the wrapping key, try exporting the
+ * private key */
+ tmpPK = pk11_loadPrivKey(key->slot, pk, NULL, PR_FALSE, PR_TRUE);
+ if (tmpPK == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+ pk = tmpPK;
+ } else {
+ /* free the old key and use the new key */
+ PK11_FreeSymKey(key);
+ key = newkey;
+ }
+ }
+
+ /* we are extracting an encrypted privateKey structure.
+ * which needs to be freed along with the buffer into which it is
+ * returned. eventually, we should retrieve an encrypted key using
+ * pkcs8/pkcs5.
+ */
+ encBufLen = 0;
+ PK11_EnterSlotMonitor(pk->pkcs11Slot);
+ crv = PK11_GETTAB(pk->pkcs11Slot)->C_WrapKey(pk->pkcs11Slot->session, &cryptoMech, key->objectID, pk->pkcs11ID, NULL, &encBufLen);
+ PK11_ExitSlotMonitor(pk->pkcs11Slot);
+ if (crv != CKR_OK) {
+ rv = SECFailure;
+ goto loser;
+ }
+ epki->encryptedData.data = PORT_ArenaAlloc(arena, encBufLen);
+ if (!epki->encryptedData.data) {
+ rv = SECFailure;
+ goto loser;
+ }
+ PK11_EnterSlotMonitor(pk->pkcs11Slot);
+ crv = PK11_GETTAB(pk->pkcs11Slot)->C_WrapKey(pk->pkcs11Slot->session, &cryptoMech, key->objectID, pk->pkcs11ID, epki->encryptedData.data, &encBufLen);
+ PK11_ExitSlotMonitor(pk->pkcs11Slot);
+ epki->encryptedData.len = (unsigned int)encBufLen;
+ if (crv != CKR_OK) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ if (!epki->encryptedData.len) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ rv = SECOID_CopyAlgorithmID(arena, &epki->algorithm, algid);
+
+loser:
+ if (crypto_param != NULL) {
+ SECITEM_ZfreeItem(crypto_param, PR_TRUE);
+ crypto_param = NULL;
+ }
+
+ if (key != NULL) {
+ PK11_FreeSymKey(key);
+ }
+ if (tmpPK != NULL) {
+ SECKEY_DestroyPrivateKey(tmpPK);
+ }
+ SECOID_DestroyAlgorithmID(algid, PR_TRUE);
+
+ if (rv == SECFailure) {
+ if (arena != NULL) {
+ PORT_FreeArena(arena, PR_TRUE);
+ }
+ epki = NULL;
+ }
+
+ return epki;
+}
+
+SECKEYEncryptedPrivateKeyInfo *
+PK11_ExportEncryptedPrivKeyInfo(
+ PK11SlotInfo *slot, /* optional, encrypt key in this slot */
+ SECOidTag algTag, /* PBE algorithm to encrypt the with key */
+ SECItem *pwitem, /* password for PBE encryption */
+ SECKEYPrivateKey *pk, /* encrypt this private key */
+ int iteration, /* interations for PBE alg */
+ void *pwArg) /* context for password callback */
+{
+ return PK11_ExportEncryptedPrivKeyInfoV2(slot, algTag, SEC_OID_UNKNOWN,
+ SEC_OID_UNKNOWN, pwitem, pk,
+ iteration, pwArg);
+}
+
+/* V2 refers to PKCS #5 V2 here. If a PKCS #5 v1 or PKCS #12 pbe is passed
+ * for pbeTag, then encTag and hashTag are ignored. If pbe is an encryption
+ * algorithm, then PKCS #5 V2 is used with prfTag for the prf. If prfTag isn't
+ * supplied prf will be SEC_OID_HMAC_SHA1 */
+SECKEYEncryptedPrivateKeyInfo *
+PK11_ExportEncryptedPrivateKeyInfoV2(
+ PK11SlotInfo *slot, /* optional, encrypt key in this slot */
+ SECOidTag pbeAlg, /* PBE algorithm to encrypt the with key */
+ SECOidTag encAlg, /* Encryption algorithm to Encrypt the key with */
+ SECOidTag prfAlg, /* HMAC algorithm for PRF*/
+ SECItem *pwitem, /* password for PBE encryption */
+ CERTCertificate *cert, /* wrap priv key for this user cert */
+ int iteration, /* interations for PBE alg */
+ void *pwArg) /* context for password callback */
+{
+ SECKEYEncryptedPrivateKeyInfo *epki = NULL;
+ SECKEYPrivateKey *pk = PK11_FindKeyByAnyCert(cert, pwArg);
+ if (pk != NULL) {
+ epki = PK11_ExportEncryptedPrivKeyInfoV2(slot, pbeAlg, encAlg, prfAlg,
+ pwitem, pk, iteration,
+ pwArg);
+ SECKEY_DestroyPrivateKey(pk);
+ }
+ return epki;
+}
+
+SECKEYEncryptedPrivateKeyInfo *
+PK11_ExportEncryptedPrivateKeyInfo(
+ PK11SlotInfo *slot, /* optional, encrypt key in this slot */
+ SECOidTag algTag, /* encrypt key with this algorithm */
+ SECItem *pwitem, /* password for PBE encryption */
+ CERTCertificate *cert, /* wrap priv key for this user cert */
+ int iteration, /* interations for PBE alg */
+ void *pwArg) /* context for password callback */
+{
+ return PK11_ExportEncryptedPrivateKeyInfoV2(slot, algTag, SEC_OID_UNKNOWN,
+ SEC_OID_UNKNOWN, pwitem, cert,
+ iteration, pwArg);
+}
+
+SECItem *
+PK11_DEREncodePublicKey(const SECKEYPublicKey *pubk)
+{
+ return SECKEY_EncodeDERSubjectPublicKeyInfo(pubk);
+}
+
+char *
+PK11_GetPrivateKeyNickname(SECKEYPrivateKey *privKey)
+{
+ return PK11_GetObjectNickname(privKey->pkcs11Slot, privKey->pkcs11ID);
+}
+
+char *
+PK11_GetPublicKeyNickname(SECKEYPublicKey *pubKey)
+{
+ return PK11_GetObjectNickname(pubKey->pkcs11Slot, pubKey->pkcs11ID);
+}
+
+SECStatus
+PK11_SetPrivateKeyNickname(SECKEYPrivateKey *privKey, const char *nickname)
+{
+ return PK11_SetObjectNickname(privKey->pkcs11Slot,
+ privKey->pkcs11ID, nickname);
+}
+
+SECStatus
+PK11_SetPublicKeyNickname(SECKEYPublicKey *pubKey, const char *nickname)
+{
+ return PK11_SetObjectNickname(pubKey->pkcs11Slot,
+ pubKey->pkcs11ID, nickname);
+}
+
+SECKEYPQGParams *
+PK11_GetPQGParamsFromPrivateKey(SECKEYPrivateKey *privKey)
+{
+ CK_ATTRIBUTE pTemplate[] = {
+ { CKA_PRIME, NULL, 0 },
+ { CKA_SUBPRIME, NULL, 0 },
+ { CKA_BASE, NULL, 0 },
+ };
+ int pTemplateLen = sizeof(pTemplate) / sizeof(pTemplate[0]);
+ PLArenaPool *arena = NULL;
+ SECKEYPQGParams *params;
+ CK_RV crv;
+
+ arena = PORT_NewArena(2048);
+ if (arena == NULL) {
+ goto loser;
+ }
+ params = (SECKEYPQGParams *)PORT_ArenaZAlloc(arena, sizeof(SECKEYPQGParams));
+ if (params == NULL) {
+ goto loser;
+ }
+
+ crv = PK11_GetAttributes(arena, privKey->pkcs11Slot, privKey->pkcs11ID,
+ pTemplate, pTemplateLen);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ goto loser;
+ }
+
+ params->arena = arena;
+ params->prime.data = pTemplate[0].pValue;
+ params->prime.len = pTemplate[0].ulValueLen;
+ params->subPrime.data = pTemplate[1].pValue;
+ params->subPrime.len = pTemplate[1].ulValueLen;
+ params->base.data = pTemplate[2].pValue;
+ params->base.len = pTemplate[2].ulValueLen;
+
+ return params;
+
+loser:
+ if (arena != NULL) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ return NULL;
+}
+
+SECKEYPrivateKey *
+PK11_CopyTokenPrivKeyToSessionPrivKey(PK11SlotInfo *destSlot,
+ SECKEYPrivateKey *privKey)
+{
+ CK_RV crv;
+ CK_OBJECT_HANDLE newKeyID;
+
+ static const CK_BBOOL ckfalse = CK_FALSE;
+ static const CK_ATTRIBUTE template[1] = {
+ { CKA_TOKEN, (CK_BBOOL *)&ckfalse, sizeof ckfalse }
+ };
+
+ if (destSlot && destSlot != privKey->pkcs11Slot) {
+ SECKEYPrivateKey *newKey =
+ pk11_loadPrivKey(destSlot,
+ privKey,
+ NULL, /* pubKey */
+ PR_FALSE, /* token */
+ PR_FALSE); /* sensitive */
+ if (newKey)
+ return newKey;
+ }
+ destSlot = privKey->pkcs11Slot;
+ PK11_Authenticate(destSlot, PR_TRUE, privKey->wincx);
+ PK11_EnterSlotMonitor(destSlot);
+ crv = PK11_GETTAB(destSlot)->C_CopyObject(destSlot->session,
+ privKey->pkcs11ID,
+ (CK_ATTRIBUTE *)template,
+ 1, &newKeyID);
+ PK11_ExitSlotMonitor(destSlot);
+
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return NULL;
+ }
+
+ return PK11_MakePrivKey(destSlot, privKey->keyType, PR_TRUE /*isTemp*/,
+ newKeyID, privKey->wincx);
+}
+
+SECKEYPrivateKey *
+PK11_ConvertSessionPrivKeyToTokenPrivKey(SECKEYPrivateKey *privk, void *wincx)
+{
+ PK11SlotInfo *slot = privk->pkcs11Slot;
+ CK_ATTRIBUTE template[1];
+ CK_ATTRIBUTE *attrs = template;
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_RV crv;
+ CK_OBJECT_HANDLE newKeyID;
+ CK_SESSION_HANDLE rwsession;
+
+ PK11_SETATTRS(attrs, CKA_TOKEN, &cktrue, sizeof(cktrue));
+ attrs++;
+
+ PK11_Authenticate(slot, PR_TRUE, wincx);
+ rwsession = PK11_GetRWSession(slot);
+ if (rwsession == CK_INVALID_HANDLE) {
+ PORT_SetError(SEC_ERROR_BAD_DATA);
+ return NULL;
+ }
+ crv = PK11_GETTAB(slot)->C_CopyObject(rwsession, privk->pkcs11ID,
+ template, 1, &newKeyID);
+ PK11_RestoreROSession(slot, rwsession);
+
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return NULL;
+ }
+
+ return PK11_MakePrivKey(slot, nullKey /*KeyType*/, PR_FALSE /*isTemp*/,
+ newKeyID, NULL /*wincx*/);
+}
+
+/*
+ * destroy a private key if there are no matching certs.
+ * this function also frees the privKey structure.
+ */
+SECStatus
+PK11_DeleteTokenPrivateKey(SECKEYPrivateKey *privKey, PRBool force)
+{
+ CERTCertificate *cert = PK11_GetCertFromPrivateKey(privKey);
+ SECStatus rv = SECWouldBlock;
+
+ if (!cert || force) {
+ /* now, then it's safe for the key to go away */
+ rv = PK11_DestroyTokenObject(privKey->pkcs11Slot, privKey->pkcs11ID);
+ }
+ if (cert) {
+ CERT_DestroyCertificate(cert);
+ }
+ SECKEY_DestroyPrivateKey(privKey);
+ return rv;
+}
+
+/*
+ * destroy a private key if there are no matching certs.
+ * this function also frees the privKey structure.
+ */
+SECStatus
+PK11_DeleteTokenPublicKey(SECKEYPublicKey *pubKey)
+{
+ /* now, then it's safe for the key to go away */
+ if (pubKey->pkcs11Slot == NULL) {
+ return SECFailure;
+ }
+ PK11_DestroyTokenObject(pubKey->pkcs11Slot, pubKey->pkcs11ID);
+ SECKEY_DestroyPublicKey(pubKey);
+ return SECSuccess;
+}
+
+/*
+ * key call back structure.
+ */
+typedef struct pk11KeyCallbackStr {
+ SECStatus (*callback)(SECKEYPrivateKey *, void *);
+ void *callbackArg;
+ void *wincx;
+} pk11KeyCallback;
+
+/*
+ * callback to map Object Handles to Private Keys;
+ */
+SECStatus
+pk11_DoKeys(PK11SlotInfo *slot, CK_OBJECT_HANDLE keyHandle, void *arg)
+{
+ SECStatus rv = SECSuccess;
+ SECKEYPrivateKey *privKey;
+ pk11KeyCallback *keycb = (pk11KeyCallback *)arg;
+ if (!arg) {
+ return SECFailure;
+ }
+
+ privKey = PK11_MakePrivKey(slot, nullKey, PR_TRUE, keyHandle, keycb->wincx);
+
+ if (privKey == NULL) {
+ return SECFailure;
+ }
+
+ if (keycb->callback) {
+ rv = (*keycb->callback)(privKey, keycb->callbackArg);
+ }
+
+ SECKEY_DestroyPrivateKey(privKey);
+ return rv;
+}
+
+/***********************************************************************
+ * PK11_TraversePrivateKeysInSlot
+ *
+ * Traverses all the private keys on a slot.
+ *
+ * INPUTS
+ * slot
+ * The PKCS #11 slot whose private keys you want to traverse.
+ * callback
+ * A callback function that will be called for each key.
+ * arg
+ * An argument that will be passed to the callback function.
+ */
+SECStatus
+PK11_TraversePrivateKeysInSlot(PK11SlotInfo *slot,
+ SECStatus (*callback)(SECKEYPrivateKey *, void *), void *arg)
+{
+ pk11KeyCallback perKeyCB;
+ pk11TraverseSlot perObjectCB;
+ CK_OBJECT_CLASS privkClass = CKO_PRIVATE_KEY;
+ CK_BBOOL ckTrue = CK_TRUE;
+ CK_ATTRIBUTE theTemplate[2];
+ int templateSize = 2;
+
+ theTemplate[0].type = CKA_CLASS;
+ theTemplate[0].pValue = &privkClass;
+ theTemplate[0].ulValueLen = sizeof(privkClass);
+ theTemplate[1].type = CKA_TOKEN;
+ theTemplate[1].pValue = &ckTrue;
+ theTemplate[1].ulValueLen = sizeof(ckTrue);
+
+ if (slot == NULL) {
+ return SECSuccess;
+ }
+
+ perObjectCB.callback = pk11_DoKeys;
+ perObjectCB.callbackArg = &perKeyCB;
+ perObjectCB.findTemplate = theTemplate;
+ perObjectCB.templateCount = templateSize;
+ perKeyCB.callback = callback;
+ perKeyCB.callbackArg = arg;
+ perKeyCB.wincx = NULL;
+
+ return PK11_TraverseSlot(slot, &perObjectCB);
+}
+
+/*
+ * return the private key with the given ID
+ */
+CK_OBJECT_HANDLE
+pk11_FindPrivateKeyFromCertID(PK11SlotInfo *slot, SECItem *keyID)
+{
+ CK_OBJECT_CLASS privKey = CKO_PRIVATE_KEY;
+ CK_ATTRIBUTE theTemplate[] = {
+ { CKA_ID, NULL, 0 },
+ { CKA_CLASS, NULL, 0 },
+ };
+ /* if you change the array, change the variable below as well */
+ int tsize = sizeof(theTemplate) / sizeof(theTemplate[0]);
+ CK_ATTRIBUTE *attrs = theTemplate;
+
+ PK11_SETATTRS(attrs, CKA_ID, keyID->data, keyID->len);
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_CLASS, &privKey, sizeof(privKey));
+
+ return pk11_FindObjectByTemplate(slot, theTemplate, tsize);
+}
+
+SECKEYPrivateKey *
+PK11_FindKeyByKeyID(PK11SlotInfo *slot, SECItem *keyID, void *wincx)
+{
+ CK_OBJECT_HANDLE keyHandle;
+ SECKEYPrivateKey *privKey;
+
+ keyHandle = pk11_FindPrivateKeyFromCertID(slot, keyID);
+ if (keyHandle == CK_INVALID_HANDLE) {
+ return NULL;
+ }
+ privKey = PK11_MakePrivKey(slot, nullKey, PR_TRUE, keyHandle, wincx);
+ return privKey;
+}
+
+/*
+ * Generate a CKA_ID from the relevant public key data. The CKA_ID is generated
+ * from the pubKeyData by SHA1_Hashing it to produce a smaller CKA_ID (to make
+ * smart cards happy.
+ */
+SECItem *
+PK11_MakeIDFromPubKey(SECItem *pubKeyData)
+{
+ PK11Context *context;
+ SECItem *certCKA_ID;
+ SECStatus rv;
+
+ if (pubKeyData->len <= SHA1_LENGTH) {
+ /* probably an already hashed value. The strongest known public
+ * key values <= 160 bits would be less than 40 bit symetric in
+ * strength. Don't hash them, just return the value. There are
+ * none at the time of this writing supported by previous versions
+ * of NSS, so change is binary compatible safe */
+ return SECITEM_DupItem(pubKeyData);
+ }
+
+ context = PK11_CreateDigestContext(SEC_OID_SHA1);
+ if (context == NULL) {
+ return NULL;
+ }
+
+ rv = PK11_DigestBegin(context);
+ if (rv == SECSuccess) {
+ rv = PK11_DigestOp(context, pubKeyData->data, pubKeyData->len);
+ }
+ if (rv != SECSuccess) {
+ PK11_DestroyContext(context, PR_TRUE);
+ return NULL;
+ }
+
+ certCKA_ID = (SECItem *)PORT_Alloc(sizeof(SECItem));
+ if (certCKA_ID == NULL) {
+ PK11_DestroyContext(context, PR_TRUE);
+ return NULL;
+ }
+
+ certCKA_ID->len = SHA1_LENGTH;
+ certCKA_ID->data = (unsigned char *)PORT_Alloc(certCKA_ID->len);
+ if (certCKA_ID->data == NULL) {
+ PORT_Free(certCKA_ID);
+ PK11_DestroyContext(context, PR_TRUE);
+ return NULL;
+ }
+
+ rv = PK11_DigestFinal(context, certCKA_ID->data, &certCKA_ID->len,
+ SHA1_LENGTH);
+ PK11_DestroyContext(context, PR_TRUE);
+ if (rv != SECSuccess) {
+ SECITEM_FreeItem(certCKA_ID, PR_TRUE);
+ return NULL;
+ }
+
+ return certCKA_ID;
+}
+
+/* Looking for PK11_GetKeyIDFromPrivateKey?
+ * Call PK11_GetLowLevelKeyIDForPrivateKey instead.
+ */
+
+SECItem *
+PK11_GetLowLevelKeyIDForPrivateKey(SECKEYPrivateKey *privKey)
+{
+ return pk11_GetLowLevelKeyFromHandle(privKey->pkcs11Slot, privKey->pkcs11ID);
+}
+
+static SECStatus
+privateKeyListCallback(SECKEYPrivateKey *key, void *arg)
+{
+ SECKEYPrivateKeyList *list = (SECKEYPrivateKeyList *)arg;
+ return SECKEY_AddPrivateKeyToListTail(list, SECKEY_CopyPrivateKey(key));
+}
+
+SECKEYPrivateKeyList *
+PK11_ListPrivateKeysInSlot(PK11SlotInfo *slot)
+{
+ SECStatus status;
+ SECKEYPrivateKeyList *keys;
+
+ keys = SECKEY_NewPrivateKeyList();
+ if (keys == NULL)
+ return NULL;
+
+ status = PK11_TraversePrivateKeysInSlot(slot, privateKeyListCallback,
+ (void *)keys);
+
+ if (status != SECSuccess) {
+ SECKEY_DestroyPrivateKeyList(keys);
+ keys = NULL;
+ }
+
+ return keys;
+}
+
+SECKEYPublicKeyList *
+PK11_ListPublicKeysInSlot(PK11SlotInfo *slot, char *nickname)
+{
+ CK_ATTRIBUTE findTemp[4];
+ CK_ATTRIBUTE *attrs;
+ CK_BBOOL ckTrue = CK_TRUE;
+ CK_OBJECT_CLASS keyclass = CKO_PUBLIC_KEY;
+ size_t tsize = 0;
+ int objCount = 0;
+ CK_OBJECT_HANDLE *key_ids;
+ SECKEYPublicKeyList *keys;
+ int i, len;
+
+ attrs = findTemp;
+ PK11_SETATTRS(attrs, CKA_CLASS, &keyclass, sizeof(keyclass));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_TOKEN, &ckTrue, sizeof(ckTrue));
+ attrs++;
+ if (nickname) {
+ len = PORT_Strlen(nickname);
+ PK11_SETATTRS(attrs, CKA_LABEL, nickname, len);
+ attrs++;
+ }
+ tsize = attrs - findTemp;
+ PORT_Assert(tsize <= sizeof(findTemp) / sizeof(CK_ATTRIBUTE));
+
+ key_ids = pk11_FindObjectsByTemplate(slot, findTemp, tsize, &objCount);
+ if (key_ids == NULL) {
+ return NULL;
+ }
+ keys = SECKEY_NewPublicKeyList();
+ if (keys == NULL) {
+ PORT_Free(key_ids);
+ return NULL;
+ }
+
+ for (i = 0; i < objCount; i++) {
+ SECKEYPublicKey *pubKey =
+ PK11_ExtractPublicKey(slot, nullKey, key_ids[i]);
+ if (pubKey) {
+ SECKEY_AddPublicKeyToListTail(keys, pubKey);
+ }
+ }
+
+ PORT_Free(key_ids);
+ return keys;
+}
+
+SECKEYPrivateKeyList *
+PK11_ListPrivKeysInSlot(PK11SlotInfo *slot, char *nickname, void *wincx)
+{
+ CK_ATTRIBUTE findTemp[4];
+ CK_ATTRIBUTE *attrs;
+ CK_BBOOL ckTrue = CK_TRUE;
+ CK_OBJECT_CLASS keyclass = CKO_PRIVATE_KEY;
+ size_t tsize = 0;
+ int objCount = 0;
+ CK_OBJECT_HANDLE *key_ids;
+ SECKEYPrivateKeyList *keys;
+ int i, len;
+
+ attrs = findTemp;
+ PK11_SETATTRS(attrs, CKA_CLASS, &keyclass, sizeof(keyclass));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_TOKEN, &ckTrue, sizeof(ckTrue));
+ attrs++;
+ if (nickname) {
+ len = PORT_Strlen(nickname);
+ PK11_SETATTRS(attrs, CKA_LABEL, nickname, len);
+ attrs++;
+ }
+ tsize = attrs - findTemp;
+ PORT_Assert(tsize <= sizeof(findTemp) / sizeof(CK_ATTRIBUTE));
+
+ key_ids = pk11_FindObjectsByTemplate(slot, findTemp, tsize, &objCount);
+ if (key_ids == NULL) {
+ return NULL;
+ }
+ keys = SECKEY_NewPrivateKeyList();
+ if (keys == NULL) {
+ PORT_Free(key_ids);
+ return NULL;
+ }
+
+ for (i = 0; i < objCount; i++) {
+ SECKEYPrivateKey *privKey =
+ PK11_MakePrivKey(slot, nullKey, PR_TRUE, key_ids[i], wincx);
+ SECKEY_AddPrivateKeyToListTail(keys, privKey);
+ }
+
+ PORT_Free(key_ids);
+ return keys;
+}
diff --git a/security/nss/lib/pk11wrap/pk11auth.c b/security/nss/lib/pk11wrap/pk11auth.c
new file mode 100644
index 0000000000..e3132e54ad
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11auth.c
@@ -0,0 +1,814 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/*
+ * This file deals with PKCS #11 passwords and authentication.
+ */
+#include "dev.h"
+#include "dev3hack.h"
+#include "seccomon.h"
+#include "secmod.h"
+#include "secmodi.h"
+#include "secmodti.h"
+#include "pkcs11t.h"
+#include "pk11func.h"
+#include "secitem.h"
+#include "secerr.h"
+
+#include "pkim.h"
+
+/*************************************************************
+ * local static and global data
+ *************************************************************/
+/*
+ * This structure keeps track of status that spans all the Slots.
+ * NOTE: This is a global data structure. It semantics expect thread crosstalk
+ * be very careful when you see it used.
+ * It's major purpose in life is to allow the user to log in one PER
+ * Tranaction, even if a transaction spans threads. The problem is the user
+ * may have to enter a password one just to be able to look at the
+ * personalities/certificates (s)he can use. Then if Auth every is one, they
+ * may have to enter the password again to use the card. See PK11_StartTransac
+ * and PK11_EndTransaction.
+ */
+static struct PK11GlobalStruct {
+ int transaction;
+ PRBool inTransaction;
+ char *(PR_CALLBACK *getPass)(PK11SlotInfo *, PRBool, void *);
+ PRBool(PR_CALLBACK *verifyPass)(PK11SlotInfo *, void *);
+ PRBool(PR_CALLBACK *isLoggedIn)(PK11SlotInfo *, void *);
+} PK11_Global = { 1, PR_FALSE, NULL, NULL, NULL };
+
+/***********************************************************
+ * Password Utilities
+ ***********************************************************/
+/*
+ * Check the user's password. Log into the card if it's correct.
+ * succeed if the user is already logged in.
+ */
+static SECStatus
+pk11_CheckPassword(PK11SlotInfo *slot, CK_SESSION_HANDLE session,
+ char *pw, PRBool alreadyLocked, PRBool contextSpecific)
+{
+ int len = 0;
+ CK_RV crv;
+ SECStatus rv;
+ PRTime currtime = PR_Now();
+ PRBool mustRetry;
+ int retry = 0;
+
+ if (slot->protectedAuthPath) {
+ len = 0;
+ pw = NULL;
+ } else if (pw == NULL) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ } else {
+ len = PORT_Strlen(pw);
+ }
+
+ do {
+ if (!alreadyLocked)
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_Login(session,
+ contextSpecific ? CKU_CONTEXT_SPECIFIC : CKU_USER,
+ (unsigned char *)pw, len);
+ slot->lastLoginCheck = 0;
+ mustRetry = PR_FALSE;
+ if (!alreadyLocked)
+ PK11_ExitSlotMonitor(slot);
+ switch (crv) {
+ /* if we're already logged in, we're good to go */
+ case CKR_OK:
+ /* TODO If it was for CKU_CONTEXT_SPECIFIC should we do this */
+ slot->authTransact = PK11_Global.transaction;
+ /* Fall through */
+ case CKR_USER_ALREADY_LOGGED_IN:
+ slot->authTime = currtime;
+ rv = SECSuccess;
+ break;
+ case CKR_PIN_INCORRECT:
+ PORT_SetError(SEC_ERROR_BAD_PASSWORD);
+ rv = SECWouldBlock; /* everything else is ok, only the pin is bad */
+ break;
+ /* someone called reset while we fetched the password, try again once
+ * if the token is still there. */
+ case CKR_SESSION_HANDLE_INVALID:
+ case CKR_SESSION_CLOSED:
+ if (session != slot->session) {
+ /* don't bother retrying, we were in a middle of an operation,
+ * which is now lost. Just fail. */
+ PORT_SetError(PK11_MapError(crv));
+ rv = SECFailure;
+ break;
+ }
+ if (retry++ == 0) {
+ rv = PK11_InitToken(slot, PR_FALSE);
+ if (rv == SECSuccess) {
+ if (slot->session != CK_INVALID_HANDLE) {
+ session = slot->session; /* we should have
+ * a new session now */
+ mustRetry = PR_TRUE;
+ } else {
+ PORT_SetError(PK11_MapError(crv));
+ rv = SECFailure;
+ }
+ }
+ break;
+ }
+ /* Fall through */
+ default:
+ PORT_SetError(PK11_MapError(crv));
+ rv = SECFailure; /* some failure we can't fix by retrying */
+ }
+ } while (mustRetry);
+ return rv;
+}
+
+/*
+ * Check the user's password. Logout before hand to make sure that
+ * we are really checking the password.
+ */
+SECStatus
+PK11_CheckUserPassword(PK11SlotInfo *slot, const char *pw)
+{
+ int len = 0;
+ CK_RV crv;
+ SECStatus rv;
+ PRTime currtime = PR_Now();
+
+ if (slot->protectedAuthPath) {
+ len = 0;
+ pw = NULL;
+ } else if (pw == NULL) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ } else {
+ len = PORT_Strlen(pw);
+ }
+
+ /*
+ * If the token doesn't need a login, don't try to relogin because the
+ * effect is undefined. It's not clear what it means to check a non-empty
+ * password with such a token, so treat that as an error.
+ */
+ if (!slot->needLogin) {
+ if (len == 0) {
+ rv = SECSuccess;
+ } else {
+ PORT_SetError(SEC_ERROR_BAD_PASSWORD);
+ rv = SECFailure;
+ }
+ return rv;
+ }
+
+ /* force a logout */
+ PK11_EnterSlotMonitor(slot);
+ PK11_GETTAB(slot)->C_Logout(slot->session);
+
+ crv = PK11_GETTAB(slot)->C_Login(slot->session, CKU_USER,
+ (unsigned char *)pw, len);
+ slot->lastLoginCheck = 0;
+ PK11_ExitSlotMonitor(slot);
+ switch (crv) {
+ /* if we're already logged in, we're good to go */
+ case CKR_OK:
+ slot->authTransact = PK11_Global.transaction;
+ slot->authTime = currtime;
+ rv = SECSuccess;
+ break;
+ case CKR_PIN_INCORRECT:
+ PORT_SetError(SEC_ERROR_BAD_PASSWORD);
+ rv = SECWouldBlock; /* everything else is ok, only the pin is bad */
+ break;
+ default:
+ PORT_SetError(PK11_MapError(crv));
+ rv = SECFailure; /* some failure we can't fix by retrying */
+ }
+ return rv;
+}
+
+SECStatus
+PK11_Logout(PK11SlotInfo *slot)
+{
+ CK_RV crv;
+
+ /* force a logout */
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_Logout(slot->session);
+ slot->lastLoginCheck = 0;
+ PK11_ExitSlotMonitor(slot);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/*
+ * transaction stuff is for when we test for the need to do every
+ * time auth to see if we already did it for this slot/transaction
+ */
+void
+PK11_StartAuthTransaction(void)
+{
+ PK11_Global.transaction++;
+ PK11_Global.inTransaction = PR_TRUE;
+}
+
+void
+PK11_EndAuthTransaction(void)
+{
+ PK11_Global.transaction++;
+ PK11_Global.inTransaction = PR_FALSE;
+}
+
+/*
+ * before we do a private key op, we check to see if we
+ * need to reauthenticate.
+ */
+void
+PK11_HandlePasswordCheck(PK11SlotInfo *slot, void *wincx)
+{
+ int askpw = slot->askpw;
+ PRBool NeedAuth = PR_FALSE;
+
+ if (!slot->needLogin)
+ return;
+
+ if ((slot->defaultFlags & PK11_OWN_PW_DEFAULTS) == 0) {
+ PK11SlotInfo *def_slot = PK11_GetInternalKeySlot();
+
+ if (def_slot) {
+ askpw = def_slot->askpw;
+ PK11_FreeSlot(def_slot);
+ }
+ }
+
+ /* timeouts are handled by isLoggedIn */
+ if (!PK11_IsLoggedIn(slot, wincx)) {
+ NeedAuth = PR_TRUE;
+ } else if (askpw == -1) {
+ if (!PK11_Global.inTransaction ||
+ (PK11_Global.transaction != slot->authTransact)) {
+ PK11_EnterSlotMonitor(slot);
+ PK11_GETTAB(slot)->C_Logout(slot->session);
+ slot->lastLoginCheck = 0;
+ PK11_ExitSlotMonitor(slot);
+ NeedAuth = PR_TRUE;
+ }
+ }
+ if (NeedAuth)
+ PK11_DoPassword(slot, slot->session, PR_TRUE,
+ wincx, PR_FALSE, PR_FALSE);
+}
+
+void
+PK11_SlotDBUpdate(PK11SlotInfo *slot)
+{
+ SECMOD_UpdateModule(slot->module);
+}
+
+/*
+ * set new askpw and timeout values
+ */
+void
+PK11_SetSlotPWValues(PK11SlotInfo *slot, int askpw, int timeout)
+{
+ slot->askpw = askpw;
+ slot->timeout = timeout;
+ slot->defaultFlags |= PK11_OWN_PW_DEFAULTS;
+ PK11_SlotDBUpdate(slot);
+}
+
+/*
+ * Get the askpw and timeout values for this slot
+ */
+void
+PK11_GetSlotPWValues(PK11SlotInfo *slot, int *askpw, int *timeout)
+{
+ *askpw = slot->askpw;
+ *timeout = slot->timeout;
+
+ if ((slot->defaultFlags & PK11_OWN_PW_DEFAULTS) == 0) {
+ PK11SlotInfo *def_slot = PK11_GetInternalKeySlot();
+
+ if (def_slot) {
+ *askpw = def_slot->askpw;
+ *timeout = def_slot->timeout;
+ PK11_FreeSlot(def_slot);
+ }
+ }
+}
+
+/*
+ * Returns true if the token is needLogin and isn't logged in.
+ * This function is used to determine if authentication is needed
+ * before attempting a potentially privelleged operation.
+ */
+PRBool
+pk11_LoginStillRequired(PK11SlotInfo *slot, void *wincx)
+{
+ return slot->needLogin && !PK11_IsLoggedIn(slot, wincx);
+}
+
+/*
+ * make sure a slot is authenticated...
+ * This function only does the authentication if it is needed.
+ */
+SECStatus
+PK11_Authenticate(PK11SlotInfo *slot, PRBool loadCerts, void *wincx)
+{
+ if (!slot) {
+ return SECFailure;
+ }
+ if (pk11_LoginStillRequired(slot, wincx)) {
+ return PK11_DoPassword(slot, slot->session, loadCerts, wincx,
+ PR_FALSE, PR_FALSE);
+ }
+ return SECSuccess;
+}
+
+/*
+ * Authenticate to "unfriendly" tokens (tokens which need to be logged
+ * in to find the certs.
+ */
+SECStatus
+pk11_AuthenticateUnfriendly(PK11SlotInfo *slot, PRBool loadCerts, void *wincx)
+{
+ SECStatus rv = SECSuccess;
+ if (!PK11_IsFriendly(slot)) {
+ rv = PK11_Authenticate(slot, loadCerts, wincx);
+ }
+ return rv;
+}
+
+/*
+ * NOTE: this assumes that we are logged out of the card before hand
+ */
+SECStatus
+PK11_CheckSSOPassword(PK11SlotInfo *slot, char *ssopw)
+{
+ CK_SESSION_HANDLE rwsession;
+ CK_RV crv;
+ SECStatus rv = SECFailure;
+ int len = 0;
+
+ /* get a rwsession */
+ rwsession = PK11_GetRWSession(slot);
+ if (rwsession == CK_INVALID_HANDLE) {
+ PORT_SetError(SEC_ERROR_BAD_DATA);
+ return rv;
+ }
+
+ if (slot->protectedAuthPath) {
+ len = 0;
+ ssopw = NULL;
+ } else if (ssopw == NULL) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ } else {
+ len = PORT_Strlen(ssopw);
+ }
+
+ /* check the password */
+ crv = PK11_GETTAB(slot)->C_Login(rwsession, CKU_SO,
+ (unsigned char *)ssopw, len);
+ slot->lastLoginCheck = 0;
+ switch (crv) {
+ /* if we're already logged in, we're good to go */
+ case CKR_OK:
+ rv = SECSuccess;
+ break;
+ case CKR_PIN_INCORRECT:
+ PORT_SetError(SEC_ERROR_BAD_PASSWORD);
+ rv = SECWouldBlock; /* everything else is ok, only the pin is bad */
+ break;
+ default:
+ PORT_SetError(PK11_MapError(crv));
+ rv = SECFailure; /* some failure we can't fix by retrying */
+ }
+ PK11_GETTAB(slot)->C_Logout(rwsession);
+ slot->lastLoginCheck = 0;
+
+ /* release rwsession */
+ PK11_RestoreROSession(slot, rwsession);
+ return rv;
+}
+
+/*
+ * make sure the password conforms to your token's requirements.
+ */
+SECStatus
+PK11_VerifyPW(PK11SlotInfo *slot, char *pw)
+{
+ int len = PORT_Strlen(pw);
+
+ if ((slot->minPassword > len) || (slot->maxPassword < len)) {
+ PORT_SetError(SEC_ERROR_BAD_DATA);
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/*
+ * initialize a user PIN Value
+ */
+SECStatus
+PK11_InitPin(PK11SlotInfo *slot, const char *ssopw, const char *userpw)
+{
+ CK_SESSION_HANDLE rwsession = CK_INVALID_HANDLE;
+ CK_RV crv;
+ SECStatus rv = SECFailure;
+ int len;
+ int ssolen;
+
+ if (userpw == NULL)
+ userpw = "";
+ if (ssopw == NULL)
+ ssopw = "";
+
+ len = PORT_Strlen(userpw);
+ ssolen = PORT_Strlen(ssopw);
+
+ /* get a rwsession */
+ rwsession = PK11_GetRWSession(slot);
+ if (rwsession == CK_INVALID_HANDLE) {
+ PORT_SetError(SEC_ERROR_BAD_DATA);
+ slot->lastLoginCheck = 0;
+ return rv;
+ }
+
+ if (slot->protectedAuthPath) {
+ len = 0;
+ ssolen = 0;
+ ssopw = NULL;
+ userpw = NULL;
+ }
+
+ /* check the password */
+ crv = PK11_GETTAB(slot)->C_Login(rwsession, CKU_SO,
+ (unsigned char *)ssopw, ssolen);
+ slot->lastLoginCheck = 0;
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ goto done;
+ }
+
+ crv = PK11_GETTAB(slot)->C_InitPIN(rwsession, (unsigned char *)userpw, len);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ } else {
+ rv = SECSuccess;
+ }
+
+done:
+ PK11_GETTAB(slot)->C_Logout(rwsession);
+ slot->lastLoginCheck = 0;
+ PK11_RestoreROSession(slot, rwsession);
+ if (rv == SECSuccess) {
+ /* update our view of the world */
+ PK11_InitToken(slot, PR_TRUE);
+ if (slot->needLogin) {
+ PK11_EnterSlotMonitor(slot);
+ PK11_GETTAB(slot)->C_Login(slot->session, CKU_USER,
+ (unsigned char *)userpw, len);
+ slot->lastLoginCheck = 0;
+ PK11_ExitSlotMonitor(slot);
+ }
+ }
+ return rv;
+}
+
+/*
+ * Change an existing user password
+ */
+SECStatus
+PK11_ChangePW(PK11SlotInfo *slot, const char *oldpw, const char *newpw)
+{
+ CK_RV crv;
+ SECStatus rv = SECFailure;
+ int newLen = 0;
+ int oldLen = 0;
+ CK_SESSION_HANDLE rwsession;
+
+ /* use NULL values to trigger the protected authentication path */
+ if (!slot->protectedAuthPath) {
+ if (newpw == NULL)
+ newpw = "";
+ if (oldpw == NULL)
+ oldpw = "";
+ }
+ if (newpw)
+ newLen = PORT_Strlen(newpw);
+ if (oldpw)
+ oldLen = PORT_Strlen(oldpw);
+
+ /* get a rwsession */
+ rwsession = PK11_GetRWSession(slot);
+ if (rwsession == CK_INVALID_HANDLE) {
+ PORT_SetError(SEC_ERROR_BAD_DATA);
+ return rv;
+ }
+
+ crv = PK11_GETTAB(slot)->C_SetPIN(rwsession,
+ (unsigned char *)oldpw, oldLen, (unsigned char *)newpw, newLen);
+ if (crv == CKR_OK) {
+ rv = SECSuccess;
+ } else {
+ PORT_SetError(PK11_MapError(crv));
+ }
+
+ PK11_RestoreROSession(slot, rwsession);
+
+ /* update our view of the world */
+ PK11_InitToken(slot, PR_TRUE);
+ return rv;
+}
+
+static char *
+pk11_GetPassword(PK11SlotInfo *slot, PRBool retry, void *wincx)
+{
+ if (PK11_Global.getPass == NULL)
+ return NULL;
+ return (*PK11_Global.getPass)(slot, retry, wincx);
+}
+
+void
+PK11_SetPasswordFunc(PK11PasswordFunc func)
+{
+ PK11_Global.getPass = func;
+}
+
+void
+PK11_SetVerifyPasswordFunc(PK11VerifyPasswordFunc func)
+{
+ PK11_Global.verifyPass = func;
+}
+
+void
+PK11_SetIsLoggedInFunc(PK11IsLoggedInFunc func)
+{
+ PK11_Global.isLoggedIn = func;
+}
+
+/*
+ * authenticate to a slot. This loops until we can't recover, the user
+ * gives up, or we succeed. If we're already logged in and this function
+ * is called we will still prompt for a password, but we will probably
+ * succeed no matter what the password was (depending on the implementation
+ * of the PKCS 11 module.
+ */
+SECStatus
+PK11_DoPassword(PK11SlotInfo *slot, CK_SESSION_HANDLE session,
+ PRBool loadCerts, void *wincx, PRBool alreadyLocked,
+ PRBool contextSpecific)
+{
+ SECStatus rv = SECFailure;
+ char *password;
+ PRBool attempt = PR_FALSE;
+
+ if (PK11_NeedUserInit(slot)) {
+ PORT_SetError(SEC_ERROR_IO);
+ return SECFailure;
+ }
+
+ /*
+ * Central server type applications which control access to multiple
+ * client applications to single crypto devices need to virtuallize the
+ * login state. This is done by a callback out of PK11_IsLoggedIn and
+ * here. If we are actually logged in, then we got here because the
+ * higher level code told us that the particular client application may
+ * still need to be logged in. If that is the case, we simply tell the
+ * server code that it should now verify the clients password and tell us
+ * the results.
+ */
+ if (PK11_IsLoggedIn(slot, NULL) &&
+ (PK11_Global.verifyPass != NULL)) {
+ if (!PK11_Global.verifyPass(slot, wincx)) {
+ PORT_SetError(SEC_ERROR_BAD_PASSWORD);
+ return SECFailure;
+ }
+ return SECSuccess;
+ }
+
+ /* get the password. This can drop out of the while loop
+ * for the following reasons:
+ * (1) the user refused to enter a password.
+ * (return error to caller)
+ * (2) the token user password is disabled [usually due to
+ * too many failed authentication attempts].
+ * (return error to caller)
+ * (3) the password was successful.
+ */
+ while ((password = pk11_GetPassword(slot, attempt, wincx)) != NULL) {
+ /* if the token has a protectedAuthPath, the application may have
+ * already issued the C_Login as part of it's pk11_GetPassword call.
+ * In this case the application will tell us what the results were in
+ * the password value (retry or the authentication was successful) so
+ * we can skip our own C_Login call (which would force the token to
+ * try to login again).
+ *
+ * Applications that don't know about protectedAuthPath will return a
+ * password, which we will ignore and trigger the token to
+ * 'authenticate' itself anyway. Hopefully the blinking display on
+ * the reader, or the flashing light under the thumbprint reader will
+ * attract the user's attention */
+ attempt = PR_TRUE;
+ if (slot->protectedAuthPath) {
+ /* application tried to authenticate and failed. it wants to try
+ * again, continue looping */
+ if (strcmp(password, PK11_PW_RETRY) == 0) {
+ rv = SECWouldBlock;
+ PORT_Free(password);
+ continue;
+ }
+ /* applicaton tried to authenticate and succeeded we're done */
+ if (strcmp(password, PK11_PW_AUTHENTICATED) == 0) {
+ rv = SECSuccess;
+ PORT_Free(password);
+ break;
+ }
+ }
+ rv = pk11_CheckPassword(slot, session, password,
+ alreadyLocked, contextSpecific);
+ PORT_Memset(password, 0, PORT_Strlen(password));
+ PORT_Free(password);
+ if (rv != SECWouldBlock)
+ break;
+ }
+ if (rv == SECSuccess) {
+ if (!contextSpecific && !PK11_IsFriendly(slot)) {
+ NSSToken *token = PK11Slot_GetNSSToken(slot);
+ if (token) {
+ nssTrustDomain_UpdateCachedTokenCerts(token->trustDomain, token);
+ (void)nssToken_Destroy(token);
+ }
+ }
+ } else if (!attempt)
+ PORT_SetError(SEC_ERROR_BAD_PASSWORD);
+ return rv;
+}
+
+void
+PK11_LogoutAll(void)
+{
+ SECMODListLock *lock = SECMOD_GetDefaultModuleListLock();
+ SECMODModuleList *modList;
+ SECMODModuleList *mlp = NULL;
+ int i;
+
+ /* NSS is not initialized, there are not tokens to log out */
+ if (lock == NULL) {
+ return;
+ }
+
+ SECMOD_GetReadLock(lock);
+ modList = SECMOD_GetDefaultModuleList();
+ /* find the number of entries */
+ for (mlp = modList; mlp != NULL; mlp = mlp->next) {
+ for (i = 0; i < mlp->module->slotCount; i++) {
+ PK11_Logout(mlp->module->slots[i]);
+ }
+ }
+
+ SECMOD_ReleaseReadLock(lock);
+}
+
+int
+PK11_GetMinimumPwdLength(PK11SlotInfo *slot)
+{
+ return ((int)slot->minPassword);
+}
+
+/* Does this slot have a protected pin path? */
+PRBool
+PK11_ProtectedAuthenticationPath(PK11SlotInfo *slot)
+{
+ return slot->protectedAuthPath;
+}
+
+/*
+ * we can initialize the password if 1) The toke is not inited
+ * (need login == true and see need UserInit) or 2) the token has
+ * a NULL password. (slot->needLogin = false & need user Init = false).
+ */
+PRBool
+PK11_NeedPWInitForSlot(PK11SlotInfo *slot)
+{
+ if (slot->needLogin && PK11_NeedUserInit(slot)) {
+ return PR_TRUE;
+ }
+ if (!slot->needLogin && !PK11_NeedUserInit(slot)) {
+ return PR_TRUE;
+ }
+ return PR_FALSE;
+}
+
+PRBool
+PK11_NeedPWInit()
+{
+ PK11SlotInfo *slot = PK11_GetInternalKeySlot();
+ PRBool ret = PR_FALSE;
+ if (slot) {
+ ret = PK11_NeedPWInitForSlot(slot);
+ PK11_FreeSlot(slot);
+ }
+ return ret;
+}
+
+PRBool
+pk11_InDelayPeriod(PRIntervalTime lastTime, PRIntervalTime delayTime,
+ PRIntervalTime *retTime)
+{
+ PRIntervalTime time;
+
+ *retTime = time = PR_IntervalNow();
+ return (PRBool)(lastTime) && ((time - lastTime) < delayTime);
+}
+
+/*
+ * Determine if the token is logged in. We have to actually query the token,
+ * because it's state can change without intervention from us.
+ */
+PRBool
+PK11_IsLoggedIn(PK11SlotInfo *slot, void *wincx)
+{
+ CK_SESSION_INFO sessionInfo;
+ int askpw = slot->askpw;
+ int timeout = slot->timeout;
+ CK_RV crv;
+ PRIntervalTime curTime;
+ static PRIntervalTime login_delay_time = 0;
+
+ if (login_delay_time == 0) {
+ login_delay_time = PR_SecondsToInterval(1);
+ }
+
+ /* If we don't have our own password default values, use the system
+ * ones */
+ if ((slot->defaultFlags & PK11_OWN_PW_DEFAULTS) == 0) {
+ PK11SlotInfo *def_slot = PK11_GetInternalKeySlot();
+
+ if (def_slot) {
+ askpw = def_slot->askpw;
+ timeout = def_slot->timeout;
+ PK11_FreeSlot(def_slot);
+ }
+ }
+
+ if ((wincx != NULL) && (PK11_Global.isLoggedIn != NULL) &&
+ (*PK11_Global.isLoggedIn)(slot, wincx) == PR_FALSE) {
+ return PR_FALSE;
+ }
+
+ /* forget the password if we've been inactive too long */
+ if (askpw == 1) {
+ PRTime currtime = PR_Now();
+ PRTime result;
+ PRTime mult;
+
+ LL_I2L(result, timeout);
+ LL_I2L(mult, 60 * 1000 * 1000);
+ LL_MUL(result, result, mult);
+ LL_ADD(result, result, slot->authTime);
+ if (LL_CMP(result, <, currtime)) {
+ PK11_EnterSlotMonitor(slot);
+ PK11_GETTAB(slot)->C_Logout(slot->session);
+ slot->lastLoginCheck = 0;
+ PK11_ExitSlotMonitor(slot);
+ } else {
+ slot->authTime = currtime;
+ }
+ }
+
+ PK11_EnterSlotMonitor(slot);
+ if (pk11_InDelayPeriod(slot->lastLoginCheck, login_delay_time, &curTime)) {
+ sessionInfo.state = slot->lastState;
+ crv = CKR_OK;
+ } else {
+ crv = PK11_GETTAB(slot)->C_GetSessionInfo(slot->session, &sessionInfo);
+ if (crv == CKR_OK) {
+ slot->lastState = sessionInfo.state;
+ slot->lastLoginCheck = curTime;
+ }
+ }
+ PK11_ExitSlotMonitor(slot);
+ /* if we can't get session info, something is really wrong */
+ if (crv != CKR_OK) {
+ slot->session = CK_INVALID_HANDLE;
+ return PR_FALSE;
+ }
+
+ switch (sessionInfo.state) {
+ case CKS_RW_PUBLIC_SESSION:
+ case CKS_RO_PUBLIC_SESSION:
+ default:
+ break; /* fail */
+ case CKS_RW_USER_FUNCTIONS:
+ case CKS_RW_SO_FUNCTIONS:
+ case CKS_RO_USER_FUNCTIONS:
+ return PR_TRUE;
+ }
+ return PR_FALSE;
+}
diff --git a/security/nss/lib/pk11wrap/pk11cert.c b/security/nss/lib/pk11wrap/pk11cert.c
new file mode 100644
index 0000000000..fd36e1979b
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11cert.c
@@ -0,0 +1,2982 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/*
+ * This file manages PKCS #11 instances of certificates.
+ */
+
+#include <stddef.h>
+
+#include "secport.h"
+#include "seccomon.h"
+#include "secmod.h"
+#include "secmodi.h"
+#include "secmodti.h"
+#include "pkcs11.h"
+#include "pk11func.h"
+#include "cert.h"
+#include "certi.h"
+#include "secitem.h"
+#include "keyhi.h"
+#include "secoid.h"
+#include "pkcs7t.h"
+#include "cmsreclist.h"
+
+#include "certdb.h"
+#include "secerr.h"
+#include "sslerr.h"
+
+#include "pki3hack.h"
+#include "dev3hack.h"
+
+#include "devm.h"
+#include "nsspki.h"
+#include "pki.h"
+#include "pkim.h"
+#include "pkitm.h"
+#include "pkistore.h" /* to remove temp cert */
+#include "devt.h"
+#include "ckhelper.h"
+#include "pkcs11uri.h"
+
+extern const NSSError NSS_ERROR_NOT_FOUND;
+extern const NSSError NSS_ERROR_INVALID_CERTIFICATE;
+
+struct nss3_cert_cbstr {
+ SECStatus (*callback)(CERTCertificate *, void *);
+ nssList *cached;
+ void *arg;
+};
+
+/* Translate from NSSCertificate to CERTCertificate, then pass the latter
+ * to a callback.
+ */
+static PRStatus
+convert_cert(NSSCertificate *c, void *arg)
+{
+ CERTCertificate *nss3cert;
+ SECStatus secrv;
+ struct nss3_cert_cbstr *nss3cb = (struct nss3_cert_cbstr *)arg;
+ /* 'c' is not adopted. caller will free it */
+ nss3cert = STAN_GetCERTCertificate(c);
+ if (!nss3cert)
+ return PR_FAILURE;
+ secrv = (*nss3cb->callback)(nss3cert, nss3cb->arg);
+ return (secrv) ? PR_FAILURE : PR_SUCCESS;
+}
+
+/*
+ * build a cert nickname based on the token name and the label of the
+ * certificate If the label in NULL, build a label based on the ID.
+ */
+static int
+toHex(int x)
+{
+ return (x < 10) ? (x + '0') : (x + 'a' - 10);
+}
+#define MAX_CERT_ID 4
+#define DEFAULT_STRING "Cert ID "
+static char *
+pk11_buildNickname(PK11SlotInfo *slot, CK_ATTRIBUTE *cert_label,
+ CK_ATTRIBUTE *key_label, CK_ATTRIBUTE *cert_id)
+{
+ int prefixLen = PORT_Strlen(slot->token_name);
+ int suffixLen = 0;
+ char *suffix = NULL;
+ char buildNew[sizeof(DEFAULT_STRING) + MAX_CERT_ID * 2];
+ char *next, *nickname;
+
+ if (cert_label && (cert_label->ulValueLen)) {
+ suffixLen = cert_label->ulValueLen;
+ suffix = (char *)cert_label->pValue;
+ } else if (key_label && (key_label->ulValueLen)) {
+ suffixLen = key_label->ulValueLen;
+ suffix = (char *)key_label->pValue;
+ } else if (cert_id && cert_id->ulValueLen > 0) {
+ int i, first = cert_id->ulValueLen - MAX_CERT_ID;
+ int offset = sizeof(DEFAULT_STRING);
+ char *idValue = (char *)cert_id->pValue;
+
+ PORT_Memcpy(buildNew, DEFAULT_STRING, sizeof(DEFAULT_STRING) - 1);
+ next = buildNew + offset;
+ if (first < 0)
+ first = 0;
+ for (i = first; i < (int)cert_id->ulValueLen; i++) {
+ *next++ = toHex((idValue[i] >> 4) & 0xf);
+ *next++ = toHex(idValue[i] & 0xf);
+ }
+ *next++ = 0;
+ suffix = buildNew;
+ suffixLen = PORT_Strlen(buildNew);
+ } else {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return NULL;
+ }
+
+ /* if is internal key slot, add code to skip the prefix!! */
+ next = nickname = (char *)PORT_Alloc(prefixLen + 1 + suffixLen + 1);
+ if (nickname == NULL)
+ return NULL;
+
+ PORT_Memcpy(next, slot->token_name, prefixLen);
+ next += prefixLen;
+ *next++ = ':';
+ PORT_Memcpy(next, suffix, suffixLen);
+ next += suffixLen;
+ *next++ = 0;
+ return nickname;
+}
+
+PRBool
+PK11_IsUserCert(PK11SlotInfo *slot, CERTCertificate *cert,
+ CK_OBJECT_HANDLE certID)
+{
+ CK_OBJECT_CLASS theClass;
+
+ if (slot == NULL)
+ return PR_FALSE;
+ if (cert == NULL)
+ return PR_FALSE;
+
+ theClass = CKO_PRIVATE_KEY;
+ if (pk11_LoginStillRequired(slot, NULL)) {
+ theClass = CKO_PUBLIC_KEY;
+ }
+ if (PK11_MatchItem(slot, certID, theClass) != CK_INVALID_HANDLE) {
+ return PR_TRUE;
+ }
+
+ if (theClass == CKO_PUBLIC_KEY) {
+ SECKEYPublicKey *pubKey = CERT_ExtractPublicKey(cert);
+ CK_ATTRIBUTE theTemplate;
+
+ if (pubKey == NULL) {
+ return PR_FALSE;
+ }
+
+ PK11_SETATTRS(&theTemplate, 0, NULL, 0);
+ switch (pubKey->keyType) {
+ case rsaKey:
+ case rsaPssKey:
+ case rsaOaepKey:
+ PK11_SETATTRS(&theTemplate, CKA_MODULUS, pubKey->u.rsa.modulus.data,
+ pubKey->u.rsa.modulus.len);
+ break;
+ case dsaKey:
+ PK11_SETATTRS(&theTemplate, CKA_VALUE, pubKey->u.dsa.publicValue.data,
+ pubKey->u.dsa.publicValue.len);
+ break;
+ case dhKey:
+ PK11_SETATTRS(&theTemplate, CKA_VALUE, pubKey->u.dh.publicValue.data,
+ pubKey->u.dh.publicValue.len);
+ break;
+ case ecKey:
+ PK11_SETATTRS(&theTemplate, CKA_EC_POINT,
+ pubKey->u.ec.publicValue.data,
+ pubKey->u.ec.publicValue.len);
+ break;
+ case keaKey:
+ case fortezzaKey:
+ case nullKey:
+ /* fall through and return false */
+ break;
+ }
+
+ if (theTemplate.ulValueLen == 0) {
+ SECKEY_DestroyPublicKey(pubKey);
+ return PR_FALSE;
+ }
+ if (pubKey->keyType != ecKey) {
+ pk11_SignedToUnsigned(&theTemplate);
+ }
+ if (pk11_FindObjectByTemplate(slot, &theTemplate, 1) != CK_INVALID_HANDLE) {
+ SECKEY_DestroyPublicKey(pubKey);
+ return PR_TRUE;
+ }
+ SECKEY_DestroyPublicKey(pubKey);
+ }
+ return PR_FALSE;
+}
+
+/*
+ * Check out if a cert has ID of zero. This is a magic ID that tells
+ * NSS that this cert may be an automagically trusted cert.
+ * The Cert has to be self signed as well. That check is done elsewhere.
+ *
+ */
+PRBool
+pk11_isID0(PK11SlotInfo *slot, CK_OBJECT_HANDLE certID)
+{
+ CK_ATTRIBUTE keyID = { CKA_ID, NULL, 0 };
+ PRBool isZero = PR_FALSE;
+ int i;
+ CK_RV crv;
+
+ crv = PK11_GetAttributes(NULL, slot, certID, &keyID, 1);
+ if (crv != CKR_OK) {
+ return isZero;
+ }
+
+ if (keyID.ulValueLen != 0) {
+ char *value = (char *)keyID.pValue;
+ isZero = PR_TRUE; /* ID exists, may be zero */
+ for (i = 0; i < (int)keyID.ulValueLen; i++) {
+ if (value[i] != 0) {
+ isZero = PR_FALSE; /* nope */
+ break;
+ }
+ }
+ }
+ PORT_Free(keyID.pValue);
+ return isZero;
+}
+
+/*
+ * Create an NSSCertificate from a slot/certID pair, return it as a
+ * CERTCertificate. Optionally, output the nickname string.
+ */
+static CERTCertificate *
+pk11_fastCert(PK11SlotInfo *slot, CK_OBJECT_HANDLE certID,
+ CK_ATTRIBUTE *privateLabel, char **nickptr)
+{
+ NSSCertificate *c;
+ nssCryptokiObject *co = NULL;
+ nssPKIObject *pkio;
+ NSSTrustDomain *td = STAN_GetDefaultTrustDomain();
+
+ /* Get the cryptoki object from the handle */
+ NSSToken *token = PK11Slot_GetNSSToken(slot);
+ if (!token || !token->defaultSession) {
+ (void)nssToken_Destroy(token); /* null token is ok */
+ PORT_SetError(SEC_ERROR_NO_TOKEN);
+ return NULL;
+ }
+ co = nssCryptokiObject_Create(token, token->defaultSession, certID);
+ (void)nssToken_Destroy(token);
+ if (!co) {
+ return NULL;
+ }
+
+ /* Create a PKI object from the cryptoki instance */
+ pkio = nssPKIObject_Create(NULL, co, td, NULL, nssPKIMonitor);
+ if (!pkio) {
+ nssCryptokiObject_Destroy(co);
+ return NULL;
+ }
+
+ /* Create a certificate */
+ c = nssCertificate_Create(pkio);
+ if (!c) {
+ nssPKIObject_Destroy(pkio);
+ return NULL;
+ }
+
+ /* Build and output a nickname, if desired.
+ * This must be done before calling nssTrustDomain_AddCertsToCache
+ * because that function may destroy c, pkio and co!
+ */
+ if ((nickptr) && (co->label)) {
+ CK_ATTRIBUTE label, id;
+
+ label.type = CKA_LABEL;
+ label.pValue = co->label;
+ label.ulValueLen = PORT_Strlen(co->label);
+
+ id.type = CKA_ID;
+ id.pValue = c->id.data;
+ id.ulValueLen = c->id.size;
+
+ *nickptr = pk11_buildNickname(slot, &label, privateLabel, &id);
+ }
+
+ /* This function may destroy the cert in "c" and all its subordinate
+ * structures, and replace the value in "c" with the address of a
+ * different NSSCertificate that it found in the cache.
+ * Presumably, the nickname which we just output above remains valid. :)
+ */
+ (void)nssTrustDomain_AddCertsToCache(td, &c, 1);
+ return STAN_GetCERTCertificateOrRelease(c);
+}
+
+/*
+ * Build an CERTCertificate structure from a PKCS#11 object ID.... certID
+ * Must be a CertObject. This code does not explicitly checks that.
+ */
+CERTCertificate *
+PK11_MakeCertFromHandle(PK11SlotInfo *slot, CK_OBJECT_HANDLE certID,
+ CK_ATTRIBUTE *privateLabel)
+{
+ char *nickname = NULL;
+ CERTCertificate *cert = NULL;
+ CERTCertTrust *trust;
+
+ if (slot == NULL || certID == CK_INVALID_HANDLE) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ cert = pk11_fastCert(slot, certID, privateLabel, &nickname);
+ if (cert == NULL) {
+ goto loser;
+ }
+
+ if (nickname) {
+ if (cert->nickname != NULL) {
+ cert->dbnickname = cert->nickname;
+ }
+ cert->nickname = PORT_ArenaStrdup(cert->arena, nickname);
+ PORT_Free(nickname);
+ nickname = NULL;
+ }
+
+ /* remember where this cert came from.... If we have just looked
+ * it up from the database and it already has a slot, don't add a new
+ * one. */
+ if (cert->slot == NULL) {
+ cert->slot = PK11_ReferenceSlot(slot);
+ cert->pkcs11ID = certID;
+ cert->ownSlot = PR_TRUE;
+ cert->series = slot->series;
+ }
+
+ trust = (CERTCertTrust *)PORT_ArenaAlloc(cert->arena, sizeof(CERTCertTrust));
+ if (trust == NULL)
+ goto loser;
+ PORT_Memset(trust, 0, sizeof(CERTCertTrust));
+
+ if (!pk11_HandleTrustObject(slot, cert, trust)) {
+ unsigned int type;
+
+ /* build some cert trust flags */
+ if (CERT_IsCACert(cert, &type)) {
+ unsigned int trustflags = CERTDB_VALID_CA;
+
+ /* Allow PKCS #11 modules to give us trusted CA's. We only accept
+ * valid CA's which are self-signed here. They must have an object
+ * ID of '0'. */
+ if (pk11_isID0(slot, certID) &&
+ cert->isRoot) {
+ trustflags |= CERTDB_TRUSTED_CA;
+ /* is the slot a fortezza card? allow the user or
+ * admin to turn on objectSigning, but don't turn
+ * full trust on explicitly */
+ if (PK11_DoesMechanism(slot, CKM_KEA_KEY_DERIVE)) {
+ trust->objectSigningFlags |= CERTDB_VALID_CA;
+ }
+ }
+ if ((type & NS_CERT_TYPE_SSL_CA) == NS_CERT_TYPE_SSL_CA) {
+ trust->sslFlags |= trustflags;
+ }
+ if ((type & NS_CERT_TYPE_EMAIL_CA) == NS_CERT_TYPE_EMAIL_CA) {
+ trust->emailFlags |= trustflags;
+ }
+ if ((type & NS_CERT_TYPE_OBJECT_SIGNING_CA) == NS_CERT_TYPE_OBJECT_SIGNING_CA) {
+ trust->objectSigningFlags |= trustflags;
+ }
+ }
+ }
+
+ if (PK11_IsUserCert(slot, cert, certID)) {
+ trust->sslFlags |= CERTDB_USER;
+ trust->emailFlags |= CERTDB_USER;
+ /* trust->objectSigningFlags |= CERTDB_USER; */
+ }
+ CERT_LockCertTrust(cert);
+ cert->trust = trust;
+ CERT_UnlockCertTrust(cert);
+
+ return cert;
+
+loser:
+ if (nickname)
+ PORT_Free(nickname);
+ if (cert)
+ CERT_DestroyCertificate(cert);
+ return NULL;
+}
+
+/*
+ * Build get a certificate from a private key
+ */
+CERTCertificate *
+PK11_GetCertFromPrivateKey(SECKEYPrivateKey *privKey)
+{
+ PK11SlotInfo *slot = privKey->pkcs11Slot;
+ CK_OBJECT_HANDLE handle = privKey->pkcs11ID;
+ CK_OBJECT_HANDLE certID =
+ PK11_MatchItem(slot, handle, CKO_CERTIFICATE);
+ CERTCertificate *cert;
+
+ if (certID == CK_INVALID_HANDLE) {
+ PORT_SetError(SSL_ERROR_NO_CERTIFICATE);
+ return NULL;
+ }
+ cert = PK11_MakeCertFromHandle(slot, certID, NULL);
+ return (cert);
+}
+
+CK_OBJECT_HANDLE *
+PK11_FindCertHandlesForKeyHandle(PK11SlotInfo *slot, CK_OBJECT_HANDLE keyHandle,
+ int *certHandleCountOut)
+{
+ if (!slot || !certHandleCountOut || keyHandle == CK_INVALID_HANDLE) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ PORTCheapArenaPool arena;
+ PORT_InitCheapArena(&arena, DER_DEFAULT_CHUNKSIZE);
+ CK_ATTRIBUTE idTemplate[] = {
+ { CKA_ID, NULL, 0 },
+ };
+ const int idAttrCount = sizeof(idTemplate) / sizeof(idTemplate[0]);
+ CK_RV crv = PK11_GetAttributes(&arena.arena, slot, keyHandle, idTemplate, idAttrCount);
+ if (crv != CKR_OK) {
+ PORT_DestroyCheapArena(&arena);
+ PORT_SetError(PK11_MapError(crv));
+ return NULL;
+ }
+
+ if ((idTemplate[0].ulValueLen == 0) || (idTemplate[0].ulValueLen == -1)) {
+ PORT_DestroyCheapArena(&arena);
+ PORT_SetError(SEC_ERROR_BAD_KEY);
+ return NULL;
+ }
+
+ CK_OBJECT_CLASS searchClass = CKO_CERTIFICATE;
+ CK_ATTRIBUTE searchTemplate[] = {
+ idTemplate[0],
+ { CKA_CLASS, &searchClass, sizeof(searchClass) }
+ };
+ const size_t searchAttrCount = sizeof(searchTemplate) / sizeof(searchTemplate[0]);
+ CK_OBJECT_HANDLE *ids = pk11_FindObjectsByTemplate(slot, searchTemplate, searchAttrCount, certHandleCountOut);
+
+ PORT_DestroyCheapArena(&arena);
+ return ids;
+}
+
+CERTCertList *
+PK11_GetCertsMatchingPrivateKey(SECKEYPrivateKey *privKey)
+{
+ if (!privKey) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+ CERTCertList *certs = CERT_NewCertList();
+ if (!certs) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+ PK11SlotInfo *slot = privKey->pkcs11Slot;
+ CK_OBJECT_HANDLE handle = privKey->pkcs11ID;
+ CK_OBJECT_HANDLE certID = PK11_MatchItem(slot, handle, CKO_CERTIFICATE);
+ /* If we can't get a matching certID, there are no matching certificates,
+ * which is not an error. */
+ if (certID == CK_INVALID_HANDLE) {
+ return certs;
+ }
+ int certHandleCount = 0;
+ CK_OBJECT_HANDLE *certHandles = PK11_FindCertHandlesForKeyHandle(slot, handle, &certHandleCount);
+ if (!certHandles) {
+ /* If certHandleCount is 0, there are no matching certificates, which is
+ * not an error. */
+ if (certHandleCount == 0) {
+ return certs;
+ }
+ CERT_DestroyCertList(certs);
+ return NULL;
+ }
+ int i;
+ for (i = 0; i < certHandleCount; i++) {
+ CERTCertificate *cert = PK11_MakeCertFromHandle(slot, certHandles[i], NULL);
+ /* If PK11_MakeCertFromHandle fails for one handle, optimistically
+ assume other handles may succeed (i.e. this is best-effort). */
+ if (!cert) {
+ continue;
+ }
+ if (CERT_AddCertToListTail(certs, cert) != SECSuccess) {
+ CERT_DestroyCertificate(cert);
+ }
+ }
+ PORT_Free(certHandles);
+ return certs;
+}
+
+/*
+ * delete a cert and it's private key (if no other certs are pointing to the
+ * private key.
+ */
+SECStatus
+PK11_DeleteTokenCertAndKey(CERTCertificate *cert, void *wincx)
+{
+ SECKEYPrivateKey *privKey = PK11_FindKeyByAnyCert(cert, wincx);
+ CK_OBJECT_HANDLE pubKey;
+ PK11SlotInfo *slot = NULL;
+
+ pubKey = pk11_FindPubKeyByAnyCert(cert, &slot, wincx);
+ if (privKey) {
+ /* For 3.4, utilize the generic cert delete function */
+ SEC_DeletePermCertificate(cert);
+ PK11_DeleteTokenPrivateKey(privKey, PR_FALSE);
+ }
+ if ((pubKey != CK_INVALID_HANDLE) && (slot != NULL)) {
+ PK11_DestroyTokenObject(slot, pubKey);
+ PK11_FreeSlot(slot);
+ }
+ return SECSuccess;
+}
+
+/*
+ * cert callback structure
+ */
+typedef struct pk11DoCertCallbackStr {
+ SECStatus (*callback)(PK11SlotInfo *slot, CERTCertificate *, void *);
+ SECStatus (*noslotcallback)(CERTCertificate *, void *);
+ SECStatus (*itemcallback)(CERTCertificate *, SECItem *, void *);
+ void *callbackArg;
+} pk11DoCertCallback;
+
+typedef struct pk11CertCallbackStr {
+ SECStatus (*callback)(CERTCertificate *, SECItem *, void *);
+ void *callbackArg;
+} pk11CertCallback;
+
+struct fake_der_cb_argstr {
+ SECStatus (*callback)(CERTCertificate *, SECItem *, void *);
+ void *arg;
+};
+
+static SECStatus
+fake_der_cb(CERTCertificate *c, void *a)
+{
+ struct fake_der_cb_argstr *fda = (struct fake_der_cb_argstr *)a;
+ return (*fda->callback)(c, &c->derCert, fda->arg);
+}
+
+/*
+ * Extract all the certs on a card from a slot.
+ */
+SECStatus
+PK11_TraverseSlotCerts(SECStatus (*callback)(CERTCertificate *, SECItem *, void *),
+ void *arg, void *wincx)
+{
+ NSSTrustDomain *defaultTD = STAN_GetDefaultTrustDomain();
+ struct fake_der_cb_argstr fda;
+ struct nss3_cert_cbstr pk11cb;
+
+ /* authenticate to the tokens first */
+ (void)pk11_TraverseAllSlots(NULL, NULL, PR_TRUE, wincx);
+
+ fda.callback = callback;
+ fda.arg = arg;
+ pk11cb.callback = fake_der_cb;
+ pk11cb.arg = &fda;
+ NSSTrustDomain_TraverseCertificates(defaultTD, convert_cert, &pk11cb);
+ return SECSuccess;
+}
+
+static void
+transfer_token_certs_to_collection(nssList *certList, NSSToken *token,
+ nssPKIObjectCollection *collection)
+{
+ NSSCertificate **certs;
+ PRUint32 i, count;
+ NSSToken **tokens, **tp;
+ count = nssList_Count(certList);
+ if (count == 0) {
+ return;
+ }
+ certs = nss_ZNEWARRAY(NULL, NSSCertificate *, count);
+ if (!certs) {
+ return;
+ }
+ nssList_GetArray(certList, (void **)certs, count);
+ for (i = 0; i < count; i++) {
+ tokens = nssPKIObject_GetTokens(&certs[i]->object, NULL);
+ if (tokens) {
+ for (tp = tokens; *tp; tp++) {
+ if (*tp == token) {
+ nssPKIObjectCollection_AddObject(collection,
+ (nssPKIObject *)certs[i]);
+ }
+ }
+ nssTokenArray_Destroy(tokens);
+ }
+ CERT_DestroyCertificate(STAN_GetCERTCertificateOrRelease(certs[i]));
+ }
+ nss_ZFreeIf(certs);
+}
+
+static void
+transfer_uri_certs_to_collection(nssList *certList, PK11URI *uri,
+ nssPKIObjectCollection *collection)
+{
+
+ NSSCertificate **certs;
+ PRUint32 i, count;
+ NSSToken **tokens, **tp;
+ PK11SlotInfo *slot;
+ const SECItem *id;
+
+ count = nssList_Count(certList);
+ if (count == 0) {
+ return;
+ }
+ certs = nss_ZNEWARRAY(NULL, NSSCertificate *, count);
+ if (!certs) {
+ return;
+ }
+ id = PK11URI_GetPathAttributeItem(uri, PK11URI_PATTR_ID);
+ nssList_GetArray(certList, (void **)certs, count);
+ for (i = 0; i < count; i++) {
+ /*
+ * Filter the subject matched certs based on the
+ * CKA_ID from the URI
+ */
+ if (id && (id->len != certs[i]->id.size ||
+ memcmp(id, certs[i]->id.data, certs[i]->id.size)))
+ continue;
+ tokens = nssPKIObject_GetTokens(&certs[i]->object, NULL);
+ if (tokens) {
+ for (tp = tokens; *tp; tp++) {
+ const char *value;
+ slot = (*tp)->pk11slot;
+
+ value = PK11URI_GetPathAttribute(uri, PK11URI_PATTR_TOKEN);
+ if (value &&
+ !pk11_MatchString(value,
+ (char *)slot->tokenInfo.label,
+ sizeof(slot->tokenInfo.label))) {
+ continue;
+ }
+
+ value = PK11URI_GetPathAttribute(uri, PK11URI_PATTR_MANUFACTURER);
+ if (value &&
+ !pk11_MatchString(value,
+ (char *)slot->tokenInfo.manufacturerID,
+ sizeof(slot->tokenInfo.manufacturerID))) {
+ continue;
+ }
+
+ value = PK11URI_GetPathAttribute(uri, PK11URI_PATTR_MODEL);
+ if (value &&
+ !pk11_MatchString(value,
+ (char *)slot->tokenInfo.model,
+ sizeof(slot->tokenInfo.model))) {
+ continue;
+ }
+
+ value = PK11URI_GetPathAttribute(uri, PK11URI_PATTR_SERIAL);
+ if (value &&
+ !pk11_MatchString(value,
+ (char *)slot->tokenInfo.serialNumber,
+ sizeof(slot->tokenInfo.serialNumber))) {
+ continue;
+ }
+
+ nssPKIObjectCollection_AddObject(collection,
+ (nssPKIObject *)certs[i]);
+ break;
+ }
+ nssTokenArray_Destroy(tokens);
+ }
+ CERT_DestroyCertificate(STAN_GetCERTCertificateOrRelease(certs[i]));
+ }
+ nss_ZFreeIf(certs);
+}
+
+static NSSCertificate **
+find_certs_from_uri(const char *uriString, void *wincx)
+{
+ PK11URI *uri = NULL;
+ CK_ATTRIBUTE attributes[10];
+ CK_ULONG nattributes = 0;
+ const SECItem *id;
+ const char *label, *type;
+ PK11SlotInfo *slotinfo;
+ nssCryptokiObject **instances;
+ PRStatus status;
+ nssPKIObjectCollection *collection = NULL;
+ NSSTrustDomain *defaultTD = STAN_GetDefaultTrustDomain();
+ NSSCertificate **certs = NULL;
+ nssList *certList = NULL;
+ SECStatus rv;
+ CK_OBJECT_CLASS s_class = CKO_CERTIFICATE;
+ static const CK_BBOOL s_true = CK_TRUE;
+ NSSToken **tokens, **tok;
+
+ uri = PK11URI_ParseURI(uriString);
+ if (uri == NULL) {
+ goto loser;
+ }
+
+ collection = nssCertificateCollection_Create(defaultTD, NULL);
+ if (!collection) {
+ goto loser;
+ }
+ certList = nssList_Create(NULL, PR_FALSE);
+ if (!certList) {
+ goto loser;
+ }
+
+ /* if the "type" attribute is specified its value must be "cert" */
+ type = PK11URI_GetPathAttribute(uri, PK11URI_PATTR_TYPE);
+ if (type && strcmp(type, "cert")) {
+ goto loser;
+ }
+
+ label = PK11URI_GetPathAttribute(uri, PK11URI_PATTR_OBJECT);
+ if (label) {
+ (void)nssTrustDomain_GetCertsForNicknameFromCache(defaultTD,
+ label,
+ certList);
+ } else {
+ (void)nssTrustDomain_GetCertsFromCache(defaultTD, certList);
+ }
+
+ transfer_uri_certs_to_collection(certList, uri, collection);
+
+ /* add the CKA_CLASS and CKA_TOKEN attributes manually */
+ attributes[nattributes].type = CKA_CLASS;
+ attributes[nattributes].pValue = (void *)&s_class;
+ attributes[nattributes].ulValueLen = sizeof(s_class);
+ nattributes++;
+
+ attributes[nattributes].type = CKA_TOKEN;
+ attributes[nattributes].pValue = (void *)&s_true;
+ attributes[nattributes].ulValueLen = sizeof(s_true);
+ nattributes++;
+
+ if (label) {
+ attributes[nattributes].type = CKA_LABEL;
+ attributes[nattributes].pValue = (void *)label;
+ attributes[nattributes].ulValueLen = strlen(label);
+ nattributes++;
+ }
+
+ id = PK11URI_GetPathAttributeItem(uri, PK11URI_PATTR_ID);
+ if (id) {
+ attributes[nattributes].type = CKA_ID;
+ attributes[nattributes].pValue = (void *)id->data;
+ attributes[nattributes].ulValueLen = id->len;
+ nattributes++;
+ }
+
+ tokens = NSSTrustDomain_FindTokensByURI(defaultTD, uri);
+ for (tok = tokens; tok && *tok; tok++) {
+ if (nssToken_IsPresent(*tok)) {
+ slotinfo = (*tok)->pk11slot;
+
+ rv = pk11_AuthenticateUnfriendly(slotinfo, PR_TRUE, wincx);
+ if (rv != SECSuccess) {
+ continue;
+ }
+ instances = nssToken_FindObjectsByTemplate(*tok, NULL,
+ attributes,
+ nattributes,
+ 0, &status);
+ nssPKIObjectCollection_AddInstances(collection, instances, 0);
+ nss_ZFreeIf(instances);
+ }
+ (void)nssToken_Destroy(*tok);
+ }
+ nss_ZFreeIf(tokens);
+ nssList_Destroy(certList);
+ certs = nssPKIObjectCollection_GetCertificates(collection, NULL, 0, NULL);
+
+loser:
+ if (collection) {
+ nssPKIObjectCollection_Destroy(collection);
+ }
+ if (uri) {
+ PK11URI_DestroyURI(uri);
+ }
+ return certs;
+}
+
+CERTCertificate *
+PK11_FindCertFromURI(const char *uri, void *wincx)
+{
+ static const NSSUsage usage = { PR_TRUE /* ... */ };
+ NSSCertificate *cert = NULL;
+ NSSCertificate **certs = NULL;
+ CERTCertificate *rvCert = NULL;
+
+ certs = find_certs_from_uri(uri, wincx);
+ if (certs) {
+ cert = nssCertificateArray_FindBestCertificate(certs, NULL,
+ &usage, NULL);
+ if (cert) {
+ rvCert = STAN_GetCERTCertificateOrRelease(cert);
+ }
+ nssCertificateArray_Destroy(certs);
+ }
+ return rvCert;
+}
+
+CERTCertList *
+PK11_FindCertsFromURI(const char *uri, void *wincx)
+{
+ int i;
+ CERTCertList *certList = NULL;
+ NSSCertificate **foundCerts;
+ NSSCertificate *c;
+
+ foundCerts = find_certs_from_uri(uri, wincx);
+ if (foundCerts) {
+ PRTime now = PR_Now();
+ certList = CERT_NewCertList();
+ for (i = 0, c = *foundCerts; c; c = foundCerts[++i]) {
+ if (certList) {
+ CERTCertificate *certCert = STAN_GetCERTCertificateOrRelease(c);
+ /* c may be invalid after this, don't reference it */
+ if (certCert) {
+ /* CERT_AddCertToListSorted adopts certCert */
+ CERT_AddCertToListSorted(certList, certCert,
+ CERT_SortCBValidity, &now);
+ }
+ } else {
+ nssCertificate_Destroy(c);
+ }
+ }
+ if (certList && CERT_LIST_HEAD(certList) == NULL) {
+ CERT_DestroyCertList(certList);
+ certList = NULL;
+ }
+ /* all the certs have been adopted or freed, free the raw array */
+ nss_ZFreeIf(foundCerts);
+ }
+ return certList;
+}
+
+static NSSCertificate **
+find_certs_from_nickname(const char *nickname, void *wincx)
+{
+ PRStatus status;
+ NSSCertificate **certs = NULL;
+ NSSToken *token = NULL;
+ NSSTrustDomain *defaultTD = STAN_GetDefaultTrustDomain();
+ PK11SlotInfo *slot = NULL;
+ SECStatus rv;
+ char *nickCopy;
+ char *delimit = NULL;
+ char *tokenName;
+
+ if (!PORT_Strncasecmp(nickname, "pkcs11:", strlen("pkcs11:"))) {
+ certs = find_certs_from_uri(nickname, wincx);
+ if (certs)
+ return certs;
+ }
+ nickCopy = PORT_Strdup(nickname);
+ if (!nickCopy) {
+ /* error code is set */
+ return NULL;
+ }
+ if ((delimit = PORT_Strchr(nickCopy, ':')) != NULL) {
+ tokenName = nickCopy;
+ nickname = delimit + 1;
+ *delimit = '\0';
+ /* find token by name */
+ token = NSSTrustDomain_FindTokenByName(defaultTD, (NSSUTF8 *)tokenName);
+ if (token) {
+ slot = PK11_ReferenceSlot(token->pk11slot);
+ } else {
+ PORT_SetError(SEC_ERROR_NO_TOKEN);
+ }
+ *delimit = ':';
+ } else {
+ slot = PK11_GetInternalKeySlot();
+ token = PK11Slot_GetNSSToken(slot);
+ if (!token) {
+ PORT_SetError(SEC_ERROR_NO_TOKEN);
+ }
+ }
+ if (token) {
+ nssList *certList;
+ nssCryptokiObject **instances;
+ nssPKIObjectCollection *collection;
+ nssTokenSearchType tokenOnly = nssTokenSearchType_TokenOnly;
+ if (!PK11_IsPresent(slot)) {
+ goto loser;
+ }
+ rv = pk11_AuthenticateUnfriendly(slot, PR_TRUE, wincx);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ collection = nssCertificateCollection_Create(defaultTD, NULL);
+ if (!collection) {
+ goto loser;
+ }
+ certList = nssList_Create(NULL, PR_FALSE);
+ if (!certList) {
+ nssPKIObjectCollection_Destroy(collection);
+ goto loser;
+ }
+ (void)nssTrustDomain_GetCertsForNicknameFromCache(defaultTD,
+ nickname,
+ certList);
+ transfer_token_certs_to_collection(certList, token, collection);
+ instances = nssToken_FindCertificatesByNickname(token,
+ NULL,
+ nickname,
+ tokenOnly,
+ 0,
+ &status);
+ nssPKIObjectCollection_AddInstances(collection, instances, 0);
+ nss_ZFreeIf(instances);
+ /* if it wasn't found, repeat the process for email address */
+ if (nssPKIObjectCollection_Count(collection) == 0 &&
+ PORT_Strchr(nickname, '@') != NULL) {
+ char *lowercaseName = CERT_FixupEmailAddr(nickname);
+ if (lowercaseName) {
+ (void)nssTrustDomain_GetCertsForEmailAddressFromCache(defaultTD,
+ lowercaseName,
+ certList);
+ transfer_token_certs_to_collection(certList, token, collection);
+ instances = nssToken_FindCertificatesByEmail(token,
+ NULL,
+ lowercaseName,
+ tokenOnly,
+ 0,
+ &status);
+ nssPKIObjectCollection_AddInstances(collection, instances, 0);
+ nss_ZFreeIf(instances);
+ PORT_Free(lowercaseName);
+ }
+ }
+ certs = nssPKIObjectCollection_GetCertificates(collection,
+ NULL, 0, NULL);
+ nssPKIObjectCollection_Destroy(collection);
+ nssList_Destroy(certList);
+ }
+loser:
+ if (token) {
+ (void)nssToken_Destroy(token);
+ }
+ if (slot) {
+ PK11_FreeSlot(slot);
+ }
+ if (nickCopy)
+ PORT_Free(nickCopy);
+ return certs;
+}
+
+CERTCertificate *
+PK11_FindCertFromNickname(const char *nickname, void *wincx)
+{
+ CERTCertificate *rvCert = NULL;
+ NSSCertificate *cert = NULL;
+ NSSCertificate **certs = NULL;
+ static const NSSUsage usage = { PR_TRUE /* ... */ };
+
+ certs = find_certs_from_nickname(nickname, wincx);
+ if (certs) {
+ cert = nssCertificateArray_FindBestCertificate(certs, NULL,
+ &usage, NULL);
+ if (cert) {
+ rvCert = STAN_GetCERTCertificateOrRelease(cert);
+ }
+ nssCertificateArray_Destroy(certs);
+ }
+ return rvCert;
+}
+
+/* Traverse slots callback */
+typedef struct FindCertsEmailArgStr {
+ char *email;
+ CERTCertList *certList;
+} FindCertsEmailArg;
+
+SECStatus
+FindCertsEmailCallback(CERTCertificate *cert, SECItem *item, void *arg)
+{
+ FindCertsEmailArg *cbparam = (FindCertsEmailArg *)arg;
+ const char *cert_email = CERT_GetFirstEmailAddress(cert);
+ PRBool found = PR_FALSE;
+
+ /* Email address present in certificate? */
+ if (cert_email == NULL) {
+ return SECSuccess;
+ }
+
+ /* Parameter correctly set? */
+ if (cbparam->email == NULL) {
+ return SECFailure;
+ }
+
+ /* Loop over all email addresses */
+ do {
+ if (!strcmp(cert_email, cbparam->email)) {
+ /* found one matching email address */
+ PRTime now = PR_Now();
+ found = PR_TRUE;
+ CERT_AddCertToListSorted(cbparam->certList,
+ CERT_DupCertificate(cert),
+ CERT_SortCBValidity, &now);
+ }
+ cert_email = CERT_GetNextEmailAddress(cert, cert_email);
+ } while (cert_email && !found);
+
+ return SECSuccess;
+}
+
+/* Find all certificates with matching email address */
+CERTCertList *
+PK11_FindCertsFromEmailAddress(const char *email, void *wincx)
+{
+ FindCertsEmailArg cbparam;
+ SECStatus rv;
+
+ cbparam.certList = CERT_NewCertList();
+ if (cbparam.certList == NULL) {
+ return NULL;
+ }
+
+ cbparam.email = CERT_FixupEmailAddr(email);
+ if (cbparam.email == NULL) {
+ CERT_DestroyCertList(cbparam.certList);
+ return NULL;
+ }
+
+ rv = PK11_TraverseSlotCerts(FindCertsEmailCallback, &cbparam, NULL);
+ if (rv != SECSuccess) {
+ CERT_DestroyCertList(cbparam.certList);
+ PORT_Free(cbparam.email);
+ return NULL;
+ }
+
+ /* empty list? */
+ if (CERT_LIST_EMPTY(cbparam.certList)) {
+ CERT_DestroyCertList(cbparam.certList);
+ cbparam.certList = NULL;
+ }
+
+ PORT_Free(cbparam.email);
+ return cbparam.certList;
+}
+
+CERTCertList *
+PK11_FindCertsFromNickname(const char *nickname, void *wincx)
+{
+ int i;
+ CERTCertList *certList = NULL;
+ NSSCertificate **foundCerts = NULL;
+ NSSCertificate *c;
+
+ foundCerts = find_certs_from_nickname(nickname, wincx);
+ if (foundCerts) {
+ PRTime now = PR_Now();
+ certList = CERT_NewCertList();
+ for (i = 0, c = *foundCerts; c; c = foundCerts[++i]) {
+ if (certList) {
+ CERTCertificate *certCert = STAN_GetCERTCertificateOrRelease(c);
+ /* c may be invalid after this, don't reference it */
+ if (certCert) {
+ /* CERT_AddCertToListSorted adopts certCert */
+ CERT_AddCertToListSorted(certList, certCert,
+ CERT_SortCBValidity, &now);
+ }
+ } else {
+ nssCertificate_Destroy(c);
+ }
+ }
+ /* all the certs have been adopted or freed, free the raw array */
+ nss_ZFreeIf(foundCerts);
+ }
+ return certList;
+}
+
+/*
+ * extract a key ID for a certificate...
+ * NOTE: We call this function from PKCS11.c If we ever use
+ * pkcs11 to extract the public key (we currently do not), this will break.
+ */
+SECItem *
+PK11_GetPubIndexKeyID(CERTCertificate *cert)
+{
+ SECKEYPublicKey *pubk;
+ SECItem *newItem = NULL;
+
+ pubk = CERT_ExtractPublicKey(cert);
+ if (pubk == NULL)
+ return NULL;
+
+ switch (pubk->keyType) {
+ case rsaKey:
+ newItem = SECITEM_DupItem(&pubk->u.rsa.modulus);
+ break;
+ case dsaKey:
+ newItem = SECITEM_DupItem(&pubk->u.dsa.publicValue);
+ break;
+ case dhKey:
+ newItem = SECITEM_DupItem(&pubk->u.dh.publicValue);
+ break;
+ case ecKey:
+ newItem = SECITEM_DupItem(&pubk->u.ec.publicValue);
+ break;
+ case fortezzaKey:
+ default:
+ newItem = NULL; /* Fortezza Fix later... */
+ }
+ SECKEY_DestroyPublicKey(pubk);
+ /* make hash of it */
+ return newItem;
+}
+
+/*
+ * generate a CKA_ID from a certificate.
+ */
+SECItem *
+pk11_mkcertKeyID(CERTCertificate *cert)
+{
+ SECItem *pubKeyData = PK11_GetPubIndexKeyID(cert);
+ SECItem *certCKA_ID;
+
+ if (pubKeyData == NULL)
+ return NULL;
+
+ certCKA_ID = PK11_MakeIDFromPubKey(pubKeyData);
+ SECITEM_FreeItem(pubKeyData, PR_TRUE);
+ return certCKA_ID;
+}
+
+/*
+ * Write the cert into the token.
+ */
+SECStatus
+PK11_ImportCert(PK11SlotInfo *slot, CERTCertificate *cert,
+ CK_OBJECT_HANDLE key, const char *nickname,
+ PRBool includeTrust)
+{
+ PRStatus status;
+ NSSCertificate *c;
+ nssCryptokiObject *keyobj, *certobj;
+ NSSToken *token = NULL;
+ char *emailAddr = NULL;
+ nssCertificateStoreTrace lockTrace = { NULL, NULL, PR_FALSE, PR_FALSE };
+ nssCertificateStoreTrace unlockTrace = { NULL, NULL, PR_FALSE, PR_FALSE };
+ SECItem *keyID = pk11_mkcertKeyID(cert);
+ if (keyID == NULL) {
+ goto loser; /* error code should be set already */
+ }
+ token = PK11Slot_GetNSSToken(slot);
+ if (!token) {
+ PORT_SetError(SEC_ERROR_NO_TOKEN);
+ goto loser;
+ }
+
+ if (PK11_IsInternal(slot) && cert->emailAddr && cert->emailAddr[0]) {
+ emailAddr = cert->emailAddr;
+ }
+
+ /* need to get the cert as a stan cert */
+ CERT_LockCertTempPerm(cert);
+ NSSCertificate *nssCert = cert->nssCertificate;
+ CERT_UnlockCertTempPerm(cert);
+ if (nssCert) {
+ c = nssCert;
+ } else {
+ c = STAN_GetNSSCertificate(cert);
+ if (c == NULL) {
+ goto loser;
+ }
+ }
+
+ /* set the id for the cert */
+ nssItem_Create(c->object.arena, &c->id, keyID->len, keyID->data);
+ if (!c->id.data) {
+ goto loser;
+ }
+
+ if (key != CK_INVALID_HANDLE) {
+ /* create an object for the key, ... */
+ keyobj = nss_ZNEW(NULL, nssCryptokiObject);
+ if (!keyobj) {
+ goto loser;
+ }
+ keyobj->token = nssToken_AddRef(token);
+ keyobj->handle = key;
+ keyobj->isTokenObject = PR_TRUE;
+
+ /* ... in order to set matching attributes for the key */
+ status = nssCryptokiPrivateKey_SetCertificate(keyobj, NULL, nickname,
+ &c->id, &c->subject);
+ nssCryptokiObject_Destroy(keyobj);
+ if (status != PR_SUCCESS) {
+ goto loser;
+ }
+ }
+
+ /* do the token import */
+ certobj = nssToken_ImportCertificate(token, NULL,
+ NSSCertificateType_PKIX,
+ &c->id,
+ nickname,
+ &c->encoding,
+ &c->issuer,
+ &c->subject,
+ &c->serial,
+ emailAddr,
+ PR_TRUE);
+ if (!certobj) {
+ if (NSS_GetError() == NSS_ERROR_INVALID_CERTIFICATE) {
+ PORT_SetError(SEC_ERROR_REUSED_ISSUER_AND_SERIAL);
+ SECITEM_FreeItem(keyID, PR_TRUE);
+ return SECFailure;
+ }
+ goto loser;
+ }
+
+ if (c->object.cryptoContext) {
+ /* Delete the temp instance */
+ NSSCryptoContext *cc = c->object.cryptoContext;
+ nssCertificateStore_Lock(cc->certStore, &lockTrace);
+ nssCertificateStore_RemoveCertLOCKED(cc->certStore, c);
+ nssCertificateStore_Unlock(cc->certStore, &lockTrace, &unlockTrace);
+ c->object.cryptoContext = NULL;
+ CERT_LockCertTempPerm(cert);
+ cert->istemp = PR_FALSE;
+ cert->isperm = PR_TRUE;
+ CERT_UnlockCertTempPerm(cert);
+ }
+
+ /* add the new instance to the cert, force an update of the
+ * CERTCertificate, and finish
+ */
+ nssPKIObject_AddInstance(&c->object, certobj);
+ /* nssTrustDomain_AddCertsToCache may release a reference to 'c' and
+ * replace 'c' with a different value. So we add a reference to 'c' to
+ * prevent 'c' from being destroyed. */
+ nssCertificate_AddRef(c);
+ nssTrustDomain_AddCertsToCache(STAN_GetDefaultTrustDomain(), &c, 1);
+ (void)STAN_ForceCERTCertificateUpdate(c);
+ nssCertificate_Destroy(c);
+ SECITEM_FreeItem(keyID, PR_TRUE);
+ (void)nssToken_Destroy(token);
+ return SECSuccess;
+loser:
+ if (token) {
+ (void)nssToken_Destroy(token);
+ }
+ CERT_MapStanError();
+ SECITEM_FreeItem(keyID, PR_TRUE);
+ if (PORT_GetError() != SEC_ERROR_TOKEN_NOT_LOGGED_IN) {
+ PORT_SetError(SEC_ERROR_ADDING_CERT);
+ }
+ return SECFailure;
+}
+
+SECStatus
+PK11_ImportDERCert(PK11SlotInfo *slot, SECItem *derCert,
+ CK_OBJECT_HANDLE key, char *nickname, PRBool includeTrust)
+{
+ CERTCertificate *cert;
+ SECStatus rv;
+
+ cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
+ derCert, NULL, PR_FALSE, PR_TRUE);
+ if (cert == NULL)
+ return SECFailure;
+
+ rv = PK11_ImportCert(slot, cert, key, nickname, includeTrust);
+ CERT_DestroyCertificate(cert);
+ return rv;
+}
+
+/*
+ * return the private key From a given Cert
+ */
+SECKEYPrivateKey *
+PK11_FindPrivateKeyFromCert(PK11SlotInfo *slot, CERTCertificate *cert,
+ void *wincx)
+{
+ int err;
+ CK_OBJECT_HANDLE certh;
+ CK_OBJECT_HANDLE keyh;
+ PRBool needLogin;
+ SECStatus rv;
+
+ certh = PK11_FindCertInSlot(slot, cert, wincx);
+ if (certh == CK_INVALID_HANDLE) {
+ return NULL;
+ }
+ /*
+ * prevent a login race condition. If slot is logged in between
+ * our call to pk11_LoginStillRequired and the
+ * PK11_MatchItem. The matchItem call will either succeed, or
+ * we will call it one more time after calling PK11_Authenticate
+ * (which is a noop on an authenticated token).
+ */
+ needLogin = pk11_LoginStillRequired(slot, wincx);
+ keyh = PK11_MatchItem(slot, certh, CKO_PRIVATE_KEY);
+ if ((keyh == CK_INVALID_HANDLE) && needLogin &&
+ (SSL_ERROR_NO_CERTIFICATE == (err = PORT_GetError()) ||
+ SEC_ERROR_TOKEN_NOT_LOGGED_IN == err)) {
+ /* try it again authenticated */
+ rv = PK11_Authenticate(slot, PR_TRUE, wincx);
+ if (rv != SECSuccess) {
+ return NULL;
+ }
+ keyh = PK11_MatchItem(slot, certh, CKO_PRIVATE_KEY);
+ }
+ if (keyh == CK_INVALID_HANDLE) {
+ return NULL;
+ }
+ return PK11_MakePrivKey(slot, nullKey, PR_TRUE, keyh, wincx);
+}
+
+/*
+ * import a cert for a private key we have already generated. Set the label
+ * on both to be the nickname. This is for the Key Gen, orphaned key case.
+ */
+PK11SlotInfo *
+PK11_KeyForCertExists(CERTCertificate *cert, CK_OBJECT_HANDLE *keyPtr,
+ void *wincx)
+{
+ PK11SlotList *list;
+ PK11SlotListElement *le;
+ SECItem *keyID;
+ CK_OBJECT_HANDLE key;
+ PK11SlotInfo *slot = NULL;
+ SECStatus rv;
+ int err;
+
+ keyID = pk11_mkcertKeyID(cert);
+ /* get them all! */
+ list = PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_TRUE, wincx);
+ if ((keyID == NULL) || (list == NULL)) {
+ if (keyID)
+ SECITEM_FreeItem(keyID, PR_TRUE);
+ if (list)
+ PK11_FreeSlotList(list);
+ return NULL;
+ }
+
+ /* Look for the slot that holds the Key */
+ for (le = list->head; le; le = le->next) {
+ /*
+ * prevent a login race condition. If le->slot is logged in between
+ * our call to pk11_LoginStillRequired and the
+ * pk11_FindPrivateKeyFromCertID, the find will either succeed, or
+ * we will call it one more time after calling PK11_Authenticate
+ * (which is a noop on an authenticated token).
+ */
+ PRBool needLogin = pk11_LoginStillRequired(le->slot, wincx);
+ key = pk11_FindPrivateKeyFromCertID(le->slot, keyID);
+ if ((key == CK_INVALID_HANDLE) && needLogin &&
+ (SSL_ERROR_NO_CERTIFICATE == (err = PORT_GetError()) ||
+ SEC_ERROR_TOKEN_NOT_LOGGED_IN == err)) {
+ /* authenticate and try again */
+ rv = PK11_Authenticate(le->slot, PR_TRUE, wincx);
+ if (rv != SECSuccess)
+ continue;
+ key = pk11_FindPrivateKeyFromCertID(le->slot, keyID);
+ }
+ if (key != CK_INVALID_HANDLE) {
+ slot = PK11_ReferenceSlot(le->slot);
+ if (keyPtr)
+ *keyPtr = key;
+ break;
+ }
+ }
+
+ SECITEM_FreeItem(keyID, PR_TRUE);
+ PK11_FreeSlotList(list);
+ return slot;
+}
+/*
+ * import a cert for a private key we have already generated. Set the label
+ * on both to be the nickname. This is for the Key Gen, orphaned key case.
+ */
+PK11SlotInfo *
+PK11_KeyForDERCertExists(SECItem *derCert, CK_OBJECT_HANDLE *keyPtr,
+ void *wincx)
+{
+ CERTCertificate *cert;
+ PK11SlotInfo *slot = NULL;
+
+ /* letting this use go -- the only thing that the cert is used for is
+ * to get the ID attribute.
+ */
+ cert = CERT_DecodeDERCertificate(derCert, PR_FALSE, NULL);
+ if (cert == NULL)
+ return NULL;
+
+ slot = PK11_KeyForCertExists(cert, keyPtr, wincx);
+ CERT_DestroyCertificate(cert);
+ return slot;
+}
+
+PK11SlotInfo *
+PK11_ImportCertForKey(CERTCertificate *cert, const char *nickname,
+ void *wincx)
+{
+ PK11SlotInfo *slot = NULL;
+ CK_OBJECT_HANDLE key;
+
+ slot = PK11_KeyForCertExists(cert, &key, wincx);
+
+ if (slot) {
+ if (PK11_ImportCert(slot, cert, key, nickname, PR_FALSE) != SECSuccess) {
+ PK11_FreeSlot(slot);
+ slot = NULL;
+ }
+ } else {
+ PORT_SetError(SEC_ERROR_ADDING_CERT);
+ }
+
+ return slot;
+}
+
+PK11SlotInfo *
+PK11_ImportDERCertForKey(SECItem *derCert, char *nickname, void *wincx)
+{
+ CERTCertificate *cert;
+ PK11SlotInfo *slot = NULL;
+
+ cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
+ derCert, NULL, PR_FALSE, PR_TRUE);
+ if (cert == NULL)
+ return NULL;
+
+ slot = PK11_ImportCertForKey(cert, nickname, wincx);
+ CERT_DestroyCertificate(cert);
+ return slot;
+}
+
+static CK_OBJECT_HANDLE
+pk11_FindCertObjectByTemplate(PK11SlotInfo **slotPtr,
+ CK_ATTRIBUTE *searchTemplate, size_t count, void *wincx)
+{
+ PK11SlotList *list;
+ PK11SlotListElement *le;
+ CK_OBJECT_HANDLE certHandle = CK_INVALID_HANDLE;
+ PK11SlotInfo *slot = NULL;
+ SECStatus rv;
+
+ *slotPtr = NULL;
+
+ /* get them all! */
+ list = PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_TRUE, wincx);
+ if (list == NULL) {
+ return CK_INVALID_HANDLE;
+ }
+
+ /* Look for the slot that holds the Key */
+ for (le = list->head; le; le = le->next) {
+ rv = pk11_AuthenticateUnfriendly(le->slot, PR_TRUE, wincx);
+ if (rv != SECSuccess)
+ continue;
+
+ certHandle = pk11_FindObjectByTemplate(le->slot, searchTemplate, count);
+ if (certHandle != CK_INVALID_HANDLE) {
+ slot = PK11_ReferenceSlot(le->slot);
+ break;
+ }
+ }
+
+ PK11_FreeSlotList(list);
+
+ if (slot == NULL) {
+ return CK_INVALID_HANDLE;
+ }
+ *slotPtr = slot;
+ return certHandle;
+}
+
+CERTCertificate *
+PK11_FindCertByIssuerAndSNOnToken(PK11SlotInfo *slot,
+ CERTIssuerAndSN *issuerSN, void *wincx)
+{
+ CERTCertificate *rvCert = NULL;
+ NSSCertificate *cert = NULL;
+ NSSDER issuer, serial;
+ NSSTrustDomain *td = STAN_GetDefaultTrustDomain();
+ NSSToken *token = NULL;
+ nssSession *session;
+ nssCryptokiObject *instance = NULL;
+ nssPKIObject *object = NULL;
+ SECItem *derSerial;
+ PRStatus status;
+
+ if (!issuerSN || !issuerSN->derIssuer.data || !issuerSN->derIssuer.len ||
+ !issuerSN->serialNumber.data || !issuerSN->serialNumber.len ||
+ issuerSN->derIssuer.len > CERT_MAX_DN_BYTES ||
+ issuerSN->serialNumber.len > CERT_MAX_SERIAL_NUMBER_BYTES) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ token = PK11Slot_GetNSSToken(slot);
+ if (!token) {
+ PORT_SetError(SEC_ERROR_NO_TOKEN);
+ return NULL;
+ }
+
+ session = nssToken_GetDefaultSession(token); /* non-owning */
+ if (!session) {
+ (void)nssToken_Destroy(token);
+ return NULL;
+ }
+
+ /* PKCS#11 needs to use DER-encoded serial numbers. Create a
+ * CERTIssuerAndSN that actually has the encoded value and pass that
+ * to PKCS#11 (and the crypto context).
+ */
+ derSerial = SEC_ASN1EncodeItem(NULL, NULL,
+ &issuerSN->serialNumber,
+ SEC_ASN1_GET(SEC_IntegerTemplate));
+ if (!derSerial) {
+ (void)nssToken_Destroy(token);
+ return NULL;
+ }
+
+ NSSITEM_FROM_SECITEM(&issuer, &issuerSN->derIssuer);
+ NSSITEM_FROM_SECITEM(&serial, derSerial);
+
+ instance = nssToken_FindCertificateByIssuerAndSerialNumber(token, session,
+ &issuer, &serial, nssTokenSearchType_TokenForced, &status);
+
+ (void)nssToken_Destroy(token);
+ SECITEM_FreeItem(derSerial, PR_TRUE);
+
+ if (!instance) {
+ goto loser;
+ }
+ object = nssPKIObject_Create(NULL, instance, td, NULL, nssPKIMonitor);
+ if (!object) {
+ goto loser;
+ }
+ instance = NULL; /* adopted by the previous call */
+ cert = nssCertificate_Create(object);
+ if (!cert) {
+ goto loser;
+ }
+ object = NULL; /* adopted by the previous call */
+ nssTrustDomain_AddCertsToCache(td, &cert, 1);
+ /* on failure, cert is freed below */
+ rvCert = STAN_GetCERTCertificate(cert);
+ if (!rvCert) {
+ goto loser;
+ }
+ return rvCert;
+
+loser:
+ if (instance) {
+ nssCryptokiObject_Destroy(instance);
+ }
+ if (object) {
+ nssPKIObject_Destroy(object);
+ }
+ if (cert) {
+ nssCertificate_Destroy(cert);
+ }
+ return NULL;
+}
+
+static PRCallOnceType keyIDHashCallOnce;
+
+static PRStatus PR_CALLBACK
+pk11_keyIDHash_populate(void *wincx)
+{
+ CERTCertList *certList;
+ CERTCertListNode *node = NULL;
+ SECItem subjKeyID = { siBuffer, NULL, 0 };
+ SECItem *slotid = NULL;
+ SECMODModuleList *modules, *mlp;
+ SECMODListLock *moduleLock;
+ int i;
+
+ certList = PK11_ListCerts(PK11CertListUser, wincx);
+ if (!certList) {
+ return PR_FAILURE;
+ }
+
+ for (node = CERT_LIST_HEAD(certList);
+ !CERT_LIST_END(node, certList);
+ node = CERT_LIST_NEXT(node)) {
+ if (CERT_FindSubjectKeyIDExtension(node->cert,
+ &subjKeyID) == SECSuccess &&
+ subjKeyID.data != NULL) {
+ cert_AddSubjectKeyIDMapping(&subjKeyID, node->cert);
+ SECITEM_FreeItem(&subjKeyID, PR_FALSE);
+ }
+ }
+ CERT_DestroyCertList(certList);
+
+ /*
+ * Record the state of each slot in a hash. The concatenation of slotID
+ * and moduleID is used as its key, with the slot series as its value.
+ */
+ slotid = SECITEM_AllocItem(NULL, NULL,
+ sizeof(CK_SLOT_ID) + sizeof(SECMODModuleID));
+ if (!slotid) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return PR_FAILURE;
+ }
+ moduleLock = SECMOD_GetDefaultModuleListLock();
+ if (!moduleLock) {
+ SECITEM_FreeItem(slotid, PR_TRUE);
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return PR_FAILURE;
+ }
+ SECMOD_GetReadLock(moduleLock);
+ modules = SECMOD_GetDefaultModuleList();
+ for (mlp = modules; mlp; mlp = mlp->next) {
+ for (i = 0; i < mlp->module->slotCount; i++) {
+ memcpy(slotid->data, &mlp->module->slots[i]->slotID,
+ sizeof(CK_SLOT_ID));
+ memcpy(&slotid->data[sizeof(CK_SLOT_ID)], &mlp->module->moduleID,
+ sizeof(SECMODModuleID));
+ cert_UpdateSubjectKeyIDSlotCheck(slotid,
+ mlp->module->slots[i]->series);
+ }
+ }
+ SECMOD_ReleaseReadLock(moduleLock);
+ SECITEM_FreeItem(slotid, PR_TRUE);
+
+ return PR_SUCCESS;
+}
+
+/*
+ * We're looking for a cert which we have the private key for that's on the
+ * list of recipients. This searches one slot.
+ * this is the new version for NSS SMIME code
+ * this stuff should REALLY be in the SMIME code, but some things in here are not public
+ * (they should be!)
+ */
+static CERTCertificate *
+pk11_FindCertObjectByRecipientNew(PK11SlotInfo *slot, NSSCMSRecipient **recipientlist,
+ int *rlIndex, void *pwarg)
+{
+ NSSCMSRecipient *ri = NULL;
+ int i;
+ PRBool tokenRescanDone = PR_FALSE;
+ CERTCertTrust trust;
+
+ for (i = 0; (ri = recipientlist[i]) != NULL; i++) {
+ CERTCertificate *cert = NULL;
+ if (ri->kind == RLSubjKeyID) {
+ SECItem *derCert = cert_FindDERCertBySubjectKeyID(ri->id.subjectKeyID);
+ if (!derCert && !tokenRescanDone) {
+ /*
+ * We didn't find the cert by its key ID. If we have slots
+ * with removable tokens, a failure from
+ * cert_FindDERCertBySubjectKeyID doesn't necessarily imply
+ * that the cert is unavailable - the token might simply
+ * have been inserted after the initial run of
+ * pk11_keyIDHash_populate (wrapped by PR_CallOnceWithArg),
+ * or a different token might have been present in that
+ * slot, initially. Let's check for new tokens...
+ */
+ PK11SlotList *sl = PK11_GetAllTokens(CKM_INVALID_MECHANISM,
+ PR_FALSE, PR_FALSE, pwarg);
+ if (sl) {
+ PK11SlotListElement *le;
+ SECItem *slotid = SECITEM_AllocItem(NULL, NULL,
+ sizeof(CK_SLOT_ID) + sizeof(SECMODModuleID));
+ if (!slotid) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ PK11_FreeSlotList(sl);
+ return NULL;
+ }
+ for (le = sl->head; le; le = le->next) {
+ memcpy(slotid->data, &le->slot->slotID,
+ sizeof(CK_SLOT_ID));
+ memcpy(&slotid->data[sizeof(CK_SLOT_ID)],
+ &le->slot->module->moduleID,
+ sizeof(SECMODModuleID));
+ /*
+ * Any changes with the slot since our last check?
+ * If so, re-read the certs in that specific slot.
+ */
+ if (cert_SubjectKeyIDSlotCheckSeries(slotid) != PK11_GetSlotSeries(le->slot)) {
+ CERTCertListNode *node = NULL;
+ SECItem subjKeyID = { siBuffer, NULL, 0 };
+ CERTCertList *cl = PK11_ListCertsInSlot(le->slot);
+ if (!cl) {
+ continue;
+ }
+ for (node = CERT_LIST_HEAD(cl);
+ !CERT_LIST_END(node, cl);
+ node = CERT_LIST_NEXT(node)) {
+ if (CERT_IsUserCert(node->cert) &&
+ CERT_FindSubjectKeyIDExtension(node->cert,
+ &subjKeyID) == SECSuccess) {
+ if (subjKeyID.data) {
+ cert_AddSubjectKeyIDMapping(&subjKeyID,
+ node->cert);
+ cert_UpdateSubjectKeyIDSlotCheck(slotid,
+ PK11_GetSlotSeries(le->slot));
+ }
+ SECITEM_FreeItem(&subjKeyID, PR_FALSE);
+ }
+ }
+ CERT_DestroyCertList(cl);
+ }
+ }
+ PK11_FreeSlotList(sl);
+ SECITEM_FreeItem(slotid, PR_TRUE);
+ }
+ /* only check once per message/recipientlist */
+ tokenRescanDone = PR_TRUE;
+ /* do another lookup (hopefully we found that cert...) */
+ derCert = cert_FindDERCertBySubjectKeyID(ri->id.subjectKeyID);
+ }
+ if (derCert) {
+ cert = PK11_FindCertFromDERCertItem(slot, derCert, pwarg);
+ SECITEM_FreeItem(derCert, PR_TRUE);
+ }
+ } else {
+ cert = PK11_FindCertByIssuerAndSNOnToken(slot, ri->id.issuerAndSN,
+ pwarg);
+ }
+ if (cert) {
+ /* this isn't our cert */
+ if (CERT_GetCertTrust(cert, &trust) != SECSuccess ||
+ ((trust.emailFlags & CERTDB_USER) != CERTDB_USER)) {
+ CERT_DestroyCertificate(cert);
+ continue;
+ }
+ ri->slot = PK11_ReferenceSlot(slot);
+ *rlIndex = i;
+ return cert;
+ }
+ }
+ *rlIndex = -1;
+ return NULL;
+}
+
+/*
+ * This function is the same as above, but it searches all the slots.
+ * this is the new version for NSS SMIME code
+ * this stuff should REALLY be in the SMIME code, but some things in here are not public
+ * (they should be!)
+ */
+static CERTCertificate *
+pk11_AllFindCertObjectByRecipientNew(NSSCMSRecipient **recipientlist, void *wincx, int *rlIndex)
+{
+ PK11SlotList *list;
+ PK11SlotListElement *le;
+ CERTCertificate *cert = NULL;
+ SECStatus rv;
+
+ /* get them all! */
+ list = PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_TRUE, wincx);
+ if (list == NULL) {
+ return CK_INVALID_HANDLE;
+ }
+
+ /* Look for the slot that holds the Key */
+ for (le = list->head; le; le = le->next) {
+ rv = pk11_AuthenticateUnfriendly(le->slot, PR_TRUE, wincx);
+ if (rv != SECSuccess)
+ continue;
+
+ cert = pk11_FindCertObjectByRecipientNew(le->slot,
+ recipientlist, rlIndex, wincx);
+ if (cert)
+ break;
+ }
+
+ PK11_FreeSlotList(list);
+
+ return cert;
+}
+
+/*
+ * We're looking for a cert which we have the private key for that's on the
+ * list of recipients. This searches one slot.
+ */
+static CERTCertificate *
+pk11_FindCertObjectByRecipient(PK11SlotInfo *slot,
+ SEC_PKCS7RecipientInfo **recipientArray,
+ SEC_PKCS7RecipientInfo **rip, void *pwarg)
+{
+ SEC_PKCS7RecipientInfo *ri = NULL;
+ CERTCertTrust trust;
+ int i;
+
+ for (i = 0; (ri = recipientArray[i]) != NULL; i++) {
+ CERTCertificate *cert;
+
+ cert = PK11_FindCertByIssuerAndSNOnToken(slot, ri->issuerAndSN,
+ pwarg);
+ if (cert) {
+ /* this isn't our cert */
+ if (CERT_GetCertTrust(cert, &trust) != SECSuccess ||
+ ((trust.emailFlags & CERTDB_USER) != CERTDB_USER)) {
+ CERT_DestroyCertificate(cert);
+ continue;
+ }
+ *rip = ri;
+ return cert;
+ }
+ }
+ *rip = NULL;
+ return NULL;
+}
+
+/*
+ * This function is the same as above, but it searches all the slots.
+ */
+static CERTCertificate *
+pk11_AllFindCertObjectByRecipient(PK11SlotInfo **slotPtr,
+ SEC_PKCS7RecipientInfo **recipientArray,
+ SEC_PKCS7RecipientInfo **rip,
+ void *wincx)
+{
+ PK11SlotList *list;
+ PK11SlotListElement *le;
+ CERTCertificate *cert = NULL;
+ PK11SlotInfo *slot = NULL;
+ SECStatus rv;
+
+ *slotPtr = NULL;
+
+ /* get them all! */
+ list = PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_TRUE, wincx);
+ if (list == NULL) {
+ return CK_INVALID_HANDLE;
+ }
+
+ *rip = NULL;
+
+ /* Look for the slot that holds the Key */
+ for (le = list->head; le; le = le->next) {
+ rv = pk11_AuthenticateUnfriendly(le->slot, PR_TRUE, wincx);
+ if (rv != SECSuccess)
+ continue;
+
+ cert = pk11_FindCertObjectByRecipient(le->slot, recipientArray,
+ rip, wincx);
+ if (cert) {
+ slot = PK11_ReferenceSlot(le->slot);
+ break;
+ }
+ }
+
+ PK11_FreeSlotList(list);
+
+ if (slot == NULL) {
+ return NULL;
+ }
+ *slotPtr = slot;
+ PORT_Assert(cert != NULL);
+ return cert;
+}
+
+/*
+ * We need to invert the search logic for PKCS 7 because if we search for
+ * each cert on the list over all the slots, we wind up with lots of spurious
+ * password prompts. This way we get only one password prompt per slot, at
+ * the max, and most of the time we can find the cert, and only prompt for
+ * the key...
+ */
+CERTCertificate *
+PK11_FindCertAndKeyByRecipientList(PK11SlotInfo **slotPtr,
+ SEC_PKCS7RecipientInfo **array,
+ SEC_PKCS7RecipientInfo **rip,
+ SECKEYPrivateKey **privKey, void *wincx)
+{
+ CERTCertificate *cert = NULL;
+
+ *privKey = NULL;
+ *slotPtr = NULL;
+ cert = pk11_AllFindCertObjectByRecipient(slotPtr, array, rip, wincx);
+ if (!cert) {
+ return NULL;
+ }
+
+ *privKey = PK11_FindKeyByAnyCert(cert, wincx);
+ if (*privKey == NULL) {
+ goto loser;
+ }
+
+ return cert;
+loser:
+ if (cert)
+ CERT_DestroyCertificate(cert);
+ if (*slotPtr)
+ PK11_FreeSlot(*slotPtr);
+ *slotPtr = NULL;
+ return NULL;
+}
+
+/*
+ * This is the new version of the above function for NSS SMIME code
+ * this stuff should REALLY be in the SMIME code, but some things in here are not public
+ * (they should be!)
+ */
+int
+PK11_FindCertAndKeyByRecipientListNew(NSSCMSRecipient **recipientlist, void *wincx)
+{
+ CERTCertificate *cert;
+ NSSCMSRecipient *rl;
+ PRStatus rv;
+ int rlIndex;
+
+ rv = PR_CallOnceWithArg(&keyIDHashCallOnce, pk11_keyIDHash_populate, wincx);
+ if (rv != PR_SUCCESS)
+ return -1;
+
+ cert = pk11_AllFindCertObjectByRecipientNew(recipientlist, wincx, &rlIndex);
+ if (!cert) {
+ return -1;
+ }
+
+ rl = recipientlist[rlIndex];
+
+ /* at this point, rl->slot is set */
+
+ rl->privkey = PK11_FindKeyByAnyCert(cert, wincx);
+ if (rl->privkey == NULL) {
+ goto loser;
+ }
+
+ /* make a cert from the cert handle */
+ rl->cert = cert;
+ return rlIndex;
+
+loser:
+ if (cert)
+ CERT_DestroyCertificate(cert);
+ if (rl->slot)
+ PK11_FreeSlot(rl->slot);
+ rl->slot = NULL;
+ return -1;
+}
+
+CERTCertificate *
+PK11_FindCertByIssuerAndSN(PK11SlotInfo **slotPtr, CERTIssuerAndSN *issuerSN,
+ void *wincx)
+{
+ CERTCertificate *rvCert = NULL;
+ NSSCertificate *cert;
+ NSSDER issuer, serial;
+ NSSCryptoContext *cc;
+ SECItem *derSerial;
+
+ if (!issuerSN || !issuerSN->derIssuer.data || !issuerSN->derIssuer.len ||
+ !issuerSN->serialNumber.data || !issuerSN->serialNumber.len ||
+ issuerSN->derIssuer.len > CERT_MAX_DN_BYTES ||
+ issuerSN->serialNumber.len > CERT_MAX_SERIAL_NUMBER_BYTES) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ if (slotPtr)
+ *slotPtr = NULL;
+
+ /* PKCS#11 needs to use DER-encoded serial numbers. Create a
+ * CERTIssuerAndSN that actually has the encoded value and pass that
+ * to PKCS#11 (and the crypto context).
+ */
+ derSerial = SEC_ASN1EncodeItem(NULL, NULL,
+ &issuerSN->serialNumber,
+ SEC_ASN1_GET(SEC_IntegerTemplate));
+ if (!derSerial) {
+ return NULL;
+ }
+
+ NSSITEM_FROM_SECITEM(&issuer, &issuerSN->derIssuer);
+ NSSITEM_FROM_SECITEM(&serial, derSerial);
+
+ cc = STAN_GetDefaultCryptoContext();
+ cert = NSSCryptoContext_FindCertificateByIssuerAndSerialNumber(cc,
+ &issuer,
+ &serial);
+ if (cert) {
+ SECITEM_FreeItem(derSerial, PR_TRUE);
+ return STAN_GetCERTCertificateOrRelease(cert);
+ }
+
+ do {
+ /* free the old cert on retry. Associated slot was not present */
+ if (rvCert) {
+ CERT_DestroyCertificate(rvCert);
+ rvCert = NULL;
+ }
+
+ cert = NSSTrustDomain_FindCertificateByIssuerAndSerialNumber(
+ STAN_GetDefaultTrustDomain(),
+ &issuer,
+ &serial);
+ if (!cert) {
+ break;
+ }
+
+ rvCert = STAN_GetCERTCertificateOrRelease(cert);
+ if (rvCert == NULL) {
+ break;
+ }
+
+ /* Check to see if the cert's token is still there */
+ } while (!PK11_IsPresent(rvCert->slot));
+
+ if (rvCert && slotPtr)
+ *slotPtr = PK11_ReferenceSlot(rvCert->slot);
+
+ SECITEM_FreeItem(derSerial, PR_TRUE);
+ return rvCert;
+}
+
+CK_OBJECT_HANDLE
+PK11_FindObjectForCert(CERTCertificate *cert, void *wincx, PK11SlotInfo **pSlot)
+{
+ CK_OBJECT_HANDLE certHandle;
+ CK_OBJECT_CLASS certClass = CKO_CERTIFICATE;
+ CK_ATTRIBUTE *attr;
+ CK_ATTRIBUTE searchTemplate[] = {
+ { CKA_CLASS, NULL, 0 },
+ { CKA_VALUE, NULL, 0 },
+ };
+ const size_t templateSize = sizeof(searchTemplate) / sizeof(searchTemplate[0]);
+
+ attr = searchTemplate;
+ PK11_SETATTRS(attr, CKA_CLASS, &certClass, sizeof(certClass));
+ attr++;
+ PK11_SETATTRS(attr, CKA_VALUE, cert->derCert.data, cert->derCert.len);
+
+ if (cert->slot) {
+ certHandle = PK11_FindCertInSlot(cert->slot, cert, wincx);
+ if (certHandle != CK_INVALID_HANDLE) {
+ *pSlot = PK11_ReferenceSlot(cert->slot);
+ return certHandle;
+ }
+ }
+
+ certHandle = pk11_FindCertObjectByTemplate(pSlot, searchTemplate,
+ templateSize, wincx);
+ if (certHandle != CK_INVALID_HANDLE) {
+ if (cert->slot == NULL) {
+ cert->slot = PK11_ReferenceSlot(*pSlot);
+ cert->pkcs11ID = certHandle;
+ cert->ownSlot = PR_TRUE;
+ cert->series = cert->slot->series;
+ }
+ }
+
+ return (certHandle);
+}
+
+SECKEYPrivateKey *
+PK11_FindKeyByAnyCert(CERTCertificate *cert, void *wincx)
+{
+ CK_OBJECT_HANDLE certHandle;
+ CK_OBJECT_HANDLE keyHandle;
+ PK11SlotInfo *slot = NULL;
+ SECKEYPrivateKey *privKey = NULL;
+ PRBool needLogin;
+ SECStatus rv;
+ int err;
+
+ certHandle = PK11_FindObjectForCert(cert, wincx, &slot);
+ if (certHandle == CK_INVALID_HANDLE) {
+ return NULL;
+ }
+ /*
+ * prevent a login race condition. If slot is logged in between
+ * our call to pk11_LoginStillRequired and the
+ * PK11_MatchItem. The matchItem call will either succeed, or
+ * we will call it one more time after calling PK11_Authenticate
+ * (which is a noop on an authenticated token).
+ */
+ needLogin = pk11_LoginStillRequired(slot, wincx);
+ keyHandle = PK11_MatchItem(slot, certHandle, CKO_PRIVATE_KEY);
+ if ((keyHandle == CK_INVALID_HANDLE) && needLogin &&
+ (SSL_ERROR_NO_CERTIFICATE == (err = PORT_GetError()) ||
+ SEC_ERROR_TOKEN_NOT_LOGGED_IN == err)) {
+ /* authenticate and try again */
+ rv = PK11_Authenticate(slot, PR_TRUE, wincx);
+ if (rv == SECSuccess) {
+ keyHandle = PK11_MatchItem(slot, certHandle, CKO_PRIVATE_KEY);
+ }
+ }
+ if (keyHandle != CK_INVALID_HANDLE) {
+ privKey = PK11_MakePrivKey(slot, nullKey, PR_TRUE, keyHandle, wincx);
+ }
+ if (slot) {
+ PK11_FreeSlot(slot);
+ }
+ return privKey;
+}
+
+CK_OBJECT_HANDLE
+pk11_FindPubKeyByAnyCert(CERTCertificate *cert, PK11SlotInfo **slot, void *wincx)
+{
+ CK_OBJECT_HANDLE certHandle;
+ CK_OBJECT_HANDLE keyHandle;
+
+ certHandle = PK11_FindObjectForCert(cert, wincx, slot);
+ if (certHandle == CK_INVALID_HANDLE) {
+ return CK_INVALID_HANDLE;
+ }
+ keyHandle = PK11_MatchItem(*slot, certHandle, CKO_PUBLIC_KEY);
+ if (keyHandle == CK_INVALID_HANDLE) {
+ PK11_FreeSlot(*slot);
+ return CK_INVALID_HANDLE;
+ }
+ return keyHandle;
+}
+
+/*
+ * find the number of certs in the slot with the same subject name
+ */
+int
+PK11_NumberCertsForCertSubject(CERTCertificate *cert)
+{
+ CK_OBJECT_CLASS certClass = CKO_CERTIFICATE;
+ CK_ATTRIBUTE theTemplate[] = {
+ { CKA_CLASS, NULL, 0 },
+ { CKA_SUBJECT, NULL, 0 },
+ };
+ CK_ATTRIBUTE *attr = theTemplate;
+ int templateSize = sizeof(theTemplate) / sizeof(theTemplate[0]);
+
+ PK11_SETATTRS(attr, CKA_CLASS, &certClass, sizeof(certClass));
+ attr++;
+ PK11_SETATTRS(attr, CKA_SUBJECT, cert->derSubject.data, cert->derSubject.len);
+
+ if (cert->slot == NULL) {
+ PK11SlotList *list = PK11_GetAllTokens(CKM_INVALID_MECHANISM,
+ PR_FALSE, PR_TRUE, NULL);
+ PK11SlotListElement *le;
+ int count = 0;
+
+ if (!list) {
+ /* error code is set */
+ return 0;
+ }
+
+ /* loop through all the fortezza tokens */
+ for (le = list->head; le; le = le->next) {
+ count += PK11_NumberObjectsFor(le->slot, theTemplate, templateSize);
+ }
+ PK11_FreeSlotList(list);
+ return count;
+ }
+
+ return PK11_NumberObjectsFor(cert->slot, theTemplate, templateSize);
+}
+
+/*
+ * Walk all the certs with the same subject
+ */
+SECStatus
+PK11_TraverseCertsForSubject(CERTCertificate *cert,
+ SECStatus (*callback)(CERTCertificate *, void *), void *arg)
+{
+ if (!cert) {
+ return SECFailure;
+ }
+ if (cert->slot == NULL) {
+ PK11SlotList *list = PK11_GetAllTokens(CKM_INVALID_MECHANISM,
+ PR_FALSE, PR_TRUE, NULL);
+ PK11SlotListElement *le;
+
+ if (!list) {
+ /* error code is set */
+ return SECFailure;
+ }
+ /* loop through all the tokens */
+ for (le = list->head; le; le = le->next) {
+ PK11_TraverseCertsForSubjectInSlot(cert, le->slot, callback, arg);
+ }
+ PK11_FreeSlotList(list);
+ return SECSuccess;
+ }
+
+ return PK11_TraverseCertsForSubjectInSlot(cert, cert->slot, callback, arg);
+}
+
+SECStatus
+PK11_TraverseCertsForSubjectInSlot(CERTCertificate *cert, PK11SlotInfo *slot,
+ SECStatus (*callback)(CERTCertificate *, void *), void *arg)
+{
+ PRStatus nssrv = PR_SUCCESS;
+ NSSToken *token;
+ NSSDER subject;
+ NSSTrustDomain *td;
+ nssList *subjectList;
+ nssPKIObjectCollection *collection;
+ nssCryptokiObject **instances;
+ NSSCertificate **certs;
+ nssTokenSearchType tokenOnly = nssTokenSearchType_TokenOnly;
+ td = STAN_GetDefaultTrustDomain();
+ NSSITEM_FROM_SECITEM(&subject, &cert->derSubject);
+ token = PK11Slot_GetNSSToken(slot);
+ if (!token) {
+ return SECSuccess;
+ }
+ if (!nssToken_IsPresent(token)) {
+ (void)nssToken_Destroy(token);
+ return SECSuccess;
+ }
+ collection = nssCertificateCollection_Create(td, NULL);
+ if (!collection) {
+ (void)nssToken_Destroy(token);
+ return SECFailure;
+ }
+ subjectList = nssList_Create(NULL, PR_FALSE);
+ if (!subjectList) {
+ nssPKIObjectCollection_Destroy(collection);
+ (void)nssToken_Destroy(token);
+ return SECFailure;
+ }
+ (void)nssTrustDomain_GetCertsForSubjectFromCache(td, &subject,
+ subjectList);
+ transfer_token_certs_to_collection(subjectList, token, collection);
+ instances = nssToken_FindCertificatesBySubject(token, NULL,
+ &subject,
+ tokenOnly, 0, &nssrv);
+ nssPKIObjectCollection_AddInstances(collection, instances, 0);
+ nss_ZFreeIf(instances);
+ nssList_Destroy(subjectList);
+ certs = nssPKIObjectCollection_GetCertificates(collection,
+ NULL, 0, NULL);
+ nssPKIObjectCollection_Destroy(collection);
+ (void)nssToken_Destroy(token);
+ if (certs) {
+ CERTCertificate *oldie;
+ NSSCertificate **cp;
+ for (cp = certs; *cp; cp++) {
+ oldie = STAN_GetCERTCertificate(*cp);
+ if (!oldie) {
+ continue;
+ }
+ if ((*callback)(oldie, arg) != SECSuccess) {
+ nssrv = PR_FAILURE;
+ break;
+ }
+ }
+ nssCertificateArray_Destroy(certs);
+ }
+ return (nssrv == PR_SUCCESS) ? SECSuccess : SECFailure;
+}
+
+SECStatus
+PK11_TraverseCertsForNicknameInSlot(SECItem *nickname, PK11SlotInfo *slot,
+ SECStatus (*callback)(CERTCertificate *, void *), void *arg)
+{
+ PRStatus nssrv = PR_SUCCESS;
+ NSSToken *token;
+ NSSTrustDomain *td;
+ NSSUTF8 *nick;
+ PRBool created = PR_FALSE;
+ nssCryptokiObject **instances;
+ nssPKIObjectCollection *collection = NULL;
+ NSSCertificate **certs;
+ nssList *nameList = NULL;
+ nssTokenSearchType tokenOnly = nssTokenSearchType_TokenOnly;
+ token = PK11Slot_GetNSSToken(slot);
+ if (!token || !nssToken_IsPresent(token)) {
+ (void)nssToken_Destroy(token);
+ return SECSuccess;
+ }
+ if (nickname->data[nickname->len - 1] != '\0') {
+ nick = nssUTF8_Create(NULL, nssStringType_UTF8String,
+ nickname->data, nickname->len);
+ created = PR_TRUE;
+ } else {
+ nick = (NSSUTF8 *)nickname->data;
+ }
+ td = STAN_GetDefaultTrustDomain();
+ collection = nssCertificateCollection_Create(td, NULL);
+ if (!collection) {
+ goto loser;
+ }
+ nameList = nssList_Create(NULL, PR_FALSE);
+ if (!nameList) {
+ goto loser;
+ }
+ (void)nssTrustDomain_GetCertsForNicknameFromCache(td, nick, nameList);
+ transfer_token_certs_to_collection(nameList, token, collection);
+ instances = nssToken_FindCertificatesByNickname(token, NULL,
+ nick,
+ tokenOnly, 0, &nssrv);
+ nssPKIObjectCollection_AddInstances(collection, instances, 0);
+ nss_ZFreeIf(instances);
+ nssList_Destroy(nameList);
+ certs = nssPKIObjectCollection_GetCertificates(collection,
+ NULL, 0, NULL);
+ nssPKIObjectCollection_Destroy(collection);
+ (void)nssToken_Destroy(token);
+ if (certs) {
+ CERTCertificate *oldie;
+ NSSCertificate **cp;
+ for (cp = certs; *cp; cp++) {
+ oldie = STAN_GetCERTCertificate(*cp);
+ if (!oldie) {
+ continue;
+ }
+ if ((*callback)(oldie, arg) != SECSuccess) {
+ nssrv = PR_FAILURE;
+ break;
+ }
+ }
+ nssCertificateArray_Destroy(certs);
+ }
+ if (created)
+ nss_ZFreeIf(nick);
+ return (nssrv == PR_SUCCESS) ? SECSuccess : SECFailure;
+loser:
+ (void)nssToken_Destroy(token);
+ if (created) {
+ nss_ZFreeIf(nick);
+ }
+ if (collection) {
+ nssPKIObjectCollection_Destroy(collection);
+ }
+ if (nameList) {
+ nssList_Destroy(nameList);
+ }
+ return SECFailure;
+}
+
+SECStatus
+PK11_TraverseCertsInSlot(PK11SlotInfo *slot,
+ SECStatus (*callback)(CERTCertificate *, void *), void *arg)
+{
+ PRStatus nssrv;
+ NSSTrustDomain *td = STAN_GetDefaultTrustDomain();
+ NSSToken *tok;
+ nssList *certList = NULL;
+ nssCryptokiObject **instances;
+ nssPKIObjectCollection *collection;
+ NSSCertificate **certs;
+ nssTokenSearchType tokenOnly = nssTokenSearchType_TokenOnly;
+ tok = PK11Slot_GetNSSToken(slot);
+ if (!tok) {
+ return SECSuccess;
+ }
+ if (!nssToken_IsPresent(tok)) {
+ (void)nssToken_Destroy(tok);
+ return SECSuccess;
+ }
+ collection = nssCertificateCollection_Create(td, NULL);
+ if (!collection) {
+ (void)nssToken_Destroy(tok);
+ return SECFailure;
+ }
+ certList = nssList_Create(NULL, PR_FALSE);
+ if (!certList) {
+ nssPKIObjectCollection_Destroy(collection);
+ (void)nssToken_Destroy(tok);
+ return SECFailure;
+ }
+ (void)nssTrustDomain_GetCertsFromCache(td, certList);
+ transfer_token_certs_to_collection(certList, tok, collection);
+ instances = nssToken_FindObjects(tok, NULL, CKO_CERTIFICATE,
+ tokenOnly, 0, &nssrv);
+ nssPKIObjectCollection_AddInstances(collection, instances, 0);
+ nss_ZFreeIf(instances);
+ nssList_Destroy(certList);
+ certs = nssPKIObjectCollection_GetCertificates(collection,
+ NULL, 0, NULL);
+ nssPKIObjectCollection_Destroy(collection);
+ (void)nssToken_Destroy(tok);
+ if (certs) {
+ CERTCertificate *oldie;
+ NSSCertificate **cp;
+ for (cp = certs; *cp; cp++) {
+ oldie = STAN_GetCERTCertificate(*cp);
+ if (!oldie) {
+ continue;
+ }
+ if ((*callback)(oldie, arg) != SECSuccess) {
+ nssrv = PR_FAILURE;
+ break;
+ }
+ }
+ nssCertificateArray_Destroy(certs);
+ }
+ return (nssrv == PR_SUCCESS) ? SECSuccess : SECFailure;
+}
+
+/*
+ * return the certificate associated with a derCert
+ */
+CERTCertificate *
+PK11_FindCertFromDERCert(PK11SlotInfo *slot, CERTCertificate *cert,
+ void *wincx)
+{
+ return PK11_FindCertFromDERCertItem(slot, &cert->derCert, wincx);
+}
+
+CERTCertificate *
+PK11_FindCertFromDERCertItem(PK11SlotInfo *slot, const SECItem *inDerCert,
+ void *wincx)
+
+{
+ NSSDER derCert;
+ NSSToken *tok;
+ nssCryptokiObject *co = NULL;
+ SECStatus rv;
+ CERTCertificate *cert = NULL;
+
+ NSSITEM_FROM_SECITEM(&derCert, inDerCert);
+ rv = pk11_AuthenticateUnfriendly(slot, PR_TRUE, wincx);
+ if (rv != SECSuccess) {
+ PK11_FreeSlot(slot);
+ return NULL;
+ }
+
+ tok = PK11Slot_GetNSSToken(slot);
+ if (!tok) {
+ PK11_FreeSlot(slot);
+ return NULL;
+ }
+ co = nssToken_FindCertificateByEncodedCertificate(tok, NULL, &derCert,
+ nssTokenSearchType_TokenOnly, NULL);
+ (void)nssToken_Destroy(tok);
+
+ if (co) {
+ cert = PK11_MakeCertFromHandle(slot, co->handle, NULL);
+ nssCryptokiObject_Destroy(co);
+ }
+
+ return cert;
+}
+
+/*
+ * import a cert for a private key we have already generated. Set the label
+ * on both to be the nickname.
+ */
+static CK_OBJECT_HANDLE
+pk11_findKeyObjectByDERCert(PK11SlotInfo *slot, CERTCertificate *cert,
+ void *wincx)
+{
+ SECItem *keyID;
+ CK_OBJECT_HANDLE key;
+ SECStatus rv;
+ PRBool needLogin;
+ int err;
+
+ if ((slot == NULL) || (cert == NULL)) {
+ return CK_INVALID_HANDLE;
+ }
+
+ keyID = pk11_mkcertKeyID(cert);
+ if (keyID == NULL) {
+ return CK_INVALID_HANDLE;
+ }
+
+ /*
+ * prevent a login race condition. If slot is logged in between
+ * our call to pk11_LoginStillRequired and the
+ * pk11_FindPrivateKeyFromCerID. The matchItem call will either succeed, or
+ * we will call it one more time after calling PK11_Authenticate
+ * (which is a noop on an authenticated token).
+ */
+ needLogin = pk11_LoginStillRequired(slot, wincx);
+ key = pk11_FindPrivateKeyFromCertID(slot, keyID);
+ if ((key == CK_INVALID_HANDLE) && needLogin &&
+ (SSL_ERROR_NO_CERTIFICATE == (err = PORT_GetError()) ||
+ SEC_ERROR_TOKEN_NOT_LOGGED_IN == err)) {
+ /* authenticate and try again */
+ rv = PK11_Authenticate(slot, PR_TRUE, wincx);
+ if (rv != SECSuccess)
+ goto loser;
+ key = pk11_FindPrivateKeyFromCertID(slot, keyID);
+ }
+
+loser:
+ SECITEM_ZfreeItem(keyID, PR_TRUE);
+ return key;
+}
+
+SECKEYPrivateKey *
+PK11_FindKeyByDERCert(PK11SlotInfo *slot, CERTCertificate *cert,
+ void *wincx)
+{
+ CK_OBJECT_HANDLE keyHandle;
+
+ if ((slot == NULL) || (cert == NULL)) {
+ return NULL;
+ }
+
+ keyHandle = pk11_findKeyObjectByDERCert(slot, cert, wincx);
+ if (keyHandle == CK_INVALID_HANDLE) {
+ return NULL;
+ }
+
+ return PK11_MakePrivKey(slot, nullKey, PR_TRUE, keyHandle, wincx);
+}
+
+SECStatus
+PK11_ImportCertForKeyToSlot(PK11SlotInfo *slot, CERTCertificate *cert,
+ char *nickname,
+ PRBool addCertUsage, void *wincx)
+{
+ CK_OBJECT_HANDLE keyHandle;
+
+ if ((slot == NULL) || (cert == NULL) || (nickname == NULL)) {
+ return SECFailure;
+ }
+
+ keyHandle = pk11_findKeyObjectByDERCert(slot, cert, wincx);
+ if (keyHandle == CK_INVALID_HANDLE) {
+ return SECFailure;
+ }
+
+ return PK11_ImportCert(slot, cert, keyHandle, nickname, addCertUsage);
+}
+
+/* remove when the real version comes out */
+#define SEC_OID_MISSI_KEA 300 /* until we have v3 stuff merged */
+PRBool
+KEAPQGCompare(CERTCertificate *server, CERTCertificate *cert)
+{
+
+ /* not implemented */
+ return PR_FALSE;
+}
+
+PRBool
+PK11_FortezzaHasKEA(CERTCertificate *cert)
+{
+ /* look at the subject and see if it is a KEA for MISSI key */
+ SECOidData *oid;
+ CERTCertTrust trust;
+
+ if (CERT_GetCertTrust(cert, &trust) != SECSuccess ||
+ ((trust.sslFlags & CERTDB_USER) != CERTDB_USER)) {
+ return PR_FALSE;
+ }
+
+ oid = SECOID_FindOID(&cert->subjectPublicKeyInfo.algorithm.algorithm);
+ if (!oid) {
+ return PR_FALSE;
+ }
+
+ return (PRBool)((oid->offset == SEC_OID_MISSI_KEA_DSS_OLD) ||
+ (oid->offset == SEC_OID_MISSI_KEA_DSS) ||
+ (oid->offset == SEC_OID_MISSI_KEA));
+}
+
+/*
+ * Find a kea cert on this slot that matches the domain of it's peer
+ */
+static CERTCertificate
+ *
+ pk11_GetKEAMate(PK11SlotInfo *slot, CERTCertificate *peer)
+{
+ int i;
+ CERTCertificate *returnedCert = NULL;
+
+ for (i = 0; i < slot->cert_count; i++) {
+ CERTCertificate *cert = slot->cert_array[i];
+
+ if (PK11_FortezzaHasKEA(cert) && KEAPQGCompare(peer, cert)) {
+ returnedCert = CERT_DupCertificate(cert);
+ break;
+ }
+ }
+ return returnedCert;
+}
+
+/*
+ * The following is a FORTEZZA only Certificate request. We call this when we
+ * are doing a non-client auth SSL connection. We are only interested in the
+ * fortezza slots, and we are only interested in certs that share the same root
+ * key as the server.
+ */
+CERTCertificate *
+PK11_FindBestKEAMatch(CERTCertificate *server, void *wincx)
+{
+ PK11SlotList *keaList = PK11_GetAllTokens(CKM_KEA_KEY_DERIVE,
+ PR_FALSE, PR_TRUE, wincx);
+ PK11SlotListElement *le;
+ CERTCertificate *returnedCert = NULL;
+ SECStatus rv;
+
+ if (!keaList) {
+ /* error code is set */
+ return NULL;
+ }
+
+ /* loop through all the fortezza tokens */
+ for (le = keaList->head; le; le = le->next) {
+ rv = PK11_Authenticate(le->slot, PR_TRUE, wincx);
+ if (rv != SECSuccess)
+ continue;
+ if (le->slot->session == CK_INVALID_HANDLE) {
+ continue;
+ }
+ returnedCert = pk11_GetKEAMate(le->slot, server);
+ if (returnedCert)
+ break;
+ }
+ PK11_FreeSlotList(keaList);
+
+ return returnedCert;
+}
+
+/*
+ * find a matched pair of kea certs to key exchange parameters from one
+ * fortezza card to another as necessary.
+ */
+SECStatus
+PK11_GetKEAMatchedCerts(PK11SlotInfo *slot1, PK11SlotInfo *slot2,
+ CERTCertificate **cert1, CERTCertificate **cert2)
+{
+ CERTCertificate *returnedCert = NULL;
+ int i;
+
+ for (i = 0; i < slot1->cert_count; i++) {
+ CERTCertificate *cert = slot1->cert_array[i];
+
+ if (PK11_FortezzaHasKEA(cert)) {
+ returnedCert = pk11_GetKEAMate(slot2, cert);
+ if (returnedCert != NULL) {
+ *cert2 = returnedCert;
+ *cert1 = CERT_DupCertificate(cert);
+ return SECSuccess;
+ }
+ }
+ }
+ return SECFailure;
+}
+
+CK_OBJECT_HANDLE
+PK11_FindEncodedCertInSlot(PK11SlotInfo *slot, SECItem *derCert, void *wincx)
+{
+ if (!slot || !derCert) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ CK_OBJECT_CLASS certClass = CKO_CERTIFICATE;
+ CK_ATTRIBUTE theTemplate[] = {
+ { CKA_VALUE, NULL, 0 },
+ { CKA_CLASS, NULL, 0 }
+ };
+ const size_t tsize = sizeof(theTemplate) / sizeof(theTemplate[0]);
+ CK_ATTRIBUTE *attrs = theTemplate;
+
+ PK11_SETATTRS(attrs, CKA_VALUE, derCert->data, derCert->len);
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_CLASS, &certClass, sizeof(certClass));
+
+ SECStatus rv = pk11_AuthenticateUnfriendly(slot, PR_TRUE, wincx);
+ if (rv != SECSuccess) {
+ return CK_INVALID_HANDLE;
+ }
+
+ return pk11_FindObjectByTemplate(slot, theTemplate, tsize);
+}
+
+CK_OBJECT_HANDLE
+PK11_FindCertInSlot(PK11SlotInfo *slot, CERTCertificate *cert, void *wincx)
+{
+ CK_OBJECT_HANDLE certh;
+
+ if (cert->slot == slot) {
+ certh = cert->pkcs11ID;
+ if ((certh == CK_INVALID_HANDLE) ||
+ (cert->series != slot->series)) {
+ certh = PK11_FindEncodedCertInSlot(slot, &cert->derCert, wincx);
+ cert->pkcs11ID = certh;
+ cert->series = slot->series;
+ }
+ } else {
+ certh = PK11_FindEncodedCertInSlot(slot, &cert->derCert, wincx);
+ }
+ return certh;
+}
+
+/* Looking for PK11_GetKeyIDFromCert?
+ * Use PK11_GetLowLevelKeyIDForCert instead.
+ */
+
+struct listCertsStr {
+ PK11CertListType type;
+ CERTCertList *certList;
+};
+
+static PRStatus
+pk11ListCertCallback(NSSCertificate *c, void *arg)
+{
+ struct listCertsStr *listCertP = (struct listCertsStr *)arg;
+ CERTCertificate *newCert = NULL;
+ PK11CertListType type = listCertP->type;
+ CERTCertList *certList = listCertP->certList;
+ PRBool isUnique = PR_FALSE;
+ PRBool isCA = PR_FALSE;
+ char *nickname = NULL;
+ unsigned int certType;
+ SECStatus rv;
+
+ if ((type == PK11CertListUnique) || (type == PK11CertListRootUnique) ||
+ (type == PK11CertListCAUnique) || (type == PK11CertListUserUnique)) {
+ /* only list one instance of each certificate, even if several exist */
+ isUnique = PR_TRUE;
+ }
+ if ((type == PK11CertListCA) || (type == PK11CertListRootUnique) ||
+ (type == PK11CertListCAUnique)) {
+ isCA = PR_TRUE;
+ }
+
+ /* if we want user certs and we don't have one skip this cert */
+ if (((type == PK11CertListUser) || (type == PK11CertListUserUnique)) &&
+ !NSSCertificate_IsPrivateKeyAvailable(c, NULL, NULL)) {
+ return PR_SUCCESS;
+ }
+
+ /* PK11CertListRootUnique means we want CA certs without a private key.
+ * This is for legacy app support . PK11CertListCAUnique should be used
+ * instead to get all CA certs, regardless of private key
+ */
+ if ((type == PK11CertListRootUnique) &&
+ NSSCertificate_IsPrivateKeyAvailable(c, NULL, NULL)) {
+ return PR_SUCCESS;
+ }
+
+ /* caller still owns the reference to 'c' */
+ newCert = STAN_GetCERTCertificate(c);
+ if (!newCert) {
+ return PR_SUCCESS;
+ }
+ /* if we want CA certs and it ain't one, skip it */
+ if (isCA && (!CERT_IsCACert(newCert, &certType))) {
+ return PR_SUCCESS;
+ }
+ if (isUnique) {
+ CERT_DupCertificate(newCert);
+
+ nickname = STAN_GetCERTCertificateName(certList->arena, c);
+
+ /* put slot certs at the end */
+ if (newCert->slot && !PK11_IsInternal(newCert->slot)) {
+ rv = CERT_AddCertToListTailWithData(certList, newCert, nickname);
+ } else {
+ rv = CERT_AddCertToListHeadWithData(certList, newCert, nickname);
+ }
+ /* if we didn't add the cert to the list, don't leak it */
+ if (rv != SECSuccess) {
+ CERT_DestroyCertificate(newCert);
+ }
+ } else {
+ /* add multiple instances to the cert list */
+ nssCryptokiObject **ip;
+ nssCryptokiObject **instances = nssPKIObject_GetInstances(&c->object);
+ if (!instances) {
+ return PR_SUCCESS;
+ }
+ for (ip = instances; *ip; ip++) {
+ nssCryptokiObject *instance = *ip;
+ PK11SlotInfo *slot = instance->token->pk11slot;
+
+ /* put the same CERTCertificate in the list for all instances */
+ CERT_DupCertificate(newCert);
+
+ nickname = STAN_GetCERTCertificateNameForInstance(
+ certList->arena, c, instance);
+
+ /* put slot certs at the end */
+ if (slot && !PK11_IsInternal(slot)) {
+ rv = CERT_AddCertToListTailWithData(certList, newCert, nickname);
+ } else {
+ rv = CERT_AddCertToListHeadWithData(certList, newCert, nickname);
+ }
+ /* if we didn't add the cert to the list, don't leak it */
+ if (rv != SECSuccess) {
+ CERT_DestroyCertificate(newCert);
+ }
+ }
+ nssCryptokiObjectArray_Destroy(instances);
+ }
+ return PR_SUCCESS;
+}
+
+CERTCertList *
+PK11_ListCerts(PK11CertListType type, void *pwarg)
+{
+ NSSTrustDomain *defaultTD = STAN_GetDefaultTrustDomain();
+ CERTCertList *certList = NULL;
+ struct listCertsStr listCerts;
+ certList = CERT_NewCertList();
+ listCerts.type = type;
+ listCerts.certList = certList;
+
+ /* authenticate to the slots */
+ (void)pk11_TraverseAllSlots(NULL, NULL, PR_TRUE, pwarg);
+ NSSTrustDomain_TraverseCertificates(defaultTD, pk11ListCertCallback,
+ &listCerts);
+ return certList;
+}
+
+SECItem *
+PK11_GetLowLevelKeyIDForCert(PK11SlotInfo *slot,
+ CERTCertificate *cert, void *wincx)
+{
+ CK_OBJECT_HANDLE certHandle;
+ PK11SlotInfo *slotRef = NULL;
+ SECItem *item;
+
+ if (slot) {
+ certHandle = PK11_FindCertInSlot(slot, cert, wincx);
+ } else {
+ certHandle = PK11_FindObjectForCert(cert, wincx, &slotRef);
+ if (certHandle == CK_INVALID_HANDLE) {
+ return pk11_mkcertKeyID(cert);
+ }
+ slot = slotRef;
+ }
+
+ if (certHandle == CK_INVALID_HANDLE) {
+ return NULL;
+ }
+
+ item = pk11_GetLowLevelKeyFromHandle(slot, certHandle);
+ if (slotRef)
+ PK11_FreeSlot(slotRef);
+ return item;
+}
+
+/* argument type for listCertsCallback */
+typedef struct {
+ CERTCertList *list;
+ PK11SlotInfo *slot;
+} ListCertsArg;
+
+static SECStatus
+listCertsCallback(CERTCertificate *cert, void *arg)
+{
+ ListCertsArg *cdata = (ListCertsArg *)arg;
+ char *nickname = NULL;
+ nssCryptokiObject *instance, **ci;
+ nssCryptokiObject **instances;
+ NSSCertificate *c = STAN_GetNSSCertificate(cert);
+ SECStatus rv;
+
+ if (c == NULL) {
+ return SECFailure;
+ }
+ instances = nssPKIObject_GetInstances(&c->object);
+ if (!instances) {
+ return SECFailure;
+ }
+ instance = NULL;
+ for (ci = instances; *ci; ci++) {
+ if ((*ci)->token->pk11slot == cdata->slot) {
+ instance = *ci;
+ break;
+ }
+ }
+ PORT_Assert(instance != NULL);
+ if (!instance) {
+ nssCryptokiObjectArray_Destroy(instances);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ nickname = STAN_GetCERTCertificateNameForInstance(cdata->list->arena,
+ c, instance);
+ nssCryptokiObjectArray_Destroy(instances);
+
+ CERT_DupCertificate(cert);
+ rv = CERT_AddCertToListTailWithData(cdata->list, cert, nickname);
+ if (rv != SECSuccess) {
+ CERT_DestroyCertificate(cert);
+ }
+ return rv;
+}
+
+CERTCertList *
+PK11_ListCertsInSlot(PK11SlotInfo *slot)
+{
+ SECStatus status;
+ CERTCertList *certs;
+ ListCertsArg cdata;
+
+ certs = CERT_NewCertList();
+ if (certs == NULL)
+ return NULL;
+ cdata.list = certs;
+ cdata.slot = slot;
+
+ status = PK11_TraverseCertsInSlot(slot, listCertsCallback,
+ &cdata);
+
+ if (status != SECSuccess) {
+ CERT_DestroyCertList(certs);
+ certs = NULL;
+ }
+
+ return certs;
+}
+
+PK11SlotList *
+PK11_GetAllSlotsForCert(CERTCertificate *cert, void *arg)
+{
+ nssCryptokiObject **ip;
+ PK11SlotList *slotList;
+ NSSCertificate *c;
+ nssCryptokiObject **instances;
+ PRBool found = PR_FALSE;
+
+ if (!cert) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ c = STAN_GetNSSCertificate(cert);
+ if (!c) {
+ CERT_MapStanError();
+ return NULL;
+ }
+
+ /* add multiple instances to the cert list */
+ instances = nssPKIObject_GetInstances(&c->object);
+ if (!instances) {
+ PORT_SetError(SEC_ERROR_NO_TOKEN);
+ return NULL;
+ }
+
+ slotList = PK11_NewSlotList();
+ if (!slotList) {
+ nssCryptokiObjectArray_Destroy(instances);
+ return NULL;
+ }
+
+ for (ip = instances; *ip; ip++) {
+ nssCryptokiObject *instance = *ip;
+ PK11SlotInfo *slot = instance->token->pk11slot;
+ if (slot) {
+ PK11_AddSlotToList(slotList, slot, PR_TRUE);
+ found = PR_TRUE;
+ }
+ }
+ if (!found) {
+ PK11_FreeSlotList(slotList);
+ PORT_SetError(SEC_ERROR_NO_TOKEN);
+ slotList = NULL;
+ }
+
+ nssCryptokiObjectArray_Destroy(instances);
+ return slotList;
+}
+
+/*
+ * Using __PK11_SetCertificateNickname is *DANGEROUS*.
+ *
+ * The API will update the NSS database, but it *will NOT* update the in-memory data.
+ * As a result, after calling this API, there will be INCONSISTENCY between
+ * in-memory data and the database.
+ *
+ * Use of the API should be limited to short-lived tools, which will exit immediately
+ * after using this API.
+ *
+ * If you ignore this warning, your process is TAINTED and will most likely misbehave.
+ */
+SECStatus
+__PK11_SetCertificateNickname(CERTCertificate *cert, const char *nickname)
+{
+ /* Can't set nickname of temp cert. */
+ if (!cert->slot || cert->pkcs11ID == CK_INVALID_HANDLE) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ return PK11_SetObjectNickname(cert->slot, cert->pkcs11ID, nickname);
+}
diff --git a/security/nss/lib/pk11wrap/pk11cxt.c b/security/nss/lib/pk11wrap/pk11cxt.c
new file mode 100644
index 0000000000..0f170c352b
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11cxt.c
@@ -0,0 +1,1795 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/*
+ * This file PK11Contexts which are used in multipart hashing,
+ * encryption/decryption, and signing/verication operations.
+ */
+
+#include "seccomon.h"
+#include "secmod.h"
+#include "nssilock.h"
+#include "secmodi.h"
+#include "secmodti.h"
+#include "pkcs11.h"
+#include "pk11func.h"
+#include "secitem.h"
+#include "secoid.h"
+#include "sechash.h"
+#include "secerr.h"
+#include "blapit.h"
+#include "secport.h"
+
+static const SECItem pk11_null_params = { 0 };
+
+/**********************************************************************
+ *
+ * Now Deal with Crypto Contexts
+ *
+ **********************************************************************/
+
+/*
+ * the monitors...
+ */
+void
+PK11_EnterContextMonitor(PK11Context *cx)
+{
+ /* if we own the session and our slot is ThreadSafe, only monitor
+ * the Context */
+ if ((cx->ownSession) && (cx->slot->isThreadSafe)) {
+ /* Should this use monitors instead? */
+ PZ_Lock(cx->sessionLock);
+ } else {
+ PK11_EnterSlotMonitor(cx->slot);
+ }
+}
+
+void
+PK11_ExitContextMonitor(PK11Context *cx)
+{
+ /* if we own the session and our slot is ThreadSafe, only monitor
+ * the Context */
+ if ((cx->ownSession) && (cx->slot->isThreadSafe)) {
+ /* Should this use monitors instead? */
+ PZ_Unlock(cx->sessionLock);
+ } else {
+ PK11_ExitSlotMonitor(cx->slot);
+ }
+}
+
+/*
+ * Free up a Cipher Context
+ */
+void
+PK11_DestroyContext(PK11Context *context, PRBool freeit)
+{
+ pk11_CloseSession(context->slot, context->session, context->ownSession);
+ /* initialize the critical fields of the context */
+ if (context->savedData != NULL)
+ PORT_Free(context->savedData);
+ if (context->key)
+ PK11_FreeSymKey(context->key);
+ if (context->param && context->param != &pk11_null_params)
+ SECITEM_FreeItem(context->param, PR_TRUE);
+ if (context->sessionLock)
+ PZ_DestroyLock(context->sessionLock);
+ PK11_FreeSlot(context->slot);
+ if (freeit)
+ PORT_Free(context);
+}
+
+/*
+ * save the current context. Allocate Space if necessary.
+ */
+static unsigned char *
+pk11_saveContextHelper(PK11Context *context, unsigned char *buffer,
+ unsigned long *savedLength)
+{
+ CK_RV crv;
+
+ /* If buffer is NULL, this will get the length */
+ crv = PK11_GETTAB(context->slot)->C_GetOperationState(context->session, (CK_BYTE_PTR)buffer, savedLength);
+ if (!buffer || (crv == CKR_BUFFER_TOO_SMALL)) {
+ /* the given buffer wasn't big enough (or was NULL), but we
+ * have the length, so try again with a new buffer and the
+ * correct length
+ */
+ unsigned long bufLen = *savedLength;
+ buffer = PORT_Alloc(bufLen);
+ if (buffer == NULL) {
+ return (unsigned char *)NULL;
+ }
+ crv = PK11_GETTAB(context->slot)->C_GetOperationState(context->session, (CK_BYTE_PTR)buffer, savedLength);
+ if (crv != CKR_OK) {
+ PORT_ZFree(buffer, bufLen);
+ }
+ }
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return (unsigned char *)NULL;
+ }
+ return buffer;
+}
+
+void *
+pk11_saveContext(PK11Context *context, void *space, unsigned long *savedLength)
+{
+ return pk11_saveContextHelper(context,
+ (unsigned char *)space, savedLength);
+}
+
+/*
+ * restore the current context
+ */
+SECStatus
+pk11_restoreContext(PK11Context *context, void *space, unsigned long savedLength)
+{
+ CK_RV crv;
+ CK_OBJECT_HANDLE objectID = context->objectID;
+
+ PORT_Assert(space != NULL);
+ if (space == NULL) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ crv = PK11_GETTAB(context->slot)->C_SetOperationState(context->session, (CK_BYTE_PTR)space, savedLength, objectID, 0);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+SECStatus pk11_Finalize(PK11Context *context);
+
+/*
+ * Initialize a Message function. Particular function is passed in as a
+ * function pointer. Since all C_Message*Init funcitons have the same
+ * prototype, we just pick one of the the prototypes to declare our init
+ * function.
+ */
+static CK_RV
+pk11_contextInitMessage(PK11Context *context, CK_MECHANISM_PTR mech,
+ CK_C_MessageEncryptInit initFunc,
+ CK_FLAGS flags, CK_RV scrv)
+{
+ PK11SlotInfo *slot = context->slot;
+ CK_VERSION version = slot->module->cryptokiVersion;
+ CK_RV crv = CKR_OK;
+
+ context->ivCounter = 0;
+ context->ivMaxCount = 0;
+ context->ivFixedBits = 0;
+ context->ivLen = 0;
+ context->ivGen = CKG_NO_GENERATE;
+ context->simulate_mechanism = (mech)->mechanism;
+ context->simulate_message = PR_FALSE;
+ /* check that we can do the Message interface. We need to check
+ * for either 1) are we using a PKCS #11 v3 interface and 2) is the
+ * Message flag set on the mechanism. If either is false we simulate
+ * the message interface for the Encrypt and Decrypt cases using the
+ * PKCS #11 V2 interface.
+ * Sign and verify do not have V2 interfaces, so we go ahead and fail
+ * if those cases */
+ if ((version.major >= 3) &&
+ PK11_DoesMechanismFlag(slot, (mech)->mechanism, flags)) {
+ PK11_EnterContextMonitor(context);
+ crv = (*initFunc)((context)->session, (mech), (context)->objectID);
+ PK11_ExitContextMonitor(context);
+ if ((crv == CKR_FUNCTION_NOT_SUPPORTED) ||
+ (crv == CKR_MECHANISM_INVALID)) {
+ /* we have a 3.0 interface, and the flag was set (or ignored)
+ * but the implementation was not there, use the V2 interface */
+ crv = (scrv);
+ context->simulate_message = PR_TRUE;
+ }
+ } else {
+ crv = (scrv);
+ context->simulate_message = PR_TRUE;
+ }
+ return crv;
+}
+
+/*
+ * Context initialization. Used by all flavors of CreateContext
+ */
+static SECStatus
+pk11_context_init(PK11Context *context, CK_MECHANISM *mech_info)
+{
+ CK_RV crv;
+ SECStatus rv = SECSuccess;
+
+ context->simulate_message = PR_FALSE;
+ switch (context->operation) {
+ case CKA_ENCRYPT:
+ PK11_EnterContextMonitor(context);
+ crv = PK11_GETTAB(context->slot)->C_EncryptInit(context->session, mech_info, context->objectID);
+ PK11_ExitContextMonitor(context);
+ break;
+ case CKA_DECRYPT:
+ PK11_EnterContextMonitor(context);
+ if (context->fortezzaHack) {
+ CK_ULONG count = 0;
+ /* generate the IV for fortezza */
+ crv = PK11_GETTAB(context->slot)->C_EncryptInit(context->session, mech_info, context->objectID);
+ if (crv != CKR_OK) {
+ PK11_ExitContextMonitor(context);
+ break;
+ }
+ PK11_GETTAB(context->slot)
+ ->C_EncryptFinal(context->session,
+ NULL, &count);
+ }
+ crv = PK11_GETTAB(context->slot)->C_DecryptInit(context->session, mech_info, context->objectID);
+ PK11_ExitContextMonitor(context);
+ break;
+ case CKA_SIGN:
+ PK11_EnterContextMonitor(context);
+ crv = PK11_GETTAB(context->slot)->C_SignInit(context->session, mech_info, context->objectID);
+ PK11_ExitContextMonitor(context);
+ break;
+ case CKA_VERIFY:
+ /* NOTE: we previously has this set to C_SignInit for Macing.
+ * It turns out now one could possibly use it that way, though,
+ * because PK11_HashOp() always called C_VerifyUpdate on CKA_VERIFY,
+ * which would have failed. So everyone just calls us with CKA_SIGN
+ * when Macing even when they are verifying, no need to 'do it
+ * for them'. It needs to be VerifyInit now so that we can do
+ * PKCS #11 hash/Verify combo operations. */
+ PK11_EnterContextMonitor(context);
+ crv = PK11_GETTAB(context->slot)->C_VerifyInit(context->session, mech_info, context->objectID);
+ PK11_ExitContextMonitor(context);
+ break;
+ case CKA_DIGEST:
+ PK11_EnterContextMonitor(context);
+ crv = PK11_GETTAB(context->slot)->C_DigestInit(context->session, mech_info);
+ PK11_ExitContextMonitor(context);
+ break;
+
+ case CKA_NSS_MESSAGE | CKA_ENCRYPT:
+ crv = pk11_contextInitMessage(context, mech_info,
+ PK11_GETTAB(context->slot)->C_MessageEncryptInit,
+ CKF_MESSAGE_ENCRYPT, CKR_OK);
+ break;
+ case CKA_NSS_MESSAGE | CKA_DECRYPT:
+ crv = pk11_contextInitMessage(context, mech_info,
+ PK11_GETTAB(context->slot)->C_MessageDecryptInit,
+ CKF_MESSAGE_DECRYPT, CKR_OK);
+ break;
+ case CKA_NSS_MESSAGE | CKA_SIGN:
+ crv = pk11_contextInitMessage(context, mech_info,
+ PK11_GETTAB(context->slot)->C_MessageSignInit,
+ CKF_MESSAGE_SIGN, CKR_FUNCTION_NOT_SUPPORTED);
+ break;
+ case CKA_NSS_MESSAGE | CKA_VERIFY:
+ crv = pk11_contextInitMessage(context, mech_info,
+ PK11_GETTAB(context->slot)->C_MessageVerifyInit,
+ CKF_MESSAGE_VERIFY, CKR_FUNCTION_NOT_SUPPORTED);
+ break;
+ default:
+ crv = CKR_OPERATION_NOT_INITIALIZED;
+ break;
+ }
+
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+
+ /* handle the case where the token is using the old NSS mechanism */
+ if (context->simulate_message &&
+ !PK11_DoesMechanism(context->slot, context->simulate_mechanism)) {
+ if ((context->simulate_mechanism == CKM_CHACHA20_POLY1305) &&
+ PK11_DoesMechanism(context->slot, CKM_NSS_CHACHA20_POLY1305)) {
+ context->simulate_mechanism = CKM_NSS_CHACHA20_POLY1305;
+ } else {
+ PORT_SetError(PK11_MapError(CKR_MECHANISM_INVALID));
+ return SECFailure;
+ }
+ }
+
+ /*
+ * handle session starvation case.. use our last session to multiplex
+ */
+ if (!context->ownSession) {
+ PK11_EnterContextMonitor(context);
+ context->savedData = pk11_saveContext(context, context->savedData,
+ &context->savedLength);
+ if (context->savedData == NULL)
+ rv = SECFailure;
+ /* clear out out session for others to use */
+ pk11_Finalize(context);
+ PK11_ExitContextMonitor(context);
+ }
+ return rv;
+}
+
+/*
+ * Testing interfaces, not for general use. This function forces
+ * an AEAD context into simulation mode even though the target token
+ * can already do PKCS #11 v3.0 Message (i.e. softoken).
+ */
+SECStatus
+_PK11_ContextSetAEADSimulation(PK11Context *context)
+{
+ CK_RV crv;
+ /* only message encrypt and message decrypt contexts can be simulated */
+ if ((context->operation != (CKA_NSS_MESSAGE | CKA_ENCRYPT)) &&
+ (context->operation != (CKA_NSS_MESSAGE | CKA_DECRYPT))) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ /* if we are already simulating, return */
+ if (context->simulate_message) {
+ return SECSuccess;
+ }
+ /* we need to shutdown the existing AEAD operation */
+ switch (context->operation) {
+ case CKA_NSS_MESSAGE | CKA_ENCRYPT:
+ crv = PK11_GETTAB(context->slot)->C_MessageEncryptFinal(context->session);
+ break;
+ case CKA_NSS_MESSAGE | CKA_DECRYPT:
+ crv = PK11_GETTAB(context->slot)->C_MessageDecryptFinal(context->session);
+ break;
+ default:
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return SECFailure;
+ }
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ context->simulate_message = PR_TRUE;
+ return SECSuccess;
+}
+
+PRBool
+_PK11_ContextGetAEADSimulation(PK11Context *context)
+{
+ return context->simulate_message;
+}
+
+/*
+ * Common Helper Function do come up with a new context.
+ */
+static PK11Context *
+pk11_CreateNewContextInSlot(CK_MECHANISM_TYPE type,
+ PK11SlotInfo *slot, CK_ATTRIBUTE_TYPE operation,
+ PK11SymKey *symKey, CK_OBJECT_HANDLE objectID,
+ const SECItem *param, void *pwArg)
+{
+ CK_MECHANISM mech_info;
+ PK11Context *context;
+ SECStatus rv;
+
+ PORT_Assert(slot != NULL);
+ if (!slot || ((objectID == CK_INVALID_HANDLE) && ((operation != CKA_DIGEST) ||
+ (type == CKM_SKIPJACK_CBC64)))) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+ context = (PK11Context *)PORT_Alloc(sizeof(PK11Context));
+ if (context == NULL) {
+ return NULL;
+ }
+
+ /* now deal with the fortezza hack... the fortezza hack is an attempt
+ * to get around the issue of the card not allowing you to do a FORTEZZA
+ * LoadIV/Encrypt, which was added because such a combination could be
+ * use to circumvent the key escrow system. Unfortunately SSL needs to
+ * do this kind of operation, so in SSL we do a loadIV (to verify it),
+ * Then GenerateIV, and through away the first 8 bytes on either side
+ * of the connection.*/
+ context->fortezzaHack = PR_FALSE;
+ if (type == CKM_SKIPJACK_CBC64) {
+ if (symKey && (symKey->origin == PK11_OriginFortezzaHack)) {
+ context->fortezzaHack = PR_TRUE;
+ }
+ }
+
+ /* initialize the critical fields of the context */
+ context->operation = operation;
+ /* If we were given a symKey, keep our own reference to it so
+ * that the key doesn't disappear in the middle of the operation
+ * if the caller frees it. Public and Private keys are not reference
+ * counted, so the caller just has to keep his copies around until
+ * the operation completes */
+ context->key = symKey ? PK11_ReferenceSymKey(symKey) : NULL;
+ context->objectID = objectID;
+ context->slot = PK11_ReferenceSlot(slot);
+ context->session = pk11_GetNewSession(slot, &context->ownSession);
+ context->pwArg = pwArg;
+ /* get our session */
+ context->savedData = NULL;
+
+ /* save the parameters so that some digesting stuff can do multiple
+ * begins on a single context */
+ context->type = type;
+ if (param) {
+ if (param->len > 0) {
+ context->param = SECITEM_DupItem(param);
+ } else {
+ context->param = (SECItem *)&pk11_null_params;
+ }
+ } else {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ context->param = NULL;
+ }
+ context->init = PR_FALSE;
+ context->sessionLock = PZ_NewLock(nssILockPK11cxt);
+ if ((context->param == NULL) || (context->sessionLock == NULL)) {
+ PK11_DestroyContext(context, PR_TRUE);
+ return NULL;
+ }
+
+ mech_info.mechanism = type;
+ mech_info.pParameter = param->data;
+ mech_info.ulParameterLen = param->len;
+ rv = pk11_context_init(context, &mech_info);
+
+ if (rv != SECSuccess) {
+ PK11_DestroyContext(context, PR_TRUE);
+ return NULL;
+ }
+ context->init = PR_TRUE;
+ return context;
+}
+
+/*
+ * put together the various PK11_Create_Context calls used by different
+ * parts of libsec.
+ */
+PK11Context *
+__PK11_CreateContextByRawKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
+ PK11Origin origin, CK_ATTRIBUTE_TYPE operation, SECItem *key,
+ SECItem *param, void *wincx)
+{
+ PK11SymKey *symKey = NULL;
+ PK11Context *context = NULL;
+
+ /* first get a slot */
+ if (slot == NULL) {
+ slot = PK11_GetBestSlot(type, wincx);
+ if (slot == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MODULE);
+ goto loser;
+ }
+ } else {
+ PK11_ReferenceSlot(slot);
+ }
+
+ /* now import the key */
+ symKey = PK11_ImportSymKey(slot, type, origin, operation, key, wincx);
+ if (symKey == NULL)
+ goto loser;
+
+ context = PK11_CreateContextBySymKey(type, operation, symKey, param);
+
+loser:
+ if (symKey) {
+ PK11_FreeSymKey(symKey);
+ }
+ if (slot) {
+ PK11_FreeSlot(slot);
+ }
+
+ return context;
+}
+
+PK11Context *
+PK11_CreateContextByRawKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
+ PK11Origin origin, CK_ATTRIBUTE_TYPE operation, SECItem *key,
+ SECItem *param, void *wincx)
+{
+ return __PK11_CreateContextByRawKey(slot, type, origin, operation,
+ key, param, wincx);
+}
+
+/*
+ * Create a context from a key. We really should make sure we aren't using
+ * the same key in multiple sessions!
+ */
+PK11Context *
+PK11_CreateContextBySymKey(CK_MECHANISM_TYPE type, CK_ATTRIBUTE_TYPE operation,
+ PK11SymKey *symKey, const SECItem *param)
+{
+ PK11SymKey *newKey;
+ PK11Context *context;
+
+ /* if this slot doesn't support the mechanism, go to a slot that does */
+ newKey = pk11_ForceSlot(symKey, type, operation);
+ if (newKey == NULL) {
+ PK11_ReferenceSymKey(symKey);
+ } else {
+ symKey = newKey;
+ }
+
+ /* Context keeps its reference to the symKey, so it's safe to
+ * free our reference we we are through, even though we may have
+ * created the key using pk11_ForceSlot. */
+ context = pk11_CreateNewContextInSlot(type, symKey->slot, operation, symKey,
+ symKey->objectID, param, symKey->cx);
+ PK11_FreeSymKey(symKey);
+ return context;
+}
+
+/* To support multipart public key operations (like hash/verify operations),
+ * we need to create contexts with public keys. */
+PK11Context *
+PK11_CreateContextByPubKey(CK_MECHANISM_TYPE type, CK_ATTRIBUTE_TYPE operation,
+ SECKEYPublicKey *pubKey, const SECItem *param,
+ void *pwArg)
+{
+ PK11SlotInfo *slot = pubKey->pkcs11Slot;
+ SECItem nullparam = { 0, 0, 0 };
+
+ /* if this slot doesn't support the mechanism, go to a slot that does */
+ /* public keys have all their data in the public key data structure,
+ * so there's no need to export the old key, just import this one. The
+ * import manages consistancy of the public key data structure */
+ if (slot == NULL || !PK11_DoesMechanism(slot, type)) {
+ CK_OBJECT_HANDLE objectID;
+ slot = PK11_GetBestSlot(type, NULL);
+ if (slot == NULL) {
+ return NULL;
+ }
+ objectID = PK11_ImportPublicKey(slot, pubKey, PR_FALSE);
+ PK11_FreeSlot(slot);
+ if (objectID == CK_INVALID_HANDLE) {
+ return NULL;
+ }
+ }
+
+ /* unlike symkeys, we accept a NULL parameter. map a null parameter
+ * to the empty parameter. This matches the semantics of
+ * PK11_VerifyWithMechanism */
+ return pk11_CreateNewContextInSlot(type, pubKey->pkcs11Slot, operation,
+ NULL, pubKey->pkcs11ID,
+ param ? param : &nullparam, pwArg);
+}
+
+/* To support multipart private key operations (like hash/sign operations),
+ * we need to create contexts with private keys. */
+PK11Context *
+PK11_CreateContextByPrivKey(CK_MECHANISM_TYPE type, CK_ATTRIBUTE_TYPE operation,
+ SECKEYPrivateKey *privKey, const SECItem *param)
+{
+ SECItem nullparam = { 0, 0, 0 };
+ /* Private keys are generally not movable. If the token the
+ * private key lives on can't do the operation, generally we are
+ * stuck anyway. So no need to try to manipulate the key into
+ * another token */
+
+ /* if this slot doesn't support the mechanism, go to a slot that does */
+ /* unlike symkeys, we accept a NULL parameter. map a null parameter
+ * to the empty parameter. This matches the semantics of
+ * PK11_SignWithMechanism */
+ return pk11_CreateNewContextInSlot(type, privKey->pkcs11Slot, operation,
+ NULL, privKey->pkcs11ID,
+ param ? param : &nullparam,
+ privKey->wincx);
+}
+
+/*
+ * Digest contexts don't need keys, but the do need to find a slot.
+ * Macing should use PK11_CreateContextBySymKey.
+ */
+PK11Context *
+PK11_CreateDigestContext(SECOidTag hashAlg)
+{
+ /* digesting has to work without authentication to the slot */
+ CK_MECHANISM_TYPE type;
+ PK11SlotInfo *slot;
+ PK11Context *context;
+ SECItem param;
+
+ type = PK11_AlgtagToMechanism(hashAlg);
+ slot = PK11_GetBestSlot(type, NULL);
+ if (slot == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MODULE);
+ return NULL;
+ }
+
+ /* maybe should really be PK11_GenerateNewParam?? */
+ param.data = NULL;
+ param.len = 0;
+ param.type = 0;
+
+ context = pk11_CreateNewContextInSlot(type, slot, CKA_DIGEST, NULL,
+ CK_INVALID_HANDLE, &param, NULL);
+ PK11_FreeSlot(slot);
+ return context;
+}
+
+/*
+ * create a new context which is the clone of the state of old context.
+ */
+PK11Context *
+PK11_CloneContext(PK11Context *old)
+{
+ PK11Context *newcx;
+ PRBool needFree = PR_FALSE;
+ SECStatus rv = SECSuccess;
+ void *data;
+ unsigned long len;
+
+ newcx = pk11_CreateNewContextInSlot(old->type, old->slot, old->operation,
+ old->key, old->objectID, old->param,
+ old->pwArg);
+ if (newcx == NULL)
+ return NULL;
+
+ /* now clone the save state. First we need to find the save state
+ * of the old session. If the old context owns it's session,
+ * the state needs to be saved, otherwise the state is in saveData. */
+ if (old->ownSession) {
+ PK11_EnterContextMonitor(old);
+ data = pk11_saveContext(old, NULL, &len);
+ PK11_ExitContextMonitor(old);
+ needFree = PR_TRUE;
+ } else {
+ data = old->savedData;
+ len = old->savedLength;
+ }
+
+ if (data == NULL) {
+ PK11_DestroyContext(newcx, PR_TRUE);
+ return NULL;
+ }
+
+ /* now copy that state into our new context. Again we have different
+ * work if the new context owns it's own session. If it does, we
+ * restore the state gathered above. If it doesn't, we copy the
+ * saveData pointer... */
+ if (newcx->ownSession) {
+ PK11_EnterContextMonitor(newcx);
+ rv = pk11_restoreContext(newcx, data, len);
+ PK11_ExitContextMonitor(newcx);
+ } else {
+ PORT_Assert(newcx->savedData != NULL);
+ if ((newcx->savedData == NULL) || (newcx->savedLength < len)) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ rv = SECFailure;
+ } else {
+ PORT_Memcpy(newcx->savedData, data, len);
+ newcx->savedLength = len;
+ }
+ }
+
+ if (needFree)
+ PORT_Free(data);
+
+ if (rv != SECSuccess) {
+ PK11_DestroyContext(newcx, PR_TRUE);
+ return NULL;
+ }
+ return newcx;
+}
+
+/*
+ * save the current context state into a variable. Required to make FORTEZZA
+ * work.
+ */
+SECStatus
+PK11_SaveContext(PK11Context *cx, unsigned char *save, int *len, int saveLength)
+{
+ unsigned char *data = NULL;
+ CK_ULONG length = saveLength;
+
+ if (cx->ownSession) {
+ PK11_EnterContextMonitor(cx);
+ data = pk11_saveContextHelper(cx, save, &length);
+ PK11_ExitContextMonitor(cx);
+ if (data)
+ *len = length;
+ } else if ((unsigned)saveLength >= cx->savedLength) {
+ data = (unsigned char *)cx->savedData;
+ if (cx->savedData) {
+ PORT_Memcpy(save, cx->savedData, cx->savedLength);
+ }
+ *len = cx->savedLength;
+ }
+ if (data != NULL) {
+ if (cx->ownSession) {
+ PORT_ZFree(data, length);
+ }
+ return SECSuccess;
+ } else {
+ return SECFailure;
+ }
+}
+
+/* same as above, but may allocate the return buffer. */
+unsigned char *
+PK11_SaveContextAlloc(PK11Context *cx,
+ unsigned char *preAllocBuf, unsigned int pabLen,
+ unsigned int *stateLen)
+{
+ unsigned char *stateBuf = NULL;
+ unsigned long length = (unsigned long)pabLen;
+
+ if (cx->ownSession) {
+ PK11_EnterContextMonitor(cx);
+ stateBuf = pk11_saveContextHelper(cx, preAllocBuf, &length);
+ PK11_ExitContextMonitor(cx);
+ *stateLen = (stateBuf != NULL) ? length : 0;
+ } else {
+ if (pabLen < cx->savedLength) {
+ stateBuf = (unsigned char *)PORT_Alloc(cx->savedLength);
+ if (!stateBuf) {
+ return (unsigned char *)NULL;
+ }
+ } else {
+ stateBuf = preAllocBuf;
+ }
+ if (cx->savedData) {
+ PORT_Memcpy(stateBuf, cx->savedData, cx->savedLength);
+ }
+ *stateLen = cx->savedLength;
+ }
+ return stateBuf;
+}
+
+/*
+ * restore the context state into a new running context. Also required for
+ * FORTEZZA .
+ */
+SECStatus
+PK11_RestoreContext(PK11Context *cx, unsigned char *save, int len)
+{
+ SECStatus rv = SECSuccess;
+ if (cx->ownSession) {
+ PK11_EnterContextMonitor(cx);
+ pk11_Finalize(cx);
+ rv = pk11_restoreContext(cx, save, len);
+ PK11_ExitContextMonitor(cx);
+ } else {
+ PORT_Assert(cx->savedData != NULL);
+ if ((cx->savedData == NULL) || (cx->savedLength < (unsigned)len)) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ rv = SECFailure;
+ } else {
+ PORT_Memcpy(cx->savedData, save, len);
+ cx->savedLength = len;
+ }
+ }
+ return rv;
+}
+
+/*
+ * This is to get FIPS compliance until we can convert
+ * libjar to use PK11_ hashing functions. It returns PR_FALSE
+ * if we can't get a PK11 Context.
+ */
+PRBool
+PK11_HashOK(SECOidTag algID)
+{
+ PK11Context *cx;
+
+ cx = PK11_CreateDigestContext(algID);
+ if (cx == NULL)
+ return PR_FALSE;
+ PK11_DestroyContext(cx, PR_TRUE);
+ return PR_TRUE;
+}
+
+/*
+ * start a new digesting or Mac'ing operation on this context
+ */
+SECStatus
+PK11_DigestBegin(PK11Context *cx)
+{
+ CK_MECHANISM mech_info;
+ SECStatus rv;
+
+ if (cx->init == PR_TRUE) {
+ return SECSuccess;
+ }
+
+ /*
+ * make sure the old context is clear first
+ */
+ PK11_EnterContextMonitor(cx);
+ pk11_Finalize(cx);
+ PK11_ExitContextMonitor(cx);
+
+ mech_info.mechanism = cx->type;
+ mech_info.pParameter = cx->param->data;
+ mech_info.ulParameterLen = cx->param->len;
+ rv = pk11_context_init(cx, &mech_info);
+
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ cx->init = PR_TRUE;
+ return SECSuccess;
+}
+
+SECStatus
+PK11_HashBuf(SECOidTag hashAlg, unsigned char *out, const unsigned char *in,
+ PRInt32 len)
+{
+ PK11Context *context;
+ unsigned int max_length;
+ unsigned int out_length;
+ SECStatus rv;
+
+ /* len will be passed to PK11_DigestOp as unsigned. */
+ if (len < 0) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ context = PK11_CreateDigestContext(hashAlg);
+ if (context == NULL)
+ return SECFailure;
+
+ rv = PK11_DigestBegin(context);
+ if (rv != SECSuccess) {
+ PK11_DestroyContext(context, PR_TRUE);
+ return rv;
+ }
+
+ rv = PK11_DigestOp(context, in, len);
+ if (rv != SECSuccess) {
+ PK11_DestroyContext(context, PR_TRUE);
+ return rv;
+ }
+
+ /* XXX This really should have been an argument to this function! */
+ max_length = HASH_ResultLenByOidTag(hashAlg);
+ PORT_Assert(max_length);
+ if (!max_length)
+ max_length = HASH_LENGTH_MAX;
+
+ rv = PK11_DigestFinal(context, out, &out_length, max_length);
+ PK11_DestroyContext(context, PR_TRUE);
+ return rv;
+}
+
+/*
+ * execute a bulk encryption operation
+ */
+SECStatus
+PK11_CipherOp(PK11Context *context, unsigned char *out, int *outlen,
+ int maxout, const unsigned char *in, int inlen)
+{
+ CK_RV crv = CKR_OK;
+ CK_ULONG length = maxout;
+ CK_ULONG offset = 0;
+ SECStatus rv = SECSuccess;
+ unsigned char *saveOut = out;
+ unsigned char *allocOut = NULL;
+
+ /* if we ran out of session, we need to restore our previously stored
+ * state.
+ */
+ PK11_EnterContextMonitor(context);
+ if (!context->ownSession) {
+ rv = pk11_restoreContext(context, context->savedData,
+ context->savedLength);
+ if (rv != SECSuccess) {
+ PK11_ExitContextMonitor(context);
+ return rv;
+ }
+ }
+
+ /*
+ * The fortezza hack is to send 8 extra bytes on the first encrypted and
+ * lose them on the first decrypt.
+ */
+ if (context->fortezzaHack) {
+ unsigned char random[8];
+ if (context->operation == CKA_ENCRYPT) {
+ PK11_ExitContextMonitor(context);
+ rv = PK11_GenerateRandom(random, sizeof(random));
+ PK11_EnterContextMonitor(context);
+
+ /* since we are offseting the output, we can't encrypt back into
+ * the same buffer... allocate a temporary buffer just for this
+ * call. */
+ allocOut = out = (unsigned char *)PORT_Alloc(maxout);
+ if (out == NULL) {
+ PK11_ExitContextMonitor(context);
+ return SECFailure;
+ }
+ crv = PK11_GETTAB(context->slot)->C_EncryptUpdate(context->session, random, sizeof(random), out, &length);
+
+ out += length;
+ maxout -= length;
+ offset = length;
+ } else if (context->operation == CKA_DECRYPT) {
+ length = sizeof(random);
+ crv = PK11_GETTAB(context->slot)->C_DecryptUpdate(context->session, (CK_BYTE_PTR)in, sizeof(random), random, &length);
+ inlen -= length;
+ in += length;
+ context->fortezzaHack = PR_FALSE;
+ }
+ }
+
+ switch (context->operation) {
+ case CKA_ENCRYPT:
+ length = maxout;
+ crv = PK11_GETTAB(context->slot)->C_EncryptUpdate(context->session, (CK_BYTE_PTR)in, inlen, out, &length);
+ length += offset;
+ break;
+ case CKA_DECRYPT:
+ length = maxout;
+ crv = PK11_GETTAB(context->slot)->C_DecryptUpdate(context->session, (CK_BYTE_PTR)in, inlen, out, &length);
+ break;
+ default:
+ crv = CKR_OPERATION_NOT_INITIALIZED;
+ break;
+ }
+
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ *outlen = 0;
+ rv = SECFailure;
+ } else {
+ *outlen = length;
+ }
+
+ if (context->fortezzaHack) {
+ if (context->operation == CKA_ENCRYPT) {
+ PORT_Assert(allocOut);
+ PORT_Memcpy(saveOut, allocOut, length);
+ PORT_Free(allocOut);
+ }
+ context->fortezzaHack = PR_FALSE;
+ }
+
+ /*
+ * handle session starvation case.. use our last session to multiplex
+ */
+ if (!context->ownSession) {
+ context->savedData = pk11_saveContext(context, context->savedData,
+ &context->savedLength);
+ if (context->savedData == NULL)
+ rv = SECFailure;
+
+ /* clear out out session for others to use */
+ pk11_Finalize(context);
+ }
+ PK11_ExitContextMonitor(context);
+ return rv;
+}
+
+/*
+ * Simulate the IV generation that normally would happen in the token.
+ *
+ * This is a modifed copy of what is in freebl/gcm.c. We can't use the
+ * version in freebl because of layering, since freebl is inside the token
+ * boundary. These issues are traditionally handled by moving them to util,
+ * but we also have two different Random functions we have two switch between.
+ * Since this is primarily here for tokens that don't support the PKCS #11
+ * Message Interface, it's OK if they diverge a bit. Slight semantic
+ * differences from the freebl/gcm.c version shouldn't be much more than the
+ * sematic differences between freebl and other tokens which do implement the
+ * Message Interface. */
+static SECStatus
+pk11_GenerateIV(PK11Context *context, CK_GENERATOR_FUNCTION ivgen,
+ int fixedBits, unsigned char *iv, int ivLen)
+{
+ unsigned int i;
+ unsigned int flexBits;
+ unsigned int ivOffset;
+ unsigned int ivNewCount;
+ unsigned char ivMask;
+ unsigned char ivSave;
+ SECStatus rv;
+
+ if (context->ivCounter != 0) {
+ /* If we've already generated a message, make sure all subsequent
+ * messages are using the same generator */
+ if ((context->ivGen != ivgen) ||
+ (context->ivFixedBits != fixedBits) ||
+ (context->ivLen != ivLen)) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ } else {
+ /* remember these values */
+ context->ivGen = ivgen;
+ context->ivFixedBits = fixedBits;
+ context->ivLen = ivLen;
+ /* now calculate how may bits of IV we have to supply */
+ flexBits = ivLen * PR_BITS_PER_BYTE;
+ /* first make sure we aren't going to overflow */
+ if (flexBits < fixedBits) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ flexBits -= fixedBits;
+ /* if we are generating a random number reduce the acceptable bits to
+ * avoid birthday attacks */
+ if (ivgen == CKG_GENERATE_RANDOM) {
+ if (flexBits <= GCMIV_RANDOM_BIRTHDAY_BITS) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ /* see freebl/blapit.h for how GCMIV_RANDOM_BIRTHDAY_BITS is
+ * calculated. */
+ flexBits -= GCMIV_RANDOM_BIRTHDAY_BITS;
+ flexBits = flexBits >> 1;
+ }
+ if (flexBits == 0) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ /* Turn those bits into the number of IV's we can safely return */
+ if (flexBits >= sizeof(context->ivMaxCount) * PR_BITS_PER_BYTE) {
+ context->ivMaxCount = PR_UINT64(0xffffffffffffffff);
+ } else {
+ context->ivMaxCount = (PR_UINT64(1) << flexBits);
+ }
+ }
+
+ /* no generate, accept the IV from the source */
+ if (ivgen == CKG_NO_GENERATE) {
+ context->ivCounter = 1;
+ return SECSuccess;
+ }
+
+ /* make sure we haven't exceeded the number of IVs we can return
+ * for this key, generator, and IV size */
+ if (context->ivCounter >= context->ivMaxCount) {
+ /* use a unique error from just bad user input */
+ PORT_SetError(SEC_ERROR_EXTRA_INPUT);
+ return SECFailure;
+ }
+
+ /* build to mask to handle the first byte of the IV */
+ ivOffset = fixedBits / PR_BITS_PER_BYTE;
+ ivMask = 0xff >> ((PR_BITS_PER_BYTE - (fixedBits & 7)) & 7);
+ ivNewCount = ivLen - ivOffset;
+
+ /* finally generate the IV */
+ switch (ivgen) {
+ case CKG_GENERATE: /* default to counter */
+ case CKG_GENERATE_COUNTER:
+ iv[ivOffset] = (iv[ivOffset] & ~ivMask) |
+ (PORT_GET_BYTE_BE(context->ivCounter, 0, ivNewCount) & ivMask);
+ for (i = 1; i < ivNewCount; i++) {
+ iv[ivOffset + i] =
+ PORT_GET_BYTE_BE(context->ivCounter, i, ivNewCount);
+ }
+ break;
+ case CKG_GENERATE_COUNTER_XOR:
+ iv[ivOffset] ^=
+ (PORT_GET_BYTE_BE(context->ivCounter, 0, ivNewCount) & ivMask);
+ for (i = 1; i < ivNewCount; i++) {
+ iv[ivOffset + i] ^=
+ PORT_GET_BYTE_BE(context->ivCounter, i, ivNewCount);
+ }
+ break;
+ case CKG_GENERATE_RANDOM:
+ ivSave = iv[ivOffset] & ~ivMask;
+ rv = PK11_GenerateRandom(iv + ivOffset, ivNewCount);
+ iv[ivOffset] = ivSave | (iv[ivOffset] & ivMask);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ break;
+ }
+ context->ivCounter++;
+ return SECSuccess;
+}
+
+/*
+ * PKCS #11 v2.40 did not have a message interface. If our module can't
+ * do the message interface use the old method of doing AEAD */
+static SECStatus
+pk11_AEADSimulateOp(PK11Context *context, void *params, int paramslen,
+ const unsigned char *aad, int aadlen,
+ unsigned char *out, int *outlen,
+ int maxout, const unsigned char *in, int inlen)
+{
+ unsigned int length = maxout;
+ SECStatus rv = SECSuccess;
+ unsigned char *saveOut = out;
+ unsigned char *allocOut = NULL;
+
+ /*
+ * first we need to convert the single shot (v2.40) parameters into
+ * the message version of the parameters. This usually involves
+ * copying the Nonce or IV, setting the AAD from our parameter list
+ * and handling the tag differences */
+ CK_GCM_PARAMS_V3 gcm;
+ CK_GCM_MESSAGE_PARAMS *gcm_message;
+ CK_CCM_PARAMS ccm;
+ CK_CCM_MESSAGE_PARAMS *ccm_message;
+ CK_SALSA20_CHACHA20_POLY1305_PARAMS chacha_poly;
+ CK_SALSA20_CHACHA20_POLY1305_MSG_PARAMS *chacha_poly_message;
+ CK_NSS_AEAD_PARAMS nss_chacha_poly;
+ CK_MECHANISM_TYPE mechanism = context->simulate_mechanism;
+ SECItem sim_params = { 0, NULL, 0 };
+ unsigned char *tag = NULL;
+ unsigned int taglen;
+ PRBool encrypt;
+
+ *outlen = 0;
+ /* figure out if we are encrypting or decrypting, as tags are
+ * handled differently in both */
+ switch (context->operation) {
+ case CKA_NSS_MESSAGE | CKA_ENCRYPT:
+ encrypt = PR_TRUE;
+ break;
+ case CKA_NSS_MESSAGE | CKA_DECRYPT:
+ encrypt = PR_FALSE;
+ break;
+ default:
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ switch (mechanism) {
+ case CKM_CHACHA20_POLY1305:
+ case CKM_SALSA20_POLY1305:
+ if (paramslen != sizeof(CK_SALSA20_CHACHA20_POLY1305_MSG_PARAMS)) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ chacha_poly_message =
+ (CK_SALSA20_CHACHA20_POLY1305_MSG_PARAMS *)params;
+ chacha_poly.pNonce = chacha_poly_message->pNonce;
+ chacha_poly.ulNonceLen = chacha_poly_message->ulNonceLen;
+ chacha_poly.pAAD = (CK_BYTE_PTR)aad;
+ chacha_poly.ulAADLen = aadlen;
+ tag = chacha_poly_message->pTag;
+ taglen = 16;
+ sim_params.data = (unsigned char *)&chacha_poly;
+ sim_params.len = sizeof(chacha_poly);
+ /* SALSA20_POLY1305 and CHACHA20_POLY1305 do not generate the iv
+ * internally, don't simulate it either */
+ break;
+ case CKM_NSS_CHACHA20_POLY1305:
+ if (paramslen != sizeof(CK_SALSA20_CHACHA20_POLY1305_MSG_PARAMS)) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ chacha_poly_message =
+ (CK_SALSA20_CHACHA20_POLY1305_MSG_PARAMS *)params;
+ tag = chacha_poly_message->pTag;
+ taglen = 16;
+ nss_chacha_poly.pNonce = chacha_poly_message->pNonce;
+ nss_chacha_poly.ulNonceLen = chacha_poly_message->ulNonceLen;
+ nss_chacha_poly.pAAD = (CK_BYTE_PTR)aad;
+ nss_chacha_poly.ulAADLen = aadlen;
+ nss_chacha_poly.ulTagLen = taglen;
+ sim_params.data = (unsigned char *)&nss_chacha_poly;
+ sim_params.len = sizeof(nss_chacha_poly);
+ /* CKM_NSS_CHACHA20_POLY1305 does not generate the iv
+ * internally, don't simulate it either */
+ break;
+ case CKM_AES_CCM:
+ if (paramslen != sizeof(CK_CCM_MESSAGE_PARAMS)) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ ccm_message = (CK_CCM_MESSAGE_PARAMS *)params;
+ ccm.ulDataLen = ccm_message->ulDataLen;
+ ccm.pNonce = ccm_message->pNonce;
+ ccm.ulNonceLen = ccm_message->ulNonceLen;
+ ccm.pAAD = (CK_BYTE_PTR)aad;
+ ccm.ulAADLen = aadlen;
+ ccm.ulMACLen = ccm_message->ulMACLen;
+ tag = ccm_message->pMAC;
+ taglen = ccm_message->ulMACLen;
+ sim_params.data = (unsigned char *)&ccm;
+ sim_params.len = sizeof(ccm);
+ if (encrypt) {
+ /* simulate generating the IV */
+ rv = pk11_GenerateIV(context, ccm_message->nonceGenerator,
+ ccm_message->ulNonceFixedBits,
+ ccm_message->pNonce,
+ ccm_message->ulNonceLen);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ }
+ break;
+ case CKM_AES_GCM:
+ if (paramslen != sizeof(CK_GCM_MESSAGE_PARAMS)) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ gcm_message = (CK_GCM_MESSAGE_PARAMS *)params;
+ gcm.pIv = gcm_message->pIv;
+ gcm.ulIvLen = gcm_message->ulIvLen;
+ gcm.ulIvBits = gcm.ulIvLen * PR_BITS_PER_BYTE;
+ gcm.pAAD = (CK_BYTE_PTR)aad;
+ gcm.ulAADLen = aadlen;
+ gcm.ulTagBits = gcm_message->ulTagBits;
+ tag = gcm_message->pTag;
+ taglen = (gcm_message->ulTagBits + (PR_BITS_PER_BYTE - 1)) / PR_BITS_PER_BYTE;
+ sim_params.data = (unsigned char *)&gcm;
+ sim_params.len = sizeof(gcm);
+ if (encrypt) {
+ /* simulate generating the IV */
+ rv = pk11_GenerateIV(context, gcm_message->ivGenerator,
+ gcm_message->ulIvFixedBits,
+ gcm_message->pIv, gcm_message->ulIvLen);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ }
+ break;
+ default:
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ return SECFailure;
+ }
+ /* now handle the tag. The message interface separates the tag from
+ * the data, while the single shot gets and puts the tag at the end of
+ * the encrypted data. */
+ if (!encrypt) {
+ /* In the decrypt case, if the tag is already at the end of the
+ * input buffer we are golden, otherwise we'll need a new input
+ * buffer and copy the tag at the end of it */
+ if (tag != in + inlen) {
+ allocOut = PORT_Alloc(inlen + taglen);
+ if (allocOut == NULL) {
+ return SECFailure;
+ }
+ PORT_Memcpy(allocOut, in, inlen);
+ PORT_Memcpy(allocOut + inlen, tag, taglen);
+ in = allocOut;
+ }
+ inlen = inlen + taglen;
+ } else {
+ /* if we end up allocating, we don't want to overrun this buffer,
+ * so we fail early here */
+ if (maxout < inlen) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ /* in the encrypt case, we are fine if maxout is big enough to hold
+ * the tag. We'll copy the tag after the operation */
+ if (maxout < inlen + taglen) {
+ allocOut = PORT_Alloc(inlen + taglen);
+ if (allocOut == NULL) {
+ return SECFailure;
+ }
+ out = allocOut;
+ length = maxout = inlen + taglen;
+ }
+ }
+ /* now do the operation */
+ if (encrypt) {
+ rv = PK11_Encrypt(context->key, mechanism, &sim_params, out, &length,
+ maxout, in, inlen);
+ } else {
+ rv = PK11_Decrypt(context->key, mechanism, &sim_params, out, &length,
+ maxout, in, inlen);
+ }
+ if (rv != SECSuccess) {
+ /* If the mechanism was CKM_AES_GCM, the module may have been
+ * following the same error as old versions of NSS. Retry with
+ * the CK_NSS_GCM_PARAMS */
+ if ((mechanism == CKM_AES_GCM) &&
+ (PORT_GetError() == SEC_ERROR_BAD_DATA)) {
+ CK_NSS_GCM_PARAMS gcm_nss;
+ gcm_message = (CK_GCM_MESSAGE_PARAMS *)params;
+ gcm_nss.pIv = gcm_message->pIv;
+ gcm_nss.ulIvLen = gcm_message->ulIvLen;
+ gcm_nss.pAAD = (CK_BYTE_PTR)aad;
+ gcm_nss.ulAADLen = aadlen;
+ gcm_nss.ulTagBits = gcm_message->ulTagBits;
+ sim_params.data = (unsigned char *)&gcm_nss;
+ sim_params.len = sizeof(gcm_nss);
+ if (encrypt) {
+ rv = PK11_Encrypt(context->key, mechanism, &sim_params, out,
+ &length, maxout, in, inlen);
+ } else {
+ rv = PK11_Decrypt(context->key, mechanism, &sim_params, out,
+ &length, maxout, in, inlen);
+ }
+ if (rv != SECSuccess) {
+ goto fail;
+ }
+ } else {
+ goto fail;
+ }
+ }
+
+ /* on encrypt, separate the output buffer from the tag */
+ if (encrypt) {
+ if ((length < taglen) || (length > inlen + taglen)) {
+ /* PKCS #11 module should not return a length smaller than
+ * taglen, or bigger than inlen+taglen */
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ rv = SECFailure;
+ goto fail;
+ }
+ length = length - taglen;
+ if (allocOut) {
+ /*
+ * If we used a temporary buffer, copy it out to the original
+ * buffer.
+ */
+ PORT_Memcpy(saveOut, allocOut, length);
+ }
+ /* if the tag isn't in the right place, copy it out */
+ if (tag != out + length) {
+ PORT_Memcpy(tag, out + length, taglen);
+ }
+ }
+ *outlen = length;
+ rv = SECSuccess;
+fail:
+ if (allocOut) {
+ PORT_Free(allocOut);
+ }
+ return rv;
+}
+
+/*
+ * Do an AEAD operation. This function optionally returns
+ * and IV on Encrypt for all mechanism. NSS knows which mechanisms
+ * generate IV's in the token and which don't. This allows the
+ * applications to make a single call without special handling for
+ * each AEAD mechanism (the special handling is all contained here.
+ */
+SECStatus
+PK11_AEADOp(PK11Context *context, CK_GENERATOR_FUNCTION ivgen,
+ int fixedbits, unsigned char *iv, int ivlen,
+ const unsigned char *aad, int aadlen,
+ unsigned char *out, int *outlen,
+ int maxout, unsigned char *tag, int taglen,
+ const unsigned char *in, int inlen)
+{
+ CK_GCM_MESSAGE_PARAMS gcm_message;
+ CK_CCM_MESSAGE_PARAMS ccm_message;
+ CK_SALSA20_CHACHA20_POLY1305_MSG_PARAMS chacha_poly_message;
+ void *params;
+ int paramslen;
+ SECStatus rv;
+
+ switch (context->simulate_mechanism) {
+ case CKM_CHACHA20_POLY1305:
+ case CKM_SALSA20_POLY1305:
+ case CKM_NSS_CHACHA20_POLY1305:
+ chacha_poly_message.pNonce = iv;
+ chacha_poly_message.ulNonceLen = ivlen;
+ chacha_poly_message.pTag = tag;
+ params = &chacha_poly_message;
+ paramslen = sizeof(CK_SALSA20_CHACHA20_POLY1305_MSG_PARAMS);
+ /* SALSA20_POLY1305 and CHACHA20_POLY1305 do not generate the iv
+ * internally, Do it here. */
+ if (context->operation == (CKA_NSS_MESSAGE | CKA_ENCRYPT)) {
+ /* simulate generating the IV */
+ rv = pk11_GenerateIV(context, ivgen, fixedbits, iv, ivlen);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ }
+ break;
+ case CKM_AES_GCM:
+ gcm_message.pIv = iv;
+ gcm_message.ulIvLen = ivlen;
+ gcm_message.ivGenerator = ivgen;
+ gcm_message.ulIvFixedBits = fixedbits;
+ gcm_message.pTag = tag;
+ gcm_message.ulTagBits = taglen * 8;
+ params = &gcm_message;
+ paramslen = sizeof(CK_GCM_MESSAGE_PARAMS);
+ /* GCM generates IV internally */
+ break;
+ case CKM_AES_CCM:
+ ccm_message.ulDataLen = inlen;
+ ccm_message.pNonce = iv;
+ ccm_message.ulNonceLen = ivlen;
+ ccm_message.nonceGenerator = ivgen;
+ ccm_message.ulNonceFixedBits = fixedbits;
+ ccm_message.pMAC = tag;
+ ccm_message.ulMACLen = taglen;
+ params = &ccm_message;
+ paramslen = sizeof(CK_GCM_MESSAGE_PARAMS);
+ /* CCM generates IV internally */
+ break;
+
+ default:
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ return SECFailure;
+ }
+ return PK11_AEADRawOp(context, params, paramslen, aad, aadlen, out, outlen,
+ maxout, in, inlen);
+}
+
+/* Do and AED operation. The application builds the params on it's own
+ * and passes them in. This allows applications direct access to the params
+ * so they can use mechanisms not yet understood by, NSS, or get semantics
+ * not suppied by PK11_AEAD. */
+SECStatus
+PK11_AEADRawOp(PK11Context *context, void *params, int paramslen,
+ const unsigned char *aad, int aadlen,
+ unsigned char *out, int *outlen,
+ int maxout, const unsigned char *in, int inlen)
+{
+ CK_RV crv = CKR_OK;
+ CK_ULONG length = maxout;
+ SECStatus rv = SECSuccess;
+
+ PORT_Assert(outlen != NULL);
+ *outlen = 0;
+ if (((context->operation) & CKA_NSS_MESSAGE_MASK) != CKA_NSS_MESSAGE) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ /*
+ * The PKCS 11 module does not support the message interface, fall
+ * back to using single shot operation */
+ if (context->simulate_message) {
+ return pk11_AEADSimulateOp(context, params, paramslen, aad, aadlen,
+ out, outlen, maxout, in, inlen);
+ }
+
+ /* if we ran out of session, we need to restore our previously stored
+ * state.
+ */
+ PK11_EnterContextMonitor(context);
+ if (!context->ownSession) {
+ rv = pk11_restoreContext(context, context->savedData,
+ context->savedLength);
+ if (rv != SECSuccess) {
+ PK11_ExitContextMonitor(context);
+ return rv;
+ }
+ }
+
+ switch (context->operation) {
+ case CKA_NSS_MESSAGE | CKA_ENCRYPT:
+ length = maxout;
+ crv = PK11_GETTAB(context->slot)->C_EncryptMessage(context->session, params, paramslen, (CK_BYTE_PTR)aad, aadlen, (CK_BYTE_PTR)in, inlen, out, &length);
+ break;
+ case CKA_NSS_MESSAGE | CKA_DECRYPT:
+ length = maxout;
+ crv = PK11_GETTAB(context->slot)->C_DecryptMessage(context->session, params, paramslen, (CK_BYTE_PTR)aad, aadlen, (CK_BYTE_PTR)in, inlen, out, &length);
+ break;
+ case CKA_NSS_MESSAGE | CKA_SIGN:
+ length = maxout;
+ crv = PK11_GETTAB(context->slot)->C_SignMessage(context->session, params, paramslen, (CK_BYTE_PTR)in, inlen, out, &length);
+ break;
+ case CKA_NSS_MESSAGE | CKA_VERIFY:
+ length = maxout; /* sig length */
+ crv = PK11_GETTAB(context->slot)->C_VerifyMessage(context->session, params, paramslen, (CK_BYTE_PTR)in, inlen, out /* sig */, length);
+ break;
+ default:
+ crv = CKR_OPERATION_NOT_INITIALIZED;
+ break;
+ }
+
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ rv = SECFailure;
+ } else {
+ *outlen = length;
+ }
+
+ /*
+ * handle session starvation case.. use our last session to multiplex
+ */
+ if (!context->ownSession) {
+ context->savedData = pk11_saveContext(context, context->savedData,
+ &context->savedLength);
+ if (context->savedData == NULL)
+ rv = SECFailure;
+
+ /* clear out out session for others to use */
+ pk11_Finalize(context);
+ }
+ PK11_ExitContextMonitor(context);
+ return rv;
+}
+
+/*
+ * execute a digest/signature operation
+ */
+SECStatus
+PK11_DigestOp(PK11Context *context, const unsigned char *in, unsigned inLen)
+{
+ CK_RV crv = CKR_OK;
+ SECStatus rv = SECSuccess;
+
+ if (inLen == 0) {
+ return SECSuccess;
+ }
+ if (!in) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ /* if we ran out of session, we need to restore our previously stored
+ * state.
+ */
+ context->init = PR_FALSE;
+ PK11_EnterContextMonitor(context);
+ if (!context->ownSession) {
+ rv = pk11_restoreContext(context, context->savedData,
+ context->savedLength);
+ if (rv != SECSuccess) {
+ PK11_ExitContextMonitor(context);
+ return rv;
+ }
+ }
+
+ switch (context->operation) {
+ /* also for MAC'ing */
+ case CKA_SIGN:
+ crv = PK11_GETTAB(context->slot)->C_SignUpdate(context->session, (unsigned char *)in, inLen);
+ break;
+ case CKA_VERIFY:
+ crv = PK11_GETTAB(context->slot)->C_VerifyUpdate(context->session, (unsigned char *)in, inLen);
+ break;
+ case CKA_DIGEST:
+ crv = PK11_GETTAB(context->slot)->C_DigestUpdate(context->session, (unsigned char *)in, inLen);
+ break;
+ default:
+ crv = CKR_OPERATION_NOT_INITIALIZED;
+ break;
+ }
+
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ rv = SECFailure;
+ }
+
+ /*
+ * handle session starvation case.. use our last session to multiplex
+ */
+ if (!context->ownSession) {
+ context->savedData = pk11_saveContext(context, context->savedData,
+ &context->savedLength);
+ if (context->savedData == NULL)
+ rv = SECFailure;
+
+ /* clear out out session for others to use */
+ pk11_Finalize(context);
+ }
+ PK11_ExitContextMonitor(context);
+ return rv;
+}
+
+/*
+ * Digest a key if possible./
+ */
+SECStatus
+PK11_DigestKey(PK11Context *context, PK11SymKey *key)
+{
+ CK_RV crv = CKR_OK;
+ SECStatus rv = SECSuccess;
+ PK11SymKey *newKey = NULL;
+
+ if (!context || !key) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ /* if we ran out of session, we need to restore our previously stored
+ * state.
+ */
+ if (context->slot != key->slot) {
+ newKey = pk11_CopyToSlot(context->slot, CKM_SSL3_SHA1_MAC, CKA_SIGN, key);
+ } else {
+ newKey = PK11_ReferenceSymKey(key);
+ }
+
+ context->init = PR_FALSE;
+ PK11_EnterContextMonitor(context);
+ if (!context->ownSession) {
+ rv = pk11_restoreContext(context, context->savedData,
+ context->savedLength);
+ if (rv != SECSuccess) {
+ PK11_ExitContextMonitor(context);
+ PK11_FreeSymKey(newKey);
+ return rv;
+ }
+ }
+
+ if (newKey == NULL) {
+ crv = CKR_KEY_TYPE_INCONSISTENT;
+ if (key->data.data) {
+ crv = PK11_GETTAB(context->slot)->C_DigestUpdate(context->session, key->data.data, key->data.len);
+ }
+ } else {
+ crv = PK11_GETTAB(context->slot)->C_DigestKey(context->session, newKey->objectID);
+ }
+
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ rv = SECFailure;
+ }
+
+ /*
+ * handle session starvation case.. use our last session to multiplex
+ */
+ if (!context->ownSession) {
+ context->savedData = pk11_saveContext(context, context->savedData,
+ &context->savedLength);
+ if (context->savedData == NULL)
+ rv = SECFailure;
+
+ /* clear out out session for others to use */
+ pk11_Finalize(context);
+ }
+ PK11_ExitContextMonitor(context);
+ if (newKey)
+ PK11_FreeSymKey(newKey);
+ return rv;
+}
+
+/*
+ * externally callable version of the lowercase pk11_finalize().
+ */
+SECStatus
+PK11_Finalize(PK11Context *context)
+{
+ SECStatus rv;
+
+ PK11_EnterContextMonitor(context);
+ rv = pk11_Finalize(context);
+ PK11_ExitContextMonitor(context);
+ return rv;
+}
+
+/*
+ * clean up a cipher operation, so the session can be used by
+ * someone new.
+ */
+SECStatus
+pk11_Finalize(PK11Context *context)
+{
+ CK_ULONG count = 0;
+ CK_RV crv;
+ unsigned char stackBuf[256];
+ unsigned char *buffer = NULL;
+
+ if (!context->ownSession) {
+ return SECSuccess;
+ }
+
+finalize:
+ switch (context->operation) {
+ case CKA_ENCRYPT:
+ crv = PK11_GETTAB(context->slot)->C_EncryptFinal(context->session, buffer, &count);
+ break;
+ case CKA_DECRYPT:
+ crv = PK11_GETTAB(context->slot)->C_DecryptFinal(context->session, buffer, &count);
+ break;
+ case CKA_SIGN:
+ crv = PK11_GETTAB(context->slot)->C_SignFinal(context->session, buffer, &count);
+ break;
+ case CKA_VERIFY:
+ crv = PK11_GETTAB(context->slot)->C_VerifyFinal(context->session, buffer, count);
+ break;
+ case CKA_DIGEST:
+ crv = PK11_GETTAB(context->slot)->C_DigestFinal(context->session, buffer, &count);
+ break;
+ case CKA_NSS_MESSAGE | CKA_ENCRYPT:
+ crv = PK11_GETTAB(context->slot)->C_MessageEncryptFinal(context->session);
+ break;
+ case CKA_NSS_MESSAGE | CKA_DECRYPT:
+ crv = PK11_GETTAB(context->slot)->C_MessageDecryptFinal(context->session);
+ break;
+ case CKA_NSS_MESSAGE | CKA_SIGN:
+ crv = PK11_GETTAB(context->slot)->C_MessageSignFinal(context->session);
+ break;
+ case CKA_NSS_MESSAGE | CKA_VERIFY:
+ crv = PK11_GETTAB(context->slot)->C_MessageVerifyFinal(context->session);
+ break;
+ default:
+ crv = CKR_OPERATION_NOT_INITIALIZED;
+ break;
+ }
+
+ if (crv != CKR_OK) {
+ if (buffer != stackBuf) {
+ PORT_Free(buffer);
+ }
+ if (crv == CKR_OPERATION_NOT_INITIALIZED) {
+ /* if there's no operation, it is finalized */
+ return SECSuccess;
+ }
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+
+ /* Message interface does not need to allocate a final buffer */
+ if (((context->operation) & CKA_NSS_MESSAGE_MASK) == CKA_NSS_MESSAGE) {
+ return SECSuccess;
+ }
+
+ /* try to finalize the session with a buffer */
+ if (buffer == NULL) {
+ if (count <= sizeof stackBuf) {
+ buffer = stackBuf;
+ } else {
+ buffer = PORT_Alloc(count);
+ if (buffer == NULL) {
+ return SECFailure;
+ }
+ }
+ goto finalize;
+ }
+ if (buffer != stackBuf) {
+ PORT_Free(buffer);
+ }
+ return SECSuccess;
+}
+
+/*
+ * Return the final digested or signed data...
+ * this routine can either take pre initialized data, or allocate data
+ * either out of an arena or out of the standard heap.
+ */
+SECStatus
+PK11_DigestFinal(PK11Context *context, unsigned char *data,
+ unsigned int *outLen, unsigned int length)
+{
+ CK_ULONG len;
+ CK_RV crv;
+ SECStatus rv;
+
+ /* message interface returns no data on Final, Should not use DigestFinal
+ * in this case */
+ if (((context->operation) & CKA_NSS_MESSAGE_MASK) == CKA_NSS_MESSAGE) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ /* if we ran out of session, we need to restore our previously stored
+ * state.
+ */
+ PK11_EnterContextMonitor(context);
+ if (!context->ownSession) {
+ rv = pk11_restoreContext(context, context->savedData,
+ context->savedLength);
+ if (rv != SECSuccess) {
+ PK11_ExitContextMonitor(context);
+ return rv;
+ }
+ }
+
+ len = length;
+ switch (context->operation) {
+ case CKA_SIGN:
+ crv = PK11_GETTAB(context->slot)->C_SignFinal(context->session, data, &len);
+ break;
+ case CKA_VERIFY:
+ crv = PK11_GETTAB(context->slot)->C_VerifyFinal(context->session, data, len);
+ break;
+ case CKA_DIGEST:
+ crv = PK11_GETTAB(context->slot)->C_DigestFinal(context->session, data, &len);
+ break;
+ case CKA_ENCRYPT:
+ crv = PK11_GETTAB(context->slot)->C_EncryptFinal(context->session, data, &len);
+ break;
+ case CKA_DECRYPT:
+ crv = PK11_GETTAB(context->slot)->C_DecryptFinal(context->session, data, &len);
+ break;
+ default:
+ crv = CKR_OPERATION_NOT_INITIALIZED;
+ break;
+ }
+ PK11_ExitContextMonitor(context);
+
+ context->init = PR_FALSE; /* allow Begin to start up again */
+
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ *outLen = (unsigned int)len;
+ return SECSuccess;
+}
+
+PRBool
+PK11_ContextGetFIPSStatus(PK11Context *context)
+{
+ if (context->slot == NULL) {
+ return PR_FALSE;
+ }
+ return pk11slot_GetFIPSStatus(context->slot, context->session,
+ CK_INVALID_HANDLE, context->init ? CKT_NSS_SESSION_CHECK : CKT_NSS_SESSION_LAST_CHECK);
+}
diff --git a/security/nss/lib/pk11wrap/pk11err.c b/security/nss/lib/pk11wrap/pk11err.c
new file mode 100644
index 0000000000..8f4fd29ba8
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11err.c
@@ -0,0 +1,141 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/*
+ * this file maps PKCS11 Errors into SECErrors
+ * This is an information reducing process, since most errors are reflected
+ * back to the user (the user doesn't care about invalid flags, or active
+ * operations). If any of these errors need more detail in the upper layers
+ * which call PK11 library functions, we can add more SEC_ERROR_XXX functions
+ * and change there mappings here.
+ *
+ * Some PKCS11 errors are mapped to SEC_ERROR_LIBRARY_FAILURE intentionally
+ * because they indicate that there is a bug in the library (either NSS or
+ * the token).
+ */
+#include "pkcs11t.h"
+#include "pk11func.h"
+#include "secerr.h"
+#include "prerror.h"
+
+#ifdef PK11_ERROR_USE_ARRAY
+
+/*
+ * build a static array of entries...
+ */
+static struct {
+ CK_RV pk11_error;
+ int sec_error;
+} pk11_error_map = {
+#define MAPERROR(x, y) { x, y },
+
+#else
+
+/* the default is to use a big switch statement */
+int
+PK11_MapError(CK_RV rv)
+{
+
+ switch (rv) {
+#define MAPERROR(x, y) \
+ case x: \
+ return y;
+
+#endif
+
+ /* the guts mapping */
+ /* clang-format off */
+ MAPERROR(CKR_OK, 0)
+ MAPERROR(CKR_CANCEL, SEC_ERROR_IO)
+ MAPERROR(CKR_HOST_MEMORY, SEC_ERROR_NO_MEMORY)
+ MAPERROR(CKR_SLOT_ID_INVALID, SEC_ERROR_BAD_DATA)
+ MAPERROR(CKR_ARGUMENTS_BAD, SEC_ERROR_INVALID_ARGS)
+ MAPERROR(CKR_ATTRIBUTE_READ_ONLY, SEC_ERROR_READ_ONLY)
+ MAPERROR(CKR_ATTRIBUTE_SENSITIVE, SEC_ERROR_IO) /* XX SENSITIVE */
+ MAPERROR(CKR_ATTRIBUTE_TYPE_INVALID, SEC_ERROR_BAD_DATA)
+ MAPERROR(CKR_ATTRIBUTE_VALUE_INVALID, SEC_ERROR_BAD_DATA)
+ MAPERROR(CKR_BUFFER_TOO_SMALL, SEC_ERROR_OUTPUT_LEN)
+ MAPERROR(CKR_DATA_INVALID, SEC_ERROR_BAD_DATA)
+ MAPERROR(CKR_DATA_LEN_RANGE, SEC_ERROR_INPUT_LEN)
+ MAPERROR(CKR_DEVICE_ERROR, SEC_ERROR_PKCS11_DEVICE_ERROR)
+ MAPERROR(CKR_DEVICE_MEMORY, SEC_ERROR_NO_MEMORY)
+ MAPERROR(CKR_DEVICE_REMOVED, SEC_ERROR_NO_TOKEN)
+ MAPERROR(CKR_DOMAIN_PARAMS_INVALID, SEC_ERROR_INVALID_KEY)
+ MAPERROR(CKR_ENCRYPTED_DATA_INVALID, SEC_ERROR_BAD_DATA)
+ MAPERROR(CKR_ENCRYPTED_DATA_LEN_RANGE, SEC_ERROR_BAD_DATA)
+ MAPERROR(CKR_FUNCTION_CANCELED, SEC_ERROR_LIBRARY_FAILURE)
+ MAPERROR(CKR_FUNCTION_FAILED, SEC_ERROR_PKCS11_FUNCTION_FAILED)
+ MAPERROR(CKR_FUNCTION_NOT_PARALLEL, SEC_ERROR_LIBRARY_FAILURE)
+ MAPERROR(CKR_FUNCTION_NOT_SUPPORTED, PR_NOT_IMPLEMENTED_ERROR)
+ MAPERROR(CKR_GENERAL_ERROR, SEC_ERROR_PKCS11_GENERAL_ERROR)
+ MAPERROR(CKR_KEY_HANDLE_INVALID, SEC_ERROR_INVALID_KEY)
+ MAPERROR(CKR_KEY_SIZE_RANGE, SEC_ERROR_INVALID_KEY)
+ MAPERROR(CKR_KEY_TYPE_INCONSISTENT, SEC_ERROR_INVALID_KEY)
+ MAPERROR(CKR_MECHANISM_INVALID, SEC_ERROR_INVALID_ALGORITHM)
+ MAPERROR(CKR_MECHANISM_PARAM_INVALID, SEC_ERROR_BAD_DATA)
+ MAPERROR(CKR_NO_EVENT, SEC_ERROR_NO_EVENT)
+ MAPERROR(CKR_OBJECT_HANDLE_INVALID, SEC_ERROR_BAD_DATA)
+ MAPERROR(CKR_OPERATION_ACTIVE, SEC_ERROR_LIBRARY_FAILURE)
+ MAPERROR(CKR_OPERATION_NOT_INITIALIZED, SEC_ERROR_LIBRARY_FAILURE)
+ MAPERROR(CKR_PIN_INCORRECT, SEC_ERROR_BAD_PASSWORD)
+ MAPERROR(CKR_PIN_INVALID, SEC_ERROR_INVALID_PASSWORD)
+ MAPERROR(CKR_PIN_LEN_RANGE, SEC_ERROR_INVALID_PASSWORD)
+ MAPERROR(CKR_PIN_EXPIRED, SEC_ERROR_EXPIRED_PASSWORD)
+ MAPERROR(CKR_PIN_LOCKED, SEC_ERROR_LOCKED_PASSWORD)
+ MAPERROR(CKR_SESSION_CLOSED, SEC_ERROR_LIBRARY_FAILURE)
+ MAPERROR(CKR_SESSION_COUNT, SEC_ERROR_NO_MEMORY) /* XXXX? */
+ MAPERROR(CKR_SESSION_HANDLE_INVALID, SEC_ERROR_BAD_DATA)
+ MAPERROR(CKR_SESSION_PARALLEL_NOT_SUPPORTED, SEC_ERROR_LIBRARY_FAILURE)
+ MAPERROR(CKR_SESSION_READ_ONLY, SEC_ERROR_READ_ONLY)
+ MAPERROR(CKR_SIGNATURE_INVALID, SEC_ERROR_BAD_SIGNATURE)
+ MAPERROR(CKR_SIGNATURE_LEN_RANGE, SEC_ERROR_BAD_SIGNATURE)
+ MAPERROR(CKR_TEMPLATE_INCOMPLETE, SEC_ERROR_BAD_DATA)
+ MAPERROR(CKR_TEMPLATE_INCONSISTENT, SEC_ERROR_BAD_DATA)
+ MAPERROR(CKR_TOKEN_NOT_PRESENT, SEC_ERROR_NO_TOKEN)
+ MAPERROR(CKR_TOKEN_NOT_RECOGNIZED, SEC_ERROR_IO)
+ MAPERROR(CKR_TOKEN_WRITE_PROTECTED, SEC_ERROR_READ_ONLY)
+ MAPERROR(CKR_UNWRAPPING_KEY_HANDLE_INVALID, SEC_ERROR_INVALID_KEY)
+ MAPERROR(CKR_UNWRAPPING_KEY_SIZE_RANGE, SEC_ERROR_INVALID_KEY)
+ MAPERROR(CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT, SEC_ERROR_INVALID_KEY)
+ MAPERROR(CKR_USER_ALREADY_LOGGED_IN, 0)
+ MAPERROR(CKR_USER_NOT_LOGGED_IN, SEC_ERROR_TOKEN_NOT_LOGGED_IN)
+ MAPERROR(CKR_USER_PIN_NOT_INITIALIZED, SEC_ERROR_NO_TOKEN)
+ MAPERROR(CKR_USER_TYPE_INVALID, SEC_ERROR_LIBRARY_FAILURE)
+ MAPERROR(CKR_WRAPPED_KEY_INVALID, SEC_ERROR_INVALID_KEY)
+ MAPERROR(CKR_WRAPPED_KEY_LEN_RANGE, SEC_ERROR_INVALID_KEY)
+ MAPERROR(CKR_WRAPPING_KEY_HANDLE_INVALID, SEC_ERROR_INVALID_KEY)
+ MAPERROR(CKR_WRAPPING_KEY_SIZE_RANGE, SEC_ERROR_INVALID_KEY)
+ MAPERROR(CKR_WRAPPING_KEY_TYPE_INCONSISTENT, SEC_ERROR_INVALID_KEY)
+ MAPERROR(CKR_VENDOR_DEFINED, SEC_ERROR_LIBRARY_FAILURE)
+ MAPERROR(CKR_NSS_CERTDB_FAILED, SEC_ERROR_BAD_DATABASE)
+ MAPERROR(CKR_NSS_KEYDB_FAILED, SEC_ERROR_BAD_DATABASE)
+ MAPERROR(CKR_CANT_LOCK, SEC_ERROR_INCOMPATIBLE_PKCS11)
+/* clang-format on */
+
+#ifdef PK11_ERROR_USE_ARRAY
+};
+
+int
+PK11_MapError(CK_RV rv)
+{
+ int size = sizeof(pk11_error_map) / sizeof(pk11_error_map[0]);
+
+ for (i = 0; i < size; i++) {
+ if (pk11_error_map[i].pk11_error == rv) {
+ return pk11_error_map[i].sec_error;
+ }
+ }
+ return SEC_ERROR_UNKNOWN_PKCS11_ERROR;
+}
+
+#else
+
+ /* clang-format off */
+ default :
+ break;
+ /* clang-format on */
+ }
+ return SEC_ERROR_UNKNOWN_PKCS11_ERROR;
+}
+
+#endif
diff --git a/security/nss/lib/pk11wrap/pk11func.h b/security/nss/lib/pk11wrap/pk11func.h
new file mode 100644
index 0000000000..331c0eb80c
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11func.h
@@ -0,0 +1,15 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _PK11FUNC_H_
+#define _PK11FUNC_H_
+
+/*
+ * The original pk11func.h had a mix of public and private functions.
+ * Continue to provide those for backward compatibility. New code should
+ * include pk11pub.h instead of pk11func.h.
+ */
+#include "pk11pub.h"
+#include "pk11priv.h"
+
+#endif
diff --git a/security/nss/lib/pk11wrap/pk11hpke.c b/security/nss/lib/pk11wrap/pk11hpke.c
new file mode 100644
index 0000000000..7c4bfc3cdc
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11hpke.c
@@ -0,0 +1,1276 @@
+/*
+ * draft-irtf-cfrg-hpke-07
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "keyhi.h"
+#include "pkcs11t.h"
+#include "pk11func.h"
+#include "pk11hpke.h"
+#include "pk11pqg.h"
+#include "secerr.h"
+#include "secitem.h"
+#include "secmod.h"
+#include "secmodi.h"
+#include "secmodti.h"
+#include "secutil.h"
+
+#define SERIALIZATION_VERSION 2
+
+static const char *V1_LABEL = "HPKE-v1";
+static const char *EXP_LABEL = "exp";
+static const char *HPKE_LABEL = "HPKE";
+static const char *INFO_LABEL = "info_hash";
+static const char *KEM_LABEL = "KEM";
+static const char *KEY_LABEL = "key";
+static const char *NONCE_LABEL = "base_nonce";
+static const char *PSK_ID_LABEL = "psk_id_hash";
+static const char *SECRET_LABEL = "secret";
+static const char *SEC_LABEL = "sec";
+static const char *EAE_PRK_LABEL = "eae_prk";
+static const char *SH_SEC_LABEL = "shared_secret";
+
+struct HpkeContextStr {
+ const hpkeKemParams *kemParams;
+ const hpkeKdfParams *kdfParams;
+ const hpkeAeadParams *aeadParams;
+ PRUint8 mode; /* Base and PSK modes supported. */
+ SECItem *encapPubKey; /* Marshalled public key, sent to receiver. */
+ SECItem *baseNonce; /* Deterministic nonce for AEAD. */
+ SECItem *pskId; /* PSK identifier (non-secret). */
+ PK11Context *aeadContext; /* AEAD context used by Seal/Open. */
+ PRUint64 sequenceNumber; /* seqNo for decrypt IV construction. */
+ PK11SymKey *sharedSecret; /* ExtractAndExpand output key. */
+ PK11SymKey *key; /* Key used with the AEAD. */
+ PK11SymKey *exporterSecret; /* Derivation key for ExportSecret. */
+ PK11SymKey *psk; /* PSK imported by the application. */
+};
+
+static const hpkeKemParams kemParams[] = {
+ /* KEM, Nsk, Nsecret, Npk, oidTag, Hash mechanism */
+ { HpkeDhKemX25519Sha256, 32, 32, 32, SEC_OID_CURVE25519, CKM_SHA256 },
+};
+
+#define MAX_WRAPPED_EXP_LEN 72 // Largest kdfParams->Nh + 8
+static const hpkeKdfParams kdfParams[] = {
+ /* KDF, Nh, mechanism */
+ { HpkeKdfHkdfSha256, SHA256_LENGTH, CKM_SHA256 },
+ { HpkeKdfHkdfSha384, SHA384_LENGTH, CKM_SHA384 },
+ { HpkeKdfHkdfSha512, SHA512_LENGTH, CKM_SHA512 },
+};
+#define MAX_WRAPPED_KEY_LEN 40 // Largest aeadParams->Nk + 8
+static const hpkeAeadParams aeadParams[] = {
+ /* AEAD, Nk, Nn, tagLen, mechanism */
+ { HpkeAeadAes128Gcm, 16, 12, 16, CKM_AES_GCM },
+ { HpkeAeadAes256Gcm, 32, 12, 16, CKM_AES_GCM },
+ { HpkeAeadChaCha20Poly1305, 32, 12, 16, CKM_CHACHA20_POLY1305 },
+};
+
+static inline const hpkeKemParams *
+kemId2Params(HpkeKemId kemId)
+{
+ switch (kemId) {
+ case HpkeDhKemX25519Sha256:
+ return &kemParams[0];
+ default:
+ return NULL;
+ }
+}
+
+static inline const hpkeKdfParams *
+kdfId2Params(HpkeKdfId kdfId)
+{
+ switch (kdfId) {
+ case HpkeKdfHkdfSha256:
+ return &kdfParams[0];
+ case HpkeKdfHkdfSha384:
+ return &kdfParams[1];
+ case HpkeKdfHkdfSha512:
+ return &kdfParams[2];
+ default:
+ return NULL;
+ }
+}
+
+static const inline hpkeAeadParams *
+aeadId2Params(HpkeAeadId aeadId)
+{
+ switch (aeadId) {
+ case HpkeAeadAes128Gcm:
+ return &aeadParams[0];
+ case HpkeAeadAes256Gcm:
+ return &aeadParams[1];
+ case HpkeAeadChaCha20Poly1305:
+ return &aeadParams[2];
+ default:
+ return NULL;
+ }
+}
+
+static PRUint8 *
+encodeNumber(PRUint64 value, PRUint8 *b, size_t count)
+{
+ PRUint64 encoded;
+ PORT_Assert(b && count > 0 && count <= sizeof(encoded));
+
+ encoded = PR_htonll(value);
+ PORT_Memcpy(b, ((unsigned char *)(&encoded)) + (sizeof(encoded) - count),
+ count);
+ return b + count;
+}
+
+static PRUint8 *
+decodeNumber(PRUint64 *value, PRUint8 *b, size_t count)
+{
+ unsigned int i;
+ PRUint64 number = 0;
+ PORT_Assert(b && value && count <= sizeof(*value));
+
+ for (i = 0; i < count; i++) {
+ number = (number << 8) + b[i];
+ }
+ *value = number;
+ return b + count;
+}
+
+SECStatus
+PK11_HPKE_ValidateParameters(HpkeKemId kemId, HpkeKdfId kdfId, HpkeAeadId aeadId)
+{
+ /* If more variants are added, ensure the combination is also
+ * legal. For now it is, since only the AEAD may vary. */
+ const hpkeKemParams *kem = kemId2Params(kemId);
+ const hpkeKdfParams *kdf = kdfId2Params(kdfId);
+ const hpkeAeadParams *aead = aeadId2Params(aeadId);
+ if (!kem || !kdf || !aead) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+HpkeContext *
+PK11_HPKE_NewContext(HpkeKemId kemId, HpkeKdfId kdfId, HpkeAeadId aeadId,
+ PK11SymKey *psk, const SECItem *pskId)
+{
+ SECStatus rv = SECSuccess;
+ PK11SlotInfo *slot = NULL;
+ HpkeContext *cx = NULL;
+ /* Both the PSK and the PSK ID default to empty. */
+ SECItem emptyItem = { siBuffer, NULL, 0 };
+
+ cx = PORT_ZNew(HpkeContext);
+ if (!cx) {
+ return NULL;
+ }
+ cx->mode = psk ? HpkeModePsk : HpkeModeBase;
+ cx->kemParams = kemId2Params(kemId);
+ cx->kdfParams = kdfId2Params(kdfId);
+ cx->aeadParams = aeadId2Params(aeadId);
+ CHECK_FAIL_ERR((!!psk != !!pskId), SEC_ERROR_INVALID_ARGS);
+ CHECK_FAIL_ERR(!cx->kemParams || !cx->kdfParams || !cx->aeadParams,
+ SEC_ERROR_INVALID_ARGS);
+
+ /* Import the provided PSK or the default. */
+ slot = PK11_GetBestSlot(CKM_EC_KEY_PAIR_GEN, NULL);
+ CHECK_FAIL(!slot);
+ if (psk) {
+ cx->psk = PK11_ReferenceSymKey(psk);
+ cx->pskId = SECITEM_DupItem(pskId);
+ } else {
+ cx->psk = PK11_ImportDataKey(slot, CKM_HKDF_DATA, PK11_OriginUnwrap,
+ CKA_DERIVE, &emptyItem, NULL);
+ cx->pskId = SECITEM_DupItem(&emptyItem);
+ }
+ CHECK_FAIL(!cx->psk);
+ CHECK_FAIL(!cx->pskId);
+
+CLEANUP:
+ if (rv != SECSuccess) {
+ PK11_FreeSymKey(cx->psk);
+ SECITEM_FreeItem(cx->pskId, PR_TRUE);
+ cx->pskId = NULL;
+ cx->psk = NULL;
+ PORT_Free(cx);
+ cx = NULL;
+ }
+ if (slot) {
+ PK11_FreeSlot(slot);
+ }
+ return cx;
+}
+
+void
+PK11_HPKE_DestroyContext(HpkeContext *cx, PRBool freeit)
+{
+ if (!cx) {
+ return;
+ }
+
+ if (cx->aeadContext) {
+ PK11_DestroyContext((PK11Context *)cx->aeadContext, PR_TRUE);
+ cx->aeadContext = NULL;
+ }
+ PK11_FreeSymKey(cx->exporterSecret);
+ PK11_FreeSymKey(cx->sharedSecret);
+ PK11_FreeSymKey(cx->key);
+ PK11_FreeSymKey(cx->psk);
+ SECITEM_FreeItem(cx->pskId, PR_TRUE);
+ SECITEM_FreeItem(cx->baseNonce, PR_TRUE);
+ SECITEM_FreeItem(cx->encapPubKey, PR_TRUE);
+ cx->exporterSecret = NULL;
+ cx->sharedSecret = NULL;
+ cx->key = NULL;
+ cx->psk = NULL;
+ cx->pskId = NULL;
+ cx->baseNonce = NULL;
+ cx->encapPubKey = NULL;
+ if (freeit) {
+ PORT_ZFree(cx, sizeof(HpkeContext));
+ }
+}
+
+/* Export Format:
+ struct {
+ uint8 serilizationVersion;
+ uint16 kemId;
+ uint16 kdfId;
+ uint16 aeadId;
+ uint16 modeId;
+ uint64 sequenceNumber;
+ opaque senderPubKey<1..2^16-1>;
+ opaque baseNonce<1..2^16-1>;
+ opaque key<1..2^16-1>;
+ opaque exporterSecret<1..2^16-1>;
+ } HpkeSerializedContext
+*/
+#define EXPORTED_CTX_BASE_LEN 25 /* Fixed size plus 2B for each variable. */
+#define REMAINING_BYTES(walker, buf) \
+ buf->len - (walker - buf->data)
+SECStatus
+PK11_HPKE_ExportContext(const HpkeContext *cx, PK11SymKey *wrapKey, SECItem **serialized)
+{
+ SECStatus rv;
+ size_t allocLen;
+ PRUint8 *walker;
+ SECItem *keyBytes = NULL; // Maybe wrapped
+ SECItem *exporterBytes = NULL; // Maybe wrapped
+ SECItem *serializedCx = NULL;
+ PRUint8 wrappedKeyBytes[MAX_WRAPPED_KEY_LEN] = { 0 };
+ PRUint8 wrappedExpBytes[MAX_WRAPPED_EXP_LEN] = { 0 };
+ SECItem wrappedKey = { siBuffer, wrappedKeyBytes, sizeof(wrappedKeyBytes) };
+ SECItem wrappedExp = { siBuffer, wrappedExpBytes, sizeof(wrappedExpBytes) };
+
+ CHECK_FAIL_ERR((!cx || !cx->aeadContext || !serialized), SEC_ERROR_INVALID_ARGS);
+ CHECK_FAIL_ERR((cx->aeadContext->operation != (CKA_NSS_MESSAGE | CKA_DECRYPT)),
+ SEC_ERROR_NOT_A_RECIPIENT);
+
+ /* If a wrapping key was provided, do the wrap first
+ * so that we know what size to allocate. */
+ if (wrapKey) {
+ rv = PK11_WrapSymKey(CKM_AES_KEY_WRAP_KWP, NULL, wrapKey,
+ cx->key, &wrappedKey);
+ CHECK_RV(rv);
+ rv = PK11_WrapSymKey(CKM_AES_KEY_WRAP_KWP, NULL, wrapKey,
+ cx->exporterSecret, &wrappedExp);
+ CHECK_RV(rv);
+
+ keyBytes = &wrappedKey;
+ exporterBytes = &wrappedExp;
+ } else {
+ rv = PK11_ExtractKeyValue(cx->key);
+ CHECK_RV(rv);
+ keyBytes = PK11_GetKeyData(cx->key);
+ CHECK_FAIL(!keyBytes);
+ PORT_Assert(keyBytes->len == cx->aeadParams->Nk);
+
+ rv = PK11_ExtractKeyValue(cx->exporterSecret);
+ CHECK_RV(rv);
+ exporterBytes = PK11_GetKeyData(cx->exporterSecret);
+ CHECK_FAIL(!exporterBytes);
+ PORT_Assert(exporterBytes->len == cx->kdfParams->Nh);
+ }
+
+ allocLen = EXPORTED_CTX_BASE_LEN + cx->baseNonce->len + cx->encapPubKey->len;
+ allocLen += wrapKey ? wrappedKey.len : cx->aeadParams->Nk;
+ allocLen += wrapKey ? wrappedExp.len : cx->kdfParams->Nh;
+
+ serializedCx = SECITEM_AllocItem(NULL, NULL, allocLen);
+ CHECK_FAIL(!serializedCx);
+
+ walker = &serializedCx->data[0];
+ *(walker)++ = (PRUint8)SERIALIZATION_VERSION;
+
+ walker = encodeNumber(cx->kemParams->id, walker, 2);
+ walker = encodeNumber(cx->kdfParams->id, walker, 2);
+ walker = encodeNumber(cx->aeadParams->id, walker, 2);
+ walker = encodeNumber(cx->mode, walker, 2);
+ walker = encodeNumber(cx->sequenceNumber, walker, 8);
+
+ /* sender public key, serialized. */
+ walker = encodeNumber(cx->encapPubKey->len, walker, 2);
+ PORT_Memcpy(walker, cx->encapPubKey->data, cx->encapPubKey->len);
+ walker += cx->encapPubKey->len;
+
+ /* base nonce */
+ walker = encodeNumber(cx->baseNonce->len, walker, 2);
+ PORT_Memcpy(walker, cx->baseNonce->data, cx->baseNonce->len);
+ walker += cx->baseNonce->len;
+
+ /* key. */
+ walker = encodeNumber(keyBytes->len, walker, 2);
+ PORT_Memcpy(walker, keyBytes->data, keyBytes->len);
+ walker += keyBytes->len;
+
+ /* exporter_secret. */
+ walker = encodeNumber(exporterBytes->len, walker, 2);
+ PORT_Memcpy(walker, exporterBytes->data, exporterBytes->len);
+ walker += exporterBytes->len;
+
+ CHECK_FAIL_ERR(REMAINING_BYTES(walker, serializedCx) != 0,
+ SEC_ERROR_LIBRARY_FAILURE);
+ *serialized = serializedCx;
+
+CLEANUP:
+ if (rv != SECSuccess) {
+ SECITEM_ZfreeItem(serializedCx, PR_TRUE);
+ }
+ return rv;
+}
+
+HpkeContext *
+PK11_HPKE_ImportContext(const SECItem *serialized, PK11SymKey *wrapKey)
+{
+ SECStatus rv = SECSuccess;
+ HpkeContext *cx = NULL;
+ PRUint8 *walker;
+ PRUint64 tmpn;
+ PRUint8 tmp8;
+ HpkeKemId kem;
+ HpkeKdfId kdf;
+ HpkeAeadId aead;
+ PK11SlotInfo *slot = NULL;
+ PK11SymKey *tmpKey = NULL;
+ SECItem tmpItem = { siBuffer, NULL, 0 };
+ SECItem emptyItem = { siBuffer, NULL, 0 };
+
+ CHECK_FAIL_ERR((!serialized || !serialized->data || serialized->len == 0),
+ SEC_ERROR_INVALID_ARGS);
+ CHECK_FAIL_ERR((serialized->len < EXPORTED_CTX_BASE_LEN), SEC_ERROR_BAD_DATA);
+
+ walker = serialized->data;
+
+ tmp8 = *(walker++);
+ CHECK_FAIL_ERR((tmp8 != SERIALIZATION_VERSION), SEC_ERROR_BAD_DATA);
+
+ walker = decodeNumber(&tmpn, walker, 2);
+ kem = (HpkeKemId)tmpn;
+
+ walker = decodeNumber(&tmpn, walker, 2);
+ kdf = (HpkeKdfId)tmpn;
+
+ walker = decodeNumber(&tmpn, walker, 2);
+ aead = (HpkeAeadId)tmpn;
+
+ /* Create context. We'll manually set the mode, though we
+ * no longer have the PSK and have no need for it. */
+ cx = PK11_HPKE_NewContext(kem, kdf, aead, NULL, NULL);
+ CHECK_FAIL(!cx);
+
+ walker = decodeNumber(&tmpn, walker, 2);
+ CHECK_FAIL_ERR((tmpn != HpkeModeBase && tmpn != HpkeModePsk),
+ SEC_ERROR_BAD_DATA);
+ cx->mode = (HpkeModeId)tmpn;
+
+ walker = decodeNumber(&cx->sequenceNumber, walker, 8);
+ slot = PK11_GetBestSlot(CKM_HKDF_DERIVE, NULL);
+ CHECK_FAIL(!slot);
+
+ /* Import sender public key (serialized). */
+ walker = decodeNumber(&tmpn, walker, 2);
+ CHECK_FAIL_ERR(tmpn >= REMAINING_BYTES(walker, serialized),
+ SEC_ERROR_BAD_DATA);
+ tmpItem.data = walker;
+ tmpItem.len = tmpn;
+ cx->encapPubKey = SECITEM_DupItem(&tmpItem);
+ CHECK_FAIL(!cx->encapPubKey);
+ walker += tmpItem.len;
+
+ /* Import base_nonce. */
+ walker = decodeNumber(&tmpn, walker, 2);
+ CHECK_FAIL_ERR(tmpn != cx->aeadParams->Nn, SEC_ERROR_BAD_DATA);
+ CHECK_FAIL_ERR(tmpn >= REMAINING_BYTES(walker, serialized),
+ SEC_ERROR_BAD_DATA);
+ tmpItem.data = walker;
+ tmpItem.len = tmpn;
+ cx->baseNonce = SECITEM_DupItem(&tmpItem);
+ CHECK_FAIL(!cx->baseNonce);
+ walker += tmpItem.len;
+
+ /* Import key */
+ walker = decodeNumber(&tmpn, walker, 2);
+ CHECK_FAIL_ERR(tmpn >= REMAINING_BYTES(walker, serialized),
+ SEC_ERROR_BAD_DATA);
+ tmpItem.data = walker;
+ tmpItem.len = tmpn;
+ walker += tmpItem.len;
+ if (wrapKey) {
+ cx->key = PK11_UnwrapSymKey(wrapKey, CKM_AES_KEY_WRAP_KWP,
+ NULL, &tmpItem, cx->aeadParams->mech,
+ CKA_NSS_MESSAGE | CKA_DECRYPT, 0);
+ CHECK_FAIL(!cx->key);
+ } else {
+ CHECK_FAIL_ERR(tmpn != cx->aeadParams->Nk, SEC_ERROR_BAD_DATA);
+ tmpKey = PK11_ImportSymKey(slot, cx->aeadParams->mech,
+ PK11_OriginUnwrap, CKA_NSS_MESSAGE | CKA_DECRYPT,
+ &tmpItem, NULL);
+ CHECK_FAIL(!tmpKey);
+ cx->key = tmpKey;
+ }
+
+ /* Import exporter_secret. */
+ walker = decodeNumber(&tmpn, walker, 2);
+ CHECK_FAIL_ERR(tmpn != REMAINING_BYTES(walker, serialized),
+ SEC_ERROR_BAD_DATA);
+ tmpItem.data = walker;
+ tmpItem.len = tmpn;
+ walker += tmpItem.len;
+
+ if (wrapKey) {
+ cx->exporterSecret = PK11_UnwrapSymKey(wrapKey, CKM_AES_KEY_WRAP_KWP,
+ NULL, &tmpItem, cx->kdfParams->mech,
+ CKM_HKDF_DERIVE, 0);
+ CHECK_FAIL(!cx->exporterSecret);
+ } else {
+ CHECK_FAIL_ERR(tmpn != cx->kdfParams->Nh, SEC_ERROR_BAD_DATA);
+ tmpKey = PK11_ImportSymKey(slot, CKM_HKDF_DERIVE, PK11_OriginUnwrap,
+ CKA_DERIVE, &tmpItem, NULL);
+ CHECK_FAIL(!tmpKey);
+ cx->exporterSecret = tmpKey;
+ }
+
+ cx->aeadContext = PK11_CreateContextBySymKey(cx->aeadParams->mech,
+ CKA_NSS_MESSAGE | CKA_DECRYPT,
+ cx->key, &emptyItem);
+
+CLEANUP:
+ if (rv != SECSuccess) {
+ PK11_FreeSymKey(tmpKey);
+ PK11_HPKE_DestroyContext(cx, PR_TRUE);
+ cx = NULL;
+ }
+ if (slot) {
+ PK11_FreeSlot(slot);
+ }
+
+ return cx;
+}
+
+SECStatus
+PK11_HPKE_Serialize(const SECKEYPublicKey *pk, PRUint8 *buf, unsigned int *len, unsigned int maxLen)
+{
+ if (!pk || !len || pk->keyType != ecKey) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ /* If no buffer provided, return the length required for
+ * the serialized public key. */
+ if (!buf) {
+ *len = pk->u.ec.publicValue.len;
+ return SECSuccess;
+ }
+
+ if (maxLen < pk->u.ec.publicValue.len) {
+ PORT_SetError(SEC_ERROR_INPUT_LEN);
+ return SECFailure;
+ }
+
+ PORT_Memcpy(buf, pk->u.ec.publicValue.data, pk->u.ec.publicValue.len);
+ *len = pk->u.ec.publicValue.len;
+ return SECSuccess;
+};
+
+SECStatus
+PK11_HPKE_Deserialize(const HpkeContext *cx, const PRUint8 *enc,
+ unsigned int encLen, SECKEYPublicKey **outPubKey)
+{
+ SECStatus rv;
+ SECKEYPublicKey *pubKey = NULL;
+ SECOidData *oidData = NULL;
+ PLArenaPool *arena;
+
+ if (!cx || !enc || encLen == 0 || !outPubKey) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ CHECK_FAIL(!arena);
+ pubKey = PORT_ArenaZNew(arena, SECKEYPublicKey);
+ CHECK_FAIL(!pubKey);
+
+ pubKey->arena = arena;
+ pubKey->keyType = ecKey;
+ pubKey->pkcs11Slot = NULL;
+ pubKey->pkcs11ID = CK_INVALID_HANDLE;
+
+ rv = SECITEM_MakeItem(pubKey->arena, &pubKey->u.ec.publicValue,
+ enc, encLen);
+ CHECK_RV(rv);
+ pubKey->u.ec.encoding = ECPoint_Undefined;
+ pubKey->u.ec.size = 0;
+
+ oidData = SECOID_FindOIDByTag(cx->kemParams->oidTag);
+ CHECK_FAIL_ERR(!oidData, SEC_ERROR_INVALID_ALGORITHM);
+
+ // Create parameters.
+ CHECK_FAIL(!SECITEM_AllocItem(pubKey->arena, &pubKey->u.ec.DEREncodedParams,
+ 2 + oidData->oid.len));
+
+ // Set parameters.
+ pubKey->u.ec.DEREncodedParams.data[0] = SEC_ASN1_OBJECT_ID;
+ pubKey->u.ec.DEREncodedParams.data[1] = oidData->oid.len;
+ PORT_Memcpy(pubKey->u.ec.DEREncodedParams.data + 2, oidData->oid.data, oidData->oid.len);
+ *outPubKey = pubKey;
+
+CLEANUP:
+ if (rv != SECSuccess) {
+ SECKEY_DestroyPublicKey(pubKey);
+ }
+ return rv;
+};
+
+static SECStatus
+pk11_hpke_CheckKeys(const HpkeContext *cx, const SECKEYPublicKey *pk,
+ const SECKEYPrivateKey *sk)
+{
+ SECOidTag pkTag;
+ unsigned int i;
+ if (pk->keyType != ecKey || (sk && sk->keyType != ecKey)) {
+ PORT_SetError(SEC_ERROR_BAD_KEY);
+ return SECFailure;
+ }
+ pkTag = SECKEY_GetECCOid(&pk->u.ec.DEREncodedParams);
+ if (pkTag != cx->kemParams->oidTag) {
+ PORT_SetError(SEC_ERROR_BAD_KEY);
+ return SECFailure;
+ }
+ for (i = 0; i < PR_ARRAY_SIZE(kemParams); i++) {
+ if (cx->kemParams->oidTag == kemParams[i].oidTag) {
+ return SECSuccess;
+ }
+ }
+
+ return SECFailure;
+}
+
+static SECStatus
+pk11_hpke_GenerateKeyPair(const HpkeContext *cx, SECKEYPublicKey **pkE,
+ SECKEYPrivateKey **skE)
+{
+ SECStatus rv = SECSuccess;
+ SECKEYPrivateKey *privKey = NULL;
+ SECKEYPublicKey *pubKey = NULL;
+ SECOidData *oidData = NULL;
+ SECKEYECParams ecp;
+ PK11SlotInfo *slot = NULL;
+ ecp.data = NULL;
+ PORT_Assert(cx && skE && pkE);
+
+ oidData = SECOID_FindOIDByTag(cx->kemParams->oidTag);
+ CHECK_FAIL_ERR(!oidData, SEC_ERROR_INVALID_ALGORITHM);
+ ecp.data = PORT_Alloc(2 + oidData->oid.len);
+ CHECK_FAIL(!ecp.data);
+
+ ecp.len = 2 + oidData->oid.len;
+ ecp.type = siDEROID;
+ ecp.data[0] = SEC_ASN1_OBJECT_ID;
+ ecp.data[1] = oidData->oid.len;
+ PORT_Memcpy(&ecp.data[2], oidData->oid.data, oidData->oid.len);
+
+ slot = PK11_GetBestSlot(CKM_EC_KEY_PAIR_GEN, NULL);
+ CHECK_FAIL(!slot);
+
+ privKey = PK11_GenerateKeyPair(slot, CKM_EC_KEY_PAIR_GEN, &ecp, &pubKey,
+ PR_FALSE, PR_TRUE, NULL);
+ CHECK_FAIL_ERR((!privKey || !pubKey), SEC_ERROR_KEYGEN_FAIL);
+ PORT_Assert(rv == SECSuccess);
+ *skE = privKey;
+ *pkE = pubKey;
+
+CLEANUP:
+ if (rv != SECSuccess) {
+ SECKEY_DestroyPrivateKey(privKey);
+ SECKEY_DestroyPublicKey(pubKey);
+ }
+ if (slot) {
+ PK11_FreeSlot(slot);
+ }
+ PORT_Free(ecp.data);
+ return rv;
+}
+
+static inline SECItem *
+pk11_hpke_MakeExtractLabel(const char *prefix, unsigned int prefixLen,
+ const char *label, unsigned int labelLen,
+ const SECItem *suiteId, const SECItem *ikm)
+{
+ SECItem *out = NULL;
+ PRUint8 *walker;
+ out = SECITEM_AllocItem(NULL, NULL, prefixLen + labelLen + suiteId->len + (ikm ? ikm->len : 0));
+ if (!out) {
+ return NULL;
+ }
+
+ walker = out->data;
+ PORT_Memcpy(walker, prefix, prefixLen);
+ walker += prefixLen;
+ PORT_Memcpy(walker, suiteId->data, suiteId->len);
+ walker += suiteId->len;
+ PORT_Memcpy(walker, label, labelLen);
+ walker += labelLen;
+ if (ikm && ikm->data) {
+ PORT_Memcpy(walker, ikm->data, ikm->len);
+ }
+
+ return out;
+}
+
+static SECStatus
+pk11_hpke_LabeledExtractData(const HpkeContext *cx, SECItem *salt,
+ const SECItem *suiteId, const char *label,
+ unsigned int labelLen, const SECItem *ikm, SECItem **out)
+{
+ SECStatus rv;
+ CK_HKDF_PARAMS params = { 0 };
+ PK11SymKey *importedIkm = NULL;
+ PK11SymKey *prk = NULL;
+ PK11SlotInfo *slot = NULL;
+ SECItem *borrowed;
+ SECItem *outDerived = NULL;
+ SECItem *labeledIkm;
+ SECItem paramsItem = { siBuffer, (unsigned char *)&params,
+ sizeof(params) };
+ PORT_Assert(cx && ikm && label && labelLen && out && suiteId);
+
+ labeledIkm = pk11_hpke_MakeExtractLabel(V1_LABEL, strlen(V1_LABEL), label, labelLen, suiteId, ikm);
+ CHECK_FAIL(!labeledIkm);
+ params.bExtract = CK_TRUE;
+ params.bExpand = CK_FALSE;
+ params.prfHashMechanism = cx->kdfParams->mech;
+ params.ulSaltType = salt ? CKF_HKDF_SALT_DATA : CKF_HKDF_SALT_NULL;
+ params.pSalt = salt ? (CK_BYTE_PTR)salt->data : NULL;
+ params.ulSaltLen = salt ? salt->len : 0;
+ params.pInfo = labeledIkm->data;
+ params.ulInfoLen = labeledIkm->len;
+
+ slot = PK11_GetBestSlot(CKM_EC_KEY_PAIR_GEN, NULL);
+ CHECK_FAIL(!slot);
+
+ importedIkm = PK11_ImportDataKey(slot, CKM_HKDF_DATA, PK11_OriginUnwrap,
+ CKA_DERIVE, labeledIkm, NULL);
+ CHECK_FAIL(!importedIkm);
+ prk = PK11_Derive(importedIkm, CKM_HKDF_DATA, &paramsItem,
+ CKM_HKDF_DERIVE, CKA_DERIVE, 0);
+ CHECK_FAIL(!prk);
+ rv = PK11_ExtractKeyValue(prk);
+ CHECK_RV(rv);
+ borrowed = PK11_GetKeyData(prk);
+ CHECK_FAIL(!borrowed);
+ outDerived = SECITEM_DupItem(borrowed);
+ CHECK_FAIL(!outDerived);
+
+ *out = outDerived;
+
+CLEANUP:
+ PK11_FreeSymKey(importedIkm);
+ PK11_FreeSymKey(prk);
+ SECITEM_FreeItem(labeledIkm, PR_TRUE);
+ if (slot) {
+ PK11_FreeSlot(slot);
+ }
+ return rv;
+}
+
+static SECStatus
+pk11_hpke_LabeledExtract(const HpkeContext *cx, PK11SymKey *salt,
+ const SECItem *suiteId, const char *label, CK_MECHANISM_TYPE hashMech,
+ unsigned int labelLen, PK11SymKey *ikm, PK11SymKey **out)
+{
+ SECStatus rv = SECSuccess;
+ SECItem *innerLabel = NULL;
+ PK11SymKey *labeledIkm = NULL;
+ PK11SymKey *prk = NULL;
+ CK_HKDF_PARAMS params = { 0 };
+ CK_KEY_DERIVATION_STRING_DATA labelData;
+ SECItem labelDataItem = { siBuffer, NULL, 0 };
+ SECItem paramsItem = { siBuffer, (unsigned char *)&params,
+ sizeof(params) };
+ PORT_Assert(cx && ikm && label && labelLen && out && suiteId);
+
+ innerLabel = pk11_hpke_MakeExtractLabel(V1_LABEL, strlen(V1_LABEL), label, labelLen, suiteId, NULL);
+ CHECK_FAIL(!innerLabel);
+ labelData.pData = innerLabel->data;
+ labelData.ulLen = innerLabel->len;
+ labelDataItem.data = (PRUint8 *)&labelData;
+ labelDataItem.len = sizeof(labelData);
+ labeledIkm = PK11_Derive(ikm, CKM_CONCATENATE_DATA_AND_BASE,
+ &labelDataItem, CKM_GENERIC_SECRET_KEY_GEN, CKA_DERIVE, 0);
+ CHECK_FAIL(!labeledIkm);
+
+ params.bExtract = CK_TRUE;
+ params.bExpand = CK_FALSE;
+ params.prfHashMechanism = hashMech;
+ params.ulSaltType = salt ? CKF_HKDF_SALT_KEY : CKF_HKDF_SALT_NULL;
+ params.hSaltKey = salt ? PK11_GetSymKeyHandle(salt) : CK_INVALID_HANDLE;
+
+ prk = PK11_Derive(labeledIkm, CKM_HKDF_DERIVE, &paramsItem,
+ CKM_HKDF_DERIVE, CKA_DERIVE, 0);
+ CHECK_FAIL(!prk);
+ *out = prk;
+
+CLEANUP:
+ PK11_FreeSymKey(labeledIkm);
+ SECITEM_ZfreeItem(innerLabel, PR_TRUE);
+ return rv;
+}
+
+static SECStatus
+pk11_hpke_LabeledExpand(const HpkeContext *cx, PK11SymKey *prk, const SECItem *suiteId,
+ const char *label, unsigned int labelLen, const SECItem *info,
+ unsigned int L, CK_MECHANISM_TYPE hashMech, PK11SymKey **outKey,
+ SECItem **outItem)
+{
+ SECStatus rv = SECSuccess;
+ CK_MECHANISM_TYPE keyMech;
+ CK_MECHANISM_TYPE deriveMech;
+ CK_HKDF_PARAMS params = { 0 };
+ PK11SymKey *derivedKey = NULL;
+ SECItem *labeledInfoItem = NULL;
+ SECItem paramsItem = { siBuffer, (unsigned char *)&params,
+ sizeof(params) };
+ SECItem *derivedKeyData;
+ PRUint8 encodedL[2];
+ PRUint8 *walker = encodedL;
+ size_t len;
+ PORT_Assert(cx && prk && label && (!!outKey != !!outItem));
+
+ walker = encodeNumber(L, walker, 2);
+ len = info ? info->len : 0;
+ len += sizeof(encodedL) + strlen(V1_LABEL) + suiteId->len + labelLen;
+ labeledInfoItem = SECITEM_AllocItem(NULL, NULL, len);
+ CHECK_FAIL(!labeledInfoItem);
+
+ walker = labeledInfoItem->data;
+ PORT_Memcpy(walker, encodedL, sizeof(encodedL));
+ walker += sizeof(encodedL);
+ PORT_Memcpy(walker, V1_LABEL, strlen(V1_LABEL));
+ walker += strlen(V1_LABEL);
+ PORT_Memcpy(walker, suiteId->data, suiteId->len);
+ walker += suiteId->len;
+ PORT_Memcpy(walker, label, labelLen);
+ walker += labelLen;
+ if (info) {
+ PORT_Memcpy(walker, info->data, info->len);
+ }
+
+ params.bExtract = CK_FALSE;
+ params.bExpand = CK_TRUE;
+ params.prfHashMechanism = hashMech;
+ params.ulSaltType = CKF_HKDF_SALT_NULL;
+ params.pInfo = labeledInfoItem->data;
+ params.ulInfoLen = labeledInfoItem->len;
+ deriveMech = outItem ? CKM_HKDF_DATA : CKM_HKDF_DERIVE;
+ /* If we're expanding to the encryption key use the appropriate mechanism. */
+ keyMech = (label && !strcmp(KEY_LABEL, label)) ? cx->aeadParams->mech : CKM_HKDF_DERIVE;
+
+ derivedKey = PK11_Derive(prk, deriveMech, &paramsItem, keyMech, CKA_DERIVE, L);
+ CHECK_FAIL(!derivedKey);
+
+ if (outItem) {
+ /* Don't allow export of real keys. */
+ CHECK_FAIL_ERR(deriveMech != CKM_HKDF_DATA, SEC_ERROR_LIBRARY_FAILURE);
+ rv = PK11_ExtractKeyValue(derivedKey);
+ CHECK_RV(rv);
+ derivedKeyData = PK11_GetKeyData(derivedKey);
+ CHECK_FAIL_ERR((!derivedKeyData), SEC_ERROR_NO_KEY);
+ *outItem = SECITEM_DupItem(derivedKeyData);
+ CHECK_FAIL(!*outItem);
+ PK11_FreeSymKey(derivedKey);
+ } else {
+ *outKey = derivedKey;
+ }
+
+CLEANUP:
+ if (rv != SECSuccess) {
+ PK11_FreeSymKey(derivedKey);
+ }
+ SECITEM_ZfreeItem(labeledInfoItem, PR_TRUE);
+ return rv;
+}
+
+static SECStatus
+pk11_hpke_ExtractAndExpand(const HpkeContext *cx, PK11SymKey *ikm,
+ const SECItem *kemContext, PK11SymKey **out)
+{
+ SECStatus rv;
+ PK11SymKey *eaePrk = NULL;
+ PK11SymKey *sharedSecret = NULL;
+ PRUint8 suiteIdBuf[5];
+ PRUint8 *walker;
+ PORT_Memcpy(suiteIdBuf, KEM_LABEL, strlen(KEM_LABEL));
+ SECItem suiteIdItem = { siBuffer, suiteIdBuf, sizeof(suiteIdBuf) };
+ PORT_Assert(cx && ikm && kemContext && out);
+
+ walker = &suiteIdBuf[3];
+ walker = encodeNumber(cx->kemParams->id, walker, 2);
+
+ rv = pk11_hpke_LabeledExtract(cx, NULL, &suiteIdItem, EAE_PRK_LABEL,
+ cx->kemParams->hashMech, strlen(EAE_PRK_LABEL),
+ ikm, &eaePrk);
+ CHECK_RV(rv);
+
+ rv = pk11_hpke_LabeledExpand(cx, eaePrk, &suiteIdItem, SH_SEC_LABEL, strlen(SH_SEC_LABEL),
+ kemContext, cx->kemParams->Nsecret, cx->kemParams->hashMech,
+ &sharedSecret, NULL);
+ CHECK_RV(rv);
+ *out = sharedSecret;
+
+CLEANUP:
+ if (rv != SECSuccess) {
+ PK11_FreeSymKey(sharedSecret);
+ }
+ PK11_FreeSymKey(eaePrk);
+ return rv;
+}
+
+static SECStatus
+pk11_hpke_Encap(HpkeContext *cx, const SECKEYPublicKey *pkE, SECKEYPrivateKey *skE,
+ SECKEYPublicKey *pkR)
+{
+ SECStatus rv;
+ PK11SymKey *dh = NULL;
+ SECItem *kemContext = NULL;
+ SECItem *encPkR = NULL;
+ unsigned int tmpLen;
+
+ PORT_Assert(cx && skE && pkE && pkR);
+
+ rv = pk11_hpke_CheckKeys(cx, pkE, skE);
+ CHECK_RV(rv);
+ rv = pk11_hpke_CheckKeys(cx, pkR, NULL);
+ CHECK_RV(rv);
+
+ dh = PK11_PubDeriveWithKDF(skE, pkR, PR_FALSE, NULL, NULL, CKM_ECDH1_DERIVE,
+ CKM_SHA512_HMAC /* unused */, CKA_DERIVE, 0,
+ CKD_NULL, NULL, NULL);
+ CHECK_FAIL(!dh);
+
+ /* Encapsulate our sender public key. Many use cases
+ * (including ECH) require that the application fetch
+ * this value, so do it once and store into the cx. */
+ rv = PK11_HPKE_Serialize(pkE, NULL, &tmpLen, 0);
+ CHECK_RV(rv);
+ cx->encapPubKey = SECITEM_AllocItem(NULL, NULL, tmpLen);
+ CHECK_FAIL(!cx->encapPubKey);
+ rv = PK11_HPKE_Serialize(pkE, cx->encapPubKey->data,
+ &cx->encapPubKey->len, cx->encapPubKey->len);
+ CHECK_RV(rv);
+
+ rv = PK11_HPKE_Serialize(pkR, NULL, &tmpLen, 0);
+ CHECK_RV(rv);
+
+ kemContext = SECITEM_AllocItem(NULL, NULL, cx->encapPubKey->len + tmpLen);
+ CHECK_FAIL(!kemContext);
+
+ PORT_Memcpy(kemContext->data, cx->encapPubKey->data, cx->encapPubKey->len);
+ rv = PK11_HPKE_Serialize(pkR, &kemContext->data[cx->encapPubKey->len], &tmpLen, tmpLen);
+ CHECK_RV(rv);
+
+ rv = pk11_hpke_ExtractAndExpand(cx, dh, kemContext, &cx->sharedSecret);
+ CHECK_RV(rv);
+
+CLEANUP:
+ if (rv != SECSuccess) {
+ PK11_FreeSymKey(cx->sharedSecret);
+ cx->sharedSecret = NULL;
+ }
+ SECITEM_FreeItem(encPkR, PR_TRUE);
+ SECITEM_FreeItem(kemContext, PR_TRUE);
+ PK11_FreeSymKey(dh);
+ return rv;
+}
+
+SECStatus
+PK11_HPKE_ExportSecret(const HpkeContext *cx, const SECItem *info, unsigned int L,
+ PK11SymKey **out)
+{
+ SECStatus rv;
+ PK11SymKey *exported;
+ PRUint8 suiteIdBuf[10];
+ PRUint8 *walker;
+ PORT_Memcpy(suiteIdBuf, HPKE_LABEL, strlen(HPKE_LABEL));
+ SECItem suiteIdItem = { siBuffer, suiteIdBuf, sizeof(suiteIdBuf) };
+
+ /* Arbitrary info length limit well under the specified max. */
+ if (!cx || !info || (!info->data && info->len) || info->len > 0xFFFF ||
+ !L || (L > 255 * cx->kdfParams->Nh)) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ walker = &suiteIdBuf[4];
+ walker = encodeNumber(cx->kemParams->id, walker, 2);
+ walker = encodeNumber(cx->kdfParams->id, walker, 2);
+ walker = encodeNumber(cx->aeadParams->id, walker, 2);
+
+ rv = pk11_hpke_LabeledExpand(cx, cx->exporterSecret, &suiteIdItem, SEC_LABEL,
+ strlen(SEC_LABEL), info, L, cx->kdfParams->mech,
+ &exported, NULL);
+ CHECK_RV(rv);
+ *out = exported;
+
+CLEANUP:
+ return rv;
+}
+
+static SECStatus
+pk11_hpke_Decap(HpkeContext *cx, const SECKEYPublicKey *pkR, SECKEYPrivateKey *skR,
+ const SECItem *encS)
+{
+ SECStatus rv;
+ PK11SymKey *dh = NULL;
+ SECItem *encR = NULL;
+ SECItem *kemContext = NULL;
+ SECKEYPublicKey *pkS = NULL;
+ unsigned int tmpLen;
+
+ if (!cx || !skR || !pkR || !encS || !encS->data || !encS->len) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ rv = PK11_HPKE_Deserialize(cx, encS->data, encS->len, &pkS);
+ CHECK_RV(rv);
+
+ rv = pk11_hpke_CheckKeys(cx, pkR, skR);
+ CHECK_RV(rv);
+ rv = pk11_hpke_CheckKeys(cx, pkS, NULL);
+ CHECK_RV(rv);
+
+ dh = PK11_PubDeriveWithKDF(skR, pkS, PR_FALSE, NULL, NULL, CKM_ECDH1_DERIVE,
+ CKM_SHA512_HMAC /* unused */, CKA_DERIVE, 0,
+ CKD_NULL, NULL, NULL);
+ CHECK_FAIL(!dh);
+
+ /* kem_context = concat(enc, pkRm) */
+ rv = PK11_HPKE_Serialize(pkR, NULL, &tmpLen, 0);
+ CHECK_RV(rv);
+
+ kemContext = SECITEM_AllocItem(NULL, NULL, encS->len + tmpLen);
+ CHECK_FAIL(!kemContext);
+
+ PORT_Memcpy(kemContext->data, encS->data, encS->len);
+ rv = PK11_HPKE_Serialize(pkR, &kemContext->data[encS->len], &tmpLen,
+ kemContext->len - encS->len);
+ CHECK_RV(rv);
+ rv = pk11_hpke_ExtractAndExpand(cx, dh, kemContext, &cx->sharedSecret);
+ CHECK_RV(rv);
+
+ /* Store the sender serialized public key, which
+ * may be required by application use cases. */
+ cx->encapPubKey = SECITEM_DupItem(encS);
+ CHECK_FAIL(!cx->encapPubKey);
+
+CLEANUP:
+ if (rv != SECSuccess) {
+ PK11_FreeSymKey(cx->sharedSecret);
+ cx->sharedSecret = NULL;
+ }
+ PK11_FreeSymKey(dh);
+ SECKEY_DestroyPublicKey(pkS);
+ SECITEM_FreeItem(encR, PR_TRUE);
+ SECITEM_ZfreeItem(kemContext, PR_TRUE);
+ return rv;
+}
+
+const SECItem *
+PK11_HPKE_GetEncapPubKey(const HpkeContext *cx)
+{
+ if (!cx) {
+ return NULL;
+ }
+ return cx->encapPubKey;
+}
+
+static SECStatus
+pk11_hpke_KeySchedule(HpkeContext *cx, const SECItem *info)
+{
+ SECStatus rv;
+ SECItem contextItem = { siBuffer, NULL, 0 };
+ unsigned int len;
+ unsigned int off;
+ PK11SymKey *secret = NULL;
+ SECItem *pskIdHash = NULL;
+ SECItem *infoHash = NULL;
+ PRUint8 suiteIdBuf[10];
+ PRUint8 *walker;
+ PORT_Memcpy(suiteIdBuf, HPKE_LABEL, strlen(HPKE_LABEL));
+ SECItem suiteIdItem = { siBuffer, suiteIdBuf, sizeof(suiteIdBuf) };
+ PORT_Assert(cx && info && cx->psk && cx->pskId);
+
+ walker = &suiteIdBuf[4];
+ walker = encodeNumber(cx->kemParams->id, walker, 2);
+ walker = encodeNumber(cx->kdfParams->id, walker, 2);
+ walker = encodeNumber(cx->aeadParams->id, walker, 2);
+
+ rv = pk11_hpke_LabeledExtractData(cx, NULL, &suiteIdItem, PSK_ID_LABEL,
+ strlen(PSK_ID_LABEL), cx->pskId, &pskIdHash);
+ CHECK_RV(rv);
+ rv = pk11_hpke_LabeledExtractData(cx, NULL, &suiteIdItem, INFO_LABEL,
+ strlen(INFO_LABEL), info, &infoHash);
+ CHECK_RV(rv);
+
+ // Make the context string
+ len = sizeof(cx->mode) + pskIdHash->len + infoHash->len;
+ CHECK_FAIL(!SECITEM_AllocItem(NULL, &contextItem, len));
+ off = 0;
+ PORT_Memcpy(&contextItem.data[off], &cx->mode, sizeof(cx->mode));
+ off += sizeof(cx->mode);
+ PORT_Memcpy(&contextItem.data[off], pskIdHash->data, pskIdHash->len);
+ off += pskIdHash->len;
+ PORT_Memcpy(&contextItem.data[off], infoHash->data, infoHash->len);
+ off += infoHash->len;
+
+ // Compute the keys
+ rv = pk11_hpke_LabeledExtract(cx, cx->sharedSecret, &suiteIdItem, SECRET_LABEL,
+ cx->kdfParams->mech, strlen(SECRET_LABEL),
+ cx->psk, &secret);
+ CHECK_RV(rv);
+ rv = pk11_hpke_LabeledExpand(cx, secret, &suiteIdItem, KEY_LABEL, strlen(KEY_LABEL),
+ &contextItem, cx->aeadParams->Nk, cx->kdfParams->mech,
+ &cx->key, NULL);
+ CHECK_RV(rv);
+ rv = pk11_hpke_LabeledExpand(cx, secret, &suiteIdItem, NONCE_LABEL, strlen(NONCE_LABEL),
+ &contextItem, cx->aeadParams->Nn, cx->kdfParams->mech,
+ NULL, &cx->baseNonce);
+ CHECK_RV(rv);
+ rv = pk11_hpke_LabeledExpand(cx, secret, &suiteIdItem, EXP_LABEL, strlen(EXP_LABEL),
+ &contextItem, cx->kdfParams->Nh, cx->kdfParams->mech,
+ &cx->exporterSecret, NULL);
+ CHECK_RV(rv);
+
+CLEANUP:
+ /* If !SECSuccess, callers will tear down the context. */
+ PK11_FreeSymKey(secret);
+ SECITEM_FreeItem(&contextItem, PR_FALSE);
+ SECITEM_FreeItem(infoHash, PR_TRUE);
+ SECITEM_FreeItem(pskIdHash, PR_TRUE);
+ return rv;
+}
+
+SECStatus
+PK11_HPKE_SetupR(HpkeContext *cx, const SECKEYPublicKey *pkR, SECKEYPrivateKey *skR,
+ const SECItem *enc, const SECItem *info)
+{
+ SECStatus rv;
+ SECItem empty = { siBuffer, NULL, 0 };
+
+ CHECK_FAIL_ERR((!cx || !skR || !info || !enc || !enc->data || !enc->len),
+ SEC_ERROR_INVALID_ARGS);
+ /* Already setup */
+ CHECK_FAIL_ERR((cx->aeadContext), SEC_ERROR_INVALID_STATE);
+
+ rv = pk11_hpke_Decap(cx, pkR, skR, enc);
+ CHECK_RV(rv);
+ rv = pk11_hpke_KeySchedule(cx, info);
+ CHECK_RV(rv);
+
+ /* Store the key context for subsequent calls to Open().
+ * PK11_CreateContextBySymKey refs the key internally. */
+ PORT_Assert(cx->key);
+ cx->aeadContext = PK11_CreateContextBySymKey(cx->aeadParams->mech,
+ CKA_NSS_MESSAGE | CKA_DECRYPT,
+ cx->key, &empty);
+ CHECK_FAIL_ERR((!cx->aeadContext), SEC_ERROR_LIBRARY_FAILURE);
+
+CLEANUP:
+ if (rv != SECSuccess) {
+ /* Clear everything past NewContext. */
+ PK11_HPKE_DestroyContext(cx, PR_FALSE);
+ }
+ return rv;
+}
+
+SECStatus
+PK11_HPKE_SetupS(HpkeContext *cx, const SECKEYPublicKey *pkE, SECKEYPrivateKey *skE,
+ SECKEYPublicKey *pkR, const SECItem *info)
+{
+ SECStatus rv;
+ SECItem empty = { siBuffer, NULL, 0 };
+ SECKEYPublicKey *tmpPkE = NULL;
+ SECKEYPrivateKey *tmpSkE = NULL;
+ CHECK_FAIL_ERR((!cx || !pkR || !info || (!!skE != !!pkE)), SEC_ERROR_INVALID_ARGS);
+ /* Already setup */
+ CHECK_FAIL_ERR((cx->aeadContext), SEC_ERROR_INVALID_STATE);
+
+ /* If NULL was passed for the local keypair, generate one. */
+ if (skE == NULL) {
+ rv = pk11_hpke_GenerateKeyPair(cx, &tmpPkE, &tmpSkE);
+ if (rv != SECSuccess) {
+ /* Code set */
+ return SECFailure;
+ }
+ rv = pk11_hpke_Encap(cx, tmpPkE, tmpSkE, pkR);
+ } else {
+ rv = pk11_hpke_Encap(cx, pkE, skE, pkR);
+ }
+ CHECK_RV(rv);
+
+ SECItem defaultInfo = { siBuffer, NULL, 0 };
+ if (!info || !info->data) {
+ info = &defaultInfo;
+ }
+ rv = pk11_hpke_KeySchedule(cx, info);
+ CHECK_RV(rv);
+
+ PORT_Assert(cx->key);
+ cx->aeadContext = PK11_CreateContextBySymKey(cx->aeadParams->mech,
+ CKA_NSS_MESSAGE | CKA_ENCRYPT,
+ cx->key, &empty);
+ CHECK_FAIL_ERR((!cx->aeadContext), SEC_ERROR_LIBRARY_FAILURE);
+
+CLEANUP:
+ if (rv != SECSuccess) {
+ /* Clear everything past NewContext. */
+ PK11_HPKE_DestroyContext(cx, PR_FALSE);
+ }
+ SECKEY_DestroyPrivateKey(tmpSkE);
+ SECKEY_DestroyPublicKey(tmpPkE);
+ return rv;
+}
+
+SECStatus
+PK11_HPKE_Seal(HpkeContext *cx, const SECItem *aad, const SECItem *pt,
+ SECItem **out)
+{
+ SECStatus rv;
+ PRUint8 ivOut[12] = { 0 };
+ SECItem *ct = NULL;
+ size_t maxOut;
+ unsigned char tagBuf[HASH_LENGTH_MAX];
+ size_t tagLen;
+ unsigned int fixedBits;
+
+ /* aad may be NULL, PT may be zero-length but not NULL. */
+ if (!cx || !cx->aeadContext ||
+ (aad && aad->len && !aad->data) ||
+ !pt || (pt->len && !pt->data) ||
+ !out) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ PORT_Assert(cx->baseNonce->len == sizeof(ivOut));
+ PORT_Memcpy(ivOut, cx->baseNonce->data, cx->baseNonce->len);
+
+ tagLen = cx->aeadParams->tagLen;
+ maxOut = pt->len + tagLen;
+ fixedBits = (cx->baseNonce->len - 8) * 8;
+ ct = SECITEM_AllocItem(NULL, NULL, maxOut);
+ CHECK_FAIL(!ct);
+
+ rv = PK11_AEADOp(cx->aeadContext,
+ CKG_GENERATE_COUNTER_XOR, fixedBits,
+ ivOut, sizeof(ivOut),
+ aad ? aad->data : NULL,
+ aad ? aad->len : 0,
+ ct->data, (int *)&ct->len, maxOut,
+ tagBuf, tagLen,
+ pt->data, pt->len);
+ CHECK_RV(rv);
+ CHECK_FAIL_ERR((ct->len > maxOut - tagLen), SEC_ERROR_LIBRARY_FAILURE);
+
+ /* Append the tag to the ciphertext. */
+ PORT_Memcpy(&ct->data[ct->len], tagBuf, tagLen);
+ ct->len += tagLen;
+ *out = ct;
+
+CLEANUP:
+ if (rv != SECSuccess) {
+ SECITEM_ZfreeItem(ct, PR_TRUE);
+ }
+ return rv;
+}
+
+/* PKCS #11 defines the IV generator function to be ignored on
+ * decrypt (i.e. it uses the nonce input, as provided, as the IV).
+ * The sequence number is kept independently on each endpoint and
+ * the XORed IV is not transmitted, so we have to do our own IV
+ * construction XOR outside of the token. */
+static SECStatus
+pk11_hpke_makeIv(HpkeContext *cx, PRUint8 *iv, size_t ivLen)
+{
+ unsigned int counterLen = sizeof(cx->sequenceNumber);
+ PORT_Assert(cx->baseNonce->len == ivLen);
+ PORT_Assert(counterLen == 8);
+ if (cx->sequenceNumber == PR_UINT64(0xffffffffffffffff)) {
+ /* Overflow */
+ PORT_SetError(SEC_ERROR_INVALID_KEY);
+ return SECFailure;
+ }
+
+ PORT_Memcpy(iv, cx->baseNonce->data, cx->baseNonce->len);
+ for (size_t i = 0; i < counterLen; i++) {
+ iv[cx->baseNonce->len - 1 - i] ^=
+ PORT_GET_BYTE_BE(cx->sequenceNumber,
+ counterLen - 1 - i, counterLen);
+ }
+ return SECSuccess;
+}
+
+SECStatus
+PK11_HPKE_Open(HpkeContext *cx, const SECItem *aad,
+ const SECItem *ct, SECItem **out)
+{
+ SECStatus rv;
+ PRUint8 constructedNonce[12] = { 0 };
+ unsigned int tagLen;
+ SECItem *pt = NULL;
+
+ /* aad may be NULL, CT may be zero-length but not NULL. */
+ if ((!cx || !cx->aeadContext || !ct || !out) ||
+ (aad && aad->len && !aad->data) ||
+ (!ct->data || (ct->data && !ct->len))) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ tagLen = cx->aeadParams->tagLen;
+ CHECK_FAIL_ERR((ct->len < tagLen), SEC_ERROR_INVALID_ARGS);
+
+ pt = SECITEM_AllocItem(NULL, NULL, ct->len);
+ CHECK_FAIL(!pt);
+
+ rv = pk11_hpke_makeIv(cx, constructedNonce, sizeof(constructedNonce));
+ CHECK_RV(rv);
+
+ rv = PK11_AEADOp(cx->aeadContext, CKG_NO_GENERATE, 0,
+ constructedNonce, sizeof(constructedNonce),
+ aad ? aad->data : NULL,
+ aad ? aad->len : 0,
+ pt->data, (int *)&pt->len, pt->len,
+ &ct->data[ct->len - tagLen], tagLen,
+ ct->data, ct->len - tagLen);
+ CHECK_RV(rv);
+ cx->sequenceNumber++;
+ *out = pt;
+
+CLEANUP:
+ if (rv != SECSuccess) {
+ SECITEM_ZfreeItem(pt, PR_TRUE);
+ }
+ return rv;
+}
diff --git a/security/nss/lib/pk11wrap/pk11hpke.h b/security/nss/lib/pk11wrap/pk11hpke.h
new file mode 100644
index 0000000000..51132817f6
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11hpke.h
@@ -0,0 +1,82 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _PK11_HPKE_H_
+#define _PK11_HPKE_H_ 1
+
+#include "blapit.h"
+#include "seccomon.h"
+
+#define CLEANUP \
+ PORT_Assert(rv == SECSuccess); \
+ cleanup
+
+/* Error code must already be set. */
+#define CHECK_RV(rv) \
+ if ((rv) != SECSuccess) { \
+ goto cleanup; \
+ }
+
+/* Error code must already be set. */
+#define CHECK_FAIL(cond) \
+ if ((cond)) { \
+ rv = SECFailure; \
+ goto cleanup; \
+ }
+
+#define CHECK_FAIL_ERR(cond, err) \
+ if ((cond)) { \
+ PORT_SetError((err)); \
+ rv = SECFailure; \
+ goto cleanup; \
+ }
+
+typedef enum {
+ HpkeModeBase = 0,
+ HpkeModePsk = 1,
+} HpkeModeId;
+
+/* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hpke-08#section-7.1 */
+typedef enum {
+ HpkeDhKemX25519Sha256 = 0x20,
+} HpkeKemId;
+
+typedef enum {
+ HpkeKdfHkdfSha256 = 1,
+ HpkeKdfHkdfSha384 = 2,
+ HpkeKdfHkdfSha512 = 3,
+} HpkeKdfId;
+
+typedef enum {
+ HpkeAeadAes128Gcm = 1,
+ HpkeAeadAes256Gcm = 2,
+ HpkeAeadChaCha20Poly1305 = 3,
+} HpkeAeadId;
+
+typedef struct hpkeKemParamsStr {
+ HpkeKemId id;
+ unsigned int Nsk;
+ unsigned int Nsecret;
+ unsigned int Npk;
+ SECOidTag oidTag;
+ CK_MECHANISM_TYPE hashMech;
+} hpkeKemParams;
+
+typedef struct hpkeKdfParamsStr {
+ HpkeKdfId id;
+ unsigned int Nh;
+ CK_MECHANISM_TYPE mech;
+} hpkeKdfParams;
+
+typedef struct hpkeAeadParamsStr {
+ HpkeAeadId id;
+ unsigned int Nk;
+ unsigned int Nn;
+ unsigned int tagLen;
+ CK_MECHANISM_TYPE mech;
+} hpkeAeadParams;
+
+typedef struct HpkeContextStr HpkeContext;
+
+#endif /* _PK11_HPKE_H_ */
diff --git a/security/nss/lib/pk11wrap/pk11kea.c b/security/nss/lib/pk11wrap/pk11kea.c
new file mode 100644
index 0000000000..805e486518
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11kea.c
@@ -0,0 +1,141 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/*
+ * This file implements the Symkey wrapper and the PKCS context
+ * Interfaces.
+ */
+
+#include <stddef.h>
+
+#include "seccomon.h"
+#include "secmod.h"
+#include "nssilock.h"
+#include "secmodi.h"
+#include "secmodti.h"
+#include "pkcs11.h"
+#include "pk11func.h"
+#include "secitem.h"
+#include "keyhi.h"
+#include "secasn1.h"
+#include "sechash.h"
+#include "cert.h"
+#include "secerr.h"
+
+/*
+ * find an RSA public key on a card
+ */
+static CK_OBJECT_HANDLE
+pk11_FindRSAPubKey(PK11SlotInfo *slot)
+{
+ CK_KEY_TYPE key_type = CKK_RSA;
+ CK_OBJECT_CLASS class_type = CKO_PUBLIC_KEY;
+ CK_ATTRIBUTE theTemplate[2];
+ size_t template_count = sizeof(theTemplate) / sizeof(theTemplate[0]);
+ CK_ATTRIBUTE *attrs = theTemplate;
+
+ PK11_SETATTRS(attrs, CKA_CLASS, &class_type, sizeof(class_type));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_KEY_TYPE, &key_type, sizeof(key_type));
+ attrs++;
+ template_count = attrs - theTemplate;
+ PR_ASSERT(template_count <= sizeof(theTemplate) / sizeof(CK_ATTRIBUTE));
+
+ return pk11_FindObjectByTemplate(slot, theTemplate, template_count);
+}
+
+PK11SymKey *
+pk11_KeyExchange(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
+ CK_ATTRIBUTE_TYPE operation, CK_FLAGS flags,
+ PRBool isPerm, PK11SymKey *symKey)
+{
+ PK11SymKey *newSymKey = NULL;
+ SECStatus rv;
+ /* performance improvement can go here --- use a generated key at startup
+ * to generate a per token wrapping key. If it exists, use it, otherwise
+ * do a full key exchange. */
+
+ /* find a common Key Exchange algorithm */
+ /* RSA */
+ if (PK11_DoesMechanism(symKey->slot, CKM_RSA_PKCS) &&
+ PK11_DoesMechanism(slot, CKM_RSA_PKCS)) {
+ CK_OBJECT_HANDLE pubKeyHandle = CK_INVALID_HANDLE;
+ CK_OBJECT_HANDLE privKeyHandle = CK_INVALID_HANDLE;
+ SECKEYPublicKey *pubKey = NULL;
+ SECKEYPrivateKey *privKey = NULL;
+ SECItem wrapData;
+ unsigned int symKeyLength = PK11_GetKeyLength(symKey);
+
+ wrapData.data = NULL;
+
+ /* find RSA Public Key on target */
+ pubKeyHandle = pk11_FindRSAPubKey(slot);
+ if (pubKeyHandle != CK_INVALID_HANDLE) {
+ privKeyHandle = PK11_MatchItem(slot, pubKeyHandle, CKO_PRIVATE_KEY);
+ }
+
+ /* if no key exists, generate a key pair */
+ if (privKeyHandle == CK_INVALID_HANDLE) {
+ PK11RSAGenParams rsaParams;
+
+ if (symKeyLength > 120) /* bytes */ {
+ /* we'd have to generate an RSA key pair > 1024 bits long,
+ ** and that's too costly. Don't even try.
+ */
+ PORT_SetError(SEC_ERROR_CANNOT_MOVE_SENSITIVE_KEY);
+ goto rsa_failed;
+ }
+ rsaParams.keySizeInBits = 1024;
+ rsaParams.pe = 0x10001;
+ privKey = PK11_GenerateKeyPair(slot, CKM_RSA_PKCS_KEY_PAIR_GEN,
+ &rsaParams, &pubKey, PR_FALSE, PR_TRUE, symKey->cx);
+ } else {
+ /* if keys exist, build SECKEY data structures for them */
+ privKey = PK11_MakePrivKey(slot, nullKey, PR_TRUE, privKeyHandle,
+ symKey->cx);
+ if (privKey != NULL) {
+ pubKey = PK11_ExtractPublicKey(slot, rsaKey, pubKeyHandle);
+ if (pubKey && pubKey->pkcs11Slot) {
+ PK11_FreeSlot(pubKey->pkcs11Slot);
+ pubKey->pkcs11Slot = NULL;
+ pubKey->pkcs11ID = CK_INVALID_HANDLE;
+ }
+ }
+ }
+ if (privKey == NULL)
+ goto rsa_failed;
+ if (pubKey == NULL)
+ goto rsa_failed;
+
+ wrapData.len = SECKEY_PublicKeyStrength(pubKey);
+ if (!wrapData.len)
+ goto rsa_failed;
+ wrapData.data = PORT_Alloc(wrapData.len);
+ if (wrapData.data == NULL)
+ goto rsa_failed;
+
+ /* now wrap the keys in and out */
+ rv = PK11_PubWrapSymKey(CKM_RSA_PKCS, pubKey, symKey, &wrapData);
+ if (rv == SECSuccess) {
+ newSymKey = PK11_PubUnwrapSymKeyWithFlagsPerm(privKey,
+ &wrapData, type, operation,
+ symKeyLength, flags, isPerm);
+ /* make sure we wound up where we wanted to be! */
+ if (newSymKey && newSymKey->slot != slot) {
+ PK11_FreeSymKey(newSymKey);
+ newSymKey = NULL;
+ }
+ }
+ rsa_failed:
+ if (wrapData.data != NULL)
+ PORT_Free(wrapData.data);
+ if (privKey != NULL)
+ SECKEY_DestroyPrivateKey(privKey);
+ if (pubKey != NULL)
+ SECKEY_DestroyPublicKey(pubKey);
+
+ return newSymKey;
+ }
+ PORT_SetError(SEC_ERROR_NO_MODULE);
+ return NULL;
+}
diff --git a/security/nss/lib/pk11wrap/pk11list.c b/security/nss/lib/pk11wrap/pk11list.c
new file mode 100644
index 0000000000..beca1a71c1
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11list.c
@@ -0,0 +1,99 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/*
+ * Locking and queue management primatives
+ *
+ */
+
+#include "seccomon.h"
+#include "nssilock.h"
+#include "secmod.h"
+#include "secmodi.h"
+#include "secmodti.h"
+#include "nssrwlk.h"
+
+/*
+ * create a new lock for a Module List
+ */
+SECMODListLock *
+SECMOD_NewListLock()
+{
+ return NSSRWLock_New(10, "moduleListLock");
+}
+
+/*
+ * destroy the lock
+ */
+void
+SECMOD_DestroyListLock(SECMODListLock *lock)
+{
+ NSSRWLock_Destroy(lock);
+}
+
+/*
+ * Lock the list for reading.
+ * Note: this uses a non-reentrant lock. Writers are given preference.
+ */
+void
+SECMOD_GetReadLock(SECMODListLock *modLock)
+{
+ NSSRWLock_LockRead(modLock);
+}
+
+/*
+ * Release the Read lock
+ */
+void
+SECMOD_ReleaseReadLock(SECMODListLock *modLock)
+{
+ NSSRWLock_UnlockRead(modLock);
+}
+
+/*
+ * lock the list for Write
+ */
+void
+SECMOD_GetWriteLock(SECMODListLock *modLock)
+{
+ NSSRWLock_LockWrite(modLock);
+}
+
+/*
+ * Release the Write Lock: NOTE, this code is pretty inefficient if you have
+ * lots of write collisions.
+ */
+void
+SECMOD_ReleaseWriteLock(SECMODListLock *modLock)
+{
+ NSSRWLock_UnlockWrite(modLock);
+}
+
+/*
+ * must Hold the Write lock
+ */
+void
+SECMOD_RemoveList(SECMODModuleList **parent, SECMODModuleList *child)
+{
+ *parent = child->next;
+ child->next = NULL;
+}
+
+/*
+ * if lock is not specified, it must already be held
+ */
+void
+SECMOD_AddList(SECMODModuleList *parent, SECMODModuleList *child,
+ SECMODListLock *lock)
+{
+ if (lock) {
+ SECMOD_GetWriteLock(lock);
+ }
+
+ child->next = parent->next;
+ parent->next = child;
+
+ if (lock) {
+ SECMOD_ReleaseWriteLock(lock);
+ }
+}
diff --git a/security/nss/lib/pk11wrap/pk11load.c b/security/nss/lib/pk11wrap/pk11load.c
new file mode 100644
index 0000000000..119c8c5120
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11load.c
@@ -0,0 +1,715 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/*
+ * The following handles the loading, unloading and management of
+ * various PCKS #11 modules
+ */
+#define FORCE_PR_LOG 1
+#include "base.h"
+#include "seccomon.h"
+#include "pkcs11.h"
+#include "secmod.h"
+#include "prlink.h"
+#include "pk11func.h"
+#include "secmodi.h"
+#include "secmodti.h"
+#include "nssilock.h"
+#include "secerr.h"
+#include "prenv.h"
+#include "utilpars.h"
+#include "prio.h"
+#include "prprf.h"
+#include <stdio.h>
+#include "prsystem.h"
+
+#define DEBUG_MODULE 1
+
+#ifdef DEBUG_MODULE
+static char *modToDBG = NULL;
+
+#include "debug_module.c"
+#endif
+
+/* build the PKCS #11 2.01 lock files */
+CK_RV PR_CALLBACK
+secmodCreateMutext(CK_VOID_PTR_PTR pmutex)
+{
+ *pmutex = (CK_VOID_PTR)PZ_NewLock(nssILockOther);
+ if (*pmutex)
+ return CKR_OK;
+ return CKR_HOST_MEMORY;
+}
+
+CK_RV PR_CALLBACK
+secmodDestroyMutext(CK_VOID_PTR mutext)
+{
+ PZ_DestroyLock((PZLock *)mutext);
+ return CKR_OK;
+}
+
+CK_RV PR_CALLBACK
+secmodLockMutext(CK_VOID_PTR mutext)
+{
+ PZ_Lock((PZLock *)mutext);
+ return CKR_OK;
+}
+
+CK_RV PR_CALLBACK
+secmodUnlockMutext(CK_VOID_PTR mutext)
+{
+ PZ_Unlock((PZLock *)mutext);
+ return CKR_OK;
+}
+
+static SECMODModuleID nextModuleID = 1;
+static const CK_C_INITIALIZE_ARGS secmodLockFunctions = {
+ secmodCreateMutext, secmodDestroyMutext, secmodLockMutext,
+ secmodUnlockMutext, CKF_LIBRARY_CANT_CREATE_OS_THREADS | CKF_OS_LOCKING_OK,
+ NULL
+};
+static const CK_C_INITIALIZE_ARGS secmodNoLockArgs = {
+ NULL, NULL, NULL, NULL,
+ CKF_LIBRARY_CANT_CREATE_OS_THREADS, NULL
+};
+
+static PRBool loadSingleThreadedModules = PR_TRUE;
+static PRBool enforceAlreadyInitializedError = PR_TRUE;
+static PRBool finalizeModules = PR_TRUE;
+
+/* set global options for NSS PKCS#11 module loader */
+SECStatus
+pk11_setGlobalOptions(PRBool noSingleThreadedModules,
+ PRBool allowAlreadyInitializedModules,
+ PRBool dontFinalizeModules)
+{
+ if (noSingleThreadedModules) {
+ loadSingleThreadedModules = PR_FALSE;
+ } else {
+ loadSingleThreadedModules = PR_TRUE;
+ }
+ if (allowAlreadyInitializedModules) {
+ enforceAlreadyInitializedError = PR_FALSE;
+ } else {
+ enforceAlreadyInitializedError = PR_TRUE;
+ }
+ if (dontFinalizeModules) {
+ finalizeModules = PR_FALSE;
+ } else {
+ finalizeModules = PR_TRUE;
+ }
+ return SECSuccess;
+}
+
+PRBool
+pk11_getFinalizeModulesOption(void)
+{
+ return finalizeModules;
+}
+
+/*
+ * Allow specification loading the same module more than once at init time.
+ * This enables 2 things.
+ *
+ * 1) we can load additional databases by manipulating secmod.db/pkcs11.txt.
+ * 2) we can handle the case where some library has already initialized NSS
+ * before the main application.
+ *
+ * oldModule is the module we have already initialized.
+ * char *modulespec is the full module spec for the library we want to
+ * initialize.
+ */
+static SECStatus
+secmod_handleReload(SECMODModule *oldModule, SECMODModule *newModule)
+{
+ PK11SlotInfo *slot;
+ char *modulespec;
+ char *newModuleSpec;
+ char **children;
+ CK_SLOT_ID *ids;
+ SECMODConfigList *conflist = NULL;
+ SECStatus rv = SECFailure;
+ int count = 0;
+
+ /* first look for tokens= key words from the module spec */
+ modulespec = newModule->libraryParams;
+ newModuleSpec = secmod_ParseModuleSpecForTokens(PR_TRUE,
+ newModule->isFIPS, modulespec, &children, &ids);
+ if (!newModuleSpec) {
+ return SECFailure;
+ }
+
+ /*
+ * We are now trying to open a new slot on an already loaded module.
+ * If that slot represents a cert/key database, we don't want to open
+ * multiple copies of that same database. Unfortunately we understand
+ * the softoken flags well enough to be able to do this, so we can only get
+ * the list of already loaded databases if we are trying to open another
+ * internal module.
+ */
+ if (oldModule->internal) {
+ conflist = secmod_GetConfigList(oldModule->isFIPS,
+ oldModule->libraryParams, &count);
+ }
+
+ /* don't open multiple of the same db */
+ if (conflist && secmod_MatchConfigList(newModuleSpec, conflist, count)) {
+ rv = SECSuccess;
+ goto loser;
+ }
+ slot = SECMOD_OpenNewSlot(oldModule, newModuleSpec);
+ if (slot) {
+ int newID;
+ char **thisChild;
+ CK_SLOT_ID *thisID;
+ char *oldModuleSpec;
+
+ if (secmod_IsInternalKeySlot(newModule)) {
+ pk11_SetInternalKeySlotIfFirst(slot);
+ }
+ newID = slot->slotID;
+ PK11_FreeSlot(slot);
+ for (thisChild = children, thisID = ids; thisChild && *thisChild;
+ thisChild++, thisID++) {
+ if (conflist &&
+ secmod_MatchConfigList(*thisChild, conflist, count)) {
+ *thisID = (CK_SLOT_ID)-1;
+ continue;
+ }
+ slot = SECMOD_OpenNewSlot(oldModule, *thisChild);
+ if (slot) {
+ *thisID = slot->slotID;
+ PK11_FreeSlot(slot);
+ } else {
+ *thisID = (CK_SLOT_ID)-1;
+ }
+ }
+
+ /* update the old module initialization string in case we need to
+ * shutdown and reinit the whole mess (this is rare, but can happen
+ * when trying to stop smart card insertion/removal threads)... */
+ oldModuleSpec = secmod_MkAppendTokensList(oldModule->arena,
+ oldModule->libraryParams, newModuleSpec, newID,
+ children, ids);
+ if (oldModuleSpec) {
+ oldModule->libraryParams = oldModuleSpec;
+ }
+
+ rv = SECSuccess;
+ }
+
+loser:
+ secmod_FreeChildren(children, ids);
+ PORT_Free(newModuleSpec);
+ if (conflist) {
+ secmod_FreeConfigList(conflist, count);
+ }
+ return rv;
+}
+
+/*
+ * collect the steps we need to initialize a module in a single function
+ */
+SECStatus
+secmod_ModuleInit(SECMODModule *mod, SECMODModule **reload,
+ PRBool *alreadyLoaded)
+{
+ CK_C_INITIALIZE_ARGS moduleArgs;
+ CK_VOID_PTR pInitArgs;
+ CK_RV crv;
+
+ if (reload) {
+ *reload = NULL;
+ }
+
+ if (!mod || !alreadyLoaded) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ if (mod->libraryParams == NULL) {
+ if (mod->isThreadSafe) {
+ pInitArgs = (void *)&secmodLockFunctions;
+ } else {
+ pInitArgs = NULL;
+ }
+ } else {
+ if (mod->isThreadSafe) {
+ moduleArgs = secmodLockFunctions;
+ } else {
+ moduleArgs = secmodNoLockArgs;
+ }
+ moduleArgs.LibraryParameters = (void *)mod->libraryParams;
+ pInitArgs = &moduleArgs;
+ }
+ crv = PK11_GETTAB(mod)->C_Initialize(pInitArgs);
+ if (CKR_CRYPTOKI_ALREADY_INITIALIZED == crv) {
+ SECMODModule *oldModule = NULL;
+
+ /* Library has already been loaded once, if caller expects it, and it
+ * has additional configuration, try reloading it as well. */
+ if (reload != NULL && mod->libraryParams) {
+ oldModule = secmod_FindModuleByFuncPtr(mod->functionList);
+ }
+ /* Library has been loaded by NSS. It means it may be capable of
+ * reloading */
+ if (oldModule) {
+ SECStatus rv;
+ rv = secmod_handleReload(oldModule, mod);
+ if (rv == SECSuccess) {
+ /* This module should go away soon, since we've
+ * simply expanded the slots on the old module.
+ * When it goes away, it should not Finalize since
+ * that will close our old module as well. Setting
+ * the function list to NULL will prevent that close */
+ mod->functionList = NULL;
+ *reload = oldModule;
+ return SECSuccess;
+ }
+ SECMOD_DestroyModule(oldModule);
+ }
+ /* reload not possible, fall back to old semantics */
+ if (!enforceAlreadyInitializedError) {
+ *alreadyLoaded = PR_TRUE;
+ return SECSuccess;
+ }
+ }
+ if (crv != CKR_OK) {
+ if (!mod->isThreadSafe ||
+ crv == CKR_NSS_CERTDB_FAILED ||
+ crv == CKR_NSS_KEYDB_FAILED) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ /* If we had attempted to init a single threaded module "with"
+ * parameters and it failed, should we retry "without" parameters?
+ * (currently we don't retry in this scenario) */
+
+ if (!loadSingleThreadedModules) {
+ PORT_SetError(SEC_ERROR_INCOMPATIBLE_PKCS11);
+ return SECFailure;
+ }
+ /* If we arrive here, the module failed a ThreadSafe init. */
+ mod->isThreadSafe = PR_FALSE;
+ if (!mod->libraryParams) {
+ pInitArgs = NULL;
+ } else {
+ moduleArgs = secmodNoLockArgs;
+ moduleArgs.LibraryParameters = (void *)mod->libraryParams;
+ pInitArgs = &moduleArgs;
+ }
+ crv = PK11_GETTAB(mod)->C_Initialize(pInitArgs);
+ if ((CKR_CRYPTOKI_ALREADY_INITIALIZED == crv) &&
+ (!enforceAlreadyInitializedError)) {
+ *alreadyLoaded = PR_TRUE;
+ return SECSuccess;
+ }
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ }
+ return SECSuccess;
+}
+
+/*
+ * set the hasRootCerts flags in the module so it can be stored back
+ * into the database.
+ */
+void
+SECMOD_SetRootCerts(PK11SlotInfo *slot, SECMODModule *mod)
+{
+ PK11PreSlotInfo *psi = NULL;
+ int i;
+
+ if (slot->hasRootCerts) {
+ for (i = 0; i < mod->slotInfoCount; i++) {
+ if (slot->slotID == mod->slotInfo[i].slotID) {
+ psi = &mod->slotInfo[i];
+ break;
+ }
+ }
+ if (psi == NULL) {
+ /* allocate more slots */
+ PK11PreSlotInfo *psi_list = (PK11PreSlotInfo *)
+ PORT_ArenaAlloc(mod->arena,
+ (mod->slotInfoCount + 1) * sizeof(PK11PreSlotInfo));
+ /* copy the old ones */
+ if (mod->slotInfoCount > 0) {
+ PORT_Memcpy(psi_list, mod->slotInfo,
+ (mod->slotInfoCount) * sizeof(PK11PreSlotInfo));
+ }
+ /* assign psi to the last new slot */
+ psi = &psi_list[mod->slotInfoCount];
+ psi->slotID = slot->slotID;
+ psi->askpw = 0;
+ psi->timeout = 0;
+ psi->defaultFlags = 0;
+
+ /* increment module count & store new list */
+ mod->slotInfo = psi_list;
+ mod->slotInfoCount++;
+ }
+ psi->hasRootCerts = 1;
+ }
+}
+
+#ifndef NSS_STATIC_SOFTOKEN
+static const char *my_shlib_name =
+ SHLIB_PREFIX "nss" NSS_SHLIB_VERSION "." SHLIB_SUFFIX;
+static const char *softoken_shlib_name =
+ SHLIB_PREFIX "softokn" SOFTOKEN_SHLIB_VERSION "." SHLIB_SUFFIX;
+static const PRCallOnceType pristineCallOnce;
+static PRCallOnceType loadSoftokenOnce;
+static PRLibrary *softokenLib;
+static PRInt32 softokenLoadCount;
+
+/* This function must be run only once. */
+/* determine if hybrid platform, then actually load the DSO. */
+static PRStatus
+softoken_LoadDSO(void)
+{
+ PRLibrary *handle;
+
+ handle = PORT_LoadLibraryFromOrigin(my_shlib_name,
+ (PRFuncPtr)&softoken_LoadDSO,
+ softoken_shlib_name);
+ if (handle) {
+ softokenLib = handle;
+ return PR_SUCCESS;
+ }
+ return PR_FAILURE;
+}
+#else
+CK_RV NSC_GetInterface(CK_UTF8CHAR_PTR pInterfaceName,
+ CK_VERSION_PTR pVersion,
+ CK_INTERFACE_PTR_PTR *ppInterface, CK_FLAGS flags);
+char **NSC_ModuleDBFunc(unsigned long function, char *parameters, void *args);
+#endif
+
+/*
+ * load a new module into our address space and initialize it.
+ */
+SECStatus
+secmod_LoadPKCS11Module(SECMODModule *mod, SECMODModule **oldModule)
+{
+ PRLibrary *library = NULL;
+ CK_C_GetInterface ientry = NULL;
+ CK_C_GetFunctionList fentry = NULL;
+ CK_INFO info;
+ CK_ULONG slotCount = 0;
+ SECStatus rv;
+ PRBool alreadyLoaded = PR_FALSE;
+ char *disableUnload = NULL;
+#ifndef NSS_STATIC_SOFTOKEN
+ const char *nss_interface;
+ const char *nss_function;
+#endif
+ CK_INTERFACE_PTR interface;
+
+ if (mod->loaded)
+ return SECSuccess;
+
+ mod->fipsIndicator = NULL;
+
+ /* internal modules get loaded from their internal list */
+ if (mod->internal && (mod->dllName == NULL)) {
+#ifdef NSS_STATIC_SOFTOKEN
+ ientry = (CK_C_GetInterface)NSC_GetInterface;
+#else
+ /*
+ * Loads softoken as a dynamic library,
+ * even though the rest of NSS assumes this as the "internal" module.
+ */
+ if (!softokenLib &&
+ PR_SUCCESS != PR_CallOnce(&loadSoftokenOnce, &softoken_LoadDSO))
+ return SECFailure;
+
+ PR_ATOMIC_INCREMENT(&softokenLoadCount);
+
+ if (mod->isFIPS) {
+ nss_interface = "FC_GetInterface";
+ nss_function = "FC_GetFunctionList";
+ } else {
+ nss_interface = "NSC_GetInterface";
+ nss_function = "NSC_GetFunctionList";
+ }
+
+ ientry = (CK_C_GetInterface)
+ PR_FindSymbol(softokenLib, nss_interface);
+ if (!ientry) {
+ fentry = (CK_C_GetFunctionList)
+ PR_FindSymbol(softokenLib, nss_function);
+ if (!fentry) {
+ return SECFailure;
+ }
+ }
+#endif
+
+ if (mod->isModuleDB) {
+ mod->moduleDBFunc = (CK_C_GetFunctionList)
+#ifdef NSS_STATIC_SOFTOKEN
+ NSC_ModuleDBFunc;
+#else
+ PR_FindSymbol(softokenLib, "NSC_ModuleDBFunc");
+#endif
+ }
+
+ if (mod->moduleDBOnly) {
+ mod->loaded = PR_TRUE;
+ return SECSuccess;
+ }
+ } else {
+ /* Not internal, load the DLL and look up C_GetFunctionList */
+ if (mod->dllName == NULL) {
+ return SECFailure;
+ }
+
+/* load the library. If this succeeds, then we have to remember to
+ * unload the library if anything goes wrong from here on out...
+ */
+#if defined(_WIN32)
+ if (nssUTF8_Length(mod->dllName, NULL)) {
+ wchar_t *dllNameWide = _NSSUTIL_UTF8ToWide(mod->dllName);
+ if (dllNameWide) {
+ PRLibSpec libSpec;
+ libSpec.type = PR_LibSpec_PathnameU;
+ libSpec.value.pathname_u = dllNameWide;
+ library = PR_LoadLibraryWithFlags(libSpec, 0);
+ PORT_Free(dllNameWide);
+ }
+ }
+ if (library == NULL) {
+ // fallback to system code page
+ library = PR_LoadLibrary(mod->dllName);
+ }
+#else
+ library = PR_LoadLibrary(mod->dllName);
+#endif // defined(_WIN32)
+ mod->library = (void *)library;
+
+ if (library == NULL) {
+ return SECFailure;
+ }
+
+ /*
+ * now we need to get the entry point to find the function pointers
+ */
+ if (!mod->moduleDBOnly) {
+ ientry = (CK_C_GetInterface)
+ PR_FindSymbol(library, "C_GetInterface");
+ if (!ientry) {
+ fentry = (CK_C_GetFunctionList)
+ PR_FindSymbol(library, "C_GetFunctionList");
+ }
+ }
+ if (mod->isModuleDB) {
+ mod->moduleDBFunc = (void *)
+ PR_FindSymbol(library, "NSS_ReturnModuleSpecData");
+ }
+ if (mod->moduleDBFunc == NULL)
+ mod->isModuleDB = PR_FALSE;
+ if ((ientry == NULL) && (fentry == NULL)) {
+ if (mod->isModuleDB) {
+ mod->loaded = PR_TRUE;
+ mod->moduleDBOnly = PR_TRUE;
+ return SECSuccess;
+ }
+ PR_UnloadLibrary(library);
+ return SECFailure;
+ }
+ }
+
+ /*
+ * We need to get the function list
+ */
+ if (ientry) {
+ /* we first try to get a FORK_SAFE interface */
+ if ((*ientry)((CK_UTF8CHAR_PTR) "PKCS 11", NULL, &interface,
+ CKF_INTERFACE_FORK_SAFE) != CKR_OK) {
+ /* one is not appearantly available, get a non-fork safe version */
+ if ((*ientry)((CK_UTF8CHAR_PTR) "PKCS 11", NULL, &interface, 0) != CKR_OK) {
+ goto fail;
+ }
+ }
+ mod->functionList = interface->pFunctionList;
+ mod->flags = interface->flags;
+ /* if we have a fips indicator, grab it */
+ if ((*ientry)((CK_UTF8CHAR_PTR) "Vendor NSS FIPS Interface", NULL,
+ &interface, 0) == CKR_OK) {
+ mod->fipsIndicator = ((CK_NSS_FIPS_FUNCTIONS *)(interface->pFunctionList))->NSC_NSSGetFIPSStatus;
+ }
+ } else {
+ if ((*fentry)((CK_FUNCTION_LIST_PTR *)&mod->functionList) != CKR_OK)
+ goto fail;
+ mod->flags = 0;
+ }
+
+#ifdef DEBUG_MODULE
+ modToDBG = PR_GetEnvSecure("NSS_DEBUG_PKCS11_MODULE");
+ if (modToDBG && strcmp(mod->commonName, modToDBG) == 0) {
+ mod->functionList = (void *)nss_InsertDeviceLog(
+ (CK_FUNCTION_LIST_3_0_PTR)mod->functionList);
+ }
+#endif
+
+ /* This test operation makes sure our locking system is
+ * consistent even if we are using non-thread safe tokens by
+ * simulating unsafe tokens with safe ones. */
+ mod->isThreadSafe = !PR_GetEnvSecure("NSS_FORCE_TOKEN_LOCK");
+
+ /* Now we initialize the module */
+ rv = secmod_ModuleInit(mod, oldModule, &alreadyLoaded);
+ if (rv != SECSuccess) {
+ goto fail;
+ }
+
+ /* module has been reloaded, this module itself is done,
+ * return to the caller */
+ if (mod->functionList == NULL) {
+ mod->loaded = PR_TRUE; /* technically the module is loaded.. */
+ return SECSuccess;
+ }
+
+ /* check the version number */
+ if (PK11_GETTAB(mod)->C_GetInfo(&info) != CKR_OK)
+ goto fail2;
+ if (info.cryptokiVersion.major < 2)
+ goto fail2;
+ /* all 2.0 are a priori *not* thread safe */
+ if ((info.cryptokiVersion.major == 2) && (info.cryptokiVersion.minor < 1)) {
+ if (!loadSingleThreadedModules) {
+ PORT_SetError(SEC_ERROR_INCOMPATIBLE_PKCS11);
+ goto fail2;
+ } else {
+ mod->isThreadSafe = PR_FALSE;
+ }
+ }
+ mod->cryptokiVersion = info.cryptokiVersion;
+
+ /* If we don't have a common name, get it from the PKCS 11 module */
+ if ((mod->commonName == NULL) || (mod->commonName[0] == 0)) {
+ mod->commonName = PK11_MakeString(mod->arena, NULL,
+ (char *)info.libraryDescription, sizeof(info.libraryDescription));
+ if (mod->commonName == NULL)
+ goto fail2;
+ }
+
+ /* initialize the Slots */
+ if (PK11_GETTAB(mod)->C_GetSlotList(CK_FALSE, NULL, &slotCount) == CKR_OK) {
+ CK_SLOT_ID *slotIDs;
+ int i;
+ CK_RV crv;
+
+ mod->slots = (PK11SlotInfo **)PORT_ArenaAlloc(mod->arena,
+ sizeof(PK11SlotInfo *) * slotCount);
+ if (mod->slots == NULL)
+ goto fail2;
+
+ slotIDs = (CK_SLOT_ID *)PORT_Alloc(sizeof(CK_SLOT_ID) * slotCount);
+ if (slotIDs == NULL) {
+ goto fail2;
+ }
+ crv = PK11_GETTAB(mod)->C_GetSlotList(CK_FALSE, slotIDs, &slotCount);
+ if (crv != CKR_OK) {
+ PORT_Free(slotIDs);
+ goto fail2;
+ }
+
+ /* Initialize each slot */
+ for (i = 0; i < (int)slotCount; i++) {
+ mod->slots[i] = PK11_NewSlotInfo(mod);
+ PK11_InitSlot(mod, slotIDs[i], mod->slots[i]);
+ /* look down the slot info table */
+ PK11_LoadSlotList(mod->slots[i], mod->slotInfo, mod->slotInfoCount);
+ SECMOD_SetRootCerts(mod->slots[i], mod);
+ /* explicitly mark the internal slot as such if IsInternalKeySlot()
+ * is set */
+ if (secmod_IsInternalKeySlot(mod) && (i == (mod->isFIPS ? 0 : 1))) {
+ pk11_SetInternalKeySlotIfFirst(mod->slots[i]);
+ }
+ }
+ mod->slotCount = slotCount;
+ mod->slotInfoCount = 0;
+ PORT_Free(slotIDs);
+ }
+
+ mod->loaded = PR_TRUE;
+ mod->moduleID = nextModuleID++;
+ return SECSuccess;
+fail2:
+ if (enforceAlreadyInitializedError || (!alreadyLoaded)) {
+ PK11_GETTAB(mod)->C_Finalize(NULL);
+ }
+fail:
+ mod->functionList = NULL;
+ disableUnload = PR_GetEnvSecure("NSS_DISABLE_UNLOAD");
+ if (library && !disableUnload) {
+ PR_UnloadLibrary(library);
+ }
+ return SECFailure;
+}
+
+SECStatus
+SECMOD_UnloadModule(SECMODModule *mod)
+{
+ PRLibrary *library;
+ char *disableUnload = NULL;
+
+ if (!mod->loaded) {
+ return SECFailure;
+ }
+ if (finalizeModules) {
+ if (mod->functionList && !mod->moduleDBOnly) {
+ PK11_GETTAB(mod)->C_Finalize(NULL);
+ }
+ }
+ mod->moduleID = 0;
+ mod->loaded = PR_FALSE;
+
+ /* do we want the semantics to allow unloading the internal library?
+ * if not, we should change this to SECFailure and move it above the
+ * mod->loaded = PR_FALSE; */
+ if (mod->internal && (mod->dllName == NULL)) {
+#ifndef NSS_STATIC_SOFTOKEN
+ if (0 == PR_ATOMIC_DECREMENT(&softokenLoadCount)) {
+ if (softokenLib) {
+ disableUnload = PR_GetEnvSecure("NSS_DISABLE_UNLOAD");
+ if (!disableUnload) {
+#ifdef DEBUG
+ PRStatus status = PR_UnloadLibrary(softokenLib);
+ PORT_Assert(PR_SUCCESS == status);
+#else
+ PR_UnloadLibrary(softokenLib);
+#endif
+ }
+ softokenLib = NULL;
+ }
+ loadSoftokenOnce = pristineCallOnce;
+ }
+#endif
+ return SECSuccess;
+ }
+
+ library = (PRLibrary *)mod->library;
+ /* paranoia */
+ if (library == NULL) {
+ return SECFailure;
+ }
+
+ disableUnload = PR_GetEnvSecure("NSS_DISABLE_UNLOAD");
+ if (!disableUnload) {
+ PR_UnloadLibrary(library);
+ }
+ return SECSuccess;
+}
+
+void
+nss_DumpModuleLog(void)
+{
+#ifdef DEBUG_MODULE
+ if (modToDBG) {
+ print_final_statistics();
+ }
+#endif
+}
diff --git a/security/nss/lib/pk11wrap/pk11mech.c b/security/nss/lib/pk11wrap/pk11mech.c
new file mode 100644
index 0000000000..2cf53e45d1
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11mech.c
@@ -0,0 +1,1952 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/*
+ * This file maps various PKCS #11 Mechanisms to related mechanisms, key
+ * types, and ASN.1 encodings.
+ */
+#include "seccomon.h"
+#include "secmod.h"
+#include "secmodi.h"
+#include "pkcs11t.h"
+#include "pk11func.h"
+#include "secitem.h"
+#include "secder.h"
+#include "secasn1.h"
+#include "secoid.h"
+#include "secerr.h"
+
+/*************************************************************
+ * local static and global data
+ *************************************************************/
+
+/*
+ * Tables used for Extended mechanism mapping (currently not used)
+ */
+typedef struct {
+ CK_MECHANISM_TYPE keyGen;
+ CK_KEY_TYPE keyType;
+ CK_MECHANISM_TYPE type;
+ CK_MECHANISM_TYPE padType;
+ int blockSize;
+ int iv;
+} pk11MechanismData;
+
+static pk11MechanismData pk11_default = { CKM_GENERIC_SECRET_KEY_GEN, CKK_GENERIC_SECRET,
+ CKM_FAKE_RANDOM, CKM_FAKE_RANDOM, 8, 8 };
+static pk11MechanismData *pk11_MechanismTable = NULL;
+static int pk11_MechTableSize = 0;
+static int pk11_MechEntrySize = 0;
+
+/*
+ * list of mechanisms we're willing to wrap secret keys with.
+ * This list is ordered by preference.
+ */
+CK_MECHANISM_TYPE wrapMechanismList[] = {
+ CKM_DES3_ECB,
+ CKM_CAST5_ECB,
+ CKM_AES_ECB,
+ CKM_CAMELLIA_ECB,
+ CKM_SEED_ECB,
+ CKM_CAST5_ECB,
+ CKM_DES_ECB,
+ CKM_KEY_WRAP_LYNKS,
+ CKM_IDEA_ECB,
+ CKM_CAST3_ECB,
+ CKM_CAST_ECB,
+ CKM_RC5_ECB,
+ CKM_RC2_ECB,
+ CKM_CDMF_ECB,
+ CKM_SKIPJACK_WRAP,
+};
+
+int wrapMechanismCount = sizeof(wrapMechanismList) / sizeof(wrapMechanismList[0]);
+
+/*********************************************************************
+ * Mechanism Mapping functions
+ *********************************************************************/
+
+/*
+ * lookup an entry in the mechanism table. If none found, return the
+ * default structure.
+ */
+static pk11MechanismData *
+pk11_lookup(CK_MECHANISM_TYPE type)
+{
+ int i;
+ for (i = 0; i < pk11_MechEntrySize; i++) {
+ if (pk11_MechanismTable[i].type == type) {
+ return (&pk11_MechanismTable[i]);
+ }
+ }
+ return &pk11_default;
+}
+
+/*
+ * find the best key wrap mechanism for this slot.
+ */
+CK_MECHANISM_TYPE
+PK11_GetBestWrapMechanism(PK11SlotInfo *slot)
+{
+ int i;
+ for (i = 0; i < wrapMechanismCount; i++) {
+ if (PK11_DoesMechanism(slot, wrapMechanismList[i])) {
+ return wrapMechanismList[i];
+ }
+ }
+ return CKM_INVALID_MECHANISM;
+}
+
+/*
+ * NOTE: This is not thread safe. Called at init time, and when loading
+ * a new Entry. It is reasonably safe as long as it is not re-entered
+ * (readers will always see a consistant table)
+ *
+ * This routine is called to add entries to the mechanism table, once there,
+ * they can not be removed.
+ */
+void
+PK11_AddMechanismEntry(CK_MECHANISM_TYPE type, CK_KEY_TYPE key,
+ CK_MECHANISM_TYPE keyGen,
+ CK_MECHANISM_TYPE padType,
+ int ivLen, int blockSize)
+{
+ int tableSize = pk11_MechTableSize;
+ int size = pk11_MechEntrySize;
+ int entry = size++;
+ pk11MechanismData *old = pk11_MechanismTable;
+ pk11MechanismData *newt = pk11_MechanismTable;
+
+ if (size > tableSize) {
+ int oldTableSize = tableSize;
+ tableSize += 10;
+ newt = PORT_NewArray(pk11MechanismData, tableSize);
+ if (newt == NULL)
+ return;
+
+ if (old)
+ PORT_Memcpy(newt, old, oldTableSize * sizeof(*newt));
+ } else
+ old = NULL;
+
+ newt[entry].type = type;
+ newt[entry].keyType = key;
+ newt[entry].keyGen = keyGen;
+ newt[entry].padType = padType;
+ newt[entry].iv = ivLen;
+ newt[entry].blockSize = blockSize;
+
+ pk11_MechanismTable = newt;
+ pk11_MechTableSize = tableSize;
+ pk11_MechEntrySize = size;
+ if (old)
+ PORT_Free(old);
+}
+
+/*
+ * Get the mechanism needed for the given key type
+ */
+CK_MECHANISM_TYPE
+PK11_GetKeyMechanism(CK_KEY_TYPE type)
+{
+ switch (type) {
+ case CKK_SEED:
+ return CKM_SEED_CBC;
+ case CKK_CAMELLIA:
+ return CKM_CAMELLIA_CBC;
+ case CKK_NSS_CHACHA20:
+ return CKM_NSS_CHACHA20_POLY1305;
+ case CKK_CHACHA20:
+ return CKM_CHACHA20_POLY1305;
+ case CKK_AES:
+ return CKM_AES_CBC;
+ case CKK_DES:
+ return CKM_DES_CBC;
+ case CKK_DES3:
+ return CKM_DES3_KEY_GEN;
+ case CKK_DES2:
+ return CKM_DES2_KEY_GEN;
+ case CKK_CDMF:
+ return CKM_CDMF_CBC;
+ case CKK_RC2:
+ return CKM_RC2_CBC;
+ case CKK_RC4:
+ return CKM_RC4;
+ case CKK_RC5:
+ return CKM_RC5_CBC;
+ case CKK_SKIPJACK:
+ return CKM_SKIPJACK_CBC64;
+ case CKK_BATON:
+ return CKM_BATON_CBC128;
+ case CKK_JUNIPER:
+ return CKM_JUNIPER_CBC128;
+ case CKK_IDEA:
+ return CKM_IDEA_CBC;
+ case CKK_CAST:
+ return CKM_CAST_CBC;
+ case CKK_CAST3:
+ return CKM_CAST3_CBC;
+ case CKK_CAST5:
+ return CKM_CAST5_CBC;
+ case CKK_RSA:
+ return CKM_RSA_PKCS;
+ case CKK_DSA:
+ return CKM_DSA;
+ case CKK_DH:
+ return CKM_DH_PKCS_DERIVE;
+ case CKK_KEA:
+ return CKM_KEA_KEY_DERIVE;
+ case CKK_EC: /* CKK_ECDSA is deprecated */
+ return CKM_ECDSA;
+ case CKK_HKDF:
+ return CKM_HKDF_DERIVE;
+ case CKK_GENERIC_SECRET:
+ default:
+ return CKM_SHA_1_HMAC;
+ }
+}
+
+/*
+ * Get the key type needed for the given mechanism
+ */
+CK_KEY_TYPE
+PK11_GetKeyType(CK_MECHANISM_TYPE type, unsigned long len)
+{
+ switch (type) {
+ case CKM_SEED_ECB:
+ case CKM_SEED_CBC:
+ case CKM_SEED_MAC:
+ case CKM_SEED_MAC_GENERAL:
+ case CKM_SEED_CBC_PAD:
+ case CKM_SEED_KEY_GEN:
+ return CKK_SEED;
+ case CKM_CAMELLIA_ECB:
+ case CKM_CAMELLIA_CBC:
+ case CKM_CAMELLIA_MAC:
+ case CKM_CAMELLIA_MAC_GENERAL:
+ case CKM_CAMELLIA_CBC_PAD:
+ case CKM_CAMELLIA_KEY_GEN:
+ return CKK_CAMELLIA;
+ case CKM_NSS_CHACHA20_POLY1305:
+ case CKM_NSS_CHACHA20_KEY_GEN:
+ case CKM_NSS_CHACHA20_CTR:
+ return CKK_NSS_CHACHA20;
+ case CKM_CHACHA20_POLY1305:
+ case CKM_CHACHA20_KEY_GEN:
+ case CKM_CHACHA20:
+ return CKK_CHACHA20;
+ case CKM_AES_ECB:
+ case CKM_AES_CBC:
+ case CKM_AES_CCM:
+ case CKM_AES_CTR:
+ case CKM_AES_CTS:
+ case CKM_AES_GCM:
+ case CKM_AES_MAC:
+ case CKM_AES_MAC_GENERAL:
+ case CKM_AES_CMAC:
+ case CKM_AES_CMAC_GENERAL:
+ case CKM_AES_CBC_PAD:
+ case CKM_AES_KEY_GEN:
+ case CKM_NSS_AES_KEY_WRAP:
+ case CKM_NSS_AES_KEY_WRAP_PAD:
+ case CKM_AES_KEY_WRAP:
+ case CKM_AES_KEY_WRAP_KWP:
+ case CKM_AES_XCBC_MAC:
+ case CKM_AES_XCBC_MAC_96:
+ return CKK_AES;
+ case CKM_DES_ECB:
+ case CKM_DES_CBC:
+ case CKM_DES_MAC:
+ case CKM_DES_MAC_GENERAL:
+ case CKM_DES_CBC_PAD:
+ case CKM_DES_KEY_GEN:
+ case CKM_KEY_WRAP_LYNKS:
+ case CKM_PBE_MD2_DES_CBC:
+ case CKM_PBE_MD5_DES_CBC:
+ return CKK_DES;
+ case CKM_DES3_ECB:
+ case CKM_DES3_CBC:
+ case CKM_DES3_MAC:
+ case CKM_DES3_MAC_GENERAL:
+ case CKM_DES3_CBC_PAD:
+ return (len == 16) ? CKK_DES2 : CKK_DES3;
+ case CKM_DES2_KEY_GEN:
+ case CKM_PBE_SHA1_DES2_EDE_CBC:
+ return CKK_DES2;
+ case CKM_PBE_SHA1_DES3_EDE_CBC:
+ case CKM_DES3_KEY_GEN:
+ return CKK_DES3;
+ case CKM_CDMF_ECB:
+ case CKM_CDMF_CBC:
+ case CKM_CDMF_MAC:
+ case CKM_CDMF_MAC_GENERAL:
+ case CKM_CDMF_CBC_PAD:
+ case CKM_CDMF_KEY_GEN:
+ return CKK_CDMF;
+ case CKM_RC2_ECB:
+ case CKM_RC2_CBC:
+ case CKM_RC2_MAC:
+ case CKM_RC2_MAC_GENERAL:
+ case CKM_RC2_CBC_PAD:
+ case CKM_RC2_KEY_GEN:
+ case CKM_PBE_SHA1_RC2_128_CBC:
+ case CKM_PBE_SHA1_RC2_40_CBC:
+ return CKK_RC2;
+ case CKM_RC4:
+ case CKM_RC4_KEY_GEN:
+ return CKK_RC4;
+ case CKM_RC5_ECB:
+ case CKM_RC5_CBC:
+ case CKM_RC5_MAC:
+ case CKM_RC5_MAC_GENERAL:
+ case CKM_RC5_CBC_PAD:
+ case CKM_RC5_KEY_GEN:
+ return CKK_RC5;
+ case CKM_SKIPJACK_CBC64:
+ case CKM_SKIPJACK_ECB64:
+ case CKM_SKIPJACK_OFB64:
+ case CKM_SKIPJACK_CFB64:
+ case CKM_SKIPJACK_CFB32:
+ case CKM_SKIPJACK_CFB16:
+ case CKM_SKIPJACK_CFB8:
+ case CKM_SKIPJACK_KEY_GEN:
+ case CKM_SKIPJACK_WRAP:
+ case CKM_SKIPJACK_PRIVATE_WRAP:
+ return CKK_SKIPJACK;
+ case CKM_BATON_ECB128:
+ case CKM_BATON_ECB96:
+ case CKM_BATON_CBC128:
+ case CKM_BATON_COUNTER:
+ case CKM_BATON_SHUFFLE:
+ case CKM_BATON_WRAP:
+ case CKM_BATON_KEY_GEN:
+ return CKK_BATON;
+ case CKM_JUNIPER_ECB128:
+ case CKM_JUNIPER_CBC128:
+ case CKM_JUNIPER_COUNTER:
+ case CKM_JUNIPER_SHUFFLE:
+ case CKM_JUNIPER_WRAP:
+ case CKM_JUNIPER_KEY_GEN:
+ return CKK_JUNIPER;
+ case CKM_IDEA_CBC:
+ case CKM_IDEA_ECB:
+ case CKM_IDEA_MAC:
+ case CKM_IDEA_MAC_GENERAL:
+ case CKM_IDEA_CBC_PAD:
+ case CKM_IDEA_KEY_GEN:
+ return CKK_IDEA;
+ case CKM_CAST_ECB:
+ case CKM_CAST_CBC:
+ case CKM_CAST_MAC:
+ case CKM_CAST_MAC_GENERAL:
+ case CKM_CAST_CBC_PAD:
+ case CKM_CAST_KEY_GEN:
+ case CKM_PBE_MD5_CAST_CBC:
+ return CKK_CAST;
+ case CKM_CAST3_ECB:
+ case CKM_CAST3_CBC:
+ case CKM_CAST3_MAC:
+ case CKM_CAST3_MAC_GENERAL:
+ case CKM_CAST3_CBC_PAD:
+ case CKM_CAST3_KEY_GEN:
+ case CKM_PBE_MD5_CAST3_CBC:
+ return CKK_CAST3;
+ case CKM_CAST5_ECB:
+ case CKM_CAST5_CBC:
+ case CKM_CAST5_MAC:
+ case CKM_CAST5_MAC_GENERAL:
+ case CKM_CAST5_CBC_PAD:
+ case CKM_CAST5_KEY_GEN:
+ case CKM_PBE_MD5_CAST5_CBC:
+ return CKK_CAST5;
+ case CKM_RSA_PKCS:
+ case CKM_RSA_9796:
+ case CKM_RSA_X_509:
+ case CKM_MD2_RSA_PKCS:
+ case CKM_MD5_RSA_PKCS:
+ case CKM_SHA1_RSA_PKCS:
+ case CKM_SHA224_RSA_PKCS:
+ case CKM_SHA256_RSA_PKCS:
+ case CKM_SHA384_RSA_PKCS:
+ case CKM_SHA512_RSA_PKCS:
+ case CKM_KEY_WRAP_SET_OAEP:
+ case CKM_RSA_PKCS_KEY_PAIR_GEN:
+ case CKM_RSA_X9_31_KEY_PAIR_GEN:
+ return CKK_RSA;
+ case CKM_DSA:
+ case CKM_DSA_SHA1:
+ case CKM_DSA_KEY_PAIR_GEN:
+ return CKK_DSA;
+ case CKM_DH_PKCS_DERIVE:
+ case CKM_DH_PKCS_KEY_PAIR_GEN:
+ return CKK_DH;
+ case CKM_KEA_KEY_DERIVE:
+ case CKM_KEA_KEY_PAIR_GEN:
+ return CKK_KEA;
+ case CKM_ECDSA:
+ case CKM_ECDSA_SHA1:
+ case CKM_EC_KEY_PAIR_GEN: /* aka CKM_ECDSA_KEY_PAIR_GEN */
+ case CKM_ECDH1_DERIVE:
+ return CKK_EC; /* CKK_ECDSA is deprecated */
+ case CKM_HKDF_KEY_GEN:
+ case CKM_HKDF_DERIVE:
+ case CKM_HKDF_DATA:
+ return CKK_HKDF;
+ case CKM_SSL3_PRE_MASTER_KEY_GEN:
+ case CKM_GENERIC_SECRET_KEY_GEN:
+ case CKM_SSL3_MASTER_KEY_DERIVE:
+ case CKM_SSL3_MASTER_KEY_DERIVE_DH:
+ case CKM_SSL3_KEY_AND_MAC_DERIVE:
+ case CKM_SSL3_SHA1_MAC:
+ case CKM_SSL3_MD5_MAC:
+ case CKM_TLS_MASTER_KEY_DERIVE:
+ case CKM_NSS_TLS_MASTER_KEY_DERIVE_SHA256:
+ case CKM_TLS_MASTER_KEY_DERIVE_DH:
+ case CKM_NSS_TLS_MASTER_KEY_DERIVE_DH_SHA256:
+ case CKM_TLS_KEY_AND_MAC_DERIVE:
+ case CKM_NSS_TLS_KEY_AND_MAC_DERIVE_SHA256:
+ case CKM_NSS_TLS_EXTENDED_MASTER_KEY_DERIVE:
+ case CKM_NSS_TLS_EXTENDED_MASTER_KEY_DERIVE_DH:
+ case CKM_SHA_1_HMAC:
+ case CKM_SHA_1_HMAC_GENERAL:
+ case CKM_SHA224_HMAC:
+ case CKM_SHA224_HMAC_GENERAL:
+ case CKM_SHA256_HMAC:
+ case CKM_SHA256_HMAC_GENERAL:
+ case CKM_SHA384_HMAC:
+ case CKM_SHA384_HMAC_GENERAL:
+ case CKM_SHA512_HMAC:
+ case CKM_SHA512_HMAC_GENERAL:
+ case CKM_MD2_HMAC:
+ case CKM_MD2_HMAC_GENERAL:
+ case CKM_MD5_HMAC:
+ case CKM_MD5_HMAC_GENERAL:
+ case CKM_TLS_PRF_GENERAL:
+ case CKM_NSS_TLS_PRF_GENERAL_SHA256:
+ return CKK_GENERIC_SECRET;
+ default:
+ return pk11_lookup(type)->keyType;
+ }
+}
+
+/*
+ * Get the Key Gen Mechanism needed for the given
+ * crypto mechanism
+ */
+CK_MECHANISM_TYPE
+PK11_GetKeyGen(CK_MECHANISM_TYPE type)
+{
+ return PK11_GetKeyGenWithSize(type, 0);
+}
+
+CK_MECHANISM_TYPE
+PK11_GetKeyGenWithSize(CK_MECHANISM_TYPE type, int size)
+{
+ switch (type) {
+ case CKM_SEED_ECB:
+ case CKM_SEED_CBC:
+ case CKM_SEED_MAC:
+ case CKM_SEED_MAC_GENERAL:
+ case CKM_SEED_CBC_PAD:
+ case CKM_SEED_KEY_GEN:
+ return CKM_SEED_KEY_GEN;
+ case CKM_CAMELLIA_ECB:
+ case CKM_CAMELLIA_CBC:
+ case CKM_CAMELLIA_MAC:
+ case CKM_CAMELLIA_MAC_GENERAL:
+ case CKM_CAMELLIA_CBC_PAD:
+ case CKM_CAMELLIA_KEY_GEN:
+ return CKM_CAMELLIA_KEY_GEN;
+ case CKM_NSS_CHACHA20_POLY1305:
+ case CKM_NSS_CHACHA20_CTR:
+ return CKM_NSS_CHACHA20_KEY_GEN;
+ case CKM_CHACHA20_POLY1305:
+ case CKM_CHACHA20:
+ return CKM_CHACHA20_KEY_GEN;
+ case CKM_AES_ECB:
+ case CKM_AES_CBC:
+ case CKM_AES_CCM:
+ case CKM_AES_CTR:
+ case CKM_AES_CTS:
+ case CKM_AES_GCM:
+ case CKM_AES_MAC:
+ case CKM_AES_MAC_GENERAL:
+ case CKM_AES_CMAC:
+ case CKM_AES_CMAC_GENERAL:
+ case CKM_AES_CBC_PAD:
+ case CKM_AES_KEY_GEN:
+ return CKM_AES_KEY_GEN;
+ case CKM_DES_ECB:
+ case CKM_DES_CBC:
+ case CKM_DES_MAC:
+ case CKM_DES_MAC_GENERAL:
+ case CKM_KEY_WRAP_LYNKS:
+ case CKM_DES_CBC_PAD:
+ case CKM_DES_KEY_GEN:
+ return CKM_DES_KEY_GEN;
+ case CKM_DES3_ECB:
+ case CKM_DES3_CBC:
+ case CKM_DES3_MAC:
+ case CKM_DES3_MAC_GENERAL:
+ case CKM_DES3_CBC_PAD:
+ return (size == 16) ? CKM_DES2_KEY_GEN : CKM_DES3_KEY_GEN;
+ case CKM_DES3_KEY_GEN:
+ return CKM_DES3_KEY_GEN;
+ case CKM_DES2_KEY_GEN:
+ return CKM_DES2_KEY_GEN;
+ case CKM_CDMF_ECB:
+ case CKM_CDMF_CBC:
+ case CKM_CDMF_MAC:
+ case CKM_CDMF_MAC_GENERAL:
+ case CKM_CDMF_CBC_PAD:
+ case CKM_CDMF_KEY_GEN:
+ return CKM_CDMF_KEY_GEN;
+ case CKM_RC2_ECB:
+ case CKM_RC2_CBC:
+ case CKM_RC2_MAC:
+ case CKM_RC2_MAC_GENERAL:
+ case CKM_RC2_CBC_PAD:
+ case CKM_RC2_KEY_GEN:
+ return CKM_RC2_KEY_GEN;
+ case CKM_RC4:
+ case CKM_RC4_KEY_GEN:
+ return CKM_RC4_KEY_GEN;
+ case CKM_RC5_ECB:
+ case CKM_RC5_CBC:
+ case CKM_RC5_MAC:
+ case CKM_RC5_MAC_GENERAL:
+ case CKM_RC5_CBC_PAD:
+ case CKM_RC5_KEY_GEN:
+ return CKM_RC5_KEY_GEN;
+ case CKM_SKIPJACK_CBC64:
+ case CKM_SKIPJACK_ECB64:
+ case CKM_SKIPJACK_OFB64:
+ case CKM_SKIPJACK_CFB64:
+ case CKM_SKIPJACK_CFB32:
+ case CKM_SKIPJACK_CFB16:
+ case CKM_SKIPJACK_CFB8:
+ case CKM_SKIPJACK_WRAP:
+ case CKM_SKIPJACK_KEY_GEN:
+ return CKM_SKIPJACK_KEY_GEN;
+ case CKM_BATON_ECB128:
+ case CKM_BATON_ECB96:
+ case CKM_BATON_CBC128:
+ case CKM_BATON_COUNTER:
+ case CKM_BATON_SHUFFLE:
+ case CKM_BATON_WRAP:
+ case CKM_BATON_KEY_GEN:
+ return CKM_BATON_KEY_GEN;
+ case CKM_JUNIPER_ECB128:
+ case CKM_JUNIPER_CBC128:
+ case CKM_JUNIPER_COUNTER:
+ case CKM_JUNIPER_SHUFFLE:
+ case CKM_JUNIPER_WRAP:
+ case CKM_JUNIPER_KEY_GEN:
+ return CKM_JUNIPER_KEY_GEN;
+ case CKM_IDEA_CBC:
+ case CKM_IDEA_ECB:
+ case CKM_IDEA_MAC:
+ case CKM_IDEA_MAC_GENERAL:
+ case CKM_IDEA_CBC_PAD:
+ case CKM_IDEA_KEY_GEN:
+ return CKM_IDEA_KEY_GEN;
+ case CKM_CAST_ECB:
+ case CKM_CAST_CBC:
+ case CKM_CAST_MAC:
+ case CKM_CAST_MAC_GENERAL:
+ case CKM_CAST_CBC_PAD:
+ case CKM_CAST_KEY_GEN:
+ return CKM_CAST_KEY_GEN;
+ case CKM_CAST3_ECB:
+ case CKM_CAST3_CBC:
+ case CKM_CAST3_MAC:
+ case CKM_CAST3_MAC_GENERAL:
+ case CKM_CAST3_CBC_PAD:
+ case CKM_CAST3_KEY_GEN:
+ return CKM_CAST3_KEY_GEN;
+ case CKM_CAST5_ECB:
+ case CKM_CAST5_CBC:
+ case CKM_CAST5_MAC:
+ case CKM_CAST5_MAC_GENERAL:
+ case CKM_CAST5_CBC_PAD:
+ case CKM_CAST5_KEY_GEN:
+ return CKM_CAST5_KEY_GEN;
+ case CKM_RSA_PKCS:
+ case CKM_RSA_9796:
+ case CKM_RSA_X_509:
+ case CKM_MD2_RSA_PKCS:
+ case CKM_MD5_RSA_PKCS:
+ case CKM_SHA1_RSA_PKCS:
+ case CKM_SHA224_RSA_PKCS:
+ case CKM_SHA256_RSA_PKCS:
+ case CKM_SHA384_RSA_PKCS:
+ case CKM_SHA512_RSA_PKCS:
+ case CKM_KEY_WRAP_SET_OAEP:
+ case CKM_RSA_PKCS_KEY_PAIR_GEN:
+ return CKM_RSA_PKCS_KEY_PAIR_GEN;
+ case CKM_RSA_X9_31_KEY_PAIR_GEN:
+ return CKM_RSA_X9_31_KEY_PAIR_GEN;
+ case CKM_DSA:
+ case CKM_DSA_SHA1:
+ case CKM_DSA_KEY_PAIR_GEN:
+ return CKM_DSA_KEY_PAIR_GEN;
+ case CKM_DH_PKCS_DERIVE:
+ case CKM_DH_PKCS_KEY_PAIR_GEN:
+ return CKM_DH_PKCS_KEY_PAIR_GEN;
+ case CKM_KEA_KEY_DERIVE:
+ case CKM_KEA_KEY_PAIR_GEN:
+ return CKM_KEA_KEY_PAIR_GEN;
+ case CKM_ECDSA:
+ case CKM_ECDSA_SHA1:
+ case CKM_EC_KEY_PAIR_GEN: /* aka CKM_ECDSA_KEY_PAIR_GEN */
+ case CKM_ECDH1_DERIVE:
+ return CKM_EC_KEY_PAIR_GEN;
+ case CKM_SSL3_PRE_MASTER_KEY_GEN:
+ case CKM_SSL3_MASTER_KEY_DERIVE:
+ case CKM_SSL3_KEY_AND_MAC_DERIVE:
+ case CKM_SSL3_SHA1_MAC:
+ case CKM_SSL3_MD5_MAC:
+ case CKM_TLS_MASTER_KEY_DERIVE:
+ case CKM_TLS_KEY_AND_MAC_DERIVE:
+ case CKM_NSS_TLS_KEY_AND_MAC_DERIVE_SHA256:
+ case CKM_NSS_TLS_EXTENDED_MASTER_KEY_DERIVE:
+ case CKM_NSS_TLS_EXTENDED_MASTER_KEY_DERIVE_DH:
+ return CKM_SSL3_PRE_MASTER_KEY_GEN;
+ case CKM_SHA_1_HMAC:
+ case CKM_SHA_1_HMAC_GENERAL:
+ case CKM_SHA224_HMAC:
+ case CKM_SHA224_HMAC_GENERAL:
+ case CKM_SHA256_HMAC:
+ case CKM_SHA256_HMAC_GENERAL:
+ case CKM_SHA384_HMAC:
+ case CKM_SHA384_HMAC_GENERAL:
+ case CKM_SHA512_HMAC:
+ case CKM_SHA512_HMAC_GENERAL:
+ case CKM_MD2_HMAC:
+ case CKM_MD2_HMAC_GENERAL:
+ case CKM_MD5_HMAC:
+ case CKM_MD5_HMAC_GENERAL:
+ case CKM_TLS_PRF_GENERAL:
+ case CKM_NSS_TLS_PRF_GENERAL_SHA256:
+ case CKM_GENERIC_SECRET_KEY_GEN:
+ return CKM_GENERIC_SECRET_KEY_GEN;
+ case CKM_PBE_MD2_DES_CBC:
+ case CKM_PBE_MD5_DES_CBC:
+ case CKM_PBA_SHA1_WITH_SHA1_HMAC:
+ case CKM_NSS_PBE_SHA1_HMAC_KEY_GEN:
+ case CKM_NSS_PBE_MD5_HMAC_KEY_GEN:
+ case CKM_NSS_PBE_MD2_HMAC_KEY_GEN:
+ 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:
+ case CKM_NSS_PBE_SHA1_DES_CBC:
+ case CKM_NSS_PBE_SHA1_40_BIT_RC2_CBC:
+ case CKM_NSS_PBE_SHA1_128_BIT_RC2_CBC:
+ case CKM_NSS_PBE_SHA1_40_BIT_RC4:
+ case CKM_NSS_PBE_SHA1_128_BIT_RC4:
+ case CKM_NSS_PBE_SHA1_TRIPLE_DES_CBC:
+ case CKM_NSS_PBE_SHA1_FAULTY_3DES_CBC:
+ case CKM_PBE_SHA1_RC2_40_CBC:
+ case CKM_PBE_SHA1_RC2_128_CBC:
+ case CKM_PBE_SHA1_RC4_40:
+ case CKM_PBE_SHA1_RC4_128:
+ case CKM_PBE_SHA1_DES3_EDE_CBC:
+ case CKM_PBE_SHA1_DES2_EDE_CBC:
+ case CKM_PKCS5_PBKD2:
+ return type;
+ default:
+ return pk11_lookup(type)->keyGen;
+ }
+}
+
+/*
+ * get the mechanism block size
+ */
+int
+PK11_GetBlockSize(CK_MECHANISM_TYPE type, SECItem *params)
+{
+ CK_RC5_PARAMS *rc5_params;
+ CK_RC5_CBC_PARAMS *rc5_cbc_params;
+ switch (type) {
+ case CKM_RC5_ECB:
+ if ((params) && (params->data)) {
+ rc5_params = (CK_RC5_PARAMS *)params->data;
+ return (rc5_params->ulWordsize) * 2;
+ }
+ return 8;
+ case CKM_RC5_CBC:
+ case CKM_RC5_CBC_PAD:
+ if ((params) && (params->data)) {
+ rc5_cbc_params = (CK_RC5_CBC_PARAMS *)params->data;
+ return (rc5_cbc_params->ulWordsize) * 2;
+ }
+ return 8;
+ case CKM_DES_ECB:
+ case CKM_DES3_ECB:
+ case CKM_RC2_ECB:
+ case CKM_IDEA_ECB:
+ case CKM_CAST_ECB:
+ case CKM_CAST3_ECB:
+ case CKM_CAST5_ECB:
+ case CKM_RC2_CBC:
+ case CKM_SKIPJACK_CBC64:
+ case CKM_SKIPJACK_ECB64:
+ case CKM_SKIPJACK_OFB64:
+ case CKM_SKIPJACK_CFB64:
+ case CKM_DES_CBC:
+ case CKM_DES3_CBC:
+ case CKM_IDEA_CBC:
+ case CKM_CAST_CBC:
+ case CKM_CAST3_CBC:
+ case CKM_CAST5_CBC:
+ case CKM_DES_CBC_PAD:
+ case CKM_DES3_CBC_PAD:
+ case CKM_RC2_CBC_PAD:
+ case CKM_IDEA_CBC_PAD:
+ case CKM_CAST_CBC_PAD:
+ case CKM_CAST3_CBC_PAD:
+ case CKM_CAST5_CBC_PAD:
+ case CKM_PBE_MD2_DES_CBC:
+ case CKM_PBE_MD5_DES_CBC:
+ case CKM_NSS_PBE_SHA1_DES_CBC:
+ case CKM_NSS_PBE_SHA1_40_BIT_RC2_CBC:
+ case CKM_NSS_PBE_SHA1_128_BIT_RC2_CBC:
+ case CKM_NSS_PBE_SHA1_TRIPLE_DES_CBC:
+ case CKM_NSS_PBE_SHA1_FAULTY_3DES_CBC:
+ case CKM_PBE_SHA1_RC2_40_CBC:
+ case CKM_PBE_SHA1_RC2_128_CBC:
+ case CKM_PBE_SHA1_DES3_EDE_CBC:
+ case CKM_PBE_SHA1_DES2_EDE_CBC:
+ return 8;
+ case CKM_SKIPJACK_CFB32:
+ case CKM_SKIPJACK_CFB16:
+ case CKM_SKIPJACK_CFB8:
+ return 4;
+ case CKM_SEED_ECB:
+ case CKM_SEED_CBC:
+ case CKM_SEED_CBC_PAD:
+ case CKM_CAMELLIA_ECB:
+ case CKM_CAMELLIA_CBC:
+ case CKM_CAMELLIA_CBC_PAD:
+ case CKM_AES_ECB:
+ case CKM_AES_CBC:
+ case CKM_AES_CBC_PAD:
+ case CKM_BATON_ECB128:
+ case CKM_BATON_CBC128:
+ case CKM_BATON_COUNTER:
+ case CKM_BATON_SHUFFLE:
+ case CKM_JUNIPER_ECB128:
+ case CKM_JUNIPER_CBC128:
+ case CKM_JUNIPER_COUNTER:
+ case CKM_JUNIPER_SHUFFLE:
+ return 16;
+ case CKM_BATON_ECB96:
+ return 12;
+ case CKM_RC4:
+ case CKM_NSS_PBE_SHA1_40_BIT_RC4:
+ case CKM_NSS_PBE_SHA1_128_BIT_RC4:
+ case CKM_PBE_SHA1_RC4_40:
+ case CKM_PBE_SHA1_RC4_128:
+ return 0;
+ case CKM_RSA_PKCS:
+ case CKM_RSA_9796:
+ case CKM_RSA_X_509:
+ /*actually it's the modulus length of the key!*/
+ return -1; /* failure */
+ case CKM_NSS_CHACHA20_POLY1305:
+ case CKM_NSS_CHACHA20_CTR:
+ case CKM_CHACHA20_POLY1305:
+ case CKM_CHACHA20:
+ return 64;
+ default:
+ return pk11_lookup(type)->blockSize;
+ }
+}
+
+/*
+ * get the iv length
+ */
+int
+PK11_GetIVLength(CK_MECHANISM_TYPE type)
+{
+ switch (type) {
+ case CKM_SEED_ECB:
+ case CKM_CAMELLIA_ECB:
+ case CKM_AES_ECB:
+ case CKM_DES_ECB:
+ case CKM_DES3_ECB:
+ case CKM_RC2_ECB:
+ case CKM_IDEA_ECB:
+ case CKM_SKIPJACK_WRAP:
+ case CKM_BATON_WRAP:
+ case CKM_RC5_ECB:
+ case CKM_CAST_ECB:
+ case CKM_CAST3_ECB:
+ case CKM_CAST5_ECB:
+ return 0;
+ case CKM_RC2_CBC:
+ case CKM_DES_CBC:
+ case CKM_DES3_CBC:
+ case CKM_IDEA_CBC:
+ case CKM_PBE_MD2_DES_CBC:
+ case CKM_PBE_MD5_DES_CBC:
+ case CKM_NSS_PBE_SHA1_DES_CBC:
+ case CKM_NSS_PBE_SHA1_40_BIT_RC2_CBC:
+ case CKM_NSS_PBE_SHA1_128_BIT_RC2_CBC:
+ case CKM_NSS_PBE_SHA1_TRIPLE_DES_CBC:
+ case CKM_NSS_PBE_SHA1_FAULTY_3DES_CBC:
+ case CKM_PBE_SHA1_RC2_40_CBC:
+ case CKM_PBE_SHA1_RC2_128_CBC:
+ case CKM_PBE_SHA1_DES3_EDE_CBC:
+ case CKM_PBE_SHA1_DES2_EDE_CBC:
+ case CKM_RC5_CBC:
+ case CKM_CAST_CBC:
+ case CKM_CAST3_CBC:
+ case CKM_CAST5_CBC:
+ case CKM_RC2_CBC_PAD:
+ case CKM_DES_CBC_PAD:
+ case CKM_DES3_CBC_PAD:
+ case CKM_IDEA_CBC_PAD:
+ case CKM_RC5_CBC_PAD:
+ case CKM_CAST_CBC_PAD:
+ case CKM_CAST3_CBC_PAD:
+ case CKM_CAST5_CBC_PAD:
+ return 8;
+ case CKM_AES_GCM:
+ case CKM_NSS_CHACHA20_POLY1305:
+ case CKM_CHACHA20_POLY1305:
+ return 12;
+ case CKM_SEED_CBC:
+ case CKM_SEED_CBC_PAD:
+ case CKM_CAMELLIA_CBC:
+ case CKM_CAMELLIA_CBC_PAD:
+ case CKM_AES_CBC:
+ case CKM_AES_CBC_PAD:
+ case CKM_NSS_CHACHA20_CTR:
+ case CKM_CHACHA20:
+ return 16;
+ case CKM_SKIPJACK_CBC64:
+ case CKM_SKIPJACK_ECB64:
+ case CKM_SKIPJACK_OFB64:
+ case CKM_SKIPJACK_CFB64:
+ case CKM_SKIPJACK_CFB32:
+ case CKM_SKIPJACK_CFB16:
+ case CKM_SKIPJACK_CFB8:
+ case CKM_BATON_ECB128:
+ case CKM_BATON_ECB96:
+ case CKM_BATON_CBC128:
+ case CKM_BATON_COUNTER:
+ case CKM_BATON_SHUFFLE:
+ case CKM_JUNIPER_ECB128:
+ case CKM_JUNIPER_CBC128:
+ case CKM_JUNIPER_COUNTER:
+ case CKM_JUNIPER_SHUFFLE:
+ return 24;
+ case CKM_RC4:
+ case CKM_RSA_PKCS:
+ case CKM_RSA_9796:
+ case CKM_RSA_X_509:
+ case CKM_NSS_PBE_SHA1_40_BIT_RC4:
+ case CKM_NSS_PBE_SHA1_128_BIT_RC4:
+ case CKM_PBE_SHA1_RC4_40:
+ case CKM_PBE_SHA1_RC4_128:
+ return 0;
+ default:
+ return pk11_lookup(type)->iv;
+ }
+}
+
+/* These next two utilities are here to help facilitate future
+ * Dynamic Encrypt/Decrypt symetric key mechanisms, and to allow functions
+ * like SSL and S-MIME to automatically add them.
+ */
+SECItem *
+pk11_ParamFromIVWithLen(CK_MECHANISM_TYPE type, SECItem *iv, int keyLen)
+{
+ CK_RC2_CBC_PARAMS *rc2_params = NULL;
+ CK_RC2_PARAMS *rc2_ecb_params = NULL;
+ CK_RC5_PARAMS *rc5_params = NULL;
+ CK_RC5_CBC_PARAMS *rc5_cbc_params = NULL;
+ SECItem *param;
+
+ param = (SECItem *)PORT_Alloc(sizeof(SECItem));
+ if (param == NULL)
+ return NULL;
+ param->data = NULL;
+ param->len = 0;
+ param->type = 0;
+ switch (type) {
+ case CKM_SEED_ECB:
+ case CKM_CAMELLIA_ECB:
+ case CKM_AES_ECB:
+ case CKM_DES_ECB:
+ case CKM_DES3_ECB:
+ case CKM_RSA_PKCS:
+ case CKM_RSA_X_509:
+ case CKM_RSA_9796:
+ case CKM_IDEA_ECB:
+ case CKM_CDMF_ECB:
+ case CKM_CAST_ECB:
+ case CKM_CAST3_ECB:
+ case CKM_CAST5_ECB:
+ case CKM_RC4:
+ break;
+ case CKM_RC2_ECB:
+ rc2_ecb_params = (CK_RC2_PARAMS *)PORT_Alloc(sizeof(CK_RC2_PARAMS));
+ if (rc2_ecb_params == NULL)
+ break;
+ /* Maybe we should pass the key size in too to get this value? */
+ *rc2_ecb_params = keyLen ? keyLen * 8 : 128;
+ param->data = (unsigned char *)rc2_ecb_params;
+ param->len = sizeof(CK_RC2_PARAMS);
+ break;
+ case CKM_RC2_CBC:
+ case CKM_RC2_CBC_PAD:
+ rc2_params = (CK_RC2_CBC_PARAMS *)PORT_Alloc(sizeof(CK_RC2_CBC_PARAMS));
+ if (rc2_params == NULL)
+ break;
+ /* Maybe we should pass the key size in too to get this value? */
+ rc2_params->ulEffectiveBits = keyLen ? keyLen * 8 : 128;
+ if (iv && iv->data)
+ PORT_Memcpy(rc2_params->iv, iv->data, sizeof(rc2_params->iv));
+ param->data = (unsigned char *)rc2_params;
+ param->len = sizeof(CK_RC2_CBC_PARAMS);
+ break;
+ case CKM_RC5_CBC:
+ case CKM_RC5_CBC_PAD:
+ rc5_cbc_params = (CK_RC5_CBC_PARAMS *)
+ PORT_Alloc(sizeof(CK_RC5_CBC_PARAMS) + ((iv) ? iv->len : 0));
+ if (rc5_cbc_params == NULL)
+ break;
+ if (iv && iv->data && iv->len) {
+ rc5_cbc_params->pIv = ((CK_BYTE_PTR)rc5_cbc_params) + sizeof(CK_RC5_CBC_PARAMS);
+ PORT_Memcpy(rc5_cbc_params->pIv, iv->data, iv->len);
+ rc5_cbc_params->ulIvLen = iv->len;
+ rc5_cbc_params->ulWordsize = iv->len / 2;
+ } else {
+ rc5_cbc_params->ulWordsize = 4;
+ rc5_cbc_params->pIv = NULL;
+ rc5_cbc_params->ulIvLen = 0;
+ }
+ rc5_cbc_params->ulRounds = 16;
+ param->data = (unsigned char *)rc5_cbc_params;
+ param->len = sizeof(CK_RC5_CBC_PARAMS);
+ break;
+ case CKM_RC5_ECB:
+ rc5_params = (CK_RC5_PARAMS *)PORT_Alloc(sizeof(CK_RC5_PARAMS));
+ if (rc5_params == NULL)
+ break;
+ if (iv && iv->data && iv->len) {
+ rc5_params->ulWordsize = iv->len / 2;
+ } else {
+ rc5_params->ulWordsize = 4;
+ }
+ rc5_params->ulRounds = 16;
+ param->data = (unsigned char *)rc5_params;
+ param->len = sizeof(CK_RC5_PARAMS);
+ break;
+
+ case CKM_SEED_CBC:
+ case CKM_CAMELLIA_CBC:
+ case CKM_AES_CBC:
+ case CKM_DES_CBC:
+ case CKM_DES3_CBC:
+ case CKM_IDEA_CBC:
+ case CKM_CDMF_CBC:
+ case CKM_CAST_CBC:
+ case CKM_CAST3_CBC:
+ case CKM_CAST5_CBC:
+ case CKM_CAMELLIA_CBC_PAD:
+ case CKM_AES_CBC_PAD:
+ case CKM_DES_CBC_PAD:
+ case CKM_DES3_CBC_PAD:
+ case CKM_IDEA_CBC_PAD:
+ case CKM_CDMF_CBC_PAD:
+ case CKM_CAST_CBC_PAD:
+ case CKM_CAST3_CBC_PAD:
+ case CKM_CAST5_CBC_PAD:
+ case CKM_SKIPJACK_CBC64:
+ case CKM_SKIPJACK_ECB64:
+ case CKM_SKIPJACK_OFB64:
+ case CKM_SKIPJACK_CFB64:
+ case CKM_SKIPJACK_CFB32:
+ case CKM_SKIPJACK_CFB16:
+ case CKM_SKIPJACK_CFB8:
+ case CKM_BATON_ECB128:
+ case CKM_BATON_ECB96:
+ case CKM_BATON_CBC128:
+ case CKM_BATON_COUNTER:
+ case CKM_BATON_SHUFFLE:
+ case CKM_JUNIPER_ECB128:
+ case CKM_JUNIPER_CBC128:
+ case CKM_JUNIPER_COUNTER:
+ case CKM_JUNIPER_SHUFFLE:
+ if ((iv == NULL) || (iv->data == NULL))
+ break;
+ param->data = (unsigned char *)PORT_Alloc(iv->len);
+ if (param->data != NULL) {
+ PORT_Memcpy(param->data, iv->data, iv->len);
+ param->len = iv->len;
+ }
+ break;
+ /* unknown mechanism, pass IV in if it's there */
+ default:
+ if (pk11_lookup(type)->iv == 0) {
+ break;
+ }
+ if ((iv == NULL) || (iv->data == NULL)) {
+ break;
+ }
+ param->data = (unsigned char *)PORT_Alloc(iv->len);
+ if (param->data != NULL) {
+ PORT_Memcpy(param->data, iv->data, iv->len);
+ param->len = iv->len;
+ }
+ break;
+ }
+ return param;
+}
+
+/* These next two utilities are here to help facilitate future
+ * Dynamic Encrypt/Decrypt symetric key mechanisms, and to allow functions
+ * like SSL and S-MIME to automatically add them.
+ */
+SECItem *
+PK11_ParamFromIV(CK_MECHANISM_TYPE type, SECItem *iv)
+{
+ return pk11_ParamFromIVWithLen(type, iv, 0);
+}
+
+unsigned char *
+PK11_IVFromParam(CK_MECHANISM_TYPE type, SECItem *param, int *len)
+{
+ CK_RC2_CBC_PARAMS *rc2_params;
+ CK_RC5_CBC_PARAMS *rc5_cbc_params;
+
+ *len = 0;
+ switch (type) {
+ case CKM_SEED_ECB:
+ case CKM_CAMELLIA_ECB:
+ case CKM_AES_ECB:
+ case CKM_DES_ECB:
+ case CKM_DES3_ECB:
+ case CKM_RSA_PKCS:
+ case CKM_RSA_X_509:
+ case CKM_RSA_9796:
+ case CKM_IDEA_ECB:
+ case CKM_CDMF_ECB:
+ case CKM_CAST_ECB:
+ case CKM_CAST3_ECB:
+ case CKM_CAST5_ECB:
+ case CKM_RC4:
+ return NULL;
+ case CKM_RC2_ECB:
+ return NULL;
+ case CKM_RC2_CBC:
+ case CKM_RC2_CBC_PAD:
+ rc2_params = (CK_RC2_CBC_PARAMS *)param->data;
+ *len = sizeof(rc2_params->iv);
+ return &rc2_params->iv[0];
+ case CKM_RC5_CBC:
+ case CKM_RC5_CBC_PAD:
+ rc5_cbc_params = (CK_RC5_CBC_PARAMS *)param->data;
+ *len = rc5_cbc_params->ulIvLen;
+ return rc5_cbc_params->pIv;
+ case CKM_SEED_CBC:
+ case CKM_CAMELLIA_CBC:
+ case CKM_AES_CBC:
+ case CKM_DES_CBC:
+ case CKM_DES3_CBC:
+ case CKM_IDEA_CBC:
+ case CKM_CDMF_CBC:
+ case CKM_CAST_CBC:
+ case CKM_CAST3_CBC:
+ case CKM_CAST5_CBC:
+ case CKM_CAMELLIA_CBC_PAD:
+ case CKM_AES_CBC_PAD:
+ case CKM_DES_CBC_PAD:
+ case CKM_DES3_CBC_PAD:
+ case CKM_IDEA_CBC_PAD:
+ case CKM_CDMF_CBC_PAD:
+ case CKM_CAST_CBC_PAD:
+ case CKM_CAST3_CBC_PAD:
+ case CKM_CAST5_CBC_PAD:
+ case CKM_SKIPJACK_CBC64:
+ case CKM_SKIPJACK_ECB64:
+ case CKM_SKIPJACK_OFB64:
+ case CKM_SKIPJACK_CFB64:
+ case CKM_SKIPJACK_CFB32:
+ case CKM_SKIPJACK_CFB16:
+ case CKM_SKIPJACK_CFB8:
+ case CKM_BATON_ECB128:
+ case CKM_BATON_ECB96:
+ case CKM_BATON_CBC128:
+ case CKM_BATON_COUNTER:
+ case CKM_BATON_SHUFFLE:
+ case CKM_JUNIPER_ECB128:
+ case CKM_JUNIPER_CBC128:
+ case CKM_JUNIPER_COUNTER:
+ case CKM_JUNIPER_SHUFFLE:
+ break;
+ /* unknown mechanism, pass IV in if it's there */
+ default:
+ break;
+ }
+ if (param->data) {
+ *len = param->len;
+ }
+ return param->data;
+}
+
+typedef struct sec_rc5cbcParameterStr {
+ SECItem version;
+ SECItem rounds;
+ SECItem blockSizeInBits;
+ SECItem iv;
+} sec_rc5cbcParameter;
+
+static const SEC_ASN1Template sec_rc5ecb_parameter_template[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(sec_rc5cbcParameter) },
+ { SEC_ASN1_INTEGER,
+ offsetof(sec_rc5cbcParameter, version) },
+ { SEC_ASN1_INTEGER,
+ offsetof(sec_rc5cbcParameter, rounds) },
+ { SEC_ASN1_INTEGER,
+ offsetof(sec_rc5cbcParameter, blockSizeInBits) },
+ { 0 }
+};
+
+static const SEC_ASN1Template sec_rc5cbc_parameter_template[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(sec_rc5cbcParameter) },
+ { SEC_ASN1_INTEGER,
+ offsetof(sec_rc5cbcParameter, version) },
+ { SEC_ASN1_INTEGER,
+ offsetof(sec_rc5cbcParameter, rounds) },
+ { SEC_ASN1_INTEGER,
+ offsetof(sec_rc5cbcParameter, blockSizeInBits) },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(sec_rc5cbcParameter, iv) },
+ { 0 }
+};
+
+typedef struct sec_rc2cbcParameterStr {
+ SECItem rc2ParameterVersion;
+ SECItem iv;
+} sec_rc2cbcParameter;
+
+static const SEC_ASN1Template sec_rc2cbc_parameter_template[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(sec_rc2cbcParameter) },
+ { SEC_ASN1_INTEGER,
+ offsetof(sec_rc2cbcParameter, rc2ParameterVersion) },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(sec_rc2cbcParameter, iv) },
+ { 0 }
+};
+
+static const SEC_ASN1Template sec_rc2ecb_parameter_template[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(sec_rc2cbcParameter) },
+ { SEC_ASN1_INTEGER,
+ offsetof(sec_rc2cbcParameter, rc2ParameterVersion) },
+ { 0 }
+};
+
+/* S/MIME picked id values to represent differnt keysizes */
+/* I do have a formula, but it ain't pretty, and it only works because you
+ * can always match three points to a parabola:) */
+static unsigned char
+rc2_map(SECItem *version)
+{
+ long x;
+
+ x = DER_GetInteger(version);
+
+ switch (x) {
+ case 58:
+ return 128;
+ case 120:
+ return 64;
+ case 160:
+ return 40;
+ }
+ return 128;
+}
+
+static unsigned long
+rc2_unmap(unsigned long x)
+{
+ switch (x) {
+ case 128:
+ return 58;
+ case 64:
+ return 120;
+ case 40:
+ return 160;
+ }
+ return 58;
+}
+
+/* Generate a mechaism param from a type, and iv. */
+SECItem *
+PK11_ParamFromAlgid(SECAlgorithmID *algid)
+{
+ CK_RC2_CBC_PARAMS *rc2_cbc_params = NULL;
+ CK_RC2_PARAMS *rc2_ecb_params = NULL;
+ CK_RC5_CBC_PARAMS *rc5_cbc_params = NULL;
+ CK_RC5_PARAMS *rc5_ecb_params = NULL;
+ PLArenaPool *arena = NULL;
+ SECItem *mech = NULL;
+ SECOidTag algtag;
+ SECStatus rv;
+ CK_MECHANISM_TYPE type;
+ /* initialize these to prevent UMRs in the ASN1 decoder. */
+ SECItem iv = { siBuffer, NULL, 0 };
+ sec_rc2cbcParameter rc2 = { { siBuffer, NULL, 0 }, { siBuffer, NULL, 0 } };
+ sec_rc5cbcParameter rc5 = { { siBuffer, NULL, 0 }, { siBuffer, NULL, 0 }, { siBuffer, NULL, 0 }, { siBuffer, NULL, 0 } };
+
+ algtag = SECOID_GetAlgorithmTag(algid);
+ type = PK11_AlgtagToMechanism(algtag);
+
+ mech = PORT_New(SECItem);
+ if (mech == NULL) {
+ return NULL;
+ }
+ mech->type = siBuffer;
+ mech->data = NULL;
+ mech->len = 0;
+
+ arena = PORT_NewArena(1024);
+ if (!arena) {
+ goto loser;
+ }
+
+ /* handle the complicated cases */
+ switch (type) {
+ case CKM_RC2_ECB:
+ rv = SEC_ASN1DecodeItem(arena, &rc2, sec_rc2ecb_parameter_template,
+ &(algid->parameters));
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ rc2_ecb_params = PORT_New(CK_RC2_PARAMS);
+ if (rc2_ecb_params == NULL) {
+ goto loser;
+ }
+ *rc2_ecb_params = rc2_map(&rc2.rc2ParameterVersion);
+ mech->data = (unsigned char *)rc2_ecb_params;
+ mech->len = sizeof *rc2_ecb_params;
+ break;
+ case CKM_RC2_CBC:
+ case CKM_RC2_CBC_PAD:
+ rv = SEC_ASN1DecodeItem(arena, &rc2, sec_rc2cbc_parameter_template,
+ &(algid->parameters));
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ rc2_cbc_params = PORT_New(CK_RC2_CBC_PARAMS);
+ if (rc2_cbc_params == NULL) {
+ goto loser;
+ }
+ mech->data = (unsigned char *)rc2_cbc_params;
+ mech->len = sizeof *rc2_cbc_params;
+ rc2_cbc_params->ulEffectiveBits = rc2_map(&rc2.rc2ParameterVersion);
+ if (rc2.iv.len != sizeof rc2_cbc_params->iv) {
+ PORT_SetError(SEC_ERROR_INPUT_LEN);
+ goto loser;
+ }
+ PORT_Memcpy(rc2_cbc_params->iv, rc2.iv.data, rc2.iv.len);
+ break;
+ case CKM_RC5_ECB:
+ rv = SEC_ASN1DecodeItem(arena, &rc5, sec_rc5ecb_parameter_template,
+ &(algid->parameters));
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ rc5_ecb_params = PORT_New(CK_RC5_PARAMS);
+ if (rc5_ecb_params == NULL) {
+ goto loser;
+ }
+ rc5_ecb_params->ulRounds = DER_GetInteger(&rc5.rounds);
+ rc5_ecb_params->ulWordsize = DER_GetInteger(&rc5.blockSizeInBits) / 8;
+ mech->data = (unsigned char *)rc5_ecb_params;
+ mech->len = sizeof *rc5_ecb_params;
+ break;
+ case CKM_RC5_CBC:
+ case CKM_RC5_CBC_PAD:
+ rv = SEC_ASN1DecodeItem(arena, &rc5, sec_rc5cbc_parameter_template,
+ &(algid->parameters));
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ rc5_cbc_params = (CK_RC5_CBC_PARAMS *)
+ PORT_Alloc(sizeof(CK_RC5_CBC_PARAMS) + rc5.iv.len);
+ if (rc5_cbc_params == NULL) {
+ goto loser;
+ }
+ mech->data = (unsigned char *)rc5_cbc_params;
+ mech->len = sizeof *rc5_cbc_params;
+ rc5_cbc_params->ulRounds = DER_GetInteger(&rc5.rounds);
+ rc5_cbc_params->ulWordsize = DER_GetInteger(&rc5.blockSizeInBits) / 8;
+ rc5_cbc_params->pIv = ((CK_BYTE_PTR)rc5_cbc_params) + sizeof(CK_RC5_CBC_PARAMS);
+ rc5_cbc_params->ulIvLen = rc5.iv.len;
+ PORT_Memcpy(rc5_cbc_params->pIv, rc5.iv.data, rc5.iv.len);
+ break;
+ case CKM_PBE_MD2_DES_CBC:
+ case CKM_PBE_MD5_DES_CBC:
+ case CKM_NSS_PBE_SHA1_DES_CBC:
+ case CKM_NSS_PBE_SHA1_TRIPLE_DES_CBC:
+ case CKM_NSS_PBE_SHA1_FAULTY_3DES_CBC:
+ case CKM_NSS_PBE_SHA1_40_BIT_RC2_CBC:
+ case CKM_NSS_PBE_SHA1_128_BIT_RC2_CBC:
+ case CKM_NSS_PBE_SHA1_40_BIT_RC4:
+ case CKM_NSS_PBE_SHA1_128_BIT_RC4:
+ case CKM_PBE_SHA1_DES2_EDE_CBC:
+ case CKM_PBE_SHA1_DES3_EDE_CBC:
+ case CKM_PBE_SHA1_RC2_40_CBC:
+ case CKM_PBE_SHA1_RC2_128_CBC:
+ case CKM_PBE_SHA1_RC4_40:
+ case CKM_PBE_SHA1_RC4_128:
+ case CKM_PKCS5_PBKD2:
+ rv = pbe_PK11AlgidToParam(algid, mech);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ break;
+ case CKM_RC4:
+ case CKM_SEED_ECB:
+ case CKM_CAMELLIA_ECB:
+ case CKM_AES_ECB:
+ case CKM_DES_ECB:
+ case CKM_DES3_ECB:
+ case CKM_IDEA_ECB:
+ case CKM_CDMF_ECB:
+ case CKM_CAST_ECB:
+ case CKM_CAST3_ECB:
+ case CKM_CAST5_ECB:
+ break;
+
+ default:
+ if (pk11_lookup(type)->iv == 0) {
+ break;
+ }
+ /* FALL THROUGH */
+ case CKM_SEED_CBC:
+ case CKM_CAMELLIA_CBC:
+ case CKM_AES_CBC:
+ case CKM_DES_CBC:
+ case CKM_DES3_CBC:
+ case CKM_IDEA_CBC:
+ case CKM_CDMF_CBC:
+ case CKM_CAST_CBC:
+ case CKM_CAST3_CBC:
+ case CKM_CAST5_CBC:
+ case CKM_SEED_CBC_PAD:
+ case CKM_CAMELLIA_CBC_PAD:
+ case CKM_AES_CBC_PAD:
+ case CKM_DES_CBC_PAD:
+ case CKM_DES3_CBC_PAD:
+ case CKM_IDEA_CBC_PAD:
+ case CKM_CDMF_CBC_PAD:
+ case CKM_CAST_CBC_PAD:
+ case CKM_CAST3_CBC_PAD:
+ case CKM_CAST5_CBC_PAD:
+ case CKM_SKIPJACK_CBC64:
+ case CKM_SKIPJACK_ECB64:
+ case CKM_SKIPJACK_OFB64:
+ case CKM_SKIPJACK_CFB64:
+ case CKM_SKIPJACK_CFB32:
+ case CKM_SKIPJACK_CFB16:
+ case CKM_SKIPJACK_CFB8:
+ case CKM_BATON_ECB128:
+ case CKM_BATON_ECB96:
+ case CKM_BATON_CBC128:
+ case CKM_BATON_COUNTER:
+ case CKM_BATON_SHUFFLE:
+ case CKM_JUNIPER_ECB128:
+ case CKM_JUNIPER_CBC128:
+ case CKM_JUNIPER_COUNTER:
+ case CKM_JUNIPER_SHUFFLE:
+ /* simple cases are simply octet string encoded IVs */
+ rv = SEC_ASN1DecodeItem(arena, &iv,
+ SEC_ASN1_GET(SEC_OctetStringTemplate),
+ &(algid->parameters));
+ if (rv != SECSuccess || iv.data == NULL) {
+ goto loser;
+ }
+ /* XXX Should be some IV length sanity check here. */
+ mech->data = (unsigned char *)PORT_Alloc(iv.len);
+ if (mech->data == NULL) {
+ goto loser;
+ }
+ PORT_Memcpy(mech->data, iv.data, iv.len);
+ mech->len = iv.len;
+ break;
+ }
+ PORT_FreeArena(arena, PR_FALSE);
+ return mech;
+
+loser:
+ if (arena)
+ PORT_FreeArena(arena, PR_FALSE);
+ SECITEM_FreeItem(mech, PR_TRUE);
+ return NULL;
+}
+
+/*
+ * Generate an IV for the given mechanism
+ */
+static SECStatus
+pk11_GenIV(CK_MECHANISM_TYPE type, SECItem *iv)
+{
+ int iv_size = PK11_GetIVLength(type);
+ SECStatus rv;
+
+ iv->len = iv_size;
+ if (iv_size == 0) {
+ iv->data = NULL;
+ return SECSuccess;
+ }
+
+ iv->data = (unsigned char *)PORT_Alloc(iv_size);
+ if (iv->data == NULL) {
+ iv->len = 0;
+ return SECFailure;
+ }
+
+ rv = PK11_GenerateRandom(iv->data, iv->len);
+ if (rv != SECSuccess) {
+ PORT_Free(iv->data);
+ iv->data = NULL;
+ iv->len = 0;
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/*
+ * create a new parameter block from the passed in MECHANISM and the
+ * key. Use Netscape's S/MIME Rules for the New param block.
+ */
+SECItem *
+pk11_GenerateNewParamWithKeyLen(CK_MECHANISM_TYPE type, int keyLen)
+{
+ CK_RC2_CBC_PARAMS *rc2_params;
+ CK_RC2_PARAMS *rc2_ecb_params;
+ SECItem *mech;
+ SECItem iv;
+ SECStatus rv;
+
+ mech = (SECItem *)PORT_Alloc(sizeof(SECItem));
+ if (mech == NULL)
+ return NULL;
+
+ rv = SECSuccess;
+ mech->type = siBuffer;
+ mech->data = NULL;
+ mech->len = 0;
+ switch (type) {
+ case CKM_RC4:
+ case CKM_SEED_ECB:
+ case CKM_CAMELLIA_ECB:
+ case CKM_AES_ECB:
+ case CKM_DES_ECB:
+ case CKM_DES3_ECB:
+ case CKM_IDEA_ECB:
+ case CKM_CDMF_ECB:
+ case CKM_CAST_ECB:
+ case CKM_CAST3_ECB:
+ case CKM_CAST5_ECB:
+ break;
+ case CKM_RC2_ECB:
+ rc2_ecb_params = (CK_RC2_PARAMS *)PORT_Alloc(sizeof(CK_RC2_PARAMS));
+ if (rc2_ecb_params == NULL) {
+ rv = SECFailure;
+ break;
+ }
+ /* NOTE PK11_GetKeyLength can return -1 if the key isn't and RC2, RC5,
+ * or RC4 key. Of course that wouldn't happen here doing RC2:).*/
+ *rc2_ecb_params = keyLen ? keyLen * 8 : 128;
+ mech->data = (unsigned char *)rc2_ecb_params;
+ mech->len = sizeof(CK_RC2_PARAMS);
+ break;
+ case CKM_RC2_CBC:
+ case CKM_RC2_CBC_PAD:
+ rv = pk11_GenIV(type, &iv);
+ if (rv != SECSuccess) {
+ break;
+ }
+ rc2_params = (CK_RC2_CBC_PARAMS *)PORT_Alloc(sizeof(CK_RC2_CBC_PARAMS));
+ if (rc2_params == NULL) {
+ PORT_Free(iv.data);
+ rv = SECFailure;
+ break;
+ }
+ /* NOTE PK11_GetKeyLength can return -1 if the key isn't and RC2, RC5,
+ * or RC4 key. Of course that wouldn't happen here doing RC2:).*/
+ rc2_params->ulEffectiveBits = keyLen ? keyLen * 8 : 128;
+ if (iv.data)
+ PORT_Memcpy(rc2_params->iv, iv.data, sizeof(rc2_params->iv));
+ mech->data = (unsigned char *)rc2_params;
+ mech->len = sizeof(CK_RC2_CBC_PARAMS);
+ PORT_Free(iv.data);
+ break;
+ case CKM_RC5_ECB:
+ PORT_Free(mech);
+ return PK11_ParamFromIV(type, NULL);
+ case CKM_RC5_CBC:
+ case CKM_RC5_CBC_PAD:
+ rv = pk11_GenIV(type, &iv);
+ if (rv != SECSuccess) {
+ break;
+ }
+ PORT_Free(mech);
+ return PK11_ParamFromIV(type, &iv);
+ default:
+ if (pk11_lookup(type)->iv == 0) {
+ break;
+ }
+ case CKM_SEED_CBC:
+ case CKM_CAMELLIA_CBC:
+ case CKM_AES_CBC:
+ case CKM_DES_CBC:
+ case CKM_DES3_CBC:
+ case CKM_IDEA_CBC:
+ case CKM_CDMF_CBC:
+ case CKM_CAST_CBC:
+ case CKM_CAST3_CBC:
+ case CKM_CAST5_CBC:
+ case CKM_DES_CBC_PAD:
+ case CKM_DES3_CBC_PAD:
+ case CKM_IDEA_CBC_PAD:
+ case CKM_CDMF_CBC_PAD:
+ case CKM_CAST_CBC_PAD:
+ case CKM_CAST3_CBC_PAD:
+ case CKM_CAST5_CBC_PAD:
+ case CKM_SKIPJACK_CBC64:
+ case CKM_SKIPJACK_ECB64:
+ case CKM_SKIPJACK_OFB64:
+ case CKM_SKIPJACK_CFB64:
+ case CKM_SKIPJACK_CFB32:
+ case CKM_SKIPJACK_CFB16:
+ case CKM_SKIPJACK_CFB8:
+ case CKM_BATON_ECB128:
+ case CKM_BATON_ECB96:
+ case CKM_BATON_CBC128:
+ case CKM_BATON_COUNTER:
+ case CKM_BATON_SHUFFLE:
+ case CKM_JUNIPER_ECB128:
+ case CKM_JUNIPER_CBC128:
+ case CKM_JUNIPER_COUNTER:
+ case CKM_JUNIPER_SHUFFLE:
+ rv = pk11_GenIV(type, &iv);
+ if (rv != SECSuccess) {
+ break;
+ }
+ mech->data = (unsigned char *)PORT_Alloc(iv.len);
+ if (mech->data == NULL) {
+ PORT_Free(iv.data);
+ rv = SECFailure;
+ break;
+ }
+ PORT_Memcpy(mech->data, iv.data, iv.len);
+ mech->len = iv.len;
+ PORT_Free(iv.data);
+ break;
+ }
+ if (rv != SECSuccess) {
+ SECITEM_FreeItem(mech, PR_TRUE);
+ return NULL;
+ }
+ return mech;
+}
+
+SECItem *
+PK11_GenerateNewParam(CK_MECHANISM_TYPE type, PK11SymKey *key)
+{
+ int keyLen = key ? PK11_GetKeyLength(key) : 0;
+
+ return pk11_GenerateNewParamWithKeyLen(type, keyLen);
+}
+
+#define RC5_V10 0x10
+
+/* turn a PKCS #11 parameter into a DER Encoded Algorithm ID */
+SECStatus
+PK11_ParamToAlgid(SECOidTag algTag, SECItem *param,
+ PLArenaPool *arena, SECAlgorithmID *algid)
+{
+ CK_RC2_CBC_PARAMS *rc2_params;
+ sec_rc2cbcParameter rc2;
+ CK_RC5_CBC_PARAMS *rc5_params;
+ sec_rc5cbcParameter rc5;
+ CK_MECHANISM_TYPE type = PK11_AlgtagToMechanism(algTag);
+ SECItem *newParams = NULL;
+ SECStatus rv = SECFailure;
+ unsigned long rc2version;
+
+ switch (type) {
+ case CKM_RC4:
+ case CKM_SEED_ECB:
+ case CKM_CAMELLIA_ECB:
+ case CKM_AES_ECB:
+ case CKM_DES_ECB:
+ case CKM_DES3_ECB:
+ case CKM_IDEA_ECB:
+ case CKM_CDMF_ECB:
+ case CKM_CAST_ECB:
+ case CKM_CAST3_ECB:
+ case CKM_CAST5_ECB:
+ newParams = NULL;
+ rv = SECSuccess;
+ break;
+ case CKM_RC2_ECB:
+ break;
+ case CKM_RC2_CBC:
+ case CKM_RC2_CBC_PAD:
+ rc2_params = (CK_RC2_CBC_PARAMS *)param->data;
+ rc2version = rc2_unmap(rc2_params->ulEffectiveBits);
+ if (SEC_ASN1EncodeUnsignedInteger(NULL, &(rc2.rc2ParameterVersion),
+ rc2version) == NULL)
+ break;
+ rc2.iv.data = rc2_params->iv;
+ rc2.iv.len = sizeof(rc2_params->iv);
+ newParams = SEC_ASN1EncodeItem(NULL, NULL, &rc2,
+ sec_rc2cbc_parameter_template);
+ PORT_Free(rc2.rc2ParameterVersion.data);
+ if (newParams == NULL)
+ break;
+ rv = SECSuccess;
+ break;
+
+ case CKM_RC5_ECB: /* well not really... */
+ break;
+ case CKM_RC5_CBC:
+ case CKM_RC5_CBC_PAD:
+ rc5_params = (CK_RC5_CBC_PARAMS *)param->data;
+ if (SEC_ASN1EncodeUnsignedInteger(NULL, &rc5.version, RC5_V10) == NULL)
+ break;
+ if (SEC_ASN1EncodeUnsignedInteger(NULL, &rc5.blockSizeInBits,
+ rc5_params->ulWordsize * 8) == NULL) {
+ PORT_Free(rc5.version.data);
+ break;
+ }
+ if (SEC_ASN1EncodeUnsignedInteger(NULL, &rc5.rounds,
+ rc5_params->ulWordsize * 8) == NULL) {
+ PORT_Free(rc5.blockSizeInBits.data);
+ PORT_Free(rc5.version.data);
+ break;
+ }
+ rc5.iv.data = rc5_params->pIv;
+ rc5.iv.len = rc5_params->ulIvLen;
+ newParams = SEC_ASN1EncodeItem(NULL, NULL, &rc5,
+ sec_rc5cbc_parameter_template);
+ PORT_Free(rc5.version.data);
+ PORT_Free(rc5.blockSizeInBits.data);
+ PORT_Free(rc5.rounds.data);
+ if (newParams == NULL)
+ break;
+ rv = SECSuccess;
+ break;
+ case CKM_PBE_MD2_DES_CBC:
+ case CKM_PBE_MD5_DES_CBC:
+ case CKM_NSS_PBE_SHA1_DES_CBC:
+ case CKM_NSS_PBE_SHA1_TRIPLE_DES_CBC:
+ case CKM_NSS_PBE_SHA1_FAULTY_3DES_CBC:
+ case CKM_NSS_PBE_SHA1_40_BIT_RC2_CBC:
+ case CKM_NSS_PBE_SHA1_128_BIT_RC2_CBC:
+ case CKM_NSS_PBE_SHA1_40_BIT_RC4:
+ case CKM_NSS_PBE_SHA1_128_BIT_RC4:
+ case CKM_PBE_SHA1_DES3_EDE_CBC:
+ case CKM_PBE_SHA1_DES2_EDE_CBC:
+ case CKM_PBE_SHA1_RC2_40_CBC:
+ case CKM_PBE_SHA1_RC2_128_CBC:
+ case CKM_PBE_SHA1_RC4_40:
+ case CKM_PBE_SHA1_RC4_128:
+ return PBE_PK11ParamToAlgid(algTag, param, arena, algid);
+ default:
+ if (pk11_lookup(type)->iv == 0) {
+ rv = SECSuccess;
+ newParams = NULL;
+ break;
+ }
+ case CKM_SEED_CBC:
+ case CKM_CAMELLIA_CBC:
+ case CKM_AES_CBC:
+ case CKM_DES_CBC:
+ case CKM_DES3_CBC:
+ case CKM_IDEA_CBC:
+ case CKM_CDMF_CBC:
+ case CKM_CAST_CBC:
+ case CKM_CAST3_CBC:
+ case CKM_CAST5_CBC:
+ case CKM_DES_CBC_PAD:
+ case CKM_DES3_CBC_PAD:
+ case CKM_IDEA_CBC_PAD:
+ case CKM_CDMF_CBC_PAD:
+ case CKM_CAST_CBC_PAD:
+ case CKM_CAST3_CBC_PAD:
+ case CKM_CAST5_CBC_PAD:
+ case CKM_SKIPJACK_CBC64:
+ case CKM_SKIPJACK_ECB64:
+ case CKM_SKIPJACK_OFB64:
+ case CKM_SKIPJACK_CFB64:
+ case CKM_SKIPJACK_CFB32:
+ case CKM_SKIPJACK_CFB16:
+ case CKM_SKIPJACK_CFB8:
+ case CKM_BATON_ECB128:
+ case CKM_BATON_ECB96:
+ case CKM_BATON_CBC128:
+ case CKM_BATON_COUNTER:
+ case CKM_BATON_SHUFFLE:
+ case CKM_JUNIPER_ECB128:
+ case CKM_JUNIPER_CBC128:
+ case CKM_JUNIPER_COUNTER:
+ case CKM_JUNIPER_SHUFFLE:
+ newParams = SEC_ASN1EncodeItem(NULL, NULL, param,
+ SEC_ASN1_GET(SEC_OctetStringTemplate));
+ if (newParams == NULL)
+ break;
+ rv = SECSuccess;
+ break;
+ }
+
+ if (rv != SECSuccess) {
+ if (newParams)
+ SECITEM_FreeItem(newParams, PR_TRUE);
+ return rv;
+ }
+
+ rv = SECOID_SetAlgorithmID(arena, algid, algTag, newParams);
+ SECITEM_FreeItem(newParams, PR_TRUE);
+ return rv;
+}
+
+/* turn an OID algorithm tag into a PKCS #11 mechanism. This allows us to
+ * map OID's directly into the PKCS #11 mechanism we want to call. We find
+ * this mapping in our standard OID table */
+CK_MECHANISM_TYPE
+PK11_AlgtagToMechanism(SECOidTag algTag)
+{
+ SECOidData *oid = SECOID_FindOIDByTag(algTag);
+
+ if (oid)
+ return (CK_MECHANISM_TYPE)oid->mechanism;
+ return CKM_INVALID_MECHANISM;
+}
+
+/* turn a mechanism into an oid. */
+SECOidTag
+PK11_MechanismToAlgtag(CK_MECHANISM_TYPE type)
+{
+ SECOidData *oid = SECOID_FindOIDByMechanism((unsigned long)type);
+
+ if (oid)
+ return oid->offset;
+ return SEC_OID_UNKNOWN;
+}
+
+/* Determine appropriate blocking mechanism, used when wrapping private keys
+ * which require PKCS padding. If the mechanism does not map to a padding
+ * mechanism, we simply return the mechanism.
+ */
+CK_MECHANISM_TYPE
+PK11_GetPadMechanism(CK_MECHANISM_TYPE type)
+{
+ switch (type) {
+ case CKM_SEED_CBC:
+ return CKM_SEED_CBC_PAD;
+ case CKM_CAMELLIA_CBC:
+ return CKM_CAMELLIA_CBC_PAD;
+ case CKM_AES_CBC:
+ return CKM_AES_CBC_PAD;
+ case CKM_DES_CBC:
+ return CKM_DES_CBC_PAD;
+ case CKM_DES3_CBC:
+ return CKM_DES3_CBC_PAD;
+ case CKM_RC2_CBC:
+ return CKM_RC2_CBC_PAD;
+ case CKM_CDMF_CBC:
+ return CKM_CDMF_CBC_PAD;
+ case CKM_CAST_CBC:
+ return CKM_CAST_CBC_PAD;
+ case CKM_CAST3_CBC:
+ return CKM_CAST3_CBC_PAD;
+ case CKM_CAST5_CBC:
+ return CKM_CAST5_CBC_PAD;
+ case CKM_RC5_CBC:
+ return CKM_RC5_CBC_PAD;
+ case CKM_IDEA_CBC:
+ return CKM_IDEA_CBC_PAD;
+ default:
+ break;
+ }
+
+ return type;
+}
+
+static PRBool
+pk11_isAllZero(unsigned char *data, int len)
+{
+ while (len--) {
+ if (*data++) {
+ return PR_FALSE;
+ }
+ }
+ return PR_TRUE;
+}
+
+CK_RV
+PK11_MapPBEMechanismToCryptoMechanism(CK_MECHANISM_PTR pPBEMechanism,
+ CK_MECHANISM_PTR pCryptoMechanism,
+ SECItem *pbe_pwd, PRBool faulty3DES)
+{
+ int iv_len = 0;
+ CK_PBE_PARAMS_PTR pPBEparams;
+ CK_RC2_CBC_PARAMS_PTR rc2_params;
+ CK_ULONG rc2_key_len;
+
+ if ((pPBEMechanism == CK_NULL_PTR) || (pCryptoMechanism == CK_NULL_PTR)) {
+ return CKR_HOST_MEMORY;
+ }
+
+ /* pkcs5 v2 cannot be supported by this interface.
+ * use PK11_GetPBECryptoMechanism instead.
+ */
+ if ((pPBEMechanism->mechanism == CKM_INVALID_MECHANISM) ||
+ (pPBEMechanism->mechanism == CKM_PKCS5_PBKD2)) {
+ return CKR_MECHANISM_INVALID;
+ }
+
+ pPBEparams = (CK_PBE_PARAMS_PTR)pPBEMechanism->pParameter;
+ iv_len = PK11_GetIVLength(pPBEMechanism->mechanism);
+
+ if (iv_len) {
+ if (pk11_isAllZero(pPBEparams->pInitVector, iv_len)) {
+ SECItem param;
+ PK11SymKey *symKey;
+ PK11SlotInfo *intSlot = PK11_GetInternalSlot();
+
+ if (intSlot == NULL) {
+ return CKR_DEVICE_ERROR;
+ }
+
+ param.data = pPBEMechanism->pParameter;
+ param.len = pPBEMechanism->ulParameterLen;
+
+ symKey = PK11_RawPBEKeyGen(intSlot,
+ pPBEMechanism->mechanism, &param, pbe_pwd, faulty3DES, NULL);
+ PK11_FreeSlot(intSlot);
+ if (symKey == NULL) {
+ return CKR_DEVICE_ERROR; /* sigh */
+ }
+ PK11_FreeSymKey(symKey);
+ }
+ }
+
+ switch (pPBEMechanism->mechanism) {
+ case CKM_PBE_MD2_DES_CBC:
+ case CKM_PBE_MD5_DES_CBC:
+ case CKM_NSS_PBE_SHA1_DES_CBC:
+ pCryptoMechanism->mechanism = CKM_DES_CBC;
+ goto have_crypto_mechanism;
+ case CKM_NSS_PBE_SHA1_TRIPLE_DES_CBC:
+ case CKM_NSS_PBE_SHA1_FAULTY_3DES_CBC:
+ case CKM_PBE_SHA1_DES3_EDE_CBC:
+ case CKM_PBE_SHA1_DES2_EDE_CBC:
+ pCryptoMechanism->mechanism = CKM_DES3_CBC;
+ have_crypto_mechanism:
+ pCryptoMechanism->pParameter = PORT_Alloc(iv_len);
+ pCryptoMechanism->ulParameterLen = (CK_ULONG)iv_len;
+ if (pCryptoMechanism->pParameter == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+ PORT_Memcpy((unsigned char *)(pCryptoMechanism->pParameter),
+ (unsigned char *)(pPBEparams->pInitVector),
+ iv_len);
+ break;
+ case CKM_NSS_PBE_SHA1_40_BIT_RC4:
+ case CKM_NSS_PBE_SHA1_128_BIT_RC4:
+ case CKM_PBE_SHA1_RC4_40:
+ case CKM_PBE_SHA1_RC4_128:
+ pCryptoMechanism->mechanism = CKM_RC4;
+ pCryptoMechanism->ulParameterLen = 0;
+ pCryptoMechanism->pParameter = CK_NULL_PTR;
+ break;
+ case CKM_NSS_PBE_SHA1_40_BIT_RC2_CBC:
+ case CKM_PBE_SHA1_RC2_40_CBC:
+ rc2_key_len = 40;
+ goto have_key_len;
+ case CKM_NSS_PBE_SHA1_128_BIT_RC2_CBC:
+ rc2_key_len = 128;
+ have_key_len:
+ pCryptoMechanism->mechanism = CKM_RC2_CBC;
+ pCryptoMechanism->ulParameterLen = (CK_ULONG)sizeof(CK_RC2_CBC_PARAMS);
+ pCryptoMechanism->pParameter = (CK_RC2_CBC_PARAMS_PTR)
+ PORT_ZAlloc(sizeof(CK_RC2_CBC_PARAMS));
+ if (pCryptoMechanism->pParameter == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+ rc2_params = (CK_RC2_CBC_PARAMS_PTR)pCryptoMechanism->pParameter;
+ PORT_Memcpy((unsigned char *)rc2_params->iv,
+ (unsigned char *)pPBEparams->pInitVector,
+ iv_len);
+ rc2_params->ulEffectiveBits = rc2_key_len;
+ break;
+ default:
+ return CKR_MECHANISM_INVALID;
+ }
+
+ return CKR_OK;
+}
+
+/* Make a Key type to an appropriate signing/verification mechanism */
+CK_MECHANISM_TYPE
+PK11_MapSignKeyType(KeyType keyType)
+{
+ switch (keyType) {
+ case rsaKey:
+ return CKM_RSA_PKCS;
+ case fortezzaKey:
+ case dsaKey:
+ return CKM_DSA;
+ case ecKey:
+ return CKM_ECDSA;
+ case dhKey:
+ default:
+ break;
+ }
+ return CKM_INVALID_MECHANISM;
+}
+
+CK_MECHANISM_TYPE
+pk11_mapWrapKeyType(KeyType keyType)
+{
+ switch (keyType) {
+ case rsaKey:
+ return CKM_RSA_PKCS;
+ /* Add fortezza?? */
+ default:
+ break;
+ }
+ return CKM_INVALID_MECHANISM;
+}
+
+SECOidTag
+PK11_FortezzaMapSig(SECOidTag algTag)
+{
+ switch (algTag) {
+ case SEC_OID_MISSI_KEA_DSS:
+ case SEC_OID_MISSI_DSS:
+ case SEC_OID_MISSI_DSS_OLD:
+ case SEC_OID_MISSI_KEA_DSS_OLD:
+ case SEC_OID_BOGUS_DSA_SIGNATURE_WITH_SHA1_DIGEST:
+ return SEC_OID_ANSIX9_DSA_SIGNATURE;
+ default:
+ break;
+ }
+ return algTag;
+}
diff --git a/security/nss/lib/pk11wrap/pk11merge.c b/security/nss/lib/pk11wrap/pk11merge.c
new file mode 100644
index 0000000000..d6d9da718b
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11merge.c
@@ -0,0 +1,1437 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Merge the source token into the target token.
+ */
+
+#include "secmod.h"
+#include "secmodi.h"
+#include "secmodti.h"
+#include "pk11pub.h"
+#include "pk11priv.h"
+#include "pkcs11.h"
+#include "seccomon.h"
+#include "secerr.h"
+#include "keyhi.h"
+#include "hasht.h"
+#include "cert.h"
+#include "certdb.h"
+
+/*************************************************************************
+ *
+ * short utilities to aid in the merge
+ *
+ *************************************************************************/
+
+/*
+ * write a bunch of attributes out to an existing object.
+ */
+static SECStatus
+pk11_setAttributes(PK11SlotInfo *slot, CK_OBJECT_HANDLE id,
+ CK_ATTRIBUTE *setTemplate, CK_ULONG setTemplCount)
+{
+ CK_RV crv;
+ CK_SESSION_HANDLE rwsession;
+
+ rwsession = PK11_GetRWSession(slot);
+ if (rwsession == CK_INVALID_HANDLE) {
+ PORT_SetError(SEC_ERROR_BAD_DATA);
+ return SECFailure;
+ }
+ crv = PK11_GETTAB(slot)->C_SetAttributeValue(rwsession, id,
+ setTemplate, setTemplCount);
+ PK11_RestoreROSession(slot, rwsession);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/*
+ * copy a template of attributes from a source object to a target object.
+ * if target object is not given, create it.
+ */
+static SECStatus
+pk11_copyAttributes(PLArenaPool *arena,
+ PK11SlotInfo *targetSlot, CK_OBJECT_HANDLE targetID,
+ PK11SlotInfo *sourceSlot, CK_OBJECT_HANDLE sourceID,
+ CK_ATTRIBUTE *copyTemplate, CK_ULONG copyTemplateCount)
+{
+ SECStatus rv;
+ CK_ATTRIBUTE *newTemplate = NULL;
+ CK_RV crv;
+
+ crv = PK11_GetAttributes(arena, sourceSlot, sourceID,
+ copyTemplate, copyTemplateCount);
+ /* if we have missing attributes, just skip them and create the object */
+ if (crv == CKR_ATTRIBUTE_TYPE_INVALID) {
+ CK_ULONG i, j;
+ newTemplate = PORT_NewArray(CK_ATTRIBUTE, copyTemplateCount);
+ if (!newTemplate) {
+ return SECFailure;
+ }
+ /* remove the unknown attributes. If we don't have enough attributes
+ * PK11_CreateNewObject() will fail */
+ for (i = 0, j = 0; i < copyTemplateCount; i++) {
+ if (copyTemplate[i].ulValueLen != -1) {
+ newTemplate[j] = copyTemplate[i];
+ j++;
+ }
+ }
+ copyTemplate = newTemplate;
+ copyTemplateCount = j;
+ crv = PK11_GetAttributes(arena, sourceSlot, sourceID,
+ copyTemplate, copyTemplateCount);
+ }
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ PORT_Free(newTemplate);
+ return SECFailure;
+ }
+ if (targetID == CK_INVALID_HANDLE) {
+ /* we need to create the object */
+ rv = PK11_CreateNewObject(targetSlot, CK_INVALID_HANDLE,
+ copyTemplate, copyTemplateCount, PR_TRUE, &targetID);
+ } else {
+ /* update the existing object with the new attributes */
+ rv = pk11_setAttributes(targetSlot, targetID,
+ copyTemplate, copyTemplateCount);
+ }
+ if (newTemplate) {
+ PORT_Free(newTemplate);
+ }
+ return rv;
+}
+
+/*
+ * look for a matching object across tokens.
+ */
+static SECStatus
+pk11_matchAcrossTokens(PLArenaPool *arena, PK11SlotInfo *targetSlot,
+ PK11SlotInfo *sourceSlot,
+ CK_ATTRIBUTE *template, CK_ULONG tsize,
+ CK_OBJECT_HANDLE id, CK_OBJECT_HANDLE *peer)
+{
+
+ CK_RV crv;
+ *peer = CK_INVALID_HANDLE;
+
+ crv = PK11_GetAttributes(arena, sourceSlot, id, template, tsize);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ goto loser;
+ }
+
+ if (template[0].ulValueLen == -1) {
+ crv = CKR_ATTRIBUTE_TYPE_INVALID;
+ PORT_SetError(PK11_MapError(crv));
+ goto loser;
+ }
+
+ *peer = pk11_FindObjectByTemplate(targetSlot, template, tsize);
+ return SECSuccess;
+
+loser:
+ return SECFailure;
+}
+
+/*
+ * Encrypt using key and parameters
+ */
+SECStatus
+pk11_encrypt(PK11SymKey *symKey, CK_MECHANISM_TYPE mechType, SECItem *param,
+ SECItem *input, SECItem **output)
+{
+ PK11Context *ctxt = NULL;
+ SECStatus rv = SECSuccess;
+
+ if (*output) {
+ SECITEM_FreeItem(*output, PR_TRUE);
+ }
+ *output = SECITEM_AllocItem(NULL, NULL, input->len + 20 /*slop*/);
+ if (!*output) {
+ rv = SECFailure;
+ goto done;
+ }
+
+ ctxt = PK11_CreateContextBySymKey(mechType, CKA_ENCRYPT, symKey, param);
+ if (ctxt == NULL) {
+ rv = SECFailure;
+ goto done;
+ }
+
+ rv = PK11_CipherOp(ctxt, (*output)->data,
+ (int *)&((*output)->len),
+ (*output)->len, input->data, input->len);
+
+done:
+ if (ctxt) {
+ PK11_Finalize(ctxt);
+ PK11_DestroyContext(ctxt, PR_TRUE);
+ }
+ if (rv != SECSuccess) {
+ if (*output) {
+ SECITEM_FreeItem(*output, PR_TRUE);
+ *output = NULL;
+ }
+ }
+ return rv;
+}
+
+/*************************************************************************
+ *
+ * Private Keys
+ *
+ *************************************************************************/
+
+/*
+ * Fetch the key usage based on the pkcs #11 flags
+ */
+unsigned int
+pk11_getPrivateKeyUsage(PK11SlotInfo *slot, CK_OBJECT_HANDLE id)
+{
+ unsigned int usage = 0;
+
+ if ((PK11_HasAttributeSet(slot, id, CKA_UNWRAP, PR_FALSE) ||
+ PK11_HasAttributeSet(slot, id, CKA_DECRYPT, PR_FALSE))) {
+ usage |= KU_KEY_ENCIPHERMENT;
+ }
+ if (PK11_HasAttributeSet(slot, id, CKA_DERIVE, PR_FALSE)) {
+ usage |= KU_KEY_AGREEMENT;
+ }
+ if ((PK11_HasAttributeSet(slot, id, CKA_SIGN_RECOVER, PR_FALSE) ||
+ PK11_HasAttributeSet(slot, id, CKA_SIGN, PR_FALSE))) {
+ usage |= KU_DIGITAL_SIGNATURE;
+ }
+ return usage;
+}
+
+/*
+ * merge a private key,
+ *
+ * Private keys are merged using PBE wrapped keys with a random
+ * value as the 'password'. Once the base key is moved, The remaining
+ * attributes (SUBJECT) is copied.
+ */
+static SECStatus
+pk11_mergePrivateKey(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
+ CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg)
+{
+ SECKEYPrivateKey *sourceKey = NULL;
+ CK_OBJECT_HANDLE targetKeyID;
+ SECKEYEncryptedPrivateKeyInfo *epki = NULL;
+ char *nickname = NULL;
+ SECItem nickItem;
+ SECItem pwitem;
+ SECItem publicValue;
+ PLArenaPool *arena = NULL;
+ SECStatus rv = SECSuccess;
+ unsigned int keyUsage;
+ unsigned char randomData[SHA1_LENGTH];
+ SECOidTag algTag = SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC;
+ CK_ATTRIBUTE privTemplate[] = {
+ { CKA_ID, NULL, 0 },
+ { CKA_CLASS, NULL, 0 }
+ };
+ CK_ULONG privTemplateCount = sizeof(privTemplate) / sizeof(privTemplate[0]);
+ CK_ATTRIBUTE privCopyTemplate[] = {
+ { CKA_SUBJECT, NULL, 0 }
+ };
+ CK_ULONG privCopyTemplateCount =
+ sizeof(privCopyTemplate) / sizeof(privCopyTemplate[0]);
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ rv = SECFailure;
+ goto done;
+ }
+
+ /* check to see if the key is already in the target slot */
+ rv = pk11_matchAcrossTokens(arena, targetSlot, sourceSlot, privTemplate,
+ privTemplateCount, id, &targetKeyID);
+ if (rv != SECSuccess) {
+ goto done;
+ }
+
+ if (targetKeyID != CK_INVALID_HANDLE) {
+ /* match found, not an error ... */
+ goto done;
+ }
+
+ /* get an NSS representation of our source key */
+ sourceKey = PK11_MakePrivKey(sourceSlot, nullKey, PR_FALSE,
+ id, sourcePwArg);
+ if (sourceKey == NULL) {
+ rv = SECFailure;
+ goto done;
+ }
+
+ /* Load the private key */
+ /* generate a random pwitem */
+ rv = PK11_GenerateRandom(randomData, sizeof(randomData));
+ if (rv != SECSuccess) {
+ goto done;
+ }
+ pwitem.data = randomData;
+ pwitem.len = sizeof(randomData);
+ /* fetch the private key encrypted */
+ epki = PK11_ExportEncryptedPrivKeyInfo(sourceSlot, algTag, &pwitem,
+ sourceKey, 1, sourcePwArg);
+ if (epki == NULL) {
+ rv = SECFailure;
+ goto done;
+ }
+ nickname = PK11_GetObjectNickname(sourceSlot, id);
+ /* NULL nickanme is fine (in fact is often normal) */
+ if (nickname) {
+ nickItem.data = (unsigned char *)nickname;
+ nickItem.len = PORT_Strlen(nickname);
+ }
+ keyUsage = pk11_getPrivateKeyUsage(sourceSlot, id);
+ /* pass in the CKA_ID */
+ publicValue.data = privTemplate[0].pValue;
+ publicValue.len = privTemplate[0].ulValueLen;
+ rv = PK11_ImportEncryptedPrivateKeyInfo(targetSlot, epki, &pwitem,
+ nickname ? &nickItem : NULL, &publicValue,
+ PR_TRUE, PR_TRUE, sourceKey->keyType, keyUsage,
+ targetPwArg);
+ if (rv != SECSuccess) {
+ goto done;
+ }
+
+ /* make sure it made it */
+ rv = pk11_matchAcrossTokens(arena, targetSlot, sourceSlot, privTemplate,
+ privTemplateCount, id, &targetKeyID);
+ if (rv != SECSuccess) {
+ goto done;
+ }
+
+ if (targetKeyID == CK_INVALID_HANDLE) {
+ /* this time the key should exist */
+ rv = SECFailure;
+ goto done;
+ }
+
+ /* fill in remaining attributes */
+ rv = pk11_copyAttributes(arena, targetSlot, targetKeyID, sourceSlot, id,
+ privCopyTemplate, privCopyTemplateCount);
+done:
+ /* make sure the 'key' is cleared */
+ PORT_Memset(randomData, 0, sizeof(randomData));
+ if (nickname) {
+ PORT_Free(nickname);
+ }
+ if (sourceKey) {
+ SECKEY_DestroyPrivateKey(sourceKey);
+ }
+ if (epki) {
+ SECKEY_DestroyEncryptedPrivateKeyInfo(epki, PR_TRUE);
+ }
+ if (arena) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ return rv;
+}
+
+/*************************************************************************
+ *
+ * Secret Keys
+ *
+ *************************************************************************/
+
+/*
+ * we need to find a unique CKA_ID.
+ * The basic idea is to just increment the lowest byte.
+ * This code also handles the following corner cases:
+ * 1) the single byte overflows. On overflow we increment the next byte up
+ * and so forth until we have overflowed the entire CKA_ID.
+ * 2) If we overflow the entire CKA_ID we expand it by one byte.
+ * 3) the CKA_ID is non-existent, we create a new one with one byte.
+ * This means no matter what CKA_ID is passed, the result of this function
+ * is always a new CKA_ID, and this function will never return the same
+ * CKA_ID the it has returned in the passed.
+ */
+static SECStatus
+pk11_incrementID(PLArenaPool *arena, CK_ATTRIBUTE *ptemplate)
+{
+ unsigned char *buf = ptemplate->pValue;
+ CK_ULONG len = ptemplate->ulValueLen;
+
+ if (buf == NULL || len == (CK_ULONG)-1) {
+ /* we have no valid CKAID, we'll create a basic one byte CKA_ID below */
+ len = 0;
+ } else {
+ CK_ULONG i;
+
+ /* walk from the back to front, incrementing
+ * the CKA_ID until we no longer have a carry,
+ * or have hit the front of the id. */
+ for (i = len; i != 0; i--) {
+ buf[i - 1]++;
+ if (buf[i - 1] != 0) {
+ /* no more carries, the increment is complete */
+ return SECSuccess;
+ }
+ }
+ /* we've now overflowed, fall through and expand the CKA_ID by
+ * one byte */
+ }
+ /* if we are here we've run the counter to zero (indicating an overflow).
+ * create an CKA_ID that is all zeros, but has one more zero than
+ * the previous CKA_ID */
+ buf = PORT_ArenaZAlloc(arena, len + 1);
+ if (buf == NULL) {
+ return SECFailure;
+ }
+ ptemplate->pValue = buf;
+ ptemplate->ulValueLen = len + 1;
+ return SECSuccess;
+}
+
+static CK_FLAGS
+pk11_getSecretKeyFlags(PK11SlotInfo *slot, CK_OBJECT_HANDLE id)
+{
+ CK_FLAGS flags = 0;
+
+ if (PK11_HasAttributeSet(slot, id, CKA_UNWRAP, PR_FALSE)) {
+ flags |= CKF_UNWRAP;
+ }
+ if (PK11_HasAttributeSet(slot, id, CKA_WRAP, PR_FALSE)) {
+ flags |= CKF_WRAP;
+ }
+ if (PK11_HasAttributeSet(slot, id, CKA_ENCRYPT, PR_FALSE)) {
+ flags |= CKF_ENCRYPT;
+ }
+ if (PK11_HasAttributeSet(slot, id, CKA_DECRYPT, PR_FALSE)) {
+ flags |= CKF_DECRYPT;
+ }
+ if (PK11_HasAttributeSet(slot, id, CKA_DERIVE, PR_FALSE)) {
+ flags |= CKF_DERIVE;
+ }
+ if (PK11_HasAttributeSet(slot, id, CKA_SIGN, PR_FALSE)) {
+ flags |= CKF_SIGN;
+ }
+ if (PK11_HasAttributeSet(slot, id, CKA_SIGN_RECOVER, PR_FALSE)) {
+ flags |= CKF_SIGN_RECOVER;
+ }
+ if (PK11_HasAttributeSet(slot, id, CKA_VERIFY, PR_FALSE)) {
+ flags |= CKF_VERIFY;
+ }
+ if (PK11_HasAttributeSet(slot, id, CKA_VERIFY_RECOVER, PR_FALSE)) {
+ flags |= CKF_VERIFY_RECOVER;
+ }
+ return flags;
+}
+
+static const char testString[] =
+ "My Encrytion Test Data (should be at least 32 bytes long)";
+/*
+ * merge a secret key,
+ *
+ * Secret keys may collide by CKA_ID as we merge 2 token. If we collide
+ * on the CKA_ID, we need to make sure we are dealing with different keys.
+ * The reason for this is it is possible that we've merged this database
+ * before, and this key could have been merged already. If the keys are
+ * the same, we are done. If they are not, we need to update the CKA_ID of
+ * the source key and try again.
+ *
+ * Once we know we have a unique key to merge in, we use NSS's underlying
+ * key Move function which will do a key exchange if necessary to move
+ * the key from one token to another. Then we set the CKA_ID and additional
+ * pkcs #11 attributes.
+ */
+static SECStatus
+pk11_mergeSecretKey(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
+ CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg)
+{
+ PK11SymKey *sourceKey = NULL;
+ PK11SymKey *targetKey = NULL;
+ SECItem *sourceOutput = NULL;
+ SECItem *targetOutput = NULL;
+ SECItem *param = NULL;
+ int blockSize;
+ SECItem input;
+ CK_OBJECT_HANDLE targetKeyID;
+ CK_FLAGS flags;
+ PLArenaPool *arena = NULL;
+ SECStatus rv = SECSuccess;
+ CK_MECHANISM_TYPE keyMechType, cryptoMechType;
+ CK_KEY_TYPE sourceKeyType, targetKeyType;
+ CK_ATTRIBUTE symTemplate[] = {
+ { CKA_ID, NULL, 0 },
+ { CKA_CLASS, NULL, 0 }
+ };
+ const CK_ULONG symTemplateCount = sizeof(symTemplate) / sizeof(symTemplate[0]);
+ CK_ATTRIBUTE symCopyTemplate[] = {
+ { CKA_LABEL, NULL, 0 }
+ };
+ CK_ULONG symCopyTemplateCount =
+ sizeof(symCopyTemplate) / sizeof(symCopyTemplate[0]);
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ rv = SECFailure;
+ goto done;
+ }
+
+ sourceKeyType = PK11_ReadULongAttribute(sourceSlot, id, CKA_KEY_TYPE);
+ if (sourceKeyType == (CK_ULONG)-1) {
+ rv = SECFailure;
+ goto done;
+ }
+
+ /* get the key mechanism */
+ keyMechType = PK11_GetKeyMechanism(sourceKeyType);
+ /* get a mechanism suitable to encryption.
+ * PK11_GetKeyMechanism returns a mechanism that is unique to the key
+ * type. It tries to return encryption/decryption mechanisms, however
+ * CKM_DES3_CBC uses and abmiguous keyType, so keyMechType is returned as
+ * 'keygen' mechanism. Detect that case here */
+ cryptoMechType = keyMechType;
+ if ((keyMechType == CKM_DES3_KEY_GEN) ||
+ (keyMechType == CKM_DES2_KEY_GEN)) {
+ cryptoMechType = CKM_DES3_CBC;
+ }
+
+ sourceKey = PK11_SymKeyFromHandle(sourceSlot, NULL, PK11_OriginDerive,
+ keyMechType, id, PR_FALSE, sourcePwArg);
+ if (sourceKey == NULL) {
+ rv = SECFailure;
+ goto done;
+ }
+
+ /* check to see a key with the same CKA_ID already exists in
+ * the target slot. If it does, then we need to verify if the keys
+ * really matches. If they don't import the key with a new CKA_ID
+ * value. */
+ rv = pk11_matchAcrossTokens(arena, targetSlot, sourceSlot,
+ symTemplate, symTemplateCount, id, &targetKeyID);
+ if (rv != SECSuccess) {
+ goto done;
+ }
+
+ /* set up the input test */
+ input.data = (unsigned char *)testString;
+ blockSize = PK11_GetBlockSize(cryptoMechType, NULL);
+ if (blockSize < 0) {
+ rv = SECFailure;
+ goto done;
+ }
+ input.len = blockSize;
+ if (input.len == 0) {
+ input.len = sizeof(testString);
+ }
+ while (targetKeyID != CK_INVALID_HANDLE) {
+ /* test to see if the keys are identical */
+ targetKeyType = PK11_ReadULongAttribute(sourceSlot, id, CKA_KEY_TYPE);
+ if (targetKeyType == sourceKeyType) {
+ /* same keyType - see if it's the same key */
+ targetKey = PK11_SymKeyFromHandle(targetSlot, NULL,
+ PK11_OriginDerive, keyMechType, targetKeyID, PR_FALSE,
+ targetPwArg);
+ /* get a parameter if we don't already have one */
+ if (!param) {
+ param = PK11_GenerateNewParam(cryptoMechType, sourceKey);
+ if (param == NULL) {
+ rv = SECFailure;
+ goto done;
+ }
+ }
+ /* use the source key to encrypt a reference */
+ if (!sourceOutput) {
+ rv = pk11_encrypt(sourceKey, cryptoMechType, param, &input,
+ &sourceOutput);
+ if (rv != SECSuccess) {
+ goto done;
+ }
+ }
+ /* encrypt the reference with the target key */
+ rv = pk11_encrypt(targetKey, cryptoMechType, param, &input,
+ &targetOutput);
+ if (rv == SECSuccess) {
+ if (SECITEM_ItemsAreEqual(sourceOutput, targetOutput)) {
+ /* they produce the same output, they must be the
+ * same key */
+ goto done;
+ }
+ SECITEM_FreeItem(targetOutput, PR_TRUE);
+ targetOutput = NULL;
+ }
+ PK11_FreeSymKey(targetKey);
+ targetKey = NULL;
+ }
+ /* keys aren't equal, update the KEY_ID and look again */
+ rv = pk11_incrementID(arena, &symTemplate[0]);
+ if (rv != SECSuccess) {
+ goto done;
+ }
+ targetKeyID = pk11_FindObjectByTemplate(targetSlot,
+ symTemplate, symTemplateCount);
+ }
+
+ /* we didn't find a matching key, import this one with the new
+ * CKAID */
+ flags = pk11_getSecretKeyFlags(sourceSlot, id);
+ targetKey = PK11_MoveSymKey(targetSlot, PK11_OriginDerive, flags, PR_TRUE,
+ sourceKey);
+ if (targetKey == NULL) {
+ rv = SECFailure;
+ goto done;
+ }
+ /* set the key new CKAID */
+ rv = pk11_setAttributes(targetSlot, targetKey->objectID, symTemplate, 1);
+ if (rv != SECSuccess) {
+ goto done;
+ }
+
+ /* fill in remaining attributes */
+ rv = pk11_copyAttributes(arena, targetSlot, targetKey->objectID,
+ sourceSlot, id, symCopyTemplate, symCopyTemplateCount);
+done:
+ if (sourceKey) {
+ PK11_FreeSymKey(sourceKey);
+ }
+ if (targetKey) {
+ PK11_FreeSymKey(targetKey);
+ }
+ if (sourceOutput) {
+ SECITEM_FreeItem(sourceOutput, PR_TRUE);
+ }
+ if (targetOutput) {
+ SECITEM_FreeItem(targetOutput, PR_TRUE);
+ }
+ if (param) {
+ SECITEM_FreeItem(param, PR_TRUE);
+ }
+ if (arena) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ return rv;
+}
+
+/*************************************************************************
+ *
+ * Public Keys
+ *
+ *************************************************************************/
+
+/*
+ * Merge public key
+ *
+ * Use the high level NSS calls to extract the public key and import it
+ * into the token. Extra attributes are then copied to the new token.
+ */
+static SECStatus
+pk11_mergePublicKey(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
+ CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg)
+{
+ SECKEYPublicKey *sourceKey = NULL;
+ CK_OBJECT_HANDLE targetKeyID;
+ PLArenaPool *arena = NULL;
+ SECStatus rv = SECSuccess;
+ CK_ATTRIBUTE pubTemplate[] = {
+ { CKA_ID, NULL, 0 },
+ { CKA_CLASS, NULL, 0 }
+ };
+ CK_ULONG pubTemplateCount = sizeof(pubTemplate) / sizeof(pubTemplate[0]);
+ CK_ATTRIBUTE pubCopyTemplate[] = {
+ { CKA_ID, NULL, 0 },
+ { CKA_LABEL, NULL, 0 },
+ { CKA_SUBJECT, NULL, 0 }
+ };
+ CK_ULONG pubCopyTemplateCount =
+ sizeof(pubCopyTemplate) / sizeof(pubCopyTemplate[0]);
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ rv = SECFailure;
+ goto done;
+ }
+
+ /* check to see if the key is already in the target slot */
+ rv = pk11_matchAcrossTokens(arena, targetSlot, sourceSlot, pubTemplate,
+ pubTemplateCount, id, &targetKeyID);
+ if (rv != SECSuccess) {
+ goto done;
+ }
+
+ /* Key is already in the target slot */
+ if (targetKeyID != CK_INVALID_HANDLE) {
+ /* not an error ... */
+ goto done;
+ }
+
+ /* fetch an NSS representation of the public key */
+ sourceKey = PK11_ExtractPublicKey(sourceSlot, nullKey, id);
+ if (sourceKey == NULL) {
+ rv = SECFailure;
+ goto done;
+ }
+
+ /* load the public key into the target token. */
+ targetKeyID = PK11_ImportPublicKey(targetSlot, sourceKey, PR_TRUE);
+ if (targetKeyID == CK_INVALID_HANDLE) {
+ rv = SECFailure;
+ goto done;
+ }
+
+ /* fill in remaining attributes */
+ rv = pk11_copyAttributes(arena, targetSlot, targetKeyID, sourceSlot, id,
+ pubCopyTemplate, pubCopyTemplateCount);
+
+done:
+ if (sourceKey) {
+ SECKEY_DestroyPublicKey(sourceKey);
+ }
+ if (arena) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ return rv;
+}
+
+/*************************************************************************
+ *
+ * Certificates
+ *
+ *************************************************************************/
+
+/*
+ * Two copies of the source code for this algorithm exist in NSS.
+ * Changes must be made in both copies.
+ * The other copy is in sftkdb_resolveConflicts() in softoken/sftkdb.c.
+ */
+static char *
+pk11_IncrementNickname(char *nickname)
+{
+ char *newNickname = NULL;
+ int end;
+ int digit;
+ int len = strlen(nickname);
+
+ /* does nickname end with " #n*" ? */
+ for (end = len - 1;
+ end >= 2 && (digit = nickname[end]) <= '9' && digit >= '0';
+ end--) /* just scan */
+ ;
+ if (len >= 3 &&
+ end < (len - 1) /* at least one digit */ &&
+ nickname[end] == '#' &&
+ nickname[end - 1] == ' ') {
+ /* Already has a suitable suffix string */
+ } else {
+ /* ... append " #2" to the name */
+ static const char num2[] = " #2";
+ newNickname = PORT_Realloc(nickname, len + sizeof(num2));
+ if (newNickname) {
+ PORT_Strcat(newNickname, num2);
+ } else {
+ PORT_Free(nickname);
+ }
+ return newNickname;
+ }
+
+ for (end = len - 1;
+ end >= 0 && (digit = nickname[end]) <= '9' && digit >= '0';
+ end--) {
+ if (digit < '9') {
+ nickname[end]++;
+ return nickname;
+ }
+ nickname[end] = '0';
+ }
+
+ /* we overflowed, insert a new '1' for a carry in front of the number */
+ newNickname = PORT_Realloc(nickname, len + 2);
+ if (newNickname) {
+ newNickname[++end] = '1';
+ PORT_Memset(&newNickname[end + 1], '0', len - end);
+ newNickname[len + 1] = 0;
+ } else {
+ PORT_Free(nickname);
+ }
+ return newNickname;
+}
+
+/*
+ * merge a certificate object
+ *
+ * Use the high level NSS calls to extract and import the certificate.
+ */
+static SECStatus
+pk11_mergeCert(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
+ CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg)
+{
+ CERTCertificate *sourceCert = NULL;
+ CK_OBJECT_HANDLE targetCertID = CK_INVALID_HANDLE;
+ char *nickname = NULL;
+ SECStatus rv = SECSuccess;
+ PLArenaPool *arena = NULL;
+ CK_ATTRIBUTE sourceCKAID = { CKA_ID, NULL, 0 };
+ CK_ATTRIBUTE targetCKAID = { CKA_ID, NULL, 0 };
+ SECStatus lrv = SECSuccess;
+ int error = SEC_ERROR_LIBRARY_FAILURE;
+
+ sourceCert = PK11_MakeCertFromHandle(sourceSlot, id, NULL);
+ if (sourceCert == NULL) {
+ rv = SECFailure;
+ goto done;
+ }
+
+ nickname = PK11_GetObjectNickname(sourceSlot, id);
+
+ /* The database code will prevent nickname collisions for certs with
+ * different subjects. This code will prevent us from getting
+ * actual import errors */
+ if (nickname) {
+ const char *tokenName = PK11_GetTokenName(targetSlot);
+ char *tokenNickname = NULL;
+
+ do {
+ tokenNickname = PR_smprintf("%s:%s", tokenName, nickname);
+ if (!tokenNickname) {
+ break;
+ }
+ if (!SEC_CertNicknameConflict(tokenNickname,
+ &sourceCert->derSubject, CERT_GetDefaultCertDB())) {
+ break;
+ }
+ nickname = pk11_IncrementNickname(nickname);
+ if (!nickname) {
+ break;
+ }
+ PR_smprintf_free(tokenNickname);
+ } while (1);
+ if (tokenNickname) {
+ PR_smprintf_free(tokenNickname);
+ }
+ }
+
+ /* see if the cert is already there */
+ targetCertID = PK11_FindCertInSlot(targetSlot, sourceCert, targetPwArg);
+ if (targetCertID == CK_INVALID_HANDLE) {
+ /* cert doesn't exist load the cert in. */
+ /* OK for the nickname to be NULL, not all certs have nicknames */
+ rv = PK11_ImportCert(targetSlot, sourceCert, CK_INVALID_HANDLE,
+ nickname, PR_FALSE);
+ goto done;
+ }
+
+ /* the cert already exists, see if the nickname and/or CKA_ID need
+ * to be updated */
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ rv = SECFailure;
+ goto done;
+ }
+
+ /* does our source have a CKA_ID ? */
+ rv = PK11_GetAttributes(arena, sourceSlot, id, &sourceCKAID, 1);
+ if (rv != SECSuccess) {
+ sourceCKAID.ulValueLen = 0;
+ }
+
+ /* if we have a source CKA_ID, see of we need to update the
+ * target's CKA_ID */
+ if (sourceCKAID.ulValueLen != 0) {
+ rv = PK11_GetAttributes(arena, targetSlot, targetCertID,
+ &targetCKAID, 1);
+ if (rv != SECSuccess) {
+ targetCKAID.ulValueLen = 0;
+ }
+ /* if the target has no CKA_ID, update it from the source */
+ if (targetCKAID.ulValueLen == 0) {
+ lrv = pk11_setAttributes(targetSlot, targetCertID, &sourceCKAID, 1);
+ if (lrv != SECSuccess) {
+ error = PORT_GetError();
+ }
+ }
+ }
+ rv = SECSuccess;
+
+ /* now check if we need to update the nickname */
+ if (nickname && *nickname) {
+ char *targetname;
+ targetname = PK11_GetObjectNickname(targetSlot, targetCertID);
+ if (!targetname || !*targetname) {
+ /* target has no nickname, or it's empty, update it */
+ rv = PK11_SetObjectNickname(targetSlot, targetCertID, nickname);
+ }
+ if (targetname) {
+ PORT_Free(targetname);
+ }
+ }
+
+ /* restore the error code if CKA_ID failed, but nickname didn't */
+ if ((rv == SECSuccess) && (lrv != SECSuccess)) {
+ rv = lrv;
+ PORT_SetError(error);
+ }
+
+done:
+ if (nickname) {
+ PORT_Free(nickname);
+ }
+ if (sourceCert) {
+ CERT_DestroyCertificate(sourceCert);
+ }
+ if (arena) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ return rv;
+}
+
+/*************************************************************************
+ *
+ * Crls
+ *
+ *************************************************************************/
+
+/*
+ * Use the raw PKCS #11 interface to merge the CRLs.
+ *
+ * In the case where of collision, choose the newest CRL that is valid.
+ */
+static SECStatus
+pk11_mergeCrl(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
+ CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg)
+{
+ CK_OBJECT_HANDLE targetCrlID;
+ PLArenaPool *arena = NULL;
+ SECStatus rv = SECSuccess;
+ CK_ATTRIBUTE crlTemplate[] = {
+ { CKA_SUBJECT, NULL, 0 },
+ { CKA_CLASS, NULL, 0 },
+ { CKA_NSS_KRL, NULL, 0 }
+ };
+ CK_ULONG crlTemplateCount = sizeof(crlTemplate) / sizeof(crlTemplate[0]);
+ CK_ATTRIBUTE crlCopyTemplate[] = {
+ { CKA_CLASS, NULL, 0 },
+ { CKA_TOKEN, NULL, 0 },
+ { CKA_LABEL, NULL, 0 },
+ { CKA_PRIVATE, NULL, 0 },
+ { CKA_MODIFIABLE, NULL, 0 },
+ { CKA_SUBJECT, NULL, 0 },
+ { CKA_NSS_KRL, NULL, 0 },
+ { CKA_NSS_URL, NULL, 0 },
+ { CKA_VALUE, NULL, 0 }
+ };
+ CK_ULONG crlCopyTemplateCount =
+ sizeof(crlCopyTemplate) / sizeof(crlCopyTemplate[0]);
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ rv = SECFailure;
+ goto done;
+ }
+ /* check to see if the crl is already in the target slot */
+ rv = pk11_matchAcrossTokens(arena, targetSlot, sourceSlot, crlTemplate,
+ crlTemplateCount, id, &targetCrlID);
+ if (rv != SECSuccess) {
+ goto done;
+ }
+ if (targetCrlID != CK_INVALID_HANDLE) {
+ /* we already have a CRL, check to see which is more up-to-date. */
+ goto done;
+ }
+
+ /* load the CRL into the target token. */
+ rv = pk11_copyAttributes(arena, targetSlot, targetCrlID, sourceSlot, id,
+ crlCopyTemplate, crlCopyTemplateCount);
+done:
+ if (arena) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ return rv;
+}
+
+/*************************************************************************
+ *
+ * SMIME objects
+ *
+ *************************************************************************/
+
+/*
+ * use the raw PKCS #11 interface to merge the S/MIME records
+ */
+static SECStatus
+pk11_mergeSmime(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
+ CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg)
+{
+ CK_OBJECT_HANDLE targetSmimeID;
+ PLArenaPool *arena = NULL;
+ SECStatus rv = SECSuccess;
+ CK_ATTRIBUTE smimeTemplate[] = {
+ { CKA_SUBJECT, NULL, 0 },
+ { CKA_NSS_EMAIL, NULL, 0 },
+ { CKA_CLASS, NULL, 0 },
+ };
+ CK_ULONG smimeTemplateCount =
+ sizeof(smimeTemplate) / sizeof(smimeTemplate[0]);
+ CK_ATTRIBUTE smimeCopyTemplate[] = {
+ { CKA_CLASS, NULL, 0 },
+ { CKA_TOKEN, NULL, 0 },
+ { CKA_LABEL, NULL, 0 },
+ { CKA_PRIVATE, NULL, 0 },
+ { CKA_MODIFIABLE, NULL, 0 },
+ { CKA_SUBJECT, NULL, 0 },
+ { CKA_NSS_EMAIL, NULL, 0 },
+ { CKA_NSS_SMIME_TIMESTAMP, NULL, 0 },
+ { CKA_VALUE, NULL, 0 }
+ };
+ CK_ULONG smimeCopyTemplateCount =
+ sizeof(smimeCopyTemplate) / sizeof(smimeCopyTemplate[0]);
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ rv = SECFailure;
+ goto done;
+ }
+ /* check to see if the crl is already in the target slot */
+ rv = pk11_matchAcrossTokens(arena, targetSlot, sourceSlot, smimeTemplate,
+ smimeTemplateCount, id, &targetSmimeID);
+ if (rv != SECSuccess) {
+ goto done;
+ }
+ if (targetSmimeID != CK_INVALID_HANDLE) {
+ /* we already have a SMIME record */
+ goto done;
+ }
+
+ /* load the SMime Record into the target token. */
+ rv = pk11_copyAttributes(arena, targetSlot, targetSmimeID, sourceSlot, id,
+ smimeCopyTemplate, smimeCopyTemplateCount);
+done:
+ if (arena) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ return rv;
+}
+
+/*************************************************************************
+ *
+ * Trust Objects
+ *
+ *************************************************************************/
+
+/*
+ * decide which trust record entry wins. PR_TRUE (source) or PR_FALSE (target)
+ */
+#define USE_TARGET PR_FALSE
+#define USE_SOURCE PR_TRUE
+PRBool
+pk11_mergeTrustEntry(CK_ATTRIBUTE *target, CK_ATTRIBUTE *source)
+{
+ CK_ULONG targetTrust = (target->ulValueLen == sizeof(CK_LONG)) ? *(CK_ULONG *)target->pValue
+ : CKT_NSS_TRUST_UNKNOWN;
+ CK_ULONG sourceTrust = (source->ulValueLen == sizeof(CK_LONG)) ? *(CK_ULONG *)source->pValue
+ : CKT_NSS_TRUST_UNKNOWN;
+
+ /*
+ * Examine a single entry and deside if the source or target version
+ * should win out. When all the entries have been checked, if there is
+ * any case we need to update, we will write the whole source record
+ * to the target database. That means for each individual record, if the
+ * target wins, we need to update the source (in case later we have a
+ * case where the source wins). If the source wins, it already
+ */
+ if (sourceTrust == targetTrust) {
+ return USE_TARGET; /* which equates to 'do nothing' */
+ }
+
+ if (sourceTrust == CKT_NSS_TRUST_UNKNOWN) {
+ return USE_TARGET;
+ }
+
+ /* target has no idea, use the source's idea of the trust value */
+ if (targetTrust == CKT_NSS_TRUST_UNKNOWN) {
+ /* source overwrites the target */
+ return USE_SOURCE;
+ }
+
+ /* so both the target and the source have some idea of what this
+ * trust attribute should be, and neither agree exactly.
+ * At this point, we prefer 'hard' attributes over 'soft' ones.
+ * 'hard' ones are CKT_NSS_TRUSTED, CKT_NSS_TRUSTED_DELEGATOR, and
+ * CKT_NSS_UNTRUTED. Soft ones are ones which don't change the
+ * actual trust of the cert (CKT_MUST_VERIFY, CKT_NSS_VALID,
+ * CKT_NSS_VALID_DELEGATOR).
+ */
+ if ((sourceTrust == CKT_NSS_MUST_VERIFY_TRUST) ||
+ (sourceTrust == CKT_NSS_VALID_DELEGATOR)) {
+ return USE_TARGET;
+ }
+ if ((targetTrust == CKT_NSS_MUST_VERIFY_TRUST) ||
+ (targetTrust == CKT_NSS_VALID_DELEGATOR)) {
+ /* source overrites the target */
+ return USE_SOURCE;
+ }
+
+ /* both have hard attributes, we have a conflict, let the target win. */
+ return USE_TARGET;
+}
+/*
+ * use the raw PKCS #11 interface to merge the S/MIME records
+ */
+static SECStatus
+pk11_mergeTrust(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
+ CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg)
+{
+ CK_OBJECT_HANDLE targetTrustID;
+ PLArenaPool *arena = NULL;
+ SECStatus rv = SECSuccess;
+ int error = 0;
+ CK_ATTRIBUTE trustTemplate[] = {
+ { CKA_ISSUER, NULL, 0 },
+ { CKA_SERIAL_NUMBER, NULL, 0 },
+ { CKA_CLASS, NULL, 0 },
+ };
+ CK_ULONG trustTemplateCount =
+ sizeof(trustTemplate) / sizeof(trustTemplate[0]);
+ CK_ATTRIBUTE trustCopyTemplate[] = {
+ { CKA_CLASS, NULL, 0 },
+ { CKA_TOKEN, NULL, 0 },
+ { CKA_LABEL, NULL, 0 },
+ { CKA_PRIVATE, NULL, 0 },
+ { CKA_MODIFIABLE, NULL, 0 },
+ { CKA_ISSUER, NULL, 0 },
+ { CKA_SERIAL_NUMBER, NULL, 0 },
+ { CKA_CERT_SHA1_HASH, NULL, 0 },
+ { CKA_CERT_MD5_HASH, NULL, 0 },
+ { CKA_TRUST_SERVER_AUTH, NULL, 0 },
+ { CKA_TRUST_CLIENT_AUTH, NULL, 0 },
+ { CKA_TRUST_CODE_SIGNING, NULL, 0 },
+ { CKA_TRUST_EMAIL_PROTECTION, NULL, 0 },
+ { CKA_TRUST_STEP_UP_APPROVED, NULL, 0 }
+ };
+ CK_ULONG trustCopyTemplateCount =
+ sizeof(trustCopyTemplate) / sizeof(trustCopyTemplate[0]);
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ rv = SECFailure;
+ goto done;
+ }
+ /* check to see if the crl is already in the target slot */
+ rv = pk11_matchAcrossTokens(arena, targetSlot, sourceSlot, trustTemplate,
+ trustTemplateCount, id, &targetTrustID);
+ if (rv != SECSuccess) {
+ goto done;
+ }
+ if (targetTrustID != CK_INVALID_HANDLE) {
+ /* a matching trust record already exists, merge it in */
+ CK_ATTRIBUTE_TYPE trustAttrs[] = {
+ CKA_TRUST_SERVER_AUTH, CKA_TRUST_CLIENT_AUTH,
+ CKA_TRUST_CODE_SIGNING, CKA_TRUST_EMAIL_PROTECTION,
+ CKA_TRUST_IPSEC_TUNNEL, CKA_TRUST_IPSEC_USER,
+ CKA_TRUST_TIME_STAMPING
+ };
+ CK_ULONG trustAttrsCount =
+ sizeof(trustAttrs) / sizeof(trustAttrs[0]);
+
+ CK_ULONG i;
+ CK_ATTRIBUTE targetTemplate, sourceTemplate;
+
+ /* existing trust record, merge the two together */
+ for (i = 0; i < trustAttrsCount; i++) {
+ targetTemplate.type = sourceTemplate.type = trustAttrs[i];
+ targetTemplate.pValue = sourceTemplate.pValue = NULL;
+ targetTemplate.ulValueLen = sourceTemplate.ulValueLen = 0;
+ PK11_GetAttributes(arena, sourceSlot, id, &sourceTemplate, 1);
+ PK11_GetAttributes(arena, targetSlot, targetTrustID,
+ &targetTemplate, 1);
+ if (pk11_mergeTrustEntry(&targetTemplate, &sourceTemplate)) {
+ /* source wins, write out the source attribute to the target */
+ SECStatus lrv = pk11_setAttributes(targetSlot, targetTrustID,
+ &sourceTemplate, 1);
+ if (lrv != SECSuccess) {
+ rv = SECFailure;
+ error = PORT_GetError();
+ }
+ }
+ }
+
+ /* handle step */
+ sourceTemplate.type = CKA_TRUST_STEP_UP_APPROVED;
+ sourceTemplate.pValue = NULL;
+ sourceTemplate.ulValueLen = 0;
+
+ /* if the source has steup set, then set it in the target */
+ PK11_GetAttributes(arena, sourceSlot, id, &sourceTemplate, 1);
+ if ((sourceTemplate.ulValueLen == sizeof(CK_BBOOL)) &&
+ (sourceTemplate.pValue) &&
+ (*(CK_BBOOL *)sourceTemplate.pValue == CK_TRUE)) {
+ SECStatus lrv = pk11_setAttributes(targetSlot, targetTrustID,
+ &sourceTemplate, 1);
+ if (lrv != SECSuccess) {
+ rv = SECFailure;
+ error = PORT_GetError();
+ }
+ }
+
+ goto done;
+ }
+
+ /* load the new trust Record into the target token. */
+ rv = pk11_copyAttributes(arena, targetSlot, targetTrustID, sourceSlot, id,
+ trustCopyTemplate, trustCopyTemplateCount);
+done:
+ if (arena) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ /* restore the error code */
+ if (rv == SECFailure && error) {
+ PORT_SetError(error);
+ }
+
+ return rv;
+}
+
+/*************************************************************************
+ *
+ * Central merge code
+ *
+ *************************************************************************/
+/*
+ * merge a single object from sourceToken to targetToken
+ */
+static SECStatus
+pk11_mergeObject(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
+ CK_OBJECT_HANDLE id, void *targetPwArg, void *sourcePwArg)
+{
+
+ CK_OBJECT_CLASS objClass;
+
+ objClass = PK11_ReadULongAttribute(sourceSlot, id, CKA_CLASS);
+ if (objClass == (CK_ULONG)-1) {
+ PORT_SetError(SEC_ERROR_UNKNOWN_OBJECT_TYPE);
+ return SECFailure;
+ }
+
+ switch (objClass) {
+ case CKO_CERTIFICATE:
+ return pk11_mergeCert(targetSlot, sourceSlot, id,
+ targetPwArg, sourcePwArg);
+ case CKO_NSS_TRUST:
+ return pk11_mergeTrust(targetSlot, sourceSlot, id,
+ targetPwArg, sourcePwArg);
+ case CKO_PUBLIC_KEY:
+ return pk11_mergePublicKey(targetSlot, sourceSlot, id,
+ targetPwArg, sourcePwArg);
+ case CKO_PRIVATE_KEY:
+ return pk11_mergePrivateKey(targetSlot, sourceSlot, id,
+ targetPwArg, sourcePwArg);
+ case CKO_SECRET_KEY:
+ return pk11_mergeSecretKey(targetSlot, sourceSlot, id,
+ targetPwArg, sourcePwArg);
+ case CKO_NSS_CRL:
+ return pk11_mergeCrl(targetSlot, sourceSlot, id,
+ targetPwArg, sourcePwArg);
+ case CKO_NSS_SMIME:
+ return pk11_mergeSmime(targetSlot, sourceSlot, id,
+ targetPwArg, sourcePwArg);
+ default:
+ break;
+ }
+
+ PORT_SetError(SEC_ERROR_UNKNOWN_OBJECT_TYPE);
+ return SECFailure;
+}
+
+PK11MergeLogNode *
+pk11_newMergeLogNode(PLArenaPool *arena,
+ PK11SlotInfo *slot, CK_OBJECT_HANDLE id, int error)
+{
+ PK11MergeLogNode *newLog;
+ PK11GenericObject *obj;
+
+ newLog = PORT_ArenaZNew(arena, PK11MergeLogNode);
+ if (newLog == NULL) {
+ return NULL;
+ }
+
+ obj = PORT_ArenaZNew(arena, PK11GenericObject);
+ if (!obj) {
+ return NULL;
+ }
+
+ /* initialize it */
+ obj->slot = slot;
+ obj->objectID = id;
+ obj->owner = PR_FALSE;
+
+ newLog->object = obj;
+ newLog->error = error;
+ return newLog;
+}
+
+/*
+ * walk down each entry and merge it. keep track of the errors in the log
+ */
+static SECStatus
+pk11_mergeByObjectIDs(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
+ CK_OBJECT_HANDLE *objectIDs, int count,
+ PK11MergeLog *log, void *targetPwArg, void *sourcePwArg)
+{
+ SECStatus rv = SECSuccess;
+ int error = SEC_ERROR_LIBRARY_FAILURE;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ /* try to update the entire database. On failure, keep going,
+ * but remember the error to report back to the caller */
+ SECStatus lrv;
+ PK11MergeLogNode *newLog;
+
+ lrv = pk11_mergeObject(targetSlot, sourceSlot, objectIDs[i],
+ targetPwArg, sourcePwArg);
+ if (lrv == SECSuccess) {
+ /* merged with no problem, go to next object */
+ continue;
+ }
+
+ /* remember that we failed and why */
+ rv = SECFailure;
+ error = PORT_GetError();
+
+ /* log the errors */
+ if (!log) {
+ /* not logging, go to next entry */
+ continue;
+ }
+ newLog = pk11_newMergeLogNode(log->arena, sourceSlot,
+ objectIDs[i], error);
+ if (!newLog) {
+ /* failed to allocate entry, just keep going */
+ continue;
+ }
+
+ /* link in the errorlog entry */
+ newLog->next = NULL;
+ if (log->tail) {
+ log->tail->next = newLog;
+ } else {
+ log->head = newLog;
+ }
+ newLog->prev = log->tail;
+ log->tail = newLog;
+ }
+
+ /* restore the last error code */
+ if (rv != SECSuccess) {
+ PORT_SetError(error);
+ }
+ return rv;
+}
+
+/*
+ * Merge all the records in sourceSlot that aren't in targetSlot
+ *
+ * This function will return failure if not all the objects
+ * successfully merged.
+ *
+ * Applications can pass in an optional error log which will record
+ * each failing object and why it failed to import. PK11MergeLog
+ * is modelled after the CERTVerifyLog.
+ */
+SECStatus
+PK11_MergeTokens(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
+ PK11MergeLog *log, void *targetPwArg, void *sourcePwArg)
+{
+ SECStatus rv = SECSuccess, lrv = SECSuccess;
+ int error = SEC_ERROR_LIBRARY_FAILURE;
+ int count = 0;
+ CK_ATTRIBUTE search[2];
+ CK_OBJECT_HANDLE *objectIDs = NULL;
+ CK_BBOOL ck_true = CK_TRUE;
+ CK_OBJECT_CLASS privKey = CKO_PRIVATE_KEY;
+
+ PK11_SETATTRS(&search[0], CKA_TOKEN, &ck_true, sizeof(ck_true));
+ PK11_SETATTRS(&search[1], CKA_CLASS, &privKey, sizeof(privKey));
+ /*
+ * make sure both tokens are already authenticated if need be.
+ */
+ rv = PK11_Authenticate(targetSlot, PR_TRUE, targetPwArg);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ rv = PK11_Authenticate(sourceSlot, PR_TRUE, sourcePwArg);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* turns out the old DB's are rather fragile if the private keys aren't
+ * merged in first, so do the private keys explicity. */
+ objectIDs = pk11_FindObjectsByTemplate(sourceSlot, search, 2, &count);
+ if (objectIDs) {
+ lrv = pk11_mergeByObjectIDs(targetSlot, sourceSlot,
+ objectIDs, count, log,
+ targetPwArg, sourcePwArg);
+ if (lrv != SECSuccess) {
+ error = PORT_GetError();
+ }
+ PORT_Free(objectIDs);
+ count = 0;
+ }
+
+ /* now do the rest (NOTE: this will repeat the private keys, but
+ * that shouldnt' be an issue as we will notice they are already
+ * merged in */
+ objectIDs = pk11_FindObjectsByTemplate(sourceSlot, search, 1, &count);
+ if (!objectIDs) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ rv = pk11_mergeByObjectIDs(targetSlot, sourceSlot, objectIDs, count, log,
+ targetPwArg, sourcePwArg);
+ if (rv == SECSuccess) {
+ /* if private keys failed, but the rest succeeded, be sure to let
+ * the caller know that private keys failed and why.
+ * NOTE: this is highly unlikely since the same keys that failed
+ * in the previous merge call will most likely fail in this one */
+ if (lrv != SECSuccess) {
+ rv = lrv;
+ PORT_SetError(error);
+ }
+ }
+
+loser:
+ if (objectIDs) {
+ PORT_Free(objectIDs);
+ }
+ return rv;
+}
+
+PK11MergeLog *
+PK11_CreateMergeLog(void)
+{
+ PLArenaPool *arena;
+ PK11MergeLog *log;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ return NULL;
+ }
+
+ log = PORT_ArenaZNew(arena, PK11MergeLog);
+ if (log == NULL) {
+ PORT_FreeArena(arena, PR_FALSE);
+ return NULL;
+ }
+ log->arena = arena;
+ log->version = 1;
+ return log;
+}
+
+void
+PK11_DestroyMergeLog(PK11MergeLog *log)
+{
+ if (log && log->arena) {
+ PORT_FreeArena(log->arena, PR_FALSE);
+ }
+}
diff --git a/security/nss/lib/pk11wrap/pk11nobj.c b/security/nss/lib/pk11wrap/pk11nobj.c
new file mode 100644
index 0000000000..586ed80e32
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11nobj.c
@@ -0,0 +1,807 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/*
+ * This file manages Netscape specific PKCS #11 objects (CRLs, Trust objects,
+ * etc).
+ */
+
+#include <stddef.h>
+
+#include "secport.h"
+#include "seccomon.h"
+#include "secmod.h"
+#include "secmodi.h"
+#include "secmodti.h"
+#include "pkcs11.h"
+#include "pk11func.h"
+#include "cert.h"
+#include "certi.h"
+#include "secitem.h"
+#include "sechash.h"
+#include "secoid.h"
+
+#include "certdb.h"
+#include "secerr.h"
+
+#include "pki3hack.h"
+#include "dev3hack.h"
+
+#include "devm.h"
+#include "pki.h"
+#include "pkim.h"
+
+extern const NSSError NSS_ERROR_NOT_FOUND;
+
+CK_TRUST
+pk11_GetTrustField(PK11SlotInfo *slot, PLArenaPool *arena,
+ CK_OBJECT_HANDLE id, CK_ATTRIBUTE_TYPE type)
+{
+ CK_TRUST rv = 0;
+ SECItem item;
+
+ item.data = NULL;
+ item.len = 0;
+
+ if (SECSuccess == PK11_ReadAttribute(slot, id, type, arena, &item)) {
+ PORT_Assert(item.len == sizeof(CK_TRUST));
+ PORT_Memcpy(&rv, item.data, sizeof(CK_TRUST));
+ /* Damn, is there an endian problem here? */
+ return rv;
+ }
+
+ return 0;
+}
+
+PRBool
+pk11_HandleTrustObject(PK11SlotInfo *slot, CERTCertificate *cert, CERTCertTrust *trust)
+{
+ PLArenaPool *arena;
+
+ CK_ATTRIBUTE tobjTemplate[] = {
+ { CKA_CLASS, NULL, 0 },
+ { CKA_CERT_SHA1_HASH, NULL, 0 },
+ };
+
+ CK_OBJECT_CLASS tobjc = CKO_NSS_TRUST;
+ CK_OBJECT_HANDLE tobjID;
+ unsigned char sha1_hash[SHA1_LENGTH];
+
+ CK_TRUST serverAuth, codeSigning, emailProtection, clientAuth;
+
+ PK11_HashBuf(SEC_OID_SHA1, sha1_hash, cert->derCert.data, cert->derCert.len);
+
+ PK11_SETATTRS(&tobjTemplate[0], CKA_CLASS, &tobjc, sizeof(tobjc));
+ PK11_SETATTRS(&tobjTemplate[1], CKA_CERT_SHA1_HASH, sha1_hash,
+ SHA1_LENGTH);
+
+ tobjID = pk11_FindObjectByTemplate(slot, tobjTemplate,
+ sizeof(tobjTemplate) / sizeof(tobjTemplate[0]));
+ if (CK_INVALID_HANDLE == tobjID) {
+ return PR_FALSE;
+ }
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (NULL == arena)
+ return PR_FALSE;
+
+ /* Unfortunately, it seems that PK11_GetAttributes doesn't deal
+ * well with nonexistent attributes. I guess we have to check
+ * the trust info fields one at a time.
+ */
+
+ /* We could verify CKA_CERT_HASH here */
+
+ /* We could verify CKA_EXPIRES here */
+
+ /* "Purpose" trust information */
+ serverAuth = pk11_GetTrustField(slot, arena, tobjID, CKA_TRUST_SERVER_AUTH);
+ clientAuth = pk11_GetTrustField(slot, arena, tobjID, CKA_TRUST_CLIENT_AUTH);
+ codeSigning = pk11_GetTrustField(slot, arena, tobjID, CKA_TRUST_CODE_SIGNING);
+ emailProtection = pk11_GetTrustField(slot, arena, tobjID,
+ CKA_TRUST_EMAIL_PROTECTION);
+ /* Here's where the fun logic happens. We have to map back from the
+ * key usage, extended key usage, purpose, and possibly other trust values
+ * into the old trust-flags bits. */
+
+ /* First implementation: keep it simple for testing. We can study what other
+ * mappings would be appropriate and add them later.. fgmr 20000724 */
+
+ if (serverAuth == CKT_NSS_TRUSTED) {
+ trust->sslFlags |= CERTDB_TERMINAL_RECORD | CERTDB_TRUSTED;
+ }
+
+ if (serverAuth == CKT_NSS_TRUSTED_DELEGATOR) {
+ trust->sslFlags |= CERTDB_VALID_CA | CERTDB_TRUSTED_CA |
+ CERTDB_NS_TRUSTED_CA;
+ }
+ if (clientAuth == CKT_NSS_TRUSTED_DELEGATOR) {
+ trust->sslFlags |= CERTDB_TRUSTED_CLIENT_CA;
+ }
+
+ if (emailProtection == CKT_NSS_TRUSTED) {
+ trust->emailFlags |= CERTDB_TERMINAL_RECORD | CERTDB_TRUSTED;
+ }
+
+ if (emailProtection == CKT_NSS_TRUSTED_DELEGATOR) {
+ trust->emailFlags |= CERTDB_VALID_CA | CERTDB_TRUSTED_CA | CERTDB_NS_TRUSTED_CA;
+ }
+
+ if (codeSigning == CKT_NSS_TRUSTED) {
+ trust->objectSigningFlags |= CERTDB_TERMINAL_RECORD | CERTDB_TRUSTED;
+ }
+
+ if (codeSigning == CKT_NSS_TRUSTED_DELEGATOR) {
+ trust->objectSigningFlags |= CERTDB_VALID_CA | CERTDB_TRUSTED_CA | CERTDB_NS_TRUSTED_CA;
+ }
+
+ /* There's certainly a lot more logic that can go here.. */
+
+ PORT_FreeArena(arena, PR_FALSE);
+
+ return PR_TRUE;
+}
+
+static SECStatus
+pk11_CollectCrls(PK11SlotInfo *slot, CK_OBJECT_HANDLE crlID, void *arg)
+{
+ SECItem derCrl;
+ CERTCrlHeadNode *head = (CERTCrlHeadNode *)arg;
+ CERTCrlNode *new_node = NULL;
+ CK_ATTRIBUTE fetchCrl[3] = {
+ { CKA_VALUE, NULL, 0 },
+ { CKA_NSS_KRL, NULL, 0 },
+ { CKA_NSS_URL, NULL, 0 },
+ };
+ const int fetchCrlSize = sizeof(fetchCrl) / sizeof(fetchCrl[2]);
+ CK_RV crv;
+ SECStatus rv = SECFailure;
+
+ crv = PK11_GetAttributes(head->arena, slot, crlID, fetchCrl, fetchCrlSize);
+ if (CKR_OK != crv) {
+ PORT_SetError(PK11_MapError(crv));
+ goto loser;
+ }
+
+ if (!fetchCrl[1].pValue) {
+ PORT_SetError(SEC_ERROR_CRL_INVALID);
+ goto loser;
+ }
+
+ new_node = (CERTCrlNode *)PORT_ArenaAlloc(head->arena, sizeof(CERTCrlNode));
+ if (new_node == NULL) {
+ goto loser;
+ }
+
+ if (*((CK_BBOOL *)fetchCrl[1].pValue))
+ new_node->type = SEC_KRL_TYPE;
+ else
+ new_node->type = SEC_CRL_TYPE;
+
+ derCrl.type = siBuffer;
+ derCrl.data = (unsigned char *)fetchCrl[0].pValue;
+ derCrl.len = fetchCrl[0].ulValueLen;
+ new_node->crl = CERT_DecodeDERCrl(head->arena, &derCrl, new_node->type);
+ if (new_node->crl == NULL) {
+ goto loser;
+ }
+
+ if (fetchCrl[2].pValue) {
+ int nnlen = fetchCrl[2].ulValueLen;
+ new_node->crl->url = (char *)PORT_ArenaAlloc(head->arena, nnlen + 1);
+ if (!new_node->crl->url) {
+ goto loser;
+ }
+ PORT_Memcpy(new_node->crl->url, fetchCrl[2].pValue, nnlen);
+ new_node->crl->url[nnlen] = 0;
+ } else {
+ new_node->crl->url = NULL;
+ }
+
+ new_node->next = NULL;
+ if (head->last) {
+ head->last->next = new_node;
+ head->last = new_node;
+ } else {
+ head->first = head->last = new_node;
+ }
+ rv = SECSuccess;
+
+loser:
+ return (rv);
+}
+
+/*
+ * Return a list of all the CRLs .
+ * CRLs are allocated in the list's arena.
+ */
+SECStatus
+PK11_LookupCrls(CERTCrlHeadNode *nodes, int type, void *wincx)
+{
+ pk11TraverseSlot creater;
+ CK_ATTRIBUTE theTemplate[2];
+ CK_ATTRIBUTE *attrs;
+ CK_OBJECT_CLASS certClass = CKO_NSS_CRL;
+ CK_BBOOL isKrl = CK_FALSE;
+
+ attrs = theTemplate;
+ PK11_SETATTRS(attrs, CKA_CLASS, &certClass, sizeof(certClass));
+ attrs++;
+ if (type != -1) {
+ isKrl = (CK_BBOOL)(type == SEC_KRL_TYPE);
+ PK11_SETATTRS(attrs, CKA_NSS_KRL, &isKrl, sizeof(isKrl));
+ attrs++;
+ }
+
+ creater.callback = pk11_CollectCrls;
+ creater.callbackArg = (void *)nodes;
+ creater.findTemplate = theTemplate;
+ creater.templateCount = (attrs - theTemplate);
+
+ return pk11_TraverseAllSlots(PK11_TraverseSlot, &creater, PR_FALSE, wincx);
+}
+
+struct crlOptionsStr {
+ CERTCrlHeadNode *head;
+ PRInt32 decodeOptions;
+};
+
+typedef struct crlOptionsStr crlOptions;
+
+static SECStatus
+pk11_RetrieveCrlsCallback(PK11SlotInfo *slot, CK_OBJECT_HANDLE crlID,
+ void *arg)
+{
+ SECItem *derCrl = NULL;
+ crlOptions *options = (crlOptions *)arg;
+ CERTCrlHeadNode *head = options->head;
+ CERTCrlNode *new_node = NULL;
+ CK_ATTRIBUTE fetchCrl[3] = {
+ { CKA_VALUE, NULL, 0 },
+ { CKA_NSS_KRL, NULL, 0 },
+ { CKA_NSS_URL, NULL, 0 },
+ };
+ const int fetchCrlSize = sizeof(fetchCrl) / sizeof(fetchCrl[2]);
+ CK_RV crv;
+ SECStatus rv = SECFailure;
+ PRBool adopted = PR_FALSE; /* whether the CRL adopted the DER memory
+ successfully */
+ int i;
+
+ crv = PK11_GetAttributes(NULL, slot, crlID, fetchCrl, fetchCrlSize);
+ if (CKR_OK != crv) {
+ PORT_SetError(PK11_MapError(crv));
+ goto loser;
+ }
+
+ if (!fetchCrl[1].pValue) {
+ /* reject KRLs */
+ PORT_SetError(SEC_ERROR_CRL_INVALID);
+ goto loser;
+ }
+
+ new_node = (CERTCrlNode *)PORT_ArenaAlloc(head->arena,
+ sizeof(CERTCrlNode));
+ if (new_node == NULL) {
+ goto loser;
+ }
+
+ new_node->type = SEC_CRL_TYPE;
+
+ derCrl = SECITEM_AllocItem(NULL, NULL, 0);
+ if (!derCrl) {
+ goto loser;
+ }
+ derCrl->type = siBuffer;
+ derCrl->data = (unsigned char *)fetchCrl[0].pValue;
+ derCrl->len = fetchCrl[0].ulValueLen;
+ new_node->crl = CERT_DecodeDERCrlWithFlags(NULL, derCrl, new_node->type,
+ options->decodeOptions);
+ if (new_node->crl == NULL) {
+ goto loser;
+ }
+ adopted = PR_TRUE; /* now that the CRL has adopted the DER memory,
+ we won't need to free it upon exit */
+
+ if (fetchCrl[2].pValue && fetchCrl[2].ulValueLen) {
+ /* copy the URL if there is one */
+ int nnlen = fetchCrl[2].ulValueLen;
+ new_node->crl->url = (char *)PORT_ArenaAlloc(new_node->crl->arena,
+ nnlen + 1);
+ if (!new_node->crl->url) {
+ goto loser;
+ }
+ PORT_Memcpy(new_node->crl->url, fetchCrl[2].pValue, nnlen);
+ new_node->crl->url[nnlen] = 0;
+ } else {
+ new_node->crl->url = NULL;
+ }
+
+ new_node->next = NULL;
+ if (head->last) {
+ head->last->next = new_node;
+ head->last = new_node;
+ } else {
+ head->first = head->last = new_node;
+ }
+ rv = SECSuccess;
+ new_node->crl->slot = PK11_ReferenceSlot(slot);
+ new_node->crl->pkcs11ID = crlID;
+
+loser:
+ /* free attributes that weren't adopted by the CRL */
+ for (i = 1; i < fetchCrlSize; i++) {
+ if (fetchCrl[i].pValue) {
+ PORT_Free(fetchCrl[i].pValue);
+ }
+ }
+ /* free the DER if the CRL object didn't adopt it */
+ if (fetchCrl[0].pValue && PR_FALSE == adopted) {
+ PORT_Free(fetchCrl[0].pValue);
+ }
+ if (derCrl && !adopted) {
+ /* clear the data fields, which we already took care of above */
+ derCrl->data = NULL;
+ derCrl->len = 0;
+ /* free the memory for the SECItem structure itself */
+ SECITEM_FreeItem(derCrl, PR_TRUE);
+ }
+ return (rv);
+}
+
+/*
+ * Return a list of CRLs matching specified issuer and type
+ * CRLs are not allocated in the list's arena, but rather in their own,
+ * arena, so that they can be used individually in the CRL cache .
+ * CRLs are always partially decoded for efficiency.
+ */
+SECStatus
+pk11_RetrieveCrls(CERTCrlHeadNode *nodes, SECItem *issuer,
+ void *wincx)
+{
+ pk11TraverseSlot creater;
+ CK_ATTRIBUTE theTemplate[2];
+ CK_ATTRIBUTE *attrs;
+ CK_OBJECT_CLASS crlClass = CKO_NSS_CRL;
+ crlOptions options;
+
+ attrs = theTemplate;
+ PK11_SETATTRS(attrs, CKA_CLASS, &crlClass, sizeof(crlClass));
+ attrs++;
+
+ options.head = nodes;
+
+ /* - do a partial decoding - we don't need to decode the entries while fetching
+ - don't copy the DER for optimal performance - CRL can be very large
+ - have the CRL objects adopt the DER, so SEC_DestroyCrl will free it
+ - keep bad CRL objects. The CRL cache is interested in them, for
+ security purposes. Bad CRL objects are a sign of something amiss.
+ */
+
+ options.decodeOptions = CRL_DECODE_SKIP_ENTRIES | CRL_DECODE_DONT_COPY_DER |
+ CRL_DECODE_ADOPT_HEAP_DER | CRL_DECODE_KEEP_BAD_CRL;
+ if (issuer) {
+ PK11_SETATTRS(attrs, CKA_SUBJECT, issuer->data, issuer->len);
+ attrs++;
+ }
+
+ creater.callback = pk11_RetrieveCrlsCallback;
+ creater.callbackArg = (void *)&options;
+ creater.findTemplate = theTemplate;
+ creater.templateCount = (attrs - theTemplate);
+
+ return pk11_TraverseAllSlots(PK11_TraverseSlot, &creater, PR_FALSE, wincx);
+}
+
+/*
+ * return the crl associated with a derSubjectName
+ */
+SECItem *
+PK11_FindCrlByName(PK11SlotInfo **slot, CK_OBJECT_HANDLE *crlHandle,
+ SECItem *name, int type, char **pUrl)
+{
+ NSSCRL **crls, **crlp, *crl = NULL;
+ NSSDER subject;
+ SECItem *rvItem;
+ NSSTrustDomain *td = STAN_GetDefaultTrustDomain();
+ char *url = NULL;
+
+ PORT_SetError(0);
+ NSSITEM_FROM_SECITEM(&subject, name);
+ if (*slot) {
+ nssCryptokiObject **instances;
+ nssPKIObjectCollection *collection;
+ nssTokenSearchType tokenOnly = nssTokenSearchType_TokenOnly;
+ NSSToken *token = PK11Slot_GetNSSToken(*slot);
+ if (!token) {
+ goto loser;
+ }
+ collection = nssCRLCollection_Create(td, NULL);
+ if (!collection) {
+ (void)nssToken_Destroy(token);
+ goto loser;
+ }
+ instances = nssToken_FindCRLsBySubject(token, NULL, &subject,
+ tokenOnly, 0, NULL);
+ (void)nssToken_Destroy(token);
+ nssPKIObjectCollection_AddInstances(collection, instances, 0);
+ nss_ZFreeIf(instances);
+ crls = nssPKIObjectCollection_GetCRLs(collection, NULL, 0, NULL);
+ nssPKIObjectCollection_Destroy(collection);
+ } else {
+ crls = nssTrustDomain_FindCRLsBySubject(td, &subject);
+ }
+ if ((!crls) || (*crls == NULL)) {
+ if (crls) {
+ nssCRLArray_Destroy(crls);
+ }
+ if (NSS_GetError() == NSS_ERROR_NOT_FOUND) {
+ PORT_SetError(SEC_ERROR_CRL_NOT_FOUND);
+ }
+ goto loser;
+ }
+ for (crlp = crls; *crlp; crlp++) {
+ if ((!(*crlp)->isKRL && type == SEC_CRL_TYPE) ||
+ ((*crlp)->isKRL && type != SEC_CRL_TYPE)) {
+ crl = nssCRL_AddRef(*crlp);
+ break;
+ }
+ }
+ nssCRLArray_Destroy(crls);
+ if (!crl) {
+ /* CRL collection was found, but no interesting CRL's were on it.
+ * Not an error */
+ PORT_SetError(SEC_ERROR_CRL_NOT_FOUND);
+ goto loser;
+ }
+ if (crl->url) {
+ url = PORT_Strdup(crl->url);
+ if (!url) {
+ goto loser;
+ }
+ }
+ rvItem = SECITEM_AllocItem(NULL, NULL, crl->encoding.size);
+ if (!rvItem) {
+ goto loser;
+ }
+ memcpy(rvItem->data, crl->encoding.data, crl->encoding.size);
+ *slot = PK11_ReferenceSlot(crl->object.instances[0]->token->pk11slot);
+ *crlHandle = crl->object.instances[0]->handle;
+ *pUrl = url;
+ nssCRL_Destroy(crl);
+ return rvItem;
+
+loser:
+ if (url)
+ PORT_Free(url);
+ if (crl)
+ nssCRL_Destroy(crl);
+ if (PORT_GetError() == 0) {
+ PORT_SetError(SEC_ERROR_CRL_NOT_FOUND);
+ }
+ return NULL;
+}
+
+CK_OBJECT_HANDLE
+PK11_PutCrl(PK11SlotInfo *slot, SECItem *crl, SECItem *name,
+ char *url, int type)
+{
+ NSSItem derCRL, derSubject;
+ NSSToken *token;
+ nssCryptokiObject *object;
+ PRBool isKRL = (type == SEC_CRL_TYPE) ? PR_FALSE : PR_TRUE;
+ CK_OBJECT_HANDLE rvH;
+
+ NSSITEM_FROM_SECITEM(&derSubject, name);
+ NSSITEM_FROM_SECITEM(&derCRL, crl);
+ token = PK11Slot_GetNSSToken(slot);
+ if (!token) {
+ PORT_SetError(SEC_ERROR_NO_TOKEN);
+ return CK_INVALID_HANDLE;
+ }
+ object = nssToken_ImportCRL(token, NULL,
+ &derSubject, &derCRL, isKRL, url, PR_TRUE);
+ (void)nssToken_Destroy(token);
+
+ if (object) {
+ rvH = object->handle;
+ nssCryptokiObject_Destroy(object);
+ } else {
+ rvH = CK_INVALID_HANDLE;
+ PORT_SetError(SEC_ERROR_CRL_IMPORT_FAILED);
+ }
+ return rvH;
+}
+
+/*
+ * delete a crl.
+ */
+SECStatus
+SEC_DeletePermCRL(CERTSignedCrl *crl)
+{
+ PRStatus status;
+ nssCryptokiObject *object;
+ NSSToken *token;
+ PK11SlotInfo *slot = crl->slot;
+
+ if (slot == NULL) {
+ PORT_Assert(slot);
+ /* shouldn't happen */
+ PORT_SetError(SEC_ERROR_CRL_INVALID);
+ return SECFailure;
+ }
+
+ token = PK11Slot_GetNSSToken(slot);
+ if (!token) {
+ return SECFailure;
+ }
+ object = nss_ZNEW(NULL, nssCryptokiObject);
+ if (!object) {
+ (void)nssToken_Destroy(token);
+ return SECFailure;
+ }
+ object->token = token; /* object takes ownership */
+ object->handle = crl->pkcs11ID;
+ object->isTokenObject = PR_TRUE;
+
+ status = nssToken_DeleteStoredObject(object);
+
+ nssCryptokiObject_Destroy(object);
+ return (status == PR_SUCCESS) ? SECSuccess : SECFailure;
+}
+
+/*
+ * return the certificate associated with a derCert
+ */
+SECItem *
+PK11_FindSMimeProfile(PK11SlotInfo **slot, char *emailAddr,
+ SECItem *name, SECItem **profileTime)
+{
+ CK_OBJECT_CLASS smimeClass = CKO_NSS_SMIME;
+ CK_ATTRIBUTE theTemplate[] = {
+ { CKA_SUBJECT, NULL, 0 },
+ { CKA_CLASS, NULL, 0 },
+ { CKA_NSS_EMAIL, NULL, 0 },
+ };
+ CK_ATTRIBUTE smimeData[] = {
+ { CKA_SUBJECT, NULL, 0 },
+ { CKA_VALUE, NULL, 0 },
+ };
+ /* if you change the array, change the variable below as well */
+ const size_t tsize = sizeof(theTemplate) / sizeof(theTemplate[0]);
+ CK_OBJECT_HANDLE smimeh = CK_INVALID_HANDLE;
+ CK_ATTRIBUTE *attrs = theTemplate;
+ CK_RV crv;
+ SECItem *emailProfile = NULL;
+
+ if (!emailAddr || !emailAddr[0]) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ PK11_SETATTRS(attrs, CKA_SUBJECT, name->data, name->len);
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_CLASS, &smimeClass, sizeof(smimeClass));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_NSS_EMAIL, emailAddr, strlen(emailAddr));
+ attrs++;
+
+ if (*slot) {
+ smimeh = pk11_FindObjectByTemplate(*slot, theTemplate, tsize);
+ } else {
+ PK11SlotList *list = PK11_GetAllTokens(CKM_INVALID_MECHANISM,
+ PR_FALSE, PR_TRUE, NULL);
+ PK11SlotListElement *le;
+
+ if (!list) {
+ return NULL;
+ }
+ /* loop through all the slots */
+ for (le = list->head; le; le = le->next) {
+ smimeh = pk11_FindObjectByTemplate(le->slot, theTemplate, tsize);
+ if (smimeh != CK_INVALID_HANDLE) {
+ *slot = PK11_ReferenceSlot(le->slot);
+ break;
+ }
+ }
+ PK11_FreeSlotList(list);
+ }
+
+ if (smimeh == CK_INVALID_HANDLE) {
+ PORT_SetError(SEC_ERROR_NO_KRL);
+ return NULL;
+ }
+
+ if (profileTime) {
+ PK11_SETATTRS(smimeData, CKA_NSS_SMIME_TIMESTAMP, NULL, 0);
+ }
+
+ crv = PK11_GetAttributes(NULL, *slot, smimeh, smimeData, 2);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ goto loser;
+ }
+
+ if (!profileTime) {
+ SECItem profileSubject;
+
+ profileSubject.data = (unsigned char *)smimeData[0].pValue;
+ profileSubject.len = smimeData[0].ulValueLen;
+ if (!SECITEM_ItemsAreEqual(&profileSubject, name)) {
+ goto loser;
+ }
+ }
+
+ emailProfile = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
+ if (emailProfile == NULL) {
+ goto loser;
+ }
+
+ emailProfile->data = (unsigned char *)smimeData[1].pValue;
+ emailProfile->len = smimeData[1].ulValueLen;
+
+ if (profileTime) {
+ *profileTime = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
+ if (*profileTime) {
+ (*profileTime)->data = (unsigned char *)smimeData[0].pValue;
+ (*profileTime)->len = smimeData[0].ulValueLen;
+ }
+ }
+
+loser:
+ if (emailProfile == NULL) {
+ if (smimeData[1].pValue) {
+ PORT_Free(smimeData[1].pValue);
+ }
+ }
+ if (profileTime == NULL || *profileTime == NULL) {
+ if (smimeData[0].pValue) {
+ PORT_Free(smimeData[0].pValue);
+ }
+ }
+ return emailProfile;
+}
+
+SECStatus
+PK11_SaveSMimeProfile(PK11SlotInfo *slot, char *emailAddr, SECItem *derSubj,
+ SECItem *emailProfile, SECItem *profileTime)
+{
+ CK_OBJECT_CLASS smimeClass = CKO_NSS_SMIME;
+ CK_BBOOL ck_true = CK_TRUE;
+ CK_ATTRIBUTE theTemplate[] = {
+ { CKA_CLASS, NULL, 0 },
+ { CKA_TOKEN, NULL, 0 },
+ { CKA_SUBJECT, NULL, 0 },
+ { CKA_NSS_EMAIL, NULL, 0 },
+ { CKA_NSS_SMIME_TIMESTAMP, NULL, 0 },
+ { CKA_VALUE, NULL, 0 }
+ };
+ /* if you change the array, change the variable below as well */
+ int realSize = 0;
+ CK_OBJECT_HANDLE smimeh = CK_INVALID_HANDLE;
+ CK_ATTRIBUTE *attrs = theTemplate;
+ CK_SESSION_HANDLE rwsession;
+ PK11SlotInfo *free_slot = NULL;
+ CK_RV crv;
+#ifdef DEBUG
+ int tsize = sizeof(theTemplate) / sizeof(theTemplate[0]);
+#endif
+
+ PK11_SETATTRS(attrs, CKA_CLASS, &smimeClass, sizeof(smimeClass));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_TOKEN, &ck_true, sizeof(ck_true));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_SUBJECT, derSubj->data, derSubj->len);
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_NSS_EMAIL,
+ emailAddr, PORT_Strlen(emailAddr) + 1);
+ attrs++;
+ if (profileTime) {
+ PK11_SETATTRS(attrs, CKA_NSS_SMIME_TIMESTAMP, profileTime->data,
+ profileTime->len);
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_VALUE, emailProfile->data,
+ emailProfile->len);
+ attrs++;
+ }
+ realSize = attrs - theTemplate;
+ PORT_Assert(realSize <= tsize);
+
+ if (slot == NULL) {
+ free_slot = slot = PK11_GetInternalKeySlot();
+ /* we need to free the key slot in the end!!! */
+ }
+
+ rwsession = PK11_GetRWSession(slot);
+ if (rwsession == CK_INVALID_HANDLE) {
+ PORT_SetError(SEC_ERROR_READ_ONLY);
+ if (free_slot) {
+ PK11_FreeSlot(free_slot);
+ }
+ return SECFailure;
+ }
+
+ crv = PK11_GETTAB(slot)->C_CreateObject(rwsession, theTemplate, realSize, &smimeh);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ }
+
+ PK11_RestoreROSession(slot, rwsession);
+
+ if (free_slot) {
+ PK11_FreeSlot(free_slot);
+ }
+ return SECSuccess;
+}
+
+CERTSignedCrl *crl_storeCRL(PK11SlotInfo *slot, char *url,
+ CERTSignedCrl *newCrl, SECItem *derCrl, int type);
+
+/* import the CRL into the token */
+
+CERTSignedCrl *
+PK11_ImportCRL(PK11SlotInfo *slot, SECItem *derCRL, char *url,
+ int type, void *wincx, PRInt32 importOptions, PLArenaPool *arena,
+ PRInt32 decodeoptions)
+{
+ CERTSignedCrl *newCrl, *crl;
+ SECStatus rv;
+ CERTCertificate *caCert = NULL;
+
+ newCrl = crl = NULL;
+
+ do {
+ newCrl = CERT_DecodeDERCrlWithFlags(arena, derCRL, type,
+ decodeoptions);
+ if (newCrl == NULL) {
+ if (type == SEC_CRL_TYPE) {
+ /* only promote error when the error code is too generic */
+ if (PORT_GetError() == SEC_ERROR_BAD_DER)
+ PORT_SetError(SEC_ERROR_CRL_INVALID);
+ } else {
+ PORT_SetError(SEC_ERROR_KRL_INVALID);
+ }
+ break;
+ }
+
+ if (0 == (importOptions & CRL_IMPORT_BYPASS_CHECKS)) {
+ CERTCertDBHandle *handle = CERT_GetDefaultCertDB();
+ PR_ASSERT(handle != NULL);
+ caCert = CERT_FindCertByName(handle,
+ &newCrl->crl.derName);
+ if (caCert == NULL) {
+ PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER);
+ break;
+ }
+
+ /* If caCert is a v3 certificate, make sure that it can be used for
+ crl signing purpose */
+ rv = CERT_CheckCertUsage(caCert, KU_CRL_SIGN);
+ if (rv != SECSuccess) {
+ break;
+ }
+
+ rv = CERT_VerifySignedData(&newCrl->signatureWrap, caCert,
+ PR_Now(), wincx);
+ if (rv != SECSuccess) {
+ if (type == SEC_CRL_TYPE) {
+ PORT_SetError(SEC_ERROR_CRL_BAD_SIGNATURE);
+ } else {
+ PORT_SetError(SEC_ERROR_KRL_BAD_SIGNATURE);
+ }
+ break;
+ }
+ }
+
+ crl = crl_storeCRL(slot, url, newCrl, derCRL, type);
+
+ } while (0);
+
+ if (crl == NULL) {
+ SEC_DestroyCrl(newCrl);
+ }
+ if (caCert) {
+ CERT_DestroyCertificate(caCert);
+ }
+ return (crl);
+}
diff --git a/security/nss/lib/pk11wrap/pk11obj.c b/security/nss/lib/pk11wrap/pk11obj.c
new file mode 100644
index 0000000000..5dd4d0fc05
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11obj.c
@@ -0,0 +1,2285 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/*
+ * This file manages object type indepentent functions.
+ */
+#include <limits.h>
+#include <stddef.h>
+
+#include "seccomon.h"
+#include "secmod.h"
+#include "secmodi.h"
+#include "secmodti.h"
+#include "pkcs11.h"
+#include "pkcs11t.h"
+#include "pk11func.h"
+#include "keyhi.h"
+#include "secitem.h"
+#include "secerr.h"
+#include "sslerr.h"
+
+#define PK11_SEARCH_CHUNKSIZE 10
+
+/*
+ * Build a block big enough to hold the data
+ */
+SECItem *
+PK11_BlockData(SECItem *data, unsigned long size)
+{
+ SECItem *newData;
+
+ if (size == 0u)
+ return NULL;
+
+ newData = (SECItem *)PORT_Alloc(sizeof(SECItem));
+ if (newData == NULL)
+ return NULL;
+
+ newData->len = (data->len + (size - 1)) / size;
+ newData->len *= size;
+
+ newData->data = (unsigned char *)PORT_ZAlloc(newData->len);
+ if (newData->data == NULL) {
+ PORT_Free(newData);
+ return NULL;
+ }
+ PORT_Memset(newData->data, newData->len - data->len, newData->len);
+ PORT_Memcpy(newData->data, data->data, data->len);
+ return newData;
+}
+
+SECStatus
+PK11_DestroyObject(PK11SlotInfo *slot, CK_OBJECT_HANDLE object)
+{
+ CK_RV crv;
+
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_DestroyObject(slot->session, object);
+ PK11_ExitSlotMonitor(slot);
+ if (crv != CKR_OK) {
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+SECStatus
+PK11_DestroyTokenObject(PK11SlotInfo *slot, CK_OBJECT_HANDLE object)
+{
+ CK_RV crv;
+ SECStatus rv = SECSuccess;
+ CK_SESSION_HANDLE rwsession;
+
+ rwsession = PK11_GetRWSession(slot);
+ if (rwsession == CK_INVALID_HANDLE) {
+ PORT_SetError(SEC_ERROR_BAD_DATA);
+ return SECFailure;
+ }
+
+ crv = PK11_GETTAB(slot)->C_DestroyObject(rwsession, object);
+ if (crv != CKR_OK) {
+ rv = SECFailure;
+ PORT_SetError(PK11_MapError(crv));
+ }
+ PK11_RestoreROSession(slot, rwsession);
+ return rv;
+}
+
+/*
+ * Read in a single attribute into a SECItem. Allocate space for it with
+ * PORT_Alloc unless an arena is supplied. In the latter case use the arena
+ * to allocate the space.
+ *
+ * PK11_ReadAttribute sets the 'data' and 'len' fields of the SECItem but
+ * does not modify its 'type' field.
+ */
+SECStatus
+PK11_ReadAttribute(PK11SlotInfo *slot, CK_OBJECT_HANDLE id,
+ CK_ATTRIBUTE_TYPE type, PLArenaPool *arena, SECItem *result)
+{
+ CK_ATTRIBUTE attr = { 0, NULL, 0 };
+ CK_RV crv;
+
+ attr.type = type;
+
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_GetAttributeValue(slot->session, id, &attr, 1);
+ if (crv != CKR_OK) {
+ PK11_ExitSlotMonitor(slot);
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ if (arena) {
+ attr.pValue = PORT_ArenaAlloc(arena, attr.ulValueLen);
+ } else {
+ attr.pValue = PORT_Alloc(attr.ulValueLen);
+ }
+ if (attr.pValue == NULL) {
+ PK11_ExitSlotMonitor(slot);
+ return SECFailure;
+ }
+ crv = PK11_GETTAB(slot)->C_GetAttributeValue(slot->session, id, &attr, 1);
+ PK11_ExitSlotMonitor(slot);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ if (!arena)
+ PORT_Free(attr.pValue);
+ return SECFailure;
+ }
+
+ result->data = (unsigned char *)attr.pValue;
+ result->len = attr.ulValueLen;
+
+ return SECSuccess;
+}
+
+/*
+ * Read in a single attribute into As a Ulong.
+ */
+CK_ULONG
+PK11_ReadULongAttribute(PK11SlotInfo *slot, CK_OBJECT_HANDLE id,
+ CK_ATTRIBUTE_TYPE type)
+{
+ CK_ATTRIBUTE attr;
+ CK_ULONG value = CK_UNAVAILABLE_INFORMATION;
+ CK_RV crv;
+
+ PK11_SETATTRS(&attr, type, &value, sizeof(value));
+
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_GetAttributeValue(slot->session, id, &attr, 1);
+ PK11_ExitSlotMonitor(slot);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ }
+ return value;
+}
+
+/*
+ * check to see if a bool has been set.
+ */
+CK_BBOOL
+pk11_HasAttributeSet_Lock(PK11SlotInfo *slot, CK_OBJECT_HANDLE id,
+ CK_ATTRIBUTE_TYPE type, PRBool haslock)
+{
+ CK_BBOOL ckvalue = CK_FALSE;
+ CK_ATTRIBUTE theTemplate;
+ CK_RV crv;
+
+ /* Prepare to retrieve the attribute. */
+ PK11_SETATTRS(&theTemplate, type, &ckvalue, sizeof(CK_BBOOL));
+
+ /* Retrieve attribute value. */
+ if (!haslock)
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_GetAttributeValue(slot->session, id,
+ &theTemplate, 1);
+ if (!haslock)
+ PK11_ExitSlotMonitor(slot);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return CK_FALSE;
+ }
+
+ return ckvalue;
+}
+
+CK_BBOOL
+PK11_HasAttributeSet(PK11SlotInfo *slot, CK_OBJECT_HANDLE id,
+ CK_ATTRIBUTE_TYPE type, PRBool haslock)
+{
+ PR_ASSERT(haslock == PR_FALSE);
+ return pk11_HasAttributeSet_Lock(slot, id, type, PR_FALSE);
+}
+
+/*
+ * returns a full list of attributes. Allocate space for them. If an arena is
+ * provided, allocate space out of the arena.
+ */
+CK_RV
+PK11_GetAttributes(PLArenaPool *arena, PK11SlotInfo *slot,
+ CK_OBJECT_HANDLE obj, CK_ATTRIBUTE *attr, int count)
+{
+ int i;
+ /* make pedantic happy... note that it's only used arena != NULL */
+ void *mark = NULL;
+ CK_RV crv;
+ if (slot->session == CK_INVALID_HANDLE)
+ return CKR_SESSION_HANDLE_INVALID;
+
+ /*
+ * first get all the lengths of the parameters.
+ */
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_GetAttributeValue(slot->session, obj, attr, count);
+ if (crv != CKR_OK) {
+ PK11_ExitSlotMonitor(slot);
+ return crv;
+ }
+
+ if (arena) {
+ mark = PORT_ArenaMark(arena);
+ if (mark == NULL)
+ return CKR_HOST_MEMORY;
+ }
+
+ /*
+ * now allocate space to store the results.
+ */
+ for (i = 0; i < count; i++) {
+ if (attr[i].ulValueLen == 0)
+ continue;
+ if (arena) {
+ attr[i].pValue = PORT_ArenaAlloc(arena, attr[i].ulValueLen);
+ if (attr[i].pValue == NULL) {
+ /* arena failures, just release the mark */
+ PORT_ArenaRelease(arena, mark);
+ PK11_ExitSlotMonitor(slot);
+ return CKR_HOST_MEMORY;
+ }
+ } else {
+ attr[i].pValue = PORT_Alloc(attr[i].ulValueLen);
+ if (attr[i].pValue == NULL) {
+ /* Separate malloc failures, loop to release what we have
+ * so far */
+ int j;
+ for (j = 0; j < i; j++) {
+ PORT_Free(attr[j].pValue);
+ /* don't give the caller pointers to freed memory */
+ attr[j].pValue = NULL;
+ }
+ PK11_ExitSlotMonitor(slot);
+ return CKR_HOST_MEMORY;
+ }
+ }
+ }
+
+ /*
+ * finally get the results.
+ */
+ crv = PK11_GETTAB(slot)->C_GetAttributeValue(slot->session, obj, attr, count);
+ PK11_ExitSlotMonitor(slot);
+ if (crv != CKR_OK) {
+ if (arena) {
+ PORT_ArenaRelease(arena, mark);
+ } else {
+ for (i = 0; i < count; i++) {
+ PORT_Free(attr[i].pValue);
+ /* don't give the caller pointers to freed memory */
+ attr[i].pValue = NULL;
+ }
+ }
+ } else if (arena && mark) {
+ PORT_ArenaUnmark(arena, mark);
+ }
+ return crv;
+}
+
+PRBool
+PK11_IsPermObject(PK11SlotInfo *slot, CK_OBJECT_HANDLE handle)
+{
+ return (PRBool)PK11_HasAttributeSet(slot, handle, CKA_TOKEN, PR_FALSE);
+}
+
+char *
+PK11_GetObjectNickname(PK11SlotInfo *slot, CK_OBJECT_HANDLE id)
+{
+ char *nickname = NULL;
+ SECItem result;
+ SECStatus rv;
+
+ rv = PK11_ReadAttribute(slot, id, CKA_LABEL, NULL, &result);
+ if (rv != SECSuccess) {
+ return NULL;
+ }
+
+ nickname = PORT_ZAlloc(result.len + 1);
+ if (nickname == NULL) {
+ PORT_Free(result.data);
+ return NULL;
+ }
+ PORT_Memcpy(nickname, result.data, result.len);
+ PORT_Free(result.data);
+ return nickname;
+}
+
+SECStatus
+PK11_SetObjectNickname(PK11SlotInfo *slot, CK_OBJECT_HANDLE id,
+ const char *nickname)
+{
+ int len = PORT_Strlen(nickname);
+ CK_ATTRIBUTE setTemplate;
+ CK_RV crv;
+ CK_SESSION_HANDLE rwsession;
+
+ if (len < 0) {
+ return SECFailure;
+ }
+
+ PK11_SETATTRS(&setTemplate, CKA_LABEL, (CK_CHAR *)nickname, len);
+ rwsession = PK11_GetRWSession(slot);
+ if (rwsession == CK_INVALID_HANDLE) {
+ PORT_SetError(SEC_ERROR_BAD_DATA);
+ return SECFailure;
+ }
+ crv = PK11_GETTAB(slot)->C_SetAttributeValue(rwsession, id,
+ &setTemplate, 1);
+ PK11_RestoreROSession(slot, rwsession);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/*
+ * strip leading zero's from key material
+ */
+void
+pk11_SignedToUnsigned(CK_ATTRIBUTE *attrib)
+{
+ char *ptr = (char *)attrib->pValue;
+ unsigned long len = attrib->ulValueLen;
+
+ while ((len > 1) && (*ptr == 0)) {
+ len--;
+ ptr++;
+ }
+ attrib->pValue = ptr;
+ attrib->ulValueLen = len;
+}
+
+/*
+ * get a new session on a slot. If we run out of session, use the slot's
+ * 'exclusive' session. In this case owner becomes false.
+ */
+CK_SESSION_HANDLE
+pk11_GetNewSession(PK11SlotInfo *slot, PRBool *owner)
+{
+ CK_SESSION_HANDLE session;
+ *owner = PR_TRUE;
+ if (!slot->isThreadSafe)
+ PK11_EnterSlotMonitor(slot);
+ if (PK11_GETTAB(slot)->C_OpenSession(slot->slotID, CKF_SERIAL_SESSION,
+ slot, pk11_notify, &session) != CKR_OK) {
+ *owner = PR_FALSE;
+ session = slot->session;
+ }
+ if (!slot->isThreadSafe)
+ PK11_ExitSlotMonitor(slot);
+
+ return session;
+}
+
+void
+pk11_CloseSession(PK11SlotInfo *slot, CK_SESSION_HANDLE session, PRBool owner)
+{
+ if (!owner)
+ return;
+ if (!slot->isThreadSafe)
+ PK11_EnterSlotMonitor(slot);
+ (void)PK11_GETTAB(slot)->C_CloseSession(session);
+ if (!slot->isThreadSafe)
+ PK11_ExitSlotMonitor(slot);
+}
+
+SECStatus
+PK11_CreateNewObject(PK11SlotInfo *slot, CK_SESSION_HANDLE session,
+ const CK_ATTRIBUTE *theTemplate, int count,
+ PRBool token, CK_OBJECT_HANDLE *objectID)
+{
+ CK_SESSION_HANDLE rwsession;
+ CK_RV crv;
+ SECStatus rv = SECSuccess;
+
+ rwsession = session;
+ if (token) {
+ rwsession = PK11_GetRWSession(slot);
+ } else if (rwsession == CK_INVALID_HANDLE) {
+ rwsession = slot->session;
+ if (rwsession != CK_INVALID_HANDLE)
+ PK11_EnterSlotMonitor(slot);
+ }
+ if (rwsession == CK_INVALID_HANDLE) {
+ PORT_SetError(SEC_ERROR_BAD_DATA);
+ return SECFailure;
+ }
+ crv = PK11_GETTAB(slot)->C_CreateObject(rwsession,
+ /* cast away const :-( */ (CK_ATTRIBUTE_PTR)theTemplate,
+ count, objectID);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ rv = SECFailure;
+ }
+ if (token) {
+ PK11_RestoreROSession(slot, rwsession);
+ } else if (session == CK_INVALID_HANDLE) {
+ PK11_ExitSlotMonitor(slot);
+ }
+
+ return rv;
+}
+
+/* This function may add a maximum of 9 attributes. */
+unsigned int
+pk11_OpFlagsToAttributes(CK_FLAGS flags, CK_ATTRIBUTE *attrs, CK_BBOOL *ckTrue)
+{
+
+ const static CK_ATTRIBUTE_TYPE attrTypes[12] = {
+ CKA_ENCRYPT, CKA_DECRYPT, 0 /* DIGEST */, CKA_SIGN,
+ CKA_SIGN_RECOVER, CKA_VERIFY, CKA_VERIFY_RECOVER, 0 /* GEN */,
+ 0 /* GEN PAIR */, CKA_WRAP, CKA_UNWRAP, CKA_DERIVE
+ };
+
+ const CK_ATTRIBUTE_TYPE *pType = attrTypes;
+ CK_ATTRIBUTE *attr = attrs;
+ CK_FLAGS test = CKF_ENCRYPT;
+
+ PR_ASSERT(!(flags & ~CKF_KEY_OPERATION_FLAGS));
+ flags &= CKF_KEY_OPERATION_FLAGS;
+
+ for (; flags && test <= CKF_DERIVE; test <<= 1, ++pType) {
+ if (test & flags) {
+ flags ^= test;
+ PR_ASSERT(*pType);
+ PK11_SETATTRS(attr, *pType, ckTrue, sizeof *ckTrue);
+ ++attr;
+ }
+ }
+ return (attr - attrs);
+}
+
+/*
+ * Check for conflicting flags, for example, if both PK11_ATTR_PRIVATE
+ * and PK11_ATTR_PUBLIC are set.
+ */
+PRBool
+pk11_BadAttrFlags(PK11AttrFlags attrFlags)
+{
+ PK11AttrFlags trueFlags = attrFlags & 0x55555555;
+ PK11AttrFlags falseFlags = (attrFlags >> 1) & 0x55555555;
+ return ((trueFlags & falseFlags) != 0);
+}
+
+/*
+ * This function may add a maximum of 5 attributes.
+ * The caller must make sure the attribute flags don't have conflicts.
+ */
+unsigned int
+pk11_AttrFlagsToAttributes(PK11AttrFlags attrFlags, CK_ATTRIBUTE *attrs,
+ CK_BBOOL *ckTrue, CK_BBOOL *ckFalse)
+{
+ const static CK_ATTRIBUTE_TYPE attrTypes[5] = {
+ CKA_TOKEN, CKA_PRIVATE, CKA_MODIFIABLE, CKA_SENSITIVE,
+ CKA_EXTRACTABLE
+ };
+
+ const CK_ATTRIBUTE_TYPE *pType = attrTypes;
+ CK_ATTRIBUTE *attr = attrs;
+ PK11AttrFlags test = PK11_ATTR_TOKEN;
+
+ PR_ASSERT(!pk11_BadAttrFlags(attrFlags));
+
+ /* we test two related bitflags in each iteration */
+ for (; attrFlags && test <= PK11_ATTR_EXTRACTABLE; test <<= 2, ++pType) {
+ if (test & attrFlags) {
+ attrFlags ^= test;
+ PK11_SETATTRS(attr, *pType, ckTrue, sizeof *ckTrue);
+ ++attr;
+ } else if ((test << 1) & attrFlags) {
+ attrFlags ^= (test << 1);
+ PK11_SETATTRS(attr, *pType, ckFalse, sizeof *ckFalse);
+ ++attr;
+ }
+ }
+ return (attr - attrs);
+}
+
+/*
+ * Some non-compliant PKCS #11 vendors do not give us the modulus, so actually
+ * set up a signature to get the signaure length.
+ */
+static int
+pk11_backupGetSignLength(SECKEYPrivateKey *key)
+{
+ PK11SlotInfo *slot = key->pkcs11Slot;
+ CK_MECHANISM mech = { 0, NULL, 0 };
+ PRBool owner = PR_TRUE;
+ CK_SESSION_HANDLE session;
+ CK_ULONG len;
+ CK_RV crv;
+ unsigned char h_data[20] = { 0 };
+ unsigned char buf[20]; /* obviously to small */
+ CK_ULONG smallLen = sizeof(buf);
+
+ mech.mechanism = PK11_MapSignKeyType(key->keyType);
+
+ session = pk11_GetNewSession(slot, &owner);
+ if (!owner || !(slot->isThreadSafe))
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_SignInit(session, &mech, key->pkcs11ID);
+ if (crv != CKR_OK) {
+ if (!owner || !(slot->isThreadSafe))
+ PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot, session, owner);
+ PORT_SetError(PK11_MapError(crv));
+ return -1;
+ }
+ len = 0;
+ crv = PK11_GETTAB(slot)->C_Sign(session, h_data, sizeof(h_data),
+ NULL, &len);
+ /* now call C_Sign with too small a buffer to clear the session state */
+ (void)PK11_GETTAB(slot)->C_Sign(session, h_data, sizeof(h_data), buf, &smallLen);
+
+ if (!owner || !(slot->isThreadSafe))
+ PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot, session, owner);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return -1;
+ }
+ return len;
+}
+
+/*
+ * get the length of a signature object based on the key
+ */
+int
+PK11_SignatureLen(SECKEYPrivateKey *key)
+{
+ int val;
+ SECItem attributeItem = { siBuffer, NULL, 0 };
+ SECStatus rv;
+ int length;
+
+ switch (key->keyType) {
+ case rsaKey:
+ val = PK11_GetPrivateModulusLen(key);
+ if (val == -1) {
+ return pk11_backupGetSignLength(key);
+ }
+ return (unsigned long)val;
+
+ case fortezzaKey:
+ return 40;
+
+ case dsaKey:
+ rv = PK11_ReadAttribute(key->pkcs11Slot, key->pkcs11ID, CKA_SUBPRIME,
+ NULL, &attributeItem);
+ if (rv == SECSuccess) {
+ length = attributeItem.len;
+ if ((length > 0) && attributeItem.data[0] == 0) {
+ length--;
+ }
+ PORT_Free(attributeItem.data);
+ return length * 2;
+ }
+ return pk11_backupGetSignLength(key);
+
+ case ecKey:
+ rv = PK11_ReadAttribute(key->pkcs11Slot, key->pkcs11ID, CKA_EC_PARAMS,
+ NULL, &attributeItem);
+ if (rv == SECSuccess) {
+ length = SECKEY_ECParamsToBasePointOrderLen(&attributeItem);
+ PORT_Free(attributeItem.data);
+ if (length != 0) {
+ length = ((length + 7) / 8) * 2;
+ return length;
+ }
+ }
+ return pk11_backupGetSignLength(key);
+ default:
+ break;
+ }
+ PORT_SetError(SEC_ERROR_INVALID_KEY);
+ return 0;
+}
+
+/*
+ * copy a key (or any other object) on a token
+ */
+CK_OBJECT_HANDLE
+PK11_CopyKey(PK11SlotInfo *slot, CK_OBJECT_HANDLE srcObject)
+{
+ CK_OBJECT_HANDLE destObject;
+ CK_RV crv;
+
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_CopyObject(slot->session, srcObject, NULL, 0,
+ &destObject);
+ PK11_ExitSlotMonitor(slot);
+ if (crv == CKR_OK)
+ return destObject;
+ PORT_SetError(PK11_MapError(crv));
+ return CK_INVALID_HANDLE;
+}
+
+PRBool
+pk11_FindAttrInTemplate(CK_ATTRIBUTE *attr, unsigned int numAttrs,
+ CK_ATTRIBUTE_TYPE target)
+{
+ for (; numAttrs > 0; ++attr, --numAttrs) {
+ if (attr->type == target)
+ return PR_TRUE;
+ }
+ return PR_FALSE;
+}
+
+/*
+ * Recover the Signed data. We need this because our old verify can't
+ * figure out which hash algorithm to use until we decryptted this.
+ */
+SECStatus
+PK11_VerifyRecover(SECKEYPublicKey *key, const SECItem *sig,
+ SECItem *dsig, void *wincx)
+{
+ PK11SlotInfo *slot = key->pkcs11Slot;
+ CK_OBJECT_HANDLE id = key->pkcs11ID;
+ CK_MECHANISM mech = { 0, NULL, 0 };
+ PRBool owner = PR_TRUE;
+ CK_SESSION_HANDLE session;
+ CK_ULONG len;
+ CK_RV crv;
+
+ mech.mechanism = PK11_MapSignKeyType(key->keyType);
+
+ if (slot == NULL) {
+ slot = PK11_GetBestSlotWithAttributes(mech.mechanism,
+ CKF_VERIFY_RECOVER, 0, wincx);
+ if (slot == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MODULE);
+ return SECFailure;
+ }
+ id = PK11_ImportPublicKey(slot, key, PR_FALSE);
+ } else {
+ PK11_ReferenceSlot(slot);
+ }
+
+ if (id == CK_INVALID_HANDLE) {
+ PK11_FreeSlot(slot);
+ PORT_SetError(SEC_ERROR_BAD_KEY);
+ return SECFailure;
+ }
+
+ session = pk11_GetNewSession(slot, &owner);
+ if (!owner || !(slot->isThreadSafe))
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_VerifyRecoverInit(session, &mech, id);
+ if (crv != CKR_OK) {
+ if (!owner || !(slot->isThreadSafe))
+ PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot, session, owner);
+ PORT_SetError(PK11_MapError(crv));
+ PK11_FreeSlot(slot);
+ return SECFailure;
+ }
+ len = dsig->len;
+ crv = PK11_GETTAB(slot)->C_VerifyRecover(session, sig->data,
+ sig->len, dsig->data, &len);
+ if (!owner || !(slot->isThreadSafe))
+ PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot, session, owner);
+ dsig->len = len;
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ PK11_FreeSlot(slot);
+ return SECFailure;
+ }
+ PK11_FreeSlot(slot);
+ return SECSuccess;
+}
+
+/*
+ * verify a signature from its hash.
+ */
+SECStatus
+PK11_Verify(SECKEYPublicKey *key, const SECItem *sig, const SECItem *hash,
+ void *wincx)
+{
+ CK_MECHANISM_TYPE mech = PK11_MapSignKeyType(key->keyType);
+ return PK11_VerifyWithMechanism(key, mech, NULL, sig, hash, wincx);
+}
+
+/*
+ * Verify a signature from its hash using the given algorithm.
+ */
+SECStatus
+PK11_VerifyWithMechanism(SECKEYPublicKey *key, CK_MECHANISM_TYPE mechanism,
+ const SECItem *param, const SECItem *sig,
+ const SECItem *hash, void *wincx)
+{
+ PK11SlotInfo *slot = key->pkcs11Slot;
+ CK_OBJECT_HANDLE id = key->pkcs11ID;
+ CK_MECHANISM mech = { 0, NULL, 0 };
+ PRBool owner = PR_TRUE;
+ CK_SESSION_HANDLE session;
+ CK_RV crv;
+
+ mech.mechanism = mechanism;
+ if (param) {
+ mech.pParameter = param->data;
+ mech.ulParameterLen = param->len;
+ }
+
+ if (slot == NULL) {
+ unsigned int length = 0;
+ if ((mech.mechanism == CKM_DSA) &&
+ /* 129 is 1024 bits translated to bytes and
+ * padded with an optional '0' to maintain a
+ * positive sign */
+ (key->u.dsa.params.prime.len > 129)) {
+ /* we need to get a slot that not only can do DSA, but can do DSA2
+ * key lengths */
+ length = key->u.dsa.params.prime.len;
+ if (key->u.dsa.params.prime.data[0] == 0) {
+ length--;
+ }
+ /* convert keysize to bits for slot lookup */
+ length *= 8;
+ }
+ slot = PK11_GetBestSlotWithAttributes(mech.mechanism,
+ CKF_VERIFY, length, wincx);
+ if (slot == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MODULE);
+ return SECFailure;
+ }
+ id = PK11_ImportPublicKey(slot, key, PR_FALSE);
+
+ } else {
+ PK11_ReferenceSlot(slot);
+ }
+
+ if (id == CK_INVALID_HANDLE) {
+ PK11_FreeSlot(slot);
+ PORT_SetError(SEC_ERROR_BAD_KEY);
+ return SECFailure;
+ }
+
+ session = pk11_GetNewSession(slot, &owner);
+ if (!owner || !(slot->isThreadSafe))
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_VerifyInit(session, &mech, id);
+ if (crv != CKR_OK) {
+ if (!owner || !(slot->isThreadSafe))
+ PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot, session, owner);
+ PK11_FreeSlot(slot);
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ crv = PK11_GETTAB(slot)->C_Verify(session, hash->data,
+ hash->len, sig->data, sig->len);
+ if (!owner || !(slot->isThreadSafe))
+ PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot, session, owner);
+ PK11_FreeSlot(slot);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/*
+ * sign a hash. The algorithm is determined by the key.
+ */
+SECStatus
+PK11_Sign(SECKEYPrivateKey *key, SECItem *sig, const SECItem *hash)
+{
+ CK_MECHANISM_TYPE mech = PK11_MapSignKeyType(key->keyType);
+ return PK11_SignWithMechanism(key, mech, NULL, sig, hash);
+}
+
+/*
+ * Sign a hash using the given algorithm.
+ */
+SECStatus
+PK11_SignWithMechanism(SECKEYPrivateKey *key, CK_MECHANISM_TYPE mechanism,
+ const SECItem *param, SECItem *sig, const SECItem *hash)
+{
+ PK11SlotInfo *slot = key->pkcs11Slot;
+ CK_MECHANISM mech = { 0, NULL, 0 };
+ PRBool owner = PR_TRUE;
+ CK_SESSION_HANDLE session;
+ PRBool haslock = PR_FALSE;
+ CK_ULONG len;
+ CK_RV crv;
+
+ mech.mechanism = mechanism;
+ if (param) {
+ mech.pParameter = param->data;
+ mech.ulParameterLen = param->len;
+ }
+
+ if (SECKEY_HAS_ATTRIBUTE_SET(key, CKA_PRIVATE)) {
+ PK11_HandlePasswordCheck(slot, key->wincx);
+ }
+
+ session = pk11_GetNewSession(slot, &owner);
+ haslock = (!owner || !(slot->isThreadSafe));
+ if (haslock)
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_SignInit(session, &mech, key->pkcs11ID);
+ if (crv != CKR_OK) {
+ if (haslock)
+ PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot, session, owner);
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+
+ /* PKCS11 2.20 says if CKA_ALWAYS_AUTHENTICATE then
+ * do C_Login with CKU_CONTEXT_SPECIFIC
+ * between C_SignInit and C_Sign */
+ if (SECKEY_HAS_ATTRIBUTE_SET_LOCK(key, CKA_ALWAYS_AUTHENTICATE, haslock)) {
+ PK11_DoPassword(slot, session, PR_FALSE, key->wincx, haslock, PR_TRUE);
+ }
+
+ len = sig->len;
+ crv = PK11_GETTAB(slot)->C_Sign(session, hash->data,
+ hash->len, sig->data, &len);
+ if (haslock)
+ PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot, session, owner);
+ sig->len = len;
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/*
+ * sign data with a MAC key.
+ */
+SECStatus
+PK11_SignWithSymKey(PK11SymKey *symKey, CK_MECHANISM_TYPE mechanism,
+ SECItem *param, SECItem *sig, const SECItem *data)
+{
+ PK11SlotInfo *slot = symKey->slot;
+ CK_MECHANISM mech = { 0, NULL, 0 };
+ PRBool owner = PR_TRUE;
+ CK_SESSION_HANDLE session;
+ PRBool haslock = PR_FALSE;
+ CK_ULONG len;
+ CK_RV crv;
+
+ mech.mechanism = mechanism;
+ if (param) {
+ mech.pParameter = param->data;
+ mech.ulParameterLen = param->len;
+ }
+
+ session = pk11_GetNewSession(slot, &owner);
+ haslock = (!owner || !(slot->isThreadSafe));
+ if (haslock)
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_SignInit(session, &mech, symKey->objectID);
+ if (crv != CKR_OK) {
+ if (haslock)
+ PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot, session, owner);
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+
+ len = sig->len;
+ crv = PK11_GETTAB(slot)->C_Sign(session, data->data,
+ data->len, sig->data, &len);
+ if (haslock)
+ PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot, session, owner);
+ sig->len = len;
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+SECStatus
+PK11_Decrypt(PK11SymKey *symKey,
+ CK_MECHANISM_TYPE mechanism, SECItem *param,
+ unsigned char *out, unsigned int *outLen,
+ unsigned int maxLen,
+ const unsigned char *enc, unsigned encLen)
+{
+ PK11SlotInfo *slot = symKey->slot;
+ CK_MECHANISM mech = { 0, NULL, 0 };
+ CK_ULONG len = maxLen;
+ PRBool owner = PR_TRUE;
+ CK_SESSION_HANDLE session;
+ PRBool haslock = PR_FALSE;
+ CK_RV crv;
+
+ mech.mechanism = mechanism;
+ if (param) {
+ mech.pParameter = param->data;
+ mech.ulParameterLen = param->len;
+ }
+
+ session = pk11_GetNewSession(slot, &owner);
+ haslock = (!owner || !slot->isThreadSafe);
+ if (haslock)
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_DecryptInit(session, &mech, symKey->objectID);
+ if (crv != CKR_OK) {
+ if (haslock)
+ PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot, session, owner);
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+
+ crv = PK11_GETTAB(slot)->C_Decrypt(session, (unsigned char *)enc, encLen,
+ out, &len);
+ if (haslock)
+ PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot, session, owner);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ *outLen = len;
+ return SECSuccess;
+}
+
+SECStatus
+PK11_Encrypt(PK11SymKey *symKey,
+ CK_MECHANISM_TYPE mechanism, SECItem *param,
+ unsigned char *out, unsigned int *outLen,
+ unsigned int maxLen,
+ const unsigned char *data, unsigned int dataLen)
+{
+ PK11SlotInfo *slot = symKey->slot;
+ CK_MECHANISM mech = { 0, NULL, 0 };
+ CK_ULONG len = maxLen;
+ PRBool owner = PR_TRUE;
+ CK_SESSION_HANDLE session;
+ PRBool haslock = PR_FALSE;
+ CK_RV crv;
+
+ mech.mechanism = mechanism;
+ if (param) {
+ mech.pParameter = param->data;
+ mech.ulParameterLen = param->len;
+ }
+
+ session = pk11_GetNewSession(slot, &owner);
+ haslock = (!owner || !slot->isThreadSafe);
+ if (haslock)
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_EncryptInit(session, &mech, symKey->objectID);
+ if (crv != CKR_OK) {
+ if (haslock)
+ PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot, session, owner);
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ crv = PK11_GETTAB(slot)->C_Encrypt(session, (unsigned char *)data,
+ dataLen, out, &len);
+ if (haslock)
+ PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot, session, owner);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ *outLen = len;
+ return SECSuccess;
+}
+
+static SECStatus
+pk11_PrivDecryptRaw(SECKEYPrivateKey *key,
+ unsigned char *data, unsigned *outLen, unsigned int maxLen,
+ const unsigned char *enc, unsigned encLen,
+ CK_MECHANISM_PTR mech)
+{
+ PK11SlotInfo *slot = key->pkcs11Slot;
+ CK_ULONG out = maxLen;
+ PRBool owner = PR_TRUE;
+ CK_SESSION_HANDLE session;
+ PRBool haslock = PR_FALSE;
+ CK_RV crv;
+
+ if (key->keyType != rsaKey) {
+ PORT_SetError(SEC_ERROR_INVALID_KEY);
+ return SECFailure;
+ }
+
+ /* Why do we do a PK11_handle check here? for simple
+ * decryption? .. because the user may have asked for 'ask always'
+ * and this is a private key operation. In practice, thought, it's mute
+ * since only servers wind up using this function */
+ if (SECKEY_HAS_ATTRIBUTE_SET(key, CKA_PRIVATE)) {
+ PK11_HandlePasswordCheck(slot, key->wincx);
+ }
+ session = pk11_GetNewSession(slot, &owner);
+ haslock = (!owner || !(slot->isThreadSafe));
+ if (haslock)
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_DecryptInit(session, mech, key->pkcs11ID);
+ if (crv != CKR_OK) {
+ if (haslock)
+ PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot, session, owner);
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+
+ /* PKCS11 2.20 says if CKA_ALWAYS_AUTHENTICATE then
+ * do C_Login with CKU_CONTEXT_SPECIFIC
+ * between C_DecryptInit and C_Decrypt
+ * ... But see note above about servers */
+ if (SECKEY_HAS_ATTRIBUTE_SET_LOCK(key, CKA_ALWAYS_AUTHENTICATE, haslock)) {
+ PK11_DoPassword(slot, session, PR_FALSE, key->wincx, haslock, PR_TRUE);
+ }
+
+ crv = PK11_GETTAB(slot)->C_Decrypt(session, (unsigned char *)enc, encLen,
+ data, &out);
+ if (haslock)
+ PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot, session, owner);
+ *outLen = out;
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+SECStatus
+PK11_PubDecryptRaw(SECKEYPrivateKey *key,
+ unsigned char *data, unsigned *outLen, unsigned int maxLen,
+ const unsigned char *enc, unsigned encLen)
+{
+ CK_MECHANISM mech = { CKM_RSA_X_509, NULL, 0 };
+ return pk11_PrivDecryptRaw(key, data, outLen, maxLen, enc, encLen, &mech);
+}
+
+SECStatus
+PK11_PrivDecryptPKCS1(SECKEYPrivateKey *key,
+ unsigned char *data, unsigned *outLen, unsigned int maxLen,
+ const unsigned char *enc, unsigned encLen)
+{
+ CK_MECHANISM mech = { CKM_RSA_PKCS, NULL, 0 };
+ return pk11_PrivDecryptRaw(key, data, outLen, maxLen, enc, encLen, &mech);
+}
+
+static SECStatus
+pk11_PubEncryptRaw(SECKEYPublicKey *key,
+ unsigned char *out, unsigned int *outLen,
+ unsigned int maxLen,
+ const unsigned char *data, unsigned dataLen,
+ CK_MECHANISM_PTR mech, void *wincx)
+{
+ PK11SlotInfo *slot;
+ CK_OBJECT_HANDLE id;
+ CK_ULONG len = maxLen;
+ PRBool owner = PR_TRUE;
+ CK_SESSION_HANDLE session;
+ CK_RV crv;
+
+ slot = PK11_GetBestSlotWithAttributes(mech->mechanism, CKF_ENCRYPT, 0, wincx);
+ if (slot == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MODULE);
+ return SECFailure;
+ }
+
+ id = PK11_ImportPublicKey(slot, key, PR_FALSE);
+
+ if (id == CK_INVALID_HANDLE) {
+ PK11_FreeSlot(slot);
+ PORT_SetError(SEC_ERROR_BAD_KEY);
+ return SECFailure;
+ }
+
+ session = pk11_GetNewSession(slot, &owner);
+ if (!owner || !(slot->isThreadSafe))
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_EncryptInit(session, mech, id);
+ if (crv != CKR_OK) {
+ if (!owner || !(slot->isThreadSafe))
+ PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot, session, owner);
+ PK11_FreeSlot(slot);
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ crv = PK11_GETTAB(slot)->C_Encrypt(session, (unsigned char *)data, dataLen,
+ out, &len);
+ if (!owner || !(slot->isThreadSafe))
+ PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot, session, owner);
+ PK11_FreeSlot(slot);
+ *outLen = len;
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+SECStatus
+PK11_PubEncryptRaw(SECKEYPublicKey *key,
+ unsigned char *enc,
+ const unsigned char *data, unsigned dataLen,
+ void *wincx)
+{
+ CK_MECHANISM mech = { CKM_RSA_X_509, NULL, 0 };
+ unsigned int outLen;
+ if (!key || key->keyType != rsaKey) {
+ PORT_SetError(SEC_ERROR_BAD_KEY);
+ return SECFailure;
+ }
+ outLen = SECKEY_PublicKeyStrength(key);
+ return pk11_PubEncryptRaw(key, enc, &outLen, outLen, data, dataLen, &mech,
+ wincx);
+}
+
+SECStatus
+PK11_PubEncryptPKCS1(SECKEYPublicKey *key,
+ unsigned char *enc,
+ const unsigned char *data, unsigned dataLen,
+ void *wincx)
+{
+ CK_MECHANISM mech = { CKM_RSA_PKCS, NULL, 0 };
+ unsigned int outLen;
+ if (!key || key->keyType != rsaKey) {
+ PORT_SetError(SEC_ERROR_BAD_KEY);
+ return SECFailure;
+ }
+ outLen = SECKEY_PublicKeyStrength(key);
+ return pk11_PubEncryptRaw(key, enc, &outLen, outLen, data, dataLen, &mech,
+ wincx);
+}
+
+SECStatus
+PK11_PrivDecrypt(SECKEYPrivateKey *key,
+ CK_MECHANISM_TYPE mechanism, SECItem *param,
+ unsigned char *out, unsigned int *outLen,
+ unsigned int maxLen,
+ const unsigned char *enc, unsigned encLen)
+{
+ CK_MECHANISM mech = { mechanism, NULL, 0 };
+ if (param) {
+ mech.pParameter = param->data;
+ mech.ulParameterLen = param->len;
+ }
+ return pk11_PrivDecryptRaw(key, out, outLen, maxLen, enc, encLen, &mech);
+}
+
+SECStatus
+PK11_PubEncrypt(SECKEYPublicKey *key,
+ CK_MECHANISM_TYPE mechanism, SECItem *param,
+ unsigned char *out, unsigned int *outLen,
+ unsigned int maxLen,
+ const unsigned char *data, unsigned dataLen,
+ void *wincx)
+{
+ CK_MECHANISM mech = { mechanism, NULL, 0 };
+ if (param) {
+ mech.pParameter = param->data;
+ mech.ulParameterLen = param->len;
+ }
+ return pk11_PubEncryptRaw(key, out, outLen, maxLen, data, dataLen, &mech,
+ wincx);
+}
+
+SECKEYPrivateKey *
+PK11_UnwrapPrivKey(PK11SlotInfo *slot, PK11SymKey *wrappingKey,
+ CK_MECHANISM_TYPE wrapType, SECItem *param,
+ SECItem *wrappedKey, SECItem *label,
+ SECItem *idValue, PRBool perm, PRBool sensitive,
+ CK_KEY_TYPE keyType, CK_ATTRIBUTE_TYPE *usage,
+ int usageCount, void *wincx)
+{
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_BBOOL ckfalse = CK_FALSE;
+ CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY;
+ CK_ATTRIBUTE keyTemplate[15];
+ int templateCount = 0;
+ CK_OBJECT_HANDLE privKeyID;
+ CK_MECHANISM mechanism;
+ CK_ATTRIBUTE *attrs = keyTemplate;
+ SECItem *param_free = NULL, *ck_id = NULL;
+ CK_RV crv;
+ CK_SESSION_HANDLE rwsession;
+ PK11SymKey *newKey = NULL;
+ int i;
+
+ if (!slot || !wrappedKey || !idValue) {
+ /* SET AN ERROR!!! */
+ return NULL;
+ }
+
+ ck_id = PK11_MakeIDFromPubKey(idValue);
+ if (!ck_id) {
+ return NULL;
+ }
+
+ PK11_SETATTRS(attrs, CKA_TOKEN, perm ? &cktrue : &ckfalse,
+ sizeof(cktrue));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof(keyClass));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(keyType));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_PRIVATE, sensitive ? &cktrue : &ckfalse,
+ sizeof(cktrue));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_SENSITIVE, sensitive ? &cktrue : &ckfalse,
+ sizeof(cktrue));
+ attrs++;
+ if (label && label->data) {
+ PK11_SETATTRS(attrs, CKA_LABEL, label->data, label->len);
+ attrs++;
+ }
+ PK11_SETATTRS(attrs, CKA_ID, ck_id->data, ck_id->len);
+ attrs++;
+ for (i = 0; i < usageCount; i++) {
+ PK11_SETATTRS(attrs, usage[i], &cktrue, sizeof(cktrue));
+ attrs++;
+ }
+
+ if (PK11_IsInternal(slot)) {
+ PK11_SETATTRS(attrs, CKA_NSS_DB, idValue->data,
+ idValue->len);
+ attrs++;
+ }
+
+ templateCount = attrs - keyTemplate;
+ PR_ASSERT(templateCount <= (sizeof(keyTemplate) / sizeof(CK_ATTRIBUTE)));
+
+ mechanism.mechanism = wrapType;
+ if (!param)
+ param = param_free = PK11_ParamFromIV(wrapType, NULL);
+ if (param) {
+ mechanism.pParameter = param->data;
+ mechanism.ulParameterLen = param->len;
+ } else {
+ mechanism.pParameter = NULL;
+ mechanism.ulParameterLen = 0;
+ }
+
+ if (wrappingKey->slot != slot) {
+ newKey = pk11_CopyToSlot(slot, wrapType, CKA_UNWRAP, wrappingKey);
+ } else {
+ newKey = PK11_ReferenceSymKey(wrappingKey);
+ }
+
+ if (newKey) {
+ if (perm) {
+ /* Get RW Session will either lock the monitor if necessary,
+ * or return a thread safe session handle, or fail. */
+ rwsession = PK11_GetRWSession(slot);
+ } else {
+ rwsession = slot->session;
+ if (rwsession != CK_INVALID_HANDLE)
+ PK11_EnterSlotMonitor(slot);
+ }
+ /* This is a lot a work to deal with fussy PKCS #11 modules
+ * that can't bother to return BAD_DATA when presented with an
+ * invalid session! */
+ if (rwsession == CK_INVALID_HANDLE) {
+ PORT_SetError(SEC_ERROR_BAD_DATA);
+ goto loser;
+ }
+ crv = PK11_GETTAB(slot)->C_UnwrapKey(rwsession, &mechanism,
+ newKey->objectID,
+ wrappedKey->data,
+ wrappedKey->len, keyTemplate,
+ templateCount, &privKeyID);
+
+ if (perm) {
+ PK11_RestoreROSession(slot, rwsession);
+ } else {
+ PK11_ExitSlotMonitor(slot);
+ }
+ PK11_FreeSymKey(newKey);
+ newKey = NULL;
+ } else {
+ crv = CKR_FUNCTION_NOT_SUPPORTED;
+ }
+
+ SECITEM_FreeItem(ck_id, PR_TRUE);
+ ck_id = NULL;
+
+ if (crv != CKR_OK) {
+ /* we couldn't unwrap the key, use the internal module to do the
+ * unwrap, then load the new key into the token */
+ PK11SlotInfo *int_slot = PK11_GetInternalSlot();
+
+ if (int_slot && (slot != int_slot)) {
+ SECKEYPrivateKey *privKey = PK11_UnwrapPrivKey(int_slot,
+ wrappingKey, wrapType, param, wrappedKey, label,
+ idValue, PR_FALSE, PR_FALSE,
+ keyType, usage, usageCount, wincx);
+ if (privKey) {
+ SECKEYPrivateKey *newPrivKey = PK11_LoadPrivKey(slot, privKey,
+ NULL, perm, sensitive);
+ SECKEY_DestroyPrivateKey(privKey);
+ PK11_FreeSlot(int_slot);
+ SECITEM_FreeItem(param_free, PR_TRUE);
+ return newPrivKey;
+ }
+ }
+ if (int_slot)
+ PK11_FreeSlot(int_slot);
+ PORT_SetError(PK11_MapError(crv));
+ SECITEM_FreeItem(param_free, PR_TRUE);
+ return NULL;
+ }
+ SECITEM_FreeItem(param_free, PR_TRUE);
+ return PK11_MakePrivKey(slot, nullKey, PR_FALSE, privKeyID, wincx);
+
+loser:
+ PK11_FreeSymKey(newKey);
+ SECITEM_FreeItem(ck_id, PR_TRUE);
+ SECITEM_FreeItem(param_free, PR_TRUE);
+ return NULL;
+}
+
+/*
+ * Now we're going to wrap a SECKEYPrivateKey with a PK11SymKey
+ * The strategy is to get both keys to reside in the same slot,
+ * one that can perform the desired crypto mechanism and then
+ * call C_WrapKey after all the setup has taken place.
+ */
+SECStatus
+PK11_WrapPrivKey(PK11SlotInfo *slot, PK11SymKey *wrappingKey,
+ SECKEYPrivateKey *privKey, CK_MECHANISM_TYPE wrapType,
+ SECItem *param, SECItem *wrappedKey, void *wincx)
+{
+ PK11SlotInfo *privSlot = privKey->pkcs11Slot; /* The slot where
+ * the private key
+ * we are going to
+ * wrap lives.
+ */
+ PK11SymKey *newSymKey = NULL;
+ SECKEYPrivateKey *newPrivKey = NULL;
+ SECItem *param_free = NULL;
+ CK_ULONG len = wrappedKey->len;
+ CK_MECHANISM mech;
+ CK_RV crv;
+
+ if (!privSlot || !PK11_DoesMechanism(privSlot, wrapType)) {
+ /* Figure out a slot that does the mechanism and try to import
+ * the private key onto that slot.
+ */
+ PK11SlotInfo *int_slot = PK11_GetInternalSlot();
+
+ privSlot = int_slot; /* The private key has a new home */
+ newPrivKey = PK11_LoadPrivKey(privSlot, privKey, NULL, PR_FALSE, PR_FALSE);
+ /* newPrivKey has allocated its own reference to the slot, so it's
+ * safe until we destroy newPrivkey.
+ */
+ PK11_FreeSlot(int_slot);
+ if (newPrivKey == NULL) {
+ return SECFailure;
+ }
+ privKey = newPrivKey;
+ }
+
+ if (privSlot != wrappingKey->slot) {
+ newSymKey = pk11_CopyToSlot(privSlot, wrapType, CKA_WRAP,
+ wrappingKey);
+ wrappingKey = newSymKey;
+ }
+
+ if (wrappingKey == NULL) {
+ if (newPrivKey) {
+ SECKEY_DestroyPrivateKey(newPrivKey);
+ }
+ return SECFailure;
+ }
+ mech.mechanism = wrapType;
+ if (!param) {
+ param = param_free = PK11_ParamFromIV(wrapType, NULL);
+ }
+ if (param) {
+ mech.pParameter = param->data;
+ mech.ulParameterLen = param->len;
+ } else {
+ mech.pParameter = NULL;
+ mech.ulParameterLen = 0;
+ }
+
+ PK11_EnterSlotMonitor(privSlot);
+ crv = PK11_GETTAB(privSlot)->C_WrapKey(privSlot->session, &mech,
+ wrappingKey->objectID,
+ privKey->pkcs11ID,
+ wrappedKey->data, &len);
+ PK11_ExitSlotMonitor(privSlot);
+
+ if (newSymKey) {
+ PK11_FreeSymKey(newSymKey);
+ }
+ if (newPrivKey) {
+ SECKEY_DestroyPrivateKey(newPrivKey);
+ }
+ if (param_free) {
+ SECITEM_FreeItem(param_free, PR_TRUE);
+ }
+
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+
+ wrappedKey->len = len;
+ return SECSuccess;
+}
+
+#if 0
+/*
+ * Sample code relating to linked list returned by PK11_FindGenericObjects
+ */
+
+/*
+ * You can walk the list with the following code:
+ */
+ firstObj = PK11_FindGenericObjects(slot, objClass);
+ for (thisObj=firstObj;
+ thisObj;
+ thisObj=PK11_GetNextGenericObject(thisObj)) {
+ /* operate on thisObj */
+ }
+/*
+ * If you want a particular object from the list...
+ */
+ firstObj = PK11_FindGenericObjects(slot, objClass);
+ for (thisObj=firstObj;
+ thisObj;
+ thisObj=PK11_GetNextGenericObject(thisObj)) {
+ if (isMyObj(thisObj)) {
+ if ( thisObj == firstObj) {
+ /* NOTE: firstObj could be NULL at this point */
+ firstObj = PK11_GetNextGenericObject(thsObj);
+ }
+ PK11_UnlinkGenericObject(thisObj);
+ myObj = thisObj;
+ break;
+ }
+ }
+
+ PK11_DestroyGenericObjects(firstObj);
+
+ /* use myObj */
+
+ PK11_DestroyGenericObject(myObj);
+#endif /* sample code */
+
+/*
+ * return a linked, non-circular list of generic objects.
+ * If you are only interested
+ * in one object, just use the first object in the list. To find the
+ * rest of the list use PK11_GetNextGenericObject() to return the next object.
+ */
+PK11GenericObject *
+PK11_FindGenericObjects(PK11SlotInfo *slot, CK_OBJECT_CLASS objClass)
+{
+ CK_ATTRIBUTE template[1];
+ CK_ATTRIBUTE *attrs = template;
+ CK_OBJECT_HANDLE *objectIDs = NULL;
+ PK11GenericObject *lastObj = NULL, *obj;
+ PK11GenericObject *firstObj = NULL;
+ int i, count = 0;
+
+ PK11_SETATTRS(attrs, CKA_CLASS, &objClass, sizeof(objClass));
+ attrs++;
+
+ objectIDs = pk11_FindObjectsByTemplate(slot, template, 1, &count);
+ if (objectIDs == NULL) {
+ return NULL;
+ }
+
+ /* where we connect our object once we've created it.. */
+ for (i = 0; i < count; i++) {
+ obj = PORT_New(PK11GenericObject);
+ if (!obj) {
+ if (firstObj) {
+ PK11_DestroyGenericObjects(firstObj);
+ }
+ PORT_Free(objectIDs);
+ return NULL;
+ }
+ /* initialize it */
+ obj->slot = PK11_ReferenceSlot(slot);
+ obj->objectID = objectIDs[i];
+ obj->owner = PR_FALSE;
+ obj->next = NULL;
+ obj->prev = NULL;
+
+ /* link it in */
+ if (firstObj == NULL) {
+ firstObj = obj;
+ } else {
+ PK11_LinkGenericObject(lastObj, obj);
+ }
+ lastObj = obj;
+ }
+ PORT_Free(objectIDs);
+ return firstObj;
+}
+
+/*
+ * get the Next Object in the list.
+ */
+PK11GenericObject *
+PK11_GetNextGenericObject(PK11GenericObject *object)
+{
+ return object->next;
+}
+
+PK11GenericObject *
+PK11_GetPrevGenericObject(PK11GenericObject *object)
+{
+ return object->prev;
+}
+
+/*
+ * Link a single object into a new list.
+ * if the object is already in another list, remove it first.
+ */
+SECStatus
+PK11_LinkGenericObject(PK11GenericObject *list, PK11GenericObject *object)
+{
+ PK11_UnlinkGenericObject(object);
+ object->prev = list;
+ object->next = list->next;
+ list->next = object;
+ if (object->next != NULL) {
+ object->next->prev = object;
+ }
+ return SECSuccess;
+}
+
+/*
+ * remove an object from the list. If the object isn't already in
+ * a list unlink becomes a noop.
+ */
+SECStatus
+PK11_UnlinkGenericObject(PK11GenericObject *object)
+{
+ if (object->prev != NULL) {
+ object->prev->next = object->next;
+ }
+ if (object->next != NULL) {
+ object->next->prev = object->prev;
+ }
+
+ object->next = NULL;
+ object->prev = NULL;
+ return SECSuccess;
+}
+
+/*
+ * This function removes a single object from the list and destroys it.
+ * For an already unlinked object there is no difference between
+ * PK11_DestroyGenericObject and PK11_DestroyGenericObjects
+ */
+SECStatus
+PK11_DestroyGenericObject(PK11GenericObject *object)
+{
+ if (object == NULL) {
+ return SECSuccess;
+ }
+
+ PK11_UnlinkGenericObject(object);
+ if (object->slot) {
+ if (object->owner) {
+ PK11_DestroyObject(object->slot, object->objectID);
+ }
+ PK11_FreeSlot(object->slot);
+ }
+ PORT_Free(object);
+ return SECSuccess;
+}
+
+/*
+ * walk down a link list of generic objects destroying them.
+ * This will destroy all objects in a list that the object is linked into.
+ * (the list is traversed in both directions).
+ */
+SECStatus
+PK11_DestroyGenericObjects(PK11GenericObject *objects)
+{
+ PK11GenericObject *nextObject;
+ PK11GenericObject *prevObject;
+
+ if (objects == NULL) {
+ return SECSuccess;
+ }
+
+ nextObject = objects->next;
+ prevObject = objects->prev;
+
+ /* delete all the objects after it in the list */
+ for (; objects; objects = nextObject) {
+ nextObject = objects->next;
+ PK11_DestroyGenericObject(objects);
+ }
+ /* delete all the objects before it in the list */
+ for (objects = prevObject; objects; objects = prevObject) {
+ prevObject = objects->prev;
+ PK11_DestroyGenericObject(objects);
+ }
+ return SECSuccess;
+}
+
+/*
+ * Hand Create a new object and return the Generic object for our new object.
+ */
+PK11GenericObject *
+pk11_CreateGenericObjectHelper(PK11SlotInfo *slot,
+ const CK_ATTRIBUTE *pTemplate,
+ int count, PRBool token, PRBool owner)
+{
+ CK_OBJECT_HANDLE objectID;
+ PK11GenericObject *obj;
+ CK_RV crv;
+
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_CreateNewObject(slot, slot->session, pTemplate, count,
+ token, &objectID);
+ PK11_ExitSlotMonitor(slot);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return NULL;
+ }
+
+ obj = PORT_New(PK11GenericObject);
+ if (!obj) {
+ /* error set by PORT_New */
+ return NULL;
+ }
+
+ /* initialize it */
+ obj->slot = PK11_ReferenceSlot(slot);
+ obj->objectID = objectID;
+ obj->owner = owner;
+ obj->next = NULL;
+ obj->prev = NULL;
+ return obj;
+}
+
+/* This is the classic interface. Applications would call this function to
+ * create new object that would not be destroyed later. This lead to resource
+ * leaks (and thus memory leaks in the PKCS #11 module). To solve this we have
+ * a new interface that automatically marks objects created on the fly to be
+ * destroyed later.
+ * The old interface is preserved because applications like Mozilla purposefully
+ * leak the reference to be found later with PK11_FindGenericObjects. New
+ * applications should use the new interface PK11_CreateManagedGenericObject */
+PK11GenericObject *
+PK11_CreateGenericObject(PK11SlotInfo *slot, const CK_ATTRIBUTE *pTemplate,
+ int count, PRBool token)
+{
+ return pk11_CreateGenericObjectHelper(slot, pTemplate, count, token,
+ PR_FALSE);
+}
+
+/* Use this interface. It will automatically destroy any temporary objects
+ * (token = PR_FALSE) when the PK11GenericObject is freed. Permanent objects still
+ * need to be destroyed by hand with PK11_DestroyTokenObject.
+ */
+PK11GenericObject *
+PK11_CreateManagedGenericObject(PK11SlotInfo *slot,
+ const CK_ATTRIBUTE *pTemplate, int count, PRBool token)
+{
+ return pk11_CreateGenericObjectHelper(slot, pTemplate, count, token,
+ !token);
+}
+
+CK_OBJECT_HANDLE
+PK11_GetObjectHandle(PK11ObjectType objType, void *objSpec,
+ PK11SlotInfo **slotp)
+{
+ CK_OBJECT_HANDLE handle = CK_INVALID_HANDLE;
+ PK11SlotInfo *slot = NULL;
+
+ switch (objType) {
+ case PK11_TypeGeneric:
+ slot = ((PK11GenericObject *)objSpec)->slot;
+ handle = ((PK11GenericObject *)objSpec)->objectID;
+ break;
+ case PK11_TypePrivKey:
+ slot = ((SECKEYPrivateKey *)objSpec)->pkcs11Slot;
+ handle = ((SECKEYPrivateKey *)objSpec)->pkcs11ID;
+ break;
+ case PK11_TypePubKey:
+ slot = ((SECKEYPublicKey *)objSpec)->pkcs11Slot;
+ handle = ((SECKEYPublicKey *)objSpec)->pkcs11ID;
+ break;
+ case PK11_TypeSymKey:
+ slot = ((PK11SymKey *)objSpec)->slot;
+ handle = ((PK11SymKey *)objSpec)->objectID;
+ break;
+ case PK11_TypeCert:
+ handle = PK11_FindObjectForCert((CERTCertificate *)objSpec, NULL,
+ &slot);
+ break;
+ default:
+ PORT_SetError(SEC_ERROR_UNKNOWN_OBJECT_TYPE);
+ break;
+ }
+ if (slotp) {
+ *slotp = slot;
+ }
+ /* paranoia. If the object doesn't have a slot, then it's handle isn't
+ * valid either */
+ if (slot == NULL) {
+ handle = CK_INVALID_HANDLE;
+ }
+ return handle;
+}
+
+/*
+ * Change an attribute on a raw object
+ */
+SECStatus
+PK11_WriteRawAttribute(PK11ObjectType objType, void *objSpec,
+ CK_ATTRIBUTE_TYPE attrType, SECItem *item)
+{
+ PK11SlotInfo *slot = NULL;
+ CK_OBJECT_HANDLE handle = 0;
+ CK_ATTRIBUTE setTemplate;
+ CK_RV crv;
+ CK_SESSION_HANDLE rwsession;
+
+ handle = PK11_GetObjectHandle(objType, objSpec, &slot);
+ if (handle == CK_INVALID_HANDLE) {
+ PORT_SetError(SEC_ERROR_UNKNOWN_OBJECT_TYPE);
+ return SECFailure;
+ }
+
+ PK11_SETATTRS(&setTemplate, attrType, (CK_CHAR *)item->data, item->len);
+ rwsession = PK11_GetRWSession(slot);
+ if (rwsession == CK_INVALID_HANDLE) {
+ PORT_SetError(SEC_ERROR_BAD_DATA);
+ return SECFailure;
+ }
+ crv = PK11_GETTAB(slot)->C_SetAttributeValue(rwsession, handle,
+ &setTemplate, 1);
+ PK11_RestoreROSession(slot, rwsession);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+SECStatus
+PK11_ReadRawAttribute(PK11ObjectType objType, void *objSpec,
+ CK_ATTRIBUTE_TYPE attrType, SECItem *item)
+{
+ PK11SlotInfo *slot = NULL;
+ CK_OBJECT_HANDLE handle = 0;
+
+ handle = PK11_GetObjectHandle(objType, objSpec, &slot);
+ if (handle == CK_INVALID_HANDLE) {
+ PORT_SetError(SEC_ERROR_UNKNOWN_OBJECT_TYPE);
+ return SECFailure;
+ }
+
+ return PK11_ReadAttribute(slot, handle, attrType, NULL, item);
+}
+
+SECStatus
+PK11_ReadRawAttributes(PLArenaPool *arena, PK11ObjectType objType, void *objSpec,
+ CK_ATTRIBUTE *pTemplate, unsigned int count)
+{
+ PK11SlotInfo *slot = NULL;
+ CK_OBJECT_HANDLE handle = 0;
+
+ handle = PK11_GetObjectHandle(objType, objSpec, &slot);
+ if (handle == CK_INVALID_HANDLE) {
+ PORT_SetError(SEC_ERROR_UNKNOWN_OBJECT_TYPE);
+ return SECFailure;
+ }
+ CK_RV crv = PK11_GetAttributes(arena, slot, handle, pTemplate, count);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/*
+ * return the object handle that matches the template
+ */
+CK_OBJECT_HANDLE
+pk11_FindObjectByTemplate(PK11SlotInfo *slot, CK_ATTRIBUTE *theTemplate, size_t tsize)
+{
+ CK_OBJECT_HANDLE object;
+ CK_RV crv = CKR_SESSION_HANDLE_INVALID;
+ CK_ULONG objectCount;
+
+ /*
+ * issue the find
+ */
+ PK11_EnterSlotMonitor(slot);
+ if (slot->session != CK_INVALID_HANDLE) {
+ crv = PK11_GETTAB(slot)->C_FindObjectsInit(slot->session,
+ theTemplate, tsize);
+ }
+ if (crv != CKR_OK) {
+ PK11_ExitSlotMonitor(slot);
+ PORT_SetError(PK11_MapError(crv));
+ return CK_INVALID_HANDLE;
+ }
+
+ crv = PK11_GETTAB(slot)->C_FindObjects(slot->session, &object, 1, &objectCount);
+ PK11_GETTAB(slot)->C_FindObjectsFinal(slot->session);
+ PK11_ExitSlotMonitor(slot);
+ if ((crv != CKR_OK) || (objectCount < 1)) {
+ /* shouldn't use SSL_ERROR... here */
+ PORT_SetError(crv != CKR_OK ? PK11_MapError(crv) : SSL_ERROR_NO_CERTIFICATE);
+ return CK_INVALID_HANDLE;
+ }
+
+ /* blow up if the PKCS #11 module returns us and invalid object handle */
+ PORT_Assert(object != CK_INVALID_HANDLE);
+ return object;
+}
+
+/*
+ * return all the object handles that matches the template
+ */
+CK_OBJECT_HANDLE *
+pk11_FindObjectsByTemplate(PK11SlotInfo *slot, CK_ATTRIBUTE *findTemplate,
+ size_t templCount, int *object_count)
+{
+ CK_OBJECT_HANDLE *objID = NULL;
+ CK_ULONG returned_count = 0;
+ PRBool owner = PR_TRUE;
+ CK_SESSION_HANDLE session;
+ PRBool haslock = PR_FALSE;
+ CK_RV crv = CKR_SESSION_HANDLE_INVALID;
+
+ session = pk11_GetNewSession(slot, &owner);
+ haslock = (!owner || !(slot->isThreadSafe));
+ if (haslock) {
+ PK11_EnterSlotMonitor(slot);
+ }
+ if (session != CK_INVALID_HANDLE) {
+ crv = PK11_GETTAB(slot)->C_FindObjectsInit(session,
+ findTemplate, templCount);
+ }
+ if (crv != CKR_OK) {
+ if (haslock)
+ PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot, session, owner);
+ PORT_SetError(PK11_MapError(crv));
+ *object_count = -1;
+ return NULL;
+ }
+
+ /*
+ * collect all the Matching Objects
+ */
+ do {
+ CK_OBJECT_HANDLE *oldObjID = objID;
+
+ if (objID == NULL) {
+ objID = (CK_OBJECT_HANDLE *)PORT_Alloc(sizeof(CK_OBJECT_HANDLE) *
+ (*object_count + PK11_SEARCH_CHUNKSIZE));
+ } else {
+ objID = (CK_OBJECT_HANDLE *)PORT_Realloc(objID,
+ sizeof(CK_OBJECT_HANDLE) * (*object_count + PK11_SEARCH_CHUNKSIZE));
+ }
+
+ if (objID == NULL) {
+ if (oldObjID)
+ PORT_Free(oldObjID);
+ break;
+ }
+ crv = PK11_GETTAB(slot)->C_FindObjects(session,
+ &objID[*object_count], PK11_SEARCH_CHUNKSIZE, &returned_count);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ PORT_Free(objID);
+ objID = NULL;
+ break;
+ }
+ *object_count += returned_count;
+ } while (returned_count == PK11_SEARCH_CHUNKSIZE);
+
+ PK11_GETTAB(slot)->C_FindObjectsFinal(session);
+ if (haslock) {
+ PK11_ExitSlotMonitor(slot);
+ }
+ pk11_CloseSession(slot, session, owner);
+
+ if (objID && (*object_count == 0)) {
+ PORT_Free(objID);
+ return NULL;
+ }
+ if (objID == NULL)
+ *object_count = -1;
+ return objID;
+}
+
+SECStatus
+PK11_FindRawCertsWithSubject(PK11SlotInfo *slot, SECItem *derSubject,
+ CERTCertificateList **results)
+{
+ if (!slot || !derSubject || !results) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ *results = NULL;
+
+ // derSubject->data may be null. If so, derSubject->len must be 0.
+ if (!derSubject->data && derSubject->len != 0) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ CK_CERTIFICATE_TYPE ckc_x_509 = CKC_X_509;
+ CK_OBJECT_CLASS cko_certificate = CKO_CERTIFICATE;
+ CK_ATTRIBUTE subjectTemplate[] = {
+ { CKA_CERTIFICATE_TYPE, &ckc_x_509, sizeof(ckc_x_509) },
+ { CKA_CLASS, &cko_certificate, sizeof(cko_certificate) },
+ { CKA_SUBJECT, derSubject->data, derSubject->len },
+ };
+ const size_t templateCount = sizeof(subjectTemplate) / sizeof(subjectTemplate[0]);
+ int handleCount = 0;
+ CK_OBJECT_HANDLE *handles =
+ pk11_FindObjectsByTemplate(slot, subjectTemplate, templateCount,
+ &handleCount);
+ if (!handles) {
+ // pk11_FindObjectsByTemplate indicates there was an error by setting
+ // handleCount to -1 (and it has set an error with PORT_SetError).
+ if (handleCount == -1) {
+ return SECFailure;
+ }
+ return SECSuccess;
+ }
+ PORT_Assert(handleCount > 0);
+ if (handleCount <= 0) {
+ PORT_Free(handles);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ if (handleCount > INT_MAX / sizeof(SECItem)) {
+ PORT_Free(handles);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ PLArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (!arena) {
+ PORT_Free(handles);
+ return SECFailure;
+ }
+ CERTCertificateList *rawCertificates =
+ PORT_ArenaNew(arena, CERTCertificateList);
+ if (!rawCertificates) {
+ PORT_Free(handles);
+ PORT_FreeArena(arena, PR_FALSE);
+ return SECFailure;
+ }
+ rawCertificates->arena = arena;
+ rawCertificates->certs = PORT_ArenaNewArray(arena, SECItem, handleCount);
+ if (!rawCertificates->certs) {
+ PORT_Free(handles);
+ PORT_FreeArena(arena, PR_FALSE);
+ return SECFailure;
+ }
+ rawCertificates->len = handleCount;
+ int handleIndex;
+ for (handleIndex = 0; handleIndex < handleCount; handleIndex++) {
+ SECStatus rv =
+ PK11_ReadAttribute(slot, handles[handleIndex], CKA_VALUE, arena,
+ &rawCertificates->certs[handleIndex]);
+ if (rv != SECSuccess) {
+ PORT_Free(handles);
+ PORT_FreeArena(arena, PR_FALSE);
+ return SECFailure;
+ }
+ if (!rawCertificates->certs[handleIndex].data) {
+ PORT_Free(handles);
+ PORT_FreeArena(arena, PR_FALSE);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ }
+ PORT_Free(handles);
+ *results = rawCertificates;
+ return SECSuccess;
+}
+
+/*
+ * given a PKCS #11 object, match it's peer based on the KeyID. searchID
+ * is typically a privateKey or a certificate while the peer is the opposite
+ */
+CK_OBJECT_HANDLE
+PK11_MatchItem(PK11SlotInfo *slot, CK_OBJECT_HANDLE searchID,
+ CK_OBJECT_CLASS matchclass)
+{
+ CK_ATTRIBUTE theTemplate[] = {
+ { CKA_ID, NULL, 0 },
+ { CKA_CLASS, NULL, 0 }
+ };
+ /* if you change the array, change the variable below as well */
+ CK_ATTRIBUTE *keyclass = &theTemplate[1];
+ const size_t tsize = sizeof(theTemplate) / sizeof(theTemplate[0]);
+ /* if you change the array, change the variable below as well */
+ CK_OBJECT_HANDLE peerID;
+ PORTCheapArenaPool tmpArena;
+ CK_RV crv;
+
+ /* now we need to create space for the public key */
+ PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE);
+
+ crv = PK11_GetAttributes(&tmpArena.arena, slot, searchID, theTemplate, tsize);
+ if (crv != CKR_OK) {
+ PORT_DestroyCheapArena(&tmpArena);
+ PORT_SetError(PK11_MapError(crv));
+ return CK_INVALID_HANDLE;
+ }
+
+ if ((theTemplate[0].ulValueLen == 0) || (theTemplate[0].ulValueLen == -1)) {
+ PORT_DestroyCheapArena(&tmpArena);
+ if (matchclass == CKO_CERTIFICATE)
+ PORT_SetError(SEC_ERROR_BAD_KEY);
+ else
+ PORT_SetError(SEC_ERROR_NO_KEY);
+ return CK_INVALID_HANDLE;
+ }
+
+ /*
+ * issue the find
+ */
+ *(CK_OBJECT_CLASS *)(keyclass->pValue) = matchclass;
+
+ peerID = pk11_FindObjectByTemplate(slot, theTemplate, tsize);
+ PORT_DestroyCheapArena(&tmpArena);
+
+ return peerID;
+}
+
+/*
+ * count the number of objects that match the template.
+ */
+int
+PK11_NumberObjectsFor(PK11SlotInfo *slot, CK_ATTRIBUTE *findTemplate,
+ int templCount)
+{
+ CK_OBJECT_HANDLE objID[PK11_SEARCH_CHUNKSIZE];
+ int object_count = 0;
+ CK_ULONG returned_count = 0;
+ CK_RV crv = CKR_SESSION_HANDLE_INVALID;
+
+ PK11_EnterSlotMonitor(slot);
+ if (slot->session != CK_INVALID_HANDLE) {
+ crv = PK11_GETTAB(slot)->C_FindObjectsInit(slot->session,
+ findTemplate, templCount);
+ }
+ if (crv != CKR_OK) {
+ PK11_ExitSlotMonitor(slot);
+ PORT_SetError(PK11_MapError(crv));
+ return object_count;
+ }
+
+ /*
+ * collect all the Matching Objects
+ */
+ do {
+ crv = PK11_GETTAB(slot)->C_FindObjects(slot->session, objID,
+ PK11_SEARCH_CHUNKSIZE,
+ &returned_count);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ break;
+ }
+ object_count += returned_count;
+ } while (returned_count == PK11_SEARCH_CHUNKSIZE);
+
+ PK11_GETTAB(slot)->C_FindObjectsFinal(slot->session);
+ PK11_ExitSlotMonitor(slot);
+ return object_count;
+}
+
+/*
+ * Traverse all the objects in a given slot.
+ */
+SECStatus
+PK11_TraverseSlot(PK11SlotInfo *slot, void *arg)
+{
+ int i;
+ CK_OBJECT_HANDLE *objID = NULL;
+ int object_count = 0;
+ pk11TraverseSlot *slotcb = (pk11TraverseSlot *)arg;
+
+ objID = pk11_FindObjectsByTemplate(slot, slotcb->findTemplate,
+ slotcb->templateCount, &object_count);
+
+ /*Actually this isn't a failure... there just were no objs to be found*/
+ if (object_count == 0) {
+ return SECSuccess;
+ }
+
+ if (objID == NULL) {
+ return SECFailure;
+ }
+
+ for (i = 0; i < object_count; i++) {
+ (*slotcb->callback)(slot, objID[i], slotcb->callbackArg);
+ }
+ PORT_Free(objID);
+ return SECSuccess;
+}
+
+/*
+ * Traverse all the objects in all slots.
+ */
+SECStatus
+pk11_TraverseAllSlots(SECStatus (*callback)(PK11SlotInfo *, void *),
+ void *arg, PRBool forceLogin, void *wincx)
+{
+ PK11SlotList *list;
+ PK11SlotListElement *le;
+ SECStatus rv;
+
+ /* get them all! */
+ list = PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_FALSE, wincx);
+ if (list == NULL)
+ return SECFailure;
+
+ /* look at each slot and authenticate as necessary */
+ for (le = list->head; le; le = le->next) {
+ if (forceLogin) {
+ rv = pk11_AuthenticateUnfriendly(le->slot, PR_FALSE, wincx);
+ if (rv != SECSuccess) {
+ continue;
+ }
+ }
+ if (callback) {
+ (*callback)(le->slot, arg);
+ }
+ }
+
+ PK11_FreeSlotList(list);
+
+ return SECSuccess;
+}
+
+CK_OBJECT_HANDLE *
+PK11_FindObjectsFromNickname(char *nickname, PK11SlotInfo **slotptr,
+ CK_OBJECT_CLASS objclass, int *returnCount, void *wincx)
+{
+ char *tokenName;
+ char *delimit;
+ PK11SlotInfo *slot;
+ CK_OBJECT_HANDLE *objID;
+ CK_ATTRIBUTE findTemplate[] = {
+ { CKA_LABEL, NULL, 0 },
+ { CKA_CLASS, NULL, 0 },
+ };
+ const size_t findCount = sizeof(findTemplate) / sizeof(findTemplate[0]);
+ SECStatus rv;
+ PK11_SETATTRS(&findTemplate[1], CKA_CLASS, &objclass, sizeof(objclass));
+
+ *slotptr = slot = NULL;
+ *returnCount = 0;
+ /* first find the slot associated with this nickname */
+ if ((delimit = PORT_Strchr(nickname, ':')) != NULL) {
+ int len = delimit - nickname;
+ tokenName = (char *)PORT_Alloc(len + 1);
+ if (!tokenName) {
+ return CK_INVALID_HANDLE;
+ }
+ PORT_Memcpy(tokenName, nickname, len);
+ tokenName[len] = 0;
+
+ slot = *slotptr = PK11_FindSlotByName(tokenName);
+ PORT_Free(tokenName);
+ /* if we couldn't find a slot, assume the nickname is an internal cert
+ * with no proceding slot name */
+ if (slot == NULL) {
+ slot = *slotptr = PK11_GetInternalKeySlot();
+ } else {
+ nickname = delimit + 1;
+ }
+ } else {
+ *slotptr = slot = PK11_GetInternalKeySlot();
+ }
+ if (slot == NULL) {
+ return CK_INVALID_HANDLE;
+ }
+
+ rv = pk11_AuthenticateUnfriendly(slot, PR_TRUE, wincx);
+ if (rv != SECSuccess) {
+ PK11_FreeSlot(slot);
+ *slotptr = NULL;
+ return CK_INVALID_HANDLE;
+ }
+
+ findTemplate[0].pValue = nickname;
+ findTemplate[0].ulValueLen = PORT_Strlen(nickname);
+ objID = pk11_FindObjectsByTemplate(slot, findTemplate, findCount, returnCount);
+ if (objID == NULL) {
+ /* PKCS #11 isn't clear on whether or not the NULL is
+ * stored in the template.... try the find again with the
+ * full null terminated string. */
+ findTemplate[0].ulValueLen += 1;
+ objID = pk11_FindObjectsByTemplate(slot, findTemplate, findCount,
+ returnCount);
+ if (objID == NULL) {
+ /* Well that's the best we can do. It's just not here */
+ /* what about faked nicknames? */
+ PK11_FreeSlot(slot);
+ *slotptr = NULL;
+ *returnCount = 0;
+ }
+ }
+
+ return objID;
+}
+
+SECItem *
+pk11_GetLowLevelKeyFromHandle(PK11SlotInfo *slot, CK_OBJECT_HANDLE handle)
+{
+ CK_ATTRIBUTE theTemplate[] = {
+ { CKA_ID, NULL, 0 },
+ };
+ int tsize = sizeof(theTemplate) / sizeof(theTemplate[0]);
+ CK_RV crv;
+ SECItem *item;
+
+ item = SECITEM_AllocItem(NULL, NULL, 0);
+
+ if (item == NULL) {
+ return NULL;
+ }
+
+ crv = PK11_GetAttributes(NULL, slot, handle, theTemplate, tsize);
+ if (crv != CKR_OK) {
+ SECITEM_FreeItem(item, PR_TRUE);
+ PORT_SetError(PK11_MapError(crv));
+ return NULL;
+ }
+
+ item->data = (unsigned char *)theTemplate[0].pValue;
+ item->len = theTemplate[0].ulValueLen;
+
+ return item;
+}
+
+PRBool
+PK11_ObjectGetFIPSStatus(PK11ObjectType objType, void *objSpec)
+{
+ PK11SlotInfo *slot = NULL;
+ CK_OBJECT_HANDLE handle = 0;
+
+ handle = PK11_GetObjectHandle(objType, objSpec, &slot);
+ if (handle == CK_INVALID_HANDLE) {
+ PORT_SetError(SEC_ERROR_UNKNOWN_OBJECT_TYPE);
+ return PR_FALSE;
+ }
+ return pk11slot_GetFIPSStatus(slot, slot->session, handle,
+ CKT_NSS_OBJECT_CHECK);
+}
diff --git a/security/nss/lib/pk11wrap/pk11pars.c b/security/nss/lib/pk11wrap/pk11pars.c
new file mode 100644
index 0000000000..0243a930b6
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11pars.c
@@ -0,0 +1,2158 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/*
+ * The following handles the loading, unloading and management of
+ * various PCKS #11 modules
+ */
+
+#include <ctype.h>
+#include <assert.h>
+#include "pkcs11.h"
+#include "seccomon.h"
+#include "secmod.h"
+#include "secmodi.h"
+#include "secmodti.h"
+#include "pki3hack.h"
+#include "secerr.h"
+#include "nss.h"
+#include "utilpars.h"
+#include "pk11pub.h"
+
+/* create a new module */
+static SECMODModule *
+secmod_NewModule(void)
+{
+ SECMODModule *newMod;
+ PLArenaPool *arena;
+
+ /* create an arena in which dllName and commonName can be
+ * allocated.
+ */
+ arena = PORT_NewArena(512);
+ if (arena == NULL) {
+ return NULL;
+ }
+
+ newMod = (SECMODModule *)PORT_ArenaAlloc(arena, sizeof(SECMODModule));
+ if (newMod == NULL) {
+ PORT_FreeArena(arena, PR_FALSE);
+ return NULL;
+ }
+
+ /*
+ * initialize of the fields of the module
+ */
+ newMod->arena = arena;
+ newMod->internal = PR_FALSE;
+ newMod->loaded = PR_FALSE;
+ newMod->isFIPS = PR_FALSE;
+ newMod->dllName = NULL;
+ newMod->commonName = NULL;
+ newMod->library = NULL;
+ newMod->functionList = NULL;
+ newMod->slotCount = 0;
+ newMod->slots = NULL;
+ newMod->slotInfo = NULL;
+ newMod->slotInfoCount = 0;
+ newMod->refCount = 1;
+ newMod->ssl[0] = 0;
+ newMod->ssl[1] = 0;
+ newMod->libraryParams = NULL;
+ newMod->moduleDBFunc = NULL;
+ newMod->parent = NULL;
+ newMod->isCritical = PR_FALSE;
+ newMod->isModuleDB = PR_FALSE;
+ newMod->moduleDBOnly = PR_FALSE;
+ newMod->trustOrder = 0;
+ newMod->cipherOrder = 0;
+ newMod->evControlMask = 0;
+ newMod->refLock = PZ_NewLock(nssILockRefLock);
+ if (newMod->refLock == NULL) {
+ PORT_FreeArena(arena, PR_FALSE);
+ return NULL;
+ }
+ return newMod;
+}
+
+/* private flags for isModuleDB (field in SECMODModule). */
+/* The meaing of these flags is as follows:
+ *
+ * SECMOD_FLAG_MODULE_DB_IS_MODULE_DB - This is a module that accesses the
+ * database of other modules to load. Module DBs are loadable modules that
+ * tells NSS which PKCS #11 modules to load and when. These module DBs are
+ * chainable. That is, one module DB can load another one. NSS system init
+ * design takes advantage of this feature. In system NSS, a fixed system
+ * module DB loads the system defined libraries, then chains out to the
+ * traditional module DBs to load any system or user configured modules
+ * (like smart cards). This bit is the same as the already existing meaning
+ * of isModuleDB = PR_TRUE. None of the other module db flags should be set
+ * if this flag isn't on.
+ *
+ * SECMOD_FLAG_MODULE_DB_SKIP_FIRST - This flag tells NSS to skip the first
+ * PKCS #11 module presented by a module DB. This allows the OS to load a
+ * softoken from the system module, then ask the existing module DB code to
+ * load the other PKCS #11 modules in that module DB (skipping it's request
+ * to load softoken). This gives the system init finer control over the
+ * configuration of that softoken module.
+ *
+ * SECMOD_FLAG_MODULE_DB_DEFAULT_MODDB - This flag allows system init to mark a
+ * different module DB as the 'default' module DB (the one in which
+ * 'Add module' changes will go). Without this flag NSS takes the first
+ * module as the default Module DB, but in system NSS, that first module
+ * is the system module, which is likely read only (at least to the user).
+ * This allows system NSS to delegate those changes to the user's module DB,
+ * preserving the user's ability to load new PKCS #11 modules (which only
+ * affect him), from existing applications like Firefox.
+ */
+#define SECMOD_FLAG_MODULE_DB_IS_MODULE_DB 0x01 /* must be set if any of the \
+ *other flags are set */
+#define SECMOD_FLAG_MODULE_DB_SKIP_FIRST 0x02
+#define SECMOD_FLAG_MODULE_DB_DEFAULT_MODDB 0x04
+#define SECMOD_FLAG_MODULE_DB_POLICY_ONLY 0x08
+
+/* private flags for internal (field in SECMODModule). */
+/* The meaing of these flags is as follows:
+ *
+ * SECMOD_FLAG_INTERNAL_IS_INTERNAL - This is a marks the the module is
+ * the internal module (that is, softoken). This bit is the same as the
+ * already existing meaning of internal = PR_TRUE. None of the other
+ * internal flags should be set if this flag isn't on.
+ *
+ * SECMOD_FLAG_MODULE_INTERNAL_KEY_SLOT - This flag allows system init to mark
+ * a different slot returned byt PK11_GetInternalKeySlot(). The 'primary'
+ * slot defined by this module will be the new internal key slot.
+ */
+#define SECMOD_FLAG_INTERNAL_IS_INTERNAL 0x01 /* must be set if any of \
+ *the other flags are set */
+#define SECMOD_FLAG_INTERNAL_KEY_SLOT 0x02
+
+/* private flags for policy check. */
+#define SECMOD_FLAG_POLICY_CHECK_IDENTIFIER 0x01
+#define SECMOD_FLAG_POLICY_CHECK_VALUE 0x02
+
+/*
+ * for 3.4 we continue to use the old SECMODModule structure
+ */
+SECMODModule *
+SECMOD_CreateModule(const char *library, const char *moduleName,
+ const char *parameters, const char *nss)
+{
+ return SECMOD_CreateModuleEx(library, moduleName, parameters, nss, NULL);
+}
+
+/*
+ * NSS config options format:
+ *
+ * The specified ciphers will be allowed by policy, but an application
+ * may allow more by policy explicitly:
+ * config="allow=curve1:curve2:hash1:hash2:rsa-1024..."
+ *
+ * Only the specified hashes and curves will be allowed:
+ * config="disallow=all allow=sha1:sha256:secp256r1:secp384r1"
+ *
+ * Only the specified hashes and curves will be allowed, and
+ * RSA keys of 2048 or more will be accepted, and DH key exchange
+ * with 1024-bit primes or more:
+ * config="disallow=all allow=sha1:sha256:secp256r1:secp384r1:min-rsa=2048:min-dh=1024"
+ *
+ * A policy that enables the AES ciphersuites and the SECP256/384 curves:
+ * config="allow=aes128-cbc:aes128-gcm:TLS1.0:TLS1.2:TLS1.1:HMAC-SHA1:SHA1:SHA256:SHA384:RSA:ECDHE-RSA:SECP256R1:SECP384R1"
+ *
+ * Disallow values are parsed first, then allow values, independent of the
+ * order they appear.
+ *
+ * flags: turn on the following flags:
+ * policy-lock: turn off the ability for applications to change policy with
+ * the call NSS_SetAlgorithmPolicy or the other system policy
+ * calls (SSL_SetPolicy, etc.)
+ * ssl-lock: turn off the ability to change the ssl defaults.
+ *
+ * The following only apply to ssl cipher suites (future smime)
+ *
+ * enable: turn on ciphersuites by default.
+ * disable: turn off ciphersuites by default without disallowing them by policy.
+ *
+ *
+ */
+
+typedef struct {
+ const char *name;
+ unsigned name_size;
+ SECOidTag oid;
+ PRUint32 val;
+} oidValDef;
+
+typedef struct {
+ const char *name;
+ unsigned name_size;
+ PRInt32 option;
+} optionFreeDef;
+
+typedef struct {
+ const char *name;
+ unsigned name_size;
+ PRUint32 flag;
+} policyFlagDef;
+
+/*
+ * This table should be merged with the SECOID table.
+ */
+#define CIPHER_NAME(x) x, (sizeof(x) - 1)
+static const oidValDef curveOptList[] = {
+ /* Curves */
+ { CIPHER_NAME("PRIME192V1"), SEC_OID_ANSIX962_EC_PRIME192V1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("PRIME192V2"), SEC_OID_ANSIX962_EC_PRIME192V2,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("PRIME192V3"), SEC_OID_ANSIX962_EC_PRIME192V3,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("PRIME239V1"), SEC_OID_ANSIX962_EC_PRIME239V1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("PRIME239V2"), SEC_OID_ANSIX962_EC_PRIME239V2,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("PRIME239V3"), SEC_OID_ANSIX962_EC_PRIME239V3,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("PRIME256V1"), SEC_OID_ANSIX962_EC_PRIME256V1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("SECP112R1"), SEC_OID_SECG_EC_SECP112R1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("SECP112R2"), SEC_OID_SECG_EC_SECP112R2,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("SECP128R1"), SEC_OID_SECG_EC_SECP128R1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("SECP128R2"), SEC_OID_SECG_EC_SECP128R2,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("SECP160K1"), SEC_OID_SECG_EC_SECP160K1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("SECP160R1"), SEC_OID_SECG_EC_SECP160R1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("SECP160R2"), SEC_OID_SECG_EC_SECP160R2,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("SECP192K1"), SEC_OID_SECG_EC_SECP192K1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("SECP192R1"), SEC_OID_ANSIX962_EC_PRIME192V1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("SECP224K1"), SEC_OID_SECG_EC_SECP224K1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("SECP256K1"), SEC_OID_SECG_EC_SECP256K1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("SECP256R1"), SEC_OID_ANSIX962_EC_PRIME256V1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("SECP384R1"), SEC_OID_SECG_EC_SECP384R1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("SECP521R1"), SEC_OID_SECG_EC_SECP521R1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("CURVE25519"), SEC_OID_CURVE25519,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ /* ANSI X9.62 named elliptic curves (characteristic two field) */
+ { CIPHER_NAME("C2PNB163V1"), SEC_OID_ANSIX962_EC_C2PNB163V1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("C2PNB163V2"), SEC_OID_ANSIX962_EC_C2PNB163V2,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("C2PNB163V3"), SEC_OID_ANSIX962_EC_C2PNB163V3,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("C2PNB176V1"), SEC_OID_ANSIX962_EC_C2PNB176V1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("C2TNB191V1"), SEC_OID_ANSIX962_EC_C2TNB191V1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("C2TNB191V2"), SEC_OID_ANSIX962_EC_C2TNB191V2,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("C2TNB191V3"), SEC_OID_ANSIX962_EC_C2TNB191V3,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("C2ONB191V4"), SEC_OID_ANSIX962_EC_C2ONB191V4,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("C2ONB191V5"), SEC_OID_ANSIX962_EC_C2ONB191V5,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("C2PNB208W1"), SEC_OID_ANSIX962_EC_C2PNB208W1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("C2TNB239V1"), SEC_OID_ANSIX962_EC_C2TNB239V1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("C2TNB239V2"), SEC_OID_ANSIX962_EC_C2TNB239V2,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("C2TNB239V3"), SEC_OID_ANSIX962_EC_C2TNB239V3,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("C2ONB239V4"), SEC_OID_ANSIX962_EC_C2ONB239V4,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("C2ONB239V5"), SEC_OID_ANSIX962_EC_C2ONB239V5,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("C2PNB272W1"), SEC_OID_ANSIX962_EC_C2PNB272W1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("C2PNB304W1"), SEC_OID_ANSIX962_EC_C2PNB304W1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("C2TNB359V1"), SEC_OID_ANSIX962_EC_C2TNB359V1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("C2PNB368W1"), SEC_OID_ANSIX962_EC_C2PNB368W1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("C2TNB431R1"), SEC_OID_ANSIX962_EC_C2TNB431R1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ /* SECG named elliptic curves (characteristic two field) */
+ { CIPHER_NAME("SECT113R1"), SEC_OID_SECG_EC_SECT113R1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("SECT131R1"), SEC_OID_SECG_EC_SECT113R2,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("SECT131R1"), SEC_OID_SECG_EC_SECT131R1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("SECT131R2"), SEC_OID_SECG_EC_SECT131R2,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("SECT163K1"), SEC_OID_SECG_EC_SECT163K1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("SECT163R1"), SEC_OID_SECG_EC_SECT163R1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("SECT163R2"), SEC_OID_SECG_EC_SECT163R2,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("SECT193R1"), SEC_OID_SECG_EC_SECT193R1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("SECT193R2"), SEC_OID_SECG_EC_SECT193R2,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("SECT233K1"), SEC_OID_SECG_EC_SECT233K1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("SECT233R1"), SEC_OID_SECG_EC_SECT233R1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("SECT239K1"), SEC_OID_SECG_EC_SECT239K1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("SECT283K1"), SEC_OID_SECG_EC_SECT283K1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("SECT283R1"), SEC_OID_SECG_EC_SECT283R1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("SECT409K1"), SEC_OID_SECG_EC_SECT409K1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("SECT409R1"), SEC_OID_SECG_EC_SECT409R1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("SECT571K1"), SEC_OID_SECG_EC_SECT571K1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("SECT571R1"), SEC_OID_SECG_EC_SECT571R1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE },
+};
+
+static const oidValDef hashOptList[] = {
+ /* Hashes */
+ { CIPHER_NAME("MD2"), SEC_OID_MD2,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_SIGNATURE },
+ { CIPHER_NAME("MD4"), SEC_OID_MD4,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_SIGNATURE },
+ { CIPHER_NAME("MD5"), SEC_OID_MD5,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_SIGNATURE },
+ { CIPHER_NAME("SHA1"), SEC_OID_SHA1,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_SIGNATURE },
+ { CIPHER_NAME("SHA224"), SEC_OID_SHA224,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_SIGNATURE },
+ { CIPHER_NAME("SHA256"), SEC_OID_SHA256,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_SIGNATURE },
+ { CIPHER_NAME("SHA384"), SEC_OID_SHA384,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_SIGNATURE },
+ { CIPHER_NAME("SHA512"), SEC_OID_SHA512,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_SIGNATURE }
+};
+
+static const oidValDef macOptList[] = {
+ /* MACs */
+ { CIPHER_NAME("HMAC-SHA1"), SEC_OID_HMAC_SHA1, NSS_USE_ALG_IN_SSL },
+ { CIPHER_NAME("HMAC-SHA224"), SEC_OID_HMAC_SHA224, NSS_USE_ALG_IN_SSL },
+ { CIPHER_NAME("HMAC-SHA256"), SEC_OID_HMAC_SHA256, NSS_USE_ALG_IN_SSL },
+ { CIPHER_NAME("HMAC-SHA384"), SEC_OID_HMAC_SHA384, NSS_USE_ALG_IN_SSL },
+ { CIPHER_NAME("HMAC-SHA512"), SEC_OID_HMAC_SHA512, NSS_USE_ALG_IN_SSL },
+ { CIPHER_NAME("HMAC-MD5"), SEC_OID_HMAC_MD5, NSS_USE_ALG_IN_SSL },
+};
+
+static const oidValDef cipherOptList[] = {
+ /* Ciphers */
+ { CIPHER_NAME("AES128-CBC"), SEC_OID_AES_128_CBC, NSS_USE_ALG_IN_SSL },
+ { CIPHER_NAME("AES192-CBC"), SEC_OID_AES_192_CBC, NSS_USE_ALG_IN_SSL },
+ { CIPHER_NAME("AES256-CBC"), SEC_OID_AES_256_CBC, NSS_USE_ALG_IN_SSL },
+ { CIPHER_NAME("AES128-GCM"), SEC_OID_AES_128_GCM, NSS_USE_ALG_IN_SSL },
+ { CIPHER_NAME("AES192-GCM"), SEC_OID_AES_192_GCM, NSS_USE_ALG_IN_SSL },
+ { CIPHER_NAME("AES256-GCM"), SEC_OID_AES_256_GCM, NSS_USE_ALG_IN_SSL },
+ { CIPHER_NAME("CAMELLIA128-CBC"), SEC_OID_CAMELLIA_128_CBC, NSS_USE_ALG_IN_SSL },
+ { CIPHER_NAME("CAMELLIA192-CBC"), SEC_OID_CAMELLIA_192_CBC, NSS_USE_ALG_IN_SSL },
+ { CIPHER_NAME("CAMELLIA256-CBC"), SEC_OID_CAMELLIA_256_CBC, NSS_USE_ALG_IN_SSL },
+ { CIPHER_NAME("CHACHA20-POLY1305"), SEC_OID_CHACHA20_POLY1305, NSS_USE_ALG_IN_SSL },
+ { CIPHER_NAME("SEED-CBC"), SEC_OID_SEED_CBC, NSS_USE_ALG_IN_SSL },
+ { CIPHER_NAME("DES-EDE3-CBC"), SEC_OID_DES_EDE3_CBC, NSS_USE_ALG_IN_SSL },
+ { CIPHER_NAME("DES-40-CBC"), SEC_OID_DES_40_CBC, NSS_USE_ALG_IN_SSL },
+ { CIPHER_NAME("DES-CBC"), SEC_OID_DES_CBC, NSS_USE_ALG_IN_SSL },
+ { CIPHER_NAME("NULL-CIPHER"), SEC_OID_NULL_CIPHER, NSS_USE_ALG_IN_SSL },
+ { CIPHER_NAME("RC2"), SEC_OID_RC2_CBC, NSS_USE_ALG_IN_SSL },
+ { CIPHER_NAME("RC4"), SEC_OID_RC4, NSS_USE_ALG_IN_SSL },
+ { CIPHER_NAME("IDEA"), SEC_OID_IDEA_CBC, NSS_USE_ALG_IN_SSL },
+};
+
+static const oidValDef kxOptList[] = {
+ /* Key exchange */
+ { CIPHER_NAME("RSA"), SEC_OID_TLS_RSA, NSS_USE_ALG_IN_SSL_KX },
+ { CIPHER_NAME("RSA-EXPORT"), SEC_OID_TLS_RSA_EXPORT, NSS_USE_ALG_IN_SSL_KX },
+ { CIPHER_NAME("DHE-RSA"), SEC_OID_TLS_DHE_RSA, NSS_USE_ALG_IN_SSL_KX },
+ { CIPHER_NAME("DHE-DSS"), SEC_OID_TLS_DHE_DSS, NSS_USE_ALG_IN_SSL_KX },
+ { CIPHER_NAME("DH-RSA"), SEC_OID_TLS_DH_RSA, NSS_USE_ALG_IN_SSL_KX },
+ { CIPHER_NAME("DH-DSS"), SEC_OID_TLS_DH_DSS, NSS_USE_ALG_IN_SSL_KX },
+ { CIPHER_NAME("ECDHE-ECDSA"), SEC_OID_TLS_ECDHE_ECDSA, NSS_USE_ALG_IN_SSL_KX },
+ { CIPHER_NAME("ECDHE-RSA"), SEC_OID_TLS_ECDHE_RSA, NSS_USE_ALG_IN_SSL_KX },
+ { CIPHER_NAME("ECDH-ECDSA"), SEC_OID_TLS_ECDH_ECDSA, NSS_USE_ALG_IN_SSL_KX },
+ { CIPHER_NAME("ECDH-RSA"), SEC_OID_TLS_ECDH_RSA, NSS_USE_ALG_IN_SSL_KX },
+};
+
+static const oidValDef signOptList[] = {
+ /* Signatures */
+ { CIPHER_NAME("DSA"), SEC_OID_ANSIX9_DSA_SIGNATURE,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_SIGNATURE },
+ { CIPHER_NAME("RSA-PKCS"), SEC_OID_PKCS1_RSA_ENCRYPTION,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_SIGNATURE },
+ { CIPHER_NAME("RSA-PSS"), SEC_OID_PKCS1_RSA_PSS_SIGNATURE,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_SIGNATURE },
+ { CIPHER_NAME("ECDSA"), SEC_OID_ANSIX962_EC_PUBLIC_KEY,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_SIGNATURE },
+};
+
+typedef struct {
+ const oidValDef *list;
+ PRUint32 entries;
+ const char *description;
+ PRBool allowEmpty;
+} algListsDef;
+
+static const algListsDef algOptLists[] = {
+ { curveOptList, PR_ARRAY_SIZE(curveOptList), "ECC", PR_FALSE },
+ { hashOptList, PR_ARRAY_SIZE(hashOptList), "HASH", PR_FALSE },
+ { macOptList, PR_ARRAY_SIZE(macOptList), "MAC", PR_FALSE },
+ { cipherOptList, PR_ARRAY_SIZE(cipherOptList), "CIPHER", PR_FALSE },
+ { kxOptList, PR_ARRAY_SIZE(kxOptList), "OTHER-KX", PR_FALSE },
+ { signOptList, PR_ARRAY_SIZE(signOptList), "OTHER-SIGN", PR_FALSE },
+};
+
+static const optionFreeDef sslOptList[] = {
+ /* Versions */
+ { CIPHER_NAME("SSL2.0"), 0x002 },
+ { CIPHER_NAME("SSL3.0"), 0x300 },
+ { CIPHER_NAME("SSL3.1"), 0x301 },
+ { CIPHER_NAME("TLS1.0"), 0x301 },
+ { CIPHER_NAME("TLS1.1"), 0x302 },
+ { CIPHER_NAME("TLS1.2"), 0x303 },
+ { CIPHER_NAME("TLS1.3"), 0x304 },
+ { CIPHER_NAME("DTLS1.0"), 0x302 },
+ { CIPHER_NAME("DTLS1.1"), 0x302 },
+ { CIPHER_NAME("DTLS1.2"), 0x303 },
+ { CIPHER_NAME("DTLS1.3"), 0x304 },
+};
+
+static const optionFreeDef keySizeFlagsList[] = {
+ { CIPHER_NAME("KEY-SIZE-SSL"), NSS_KEY_SIZE_POLICY_SSL_FLAG },
+ { CIPHER_NAME("KEY-SIZE-SIGN"), NSS_KEY_SIZE_POLICY_SIGN_FLAG },
+ { CIPHER_NAME("KEY-SIZE-VERIFY"), NSS_KEY_SIZE_POLICY_VERIFY_FLAG },
+};
+
+static const optionFreeDef freeOptList[] = {
+
+ /* Restrictions for asymetric keys */
+ { CIPHER_NAME("RSA-MIN"), NSS_RSA_MIN_KEY_SIZE },
+ { CIPHER_NAME("DH-MIN"), NSS_DH_MIN_KEY_SIZE },
+ { CIPHER_NAME("DSA-MIN"), NSS_DSA_MIN_KEY_SIZE },
+ { CIPHER_NAME("ECC-MIN"), NSS_ECC_MIN_KEY_SIZE },
+ /* what operations doe the key size apply to */
+ { CIPHER_NAME("KEY-SIZE-FLAGS"), NSS_KEY_SIZE_POLICY_FLAGS },
+ /* constraints on SSL Protocols */
+ { CIPHER_NAME("TLS-VERSION-MIN"), NSS_TLS_VERSION_MIN_POLICY },
+ { CIPHER_NAME("TLS-VERSION-MAX"), NSS_TLS_VERSION_MAX_POLICY },
+ /* constraints on DTLS Protocols */
+ { CIPHER_NAME("DTLS-VERSION-MIN"), NSS_DTLS_VERSION_MIN_POLICY },
+ { CIPHER_NAME("DTLS-VERSION-MAX"), NSS_DTLS_VERSION_MAX_POLICY }
+};
+
+static const policyFlagDef policyFlagList[] = {
+ { CIPHER_NAME("SSL"), NSS_USE_ALG_IN_SSL },
+ { CIPHER_NAME("SSL-KEY-EXCHANGE"), NSS_USE_ALG_IN_SSL_KX },
+ /* add other key exhanges in the future */
+ { CIPHER_NAME("KEY-EXCHANGE"), NSS_USE_ALG_IN_SSL_KX },
+ { CIPHER_NAME("CERT-SIGNATURE"), NSS_USE_ALG_IN_CERT_SIGNATURE },
+ { CIPHER_NAME("CMS-SIGNATURE"), NSS_USE_ALG_IN_CMS_SIGNATURE },
+ { CIPHER_NAME("ALL-SIGNATURE"), NSS_USE_ALG_IN_SIGNATURE },
+ /* sign turns off all signatures, but doesn't change the
+ * allowance for specific sigantures... for example:
+ * disallow=sha256/all allow=sha256/signature doesn't allow
+ * cert-sigantures, where disallow=sha256/all allow=sha256/all-signature
+ * does.
+ * however, disallow=sha356/signature and disallow=sha256/all-siganture are
+ * equivalent in effect */
+ { CIPHER_NAME("SIGNATURE"), NSS_USE_ALG_IN_ANY_SIGNATURE },
+ /* enable/disable everything */
+ { CIPHER_NAME("ALL"), NSS_USE_ALG_IN_SSL | NSS_USE_ALG_IN_SSL_KX |
+ NSS_USE_ALG_IN_SIGNATURE },
+ { CIPHER_NAME("NONE"), 0 }
+};
+
+/*
+ * Get the next cipher on the list. point to the next one in 'next'.
+ * return the length;
+ */
+static const char *
+secmod_ArgGetSubValue(const char *cipher, char sep1, char sep2,
+ int *len, const char **next)
+{
+ const char *start = cipher;
+
+ if (start == NULL) {
+ *len = 0;
+ *next = NULL;
+ return start;
+ }
+
+ for (; *cipher && *cipher != sep2; cipher++) {
+ if (*cipher == sep1) {
+ *next = cipher + 1;
+ *len = cipher - start;
+ return start;
+ }
+ }
+ *next = NULL;
+ *len = cipher - start;
+ return start;
+}
+
+static PRUint32
+secmod_parsePolicyValue(const char *policyFlags, int policyLength,
+ PRBool printPolicyFeedback, PRUint32 policyCheckFlags)
+{
+ const char *flag, *currentString;
+ PRUint32 flags = 0;
+ int i;
+
+ for (currentString = policyFlags; currentString &&
+ currentString < policyFlags + policyLength;) {
+ int length;
+ PRBool unknown = PR_TRUE;
+ flag = secmod_ArgGetSubValue(currentString, ',', ':', &length,
+ &currentString);
+ if (length == 0) {
+ continue;
+ }
+ for (i = 0; i < PR_ARRAY_SIZE(policyFlagList); i++) {
+ const policyFlagDef *policy = &policyFlagList[i];
+ unsigned name_size = policy->name_size;
+ if ((policy->name_size == length) &&
+ PORT_Strncasecmp(policy->name, flag, name_size) == 0) {
+ flags |= policy->flag;
+ unknown = PR_FALSE;
+ break;
+ }
+ }
+ if (unknown && printPolicyFeedback &&
+ (policyCheckFlags & SECMOD_FLAG_POLICY_CHECK_VALUE)) {
+ PR_SetEnv("NSS_POLICY_FAIL=1");
+ fprintf(stderr, "NSS-POLICY-FAIL %.*s: unknown value: %.*s\n",
+ policyLength, policyFlags, length, flag);
+ }
+ }
+ return flags;
+}
+
+/* allow symbolic names for values. The only ones currently defines or
+ * SSL protocol versions. */
+static SECStatus
+secmod_getPolicyOptValue(const char *policyValue, int policyValueLength,
+ PRInt32 *result)
+{
+ PRInt32 val = atoi(policyValue);
+ int i;
+
+ if ((val != 0) || (*policyValue == '0')) {
+ *result = val;
+ return SECSuccess;
+ }
+ /* handle any ssl strings */
+ for (i = 0; i < PR_ARRAY_SIZE(sslOptList); i++) {
+ if (policyValueLength == sslOptList[i].name_size &&
+ PORT_Strncasecmp(sslOptList[i].name, policyValue,
+ sslOptList[i].name_size) == 0) {
+ *result = sslOptList[i].option;
+ return SECSuccess;
+ }
+ }
+ /* handle key_size flags. Each flag represents a bit, which
+ * gets or'd together. They can be separated by , | or + */
+ val = 0;
+ while (*policyValue) {
+ PRBool found = PR_FALSE;
+ for (i = 0; i < PR_ARRAY_SIZE(keySizeFlagsList); i++) {
+ if (PORT_Strncasecmp(keySizeFlagsList[i].name, policyValue,
+ keySizeFlagsList[i].name_size) == 0) {
+ val |= keySizeFlagsList[i].option;
+ found = PR_TRUE;
+ policyValue += keySizeFlagsList[i].name_size;
+ break;
+ }
+ }
+ if (!found) {
+ return SECFailure;
+ }
+ if (*policyValue == ',' || *policyValue == '|' || *policyValue == '+') {
+ policyValue++;
+ }
+ }
+ *result = val;
+ return SECSuccess;
+}
+
+/* Policy operations:
+ * Disallow: operation is disallowed by policy. Implies disabled.
+ * Allow: operation is allowed by policy (but could be disabled).
+ * Disable: operation is turned off by default (but could be allowed).
+ * Enable: operation is enabled by default. Implies allowed.
+ */
+typedef enum {
+ NSS_DISALLOW,
+ NSS_ALLOW,
+ NSS_DISABLE,
+ NSS_ENABLE
+} NSSPolicyOperation;
+
+/* apply the operator specific policy */
+SECStatus
+secmod_setPolicyOperation(SECOidTag oid, NSSPolicyOperation operation,
+ PRUint32 value)
+{
+ SECStatus rv = SECSuccess;
+ switch (operation) {
+ case NSS_DISALLOW:
+ /* clear the requested policy bits */
+ rv = NSS_SetAlgorithmPolicy(oid, 0, value);
+ break;
+ case NSS_ALLOW:
+ /* set the requested policy bits */
+ rv = NSS_SetAlgorithmPolicy(oid, value, 0);
+ break;
+ /* enable/disable only apply to SSL cipher suites (future S/MIME).
+ * Enable/disable is implemented by clearing the DEFAULT_NOT_VALID
+ * flag, then setting the NSS_USE_DEFAULT_SSL_ENABLE flag to the
+ * correct value. The ssl policy code will then sort out what to
+ * set based on ciphers and cipher suite values.*/
+ case NSS_DISABLE:
+ if (value & (NSS_USE_ALG_IN_SSL | NSS_USE_ALG_IN_SSL_KX)) {
+ /* clear not valid and enable */
+ rv = NSS_SetAlgorithmPolicy(oid, 0,
+ NSS_USE_DEFAULT_NOT_VALID |
+ NSS_USE_DEFAULT_SSL_ENABLE);
+ }
+ break;
+ case NSS_ENABLE:
+ if (value & (NSS_USE_ALG_IN_SSL | NSS_USE_ALG_IN_SSL_KX)) {
+ /* set enable, clear not valid. NOTE: enable implies allow! */
+ rv = NSS_SetAlgorithmPolicy(oid, value | NSS_USE_DEFAULT_SSL_ENABLE,
+ NSS_USE_DEFAULT_NOT_VALID);
+ }
+ break;
+ default:
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ rv = SECFailure;
+ break;
+ }
+ return rv;
+}
+
+const char *
+secmod_getOperationString(NSSPolicyOperation operation)
+{
+ switch (operation) {
+ case NSS_DISALLOW:
+ return "disallow";
+ case NSS_ALLOW:
+ return "allow";
+ case NSS_DISABLE:
+ return "disable";
+ case NSS_ENABLE:
+ return "enable";
+ default:
+ break;
+ }
+ return "invalid";
+}
+
+static SECStatus
+secmod_applyCryptoPolicy(const char *policyString, NSSPolicyOperation operation,
+ PRBool printPolicyFeedback, PRUint32 policyCheckFlags)
+{
+ const char *cipher, *currentString;
+ unsigned i, j;
+ SECStatus rv = SECSuccess;
+ PRBool unknown;
+
+ if (policyString == NULL || policyString[0] == 0) {
+ return SECSuccess; /* do nothing */
+ }
+
+ /* if we change any of these, make sure it gets applied in ssl as well */
+ NSS_SetAlgorithmPolicy(SEC_OID_APPLY_SSL_POLICY, NSS_USE_POLICY_IN_SSL, 0);
+
+ for (currentString = policyString; currentString;) {
+ int length;
+ PRBool newValue = PR_FALSE;
+
+ cipher = secmod_ArgGetSubValue(currentString, ':', 0, &length,
+ &currentString);
+ unknown = PR_TRUE;
+ if (length >= 3 && cipher[3] == '/') {
+ newValue = PR_TRUE;
+ }
+ if ((newValue || (length == 3)) && PORT_Strncasecmp(cipher, "all", 3) == 0) {
+ /* disable or enable all options by default */
+ PRUint32 value = 0;
+ if (newValue) {
+ value = secmod_parsePolicyValue(&cipher[3] + 1, length - 3 - 1, printPolicyFeedback, policyCheckFlags);
+ }
+ for (i = 0; i < PR_ARRAY_SIZE(algOptLists); i++) {
+ const algListsDef *algOptList = &algOptLists[i];
+ for (j = 0; j < algOptList->entries; j++) {
+ if (!newValue) {
+ value = algOptList->list[j].val;
+ }
+ secmod_setPolicyOperation(algOptList->list[j].oid, operation, value);
+ }
+ }
+ continue;
+ }
+
+ for (i = 0; i < PR_ARRAY_SIZE(algOptLists); i++) {
+ const algListsDef *algOptList = &algOptLists[i];
+ for (j = 0; j < algOptList->entries; j++) {
+ const oidValDef *algOpt = &algOptList->list[j];
+ unsigned name_size = algOpt->name_size;
+ PRBool newOption = PR_FALSE;
+
+ if ((length >= name_size) && (cipher[name_size] == '/')) {
+ newOption = PR_TRUE;
+ }
+ if ((newOption || algOpt->name_size == length) &&
+ PORT_Strncasecmp(algOpt->name, cipher, name_size) == 0) {
+ PRUint32 value = algOpt->val;
+ if (newOption) {
+ value = secmod_parsePolicyValue(&cipher[name_size] + 1,
+ length - name_size - 1,
+ printPolicyFeedback,
+ policyCheckFlags);
+ }
+ rv = secmod_setPolicyOperation(algOptList->list[j].oid, operation, value);
+ if (rv != SECSuccess) {
+ /* could not enable option */
+ /* NSS_SetAlgorithPolicy should have set the error code */
+ return SECFailure;
+ }
+ unknown = PR_FALSE;
+ break;
+ }
+ }
+ }
+ if (!unknown) {
+ continue;
+ }
+
+ for (i = 0; i < PR_ARRAY_SIZE(freeOptList); i++) {
+ const optionFreeDef *freeOpt = &freeOptList[i];
+ unsigned name_size = freeOpt->name_size;
+
+ if ((length > name_size) && cipher[name_size] == '=' &&
+ PORT_Strncasecmp(freeOpt->name, cipher, name_size) == 0) {
+ PRInt32 val;
+ const char *policyValue = &cipher[name_size + 1];
+ int policyValueLength = length - name_size - 1;
+ rv = secmod_getPolicyOptValue(policyValue, policyValueLength,
+ &val);
+ if (rv != SECSuccess) {
+ if (printPolicyFeedback &&
+ (policyCheckFlags & SECMOD_FLAG_POLICY_CHECK_VALUE)) {
+ PR_SetEnv("NSS_POLICY_FAIL=1");
+ fprintf(stderr, "NSS-POLICY-FAIL %.*s: unknown value: %.*s\n",
+ length, cipher, policyValueLength, policyValue);
+ }
+ return SECFailure;
+ }
+ rv = NSS_OptionSet(freeOpt->option, val);
+ if (rv != SECSuccess) {
+ /* could not enable option */
+ /* NSS_OptionSet should have set the error code */
+ return SECFailure;
+ }
+ /* to allow the policy to expand in the future. ignore ciphers
+ * we don't understand */
+ unknown = PR_FALSE;
+ break;
+ }
+ }
+
+ if (unknown && printPolicyFeedback &&
+ (policyCheckFlags & SECMOD_FLAG_POLICY_CHECK_IDENTIFIER)) {
+ PR_SetEnv("NSS_POLICY_FAIL=1");
+ fprintf(stderr, "NSS-POLICY-FAIL %s: unknown identifier: %.*s\n",
+ secmod_getOperationString(operation), length, cipher);
+ }
+ }
+ return rv;
+}
+
+static void
+secmod_sanityCheckCryptoPolicy(void)
+{
+ unsigned i, j;
+ SECStatus rv = SECSuccess;
+ unsigned num_kx_enabled = 0;
+ unsigned num_ssl_enabled = 0;
+ unsigned num_sig_enabled = 0;
+ unsigned enabledCount[PR_ARRAY_SIZE(algOptLists)];
+ const char *sWarn = "WARN";
+ const char *sInfo = "INFO";
+ PRBool haveWarning = PR_FALSE;
+
+ for (i = 0; i < PR_ARRAY_SIZE(algOptLists); i++) {
+ const algListsDef *algOptList = &algOptLists[i];
+ enabledCount[i] = 0;
+ for (j = 0; j < algOptList->entries; j++) {
+ const oidValDef *algOpt = &algOptList->list[j];
+ PRUint32 value;
+ PRBool anyEnabled = PR_FALSE;
+ rv = NSS_GetAlgorithmPolicy(algOpt->oid, &value);
+ if (rv != SECSuccess) {
+ PR_SetEnv("NSS_POLICY_FAIL=1");
+ fprintf(stderr, "NSS-POLICY-FAIL: internal failure with NSS_GetAlgorithmPolicy at %u\n", i);
+ return;
+ }
+
+ if ((algOpt->val & NSS_USE_ALG_IN_SSL_KX) && (value & NSS_USE_ALG_IN_SSL_KX)) {
+ ++num_kx_enabled;
+ anyEnabled = PR_TRUE;
+ fprintf(stderr, "NSS-POLICY-INFO: %s is enabled for KX\n", algOpt->name);
+ }
+ if ((algOpt->val & NSS_USE_ALG_IN_SSL) && (value & NSS_USE_ALG_IN_SSL)) {
+ ++num_ssl_enabled;
+ anyEnabled = PR_TRUE;
+ fprintf(stderr, "NSS-POLICY-INFO: %s is enabled for SSL\n", algOpt->name);
+ }
+ if ((algOpt->val & NSS_USE_ALG_IN_CERT_SIGNATURE) &&
+ ((value & NSS_USE_CERT_SIGNATURE_OK) == NSS_USE_CERT_SIGNATURE_OK)) {
+ ++num_sig_enabled;
+ anyEnabled = PR_TRUE;
+ fprintf(stderr, "NSS-POLICY-INFO: %s is enabled for CERT-SIGNATURE\n", algOpt->name);
+ }
+ if (anyEnabled) {
+ ++enabledCount[i];
+ }
+ }
+ }
+ fprintf(stderr, "NSS-POLICY-%s: NUMBER-OF-SSL-ALG-KX: %u\n", num_kx_enabled ? sInfo : sWarn, num_kx_enabled);
+ fprintf(stderr, "NSS-POLICY-%s: NUMBER-OF-SSL-ALG: %u\n", num_ssl_enabled ? sInfo : sWarn, num_ssl_enabled);
+ fprintf(stderr, "NSS-POLICY-%s: NUMBER-OF-CERT-SIG: %u\n", num_sig_enabled ? sInfo : sWarn, num_sig_enabled);
+ if (!num_kx_enabled || !num_ssl_enabled || !num_sig_enabled) {
+ haveWarning = PR_TRUE;
+ }
+ for (i = 0; i < PR_ARRAY_SIZE(algOptLists); i++) {
+ const algListsDef *algOptList = &algOptLists[i];
+ fprintf(stderr, "NSS-POLICY-%s: NUMBER-OF-%s: %u\n", enabledCount[i] ? sInfo : sWarn, algOptList->description, enabledCount[i]);
+ if (!enabledCount[i] && !algOptList->allowEmpty) {
+ haveWarning = PR_TRUE;
+ }
+ }
+ if (haveWarning) {
+ PR_SetEnv("NSS_POLICY_WARN=1");
+ }
+}
+
+static SECStatus
+secmod_parseCryptoPolicy(const char *policyConfig, PRBool printPolicyFeedback,
+ PRUint32 policyCheckFlags)
+{
+ char *args;
+ SECStatus rv;
+
+ if (policyConfig == NULL) {
+ return SECSuccess; /* no policy given */
+ }
+ /* make sure we initialize the oid table and set all the default policy
+ * values first so we can override them here */
+ rv = SECOID_Init();
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ args = NSSUTIL_ArgGetParamValue("disallow", policyConfig);
+ rv = secmod_applyCryptoPolicy(args, NSS_DISALLOW, printPolicyFeedback,
+ policyCheckFlags);
+ if (args)
+ PORT_Free(args);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ args = NSSUTIL_ArgGetParamValue("allow", policyConfig);
+ rv = secmod_applyCryptoPolicy(args, NSS_ALLOW, printPolicyFeedback,
+ policyCheckFlags);
+ if (args)
+ PORT_Free(args);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ args = NSSUTIL_ArgGetParamValue("disable", policyConfig);
+ rv = secmod_applyCryptoPolicy(args, NSS_DISABLE, printPolicyFeedback,
+ policyCheckFlags);
+ if (args)
+ PORT_Free(args);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ args = NSSUTIL_ArgGetParamValue("enable", policyConfig);
+ rv = secmod_applyCryptoPolicy(args, NSS_ENABLE, printPolicyFeedback,
+ policyCheckFlags);
+ if (args)
+ PORT_Free(args);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ /* this has to be last. Everything after this will be a noop */
+ if (NSSUTIL_ArgHasFlag("flags", "ssl-lock", policyConfig)) {
+ PRInt32 locks;
+ /* don't overwrite other (future) lock flags */
+ rv = NSS_OptionGet(NSS_DEFAULT_LOCKS, &locks);
+ if (rv == SECSuccess) {
+ rv = NSS_OptionSet(NSS_DEFAULT_LOCKS, locks | NSS_DEFAULT_SSL_LOCK);
+ }
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ }
+ if (NSSUTIL_ArgHasFlag("flags", "policy-lock", policyConfig)) {
+ NSS_LockPolicy();
+ }
+ if (printPolicyFeedback) {
+ /* This helps to distinguish configurations that don't contain any
+ * policy config= statement. */
+ PR_SetEnv("NSS_POLICY_LOADED=1");
+ fprintf(stderr, "NSS-POLICY-INFO: LOADED-SUCCESSFULLY\n");
+ secmod_sanityCheckCryptoPolicy();
+ }
+ return rv;
+}
+
+static PRUint32
+secmod_parsePolicyCheckFlags(const char *nss)
+{
+ PRUint32 policyCheckFlags = 0;
+
+ if (NSSUTIL_ArgHasFlag("flags", "policyCheckIdentifier", nss)) {
+ policyCheckFlags |= SECMOD_FLAG_POLICY_CHECK_IDENTIFIER;
+ }
+
+ if (NSSUTIL_ArgHasFlag("flags", "policyCheckValue", nss)) {
+ policyCheckFlags |= SECMOD_FLAG_POLICY_CHECK_VALUE;
+ }
+
+ return policyCheckFlags;
+}
+
+/*
+ * for 3.4 we continue to use the old SECMODModule structure
+ */
+SECMODModule *
+SECMOD_CreateModuleEx(const char *library, const char *moduleName,
+ const char *parameters, const char *nss,
+ const char *config)
+{
+ SECMODModule *mod;
+ SECStatus rv;
+ char *slotParams, *ciphers;
+ PRBool printPolicyFeedback = NSSUTIL_ArgHasFlag("flags", "printPolicyFeedback", nss);
+ PRUint32 policyCheckFlags = secmod_parsePolicyCheckFlags(nss);
+
+ rv = secmod_parseCryptoPolicy(config, printPolicyFeedback, policyCheckFlags);
+
+ /* do not load the module if policy parsing fails */
+ if (rv != SECSuccess) {
+ if (printPolicyFeedback) {
+ PR_SetEnv("NSS_POLICY_FAIL=1");
+ fprintf(stderr, "NSS-POLICY-FAIL: policy config parsing failed, not loading module %s\n", moduleName);
+ }
+ return NULL;
+ }
+
+ mod = secmod_NewModule();
+ if (mod == NULL)
+ return NULL;
+
+ mod->commonName = PORT_ArenaStrdup(mod->arena, moduleName ? moduleName : "");
+ if (library) {
+ mod->dllName = PORT_ArenaStrdup(mod->arena, library);
+ }
+ /* new field */
+ if (parameters) {
+ mod->libraryParams = PORT_ArenaStrdup(mod->arena, parameters);
+ }
+
+ mod->internal = NSSUTIL_ArgHasFlag("flags", "internal", nss);
+ mod->isFIPS = NSSUTIL_ArgHasFlag("flags", "FIPS", nss);
+ /* if the system FIPS mode is enabled, force FIPS to be on */
+ if (SECMOD_GetSystemFIPSEnabled()) {
+ mod->isFIPS = PR_TRUE;
+ }
+ mod->isCritical = NSSUTIL_ArgHasFlag("flags", "critical", nss);
+ slotParams = NSSUTIL_ArgGetParamValue("slotParams", nss);
+ mod->slotInfo = NSSUTIL_ArgParseSlotInfo(mod->arena, slotParams,
+ &mod->slotInfoCount);
+ if (slotParams)
+ PORT_Free(slotParams);
+ /* new field */
+ mod->trustOrder = NSSUTIL_ArgReadLong("trustOrder", nss,
+ NSSUTIL_DEFAULT_TRUST_ORDER, NULL);
+ /* new field */
+ mod->cipherOrder = NSSUTIL_ArgReadLong("cipherOrder", nss,
+ NSSUTIL_DEFAULT_CIPHER_ORDER, NULL);
+ /* new field */
+ mod->isModuleDB = NSSUTIL_ArgHasFlag("flags", "moduleDB", nss);
+ mod->moduleDBOnly = NSSUTIL_ArgHasFlag("flags", "moduleDBOnly", nss);
+ if (mod->moduleDBOnly)
+ mod->isModuleDB = PR_TRUE;
+
+ /* we need more bits, but we also want to preserve binary compatibility
+ * so we overload the isModuleDB PRBool with additional flags.
+ * These flags are only valid if mod->isModuleDB is already set.
+ * NOTE: this depends on the fact that PRBool is at least a char on
+ * all platforms. These flags are only valid if moduleDB is set, so
+ * code checking if (mod->isModuleDB) will continue to work correctly. */
+ if (mod->isModuleDB) {
+ char flags = SECMOD_FLAG_MODULE_DB_IS_MODULE_DB;
+ if (NSSUTIL_ArgHasFlag("flags", "skipFirst", nss)) {
+ flags |= SECMOD_FLAG_MODULE_DB_SKIP_FIRST;
+ }
+ if (NSSUTIL_ArgHasFlag("flags", "defaultModDB", nss)) {
+ flags |= SECMOD_FLAG_MODULE_DB_DEFAULT_MODDB;
+ }
+ if (NSSUTIL_ArgHasFlag("flags", "policyOnly", nss)) {
+ flags |= SECMOD_FLAG_MODULE_DB_POLICY_ONLY;
+ }
+ /* additional moduleDB flags could be added here in the future */
+ mod->isModuleDB = (PRBool)flags;
+ }
+
+ if (mod->internal) {
+ char flags = SECMOD_FLAG_INTERNAL_IS_INTERNAL;
+
+ if (NSSUTIL_ArgHasFlag("flags", "internalKeySlot", nss)) {
+ flags |= SECMOD_FLAG_INTERNAL_KEY_SLOT;
+ }
+ mod->internal = (PRBool)flags;
+ }
+
+ ciphers = NSSUTIL_ArgGetParamValue("ciphers", nss);
+ NSSUTIL_ArgParseCipherFlags(&mod->ssl[0], ciphers);
+ if (ciphers)
+ PORT_Free(ciphers);
+
+ secmod_PrivateModuleCount++;
+
+ return mod;
+}
+
+PRBool
+SECMOD_GetSkipFirstFlag(SECMODModule *mod)
+{
+ char flags = (char)mod->isModuleDB;
+
+ return (flags & SECMOD_FLAG_MODULE_DB_SKIP_FIRST) ? PR_TRUE : PR_FALSE;
+}
+
+PRBool
+SECMOD_GetDefaultModDBFlag(SECMODModule *mod)
+{
+ char flags = (char)mod->isModuleDB;
+
+ return (flags & SECMOD_FLAG_MODULE_DB_DEFAULT_MODDB) ? PR_TRUE : PR_FALSE;
+}
+
+PRBool
+secmod_PolicyOnly(SECMODModule *mod)
+{
+ char flags = (char)mod->isModuleDB;
+
+ return (flags & SECMOD_FLAG_MODULE_DB_POLICY_ONLY) ? PR_TRUE : PR_FALSE;
+}
+
+PRBool
+secmod_IsInternalKeySlot(SECMODModule *mod)
+{
+ char flags = (char)mod->internal;
+
+ return (flags & SECMOD_FLAG_INTERNAL_KEY_SLOT) ? PR_TRUE : PR_FALSE;
+}
+
+void
+secmod_SetInternalKeySlotFlag(SECMODModule *mod, PRBool val)
+{
+ char flags = (char)mod->internal;
+
+ if (val) {
+ flags |= SECMOD_FLAG_INTERNAL_KEY_SLOT;
+ } else {
+ flags &= ~SECMOD_FLAG_INTERNAL_KEY_SLOT;
+ }
+ mod->internal = flags;
+}
+
+/*
+ * copy desc and value into target. Target is known to be big enough to
+ * hold desc +2 +value, which is good because the result of this will be
+ * *desc"*value". We may, however, have to add some escapes for special
+ * characters imbedded into value (rare). This string potentially comes from
+ * a user, so we don't want the user overflowing the target buffer by using
+ * excessive escapes. To prevent this we count the escapes we need to add and
+ * try to expand the buffer with Realloc.
+ */
+static char *
+secmod_doDescCopy(char *target, char **base, int *baseLen,
+ const char *desc, int descLen, char *value)
+{
+ int diff, esc_len;
+
+ esc_len = NSSUTIL_EscapeSize(value, '\"') - 1;
+ diff = esc_len - strlen(value);
+ if (diff > 0) {
+ /* we need to escape... expand newSpecPtr as well to make sure
+ * we don't overflow it */
+ int offset = target - *base;
+ char *newPtr = PORT_Realloc(*base, *baseLen + diff);
+ if (!newPtr) {
+ return target; /* not enough space, just drop the whole copy */
+ }
+ *baseLen += diff;
+ target = newPtr + offset;
+ *base = newPtr;
+ value = NSSUTIL_Escape(value, '\"');
+ if (value == NULL) {
+ return target; /* couldn't escape value, just drop the copy */
+ }
+ }
+ PORT_Memcpy(target, desc, descLen);
+ target += descLen;
+ *target++ = '\"';
+ PORT_Memcpy(target, value, esc_len);
+ target += esc_len;
+ *target++ = '\"';
+ if (diff > 0) {
+ PORT_Free(value);
+ }
+ return target;
+}
+
+#define SECMOD_SPEC_COPY(new, start, end) \
+ if (end > start) { \
+ int _cnt = end - start; \
+ PORT_Memcpy(new, start, _cnt); \
+ new += _cnt; \
+ }
+#define SECMOD_TOKEN_DESCRIPTION "tokenDescription="
+#define SECMOD_SLOT_DESCRIPTION "slotDescription="
+
+/*
+ * Find any tokens= values in the module spec.
+ * Always return a new spec which does not have any tokens= arguments.
+ * If tokens= arguments are found, Split the the various tokens defined into
+ * an array of child specs to return.
+ *
+ * Caller is responsible for freeing the child spec and the new token
+ * spec.
+ */
+char *
+secmod_ParseModuleSpecForTokens(PRBool convert, PRBool isFIPS,
+ const char *moduleSpec, char ***children,
+ CK_SLOT_ID **ids)
+{
+ int newSpecLen = PORT_Strlen(moduleSpec) + 2;
+ char *newSpec = PORT_Alloc(newSpecLen);
+ char *newSpecPtr = newSpec;
+ const char *modulePrev = moduleSpec;
+ char *target = NULL;
+ char *tmp = NULL;
+ char **childArray = NULL;
+ const char *tokenIndex;
+ CK_SLOT_ID *idArray = NULL;
+ int tokenCount = 0;
+ int i;
+
+ if (newSpec == NULL) {
+ return NULL;
+ }
+
+ *children = NULL;
+ if (ids) {
+ *ids = NULL;
+ }
+ moduleSpec = NSSUTIL_ArgStrip(moduleSpec);
+ SECMOD_SPEC_COPY(newSpecPtr, modulePrev, moduleSpec);
+
+ /* Notes on 'convert' and 'isFIPS' flags: The base parameters for opening
+ * a new softoken module takes the following parameters to name the
+ * various tokens:
+ *
+ * cryptoTokenDescription: name of the non-fips crypto token.
+ * cryptoSlotDescription: name of the non-fips crypto slot.
+ * dbTokenDescription: name of the non-fips db token.
+ * dbSlotDescription: name of the non-fips db slot.
+ * FIPSTokenDescription: name of the fips db/crypto token.
+ * FIPSSlotDescription: name of the fips db/crypto slot.
+ *
+ * if we are opening a new slot, we need to have the following
+ * parameters:
+ * tokenDescription: name of the token.
+ * slotDescription: name of the slot.
+ *
+ *
+ * The convert flag tells us to drop the unnecessary *TokenDescription
+ * and *SlotDescription arguments and convert the appropriate pair
+ * (either db or FIPS based on the isFIPS flag) to tokenDescription and
+ * slotDescription).
+ */
+ /*
+ * walk down the list. if we find a tokens= argument, save it,
+ * otherise copy the argument.
+ */
+ while (*moduleSpec) {
+ int next;
+ modulePrev = moduleSpec;
+ NSSUTIL_HANDLE_STRING_ARG(moduleSpec, target, "tokens=",
+ modulePrev = moduleSpec;
+ /* skip copying */)
+ NSSUTIL_HANDLE_STRING_ARG(
+ moduleSpec, tmp, "cryptoTokenDescription=",
+ if (convert) { modulePrev = moduleSpec; })
+ NSSUTIL_HANDLE_STRING_ARG(
+ moduleSpec, tmp, "cryptoSlotDescription=",
+ if (convert) { modulePrev = moduleSpec; })
+ NSSUTIL_HANDLE_STRING_ARG(
+ moduleSpec, tmp, "dbTokenDescription=",
+ if (convert) {
+ modulePrev = moduleSpec;
+ if (!isFIPS) {
+ newSpecPtr = secmod_doDescCopy(newSpecPtr,
+ &newSpec, &newSpecLen,
+ SECMOD_TOKEN_DESCRIPTION,
+ sizeof(SECMOD_TOKEN_DESCRIPTION) - 1,
+ tmp);
+ }
+ })
+ NSSUTIL_HANDLE_STRING_ARG(
+ moduleSpec, tmp, "dbSlotDescription=",
+ if (convert) {
+ modulePrev = moduleSpec; /* skip copying */
+ if (!isFIPS) {
+ newSpecPtr = secmod_doDescCopy(newSpecPtr,
+ &newSpec, &newSpecLen,
+ SECMOD_SLOT_DESCRIPTION,
+ sizeof(SECMOD_SLOT_DESCRIPTION) - 1,
+ tmp);
+ }
+ })
+ NSSUTIL_HANDLE_STRING_ARG(
+ moduleSpec, tmp, "FIPSTokenDescription=",
+ if (convert) {
+ modulePrev = moduleSpec; /* skip copying */
+ if (isFIPS) {
+ newSpecPtr = secmod_doDescCopy(newSpecPtr,
+ &newSpec, &newSpecLen,
+ SECMOD_TOKEN_DESCRIPTION,
+ sizeof(SECMOD_TOKEN_DESCRIPTION) - 1,
+ tmp);
+ }
+ })
+ NSSUTIL_HANDLE_STRING_ARG(
+ moduleSpec, tmp, "FIPSSlotDescription=",
+ if (convert) {
+ modulePrev = moduleSpec; /* skip copying */
+ if (isFIPS) {
+ newSpecPtr = secmod_doDescCopy(newSpecPtr,
+ &newSpec, &newSpecLen,
+ SECMOD_SLOT_DESCRIPTION,
+ sizeof(SECMOD_SLOT_DESCRIPTION) - 1,
+ tmp);
+ }
+ })
+ NSSUTIL_HANDLE_FINAL_ARG(moduleSpec)
+ SECMOD_SPEC_COPY(newSpecPtr, modulePrev, moduleSpec);
+ }
+ if (tmp) {
+ PORT_Free(tmp);
+ tmp = NULL;
+ }
+ *newSpecPtr = 0;
+
+ /* no target found, return the newSpec */
+ if (target == NULL) {
+ return newSpec;
+ }
+
+ /* now build the child array from target */
+ /*first count them */
+ for (tokenIndex = NSSUTIL_ArgStrip(target); *tokenIndex;
+ tokenIndex = NSSUTIL_ArgStrip(NSSUTIL_ArgSkipParameter(tokenIndex))) {
+ tokenCount++;
+ }
+
+ childArray = PORT_NewArray(char *, tokenCount + 1);
+ if (childArray == NULL) {
+ /* just return the spec as is then */
+ PORT_Free(target);
+ return newSpec;
+ }
+ if (ids) {
+ idArray = PORT_NewArray(CK_SLOT_ID, tokenCount + 1);
+ if (idArray == NULL) {
+ PORT_Free(childArray);
+ PORT_Free(target);
+ return newSpec;
+ }
+ }
+
+ /* now fill them in */
+ for (tokenIndex = NSSUTIL_ArgStrip(target), i = 0;
+ *tokenIndex && (i < tokenCount);
+ tokenIndex = NSSUTIL_ArgStrip(tokenIndex)) {
+ int next;
+ char *name = NSSUTIL_ArgGetLabel(tokenIndex, &next);
+ tokenIndex += next;
+
+ if (idArray) {
+ idArray[i] = NSSUTIL_ArgDecodeNumber(name);
+ }
+
+ PORT_Free(name); /* drop the explicit number */
+
+ /* if anything is left, copy the args to the child array */
+ if (!NSSUTIL_ArgIsBlank(*tokenIndex)) {
+ childArray[i++] = NSSUTIL_ArgFetchValue(tokenIndex, &next);
+ tokenIndex += next;
+ }
+ }
+
+ PORT_Free(target);
+ childArray[i] = 0;
+ if (idArray) {
+ idArray[i] = 0;
+ }
+
+ /* return it */
+ *children = childArray;
+ if (ids) {
+ *ids = idArray;
+ }
+ return newSpec;
+}
+
+/* get the database and flags from the spec */
+static char *
+secmod_getConfigDir(const char *spec, char **certPrefix, char **keyPrefix,
+ PRBool *readOnly)
+{
+ char *config = NULL;
+
+ *certPrefix = NULL;
+ *keyPrefix = NULL;
+ *readOnly = NSSUTIL_ArgHasFlag("flags", "readOnly", spec);
+ if (NSSUTIL_ArgHasFlag("flags", "nocertdb", spec) ||
+ NSSUTIL_ArgHasFlag("flags", "nokeydb", spec)) {
+ return NULL;
+ }
+
+ spec = NSSUTIL_ArgStrip(spec);
+ while (*spec) {
+ int next;
+ NSSUTIL_HANDLE_STRING_ARG(spec, config, "configdir=", ;)
+ NSSUTIL_HANDLE_STRING_ARG(spec, *certPrefix, "certPrefix=", ;)
+ NSSUTIL_HANDLE_STRING_ARG(spec, *keyPrefix, "keyPrefix=", ;)
+ NSSUTIL_HANDLE_FINAL_ARG(spec)
+ }
+ return config;
+}
+
+struct SECMODConfigListStr {
+ char *config;
+ char *certPrefix;
+ char *keyPrefix;
+ PRBool isReadOnly;
+};
+
+/*
+ * return an array of already openned databases from a spec list.
+ */
+SECMODConfigList *
+secmod_GetConfigList(PRBool isFIPS, char *spec, int *count)
+{
+ char **children;
+ CK_SLOT_ID *ids;
+ char *strippedSpec;
+ int childCount;
+ SECMODConfigList *conflist = NULL;
+ int i;
+
+ strippedSpec = secmod_ParseModuleSpecForTokens(PR_TRUE, isFIPS,
+ spec, &children, &ids);
+ if (strippedSpec == NULL) {
+ return NULL;
+ }
+
+ for (childCount = 0; children && children[childCount]; childCount++)
+ ;
+ *count = childCount + 1; /* include strippedSpec */
+ conflist = PORT_NewArray(SECMODConfigList, *count);
+ if (conflist == NULL) {
+ *count = 0;
+ goto loser;
+ }
+
+ conflist[0].config = secmod_getConfigDir(strippedSpec,
+ &conflist[0].certPrefix,
+ &conflist[0].keyPrefix,
+ &conflist[0].isReadOnly);
+ for (i = 0; i < childCount; i++) {
+ conflist[i + 1].config = secmod_getConfigDir(children[i],
+ &conflist[i + 1].certPrefix,
+ &conflist[i + 1].keyPrefix,
+ &conflist[i + 1].isReadOnly);
+ }
+
+loser:
+ secmod_FreeChildren(children, ids);
+ PORT_Free(strippedSpec);
+ return conflist;
+}
+
+/*
+ * determine if we are trying to open an old dbm database. For this test
+ * RDB databases should return PR_FALSE.
+ */
+static PRBool
+secmod_configIsDBM(char *configDir)
+{
+ char *env;
+
+ /* explicit dbm open */
+ if (strncmp(configDir, "dbm:", 4) == 0) {
+ return PR_TRUE;
+ }
+ /* explicit open of a non-dbm database */
+ if ((strncmp(configDir, "sql:", 4) == 0) ||
+ (strncmp(configDir, "rdb:", 4) == 0) ||
+ (strncmp(configDir, "extern:", 7) == 0)) {
+ return PR_FALSE;
+ }
+ env = PR_GetEnvSecure("NSS_DEFAULT_DB_TYPE");
+ /* implicit dbm open */
+ if ((env == NULL) || (strcmp(env, "dbm") == 0)) {
+ return PR_TRUE;
+ }
+ /* implicit non-dbm open */
+ return PR_FALSE;
+}
+
+/*
+ * match two prefixes. prefix may be NULL. NULL patches '\0'
+ */
+static PRBool
+secmod_matchPrefix(char *prefix1, char *prefix2)
+{
+ if ((prefix1 == NULL) || (*prefix1 == 0)) {
+ if ((prefix2 == NULL) || (*prefix2 == 0)) {
+ return PR_TRUE;
+ }
+ return PR_FALSE;
+ }
+ if (strcmp(prefix1, prefix2) == 0) {
+ return PR_TRUE;
+ }
+ return PR_FALSE;
+}
+
+/* do two config paramters match? Not all callers are compariing
+ * SECMODConfigLists directly, so this function breaks them out to their
+ * components. */
+static PRBool
+secmod_matchConfig(char *configDir1, char *configDir2,
+ char *certPrefix1, char *certPrefix2,
+ char *keyPrefix1, char *keyPrefix2,
+ PRBool isReadOnly1, PRBool isReadOnly2)
+{
+ /* TODO: Document the answer to the question:
+ * "Why not allow them to match if they are both NULL?"
+ * See: https://bugzilla.mozilla.org/show_bug.cgi?id=1318633#c1
+ */
+ if ((configDir1 == NULL) || (configDir2 == NULL)) {
+ return PR_FALSE;
+ }
+ if (strcmp(configDir1, configDir2) != 0) {
+ return PR_FALSE;
+ }
+ if (!secmod_matchPrefix(certPrefix1, certPrefix2)) {
+ return PR_FALSE;
+ }
+ if (!secmod_matchPrefix(keyPrefix1, keyPrefix2)) {
+ return PR_FALSE;
+ }
+ /* these last test -- if we just need the DB open read only,
+ * than any open will suffice, but if we requested it read/write
+ * and it's only open read only, we need to open it again */
+ if (isReadOnly1) {
+ return PR_TRUE;
+ }
+ if (isReadOnly2) { /* isReadonly1 == PR_FALSE */
+ return PR_FALSE;
+ }
+ return PR_TRUE;
+}
+
+/*
+ * return true if we are requesting a database that is already openned.
+ */
+PRBool
+secmod_MatchConfigList(const char *spec, SECMODConfigList *conflist, int count)
+{
+ char *config;
+ char *certPrefix;
+ char *keyPrefix;
+ PRBool isReadOnly;
+ PRBool ret = PR_FALSE;
+ int i;
+
+ config = secmod_getConfigDir(spec, &certPrefix, &keyPrefix, &isReadOnly);
+ if (!config) {
+ goto done;
+ }
+
+ /* NOTE: we dbm isn't multiple open safe. If we open the same database
+ * twice from two different locations, then we can corrupt our database
+ * (the cache will be inconsistent). Protect against this by claiming
+ * for comparison only that we are always openning dbm databases read only.
+ */
+ if (secmod_configIsDBM(config)) {
+ isReadOnly = 1;
+ }
+ for (i = 0; i < count; i++) {
+ if (secmod_matchConfig(config, conflist[i].config, certPrefix,
+ conflist[i].certPrefix, keyPrefix,
+ conflist[i].keyPrefix, isReadOnly,
+ conflist[i].isReadOnly)) {
+ ret = PR_TRUE;
+ goto done;
+ }
+ }
+
+ ret = PR_FALSE;
+done:
+ PORT_Free(config);
+ PORT_Free(certPrefix);
+ PORT_Free(keyPrefix);
+ return ret;
+}
+
+/*
+ * Find the slot id from the module spec. If the slot is the database slot, we
+ * can get the slot id from the default database slot.
+ */
+CK_SLOT_ID
+secmod_GetSlotIDFromModuleSpec(const char *moduleSpec, SECMODModule *module)
+{
+ char *tmp_spec = NULL;
+ char **children, **thisChild;
+ CK_SLOT_ID *ids, *thisID, slotID = -1;
+ char *inConfig = NULL, *thisConfig = NULL;
+ char *inCertPrefix = NULL, *thisCertPrefix = NULL;
+ char *inKeyPrefix = NULL, *thisKeyPrefix = NULL;
+ PRBool inReadOnly, thisReadOnly;
+
+ inConfig = secmod_getConfigDir(moduleSpec, &inCertPrefix, &inKeyPrefix,
+ &inReadOnly);
+ if (!inConfig) {
+ goto done;
+ }
+
+ if (secmod_configIsDBM(inConfig)) {
+ inReadOnly = 1;
+ }
+
+ tmp_spec = secmod_ParseModuleSpecForTokens(PR_TRUE, module->isFIPS,
+ module->libraryParams, &children, &ids);
+ if (tmp_spec == NULL) {
+ goto done;
+ }
+
+ /* first check to see if the parent is the database */
+ thisConfig = secmod_getConfigDir(tmp_spec, &thisCertPrefix, &thisKeyPrefix,
+ &thisReadOnly);
+ if (!thisConfig) {
+ goto done;
+ }
+ if (secmod_matchConfig(inConfig, thisConfig, inCertPrefix, thisCertPrefix,
+ inKeyPrefix, thisKeyPrefix, inReadOnly, thisReadOnly)) {
+ /* yup it's the default key slot, get the id for it */
+ PK11SlotInfo *slot = PK11_GetInternalKeySlot();
+ if (slot) {
+ slotID = slot->slotID;
+ PK11_FreeSlot(slot);
+ }
+ goto done;
+ }
+
+ /* find id of the token */
+ for (thisChild = children, thisID = ids; thisChild && *thisChild; thisChild++, thisID++) {
+ PORT_Free(thisConfig);
+ PORT_Free(thisCertPrefix);
+ PORT_Free(thisKeyPrefix);
+ thisConfig = secmod_getConfigDir(*thisChild, &thisCertPrefix,
+ &thisKeyPrefix, &thisReadOnly);
+ if (thisConfig == NULL) {
+ continue;
+ }
+ if (secmod_matchConfig(inConfig, thisConfig, inCertPrefix, thisCertPrefix,
+ inKeyPrefix, thisKeyPrefix, inReadOnly, thisReadOnly)) {
+ slotID = *thisID;
+ break;
+ }
+ }
+
+done:
+ PORT_Free(inConfig);
+ PORT_Free(inCertPrefix);
+ PORT_Free(inKeyPrefix);
+ PORT_Free(thisConfig);
+ PORT_Free(thisCertPrefix);
+ PORT_Free(thisKeyPrefix);
+ if (tmp_spec) {
+ secmod_FreeChildren(children, ids);
+ PORT_Free(tmp_spec);
+ }
+ return slotID;
+}
+
+void
+secmod_FreeConfigList(SECMODConfigList *conflist, int count)
+{
+ int i;
+ for (i = 0; i < count; i++) {
+ PORT_Free(conflist[i].config);
+ PORT_Free(conflist[i].certPrefix);
+ PORT_Free(conflist[i].keyPrefix);
+ }
+ PORT_Free(conflist);
+}
+
+void
+secmod_FreeChildren(char **children, CK_SLOT_ID *ids)
+{
+ char **thisChild;
+
+ if (!children) {
+ return;
+ }
+
+ for (thisChild = children; thisChild && *thisChild; thisChild++) {
+ PORT_Free(*thisChild);
+ }
+ PORT_Free(children);
+ if (ids) {
+ PORT_Free(ids);
+ }
+ return;
+}
+
+/*
+ * caclulate the length of each child record:
+ * " 0x{id}=<{escaped_child}>"
+ */
+static int
+secmod_getChildLength(char *child, CK_SLOT_ID id)
+{
+ int length = NSSUTIL_DoubleEscapeSize(child, '>', ']');
+ if (id == 0) {
+ length++;
+ }
+ while (id) {
+ length++;
+ id = id >> 4;
+ }
+ length += 6; /* {sp}0x[id]=<{child}> */
+ return length;
+}
+
+/*
+ * Build a child record:
+ * " 0x{id}=<{escaped_child}>"
+ */
+static SECStatus
+secmod_mkTokenChild(char **next, int *length, char *child, CK_SLOT_ID id)
+{
+ int len;
+ char *escSpec;
+
+ len = PR_snprintf(*next, *length, " 0x%x=<", id);
+ if (len < 0) {
+ return SECFailure;
+ }
+ *next += len;
+ *length -= len;
+ escSpec = NSSUTIL_DoubleEscape(child, '>', ']');
+ if (escSpec == NULL) {
+ return SECFailure;
+ }
+ if (*child && (*escSpec == 0)) {
+ PORT_Free(escSpec);
+ return SECFailure;
+ }
+ len = strlen(escSpec);
+ if (len + 1 > *length) {
+ PORT_Free(escSpec);
+ return SECFailure;
+ }
+ PORT_Memcpy(*next, escSpec, len);
+ *next += len;
+ *length -= len;
+ PORT_Free(escSpec);
+ **next = '>';
+ (*next)++;
+ (*length)--;
+ return SECSuccess;
+}
+
+#define TOKEN_STRING " tokens=["
+
+char *
+secmod_MkAppendTokensList(PLArenaPool *arena, char *oldParam, char *newToken,
+ CK_SLOT_ID newID, char **children, CK_SLOT_ID *ids)
+{
+ char *rawParam = NULL; /* oldParam with tokens stripped off */
+ char *newParam = NULL; /* space for the return parameter */
+ char *nextParam = NULL; /* current end of the new parameter */
+ char **oldChildren = NULL;
+ CK_SLOT_ID *oldIds = NULL;
+ void *mark = NULL; /* mark the arena pool in case we need
+ * to release it */
+ int length, i, tmpLen;
+ SECStatus rv;
+
+ /* first strip out and save the old tokenlist */
+ rawParam = secmod_ParseModuleSpecForTokens(PR_FALSE, PR_FALSE,
+ oldParam, &oldChildren, &oldIds);
+ if (!rawParam) {
+ goto loser;
+ }
+
+ /* now calculate the total length of the new buffer */
+ /* First the 'fixed stuff', length of rawparam (does not include a NULL),
+ * length of the token string (does include the NULL), closing bracket */
+ length = strlen(rawParam) + sizeof(TOKEN_STRING) + 1;
+ /* now add then length of all the old children */
+ for (i = 0; oldChildren && oldChildren[i]; i++) {
+ length += secmod_getChildLength(oldChildren[i], oldIds[i]);
+ }
+
+ /* add the new token */
+ length += secmod_getChildLength(newToken, newID);
+
+ /* and it's new children */
+ for (i = 0; children && children[i]; i++) {
+ if (ids[i] == -1) {
+ continue;
+ }
+ length += secmod_getChildLength(children[i], ids[i]);
+ }
+
+ /* now allocate and build the string */
+ mark = PORT_ArenaMark(arena);
+ if (!mark) {
+ goto loser;
+ }
+ newParam = PORT_ArenaAlloc(arena, length);
+ if (!newParam) {
+ goto loser;
+ }
+
+ PORT_Strcpy(newParam, oldParam);
+ tmpLen = strlen(oldParam);
+ nextParam = newParam + tmpLen;
+ length -= tmpLen;
+ PORT_Memcpy(nextParam, TOKEN_STRING, sizeof(TOKEN_STRING) - 1);
+ nextParam += sizeof(TOKEN_STRING) - 1;
+ length -= sizeof(TOKEN_STRING) - 1;
+
+ for (i = 0; oldChildren && oldChildren[i]; i++) {
+ rv = secmod_mkTokenChild(&nextParam, &length, oldChildren[i], oldIds[i]);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ }
+
+ rv = secmod_mkTokenChild(&nextParam, &length, newToken, newID);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ for (i = 0; children && children[i]; i++) {
+ if (ids[i] == -1) {
+ continue;
+ }
+ rv = secmod_mkTokenChild(&nextParam, &length, children[i], ids[i]);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ }
+
+ if (length < 2) {
+ goto loser;
+ }
+
+ *nextParam++ = ']';
+ *nextParam++ = 0;
+
+ /* we are going to return newParam now, don't release the mark */
+ PORT_ArenaUnmark(arena, mark);
+ mark = NULL;
+
+loser:
+ if (mark) {
+ PORT_ArenaRelease(arena, mark);
+ newParam = NULL; /* if the mark is still active,
+ * don't return the param */
+ }
+ if (rawParam) {
+ PORT_Free(rawParam);
+ }
+ if (oldChildren) {
+ secmod_FreeChildren(oldChildren, oldIds);
+ }
+ return newParam;
+}
+
+static char *
+secmod_mkModuleSpec(SECMODModule *module)
+{
+ char *nss = NULL, *modSpec = NULL, **slotStrings = NULL;
+ int slotCount, i, si;
+ SECMODListLock *moduleLock = SECMOD_GetDefaultModuleListLock();
+
+ /* allocate target slot info strings */
+ slotCount = 0;
+
+ SECMOD_GetReadLock(moduleLock);
+ if (module->slotCount) {
+ for (i = 0; i < module->slotCount; i++) {
+ if (module->slots[i]->defaultFlags != 0) {
+ slotCount++;
+ }
+ }
+ } else {
+ slotCount = module->slotInfoCount;
+ }
+
+ slotStrings = (char **)PORT_ZAlloc(slotCount * sizeof(char *));
+ if (slotStrings == NULL) {
+ SECMOD_ReleaseReadLock(moduleLock);
+ goto loser;
+ }
+
+ /* build the slot info strings */
+ if (module->slotCount) {
+ for (i = 0, si = 0; i < module->slotCount; i++) {
+ if (module->slots[i]->defaultFlags) {
+ PORT_Assert(si < slotCount);
+ if (si >= slotCount)
+ break;
+ slotStrings[si] = NSSUTIL_MkSlotString(module->slots[i]->slotID,
+ module->slots[i]->defaultFlags,
+ module->slots[i]->timeout,
+ module->slots[i]->askpw,
+ module->slots[i]->hasRootCerts,
+ module->slots[i]->hasRootTrust);
+ si++;
+ }
+ }
+ } else {
+ for (i = 0; i < slotCount; i++) {
+ slotStrings[i] = NSSUTIL_MkSlotString(
+ module->slotInfo[i].slotID,
+ module->slotInfo[i].defaultFlags,
+ module->slotInfo[i].timeout,
+ module->slotInfo[i].askpw,
+ module->slotInfo[i].hasRootCerts,
+ module->slotInfo[i].hasRootTrust);
+ }
+ }
+
+ SECMOD_ReleaseReadLock(moduleLock);
+ nss = NSSUTIL_MkNSSString(slotStrings, slotCount, module->internal,
+ module->isFIPS, module->isModuleDB,
+ module->moduleDBOnly, module->isCritical,
+ module->trustOrder, module->cipherOrder,
+ module->ssl[0], module->ssl[1]);
+ modSpec = NSSUTIL_MkModuleSpec(module->dllName, module->commonName,
+ module->libraryParams, nss);
+ PORT_Free(slotStrings);
+ PR_smprintf_free(nss);
+loser:
+ return (modSpec);
+}
+
+char **
+SECMOD_GetModuleSpecList(SECMODModule *module)
+{
+ SECMODModuleDBFunc func = (SECMODModuleDBFunc)module->moduleDBFunc;
+ if (func) {
+ return (*func)(SECMOD_MODULE_DB_FUNCTION_FIND,
+ module->libraryParams, NULL);
+ }
+ return NULL;
+}
+
+SECStatus
+SECMOD_AddPermDB(SECMODModule *module)
+{
+ SECMODModuleDBFunc func;
+ char *moduleSpec;
+ char **retString;
+
+ if (module->parent == NULL)
+ return SECFailure;
+
+ func = (SECMODModuleDBFunc)module->parent->moduleDBFunc;
+ if (func) {
+ moduleSpec = secmod_mkModuleSpec(module);
+ retString = (*func)(SECMOD_MODULE_DB_FUNCTION_ADD,
+ module->parent->libraryParams, moduleSpec);
+ PORT_Free(moduleSpec);
+ if (retString != NULL)
+ return SECSuccess;
+ }
+ return SECFailure;
+}
+
+SECStatus
+SECMOD_DeletePermDB(SECMODModule *module)
+{
+ SECMODModuleDBFunc func;
+ char *moduleSpec;
+ char **retString;
+
+ if (module->parent == NULL)
+ return SECFailure;
+
+ func = (SECMODModuleDBFunc)module->parent->moduleDBFunc;
+ if (func) {
+ moduleSpec = secmod_mkModuleSpec(module);
+ retString = (*func)(SECMOD_MODULE_DB_FUNCTION_DEL,
+ module->parent->libraryParams, moduleSpec);
+ PORT_Free(moduleSpec);
+ if (retString != NULL)
+ return SECSuccess;
+ }
+ return SECFailure;
+}
+
+SECStatus
+SECMOD_FreeModuleSpecList(SECMODModule *module, char **moduleSpecList)
+{
+ SECMODModuleDBFunc func = (SECMODModuleDBFunc)module->moduleDBFunc;
+ char **retString;
+ if (func) {
+ retString = (*func)(SECMOD_MODULE_DB_FUNCTION_RELEASE,
+ module->libraryParams, moduleSpecList);
+ if (retString != NULL)
+ return SECSuccess;
+ }
+ return SECFailure;
+}
+
+/*
+ * load a PKCS#11 module but do not add it to the default NSS trust domain
+ */
+SECMODModule *
+SECMOD_LoadModule(char *modulespec, SECMODModule *parent, PRBool recurse)
+{
+ char *library = NULL, *moduleName = NULL, *parameters = NULL, *nss = NULL;
+ char *config = NULL;
+ SECStatus status;
+ SECMODModule *module = NULL;
+ SECMODModule *oldModule = NULL;
+ SECStatus rv;
+ PRBool forwardPolicyFeedback = PR_FALSE;
+ PRUint32 forwardPolicyCheckFlags;
+
+ /* initialize the underlying module structures */
+ SECMOD_Init();
+
+ status = NSSUTIL_ArgParseModuleSpecEx(modulespec, &library, &moduleName,
+ &parameters, &nss,
+ &config);
+ if (status != SECSuccess) {
+ goto loser;
+ }
+
+ module = SECMOD_CreateModuleEx(library, moduleName, parameters, nss, config);
+ forwardPolicyFeedback = NSSUTIL_ArgHasFlag("flags", "printPolicyFeedback", nss);
+ forwardPolicyCheckFlags = secmod_parsePolicyCheckFlags(nss);
+
+ if (library)
+ PORT_Free(library);
+ if (moduleName)
+ PORT_Free(moduleName);
+ if (parameters)
+ PORT_Free(parameters);
+ if (nss)
+ PORT_Free(nss);
+ if (config)
+ PORT_Free(config);
+ if (!module) {
+ goto loser;
+ }
+
+ /* a policy only stanza doesn't actually get 'loaded'. policy has already
+ * been parsed as a side effect of the CreateModuleEx call */
+ if (secmod_PolicyOnly(module)) {
+ return module;
+ }
+ if (parent) {
+ module->parent = SECMOD_ReferenceModule(parent);
+ if (module->internal && secmod_IsInternalKeySlot(parent)) {
+ module->internal = parent->internal;
+ }
+ }
+
+ /* load it */
+ rv = secmod_LoadPKCS11Module(module, &oldModule);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* if we just reload an old module, no need to add it to any lists.
+ * we simple release all our references */
+ if (oldModule) {
+ /* This module already exists, don't link it anywhere. This
+ * will probably destroy this module */
+ SECMOD_DestroyModule(module);
+ return oldModule;
+ }
+
+ if (recurse && module->isModuleDB) {
+ char **moduleSpecList;
+ PORT_SetError(0);
+
+ moduleSpecList = SECMOD_GetModuleSpecList(module);
+ if (moduleSpecList) {
+ char **index;
+
+ index = moduleSpecList;
+ if (*index && SECMOD_GetSkipFirstFlag(module)) {
+ index++;
+ }
+
+ for (; *index; index++) {
+ SECMODModule *child;
+ if (0 == PORT_Strcmp(*index, modulespec)) {
+ /* avoid trivial infinite recursion */
+ PORT_SetError(SEC_ERROR_NO_MODULE);
+ rv = SECFailure;
+ break;
+ }
+ if (!forwardPolicyFeedback) {
+ child = SECMOD_LoadModule(*index, module, PR_TRUE);
+ } else {
+ /* Add printPolicyFeedback to the nss flags */
+ char *specWithForwards =
+ NSSUTIL_AddNSSFlagToModuleSpec(*index, "printPolicyFeedback");
+ char *tmp;
+ if (forwardPolicyCheckFlags & SECMOD_FLAG_POLICY_CHECK_IDENTIFIER) {
+ tmp = NSSUTIL_AddNSSFlagToModuleSpec(specWithForwards, "policyCheckIdentifier");
+ PORT_Free(specWithForwards);
+ specWithForwards = tmp;
+ }
+ if (forwardPolicyCheckFlags & SECMOD_FLAG_POLICY_CHECK_VALUE) {
+ tmp = NSSUTIL_AddNSSFlagToModuleSpec(specWithForwards, "policyCheckValue");
+ PORT_Free(specWithForwards);
+ specWithForwards = tmp;
+ }
+ child = SECMOD_LoadModule(specWithForwards, module, PR_TRUE);
+ PORT_Free(specWithForwards);
+ }
+ if (!child)
+ break;
+ if (child->isCritical && !child->loaded) {
+ int err = PORT_GetError();
+ if (!err)
+ err = SEC_ERROR_NO_MODULE;
+ SECMOD_DestroyModule(child);
+ PORT_SetError(err);
+ rv = SECFailure;
+ break;
+ }
+ SECMOD_DestroyModule(child);
+ }
+ SECMOD_FreeModuleSpecList(module, moduleSpecList);
+ } else {
+ if (!PORT_GetError())
+ PORT_SetError(SEC_ERROR_NO_MODULE);
+ rv = SECFailure;
+ }
+ }
+
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* inherit the reference */
+ if (!module->moduleDBOnly) {
+ SECMOD_AddModuleToList(module);
+ } else {
+ SECMOD_AddModuleToDBOnlyList(module);
+ }
+
+ /* handle any additional work here */
+ return module;
+
+loser:
+ if (module) {
+ if (module->loaded) {
+ SECMOD_UnloadModule(module);
+ }
+ SECMOD_AddModuleToUnloadList(module);
+ }
+ return module;
+}
+
+/*
+ * load a PKCS#11 module and add it to the default NSS trust domain
+ */
+SECMODModule *
+SECMOD_LoadUserModule(char *modulespec, SECMODModule *parent, PRBool recurse)
+{
+ SECStatus rv = SECSuccess;
+ SECMODModule *newmod = SECMOD_LoadModule(modulespec, parent, recurse);
+ SECMODListLock *moduleLock = SECMOD_GetDefaultModuleListLock();
+
+ if (newmod) {
+ SECMOD_GetReadLock(moduleLock);
+ rv = STAN_AddModuleToDefaultTrustDomain(newmod);
+ SECMOD_ReleaseReadLock(moduleLock);
+ if (SECSuccess != rv) {
+ SECMOD_DestroyModule(newmod);
+ return NULL;
+ }
+ }
+ return newmod;
+}
+
+/*
+ * remove the PKCS#11 module from the default NSS trust domain, call
+ * C_Finalize, and destroy the module structure
+ */
+SECStatus
+SECMOD_UnloadUserModule(SECMODModule *mod)
+{
+ SECStatus rv = SECSuccess;
+ int atype = 0;
+ SECMODListLock *moduleLock = SECMOD_GetDefaultModuleListLock();
+ if (!mod) {
+ return SECFailure;
+ }
+
+ SECMOD_GetReadLock(moduleLock);
+ rv = STAN_RemoveModuleFromDefaultTrustDomain(mod);
+ SECMOD_ReleaseReadLock(moduleLock);
+ if (SECSuccess != rv) {
+ return SECFailure;
+ }
+ return SECMOD_DeleteModuleEx(NULL, mod, &atype, PR_FALSE);
+}
diff --git a/security/nss/lib/pk11wrap/pk11pbe.c b/security/nss/lib/pk11wrap/pk11pbe.c
new file mode 100644
index 0000000000..dfe4dee716
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11pbe.c
@@ -0,0 +1,1486 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "plarena.h"
+
+#include "blapit.h"
+#include "seccomon.h"
+#include "secitem.h"
+#include "secport.h"
+#include "hasht.h"
+#include "pkcs11t.h"
+#include "sechash.h"
+#include "secasn1.h"
+#include "secder.h"
+#include "secoid.h"
+#include "secerr.h"
+#include "secmod.h"
+#include "pk11func.h"
+#include "secpkcs5.h"
+#include "secmodi.h"
+#include "secmodti.h"
+#include "pkcs11.h"
+#include "pk11func.h"
+#include "secitem.h"
+#include "keyhi.h"
+
+typedef struct SEC_PKCS5PBEParameterStr SEC_PKCS5PBEParameter;
+struct SEC_PKCS5PBEParameterStr {
+ PLArenaPool *poolp;
+ SECItem salt; /* octet string */
+ SECItem iteration; /* integer */
+ SECItem keyLength; /* PKCS5v2 only */
+ SECAlgorithmID *pPrfAlgId; /* PKCS5v2 only */
+ SECAlgorithmID prfAlgId; /* PKCS5v2 only */
+};
+
+/* PKCS5 V2 has an algorithm ID for the encryption and for
+ * the key generation. This is valid for SEC_OID_PKCS5_PBES2
+ * and SEC_OID_PKCS5_PBMAC1
+ */
+struct sec_pkcs5V2ParameterStr {
+ PLArenaPool *poolp;
+ SECAlgorithmID pbeAlgId; /* real pbe algorithms */
+ SECAlgorithmID cipherAlgId; /* encryption/mac */
+};
+
+typedef struct sec_pkcs5V2ParameterStr sec_pkcs5V2Parameter;
+
+/* template for PKCS 5 PBE Parameter. This template has been expanded
+ * based upon the additions in PKCS 12. This should eventually be moved
+ * if RSA updates PKCS 5.
+ */
+const SEC_ASN1Template SEC_PKCS5PBEParameterTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(SEC_PKCS5PBEParameter) },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(SEC_PKCS5PBEParameter, salt) },
+ { SEC_ASN1_INTEGER,
+ offsetof(SEC_PKCS5PBEParameter, iteration) },
+ { 0 }
+};
+
+const SEC_ASN1Template SEC_V2PKCS12PBEParameterTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS5PBEParameter) },
+ { SEC_ASN1_OCTET_STRING, offsetof(SEC_PKCS5PBEParameter, salt) },
+ { SEC_ASN1_INTEGER, offsetof(SEC_PKCS5PBEParameter, iteration) },
+ { 0 }
+};
+
+SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
+
+/* SECOID_PKCS5_PBKDF2 */
+const SEC_ASN1Template SEC_PKCS5V2PBEParameterTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS5PBEParameter) },
+ /* This is really a choice, but since we only understand this
+ * choice, just inline it */
+ { SEC_ASN1_OCTET_STRING, offsetof(SEC_PKCS5PBEParameter, salt) },
+ { SEC_ASN1_INTEGER, offsetof(SEC_PKCS5PBEParameter, iteration) },
+ { SEC_ASN1_INTEGER | SEC_ASN1_OPTIONAL,
+ offsetof(SEC_PKCS5PBEParameter, keyLength) },
+ { SEC_ASN1_POINTER | SEC_ASN1_XTRN | SEC_ASN1_OPTIONAL,
+ offsetof(SEC_PKCS5PBEParameter, pPrfAlgId),
+ SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+ { 0 }
+};
+
+/* SEC_OID_PKCS5_PBES2, SEC_OID_PKCS5_PBMAC1 */
+const SEC_ASN1Template SEC_PKCS5V2ParameterTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS5PBEParameter) },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(sec_pkcs5V2Parameter, pbeAlgId),
+ SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+ offsetof(sec_pkcs5V2Parameter, cipherAlgId),
+ SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+ { 0 }
+};
+
+/*
+ * maps a PBE algorithm to a crypto algorithm. for PKCS12 and PKCS5v1
+ * for PKCS5v2 it returns SEC_OID_PKCS5_PBKDF2.
+ */
+SECOidTag
+sec_pkcs5GetCryptoFromAlgTag(SECOidTag algorithm)
+{
+ switch (algorithm) {
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC:
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_2KEY_TRIPLE_DES_CBC:
+ case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC:
+ return SEC_OID_DES_EDE3_CBC;
+ case SEC_OID_PKCS5_PBE_WITH_SHA1_AND_DES_CBC:
+ case SEC_OID_PKCS5_PBE_WITH_MD5_AND_DES_CBC:
+ case SEC_OID_PKCS5_PBE_WITH_MD2_AND_DES_CBC:
+ return SEC_OID_DES_CBC;
+ case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC:
+ case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC:
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC:
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC:
+ return SEC_OID_RC2_CBC;
+ case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC4:
+ case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC4:
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC4:
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC4:
+ return SEC_OID_RC4;
+ case SEC_OID_PKCS5_PBKDF2:
+ case SEC_OID_PKCS5_PBES2:
+ case SEC_OID_PKCS5_PBMAC1:
+ return SEC_OID_PKCS5_PBKDF2;
+ default:
+ break;
+ }
+
+ return SEC_OID_UNKNOWN;
+}
+
+/*
+ * get a new PKCS5 V2 Parameter from the algorithm id.
+ * if arena is passed in, use it, otherwise create a new arena.
+ */
+sec_pkcs5V2Parameter *
+sec_pkcs5_v2_get_v2_param(PLArenaPool *arena, SECAlgorithmID *algid)
+{
+ PLArenaPool *localArena = NULL;
+ sec_pkcs5V2Parameter *pbeV2_param;
+ SECStatus rv;
+
+ if (arena == NULL) {
+ localArena = arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if (arena == NULL) {
+ return NULL;
+ }
+ }
+ pbeV2_param = PORT_ArenaZNew(arena, sec_pkcs5V2Parameter);
+ if (pbeV2_param == NULL) {
+ goto loser;
+ }
+
+ rv = SEC_ASN1DecodeItem(arena, pbeV2_param,
+ SEC_PKCS5V2ParameterTemplate, &algid->parameters);
+ if (rv == SECFailure) {
+ goto loser;
+ }
+
+ pbeV2_param->poolp = arena;
+ return pbeV2_param;
+loser:
+ if (localArena) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ return NULL;
+}
+
+void
+sec_pkcs5_v2_destroy_v2_param(sec_pkcs5V2Parameter *param)
+{
+ if (param && param->poolp) {
+ PORT_FreeArena(param->poolp, PR_TRUE);
+ }
+}
+
+/* maps crypto algorithm from PBE algorithm.
+ */
+SECOidTag
+SEC_PKCS5GetCryptoAlgorithm(SECAlgorithmID *algid)
+{
+
+ SECOidTag pbeAlg;
+ SECOidTag cipherAlg;
+
+ if (algid == NULL)
+ return SEC_OID_UNKNOWN;
+
+ pbeAlg = SECOID_GetAlgorithmTag(algid);
+ cipherAlg = sec_pkcs5GetCryptoFromAlgTag(pbeAlg);
+ if ((cipherAlg == SEC_OID_PKCS5_PBKDF2) &&
+ (pbeAlg != SEC_OID_PKCS5_PBKDF2)) {
+ sec_pkcs5V2Parameter *pbeV2_param;
+ cipherAlg = SEC_OID_UNKNOWN;
+
+ pbeV2_param = sec_pkcs5_v2_get_v2_param(NULL, algid);
+ if (pbeV2_param != NULL) {
+ cipherAlg = SECOID_GetAlgorithmTag(&pbeV2_param->cipherAlgId);
+ sec_pkcs5_v2_destroy_v2_param(pbeV2_param);
+ }
+ }
+
+ return cipherAlg;
+}
+
+/* check to see if an oid is a pbe algorithm
+ */
+PRBool
+SEC_PKCS5IsAlgorithmPBEAlg(SECAlgorithmID *algid)
+{
+ return (PRBool)(SEC_PKCS5GetCryptoAlgorithm(algid) != SEC_OID_UNKNOWN);
+}
+
+PRBool
+SEC_PKCS5IsAlgorithmPBEAlgTag(SECOidTag algtag)
+{
+ return (PRBool)(sec_pkcs5GetCryptoFromAlgTag(algtag) != SEC_OID_UNKNOWN);
+}
+
+/*
+ * find the most appropriate PKCS5v2 overall oid tag from a regular
+ * cipher/hash algorithm tag.
+ */
+static SECOidTag
+sec_pkcs5v2_get_pbe(SECOidTag algTag)
+{
+ /* if it's a valid hash oid... */
+ if (HASH_GetHashOidTagByHMACOidTag(algTag) != SEC_OID_UNKNOWN) {
+ /* use the MAC tag */
+ return SEC_OID_PKCS5_PBMAC1;
+ }
+ if (HASH_GetHashTypeByOidTag(algTag) != HASH_AlgNULL) {
+ /* eliminate Hash algorithms */
+ return SEC_OID_UNKNOWN;
+ }
+ if (PK11_AlgtagToMechanism(algTag) != CKM_INVALID_MECHANISM) {
+ /* it's not a hash, if it has a PKCS #11 mechanism associated
+ * with it, assume it's a cipher. (NOTE this will generate
+ * some false positives). */
+ return SEC_OID_PKCS5_PBES2;
+ }
+ return SEC_OID_UNKNOWN;
+}
+
+/*
+ * maps PBE algorithm from crypto algorithm, assumes SHA1 hashing.
+ * input keyLen in bits.
+ */
+SECOidTag
+SEC_PKCS5GetPBEAlgorithm(SECOidTag algTag, int keyLen)
+{
+ switch (algTag) {
+ case SEC_OID_DES_EDE3_CBC:
+ switch (keyLen) {
+ case 168:
+ case 192:
+ case 0:
+ return SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC;
+ case 128:
+ case 92:
+ return SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_2KEY_TRIPLE_DES_CBC;
+ default:
+ break;
+ }
+ break;
+ case SEC_OID_DES_CBC:
+ return SEC_OID_PKCS5_PBE_WITH_SHA1_AND_DES_CBC;
+ case SEC_OID_RC2_CBC:
+ switch (keyLen) {
+ case 40:
+ return SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC;
+ case 128:
+ case 0:
+ return SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC;
+ default:
+ break;
+ }
+ break;
+ case SEC_OID_RC4:
+ switch (keyLen) {
+ case 40:
+ return SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC4;
+ case 128:
+ case 0:
+ return SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC4;
+ default:
+ break;
+ }
+ break;
+ default:
+ return sec_pkcs5v2_get_pbe(algTag);
+ }
+
+ return SEC_OID_UNKNOWN;
+}
+
+/*
+ * Some oids encode the key size in the oid, while the actual PKCS
+ * PKCS #11 mechanism does not. In those cases we can't use
+ * the PKCS #11 automated key length code to select the key size.
+ */
+static int
+sec_pkcs5v2_key_length_by_oid(SECOidTag algorithm)
+{
+ switch (algorithm) {
+ case SEC_OID_AES_128_CBC:
+ case SEC_OID_CAMELLIA_128_CBC:
+ return AES_128_KEY_LENGTH;
+ case SEC_OID_AES_192_CBC:
+ case SEC_OID_CAMELLIA_192_CBC:
+ return AES_192_KEY_LENGTH;
+ case SEC_OID_AES_256_CBC:
+ case SEC_OID_CAMELLIA_256_CBC:
+ return AES_256_KEY_LENGTH;
+ default:
+ break;
+ }
+ return -1;
+}
+
+/* find the keylength from the algorithm id */
+static int
+sec_pkcs5v2_default_key_length(SECOidTag algorithm)
+{
+ CK_MECHANISM_TYPE cryptoMech;
+ int key_length = sec_pkcs5v2_key_length_by_oid(algorithm);
+ if (key_length != -1) {
+ return key_length;
+ }
+ cryptoMech = PK11_AlgtagToMechanism(algorithm);
+ if (cryptoMech == CKM_INVALID_MECHANISM) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ return -1;
+ }
+ return PK11_GetMaxKeyLength(cryptoMech);
+}
+
+/*
+ * get the key length in bytes from a PKCS5 PBE
+ */
+static int
+sec_pkcs5v2_key_length(SECAlgorithmID *algid, SECAlgorithmID *cipherAlgId)
+{
+ SECOidTag algorithm;
+ PLArenaPool *arena = NULL;
+ SEC_PKCS5PBEParameter p5_param;
+ SECStatus rv;
+ int length = -1;
+ SECOidTag cipherAlg = SEC_OID_UNKNOWN;
+
+ algorithm = SECOID_GetAlgorithmTag(algid);
+ /* sanity check, they should all be PBKDF2 here */
+ if (algorithm != SEC_OID_PKCS5_PBKDF2) {
+ return -1;
+ }
+
+ arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if (arena == NULL) {
+ goto loser;
+ }
+ PORT_Memset(&p5_param, 0, sizeof(p5_param));
+ rv = SEC_ASN1DecodeItem(arena, &p5_param,
+ SEC_PKCS5V2PBEParameterTemplate, &algid->parameters);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ if (cipherAlgId)
+ cipherAlg = SECOID_GetAlgorithmTag(cipherAlgId);
+
+ if (p5_param.keyLength.data != NULL) {
+ /* if the length is given, accept that length. This
+ * will allow us to decode old NSS encrypted data
+ * where we used the MAX keysize for the algorithm,
+ * but put an incorrect header for a different keysize.
+ */
+ length = DER_GetInteger(&p5_param.keyLength);
+ } else {
+ /* if the keylength was not specified, figure it
+ * out from the oid */
+ length = sec_pkcs5v2_default_key_length(cipherAlg);
+ }
+
+loser:
+ if (arena) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ return length;
+}
+
+/*
+ * get the key length in bytes needed for the PBE algorithm
+ */
+int
+SEC_PKCS5GetKeyLength(SECAlgorithmID *algid)
+{
+
+ SECOidTag algorithm;
+
+ if (algid == NULL)
+ return SEC_OID_UNKNOWN;
+
+ algorithm = SECOID_GetAlgorithmTag(algid);
+
+ switch (algorithm) {
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC:
+ case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC:
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_2KEY_TRIPLE_DES_CBC:
+ return 24;
+ case SEC_OID_PKCS5_PBE_WITH_MD2_AND_DES_CBC:
+ case SEC_OID_PKCS5_PBE_WITH_SHA1_AND_DES_CBC:
+ case SEC_OID_PKCS5_PBE_WITH_MD5_AND_DES_CBC:
+ return 8;
+ case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC:
+ case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC4:
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC4:
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC:
+ return 5;
+ case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC:
+ case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC4:
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC:
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC4:
+ return 16;
+ case SEC_OID_PKCS5_PBKDF2:
+ return sec_pkcs5v2_key_length(algid, NULL);
+ case SEC_OID_PKCS5_PBES2:
+ case SEC_OID_PKCS5_PBMAC1: {
+ sec_pkcs5V2Parameter *pbeV2_param;
+ int length = -1;
+ pbeV2_param = sec_pkcs5_v2_get_v2_param(NULL, algid);
+ if (pbeV2_param != NULL) {
+ length = sec_pkcs5v2_key_length(&pbeV2_param->pbeAlgId,
+ &pbeV2_param->cipherAlgId);
+ sec_pkcs5_v2_destroy_v2_param(pbeV2_param);
+ }
+ return length;
+ }
+
+ default:
+ break;
+ }
+ return -1;
+}
+
+/* the PKCS12 V2 algorithms only encode the salt, there is no iteration
+ * count so we need a check for V2 algorithm parameters.
+ */
+static PRBool
+sec_pkcs5_is_algorithm_v2_pkcs12_algorithm(SECOidTag algorithm)
+{
+ switch (algorithm) {
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC4:
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC4:
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC:
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_2KEY_TRIPLE_DES_CBC:
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC:
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC:
+ return PR_TRUE;
+ default:
+ break;
+ }
+
+ return PR_FALSE;
+}
+
+static PRBool
+sec_pkcs5_is_algorithm_v2_pkcs5_algorithm(SECOidTag algorithm)
+{
+ switch (algorithm) {
+ case SEC_OID_PKCS5_PBES2:
+ case SEC_OID_PKCS5_PBMAC1:
+ case SEC_OID_PKCS5_PBKDF2:
+ return PR_TRUE;
+ default:
+ break;
+ }
+
+ return PR_FALSE;
+}
+
+/* destroy a pbe parameter. it assumes that the parameter was
+ * generated using the appropriate create function and therefor
+ * contains an arena pool.
+ */
+static void
+sec_pkcs5_destroy_pbe_param(SEC_PKCS5PBEParameter *pbe_param)
+{
+ if (pbe_param != NULL)
+ PORT_FreeArena(pbe_param->poolp, PR_TRUE);
+}
+
+/* creates a PBE parameter based on the PBE algorithm. the only required
+ * parameters are algorithm and interation. the return is a PBE parameter
+ * which conforms to PKCS 5 parameter unless an extended parameter is needed.
+ * this is primarily if keyLength and a variable key length algorithm are
+ * specified.
+ * salt - if null, a salt will be generated from random bytes.
+ * iteration - number of iterations to perform hashing.
+ * keyLength - only used in variable key length algorithms. if specified,
+ * should be in bytes.
+ * once a parameter is allocated, it should be destroyed calling
+ * sec_pkcs5_destroy_pbe_parameter or SEC_PKCS5DestroyPBEParameter.
+ */
+#define DEFAULT_SALT_LENGTH 16
+static SEC_PKCS5PBEParameter *
+sec_pkcs5_create_pbe_parameter(SECOidTag algorithm,
+ SECItem *salt,
+ int iteration,
+ int keyLength,
+ SECOidTag prfAlg)
+{
+ PLArenaPool *poolp = NULL;
+ SEC_PKCS5PBEParameter *pbe_param = NULL;
+ SECStatus rv = SECSuccess;
+ void *dummy = NULL;
+
+ if (iteration < 0) {
+ return NULL;
+ }
+
+ poolp = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if (poolp == NULL)
+ return NULL;
+
+ pbe_param = (SEC_PKCS5PBEParameter *)PORT_ArenaZAlloc(poolp,
+ sizeof(SEC_PKCS5PBEParameter));
+ if (!pbe_param) {
+ PORT_FreeArena(poolp, PR_TRUE);
+ return NULL;
+ }
+
+ pbe_param->poolp = poolp;
+
+ rv = SECFailure;
+ if (salt && salt->data) {
+ rv = SECITEM_CopyItem(poolp, &pbe_param->salt, salt);
+ } else {
+ /* sigh, the old interface generated salt on the fly, so we have to
+ * preserve the semantics */
+ pbe_param->salt.len = DEFAULT_SALT_LENGTH;
+ pbe_param->salt.data = PORT_ArenaZAlloc(poolp, DEFAULT_SALT_LENGTH);
+ if (pbe_param->salt.data) {
+ rv = PK11_GenerateRandom(pbe_param->salt.data, DEFAULT_SALT_LENGTH);
+ }
+ }
+
+ if (rv != SECSuccess) {
+ PORT_FreeArena(poolp, PR_TRUE);
+ return NULL;
+ }
+
+ /* encode the integer */
+ dummy = SEC_ASN1EncodeInteger(poolp, &pbe_param->iteration,
+ iteration);
+ rv = (dummy) ? SECSuccess : SECFailure;
+
+ if (rv != SECSuccess) {
+ PORT_FreeArena(poolp, PR_FALSE);
+ return NULL;
+ }
+
+ /*
+ * for PKCS5 v2 Add the keylength and the prf
+ */
+ if (algorithm == SEC_OID_PKCS5_PBKDF2) {
+ dummy = SEC_ASN1EncodeInteger(poolp, &pbe_param->keyLength,
+ keyLength);
+ rv = (dummy) ? SECSuccess : SECFailure;
+ if (rv != SECSuccess) {
+ PORT_FreeArena(poolp, PR_FALSE);
+ return NULL;
+ }
+ rv = SECOID_SetAlgorithmID(poolp, &pbe_param->prfAlgId, prfAlg, NULL);
+ if (rv != SECSuccess) {
+ PORT_FreeArena(poolp, PR_FALSE);
+ return NULL;
+ }
+ pbe_param->pPrfAlgId = &pbe_param->prfAlgId;
+ }
+
+ return pbe_param;
+}
+
+/* creates a algorithm ID containing the PBE algorithm and appropriate
+ * parameters. the required parameter is the algorithm. if salt is
+ * not specified, it is generated randomly.
+ *
+ * the returned SECAlgorithmID should be destroyed using
+ * SECOID_DestroyAlgorithmID
+ */
+SECAlgorithmID *
+sec_pkcs5CreateAlgorithmID(SECOidTag algorithm,
+ SECOidTag cipherAlgorithm,
+ SECOidTag prfAlg,
+ SECOidTag *pPbeAlgorithm,
+ int keyLength,
+ SECItem *salt,
+ int iteration)
+{
+ PLArenaPool *poolp = NULL;
+ SECAlgorithmID *algid, *ret_algid = NULL;
+ SECOidTag pbeAlgorithm = algorithm;
+ SECItem der_param;
+ void *dummy;
+ SECStatus rv = SECFailure;
+ SEC_PKCS5PBEParameter *pbe_param = NULL;
+ sec_pkcs5V2Parameter pbeV2_param;
+
+ if (iteration <= 0) {
+ return NULL;
+ }
+
+ poolp = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if (!poolp) {
+ goto loser;
+ }
+
+ if (!SEC_PKCS5IsAlgorithmPBEAlgTag(algorithm) ||
+ sec_pkcs5_is_algorithm_v2_pkcs5_algorithm(algorithm)) {
+ /* use PKCS 5 v2 */
+ SECItem *cipherParams;
+
+ /*
+ * if we ask for pkcs5 Algorithms directly, then the
+ * application needs to supply the cipher algorithm,
+ * otherwise we are implicitly using pkcs5 v2 and the
+ * passed in algorithm is the encryption algorithm.
+ */
+ if (sec_pkcs5_is_algorithm_v2_pkcs5_algorithm(algorithm)) {
+ if (cipherAlgorithm == SEC_OID_UNKNOWN) {
+ goto loser;
+ }
+ } else {
+ cipherAlgorithm = algorithm;
+ /* force algorithm to be chosen below */
+ algorithm = SEC_OID_PKCS5_PBKDF2;
+ }
+
+ pbeAlgorithm = SEC_OID_PKCS5_PBKDF2;
+ /*
+ * 'algorithm' is the overall algorithm oid tag used to wrap the
+ * entire algorithm ID block. For PKCS5v1 and PKCS12, this
+ * algorithm OID has encoded in it both the PBE KDF function
+ * and the encryption algorithm. For PKCS 5v2, PBE KDF and
+ * encryption/macing oids are encoded as parameters in
+ * the algorithm ID block.
+ *
+ * Thus in PKCS5 v1 and PKCS12, this algorithm maps to a pkcs #11
+ * mechanism, where as in PKCS 5v2, this algorithm tag does not map
+ * directly to a PKCS #11 mechanim, instead the 2 oids in the
+ * algorithm ID block map the the actual PKCS #11 mechanism.
+ * algorithm is). We use choose this algorithm oid based on the
+ * cipherAlgorithm to determine what this should be (MAC1 or PBES2).
+ */
+ if (algorithm == SEC_OID_PKCS5_PBKDF2) {
+ /* choose mac or pbes */
+ algorithm = sec_pkcs5v2_get_pbe(cipherAlgorithm);
+ }
+
+ /* set the PKCS5v2 specific parameters */
+ if (keyLength == 0) {
+ SECOidTag hashAlg = HASH_GetHashOidTagByHMACOidTag(cipherAlgorithm);
+ if (hashAlg != SEC_OID_UNKNOWN) {
+ keyLength = HASH_ResultLenByOidTag(hashAlg);
+ } else {
+ keyLength = sec_pkcs5v2_default_key_length(cipherAlgorithm);
+ }
+ if (keyLength <= 0) {
+ goto loser;
+ }
+ }
+ /* currently SEC_OID_HMAC_SHA1 is the default */
+ if (prfAlg == SEC_OID_UNKNOWN) {
+ prfAlg = SEC_OID_HMAC_SHA1;
+ }
+
+ /* build the PKCS5v2 cipher algorithm id */
+ cipherParams = pk11_GenerateNewParamWithKeyLen(
+ PK11_AlgtagToMechanism(cipherAlgorithm), keyLength);
+ if (!cipherParams) {
+ goto loser;
+ }
+
+ PORT_Memset(&pbeV2_param, 0, sizeof(pbeV2_param));
+
+ rv = PK11_ParamToAlgid(cipherAlgorithm, cipherParams,
+ poolp, &pbeV2_param.cipherAlgId);
+ SECITEM_FreeItem(cipherParams, PR_TRUE);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ }
+
+ /* generate the parameter */
+ pbe_param = sec_pkcs5_create_pbe_parameter(pbeAlgorithm, salt, iteration,
+ keyLength, prfAlg);
+ if (!pbe_param) {
+ goto loser;
+ }
+
+ /* generate the algorithm id */
+ algid = (SECAlgorithmID *)PORT_ArenaZAlloc(poolp, sizeof(SECAlgorithmID));
+ if (algid == NULL) {
+ goto loser;
+ }
+
+ der_param.data = NULL;
+ der_param.len = 0;
+ if (sec_pkcs5_is_algorithm_v2_pkcs5_algorithm(algorithm)) {
+ /* first encode the PBE algorithm ID */
+ dummy = SEC_ASN1EncodeItem(poolp, &der_param, pbe_param,
+ SEC_PKCS5V2PBEParameterTemplate);
+ if (dummy == NULL) {
+ goto loser;
+ }
+ rv = SECOID_SetAlgorithmID(poolp, &pbeV2_param.pbeAlgId,
+ pbeAlgorithm, &der_param);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* now encode the Full PKCS 5 parameter */
+ der_param.data = NULL;
+ der_param.len = 0;
+ dummy = SEC_ASN1EncodeItem(poolp, &der_param, &pbeV2_param,
+ SEC_PKCS5V2ParameterTemplate);
+ } else if (!sec_pkcs5_is_algorithm_v2_pkcs12_algorithm(algorithm)) {
+ dummy = SEC_ASN1EncodeItem(poolp, &der_param, pbe_param,
+ SEC_PKCS5PBEParameterTemplate);
+ } else {
+ dummy = SEC_ASN1EncodeItem(poolp, &der_param, pbe_param,
+ SEC_V2PKCS12PBEParameterTemplate);
+ }
+ if (dummy == NULL) {
+ goto loser;
+ }
+
+ rv = SECOID_SetAlgorithmID(poolp, algid, algorithm, &der_param);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ ret_algid = (SECAlgorithmID *)PORT_ZAlloc(sizeof(SECAlgorithmID));
+ if (ret_algid == NULL) {
+ goto loser;
+ }
+
+ rv = SECOID_CopyAlgorithmID(NULL, ret_algid, algid);
+ if (rv != SECSuccess) {
+ SECOID_DestroyAlgorithmID(ret_algid, PR_TRUE);
+ ret_algid = NULL;
+ } else if (pPbeAlgorithm) {
+ *pPbeAlgorithm = pbeAlgorithm;
+ }
+
+loser:
+ if (poolp != NULL) {
+ PORT_FreeArena(poolp, PR_TRUE);
+ algid = NULL;
+ }
+
+ if (pbe_param) {
+ sec_pkcs5_destroy_pbe_param(pbe_param);
+ }
+
+ return ret_algid;
+}
+
+SECStatus
+pbe_PK11AlgidToParam(SECAlgorithmID *algid, SECItem *mech)
+{
+ SEC_PKCS5PBEParameter p5_param;
+ SECItem *salt = NULL;
+ SECOidTag algorithm = SECOID_GetAlgorithmTag(algid);
+ PLArenaPool *arena = NULL;
+ SECStatus rv = SECFailure;
+ unsigned char *paramData = NULL;
+ unsigned char *pSalt = NULL;
+ CK_ULONG iterations;
+ int paramLen = 0;
+ int iv_len;
+
+ arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if (arena == NULL) {
+ goto loser;
+ }
+
+ /*
+ * decode the algid based on the pbe type
+ */
+ PORT_Memset(&p5_param, 0, sizeof(p5_param));
+ if (sec_pkcs5_is_algorithm_v2_pkcs12_algorithm(algorithm)) {
+ iv_len = PK11_GetIVLength(PK11_AlgtagToMechanism(algorithm));
+ rv = SEC_ASN1DecodeItem(arena, &p5_param,
+ SEC_V2PKCS12PBEParameterTemplate, &algid->parameters);
+ } else if (algorithm == SEC_OID_PKCS5_PBKDF2) {
+ iv_len = 0;
+ rv = SEC_ASN1DecodeItem(arena, &p5_param,
+ SEC_PKCS5V2PBEParameterTemplate, &algid->parameters);
+ } else {
+ iv_len = PK11_GetIVLength(PK11_AlgtagToMechanism(algorithm));
+ rv = SEC_ASN1DecodeItem(arena, &p5_param, SEC_PKCS5PBEParameterTemplate,
+ &algid->parameters);
+ }
+
+ if (iv_len < 0) {
+ goto loser;
+ }
+
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* get salt */
+ salt = &p5_param.salt;
+ iterations = (CK_ULONG)DER_GetInteger(&p5_param.iteration);
+
+ /* allocate and fill in the PKCS #11 parameters
+ * based on the algorithm. */
+ if (algorithm == SEC_OID_PKCS5_PBKDF2) {
+ SECOidTag prfAlgTag;
+ CK_PKCS5_PBKD2_PARAMS *pbeV2_params =
+ (CK_PKCS5_PBKD2_PARAMS *)PORT_ZAlloc(
+ sizeof(CK_PKCS5_PBKD2_PARAMS) + salt->len);
+
+ if (pbeV2_params == NULL) {
+ goto loser;
+ }
+ paramData = (unsigned char *)pbeV2_params;
+ paramLen = sizeof(CK_PKCS5_PBKD2_PARAMS);
+
+ /* set the prf */
+ prfAlgTag = SEC_OID_HMAC_SHA1;
+ if (p5_param.pPrfAlgId &&
+ p5_param.pPrfAlgId->algorithm.data != 0) {
+ prfAlgTag = SECOID_GetAlgorithmTag(p5_param.pPrfAlgId);
+ }
+ switch (prfAlgTag) {
+ case SEC_OID_HMAC_SHA1:
+ pbeV2_params->prf = CKP_PKCS5_PBKD2_HMAC_SHA1;
+ break;
+ case SEC_OID_HMAC_SHA224:
+ pbeV2_params->prf = CKP_PKCS5_PBKD2_HMAC_SHA224;
+ break;
+ case SEC_OID_HMAC_SHA256:
+ pbeV2_params->prf = CKP_PKCS5_PBKD2_HMAC_SHA256;
+ break;
+ case SEC_OID_HMAC_SHA384:
+ pbeV2_params->prf = CKP_PKCS5_PBKD2_HMAC_SHA384;
+ break;
+ case SEC_OID_HMAC_SHA512:
+ pbeV2_params->prf = CKP_PKCS5_PBKD2_HMAC_SHA512;
+ break;
+ default:
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ goto loser;
+ }
+
+ /* probably should fetch these from the prfAlgid */
+ pbeV2_params->pPrfData = NULL;
+ pbeV2_params->ulPrfDataLen = 0;
+ pbeV2_params->saltSource = CKZ_SALT_SPECIFIED;
+ pSalt = ((CK_CHAR_PTR)pbeV2_params) + sizeof(CK_PKCS5_PBKD2_PARAMS);
+ if (salt->data) {
+ PORT_Memcpy(pSalt, salt->data, salt->len);
+ }
+ pbeV2_params->pSaltSourceData = pSalt;
+ pbeV2_params->ulSaltSourceDataLen = salt->len;
+ pbeV2_params->iterations = iterations;
+ } else {
+ CK_PBE_PARAMS *pbe_params = NULL;
+ pbe_params = (CK_PBE_PARAMS *)PORT_ZAlloc(sizeof(CK_PBE_PARAMS) +
+ salt->len + iv_len);
+ if (pbe_params == NULL) {
+ goto loser;
+ }
+ paramData = (unsigned char *)pbe_params;
+ paramLen = sizeof(CK_PBE_PARAMS);
+
+ pSalt = ((CK_CHAR_PTR)pbe_params) + sizeof(CK_PBE_PARAMS);
+ pbe_params->pSalt = pSalt;
+ if (salt->data) {
+ PORT_Memcpy(pSalt, salt->data, salt->len);
+ }
+ pbe_params->ulSaltLen = salt->len;
+ if (iv_len) {
+ pbe_params->pInitVector =
+ ((CK_CHAR_PTR)pbe_params) + sizeof(CK_PBE_PARAMS) + salt->len;
+ }
+ pbe_params->ulIteration = iterations;
+ }
+
+ /* copy into the mechanism sec item */
+ mech->data = paramData;
+ mech->len = paramLen;
+ if (arena) {
+ PORT_FreeArena(arena, PR_TRUE);
+ }
+ return SECSuccess;
+
+loser:
+ if (paramData) {
+ PORT_Free(paramData);
+ }
+ if (arena) {
+ PORT_FreeArena(arena, PR_TRUE);
+ }
+ return SECFailure;
+}
+
+/*
+ * public, deprecated, not valid for pkcs5 v2
+ *
+ * use PK11_CreatePBEV2AlgorithmID or PK11_CreatePBEAlgorithmID to create
+ * PBE algorithmID's directly.
+ */
+SECStatus
+PBE_PK11ParamToAlgid(SECOidTag algTag, SECItem *param, PLArenaPool *arena,
+ SECAlgorithmID *algId)
+{
+ CK_PBE_PARAMS *pbe_param;
+ SECItem pbeSalt;
+ SECAlgorithmID *pbeAlgID = NULL;
+ SECStatus rv;
+
+ if (!param || !algId) {
+ return SECFailure;
+ }
+
+ pbe_param = (CK_PBE_PARAMS *)param->data;
+ pbeSalt.data = (unsigned char *)pbe_param->pSalt;
+ pbeSalt.len = pbe_param->ulSaltLen;
+ pbeAlgID = sec_pkcs5CreateAlgorithmID(algTag, SEC_OID_UNKNOWN,
+ SEC_OID_UNKNOWN, NULL, 0,
+ &pbeSalt, (int)pbe_param->ulIteration);
+ if (!pbeAlgID) {
+ return SECFailure;
+ }
+
+ rv = SECOID_CopyAlgorithmID(arena, algId, pbeAlgID);
+ SECOID_DestroyAlgorithmID(pbeAlgID, PR_TRUE);
+ return rv;
+}
+
+/*
+ * public, Deprecated, This function is only for binary compatibility with
+ * older applications. Does not support PKCS5v2.
+ *
+ * Applications should use PK11_PBEKeyGen() for keys and PK11_GetPBEIV() for
+ * iv values rather than generating PBE bits directly.
+ */
+PBEBitGenContext *
+PBE_CreateContext(SECOidTag hashAlgorithm, PBEBitGenID bitGenPurpose,
+ SECItem *pwitem, SECItem *salt, unsigned int bitsNeeded,
+ unsigned int iterations)
+{
+ SECItem *context = NULL;
+ SECItem mechItem;
+ CK_PBE_PARAMS pbe_params;
+ CK_MECHANISM_TYPE mechanism = CKM_INVALID_MECHANISM;
+ PK11SlotInfo *slot;
+ PK11SymKey *symKey = NULL;
+ unsigned char ivData[8];
+
+ /* use the purpose to select the low level keygen algorithm */
+ switch (bitGenPurpose) {
+ case pbeBitGenIntegrityKey:
+ switch (hashAlgorithm) {
+ case SEC_OID_SHA1:
+ mechanism = CKM_PBA_SHA1_WITH_SHA1_HMAC;
+ break;
+ case SEC_OID_MD2:
+ mechanism = CKM_NSS_PBE_MD2_HMAC_KEY_GEN;
+ break;
+ case SEC_OID_MD5:
+ mechanism = CKM_NSS_PBE_MD5_HMAC_KEY_GEN;
+ break;
+ default:
+ break;
+ }
+ break;
+ case pbeBitGenCipherIV:
+ if (bitsNeeded > 64) {
+ break;
+ }
+ if (hashAlgorithm != SEC_OID_SHA1) {
+ break;
+ }
+ mechanism = CKM_PBE_SHA1_DES3_EDE_CBC;
+ break;
+ case pbeBitGenCipherKey:
+ if (hashAlgorithm != SEC_OID_SHA1) {
+ break;
+ }
+ switch (bitsNeeded) {
+ case 40:
+ mechanism = CKM_PBE_SHA1_RC4_40;
+ break;
+ case 128:
+ mechanism = CKM_PBE_SHA1_RC4_128;
+ break;
+ default:
+ break;
+ }
+ case pbeBitGenIDNull:
+ break;
+ }
+
+ if (mechanism == CKM_INVALID_MECHANISM) {
+ /* we should set an error, but this is a deprecated function, and
+ * we are keeping bug for bug compatibility;)... */
+ return NULL;
+ }
+
+ pbe_params.pInitVector = ivData;
+ pbe_params.pPassword = pwitem->data;
+ pbe_params.ulPasswordLen = pwitem->len;
+ pbe_params.pSalt = salt->data;
+ pbe_params.ulSaltLen = salt->len;
+ pbe_params.ulIteration = iterations;
+ mechItem.data = (unsigned char *)&pbe_params;
+ mechItem.len = sizeof(pbe_params);
+
+ slot = PK11_GetInternalSlot();
+ symKey = PK11_RawPBEKeyGen(slot, mechanism,
+ &mechItem, pwitem, PR_FALSE, NULL);
+ PK11_FreeSlot(slot);
+ if (symKey != NULL) {
+ if (bitGenPurpose == pbeBitGenCipherIV) {
+ /* NOTE: this assumes that bitsNeeded is a multiple of 8! */
+ SECItem ivItem;
+
+ ivItem.data = ivData;
+ ivItem.len = bitsNeeded / 8;
+ context = SECITEM_DupItem(&ivItem);
+ } else {
+ SECItem *keyData;
+ PK11_ExtractKeyValue(symKey);
+ keyData = PK11_GetKeyData(symKey);
+
+ /* assert bitsNeeded with length? */
+ if (keyData) {
+ context = SECITEM_DupItem(keyData);
+ }
+ }
+ PK11_FreeSymKey(symKey);
+ }
+
+ return (PBEBitGenContext *)context;
+}
+
+/*
+ * public, Deprecated, This function is only for binary compatibility with
+ * older applications. Does not support PKCS5v2.
+ *
+ * Applications should use PK11_PBEKeyGen() for keys and PK11_GetIV() for
+ * iv values rather than generating PBE bits directly.
+ */
+SECItem *
+PBE_GenerateBits(PBEBitGenContext *context)
+{
+ return (SECItem *)context;
+}
+
+/*
+ * public, Deprecated, This function is only for binary compatibility with
+ * older applications. Does not support PKCS5v2.
+ *
+ * Applications should use PK11_PBEKeyGen() for keys and PK11_GetPBEIV() for
+ * iv values rather than generating PBE bits directly.
+ */
+void
+PBE_DestroyContext(PBEBitGenContext *context)
+{
+ SECITEM_FreeItem((SECItem *)context, PR_TRUE);
+}
+
+/*
+ * public, deprecated. Replaced with PK11_GetPBEIV().
+ */
+SECItem *
+SEC_PKCS5GetIV(SECAlgorithmID *algid, SECItem *pwitem, PRBool faulty3DES)
+{
+ /* pbe stuff */
+ CK_MECHANISM_TYPE type;
+ SECItem *param = NULL;
+ SECItem *iv = NULL;
+ SECItem src;
+ int iv_len = 0;
+ PK11SymKey *symKey;
+ PK11SlotInfo *slot;
+ CK_PBE_PARAMS_PTR pPBEparams;
+ SECOidTag pbeAlg;
+
+ pbeAlg = SECOID_GetAlgorithmTag(algid);
+ if (sec_pkcs5_is_algorithm_v2_pkcs5_algorithm(pbeAlg)) {
+ unsigned char *ivData;
+ sec_pkcs5V2Parameter *pbeV2_param = NULL;
+
+ /* can only return the IV if the crypto Algorithm exists */
+ if (pbeAlg == SEC_OID_PKCS5_PBKDF2) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ goto loser;
+ }
+ pbeV2_param = sec_pkcs5_v2_get_v2_param(NULL, algid);
+ if (pbeV2_param == NULL) {
+ goto loser;
+ }
+ /* extract the IV from the cipher algid portion of our pkcs 5 v2
+ * algorithm id */
+ type = PK11_AlgtagToMechanism(
+ SECOID_GetAlgorithmTag(&pbeV2_param->cipherAlgId));
+ param = PK11_ParamFromAlgid(&pbeV2_param->cipherAlgId);
+ sec_pkcs5_v2_destroy_v2_param(pbeV2_param);
+ if (!param) {
+ goto loser;
+ }
+ /* NOTE: NULL is a permissible return here */
+ ivData = PK11_IVFromParam(type, param, &iv_len);
+ src.data = ivData;
+ src.len = iv_len;
+ goto done;
+ }
+
+ type = PK11_AlgtagToMechanism(pbeAlg);
+ param = PK11_ParamFromAlgid(algid);
+ if (param == NULL) {
+ goto done;
+ }
+ slot = PK11_GetInternalSlot();
+ symKey = PK11_RawPBEKeyGen(slot, type, param, pwitem, faulty3DES, NULL);
+ PK11_FreeSlot(slot);
+ if (symKey == NULL) {
+ goto loser;
+ }
+ PK11_FreeSymKey(symKey);
+ pPBEparams = (CK_PBE_PARAMS_PTR)param->data;
+ iv_len = PK11_GetIVLength(type);
+
+ src.data = (unsigned char *)pPBEparams->pInitVector;
+ src.len = iv_len;
+
+done:
+ iv = SECITEM_DupItem(&src);
+
+loser:
+ if (param) {
+ SECITEM_ZfreeItem(param, PR_TRUE);
+ }
+ return iv;
+}
+
+/*
+ * Subs from nss 3.x that are deprecated
+ */
+PBEBitGenContext *
+__PBE_CreateContext(SECOidTag hashAlgorithm, PBEBitGenID bitGenPurpose,
+ SECItem *pwitem, SECItem *salt, unsigned int bitsNeeded,
+ unsigned int iterations)
+{
+ PORT_Assert("__PBE_CreateContext is Deprecated" == NULL);
+ return NULL;
+}
+
+SECItem *
+__PBE_GenerateBits(PBEBitGenContext *context)
+{
+ PORT_Assert("__PBE_GenerateBits is Deprecated" == NULL);
+ return NULL;
+}
+
+void
+__PBE_DestroyContext(PBEBitGenContext *context)
+{
+ PORT_Assert("__PBE_DestroyContext is Deprecated" == NULL);
+}
+
+SECStatus
+RSA_FormatBlock(SECItem *result, unsigned modulusLen,
+ int blockType, SECItem *data)
+{
+ PORT_Assert("RSA_FormatBlock is Deprecated" == NULL);
+ return SECFailure;
+}
+
+/****************************************************************************
+ *
+ * Now Do The PBE Functions Here...
+ *
+ ****************************************************************************/
+
+static void
+pk11_destroy_ck_pbe_params(CK_PBE_PARAMS *pbe_params)
+{
+ if (pbe_params) {
+ if (pbe_params->pPassword)
+ PORT_ZFree(pbe_params->pPassword, pbe_params->ulPasswordLen);
+ if (pbe_params->pSalt)
+ PORT_ZFree(pbe_params->pSalt, pbe_params->ulSaltLen);
+ PORT_ZFree(pbe_params, sizeof(CK_PBE_PARAMS));
+ }
+}
+
+/*
+ * public, deprecated. use PK11_CreatePBEAlgorithmID or
+ * PK11_CreatePBEV2AlgorithmID instead. If you needthe pkcs #11 parameters,
+ * use PK11_ParamFromAlgid from the algorithm id you created using
+ * PK11_CreatePBEAlgorithmID or PK11_CreatePBEV2AlgorithmID.
+ */
+SECItem *
+PK11_CreatePBEParams(SECItem *salt, SECItem *pwd, unsigned int iterations)
+{
+ CK_PBE_PARAMS *pbe_params = NULL;
+ SECItem *paramRV = NULL;
+
+ paramRV = SECITEM_AllocItem(NULL, NULL, sizeof(CK_PBE_PARAMS));
+ if (!paramRV) {
+ goto loser;
+ }
+ /* init paramRV->data with zeros. SECITEM_AllocItem does not do it */
+ PORT_Memset(paramRV->data, 0, sizeof(CK_PBE_PARAMS));
+
+ pbe_params = (CK_PBE_PARAMS *)paramRV->data;
+ pbe_params->pPassword = (CK_CHAR_PTR)PORT_ZAlloc(pwd->len);
+ if (!pbe_params->pPassword) {
+ goto loser;
+ }
+ if (pwd->data) {
+ PORT_Memcpy(pbe_params->pPassword, pwd->data, pwd->len);
+ }
+ pbe_params->ulPasswordLen = pwd->len;
+
+ pbe_params->pSalt = (CK_CHAR_PTR)PORT_ZAlloc(salt->len);
+ if (!pbe_params->pSalt) {
+ goto loser;
+ }
+ PORT_Memcpy(pbe_params->pSalt, salt->data, salt->len);
+ pbe_params->ulSaltLen = salt->len;
+
+ pbe_params->ulIteration = (CK_ULONG)iterations;
+ return paramRV;
+
+loser:
+ if (pbe_params)
+ pk11_destroy_ck_pbe_params(pbe_params);
+ if (paramRV)
+ PORT_ZFree(paramRV, sizeof(SECItem));
+ return NULL;
+}
+
+/*
+ * public, deprecated.
+ */
+void
+PK11_DestroyPBEParams(SECItem *pItem)
+{
+ if (pItem) {
+ CK_PBE_PARAMS *params = (CK_PBE_PARAMS *)(pItem->data);
+ if (params)
+ pk11_destroy_ck_pbe_params(params);
+ PORT_ZFree(pItem, sizeof(SECItem));
+ }
+}
+
+/*
+ * public, Partially supports PKCS5 V2 (some parameters are not controllable
+ * through this interface). Use PK11_CreatePBEV2AlgorithmID() if you need
+ * finer control these.
+ */
+SECAlgorithmID *
+PK11_CreatePBEAlgorithmID(SECOidTag algorithm, int iteration, SECItem *salt)
+{
+ SECAlgorithmID *algid = NULL;
+ algid = sec_pkcs5CreateAlgorithmID(algorithm,
+ SEC_OID_UNKNOWN, SEC_OID_UNKNOWN, NULL,
+ 0, salt, iteration);
+ return algid;
+}
+
+/*
+ * public, fully support pkcs5v2.
+ */
+SECAlgorithmID *
+PK11_CreatePBEV2AlgorithmID(SECOidTag pbeAlgTag, SECOidTag cipherAlgTag,
+ SECOidTag prfAlgTag, int keyLength, int iteration,
+ SECItem *salt)
+{
+ SECAlgorithmID *algid = NULL;
+ algid = sec_pkcs5CreateAlgorithmID(pbeAlgTag, cipherAlgTag, prfAlgTag,
+ NULL, keyLength, salt, iteration);
+ return algid;
+}
+
+/*
+ * private.
+ */
+PK11SymKey *
+pk11_RawPBEKeyGenWithKeyType(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
+ SECItem *params, CK_KEY_TYPE keyType, int keyLen,
+ SECItem *pwitem, void *wincx)
+{
+ CK_ULONG pwLen;
+ /* do some sanity checks */
+ if ((params == NULL) || (params->data == NULL)) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ if (type == CKM_INVALID_MECHANISM) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ return NULL;
+ }
+
+ /* set the password pointer in the parameters... */
+ if (type == CKM_PKCS5_PBKD2) {
+ CK_PKCS5_PBKD2_PARAMS *pbev2_params;
+ if (params->len < sizeof(CK_PKCS5_PBKD2_PARAMS)) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+ pbev2_params = (CK_PKCS5_PBKD2_PARAMS *)params->data;
+ pbev2_params->pPassword = pwitem->data;
+ pwLen = pwitem->len;
+ pbev2_params->ulPasswordLen = &pwLen;
+ } else {
+ CK_PBE_PARAMS *pbe_params;
+ if (params->len < sizeof(CK_PBE_PARAMS)) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+ pbe_params = (CK_PBE_PARAMS *)params->data;
+ pbe_params->pPassword = pwitem->data;
+ pbe_params->ulPasswordLen = pwitem->len;
+ }
+
+ /* generate the key (and sometimes the IV as a side effect...) */
+ return pk11_TokenKeyGenWithFlagsAndKeyType(slot, type, params, keyType,
+ keyLen, NULL,
+ CKF_SIGN | CKF_ENCRYPT | CKF_DECRYPT | CKF_UNWRAP | CKF_WRAP,
+ 0, wincx);
+}
+
+/*
+ * public, deprecated. use PK11_PBEKeyGen instead.
+ */
+PK11SymKey *
+PK11_RawPBEKeyGen(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, SECItem *mech,
+ SECItem *pwitem, PRBool faulty3DES, void *wincx)
+{
+ if (faulty3DES && (type == CKM_NSS_PBE_SHA1_TRIPLE_DES_CBC)) {
+ type = CKM_NSS_PBE_SHA1_FAULTY_3DES_CBC;
+ }
+ return pk11_RawPBEKeyGenWithKeyType(slot, type, mech, -1, 0, pwitem, wincx);
+}
+
+/*
+ * pubic, supports pkcs5 v2.
+ *
+ * Create symkey from a PBE key. The algid can be created with
+ * PK11_CreatePBEV2AlgorithmID and PK11_CreatePBEAlgorithmID, or by
+ * extraction of der data.
+ */
+PK11SymKey *
+PK11_PBEKeyGen(PK11SlotInfo *slot, SECAlgorithmID *algid, SECItem *pwitem,
+ PRBool faulty3DES, void *wincx)
+{
+ CK_MECHANISM_TYPE type;
+ SECItem *param = NULL;
+ PK11SymKey *symKey = NULL;
+ SECOidTag pbeAlg;
+ CK_KEY_TYPE keyType = -1;
+ int keyLen = 0;
+
+ pbeAlg = SECOID_GetAlgorithmTag(algid);
+ /* if we're using PKCS5v2, extract the additional information we need
+ * (key length, key type, and pbeAlg). */
+ if (sec_pkcs5_is_algorithm_v2_pkcs5_algorithm(pbeAlg)) {
+ CK_MECHANISM_TYPE cipherMech;
+ sec_pkcs5V2Parameter *pbeV2_param;
+
+ pbeV2_param = sec_pkcs5_v2_get_v2_param(NULL, algid);
+ if (pbeV2_param == NULL) {
+ return NULL;
+ }
+ cipherMech = PK11_AlgtagToMechanism(
+ SECOID_GetAlgorithmTag(&pbeV2_param->cipherAlgId));
+ pbeAlg = SECOID_GetAlgorithmTag(&pbeV2_param->pbeAlgId);
+ param = PK11_ParamFromAlgid(&pbeV2_param->pbeAlgId);
+ sec_pkcs5_v2_destroy_v2_param(pbeV2_param);
+ keyLen = SEC_PKCS5GetKeyLength(algid);
+ if (keyLen == -1) {
+ keyLen = 0;
+ }
+ keyType = PK11_GetKeyType(cipherMech, keyLen);
+ } else {
+ param = PK11_ParamFromAlgid(algid);
+ }
+
+ if (param == NULL) {
+ goto loser;
+ }
+
+ type = PK11_AlgtagToMechanism(pbeAlg);
+ if (type == CKM_INVALID_MECHANISM) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ goto loser;
+ }
+ if (faulty3DES && (type == CKM_NSS_PBE_SHA1_TRIPLE_DES_CBC)) {
+ type = CKM_NSS_PBE_SHA1_FAULTY_3DES_CBC;
+ }
+ symKey = pk11_RawPBEKeyGenWithKeyType(slot, type, param, keyType, keyLen,
+ pwitem, wincx);
+
+loser:
+ if (param) {
+ SECITEM_ZfreeItem(param, PR_TRUE);
+ }
+ return symKey;
+}
+
+/*
+ * public, supports pkcs5v2
+ */
+SECItem *
+PK11_GetPBEIV(SECAlgorithmID *algid, SECItem *pwitem)
+{
+ return SEC_PKCS5GetIV(algid, pwitem, PR_FALSE);
+}
+
+CK_MECHANISM_TYPE
+pk11_GetPBECryptoMechanism(SECAlgorithmID *algid, SECItem **param,
+ SECItem *pbe_pwd, PRBool faulty3DES)
+{
+ int keyLen = 0;
+ SECOidTag algTag = SEC_PKCS5GetCryptoAlgorithm(algid);
+ CK_MECHANISM_TYPE mech = PK11_AlgtagToMechanism(algTag);
+ CK_MECHANISM_TYPE returnedMechanism = CKM_INVALID_MECHANISM;
+ SECItem *iv = NULL;
+
+ if (mech == CKM_INVALID_MECHANISM) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ goto loser;
+ }
+ if (PK11_GetIVLength(mech)) {
+ iv = SEC_PKCS5GetIV(algid, pbe_pwd, faulty3DES);
+ if (iv == NULL) {
+ goto loser;
+ }
+ }
+
+ keyLen = SEC_PKCS5GetKeyLength(algid);
+
+ *param = pk11_ParamFromIVWithLen(mech, iv, keyLen);
+ if (*param == NULL) {
+ goto loser;
+ }
+ returnedMechanism = mech;
+
+loser:
+ if (iv) {
+ SECITEM_FreeItem(iv, PR_TRUE);
+ }
+ return returnedMechanism;
+}
+
+/*
+ * Public, supports pkcs5 v2
+ *
+ * Get the crypto mechanism directly from the pbe algorithmid.
+ *
+ * It's important to go directly from the algorithm id so that we can
+ * handle both the PKCS #5 v1, PKCS #12, and PKCS #5 v2 cases.
+ *
+ * This function returns both the mechanism and the parameter for the mechanism.
+ * The caller is responsible for freeing the parameter.
+ */
+CK_MECHANISM_TYPE
+PK11_GetPBECryptoMechanism(SECAlgorithmID *algid, SECItem **param,
+ SECItem *pbe_pwd)
+{
+ return pk11_GetPBECryptoMechanism(algid, param, pbe_pwd, PR_FALSE);
+}
diff --git a/security/nss/lib/pk11wrap/pk11pk12.c b/security/nss/lib/pk11wrap/pk11pk12.c
new file mode 100644
index 0000000000..917b7f0f67
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11pk12.c
@@ -0,0 +1,829 @@
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/*
+ * This file PKCS #12 fuctions that should really be moved to the
+ * PKCS #12 directory, however we can't do that in a point release
+ * because that will break binary compatibility, so we keep them here for now.
+ */
+
+#include "seccomon.h"
+#include "secmod.h"
+#include "secmodi.h"
+#include "secmodti.h"
+#include "secmodt.h"
+#include "pkcs11.h"
+#include "pk11func.h"
+#include "secitem.h"
+#include "keyhi.h"
+#include "secoid.h"
+#include "secasn1.h"
+#include "secerr.h"
+#include "prerror.h"
+
+/* These data structures should move to a common .h file shared between the
+ * wrappers and the pkcs 12 code. */
+
+/*
+** RSA Raw Private Key structures
+*/
+
+/* member names from PKCS#1, section 7.2 */
+struct SECKEYRSAPrivateKeyStr {
+ PLArenaPool *arena;
+ SECItem version;
+ SECItem modulus;
+ SECItem publicExponent;
+ SECItem privateExponent;
+ SECItem prime1;
+ SECItem prime2;
+ SECItem exponent1;
+ SECItem exponent2;
+ SECItem coefficient;
+};
+typedef struct SECKEYRSAPrivateKeyStr SECKEYRSAPrivateKey;
+
+/*
+** DSA Raw Private Key structures
+*/
+
+struct SECKEYDSAPrivateKeyStr {
+ SECKEYPQGParams params;
+ SECItem privateValue;
+};
+typedef struct SECKEYDSAPrivateKeyStr SECKEYDSAPrivateKey;
+
+/*
+** Diffie-Hellman Raw Private Key structures
+** Structure member names suggested by PKCS#3.
+*/
+struct SECKEYDHPrivateKeyStr {
+ PLArenaPool *arena;
+ SECItem prime;
+ SECItem base;
+ SECItem privateValue;
+};
+typedef struct SECKEYDHPrivateKeyStr SECKEYDHPrivateKey;
+
+/*
+** Elliptic Curve Private Key structures
+** <https://tools.ietf.org/html/rfc5915#section-3>
+*/
+struct SECKEYECPrivateKeyStr {
+ PLArenaPool *arena;
+ SECItem version;
+ SECItem curveOID; /* optional/ignored */
+ SECItem publicValue; /* required (for now) */
+ SECItem privateValue;
+};
+typedef struct SECKEYECPrivateKeyStr SECKEYECPrivateKey;
+
+/*
+** raw private key object
+*/
+struct SECKEYRawPrivateKeyStr {
+ PLArenaPool *arena;
+ KeyType keyType;
+ union {
+ SECKEYRSAPrivateKey rsa;
+ SECKEYDSAPrivateKey dsa;
+ SECKEYDHPrivateKey dh;
+ SECKEYECPrivateKey ec;
+ } u;
+};
+typedef struct SECKEYRawPrivateKeyStr SECKEYRawPrivateKey;
+
+SEC_ASN1_MKSUB(SEC_AnyTemplate)
+SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
+
+/* ASN1 Templates for new decoder/encoder */
+/*
+ * Attribute value for PKCS8 entries (static?)
+ */
+const SEC_ASN1Template SECKEY_AttributeTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(SECKEYAttribute) },
+ { SEC_ASN1_OBJECT_ID, offsetof(SECKEYAttribute, attrType) },
+ { SEC_ASN1_SET_OF | SEC_ASN1_XTRN, offsetof(SECKEYAttribute, attrValue),
+ SEC_ASN1_SUB(SEC_AnyTemplate) },
+ { 0 }
+};
+
+const SEC_ASN1Template SECKEY_SetOfAttributeTemplate[] = {
+ { SEC_ASN1_SET_OF, 0, SECKEY_AttributeTemplate },
+};
+
+const SEC_ASN1Template SECKEY_PrivateKeyInfoTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECKEYPrivateKeyInfo) },
+ { SEC_ASN1_INTEGER, offsetof(SECKEYPrivateKeyInfo, version) },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+ offsetof(SECKEYPrivateKeyInfo, algorithm),
+ SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+ { SEC_ASN1_OCTET_STRING, offsetof(SECKEYPrivateKeyInfo, privateKey) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(SECKEYPrivateKeyInfo, attributes),
+ SECKEY_SetOfAttributeTemplate },
+ { 0 }
+};
+
+const SEC_ASN1Template SECKEY_PointerToPrivateKeyInfoTemplate[] = {
+ { SEC_ASN1_POINTER, 0, SECKEY_PrivateKeyInfoTemplate }
+};
+
+const SEC_ASN1Template SECKEY_RSAPrivateKeyExportTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECKEYRawPrivateKey) },
+ { SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.rsa.version) },
+ { SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.rsa.modulus) },
+ { SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.rsa.publicExponent) },
+ { SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.rsa.privateExponent) },
+ { SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.rsa.prime1) },
+ { SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.rsa.prime2) },
+ { SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.rsa.exponent1) },
+ { SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.rsa.exponent2) },
+ { SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.rsa.coefficient) },
+ { 0 }
+};
+
+const SEC_ASN1Template SECKEY_DSAPrivateKeyExportTemplate[] = {
+ { SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.dsa.privateValue) },
+};
+
+const SEC_ASN1Template SECKEY_DHPrivateKeyExportTemplate[] = {
+ { SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.dh.privateValue) },
+ { SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.dh.base) },
+ { SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.dh.prime) },
+};
+
+SEC_ASN1_MKSUB(SEC_BitStringTemplate)
+SEC_ASN1_MKSUB(SEC_ObjectIDTemplate)
+
+const SEC_ASN1Template SECKEY_ECPrivateKeyExportTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECKEYRawPrivateKey) },
+ { SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.ec.version) },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(SECKEYRawPrivateKey, u.ec.privateValue) },
+ /* This value will always be ignored. u.ec.curveOID will always be
+ * overriden with the outer AlgorithmID.parameters. */
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED |
+ SEC_ASN1_EXPLICIT | SEC_ASN1_CONTEXT_SPECIFIC |
+ SEC_ASN1_XTRN | 0,
+ offsetof(SECKEYRawPrivateKey, u.ec.curveOID),
+ SEC_ASN1_SUB(SEC_ObjectIDTemplate) },
+ /* The public value is optional per RFC, but required in NSS. We
+ * can't do scalar mult on ECs to get a raw point with PK11 APIs. */
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED |
+ SEC_ASN1_EXPLICIT | SEC_ASN1_CONTEXT_SPECIFIC |
+ SEC_ASN1_XTRN | 1,
+ offsetof(SECKEYRawPrivateKey, u.ec.publicValue),
+ SEC_ASN1_SUB(SEC_BitStringTemplate) },
+ { 0 }
+};
+
+const SEC_ASN1Template SECKEY_EncryptedPrivateKeyInfoTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(SECKEYEncryptedPrivateKeyInfo) },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+ offsetof(SECKEYEncryptedPrivateKeyInfo, algorithm),
+ SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(SECKEYEncryptedPrivateKeyInfo, encryptedData) },
+ { 0 }
+};
+
+const SEC_ASN1Template SECKEY_PointerToEncryptedPrivateKeyInfoTemplate[] = {
+ { SEC_ASN1_POINTER, 0, SECKEY_EncryptedPrivateKeyInfoTemplate }
+};
+
+SEC_ASN1_CHOOSER_IMPLEMENT(SECKEY_EncryptedPrivateKeyInfoTemplate)
+SEC_ASN1_CHOOSER_IMPLEMENT(SECKEY_PointerToEncryptedPrivateKeyInfoTemplate)
+SEC_ASN1_CHOOSER_IMPLEMENT(SECKEY_PrivateKeyInfoTemplate)
+SEC_ASN1_CHOOSER_IMPLEMENT(SECKEY_PointerToPrivateKeyInfoTemplate)
+
+/*
+ * See bugzilla bug 125359
+ * Since NSS (via PKCS#11) wants to handle big integers as unsigned ints,
+ * all of the templates above that en/decode into integers must be converted
+ * from ASN.1's signed integer type. This is done by marking either the
+ * source or destination (encoding or decoding, respectively) type as
+ * siUnsignedInteger.
+ */
+
+static void
+prepare_rsa_priv_key_export_for_asn1(SECKEYRawPrivateKey *key)
+{
+ key->u.rsa.modulus.type = siUnsignedInteger;
+ key->u.rsa.publicExponent.type = siUnsignedInteger;
+ key->u.rsa.privateExponent.type = siUnsignedInteger;
+ key->u.rsa.prime1.type = siUnsignedInteger;
+ key->u.rsa.prime2.type = siUnsignedInteger;
+ key->u.rsa.exponent1.type = siUnsignedInteger;
+ key->u.rsa.exponent2.type = siUnsignedInteger;
+ key->u.rsa.coefficient.type = siUnsignedInteger;
+}
+
+static void
+prepare_dsa_priv_key_export_for_asn1(SECKEYRawPrivateKey *key)
+{
+ key->u.dsa.privateValue.type = siUnsignedInteger;
+ key->u.dsa.params.prime.type = siUnsignedInteger;
+ key->u.dsa.params.subPrime.type = siUnsignedInteger;
+ key->u.dsa.params.base.type = siUnsignedInteger;
+}
+
+static void
+prepare_dh_priv_key_export_for_asn1(SECKEYRawPrivateKey *key)
+{
+ key->u.dh.privateValue.type = siUnsignedInteger;
+ key->u.dh.prime.type = siUnsignedInteger;
+ key->u.dh.base.type = siUnsignedInteger;
+}
+
+static void
+prepare_ec_priv_key_export_for_asn1(SECKEYRawPrivateKey *key)
+{
+ key->u.ec.version.type = siUnsignedInteger;
+ key->u.ec.curveOID.type = siUnsignedInteger;
+ key->u.ec.privateValue.type = siUnsignedInteger;
+ key->u.ec.publicValue.type = siUnsignedInteger;
+}
+
+SECStatus
+PK11_ImportDERPrivateKeyInfo(PK11SlotInfo *slot, SECItem *derPKI,
+ SECItem *nickname, SECItem *publicValue, PRBool isPerm,
+ PRBool isPrivate, unsigned int keyUsage, void *wincx)
+{
+ return PK11_ImportDERPrivateKeyInfoAndReturnKey(slot, derPKI,
+ nickname, publicValue,
+ isPerm, isPrivate, keyUsage,
+ NULL, wincx);
+}
+
+SECStatus
+PK11_ImportDERPrivateKeyInfoAndReturnKey(PK11SlotInfo *slot, SECItem *derPKI,
+ SECItem *nickname, SECItem *publicValue,
+ PRBool isPerm, PRBool isPrivate, unsigned int keyUsage,
+ SECKEYPrivateKey **privk, void *wincx)
+{
+ SECKEYPrivateKeyInfo *pki = NULL;
+ PLArenaPool *temparena = NULL;
+ SECStatus rv = SECFailure;
+
+ temparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (!temparena)
+ return rv;
+ pki = PORT_ArenaZNew(temparena, SECKEYPrivateKeyInfo);
+ if (!pki) {
+ PORT_FreeArena(temparena, PR_FALSE);
+ return rv;
+ }
+ pki->arena = temparena;
+
+ rv = SEC_ASN1DecodeItem(pki->arena, pki, SECKEY_PrivateKeyInfoTemplate,
+ derPKI);
+ if (rv != SECSuccess) {
+ /* If SEC_ASN1DecodeItem fails, we cannot assume anything about the
+ * validity of the data in pki. The best we can do is free the arena
+ * and return. */
+ PORT_FreeArena(temparena, PR_TRUE);
+ return rv;
+ }
+ if (pki->privateKey.data == NULL) {
+ /* If SEC_ASN1DecodeItems succeeds but SECKEYPrivateKeyInfo.privateKey
+ * is a zero-length octet string, free the arena and return a failure
+ * to avoid trying to zero the corresponding SECItem in
+ * SECKEY_DestroyPrivateKeyInfo(). */
+ PORT_FreeArena(temparena, PR_TRUE);
+ PORT_SetError(SEC_ERROR_BAD_KEY);
+ return SECFailure;
+ }
+
+ rv = PK11_ImportPrivateKeyInfoAndReturnKey(slot, pki, nickname,
+ publicValue, isPerm, isPrivate,
+ keyUsage, privk, wincx);
+
+ /* this zeroes the key and frees the arena */
+ SECKEY_DestroyPrivateKeyInfo(pki, PR_TRUE /*freeit*/);
+ return rv;
+}
+
+SECStatus
+PK11_ImportAndReturnPrivateKey(PK11SlotInfo *slot, SECKEYRawPrivateKey *lpk,
+ SECItem *nickname, SECItem *publicValue, PRBool isPerm,
+ PRBool isPrivate, unsigned int keyUsage, SECKEYPrivateKey **privk,
+ void *wincx)
+{
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_BBOOL ckfalse = CK_FALSE;
+ CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY;
+ CK_KEY_TYPE keyType = CKK_RSA;
+ CK_OBJECT_HANDLE objectID;
+ CK_ATTRIBUTE theTemplate[20];
+ int templateCount = 0;
+ SECStatus rv = SECFailure;
+ CK_ATTRIBUTE *attrs;
+ CK_ATTRIBUTE *signedattr = NULL;
+ int signedcount = 0;
+ CK_ATTRIBUTE *ap;
+ SECItem *ck_id = NULL;
+
+ attrs = theTemplate;
+
+ PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof(keyClass));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(keyType));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_TOKEN, isPerm ? &cktrue : &ckfalse,
+ sizeof(CK_BBOOL));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_SENSITIVE, isPrivate ? &cktrue : &ckfalse,
+ sizeof(CK_BBOOL));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_PRIVATE, isPrivate ? &cktrue : &ckfalse,
+ sizeof(CK_BBOOL));
+ attrs++;
+
+ switch (lpk->keyType) {
+ case rsaKey:
+ keyType = CKK_RSA;
+ PK11_SETATTRS(attrs, CKA_UNWRAP, (keyUsage & KU_KEY_ENCIPHERMENT) ? &cktrue : &ckfalse,
+ sizeof(CK_BBOOL));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_DECRYPT, (keyUsage & KU_DATA_ENCIPHERMENT) ? &cktrue : &ckfalse,
+ sizeof(CK_BBOOL));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_SIGN, (keyUsage & KU_DIGITAL_SIGNATURE) ? &cktrue : &ckfalse,
+ sizeof(CK_BBOOL));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_SIGN_RECOVER,
+ (keyUsage & KU_DIGITAL_SIGNATURE) ? &cktrue
+ : &ckfalse,
+ sizeof(CK_BBOOL));
+ attrs++;
+ ck_id = PK11_MakeIDFromPubKey(&lpk->u.rsa.modulus);
+ if (ck_id == NULL) {
+ goto loser;
+ }
+ PK11_SETATTRS(attrs, CKA_ID, ck_id->data, ck_id->len);
+ attrs++;
+ if (nickname) {
+ PK11_SETATTRS(attrs, CKA_LABEL, nickname->data, nickname->len);
+ attrs++;
+ }
+ signedattr = attrs;
+ PK11_SETATTRS(attrs, CKA_MODULUS, lpk->u.rsa.modulus.data,
+ lpk->u.rsa.modulus.len);
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_PUBLIC_EXPONENT,
+ lpk->u.rsa.publicExponent.data,
+ lpk->u.rsa.publicExponent.len);
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_PRIVATE_EXPONENT,
+ lpk->u.rsa.privateExponent.data,
+ lpk->u.rsa.privateExponent.len);
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_PRIME_1,
+ lpk->u.rsa.prime1.data,
+ lpk->u.rsa.prime1.len);
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_PRIME_2,
+ lpk->u.rsa.prime2.data,
+ lpk->u.rsa.prime2.len);
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_EXPONENT_1,
+ lpk->u.rsa.exponent1.data,
+ lpk->u.rsa.exponent1.len);
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_EXPONENT_2,
+ lpk->u.rsa.exponent2.data,
+ lpk->u.rsa.exponent2.len);
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_COEFFICIENT,
+ lpk->u.rsa.coefficient.data,
+ lpk->u.rsa.coefficient.len);
+ attrs++;
+ break;
+ case dsaKey:
+ keyType = CKK_DSA;
+ /* To make our intenal PKCS #11 module work correctly with
+ * our database, we need to pass in the public key value for
+ * this dsa key. We have a netscape only CKA_ value to do this.
+ * Only send it to internal slots */
+ if (publicValue == NULL) {
+ goto loser;
+ }
+ if (PK11_IsInternal(slot)) {
+ PK11_SETATTRS(attrs, CKA_NSS_DB,
+ publicValue->data, publicValue->len);
+ attrs++;
+ }
+ PK11_SETATTRS(attrs, CKA_SIGN, &cktrue, sizeof(CK_BBOOL));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_SIGN_RECOVER, &cktrue, sizeof(CK_BBOOL));
+ attrs++;
+ if (nickname) {
+ PK11_SETATTRS(attrs, CKA_LABEL, nickname->data, nickname->len);
+ attrs++;
+ }
+ ck_id = PK11_MakeIDFromPubKey(publicValue);
+ if (ck_id == NULL) {
+ goto loser;
+ }
+ PK11_SETATTRS(attrs, CKA_ID, ck_id->data, ck_id->len);
+ attrs++;
+ signedattr = attrs;
+ PK11_SETATTRS(attrs, CKA_PRIME, lpk->u.dsa.params.prime.data,
+ lpk->u.dsa.params.prime.len);
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_SUBPRIME, lpk->u.dsa.params.subPrime.data,
+ lpk->u.dsa.params.subPrime.len);
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_BASE, lpk->u.dsa.params.base.data,
+ lpk->u.dsa.params.base.len);
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_VALUE, lpk->u.dsa.privateValue.data,
+ lpk->u.dsa.privateValue.len);
+ attrs++;
+ break;
+ case dhKey:
+ keyType = CKK_DH;
+ /* To make our intenal PKCS #11 module work correctly with
+ * our database, we need to pass in the public key value for
+ * this dh key. We have a netscape only CKA_ value to do this.
+ * Only send it to internal slots */
+ if (PK11_IsInternal(slot)) {
+ PK11_SETATTRS(attrs, CKA_NSS_DB,
+ publicValue->data, publicValue->len);
+ attrs++;
+ }
+ PK11_SETATTRS(attrs, CKA_DERIVE, &cktrue, sizeof(CK_BBOOL));
+ attrs++;
+ if (nickname) {
+ PK11_SETATTRS(attrs, CKA_LABEL, nickname->data, nickname->len);
+ attrs++;
+ }
+ ck_id = PK11_MakeIDFromPubKey(publicValue);
+ if (ck_id == NULL) {
+ goto loser;
+ }
+ PK11_SETATTRS(attrs, CKA_ID, ck_id->data, ck_id->len);
+ attrs++;
+ signedattr = attrs;
+ PK11_SETATTRS(attrs, CKA_PRIME, lpk->u.dh.prime.data,
+ lpk->u.dh.prime.len);
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_BASE, lpk->u.dh.base.data,
+ lpk->u.dh.base.len);
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_VALUE, lpk->u.dh.privateValue.data,
+ lpk->u.dh.privateValue.len);
+ attrs++;
+ break;
+ case ecKey:
+ keyType = CKK_EC;
+ if (lpk->u.ec.publicValue.len == 0) {
+ goto loser;
+ }
+ if (PK11_IsInternal(slot)) {
+ PK11_SETATTRS(attrs, CKA_NSS_DB,
+ lpk->u.ec.publicValue.data,
+ lpk->u.ec.publicValue.len);
+ attrs++;
+ }
+ PK11_SETATTRS(attrs, CKA_SIGN, (keyUsage & KU_DIGITAL_SIGNATURE) ? &cktrue : &ckfalse,
+ sizeof(CK_BBOOL));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_SIGN_RECOVER,
+ (keyUsage & KU_DIGITAL_SIGNATURE) ? &cktrue
+ : &ckfalse,
+ sizeof(CK_BBOOL));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_DERIVE, (keyUsage & KU_KEY_AGREEMENT) ? &cktrue : &ckfalse,
+ sizeof(CK_BBOOL));
+ attrs++;
+ if (nickname) {
+ PK11_SETATTRS(attrs, CKA_LABEL, nickname->data, nickname->len);
+ attrs++;
+ }
+ ck_id = PK11_MakeIDFromPubKey(&lpk->u.ec.publicValue);
+ if (ck_id == NULL) {
+ goto loser;
+ }
+ PK11_SETATTRS(attrs, CKA_ID, ck_id->data, ck_id->len);
+ attrs++;
+ /* No signed attrs for EC */
+ /* curveOID always is a copy of AlgorithmID.parameters. */
+ PK11_SETATTRS(attrs, CKA_EC_PARAMS, lpk->u.ec.curveOID.data,
+ lpk->u.ec.curveOID.len);
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_VALUE, lpk->u.ec.privateValue.data,
+ lpk->u.ec.privateValue.len);
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_EC_POINT, lpk->u.ec.publicValue.data,
+ lpk->u.ec.publicValue.len);
+ attrs++;
+ break;
+ default:
+ PORT_SetError(SEC_ERROR_BAD_KEY);
+ goto loser;
+ }
+ templateCount = attrs - theTemplate;
+ PORT_Assert(templateCount <= sizeof(theTemplate) / sizeof(CK_ATTRIBUTE));
+ if (lpk->keyType != ecKey) {
+ PORT_Assert(signedattr);
+ signedcount = attrs - signedattr;
+ for (ap = signedattr; signedcount; ap++, signedcount--) {
+ pk11_SignedToUnsigned(ap);
+ }
+ }
+
+ rv = PK11_CreateNewObject(slot, CK_INVALID_HANDLE,
+ theTemplate, templateCount, isPerm, &objectID);
+
+ /* create and return a SECKEYPrivateKey */
+ if (rv == SECSuccess && privk != NULL) {
+ *privk = PK11_MakePrivKey(slot, lpk->keyType, !isPerm, objectID, wincx);
+ if (*privk == NULL) {
+ rv = SECFailure;
+ }
+ }
+loser:
+ if (ck_id) {
+ SECITEM_ZfreeItem(ck_id, PR_TRUE);
+ }
+ return rv;
+}
+
+SECStatus
+PK11_ImportPrivateKeyInfoAndReturnKey(PK11SlotInfo *slot,
+ SECKEYPrivateKeyInfo *pki, SECItem *nickname, SECItem *publicValue,
+ PRBool isPerm, PRBool isPrivate, unsigned int keyUsage,
+ SECKEYPrivateKey **privk, void *wincx)
+{
+ SECStatus rv = SECFailure;
+ SECKEYRawPrivateKey *lpk = NULL;
+ const SEC_ASN1Template *keyTemplate, *paramTemplate;
+ void *paramDest = NULL;
+ PLArenaPool *arena = NULL;
+
+ arena = PORT_NewArena(2048);
+ if (!arena) {
+ return SECFailure;
+ }
+
+ /* need to change this to use RSA/DSA keys */
+ lpk = (SECKEYRawPrivateKey *)PORT_ArenaZAlloc(arena,
+ sizeof(SECKEYRawPrivateKey));
+ if (lpk == NULL) {
+ goto loser;
+ }
+ lpk->arena = arena;
+
+ switch (SECOID_GetAlgorithmTag(&pki->algorithm)) {
+ case SEC_OID_PKCS1_RSA_ENCRYPTION:
+ prepare_rsa_priv_key_export_for_asn1(lpk);
+ keyTemplate = SECKEY_RSAPrivateKeyExportTemplate;
+ paramTemplate = NULL;
+ paramDest = NULL;
+ lpk->keyType = rsaKey;
+ break;
+ case SEC_OID_ANSIX9_DSA_SIGNATURE:
+ prepare_dsa_priv_key_export_for_asn1(lpk);
+ keyTemplate = SECKEY_DSAPrivateKeyExportTemplate;
+ paramTemplate = SECKEY_PQGParamsTemplate;
+ paramDest = &(lpk->u.dsa.params);
+ lpk->keyType = dsaKey;
+ break;
+ case SEC_OID_X942_DIFFIE_HELMAN_KEY:
+ if (!publicValue) {
+ goto loser;
+ }
+ prepare_dh_priv_key_export_for_asn1(lpk);
+ keyTemplate = SECKEY_DHPrivateKeyExportTemplate;
+ paramTemplate = NULL;
+ paramDest = NULL;
+ lpk->keyType = dhKey;
+ break;
+ case SEC_OID_ANSIX962_EC_PUBLIC_KEY:
+ prepare_ec_priv_key_export_for_asn1(lpk);
+ keyTemplate = SECKEY_ECPrivateKeyExportTemplate;
+ paramTemplate = NULL;
+ paramDest = NULL;
+ lpk->keyType = ecKey;
+ break;
+
+ default:
+ keyTemplate = NULL;
+ paramTemplate = NULL;
+ paramDest = NULL;
+ break;
+ }
+
+ if (!keyTemplate) {
+ goto loser;
+ }
+
+ /* decode the private key and any algorithm parameters */
+ rv = SEC_QuickDERDecodeItem(arena, lpk, keyTemplate, &pki->privateKey);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ if (lpk->keyType == ecKey) {
+ /* Convert length in bits to length in bytes. */
+ lpk->u.ec.publicValue.len >>= 3;
+
+ /* Always override curveOID, we're ignoring any given value. */
+ rv = SECITEM_CopyItem(arena, &lpk->u.ec.curveOID,
+ &pki->algorithm.parameters);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ }
+
+ if (paramDest && paramTemplate) {
+ rv = SEC_ASN1DecodeItem(arena, paramDest, paramTemplate,
+ &(pki->algorithm.parameters));
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ }
+
+ rv = PK11_ImportAndReturnPrivateKey(slot, lpk, nickname, publicValue, isPerm,
+ isPrivate, keyUsage, privk, wincx);
+
+loser:
+ if (arena != NULL) {
+ PORT_FreeArena(arena, PR_TRUE);
+ }
+
+ return rv;
+}
+
+SECStatus
+PK11_ImportPrivateKeyInfo(PK11SlotInfo *slot, SECKEYPrivateKeyInfo *pki,
+ SECItem *nickname, SECItem *publicValue, PRBool isPerm,
+ PRBool isPrivate, unsigned int keyUsage, void *wincx)
+{
+ return PK11_ImportPrivateKeyInfoAndReturnKey(slot, pki, nickname,
+ publicValue, isPerm, isPrivate, keyUsage, NULL, wincx);
+}
+
+SECItem *
+PK11_ExportDERPrivateKeyInfo(SECKEYPrivateKey *pk, void *wincx)
+{
+ SECKEYPrivateKeyInfo *pki = PK11_ExportPrivKeyInfo(pk, wincx);
+ SECItem *derPKI;
+
+ if (!pki) {
+ return NULL;
+ }
+ derPKI = SEC_ASN1EncodeItem(NULL, NULL, pki,
+ SECKEY_PrivateKeyInfoTemplate);
+ SECKEY_DestroyPrivateKeyInfo(pki, PR_TRUE);
+ return derPKI;
+}
+
+static PRBool
+ReadAttribute(SECKEYPrivateKey *key, CK_ATTRIBUTE_TYPE type,
+ PLArenaPool *arena, SECItem *output)
+{
+ SECStatus rv = PK11_ReadAttribute(key->pkcs11Slot, key->pkcs11ID, type,
+ arena, output);
+ return rv == SECSuccess;
+}
+
+/*
+ * The caller is responsible for freeing the return value by passing it to
+ * SECKEY_DestroyPrivateKeyInfo(..., PR_TRUE).
+ */
+SECKEYPrivateKeyInfo *
+PK11_ExportPrivKeyInfo(SECKEYPrivateKey *pk, void *wincx)
+{
+ /* PrivateKeyInfo version (always zero) */
+ const unsigned char pkiVersion = 0;
+ /* RSAPrivateKey version (always zero) */
+ const unsigned char rsaVersion = 0;
+ /* ECPrivateKey version (always one) */
+ const unsigned char ecVersion = 1;
+ PLArenaPool *arena = NULL;
+ SECKEYRawPrivateKey rawKey;
+ SECKEYPrivateKeyInfo *pki;
+ SECItem *encoded;
+ const SEC_ASN1Template *keyTemplate;
+ SECStatus rv;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (!arena) {
+ goto loser;
+ }
+ memset(&rawKey, 0, sizeof(rawKey));
+ rawKey.keyType = pk->keyType;
+ pki = PORT_ArenaZNew(arena, SECKEYPrivateKeyInfo);
+ if (!pki) {
+ goto loser;
+ }
+
+ switch (pk->keyType) {
+ case rsaKey: {
+ rawKey.u.rsa.version.type = siUnsignedInteger;
+ rawKey.u.rsa.version.data = (unsigned char *)PORT_ArenaAlloc(arena, 1);
+ if (!rawKey.u.rsa.version.data) {
+ goto loser;
+ }
+
+ rawKey.u.rsa.version.data[0] = rsaVersion;
+ rawKey.u.rsa.version.len = 1;
+
+ /* Read the component attributes of the private key */
+ prepare_rsa_priv_key_export_for_asn1(&rawKey);
+ if (!ReadAttribute(pk, CKA_MODULUS, arena, &rawKey.u.rsa.modulus) ||
+ !ReadAttribute(pk, CKA_PUBLIC_EXPONENT, arena,
+ &rawKey.u.rsa.publicExponent) ||
+ !ReadAttribute(pk, CKA_PRIVATE_EXPONENT, arena,
+ &rawKey.u.rsa.privateExponent) ||
+ !ReadAttribute(pk, CKA_PRIME_1, arena, &rawKey.u.rsa.prime1) ||
+ !ReadAttribute(pk, CKA_PRIME_2, arena, &rawKey.u.rsa.prime2) ||
+ !ReadAttribute(pk, CKA_EXPONENT_1, arena,
+ &rawKey.u.rsa.exponent1) ||
+ !ReadAttribute(pk, CKA_EXPONENT_2, arena,
+ &rawKey.u.rsa.exponent2) ||
+ !ReadAttribute(pk, CKA_COEFFICIENT, arena,
+ &rawKey.u.rsa.coefficient)) {
+ goto loser;
+ }
+
+ keyTemplate = SECKEY_RSAPrivateKeyExportTemplate;
+
+ rv = SECOID_SetAlgorithmID(arena, &pki->algorithm, SEC_OID_PKCS1_RSA_ENCRYPTION, NULL);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ } break;
+ case ecKey: {
+ rawKey.u.ec.version.type = siUnsignedInteger;
+ rawKey.u.ec.version.data = (unsigned char *)PORT_ArenaAlloc(arena, 1);
+ if (!rawKey.u.ec.version.data) {
+ goto loser;
+ }
+ rawKey.u.ec.version.data[0] = ecVersion;
+ rawKey.u.ec.version.len = 1;
+
+ SECItem curveOID;
+ /* Read the component attributes of the private key */
+ prepare_ec_priv_key_export_for_asn1(&rawKey);
+ if (!ReadAttribute(pk, CKA_VALUE, arena,
+ &rawKey.u.ec.privateValue) ||
+ !ReadAttribute(pk, CKA_EC_PARAMS, arena, &curveOID)) {
+ goto loser;
+ }
+ if (!ReadAttribute(pk, CKA_EC_POINT, arena,
+ &rawKey.u.ec.publicValue)) {
+ SECKEYPublicKey *pubk = SECKEY_ConvertToPublicKey(pk);
+ if (pubk == NULL)
+ goto loser;
+ rv = SECITEM_CopyItem(arena, &rawKey.u.ec.publicValue, &pubk->u.ec.publicValue);
+ SECKEY_DestroyPublicKey(pubk);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ }
+
+ keyTemplate = SECKEY_ECPrivateKeyExportTemplate;
+ /* Convert length in bytes to length in bits. */
+ rawKey.u.ec.publicValue.len <<= 3;
+
+ rv = SECOID_SetAlgorithmID(arena, &pki->algorithm, SEC_OID_ANSIX962_EC_PUBLIC_KEY, &curveOID);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ } break;
+ default: {
+ PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
+ goto loser;
+ }
+ }
+
+ encoded = SEC_ASN1EncodeItem(arena, &pki->privateKey, &rawKey, keyTemplate);
+ if (!encoded) {
+ goto loser;
+ }
+ pki->version.type = siUnsignedInteger;
+ pki->version.data = (unsigned char *)PORT_ArenaAlloc(arena, 1);
+ if (!pki->version.data) {
+ goto loser;
+ }
+ pki->version.data[0] = pkiVersion;
+ pki->version.len = 1;
+ pki->arena = arena;
+
+ return pki;
+
+loser:
+ if (arena) {
+ PORT_FreeArena(arena, PR_TRUE);
+ }
+ return NULL;
+}
diff --git a/security/nss/lib/pk11wrap/pk11pqg.c b/security/nss/lib/pk11wrap/pk11pqg.c
new file mode 100644
index 0000000000..4ad2beee6a
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11pqg.c
@@ -0,0 +1,522 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/* Thse functions are stub functions which will get replaced with calls through
+ * PKCS #11.
+ */
+
+#include "pk11func.h"
+#include "secmod.h"
+#include "secmodi.h"
+#include "secmodti.h"
+#include "pkcs11t.h"
+#include "pk11pqg.h"
+#include "secerr.h"
+
+/* Generate PQGParams and PQGVerify structs.
+ * Length of P specified by L.
+ * if L is greater than 1024 then the resulting verify parameters will be
+ * DSA2.
+ * Length of Q specified by N. If zero, The PKCS #11 module will
+ * pick an appropriately sized Q for P. If N is specified and L = 1024, then
+ * the resulting verify parameters will be DSA2, Otherwise DSA1 parameters
+ * will be returned.
+ * Length of SEED in bytes specified in seedBytes.
+ *
+ * The underlying PKCS #11 module will check the values for L, N,
+ * and seedBytes. The rules for softoken are:
+ *
+ * If L <= 1024, then L must be between 512 and 1024 in increments of 64 bits.
+ * If L <= 1024, then N must be 0 or 160.
+ * If L >= 1024, then L and N must match the following table:
+ * L=1024 N=0 or 160
+ * L=2048 N=0 or 224
+ * L=2048 N=256
+ * L=3072 N=0 or 256
+ * if L <= 1024
+ * seedBbytes must be in the range [20..256].
+ * if L >= 1024
+ * seedBbytes must be in the range [20..L/16].
+ */
+extern SECStatus
+PK11_PQG_ParamGenV2(unsigned int L, unsigned int N,
+ unsigned int seedBytes, PQGParams **pParams, PQGVerify **pVfy)
+{
+ PK11SlotInfo *slot = NULL;
+ CK_ATTRIBUTE genTemplate[5];
+ CK_ATTRIBUTE *attrs = genTemplate;
+ int count = sizeof(genTemplate) / sizeof(genTemplate[0]);
+ CK_MECHANISM mechanism;
+ CK_OBJECT_HANDLE objectID = CK_INVALID_HANDLE;
+ CK_RV crv;
+ CK_ATTRIBUTE pTemplate[] = {
+ { CKA_PRIME, NULL, 0 },
+ { CKA_SUBPRIME, NULL, 0 },
+ { CKA_BASE, NULL, 0 },
+ };
+ CK_ATTRIBUTE vTemplate[] = {
+ { CKA_NSS_PQG_COUNTER, NULL, 0 },
+ { CKA_NSS_PQG_SEED, NULL, 0 },
+ { CKA_NSS_PQG_H, NULL, 0 },
+ };
+ CK_ULONG primeBits = L;
+ CK_ULONG subPrimeBits = N;
+ int pTemplateCount = sizeof(pTemplate) / sizeof(pTemplate[0]);
+ int vTemplateCount = sizeof(vTemplate) / sizeof(vTemplate[0]);
+ PLArenaPool *parena = NULL;
+ PLArenaPool *varena = NULL;
+ PQGParams *params = NULL;
+ PQGVerify *verify = NULL;
+ CK_ULONG seedBits = seedBytes * 8;
+
+ *pParams = NULL;
+ *pVfy = NULL;
+
+ if (primeBits == (CK_ULONG)-1) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ goto loser;
+ }
+ PK11_SETATTRS(attrs, CKA_PRIME_BITS, &primeBits, sizeof(primeBits));
+ attrs++;
+ if (subPrimeBits != 0) {
+ PK11_SETATTRS(attrs, CKA_SUB_PRIME_BITS,
+ &subPrimeBits, sizeof(subPrimeBits));
+ attrs++;
+ }
+ if (seedBits != 0) {
+ PK11_SETATTRS(attrs, CKA_NSS_PQG_SEED_BITS,
+ &seedBits, sizeof(seedBits));
+ attrs++;
+ }
+ count = attrs - genTemplate;
+ PR_ASSERT(count <= sizeof(genTemplate) / sizeof(CK_ATTRIBUTE));
+
+ slot = PK11_GetInternalSlot();
+ if (slot == NULL) {
+ /* set error */
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); /* shouldn't happen */
+ goto loser;
+ }
+
+ /* make sure the internal slot can handle DSA2 type parameters. */
+ if (primeBits > 1024) {
+ CK_MECHANISM_INFO mechanism_info;
+
+ if (!slot->isThreadSafe)
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_GetMechanismInfo(slot->slotID,
+ CKM_DSA_PARAMETER_GEN,
+ &mechanism_info);
+ if (!slot->isThreadSafe)
+ PK11_ExitSlotMonitor(slot);
+ /* a bug in the old softoken left CKM_DSA_PARAMETER_GEN off of the
+ * mechanism List. If we get a failure asking for this value, we know
+ * it can't handle DSA2 */
+ if ((crv != CKR_OK) || (mechanism_info.ulMaxKeySize < primeBits)) {
+ PK11_FreeSlot(slot);
+ slot = PK11_GetBestSlotWithAttributes(CKM_DSA_PARAMETER_GEN, 0,
+ primeBits, NULL);
+ if (slot == NULL) {
+ PORT_SetError(SEC_ERROR_NO_TOKEN); /* can happen */
+ goto loser;
+ }
+ /* ditch seedBits in this case, they are NSS specific and at
+ * this point we have a token that claims to handle DSA2 */
+ if (seedBits) {
+ attrs--;
+ }
+ }
+ }
+
+ /* Initialize the Key Gen Mechanism */
+ mechanism.mechanism = CKM_DSA_PARAMETER_GEN;
+ mechanism.pParameter = NULL;
+ mechanism.ulParameterLen = 0;
+
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_GenerateKey(slot->session,
+ &mechanism, genTemplate, count, &objectID);
+ PK11_ExitSlotMonitor(slot);
+
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ goto loser;
+ }
+
+ parena = PORT_NewArena(60);
+ if (!parena) {
+ goto loser;
+ }
+
+ crv = PK11_GetAttributes(parena, slot, objectID, pTemplate, pTemplateCount);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ goto loser;
+ }
+
+ params = (PQGParams *)PORT_ArenaAlloc(parena, sizeof(PQGParams));
+ if (params == NULL) {
+ goto loser;
+ }
+
+ /* fill in Params */
+ params->arena = parena;
+ params->prime.type = siUnsignedInteger;
+ params->prime.data = pTemplate[0].pValue;
+ params->prime.len = pTemplate[0].ulValueLen;
+ params->subPrime.type = siUnsignedInteger;
+ params->subPrime.data = pTemplate[1].pValue;
+ params->subPrime.len = pTemplate[1].ulValueLen;
+ params->base.type = siUnsignedInteger;
+ params->base.data = pTemplate[2].pValue;
+ params->base.len = pTemplate[2].ulValueLen;
+
+ varena = PORT_NewArena(60);
+ if (!varena) {
+ goto loser;
+ }
+
+ crv = PK11_GetAttributes(varena, slot, objectID, vTemplate, vTemplateCount);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ goto loser;
+ }
+
+ verify = (PQGVerify *)PORT_ArenaAlloc(varena, sizeof(PQGVerify));
+ if (verify == NULL) {
+ goto loser;
+ }
+ /* fill in Params */
+ verify->arena = varena;
+ verify->counter = (unsigned int)(*(CK_ULONG *)vTemplate[0].pValue);
+ verify->seed.type = siUnsignedInteger;
+ verify->seed.data = vTemplate[1].pValue;
+ verify->seed.len = vTemplate[1].ulValueLen;
+ verify->h.type = siUnsignedInteger;
+ verify->h.data = vTemplate[2].pValue;
+ verify->h.len = vTemplate[2].ulValueLen;
+
+ PK11_DestroyObject(slot, objectID);
+ PK11_FreeSlot(slot);
+
+ *pParams = params;
+ *pVfy = verify;
+
+ return SECSuccess;
+
+loser:
+ if (objectID != CK_INVALID_HANDLE) {
+ PK11_DestroyObject(slot, objectID);
+ }
+ if (parena != NULL) {
+ PORT_FreeArena(parena, PR_FALSE);
+ }
+ if (varena != NULL) {
+ PORT_FreeArena(varena, PR_FALSE);
+ }
+ if (slot) {
+ PK11_FreeSlot(slot);
+ }
+ return SECFailure;
+}
+
+/* Generate PQGParams and PQGVerify structs.
+ * Length of P specified by j. Length of h will match length of P.
+ * Length of SEED in bytes specified in seedBytes.
+ * seedBbytes must be in the range [20..255] or an error will result.
+ */
+extern SECStatus
+PK11_PQG_ParamGenSeedLen(unsigned int j, unsigned int seedBytes,
+ PQGParams **pParams, PQGVerify **pVfy)
+{
+ unsigned int primeBits = PQG_INDEX_TO_PBITS(j);
+ return PK11_PQG_ParamGenV2(primeBits, 0, seedBytes, pParams, pVfy);
+}
+
+/* Generate PQGParams and PQGVerify structs.
+ * Length of seed and length of h both equal length of P.
+ * All lengths are specified by "j", according to the table above.
+ */
+extern SECStatus
+PK11_PQG_ParamGen(unsigned int j, PQGParams **pParams, PQGVerify **pVfy)
+{
+ unsigned int primeBits = PQG_INDEX_TO_PBITS(j);
+ return PK11_PQG_ParamGenV2(primeBits, 0, 0, pParams, pVfy);
+}
+
+/* Test PQGParams for validity as DSS PQG values.
+ * If vfy is non-NULL, test PQGParams to make sure they were generated
+ * using the specified seed, counter, and h values.
+ *
+ * Return value indicates whether Verification operation ran successfully
+ * to completion, but does not indicate if PQGParams are valid or not.
+ * If return value is SECSuccess, then *pResult has these meanings:
+ * SECSuccess: PQGParams are valid.
+ * SECFailure: PQGParams are invalid.
+ */
+
+extern SECStatus
+PK11_PQG_VerifyParams(const PQGParams *params, const PQGVerify *vfy,
+ SECStatus *result)
+{
+ CK_ATTRIBUTE keyTempl[] = {
+ { CKA_CLASS, NULL, 0 },
+ { CKA_KEY_TYPE, NULL, 0 },
+ { CKA_PRIME, NULL, 0 },
+ { CKA_SUBPRIME, NULL, 0 },
+ { CKA_BASE, NULL, 0 },
+ { CKA_TOKEN, NULL, 0 },
+ { CKA_NSS_PQG_COUNTER, NULL, 0 },
+ { CKA_NSS_PQG_SEED, NULL, 0 },
+ { CKA_NSS_PQG_H, NULL, 0 },
+ };
+ CK_ATTRIBUTE *attrs;
+ CK_BBOOL ckfalse = CK_FALSE;
+ CK_OBJECT_CLASS class = CKO_DOMAIN_PARAMETERS;
+ CK_KEY_TYPE keyType = CKK_DSA;
+ SECStatus rv = SECSuccess;
+ PK11SlotInfo *slot;
+ int keyCount;
+ CK_OBJECT_HANDLE objectID;
+ CK_ULONG counter;
+ CK_RV crv;
+
+ attrs = keyTempl;
+ PK11_SETATTRS(attrs, CKA_CLASS, &class, sizeof(class));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(keyType));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_PRIME, params->prime.data,
+ params->prime.len);
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_SUBPRIME, params->subPrime.data,
+ params->subPrime.len);
+ attrs++;
+ if (params->base.len) {
+ PK11_SETATTRS(attrs, CKA_BASE, params->base.data, params->base.len);
+ attrs++;
+ }
+ PK11_SETATTRS(attrs, CKA_TOKEN, &ckfalse, sizeof(ckfalse));
+ attrs++;
+ if (vfy) {
+ if (vfy->counter != -1) {
+ counter = vfy->counter;
+ PK11_SETATTRS(attrs, CKA_NSS_PQG_COUNTER,
+ &counter, sizeof(counter));
+ attrs++;
+ }
+ PK11_SETATTRS(attrs, CKA_NSS_PQG_SEED,
+ vfy->seed.data, vfy->seed.len);
+ attrs++;
+ if (vfy->h.len) {
+ PK11_SETATTRS(attrs, CKA_NSS_PQG_H,
+ vfy->h.data, vfy->h.len);
+ attrs++;
+ }
+ }
+
+ keyCount = attrs - keyTempl;
+ PORT_Assert(keyCount <= sizeof(keyTempl) / sizeof(keyTempl[0]));
+
+ slot = PK11_GetInternalSlot();
+ if (slot == NULL) {
+ return SECFailure;
+ }
+
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_CreateObject(slot->session, keyTempl, keyCount,
+ &objectID);
+ PK11_ExitSlotMonitor(slot);
+
+ /* throw away the keys, we only wanted the return code */
+ PK11_DestroyObject(slot, objectID);
+ PK11_FreeSlot(slot);
+
+ *result = SECSuccess;
+ if (crv == CKR_ATTRIBUTE_VALUE_INVALID) {
+ *result = SECFailure;
+ } else if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ rv = SECFailure;
+ }
+ return rv;
+}
+
+/**************************************************************************
+ * Free the PQGParams struct and the things it points to. *
+ **************************************************************************/
+extern void
+PK11_PQG_DestroyParams(PQGParams *params)
+{
+ if (params == NULL)
+ return;
+ if (params->arena != NULL) {
+ PORT_FreeArena(params->arena, PR_FALSE); /* don't zero it */
+ } else {
+ SECITEM_FreeItem(&params->prime, PR_FALSE); /* don't free prime */
+ SECITEM_FreeItem(&params->subPrime, PR_FALSE); /* don't free subPrime */
+ SECITEM_FreeItem(&params->base, PR_FALSE); /* don't free base */
+ PORT_Free(params);
+ }
+}
+
+/**************************************************************************
+ * Free the PQGVerify struct and the things it points to. *
+ **************************************************************************/
+extern void
+PK11_PQG_DestroyVerify(PQGVerify *vfy)
+{
+ if (vfy == NULL)
+ return;
+ if (vfy->arena != NULL) {
+ PORT_FreeArena(vfy->arena, PR_FALSE); /* don't zero it */
+ } else {
+ SECITEM_FreeItem(&vfy->seed, PR_FALSE); /* don't free seed */
+ SECITEM_FreeItem(&vfy->h, PR_FALSE); /* don't free h */
+ PORT_Free(vfy);
+ }
+}
+
+#define PQG_DEFAULT_CHUNKSIZE 2048 /* bytes */
+
+/**************************************************************************
+ * Return a pointer to a new PQGParams struct that is constructed from *
+ * copies of the arguments passed in. *
+ * Return NULL on failure. *
+ **************************************************************************/
+extern PQGParams *
+PK11_PQG_NewParams(const SECItem *prime, const SECItem *subPrime,
+ const SECItem *base)
+{
+ PLArenaPool *arena;
+ PQGParams *dest;
+ SECStatus status;
+
+ arena = PORT_NewArena(PQG_DEFAULT_CHUNKSIZE);
+ if (arena == NULL)
+ goto loser;
+
+ dest = (PQGParams *)PORT_ArenaZAlloc(arena, sizeof(PQGParams));
+ if (dest == NULL)
+ goto loser;
+
+ dest->arena = arena;
+
+ status = SECITEM_CopyItem(arena, &dest->prime, prime);
+ if (status != SECSuccess)
+ goto loser;
+
+ status = SECITEM_CopyItem(arena, &dest->subPrime, subPrime);
+ if (status != SECSuccess)
+ goto loser;
+
+ status = SECITEM_CopyItem(arena, &dest->base, base);
+ if (status != SECSuccess)
+ goto loser;
+
+ return dest;
+
+loser:
+ if (arena != NULL)
+ PORT_FreeArena(arena, PR_FALSE);
+ return NULL;
+}
+
+/**************************************************************************
+ * Fills in caller's "prime" SECItem with the prime value in params.
+ * Contents can be freed by calling SECITEM_FreeItem(prime, PR_FALSE);
+ **************************************************************************/
+extern SECStatus
+PK11_PQG_GetPrimeFromParams(const PQGParams *params, SECItem *prime)
+{
+ return SECITEM_CopyItem(NULL, prime, &params->prime);
+}
+
+/**************************************************************************
+ * Fills in caller's "subPrime" SECItem with the prime value in params.
+ * Contents can be freed by calling SECITEM_FreeItem(subPrime, PR_FALSE);
+ **************************************************************************/
+extern SECStatus
+PK11_PQG_GetSubPrimeFromParams(const PQGParams *params, SECItem *subPrime)
+{
+ return SECITEM_CopyItem(NULL, subPrime, &params->subPrime);
+}
+
+/**************************************************************************
+ * Fills in caller's "base" SECItem with the base value in params.
+ * Contents can be freed by calling SECITEM_FreeItem(base, PR_FALSE);
+ **************************************************************************/
+extern SECStatus
+PK11_PQG_GetBaseFromParams(const PQGParams *params, SECItem *base)
+{
+ return SECITEM_CopyItem(NULL, base, &params->base);
+}
+
+/**************************************************************************
+ * Return a pointer to a new PQGVerify struct that is constructed from *
+ * copies of the arguments passed in. *
+ * Return NULL on failure. *
+ **************************************************************************/
+extern PQGVerify *
+PK11_PQG_NewVerify(unsigned int counter, const SECItem *seed,
+ const SECItem *h)
+{
+ PLArenaPool *arena;
+ PQGVerify *dest;
+ SECStatus status;
+
+ arena = PORT_NewArena(PQG_DEFAULT_CHUNKSIZE);
+ if (arena == NULL)
+ goto loser;
+
+ dest = (PQGVerify *)PORT_ArenaZAlloc(arena, sizeof(PQGVerify));
+ if (dest == NULL)
+ goto loser;
+
+ dest->arena = arena;
+ dest->counter = counter;
+
+ status = SECITEM_CopyItem(arena, &dest->seed, seed);
+ if (status != SECSuccess)
+ goto loser;
+
+ status = SECITEM_CopyItem(arena, &dest->h, h);
+ if (status != SECSuccess)
+ goto loser;
+
+ return dest;
+
+loser:
+ if (arena != NULL)
+ PORT_FreeArena(arena, PR_FALSE);
+ return NULL;
+}
+
+/**************************************************************************
+ * Returns "counter" value from the PQGVerify.
+ **************************************************************************/
+extern unsigned int
+PK11_PQG_GetCounterFromVerify(const PQGVerify *verify)
+{
+ return verify->counter;
+}
+
+/**************************************************************************
+ * Fills in caller's "seed" SECItem with the seed value in verify.
+ * Contents can be freed by calling SECITEM_FreeItem(seed, PR_FALSE);
+ **************************************************************************/
+extern SECStatus
+PK11_PQG_GetSeedFromVerify(const PQGVerify *verify, SECItem *seed)
+{
+ return SECITEM_CopyItem(NULL, seed, &verify->seed);
+}
+
+/**************************************************************************
+ * Fills in caller's "h" SECItem with the h value in verify.
+ * Contents can be freed by calling SECITEM_FreeItem(h, PR_FALSE);
+ **************************************************************************/
+extern SECStatus
+PK11_PQG_GetHFromVerify(const PQGVerify *verify, SECItem *h)
+{
+ return SECITEM_CopyItem(NULL, h, &verify->h);
+}
diff --git a/security/nss/lib/pk11wrap/pk11pqg.h b/security/nss/lib/pk11wrap/pk11pqg.h
new file mode 100644
index 0000000000..36596dd479
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11pqg.h
@@ -0,0 +1,135 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/* Thse functions are stub functions which will get replaced with calls through
+ * PKCS #11.
+ */
+
+#ifndef _PK11PQG_H_
+#define _PK11PQG_H_ 1
+
+#include "blapit.h"
+
+SEC_BEGIN_PROTOS
+
+/* Generate PQGParams and PQGVerify structs.
+ * Length of seed and length of h both equal length of P.
+ * All lengths are specified by "j", according to the table above.
+ */
+extern SECStatus PK11_PQG_ParamGen(unsigned int j, PQGParams **pParams,
+ PQGVerify **pVfy);
+
+/* Generate PQGParams and PQGVerify structs.
+ * Length of P specified by j. Length of h will match length of P.
+ * Length of SEED in bytes specified in seedBytes.
+ * seedBbytes must be in the range [20..255] or an error will result.
+ */
+extern SECStatus PK11_PQG_ParamGenSeedLen(unsigned int j,
+ unsigned int seedBytes, PQGParams **pParams, PQGVerify **pVfy);
+
+/* Generate PQGParams and PQGVerify structs.
+ * Length of P specified by L.
+ * if L is greater than 1024 then the resulting verify parameters will be
+ * DSA2.
+ * Length of Q specified by N. If zero, The PKCS #11 module will
+ * pick an appropriately sized Q for L. If N is specified and L = 1024, then
+ * the resulting verify parameters will be DSA2, Otherwise DSA1 parameters
+ * will be returned.
+ * Length of SEED in bytes specified in seedBytes.
+ *
+ * The underlying PKCS #11 module will check the values for L, N,
+ * and seedBytes. The rules for softoken are:
+ *
+ * If L <= 1024, then L must be between 512 and 1024 in increments of 64 bits.
+ * If L <= 1024, then N must be 0 or 160.
+ * If L >= 1024, then L and N must match the following table:
+ * L=1024 N=0 or 160
+ * L=2048 N=0 or 224
+ * L=2048 N=256
+ * L=3072 N=0 or 256
+ * if L <= 1024
+ * seedBbytes must be in the range [20..256].
+ * if L >= 1024
+ * seedBbytes must be in the range [20..L/16].
+ */
+extern SECStatus
+PK11_PQG_ParamGenV2(unsigned int L, unsigned int N, unsigned int seedBytes,
+ PQGParams **pParams, PQGVerify **pVfy);
+
+/* Test PQGParams for validity as DSS PQG values.
+ * If vfy is non-NULL, test PQGParams to make sure they were generated
+ * using the specified seed, counter, and h values.
+ *
+ * Return value indicates whether Verification operation ran successfully
+ * to completion, but does not indicate if PQGParams are valid or not.
+ * If return value is SECSuccess, then *pResult has these meanings:
+ * SECSuccess: PQGParams are valid.
+ * SECFailure: PQGParams are invalid.
+ *
+ * Verify the following 12 facts about PQG counter SEED g and h
+ * These tests are specified in FIPS 186-3 Appendix A.1.1.1, A.1.1.3, and A.2.2
+ * PQG_VerifyParams in softoken/freebl will automatically choose the
+ * appropriate test.
+ */
+extern SECStatus PK11_PQG_VerifyParams(const PQGParams *params,
+ const PQGVerify *vfy, SECStatus *result);
+extern void PK11_PQG_DestroyParams(PQGParams *params);
+extern void PK11_PQG_DestroyVerify(PQGVerify *vfy);
+
+/**************************************************************************
+ * Return a pointer to a new PQGParams struct that is constructed from *
+ * copies of the arguments passed in. *
+ * Return NULL on failure. *
+ **************************************************************************/
+extern PQGParams *PK11_PQG_NewParams(const SECItem *prime, const SECItem *subPrime, const SECItem *base);
+
+/**************************************************************************
+ * Fills in caller's "prime" SECItem with the prime value in params.
+ * Contents can be freed by calling SECITEM_FreeItem(prime, PR_FALSE);
+ **************************************************************************/
+extern SECStatus PK11_PQG_GetPrimeFromParams(const PQGParams *params,
+ SECItem *prime);
+
+/**************************************************************************
+ * Fills in caller's "subPrime" SECItem with the prime value in params.
+ * Contents can be freed by calling SECITEM_FreeItem(subPrime, PR_FALSE);
+ **************************************************************************/
+extern SECStatus PK11_PQG_GetSubPrimeFromParams(const PQGParams *params,
+ SECItem *subPrime);
+
+/**************************************************************************
+ * Fills in caller's "base" SECItem with the base value in params.
+ * Contents can be freed by calling SECITEM_FreeItem(base, PR_FALSE);
+ **************************************************************************/
+extern SECStatus PK11_PQG_GetBaseFromParams(const PQGParams *params,
+ SECItem *base);
+
+/**************************************************************************
+ * Return a pointer to a new PQGVerify struct that is constructed from *
+ * copies of the arguments passed in. *
+ * Return NULL on failure. *
+ **************************************************************************/
+extern PQGVerify *PK11_PQG_NewVerify(unsigned int counter,
+ const SECItem *seed, const SECItem *h);
+
+/**************************************************************************
+ * Returns "counter" value from the PQGVerify.
+ **************************************************************************/
+extern unsigned int PK11_PQG_GetCounterFromVerify(const PQGVerify *verify);
+
+/**************************************************************************
+ * Fills in caller's "seed" SECItem with the seed value in verify.
+ * Contents can be freed by calling SECITEM_FreeItem(seed, PR_FALSE);
+ **************************************************************************/
+extern SECStatus PK11_PQG_GetSeedFromVerify(const PQGVerify *verify,
+ SECItem *seed);
+
+/**************************************************************************
+ * Fills in caller's "h" SECItem with the h value in verify.
+ * Contents can be freed by calling SECITEM_FreeItem(h, PR_FALSE);
+ **************************************************************************/
+extern SECStatus PK11_PQG_GetHFromVerify(const PQGVerify *verify, SECItem *h);
+
+SEC_END_PROTOS
+
+#endif
diff --git a/security/nss/lib/pk11wrap/pk11priv.h b/security/nss/lib/pk11wrap/pk11priv.h
new file mode 100644
index 0000000000..faf9ad2985
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11priv.h
@@ -0,0 +1,210 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _PK11PRIV_H_
+#define _PK11PRIV_H_
+
+#include <stddef.h>
+
+#include "plarena.h"
+#include "seccomon.h"
+#include "secoidt.h"
+#include "secdert.h"
+#include "keythi.h"
+#include "certt.h"
+#include "pkcs11t.h"
+#include "secmodt.h"
+#include "seccomon.h"
+#include "pkcs7t.h"
+#include "cmsreclist.h"
+#include "pkcs11uri.h"
+
+/*
+ * These are the private NSS functions. They are not exported by nss.def, and
+ * are not callable outside nss3.dll.
+ */
+
+SEC_BEGIN_PROTOS
+
+/************************************************************
+ * Generic Slot Lists Management
+ ************************************************************/
+PK11SlotList *PK11_NewSlotList(void);
+PK11SlotList *PK11_GetPrivateKeyTokens(CK_MECHANISM_TYPE type,
+ PRBool needRW, void *wincx);
+SECStatus PK11_AddSlotToList(PK11SlotList *list, PK11SlotInfo *slot, PRBool sorted);
+SECStatus PK11_DeleteSlotFromList(PK11SlotList *list, PK11SlotListElement *le);
+PK11SlotListElement *PK11_FindSlotElement(PK11SlotList *list,
+ PK11SlotInfo *slot);
+PK11SlotInfo *PK11_FindSlotBySerial(char *serial);
+int PK11_GetMaxKeyLength(CK_MECHANISM_TYPE type);
+
+/************************************************************
+ * Generic Slot Management
+ ************************************************************/
+CK_OBJECT_HANDLE PK11_CopyKey(PK11SlotInfo *slot, CK_OBJECT_HANDLE srcObject);
+PRBool pk11_MatchUriTokenInfo(PK11SlotInfo *slot, PK11URI *uri);
+SECStatus PK11_ReadAttribute(PK11SlotInfo *slot, CK_OBJECT_HANDLE id,
+ CK_ATTRIBUTE_TYPE type, PLArenaPool *arena, SECItem *result);
+CK_ULONG PK11_ReadULongAttribute(PK11SlotInfo *slot, CK_OBJECT_HANDLE id,
+ CK_ATTRIBUTE_TYPE type);
+char *PK11_MakeString(PLArenaPool *arena, char *space, char *staticSring,
+ int stringLen);
+PRBool pk11_MatchString(const char *string,
+ const char *staticString, size_t staticStringLen);
+int PK11_MapError(CK_RV error);
+CK_SESSION_HANDLE PK11_GetRWSession(PK11SlotInfo *slot);
+void PK11_RestoreROSession(PK11SlotInfo *slot, CK_SESSION_HANDLE rwsession);
+PRBool PK11_RWSessionHasLock(PK11SlotInfo *slot,
+ CK_SESSION_HANDLE session_handle);
+PK11SlotInfo *PK11_NewSlotInfo(SECMODModule *mod);
+void PK11_EnterSlotMonitor(PK11SlotInfo *);
+void PK11_ExitSlotMonitor(PK11SlotInfo *);
+void PK11_CleanKeyList(PK11SlotInfo *slot);
+
+/************************************************************
+ * Slot Password Management
+ ************************************************************/
+SECStatus PK11_DoPassword(PK11SlotInfo *slot, CK_SESSION_HANDLE session,
+ PRBool loadCerts, void *wincx, PRBool alreadyLocked,
+ PRBool contextSpecific);
+SECStatus PK11_VerifyPW(PK11SlotInfo *slot, char *pw);
+void PK11_HandlePasswordCheck(PK11SlotInfo *slot, void *wincx);
+void PK11_SetVerifyPasswordFunc(PK11VerifyPasswordFunc func);
+void PK11_SetIsLoggedInFunc(PK11IsLoggedInFunc func);
+
+/************************************************************
+ * Manage the built-In Slot Lists
+ ************************************************************/
+SECStatus PK11_InitSlotLists(void);
+void PK11_DestroySlotLists(void);
+PK11SlotList *PK11_GetSlotList(CK_MECHANISM_TYPE type);
+void PK11_LoadSlotList(PK11SlotInfo *slot, PK11PreSlotInfo *psi, int count);
+void PK11_ClearSlotList(PK11SlotInfo *slot);
+
+/******************************************************************
+ * Slot initialization
+ ******************************************************************/
+SECStatus PK11_InitToken(PK11SlotInfo *slot, PRBool loadCerts);
+void PK11_InitSlot(SECMODModule *mod, CK_SLOT_ID slotID, PK11SlotInfo *slot);
+PRBool PK11_NeedPWInitForSlot(PK11SlotInfo *slot);
+SECStatus PK11_ReadSlotCerts(PK11SlotInfo *slot);
+void pk11_SetInternalKeySlot(PK11SlotInfo *slot);
+PK11SlotInfo *pk11_SwapInternalKeySlot(PK11SlotInfo *slot);
+void pk11_SetInternalKeySlotIfFirst(PK11SlotInfo *slot);
+
+/*********************************************************************
+ * Mechanism Mapping functions
+ *********************************************************************/
+void PK11_AddMechanismEntry(CK_MECHANISM_TYPE type, CK_KEY_TYPE key,
+ CK_MECHANISM_TYPE keygen, CK_MECHANISM_TYPE pad,
+ int ivLen, int blocksize);
+CK_MECHANISM_TYPE PK11_GetKeyMechanism(CK_KEY_TYPE type);
+CK_MECHANISM_TYPE PK11_GetKeyGenWithSize(CK_MECHANISM_TYPE type, int size);
+PRBool PK11_DoesMechanismFlag(PK11SlotInfo *, CK_MECHANISM_TYPE type, CK_FLAGS flags);
+
+/**********************************************************************
+ * Symetric, Public, and Private Keys
+ **********************************************************************/
+/* Key Generation specialized for SDR (fixed DES3 key) */
+PK11SymKey *PK11_GenDES3TokenKey(PK11SlotInfo *slot, SECItem *keyid, void *cx);
+SECKEYPublicKey *PK11_ExtractPublicKey(PK11SlotInfo *slot, KeyType keyType,
+ CK_OBJECT_HANDLE id);
+CK_OBJECT_HANDLE PK11_FindObjectForCert(CERTCertificate *cert,
+ void *wincx, PK11SlotInfo **pSlot);
+PK11SymKey *pk11_CopyToSlot(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
+ CK_ATTRIBUTE_TYPE operation, PK11SymKey *symKey);
+unsigned int pk11_GetPredefinedKeyLength(CK_KEY_TYPE keyType);
+CK_OBJECT_HANDLE PK11_DerivePubKeyFromPrivKey(SECKEYPrivateKey *privKey);
+
+/**********************************************************************
+ * Certs
+ **********************************************************************/
+SECStatus PK11_TraversePrivateKeysInSlot(PK11SlotInfo *slot,
+ SECStatus (*callback)(SECKEYPrivateKey *, void *), void *arg);
+SECKEYPrivateKey *PK11_FindPrivateKeyFromNickname(char *nickname, void *wincx);
+CK_OBJECT_HANDLE *PK11_FindObjectsFromNickname(char *nickname,
+ PK11SlotInfo **slotptr, CK_OBJECT_CLASS objclass, int *returnCount,
+ void *wincx);
+CK_OBJECT_HANDLE PK11_MatchItem(PK11SlotInfo *slot, CK_OBJECT_HANDLE peer,
+ CK_OBJECT_CLASS o_class);
+CK_BBOOL pk11_HasAttributeSet_Lock(PK11SlotInfo *slot,
+ CK_OBJECT_HANDLE id,
+ CK_ATTRIBUTE_TYPE type,
+ PRBool haslock);
+CK_RV PK11_GetAttributes(PLArenaPool *arena, PK11SlotInfo *slot,
+ CK_OBJECT_HANDLE obj, CK_ATTRIBUTE *attr, int count);
+int PK11_NumberCertsForCertSubject(CERTCertificate *cert);
+SECStatus PK11_TraverseCertsForSubject(CERTCertificate *cert,
+ SECStatus (*callback)(CERTCertificate *, void *), void *arg);
+SECStatus PK11_GetKEAMatchedCerts(PK11SlotInfo *slot1,
+ PK11SlotInfo *slot2, CERTCertificate **cert1, CERTCertificate **cert2);
+SECStatus PK11_TraverseCertsInSlot(PK11SlotInfo *slot,
+ SECStatus (*callback)(CERTCertificate *, void *), void *arg);
+SECStatus PK11_LookupCrls(CERTCrlHeadNode *nodes, int type, void *wincx);
+
+/**********************************************************************
+ * Crypto Contexts
+ **********************************************************************/
+PK11Context *PK11_CreateContextByRawKey(PK11SlotInfo *slot,
+ CK_MECHANISM_TYPE type, PK11Origin origin, CK_ATTRIBUTE_TYPE operation,
+ SECItem *key, SECItem *param, void *wincx);
+PRBool PK11_HashOK(SECOidTag hashAlg);
+/*
+ * Testing interfaces, not for general use. If your code isn't in
+ * gtests or cmd, stay away from these. This function forces
+ * an AEAD context into simulation mode even though the target token
+ * can already do PKCS #11 v3.0 Message (e.i. softoken).
+ */
+SECStatus _PK11_ContextSetAEADSimulation(PK11Context *context);
+PRBool _PK11_ContextGetAEADSimulation(PK11Context *context);
+
+/**********************************************************************
+ * Functions which are deprecated....
+ **********************************************************************/
+
+SECItem *
+PK11_FindCrlByName(PK11SlotInfo **slot, CK_OBJECT_HANDLE *handle,
+ SECItem *derName, int type, char **url);
+
+CK_OBJECT_HANDLE
+PK11_PutCrl(PK11SlotInfo *slot, SECItem *crl,
+ SECItem *name, char *url, int type);
+
+SECItem *
+PK11_FindSMimeProfile(PK11SlotInfo **slotp, char *emailAddr, SECItem *derSubj,
+ SECItem **profileTime);
+SECStatus
+PK11_SaveSMimeProfile(PK11SlotInfo *slot, char *emailAddr, SECItem *derSubj,
+ SECItem *emailProfile, SECItem *profileTime);
+
+PRBool PK11_IsPermObject(PK11SlotInfo *slot, CK_OBJECT_HANDLE handle);
+
+char *PK11_GetObjectNickname(PK11SlotInfo *slot, CK_OBJECT_HANDLE id);
+SECStatus PK11_SetObjectNickname(PK11SlotInfo *slot, CK_OBJECT_HANDLE id,
+ const char *nickname);
+
+/* private */
+SECStatus pk11_TraverseAllSlots(SECStatus (*callback)(PK11SlotInfo *, void *),
+ void *cbArg, PRBool forceLogin, void *pwArg);
+
+/* fetch multiple CRLs for a specific issuer */
+SECStatus pk11_RetrieveCrls(CERTCrlHeadNode *nodes, SECItem *issuer,
+ void *wincx);
+
+/* set global options for NSS PKCS#11 module loader */
+SECStatus pk11_setGlobalOptions(PRBool noSingleThreadedModules,
+ PRBool allowAlreadyInitializedModules,
+ PRBool dontFinalizeModules);
+
+/* return whether NSS is allowed to call C_Finalize */
+PRBool pk11_getFinalizeModulesOption(void);
+
+/* fetch the FIPS state from the fips indicator, public versions of
+ * this function operate on the slot, the context, and the object */
+PRBool pk11slot_GetFIPSStatus(PK11SlotInfo *slot, CK_SESSION_HANDLE session,
+ CK_OBJECT_HANDLE object, CK_ULONG operationType);
+
+SEC_END_PROTOS
+
+#endif
diff --git a/security/nss/lib/pk11wrap/pk11pub.h b/security/nss/lib/pk11wrap/pk11pub.h
new file mode 100644
index 0000000000..6530d42bd5
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11pub.h
@@ -0,0 +1,1054 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _PK11PUB_H_
+#define _PK11PUB_H_
+#include "plarena.h"
+#include "seccomon.h"
+#include "secoidt.h"
+#include "secdert.h"
+#include "keythi.h"
+#include "certt.h"
+#include "pk11hpke.h"
+#include "pkcs11t.h"
+#include "secmodt.h"
+#include "seccomon.h"
+#include "pkcs7t.h"
+#include "cmsreclist.h"
+
+/*
+ * Exported PK11 wrap functions.
+ */
+
+SEC_BEGIN_PROTOS
+
+/************************************************************
+ * Generic Slot Lists Management
+ ************************************************************/
+void PK11_FreeSlotList(PK11SlotList *list);
+SECStatus PK11_FreeSlotListElement(PK11SlotList *list, PK11SlotListElement *le);
+PK11SlotListElement *PK11_GetFirstSafe(PK11SlotList *list);
+PK11SlotListElement *PK11_GetNextSafe(PK11SlotList *list,
+ PK11SlotListElement *le, PRBool restart);
+
+/************************************************************
+ * Generic Slot Management
+ ************************************************************/
+PK11SlotInfo *PK11_ReferenceSlot(PK11SlotInfo *slot);
+void PK11_FreeSlot(PK11SlotInfo *slot);
+SECStatus PK11_DestroyObject(PK11SlotInfo *slot, CK_OBJECT_HANDLE object);
+SECStatus PK11_DestroyTokenObject(PK11SlotInfo *slot, CK_OBJECT_HANDLE object);
+PK11SlotInfo *PK11_GetInternalKeySlot(void);
+PK11SlotInfo *PK11_GetInternalSlot(void);
+SECStatus PK11_Logout(PK11SlotInfo *slot);
+void PK11_LogoutAll(void);
+
+/************************************************************
+ * Slot Password Management
+ ************************************************************/
+void PK11_SetSlotPWValues(PK11SlotInfo *slot, int askpw, int timeout);
+void PK11_GetSlotPWValues(PK11SlotInfo *slot, int *askpw, int *timeout);
+SECStatus PK11_CheckSSOPassword(PK11SlotInfo *slot, char *ssopw);
+SECStatus PK11_CheckUserPassword(PK11SlotInfo *slot, const char *pw);
+PRBool PK11_IsLoggedIn(PK11SlotInfo *slot, void *wincx);
+SECStatus PK11_InitPin(PK11SlotInfo *slot, const char *ssopw,
+ const char *pk11_userpwd);
+SECStatus PK11_ChangePW(PK11SlotInfo *slot, const char *oldpw,
+ const char *newpw);
+void PK11_SetPasswordFunc(PK11PasswordFunc func);
+int PK11_GetMinimumPwdLength(PK11SlotInfo *slot);
+SECStatus PK11_ResetToken(PK11SlotInfo *slot, char *sso_pwd);
+SECStatus PK11_Authenticate(PK11SlotInfo *slot, PRBool loadCerts, void *wincx);
+SECStatus PK11_TokenRefresh(PK11SlotInfo *slot);
+
+/******************************************************************
+ * Slot info functions
+ ******************************************************************/
+PK11SlotInfo *PK11_FindSlotByName(const char *name);
+/******************************************************************
+ * PK11_FindSlotsByNames searches for a PK11SlotInfo using one or
+ * more criteria : dllName, slotName and tokenName . In addition, if
+ * presentOnly is set , only slots with a token inserted will be
+ * returned.
+ ******************************************************************/
+PK11SlotList *PK11_FindSlotsByNames(const char *dllName,
+ const char *slotName, const char *tokenName, PRBool presentOnly);
+PRBool PK11_IsReadOnly(PK11SlotInfo *slot);
+PRBool PK11_IsInternal(PK11SlotInfo *slot);
+PRBool PK11_IsInternalKeySlot(PK11SlotInfo *slot);
+char *PK11_GetTokenName(PK11SlotInfo *slot);
+char *PK11_GetTokenURI(PK11SlotInfo *slot);
+char *PK11_GetSlotName(PK11SlotInfo *slot);
+PRBool PK11_NeedLogin(PK11SlotInfo *slot);
+PRBool PK11_IsFriendly(PK11SlotInfo *slot);
+PRBool PK11_IsHW(PK11SlotInfo *slot);
+PRBool PK11_IsRemovable(PK11SlotInfo *slot);
+PRBool PK11_NeedUserInit(PK11SlotInfo *slot);
+PRBool PK11_ProtectedAuthenticationPath(PK11SlotInfo *slot);
+int PK11_GetSlotSeries(PK11SlotInfo *slot);
+int PK11_GetCurrentWrapIndex(PK11SlotInfo *slot);
+unsigned long PK11_GetDefaultFlags(PK11SlotInfo *slot);
+CK_SLOT_ID PK11_GetSlotID(PK11SlotInfo *slot);
+SECMODModuleID PK11_GetModuleID(PK11SlotInfo *slot);
+SECStatus PK11_GetSlotInfo(PK11SlotInfo *slot, CK_SLOT_INFO *info);
+SECStatus PK11_GetTokenInfo(PK11SlotInfo *slot, CK_TOKEN_INFO *info);
+PRBool PK11_IsDisabled(PK11SlotInfo *slot);
+PRBool PK11_HasRootCerts(PK11SlotInfo *slot);
+PK11DisableReasons PK11_GetDisabledReason(PK11SlotInfo *slot);
+/* Prevents the slot from being used, and set disable reason to user-disable */
+/* NOTE: Mechanisms that were ON continue to stay ON */
+/* Therefore, when the slot is enabled, it will remember */
+/* what mechanisms needs to be turned on */
+PRBool PK11_UserDisableSlot(PK11SlotInfo *slot);
+/* Allow all mechanisms that are ON before UserDisableSlot() */
+/* was called to be available again */
+PRBool PK11_UserEnableSlot(PK11SlotInfo *slot);
+/*
+ * wait for a specific slot event.
+ * event is a specific event to wait for. Currently only
+ * PK11TokenChangeOrRemovalEvent and PK11TokenPresentEvents are defined.
+ * timeout can be an interval time to wait, PR_INTERVAL_NO_WAIT (meaning only
+ * poll once), or PR_INTERVAL_NO_TIMEOUT (meaning block until a change).
+ * pollInterval is a suggested pulling interval value. '0' means use the
+ * default. Future implementations that don't poll may ignore this value.
+ * series is the current series for the last slot. This should be the series
+ * value for the slot the last time you read persistant information from the
+ * slot. For instance, if you publish a cert from the slot, you should obtain
+ * the slot series at that time. Then PK11_WaitForTokenEvent can detect a
+ * a change in the slot between the time you publish and the time
+ * PK11_WaitForTokenEvent is called, elliminating potential race conditions.
+ *
+ * The current status that is returned is:
+ * PK11TokenNotRemovable - always returned for any non-removable token.
+ * PK11TokenPresent - returned when the token is present and we are waiting
+ * on a PK11TokenPresentEvent. Then next event to look for is a
+ * PK11TokenChangeOrRemovalEvent.
+ * PK11TokenChanged - returned when the old token has been removed and a new
+ * token ad been inserted, and we are waiting for a
+ * PK11TokenChangeOrRemovalEvent. The next event to look for is another
+ * PK11TokenChangeOrRemovalEvent.
+ * PK11TokenRemoved - returned when the token is not present and we are
+ * waiting for a PK11TokenChangeOrRemovalEvent. The next event to look for
+ * is a PK11TokenPresentEvent.
+ */
+PK11TokenStatus PK11_WaitForTokenEvent(PK11SlotInfo *slot, PK11TokenEvent event,
+ PRIntervalTime timeout, PRIntervalTime pollInterval, int series);
+
+PRBool PK11_NeedPWInit(void);
+PRBool PK11_TokenExists(CK_MECHANISM_TYPE);
+SECStatus PK11_GetModInfo(SECMODModule *mod, CK_INFO *info);
+char *PK11_GetModuleURI(SECMODModule *mod);
+PRBool PK11_IsFIPS(void);
+SECMODModule *PK11_GetModule(PK11SlotInfo *slot);
+
+/*********************************************************************
+ * Slot mapping utility functions.
+ *********************************************************************/
+PRBool PK11_IsPresent(PK11SlotInfo *slot);
+PRBool PK11_DoesMechanism(PK11SlotInfo *slot, CK_MECHANISM_TYPE type);
+PK11SlotList *PK11_GetAllTokens(CK_MECHANISM_TYPE type, PRBool needRW,
+ PRBool loadCerts, void *wincx);
+PK11SlotInfo *PK11_GetBestSlotMultipleWithAttributes(CK_MECHANISM_TYPE *type,
+ CK_FLAGS *mechFlag, unsigned int *keySize,
+ unsigned int count, void *wincx);
+PK11SlotInfo *PK11_GetBestSlotMultiple(CK_MECHANISM_TYPE *type,
+ unsigned int count, void *wincx);
+PK11SlotInfo *PK11_GetBestSlot(CK_MECHANISM_TYPE type, void *wincx);
+PK11SlotInfo *PK11_GetBestSlotWithAttributes(CK_MECHANISM_TYPE type,
+ CK_FLAGS mechFlag, unsigned int keySize, void *wincx);
+CK_MECHANISM_TYPE PK11_GetBestWrapMechanism(PK11SlotInfo *slot);
+int PK11_GetBestKeyLength(PK11SlotInfo *slot, CK_MECHANISM_TYPE type);
+
+/*
+ * Open a new database using the softoken. The caller is responsible for making
+ * sure the module spec is correct and usable. The caller should ask for one
+ * new database per call if the caller wants to get meaningful information
+ * about the new database.
+ *
+ * moduleSpec is the same data that you would pass to softoken at
+ * initialization time under the 'tokens' options. For example, if you were
+ * to specify tokens=<0x4=[configdir='./mybackup' tokenDescription='Backup']>
+ * You would specify "configdir='./mybackup' tokenDescription='Backup'" as your
+ * module spec here. The slot ID will be calculated for you by
+ * SECMOD_OpenUserDB().
+ *
+ * Typical parameters here are configdir, tokenDescription and flags.
+ *
+ * a Full list is below:
+ *
+ *
+ * configDir - The location of the databases for this token. If configDir is
+ * not specified, and noCertDB and noKeyDB is not specified, the load
+ * will fail.
+ * certPrefix - Cert prefix for this token.
+ * keyPrefix - Prefix for the key database for this token. (if not specified,
+ * certPrefix will be used).
+ * tokenDescription - The label value for this token returned in the
+ * CK_TOKEN_INFO structure with an internationalize string (UTF8).
+ * This value will be truncated at 32 bytes (no NULL, partial UTF8
+ * characters dropped). You should specify a user friendly name here
+ * as this is the value the token will be referred to in most
+ * application UI's. You should make sure tokenDescription is unique.
+ * slotDescription - The slotDescription value for this token returned
+ * in the CK_SLOT_INFO structure with an internationalize string
+ * (UTF8). This value will be truncated at 64 bytes (no NULL, partial
+ * UTF8 characters dropped). This name will not change after the
+ * database is closed. It should have some number to make this unique.
+ * minPWLen - minimum password length for this token.
+ * flags - comma separated list of flag values, parsed case-insensitive.
+ * Valid flags are:
+ * readOnly - Databases should be opened read only.
+ * noCertDB - Don't try to open a certificate database.
+ * noKeyDB - Don't try to open a key database.
+ * forceOpen - Don't fail to initialize the token if the
+ * databases could not be opened.
+ * passwordRequired - zero length passwords are not acceptable
+ * (valid only if there is a keyDB).
+ * optimizeSpace - allocate smaller hash tables and lock tables.
+ * When this flag is not specified, Softoken will allocate
+ * large tables to prevent lock contention.
+ */
+PK11SlotInfo *SECMOD_OpenUserDB(const char *moduleSpec);
+SECStatus SECMOD_CloseUserDB(PK11SlotInfo *slot);
+
+/*
+ * This is exactly the same as OpenUserDB except it can be called on any
+ * module that understands softoken style new slot entries. The resulting
+ * slot can be closed using SECMOD_CloseUserDB above. Value of moduleSpec
+ * is token specific.
+ */
+PK11SlotInfo *SECMOD_OpenNewSlot(SECMODModule *mod, const char *moduleSpec);
+
+/*
+ * merge the permanent objects from on token to another
+ */
+SECStatus PK11_MergeTokens(PK11SlotInfo *targetSlot, PK11SlotInfo *sourceSlot,
+ PK11MergeLog *log, void *targetPwArg, void *sourcePwArg);
+
+/*
+ * create and destroy merge logs needed by PK11_MergeTokens
+ */
+PK11MergeLog *PK11_CreateMergeLog(void);
+void PK11_DestroyMergeLog(PK11MergeLog *log);
+
+/*********************************************************************
+ * Mechanism Mapping functions
+ *********************************************************************/
+CK_KEY_TYPE PK11_GetKeyType(CK_MECHANISM_TYPE type, unsigned long len);
+CK_MECHANISM_TYPE PK11_GetKeyGen(CK_MECHANISM_TYPE type);
+int PK11_GetBlockSize(CK_MECHANISM_TYPE type, SECItem *params);
+int PK11_GetIVLength(CK_MECHANISM_TYPE type);
+SECItem *PK11_ParamFromIV(CK_MECHANISM_TYPE type, SECItem *iv);
+unsigned char *PK11_IVFromParam(CK_MECHANISM_TYPE type, SECItem *param, int *len);
+SECItem *PK11_BlockData(SECItem *data, unsigned long size);
+
+/* PKCS #11 to DER mapping functions */
+SECItem *PK11_ParamFromAlgid(SECAlgorithmID *algid);
+SECItem *PK11_GenerateNewParam(CK_MECHANISM_TYPE, PK11SymKey *);
+CK_MECHANISM_TYPE PK11_AlgtagToMechanism(SECOidTag algTag);
+SECOidTag PK11_MechanismToAlgtag(CK_MECHANISM_TYPE type);
+SECOidTag PK11_FortezzaMapSig(SECOidTag algTag);
+SECStatus PK11_ParamToAlgid(SECOidTag algtag, SECItem *param,
+ PLArenaPool *arena, SECAlgorithmID *algid);
+SECStatus PK11_SeedRandom(PK11SlotInfo *, unsigned char *data, int len);
+SECStatus PK11_GenerateRandomOnSlot(PK11SlotInfo *, unsigned char *data, int len);
+SECStatus PK11_RandomUpdate(void *data, size_t bytes);
+SECStatus PK11_GenerateRandom(unsigned char *data, int len);
+
+/* warning: cannot work with pkcs 5 v2
+ * use algorithm ID s instead of pkcs #11 mechanism pointers */
+CK_RV PK11_MapPBEMechanismToCryptoMechanism(CK_MECHANISM_PTR pPBEMechanism,
+ CK_MECHANISM_PTR pCryptoMechanism,
+ SECItem *pbe_pwd, PRBool bad3DES);
+CK_MECHANISM_TYPE PK11_GetPadMechanism(CK_MECHANISM_TYPE);
+CK_MECHANISM_TYPE PK11_MapSignKeyType(KeyType keyType);
+
+/**********************************************************************
+ * Symmetric, Public, and Private Keys
+ **********************************************************************/
+void PK11_FreeSymKey(PK11SymKey *key);
+PK11SymKey *PK11_ReferenceSymKey(PK11SymKey *symKey);
+PK11SymKey *PK11_ImportDataKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, PK11Origin origin,
+ CK_ATTRIBUTE_TYPE operation, SECItem *key, void *wincx);
+PK11SymKey *PK11_ImportSymKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
+ PK11Origin origin, CK_ATTRIBUTE_TYPE operation, SECItem *key, void *wincx);
+PK11SymKey *PK11_ImportSymKeyWithFlags(PK11SlotInfo *slot,
+ CK_MECHANISM_TYPE type, PK11Origin origin, CK_ATTRIBUTE_TYPE operation,
+ SECItem *key, CK_FLAGS flags, PRBool isPerm, void *wincx);
+PK11SymKey *PK11_SymKeyFromHandle(PK11SlotInfo *slot, PK11SymKey *parent,
+ PK11Origin origin, CK_MECHANISM_TYPE type, CK_OBJECT_HANDLE keyID,
+ PRBool owner, void *wincx);
+/* PK11_GetWrapKey and PK11_SetWrapKey are not thread safe. */
+PK11SymKey *PK11_GetWrapKey(PK11SlotInfo *slot, int wrap,
+ CK_MECHANISM_TYPE type, int series, void *wincx);
+void PK11_SetWrapKey(PK11SlotInfo *slot, int wrap, PK11SymKey *wrapKey);
+CK_MECHANISM_TYPE PK11_GetMechanism(PK11SymKey *symKey);
+/*
+ * import a public key into the desired slot
+ *
+ * This function takes a public key structure and creates a public key in a
+ * given slot. If isToken is set, then a persistant public key is created.
+ *
+ * Note: it is possible for this function to return a handle for a key which
+ * is persistant, even if isToken is not set.
+ */
+CK_OBJECT_HANDLE PK11_ImportPublicKey(PK11SlotInfo *slot,
+ SECKEYPublicKey *pubKey, PRBool isToken);
+PK11SymKey *PK11_KeyGen(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
+ SECItem *param, int keySize, void *wincx);
+PK11SymKey *PK11_TokenKeyGen(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
+ SECItem *param, int keySize, SECItem *keyid,
+ PRBool isToken, void *wincx);
+PK11SymKey *PK11_TokenKeyGenWithFlags(PK11SlotInfo *slot,
+ CK_MECHANISM_TYPE type, SECItem *param,
+ int keySize, SECItem *keyid, CK_FLAGS opFlags,
+ PK11AttrFlags attrFlags, void *wincx);
+/* Generates a key using the exact template supplied by the caller. The other
+ * PK11_[Token]KeyGen mechanisms should be used instead of this one whenever
+ * they work because they include/exclude the CKA_VALUE_LEN template value
+ * based on the mechanism type as required by many tokens.
+ *
+ * keyGenType should be PK11_GetKeyGenWithSize(type, <key size>) or it should
+ * be equal to type if PK11_GetKeyGenWithSize cannot be used (e.g. because
+ * pk11wrap does not know about the mechanisms).
+ */
+PK11SymKey *PK11_KeyGenWithTemplate(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
+ CK_MECHANISM_TYPE keyGenType,
+ SECItem *param, CK_ATTRIBUTE *attrs,
+ unsigned int attrsCount, void *wincx);
+PK11SymKey *PK11_ListFixedKeysInSlot(PK11SlotInfo *slot, char *nickname,
+ void *wincx);
+PK11SymKey *PK11_GetNextSymKey(PK11SymKey *symKey);
+CK_KEY_TYPE PK11_GetSymKeyType(PK11SymKey *key);
+CK_OBJECT_HANDLE PK11_GetSymKeyHandle(PK11SymKey *symKey);
+
+/*
+ * PK11_SetSymKeyUserData
+ * sets generic user data on keys (usually a pointer to a data structure)
+ * that can later be retrieved by PK11_GetSymKeyUserData().
+ * symKey - key where data will be set.
+ * data - data to be set.
+ * freefunc - function used to free the data.
+ * Setting user data on symKeys with existing user data already set will cause
+ * the existing user data to be freed before the new user data is set.
+ * Freeing user data is done by calling the user specified freefunc.
+ * If freefunc is NULL, the user data is assumed to be global or static an
+ * not freed. Passing NULL for user data to PK11_SetSymKeyUserData has the
+ * effect of freeing any existing user data, and clearing the user data
+ * pointer. If user data exists when the symKey is finally freed, that
+ * data will be freed with freefunc.
+ *
+ * Applications should only use this function on keys which the application
+ * has created directly, as there is only one user data value per key.
+ */
+void PK11_SetSymKeyUserData(PK11SymKey *symKey, void *data,
+ PK11FreeDataFunc freefunc);
+/* PK11_GetSymKeyUserData
+ * retrieves generic user data which was set on a key by
+ * PK11_SetSymKeyUserData.
+ * symKey - key with data to be fetched
+ *
+ * If no data exists, or the data has been cleared, PK11_GetSymKeyUserData
+ * will return NULL. Returned data is still owned and managed by the SymKey,
+ * the caller should not free the data.
+ *
+ */
+void *PK11_GetSymKeyUserData(PK11SymKey *symKey);
+
+SECStatus PK11_PubWrapSymKey(CK_MECHANISM_TYPE type, SECKEYPublicKey *pubKey,
+ PK11SymKey *symKey, SECItem *wrappedKey);
+SECStatus PK11_PubWrapSymKeyWithMechanism(SECKEYPublicKey *pubKey,
+ CK_MECHANISM_TYPE mechType,
+ SECItem *param,
+ PK11SymKey *symKey,
+ SECItem *wrappedKey);
+SECStatus PK11_WrapSymKey(CK_MECHANISM_TYPE type, SECItem *params,
+ PK11SymKey *wrappingKey, PK11SymKey *symKey, SECItem *wrappedKey);
+/* move a key to 'slot' optionally set the key attributes according to either
+ * operation or the flags and making the key permanent at the same time.
+ * If the key is moved to the same slot, operation and flags values are
+ * currently ignored */
+PK11SymKey *PK11_MoveSymKey(PK11SlotInfo *slot, CK_ATTRIBUTE_TYPE operation,
+ CK_FLAGS flags, PRBool perm, PK11SymKey *symKey);
+/*
+ * To do joint operations, we often need two keys in the same slot.
+ * Usually the PKCS #11 wrappers handle this correctly (like for PK11_WrapKey),
+ * but sometimes the wrappers don't know about mechanism specific keys in
+ * the Mechanism params. This function makes sure the two keys are in the
+ * same slot by copying one or both of the keys into a common slot. This
+ * functions makes sure the slot can handle the target mechanism. If the copy
+ * is warranted, this function will prefer to move the movingKey first, then
+ * the preferedKey. If the keys are moved, the new keys are returned in
+ * newMovingKey and/or newPreferedKey. The application is responsible
+ * for freeing those keys one the operation is complete.
+ */
+SECStatus PK11_SymKeysToSameSlot(CK_MECHANISM_TYPE mech,
+ CK_ATTRIBUTE_TYPE preferedOperation,
+ CK_ATTRIBUTE_TYPE movingOperation,
+ PK11SymKey *preferedKey, PK11SymKey *movingKey,
+ PK11SymKey **newPreferedKey,
+ PK11SymKey **newMovingKey);
+
+/*
+ * derive a new key from the base key.
+ * PK11_Derive returns a key which can do exactly one operation, and is
+ * ephemeral (session key).
+ * PK11_DeriveWithFlags is the same as PK11_Derive, except you can use
+ * CKF_ flags to enable more than one operation.
+ * PK11_DeriveWithFlagsPerm is the same as PK11_DeriveWithFlags except you can
+ * (optionally) make the key permanent (token key).
+ */
+PK11SymKey *PK11_Derive(PK11SymKey *baseKey, CK_MECHANISM_TYPE mechanism,
+ SECItem *param, CK_MECHANISM_TYPE target,
+ CK_ATTRIBUTE_TYPE operation, int keySize);
+PK11SymKey *PK11_DeriveWithFlags(PK11SymKey *baseKey,
+ CK_MECHANISM_TYPE derive, SECItem *param, CK_MECHANISM_TYPE target,
+ CK_ATTRIBUTE_TYPE operation, int keySize, CK_FLAGS flags);
+PK11SymKey *PK11_DeriveWithFlagsPerm(PK11SymKey *baseKey,
+ CK_MECHANISM_TYPE derive,
+ SECItem *param, CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation,
+ int keySize, CK_FLAGS flags, PRBool isPerm);
+PK11SymKey *
+PK11_DeriveWithTemplate(PK11SymKey *baseKey, CK_MECHANISM_TYPE derive,
+ SECItem *param, CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation,
+ int keySize, CK_ATTRIBUTE *userAttr, unsigned int numAttrs,
+ PRBool isPerm);
+
+PK11SymKey *PK11_PubDerive(SECKEYPrivateKey *privKey,
+ SECKEYPublicKey *pubKey, PRBool isSender, SECItem *randomA, SECItem *randomB,
+ CK_MECHANISM_TYPE derive, CK_MECHANISM_TYPE target,
+ CK_ATTRIBUTE_TYPE operation, int keySize, void *wincx);
+PK11SymKey *PK11_PubDeriveWithKDF(SECKEYPrivateKey *privKey,
+ SECKEYPublicKey *pubKey, PRBool isSender, SECItem *randomA, SECItem *randomB,
+ CK_MECHANISM_TYPE derive, CK_MECHANISM_TYPE target,
+ CK_ATTRIBUTE_TYPE operation, int keySize,
+ CK_ULONG kdf, SECItem *sharedData, void *wincx);
+
+/*
+ * unwrap a new key with a symetric key.
+ * PK11_Unwrap returns a key which can do exactly one operation, and is
+ * ephemeral (session key).
+ * PK11_UnwrapWithFlags is the same as PK11_Unwrap, except you can use
+ * CKF_ flags to enable more than one operation.
+ * PK11_UnwrapWithFlagsPerm is the same as PK11_UnwrapWithFlags except you can
+ * (optionally) make the key permanent (token key).
+ */
+PK11SymKey *PK11_UnwrapSymKey(PK11SymKey *key,
+ CK_MECHANISM_TYPE wraptype, SECItem *param, SECItem *wrapppedKey,
+ CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation, int keySize);
+PK11SymKey *PK11_UnwrapSymKeyWithFlags(PK11SymKey *wrappingKey,
+ CK_MECHANISM_TYPE wrapType, SECItem *param, SECItem *wrappedKey,
+ CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation, int keySize,
+ CK_FLAGS flags);
+PK11SymKey *PK11_UnwrapSymKeyWithFlagsPerm(PK11SymKey *wrappingKey,
+ CK_MECHANISM_TYPE wrapType,
+ SECItem *param, SECItem *wrappedKey,
+ CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation,
+ int keySize, CK_FLAGS flags, PRBool isPerm);
+
+/*
+ * unwrap a new key with a private key.
+ * PK11_PubUnwrap returns a key which can do exactly one operation, and is
+ * ephemeral (session key).
+ * PK11_PubUnwrapWithFlagsPerm is the same as PK11_PubUnwrap except you can
+ * use * CKF_ flags to enable more than one operation, and optionally make
+ * the key permanent (token key).
+ */
+PK11SymKey *PK11_PubUnwrapSymKey(SECKEYPrivateKey *key, SECItem *wrapppedKey,
+ CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation, int keySize);
+PK11SymKey *PK11_PubUnwrapSymKeyWithMechanism(SECKEYPrivateKey *key,
+ CK_MECHANISM_TYPE mechType,
+ SECItem *param,
+ SECItem *wrapppedKey,
+ CK_MECHANISM_TYPE target,
+ CK_ATTRIBUTE_TYPE operation,
+ int keySize);
+PK11SymKey *PK11_PubUnwrapSymKeyWithFlagsPerm(SECKEYPrivateKey *wrappingKey,
+ SECItem *wrappedKey, CK_MECHANISM_TYPE target,
+ CK_ATTRIBUTE_TYPE operation, int keySize,
+ CK_FLAGS flags, PRBool isPerm);
+PK11SymKey *PK11_FindFixedKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
+ SECItem *keyID, void *wincx);
+SECStatus PK11_DeleteTokenPrivateKey(SECKEYPrivateKey *privKey, PRBool force);
+SECStatus PK11_DeleteTokenPublicKey(SECKEYPublicKey *pubKey);
+SECStatus PK11_DeleteTokenSymKey(PK11SymKey *symKey);
+SECStatus PK11_DeleteTokenCertAndKey(CERTCertificate *cert, void *wincx);
+SECKEYPrivateKey *PK11_LoadPrivKey(PK11SlotInfo *slot,
+ SECKEYPrivateKey *privKey, SECKEYPublicKey *pubKey,
+ PRBool token, PRBool sensitive);
+char *PK11_GetSymKeyNickname(PK11SymKey *symKey);
+char *PK11_GetPrivateKeyNickname(SECKEYPrivateKey *privKey);
+char *PK11_GetPublicKeyNickname(SECKEYPublicKey *pubKey);
+SECStatus PK11_SetSymKeyNickname(PK11SymKey *symKey, const char *nickname);
+SECStatus PK11_SetPrivateKeyNickname(SECKEYPrivateKey *privKey,
+ const char *nickname);
+SECStatus PK11_SetPublicKeyNickname(SECKEYPublicKey *pubKey,
+ const char *nickname);
+
+/*
+ * Using __PK11_SetCertificateNickname is *DANGEROUS*.
+ *
+ * The API will update the NSS database, but it *will NOT* update the in-memory data.
+ * As a result, after calling this API, there will be INCONSISTENCY between
+ * in-memory data and the database.
+ *
+ * Use of the API should be limited to short-lived tools, which will exit immediately
+ * after using this API.
+ *
+ * If you ignore this warning, your process is TAINTED and will most likely misbehave.
+ */
+SECStatus __PK11_SetCertificateNickname(CERTCertificate *cert,
+ const char *nickname);
+
+/* size to hold key in bytes */
+unsigned int PK11_GetKeyLength(PK11SymKey *key);
+/* size of actual secret parts of key in bits */
+/* algid is because RC4 strength is determined by the effective bits as well
+ * as the key bits */
+unsigned int PK11_GetKeyStrength(PK11SymKey *key, SECAlgorithmID *algid);
+SECStatus PK11_ExtractKeyValue(PK11SymKey *symKey);
+SECItem *PK11_GetKeyData(PK11SymKey *symKey);
+PK11SlotInfo *PK11_GetSlotFromKey(PK11SymKey *symKey);
+void *PK11_GetWindow(PK11SymKey *symKey);
+
+/*
+ * Explicitly set the key usage for the generated private key.
+ *
+ * This allows us to specify single use EC and RSA keys whose usage
+ * can be regulated by the underlying token.
+ *
+ * The underlying key usage is set using opFlags. opFlagsMask specifies
+ * which operations are specified by opFlags. For instance to turn encrypt
+ * on and signing off, opFlags would be CKF_ENCRYPT|CKF_DECRYPT and
+ * opFlagsMask would be CKF_ENCRYPT|CKF_DECRYPT|CKF_SIGN|CKF_VERIFY. You
+ * need to specify both the public and private key flags,
+ * PK11_GenerateKeyPairWithOpFlags will sort out the correct flag to the
+ * correct key type. Flags not specified in opFlagMask will be defaulted
+ * according to mechanism type and token capabilities.
+ */
+SECKEYPrivateKey *PK11_GenerateKeyPairWithOpFlags(PK11SlotInfo *slot,
+ CK_MECHANISM_TYPE type, void *param, SECKEYPublicKey **pubk,
+ PK11AttrFlags attrFlags, CK_FLAGS opFlags, CK_FLAGS opFlagsMask,
+ void *wincx);
+/*
+ * The attrFlags is the logical OR of the PK11_ATTR_XXX bitflags.
+ * These flags apply to the private key. The PK11_ATTR_TOKEN,
+ * PK11_ATTR_SESSION, PK11_ATTR_MODIFIABLE, and PK11_ATTR_UNMODIFIABLE
+ * flags also apply to the public key.
+ */
+SECKEYPrivateKey *PK11_GenerateKeyPairWithFlags(PK11SlotInfo *slot,
+ CK_MECHANISM_TYPE type, void *param, SECKEYPublicKey **pubk,
+ PK11AttrFlags attrFlags, void *wincx);
+SECKEYPrivateKey *PK11_GenerateKeyPair(PK11SlotInfo *slot,
+ CK_MECHANISM_TYPE type, void *param, SECKEYPublicKey **pubk,
+ PRBool isPerm, PRBool isSensitive, void *wincx);
+SECKEYPrivateKey *PK11_FindPrivateKeyFromCert(PK11SlotInfo *slot,
+ CERTCertificate *cert, void *wincx);
+SECKEYPrivateKey *PK11_FindKeyByAnyCert(CERTCertificate *cert, void *wincx);
+SECKEYPrivateKey *PK11_FindKeyByKeyID(PK11SlotInfo *slot, SECItem *keyID,
+ void *wincx);
+int PK11_GetPrivateModulusLen(SECKEYPrivateKey *key);
+
+SECStatus PK11_Decrypt(PK11SymKey *symkey,
+ CK_MECHANISM_TYPE mechanism, SECItem *param,
+ unsigned char *out, unsigned int *outLen,
+ unsigned int maxLen,
+ const unsigned char *enc, unsigned int encLen);
+SECStatus PK11_Encrypt(PK11SymKey *symKey,
+ CK_MECHANISM_TYPE mechanism, SECItem *param,
+ unsigned char *out, unsigned int *outLen,
+ unsigned int maxLen,
+ const unsigned char *data, unsigned int dataLen);
+
+/* note: despite the name, this function takes a private key. */
+SECStatus PK11_PubDecryptRaw(SECKEYPrivateKey *key,
+ unsigned char *data, unsigned *outLen,
+ unsigned int maxLen,
+ const unsigned char *enc, unsigned encLen);
+#define PK11_PrivDecryptRaw PK11_PubDecryptRaw
+/* The encrypt function that complements the above decrypt function. */
+SECStatus PK11_PubEncryptRaw(SECKEYPublicKey *key,
+ unsigned char *enc,
+ const unsigned char *data, unsigned dataLen,
+ void *wincx);
+
+SECStatus PK11_PrivDecryptPKCS1(SECKEYPrivateKey *key,
+ unsigned char *data, unsigned *outLen,
+ unsigned int maxLen,
+ const unsigned char *enc, unsigned encLen);
+/* The encrypt function that complements the above decrypt function. */
+SECStatus PK11_PubEncryptPKCS1(SECKEYPublicKey *key,
+ unsigned char *enc,
+ const unsigned char *data, unsigned dataLen,
+ void *wincx);
+
+SECStatus PK11_PrivDecrypt(SECKEYPrivateKey *key,
+ CK_MECHANISM_TYPE mechanism, SECItem *param,
+ unsigned char *out, unsigned int *outLen,
+ unsigned int maxLen,
+ const unsigned char *enc, unsigned int encLen);
+SECStatus PK11_PubEncrypt(SECKEYPublicKey *key,
+ CK_MECHANISM_TYPE mechanism, SECItem *param,
+ unsigned char *out, unsigned int *outLen,
+ unsigned int maxLen,
+ const unsigned char *data, unsigned int dataLen,
+ void *wincx);
+
+SECStatus PK11_ImportPrivateKeyInfo(PK11SlotInfo *slot,
+ SECKEYPrivateKeyInfo *pki, SECItem *nickname,
+ SECItem *publicValue, PRBool isPerm, PRBool isPrivate,
+ unsigned int usage, void *wincx);
+SECStatus PK11_ImportPrivateKeyInfoAndReturnKey(PK11SlotInfo *slot,
+ SECKEYPrivateKeyInfo *pki, SECItem *nickname,
+ SECItem *publicValue, PRBool isPerm, PRBool isPrivate,
+ unsigned int usage, SECKEYPrivateKey **privk, void *wincx);
+SECStatus PK11_ImportDERPrivateKeyInfo(PK11SlotInfo *slot,
+ SECItem *derPKI, SECItem *nickname,
+ SECItem *publicValue, PRBool isPerm, PRBool isPrivate,
+ unsigned int usage, void *wincx);
+SECStatus PK11_ImportDERPrivateKeyInfoAndReturnKey(PK11SlotInfo *slot,
+ SECItem *derPKI, SECItem *nickname,
+ SECItem *publicValue, PRBool isPerm, PRBool isPrivate,
+ unsigned int usage, SECKEYPrivateKey **privk, void *wincx);
+SECStatus PK11_ImportEncryptedPrivateKeyInfo(PK11SlotInfo *slot,
+ SECKEYEncryptedPrivateKeyInfo *epki, SECItem *pwitem,
+ SECItem *nickname, SECItem *publicValue, PRBool isPerm,
+ PRBool isPrivate, KeyType type,
+ unsigned int usage, void *wincx);
+SECStatus PK11_ImportEncryptedPrivateKeyInfoAndReturnKey(PK11SlotInfo *slot,
+ SECKEYEncryptedPrivateKeyInfo *epki, SECItem *pwitem,
+ SECItem *nickname, SECItem *publicValue, PRBool isPerm,
+ PRBool isPrivate, KeyType type,
+ unsigned int usage, SECKEYPrivateKey **privk, void *wincx);
+SECItem *PK11_ExportDERPrivateKeyInfo(SECKEYPrivateKey *pk, void *wincx);
+SECKEYPrivateKeyInfo *PK11_ExportPrivKeyInfo(
+ SECKEYPrivateKey *pk, void *wincx);
+SECKEYPrivateKeyInfo *PK11_ExportPrivateKeyInfo(
+ CERTCertificate *cert, void *wincx);
+SECKEYEncryptedPrivateKeyInfo *PK11_ExportEncryptedPrivKeyInfo(
+ PK11SlotInfo *slot, SECOidTag algTag, SECItem *pwitem,
+ SECKEYPrivateKey *pk, int iteration, void *pwArg);
+SECKEYEncryptedPrivateKeyInfo *PK11_ExportEncryptedPrivateKeyInfo(
+ PK11SlotInfo *slot, SECOidTag algTag, SECItem *pwitem,
+ CERTCertificate *cert, int iteration, void *pwArg);
+/* V2 refers to PKCS #5 V2 here. If a PKCS #5 v1 or PKCS #12 pbe is passed
+ * for pbeTag, then encTag and hashTag are ignored. If pbe is an encryption
+ * algorithm, then PKCS #5 V2 is used with prfTag for the prf. If prfTag isn't
+ * supplied prf will be SEC_OID_HMAC_SHA1 */
+SECKEYEncryptedPrivateKeyInfo *PK11_ExportEncryptedPrivKeyInfoV2(
+ PK11SlotInfo *slot, SECOidTag pbeTag, SECOidTag encTag, SECOidTag prfTag,
+ SECItem *pwitem, SECKEYPrivateKey *pk, int iteration, void *pwArg);
+SECKEYEncryptedPrivateKeyInfo *PK11_ExportEncryptedPrivateKeyInfoV2(
+ PK11SlotInfo *slot, SECOidTag pbeTag, SECOidTag encTag, SECOidTag prfTag,
+ SECItem *pwitem, CERTCertificate *cert, int iteration, void *pwArg);
+SECKEYPrivateKey *PK11_FindKeyByDERCert(PK11SlotInfo *slot,
+ CERTCertificate *cert, void *wincx);
+SECKEYPublicKey *PK11_MakeKEAPubKey(unsigned char *data, int length);
+SECStatus PK11_DigestKey(PK11Context *context, PK11SymKey *key);
+PRBool PK11_VerifyKeyOK(PK11SymKey *key);
+SECKEYPrivateKey *PK11_UnwrapPrivKey(PK11SlotInfo *slot,
+ PK11SymKey *wrappingKey, CK_MECHANISM_TYPE wrapType,
+ SECItem *param, SECItem *wrappedKey, SECItem *label,
+ SECItem *publicValue, PRBool token, PRBool sensitive,
+ CK_KEY_TYPE keyType, CK_ATTRIBUTE_TYPE *usage, int usageCount,
+ void *wincx);
+SECStatus PK11_WrapPrivKey(PK11SlotInfo *slot, PK11SymKey *wrappingKey,
+ SECKEYPrivateKey *privKey, CK_MECHANISM_TYPE wrapType,
+ SECItem *param, SECItem *wrappedKey, void *wincx);
+/*
+ * The caller of PK11_DEREncodePublicKey should free the returned SECItem with
+ * a SECITEM_FreeItem(..., PR_TRUE) call.
+ */
+SECItem *PK11_DEREncodePublicKey(const SECKEYPublicKey *pubk);
+PK11SymKey *PK11_CopySymKeyForSigning(PK11SymKey *originalKey,
+ CK_MECHANISM_TYPE mech);
+SECKEYPrivateKeyList *PK11_ListPrivKeysInSlot(PK11SlotInfo *slot,
+ char *nickname, void *wincx);
+SECKEYPublicKeyList *PK11_ListPublicKeysInSlot(PK11SlotInfo *slot,
+ char *nickname);
+SECKEYPQGParams *PK11_GetPQGParamsFromPrivateKey(SECKEYPrivateKey *privKey);
+/* deprecated */
+SECKEYPrivateKeyList *PK11_ListPrivateKeysInSlot(PK11SlotInfo *slot);
+
+PK11SymKey *PK11_ConvertSessionSymKeyToTokenSymKey(PK11SymKey *symk,
+ void *wincx);
+SECKEYPrivateKey *PK11_ConvertSessionPrivKeyToTokenPrivKey(
+ SECKEYPrivateKey *privk, void *wincx);
+SECKEYPrivateKey *PK11_CopyTokenPrivKeyToSessionPrivKey(PK11SlotInfo *destSlot,
+ SECKEYPrivateKey *privKey);
+
+/**********************************************************************
+ * Certs
+ **********************************************************************/
+SECItem *PK11_MakeIDFromPubKey(SECItem *pubKeyData);
+SECStatus PK11_TraverseSlotCerts(
+ SECStatus (*callback)(CERTCertificate *, SECItem *, void *),
+ void *arg, void *wincx);
+CERTCertificate *PK11_FindCertFromNickname(const char *nickname, void *wincx);
+CERTCertificate *PK11_FindCertFromURI(const char *uri, void *wincx);
+CERTCertList *PK11_FindCertsFromURI(const char *uri, void *wincx);
+CERTCertList *PK11_FindCertsFromEmailAddress(const char *email, void *wincx);
+CERTCertList *PK11_FindCertsFromNickname(const char *nickname, void *wincx);
+CERTCertificate *PK11_GetCertFromPrivateKey(SECKEYPrivateKey *privKey);
+SECStatus PK11_ImportCert(PK11SlotInfo *slot, CERTCertificate *cert,
+ CK_OBJECT_HANDLE key, const char *nickname,
+ PRBool includeTrust);
+SECStatus PK11_ImportDERCert(PK11SlotInfo *slot, SECItem *derCert,
+ CK_OBJECT_HANDLE key, char *nickname, PRBool includeTrust);
+PK11SlotInfo *PK11_ImportCertForKey(CERTCertificate *cert,
+ const char *nickname, void *wincx);
+PK11SlotInfo *PK11_ImportDERCertForKey(SECItem *derCert, char *nickname,
+ void *wincx);
+PK11SlotInfo *PK11_KeyForCertExists(CERTCertificate *cert,
+ CK_OBJECT_HANDLE *keyPtr, void *wincx);
+PK11SlotInfo *PK11_KeyForDERCertExists(SECItem *derCert,
+ CK_OBJECT_HANDLE *keyPtr, void *wincx);
+CERTCertificate *PK11_FindCertByIssuerAndSN(PK11SlotInfo **slot,
+ CERTIssuerAndSN *sn, void *wincx);
+CERTCertificate *PK11_FindCertAndKeyByRecipientList(PK11SlotInfo **slot,
+ SEC_PKCS7RecipientInfo **array, SEC_PKCS7RecipientInfo **rip,
+ SECKEYPrivateKey **privKey, void *wincx);
+int PK11_FindCertAndKeyByRecipientListNew(NSSCMSRecipient **recipientlist,
+ void *wincx);
+SECStatus PK11_TraverseCertsForSubjectInSlot(CERTCertificate *cert,
+ PK11SlotInfo *slot, SECStatus (*callback)(CERTCertificate *, void *),
+ void *arg);
+CERTCertificate *PK11_FindCertFromDERCert(PK11SlotInfo *slot,
+ CERTCertificate *cert, void *wincx);
+CERTCertificate *PK11_FindCertFromDERCertItem(PK11SlotInfo *slot,
+ const SECItem *derCert, void *wincx);
+SECStatus PK11_ImportCertForKeyToSlot(PK11SlotInfo *slot, CERTCertificate *cert,
+ char *nickname, PRBool addUsage,
+ void *wincx);
+CERTCertificate *PK11_FindBestKEAMatch(CERTCertificate *serverCert, void *wincx);
+PRBool PK11_FortezzaHasKEA(CERTCertificate *cert);
+CK_OBJECT_HANDLE PK11_FindEncodedCertInSlot(PK11SlotInfo *slot, SECItem *derCert, void *wincx);
+CK_OBJECT_HANDLE PK11_FindCertInSlot(PK11SlotInfo *slot, CERTCertificate *cert,
+ void *wincx);
+CK_OBJECT_HANDLE PK11_FindObjectForCert(CERTCertificate *cert,
+ void *wincx, PK11SlotInfo **pSlot);
+SECStatus PK11_TraverseCertsForNicknameInSlot(SECItem *nickname,
+ PK11SlotInfo *slot, SECStatus (*callback)(CERTCertificate *, void *),
+ void *arg);
+CERTCertList *PK11_ListCerts(PK11CertListType type, void *pwarg);
+CERTCertList *PK11_ListCertsInSlot(PK11SlotInfo *slot);
+CERTSignedCrl *PK11_ImportCRL(PK11SlotInfo *slot, SECItem *derCRL, char *url,
+ int type, void *wincx, PRInt32 importOptions, PLArenaPool *arena, PRInt32 decodeOptions);
+CK_BBOOL PK11_HasAttributeSet(PK11SlotInfo *slot,
+ CK_OBJECT_HANDLE id,
+ CK_ATTRIBUTE_TYPE type,
+ PRBool haslock /* must be set to PR_FALSE */);
+
+/**********************************************************************
+ * Hybrid Public Key Encryption
+ **********************************************************************/
+
+/* Some of the various HPKE arguments would ideally be const, but the
+ * underlying PK11 functions take them as non-const. To avoid lying to
+ * the application with a cast, this idiosyncrasy is exposed. */
+SECStatus PK11_HPKE_ValidateParameters(HpkeKemId kemId, HpkeKdfId kdfId, HpkeAeadId aeadId);
+HpkeContext *PK11_HPKE_NewContext(HpkeKemId kemId, HpkeKdfId kdfId, HpkeAeadId aeadId,
+ PK11SymKey *psk, const SECItem *pskId);
+SECStatus PK11_HPKE_Deserialize(const HpkeContext *cx, const PRUint8 *enc,
+ unsigned int encLen, SECKEYPublicKey **outPubKey);
+void PK11_HPKE_DestroyContext(HpkeContext *cx, PRBool freeit);
+
+/* Serialize an initialized receiver context. This only retains the keys and
+ * associated information necessary to resume Export and Open operations after
+ * import. Serialization is currently supported for receiver contexts only.
+ * This is done for two reasons: 1) it avoids having to move the encryption
+ * sequence number outside of the token (or adding encryption context
+ * serialization support to softoken), and 2) we don't have to worry about IV
+ * reuse due to sequence number cloning.
+ *
+ * |wrapKey| is required when exporting in FIPS mode. If exported with a
+ * wrapping key, that same key must be provided to the import function,
+ * otherwise behavior is undefined.
+ *
+ * Even when exported with key wrap, HPKE expects the nonce to also be kept
+ * secret and that value is not protected by wrapKey. Applications are
+ * responsible for maintaining the confidentiality of the exported information.
+ */
+SECStatus PK11_HPKE_ExportContext(const HpkeContext *cx, PK11SymKey *wrapKey, SECItem **serialized);
+SECStatus PK11_HPKE_ExportSecret(const HpkeContext *cx, const SECItem *info, unsigned int L,
+ PK11SymKey **outKey);
+const SECItem *PK11_HPKE_GetEncapPubKey(const HpkeContext *cx);
+HpkeContext *PK11_HPKE_ImportContext(const SECItem *serialized, PK11SymKey *wrapKey);
+SECStatus PK11_HPKE_Open(HpkeContext *cx, const SECItem *aad, const SECItem *ct, SECItem **outPt);
+SECStatus PK11_HPKE_Seal(HpkeContext *cx, const SECItem *aad, const SECItem *pt, SECItem **outCt);
+SECStatus PK11_HPKE_Serialize(const SECKEYPublicKey *pk, PRUint8 *buf, unsigned int *len, unsigned int maxLen);
+SECStatus PK11_HPKE_SetupS(HpkeContext *cx, const SECKEYPublicKey *pkE, SECKEYPrivateKey *skE,
+ SECKEYPublicKey *pkR, const SECItem *info);
+SECStatus PK11_HPKE_SetupR(HpkeContext *cx, const SECKEYPublicKey *pkR, SECKEYPrivateKey *skR,
+ const SECItem *enc, const SECItem *info);
+
+/**********************************************************************
+ * Sign/Verify
+ **********************************************************************/
+
+/*
+ * Return the length in bytes of a signature generated with the
+ * private key.
+ *
+ * Return 0 or -1 on failure. (XXX Should we fix it to always return
+ * -1 on failure?)
+ */
+int PK11_SignatureLen(SECKEYPrivateKey *key);
+PK11SlotInfo *PK11_GetSlotFromPrivateKey(SECKEYPrivateKey *key);
+SECStatus PK11_Sign(SECKEYPrivateKey *key, SECItem *sig,
+ const SECItem *hash);
+SECStatus PK11_SignWithMechanism(SECKEYPrivateKey *key,
+ CK_MECHANISM_TYPE mechanism,
+ const SECItem *param, SECItem *sig,
+ const SECItem *hash);
+SECStatus PK11_SignWithSymKey(PK11SymKey *symKey, CK_MECHANISM_TYPE mechanism,
+ SECItem *param, SECItem *sig, const SECItem *data);
+SECStatus PK11_VerifyRecover(SECKEYPublicKey *key, const SECItem *sig,
+ SECItem *dsig, void *wincx);
+SECStatus PK11_Verify(SECKEYPublicKey *key, const SECItem *sig,
+ const SECItem *hash, void *wincx);
+SECStatus PK11_VerifyWithMechanism(SECKEYPublicKey *key,
+ CK_MECHANISM_TYPE mechanism,
+ const SECItem *param, const SECItem *sig,
+ const SECItem *hash, void *wincx);
+
+/**********************************************************************
+ * Crypto Contexts
+ **********************************************************************/
+void PK11_DestroyContext(PK11Context *context, PRBool freeit);
+PK11Context *PK11_CreateContextBySymKey(CK_MECHANISM_TYPE type,
+ CK_ATTRIBUTE_TYPE operation,
+ PK11SymKey *symKey,
+ const SECItem *param);
+PK11Context *PK11_CreateContextByPubKey(CK_MECHANISM_TYPE type,
+ CK_ATTRIBUTE_TYPE operation,
+ SECKEYPublicKey *pubKey,
+ const SECItem *param, void *pwArg);
+PK11Context *PK11_CreateContextByPrivKey(CK_MECHANISM_TYPE type,
+ CK_ATTRIBUTE_TYPE operation,
+ SECKEYPrivateKey *privKey,
+ const SECItem *param);
+PK11Context *PK11_CreateDigestContext(SECOidTag hashAlg);
+PK11Context *PK11_CloneContext(PK11Context *old);
+SECStatus PK11_DigestBegin(PK11Context *cx);
+/*
+ * The output buffer 'out' must be big enough to hold the output of
+ * the hash algorithm 'hashAlg'.
+ */
+SECStatus PK11_HashBuf(SECOidTag hashAlg, unsigned char *out,
+ const unsigned char *in, PRInt32 len);
+SECStatus PK11_DigestOp(PK11Context *context, const unsigned char *in,
+ unsigned len);
+SECStatus PK11_CipherOp(PK11Context *context, unsigned char *out, int *outlen,
+ int maxout, const unsigned char *in, int inlen);
+/* application builds the mechanism specific params */
+SECStatus PK11_AEADRawOp(PK11Context *context, void *params, int paramslen,
+ const unsigned char *aad, int aadlen,
+ unsigned char *out, int *outlen,
+ int maxout, const unsigned char *in, int inlen);
+/* NSS builds the mechanism specific params */
+SECStatus PK11_AEADOp(PK11Context *context, CK_GENERATOR_FUNCTION ivGen,
+ int fixedbits, unsigned char *iv, int ivlen,
+ const unsigned char *aad, int aadlen,
+ unsigned char *out, int *outlen,
+ int maxout, unsigned char *tag, int taglen,
+ const unsigned char *in, int inlen);
+
+SECStatus PK11_Finalize(PK11Context *context);
+SECStatus PK11_DigestFinal(PK11Context *context, unsigned char *data,
+ unsigned int *outLen, unsigned int length);
+#define PK11_CipherFinal PK11_DigestFinal
+SECStatus PK11_SaveContext(PK11Context *cx, unsigned char *save,
+ int *len, int saveLength);
+
+/* Save the context's state, with possible allocation.
+ * The caller may supply an already allocated buffer in preAllocBuf,
+ * with length pabLen. If the buffer is large enough for the context's
+ * state, it will receive the state.
+ * If the buffer is not large enough (or NULL), then a new buffer will
+ * be allocated with PORT_Alloc.
+ * In either case, the state will be returned as a buffer, and the length
+ * of the state will be given in *stateLen.
+ */
+unsigned char *
+PK11_SaveContextAlloc(PK11Context *cx,
+ unsigned char *preAllocBuf, unsigned int pabLen,
+ unsigned int *stateLen);
+
+SECStatus PK11_RestoreContext(PK11Context *cx, unsigned char *save, int len);
+SECStatus PK11_GenerateFortezzaIV(PK11SymKey *symKey, unsigned char *iv, int len);
+void PK11_SetFortezzaHack(PK11SymKey *symKey);
+
+/**********************************************************************
+ * PBE functions
+ **********************************************************************/
+
+/* This function creates PBE parameters from the given inputs. The result
+ * can be used to create a password integrity key for PKCS#12, by sending
+ * the return value to PK11_KeyGen along with the appropriate mechanism.
+ */
+SECItem *
+PK11_CreatePBEParams(SECItem *salt, SECItem *pwd, unsigned int iterations);
+
+/* free params created above (can be called after keygen is done */
+void PK11_DestroyPBEParams(SECItem *params);
+
+SECAlgorithmID *
+PK11_CreatePBEAlgorithmID(SECOidTag algorithm, int iteration, SECItem *salt);
+
+/* use to create PKCS5 V2 algorithms with finder control than that provided
+ * by PK11_CreatePBEAlgorithmID. */
+SECAlgorithmID *
+PK11_CreatePBEV2AlgorithmID(SECOidTag pbeAlgTag, SECOidTag cipherAlgTag,
+ SECOidTag prfAlgTag, int keyLength, int iteration,
+ SECItem *salt);
+PK11SymKey *
+PK11_PBEKeyGen(PK11SlotInfo *slot, SECAlgorithmID *algid, SECItem *pwitem,
+ PRBool faulty3DES, void *wincx);
+
+/* warning: cannot work with PKCS 5 v2 use PK11_PBEKeyGen instead */
+PK11SymKey *
+PK11_RawPBEKeyGen(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, SECItem *params,
+ SECItem *pwitem, PRBool faulty3DES, void *wincx);
+SECItem *
+PK11_GetPBEIV(SECAlgorithmID *algid, SECItem *pwitem);
+/*
+ * Get the Mechanism and parameter of the base encryption or mac scheme from
+ * a PBE algorithm ID.
+ * Caller is responsible for freeing the return parameter (param).
+ */
+CK_MECHANISM_TYPE
+PK11_GetPBECryptoMechanism(SECAlgorithmID *algid,
+ SECItem **param, SECItem *pwd);
+
+/**********************************************************************
+ * Functions to manage secmod flags
+ **********************************************************************/
+const PK11DefaultArrayEntry *PK11_GetDefaultArray(int *size);
+SECStatus PK11_UpdateSlotAttribute(PK11SlotInfo *slot,
+ const PK11DefaultArrayEntry *entry,
+ PRBool add);
+
+/**********************************************************************
+ * Functions to look at PKCS #11 dependent data
+ **********************************************************************/
+PK11GenericObject *PK11_FindGenericObjects(PK11SlotInfo *slot,
+ CK_OBJECT_CLASS objClass);
+PK11GenericObject *PK11_GetNextGenericObject(PK11GenericObject *object);
+PK11GenericObject *PK11_GetPrevGenericObject(PK11GenericObject *object);
+SECStatus PK11_UnlinkGenericObject(PK11GenericObject *object);
+SECStatus PK11_LinkGenericObject(PK11GenericObject *list,
+ PK11GenericObject *object);
+SECStatus PK11_DestroyGenericObjects(PK11GenericObject *object);
+SECStatus PK11_DestroyGenericObject(PK11GenericObject *object);
+PK11GenericObject *PK11_CreateManagedGenericObject(PK11SlotInfo *slot,
+ const CK_ATTRIBUTE *pTemplate,
+ int count, PRBool token);
+/* deprecated */
+PK11GenericObject *PK11_CreateGenericObject(PK11SlotInfo *slot,
+ const CK_ATTRIBUTE *pTemplate,
+ int count, PRBool token);
+
+/*
+ * PK11_ReadRawAttribute and PK11_WriteRawAttribute are generic
+ * functions to read and modify the actual PKCS #11 attributes of
+ * the underlying pkcs #11 object.
+ *
+ * object is a pointer to an NSS object that represents the underlying
+ * PKCS #11 object. It's type must match the type of PK11ObjectType
+ * as follows:
+ *
+ * type object
+ * PK11_TypeGeneric PK11GenericObject *
+ * PK11_TypePrivKey SECKEYPrivateKey *
+ * PK11_TypePubKey SECKEYPublicKey *
+ * PK11_TypeSymKey PK11SymKey *
+ *
+ * All other types are considered invalid. If type does not match the object
+ * passed, unpredictable results will occur.
+ *
+ * PK11_ReadRawAttribute allocates the buffer for returning the attribute
+ * value. The caller of PK11_ReadRawAttribute should free the data buffer
+ * pointed to by item using a SECITEM_FreeItem(item, PR_FALSE) or
+ * PORT_Free(item->data) call.
+ */
+SECStatus PK11_ReadRawAttribute(PK11ObjectType type, void *object,
+ CK_ATTRIBUTE_TYPE attr, SECItem *item);
+SECStatus PK11_ReadRawAttributes(PLArenaPool *arena, PK11ObjectType type, void *object,
+ CK_ATTRIBUTE *pTemplate, unsigned int count);
+SECStatus PK11_WriteRawAttribute(PK11ObjectType type, void *object,
+ CK_ATTRIBUTE_TYPE attr, SECItem *item);
+/* get the PKCS #11 handle and slot for a generic object */
+CK_OBJECT_HANDLE PK11_GetObjectHandle(PK11ObjectType objType, void *objSpec,
+ PK11SlotInfo **slotp);
+
+/*
+ * PK11_GetAllSlotsForCert returns all the slots that a given certificate
+ * exists on, since it's possible for a cert to exist on more than one
+ * PKCS#11 token.
+ */
+PK11SlotList *
+PK11_GetAllSlotsForCert(CERTCertificate *cert, void *arg);
+
+/*
+ * Finds all certificates on the given slot with the given subject distinguished
+ * name and returns them as DER bytes. If no such certificates can be found,
+ * returns SECSuccess and sets *results to NULL. If a failure is encountered
+ * while fetching any of the matching certificates, SECFailure is returned and
+ * *results will be NULL.
+ */
+SECStatus
+PK11_FindRawCertsWithSubject(PK11SlotInfo *slot, SECItem *derSubject,
+ CERTCertificateList **results);
+
+/*
+ * Finds and returns all certificates with a public key that matches the given
+ * private key. May return an empty list if no certificates match. Returns NULL
+ * if a failure is encountered.
+ */
+CERTCertList *
+PK11_GetCertsMatchingPrivateKey(SECKEYPrivateKey *privKey);
+
+/**********************************************************************
+ * New functions which are already deprecated....
+ **********************************************************************/
+SECItem *
+PK11_GetLowLevelKeyIDForCert(PK11SlotInfo *slot,
+ CERTCertificate *cert, void *pwarg);
+SECItem *
+PK11_GetLowLevelKeyIDForPrivateKey(SECKEYPrivateKey *key);
+
+PRBool SECMOD_HasRootCerts(void);
+
+/**********************************************************************
+ * Other Utilities
+ **********************************************************************/
+/*
+ * Get the state of the system FIPS mode -
+ * NSS uses this to force FIPS mode if the system bit is on. This returns
+ * the system state independent of the database state and can be called
+ * before NSS initializes.
+ */
+int SECMOD_GetSystemFIPSEnabled(void);
+
+/* FIPS indicator functions. Some operations are physically allowed, but
+ * are against the NSS FIPS security policy. This is because sometimes NSS
+ * functions are used in non-security contexts. You can call these functions
+ * to determine if you are operating inside or outside the the current vendor's
+ * FIPS Security Policy for NSS. NOTE: if the current version of NSS is not
+ * actually FIPS certified, then these functions will always return PR_FALSE */
+
+/* This function tells if if the last single shot operation on the slot
+ * was inside or outside the FIPS security policy */
+PRBool PK11_SlotGetLastFIPSStatus(PK11SlotInfo *slot);
+/* This tells you if the current operation is within the FIPS security policy. If
+ * you have called finalize on the context, it tells you if the last operation
+ * was within the FIPS security policy */
+PRBool PK11_ContextGetFIPSStatus(PK11Context *context);
+/* This tells you if the requested object was created in accordance to the
+ * NSS FIPS security policy. */
+PRBool PK11_ObjectGetFIPSStatus(PK11ObjectType objType, void *objSpec);
+
+SEC_END_PROTOS
+
+#endif
diff --git a/security/nss/lib/pk11wrap/pk11sdr.c b/security/nss/lib/pk11wrap/pk11sdr.c
new file mode 100644
index 0000000000..eb67bfba82
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11sdr.c
@@ -0,0 +1,437 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "seccomon.h"
+#include "secoid.h"
+#include "secasn1.h"
+#include "pkcs11.h"
+#include "pk11func.h"
+#include "pk11sdr.h"
+
+/*
+ * Data structure and template for encoding the result of an SDR operation
+ * This is temporary. It should include the algorithm ID of the encryption mechanism
+ */
+struct SDRResult {
+ SECItem keyid;
+ SECAlgorithmID alg;
+ SECItem data;
+};
+typedef struct SDRResult SDRResult;
+
+SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
+
+static SEC_ASN1Template template[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SDRResult) },
+ { SEC_ASN1_OCTET_STRING, offsetof(SDRResult, keyid) },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(SDRResult, alg),
+ SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+ { SEC_ASN1_OCTET_STRING, offsetof(SDRResult, data) },
+ { 0 }
+};
+
+static unsigned char keyID[] = {
+ 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
+};
+
+static SECItem keyIDItem = {
+ 0,
+ keyID,
+ sizeof keyID
+};
+
+/* local utility function for padding an incoming data block
+ * to the mechanism block size.
+ */
+static SECStatus
+padBlock(SECItem *data, int blockSize, SECItem *result)
+{
+ SECStatus rv = SECSuccess;
+ int padLength;
+ unsigned int i;
+
+ result->data = 0;
+ result->len = 0;
+
+ /* This algorithm always adds to the block (to indicate the number
+ * of pad bytes). So allocate a block large enough.
+ */
+ padLength = blockSize - (data->len % blockSize);
+ result->len = data->len + padLength;
+ result->data = (unsigned char *)PORT_Alloc(result->len);
+
+ /* Copy the data */
+ PORT_Memcpy(result->data, data->data, data->len);
+
+ /* Add the pad values */
+ for (i = data->len; i < result->len; i++)
+ result->data[i] = (unsigned char)padLength;
+
+ return rv;
+}
+
+static SECStatus
+unpadBlock(SECItem *data, int blockSize, SECItem *result)
+{
+ SECStatus rv = SECSuccess;
+ int padLength;
+ unsigned int i;
+
+ result->data = 0;
+ result->len = 0;
+
+ /* Remove the padding from the end if the input data */
+ if (data->len == 0 || data->len % blockSize != 0) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ padLength = data->data[data->len - 1];
+ if (padLength > blockSize) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ /* verify padding */
+ for (i = data->len - padLength; i < data->len; i++) {
+ if (data->data[i] != padLength) {
+ rv = SECFailure;
+ goto loser;
+ }
+ }
+
+ result->len = data->len - padLength;
+ result->data = (unsigned char *)PORT_Alloc(result->len);
+ if (!result->data) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ PORT_Memcpy(result->data, data->data, result->len);
+
+ if (padLength < 2) {
+ return SECWouldBlock;
+ }
+
+loser:
+ return rv;
+}
+
+static PRLock *pk11sdrLock = NULL;
+
+void
+pk11sdr_Init(void)
+{
+ pk11sdrLock = PR_NewLock();
+}
+
+void
+pk11sdr_Shutdown(void)
+{
+ if (pk11sdrLock) {
+ PR_DestroyLock(pk11sdrLock);
+ pk11sdrLock = NULL;
+ }
+}
+
+/*
+ * PK11SDR_Encrypt
+ * Encrypt a block of data using the symmetric key identified. The result
+ * is an ASN.1 (DER) encoded block of keyid, params and data.
+ */
+SECStatus
+PK11SDR_Encrypt(SECItem *keyid, SECItem *data, SECItem *result, void *cx)
+{
+ SECStatus rv = SECSuccess;
+ PK11SlotInfo *slot = 0;
+ PK11SymKey *key = 0;
+ SECItem *params = 0;
+ PK11Context *ctx = 0;
+ CK_MECHANISM_TYPE type;
+ SDRResult sdrResult;
+ SECItem paddedData;
+ SECItem *pKeyID;
+ PLArenaPool *arena = 0;
+
+ /* Initialize */
+ paddedData.len = 0;
+ paddedData.data = 0;
+
+ arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if (!arena) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ /* 1. Locate the requested keyid, or the default key (which has a keyid)
+ * 2. Create an encryption context
+ * 3. Encrypt
+ * 4. Encode the results (using ASN.1)
+ */
+
+ slot = PK11_GetInternalKeySlot();
+ if (!slot) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ /* Use triple-DES */
+ type = CKM_DES3_CBC;
+
+ /*
+ * Login to the internal token before we look for the key, otherwise we
+ * won't find it.
+ */
+ rv = PK11_Authenticate(slot, PR_TRUE, cx);
+ if (rv != SECSuccess)
+ goto loser;
+
+ /* Find the key to use */
+ pKeyID = keyid;
+ if (pKeyID->len == 0) {
+ pKeyID = &keyIDItem; /* Use default value */
+
+ /* put in a course lock to prevent a race between not finding the
+ * key and creating one.
+ */
+
+ if (pk11sdrLock)
+ PR_Lock(pk11sdrLock);
+
+ /* Try to find the key */
+ key = PK11_FindFixedKey(slot, type, pKeyID, cx);
+
+ /* If the default key doesn't exist yet, try to create it */
+ if (!key)
+ key = PK11_GenDES3TokenKey(slot, pKeyID, cx);
+ if (pk11sdrLock)
+ PR_Unlock(pk11sdrLock);
+ } else {
+ key = PK11_FindFixedKey(slot, type, pKeyID, cx);
+ }
+
+ if (!key) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ params = PK11_GenerateNewParam(type, key);
+ if (!params) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ ctx = PK11_CreateContextBySymKey(type, CKA_ENCRYPT, key, params);
+ if (!ctx) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ rv = padBlock(data, PK11_GetBlockSize(type, 0), &paddedData);
+ if (rv != SECSuccess)
+ goto loser;
+
+ sdrResult.data.len = paddedData.len;
+ sdrResult.data.data = (unsigned char *)PORT_ArenaAlloc(arena, sdrResult.data.len);
+
+ rv = PK11_CipherOp(ctx, sdrResult.data.data, (int *)&sdrResult.data.len, sdrResult.data.len,
+ paddedData.data, paddedData.len);
+ if (rv != SECSuccess)
+ goto loser;
+
+ PK11_Finalize(ctx);
+
+ sdrResult.keyid = *pKeyID;
+
+ rv = PK11_ParamToAlgid(SEC_OID_DES_EDE3_CBC, params, arena, &sdrResult.alg);
+ if (rv != SECSuccess)
+ goto loser;
+
+ if (!SEC_ASN1EncodeItem(0, result, &sdrResult, template)) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+loser:
+ SECITEM_ZfreeItem(&paddedData, PR_FALSE);
+ if (arena)
+ PORT_FreeArena(arena, PR_TRUE);
+ if (ctx)
+ PK11_DestroyContext(ctx, PR_TRUE);
+ if (params)
+ SECITEM_ZfreeItem(params, PR_TRUE);
+ if (key)
+ PK11_FreeSymKey(key);
+ if (slot)
+ PK11_FreeSlot(slot);
+
+ return rv;
+}
+
+/* decrypt a block */
+static SECStatus
+pk11Decrypt(PK11SlotInfo *slot, PLArenaPool *arena,
+ CK_MECHANISM_TYPE type, PK11SymKey *key,
+ SECItem *params, SECItem *in, SECItem *result)
+{
+ PK11Context *ctx = 0;
+ SECItem paddedResult;
+ SECStatus rv;
+
+ paddedResult.len = 0;
+ paddedResult.data = 0;
+
+ ctx = PK11_CreateContextBySymKey(type, CKA_DECRYPT, key, params);
+ if (!ctx) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ paddedResult.len = in->len;
+ paddedResult.data = PORT_ArenaAlloc(arena, paddedResult.len);
+
+ rv = PK11_CipherOp(ctx, paddedResult.data,
+ (int *)&paddedResult.len, paddedResult.len,
+ in->data, in->len);
+ if (rv != SECSuccess)
+ goto loser;
+
+ PK11_Finalize(ctx);
+
+ /* Remove the padding */
+ rv = unpadBlock(&paddedResult, PK11_GetBlockSize(type, 0), result);
+ if (rv)
+ goto loser;
+
+loser:
+ if (ctx)
+ PK11_DestroyContext(ctx, PR_TRUE);
+ return rv;
+}
+
+/*
+ * PK11SDR_Decrypt
+ * Decrypt a block of data produced by PK11SDR_Encrypt. The key used is identified
+ * by the keyid field within the input.
+ */
+SECStatus
+PK11SDR_Decrypt(SECItem *data, SECItem *result, void *cx)
+{
+ SECStatus rv = SECSuccess;
+ PK11SlotInfo *slot = 0;
+ PK11SymKey *key = 0;
+ CK_MECHANISM_TYPE type;
+ SDRResult sdrResult;
+ SECItem *params = 0;
+ SECItem possibleResult = { 0, NULL, 0 };
+ PLArenaPool *arena = 0;
+
+ arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if (!arena) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ /* Decode the incoming data */
+ memset(&sdrResult, 0, sizeof sdrResult);
+ rv = SEC_QuickDERDecodeItem(arena, &sdrResult, template, data);
+ if (rv != SECSuccess)
+ goto loser; /* Invalid format */
+
+ /* Find the slot and key for the given keyid */
+ slot = PK11_GetInternalKeySlot();
+ if (!slot) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ rv = PK11_Authenticate(slot, PR_TRUE, cx);
+ if (rv != SECSuccess)
+ goto loser;
+
+ /* Get the parameter values from the data */
+ params = PK11_ParamFromAlgid(&sdrResult.alg);
+ if (!params) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ /* Use triple-DES (Should look up the algorithm) */
+ type = CKM_DES3_CBC;
+ key = PK11_FindFixedKey(slot, type, &sdrResult.keyid, cx);
+ if (!key) {
+ rv = SECFailure;
+ } else {
+ rv = pk11Decrypt(slot, arena, type, key, params,
+ &sdrResult.data, result);
+ }
+
+ /*
+ * if the pad value was too small (1 or 2), then it's statistically
+ * 'likely' that (1 in 256) that we may not have the correct key.
+ * Check the other keys for a better match. If we find none, use
+ * this result.
+ */
+ if (rv == SECWouldBlock) {
+ possibleResult = *result;
+ }
+
+ /*
+ * handle the case where your key indicies may have been broken
+ */
+ if (rv != SECSuccess) {
+ PK11SymKey *keyList = PK11_ListFixedKeysInSlot(slot, NULL, cx);
+ PK11SymKey *testKey = NULL;
+ PK11SymKey *nextKey = NULL;
+
+ for (testKey = keyList; testKey;
+ testKey = PK11_GetNextSymKey(testKey)) {
+ rv = pk11Decrypt(slot, arena, type, testKey, params,
+ &sdrResult.data, result);
+ if (rv == SECSuccess) {
+ break;
+ }
+ /* found a close match. If it's our first remember it */
+ if (rv == SECWouldBlock) {
+ if (possibleResult.data) {
+ /* this is unlikely but possible. If we hit this condition,
+ * we have no way of knowing which possibility to prefer.
+ * in this case we just match the key the application
+ * thought was the right one */
+ SECITEM_ZfreeItem(result, PR_FALSE);
+ } else {
+ possibleResult = *result;
+ }
+ }
+ }
+
+ /* free the list */
+ for (testKey = keyList; testKey; testKey = nextKey) {
+ nextKey = PK11_GetNextSymKey(testKey);
+ PK11_FreeSymKey(testKey);
+ }
+ }
+
+ /* we didn't find a better key, use the one with a small pad value */
+ if ((rv != SECSuccess) && (possibleResult.data)) {
+ *result = possibleResult;
+ possibleResult.data = NULL;
+ rv = SECSuccess;
+ }
+
+loser:
+ if (arena)
+ PORT_FreeArena(arena, PR_TRUE);
+ if (key)
+ PK11_FreeSymKey(key);
+ if (params)
+ SECITEM_ZfreeItem(params, PR_TRUE);
+ if (slot)
+ PK11_FreeSlot(slot);
+ if (possibleResult.data)
+ SECITEM_ZfreeItem(&possibleResult, PR_FALSE);
+
+ return rv;
+}
diff --git a/security/nss/lib/pk11wrap/pk11sdr.h b/security/nss/lib/pk11wrap/pk11sdr.h
new file mode 100644
index 0000000000..0ffa425340
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11sdr.h
@@ -0,0 +1,28 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _PK11SDR_H_
+#define _PK11SDR_H_
+
+#include "seccomon.h"
+
+SEC_BEGIN_PROTOS
+
+/*
+ * PK11SDR_Encrypt - encrypt data using the specified key id or SDR default
+ * result should be freed with SECItem_ZfreeItem
+ */
+SECStatus
+PK11SDR_Encrypt(SECItem *keyid, SECItem *data, SECItem *result, void *cx);
+
+/*
+ * PK11SDR_Decrypt - decrypt data previously encrypted with PK11SDR_Encrypt
+ * result should be freed with SECItem_ZfreeItem
+ */
+SECStatus
+PK11SDR_Decrypt(SECItem *data, SECItem *result, void *cx);
+
+SEC_END_PROTOS
+
+#endif
diff --git a/security/nss/lib/pk11wrap/pk11skey.c b/security/nss/lib/pk11wrap/pk11skey.c
new file mode 100644
index 0000000000..66b4ed6a11
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11skey.c
@@ -0,0 +1,3047 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/*
+ * This file implements the Symkey wrapper and the PKCS context
+ * Interfaces.
+ */
+
+#include <stddef.h>
+
+#include "seccomon.h"
+#include "secmod.h"
+#include "nssilock.h"
+#include "secmodi.h"
+#include "secmodti.h"
+#include "pkcs11.h"
+#include "pk11func.h"
+#include "secitem.h"
+#include "secoid.h"
+#include "secerr.h"
+#include "hasht.h"
+
+static ECPointEncoding pk11_ECGetPubkeyEncoding(const SECKEYPublicKey *pubKey);
+
+static void
+pk11_EnterKeyMonitor(PK11SymKey *symKey)
+{
+ if (!symKey->sessionOwner || !(symKey->slot->isThreadSafe))
+ PK11_EnterSlotMonitor(symKey->slot);
+}
+
+static void
+pk11_ExitKeyMonitor(PK11SymKey *symKey)
+{
+ if (!symKey->sessionOwner || !(symKey->slot->isThreadSafe))
+ PK11_ExitSlotMonitor(symKey->slot);
+}
+
+/*
+ * pk11_getKeyFromList returns a symKey that has a session (if needSession
+ * was specified), or explicitly does not have a session (if needSession
+ * was not specified).
+ */
+static PK11SymKey *
+pk11_getKeyFromList(PK11SlotInfo *slot, PRBool needSession)
+{
+ PK11SymKey *symKey = NULL;
+
+ PZ_Lock(slot->freeListLock);
+ /* own session list are symkeys with sessions that the symkey owns.
+ * 'most' symkeys will own their own session. */
+ if (needSession) {
+ if (slot->freeSymKeysWithSessionHead) {
+ symKey = slot->freeSymKeysWithSessionHead;
+ slot->freeSymKeysWithSessionHead = symKey->next;
+ slot->keyCount--;
+ }
+ }
+ /* if we don't need a symkey with its own session, or we couldn't find
+ * one on the owner list, get one from the non-owner free list. */
+ if (!symKey) {
+ if (slot->freeSymKeysHead) {
+ symKey = slot->freeSymKeysHead;
+ slot->freeSymKeysHead = symKey->next;
+ slot->keyCount--;
+ }
+ }
+ PZ_Unlock(slot->freeListLock);
+ if (symKey) {
+ symKey->next = NULL;
+ if (!needSession) {
+ return symKey;
+ }
+ /* if we are getting an owner key, make sure we have a valid session.
+ * session could be invalid if the token has been removed or because
+ * we got it from the non-owner free list */
+ if ((symKey->series != slot->series) ||
+ (symKey->session == CK_INVALID_HANDLE)) {
+ symKey->session = pk11_GetNewSession(slot, &symKey->sessionOwner);
+ }
+ PORT_Assert(symKey->session != CK_INVALID_HANDLE);
+ if (symKey->session != CK_INVALID_HANDLE)
+ return symKey;
+ PK11_FreeSymKey(symKey);
+ /* if we are here, we need a session, but couldn't get one, it's
+ * unlikely we pk11_GetNewSession will succeed if we call it a second
+ * time. */
+ return NULL;
+ }
+
+ symKey = PORT_New(PK11SymKey);
+ if (symKey == NULL) {
+ return NULL;
+ }
+
+ symKey->next = NULL;
+ if (needSession) {
+ symKey->session = pk11_GetNewSession(slot, &symKey->sessionOwner);
+ PORT_Assert(symKey->session != CK_INVALID_HANDLE);
+ if (symKey->session == CK_INVALID_HANDLE) {
+ PK11_FreeSymKey(symKey);
+ symKey = NULL;
+ }
+ } else {
+ symKey->session = CK_INVALID_HANDLE;
+ }
+ return symKey;
+}
+
+/* Caller MUST hold slot->freeListLock (or ref count == 0?) !! */
+void
+PK11_CleanKeyList(PK11SlotInfo *slot)
+{
+ PK11SymKey *symKey = NULL;
+
+ while (slot->freeSymKeysWithSessionHead) {
+ symKey = slot->freeSymKeysWithSessionHead;
+ slot->freeSymKeysWithSessionHead = symKey->next;
+ pk11_CloseSession(slot, symKey->session, symKey->sessionOwner);
+ PORT_Free(symKey);
+ }
+ while (slot->freeSymKeysHead) {
+ symKey = slot->freeSymKeysHead;
+ slot->freeSymKeysHead = symKey->next;
+ pk11_CloseSession(slot, symKey->session, symKey->sessionOwner);
+ PORT_Free(symKey);
+ }
+ return;
+}
+
+/*
+ * create a symetric key:
+ * Slot is the slot to create the key in.
+ * type is the mechanism type
+ * owner is does this symKey structure own it's object handle (rare
+ * that this is false).
+ * needSession means the returned symKey will return with a valid session
+ * allocated already.
+ */
+static PK11SymKey *
+pk11_CreateSymKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
+ PRBool owner, PRBool needSession, void *wincx)
+{
+
+ PK11SymKey *symKey = pk11_getKeyFromList(slot, needSession);
+
+ if (symKey == NULL) {
+ return NULL;
+ }
+ /* if needSession was specified, make sure we have a valid session.
+ * callers which specify needSession as false should do their own
+ * check of the session before returning the symKey */
+ if (needSession && symKey->session == CK_INVALID_HANDLE) {
+ PK11_FreeSymKey(symKey);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return NULL;
+ }
+
+ symKey->type = type;
+ symKey->data.type = siBuffer;
+ symKey->data.data = NULL;
+ symKey->data.len = 0;
+ symKey->owner = owner;
+ symKey->objectID = CK_INVALID_HANDLE;
+ symKey->slot = slot;
+ symKey->series = slot->series;
+ symKey->cx = wincx;
+ symKey->size = 0;
+ symKey->refCount = 1;
+ symKey->origin = PK11_OriginNULL;
+ symKey->parent = NULL;
+ symKey->freeFunc = NULL;
+ symKey->userData = NULL;
+ PK11_ReferenceSlot(slot);
+ return symKey;
+}
+
+/*
+ * destroy a symetric key
+ */
+void
+PK11_FreeSymKey(PK11SymKey *symKey)
+{
+ PK11SlotInfo *slot;
+ PRBool freeit = PR_TRUE;
+
+ if (!symKey) {
+ return;
+ }
+
+ if (PR_ATOMIC_DECREMENT(&symKey->refCount) == 0) {
+ PK11SymKey *parent = symKey->parent;
+
+ symKey->parent = NULL;
+ if ((symKey->owner) && symKey->objectID != CK_INVALID_HANDLE) {
+ pk11_EnterKeyMonitor(symKey);
+ (void)PK11_GETTAB(symKey->slot)->C_DestroyObject(symKey->session, symKey->objectID);
+ pk11_ExitKeyMonitor(symKey);
+ }
+ if (symKey->data.data) {
+ PORT_Memset(symKey->data.data, 0, symKey->data.len);
+ PORT_Free(symKey->data.data);
+ }
+ /* free any existing data */
+ if (symKey->userData && symKey->freeFunc) {
+ (*symKey->freeFunc)(symKey->userData);
+ }
+ slot = symKey->slot;
+ PZ_Lock(slot->freeListLock);
+ if (slot->keyCount < slot->maxKeyCount) {
+ /*
+ * freeSymkeysWithSessionHead contain a list of reusable
+ * SymKey structures with valid sessions.
+ * sessionOwner must be true.
+ * session must be valid.
+ * freeSymKeysHead contain a list of SymKey structures without
+ * valid session.
+ * session must be CK_INVALID_HANDLE.
+ * though sessionOwner is false, callers should not depend on
+ * this fact.
+ */
+ if (symKey->sessionOwner) {
+ PORT_Assert(symKey->session != CK_INVALID_HANDLE);
+ symKey->next = slot->freeSymKeysWithSessionHead;
+ slot->freeSymKeysWithSessionHead = symKey;
+ } else {
+ symKey->session = CK_INVALID_HANDLE;
+ symKey->next = slot->freeSymKeysHead;
+ slot->freeSymKeysHead = symKey;
+ }
+ slot->keyCount++;
+ symKey->slot = NULL;
+ freeit = PR_FALSE;
+ }
+ PZ_Unlock(slot->freeListLock);
+ if (freeit) {
+ pk11_CloseSession(symKey->slot, symKey->session,
+ symKey->sessionOwner);
+ PORT_Free(symKey);
+ }
+ PK11_FreeSlot(slot);
+
+ if (parent) {
+ PK11_FreeSymKey(parent);
+ }
+ }
+}
+
+PK11SymKey *
+PK11_ReferenceSymKey(PK11SymKey *symKey)
+{
+ PR_ATOMIC_INCREMENT(&symKey->refCount);
+ return symKey;
+}
+
+/*
+ * Accessors
+ */
+CK_MECHANISM_TYPE
+PK11_GetMechanism(PK11SymKey *symKey)
+{
+ return symKey->type;
+}
+
+/*
+ * return the slot associated with a symetric key
+ */
+PK11SlotInfo *
+PK11_GetSlotFromKey(PK11SymKey *symKey)
+{
+ return PK11_ReferenceSlot(symKey->slot);
+}
+
+CK_KEY_TYPE
+PK11_GetSymKeyType(PK11SymKey *symKey)
+{
+ return PK11_GetKeyType(symKey->type, symKey->size);
+}
+
+PK11SymKey *
+PK11_GetNextSymKey(PK11SymKey *symKey)
+{
+ return symKey ? symKey->next : NULL;
+}
+
+char *
+PK11_GetSymKeyNickname(PK11SymKey *symKey)
+{
+ return PK11_GetObjectNickname(symKey->slot, symKey->objectID);
+}
+
+SECStatus
+PK11_SetSymKeyNickname(PK11SymKey *symKey, const char *nickname)
+{
+ return PK11_SetObjectNickname(symKey->slot, symKey->objectID, nickname);
+}
+
+void *
+PK11_GetSymKeyUserData(PK11SymKey *symKey)
+{
+ return symKey->userData;
+}
+
+void
+PK11_SetSymKeyUserData(PK11SymKey *symKey, void *userData,
+ PK11FreeDataFunc freeFunc)
+{
+ /* free any existing data */
+ if (symKey->userData && symKey->freeFunc) {
+ (*symKey->freeFunc)(symKey->userData);
+ }
+ symKey->userData = userData;
+ symKey->freeFunc = freeFunc;
+ return;
+}
+
+/*
+ * turn key handle into an appropriate key object
+ */
+PK11SymKey *
+PK11_SymKeyFromHandle(PK11SlotInfo *slot, PK11SymKey *parent, PK11Origin origin,
+ CK_MECHANISM_TYPE type, CK_OBJECT_HANDLE keyID, PRBool owner, void *wincx)
+{
+ PK11SymKey *symKey;
+ PRBool needSession = !(owner && parent);
+
+ if (keyID == CK_INVALID_HANDLE) {
+ return NULL;
+ }
+
+ symKey = pk11_CreateSymKey(slot, type, owner, needSession, wincx);
+ if (symKey == NULL) {
+ return NULL;
+ }
+
+ symKey->objectID = keyID;
+ symKey->origin = origin;
+
+ /* adopt the parent's session */
+ /* This is only used by SSL. What we really want here is a session
+ * structure with a ref count so the session goes away only after all the
+ * keys do. */
+ if (!needSession) {
+ symKey->sessionOwner = PR_FALSE;
+ symKey->session = parent->session;
+ symKey->parent = PK11_ReferenceSymKey(parent);
+ /* This is the only case where pk11_CreateSymKey does not explicitly
+ * check symKey->session. We need to assert here to make sure.
+ * the session isn't invalid. */
+ PORT_Assert(parent->session != CK_INVALID_HANDLE);
+ if (parent->session == CK_INVALID_HANDLE) {
+ PK11_FreeSymKey(symKey);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return NULL;
+ }
+ }
+
+ return symKey;
+}
+
+/*
+ * Restore a symmetric wrapping key that was saved using PK11_SetWrapKey.
+ *
+ * This function is provided for ABI compatibility; see PK11_SetWrapKey below.
+ */
+PK11SymKey *
+PK11_GetWrapKey(PK11SlotInfo *slot, int wrap, CK_MECHANISM_TYPE type,
+ int series, void *wincx)
+{
+ PK11SymKey *symKey = NULL;
+ CK_OBJECT_HANDLE keyHandle;
+
+ PK11_EnterSlotMonitor(slot);
+ if (slot->series != series ||
+ slot->refKeys[wrap] == CK_INVALID_HANDLE) {
+ PK11_ExitSlotMonitor(slot);
+ return NULL;
+ }
+
+ if (type == CKM_INVALID_MECHANISM) {
+ type = slot->wrapMechanism;
+ }
+
+ keyHandle = slot->refKeys[wrap];
+ PK11_ExitSlotMonitor(slot);
+ symKey = PK11_SymKeyFromHandle(slot, NULL, PK11_OriginDerive,
+ slot->wrapMechanism, keyHandle, PR_FALSE, wincx);
+ return symKey;
+}
+
+/*
+ * This function sets an attribute on the current slot with a wrapping key. The
+ * data saved is ephemeral; it needs to be run every time the program is
+ * invoked.
+ *
+ * Since NSS 3.45, this function is marginally more thread safe. It uses the
+ * slot lock (if present) and fails silently if a value is already set. Use
+ * PK11_GetWrapKey() after calling this function to get the current wrapping key
+ * in case there was an update on another thread.
+ *
+ * Either way, using this function is inadvisable. It's provided for ABI
+ * compatibility only.
+ */
+void
+PK11_SetWrapKey(PK11SlotInfo *slot, int wrap, PK11SymKey *wrapKey)
+{
+ PK11_EnterSlotMonitor(slot);
+ if (wrap >= 0) {
+ size_t uwrap = (size_t)wrap;
+ if (uwrap < PR_ARRAY_SIZE(slot->refKeys) &&
+ slot->refKeys[uwrap] == CK_INVALID_HANDLE) {
+ /* save the handle and mechanism for the wrapping key */
+ /* mark the key and session as not owned by us so they don't get
+ * freed when the key goes way... that lets us reuse the key
+ * later */
+ slot->refKeys[uwrap] = wrapKey->objectID;
+ wrapKey->owner = PR_FALSE;
+ wrapKey->sessionOwner = PR_FALSE;
+ slot->wrapMechanism = wrapKey->type;
+ }
+ }
+ PK11_ExitSlotMonitor(slot);
+}
+
+/*
+ * figure out if a key is still valid or if it is stale.
+ */
+PRBool
+PK11_VerifyKeyOK(PK11SymKey *key)
+{
+ if (!PK11_IsPresent(key->slot)) {
+ return PR_FALSE;
+ }
+ return (PRBool)(key->series == key->slot->series);
+}
+
+static PK11SymKey *
+pk11_ImportSymKeyWithTempl(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
+ PK11Origin origin, PRBool isToken, CK_ATTRIBUTE *keyTemplate,
+ unsigned int templateCount, SECItem *key, void *wincx)
+{
+ PK11SymKey *symKey;
+ SECStatus rv;
+
+ symKey = pk11_CreateSymKey(slot, type, !isToken, PR_TRUE, wincx);
+ if (symKey == NULL) {
+ return NULL;
+ }
+
+ symKey->size = key->len;
+
+ PK11_SETATTRS(&keyTemplate[templateCount], CKA_VALUE, key->data, key->len);
+ templateCount++;
+
+ if (SECITEM_CopyItem(NULL, &symKey->data, key) != SECSuccess) {
+ PK11_FreeSymKey(symKey);
+ return NULL;
+ }
+
+ symKey->origin = origin;
+
+ /* import the keys */
+ rv = PK11_CreateNewObject(slot, symKey->session, keyTemplate,
+ templateCount, isToken, &symKey->objectID);
+ if (rv != SECSuccess) {
+ PK11_FreeSymKey(symKey);
+ return NULL;
+ }
+
+ return symKey;
+}
+
+/*
+ * turn key bits into an appropriate key object
+ */
+PK11SymKey *
+PK11_ImportSymKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
+ PK11Origin origin, CK_ATTRIBUTE_TYPE operation, SECItem *key, void *wincx)
+{
+ PK11SymKey *symKey;
+ unsigned int templateCount = 0;
+ CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY;
+ CK_KEY_TYPE keyType = CKK_GENERIC_SECRET;
+ CK_BBOOL cktrue = CK_TRUE; /* sigh */
+ CK_ATTRIBUTE keyTemplate[5];
+ CK_ATTRIBUTE *attrs = keyTemplate;
+
+ /* CKA_NSS_MESSAGE is a fake operation to distinguish between
+ * Normal Encrypt/Decrypt and MessageEncrypt/Decrypt. Don't try to set
+ * it as a real attribute */
+ if ((operation & CKA_NSS_MESSAGE_MASK) == CKA_NSS_MESSAGE) {
+ /* Message is or'd with a real Attribute (CKA_ENCRYPT, CKA_DECRYPT),
+ * etc. Strip out the real attribute here */
+ operation &= ~CKA_NSS_MESSAGE_MASK;
+ }
+
+ PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof(keyClass));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(keyType));
+ attrs++;
+ PK11_SETATTRS(attrs, operation, &cktrue, 1);
+ attrs++;
+ templateCount = attrs - keyTemplate;
+ PR_ASSERT(templateCount + 1 <= sizeof(keyTemplate) / sizeof(CK_ATTRIBUTE));
+
+ keyType = PK11_GetKeyType(type, key->len);
+ symKey = pk11_ImportSymKeyWithTempl(slot, type, origin, PR_FALSE,
+ keyTemplate, templateCount, key, wincx);
+ return symKey;
+}
+/* Import a PKCS #11 data object and return it as a key. This key is
+ * only useful in a limited number of mechanisms, such as HKDF. */
+PK11SymKey *
+PK11_ImportDataKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, PK11Origin origin,
+ CK_ATTRIBUTE_TYPE operation, SECItem *key, void *wincx)
+{
+ CK_OBJECT_CLASS ckoData = CKO_DATA;
+ CK_ATTRIBUTE template[2] = { { CKA_CLASS, (CK_BYTE_PTR)&ckoData, sizeof(ckoData) },
+ { CKA_VALUE, (CK_BYTE_PTR)key->data, key->len } };
+ CK_OBJECT_HANDLE handle;
+ PK11GenericObject *genObject;
+
+ genObject = PK11_CreateGenericObject(slot, template, PR_ARRAY_SIZE(template), PR_FALSE);
+ if (genObject == NULL) {
+ return NULL;
+ }
+ handle = PK11_GetObjectHandle(PK11_TypeGeneric, genObject, NULL);
+ if (handle == CK_INVALID_HANDLE) {
+ return NULL;
+ }
+ /* A note about ownership of the PKCS #11 handle:
+ * PK11_CreateGenericObject() will not destroy the object it creates
+ * on Free, For that you want PK11_CreateManagedGenericObject().
+ * Below we import the handle into the symKey structure. We pass
+ * PR_TRUE as the owner so that the symKey will destroy the object
+ * once it's freed. This is way it's safe to free now. */
+ PK11_DestroyGenericObject(genObject);
+ return PK11_SymKeyFromHandle(slot, NULL, origin, type, handle, PR_TRUE, wincx);
+}
+
+/* turn key bits into an appropriate key object */
+PK11SymKey *
+PK11_ImportSymKeyWithFlags(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
+ PK11Origin origin, CK_ATTRIBUTE_TYPE operation, SECItem *key,
+ CK_FLAGS flags, PRBool isPerm, void *wincx)
+{
+ PK11SymKey *symKey;
+ unsigned int templateCount = 0;
+ CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY;
+ CK_KEY_TYPE keyType = CKK_GENERIC_SECRET;
+ CK_BBOOL cktrue = CK_TRUE; /* sigh */
+ CK_ATTRIBUTE keyTemplate[MAX_TEMPL_ATTRS];
+ CK_ATTRIBUTE *attrs = keyTemplate;
+
+ /* CKA_NSS_MESSAGE is a fake operation to distinguish between
+ * Normal Encrypt/Decrypt and MessageEncrypt/Decrypt. Don't try to set
+ * it as a real attribute */
+ if ((operation & CKA_NSS_MESSAGE_MASK) == CKA_NSS_MESSAGE) {
+ /* Message is or'd with a real Attribute (CKA_ENCRYPT, CKA_DECRYPT),
+ * etc. Strip out the real attribute here */
+ operation &= ~CKA_NSS_MESSAGE_MASK;
+ }
+
+ PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof(keyClass));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(keyType));
+ attrs++;
+ if (isPerm) {
+ PK11_SETATTRS(attrs, CKA_TOKEN, &cktrue, sizeof(cktrue));
+ attrs++;
+ /* sigh some tokens think CKA_PRIVATE = false is a reasonable
+ * default for secret keys */
+ PK11_SETATTRS(attrs, CKA_PRIVATE, &cktrue, sizeof(cktrue));
+ attrs++;
+ }
+ attrs += pk11_OpFlagsToAttributes(flags, attrs, &cktrue);
+ if ((operation != CKA_FLAGS_ONLY) &&
+ !pk11_FindAttrInTemplate(keyTemplate, attrs - keyTemplate, operation)) {
+ PK11_SETATTRS(attrs, operation, &cktrue, sizeof(cktrue));
+ attrs++;
+ }
+ templateCount = attrs - keyTemplate;
+ PR_ASSERT(templateCount + 1 <= sizeof(keyTemplate) / sizeof(CK_ATTRIBUTE));
+
+ keyType = PK11_GetKeyType(type, key->len);
+ symKey = pk11_ImportSymKeyWithTempl(slot, type, origin, isPerm,
+ keyTemplate, templateCount, key, wincx);
+ if (symKey && isPerm) {
+ symKey->owner = PR_FALSE;
+ }
+ return symKey;
+}
+
+PK11SymKey *
+PK11_FindFixedKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, SECItem *keyID,
+ void *wincx)
+{
+ CK_ATTRIBUTE findTemp[4];
+ CK_ATTRIBUTE *attrs;
+ CK_BBOOL ckTrue = CK_TRUE;
+ CK_OBJECT_CLASS keyclass = CKO_SECRET_KEY;
+ size_t tsize = 0;
+ CK_OBJECT_HANDLE key_id;
+
+ attrs = findTemp;
+ PK11_SETATTRS(attrs, CKA_CLASS, &keyclass, sizeof(keyclass));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_TOKEN, &ckTrue, sizeof(ckTrue));
+ attrs++;
+ if (keyID) {
+ PK11_SETATTRS(attrs, CKA_ID, keyID->data, keyID->len);
+ attrs++;
+ }
+ tsize = attrs - findTemp;
+ PORT_Assert(tsize <= sizeof(findTemp) / sizeof(CK_ATTRIBUTE));
+
+ key_id = pk11_FindObjectByTemplate(slot, findTemp, tsize);
+ if (key_id == CK_INVALID_HANDLE) {
+ return NULL;
+ }
+ return PK11_SymKeyFromHandle(slot, NULL, PK11_OriginDerive, type, key_id,
+ PR_FALSE, wincx);
+}
+
+PK11SymKey *
+PK11_ListFixedKeysInSlot(PK11SlotInfo *slot, char *nickname, void *wincx)
+{
+ CK_ATTRIBUTE findTemp[4];
+ CK_ATTRIBUTE *attrs;
+ CK_BBOOL ckTrue = CK_TRUE;
+ CK_OBJECT_CLASS keyclass = CKO_SECRET_KEY;
+ int tsize = 0;
+ int objCount = 0;
+ CK_OBJECT_HANDLE *key_ids;
+ PK11SymKey *nextKey = NULL;
+ PK11SymKey *topKey = NULL;
+ int i, len;
+
+ attrs = findTemp;
+ PK11_SETATTRS(attrs, CKA_CLASS, &keyclass, sizeof(keyclass));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_TOKEN, &ckTrue, sizeof(ckTrue));
+ attrs++;
+ if (nickname) {
+ len = PORT_Strlen(nickname);
+ PK11_SETATTRS(attrs, CKA_LABEL, nickname, len);
+ attrs++;
+ }
+ tsize = attrs - findTemp;
+ PORT_Assert(tsize <= sizeof(findTemp) / sizeof(CK_ATTRIBUTE));
+
+ key_ids = pk11_FindObjectsByTemplate(slot, findTemp, tsize, &objCount);
+ if (key_ids == NULL) {
+ return NULL;
+ }
+
+ for (i = 0; i < objCount; i++) {
+ SECItem typeData;
+ CK_KEY_TYPE type = CKK_GENERIC_SECRET;
+ SECStatus rv = PK11_ReadAttribute(slot, key_ids[i],
+ CKA_KEY_TYPE, NULL, &typeData);
+ if (rv == SECSuccess) {
+ if (typeData.len == sizeof(CK_KEY_TYPE)) {
+ type = *(CK_KEY_TYPE *)typeData.data;
+ }
+ PORT_Free(typeData.data);
+ }
+ nextKey = PK11_SymKeyFromHandle(slot, NULL, PK11_OriginDerive,
+ PK11_GetKeyMechanism(type), key_ids[i], PR_FALSE, wincx);
+ if (nextKey) {
+ nextKey->next = topKey;
+ topKey = nextKey;
+ }
+ }
+ PORT_Free(key_ids);
+ return topKey;
+}
+
+void *
+PK11_GetWindow(PK11SymKey *key)
+{
+ return key->cx;
+}
+
+/*
+ * extract a symmetric key value. NOTE: if the key is sensitive, we will
+ * not be able to do this operation. This function is used to move
+ * keys from one token to another */
+SECStatus
+PK11_ExtractKeyValue(PK11SymKey *symKey)
+{
+ SECStatus rv;
+
+ if (symKey == NULL) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ if (symKey->data.data != NULL) {
+ if (symKey->size == 0) {
+ symKey->size = symKey->data.len;
+ }
+ return SECSuccess;
+ }
+
+ if (symKey->slot == NULL) {
+ PORT_SetError(SEC_ERROR_INVALID_KEY);
+ return SECFailure;
+ }
+
+ rv = PK11_ReadAttribute(symKey->slot, symKey->objectID, CKA_VALUE, NULL,
+ &symKey->data);
+ if (rv == SECSuccess) {
+ symKey->size = symKey->data.len;
+ }
+ return rv;
+}
+
+SECStatus
+PK11_DeleteTokenSymKey(PK11SymKey *symKey)
+{
+ if (!PK11_IsPermObject(symKey->slot, symKey->objectID)) {
+ return SECFailure;
+ }
+ PK11_DestroyTokenObject(symKey->slot, symKey->objectID);
+ symKey->objectID = CK_INVALID_HANDLE;
+ return SECSuccess;
+}
+
+SECItem *
+PK11_GetKeyData(PK11SymKey *symKey)
+{
+ return &symKey->data;
+}
+
+/* This symbol is exported for backward compatibility. */
+SECItem *
+__PK11_GetKeyData(PK11SymKey *symKey)
+{
+ return PK11_GetKeyData(symKey);
+}
+
+/*
+ * PKCS #11 key Types with predefined length
+ */
+unsigned int
+pk11_GetPredefinedKeyLength(CK_KEY_TYPE keyType)
+{
+ int length = 0;
+ switch (keyType) {
+ case CKK_DES:
+ length = 8;
+ break;
+ case CKK_DES2:
+ length = 16;
+ break;
+ case CKK_DES3:
+ length = 24;
+ break;
+ case CKK_SKIPJACK:
+ length = 10;
+ break;
+ case CKK_BATON:
+ length = 20;
+ break;
+ case CKK_JUNIPER:
+ length = 20;
+ break;
+ default:
+ break;
+ }
+ return length;
+}
+
+/* return the keylength if possible. '0' if not */
+unsigned int
+PK11_GetKeyLength(PK11SymKey *key)
+{
+ CK_KEY_TYPE keyType;
+
+ if (key->size != 0)
+ return key->size;
+
+ /* First try to figure out the key length from its type */
+ keyType = PK11_ReadULongAttribute(key->slot, key->objectID, CKA_KEY_TYPE);
+ key->size = pk11_GetPredefinedKeyLength(keyType);
+ if ((keyType == CKK_GENERIC_SECRET) &&
+ (key->type == CKM_SSL3_PRE_MASTER_KEY_GEN)) {
+ key->size = 48;
+ }
+
+ if (key->size != 0)
+ return key->size;
+
+ if (key->data.data == NULL) {
+ PK11_ExtractKeyValue(key);
+ }
+ /* key is probably secret. Look up its length */
+ /* this is new PKCS #11 version 2.0 functionality. */
+ if (key->size == 0) {
+ CK_ULONG keyLength;
+
+ keyLength = PK11_ReadULongAttribute(key->slot, key->objectID, CKA_VALUE_LEN);
+ if (keyLength != CK_UNAVAILABLE_INFORMATION) {
+ key->size = (unsigned int)keyLength;
+ }
+ }
+
+ return key->size;
+}
+
+/* return the strength of a key. This is different from length in that
+ * 1) it returns the size in bits, and 2) it returns only the secret portions
+ * of the key minus any checksums or parity.
+ */
+unsigned int
+PK11_GetKeyStrength(PK11SymKey *key, SECAlgorithmID *algid)
+{
+ int size = 0;
+ CK_MECHANISM_TYPE mechanism = CKM_INVALID_MECHANISM; /* RC2 only */
+ SECItem *param = NULL; /* RC2 only */
+ CK_RC2_CBC_PARAMS *rc2_params = NULL; /* RC2 ONLY */
+ unsigned int effectiveBits = 0; /* RC2 ONLY */
+
+ switch (PK11_GetKeyType(key->type, 0)) {
+ case CKK_CDMF:
+ return 40;
+ case CKK_DES:
+ return 56;
+ case CKK_DES3:
+ case CKK_DES2:
+ size = PK11_GetKeyLength(key);
+ if (size == 16) {
+ /* double des */
+ return 112; /* 16*7 */
+ }
+ return 168;
+ /*
+ * RC2 has is different than other ciphers in that it allows the user
+ * to deprecating keysize while still requiring all the bits for the
+ * original key. The info
+ * on what the effective key strength is in the parameter for the key.
+ * In S/MIME this parameter is stored in the DER encoded algid. In Our
+ * other uses of RC2, effectiveBits == keyBits, so this code functions
+ * correctly without an algid.
+ */
+ case CKK_RC2:
+ /* if no algid was provided, fall through to default */
+ if (!algid) {
+ break;
+ }
+ /* verify that the algid is for RC2 */
+ mechanism = PK11_AlgtagToMechanism(SECOID_GetAlgorithmTag(algid));
+ if ((mechanism != CKM_RC2_CBC) && (mechanism != CKM_RC2_ECB)) {
+ break;
+ }
+
+ /* now get effective bits from the algorithm ID. */
+ param = PK11_ParamFromAlgid(algid);
+ /* if we couldn't get memory just use key length */
+ if (param == NULL) {
+ break;
+ }
+
+ rc2_params = (CK_RC2_CBC_PARAMS *)param->data;
+ /* paranoia... shouldn't happen */
+ PORT_Assert(param->data != NULL);
+ if (param->data == NULL) {
+ SECITEM_FreeItem(param, PR_TRUE);
+ break;
+ }
+ effectiveBits = (unsigned int)rc2_params->ulEffectiveBits;
+ SECITEM_FreeItem(param, PR_TRUE);
+ param = NULL;
+ rc2_params = NULL; /* paranoia */
+
+ /* we have effective bits, is and allocated memory is free, now
+ * we need to return the smaller of effective bits and keysize */
+ size = PK11_GetKeyLength(key);
+ if ((unsigned int)size * 8 > effectiveBits) {
+ return effectiveBits;
+ }
+
+ return size * 8; /* the actual key is smaller, the strength can't be
+ * greater than the actual key size */
+
+ default:
+ break;
+ }
+ return PK11_GetKeyLength(key) * 8;
+}
+
+/*
+ * The next three utilities are to deal with the fact that a given operation
+ * may be a multi-slot affair. This creates a new key object that is copied
+ * into the new slot.
+ */
+PK11SymKey *
+pk11_CopyToSlotPerm(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
+ CK_ATTRIBUTE_TYPE operation, CK_FLAGS flags,
+ PRBool isPerm, PK11SymKey *symKey)
+{
+ SECStatus rv;
+ PK11SymKey *newKey = NULL;
+
+ /* Extract the raw key data if possible */
+ if (symKey->data.data == NULL) {
+ rv = PK11_ExtractKeyValue(symKey);
+ /* KEY is sensitive, we're try key exchanging it. */
+ if (rv != SECSuccess) {
+ return pk11_KeyExchange(slot, type, operation,
+ flags, isPerm, symKey);
+ }
+ }
+
+ newKey = PK11_ImportSymKeyWithFlags(slot, type, symKey->origin,
+ operation, &symKey->data, flags, isPerm, symKey->cx);
+ if (newKey == NULL) {
+ newKey = pk11_KeyExchange(slot, type, operation, flags, isPerm, symKey);
+ }
+ return newKey;
+}
+
+PK11SymKey *
+pk11_CopyToSlot(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
+ CK_ATTRIBUTE_TYPE operation, PK11SymKey *symKey)
+{
+ return pk11_CopyToSlotPerm(slot, type, operation, 0, PR_FALSE, symKey);
+}
+
+/*
+ * Make sure the slot we are in is the correct slot for the operation
+ * by verifying that it supports all of the specified mechanism types.
+ */
+PK11SymKey *
+pk11_ForceSlotMultiple(PK11SymKey *symKey, CK_MECHANISM_TYPE *type,
+ int mechCount, CK_ATTRIBUTE_TYPE operation)
+{
+ PK11SlotInfo *slot = symKey->slot;
+ PK11SymKey *newKey = NULL;
+ PRBool needToCopy = PR_FALSE;
+ int i;
+
+ if (slot == NULL) {
+ needToCopy = PR_TRUE;
+ } else {
+ i = 0;
+ while ((i < mechCount) && (needToCopy == PR_FALSE)) {
+ if (!PK11_DoesMechanism(slot, type[i])) {
+ needToCopy = PR_TRUE;
+ }
+ i++;
+ }
+ }
+
+ if (needToCopy == PR_TRUE) {
+ slot = PK11_GetBestSlotMultiple(type, mechCount, symKey->cx);
+ if (slot == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MODULE);
+ return NULL;
+ }
+ newKey = pk11_CopyToSlot(slot, type[0], operation, symKey);
+ PK11_FreeSlot(slot);
+ }
+ return newKey;
+}
+
+/*
+ * Make sure the slot we are in is the correct slot for the operation
+ */
+PK11SymKey *
+pk11_ForceSlot(PK11SymKey *symKey, CK_MECHANISM_TYPE type,
+ CK_ATTRIBUTE_TYPE operation)
+{
+ return pk11_ForceSlotMultiple(symKey, &type, 1, operation);
+}
+
+PK11SymKey *
+PK11_MoveSymKey(PK11SlotInfo *slot, CK_ATTRIBUTE_TYPE operation,
+ CK_FLAGS flags, PRBool perm, PK11SymKey *symKey)
+{
+ if (symKey->slot == slot) {
+ if (perm) {
+ return PK11_ConvertSessionSymKeyToTokenSymKey(symKey, symKey->cx);
+ } else {
+ return PK11_ReferenceSymKey(symKey);
+ }
+ }
+
+ return pk11_CopyToSlotPerm(slot, symKey->type,
+ operation, flags, perm, symKey);
+}
+
+/*
+ * Use the token to generate a key.
+ *
+ * keySize must be 'zero' for fixed key length algorithms. A nonzero
+ * keySize causes the CKA_VALUE_LEN attribute to be added to the template
+ * for the key. Most PKCS #11 modules fail if you specify the CKA_VALUE_LEN
+ * attribute for keys with fixed length. The exception is DES2. If you
+ * select a CKM_DES3_CBC mechanism, this code will not add the CKA_VALUE_LEN
+ * parameter and use the key size to determine which underlying DES keygen
+ * function to use (CKM_DES2_KEY_GEN or CKM_DES3_KEY_GEN).
+ *
+ * keyType must be -1 for most algorithms. Some PBE algorthims cannot
+ * determine the correct key type from the mechanism or the parameters,
+ * so key type must be specified. Other PKCS #11 mechanisms may do so in
+ * the future. Currently there is no need to export this publically.
+ * Keep it private until there is a need in case we need to expand the
+ * keygen parameters again...
+ *
+ * CK_FLAGS flags: key operation flags
+ * PK11AttrFlags attrFlags: PK11_ATTR_XXX key attribute flags
+ */
+PK11SymKey *
+pk11_TokenKeyGenWithFlagsAndKeyType(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
+ SECItem *param, CK_KEY_TYPE keyType, int keySize, SECItem *keyid,
+ CK_FLAGS opFlags, PK11AttrFlags attrFlags, void *wincx)
+{
+ PK11SymKey *symKey;
+ CK_ATTRIBUTE genTemplate[MAX_TEMPL_ATTRS];
+ CK_ATTRIBUTE *attrs = genTemplate;
+ int count = sizeof(genTemplate) / sizeof(genTemplate[0]);
+ CK_MECHANISM_TYPE keyGenType;
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_BBOOL ckfalse = CK_FALSE;
+ CK_ULONG ck_key_size; /* only used for variable-length keys */
+
+ if (pk11_BadAttrFlags(attrFlags)) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ if ((keySize != 0) && (type != CKM_DES3_CBC) &&
+ (type != CKM_DES3_CBC_PAD) && (type != CKM_DES3_ECB)) {
+ ck_key_size = keySize; /* Convert to PK11 type */
+
+ PK11_SETATTRS(attrs, CKA_VALUE_LEN, &ck_key_size, sizeof(ck_key_size));
+ attrs++;
+ }
+
+ if (keyType != -1) {
+ PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(CK_KEY_TYPE));
+ attrs++;
+ }
+
+ /* Include key id value if provided */
+ if (keyid) {
+ PK11_SETATTRS(attrs, CKA_ID, keyid->data, keyid->len);
+ attrs++;
+ }
+
+ attrs += pk11_AttrFlagsToAttributes(attrFlags, attrs, &cktrue, &ckfalse);
+ attrs += pk11_OpFlagsToAttributes(opFlags, attrs, &cktrue);
+
+ count = attrs - genTemplate;
+ PR_ASSERT(count <= sizeof(genTemplate) / sizeof(CK_ATTRIBUTE));
+
+ keyGenType = PK11_GetKeyGenWithSize(type, keySize);
+ if (keyGenType == CKM_FAKE_RANDOM) {
+ PORT_SetError(SEC_ERROR_NO_MODULE);
+ return NULL;
+ }
+ symKey = PK11_KeyGenWithTemplate(slot, type, keyGenType,
+ param, genTemplate, count, wincx);
+ if (symKey != NULL) {
+ symKey->size = keySize;
+ }
+ return symKey;
+}
+
+/*
+ * Use the token to generate a key. - Public
+ *
+ * keySize must be 'zero' for fixed key length algorithms. A nonzero
+ * keySize causes the CKA_VALUE_LEN attribute to be added to the template
+ * for the key. Most PKCS #11 modules fail if you specify the CKA_VALUE_LEN
+ * attribute for keys with fixed length. The exception is DES2. If you
+ * select a CKM_DES3_CBC mechanism, this code will not add the CKA_VALUE_LEN
+ * parameter and use the key size to determine which underlying DES keygen
+ * function to use (CKM_DES2_KEY_GEN or CKM_DES3_KEY_GEN).
+ *
+ * CK_FLAGS flags: key operation flags
+ * PK11AttrFlags attrFlags: PK11_ATTR_XXX key attribute flags
+ */
+PK11SymKey *
+PK11_TokenKeyGenWithFlags(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
+ SECItem *param, int keySize, SECItem *keyid, CK_FLAGS opFlags,
+ PK11AttrFlags attrFlags, void *wincx)
+{
+ return pk11_TokenKeyGenWithFlagsAndKeyType(slot, type, param, -1, keySize,
+ keyid, opFlags, attrFlags, wincx);
+}
+
+/*
+ * Use the token to generate a key. keySize must be 'zero' for fixed key
+ * length algorithms. A nonzero keySize causes the CKA_VALUE_LEN attribute
+ * to be added to the template for the key. PKCS #11 modules fail if you
+ * specify the CKA_VALUE_LEN attribute for keys with fixed length.
+ * NOTE: this means to generate a DES2 key from this interface you must
+ * specify CKM_DES2_KEY_GEN as the mechanism directly; specifying
+ * CKM_DES3_CBC as the mechanism and 16 as keySize currently doesn't work.
+ */
+PK11SymKey *
+PK11_TokenKeyGen(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, SECItem *param,
+ int keySize, SECItem *keyid, PRBool isToken, void *wincx)
+{
+ PK11SymKey *symKey;
+ PRBool weird = PR_FALSE; /* hack for fortezza */
+ CK_FLAGS opFlags = CKF_SIGN;
+ PK11AttrFlags attrFlags = 0;
+
+ if ((keySize == -1) && (type == CKM_SKIPJACK_CBC64)) {
+ weird = PR_TRUE;
+ keySize = 0;
+ }
+
+ opFlags |= weird ? CKF_DECRYPT : CKF_ENCRYPT;
+
+ if (isToken) {
+ attrFlags |= (PK11_ATTR_TOKEN | PK11_ATTR_PRIVATE);
+ }
+
+ symKey = pk11_TokenKeyGenWithFlagsAndKeyType(slot, type, param,
+ -1, keySize, keyid, opFlags, attrFlags, wincx);
+ if (symKey && weird) {
+ PK11_SetFortezzaHack(symKey);
+ }
+
+ return symKey;
+}
+
+PK11SymKey *
+PK11_KeyGen(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, SECItem *param,
+ int keySize, void *wincx)
+{
+ return PK11_TokenKeyGen(slot, type, param, keySize, 0, PR_FALSE, wincx);
+}
+
+PK11SymKey *
+PK11_KeyGenWithTemplate(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
+ CK_MECHANISM_TYPE keyGenType,
+ SECItem *param, CK_ATTRIBUTE *attrs,
+ unsigned int attrsCount, void *wincx)
+{
+ PK11SymKey *symKey;
+ CK_SESSION_HANDLE session;
+ CK_MECHANISM mechanism;
+ CK_RV crv;
+ PRBool isToken = CK_FALSE;
+ CK_ULONG keySize = 0;
+ unsigned i;
+
+ /* Extract the template's CKA_VALUE_LEN into keySize and CKA_TOKEN into
+ isToken. */
+ for (i = 0; i < attrsCount; ++i) {
+ switch (attrs[i].type) {
+ case CKA_VALUE_LEN:
+ if (attrs[i].pValue == NULL ||
+ attrs[i].ulValueLen != sizeof(CK_ULONG)) {
+ PORT_SetError(PK11_MapError(CKR_TEMPLATE_INCONSISTENT));
+ return NULL;
+ }
+ keySize = *(CK_ULONG *)attrs[i].pValue;
+ break;
+ case CKA_TOKEN:
+ if (attrs[i].pValue == NULL ||
+ attrs[i].ulValueLen != sizeof(CK_BBOOL)) {
+ PORT_SetError(PK11_MapError(CKR_TEMPLATE_INCONSISTENT));
+ return NULL;
+ }
+ isToken = (*(CK_BBOOL *)attrs[i].pValue) ? PR_TRUE : PR_FALSE;
+ break;
+ }
+ }
+
+ /* find a slot to generate the key into */
+ /* Only do slot management if this is not a token key */
+ if (!isToken && (slot == NULL || !PK11_DoesMechanism(slot, type))) {
+ PK11SlotInfo *bestSlot = PK11_GetBestSlot(type, wincx);
+ if (bestSlot == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MODULE);
+ return NULL;
+ }
+ symKey = pk11_CreateSymKey(bestSlot, type, !isToken, PR_TRUE, wincx);
+ PK11_FreeSlot(bestSlot);
+ } else {
+ symKey = pk11_CreateSymKey(slot, type, !isToken, PR_TRUE, wincx);
+ }
+ if (symKey == NULL)
+ return NULL;
+
+ symKey->size = keySize;
+ symKey->origin = PK11_OriginGenerated;
+
+ /* Set the parameters for the key gen if provided */
+ mechanism.mechanism = keyGenType;
+ mechanism.pParameter = NULL;
+ mechanism.ulParameterLen = 0;
+ if (param) {
+ mechanism.pParameter = param->data;
+ mechanism.ulParameterLen = param->len;
+ }
+
+ /* Get session and perform locking */
+ if (isToken) {
+ PK11_Authenticate(symKey->slot, PR_TRUE, wincx);
+ /* Should always be original slot */
+ session = PK11_GetRWSession(symKey->slot);
+ symKey->owner = PR_FALSE;
+ } else {
+ session = symKey->session;
+ if (session != CK_INVALID_HANDLE)
+ pk11_EnterKeyMonitor(symKey);
+ }
+ if (session == CK_INVALID_HANDLE) {
+ PK11_FreeSymKey(symKey);
+ PORT_SetError(SEC_ERROR_BAD_DATA);
+ return NULL;
+ }
+
+ crv = PK11_GETTAB(symKey->slot)->C_GenerateKey(session, &mechanism, attrs, attrsCount, &symKey->objectID);
+
+ /* Release lock and session */
+ if (isToken) {
+ PK11_RestoreROSession(symKey->slot, session);
+ } else {
+ pk11_ExitKeyMonitor(symKey);
+ }
+
+ if (crv != CKR_OK) {
+ PK11_FreeSymKey(symKey);
+ PORT_SetError(PK11_MapError(crv));
+ return NULL;
+ }
+
+ return symKey;
+}
+
+/* --- */
+PK11SymKey *
+PK11_GenDES3TokenKey(PK11SlotInfo *slot, SECItem *keyid, void *cx)
+{
+ return PK11_TokenKeyGen(slot, CKM_DES3_CBC, 0, 0, keyid, PR_TRUE, cx);
+}
+
+PK11SymKey *
+PK11_ConvertSessionSymKeyToTokenSymKey(PK11SymKey *symk, void *wincx)
+{
+ PK11SlotInfo *slot = symk->slot;
+ CK_ATTRIBUTE template[1];
+ CK_ATTRIBUTE *attrs = template;
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_RV crv;
+ CK_OBJECT_HANDLE newKeyID;
+ CK_SESSION_HANDLE rwsession;
+
+ PK11_SETATTRS(attrs, CKA_TOKEN, &cktrue, sizeof(cktrue));
+ attrs++;
+
+ PK11_Authenticate(slot, PR_TRUE, wincx);
+ rwsession = PK11_GetRWSession(slot);
+ if (rwsession == CK_INVALID_HANDLE) {
+ PORT_SetError(SEC_ERROR_BAD_DATA);
+ return NULL;
+ }
+ crv = PK11_GETTAB(slot)->C_CopyObject(rwsession, symk->objectID,
+ template, 1, &newKeyID);
+ PK11_RestoreROSession(slot, rwsession);
+
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return NULL;
+ }
+
+ return PK11_SymKeyFromHandle(slot, NULL /*parent*/, symk->origin,
+ symk->type, newKeyID, PR_FALSE /*owner*/, NULL /*wincx*/);
+}
+
+/* This function does a straight public key wrap with the CKM_RSA_PKCS
+ * mechanism. */
+SECStatus
+PK11_PubWrapSymKey(CK_MECHANISM_TYPE type, SECKEYPublicKey *pubKey,
+ PK11SymKey *symKey, SECItem *wrappedKey)
+{
+ CK_MECHANISM_TYPE inferred = pk11_mapWrapKeyType(pubKey->keyType);
+ return PK11_PubWrapSymKeyWithMechanism(pubKey, inferred, NULL, symKey,
+ wrappedKey);
+}
+
+/* This function wraps a symmetric key with a public key, such as with the
+ * CKM_RSA_PKCS and CKM_RSA_PKCS_OAEP mechanisms. */
+SECStatus
+PK11_PubWrapSymKeyWithMechanism(SECKEYPublicKey *pubKey,
+ CK_MECHANISM_TYPE mechType, SECItem *param,
+ PK11SymKey *symKey, SECItem *wrappedKey)
+{
+ PK11SlotInfo *slot;
+ CK_ULONG len = wrappedKey->len;
+ PK11SymKey *newKey = NULL;
+ CK_OBJECT_HANDLE id;
+ CK_MECHANISM mechanism;
+ PRBool owner = PR_TRUE;
+ CK_SESSION_HANDLE session;
+ CK_RV crv;
+
+ if (symKey == NULL) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ /* if this slot doesn't support the mechanism, go to a slot that does */
+ newKey = pk11_ForceSlot(symKey, mechType, CKA_ENCRYPT);
+ if (newKey != NULL) {
+ symKey = newKey;
+ }
+
+ if (symKey->slot == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MODULE);
+ return SECFailure;
+ }
+
+ slot = symKey->slot;
+
+ mechanism.mechanism = mechType;
+ if (param == NULL) {
+ mechanism.pParameter = NULL;
+ mechanism.ulParameterLen = 0;
+ } else {
+ mechanism.pParameter = param->data;
+ mechanism.ulParameterLen = param->len;
+ }
+
+ id = PK11_ImportPublicKey(slot, pubKey, PR_FALSE);
+ if (id == CK_INVALID_HANDLE) {
+ if (newKey) {
+ PK11_FreeSymKey(newKey);
+ }
+ return SECFailure; /* Error code has been set. */
+ }
+
+ session = pk11_GetNewSession(slot, &owner);
+ if (!owner || !(slot->isThreadSafe))
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_WrapKey(session, &mechanism,
+ id, symKey->objectID, wrappedKey->data, &len);
+ if (!owner || !(slot->isThreadSafe))
+ PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot, session, owner);
+ if (newKey) {
+ PK11_FreeSymKey(newKey);
+ }
+
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ wrappedKey->len = len;
+ return SECSuccess;
+}
+
+/*
+ * this little function uses the Encrypt function to wrap a key, just in
+ * case we have problems with the wrap implementation for a token.
+ */
+static SECStatus
+pk11_HandWrap(PK11SymKey *wrappingKey, SECItem *param, CK_MECHANISM_TYPE type,
+ SECItem *inKey, SECItem *outKey)
+{
+ PK11SlotInfo *slot;
+ CK_ULONG len;
+ SECItem *data;
+ CK_MECHANISM mech;
+ PRBool owner = PR_TRUE;
+ CK_SESSION_HANDLE session;
+ CK_RV crv;
+
+ slot = wrappingKey->slot;
+ /* use NULL IV's for wrapping */
+ mech.mechanism = type;
+ if (param) {
+ mech.pParameter = param->data;
+ mech.ulParameterLen = param->len;
+ } else {
+ mech.pParameter = NULL;
+ mech.ulParameterLen = 0;
+ }
+ session = pk11_GetNewSession(slot, &owner);
+ if (!owner || !(slot->isThreadSafe))
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_EncryptInit(session, &mech,
+ wrappingKey->objectID);
+ if (crv != CKR_OK) {
+ if (!owner || !(slot->isThreadSafe))
+ PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot, session, owner);
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+
+ /* keys are almost always aligned, but if we get this far,
+ * we've gone above and beyond anyway... */
+ data = PK11_BlockData(inKey, PK11_GetBlockSize(type, param));
+ if (data == NULL) {
+ if (!owner || !(slot->isThreadSafe))
+ PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot, session, owner);
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+ len = outKey->len;
+ crv = PK11_GETTAB(slot)->C_Encrypt(session, data->data, data->len,
+ outKey->data, &len);
+ if (!owner || !(slot->isThreadSafe))
+ PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot, session, owner);
+ SECITEM_FreeItem(data, PR_TRUE);
+ outKey->len = len;
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/*
+ * helper function which moves two keys into a new slot based on the
+ * desired mechanism.
+ */
+static SECStatus
+pk11_moveTwoKeys(CK_MECHANISM_TYPE mech,
+ CK_ATTRIBUTE_TYPE preferedOperation,
+ CK_ATTRIBUTE_TYPE movingOperation,
+ PK11SymKey *preferedKey, PK11SymKey *movingKey,
+ PK11SymKey **newPreferedKey, PK11SymKey **newMovingKey)
+{
+ PK11SlotInfo *newSlot;
+ *newMovingKey = NULL;
+ *newPreferedKey = NULL;
+
+ newSlot = PK11_GetBestSlot(mech, preferedKey->cx);
+ if (newSlot == NULL) {
+ return SECFailure;
+ }
+ *newMovingKey = pk11_CopyToSlot(newSlot, movingKey->type,
+ movingOperation, movingKey);
+ if (*newMovingKey == NULL) {
+ goto loser;
+ }
+ *newPreferedKey = pk11_CopyToSlot(newSlot, preferedKey->type,
+ preferedOperation, preferedKey);
+ if (*newPreferedKey == NULL) {
+ goto loser;
+ }
+
+ PK11_FreeSlot(newSlot);
+ return SECSuccess;
+loser:
+ PK11_FreeSlot(newSlot);
+ PK11_FreeSymKey(*newMovingKey);
+ PK11_FreeSymKey(*newPreferedKey);
+ *newMovingKey = NULL;
+ *newPreferedKey = NULL;
+ return SECFailure;
+}
+
+/*
+ * To do joint operations, we often need two keys in the same slot.
+ * Usually the PKCS #11 wrappers handle this correctly (like for PK11_WrapKey),
+ * but sometimes the wrappers don't know about mechanism specific keys in
+ * the Mechanism params. This function makes sure the two keys are in the
+ * same slot by copying one or both of the keys into a common slot. This
+ * functions makes sure the slot can handle the target mechanism. If the copy
+ * is warranted, this function will prefer to move the movingKey first, then
+ * the preferedKey. If the keys are moved, the new keys are returned in
+ * newMovingKey and/or newPreferedKey. The application is responsible
+ * for freeing those keys once the operation is complete.
+ */
+SECStatus
+PK11_SymKeysToSameSlot(CK_MECHANISM_TYPE mech,
+ CK_ATTRIBUTE_TYPE preferedOperation,
+ CK_ATTRIBUTE_TYPE movingOperation,
+ PK11SymKey *preferedKey, PK11SymKey *movingKey,
+ PK11SymKey **newPreferedKey, PK11SymKey **newMovingKey)
+{
+ /* usually don't return new keys */
+ *newMovingKey = NULL;
+ *newPreferedKey = NULL;
+ if (movingKey->slot == preferedKey->slot) {
+
+ /* this should be the most common case */
+ if ((preferedKey->slot != NULL) &&
+ PK11_DoesMechanism(preferedKey->slot, mech)) {
+ return SECSuccess;
+ }
+
+ /* we are in the same slot, but it doesn't do the operation,
+ * move both keys to an appropriate target slot */
+ return pk11_moveTwoKeys(mech, preferedOperation, movingOperation,
+ preferedKey, movingKey,
+ newPreferedKey, newMovingKey);
+ }
+
+ /* keys are in different slot, try moving the moving key to the prefered
+ * key's slot */
+ if ((preferedKey->slot != NULL) &&
+ PK11_DoesMechanism(preferedKey->slot, mech)) {
+ *newMovingKey = pk11_CopyToSlot(preferedKey->slot, movingKey->type,
+ movingOperation, movingKey);
+ if (*newMovingKey != NULL) {
+ return SECSuccess;
+ }
+ }
+ /* couldn't moving the moving key to the prefered slot, try moving
+ * the prefered key */
+ if ((movingKey->slot != NULL) &&
+ PK11_DoesMechanism(movingKey->slot, mech)) {
+ *newPreferedKey = pk11_CopyToSlot(movingKey->slot, preferedKey->type,
+ preferedOperation, preferedKey);
+ if (*newPreferedKey != NULL) {
+ return SECSuccess;
+ }
+ }
+ /* Neither succeeded, but that could be that they were not in slots that
+ * supported the operation, try moving both keys into a common slot that
+ * can do the operation. */
+ return pk11_moveTwoKeys(mech, preferedOperation, movingOperation,
+ preferedKey, movingKey,
+ newPreferedKey, newMovingKey);
+}
+
+/*
+ * This function does a symetric based wrap.
+ */
+SECStatus
+PK11_WrapSymKey(CK_MECHANISM_TYPE type, SECItem *param,
+ PK11SymKey *wrappingKey, PK11SymKey *symKey,
+ SECItem *wrappedKey)
+{
+ PK11SlotInfo *slot;
+ CK_ULONG len = wrappedKey->len;
+ PK11SymKey *newSymKey = NULL;
+ PK11SymKey *newWrappingKey = NULL;
+ SECItem *param_save = NULL;
+ CK_MECHANISM mechanism;
+ PRBool owner = PR_TRUE;
+ CK_SESSION_HANDLE session;
+ CK_RV crv;
+ SECStatus rv;
+
+ /* force the keys into same slot */
+ rv = PK11_SymKeysToSameSlot(type, CKA_ENCRYPT, CKA_WRAP,
+ symKey, wrappingKey,
+ &newSymKey, &newWrappingKey);
+ if (rv != SECSuccess) {
+ /* Couldn't move the keys as desired, try to hand unwrap if possible */
+ if (symKey->data.data == NULL) {
+ rv = PK11_ExtractKeyValue(symKey);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_NO_MODULE);
+ return SECFailure;
+ }
+ }
+ if (param == NULL) {
+ param_save = param = PK11_ParamFromIV(type, NULL);
+ }
+ rv = pk11_HandWrap(wrappingKey, param, type, &symKey->data, wrappedKey);
+ if (param_save)
+ SECITEM_FreeItem(param_save, PR_TRUE);
+ return rv;
+ }
+ if (newSymKey) {
+ symKey = newSymKey;
+ }
+ if (newWrappingKey) {
+ wrappingKey = newWrappingKey;
+ }
+
+ /* at this point both keys are in the same token */
+ slot = wrappingKey->slot;
+ mechanism.mechanism = type;
+ /* use NULL IV's for wrapping */
+ if (param == NULL) {
+ param_save = param = PK11_ParamFromIV(type, NULL);
+ }
+ if (param) {
+ mechanism.pParameter = param->data;
+ mechanism.ulParameterLen = param->len;
+ } else {
+ mechanism.pParameter = NULL;
+ mechanism.ulParameterLen = 0;
+ }
+
+ len = wrappedKey->len;
+
+ session = pk11_GetNewSession(slot, &owner);
+ if (!owner || !(slot->isThreadSafe))
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_WrapKey(session, &mechanism,
+ wrappingKey->objectID, symKey->objectID,
+ wrappedKey->data, &len);
+ if (!owner || !(slot->isThreadSafe))
+ PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot, session, owner);
+ rv = SECSuccess;
+ if (crv != CKR_OK) {
+ /* can't wrap it? try hand wrapping it... */
+ do {
+ if (symKey->data.data == NULL) {
+ rv = PK11_ExtractKeyValue(symKey);
+ if (rv != SECSuccess)
+ break;
+ }
+ rv = pk11_HandWrap(wrappingKey, param, type, &symKey->data,
+ wrappedKey);
+ } while (PR_FALSE);
+ } else {
+ wrappedKey->len = len;
+ }
+ PK11_FreeSymKey(newSymKey);
+ PK11_FreeSymKey(newWrappingKey);
+ if (param_save)
+ SECITEM_FreeItem(param_save, PR_TRUE);
+ return rv;
+}
+
+/*
+ * This Generates a new key based on a symetricKey
+ */
+PK11SymKey *
+PK11_Derive(PK11SymKey *baseKey, CK_MECHANISM_TYPE derive, SECItem *param,
+ CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation,
+ int keySize)
+{
+ return PK11_DeriveWithTemplate(baseKey, derive, param, target, operation,
+ keySize, NULL, 0, PR_FALSE);
+}
+
+PK11SymKey *
+PK11_DeriveWithFlags(PK11SymKey *baseKey, CK_MECHANISM_TYPE derive,
+ SECItem *param, CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation,
+ int keySize, CK_FLAGS flags)
+{
+ CK_BBOOL ckTrue = CK_TRUE;
+ CK_ATTRIBUTE keyTemplate[MAX_TEMPL_ATTRS];
+ unsigned int templateCount;
+
+ templateCount = pk11_OpFlagsToAttributes(flags, keyTemplate, &ckTrue);
+ return PK11_DeriveWithTemplate(baseKey, derive, param, target, operation,
+ keySize, keyTemplate, templateCount, PR_FALSE);
+}
+
+PK11SymKey *
+PK11_DeriveWithFlagsPerm(PK11SymKey *baseKey, CK_MECHANISM_TYPE derive,
+ SECItem *param, CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation,
+ int keySize, CK_FLAGS flags, PRBool isPerm)
+{
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_ATTRIBUTE keyTemplate[MAX_TEMPL_ATTRS];
+ CK_ATTRIBUTE *attrs;
+ unsigned int templateCount = 0;
+
+ attrs = keyTemplate;
+ if (isPerm) {
+ PK11_SETATTRS(attrs, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL));
+ attrs++;
+ }
+ templateCount = attrs - keyTemplate;
+ templateCount += pk11_OpFlagsToAttributes(flags, attrs, &cktrue);
+ return PK11_DeriveWithTemplate(baseKey, derive, param, target, operation,
+ keySize, keyTemplate, templateCount, isPerm);
+}
+
+PK11SymKey *
+PK11_DeriveWithTemplate(PK11SymKey *baseKey, CK_MECHANISM_TYPE derive,
+ SECItem *param, CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation,
+ int keySize, CK_ATTRIBUTE *userAttr, unsigned int numAttrs,
+ PRBool isPerm)
+{
+ PK11SlotInfo *slot = baseKey->slot;
+ PK11SymKey *symKey;
+ PK11SymKey *newBaseKey = NULL;
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY;
+ CK_KEY_TYPE keyType = CKK_GENERIC_SECRET;
+ CK_ULONG valueLen = 0;
+ CK_MECHANISM mechanism;
+ CK_RV crv;
+#define MAX_ADD_ATTRS 4
+ CK_ATTRIBUTE keyTemplate[MAX_TEMPL_ATTRS + MAX_ADD_ATTRS];
+#undef MAX_ADD_ATTRS
+ CK_ATTRIBUTE *attrs = keyTemplate;
+ CK_SESSION_HANDLE session;
+ unsigned int templateCount;
+
+ if (numAttrs > MAX_TEMPL_ATTRS) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+ /* CKA_NSS_MESSAGE is a fake operation to distinguish between
+ * Normal Encrypt/Decrypt and MessageEncrypt/Decrypt. Don't try to set
+ * it as a real attribute */
+ if ((operation & CKA_NSS_MESSAGE_MASK) == CKA_NSS_MESSAGE) {
+ /* Message is or'd with a real Attribute (CKA_ENCRYPT, CKA_DECRYPT),
+ * etc. Strip out the real attribute here */
+ operation &= ~CKA_NSS_MESSAGE_MASK;
+ }
+
+ /* first copy caller attributes in. */
+ for (templateCount = 0; templateCount < numAttrs; ++templateCount) {
+ *attrs++ = *userAttr++;
+ }
+
+ /* We only add the following attributes to the template if the caller
+ ** didn't already supply them.
+ */
+ if (!pk11_FindAttrInTemplate(keyTemplate, numAttrs, CKA_CLASS)) {
+ PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof keyClass);
+ attrs++;
+ }
+ if (!pk11_FindAttrInTemplate(keyTemplate, numAttrs, CKA_KEY_TYPE)) {
+ keyType = PK11_GetKeyType(target, keySize);
+ PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof keyType);
+ attrs++;
+ }
+ if (keySize > 0 &&
+ !pk11_FindAttrInTemplate(keyTemplate, numAttrs, CKA_VALUE_LEN)) {
+ valueLen = (CK_ULONG)keySize;
+ PK11_SETATTRS(attrs, CKA_VALUE_LEN, &valueLen, sizeof valueLen);
+ attrs++;
+ }
+ if ((operation != CKA_FLAGS_ONLY) &&
+ !pk11_FindAttrInTemplate(keyTemplate, numAttrs, operation)) {
+ PK11_SETATTRS(attrs, operation, &cktrue, sizeof cktrue);
+ attrs++;
+ }
+
+ templateCount = attrs - keyTemplate;
+ PR_ASSERT(templateCount <= sizeof(keyTemplate) / sizeof(CK_ATTRIBUTE));
+
+ /* move the key to a slot that can do the function */
+ if (!PK11_DoesMechanism(slot, derive)) {
+ /* get a new base key & slot */
+ PK11SlotInfo *newSlot = PK11_GetBestSlot(derive, baseKey->cx);
+
+ if (newSlot == NULL)
+ return NULL;
+
+ newBaseKey = pk11_CopyToSlot(newSlot, derive, CKA_DERIVE,
+ baseKey);
+ PK11_FreeSlot(newSlot);
+ if (newBaseKey == NULL)
+ return NULL;
+ baseKey = newBaseKey;
+ slot = baseKey->slot;
+ }
+
+ /* get our key Structure */
+ symKey = pk11_CreateSymKey(slot, target, !isPerm, PR_TRUE, baseKey->cx);
+ if (symKey == NULL) {
+ return NULL;
+ }
+
+ symKey->size = keySize;
+
+ mechanism.mechanism = derive;
+ if (param) {
+ mechanism.pParameter = param->data;
+ mechanism.ulParameterLen = param->len;
+ } else {
+ mechanism.pParameter = NULL;
+ mechanism.ulParameterLen = 0;
+ }
+ symKey->origin = PK11_OriginDerive;
+
+ if (isPerm) {
+ session = PK11_GetRWSession(slot);
+ } else {
+ pk11_EnterKeyMonitor(symKey);
+ session = symKey->session;
+ }
+ if (session == CK_INVALID_HANDLE) {
+ if (!isPerm)
+ pk11_ExitKeyMonitor(symKey);
+ crv = CKR_SESSION_HANDLE_INVALID;
+ } else {
+ crv = PK11_GETTAB(slot)->C_DeriveKey(session, &mechanism,
+ baseKey->objectID, keyTemplate, templateCount, &symKey->objectID);
+ if (isPerm) {
+ PK11_RestoreROSession(slot, session);
+ } else {
+ pk11_ExitKeyMonitor(symKey);
+ }
+ }
+ if (newBaseKey)
+ PK11_FreeSymKey(newBaseKey);
+ if (crv != CKR_OK) {
+ PK11_FreeSymKey(symKey);
+ PORT_SetError(PK11_MapError(crv));
+ return NULL;
+ }
+ return symKey;
+}
+
+/* Create a new key by concatenating base and data
+ */
+static PK11SymKey *
+pk11_ConcatenateBaseAndData(PK11SymKey *base,
+ CK_BYTE *data, CK_ULONG dataLen, CK_MECHANISM_TYPE target,
+ CK_ATTRIBUTE_TYPE operation)
+{
+ CK_KEY_DERIVATION_STRING_DATA mechParams;
+ SECItem param;
+
+ if (base == NULL) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ mechParams.pData = data;
+ mechParams.ulLen = dataLen;
+ param.data = (unsigned char *)&mechParams;
+ param.len = sizeof(CK_KEY_DERIVATION_STRING_DATA);
+
+ return PK11_Derive(base, CKM_CONCATENATE_BASE_AND_DATA,
+ &param, target, operation, 0);
+}
+
+/* Create a new key by concatenating base and key
+ */
+static PK11SymKey *
+pk11_ConcatenateBaseAndKey(PK11SymKey *base,
+ PK11SymKey *key, CK_MECHANISM_TYPE target,
+ CK_ATTRIBUTE_TYPE operation, CK_ULONG keySize)
+{
+ SECItem param;
+
+ if ((base == NULL) || (key == NULL)) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ param.data = (unsigned char *)&(key->objectID);
+ param.len = sizeof(CK_OBJECT_HANDLE);
+
+ return PK11_Derive(base, CKM_CONCATENATE_BASE_AND_KEY,
+ &param, target, operation, keySize);
+}
+
+/* Create a new key whose value is the hash of tobehashed.
+ * type is the mechanism for the derived key.
+ */
+static PK11SymKey *
+pk11_HashKeyDerivation(PK11SymKey *toBeHashed,
+ CK_MECHANISM_TYPE hashMechanism, CK_MECHANISM_TYPE target,
+ CK_ATTRIBUTE_TYPE operation, CK_ULONG keySize)
+{
+ return PK11_Derive(toBeHashed, hashMechanism, NULL, target, operation, keySize);
+}
+
+/* This function implements the ANSI X9.63 key derivation function
+ */
+static PK11SymKey *
+pk11_ANSIX963Derive(PK11SymKey *sharedSecret,
+ CK_EC_KDF_TYPE kdf, SECItem *sharedData,
+ CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation,
+ CK_ULONG keySize)
+{
+ CK_KEY_TYPE keyType;
+ CK_MECHANISM_TYPE hashMechanism, mechanismArray[4];
+ CK_ULONG derivedKeySize, HashLen, counter, maxCounter, bufferLen;
+ CK_ULONG SharedInfoLen;
+ CK_BYTE *buffer = NULL;
+ PK11SymKey *toBeHashed, *hashOutput;
+ PK11SymKey *newSharedSecret = NULL;
+ PK11SymKey *oldIntermediateResult, *intermediateResult = NULL;
+
+ if (sharedSecret == NULL) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ switch (kdf) {
+ case CKD_SHA1_KDF:
+ HashLen = SHA1_LENGTH;
+ hashMechanism = CKM_SHA1_KEY_DERIVATION;
+ break;
+ case CKD_SHA224_KDF:
+ HashLen = SHA224_LENGTH;
+ hashMechanism = CKM_SHA224_KEY_DERIVATION;
+ break;
+ case CKD_SHA256_KDF:
+ HashLen = SHA256_LENGTH;
+ hashMechanism = CKM_SHA256_KEY_DERIVATION;
+ break;
+ case CKD_SHA384_KDF:
+ HashLen = SHA384_LENGTH;
+ hashMechanism = CKM_SHA384_KEY_DERIVATION;
+ break;
+ case CKD_SHA512_KDF:
+ HashLen = SHA512_LENGTH;
+ hashMechanism = CKM_SHA512_KEY_DERIVATION;
+ break;
+ default:
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ derivedKeySize = keySize;
+ if (derivedKeySize == 0) {
+ keyType = PK11_GetKeyType(target, keySize);
+ derivedKeySize = pk11_GetPredefinedKeyLength(keyType);
+ if (derivedKeySize == 0) {
+ derivedKeySize = HashLen;
+ }
+ }
+
+ /* Check that key_len isn't too long. The maximum key length could be
+ * greatly increased if the code below did not limit the 4-byte counter
+ * to a maximum value of 255. */
+ if (derivedKeySize > 254 * HashLen) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ maxCounter = derivedKeySize / HashLen;
+ if (derivedKeySize > maxCounter * HashLen)
+ maxCounter++;
+
+ if ((sharedData == NULL) || (sharedData->data == NULL))
+ SharedInfoLen = 0;
+ else
+ SharedInfoLen = sharedData->len;
+
+ bufferLen = SharedInfoLen + 4;
+
+ /* Populate buffer with Counter || sharedData
+ * where Counter is 0x00000001. */
+ buffer = (unsigned char *)PORT_Alloc(bufferLen);
+ if (buffer == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
+ buffer[0] = 0;
+ buffer[1] = 0;
+ buffer[2] = 0;
+ buffer[3] = 1;
+ if (SharedInfoLen > 0) {
+ PORT_Memcpy(&buffer[4], sharedData->data, SharedInfoLen);
+ }
+
+ /* Look for a slot that supports the mechanisms needed
+ * to implement the ANSI X9.63 KDF as well as the
+ * target mechanism.
+ */
+ mechanismArray[0] = CKM_CONCATENATE_BASE_AND_DATA;
+ mechanismArray[1] = hashMechanism;
+ mechanismArray[2] = CKM_CONCATENATE_BASE_AND_KEY;
+ mechanismArray[3] = target;
+
+ newSharedSecret = pk11_ForceSlotMultiple(sharedSecret,
+ mechanismArray, 4, operation);
+ if (newSharedSecret != NULL) {
+ sharedSecret = newSharedSecret;
+ }
+
+ for (counter = 1; counter <= maxCounter; counter++) {
+ /* Concatenate shared_secret and buffer */
+ toBeHashed = pk11_ConcatenateBaseAndData(sharedSecret, buffer,
+ bufferLen, hashMechanism, operation);
+ if (toBeHashed == NULL) {
+ goto loser;
+ }
+
+ /* Hash value */
+ if (maxCounter == 1) {
+ /* In this case the length of the key to be derived is
+ * less than or equal to the length of the hash output.
+ * So, the output of the hash operation will be the
+ * dervied key. */
+ hashOutput = pk11_HashKeyDerivation(toBeHashed, hashMechanism,
+ target, operation, keySize);
+ } else {
+ /* In this case, the output of the hash operation will be
+ * concatenated with other data to create the derived key. */
+ hashOutput = pk11_HashKeyDerivation(toBeHashed, hashMechanism,
+ CKM_CONCATENATE_BASE_AND_KEY, operation, 0);
+ }
+ PK11_FreeSymKey(toBeHashed);
+ if (hashOutput == NULL) {
+ goto loser;
+ }
+
+ /* Append result to intermediate result, if necessary */
+ oldIntermediateResult = intermediateResult;
+
+ if (oldIntermediateResult == NULL) {
+ intermediateResult = hashOutput;
+ } else {
+ if (counter == maxCounter) {
+ /* This is the final concatenation, and so the output
+ * will be the derived key. */
+ intermediateResult =
+ pk11_ConcatenateBaseAndKey(oldIntermediateResult,
+ hashOutput, target, operation, keySize);
+ } else {
+ /* The output of this concatenation will be concatenated
+ * with other data to create the derived key. */
+ intermediateResult =
+ pk11_ConcatenateBaseAndKey(oldIntermediateResult,
+ hashOutput, CKM_CONCATENATE_BASE_AND_KEY,
+ operation, 0);
+ }
+
+ PK11_FreeSymKey(hashOutput);
+ PK11_FreeSymKey(oldIntermediateResult);
+ if (intermediateResult == NULL) {
+ goto loser;
+ }
+ }
+
+ /* Increment counter (assumes maxCounter < 255) */
+ buffer[3]++;
+ }
+
+ PORT_ZFree(buffer, bufferLen);
+ if (newSharedSecret != NULL)
+ PK11_FreeSymKey(newSharedSecret);
+ return intermediateResult;
+
+loser:
+ PORT_ZFree(buffer, bufferLen);
+ if (newSharedSecret != NULL)
+ PK11_FreeSymKey(newSharedSecret);
+ if (intermediateResult != NULL)
+ PK11_FreeSymKey(intermediateResult);
+ return NULL;
+}
+
+/*
+ * This regenerate a public key from a private key. This function is currently
+ * NSS private. If we want to make it public, we need to add and optional
+ * template or at least flags (a.la. PK11_DeriveWithFlags).
+ */
+CK_OBJECT_HANDLE
+PK11_DerivePubKeyFromPrivKey(SECKEYPrivateKey *privKey)
+{
+ PK11SlotInfo *slot = privKey->pkcs11Slot;
+ CK_MECHANISM mechanism;
+ CK_OBJECT_HANDLE objectID = CK_INVALID_HANDLE;
+ CK_RV crv;
+
+ mechanism.mechanism = CKM_NSS_PUB_FROM_PRIV;
+ mechanism.pParameter = NULL;
+ mechanism.ulParameterLen = 0;
+
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_DeriveKey(slot->session, &mechanism,
+ privKey->pkcs11ID, NULL, 0,
+ &objectID);
+ PK11_ExitSlotMonitor(slot);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return CK_INVALID_HANDLE;
+ }
+ return objectID;
+}
+
+/*
+ * This Generates a wrapping key based on a privateKey, publicKey, and two
+ * random numbers. For Mail usage RandomB should be NULL. In the Sender's
+ * case RandomA is generate, outherwize it is passed.
+ */
+PK11SymKey *
+PK11_PubDerive(SECKEYPrivateKey *privKey, SECKEYPublicKey *pubKey,
+ PRBool isSender, SECItem *randomA, SECItem *randomB,
+ CK_MECHANISM_TYPE derive, CK_MECHANISM_TYPE target,
+ CK_ATTRIBUTE_TYPE operation, int keySize, void *wincx)
+{
+ PK11SlotInfo *slot = privKey->pkcs11Slot;
+ CK_MECHANISM mechanism;
+ PK11SymKey *symKey;
+ CK_RV crv;
+
+ /* get our key Structure */
+ symKey = pk11_CreateSymKey(slot, target, PR_TRUE, PR_TRUE, wincx);
+ if (symKey == NULL) {
+ return NULL;
+ }
+
+ /* CKA_NSS_MESSAGE is a fake operation to distinguish between
+ * Normal Encrypt/Decrypt and MessageEncrypt/Decrypt. Don't try to set
+ * it as a real attribute */
+ if ((operation & CKA_NSS_MESSAGE_MASK) == CKA_NSS_MESSAGE) {
+ /* Message is or'd with a real Attribute (CKA_ENCRYPT, CKA_DECRYPT),
+ * etc. Strip out the real attribute here */
+ operation &= ~CKA_NSS_MESSAGE_MASK;
+ }
+
+ symKey->origin = PK11_OriginDerive;
+
+ switch (privKey->keyType) {
+ case rsaKey:
+ case rsaPssKey:
+ case rsaOaepKey:
+ case nullKey:
+ PORT_SetError(SEC_ERROR_BAD_KEY);
+ break;
+ case dsaKey:
+ case keaKey:
+ case fortezzaKey: {
+ static unsigned char rb_email[128] = { 0 };
+ CK_KEA_DERIVE_PARAMS param;
+ param.isSender = (CK_BBOOL)isSender;
+ param.ulRandomLen = randomA->len;
+ param.pRandomA = randomA->data;
+ param.pRandomB = rb_email;
+ param.pRandomB[127] = 1;
+ if (randomB)
+ param.pRandomB = randomB->data;
+ if (pubKey->keyType == fortezzaKey) {
+ param.ulPublicDataLen = pubKey->u.fortezza.KEAKey.len;
+ param.pPublicData = pubKey->u.fortezza.KEAKey.data;
+ } else {
+ /* assert type == keaKey */
+ /* XXX change to match key key types */
+ param.ulPublicDataLen = pubKey->u.fortezza.KEAKey.len;
+ param.pPublicData = pubKey->u.fortezza.KEAKey.data;
+ }
+
+ mechanism.mechanism = derive;
+ mechanism.pParameter = &param;
+ mechanism.ulParameterLen = sizeof(param);
+
+ /* get a new symKey structure */
+ pk11_EnterKeyMonitor(symKey);
+ crv = PK11_GETTAB(slot)->C_DeriveKey(symKey->session, &mechanism,
+ privKey->pkcs11ID, NULL, 0,
+ &symKey->objectID);
+ pk11_ExitKeyMonitor(symKey);
+ if (crv == CKR_OK)
+ return symKey;
+ PORT_SetError(PK11_MapError(crv));
+ } break;
+ case dhKey: {
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY;
+ CK_KEY_TYPE keyType = CKK_GENERIC_SECRET;
+ CK_ULONG key_size = 0;
+ CK_ATTRIBUTE keyTemplate[4];
+ int templateCount;
+ CK_ATTRIBUTE *attrs = keyTemplate;
+
+ if (pubKey->keyType != dhKey) {
+ PORT_SetError(SEC_ERROR_BAD_KEY);
+ break;
+ }
+
+ PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof(keyClass));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(keyType));
+ attrs++;
+ PK11_SETATTRS(attrs, operation, &cktrue, 1);
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_VALUE_LEN, &key_size, sizeof(key_size));
+ attrs++;
+ templateCount = attrs - keyTemplate;
+ PR_ASSERT(templateCount <= sizeof(keyTemplate) / sizeof(CK_ATTRIBUTE));
+
+ keyType = PK11_GetKeyType(target, keySize);
+ key_size = keySize;
+ symKey->size = keySize;
+ if (key_size == 0)
+ templateCount--;
+
+ mechanism.mechanism = derive;
+
+ /* we can undefine these when we define diffie-helman keys */
+
+ mechanism.pParameter = pubKey->u.dh.publicValue.data;
+ mechanism.ulParameterLen = pubKey->u.dh.publicValue.len;
+
+ pk11_EnterKeyMonitor(symKey);
+ crv = PK11_GETTAB(slot)->C_DeriveKey(symKey->session, &mechanism,
+ privKey->pkcs11ID, keyTemplate,
+ templateCount, &symKey->objectID);
+ pk11_ExitKeyMonitor(symKey);
+ if (crv == CKR_OK)
+ return symKey;
+ PORT_SetError(PK11_MapError(crv));
+ } break;
+ case ecKey: {
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY;
+ CK_KEY_TYPE keyType = CKK_GENERIC_SECRET;
+ CK_ULONG key_size = 0;
+ CK_ATTRIBUTE keyTemplate[4];
+ int templateCount;
+ CK_ATTRIBUTE *attrs = keyTemplate;
+ CK_ECDH1_DERIVE_PARAMS *mechParams = NULL;
+
+ if (pubKey->keyType != ecKey) {
+ PORT_SetError(SEC_ERROR_BAD_KEY);
+ break;
+ }
+
+ PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof(keyClass));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(keyType));
+ attrs++;
+ PK11_SETATTRS(attrs, operation, &cktrue, 1);
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_VALUE_LEN, &key_size, sizeof(key_size));
+ attrs++;
+ templateCount = attrs - keyTemplate;
+ PR_ASSERT(templateCount <= sizeof(keyTemplate) / sizeof(CK_ATTRIBUTE));
+
+ keyType = PK11_GetKeyType(target, keySize);
+ key_size = keySize;
+ if (key_size == 0) {
+ if ((key_size = pk11_GetPredefinedKeyLength(keyType))) {
+ templateCount--;
+ } else {
+ /* sigh, some tokens can't figure this out and require
+ * CKA_VALUE_LEN to be set */
+ key_size = SHA1_LENGTH;
+ }
+ }
+ symKey->size = key_size;
+
+ mechParams = PORT_ZNew(CK_ECDH1_DERIVE_PARAMS);
+ mechParams->kdf = CKD_SHA1_KDF;
+ mechParams->ulSharedDataLen = 0;
+ mechParams->pSharedData = NULL;
+ mechParams->ulPublicDataLen = pubKey->u.ec.publicValue.len;
+ mechParams->pPublicData = pubKey->u.ec.publicValue.data;
+
+ mechanism.mechanism = derive;
+ mechanism.pParameter = mechParams;
+ mechanism.ulParameterLen = sizeof(CK_ECDH1_DERIVE_PARAMS);
+
+ pk11_EnterKeyMonitor(symKey);
+ crv = PK11_GETTAB(slot)->C_DeriveKey(symKey->session,
+ &mechanism, privKey->pkcs11ID, keyTemplate,
+ templateCount, &symKey->objectID);
+ pk11_ExitKeyMonitor(symKey);
+
+ /* old PKCS #11 spec was ambiguous on what needed to be passed,
+ * try this again with and encoded public key */
+ if (crv != CKR_OK && pk11_ECGetPubkeyEncoding(pubKey) != ECPoint_XOnly) {
+ SECItem *pubValue = SEC_ASN1EncodeItem(NULL, NULL,
+ &pubKey->u.ec.publicValue,
+ SEC_ASN1_GET(SEC_OctetStringTemplate));
+ if (pubValue == NULL) {
+ PORT_ZFree(mechParams, sizeof(CK_ECDH1_DERIVE_PARAMS));
+ break;
+ }
+ mechParams->ulPublicDataLen = pubValue->len;
+ mechParams->pPublicData = pubValue->data;
+
+ pk11_EnterKeyMonitor(symKey);
+ crv = PK11_GETTAB(slot)->C_DeriveKey(symKey->session,
+ &mechanism, privKey->pkcs11ID, keyTemplate,
+ templateCount, &symKey->objectID);
+ pk11_ExitKeyMonitor(symKey);
+
+ SECITEM_FreeItem(pubValue, PR_TRUE);
+ }
+
+ PORT_ZFree(mechParams, sizeof(CK_ECDH1_DERIVE_PARAMS));
+
+ if (crv == CKR_OK)
+ return symKey;
+ PORT_SetError(PK11_MapError(crv));
+ }
+ }
+
+ PK11_FreeSymKey(symKey);
+ return NULL;
+}
+
+/* Test for curves that are known to use a special encoding.
+ * Extend this function when additional curves are added. */
+static ECPointEncoding
+pk11_ECGetPubkeyEncoding(const SECKEYPublicKey *pubKey)
+{
+ SECItem oid;
+ SECStatus rv;
+ PORTCheapArenaPool tmpArena;
+ ECPointEncoding encoding = ECPoint_Undefined;
+
+ PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE);
+
+ /* decode the OID tag */
+ rv = SEC_QuickDERDecodeItem(&tmpArena.arena, &oid,
+ SEC_ASN1_GET(SEC_ObjectIDTemplate),
+ &pubKey->u.ec.DEREncodedParams);
+ if (rv == SECSuccess) {
+ SECOidTag tag = SECOID_FindOIDTag(&oid);
+ switch (tag) {
+ case SEC_OID_CURVE25519:
+ encoding = ECPoint_XOnly;
+ break;
+ case SEC_OID_SECG_EC_SECP256R1:
+ case SEC_OID_SECG_EC_SECP384R1:
+ case SEC_OID_SECG_EC_SECP521R1:
+ default:
+ /* unknown curve, default to uncompressed */
+ encoding = ECPoint_Uncompressed;
+ }
+ }
+ PORT_DestroyCheapArena(&tmpArena);
+ return encoding;
+}
+
+/* Returns the size of the public key, or 0 if there
+ * is an error. */
+static CK_ULONG
+pk11_ECPubKeySize(SECKEYPublicKey *pubKey)
+{
+ SECItem *publicValue = &pubKey->u.ec.publicValue;
+
+ ECPointEncoding encoding = pk11_ECGetPubkeyEncoding(pubKey);
+ if (encoding == ECPoint_XOnly) {
+ return publicValue->len;
+ }
+ if (encoding == ECPoint_Uncompressed) {
+ /* key encoded in uncompressed form */
+ return ((publicValue->len - 1) / 2);
+ }
+ /* key encoding not recognized */
+ return 0;
+}
+
+static PK11SymKey *
+pk11_PubDeriveECKeyWithKDF(
+ SECKEYPrivateKey *privKey, SECKEYPublicKey *pubKey,
+ PRBool isSender, SECItem *randomA, SECItem *randomB,
+ CK_MECHANISM_TYPE derive, CK_MECHANISM_TYPE target,
+ CK_ATTRIBUTE_TYPE operation, int keySize,
+ CK_ULONG kdf, SECItem *sharedData, void *wincx)
+{
+ PK11SlotInfo *slot = privKey->pkcs11Slot;
+ PK11SymKey *symKey;
+ PK11SymKey *SharedSecret;
+ CK_MECHANISM mechanism;
+ CK_RV crv;
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY;
+ CK_KEY_TYPE keyType = CKK_GENERIC_SECRET;
+ CK_ULONG key_size = 0;
+ CK_ATTRIBUTE keyTemplate[4];
+ int templateCount;
+ CK_ATTRIBUTE *attrs = keyTemplate;
+ CK_ECDH1_DERIVE_PARAMS *mechParams = NULL;
+
+ if (pubKey->keyType != ecKey) {
+ PORT_SetError(SEC_ERROR_BAD_KEY);
+ return NULL;
+ }
+ if ((kdf != CKD_NULL) && (kdf != CKD_SHA1_KDF) &&
+ (kdf != CKD_SHA224_KDF) && (kdf != CKD_SHA256_KDF) &&
+ (kdf != CKD_SHA384_KDF) && (kdf != CKD_SHA512_KDF)) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ return NULL;
+ }
+
+ /* get our key Structure */
+ symKey = pk11_CreateSymKey(slot, target, PR_TRUE, PR_TRUE, wincx);
+ if (symKey == NULL) {
+ return NULL;
+ }
+ /* CKA_NSS_MESSAGE is a fake operation to distinguish between
+ * Normal Encrypt/Decrypt and MessageEncrypt/Decrypt. Don't try to set
+ * it as a real attribute */
+ if ((operation & CKA_NSS_MESSAGE_MASK) == CKA_NSS_MESSAGE) {
+ /* Message is or'd with a real Attribute (CKA_ENCRYPT, CKA_DECRYPT),
+ * etc. Strip out the real attribute here */
+ operation &= ~CKA_NSS_MESSAGE_MASK;
+ }
+
+ symKey->origin = PK11_OriginDerive;
+
+ PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof(keyClass));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(keyType));
+ attrs++;
+ PK11_SETATTRS(attrs, operation, &cktrue, 1);
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_VALUE_LEN, &key_size, sizeof(key_size));
+ attrs++;
+ templateCount = attrs - keyTemplate;
+ PR_ASSERT(templateCount <= sizeof(keyTemplate) / sizeof(CK_ATTRIBUTE));
+
+ keyType = PK11_GetKeyType(target, keySize);
+ key_size = keySize;
+ if (key_size == 0) {
+ if ((key_size = pk11_GetPredefinedKeyLength(keyType))) {
+ templateCount--;
+ } else {
+ /* sigh, some tokens can't figure this out and require
+ * CKA_VALUE_LEN to be set */
+ switch (kdf) {
+ case CKD_NULL:
+ key_size = pk11_ECPubKeySize(pubKey);
+ if (key_size == 0) {
+ PK11_FreeSymKey(symKey);
+ return NULL;
+ }
+ break;
+ case CKD_SHA1_KDF:
+ key_size = SHA1_LENGTH;
+ break;
+ case CKD_SHA224_KDF:
+ key_size = SHA224_LENGTH;
+ break;
+ case CKD_SHA256_KDF:
+ key_size = SHA256_LENGTH;
+ break;
+ case CKD_SHA384_KDF:
+ key_size = SHA384_LENGTH;
+ break;
+ case CKD_SHA512_KDF:
+ key_size = SHA512_LENGTH;
+ break;
+ default:
+ PORT_AssertNotReached("Invalid CKD");
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ return NULL;
+ }
+ }
+ }
+ symKey->size = key_size;
+
+ mechParams = PORT_ZNew(CK_ECDH1_DERIVE_PARAMS);
+ if (!mechParams) {
+ PK11_FreeSymKey(symKey);
+ return NULL;
+ }
+ mechParams->kdf = kdf;
+ if (sharedData == NULL) {
+ mechParams->ulSharedDataLen = 0;
+ mechParams->pSharedData = NULL;
+ } else {
+ mechParams->ulSharedDataLen = sharedData->len;
+ mechParams->pSharedData = sharedData->data;
+ }
+ mechParams->ulPublicDataLen = pubKey->u.ec.publicValue.len;
+ mechParams->pPublicData = pubKey->u.ec.publicValue.data;
+
+ mechanism.mechanism = derive;
+ mechanism.pParameter = mechParams;
+ mechanism.ulParameterLen = sizeof(CK_ECDH1_DERIVE_PARAMS);
+
+ pk11_EnterKeyMonitor(symKey);
+ crv = PK11_GETTAB(slot)->C_DeriveKey(symKey->session, &mechanism,
+ privKey->pkcs11ID, keyTemplate,
+ templateCount, &symKey->objectID);
+ pk11_ExitKeyMonitor(symKey);
+
+ /* old PKCS #11 spec was ambiguous on what needed to be passed,
+ * try this again with an encoded public key */
+ if (crv != CKR_OK) {
+ /* For curves that only use X as public value and no encoding we don't
+ * have to try again. (Currently only Curve25519) */
+ if (pk11_ECGetPubkeyEncoding(pubKey) == ECPoint_XOnly) {
+ goto loser;
+ }
+ SECItem *pubValue = SEC_ASN1EncodeItem(NULL, NULL,
+ &pubKey->u.ec.publicValue,
+ SEC_ASN1_GET(SEC_OctetStringTemplate));
+ if (pubValue == NULL) {
+ goto loser;
+ }
+ mechParams->ulPublicDataLen = pubValue->len;
+ mechParams->pPublicData = pubValue->data;
+
+ pk11_EnterKeyMonitor(symKey);
+ crv = PK11_GETTAB(slot)->C_DeriveKey(symKey->session,
+ &mechanism, privKey->pkcs11ID, keyTemplate,
+ templateCount, &symKey->objectID);
+ pk11_ExitKeyMonitor(symKey);
+
+ if ((crv != CKR_OK) && (kdf != CKD_NULL)) {
+ /* Some PKCS #11 libraries cannot perform the key derivation
+ * function. So, try calling C_DeriveKey with CKD_NULL and then
+ * performing the KDF separately.
+ */
+ CK_ULONG derivedKeySize = key_size;
+
+ keyType = CKK_GENERIC_SECRET;
+ key_size = pk11_ECPubKeySize(pubKey);
+ if (key_size == 0) {
+ SECITEM_FreeItem(pubValue, PR_TRUE);
+ goto loser;
+ }
+ SharedSecret = symKey;
+ SharedSecret->size = key_size;
+
+ mechParams->kdf = CKD_NULL;
+ mechParams->ulSharedDataLen = 0;
+ mechParams->pSharedData = NULL;
+ mechParams->ulPublicDataLen = pubKey->u.ec.publicValue.len;
+ mechParams->pPublicData = pubKey->u.ec.publicValue.data;
+
+ pk11_EnterKeyMonitor(SharedSecret);
+ crv = PK11_GETTAB(slot)->C_DeriveKey(SharedSecret->session,
+ &mechanism, privKey->pkcs11ID, keyTemplate,
+ templateCount, &SharedSecret->objectID);
+ pk11_ExitKeyMonitor(SharedSecret);
+
+ if (crv != CKR_OK) {
+ /* old PKCS #11 spec was ambiguous on what needed to be passed,
+ * try this one final time with an encoded public key */
+ mechParams->ulPublicDataLen = pubValue->len;
+ mechParams->pPublicData = pubValue->data;
+
+ pk11_EnterKeyMonitor(SharedSecret);
+ crv = PK11_GETTAB(slot)->C_DeriveKey(SharedSecret->session,
+ &mechanism, privKey->pkcs11ID, keyTemplate,
+ templateCount, &SharedSecret->objectID);
+ pk11_ExitKeyMonitor(SharedSecret);
+ }
+
+ /* Perform KDF. */
+ if (crv == CKR_OK) {
+ symKey = pk11_ANSIX963Derive(SharedSecret, kdf,
+ sharedData, target, operation,
+ derivedKeySize);
+ PK11_FreeSymKey(SharedSecret);
+ if (symKey == NULL) {
+ SECITEM_FreeItem(pubValue, PR_TRUE);
+ PORT_ZFree(mechParams, sizeof(CK_ECDH1_DERIVE_PARAMS));
+ return NULL;
+ }
+ }
+ }
+ SECITEM_FreeItem(pubValue, PR_TRUE);
+ }
+
+loser:
+ PORT_ZFree(mechParams, sizeof(CK_ECDH1_DERIVE_PARAMS));
+
+ if (crv != CKR_OK) {
+ PK11_FreeSymKey(symKey);
+ symKey = NULL;
+ PORT_SetError(PK11_MapError(crv));
+ }
+ return symKey;
+}
+
+PK11SymKey *
+PK11_PubDeriveWithKDF(SECKEYPrivateKey *privKey, SECKEYPublicKey *pubKey,
+ PRBool isSender, SECItem *randomA, SECItem *randomB,
+ CK_MECHANISM_TYPE derive, CK_MECHANISM_TYPE target,
+ CK_ATTRIBUTE_TYPE operation, int keySize,
+ CK_ULONG kdf, SECItem *sharedData, void *wincx)
+{
+
+ switch (privKey->keyType) {
+ case rsaKey:
+ case nullKey:
+ case dsaKey:
+ case keaKey:
+ case fortezzaKey:
+ case dhKey:
+ return PK11_PubDerive(privKey, pubKey, isSender, randomA, randomB,
+ derive, target, operation, keySize, wincx);
+ case ecKey:
+ return pk11_PubDeriveECKeyWithKDF(privKey, pubKey, isSender,
+ randomA, randomB, derive, target,
+ operation, keySize,
+ kdf, sharedData, wincx);
+ default:
+ PORT_SetError(SEC_ERROR_BAD_KEY);
+ break;
+ }
+
+ return NULL;
+}
+
+/*
+ * this little function uses the Decrypt function to unwrap a key, just in
+ * case we are having problem with unwrap. NOTE: The key size may
+ * not be preserved properly for some algorithms!
+ */
+static PK11SymKey *
+pk11_HandUnwrap(PK11SlotInfo *slot, CK_OBJECT_HANDLE wrappingKey,
+ CK_MECHANISM *mech, SECItem *inKey, CK_MECHANISM_TYPE target,
+ CK_ATTRIBUTE *keyTemplate, unsigned int templateCount,
+ int key_size, void *wincx, CK_RV *crvp, PRBool isPerm)
+{
+ CK_ULONG len;
+ SECItem outKey;
+ PK11SymKey *symKey;
+ CK_RV crv;
+ PRBool owner = PR_TRUE;
+ CK_SESSION_HANDLE session;
+
+ /* remove any VALUE_LEN parameters */
+ if (keyTemplate[templateCount - 1].type == CKA_VALUE_LEN) {
+ templateCount--;
+ }
+
+ /* keys are almost always aligned, but if we get this far,
+ * we've gone above and beyond anyway... */
+ outKey.data = (unsigned char *)PORT_Alloc(inKey->len);
+ if (outKey.data == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ if (crvp)
+ *crvp = CKR_HOST_MEMORY;
+ return NULL;
+ }
+ len = inKey->len;
+
+ /* use NULL IV's for wrapping */
+ session = pk11_GetNewSession(slot, &owner);
+ if (!owner || !(slot->isThreadSafe))
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_DecryptInit(session, mech, wrappingKey);
+ if (crv != CKR_OK) {
+ if (!owner || !(slot->isThreadSafe))
+ PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot, session, owner);
+ PORT_Free(outKey.data);
+ PORT_SetError(PK11_MapError(crv));
+ if (crvp)
+ *crvp = crv;
+ return NULL;
+ }
+ crv = PK11_GETTAB(slot)->C_Decrypt(session, inKey->data, inKey->len,
+ outKey.data, &len);
+ if (!owner || !(slot->isThreadSafe))
+ PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot, session, owner);
+ if (crv != CKR_OK) {
+ PORT_Free(outKey.data);
+ PORT_SetError(PK11_MapError(crv));
+ if (crvp)
+ *crvp = crv;
+ return NULL;
+ }
+
+ outKey.len = (key_size == 0) ? len : key_size;
+ outKey.type = siBuffer;
+
+ if (PK11_DoesMechanism(slot, target)) {
+ symKey = pk11_ImportSymKeyWithTempl(slot, target, PK11_OriginUnwrap,
+ isPerm, keyTemplate,
+ templateCount, &outKey, wincx);
+ } else {
+ slot = PK11_GetBestSlot(target, wincx);
+ if (slot == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MODULE);
+ PORT_Free(outKey.data);
+ if (crvp)
+ *crvp = CKR_DEVICE_ERROR;
+ return NULL;
+ }
+ symKey = pk11_ImportSymKeyWithTempl(slot, target, PK11_OriginUnwrap,
+ isPerm, keyTemplate,
+ templateCount, &outKey, wincx);
+ PK11_FreeSlot(slot);
+ }
+ PORT_Free(outKey.data);
+
+ if (crvp)
+ *crvp = symKey ? CKR_OK : CKR_DEVICE_ERROR;
+ return symKey;
+}
+
+/*
+ * The wrap/unwrap function is pretty much the same for private and
+ * public keys. It's just getting the Object ID and slot right. This is
+ * the combined unwrap function.
+ */
+static PK11SymKey *
+pk11_AnyUnwrapKey(PK11SlotInfo *slot, CK_OBJECT_HANDLE wrappingKey,
+ CK_MECHANISM_TYPE wrapType, SECItem *param, SECItem *wrappedKey,
+ CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation, int keySize,
+ void *wincx, CK_ATTRIBUTE *userAttr, unsigned int numAttrs, PRBool isPerm)
+{
+ PK11SymKey *symKey;
+ SECItem *param_free = NULL;
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY;
+ CK_KEY_TYPE keyType = CKK_GENERIC_SECRET;
+ CK_ULONG valueLen = 0;
+ CK_MECHANISM mechanism;
+ CK_SESSION_HANDLE rwsession;
+ CK_RV crv;
+ CK_MECHANISM_INFO mechanism_info;
+#define MAX_ADD_ATTRS 4
+ CK_ATTRIBUTE keyTemplate[MAX_TEMPL_ATTRS + MAX_ADD_ATTRS];
+#undef MAX_ADD_ATTRS
+ CK_ATTRIBUTE *attrs = keyTemplate;
+ unsigned int templateCount;
+
+ if (numAttrs > MAX_TEMPL_ATTRS) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+ /* CKA_NSS_MESSAGE is a fake operation to distinguish between
+ * Normal Encrypt/Decrypt and MessageEncrypt/Decrypt. Don't try to set
+ * it as a real attribute */
+ if ((operation & CKA_NSS_MESSAGE_MASK) == CKA_NSS_MESSAGE) {
+ /* Message is or'd with a real Attribute (CKA_ENCRYPT, CKA_DECRYPT),
+ * etc. Strip out the real attribute here */
+ operation &= ~CKA_NSS_MESSAGE_MASK;
+ }
+
+ /* first copy caller attributes in. */
+ for (templateCount = 0; templateCount < numAttrs; ++templateCount) {
+ *attrs++ = *userAttr++;
+ }
+
+ /* We only add the following attributes to the template if the caller
+ ** didn't already supply them.
+ */
+ if (!pk11_FindAttrInTemplate(keyTemplate, numAttrs, CKA_CLASS)) {
+ PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof keyClass);
+ attrs++;
+ }
+ if (!pk11_FindAttrInTemplate(keyTemplate, numAttrs, CKA_KEY_TYPE)) {
+ keyType = PK11_GetKeyType(target, keySize);
+ PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof keyType);
+ attrs++;
+ }
+ if ((operation != CKA_FLAGS_ONLY) &&
+ !pk11_FindAttrInTemplate(keyTemplate, numAttrs, operation)) {
+ PK11_SETATTRS(attrs, operation, &cktrue, 1);
+ attrs++;
+ }
+
+ /*
+ * must be last in case we need to use this template to import the key
+ */
+ if (keySize > 0 &&
+ !pk11_FindAttrInTemplate(keyTemplate, numAttrs, CKA_VALUE_LEN)) {
+ valueLen = (CK_ULONG)keySize;
+ PK11_SETATTRS(attrs, CKA_VALUE_LEN, &valueLen, sizeof valueLen);
+ attrs++;
+ }
+
+ templateCount = attrs - keyTemplate;
+ PR_ASSERT(templateCount <= sizeof(keyTemplate) / sizeof(CK_ATTRIBUTE));
+
+ /* find out if we can do wrap directly. Because the RSA case if *very*
+ * common, cache the results for it. */
+ if ((wrapType == CKM_RSA_PKCS) && (slot->hasRSAInfo)) {
+ mechanism_info.flags = slot->RSAInfoFlags;
+ } else {
+ if (!slot->isThreadSafe)
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_GetMechanismInfo(slot->slotID, wrapType,
+ &mechanism_info);
+ if (!slot->isThreadSafe)
+ PK11_ExitSlotMonitor(slot);
+ if (crv != CKR_OK) {
+ mechanism_info.flags = 0;
+ }
+ if (wrapType == CKM_RSA_PKCS) {
+ slot->RSAInfoFlags = mechanism_info.flags;
+ slot->hasRSAInfo = PR_TRUE;
+ }
+ }
+
+ /* initialize the mechanism structure */
+ mechanism.mechanism = wrapType;
+ /* use NULL IV's for wrapping */
+ if (param == NULL)
+ param = param_free = PK11_ParamFromIV(wrapType, NULL);
+ if (param) {
+ mechanism.pParameter = param->data;
+ mechanism.ulParameterLen = param->len;
+ } else {
+ mechanism.pParameter = NULL;
+ mechanism.ulParameterLen = 0;
+ }
+
+ if ((mechanism_info.flags & CKF_DECRYPT) && !PK11_DoesMechanism(slot, target)) {
+ symKey = pk11_HandUnwrap(slot, wrappingKey, &mechanism, wrappedKey,
+ target, keyTemplate, templateCount, keySize,
+ wincx, &crv, isPerm);
+ if (symKey) {
+ if (param_free)
+ SECITEM_FreeItem(param_free, PR_TRUE);
+ return symKey;
+ }
+ /*
+ * if the RSA OP simply failed, don't try to unwrap again
+ * with this module.
+ */
+ if (crv == CKR_DEVICE_ERROR) {
+ if (param_free)
+ SECITEM_FreeItem(param_free, PR_TRUE);
+ return NULL;
+ }
+ /* fall through, maybe they incorrectly set CKF_DECRYPT */
+ }
+
+ /* get our key Structure */
+ symKey = pk11_CreateSymKey(slot, target, !isPerm, PR_TRUE, wincx);
+ if (symKey == NULL) {
+ if (param_free)
+ SECITEM_FreeItem(param_free, PR_TRUE);
+ return NULL;
+ }
+
+ symKey->size = keySize;
+ symKey->origin = PK11_OriginUnwrap;
+
+ if (isPerm) {
+ rwsession = PK11_GetRWSession(slot);
+ } else {
+ pk11_EnterKeyMonitor(symKey);
+ rwsession = symKey->session;
+ }
+ PORT_Assert(rwsession != CK_INVALID_HANDLE);
+ if (rwsession == CK_INVALID_HANDLE)
+ crv = CKR_SESSION_HANDLE_INVALID;
+ else
+ crv = PK11_GETTAB(slot)->C_UnwrapKey(rwsession, &mechanism, wrappingKey,
+ wrappedKey->data, wrappedKey->len,
+ keyTemplate, templateCount,
+ &symKey->objectID);
+ if (isPerm) {
+ if (rwsession != CK_INVALID_HANDLE)
+ PK11_RestoreROSession(slot, rwsession);
+ } else {
+ pk11_ExitKeyMonitor(symKey);
+ }
+ if (param_free)
+ SECITEM_FreeItem(param_free, PR_TRUE);
+ if (crv != CKR_OK) {
+ PK11_FreeSymKey(symKey);
+ symKey = NULL;
+ if (crv != CKR_DEVICE_ERROR) {
+ /* try hand Unwrapping */
+ symKey = pk11_HandUnwrap(slot, wrappingKey, &mechanism, wrappedKey,
+ target, keyTemplate, templateCount,
+ keySize, wincx, NULL, isPerm);
+ }
+ }
+
+ return symKey;
+}
+
+/* use a symetric key to unwrap another symetric key */
+PK11SymKey *
+PK11_UnwrapSymKey(PK11SymKey *wrappingKey, CK_MECHANISM_TYPE wrapType,
+ SECItem *param, SECItem *wrappedKey,
+ CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation,
+ int keySize)
+{
+ return pk11_AnyUnwrapKey(wrappingKey->slot, wrappingKey->objectID,
+ wrapType, param, wrappedKey, target, operation, keySize,
+ wrappingKey->cx, NULL, 0, PR_FALSE);
+}
+
+/* use a symetric key to unwrap another symetric key */
+PK11SymKey *
+PK11_UnwrapSymKeyWithFlags(PK11SymKey *wrappingKey, CK_MECHANISM_TYPE wrapType,
+ SECItem *param, SECItem *wrappedKey,
+ CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation,
+ int keySize, CK_FLAGS flags)
+{
+ CK_BBOOL ckTrue = CK_TRUE;
+ CK_ATTRIBUTE keyTemplate[MAX_TEMPL_ATTRS];
+ unsigned int templateCount;
+
+ templateCount = pk11_OpFlagsToAttributes(flags, keyTemplate, &ckTrue);
+ return pk11_AnyUnwrapKey(wrappingKey->slot, wrappingKey->objectID,
+ wrapType, param, wrappedKey, target, operation, keySize,
+ wrappingKey->cx, keyTemplate, templateCount, PR_FALSE);
+}
+
+PK11SymKey *
+PK11_UnwrapSymKeyWithFlagsPerm(PK11SymKey *wrappingKey,
+ CK_MECHANISM_TYPE wrapType,
+ SECItem *param, SECItem *wrappedKey,
+ CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation,
+ int keySize, CK_FLAGS flags, PRBool isPerm)
+{
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_ATTRIBUTE keyTemplate[MAX_TEMPL_ATTRS];
+ CK_ATTRIBUTE *attrs;
+ unsigned int templateCount;
+
+ attrs = keyTemplate;
+ if (isPerm) {
+ PK11_SETATTRS(attrs, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL));
+ attrs++;
+ }
+ templateCount = attrs - keyTemplate;
+ templateCount += pk11_OpFlagsToAttributes(flags, attrs, &cktrue);
+
+ return pk11_AnyUnwrapKey(wrappingKey->slot, wrappingKey->objectID,
+ wrapType, param, wrappedKey, target, operation, keySize,
+ wrappingKey->cx, keyTemplate, templateCount, isPerm);
+}
+
+/* unwrap a symmetric key with a private key. Only supports CKM_RSA_PKCS. */
+PK11SymKey *
+PK11_PubUnwrapSymKey(SECKEYPrivateKey *wrappingKey, SECItem *wrappedKey,
+ CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation, int keySize)
+{
+ CK_MECHANISM_TYPE wrapType = pk11_mapWrapKeyType(wrappingKey->keyType);
+
+ return PK11_PubUnwrapSymKeyWithMechanism(wrappingKey, wrapType, NULL,
+ wrappedKey, target, operation,
+ keySize);
+}
+
+/* unwrap a symmetric key with a private key with the given parameters. */
+PK11SymKey *
+PK11_PubUnwrapSymKeyWithMechanism(SECKEYPrivateKey *wrappingKey,
+ CK_MECHANISM_TYPE mechType, SECItem *param,
+ SECItem *wrappedKey, CK_MECHANISM_TYPE target,
+ CK_ATTRIBUTE_TYPE operation, int keySize)
+{
+ PK11SlotInfo *slot = wrappingKey->pkcs11Slot;
+
+ if (SECKEY_HAS_ATTRIBUTE_SET(wrappingKey, CKA_PRIVATE)) {
+ PK11_HandlePasswordCheck(slot, wrappingKey->wincx);
+ }
+
+ return pk11_AnyUnwrapKey(slot, wrappingKey->pkcs11ID, mechType, param,
+ wrappedKey, target, operation, keySize,
+ wrappingKey->wincx, NULL, 0, PR_FALSE);
+}
+
+/* unwrap a symetric key with a private key. */
+PK11SymKey *
+PK11_PubUnwrapSymKeyWithFlags(SECKEYPrivateKey *wrappingKey,
+ SECItem *wrappedKey, CK_MECHANISM_TYPE target,
+ CK_ATTRIBUTE_TYPE operation, int keySize, CK_FLAGS flags)
+{
+ CK_MECHANISM_TYPE wrapType = pk11_mapWrapKeyType(wrappingKey->keyType);
+ CK_BBOOL ckTrue = CK_TRUE;
+ CK_ATTRIBUTE keyTemplate[MAX_TEMPL_ATTRS];
+ unsigned int templateCount;
+ PK11SlotInfo *slot = wrappingKey->pkcs11Slot;
+
+ templateCount = pk11_OpFlagsToAttributes(flags, keyTemplate, &ckTrue);
+
+ if (SECKEY_HAS_ATTRIBUTE_SET(wrappingKey, CKA_PRIVATE)) {
+ PK11_HandlePasswordCheck(slot, wrappingKey->wincx);
+ }
+
+ return pk11_AnyUnwrapKey(slot, wrappingKey->pkcs11ID,
+ wrapType, NULL, wrappedKey, target, operation, keySize,
+ wrappingKey->wincx, keyTemplate, templateCount, PR_FALSE);
+}
+
+PK11SymKey *
+PK11_PubUnwrapSymKeyWithFlagsPerm(SECKEYPrivateKey *wrappingKey,
+ SECItem *wrappedKey, CK_MECHANISM_TYPE target,
+ CK_ATTRIBUTE_TYPE operation, int keySize,
+ CK_FLAGS flags, PRBool isPerm)
+{
+ CK_MECHANISM_TYPE wrapType = pk11_mapWrapKeyType(wrappingKey->keyType);
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_ATTRIBUTE keyTemplate[MAX_TEMPL_ATTRS];
+ CK_ATTRIBUTE *attrs;
+ unsigned int templateCount;
+ PK11SlotInfo *slot = wrappingKey->pkcs11Slot;
+
+ attrs = keyTemplate;
+ if (isPerm) {
+ PK11_SETATTRS(attrs, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL));
+ attrs++;
+ }
+ templateCount = attrs - keyTemplate;
+
+ templateCount += pk11_OpFlagsToAttributes(flags, attrs, &cktrue);
+
+ if (SECKEY_HAS_ATTRIBUTE_SET(wrappingKey, CKA_PRIVATE)) {
+ PK11_HandlePasswordCheck(slot, wrappingKey->wincx);
+ }
+
+ return pk11_AnyUnwrapKey(slot, wrappingKey->pkcs11ID,
+ wrapType, NULL, wrappedKey, target, operation, keySize,
+ wrappingKey->wincx, keyTemplate, templateCount, isPerm);
+}
+
+PK11SymKey *
+PK11_CopySymKeyForSigning(PK11SymKey *originalKey, CK_MECHANISM_TYPE mech)
+{
+ CK_RV crv;
+ CK_ATTRIBUTE setTemplate;
+ CK_BBOOL ckTrue = CK_TRUE;
+ PK11SlotInfo *slot = originalKey->slot;
+
+ /* first just try to set this key up for signing */
+ PK11_SETATTRS(&setTemplate, CKA_SIGN, &ckTrue, sizeof(ckTrue));
+ pk11_EnterKeyMonitor(originalKey);
+ crv = PK11_GETTAB(slot)->C_SetAttributeValue(originalKey->session,
+ originalKey->objectID, &setTemplate, 1);
+ pk11_ExitKeyMonitor(originalKey);
+ if (crv == CKR_OK) {
+ return PK11_ReferenceSymKey(originalKey);
+ }
+
+ /* nope, doesn't like it, use the pk11 copy object command */
+ return pk11_CopyToSlot(slot, mech, CKA_SIGN, originalKey);
+}
+
+void
+PK11_SetFortezzaHack(PK11SymKey *symKey)
+{
+ symKey->origin = PK11_OriginFortezzaHack;
+}
+
+/*
+ * This is required to allow FORTEZZA_NULL and FORTEZZA_RC4
+ * working. This function simply gets a valid IV for the keys.
+ */
+SECStatus
+PK11_GenerateFortezzaIV(PK11SymKey *symKey, unsigned char *iv, int len)
+{
+ CK_MECHANISM mech_info;
+ CK_ULONG count = 0;
+ CK_RV crv;
+ SECStatus rv = SECFailure;
+
+ mech_info.mechanism = CKM_SKIPJACK_CBC64;
+ mech_info.pParameter = iv;
+ mech_info.ulParameterLen = len;
+
+ /* generate the IV for fortezza */
+ PK11_EnterSlotMonitor(symKey->slot);
+ crv = PK11_GETTAB(symKey->slot)->C_EncryptInit(symKey->slot->session, &mech_info, symKey->objectID);
+ if (crv == CKR_OK) {
+ PK11_GETTAB(symKey->slot)->C_EncryptFinal(symKey->slot->session, NULL, &count);
+ rv = SECSuccess;
+ }
+ PK11_ExitSlotMonitor(symKey->slot);
+ return rv;
+}
+
+CK_OBJECT_HANDLE
+PK11_GetSymKeyHandle(PK11SymKey *symKey)
+{
+ return symKey->objectID;
+}
diff --git a/security/nss/lib/pk11wrap/pk11slot.c b/security/nss/lib/pk11wrap/pk11slot.c
new file mode 100644
index 0000000000..0bd8c8d1c8
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11slot.c
@@ -0,0 +1,2787 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/*
+ * Deal with PKCS #11 Slots.
+ */
+
+#include <stddef.h>
+
+#include "seccomon.h"
+#include "secmod.h"
+#include "nssilock.h"
+#include "secmodi.h"
+#include "secmodti.h"
+#include "pkcs11t.h"
+#include "pk11func.h"
+#include "secitem.h"
+#include "secerr.h"
+
+#include "dev.h"
+#include "dev3hack.h"
+#include "pkim.h"
+#include "utilpars.h"
+#include "pkcs11uri.h"
+
+/*************************************************************
+ * local static and global data
+ *************************************************************/
+
+/*
+ * This array helps parsing between names, mechanisms, and flags.
+ * to make the config files understand more entries, add them
+ * to this table.
+ */
+const PK11DefaultArrayEntry PK11_DefaultArray[] = {
+ { "RSA", SECMOD_RSA_FLAG, CKM_RSA_PKCS },
+ { "DSA", SECMOD_DSA_FLAG, CKM_DSA },
+ { "ECC", SECMOD_ECC_FLAG, CKM_ECDSA },
+ { "DH", SECMOD_DH_FLAG, CKM_DH_PKCS_DERIVE },
+ { "RC2", SECMOD_RC2_FLAG, CKM_RC2_CBC },
+ { "RC4", SECMOD_RC4_FLAG, CKM_RC4 },
+ { "DES", SECMOD_DES_FLAG, CKM_DES_CBC },
+ { "AES", SECMOD_AES_FLAG, CKM_AES_CBC },
+ { "Camellia", SECMOD_CAMELLIA_FLAG, CKM_CAMELLIA_CBC },
+ { "SEED", SECMOD_SEED_FLAG, CKM_SEED_CBC },
+ { "RC5", SECMOD_RC5_FLAG, CKM_RC5_CBC },
+ { "SHA-1", SECMOD_SHA1_FLAG, CKM_SHA_1 },
+ /* { "SHA224", SECMOD_SHA256_FLAG, CKM_SHA224 }, */
+ { "SHA256", SECMOD_SHA256_FLAG, CKM_SHA256 },
+ /* { "SHA384", SECMOD_SHA512_FLAG, CKM_SHA384 }, */
+ { "SHA512", SECMOD_SHA512_FLAG, CKM_SHA512 },
+ { "MD5", SECMOD_MD5_FLAG, CKM_MD5 },
+ { "MD2", SECMOD_MD2_FLAG, CKM_MD2 },
+ { "SSL", SECMOD_SSL_FLAG, CKM_SSL3_PRE_MASTER_KEY_GEN },
+ { "TLS", SECMOD_TLS_FLAG, CKM_TLS_MASTER_KEY_DERIVE },
+ { "SKIPJACK", SECMOD_FORTEZZA_FLAG, CKM_SKIPJACK_CBC64 },
+ { "Publicly-readable certs", SECMOD_FRIENDLY_FLAG, CKM_INVALID_MECHANISM },
+ { "Random Num Generator", SECMOD_RANDOM_FLAG, CKM_FAKE_RANDOM },
+};
+const int num_pk11_default_mechanisms =
+ sizeof(PK11_DefaultArray) / sizeof(PK11_DefaultArray[0]);
+
+const PK11DefaultArrayEntry *
+PK11_GetDefaultArray(int *size)
+{
+ if (size) {
+ *size = num_pk11_default_mechanisms;
+ }
+ return PK11_DefaultArray;
+}
+
+/*
+ * These slotlists are lists of modules which provide default support for
+ * a given algorithm or mechanism.
+ */
+static PK11SlotList
+ pk11_seedSlotList,
+ pk11_camelliaSlotList,
+ pk11_aesSlotList,
+ pk11_desSlotList,
+ pk11_rc4SlotList,
+ pk11_rc2SlotList,
+ pk11_rc5SlotList,
+ pk11_sha1SlotList,
+ pk11_md5SlotList,
+ pk11_md2SlotList,
+ pk11_rsaSlotList,
+ pk11_dsaSlotList,
+ pk11_dhSlotList,
+ pk11_ecSlotList,
+ pk11_ideaSlotList,
+ pk11_sslSlotList,
+ pk11_tlsSlotList,
+ pk11_randomSlotList,
+ pk11_sha256SlotList,
+ pk11_sha512SlotList; /* slots do SHA512 and SHA384 */
+
+/************************************************************
+ * Generic Slot List and Slot List element manipulations
+ ************************************************************/
+
+/*
+ * allocate a new list
+ */
+PK11SlotList *
+PK11_NewSlotList(void)
+{
+ PK11SlotList *list;
+
+ list = (PK11SlotList *)PORT_Alloc(sizeof(PK11SlotList));
+ if (list == NULL)
+ return NULL;
+ list->head = NULL;
+ list->tail = NULL;
+ list->lock = PZ_NewLock(nssILockList);
+ if (list->lock == NULL) {
+ PORT_Free(list);
+ return NULL;
+ }
+
+ return list;
+}
+
+/*
+ * free a list element when all the references go away.
+ */
+SECStatus
+PK11_FreeSlotListElement(PK11SlotList *list, PK11SlotListElement *le)
+{
+ PRBool freeit = PR_FALSE;
+
+ if (list == NULL || le == NULL) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ PZ_Lock(list->lock);
+ if (le->refCount-- == 1) {
+ freeit = PR_TRUE;
+ }
+ PZ_Unlock(list->lock);
+ if (freeit) {
+ PK11_FreeSlot(le->slot);
+ PORT_Free(le);
+ }
+ return SECSuccess;
+}
+
+static void
+pk11_FreeSlotListStatic(PK11SlotList *list)
+{
+ PK11SlotListElement *le, *next;
+ if (list == NULL)
+ return;
+
+ for (le = list->head; le; le = next) {
+ next = le->next;
+ PK11_FreeSlotListElement(list, le);
+ }
+ if (list->lock) {
+ PZ_DestroyLock(list->lock);
+ }
+ list->lock = NULL;
+ list->head = NULL;
+}
+
+/*
+ * if we are freeing the list, we must be the only ones with a pointer
+ * to the list.
+ */
+void
+PK11_FreeSlotList(PK11SlotList *list)
+{
+ pk11_FreeSlotListStatic(list);
+ PORT_Free(list);
+}
+
+/*
+ * add a slot to a list
+ * "slot" is the slot to be added. Ownership is not transferred.
+ * "sorted" indicates whether or not the slot should be inserted according to
+ * cipherOrder of the associated module. PR_FALSE indicates that the slot
+ * should be inserted to the head of the list.
+ */
+SECStatus
+PK11_AddSlotToList(PK11SlotList *list, PK11SlotInfo *slot, PRBool sorted)
+{
+ PK11SlotListElement *le;
+ PK11SlotListElement *element;
+
+ le = (PK11SlotListElement *)PORT_Alloc(sizeof(PK11SlotListElement));
+ if (le == NULL)
+ return SECFailure;
+
+ le->slot = PK11_ReferenceSlot(slot);
+ le->prev = NULL;
+ le->refCount = 1;
+ PZ_Lock(list->lock);
+ element = list->head;
+ /* Insertion sort, with higher cipherOrders are sorted first in the list */
+ while (element && sorted && (element->slot->module->cipherOrder > le->slot->module->cipherOrder)) {
+ element = element->next;
+ }
+ if (element) {
+ le->prev = element->prev;
+ element->prev = le;
+ le->next = element;
+ } else {
+ le->prev = list->tail;
+ le->next = NULL;
+ list->tail = le;
+ }
+ if (le->prev)
+ le->prev->next = le;
+ if (list->head == element)
+ list->head = le;
+ PZ_Unlock(list->lock);
+
+ return SECSuccess;
+}
+
+/*
+ * remove a slot entry from the list
+ */
+SECStatus
+PK11_DeleteSlotFromList(PK11SlotList *list, PK11SlotListElement *le)
+{
+ PZ_Lock(list->lock);
+ if (le->prev)
+ le->prev->next = le->next;
+ else
+ list->head = le->next;
+ if (le->next)
+ le->next->prev = le->prev;
+ else
+ list->tail = le->prev;
+ le->next = le->prev = NULL;
+ PZ_Unlock(list->lock);
+ PK11_FreeSlotListElement(list, le);
+ return SECSuccess;
+}
+
+/*
+ * Move a list to the end of the target list.
+ * NOTE: There is no locking here... This assumes BOTH lists are private copy
+ * lists. It also does not re-sort the target list.
+ */
+SECStatus
+pk11_MoveListToList(PK11SlotList *target, PK11SlotList *src)
+{
+ if (src->head == NULL)
+ return SECSuccess;
+
+ if (target->tail == NULL) {
+ target->head = src->head;
+ } else {
+ target->tail->next = src->head;
+ }
+ src->head->prev = target->tail;
+ target->tail = src->tail;
+ src->head = src->tail = NULL;
+ return SECSuccess;
+}
+
+/*
+ * get an element from the list with a reference. You must own the list.
+ */
+PK11SlotListElement *
+PK11_GetFirstRef(PK11SlotList *list)
+{
+ PK11SlotListElement *le;
+
+ le = list->head;
+ if (le != NULL)
+ (le)->refCount++;
+ return le;
+}
+
+/*
+ * get the next element from the list with a reference. You must own the list.
+ */
+PK11SlotListElement *
+PK11_GetNextRef(PK11SlotList *list, PK11SlotListElement *le, PRBool restart)
+{
+ PK11SlotListElement *new_le;
+ new_le = le->next;
+ if (new_le)
+ new_le->refCount++;
+ PK11_FreeSlotListElement(list, le);
+ return new_le;
+}
+
+/*
+ * get an element safely from the list. This just makes sure that if
+ * this element is not deleted while we deal with it.
+ */
+PK11SlotListElement *
+PK11_GetFirstSafe(PK11SlotList *list)
+{
+ PK11SlotListElement *le;
+
+ PZ_Lock(list->lock);
+ le = list->head;
+ if (le != NULL)
+ (le)->refCount++;
+ PZ_Unlock(list->lock);
+ return le;
+}
+
+/*
+ * NOTE: if this element gets deleted, we can no longer safely traverse using
+ * it's pointers. We can either terminate the loop, or restart from the
+ * beginning. This is controlled by the restart option.
+ */
+PK11SlotListElement *
+PK11_GetNextSafe(PK11SlotList *list, PK11SlotListElement *le, PRBool restart)
+{
+ PK11SlotListElement *new_le;
+ PZ_Lock(list->lock);
+ new_le = le->next;
+ if (le->next == NULL) {
+ /* if the prev and next fields are NULL then either this element
+ * has been removed and we need to walk the list again (if restart
+ * is true) or this was the only element on the list */
+ if ((le->prev == NULL) && restart && (list->head != le)) {
+ new_le = list->head;
+ }
+ }
+ if (new_le)
+ new_le->refCount++;
+ PZ_Unlock(list->lock);
+ PK11_FreeSlotListElement(list, le);
+ return new_le;
+}
+
+/*
+ * Find the element that holds this slot
+ */
+PK11SlotListElement *
+PK11_FindSlotElement(PK11SlotList *list, PK11SlotInfo *slot)
+{
+ PK11SlotListElement *le;
+
+ for (le = PK11_GetFirstSafe(list); le;
+ le = PK11_GetNextSafe(list, le, PR_TRUE)) {
+ if (le->slot == slot)
+ return le;
+ }
+ return NULL;
+}
+
+/************************************************************
+ * Generic Slot Utilities
+ ************************************************************/
+/*
+ * Create a new slot structure
+ */
+PK11SlotInfo *
+PK11_NewSlotInfo(SECMODModule *mod)
+{
+ PK11SlotInfo *slot;
+
+ slot = (PK11SlotInfo *)PORT_Alloc(sizeof(PK11SlotInfo));
+ if (slot == NULL) {
+ return slot;
+ }
+ slot->freeListLock = PZ_NewLock(nssILockFreelist);
+ if (slot->freeListLock == NULL) {
+ PORT_Free(slot);
+ return NULL;
+ }
+ slot->nssTokenLock = PZ_NewLock(nssILockOther);
+ if (slot->nssTokenLock == NULL) {
+ PZ_DestroyLock(slot->freeListLock);
+ PORT_Free(slot);
+ return NULL;
+ }
+ slot->sessionLock = mod->isThreadSafe ? PZ_NewLock(nssILockSession) : mod->refLock;
+ if (slot->sessionLock == NULL) {
+ PZ_DestroyLock(slot->nssTokenLock);
+ PZ_DestroyLock(slot->freeListLock);
+ PORT_Free(slot);
+ return NULL;
+ }
+ slot->freeSymKeysWithSessionHead = NULL;
+ slot->freeSymKeysHead = NULL;
+ slot->keyCount = 0;
+ slot->maxKeyCount = 0;
+ slot->functionList = NULL;
+ slot->needTest = PR_TRUE;
+ slot->isPerm = PR_FALSE;
+ slot->isHW = PR_FALSE;
+ slot->isInternal = PR_FALSE;
+ slot->isThreadSafe = PR_FALSE;
+ slot->disabled = PR_FALSE;
+ slot->series = 1;
+ slot->wrapKey = 0;
+ slot->wrapMechanism = CKM_INVALID_MECHANISM;
+ slot->refKeys[0] = CK_INVALID_HANDLE;
+ slot->reason = PK11_DIS_NONE;
+ slot->readOnly = PR_TRUE;
+ slot->needLogin = PR_FALSE;
+ slot->hasRandom = PR_FALSE;
+ slot->defRWSession = PR_FALSE;
+ slot->protectedAuthPath = PR_FALSE;
+ slot->flags = 0;
+ slot->session = CK_INVALID_HANDLE;
+ slot->slotID = 0;
+ slot->defaultFlags = 0;
+ slot->refCount = 1;
+ slot->askpw = 0;
+ slot->timeout = 0;
+ slot->mechanismList = NULL;
+ slot->mechanismCount = 0;
+ slot->cert_array = NULL;
+ slot->cert_count = 0;
+ slot->slot_name[0] = 0;
+ slot->token_name[0] = 0;
+ PORT_Memset(slot->serial, ' ', sizeof(slot->serial));
+ PORT_Memset(&slot->tokenInfo, 0, sizeof(slot->tokenInfo));
+ slot->module = NULL;
+ slot->authTransact = 0;
+ slot->authTime = LL_ZERO;
+ slot->minPassword = 0;
+ slot->maxPassword = 0;
+ slot->hasRootCerts = PR_FALSE;
+ slot->hasRootTrust = PR_FALSE;
+ slot->nssToken = NULL;
+ slot->profileList = NULL;
+ slot->profileCount = 0;
+ return slot;
+}
+
+/* create a new reference to a slot so it doesn't go away */
+PK11SlotInfo *
+PK11_ReferenceSlot(PK11SlotInfo *slot)
+{
+ PR_ATOMIC_INCREMENT(&slot->refCount);
+ return slot;
+}
+
+/* Destroy all info on a slot we have built up */
+void
+PK11_DestroySlot(PK11SlotInfo *slot)
+{
+ /* free up the cached keys and sessions */
+ PK11_CleanKeyList(slot);
+
+ /* free up all the sessions on this slot */
+ if (slot->functionList) {
+ PK11_GETTAB(slot)
+ ->C_CloseAllSessions(slot->slotID);
+ }
+
+ if (slot->mechanismList) {
+ PORT_Free(slot->mechanismList);
+ }
+ if (slot->profileList) {
+ PORT_Free(slot->profileList);
+ }
+ if (slot->isThreadSafe && slot->sessionLock) {
+ PZ_DestroyLock(slot->sessionLock);
+ }
+ slot->sessionLock = NULL;
+ if (slot->freeListLock) {
+ PZ_DestroyLock(slot->freeListLock);
+ slot->freeListLock = NULL;
+ }
+ if (slot->nssTokenLock) {
+ PZ_DestroyLock(slot->nssTokenLock);
+ slot->nssTokenLock = NULL;
+ }
+
+ /* finally Tell our parent module that we've gone away so it can unload */
+ if (slot->module) {
+ SECMOD_SlotDestroyModule(slot->module, PR_TRUE);
+ }
+
+ /* ok, well not quit finally... now we free the memory */
+ PORT_Free(slot);
+}
+
+/* We're all done with the slot, free it */
+void
+PK11_FreeSlot(PK11SlotInfo *slot)
+{
+ if (PR_ATOMIC_DECREMENT(&slot->refCount) == 0) {
+ PK11_DestroySlot(slot);
+ }
+}
+
+void
+PK11_EnterSlotMonitor(PK11SlotInfo *slot)
+{
+ PZ_Lock(slot->sessionLock);
+}
+
+void
+PK11_ExitSlotMonitor(PK11SlotInfo *slot)
+{
+ PZ_Unlock(slot->sessionLock);
+}
+
+/***********************************************************
+ * Functions to find specific slots.
+ ***********************************************************/
+PRBool
+SECMOD_HasRootCerts(void)
+{
+ SECMODModuleList *mlp;
+ SECMODModuleList *modules;
+ SECMODListLock *moduleLock = SECMOD_GetDefaultModuleListLock();
+ int i;
+ PRBool found = PR_FALSE;
+
+ if (!moduleLock) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return found;
+ }
+
+ /* work through all the slots */
+ SECMOD_GetReadLock(moduleLock);
+ modules = SECMOD_GetDefaultModuleList();
+ for (mlp = modules; mlp != NULL; mlp = mlp->next) {
+ for (i = 0; i < mlp->module->slotCount; i++) {
+ PK11SlotInfo *tmpSlot = mlp->module->slots[i];
+ if (PK11_IsPresent(tmpSlot)) {
+ if (tmpSlot->hasRootCerts) {
+ found = PR_TRUE;
+ break;
+ }
+ }
+ }
+ if (found)
+ break;
+ }
+ SECMOD_ReleaseReadLock(moduleLock);
+
+ return found;
+}
+
+/***********************************************************
+ * Functions to find specific slots.
+ ***********************************************************/
+PK11SlotList *
+PK11_FindSlotsByNames(const char *dllName, const char *slotName,
+ const char *tokenName, PRBool presentOnly)
+{
+ SECMODModuleList *mlp;
+ SECMODModuleList *modules;
+ SECMODListLock *moduleLock = SECMOD_GetDefaultModuleListLock();
+ int i;
+ PK11SlotList *slotList = NULL;
+ PRUint32 slotcount = 0;
+ SECStatus rv = SECSuccess;
+
+ if (!moduleLock) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return slotList;
+ }
+
+ slotList = PK11_NewSlotList();
+ if (!slotList) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return slotList;
+ }
+
+ if (((NULL == dllName) || (0 == *dllName)) &&
+ ((NULL == slotName) || (0 == *slotName)) &&
+ ((NULL == tokenName) || (0 == *tokenName))) {
+ /* default to softoken */
+ /* PK11_GetInternalKeySlot increments the refcount on the internal slot,
+ * but so does PK11_AddSlotToList. To avoid erroneously increasing the
+ * refcount twice, we get our own reference to the internal slot and
+ * decrement its refcount when we're done with it. */
+ PK11SlotInfo *internalKeySlot = PK11_GetInternalKeySlot();
+ PK11_AddSlotToList(slotList, internalKeySlot, PR_TRUE);
+ PK11_FreeSlot(internalKeySlot);
+ return slotList;
+ }
+
+ /* work through all the slots */
+ SECMOD_GetReadLock(moduleLock);
+ modules = SECMOD_GetDefaultModuleList();
+ for (mlp = modules; mlp != NULL; mlp = mlp->next) {
+ PORT_Assert(mlp->module);
+ if (!mlp->module) {
+ rv = SECFailure;
+ break;
+ }
+ if ((!dllName) || (mlp->module->dllName &&
+ (0 == PORT_Strcmp(mlp->module->dllName, dllName)))) {
+ for (i = 0; i < mlp->module->slotCount; i++) {
+ PK11SlotInfo *tmpSlot = (mlp->module->slots ? mlp->module->slots[i] : NULL);
+ PORT_Assert(tmpSlot);
+ if (!tmpSlot) {
+ rv = SECFailure;
+ break;
+ }
+ if ((PR_FALSE == presentOnly || PK11_IsPresent(tmpSlot)) &&
+ ((!tokenName) ||
+ (0 == PORT_Strcmp(tmpSlot->token_name, tokenName))) &&
+ ((!slotName) ||
+ (0 == PORT_Strcmp(tmpSlot->slot_name, slotName)))) {
+ PK11_AddSlotToList(slotList, tmpSlot, PR_TRUE);
+ slotcount++;
+ }
+ }
+ }
+ }
+ SECMOD_ReleaseReadLock(moduleLock);
+
+ if ((0 == slotcount) || (SECFailure == rv)) {
+ PORT_SetError(SEC_ERROR_NO_TOKEN);
+ PK11_FreeSlotList(slotList);
+ slotList = NULL;
+ }
+
+ if (SECFailure == rv) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ }
+
+ return slotList;
+}
+
+typedef PRBool (*PK11SlotMatchFunc)(PK11SlotInfo *slot, const void *arg);
+
+static PRBool
+pk11_MatchSlotByTokenName(PK11SlotInfo *slot, const void *arg)
+{
+ return PORT_Strcmp(slot->token_name, arg) == 0;
+}
+
+static PRBool
+pk11_MatchSlotBySerial(PK11SlotInfo *slot, const void *arg)
+{
+ return PORT_Memcmp(slot->serial, arg, sizeof(slot->serial)) == 0;
+}
+
+static PRBool
+pk11_MatchSlotByTokenURI(PK11SlotInfo *slot, const void *arg)
+{
+ return pk11_MatchUriTokenInfo(slot, (PK11URI *)arg);
+}
+
+static PK11SlotInfo *
+pk11_FindSlot(const void *arg, PK11SlotMatchFunc func)
+{
+ SECMODListLock *moduleLock = SECMOD_GetDefaultModuleListLock();
+ SECMODModuleList *mlp;
+ SECMODModuleList *modules;
+ int i;
+ PK11SlotInfo *slot = NULL;
+
+ if (!moduleLock) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return slot;
+ }
+ /* work through all the slots */
+ SECMOD_GetReadLock(moduleLock);
+ modules = SECMOD_GetDefaultModuleList();
+ for (mlp = modules; mlp != NULL; mlp = mlp->next) {
+ for (i = 0; i < mlp->module->slotCount; i++) {
+ PK11SlotInfo *tmpSlot = mlp->module->slots[i];
+ if (PK11_IsPresent(tmpSlot)) {
+ if (func(tmpSlot, arg)) {
+ slot = PK11_ReferenceSlot(tmpSlot);
+ break;
+ }
+ }
+ }
+ if (slot != NULL)
+ break;
+ }
+ SECMOD_ReleaseReadLock(moduleLock);
+
+ if (slot == NULL) {
+ PORT_SetError(SEC_ERROR_NO_TOKEN);
+ }
+
+ return slot;
+}
+
+static PK11SlotInfo *
+pk11_FindSlotByTokenURI(const char *uriString)
+{
+ PK11SlotInfo *slot = NULL;
+ PK11URI *uri;
+
+ uri = PK11URI_ParseURI(uriString);
+ if (!uri) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return slot;
+ }
+
+ slot = pk11_FindSlot(uri, pk11_MatchSlotByTokenURI);
+ PK11URI_DestroyURI(uri);
+ return slot;
+}
+
+PK11SlotInfo *
+PK11_FindSlotByName(const char *name)
+{
+ if ((name == NULL) || (*name == 0)) {
+ return PK11_GetInternalKeySlot();
+ }
+
+ if (!PORT_Strncasecmp(name, "pkcs11:", strlen("pkcs11:"))) {
+ return pk11_FindSlotByTokenURI(name);
+ }
+
+ return pk11_FindSlot(name, pk11_MatchSlotByTokenName);
+}
+
+PK11SlotInfo *
+PK11_FindSlotBySerial(char *serial)
+{
+ return pk11_FindSlot(serial, pk11_MatchSlotBySerial);
+}
+
+/*
+ * notification stub. If we ever get interested in any events that
+ * the pkcs11 functions may pass back to use, we can catch them here...
+ * currently pdata is a slotinfo structure.
+ */
+CK_RV
+pk11_notify(CK_SESSION_HANDLE session, CK_NOTIFICATION event,
+ CK_VOID_PTR pdata)
+{
+ return CKR_OK;
+}
+
+/*
+ * grab a new RW session
+ * !!! has a side effect of grabbing the Monitor if either the slot's default
+ * session is RW or the slot is not thread safe. Monitor is release in function
+ * below
+ */
+CK_SESSION_HANDLE
+PK11_GetRWSession(PK11SlotInfo *slot)
+{
+ CK_SESSION_HANDLE rwsession;
+ CK_RV crv;
+ PRBool haveMonitor = PR_FALSE;
+
+ if (!slot->isThreadSafe || slot->defRWSession) {
+ PK11_EnterSlotMonitor(slot);
+ haveMonitor = PR_TRUE;
+ }
+ if (slot->defRWSession) {
+ PORT_Assert(slot->session != CK_INVALID_HANDLE);
+ if (slot->session != CK_INVALID_HANDLE)
+ return slot->session;
+ }
+
+ crv = PK11_GETTAB(slot)->C_OpenSession(slot->slotID,
+ CKF_RW_SESSION | CKF_SERIAL_SESSION,
+ slot, pk11_notify, &rwsession);
+ PORT_Assert(rwsession != CK_INVALID_HANDLE || crv != CKR_OK);
+ if (crv != CKR_OK || rwsession == CK_INVALID_HANDLE) {
+ if (crv == CKR_OK)
+ crv = CKR_DEVICE_ERROR;
+ if (haveMonitor)
+ PK11_ExitSlotMonitor(slot);
+ PORT_SetError(PK11_MapError(crv));
+ return CK_INVALID_HANDLE;
+ }
+ if (slot->defRWSession) { /* we have the monitor */
+ slot->session = rwsession;
+ }
+ return rwsession;
+}
+
+PRBool
+PK11_RWSessionHasLock(PK11SlotInfo *slot, CK_SESSION_HANDLE session_handle)
+{
+ PRBool hasLock;
+ hasLock = (PRBool)(!slot->isThreadSafe ||
+ (slot->defRWSession && slot->session != CK_INVALID_HANDLE));
+ return hasLock;
+}
+
+static PRBool
+pk11_RWSessionIsDefault(PK11SlotInfo *slot, CK_SESSION_HANDLE rwsession)
+{
+ PRBool isDefault;
+ isDefault = (PRBool)(slot->session == rwsession &&
+ slot->defRWSession &&
+ slot->session != CK_INVALID_HANDLE);
+ return isDefault;
+}
+
+/*
+ * close the rwsession and restore our readonly session
+ * !!! has a side effect of releasing the Monitor if either the slot's default
+ * session is RW or the slot is not thread safe.
+ */
+void
+PK11_RestoreROSession(PK11SlotInfo *slot, CK_SESSION_HANDLE rwsession)
+{
+ PORT_Assert(rwsession != CK_INVALID_HANDLE);
+ if (rwsession != CK_INVALID_HANDLE) {
+ PRBool doExit = PK11_RWSessionHasLock(slot, rwsession);
+ if (!pk11_RWSessionIsDefault(slot, rwsession))
+ PK11_GETTAB(slot)
+ ->C_CloseSession(rwsession);
+ if (doExit)
+ PK11_ExitSlotMonitor(slot);
+ }
+}
+
+/************************************************************
+ * Manage the built-In Slot Lists
+ ************************************************************/
+
+/* Init the static built int slot list (should actually integrate
+ * with PK11_NewSlotList */
+static void
+pk11_InitSlotListStatic(PK11SlotList *list)
+{
+ list->lock = PZ_NewLock(nssILockList);
+ list->head = NULL;
+}
+
+/* initialize the system slotlists */
+SECStatus
+PK11_InitSlotLists(void)
+{
+ pk11_InitSlotListStatic(&pk11_seedSlotList);
+ pk11_InitSlotListStatic(&pk11_camelliaSlotList);
+ pk11_InitSlotListStatic(&pk11_aesSlotList);
+ pk11_InitSlotListStatic(&pk11_desSlotList);
+ pk11_InitSlotListStatic(&pk11_rc4SlotList);
+ pk11_InitSlotListStatic(&pk11_rc2SlotList);
+ pk11_InitSlotListStatic(&pk11_rc5SlotList);
+ pk11_InitSlotListStatic(&pk11_md5SlotList);
+ pk11_InitSlotListStatic(&pk11_md2SlotList);
+ pk11_InitSlotListStatic(&pk11_sha1SlotList);
+ pk11_InitSlotListStatic(&pk11_rsaSlotList);
+ pk11_InitSlotListStatic(&pk11_dsaSlotList);
+ pk11_InitSlotListStatic(&pk11_dhSlotList);
+ pk11_InitSlotListStatic(&pk11_ecSlotList);
+ pk11_InitSlotListStatic(&pk11_ideaSlotList);
+ pk11_InitSlotListStatic(&pk11_sslSlotList);
+ pk11_InitSlotListStatic(&pk11_tlsSlotList);
+ pk11_InitSlotListStatic(&pk11_randomSlotList);
+ pk11_InitSlotListStatic(&pk11_sha256SlotList);
+ pk11_InitSlotListStatic(&pk11_sha512SlotList);
+ return SECSuccess;
+}
+
+void
+PK11_DestroySlotLists(void)
+{
+ pk11_FreeSlotListStatic(&pk11_seedSlotList);
+ pk11_FreeSlotListStatic(&pk11_camelliaSlotList);
+ pk11_FreeSlotListStatic(&pk11_aesSlotList);
+ pk11_FreeSlotListStatic(&pk11_desSlotList);
+ pk11_FreeSlotListStatic(&pk11_rc4SlotList);
+ pk11_FreeSlotListStatic(&pk11_rc2SlotList);
+ pk11_FreeSlotListStatic(&pk11_rc5SlotList);
+ pk11_FreeSlotListStatic(&pk11_md5SlotList);
+ pk11_FreeSlotListStatic(&pk11_md2SlotList);
+ pk11_FreeSlotListStatic(&pk11_sha1SlotList);
+ pk11_FreeSlotListStatic(&pk11_rsaSlotList);
+ pk11_FreeSlotListStatic(&pk11_dsaSlotList);
+ pk11_FreeSlotListStatic(&pk11_dhSlotList);
+ pk11_FreeSlotListStatic(&pk11_ecSlotList);
+ pk11_FreeSlotListStatic(&pk11_ideaSlotList);
+ pk11_FreeSlotListStatic(&pk11_sslSlotList);
+ pk11_FreeSlotListStatic(&pk11_tlsSlotList);
+ pk11_FreeSlotListStatic(&pk11_randomSlotList);
+ pk11_FreeSlotListStatic(&pk11_sha256SlotList);
+ pk11_FreeSlotListStatic(&pk11_sha512SlotList);
+ return;
+}
+
+/* return a system slot list based on mechanism */
+PK11SlotList *
+PK11_GetSlotList(CK_MECHANISM_TYPE type)
+{
+/* XXX a workaround for Bugzilla bug #55267 */
+#if defined(HPUX) && defined(__LP64__)
+ if (CKM_INVALID_MECHANISM == type)
+ return NULL;
+#endif
+ switch (type) {
+ case CKM_SEED_CBC:
+ case CKM_SEED_ECB:
+ return &pk11_seedSlotList;
+ case CKM_CAMELLIA_CBC:
+ case CKM_CAMELLIA_ECB:
+ return &pk11_camelliaSlotList;
+ case CKM_AES_CBC:
+ case CKM_AES_CCM:
+ case CKM_AES_CTR:
+ case CKM_AES_CTS:
+ case CKM_AES_GCM:
+ case CKM_AES_ECB:
+ return &pk11_aesSlotList;
+ case CKM_DES_CBC:
+ case CKM_DES_ECB:
+ case CKM_DES3_ECB:
+ case CKM_DES3_CBC:
+ return &pk11_desSlotList;
+ case CKM_RC4:
+ return &pk11_rc4SlotList;
+ case CKM_RC5_CBC:
+ return &pk11_rc5SlotList;
+ case CKM_SHA_1:
+ return &pk11_sha1SlotList;
+ case CKM_SHA224:
+ case CKM_SHA256:
+ return &pk11_sha256SlotList;
+ case CKM_SHA384:
+ case CKM_SHA512:
+ return &pk11_sha512SlotList;
+ case CKM_MD5:
+ return &pk11_md5SlotList;
+ case CKM_MD2:
+ return &pk11_md2SlotList;
+ case CKM_RC2_ECB:
+ case CKM_RC2_CBC:
+ return &pk11_rc2SlotList;
+ case CKM_RSA_PKCS:
+ case CKM_RSA_PKCS_KEY_PAIR_GEN:
+ case CKM_RSA_X_509:
+ return &pk11_rsaSlotList;
+ case CKM_DSA:
+ return &pk11_dsaSlotList;
+ case CKM_DH_PKCS_KEY_PAIR_GEN:
+ case CKM_DH_PKCS_DERIVE:
+ return &pk11_dhSlotList;
+ case CKM_ECDSA:
+ case CKM_ECDSA_SHA1:
+ case CKM_EC_KEY_PAIR_GEN: /* aka CKM_ECDSA_KEY_PAIR_GEN */
+ case CKM_ECDH1_DERIVE:
+ return &pk11_ecSlotList;
+ case CKM_SSL3_PRE_MASTER_KEY_GEN:
+ case CKM_SSL3_MASTER_KEY_DERIVE:
+ case CKM_SSL3_SHA1_MAC:
+ case CKM_SSL3_MD5_MAC:
+ return &pk11_sslSlotList;
+ case CKM_TLS_MASTER_KEY_DERIVE:
+ case CKM_TLS_KEY_AND_MAC_DERIVE:
+ case CKM_NSS_TLS_KEY_AND_MAC_DERIVE_SHA256:
+ return &pk11_tlsSlotList;
+ case CKM_IDEA_CBC:
+ case CKM_IDEA_ECB:
+ return &pk11_ideaSlotList;
+ case CKM_FAKE_RANDOM:
+ return &pk11_randomSlotList;
+ }
+ return NULL;
+}
+
+/*
+ * load the static SlotInfo structures used to select a PKCS11 slot.
+ * preSlotInfo has a list of all the default flags for the slots on this
+ * module.
+ */
+void
+PK11_LoadSlotList(PK11SlotInfo *slot, PK11PreSlotInfo *psi, int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ if (psi[i].slotID == slot->slotID)
+ break;
+ }
+
+ if (i == count)
+ return;
+
+ slot->defaultFlags = psi[i].defaultFlags;
+ slot->askpw = psi[i].askpw;
+ slot->timeout = psi[i].timeout;
+ slot->hasRootCerts = psi[i].hasRootCerts;
+
+ /* if the slot is already disabled, don't load them into the
+ * default slot lists. We get here so we can save the default
+ * list value. */
+ if (slot->disabled)
+ return;
+
+ /* if the user has disabled us, don't load us in */
+ if (slot->defaultFlags & PK11_DISABLE_FLAG) {
+ slot->disabled = PR_TRUE;
+ slot->reason = PK11_DIS_USER_SELECTED;
+ /* free up sessions and things?? */
+ return;
+ }
+
+ for (i = 0; i < num_pk11_default_mechanisms; i++) {
+ if (slot->defaultFlags & PK11_DefaultArray[i].flag) {
+ CK_MECHANISM_TYPE mechanism = PK11_DefaultArray[i].mechanism;
+ PK11SlotList *slotList = PK11_GetSlotList(mechanism);
+
+ if (slotList)
+ PK11_AddSlotToList(slotList, slot, PR_FALSE);
+ }
+ }
+
+ return;
+}
+
+/*
+ * update a slot to its new attribute according to the slot list
+ * returns: SECSuccess if nothing to do or add/delete is successful
+ */
+SECStatus
+PK11_UpdateSlotAttribute(PK11SlotInfo *slot,
+ const PK11DefaultArrayEntry *entry,
+ PRBool add)
+/* add: PR_TRUE if want to turn on */
+{
+ SECStatus result = SECSuccess;
+ PK11SlotList *slotList = PK11_GetSlotList(entry->mechanism);
+
+ if (add) { /* trying to turn on a mechanism */
+
+ /* turn on the default flag in the slot */
+ slot->defaultFlags |= entry->flag;
+
+ /* add this slot to the list */
+ if (slotList != NULL)
+ result = PK11_AddSlotToList(slotList, slot, PR_FALSE);
+
+ } else { /* trying to turn off */
+
+ /* turn OFF the flag in the slot */
+ slot->defaultFlags &= ~entry->flag;
+
+ if (slotList) {
+ /* find the element in the list & delete it */
+ PK11SlotListElement *le = PK11_FindSlotElement(slotList, slot);
+
+ /* remove the slot from the list */
+ if (le)
+ result = PK11_DeleteSlotFromList(slotList, le);
+ }
+ }
+ return result;
+}
+
+/*
+ * clear a slot off of all of it's default list
+ */
+void
+PK11_ClearSlotList(PK11SlotInfo *slot)
+{
+ int i;
+
+ if (slot->disabled)
+ return;
+ if (slot->defaultFlags == 0)
+ return;
+
+ for (i = 0; i < num_pk11_default_mechanisms; i++) {
+ if (slot->defaultFlags & PK11_DefaultArray[i].flag) {
+ CK_MECHANISM_TYPE mechanism = PK11_DefaultArray[i].mechanism;
+ PK11SlotList *slotList = PK11_GetSlotList(mechanism);
+ PK11SlotListElement *le = NULL;
+
+ if (slotList)
+ le = PK11_FindSlotElement(slotList, slot);
+
+ if (le) {
+ PK11_DeleteSlotFromList(slotList, le);
+ PK11_FreeSlotListElement(slotList, le);
+ }
+ }
+ }
+}
+
+/******************************************************************
+ * Slot initialization
+ ******************************************************************/
+/*
+ * turn a PKCS11 Static Label into a string
+ */
+char *
+PK11_MakeString(PLArenaPool *arena, char *space,
+ char *staticString, int stringLen)
+{
+ int i;
+ char *newString;
+ for (i = (stringLen - 1); i >= 0; i--) {
+ if (staticString[i] != ' ')
+ break;
+ }
+ /* move i to point to the last space */
+ i++;
+ if (arena) {
+ newString = (char *)PORT_ArenaAlloc(arena, i + 1 /* space for NULL */);
+ } else if (space) {
+ newString = space;
+ } else {
+ newString = (char *)PORT_Alloc(i + 1 /* space for NULL */);
+ }
+ if (newString == NULL)
+ return NULL;
+
+ if (i)
+ PORT_Memcpy(newString, staticString, i);
+ newString[i] = 0;
+
+ return newString;
+}
+
+/*
+ * check if a null-terminated string matches with a PKCS11 Static Label
+ */
+PRBool
+pk11_MatchString(const char *string,
+ const char *staticString, size_t staticStringLen)
+{
+ size_t i = staticStringLen;
+
+ /* move i to point to the last space */
+ while (i > 0) {
+ if (staticString[i - 1] != ' ')
+ break;
+ i--;
+ }
+
+ if (strlen(string) == i && memcmp(string, staticString, i) == 0) {
+ return PR_TRUE;
+ }
+
+ return PR_FALSE;
+}
+
+/*
+ * Reads in the slots mechanism list for later use
+ */
+SECStatus
+PK11_ReadMechanismList(PK11SlotInfo *slot)
+{
+ CK_ULONG count;
+ CK_RV crv;
+ PRUint32 i;
+
+ if (slot->mechanismList) {
+ PORT_Free(slot->mechanismList);
+ slot->mechanismList = NULL;
+ }
+ slot->mechanismCount = 0;
+
+ if (!slot->isThreadSafe)
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_GetMechanismList(slot->slotID, NULL, &count);
+ if (crv != CKR_OK) {
+ if (!slot->isThreadSafe)
+ PK11_ExitSlotMonitor(slot);
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+
+ slot->mechanismList = (CK_MECHANISM_TYPE *)
+ PORT_Alloc(count * sizeof(CK_MECHANISM_TYPE));
+ if (slot->mechanismList == NULL) {
+ if (!slot->isThreadSafe)
+ PK11_ExitSlotMonitor(slot);
+ return SECFailure;
+ }
+ crv = PK11_GETTAB(slot)->C_GetMechanismList(slot->slotID,
+ slot->mechanismList, &count);
+ if (!slot->isThreadSafe)
+ PK11_ExitSlotMonitor(slot);
+ if (crv != CKR_OK) {
+ PORT_Free(slot->mechanismList);
+ slot->mechanismList = NULL;
+ PORT_SetError(PK11_MapError(crv));
+ return SECSuccess;
+ }
+ slot->mechanismCount = count;
+ PORT_Memset(slot->mechanismBits, 0, sizeof(slot->mechanismBits));
+
+ for (i = 0; i < count; i++) {
+ CK_MECHANISM_TYPE mech = slot->mechanismList[i];
+ if (mech < 0x7ff) {
+ slot->mechanismBits[mech & 0xff] |= 1 << (mech >> 8);
+ }
+ }
+ return SECSuccess;
+}
+
+static SECStatus
+pk11_ReadProfileList(PK11SlotInfo *slot)
+{
+ CK_ATTRIBUTE findTemp[2];
+ CK_ATTRIBUTE *attrs;
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_OBJECT_CLASS oclass = CKO_PROFILE;
+ size_t tsize;
+ int objCount;
+ CK_OBJECT_HANDLE *handles = NULL;
+ int i;
+
+ attrs = findTemp;
+ PK11_SETATTRS(attrs, CKA_TOKEN, &cktrue, sizeof(cktrue));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_CLASS, &oclass, sizeof(oclass));
+ attrs++;
+ tsize = attrs - findTemp;
+ PORT_Assert(tsize <= sizeof(findTemp) / sizeof(CK_ATTRIBUTE));
+
+ if (slot->profileList) {
+ PORT_Free(slot->profileList);
+ slot->profileList = NULL;
+ }
+ slot->profileCount = 0;
+
+ objCount = 0;
+ handles = pk11_FindObjectsByTemplate(slot, findTemp, tsize, &objCount);
+ if (handles == NULL) {
+ if (objCount < 0) {
+ return SECFailure; /* error code is set */
+ }
+ PORT_Assert(objCount == 0);
+ return SECSuccess;
+ }
+
+ slot->profileList = (CK_PROFILE_ID *)
+ PORT_Alloc(objCount * sizeof(CK_PROFILE_ID));
+ if (slot->profileList == NULL) {
+ PORT_Free(handles);
+ return SECFailure; /* error code is set */
+ }
+
+ for (i = 0; i < objCount; i++) {
+ CK_ULONG value;
+
+ value = PK11_ReadULongAttribute(slot, handles[i], CKA_PROFILE_ID);
+ if (value == CK_UNAVAILABLE_INFORMATION) {
+ continue;
+ }
+ slot->profileList[slot->profileCount++] = value;
+ }
+
+ PORT_Free(handles);
+ return SECSuccess;
+}
+
+static PRBool
+pk11_HasProfile(PK11SlotInfo *slot, CK_PROFILE_ID id)
+{
+ int i;
+
+ for (i = 0; i < slot->profileCount; i++) {
+ if (slot->profileList[i] == id) {
+ return PR_TRUE;
+ }
+ }
+ return PR_FALSE;
+}
+
+/*
+ * initialize a new token
+ * unlike initialize slot, this can be called multiple times in the lifetime
+ * of NSS. It reads the information associated with a card or token,
+ * that is not going to change unless the card or token changes.
+ */
+SECStatus
+PK11_InitToken(PK11SlotInfo *slot, PRBool loadCerts)
+{
+ CK_RV crv;
+ SECStatus rv;
+ PRStatus status;
+ NSSToken *nssToken;
+
+ /* set the slot flags to the current token values */
+ if (!slot->isThreadSafe)
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_GetTokenInfo(slot->slotID, &slot->tokenInfo);
+ if (!slot->isThreadSafe)
+ PK11_ExitSlotMonitor(slot);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+
+ /* set the slot flags to the current token values */
+ slot->series++; /* allow other objects to detect that the
+ * slot is different */
+ slot->flags = slot->tokenInfo.flags;
+ slot->needLogin = ((slot->tokenInfo.flags & CKF_LOGIN_REQUIRED) ? PR_TRUE : PR_FALSE);
+ slot->readOnly = ((slot->tokenInfo.flags & CKF_WRITE_PROTECTED) ? PR_TRUE : PR_FALSE);
+
+ slot->hasRandom = ((slot->tokenInfo.flags & CKF_RNG) ? PR_TRUE : PR_FALSE);
+ slot->protectedAuthPath =
+ ((slot->tokenInfo.flags & CKF_PROTECTED_AUTHENTICATION_PATH)
+ ? PR_TRUE
+ : PR_FALSE);
+ slot->lastLoginCheck = 0;
+ slot->lastState = 0;
+ /* on some platforms Active Card incorrectly sets the
+ * CKF_PROTECTED_AUTHENTICATION_PATH bit when it doesn't mean to. */
+ if (slot->isActiveCard) {
+ slot->protectedAuthPath = PR_FALSE;
+ }
+ (void)PK11_MakeString(NULL, slot->token_name,
+ (char *)slot->tokenInfo.label, sizeof(slot->tokenInfo.label));
+ slot->minPassword = slot->tokenInfo.ulMinPinLen;
+ slot->maxPassword = slot->tokenInfo.ulMaxPinLen;
+ PORT_Memcpy(slot->serial, slot->tokenInfo.serialNumber, sizeof(slot->serial));
+
+ nssToken = PK11Slot_GetNSSToken(slot);
+ nssToken_UpdateName(nssToken); /* null token is OK */
+ (void)nssToken_Destroy(nssToken);
+
+ slot->defRWSession = (PRBool)((!slot->readOnly) &&
+ (slot->tokenInfo.ulMaxSessionCount == 1));
+ rv = PK11_ReadMechanismList(slot);
+ if (rv != SECSuccess)
+ return rv;
+
+ slot->hasRSAInfo = PR_FALSE;
+ slot->RSAInfoFlags = 0;
+
+ /* initialize the maxKeyCount value */
+ if (slot->tokenInfo.ulMaxSessionCount == 0) {
+ slot->maxKeyCount = 800; /* should be #define or a config param */
+ } else if (slot->tokenInfo.ulMaxSessionCount < 20) {
+ /* don't have enough sessions to keep that many keys around */
+ slot->maxKeyCount = 0;
+ } else {
+ slot->maxKeyCount = slot->tokenInfo.ulMaxSessionCount / 2;
+ }
+
+ /* Make sure our session handle is valid */
+ if (slot->session == CK_INVALID_HANDLE) {
+ /* we know we don't have a valid session, go get one */
+ CK_SESSION_HANDLE session;
+
+ /* session should be Readonly, serial */
+ if (!slot->isThreadSafe)
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_OpenSession(slot->slotID,
+ (slot->defRWSession ? CKF_RW_SESSION : 0) | CKF_SERIAL_SESSION,
+ slot, pk11_notify, &session);
+ if (!slot->isThreadSafe)
+ PK11_ExitSlotMonitor(slot);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ slot->session = session;
+ } else {
+ /* The session we have may be defunct (the token associated with it)
+ * has been removed */
+ CK_SESSION_INFO sessionInfo;
+
+ if (!slot->isThreadSafe)
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_GetSessionInfo(slot->session, &sessionInfo);
+ if (crv == CKR_DEVICE_ERROR) {
+ PK11_GETTAB(slot)
+ ->C_CloseSession(slot->session);
+ crv = CKR_SESSION_CLOSED;
+ }
+ if ((crv == CKR_SESSION_CLOSED) || (crv == CKR_SESSION_HANDLE_INVALID)) {
+ crv = PK11_GETTAB(slot)->C_OpenSession(slot->slotID,
+ (slot->defRWSession ? CKF_RW_SESSION : 0) | CKF_SERIAL_SESSION,
+ slot, pk11_notify, &slot->session);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ slot->session = CK_INVALID_HANDLE;
+ if (!slot->isThreadSafe)
+ PK11_ExitSlotMonitor(slot);
+ return SECFailure;
+ }
+ }
+ if (!slot->isThreadSafe)
+ PK11_ExitSlotMonitor(slot);
+ }
+
+ nssToken = PK11Slot_GetNSSToken(slot);
+ status = nssToken_Refresh(nssToken); /* null token is OK */
+ (void)nssToken_Destroy(nssToken);
+ if (status != PR_SUCCESS)
+ return SECFailure;
+
+ /* Not all tokens have profile objects or even recognize what profile
+ * objects are it's OK for pk11_ReadProfileList to fail */
+ (void)pk11_ReadProfileList(slot);
+
+ if (!(slot->isInternal) && (slot->hasRandom)) {
+ /* if this slot has a random number generater, use it to add entropy
+ * to the internal slot. */
+ PK11SlotInfo *int_slot = PK11_GetInternalSlot();
+
+ if (int_slot) {
+ unsigned char random_bytes[32];
+
+ /* if this slot can issue random numbers, get some entropy from
+ * that random number generater and give it to our internal token.
+ */
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_GenerateRandom(slot->session, random_bytes, sizeof(random_bytes));
+ PK11_ExitSlotMonitor(slot);
+ if (crv == CKR_OK) {
+ PK11_EnterSlotMonitor(int_slot);
+ PK11_GETTAB(int_slot)
+ ->C_SeedRandom(int_slot->session,
+ random_bytes, sizeof(random_bytes));
+ PK11_ExitSlotMonitor(int_slot);
+ }
+
+ /* Now return the favor and send entropy to the token's random
+ * number generater */
+ PK11_EnterSlotMonitor(int_slot);
+ crv = PK11_GETTAB(int_slot)->C_GenerateRandom(int_slot->session,
+ random_bytes, sizeof(random_bytes));
+ PK11_ExitSlotMonitor(int_slot);
+ if (crv == CKR_OK) {
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_SeedRandom(slot->session,
+ random_bytes, sizeof(random_bytes));
+ PK11_ExitSlotMonitor(slot);
+ }
+ PK11_FreeSlot(int_slot);
+ }
+ }
+ /* work around a problem in softoken where it incorrectly
+ * reports databases opened read only as read/write. */
+ if (slot->isInternal && !slot->readOnly) {
+ CK_SESSION_HANDLE session = CK_INVALID_HANDLE;
+
+ /* try to open a R/W session */
+ crv = PK11_GETTAB(slot)->C_OpenSession(slot->slotID,
+ CKF_RW_SESSION | CKF_SERIAL_SESSION, slot, pk11_notify, &session);
+ /* what a well behaved token should return if you open
+ * a RW session on a read only token */
+ if (crv == CKR_TOKEN_WRITE_PROTECTED) {
+ slot->readOnly = PR_TRUE;
+ } else if (crv == CKR_OK) {
+ CK_SESSION_INFO sessionInfo;
+
+ /* Because of a second bug in softoken, which silently returns
+ * a RO session, we need to check what type of session we got. */
+ crv = PK11_GETTAB(slot)->C_GetSessionInfo(session, &sessionInfo);
+ if (crv == CKR_OK) {
+ if ((sessionInfo.flags & CKF_RW_SESSION) == 0) {
+ /* session was readonly, so this softoken slot must be readonly */
+ slot->readOnly = PR_TRUE;
+ }
+ }
+ PK11_GETTAB(slot)
+ ->C_CloseSession(session);
+ }
+ }
+
+ return SECSuccess;
+}
+
+/*
+ * initialize a new token
+ * unlike initialize slot, this can be called multiple times in the lifetime
+ * of NSS. It reads the information associated with a card or token,
+ * that is not going to change unless the card or token changes.
+ */
+SECStatus
+PK11_TokenRefresh(PK11SlotInfo *slot)
+{
+ CK_RV crv;
+
+ /* set the slot flags to the current token values */
+ if (!slot->isThreadSafe)
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_GetTokenInfo(slot->slotID, &slot->tokenInfo);
+ if (!slot->isThreadSafe)
+ PK11_ExitSlotMonitor(slot);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+
+ slot->flags = slot->tokenInfo.flags;
+ slot->needLogin = ((slot->tokenInfo.flags & CKF_LOGIN_REQUIRED) ? PR_TRUE : PR_FALSE);
+ slot->readOnly = ((slot->tokenInfo.flags & CKF_WRITE_PROTECTED) ? PR_TRUE : PR_FALSE);
+ slot->hasRandom = ((slot->tokenInfo.flags & CKF_RNG) ? PR_TRUE : PR_FALSE);
+ slot->protectedAuthPath =
+ ((slot->tokenInfo.flags & CKF_PROTECTED_AUTHENTICATION_PATH)
+ ? PR_TRUE
+ : PR_FALSE);
+ /* on some platforms Active Card incorrectly sets the
+ * CKF_PROTECTED_AUTHENTICATION_PATH bit when it doesn't mean to. */
+ if (slot->isActiveCard) {
+ slot->protectedAuthPath = PR_FALSE;
+ }
+ return SECSuccess;
+}
+
+static PRBool
+pk11_isRootSlot(PK11SlotInfo *slot)
+{
+ CK_ATTRIBUTE findTemp[1];
+ CK_ATTRIBUTE *attrs;
+ CK_OBJECT_CLASS oclass = CKO_NSS_BUILTIN_ROOT_LIST;
+ size_t tsize;
+ CK_OBJECT_HANDLE handle;
+
+ attrs = findTemp;
+ PK11_SETATTRS(attrs, CKA_CLASS, &oclass, sizeof(oclass));
+ attrs++;
+ tsize = attrs - findTemp;
+ PORT_Assert(tsize <= sizeof(findTemp) / sizeof(CK_ATTRIBUTE));
+
+ handle = pk11_FindObjectByTemplate(slot, findTemp, tsize);
+ if (handle == CK_INVALID_HANDLE) {
+ return PR_FALSE;
+ }
+ return PR_TRUE;
+}
+
+/*
+ * Initialize the slot :
+ * This initialization code is called on each slot a module supports when
+ * it is loaded. It does the bringup initialization. The difference between
+ * this and InitToken is Init slot does those one time initialization stuff,
+ * usually associated with the reader, while InitToken may get called multiple
+ * times as tokens are removed and re-inserted.
+ */
+void
+PK11_InitSlot(SECMODModule *mod, CK_SLOT_ID slotID, PK11SlotInfo *slot)
+{
+ SECStatus rv;
+ CK_SLOT_INFO slotInfo;
+
+ slot->functionList = mod->functionList;
+ slot->isInternal = mod->internal;
+ slot->slotID = slotID;
+ slot->isThreadSafe = mod->isThreadSafe;
+ slot->hasRSAInfo = PR_FALSE;
+ slot->module = mod; /* NOTE: we don't make a reference here because
+ * modules have references to their slots. This
+ * works because modules keep implicit references
+ * from their slots, and won't unload and disappear
+ * until all their slots have been freed */
+
+ if (PK11_GetSlotInfo(slot, &slotInfo) != SECSuccess) {
+ slot->disabled = PR_TRUE;
+ slot->reason = PK11_DIS_COULD_NOT_INIT_TOKEN;
+ return;
+ }
+
+ /* test to make sure claimed mechanism work */
+ slot->needTest = mod->internal ? PR_FALSE : PR_TRUE;
+ (void)PK11_MakeString(NULL, slot->slot_name,
+ (char *)slotInfo.slotDescription, sizeof(slotInfo.slotDescription));
+ slot->isHW = (PRBool)((slotInfo.flags & CKF_HW_SLOT) == CKF_HW_SLOT);
+#define ACTIVE_CARD "ActivCard SA"
+ slot->isActiveCard = (PRBool)(PORT_Strncmp((char *)slotInfo.manufacturerID,
+ ACTIVE_CARD, sizeof(ACTIVE_CARD) - 1) == 0);
+ if ((slotInfo.flags & CKF_REMOVABLE_DEVICE) == 0) {
+ slot->isPerm = PR_TRUE;
+ /* permanment slots must have the token present always */
+ if ((slotInfo.flags & CKF_TOKEN_PRESENT) == 0) {
+ slot->disabled = PR_TRUE;
+ slot->reason = PK11_DIS_TOKEN_NOT_PRESENT;
+ return; /* nothing else to do */
+ }
+ }
+ /* if the token is present, initialize it */
+ if ((slotInfo.flags & CKF_TOKEN_PRESENT) != 0) {
+ rv = PK11_InitToken(slot, PR_TRUE);
+ /* the only hard failures are on permanent devices, or function
+ * verify failures... function verify failures are already handled
+ * by tokenInit */
+ if ((rv != SECSuccess) && (slot->isPerm) && (!slot->disabled)) {
+ slot->disabled = PR_TRUE;
+ slot->reason = PK11_DIS_COULD_NOT_INIT_TOKEN;
+ }
+ if (rv == SECSuccess && pk11_isRootSlot(slot)) {
+ if (!slot->hasRootCerts) {
+ slot->module->trustOrder = 100;
+ }
+ slot->hasRootCerts = PR_TRUE;
+ }
+ }
+ if ((slotInfo.flags & CKF_USER_PIN_INITIALIZED) != 0) {
+ slot->flags |= CKF_USER_PIN_INITIALIZED;
+ }
+}
+
+/*********************************************************************
+ * Slot mapping utility functions.
+ *********************************************************************/
+
+/*
+ * determine if the token is present. If the token is present, make sure
+ * we have a valid session handle. Also set the value of needLogin
+ * appropriately.
+ */
+static PRBool
+pk11_IsPresentCertLoad(PK11SlotInfo *slot, PRBool loadCerts)
+{
+ CK_SLOT_INFO slotInfo;
+ CK_SESSION_INFO sessionInfo;
+ CK_RV crv;
+
+ /* disabled slots are never present */
+ if (slot->disabled) {
+ return PR_FALSE;
+ }
+
+ /* permanent slots are always present */
+ if (slot->isPerm && (slot->session != CK_INVALID_HANDLE)) {
+ return PR_TRUE;
+ }
+
+ NSSToken *nssToken = PK11Slot_GetNSSToken(slot);
+ if (nssToken) {
+ PRBool present = nssToken_IsPresent(nssToken);
+ (void)nssToken_Destroy(nssToken);
+ return present;
+ }
+
+ /* removable slots have a flag that says they are present */
+ if (PK11_GetSlotInfo(slot, &slotInfo) != SECSuccess) {
+ return PR_FALSE;
+ }
+
+ if ((slotInfo.flags & CKF_TOKEN_PRESENT) == 0) {
+ /* if the slot is no longer present, close the session */
+ if (slot->session != CK_INVALID_HANDLE) {
+ if (!slot->isThreadSafe) {
+ PK11_EnterSlotMonitor(slot);
+ }
+ PK11_GETTAB(slot)
+ ->C_CloseSession(slot->session);
+ slot->session = CK_INVALID_HANDLE;
+ if (!slot->isThreadSafe) {
+ PK11_ExitSlotMonitor(slot);
+ }
+ }
+ return PR_FALSE;
+ }
+
+ /* use the session Info to determine if the card has been removed and then
+ * re-inserted */
+ if (slot->session != CK_INVALID_HANDLE) {
+ if (slot->isThreadSafe) {
+ PK11_EnterSlotMonitor(slot);
+ }
+ crv = PK11_GETTAB(slot)->C_GetSessionInfo(slot->session, &sessionInfo);
+ if (crv != CKR_OK) {
+ PK11_GETTAB(slot)
+ ->C_CloseSession(slot->session);
+ slot->session = CK_INVALID_HANDLE;
+ }
+ if (slot->isThreadSafe) {
+ PK11_ExitSlotMonitor(slot);
+ }
+ }
+
+ /* card has not been removed, current token info is correct */
+ if (slot->session != CK_INVALID_HANDLE)
+ return PR_TRUE;
+
+ /* initialize the token info state */
+ if (PK11_InitToken(slot, loadCerts) != SECSuccess) {
+ return PR_FALSE;
+ }
+
+ return PR_TRUE;
+}
+
+/*
+ * old version of the routine
+ */
+PRBool
+PK11_IsPresent(PK11SlotInfo *slot)
+{
+ return pk11_IsPresentCertLoad(slot, PR_TRUE);
+}
+
+/* is the slot disabled? */
+PRBool
+PK11_IsDisabled(PK11SlotInfo *slot)
+{
+ return slot->disabled;
+}
+
+/* and why? */
+PK11DisableReasons
+PK11_GetDisabledReason(PK11SlotInfo *slot)
+{
+ return slot->reason;
+}
+
+/* returns PR_TRUE if successfully disable the slot */
+/* returns PR_FALSE otherwise */
+PRBool
+PK11_UserDisableSlot(PK11SlotInfo *slot)
+{
+
+ /* Prevent users from disabling the internal module. */
+ if (slot->isInternal) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return PR_FALSE;
+ }
+
+ slot->defaultFlags |= PK11_DISABLE_FLAG;
+ slot->disabled = PR_TRUE;
+ slot->reason = PK11_DIS_USER_SELECTED;
+
+ return PR_TRUE;
+}
+
+PRBool
+PK11_UserEnableSlot(PK11SlotInfo *slot)
+{
+
+ slot->defaultFlags &= ~PK11_DISABLE_FLAG;
+ slot->disabled = PR_FALSE;
+ slot->reason = PK11_DIS_NONE;
+ return PR_TRUE;
+}
+
+PRBool
+PK11_HasRootCerts(PK11SlotInfo *slot)
+{
+ return slot->hasRootCerts;
+}
+
+/* Get the module this slot is attached to */
+SECMODModule *
+PK11_GetModule(PK11SlotInfo *slot)
+{
+ return slot->module;
+}
+
+/* return the default flags of a slot */
+unsigned long
+PK11_GetDefaultFlags(PK11SlotInfo *slot)
+{
+ return slot->defaultFlags;
+}
+
+/*
+ * The following wrapper functions allow us to export an opaque slot
+ * function to the rest of libsec and the world... */
+PRBool
+PK11_IsReadOnly(PK11SlotInfo *slot)
+{
+ return slot->readOnly;
+}
+
+PRBool
+PK11_IsHW(PK11SlotInfo *slot)
+{
+ return slot->isHW;
+}
+
+PRBool
+PK11_IsRemovable(PK11SlotInfo *slot)
+{
+ return !slot->isPerm;
+}
+
+PRBool
+PK11_IsInternal(PK11SlotInfo *slot)
+{
+ return slot->isInternal;
+}
+
+PRBool
+PK11_IsInternalKeySlot(PK11SlotInfo *slot)
+{
+ PK11SlotInfo *int_slot;
+ PRBool result;
+
+ if (!slot->isInternal) {
+ return PR_FALSE;
+ }
+
+ int_slot = PK11_GetInternalKeySlot();
+ result = (int_slot == slot) ? PR_TRUE : PR_FALSE;
+ PK11_FreeSlot(int_slot);
+ return result;
+}
+
+PRBool
+PK11_NeedLogin(PK11SlotInfo *slot)
+{
+ return slot->needLogin;
+}
+
+PRBool
+PK11_IsFriendly(PK11SlotInfo *slot)
+{
+ /* internal slot always has public readable certs */
+ return (PRBool)(slot->isInternal ||
+ pk11_HasProfile(slot, CKP_PUBLIC_CERTIFICATES_TOKEN) ||
+ ((slot->defaultFlags & SECMOD_FRIENDLY_FLAG) ==
+ SECMOD_FRIENDLY_FLAG));
+}
+
+char *
+PK11_GetTokenName(PK11SlotInfo *slot)
+{
+ return slot->token_name;
+}
+
+char *
+PK11_GetTokenURI(PK11SlotInfo *slot)
+{
+ PK11URI *uri;
+ char *ret = NULL;
+ char label[32 + 1], manufacturer[32 + 1], serial[16 + 1], model[16 + 1];
+ PK11URIAttribute attrs[4];
+ size_t nattrs = 0;
+
+ PK11_MakeString(NULL, label, (char *)slot->tokenInfo.label,
+ sizeof(slot->tokenInfo.label));
+ if (*label != '\0') {
+ attrs[nattrs].name = PK11URI_PATTR_TOKEN;
+ attrs[nattrs].value = label;
+ nattrs++;
+ }
+
+ PK11_MakeString(NULL, manufacturer, (char *)slot->tokenInfo.manufacturerID,
+ sizeof(slot->tokenInfo.manufacturerID));
+ if (*manufacturer != '\0') {
+ attrs[nattrs].name = PK11URI_PATTR_MANUFACTURER;
+ attrs[nattrs].value = manufacturer;
+ nattrs++;
+ }
+
+ PK11_MakeString(NULL, serial, (char *)slot->tokenInfo.serialNumber,
+ sizeof(slot->tokenInfo.serialNumber));
+ if (*serial != '\0') {
+ attrs[nattrs].name = PK11URI_PATTR_SERIAL;
+ attrs[nattrs].value = serial;
+ nattrs++;
+ }
+
+ PK11_MakeString(NULL, model, (char *)slot->tokenInfo.model,
+ sizeof(slot->tokenInfo.model));
+ if (*model != '\0') {
+ attrs[nattrs].name = PK11URI_PATTR_MODEL;
+ attrs[nattrs].value = model;
+ nattrs++;
+ }
+
+ uri = PK11URI_CreateURI(attrs, nattrs, NULL, 0);
+ if (uri == NULL) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return NULL;
+ }
+
+ ret = PK11URI_FormatURI(NULL, uri);
+ PK11URI_DestroyURI(uri);
+
+ if (ret == NULL) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ }
+
+ return ret;
+}
+
+char *
+PK11_GetSlotName(PK11SlotInfo *slot)
+{
+ return slot->slot_name;
+}
+
+int
+PK11_GetSlotSeries(PK11SlotInfo *slot)
+{
+ return slot->series;
+}
+
+int
+PK11_GetCurrentWrapIndex(PK11SlotInfo *slot)
+{
+ return slot->wrapKey;
+}
+
+CK_SLOT_ID
+PK11_GetSlotID(PK11SlotInfo *slot)
+{
+ return slot->slotID;
+}
+
+SECMODModuleID
+PK11_GetModuleID(PK11SlotInfo *slot)
+{
+ return slot->module->moduleID;
+}
+
+static void
+pk11_zeroTerminatedToBlankPadded(CK_CHAR *buffer, size_t buffer_size)
+{
+ CK_CHAR *walk = buffer;
+ CK_CHAR *end = buffer + buffer_size;
+
+ /* find the NULL */
+ while (walk < end && *walk != '\0') {
+ walk++;
+ }
+
+ /* clear out the buffer */
+ while (walk < end) {
+ *walk++ = ' ';
+ }
+}
+
+/* return the slot info structure */
+SECStatus
+PK11_GetSlotInfo(PK11SlotInfo *slot, CK_SLOT_INFO *info)
+{
+ CK_RV crv;
+
+ if (!slot->isThreadSafe)
+ PK11_EnterSlotMonitor(slot);
+ /*
+ * some buggy drivers do not fill the buffer completely,
+ * erase the buffer first
+ */
+ PORT_Memset(info->slotDescription, ' ', sizeof(info->slotDescription));
+ PORT_Memset(info->manufacturerID, ' ', sizeof(info->manufacturerID));
+ crv = PK11_GETTAB(slot)->C_GetSlotInfo(slot->slotID, info);
+ pk11_zeroTerminatedToBlankPadded(info->slotDescription,
+ sizeof(info->slotDescription));
+ pk11_zeroTerminatedToBlankPadded(info->manufacturerID,
+ sizeof(info->manufacturerID));
+ if (!slot->isThreadSafe)
+ PK11_ExitSlotMonitor(slot);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/* return the token info structure */
+SECStatus
+PK11_GetTokenInfo(PK11SlotInfo *slot, CK_TOKEN_INFO *info)
+{
+ CK_RV crv;
+ if (!slot->isThreadSafe)
+ PK11_EnterSlotMonitor(slot);
+ /*
+ * some buggy drivers do not fill the buffer completely,
+ * erase the buffer first
+ */
+ PORT_Memset(info->label, ' ', sizeof(info->label));
+ PORT_Memset(info->manufacturerID, ' ', sizeof(info->manufacturerID));
+ PORT_Memset(info->model, ' ', sizeof(info->model));
+ PORT_Memset(info->serialNumber, ' ', sizeof(info->serialNumber));
+ crv = PK11_GETTAB(slot)->C_GetTokenInfo(slot->slotID, info);
+ pk11_zeroTerminatedToBlankPadded(info->label, sizeof(info->label));
+ pk11_zeroTerminatedToBlankPadded(info->manufacturerID,
+ sizeof(info->manufacturerID));
+ pk11_zeroTerminatedToBlankPadded(info->model, sizeof(info->model));
+ pk11_zeroTerminatedToBlankPadded(info->serialNumber,
+ sizeof(info->serialNumber));
+ if (!slot->isThreadSafe)
+ PK11_ExitSlotMonitor(slot);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+PRBool
+pk11_MatchUriTokenInfo(PK11SlotInfo *slot, PK11URI *uri)
+{
+ const char *value;
+
+ value = PK11URI_GetPathAttribute(uri, PK11URI_PATTR_TOKEN);
+ if (value) {
+ if (!pk11_MatchString(value, (char *)slot->tokenInfo.label,
+ sizeof(slot->tokenInfo.label))) {
+ return PR_FALSE;
+ }
+ }
+
+ value = PK11URI_GetPathAttribute(uri, PK11URI_PATTR_MANUFACTURER);
+ if (value) {
+ if (!pk11_MatchString(value, (char *)slot->tokenInfo.manufacturerID,
+ sizeof(slot->tokenInfo.manufacturerID))) {
+ return PR_FALSE;
+ }
+ }
+
+ value = PK11URI_GetPathAttribute(uri, PK11URI_PATTR_SERIAL);
+ if (value) {
+ if (!pk11_MatchString(value, (char *)slot->tokenInfo.serialNumber,
+ sizeof(slot->tokenInfo.serialNumber))) {
+ return PR_FALSE;
+ }
+ }
+
+ value = PK11URI_GetPathAttribute(uri, PK11URI_PATTR_MODEL);
+ if (value) {
+ if (!pk11_MatchString(value, (char *)slot->tokenInfo.model,
+ sizeof(slot->tokenInfo.model))) {
+ return PR_FALSE;
+ }
+ }
+
+ return PR_TRUE;
+}
+
+/* Find out if we need to initialize the user's pin */
+PRBool
+PK11_NeedUserInit(PK11SlotInfo *slot)
+{
+ PRBool needUserInit = (PRBool)((slot->flags & CKF_USER_PIN_INITIALIZED) == 0);
+
+ if (needUserInit) {
+ CK_TOKEN_INFO info;
+ SECStatus rv;
+
+ /* see if token has been initialized off line */
+ rv = PK11_GetTokenInfo(slot, &info);
+ if (rv == SECSuccess) {
+ slot->flags = info.flags;
+ }
+ }
+ return (PRBool)((slot->flags & CKF_USER_PIN_INITIALIZED) == 0);
+}
+
+static PK11SlotInfo *pk11InternalKeySlot = NULL;
+
+/*
+ * Set a new default internal keyslot. If one has already been set, clear it.
+ * Passing NULL falls back to the NSS normally selected default internal key
+ * slot.
+ */
+void
+pk11_SetInternalKeySlot(PK11SlotInfo *slot)
+{
+ if (pk11InternalKeySlot) {
+ PK11_FreeSlot(pk11InternalKeySlot);
+ }
+ pk11InternalKeySlot = slot ? PK11_ReferenceSlot(slot) : NULL;
+}
+
+/*
+ * Set a new default internal keyslot if the normal key slot has not already
+ * been overridden. Subsequent calls to this function will be ignored unless
+ * pk11_SetInternalKeySlot is used to clear the current default.
+ */
+void
+pk11_SetInternalKeySlotIfFirst(PK11SlotInfo *slot)
+{
+ if (pk11InternalKeySlot) {
+ return;
+ }
+ pk11InternalKeySlot = slot ? PK11_ReferenceSlot(slot) : NULL;
+}
+
+/*
+ * Swap out a default internal keyslot. Caller owns the Slot Reference
+ */
+PK11SlotInfo *
+pk11_SwapInternalKeySlot(PK11SlotInfo *slot)
+{
+ PK11SlotInfo *swap = pk11InternalKeySlot;
+
+ pk11InternalKeySlot = slot ? PK11_ReferenceSlot(slot) : NULL;
+ return swap;
+}
+
+/* get the internal key slot. FIPS has only one slot for both key slots and
+ * default slots */
+PK11SlotInfo *
+PK11_GetInternalKeySlot(void)
+{
+ SECMODModule *mod;
+
+ if (pk11InternalKeySlot) {
+ return PK11_ReferenceSlot(pk11InternalKeySlot);
+ }
+
+ mod = SECMOD_GetInternalModule();
+ PORT_Assert(mod != NULL);
+ if (!mod) {
+ PORT_SetError(SEC_ERROR_NO_MODULE);
+ return NULL;
+ }
+ return PK11_ReferenceSlot(mod->isFIPS ? mod->slots[0] : mod->slots[1]);
+}
+
+/* get the internal default slot */
+PK11SlotInfo *
+PK11_GetInternalSlot(void)
+{
+ SECMODModule *mod = SECMOD_GetInternalModule();
+ PORT_Assert(mod != NULL);
+ if (!mod) {
+ PORT_SetError(SEC_ERROR_NO_MODULE);
+ return NULL;
+ }
+ if (mod->isFIPS) {
+ return PK11_GetInternalKeySlot();
+ }
+ return PK11_ReferenceSlot(mod->slots[0]);
+}
+
+/*
+ * check if a given slot supports the requested mechanism
+ */
+PRBool
+PK11_DoesMechanism(PK11SlotInfo *slot, CK_MECHANISM_TYPE type)
+{
+ int i;
+
+ /* CKM_FAKE_RANDOM is not a real PKCS mechanism. It's a marker to
+ * tell us we're looking form someone that has implemented get
+ * random bits */
+ if (type == CKM_FAKE_RANDOM) {
+ return slot->hasRandom;
+ }
+
+ /* for most mechanism, bypass the linear lookup */
+ if (type < 0x7ff) {
+ return (slot->mechanismBits[type & 0xff] & (1 << (type >> 8))) ? PR_TRUE : PR_FALSE;
+ }
+
+ for (i = 0; i < (int)slot->mechanismCount; i++) {
+ if (slot->mechanismList[i] == type)
+ return PR_TRUE;
+ }
+ return PR_FALSE;
+}
+
+PRBool pk11_filterSlot(PK11SlotInfo *slot, CK_MECHANISM_TYPE mechanism,
+ CK_FLAGS mechanismInfoFlags, unsigned int keySize);
+/*
+ * Check that the given mechanism has the appropriate flags. This function
+ * presumes that slot can already do the given mechanism.
+ */
+PRBool
+PK11_DoesMechanismFlag(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
+ CK_FLAGS flags)
+{
+ return !pk11_filterSlot(slot, type, flags, 0);
+}
+
+/*
+ * Return true if a token that can do the desired mechanism exists.
+ * This allows us to have hardware tokens that can do function XYZ magically
+ * allow SSL Ciphers to appear if they are plugged in.
+ */
+PRBool
+PK11_TokenExists(CK_MECHANISM_TYPE type)
+{
+ SECMODModuleList *mlp;
+ SECMODModuleList *modules;
+ SECMODListLock *moduleLock = SECMOD_GetDefaultModuleListLock();
+ PK11SlotInfo *slot;
+ PRBool found = PR_FALSE;
+ int i;
+
+ if (!moduleLock) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return found;
+ }
+ /* we only need to know if there is a token that does this mechanism.
+ * check the internal module first because it's fast, and supports
+ * almost everything. */
+ slot = PK11_GetInternalSlot();
+ if (slot) {
+ found = PK11_DoesMechanism(slot, type);
+ PK11_FreeSlot(slot);
+ }
+ if (found)
+ return PR_TRUE; /* bypass getting module locks */
+
+ SECMOD_GetReadLock(moduleLock);
+ modules = SECMOD_GetDefaultModuleList();
+ for (mlp = modules; mlp != NULL && (!found); mlp = mlp->next) {
+ for (i = 0; i < mlp->module->slotCount; i++) {
+ slot = mlp->module->slots[i];
+ if (PK11_IsPresent(slot)) {
+ if (PK11_DoesMechanism(slot, type)) {
+ found = PR_TRUE;
+ break;
+ }
+ }
+ }
+ }
+ SECMOD_ReleaseReadLock(moduleLock);
+ return found;
+}
+
+/*
+ * get all the currently available tokens in a list.
+ * that can perform the given mechanism. If mechanism is CKM_INVALID_MECHANISM,
+ * get all the tokens. Make sure tokens that need authentication are put at
+ * the end of this list.
+ */
+PK11SlotList *
+PK11_GetAllTokens(CK_MECHANISM_TYPE type, PRBool needRW, PRBool loadCerts,
+ void *wincx)
+{
+ PK11SlotList *list;
+ PK11SlotList *loginList;
+ PK11SlotList *friendlyList;
+ SECMODModuleList *mlp;
+ SECMODModuleList *modules;
+ SECMODListLock *moduleLock;
+ int i;
+
+ moduleLock = SECMOD_GetDefaultModuleListLock();
+ if (!moduleLock) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return NULL;
+ }
+
+ list = PK11_NewSlotList();
+ loginList = PK11_NewSlotList();
+ friendlyList = PK11_NewSlotList();
+ if ((list == NULL) || (loginList == NULL) || (friendlyList == NULL)) {
+ if (list)
+ PK11_FreeSlotList(list);
+ if (loginList)
+ PK11_FreeSlotList(loginList);
+ if (friendlyList)
+ PK11_FreeSlotList(friendlyList);
+ return NULL;
+ }
+
+ SECMOD_GetReadLock(moduleLock);
+
+ modules = SECMOD_GetDefaultModuleList();
+ for (mlp = modules; mlp != NULL; mlp = mlp->next) {
+ for (i = 0; i < mlp->module->slotCount; i++) {
+ PK11SlotInfo *slot = mlp->module->slots[i];
+
+ if (pk11_IsPresentCertLoad(slot, loadCerts)) {
+ if (needRW && slot->readOnly)
+ continue;
+ if ((type == CKM_INVALID_MECHANISM) || PK11_DoesMechanism(slot, type)) {
+ if (pk11_LoginStillRequired(slot, wincx)) {
+ if (PK11_IsFriendly(slot)) {
+ PK11_AddSlotToList(friendlyList, slot, PR_TRUE);
+ } else {
+ PK11_AddSlotToList(loginList, slot, PR_TRUE);
+ }
+ } else {
+ PK11_AddSlotToList(list, slot, PR_TRUE);
+ }
+ }
+ }
+ }
+ }
+ SECMOD_ReleaseReadLock(moduleLock);
+
+ pk11_MoveListToList(list, friendlyList);
+ PK11_FreeSlotList(friendlyList);
+ pk11_MoveListToList(list, loginList);
+ PK11_FreeSlotList(loginList);
+
+ return list;
+}
+
+/*
+ * NOTE: This routine is working from a private List generated by
+ * PK11_GetAllTokens. That is why it does not need to lock.
+ */
+PK11SlotList *
+PK11_GetPrivateKeyTokens(CK_MECHANISM_TYPE type, PRBool needRW, void *wincx)
+{
+ PK11SlotList *list = PK11_GetAllTokens(type, needRW, PR_TRUE, wincx);
+ PK11SlotListElement *le, *next;
+ SECStatus rv;
+
+ if (list == NULL)
+ return list;
+
+ for (le = list->head; le; le = next) {
+ next = le->next; /* save the pointer here in case we have to
+ * free the element later */
+ rv = PK11_Authenticate(le->slot, PR_TRUE, wincx);
+ if (rv != SECSuccess) {
+ PK11_DeleteSlotFromList(list, le);
+ continue;
+ }
+ }
+ return list;
+}
+
+/*
+ * returns true if the slot doesn't conform to the requested attributes
+ */
+PRBool
+pk11_filterSlot(PK11SlotInfo *slot, CK_MECHANISM_TYPE mechanism,
+ CK_FLAGS mechanismInfoFlags, unsigned int keySize)
+{
+ CK_MECHANISM_INFO mechanism_info;
+ CK_RV crv = CKR_OK;
+
+ /* handle the only case where we don't actually fetch the mechanisms
+ * on the fly */
+ if ((keySize == 0) && (mechanism == CKM_RSA_PKCS) && (slot->hasRSAInfo)) {
+ mechanism_info.flags = slot->RSAInfoFlags;
+ } else {
+ if (!slot->isThreadSafe)
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_GetMechanismInfo(slot->slotID, mechanism,
+ &mechanism_info);
+ if (!slot->isThreadSafe)
+ PK11_ExitSlotMonitor(slot);
+ /* if we were getting the RSA flags, save them */
+ if ((crv == CKR_OK) && (mechanism == CKM_RSA_PKCS) && (!slot->hasRSAInfo)) {
+ slot->RSAInfoFlags = mechanism_info.flags;
+ slot->hasRSAInfo = PR_TRUE;
+ }
+ }
+ /* couldn't get the mechanism info */
+ if (crv != CKR_OK) {
+ return PR_TRUE;
+ }
+ if (keySize && ((mechanism_info.ulMinKeySize > keySize) || (mechanism_info.ulMaxKeySize < keySize))) {
+ /* Token can do mechanism, but not at the key size we
+ * want */
+ return PR_TRUE;
+ }
+ if (mechanismInfoFlags && ((mechanism_info.flags & mechanismInfoFlags) !=
+ mechanismInfoFlags)) {
+ return PR_TRUE;
+ }
+ return PR_FALSE;
+}
+
+/*
+ * Find the best slot which supports the given set of mechanisms and key sizes.
+ * In normal cases this should grab the first slot on the list with no fuss.
+ * The size array is presumed to match one for one with the mechanism type
+ * array, which allows you to specify the required key size for each
+ * mechanism in the list. Whether key size is in bits or bytes is mechanism
+ * dependent. Typically asymetric keys are in bits and symetric keys are in
+ * bytes.
+ */
+PK11SlotInfo *
+PK11_GetBestSlotMultipleWithAttributes(CK_MECHANISM_TYPE *type,
+ CK_FLAGS *mechanismInfoFlags, unsigned int *keySize,
+ unsigned int mech_count, void *wincx)
+{
+ PK11SlotList *list = NULL;
+ PK11SlotListElement *le;
+ PK11SlotInfo *slot = NULL;
+ PRBool freeit = PR_FALSE;
+ PRBool listNeedLogin = PR_FALSE;
+ unsigned int i;
+ SECStatus rv;
+
+ list = PK11_GetSlotList(type[0]);
+
+ if ((list == NULL) || (list->head == NULL)) {
+ /* We need to look up all the tokens for the mechanism */
+ list = PK11_GetAllTokens(type[0], PR_FALSE, PR_TRUE, wincx);
+ freeit = PR_TRUE;
+ }
+
+ /* no one can do it! */
+ if (list == NULL) {
+ PORT_SetError(SEC_ERROR_NO_TOKEN);
+ return NULL;
+ }
+
+ PORT_SetError(0);
+
+ listNeedLogin = PR_FALSE;
+ for (i = 0; i < mech_count; i++) {
+ if ((type[i] != CKM_FAKE_RANDOM) &&
+ (type[i] != CKM_SHA_1) &&
+ (type[i] != CKM_SHA224) &&
+ (type[i] != CKM_SHA256) &&
+ (type[i] != CKM_SHA384) &&
+ (type[i] != CKM_SHA512) &&
+ (type[i] != CKM_MD5) &&
+ (type[i] != CKM_MD2)) {
+ listNeedLogin = PR_TRUE;
+ break;
+ }
+ }
+
+ for (le = PK11_GetFirstSafe(list); le;
+ le = PK11_GetNextSafe(list, le, PR_TRUE)) {
+ if (PK11_IsPresent(le->slot)) {
+ PRBool doExit = PR_FALSE;
+ for (i = 0; i < mech_count; i++) {
+ if (!PK11_DoesMechanism(le->slot, type[i])) {
+ doExit = PR_TRUE;
+ break;
+ }
+ if ((mechanismInfoFlags && mechanismInfoFlags[i]) ||
+ (keySize && keySize[i])) {
+ if (pk11_filterSlot(le->slot, type[i],
+ mechanismInfoFlags ? mechanismInfoFlags[i] : 0,
+ keySize ? keySize[i] : 0)) {
+ doExit = PR_TRUE;
+ break;
+ }
+ }
+ }
+
+ if (doExit)
+ continue;
+
+ if (listNeedLogin && le->slot->needLogin) {
+ rv = PK11_Authenticate(le->slot, PR_TRUE, wincx);
+ if (rv != SECSuccess)
+ continue;
+ }
+ slot = le->slot;
+ PK11_ReferenceSlot(slot);
+ PK11_FreeSlotListElement(list, le);
+ if (freeit) {
+ PK11_FreeSlotList(list);
+ }
+ return slot;
+ }
+ }
+ if (freeit) {
+ PK11_FreeSlotList(list);
+ }
+ if (PORT_GetError() == 0) {
+ PORT_SetError(SEC_ERROR_NO_TOKEN);
+ }
+ return NULL;
+}
+
+PK11SlotInfo *
+PK11_GetBestSlotMultiple(CK_MECHANISM_TYPE *type,
+ unsigned int mech_count, void *wincx)
+{
+ return PK11_GetBestSlotMultipleWithAttributes(type, NULL, NULL,
+ mech_count, wincx);
+}
+
+/* original get best slot now calls the multiple version with only one type */
+PK11SlotInfo *
+PK11_GetBestSlot(CK_MECHANISM_TYPE type, void *wincx)
+{
+ return PK11_GetBestSlotMultipleWithAttributes(&type, NULL, NULL, 1, wincx);
+}
+
+PK11SlotInfo *
+PK11_GetBestSlotWithAttributes(CK_MECHANISM_TYPE type, CK_FLAGS mechanismFlags,
+ unsigned int keySize, void *wincx)
+{
+ return PK11_GetBestSlotMultipleWithAttributes(&type, &mechanismFlags,
+ &keySize, 1, wincx);
+}
+
+int
+PK11_GetBestKeyLength(PK11SlotInfo *slot, CK_MECHANISM_TYPE mechanism)
+{
+ CK_MECHANISM_INFO mechanism_info;
+ CK_RV crv;
+
+ if (!slot->isThreadSafe)
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_GetMechanismInfo(slot->slotID,
+ mechanism, &mechanism_info);
+ if (!slot->isThreadSafe)
+ PK11_ExitSlotMonitor(slot);
+ if (crv != CKR_OK)
+ return 0;
+
+ if (mechanism_info.ulMinKeySize == mechanism_info.ulMaxKeySize)
+ return 0;
+ return mechanism_info.ulMaxKeySize;
+}
+
+/*
+ * This function uses the existing PKCS #11 module to find the
+ * longest supported key length in the preferred token for a mechanism.
+ * This varies from the above function in that 1) it returns the key length
+ * even for fixed key algorithms, and 2) it looks through the tokens
+ * generally rather than for a specific token. This is used in liu of
+ * a PK11_GetKeyLength function in pk11mech.c since we can actually read
+ * supported key lengths from PKCS #11.
+ *
+ * For symmetric key operations the length is returned in bytes.
+ */
+int
+PK11_GetMaxKeyLength(CK_MECHANISM_TYPE mechanism)
+{
+ CK_MECHANISM_INFO mechanism_info;
+ PK11SlotList *list = NULL;
+ PK11SlotListElement *le;
+ PRBool freeit = PR_FALSE;
+ int keyLength = 0;
+
+ list = PK11_GetSlotList(mechanism);
+
+ if ((list == NULL) || (list->head == NULL)) {
+ /* We need to look up all the tokens for the mechanism */
+ list = PK11_GetAllTokens(mechanism, PR_FALSE, PR_FALSE, NULL);
+ freeit = PR_TRUE;
+ }
+
+ /* no tokens recognize this mechanism */
+ if (list == NULL) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ return 0;
+ }
+
+ for (le = PK11_GetFirstSafe(list); le;
+ le = PK11_GetNextSafe(list, le, PR_TRUE)) {
+ PK11SlotInfo *slot = le->slot;
+ CK_RV crv;
+ if (PK11_IsPresent(slot)) {
+ if (!slot->isThreadSafe)
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_GetMechanismInfo(slot->slotID,
+ mechanism, &mechanism_info);
+ if (!slot->isThreadSafe)
+ PK11_ExitSlotMonitor(slot);
+ if ((crv == CKR_OK) && (mechanism_info.ulMaxKeySize != 0) && (mechanism_info.ulMaxKeySize != 0xffffffff)) {
+ keyLength = mechanism_info.ulMaxKeySize;
+ break;
+ }
+ }
+ }
+
+ /* fallback to pk11_GetPredefinedKeyLength for fixed key size algorithms */
+ if (keyLength == 0) {
+ CK_KEY_TYPE keyType;
+ keyType = PK11_GetKeyType(mechanism, 0);
+ keyLength = pk11_GetPredefinedKeyLength(keyType);
+ }
+
+ if (le)
+ PK11_FreeSlotListElement(list, le);
+ if (freeit)
+ PK11_FreeSlotList(list);
+ return keyLength;
+}
+
+SECStatus
+PK11_SeedRandom(PK11SlotInfo *slot, unsigned char *data, int len)
+{
+ CK_RV crv;
+
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_SeedRandom(slot->session, data, (CK_ULONG)len);
+ PK11_ExitSlotMonitor(slot);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+SECStatus
+PK11_GenerateRandomOnSlot(PK11SlotInfo *slot, unsigned char *data, int len)
+{
+ CK_RV crv;
+
+ if (!slot->isInternal)
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_GenerateRandom(slot->session, data,
+ (CK_ULONG)len);
+ if (!slot->isInternal)
+ PK11_ExitSlotMonitor(slot);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/* Attempts to update the Best Slot for "FAKE RANDOM" generation.
+** If that's not the internal slot, then it also attempts to update the
+** internal slot.
+** The return value indicates if the INTERNAL slot was updated OK.
+*/
+SECStatus
+PK11_RandomUpdate(void *data, size_t bytes)
+{
+ PK11SlotInfo *slot;
+ PRBool bestIsInternal;
+ SECStatus status;
+
+ slot = PK11_GetBestSlot(CKM_FAKE_RANDOM, NULL);
+ if (slot == NULL) {
+ slot = PK11_GetInternalSlot();
+ if (!slot)
+ return SECFailure;
+ }
+
+ bestIsInternal = PK11_IsInternal(slot);
+ status = PK11_SeedRandom(slot, data, bytes);
+ PK11_FreeSlot(slot);
+
+ if (!bestIsInternal) {
+ /* do internal slot, too. */
+ slot = PK11_GetInternalSlot();
+ PORT_Assert(slot);
+ if (!slot) {
+ return SECFailure;
+ }
+ status = PK11_SeedRandom(slot, data, bytes);
+ PK11_FreeSlot(slot);
+ }
+ return status;
+}
+
+SECStatus
+PK11_GenerateRandom(unsigned char *data, int len)
+{
+ PK11SlotInfo *slot;
+ SECStatus rv;
+
+ slot = PK11_GetBestSlot(CKM_FAKE_RANDOM, NULL);
+ if (slot == NULL)
+ return SECFailure;
+
+ rv = PK11_GenerateRandomOnSlot(slot, data, len);
+ PK11_FreeSlot(slot);
+ return rv;
+}
+
+/*
+ * Reset the token to it's initial state. For the internal module, this will
+ * Purge your keydb, and reset your cert db certs to USER_INIT.
+ */
+SECStatus
+PK11_ResetToken(PK11SlotInfo *slot, char *sso_pwd)
+{
+ unsigned char tokenName[32];
+ size_t tokenNameLen;
+ CK_RV crv;
+
+ /* reconstruct the token name */
+ tokenNameLen = PORT_Strlen(slot->token_name);
+ if (tokenNameLen > sizeof(tokenName)) {
+ tokenNameLen = sizeof(tokenName);
+ }
+
+ PORT_Memcpy(tokenName, slot->token_name, tokenNameLen);
+ if (tokenNameLen < sizeof(tokenName)) {
+ PORT_Memset(&tokenName[tokenNameLen], ' ',
+ sizeof(tokenName) - tokenNameLen);
+ }
+
+ /* initialize the token */
+ PK11_EnterSlotMonitor(slot);
+
+ /* first shutdown the token. Existing sessions will get closed here */
+ PK11_GETTAB(slot)
+ ->C_CloseAllSessions(slot->slotID);
+ slot->session = CK_INVALID_HANDLE;
+
+ /* now re-init the token */
+ crv = PK11_GETTAB(slot)->C_InitToken(slot->slotID,
+ (unsigned char *)sso_pwd, sso_pwd ? PORT_Strlen(sso_pwd) : 0, tokenName);
+
+ /* finally bring the token back up */
+ PK11_InitToken(slot, PR_TRUE);
+ PK11_ExitSlotMonitor(slot);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ NSSToken *token = PK11Slot_GetNSSToken(slot);
+ if (token) {
+ nssTrustDomain_UpdateCachedTokenCerts(token->trustDomain, token);
+ (void)nssToken_Destroy(token);
+ }
+ return SECSuccess;
+}
+
+void
+PK11Slot_SetNSSToken(PK11SlotInfo *sl, NSSToken *nsst)
+{
+ NSSToken *old;
+ if (nsst) {
+ nsst = nssToken_AddRef(nsst);
+ }
+
+ PZ_Lock(sl->nssTokenLock);
+ old = sl->nssToken;
+ sl->nssToken = nsst;
+ PZ_Unlock(sl->nssTokenLock);
+
+ if (old) {
+ (void)nssToken_Destroy(old);
+ }
+}
+
+NSSToken *
+PK11Slot_GetNSSToken(PK11SlotInfo *sl)
+{
+ NSSToken *rv = NULL;
+
+ PZ_Lock(sl->nssTokenLock);
+ if (sl->nssToken) {
+ rv = nssToken_AddRef(sl->nssToken);
+ }
+ PZ_Unlock(sl->nssTokenLock);
+
+ return rv;
+}
+
+PRBool
+pk11slot_GetFIPSStatus(PK11SlotInfo *slot, CK_SESSION_HANDLE session,
+ CK_OBJECT_HANDLE object, CK_ULONG operationType)
+{
+ SECMODModule *mod = slot->module;
+ CK_RV crv;
+ CK_ULONG fipsState = CKS_NSS_FIPS_NOT_OK;
+
+ /* handle the obvious conditions:
+ * 1) the module doesn't have a fipsIndicator - fips state must be false */
+ if (mod->fipsIndicator == NULL) {
+ return PR_FALSE;
+ }
+ /* 2) the session doesn't exist - fips state must be false */
+ if (session == CK_INVALID_HANDLE) {
+ return PR_FALSE;
+ }
+
+ /* go fetch the state */
+ crv = mod->fipsIndicator(session, object, operationType, &fipsState);
+ if (crv != CKR_OK) {
+ return PR_FALSE;
+ }
+ return (fipsState == CKS_NSS_FIPS_OK) ? PR_TRUE : PR_FALSE;
+}
+
+PRBool
+PK11_SlotGetLastFIPSStatus(PK11SlotInfo *slot)
+{
+ return pk11slot_GetFIPSStatus(slot, slot->session, CK_INVALID_HANDLE,
+ CKT_NSS_SESSION_LAST_CHECK);
+}
+
+/*
+ * wait for a token to change it's state. The application passes in the expected
+ * new state in event.
+ */
+PK11TokenStatus
+PK11_WaitForTokenEvent(PK11SlotInfo *slot, PK11TokenEvent event,
+ PRIntervalTime timeout, PRIntervalTime latency, int series)
+{
+ PRIntervalTime first_time = 0;
+ PRBool first_time_set = PR_FALSE;
+ PRBool waitForRemoval;
+
+ if (slot->isPerm) {
+ return PK11TokenNotRemovable;
+ }
+ if (latency == 0) {
+ latency = PR_SecondsToInterval(5);
+ }
+ waitForRemoval = (PRBool)(event == PK11TokenRemovedOrChangedEvent);
+
+ if (series == 0) {
+ series = PK11_GetSlotSeries(slot);
+ }
+ while (PK11_IsPresent(slot) == waitForRemoval) {
+ PRIntervalTime interval;
+
+ if (waitForRemoval && series != PK11_GetSlotSeries(slot)) {
+ return PK11TokenChanged;
+ }
+ if (timeout == PR_INTERVAL_NO_WAIT) {
+ return waitForRemoval ? PK11TokenPresent : PK11TokenRemoved;
+ }
+ if (timeout != PR_INTERVAL_NO_TIMEOUT) {
+ interval = PR_IntervalNow();
+ if (!first_time_set) {
+ first_time = interval;
+ first_time_set = PR_TRUE;
+ }
+ if ((interval - first_time) > timeout) {
+ return waitForRemoval ? PK11TokenPresent : PK11TokenRemoved;
+ }
+ }
+ PR_Sleep(latency);
+ }
+ return waitForRemoval ? PK11TokenRemoved : PK11TokenPresent;
+}
diff --git a/security/nss/lib/pk11wrap/pk11util.c b/security/nss/lib/pk11wrap/pk11util.c
new file mode 100644
index 0000000000..2584ec3e8e
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11util.c
@@ -0,0 +1,1746 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/*
+ * Initialize the PCKS 11 subsystem
+ */
+#include "seccomon.h"
+#include "secmod.h"
+#include "nssilock.h"
+#include "secmodi.h"
+#include "secmodti.h"
+#include "pk11func.h"
+#include "pki3hack.h"
+#include "secerr.h"
+#include "dev.h"
+#include "dev3hack.h"
+#include "utilpars.h"
+#include "pkcs11uri.h"
+
+/* these are for displaying error messages */
+
+static SECMODModuleList *modules = NULL;
+static SECMODModuleList *modulesDB = NULL;
+static SECMODModuleList *modulesUnload = NULL;
+static SECMODModule *internalModule = NULL;
+static SECMODModule *defaultDBModule = NULL;
+static SECMODModule *pendingModule = NULL;
+static SECMODListLock *moduleLock = NULL;
+
+int secmod_PrivateModuleCount = 0;
+
+extern const PK11DefaultArrayEntry PK11_DefaultArray[];
+extern const int num_pk11_default_mechanisms;
+
+void
+SECMOD_Init()
+{
+ /* don't initialize twice */
+ if (moduleLock)
+ return;
+
+ moduleLock = SECMOD_NewListLock();
+ PK11_InitSlotLists();
+}
+
+SECStatus
+SECMOD_Shutdown()
+{
+ /* destroy the lock */
+ if (moduleLock) {
+ SECMOD_DestroyListLock(moduleLock);
+ moduleLock = NULL;
+ }
+ /* free the internal module */
+ if (internalModule) {
+ SECMOD_DestroyModule(internalModule);
+ internalModule = NULL;
+ }
+
+ /* free the default database module */
+ if (defaultDBModule) {
+ SECMOD_DestroyModule(defaultDBModule);
+ defaultDBModule = NULL;
+ }
+
+ /* destroy the list */
+ if (modules) {
+ SECMOD_DestroyModuleList(modules);
+ modules = NULL;
+ }
+
+ if (modulesDB) {
+ SECMOD_DestroyModuleList(modulesDB);
+ modulesDB = NULL;
+ }
+
+ if (modulesUnload) {
+ SECMOD_DestroyModuleList(modulesUnload);
+ modulesUnload = NULL;
+ }
+
+ /* make all the slots and the lists go away */
+ PK11_DestroySlotLists();
+
+ nss_DumpModuleLog();
+
+#ifdef DEBUG
+ if (PR_GetEnvSecure("NSS_STRICT_SHUTDOWN")) {
+ PORT_Assert(secmod_PrivateModuleCount == 0);
+ }
+#endif
+ if (secmod_PrivateModuleCount) {
+ PORT_SetError(SEC_ERROR_BUSY);
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+PRBool
+SECMOD_GetSystemFIPSEnabled(void)
+{
+#ifdef LINUX
+#ifndef NSS_FIPS_DISABLED
+ FILE *f;
+ char d;
+ size_t size;
+
+ f = fopen("/proc/sys/crypto/fips_enabled", "r");
+ if (!f) {
+ return PR_FALSE;
+ }
+
+ size = fread(&d, 1, sizeof(d), f);
+ fclose(f);
+ if (size != sizeof(d)) {
+ return PR_FALSE;
+ }
+ if (d == '1') {
+ return PR_TRUE;
+ }
+#endif
+#endif
+ return PR_FALSE;
+}
+
+/*
+ * retrieve the internal module
+ */
+SECMODModule *
+SECMOD_GetInternalModule(void)
+{
+ return internalModule;
+}
+
+SECStatus
+secmod_AddModuleToList(SECMODModuleList **moduleList, SECMODModule *newModule)
+{
+ SECMODModuleList *mlp, *newListElement, *last = NULL;
+
+ newListElement = SECMOD_NewModuleListElement();
+ if (newListElement == NULL) {
+ return SECFailure;
+ }
+
+ newListElement->module = SECMOD_ReferenceModule(newModule);
+
+ SECMOD_GetWriteLock(moduleLock);
+ /* Added it to the end (This is very inefficient, but Adding a module
+ * on the fly should happen maybe 2-3 times through the life this program
+ * on a given computer, and this list should be *SHORT*. */
+ for (mlp = *moduleList; mlp != NULL; mlp = mlp->next) {
+ last = mlp;
+ }
+
+ if (last == NULL) {
+ *moduleList = newListElement;
+ } else {
+ SECMOD_AddList(last, newListElement, NULL);
+ }
+ SECMOD_ReleaseWriteLock(moduleLock);
+ return SECSuccess;
+}
+
+SECStatus
+SECMOD_AddModuleToList(SECMODModule *newModule)
+{
+ if (newModule->internal && !internalModule) {
+ internalModule = SECMOD_ReferenceModule(newModule);
+ }
+ return secmod_AddModuleToList(&modules, newModule);
+}
+
+SECStatus
+SECMOD_AddModuleToDBOnlyList(SECMODModule *newModule)
+{
+ if (defaultDBModule && SECMOD_GetDefaultModDBFlag(newModule)) {
+ SECMOD_DestroyModule(defaultDBModule);
+ defaultDBModule = SECMOD_ReferenceModule(newModule);
+ } else if (defaultDBModule == NULL) {
+ defaultDBModule = SECMOD_ReferenceModule(newModule);
+ }
+ return secmod_AddModuleToList(&modulesDB, newModule);
+}
+
+SECStatus
+SECMOD_AddModuleToUnloadList(SECMODModule *newModule)
+{
+ return secmod_AddModuleToList(&modulesUnload, newModule);
+}
+
+/*
+ * get the list of PKCS11 modules that are available.
+ */
+SECMODModuleList *
+SECMOD_GetDefaultModuleList()
+{
+ return modules;
+}
+SECMODModuleList *
+SECMOD_GetDeadModuleList()
+{
+ return modulesUnload;
+}
+SECMODModuleList *
+SECMOD_GetDBModuleList()
+{
+ return modulesDB;
+}
+
+/*
+ * This lock protects the global module lists.
+ * it also protects changes to the slot array (module->slots[]) and slot count
+ * (module->slotCount) in each module. It is a read/write lock with multiple
+ * readers or one writer. Writes are uncommon.
+ * Because of legacy considerations protection of the slot array and count is
+ * only necessary in applications if the application calls
+ * SECMOD_UpdateSlotList() or SECMOD_WaitForAnyTokenEvent(), though all new
+ * applications are encouraged to acquire this lock when reading the
+ * slot array information directly.
+ */
+SECMODListLock *
+SECMOD_GetDefaultModuleListLock()
+{
+ return moduleLock;
+}
+
+/*
+ * find a module by name, and add a reference to it.
+ * return that module.
+ */
+SECMODModule *
+SECMOD_FindModule(const char *name)
+{
+ SECMODModuleList *mlp;
+ SECMODModule *module = NULL;
+
+ if (!moduleLock) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return module;
+ }
+ SECMOD_GetReadLock(moduleLock);
+ for (mlp = modules; mlp != NULL; mlp = mlp->next) {
+ if (PORT_Strcmp(name, mlp->module->commonName) == 0) {
+ module = mlp->module;
+ SECMOD_ReferenceModule(module);
+ break;
+ }
+ }
+ if (module) {
+ goto found;
+ }
+ for (mlp = modulesUnload; mlp != NULL; mlp = mlp->next) {
+ if (PORT_Strcmp(name, mlp->module->commonName) == 0) {
+ module = mlp->module;
+ SECMOD_ReferenceModule(module);
+ break;
+ }
+ }
+
+found:
+ SECMOD_ReleaseReadLock(moduleLock);
+
+ return module;
+}
+
+/*
+ * find a module by ID, and add a reference to it.
+ * return that module.
+ */
+SECMODModule *
+SECMOD_FindModuleByID(SECMODModuleID id)
+{
+ SECMODModuleList *mlp;
+ SECMODModule *module = NULL;
+
+ if (!moduleLock) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return module;
+ }
+ SECMOD_GetReadLock(moduleLock);
+ for (mlp = modules; mlp != NULL; mlp = mlp->next) {
+ if (id == mlp->module->moduleID) {
+ module = mlp->module;
+ SECMOD_ReferenceModule(module);
+ break;
+ }
+ }
+ SECMOD_ReleaseReadLock(moduleLock);
+ if (module == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MODULE);
+ }
+ return module;
+}
+
+/*
+ * find the function pointer.
+ */
+SECMODModule *
+secmod_FindModuleByFuncPtr(void *funcPtr)
+{
+ SECMODModuleList *mlp;
+ SECMODModule *module = NULL;
+
+ SECMOD_GetReadLock(moduleLock);
+ for (mlp = modules; mlp != NULL; mlp = mlp->next) {
+ /* paranoia, shouldn't ever happen */
+ if (!mlp->module) {
+ continue;
+ }
+ if (funcPtr == mlp->module->functionList) {
+ module = mlp->module;
+ SECMOD_ReferenceModule(module);
+ break;
+ }
+ }
+ SECMOD_ReleaseReadLock(moduleLock);
+ if (module == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MODULE);
+ }
+ return module;
+}
+
+/*
+ * Find the Slot based on ID and the module.
+ */
+PK11SlotInfo *
+SECMOD_FindSlotByID(SECMODModule *module, CK_SLOT_ID slotID)
+{
+ int i;
+ PK11SlotInfo *slot = NULL;
+
+ if (!moduleLock) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return slot;
+ }
+ SECMOD_GetReadLock(moduleLock);
+ for (i = 0; i < module->slotCount; i++) {
+ PK11SlotInfo *cSlot = module->slots[i];
+
+ if (cSlot->slotID == slotID) {
+ slot = PK11_ReferenceSlot(cSlot);
+ break;
+ }
+ }
+ SECMOD_ReleaseReadLock(moduleLock);
+
+ if (slot == NULL) {
+ PORT_SetError(SEC_ERROR_NO_SLOT_SELECTED);
+ }
+ return slot;
+}
+
+/*
+ * lookup the Slot module based on it's module ID and slot ID.
+ */
+PK11SlotInfo *
+SECMOD_LookupSlot(SECMODModuleID moduleID, CK_SLOT_ID slotID)
+{
+ SECMODModule *module;
+ PK11SlotInfo *slot;
+
+ module = SECMOD_FindModuleByID(moduleID);
+ if (module == NULL)
+ return NULL;
+
+ slot = SECMOD_FindSlotByID(module, slotID);
+ SECMOD_DestroyModule(module);
+ return slot;
+}
+
+/*
+ * find a module by name or module pointer and delete it off the module list.
+ * optionally remove it from secmod.db.
+ */
+SECStatus
+SECMOD_DeleteModuleEx(const char *name, SECMODModule *mod,
+ int *type, PRBool permdb)
+{
+ SECMODModuleList *mlp;
+ SECMODModuleList **mlpp;
+ SECStatus rv = SECFailure;
+
+ if (!moduleLock) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return rv;
+ }
+
+ *type = SECMOD_EXTERNAL;
+
+ SECMOD_GetWriteLock(moduleLock);
+ for (mlpp = &modules, mlp = modules;
+ mlp != NULL; mlpp = &mlp->next, mlp = *mlpp) {
+ if ((name && (PORT_Strcmp(name, mlp->module->commonName) == 0)) ||
+ mod == mlp->module) {
+ /* don't delete the internal module */
+ if (!mlp->module->internal) {
+ SECMOD_RemoveList(mlpp, mlp);
+ /* delete it after we release the lock */
+ rv = STAN_RemoveModuleFromDefaultTrustDomain(mlp->module);
+ } else if (mlp->module->isFIPS) {
+ *type = SECMOD_FIPS;
+ } else {
+ *type = SECMOD_INTERNAL;
+ }
+ break;
+ }
+ }
+ if (mlp) {
+ goto found;
+ }
+ /* not on the internal list, check the unload list */
+ for (mlpp = &modulesUnload, mlp = modulesUnload;
+ mlp != NULL; mlpp = &mlp->next, mlp = *mlpp) {
+ if ((name && (PORT_Strcmp(name, mlp->module->commonName) == 0)) ||
+ mod == mlp->module) {
+ /* don't delete the internal module */
+ if (!mlp->module->internal) {
+ SECMOD_RemoveList(mlpp, mlp);
+ rv = SECSuccess;
+ } else if (mlp->module->isFIPS) {
+ *type = SECMOD_FIPS;
+ } else {
+ *type = SECMOD_INTERNAL;
+ }
+ break;
+ }
+ }
+found:
+ SECMOD_ReleaseWriteLock(moduleLock);
+
+ if (rv == SECSuccess) {
+ if (permdb) {
+ SECMOD_DeletePermDB(mlp->module);
+ }
+ SECMOD_DestroyModuleListElement(mlp);
+ }
+ return rv;
+}
+
+/*
+ * find a module by name and delete it off the module list
+ */
+SECStatus
+SECMOD_DeleteModule(const char *name, int *type)
+{
+ return SECMOD_DeleteModuleEx(name, NULL, type, PR_TRUE);
+}
+
+/*
+ * find a module by name and delete it off the module list
+ */
+SECStatus
+SECMOD_DeleteInternalModule(const char *name)
+{
+#ifndef NSS_FIPS_DISABLED
+ SECMODModuleList *mlp;
+ SECMODModuleList **mlpp;
+#endif
+ SECStatus rv = SECFailure;
+
+ if (SECMOD_GetSystemFIPSEnabled() || pendingModule) {
+ PORT_SetError(SEC_ERROR_MODULE_STUCK);
+ return rv;
+ }
+ if (!moduleLock) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return rv;
+ }
+
+#ifdef NSS_FIPS_DISABLED
+ PORT_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR);
+ return rv;
+#else
+ SECMOD_GetWriteLock(moduleLock);
+ for (mlpp = &modules, mlp = modules;
+ mlp != NULL; mlpp = &mlp->next, mlp = *mlpp) {
+ if (PORT_Strcmp(name, mlp->module->commonName) == 0) {
+ /* don't delete the internal module */
+ if (mlp->module->internal) {
+ SECMOD_RemoveList(mlpp, mlp);
+ rv = STAN_RemoveModuleFromDefaultTrustDomain(mlp->module);
+ }
+ break;
+ }
+ }
+ SECMOD_ReleaseWriteLock(moduleLock);
+
+ if (rv == SECSuccess) {
+ SECMODModule *newModule, *oldModule;
+
+ if (mlp->module->isFIPS) {
+ newModule = SECMOD_CreateModule(NULL, SECMOD_INT_NAME,
+ NULL, SECMOD_INT_FLAGS);
+ } else {
+ newModule = SECMOD_CreateModule(NULL, SECMOD_FIPS_NAME,
+ NULL, SECMOD_FIPS_FLAGS);
+ }
+ if (newModule) {
+ PK11SlotInfo *slot;
+ newModule->libraryParams =
+ PORT_ArenaStrdup(newModule->arena, mlp->module->libraryParams);
+ /* if an explicit internal key slot has been set, reset it */
+ slot = pk11_SwapInternalKeySlot(NULL);
+ if (slot) {
+ secmod_SetInternalKeySlotFlag(newModule, PR_TRUE);
+ }
+ rv = SECMOD_AddModule(newModule);
+ if (rv != SECSuccess) {
+ /* load failed, restore the internal key slot */
+ pk11_SetInternalKeySlot(slot);
+ SECMOD_DestroyModule(newModule);
+ newModule = NULL;
+ }
+ /* free the old explicit internal key slot, we now have a new one */
+ if (slot) {
+ PK11_FreeSlot(slot);
+ }
+ }
+ if (newModule == NULL) {
+ SECMODModuleList *last = NULL, *mlp2;
+ /* we're in pretty deep trouble if this happens...Security
+ * not going to work well... try to put the old module back on
+ * the list */
+ SECMOD_GetWriteLock(moduleLock);
+ for (mlp2 = modules; mlp2 != NULL; mlp2 = mlp->next) {
+ last = mlp2;
+ }
+
+ if (last == NULL) {
+ modules = mlp;
+ } else {
+ SECMOD_AddList(last, mlp, NULL);
+ }
+ SECMOD_ReleaseWriteLock(moduleLock);
+ return SECFailure;
+ }
+ pendingModule = oldModule = internalModule;
+ internalModule = NULL;
+ SECMOD_DestroyModule(oldModule);
+ SECMOD_DeletePermDB(mlp->module);
+ SECMOD_DestroyModuleListElement(mlp);
+ internalModule = newModule; /* adopt the module */
+ }
+ return rv;
+#endif
+}
+
+SECStatus
+SECMOD_AddModule(SECMODModule *newModule)
+{
+ SECStatus rv;
+ SECMODModule *oldModule;
+
+ /* Test if a module w/ the same name already exists */
+ /* and return SECWouldBlock if so. */
+ /* We should probably add a new return value such as */
+ /* SECDublicateModule, but to minimize ripples, I'll */
+ /* give SECWouldBlock a new meaning */
+ if ((oldModule = SECMOD_FindModule(newModule->commonName)) != NULL) {
+ SECMOD_DestroyModule(oldModule);
+ return SECWouldBlock;
+ /* module already exists. */
+ }
+
+ rv = secmod_LoadPKCS11Module(newModule, NULL);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+
+ if (newModule->parent == NULL) {
+ newModule->parent = SECMOD_ReferenceModule(defaultDBModule);
+ }
+
+ SECMOD_AddPermDB(newModule);
+ SECMOD_AddModuleToList(newModule);
+
+ rv = STAN_AddModuleToDefaultTrustDomain(newModule);
+
+ return rv;
+}
+
+PK11SlotInfo *
+SECMOD_FindSlot(SECMODModule *module, const char *name)
+{
+ int i;
+ char *string;
+ PK11SlotInfo *retSlot = NULL;
+
+ if (!moduleLock) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return retSlot;
+ }
+ SECMOD_GetReadLock(moduleLock);
+ for (i = 0; i < module->slotCount; i++) {
+ PK11SlotInfo *slot = module->slots[i];
+
+ if (PK11_IsPresent(slot)) {
+ string = PK11_GetTokenName(slot);
+ } else {
+ string = PK11_GetSlotName(slot);
+ }
+ if (PORT_Strcmp(name, string) == 0) {
+ retSlot = PK11_ReferenceSlot(slot);
+ break;
+ }
+ }
+ SECMOD_ReleaseReadLock(moduleLock);
+
+ if (retSlot == NULL) {
+ PORT_SetError(SEC_ERROR_NO_SLOT_SELECTED);
+ }
+ return retSlot;
+}
+
+SECStatus
+PK11_GetModInfo(SECMODModule *mod, CK_INFO *info)
+{
+ CK_RV crv;
+
+ if (mod->functionList == NULL)
+ return SECFailure;
+ crv = PK11_GETTAB(mod)->C_GetInfo(info);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ }
+ return (crv == CKR_OK) ? SECSuccess : SECFailure;
+}
+
+char *
+PK11_GetModuleURI(SECMODModule *mod)
+{
+ CK_INFO info;
+ PK11URI *uri;
+ char *ret = NULL;
+ PK11URIAttribute attrs[3];
+ size_t nattrs = 0;
+ char libraryManufacturer[32 + 1], libraryDescription[32 + 1], libraryVersion[8];
+
+ if (PK11_GetModInfo(mod, &info) == SECFailure) {
+ return NULL;
+ }
+
+ PK11_MakeString(NULL, libraryManufacturer, (char *)info.manufacturerID,
+ sizeof(info.manufacturerID));
+ if (*libraryManufacturer != '\0') {
+ attrs[nattrs].name = PK11URI_PATTR_LIBRARY_MANUFACTURER;
+ attrs[nattrs].value = libraryManufacturer;
+ nattrs++;
+ }
+
+ PK11_MakeString(NULL, libraryDescription, (char *)info.libraryDescription,
+ sizeof(info.libraryDescription));
+ if (*libraryDescription != '\0') {
+ attrs[nattrs].name = PK11URI_PATTR_LIBRARY_DESCRIPTION;
+ attrs[nattrs].value = libraryDescription;
+ nattrs++;
+ }
+
+ PR_snprintf(libraryVersion, sizeof(libraryVersion), "%d.%d",
+ info.libraryVersion.major, info.libraryVersion.minor);
+ attrs[nattrs].name = PK11URI_PATTR_LIBRARY_VERSION;
+ attrs[nattrs].value = libraryVersion;
+ nattrs++;
+
+ uri = PK11URI_CreateURI(attrs, nattrs, NULL, 0);
+ if (uri == NULL) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return NULL;
+ }
+
+ ret = PK11URI_FormatURI(NULL, uri);
+ PK11URI_DestroyURI(uri);
+ if (ret == NULL) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return NULL;
+ }
+
+ return ret;
+}
+
+/* Determine if we have the FIP's module loaded as the default
+ * module to trigger other bogus FIPS requirements in PKCS #12 and
+ * SSL
+ */
+PRBool
+PK11_IsFIPS(void)
+{
+ SECMODModule *mod = SECMOD_GetInternalModule();
+
+ if (mod && mod->internal) {
+ return mod->isFIPS;
+ }
+
+ return PR_FALSE;
+}
+
+/* combines NewModule() & AddModule */
+/* give a string for the module name & the full-path for the dll, */
+/* installs the PKCS11 module & update registry */
+SECStatus
+SECMOD_AddNewModuleEx(const char *moduleName, const char *dllPath,
+ unsigned long defaultMechanismFlags,
+ unsigned long cipherEnableFlags,
+ char *modparms, char *nssparms)
+{
+ SECMODModule *module;
+ SECStatus result = SECFailure;
+ int s, i;
+ PK11SlotInfo *slot;
+
+ PR_SetErrorText(0, NULL);
+ if (!moduleLock) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return result;
+ }
+
+ module = SECMOD_CreateModule(dllPath, moduleName, modparms, nssparms);
+
+ if (module == NULL) {
+ return result;
+ }
+
+ if (module->dllName != NULL) {
+ if (module->dllName[0] != 0) {
+ result = SECMOD_AddModule(module);
+ if (result == SECSuccess) {
+ /* turn on SSL cipher enable flags */
+ module->ssl[0] = cipherEnableFlags;
+
+ SECMOD_GetReadLock(moduleLock);
+ /* check each slot to turn on appropriate mechanisms */
+ for (s = 0; s < module->slotCount; s++) {
+ slot = (module->slots)[s];
+ /* for each possible mechanism */
+ for (i = 0; i < num_pk11_default_mechanisms; i++) {
+ /* we are told to turn it on by default ? */
+ PRBool add =
+ (PK11_DefaultArray[i].flag & defaultMechanismFlags) ? PR_TRUE : PR_FALSE;
+ result = PK11_UpdateSlotAttribute(slot,
+ &(PK11_DefaultArray[i]), add);
+ if (result != SECSuccess) {
+ SECMOD_ReleaseReadLock(moduleLock);
+ SECMOD_DestroyModule(module);
+ return result;
+ }
+ } /* for each mechanism */
+ /* disable each slot if the defaultFlags say so */
+ if (defaultMechanismFlags & PK11_DISABLE_FLAG) {
+ PK11_UserDisableSlot(slot);
+ }
+ } /* for each slot of this module */
+ SECMOD_ReleaseReadLock(moduleLock);
+
+ /* delete and re-add module in order to save changes
+ * to the module */
+ result = SECMOD_UpdateModule(module);
+ }
+ }
+ }
+ SECMOD_DestroyModule(module);
+ return result;
+}
+
+SECStatus
+SECMOD_AddNewModule(const char *moduleName, const char *dllPath,
+ unsigned long defaultMechanismFlags,
+ unsigned long cipherEnableFlags)
+{
+ return SECMOD_AddNewModuleEx(moduleName, dllPath, defaultMechanismFlags,
+ cipherEnableFlags,
+ NULL, NULL); /* don't pass module or nss params */
+}
+
+SECStatus
+SECMOD_UpdateModule(SECMODModule *module)
+{
+ SECStatus result;
+
+ result = SECMOD_DeletePermDB(module);
+
+ if (result == SECSuccess) {
+ result = SECMOD_AddPermDB(module);
+ }
+ return result;
+}
+
+/* Public & Internal(Security Library) representation of
+ * encryption mechanism flags conversion */
+
+/* Currently, the only difference is that internal representation
+ * puts RANDOM_FLAG at bit 31 (Most-significant bit), but
+ * public representation puts this bit at bit 28
+ */
+unsigned long
+SECMOD_PubMechFlagstoInternal(unsigned long publicFlags)
+{
+ unsigned long internalFlags = publicFlags;
+
+ if (publicFlags & PUBLIC_MECH_RANDOM_FLAG) {
+ internalFlags &= ~PUBLIC_MECH_RANDOM_FLAG;
+ internalFlags |= SECMOD_RANDOM_FLAG;
+ }
+ return internalFlags;
+}
+
+unsigned long
+SECMOD_InternaltoPubMechFlags(unsigned long internalFlags)
+{
+ unsigned long publicFlags = internalFlags;
+
+ if (internalFlags & SECMOD_RANDOM_FLAG) {
+ publicFlags &= ~SECMOD_RANDOM_FLAG;
+ publicFlags |= PUBLIC_MECH_RANDOM_FLAG;
+ }
+ return publicFlags;
+}
+
+/* Public & Internal(Security Library) representation of */
+/* cipher flags conversion */
+/* Note: currently they are just stubs */
+unsigned long
+SECMOD_PubCipherFlagstoInternal(unsigned long publicFlags)
+{
+ return publicFlags;
+}
+
+unsigned long
+SECMOD_InternaltoPubCipherFlags(unsigned long internalFlags)
+{
+ return internalFlags;
+}
+
+/* Funtion reports true if module of modType is installed/configured */
+PRBool
+SECMOD_IsModulePresent(unsigned long int pubCipherEnableFlags)
+{
+ PRBool result = PR_FALSE;
+ SECMODModuleList *mods;
+
+ if (!moduleLock) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return result;
+ }
+ SECMOD_GetReadLock(moduleLock);
+ mods = SECMOD_GetDefaultModuleList();
+ for (; mods != NULL; mods = mods->next) {
+ if (mods->module->ssl[0] &
+ SECMOD_PubCipherFlagstoInternal(pubCipherEnableFlags)) {
+ result = PR_TRUE;
+ }
+ }
+
+ SECMOD_ReleaseReadLock(moduleLock);
+ return result;
+}
+
+/* create a new ModuleListElement */
+SECMODModuleList *
+SECMOD_NewModuleListElement(void)
+{
+ SECMODModuleList *newModList;
+
+ newModList = (SECMODModuleList *)PORT_Alloc(sizeof(SECMODModuleList));
+ if (newModList) {
+ newModList->next = NULL;
+ newModList->module = NULL;
+ }
+ return newModList;
+}
+
+/*
+ * make a new reference to a module so It doesn't go away on us
+ */
+SECMODModule *
+SECMOD_ReferenceModule(SECMODModule *module)
+{
+ PZ_Lock(module->refLock);
+ PORT_Assert(module->refCount > 0);
+
+ module->refCount++;
+ PZ_Unlock(module->refLock);
+ return module;
+}
+
+/* destroy an existing module */
+void
+SECMOD_DestroyModule(SECMODModule *module)
+{
+ PRBool willfree = PR_FALSE;
+ int slotCount;
+ int i;
+
+ PZ_Lock(module->refLock);
+ if (module->refCount-- == 1) {
+ willfree = PR_TRUE;
+ }
+ PORT_Assert(willfree || (module->refCount > 0));
+ PZ_Unlock(module->refLock);
+
+ if (!willfree) {
+ return;
+ }
+
+ if (module->parent != NULL) {
+ SECMODModule *parent = module->parent;
+ /* paranoia, don't loop forever if the modules are looped */
+ module->parent = NULL;
+ SECMOD_DestroyModule(parent);
+ }
+
+ /* slots can't really disappear until our module starts freeing them,
+ * so this check is safe */
+ slotCount = module->slotCount;
+ if (slotCount == 0) {
+ SECMOD_SlotDestroyModule(module, PR_FALSE);
+ return;
+ }
+
+ /* now free all out slots, when they are done, they will cause the
+ * module to disappear altogether */
+ for (i = 0; i < slotCount; i++) {
+ if (!module->slots[i]->disabled) {
+ PK11_ClearSlotList(module->slots[i]);
+ }
+ PK11_FreeSlot(module->slots[i]);
+ }
+ /* WARNING: once the last slot has been freed is it possible (even likely)
+ * that module is no more... touching it now is a good way to go south */
+}
+
+/* we can only get here if we've destroyed the module, or some one has
+ * erroneously freed a slot that wasn't referenced. */
+void
+SECMOD_SlotDestroyModule(SECMODModule *module, PRBool fromSlot)
+{
+ PRBool willfree = PR_FALSE;
+ if (fromSlot) {
+ PORT_Assert(module->refCount == 0);
+ PZ_Lock(module->refLock);
+ if (module->slotCount-- == 1) {
+ willfree = PR_TRUE;
+ }
+ PORT_Assert(willfree || (module->slotCount > 0));
+ PZ_Unlock(module->refLock);
+ if (!willfree)
+ return;
+ }
+
+ if (module == pendingModule) {
+ pendingModule = NULL;
+ }
+
+ if (module->loaded) {
+ SECMOD_UnloadModule(module);
+ }
+ PZ_DestroyLock(module->refLock);
+ PORT_FreeArena(module->arena, PR_FALSE);
+ secmod_PrivateModuleCount--;
+}
+
+/* destroy a list element
+ * this destroys a single element, and returns the next element
+ * on the chain. It makes it easy to implement for loops to delete
+ * the chain. It also make deleting a single element easy */
+SECMODModuleList *
+SECMOD_DestroyModuleListElement(SECMODModuleList *element)
+{
+ SECMODModuleList *next = element->next;
+
+ if (element->module) {
+ SECMOD_DestroyModule(element->module);
+ element->module = NULL;
+ }
+ PORT_Free(element);
+ return next;
+}
+
+/*
+ * Destroy an entire module list
+ */
+void
+SECMOD_DestroyModuleList(SECMODModuleList *list)
+{
+ SECMODModuleList *lp;
+
+ for (lp = list; lp != NULL; lp = SECMOD_DestroyModuleListElement(lp))
+ ;
+}
+
+PRBool
+SECMOD_CanDeleteInternalModule(void)
+{
+#ifdef NSS_FIPS_DISABLED
+ return PR_FALSE;
+#else
+ return (PRBool)((pendingModule == NULL) && !SECMOD_GetSystemFIPSEnabled());
+#endif
+}
+
+/*
+ * check to see if the module has added new slots. PKCS 11 v2.20 allows for
+ * modules to add new slots, but never remove them. Slots cannot be added
+ * between a call to C_GetSlotLlist(Flag, NULL, &count) and the subsequent
+ * C_GetSlotList(flag, &data, &count) so that the array doesn't accidently
+ * grow on the caller. It is permissible for the slots to increase between
+ * successive calls with NULL to get the size.
+ *
+ * Caller must not hold a module list read lock.
+ */
+SECStatus
+SECMOD_UpdateSlotList(SECMODModule *mod)
+{
+ CK_RV crv;
+ CK_ULONG count;
+ CK_ULONG i, oldCount;
+ PRBool freeRef = PR_FALSE;
+ void *mark = NULL;
+ CK_ULONG *slotIDs = NULL;
+ PK11SlotInfo **newSlots = NULL;
+ PK11SlotInfo **oldSlots = NULL;
+
+ if (!moduleLock) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return SECFailure;
+ }
+
+ /* C_GetSlotList is not a session function, make sure
+ * calls are serialized */
+ PZ_Lock(mod->refLock);
+ freeRef = PR_TRUE;
+ /* see if the number of slots have changed */
+ crv = PK11_GETTAB(mod)->C_GetSlotList(PR_FALSE, NULL, &count);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ goto loser;
+ }
+ /* nothing new, blow out early, we want this function to be quick
+ * and cheap in the normal case */
+ if (count == mod->slotCount) {
+ PZ_Unlock(mod->refLock);
+ return SECSuccess;
+ }
+ if (count < (CK_ULONG)mod->slotCount) {
+ /* shouldn't happen with a properly functioning PKCS #11 module */
+ PORT_SetError(SEC_ERROR_INCOMPATIBLE_PKCS11);
+ goto loser;
+ }
+
+ /* get the new slot list */
+ slotIDs = PORT_NewArray(CK_SLOT_ID, count);
+ if (slotIDs == NULL) {
+ goto loser;
+ }
+
+ crv = PK11_GETTAB(mod)->C_GetSlotList(PR_FALSE, slotIDs, &count);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ goto loser;
+ }
+ freeRef = PR_FALSE;
+ PZ_Unlock(mod->refLock);
+ mark = PORT_ArenaMark(mod->arena);
+ if (mark == NULL) {
+ goto loser;
+ }
+ newSlots = PORT_ArenaZNewArray(mod->arena, PK11SlotInfo *, count);
+
+ /* walk down the new slot ID list returned from the module. We keep
+ * the old slots which match a returned ID, and we initialize the new
+ * slots. */
+ for (i = 0; i < count; i++) {
+ PK11SlotInfo *slot = SECMOD_FindSlotByID(mod, slotIDs[i]);
+
+ if (!slot) {
+ /* we have a new slot create a new slot data structure */
+ slot = PK11_NewSlotInfo(mod);
+ if (!slot) {
+ goto loser;
+ }
+ PK11_InitSlot(mod, slotIDs[i], slot);
+ STAN_InitTokenForSlotInfo(NULL, slot);
+ }
+ newSlots[i] = slot;
+ }
+ STAN_ResetTokenInterator(NULL);
+ PORT_Free(slotIDs);
+ slotIDs = NULL;
+ PORT_ArenaUnmark(mod->arena, mark);
+
+ /* until this point we're still using the old slot list. Now we update
+ * module slot list. We update the slots (array) first then the count,
+ * since we've already guarrenteed that count has increased (just in case
+ * someone is looking at the slots field of module without holding the
+ * moduleLock */
+ SECMOD_GetWriteLock(moduleLock);
+ oldCount = mod->slotCount;
+ oldSlots = mod->slots;
+ mod->slots = newSlots; /* typical arena 'leak'... old mod->slots is
+ * allocated out of the module arena and won't
+ * be freed until the module is freed */
+ mod->slotCount = count;
+ SECMOD_ReleaseWriteLock(moduleLock);
+ /* free our old references before forgetting about oldSlot*/
+ for (i = 0; i < oldCount; i++) {
+ PK11_FreeSlot(oldSlots[i]);
+ }
+ return SECSuccess;
+
+loser:
+ if (freeRef) {
+ PZ_Unlock(mod->refLock);
+ }
+ if (slotIDs) {
+ PORT_Free(slotIDs);
+ }
+ /* free all the slots we allocated. newSlots are part of the
+ * mod arena. NOTE: the newSlots array contain both new and old
+ * slots, but we kept a reference to the old slots when we built the new
+ * array, so we need to free all the slots in newSlots array. */
+ if (newSlots) {
+ for (i = 0; i < count; i++) {
+ if (newSlots[i] == NULL) {
+ break; /* hit the last one */
+ }
+ PK11_FreeSlot(newSlots[i]);
+ }
+ }
+ /* must come after freeing newSlots */
+ if (mark) {
+ PORT_ArenaRelease(mod->arena, mark);
+ }
+ return SECFailure;
+}
+
+/*
+ * this handles modules that do not support C_WaitForSlotEvent().
+ * The internal flags are stored. Note that C_WaitForSlotEvent() does not
+ * have a timeout, so we don't have one for handleWaitForSlotEvent() either.
+ */
+PK11SlotInfo *
+secmod_HandleWaitForSlotEvent(SECMODModule *mod, unsigned long flags,
+ PRIntervalTime latency)
+{
+ PRBool removableSlotsFound = PR_FALSE;
+ int i;
+ int error = SEC_ERROR_NO_EVENT;
+
+ if (!moduleLock) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return NULL;
+ }
+ PZ_Lock(mod->refLock);
+ if (mod->evControlMask & SECMOD_END_WAIT) {
+ mod->evControlMask &= ~SECMOD_END_WAIT;
+ PZ_Unlock(mod->refLock);
+ PORT_SetError(SEC_ERROR_NO_EVENT);
+ return NULL;
+ }
+ mod->evControlMask |= SECMOD_WAIT_SIMULATED_EVENT;
+ while (mod->evControlMask & SECMOD_WAIT_SIMULATED_EVENT) {
+ PZ_Unlock(mod->refLock);
+ /* now is a good time to see if new slots have been added */
+ SECMOD_UpdateSlotList(mod);
+
+ /* loop through all the slots on a module */
+ SECMOD_GetReadLock(moduleLock);
+ for (i = 0; i < mod->slotCount; i++) {
+ PK11SlotInfo *slot = mod->slots[i];
+ PRUint16 series;
+ PRBool present;
+
+ /* perm modules do not change */
+ if (slot->isPerm) {
+ continue;
+ }
+ removableSlotsFound = PR_TRUE;
+ /* simulate the PKCS #11 module flags. are the flags different
+ * from the last time we called? */
+ series = slot->series;
+ present = PK11_IsPresent(slot);
+ if ((slot->flagSeries != series) || (slot->flagState != present)) {
+ slot->flagState = present;
+ slot->flagSeries = series;
+ SECMOD_ReleaseReadLock(moduleLock);
+ PZ_Lock(mod->refLock);
+ mod->evControlMask &= ~SECMOD_END_WAIT;
+ PZ_Unlock(mod->refLock);
+ return PK11_ReferenceSlot(slot);
+ }
+ }
+ SECMOD_ReleaseReadLock(moduleLock);
+ /* if everything was perm modules, don't lock up forever */
+ if ((mod->slotCount != 0) && !removableSlotsFound) {
+ error = SEC_ERROR_NO_SLOT_SELECTED;
+ PZ_Lock(mod->refLock);
+ break;
+ }
+ if (flags & CKF_DONT_BLOCK) {
+ PZ_Lock(mod->refLock);
+ break;
+ }
+ PR_Sleep(latency);
+ PZ_Lock(mod->refLock);
+ }
+ mod->evControlMask &= ~SECMOD_END_WAIT;
+ PZ_Unlock(mod->refLock);
+ PORT_SetError(error);
+ return NULL;
+}
+
+/*
+ * this function waits for a token event on any slot of a given module
+ * This function should not be called from more than one thread of the
+ * same process (though other threads can make other library calls
+ * on this module while this call is blocked).
+ */
+PK11SlotInfo *
+SECMOD_WaitForAnyTokenEvent(SECMODModule *mod, unsigned long flags,
+ PRIntervalTime latency)
+{
+ CK_SLOT_ID id;
+ CK_RV crv;
+ PK11SlotInfo *slot;
+
+ if (!pk11_getFinalizeModulesOption() ||
+ ((mod->cryptokiVersion.major == 2) &&
+ (mod->cryptokiVersion.minor < 1))) {
+ /* if we are sharing the module with other software in our
+ * address space, we can't reliably use C_WaitForSlotEvent(),
+ * and if the module is version 2.0, C_WaitForSlotEvent() doesn't
+ * exist */
+ return secmod_HandleWaitForSlotEvent(mod, flags, latency);
+ }
+ /* first the the PKCS #11 call */
+ PZ_Lock(mod->refLock);
+ if (mod->evControlMask & SECMOD_END_WAIT) {
+ goto end_wait;
+ }
+ mod->evControlMask |= SECMOD_WAIT_PKCS11_EVENT;
+ PZ_Unlock(mod->refLock);
+ crv = PK11_GETTAB(mod)->C_WaitForSlotEvent(flags, &id, NULL);
+ PZ_Lock(mod->refLock);
+ mod->evControlMask &= ~SECMOD_WAIT_PKCS11_EVENT;
+ /* if we are in end wait, short circuit now, don't even risk
+ * going into secmod_HandleWaitForSlotEvent */
+ if (mod->evControlMask & SECMOD_END_WAIT) {
+ goto end_wait;
+ }
+ PZ_Unlock(mod->refLock);
+ if (crv == CKR_FUNCTION_NOT_SUPPORTED) {
+ /* module doesn't support that call, simulate it */
+ return secmod_HandleWaitForSlotEvent(mod, flags, latency);
+ }
+ if (crv != CKR_OK) {
+ /* we can get this error if finalize was called while we were
+ * still running. This is the only way to force a C_WaitForSlotEvent()
+ * to return in PKCS #11. In this case, just return that there
+ * was no event. */
+ if (crv == CKR_CRYPTOKI_NOT_INITIALIZED) {
+ PORT_SetError(SEC_ERROR_NO_EVENT);
+ } else {
+ PORT_SetError(PK11_MapError(crv));
+ }
+ return NULL;
+ }
+ slot = SECMOD_FindSlotByID(mod, id);
+ if (slot == NULL) {
+ /* possibly a new slot that was added? */
+ SECMOD_UpdateSlotList(mod);
+ slot = SECMOD_FindSlotByID(mod, id);
+ }
+ /* if we are in the delay period for the "isPresent" call, reset
+ * the delay since we know things have probably changed... */
+ if (slot) {
+ NSSToken *nssToken = PK11Slot_GetNSSToken(slot);
+ if (nssToken) {
+ if (nssToken->slot) {
+ nssSlot_ResetDelay(nssToken->slot);
+ }
+ (void)nssToken_Destroy(nssToken);
+ }
+ }
+ return slot;
+
+/* must be called with the lock on. */
+end_wait:
+ mod->evControlMask &= ~SECMOD_END_WAIT;
+ PZ_Unlock(mod->refLock);
+ PORT_SetError(SEC_ERROR_NO_EVENT);
+ return NULL;
+}
+
+/*
+ * This function "wakes up" WaitForAnyTokenEvent. It's a pretty drastic
+ * function, possibly bringing down the pkcs #11 module in question. This
+ * should be OK because 1) it does reinitialize, and 2) it should only be
+ * called when we are on our way to tear the whole system down anyway.
+ */
+SECStatus
+SECMOD_CancelWait(SECMODModule *mod)
+{
+ unsigned long controlMask;
+ SECStatus rv = SECSuccess;
+ CK_RV crv;
+
+ PZ_Lock(mod->refLock);
+ mod->evControlMask |= SECMOD_END_WAIT;
+ controlMask = mod->evControlMask;
+ if (controlMask & SECMOD_WAIT_PKCS11_EVENT) {
+ if (!pk11_getFinalizeModulesOption()) {
+ /* can't get here unless pk11_getFinalizeModulesOption is set */
+ PORT_Assert(0);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ rv = SECFailure;
+ goto loser;
+ }
+ /* NOTE: this call will drop all transient keys, in progress
+ * operations, and any authentication. This is the only documented
+ * way to get WaitForSlotEvent to return. Also note: for non-thread
+ * safe tokens, we need to hold the module lock, this is not yet at
+ * system shutdown/startup time, so we need to protect these calls */
+ crv = PK11_GETTAB(mod)->C_Finalize(NULL);
+ /* ok, we slammed the module down, now we need to reinit it in case
+ * we intend to use it again */
+ if (CKR_OK == crv) {
+ PRBool alreadyLoaded;
+ secmod_ModuleInit(mod, NULL, &alreadyLoaded);
+ } else {
+ /* Finalized failed for some reason, notify the application
+ * so maybe it has a prayer of recovering... */
+ PORT_SetError(PK11_MapError(crv));
+ rv = SECFailure;
+ }
+ } else if (controlMask & SECMOD_WAIT_SIMULATED_EVENT) {
+ mod->evControlMask &= ~SECMOD_WAIT_SIMULATED_EVENT;
+ /* Simulated events will eventually timeout
+ * and wake up in the loop */
+ }
+loser:
+ PZ_Unlock(mod->refLock);
+ return rv;
+}
+
+/*
+ * check to see if the module has removable slots that we may need to
+ * watch for.
+ */
+PRBool
+SECMOD_HasRemovableSlots(SECMODModule *mod)
+{
+ PRBool ret = PR_FALSE;
+ if (!moduleLock) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return ret;
+ }
+ SECMOD_GetReadLock(moduleLock);
+ ret = SECMOD_LockedModuleHasRemovableSlots(mod);
+ SECMOD_ReleaseReadLock(moduleLock);
+ return ret;
+}
+
+PRBool
+SECMOD_LockedModuleHasRemovableSlots(SECMODModule *mod)
+{
+ int i;
+ PRBool ret;
+ if (mod->slotCount == 0) {
+ return PR_TRUE;
+ }
+
+ ret = PR_FALSE;
+ for (i = 0; i < mod->slotCount; i++) {
+ PK11SlotInfo *slot = mod->slots[i];
+ /* perm modules are not inserted or removed */
+ if (slot->isPerm) {
+ continue;
+ }
+ ret = PR_TRUE;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * helper function to actually create and destroy user defined slots
+ */
+static SECStatus
+secmod_UserDBOp(PK11SlotInfo *slot, CK_OBJECT_CLASS objClass,
+ const char *sendSpec)
+{
+ CK_OBJECT_HANDLE dummy;
+ CK_ATTRIBUTE template[2];
+ CK_ATTRIBUTE *attrs = template;
+ CK_RV crv;
+
+ PK11_SETATTRS(attrs, CKA_CLASS, &objClass, sizeof(objClass));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_NSS_MODULE_SPEC, (unsigned char *)sendSpec,
+ strlen(sendSpec) + 1);
+ attrs++;
+
+ PORT_Assert(attrs - template <= 2);
+
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_CreateNewObject(slot, slot->session,
+ template, attrs - template, PR_FALSE, &dummy);
+ PK11_ExitSlotMonitor(slot);
+
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ return SECMOD_UpdateSlotList(slot->module);
+}
+
+/*
+ * return true if the selected slot ID is not present or doesn't exist
+ */
+static PRBool
+secmod_SlotIsEmpty(SECMODModule *mod, CK_SLOT_ID slotID)
+{
+ PK11SlotInfo *slot = SECMOD_LookupSlot(mod->moduleID, slotID);
+ if (slot) {
+ PRBool present = PK11_IsPresent(slot);
+ PK11_FreeSlot(slot);
+ if (present) {
+ return PR_FALSE;
+ }
+ }
+ /* it doesn't exist or isn't present, it's available */
+ return PR_TRUE;
+}
+
+/*
+ * Find an unused slot id in module.
+ */
+static CK_SLOT_ID
+secmod_FindFreeSlot(SECMODModule *mod)
+{
+ CK_SLOT_ID i, minSlotID, maxSlotID;
+
+ /* look for a free slot id on the internal module */
+ if (mod->internal && mod->isFIPS) {
+ minSlotID = SFTK_MIN_FIPS_USER_SLOT_ID;
+ maxSlotID = SFTK_MAX_FIPS_USER_SLOT_ID;
+ } else {
+ minSlotID = SFTK_MIN_USER_SLOT_ID;
+ maxSlotID = SFTK_MAX_USER_SLOT_ID;
+ }
+ for (i = minSlotID; i < maxSlotID; i++) {
+ if (secmod_SlotIsEmpty(mod, i)) {
+ return i;
+ }
+ }
+ PORT_SetError(SEC_ERROR_NO_SLOT_SELECTED);
+ return (CK_SLOT_ID)-1;
+}
+
+/*
+ * Attempt to open a new slot.
+ *
+ * This works the same os OpenUserDB except it can be called against
+ * any module that understands the softoken protocol for opening new
+ * slots, not just the softoken itself. If the selected module does not
+ * understand the protocol, C_CreateObject will fail with
+ * CKR_INVALID_ATTRIBUTE, and SECMOD_OpenNewSlot will return NULL and set
+ * SEC_ERROR_BAD_DATA.
+ *
+ * NewSlots can be closed with SECMOD_CloseUserDB();
+ *
+ * Modulespec is module dependent.
+ */
+PK11SlotInfo *
+SECMOD_OpenNewSlot(SECMODModule *mod, const char *moduleSpec)
+{
+ CK_SLOT_ID slotID = 0;
+ PK11SlotInfo *slot;
+ char *escSpec;
+ char *sendSpec;
+ SECStatus rv;
+
+ slotID = secmod_FindFreeSlot(mod);
+ if (slotID == (CK_SLOT_ID)-1) {
+ return NULL;
+ }
+
+ if (mod->slotCount == 0) {
+ return NULL;
+ }
+
+ /* just grab the first slot in the module, any present slot should work */
+ slot = PK11_ReferenceSlot(mod->slots[0]);
+ if (slot == NULL) {
+ return NULL;
+ }
+
+ /* we've found the slot, now build the moduleSpec */
+ escSpec = NSSUTIL_DoubleEscape(moduleSpec, '>', ']');
+ if (escSpec == NULL) {
+ PK11_FreeSlot(slot);
+ return NULL;
+ }
+ sendSpec = PR_smprintf("tokens=[0x%x=<%s>]", slotID, escSpec);
+ PORT_Free(escSpec);
+
+ if (sendSpec == NULL) {
+ /* PR_smprintf does not set SEC_ERROR_NO_MEMORY on failure. */
+ PK11_FreeSlot(slot);
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+ rv = secmod_UserDBOp(slot, CKO_NSS_NEWSLOT, sendSpec);
+ PR_smprintf_free(sendSpec);
+ PK11_FreeSlot(slot);
+ if (rv != SECSuccess) {
+ return NULL;
+ }
+
+ slot = SECMOD_FindSlotByID(mod, slotID);
+ if (slot) {
+ /* if we are in the delay period for the "isPresent" call, reset
+ * the delay since we know things have probably changed... */
+ NSSToken *nssToken = PK11Slot_GetNSSToken(slot);
+ if (nssToken) {
+ if (nssToken->slot) {
+ nssSlot_ResetDelay(nssToken->slot);
+ }
+ (void)nssToken_Destroy(nssToken);
+ }
+ /* force the slot info structures to properly reset */
+ (void)PK11_IsPresent(slot);
+ }
+ return slot;
+}
+
+/*
+ * given a module spec, find the slot in the module for it.
+ */
+PK11SlotInfo *
+secmod_FindSlotFromModuleSpec(const char *moduleSpec, SECMODModule *module)
+{
+ CK_SLOT_ID slot_id = secmod_GetSlotIDFromModuleSpec(moduleSpec, module);
+ if (slot_id == -1) {
+ return NULL;
+ }
+
+ return SECMOD_FindSlotByID(module, slot_id);
+}
+
+/*
+ * Open a new database using the softoken. The caller is responsible for making
+ * sure the module spec is correct and usable. The caller should ask for one
+ * new database per call if the caller wants to get meaningful information
+ * about the new database.
+ *
+ * moduleSpec is the same data that you would pass to softoken at
+ * initialization time under the 'tokens' options. For example, if you were
+ * to specify tokens=<0x4=[configdir='./mybackup' tokenDescription='Backup']>
+ * You would specify "configdir='./mybackup' tokenDescription='Backup'" as your
+ * module spec here. The slot ID will be calculated for you by
+ * SECMOD_OpenUserDB().
+ *
+ * Typical parameters here are configdir, tokenDescription and flags.
+ *
+ * a Full list is below:
+ *
+ *
+ * configDir - The location of the databases for this token. If configDir is
+ * not specified, and noCertDB and noKeyDB is not specified, the load
+ * will fail.
+ * certPrefix - Cert prefix for this token.
+ * keyPrefix - Prefix for the key database for this token. (if not specified,
+ * certPrefix will be used).
+ * tokenDescription - The label value for this token returned in the
+ * CK_TOKEN_INFO structure with an internationalize string (UTF8).
+ * This value will be truncated at 32 bytes (no NULL, partial UTF8
+ * characters dropped). You should specify a user friendly name here
+ * as this is the value the token will be referred to in most
+ * application UI's. You should make sure tokenDescription is unique.
+ * slotDescription - The slotDescription value for this token returned
+ * in the CK_SLOT_INFO structure with an internationalize string
+ * (UTF8). This value will be truncated at 64 bytes (no NULL, partial
+ * UTF8 characters dropped). This name will not change after the
+ * database is closed. It should have some number to make this unique.
+ * minPWLen - minimum password length for this token.
+ * flags - comma separated list of flag values, parsed case-insensitive.
+ * Valid flags are:
+ * readOnly - Databases should be opened read only.
+ * noCertDB - Don't try to open a certificate database.
+ * noKeyDB - Don't try to open a key database.
+ * forceOpen - Don't fail to initialize the token if the
+ * databases could not be opened.
+ * passwordRequired - zero length passwords are not acceptable
+ * (valid only if there is a keyDB).
+ * optimizeSpace - allocate smaller hash tables and lock tables.
+ * When this flag is not specified, Softoken will allocate
+ * large tables to prevent lock contention.
+ */
+PK11SlotInfo *
+SECMOD_OpenUserDB(const char *moduleSpec)
+{
+ SECMODModule *mod;
+ SECMODConfigList *conflist = NULL;
+ int count = 0;
+
+ if (moduleSpec == NULL) {
+ return NULL;
+ }
+
+ /* NOTE: unlike most PK11 function, this does not return a reference
+ * to the module */
+ mod = SECMOD_GetInternalModule();
+ if (!mod) {
+ /* shouldn't happen */
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return NULL;
+ }
+
+ /* make sure we don't open the same database twice. We only understand
+ * the moduleSpec for internal databases well enough to do this, so only
+ * do this in OpenUserDB */
+ conflist = secmod_GetConfigList(mod->isFIPS, mod->libraryParams, &count);
+ if (conflist) {
+ PK11SlotInfo *slot = NULL;
+ if (secmod_MatchConfigList(moduleSpec, conflist, count)) {
+ slot = secmod_FindSlotFromModuleSpec(moduleSpec, mod);
+ }
+ secmod_FreeConfigList(conflist, count);
+ if (slot) {
+ return slot;
+ }
+ }
+ return SECMOD_OpenNewSlot(mod, moduleSpec);
+}
+
+/*
+ * close an already opened user database. NOTE: the database must be
+ * in the internal token, and must be one created with SECMOD_OpenUserDB().
+ * Once the database is closed, the slot will remain as an empty slot
+ * until it's used again with SECMOD_OpenUserDB() or SECMOD_OpenNewSlot().
+ */
+SECStatus
+SECMOD_CloseUserDB(PK11SlotInfo *slot)
+{
+ SECStatus rv;
+ char *sendSpec;
+
+ sendSpec = PR_smprintf("tokens=[0x%x=<>]", slot->slotID);
+ if (sendSpec == NULL) {
+ /* PR_smprintf does not set no memory error */
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+ rv = secmod_UserDBOp(slot, CKO_NSS_DELSLOT, sendSpec);
+ PR_smprintf_free(sendSpec);
+ /* if we are in the delay period for the "isPresent" call, reset
+ * the delay since we know things have probably changed... */
+ NSSToken *nssToken = PK11Slot_GetNSSToken(slot);
+ if (nssToken) {
+ if (nssToken->slot) {
+ nssSlot_ResetDelay(nssToken->slot);
+ }
+ (void)nssToken_Destroy(nssToken);
+ /* force the slot info structures to properly reset */
+ (void)PK11_IsPresent(slot);
+ }
+ return rv;
+}
+
+/*
+ * Restart PKCS #11 modules after a fork(). See secmod.h for more information.
+ */
+SECStatus
+SECMOD_RestartModules(PRBool force)
+{
+ SECMODModuleList *mlp;
+ SECStatus rrv = SECSuccess;
+ int lastError = 0;
+
+ if (!moduleLock) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return SECFailure;
+ }
+
+ /* Only need to restart the PKCS #11 modules that were initialized */
+ SECMOD_GetReadLock(moduleLock);
+ for (mlp = modules; mlp != NULL; mlp = mlp->next) {
+ SECMODModule *mod = mlp->module;
+ CK_ULONG count;
+ SECStatus rv;
+ int i;
+
+ /* If the module needs to be reset, do so */
+ if (force || (PK11_GETTAB(mod)->C_GetSlotList(CK_FALSE, NULL, &count) != CKR_OK)) {
+ PRBool alreadyLoaded;
+ /* first call Finalize. This is not required by PKCS #11, but some
+ * older modules require it, and it doesn't hurt (compliant modules
+ * will return CKR_NOT_INITIALIZED */
+ (void)PK11_GETTAB(mod)->C_Finalize(NULL);
+ /* now initialize the module, this function reinitializes
+ * a module in place, preserving existing slots (even if they
+ * no longer exist) */
+ rv = secmod_ModuleInit(mod, NULL, &alreadyLoaded);
+ if (rv != SECSuccess) {
+ /* save the last error code */
+ lastError = PORT_GetError();
+ rrv = rv;
+ /* couldn't reinit the module, disable all its slots */
+ for (i = 0; i < mod->slotCount; i++) {
+ mod->slots[i]->disabled = PR_TRUE;
+ mod->slots[i]->reason = PK11_DIS_COULD_NOT_INIT_TOKEN;
+ }
+ continue;
+ }
+ for (i = 0; i < mod->slotCount; i++) {
+ /* get new token sessions, bump the series up so that
+ * we refresh other old sessions. This will tell much of
+ * NSS to flush cached handles it may hold as well */
+ rv = PK11_InitToken(mod->slots[i], PR_TRUE);
+ /* PK11_InitToken could fail if the slot isn't present.
+ * If it is present, though, something is wrong and we should
+ * disable the slot and let the caller know. */
+ if (rv != SECSuccess && PK11_IsPresent(mod->slots[i])) {
+ /* save the last error code */
+ lastError = PORT_GetError();
+ rrv = rv;
+ /* disable the token */
+ mod->slots[i]->disabled = PR_TRUE;
+ mod->slots[i]->reason = PK11_DIS_COULD_NOT_INIT_TOKEN;
+ }
+ }
+ }
+ }
+ SECMOD_ReleaseReadLock(moduleLock);
+
+ /*
+ * on multiple failures, we are only returning the lastError. The caller
+ * can determine which slots are bad by calling PK11_IsDisabled().
+ */
+ if (rrv != SECSuccess) {
+ /* restore the last error code */
+ PORT_SetError(lastError);
+ }
+
+ return rrv;
+}
diff --git a/security/nss/lib/pk11wrap/pk11wrap.gyp b/security/nss/lib/pk11wrap/pk11wrap.gyp
new file mode 100644
index 0000000000..eebb4ea3cb
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11wrap.gyp
@@ -0,0 +1,71 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+{
+ 'includes': [
+ '../../coreconf/config.gypi'
+ ],
+ 'targets': [
+ {
+ 'target_name': 'pk11wrap_static',
+ 'type': 'static_library',
+ 'defines': [
+ 'NSS_STATIC_SOFTOKEN',
+ ],
+ 'dependencies': [
+ 'pk11wrap_base',
+ '<(DEPTH)/exports.gyp:nss_exports',
+ '<(DEPTH)/lib/softoken/softoken.gyp:softokn_static',
+ ],
+ },
+ {
+ 'target_name': 'pk11wrap',
+ 'type': 'static_library',
+ 'dependencies': [
+ 'pk11wrap_base',
+ '<(DEPTH)/exports.gyp:nss_exports',
+ ],
+ },
+ {
+ 'target_name': 'pk11wrap_base',
+ 'type': 'none',
+ 'direct_dependent_settings': {
+ 'sources': [
+ 'dev3hack.c',
+ 'pk11akey.c',
+ 'pk11auth.c',
+ 'pk11cert.c',
+ 'pk11cxt.c',
+ 'pk11err.c',
+ 'pk11hpke.c',
+ 'pk11kea.c',
+ 'pk11list.c',
+ 'pk11load.c',
+ 'pk11mech.c',
+ 'pk11merge.c',
+ 'pk11nobj.c',
+ 'pk11obj.c',
+ 'pk11pars.c',
+ 'pk11pbe.c',
+ 'pk11pk12.c',
+ 'pk11pqg.c',
+ 'pk11sdr.c',
+ 'pk11skey.c',
+ 'pk11slot.c',
+ 'pk11util.c'
+ ],
+ },
+ },
+ ],
+ 'target_defaults': {
+ 'defines': [
+ 'SHLIB_SUFFIX=\"<(dll_suffix)\"',
+ 'SHLIB_PREFIX=\"<(dll_prefix)\"',
+ 'NSS_SHLIB_VERSION=\"3\"',
+ 'SOFTOKEN_SHLIB_VERSION=\"3\"'
+ ]
+ },
+ 'variables': {
+ 'module': 'nss'
+ }
+}
diff --git a/security/nss/lib/pk11wrap/secmod.h b/security/nss/lib/pk11wrap/secmod.h
new file mode 100644
index 0000000000..53181f0118
--- /dev/null
+++ b/security/nss/lib/pk11wrap/secmod.h
@@ -0,0 +1,194 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _SECMOD_H_
+#define _SECMOD_H_
+#include "seccomon.h"
+#include "secmodt.h"
+#include "prinrval.h"
+
+/* These mechanisms flags are visible to all other libraries. */
+/* They must be converted to internal SECMOD_*_FLAG */
+/* if used inside the functions of the security library */
+#define PUBLIC_MECH_RSA_FLAG 0x00000001ul
+#define PUBLIC_MECH_DSA_FLAG 0x00000002ul
+#define PUBLIC_MECH_RC2_FLAG 0x00000004ul
+#define PUBLIC_MECH_RC4_FLAG 0x00000008ul
+#define PUBLIC_MECH_DES_FLAG 0x00000010ul
+#define PUBLIC_MECH_DH_FLAG 0x00000020ul
+#define PUBLIC_MECH_FORTEZZA_FLAG 0x00000040ul
+#define PUBLIC_MECH_RC5_FLAG 0x00000080ul
+#define PUBLIC_MECH_SHA1_FLAG 0x00000100ul
+#define PUBLIC_MECH_MD5_FLAG 0x00000200ul
+#define PUBLIC_MECH_MD2_FLAG 0x00000400ul
+#define PUBLIC_MECH_SSL_FLAG 0x00000800ul
+#define PUBLIC_MECH_TLS_FLAG 0x00001000ul
+#define PUBLIC_MECH_AES_FLAG 0x00002000ul
+#define PUBLIC_MECH_SHA256_FLAG 0x00004000ul
+#define PUBLIC_MECH_SHA512_FLAG 0x00008000ul
+#define PUBLIC_MECH_CAMELLIA_FLAG 0x00010000ul
+#define PUBLIC_MECH_SEED_FLAG 0x00020000ul
+#define PUBLIC_MECH_ECC_FLAG 0x00040000ul
+
+#define PUBLIC_MECH_RANDOM_FLAG 0x08000000ul
+#define PUBLIC_MECH_FRIENDLY_FLAG 0x10000000ul
+#define PUBLIC_OWN_PW_DEFAULTS 0X20000000ul
+#define PUBLIC_DISABLE_FLAG 0x40000000ul
+
+/* warning: reserved means reserved */
+#define PUBLIC_MECH_RESERVED_FLAGS 0x87FF0000ul
+
+/* These cipher flags are visible to all other libraries, */
+/* But they must be converted before used in functions */
+/* withing the security module */
+#define PUBLIC_CIPHER_FORTEZZA_FLAG 0x00000001ul
+
+/* warning: reserved means reserved */
+#define PUBLIC_CIPHER_RESERVED_FLAGS 0xFFFFFFFEul
+
+SEC_BEGIN_PROTOS
+
+/*
+ * the following functions are going to be deprecated in NSS 4.0 in
+ * favor of the new stan functions.
+ */
+
+/* Initialization */
+extern SECMODModule *SECMOD_LoadModule(char *moduleSpec, SECMODModule *parent,
+ PRBool recurse);
+
+extern SECMODModule *SECMOD_LoadUserModule(char *moduleSpec, SECMODModule *parent,
+ PRBool recurse);
+
+SECStatus SECMOD_UnloadUserModule(SECMODModule *mod);
+
+SECMODModule *SECMOD_CreateModule(const char *lib, const char *name,
+ const char *param, const char *nss);
+SECMODModule *SECMOD_CreateModuleEx(const char *lib, const char *name,
+ const char *param, const char *nss,
+ const char *config);
+/*
+ * After a fork(), PKCS #11 says we need to call C_Initialize again in
+ * the child before we can use the module. This function causes this
+ * reinitialization.
+ * NOTE: Any outstanding handles will become invalid, which means your
+ * keys and contexts will fail, but new ones can be created.
+ *
+ * Setting 'force' to true means to do the reinitialization even if the
+ * PKCS #11 module does not seem to need it. This allows software modules
+ * which ignore fork to preserve their keys across the fork().
+ */
+SECStatus SECMOD_RestartModules(PRBool force);
+
+/* Module Management */
+char **SECMOD_GetModuleSpecList(SECMODModule *module);
+SECStatus SECMOD_FreeModuleSpecList(SECMODModule *module, char **moduleSpecList);
+
+/* protoypes */
+/* Get a list of active PKCS #11 modules */
+extern SECMODModuleList *SECMOD_GetDefaultModuleList(void);
+/* Get a list of defined but not loaded PKCS #11 modules */
+extern SECMODModuleList *SECMOD_GetDeadModuleList(void);
+/* Get a list of Modules which define PKCS #11 modules to load */
+extern SECMODModuleList *SECMOD_GetDBModuleList(void);
+
+/* lock to protect all three module lists above */
+extern SECMODListLock *SECMOD_GetDefaultModuleListLock(void);
+
+extern SECStatus SECMOD_UpdateModule(SECMODModule *module);
+
+/* lock management */
+extern void SECMOD_GetReadLock(SECMODListLock *);
+extern void SECMOD_ReleaseReadLock(SECMODListLock *);
+
+/* Operate on modules by name */
+extern SECMODModule *SECMOD_FindModule(const char *name);
+extern SECStatus SECMOD_DeleteModule(const char *name, int *type);
+extern SECStatus SECMOD_DeleteModuleEx(const char *name,
+ SECMODModule *mod,
+ int *type,
+ PRBool permdb);
+extern SECStatus SECMOD_DeleteInternalModule(const char *name);
+extern PRBool SECMOD_CanDeleteInternalModule(void);
+extern SECStatus SECMOD_AddNewModule(const char *moduleName,
+ const char *dllPath,
+ unsigned long defaultMechanismFlags,
+ unsigned long cipherEnableFlags);
+extern SECStatus SECMOD_AddNewModuleEx(const char *moduleName,
+ const char *dllPath,
+ unsigned long defaultMechanismFlags,
+ unsigned long cipherEnableFlags,
+ char *modparms,
+ char *nssparms);
+
+/* database/memory management */
+extern SECMODModule *SECMOD_GetInternalModule(void);
+extern SECMODModule *SECMOD_ReferenceModule(SECMODModule *module);
+extern void SECMOD_DestroyModule(SECMODModule *module);
+extern PK11SlotInfo *SECMOD_LookupSlot(SECMODModuleID module,
+ unsigned long slotID);
+extern PK11SlotInfo *SECMOD_FindSlot(SECMODModule *module, const char *name);
+
+/* Funtion reports true if at least one of the modules */
+/* of modType has been installed */
+PRBool SECMOD_IsModulePresent(unsigned long int pubCipherEnableFlags);
+
+/* accessors */
+PRBool SECMOD_GetSkipFirstFlag(SECMODModule *mod);
+PRBool SECMOD_GetDefaultModDBFlag(SECMODModule *mod);
+
+/* Functions used to convert between internal & public representation
+ * of Mechanism Flags and Cipher Enable Flags */
+extern unsigned long SECMOD_PubMechFlagstoInternal(unsigned long publicFlags);
+extern unsigned long SECMOD_InternaltoPubMechFlags(unsigned long internalFlags);
+extern unsigned long SECMOD_PubCipherFlagstoInternal(unsigned long publicFlags);
+
+/*
+ * Check to see if the module has removable slots that we may need to
+ * watch for.
+ *
+ * NB: This function acquires the module list lock in order to access
+ * mod->slotCount and mod->slots. Deadlock can occur if the caller holds the
+ * module list lock. Callers that already hold the module list lock must use
+ * SECMOD_LockedModuleHasRemovableSlots instead.
+ */
+PRBool SECMOD_HasRemovableSlots(SECMODModule *mod);
+
+/*
+ * Like SECMOD_HasRemovableSlots but this function does not acquire the module
+ * list lock.
+ */
+PRBool SECMOD_LockedModuleHasRemovableSlots(SECMODModule *mod);
+
+/*
+ * this function waits for a token event on any slot of a given module
+ * This function should not be called from more than one thread of the
+ * same process (though other threads can make other library calls
+ * on this module while this call is blocked).
+ *
+ * Caller must not hold a module list read lock.
+ */
+PK11SlotInfo *SECMOD_WaitForAnyTokenEvent(SECMODModule *mod,
+ unsigned long flags, PRIntervalTime latency);
+/*
+ * Warning: the SECMOD_CancelWait function is highly destructive, potentially
+ * finalizing the module 'mod' (causing inprogress operations to fail,
+ * and session key material to disappear). It should only be called when
+ * shutting down the module.
+ */
+SECStatus SECMOD_CancelWait(SECMODModule *mod);
+
+/*
+ * check to see if the module has added new slots. PKCS 11 v2.20 allows for
+ * modules to add new slots, but never remove them. Slots not be added between
+ * a call to C_GetSlotLlist(Flag, NULL, &count) and the corresponding
+ * C_GetSlotList(flag, &data, &count) so that the array doesn't accidently
+ * grow on the caller. It is permissible for the slots to increase between
+ * corresponding calls with NULL to get the size.
+ *
+ * Caller must not hold a module list read lock.
+ */
+SECStatus SECMOD_UpdateSlotList(SECMODModule *mod);
+SEC_END_PROTOS
+
+#endif
diff --git a/security/nss/lib/pk11wrap/secmodi.h b/security/nss/lib/pk11wrap/secmodi.h
new file mode 100644
index 0000000000..9f220d6bf1
--- /dev/null
+++ b/security/nss/lib/pk11wrap/secmodi.h
@@ -0,0 +1,174 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/*
+ * Internal header file included only by files in pkcs11 dir, or in
+ * pkcs11 specific client and server files.
+ */
+#ifndef _SECMODI_H_
+#define _SECMODI_H_ 1
+
+#include <stddef.h>
+
+#include "pkcs11.h"
+#include "nssilock.h"
+#include "secoidt.h"
+#include "secdert.h"
+#include "certt.h"
+#include "secmodt.h"
+#include "keythi.h"
+
+SEC_BEGIN_PROTOS
+
+/* proto-types */
+extern SECStatus SECMOD_DeletePermDB(SECMODModule *module);
+extern SECStatus SECMOD_AddPermDB(SECMODModule *module);
+extern SECStatus SECMOD_Shutdown(void);
+void nss_DumpModuleLog(void);
+
+extern int secmod_PrivateModuleCount;
+
+extern void SECMOD_Init(void);
+SECStatus secmod_ModuleInit(SECMODModule *mod, SECMODModule **oldModule,
+ PRBool *alreadyLoaded);
+
+/* list managment */
+extern SECStatus SECMOD_AddModuleToList(SECMODModule *newModule);
+extern SECStatus SECMOD_AddModuleToDBOnlyList(SECMODModule *newModule);
+extern SECStatus SECMOD_AddModuleToUnloadList(SECMODModule *newModule);
+extern void SECMOD_RemoveList(SECMODModuleList **, SECMODModuleList *);
+extern void SECMOD_AddList(SECMODModuleList *, SECMODModuleList *, SECMODListLock *);
+extern SECMODListLock *SECMOD_NewListLock(void);
+extern void SECMOD_DestroyListLock(SECMODListLock *);
+extern void SECMOD_GetWriteLock(SECMODListLock *);
+extern void SECMOD_ReleaseWriteLock(SECMODListLock *);
+
+/* Operate on modules by name */
+extern SECMODModule *SECMOD_FindModuleByID(SECMODModuleID);
+extern SECMODModule *secmod_FindModuleByFuncPtr(void *funcPtr);
+
+/* database/memory management */
+extern SECMODModuleList *SECMOD_NewModuleListElement(void);
+extern SECMODModuleList *SECMOD_DestroyModuleListElement(SECMODModuleList *);
+extern void SECMOD_DestroyModuleList(SECMODModuleList *);
+extern SECStatus SECMOD_AddModule(SECMODModule *newModule);
+
+extern unsigned long SECMOD_InternaltoPubCipherFlags(unsigned long internalFlags);
+
+/* Library functions */
+SECStatus secmod_LoadPKCS11Module(SECMODModule *, SECMODModule **oldModule);
+SECStatus SECMOD_UnloadModule(SECMODModule *);
+void SECMOD_SetInternalModule(SECMODModule *);
+PRBool secmod_IsInternalKeySlot(SECMODModule *);
+void secmod_SetInternalKeySlotFlag(SECMODModule *mod, PRBool val);
+
+/* tools for checking if we are loading the same database twice */
+typedef struct SECMODConfigListStr SECMODConfigList;
+/* collect all the databases in a given spec */
+SECMODConfigList *secmod_GetConfigList(PRBool isFIPS, char *spec, int *count);
+/* see is a spec matches a database on the list */
+PRBool secmod_MatchConfigList(const char *spec,
+ SECMODConfigList *conflist, int count);
+/* returns the slot id from a module and modulespec */
+CK_SLOT_ID secmod_GetSlotIDFromModuleSpec(const char *moduleSpec, SECMODModule *module);
+/* free our list of databases */
+void secmod_FreeConfigList(SECMODConfigList *conflist, int count);
+
+/* parsing parameters */
+/* returned char * must be freed by caller with PORT_Free */
+/* children and ids are null terminated arrays which must be freed with
+ * secmod_FreeChildren */
+char *secmod_ParseModuleSpecForTokens(PRBool convert,
+ PRBool isFIPS,
+ const char *moduleSpec,
+ char ***children,
+ CK_SLOT_ID **ids);
+void secmod_FreeChildren(char **children, CK_SLOT_ID *ids);
+char *secmod_MkAppendTokensList(PLArenaPool *arena, char *origModuleSpec,
+ char *newModuleSpec, CK_SLOT_ID newID,
+ char **children, CK_SLOT_ID *ids);
+
+void SECMOD_SlotDestroyModule(SECMODModule *module, PRBool fromSlot);
+CK_RV pk11_notify(CK_SESSION_HANDLE session, CK_NOTIFICATION event,
+ CK_VOID_PTR pdata);
+void pk11_SignedToUnsigned(CK_ATTRIBUTE *attrib);
+CK_OBJECT_HANDLE pk11_FindObjectByTemplate(PK11SlotInfo *slot,
+ CK_ATTRIBUTE *inTemplate, size_t tsize);
+CK_OBJECT_HANDLE *pk11_FindObjectsByTemplate(PK11SlotInfo *slot,
+ CK_ATTRIBUTE *inTemplate, size_t tsize, int *objCount);
+
+#define PK11_GETTAB(x) ((CK_FUNCTION_LIST_3_0_PTR)((x)->functionList))
+#define PK11_SETATTRS(x, id, v, l) \
+ (x)->type = (id); \
+ (x)->pValue = (v); \
+ (x)->ulValueLen = (l);
+SECStatus PK11_CreateNewObject(PK11SlotInfo *slot, CK_SESSION_HANDLE session,
+ const CK_ATTRIBUTE *theTemplate, int count,
+ PRBool token, CK_OBJECT_HANDLE *objectID);
+
+SECStatus pbe_PK11AlgidToParam(SECAlgorithmID *algid, SECItem *mech);
+SECStatus PBE_PK11ParamToAlgid(SECOidTag algTag, SECItem *param,
+ PLArenaPool *arena, SECAlgorithmID *algId);
+
+PK11SymKey *pk11_TokenKeyGenWithFlagsAndKeyType(PK11SlotInfo *slot,
+ CK_MECHANISM_TYPE type, SECItem *param, CK_KEY_TYPE keyType,
+ int keySize, SECItem *keyId, CK_FLAGS opFlags,
+ PK11AttrFlags attrFlags, void *wincx);
+
+CK_MECHANISM_TYPE pk11_GetPBECryptoMechanism(SECAlgorithmID *algid,
+ SECItem **param, SECItem *pwd, PRBool faulty3DES);
+
+extern void pk11sdr_Init(void);
+extern void pk11sdr_Shutdown(void);
+
+/*
+ * Private to pk11wrap.
+ */
+
+PRBool pk11_LoginStillRequired(PK11SlotInfo *slot, void *wincx);
+CK_SESSION_HANDLE pk11_GetNewSession(PK11SlotInfo *slot, PRBool *owner);
+void pk11_CloseSession(PK11SlotInfo *slot, CK_SESSION_HANDLE sess, PRBool own);
+PK11SymKey *pk11_ForceSlot(PK11SymKey *symKey, CK_MECHANISM_TYPE type,
+ CK_ATTRIBUTE_TYPE operation);
+/* Convert key operation flags to PKCS #11 attributes. */
+unsigned int pk11_OpFlagsToAttributes(CK_FLAGS flags,
+ CK_ATTRIBUTE *attrs, CK_BBOOL *ckTrue);
+/* Check for bad (conflicting) attribute flags */
+PRBool pk11_BadAttrFlags(PK11AttrFlags attrFlags);
+/* Convert key attribute flags to PKCS #11 attributes. */
+unsigned int pk11_AttrFlagsToAttributes(PK11AttrFlags attrFlags,
+ CK_ATTRIBUTE *attrs, CK_BBOOL *ckTrue, CK_BBOOL *ckFalse);
+PRBool pk11_FindAttrInTemplate(CK_ATTRIBUTE *attr, unsigned int numAttrs,
+ CK_ATTRIBUTE_TYPE target);
+
+CK_MECHANISM_TYPE pk11_mapWrapKeyType(KeyType keyType);
+PK11SymKey *pk11_KeyExchange(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
+ CK_ATTRIBUTE_TYPE operation, CK_FLAGS flags, PRBool isPerm,
+ PK11SymKey *symKey);
+
+PRBool pk11_HandleTrustObject(PK11SlotInfo *slot, CERTCertificate *cert,
+ CERTCertTrust *trust);
+CK_OBJECT_HANDLE pk11_FindPubKeyByAnyCert(CERTCertificate *cert,
+ PK11SlotInfo **slot, void *wincx);
+SECStatus pk11_AuthenticateUnfriendly(PK11SlotInfo *slot, PRBool loadCerts,
+ void *wincx);
+int PK11_NumberObjectsFor(PK11SlotInfo *slot, CK_ATTRIBUTE *findTemplate,
+ int templateCount);
+SECItem *pk11_GetLowLevelKeyFromHandle(PK11SlotInfo *slot,
+ CK_OBJECT_HANDLE handle);
+SECStatus PK11_TraverseSlot(PK11SlotInfo *slot, void *arg);
+CK_OBJECT_HANDLE pk11_FindPrivateKeyFromCertID(PK11SlotInfo *slot,
+ SECItem *keyID);
+SECKEYPrivateKey *PK11_MakePrivKey(PK11SlotInfo *slot, KeyType keyType,
+ PRBool isTemp, CK_OBJECT_HANDLE privID, void *wincx);
+CERTCertificate *PK11_MakeCertFromHandle(PK11SlotInfo *slot,
+ CK_OBJECT_HANDLE certID, CK_ATTRIBUTE *privateLabel);
+
+SECItem *pk11_GenerateNewParamWithKeyLen(CK_MECHANISM_TYPE type, int keyLen);
+SECItem *pk11_ParamFromIVWithLen(CK_MECHANISM_TYPE type,
+ SECItem *iv, int keyLen);
+SECItem *pk11_mkcertKeyID(CERTCertificate *cert);
+
+SEC_END_PROTOS
+
+#endif
diff --git a/security/nss/lib/pk11wrap/secmodt.h b/security/nss/lib/pk11wrap/secmodt.h
new file mode 100644
index 0000000000..5f15e5967c
--- /dev/null
+++ b/security/nss/lib/pk11wrap/secmodt.h
@@ -0,0 +1,448 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _SECMODT_H_
+#define _SECMODT_H_ 1
+
+#include "nssrwlkt.h"
+#include "nssilckt.h"
+#include "secoid.h"
+#include "secasn1.h"
+#include "pkcs11t.h"
+#include "utilmodt.h"
+
+SEC_BEGIN_PROTOS
+
+/* find a better home for these... */
+extern const SEC_ASN1Template SECKEY_PointerToEncryptedPrivateKeyInfoTemplate[];
+SEC_ASN1_CHOOSER_DECLARE(SECKEY_PointerToEncryptedPrivateKeyInfoTemplate)
+extern const SEC_ASN1Template SECKEY_EncryptedPrivateKeyInfoTemplate[];
+SEC_ASN1_CHOOSER_DECLARE(SECKEY_EncryptedPrivateKeyInfoTemplate)
+extern const SEC_ASN1Template SECKEY_PrivateKeyInfoTemplate[];
+SEC_ASN1_CHOOSER_DECLARE(SECKEY_PrivateKeyInfoTemplate)
+extern const SEC_ASN1Template SECKEY_PointerToPrivateKeyInfoTemplate[];
+SEC_ASN1_CHOOSER_DECLARE(SECKEY_PointerToPrivateKeyInfoTemplate)
+
+SEC_END_PROTOS
+
+/* PKCS11 needs to be included */
+typedef struct SECMODModuleStr SECMODModule;
+typedef struct SECMODModuleListStr SECMODModuleList;
+typedef NSSRWLock SECMODListLock;
+typedef struct PK11SlotInfoStr PK11SlotInfo; /* defined in secmodti.h */
+typedef struct NSSUTILPreSlotInfoStr PK11PreSlotInfo; /* defined in secmodti.h */
+typedef struct PK11SymKeyStr PK11SymKey; /* defined in secmodti.h */
+typedef struct PK11ContextStr PK11Context; /* defined in secmodti.h */
+typedef struct PK11SlotListStr PK11SlotList;
+typedef struct PK11SlotListElementStr PK11SlotListElement;
+typedef struct PK11RSAGenParamsStr PK11RSAGenParams;
+typedef unsigned long SECMODModuleID;
+typedef struct PK11DefaultArrayEntryStr PK11DefaultArrayEntry;
+typedef struct PK11GenericObjectStr PK11GenericObject;
+typedef void (*PK11FreeDataFunc)(void *);
+
+struct SECMODModuleStr {
+ PLArenaPool *arena;
+ PRBool internal; /* true of internally linked modules, false
+ * for the loaded modules */
+ PRBool loaded; /* Set to true if module has been loaded */
+ PRBool isFIPS; /* Set to true if module is finst internal */
+ char *dllName; /* name of the shared library which implements
+ * this module */
+ char *commonName; /* name of the module to display to the user */
+ void *library; /* pointer to the library. opaque. used only by
+ * pk11load.c */
+ void *functionList; /* The PKCS #11 function table */
+ PZLock *refLock; /* only used pk11db.c */
+ int refCount; /* Module reference count */
+ PK11SlotInfo **slots; /* array of slot points attached to this mod*/
+ int slotCount; /* count of slot in above array */
+ PK11PreSlotInfo *slotInfo; /* special info about slots default settings */
+ int slotInfoCount; /* count */
+ SECMODModuleID moduleID; /* ID so we can find this module again */
+ PRBool isThreadSafe;
+ unsigned long ssl[2]; /* SSL cipher enable flags */
+ char *libraryParams; /* Module specific parameters */
+ void *moduleDBFunc; /* function to return module configuration data*/
+ SECMODModule *parent; /* module that loaded us */
+ PRBool isCritical; /* This module must load successfully */
+ PRBool isModuleDB; /* this module has lists of PKCS #11 modules */
+ PRBool moduleDBOnly; /* this module only has lists of PKCS #11 modules */
+ int trustOrder; /* order for this module's certificate trust rollup */
+ int cipherOrder; /* order for cipher operations */
+ unsigned long evControlMask; /* control the running and shutdown of slot
+ * events (SECMOD_WaitForAnyTokenEvent) */
+ CK_VERSION cryptokiVersion; /* version of this library */
+ CK_FLAGS flags; /* pkcs11 v3 flags */
+ /* Warning this could go way in future versions of NSS
+ * when FIPS indicators wind up in the functionList */
+ CK_NSS_GetFIPSStatus fipsIndicator;
+};
+
+/* evControlMask flags */
+/*
+ * These bits tell the current state of a SECMOD_WaitForAnyTokenEvent.
+ *
+ * SECMOD_WAIT_PKCS11_EVENT - we're waiting in the PKCS #11 module in
+ * C_WaitForSlotEvent().
+ * SECMOD_WAIT_SIMULATED_EVENT - we're waiting in the NSS simulation code
+ * which polls for token insertion and removal events.
+ * SECMOD_END_WAIT - SECMOD_CancelWait has been called while the module is
+ * waiting in SECMOD_WaitForAnyTokenEvent. SECMOD_WaitForAnyTokenEvent
+ * should return immediately to it's caller.
+ */
+#define SECMOD_END_WAIT 0x01
+#define SECMOD_WAIT_SIMULATED_EVENT 0x02
+#define SECMOD_WAIT_PKCS11_EVENT 0x04
+
+struct SECMODModuleListStr {
+ SECMODModuleList *next;
+ SECMODModule *module;
+};
+
+struct PK11SlotListStr {
+ PK11SlotListElement *head;
+ PK11SlotListElement *tail;
+ PZLock *lock;
+};
+
+struct PK11SlotListElementStr {
+ PK11SlotListElement *next;
+ PK11SlotListElement *prev;
+ PK11SlotInfo *slot;
+ int refCount;
+};
+
+struct PK11RSAGenParamsStr {
+ int keySizeInBits;
+ unsigned long pe;
+};
+
+typedef enum {
+ PK11CertListUnique = 0, /* get one instance of all certs */
+ PK11CertListUser = 1, /* get all instances of user certs */
+ PK11CertListRootUnique = 2, /* get one instance of CA certs without a private key.
+ * deprecated. Use PK11CertListCAUnique
+ */
+ PK11CertListCA = 3, /* get all instances of CA certs */
+ PK11CertListCAUnique = 4, /* get one instance of CA certs */
+ PK11CertListUserUnique = 5, /* get one instance of user certs */
+ PK11CertListAll = 6 /* get all instances of all certs */
+} PK11CertListType;
+
+/*
+ * Entry into the array which lists all the legal bits for the default flags
+ * in the slot, their definition, and the PKCS #11 mechanism they represent.
+ * Always statically allocated.
+ */
+struct PK11DefaultArrayEntryStr {
+ const char *name;
+ unsigned long flag;
+ unsigned long mechanism; /* this is a long so we don't include the
+ * whole pkcs 11 world to use this header */
+};
+
+/*
+ * PK11AttrFlags
+ *
+ * A 32-bit bitmask of PK11_ATTR_XXX flags
+ */
+typedef PRUint32 PK11AttrFlags;
+
+/*
+ * PK11_ATTR_XXX
+ *
+ * The following PK11_ATTR_XXX bitflags are used to specify
+ * PKCS #11 object attributes that have Boolean values. Some NSS
+ * functions have a "PK11AttrFlags attrFlags" parameter whose value
+ * is the logical OR of these bitflags. NSS use these bitflags on
+ * private keys or secret keys. Some of these bitflags also apply
+ * to the public keys associated with the private keys.
+ *
+ * For each PKCS #11 object attribute, we need two bitflags to
+ * specify not only "true" and "false" but also "default". For
+ * example, PK11_ATTR_PRIVATE and PK11_ATTR_PUBLIC control the
+ * CKA_PRIVATE attribute. If PK11_ATTR_PRIVATE is set, we add
+ * { CKA_PRIVATE, &cktrue, sizeof(CK_BBOOL) }
+ * to the template. If PK11_ATTR_PUBLIC is set, we add
+ * { CKA_PRIVATE, &ckfalse, sizeof(CK_BBOOL) }
+ * to the template. If neither flag is set, we don't add any
+ * CKA_PRIVATE entry to the template.
+ */
+
+/*
+ * Attributes for PKCS #11 storage objects, which include not only
+ * keys but also certificates and domain parameters.
+ */
+
+/*
+ * PK11_ATTR_TOKEN
+ * PK11_ATTR_SESSION
+ *
+ * These two flags determine whether the object is a token or
+ * session object.
+ *
+ * These two flags are related and cannot both be set.
+ * If the PK11_ATTR_TOKEN flag is set, the object is a token
+ * object. If the PK11_ATTR_SESSION flag is set, the object is
+ * a session object. If neither flag is set, the object is *by
+ * default* a session object.
+ *
+ * These two flags specify the value of the PKCS #11 CKA_TOKEN
+ * attribute.
+ */
+#define PK11_ATTR_TOKEN 0x00000001L
+#define PK11_ATTR_SESSION 0x00000002L
+
+/*
+ * PK11_ATTR_PRIVATE
+ * PK11_ATTR_PUBLIC
+ *
+ * These two flags determine whether the object is a private or
+ * public object. A user may not access a private object until the
+ * user has authenticated to the token.
+ *
+ * These two flags are related and cannot both be set.
+ * If the PK11_ATTR_PRIVATE flag is set, the object is a private
+ * object. If the PK11_ATTR_PUBLIC flag is set, the object is a
+ * public object. If neither flag is set, it is token-specific
+ * whether the object is private or public.
+ *
+ * These two flags specify the value of the PKCS #11 CKA_PRIVATE
+ * attribute. NSS only uses this attribute on private and secret
+ * keys, so public keys created by NSS get the token-specific
+ * default value of the CKA_PRIVATE attribute.
+ */
+#define PK11_ATTR_PRIVATE 0x00000004L
+#define PK11_ATTR_PUBLIC 0x00000008L
+
+/*
+ * PK11_ATTR_MODIFIABLE
+ * PK11_ATTR_UNMODIFIABLE
+ *
+ * These two flags determine whether the object is modifiable or
+ * read-only.
+ *
+ * These two flags are related and cannot both be set.
+ * If the PK11_ATTR_MODIFIABLE flag is set, the object can be
+ * modified. If the PK11_ATTR_UNMODIFIABLE flag is set, the object
+ * is read-only. If neither flag is set, the object is *by default*
+ * modifiable.
+ *
+ * These two flags specify the value of the PKCS #11 CKA_MODIFIABLE
+ * attribute.
+ */
+#define PK11_ATTR_MODIFIABLE 0x00000010L
+#define PK11_ATTR_UNMODIFIABLE 0x00000020L
+
+/* Attributes for PKCS #11 key objects. */
+
+/*
+ * PK11_ATTR_SENSITIVE
+ * PK11_ATTR_INSENSITIVE
+ *
+ * These two flags are related and cannot both be set.
+ * If the PK11_ATTR_SENSITIVE flag is set, the key is sensitive.
+ * If the PK11_ATTR_INSENSITIVE flag is set, the key is not
+ * sensitive. If neither flag is set, it is token-specific whether
+ * the key is sensitive or not.
+ *
+ * If a key is sensitive, certain attributes of the key cannot be
+ * revealed in plaintext outside the token.
+ *
+ * This flag specifies the value of the PKCS #11 CKA_SENSITIVE
+ * attribute. Although the default value of the CKA_SENSITIVE
+ * attribute for secret keys is CK_FALSE per PKCS #11, some FIPS
+ * tokens set the default value to CK_TRUE because only CK_TRUE
+ * is allowed. So in practice the default value of this attribute
+ * is token-specific, hence the need for two bitflags.
+ */
+#define PK11_ATTR_SENSITIVE 0x00000040L
+#define PK11_ATTR_INSENSITIVE 0x00000080L
+
+/*
+ * PK11_ATTR_EXTRACTABLE
+ * PK11_ATTR_UNEXTRACTABLE
+ *
+ * These two flags are related and cannot both be set.
+ * If the PK11_ATTR_EXTRACTABLE flag is set, the key is extractable
+ * and can be wrapped. If the PK11_ATTR_UNEXTRACTABLE flag is set,
+ * the key is not extractable, and certain attributes of the key
+ * cannot be revealed in plaintext outside the token (just like a
+ * sensitive key). If neither flag is set, it is token-specific
+ * whether the key is extractable or not.
+ *
+ * These two flags specify the value of the PKCS #11 CKA_EXTRACTABLE
+ * attribute.
+ */
+#define PK11_ATTR_EXTRACTABLE 0x00000100L
+#define PK11_ATTR_UNEXTRACTABLE 0x00000200L
+
+/* Cryptographic module types */
+#define SECMOD_EXTERNAL 0 /* external module */
+#define SECMOD_INTERNAL 1 /* internal default module */
+#define SECMOD_FIPS 2 /* internal fips module */
+
+/* default module configuration strings */
+#define SECMOD_SLOT_FLAGS "slotFlags=[RSA,DSA,DH,RC2,RC4,DES,RANDOM,SHA1,MD5,MD2,SSL,TLS,AES,Camellia,SEED,SHA256,SHA512]"
+
+#define SECMOD_MAKE_NSS_FLAGS(fips, slot) \
+ "Flags=internal,critical" fips " slotparams=(" #slot "={" SECMOD_SLOT_FLAGS "})"
+
+#define SECMOD_INT_NAME "NSS Internal PKCS #11 Module"
+#define SECMOD_INT_FLAGS SECMOD_MAKE_NSS_FLAGS("", 1)
+#define SECMOD_FIPS_NAME "NSS Internal FIPS PKCS #11 Module"
+#define SECMOD_FIPS_FLAGS SECMOD_MAKE_NSS_FLAGS(",fips", 3)
+
+/*
+ * What is the origin of a given Key. Normally this doesn't matter, but
+ * the fortezza code needs to know if it needs to invoke the SSL3 fortezza
+ * hack.
+ */
+typedef enum {
+ PK11_OriginNULL = 0, /* There is not key, it's a null SymKey */
+ PK11_OriginDerive = 1, /* Key was derived from some other key */
+ PK11_OriginGenerated = 2, /* Key was generated (also PBE keys) */
+ PK11_OriginFortezzaHack = 3, /* Key was marked for fortezza hack */
+ PK11_OriginUnwrap = 4 /* Key was unwrapped or decrypted */
+} PK11Origin;
+
+/* PKCS #11 disable reasons */
+typedef enum {
+ PK11_DIS_NONE = 0,
+ PK11_DIS_USER_SELECTED = 1,
+ PK11_DIS_COULD_NOT_INIT_TOKEN = 2,
+ PK11_DIS_TOKEN_VERIFY_FAILED = 3,
+ PK11_DIS_TOKEN_NOT_PRESENT = 4
+} PK11DisableReasons;
+
+/* types of PKCS #11 objects
+ * used to identify which NSS data structure is
+ * passed to the PK11_Raw* functions. Types map as follows:
+ * PK11_TypeGeneric PK11GenericObject *
+ * PK11_TypePrivKey SECKEYPrivateKey *
+ * PK11_TypePubKey SECKEYPublicKey *
+ * PK11_TypeSymKey PK11SymKey *
+ * PK11_TypeCert CERTCertificate * (currently not used).
+ */
+typedef enum {
+ PK11_TypeGeneric = 0,
+ PK11_TypePrivKey = 1,
+ PK11_TypePubKey = 2,
+ PK11_TypeCert = 3,
+ PK11_TypeSymKey = 4
+} PK11ObjectType;
+
+/* function pointer type for password callback function.
+ * This type is passed in to PK11_SetPasswordFunc()
+ */
+typedef char *(PR_CALLBACK *PK11PasswordFunc)(PK11SlotInfo *slot, PRBool retry, void *arg);
+typedef PRBool(PR_CALLBACK *PK11VerifyPasswordFunc)(PK11SlotInfo *slot, void *arg);
+typedef PRBool(PR_CALLBACK *PK11IsLoggedInFunc)(PK11SlotInfo *slot, void *arg);
+
+/*
+ * Special strings the password callback function can return only if
+ * the slot is an protected auth path slot.
+ */
+#define PK11_PW_RETRY "RETRY" /* an failed attempt to authenticate \
+ * has already been made, just retry \
+ * the operation */
+#define PK11_PW_AUTHENTICATED "AUTH" /* a successful attempt to authenticate \
+ * has completed. Continue without \
+ * another call to C_Login */
+/* All other non-null values mean that that NSS could call C_Login to force
+ * the authentication. The following define is to aid applications in
+ * documenting that is what it's trying to do */
+#define PK11_PW_TRY "TRY" /* Default: a prompt has been presented \
+ * to the user, initiate a C_Login \
+ * to authenticate the token */
+
+/*
+ * PKCS #11 key structures
+ */
+
+/*
+** Attributes
+*/
+struct SECKEYAttributeStr {
+ SECItem attrType;
+ SECItem **attrValue;
+};
+typedef struct SECKEYAttributeStr SECKEYAttribute;
+
+/*
+** A PKCS#8 private key info object
+*/
+struct SECKEYPrivateKeyInfoStr {
+ PLArenaPool *arena;
+ SECItem version;
+ SECAlgorithmID algorithm;
+ SECItem privateKey;
+ SECKEYAttribute **attributes;
+};
+typedef struct SECKEYPrivateKeyInfoStr SECKEYPrivateKeyInfo;
+
+/*
+** A PKCS#8 private key info object
+*/
+struct SECKEYEncryptedPrivateKeyInfoStr {
+ PLArenaPool *arena;
+ SECAlgorithmID algorithm;
+ SECItem encryptedData;
+};
+typedef struct SECKEYEncryptedPrivateKeyInfoStr SECKEYEncryptedPrivateKeyInfo;
+
+/*
+ * token removal detection
+ */
+typedef enum {
+ PK11TokenNotRemovable = 0,
+ PK11TokenPresent = 1,
+ PK11TokenChanged = 2,
+ PK11TokenRemoved = 3
+} PK11TokenStatus;
+
+typedef enum {
+ PK11TokenRemovedOrChangedEvent = 0,
+ PK11TokenPresentEvent = 1
+} PK11TokenEvent;
+
+/*
+ * CRL Import Flags
+ */
+#define CRL_IMPORT_DEFAULT_OPTIONS 0x00000000
+#define CRL_IMPORT_BYPASS_CHECKS 0x00000001
+
+/*
+ * Merge Error Log
+ */
+typedef struct PK11MergeLogStr PK11MergeLog;
+typedef struct PK11MergeLogNodeStr PK11MergeLogNode;
+
+/* These need to be global, leave some open fields so we can 'expand'
+ * these without breaking binary compatibility */
+struct PK11MergeLogNodeStr {
+ PK11MergeLogNode *next; /* next entry in the list */
+ PK11MergeLogNode *prev; /* last entry in the list */
+ PK11GenericObject *object; /* object that failed */
+ int error; /* what the error was */
+ CK_RV reserved1;
+ unsigned long reserved2; /* future flags */
+ unsigned long reserved3; /* future scalar */
+ void *reserved4; /* future pointer */
+ void *reserved5; /* future expansion pointer */
+};
+
+struct PK11MergeLogStr {
+ PK11MergeLogNode *head;
+ PK11MergeLogNode *tail;
+ PLArenaPool *arena;
+ int version;
+ unsigned long reserved1;
+ unsigned long reserved2;
+ unsigned long reserved3;
+ void *reserverd4;
+ void *reserverd5;
+};
+
+#endif /*_SECMODT_H_ */
diff --git a/security/nss/lib/pk11wrap/secmodti.h b/security/nss/lib/pk11wrap/secmodti.h
new file mode 100644
index 0000000000..5dca1e46cd
--- /dev/null
+++ b/security/nss/lib/pk11wrap/secmodti.h
@@ -0,0 +1,205 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/*
+ * Internal header file included only by files in pkcs11 dir, or in
+ * pkcs11 specific client and server files.
+ */
+
+#ifndef _SECMODTI_H_
+#define _SECMODTI_H_ 1
+#include "prmon.h"
+#include "prtypes.h"
+#include "nssilckt.h"
+#include "secmodt.h"
+#include "pkcs11t.h"
+
+#include "nssdevt.h"
+
+/* internal data structures */
+
+/* Traverse slots callback */
+typedef struct pk11TraverseSlotStr {
+ SECStatus (*callback)(PK11SlotInfo *, CK_OBJECT_HANDLE, void *);
+ void *callbackArg;
+ CK_ATTRIBUTE *findTemplate;
+ int templateCount;
+} pk11TraverseSlot;
+
+/* represent a pkcs#11 slot reference counted. */
+struct PK11SlotInfoStr {
+ /* the PKCS11 function list for this slot */
+ void *functionList;
+ SECMODModule *module; /* our parent module */
+ /* Boolean to indicate the current state of this slot */
+ PRBool needTest; /* Has this slot been tested for Export complience */
+ PRBool isPerm; /* is this slot a permanment device */
+ PRBool isHW; /* is this slot a hardware device */
+ PRBool isInternal; /* is this slot one of our internal PKCS #11 devices */
+ PRBool disabled; /* is this slot disabled... */
+ PK11DisableReasons reason; /* Why this slot is disabled */
+ PRBool readOnly; /* is the token in this slot read-only */
+ PRBool needLogin; /* does the token of the type that needs
+ * authentication (still true even if token is logged
+ * in) */
+ PRBool hasRandom; /* can this token generated random numbers */
+ PRBool defRWSession; /* is the default session RW (we open our default
+ * session rw if the token can only handle one session
+ * at a time. */
+ PRBool isThreadSafe; /* copied from the module */
+ /* The actual flags (many of which are distilled into the above PRBools) */
+ CK_FLAGS flags; /* flags from PKCS #11 token Info */
+ /* a default session handle to do quick and dirty functions */
+ CK_SESSION_HANDLE session;
+ PZLock *sessionLock; /* lock for this session */
+ /* our ID */
+ CK_SLOT_ID slotID;
+ /* persistant flags saved from startup to startup */
+ unsigned long defaultFlags;
+ /* keep track of who is using us so we don't accidently get freed while
+ * still in use */
+ PRInt32 refCount; /* to be in/decremented by atomic calls ONLY! */
+ PZLock *freeListLock;
+ PK11SymKey *freeSymKeysWithSessionHead;
+ PK11SymKey *freeSymKeysHead;
+ int keyCount;
+ int maxKeyCount;
+ /* Password control functions for this slot. many of these are only
+ * active if the appropriate flag is on in defaultFlags */
+ int askpw; /* what our password options are */
+ int timeout; /* If we're ask_timeout, what is our timeout time is
+ * seconds */
+ int authTransact; /* allow multiple authentications off one password if
+ * they are all part of the same transaction */
+ PRTime authTime; /* when were we last authenticated */
+ int minPassword; /* smallest legal password */
+ int maxPassword; /* largest legal password */
+ PRUint16 series; /* break up the slot info into various groups of
+ * inserted tokens so that keys and certs can be
+ * invalidated */
+ PRUint16 flagSeries; /* record the last series for the last event
+ * returned for this slot */
+ PRBool flagState; /* record the state of the last event returned for this
+ * slot. */
+ PRUint16 wrapKey; /* current wrapping key for SSL master secrets */
+ CK_MECHANISM_TYPE wrapMechanism;
+ /* current wrapping mechanism for current wrapKey */
+ CK_OBJECT_HANDLE refKeys[1]; /* array of existing wrapping keys for */
+ CK_MECHANISM_TYPE *mechanismList; /* list of mechanism supported by this
+ * token */
+ int mechanismCount;
+ /* cache the certificates stored on the token of this slot */
+ CERTCertificate **cert_array;
+ int array_size;
+ int cert_count;
+ char serial[16];
+ /* since these are odd sizes, keep them last. They are odd sizes to
+ * allow them to become null terminated strings */
+ char slot_name[65];
+ char token_name[33];
+ PRBool hasRootCerts;
+ PRBool hasRootTrust;
+ PRBool hasRSAInfo;
+ CK_FLAGS RSAInfoFlags;
+ PRBool protectedAuthPath;
+ PRBool isActiveCard;
+ PRIntervalTime lastLoginCheck;
+ unsigned int lastState;
+ /* for Stan */
+ NSSToken *nssToken;
+ PZLock *nssTokenLock;
+ /* the tokeninfo struct */
+ CK_TOKEN_INFO tokenInfo;
+ /* fast mechanism lookup */
+ char mechanismBits[256];
+ CK_PROFILE_ID *profileList;
+ int profileCount;
+};
+
+/* Symetric Key structure. Reference Counted */
+struct PK11SymKeyStr {
+ CK_MECHANISM_TYPE type; /* type of operation this key was created for*/
+ CK_OBJECT_HANDLE objectID; /* object id of this key in the slot */
+ PK11SlotInfo *slot; /* Slot this key is loaded into */
+ void *cx; /* window context in case we need to loggin */
+ PK11SymKey *next;
+ PRBool owner;
+ SECItem data; /* raw key data if available */
+ CK_SESSION_HANDLE session;
+ PRBool sessionOwner;
+ PRInt32 refCount; /* number of references to this key */
+ int size; /* key size in bytes */
+ PK11Origin origin; /* where this key came from
+ * (see def in secmodt.h) */
+ PK11SymKey *parent; /* potential owner key of the session */
+ PRUint16 series; /* break up the slot info into various groups
+ * of inserted tokens so that keys and certs
+ * can be invalidated */
+ void *userData; /* random data the application can attach to
+ * this key */
+ PK11FreeDataFunc freeFunc; /* function to free the user data */
+};
+
+/*
+ * hold a hash, encryption or signing context for multi-part operations.
+ * hold enough information so that multiple contexts can be interleaved
+ * if necessary. ... Not RefCounted.
+ */
+struct PK11ContextStr {
+ CK_ATTRIBUTE_TYPE operation; /* type of operation this context is
+ * doing (CKA_ENCRYPT, CKA_SIGN,
+ * CKA_HASH, etc.) */
+ PK11SymKey *key; /* symetric key for this context */
+ CK_OBJECT_HANDLE objectID; /* object handle to key */
+ PK11SlotInfo *slot; /* slot this context is using */
+ CK_SESSION_HANDLE session; /* session this context is using */
+ PZLock *sessionLock; /* lock before accessing a PKCS #11
+ * session */
+ PRBool ownSession; /* do we own the session? */
+ void *pwArg; /* applicaton specific passwd arg */
+ void *savedData; /* save data when we are
+ * multiplexing on a single context */
+ unsigned long savedLength; /* length of the saved context */
+ SECItem *param; /* mechanism parameters used to
+ * build this context */
+ PRBool init; /* this contexted been initialized? */
+ CK_MECHANISM_TYPE type; /* what is the PKCS #11 this context
+ * is representing (usually what
+ * algorithm is being used
+ * (CKM_RSA_PKCS, CKM_DES, CKM_SHA,
+ * etc. */
+ PRBool fortezzaHack; /* Fortezza SSL has some special
+ * non-standard semantics*/
+ PRBool simulate_message; /* We are initializing a message
+ * interface but the underlying
+ * PKCS #11 module does not support
+ * it. We simulate the interface with
+ * the PCKS #11 v2 interface */
+ CK_MECHANISM_TYPE simulate_mechanism; /* The mechanism we are simulating */
+ PRUint64 ivCounter; /* iv counter for simulated message */
+ PRUint64 ivMaxCount; /* total number of IVs valid for
+ * this key */
+ unsigned long ivLen; /* length of the iv in bytes */
+ unsigned int ivFixedBits; /* number of bits not generated
+ * for the iv */
+ CK_GENERATOR_FUNCTION ivGen; /* PKCS #11 iv generator value */
+};
+
+/*
+ * structure to hold a pointer to a unique PKCS #11 object
+ * (pointer to the slot and the object id).
+ */
+struct PK11GenericObjectStr {
+ PK11GenericObject *prev;
+ PK11GenericObject *next;
+ PK11SlotInfo *slot;
+ CK_OBJECT_HANDLE objectID;
+ PRBool owner;
+};
+
+#define MAX_TEMPL_ATTRS 16 /* maximum attributes in template */
+
+/* This mask includes all CK_FLAGs with an equivalent CKA_ attribute. */
+#define CKF_KEY_OPERATION_FLAGS 0x000e7b00UL
+
+#endif /* _SECMODTI_H_ */
diff --git a/security/nss/lib/pk11wrap/secpkcs5.h b/security/nss/lib/pk11wrap/secpkcs5.h
new file mode 100644
index 0000000000..57856767be
--- /dev/null
+++ b/security/nss/lib/pk11wrap/secpkcs5.h
@@ -0,0 +1,61 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _SECPKCS5_H_
+#define _SECPKCS5_H_
+#include "seccomon.h"
+#include "secmodt.h"
+
+/* used for V2 PKCS 12 Draft Spec */
+typedef enum {
+ pbeBitGenIDNull = 0,
+ pbeBitGenCipherKey = 0x01,
+ pbeBitGenCipherIV = 0x02,
+ pbeBitGenIntegrityKey = 0x03
+} PBEBitGenID;
+
+typedef struct PBEBitGenContextStr PBEBitGenContext;
+
+SEC_BEGIN_PROTOS
+
+/* private */
+SECAlgorithmID *
+sec_pkcs5CreateAlgorithmID(SECOidTag algorithm, SECOidTag cipherAlgorithm,
+ SECOidTag prfAlg, SECOidTag *pPbeAlgorithm,
+ int keyLengh, SECItem *salt, int iteration);
+
+/* Get the initialization vector. The password is passed in, hashing
+ * is performed, and the initialization vector is returned.
+ * algid is a pointer to a PBE algorithm ID
+ * pwitem is the password
+ * If an error occurs or the algorithm id is not a PBE algrithm,
+ * NULL is returned. Otherwise, the iv is returned in a secitem.
+ */
+SECItem *
+SEC_PKCS5GetIV(SECAlgorithmID *algid, SECItem *pwitem, PRBool faulty3DES);
+
+SECOidTag SEC_PKCS5GetCryptoAlgorithm(SECAlgorithmID *algid);
+PRBool SEC_PKCS5IsAlgorithmPBEAlg(SECAlgorithmID *algid);
+PRBool SEC_PKCS5IsAlgorithmPBEAlgTag(SECOidTag algTag);
+SECOidTag SEC_PKCS5GetPBEAlgorithm(SECOidTag algTag, int keyLen);
+int SEC_PKCS5GetKeyLength(SECAlgorithmID *algid);
+
+/**********************************************************************
+ * Deprecated PBE functions. Use the PBE functions in pk11func.h
+ * instead.
+ **********************************************************************/
+
+PBEBitGenContext *
+PBE_CreateContext(SECOidTag hashAlgorithm, PBEBitGenID bitGenPurpose,
+ SECItem *pwitem, SECItem *salt, unsigned int bitsNeeded,
+ unsigned int iterations);
+
+void
+PBE_DestroyContext(PBEBitGenContext *context);
+
+SECItem *
+PBE_GenerateBits(PBEBitGenContext *context);
+
+SEC_END_PROTOS
+
+#endif /* _SECPKS5_H_ */