diff options
Diffstat (limited to 'security/nss/lib/pk11wrap')
38 files changed, 31660 insertions, 0 deletions
diff --git a/security/nss/lib/pk11wrap/Makefile b/security/nss/lib/pk11wrap/Makefile new file mode 100644 index 000000000..86ab29653 --- /dev/null +++ b/security/nss/lib/pk11wrap/Makefile @@ -0,0 +1,62 @@ +#! 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). # +####################################################################### + +-include config.mk + +####################################################################### +# (5) Execute "global" rules. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/rules.mk + +####################################################################### +# (6) Execute "component" rules. (OPTIONAL) # +####################################################################### + + + +####################################################################### +# (7) Execute "local" rules. (OPTIONAL). # +####################################################################### + +export:: private_export + +$(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 + @$(MAKE_OBJDIR) + $(CC) -o $@ -c -O2 $(CFLAGS) $< +endif +endif +endif diff --git a/security/nss/lib/pk11wrap/config.mk b/security/nss/lib/pk11wrap/config.mk new file mode 100644 index 000000000..b8c03de79 --- /dev/null +++ b/security/nss/lib/pk11wrap/config.mk @@ -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/. + +# +# Override TARGETS variable so that only static libraries +# are specifed as dependencies within rules.mk. +# + +TARGETS = $(LIBRARY) +SHARED_LIBRARY = +IMPORT_LIBRARY = +PROGRAM = + diff --git a/security/nss/lib/pk11wrap/debug_module.c b/security/nss/lib/pk11wrap/debug_module.c new file mode 100644 index 000000000..9230c0136 --- /dev/null +++ b/security/nss/lib/pk11wrap/debug_module.c @@ -0,0 +1,2760 @@ +/* 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_PTR module_functions; + +static CK_FUNCTION_LIST 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_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_pPart[] = " pPart = 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_pulDataLen[] = " pulDataLen = 0x%p"; +STRING fmt_pulDigestLen[] = " pulDigestLen = 0x%p"; +STRING fmt_pulEncryptedPartLen[] = " pulEncryptedPartLen = 0x%p"; +STRING fmt_pulPartLen[] = " pulPartLen = 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_ulCount[] = " ulCount = %d"; +STRING fmt_ulDataLen[] = " ulDataLen = %d"; +STRING fmt_ulEncryptedPartLen[] = " ulEncryptedPartLen = %d"; +STRING fmt_ulPartLen[] = " ulPartLen = %d"; +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_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_NETSCAPE_DB); + CASE(CKA_NETSCAPE_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_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_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_KEY_PARAMS_INVALID); + 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(int 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_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_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_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_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); + 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_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) +}; + +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_FUNCTION_LIST_PTR +nss_InsertDeviceLog( + CK_FUNCTION_LIST_PTR devEPV) +{ + module_functions = devEPV; + 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; + 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 000000000..27325a55a --- /dev/null +++ b/security/nss/lib/pk11wrap/dev3hack.c @@ -0,0 +1,279 @@ +/* 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_SESSION) { + 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 = 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; + 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 = 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; + } + rvToken->slot->token = rvToken; + 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 +nssSlot_Refresh(NSSSlot *slot) +{ + PK11SlotInfo *nss3slot = slot->pk11slot; + PRBool doit = PR_FALSE; + if (slot->token && slot->token->base.name[0] == 0) { + doit = PR_TRUE; + } + if (PK11_InitToken(nss3slot, PR_FALSE) != SECSuccess) { + return PR_FAILURE; + } + if (doit) { + nssTrustDomain_UpdateCachedTokenCerts(slot->token->trustDomain, + slot->token); + } + return nssToken_Refresh(slot->token); +} + +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 000000000..610525910 --- /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 000000000..b3d4bf468 --- /dev/null +++ b/security/nss/lib/pk11wrap/exports.gyp @@ -0,0 +1,39 @@ +# 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', + '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' + ], + '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 000000000..1a2d2446d --- /dev/null +++ b/security/nss/lib/pk11wrap/manifest.mn @@ -0,0 +1,63 @@ +# +# 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 \ + pk11pub.h \ + pk11priv.h \ + pk11sdr.h \ + pk11pqg.h \ + $(NULL) + +PRIVATE_EXPORTS = \ + secmodi.h \ + dev3hack.h \ + $(NULL) + +MODULE = nss + +CSRCS = \ + dev3hack.c \ + pk11akey.c \ + pk11auth.c \ + pk11cert.c \ + pk11cxt.c \ + pk11err.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 + +LIBRARY_VERSION = 3 +SOFTOKEN_LIBRARY_VERSION = 3 +DEFINES += -DSHLIB_SUFFIX=\"$(DLL_SUFFIX)\" -DSHLIB_PREFIX=\"$(DLL_PREFIX)\" \ + -DSHLIB_VERSION=\"$(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 000000000..01d1d7fd9 --- /dev/null +++ b/security/nss/lib/pk11wrap/pk11akey.c @@ -0,0 +1,2525 @@ +/* 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 "seccomon.h" +#include "secmod.h" +#include "secmodi.h" +#include "secmodti.h" +#include "pkcs11.h" +#include "pkcs11t.h" +#include "pk11func.h" +#include "cert.h" +#include "key.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++; + signedattr = 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; + signedcount = attrs - signedattr; + PORT_Assert(templateCount <= (sizeof(theTemplate) / sizeof(CK_ATTRIBUTE))); + for (attrs = signedattr; signedcount; attrs++, signedcount--) { + pk11_SignedToUnsigned(attrs); + } + rv = PK11_CreateNewObject(slot, CK_INVALID_SESSION, 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; + } + 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 */ + for (ap = attrs; extra_count; ap++, extra_count--) { + pk11_SignedToUnsigned(ap); + } + + /* now Store the puppies */ + rv = PK11_CreateNewObject(slot, CK_INVALID_SESSION, 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_SESSION) + PK11_EnterSlotMonitor(slot); + restore = PR_FALSE; + haslock = PR_TRUE; + } + + if (session_handle == CK_INVALID_SESSION) { + 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; +} + +/* + * 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) { + if (privk) { + *privk = privKey; + } else { + SECKEY_DestroyPrivateKey(privKey); + } + privKey = NULL; + rv = SECSuccess; + goto done; + } + + /* if we are unable to import the key and the pbeMechType is + * CKM_NETSCAPE_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_NETSCAPE_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 (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; +} + +SECKEYEncryptedPrivateKeyInfo * +PK11_ExportEncryptedPrivKeyInfo( + PK11SlotInfo *slot, /* optional, encrypt key in this slot */ + SECOidTag algTag, /* encrypt key with this algorithm */ + SECItem *pwitem, /* password for PBE encryption */ + SECKEYPrivateKey *pk, /* encrypt this private key */ + int iteration, /* interations for PBE alg */ + void *wincx) /* 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(algTag, SEC_OID_UNKNOWN, SEC_OID_UNKNOWN, + &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, wincx); + 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_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 *wincx) /* context for password callback ? */ +{ + SECKEYEncryptedPrivateKeyInfo *epki = NULL; + SECKEYPrivateKey *pk = PK11_FindKeyByAnyCert(cert, wincx); + if (pk != NULL) { + epki = PK11_ExportEncryptedPrivKeyInfo(slot, algTag, pwitem, pk, + iteration, wincx); + SECKEY_DestroyPrivateKey(pk); + } + return epki; +} + +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_SESSION) { + 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; + unsigned int 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; + unsigned int 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 000000000..4ccfad6f1 --- /dev/null +++ b/security/nss/lib/pk11wrap/pk11auth.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 deals with PKCS #11 passwords and authentication. + */ +#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_SESSION) { + 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_SESSION) { + 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_SESSION; + 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_SESSION) { + 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_SESSION) { + 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 + * slave 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 (!PK11_IsFriendly(slot)) { + nssTrustDomain_UpdateCachedTokenCerts(slot->nssToken->trustDomain, + slot->nssToken); + } + } 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 = 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_SESSION; + 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 000000000..6968ae70a --- /dev/null +++ b/security/nss/lib/pk11wrap/pk11cert.c @@ -0,0 +1,2744 @@ +/* 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 "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 "key.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" + +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; + } + 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; + NSSToken *token; + NSSTrustDomain *td = STAN_GetDefaultTrustDomain(); + + /* Get the cryptoki object from the handle */ + token = PK11Slot_GetNSSToken(slot); + if (token->defaultSession) { + co = nssCryptokiObject_Create(token, token->defaultSession, certID); + } else { + PORT_SetError(SEC_ERROR_NO_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; + + 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); +} + +/* + * 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); +} + +CERTCertificate * +PK11_FindCertFromNickname(const char *nickname, void *wincx) +{ + PRStatus status; + CERTCertificate *rvCert = NULL; + NSSCertificate *cert = NULL; + NSSCertificate **certs = NULL; + static const NSSUsage usage = { PR_TRUE /* ... */ }; + NSSToken *token; + NSSTrustDomain *defaultTD = STAN_GetDefaultTrustDomain(); + PK11SlotInfo *slot = NULL; + SECStatus rv; + char *nickCopy; + char *delimit = NULL; + char *tokenName; + + 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) { + 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); + if (certs) { + cert = nssCertificateArray_FindBestCertificate(certs, NULL, + &usage, NULL); + if (cert) { + rvCert = STAN_GetCERTCertificateOrRelease(cert); + } + nssCertificateArray_Destroy(certs); + } + nssList_Destroy(certList); + } + if (slot) { + PK11_FreeSlot(slot); + } + if (nickCopy) + PORT_Free(nickCopy); + return rvCert; +loser: + if (slot) { + PK11_FreeSlot(slot); + } + if (nickCopy) + PORT_Free(nickCopy); + return NULL; +} + +/* 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_HEAD(cbparam.certList) == NULL || + CERT_LIST_END(CERT_LIST_HEAD(cbparam.certList), cbparam.certList)) { + CERT_DestroyCertList(cbparam.certList); + cbparam.certList = NULL; + } + + PORT_Free(cbparam.email); + return cbparam.certList; +} + +CERTCertList * +PK11_FindCertsFromNickname(const char *nickname, void *wincx) +{ + char *nickCopy; + char *delimit = NULL; + char *tokenName; + int i; + CERTCertList *certList = NULL; + nssPKIObjectCollection *collection = NULL; + NSSCertificate **foundCerts = NULL; + NSSTrustDomain *defaultTD = STAN_GetDefaultTrustDomain(); + NSSCertificate *c; + NSSToken *token; + PK11SlotInfo *slot; + SECStatus rv; + + 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); + slot = NULL; + } + *delimit = ':'; + } else { + slot = PK11_GetInternalKeySlot(); + token = PK11Slot_GetNSSToken(slot); + } + if (token) { + PRStatus status; + nssList *nameList; + nssCryptokiObject **instances; + nssTokenSearchType tokenOnly = nssTokenSearchType_TokenOnly; + rv = pk11_AuthenticateUnfriendly(slot, PR_TRUE, wincx); + if (rv != SECSuccess) { + PK11_FreeSlot(slot); + if (nickCopy) + PORT_Free(nickCopy); + return NULL; + } + collection = nssCertificateCollection_Create(defaultTD, NULL); + if (!collection) { + PK11_FreeSlot(slot); + if (nickCopy) + PORT_Free(nickCopy); + return NULL; + } + nameList = nssList_Create(NULL, PR_FALSE); + if (!nameList) { + PK11_FreeSlot(slot); + if (nickCopy) + PORT_Free(nickCopy); + return NULL; + } + (void)nssTrustDomain_GetCertsForNicknameFromCache(defaultTD, + nickname, + nameList); + transfer_token_certs_to_collection(nameList, 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, + nameList); + transfer_token_certs_to_collection(nameList, token, collection); + instances = nssToken_FindCertificatesByEmail(token, + NULL, + lowercaseName, + tokenOnly, + 0, + &status); + nssPKIObjectCollection_AddInstances(collection, instances, 0); + nss_ZFreeIf(instances); + PORT_Free(lowercaseName); + } + } + + nssList_Destroy(nameList); + foundCerts = nssPKIObjectCollection_GetCertificates(collection, + NULL, 0, NULL); + nssPKIObjectCollection_Destroy(collection); + } + if (slot) { + PK11_FreeSlot(slot); + } + if (nickCopy) + PORT_Free(nickCopy); + 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; +} + +/* + * 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 = PK11Slot_GetNSSToken(slot); + SECItem *keyID = pk11_mkcertKeyID(cert); + char *emailAddr = NULL; + nssCertificateStoreTrace lockTrace = { NULL, NULL, PR_FALSE, PR_FALSE }; + nssCertificateStoreTrace unlockTrace = { NULL, NULL, PR_FALSE, PR_FALSE }; + + if (keyID == NULL) { + goto loser; /* error code should be set already */ + } + 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 */ + if (cert->nssCertificate) { + c = cert->nssCertificate; + } 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->istemp = PR_FALSE; + cert->isperm = PR_TRUE; + } + + /* 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); + return SECSuccess; +loser: + 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; +} + +/* + * get a certificate handle, look at the cached handle first.. + */ +CK_OBJECT_HANDLE +pk11_getcerthandle(PK11SlotInfo *slot, CERTCertificate *cert, + CK_ATTRIBUTE *theTemplate, int tsize) +{ + CK_OBJECT_HANDLE certh; + + if (cert->slot == slot) { + certh = cert->pkcs11ID; + if ((certh == CK_INVALID_HANDLE) || + (cert->series != slot->series)) { + certh = pk11_FindObjectByTemplate(slot, theTemplate, tsize); + cert->pkcs11ID = certh; + cert->series = slot->series; + } + } else { + certh = pk11_FindObjectByTemplate(slot, theTemplate, tsize); + } + return certh; +} + +/* + * return the private key From a given Cert + */ +SECKEYPrivateKey * +PK11_FindPrivateKeyFromCert(PK11SlotInfo *slot, CERTCertificate *cert, + void *wincx) +{ + int err; + CK_OBJECT_CLASS certClass = CKO_CERTIFICATE; + CK_ATTRIBUTE theTemplate[] = { + { CKA_VALUE, 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_OBJECT_HANDLE certh; + CK_OBJECT_HANDLE keyh; + CK_ATTRIBUTE *attrs = theTemplate; + PRBool needLogin; + SECStatus rv; + + PK11_SETATTRS(attrs, CKA_VALUE, cert->derCert.data, + cert->derCert.len); + attrs++; + PK11_SETATTRS(attrs, CKA_CLASS, &certClass, sizeof(certClass)); + + /* + * issue the find + */ + rv = pk11_AuthenticateUnfriendly(slot, PR_TRUE, wincx); + if (rv != SECSuccess) { + return NULL; + } + + certh = pk11_getcerthandle(slot, cert, theTemplate, tsize); + 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, int 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 = slot->nssToken; + 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; + } + + /* Paranoia */ + if (token == NULL) { + PORT_SetError(SEC_ERROR_NO_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) { + return NULL; + } + + NSSITEM_FROM_SECITEM(&issuer, &issuerSN->derIssuer); + NSSITEM_FROM_SECITEM(&serial, derSerial); + + session = nssToken_GetDefaultSession(token); + if (!session) { + goto loser; + } + + instance = nssToken_FindCertificateByIssuerAndSerialNumber(token, session, + &issuer, &serial, nssTokenSearchType_TokenForced, &status); + + 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 }, + }; + int 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_getcerthandle(cert->slot, cert, searchTemplate, + templateSize); + 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 (!nssToken_IsPresent(token)) { + return SECSuccess; + } + collection = nssCertificateCollection_Create(td, NULL); + if (!collection) { + return SECFailure; + } + subjectList = nssList_Create(NULL, PR_FALSE); + if (!subjectList) { + nssPKIObjectCollection_Destroy(collection); + 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); + 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 (!nssToken_IsPresent(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); + 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: + 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 (!nssToken_IsPresent(tok)) { + return SECSuccess; + } + collection = nssCertificateCollection_Create(td, NULL); + if (!collection) { + return SECFailure; + } + certList = nssList_Create(NULL, PR_FALSE); + if (!certList) { + nssPKIObjectCollection_Destroy(collection); + 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); + 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; + + tok = PK11Slot_GetNSSToken(slot); + NSSITEM_FROM_SECITEM(&derCert, inDerCert); + rv = pk11_AuthenticateUnfriendly(slot, PR_TRUE, wincx); + if (rv != SECSuccess) { + PK11_FreeSlot(slot); + return NULL; + } + + co = nssToken_FindCertificateByEncodedCertificate(tok, NULL, &derCert, + nssTokenSearchType_TokenOnly, NULL); + + 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_SESSION) { + 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; +} + +/* + * return the private key From a given Cert + */ +CK_OBJECT_HANDLE +PK11_FindCertInSlot(PK11SlotInfo *slot, CERTCertificate *cert, void *wincx) +{ + CK_OBJECT_CLASS certClass = CKO_CERTIFICATE; + CK_ATTRIBUTE theTemplate[] = { + { CKA_VALUE, 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; + SECStatus rv; + + PK11_SETATTRS(attrs, CKA_VALUE, cert->derCert.data, + cert->derCert.len); + attrs++; + PK11_SETATTRS(attrs, CKA_CLASS, &certClass, sizeof(certClass)); + + /* + * issue the find + */ + rv = pk11_AuthenticateUnfriendly(slot, PR_TRUE, wincx); + if (rv != SECSuccess) { + return CK_INVALID_HANDLE; + } + + return pk11_getcerthandle(slot, cert, theTemplate, tsize); +} + +/* 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_CLASS certClass = CKO_CERTIFICATE; + CK_ATTRIBUTE theTemplate[] = { + { CKA_VALUE, 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_OBJECT_HANDLE certHandle; + CK_ATTRIBUTE *attrs = theTemplate; + PK11SlotInfo *slotRef = NULL; + SECItem *item; + SECStatus rv; + + if (slot) { + PK11_SETATTRS(attrs, CKA_VALUE, cert->derCert.data, + cert->derCert.len); + attrs++; + PK11_SETATTRS(attrs, CKA_CLASS, &certClass, sizeof(certClass)); + + rv = pk11_AuthenticateUnfriendly(slot, PR_TRUE, wincx); + if (rv != SECSuccess) { + return NULL; + } + certHandle = pk11_getcerthandle(slot, cert, theTemplate, tsize); + } 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 000000000..e9726d03c --- /dev/null +++ b/security/nss/lib/pk11wrap/pk11cxt.c @@ -0,0 +1,1019 @@ +/* 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" + +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->key) ? context->key->objectID : CK_INVALID_HANDLE; + + 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); + +/* + * Context initialization. Used by all flavors of CreateContext + */ +static SECStatus +pk11_context_init(PK11Context *context, CK_MECHANISM *mech_info) +{ + CK_RV crv; + PK11SymKey *symKey = context->key; + SECStatus rv = SECSuccess; + + switch (context->operation) { + case CKA_ENCRYPT: + crv = PK11_GETTAB(context->slot)->C_EncryptInit(context->session, mech_info, symKey->objectID); + break; + case CKA_DECRYPT: + if (context->fortezzaHack) { + CK_ULONG count = 0; + /* generate the IV for fortezza */ + crv = PK11_GETTAB(context->slot)->C_EncryptInit(context->session, mech_info, symKey->objectID); + if (crv != CKR_OK) + break; + PK11_GETTAB(context->slot) + ->C_EncryptFinal(context->session, + NULL, &count); + } + crv = PK11_GETTAB(context->slot)->C_DecryptInit(context->session, mech_info, symKey->objectID); + break; + case CKA_SIGN: + crv = PK11_GETTAB(context->slot)->C_SignInit(context->session, mech_info, symKey->objectID); + break; + case CKA_VERIFY: + crv = PK11_GETTAB(context->slot)->C_SignInit(context->session, mech_info, symKey->objectID); + break; + case CKA_DIGEST: + crv = PK11_GETTAB(context->slot)->C_DigestInit(context->session, mech_info); + break; + default: + crv = CKR_OPERATION_NOT_INITIALIZED; + break; + } + + if (crv != CKR_OK) { + PORT_SetError(PK11_MapError(crv)); + return 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); + } + return rv; +} + +/* + * 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, + SECItem *param) +{ + CK_MECHANISM mech_info; + PK11Context *context; + SECStatus rv; + + PORT_Assert(slot != NULL); + if (!slot || (!symKey && ((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->origin == PK11_OriginFortezzaHack) { + context->fortezzaHack = PR_TRUE; + } + } + + /* initialize the critical fields of the context */ + context->operation = operation; + context->key = symKey ? PK11_ReferenceSymKey(symKey) : NULL; + context->slot = PK11_ReferenceSlot(slot); + context->session = pk11_GetNewSession(slot, &context->ownSession); + context->cx = symKey ? symKey->cx : NULL; + /* 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; + PK11_EnterContextMonitor(context); + rv = pk11_context_init(context, &mech_info); + PK11_ExitContextMonitor(context); + + 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 session! + */ +PK11Context * +PK11_CreateContextBySymKey(CK_MECHANISM_TYPE type, CK_ATTRIBUTE_TYPE operation, + PK11SymKey *symKey, 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 Adopts the symKey.... */ + context = pk11_CreateNewContextInSlot(type, symKey->slot, operation, symKey, + param); + PK11_FreeSymKey(symKey); + return context; +} + +/* + * 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, ¶m); + 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->param); + 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); + + mech_info.mechanism = cx->type; + mech_info.pParameter = cx->param->data; + mech_info.ulParameterLen = cx->param->len; + rv = pk11_context_init(cx, &mech_info); + PK11_ExitContextMonitor(cx); + + 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; +} + +/* + * 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; + 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; + } + + /* 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) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + 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; + + /* 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); + + *outLen = (unsigned int)len; + context->init = PR_FALSE; /* allow Begin to start up again */ + + if (crv != CKR_OK) { + PORT_SetError(PK11_MapError(crv)); + return SECFailure; + } + return SECSuccess; +} diff --git a/security/nss/lib/pk11wrap/pk11err.c b/security/nss/lib/pk11wrap/pk11err.c new file mode 100644 index 000000000..db6d2440d --- /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_NETSCAPE_CERTDB_FAILED, SEC_ERROR_BAD_DATABASE) + MAPERROR(CKR_NETSCAPE_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 000000000..331c0eb80 --- /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/pk11kea.c b/security/nss/lib/pk11wrap/pk11kea.c new file mode 100644 index 000000000..331a19c16 --- /dev/null +++ b/security/nss/lib/pk11wrap/pk11kea.c @@ -0,0 +1,140 @@ +/* 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 "seccomon.h" +#include "secmod.h" +#include "nssilock.h" +#include "secmodi.h" +#include "secmodti.h" +#include "pkcs11.h" +#include "pk11func.h" +#include "secitem.h" +#include "key.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]; + int 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 > 53) /* bytes */ { + /* we'd have to generate an RSA key pair > 512 bits long, + ** and that's too costly. Don't even try. + */ + PORT_SetError(SEC_ERROR_CANNOT_MOVE_SENSITIVE_KEY); + goto rsa_failed; + } + rsaParams.keySizeInBits = + (symKeyLength > 21 || symKeyLength == 0) ? 512 : 256; + 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 000000000..aaf29f69d --- /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 Read: NOTE: this assumes the reading isn't so common + * the writing will be starved. + */ +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 000000000..f12d0fd4f --- /dev/null +++ b/security/nss/lib/pk11wrap/pk11load.c @@ -0,0 +1,643 @@ +/* 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 "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 "utilparst.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_NETSCAPE_CERTDB_FAILED || + crv == CKR_NETSCAPE_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; + } +} + +static const char *my_shlib_name = + SHLIB_PREFIX "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; + +#include "prio.h" +#include "prprf.h" +#include <stdio.h> +#include "prsystem.h" + +/* 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; +} + +/* + * load a new module into our address space and initialize it. + */ +SECStatus +secmod_LoadPKCS11Module(SECMODModule *mod, SECMODModule **oldModule) +{ + PRLibrary *library = NULL; + CK_C_GetFunctionList entry = NULL; + CK_INFO info; + CK_ULONG slotCount = 0; + SECStatus rv; + PRBool alreadyLoaded = PR_FALSE; + char *disableUnload = NULL; + + if (mod->loaded) + return SECSuccess; + + /* intenal modules get loaded from their internal list */ + if (mod->internal && (mod->dllName == NULL)) { + /* + * 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) { + entry = (CK_C_GetFunctionList) + PR_FindSymbol(softokenLib, "FC_GetFunctionList"); + } else { + entry = (CK_C_GetFunctionList) + PR_FindSymbol(softokenLib, "NSC_GetFunctionList"); + } + + if (!entry) + return SECFailure; + + if (mod->isModuleDB) { + mod->moduleDBFunc = (CK_C_GetFunctionList) + PR_FindSymbol(softokenLib, "NSC_ModuleDBFunc"); + } + + 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... + */ + library = PR_LoadLibrary(mod->dllName); + 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) { + entry = (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 (entry == 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 ((*entry)((CK_FUNCTION_LIST_PTR *)&mod->functionList) != CKR_OK) + goto fail; + +#ifdef DEBUG_MODULE + if (PR_TRUE) { + modToDBG = PR_GetEnvSecure("NSS_DEBUG_PKCS11_MODULE"); + if (modToDBG && strcmp(mod->commonName, modToDBG) == 0) { + mod->functionList = (void *)nss_InsertDeviceLog( + (CK_FUNCTION_LIST_PTR)mod->functionList); + } + } +#endif + + mod->isThreadSafe = PR_TRUE; + + /* 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.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)) { + 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; + } + 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 000000000..4db05ff39 --- /dev/null +++ b/security/nss/lib/pk11wrap/pk11mech.c @@ -0,0 +1,1913 @@ +/* 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_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_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: + return CKK_NSS_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_CBC_PAD: + case CKM_AES_KEY_GEN: + case CKM_NETSCAPE_AES_KEY_WRAP: + case CKM_NETSCAPE_AES_KEY_WRAP_PAD: + 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_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: + return CKM_NSS_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_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_NETSCAPE_PBE_SHA1_HMAC_KEY_GEN: + case CKM_NETSCAPE_PBE_MD5_HMAC_KEY_GEN: + case CKM_NETSCAPE_PBE_MD2_HMAC_KEY_GEN: + case CKM_NETSCAPE_PBE_SHA1_DES_CBC: + case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC2_CBC: + case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC2_CBC: + case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC4: + case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC4: + case CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC: + case CKM_NETSCAPE_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_NETSCAPE_PBE_SHA1_DES_CBC: + case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC2_CBC: + case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC2_CBC: + case CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC: + case CKM_NETSCAPE_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_NETSCAPE_PBE_SHA1_40_BIT_RC4: + case CKM_NETSCAPE_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 */ + 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_NETSCAPE_PBE_SHA1_DES_CBC: + case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC2_CBC: + case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC2_CBC: + case CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC: + case CKM_NETSCAPE_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_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: + 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_NETSCAPE_PBE_SHA1_40_BIT_RC4: + case CKM_NETSCAPE_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_NETSCAPE_PBE_SHA1_DES_CBC: + case CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC: + case CKM_NETSCAPE_PBE_SHA1_FAULTY_3DES_CBC: + case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC2_CBC: + case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC2_CBC: + case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC4: + case CKM_NETSCAPE_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_NETSCAPE_PBE_SHA1_DES_CBC: + case CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC: + case CKM_NETSCAPE_PBE_SHA1_FAULTY_3DES_CBC: + case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC2_CBC: + case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC2_CBC: + case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC4: + case CKM_NETSCAPE_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, ¶m, 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_NETSCAPE_PBE_SHA1_DES_CBC: + pCryptoMechanism->mechanism = CKM_DES_CBC; + goto have_crypto_mechanism; + case CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC: + case CKM_NETSCAPE_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_NETSCAPE_PBE_SHA1_40_BIT_RC4: + case CKM_NETSCAPE_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_NETSCAPE_PBE_SHA1_40_BIT_RC2_CBC: + case CKM_PBE_SHA1_RC2_40_CBC: + rc2_key_len = 40; + goto have_key_len; + case CKM_NETSCAPE_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 000000000..8c4c5129a --- /dev/null +++ b/security/nss/lib/pk11wrap/pk11merge.c @@ -0,0 +1,1432 @@ +/* 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_SESSION) { + 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) { + int i, j; + newTemplate = PORT_NewArray(CK_ATTRIBUTE, copyTemplateCount); + /* 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)); + return SECFailure; + } + if (targetID == CK_INVALID_HANDLE) { + /* we need to create the object */ + rv = PK11_CreateNewObject(targetSlot, CK_INVALID_SESSION, + copyTemplate, copyTemplateCount, PR_TRUE, &targetID); + } else { + /* update the existing object with the new attributes */ + rv = pk11_setAttributes(targetSlot, targetID, + copyTemplate, copyTemplateCount); + } + if (newTemplate) { + 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 } + }; + 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; + + 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 000000000..41d1d9688 --- /dev/null +++ b/security/nss/lib/pk11wrap/pk11nobj.c @@ -0,0 +1,791 @@ +/* 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 "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_NETSCAPE_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_NETSCAPE_KRL, NULL, 0 }, + { CKA_NETSCAPE_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_NETSCAPE_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_NETSCAPE_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_NETSCAPE_KRL, NULL, 0 }, + { CKA_NETSCAPE_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_NETSCAPE_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); + collection = nssCRLCollection_Create(td, NULL); + if (!collection) { + goto loser; + } + instances = nssToken_FindCRLsBySubject(token, NULL, &subject, + tokenOnly, 0, NULL); + 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 = PK11Slot_GetNSSToken(slot); + 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); + + object = nssToken_ImportCRL(token, NULL, + &derSubject, &derCRL, isKRL, url, PR_TRUE); + + 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; + NSSToken *token; + nssCryptokiObject *object; + 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); + + object = nss_ZNEW(NULL, nssCryptokiObject); + if (!object) { + return SECFailure; + } + object->token = nssToken_AddRef(token); + 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_NETSCAPE_SMIME; + CK_ATTRIBUTE theTemplate[] = { + { CKA_SUBJECT, NULL, 0 }, + { CKA_CLASS, NULL, 0 }, + { CKA_NETSCAPE_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 */ + int 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_NETSCAPE_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_NETSCAPE_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_NETSCAPE_SMIME; + CK_BBOOL ck_true = CK_TRUE; + CK_ATTRIBUTE theTemplate[] = { + { CKA_CLASS, NULL, 0 }, + { CKA_TOKEN, NULL, 0 }, + { CKA_SUBJECT, NULL, 0 }, + { CKA_NETSCAPE_EMAIL, NULL, 0 }, + { CKA_NETSCAPE_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_NETSCAPE_EMAIL, + emailAddr, PORT_Strlen(emailAddr) + 1); + attrs++; + if (profileTime) { + PK11_SETATTRS(attrs, CKA_NETSCAPE_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_SESSION) { + 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 000000000..18850b29d --- /dev/null +++ b/security/nss/lib/pk11wrap/pk11obj.c @@ -0,0 +1,2098 @@ +/* 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 "seccomon.h" +#include "secmod.h" +#include "secmodi.h" +#include "secmodti.h" +#include "pkcs11.h" +#include "pkcs11t.h" +#include "pk11func.h" +#include "key.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_SESSION) { + 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(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; +} + +/* + * 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; + PORT_Assert(slot->session != CK_INVALID_SESSION); + if (slot->session == CK_INVALID_SESSION) + 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_SESSION) { + 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_SESSION) { + rwsession = slot->session; + if (rwsession != CK_INVALID_SESSION) + PK11_EnterSlotMonitor(slot); + } + if (rwsession == CK_INVALID_SESSION) { + 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_SESSION) { + 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); + *outLen = len; + if (crv != CKR_OK) { + PORT_SetError(PK11_MapError(crv)); + return SECFailure; + } + 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); + *outLen = len; + if (crv != CKR_OK) { + PORT_SetError(PK11_MapError(crv)); + return SECFailure; + } + 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_NETSCAPE_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_SESSION) + 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_SESSION) { + 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); + return newPrivKey; + } + } + if (int_slot) + PK11_FreeSlot(int_slot); + PORT_SetError(PK11_MapError(crv)); + return NULL; + } + return PK11_MakePrivKey(slot, nullKey, PR_FALSE, privKeyID, wincx); + +loser: + if (newKey) { + PK11_FreeSymKey(newKey); + } + if (ck_id) { + SECITEM_FreeItem(ck_id, 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->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) { + 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_CreateGenericObject(PK11SlotInfo *slot, const CK_ATTRIBUTE *pTemplate, + int count, PRBool token) +{ + 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->next = NULL; + obj->prev = NULL; + return obj; +} + +/* + * 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; + + 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: /* don't handle cert case for now */ + default: + break; + } + if (slot == NULL) { + 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_SESSION) { + 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; + + 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: /* don't handle cert case for now */ + default: + break; + } + if (slot == NULL) { + PORT_SetError(SEC_ERROR_UNKNOWN_OBJECT_TYPE); + return SECFailure; + } + + return PK11_ReadAttribute(slot, handle, attrType, NULL, item); +} + +/* + * return the object handle that matches the template + */ +CK_OBJECT_HANDLE +pk11_FindObjectByTemplate(PK11SlotInfo *slot, CK_ATTRIBUTE *theTemplate, int 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_SESSION) { + 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, + int templCount, int *object_count) +{ + CK_OBJECT_HANDLE *objID = NULL; + CK_ULONG returned_count = 0; + CK_RV crv = CKR_SESSION_HANDLE_INVALID; + + PK11_EnterSlotMonitor(slot); + if (slot->session != CK_INVALID_SESSION) { + crv = PK11_GETTAB(slot)->C_FindObjectsInit(slot->session, + findTemplate, templCount); + } + if (crv != CKR_OK) { + PK11_ExitSlotMonitor(slot); + 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(slot->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(slot->session); + PK11_ExitSlotMonitor(slot); + + if (objID && (*object_count == 0)) { + PORT_Free(objID); + return NULL; + } + if (objID == NULL) + *object_count = -1; + return objID; +} +/* + * 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]; + int 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_SESSION) { + 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 }, + }; + int 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); + 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; +} diff --git a/security/nss/lib/pk11wrap/pk11pars.c b/security/nss/lib/pk11wrap/pk11pars.c new file mode 100644 index 000000000..ee20789cc --- /dev/null +++ b/security/nss/lib/pk11wrap/pk11pars.c @@ -0,0 +1,1796 @@ +/* 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 + +/* 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 + +/* + * 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. + * + * Future key words (not yet implemented): + * enable: turn on ciphersuites by default. + * disable: turn off ciphersuites by default without disallowing them by policy. + * flags: turn on the following flags: + * ssl-lock: turn off the ability for applications to change policy with + * the SSL_SetCipherPolicy (or SSL_SetPolicy). + * policy-lock: turn off the ability for applications to change policy with + * the call NSS_SetAlgorithmPolicy. + * ssl-default-lock: turn off the ability for applications to change cipher + * suite states with SSL_EnableCipher, SSL_DisableCipher. + * + */ + +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 algOptList[] = { + /* 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 }, + /* 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 }, + + /* Hashes */ + { CIPHER_NAME("MD2"), SEC_OID_MD2, + NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, + { CIPHER_NAME("MD4"), SEC_OID_MD4, + NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, + { CIPHER_NAME("MD5"), SEC_OID_MD5, + NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, + { CIPHER_NAME("SHA1"), SEC_OID_SHA1, + NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, + { CIPHER_NAME("SHA224"), SEC_OID_SHA224, + NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, + { CIPHER_NAME("SHA256"), SEC_OID_SHA256, + NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, + { CIPHER_NAME("SHA384"), SEC_OID_SHA384, + NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, + { CIPHER_NAME("SHA512"), SEC_OID_SHA512, + NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, + + /* 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 }, + + /* 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 }, + + /* 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 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 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 }, + /* 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_MIN_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 }, + /* add other signatures in the future */ + { CIPHER_NAME("SIGNATURE"), NSS_USE_ALG_IN_CERT_SIGNATURE }, + /* enable everything */ + { CIPHER_NAME("ALL"), NSS_USE_ALG_IN_SSL | NSS_USE_ALG_IN_SSL_KX | + NSS_USE_ALG_IN_CERT_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) +{ + const char *flag, *currentString; + PRUint32 flags = 0; + int i; + + for (currentString = policyFlags; currentString && + currentString < policyFlags + policyLength;) { + int length; + flag = secmod_ArgGetSubValue(currentString, ',', ':', &length, + ¤tString); + 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; + break; + } + } + } + return flags; +} + +/* allow symbolic names for values. The only ones currently defines or + * SSL protocol versions. */ +static PRInt32 +secmod_getPolicyOptValue(const char *policyValue, int policyValueLength) +{ + PRInt32 val = atoi(policyValue); + int i; + + if ((val != 0) || (*policyValue == '0')) { + return val; + } + 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) { + val = sslOptList[i].option; + break; + } + } + return val; +} + +static SECStatus +secmod_applyCryptoPolicy(const char *policyString, + PRBool allow) +{ + const char *cipher, *currentString; + unsigned i; + 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, + ¤tString); + 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); + } + for (i = 0; i < PR_ARRAY_SIZE(algOptList); i++) { + PRUint32 enable, disable; + if (!newValue) { + value = algOptList[i].val; + } + if (allow) { + enable = value; + disable = 0; + } else { + enable = 0; + disable = value; + } + NSS_SetAlgorithmPolicy(algOptList[i].oid, enable, disable); + } + continue; + } + + for (i = 0; i < PR_ARRAY_SIZE(algOptList); i++) { + const oidValDef *algOpt = &algOptList[i]; + unsigned name_size = algOpt->name_size; + PRBool newValue = PR_FALSE; + + if ((length >= name_size) && (cipher[name_size] == '/')) { + newValue = PR_TRUE; + } + if ((newValue || algOpt->name_size == length) && + PORT_Strncasecmp(algOpt->name, cipher, name_size) == 0) { + PRUint32 value = algOpt->val; + PRUint32 enable, disable; + if (newValue) { + value = secmod_parsePolicyValue(&cipher[name_size] + 1, + length - name_size - 1); + } + if (allow) { + enable = value; + disable = 0; + } else { + enable = 0; + disable = value; + } + rv = NSS_SetAlgorithmPolicy(algOpt->oid, enable, disable); + 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 = secmod_getPolicyOptValue(&cipher[name_size + 1], + length - name_size - 1); + + 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; + } + } + } + return rv; +} + +static SECStatus +secmod_parseCryptoPolicy(const char *policyConfig) +{ + char *disallow, *allow; + 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; + } + disallow = NSSUTIL_ArgGetParamValue("disallow", policyConfig); + rv = secmod_applyCryptoPolicy(disallow, PR_FALSE); + if (disallow) + PORT_Free(disallow); + if (rv != SECSuccess) { + return rv; + } + allow = NSSUTIL_ArgGetParamValue("allow", policyConfig); + rv = secmod_applyCryptoPolicy(allow, PR_TRUE); + if (allow) + PORT_Free(allow); + return rv; +} + +/* + * 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; + /* pk11pars.h still does not have const char * interfaces */ + char *nssc = (char *)nss; + + rv = secmod_parseCryptoPolicy(config); + + /* do not load the module if policy parsing fails */ + if (rv != SECSuccess) { + 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", nssc); + mod->isFIPS = NSSUTIL_ArgHasFlag("flags", "FIPS", nssc); + mod->isCritical = NSSUTIL_ArgHasFlag("flags", "critical", nssc); + slotParams = NSSUTIL_ArgGetParamValue("slotParams", nssc); + mod->slotInfo = NSSUTIL_ArgParseSlotInfo(mod->arena, slotParams, + &mod->slotInfoCount); + if (slotParams) + PORT_Free(slotParams); + /* new field */ + mod->trustOrder = NSSUTIL_ArgReadLong("trustOrder", nssc, + NSSUTIL_DEFAULT_TRUST_ORDER, NULL); + /* new field */ + mod->cipherOrder = NSSUTIL_ArgReadLong("cipherOrder", nssc, + NSSUTIL_DEFAULT_CIPHER_ORDER, NULL); + /* new field */ + mod->isModuleDB = NSSUTIL_ArgHasFlag("flags", "moduleDB", nssc); + mod->moduleDBOnly = NSSUTIL_ArgHasFlag("flags", "moduleDBOnly", nssc); + 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", nssc)) { + flags |= SECMOD_FLAG_MODULE_DB_SKIP_FIRST; + } + if (NSSUTIL_ArgHasFlag("flags", "defaultModDB", nssc)) { + flags |= SECMOD_FLAG_MODULE_DB_DEFAULT_MODDB; + } + /* 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", nssc)) { + flags |= SECMOD_FLAG_INTERNAL_KEY_SLOT; + } + mod->internal = (PRBool)flags; + } + + ciphers = NSSUTIL_ArgGetParamValue("ciphers", nssc); + 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_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, int *targetLen, 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 */ + char *newPtr = PORT_Realloc(target, *targetLen * diff); + if (!newPtr) { + return target; /* not enough space, just drop the whole copy */ + } + *targetLen += diff; + target = 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, + &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, + &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, + &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, + &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; + + /* initialize the underlying module structures */ + SECMOD_Init(); + + status = NSSUTIL_ArgParseModuleSpecEx(modulespec, &library, &moduleName, + ¶meters, &nss, + &config); + if (status != SECSuccess) { + goto loser; + } + + module = SECMOD_CreateModuleEx(library, moduleName, parameters, nss, config); + 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; + } + 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; + } + child = SECMOD_LoadModule(*index, module, PR_TRUE); + 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 000000000..7837bfe9c --- /dev/null +++ b/security/nss/lib/pk11wrap/pk11pbe.c @@ -0,0 +1,1433 @@ +/* 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 "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 "key.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; +} + +/* + * get the key length in bytes from a PKCS5 PBE + */ +int +sec_pkcs5v2_key_length(SECAlgorithmID *algid) +{ + SECOidTag algorithm; + PLArenaPool *arena = NULL; + SEC_PKCS5PBEParameter p5_param; + SECStatus rv; + int length = -1; + + 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 (p5_param.keyLength.data != NULL) { + length = DER_GetInteger(&p5_param.keyLength); + } + +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); + 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); + 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 algoithm 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 alogithm 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. + * gorithm 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 { + CK_MECHANISM_TYPE cryptoMech; + cryptoMech = PK11_AlgtagToMechanism(cipherAlgorithm); + if (cryptoMech == CKM_INVALID_MECHANISM) { + goto loser; + } + keyLength = PK11_GetMaxKeyLength(cryptoMech); + } + 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); + 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; + 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_NETSCAPE_PBE_MD2_HMAC_KEY_GEN; + break; + case SEC_OID_MD5: + mechanism = CKM_NETSCAPE_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; + } + 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_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC)) { + type = CKM_NETSCAPE_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_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC)) { + type = CKM_NETSCAPE_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 000000000..1683cc564 --- /dev/null +++ b/security/nss/lib/pk11wrap/pk11pk12.c @@ -0,0 +1,669 @@ + +/* 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 "pkcs11.h" +#include "pk11func.h" +#include "secitem.h" +#include "key.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; + +/* +** raw private key object +*/ +struct SECKEYRawPrivateKeyStr { + PLArenaPool *arena; + KeyType keyType; + union { + SECKEYRSAPrivateKey rsa; + SECKEYDSAPrivateKey dsa; + SECKEYDHPrivateKey dh; + } 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) }, +}; + +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; +} + +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_NETSCAPE_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_NETSCAPE_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; + /* what about fortezza??? */ + default: + PORT_SetError(SEC_ERROR_BAD_KEY); + goto loser; + } + templateCount = attrs - theTemplate; + PORT_Assert(templateCount <= sizeof(theTemplate) / sizeof(CK_ATTRIBUTE)); + PORT_Assert(signedattr != NULL); + signedcount = attrs - signedattr; + + for (ap = signedattr; signedcount; ap++, signedcount--) { + pk11_SignedToUnsigned(ap); + } + + rv = PK11_CreateNewObject(slot, CK_INVALID_SESSION, + 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; + + default: + keyTemplate = NULL; + paramTemplate = NULL; + paramDest = NULL; + break; + } + + if (!keyTemplate) { + goto loser; + } + + /* decode the private key and any algorithm parameters */ + rv = SEC_ASN1DecodeItem(arena, lpk, keyTemplate, &pki->privateKey); + 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; + PLArenaPool *arena = NULL; + SECKEYRawPrivateKey rawKey; + SECKEYPrivateKeyInfo *pki; + SECItem *encoded; + SECStatus rv; + + if (pk->keyType != rsaKey) { + PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); + goto loser; + } + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!arena) { + goto loser; + } + memset(&rawKey, 0, sizeof(rawKey)); + rawKey.keyType = pk->keyType; + 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; + } + + pki = PORT_ArenaZNew(arena, SECKEYPrivateKeyInfo); + if (!pki) { + goto loser; + } + encoded = SEC_ASN1EncodeItem(arena, &pki->privateKey, &rawKey, + SECKEY_RSAPrivateKeyExportTemplate); + if (!encoded) { + goto loser; + } + rv = SECOID_SetAlgorithmID(arena, &pki->algorithm, + SEC_OID_PKCS1_RSA_ENCRYPTION, NULL); + if (rv != SECSuccess) { + 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 000000000..d7c5f3613 --- /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_NETSCAPE_PQG_COUNTER, NULL, 0 }, + { CKA_NETSCAPE_PQG_SEED, NULL, 0 }, + { CKA_NETSCAPE_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_NETSCAPE_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_NETSCAPE_PQG_COUNTER, NULL, 0 }, + { CKA_NETSCAPE_PQG_SEED, NULL, 0 }, + { CKA_NETSCAPE_PQG_H, NULL, 0 }, + }; + CK_ATTRIBUTE *attrs; + CK_BBOOL ckfalse = CK_FALSE; + CK_OBJECT_CLASS class = CKO_KG_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_NETSCAPE_PQG_COUNTER, + &counter, sizeof(counter)); + attrs++; + } + PK11_SETATTRS(attrs, CKA_NETSCAPE_PQG_SEED, + vfy->seed.data, vfy->seed.len); + attrs++; + if (vfy->h.len) { + PK11_SETATTRS(attrs, CKA_NETSCAPE_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(¶ms->prime, PR_FALSE); /* don't free prime */ + SECITEM_FreeItem(¶ms->subPrime, PR_FALSE); /* don't free subPrime */ + SECITEM_FreeItem(¶ms->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, ¶ms->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, ¶ms->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, ¶ms->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 000000000..36596dd47 --- /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 000000000..45a60b42a --- /dev/null +++ b/security/nss/lib/pk11wrap/pk11priv.h @@ -0,0 +1,187 @@ +/* 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 "plarena.h" +#include "seccomon.h" +#include "secoidt.h" +#include "secdert.h" +#include "keyt.h" +#include "certt.h" +#include "pkcs11t.h" +#include "secmodt.h" +#include "seccomon.h" +#include "pkcs7t.h" +#include "cmsreclist.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); +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); +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); + +/********************************************************************** + * 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); + +/********************************************************************** + * 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(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); + +/********************************************************************** + * 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); + +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 000000000..e11af86ad --- /dev/null +++ b/security/nss/lib/pk11wrap/pk11pub.h @@ -0,0 +1,879 @@ +/* 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 "keyt.h" +#include "certt.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_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); +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_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); +PK11SymKey *PK11_GetWrapKey(PK11SlotInfo *slot, int wrap, + CK_MECHANISM_TYPE type, int series, void *wincx); +/* + * This function is not thread-safe. It can only be called when only + * one thread has a reference to wrapKey. + */ +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_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); +/* + * 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_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 *wincx); +SECKEYEncryptedPrivateKeyInfo *PK11_ExportEncryptedPrivateKeyInfo( + PK11SlotInfo *slot, SECOidTag algTag, SECItem *pwitem, + CERTCertificate *cert, int iteration, void *wincx); +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); +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_FindCertInSlot(PK11SlotInfo *slot, CERTCertificate *cert, + void *wincx); +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); + +/********************************************************************** + * 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, 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); +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_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_WriteRawAttribute(PK11ObjectType type, void *object, + CK_ATTRIBUTE_TYPE attr, SECItem *item); + +/* + * 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); + +/********************************************************************** + * New functions which are already deprecated.... + **********************************************************************/ +SECItem * +PK11_GetLowLevelKeyIDForCert(PK11SlotInfo *slot, + CERTCertificate *cert, void *pwarg); +SECItem * +PK11_GetLowLevelKeyIDForPrivateKey(SECKEYPrivateKey *key); + +PRBool SECMOD_HasRootCerts(void); + +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 000000000..eb67bfba8 --- /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 000000000..0ffa42534 --- /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 000000000..e6301388e --- /dev/null +++ b/security/nss/lib/pk11wrap/pk11skey.c @@ -0,0 +1,2770 @@ +/* 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 "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 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_SESSION)) { + symKey->session = pk11_GetNewSession(slot, &symKey->sessionOwner); + } + PORT_Assert(symKey->session != CK_INVALID_SESSION); + if (symKey->session != CK_INVALID_SESSION) + 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_SESSION); + if (symKey->session == CK_INVALID_SESSION) { + PK11_FreeSymKey(symKey); + symKey = NULL; + } + } else { + symKey->session = CK_INVALID_SESSION; + } + 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_SESSION) { + 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 (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_SESSION. + * though sessionOwner is false, callers should not depend on + * this fact. + */ + if (symKey->sessionOwner) { + PORT_Assert(symKey->session != CK_INVALID_SESSION); + symKey->next = slot->freeSymKeysWithSessionHead; + slot->freeSymKeysWithSessionHead = symKey; + } else { + symKey->session = CK_INVALID_SESSION; + 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_SESSION); + if (parent->session == CK_INVALID_SESSION) { + PK11_FreeSymKey(symKey); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return NULL; + } + } + + return symKey; +} + +/* + * turn key handle into an appropriate key object + */ +PK11SymKey * +PK11_GetWrapKey(PK11SlotInfo *slot, int wrap, CK_MECHANISM_TYPE type, + int series, void *wincx) +{ + PK11SymKey *symKey = NULL; + + if (slot->series != series) + return NULL; + if (slot->refKeys[wrap] == CK_INVALID_HANDLE) + return NULL; + if (type == CKM_INVALID_MECHANISM) + type = slot->wrapMechanism; + + symKey = PK11_SymKeyFromHandle(slot, NULL, PK11_OriginDerive, + slot->wrapMechanism, slot->refKeys[wrap], PR_FALSE, wincx); + return symKey; +} + +/* + * This function is not thread-safe because it sets wrapKey->sessionOwner + * without using a lock or atomic routine. It can only be called when + * only one thread has a reference to wrapKey. + */ +void +PK11_SetWrapKey(PK11SlotInfo *slot, int wrap, PK11SymKey *wrapKey) +{ + /* save the handle and mechanism for the wrapping key */ + /* mark the key and session as not owned by us to they don't get freed + * when the key goes way... that lets us reuse the key later */ + slot->refKeys[wrap] = wrapKey->objectID; + wrapKey->owner = PR_FALSE; + wrapKey->sessionOwner = PR_FALSE; + slot->wrapMechanism = wrapKey->type; +} + +/* + * 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; + + 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; +} + +/* + * 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; + + 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; + int 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 symetric 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->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_SESSION) + pk11_EnterKeyMonitor(symKey); + } + if (session == CK_INVALID_SESSION) { + 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_SESSION) { + 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 (which only RSA can do). + * Use PK11_PubGenKey and PK11_WrapSymKey to implement the FORTEZZA and + * Diffie-Hellman Ciphers. */ +SECStatus +PK11_PubWrapSymKey(CK_MECHANISM_TYPE type, SECKEYPublicKey *pubKey, + 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, type, CKA_ENCRYPT); + if (newKey != NULL) { + symKey = newKey; + } + + if (symKey->slot == NULL) { + PORT_SetError(SEC_ERROR_NO_MODULE); + return SECFailure; + } + + slot = symKey->slot; + mechanism.mechanism = pk11_mapWrapKeyType(pubKey->keyType); + mechanism.pParameter = NULL; + mechanism.ulParameterLen = 0; + + 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; +} + +/* + * 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 *newKey = NULL; + SECItem *param_save = NULL; + CK_MECHANISM mechanism; + PRBool owner = PR_TRUE; + CK_SESSION_HANDLE session; + CK_RV crv; + SECStatus rv; + + /* if this slot doesn't support the mechanism, go to a slot that does */ + /* Force symKey and wrappingKey into the same slot */ + if ((wrappingKey->slot == NULL) || (symKey->slot != wrappingKey->slot)) { + /* first try copying the wrapping Key to the symKey slot */ + if (symKey->slot && PK11_DoesMechanism(symKey->slot, type)) { + newKey = pk11_CopyToSlot(symKey->slot, type, CKA_WRAP, wrappingKey); + } + /* Nope, try it the other way */ + if (newKey == NULL) { + if (wrappingKey->slot) { + newKey = pk11_CopyToSlot(wrappingKey->slot, + symKey->type, CKA_ENCRYPT, symKey); + } + /* just not playing... one last thing, can we get symKey's data? + * If it's possible, we it should already be in the + * symKey->data.data pointer because pk11_CopyToSlot would have + * tried to put it there. */ + if (newKey == NULL) { + /* Can't get symKey's data: Game Over */ + if (symKey->data.data == NULL) { + 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; + } + /* we successfully moved the sym Key */ + symKey = newKey; + } else { + /* we successfully moved the wrapping Key */ + wrappingKey = newKey; + } + } + + /* 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; + } + if (newKey) + PK11_FreeSymKey(newKey); + 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; + } + + /* 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_SESSION) { + 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); + 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, + ¶m, 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, + ¶m, 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 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; + } + + 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 = ¶m; + 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) { + 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; + } + + 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_Assert(!"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) { + 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; + } + + /* 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_SESSION); + if (rwsession == CK_INVALID_SESSION) + 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_SESSION) + 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 symetric key with a private key. */ +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); + PK11SlotInfo *slot = wrappingKey->pkcs11Slot; + + 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, 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 000000000..c66ae275c --- /dev/null +++ b/security/nss/lib/pk11wrap/pk11slot.c @@ -0,0 +1,2484 @@ +/* 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 "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" + +/************************************************************* + * 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->sessionLock = mod->isThreadSafe ? PZ_NewLock(nssILockSession) : mod->refLock; + if (slot->sessionLock == NULL) { + PORT_Free(slot); + return NULL; + } + slot->freeListLock = PZ_NewLock(nssILockFreelist); + if (slot->freeListLock == NULL) { + if (mod->isThreadSafe) { + PZ_DestroyLock(slot->sessionLock); + } + 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_SESSION; + 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)); + 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; + 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->isThreadSafe && slot->sessionLock) { + PZ_DestroyLock(slot->sessionLock); + } + slot->sessionLock = NULL; + if (slot->freeListLock) { + PZ_DestroyLock(slot->freeListLock); + slot->freeListLock = 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; +} + +PK11SlotInfo * +PK11_FindSlotByName(const char *name) +{ + SECMODModuleList *mlp; + SECMODModuleList *modules; + SECMODListLock *moduleLock = SECMOD_GetDefaultModuleListLock(); + int i; + PK11SlotInfo *slot = NULL; + + if (!moduleLock) { + PORT_SetError(SEC_ERROR_NOT_INITIALIZED); + return slot; + } + if ((name == NULL) || (*name == 0)) { + return PK11_GetInternalKeySlot(); + } + + /* 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 (PORT_Strcmp(tmpSlot->token_name, name) == 0) { + slot = PK11_ReferenceSlot(tmpSlot); + break; + } + } + } + if (slot != NULL) + break; + } + SECMOD_ReleaseReadLock(moduleLock); + + if (slot == NULL) { + PORT_SetError(SEC_ERROR_NO_TOKEN); + } + + return slot; +} + +PK11SlotInfo * +PK11_FindSlotBySerial(char *serial) +{ + SECMODModuleList *mlp; + SECMODModuleList *modules; + SECMODListLock *moduleLock = SECMOD_GetDefaultModuleListLock(); + 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 (PORT_Memcmp(tmpSlot->serial, serial, + sizeof(tmpSlot->serial)) == 0) { + slot = PK11_ReferenceSlot(tmpSlot); + break; + } + } + } + if (slot != NULL) + break; + } + SECMOD_ReleaseReadLock(moduleLock); + + if (slot == NULL) { + PORT_SetError(SEC_ERROR_NO_TOKEN); + } + + return slot; +} + +/* + * 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_SESSION); + if (slot->session != CK_INVALID_SESSION) + 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_SESSION || crv != CKR_OK); + if (crv != CKR_OK || rwsession == CK_INVALID_SESSION) { + if (crv == CKR_OK) + crv = CKR_DEVICE_ERROR; + if (haveMonitor) + PK11_ExitSlotMonitor(slot); + PORT_SetError(PK11_MapError(crv)); + return CK_INVALID_SESSION; + } + 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_SESSION)); + 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_SESSION); + 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_SESSION); + if (rwsession != CK_INVALID_SESSION) { + 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; +} + +/* + * 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; +} + +/* + * 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_TOKEN_INFO tokenInfo; + CK_RV crv; + SECStatus rv; + PRStatus status; + + /* set the slot flags to the current token values */ + if (!slot->isThreadSafe) + PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_GetTokenInfo(slot->slotID, &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 = tokenInfo.flags; + slot->needLogin = ((tokenInfo.flags & CKF_LOGIN_REQUIRED) ? PR_TRUE : PR_FALSE); + slot->readOnly = ((tokenInfo.flags & CKF_WRITE_PROTECTED) ? PR_TRUE : PR_FALSE); + + slot->hasRandom = ((tokenInfo.flags & CKF_RNG) ? PR_TRUE : PR_FALSE); + slot->protectedAuthPath = + ((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 *)tokenInfo.label, sizeof(tokenInfo.label)); + slot->minPassword = tokenInfo.ulMinPinLen; + slot->maxPassword = tokenInfo.ulMaxPinLen; + PORT_Memcpy(slot->serial, tokenInfo.serialNumber, sizeof(slot->serial)); + + nssToken_UpdateName(slot->nssToken); + + slot->defRWSession = (PRBool)((!slot->readOnly) && + (tokenInfo.ulMaxSessionCount == 1)); + rv = PK11_ReadMechanismList(slot); + if (rv != SECSuccess) + return rv; + + slot->hasRSAInfo = PR_FALSE; + slot->RSAInfoFlags = 0; + + /* initialize the maxKeyCount value */ + if (tokenInfo.ulMaxSessionCount == 0) { + slot->maxKeyCount = 800; /* should be #define or a config param */ + } else if (tokenInfo.ulMaxSessionCount < 20) { + /* don't have enough sessions to keep that many keys around */ + slot->maxKeyCount = 0; + } else { + slot->maxKeyCount = tokenInfo.ulMaxSessionCount / 2; + } + + /* Make sure our session handle is valid */ + if (slot->session == CK_INVALID_SESSION) { + /* 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_SESSION; + if (!slot->isThreadSafe) + PK11_ExitSlotMonitor(slot); + return SECFailure; + } + } + if (!slot->isThreadSafe) + PK11_ExitSlotMonitor(slot); + } + + status = nssToken_Refresh(slot->nssToken); + if (status != PR_SUCCESS) + return SECFailure; + + 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_SESSION; + + /* 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_TOKEN_INFO tokenInfo; + 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, &tokenInfo); + if (!slot->isThreadSafe) + PK11_ExitSlotMonitor(slot); + if (crv != CKR_OK) { + PORT_SetError(PK11_MapError(crv)); + return SECFailure; + } + + slot->flags = tokenInfo.flags; + slot->needLogin = ((tokenInfo.flags & CKF_LOGIN_REQUIRED) ? PR_TRUE : PR_FALSE); + slot->readOnly = ((tokenInfo.flags & CKF_WRITE_PROTECTED) ? PR_TRUE : PR_FALSE); + slot->hasRandom = ((tokenInfo.flags & CKF_RNG) ? PR_TRUE : PR_FALSE); + slot->protectedAuthPath = + ((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_NETSCAPE_BUILTIN_ROOT_LIST; + int 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; + + if (PK11_GETTAB(slot)->C_GetSlotInfo(slotID, &slotInfo) != CKR_OK) { + 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; + 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 */ + (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; + } + } +} + +/********************************************************************* + * 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_SESSION)) { + return PR_TRUE; + } + + if (slot->nssToken) { + return nssToken_IsPresent(slot->nssToken); + } + + /* removable slots have a flag that says they are present */ + if (!slot->isThreadSafe) + PK11_EnterSlotMonitor(slot); + if (PK11_GETTAB(slot)->C_GetSlotInfo(slot->slotID, &slotInfo) != CKR_OK) { + if (!slot->isThreadSafe) + PK11_ExitSlotMonitor(slot); + 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_SESSION) { + PK11_GETTAB(slot) + ->C_CloseSession(slot->session); + slot->session = CK_INVALID_SESSION; + } + 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_SESSION) { + 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_SESSION; + } + if (slot->isThreadSafe) + PK11_ExitSlotMonitor(slot); + } + if (!slot->isThreadSafe) + PK11_ExitSlotMonitor(slot); + + /* card has not been removed, current token info is correct */ + if (slot->session != CK_INVALID_SESSION) + 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 || + ((slot->defaultFlags & SECMOD_FRIENDLY_FLAG) == + SECMOD_FRIENDLY_FLAG)); +} + +char * +PK11_GetTokenName(PK11SlotInfo *slot) +{ + return slot->token_name; +} + +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; +} + +/* 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; +} + +/* + * 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; +#if defined(XP_WIN32) + int j = 0; + PRInt32 waste[16]; +#endif + + 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) { + +#if defined(XP_WIN32) + /* This is works around some horrible cache/page thrashing problems + ** on Win32. Without this, this loop can take up to 6 seconds at + ** 100% CPU on a Pentium-Pro 200. The thing this changes is to + ** increase the size of the stack frame and modify it. + ** Moving the loop code itself seems to have no effect. + ** Dunno why this combination makes a difference, but it does. + */ + waste[j & 0xf] = j++; +#endif + + 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; + } + } + } + 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(); /* can't fail */ + 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]; + int 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_SESSION; + + /* 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; + } + nssTrustDomain_UpdateCachedTokenCerts(slot->nssToken->trustDomain, + slot->nssToken); + return SECSuccess; +} +void +PK11Slot_SetNSSToken(PK11SlotInfo *sl, NSSToken *nsst) +{ + sl->nssToken = nsst; +} + +NSSToken * +PK11Slot_GetNSSToken(PK11SlotInfo *sl) +{ + return sl->nssToken; +} + +/* + * 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 000000000..9636b073c --- /dev/null +++ b/security/nss/lib/pk11wrap/pk11util.c @@ -0,0 +1,1629 @@ +/* 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 "utilpars.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; +} + +/* + * 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) +{ + SECMODModuleList *mlp; + SECMODModuleList **mlpp; + SECStatus rv = SECFailure; + + if (pendingModule) { + PORT_SetError(SEC_ERROR_MODULE_STUCK); + return rv; + } + if (!moduleLock) { + PORT_SetError(SEC_ERROR_NOT_INITIALIZED); + return rv; + } + + 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; +} + +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; +} + +/* 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) +{ + return (PRBool)(pendingModule == NULL); +} + +/* + * 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. + */ +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 && slot->nssToken && slot->nssToken->slot) { + nssSlot_ResetDelay(slot->nssToken->slot); + } + 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) +{ + int i; + PRBool ret = PR_FALSE; + + if (!moduleLock) { + PORT_SetError(SEC_ERROR_NOT_INITIALIZED); + return ret; + } + SECMOD_GetReadLock(moduleLock); + 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; + } + if (mod->slotCount == 0) { + ret = PR_TRUE; + } + SECMOD_ReleaseReadLock(moduleLock); + 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_NETSCAPE_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_NETSCAPE_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... */ + if (slot->nssToken && slot->nssToken->slot) { + nssSlot_ResetDelay(slot->nssToken->slot); + } + /* 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_NETSCAPE_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... */ + if (slot->nssToken && slot->nssToken->slot) { + nssSlot_ResetDelay(slot->nssToken->slot); + /* 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 000000000..2af27a052 --- /dev/null +++ b/security/nss/lib/pk11wrap/pk11wrap.gyp @@ -0,0 +1,51 @@ +# 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', + 'type': 'static_library', + 'sources': [ + 'dev3hack.c', + 'pk11akey.c', + 'pk11auth.c', + 'pk11cert.c', + 'pk11cxt.c', + 'pk11err.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' + ], + 'dependencies': [ + '<(DEPTH)/exports.gyp:nss_exports' + ] + } + ], + 'target_defaults': { + 'defines': [ + 'SHLIB_SUFFIX=\"<(dll_suffix)\"', + 'SHLIB_PREFIX=\"<(dll_prefix)\"', + 'SHLIB_VERSION=\"3\"', + 'SOFTOKEN_SHLIB_VERSION=\"3\"' + ] + }, + 'variables': { + 'module': 'nss' + } +}
\ No newline at end of file diff --git a/security/nss/lib/pk11wrap/secmod.h b/security/nss/lib/pk11wrap/secmod.h new file mode 100644 index 000000000..fcc770780 --- /dev/null +++ b/security/nss/lib/pk11wrap/secmod.h @@ -0,0 +1,167 @@ +/* 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); + +PRBool SECMOD_HasRemovableSlots(SECMODModule *mod); +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. + */ +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 000000000..1225661ad --- /dev/null +++ b/security/nss/lib/pk11wrap/secmodi.h @@ -0,0 +1,170 @@ +/* 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 "pkcs11.h" +#include "nssilock.h" +#include "secoidt.h" +#include "secdert.h" +#include "certt.h" +#include "secmodt.h" +#include "keyt.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, int tsize); +CK_OBJECT_HANDLE *pk11_FindObjectsByTemplate(PK11SlotInfo *slot, + CK_ATTRIBUTE *inTemplate, int tsize, int *objCount); + +#define PK11_GETTAB(x) ((CK_FUNCTION_LIST_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); + +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 000000000..23abe307f --- /dev/null +++ b/security/nss/lib/pk11wrap/secmodt.h @@ -0,0 +1,444 @@ +/* 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 */ +}; + +/* 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 000000000..520165573 --- /dev/null +++ b/security/nss/lib/pk11wrap/secmodti.h @@ -0,0 +1,183 @@ +/* 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; + /* fast mechanism lookup */ + char mechanismBits[256]; +}; + +/* 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 used in this context */ + PK11SlotInfo *slot; /* slot this context is operationing on */ + 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 *cx; /* window context in case we need to loggin*/ + 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; /* has 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*/ +}; + +/* + * 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; +}; + +#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 000000000..57856767b --- /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_ */ |