# # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # include $(SRC)/lib/Makefile.lib # Hammerhead: amd64-only SUBDIRS= $(MACH64) all : TARGET= all check : TARGET= check clean : TARGET= clean clobber : TARGET= clobber install : TARGET= install .KEEP_STATE: all check clean clobber install: $(SUBDIRS) $(SUBDIRS): FRC @cd $@; pwd; $(MAKE) $(TARGET) FRC: # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. # # Copyright 2010 Nexenta Systems, Inc. All rights reserved. # Copyright 2020 Joyent, Inc. # LIBRARY = pkcs11_softtoken.a VERS= .1 LCL_OBJECTS = \ softGeneral.o \ softSlotToken.o \ softSession.o \ softObject.o \ softDigest.o \ softSign.o \ softVerify.o \ softDualCrypt.o \ softKeys.o \ softRand.o \ softSessionUtil.o \ softDigestUtil.o \ softAttributeUtil.o \ softObjectUtil.o \ softDESCrypt.o \ softEncrypt.o \ softDecrypt.o \ softEncryptUtil.o \ softDecryptUtil.o \ softSignUtil.o \ softVerifyUtil.o \ softMAC.o \ softRSA.o \ softKeysUtil.o \ softARCFourCrypt.o \ softDSA.o \ softDH.o \ softAESCrypt.o \ softKeystore.o \ softKeystoreUtil.o \ softSSL.o \ softASN1.o \ softBlowfishCrypt.o \ softEC.o ASFLAGS = $(AS_PICFLAGS) -D__STDC__ -D_ASM $(CPPFLAGS) ECC_COBJECTS = \ ec.o ec2_163.o ec2_mont.o ecdecode.o ecl_mult.o ecp_384.o \ ecp_jac.o ec2_193.o ecl.o ecp_192.o ecp_521.o \ ecp_jm.o ec2_233.o ecl_curve.o ecp_224.o ecp_aff.o ecp_mont.o \ ec2_aff.o ec_naf.o ecl_gf.o ecp_256.o oid.o secitem.o \ ec2_test.o ecp_test.o MPI_COBJECTS = mp_gf2m.o mpi.o mplogic.o mpmontg.o mpprime.o RNG_COBJECTS = fips_random.o ECC_OBJECTS = $(ECC_COBJECTS) $(ECC_PSR_OBJECTS) MPI_OBJECTS = $(MPI_COBJECTS) $(MPI_PSR_OBJECTS) RNG_OBJECTS = $(RNG_COBJECTS) BER_OBJECTS = bprint.o decode.o encode.o io.o OBJECTS = \ $(LCL_OBJECTS) \ $(MPI_OBJECTS) \ $(RNG_OBJECTS) \ $(BIGNUM_OBJECTS) \ $(BER_OBJECTS) \ $(ECC_OBJECTS) AESDIR= $(SRC)/common/crypto/aes BLOWFISHDIR= $(SRC)/common/crypto/blowfish ARCFOURDIR= $(SRC)/common/crypto/arcfour DESDIR= $(SRC)/common/crypto/des DHDIR= $(SRC)/common/crypto/dh DSADIR= $(SRC)/common/crypto/dsa ECCDIR= $(SRC)/common/crypto/ecc MPIDIR= $(SRC)/common/mpi RSADIR= $(SRC)/common/crypto/rsa RNGDIR= $(SRC)/common/crypto/rng SHA1DIR= $(SRC)/common/crypto/sha1 SHA2DIR= $(SRC)/common/crypto/sha2 BIGNUMDIR= $(SRC)/common/bignum PADDIR= $(SRC)/common/crypto/padding BERDIR= ../../../libldap5/sources/ldap/ber include $(SRC)/lib/Makefile.lib # set signing mode POST_PROCESS_SO += ; $(ELFSIGN_CRYPTO) SRCDIR= ../common SRCS = \ $(LCL_OBJECTS:%.o=$(SRCDIR)/%.c) \ $(MPI_COBJECTS:%.o=$(MPIDIR)/%.c) \ $(ECC_COBJECTS:%.o=$(ECCDIR)/%.c) \ $(RNG_COBJECTS:%.o=$(RNGDIR)/%.c) # libelfsign needs a static pkcs11_softtoken LIBS = $(DYNLIB) LDLIBS += -lc -lmd -lcryptoutil -lsoftcrypto -lgen -lavl # Hammerhead: GNU ld needs -Bsymbolic so provider's C_* function table # binds to its own definitions, not libpkcs11's (which are already in # global scope when the provider is dlopen'd). Without this, the # CK_FUNCTION_LIST relocations resolve to libpkcs11's C_Initialize # causing infinite recursion and stack overflow. DYNFLAGS += -Wl,-Bsymbolic CSTD = $(CSTD_GNU99) CFLAGS += $(CCVERBOSE) CERRWARN += -Wno-parentheses CERRWARN += $(CNOWARN_UNINIT) CERRWARN += -Wno-type-limits CERRWARN += -Wno-unused-variable CERRWARN += -Wno-empty-body # not linted SMATCH=off CPPFLAGS += -I$(AESDIR) -I$(BLOWFISHDIR) -I$(ARCFOURDIR) -I$(DESDIR) \ -I$(DHDIR) -I$(DSADIR) -I$(ECCDIR) -I$(SRC)/common/crypto \ -I$(MPIDIR) -I$(RSADIR) -I$(RNGDIR) \ -I$(SHA1DIR) -I$(SHA2DIR) -I$(SRCDIR) \ -I$(BIGNUMDIR) -I$(PADDIR) -D_POSIX_PTHREAD_SEMANTICS \ -DMP_API_COMPATIBLE -DNSS_ECC_MORE_THAN_SUITE_B ROOTLIBDIR= $(ROOT)/usr/lib/security # Hammerhead: 64-bit only - flatten security library path ROOTLIBDIR64= $(ROOT)/usr/lib/security .KEEP_STATE: all: $(LIBS) pics/%.o: $(BERDIR)/%.c $(COMPILE.c) -o $@ $< -D_SOLARIS_SDK -I$(BERDIR) \ -I../../../libldap5/include/ldap $(POST_PROCESS_O) pics/%.o: $(ECCDIR)/%.c $(COMPILE.c) -o $@ $< $(POST_PROCESS_O) pics/%.o: $(MPIDIR)/%.c $(COMPILE.c) -o $@ $< $(POST_PROCESS_O) pics/%.o: $(RNGDIR)/%.c $(COMPILE.c) -o $@ $< $(POST_PROCESS_O) include $(SRC)/lib/Makefile.targ # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # lib/pkcs11/pkcs11_softtoken/amd64/Makefile include ../Makefile.com include ../../../Makefile.lib.64 install: all $(ROOTLIBS64) $(ROOTLINKS64) # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. # # # MAPFILE HEADER START # # WARNING: STOP NOW. DO NOT MODIFY THIS FILE. # Object versioning must comply with the rules detailed in # # usr/src/lib/README.mapfiles # # You should not be making modifications here until you've read the most current # copy of that file. If you need help, contact a gatekeeper for guidance. # # MAPFILE HEADER END # $mapfile_version 2 SYMBOL_VERSION SUNW_1.1 { global: C_CancelFunction; C_CloseAllSessions; C_CloseSession; C_CopyObject; C_CreateObject; C_Decrypt; C_DecryptDigestUpdate; C_DecryptFinal; C_DecryptInit; C_DecryptUpdate; C_DecryptVerifyUpdate; C_DeriveKey; C_DestroyObject; C_Digest; C_DigestEncryptUpdate; C_DigestFinal; C_DigestInit; C_DigestKey; C_DigestUpdate; C_Encrypt; C_EncryptFinal; C_EncryptInit; C_EncryptUpdate; C_Finalize; C_FindObjects; C_FindObjectsFinal; C_FindObjectsInit; C_GenerateKey; C_GenerateKeyPair; C_GenerateRandom; C_GetAttributeValue; C_GetFunctionList; C_GetFunctionStatus; C_GetInfo; C_GetMechanismInfo; C_GetMechanismList; C_GetObjectSize; C_GetOperationState; C_GetSessionInfo; C_GetSlotInfo; C_GetSlotList; C_GetTokenInfo; C_Initialize; C_InitPIN; C_InitToken; C_Login; C_Logout; C_OpenSession; C_SeedRandom; C_SetAttributeValue; C_SetOperationState; C_SetPIN; C_Sign; C_SignEncryptUpdate; C_SignFinal; C_SignInit; C_SignRecover; C_SignRecoverInit; C_SignUpdate; C_UnwrapKey; C_Verify; C_VerifyFinal; C_VerifyInit; C_VerifyRecover; C_VerifyRecoverInit; C_VerifyUpdate; C_WaitForSlotEvent; C_WrapKey; local: *; }; /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2014 Nexenta Systems, Inc. All rights reserved. * Copyright 2019 Joyent, Inc. * Copyright 2017 Jason King. * Copyright 2025 RackTop Systems, Inc. */ #include #include #include #include #include #include #include #include #include #include "softSession.h" #include "softObject.h" #include "softCrypt.h" #include "softOps.h" /* * Check that the mechanism parameter is present (if required) * and has the correct size. If so, allocate an AES context. * * Special handling for AES_GMAC paramters: * * The current PKCS#11 documentation for using AES_GMAC is: * https://docs.oasis-open.org/pkcs11/pkcs11-curr/v2.40/ * errata01/os/pkcs11-curr-v2.40-errata01-os-complete.html * According to that, the C_SignInit parameter for AES_GMAC is * just the 12-byte Initialization Vector (IV). The passed IV * is converted to CK_AES_GMAC_PARAMS soft_aes_init_ctx(). */ static CK_RV soft_aes_check_mech_param(CK_MECHANISM_PTR mech, aes_ctx_t **ctxp) { void *(*allocf)(int) = NULL; size_t param_len = 0; boolean_t param_req = B_TRUE; switch (mech->mechanism) { case CKM_AES_ECB: param_req = B_FALSE; allocf = ecb_alloc_ctx; break; case CKM_AES_CMAC: param_req = B_FALSE; allocf = cmac_alloc_ctx; break; case CKM_AES_CMAC_GENERAL: param_len = sizeof (CK_MAC_GENERAL_PARAMS); allocf = cmac_alloc_ctx; break; case CKM_AES_GMAC: /* See comment above this function. */ param_len = AES_GMAC_IV_LEN; allocf = gmac_alloc_ctx; break; case CKM_AES_CBC: case CKM_AES_CBC_PAD: param_len = AES_BLOCK_LEN; allocf = cbc_alloc_ctx; break; case CKM_AES_CTR: param_len = sizeof (CK_AES_CTR_PARAMS); allocf = ctr_alloc_ctx; break; case CKM_AES_CCM: param_len = sizeof (CK_CCM_PARAMS); allocf = ccm_alloc_ctx; break; case CKM_AES_GCM: param_len = sizeof (CK_GCM_PARAMS); allocf = gcm_alloc_ctx; break; default: return (CKR_MECHANISM_INVALID); } if (param_req && (mech->pParameter == NULL || mech->ulParameterLen != param_len)) { return (CKR_MECHANISM_PARAM_INVALID); } *ctxp = allocf(0); if (*ctxp == NULL) { return (CKR_HOST_MEMORY); } return (CKR_OK); } /* * Create an AES key schedule for the given AES context from the given key. * If the key is not sensitive, cache a copy of the key schedule in the * key object and/or use the cached copy of the key schedule. * * Must be called before the init function for a given mode is called. */ static CK_RV soft_aes_init_key(aes_ctx_t *aes_ctx, soft_object_t *key_p) { void *ks = NULL; size_t size = 0; CK_RV rv = CKR_OK; (void) pthread_mutex_lock(&key_p->object_mutex); /* * AES keys should be either 128, 192, or 256 bits long. * soft_object_t stores the key size in bytes, so we check those sizes * in bytes. * * While soft_build_secret_key_object() does these same validations for * keys created by the user, it may be possible that a key loaded from * disk could be invalid or corrupt. We err on the side of caution * and check again that it's the correct size before performing any * AES operations. */ switch (OBJ_SEC_VALUE_LEN(key_p)) { case AES_MIN_KEY_BYTES: case AES_MAX_KEY_BYTES: case AES_192_KEY_BYTES: break; default: rv = CKR_KEY_SIZE_RANGE; goto done; } ks = aes_alloc_keysched(&size, 0); if (ks == NULL) { rv = CKR_HOST_MEMORY; goto done; } /* If this is a sensitive key, always expand the key schedule */ if (key_p->bool_attr_mask & SENSITIVE_BOOL_ON) { /* aes_init_keysched() requires key length in bits. */ #ifdef __sparcv9 /* LINTED */ aes_init_keysched(OBJ_SEC_VALUE(key_p), (uint_t) (OBJ_SEC_VALUE_LEN(key_p) * NBBY), ks); #else /* !__sparcv9 */ aes_init_keysched(OBJ_SEC_VALUE(key_p), (OBJ_SEC_VALUE_LEN(key_p) * NBBY), ks); #endif /* __sparcv9 */ goto done; } /* If a non-sensitive key and doesn't have a key schedule, create it */ if (OBJ_KEY_SCHED(key_p) == NULL) { void *obj_ks = NULL; obj_ks = aes_alloc_keysched(&size, 0); if (obj_ks == NULL) { rv = CKR_HOST_MEMORY; goto done; } #ifdef __sparcv9 /* LINTED */ aes_init_keysched(OBJ_SEC_VALUE(key_p), (uint_t)(OBJ_SEC_VALUE_LEN(key_p) * 8), obj_ks); #else /* !__sparcv9 */ aes_init_keysched(OBJ_SEC_VALUE(key_p), (OBJ_SEC_VALUE_LEN(key_p) * 8), obj_ks); #endif /* __sparcv9 */ OBJ_KEY_SCHED_LEN(key_p) = size; OBJ_KEY_SCHED(key_p) = obj_ks; } (void) memcpy(ks, OBJ_KEY_SCHED(key_p), OBJ_KEY_SCHED_LEN(key_p)); done: (void) pthread_mutex_unlock(&key_p->object_mutex); if (rv == CKR_OK) { aes_ctx->ac_keysched = ks; aes_ctx->ac_keysched_len = size; } else { freezero(ks, size); } return (rv); } /* * Initialize the AES context for the given mode, including allocating and * expanding the key schedule if required. */ static CK_RV soft_aes_init_ctx(aes_ctx_t *aes_ctx, CK_MECHANISM_PTR mech_p, boolean_t encrypt) { int rc = CRYPTO_SUCCESS; switch (mech_p->mechanism) { case CKM_AES_ECB: aes_ctx->ac_flags |= ECB_MODE; break; case CKM_AES_CMAC: case CKM_AES_CMAC_GENERAL: rc = cmac_init_ctx((cbc_ctx_t *)aes_ctx, AES_BLOCK_LEN); break; case CKM_AES_GMAC: { /* * Given params are just the initialization vector. * See comment above soft_aes_check_mech_param. * Convert to CK_AES_GMAC_PARAMS for gmac_init_ctx. */ CK_AES_GMAC_PARAMS gmac_params = { .pIv = mech_p->pParameter, .pAAD = NULL, .ulAADLen = 0 }; rc = gmac_init_ctx((gcm_ctx_t *)aes_ctx, (char *)&gmac_params, AES_BLOCK_LEN, aes_encrypt_block, aes_copy_block, aes_xor_block); break; } case CKM_AES_CBC: case CKM_AES_CBC_PAD: rc = cbc_init_ctx((cbc_ctx_t *)aes_ctx, mech_p->pParameter, mech_p->ulParameterLen, AES_BLOCK_LEN, aes_copy_block64); break; case CKM_AES_CTR: { /* * soft_aes_check_param() verifies this is !NULL and is the * correct size. */ CK_AES_CTR_PARAMS *pp = (CK_AES_CTR_PARAMS *)mech_p->pParameter; rc = ctr_init_ctx((ctr_ctx_t *)aes_ctx, pp->ulCounterBits, pp->cb, aes_encrypt_block, aes_copy_block); break; } case CKM_AES_CCM: { CK_CCM_PARAMS *pp = (CK_CCM_PARAMS *)mech_p->pParameter; /* * The illumos ccm mode implementation predates the PKCS#11 * version that specifies CK_CCM_PARAMS. As a result, the order * and names of the struct members are different, so we must * translate. ccm_init_ctx() does not store a ref ccm_params, * so it is safe to allocate on the stack. */ CK_AES_CCM_PARAMS ccm_params = { .ulMACSize = pp->ulMACLen, .ulNonceSize = pp->ulNonceLen, .ulAuthDataSize = pp->ulAADLen, .ulDataSize = pp->ulDataLen, .nonce = pp->pNonce, .authData = pp->pAAD }; rc = ccm_init_ctx((ccm_ctx_t *)aes_ctx, (char *)&ccm_params, 0, encrypt, AES_BLOCK_LEN, aes_encrypt_block, aes_xor_block); break; } case CKM_AES_GCM: /* * Similar to the ccm mode implementation, the gcm mode also * predates PKCS#11 2.40, however in this instance * CK_AES_GCM_PARAMS and CK_GCM_PARAMS are identical except * for the member names, so we can just pass it along. */ rc = gcm_init_ctx((gcm_ctx_t *)aes_ctx, mech_p->pParameter, AES_BLOCK_LEN, aes_encrypt_block, aes_copy_block, aes_xor_block); break; } return (crypto2pkcs11_error_number(rc)); } /* * Allocate context for the active encryption or decryption operation, and * generate AES key schedule to speed up the operation. */ CK_RV soft_aes_crypt_init_common(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism, soft_object_t *key_p, boolean_t encrypt) { aes_ctx_t *aes_ctx = NULL; CK_RV rv = CKR_OK; if (key_p->key_type != CKK_AES) return (CKR_KEY_TYPE_INCONSISTENT); /* C_{Encrypt,Decrypt}Init() validate pMechanism != NULL */ rv = soft_aes_check_mech_param(pMechanism, &aes_ctx); if (rv != CKR_OK) { goto done; } rv = soft_aes_init_key(aes_ctx, key_p); if (rv != CKR_OK) { goto done; } rv = soft_aes_init_ctx(aes_ctx, pMechanism, encrypt); if (rv != CKR_OK) { goto done; } (void) pthread_mutex_lock(&session_p->session_mutex); if (encrypt) { /* Called by C_EncryptInit. */ session_p->encrypt.context = aes_ctx; session_p->encrypt.mech.mechanism = pMechanism->mechanism; } else { /* Called by C_DecryptInit. */ session_p->decrypt.context = aes_ctx; session_p->decrypt.mech.mechanism = pMechanism->mechanism; } (void) pthread_mutex_unlock(&session_p->session_mutex); done: if (rv != CKR_OK) { soft_aes_free_ctx(aes_ctx); } return (rv); } CK_RV soft_aes_encrypt(soft_session_t *session_p, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pEncryptedData, CK_ULONG_PTR pulEncryptedDataLen) { aes_ctx_t *aes_ctx = session_p->encrypt.context; CK_MECHANISM_TYPE mech = session_p->encrypt.mech.mechanism; size_t length_needed; size_t remainder; int rc = CRYPTO_SUCCESS; CK_RV rv = CKR_OK; crypto_data_t out = { .cd_format = CRYPTO_DATA_RAW, .cd_offset = 0, .cd_length = *pulEncryptedDataLen, .cd_raw.iov_base = (char *)pEncryptedData, .cd_raw.iov_len = *pulEncryptedDataLen }; /* * A bit unusual, but it's permissible for ccm and gcm modes to not * encrypt any data. This ends up being equivalent to CKM_AES_CMAC * or CKM_AES_GMAC of the additional authenticated data (AAD). */ if ((pData == NULL || ulDataLen == 0) && !(aes_ctx->ac_flags & (CCM_MODE|GCM_MODE|CMAC_MODE|GMAC_MODE))) { return (CKR_ARGUMENTS_BAD); } remainder = ulDataLen % AES_BLOCK_LEN; /* * CTR, CCM, CMAC, and GCM modes do not require the plaintext * to be a multiple of the AES block size. CKM_AES_CBC_PAD as the * name suggests pads it's output, so it can also accept any * size plaintext. */ switch (mech) { case CKM_AES_CBC_PAD: case CKM_AES_CMAC: case CKM_AES_CMAC_GENERAL: case CKM_AES_CTR: case CKM_AES_CCM: case CKM_AES_GCM: case CKM_AES_GMAC: break; default: if (remainder != 0) { rv = CKR_DATA_LEN_RANGE; goto cleanup; } } switch (mech) { case CKM_AES_CCM: length_needed = ulDataLen + aes_ctx->ac_mac_len; break; case CKM_AES_GCM: length_needed = ulDataLen + aes_ctx->ac_tag_len; break; case CKM_AES_CMAC: case CKM_AES_CMAC_GENERAL: length_needed = AES_BLOCK_LEN; break; case CKM_AES_GMAC: length_needed = AES_BLOCK_LEN; break; case CKM_AES_CBC_PAD: /* CKM_AES_CBC_PAD always adds 1..AES_BLOCK_LEN of padding */ length_needed = ulDataLen + AES_BLOCK_LEN - remainder; break; default: length_needed = ulDataLen; break; } if (pEncryptedData == NULL) { /* * The application can ask for the size of the output buffer * with a NULL output buffer (pEncryptedData). * C_Encrypt() guarantees pulEncryptedDataLen != NULL. */ *pulEncryptedDataLen = length_needed; return (CKR_OK); } if (*pulEncryptedDataLen < length_needed) { *pulEncryptedDataLen = length_needed; return (CKR_BUFFER_TOO_SMALL); } if (ulDataLen > 0) { rv = soft_aes_encrypt_update(session_p, pData, ulDataLen, pEncryptedData, pulEncryptedDataLen); if (rv != CKR_OK) { rv = CKR_FUNCTION_FAILED; goto cleanup; } /* * Some modes (e.g. CCM and GCM) will append data such as a MAC * to the ciphertext after the plaintext has been encrypted. * Update out to reflect the amount of data in pEncryptedData * after encryption. */ out.cd_offset = *pulEncryptedDataLen; } switch (mech) { case CKM_AES_CBC_PAD: { /* * aes_encrypt_contiguous_blocks() accumulates plaintext * in aes_ctx until it has at least one full block of * plaintext. Any partial blocks of data remaining after * encrypting are left for subsequent calls to * aes_encrypt_contiguous_blocks(). If the input happened * to be an exact multiple of AES_BLOCK_LEN, we must still * append a block of padding (a full block in that case) so * that the correct amount of padding to remove is known * during decryption. * * soft_add_pkcs7_padding() is a bit overkill -- we just * create a block filled with the pad amount using memset(), * and encrypt 'amt' bytes of the block to pad out the input. */ char block[AES_BLOCK_LEN]; size_t amt = AES_BLOCK_LEN - remainder; VERIFY3U(remainder, ==, aes_ctx->ac_remainder_len); (void) memset(block, amt & 0xff, sizeof (block)); rc = aes_encrypt_contiguous_blocks(aes_ctx, block, amt, &out); rv = crypto2pkcs11_error_number(rc); explicit_bzero(block, sizeof (block)); break; } case CKM_AES_CCM: rc = ccm_encrypt_final((ccm_ctx_t *)aes_ctx, &out, AES_BLOCK_LEN, aes_encrypt_block, aes_xor_block); rv = crypto2pkcs11_error_number(rc); break; case CKM_AES_GCM: rc = gcm_encrypt_final((gcm_ctx_t *)aes_ctx, &out, AES_BLOCK_LEN, aes_encrypt_block, aes_copy_block, aes_xor_block); rv = crypto2pkcs11_error_number(rc); break; case CKM_AES_CMAC: case CKM_AES_CMAC_GENERAL: rc = cmac_mode_final((cbc_ctx_t *)aes_ctx, &out, aes_encrypt_block, aes_xor_block); rv = crypto2pkcs11_error_number(rc); aes_ctx->ac_remainder_len = 0; break; case CKM_AES_GMAC: rc = gmac_mode_final((gcm_ctx_t *)aes_ctx, &out, AES_BLOCK_LEN, aes_encrypt_block, aes_xor_block); rv = crypto2pkcs11_error_number(rc); break; case CKM_AES_CTR: /* * As CKM_AES_CTR is a stream cipher, ctr_mode_final is always * invoked in the xx_update() functions, so we do not need to * call it again here. */ break; case CKM_AES_ECB: case CKM_AES_CBC: /* * These mechanisms do not have nor require a xx_final function. */ break; default: rv = CKR_MECHANISM_INVALID; break; } cleanup: switch (rv) { case CKR_OK: *pulEncryptedDataLen = out.cd_offset; break; case CKR_BUFFER_TOO_SMALL: /* *pulEncryptedDataLen was set earlier */ break; default: /* something else failed */ *pulEncryptedDataLen = 0; break; } (void) pthread_mutex_lock(&session_p->session_mutex); soft_aes_free_ctx(aes_ctx); session_p->encrypt.context = NULL; (void) pthread_mutex_unlock(&session_p->session_mutex); return (rv); } static CK_RV soft_aes_cbc_pad_decrypt(aes_ctx_t *aes_ctx, CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen, crypto_data_t *out_orig) { aes_ctx_t *ctx = aes_ctx; uint8_t *buf = NULL; uint8_t *outbuf = (uint8_t *)out_orig->cd_raw.iov_base; crypto_data_t out = *out_orig; size_t i; int rc; CK_RV rv = CKR_OK; uint8_t pad_len; boolean_t speculate = B_FALSE; /* * Just a query for the output size. When the output buffer is * NULL, we are allowed to return a size slightly larger than * necessary. We know the output will never be larger than the * input ciphertext, so we use that as an estimate. */ if (out_orig->cd_raw.iov_base == NULL) { out_orig->cd_length = ulEncryptedDataLen; return (CKR_OK); } /* * The output plaintext size will be 1..AES_BLOCK_LEN bytes * smaller than the input ciphertext. However we cannot know * exactly how much smaller until we decrypt the entire * input ciphertext. If we are unsure we have enough output buffer * space, we have to allocate our own memory to hold the output, * then see if we have enough room to hold the result. * * Unfortunately, having an output buffer that's too small does * not terminate the operation, nor are we allowed to return * partial results. Therefore we must also duplicate the initial * aes_ctx so that this can potentially be run again. */ if (out_orig->cd_length < ulEncryptedDataLen) { void *ks = malloc(aes_ctx->ac_keysched_len); ctx = malloc(sizeof (*aes_ctx)); buf = malloc(ulEncryptedDataLen); if (ks == NULL || ctx == NULL || buf == NULL) { free(ks); free(ctx); free(buf); return (CKR_HOST_MEMORY); } bcopy(aes_ctx, ctx, sizeof (*ctx)); bcopy(aes_ctx->ac_keysched, ks, aes_ctx->ac_keysched_len); ctx->ac_keysched = ks; out.cd_length = ulEncryptedDataLen; out.cd_raw.iov_base = (char *)buf; out.cd_raw.iov_len = ulEncryptedDataLen; outbuf = buf; speculate = B_TRUE; } rc = aes_decrypt_contiguous_blocks(ctx, (char *)pEncryptedData, ulEncryptedDataLen, &out); if (rc != CRYPTO_SUCCESS) { out_orig->cd_offset = 0; rv = CKR_FUNCTION_FAILED; goto done; } /* * RFC5652 6.3 The amount of padding must be * block_sz - (len mod block_size). This means * the amount of padding must always be in the * range [1..block_size]. */ pad_len = outbuf[ulEncryptedDataLen - 1]; if (pad_len == 0 || pad_len > AES_BLOCK_LEN) { rv = CKR_ENCRYPTED_DATA_INVALID; goto done; } out.cd_offset -= pad_len; /* * Verify pad values, trying to do so in as close to constant * time as possible. */ for (i = ulEncryptedDataLen - pad_len; i < ulEncryptedDataLen; i++) { if (outbuf[i] != pad_len) { rv = CKR_ENCRYPTED_DATA_INVALID; } } if (rv != CKR_OK) { goto done; } if (speculate) { if (out.cd_offset <= out_orig->cd_length) { bcopy(out.cd_raw.iov_base, out_orig->cd_raw.iov_base, out.cd_offset); } else { rv = CKR_BUFFER_TOO_SMALL; } } /* * No matter what, we report the exact size required. */ out_orig->cd_offset = out.cd_offset; done: freezero(buf, ulEncryptedDataLen); if (ctx != aes_ctx) { VERIFY(speculate); soft_aes_free_ctx(ctx); } return (rv); } CK_RV soft_aes_decrypt(soft_session_t *session_p, CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) { aes_ctx_t *aes_ctx = session_p->decrypt.context; CK_MECHANISM_TYPE mech = session_p->decrypt.mech.mechanism; size_t length_needed; size_t remainder; int rc = CRYPTO_SUCCESS; CK_RV rv = CKR_OK; crypto_data_t out = { .cd_format = CRYPTO_DATA_RAW, .cd_offset = 0, .cd_length = *pulDataLen, .cd_raw.iov_base = (char *)pData, .cd_raw.iov_len = *pulDataLen }; /* * A bit unusual, but it's permissible for ccm and gcm modes to not * decrypt any data. This ends up being equivalent to CKM_AES_CMAC * or CKM_AES_GMAC of the additional authenticated data (AAD). */ if ((pEncryptedData == NULL || ulEncryptedDataLen == 0) && !(aes_ctx->ac_flags & (CCM_MODE|GCM_MODE))) { return (CKR_ARGUMENTS_BAD); } remainder = ulEncryptedDataLen % AES_BLOCK_LEN; /* * CTR, CCM, CMAC, and GCM modes do not require the ciphertext * to be a multiple of the AES block size. Note that while * CKM_AES_CBC_PAD accepts an arbitrary sized plaintext, the * ciphertext is always a multiple of the AES block size */ switch (mech) { case CKM_AES_CMAC: case CKM_AES_CMAC_GENERAL: case CKM_AES_CTR: case CKM_AES_CCM: case CKM_AES_GCM: case CKM_AES_GMAC: break; default: if (remainder != 0) { rv = CKR_DATA_LEN_RANGE; goto cleanup; } } if (mech == CKM_AES_CBC_PAD) { rv = soft_aes_cbc_pad_decrypt(aes_ctx, pEncryptedData, ulEncryptedDataLen, &out); if (pData == NULL || rv == CKR_BUFFER_TOO_SMALL) { *pulDataLen = out.cd_offset; return (rv); } goto cleanup; } switch (aes_ctx->ac_flags & (CCM_MODE|GCM_MODE)) { case CCM_MODE: length_needed = aes_ctx->ac_processed_data_len; break; case GCM_MODE: length_needed = ulEncryptedDataLen - aes_ctx->ac_tag_len; break; default: /* * Note: for CKM_AES_CBC_PAD, we cannot know exactly how much * space is needed for the plaintext until after we decrypt it. * However, it is permissible to return a value 'somewhat' * larger than necessary (PKCS#11 Base Specification, sec 5.2). * * Since CKM_AES_CBC_PAD adds at most AES_BLOCK_LEN bytes to * the plaintext, we report the ciphertext length as the * required plaintext length. This means we specify at most * AES_BLOCK_LEN additional bytes of memory for the plaintext. * * This behavior is slightly different from the earlier * version of this code which returned the value of * (ulEncryptedDataLen - AES_BLOCK_LEN), which was only ever * correct when the original plaintext was already a multiple * of AES_BLOCK_LEN (i.e. when AES_BLOCK_LEN of padding was * added). This should not be a concern for existing * consumers -- if they were previously using the value of * *pulDataLen to size the outbut buffer, the resulting * plaintext would be truncated anytime the original plaintext * wasn't a multiple of AES_BLOCK_LEN. No consumer should * be relying on such wrong behavior. More likely they are * using the size of the ciphertext or larger for the * buffer to hold the decrypted plaintext (which is always * acceptable). */ length_needed = ulEncryptedDataLen; } if (pData == NULL) { /* * The application can ask for the size of the output buffer * with a NULL output buffer (pData). * C_Decrypt() guarantees pulDataLen != NULL. */ *pulDataLen = length_needed; return (CKR_OK); } if (*pulDataLen < length_needed) { *pulDataLen = length_needed; return (CKR_BUFFER_TOO_SMALL); } if (ulEncryptedDataLen > 0) { rv = soft_aes_decrypt_update(session_p, pEncryptedData, ulEncryptedDataLen, pData, pulDataLen); } if (rv != CKR_OK) { rv = CKR_FUNCTION_FAILED; goto cleanup; } /* * Some modes (e.g. CCM and GCM) will output additional data * after the plaintext (such as the MAC). Update out to * reflect the amount of data in pData for the _final() functions. */ out.cd_offset = *pulDataLen; /* * As CKM_AES_CTR is a stream cipher, ctr_mode_final is always * invoked in the _update() functions, so we do not need to call it * here. */ if (aes_ctx->ac_flags & CCM_MODE) { ASSERT3U(aes_ctx->ac_processed_data_len, ==, aes_ctx->ac_data_len); ASSERT3U(aes_ctx->ac_processed_mac_len, ==, aes_ctx->ac_mac_len); rc = ccm_decrypt_final((ccm_ctx_t *)aes_ctx, &out, AES_BLOCK_LEN, aes_encrypt_block, aes_copy_block, aes_xor_block); rv = crypto2pkcs11_error_number(rc); } else if (aes_ctx->ac_flags & GCM_MODE) { rc = gcm_decrypt_final((gcm_ctx_t *)aes_ctx, &out, AES_BLOCK_LEN, aes_encrypt_block, aes_xor_block); rv = crypto2pkcs11_error_number(rc); } cleanup: if (rv == CKR_OK) { *pulDataLen = out.cd_offset; } else { *pulDataLen = 0; } (void) pthread_mutex_lock(&session_p->session_mutex); soft_aes_free_ctx(aes_ctx); session_p->decrypt.context = NULL; (void) pthread_mutex_unlock(&session_p->session_mutex); return (rv); } CK_RV soft_aes_encrypt_update(soft_session_t *session_p, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pEncryptedData, CK_ULONG_PTR pulEncryptedDataLen) { aes_ctx_t *aes_ctx = session_p->encrypt.context; crypto_data_t out = { .cd_format = CRYPTO_DATA_RAW, .cd_offset = 0, .cd_length = *pulEncryptedDataLen, .cd_raw.iov_base = (char *)pEncryptedData, .cd_raw.iov_len = *pulEncryptedDataLen }; CK_MECHANISM_TYPE mech = session_p->encrypt.mech.mechanism; CK_RV rv = CKR_OK; size_t out_len; int rc; /* * If pData is NULL, we should have zero bytes to process, and * the aes_encrypt_contiguous_blocks() call will be an effective no-op. */ IMPLY(pData == NULL, ulDataLen == 0); /* Check size of the output buffer */ switch (mech) { case CKM_AES_CMAC: /* * The underlying CMAC implementation handles the storing of * extra bytes and does not output any data until *_final, * so do not bother looking at the size of the output * buffer at this time. */ out_len = 0; break; case CKM_AES_GMAC: out_len = 0; break; case CKM_AES_CTR: /* * CTR mode is a stream cipher, so we always output exactly as * much ciphertext as input plaintext */ out_len = ulDataLen; break; default: out_len = aes_ctx->ac_remainder_len + ulDataLen; /* * The number of complete blocks we can encrypt right now. * The underlying implementation will buffer any remaining data * until the next *_update call. */ out_len &= ~(AES_BLOCK_LEN - 1); break; } if (pEncryptedData == NULL) { *pulEncryptedDataLen = out_len; return (CKR_OK); } if (*pulEncryptedDataLen < out_len) { *pulEncryptedDataLen = out_len; return (CKR_BUFFER_TOO_SMALL); } rc = aes_encrypt_contiguous_blocks(aes_ctx, (char *)pData, ulDataLen, &out); /* * Since out.cd_offset is set to 0 initially and the underlying * implementation increments out.cd_offset by the amount of output * written, so we can just use the value as the amount written. */ *pulEncryptedDataLen = out.cd_offset; if (rc != CRYPTO_SUCCESS) { return (CKR_FUNCTION_FAILED); } rv = crypto2pkcs11_error_number(rc); return (rv); } CK_RV soft_aes_decrypt_update(soft_session_t *session_p, CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) { aes_ctx_t *aes_ctx = session_p->decrypt.context; uint8_t *buffer_block = NULL; crypto_data_t out = { .cd_format = CRYPTO_DATA_RAW, .cd_offset = 0, .cd_length = *pulDataLen, .cd_raw.iov_base = (char *)pData, .cd_raw.iov_len = *pulDataLen }; CK_MECHANISM_TYPE mech = session_p->decrypt.mech.mechanism; CK_RV rv = CKR_OK; size_t in_len = ulEncryptedDataLen; size_t out_len; int rc = CRYPTO_SUCCESS; switch (mech) { case CKM_AES_CCM: case CKM_AES_GCM: out_len = 0; break; case CKM_AES_CBC_PAD: /* * For CKM_AES_CBC_PAD, we use the existing code for CBC * mode in libsoftcrypto (which itself uses the code in * usr/src/common/crypto/modes for CBC mode). For * non-padding AES CBC mode, aes_decrypt_contiguous_blocks() * will accumulate ciphertext in aes_ctx->ac_remainder until * there is at least AES_BLOCK_LEN bytes of ciphertext available * to decrypt. At that point, as many blocks of AES_BLOCK_LEN * sized ciphertext blocks are decrypted. Any remainder is * copied into aes_ctx->ac_remainder for decryption in * subsequent calls to aes_decrypt_contiguous_blocks(). * * When PKCS#7 padding is used, the buffering * aes_decrypt_contigous_blocks() performs is insufficient. * PKCS#7 padding always adds [1..AES_BLOCK_LEN] bytes of * padding to plaintext, so the resulting ciphertext is always * larger than the input plaintext. However we cannot know * which block is the final block (and needs its padding * stripped) until C_DecryptFinal() is called. Additionally, * it is permissible for a caller to use buffers sized to the * output plaintext -- i.e. smaller than the input ciphertext. * This leads to a more complicated buffering/accumulation * strategy than what aes_decrypt_contiguous_blocks() provides * us. * * Our buffering strategy works as follows: * For each call to C_DecryptUpdate, we calculate the * total amount of ciphertext available (buffered plus what's * passed in) as the initial output size (out_len). Based * on the value of out_len, there are three possibilties: * * 1. We have less than AES_BLOCK_LEN + 1 bytes of * ciphertext available. Accumulate the ciphertext in * aes_ctx->ac_remainder. Note that while we could let * aes_decrypt_contiguous_blocks() buffer the input for us * when we have less than AES_BLOCK_LEN bytes, we would still * need to buffer when we have exactly AES_BLOCK_LEN * bytes available, so we just handle both situations with * one if clause. * * 2. We have at least AES_BLOCK_LEN + 1 bytes of * ciphertext, and the total amount available is also an * exact multiple of AES_BLOCK_LEN. We cannot know if the * last block of input is the final block (yet), but we * are an exact multiple of AES_BLOCK_LEN, and we have * at least AES_BLOCK_LEN + 1 bytes available, therefore * there must be at least 2 * AES_BLOCK_LEN bytes of input * ciphertext available. It also means there's at least one * full block of input ciphertext that can be decrypted. We * reduce the size of the input (in_len) given to * aes_decrypt_contiguous_bytes() by AES_BLOCK_LEN to prevent * it from decrypting the last full block of data. * aes_decrypt_contiguous_blocks() will when decrypt any * buffered data in aex_ctx->ac_remainder, and then any * input data passed. Since we have an exact multiple of * AES_BLOCK_LEN, aes_ctx->ac_remainder will be empty * (aes_ctx->ac_remainder_len == 0), once * aes_decrypt_contiguout_block() completes, and we can * copy the last block of data into aes_ctx->ac_remainder. * * 3. We have at least AES_BLOCK_LEN + 1 bytes of * ciphertext, but the total amount available is not an * exact multiple of AES_BLOCK_LEN. We decrypt all of * full blocks of data we have. The remainder will be * less than AES_BLOCK_LEN bytes. We let * aes_decrypt_contiguous_blocks() buffer the remainder * for us since it would normally do this anyway. Since there * is a remainder, the full blocks that are present cannot * be the last block, so we can safey decrypt all of them. * * Some things to note: * - The above semantics will cause aes_ctx->ac_remainder to * never accumulate more than AES_BLOCK_LEN bytes of * ciphertext. Once we reach at least AES_BLOCK_LEN + 1 bytes, * we will decrypt the contents of aes_ctx->ac_remainder by one * of the last two scenarios described above. * * - We must always end up with AES_BLOCK_LEN bytes of data * in aes_ctx->ac_remainder when C_DecryptFinal() is called. * The first and third scenarios above may leave * aes_ctx->ac_remainder with less than AES_BLOCK_LEN bytes, * however the total size of the input ciphertext that's * been decrypted must end up a multiple of AES_BLOCK_LEN. * Therefore, we can always assume when there is a * remainder that more data is coming. If we do end up * with a remainder that's not AES_BLOCK_LEN bytes long * when C_DecryptFinal() is called, the input is assumed * invalid and we return CKR_DATA_LEN_RANGE (see * soft_aes_decrypt_final()). */ VERIFY3U(aes_ctx->ac_remainder_len, <=, AES_BLOCK_LEN); if (in_len >= SIZE_MAX - AES_BLOCK_LEN) return (CKR_ENCRYPTED_DATA_LEN_RANGE); out_len = aes_ctx->ac_remainder_len + in_len; if (out_len <= AES_BLOCK_LEN) { /* * The first scenario detailed above, accumulate * ciphertext in ac_remainder_len and return. */ uint8_t *dest = (uint8_t *)aes_ctx->ac_remainder + aes_ctx->ac_remainder_len; bcopy(pEncryptedData, dest, in_len); aes_ctx->ac_remainder_len += in_len; *pulDataLen = 0; /* * Since we aren't writing an output, and are returning * here, we don't need to adjust out_len -- we never * reach the output buffer size checks after the * switch statement. */ return (CKR_OK); } else if (out_len % AES_BLOCK_LEN == 0) { /* * The second scenario decribed above. The total amount * available is a multiple of AES_BLOCK_LEN, and * we have more than one block. We reduce the * input size (in_len) by AES_BLOCK_LEN. We also * reduce the output size (out_len) by AES_BLOCK_LEN * for the output buffer size checks that follow * the switch statement. In certain situations, * PKCS#11 requires this to be an exact value, so * the size check cannot occur for CKM_AES_CBC_PAD * until after we've determine which scenario we * have. * * Because we never accumulate more than AES_BLOCK_LEN * bytes in aes_ctx->ac_remainder, when we are in * this scenario, the following VERIFYs should always * be true (and serve as a final safeguard against * underflow). */ VERIFY3U(in_len, >=, AES_BLOCK_LEN); buffer_block = pEncryptedData + in_len - AES_BLOCK_LEN; in_len -= AES_BLOCK_LEN; /* * This else clause explicity checks * out_len > AES_BLOCK_LEN, so this is also safe. */ out_len -= AES_BLOCK_LEN; } else { /* * The third scenario above. We have at least * AES_BLOCK_LEN + 1 bytes, but the total amount of * input ciphertext available is not an exact * multiple of AES_BLOCK_LEN. Let * aes_decrypt_contiguous_blocks() handle the * buffering of the remainder. Update the * output size to reflect the actual amount of output * we want to emit for the checks after the switch * statement. */ out_len &= ~(AES_BLOCK_LEN - 1); } break; case CKM_AES_CTR: /* * CKM_AES_CTR is a stream cipher, so we always output * exactly as much output plaintext as input ciphertext */ out_len = in_len; break; default: out_len = aes_ctx->ac_remainder_len + in_len; out_len &= ~(AES_BLOCK_LEN - 1); break; } /* * C_DecryptUpdate() verifies that pulDataLen is not NULL prior * to calling soft_decrypt_common() (which calls us). */ if (pData == NULL) { /* * If the output buffer (pData) is NULL, that means the * caller is inquiring about the size buffer needed to * complete the C_DecryptUpdate() request. While we are * permitted to set *pulDataLen to an estimated value that can * be 'slightly' larger than the actual value required, * since we know the exact size we need, we stick with the * exact size. */ *pulDataLen = out_len; return (CKR_OK); } if (*pulDataLen < out_len) { /* * Not an inquiry, but the output buffer isn't large enough. * PKCS#11 requires that this scenario not fail fatally (as * well as return a different error value). This situation * also requires us to set *pulDataLen to the _exact_ size * required. */ *pulDataLen = out_len; return (CKR_BUFFER_TOO_SMALL); } rc = aes_decrypt_contiguous_blocks(aes_ctx, (char *)pEncryptedData, in_len, &out); if (rc != CRYPTO_SUCCESS) { rv = CKR_FUNCTION_FAILED; goto done; } *pulDataLen = out.cd_offset; switch (mech) { case CKM_AES_CBC_PAD: if (buffer_block == NULL) { break; } VERIFY0(aes_ctx->ac_remainder_len); /* * We had multiple blocks of data to decrypt with nothing * left over and deferred decrypting the last block of data. * Copy it into aes_ctx->ac_remainder to decrypt on the * next update call (or final). */ bcopy(buffer_block, aes_ctx->ac_remainder, AES_BLOCK_LEN); aes_ctx->ac_remainder_len = AES_BLOCK_LEN; break; } done: return (rv); } CK_RV soft_aes_encrypt_final(soft_session_t *session_p, CK_BYTE_PTR pLastEncryptedPart, CK_ULONG_PTR pulLastEncryptedPartLen) { aes_ctx_t *aes_ctx = session_p->encrypt.context; crypto_data_t data = { .cd_format = CRYPTO_DATA_RAW, .cd_offset = 0, .cd_length = *pulLastEncryptedPartLen, .cd_raw.iov_base = (char *)pLastEncryptedPart, .cd_raw.iov_len = *pulLastEncryptedPartLen }; CK_MECHANISM_TYPE mech = session_p->encrypt.mech.mechanism; CK_RV rv = CKR_OK; size_t out_len; int rc = CRYPTO_SUCCESS; switch (mech) { case CKM_AES_CBC_PAD: /* * We always add 1..AES_BLOCK_LEN of padding to the input * plaintext to round up to a multiple of AES_BLOCK_LEN. * During encryption, we never output a partially encrypted * block (that is the amount encrypted by each call of * C_EncryptUpdate() is always either 0 or n * AES_BLOCK_LEN). * As a result, at the end of the encryption operation, we * output AES_BLOCK_LEN bytes of data -- this could be a full * block of padding, or a combination of data + padding. */ out_len = AES_BLOCK_LEN; break; case CKM_AES_CTR: /* * Since CKM_AES_CTR is a stream cipher, we never buffer any * input, so we always have 0 remaining bytes of output. */ out_len = 0; break; case CKM_AES_CCM: out_len = aes_ctx->ac_remainder_len + aes_ctx->acu.acu_ccm.ccm_mac_len; break; case CKM_AES_GCM: out_len = aes_ctx->ac_remainder_len + aes_ctx->acu.acu_gcm.gcm_tag_len; break; case CKM_AES_CMAC: case CKM_AES_CMAC_GENERAL: out_len = AES_BLOCK_LEN; break; case CKM_AES_GMAC: out_len = AES_BLOCK_LEN; break; default: /* * Everything other AES mechansism requires full blocks of * input. If the input was not an exact multiple of * AES_BLOCK_LEN, it is a fatal error. */ if (aes_ctx->ac_remainder_len > 0) { rv = CKR_DATA_LEN_RANGE; goto done; } out_len = 0; } if (*pulLastEncryptedPartLen < out_len || pLastEncryptedPart == NULL) { *pulLastEncryptedPartLen = out_len; return ((pLastEncryptedPart == NULL) ? CKR_OK : CKR_BUFFER_TOO_SMALL); } switch (mech) { case CKM_AES_CBC_PAD: { char block[AES_BLOCK_LEN] = { 0 }; size_t padlen = AES_BLOCK_LEN - aes_ctx->ac_remainder_len; if (padlen == 0) { padlen = AES_BLOCK_LEN; } (void) memset(block, padlen & 0xff, sizeof (block)); rc = aes_encrypt_contiguous_blocks(aes_ctx, block, padlen, &data); explicit_bzero(block, sizeof (block)); break; } case CKM_AES_CTR: /* * Since CKM_AES_CTR is a stream cipher, we never * buffer any data, and thus have no remaining data * to output at the end */ break; case CKM_AES_CCM: rc = ccm_encrypt_final((ccm_ctx_t *)aes_ctx, &data, AES_BLOCK_LEN, aes_encrypt_block, aes_xor_block); break; case CKM_AES_GCM: rc = gcm_encrypt_final((gcm_ctx_t *)aes_ctx, &data, AES_BLOCK_LEN, aes_encrypt_block, aes_copy_block, aes_xor_block); break; case CKM_AES_CMAC: case CKM_AES_CMAC_GENERAL: rc = cmac_mode_final((cbc_ctx_t *)aes_ctx, &data, aes_encrypt_block, aes_xor_block); break; case CKM_AES_GMAC: rc = gmac_mode_final((gcm_ctx_t *)aes_ctx, &data, AES_BLOCK_LEN, aes_encrypt_block, aes_xor_block); break; default: break; } rv = crypto2pkcs11_error_number(rc); done: if (rv == CKR_OK) { *pulLastEncryptedPartLen = data.cd_offset; } soft_aes_free_ctx(aes_ctx); session_p->encrypt.context = NULL; return (rv); } CK_RV soft_aes_decrypt_final(soft_session_t *session_p, CK_BYTE_PTR pLastPart, CK_ULONG_PTR pulLastPartLen) { aes_ctx_t *aes_ctx = session_p->decrypt.context; CK_MECHANISM_TYPE mech = session_p->decrypt.mech.mechanism; CK_RV rv = CKR_OK; int rc = CRYPTO_SUCCESS; size_t out_len; crypto_data_t out = { .cd_format = CRYPTO_DATA_RAW, .cd_offset = 0, .cd_length = *pulLastPartLen, .cd_raw.iov_base = (char *)pLastPart, .cd_raw.iov_len = *pulLastPartLen }; switch (mech) { case CKM_AES_CBC_PAD: /* * PKCS#11 requires that a caller can discover the size of * the output buffer required by calling * C_DecryptFinal(hSession, NULL, &len) which sets * *pulLastPartLen to the size required. However, it also * allows if one calls C_DecryptFinal with a buffer (i.e. * pLastPart != NULL) that is too small, to return * CKR_BUFFER_TOO_SMALL with *pulLastPartLen set to the * _exact_ size required (when pLastPart is NULL, the * implementation is allowed to set a 'sightly' larger * value than is strictly necessary. In either case, the * caller is allowed to retry the operation (the operation * is not terminated). * * With PKCS#7 padding, we cannot determine the exact size of * the output until we decrypt the final block. As such, the * first time for a given decrypt operation we are called, * we decrypt the final block and stash it in the aes_ctx * remainder block. On any subsequent calls in the * current decrypt operation, we then can use the decrypted * block as necessary to provide the correct semantics. * * The cleanup of aes_ctx when the operation terminates * will take care of clearing out aes_ctx->ac_remainder_len. */ if ((aes_ctx->ac_flags & P11_DECRYPTED) == 0) { uint8_t block[AES_BLOCK_LEN] = { 0 }; crypto_data_t block_out = { .cd_format = CRYPTO_DATA_RAW, .cd_offset = 0, .cd_length = sizeof (block), .cd_raw.iov_base = (char *)block, .cd_raw.iov_len = sizeof (block) }; size_t amt, i; uint8_t pad_len; if (aes_ctx->ac_remainder_len != AES_BLOCK_LEN) { return (CKR_DATA_LEN_RANGE); } rc = aes_decrypt_contiguous_blocks(aes_ctx, (char *)block, 0, &block_out); if (rc != CRYPTO_SUCCESS) { explicit_bzero(block, sizeof (block)); return (CKR_FUNCTION_FAILED); } pad_len = block[AES_BLOCK_LEN - 1]; /* * RFC5652 6.3 The amount of padding must be * block_sz - (len mod block_size). This means * the amount of padding must always be in the * range [1..block_size]. */ if (pad_len == 0 || pad_len > AES_BLOCK_LEN) { rv = CKR_ENCRYPTED_DATA_INVALID; explicit_bzero(block, sizeof (block)); goto done; } amt = AES_BLOCK_LEN - pad_len; /* * Verify the padding is correct. Try to do so * in as constant a time as possible. */ for (i = amt; i < AES_BLOCK_LEN; i++) { if (block[i] != pad_len) { rv = CKR_ENCRYPTED_DATA_INVALID; } } if (rv != CKR_OK) { explicit_bzero(block, sizeof (block)); goto done; } bcopy(block, aes_ctx->ac_remainder, amt); explicit_bzero(block, sizeof (block)); aes_ctx->ac_flags |= P11_DECRYPTED; aes_ctx->ac_remainder_len = amt; } out_len = aes_ctx->ac_remainder_len; break; case CKM_AES_CTR: /* * Since CKM_AES_CTR is a stream cipher, we never have * any remaining bytes to output. */ out_len = 0; break; case CKM_AES_CCM: out_len = aes_ctx->ac_data_len; break; case CKM_AES_GCM: out_len = aes_ctx->acu.acu_gcm.gcm_processed_data_len - aes_ctx->acu.acu_gcm.gcm_tag_len; break; default: /* * The remaining mechanims require an exact multiple of * AES_BLOCK_LEN of ciphertext. Any other value is an error. */ if (aes_ctx->ac_remainder_len > 0) { rv = CKR_DATA_LEN_RANGE; goto done; } out_len = 0; break; } if (*pulLastPartLen < out_len || pLastPart == NULL) { *pulLastPartLen = out_len; return ((pLastPart == NULL) ? CKR_OK : CKR_BUFFER_TOO_SMALL); } switch (mech) { case CKM_AES_CBC_PAD: *pulLastPartLen = out_len; if (out_len == 0) { break; } bcopy(aes_ctx->ac_remainder, pLastPart, out_len); out.cd_offset += out_len; break; case CKM_AES_CCM: ASSERT3U(aes_ctx->ac_processed_data_len, ==, out_len); ASSERT3U(aes_ctx->ac_processed_mac_len, ==, aes_ctx->ac_mac_len); rc = ccm_decrypt_final((ccm_ctx_t *)aes_ctx, &out, AES_BLOCK_LEN, aes_encrypt_block, aes_copy_block, aes_xor_block); break; case CKM_AES_GCM: rc = gcm_decrypt_final((gcm_ctx_t *)aes_ctx, &out, AES_BLOCK_LEN, aes_encrypt_block, aes_xor_block); break; default: break; } VERIFY3U(out.cd_offset, ==, out_len); rv = crypto2pkcs11_error_number(rc); done: if (rv == CKR_OK) { *pulLastPartLen = out.cd_offset; } soft_aes_free_ctx(aes_ctx); session_p->decrypt.context = NULL; return (rv); } /* * Allocate and initialize AES contexts for sign and verify operations * (including the underlying encryption context needed to sign or verify) -- * called by C_SignInit() and C_VerifyInit() to perform the CKM_AES_* MAC * mechanisms. For general-length AES MAC, also validate the MAC length. */ CK_RV soft_aes_sign_verify_init_common(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism, soft_object_t *key_p, boolean_t sign_op) { soft_aes_sign_ctx_t *ctx = NULL; size_t mac_len = AES_BLOCK_LEN; /* For AES CMAC the IV is always 0 */ CK_BYTE iv_zero[AES_BLOCK_LEN] = { 0 }; CK_MECHANISM encrypt_mech = { 0 }; CK_RV rv; switch (pMechanism->mechanism) { case CKM_AES_CMAC_GENERAL: if (pMechanism->pParameter == NULL) { return (CKR_MECHANISM_PARAM_INVALID); } mac_len = *(CK_MAC_GENERAL_PARAMS *)pMechanism->pParameter; if (mac_len > AES_BLOCK_LEN) { return (CKR_MECHANISM_PARAM_INVALID); } encrypt_mech.mechanism = CKM_AES_CMAC; encrypt_mech.pParameter = iv_zero; encrypt_mech.ulParameterLen = AES_BLOCK_LEN; break; case CKM_AES_CMAC: encrypt_mech.mechanism = CKM_AES_CMAC; encrypt_mech.pParameter = iv_zero; encrypt_mech.ulParameterLen = AES_BLOCK_LEN; break; case CKM_AES_GMAC: /* See soft_aes_init_ctx */ if (pMechanism->pParameter == NULL || pMechanism->ulParameterLen != AES_GMAC_IV_LEN) return (CKR_MECHANISM_PARAM_INVALID); encrypt_mech.mechanism = CKM_AES_GMAC; encrypt_mech.pParameter = pMechanism->pParameter; encrypt_mech.ulParameterLen = pMechanism->ulParameterLen; break; default: return (CKR_MECHANISM_INVALID); } if (key_p->key_type != CKK_AES) return (CKR_KEY_TYPE_INCONSISTENT); ctx = calloc(1, sizeof (*ctx)); if (ctx == NULL) { return (CKR_HOST_MEMORY); } rv = soft_aes_check_mech_param(pMechanism, &ctx->aes_ctx); if (rv != CKR_OK) { soft_aes_free_ctx(ctx->aes_ctx); goto done; } /* See soft_aes_crypt_init_common */ if ((rv = soft_encrypt_init_internal(session_p, &encrypt_mech, key_p)) != CKR_OK) { soft_aes_free_ctx(ctx->aes_ctx); goto done; } ctx->mac_len = mac_len; (void) pthread_mutex_lock(&session_p->session_mutex); if (sign_op) { session_p->sign.context = ctx; session_p->sign.mech.mechanism = pMechanism->mechanism; } else { session_p->verify.context = ctx; session_p->verify.mech.mechanism = pMechanism->mechanism; } (void) pthread_mutex_unlock(&session_p->session_mutex); done: if (rv != CKR_OK) { soft_aes_free_ctx(ctx->aes_ctx); free(ctx); } return (rv); } CK_RV soft_aes_sign_verify_common(soft_session_t *session_p, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSigned, CK_ULONG_PTR pulSignedLen, boolean_t sign_op, boolean_t Final) { soft_aes_sign_ctx_t *soft_aes_ctx_sign_verify; CK_RV rv; CK_BYTE *pEncrypted = NULL; CK_ULONG ulEncryptedLen = AES_BLOCK_LEN; CK_BYTE last_block[AES_BLOCK_LEN]; if (sign_op) { soft_aes_ctx_sign_verify = (soft_aes_sign_ctx_t *)session_p->sign.context; if (soft_aes_ctx_sign_verify->mac_len == 0) { *pulSignedLen = 0; goto clean_exit; } /* Application asks for the length of the output buffer. */ if (pSigned == NULL) { *pulSignedLen = soft_aes_ctx_sign_verify->mac_len; return (CKR_OK); } /* Is the application-supplied buffer large enough? */ if (*pulSignedLen < soft_aes_ctx_sign_verify->mac_len) { *pulSignedLen = soft_aes_ctx_sign_verify->mac_len; return (CKR_BUFFER_TOO_SMALL); } } else { soft_aes_ctx_sign_verify = (soft_aes_sign_ctx_t *)session_p->verify.context; } if (Final) { rv = soft_encrypt_final(session_p, last_block, &ulEncryptedLen); } else { rv = soft_encrypt(session_p, pData, ulDataLen, last_block, &ulEncryptedLen); } if (rv == CKR_OK) { *pulSignedLen = soft_aes_ctx_sign_verify->mac_len; /* the leftmost mac_len bytes of last_block is our MAC */ (void) memcpy(pSigned, last_block, *pulSignedLen); } clean_exit: (void) pthread_mutex_lock(&session_p->session_mutex); /* soft_encrypt_common() has freed the encrypt context */ if (sign_op) { free(session_p->sign.context); session_p->sign.context = NULL; } else { free(session_p->verify.context); session_p->verify.context = NULL; } session_p->encrypt.flags = 0; (void) pthread_mutex_unlock(&session_p->session_mutex); if (pEncrypted) { free(pEncrypted); } return (rv); } /* * Called by soft_sign_update() */ CK_RV soft_aes_mac_sign_verify_update(soft_session_t *session_p, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) { CK_BYTE buf[AES_BLOCK_LEN]; CK_ULONG ulEncryptedLen = AES_BLOCK_LEN; CK_RV rv; rv = soft_encrypt_update(session_p, pPart, ulPartLen, buf, &ulEncryptedLen); explicit_bzero(buf, sizeof (buf)); return (rv); } /* * Compare with crypto_free_mode_ctx() * in $SRC/common/crypto/modes/modes.c */ void soft_aes_free_ctx(aes_ctx_t *ctx) { size_t len = 0; if (ctx == NULL) return; if (ctx->ac_flags & ECB_MODE) { len = sizeof (ecb_ctx_t); } else if (ctx->ac_flags & (CBC_MODE|CMAC_MODE)) { len = sizeof (cbc_ctx_t); } else if (ctx->ac_flags & CTR_MODE) { len = sizeof (ctr_ctx_t); } else if (ctx->ac_flags & CCM_MODE) { ccm_ctx_t *ccm_ctx = &ctx->acu.acu_ccm; if (ccm_ctx->ccm_pt_buf != NULL) { freezero(ccm_ctx->ccm_pt_buf, ccm_ctx->ccm_data_len); ccm_ctx->ccm_pt_buf = NULL; } len = sizeof (ccm_ctx_t); } else if (ctx->ac_flags & GMAC_MODE) { len = sizeof (gcm_ctx_t); } else if (ctx->ac_flags & GCM_MODE) { gcm_ctx_t *gcm_ctx = &ctx->acu.acu_gcm; if (gcm_ctx->gcm_pt_buf != NULL) { freezero(gcm_ctx->gcm_pt_buf, gcm_ctx->gcm_pt_buf_len); gcm_ctx->gcm_pt_buf = NULL; } len = sizeof (gcm_ctx_t); } freezero(ctx->ac_keysched, ctx->ac_keysched_len); freezero(ctx, len); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include "softSession.h" #include "softObject.h" #include "softCrypt.h" /* * Allocate the ARCFour key stream for the active encryption or decryption * operation. */ CK_RV soft_arcfour_crypt_init(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism, soft_object_t *key_p, boolean_t encrypt) { uint8_t *keyval; int keyvallen; ARCFour_key *keystream; crypto_active_op_t *active_op; #ifdef __sparcv9 /* LINTED */ keyvallen = (int)OBJ_SEC_VALUE_LEN(key_p); #else /* !__sparcv9 */ keyvallen = OBJ_SEC_VALUE_LEN(key_p); #endif /* __sparcv9 */ if ((keyvallen < ARCFOUR_MIN_KEY_BYTES) || (keyvallen > ARCFOUR_MAX_KEY_BYTES)) return (CKR_KEY_SIZE_RANGE); keyval = OBJ_SEC_VALUE(key_p); if (keyval == NULL) return (CKR_KEY_TYPE_INCONSISTENT); keystream = malloc(sizeof (ARCFour_key)); if (keystream == NULL) { return (CKR_HOST_MEMORY); } arcfour_key_init(keystream, keyval, keyvallen); (void) pthread_mutex_lock(&session_p->session_mutex); active_op = (encrypt) ? &(session_p->encrypt) : &(session_p->decrypt); active_op->context = keystream; active_op->mech.mechanism = pMechanism->mechanism; (void) pthread_mutex_unlock(&session_p->session_mutex); return (CKR_OK); } /* * soft_arcfour_crypt() * * Arguments: * active_op: pointer to the active operation in the session * input: pointer to the input data to be transformed * inputlen: length of the input. * output: pointer to the output storage. * outputlenp: pointer to the length of the output * * Description: * Encrypts/Decrypts the 'input' and gets the result in the 'output' * * Returns: * CKR_OK: success * CKR_BUFFER_TOO_SMALL: the output buffer provided by application * is too small * CKR_ARGUMENTS_BAD: keystream is a NULL pointer, cipher is not * initialized */ CK_RV soft_arcfour_crypt(crypto_active_op_t *active_op, CK_BYTE_PTR input, CK_ULONG inputlen, CK_BYTE_PTR output, CK_ULONG_PTR outputlenp) { ARCFour_key *keystream = active_op->context; if (keystream == NULL) { return (CKR_ARGUMENTS_BAD); } /* * If application asks for the length of the output buffer * to hold the transformed text */ if (output == NULL) { *outputlenp = inputlen; return (CKR_OK); } /* Is the application-supplied buffer large enough? */ if (*outputlenp < inputlen) { *outputlenp = inputlen; return (CKR_BUFFER_TOO_SMALL); } arcfour_crypt(keystream, input, output, inputlen); *outputlenp = inputlen; return (CKR_OK); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2012 Milan Jurik. All rights reserved. * Copyright (c) 2018, Joyent. Inc. */ #include #include #include #include #include #include "softDSA.h" #include "softDH.h" #include "softRSA.h" #include "softObject.h" #include "softASN1.h" #define OID_TAG 0x06 #define MAX_DH_KEY MAX_DH_KEYLENGTH_IN_BYTES /* bytes in DH key */ static uchar_t DH_OID[] = { /* DH key agreement OID: 1 . 2 . 840 . 113549 . 1 . 3 . 1 */ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x03, 0x01 }; #define MAX_DH942_KEY MAX_DH_KEYLENGTH_IN_BYTES /* bytes in DH X9.42 key */ static uchar_t DH942_OID[] = { /* DH X9.42 OID: 1 . 2 . 840 . 10046 . 1 */ 0x2A, 0x86, 0x48, 0xCE, 0x3E, 0x01 }; #define MAX_DSA_KEY MAX_DSA_KEY_LEN /* bytes in DSA key */ static uchar_t DSA_OID[] = { /* DSA algorithm OID: 1 . 2 . 840 . 10040 . 4 . 1 */ 0x2A, 0x86, 0x48, 0xCE, 0x38, 0x04, 0x01 }; #define MAX_RSA_KEY MAX_RSA_KEYLENGTH_IN_BYTES /* bytes in RSA key */ static uchar_t RSA_OID[] = { /* RSA algorithm OID: 1 . 2 . 840 . 113549 . 1 . 1 . 1 */ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 }; /* * If the first bit of big integer is non-zero (i.e, first byte is * 0x80 or greater), it may be interpreted as an ASN.1 negative number. * Add one leading byte of zero-padding only in these cases to ensure * it is treated as an unsigned integer. */ static CK_RV pad_bigint_attr(biginteger_t *src, biginteger_t *dst) { int padding; /* Src and dst must already by previously allocated. */ if (src == NULL || dst == NULL) return (CKR_HOST_MEMORY); if (src->big_value_len == 0) { dst->big_value = NULL; dst->big_value_len = 0; return (CKR_OK); } /* * Realloc() may free() or shrink previous memory location, so * clear out potentially sensitive data before that happens. */ if (dst->big_value != NULL) explicit_bzero(dst->big_value, dst->big_value_len); padding = (src->big_value[0] < 0x80) ? 0 : 1; dst->big_value_len = src->big_value_len + padding; dst->big_value = realloc(dst->big_value, dst->big_value_len); if (dst->big_value == NULL) return (CKR_HOST_MEMORY); /* Set zero-pad at first byte, then append actual big_value. */ dst->big_value[0] = 0x0; (void) memcpy(&(dst->big_value[padding]), src->big_value, src->big_value_len); return (CKR_OK); } /* * Sometimes there is one bytes of zero-padding, if a big integer may * be interpreted as an ASN.1 negative number (i.e, the first bit is * non-zero, the first byte is 0x80 or greater). Remove first byte * of zero-padding in those cases from the decoded octet strings. */ static CK_RV unpad_bigint_attr(biginteger_t src, biginteger_t *dst) { int offset; if (dst == NULL) return (CKR_HOST_MEMORY); if (src.big_value_len == 0) { dst->big_value = NULL; dst->big_value_len = 0; return (CKR_OK); } offset = (src.big_value[0] == 0x00) ? 1 : 0; dst->big_value_len = src.big_value_len - offset; /* * Must allocate memory here because subsequent calls to * copy_bigint_attr() just redirect pointer; it doesn't * really copy the bigint like the function name implies. */ dst->big_value = malloc(dst->big_value_len); if (dst->big_value == NULL) return (CKR_HOST_MEMORY); (void) memcpy(dst->big_value, &(src.big_value[offset]), dst->big_value_len); return (CKR_OK); } /* Encode RSA private key in ASN.1 BER syntax. */ static CK_RV rsa_pri_to_asn1(soft_object_t *objp, uchar_t *buf, ulong_t *buf_len) { CK_RV rv = CKR_OK; BerElement *key_asn = NULLBER, *p8obj_asn = NULLBER; BerValue *key_octs = NULL, *p8obj_octs = NULL; int version = SOFT_ASN_VERSION; biginteger_t tmp_pad = { NULL, 0 }; /* * The ASN.1 syntax for an RSA private key is: * * PKCS#8 \* PrivateKeyInfo *\ * --------------------------------- * Sequence { * version INTEGER; * Sequence { \* PrivateKeyAlgorithm *\ * OID 0x06, \* RSA algorithm OID *\ * param(NULL) * } * RSAPrivateKey OCTETSTRING = * PKCS#1 \* RSAPrivateKey *\ * --------------------------- * Sequence { * version INTEGER, * modulus INTEGER, * publicExponent INTEGER, * privateExponent INTEGER, * prime1 INTEGER, * prime2 INTEGER, * exponent1 INTEGER, * exponent2 INTEGER, * coefficient INTEGER * } * } * * The code below starts building the innermost octets * RSAPrivateKey, and then builds the PrivateKeyInfo * sequence around that octet string. The BER syntax * used in this function is (others may be possible): * { i { to n } { i to to to to to to to to } } * where "i" is for integers with fixed size * where "to" is for integers that vary in size (length + value) * where "n" is for nulls * where "{}" delimit sequences */ /* RSAPrivateKey ... */ if ((key_asn = ber_alloc()) == NULLBER) return (CKR_HOST_MEMORY); /* ... begin-sequence { version, */ if (ber_printf(key_asn, "{i", version) == -1) { rv = CKR_GENERAL_ERROR; goto cleanup_rsapri2asn; } /* ... modulus, */ if ((rv = pad_bigint_attr(OBJ_PRI_RSA_MOD(objp), &tmp_pad)) != CKR_OK) goto cleanup_rsapri2asn; if (ber_printf(key_asn, "to", LBER_INTEGER, tmp_pad.big_value, tmp_pad.big_value_len) == -1) { rv = CKR_GENERAL_ERROR; goto cleanup_rsapri2asn; } /* ... public exponent, */ if ((rv = pad_bigint_attr(OBJ_PRI_RSA_PUBEXPO(objp), &tmp_pad)) != CKR_OK) goto cleanup_rsapri2asn; else if (ber_printf(key_asn, "to", LBER_INTEGER, tmp_pad.big_value, tmp_pad.big_value_len) == -1) { rv = CKR_GENERAL_ERROR; goto cleanup_rsapri2asn; } /* ... private exponent, */ if ((rv = pad_bigint_attr(OBJ_PRI_RSA_PRIEXPO(objp), &tmp_pad)) != CKR_OK) goto cleanup_rsapri2asn; if (ber_printf(key_asn, "to", LBER_INTEGER, tmp_pad.big_value, tmp_pad.big_value_len) == -1) { rv = CKR_GENERAL_ERROR; goto cleanup_rsapri2asn; } /* ... prime 1, */ if ((rv = pad_bigint_attr(OBJ_PRI_RSA_PRIME1(objp), &tmp_pad)) != CKR_OK) goto cleanup_rsapri2asn; else if (ber_printf(key_asn, "to", LBER_INTEGER, tmp_pad.big_value, tmp_pad.big_value_len) == -1) { rv = CKR_GENERAL_ERROR; goto cleanup_rsapri2asn; } /* ... prime 2, */ if ((rv = pad_bigint_attr(OBJ_PRI_RSA_PRIME2(objp), &tmp_pad)) != CKR_OK) goto cleanup_rsapri2asn; else if (ber_printf(key_asn, "to", LBER_INTEGER, tmp_pad.big_value, tmp_pad.big_value_len) == -1) { rv = CKR_GENERAL_ERROR; goto cleanup_rsapri2asn; } /* ... exponent 1, */ if ((rv = pad_bigint_attr(OBJ_PRI_RSA_EXPO1(objp), &tmp_pad)) != CKR_OK) goto cleanup_rsapri2asn; else if (ber_printf(key_asn, "to", LBER_INTEGER, tmp_pad.big_value, tmp_pad.big_value_len) == -1) { rv = CKR_GENERAL_ERROR; goto cleanup_rsapri2asn; } /* ... exponent 2, */ if ((rv = pad_bigint_attr(OBJ_PRI_RSA_EXPO2(objp), &tmp_pad)) != CKR_OK) goto cleanup_rsapri2asn; else if (ber_printf(key_asn, "to", LBER_INTEGER, tmp_pad.big_value, tmp_pad.big_value_len) == -1) { rv = CKR_GENERAL_ERROR; goto cleanup_rsapri2asn; } /* ... coefficient } end-sequence */ if ((rv = pad_bigint_attr(OBJ_PRI_RSA_COEF(objp), &tmp_pad)) != CKR_OK) goto cleanup_rsapri2asn; else if (ber_printf(key_asn, "to}", LBER_INTEGER, tmp_pad.big_value, tmp_pad.big_value_len) == -1) { rv = CKR_GENERAL_ERROR; goto cleanup_rsapri2asn; } /* Convert key ASN.1 to octet string. */ if (ber_flatten(key_asn, &key_octs) == -1) { rv = CKR_GENERAL_ERROR; goto cleanup_rsapri2asn; } /* PKCS#8 PrivateKeyInfo ... */ if ((p8obj_asn = ber_alloc()) == NULLBER) { rv = CKR_HOST_MEMORY; goto cleanup_rsapri2asn; } /* * Embed key octet string into PKCS#8 object ASN.1: * begin-sequence { * version * begin-sequence { * OID, * NULL * } end-sequence * RSAPrivateKey * } end-sequence */ if (ber_printf(p8obj_asn, "{i{ton}o}", version, OID_TAG, RSA_OID, sizeof (RSA_OID), /* NULL parameter, */ key_octs->bv_val, key_octs->bv_len) == -1) { rv = CKR_GENERAL_ERROR; goto cleanup_rsapri2asn; } /* Convert PKCS#8 object ASN.1 to octet string. */ if (ber_flatten(p8obj_asn, &p8obj_octs) == -1) { rv = CKR_GENERAL_ERROR; goto cleanup_rsapri2asn; } /* Ship out the PKCS#8 object ASN.1 octet string, if possible. */ /* * If the user passes in a null buf, then buf_len is set. * If the user passes in a value with buf_len, then it can * be checked to see if the accompanying buf is big enough. * If it is, the octet string is copied into a pre-malloc'd * buf; otherwise the user must resize buf and call again. * In either case, buf_len is reset to the corrected size. * See PKCS#11 section 11.2. */ #ifdef _LP64 /* LINTED E_CAST_INT_TO_SMALL_INT */ if ((buf == NULL) || ((ber_len_t)(*buf_len) < p8obj_octs->bv_len)) { #else if ((buf == NULL) || ((ber_len_t)(*buf_len) < p8obj_octs->bv_len)) { #endif *buf_len = p8obj_octs->bv_len; rv = (buf == NULL) ? CKR_OK : CKR_BUFFER_TOO_SMALL; goto cleanup_rsapri2asn; } *buf_len = p8obj_octs->bv_len; (void) memcpy(buf, p8obj_octs->bv_val, *buf_len); cleanup_rsapri2asn: freezero(tmp_pad.big_value, tmp_pad.big_value_len); if (key_asn != NULLBER) ber_free(key_asn, 1); if (key_octs != NULL) ber_bvfree(key_octs); if (p8obj_asn != NULLBER) ber_free(p8obj_asn, 1); if (p8obj_octs != NULL) ber_bvfree(p8obj_octs); return (rv); } /* Encode DSA private key in ASN.1 BER syntax. */ static CK_RV dsa_pri_to_asn1(soft_object_t *objp, uchar_t *buf, ulong_t *buf_len) { CK_RV rv = CKR_OK; BerElement *key_asn = NULLBER, *p8obj_asn = NULLBER; BerValue *key_octs = NULL, *p8obj_octs = NULL; int version = SOFT_ASN_VERSION; biginteger_t tmp_pad = { NULL, 0 }; /* * The ASN.1 syntax for a DSA private key is: * * PKCS#8 \* PrivateKeyInfo *\ * --------------------------------- * Sequence { * version INTEGER; * Sequence { \* PrivateKeyAlgorithm *\ * OID 0x06, \* DSA algorithm OID *\ * param(DSS-params) OCTETSTRING = * PKCS#? \* DSSParameter *\ * ---------------------------------- * Sequence { * prime INTEGER, * subprime INTEGER, * base INTEGER, * } * } * DSAPrivateKey OCTETSTRING = * PKCS#1 \* DSAPrivateKey *\ * --------------------------- * value INTEGER * } * * The code below starts building the innermost octets * DSAPrivateKey, and then builds the PrivateKeyInfo * sequence around that octet string. The BER syntax * used in this function is (others may be possible): * { i { to { to to to } } to } * where "i" is for integers with fixed size * where "to" is for integers that vary in size (length + value) * where "{}" delimit sequences */ /* DSAPrivateKey ... */ if ((key_asn = ber_alloc()) == NULLBER) return (CKR_HOST_MEMORY); /* ... value */ if ((rv = pad_bigint_attr(OBJ_PRI_DSA_VALUE(objp), &tmp_pad)) != CKR_OK) goto cleanup_dsapri2asn; if (ber_printf(key_asn, "to", LBER_INTEGER, tmp_pad.big_value, tmp_pad.big_value_len) == -1) { rv = CKR_GENERAL_ERROR; goto cleanup_dsapri2asn; } /* Convert key ASN.1 to octet string. */ if (ber_flatten(key_asn, &key_octs) == -1) { rv = CKR_GENERAL_ERROR; goto cleanup_dsapri2asn; } /* PKCS#8 PrivateKeyInfo ... */ if ((p8obj_asn = ber_alloc()) == NULLBER) { rv = CKR_HOST_MEMORY; goto cleanup_dsapri2asn; } /* * Start off the PKCS#8 object ASN.1: * begin-sequence { * version * begin-sequence { * OID, * ... */ if (ber_printf(p8obj_asn, "{i{to", version, OID_TAG, DSA_OID, sizeof (DSA_OID)) == -1) { rv = CKR_GENERAL_ERROR; goto cleanup_dsapri2asn; } /* * Add DSS parameters: * ... * begin-sequence { * prime, * ... */ if ((rv = pad_bigint_attr(OBJ_PRI_DSA_PRIME(objp), &tmp_pad)) != CKR_OK) goto cleanup_dsapri2asn; if (ber_printf(p8obj_asn, "{to", LBER_INTEGER, tmp_pad.big_value, tmp_pad.big_value_len) == -1) { rv = CKR_GENERAL_ERROR; goto cleanup_dsapri2asn; } /* * ... * subprime, * ... */ if ((rv = pad_bigint_attr(OBJ_PRI_DSA_SUBPRIME(objp), &tmp_pad)) != CKR_OK) goto cleanup_dsapri2asn; if (ber_printf(p8obj_asn, "to", LBER_INTEGER, tmp_pad.big_value, tmp_pad.big_value_len) == -1) { rv = CKR_GENERAL_ERROR; goto cleanup_dsapri2asn; } /* * ... * base * } end-sequence */ if ((rv = pad_bigint_attr(OBJ_PRI_DSA_BASE(objp), &tmp_pad)) != CKR_OK) goto cleanup_dsapri2asn; if (ber_printf(p8obj_asn, "to}", LBER_INTEGER, tmp_pad.big_value, tmp_pad.big_value_len) == -1) { rv = CKR_GENERAL_ERROR; goto cleanup_dsapri2asn; } /* * Add the key octet string: * } end-sequence * DSAPrivateKey * } end-sequence */ if (ber_printf(p8obj_asn, "}o}", key_octs->bv_val, key_octs->bv_len) == -1) { rv = CKR_GENERAL_ERROR; goto cleanup_dsapri2asn; } /* Convert PKCS#8 object ASN.1 to octet string. */ if (ber_flatten(p8obj_asn, &p8obj_octs) == -1) { rv = CKR_GENERAL_ERROR; goto cleanup_dsapri2asn; } /* Ship out the PKCS#8 object ASN.1 octet string, if possible. */ /* * If the user passes in a null buf, then buf_len is set. * If the user passes in a value with buf_len, then it can * be checked to see if the accompanying buf is big enough. * If it is, the octet string is copied into a pre-malloc'd * buf; otherwise the user must resize buf and call again. * In either case, buf_len is reset to the corrected size. * See PKCS#11 section 11.2. */ #ifdef _LP64 /* LINTED E_CAST_INT_TO_SMALL_INT */ if ((buf == NULL) || ((ber_len_t)(*buf_len) < p8obj_octs->bv_len)) { #else if ((buf == NULL) || ((ber_len_t)(*buf_len) < p8obj_octs->bv_len)) { #endif *buf_len = p8obj_octs->bv_len; rv = (buf == NULL) ? CKR_OK : CKR_BUFFER_TOO_SMALL; goto cleanup_dsapri2asn; } *buf_len = p8obj_octs->bv_len; (void) memcpy(buf, p8obj_octs->bv_val, *buf_len); cleanup_dsapri2asn: freezero(tmp_pad.big_value, tmp_pad.big_value_len); if (key_asn != NULLBER) ber_free(key_asn, 1); if (key_octs != NULL) ber_bvfree(key_octs); if (p8obj_asn != NULLBER) ber_free(p8obj_asn, 1); if (p8obj_octs != NULL) ber_bvfree(p8obj_octs); return (rv); } /* Encode DH private key in ASN.1 BER syntax. */ static CK_RV dh_pri_to_asn1(soft_object_t *objp, uchar_t *buf, ulong_t *buf_len) { CK_RV rv = CKR_OK; BerElement *key_asn = NULLBER, *p8obj_asn = NULLBER; BerValue *key_octs = NULL, *p8obj_octs = NULL; int version = SOFT_ASN_VERSION; biginteger_t tmp_pad = { NULL, 0 }; /* * The ASN.1 syntax for a DH private key is: * * PKCS#8 \* PrivateKeyInfo *\ * --------------------------------- * Sequence { * version INTEGER; * Sequence { \* PrivateKeyAlgorithm *\ * OID 0x06, \* DH algorithm OID *\ * param(DH-params) OCTETSTRING = * PKCS#3 \* DHParameter *\ * ------------------------- * Sequence { * prime INTEGER, * base INTEGER * } * } * DHPrivateKey OCTETSTRING = * PKCS#1 \* DHPrivateKey *\ * -------------------------- * value INTEGER * } * * The code below starts building the innermost octets * DHPrivateKey, and then builds the PrivateKeyInfo * sequence around that octet string. The BER syntax * used in this function is (others may be possible): * { i { to { to to } } to } * where "i" is for integers with fixed size * where "to" is for integers that vary in size (length + value) * where "{}" delimit sequences */ /* DHPrivateKey ... */ if ((key_asn = ber_alloc()) == NULLBER) return (CKR_HOST_MEMORY); /* ... value */ if ((rv = pad_bigint_attr(OBJ_PRI_DH_VALUE(objp), &tmp_pad)) != CKR_OK) goto cleanup_dhpri2asn; if (ber_printf(key_asn, "to", LBER_INTEGER, tmp_pad.big_value, tmp_pad.big_value_len) == -1) { rv = CKR_GENERAL_ERROR; goto cleanup_dhpri2asn; } /* Convert key ASN.1 to octet string. */ if (ber_flatten(key_asn, &key_octs) == -1) { rv = CKR_GENERAL_ERROR; goto cleanup_dhpri2asn; } /* PKCS#8 PrivateKeyInfo ... */ if ((p8obj_asn = ber_alloc()) == NULLBER) { rv = CKR_HOST_MEMORY; goto cleanup_dhpri2asn; } /* * Start off the PKCS#8 object ASN.1: * begin-sequence { * version * begin-sequence { * OID, * ... */ if (ber_printf(p8obj_asn, "{i{to", version, OID_TAG, DH_OID, sizeof (DH_OID)) == -1) { rv = CKR_GENERAL_ERROR; goto cleanup_dhpri2asn; } /* * Add DH parameters: * ... * begin-sequence { * prime, * ... */ if ((rv = pad_bigint_attr(OBJ_PRI_DH_PRIME(objp), &tmp_pad)) != CKR_OK) goto cleanup_dhpri2asn; if (ber_printf(p8obj_asn, "{to", LBER_INTEGER, tmp_pad.big_value, tmp_pad.big_value_len) == -1) { rv = CKR_GENERAL_ERROR; goto cleanup_dhpri2asn; } /* * ... * base * } end-sequence */ if ((rv = pad_bigint_attr(OBJ_PRI_DH_BASE(objp), &tmp_pad)) != CKR_OK) goto cleanup_dhpri2asn; if (ber_printf(p8obj_asn, "to}", LBER_INTEGER, tmp_pad.big_value, tmp_pad.big_value_len) == -1) { rv = CKR_GENERAL_ERROR; goto cleanup_dhpri2asn; } /* * Add the key octet string: * } end-sequence * DSAPrivateKey * } end-sequence */ if (ber_printf(p8obj_asn, "}o}", key_octs->bv_val, key_octs->bv_len) == -1) { rv = CKR_GENERAL_ERROR; goto cleanup_dhpri2asn; } /* Convert PKCS#8 object ASN.1 to octet string. */ if (ber_flatten(p8obj_asn, &p8obj_octs) == -1) { rv = CKR_GENERAL_ERROR; goto cleanup_dhpri2asn; } /* Ship out the PKCS#8 object ASN.1 octet string, if possible. */ /* * If the user passes in a null buf, then buf_len is set. * If the user passes in a value with buf_len, then it can * be checked to see if the accompanying buf is big enough. * If it is, the octet string is copied into a pre-malloc'd * buf; otherwise the user must resize buf and call again. * In either case, buf_len is reset to the corrected size. * See PKCS#11 section 11.2. */ #ifdef _LP64 /* LINTED E_CAST_INT_TO_SMALL_INT */ if ((buf == NULL) || ((ber_len_t)(*buf_len) < p8obj_octs->bv_len)) { #else if ((buf == NULL) || ((ber_len_t)(*buf_len) < p8obj_octs->bv_len)) { #endif *buf_len = p8obj_octs->bv_len; rv = (buf == NULL) ? CKR_OK : CKR_BUFFER_TOO_SMALL; goto cleanup_dhpri2asn; } *buf_len = p8obj_octs->bv_len; (void) memcpy(buf, p8obj_octs->bv_val, *buf_len); cleanup_dhpri2asn: freezero(tmp_pad.big_value, tmp_pad.big_value_len); if (key_asn != NULLBER) ber_free(key_asn, 1); if (key_octs != NULL) ber_bvfree(key_octs); if (p8obj_asn != NULLBER) ber_free(p8obj_asn, 1); if (p8obj_octs != NULL) ber_bvfree(p8obj_octs); return (rv); } /* Encode DH X9.42 private key in ASN.1 BER syntax. */ static CK_RV x942_dh_pri_to_asn1(soft_object_t *objp, uchar_t *buf, ulong_t *buf_len) { CK_RV rv = CKR_OK; BerElement *key_asn = NULLBER, *p8obj_asn = NULLBER; BerValue *key_octs = NULL, *p8obj_octs = NULL; int version = SOFT_ASN_VERSION; biginteger_t tmp_pad = { NULL, 0 }; /* * The ASN.1 syntax for a X9.42 DH private key is: * * PKCS#8 \* PrivateKeyInfo *\ * --------------------------------- * Sequence { * version INTEGER; * Sequence { \* PrivateKeyAlgorithm *\ * OID 0x06, \* DH X9.42 algorithm OID *\ * param(DH-params) OCTETSTRING = * PKCS#3 \* DHParameter *\ * ------------------------- * Sequence { * prime INTEGER, * base INTEGER, * subprime INTEGER \* for X9.42 *\ * } * } * DHPrivateKey OCTETSTRING = * PKCS#1 \* DHPrivateKey *\ * -------------------------- * value INTEGER * } * * The code below starts building the innermost octets * DHPrivateKey, and then builds the PrivateKeyInfo * sequence around that octet string. The BER syntax * used in this function is (others may be possible): * { i { to { to to } } to } * where "i" is for integers with fixed size * where "to" is for integers that vary in size (length + value) * where "{}" delimit sequences */ /* DHPrivateKey ... */ if ((key_asn = ber_alloc()) == NULLBER) return (CKR_HOST_MEMORY); /* ... value */ if ((rv = pad_bigint_attr(OBJ_PRI_DH942_VALUE(objp), &tmp_pad)) != CKR_OK) goto cleanup_x942dhpri2asn; if (ber_printf(key_asn, "to", LBER_INTEGER, tmp_pad.big_value, tmp_pad.big_value_len) == -1) { rv = CKR_GENERAL_ERROR; goto cleanup_x942dhpri2asn; } /* Convert key ASN.1 to octet string. */ if (ber_flatten(key_asn, &key_octs) == -1) { rv = CKR_GENERAL_ERROR; goto cleanup_x942dhpri2asn; } /* PKCS#8 PrivateKeyInfo ... */ if ((p8obj_asn = ber_alloc()) == NULLBER) { rv = CKR_HOST_MEMORY; goto cleanup_x942dhpri2asn; } /* * Start off the PKCS#8 object ASN.1: * begin-sequence { * version * begin-sequence { * OID, * ... */ if (ber_printf(p8obj_asn, "{i{to", version, OID_TAG, DH942_OID, sizeof (DH942_OID)) == -1) { rv = CKR_GENERAL_ERROR; goto cleanup_x942dhpri2asn; } /* * Add DH parameters: * ... * begin-sequence { * prime, * ... */ if ((rv = pad_bigint_attr(OBJ_PRI_DH942_PRIME(objp), &tmp_pad)) != CKR_OK) goto cleanup_x942dhpri2asn; if (ber_printf(p8obj_asn, "{to", LBER_INTEGER, tmp_pad.big_value, tmp_pad.big_value_len) == -1) { rv = CKR_GENERAL_ERROR; goto cleanup_x942dhpri2asn; } /* * ... * base, * ... */ if ((rv = pad_bigint_attr(OBJ_PRI_DH942_BASE(objp), &tmp_pad)) != CKR_OK) goto cleanup_x942dhpri2asn; if (ber_printf(p8obj_asn, "to", LBER_INTEGER, tmp_pad.big_value, tmp_pad.big_value_len) == -1) { rv = CKR_GENERAL_ERROR; goto cleanup_x942dhpri2asn; } /* * ... * subprime * } end-sequence */ if ((rv = pad_bigint_attr(OBJ_PRI_DH942_SUBPRIME(objp), &tmp_pad)) != CKR_OK) goto cleanup_x942dhpri2asn; if (ber_printf(p8obj_asn, "to}", LBER_INTEGER, tmp_pad.big_value, tmp_pad.big_value_len) == -1) { rv = CKR_GENERAL_ERROR; goto cleanup_x942dhpri2asn; } /* * Add the key octet string: * } end-sequence * DHPrivateKey * } end-sequence */ if (ber_printf(p8obj_asn, "}o}", key_octs->bv_val, key_octs->bv_len) == -1) { rv = CKR_GENERAL_ERROR; goto cleanup_x942dhpri2asn; } /* Convert PKCS#8 object ASN.1 to octet string. */ if (ber_flatten(p8obj_asn, &p8obj_octs) == -1) { rv = CKR_GENERAL_ERROR; goto cleanup_x942dhpri2asn; } /* Ship out the PKCS#8 object ASN.1 octet string, if possible. */ /* * If the user passes in a null buf, then buf_len is set. * If the user passes in a value with buf_len, then it can * be checked to see if the accompanying buf is big enough. * If it is, the octet string is copied into a pre-malloc'd * buf; otherwise the user must resize buf and call again. * In either case, buf_len is reset to the corrected size. * See PKCS#11 section 11.2. */ #ifdef _LP64 /* LINTED E_CAST_INT_TO_SMALL_INT */ if ((buf == NULL) || ((ber_len_t)(*buf_len) < p8obj_octs->bv_len)) { #else if ((buf == NULL) || ((ber_len_t)(*buf_len) < p8obj_octs->bv_len)) { #endif *buf_len = p8obj_octs->bv_len; rv = (buf == NULL) ? CKR_OK : CKR_BUFFER_TOO_SMALL; goto cleanup_x942dhpri2asn; } *buf_len = p8obj_octs->bv_len; (void) memcpy(buf, p8obj_octs->bv_val, *buf_len); cleanup_x942dhpri2asn: freezero(tmp_pad.big_value, tmp_pad.big_value_len); if (key_asn != NULLBER) ber_free(key_asn, 1); if (key_octs != NULL) ber_bvfree(key_octs); if (p8obj_asn != NULLBER) ber_free(p8obj_asn, 1); if (p8obj_octs != NULL) ber_bvfree(p8obj_octs); return (rv); } /* * Encode the object key from the soft_object_t into ASN.1 format. */ CK_RV soft_object_to_asn1(soft_object_t *objp, uchar_t *buf, ulong_t *buf_len) { CK_OBJECT_CLASS class = objp->class; CK_KEY_TYPE keytype = objp->key_type; switch (class) { case CKO_PRIVATE_KEY: switch (keytype) { case CKK_RSA: return (rsa_pri_to_asn1(objp, buf, buf_len)); case CKK_DSA: return (dsa_pri_to_asn1(objp, buf, buf_len)); case CKK_DH: return (dh_pri_to_asn1(objp, buf, buf_len)); case CKK_X9_42_DH: return (x942_dh_pri_to_asn1(objp, buf, buf_len)); default: return (CKR_FUNCTION_NOT_SUPPORTED); } /* keytype */ default: return (CKR_FUNCTION_NOT_SUPPORTED); } /* class */ } /* Decode ASN.1 BER syntax into RSA private key. */ static CK_RV asn1_to_rsa_pri(private_key_obj_t *keyp, uchar_t *buf, ulong_t buf_len) { CK_RV rv = CKR_OK; BerValue p8obj_octs, key_octs; BerElement *p8obj_asn = NULLBER, *key_asn = NULLBER; ber_len_t size, tmplen; char *cookie; int version; uchar_t oid[sizeof (RSA_OID) + 1]; biginteger_t tmp, tmp_nopad = { NULL, 0 }; p8obj_octs.bv_val = (char *)buf; #ifdef _LP64 /* LINTED E_CAST_INT_TO_SMALL_INT */ p8obj_octs.bv_len = (ber_len_t)buf_len; #else p8obj_octs.bv_len = (ber_len_t)buf_len; #endif key_octs.bv_val = NULL; key_octs.bv_len = 0; /* Decode PKCS#8 object ASN.1, verifying it is RSA private key. */ if ((p8obj_asn = ber_init(&p8obj_octs)) == NULLBER) return (CKR_GENERAL_ERROR); /* PKCS#8 PrivateKeyInfo ... */ if (ber_first_element(p8obj_asn, &size, &cookie) != LBER_INTEGER) { rv = CKR_WRAPPED_KEY_INVALID; goto cleanup_asn2rsapri; } /* ... begin-sequence { version, */ (void) ber_scanf(p8obj_asn, "i", &version); /* "{i" ? */ if (ber_next_element(p8obj_asn, &size, cookie) != LBER_SEQUENCE) { rv = CKR_WRAPPED_KEY_INVALID; goto cleanup_asn2rsapri; } /* ... begin-sequence { */ (void) ber_scanf(p8obj_asn, "{"); if (ber_next_element(p8obj_asn, &size, cookie) != OID_TAG) { rv = CKR_WRAPPED_KEY_INVALID; goto cleanup_asn2rsapri; } /* ... OID, \* RSA algorithm OID *\ */ if (size != sizeof (RSA_OID)) { rv = CKR_FUNCTION_NOT_SUPPORTED; goto cleanup_asn2rsapri; } size = sizeof (oid); (void) ber_scanf(p8obj_asn, "s", oid, &size); if (memcmp(oid, RSA_OID, size) != 0) { rv = CKR_FUNCTION_NOT_SUPPORTED; goto cleanup_asn2rsapri; } if (ber_next_element(p8obj_asn, &size, cookie) != LBER_NULL) { rv = CKR_WRAPPED_KEY_INVALID; goto cleanup_asn2rsapri; } /* ... param(NULL) } end-sequence */ (void) ber_scanf(p8obj_asn, "n"); /* "n}" ? */ if (ber_next_element(p8obj_asn, &size, cookie) != LBER_OCTETSTRING) { rv = CKR_WRAPPED_KEY_INVALID; goto cleanup_asn2rsapri; } /* ... RSAPrivateKey } end-sequence */ key_octs.bv_len = size + 1; if ((key_octs.bv_val = malloc(size + 1)) == NULL) { rv = CKR_HOST_MEMORY; goto cleanup_asn2rsapri; } (void) ber_scanf(p8obj_asn, "s", /* "s}" ? */ key_octs.bv_val, &key_octs.bv_len); /* Decode key octet string into softtoken key object. */ if ((key_asn = ber_init(&key_octs)) == NULLBER) { rv = CKR_GENERAL_ERROR; goto cleanup_asn2rsapri; } /* ... begin-sequence { version, */ if (ber_first_element(key_asn, &size, &cookie) != LBER_INTEGER) { rv = CKR_WRAPPED_KEY_INVALID; goto cleanup_asn2rsapri; } (void) ber_scanf(key_asn, "i", &version); /* "{i" ? */ /* ... modulus, */ if (ber_next_element(key_asn, &size, cookie) != LBER_INTEGER) { rv = CKR_WRAPPED_KEY_INVALID; goto cleanup_asn2rsapri; } if (size > MAX_RSA_KEY) { rv = CKR_FUNCTION_NOT_SUPPORTED; goto cleanup_asn2rsapri; } tmplen = size + 1; if ((tmp.big_value = malloc(tmplen)) == NULL) { rv = CKR_HOST_MEMORY; goto cleanup_asn2rsapri; } (void) ber_scanf(key_asn, "s", tmp.big_value, &tmplen); tmp.big_value_len = tmplen; if ((rv = unpad_bigint_attr(tmp, &tmp_nopad)) != CKR_OK) { free(tmp.big_value); goto cleanup_asn2rsapri; } free(tmp.big_value); copy_bigint_attr(&tmp_nopad, KEY_PRI_RSA_MOD(keyp)); /* ... public exponent, */ if (ber_next_element(key_asn, &size, cookie) != LBER_INTEGER) { rv = CKR_WRAPPED_KEY_INVALID; goto error_asn2rsapri; } if (size > MAX_RSA_KEY) { rv = CKR_FUNCTION_NOT_SUPPORTED; goto error_asn2rsapri; } tmplen = size + 1; if ((tmp.big_value = malloc(tmplen)) == NULL) { rv = CKR_HOST_MEMORY; goto error_asn2rsapri; } (void) ber_scanf(key_asn, "s", tmp.big_value, &tmplen); tmp.big_value_len = tmplen; if ((rv = unpad_bigint_attr(tmp, &tmp_nopad)) != CKR_OK) { free(tmp.big_value); goto error_asn2rsapri; } free(tmp.big_value); copy_bigint_attr(&tmp_nopad, KEY_PRI_RSA_PUBEXPO(keyp)); /* ... private exponent, */ if (ber_next_element(key_asn, &size, cookie) != LBER_INTEGER) { rv = CKR_WRAPPED_KEY_INVALID; goto error_asn2rsapri; } if (size > MAX_RSA_KEY) { rv = CKR_FUNCTION_NOT_SUPPORTED; goto error_asn2rsapri; } tmplen = size + 1; if ((tmp.big_value = malloc(tmplen)) == NULL) { rv = CKR_HOST_MEMORY; goto error_asn2rsapri; } (void) ber_scanf(key_asn, "s", tmp.big_value, &tmplen); tmp.big_value_len = tmplen; if ((rv = unpad_bigint_attr(tmp, &tmp_nopad)) != CKR_OK) { free(tmp.big_value); goto error_asn2rsapri; } free(tmp.big_value); copy_bigint_attr(&tmp_nopad, KEY_PRI_RSA_PRIEXPO(keyp)); /* ... prime 1, */ if (ber_next_element(key_asn, &size, cookie) != LBER_INTEGER) { rv = CKR_WRAPPED_KEY_INVALID; goto error_asn2rsapri; } if (size > MAX_RSA_KEY) { rv = CKR_FUNCTION_NOT_SUPPORTED; goto error_asn2rsapri; } tmplen = size + 1; if ((tmp.big_value = malloc(tmplen)) == NULL) { rv = CKR_HOST_MEMORY; goto error_asn2rsapri; } (void) ber_scanf(key_asn, "s", tmp.big_value, &tmplen); tmp.big_value_len = tmplen; if ((rv = unpad_bigint_attr(tmp, &tmp_nopad)) != CKR_OK) { free(tmp.big_value); goto error_asn2rsapri; } free(tmp.big_value); copy_bigint_attr(&tmp_nopad, KEY_PRI_RSA_PRIME1(keyp)); /* ... prime 2, */ if (ber_next_element(key_asn, &size, cookie) != LBER_INTEGER) { rv = CKR_WRAPPED_KEY_INVALID; goto error_asn2rsapri; } if (size > MAX_RSA_KEY) { rv = CKR_FUNCTION_NOT_SUPPORTED; goto error_asn2rsapri; } tmplen = size + 1; if ((tmp.big_value = malloc(tmplen)) == NULL) { rv = CKR_HOST_MEMORY; goto error_asn2rsapri; } (void) ber_scanf(key_asn, "s", tmp.big_value, &tmplen); tmp.big_value_len = tmplen; if ((rv = unpad_bigint_attr(tmp, &tmp_nopad)) != CKR_OK) { free(tmp.big_value); goto error_asn2rsapri; } free(tmp.big_value); copy_bigint_attr(&tmp_nopad, KEY_PRI_RSA_PRIME2(keyp)); /* ... exponent 1, */ if (ber_next_element(key_asn, &size, cookie) != LBER_INTEGER) { rv = CKR_WRAPPED_KEY_INVALID; goto error_asn2rsapri; } if (size > MAX_RSA_KEY) { rv = CKR_FUNCTION_NOT_SUPPORTED; goto error_asn2rsapri; } tmplen = size + 1; if ((tmp.big_value = malloc(tmplen)) == NULL) { rv = CKR_HOST_MEMORY; goto error_asn2rsapri; } (void) ber_scanf(key_asn, "s", tmp.big_value, &tmplen); tmp.big_value_len = tmplen; if ((rv = unpad_bigint_attr(tmp, &tmp_nopad)) != CKR_OK) { free(tmp.big_value); goto error_asn2rsapri; } free(tmp.big_value); copy_bigint_attr(&tmp_nopad, KEY_PRI_RSA_EXPO1(keyp)); /* ... exponent 2, */ if (ber_next_element(key_asn, &size, cookie) != LBER_INTEGER) { rv = CKR_WRAPPED_KEY_INVALID; goto error_asn2rsapri; } if (size > MAX_RSA_KEY) { rv = CKR_FUNCTION_NOT_SUPPORTED; goto error_asn2rsapri; } tmplen = size + 1; if ((tmp.big_value = malloc(tmplen)) == NULL) { rv = CKR_HOST_MEMORY; goto error_asn2rsapri; } (void) ber_scanf(key_asn, "s", tmp.big_value, &tmplen); tmp.big_value_len = tmplen; if ((rv = unpad_bigint_attr(tmp, &tmp_nopad)) != CKR_OK) { free(tmp.big_value); goto error_asn2rsapri; } free(tmp.big_value); copy_bigint_attr(&tmp_nopad, KEY_PRI_RSA_EXPO2(keyp)); /* ... coefficient } end-sequence */ if (ber_next_element(key_asn, &size, cookie) != LBER_INTEGER) { rv = CKR_WRAPPED_KEY_INVALID; goto error_asn2rsapri; } if (size > MAX_RSA_KEY) { rv = CKR_FUNCTION_NOT_SUPPORTED; goto error_asn2rsapri; } tmplen = size + 1; if ((tmp.big_value = malloc(tmplen)) == NULL) { rv = CKR_HOST_MEMORY; goto error_asn2rsapri; } (void) ber_scanf(key_asn, "s", /* "s}" ? */ tmp.big_value, &tmplen); tmp.big_value_len = tmplen; if ((rv = unpad_bigint_attr(tmp, &tmp_nopad)) != CKR_OK) { free(tmp.big_value); goto error_asn2rsapri; } free(tmp.big_value); copy_bigint_attr(&tmp_nopad, KEY_PRI_RSA_COEF(keyp)); goto cleanup_asn2rsapri; error_asn2rsapri: bigint_attr_cleanup(KEY_PRI_RSA_MOD(keyp)); bigint_attr_cleanup(KEY_PRI_RSA_PUBEXPO(keyp)); bigint_attr_cleanup(KEY_PRI_RSA_PRIEXPO(keyp)); bigint_attr_cleanup(KEY_PRI_RSA_PRIME1(keyp)); bigint_attr_cleanup(KEY_PRI_RSA_PRIME2(keyp)); bigint_attr_cleanup(KEY_PRI_RSA_EXPO1(keyp)); bigint_attr_cleanup(KEY_PRI_RSA_EXPO2(keyp)); bigint_attr_cleanup(KEY_PRI_RSA_COEF(keyp)); cleanup_asn2rsapri: freezero(tmp_nopad.big_value, tmp_nopad.big_value_len); if (p8obj_asn != NULLBER) ber_free(p8obj_asn, 1); if (key_octs.bv_val != NULL) free(key_octs.bv_val); if (key_asn != NULLBER) ber_free(key_asn, 1); return (rv); } /* Decode ASN.1 BER syntax into DSA private key. */ static CK_RV asn1_to_dsa_pri(private_key_obj_t *keyp, uchar_t *buf, ulong_t buf_len) { CK_RV rv = CKR_OK; BerValue p8obj_octs, key_octs; BerElement *p8obj_asn = NULLBER, *key_asn = NULLBER; ber_len_t size, tmplen; char *cookie; int version; uchar_t oid[sizeof (DSA_OID) + 1]; biginteger_t tmp, tmp_nopad = { NULL, 0 }; p8obj_octs.bv_val = (char *)buf; #ifdef _LP64 /* LINTED E_CAST_INT_TO_SMALL_INT */ p8obj_octs.bv_len = (ber_len_t)buf_len; #else p8obj_octs.bv_len = (ber_len_t)buf_len; #endif key_octs.bv_val = NULL; key_octs.bv_len = 0; /* Decode PKCS#8 object ASN.1, verifying it is DSA private key. */ if ((p8obj_asn = ber_init(&p8obj_octs)) == NULLBER) return (CKR_GENERAL_ERROR); /* PKCS#8 PrivateKeyInfo ... */ if (ber_first_element(p8obj_asn, &size, &cookie) != LBER_INTEGER) { rv = CKR_WRAPPED_KEY_INVALID; goto cleanup_asn2dsapri; } /* ... begin-sequence { version, */ (void) ber_scanf(p8obj_asn, "i", &version); /* "{i" ? */ if (ber_next_element(p8obj_asn, &size, cookie) != LBER_SEQUENCE) { rv = CKR_WRAPPED_KEY_INVALID; goto cleanup_asn2dsapri; } /* ... begin-sequence { */ (void) ber_scanf(p8obj_asn, "{"); if (ber_next_element(p8obj_asn, &size, cookie) != OID_TAG) { rv = CKR_WRAPPED_KEY_INVALID; goto cleanup_asn2dsapri; } /* ... OID, \* DSA algorithm OID *\ */ if (size != sizeof (DSA_OID)) { rv = CKR_FUNCTION_NOT_SUPPORTED; goto cleanup_asn2dsapri; } size = sizeof (oid); (void) ber_scanf(p8obj_asn, "s", oid, &size); if (memcmp(oid, DSA_OID, size) != 0) { rv = CKR_FUNCTION_NOT_SUPPORTED; goto cleanup_asn2dsapri; } if (ber_next_element(p8obj_asn, &size, cookie) != LBER_SEQUENCE) { rv = CKR_WRAPPED_KEY_INVALID; goto cleanup_asn2dsapri; } /* ... begin-sequence { */ (void) ber_scanf(p8obj_asn, "{"); if (ber_next_element(p8obj_asn, &size, cookie) != LBER_INTEGER) { rv = CKR_WRAPPED_KEY_INVALID; goto cleanup_asn2dsapri; } /* ... prime, */ if (size > MAX_DSA_KEY) { rv = CKR_FUNCTION_NOT_SUPPORTED; goto cleanup_asn2dsapri; } tmplen = size + 1; if ((tmp.big_value = malloc(tmplen)) == NULL) { rv = CKR_HOST_MEMORY; goto cleanup_asn2dsapri; } (void) ber_scanf(p8obj_asn, "s", tmp.big_value, &tmplen); tmp.big_value_len = tmplen; if ((rv = unpad_bigint_attr(tmp, &tmp_nopad)) != CKR_OK) { free(tmp.big_value); goto cleanup_asn2dsapri; } free(tmp.big_value); copy_bigint_attr(&tmp_nopad, KEY_PRI_DSA_PRIME(keyp)); if (ber_next_element(p8obj_asn, &size, cookie) != LBER_INTEGER) { rv = CKR_WRAPPED_KEY_INVALID; goto error_asn2dsapri; } /* ... subprime, */ if (size > MAX_DSA_KEY) { rv = CKR_FUNCTION_NOT_SUPPORTED; goto error_asn2dsapri; } tmplen = size + 1; if ((tmp.big_value = malloc(tmplen)) == NULL) { rv = CKR_HOST_MEMORY; goto error_asn2dsapri; } (void) ber_scanf(p8obj_asn, "s", tmp.big_value, &tmplen); tmp.big_value_len = tmplen; if ((rv = unpad_bigint_attr(tmp, &tmp_nopad)) != CKR_OK) { free(tmp.big_value); goto error_asn2dsapri; } free(tmp.big_value); copy_bigint_attr(&tmp_nopad, KEY_PRI_DSA_SUBPRIME(keyp)); if (ber_next_element(p8obj_asn, &size, cookie) != LBER_INTEGER) { rv = CKR_WRAPPED_KEY_INVALID; goto error_asn2dsapri; } /* ... base } end-sequence } end-sequence */ if (size > MAX_DSA_KEY) { rv = CKR_FUNCTION_NOT_SUPPORTED; goto error_asn2dsapri; } tmplen = size + 1; if ((tmp.big_value = malloc(tmplen)) == NULL) { rv = CKR_HOST_MEMORY; goto error_asn2dsapri; } (void) ber_scanf(p8obj_asn, "s", /* "s}}" ? */ tmp.big_value, &tmplen); tmp.big_value_len = tmplen; if ((rv = unpad_bigint_attr(tmp, &tmp_nopad)) != CKR_OK) { free(tmp.big_value); goto error_asn2dsapri; } free(tmp.big_value); copy_bigint_attr(&tmp_nopad, KEY_PRI_DSA_BASE(keyp)); if (ber_next_element(p8obj_asn, &size, cookie) != LBER_OCTETSTRING) { rv = CKR_WRAPPED_KEY_INVALID; goto error_asn2dsapri; } /* ... DSAPrivateKey } end-sequence */ key_octs.bv_len = size + 1; if ((key_octs.bv_val = malloc(size + 1)) == NULL) { rv = CKR_HOST_MEMORY; goto error_asn2dsapri; } (void) ber_scanf(p8obj_asn, "s", /* "s}" ? */ key_octs.bv_val, &key_octs.bv_len); /* Decode key octet string into softtoken key object. */ if ((key_asn = ber_init(&key_octs)) == NULLBER) { rv = CKR_GENERAL_ERROR; goto error_asn2dsapri; } if (ber_next_element(key_asn, &size, cookie) != LBER_INTEGER) { rv = CKR_WRAPPED_KEY_INVALID; goto error_asn2dsapri; } /* ... value } end-sequence */ if (size > MAX_DSA_KEY) { rv = CKR_FUNCTION_NOT_SUPPORTED; goto error_asn2dsapri; } tmplen = size + 1; if ((tmp.big_value = malloc(tmplen)) == NULL) { rv = CKR_HOST_MEMORY; goto error_asn2dsapri; } (void) ber_scanf(key_asn, "s", /* "s}" ? */ tmp.big_value, &tmplen); tmp.big_value_len = tmplen; if ((rv = unpad_bigint_attr(tmp, &tmp_nopad)) != CKR_OK) { free(tmp.big_value); goto error_asn2dsapri; } free(tmp.big_value); copy_bigint_attr(&tmp_nopad, KEY_PRI_DSA_VALUE(keyp)); goto cleanup_asn2dsapri; error_asn2dsapri: bigint_attr_cleanup(KEY_PRI_DSA_PRIME(keyp)); bigint_attr_cleanup(KEY_PRI_DSA_SUBPRIME(keyp)); bigint_attr_cleanup(KEY_PRI_DSA_BASE(keyp)); bigint_attr_cleanup(KEY_PRI_DSA_VALUE(keyp)); cleanup_asn2dsapri: freezero(tmp_nopad.big_value, tmp_nopad.big_value_len); if (p8obj_asn != NULLBER) ber_free(p8obj_asn, 1); if (key_octs.bv_val != NULL) free(key_octs.bv_val); if (key_asn != NULLBER) ber_free(key_asn, 1); return (rv); } /* Decode ASN.1 BER syntax into DH private key. */ static CK_RV asn1_to_dh_pri(private_key_obj_t *keyp, uchar_t *buf, ulong_t buf_len) { CK_RV rv = CKR_OK; BerValue p8obj_octs, key_octs; BerElement *p8obj_asn = NULLBER, *key_asn = NULLBER; ber_len_t size, tmplen; char *cookie; int version; uchar_t oid[sizeof (DH_OID) + 1]; biginteger_t tmp, tmp_nopad = { NULL, 0 }; p8obj_octs.bv_val = (char *)buf; #ifdef _LP64 /* LINTED E_CAST_INT_TO_SMALL_INT */ p8obj_octs.bv_len = (ber_len_t)buf_len; #else p8obj_octs.bv_len = (ber_len_t)buf_len; #endif key_octs.bv_val = NULL; key_octs.bv_len = 0; /* Decode PKCS#8 object ASN.1, verifying it is DH private key. */ if ((p8obj_asn = ber_init(&p8obj_octs)) == NULLBER) return (CKR_GENERAL_ERROR); /* PKCS#8 PrivateKeyInfo ... */ if (ber_first_element(p8obj_asn, &size, &cookie) != LBER_INTEGER) { rv = CKR_WRAPPED_KEY_INVALID; goto cleanup_asn2dhpri; } /* ... begin-sequence { version, */ (void) ber_scanf(p8obj_asn, "i", &version); /* "{i" ? */ if (ber_next_element(p8obj_asn, &size, cookie) != LBER_SEQUENCE) { rv = CKR_WRAPPED_KEY_INVALID; goto cleanup_asn2dhpri; } /* ... begin-sequence { */ (void) ber_scanf(p8obj_asn, "{"); if (ber_next_element(p8obj_asn, &size, cookie) != OID_TAG) { rv = CKR_WRAPPED_KEY_INVALID; goto cleanup_asn2dhpri; } /* ... OID, \* DH algorithm OID *\ */ if (size != sizeof (DH_OID)) { rv = CKR_FUNCTION_NOT_SUPPORTED; goto cleanup_asn2dhpri; } size = sizeof (oid); (void) ber_scanf(p8obj_asn, "s", oid, &size); if (memcmp(oid, DH_OID, size) != 0) { rv = CKR_FUNCTION_NOT_SUPPORTED; goto cleanup_asn2dhpri; } if (ber_next_element(p8obj_asn, &size, cookie) != LBER_SEQUENCE) { rv = CKR_WRAPPED_KEY_INVALID; goto cleanup_asn2dhpri; } /* ... begin-sequence { */ (void) ber_scanf(p8obj_asn, "{"); if (ber_next_element(p8obj_asn, &size, cookie) != LBER_INTEGER) { rv = CKR_WRAPPED_KEY_INVALID; goto cleanup_asn2dhpri; } /* ... prime, */ if (size > MAX_DH_KEY) { rv = CKR_FUNCTION_NOT_SUPPORTED; goto cleanup_asn2dhpri; } tmplen = size + 1; if ((tmp.big_value = malloc(tmplen)) == NULL) { rv = CKR_HOST_MEMORY; goto cleanup_asn2dhpri; } (void) ber_scanf(p8obj_asn, "s", tmp.big_value, &tmplen); tmp.big_value_len = tmplen; if ((rv = unpad_bigint_attr(tmp, &tmp_nopad)) != CKR_OK) { free(tmp.big_value); goto cleanup_asn2dhpri; } free(tmp.big_value); copy_bigint_attr(&tmp_nopad, KEY_PRI_DH_PRIME(keyp)); if (ber_next_element(p8obj_asn, &size, cookie) != LBER_INTEGER) { rv = CKR_WRAPPED_KEY_INVALID; goto error_asn2dhpri; } /* ... base } end-sequence } end-sequence */ if (size > MAX_DH_KEY) { rv = CKR_FUNCTION_NOT_SUPPORTED; goto error_asn2dhpri; } tmplen = size + 1; if ((tmp.big_value = malloc(tmplen)) == NULL) { rv = CKR_HOST_MEMORY; goto error_asn2dhpri; } (void) ber_scanf(p8obj_asn, "s", /* "s}}" ? */ tmp.big_value, &tmplen); tmp.big_value_len = tmplen; if ((rv = unpad_bigint_attr(tmp, &tmp_nopad)) != CKR_OK) { free(tmp.big_value); goto error_asn2dhpri; } free(tmp.big_value); copy_bigint_attr(&tmp_nopad, KEY_PRI_DH_BASE(keyp)); if (ber_next_element(p8obj_asn, &size, cookie) != LBER_OCTETSTRING) { rv = CKR_WRAPPED_KEY_INVALID; goto error_asn2dhpri; } /* ... DHPrivateKey } end-sequence */ key_octs.bv_len = size + 1; if ((key_octs.bv_val = malloc(size + 1)) == NULL) { rv = CKR_HOST_MEMORY; goto error_asn2dhpri; } (void) ber_scanf(p8obj_asn, "s", /* "s}" ? */ key_octs.bv_val, &key_octs.bv_len); /* Decode key octet string into softtoken key object. */ if ((key_asn = ber_init(&key_octs)) == NULLBER) { rv = CKR_GENERAL_ERROR; goto error_asn2dhpri; } if (ber_next_element(key_asn, &size, cookie) != LBER_INTEGER) { rv = CKR_WRAPPED_KEY_INVALID; goto error_asn2dhpri; } /* ... value } end-sequence */ if (size > MAX_DH_KEY) { rv = CKR_FUNCTION_NOT_SUPPORTED; goto error_asn2dhpri; } tmplen = size + 1; if ((tmp.big_value = malloc(tmplen)) == NULL) { rv = CKR_HOST_MEMORY; goto error_asn2dhpri; } (void) ber_scanf(key_asn, "s", /* "s}" ? */ tmp.big_value, &tmplen); tmp.big_value_len = tmplen; if ((rv = unpad_bigint_attr(tmp, &tmp_nopad)) != CKR_OK) { free(tmp.big_value); goto error_asn2dhpri; } free(tmp.big_value); copy_bigint_attr(&tmp_nopad, KEY_PRI_DH_VALUE(keyp)); goto cleanup_asn2dhpri; error_asn2dhpri: bigint_attr_cleanup(KEY_PRI_DH_PRIME(keyp)); bigint_attr_cleanup(KEY_PRI_DH_BASE(keyp)); bigint_attr_cleanup(KEY_PRI_DH_VALUE(keyp)); cleanup_asn2dhpri: freezero(tmp_nopad.big_value, tmp_nopad.big_value_len); if (p8obj_asn != NULLBER) ber_free(p8obj_asn, 1); if (key_octs.bv_val != NULL) free(key_octs.bv_val); if (key_asn != NULLBER) ber_free(key_asn, 1); return (rv); } /* Decode ASN.1 BER syntax into DH X9.42 private key. */ static CK_RV asn1_to_x942_dh_pri(private_key_obj_t *keyp, uchar_t *buf, ulong_t buf_len) { CK_RV rv = CKR_OK; BerValue p8obj_octs, key_octs; BerElement *p8obj_asn = NULLBER, *key_asn = NULLBER; ber_len_t size, tmplen; char *cookie; int version; uchar_t oid[sizeof (DH942_OID) + 1]; biginteger_t tmp, tmp_nopad = { NULL, 0 }; p8obj_octs.bv_val = (char *)buf; #ifdef _LP64 /* LINTED E_CAST_INT_TO_SMALL_INT */ p8obj_octs.bv_len = (ber_len_t)buf_len; #else p8obj_octs.bv_len = (ber_len_t)buf_len; #endif key_octs.bv_val = NULL; key_octs.bv_len = 0; /* Decode PKCS#8 object ASN.1, verifying it is DH X9.42 private key. */ if ((p8obj_asn = ber_init(&p8obj_octs)) == NULLBER) return (CKR_GENERAL_ERROR); /* PKCS#8 PrivateKeyInfo ... */ if (ber_first_element(p8obj_asn, &size, &cookie) != LBER_INTEGER) { rv = CKR_WRAPPED_KEY_INVALID; goto cleanup_asn2x942dhpri; } /* ... begin-sequence { version, */ (void) ber_scanf(p8obj_asn, "i", &version); /* "{i" ? */ if (ber_next_element(p8obj_asn, &size, cookie) != LBER_SEQUENCE) { rv = CKR_WRAPPED_KEY_INVALID; goto cleanup_asn2x942dhpri; } /* ... begin-sequence { */ (void) ber_scanf(p8obj_asn, "{"); if (ber_next_element(p8obj_asn, &size, cookie) != OID_TAG) { rv = CKR_WRAPPED_KEY_INVALID; goto cleanup_asn2x942dhpri; } /* ... OID, \* DH X9.42 algorithm OID *\ */ if (size != sizeof (DH942_OID)) { rv = CKR_FUNCTION_NOT_SUPPORTED; goto cleanup_asn2x942dhpri; } size = sizeof (oid); (void) ber_scanf(p8obj_asn, "s", oid, &size); if (memcmp(oid, DH942_OID, size) != 0) { rv = CKR_FUNCTION_NOT_SUPPORTED; goto cleanup_asn2x942dhpri; } if (ber_next_element(p8obj_asn, &size, cookie) != LBER_SEQUENCE) { rv = CKR_WRAPPED_KEY_INVALID; goto cleanup_asn2x942dhpri; } /* ... begin-sequence { */ (void) ber_scanf(p8obj_asn, "{"); if (ber_next_element(p8obj_asn, &size, cookie) != LBER_INTEGER) { rv = CKR_WRAPPED_KEY_INVALID; goto cleanup_asn2x942dhpri; } /* ... prime, */ if (size > MAX_DH942_KEY) { rv = CKR_FUNCTION_NOT_SUPPORTED; goto cleanup_asn2x942dhpri; } tmplen = size + 1; if ((tmp.big_value = malloc(tmplen)) == NULL) { rv = CKR_HOST_MEMORY; goto cleanup_asn2x942dhpri; } (void) ber_scanf(p8obj_asn, "s", tmp.big_value, &tmplen); tmp.big_value_len = tmplen; if ((rv = unpad_bigint_attr(tmp, &tmp_nopad)) != CKR_OK) { free(tmp.big_value); goto cleanup_asn2x942dhpri; } free(tmp.big_value); copy_bigint_attr(&tmp_nopad, KEY_PRI_DH942_PRIME(keyp)); if (ber_next_element(p8obj_asn, &size, cookie) != LBER_INTEGER) { rv = CKR_WRAPPED_KEY_INVALID; goto error_asn2x942dhpri; } /* ... base, */ if (size > MAX_DH942_KEY) { rv = CKR_FUNCTION_NOT_SUPPORTED; goto error_asn2x942dhpri; } tmplen = size + 1; if ((tmp.big_value = malloc(tmplen)) == NULL) { rv = CKR_HOST_MEMORY; goto error_asn2x942dhpri; } (void) ber_scanf(p8obj_asn, "s", tmp.big_value, &tmplen); tmp.big_value_len = tmplen; if ((rv = unpad_bigint_attr(tmp, &tmp_nopad)) != CKR_OK) { free(tmp.big_value); goto error_asn2x942dhpri; } free(tmp.big_value); copy_bigint_attr(&tmp_nopad, KEY_PRI_DH942_BASE(keyp)); if (ber_next_element(p8obj_asn, &size, cookie) != LBER_INTEGER) { rv = CKR_WRAPPED_KEY_INVALID; goto error_asn2x942dhpri; } /* ... subprime } end-sequence } end-sequence */ if (size > MAX_DH942_KEY) { rv = CKR_FUNCTION_NOT_SUPPORTED; goto error_asn2x942dhpri; } tmplen = size + 1; if ((tmp.big_value = malloc(tmplen)) == NULL) { rv = CKR_HOST_MEMORY; goto error_asn2x942dhpri; } (void) ber_scanf(p8obj_asn, "s", /* "s}}" ? */ tmp.big_value, &tmplen); tmp.big_value_len = tmplen; if ((rv = unpad_bigint_attr(tmp, &tmp_nopad)) != CKR_OK) { free(tmp.big_value); goto error_asn2x942dhpri; } free(tmp.big_value); copy_bigint_attr(&tmp_nopad, KEY_PRI_DH942_SUBPRIME(keyp)); if (ber_next_element(p8obj_asn, &size, cookie) != LBER_OCTETSTRING) { rv = CKR_WRAPPED_KEY_INVALID; goto error_asn2x942dhpri; } /* ... DHPrivateKey } end-sequence */ key_octs.bv_len = size + 1; if ((key_octs.bv_val = malloc(size + 1)) == NULL) { rv = CKR_HOST_MEMORY; goto error_asn2x942dhpri; } (void) ber_scanf(p8obj_asn, "s", /* "s}" ? */ key_octs.bv_val, &key_octs.bv_len); /* Decode key octet string into softtoken key object. */ if ((key_asn = ber_init(&key_octs)) == NULLBER) { rv = CKR_GENERAL_ERROR; goto error_asn2x942dhpri; } if (ber_next_element(key_asn, &size, cookie) != LBER_INTEGER) { rv = CKR_WRAPPED_KEY_INVALID; goto error_asn2x942dhpri; } /* ... value } end-sequence */ if (size > MAX_DH942_KEY) { rv = CKR_FUNCTION_NOT_SUPPORTED; goto error_asn2x942dhpri; } tmplen = size + 1; if ((tmp.big_value = malloc(tmplen)) == NULL) { rv = CKR_HOST_MEMORY; goto error_asn2x942dhpri; } (void) ber_scanf(key_asn, "s", /* "s}" ? */ tmp.big_value, &tmplen); tmp.big_value_len = tmplen; if ((rv = unpad_bigint_attr(tmp, &tmp_nopad)) != CKR_OK) { free(tmp.big_value); goto error_asn2x942dhpri; } free(tmp.big_value); copy_bigint_attr(&tmp_nopad, KEY_PRI_DH942_VALUE(keyp)); goto cleanup_asn2x942dhpri; error_asn2x942dhpri: bigint_attr_cleanup(KEY_PRI_DH942_PRIME(keyp)); bigint_attr_cleanup(KEY_PRI_DH942_BASE(keyp)); bigint_attr_cleanup(KEY_PRI_DH942_SUBPRIME(keyp)); bigint_attr_cleanup(KEY_PRI_DH942_VALUE(keyp)); cleanup_asn2x942dhpri: freezero(tmp_nopad.big_value, tmp_nopad.big_value_len); if (p8obj_asn != NULLBER) ber_free(p8obj_asn, 1); if (key_octs.bv_val != NULL) free(key_octs.bv_val); if (key_asn != NULLBER) ber_free(key_asn, 1); return (rv); } /* * Decode the object key from ASN.1 format into soft_object_t. */ CK_RV soft_asn1_to_object(soft_object_t *objp, uchar_t *buf, ulong_t buf_len) { CK_RV rv = CKR_OK; CK_OBJECT_CLASS class = objp->class; CK_KEY_TYPE keytype = objp->key_type; private_key_obj_t *pvk; switch (class) { case CKO_PRIVATE_KEY: /* Allocate storage for Private Key Object. */ if ((pvk = calloc(1, sizeof (private_key_obj_t))) == NULL) { rv = CKR_HOST_MEMORY; return (rv); } switch (keytype) { case CKK_RSA: rv = asn1_to_rsa_pri(pvk, buf, buf_len); break; case CKK_DSA: rv = asn1_to_dsa_pri(pvk, buf, buf_len); break; case CKK_DH: rv = asn1_to_dh_pri(pvk, buf, buf_len); break; case CKK_X9_42_DH: rv = asn1_to_x942_dh_pri(pvk, buf, buf_len); break; default: rv = CKR_FUNCTION_NOT_SUPPORTED; break; } /* keytype */ if (rv != CKR_OK) free(pvk); else objp->object_class_u.private_key = pvk; break; default: rv = CKR_FUNCTION_NOT_SUPPORTED; break; } /* class */ return (rv); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SOFTASN1_H #define _SOFTASN1_H #ifdef __cplusplus extern "C" { #endif #include #include "softObject.h" #define SOFT_ASN_VERSION 0x00 /* * Function Prototypes. */ CK_RV soft_object_to_asn1(soft_object_t *, uchar_t *, ulong_t *); CK_RV soft_asn1_to_object(soft_object_t *, uchar_t *, ulong_t); #ifdef __cplusplus } #endif #endif /* _SOFTASN1_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2012 Milan Jurik. All rights reserved. * Copyright (c) 2018, Joyent, Inc. */ #include #include #include #include #include #include #include #include #include #include #include "softGlobal.h" #include "softObject.h" #include "softSession.h" #include "softKeystore.h" #include "softKeystoreUtil.h" #include "softCrypt.h" /* * This attribute table is used by the soft_lookup_attr() * to validate the attributes. */ CK_ATTRIBUTE_TYPE attr_map[] = { CKA_PRIVATE, CKA_LABEL, CKA_APPLICATION, CKA_OBJECT_ID, CKA_CERTIFICATE_TYPE, CKA_ISSUER, CKA_SERIAL_NUMBER, CKA_AC_ISSUER, CKA_OWNER, CKA_ATTR_TYPES, CKA_SUBJECT, CKA_ID, CKA_SENSITIVE, CKA_START_DATE, CKA_END_DATE, CKA_MODULUS, CKA_MODULUS_BITS, CKA_PUBLIC_EXPONENT, CKA_PRIVATE_EXPONENT, CKA_PRIME_1, CKA_PRIME_2, CKA_EXPONENT_1, CKA_EXPONENT_2, CKA_COEFFICIENT, CKA_PRIME, CKA_SUBPRIME, CKA_BASE, CKA_EXTRACTABLE, CKA_LOCAL, CKA_NEVER_EXTRACTABLE, CKA_ALWAYS_SENSITIVE, CKA_MODIFIABLE, CKA_ECDSA_PARAMS, CKA_EC_PARAMS, CKA_EC_POINT, CKA_SECONDARY_AUTH, CKA_AUTH_PIN_FLAGS, CKA_HW_FEATURE_TYPE, CKA_RESET_ON_INIT, CKA_HAS_RESET }; /* * attributes that exists only in public key objects * Note: some attributes may also exist in one or two * other object classes, but they are also listed * because not all object have them. */ CK_ATTRIBUTE_TYPE PUB_KEY_ATTRS[] = { CKA_SUBJECT, CKA_ENCRYPT, CKA_WRAP, CKA_VERIFY, CKA_VERIFY_RECOVER, CKA_MODULUS, CKA_MODULUS_BITS, CKA_PUBLIC_EXPONENT, CKA_PRIME, CKA_SUBPRIME, CKA_BASE, CKA_TRUSTED, CKA_ECDSA_PARAMS, CKA_EC_PARAMS, CKA_EC_POINT }; /* * attributes that exists only in private key objects * Note: some attributes may also exist in one or two * other object classes, but they are also listed * because not all object have them. */ CK_ATTRIBUTE_TYPE PRIV_KEY_ATTRS[] = { CKA_DECRYPT, CKA_UNWRAP, CKA_SIGN, CKA_SIGN_RECOVER, CKA_MODULUS, CKA_PUBLIC_EXPONENT, CKA_PRIVATE_EXPONENT, CKA_PRIME, CKA_SUBPRIME, CKA_BASE, CKA_PRIME_1, CKA_PRIME_2, CKA_EXPONENT_1, CKA_EXPONENT_2, CKA_COEFFICIENT, CKA_VALUE_BITS, CKA_SUBJECT, CKA_SENSITIVE, CKA_EXTRACTABLE, CKA_NEVER_EXTRACTABLE, CKA_ALWAYS_SENSITIVE, CKA_EC_PARAMS }; /* * attributes that exists only in secret key objects * Note: some attributes may also exist in one or two * other object classes, but they are also listed * because not all object have them. */ CK_ATTRIBUTE_TYPE SECRET_KEY_ATTRS[] = { CKA_VALUE_LEN, CKA_ENCRYPT, CKA_DECRYPT, CKA_WRAP, CKA_UNWRAP, CKA_SIGN, CKA_VERIFY, CKA_SENSITIVE, CKA_EXTRACTABLE, CKA_NEVER_EXTRACTABLE, CKA_ALWAYS_SENSITIVE }; /* * attributes that exists only in domain parameter objects * Note: some attributes may also exist in one or two * other object classes, but they are also listed * because not all object have them. */ CK_ATTRIBUTE_TYPE DOMAIN_ATTRS[] = { CKA_PRIME, CKA_SUBPRIME, CKA_BASE, CKA_PRIME_BITS, CKA_SUBPRIME_BITS, CKA_SUB_PRIME_BITS }; /* * attributes that exists only in hardware feature objects * */ CK_ATTRIBUTE_TYPE HARDWARE_ATTRS[] = { CKA_HW_FEATURE_TYPE, CKA_RESET_ON_INIT, CKA_HAS_RESET }; /* * attributes that exists only in certificate objects */ CK_ATTRIBUTE_TYPE CERT_ATTRS[] = { CKA_CERTIFICATE_TYPE, CKA_TRUSTED, CKA_SUBJECT, CKA_ID, CKA_ISSUER, CKA_AC_ISSUER, CKA_SERIAL_NUMBER, CKA_OWNER, CKA_ATTR_TYPES }; /* * Validate the attribute by using binary search algorithm. */ CK_RV soft_lookup_attr(CK_ATTRIBUTE_TYPE type) { size_t lower, middle, upper; lower = 0; upper = (sizeof (attr_map) / sizeof (CK_ATTRIBUTE_TYPE)) - 1; while (lower <= upper) { /* Always starts from middle. */ middle = (lower + upper) / 2; if (type > attr_map[middle]) { /* Adjust the lower bound to upper half. */ lower = middle + 1; continue; } if (type == attr_map[middle]) { /* Found it. */ return (CKR_OK); } if (type < attr_map[middle]) { /* Adjust the upper bound to lower half. */ upper = middle - 1; continue; } } /* Failed to find the matching attribute from the attribute table. */ return (CKR_ATTRIBUTE_TYPE_INVALID); } /* * Validate the attribute by using the following search algorithm: * * 1) Search for the most frequently used attributes first. * 2) If not found, search for the usage-purpose attributes - these * attributes have dense set of values, therefore compiler will * optimize it with a branch table and branch to the appropriate * case. * 3) If still not found, use binary search for the rest of the * attributes in the attr_map[] table. */ CK_RV soft_validate_attr(CK_ATTRIBUTE_PTR template, CK_ULONG ulAttrNum, CK_OBJECT_CLASS *class) { CK_ULONG i; CK_RV rv = CKR_OK; for (i = 0; i < ulAttrNum; i++) { /* First tier search */ switch (template[i].type) { case CKA_CLASS: *class = *((CK_OBJECT_CLASS*)template[i].pValue); break; case CKA_TOKEN: break; case CKA_KEY_TYPE: break; case CKA_VALUE: break; case CKA_VALUE_LEN: break; case CKA_VALUE_BITS: break; default: /* Second tier search */ switch (template[i].type) { case CKA_ENCRYPT: break; case CKA_DECRYPT: break; case CKA_WRAP: break; case CKA_UNWRAP: break; case CKA_SIGN: break; case CKA_SIGN_RECOVER: break; case CKA_VERIFY: break; case CKA_VERIFY_RECOVER: break; case CKA_DERIVE: break; default: /* Third tier search */ rv = soft_lookup_attr(template[i].type); if (rv != CKR_OK) return (rv); break; } break; } } return (rv); } static void cleanup_cert_attr(cert_attr_t *attr) { if (attr != NULL) { freezero(attr->value, attr->length); attr->value = NULL; attr->length = 0; } } static CK_RV copy_cert_attr(cert_attr_t *src_attr, cert_attr_t **dest_attr) { CK_RV rv = CKR_OK; if (src_attr == NULL || dest_attr == NULL) return (CKR_HOST_MEMORY); if (src_attr->value == NULL) return (CKR_HOST_MEMORY); /* free memory if its already allocated */ if (*dest_attr != NULL) { cleanup_cert_attr(*dest_attr); } else { *dest_attr = malloc(sizeof (cert_attr_t)); if (*dest_attr == NULL) return (CKR_HOST_MEMORY); } (*dest_attr)->value = NULL; (*dest_attr)->length = 0; if (src_attr->length) { (*dest_attr)->value = malloc(src_attr->length); if ((*dest_attr)->value == NULL) { free(*dest_attr); return (CKR_HOST_MEMORY); } (void) memcpy((*dest_attr)->value, src_attr->value, src_attr->length); (*dest_attr)->length = src_attr->length; } return (rv); } void soft_cleanup_cert_object(soft_object_t *object_p) { CK_CERTIFICATE_TYPE certtype = object_p->cert_type; if (object_p->class != CKO_CERTIFICATE || OBJ_CERT(object_p) == NULL) return; if (certtype == CKC_X_509) { if (X509_CERT_SUBJECT(object_p) != NULL) { cleanup_cert_attr(X509_CERT_SUBJECT(object_p)); free(X509_CERT_SUBJECT(object_p)); X509_CERT_SUBJECT(object_p) = NULL; } if (X509_CERT_VALUE(object_p) != NULL) { cleanup_cert_attr(X509_CERT_VALUE(object_p)); free(X509_CERT_VALUE(object_p)); X509_CERT_VALUE(object_p) = NULL; } free(OBJ_CERT(object_p)); } else if (certtype == CKC_X_509_ATTR_CERT) { if (X509_ATTR_CERT_VALUE(object_p) != NULL) { cleanup_cert_attr(X509_ATTR_CERT_VALUE(object_p)); free(X509_ATTR_CERT_VALUE(object_p)); X509_ATTR_CERT_VALUE(object_p) = NULL; } if (X509_ATTR_CERT_OWNER(object_p) != NULL) { cleanup_cert_attr(X509_ATTR_CERT_OWNER(object_p)); free(X509_ATTR_CERT_OWNER(object_p)); X509_ATTR_CERT_OWNER(object_p) = NULL; } free(OBJ_CERT(object_p)); } } /* * Clean up and release all the storage in the extra attribute list * of an object. */ void soft_cleanup_extra_attr(soft_object_t *object_p) { CK_ATTRIBUTE_INFO_PTR extra_attr; CK_ATTRIBUTE_INFO_PTR tmp; extra_attr = object_p->extra_attrlistp; while (extra_attr) { tmp = extra_attr->next; if (extra_attr->attr.pValue != NULL) { /* * All extra attributes in the extra attribute * list have pValue points to the value of the * attribute (with simple byte array type). * Free the storage for the value of the attribute. */ freezero(extra_attr->attr.pValue, extra_attr->attr.ulValueLen); } /* Free the storage for the attribute_info struct. */ free(extra_attr); extra_attr = tmp; } object_p->extra_attrlistp = NULL; } /* * Create the attribute_info struct to hold the object's attribute, * and add it to the extra attribute list of an object. */ CK_RV soft_add_extra_attr(CK_ATTRIBUTE_PTR template, soft_object_t *object_p) { CK_ATTRIBUTE_INFO_PTR attrp; /* Allocate the storage for the attribute_info struct. */ attrp = calloc(1, sizeof (attribute_info_t)); if (attrp == NULL) { return (CKR_HOST_MEMORY); } /* Set up attribute_info struct. */ attrp->attr.type = template->type; attrp->attr.ulValueLen = template->ulValueLen; if ((template->pValue != NULL) && (template->ulValueLen > 0)) { /* Allocate storage for the value of the attribute. */ attrp->attr.pValue = malloc(template->ulValueLen); if (attrp->attr.pValue == NULL) { free(attrp); return (CKR_HOST_MEMORY); } (void) memcpy(attrp->attr.pValue, template->pValue, template->ulValueLen); } else { attrp->attr.pValue = NULL; } /* Insert the new attribute in front of extra attribute list. */ if (object_p->extra_attrlistp == NULL) { object_p->extra_attrlistp = attrp; attrp->next = NULL; } else { attrp->next = object_p->extra_attrlistp; object_p->extra_attrlistp = attrp; } return (CKR_OK); } CK_RV soft_copy_certificate(certificate_obj_t *oldcert, certificate_obj_t **newcert, CK_CERTIFICATE_TYPE type) { CK_RV rv = CKR_OK; certificate_obj_t *cert; x509_cert_t x509; x509_attr_cert_t x509_attr; cert = calloc(1, sizeof (certificate_obj_t)); if (cert == NULL) { return (CKR_HOST_MEMORY); } if (type == CKC_X_509) { x509 = oldcert->cert_type_u.x509; if (x509.subject) if ((rv = copy_cert_attr(x509.subject, &cert->cert_type_u.x509.subject))) return (rv); if (x509.value) if ((rv = copy_cert_attr(x509.value, &cert->cert_type_u.x509.value))) return (rv); } else if (type == CKC_X_509_ATTR_CERT) { x509_attr = oldcert->cert_type_u.x509_attr; if (x509_attr.owner) if ((rv = copy_cert_attr(x509_attr.owner, &cert->cert_type_u.x509_attr.owner))) return (rv); if (x509_attr.value) if ((rv = copy_cert_attr(x509_attr.value, &cert->cert_type_u.x509_attr.value))) return (rv); } else { /* wrong certificate type */ rv = CKR_ATTRIBUTE_TYPE_INVALID; } if (rv == CKR_OK) *newcert = cert; return (rv); } /* * Copy the attribute_info struct from the old object to a new attribute_info * struct, and add that new struct to the extra attribute list of the new * object. */ CK_RV soft_copy_extra_attr(CK_ATTRIBUTE_INFO_PTR old_attrp, soft_object_t *object_p) { CK_ATTRIBUTE_INFO_PTR attrp; /* Allocate attribute_info struct. */ attrp = calloc(1, sizeof (attribute_info_t)); if (attrp == NULL) { return (CKR_HOST_MEMORY); } attrp->attr.type = old_attrp->attr.type; attrp->attr.ulValueLen = old_attrp->attr.ulValueLen; if ((old_attrp->attr.pValue != NULL) && (old_attrp->attr.ulValueLen > 0)) { attrp->attr.pValue = malloc(old_attrp->attr.ulValueLen); if (attrp->attr.pValue == NULL) { free(attrp); return (CKR_HOST_MEMORY); } (void) memcpy(attrp->attr.pValue, old_attrp->attr.pValue, old_attrp->attr.ulValueLen); } else { attrp->attr.pValue = NULL; } /* Insert the new attribute in front of extra attribute list */ if (object_p->extra_attrlistp == NULL) { object_p->extra_attrlistp = attrp; attrp->next = NULL; } else { attrp->next = object_p->extra_attrlistp; object_p->extra_attrlistp = attrp; } return (CKR_OK); } /* * Get the attribute triple from the extra attribute list in the object * (if the specified attribute type is found), and copy it to a template. * Note the type of the attribute to be copied is specified by the template, * and the storage is pre-allocated for the atrribute value in the template * for doing the copy. */ CK_RV get_extra_attr_from_object(soft_object_t *object_p, CK_ATTRIBUTE_PTR template) { CK_ATTRIBUTE_INFO_PTR extra_attr; CK_ATTRIBUTE_TYPE type = template->type; extra_attr = object_p->extra_attrlistp; while (extra_attr) { if (type == extra_attr->attr.type) { /* Found it. */ break; } else { /* Does not match, try next one. */ extra_attr = extra_attr->next; } } if (extra_attr == NULL) { /* A valid but un-initialized attribute. */ template->ulValueLen = 0; return (CKR_OK); } /* * We found the attribute in the extra attribute list. */ if (template->pValue == NULL) { template->ulValueLen = extra_attr->attr.ulValueLen; return (CKR_OK); } if (template->ulValueLen >= extra_attr->attr.ulValueLen) { /* * The buffer provided by the application is large * enough to hold the value of the attribute. */ (void) memcpy(template->pValue, extra_attr->attr.pValue, extra_attr->attr.ulValueLen); template->ulValueLen = extra_attr->attr.ulValueLen; return (CKR_OK); } else { /* * The buffer provided by the application does * not have enough space to hold the value. */ template->ulValueLen = (CK_ULONG)-1; return (CKR_BUFFER_TOO_SMALL); } } /* * Modify the attribute triple in the extra attribute list of the object * if the specified attribute type is found. Otherwise, just add it to * list. */ CK_RV set_extra_attr_to_object(soft_object_t *object_p, CK_ATTRIBUTE_TYPE type, CK_ATTRIBUTE_PTR template) { CK_ATTRIBUTE_INFO_PTR extra_attr; extra_attr = object_p->extra_attrlistp; while (extra_attr) { if (type == extra_attr->attr.type) { /* Found it. */ break; } else { /* Does not match, try next one. */ extra_attr = extra_attr->next; } } if (extra_attr == NULL) { /* * This attribute is a new one, go ahead adding it to * the extra attribute list. */ return (soft_add_extra_attr(template, object_p)); } /* We found the attribute in the extra attribute list. */ if ((template->pValue != NULL) && (template->ulValueLen > 0)) { if (template->ulValueLen > extra_attr->attr.ulValueLen) { /* The old buffer is too small to hold the new value. */ if (extra_attr->attr.pValue != NULL) { /* Free storage for the old attribute value. */ freezero(extra_attr->attr.pValue, extra_attr->attr.ulValueLen); } /* Allocate storage for the new attribute value. */ extra_attr->attr.pValue = malloc(template->ulValueLen); if (extra_attr->attr.pValue == NULL) { return (CKR_HOST_MEMORY); } } /* Replace the attribute with new value. */ extra_attr->attr.ulValueLen = template->ulValueLen; (void) memcpy(extra_attr->attr.pValue, template->pValue, template->ulValueLen); } else { extra_attr->attr.pValue = NULL; } return (CKR_OK); } /* * Copy the big integer attribute value from template to a biginteger_t struct. */ CK_RV get_bigint_attr_from_template(biginteger_t *big, CK_ATTRIBUTE_PTR template) { if ((template->pValue != NULL) && (template->ulValueLen > 0)) { /* Allocate storage for the value of the attribute. */ big->big_value = malloc(template->ulValueLen); if (big->big_value == NULL) { return (CKR_HOST_MEMORY); } (void) memcpy(big->big_value, template->pValue, template->ulValueLen); big->big_value_len = template->ulValueLen; } else { big->big_value = NULL; big->big_value_len = 0; } return (CKR_OK); } /* * Copy the big integer attribute value from a biginteger_t struct in the * object to a template. */ CK_RV get_bigint_attr_from_object(biginteger_t *big, CK_ATTRIBUTE_PTR template) { if (template->pValue == NULL) { template->ulValueLen = big->big_value_len; return (CKR_OK); } if (big->big_value == NULL) { template->ulValueLen = 0; return (CKR_OK); } if (template->ulValueLen >= big->big_value_len) { /* * The buffer provided by the application is large * enough to hold the value of the attribute. */ (void) memcpy(template->pValue, big->big_value, big->big_value_len); template->ulValueLen = big->big_value_len; return (CKR_OK); } else { /* * The buffer provided by the application does * not have enough space to hold the value. */ template->ulValueLen = (CK_ULONG)-1; return (CKR_BUFFER_TOO_SMALL); } } /* * Copy the boolean data type attribute value from an object for the * specified attribute to the template. */ CK_RV get_bool_attr_from_object(soft_object_t *object_p, CK_ULONG bool_flag, CK_ATTRIBUTE_PTR template) { if (template->pValue == NULL) { template->ulValueLen = sizeof (CK_BBOOL); return (CKR_OK); } if (template->ulValueLen >= sizeof (CK_BBOOL)) { /* * The buffer provided by the application is large * enough to hold the value of the attribute. */ if (object_p->bool_attr_mask & bool_flag) { *((CK_BBOOL *)template->pValue) = B_TRUE; } else { *((CK_BBOOL *)template->pValue) = B_FALSE; } template->ulValueLen = sizeof (CK_BBOOL); return (CKR_OK); } else { /* * The buffer provided by the application does * not have enough space to hold the value. */ template->ulValueLen = (CK_ULONG)-1; return (CKR_BUFFER_TOO_SMALL); } } /* * Set the boolean data type attribute value in the object. */ CK_RV set_bool_attr_to_object(soft_object_t *object_p, CK_ULONG bool_flag, CK_ATTRIBUTE_PTR template) { if (*(CK_BBOOL *)template->pValue) object_p->bool_attr_mask |= bool_flag; else object_p->bool_attr_mask &= ~bool_flag; return (CKR_OK); } /* * Copy the CK_ULONG data type attribute value from an object to the * template. */ CK_RV get_ulong_attr_from_object(CK_ULONG value, CK_ATTRIBUTE_PTR template) { if (template->pValue == NULL) { template->ulValueLen = sizeof (CK_ULONG); return (CKR_OK); } if (template->ulValueLen >= sizeof (CK_ULONG)) { /* * The buffer provided by the application is large * enough to hold the value of the attribute. * It is also assumed to be correctly aligned. */ *(CK_ULONG_PTR)template->pValue = value; template->ulValueLen = sizeof (CK_ULONG); return (CKR_OK); } else { /* * The buffer provided by the application does * not have enough space to hold the value. */ template->ulValueLen = (CK_ULONG)-1; return (CKR_BUFFER_TOO_SMALL); } } /* * Copy the CK_ULONG data type attribute value from a template to the * object. */ static CK_RV get_ulong_attr_from_template(CK_ULONG *value, CK_ATTRIBUTE_PTR template) { if (template->ulValueLen < sizeof (CK_ULONG)) return (CKR_ATTRIBUTE_VALUE_INVALID); if (template->pValue != NULL) { *value = *(CK_ULONG_PTR)template->pValue; } else { *value = 0; } return (CKR_OK); } /* * Copy the big integer attribute value from source's biginteger_t to * destination's biginteger_t. */ void copy_bigint_attr(biginteger_t *src, biginteger_t *dst) { if ((src->big_value != NULL) && (src->big_value_len > 0)) { /* * To do the copy, just have dst's big_value points * to src's. */ dst->big_value = src->big_value; dst->big_value_len = src->big_value_len; /* * After the copy, nullify the src's big_value pointer. * It prevents any double freeing the value. */ src->big_value = NULL; src->big_value_len = 0; } else { dst->big_value = NULL; dst->big_value_len = 0; } } CK_RV get_string_from_template(CK_ATTRIBUTE_PTR dest, CK_ATTRIBUTE_PTR src) { if ((src->pValue != NULL) && (src->ulValueLen > 0)) { /* Allocate storage for the value of the attribute. */ dest->pValue = malloc(src->ulValueLen); if (dest->pValue == NULL) { return (CKR_HOST_MEMORY); } (void) memcpy(dest->pValue, src->pValue, src->ulValueLen); dest->ulValueLen = src->ulValueLen; dest->type = src->type; } else { dest->pValue = NULL; dest->ulValueLen = 0; dest->type = src->type; } return (CKR_OK); } CK_RV get_cert_attr_from_template(cert_attr_t **dest, CK_ATTRIBUTE_PTR src) { if (src->pValue != NULL && src->ulValueLen > 0) { /* * If the attribute was already set, clear out the * existing value and release the memory. */ if (*dest != NULL) { cleanup_cert_attr(*dest); } else { *dest = malloc(sizeof (cert_attr_t)); if (*dest == NULL) { return (CKR_HOST_MEMORY); } (void) memset(*dest, 0, sizeof (cert_attr_t)); } (*dest)->value = malloc(src->ulValueLen); if ((*dest)->value == NULL) { free(*dest); *dest = NULL; return (CKR_HOST_MEMORY); } (void) memcpy((*dest)->value, src->pValue, src->ulValueLen); (*dest)->length = src->ulValueLen; } return (CKR_OK); } /* * Copy the certificate attribute information to the template. * If the template attribute is not big enough, set the ulValueLen=-1 * and return CKR_BUFFER_TOO_SMALL. */ static CK_RV get_cert_attr_from_object(cert_attr_t *src, CK_ATTRIBUTE_PTR template) { if (template->pValue == NULL) { template->ulValueLen = src->length; return (CKR_OK); } else if (template->ulValueLen >= src->length) { /* * The buffer provided by the application is large * enough to hold the value of the attribute. */ (void) memcpy(template->pValue, src->value, src->length); template->ulValueLen = src->length; return (CKR_OK); } else { /* * The buffer provided by the application does * not have enough space to hold the value. */ template->ulValueLen = (CK_ULONG)-1; return (CKR_BUFFER_TOO_SMALL); } } void string_attr_cleanup(CK_ATTRIBUTE_PTR template) { freezero(template->pValue, template->ulValueLen); template->pValue = NULL; template->ulValueLen = 0; } /* * Release the storage allocated for object attribute with big integer * value. */ void bigint_attr_cleanup(biginteger_t *big) { if (big == NULL) return; freezero(big->big_value, big->big_value_len); big->big_value = NULL; big->big_value_len = 0; } /* * Clean up and release all the storage allocated to hold the big integer * attributes associated with the type (i.e. class) of the object. Also, * release the storage allocated to the type of the object. */ void soft_cleanup_object_bigint_attrs(soft_object_t *object_p) { CK_OBJECT_CLASS class = object_p->class; CK_KEY_TYPE keytype = object_p->key_type; switch (class) { case CKO_PUBLIC_KEY: if (OBJ_PUB(object_p)) { switch (keytype) { case CKK_RSA: bigint_attr_cleanup(OBJ_PUB_RSA_MOD( object_p)); bigint_attr_cleanup(OBJ_PUB_RSA_PUBEXPO( object_p)); break; case CKK_DSA: bigint_attr_cleanup(OBJ_PUB_DSA_PRIME( object_p)); bigint_attr_cleanup(OBJ_PUB_DSA_SUBPRIME( object_p)); bigint_attr_cleanup(OBJ_PUB_DSA_BASE( object_p)); bigint_attr_cleanup(OBJ_PUB_DSA_VALUE( object_p)); break; case CKK_DH: bigint_attr_cleanup(OBJ_PUB_DH_PRIME( object_p)); bigint_attr_cleanup(OBJ_PUB_DH_BASE( object_p)); bigint_attr_cleanup(OBJ_PUB_DH_VALUE( object_p)); break; case CKK_X9_42_DH: bigint_attr_cleanup(OBJ_PUB_DH942_PRIME( object_p)); bigint_attr_cleanup(OBJ_PUB_DH942_BASE( object_p)); bigint_attr_cleanup(OBJ_PUB_DH942_SUBPRIME( object_p)); bigint_attr_cleanup(OBJ_PUB_DH942_VALUE( object_p)); break; case CKK_EC: bigint_attr_cleanup(OBJ_PUB_EC_POINT( object_p)); break; } /* Release Public Key Object struct */ free(OBJ_PUB(object_p)); OBJ_PUB(object_p) = NULL; } break; case CKO_PRIVATE_KEY: if (OBJ_PRI(object_p)) { switch (keytype) { case CKK_RSA: bigint_attr_cleanup(OBJ_PRI_RSA_MOD( object_p)); bigint_attr_cleanup(OBJ_PRI_RSA_PUBEXPO( object_p)); bigint_attr_cleanup(OBJ_PRI_RSA_PRIEXPO( object_p)); bigint_attr_cleanup(OBJ_PRI_RSA_PRIME1( object_p)); bigint_attr_cleanup(OBJ_PRI_RSA_PRIME2( object_p)); bigint_attr_cleanup(OBJ_PRI_RSA_EXPO1( object_p)); bigint_attr_cleanup(OBJ_PRI_RSA_EXPO2( object_p)); bigint_attr_cleanup(OBJ_PRI_RSA_COEF( object_p)); break; case CKK_DSA: bigint_attr_cleanup(OBJ_PRI_DSA_PRIME( object_p)); bigint_attr_cleanup(OBJ_PRI_DSA_SUBPRIME( object_p)); bigint_attr_cleanup(OBJ_PRI_DSA_BASE( object_p)); bigint_attr_cleanup(OBJ_PRI_DSA_VALUE( object_p)); break; case CKK_DH: bigint_attr_cleanup(OBJ_PRI_DH_PRIME( object_p)); bigint_attr_cleanup(OBJ_PRI_DH_BASE( object_p)); bigint_attr_cleanup(OBJ_PRI_DH_VALUE( object_p)); break; case CKK_X9_42_DH: bigint_attr_cleanup(OBJ_PRI_DH942_PRIME( object_p)); bigint_attr_cleanup(OBJ_PRI_DH942_BASE( object_p)); bigint_attr_cleanup(OBJ_PRI_DH942_SUBPRIME( object_p)); bigint_attr_cleanup(OBJ_PRI_DH942_VALUE( object_p)); break; case CKK_EC: bigint_attr_cleanup(OBJ_PRI_EC_VALUE( object_p)); break; } /* Release Private Key Object struct. */ free(OBJ_PRI(object_p)); OBJ_PRI(object_p) = NULL; } break; case CKO_SECRET_KEY: if (OBJ_SEC(object_p)) { /* cleanup key data area */ if (OBJ_SEC_VALUE(object_p) != NULL && OBJ_SEC_VALUE_LEN(object_p) > 0) { freezero(OBJ_SEC_VALUE(object_p), OBJ_SEC_VALUE_LEN(object_p)); } /* cleanup key schedule data area */ if (OBJ_KEY_SCHED(object_p) != NULL && OBJ_KEY_SCHED_LEN(object_p) > 0) { freezero(OBJ_KEY_SCHED(object_p), OBJ_KEY_SCHED_LEN(object_p)); } /* Release Secret Key Object struct. */ free(OBJ_SEC(object_p)); OBJ_SEC(object_p) = NULL; } break; case CKO_DOMAIN_PARAMETERS: if (OBJ_DOM(object_p)) { switch (keytype) { case CKK_DSA: bigint_attr_cleanup(OBJ_DOM_DSA_PRIME( object_p)); bigint_attr_cleanup(OBJ_DOM_DSA_SUBPRIME( object_p)); bigint_attr_cleanup(OBJ_DOM_DSA_BASE( object_p)); break; case CKK_DH: bigint_attr_cleanup(OBJ_DOM_DH_PRIME( object_p)); bigint_attr_cleanup(OBJ_DOM_DH_BASE( object_p)); break; case CKK_X9_42_DH: bigint_attr_cleanup(OBJ_DOM_DH942_PRIME( object_p)); bigint_attr_cleanup(OBJ_DOM_DH942_BASE( object_p)); bigint_attr_cleanup(OBJ_DOM_DH942_SUBPRIME( object_p)); break; } /* Release Domain Parameters Object struct. */ free(OBJ_DOM(object_p)); OBJ_DOM(object_p) = NULL; } break; } } /* * Parse the common attributes. Return to caller with appropriate return * value to indicate if the supplied template specifies a valid attribute * with a valid value. */ CK_RV soft_parse_common_attrs(CK_ATTRIBUTE_PTR template, uchar_t *object_type) { CK_RV rv = CKR_OK; switch (template->type) { case CKA_CLASS: break; /* default boolean attributes */ case CKA_TOKEN: if ((*(CK_BBOOL *)template->pValue) == B_TRUE) { if (!soft_keystore_status(KEYSTORE_INITIALIZED)) return (CKR_DEVICE_REMOVED); *object_type |= TOKEN_OBJECT; } break; case CKA_PRIVATE: if ((*(CK_BBOOL *)template->pValue) == B_TRUE) { (void) pthread_mutex_lock(&soft_giant_mutex); if (!soft_slot.authenticated) { /* * Check if this is the special case when * the PIN is never initialized in the keystore. * If true, we will let it pass here and let * it fail with CKR_PIN_EXPIRED later on. */ if (!soft_slot.userpin_change_needed) { (void) pthread_mutex_unlock( &soft_giant_mutex); return (CKR_USER_NOT_LOGGED_IN); } } (void) pthread_mutex_unlock(&soft_giant_mutex); *object_type |= PRIVATE_OBJECT; } break; case CKA_LABEL: break; default: rv = CKR_TEMPLATE_INCONSISTENT; } return (rv); } /* * Build a Public Key Object. * * - Parse the object's template, and when an error is detected such as * invalid attribute type, invalid attribute value, etc., return * with appropriate return value. * - Set up attribute mask field in the object for the supplied common * attributes that have boolean type. * - Build the attribute_info struct to hold the value of each supplied * attribute that has byte array type. Link attribute_info structs * together to form the extra attribute list of the object. * - Allocate storage for the Public Key object. * - Build the Public Key object according to the key type. Allocate * storage to hold the big integer value for the supplied attributes * that are required for a certain key type. * */ CK_RV soft_build_public_key_object(CK_ATTRIBUTE_PTR template, CK_ULONG ulAttrNum, soft_object_t *new_object, CK_ULONG mode, CK_KEY_TYPE key_type) { ulong_t i; CK_KEY_TYPE keytype = (CK_KEY_TYPE)~0UL; uint64_t attr_mask = PUBLIC_KEY_DEFAULT; CK_RV rv = CKR_OK; int isLabel = 0; /* Must set flags */ int isModulus = 0; int isPubExpo = 0; int isPrime = 0; int isSubprime = 0; int isBase = 0; int isValue = 0; int isECParam = 0; int isECPoint = 0; /* Must not set flags */ int isModulusBits = 0; CK_ULONG modulus_bits = 0; biginteger_t modulus; biginteger_t pubexpo; biginteger_t prime; biginteger_t subprime; biginteger_t base; biginteger_t value; biginteger_t point; CK_ATTRIBUTE string_tmp; CK_ATTRIBUTE param_tmp; public_key_obj_t *pbk; uchar_t object_type = 0; CK_ATTRIBUTE defpubexpo = { CKA_PUBLIC_EXPONENT, (CK_BYTE_PTR)DEFAULT_PUB_EXPO, DEFAULT_PUB_EXPO_Len }; BIGNUM n; /* prevent bigint_attr_cleanup from freeing invalid attr value */ (void) memset(&modulus, 0x0, sizeof (biginteger_t)); (void) memset(&pubexpo, 0x0, sizeof (biginteger_t)); (void) memset(&prime, 0x0, sizeof (biginteger_t)); (void) memset(&subprime, 0x0, sizeof (biginteger_t)); (void) memset(&base, 0x0, sizeof (biginteger_t)); (void) memset(&value, 0x0, sizeof (biginteger_t)); (void) memset(&point, 0x0, sizeof (biginteger_t)); string_tmp.pValue = NULL; param_tmp.pValue = NULL; for (i = 0; i < ulAttrNum; i++) { /* Public Key Object Attributes */ switch (template[i].type) { /* common key attributes */ case CKA_KEY_TYPE: keytype = *((CK_KEY_TYPE*)template[i].pValue); break; case CKA_ID: case CKA_START_DATE: case CKA_END_DATE: /* common public key attribute */ case CKA_SUBJECT: /* * Allocate storage to hold the attribute * value with byte array type, and add it to * the extra attribute list of the object. */ rv = soft_add_extra_attr(&template[i], new_object); if (rv != CKR_OK) { goto fail_cleanup; } break; /* * The following key related attribute types must * not be specified by C_CreateObject, C_GenerateKey(Pair). */ case CKA_LOCAL: case CKA_KEY_GEN_MECHANISM: rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; /* Key related boolean attributes */ case CKA_DERIVE: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= DERIVE_BOOL_ON; break; case CKA_ENCRYPT: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= ENCRYPT_BOOL_ON; else attr_mask &= ~ENCRYPT_BOOL_ON; break; case CKA_VERIFY: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= VERIFY_BOOL_ON; else attr_mask &= ~VERIFY_BOOL_ON; break; case CKA_VERIFY_RECOVER: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= VERIFY_RECOVER_BOOL_ON; else attr_mask &= ~VERIFY_RECOVER_BOOL_ON; break; case CKA_WRAP: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= WRAP_BOOL_ON; else attr_mask &= ~WRAP_BOOL_ON; break; case CKA_TRUSTED: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= TRUSTED_BOOL_ON; break; case CKA_MODIFIABLE: if ((*(CK_BBOOL *)template[i].pValue) == B_FALSE) attr_mask |= NOT_MODIFIABLE_BOOL_ON; break; /* * The following key related attribute types must * be specified according to the key type by * C_CreateObject. */ case CKA_MODULUS: isModulus = 1; /* * Copyin big integer attribute from template * to a local variable. */ rv = get_bigint_attr_from_template(&modulus, &template[i]); if (rv != CKR_OK) goto fail_cleanup; /* * Modulus length needs to be between min key length and * max key length. */ if ((modulus.big_value_len < MIN_RSA_KEYLENGTH_IN_BYTES) || (modulus.big_value_len > MAX_RSA_KEYLENGTH_IN_BYTES)) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto fail_cleanup; } break; case CKA_PUBLIC_EXPONENT: isPubExpo = 1; rv = get_bigint_attr_from_template(&pubexpo, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_PRIME: isPrime = 1; rv = get_bigint_attr_from_template(&prime, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_SUBPRIME: isSubprime = 1; rv = get_bigint_attr_from_template(&subprime, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_BASE: isBase = 1; rv = get_bigint_attr_from_template(&base, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_VALUE: isValue = 1; if (mode == SOFT_CREATE_OBJ) { if ((template[i].ulValueLen == 0) || (template[i].pValue == NULL)) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto fail_cleanup; } } rv = get_bigint_attr_from_template(&value, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_MODULUS_BITS: isModulusBits = 1; rv = get_ulong_attr_from_template(&modulus_bits, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_LABEL: isLabel = 1; rv = get_string_from_template(&string_tmp, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_EC_PARAMS: isECParam = 1; rv = get_string_from_template(¶m_tmp, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_EC_POINT: isECPoint = 1; rv = get_bigint_attr_from_template(&point, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; default: rv = soft_parse_common_attrs(&template[i], &object_type); if (rv != CKR_OK) goto fail_cleanup; break; } } /* For */ /* Allocate storage for Public Key Object. */ pbk = calloc(1, sizeof (public_key_obj_t)); if (pbk == NULL) { rv = CKR_HOST_MEMORY; goto fail_cleanup; } new_object->object_class_u.public_key = pbk; new_object->class = CKO_PUBLIC_KEY; if ((mode == SOFT_CREATE_OBJ) && (keytype == (CK_KEY_TYPE)~0UL)) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } if ((mode == SOFT_GEN_KEY) && (keytype == (CK_KEY_TYPE)~0UL)) { keytype = key_type; } if ((mode == SOFT_GEN_KEY) && (keytype != key_type)) { /* * The key type specified in the template does not * match the implied key type based on the mechanism. */ rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } new_object->key_type = keytype; /* Supported key types of the Public Key Object */ switch (keytype) { case CKK_RSA: if (mode == SOFT_CREATE_OBJ) { if (isModulusBits || isPrime || isSubprime || isBase || isValue) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } if (isModulus && isPubExpo) { /* * Derive modulus_bits attribute from modulus. * Save modulus_bits integer value to the * designated place in the public key object. */ n.malloced = 0; #ifdef __sparcv9 if (big_init(&n, (int)CHARLEN2BIGNUMLEN( modulus.big_value_len)) != BIG_OK) { #else /* !__sparcv9 */ if (big_init(&n, CHARLEN2BIGNUMLEN( modulus.big_value_len)) != BIG_OK) { #endif /* __sparcv9 */ rv = CKR_HOST_MEMORY; big_finish(&n); goto fail_cleanup; } bytestring2bignum(&n, modulus.big_value, modulus.big_value_len); modulus_bits = big_bitlength(&n); KEY_PUB_RSA_MOD_BITS(pbk) = modulus_bits; big_finish(&n); /* * After modulus_bits has been computed, * it is safe to move modulus and pubexpo * big integer attribute value to the * designated place in the public key object. */ copy_bigint_attr(&modulus, KEY_PUB_RSA_MOD(pbk)); copy_bigint_attr(&pubexpo, KEY_PUB_RSA_PUBEXPO(pbk)); } else { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } } else { /* mode is SOFT_GEN_KEY */ if (isModulus || isPrime || isSubprime || isBase || isValue) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } if (isModulusBits) { /* * Copy big integer attribute value to the * designated place in the public key object. */ KEY_PUB_RSA_MOD_BITS(pbk) = modulus_bits; } else { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } /* * Use PKCS#11 default 0x010001 for public exponent * if not not specified in attribute template. */ if (!isPubExpo) { isPubExpo = 1; rv = get_bigint_attr_from_template(&pubexpo, &defpubexpo); if (rv != CKR_OK) goto fail_cleanup; } /* * Copy big integer attribute value to the * designated place in the public key object. */ copy_bigint_attr(&pubexpo, KEY_PUB_RSA_PUBEXPO(pbk)); } break; case CKK_DSA: if (mode == SOFT_CREATE_OBJ) { if (isModulusBits || isModulus || isPubExpo) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } if (isPrime && isSubprime && isBase && isValue) { copy_bigint_attr(&value, KEY_PUB_DSA_VALUE(pbk)); } else { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } } else { if (isModulusBits || isModulus || isPubExpo || isValue) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } if (!(isPrime && isSubprime && isBase)) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } } copy_bigint_attr(&prime, KEY_PUB_DSA_PRIME(pbk)); copy_bigint_attr(&subprime, KEY_PUB_DSA_SUBPRIME(pbk)); copy_bigint_attr(&base, KEY_PUB_DSA_BASE(pbk)); break; case CKK_DH: if (mode == SOFT_CREATE_OBJ) { if (isModulusBits || isModulus || isPubExpo || isSubprime) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } if (isPrime && isBase && isValue) { copy_bigint_attr(&value, KEY_PUB_DH_VALUE(pbk)); } else { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } } else { if (isModulusBits || isModulus || isPubExpo || isSubprime || isValue) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } if (!(isPrime && isBase)) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } } copy_bigint_attr(&prime, KEY_PUB_DH_PRIME(pbk)); copy_bigint_attr(&base, KEY_PUB_DH_BASE(pbk)); break; case CKK_X9_42_DH: if (mode == SOFT_CREATE_OBJ) { if (isModulusBits || isModulus || isPubExpo) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } if (isPrime && isSubprime && isBase && isValue) { copy_bigint_attr(&value, KEY_PUB_DH942_VALUE(pbk)); } else { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } } else { if (isModulusBits || isModulus || isPubExpo || isValue) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } if (!(isPrime && isSubprime && isBase)) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } } copy_bigint_attr(&prime, KEY_PUB_DH942_PRIME(pbk)); copy_bigint_attr(&base, KEY_PUB_DH942_BASE(pbk)); copy_bigint_attr(&subprime, KEY_PUB_DH942_SUBPRIME(pbk)); break; case CKK_EC: if (mode == SOFT_CREATE_OBJ) { if (isModulusBits || isModulus || isPubExpo || isPrime || isSubprime || isBase || isValue) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } else if (!isECParam || !isECPoint) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } } else { if (isModulusBits || isModulus || isPubExpo || isPrime || isSubprime || isBase || isValue) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } else if (!isECParam) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } } if (isECPoint) { copy_bigint_attr(&point, KEY_PUB_EC_POINT(pbk)); } rv = soft_add_extra_attr(¶m_tmp, new_object); if (rv != CKR_OK) goto fail_cleanup; string_attr_cleanup(¶m_tmp); break; default: rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } /* Set up object. */ new_object->object_type = object_type; new_object->bool_attr_mask = attr_mask; if (isLabel) { rv = soft_add_extra_attr(&string_tmp, new_object); if (rv != CKR_OK) goto fail_cleanup; string_attr_cleanup(&string_tmp); } return (rv); fail_cleanup: /* * cleanup the storage allocated to the local variables. */ bigint_attr_cleanup(&modulus); bigint_attr_cleanup(&pubexpo); bigint_attr_cleanup(&prime); bigint_attr_cleanup(&subprime); bigint_attr_cleanup(&base); bigint_attr_cleanup(&value); bigint_attr_cleanup(&point); string_attr_cleanup(&string_tmp); string_attr_cleanup(¶m_tmp); /* * cleanup the storage allocated inside the object itself. */ soft_cleanup_object(new_object); return (rv); } /* * Build a Private Key Object. * * - Parse the object's template, and when an error is detected such as * invalid attribute type, invalid attribute value, etc., return * with appropriate return value. * - Set up attribute mask field in the object for the supplied common * attributes that have boolean type. * - Build the attribute_info struct to hold the value of each supplied * attribute that has byte array type. Link attribute_info structs * together to form the extra attribute list of the object. * - Allocate storage for the Private Key object. * - Build the Private Key object according to the key type. Allocate * storage to hold the big integer value for the supplied attributes * that are required for a certain key type. * */ CK_RV soft_build_private_key_object(CK_ATTRIBUTE_PTR template, CK_ULONG ulAttrNum, soft_object_t *new_object, CK_ULONG mode, CK_KEY_TYPE key_type) { ulong_t i; CK_KEY_TYPE keytype = (CK_KEY_TYPE)~0UL; uint64_t attr_mask = PRIVATE_KEY_DEFAULT; CK_RV rv = CKR_OK; int isLabel = 0; int isECParam = 0; /* Must set flags unless mode == SOFT_UNWRAP_KEY */ int isModulus = 0; int isPriExpo = 0; int isPrime = 0; int isSubprime = 0; int isBase = 0; /* Must set flags if mode == SOFT_GEN_KEY */ int isValue = 0; /* Must not set flags */ int isValueBits = 0; CK_ULONG value_bits = 0; /* Private Key RSA optional */ int isPubExpo = 0; int isPrime1 = 0; int isPrime2 = 0; int isExpo1 = 0; int isExpo2 = 0; int isCoef = 0; biginteger_t modulus; biginteger_t priexpo; biginteger_t prime; biginteger_t subprime; biginteger_t base; biginteger_t value; biginteger_t pubexpo; biginteger_t prime1; biginteger_t prime2; biginteger_t expo1; biginteger_t expo2; biginteger_t coef; CK_ATTRIBUTE string_tmp; CK_ATTRIBUTE param_tmp; BIGNUM x, q; private_key_obj_t *pvk; uchar_t object_type = 0; /* prevent bigint_attr_cleanup from freeing invalid attr value */ (void) memset(&modulus, 0x0, sizeof (biginteger_t)); (void) memset(&priexpo, 0x0, sizeof (biginteger_t)); (void) memset(&prime, 0x0, sizeof (biginteger_t)); (void) memset(&subprime, 0x0, sizeof (biginteger_t)); (void) memset(&base, 0x0, sizeof (biginteger_t)); (void) memset(&value, 0x0, sizeof (biginteger_t)); (void) memset(&pubexpo, 0x0, sizeof (biginteger_t)); (void) memset(&prime1, 0x0, sizeof (biginteger_t)); (void) memset(&prime2, 0x0, sizeof (biginteger_t)); (void) memset(&expo1, 0x0, sizeof (biginteger_t)); (void) memset(&expo2, 0x0, sizeof (biginteger_t)); (void) memset(&coef, 0x0, sizeof (biginteger_t)); string_tmp.pValue = NULL; param_tmp.pValue = NULL; x.malloced = 0; q.malloced = 0; for (i = 0; i < ulAttrNum; i++) { /* Private Key Object Attributes */ switch (template[i].type) { /* common key attributes */ case CKA_KEY_TYPE: keytype = *((CK_KEY_TYPE*)template[i].pValue); break; case CKA_ID: case CKA_START_DATE: case CKA_END_DATE: /* common private key attribute */ case CKA_SUBJECT: /* * Allocate storage to hold the attribute * value with byte array type, and add it to * the extra attribute list of the object. */ rv = soft_add_extra_attr(&template[i], new_object); if (rv != CKR_OK) { goto fail_cleanup; } break; /* * The following key related attribute types must * not be specified by C_CreateObject or C_GenerateKey(Pair). */ case CKA_LOCAL: case CKA_KEY_GEN_MECHANISM: case CKA_AUTH_PIN_FLAGS: case CKA_ALWAYS_SENSITIVE: case CKA_NEVER_EXTRACTABLE: rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; /* Key related boolean attributes */ case CKA_DERIVE: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= DERIVE_BOOL_ON; break; case CKA_SENSITIVE: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= SENSITIVE_BOOL_ON; break; case CKA_SECONDARY_AUTH: if (*(CK_BBOOL *)template[i].pValue) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto fail_cleanup; } break; case CKA_DECRYPT: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= DECRYPT_BOOL_ON; else attr_mask &= ~DECRYPT_BOOL_ON; break; case CKA_SIGN: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= SIGN_BOOL_ON; else attr_mask &= ~SIGN_BOOL_ON; break; case CKA_SIGN_RECOVER: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= SIGN_RECOVER_BOOL_ON; else attr_mask &= ~SIGN_RECOVER_BOOL_ON; break; case CKA_UNWRAP: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= UNWRAP_BOOL_ON; else attr_mask &= ~UNWRAP_BOOL_ON; break; case CKA_EXTRACTABLE: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= EXTRACTABLE_BOOL_ON; else attr_mask &= ~EXTRACTABLE_BOOL_ON; break; case CKA_MODIFIABLE: if ((*(CK_BBOOL *)template[i].pValue) == B_FALSE) attr_mask |= NOT_MODIFIABLE_BOOL_ON; break; /* * The following key related attribute types must * be specified according to the key type by * C_CreateObject. */ case CKA_MODULUS: isModulus = 1; /* * Copyin big integer attribute from template * to a local variable. */ rv = get_bigint_attr_from_template(&modulus, &template[i]); if (rv != CKR_OK) goto fail_cleanup; /* * Modulus length needs to be between min key length and * max key length. */ if ((modulus.big_value_len < MIN_RSA_KEYLENGTH_IN_BYTES) || (modulus.big_value_len > MAX_RSA_KEYLENGTH_IN_BYTES)) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto fail_cleanup; } break; case CKA_PUBLIC_EXPONENT: isPubExpo = 1; rv = get_bigint_attr_from_template(&pubexpo, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_PRIVATE_EXPONENT: isPriExpo = 1; rv = get_bigint_attr_from_template(&priexpo, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_PRIME_1: isPrime1 = 1; rv = get_bigint_attr_from_template(&prime1, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_PRIME_2: isPrime2 = 1; rv = get_bigint_attr_from_template(&prime2, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_EXPONENT_1: isExpo1 = 1; rv = get_bigint_attr_from_template(&expo1, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_EXPONENT_2: isExpo2 = 1; rv = get_bigint_attr_from_template(&expo2, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_COEFFICIENT: isCoef = 1; rv = get_bigint_attr_from_template(&coef, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_PRIME: isPrime = 1; rv = get_bigint_attr_from_template(&prime, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_SUBPRIME: isSubprime = 1; rv = get_bigint_attr_from_template(&subprime, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_BASE: isBase = 1; rv = get_bigint_attr_from_template(&base, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_VALUE: isValue = 1; if (mode == SOFT_CREATE_OBJ) { if ((template[i].ulValueLen == 0) || (template[i].pValue == NULL)) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto fail_cleanup; } } rv = get_bigint_attr_from_template(&value, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_VALUE_BITS: isValueBits = 1; rv = get_ulong_attr_from_template(&value_bits, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_LABEL: isLabel = 1; rv = get_string_from_template(&string_tmp, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_EC_PARAMS: isECParam = 1; rv = get_string_from_template(¶m_tmp, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; default: rv = soft_parse_common_attrs(&template[i], &object_type); if (rv != CKR_OK) goto fail_cleanup; break; } } /* For */ /* Allocate storage for Private Key Object. */ pvk = calloc(1, sizeof (private_key_obj_t)); if (pvk == NULL) { rv = CKR_HOST_MEMORY; goto fail_cleanup; } new_object->object_class_u.private_key = pvk; new_object->class = CKO_PRIVATE_KEY; if ((mode == SOFT_CREATE_OBJ) && (keytype == (CK_KEY_TYPE)~0UL)) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } if (mode == SOFT_GEN_KEY) { /* * The key type is not specified in the application's * template, so we use the implied key type based on * the mechanism. */ if (keytype == (CK_KEY_TYPE)~0UL) { keytype = key_type; } /* If still unspecified, template is incomplete */ if (keytype == (CK_KEY_TYPE)~0UL) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } /* * The key type specified in the template does not * match the implied key type based on the mechanism. */ if (keytype != key_type) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } } if (mode == SOFT_UNWRAP_KEY) { /* * Note that, for mode SOFT_UNWRAP_KEY, key type is not * implied by the mechanism (key_type), so if it is not * specified from the attribute template (keytype), it is * incomplete. */ if (keytype == (CK_KEY_TYPE)~0UL) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } } new_object->key_type = keytype; /* Supported key types of the Private Key Object */ switch (keytype) { case CKK_RSA: if (isPrime || isSubprime || isBase || isValue || isValueBits) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } if (mode == SOFT_GEN_KEY || mode == SOFT_UNWRAP_KEY) { if (isModulus || isPubExpo || isPriExpo || isPrime1 || isPrime2 || isExpo1 || isExpo2 || isCoef) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } else break; } if (isModulus && isPriExpo) { /* * Copy big integer attribute value to the * designated place in the Private Key object. */ copy_bigint_attr(&modulus, KEY_PRI_RSA_MOD(pvk)); copy_bigint_attr(&priexpo, KEY_PRI_RSA_PRIEXPO(pvk)); } else { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } /* The following attributes are optional. */ if (isPubExpo) { copy_bigint_attr(&pubexpo, KEY_PRI_RSA_PUBEXPO(pvk)); } if (isPrime1) { copy_bigint_attr(&prime1, KEY_PRI_RSA_PRIME1(pvk)); } if (isPrime2) { copy_bigint_attr(&prime2, KEY_PRI_RSA_PRIME2(pvk)); } if (isExpo1) { copy_bigint_attr(&expo1, KEY_PRI_RSA_EXPO1(pvk)); } if (isExpo2) { copy_bigint_attr(&expo2, KEY_PRI_RSA_EXPO2(pvk)); } if (isCoef) { copy_bigint_attr(&coef, KEY_PRI_RSA_COEF(pvk)); } break; case CKK_DSA: if (isModulus || isPubExpo || isPriExpo || isPrime1 || isPrime2 || isExpo1 || isExpo2 || isCoef || isValueBits) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } if (mode == SOFT_GEN_KEY || mode == SOFT_UNWRAP_KEY) { if (isPrime || isSubprime || isBase || isValue) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } else break; } if (isPrime && isSubprime && isBase && isValue) { /* * The private value x must be less than subprime q. * Size for big_init is in BIG_CHUNK_TYPE words. */ #ifdef __sparcv9 if (big_init(&x, (int)CHARLEN2BIGNUMLEN(value.big_value_len)) != BIG_OK) { #else /* !__sparcv9 */ if (big_init(&x, CHARLEN2BIGNUMLEN(value.big_value_len)) != BIG_OK) { #endif /* __sparcv9 */ rv = CKR_HOST_MEMORY; goto fail_cleanup; } #ifdef __sparcv9 if (big_init(&q, (int)CHARLEN2BIGNUMLEN(subprime.big_value_len)) != BIG_OK) { #else /* !__sparcv9 */ if (big_init(&q, CHARLEN2BIGNUMLEN(subprime.big_value_len)) != BIG_OK) { #endif /* __sparcv9 */ rv = CKR_HOST_MEMORY; goto fail_cleanup; } bytestring2bignum(&x, value.big_value, value.big_value_len); bytestring2bignum(&q, subprime.big_value, subprime.big_value_len); if (big_cmp_abs(&x, &q) > 0) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto fail_cleanup; } copy_bigint_attr(&prime, KEY_PRI_DSA_PRIME(pvk)); copy_bigint_attr(&subprime, KEY_PRI_DSA_SUBPRIME(pvk)); copy_bigint_attr(&base, KEY_PRI_DSA_BASE(pvk)); copy_bigint_attr(&value, KEY_PRI_DSA_VALUE(pvk)); } else { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } break; case CKK_DH: if (isModulus || isPubExpo || isPriExpo || isPrime1 || isPrime2 || isExpo1 || isExpo2 || isCoef || isSubprime) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } /* CKA_VALUE_BITS is for key gen but not unwrap */ if (mode == SOFT_GEN_KEY) KEY_PRI_DH_VAL_BITS(pvk) = (isValueBits) ? value_bits : 0; else if (mode == SOFT_UNWRAP_KEY) { if (isValueBits) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } } if (mode == SOFT_GEN_KEY || mode == SOFT_UNWRAP_KEY) { if (isPrime || isBase || isValue) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } else break; } if (isValueBits) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } if (isPrime && isBase && isValue) { copy_bigint_attr(&prime, KEY_PRI_DH_PRIME(pvk)); copy_bigint_attr(&base, KEY_PRI_DH_BASE(pvk)); copy_bigint_attr(&value, KEY_PRI_DH_VALUE(pvk)); } else { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } break; case CKK_X9_42_DH: if (isModulus || isPubExpo || isPriExpo || isPrime1 || isPrime2 || isExpo1 || isExpo2 || isCoef || isValueBits) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } if (mode == SOFT_GEN_KEY || mode == SOFT_UNWRAP_KEY) { if (isPrime || isSubprime || isBase || isValue) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } else break; } if (isPrime && isSubprime && isBase && isValue) { copy_bigint_attr(&prime, KEY_PRI_DH942_PRIME(pvk)); copy_bigint_attr(&base, KEY_PRI_DH942_BASE(pvk)); copy_bigint_attr(&subprime, KEY_PRI_DH942_SUBPRIME(pvk)); copy_bigint_attr(&value, KEY_PRI_DH942_VALUE(pvk)); } else { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } break; case CKK_EC: if (isModulus || isPubExpo || isPrime || isPrime1 || isPrime2 || isExpo1 || isExpo2 || isCoef || isValueBits || isBase) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } else if (isECParam) { rv = soft_add_extra_attr(¶m_tmp, new_object); if (rv != CKR_OK) goto fail_cleanup; string_attr_cleanup(¶m_tmp); } if (isValue) { copy_bigint_attr(&value, KEY_PRI_EC_VALUE(pvk)); } break; default: rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } /* Set up object. */ new_object->object_type = object_type; new_object->bool_attr_mask = attr_mask; if (isLabel) { rv = soft_add_extra_attr(&string_tmp, new_object); if (rv != CKR_OK) goto fail_cleanup; string_attr_cleanup(&string_tmp); } big_finish(&x); big_finish(&q); return (rv); fail_cleanup: /* * cleanup the storage allocated to the local variables. */ bigint_attr_cleanup(&modulus); bigint_attr_cleanup(&priexpo); bigint_attr_cleanup(&prime); bigint_attr_cleanup(&subprime); bigint_attr_cleanup(&base); bigint_attr_cleanup(&value); bigint_attr_cleanup(&pubexpo); bigint_attr_cleanup(&prime1); bigint_attr_cleanup(&prime2); bigint_attr_cleanup(&expo1); bigint_attr_cleanup(&expo2); bigint_attr_cleanup(&coef); string_attr_cleanup(&string_tmp); string_attr_cleanup(¶m_tmp); big_finish(&x); big_finish(&q); /* * cleanup the storage allocated inside the object itself. */ soft_cleanup_object(new_object); return (rv); } /* * Build a Secret Key Object. * * - Parse the object's template, and when an error is detected such as * invalid attribute type, invalid attribute value, etc., return * with appropriate return value. * - Set up attribute mask field in the object for the supplied common * attributes that have boolean type. * - Build the attribute_info struct to hold the value of each supplied * attribute that has byte array type. Link attribute_info structs * together to form the extra attribute list of the object. * - Allocate storage for the Secret Key object. * - Build the Secret Key object. Allocate storage to hold the big integer * value for the attribute CKA_VALUE that is required for all the key * types supported by secret key object. * This function is called internally with mode = SOFT_CREATE_OBJ_INT. * */ CK_RV soft_build_secret_key_object(CK_ATTRIBUTE_PTR template, CK_ULONG ulAttrNum, soft_object_t *new_object, CK_ULONG mode, CK_ULONG key_len, CK_KEY_TYPE key_type) { ulong_t i; CK_KEY_TYPE keytype = (CK_KEY_TYPE)~0UL; uint64_t attr_mask = SECRET_KEY_DEFAULT; CK_RV rv = CKR_OK; int isLabel = 0; /* Must set flags if mode != SOFT_UNWRAP_KEY, else must not set */ int isValue = 0; /* Must not set flags if mode != SOFT_UNWRAP_KEY, else optional */ int isValueLen = 0; CK_ATTRIBUTE string_tmp; secret_key_obj_t *sck; uchar_t object_type = 0; string_tmp.pValue = NULL; /* Allocate storage for Secret Key Object. */ sck = calloc(1, sizeof (secret_key_obj_t)); if (sck == NULL) { rv = CKR_HOST_MEMORY; goto fail_cleanup; } new_object->object_class_u.secret_key = sck; new_object->class = CKO_SECRET_KEY; for (i = 0; i < ulAttrNum; i++) { /* Secret Key Object Attributes */ switch (template[i].type) { /* common key attributes */ case CKA_KEY_TYPE: keytype = *((CK_KEY_TYPE*)template[i].pValue); break; case CKA_ID: case CKA_START_DATE: case CKA_END_DATE: /* * Allocate storage to hold the attribute * value with byte array type, and add it to * the extra attribute list of the object. */ rv = soft_add_extra_attr(&template[i], new_object); if (rv != CKR_OK) { goto fail_cleanup; } break; /* * The following key related attribute types must * not be specified by C_CreateObject and C_GenerateKey. */ case CKA_LOCAL: case CKA_KEY_GEN_MECHANISM: case CKA_ALWAYS_SENSITIVE: case CKA_NEVER_EXTRACTABLE: rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; /* Key related boolean attributes */ case CKA_DERIVE: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= DERIVE_BOOL_ON; break; case CKA_SENSITIVE: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= SENSITIVE_BOOL_ON; break; case CKA_ENCRYPT: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= ENCRYPT_BOOL_ON; else attr_mask &= ~ENCRYPT_BOOL_ON; break; case CKA_DECRYPT: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= DECRYPT_BOOL_ON; else attr_mask &= ~DECRYPT_BOOL_ON; break; case CKA_SIGN: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= SIGN_BOOL_ON; else attr_mask &= ~SIGN_BOOL_ON; break; case CKA_VERIFY: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= VERIFY_BOOL_ON; else attr_mask &= ~VERIFY_BOOL_ON; break; case CKA_WRAP: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= WRAP_BOOL_ON; else attr_mask &= ~WRAP_BOOL_ON; break; case CKA_UNWRAP: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= UNWRAP_BOOL_ON; else attr_mask &= ~UNWRAP_BOOL_ON; break; case CKA_EXTRACTABLE: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= EXTRACTABLE_BOOL_ON; else attr_mask &= ~EXTRACTABLE_BOOL_ON; break; case CKA_MODIFIABLE: if ((*(CK_BBOOL *)template[i].pValue) == B_FALSE) attr_mask |= NOT_MODIFIABLE_BOOL_ON; break; case CKA_VALUE: isValue = 1; if (mode == SOFT_CREATE_OBJ) { if ((template[i].ulValueLen == 0) || (template[i].pValue == NULL)) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto fail_cleanup; } } /* * Copyin attribute from template * to a local variable. */ rv = get_bigint_attr_from_template((biginteger_t *)sck, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_VALUE_LEN: isValueLen = 1; rv = get_ulong_attr_from_template(&sck->sk_value_len, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_LABEL: isLabel = 1; rv = get_string_from_template(&string_tmp, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; default: rv = soft_parse_common_attrs(&template[i], &object_type); if (rv != CKR_OK) goto fail_cleanup; break; } } /* For */ switch (mode) { case SOFT_CREATE_OBJ: case SOFT_CREATE_OBJ_INT: case SOFT_DERIVE_KEY_DH: /* * The key type must be specified in the application's * template. Otherwise, returns error. */ if (keytype == (CK_KEY_TYPE)~0UL) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } break; case SOFT_GEN_KEY: if (keytype == (CK_KEY_TYPE)~0UL) { /* * The key type is not specified in the application's * template, so we use the implied key type based on * the mechanism. */ keytype = key_type; } else { if (keytype != key_type) { /* * The key type specified in the template * does not match the implied key type based * on the mechanism. */ rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } } /* * If a key_len is passed as a parameter, it has to * match the one found in the template. */ if (key_len > 0) { if (isValueLen && sck->sk_value_len != key_len) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } isValueLen = 1; sck->sk_value_len = key_len; } break; case SOFT_UNWRAP_KEY: /* * Note that, for mode SOFT_UNWRAP_KEY, key type is not * implied by the mechanism (key_type), so if it is not * specified from the attribute template (keytype), it is * incomplete. */ if (keytype == (CK_KEY_TYPE)~0UL) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } break; case SOFT_DERIVE_KEY_OTHER: /* * For CKM_MD5_KEY_DERIVATION & CKM_SHA1_KEY_DERIVATION, the * key type is optional. */ if (keytype == (CK_KEY_TYPE)~0UL) { keytype = key_type; } break; } switch (mode) { case SOFT_CREATE_OBJ: case SOFT_CREATE_OBJ_INT: switch (keytype) { case CKK_RC4: if (!isValue) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } if ((sck->sk_value_len < ARCFOUR_MIN_KEY_BYTES) || (sck->sk_value_len > ARCFOUR_MAX_KEY_BYTES)) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto fail_cleanup; } break; case CKK_GENERIC_SECRET: if (!isValue) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } break; case CKK_AES: if (!isValue) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } if ((sck->sk_value_len != AES_MIN_KEY_BYTES) && (sck->sk_value_len != AES_192_KEY_BYTES) && (sck->sk_value_len != AES_MAX_KEY_BYTES)) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto fail_cleanup; } break; case CKK_BLOWFISH: if (!isValue) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } if ((sck->sk_value_len < BLOWFISH_MINBYTES) || (sck->sk_value_len > BLOWFISH_MAXBYTES)) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto fail_cleanup; } break; case CKK_DES: if (!isValue) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } if (sck->sk_value_len != DES_KEYSIZE) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto fail_cleanup; } break; case CKK_DES2: if (!isValue) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } if (sck->sk_value_len != DES2_KEYSIZE) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto fail_cleanup; } break; case CKK_DES3: if (!isValue) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } if (sck->sk_value_len != DES3_KEYSIZE) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto fail_cleanup; } break; default: rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } if (isValueLen) { /* * Templates for internal object creation come from * applications calls to C_DeriveKey(), for which it * is OKey to pass a CKA_VALUE_LEN attribute, as * long as it does not conflict with the length of the * CKA_VALUE attribute. */ if ((mode != SOFT_CREATE_OBJ_INT) || ((key_len > 0) && sck->sk_value_len != key_len)) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } } break; case SOFT_GEN_KEY: /* CKA_VALUE must not be specified */ if (isValue) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } switch (keytype) { /* * CKA_VALUE_LEN must be specified by C_GenerateKey * if mech is CKK_RC4, CKK_AES, CKK_GENERIC_SECRET. */ case CKK_RC4: if (!isValueLen) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } ; if ((sck->sk_value_len < ARCFOUR_MIN_KEY_BYTES) || (sck->sk_value_len > ARCFOUR_MAX_KEY_BYTES)) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto fail_cleanup; } break; case CKK_GENERIC_SECRET: /* arbitrary key length - no length checking */ if (!isValueLen) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } break; case CKK_AES: if (!isValueLen) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } if ((sck->sk_value_len != AES_MIN_KEY_BYTES) && (sck->sk_value_len != AES_192_KEY_BYTES) && (sck->sk_value_len != AES_MAX_KEY_BYTES)) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto fail_cleanup; } break; case CKK_BLOWFISH: if (!isValueLen) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } if ((sck->sk_value_len < BLOWFISH_MINBYTES) || (sck->sk_value_len > BLOWFISH_MAXBYTES)) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto fail_cleanup; } break; case CKK_DES: case CKK_DES2: case CKK_DES3: /* CKA_VALUE_LEN attribute does not apply to DES */ if (isValueLen) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } break; default: rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } break; case SOFT_UNWRAP_KEY: /* * According to v2.11 of PKCS#11 spec, neither CKA_VALUE nor * CKA_VALUE_LEN can be be specified; however v2.20 has this * restriction removed, perhaps because it makes it hard to * determine variable-length key sizes. This case statement * complied with v2.20. */ if (isValue) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } switch (keytype) { /* * CKA_VALUE_LEN is optional * if key is CKK_RC4, CKK_AES, CKK_GENERIC_SECRET * and the unwrapping mech is *_CBC_PAD. * * CKA_VALUE_LEN is required * if key is CKK_RC4, CKK_AES, CKK_GENERIC_SECRET * and the unwrapping mech is *_ECB or *_CBC. * * since mech is not known at this point, CKA_VALUE_LEN is * treated as optional and the caller needs to enforce it. */ case CKK_RC4: if (isValueLen) { if ((sck->sk_value_len < ARCFOUR_MIN_KEY_BYTES) || (sck->sk_value_len > ARCFOUR_MAX_KEY_BYTES)) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto fail_cleanup; } } break; case CKK_GENERIC_SECRET: /* arbitrary key length - no length checking */ break; case CKK_AES: if (isValueLen) { if ((sck->sk_value_len != AES_MIN_KEY_BYTES) && (sck->sk_value_len != AES_192_KEY_BYTES) && (sck->sk_value_len != AES_MAX_KEY_BYTES)) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto fail_cleanup; } } break; case CKK_BLOWFISH: if (isValueLen && ((sck->sk_value_len < BLOWFISH_MINBYTES) || (sck->sk_value_len > BLOWFISH_MAXBYTES))) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto fail_cleanup; } break; case CKK_DES: case CKK_DES2: case CKK_DES3: /* CKA_VALUE_LEN attribute does not apply to DES */ if (isValueLen) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } break; default: rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } break; case SOFT_DERIVE_KEY_DH: /* CKA_VALUE must not be specified */ if (isValue) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } switch (keytype) { /* * CKA_VALUE_LEN is optional * if mech is CKK_RC4, CKK_AES, CKK_GENERIC_SECRET. */ case CKK_RC4: if (isValueLen) { if ((sck->sk_value_len < ARCFOUR_MIN_KEY_BYTES) || (sck->sk_value_len > ARCFOUR_MAX_KEY_BYTES)) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto fail_cleanup; } } break; case CKK_GENERIC_SECRET: /* arbitrary key length - no length checking */ break; case CKK_AES: if (isValueLen) { if ((sck->sk_value_len != AES_MIN_KEY_BYTES) && (sck->sk_value_len != AES_192_KEY_BYTES) && (sck->sk_value_len != AES_MAX_KEY_BYTES)) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto fail_cleanup; } } break; case CKK_BLOWFISH: if (isValueLen && ((sck->sk_value_len < BLOWFISH_MINBYTES) || (sck->sk_value_len > BLOWFISH_MAXBYTES))) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto fail_cleanup; } break; case CKK_DES: case CKK_DES2: case CKK_DES3: /* CKA_VALUE_LEN attribute does not apply to DES */ if (isValueLen) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } break; default: rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } break; case SOFT_DERIVE_KEY_OTHER: /* CKA_VALUE must not be specified */ if (isValue) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } switch (keytype) { /* * CKA_VALUE_LEN is an optional attribute for * CKM_SHA1_KEY_DERIVATION and CKM_MD5_KEY_DERIVATION * if mech is CKK_RC4, CKK_AES, CKK_GENERIC_SECRET. */ case CKK_RC4: case CKK_GENERIC_SECRET: case CKK_AES: case CKK_BLOWFISH: /* * No need to check key length value here, it will be * validated later in soft_key_derive_check_length(). */ break; case CKK_DES: case CKK_DES2: case CKK_DES3: /* CKA_VALUE_LEN attribute does not apply to DES */ if (isValueLen) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } break; default: rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } break; } /* Set up object. */ new_object->key_type = keytype; new_object->object_type = object_type; new_object->bool_attr_mask = attr_mask; if (isLabel) { rv = soft_add_extra_attr(&string_tmp, new_object); if (rv != CKR_OK) goto fail_cleanup; string_attr_cleanup(&string_tmp); } return (rv); fail_cleanup: /* * cleanup the storage allocated to the local variables. */ bigint_attr_cleanup((biginteger_t *)sck); string_attr_cleanup(&string_tmp); /* * cleanup the storage allocated inside the object itself. */ soft_cleanup_object(new_object); return (rv); } /* * Build a Domain Parameter Object. * * - Parse the object's template, and when an error is detected such as * invalid attribute type, invalid attribute value, etc., return * with appropriate return value. * - Allocate storage for the Domain Parameter object. * - Build the Domain Parameter object according to the key type. Allocate * storage to hold the big integer value for the supplied attributes * that are required for a certain key type. * */ CK_RV soft_build_domain_parameters_object(CK_ATTRIBUTE_PTR template, CK_ULONG ulAttrNum, soft_object_t *new_object) { ulong_t i; CK_KEY_TYPE keytype = (CK_KEY_TYPE)~0UL; CK_RV rv = CKR_OK; int isLabel = 0; /* Must set flags */ int isPrime = 0; int isSubprime = 0; int isBase = 0; /* Must not set flags */ int isPrimeBits = 0; int isSubPrimeBits = 0; biginteger_t prime; biginteger_t subprime; biginteger_t base; CK_ATTRIBUTE string_tmp; domain_obj_t *dom; uchar_t object_type = 0; /* prevent bigint_attr_cleanup from freeing invalid attr value */ (void) memset(&prime, 0x0, sizeof (biginteger_t)); (void) memset(&subprime, 0x0, sizeof (biginteger_t)); (void) memset(&base, 0x0, sizeof (biginteger_t)); string_tmp.pValue = NULL; for (i = 0; i < ulAttrNum; i++) { /* Domain Parameters Object Attributes */ switch (template[i].type) { /* common domain parameter attribute */ case CKA_KEY_TYPE: keytype = *((CK_KEY_TYPE*)template[i].pValue); break; /* * The following common domain parameter attribute * must not be specified by C_CreateObject. */ case CKA_LOCAL: rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; /* * The following domain parameter attributes must be * specified according to the key type by * C_CreateObject. */ case CKA_PRIME: isPrime = 1; /* * Copyin big integer attribute from template * to a local variable. */ rv = get_bigint_attr_from_template(&prime, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_SUBPRIME: isSubprime = 1; rv = get_bigint_attr_from_template(&subprime, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_BASE: isBase = 1; rv = get_bigint_attr_from_template(&base, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_PRIME_BITS: isPrimeBits = 1; break; case CKA_SUB_PRIME_BITS: isSubPrimeBits = 1; break; case CKA_LABEL: isLabel = 1; rv = get_string_from_template(&string_tmp, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; default: rv = soft_parse_common_attrs(&template[i], &object_type); if (rv != CKR_OK) goto fail_cleanup; break; } } /* For */ /* Allocate storage for Domain Parameters Object. */ dom = calloc(1, sizeof (domain_obj_t)); if (dom == NULL) { rv = CKR_HOST_MEMORY; goto fail_cleanup; } new_object->object_class_u.domain = dom; new_object->class = CKO_DOMAIN_PARAMETERS; if (keytype == (CK_KEY_TYPE)~0UL) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } new_object->key_type = keytype; /* Supported key types of the Domain Parameters Object */ switch (keytype) { case CKK_DSA: if (isPrimeBits || isSubPrimeBits) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } if (isPrime && isSubprime && isBase) { /* * Copy big integer attribute value to the * designated place in the domain parameter * object. */ copy_bigint_attr(&prime, KEY_DOM_DSA_PRIME(dom)); copy_bigint_attr(&subprime, KEY_DOM_DSA_SUBPRIME(dom)); copy_bigint_attr(&base, KEY_DOM_DSA_BASE(dom)); } else { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } break; case CKK_DH: if (isPrimeBits || isSubprime || isSubPrimeBits) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } if (isPrime && isBase) { copy_bigint_attr(&prime, KEY_DOM_DH_PRIME(dom)); copy_bigint_attr(&base, KEY_DOM_DH_BASE(dom)); } else { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } break; case CKK_X9_42_DH: if (isPrimeBits || isSubPrimeBits) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } if (isPrime && isSubprime && isBase) { copy_bigint_attr(&prime, KEY_DOM_DH942_PRIME(dom)); copy_bigint_attr(&base, KEY_DOM_DH942_BASE(dom)); copy_bigint_attr(&subprime, KEY_DOM_DH942_SUBPRIME(dom)); } else { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } break; default: rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } new_object->object_type = object_type; if (isLabel) { rv = soft_add_extra_attr(&string_tmp, new_object); if (rv != CKR_OK) goto fail_cleanup; string_attr_cleanup(&string_tmp); } return (rv); fail_cleanup: /* * cleanup the storage allocated to the local variables. */ bigint_attr_cleanup(&prime); bigint_attr_cleanup(&subprime); bigint_attr_cleanup(&base); string_attr_cleanup(&string_tmp); /* * cleanup the storage allocated inside the object itself. */ soft_cleanup_object(new_object); return (rv); } /* * Build a Certificate Object * * - Parse the object's template, and when an error is detected such as * invalid attribute type, invalid attribute value, etc., return * with appropriate return value. * - Allocate storage for the Certificate object */ static CK_RV soft_build_certificate_object(CK_ATTRIBUTE_PTR template, CK_ULONG ulAttrNum, soft_object_t *new_object, CK_CERTIFICATE_TYPE cert_type) { uint64_t attr_mask = 0; CK_RV rv = CKR_OK; CK_ULONG i; int owner_set = 0; int value_set = 0; int subject_set = 0; certificate_obj_t *cert; /* certificate type defaults to the value given as a parameter */ CK_CERTIFICATE_TYPE certtype = cert_type; CK_ATTRIBUTE string_tmp; int isLabel = 0; uchar_t object_type = 0; /* * Look for the certificate type attribute and do some * sanity checking before creating the structures. */ for (i = 0; i < ulAttrNum; i++) { /* Certificate Object Attributes */ switch (template[i].type) { case CKA_CERTIFICATE_TYPE: certtype = *((CK_CERTIFICATE_TYPE*)template[i].pValue); break; case CKA_SUBJECT: subject_set = 1; break; case CKA_OWNER: owner_set = 1; break; case CKA_VALUE: value_set = 1; break; } } /* The certificate type MUST be specified */ if (certtype != CKC_X_509 && certtype != CKC_X_509_ATTR_CERT) return (CKR_TEMPLATE_INCOMPLETE); /* * For X.509 certs, the CKA_SUBJECT and CKA_VALUE * must be present at creation time. */ if (certtype == CKC_X_509 && (!subject_set || !value_set)) return (CKR_TEMPLATE_INCOMPLETE); /* * For X.509 Attribute certs, the CKA_OWNER and CKA_VALUE * must be present at creation time. */ if (certtype == CKC_X_509_ATTR_CERT && (!owner_set || !value_set)) return (CKR_TEMPLATE_INCOMPLETE); string_tmp.pValue = NULL; cert = calloc(1, sizeof (certificate_obj_t)); if (cert == NULL) { return (CKR_HOST_MEMORY); } cert->certificate_type = certtype; for (i = 0; i < ulAttrNum; i++) { /* Certificate Object Attributes */ switch (certtype) { case CKC_X_509: switch (template[i].type) { case CKA_SUBJECT: rv = get_cert_attr_from_template( &cert->cert_type_u.x509.subject, &template[i]); break; case CKA_VALUE: rv = get_cert_attr_from_template( &cert->cert_type_u.x509.value, &template[i]); break; case CKA_LABEL: isLabel = 1; rv = get_string_from_template( &string_tmp, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_ID: case CKA_ISSUER: case CKA_SERIAL_NUMBER: rv = soft_add_extra_attr(&template[i], new_object); break; case CKA_MODIFIABLE: if ((*(CK_BBOOL *)template[i].pValue) == B_FALSE) attr_mask |= NOT_MODIFIABLE_BOOL_ON; break; case CKA_CERTIFICATE_TYPE: break; default: rv = soft_parse_common_attrs( &template[i], &object_type); if (rv != CKR_OK) goto fail_cleanup; } break; case CKC_X_509_ATTR_CERT: switch (template[i].type) { case CKA_OWNER: rv = get_cert_attr_from_template( &cert->cert_type_u.x509_attr.owner, &template[i]); break; case CKA_VALUE: rv = get_cert_attr_from_template( &cert->cert_type_u.x509_attr.value, &template[i]); break; case CKA_LABEL: isLabel = 1; rv = get_string_from_template( &string_tmp, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_SERIAL_NUMBER: case CKA_AC_ISSUER: case CKA_ATTR_TYPES: rv = soft_add_extra_attr(&template[i], new_object); break; case CKA_MODIFIABLE: if ((*(CK_BBOOL *)template[i].pValue) == B_FALSE) attr_mask |= NOT_MODIFIABLE_BOOL_ON; break; case CKA_CERTIFICATE_TYPE: break; default: rv = soft_parse_common_attrs( &template[i], &object_type); if (rv != CKR_OK) goto fail_cleanup; break; } break; default: rv = CKR_TEMPLATE_INCOMPLETE; break; } } if (rv == CKR_OK) { new_object->object_class_u.certificate = cert; new_object->class = CKO_CERTIFICATE; new_object->object_type = object_type; new_object->cert_type = certtype; new_object->bool_attr_mask = attr_mask; if (isLabel) { rv = soft_add_extra_attr(&string_tmp, new_object); if (rv != CKR_OK) goto fail_cleanup; string_attr_cleanup(&string_tmp); } } fail_cleanup: if (rv != CKR_OK) { soft_cleanup_cert_object(new_object); } return (rv); } /* * Validate the attribute types in the object's template. Then, * call the appropriate build function according to the class of * the object specified in the template. * * Note: The following classes of objects are supported: * - CKO_PUBLIC_KEY * - CKO_PRIVATE_KEY * - CKO_SECRET_KEY * - CKO_DOMAIN_PARAMETERS * - CKO_CERTIFICATE * */ CK_RV soft_build_object(CK_ATTRIBUTE_PTR template, CK_ULONG ulAttrNum, soft_object_t *new_object) { CK_OBJECT_CLASS class = (CK_OBJECT_CLASS)~0UL; CK_RV rv = CKR_OK; if (template == NULL) { return (CKR_ARGUMENTS_BAD); } /* Validate the attribute type in the template. */ rv = soft_validate_attr(template, ulAttrNum, &class); if (rv != CKR_OK) return (rv); /* * CKA_CLASS is a mandatory attribute for C_CreateObject */ if (class == (CK_OBJECT_CLASS)~0UL) return (CKR_TEMPLATE_INCOMPLETE); /* * Call the appropriate function based on the supported class * of the object. */ switch (class) { case CKO_PUBLIC_KEY: rv = soft_build_public_key_object(template, ulAttrNum, new_object, SOFT_CREATE_OBJ, (CK_KEY_TYPE)~0UL); break; case CKO_PRIVATE_KEY: rv = soft_build_private_key_object(template, ulAttrNum, new_object, SOFT_CREATE_OBJ, (CK_KEY_TYPE)~0UL); break; case CKO_SECRET_KEY: rv = soft_build_secret_key_object(template, ulAttrNum, new_object, SOFT_CREATE_OBJ, 0, (CK_KEY_TYPE)~0UL); break; case CKO_DOMAIN_PARAMETERS: rv = soft_build_domain_parameters_object(template, ulAttrNum, new_object); break; case CKO_CERTIFICATE: rv = soft_build_certificate_object(template, ulAttrNum, new_object, (CK_CERTIFICATE_TYPE)~0UL); break; case CKO_DATA: case CKO_HW_FEATURE: case CKO_VENDOR_DEFINED: default: return (CKR_ATTRIBUTE_VALUE_INVALID); } return (rv); } /* * Validate the attribute types in the object's template. Then, * call the appropriate build function according to the class of * the object specified in the template. * */ CK_RV soft_build_key(CK_ATTRIBUTE_PTR template, CK_ULONG ulAttrNum, soft_object_t *new_object, CK_OBJECT_CLASS class, CK_KEY_TYPE key_type, CK_ULONG key_len, CK_ULONG mode) { CK_RV rv = CKR_OK; CK_OBJECT_CLASS temp_class = (CK_OBJECT_CLASS)~0UL; /* Validate the attribute type in the template. */ if ((template != NULL) && (ulAttrNum != 0)) { rv = soft_validate_attr(template, ulAttrNum, &temp_class); if (rv != CKR_OK) return (rv); } /* * If either the class from the parameter list ("class") or * the class from the template ("temp_class") is not specified, * try to use the other one. */ if (temp_class == (CK_OBJECT_CLASS)~0UL) { temp_class = class; } else if (class == (CK_OBJECT_CLASS)~0UL) { class = temp_class; } /* If object class is still not specified, template is incomplete. */ if (class == (CK_OBJECT_CLASS)~0UL) return (CKR_TEMPLATE_INCOMPLETE); /* Class should match if specified in both parameters and template. */ if (class != temp_class) return (CKR_TEMPLATE_INCONSISTENT); /* * Call the appropriate function based on the supported class * of the object. */ switch (class) { case CKO_PUBLIC_KEY: /* Unwrapping public keys is not supported. */ if (mode == SOFT_UNWRAP_KEY) { rv = CKR_ATTRIBUTE_VALUE_INVALID; break; } rv = soft_build_public_key_object(template, ulAttrNum, new_object, mode, key_type); break; case CKO_PRIVATE_KEY: rv = soft_build_private_key_object(template, ulAttrNum, new_object, mode, key_type); break; case CKO_SECRET_KEY: rv = soft_build_secret_key_object(template, ulAttrNum, new_object, mode, key_len, key_type); break; case CKO_DOMAIN_PARAMETERS: /* Unwrapping domain parameters is not supported. */ if (mode == SOFT_UNWRAP_KEY) { rv = CKR_ATTRIBUTE_VALUE_INVALID; break; } rv = soft_build_domain_parameters_object(template, ulAttrNum, new_object); break; case CKO_DATA: case CKO_CERTIFICATE: case CKO_HW_FEATURE: case CKO_VENDOR_DEFINED: default: return (CKR_ATTRIBUTE_VALUE_INVALID); } return (rv); } /* * Get the value of a requested attribute that is common to all supported * classes (i.e. public key, private key, secret key, domain parameters, * and certificate classes). */ CK_RV soft_get_common_attrs(soft_object_t *object_p, CK_ATTRIBUTE_PTR template, uchar_t object_type) { CK_RV rv = CKR_OK; switch (template->type) { case CKA_CLASS: return (get_ulong_attr_from_object(object_p->class, template)); /* default boolean attributes */ case CKA_TOKEN: template->ulValueLen = sizeof (CK_BBOOL); if (template->pValue == NULL) { return (CKR_OK); } if (object_type & TOKEN_OBJECT) *((CK_BBOOL *)template->pValue) = B_TRUE; else *((CK_BBOOL *)template->pValue) = B_FALSE; break; case CKA_PRIVATE: template->ulValueLen = sizeof (CK_BBOOL); if (template->pValue == NULL) { return (CKR_OK); } if (object_type & PRIVATE_OBJECT) *((CK_BBOOL *)template->pValue) = B_TRUE; else *((CK_BBOOL *)template->pValue) = B_FALSE; break; case CKA_MODIFIABLE: template->ulValueLen = sizeof (CK_BBOOL); if (template->pValue == NULL) { return (CKR_OK); } if ((object_p->bool_attr_mask) & NOT_MODIFIABLE_BOOL_ON) *((CK_BBOOL *)template->pValue) = B_FALSE; else *((CK_BBOOL *)template->pValue) = B_TRUE; break; case CKA_LABEL: return (get_extra_attr_from_object(object_p, template)); default: /* * The specified attribute for the object is invalid. * (the object does not possess such an attribute.) */ template->ulValueLen = (CK_ULONG)-1; return (CKR_ATTRIBUTE_TYPE_INVALID); } return (rv); } /* * Get the value of a requested attribute that is common to all key objects * (i.e. public key, private key and secret key). */ CK_RV soft_get_common_key_attrs(soft_object_t *object_p, CK_ATTRIBUTE_PTR template) { switch (template->type) { case CKA_KEY_TYPE: return (get_ulong_attr_from_object(object_p->key_type, template)); case CKA_ID: case CKA_START_DATE: case CKA_END_DATE: /* * The above extra attributes have byte array type. */ return (get_extra_attr_from_object(object_p, template)); /* Key related boolean attributes */ case CKA_LOCAL: return (get_bool_attr_from_object(object_p, LOCAL_BOOL_ON, template)); case CKA_DERIVE: return (get_bool_attr_from_object(object_p, DERIVE_BOOL_ON, template)); case CKA_KEY_GEN_MECHANISM: return (get_ulong_attr_from_object(object_p->mechanism, template)); default: return (CKR_ATTRIBUTE_TYPE_INVALID); } } /* * Get the value of a requested attribute of a Public Key Object. * * Rule: All the attributes in the public key object can be revealed. */ CK_RV soft_get_public_key_attribute(soft_object_t *object_p, CK_ATTRIBUTE_PTR template) { CK_RV rv = CKR_OK; CK_KEY_TYPE keytype = object_p->key_type; switch (template->type) { case CKA_SUBJECT: case CKA_EC_PARAMS: /* * The above extra attributes have byte array type. */ return (get_extra_attr_from_object(object_p, template)); /* Key related boolean attributes */ case CKA_ENCRYPT: return (get_bool_attr_from_object(object_p, ENCRYPT_BOOL_ON, template)); case CKA_VERIFY: return (get_bool_attr_from_object(object_p, VERIFY_BOOL_ON, template)); case CKA_VERIFY_RECOVER: return (get_bool_attr_from_object(object_p, VERIFY_RECOVER_BOOL_ON, template)); case CKA_WRAP: return (get_bool_attr_from_object(object_p, WRAP_BOOL_ON, template)); case CKA_TRUSTED: return (get_bool_attr_from_object(object_p, TRUSTED_BOOL_ON, template)); case CKA_MODULUS: /* * This attribute is valid only for RSA public key * object. */ if (keytype == CKK_RSA) { return (get_bigint_attr_from_object( OBJ_PUB_RSA_MOD(object_p), template)); } else { template->ulValueLen = (CK_ULONG)-1; return (CKR_ATTRIBUTE_TYPE_INVALID); } case CKA_PUBLIC_EXPONENT: if (keytype == CKK_RSA) { return (get_bigint_attr_from_object( OBJ_PUB_RSA_PUBEXPO(object_p), template)); } else { template->ulValueLen = (CK_ULONG)-1; return (CKR_ATTRIBUTE_TYPE_INVALID); } case CKA_MODULUS_BITS: if (keytype == CKK_RSA) { return (get_ulong_attr_from_object( OBJ_PUB_RSA_MOD_BITS(object_p), template)); } else { template->ulValueLen = (CK_ULONG)-1; return (CKR_ATTRIBUTE_TYPE_INVALID); } case CKA_PRIME: switch (keytype) { case CKK_DSA: return (get_bigint_attr_from_object( OBJ_PUB_DSA_PRIME(object_p), template)); case CKK_DH: return (get_bigint_attr_from_object( OBJ_PUB_DH_PRIME(object_p), template)); case CKK_X9_42_DH: return (get_bigint_attr_from_object( OBJ_PUB_DH942_PRIME(object_p), template)); default: template->ulValueLen = (CK_ULONG)-1; return (CKR_ATTRIBUTE_TYPE_INVALID); } case CKA_SUBPRIME: switch (keytype) { case CKK_DSA: return (get_bigint_attr_from_object( OBJ_PUB_DSA_SUBPRIME(object_p), template)); case CKK_X9_42_DH: return (get_bigint_attr_from_object( OBJ_PUB_DH942_SUBPRIME(object_p), template)); default: template->ulValueLen = (CK_ULONG)-1; return (CKR_ATTRIBUTE_TYPE_INVALID); } case CKA_BASE: switch (keytype) { case CKK_DSA: return (get_bigint_attr_from_object( OBJ_PUB_DSA_BASE(object_p), template)); case CKK_DH: return (get_bigint_attr_from_object( OBJ_PUB_DH_BASE(object_p), template)); case CKK_X9_42_DH: return (get_bigint_attr_from_object( OBJ_PUB_DH942_BASE(object_p), template)); default: template->ulValueLen = (CK_ULONG)-1; return (CKR_ATTRIBUTE_TYPE_INVALID); } case CKA_EC_POINT: return (get_bigint_attr_from_object( OBJ_PUB_EC_POINT(object_p), template)); case CKA_VALUE: switch (keytype) { case CKK_DSA: return (get_bigint_attr_from_object( OBJ_PUB_DSA_VALUE(object_p), template)); case CKK_DH: return (get_bigint_attr_from_object( OBJ_PUB_DH_VALUE(object_p), template)); case CKK_X9_42_DH: return (get_bigint_attr_from_object( OBJ_PUB_DH942_VALUE(object_p), template)); default: template->ulValueLen = (CK_ULONG)-1; return (CKR_ATTRIBUTE_TYPE_INVALID); } default: /* * First, get the value of the request attribute defined * in the list of common key attributes. If the request * attribute is not found in that list, then get the * attribute from the list of common attributes. */ rv = soft_get_common_key_attrs(object_p, template); if (rv == CKR_ATTRIBUTE_TYPE_INVALID) { rv = soft_get_common_attrs(object_p, template, object_p->object_type); } break; } return (rv); } /* * Get the value of a requested attribute of a Private Key Object. * * Rule: All the attributes in the private key object can be revealed * except those marked with footnote number "7" when the object * has its CKA_SENSITIVE attribute set to TRUE or its * CKA_EXTRACTABLE attribute set to FALSE (p.88 in PKCS11 spec.). */ CK_RV soft_get_private_key_attribute(soft_object_t *object_p, CK_ATTRIBUTE_PTR template) { CK_RV rv = CKR_OK; CK_KEY_TYPE keytype = object_p->key_type; /* * If the following specified attributes for the private key * object cannot be revealed because the object is sensitive * or unextractable, then the ulValueLen is set to -1. */ if ((object_p->bool_attr_mask & SENSITIVE_BOOL_ON) || !(object_p->bool_attr_mask & EXTRACTABLE_BOOL_ON)) { switch (template->type) { 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_VALUE: template->ulValueLen = (CK_ULONG)-1; return (CKR_ATTRIBUTE_SENSITIVE); } } switch (template->type) { case CKA_SUBJECT: case CKA_EC_PARAMS: /* * The above extra attributes have byte array type. */ return (get_extra_attr_from_object(object_p, template)); /* Key related boolean attributes */ case CKA_SENSITIVE: return (get_bool_attr_from_object(object_p, SENSITIVE_BOOL_ON, template)); case CKA_SECONDARY_AUTH: return (get_bool_attr_from_object(object_p, SECONDARY_AUTH_BOOL_ON, template)); case CKA_DECRYPT: return (get_bool_attr_from_object(object_p, DECRYPT_BOOL_ON, template)); case CKA_SIGN: return (get_bool_attr_from_object(object_p, SIGN_BOOL_ON, template)); case CKA_SIGN_RECOVER: return (get_bool_attr_from_object(object_p, SIGN_RECOVER_BOOL_ON, template)); case CKA_UNWRAP: return (get_bool_attr_from_object(object_p, UNWRAP_BOOL_ON, template)); case CKA_EXTRACTABLE: return (get_bool_attr_from_object(object_p, EXTRACTABLE_BOOL_ON, template)); case CKA_ALWAYS_SENSITIVE: return (get_bool_attr_from_object(object_p, ALWAYS_SENSITIVE_BOOL_ON, template)); case CKA_NEVER_EXTRACTABLE: return (get_bool_attr_from_object(object_p, NEVER_EXTRACTABLE_BOOL_ON, template)); case CKA_MODULUS: if (keytype == CKK_RSA) { return (get_bigint_attr_from_object( OBJ_PRI_RSA_MOD(object_p), template)); } else { template->ulValueLen = (CK_ULONG)-1; rv = CKR_ATTRIBUTE_TYPE_INVALID; break; } case CKA_PUBLIC_EXPONENT: if (keytype == CKK_RSA) { return (get_bigint_attr_from_object( OBJ_PRI_RSA_PUBEXPO(object_p), template)); } else { template->ulValueLen = (CK_ULONG)-1; rv = CKR_ATTRIBUTE_TYPE_INVALID; break; } case CKA_PRIVATE_EXPONENT: if (keytype == CKK_RSA) { return (get_bigint_attr_from_object( OBJ_PRI_RSA_PRIEXPO(object_p), template)); } else { template->ulValueLen = (CK_ULONG)-1; rv = CKR_ATTRIBUTE_TYPE_INVALID; break; } case CKA_PRIME_1: if (keytype == CKK_RSA) { return (get_bigint_attr_from_object( OBJ_PRI_RSA_PRIME1(object_p), template)); } else { template->ulValueLen = (CK_ULONG)-1; rv = CKR_ATTRIBUTE_TYPE_INVALID; break; } case CKA_PRIME_2: if (keytype == CKK_RSA) { return (get_bigint_attr_from_object( OBJ_PRI_RSA_PRIME2(object_p), template)); } else { template->ulValueLen = (CK_ULONG)-1; rv = CKR_ATTRIBUTE_TYPE_INVALID; break; } case CKA_EXPONENT_1: if (keytype == CKK_RSA) { return (get_bigint_attr_from_object( OBJ_PRI_RSA_EXPO1(object_p), template)); } else { template->ulValueLen = (CK_ULONG)-1; rv = CKR_ATTRIBUTE_TYPE_INVALID; break; } case CKA_EXPONENT_2: if (keytype == CKK_RSA) { return (get_bigint_attr_from_object( OBJ_PRI_RSA_EXPO2(object_p), template)); } else { template->ulValueLen = (CK_ULONG)-1; rv = CKR_ATTRIBUTE_TYPE_INVALID; break; } case CKA_COEFFICIENT: if (keytype == CKK_RSA) { return (get_bigint_attr_from_object( OBJ_PRI_RSA_COEF(object_p), template)); } else { template->ulValueLen = (CK_ULONG)-1; rv = CKR_ATTRIBUTE_TYPE_INVALID; break; } case CKA_VALUE_BITS: if (keytype == CKK_DH) { return (get_ulong_attr_from_object( OBJ_PRI_DH_VAL_BITS(object_p), template)); } else { template->ulValueLen = (CK_ULONG)-1; rv = CKR_ATTRIBUTE_TYPE_INVALID; break; } case CKA_PRIME: switch (keytype) { case CKK_DSA: return (get_bigint_attr_from_object( OBJ_PRI_DSA_PRIME(object_p), template)); case CKK_DH: return (get_bigint_attr_from_object( OBJ_PRI_DH_PRIME(object_p), template)); case CKK_X9_42_DH: return (get_bigint_attr_from_object( OBJ_PRI_DH942_PRIME(object_p), template)); default: template->ulValueLen = (CK_ULONG)-1; return (CKR_ATTRIBUTE_TYPE_INVALID); } case CKA_SUBPRIME: switch (keytype) { case CKK_DSA: return (get_bigint_attr_from_object( OBJ_PRI_DSA_SUBPRIME(object_p), template)); case CKK_X9_42_DH: return (get_bigint_attr_from_object( OBJ_PRI_DH942_SUBPRIME(object_p), template)); default: template->ulValueLen = (CK_ULONG)-1; return (CKR_ATTRIBUTE_TYPE_INVALID); } case CKA_BASE: switch (keytype) { case CKK_DSA: return (get_bigint_attr_from_object( OBJ_PRI_DSA_BASE(object_p), template)); case CKK_DH: return (get_bigint_attr_from_object( OBJ_PRI_DH_BASE(object_p), template)); case CKK_X9_42_DH: return (get_bigint_attr_from_object( OBJ_PRI_DH942_BASE(object_p), template)); default: template->ulValueLen = (CK_ULONG)-1; return (CKR_ATTRIBUTE_TYPE_INVALID); } case CKA_VALUE: switch (keytype) { case CKK_DSA: return (get_bigint_attr_from_object( OBJ_PRI_DSA_VALUE(object_p), template)); case CKK_DH: return (get_bigint_attr_from_object( OBJ_PRI_DH_VALUE(object_p), template)); case CKK_X9_42_DH: return (get_bigint_attr_from_object( OBJ_PRI_DH942_VALUE(object_p), template)); case CKK_EC: return (get_bigint_attr_from_object( OBJ_PRI_EC_VALUE(object_p), template)); default: template->ulValueLen = (CK_ULONG)-1; return (CKR_ATTRIBUTE_TYPE_INVALID); } default: /* * First, get the value of the request attribute defined * in the list of common key attributes. If the request * attribute is not found in that list, then get the * attribute from the list of common attributes. */ rv = soft_get_common_key_attrs(object_p, template); if (rv == CKR_ATTRIBUTE_TYPE_INVALID) { rv = soft_get_common_attrs(object_p, template, object_p->object_type); } break; } return (rv); } /* * Get the value of a requested attribute of a Secret Key Object. * * Rule: All the attributes in the secret key object can be revealed * except those marked with footnote number "7" when the object * has its CKA_SENSITIVE attribute set to TRUE or its * CKA_EXTRACTABLE attribute set to FALSE (p.88 in PKCS11 spec.). */ CK_RV soft_get_secret_key_attribute(soft_object_t *object_p, CK_ATTRIBUTE_PTR template) { CK_RV rv = CKR_OK; CK_KEY_TYPE keytype = object_p->key_type; switch (template->type) { /* Key related boolean attributes */ case CKA_SENSITIVE: return (get_bool_attr_from_object(object_p, SENSITIVE_BOOL_ON, template)); case CKA_ENCRYPT: return (get_bool_attr_from_object(object_p, ENCRYPT_BOOL_ON, template)); case CKA_DECRYPT: return (get_bool_attr_from_object(object_p, DECRYPT_BOOL_ON, template)); case CKA_SIGN: return (get_bool_attr_from_object(object_p, SIGN_BOOL_ON, template)); case CKA_VERIFY: return (get_bool_attr_from_object(object_p, VERIFY_BOOL_ON, template)); case CKA_WRAP: return (get_bool_attr_from_object(object_p, WRAP_BOOL_ON, template)); case CKA_UNWRAP: return (get_bool_attr_from_object(object_p, UNWRAP_BOOL_ON, template)); case CKA_EXTRACTABLE: return (get_bool_attr_from_object(object_p, EXTRACTABLE_BOOL_ON, template)); case CKA_ALWAYS_SENSITIVE: return (get_bool_attr_from_object(object_p, ALWAYS_SENSITIVE_BOOL_ON, template)); case CKA_NEVER_EXTRACTABLE: return (get_bool_attr_from_object(object_p, NEVER_EXTRACTABLE_BOOL_ON, template)); case CKA_VALUE: case CKA_VALUE_LEN: /* * If the specified attribute for the secret key object * cannot be revealed because the object is sensitive * or unextractable, then the ulValueLen is set to -1. */ if ((object_p->bool_attr_mask & SENSITIVE_BOOL_ON) || !(object_p->bool_attr_mask & EXTRACTABLE_BOOL_ON)) { template->ulValueLen = (CK_ULONG)-1; return (CKR_ATTRIBUTE_SENSITIVE); } switch (keytype) { case CKK_RC4: case CKK_GENERIC_SECRET: case CKK_RC5: case CKK_DES: case CKK_DES2: case CKK_DES3: case CKK_CDMF: case CKK_AES: case CKK_BLOWFISH: if (template->type == CKA_VALUE_LEN) { return (get_ulong_attr_from_object( OBJ_SEC_VALUE_LEN(object_p), template)); } else { return (get_bigint_attr_from_object( (biginteger_t *)OBJ_SEC(object_p), template)); } default: template->ulValueLen = (CK_ULONG)-1; rv = CKR_ATTRIBUTE_TYPE_INVALID; break; } break; default: /* * First, get the value of the request attribute defined * in the list of common key attributes. If the request * attribute is not found in that list, then get the * attribute from the list of common attributes. */ rv = soft_get_common_key_attrs(object_p, template); if (rv == CKR_ATTRIBUTE_TYPE_INVALID) { rv = soft_get_common_attrs(object_p, template, object_p->object_type); } break; } return (rv); } /* * Get the value of a requested attribute of a Domain Parameters Object. * * Rule: All the attributes in the domain parameters object can be revealed. */ CK_RV soft_get_domain_parameters_attribute(soft_object_t *object_p, CK_ATTRIBUTE_PTR template) { CK_RV rv = CKR_OK; CK_KEY_TYPE keytype = object_p->key_type; switch (template->type) { case CKA_KEY_TYPE: return (get_ulong_attr_from_object(keytype, template)); case CKA_LOCAL: return (get_bool_attr_from_object(object_p, LOCAL_BOOL_ON, template)); case CKA_PRIME: switch (keytype) { case CKK_DSA: return (get_bigint_attr_from_object( OBJ_DOM_DSA_PRIME(object_p), template)); case CKK_DH: return (get_bigint_attr_from_object( OBJ_DOM_DH_PRIME(object_p), template)); case CKK_X9_42_DH: return (get_bigint_attr_from_object( OBJ_DOM_DH942_PRIME(object_p), template)); default: template->ulValueLen = (CK_ULONG)-1; return (CKR_ATTRIBUTE_TYPE_INVALID); } case CKA_SUBPRIME: switch (keytype) { case CKK_DSA: return (get_bigint_attr_from_object( OBJ_DOM_DSA_SUBPRIME(object_p), template)); case CKK_X9_42_DH: return (get_bigint_attr_from_object( OBJ_DOM_DH942_SUBPRIME(object_p), template)); default: template->ulValueLen = (CK_ULONG)-1; return (CKR_ATTRIBUTE_TYPE_INVALID); } case CKA_BASE: switch (keytype) { case CKK_DSA: return (get_bigint_attr_from_object( OBJ_DOM_DSA_BASE(object_p), template)); case CKK_DH: return (get_bigint_attr_from_object( OBJ_DOM_DH_BASE(object_p), template)); case CKK_X9_42_DH: return (get_bigint_attr_from_object( OBJ_DOM_DH942_BASE(object_p), template)); default: template->ulValueLen = (CK_ULONG)-1; return (CKR_ATTRIBUTE_TYPE_INVALID); } case CKA_PRIME_BITS: switch (keytype) { case CKK_DSA: return (get_ulong_attr_from_object( OBJ_DOM_DSA_PRIME_BITS(object_p), template)); case CKK_DH: return (get_ulong_attr_from_object( OBJ_DOM_DH_PRIME_BITS(object_p), template)); case CKK_X9_42_DH: return (get_ulong_attr_from_object( OBJ_DOM_DH942_PRIME_BITS(object_p), template)); default: template->ulValueLen = (CK_ULONG)-1; return (CKR_ATTRIBUTE_TYPE_INVALID); } case CKA_SUB_PRIME_BITS: switch (keytype) { case CKK_X9_42_DH: return (get_ulong_attr_from_object( OBJ_DOM_DH942_SUBPRIME_BITS(object_p), template)); default: template->ulValueLen = (CK_ULONG)-1; return (CKR_ATTRIBUTE_TYPE_INVALID); } default: /* * Get the value of a common attribute. */ rv = soft_get_common_attrs(object_p, template, object_p->object_type); break; } return (rv); } /* * Get certificate attributes from an object. * return CKR_ATTRIBUTE_TYPE_INVALID if the requested type * does not exist in the certificate. */ CK_RV soft_get_certificate_attribute(soft_object_t *object_p, CK_ATTRIBUTE_PTR template) { CK_CERTIFICATE_TYPE certtype = object_p->cert_type; cert_attr_t src; switch (template->type) { case CKA_SUBJECT: if (certtype == CKC_X_509) { return (get_cert_attr_from_object( X509_CERT_SUBJECT(object_p), template)); } break; case CKA_VALUE: if (certtype == CKC_X_509) { return (get_cert_attr_from_object( X509_CERT_VALUE(object_p), template)); } else if (certtype == CKC_X_509_ATTR_CERT) { return (get_cert_attr_from_object( X509_ATTR_CERT_VALUE(object_p), template)); } break; case CKA_OWNER: if (certtype == CKC_X_509_ATTR_CERT) { return (get_cert_attr_from_object( X509_ATTR_CERT_OWNER(object_p), template)); } break; case CKA_CERTIFICATE_TYPE: src.value = (CK_BYTE *)&certtype; src.length = sizeof (certtype); return (get_cert_attr_from_object(&src, template)); case CKA_TRUSTED: return (get_bool_attr_from_object(object_p, TRUSTED_BOOL_ON, template)); case CKA_ID: case CKA_ISSUER: case CKA_SERIAL_NUMBER: case CKA_AC_ISSUER: case CKA_ATTR_TYPES: return (get_extra_attr_from_object(object_p, template)); default: return (soft_get_common_attrs(object_p, template, object_p->object_type)); } /* * If we got this far, then the combination of certificate type * and requested attribute is invalid. */ return (CKR_ATTRIBUTE_TYPE_INVALID); } CK_RV soft_set_certificate_attribute(soft_object_t *object_p, CK_ATTRIBUTE_PTR template, boolean_t copy) { CK_CERTIFICATE_TYPE certtype = object_p->cert_type; switch (template->type) { case CKA_SUBJECT: if (certtype == CKC_X_509) { /* SUBJECT attr cannot be modified. */ return (CKR_ATTRIBUTE_READ_ONLY); } break; case CKA_OWNER: if (certtype == CKC_X_509_ATTR_CERT) { /* OWNER attr cannot be modified. */ return (CKR_ATTRIBUTE_READ_ONLY); } break; case CKA_VALUE: /* VALUE attr cannot be modified. */ return (CKR_ATTRIBUTE_READ_ONLY); case CKA_ID: case CKA_ISSUER: if (certtype == CKC_X_509) { return (set_extra_attr_to_object(object_p, template->type, template)); } break; case CKA_AC_ISSUER: case CKA_ATTR_TYPES: if (certtype == CKC_X_509_ATTR_CERT) { return (set_extra_attr_to_object(object_p, template->type, template)); } break; case CKA_SERIAL_NUMBER: case CKA_LABEL: return (set_extra_attr_to_object(object_p, template->type, template)); default: return (soft_set_common_storage_attribute( object_p, template, copy)); } /* * If we got this far, then the combination of certificate type * and requested attribute is invalid. */ return (CKR_ATTRIBUTE_TYPE_INVALID); } /* * Call the appropriate get attribute function according to the class * of object. * * The caller of this function holds the lock on the object. */ CK_RV soft_get_attribute(soft_object_t *object_p, CK_ATTRIBUTE_PTR template) { CK_RV rv = CKR_OK; CK_OBJECT_CLASS class = object_p->class; switch (class) { case CKO_PUBLIC_KEY: rv = soft_get_public_key_attribute(object_p, template); break; case CKO_PRIVATE_KEY: rv = soft_get_private_key_attribute(object_p, template); break; case CKO_SECRET_KEY: rv = soft_get_secret_key_attribute(object_p, template); break; case CKO_DOMAIN_PARAMETERS: rv = soft_get_domain_parameters_attribute(object_p, template); break; case CKO_CERTIFICATE: rv = soft_get_certificate_attribute(object_p, template); break; default: /* * If the specified attribute for the object is invalid * (the object does not possess such as attribute), then * the ulValueLen is modified to hold the value -1. */ template->ulValueLen = (CK_ULONG)-1; return (CKR_ATTRIBUTE_TYPE_INVALID); } return (rv); } CK_RV soft_set_common_storage_attribute(soft_object_t *object_p, CK_ATTRIBUTE_PTR template, boolean_t copy) { CK_RV rv = CKR_OK; switch (template->type) { case CKA_TOKEN: if (copy) { if ((*(CK_BBOOL *)template->pValue) == B_TRUE) { if (!soft_keystore_status(KEYSTORE_INITIALIZED)) return (CKR_DEVICE_REMOVED); object_p->object_type |= TOKEN_OBJECT; } } else { rv = CKR_ATTRIBUTE_READ_ONLY; } break; case CKA_PRIVATE: if (copy) { if ((*(CK_BBOOL *)template->pValue) == B_TRUE) { (void) pthread_mutex_lock(&soft_giant_mutex); if (!soft_slot.authenticated) { /* * Check if this is the special case * when the PIN is never initialized * in the keystore. If true, we will * let it pass here and let it fail * with CKR_PIN_EXPIRED later on. */ if (!soft_slot.userpin_change_needed) { (void) pthread_mutex_unlock( &soft_giant_mutex); return (CKR_USER_NOT_LOGGED_IN); } } (void) pthread_mutex_unlock(&soft_giant_mutex); object_p->object_type |= PRIVATE_OBJECT; } } else { rv = CKR_ATTRIBUTE_READ_ONLY; } break; case CKA_MODIFIABLE: if (copy) { if ((*(CK_BBOOL *)template->pValue) == TRUE) object_p->bool_attr_mask &= ~NOT_MODIFIABLE_BOOL_ON; else object_p->bool_attr_mask |= NOT_MODIFIABLE_BOOL_ON; } else { rv = CKR_ATTRIBUTE_READ_ONLY; } break; case CKA_CLASS: rv = CKR_ATTRIBUTE_READ_ONLY; break; default: rv = CKR_TEMPLATE_INCONSISTENT; } return (rv); } /* * Set the value of an attribute that is common to all key objects * (i.e. public key, private key and secret key). */ CK_RV soft_set_common_key_attribute(soft_object_t *object_p, CK_ATTRIBUTE_PTR template, boolean_t copy) { switch (template->type) { case CKA_LABEL: /* * Only the LABEL can be modified in the common storage * object attributes after the object is created. */ return (set_extra_attr_to_object(object_p, CKA_LABEL, template)); case CKA_ID: return (set_extra_attr_to_object(object_p, CKA_ID, template)); case CKA_START_DATE: return (set_extra_attr_to_object(object_p, CKA_START_DATE, template)); case CKA_END_DATE: return (set_extra_attr_to_object(object_p, CKA_END_DATE, template)); case CKA_DERIVE: return (set_bool_attr_to_object(object_p, DERIVE_BOOL_ON, template)); case CKA_KEY_TYPE: case CKA_LOCAL: case CKA_KEY_GEN_MECHANISM: return (CKR_ATTRIBUTE_READ_ONLY); default: return (soft_set_common_storage_attribute(object_p, template, copy)); } } /* * Set the value of an attribute of a Public Key Object. * * Rule: The attributes marked with footnote number "8" in the PKCS11 * spec may be modified (p.88 in PKCS11 spec.). */ CK_RV soft_set_public_key_attribute(soft_object_t *object_p, CK_ATTRIBUTE_PTR template, boolean_t copy) { CK_KEY_TYPE keytype = object_p->key_type; switch (template->type) { case CKA_SUBJECT: return (set_extra_attr_to_object(object_p, CKA_SUBJECT, template)); case CKA_ENCRYPT: return (set_bool_attr_to_object(object_p, ENCRYPT_BOOL_ON, template)); case CKA_VERIFY: return (set_bool_attr_to_object(object_p, VERIFY_BOOL_ON, template)); case CKA_VERIFY_RECOVER: return (set_bool_attr_to_object(object_p, VERIFY_RECOVER_BOOL_ON, template)); case CKA_WRAP: return (set_bool_attr_to_object(object_p, WRAP_BOOL_ON, template)); case CKA_MODULUS: case CKA_MODULUS_BITS: case CKA_PUBLIC_EXPONENT: if (keytype == CKK_RSA) return (CKR_ATTRIBUTE_READ_ONLY); break; case CKA_SUBPRIME: if ((keytype == CKK_DSA) || (keytype == CKK_X9_42_DH)) return (CKR_ATTRIBUTE_READ_ONLY); break; case CKA_PRIME: case CKA_BASE: case CKA_VALUE: if ((keytype == CKK_DSA) || (keytype == CKK_DH) || (keytype == CKK_X9_42_DH)) return (CKR_ATTRIBUTE_READ_ONLY); break; default: /* * Set the value of a common key attribute. */ return (soft_set_common_key_attribute(object_p, template, copy)); } /* * If we got this far, then the combination of key type * and requested attribute is invalid. */ return (CKR_ATTRIBUTE_TYPE_INVALID); } /* * Set the value of an attribute of a Private Key Object. * * Rule: The attributes marked with footnote number "8" in the PKCS11 * spec may be modified (p.88 in PKCS11 spec.). */ CK_RV soft_set_private_key_attribute(soft_object_t *object_p, CK_ATTRIBUTE_PTR template, boolean_t copy) { CK_KEY_TYPE keytype = object_p->key_type; switch (template->type) { case CKA_SUBJECT: return (set_extra_attr_to_object(object_p, CKA_SUBJECT, template)); case CKA_SENSITIVE: /* * Cannot set SENSITIVE to FALSE if it is already ON. */ if (((*(CK_BBOOL *)template->pValue) == B_FALSE) && (object_p->bool_attr_mask & SENSITIVE_BOOL_ON)) { return (CKR_ATTRIBUTE_READ_ONLY); } if (*(CK_BBOOL *)template->pValue) object_p->bool_attr_mask |= SENSITIVE_BOOL_ON; return (CKR_OK); case CKA_DECRYPT: return (set_bool_attr_to_object(object_p, DECRYPT_BOOL_ON, template)); case CKA_SIGN: return (set_bool_attr_to_object(object_p, SIGN_BOOL_ON, template)); case CKA_SIGN_RECOVER: return (set_bool_attr_to_object(object_p, SIGN_RECOVER_BOOL_ON, template)); case CKA_UNWRAP: return (set_bool_attr_to_object(object_p, UNWRAP_BOOL_ON, template)); case CKA_EXTRACTABLE: /* * Cannot set EXTRACTABLE to TRUE if it is already OFF. */ if ((*(CK_BBOOL *)template->pValue) && !(object_p->bool_attr_mask & EXTRACTABLE_BOOL_ON)) { return (CKR_ATTRIBUTE_READ_ONLY); } if ((*(CK_BBOOL *)template->pValue) == B_FALSE) object_p->bool_attr_mask &= ~EXTRACTABLE_BOOL_ON; return (CKR_OK); case CKA_MODULUS: 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: if (keytype == CKK_RSA) { return (CKR_ATTRIBUTE_READ_ONLY); } break; case CKA_SUBPRIME: if ((keytype == CKK_DSA) || (keytype == CKK_X9_42_DH)) return (CKR_ATTRIBUTE_READ_ONLY); break; case CKA_PRIME: case CKA_BASE: case CKA_VALUE: if ((keytype == CKK_DSA) || (keytype == CKK_DH) || (keytype == CKK_X9_42_DH)) return (CKR_ATTRIBUTE_READ_ONLY); break; case CKA_VALUE_BITS: if (keytype == CKK_DH) return (CKR_ATTRIBUTE_READ_ONLY); break; default: /* * Set the value of a common key attribute. */ return (soft_set_common_key_attribute(object_p, template, copy)); } /* * If we got this far, then the combination of key type * and requested attribute is invalid. */ return (CKR_ATTRIBUTE_TYPE_INVALID); } /* * Set the value of an attribute of a Secret Key Object. * * Rule: The attributes marked with footnote number "8" in the PKCS11 * spec may be modified (p.88 in PKCS11 spec.). */ CK_RV soft_set_secret_key_attribute(soft_object_t *object_p, CK_ATTRIBUTE_PTR template, boolean_t copy) { CK_KEY_TYPE keytype = object_p->key_type; switch (template->type) { case CKA_SENSITIVE: /* * Cannot set SENSITIVE to FALSE if it is already ON. */ if (((*(CK_BBOOL *)template->pValue) == B_FALSE) && (object_p->bool_attr_mask & SENSITIVE_BOOL_ON)) { return (CKR_ATTRIBUTE_READ_ONLY); } if (*(CK_BBOOL *)template->pValue) object_p->bool_attr_mask |= SENSITIVE_BOOL_ON; return (CKR_OK); case CKA_ENCRYPT: return (set_bool_attr_to_object(object_p, ENCRYPT_BOOL_ON, template)); case CKA_DECRYPT: return (set_bool_attr_to_object(object_p, DECRYPT_BOOL_ON, template)); case CKA_SIGN: return (set_bool_attr_to_object(object_p, SIGN_BOOL_ON, template)); case CKA_VERIFY: return (set_bool_attr_to_object(object_p, VERIFY_BOOL_ON, template)); case CKA_WRAP: return (set_bool_attr_to_object(object_p, WRAP_BOOL_ON, template)); case CKA_UNWRAP: return (set_bool_attr_to_object(object_p, UNWRAP_BOOL_ON, template)); case CKA_EXTRACTABLE: /* * Cannot set EXTRACTABLE to TRUE if it is already OFF. */ if ((*(CK_BBOOL *)template->pValue) && !(object_p->bool_attr_mask & EXTRACTABLE_BOOL_ON)) { return (CKR_ATTRIBUTE_READ_ONLY); } if ((*(CK_BBOOL *)template->pValue) == B_FALSE) object_p->bool_attr_mask &= ~EXTRACTABLE_BOOL_ON; return (CKR_OK); case CKA_VALUE: return (CKR_ATTRIBUTE_READ_ONLY); case CKA_VALUE_LEN: if ((keytype == CKK_RC4) || (keytype == CKK_GENERIC_SECRET) || (keytype == CKK_AES) || (keytype == CKK_BLOWFISH)) return (CKR_ATTRIBUTE_READ_ONLY); break; default: /* * Set the value of a common key attribute. */ return (soft_set_common_key_attribute(object_p, template, copy)); } /* * If we got this far, then the combination of key type * and requested attribute is invalid. */ return (CKR_ATTRIBUTE_TYPE_INVALID); } /* * Call the appropriate set attribute function according to the class * of object. * * The caller of this function does not hold the lock on the original * object, since this function is setting the attribute on the new object * that is being modified. * * Argument copy: TRUE when called by C_CopyObject, * FALSE when called by C_SetAttributeValue. */ CK_RV soft_set_attribute(soft_object_t *object_p, CK_ATTRIBUTE_PTR template, boolean_t copy) { CK_RV rv = CKR_OK; CK_OBJECT_CLASS class = object_p->class; switch (class) { case CKO_PUBLIC_KEY: rv = soft_set_public_key_attribute(object_p, template, copy); break; case CKO_PRIVATE_KEY: rv = soft_set_private_key_attribute(object_p, template, copy); break; case CKO_SECRET_KEY: rv = soft_set_secret_key_attribute(object_p, template, copy); break; case CKO_DOMAIN_PARAMETERS: switch (template->type) { case CKA_LABEL: /* * Only the LABEL can be modified in the common * storage object attributes after the object is * created. */ return (set_extra_attr_to_object(object_p, CKA_LABEL, template)); default: return (CKR_TEMPLATE_INCONSISTENT); } case CKO_CERTIFICATE: rv = soft_set_certificate_attribute(object_p, template, copy); break; default: /* * If the template specifies a value of an attribute * which is incompatible with other existing attributes * of the object, then fails with return code * CKR_TEMPLATE_INCONSISTENT. */ rv = CKR_TEMPLATE_INCONSISTENT; break; } return (rv); } CK_RV soft_get_public_value(soft_object_t *key, CK_ATTRIBUTE_TYPE type, uchar_t *value, uint32_t *value_len) { uint32_t len = 0; switch (type) { /* The following attributes belong to RSA */ case CKA_MODULUS: #ifdef __sparcv9 len = /* LINTED */ (uint32_t) ((biginteger_t *)OBJ_PUB_RSA_MOD(key))->big_value_len; #else /* !__sparcv9 */ len = ((biginteger_t *)OBJ_PUB_RSA_MOD(key))->big_value_len; #endif /* __sparcv9 */ /* This attribute MUST BE set */ if (len == 0 || len > *value_len) { return (CKR_ATTRIBUTE_VALUE_INVALID); } *value_len = len; (void) memcpy(value, ((biginteger_t *)OBJ_PUB_RSA_MOD(key))->big_value, *value_len); break; case CKA_PUBLIC_EXPONENT: #ifdef __sparcv9 len = /* LINTED */ (uint32_t) ((biginteger_t *)OBJ_PUB_RSA_PUBEXPO(key))->big_value_len; #else /* !__sparcv9 */ len = ((biginteger_t *)OBJ_PUB_RSA_PUBEXPO(key))->big_value_len; #endif /* __sparcv9 */ /* This attribute MUST BE set */ if (len == 0 || len > *value_len) { return (CKR_ATTRIBUTE_VALUE_INVALID); } *value_len = len; (void) memcpy(value, ((biginteger_t *)OBJ_PUB_RSA_PUBEXPO(key))->big_value, *value_len); break; /* The following attributes belong to DSA and DH */ case CKA_PRIME: if (key->key_type == CKK_DSA) #ifdef __sparcv9 len = /* LINTED */ (uint32_t) ((biginteger_t *)OBJ_PUB_DSA_PRIME(key))-> big_value_len; #else /* !__sparcv9 */ len = ((biginteger_t *)OBJ_PUB_DSA_PRIME(key))-> big_value_len; #endif /* __sparcv9 */ else #ifdef __sparcv9 len = /* LINTED */ (uint32_t) ((biginteger_t *)OBJ_PUB_DH_PRIME(key))-> big_value_len; #else /* !__sparcv9 */ len = ((biginteger_t *)OBJ_PUB_DH_PRIME(key))-> big_value_len; #endif /* __sparcv9 */ /* This attribute MUST BE set */ if (len == 0 || len > *value_len) { return (CKR_ATTRIBUTE_VALUE_INVALID); } *value_len = len; if (key->key_type == CKK_DSA) (void) memcpy(value, ((biginteger_t *)OBJ_PUB_DSA_PRIME(key))->big_value, *value_len); else (void) memcpy(value, ((biginteger_t *)OBJ_PUB_DH_PRIME(key))->big_value, *value_len); break; case CKA_SUBPRIME: #ifdef __sparcv9 len = /* LINTED */ (uint32_t) ((biginteger_t *)OBJ_PUB_DSA_SUBPRIME(key))->big_value_len; #else /* !__sparcv9 */ len = ((biginteger_t *)OBJ_PUB_DSA_SUBPRIME(key))->big_value_len; #endif /* __sparcv9 */ /* This attribute MUST BE set */ if (len == 0 || len > *value_len) { return (CKR_ATTRIBUTE_VALUE_INVALID); } *value_len = len; (void) memcpy(value, ((biginteger_t *)OBJ_PUB_DSA_SUBPRIME(key))->big_value, *value_len); break; case CKA_BASE: if (key->key_type == CKK_DSA) #ifdef __sparcv9 len = /* LINTED */ (uint32_t) ((biginteger_t *)OBJ_PUB_DSA_BASE(key))-> big_value_len; #else /* !__sparcv9 */ len = ((biginteger_t *)OBJ_PUB_DSA_BASE(key))-> big_value_len; #endif /* __sparcv9 */ else #ifdef __sparcv9 len = /* LINTED */ (uint32_t) ((biginteger_t *)OBJ_PUB_DH_BASE(key))-> big_value_len; #else /* !__sparcv9 */ len = ((biginteger_t *)OBJ_PUB_DH_BASE(key))-> big_value_len; #endif /* __sparcv9 */ /* This attribute MUST BE set */ if (len == 0 || len > *value_len) { return (CKR_ATTRIBUTE_VALUE_INVALID); } *value_len = len; if (key->key_type == CKK_DSA) (void) memcpy(value, ((biginteger_t *)OBJ_PUB_DSA_BASE(key))->big_value, *value_len); else (void) memcpy(value, ((biginteger_t *)OBJ_PUB_DH_BASE(key))->big_value, *value_len); break; case CKA_VALUE: if (key->key_type == CKK_DSA) #ifdef __sparcv9 len = /* LINTED */ (uint32_t) ((biginteger_t *)OBJ_PUB_DSA_VALUE(key))-> big_value_len; #else /* !__sparcv9 */ len = ((biginteger_t *)OBJ_PUB_DSA_VALUE(key))-> big_value_len; #endif /* __sparcv9 */ else #ifdef __sparcv9 len = /* LINTED */ (uint32_t) ((biginteger_t *)OBJ_PUB_DH_VALUE(key))-> big_value_len; #else /* !__sparcv9 */ len = ((biginteger_t *)OBJ_PUB_DH_VALUE(key))-> big_value_len; #endif /* __sparcv9 */ /* This attribute MUST BE set */ if (len == 0 || len > *value_len) { return (CKR_ATTRIBUTE_VALUE_INVALID); } *value_len = len; if (key->key_type == CKK_DSA) (void) memcpy(value, ((biginteger_t *)OBJ_PUB_DSA_VALUE(key))->big_value, *value_len); else (void) memcpy(value, ((biginteger_t *)OBJ_PUB_DH_VALUE(key))->big_value, *value_len); break; } return (CKR_OK); } CK_RV soft_get_private_value(soft_object_t *key, CK_ATTRIBUTE_TYPE type, uchar_t *value, uint32_t *value_len) { uint32_t len = 0; switch (type) { /* The following attributes belong to RSA */ case CKA_MODULUS: #ifdef __sparcv9 len = /* LINTED */ (uint32_t) ((biginteger_t *)OBJ_PRI_RSA_MOD(key))->big_value_len; #else /* !__sparcv9 */ len = ((biginteger_t *)OBJ_PRI_RSA_MOD(key))->big_value_len; #endif /* __sparcv9 */ /* This attribute MUST BE set */ if (len == 0 || len > *value_len) { return (CKR_ATTRIBUTE_VALUE_INVALID); } *value_len = len; (void) memcpy(value, ((biginteger_t *)OBJ_PRI_RSA_MOD(key))->big_value, *value_len); break; case CKA_PRIVATE_EXPONENT: #ifdef __sparcv9 len = /* LINTED */ (uint32_t) ((biginteger_t *)OBJ_PRI_RSA_PRIEXPO(key))->big_value_len; #else /* !__sparcv9 */ len = ((biginteger_t *)OBJ_PRI_RSA_PRIEXPO(key))->big_value_len; #endif /* __sparcv9 */ /* This attribute MUST BE set */ if (len == 0 || len > *value_len) { return (CKR_ATTRIBUTE_VALUE_INVALID); } *value_len = len; (void) memcpy(value, ((biginteger_t *)OBJ_PRI_RSA_PRIEXPO(key))->big_value, *value_len); break; case CKA_PRIME_1: #ifdef __sparcv9 len = /* LINTED */ (uint32_t) ((biginteger_t *)OBJ_PRI_RSA_PRIME1(key))->big_value_len; #else /* !__sparcv9 */ len = ((biginteger_t *)OBJ_PRI_RSA_PRIME1(key))->big_value_len; #endif /* __sparcv9 */ if (len > *value_len) { return (CKR_ATTRIBUTE_VALUE_INVALID); } *value_len = len; if (*value_len == 0) { return (CKR_OK); } (void) memcpy(value, ((biginteger_t *)OBJ_PRI_RSA_PRIME1(key))->big_value, *value_len); break; case CKA_PRIME_2: #ifdef __sparcv9 len = /* LINTED */ (uint32_t) ((biginteger_t *)OBJ_PRI_RSA_PRIME2(key))->big_value_len; #else /* !__sparcv9 */ len = ((biginteger_t *)OBJ_PRI_RSA_PRIME2(key))->big_value_len; #endif /* __sparcv9 */ if (len > *value_len) { return (CKR_ATTRIBUTE_VALUE_INVALID); } *value_len = len; if (*value_len == 0) { return (CKR_OK); } (void) memcpy(value, ((biginteger_t *)OBJ_PRI_RSA_PRIME2(key))->big_value, *value_len); break; case CKA_EXPONENT_1: #ifdef __sparcv9 len = /* LINTED */ (uint32_t) ((biginteger_t *)OBJ_PRI_RSA_EXPO1(key))->big_value_len; #else /* !__sparcv9 */ len = ((biginteger_t *)OBJ_PRI_RSA_EXPO1(key))->big_value_len; #endif /* __sparcv9 */ if (len > *value_len) { return (CKR_ATTRIBUTE_VALUE_INVALID); } *value_len = len; if (*value_len == 0) { return (CKR_OK); } (void) memcpy(value, ((biginteger_t *)OBJ_PRI_RSA_EXPO1(key))->big_value, *value_len); break; case CKA_EXPONENT_2: #ifdef __sparcv9 len = /* LINTED */ (uint32_t) ((biginteger_t *)OBJ_PRI_RSA_EXPO2(key))->big_value_len; #else /* !__sparcv9 */ len = ((biginteger_t *)OBJ_PRI_RSA_EXPO2(key))->big_value_len; #endif /* __sparcv9 */ if (len > *value_len) { return (CKR_ATTRIBUTE_VALUE_INVALID); } *value_len = len; if (*value_len == 0) { return (CKR_OK); } (void) memcpy(value, ((biginteger_t *)OBJ_PRI_RSA_EXPO2(key))->big_value, *value_len); break; case CKA_COEFFICIENT: #ifdef __sparcv9 len = /* LINTED */ (uint32_t) ((biginteger_t *)OBJ_PRI_RSA_COEF(key))->big_value_len; #else /* !__sparcv9 */ len = ((biginteger_t *)OBJ_PRI_RSA_COEF(key))->big_value_len; #endif /* __sparcv9 */ if (len > *value_len) { return (CKR_ATTRIBUTE_VALUE_INVALID); } *value_len = len; if (*value_len == 0) { return (CKR_OK); } (void) memcpy(value, ((biginteger_t *)OBJ_PRI_RSA_COEF(key))->big_value, *value_len); break; /* The following attributes belong to DSA and DH */ case CKA_PRIME: if (key->key_type == CKK_DSA) #ifdef __sparcv9 len = /* LINTED */ (uint32_t) ((biginteger_t *)OBJ_PRI_DSA_PRIME(key))-> big_value_len; #else /* !__sparcv9 */ len = ((biginteger_t *)OBJ_PRI_DSA_PRIME(key))-> big_value_len; #endif /* __sparcv9 */ else #ifdef __sparcv9 len = /* LINTED */ (uint32_t) ((biginteger_t *)OBJ_PRI_DH_PRIME(key))-> big_value_len; #else /* !__sparcv9 */ len = ((biginteger_t *)OBJ_PRI_DH_PRIME(key))-> big_value_len; #endif /* __sparcv9 */ /* This attribute MUST BE set */ if (len == 0 || len > *value_len) { return (CKR_ATTRIBUTE_VALUE_INVALID); } *value_len = len; if (key->key_type == CKK_DSA) (void) memcpy(value, ((biginteger_t *)OBJ_PRI_DSA_PRIME(key))->big_value, *value_len); else (void) memcpy(value, ((biginteger_t *)OBJ_PRI_DH_PRIME(key))->big_value, *value_len); break; case CKA_SUBPRIME: #ifdef __sparcv9 len = /* LINTED */ (uint32_t) ((biginteger_t *)OBJ_PRI_DSA_SUBPRIME(key))->big_value_len; #else /* !__sparcv9 */ len = ((biginteger_t *)OBJ_PRI_DSA_SUBPRIME(key))->big_value_len; #endif /* __sparcv9 */ /* This attribute MUST BE set */ if (len == 0 || len > *value_len) { return (CKR_ATTRIBUTE_VALUE_INVALID); } *value_len = len; (void) memcpy(value, ((biginteger_t *)OBJ_PRI_DSA_SUBPRIME(key))->big_value, *value_len); break; case CKA_BASE: if (key->key_type == CKK_DSA) #ifdef __sparcv9 len = /* LINTED */ (uint32_t) ((biginteger_t *)OBJ_PRI_DSA_BASE(key))-> big_value_len; #else /* !__sparcv9 */ len = ((biginteger_t *)OBJ_PRI_DSA_BASE(key))-> big_value_len; #endif /* __sparcv9 */ else #ifdef __sparcv9 len = /* LINTED */ (uint32_t) ((biginteger_t *)OBJ_PRI_DH_BASE(key))-> big_value_len; #else /* !__sparcv9 */ len = ((biginteger_t *)OBJ_PRI_DH_BASE(key))-> big_value_len; #endif /* __sparcv9 */ /* This attribute MUST BE set */ if (len == 0 || len > *value_len) { return (CKR_ATTRIBUTE_VALUE_INVALID); } *value_len = len; if (key->key_type == CKK_DSA) (void) memcpy(value, ((biginteger_t *)OBJ_PRI_DSA_BASE(key))->big_value, *value_len); else (void) memcpy(value, ((biginteger_t *)OBJ_PRI_DH_BASE(key))->big_value, *value_len); break; case CKA_VALUE: if (key->key_type == CKK_DSA) { #ifdef __sparcv9 len = /* LINTED */ (uint32_t) ((biginteger_t *)OBJ_PRI_DSA_VALUE(key))-> big_value_len; #else /* !__sparcv9 */ len = ((biginteger_t *)OBJ_PRI_DSA_VALUE(key))-> big_value_len; #endif /* __sparcv9 */ } else if (key->key_type == CKK_DH) { #ifdef __sparcv9 len = /* LINTED */ (uint32_t) ((biginteger_t *)OBJ_PRI_DH_VALUE(key))-> big_value_len; #else /* !__sparcv9 */ len = ((biginteger_t *)OBJ_PRI_DH_VALUE(key))-> big_value_len; #endif /* __sparcv9 */ } else { #ifdef __sparcv9 len = /* LINTED */ (uint32_t) ((biginteger_t *)OBJ_PRI_EC_VALUE(key))-> big_value_len; #else /* !__sparcv9 */ len = ((biginteger_t *)OBJ_PRI_EC_VALUE(key))-> big_value_len; #endif /* __sparcv9 */ } /* This attribute MUST BE set */ if (len == 0 || len > *value_len) { return (CKR_ATTRIBUTE_VALUE_INVALID); } *value_len = len; if (key->key_type == CKK_DSA) { (void) memcpy(value, ((biginteger_t *)OBJ_PRI_DSA_VALUE(key))->big_value, *value_len); } else if (key->key_type == CKK_DH) { (void) memcpy(value, ((biginteger_t *)OBJ_PRI_DH_VALUE(key))->big_value, *value_len); } else { (void) memcpy(value, ((biginteger_t *)OBJ_PRI_EC_VALUE(key))->big_value, *value_len); } break; } return (CKR_OK); } static CK_RV copy_bigint(biginteger_t *new_bigint, biginteger_t *old_bigint) { new_bigint->big_value = malloc((sizeof (CK_BYTE) * new_bigint->big_value_len)); if (new_bigint->big_value == NULL) { return (CKR_HOST_MEMORY); } (void) memcpy(new_bigint->big_value, old_bigint->big_value, (sizeof (CK_BYTE) * new_bigint->big_value_len)); return (CKR_OK); } static void free_public_key_attr(public_key_obj_t *pbk, CK_KEY_TYPE key_type) { if (pbk == NULL) { return; } switch (key_type) { case CKK_RSA: bigint_attr_cleanup(KEY_PUB_RSA_MOD(pbk)); bigint_attr_cleanup(KEY_PUB_RSA_PUBEXPO(pbk)); break; case CKK_DSA: bigint_attr_cleanup(KEY_PUB_DSA_PRIME(pbk)); bigint_attr_cleanup(KEY_PUB_DSA_SUBPRIME(pbk)); bigint_attr_cleanup(KEY_PUB_DSA_BASE(pbk)); bigint_attr_cleanup(KEY_PUB_DSA_VALUE(pbk)); break; case CKK_DH: bigint_attr_cleanup(KEY_PUB_DH_PRIME(pbk)); bigint_attr_cleanup(KEY_PUB_DH_BASE(pbk)); bigint_attr_cleanup(KEY_PUB_DH_VALUE(pbk)); break; case CKK_EC: bigint_attr_cleanup(KEY_PUB_EC_POINT(pbk)); break; case CKK_X9_42_DH: bigint_attr_cleanup(KEY_PUB_DH942_PRIME(pbk)); bigint_attr_cleanup(KEY_PUB_DH942_SUBPRIME(pbk)); bigint_attr_cleanup(KEY_PUB_DH942_BASE(pbk)); bigint_attr_cleanup(KEY_PUB_DH942_VALUE(pbk)); break; default: break; } free(pbk); } CK_RV soft_copy_public_key_attr(public_key_obj_t *old_pub_key_obj_p, public_key_obj_t **new_pub_key_obj_p, CK_KEY_TYPE key_type) { public_key_obj_t *pbk; CK_RV rv = CKR_OK; pbk = calloc(1, sizeof (public_key_obj_t)); if (pbk == NULL) { return (CKR_HOST_MEMORY); } switch (key_type) { case CKK_RSA: (void) memcpy(KEY_PUB_RSA(pbk), KEY_PUB_RSA(old_pub_key_obj_p), sizeof (rsa_pub_key_t)); /* copy modulus */ rv = copy_bigint(KEY_PUB_RSA_MOD(pbk), KEY_PUB_RSA_MOD(old_pub_key_obj_p)); if (rv != CKR_OK) { free_public_key_attr(pbk, key_type); return (rv); } /* copy public exponent */ rv = copy_bigint(KEY_PUB_RSA_PUBEXPO(pbk), KEY_PUB_RSA_PUBEXPO(old_pub_key_obj_p)); if (rv != CKR_OK) { free_public_key_attr(pbk, key_type); return (rv); } break; case CKK_DSA: (void) memcpy(KEY_PUB_DSA(pbk), KEY_PUB_DSA(old_pub_key_obj_p), sizeof (dsa_pub_key_t)); /* copy prime */ rv = copy_bigint(KEY_PUB_DSA_PRIME(pbk), KEY_PUB_DSA_PRIME(old_pub_key_obj_p)); if (rv != CKR_OK) { free_public_key_attr(pbk, key_type); return (rv); } /* copy subprime */ rv = copy_bigint(KEY_PUB_DSA_SUBPRIME(pbk), KEY_PUB_DSA_SUBPRIME(old_pub_key_obj_p)); if (rv != CKR_OK) { free_public_key_attr(pbk, key_type); return (rv); } /* copy base */ rv = copy_bigint(KEY_PUB_DSA_BASE(pbk), KEY_PUB_DSA_BASE(old_pub_key_obj_p)); if (rv != CKR_OK) { free_public_key_attr(pbk, key_type); return (rv); } /* copy value */ rv = copy_bigint(KEY_PUB_DSA_VALUE(pbk), KEY_PUB_DSA_VALUE(old_pub_key_obj_p)); if (rv != CKR_OK) { free_public_key_attr(pbk, key_type); return (rv); } break; case CKK_DH: (void) memcpy(KEY_PUB_DH(pbk), KEY_PUB_DH(old_pub_key_obj_p), sizeof (dh_pub_key_t)); /* copy prime */ rv = copy_bigint(KEY_PUB_DH_PRIME(pbk), KEY_PUB_DH_PRIME(old_pub_key_obj_p)); if (rv != CKR_OK) { free_public_key_attr(pbk, key_type); return (rv); } /* copy base */ rv = copy_bigint(KEY_PUB_DH_BASE(pbk), KEY_PUB_DH_BASE(old_pub_key_obj_p)); if (rv != CKR_OK) { free_public_key_attr(pbk, key_type); return (rv); } /* copy value */ rv = copy_bigint(KEY_PUB_DH_VALUE(pbk), KEY_PUB_DH_VALUE(old_pub_key_obj_p)); if (rv != CKR_OK) { free_public_key_attr(pbk, key_type); return (rv); } break; case CKK_EC: (void) memcpy(KEY_PUB_EC(pbk), KEY_PUB_EC(old_pub_key_obj_p), sizeof (ec_pub_key_t)); /* copy point */ rv = copy_bigint(KEY_PUB_EC_POINT(pbk), KEY_PUB_EC_POINT(old_pub_key_obj_p)); if (rv != CKR_OK) { free_public_key_attr(pbk, key_type); return (rv); } break; case CKK_X9_42_DH: (void) memcpy(KEY_PUB_DH942(pbk), KEY_PUB_DH942(old_pub_key_obj_p), sizeof (dh942_pub_key_t)); /* copy prime */ rv = copy_bigint(KEY_PUB_DH942_PRIME(pbk), KEY_PUB_DH942_PRIME(old_pub_key_obj_p)); if (rv != CKR_OK) { free_public_key_attr(pbk, key_type); return (rv); } /* copy subprime */ rv = copy_bigint(KEY_PUB_DH942_SUBPRIME(pbk), KEY_PUB_DH942_SUBPRIME(old_pub_key_obj_p)); if (rv != CKR_OK) { free_public_key_attr(pbk, key_type); return (rv); } /* copy base */ rv = copy_bigint(KEY_PUB_DH942_BASE(pbk), KEY_PUB_DH942_BASE(old_pub_key_obj_p)); if (rv != CKR_OK) { free_public_key_attr(pbk, key_type); return (rv); } /* copy value */ rv = copy_bigint(KEY_PUB_DH942_VALUE(pbk), KEY_PUB_DH942_VALUE(old_pub_key_obj_p)); if (rv != CKR_OK) { free_public_key_attr(pbk, key_type); return (rv); } break; default: break; } *new_pub_key_obj_p = pbk; return (rv); } static void free_private_key_attr(private_key_obj_t *pbk, CK_KEY_TYPE key_type) { if (pbk == NULL) { return; } switch (key_type) { case CKK_RSA: bigint_attr_cleanup(KEY_PRI_RSA_MOD(pbk)); bigint_attr_cleanup(KEY_PRI_RSA_PUBEXPO(pbk)); bigint_attr_cleanup(KEY_PRI_RSA_PRIEXPO(pbk)); bigint_attr_cleanup(KEY_PRI_RSA_PRIME1(pbk)); bigint_attr_cleanup(KEY_PRI_RSA_PRIME2(pbk)); bigint_attr_cleanup(KEY_PRI_RSA_EXPO1(pbk)); bigint_attr_cleanup(KEY_PRI_RSA_EXPO2(pbk)); bigint_attr_cleanup(KEY_PRI_RSA_COEF(pbk)); break; case CKK_DSA: bigint_attr_cleanup(KEY_PRI_DSA_PRIME(pbk)); bigint_attr_cleanup(KEY_PRI_DSA_SUBPRIME(pbk)); bigint_attr_cleanup(KEY_PRI_DSA_BASE(pbk)); bigint_attr_cleanup(KEY_PRI_DSA_VALUE(pbk)); break; case CKK_DH: bigint_attr_cleanup(KEY_PRI_DH_PRIME(pbk)); bigint_attr_cleanup(KEY_PRI_DH_BASE(pbk)); bigint_attr_cleanup(KEY_PRI_DH_VALUE(pbk)); break; case CKK_EC: bigint_attr_cleanup(KEY_PRI_EC_VALUE(pbk)); break; case CKK_X9_42_DH: bigint_attr_cleanup(KEY_PRI_DH942_PRIME(pbk)); bigint_attr_cleanup(KEY_PRI_DH942_SUBPRIME(pbk)); bigint_attr_cleanup(KEY_PRI_DH942_BASE(pbk)); bigint_attr_cleanup(KEY_PRI_DH942_VALUE(pbk)); break; default: break; } free(pbk); } CK_RV soft_copy_private_key_attr(private_key_obj_t *old_pri_key_obj_p, private_key_obj_t **new_pri_key_obj_p, CK_KEY_TYPE key_type) { CK_RV rv = CKR_OK; private_key_obj_t *pbk; pbk = calloc(1, sizeof (private_key_obj_t)); if (pbk == NULL) { return (CKR_HOST_MEMORY); } switch (key_type) { case CKK_RSA: (void) memcpy(KEY_PRI_RSA(pbk), KEY_PRI_RSA(old_pri_key_obj_p), sizeof (rsa_pri_key_t)); /* copy modulus */ rv = copy_bigint(KEY_PRI_RSA_MOD(pbk), KEY_PRI_RSA_MOD(old_pri_key_obj_p)); if (rv != CKR_OK) { free_private_key_attr(pbk, key_type); return (rv); } /* copy public exponent */ rv = copy_bigint(KEY_PRI_RSA_PUBEXPO(pbk), KEY_PRI_RSA_PUBEXPO(old_pri_key_obj_p)); if (rv != CKR_OK) { free_private_key_attr(pbk, key_type); return (rv); } /* copy private exponent */ rv = copy_bigint(KEY_PRI_RSA_PRIEXPO(pbk), KEY_PRI_RSA_PRIEXPO(old_pri_key_obj_p)); if (rv != CKR_OK) { free_private_key_attr(pbk, key_type); return (rv); } /* copy prime_1 */ rv = copy_bigint(KEY_PRI_RSA_PRIME1(pbk), KEY_PRI_RSA_PRIME1(old_pri_key_obj_p)); if (rv != CKR_OK) { free_private_key_attr(pbk, key_type); return (rv); } /* copy prime_2 */ rv = copy_bigint(KEY_PRI_RSA_PRIME2(pbk), KEY_PRI_RSA_PRIME2(old_pri_key_obj_p)); if (rv != CKR_OK) { free_private_key_attr(pbk, key_type); return (rv); } /* copy exponent_1 */ rv = copy_bigint(KEY_PRI_RSA_EXPO1(pbk), KEY_PRI_RSA_EXPO1(old_pri_key_obj_p)); if (rv != CKR_OK) { free_private_key_attr(pbk, key_type); return (rv); } /* copy exponent_2 */ rv = copy_bigint(KEY_PRI_RSA_EXPO2(pbk), KEY_PRI_RSA_EXPO2(old_pri_key_obj_p)); if (rv != CKR_OK) { free_private_key_attr(pbk, key_type); return (rv); } /* copy coefficient */ rv = copy_bigint(KEY_PRI_RSA_COEF(pbk), KEY_PRI_RSA_COEF(old_pri_key_obj_p)); if (rv != CKR_OK) { free_private_key_attr(pbk, key_type); return (rv); } break; case CKK_DSA: (void) memcpy(KEY_PRI_DSA(pbk), KEY_PRI_DSA(old_pri_key_obj_p), sizeof (dsa_pri_key_t)); /* copy prime */ rv = copy_bigint(KEY_PRI_DSA_PRIME(pbk), KEY_PRI_DSA_PRIME(old_pri_key_obj_p)); if (rv != CKR_OK) { free_private_key_attr(pbk, key_type); return (rv); } /* copy subprime */ rv = copy_bigint(KEY_PRI_DSA_SUBPRIME(pbk), KEY_PRI_DSA_SUBPRIME(old_pri_key_obj_p)); if (rv != CKR_OK) { free_private_key_attr(pbk, key_type); return (rv); } /* copy base */ rv = copy_bigint(KEY_PRI_DSA_BASE(pbk), KEY_PRI_DSA_BASE(old_pri_key_obj_p)); if (rv != CKR_OK) { free_private_key_attr(pbk, key_type); return (rv); } /* copy value */ rv = copy_bigint(KEY_PRI_DSA_VALUE(pbk), KEY_PRI_DSA_VALUE(old_pri_key_obj_p)); if (rv != CKR_OK) { free_private_key_attr(pbk, key_type); return (rv); } break; case CKK_DH: (void) memcpy(KEY_PRI_DH(pbk), KEY_PRI_DH(old_pri_key_obj_p), sizeof (dh_pri_key_t)); /* copy prime */ rv = copy_bigint(KEY_PRI_DH_PRIME(pbk), KEY_PRI_DH_PRIME(old_pri_key_obj_p)); if (rv != CKR_OK) { free_private_key_attr(pbk, key_type); return (rv); } /* copy base */ rv = copy_bigint(KEY_PRI_DH_BASE(pbk), KEY_PRI_DH_BASE(old_pri_key_obj_p)); if (rv != CKR_OK) { free_private_key_attr(pbk, key_type); return (rv); } /* copy value */ rv = copy_bigint(KEY_PRI_DH_VALUE(pbk), KEY_PRI_DH_VALUE(old_pri_key_obj_p)); if (rv != CKR_OK) { free_private_key_attr(pbk, key_type); return (rv); } break; case CKK_EC: (void) memcpy(KEY_PRI_EC(pbk), KEY_PRI_EC(old_pri_key_obj_p), sizeof (ec_pri_key_t)); /* copy value */ rv = copy_bigint(KEY_PRI_EC_VALUE(pbk), KEY_PRI_EC_VALUE(old_pri_key_obj_p)); if (rv != CKR_OK) { free_private_key_attr(pbk, key_type); return (rv); } break; case CKK_X9_42_DH: (void) memcpy(KEY_PRI_DH942(pbk), KEY_PRI_DH942(old_pri_key_obj_p), sizeof (dh942_pri_key_t)); /* copy prime */ rv = copy_bigint(KEY_PRI_DH942_PRIME(pbk), KEY_PRI_DH942_PRIME(old_pri_key_obj_p)); if (rv != CKR_OK) { free_private_key_attr(pbk, key_type); return (rv); } /* copy subprime */ rv = copy_bigint(KEY_PRI_DH942_SUBPRIME(pbk), KEY_PRI_DH942_SUBPRIME(old_pri_key_obj_p)); if (rv != CKR_OK) { free_private_key_attr(pbk, key_type); return (rv); } /* copy base */ rv = copy_bigint(KEY_PRI_DH942_BASE(pbk), KEY_PRI_DH942_BASE(old_pri_key_obj_p)); if (rv != CKR_OK) { free_private_key_attr(pbk, key_type); return (rv); } /* copy value */ rv = copy_bigint(KEY_PRI_DH942_VALUE(pbk), KEY_PRI_DH942_VALUE(old_pri_key_obj_p)); if (rv != CKR_OK) { free_private_key_attr(pbk, key_type); return (rv); } break; default: break; } *new_pri_key_obj_p = pbk; return (rv); } static void free_domain_attr(domain_obj_t *domain, CK_KEY_TYPE key_type) { if (domain == NULL) { return; } switch (key_type) { case CKK_DSA: bigint_attr_cleanup(KEY_DOM_DSA_PRIME(domain)); bigint_attr_cleanup(KEY_DOM_DSA_SUBPRIME(domain)); bigint_attr_cleanup(KEY_DOM_DSA_BASE(domain)); break; case CKK_DH: bigint_attr_cleanup(KEY_DOM_DH_PRIME(domain)); bigint_attr_cleanup(KEY_DOM_DH_BASE(domain)); break; case CKK_X9_42_DH: bigint_attr_cleanup(KEY_DOM_DH942_PRIME(domain)); bigint_attr_cleanup(KEY_DOM_DH942_SUBPRIME(domain)); bigint_attr_cleanup(KEY_DOM_DH942_BASE(domain)); break; default: break; } free(domain); } CK_RV soft_copy_domain_attr(domain_obj_t *old_domain_obj_p, domain_obj_t **new_domain_obj_p, CK_KEY_TYPE key_type) { CK_RV rv = CKR_OK; domain_obj_t *domain; domain = calloc(1, sizeof (domain_obj_t)); if (domain == NULL) { return (CKR_HOST_MEMORY); } switch (key_type) { case CKK_DSA: (void) memcpy(KEY_DOM_DSA(domain), KEY_DOM_DSA(old_domain_obj_p), sizeof (dsa_dom_key_t)); /* copy prime */ rv = copy_bigint(KEY_DOM_DSA_PRIME(domain), KEY_DOM_DSA_PRIME(old_domain_obj_p)); if (rv != CKR_OK) { free_domain_attr(domain, key_type); return (rv); } /* copy subprime */ rv = copy_bigint(KEY_DOM_DSA_SUBPRIME(domain), KEY_DOM_DSA_SUBPRIME(old_domain_obj_p)); if (rv != CKR_OK) { free_domain_attr(domain, key_type); return (rv); } /* copy base */ rv = copy_bigint(KEY_DOM_DSA_BASE(domain), KEY_DOM_DSA_BASE(old_domain_obj_p)); if (rv != CKR_OK) { free_domain_attr(domain, key_type); return (rv); } break; case CKK_DH: (void) memcpy(KEY_DOM_DH(domain), KEY_DOM_DH(old_domain_obj_p), sizeof (dh_dom_key_t)); /* copy prime */ rv = copy_bigint(KEY_DOM_DH_PRIME(domain), KEY_DOM_DH_PRIME(old_domain_obj_p)); if (rv != CKR_OK) { free_domain_attr(domain, key_type); return (rv); } /* copy base */ rv = copy_bigint(KEY_DOM_DH_BASE(domain), KEY_DOM_DH_BASE(old_domain_obj_p)); if (rv != CKR_OK) { free_domain_attr(domain, key_type); return (rv); } break; case CKK_X9_42_DH: (void) memcpy(KEY_DOM_DH942(domain), KEY_DOM_DH942(old_domain_obj_p), sizeof (dh942_dom_key_t)); /* copy prime */ rv = copy_bigint(KEY_DOM_DH942_PRIME(domain), KEY_DOM_DH942_PRIME(old_domain_obj_p)); if (rv != CKR_OK) { free_domain_attr(domain, key_type); return (rv); } /* copy subprime */ rv = copy_bigint(KEY_DOM_DH942_SUBPRIME(domain), KEY_DOM_DH942_SUBPRIME(old_domain_obj_p)); if (rv != CKR_OK) { free_domain_attr(domain, key_type); return (rv); } /* copy base */ rv = copy_bigint(KEY_DOM_DH942_BASE(domain), KEY_DOM_DH942_BASE(old_domain_obj_p)); if (rv != CKR_OK) { free_domain_attr(domain, key_type); return (rv); } break; default: break; } *new_domain_obj_p = domain; return (rv); } CK_RV soft_copy_secret_key_attr(secret_key_obj_t *old_secret_key_obj_p, secret_key_obj_t **new_secret_key_obj_p) { secret_key_obj_t *sk; sk = malloc(sizeof (secret_key_obj_t)); if (sk == NULL) { return (CKR_HOST_MEMORY); } (void) memcpy(sk, old_secret_key_obj_p, sizeof (secret_key_obj_t)); /* copy the secret key value */ sk->sk_value = malloc(sk->sk_value_len); if (sk->sk_value == NULL) { free(sk); return (CKR_HOST_MEMORY); } (void) memcpy(sk->sk_value, old_secret_key_obj_p->sk_value, (sizeof (CK_BYTE) * sk->sk_value_len)); /* * Copy the pre-expanded key schedule. */ if (old_secret_key_obj_p->key_sched != NULL && old_secret_key_obj_p->keysched_len > 0) { sk->key_sched = malloc(old_secret_key_obj_p->keysched_len); if (sk->key_sched == NULL) { freezero(sk->sk_value, sk->sk_value_len); free(sk); return (CKR_HOST_MEMORY); } sk->keysched_len = old_secret_key_obj_p->keysched_len; (void) memcpy(sk->key_sched, old_secret_key_obj_p->key_sched, sk->keysched_len); } *new_secret_key_obj_p = sk; return (CKR_OK); } /* * If CKA_CLASS not given, guess CKA_CLASS using * attributes on template . * * Some attributes are specific to an object class. If one or more * of these attributes are in the template, make a list of classes * that can have these attributes. This would speed up the search later, * because we can immediately skip an object if the class of that * object can not possibly contain one of the attributes. * */ void soft_process_find_attr(CK_OBJECT_CLASS *pclasses, CK_ULONG *num_result_pclasses, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) { ulong_t i; int j; boolean_t pub_found = B_FALSE, priv_found = B_FALSE, secret_found = B_FALSE, domain_found = B_FALSE, hardware_found = B_FALSE, cert_found = B_FALSE; int num_pub_key_attrs, num_priv_key_attrs, num_secret_key_attrs, num_domain_attrs, num_hardware_attrs, num_cert_attrs; int num_pclasses = 0; for (i = 0; i < ulCount; i++) { if (pTemplate[i].type == CKA_CLASS) { /* * don't need to guess the class, it is specified. * Just record the class, and return. */ pclasses[0] = (*((CK_OBJECT_CLASS *)pTemplate[i].pValue)); *num_result_pclasses = 1; return; } } num_pub_key_attrs = sizeof (PUB_KEY_ATTRS) / sizeof (CK_ATTRIBUTE_TYPE); num_priv_key_attrs = sizeof (PRIV_KEY_ATTRS) / sizeof (CK_ATTRIBUTE_TYPE); num_secret_key_attrs = sizeof (SECRET_KEY_ATTRS) / sizeof (CK_ATTRIBUTE_TYPE); num_domain_attrs = sizeof (DOMAIN_ATTRS) / sizeof (CK_ATTRIBUTE_TYPE); num_hardware_attrs = sizeof (HARDWARE_ATTRS) / sizeof (CK_ATTRIBUTE_TYPE); num_cert_attrs = sizeof (CERT_ATTRS) / sizeof (CK_ATTRIBUTE_TYPE); /* * Get the list of objects class that might contain * some attributes. */ for (i = 0; i < ulCount; i++) { /* * only check if this attribute can belong to public key object * class if public key object isn't already in the list */ if (!pub_found) { for (j = 0; j < num_pub_key_attrs; j++) { if (pTemplate[i].type == PUB_KEY_ATTRS[j]) { pub_found = B_TRUE; pclasses[num_pclasses++] = CKO_PUBLIC_KEY; break; } } } if (!priv_found) { for (j = 0; j < num_priv_key_attrs; j++) { if (pTemplate[i].type == PRIV_KEY_ATTRS[j]) { priv_found = B_TRUE; pclasses[num_pclasses++] = CKO_PRIVATE_KEY; break; } } } if (!secret_found) { for (j = 0; j < num_secret_key_attrs; j++) { if (pTemplate[i].type == SECRET_KEY_ATTRS[j]) { secret_found = B_TRUE; pclasses[num_pclasses++] = CKO_SECRET_KEY; break; } } } if (!domain_found) { for (j = 0; j < num_domain_attrs; j++) { if (pTemplate[i].type == DOMAIN_ATTRS[j]) { domain_found = B_TRUE; pclasses[num_pclasses++] = CKO_DOMAIN_PARAMETERS; break; } } } if (!hardware_found) { for (j = 0; j < num_hardware_attrs; j++) { if (pTemplate[i].type == HARDWARE_ATTRS[j]) { hardware_found = B_TRUE; pclasses[num_pclasses++] = CKO_HW_FEATURE; break; } } } if (!cert_found) { for (j = 0; j < num_cert_attrs; j++) { if (pTemplate[i].type == CERT_ATTRS[j]) { cert_found = B_TRUE; pclasses[num_pclasses++] = CKO_CERTIFICATE; break; } } } } *num_result_pclasses = num_pclasses; } boolean_t soft_find_match_attrs(soft_object_t *obj, CK_OBJECT_CLASS *pclasses, CK_ULONG num_pclasses, CK_ATTRIBUTE *template, CK_ULONG num_attr) { ulong_t i; CK_ATTRIBUTE *tmpl_attr, *obj_attr; cert_attr_t *cert_attr; uint64_t attr_mask; biginteger_t *bigint; boolean_t compare_attr, compare_bigint, compare_boolean; boolean_t compare_cert_val, compare_cert_type; /* * Check if the class of this object match with any * of object classes that can possibly contain the * requested attributes. */ if (num_pclasses > 0) { for (i = 0; i < num_pclasses; i++) { if (obj->class == pclasses[i]) { break; } } if (i == num_pclasses) { /* * this object can't possibly contain one or * more attributes, don't need to check this object */ return (B_FALSE); } } /* need to examine everything */ for (i = 0; i < num_attr; i++) { tmpl_attr = &(template[i]); compare_attr = B_FALSE; compare_bigint = B_FALSE; compare_boolean = B_FALSE; compare_cert_val = B_FALSE; compare_cert_type = B_FALSE; switch (tmpl_attr->type) { /* First, check the most common attributes */ case CKA_CLASS: if (*((CK_OBJECT_CLASS *)tmpl_attr->pValue) != obj->class) { return (B_FALSE); } break; case CKA_KEY_TYPE: if (*((CK_KEY_TYPE *)tmpl_attr->pValue) != obj->key_type) { return (B_FALSE); } break; case CKA_ENCRYPT: attr_mask = (obj->bool_attr_mask) & ENCRYPT_BOOL_ON; compare_boolean = B_TRUE; break; case CKA_DECRYPT: attr_mask = (obj->bool_attr_mask) & DECRYPT_BOOL_ON; compare_boolean = B_TRUE; break; case CKA_WRAP: attr_mask = (obj->bool_attr_mask) & WRAP_BOOL_ON; compare_boolean = B_TRUE; break; case CKA_UNWRAP: attr_mask = (obj->bool_attr_mask) & UNWRAP_BOOL_ON; compare_boolean = B_TRUE; break; case CKA_SIGN: attr_mask = (obj->bool_attr_mask) & SIGN_BOOL_ON; compare_boolean = B_TRUE; break; case CKA_SIGN_RECOVER: attr_mask = (obj->bool_attr_mask) & SIGN_RECOVER_BOOL_ON; compare_boolean = B_TRUE; break; case CKA_VERIFY: attr_mask = (obj->bool_attr_mask) & VERIFY_BOOL_ON; compare_boolean = B_TRUE; break; case CKA_VERIFY_RECOVER: attr_mask = (obj->bool_attr_mask) & VERIFY_RECOVER_BOOL_ON; compare_boolean = B_TRUE; break; case CKA_DERIVE: attr_mask = (obj->bool_attr_mask) & DERIVE_BOOL_ON; compare_boolean = B_TRUE; break; case CKA_LOCAL: attr_mask = (obj->bool_attr_mask) & LOCAL_BOOL_ON; compare_boolean = B_TRUE; break; case CKA_SENSITIVE: attr_mask = (obj->bool_attr_mask) & SENSITIVE_BOOL_ON; compare_boolean = B_TRUE; break; case CKA_SECONDARY_AUTH: attr_mask = (obj->bool_attr_mask) & SECONDARY_AUTH_BOOL_ON; compare_boolean = B_TRUE; break; case CKA_TRUSTED: attr_mask = (obj->bool_attr_mask) & TRUSTED_BOOL_ON; compare_boolean = B_TRUE; break; case CKA_EXTRACTABLE: attr_mask = (obj->bool_attr_mask) & EXTRACTABLE_BOOL_ON; compare_boolean = B_TRUE; break; case CKA_ALWAYS_SENSITIVE: attr_mask = (obj->bool_attr_mask) & ALWAYS_SENSITIVE_BOOL_ON; compare_boolean = B_TRUE; break; case CKA_NEVER_EXTRACTABLE: attr_mask = (obj->bool_attr_mask) & NEVER_EXTRACTABLE_BOOL_ON; compare_boolean = B_TRUE; break; case CKA_TOKEN: attr_mask = (obj->object_type) & TOKEN_OBJECT; compare_boolean = B_TRUE; break; case CKA_PRIVATE: attr_mask = (obj->object_type) & PRIVATE_OBJECT; compare_boolean = B_TRUE; break; case CKA_MODIFIABLE: { CK_BBOOL bval; attr_mask = (obj->bool_attr_mask) & NOT_MODIFIABLE_BOOL_ON; if (attr_mask) { bval = FALSE; } else { bval = TRUE; } if (bval != *((CK_BBOOL *)tmpl_attr->pValue)) { return (B_FALSE); } break; } case CKA_OWNER: /* * For X.509 attribute certificate object, get its * CKA_OWNER attribute from the x509_attr_cert_t struct. */ if ((obj->class == CKO_CERTIFICATE) && (obj->cert_type == CKC_X_509_ATTR_CERT)) { cert_attr = X509_ATTR_CERT_OWNER(obj); compare_cert_val = B_TRUE; } break; case CKA_SUBJECT: /* * For X.509 certificate object, get its CKA_SUBJECT * attribute from the x509_cert_t struct (not from * the extra_attrlistp). */ if ((obj->class == CKO_CERTIFICATE) && (obj->cert_type == CKC_X_509)) { cert_attr = X509_CERT_SUBJECT(obj); compare_cert_val = B_TRUE; break; } /*FALLTHRU*/ case CKA_ID: case CKA_START_DATE: case CKA_END_DATE: case CKA_KEY_GEN_MECHANISM: case CKA_LABEL: case CKA_ISSUER: case CKA_SERIAL_NUMBER: case CKA_AC_ISSUER: case CKA_ATTR_TYPES: /* find these attributes from extra_attrlistp */ obj_attr = get_extra_attr(tmpl_attr->type, obj); compare_attr = B_TRUE; break; case CKA_CERTIFICATE_TYPE: compare_cert_type = B_TRUE; break; case CKA_VALUE_LEN: /* only secret key has this attribute */ if (obj->class == CKO_SECRET_KEY) { if (*((CK_ULONG *)tmpl_attr->pValue) != OBJ_SEC_VALUE_LEN(obj)) { return (B_FALSE); } } else { return (B_FALSE); } break; case CKA_VALUE: switch (obj->class) { case CKO_SECRET_KEY: /* * secret_key_obj_t is the same as * biginteger_t */ bigint = (biginteger_t *)OBJ_SEC(obj); compare_bigint = B_TRUE; break; case CKO_PRIVATE_KEY: if (obj->key_type == CKK_DSA) { bigint = OBJ_PRI_DSA_VALUE(obj); } else if (obj->key_type == CKK_DH) { bigint = OBJ_PRI_DH_VALUE(obj); } else if (obj->key_type == CKK_X9_42_DH) { bigint = OBJ_PRI_DH942_VALUE(obj); } else { return (B_FALSE); } compare_bigint = B_TRUE; break; case CKO_PUBLIC_KEY: if (obj->key_type == CKK_DSA) { bigint = OBJ_PUB_DSA_VALUE(obj); } else if (obj->key_type == CKK_DH) { bigint = OBJ_PUB_DH_VALUE(obj); } else if (obj->key_type == CKK_X9_42_DH) { bigint = OBJ_PUB_DH942_VALUE(obj); } else { return (B_FALSE); } compare_bigint = B_TRUE; break; case CKO_CERTIFICATE: if (obj->cert_type == CKC_X_509) { cert_attr = X509_CERT_VALUE(obj); } else if (obj->cert_type == CKC_X_509_ATTR_CERT) { cert_attr = X509_ATTR_CERT_VALUE(obj); } compare_cert_val = B_TRUE; break; default: return (B_FALSE); } break; case CKA_MODULUS: /* only RSA public and private key have this attr */ if (obj->key_type == CKK_RSA) { if (obj->class == CKO_PUBLIC_KEY) { bigint = OBJ_PUB_RSA_MOD(obj); } else if (obj->class == CKO_PRIVATE_KEY) { bigint = OBJ_PRI_RSA_MOD(obj); } else { return (B_FALSE); } compare_bigint = B_TRUE; } else { return (B_FALSE); } break; case CKA_MODULUS_BITS: /* only RSA public key has this attribute */ if ((obj->key_type == CKK_RSA) && (obj->class == CKO_PUBLIC_KEY)) { CK_ULONG mod_bits = OBJ_PUB_RSA_MOD_BITS(obj); if (mod_bits != *((CK_ULONG *)tmpl_attr->pValue)) { return (B_FALSE); } } else { return (B_FALSE); } break; case CKA_PUBLIC_EXPONENT: /* only RSA public and private key have this attr */ if (obj->key_type == CKK_RSA) { if (obj->class == CKO_PUBLIC_KEY) { bigint = OBJ_PUB_RSA_PUBEXPO(obj); } else if (obj->class == CKO_PRIVATE_KEY) { bigint = OBJ_PRI_RSA_PUBEXPO(obj); } else { return (B_FALSE); } compare_bigint = B_TRUE; } else { return (B_FALSE); } break; case CKA_PRIVATE_EXPONENT: /* only RSA private key has this attribute */ if ((obj->key_type == CKK_RSA) && (obj->class == CKO_PRIVATE_KEY)) { bigint = OBJ_PRI_RSA_PRIEXPO(obj); compare_bigint = B_TRUE; } else { return (B_FALSE); } break; case CKA_PRIME_1: /* only RSA private key has this attribute */ if ((obj->key_type == CKK_RSA) && (obj->class == CKO_PRIVATE_KEY)) { bigint = OBJ_PRI_RSA_PRIME1(obj); compare_bigint = B_TRUE; } else { return (B_FALSE); } break; case CKA_PRIME_2: /* only RSA private key has this attribute */ if ((obj->key_type == CKK_RSA) && (obj->class == CKO_PRIVATE_KEY)) { bigint = OBJ_PRI_RSA_PRIME2(obj); compare_bigint = B_TRUE; } else { return (B_FALSE); } break; case CKA_EXPONENT_1: /* only RSA private key has this attribute */ if ((obj->key_type == CKK_RSA) && (obj->class == CKO_PRIVATE_KEY)) { bigint = OBJ_PRI_RSA_EXPO1(obj); compare_bigint = B_TRUE; } else { return (B_FALSE); } break; case CKA_EXPONENT_2: /* only RSA private key has this attribute */ if ((obj->key_type == CKK_RSA) && (obj->class == CKO_PRIVATE_KEY)) { bigint = OBJ_PRI_RSA_EXPO2(obj); compare_bigint = B_TRUE; } else { return (B_FALSE); } break; case CKA_COEFFICIENT: /* only RSA private key has this attribute */ if ((obj->key_type == CKK_RSA) && (obj->class == CKO_PRIVATE_KEY)) { bigint = OBJ_PRI_RSA_COEF(obj); compare_bigint = B_TRUE; } else { return (B_FALSE); } break; case CKA_VALUE_BITS: /* only Diffie-Hellman private key has this attr */ if ((obj->key_type == CKK_DH) && (obj->class == CKO_PRIVATE_KEY)) { CK_ULONG val_bits = OBJ_PRI_DH_VAL_BITS(obj); if (val_bits != *((CK_ULONG *)tmpl_attr->pValue)) { return (B_FALSE); } } else { return (B_FALSE); } break; case CKA_PRIME: if (obj->class == CKO_PUBLIC_KEY) { switch (obj->key_type) { case CKK_DSA: bigint = OBJ_PUB_DSA_PRIME(obj); break; case CKK_DH: bigint = OBJ_PUB_DH_PRIME(obj); break; case CKK_X9_42_DH: bigint = OBJ_PUB_DH942_PRIME(obj); break; default: return (B_FALSE); } } else if (obj->class == CKO_PRIVATE_KEY) { switch (obj->key_type) { case CKK_DSA: bigint = OBJ_PRI_DSA_PRIME(obj); break; case CKK_DH: bigint = OBJ_PRI_DH_PRIME(obj); break; case CKK_X9_42_DH: bigint = OBJ_PRI_DH942_PRIME(obj); break; default: return (B_FALSE); } } else if (obj->class == CKO_DOMAIN_PARAMETERS) { switch (obj->key_type) { case CKK_DSA: bigint = OBJ_DOM_DSA_PRIME(obj); break; case CKK_DH: bigint = OBJ_DOM_DH_PRIME(obj); break; case CKK_X9_42_DH: bigint = OBJ_DOM_DH942_PRIME(obj); break; default: return (B_FALSE); } } else { return (B_FALSE); } compare_bigint = B_TRUE; break; case CKA_SUBPRIME: if (obj->class == CKO_PUBLIC_KEY) { switch (obj->key_type) { case CKK_DSA: bigint = OBJ_PUB_DSA_SUBPRIME(obj); break; case CKK_X9_42_DH: bigint = OBJ_PUB_DH942_SUBPRIME(obj); break; default: return (B_FALSE); } } else if (obj->class == CKO_PRIVATE_KEY) { switch (obj->key_type) { case CKK_DSA: bigint = OBJ_PRI_DSA_SUBPRIME(obj); break; case CKK_X9_42_DH: bigint = OBJ_PRI_DH942_SUBPRIME(obj); break; default: return (B_FALSE); } } else if (obj->class == CKO_DOMAIN_PARAMETERS) { switch (obj->key_type) { case CKK_DSA: bigint = OBJ_DOM_DSA_SUBPRIME(obj); break; case CKK_X9_42_DH: bigint = OBJ_DOM_DH942_SUBPRIME(obj); break; default: return (B_FALSE); } } else { return (B_FALSE); } compare_bigint = B_TRUE; break; case CKA_BASE: if (obj->class == CKO_PUBLIC_KEY) { switch (obj->key_type) { case CKK_DSA: bigint = OBJ_PUB_DSA_BASE(obj); break; case CKK_DH: bigint = OBJ_PUB_DH_BASE(obj); break; case CKK_X9_42_DH: bigint = OBJ_PUB_DH942_BASE(obj); break; default: return (B_FALSE); } } else if (obj->class == CKO_PRIVATE_KEY) { switch (obj->key_type) { case CKK_DSA: bigint = OBJ_PRI_DSA_BASE(obj); break; case CKK_DH: bigint = OBJ_PRI_DH_BASE(obj); break; case CKK_X9_42_DH: bigint = OBJ_PRI_DH942_BASE(obj); break; default: return (B_FALSE); } } else if (obj->class == CKO_DOMAIN_PARAMETERS) { switch (obj->key_type) { case CKK_DSA: bigint = OBJ_DOM_DSA_BASE(obj); break; case CKK_DH: bigint = OBJ_DOM_DH_BASE(obj); break; case CKK_X9_42_DH: bigint = OBJ_DOM_DH942_BASE(obj); break; default: return (B_FALSE); } } else { return (B_FALSE); } compare_bigint = B_TRUE; break; case CKA_PRIME_BITS: if (obj->class == CKO_DOMAIN_PARAMETERS) { CK_ULONG prime_bits; if (obj->key_type == CKK_DSA) { prime_bits = OBJ_DOM_DSA_PRIME_BITS(obj); } else if (obj->key_type == CKK_DH) { prime_bits = OBJ_DOM_DH_PRIME_BITS(obj); } else if (obj->key_type == CKK_X9_42_DH) { prime_bits = OBJ_DOM_DH942_PRIME_BITS(obj); } else { return (B_FALSE); } if (prime_bits != *((CK_ULONG *)tmpl_attr->pValue)) { return (B_FALSE); } } else { return (B_FALSE); } break; case CKA_SUBPRIME_BITS: if ((obj->class == CKO_DOMAIN_PARAMETERS) && (obj->key_type == CKK_X9_42_DH)) { CK_ULONG subprime_bits = OBJ_DOM_DH942_SUBPRIME_BITS(obj); if (subprime_bits != *((CK_ULONG *)tmpl_attr->pValue)) { return (B_FALSE); } } else { return (B_FALSE); } break; default: /* * any other attributes are currently not supported. * so, it's not possible for them to be in the * object */ return (B_FALSE); } if (compare_boolean) { CK_BBOOL bval; if (attr_mask) { bval = TRUE; } else { bval = FALSE; } if (bval != *((CK_BBOOL *)tmpl_attr->pValue)) { return (B_FALSE); } } else if (compare_bigint) { if (bigint == NULL) { return (B_FALSE); } if (tmpl_attr->ulValueLen != bigint->big_value_len) { return (B_FALSE); } if (memcmp(tmpl_attr->pValue, bigint->big_value, tmpl_attr->ulValueLen) != 0) { return (B_FALSE); } } else if (compare_attr) { if (obj_attr == NULL) { /* * The attribute type is valid, and its value * has not been initialized in the object. In * this case, it only matches the template's * attribute if the template's value length * is 0. */ if (tmpl_attr->ulValueLen != 0) return (B_FALSE); } else { if (tmpl_attr->ulValueLen != obj_attr->ulValueLen) { return (B_FALSE); } if (memcmp(tmpl_attr->pValue, obj_attr->pValue, tmpl_attr->ulValueLen) != 0) { return (B_FALSE); } } } else if (compare_cert_val) { if (cert_attr == NULL) { /* specific attribute not found */ return (B_FALSE); } if (tmpl_attr->ulValueLen != cert_attr->length) { return (B_FALSE); } if (memcmp(tmpl_attr->pValue, cert_attr->value, tmpl_attr->ulValueLen) != 0) { return (B_FALSE); } } else if (compare_cert_type) { if (memcmp(tmpl_attr->pValue, &(obj->cert_type), tmpl_attr->ulValueLen) != 0) { return (B_FALSE); } } } return (B_TRUE); } CK_ATTRIBUTE_PTR get_extra_attr(CK_ATTRIBUTE_TYPE type, soft_object_t *obj) { CK_ATTRIBUTE_INFO_PTR tmp; tmp = obj->extra_attrlistp; while (tmp != NULL) { if (tmp->attr.type == type) { return (&(tmp->attr)); } tmp = tmp->next; } /* if get there, the specified attribute is not found */ return (NULL); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright (c) 2018, Joyent, Inc. */ #include #include #include #include #include #include #include "softSession.h" #include "softObject.h" #include "softCrypt.h" #include CK_RV soft_blowfish_crypt_init_common(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism, soft_object_t *key_p, boolean_t encrypt) { size_t size; soft_blowfish_ctx_t *soft_blowfish_ctx; soft_blowfish_ctx = calloc(1, sizeof (soft_blowfish_ctx_t)); if (soft_blowfish_ctx == NULL) { return (CKR_HOST_MEMORY); } soft_blowfish_ctx->key_sched = blowfish_alloc_keysched(&size, 0); if (soft_blowfish_ctx->key_sched == NULL) { free(soft_blowfish_ctx); return (CKR_HOST_MEMORY); } soft_blowfish_ctx->keysched_len = size; (void) pthread_mutex_lock(&session_p->session_mutex); if (encrypt) { /* Called by C_EncryptInit */ session_p->encrypt.context = soft_blowfish_ctx; session_p->encrypt.mech.mechanism = pMechanism->mechanism; } else { /* Called by C_DecryptInit */ session_p->decrypt.context = soft_blowfish_ctx; session_p->decrypt.mech.mechanism = pMechanism->mechanism; } (void) pthread_mutex_unlock(&session_p->session_mutex); /* * If this is a non-sensitive key and it does NOT have * a key schedule yet, then allocate one and expand it. * Otherwise, if it's a non-sensitive key, and it DOES have * a key schedule already attached to it, just copy the * pre-expanded schedule to the context and avoid the * extra key schedule expansion operation. */ if (!(key_p->bool_attr_mask & SENSITIVE_BOOL_ON)) { if (OBJ_KEY_SCHED(key_p) == NULL) { void *ks; (void) pthread_mutex_lock(&key_p->object_mutex); if (OBJ_KEY_SCHED(key_p) == NULL) { ks = blowfish_alloc_keysched(&size, 0); if (ks == NULL) { (void) pthread_mutex_unlock( &key_p->object_mutex); free(soft_blowfish_ctx); return (CKR_HOST_MEMORY); } blowfish_init_keysched(OBJ_SEC_VALUE(key_p), (OBJ_SEC_VALUE_LEN(key_p) * 8), ks); OBJ_KEY_SCHED_LEN(key_p) = size; OBJ_KEY_SCHED(key_p) = ks; } (void) pthread_mutex_unlock(&key_p->object_mutex); } (void) memcpy(soft_blowfish_ctx->key_sched, OBJ_KEY_SCHED(key_p), OBJ_KEY_SCHED_LEN(key_p)); soft_blowfish_ctx->keysched_len = OBJ_KEY_SCHED_LEN(key_p); } else { /* * Initialize key schedule for Blowfish. * blowfish_init_keysched() requires key length in bits. */ blowfish_init_keysched(OBJ_SEC_VALUE(key_p), (OBJ_SEC_VALUE_LEN(key_p) * 8), soft_blowfish_ctx->key_sched); } return (CKR_OK); } /* * soft_blowfish_encrypt_common() * * Arguments: * session_p: pointer to soft_session_t struct * pData: pointer to the input data to be encrypted * ulDataLen: length of the input data * pEncrypted: pointer to the output data after encryption * pulEncryptedLen: pointer to the length of the output data * update: boolean flag indicates caller is soft_encrypt * or soft_encrypt_update * * Description: * This function calls the corresponding encrypt routine based * on the mechanism. * * Returns: * CKR_OK: success * CKR_BUFFER_TOO_SMALL: the output buffer provided by application * is too small * CKR_FUNCTION_FAILED: encrypt function failed * CKR_DATA_LEN_RANGE: the input data is not a multiple of blocksize */ CK_RV soft_blowfish_encrypt_common(soft_session_t *session_p, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pEncrypted, CK_ULONG_PTR pulEncryptedLen, boolean_t update) { int rc = 0; CK_RV rv = CKR_OK; soft_blowfish_ctx_t *soft_blowfish_ctx = (soft_blowfish_ctx_t *)session_p->encrypt.context; blowfish_ctx_t *blowfish_ctx; CK_BYTE *in_buf = NULL; CK_BYTE *out_buf = NULL; CK_ULONG out_len; CK_ULONG total_len; CK_ULONG remain; crypto_data_t out; /* * Blowfish only takes input length that is a multiple of blocksize * for C_Encrypt function with the mechanism CKM_BLOWFISH_CBC. * */ if (!update) { if ((ulDataLen % BLOWFISH_BLOCK_LEN) != 0) { rv = CKR_DATA_LEN_RANGE; goto cleanup; } out_len = ulDataLen; /* * If application asks for the length of the output buffer * to hold the ciphertext? */ if (pEncrypted == NULL) { *pulEncryptedLen = out_len; return (CKR_OK); } /* Is the application-supplied buffer large enough? */ if (*pulEncryptedLen < out_len) { *pulEncryptedLen = out_len; return (CKR_BUFFER_TOO_SMALL); } in_buf = pData; out_buf = pEncrypted; } else { /* * Called by C_EncryptUpdate * * Add the lengths of last remaining data and current * plaintext together to get the total input length. */ total_len = soft_blowfish_ctx->remain_len + ulDataLen; /* * If the total input length is less than one blocksize, * we will need to delay encryption until when more data * comes in next C_EncryptUpdate or when C_EncryptFinal * is called. */ if (total_len < BLOWFISH_BLOCK_LEN) { if (pEncrypted != NULL) { /* * Save input data and its length in * the remaining buffer of BLOWFISH context. */ (void) memcpy(soft_blowfish_ctx->data + soft_blowfish_ctx->remain_len, pData, ulDataLen); soft_blowfish_ctx->remain_len += ulDataLen; } /* Set encrypted data length to 0. */ *pulEncryptedLen = 0; return (CKR_OK); } /* Compute the length of remaing data. */ remain = total_len % BLOWFISH_BLOCK_LEN; /* * Make sure that the output length is a multiple of * blocksize. */ out_len = total_len - remain; /* * If application asks for the length of the output buffer * to hold the ciphertext? */ if (pEncrypted == NULL) { *pulEncryptedLen = out_len; return (CKR_OK); } /* Is the application-supplied buffer large enough? */ if (*pulEncryptedLen < out_len) { *pulEncryptedLen = out_len; return (CKR_BUFFER_TOO_SMALL); } if (soft_blowfish_ctx->remain_len != 0) { /* * Copy last remaining data and current input data * to the output buffer. */ (void) memmove(pEncrypted + soft_blowfish_ctx->remain_len, pData, out_len - soft_blowfish_ctx->remain_len); (void) memcpy(pEncrypted, soft_blowfish_ctx->data, soft_blowfish_ctx->remain_len); bzero(soft_blowfish_ctx->data, soft_blowfish_ctx->remain_len); in_buf = pEncrypted; } else { in_buf = pData; } out_buf = pEncrypted; } /* * Begin Encryption now. */ out.cd_format = CRYPTO_DATA_RAW; out.cd_offset = 0; out.cd_length = out_len; out.cd_raw.iov_base = (char *)out_buf; out.cd_raw.iov_len = out_len; /* Encrypt multiple blocks of data. */ rc = blowfish_encrypt_contiguous_blocks( (blowfish_ctx_t *)soft_blowfish_ctx->blowfish_cbc, (char *)in_buf, out_len, &out); if (rc == 0) { *pulEncryptedLen = out_len; if (update) { /* * For encrypt update, if there is remaining data, * save it and it's length in the context. */ if (remain != 0) (void) memcpy(soft_blowfish_ctx->data, pData + (ulDataLen - remain), remain); soft_blowfish_ctx->remain_len = remain; return (CKR_OK); } } else { *pulEncryptedLen = 0; rv = CKR_FUNCTION_FAILED; } cleanup: (void) pthread_mutex_lock(&session_p->session_mutex); blowfish_ctx = (blowfish_ctx_t *)soft_blowfish_ctx->blowfish_cbc; freezero(blowfish_ctx, sizeof (cbc_ctx_t)); freezero(soft_blowfish_ctx->key_sched, soft_blowfish_ctx->keysched_len); freezero(session_p->encrypt.context, sizeof (soft_blowfish_ctx_t)); session_p->encrypt.context = NULL; (void) pthread_mutex_unlock(&session_p->session_mutex); return (rv); } CK_RV soft_blowfish_decrypt_common(soft_session_t *session_p, CK_BYTE_PTR pEncrypted, CK_ULONG ulEncryptedLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen, boolean_t update) { int rc = 0; CK_RV rv = CKR_OK; soft_blowfish_ctx_t *soft_blowfish_ctx = (soft_blowfish_ctx_t *)session_p->decrypt.context; blowfish_ctx_t *blowfish_ctx; CK_BYTE *in_buf = NULL; CK_BYTE *out_buf = NULL; CK_ULONG out_len; CK_ULONG total_len; CK_ULONG remain; crypto_data_t out; /* * Blowfish only takes input length that is a multiple of 16 bytes * for C_Decrypt function using CKM_BLOWFISH_CBC. */ if (!update) { /* Called by C_Decrypt */ if ((ulEncryptedLen % BLOWFISH_BLOCK_LEN) != 0) { rv = CKR_ENCRYPTED_DATA_LEN_RANGE; goto cleanup; } /* * If application asks for the length of the output buffer * to hold the plaintext? */ if (pData == NULL) { *pulDataLen = ulEncryptedLen; return (CKR_OK); } /* Is the application-supplied buffer large enough? */ if (*pulDataLen < ulEncryptedLen) { *pulDataLen = ulEncryptedLen; return (CKR_BUFFER_TOO_SMALL); } out_len = ulEncryptedLen; in_buf = pEncrypted; out_buf = pData; } else { /* * Called by C_DecryptUpdate * * Add the lengths of last remaining data and current * input data together to get the total input length. */ total_len = soft_blowfish_ctx->remain_len + ulEncryptedLen; if (total_len < BLOWFISH_BLOCK_LEN) { if (pData != NULL) { (void) memcpy(soft_blowfish_ctx->data + soft_blowfish_ctx->remain_len, pEncrypted, ulEncryptedLen); soft_blowfish_ctx->remain_len += ulEncryptedLen; } /* Set output data length to 0. */ *pulDataLen = 0; return (CKR_OK); } /* Compute the length of remaining data. */ remain = total_len % BLOWFISH_BLOCK_LEN; /* * Make sure that the output length is a multiple of * blocksize. */ out_len = total_len - remain; /* * if application asks for the length of the output buffer * to hold the plaintext? */ if (pData == NULL) { *pulDataLen = out_len; return (CKR_OK); } /* * Is the application-supplied buffer large enough? */ if (*pulDataLen < out_len) { *pulDataLen = out_len; return (CKR_BUFFER_TOO_SMALL); } if (soft_blowfish_ctx->remain_len != 0) { /* * Copy last remaining data and current input data * to the output buffer. */ (void) memmove(pData + soft_blowfish_ctx->remain_len, pEncrypted, out_len - soft_blowfish_ctx->remain_len); (void) memcpy(pData, soft_blowfish_ctx->data, soft_blowfish_ctx->remain_len); bzero(soft_blowfish_ctx->data, soft_blowfish_ctx->remain_len); in_buf = pData; } else { in_buf = pEncrypted; } out_buf = pData; } out.cd_format = CRYPTO_DATA_RAW; out.cd_offset = 0; out.cd_length = out_len; out.cd_raw.iov_base = (char *)out_buf; out.cd_raw.iov_len = out_len; /* Decrypt multiple blocks of data. */ rc = blowfish_decrypt_contiguous_blocks( (blowfish_ctx_t *)soft_blowfish_ctx->blowfish_cbc, (char *)in_buf, out_len, &out); if (rc == 0) { *pulDataLen = out_len; if (update) { /* * For decrypt update, if there is remaining data, * save it and its length in the context. */ if (remain != 0) (void) memcpy(soft_blowfish_ctx->data, pEncrypted + (ulEncryptedLen - remain), remain); soft_blowfish_ctx->remain_len = remain; return (CKR_OK); } } else { *pulDataLen = 0; rv = CKR_FUNCTION_FAILED; } cleanup: (void) pthread_mutex_lock(&session_p->session_mutex); blowfish_ctx = (blowfish_ctx_t *)soft_blowfish_ctx->blowfish_cbc; free(blowfish_ctx); freezero(soft_blowfish_ctx->key_sched, soft_blowfish_ctx->keysched_len); freezero(session_p->decrypt.context, sizeof (soft_blowfish_ctx_t)); session_p->decrypt.context = NULL; (void) pthread_mutex_unlock(&session_p->session_mutex); return (rv); } /* * Allocate and initialize a context for BLOWFISH CBC mode of operation. */ void * blowfish_cbc_ctx_init(void *key_sched, size_t size, uint8_t *ivec) { cbc_ctx_t *cbc_ctx; if ((cbc_ctx = calloc(1, sizeof (cbc_ctx_t))) == NULL) return (NULL); cbc_ctx->cbc_keysched = key_sched; (void) memcpy(&cbc_ctx->cbc_iv[0], ivec, BLOWFISH_BLOCK_LEN); cbc_ctx->cbc_lastp = (uint8_t *)&(cbc_ctx->cbc_iv); cbc_ctx->cbc_keysched_len = size; cbc_ctx->cbc_flags |= CBC_MODE; return (cbc_ctx); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, Joyent, Inc. */ #ifndef _SOFTCRYPT_H #define _SOFTCRYPT_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #include #include "softObject.h" #include "softSession.h" #define DES_MAC_LEN (DES_BLOCK_LEN / 2) typedef struct soft_des_ctx { void *key_sched; /* pointer to key schedule */ size_t keysched_len; /* Length of the key schedule */ uint8_t ivec[DES_BLOCK_LEN]; /* initialization vector */ uint8_t data[DES_BLOCK_LEN]; /* for use by update */ size_t remain_len; /* for use by update */ void *des_cbc; /* to be used by CBC mode */ CK_KEY_TYPE key_type; /* used to determine DES or DES3 */ size_t mac_len; /* digest len in bytes */ } soft_des_ctx_t; typedef struct soft_blowfish_ctx { void *key_sched; /* pointer to key schedule */ size_t keysched_len; /* Length of the key schedule */ uint8_t ivec[BLOWFISH_BLOCK_LEN]; /* initialization vector */ uint8_t data[BLOWFISH_BLOCK_LEN]; /* for use by update */ size_t remain_len; /* for use by update */ void *blowfish_cbc; /* to be used by CBC mode */ } soft_blowfish_ctx_t; /* * For sign/verify operations, the hash generated is AES_BLOCK_LEN bytes long, * however for CKM_AES_CMAC_GENERAL, one can specify a smaller hash size if * desired (the output being the output of CKM_AES_CMAC truncated to the * specified size). Since this size is specified in the C_{Sign,Verify}Init() * call, we must carry it through to the C_{Sign,Verify}Final() call via * the mac_len field. * * Note that the context pointed to by aes_ctx is cleaned up as part of the * soft_aes_encrypt() calls. */ typedef struct soft_aes_sign_ctx { aes_ctx_t *aes_ctx; size_t mac_len; } soft_aes_sign_ctx_t; /* * Function Prototypes. */ void *des_cbc_ctx_init(void *, size_t, uint8_t *, CK_KEY_TYPE); CK_RV soft_des_crypt_init_common(soft_session_t *, CK_MECHANISM_PTR, soft_object_t *, boolean_t); CK_RV soft_des_encrypt_common(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR, boolean_t); CK_RV soft_des_decrypt_common(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR, boolean_t); CK_RV soft_des_sign_verify_common(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR, boolean_t, boolean_t); CK_RV soft_des_sign_verify_init_common(soft_session_t *, CK_MECHANISM_PTR, soft_object_t *, boolean_t); CK_RV soft_des_mac_sign_verify_update(soft_session_t *, CK_BYTE_PTR, CK_ULONG); void soft_add_pkcs7_padding(CK_BYTE *, int, CK_ULONG); CK_RV soft_remove_pkcs7_padding(CK_BYTE *, CK_ULONG, CK_ULONG *); CK_RV soft_arcfour_crypt_init(soft_session_t *, CK_MECHANISM_PTR, soft_object_t *, boolean_t); CK_RV soft_arcfour_crypt(crypto_active_op_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR); CK_RV soft_aes_crypt_init_common(soft_session_t *, CK_MECHANISM_PTR, soft_object_t *, boolean_t); CK_RV soft_aes_encrypt(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR); CK_RV soft_aes_decrypt(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR); CK_RV soft_aes_encrypt_update(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR); CK_RV soft_aes_decrypt_update(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR); CK_RV soft_aes_encrypt_final(soft_session_t *, CK_BYTE_PTR, CK_ULONG_PTR); CK_RV soft_aes_decrypt_final(soft_session_t *, CK_BYTE_PTR, CK_ULONG_PTR); CK_RV soft_aes_decrypt_common(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR, boolean_t); CK_RV soft_aes_sign_verify_common(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR, boolean_t, boolean_t); CK_RV soft_aes_sign_verify_init_common(soft_session_t *, CK_MECHANISM_PTR, soft_object_t *, boolean_t); CK_RV soft_aes_mac_sign_verify_update(soft_session_t *, CK_BYTE_PTR, CK_ULONG); void soft_aes_free_ctx(aes_ctx_t *); void *blowfish_cbc_ctx_init(void *, size_t, uint8_t *); CK_RV soft_blowfish_crypt_init_common(soft_session_t *, CK_MECHANISM_PTR, soft_object_t *, boolean_t); CK_RV soft_blowfish_encrypt_common(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR, boolean_t); CK_RV soft_blowfish_decrypt_common(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR, boolean_t); #ifdef __cplusplus } #endif #endif /* _SOFTCRYPT_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, Joyent, Inc. */ #include #include #include #include #include #include #include #include #include "softSession.h" #include "softObject.h" #include "softCrypt.h" #include "softOps.h" /* * Allocate context for the active encryption or decryption operation, and * generate DES or DES3 key schedule to speed up the operation. */ CK_RV soft_des_crypt_init_common(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism, soft_object_t *key_p, boolean_t encrypt) { size_t size; soft_des_ctx_t *soft_des_ctx; soft_des_ctx = calloc(1, sizeof (soft_des_ctx_t)); if (soft_des_ctx == NULL) { return (CKR_HOST_MEMORY); } /* Allocate key schedule for DES or DES3 based on key type. */ if (key_p->key_type == CKK_DES) soft_des_ctx->key_sched = des_alloc_keysched(&size, DES, 0); else soft_des_ctx->key_sched = des_alloc_keysched(&size, DES3, 0); if (soft_des_ctx->key_sched == NULL) { free(soft_des_ctx); return (CKR_HOST_MEMORY); } soft_des_ctx->keysched_len = size; soft_des_ctx->key_type = key_p->key_type; (void) pthread_mutex_lock(&session_p->session_mutex); if (encrypt) { /* Called by C_EncryptInit. */ session_p->encrypt.context = soft_des_ctx; session_p->encrypt.mech.mechanism = pMechanism->mechanism; } else { /* Called by C_DecryptInit. */ session_p->decrypt.context = soft_des_ctx; session_p->decrypt.mech.mechanism = pMechanism->mechanism; } (void) pthread_mutex_unlock(&session_p->session_mutex); /* * If this is a non-sensitive key and it does NOT have * a key schedule yet, then allocate one and expand it. * Otherwise, if its a non-sensitive key, and it DOES have * a key schedule already attached to it, just copy the * pre-expanded schedule to the context and avoid the * extra key schedule expansion operation. */ if (!(key_p->bool_attr_mask & SENSITIVE_BOOL_ON)) { if (OBJ_KEY_SCHED(key_p) == NULL) { void *ks; (void) pthread_mutex_lock(&key_p->object_mutex); if (OBJ_KEY_SCHED(key_p) == NULL) { if (key_p->key_type == CKK_DES) ks = des_alloc_keysched(&size, DES, 0); else ks = des_alloc_keysched(&size, DES3, 0); if (ks == NULL) { (void) pthread_mutex_unlock( &key_p->object_mutex); free(soft_des_ctx); return (CKR_HOST_MEMORY); } /* Initialize key schedule for DES or DES3. */ if (key_p->key_type == CKK_DES) des_init_keysched( OBJ_SEC(key_p)->sk_value, DES, ks); else if (key_p->key_type == CKK_DES2) /* * DES3 encryption/decryption needs to * support a DES2 key. */ des_init_keysched( OBJ_SEC(key_p)->sk_value, DES2, ks); else des_init_keysched( OBJ_SEC(key_p)->sk_value, DES3, ks); OBJ_KEY_SCHED_LEN(key_p) = size; OBJ_KEY_SCHED(key_p) = ks; } (void) pthread_mutex_unlock(&key_p->object_mutex); } /* Copy the pre-expanded key schedule from the key object */ (void) memcpy(soft_des_ctx->key_sched, OBJ_KEY_SCHED(key_p), OBJ_KEY_SCHED_LEN(key_p)); soft_des_ctx->keysched_len = OBJ_KEY_SCHED_LEN(key_p); } else { /* for sensitive keys, we cannot cache the key schedule */ if (key_p->key_type == CKK_DES) des_init_keysched(OBJ_SEC(key_p)->sk_value, DES, soft_des_ctx->key_sched); else if (key_p->key_type == CKK_DES2) /* * DES3 encryption/decryption needs to * support a DES2 key. */ des_init_keysched(OBJ_SEC(key_p)->sk_value, DES2, soft_des_ctx->key_sched); else des_init_keysched(OBJ_SEC(key_p)->sk_value, DES3, soft_des_ctx->key_sched); } return (CKR_OK); } /* * soft_des_encrypt_common() * * Arguments: * session_p: pointer to soft_session_t struct * pData: pointer to the input data to be encrypted * ulDataLen: length of the input data * pEncrypted: pointer to the output data after encryption * pulEncryptedLen: pointer to the length of the output data * update: boolean flag indicates caller is soft_encrypt * or soft_encrypt_update * * Description: * This function calls the corresponding encrypt routine based * on the mechanism. * * Returns: * CKR_OK: success * CKR_BUFFER_TOO_SMALL: the output buffer provided by application * is too small * CKR_FUNCTION_FAILED: encrypt function failed * CKR_DATA_LEN_RANGE: the input data is not a multiple of blocksize */ CK_RV soft_des_encrypt_common(soft_session_t *session_p, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pEncrypted, CK_ULONG_PTR pulEncryptedLen, boolean_t update) { int rc = 0; CK_RV rv = CKR_OK; soft_des_ctx_t *soft_des_ctx = (soft_des_ctx_t *)session_p->encrypt.context; des_ctx_t *des_ctx; CK_MECHANISM_TYPE mechanism = session_p->encrypt.mech.mechanism; CK_BYTE *in_buf = NULL; CK_BYTE *out_buf = NULL; CK_ULONG out_len; CK_ULONG total_len; CK_ULONG remain; boolean_t pad_mechanism = B_FALSE; pad_mechanism = (mechanism == CKM_DES_CBC_PAD || mechanism == CKM_DES3_CBC_PAD); /* * DES only takes input length that is a multiple of blocksize * for C_Encrypt function with the mechanism CKM_DES_ECB or * CKM_DES_CBC. * * DES allows any input length for C_Encrypt function with the * mechanism CKM_DES_CBC_PAD and for C_EncryptUpdate function. */ if (!update && !pad_mechanism) { if ((ulDataLen % DES_BLOCK_LEN) != 0) { rv = CKR_DATA_LEN_RANGE; goto cleanup; } } if (!update) { /* * Called by C_Encrypt */ if (pad_mechanism) { /* * For CKM_DES_CBC_PAD, compute output length to * count for the padding. If the length of input * data is a multiple of blocksize, then make output * length to be the sum of the input length and * one blocksize. Otherwise, output length will * be rounded up to the next multiple of blocksize. */ out_len = DES_BLOCK_LEN * (ulDataLen / DES_BLOCK_LEN + 1); } else { /* * For non-padding mode, the output length will * be same as the input length. */ out_len = ulDataLen; } /* * If application asks for the length of the output buffer * to hold the ciphertext? */ if (pEncrypted == NULL) { *pulEncryptedLen = out_len; return (CKR_OK); } /* Is the application-supplied buffer large enough? */ if (*pulEncryptedLen < out_len) { *pulEncryptedLen = out_len; return (CKR_BUFFER_TOO_SMALL); } /* Encrypt pad bytes in a separate operation */ if (pad_mechanism) { out_len -= DES_BLOCK_LEN; } in_buf = pData; out_buf = pEncrypted; } else { /* * Called by C_EncryptUpdate * * Add the lengths of last remaining data and current * plaintext together to get the total input length. */ total_len = soft_des_ctx->remain_len + ulDataLen; /* * If the total input length is less than one blocksize, * or if the total input length is just one blocksize and * the mechanism is CKM_DES_CBC_PAD, we will need to delay * encryption until when more data comes in next * C_EncryptUpdate or when C_EncryptFinal is called. */ if ((total_len < DES_BLOCK_LEN) || (pad_mechanism && (total_len == DES_BLOCK_LEN))) { if (pData != NULL) { /* * Save input data and its length in * the remaining buffer of DES context. */ (void) memcpy(soft_des_ctx->data + soft_des_ctx->remain_len, pData, ulDataLen); soft_des_ctx->remain_len += ulDataLen; } /* Set encrypted data length to 0. */ *pulEncryptedLen = 0; return (CKR_OK); } /* Compute the length of remaing data. */ remain = total_len % DES_BLOCK_LEN; /* * Make sure that the output length is a multiple of * blocksize. */ out_len = total_len - remain; /* * If application asks for the length of the output buffer * to hold the ciphertext? */ if (pEncrypted == NULL) { *pulEncryptedLen = out_len; return (CKR_OK); } /* Is the application-supplied buffer large enough? */ if (*pulEncryptedLen < out_len) { *pulEncryptedLen = out_len; return (CKR_BUFFER_TOO_SMALL); } if (soft_des_ctx->remain_len != 0) { /* * Copy last remaining data and current input data * to the output buffer. */ (void) memmove(pEncrypted + soft_des_ctx->remain_len, pData, out_len - soft_des_ctx->remain_len); (void) memcpy(pEncrypted, soft_des_ctx->data, soft_des_ctx->remain_len); bzero(soft_des_ctx->data, soft_des_ctx->remain_len); in_buf = pEncrypted; } else { in_buf = pData; } out_buf = pEncrypted; } /* * Begin Encryption now. */ switch (mechanism) { case CKM_DES_ECB: case CKM_DES3_ECB: { ulong_t i; uint8_t *tmp_inbuf; uint8_t *tmp_outbuf; for (i = 0; i < out_len; i += DES_BLOCK_LEN) { tmp_inbuf = &in_buf[i]; tmp_outbuf = &out_buf[i]; /* Crunch one block of data for DES. */ if (soft_des_ctx->key_type == CKK_DES) (void) des_crunch_block( soft_des_ctx->key_sched, tmp_inbuf, tmp_outbuf, B_FALSE); else (void) des3_crunch_block( soft_des_ctx->key_sched, tmp_inbuf, tmp_outbuf, B_FALSE); } if (update) { /* * For encrypt update, if there is remaining * data, save it and its length in the context. */ if (remain != 0) (void) memcpy(soft_des_ctx->data, pData + (ulDataLen - remain), remain); soft_des_ctx->remain_len = remain; } *pulEncryptedLen = out_len; break; } case CKM_DES_CBC: case CKM_DES_CBC_PAD: case CKM_DES3_CBC: case CKM_DES3_CBC_PAD: { crypto_data_t out; out.cd_format = CRYPTO_DATA_RAW; out.cd_offset = 0; out.cd_length = out_len; out.cd_raw.iov_base = (char *)out_buf; out.cd_raw.iov_len = out_len; /* Encrypt multiple blocks of data. */ rc = des_encrypt_contiguous_blocks( (des_ctx_t *)soft_des_ctx->des_cbc, (char *)in_buf, out_len, &out); if (rc != 0) goto encrypt_failed; if (update) { /* * For encrypt update, if there is remaining data, * save it and its length in the context. */ if (remain != 0) (void) memcpy(soft_des_ctx->data, pData + (ulDataLen - remain), remain); soft_des_ctx->remain_len = remain; } else if (pad_mechanism) { /* * Save the remainder of the input * block in a temporary block because * we don't want to overrun the input buffer * by tacking on pad bytes. */ CK_BYTE tmpblock[DES_BLOCK_LEN]; (void) memcpy(tmpblock, in_buf + out_len, ulDataLen - out_len); soft_add_pkcs7_padding(tmpblock + (ulDataLen - out_len), DES_BLOCK_LEN, ulDataLen - out_len); out.cd_offset = out_len; out.cd_length = DES_BLOCK_LEN; out.cd_raw.iov_base = (char *)out_buf; out.cd_raw.iov_len = out_len + DES_BLOCK_LEN; /* Encrypt last block containing pad bytes. */ rc = des_encrypt_contiguous_blocks( (des_ctx_t *)soft_des_ctx->des_cbc, (char *)tmpblock, DES_BLOCK_LEN, &out); out_len += DES_BLOCK_LEN; } if (rc == 0) { *pulEncryptedLen = out_len; break; } encrypt_failed: *pulEncryptedLen = 0; rv = CKR_FUNCTION_FAILED; goto cleanup; } } /* end switch */ if (update) return (CKR_OK); /* * The following code will be executed if the caller is * soft_encrypt() or an error occurred. The encryption * operation will be terminated so we need to do some cleanup. */ cleanup: (void) pthread_mutex_lock(&session_p->session_mutex); des_ctx = (des_ctx_t *)soft_des_ctx->des_cbc; free(des_ctx); freezero(soft_des_ctx->key_sched, soft_des_ctx->keysched_len); freezero(session_p->encrypt.context, sizeof (soft_des_ctx_t)); session_p->encrypt.context = NULL; (void) pthread_mutex_unlock(&session_p->session_mutex); return (rv); } /* * soft_des_decrypt_common() * * Arguments: * session_p: pointer to soft_session_t struct * pEncrypted: pointer to the input data to be decrypted * ulEncryptedLen: length of the input data * pData: pointer to the output data * pulDataLen: pointer to the length of the output data * Update: boolean flag indicates caller is soft_decrypt * or soft_decrypt_update * * Description: * This function calls the corresponding decrypt routine based * on the mechanism. * * Returns: * CKR_OK: success * CKR_BUFFER_TOO_SMALL: the output buffer provided by application * is too small * CKR_ENCRYPTED_DATA_LEN_RANGE: the input data is not a multiple * of blocksize * CKR_FUNCTION_FAILED: decrypt function failed */ CK_RV soft_des_decrypt_common(soft_session_t *session_p, CK_BYTE_PTR pEncrypted, CK_ULONG ulEncryptedLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen, boolean_t update) { int rc = 0; CK_RV rv = CKR_OK; soft_des_ctx_t *soft_des_ctx = (soft_des_ctx_t *)session_p->decrypt.context; des_ctx_t *des_ctx; CK_MECHANISM_TYPE mechanism = session_p->decrypt.mech.mechanism; CK_BYTE *in_buf = NULL; CK_BYTE *out_buf = NULL; CK_ULONG out_len; CK_ULONG total_len; CK_ULONG remain; boolean_t pad_mechanism = B_FALSE; pad_mechanism = (mechanism == CKM_DES_CBC_PAD || mechanism == CKM_DES3_CBC_PAD); /* * DES only takes input length that is a multiple of 8 bytes * for C_Decrypt function with the mechanism CKM_DES_ECB, * CKM_DES_CBC or CKM_DES_CBC_PAD. * * DES allows any input length for C_DecryptUpdate function. */ if (!update) { /* * Called by C_Decrypt */ if ((ulEncryptedLen % DES_BLOCK_LEN) != 0) { rv = CKR_ENCRYPTED_DATA_LEN_RANGE; goto cleanup; } /* * If application asks for the length of the output buffer * to hold the plaintext? */ if (pData == NULL) { *pulDataLen = ulEncryptedLen; return (CKR_OK); } /* Is the application-supplied buffer large enough? */ if (!pad_mechanism) { if (*pulDataLen < ulEncryptedLen) { *pulDataLen = ulEncryptedLen; return (CKR_BUFFER_TOO_SMALL); } /* Set output length same as input length. */ out_len = ulEncryptedLen; } else { /* * For CKM_DES_CBC_PAD, we don't know how * many bytes for padding at this time, so * we'd assume one block was padded. */ if (*pulDataLen < (ulEncryptedLen - DES_BLOCK_LEN)) { *pulDataLen = ulEncryptedLen - DES_BLOCK_LEN; return (CKR_BUFFER_TOO_SMALL); } out_len = ulEncryptedLen - DES_BLOCK_LEN; } in_buf = pEncrypted; out_buf = pData; } else { /* * Called by C_DecryptUpdate * * Add the lengths of last remaining data and current * input data together to get the total input length. */ total_len = soft_des_ctx->remain_len + ulEncryptedLen; /* * If the total input length is less than one blocksize, * or if the total input length is just one blocksize and * the mechanism is CKM_DES_CBC_PAD, we will need to delay * decryption until when more data comes in next * C_DecryptUpdate or when C_DecryptFinal is called. */ if ((total_len < DES_BLOCK_LEN) || (pad_mechanism && (total_len == DES_BLOCK_LEN))) { if (pEncrypted != NULL) { /* * Save input data and its length in * the remaining buffer of DES context. */ (void) memcpy(soft_des_ctx->data + soft_des_ctx->remain_len, pEncrypted, ulEncryptedLen); soft_des_ctx->remain_len += ulEncryptedLen; } /* Set output data length to 0. */ *pulDataLen = 0; return (CKR_OK); } /* Compute the length of remaing data. */ remain = total_len % DES_BLOCK_LEN; /* * Make sure that the output length is a multiple of * blocksize. */ out_len = total_len - remain; if (pad_mechanism) { /* * If the input data length is a multiple of * blocksize, then save the last block of input * data in the remaining buffer. C_DecryptFinal * will handle this last block of data. */ if (remain == 0) { remain = DES_BLOCK_LEN; out_len -= DES_BLOCK_LEN; } } /* * If application asks for the length of the output buffer * to hold the plaintext? */ if (pData == NULL) { *pulDataLen = out_len; return (CKR_OK); } /* * Is the application-supplied buffer large enough? */ if (*pulDataLen < out_len) { *pulDataLen = out_len; return (CKR_BUFFER_TOO_SMALL); } if (soft_des_ctx->remain_len != 0) { /* * Copy last remaining data and current input data * to the output buffer. */ (void) memmove(pData + soft_des_ctx->remain_len, pEncrypted, out_len - soft_des_ctx->remain_len); (void) memcpy(pData, soft_des_ctx->data, soft_des_ctx->remain_len); bzero(soft_des_ctx->data, soft_des_ctx->remain_len); in_buf = pData; } else { in_buf = pEncrypted; } out_buf = pData; } /* * Begin Decryption. */ switch (mechanism) { case CKM_DES_ECB: case CKM_DES3_ECB: { uint8_t *tmp_inbuf; uint8_t *tmp_outbuf; ulong_t i; for (i = 0; i < out_len; i += DES_BLOCK_LEN) { tmp_inbuf = &in_buf[i]; tmp_outbuf = &out_buf[i]; /* Crunch one block of data for DES. */ if (soft_des_ctx->key_type == CKK_DES) (void) des_crunch_block( soft_des_ctx->key_sched, tmp_inbuf, tmp_outbuf, B_TRUE); else (void) des3_crunch_block( soft_des_ctx->key_sched, tmp_inbuf, tmp_outbuf, B_TRUE); } if (update) { /* * For decrypt update, if there is remaining * data, save it and its length in the context. */ if (remain != 0) (void) memcpy(soft_des_ctx->data, pEncrypted + (ulEncryptedLen - remain), remain); soft_des_ctx->remain_len = remain; } *pulDataLen = out_len; break; } case CKM_DES_CBC: case CKM_DES_CBC_PAD: case CKM_DES3_CBC: case CKM_DES3_CBC_PAD: { crypto_data_t out; CK_ULONG rem_len; uint8_t last_block[DES_BLOCK_LEN]; out.cd_format = CRYPTO_DATA_RAW; out.cd_offset = 0; out.cd_length = out_len; out.cd_raw.iov_base = (char *)out_buf; out.cd_raw.iov_len = out_len; /* Decrypt multiple blocks of data. */ rc = des_decrypt_contiguous_blocks( (des_ctx_t *)soft_des_ctx->des_cbc, (char *)in_buf, out_len, &out); if (rc != 0) goto decrypt_failed; if (pad_mechanism && !update) { /* Decrypt last block containing pad bytes. */ out.cd_offset = 0; out.cd_length = DES_BLOCK_LEN; out.cd_raw.iov_base = (char *)last_block; out.cd_raw.iov_len = DES_BLOCK_LEN; /* Decrypt last block containing pad bytes. */ rc = des_decrypt_contiguous_blocks( (des_ctx_t *)soft_des_ctx->des_cbc, (char *)in_buf + out_len, DES_BLOCK_LEN, &out); if (rc != 0) goto decrypt_failed; /* * Remove padding bytes after decryption of * ciphertext block to produce the original * plaintext. */ rv = soft_remove_pkcs7_padding(last_block, DES_BLOCK_LEN, &rem_len); if (rv == CKR_OK) { if (rem_len != 0) (void) memcpy(out_buf + out_len, last_block, rem_len); *pulDataLen = out_len + rem_len; } else { *pulDataLen = 0; goto cleanup; } } else { *pulDataLen = out_len; } if (update) { /* * For decrypt update, if there is remaining data, * save it and its length in the context. */ if (remain != 0) (void) memcpy(soft_des_ctx->data, pEncrypted + (ulEncryptedLen - remain), remain); soft_des_ctx->remain_len = remain; } if (rc == 0) break; decrypt_failed: *pulDataLen = 0; rv = CKR_FUNCTION_FAILED; goto cleanup; } } /* end switch */ if (update) return (CKR_OK); /* * The following code will be executed if the caller is * soft_decrypt() or an error occurred. The decryption * operation will be terminated so we need to do some cleanup. */ cleanup: (void) pthread_mutex_lock(&session_p->session_mutex); des_ctx = (des_ctx_t *)soft_des_ctx->des_cbc; free(des_ctx); freezero(soft_des_ctx->key_sched, soft_des_ctx->keysched_len); freezero(session_p->decrypt.context, sizeof (soft_des_ctx_t)); (void) pthread_mutex_unlock(&session_p->session_mutex); return (rv); } /* * Allocate and initialize a context for DES CBC mode of operation. */ void * des_cbc_ctx_init(void *key_sched, size_t size, uint8_t *ivec, CK_KEY_TYPE type) { cbc_ctx_t *cbc_ctx; if ((cbc_ctx = calloc(1, sizeof (cbc_ctx_t))) == NULL) return (NULL); cbc_ctx->cbc_keysched = key_sched; (void) memcpy(&cbc_ctx->cbc_iv[0], ivec, DES_BLOCK_LEN); cbc_ctx->cbc_lastp = (uint8_t *)&cbc_ctx->cbc_iv[0]; cbc_ctx->cbc_keysched_len = size; if (type == CKK_DES) cbc_ctx->cbc_flags |= CBC_MODE; else cbc_ctx->cbc_flags |= CBC_MODE | DES3_STRENGTH; return (cbc_ctx); } /* * Allocate and initialize DES contexts for both signing and encrypting, * saving both context pointers in the session struct. For general-length DES * MAC, check the length in the parameter to see if it is in the right range. */ CK_RV soft_des_sign_verify_init_common(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism, soft_object_t *key_p, boolean_t sign_op) { soft_des_ctx_t *soft_des_ctx; CK_MECHANISM encrypt_mech; CK_RV rv; if ((key_p->class != CKO_SECRET_KEY) || (key_p->key_type != CKK_DES)) { return (CKR_KEY_TYPE_INCONSISTENT); } /* allocate memory for the sign/verify context */ soft_des_ctx = malloc(sizeof (soft_des_ctx_t)); if (soft_des_ctx == NULL) { return (CKR_HOST_MEMORY); } soft_des_ctx->key_type = key_p->key_type; /* initialization vector is zero for DES MAC */ bzero(soft_des_ctx->ivec, DES_BLOCK_LEN); switch (pMechanism->mechanism) { case CKM_DES_MAC_GENERAL: if (pMechanism->ulParameterLen != sizeof (CK_MAC_GENERAL_PARAMS)) { free(soft_des_ctx); return (CKR_MECHANISM_PARAM_INVALID); } if (*(CK_MAC_GENERAL_PARAMS *)pMechanism->pParameter > DES_BLOCK_LEN) { free(soft_des_ctx); return (CKR_MECHANISM_PARAM_INVALID); } soft_des_ctx->mac_len = *((CK_MAC_GENERAL_PARAMS_PTR) pMechanism->pParameter); /*FALLTHRU*/ case CKM_DES_MAC: /* * For non-general DES MAC, output is always half as * large as block size */ if (pMechanism->mechanism == CKM_DES_MAC) { soft_des_ctx->mac_len = DES_MAC_LEN; } /* allocate a context for DES encryption */ encrypt_mech.mechanism = CKM_DES_CBC_PAD; encrypt_mech.pParameter = (void *)soft_des_ctx->ivec; encrypt_mech.ulParameterLen = DES_BLOCK_LEN; rv = soft_encrypt_init_internal(session_p, &encrypt_mech, key_p); if (rv != CKR_OK) { free(soft_des_ctx); return (rv); } (void) pthread_mutex_lock(&session_p->session_mutex); if (sign_op) { session_p->sign.context = soft_des_ctx; session_p->sign.mech.mechanism = pMechanism->mechanism; } else { session_p->verify.context = soft_des_ctx; session_p->verify.mech.mechanism = pMechanism->mechanism; } (void) pthread_mutex_unlock(&session_p->session_mutex); break; } return (CKR_OK); } /* * Called by soft_sign(), soft_sign_final(), soft_verify() or * soft_verify_final(). */ CK_RV soft_des_sign_verify_common(soft_session_t *session_p, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSigned, CK_ULONG_PTR pulSignedLen, boolean_t sign_op, boolean_t Final) { soft_des_ctx_t *soft_des_ctx_sign_verify; soft_des_ctx_t *soft_des_ctx_encrypt; CK_RV rv; CK_BYTE *pEncrypted = NULL; CK_ULONG ulEncryptedLen = 0; uint8_t remainder; CK_BYTE last_block[DES_BLOCK_LEN]; des_ctx_t *des_ctx = NULL; if (sign_op) { soft_des_ctx_sign_verify = (soft_des_ctx_t *)session_p->sign.context; if (soft_des_ctx_sign_verify->mac_len == 0) { *pulSignedLen = 0; goto clean_exit; } /* Application asks for the length of the output buffer. */ if (pSigned == NULL) { *pulSignedLen = soft_des_ctx_sign_verify->mac_len; return (CKR_OK); } /* Is the application-supplied buffer large enough? */ if (*pulSignedLen < soft_des_ctx_sign_verify->mac_len) { *pulSignedLen = soft_des_ctx_sign_verify->mac_len; return (CKR_BUFFER_TOO_SMALL); } } else { soft_des_ctx_sign_verify = (soft_des_ctx_t *)session_p->verify.context; } if (Final) { soft_des_ctx_encrypt = (soft_des_ctx_t *)session_p->encrypt.context; /* * If there is data left in the buffer from a previous * SignUpdate() call, pass enough zeroed data to a * soft_sign_update call to pad the remainder */ if (soft_des_ctx_encrypt->remain_len != 0) { bzero(last_block, DES_BLOCK_LEN); ulEncryptedLen = DES_BLOCK_LEN; /* * By passing a buffer to soft_encrypt_final, * we force it to pad the remaining block * and encrypt it. */ rv = soft_encrypt_final(session_p, last_block, &ulEncryptedLen); if (rv != CKR_OK) { goto clean_exit; } } else { /* * The last block of enciphered data is stored in: * soft_des_ctx_encrypt->des_cbc->des_ctx->dc_lastp * Copy that data to last_block */ soft_des_ctx_encrypt = (soft_des_ctx_t *)session_p->encrypt.context; des_ctx = (des_ctx_t *)soft_des_ctx_encrypt->des_cbc; (void) memcpy(last_block, des_ctx->dc_lastp, DES_BLOCK_LEN); /* * Passing a NULL output buffer here * forces the routine to just return. */ rv = soft_encrypt_final(session_p, NULL, &ulEncryptedLen); } } else { /* * If the input length is not multiple of block size, then * determine the correct encrypted data length by rounding */ remainder = ulDataLen % DES_BLOCK_LEN; /* * Because we always use DES_CBC_PAD mechanism * for sign/verify operations, the input will * be padded to the next 8 byte boundary. * Adjust the length fields here accordingly. */ ulEncryptedLen = ulDataLen + (DES_BLOCK_LEN - remainder); pEncrypted = malloc(sizeof (CK_BYTE) * ulEncryptedLen); if (pEncrypted == NULL) { rv = CKR_HOST_MEMORY; goto clean_exit; } /* * Pad the last block with zeros by copying pData into a zeroed * pEncrypted. Then pass pEncrypted into soft_encrypt as input */ bzero(pEncrypted, ulEncryptedLen); (void) memcpy(pEncrypted, pData, ulDataLen); rv = soft_encrypt(session_p, pEncrypted, ulDataLen, pEncrypted, &ulEncryptedLen); (void) memcpy(last_block, &pEncrypted[ulEncryptedLen - DES_BLOCK_LEN], DES_BLOCK_LEN); } if (rv == CKR_OK) { *pulSignedLen = soft_des_ctx_sign_verify->mac_len; /* the leftmost mac_len bytes of last_block is our MAC */ (void) memcpy(pSigned, last_block, *pulSignedLen); } clean_exit: (void) pthread_mutex_lock(&session_p->session_mutex); /* soft_encrypt_common() has freed the encrypt context */ if (sign_op) { free(session_p->sign.context); session_p->sign.context = NULL; } else { free(session_p->verify.context); session_p->verify.context = NULL; } session_p->encrypt.flags = 0; (void) pthread_mutex_unlock(&session_p->session_mutex); if (pEncrypted) { free(pEncrypted); } return (rv); } /* * Called by soft_sign_update() */ CK_RV soft_des_mac_sign_verify_update(soft_session_t *session_p, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) { /* * The DES MAC is calculated by taking the specified number of * left-most bytes within the last block of * encrypted data, while the context of the multi-part * encryption stores the block necessary for XORing with the * input as per cipher block chaining . Therefore, none of the * intermediary encrypted blocks of data are necessary for * the DES MAC, and we can create a placeholder local buffer * for the encrypted data, which is immediately throw away. */ soft_des_ctx_t *soft_des_ctx_encrypt; CK_BYTE *pEncrypted = NULL; CK_ULONG ulEncryptedLen; CK_ULONG total_len; uint8_t remainder; CK_RV rv; soft_des_ctx_encrypt = (soft_des_ctx_t *)session_p->encrypt.context; /* Avoid the malloc if we won't be encrypting any data */ total_len = soft_des_ctx_encrypt->remain_len + ulPartLen; if (total_len < DES_BLOCK_LEN) { rv = soft_encrypt_update(session_p, pPart, ulPartLen, NULL, &ulEncryptedLen); } else { remainder = ulPartLen % DES_BLOCK_LEN; /* round up to the nearest multiple of block size */ ulEncryptedLen = ulPartLen + (DES_BLOCK_LEN - remainder); pEncrypted = malloc(sizeof (CK_BYTE) * ulEncryptedLen); if (pEncrypted != NULL) { rv = soft_encrypt_update(session_p, pPart, ulPartLen, pEncrypted, &ulEncryptedLen); free(pEncrypted); } else { rv = CKR_HOST_MEMORY; } } return (rv); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include "softGlobal.h" #include "softSession.h" #include "softObject.h" #include "softDH.h" #include "softCrypt.h" /* * This function takes a converted big integer of the specified attribute * as an octet string and stores it in the corresponding key object. */ static CK_RV soft_genDHkey_set_attribute(soft_object_t *key, CK_ATTRIBUTE_TYPE type, uchar_t *buf, uint32_t buflen, boolean_t public) { CK_RV rv = CKR_OK; biginteger_t *dst = NULL; biginteger_t src; switch (type) { case CKA_VALUE: if (public) dst = OBJ_PUB_DH_VALUE(key); else dst = OBJ_PRI_DH_VALUE(key); break; case CKA_PRIME: dst = OBJ_PRI_DH_PRIME(key); break; case CKA_BASE: dst = OBJ_PRI_DH_BASE(key); break; } if ((rv = dup_bigint_attr(&src, buf, buflen)) != CKR_OK) goto cleanexit; /* Copy the attribute in the key object. */ copy_bigint_attr(&src, dst); cleanexit: /* No need to free big_value because dst holds it now after copy. */ return (rv); } /* * This function covers the DH Key agreement. */ CK_RV soft_dh_genkey_pair(soft_object_t *pubkey, soft_object_t *prikey) { CK_RV rv; CK_ATTRIBUTE template; uchar_t prime[MAX_KEY_ATTR_BUFLEN]; uint32_t prime_len = sizeof (prime); uchar_t base[MAX_KEY_ATTR_BUFLEN]; uint32_t base_len = sizeof (base); uint32_t value_bits; uchar_t private_x[MAX_KEY_ATTR_BUFLEN]; uchar_t public_y[MAX_KEY_ATTR_BUFLEN]; DHbytekey k; if ((pubkey->class != CKO_PUBLIC_KEY) || (pubkey->key_type != CKK_DH)) { return (CKR_KEY_TYPE_INCONSISTENT); } if ((prikey->class != CKO_PRIVATE_KEY) || (prikey->key_type != CKK_DH)) { return (CKR_KEY_TYPE_INCONSISTENT); } /* Get private-value length in bits */ template.pValue = malloc(sizeof (CK_ULONG)); if (template.pValue == NULL) { return (CKR_HOST_MEMORY); } template.ulValueLen = sizeof (CK_ULONG); rv = get_ulong_attr_from_object(OBJ_PRI_DH_VAL_BITS(prikey), &template); if (rv != CKR_OK) { free(template.pValue); return (rv); } #ifdef __sparcv9 /* LINTED */ value_bits = (uint32_t)(*((CK_ULONG *)(template.pValue))); #else /* !__sparcv9 */ value_bits = *((CK_ULONG *)(template.pValue)); #endif /* __sparcv9 */ free(template.pValue); /* * The input to the first phase shall be the Diffie-Hellman * parameters, which include prime, base, and private-value length. */ rv = soft_get_public_value(pubkey, CKA_PRIME, prime, &prime_len); if (rv != CKR_OK) { return (rv); } rv = soft_get_public_value(pubkey, CKA_BASE, base, &base_len); if (rv != CKR_OK) { goto ret; } /* Inputs to DH key pair generation. */ k.prime = prime; k.prime_bits = CRYPTO_BYTES2BITS(prime_len); k.base = base; k.base_bytes = base_len; k.value_bits = value_bits; k.rfunc = (IS_TOKEN_OBJECT(pubkey) || IS_TOKEN_OBJECT(prikey)) ? pkcs11_get_random : pkcs11_get_urandom; /* Outputs from DH key pair generation. */ k.private_x = private_x; k.public_y = public_y; /* If value_bits is 0, it will return as same size as prime */ if ((rv = dh_genkey_pair(&k)) != CKR_OK) { goto ret; } /* * The integer public value y shall be converted to an octet * string PV of length k, the public value. */ if ((rv = soft_genDHkey_set_attribute(pubkey, CKA_VALUE, public_y, prime_len, B_TRUE)) != CKR_OK) { goto ret; } /* Convert the big integer private value to an octet string. */ if ((rv = soft_genDHkey_set_attribute(prikey, CKA_VALUE, private_x, CRYPTO_BITS2BYTES(k.value_bits), B_FALSE)) != CKR_OK) { goto ret; } /* Convert the big integer prime to an octet string. */ if ((rv = soft_genDHkey_set_attribute(prikey, CKA_PRIME, prime, CRYPTO_BITS2BYTES(k.prime_bits), B_FALSE)) != CKR_OK) { goto ret; } /* Convert the big integer base to an octet string. */ if ((rv = soft_genDHkey_set_attribute(prikey, CKA_BASE, base, k.base_bytes, B_FALSE)) != CKR_OK) { goto ret; } /* Update private-value length in bits; could have been 0 before */ OBJ_PRI_DH_VAL_BITS(prikey) = k.value_bits; ret: return (rv); } /* ARGSUSED3 */ CK_RV soft_dh_key_derive(soft_object_t *basekey, soft_object_t *secretkey, void *publicvalue, size_t publicvaluelen) { CK_RV rv; uchar_t privatevalue[MAX_KEY_ATTR_BUFLEN]; uint32_t privatevaluelen = sizeof (privatevalue); uchar_t privateprime[MAX_KEY_ATTR_BUFLEN]; uint32_t privateprimelen = sizeof (privateprime); uchar_t key[MAX_KEY_ATTR_BUFLEN]; uint32_t keylen; DHbytekey k; rv = soft_get_private_value(basekey, CKA_VALUE, privatevalue, &privatevaluelen); if (rv != CKR_OK) { return (rv); } rv = soft_get_private_value(basekey, CKA_PRIME, privateprime, &privateprimelen); if (rv != CKR_OK) { goto ret; } /* keylen may be 0 if CKA_VALUE_LEN did not specify */ keylen = OBJ_SEC_VALUE_LEN(secretkey); if (keylen > sizeof (key)) { /* check for overflow */ rv = CKR_ATTRIBUTE_VALUE_INVALID; goto ret; } k.prime = privateprime; k.prime_bits = CRYPTO_BYTES2BITS(privateprimelen); k.value_bits = CRYPTO_BYTES2BITS(privatevaluelen); k.private_x = privatevalue; k.public_y = publicvalue; k.rfunc = NULL; /* keylen may be modified if it was 0 or conflicts with key type */ rv = dh_key_derive(&k, secretkey->key_type, key, &keylen, 0); if (rv != CKR_OK) { goto ret; } if ((OBJ_SEC_VALUE(secretkey) = malloc(keylen)) == NULL) { rv = CKR_HOST_MEMORY; goto ret; } OBJ_SEC_VALUE_LEN(secretkey) = keylen; (void) memcpy(OBJ_SEC_VALUE(secretkey), key, keylen); ret: return (rv); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _SOFTDH_H #define _SOFTDH_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include "softObject.h" #include "softSession.h" /* * Function Prototypes. */ CK_RV soft_dh_genkey_pair(soft_object_t *, soft_object_t *); CK_RV soft_dh_key_derive(soft_object_t *, soft_object_t *, void *, size_t); #ifdef __cplusplus } #endif #endif /* _SOFTDH_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include "softGlobal.h" #include "softSession.h" #include "softObject.h" #include "softDSA.h" #include "softOps.h" #include "softMAC.h" #include "softCrypt.h" /* * Allocate a DSA context for the active sign or verify operation. * This function is called without the session lock held. */ CK_RV soft_dsa_sign_verify_init_common(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism, soft_object_t *key_p, boolean_t sign) { soft_dsa_ctx_t *dsa_ctx; CK_MECHANISM digest_mech; soft_object_t *tmp_key = NULL; CK_RV rv; if (sign) { if ((key_p->class != CKO_PRIVATE_KEY) || (key_p->key_type != CKK_DSA)) return (CKR_KEY_TYPE_INCONSISTENT); } else { if ((key_p->class != CKO_PUBLIC_KEY) || (key_p->key_type != CKK_DSA)) return (CKR_KEY_TYPE_INCONSISTENT); } if (pMechanism->mechanism == CKM_DSA_SHA1) { digest_mech.mechanism = CKM_SHA_1; rv = soft_digest_init_internal(session_p, &digest_mech); if (rv != CKR_OK) return (rv); } dsa_ctx = malloc(sizeof (soft_dsa_ctx_t)); if (dsa_ctx == NULL) { return (CKR_HOST_MEMORY); } /* * Make a copy of the signature or verification key, and save it * in the DSA crypto context since it will be used later for * signing/verification. We don't want to hold any object reference * on this original key while doing signing/verification. */ (void) pthread_mutex_lock(&key_p->object_mutex); rv = soft_copy_object(key_p, &tmp_key, SOFT_COPY_OBJ_ORIG_SH, NULL); if ((rv != CKR_OK) || (tmp_key == NULL)) { /* Most likely we ran out of space. */ (void) pthread_mutex_unlock(&key_p->object_mutex); free(dsa_ctx); return (rv); } /* No need to hold the lock on the old object. */ (void) pthread_mutex_unlock(&key_p->object_mutex); dsa_ctx->key = tmp_key; (void) pthread_mutex_lock(&session_p->session_mutex); if (sign) { session_p->sign.context = dsa_ctx; session_p->sign.mech.mechanism = pMechanism->mechanism; } else { session_p->verify.context = dsa_ctx; session_p->verify.mech.mechanism = pMechanism->mechanism; } (void) pthread_mutex_unlock(&session_p->session_mutex); return (CKR_OK); } static CK_RV local_dsa_sign(soft_object_t *key, CK_BYTE_PTR in, CK_ULONG inlen, CK_BYTE_PTR out) { CK_RV rv; uchar_t q[MAX_KEY_ATTR_BUFLEN]; uchar_t p[MAX_KEY_ATTR_BUFLEN]; uchar_t g[MAX_KEY_ATTR_BUFLEN]; uchar_t x[MAX_KEY_ATTR_BUFLEN]; uint_t qlen = sizeof (q); uint_t plen = sizeof (p); uint_t glen = sizeof (g); uint_t xlen = sizeof (x); DSAbytekey k; rv = soft_get_private_value(key, CKA_PRIME, p, &plen); if (rv != CKR_OK) { goto clean1; } rv = soft_get_private_value(key, CKA_SUBPRIME, q, &qlen); if (rv != CKR_OK) { goto clean1; } rv = soft_get_private_value(key, CKA_BASE, g, &glen); if (rv != CKR_OK) { goto clean1; } rv = soft_get_private_value(key, CKA_VALUE, x, &xlen); if (rv != CKR_OK) { goto clean1; } k.prime = p; k.prime_bits = CRYPTO_BYTES2BITS(plen); k.subprime = q; k.subprime_bits = CRYPTO_BYTES2BITS(qlen); k.base = g; k.base_bytes = glen; k.private_x_bits = CRYPTO_BYTES2BITS(xlen); k.private_x = x; k.rfunc = NULL; rv = dsa_sign(&k, in, inlen, out); clean1: return (rv); } static CK_RV local_dsa_verify(soft_object_t *key, CK_BYTE_PTR data, CK_BYTE_PTR sig) { CK_RV rv; uchar_t g[MAX_KEY_ATTR_BUFLEN]; uchar_t y[MAX_KEY_ATTR_BUFLEN]; uchar_t p[MAX_KEY_ATTR_BUFLEN]; uchar_t q[MAX_KEY_ATTR_BUFLEN]; uint_t glen = sizeof (g); uint_t ylen = sizeof (y); uint_t plen = sizeof (p); uint_t qlen = sizeof (q); DSAbytekey k; rv = soft_get_public_value(key, CKA_PRIME, p, &plen); if (rv != CKR_OK) { goto clean1; } rv = soft_get_public_value(key, CKA_SUBPRIME, q, &qlen); if (rv != CKR_OK) { goto clean1; } rv = soft_get_public_value(key, CKA_BASE, g, &glen); if (rv != CKR_OK) { goto clean1; } rv = soft_get_public_value(key, CKA_VALUE, y, &ylen); if (rv != CKR_OK) { goto clean1; } k.prime = p; k.prime_bits = CRYPTO_BYTES2BITS(plen); k.subprime = q; k.subprime_bits = CRYPTO_BYTES2BITS(qlen); k.base = g; k.base_bytes = glen; k.public_y_bits = CRYPTO_BYTES2BITS(ylen); k.public_y = y; k.rfunc = NULL; rv = dsa_verify(&k, data, sig); clean1: return (rv); } CK_RV soft_dsa_digest_sign_common(soft_session_t *session_p, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSigned, CK_ULONG_PTR pulSignedLen, boolean_t Final) { CK_RV rv = CKR_OK; CK_BYTE hash[SHA1_HASH_SIZE]; /* space enough for SHA1 and MD5 */ CK_ULONG hash_len = SHA1_HASH_SIZE; soft_dsa_ctx_t *dsa_ctx = session_p->sign.context; soft_object_t *key = dsa_ctx->key; /* Check arguments before performing message digest. */ if (pSigned == NULL) { /* Application asks for the length of the output buffer. */ *pulSignedLen = DSA_SIGNATURE_LENGTH; goto clean1; } /* Is the application-supplied buffer large enough? */ if (*pulSignedLen < DSA_SIGNATURE_LENGTH) { *pulSignedLen = DSA_SIGNATURE_LENGTH; rv = CKR_BUFFER_TOO_SMALL; goto clean1; } if (Final) { rv = soft_digest_final(session_p, hash, &hash_len); } else { rv = soft_digest(session_p, pData, ulDataLen, hash, &hash_len); } if (rv != CKR_OK) { /* free the signature key */ soft_cleanup_object(key); free(key); goto clean_exit; } /* * Now, we are ready to sign the data * soft_dsa_sign() will free the signature key. */ rv = soft_dsa_sign(session_p, hash, hash_len, pSigned, pulSignedLen); clean_exit: (void) pthread_mutex_lock(&session_p->session_mutex); /* soft_digest_common() has freed the digest context */ session_p->digest.flags = 0; (void) pthread_mutex_unlock(&session_p->session_mutex); clean1: return (rv); } CK_RV soft_dsa_sign(soft_session_t *session_p, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSigned, CK_ULONG_PTR pulSignedLen) { CK_RV rv = CKR_OK; soft_dsa_ctx_t *dsa_ctx = session_p->sign.context; soft_object_t *key = dsa_ctx->key; if ((key->class != CKO_PRIVATE_KEY) || (key->key_type != CKK_DSA)) { rv = CKR_KEY_TYPE_INCONSISTENT; goto clean_exit; } /* Output length is always 40 bytes. */ if (pSigned == NULL) { /* Application asks for the length of the output buffer. */ *pulSignedLen = DSA_SIGNATURE_LENGTH; return (CKR_OK); } /* Input data length needs to be 20 bytes. */ if (ulDataLen != DSA_SUBPRIME_BYTES) { rv = CKR_DATA_LEN_RANGE; goto clean_exit; } if (*pulSignedLen < DSA_SIGNATURE_LENGTH) { *pulSignedLen = DSA_SIGNATURE_LENGTH; return (CKR_BUFFER_TOO_SMALL); } rv = local_dsa_sign(key, pData, ulDataLen, pSigned); if (rv == CKR_OK) { *pulSignedLen = DSA_SIGNATURE_LENGTH; } clean_exit: (void) pthread_mutex_lock(&session_p->session_mutex); free(session_p->sign.context); session_p->sign.context = NULL; (void) pthread_mutex_unlock(&session_p->session_mutex); soft_cleanup_object(key); free(key); return (rv); } CK_RV soft_dsa_verify(soft_session_t *session_p, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) { CK_RV rv = CKR_OK; soft_dsa_ctx_t *dsa_ctx = session_p->verify.context; soft_object_t *key = dsa_ctx->key; if ((key->class != CKO_PUBLIC_KEY) ||(key->key_type != CKK_DSA)) { rv = CKR_KEY_TYPE_INCONSISTENT; goto clean_exit; } /* Input data length needs to be 20 bytes. */ if (ulDataLen != DSA_SUBPRIME_BYTES) { rv = CKR_DATA_LEN_RANGE; goto clean_exit; } /* The signature length is always 40 bytes. */ if (ulSignatureLen != DSA_SIGNATURE_LENGTH) { rv = CKR_SIGNATURE_LEN_RANGE; goto clean_exit; } rv = local_dsa_verify(key, pData, pSignature); clean_exit: (void) pthread_mutex_lock(&session_p->session_mutex); free(session_p->verify.context); session_p->verify.context = NULL; (void) pthread_mutex_unlock(&session_p->session_mutex); soft_cleanup_object(key); free(key); return (rv); } CK_RV soft_dsa_digest_verify_common(soft_session_t *session_p, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSigned, CK_ULONG ulSignedLen, boolean_t Final) { CK_RV rv; CK_BYTE hash[SHA1_HASH_SIZE]; /* space enough for SHA1 and MD5 */ CK_ULONG hash_len = SHA1_HASH_SIZE; soft_dsa_ctx_t *dsa_ctx = session_p->verify.context; soft_object_t *key = dsa_ctx->key; if (Final) { rv = soft_digest_final(session_p, hash, &hash_len); } else { rv = soft_digest(session_p, pData, ulDataLen, hash, &hash_len); } if (rv != CKR_OK) { /* free the verification key */ soft_cleanup_object(key); free(key); goto clean_exit; } /* * Now, we are ready to verify the data using signature. * soft_dsa_verify() will free the verification key. */ rv = soft_dsa_verify(session_p, hash, hash_len, pSigned, ulSignedLen); clean_exit: (void) pthread_mutex_lock(&session_p->session_mutex); /* soft_digest_common() has freed the digest context */ session_p->digest.flags = 0; (void) pthread_mutex_unlock(&session_p->session_mutex); return (rv); } static CK_RV soft_genDSAkey_set_attribute(soft_object_t *key, CK_ATTRIBUTE_TYPE type, uchar_t *value, uint32_t value_len, boolean_t public) { CK_RV rv = CKR_OK; biginteger_t *dst = NULL; biginteger_t src; switch (type) { case CKA_VALUE: if (public) dst = OBJ_PUB_DSA_VALUE(key); else dst = OBJ_PRI_DSA_VALUE(key); break; case CKA_PRIME: if (public) dst = OBJ_PUB_DSA_PRIME(key); else dst = OBJ_PRI_DSA_PRIME(key); break; case CKA_SUBPRIME: if (public) dst = OBJ_PUB_DSA_SUBPRIME(key); else dst = OBJ_PRI_DSA_SUBPRIME(key); break; case CKA_BASE: if (public) dst = OBJ_PUB_DSA_BASE(key); else dst = OBJ_PRI_DSA_BASE(key); break; } /* Note: removal of preceding 0x00 imitates similar code in RSA */ while (value[0] == 0) { /* remove preceding 0x00 */ value++; value_len--; } if ((rv = dup_bigint_attr(&src, value, value_len)) != CKR_OK) goto cleanexit; /* Copy the attribute in the key object. */ copy_bigint_attr(&src, dst); cleanexit: /* No need to free big_value because dst holds it now after copy. */ return (rv); } CK_RV soft_dsa_genkey_pair(soft_object_t *pubkey, soft_object_t *prikey) { CK_RV rv; uchar_t prime[MAX_KEY_ATTR_BUFLEN]; uint32_t prime_len = sizeof (prime); uchar_t subprime[MAX_KEY_ATTR_BUFLEN]; uint32_t subprime_len = sizeof (subprime); uchar_t base[MAX_KEY_ATTR_BUFLEN]; uint32_t base_len = sizeof (base); uchar_t pubvalue[MAX_KEY_ATTR_BUFLEN]; uint32_t pubvalue_len = sizeof (pubvalue); uchar_t privalue[DSA_SUBPRIME_BYTES]; uint32_t privalue_len = sizeof (privalue); DSAbytekey k; if ((pubkey == NULL) || (prikey == NULL)) { return (CKR_ARGUMENTS_BAD); } /* lookup prime, subprime and base */ rv = soft_get_public_value(pubkey, CKA_PRIME, prime, &prime_len); if (rv != CKR_OK) { rv = CKR_TEMPLATE_INCOMPLETE; goto cleanexit; } rv = soft_get_public_value(pubkey, CKA_SUBPRIME, subprime, &subprime_len); if (rv != CKR_OK) { rv = CKR_TEMPLATE_INCOMPLETE; goto cleanexit; } rv = soft_get_public_value(pubkey, CKA_BASE, base, &base_len); if (rv != CKR_OK) { rv = CKR_TEMPLATE_INCOMPLETE; goto cleanexit; } /* Inputs to DSA key pair generation. */ k.prime = prime; k.prime_bits = CRYPTO_BYTES2BITS(prime_len); k.subprime = subprime; k.subprime_bits = CRYPTO_BYTES2BITS(subprime_len); k.base = base; k.base_bytes = base_len; k.rfunc = (IS_TOKEN_OBJECT(pubkey) || IS_TOKEN_OBJECT(prikey)) ? pkcs11_get_random : pkcs11_get_urandom; /* Outputs from DSA key pair generation. */ k.public_y = pubvalue; k.public_y_bits = CRYPTO_BYTES2BITS(pubvalue_len); k.private_x = privalue; k.private_x_bits = CRYPTO_BYTES2BITS(privalue_len); rv = dsa_genkey_pair(&k); if (rv != CKR_OK) { goto cleanexit; } /* Update attribute in public key. */ if ((rv = soft_genDSAkey_set_attribute(pubkey, CKA_VALUE, pubvalue, CRYPTO_BITS2BYTES(k.public_y_bits), B_TRUE)) != CKR_OK) { goto cleanexit; } /* Update attributes in private key. */ if ((rv = soft_genDSAkey_set_attribute(prikey, CKA_PRIME, prime, CRYPTO_BITS2BYTES(k.prime_bits), B_FALSE)) != CKR_OK) { goto cleanexit; } if ((rv = soft_genDSAkey_set_attribute(prikey, CKA_SUBPRIME, subprime, CRYPTO_BITS2BYTES(k.subprime_bits), B_FALSE)) != CKR_OK) { goto cleanexit; } if ((rv = soft_genDSAkey_set_attribute(prikey, CKA_BASE, base, k.base_bytes, B_FALSE)) != CKR_OK) { goto cleanexit; } if ((rv = soft_genDSAkey_set_attribute(prikey, CKA_VALUE, privalue, CRYPTO_BITS2BYTES(k.private_x_bits), B_FALSE)) != CKR_OK) { goto cleanexit; } cleanexit: return (rv); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _SOFTDSA_H #define _SOFTDSA_H #ifdef __cplusplus extern "C" { #endif #include #include #include #define _DSA_FIPS_POST #include #include "softObject.h" #include "softSession.h" typedef struct soft_dsa_ctx { soft_object_t *key; } soft_dsa_ctx_t; /* * Function Prototypes. */ /* DSA */ CK_RV soft_dsa_sign_verify_init_common(soft_session_t *, CK_MECHANISM_PTR, soft_object_t *, boolean_t); CK_RV soft_dsa_verify(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG); CK_RV soft_dsa_sign(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR); CK_RV soft_dsa_genkey_pair(soft_object_t *, soft_object_t *); CK_RV soft_dsa_digest_sign_common(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR, boolean_t); CK_RV soft_dsa_digest_verify_common(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG, boolean_t); #ifdef __cplusplus } #endif #endif /* _SOFTDSA_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include "softGlobal.h" #include "softSession.h" #include "softObject.h" #include "softOps.h" CK_RV C_DecryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) { CK_RV rv; soft_session_t *session_p; soft_object_t *key_p; boolean_t lock_held = B_FALSE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if (pMechanism == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } /* Obtain the object pointer. */ HANDLE2OBJECT(hKey, key_p, rv); if (rv != CKR_OK) goto clean_exit; /* Check to see if key object allows for decryption. */ if (!(key_p->bool_attr_mask & DECRYPT_BOOL_ON)) { rv = CKR_KEY_FUNCTION_NOT_PERMITTED; goto clean_exit1; } (void) pthread_mutex_lock(&session_p->session_mutex); lock_held = B_TRUE; /* Check to see if decrypt operation is already active. */ if (session_p->decrypt.flags & CRYPTO_OPERATION_ACTIVE) { /* free the memory to avoid memory leak */ soft_crypt_cleanup(session_p, B_FALSE, lock_held); } /* * This active flag will remain ON until application calls either * C_Decrypt or C_DecryptFinal to actually obtain the final piece * of plaintext. */ session_p->decrypt.flags = CRYPTO_OPERATION_ACTIVE; (void) pthread_mutex_unlock(&session_p->session_mutex); lock_held = B_FALSE; rv = soft_decrypt_init(session_p, pMechanism, key_p); if (rv != CKR_OK) { (void) pthread_mutex_lock(&session_p->session_mutex); session_p->decrypt.flags &= ~CRYPTO_OPERATION_ACTIVE; lock_held = B_TRUE; } clean_exit1: OBJ_REFRELE(key_p); clean_exit: SES_REFRELE(session_p, lock_held); return (rv); } CK_RV C_Decrypt(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) { CK_RV rv; soft_session_t *session_p; boolean_t lock_held = B_FALSE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obatin the session pointer. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); /* * Only check if input buffer is null. How to handle zero input * length depents on the mechanism in use. For secret key mechanisms, * unpadded ones yield zero length output, but padded ones always * result in smaller than original, possibly zero, length output. */ if (pEncryptedData == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } /* * No need to check pData because application might * just want to know the length of decrypted data. */ if (pulDataLen == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } (void) pthread_mutex_lock(&session_p->session_mutex); lock_held = B_TRUE; /* Application must call C_DecryptInit before calling C_Decrypt. */ if (!(session_p->decrypt.flags & CRYPTO_OPERATION_ACTIVE)) { SES_REFRELE(session_p, lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } /* * C_Decrypt must be called without intervening C_DecryptUpdate * calls. */ if (session_p->decrypt.flags & CRYPTO_OPERATION_UPDATE) { /* * C_Decrypt can not be used to terminate a multi-part * operation, so we'll leave the active decrypt operation * flag on and let the application continue with the * decrypt update operation. */ SES_REFRELE(session_p, lock_held); return (CKR_FUNCTION_FAILED); } (void) pthread_mutex_unlock(&session_p->session_mutex); lock_held = B_FALSE; rv = soft_decrypt(session_p, pEncryptedData, ulEncryptedDataLen, pData, pulDataLen); if ((rv == CKR_BUFFER_TOO_SMALL) || (pData == NULL && rv == CKR_OK)) { /* * We will not terminate the active decrypt operation flag, * when the application-supplied buffer is too small, or * the application asks for the length of buffer to hold * the plaintext. */ SES_REFRELE(session_p, lock_held); return (rv); } clean_exit: /* Clear context, free key, and release session counter */ soft_crypt_cleanup(session_p, B_FALSE, B_FALSE); return (rv); } CK_RV C_DecryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen, CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen) { CK_RV rv; soft_session_t *session_p; boolean_t lock_held = B_FALSE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); /* * Only check if input buffer is null. How to handle zero input * length depents on the mechanism in use. For secret key mechanisms, * unpadded ones yeild zero length output, but padded ones always * result in smaller than original, possibly zero, length output. */ if (pEncryptedPart == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } /* * Only check if pulPartLen is NULL. * No need to check if pPart is NULL because application * might just ask for the length of buffer to hold the * recovered data. */ if (pulPartLen == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } (void) pthread_mutex_lock(&session_p->session_mutex); lock_held = B_TRUE; /* * Application must call C_DecryptInit before calling * C_DecryptUpdate. */ if (!(session_p->decrypt.flags & CRYPTO_OPERATION_ACTIVE)) { SES_REFRELE(session_p, lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } session_p->decrypt.flags |= CRYPTO_OPERATION_UPDATE; (void) pthread_mutex_unlock(&session_p->session_mutex); lock_held = B_FALSE; rv = soft_decrypt_update(session_p, pEncryptedPart, ulEncryptedPartLen, pPart, pulPartLen); /* * If CKR_OK or CKR_BUFFER_TOO_SMALL, don't terminate the * current decryption operation. */ if ((rv == CKR_OK) || (rv == CKR_BUFFER_TOO_SMALL)) { SES_REFRELE(session_p, lock_held); return (rv); } clean_exit: /* * After an error occurred, terminate the current decrypt * operation by resetting the active and update flags. */ soft_crypt_cleanup(session_p, B_FALSE, lock_held); return (rv); } CK_RV C_DecryptFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pLastPart, CK_ULONG_PTR pulLastPartLen) { CK_RV rv; soft_session_t *session_p; boolean_t lock_held = B_FALSE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if (pulLastPartLen == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } (void) pthread_mutex_lock(&session_p->session_mutex); lock_held = B_TRUE; /* * Application must call C_DecryptInit before calling * C_DecryptFinal. */ if (!(session_p->decrypt.flags & CRYPTO_OPERATION_ACTIVE)) { SES_REFRELE(session_p, lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } (void) pthread_mutex_unlock(&session_p->session_mutex); lock_held = B_FALSE; rv = soft_decrypt_final(session_p, pLastPart, pulLastPartLen); if ((rv == CKR_BUFFER_TOO_SMALL) || (pLastPart == NULL && rv == CKR_OK)) { /* * We will not terminate the active decrypt operation flag, * when the application-supplied buffer is too small, or * the application asks for the length of buffer to hold * the plaintext. */ SES_REFRELE(session_p, lock_held); return (rv); } /* Terminates the active encrypt operation. */ (void) pthread_mutex_lock(&session_p->session_mutex); session_p->decrypt.flags = 0; lock_held = B_TRUE; SES_REFRELE(session_p, lock_held); return (rv); clean_exit: /* Terminates the active decrypt operation */ soft_crypt_cleanup(session_p, B_FALSE, lock_held); return (rv); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, Joyent, Inc. * Copyright 2017 Jason King. */ #include #include #include #include #include #include #include #include #include "softSession.h" #include "softObject.h" #include "softOps.h" #include "softCrypt.h" #include "softRSA.h" /* * Remove padding bytes. */ CK_RV soft_remove_pkcs7_padding(CK_BYTE *pData, CK_ULONG padded_len, CK_ULONG *pulDataLen) { CK_RV rv; #ifdef __sparcv9 if ((rv = pkcs7_decode(pData, (&padded_len))) != CKR_OK) #else /* !__sparcv9 */ if ((rv = pkcs7_decode(pData, (size_t *)(&padded_len))) != CKR_OK) #endif /* __sparcv9 */ return (rv); *pulDataLen = padded_len; return (CKR_OK); } /* * soft_decrypt_init() * * Arguments: * session_p: pointer to soft_session_t struct * pMechanism: pointer to CK_MECHANISM struct provided by application * key_p: pointer to key soft_object_t struct * * Description: * called by C_DecryptInit(). This function calls the corresponding * decrypt init routine based on the mechanism. * * Returns: * CKR_OK: success * CKR_HOST_MEMORY: run out of system memory * CKR_MECHANISM_PARAM_INVALID: invalid parameters in mechanism * CKR_MECHANISM_INVALID: invalid mechanism type * CKR_KEY_TYPE_INCONSISTENT: incorrect type of key to use * with the specified mechanism */ CK_RV soft_decrypt_init(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism, soft_object_t *key_p) { CK_RV rv; switch (pMechanism->mechanism) { case CKM_DES_ECB: if (key_p->key_type != CKK_DES) { return (CKR_KEY_TYPE_INCONSISTENT); } goto ecb_common; case CKM_DES3_ECB: if ((key_p->key_type != CKK_DES2) && (key_p->key_type != CKK_DES3)) { return (CKR_KEY_TYPE_INCONSISTENT); } ecb_common: return (soft_des_crypt_init_common(session_p, pMechanism, key_p, B_FALSE)); case CKM_DES_CBC: case CKM_DES_CBC_PAD: if (key_p->key_type != CKK_DES) { return (CKR_KEY_TYPE_INCONSISTENT); } goto cbc_common; case CKM_DES3_CBC: case CKM_DES3_CBC_PAD: { soft_des_ctx_t *soft_des_ctx; if ((key_p->key_type != CKK_DES2) && (key_p->key_type != CKK_DES3)) { return (CKR_KEY_TYPE_INCONSISTENT); } cbc_common: if ((pMechanism->pParameter == NULL) || (pMechanism->ulParameterLen != DES_BLOCK_LEN)) { return (CKR_MECHANISM_PARAM_INVALID); } rv = soft_des_crypt_init_common(session_p, pMechanism, key_p, B_FALSE); if (rv != CKR_OK) return (rv); (void) pthread_mutex_lock(&session_p->session_mutex); soft_des_ctx = (soft_des_ctx_t *)session_p->decrypt.context; /* Save Initialization Vector (IV) in the context. */ (void) memcpy(soft_des_ctx->ivec, pMechanism->pParameter, DES_BLOCK_LEN); /* Allocate a context for DES cipher-block chaining. */ soft_des_ctx->des_cbc = (void *)des_cbc_ctx_init( soft_des_ctx->key_sched, soft_des_ctx->keysched_len, soft_des_ctx->ivec, key_p->key_type); if (soft_des_ctx->des_cbc == NULL) { freezero(soft_des_ctx->key_sched, soft_des_ctx->keysched_len); freezero(session_p->decrypt.context, sizeof (soft_des_ctx_t)); session_p->decrypt.context = NULL; (void) pthread_mutex_unlock(&session_p->session_mutex); return (CKR_HOST_MEMORY); } (void) pthread_mutex_unlock(&session_p->session_mutex); return (rv); } case CKM_AES_ECB: case CKM_AES_CBC: case CKM_AES_CBC_PAD: case CKM_AES_CTR: case CKM_AES_CCM: case CKM_AES_GCM: case CKM_AES_GMAC: return (soft_aes_crypt_init_common(session_p, pMechanism, key_p, B_FALSE)); case CKM_BLOWFISH_CBC: { soft_blowfish_ctx_t *soft_blowfish_ctx; if (key_p->key_type != CKK_BLOWFISH) return (CKR_KEY_TYPE_INCONSISTENT); if ((pMechanism->pParameter == NULL) || (pMechanism->ulParameterLen != BLOWFISH_BLOCK_LEN)) return (CKR_MECHANISM_PARAM_INVALID); rv = soft_blowfish_crypt_init_common(session_p, pMechanism, key_p, B_FALSE); if (rv != CKR_OK) return (rv); (void) pthread_mutex_lock(&session_p->session_mutex); soft_blowfish_ctx = (soft_blowfish_ctx_t *)session_p->decrypt.context; /* Save Initialization Vector in the context. */ (void) memcpy(soft_blowfish_ctx->ivec, pMechanism->pParameter, BLOWFISH_BLOCK_LEN); /* Allocate a context for CBC */ soft_blowfish_ctx->blowfish_cbc = (void *)blowfish_cbc_ctx_init(soft_blowfish_ctx->key_sched, soft_blowfish_ctx->keysched_len, soft_blowfish_ctx->ivec); if (soft_blowfish_ctx->blowfish_cbc == NULL) { freezero(soft_blowfish_ctx->key_sched, soft_blowfish_ctx->keysched_len); freezero(session_p->decrypt.context, sizeof (soft_blowfish_ctx_t)); session_p->decrypt.context = NULL; (void) pthread_mutex_unlock(&session_p->session_mutex); return (CKR_HOST_MEMORY); } (void) pthread_mutex_unlock(&session_p->session_mutex); return (rv); } case CKM_RC4: if (key_p->key_type != CKK_RC4) { return (CKR_KEY_TYPE_INCONSISTENT); } return (soft_arcfour_crypt_init(session_p, pMechanism, key_p, B_FALSE)); case CKM_RSA_X_509: case CKM_RSA_PKCS: if (key_p->key_type != CKK_RSA) { return (CKR_KEY_TYPE_INCONSISTENT); } return (soft_rsa_crypt_init_common(session_p, pMechanism, key_p, B_FALSE)); default: return (CKR_MECHANISM_INVALID); } } /* * soft_decrypt_common() * * Arguments: * session_p: pointer to soft_session_t struct * pEncrypted: pointer to the encrypted data as input * ulEncryptedLen: length of the input data * pData: pointer to the output data contains plaintext * pulDataLen: pointer to the length of the output data * Update: boolean flag indicates caller is soft_decrypt * or soft_decrypt_update * * Description: * This function calls the corresponding decrypt routine based * on the mechanism. * * Returns: * see soft_decrypt_common(). */ CK_RV soft_decrypt_common(soft_session_t *session_p, CK_BYTE_PTR pEncrypted, CK_ULONG ulEncryptedLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen, boolean_t Update) { CK_MECHANISM_TYPE mechanism = session_p->decrypt.mech.mechanism; switch (mechanism) { case CKM_DES_ECB: case CKM_DES_CBC: case CKM_DES3_ECB: case CKM_DES3_CBC: if (ulEncryptedLen == 0) { *pulDataLen = 0; return (CKR_OK); } /* FALLTHROUGH */ case CKM_DES_CBC_PAD: case CKM_DES3_CBC_PAD: return (soft_des_decrypt_common(session_p, pEncrypted, ulEncryptedLen, pData, pulDataLen, Update)); case CKM_AES_ECB: case CKM_AES_CBC: case CKM_AES_CBC_PAD: case CKM_AES_CTR: case CKM_AES_CCM: case CKM_AES_GCM: case CKM_AES_GMAC: if (Update) { return (soft_aes_decrypt_update(session_p, pEncrypted, ulEncryptedLen, pData, pulDataLen)); } else { return (soft_aes_decrypt(session_p, pEncrypted, ulEncryptedLen, pData, pulDataLen)); } case CKM_BLOWFISH_CBC: if (ulEncryptedLen == 0) { *pulDataLen = 0; return (CKR_OK); } return (soft_blowfish_decrypt_common(session_p, pEncrypted, ulEncryptedLen, pData, pulDataLen, Update)); case CKM_RC4: if (ulEncryptedLen == 0) { *pulDataLen = 0; return (CKR_OK); } return (soft_arcfour_crypt(&(session_p->decrypt), pEncrypted, ulEncryptedLen, pData, pulDataLen)); case CKM_RSA_X_509: case CKM_RSA_PKCS: return (soft_rsa_decrypt_common(session_p, pEncrypted, ulEncryptedLen, pData, pulDataLen, mechanism)); default: return (CKR_MECHANISM_INVALID); } } /* * soft_decrypt() * * Arguments: * session_p: pointer to soft_session_t struct * pEncryptedData: pointer to the encrypted data as input * ulEncryptedDataLen: length of the input data * pData: pointer to the output data contains plaintext * pulDataLen: pointer to the length of the output data * * Description: * called by C_Decrypt(). This function calls the soft_decrypt_common * routine. * * Returns: * see soft_decrypt_common(). */ CK_RV soft_decrypt(soft_session_t *session_p, CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) { return (soft_decrypt_common(session_p, pEncryptedData, ulEncryptedDataLen, pData, pulDataLen, B_FALSE)); } /* * soft_decrypt_update() * * Arguments: * session_p: pointer to soft_session_t struct * pEncryptedPart: pointer to the encrypted data as input * ulEncryptedPartLen: length of the input data * pPart: pointer to the output data contains plaintext * pulPartLen: pointer to the length of the output data * * Description: * called by C_DecryptUpdate(). This function calls the * soft_decrypt_common routine (with update flag on). * * Returns: * see soft_decrypt_common(). */ CK_RV soft_decrypt_update(soft_session_t *session_p, CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen, CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen) { CK_MECHANISM_TYPE mechanism = session_p->decrypt.mech.mechanism; switch (mechanism) { case CKM_DES_ECB: case CKM_DES_CBC: case CKM_DES_CBC_PAD: case CKM_DES3_ECB: case CKM_DES3_CBC: case CKM_DES3_CBC_PAD: case CKM_AES_ECB: case CKM_AES_CBC: case CKM_AES_CBC_PAD: case CKM_AES_CTR: case CKM_AES_CCM: case CKM_AES_GCM: case CKM_AES_GMAC: case CKM_BLOWFISH_CBC: case CKM_RC4: return (soft_decrypt_common(session_p, pEncryptedPart, ulEncryptedPartLen, pPart, pulPartLen, B_TRUE)); default: /* PKCS11: The mechanism only supports single-part operation. */ return (CKR_MECHANISM_INVALID); } } /* * soft_decrypt_final() * * Arguments: * session_p: pointer to soft_session_t struct * pLastPart: pointer to the last recovered data part * pulLastPartLen: pointer to the length of the last recovered data part * * Description: * called by C_DecryptFinal(). * * Returns: * CKR_OK: success * CKR_FUNCTION_FAILED: decrypt final function failed * CKR_ENCRYPTED_DATA_LEN_RANGE: remaining buffer contains bad length */ CK_RV soft_decrypt_final(soft_session_t *session_p, CK_BYTE_PTR pLastPart, CK_ULONG_PTR pulLastPartLen) { CK_MECHANISM_TYPE mechanism = session_p->decrypt.mech.mechanism; CK_ULONG out_len; CK_RV rv = CKR_OK; int rc; (void) pthread_mutex_lock(&session_p->session_mutex); if (session_p->decrypt.context == NULL) { rv = CKR_OPERATION_NOT_INITIALIZED; *pulLastPartLen = 0; goto clean2; } switch (mechanism) { case CKM_DES_CBC_PAD: case CKM_DES3_CBC_PAD: { soft_des_ctx_t *soft_des_ctx; soft_des_ctx = (soft_des_ctx_t *)session_p->decrypt.context; /* * We should have only one block of data left in the * remaining buffer. */ if (soft_des_ctx->remain_len != DES_BLOCK_LEN) { *pulLastPartLen = 0; rv = CKR_ENCRYPTED_DATA_LEN_RANGE; /* Cleanup memory space. */ free(soft_des_ctx->des_cbc); freezero(soft_des_ctx->key_sched, soft_des_ctx->keysched_len); goto clean1; } out_len = DES_BLOCK_LEN; /* * If application asks for the length of the output buffer * to hold the plaintext? */ if (pLastPart == NULL) { *pulLastPartLen = out_len; rv = CKR_OK; goto clean2; } else { crypto_data_t out; /* Copy remaining data to the output buffer. */ (void) memcpy(pLastPart, soft_des_ctx->data, DES_BLOCK_LEN); out.cd_format = CRYPTO_DATA_RAW; out.cd_offset = 0; out.cd_length = DES_BLOCK_LEN; out.cd_raw.iov_base = (char *)pLastPart; out.cd_raw.iov_len = DES_BLOCK_LEN; /* Decrypt final block of data. */ rc = des_decrypt_contiguous_blocks( (des_ctx_t *)soft_des_ctx->des_cbc, (char *)pLastPart, DES_BLOCK_LEN, &out); if (rc == 0) { /* * Remove padding bytes after decryption of * ciphertext block to produce the original * plaintext. */ rv = soft_remove_pkcs7_padding(pLastPart, DES_BLOCK_LEN, &out_len); if (rv != CKR_OK) *pulLastPartLen = 0; else *pulLastPartLen = out_len; } else { *pulLastPartLen = 0; rv = CKR_FUNCTION_FAILED; } /* Cleanup memory space. */ free(soft_des_ctx->des_cbc); freezero(soft_des_ctx->key_sched, soft_des_ctx->keysched_len); } break; } case CKM_DES_CBC: case CKM_DES_ECB: case CKM_DES3_CBC: case CKM_DES3_ECB: { soft_des_ctx_t *soft_des_ctx; soft_des_ctx = (soft_des_ctx_t *)session_p->decrypt.context; /* * CKM_DES_CBC and CKM_DES_ECB does not do any padding, * so when the final is called, the remaining buffer * should not contain any more data. */ *pulLastPartLen = 0; if (soft_des_ctx->remain_len != 0) { rv = CKR_ENCRYPTED_DATA_LEN_RANGE; } else { if (pLastPart == NULL) goto clean2; } /* Cleanup memory space. */ free(soft_des_ctx->des_cbc); freezero(soft_des_ctx->key_sched, soft_des_ctx->keysched_len); break; } case CKM_AES_CBC_PAD: case CKM_AES_CBC: case CKM_AES_ECB: case CKM_AES_CTR: case CKM_AES_CCM: case CKM_AES_GCM: case CKM_AES_GMAC: rv = soft_aes_decrypt_final(session_p, pLastPart, pulLastPartLen); break; case CKM_BLOWFISH_CBC: { soft_blowfish_ctx_t *soft_blowfish_ctx; soft_blowfish_ctx = (soft_blowfish_ctx_t *)session_p->decrypt.context; *pulLastPartLen = 0; if (soft_blowfish_ctx->remain_len != 0) rv = CKR_ENCRYPTED_DATA_LEN_RANGE; else { if (pLastPart == NULL) goto clean2; } free(soft_blowfish_ctx->blowfish_cbc); freezero(soft_blowfish_ctx->key_sched, soft_blowfish_ctx->keysched_len); break; } case CKM_RC4: { ARCFour_key *key = (ARCFour_key *)session_p->decrypt.context; explicit_bzero(key, sizeof (*key)); *pulLastPartLen = 0; break; } default: /* PKCS11: The mechanism only supports single-part operation. */ rv = CKR_MECHANISM_INVALID; break; } clean1: free(session_p->decrypt.context); session_p->decrypt.context = NULL; clean2: (void) pthread_mutex_unlock(&session_p->session_mutex); return (rv); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2018, Joyent, Inc. */ #include #include #include "softGlobal.h" #include "softOps.h" #include "softSession.h" CK_RV C_DigestInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism) { CK_RV rv; soft_session_t *session_p; boolean_t lock_held = B_TRUE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if (pMechanism == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } /* Acquire the session lock */ (void) pthread_mutex_lock(&session_p->session_mutex); /* Check to see if digest operation is already active */ if (session_p->digest.flags & CRYPTO_OPERATION_ACTIVE) { /* * Free the memory to avoid memory leak. * digest.context is only a flat structure. */ soft_digest_cleanup(session_p, lock_held); } /* * This active flag will remain ON until application calls either * C_Digest or C_DigestFinal to actually obtain the value of * the message digest. */ session_p->digest.flags = CRYPTO_OPERATION_ACTIVE; (void) pthread_mutex_unlock(&session_p->session_mutex); rv = soft_digest_init(session_p, pMechanism); if (rv != CKR_OK) { (void) pthread_mutex_lock(&session_p->session_mutex); session_p->digest.flags &= ~CRYPTO_OPERATION_ACTIVE; /* * Decrement the session reference count. * We hold the session lock, and SES_REFRELE() * will release the session lock for us. */ SES_REFRELE(session_p, lock_held); return (rv); } clean_exit: /* * Decrement the session reference count. * We do not hold the session lock. */ lock_held = B_FALSE; SES_REFRELE(session_p, lock_held); return (rv); } CK_RV C_Digest(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pDigest, CK_ULONG_PTR pulDigestLen) { CK_RV rv; soft_session_t *session_p; boolean_t lock_held = B_TRUE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if ((pData == NULL && ulDataLen != 0) || pulDigestLen == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } /* Acquire the session lock */ (void) pthread_mutex_lock(&session_p->session_mutex); /* Application must call C_DigestInit before calling C_Digest */ if (!(session_p->digest.flags & CRYPTO_OPERATION_ACTIVE)) { /* * Decrement the session reference count. * We hold the session lock, and SES_REFRELE() * will release the session lock for us. */ SES_REFRELE(session_p, lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } /* * C_Digest must be called without intervening C_DigestUpdate * calls. */ if (session_p->digest.flags & CRYPTO_OPERATION_UPDATE) { /* * C_Digest can not be used to terminate a multi-part * operation, so we'll leave the active digest operation * flag on and let the application continue with the * digest update operation. * * Decrement the session reference count. * We hold the session lock, and SES_REFRELE() * will release the session lock for us. */ SES_REFRELE(session_p, lock_held); return (CKR_FUNCTION_FAILED); } (void) pthread_mutex_unlock(&session_p->session_mutex); rv = soft_digest(session_p, pData, ulDataLen, pDigest, pulDigestLen); if ((rv == CKR_BUFFER_TOO_SMALL) || (pDigest == NULL && rv == CKR_OK)) { /* * We will not terminate the active digest operation flag, * when the application-supplied buffer is too small, or * the application asks for the length of buffer to hold * the message digest. * * Decrement the session reference count. * We do not hold the session lock. */ lock_held = B_FALSE; SES_REFRELE(session_p, lock_held); return (rv); } clean_exit: /* * Terminates the active digest operation. * Application needs to call C_DigestInit again for next * digest operation. */ (void) pthread_mutex_lock(&session_p->session_mutex); soft_digest_cleanup(session_p, lock_held); /* * Decrement the session reference count. * We hold the session lock, and SES_REFRELE() * will release the session lock for us. */ SES_REFRELE(session_p, lock_held); return (rv); } CK_RV C_DigestUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) { CK_RV rv; soft_session_t *session_p; boolean_t lock_held = B_TRUE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if (pPart == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } /* Acquire the session lock */ (void) pthread_mutex_lock(&session_p->session_mutex); /* * Application must call C_DigestInit before calling * C_DigestUpdate. */ if (!(session_p->digest.flags & CRYPTO_OPERATION_ACTIVE)) { /* * Decrement the session reference count. * We hold the session lock, and SES_REFRELE() * will release the session lock for us. */ SES_REFRELE(session_p, lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } /* Set update flag to protect C_Digest */ session_p->digest.flags |= CRYPTO_OPERATION_UPDATE; (void) pthread_mutex_unlock(&session_p->session_mutex); rv = soft_digest_update(session_p, pPart, ulPartLen); if (rv == CKR_OK) { /* * Decrement the session reference count. * We do not hold the session lock. */ lock_held = B_FALSE; SES_REFRELE(session_p, lock_held); return (CKR_OK); } clean_exit: /* * After an error occurred, terminate the current digest * operation by resetting the active and update flags. */ (void) pthread_mutex_lock(&session_p->session_mutex); soft_digest_cleanup(session_p, lock_held); /* * Decrement the session reference count. * We hold the session lock, and SES_REFRELE() * will release the session lock for us. */ SES_REFRELE(session_p, lock_held); return (rv); } CK_RV C_DigestKey(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hKey) { CK_RV rv; soft_session_t *session_p; soft_object_t *key_p; boolean_t lock_held = B_TRUE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); /* Obtain the object pointer. */ HANDLE2OBJECT(hKey, key_p, rv); if (rv != CKR_OK) goto clean_exit; (void) pthread_mutex_lock(&session_p->session_mutex); /* * Application must call C_DigestInit before calling * C_DigestKey. */ if (!(session_p->digest.flags & CRYPTO_OPERATION_ACTIVE)) { /* * Decrement the session reference count. * We hold the session lock, and SES_REFRELE() * will release the session lock for us. */ OBJ_REFRELE(key_p); SES_REFRELE(session_p, lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } /* * Remember the fact that a key was thrown into the mix, so that * C_DigestFinal bzero()'s the digest context before freeing it. */ session_p->digest.flags |= (CRYPTO_KEY_DIGESTED | CRYPTO_OPERATION_UPDATE); (void) pthread_mutex_unlock(&session_p->session_mutex); rv = soft_digest_key(session_p, key_p); if (rv == CKR_OK) { /* * Decrement the session reference count. * We do not hold the session lock. */ lock_held = B_FALSE; OBJ_REFRELE(key_p); SES_REFRELE(session_p, lock_held); return (CKR_OK); } OBJ_REFRELE(key_p); clean_exit: /* * After an error occurred, terminate the current digest * operation by resetting the active and update flags. */ (void) pthread_mutex_lock(&session_p->session_mutex); soft_digest_cleanup(session_p, lock_held); /* * Decrement the session reference count. * We hold the session lock, and SES_REFRELE() * will release the session lock for us. */ SES_REFRELE(session_p, lock_held); return (rv); } CK_RV C_DigestFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pDigest, CK_ULONG_PTR pulDigestLen) { CK_RV rv; soft_session_t *session_p; boolean_t lock_held = B_TRUE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if (pulDigestLen == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } /* Acquire the session lock */ (void) pthread_mutex_lock(&session_p->session_mutex); /* * Application must call C_DigestInit before calling * C_DigestFinal. */ if (!(session_p->digest.flags & CRYPTO_OPERATION_ACTIVE)) { /* * Decrement the session reference count. * We hold the session lock, and SES_REFRELE() * will release the session lock for us. */ SES_REFRELE(session_p, lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } (void) pthread_mutex_unlock(&session_p->session_mutex); rv = soft_digest_final(session_p, pDigest, pulDigestLen); if ((rv == CKR_BUFFER_TOO_SMALL) || (pDigest == NULL && rv == CKR_OK)) { /* * We will not terminate the active digest operation flag, * when the application-supplied buffer is too small, or * the application asks for the length of buffer to hold * the message digest. * * Decrement the session reference count. * We do not hold the session lock. */ lock_held = B_FALSE; SES_REFRELE(session_p, lock_held); return (rv); } clean_exit: /* Terminates the active digest operation */ (void) pthread_mutex_lock(&session_p->session_mutex); soft_digest_cleanup(session_p, lock_held); /* * Decrement the session reference count. * We hold the session lock, and SES_REFRELE() * will release the session lock for us. */ SES_REFRELE(session_p, lock_held); return (rv); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright (c) 2018, Joyent, Inc. */ #include #include #include #include #include #include #include #include #include "softGlobal.h" #include "softOps.h" #include "softSession.h" #include "softObject.h" /* * soft_digest_init() * * Arguments: * session_p: pointer to soft_session_t struct * pMechanism: pointer to CK_MECHANISM struct provided by application * * Description: * called by C_DigestInit(). This function allocates space for * context, then calls the corresponding software provided digest * init routine based on the mechanism. * * Returns: * CKR_OK: success * CKR_HOST_MEMORY: run out of system memory * CKR_MECHANISM_INVALID: invalid mechanism type */ CK_RV soft_digest_init(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism) { switch (pMechanism->mechanism) { case CKM_MD5: (void) pthread_mutex_lock(&session_p->session_mutex); session_p->digest.context = malloc(sizeof (MD5_CTX)); if (session_p->digest.context == NULL) { (void) pthread_mutex_unlock(&session_p->session_mutex); return (CKR_HOST_MEMORY); } session_p->digest.mech.mechanism = CKM_MD5; (void) pthread_mutex_unlock(&session_p->session_mutex); MD5Init((MD5_CTX *)session_p->digest.context); break; case CKM_SHA_1: (void) pthread_mutex_lock(&session_p->session_mutex); session_p->digest.context = malloc(sizeof (SHA1_CTX)); if (session_p->digest.context == NULL) { (void) pthread_mutex_unlock(&session_p->session_mutex); return (CKR_HOST_MEMORY); } session_p->digest.mech.mechanism = CKM_SHA_1; session_p->digest.mech.pParameter = pMechanism->pParameter; session_p->digest.mech.ulParameterLen = pMechanism->ulParameterLen; (void) pthread_mutex_unlock(&session_p->session_mutex); SHA1Init((SHA1_CTX *)session_p->digest.context); break; case CKM_SHA256: case CKM_SHA384: case CKM_SHA512: case CKM_SHA512_224: case CKM_SHA512_256: (void) pthread_mutex_lock(&session_p->session_mutex); session_p->digest.context = malloc(sizeof (SHA2_CTX)); if (session_p->digest.context == NULL) { (void) pthread_mutex_unlock(&session_p->session_mutex); return (CKR_HOST_MEMORY); } session_p->digest.mech.mechanism = pMechanism->mechanism; (void) pthread_mutex_unlock(&session_p->session_mutex); switch (pMechanism->mechanism) { case CKM_SHA256: SHA2Init(SHA256, (SHA2_CTX *)session_p->digest.context); break; case CKM_SHA384: SHA2Init(SHA384, (SHA2_CTX *)session_p->digest.context); break; case CKM_SHA512: SHA2Init(SHA512, (SHA2_CTX *)session_p->digest.context); break; case CKM_SHA512_224: SHA2Init(SHA512_224, (SHA2_CTX *)session_p->digest.context); break; case CKM_SHA512_256: SHA2Init(SHA512_256, (SHA2_CTX *)session_p->digest.context); break; } break; default: return (CKR_MECHANISM_INVALID); } return (CKR_OK); } /* * soft_digest_common() * * Arguments: * session_p: pointer to soft_session_t struct * pData: pointer to the input data to be digested * ulDataLen: length of the input data * pDigest: pointer to the output data after digesting * pulDigestLen: length of the output data * * Description: * called by soft_digest() or soft_digest_final(). This function * determines the length of output buffer and calls the corresponding * software provided digest routine based on the mechanism. * * Returns: * CKR_OK: success * CKR_MECHANISM_INVALID: invalid mechanism type * CKR_BUFFER_TOO_SMALL: the output buffer provided by application * is too small */ CK_RV soft_digest_common(soft_session_t *session_p, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pDigest, CK_ULONG_PTR pulDigestLen) { CK_ULONG digestLen = 0; size_t len = 0; /* * Determine the output data length based on the mechanism */ switch (session_p->digest.mech.mechanism) { case CKM_MD5: digestLen = 16; break; case CKM_SHA_1: digestLen = SHA1_DIGEST_LENGTH; break; case CKM_SHA256: digestLen = SHA256_DIGEST_LENGTH; break; case CKM_SHA384: digestLen = SHA384_DIGEST_LENGTH; break; case CKM_SHA512: digestLen = SHA512_DIGEST_LENGTH; break; case CKM_SHA512_224: digestLen = SHA512_224_DIGEST_LENGTH; break; case CKM_SHA512_256: digestLen = SHA512_256_DIGEST_LENGTH; break; default: return (CKR_MECHANISM_INVALID); } if (pDigest == NULL) { /* * Application only wants to know the length of the * buffer needed to hold the message digest. */ *pulDigestLen = digestLen; return (CKR_OK); } if (*pulDigestLen < digestLen) { /* * Application provides buffer too small to hold the * digest message. Return the length of buffer needed * to the application. */ *pulDigestLen = digestLen; return (CKR_BUFFER_TOO_SMALL); } /* * Call the corresponding system provided software digest routine. * If the soft_digest_common() is called by soft_digest_final() * the pData is NULL, and the ulDataLen is zero. */ switch (session_p->digest.mech.mechanism) { case CKM_MD5: if (pData != NULL) { /* * this is called by soft_digest() */ #ifdef __sparcv9 MD5Update((MD5_CTX *)session_p->digest.context, /* LINTED */ pData, (uint_t)ulDataLen); #else /* !__sparcv9 */ MD5Update((MD5_CTX *)session_p->digest.context, pData, ulDataLen); #endif /* __sparcv9 */ MD5Final(pDigest, (MD5_CTX *)session_p->digest.context); } else { /* * this is called by soft_digest_final() */ MD5Final(pDigest, (MD5_CTX *)session_p->digest.context); len = sizeof (MD5_CTX); } break; case CKM_SHA_1: if (pData != NULL) { /* * this is called by soft_digest() */ #ifdef __sparcv9 SHA1Update((SHA1_CTX *)session_p->digest.context, /* LINTED */ pData, (uint32_t)ulDataLen); #else /* !__sparcv9 */ SHA1Update((SHA1_CTX *)session_p->digest.context, pData, ulDataLen); #endif /* __sparcv9 */ SHA1Final(pDigest, (SHA1_CTX *)session_p->digest.context); } else { /* * this is called by soft_digest_final() */ SHA1Final(pDigest, (SHA1_CTX *)session_p->digest.context); len = sizeof (SHA1_CTX); } break; case CKM_SHA256: case CKM_SHA384: case CKM_SHA512: case CKM_SHA512_224: case CKM_SHA512_256: if (pData != NULL) { /* * this is called by soft_digest() */ SHA2Update((SHA2_CTX *)session_p->digest.context, pData, ulDataLen); SHA2Final(pDigest, (SHA2_CTX *)session_p->digest.context); } else { /* * this is called by soft_digest_final() */ SHA2Final(pDigest, (SHA2_CTX *)session_p->digest.context); len = sizeof (SHA2_CTX); } break; } /* Paranoia on behalf of C_DigestKey callers: bzero the context */ if (session_p->digest.flags & CRYPTO_KEY_DIGESTED) { explicit_bzero(session_p->digest.context, len); session_p->digest.flags &= ~CRYPTO_KEY_DIGESTED; } *pulDigestLen = digestLen; (void) pthread_mutex_lock(&session_p->session_mutex); free(session_p->digest.context); session_p->digest.context = NULL; (void) pthread_mutex_unlock(&session_p->session_mutex); return (CKR_OK); } /* * soft_digest() * * Arguments: * session_p: pointer to soft_session_t struct * pData: pointer to the input data to be digested * ulDataLen: length of the input data * pDigest: pointer to the output data after digesting * pulDigestLen: length of the output data * * Description: * called by C_Digest(). This function calls soft_digest_common(). * * Returns: * see return values in soft_digest_common(). */ CK_RV soft_digest(soft_session_t *session_p, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pDigest, CK_ULONG_PTR pulDigestLen) { return (soft_digest_common(session_p, pData, ulDataLen, pDigest, pulDigestLen)); } /* * soft_digest_update() * * Arguments: * session_p: pointer to soft_session_t struct * pPart: pointer to the input data to be digested * ulPartLen: length of the input data * * Description: * called by C_DigestUpdate(). This function calls the corresponding * software provided digest update routine based on the mechanism. * * Returns: * CKR_OK: success * CKR_MECHANISM_INVALID: invalid MECHANISM type. */ CK_RV soft_digest_update(soft_session_t *session_p, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) { switch (session_p->digest.mech.mechanism) { case CKM_MD5: #ifdef __sparcv9 MD5Update((MD5_CTX *)session_p->digest.context, /* LINTED */ pPart, (uint_t)ulPartLen); #else /* !__sparcv9 */ MD5Update((MD5_CTX *)session_p->digest.context, pPart, ulPartLen); #endif /* __sparcv9 */ break; case CKM_SHA_1: #ifdef __sparcv9 SHA1Update((SHA1_CTX *)session_p->digest.context, /* LINTED */ pPart, (uint32_t)ulPartLen); #else /* !__sparcv9 */ SHA1Update((SHA1_CTX *)session_p->digest.context, pPart, ulPartLen); #endif /* __sparcv9 */ break; case CKM_SHA256: case CKM_SHA384: case CKM_SHA512: case CKM_SHA512_224: case CKM_SHA512_256: SHA2Update((SHA2_CTX *)session_p->digest.context, pPart, ulPartLen); break; default: return (CKR_MECHANISM_INVALID); } return (CKR_OK); } /* * soft_digest_final() * * Arguments: * session_p: pointer to soft_session_t struct * pDigest: pointer to the output data after digesting * pulDigestLen: length of the output data * * Description: * called by C_DigestFinal(). This function calls soft_digest_common(). * * Returns: * see return values in soft_digest_common(). */ CK_RV soft_digest_final(soft_session_t *session_p, CK_BYTE_PTR pDigest, CK_ULONG_PTR pulDigestLen) { return (soft_digest_common(session_p, NULL, 0, pDigest, pulDigestLen)); } /* * Perform digest init operation internally for the support of * CKM_MD5_RSA_PKCS, CKM_SHA1_RSA_PKCS, CKM_SHA1_KEY_DERIVATION * and CKM_MD5_KEY_DERIVATION mechanisms. * * This function is called with the session being held, and without * its mutex taken. */ CK_RV soft_digest_init_internal(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism) { CK_RV rv; (void) pthread_mutex_lock(&session_p->session_mutex); /* Check to see if digest operation is already active */ if (session_p->digest.flags & CRYPTO_OPERATION_ACTIVE) { (void) pthread_mutex_unlock(&session_p->session_mutex); return (CKR_OPERATION_ACTIVE); } session_p->digest.flags = CRYPTO_OPERATION_ACTIVE; (void) pthread_mutex_unlock(&session_p->session_mutex); rv = soft_digest_init(session_p, pMechanism); if (rv != CKR_OK) { (void) pthread_mutex_lock(&session_p->session_mutex); session_p->digest.flags &= ~CRYPTO_OPERATION_ACTIVE; (void) pthread_mutex_unlock(&session_p->session_mutex); } return (rv); } /* * Call soft_digest_update() function with the value of a secret key. */ CK_RV soft_digest_key(soft_session_t *session_p, soft_object_t *key_p) { CK_RV rv; /* Only secret key is allowed to be digested */ if (key_p->class != CKO_SECRET_KEY) return (CKR_KEY_INDIGESTIBLE); if ((OBJ_SEC_VALUE(key_p) == NULL) || (OBJ_SEC_VALUE_LEN(key_p) == 0)) return (CKR_KEY_SIZE_RANGE); rv = soft_digest_update(session_p, OBJ_SEC_VALUE(key_p), OBJ_SEC_VALUE_LEN(key_p)); return (rv); } /* * This function releases allocated digest context. The caller * may (lock_held == B_TRUE) or may not (lock_held == B_FALSE) * hold a session mutex. */ void soft_digest_cleanup(soft_session_t *session_p, boolean_t lock_held) { boolean_t lock_true = B_TRUE; if (!lock_held) (void) pthread_mutex_lock(&session_p->session_mutex); if (session_p->digest.context != NULL) { free(session_p->digest.context); session_p->digest.context = NULL; } session_p->digest.flags = 0; if (!lock_held) SES_REFRELE(session_p, lock_true); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2003 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include "softGlobal.h" #include "softSession.h" #include "softObject.h" #include "softOps.h" CK_RV C_DigestEncryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, CK_ULONG_PTR pulEncryptedPartLen) { CK_RV rv; /* * All the front-end checkings will be done in the * C_EncryptUpdate and C_DigestUpdate. */ rv = C_EncryptUpdate(hSession, pPart, ulPartLen, pEncryptedPart, pulEncryptedPartLen); if (rv != CKR_OK) return (rv); /* * If the application just wants to know the length of output * buffer, then we do not digest the data. */ if (pEncryptedPart == NULL) return (CKR_OK); return (C_DigestUpdate(hSession, pPart, ulPartLen)); } CK_RV C_DecryptDigestUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen, CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen) { CK_RV rv; /* * All the front-end checkings will be done in the * C_DecryptUpdate and C_DigestUpdate. */ rv = C_DecryptUpdate(hSession, pEncryptedPart, ulEncryptedPartLen, pPart, pulPartLen); if (rv != CKR_OK) return (rv); /* * If the application just wants to know the length of output * buffer, then we do not digest the data. */ if (pPart == NULL) return (CKR_OK); return (C_DigestUpdate(hSession, pPart, *pulPartLen)); } CK_RV C_SignEncryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, CK_ULONG_PTR pulEncryptedPartLen) { CK_RV rv; /* * All the front-end checkings will be done in the * C_EncryptUpdate and C_SignUpdate. */ rv = C_EncryptUpdate(hSession, pPart, ulPartLen, pEncryptedPart, pulEncryptedPartLen); if (rv != CKR_OK) return (rv); /* * If the application just wants to know the length of output * buffer, then we do not sign the data. */ if (pEncryptedPart == NULL) return (CKR_OK); return (C_SignUpdate(hSession, pPart, ulPartLen)); } CK_RV C_DecryptVerifyUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen, CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen) { CK_RV rv; /* * All the front-end checkings will be done in the * C_DecryptUpdate and C_VerifyUpdate. */ rv = C_DecryptUpdate(hSession, pEncryptedPart, ulEncryptedPartLen, pPart, pulPartLen); if (rv != CKR_OK) return (rv); /* * If the application just wants to know the length of output * buffer, then we do not verify the data. */ if (pPart == NULL) return (CKR_OK); return (C_VerifyUpdate(hSession, pPart, *pulPartLen)); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include "softGlobal.h" #include "softSession.h" #include "softObject.h" #include "softEC.h" #include "softCrypt.h" #include "softOps.h" #include "softMAC.h" void soft_free_ecparams(ECParams *params, boolean_t freeit) { SECITEM_FreeItem(¶ms->fieldID.u.prime, B_FALSE); SECITEM_FreeItem(¶ms->curve.a, B_FALSE); SECITEM_FreeItem(¶ms->curve.b, B_FALSE); SECITEM_FreeItem(¶ms->curve.seed, B_FALSE); SECITEM_FreeItem(¶ms->base, B_FALSE); SECITEM_FreeItem(¶ms->order, B_FALSE); SECITEM_FreeItem(¶ms->DEREncoding, B_FALSE); SECITEM_FreeItem(¶ms->curveOID, B_FALSE); if (freeit) free(params); } static void soft_free_ecc_context(soft_ecc_ctx_t *ecc_ctx) { if (ecc_ctx != NULL) { if (ecc_ctx->key != NULL) { soft_cleanup_object(ecc_ctx->key); free(ecc_ctx->key); } soft_free_ecparams(&ecc_ctx->ecparams, B_FALSE); free(ecc_ctx); } } void soft_free_ecprivkey(ECPrivateKey *key) { soft_free_ecparams(&key->ecParams, B_FALSE); /* * Don't free publicValue or privateValue * as these values are copied into objects. */ SECITEM_FreeItem(&key->version, B_FALSE); free(key); } /* * Called from init routines to do basic sanity checks. Init routines, * e.g. sign_init should fail rather than subsequent operations. */ static int check_key(soft_object_t *key_p, boolean_t sign) { biginteger_t *p; ulong_t len; if (sign) { if ((key_p->class != CKO_PRIVATE_KEY) || (key_p->key_type != CKK_EC)) return (CKR_KEY_TYPE_INCONSISTENT); p = OBJ_PRI_EC_VALUE(key_p); len = p->big_value_len; if (p->big_value == NULL) len = 0; if (len < CRYPTO_BITS2BYTES(EC_MIN_KEY_LEN) || len > CRYPTO_BITS2BYTES(EC_MAX_KEY_LEN)) return (CKR_KEY_SIZE_RANGE); } else { if ((key_p->class != CKO_PUBLIC_KEY) || (key_p->key_type != CKK_EC)) return (CKR_KEY_TYPE_INCONSISTENT); p = OBJ_PUB_EC_POINT(key_p); len = p->big_value_len; if (p->big_value == NULL) len = 0; if (len < CRYPTO_BITS2BYTES(EC_MIN_KEY_LEN) * 2 + 1 || len > CRYPTO_BITS2BYTES(EC_MAX_KEY_LEN) * 2 + 1) return (CKR_KEY_SIZE_RANGE); } return (CKR_OK); } /* * This function places the octet string of the specified attribute * into the corresponding key object. */ static void soft_genECkey_set_attribute(soft_object_t *key, biginteger_t *bi, CK_ATTRIBUTE_TYPE type) { biginteger_t *dst; switch (type) { case CKA_VALUE: dst = OBJ_PRI_EC_VALUE(key); break; case CKA_EC_POINT: dst = OBJ_PUB_EC_POINT(key); break; } copy_bigint_attr(bi, dst); } CK_RV soft_ec_genkey_pair(soft_object_t *pubkey, soft_object_t *prikey) { CK_RV rv; CK_ATTRIBUTE template; ECPrivateKey *privKey; /* contains both public and private values */ ECParams *ecparams; SECKEYECParams params_item; biginteger_t bi; uchar_t param_buffer[EC_MAX_OID_LEN]; uint_t paramlen; if ((pubkey->class != CKO_PUBLIC_KEY) || (pubkey->key_type != CKK_EC)) return (CKR_KEY_TYPE_INCONSISTENT); if ((prikey->class != CKO_PRIVATE_KEY) || (prikey->key_type != CKK_EC)) return (CKR_KEY_TYPE_INCONSISTENT); template.type = CKA_EC_PARAMS; template.pValue = param_buffer; template.ulValueLen = sizeof (param_buffer); rv = soft_get_public_key_attribute(pubkey, &template); if (rv != CKR_OK) { return (rv); } paramlen = template.ulValueLen; /* private key also has CKA_EC_PARAMS attribute */ rv = set_extra_attr_to_object(prikey, CKA_EC_PARAMS, &template); if (rv != CKR_OK) { return (rv); } /* ASN1 check */ if (param_buffer[0] != 0x06 || param_buffer[1] != paramlen - 2) { return (CKR_ATTRIBUTE_VALUE_INVALID); } params_item.len = paramlen; params_item.data = param_buffer; if (EC_DecodeParams(¶ms_item, &ecparams, 0) != SECSuccess) { /* bad curve OID */ return (CKR_ARGUMENTS_BAD); } if (EC_NewKey(ecparams, &privKey, 0) != SECSuccess) { soft_free_ecparams(ecparams, B_TRUE); return (CKR_FUNCTION_FAILED); } bi.big_value = privKey->privateValue.data; bi.big_value_len = privKey->privateValue.len; soft_genECkey_set_attribute(prikey, &bi, CKA_VALUE); bi.big_value = privKey->publicValue.data; bi.big_value_len = privKey->publicValue.len; soft_genECkey_set_attribute(pubkey, &bi, CKA_EC_POINT); soft_free_ecprivkey(privKey); soft_free_ecparams(ecparams, B_TRUE); return (CKR_OK); } CK_RV soft_ec_key_derive(soft_object_t *basekey, soft_object_t *secretkey, void *mech_params, size_t mech_params_len) { CK_RV rv; CK_ATTRIBUTE template; CK_ECDH1_DERIVE_PARAMS *ecdh1_derive_params = mech_params; uchar_t value[EC_MAX_VALUE_LEN]; uint32_t value_len = sizeof (value); uchar_t params[EC_MAX_OID_LEN]; uint32_t params_len = sizeof (params); uint32_t keylen; ECParams *ecparams; SECKEYECParams params_item; SECItem public_value_item, private_value_item, secret_item; uchar_t *buf; if (mech_params_len != sizeof (CK_ECDH1_DERIVE_PARAMS) || ecdh1_derive_params->kdf != CKD_NULL) { return (CKR_MECHANISM_PARAM_INVALID); } template.type = CKA_VALUE; template.pValue = value; template.ulValueLen = value_len; rv = soft_get_private_key_attribute(basekey, &template); if (rv != CKR_OK) { return (rv); } value_len = template.ulValueLen; private_value_item.data = value; private_value_item.len = value_len; template.type = CKA_EC_PARAMS; template.pValue = params; template.ulValueLen = params_len; rv = soft_get_private_key_attribute(basekey, &template); if (rv != CKR_OK) { return (rv); } params_len = template.ulValueLen; switch (secretkey->key_type) { case CKK_DES: keylen = DES_KEYSIZE; break; case CKK_DES2: keylen = DES2_KEYSIZE; break; case CKK_DES3: keylen = DES3_KEYSIZE; break; case CKK_RC4: case CKK_AES: case CKK_GENERIC_SECRET: #ifdef __sparcv9 /* LINTED */ keylen = (uint32_t)OBJ_SEC_VALUE_LEN(secretkey); #else /* !__sparcv9 */ keylen = OBJ_SEC_VALUE_LEN(secretkey); #endif /* __sparcv9 */ break; } /* ASN1 check */ if (params[0] != 0x06 || params[1] != params_len - 2) { return (CKR_ATTRIBUTE_VALUE_INVALID); } params_item.data = params; params_item.len = params_len; if (EC_DecodeParams(¶ms_item, &ecparams, 0) != SECSuccess) { /* bad curve OID */ return (CKR_ARGUMENTS_BAD); } public_value_item.data = ecdh1_derive_params->pPublicData; public_value_item.len = ecdh1_derive_params->ulPublicDataLen; secret_item.data = NULL; secret_item.len = 0; if (ECDH_Derive(&public_value_item, ecparams, &private_value_item, B_FALSE, &secret_item, 0) != SECSuccess) { soft_free_ecparams(ecparams, B_TRUE); return (CKR_FUNCTION_FAILED); } else { rv = CKR_OK; } if (keylen == 0) keylen = secret_item.len; if (keylen > secret_item.len) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto out; } buf = malloc(keylen); if (buf == NULL) { rv = CKR_HOST_MEMORY; goto out; } bcopy(secret_item.data + secret_item.len - keylen, buf, keylen); OBJ_SEC_VALUE_LEN(secretkey) = keylen; OBJ_SEC_VALUE(secretkey) = buf; out: soft_free_ecparams(ecparams, B_TRUE); SECITEM_FreeItem(&secret_item, B_FALSE); return (rv); } /* * Allocate a ECC context for the active sign or verify operation. * This function is called without the session lock held. */ CK_RV soft_ecc_sign_verify_init_common(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism, soft_object_t *key_p, boolean_t sign) { CK_RV rv; CK_ATTRIBUTE template; CK_MECHANISM digest_mech; soft_ecc_ctx_t *ecc_ctx; soft_object_t *tmp_key = NULL; uchar_t params[EC_MAX_OID_LEN]; ECParams *ecparams; SECKEYECParams params_item; if ((rv = check_key(key_p, sign)) != CKR_OK) return (rv); if (pMechanism->mechanism == CKM_ECDSA_SHA1) { digest_mech.mechanism = CKM_SHA_1; rv = soft_digest_init_internal(session_p, &digest_mech); if (rv != CKR_OK) return (rv); } ecc_ctx = malloc(sizeof (soft_ecc_ctx_t)); if (ecc_ctx == NULL) { return (CKR_HOST_MEMORY); } /* * Make a copy of the signature or verification key, and save it * in the ECC crypto context since it will be used later for * signing/verification. We don't want to hold any object reference * on this original key while doing signing/verification. */ (void) pthread_mutex_lock(&key_p->object_mutex); rv = soft_copy_object(key_p, &tmp_key, SOFT_COPY_OBJ_ORIG_SH, NULL); if ((rv != CKR_OK) || (tmp_key == NULL)) { /* Most likely we ran out of space. */ (void) pthread_mutex_unlock(&key_p->object_mutex); free(ecc_ctx); return (rv); } template.type = CKA_EC_PARAMS; template.pValue = params; template.ulValueLen = sizeof (params); rv = soft_get_private_key_attribute(key_p, &template); (void) pthread_mutex_unlock(&key_p->object_mutex); if (rv != CKR_OK) { goto out; } /* ASN1 check */ if (params[0] != 0x06 || params[1] != template.ulValueLen - 2) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto out; } params_item.data = params; params_item.len = template.ulValueLen; ecc_ctx->key = tmp_key; if (EC_DecodeParams(¶ms_item, &ecparams, 0) != SECSuccess) { /* bad curve OID */ rv = CKR_ARGUMENTS_BAD; goto out; } ecc_ctx->ecparams = *ecparams; free(ecparams); (void) pthread_mutex_lock(&session_p->session_mutex); if (sign) { session_p->sign.context = ecc_ctx; session_p->sign.mech.mechanism = pMechanism->mechanism; } else { session_p->verify.context = ecc_ctx; session_p->verify.mech.mechanism = pMechanism->mechanism; } (void) pthread_mutex_unlock(&session_p->session_mutex); return (CKR_OK); out: soft_cleanup_object(tmp_key); free(tmp_key); free(ecc_ctx); return (rv); } CK_RV soft_ecc_digest_sign_common(soft_session_t *session_p, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSigned, CK_ULONG_PTR pulSignedLen, boolean_t Final) { CK_RV rv = CKR_OK; CK_BYTE hash[SHA1_HASH_SIZE]; CK_ULONG hash_len = SHA1_HASH_SIZE; if (pSigned != NULL) { if (Final) { rv = soft_digest_final(session_p, hash, &hash_len); } else { rv = soft_digest(session_p, pData, ulDataLen, hash, &hash_len); } if (rv != CKR_OK) { (void) pthread_mutex_lock(&session_p->session_mutex); soft_free_ecc_context(session_p->sign.context); session_p->sign.context = NULL; session_p->digest.flags = 0; (void) pthread_mutex_unlock(&session_p->session_mutex); return (rv); } } rv = soft_ecc_sign(session_p, hash, hash_len, pSigned, pulSignedLen); (void) pthread_mutex_lock(&session_p->session_mutex); /* soft_digest_common() has freed the digest context */ session_p->digest.flags = 0; (void) pthread_mutex_unlock(&session_p->session_mutex); return (rv); } CK_RV soft_ecc_sign(soft_session_t *session_p, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSigned, CK_ULONG_PTR pulSignedLen) { CK_RV rv = CKR_OK; SECStatus ss; soft_ecc_ctx_t *ecc_ctx = session_p->sign.context; soft_object_t *key = ecc_ctx->key; uchar_t value[EC_MAX_VALUE_LEN]; ECPrivateKey ECkey; SECItem signature_item; SECItem digest_item; uint_t value_len; if ((key->class != CKO_PRIVATE_KEY) || (key->key_type != CKK_EC)) { rv = CKR_KEY_TYPE_INCONSISTENT; goto clean_exit; } if (ulDataLen > EC_MAX_DIGEST_LEN) { rv = CKR_DATA_LEN_RANGE; goto clean_exit; } /* structure assignment */ ECkey.ecParams = ecc_ctx->ecparams; value_len = EC_MAX_VALUE_LEN; rv = soft_get_private_value(key, CKA_VALUE, value, &value_len); if (rv != CKR_OK) { goto clean_exit; } ECkey.privateValue.data = value; ECkey.privateValue.len = value_len; signature_item.data = pSigned; signature_item.len = *pulSignedLen; digest_item.data = pData; digest_item.len = ulDataLen; if ((ss = ECDSA_SignDigest(&ECkey, &signature_item, &digest_item, 0)) != SECSuccess) { if (ss == SECBufferTooSmall) return (CKR_BUFFER_TOO_SMALL); rv = CKR_FUNCTION_FAILED; goto clean_exit; } if (rv == CKR_OK) { *pulSignedLen = signature_item.len; if (pSigned == NULL) return (rv); } clean_exit: (void) pthread_mutex_lock(&session_p->session_mutex); soft_free_ecc_context(session_p->sign.context); session_p->sign.context = NULL; (void) pthread_mutex_unlock(&session_p->session_mutex); return (rv); } CK_RV soft_ecc_verify(soft_session_t *session_p, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) { CK_RV rv = CKR_OK; soft_ecc_ctx_t *ecc_ctx = session_p->verify.context; soft_object_t *key = ecc_ctx->key; uchar_t point[EC_MAX_POINT_LEN]; CK_ATTRIBUTE template; ECPublicKey ECkey; SECItem signature_item; SECItem digest_item; if ((key->class != CKO_PUBLIC_KEY) ||(key->key_type != CKK_EC)) { rv = CKR_KEY_TYPE_INCONSISTENT; goto clean_exit; } if (ulSignatureLen > EC_MAX_SIG_LEN) { rv = CKR_SIGNATURE_LEN_RANGE; goto clean_exit; } if (ulDataLen > EC_MAX_DIGEST_LEN) { rv = CKR_DATA_LEN_RANGE; goto clean_exit; } /* structure assignment */ ECkey.ecParams = ecc_ctx->ecparams; template.type = CKA_EC_POINT; template.pValue = point; template.ulValueLen = sizeof (point); rv = soft_get_public_key_attribute(key, &template); if (rv != CKR_OK) { goto clean_exit; } ECkey.publicValue.data = point; ECkey.publicValue.len = template.ulValueLen; signature_item.data = pSignature; signature_item.len = ulSignatureLen; digest_item.data = pData; digest_item.len = ulDataLen; if (ECDSA_VerifyDigest(&ECkey, &signature_item, &digest_item, 0) != SECSuccess) { rv = CKR_SIGNATURE_INVALID; } else { rv = CKR_OK; } clean_exit: (void) pthread_mutex_lock(&session_p->session_mutex); soft_free_ecc_context(session_p->verify.context); session_p->verify.context = NULL; (void) pthread_mutex_unlock(&session_p->session_mutex); return (rv); } CK_RV soft_ecc_digest_verify_common(soft_session_t *session_p, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSigned, CK_ULONG ulSignedLen, boolean_t Final) { CK_RV rv; CK_BYTE hash[SHA1_HASH_SIZE]; CK_ULONG hash_len = SHA1_HASH_SIZE; if (Final) { rv = soft_digest_final(session_p, hash, &hash_len); } else { rv = soft_digest(session_p, pData, ulDataLen, hash, &hash_len); } if (rv != CKR_OK) { (void) pthread_mutex_lock(&session_p->session_mutex); soft_free_ecc_context(session_p->verify.context); session_p->verify.context = NULL; session_p->digest.flags = 0; (void) pthread_mutex_unlock(&session_p->session_mutex); return (rv); } /* * Now, we are ready to verify the data using signature. * soft_ecc_verify() will free the verification key. */ rv = soft_ecc_verify(session_p, hash, hash_len, pSigned, ulSignedLen); (void) pthread_mutex_lock(&session_p->session_mutex); /* soft_digest_common() has freed the digest context */ session_p->digest.flags = 0; (void) pthread_mutex_unlock(&session_p->session_mutex); return (rv); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SOFTEC_H #define _SOFTEC_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include "softObject.h" #include "softSession.h" typedef struct soft_ecc_ctx { soft_object_t *key; ECParams ecparams; } soft_ecc_ctx_t; extern CK_RV soft_get_public_key_attribute(soft_object_t *, CK_ATTRIBUTE_PTR); extern CK_RV soft_get_private_key_attribute(soft_object_t *, CK_ATTRIBUTE_PTR); extern CK_RV set_extra_attr_to_object(soft_object_t *, CK_ATTRIBUTE_TYPE, CK_ATTRIBUTE_PTR); extern CK_RV soft_ec_genkey_pair(soft_object_t *, soft_object_t *); extern CK_RV soft_ec_key_derive(soft_object_t *, soft_object_t *, void *, size_t); extern CK_RV soft_ecc_sign_verify_init_common(soft_session_t *, CK_MECHANISM_PTR, soft_object_t *, boolean_t); extern CK_RV soft_ecc_sign(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR); extern CK_RV soft_ecc_verify(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG); extern CK_RV soft_ecc_digest_sign_common(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR, boolean_t); extern CK_RV soft_ecc_digest_verify_common(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG, boolean_t); #ifdef __cplusplus } #endif #endif /* _SOFTEC_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright (c) 2018, Joyent, Inc. */ #include #include #include "softGlobal.h" #include "softSession.h" #include "softObject.h" #include "softOps.h" CK_RV C_EncryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) { CK_RV rv; soft_session_t *session_p; soft_object_t *key_p; boolean_t lock_held = B_FALSE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if (pMechanism == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } /* Obtain the object pointer. */ HANDLE2OBJECT(hKey, key_p, rv); if (rv != CKR_OK) goto clean_exit; /* Check to see if key object allows for encryption. */ if (!(key_p->bool_attr_mask & ENCRYPT_BOOL_ON)) { rv = CKR_KEY_FUNCTION_NOT_PERMITTED; goto clean_exit1; } (void) pthread_mutex_lock(&session_p->session_mutex); lock_held = B_TRUE; /* Check to see if encrypt operation is already active. */ if (session_p->encrypt.flags & CRYPTO_OPERATION_ACTIVE) { /* free the memory to avoid memory leak */ soft_crypt_cleanup(session_p, B_TRUE, lock_held); } /* * This active flag will remain ON until application calls either * C_Encrypt or C_EncryptFinal to actually obtain the final piece * of ciphertext. */ session_p->encrypt.flags = CRYPTO_OPERATION_ACTIVE; (void) pthread_mutex_unlock(&session_p->session_mutex); lock_held = B_FALSE; rv = soft_encrypt_init(session_p, pMechanism, key_p); if (rv != CKR_OK) { (void) pthread_mutex_lock(&session_p->session_mutex); session_p->encrypt.flags &= ~CRYPTO_OPERATION_ACTIVE; lock_held = B_TRUE; } clean_exit1: OBJ_REFRELE(key_p); clean_exit: SES_REFRELE(session_p, lock_held); return (rv); } CK_RV C_Encrypt(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pEncryptedData, CK_ULONG_PTR pulEncryptedDataLen) { CK_RV rv; soft_session_t *session_p; boolean_t lock_held = B_FALSE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); /* * How to handle zero input length depends on the mechanism in use. * For secret key mechanisms, unpadded ones yield zero length output, * but padded ones always result in greater than zero length output. */ if (pData == NULL && ulDataLen != 0) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } /* * Only check if pulEncryptedDataLen is NULL. * No need to check if pEncryptedData is NULL because * application might just ask for the length of buffer to hold * the ciphertext. */ if (pulEncryptedDataLen == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } (void) pthread_mutex_lock(&session_p->session_mutex); lock_held = B_TRUE; /* Application must call C_EncryptInit before calling C_Encrypt. */ if (!(session_p->encrypt.flags & CRYPTO_OPERATION_ACTIVE)) { SES_REFRELE(session_p, lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } /* * C_Encrypt must be called without intervening C_EncryptUpdate * calls. */ if (session_p->encrypt.flags & CRYPTO_OPERATION_UPDATE) { /* * C_Encrypt can not be used to terminate a multi-part * operation, so we'll leave the active encrypt operation * flag on and let the application continue with the * encrypt update operation. */ SES_REFRELE(session_p, lock_held); return (CKR_FUNCTION_FAILED); } (void) pthread_mutex_unlock(&session_p->session_mutex); lock_held = B_FALSE; rv = soft_encrypt(session_p, pData, ulDataLen, pEncryptedData, pulEncryptedDataLen); if ((rv == CKR_BUFFER_TOO_SMALL) || (pEncryptedData == NULL && rv == CKR_OK)) { /* * We will not terminate the active encrypt operation flag, * when the application-supplied buffer is too small, or * the application asks for the length of buffer to hold * the ciphertext. */ SES_REFRELE(session_p, lock_held); return (rv); } clean_exit: /* Clear context, free key, and release session counter */ soft_crypt_cleanup(session_p, B_TRUE, B_FALSE); return (rv); } CK_RV C_EncryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, CK_ULONG_PTR pulEncryptedPartLen) { CK_RV rv; soft_session_t *session_p; boolean_t lock_held = B_FALSE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); /* * Only check if input buffer is null. How to handle zero input * length depends on the mechanism in use. For secret key mechanisms, * unpadded ones yeild zero length output, but padded ones always * result in greater than zero length output. */ if (pPart == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } /* * Only check if pulEncryptedPartLen is NULL. * No need to check if pEncryptedPart is NULL because * application might just ask for the length of buffer to hold * the ciphertext. */ if (pulEncryptedPartLen == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } (void) pthread_mutex_lock(&session_p->session_mutex); lock_held = B_TRUE; /* * Application must call C_EncryptInit before calling * C_EncryptUpdate. */ if (!(session_p->encrypt.flags & CRYPTO_OPERATION_ACTIVE)) { SES_REFRELE(session_p, lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } session_p->encrypt.flags |= CRYPTO_OPERATION_UPDATE; (void) pthread_mutex_unlock(&session_p->session_mutex); lock_held = B_FALSE; rv = soft_encrypt_update(session_p, pPart, ulPartLen, pEncryptedPart, pulEncryptedPartLen); /* * If CKR_OK or CKR_BUFFER_TOO_SMALL, don't terminate the * current encryption operation. */ if ((rv == CKR_OK) || (rv == CKR_BUFFER_TOO_SMALL)) { SES_REFRELE(session_p, lock_held); return (rv); } clean_exit: /* * After an error occurred, terminate the current encrypt * operation by resetting the active and update flags. */ soft_crypt_cleanup(session_p, B_TRUE, lock_held); return (rv); } CK_RV C_EncryptFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pLastEncryptedPart, CK_ULONG_PTR pulLastEncryptedPartLen) { CK_RV rv; soft_session_t *session_p; boolean_t lock_held = B_FALSE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if (pulLastEncryptedPartLen == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } (void) pthread_mutex_lock(&session_p->session_mutex); lock_held = B_TRUE; /* * Application must call C_EncryptInit before calling * C_EncryptFinal. */ if (!(session_p->encrypt.flags & CRYPTO_OPERATION_ACTIVE)) { SES_REFRELE(session_p, lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } (void) pthread_mutex_unlock(&session_p->session_mutex); lock_held = B_FALSE; rv = soft_encrypt_final(session_p, pLastEncryptedPart, pulLastEncryptedPartLen); if ((rv == CKR_BUFFER_TOO_SMALL) || (pLastEncryptedPart == NULL && rv == CKR_OK)) { /* * We will not terminate the active encrypt operation flag, * when the application-supplied buffer is too small, or * the application asks for the length of buffer to hold * the ciphertext. */ SES_REFRELE(session_p, lock_held); return (rv); } /* Terminates the active encrypt operation. */ (void) pthread_mutex_lock(&session_p->session_mutex); session_p->encrypt.flags = 0; lock_held = B_TRUE; SES_REFRELE(session_p, lock_held); return (rv); clean_exit: /* Terminates the active encrypt operation. */ soft_crypt_cleanup(session_p, B_TRUE, lock_held); return (rv); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2015 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2018, Joyent, Inc. * Copyright 2017 Jason King. */ #include #include #include #include #include #include #include #include #include "softSession.h" #include "softObject.h" #include "softOps.h" #include "softCrypt.h" #include "softRSA.h" /* * Add padding bytes with the value of length of padding. */ void soft_add_pkcs7_padding(CK_BYTE *buf, int block_size, CK_ULONG data_len) { (void) pkcs7_encode(NULL, data_len, buf, block_size, block_size); } /* * Perform encrypt init operation internally for the support of * CKM_AES and CKM_DES MAC operations. * * This function is called with the session being held, and without * its mutex taken. */ CK_RV soft_encrypt_init_internal(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism, soft_object_t *key_p) { CK_RV rv; (void) pthread_mutex_lock(&session_p->session_mutex); /* Check to see if encrypt operation is already active */ if (session_p->encrypt.flags & CRYPTO_OPERATION_ACTIVE) { (void) pthread_mutex_unlock(&session_p->session_mutex); return (CKR_OPERATION_ACTIVE); } session_p->encrypt.flags = CRYPTO_OPERATION_ACTIVE; (void) pthread_mutex_unlock(&session_p->session_mutex); rv = soft_encrypt_init(session_p, pMechanism, key_p); if (rv != CKR_OK) { (void) pthread_mutex_lock(&session_p->session_mutex); session_p->encrypt.flags &= ~CRYPTO_OPERATION_ACTIVE; (void) pthread_mutex_unlock(&session_p->session_mutex); } return (rv); } /* * soft_encrypt_init() * * Arguments: * session_p: pointer to soft_session_t struct * pMechanism: pointer to CK_MECHANISM struct provided by application * key_p: pointer to key soft_object_t struct * * Description: * called by C_EncryptInit(). This function calls the corresponding * encrypt init routine based on the mechanism. * * Returns: * CKR_OK: success * CKR_HOST_MEMORY: run out of system memory * CKR_MECHANISM_PARAM_INVALID: invalid parameters in mechanism * CKR_MECHANISM_INVALID: invalid mechanism type * CKR_KEY_TYPE_INCONSISTENT: incorrect type of key to use * with the specified mechanism */ CK_RV soft_encrypt_init(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism, soft_object_t *key_p) { CK_RV rv; switch (pMechanism->mechanism) { case CKM_DES_ECB: if (key_p->key_type != CKK_DES) { return (CKR_KEY_TYPE_INCONSISTENT); } goto ecb_common; case CKM_DES3_ECB: if ((key_p->key_type != CKK_DES2) && (key_p->key_type != CKK_DES3)) { return (CKR_KEY_TYPE_INCONSISTENT); } ecb_common: return (soft_des_crypt_init_common(session_p, pMechanism, key_p, B_TRUE)); case CKM_DES_CBC: case CKM_DES_CBC_PAD: if (key_p->key_type != CKK_DES) { return (CKR_KEY_TYPE_INCONSISTENT); } goto cbc_common; case CKM_DES3_CBC: case CKM_DES3_CBC_PAD: { soft_des_ctx_t *soft_des_ctx; if ((key_p->key_type != CKK_DES2) && (key_p->key_type != CKK_DES3)) { return (CKR_KEY_TYPE_INCONSISTENT); } cbc_common: if ((pMechanism->pParameter == NULL) || (pMechanism->ulParameterLen != DES_BLOCK_LEN)) { return (CKR_MECHANISM_PARAM_INVALID); } rv = soft_des_crypt_init_common(session_p, pMechanism, key_p, B_TRUE); if (rv != CKR_OK) return (rv); (void) pthread_mutex_lock(&session_p->session_mutex); soft_des_ctx = (soft_des_ctx_t *)session_p->encrypt.context; /* Copy Initialization Vector (IV) into the context. */ (void) memcpy(soft_des_ctx->ivec, pMechanism->pParameter, DES_BLOCK_LEN); /* Allocate a context for DES cipher-block chaining. */ soft_des_ctx->des_cbc = (void *)des_cbc_ctx_init( soft_des_ctx->key_sched, soft_des_ctx->keysched_len, soft_des_ctx->ivec, key_p->key_type); if (soft_des_ctx->des_cbc == NULL) { freezero(soft_des_ctx->key_sched, soft_des_ctx->keysched_len); freezero(session_p->encrypt.context, sizeof (soft_des_ctx_t)); session_p->encrypt.context = NULL; rv = CKR_HOST_MEMORY; } (void) pthread_mutex_unlock(&session_p->session_mutex); return (rv); } case CKM_AES_ECB: case CKM_AES_CBC: case CKM_AES_CBC_PAD: case CKM_AES_CMAC: case CKM_AES_CTR: case CKM_AES_CCM: case CKM_AES_GCM: case CKM_AES_GMAC: return (soft_aes_crypt_init_common(session_p, pMechanism, key_p, B_TRUE)); case CKM_RC4: if (key_p->key_type != CKK_RC4) { return (CKR_KEY_TYPE_INCONSISTENT); } return (soft_arcfour_crypt_init(session_p, pMechanism, key_p, B_TRUE)); case CKM_RSA_X_509: case CKM_RSA_PKCS: if (key_p->key_type != CKK_RSA) { return (CKR_KEY_TYPE_INCONSISTENT); } return (soft_rsa_crypt_init_common(session_p, pMechanism, key_p, B_TRUE)); case CKM_BLOWFISH_CBC: { soft_blowfish_ctx_t *soft_blowfish_ctx; if (key_p->key_type != CKK_BLOWFISH) return (CKR_KEY_TYPE_INCONSISTENT); if ((pMechanism->pParameter == NULL) || (pMechanism->ulParameterLen != BLOWFISH_BLOCK_LEN)) return (CKR_MECHANISM_PARAM_INVALID); rv = soft_blowfish_crypt_init_common(session_p, pMechanism, key_p, B_TRUE); if (rv != CKR_OK) return (rv); (void) pthread_mutex_lock(&session_p->session_mutex); soft_blowfish_ctx = (soft_blowfish_ctx_t *)session_p->encrypt.context; /* Copy Initialization Vector (IV) into the context. */ (void) memcpy(soft_blowfish_ctx->ivec, pMechanism->pParameter, BLOWFISH_BLOCK_LEN); /* Allocate a context for Blowfish cipher-block chaining */ soft_blowfish_ctx->blowfish_cbc = (void *)blowfish_cbc_ctx_init(soft_blowfish_ctx->key_sched, soft_blowfish_ctx->keysched_len, soft_blowfish_ctx->ivec); if (soft_blowfish_ctx->blowfish_cbc == NULL) { freezero(soft_blowfish_ctx->key_sched, soft_blowfish_ctx->keysched_len); freezero(session_p->encrypt.context, sizeof (soft_blowfish_ctx_t)); session_p->encrypt.context = NULL; rv = CKR_HOST_MEMORY; } (void) pthread_mutex_unlock(&session_p->session_mutex); return (rv); } default: return (CKR_MECHANISM_INVALID); } } /* * soft_encrypt_common() * * Arguments: * session_p: pointer to soft_session_t struct * pData: pointer to the input data to be encrypted * ulDataLen: length of the input data * pEncrypted: pointer to the output data after encryption * pulEncryptedLen: pointer to the length of the output data * update: boolean flag indicates caller is soft_encrypt * or soft_encrypt_update * * Description: * This function calls the corresponding encrypt routine based * on the mechanism. * * Returns: * see corresponding encrypt routine. */ CK_RV soft_encrypt_common(soft_session_t *session_p, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pEncrypted, CK_ULONG_PTR pulEncryptedLen, boolean_t update) { CK_MECHANISM_TYPE mechanism = session_p->encrypt.mech.mechanism; switch (mechanism) { case CKM_DES_ECB: case CKM_DES_CBC: case CKM_DES3_ECB: case CKM_DES3_CBC: if (ulDataLen == 0) { *pulEncryptedLen = 0; return (CKR_OK); } /* FALLTHROUGH */ case CKM_DES_CBC_PAD: case CKM_DES3_CBC_PAD: return (soft_des_encrypt_common(session_p, pData, ulDataLen, pEncrypted, pulEncryptedLen, update)); case CKM_AES_ECB: case CKM_AES_CBC: case CKM_AES_CBC_PAD: case CKM_AES_CTR: case CKM_AES_CCM: case CKM_AES_CMAC: case CKM_AES_GCM: case CKM_AES_GMAC: if (update) { return (soft_aes_encrypt_update(session_p, pData, ulDataLen, pEncrypted, pulEncryptedLen)); } else { return (soft_aes_encrypt(session_p, pData, ulDataLen, pEncrypted, pulEncryptedLen)); } case CKM_BLOWFISH_CBC: if (ulDataLen == 0) { *pulEncryptedLen = 0; return (CKR_OK); } return (soft_blowfish_encrypt_common(session_p, pData, ulDataLen, pEncrypted, pulEncryptedLen, update)); case CKM_RC4: if (ulDataLen == 0) { *pulEncryptedLen = 0; return (CKR_OK); } return (soft_arcfour_crypt(&(session_p->encrypt), pData, ulDataLen, pEncrypted, pulEncryptedLen)); case CKM_RSA_X_509: case CKM_RSA_PKCS: return (soft_rsa_encrypt_common(session_p, pData, ulDataLen, pEncrypted, pulEncryptedLen, mechanism)); default: return (CKR_MECHANISM_INVALID); } } /* * soft_encrypt() * * Arguments: * session_p: pointer to soft_session_t struct * pData: pointer to the input data to be encrypted * ulDataLen: length of the input data * pEncryptedData: pointer to the output data after encryption * pulEncryptedDataLen: pointer to the length of the output data * * Description: * called by C_Encrypt(). This function calls the soft_encrypt_common * routine. * * Returns: * see soft_encrypt_common(). */ CK_RV soft_encrypt(soft_session_t *session_p, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pEncryptedData, CK_ULONG_PTR pulEncryptedDataLen) { return (soft_encrypt_common(session_p, pData, ulDataLen, pEncryptedData, pulEncryptedDataLen, B_FALSE)); } /* * soft_encrypt_update() * * Arguments: * session_p: pointer to soft_session_t struct * pPart: pointer to the input data to be digested * ulPartLen: length of the input data * pEncryptedPart: pointer to the ciphertext * pulEncryptedPartLen: pointer to the length of the ciphertext * * Description: * called by C_EncryptUpdate(). This function calls the * soft_encrypt_common routine (with update flag on). * * Returns: * see soft_encrypt_common(). */ CK_RV soft_encrypt_update(soft_session_t *session_p, CK_BYTE_PTR pPart, CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, CK_ULONG_PTR pulEncryptedPartLen) { CK_MECHANISM_TYPE mechanism = session_p->encrypt.mech.mechanism; switch (mechanism) { case CKM_DES_ECB: case CKM_DES_CBC: case CKM_DES_CBC_PAD: case CKM_DES3_ECB: case CKM_DES3_CBC: case CKM_DES3_CBC_PAD: case CKM_AES_ECB: case CKM_AES_CBC: case CKM_AES_CBC_PAD: case CKM_AES_CMAC: case CKM_AES_CTR: case CKM_AES_CCM: case CKM_AES_GCM: case CKM_AES_GMAC: case CKM_BLOWFISH_CBC: case CKM_RC4: return (soft_encrypt_common(session_p, pPart, ulPartLen, pEncryptedPart, pulEncryptedPartLen, B_TRUE)); default: /* PKCS11: The mechanism only supports single-part operation. */ return (CKR_MECHANISM_INVALID); } } /* * soft_encrypt_final() * * Arguments: * session_p: pointer to soft_session_t struct * pLastEncryptedPart: pointer to the last encrypted data part * pulLastEncryptedPartLen: pointer to the length of the last * encrypted data part * * Description: * called by C_EncryptFinal(). * * Returns: * CKR_OK: success * CKR_FUNCTION_FAILED: encrypt final function failed * CKR_DATA_LEN_RANGE: remaining buffer contains bad length */ CK_RV soft_encrypt_final(soft_session_t *session_p, CK_BYTE_PTR pLastEncryptedPart, CK_ULONG_PTR pulLastEncryptedPartLen) { CK_MECHANISM_TYPE mechanism = session_p->encrypt.mech.mechanism; CK_ULONG out_len; CK_RV rv = CKR_OK; int rc; (void) pthread_mutex_lock(&session_p->session_mutex); if (session_p->encrypt.context == NULL) { rv = CKR_OPERATION_NOT_INITIALIZED; *pulLastEncryptedPartLen = 0; goto clean1; } switch (mechanism) { case CKM_DES_CBC_PAD: case CKM_DES3_CBC_PAD: { soft_des_ctx_t *soft_des_ctx; soft_des_ctx = (soft_des_ctx_t *)session_p->encrypt.context; /* * For CKM_DES_CBC_PAD, compute output length with * padding. If the remaining buffer has one block * of data, then output length will be two blocksize of * ciphertext. If the remaining buffer has less than * one block of data, then output length will be * one blocksize. */ if (soft_des_ctx->remain_len == DES_BLOCK_LEN) out_len = 2 * DES_BLOCK_LEN; else out_len = DES_BLOCK_LEN; if (pLastEncryptedPart == NULL) { /* * Application asks for the length of the output * buffer to hold the ciphertext. */ *pulLastEncryptedPartLen = out_len; goto clean1; } else { crypto_data_t out; /* Copy remaining data to the output buffer. */ (void) memcpy(pLastEncryptedPart, soft_des_ctx->data, soft_des_ctx->remain_len); /* * Add padding bytes prior to encrypt final. */ soft_add_pkcs7_padding(pLastEncryptedPart + soft_des_ctx->remain_len, DES_BLOCK_LEN, soft_des_ctx->remain_len); out.cd_format = CRYPTO_DATA_RAW; out.cd_offset = 0; out.cd_length = out_len; out.cd_raw.iov_base = (char *)pLastEncryptedPart; out.cd_raw.iov_len = out_len; /* Encrypt multiple blocks of data. */ rc = des_encrypt_contiguous_blocks( (des_ctx_t *)soft_des_ctx->des_cbc, (char *)pLastEncryptedPart, out_len, &out); if (rc == 0) { *pulLastEncryptedPartLen = out_len; } else { *pulLastEncryptedPartLen = 0; rv = CKR_FUNCTION_FAILED; } /* Cleanup memory space. */ free(soft_des_ctx->des_cbc); freezero(soft_des_ctx->key_sched, soft_des_ctx->keysched_len); } break; } case CKM_DES_CBC: case CKM_DES_ECB: case CKM_DES3_CBC: case CKM_DES3_ECB: { soft_des_ctx_t *soft_des_ctx; soft_des_ctx = (soft_des_ctx_t *)session_p->encrypt.context; /* * CKM_DES_CBC and CKM_DES_ECB does not do any padding, * so when the final is called, the remaining buffer * should not contain any more data. */ *pulLastEncryptedPartLen = 0; if (soft_des_ctx->remain_len != 0) { rv = CKR_DATA_LEN_RANGE; } else { if (pLastEncryptedPart == NULL) goto clean1; } /* Cleanup memory space. */ free(soft_des_ctx->des_cbc); freezero(soft_des_ctx->key_sched, soft_des_ctx->keysched_len); break; } case CKM_AES_CBC: case CKM_AES_CBC_PAD: case CKM_AES_CMAC: case CKM_AES_ECB: case CKM_AES_CTR: case CKM_AES_CCM: case CKM_AES_GCM: case CKM_AES_GMAC: rv = soft_aes_encrypt_final(session_p, pLastEncryptedPart, pulLastEncryptedPartLen); break; case CKM_BLOWFISH_CBC: { soft_blowfish_ctx_t *soft_blowfish_ctx; soft_blowfish_ctx = (soft_blowfish_ctx_t *)session_p->encrypt.context; /* * CKM_BLOWFISH_CBC does not do any padding, so when the * final is called, the remaining buffer should not contain * any more data */ *pulLastEncryptedPartLen = 0; if (soft_blowfish_ctx->remain_len != 0) rv = CKR_DATA_LEN_RANGE; else { if (pLastEncryptedPart == NULL) goto clean1; } free(soft_blowfish_ctx->blowfish_cbc); freezero(soft_blowfish_ctx->key_sched, soft_blowfish_ctx->keysched_len); break; } case CKM_RC4: { ARCFour_key *key = (ARCFour_key *)session_p->encrypt.context; /* Remaining data size is always zero for RC4. */ *pulLastEncryptedPartLen = 0; if (pLastEncryptedPart == NULL) goto clean1; explicit_bzero(key, sizeof (*key)); break; } default: /* PKCS11: The mechanism only supports single-part operation. */ rv = CKR_MECHANISM_INVALID; break; } free(session_p->encrypt.context); session_p->encrypt.context = NULL; clean1: (void) pthread_mutex_unlock(&session_p->session_mutex); return (rv); } /* * This function frees the allocated active crypto context and the * lower level of allocated struct as needed. * This function is called by the 1st tier of encrypt/decrypt routines * or by the 2nd tier of session close routine. Since the 1st tier * caller will always call this function without locking the session * mutex and the 2nd tier caller will call with the lock, we add the * third parameter "lock_held" to distinguish this case. */ void soft_crypt_cleanup(soft_session_t *session_p, boolean_t encrypt, boolean_t lock_held) { crypto_active_op_t *active_op; boolean_t lock_true = B_TRUE; if (!lock_held) (void) pthread_mutex_lock(&session_p->session_mutex); active_op = (encrypt) ? &(session_p->encrypt) : &(session_p->decrypt); switch (active_op->mech.mechanism) { case CKM_DES_CBC_PAD: case CKM_DES3_CBC_PAD: case CKM_DES_CBC: case CKM_DES_ECB: case CKM_DES3_CBC: case CKM_DES3_ECB: { soft_des_ctx_t *soft_des_ctx = (soft_des_ctx_t *)active_op->context; des_ctx_t *des_ctx; if (soft_des_ctx != NULL) { des_ctx = (des_ctx_t *)soft_des_ctx->des_cbc; if (des_ctx != NULL) { explicit_bzero(des_ctx->dc_keysched, des_ctx->dc_keysched_len); free(soft_des_ctx->des_cbc); } freezero(soft_des_ctx->key_sched, soft_des_ctx->keysched_len); } break; } case CKM_AES_CBC: case CKM_AES_CBC_PAD: case CKM_AES_CMAC: case CKM_AES_ECB: case CKM_AES_CTR: case CKM_AES_CCM: case CKM_AES_GCM: case CKM_AES_GMAC: soft_aes_free_ctx(active_op->context); active_op->context = NULL; break; case CKM_BLOWFISH_CBC: { soft_blowfish_ctx_t *soft_blowfish_ctx = (soft_blowfish_ctx_t *)active_op->context; blowfish_ctx_t *blowfish_ctx; if (soft_blowfish_ctx != NULL) { blowfish_ctx = (blowfish_ctx_t *)soft_blowfish_ctx->blowfish_cbc; if (blowfish_ctx != NULL) { explicit_bzero(blowfish_ctx->bc_keysched, blowfish_ctx->bc_keysched_len); free(soft_blowfish_ctx->blowfish_cbc); } freezero(soft_blowfish_ctx->key_sched, soft_blowfish_ctx->keysched_len); } break; } case CKM_RC4: { ARCFour_key *key = (ARCFour_key *)active_op->context; if (key != NULL) explicit_bzero(key, sizeof (*key)); break; } case CKM_RSA_X_509: case CKM_RSA_PKCS: { soft_rsa_ctx_t *rsa_ctx = (soft_rsa_ctx_t *)active_op->context; if (rsa_ctx != NULL) if (rsa_ctx->key != NULL) { soft_cleanup_object(rsa_ctx->key); free(rsa_ctx->key); } break; } } /* switch */ if (active_op->context != NULL) { free(active_op->context); active_op->context = NULL; } active_op->flags = 0; if (!lock_held) SES_REFRELE(session_p, lock_true); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2014, OmniTI Computer Consulting, Inc. All rights reserved. * * Copyright 2020 Joyent, Inc. */ #include #include #include #include /* for pid_t */ #include #include #include #include #include "softGlobal.h" #include "softSession.h" #include "softObject.h" #include "softKeystore.h" #include "softKeystoreUtil.h" #pragma init(softtoken_init) #pragma fini(softtoken_fini) extern soft_session_t token_session; /* for fork handler */ static struct CK_FUNCTION_LIST functionList = { { 2, 20 }, /* version */ C_Initialize, C_Finalize, C_GetInfo, C_GetFunctionList, C_GetSlotList, C_GetSlotInfo, C_GetTokenInfo, C_GetMechanismList, C_GetMechanismInfo, C_InitToken, C_InitPIN, C_SetPIN, C_OpenSession, C_CloseSession, C_CloseAllSessions, C_GetSessionInfo, C_GetOperationState, C_SetOperationState, C_Login, C_Logout, C_CreateObject, C_CopyObject, C_DestroyObject, C_GetObjectSize, C_GetAttributeValue, C_SetAttributeValue, C_FindObjectsInit, C_FindObjects, C_FindObjectsFinal, C_EncryptInit, C_Encrypt, C_EncryptUpdate, C_EncryptFinal, C_DecryptInit, C_Decrypt, C_DecryptUpdate, C_DecryptFinal, C_DigestInit, C_Digest, C_DigestUpdate, C_DigestKey, C_DigestFinal, C_SignInit, C_Sign, C_SignUpdate, C_SignFinal, C_SignRecoverInit, C_SignRecover, C_VerifyInit, C_Verify, C_VerifyUpdate, C_VerifyFinal, C_VerifyRecoverInit, C_VerifyRecover, C_DigestEncryptUpdate, C_DecryptDigestUpdate, C_SignEncryptUpdate, C_DecryptVerifyUpdate, C_GenerateKey, C_GenerateKeyPair, C_WrapKey, C_UnwrapKey, C_DeriveKey, C_SeedRandom, C_GenerateRandom, C_GetFunctionStatus, C_CancelFunction, C_WaitForSlotEvent }; boolean_t softtoken_initialized = B_FALSE; static pid_t softtoken_pid = 0; /* * Lock ordering in pkcs11_softtoken is as follows: * soft_giant_mutex * soft_sessionlist_mutex * soft_slot.slot_mutex * soft_slot.keystore_mutex * token session mutexes * soft_session_t->session_mutex * soft_object_mutex * soft_object_t->object_mutex * obj_delay_freed.obj_to_be_free_mutex * ses_delay_freed.ses_to_br_free_mutex */ /* * This mutex protects soft_session_list, all_sessions_closing, * and soft_session_tree */ pthread_mutex_t soft_sessionlist_mutex; soft_session_t *soft_session_list = NULL; avl_tree_t soft_session_tree; /* This protects soft_object_tree */ pthread_mutex_t soft_object_mutex; avl_tree_t soft_object_tree; int all_sessions_closing = 0; slot_t soft_slot; obj_to_be_freed_list_t obj_delay_freed; ses_to_be_freed_list_t ses_delay_freed; /* protects softtoken_initialized and access to C_Initialize/C_Finalize */ pthread_mutex_t soft_giant_mutex = PTHREAD_MUTEX_INITIALIZER; static CK_RV finalize_common(boolean_t force, CK_VOID_PTR pReserved); static void softtoken_init(void); static void softtoken_fini(void); static void softtoken_fork_prepare(void); static void softtoken_fork_after(void); static int session_compare(const void *a, const void *b); static int object_compare(const void *a, const void *b); CK_RV C_Initialize(CK_VOID_PTR pInitArgs) { pthread_mutexattr_t attr = { 0 }; int initialize_pid; boolean_t supplied_ok; CK_RV rv; /* * Get lock to insure only one thread enters this * function at a time. */ (void) pthread_mutex_lock(&soft_giant_mutex); initialize_pid = getpid(); if (softtoken_initialized) { if (initialize_pid == softtoken_pid) { /* * This process has called C_Initialize already */ (void) pthread_mutex_unlock(&soft_giant_mutex); return (CKR_CRYPTOKI_ALREADY_INITIALIZED); } else { /* * A fork has happened and the child is * reinitializing. Do a finalize_common to close * out any state from the parent, and then * continue on. */ (void) finalize_common(B_TRUE, NULL); } } if (pInitArgs != NULL) { CK_C_INITIALIZE_ARGS *initargs1 = (CK_C_INITIALIZE_ARGS *) pInitArgs; /* pReserved must be NULL */ if (initargs1->pReserved != NULL) { (void) pthread_mutex_unlock(&soft_giant_mutex); return (CKR_ARGUMENTS_BAD); } /* * ALL supplied function pointers need to have the value * either NULL or non-NULL. */ supplied_ok = (initargs1->CreateMutex == NULL && initargs1->DestroyMutex == NULL && initargs1->LockMutex == NULL && initargs1->UnlockMutex == NULL) || (initargs1->CreateMutex != NULL && initargs1->DestroyMutex != NULL && initargs1->LockMutex != NULL && initargs1->UnlockMutex != NULL); if (!supplied_ok) { (void) pthread_mutex_unlock(&soft_giant_mutex); return (CKR_ARGUMENTS_BAD); } /* * When the CKF_OS_LOCKING_OK flag isn't set and mutex * function pointers are supplied by an application, * return an error. We must be able to use our own primitives. */ if (!(initargs1->flags & CKF_OS_LOCKING_OK) && (initargs1->CreateMutex != NULL)) { (void) pthread_mutex_unlock(&soft_giant_mutex); return (CKR_CANT_LOCK); } } if (pthread_mutexattr_init(&attr) != 0) { (void) pthread_mutex_unlock(&soft_giant_mutex); return (CKR_HOST_MEMORY); } VERIFY0(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK)); /* Initialize the session list lock */ if (pthread_mutex_init(&soft_sessionlist_mutex, &attr) != 0) { (void) pthread_mutexattr_destroy(&attr); (void) pthread_mutex_unlock(&soft_giant_mutex); return (CKR_CANT_LOCK); } if (pthread_mutex_init(&soft_object_mutex, &attr) != 0) { (void) pthread_mutexattr_destroy(&attr); (void) pthread_mutex_unlock(&soft_giant_mutex); return (CKR_CANT_LOCK); } VERIFY0(pthread_mutexattr_destroy(&attr)); /* * token object related initialization */ soft_slot.authenticated = 0; soft_slot.userpin_change_needed = 0; soft_slot.token_object_list = NULL; soft_slot.keystore_load_status = KEYSTORE_UNINITIALIZED; if ((rv = soft_init_token_session()) != CKR_OK) { (void) pthread_mutex_destroy(&soft_sessionlist_mutex); (void) pthread_mutex_unlock(&soft_giant_mutex); return (rv); } /* Initialize the slot lock */ if (pthread_mutex_init(&soft_slot.slot_mutex, NULL) != 0) { (void) pthread_mutex_destroy(&soft_sessionlist_mutex); (void) soft_destroy_token_session(); (void) pthread_mutex_unlock(&soft_giant_mutex); return (CKR_CANT_LOCK); } /* Initialize the keystore lock */ if (pthread_mutex_init(&soft_slot.keystore_mutex, NULL) != 0) { (void) pthread_mutex_destroy(&soft_slot.slot_mutex); (void) pthread_mutex_destroy(&soft_sessionlist_mutex); (void) soft_destroy_token_session(); (void) pthread_mutex_unlock(&soft_giant_mutex); return (CKR_CANT_LOCK); } /* Initialize the object_to_be_freed list */ if (pthread_mutex_init(&obj_delay_freed.obj_to_be_free_mutex, NULL) != 0) { (void) pthread_mutex_destroy(&soft_slot.keystore_mutex); (void) pthread_mutex_destroy(&soft_slot.slot_mutex); (void) pthread_mutex_destroy(&soft_sessionlist_mutex); (void) soft_destroy_token_session(); (void) pthread_mutex_unlock(&soft_giant_mutex); return (CKR_CANT_LOCK); } obj_delay_freed.count = 0; obj_delay_freed.first = NULL; obj_delay_freed.last = NULL; if (pthread_mutex_init(&ses_delay_freed.ses_to_be_free_mutex, NULL) != 0) { (void) pthread_mutex_destroy( &obj_delay_freed.obj_to_be_free_mutex); (void) pthread_mutex_destroy(&soft_slot.keystore_mutex); (void) pthread_mutex_destroy(&soft_slot.slot_mutex); (void) pthread_mutex_destroy(&soft_sessionlist_mutex); (void) soft_destroy_token_session(); (void) pthread_mutex_unlock(&soft_giant_mutex); return (CKR_CANT_LOCK); } ses_delay_freed.count = 0; ses_delay_freed.first = NULL; ses_delay_freed.last = NULL; if (rv != CKR_OK) { (void) pthread_mutex_destroy( &ses_delay_freed.ses_to_be_free_mutex); (void) pthread_mutex_destroy( &obj_delay_freed.obj_to_be_free_mutex); (void) pthread_mutex_destroy(&soft_slot.keystore_mutex); (void) pthread_mutex_destroy(&soft_slot.slot_mutex); (void) pthread_mutex_destroy(&soft_sessionlist_mutex); (void) soft_destroy_token_session(); (void) pthread_mutex_unlock(&soft_giant_mutex); return (CKR_FUNCTION_FAILED); } softtoken_pid = initialize_pid; softtoken_initialized = B_TRUE; (void) pthread_mutex_unlock(&soft_giant_mutex); return (CKR_OK); } /* * C_Finalize is a wrapper around finalize_common. The * soft_giant_mutex should be locked by C_Finalize(). */ CK_RV C_Finalize(CK_VOID_PTR pReserved) { CK_RV rv; (void) pthread_mutex_lock(&soft_giant_mutex); rv = finalize_common(B_FALSE, pReserved); (void) pthread_mutex_unlock(&soft_giant_mutex); return (rv); } /* * finalize_common() does the work for C_Finalize. soft_giant_mutex * must be held before calling this function. */ static CK_RV finalize_common(boolean_t force, CK_VOID_PTR pReserved) { CK_RV rv = CKR_OK; struct object *delay_free_obj, *tmpo; struct session *delay_free_ses, *tmps; if (!softtoken_initialized) { return (CKR_CRYPTOKI_NOT_INITIALIZED); } /* Check to see if pReseved is NULL */ if (pReserved != NULL) { return (CKR_ARGUMENTS_BAD); } (void) pthread_mutex_lock(&soft_sessionlist_mutex); /* * Set all_sessions_closing flag so any access to any * existing sessions will be rejected. */ all_sessions_closing = 1; (void) pthread_mutex_unlock(&soft_sessionlist_mutex); /* Delete all the sessions and release the allocated resources */ rv = soft_delete_all_sessions(force); (void) pthread_mutex_lock(&soft_sessionlist_mutex); /* Reset all_sessions_closing flag. */ all_sessions_closing = 0; (void) pthread_mutex_unlock(&soft_sessionlist_mutex); softtoken_initialized = B_FALSE; softtoken_pid = 0; /* * There used to be calls to cleanup libcryptoutil here. Given that * libcryptoutil can be linked and invoked independently of PKCS#11, * cleaning up libcryptoutil here makes no sense. Decoupling these * two also prevent deadlocks and other artificial dependencies. */ (void) pthread_mutex_destroy(&soft_object_mutex); /* Destroy the session list lock here */ (void) pthread_mutex_destroy(&soft_sessionlist_mutex); /* * Destroy token object related stuffs * 1. Clean up the token object list * 2. Destroy slot mutex * 3. Destroy mutex in token_session */ soft_delete_all_in_core_token_objects(ALL_TOKEN); (void) pthread_mutex_destroy(&soft_slot.slot_mutex); (void) pthread_mutex_destroy(&soft_slot.keystore_mutex); (void) soft_destroy_token_session(); /* * free all entries in the delay_freed list */ delay_free_obj = obj_delay_freed.first; while (delay_free_obj != NULL) { tmpo = delay_free_obj->next; free(delay_free_obj); delay_free_obj = tmpo; } soft_slot.keystore_load_status = KEYSTORE_UNINITIALIZED; (void) pthread_mutex_destroy(&obj_delay_freed.obj_to_be_free_mutex); delay_free_ses = ses_delay_freed.first; while (delay_free_ses != NULL) { tmps = delay_free_ses->next; free(delay_free_ses); delay_free_ses = tmps; } (void) pthread_mutex_destroy(&ses_delay_freed.ses_to_be_free_mutex); return (rv); } static void softtoken_init() { avl_create(&soft_session_tree, session_compare, sizeof (soft_session_t), offsetof(soft_session_t, node)); avl_create(&soft_object_tree, object_compare, sizeof (soft_object_t), offsetof(soft_object_t, node)); /* Children inherit parent's atfork handlers */ (void) pthread_atfork(softtoken_fork_prepare, softtoken_fork_after, softtoken_fork_after); } /* * softtoken_fini() function required to make sure complete cleanup * is done if softtoken is ever unloaded without a C_Finalize() call. */ static void softtoken_fini() { (void) pthread_mutex_lock(&soft_giant_mutex); /* if we're not initilized, do not attempt to finalize */ if (!softtoken_initialized) { (void) pthread_mutex_unlock(&soft_giant_mutex); return; } (void) finalize_common(B_TRUE, NULL_PTR); avl_destroy(&soft_object_tree); avl_destroy(&soft_session_tree); (void) pthread_mutex_unlock(&soft_giant_mutex); } CK_RV C_GetInfo(CK_INFO_PTR pInfo) { if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); if (pInfo == NULL) { return (CKR_ARGUMENTS_BAD); } /* Provide general information in the provided buffer */ pInfo->cryptokiVersion.major = CRYPTOKI_VERSION_MAJOR; pInfo->cryptokiVersion.minor = CRYPTOKI_VERSION_MINOR; (void) strncpy((char *)pInfo->manufacturerID, SOFT_MANUFACTURER_ID, 32); pInfo->flags = 0; (void) strncpy((char *)pInfo->libraryDescription, LIBRARY_DESCRIPTION, 32); pInfo->libraryVersion.major = LIBRARY_VERSION_MAJOR; pInfo->libraryVersion.minor = LIBRARY_VERSION_MINOR; return (CKR_OK); } CK_RV C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList) { if (ppFunctionList == NULL) { return (CKR_ARGUMENTS_BAD); } *ppFunctionList = &functionList; return (CKR_OK); } /* * PKCS#11 states that C_GetFunctionStatus should always return * CKR_FUNCTION_NOT_PARALLEL */ /*ARGSUSED*/ CK_RV C_GetFunctionStatus(CK_SESSION_HANDLE hSession) { return (CKR_FUNCTION_NOT_PARALLEL); } /* * PKCS#11 states that C_CancelFunction should always return * CKR_FUNCTION_NOT_PARALLEL */ /*ARGSUSED*/ CK_RV C_CancelFunction(CK_SESSION_HANDLE hSession) { return (CKR_FUNCTION_NOT_PARALLEL); } /* * Take out all mutexes before fork. * * Order: * 1. soft_giant_mutex * 2. soft_sessionlist_mutex * 3. soft_slot.slot_mutex * 4. soft_slot.keystore_mutex * 5. token_session mutexes via soft_acquire_all_session_mutexes() * 6. all soft_session_list mutexes via soft_acquire_all_session_mutexes() * 7. obj_delay_freed.obj_to_be_free_mutex; * 8. ses_delay_freed.ses_to_be_free_mutex */ void softtoken_fork_prepare() { (void) pthread_mutex_lock(&soft_giant_mutex); if (softtoken_initialized) { (void) pthread_mutex_lock(&soft_sessionlist_mutex); (void) pthread_mutex_lock(&soft_slot.slot_mutex); (void) pthread_mutex_lock(&soft_slot.keystore_mutex); soft_acquire_all_session_mutexes(&token_session); soft_acquire_all_session_mutexes(soft_session_list); VERIFY0(pthread_mutex_lock(&soft_object_mutex)); (void) pthread_mutex_lock( &obj_delay_freed.obj_to_be_free_mutex); (void) pthread_mutex_lock( &ses_delay_freed.ses_to_be_free_mutex); } } /* * Release in opposite order to softtoken_fork_prepare(). * Function is used for parent and child. */ void softtoken_fork_after() { if (softtoken_initialized) { (void) pthread_mutex_unlock( &ses_delay_freed.ses_to_be_free_mutex); (void) pthread_mutex_unlock( &obj_delay_freed.obj_to_be_free_mutex); VERIFY0(pthread_mutex_unlock(&soft_object_mutex)); soft_release_all_session_mutexes(soft_session_list); soft_release_all_session_mutexes(&token_session); (void) pthread_mutex_unlock(&soft_slot.keystore_mutex); (void) pthread_mutex_unlock(&soft_slot.slot_mutex); (void) pthread_mutex_unlock(&soft_sessionlist_mutex); } (void) pthread_mutex_unlock(&soft_giant_mutex); } static int session_compare(const void *a, const void *b) { const soft_session_t *l = a; const soft_session_t *r = b; if (l->handle < r->handle) return (-1); if (l->handle > r->handle) return (1); return (0); } static int object_compare(const void *a, const void *b) { const soft_object_t *l = a; const soft_object_t *r = b; if (l->handle < r->handle) return (-1); if (l->handle > r->handle) return (1); return (0); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SOFTGLOBAL_H #define _SOFTGLOBAL_H #ifdef __cplusplus extern "C" { #endif #include #include /* * The following global variables are defined in softGeneral.c */ extern boolean_t softtoken_initialized; extern pthread_mutex_t soft_giant_mutex; extern struct slot soft_slot; extern struct obj_to_be_freed_list obj_delay_freed; extern struct ses_to_be_freed_list ses_delay_freed; #define SOFTTOKEN_SLOTID 1 /* CK_INFO: Information about cryptoki */ #define CRYPTOKI_VERSION_MAJOR 2 #define CRYPTOKI_VERSION_MINOR 40 #define LIBRARY_DESCRIPTION "Sun Crypto Softtoken " #define LIBRARY_VERSION_MAJOR 1 #define LIBRARY_VERSION_MINOR 1 /* CK_SLOT_INFO: Information about our slot */ #define HARDWARE_VERSION_MAJOR 0 #define HARDWARE_VERSION_MINOR 0 #define FIRMWARE_VERSION_MAJOR 0 #define FIRMWARE_VERSION_MINOR 0 /* CK_TOKEN_INFO: More information about token */ #define TOKEN_MODEL "1.0 " #define MAX_PIN_LEN 256 #define MIN_PIN_LEN 1 #define SOFT_TOKEN_FLAGS CKF_RNG|\ CKF_USER_PIN_INITIALIZED|\ CKF_LOGIN_REQUIRED|\ CKF_RESTORE_KEY_NOT_NEEDED|\ CKF_DUAL_CRYPTO_OPERATIONS|\ CKF_TOKEN_INITIALIZED #ifdef __cplusplus } #endif #endif /* _SOFTGLOBAL_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include "softGlobal.h" #include "softSession.h" #include "softKeys.h" #include "softOps.h" CK_RV C_GenerateKey(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phKey) { CK_RV rv; soft_session_t *session_p; boolean_t lock_held = B_FALSE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if ((pMechanism == NULL) || (phKey == NULL)) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } if ((pTemplate == NULL) && (ulCount != 0)) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } rv = soft_genkey(session_p, pMechanism, pTemplate, ulCount, phKey); clean_exit: SES_REFRELE(session_p, lock_held); return (rv); } CK_RV C_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) { CK_RV rv; soft_session_t *session_p; boolean_t lock_held = B_FALSE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if ((pMechanism == NULL) || (phPublicKey == NULL) || (phPrivateKey == NULL)) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } if ((pPublicKeyTemplate == NULL) || (ulPublicKeyAttributeCount == 0)) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } if ((pPrivateKeyTemplate == NULL) && (ulPrivateKeyAttributeCount != 0)) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } rv = soft_genkey_pair(session_p, pMechanism, pPublicKeyTemplate, ulPublicKeyAttributeCount, pPrivateKeyTemplate, ulPrivateKeyAttributeCount, phPublicKey, phPrivateKey); clean_exit: SES_REFRELE(session_p, lock_held); return (rv); } CK_RV C_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) { CK_RV rv; soft_session_t *session_p; soft_object_t *wrappingkey_p; soft_object_t *hkey_p; boolean_t lock_held = B_FALSE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if (pMechanism == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } if (pulWrappedKeyLen == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } /* Obtain the wrapping key object pointer. */ HANDLE2OBJECT(hWrappingKey, wrappingkey_p, rv); if (rv != CKR_OK) { rv = CKR_WRAPPING_KEY_HANDLE_INVALID; goto clean_exit; } /* Obtain the to-be-wrapped key object pointer. */ HANDLE2OBJECT(hKey, hkey_p, rv); if (rv != CKR_OK) goto clean_exit1; /* Check if given wrapping key may be used for wrapping. */ if (!(wrappingkey_p->bool_attr_mask & WRAP_BOOL_ON)) { rv = CKR_WRAPPING_KEY_TYPE_INCONSISTENT; goto clean_exit2; } /* Check if given wrapping key may be used for encryption. */ if (!(wrappingkey_p->bool_attr_mask & ENCRYPT_BOOL_ON)) { rv = CKR_KEY_FUNCTION_NOT_PERMITTED; goto clean_exit2; } /* * Check to see if key to be wrapped is extractable. * Note: this should always be true for softtoken keys. */ if (!(hkey_p->bool_attr_mask & EXTRACTABLE_BOOL_ON)) { rv = CKR_KEY_UNEXTRACTABLE; goto clean_exit2; } (void) pthread_mutex_lock(&session_p->session_mutex); lock_held = B_TRUE; /* * Wrapping key objects requires calling encrypt operations. * Check to see if encrypt operation is already active. */ if (session_p->encrypt.flags & CRYPTO_OPERATION_ACTIVE) { /* free the memory to avoid memory leak */ soft_crypt_cleanup(session_p, B_TRUE, lock_held); } /* This active flag will remain ON while wrapping the key. */ session_p->encrypt.flags = CRYPTO_OPERATION_ACTIVE; (void) pthread_mutex_unlock(&session_p->session_mutex); lock_held = B_FALSE; rv = soft_wrapkey(session_p, pMechanism, wrappingkey_p, hkey_p, pWrappedKey, pulWrappedKeyLen); (void) pthread_mutex_lock(&session_p->session_mutex); lock_held = B_TRUE; session_p->encrypt.flags = 0; if ((rv == CKR_OK && pWrappedKey == NULL) || rv == CKR_BUFFER_TOO_SMALL) soft_crypt_cleanup(session_p, B_TRUE, lock_held); clean_exit2: OBJ_REFRELE(hkey_p); clean_exit1: OBJ_REFRELE(wrappingkey_p); clean_exit: SES_REFRELE(session_p, lock_held); return (rv); } CK_RV C_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) { CK_RV rv; soft_session_t *session_p; soft_object_t *unwrappingkey_p; boolean_t lock_held = B_FALSE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if (pMechanism == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } if ((pTemplate == NULL) || (ulAttributeCount == 0)) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } if ((pWrappedKey == NULL) || (ulWrappedKeyLen == 0)) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } if (phKey == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } /* Obtain the unwrapping key object pointer. */ HANDLE2OBJECT(hUnwrappingKey, unwrappingkey_p, rv); if (rv != CKR_OK) { rv = CKR_UNWRAPPING_KEY_HANDLE_INVALID; goto clean_exit; } /* Check if given unwrapping key may be used for unwrapping. */ if (!(unwrappingkey_p->bool_attr_mask & UNWRAP_BOOL_ON)) { rv = CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT; goto clean_exit1; } /* Check if given unwrapping key may be used to decrypt. */ if (!(unwrappingkey_p->bool_attr_mask & DECRYPT_BOOL_ON)) { rv = CKR_KEY_FUNCTION_NOT_PERMITTED; goto clean_exit1; } (void) pthread_mutex_lock(&session_p->session_mutex); lock_held = B_TRUE; /* * Unwrapping key objects requires calling decrypt operations. * Check to see if decrypt operation is already active. */ if (session_p->decrypt.flags & CRYPTO_OPERATION_ACTIVE) { /* free the memory to avoid memory leak */ soft_crypt_cleanup(session_p, B_FALSE, lock_held); } /* * This active flag will remain ON until application * is done unwrapping the key. */ session_p->decrypt.flags = CRYPTO_OPERATION_ACTIVE; (void) pthread_mutex_unlock(&session_p->session_mutex); lock_held = B_FALSE; rv = soft_unwrapkey(session_p, pMechanism, unwrappingkey_p, pWrappedKey, ulWrappedKeyLen, pTemplate, ulAttributeCount, phKey); (void) pthread_mutex_lock(&session_p->session_mutex); if ((rv == CKR_OK && pWrappedKey == NULL) || rv == CKR_BUFFER_TOO_SMALL) soft_crypt_cleanup(session_p, B_TRUE, lock_held); session_p->decrypt.flags = 0; lock_held = B_TRUE; clean_exit1: OBJ_REFRELE(unwrappingkey_p); clean_exit: SES_REFRELE(session_p, lock_held); return (rv); } CK_RV C_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) { CK_RV rv; soft_session_t *session_p; soft_object_t *basekey_p; boolean_t lock_held = B_FALSE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if (pMechanism == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } if (((pTemplate != NULL) && (ulAttributeCount == 0)) || ((pTemplate == NULL) && (ulAttributeCount != 0))) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } /* Obtain the private key object pointer. */ HANDLE2OBJECT(hBaseKey, basekey_p, rv); if (rv != CKR_OK) goto clean_exit; /* Check to see if key object allows for derivation. */ if (!(basekey_p->bool_attr_mask & DERIVE_BOOL_ON)) { rv = CKR_KEY_FUNCTION_NOT_PERMITTED; goto clean_exit1; } rv = soft_derivekey(session_p, pMechanism, basekey_p, pTemplate, ulAttributeCount, phKey); clean_exit1: OBJ_REFRELE(basekey_p); clean_exit: SES_REFRELE(session_p, lock_held); return (rv); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2020 Joyent, Inc. */ #ifndef _SOFTKEYS_H #define _SOFTKEYS_H #ifdef __cplusplus extern "C" { #endif #include #include "softObject.h" #include "softSession.h" #define KEYGEN_RETRY 3 /* * Function Prototypes. */ CK_RV soft_genkey(soft_session_t *, CK_MECHANISM_PTR, CK_ATTRIBUTE_PTR, CK_ULONG, CK_OBJECT_HANDLE_PTR); CK_RV soft_genkey_pair(soft_session_t *, CK_MECHANISM_PTR, CK_ATTRIBUTE_PTR, CK_ULONG, CK_ATTRIBUTE_PTR, CK_ULONG, CK_OBJECT_HANDLE_PTR, CK_OBJECT_HANDLE_PTR); CK_RV soft_derivekey(soft_session_t *, CK_MECHANISM_PTR, soft_object_t *, CK_ATTRIBUTE_PTR, CK_ULONG, CK_OBJECT_HANDLE_PTR); void soft_derive_enforce_flags(soft_object_t *, soft_object_t *); CK_RV soft_gen_keyobject(CK_ATTRIBUTE_PTR, CK_ULONG, soft_object_t **, soft_session_t *, CK_OBJECT_CLASS, CK_KEY_TYPE, CK_ULONG, CK_ULONG, boolean_t); CK_RV soft_generate_pkcs5_pbkdf2_key(soft_session_t *, CK_MECHANISM_PTR, soft_object_t *); CK_RV soft_wrapkey(soft_session_t *, CK_MECHANISM_PTR, soft_object_t *, soft_object_t *, CK_BYTE_PTR, CK_ULONG_PTR); CK_RV soft_unwrapkey(soft_session_t *, CK_MECHANISM_PTR, soft_object_t *, CK_BYTE_PTR, CK_ULONG, CK_ATTRIBUTE_PTR, CK_ULONG, CK_OBJECT_HANDLE_PTR); #ifdef __cplusplus } #endif #endif /* _SOFTKEYS_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2020 Joyent, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "softGlobal.h" #include "softSession.h" #include "softObject.h" #include "softDSA.h" #include "softRSA.h" #include "softDH.h" #include "softEC.h" #include "softMAC.h" #include "softOps.h" #include "softKeys.h" #include "softKeystore.h" #include "softSSL.h" #include "softASN1.h" #define local_min(a, b) ((a) < (b) ? (a) : (b)) static CK_RV soft_pkcs12_pbe(soft_session_t *, CK_MECHANISM_PTR, soft_object_t *); /* * Create a temporary key object struct by filling up its template attributes. */ CK_RV soft_gen_keyobject(CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, soft_object_t **objp, soft_session_t *sp, CK_OBJECT_CLASS class, CK_KEY_TYPE key_type, CK_ULONG keylen, CK_ULONG mode, boolean_t internal) { CK_RV rv; soft_object_t *new_objp = NULL; new_objp = calloc(1, sizeof (soft_object_t)); if (new_objp == NULL) { return (CKR_HOST_MEMORY); } new_objp->extra_attrlistp = NULL; /* * Validate attribute template and fill in the attributes * in the soft_object_t. */ rv = soft_build_key(pTemplate, ulCount, new_objp, class, key_type, keylen, mode); if (rv != CKR_OK) { goto fail_cleanup1; } /* * If generating a key is an internal request (i.e. not a C_XXX * API request), then skip the following checks. */ if (!internal) { rv = soft_pin_expired_check(new_objp); if (rv != CKR_OK) { goto fail_cleanup2; } rv = soft_object_write_access_check(sp, new_objp); if (rv != CKR_OK) { goto fail_cleanup2; } } /* Initialize the rest of stuffs in soft_object_t. */ (void) pthread_mutex_init(&new_objp->object_mutex, NULL); new_objp->magic_marker = SOFTTOKEN_OBJECT_MAGIC; /* Write the new token object to the keystore */ if (IS_TOKEN_OBJECT(new_objp)) { new_objp->version = 1; new_objp->session_handle = CK_INVALID_HANDLE; soft_add_token_object_to_slot(new_objp); set_objecthandle(new_objp); *objp = new_objp; return (CKR_OK); } new_objp->session_handle = sp->handle; /* Add the new object to the session's object list. */ soft_add_object_to_session(new_objp, sp); set_objecthandle(new_objp); *objp = new_objp; return (CKR_OK); fail_cleanup2: /* * When any error occurs after soft_build_key(), we will need to * clean up the memory allocated by the soft_build_key(). */ soft_cleanup_object(new_objp); fail_cleanup1: if (new_objp) { /* * The storage allocated inside of this object should have * been cleaned up by the soft_build_key() if it failed. * Therefore, we can safely free the object. */ free(new_objp); } return (rv); } CK_RV soft_genkey(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phKey) { CK_RV rv = CKR_OK; soft_object_t *secret_key; CK_KEY_TYPE key_type; CK_ULONG keylen = 0; CK_ULONG i; int des_strength = 0; int retry = 0; int keyfound = 0; boolean_t is_ssl_mech = B_FALSE; switch (pMechanism->mechanism) { case CKM_DES_KEY_GEN: key_type = CKK_DES; break; case CKM_DES2_KEY_GEN: key_type = CKK_DES2; break; case CKM_DES3_KEY_GEN: key_type = CKK_DES3; break; case CKM_AES_KEY_GEN: key_type = CKK_AES; break; case CKM_BLOWFISH_KEY_GEN: key_type = CKK_BLOWFISH; break; case CKM_RC4_KEY_GEN: key_type = CKK_RC4; break; case CKM_SSL3_PRE_MASTER_KEY_GEN: case CKM_TLS_PRE_MASTER_KEY_GEN: if (pMechanism->pParameter == NULL || pMechanism->ulParameterLen != sizeof (CK_VERSION)) return (CKR_TEMPLATE_INCOMPLETE); is_ssl_mech = B_TRUE; key_type = CKK_GENERIC_SECRET; keylen = 48; break; case CKM_PKCS5_PBKD2: keyfound = 0; for (i = 0; i < ulCount && !keyfound; i++) { if (pTemplate[i].type == CKA_KEY_TYPE && pTemplate[i].pValue != NULL) { key_type = *((CK_KEY_TYPE*)pTemplate[i].pValue); keyfound = 1; } } if (!keyfound) return (CKR_TEMPLATE_INCOMPLETE); /* * Make sure that parameters were given for this * mechanism. */ if (pMechanism->pParameter == NULL || pMechanism->ulParameterLen != sizeof (CK_PKCS5_PBKD2_PARAMS)) return (CKR_TEMPLATE_INCOMPLETE); break; case CKM_PBE_SHA1_RC4_128: keyfound = 0; for (i = 0; i < ulCount; i++) { if (pTemplate[i].type == CKA_KEY_TYPE && pTemplate[i].pValue != NULL) { key_type = *((CK_KEY_TYPE*)pTemplate[i].pValue); keyfound = 1; } if (pTemplate[i].type == CKA_VALUE_LEN && pTemplate[i].pValue != NULL) { keylen = *((CK_ULONG*)pTemplate[i].pValue); } } /* If a keytype was specified, it had better be CKK_RC4 */ if (keyfound && key_type != CKK_RC4) return (CKR_TEMPLATE_INCONSISTENT); else if (!keyfound) key_type = CKK_RC4; /* If key length was specified, it better be 16 bytes */ if (keylen != 0 && keylen != 16) return (CKR_TEMPLATE_INCONSISTENT); /* * Make sure that parameters were given for this * mechanism. */ if (pMechanism->pParameter == NULL || pMechanism->ulParameterLen != sizeof (CK_PBE_PARAMS)) return (CKR_TEMPLATE_INCOMPLETE); break; default: return (CKR_MECHANISM_INVALID); } /* Create a new object for secret key. */ rv = soft_gen_keyobject(pTemplate, ulCount, &secret_key, session_p, CKO_SECRET_KEY, key_type, keylen, SOFT_GEN_KEY, B_FALSE); if (rv != CKR_OK) { return (rv); } switch (pMechanism->mechanism) { case CKM_DES_KEY_GEN: /* * Set up key value len since it is not a required * attribute for C_GenerateKey. */ keylen = OBJ_SEC_VALUE_LEN(secret_key) = DES_KEYSIZE; des_strength = DES; break; case CKM_DES2_KEY_GEN: /* * Set up key value len since it is not a required * attribute for C_GenerateKey. */ keylen = OBJ_SEC_VALUE_LEN(secret_key) = DES2_KEYSIZE; des_strength = DES2; break; case CKM_DES3_KEY_GEN: /* * Set up key value len since it is not a required * attribute for C_GenerateKey. */ keylen = OBJ_SEC_VALUE_LEN(secret_key) = DES3_KEYSIZE; des_strength = DES3; break; case CKM_SSL3_PRE_MASTER_KEY_GEN: case CKM_TLS_PRE_MASTER_KEY_GEN: secret_key->bool_attr_mask |= DERIVE_BOOL_ON; /* FALLTHRU */ case CKM_AES_KEY_GEN: case CKM_BLOWFISH_KEY_GEN: case CKM_PBE_SHA1_RC4_128: case CKM_RC4_KEY_GEN: keylen = OBJ_SEC_VALUE_LEN(secret_key); break; case CKM_PKCS5_PBKD2: /* * PKCS#11 does not allow one to specify key * sizes for DES and 3DES, so we must set it here * when using PBKD2 algorithms. */ if (key_type == CKK_DES) { OBJ_SEC_VALUE_LEN(secret_key) = DES_KEYSIZE; des_strength = DES; } else if (key_type == CKK_DES3) { OBJ_SEC_VALUE_LEN(secret_key) = DES3_KEYSIZE; des_strength = DES3; } keylen = OBJ_SEC_VALUE_LEN(secret_key); break; } if ((OBJ_SEC_VALUE(secret_key) = malloc(keylen)) == NULL) { if (IS_TOKEN_OBJECT(secret_key)) soft_delete_token_object(secret_key, B_FALSE, B_FALSE); else soft_delete_object(session_p, secret_key, B_FALSE, B_FALSE); return (CKR_HOST_MEMORY); } switch (pMechanism->mechanism) { case CKM_PBE_SHA1_RC4_128: /* * Use the PBE algorithm described in PKCS#11 section * 12.33 to derive the key. */ rv = soft_pkcs12_pbe(session_p, pMechanism, secret_key); break; case CKM_PKCS5_PBKD2: /* Generate keys using PKCS#5 PBKD2 algorithm */ rv = soft_generate_pkcs5_pbkdf2_key(session_p, pMechanism, secret_key); if (rv == CKR_OK && des_strength > 0) { /* Perform weak key checking for DES and DES3. */ if (des_keycheck(OBJ_SEC_VALUE(secret_key), des_strength, OBJ_SEC_VALUE(secret_key)) == B_FALSE) { /* We got a weak secret key. */ rv = CKR_FUNCTION_FAILED; } } break; default: do { /* If this fails, bail out */ rv = CKR_OK; if (pkcs11_get_urandom( OBJ_SEC_VALUE(secret_key), keylen) < 0) { rv = CKR_DEVICE_ERROR; break; } /* Perform weak key checking for DES and DES3. */ if (des_strength > 0) { rv = CKR_OK; if (des_keycheck(OBJ_SEC_VALUE(secret_key), des_strength, OBJ_SEC_VALUE(secret_key)) == B_FALSE) { /* We got a weak key, retry! */ retry++; rv = CKR_FUNCTION_FAILED; } } /* * Copy over the SSL client version For SSL mechs * The first two bytes of the key is the version */ if (is_ssl_mech) bcopy(pMechanism->pParameter, OBJ_SEC_VALUE(secret_key), sizeof (CK_VERSION)); } while (rv != CKR_OK && retry < KEYGEN_RETRY); if (retry == KEYGEN_RETRY) rv = CKR_FUNCTION_FAILED; break; } if (rv != CKR_OK) if (IS_TOKEN_OBJECT(secret_key)) soft_delete_token_object(secret_key, B_FALSE, B_FALSE); else soft_delete_object(session_p, secret_key, B_FALSE, B_FALSE); if (IS_TOKEN_OBJECT(secret_key)) { /* * All the info has been filled, so we can write to * keystore now. */ rv = soft_put_object_to_keystore(secret_key); if (rv != CKR_OK) soft_delete_token_object(secret_key, B_FALSE, B_FALSE); } *phKey = secret_key->handle; return (rv); } CK_RV soft_genkey_pair(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism, CK_ATTRIBUTE_PTR pPublicKeyTemplate, CK_ULONG ulPublicAttrCount, CK_ATTRIBUTE_PTR pPrivateKeyTemplate, CK_ULONG ulPrivateAttrCount, CK_OBJECT_HANDLE_PTR phPublicKey, CK_OBJECT_HANDLE_PTR phPrivateKey) { CK_RV rv; soft_object_t *public_key, *private_key; CK_KEY_TYPE key_type; switch (pMechanism->mechanism) { case CKM_RSA_PKCS_KEY_PAIR_GEN: key_type = CKK_RSA; break; case CKM_DSA_KEY_PAIR_GEN: key_type = CKK_DSA; break; case CKM_DH_PKCS_KEY_PAIR_GEN: key_type = CKK_DH; break; case CKM_EC_KEY_PAIR_GEN: key_type = CKK_EC; break; default: return (CKR_MECHANISM_INVALID); } /* Create a new object for public key. */ rv = soft_gen_keyobject(pPublicKeyTemplate, ulPublicAttrCount, &public_key, session_p, CKO_PUBLIC_KEY, key_type, 0, SOFT_GEN_KEY, B_FALSE); if (rv != CKR_OK) { return (rv); } /* Create a new object for private key. */ rv = soft_gen_keyobject(pPrivateKeyTemplate, ulPrivateAttrCount, &private_key, session_p, CKO_PRIVATE_KEY, key_type, 0, SOFT_GEN_KEY, B_FALSE); if (rv != CKR_OK) { /* * Both public key and private key must be successful. */ if (IS_TOKEN_OBJECT(public_key)) soft_delete_token_object(public_key, B_FALSE, B_FALSE); else soft_delete_object(session_p, public_key, B_FALSE, B_FALSE); return (rv); } /* * At this point, both public key and private key objects * are settled with the application specified attributes. * We are ready to generate the rest of key attributes based * on the existing attributes. */ switch (key_type) { case CKK_RSA: rv = soft_rsa_genkey_pair(public_key, private_key); break; case CKK_DSA: rv = soft_dsa_genkey_pair(public_key, private_key); break; case CKK_DH: rv = soft_dh_genkey_pair(public_key, private_key); private_key->bool_attr_mask |= DERIVE_BOOL_ON; break; case CKK_EC: rv = soft_ec_genkey_pair(public_key, private_key); private_key->bool_attr_mask |= DERIVE_BOOL_ON; break; } if (rv != CKR_OK) { if (IS_TOKEN_OBJECT(public_key)) { soft_delete_token_object(public_key, B_FALSE, B_FALSE); soft_delete_token_object(private_key, B_FALSE, B_FALSE); } else { soft_delete_object(session_p, public_key, B_FALSE, B_FALSE); soft_delete_object(session_p, private_key, B_FALSE, B_FALSE); } return (rv); } if (IS_TOKEN_OBJECT(public_key)) { /* * All the info has been filled, so we can write to * keystore now. */ rv = soft_put_object_to_keystore(public_key); if (rv != CKR_OK) { soft_delete_token_object(public_key, B_FALSE, B_FALSE); soft_delete_token_object(private_key, B_FALSE, B_FALSE); return (rv); } } if (IS_TOKEN_OBJECT(private_key)) { rv = soft_put_object_to_keystore(private_key); if (rv != CKR_OK) { /* * We also need to delete the public token object * from keystore. */ soft_delete_token_object(public_key, B_TRUE, B_FALSE); soft_delete_token_object(private_key, B_FALSE, B_FALSE); } } *phPublicKey = public_key->handle; *phPrivateKey = private_key->handle; return (rv); } CK_RV soft_key_derive_check_length(soft_object_t *secret_key, CK_ULONG max_keylen) { switch (secret_key->key_type) { case CKK_GENERIC_SECRET: if (OBJ_SEC_VALUE_LEN(secret_key) == 0) { OBJ_SEC_VALUE_LEN(secret_key) = max_keylen; return (CKR_OK); } else if (OBJ_SEC_VALUE_LEN(secret_key) > max_keylen) { return (CKR_ATTRIBUTE_VALUE_INVALID); } break; case CKK_RC4: case CKK_AES: case CKK_BLOWFISH: if ((OBJ_SEC_VALUE_LEN(secret_key) == 0) || (OBJ_SEC_VALUE_LEN(secret_key) > max_keylen)) { /* RC4 and AES has variable key length */ return (CKR_ATTRIBUTE_VALUE_INVALID); } break; case CKK_DES: if (OBJ_SEC_VALUE_LEN(secret_key) == 0) { /* DES has a well-defined length */ OBJ_SEC_VALUE_LEN(secret_key) = DES_KEYSIZE; return (CKR_OK); } else if (OBJ_SEC_VALUE_LEN(secret_key) != DES_KEYSIZE) { return (CKR_ATTRIBUTE_VALUE_INVALID); } break; case CKK_DES2: if (OBJ_SEC_VALUE_LEN(secret_key) == 0) { /* DES2 has a well-defined length */ OBJ_SEC_VALUE_LEN(secret_key) = DES2_KEYSIZE; return (CKR_OK); } else if (OBJ_SEC_VALUE_LEN(secret_key) != DES2_KEYSIZE) { return (CKR_ATTRIBUTE_VALUE_INVALID); } break; default: return (CKR_MECHANISM_INVALID); } return (CKR_OK); } /* * PKCS#11 (12.33) says that v = 512 bits (64 bytes) for SHA1 * PBE methods. */ #define PKCS12_BUFFER_SIZE 64 /* * PKCS#12 defines 3 different ID bytes to be used for * deriving keys for different operations. */ #define PBE_ID_ENCRYPT 1 #define PBE_ID_IV 2 #define PBE_ID_MAC 3 #define PBE_CEIL(a, b) (((a)/(b)) + (((a)%(b)) > 0)) static CK_RV soft_pkcs12_pbe(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism, soft_object_t *derived_key) { CK_RV rv = CKR_OK; CK_PBE_PARAMS *params = pMechanism->pParameter; CK_ULONG c, i, j, k; CK_ULONG hashSize; CK_ULONG buffSize; /* * Terse variable names are used to make following * the PKCS#12 spec easier. */ CK_BYTE *A = NULL; CK_BYTE *Ai = NULL; CK_BYTE *B = NULL; CK_BYTE *D = NULL; CK_BYTE *I = NULL, *S, *P; CK_BYTE *keybuf = NULL; CK_ULONG Alen, Ilen, Slen, Plen, AiLen, Blen, Dlen; CK_ULONG keysize = OBJ_SEC_VALUE_LEN(derived_key); CK_MECHANISM digest_mech; /* U = hash function output bits */ if (pMechanism->mechanism == CKM_PBE_SHA1_RC4_128) { hashSize = SHA1_HASH_SIZE; buffSize = PKCS12_BUFFER_SIZE; digest_mech.mechanism = CKM_SHA_1; digest_mech.pParameter = NULL; digest_mech.ulParameterLen = 0; } else { /* we only support 1 PBE mech for now */ return (CKR_MECHANISM_INVALID); } keybuf = OBJ_SEC_VALUE(derived_key); Blen = Dlen = buffSize; D = (CK_BYTE *)malloc(Dlen); if (D == NULL) { rv = CKR_HOST_MEMORY; goto cleanup; } B = (CK_BYTE *)malloc(Blen); if (B == NULL) { rv = CKR_HOST_MEMORY; goto cleanup; } /* * Initialize some values and create some buffers * that we need later. * * Slen = buffSize * CEIL(SaltLength/buffSize) */ Slen = buffSize * PBE_CEIL(params->ulSaltLen, buffSize); /* * Plen = buffSize * CEIL(PasswordLength/buffSize) */ Plen = buffSize * PBE_CEIL(params->ulPasswordLen, buffSize); /* * From step 4: I = S + P, so: Ilen = Slen + Plen */ Ilen = Slen + Plen; I = (CK_BYTE *)malloc(Ilen); if (I == NULL) { rv = CKR_HOST_MEMORY; goto cleanup; } S = I; P = I + Slen; /* * Step 1. * We are only interested in deriving keys for encrypt/decrypt * for now, so construct the "D"iversifier accordingly. */ (void) memset(D, PBE_ID_ENCRYPT, Dlen); /* * Step 2. * Concatenate copies of the salt together to make S. */ for (i = 0; i < Slen; i += params->ulSaltLen) { (void) memcpy(S+i, params->pSalt, ((Slen - i) > params->ulSaltLen ? params->ulSaltLen : (Slen - i))); } /* * Step 3. * Concatenate copies of the password together to make * a string P. */ for (i = 0; i < Plen; i += params->ulPasswordLen) { (void) memcpy(P+i, params->pPassword, ((Plen - i) > params->ulPasswordLen ? params->ulPasswordLen : (Plen - i))); } /* * Step 4. * I = S+P - this is now done because S and P are * pointers into I. * * Step 5. * c= CEIL[n/u] * where n = pseudorandom bits of output desired. */ c = PBE_CEIL(keysize, hashSize); /* * Step 6. */ Alen = c * hashSize; A = (CK_BYTE *)malloc(Alen); if (A == NULL) { rv = CKR_HOST_MEMORY; goto cleanup; } AiLen = hashSize; Ai = (CK_BYTE *)malloc(AiLen); if (Ai == NULL) { rv = CKR_HOST_MEMORY; goto cleanup; } /* * Step 6a. * Ai = Hr(D+I) */ for (i = 0; i < c; i++) { (void) pthread_mutex_lock(&session_p->session_mutex); if (session_p->sign.flags & CRYPTO_OPERATION_ACTIVE) { (void) pthread_mutex_unlock(&session_p->session_mutex); rv = CKR_OPERATION_ACTIVE; goto cleanup; } session_p->sign.flags |= CRYPTO_OPERATION_ACTIVE; (void) pthread_mutex_unlock(&session_p->session_mutex); for (j = 0; j < params->ulIteration; j++) { rv = soft_digest_init(session_p, &digest_mech); if (rv != CKR_OK) goto digest_done; if (j == 0) { rv = soft_digest_update(session_p, D, Dlen); if (rv != CKR_OK) goto digest_done; rv = soft_digest_update(session_p, I, Ilen); } else { rv = soft_digest_update(session_p, Ai, AiLen); } if (rv != CKR_OK) goto digest_done; rv = soft_digest_final(session_p, Ai, &AiLen); if (rv != CKR_OK) goto digest_done; } digest_done: (void) pthread_mutex_lock(&session_p->session_mutex); session_p->sign.flags &= ~CRYPTO_OPERATION_ACTIVE; (void) pthread_mutex_unlock(&session_p->session_mutex); if (rv != CKR_OK) goto cleanup; /* * Step 6b. * Concatenate Ai to make B */ for (j = 0; j < Blen; j += hashSize) { (void) memcpy(B+j, Ai, ((Blen - j > hashSize) ? hashSize : Blen - j)); } /* * Step 6c. */ k = Ilen / Blen; for (j = 0; j < k; j++) { uchar_t idx; CK_ULONG m, q = 1, cbit = 0; for (m = Blen - 1; m >= (CK_ULONG)0; m--, q = 0) { idx = m + j*Blen; q += (CK_ULONG)I[idx] + (CK_ULONG)B[m]; q += cbit; I[idx] = (CK_BYTE)(q & 0xff); cbit = (q > 0xff); } } /* * Step 7. * A += Ai */ (void) memcpy(A + i*hashSize, Ai, AiLen); } /* * Step 8. * The final output of this process is the A buffer */ (void) memcpy(keybuf, A, keysize); cleanup: freezero(A, Alen); freezero(Ai, AiLen); freezero(B, Blen); freezero(D, Dlen); freezero(I, Ilen); return (rv); } CK_RV soft_derivekey(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism, soft_object_t *basekey_p, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey) { CK_RV rv = CKR_OK; soft_object_t *secret_key; CK_MECHANISM digest_mech; CK_BYTE hash[SHA512_DIGEST_LENGTH]; /* space enough for all mechs */ CK_ULONG hash_len = SHA512_DIGEST_LENGTH; CK_ULONG secret_key_len; CK_ULONG hash_size; switch (pMechanism->mechanism) { case CKM_DH_PKCS_DERIVE: if (phKey == NULL_PTR) return (CKR_ARGUMENTS_BAD); /* * Create a new object for secret key. The key type should * be provided in the template. */ rv = soft_gen_keyobject(pTemplate, ulAttributeCount, &secret_key, session_p, CKO_SECRET_KEY, (CK_KEY_TYPE)~0UL, 0, SOFT_DERIVE_KEY_DH, B_FALSE); if (rv != CKR_OK) { return (rv); } rv = soft_dh_key_derive(basekey_p, secret_key, (CK_BYTE *)pMechanism->pParameter, pMechanism->ulParameterLen); if (rv != CKR_OK) { if (IS_TOKEN_OBJECT(secret_key)) soft_delete_token_object(secret_key, B_FALSE, B_FALSE); else soft_delete_object(session_p, secret_key, B_FALSE, B_FALSE); return (rv); } break; case CKM_ECDH1_DERIVE: if (phKey == NULL_PTR) return (CKR_ARGUMENTS_BAD); /* * Create a new object for secret key. The key type should * be provided in the template. */ rv = soft_gen_keyobject(pTemplate, ulAttributeCount, &secret_key, session_p, CKO_SECRET_KEY, (CK_KEY_TYPE)~0UL, 0, SOFT_DERIVE_KEY_DH, B_FALSE); if (rv != CKR_OK) { return (rv); } rv = soft_ec_key_derive(basekey_p, secret_key, (CK_BYTE *)pMechanism->pParameter, pMechanism->ulParameterLen); if (rv != CKR_OK) { if (IS_TOKEN_OBJECT(secret_key)) soft_delete_token_object(secret_key, B_FALSE, B_FALSE); else soft_delete_object(session_p, secret_key, B_FALSE, B_FALSE); return (rv); } break; case CKM_SHA1_KEY_DERIVATION: hash_size = SHA1_HASH_SIZE; digest_mech.mechanism = CKM_SHA_1; goto common; case CKM_MD5_KEY_DERIVATION: hash_size = MD5_HASH_SIZE; digest_mech.mechanism = CKM_MD5; goto common; case CKM_SHA256_KEY_DERIVATION: hash_size = SHA256_DIGEST_LENGTH; digest_mech.mechanism = CKM_SHA256; goto common; case CKM_SHA384_KEY_DERIVATION: hash_size = SHA384_DIGEST_LENGTH; digest_mech.mechanism = CKM_SHA384; goto common; case CKM_SHA512_KEY_DERIVATION: hash_size = SHA512_DIGEST_LENGTH; digest_mech.mechanism = CKM_SHA512; goto common; case CKM_SHA512_224_KEY_DERIVATION: hash_size = SHA512_224_DIGEST_LENGTH; digest_mech.mechanism = CKM_SHA512_224; goto common; case CKM_SHA512_256_KEY_DERIVATION: hash_size = SHA512_256_DIGEST_LENGTH; digest_mech.mechanism = CKM_SHA512_256; goto common; common: if (phKey == NULL_PTR) return (CKR_ARGUMENTS_BAD); /* * Create a new object for secret key. The key type is optional * to be provided in the template. If it is not specified in * the template, the default is CKK_GENERIC_SECRET. */ rv = soft_gen_keyobject(pTemplate, ulAttributeCount, &secret_key, session_p, CKO_SECRET_KEY, (CK_KEY_TYPE)CKK_GENERIC_SECRET, 0, SOFT_DERIVE_KEY_OTHER, B_FALSE); if (rv != CKR_OK) { return (rv); } /* Validate the key type and key length */ rv = soft_key_derive_check_length(secret_key, hash_size); if (rv != CKR_OK) { if (IS_TOKEN_OBJECT(secret_key)) soft_delete_token_object(secret_key, B_FALSE, B_FALSE); else soft_delete_object(session_p, secret_key, B_FALSE, B_FALSE); return (rv); } /* * Derive the secret key by digesting the value of another * secret key (base key) with SHA-1 or MD5. */ rv = soft_digest_init_internal(session_p, &digest_mech); if (rv != CKR_OK) { if (IS_TOKEN_OBJECT(secret_key)) soft_delete_token_object(secret_key, B_FALSE, B_FALSE); else soft_delete_object(session_p, secret_key, B_FALSE, B_FALSE); return (rv); } rv = soft_digest(session_p, OBJ_SEC_VALUE(basekey_p), OBJ_SEC_VALUE_LEN(basekey_p), hash, &hash_len); (void) pthread_mutex_lock(&session_p->session_mutex); /* soft_digest_common() has freed the digest context */ session_p->digest.flags = 0; (void) pthread_mutex_unlock(&session_p->session_mutex); if (rv != CKR_OK) { if (IS_TOKEN_OBJECT(secret_key)) soft_delete_token_object(secret_key, B_FALSE, B_FALSE); else soft_delete_object(session_p, secret_key, B_FALSE, B_FALSE); return (rv); } secret_key_len = OBJ_SEC_VALUE_LEN(secret_key); if ((OBJ_SEC_VALUE(secret_key) = malloc(secret_key_len)) == NULL) { if (IS_TOKEN_OBJECT(secret_key)) soft_delete_token_object(secret_key, B_FALSE, B_FALSE); else soft_delete_object(session_p, secret_key, B_FALSE, B_FALSE); return (CKR_HOST_MEMORY); } /* * The key produced by this mechanism will be of the * specified type and length. * The truncation removes extra bytes from the leading * of the digested key value. */ (void) memcpy(OBJ_SEC_VALUE(secret_key), (hash + hash_len - secret_key_len), secret_key_len); break; /* * The key sensitivity and extractability rules for the generated * keys will be enforced inside soft_ssl_master_key_derive() and * soft_ssl_key_and_mac_derive() */ case CKM_SSL3_MASTER_KEY_DERIVE: case CKM_SSL3_MASTER_KEY_DERIVE_DH: case CKM_TLS_MASTER_KEY_DERIVE: case CKM_TLS_MASTER_KEY_DERIVE_DH: if (phKey == NULL_PTR) return (CKR_ARGUMENTS_BAD); return (soft_ssl_master_key_derive(session_p, pMechanism, basekey_p, pTemplate, ulAttributeCount, phKey)); case CKM_SSL3_KEY_AND_MAC_DERIVE: case CKM_TLS_KEY_AND_MAC_DERIVE: /* These mechanisms do not use phKey */ return (soft_ssl_key_and_mac_derive(session_p, pMechanism, basekey_p, pTemplate, ulAttributeCount)); case CKM_TLS_PRF: /* This mechanism does not use phKey */ if (pMechanism->pParameter == NULL || pMechanism->ulParameterLen != sizeof (CK_TLS_PRF_PARAMS) || phKey != NULL) return (CKR_ARGUMENTS_BAD); if (pTemplate != NULL) return (CKR_TEMPLATE_INCONSISTENT); return (derive_tls_prf( (CK_TLS_PRF_PARAMS_PTR)pMechanism->pParameter, basekey_p)); default: return (CKR_MECHANISM_INVALID); } soft_derive_enforce_flags(basekey_p, secret_key); if (IS_TOKEN_OBJECT(secret_key)) { /* * All the info has been filled, so we can write to * keystore now. */ rv = soft_put_object_to_keystore(secret_key); if (rv != CKR_OK) soft_delete_token_object(secret_key, B_FALSE, B_FALSE); } /* * Some mechanisms don't use phKey either because they create * multiple key objects and instead populate a structure passed in * as a field in their pParameter parameter with the resulting key * objects (e.g. CKM_TLS_KEY_AND_MAC_DERIVE) or they instead write * their result to an output buffer passed in their pParameter * parameter (e.g. CKM_TLS_PRF). All such mechanisms return prior * to reaching here. The remaining mechanisms (which do use phKey) * should have already validated phKey is not NULL prior to doing * their key derivation. */ *phKey = secret_key->handle; return (rv); } /* * Perform key derivation rules on key's sensitivity and extractability. */ void soft_derive_enforce_flags(soft_object_t *basekey, soft_object_t *newkey) { boolean_t new_sensitive = B_FALSE; boolean_t new_extractable = B_FALSE; /* * The sensitive and extractable bits have been set when * the newkey was built. */ if (newkey->bool_attr_mask & SENSITIVE_BOOL_ON) { new_sensitive = B_TRUE; } if (newkey->bool_attr_mask & EXTRACTABLE_BOOL_ON) { new_extractable = B_TRUE; } /* Derive the CKA_ALWAYS_SENSITIVE flag */ if (!basekey->bool_attr_mask & ALWAYS_SENSITIVE_BOOL_ON) { /* * If the base key has its CKA_ALWAYS_SENSITIVE set to * FALSE, then the derived key will as well. */ newkey->bool_attr_mask &= ~ALWAYS_SENSITIVE_BOOL_ON; } else { /* * If the base key has its CKA_ALWAYS_SENSITIVE set to TRUE, * then the derived key has the CKA_ALWAYS_SENSITIVE set to * the same value as its CKA_SENSITIVE; */ if (new_sensitive) { newkey->bool_attr_mask |= ALWAYS_SENSITIVE_BOOL_ON; } else { newkey->bool_attr_mask &= ~ALWAYS_SENSITIVE_BOOL_ON; } } /* Derive the CKA_NEVER_EXTRACTABLE flag */ if (!basekey->bool_attr_mask & NEVER_EXTRACTABLE_BOOL_ON) { /* * If the base key has its CKA_NEVER_EXTRACTABLE set to * FALSE, then the derived key will as well. */ newkey->bool_attr_mask &= ~NEVER_EXTRACTABLE_BOOL_ON; } else { /* * If the base key has its CKA_NEVER_EXTRACTABLE set to TRUE, * then the derived key has the CKA_NEVER_EXTRACTABLE set to * the opposite value from its CKA_EXTRACTABLE; */ if (new_extractable) { newkey->bool_attr_mask &= ~NEVER_EXTRACTABLE_BOOL_ON; } else { newkey->bool_attr_mask |= NEVER_EXTRACTABLE_BOOL_ON; } } /* Set the CKA_LOCAL flag to false */ newkey->bool_attr_mask &= ~LOCAL_BOOL_ON; } /* * do_prf * * This routine implements Step 3. of the PBKDF2 function * defined in PKCS#5 for generating derived keys from a * password. * * Currently, PRF is always SHA_1_HMAC. */ static CK_RV do_prf(soft_session_t *session_p, CK_PKCS5_PBKD2_PARAMS_PTR params, soft_object_t *hmac_key, CK_BYTE *newsalt, CK_ULONG saltlen, CK_BYTE *blockdata, CK_ULONG blocklen) { CK_RV rv = CKR_OK; CK_MECHANISM digest_mech = {CKM_SHA_1_HMAC, NULL, 0}; CK_BYTE buffer[2][SHA1_HASH_SIZE]; CK_ULONG hmac_outlen = SHA1_HASH_SIZE; CK_ULONG inlen; CK_BYTE *input, *output; CK_ULONG i, j; input = newsalt; inlen = saltlen; output = buffer[1]; (void) pthread_mutex_lock(&session_p->session_mutex); if (session_p->sign.flags & CRYPTO_OPERATION_ACTIVE) { (void) pthread_mutex_unlock(&session_p->session_mutex); return (CKR_OPERATION_ACTIVE); } session_p->sign.flags |= CRYPTO_OPERATION_ACTIVE; (void) pthread_mutex_unlock(&session_p->session_mutex); for (i = 0; i < params->iterations; i++) { /* * The key doesn't change, its always the * password iniitally given. */ rv = soft_sign_init(session_p, &digest_mech, hmac_key); if (rv != CKR_OK) { goto cleanup; } /* Call PRF function (SHA1_HMAC for now). */ rv = soft_sign(session_p, input, inlen, output, &hmac_outlen); if (rv != CKR_OK) { goto cleanup; } /* * The first time, initialize the output buffer * with the HMAC signature. */ if (i == 0) { (void) memcpy(blockdata, output, local_min(blocklen, hmac_outlen)); } else { /* * XOR the existing data with output from PRF. * * Only XOR up to the length of the blockdata, * it may be less than a full hmac buffer when * the final block is being computed. */ for (j = 0; j < hmac_outlen && j < blocklen; j++) blockdata[j] ^= output[j]; } /* Output from previous PRF is input for next round */ input = output; inlen = hmac_outlen; /* * Switch buffers to avoid overuse of memcpy. * Initially we used buffer[1], so after the end of * the first iteration (i==0), we switch to buffer[0] * and continue swapping with each iteration. */ output = buffer[i%2]; } cleanup: (void) pthread_mutex_lock(&session_p->session_mutex); session_p->sign.flags &= ~CRYPTO_OPERATION_ACTIVE; (void) pthread_mutex_unlock(&session_p->session_mutex); return (rv); } static CK_RV soft_create_hmac_key(soft_session_t *session_p, CK_BYTE *passwd, CK_ULONG passwd_len, soft_object_t **keyp) { CK_RV rv = CKR_OK; CK_OBJECT_CLASS keyclass = CKO_SECRET_KEY; CK_KEY_TYPE keytype = CKK_GENERIC_SECRET; CK_BBOOL True = TRUE; CK_ATTRIBUTE keytemplate[4]; /* * We must initialize each template member individually * because at the time of initial coding for ON10, the * compiler was using the "-xc99=%none" option * which prevents us from being able to declare the whole * template in place as usual. */ keytemplate[0].type = CKA_CLASS; keytemplate[0].pValue = &keyclass; keytemplate[0].ulValueLen = sizeof (keyclass); keytemplate[1].type = CKA_KEY_TYPE; keytemplate[1].pValue = &keytype; keytemplate[1].ulValueLen = sizeof (keytype); keytemplate[2].type = CKA_SIGN; keytemplate[2].pValue = &True; keytemplate[2].ulValueLen = sizeof (True); keytemplate[3].type = CKA_VALUE; keytemplate[3].pValue = passwd; keytemplate[3].ulValueLen = passwd_len; /* * Create a generic key object to be used for HMAC operations. * The "value" for this key is the password from the * mechanism parameter structure. */ rv = soft_gen_keyobject(keytemplate, sizeof (keytemplate)/sizeof (CK_ATTRIBUTE), keyp, session_p, CKO_SECRET_KEY, (CK_KEY_TYPE)CKK_GENERIC_SECRET, 0, SOFT_CREATE_OBJ, B_TRUE); return (rv); } CK_RV soft_generate_pkcs5_pbkdf2_key(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism, soft_object_t *secret_key) { CK_RV rv = CKR_OK; CK_PKCS5_PBKD2_PARAMS *params = (CK_PKCS5_PBKD2_PARAMS *)pMechanism->pParameter; CK_ULONG hLen = SHA1_HASH_SIZE; CK_ULONG dkLen, i; CK_ULONG blocks, remainder; soft_object_t *hmac_key = NULL; CK_BYTE *salt = NULL; CK_BYTE *keydata = NULL; params = (CK_PKCS5_PBKD2_PARAMS_PTR) pMechanism->pParameter; if (params->prf != CKP_PKCS5_PBKD2_HMAC_SHA1) return (CKR_MECHANISM_PARAM_INVALID); if (params->pPrfData != NULL || params->ulPrfDataLen != 0) return (CKR_DATA_INVALID); if (params->saltSource != CKZ_SALT_SPECIFIED || params->iterations == 0) return (CKR_MECHANISM_PARAM_INVALID); /* * Create a key object to use for HMAC operations. */ rv = soft_create_hmac_key(session_p, params->pPassword, *params->ulPasswordLen, &hmac_key); if (rv != CKR_OK) return (rv); /* Step 1. */ dkLen = OBJ_SEC_VALUE_LEN(secret_key); /* length of desired key */ if (dkLen > ((((u_longlong_t)1)<<32)-1)*hLen) { (void) soft_delete_object(session_p, hmac_key, B_FALSE, B_FALSE); return (CKR_KEY_SIZE_RANGE); } /* Step 2. */ blocks = dkLen / hLen; /* crude "Ceiling" function to adjust the number of blocks to use */ if (blocks * hLen != dkLen) blocks++; remainder = dkLen - ((blocks - 1) * hLen); /* Step 3 */ salt = (CK_BYTE *)malloc(params->ulSaltSourceDataLen + 4); if (salt == NULL) { (void) soft_delete_object(session_p, hmac_key, B_FALSE, B_FALSE); return (CKR_HOST_MEMORY); } /* * Nothing in PKCS#5 says you cannot pass an empty * salt, so we will allow for this and not return error * if the salt is not specified. */ if (params->pSaltSourceData != NULL && params->ulSaltSourceDataLen > 0) (void) memcpy(salt, params->pSaltSourceData, params->ulSaltSourceDataLen); /* * Get pointer to the data section of the key, * this will be used below as output from the * PRF iteration/concatenations so that when the * blocks are all iterated, the secret_key will * have the resulting derived key value. */ keydata = (CK_BYTE *)OBJ_SEC_VALUE(secret_key); /* Step 4. */ for (i = 0; i < blocks && (rv == CKR_OK); i++) { CK_BYTE *s; s = salt + params->ulSaltSourceDataLen; /* * Append the block index to the salt as input * to the PRF. Block index should start at 1 * not 0. */ *s++ = ((i+1) >> 24) & 0xff; *s++ = ((i+1) >> 16) & 0xff; *s++ = ((i+1) >> 8) & 0xff; *s = ((i+1)) & 0xff; /* * Adjust the key pointer so we always append the * PRF output to the current key. */ rv = do_prf(session_p, params, hmac_key, salt, params->ulSaltSourceDataLen + 4, keydata, ((i + 1) == blocks ? remainder : hLen)); keydata += hLen; } (void) soft_delete_object(session_p, hmac_key, B_FALSE, B_FALSE); freezero(salt, params->ulSaltSourceDataLen); return (rv); } CK_RV soft_wrapkey(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism, soft_object_t *wrappingKey_p, soft_object_t *hkey_p, CK_BYTE_PTR pWrappedKey, CK_ULONG_PTR pulWrappedKeyLen) { CK_RV rv = CKR_OK; CK_ULONG plain_len = 0; CK_BYTE_PTR plain_data = NULL; CK_ULONG padded_len = 0; CK_BYTE_PTR padded_data = NULL; CK_ULONG wkey_blksz = 1; /* so modulo will work right */ /* Check if the mechanism is supported. */ switch (pMechanism->mechanism) { case CKM_DES_CBC_PAD: case CKM_DES3_CBC_PAD: case CKM_AES_CBC_PAD: /* * Secret key mechs with padding can be used to wrap secret * keys and private keys only. See PKCS#11, * sec 11.14, * C_WrapKey and secs 12.* for each mechanism's wrapping/ * unwrapping constraints. */ if (hkey_p->class != CKO_SECRET_KEY && hkey_p->class != CKO_PRIVATE_KEY) return (CKR_MECHANISM_INVALID); break; case CKM_RSA_PKCS: case CKM_RSA_X_509: case CKM_DES_ECB: case CKM_DES3_ECB: case CKM_AES_ECB: case CKM_DES_CBC: case CKM_DES3_CBC: case CKM_AES_CBC: case CKM_AES_CTR: case CKM_BLOWFISH_CBC: /* * Unpadded secret key mechs and private key mechs are only * defined for wrapping secret keys. See PKCS#11 refs above. */ if (hkey_p->class != CKO_SECRET_KEY) return (CKR_MECHANISM_INVALID); break; default: return (CKR_MECHANISM_INVALID); } if (hkey_p->class == CKO_SECRET_KEY) { plain_data = OBJ_SEC_VALUE(hkey_p); plain_len = OBJ_SEC_VALUE_LEN(hkey_p); } else { /* * BER-encode the object to be wrapped: call first with * plain_data = NULL to get the size needed, allocate that * much space, call again to fill space with actual data. */ rv = soft_object_to_asn1(hkey_p, NULL, &plain_len); if (rv != CKR_OK) return (rv); if ((plain_data = malloc(plain_len)) == NULL) return (CKR_HOST_MEMORY); (void) memset(plain_data, 0x0, plain_len); rv = soft_object_to_asn1(hkey_p, plain_data, &plain_len); if (rv != CKR_OK) goto cleanup_wrap; } /* * For unpadded ECB and CBC mechanisms, the object needs to be * padded to the wrapping key's blocksize prior to the encryption. */ padded_len = plain_len; padded_data = plain_data; switch (pMechanism->mechanism) { case CKM_DES_ECB: case CKM_DES3_ECB: case CKM_AES_ECB: case CKM_DES_CBC: case CKM_DES3_CBC: case CKM_AES_CBC: case CKM_BLOWFISH_CBC: /* Find the block size of the wrapping key. */ if (wrappingKey_p->class == CKO_SECRET_KEY) { switch (wrappingKey_p->key_type) { case CKK_DES: case CKK_DES2: case CKK_DES3: wkey_blksz = DES_BLOCK_LEN; break; case CKK_AES: wkey_blksz = AES_BLOCK_LEN; break; case CKK_BLOWFISH: wkey_blksz = BLOWFISH_BLOCK_LEN; break; default: break; } } else { rv = CKR_WRAPPING_KEY_TYPE_INCONSISTENT; goto cleanup_wrap; } /* Extend the plain text data to block size boundary. */ if ((padded_len % wkey_blksz) != 0) { padded_len += (wkey_blksz - (plain_len % wkey_blksz)); if ((padded_data = malloc(padded_len)) == NULL) { rv = CKR_HOST_MEMORY; goto cleanup_wrap; } (void) memset(padded_data, 0x0, padded_len); (void) memcpy(padded_data, plain_data, plain_len); } break; default: break; } rv = soft_encrypt_init(session_p, pMechanism, wrappingKey_p); if (rv != CKR_OK) goto cleanup_wrap; rv = soft_encrypt(session_p, padded_data, padded_len, pWrappedKey, pulWrappedKeyLen); cleanup_wrap: if (padded_data != NULL && padded_len != plain_len) { /* Clear buffer before returning to memory pool. */ freezero(padded_data, padded_len); } if ((hkey_p->class != CKO_SECRET_KEY) && (plain_data != NULL)) { /* Clear buffer before returning to memory pool. */ freezero(plain_data, plain_len); } return (rv); } /* * Quick check for whether unwrapped key length is appropriate for key type * and whether it needs to be truncated (in case the wrapping function had * to pad the key prior to wrapping). */ static CK_RV soft_unwrap_secret_len_check(CK_KEY_TYPE keytype, CK_MECHANISM_TYPE mechtype, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount) { CK_ULONG i; boolean_t isValueLen = B_FALSE; /* * Based on the key type and the mech used to unwrap, need to * determine if CKA_VALUE_LEN should or should not be specified. * PKCS#11 v2.11 restricts CKA_VALUE_LEN from being specified * for C_UnwrapKey for all mechs and key types, but v2.20 loosens * that restriction, perhaps because it makes it impossible to * determine the original length of unwrapped variable-length secret * keys, such as RC4, AES, and GENERIC_SECRET. These variable-length * secret keys would have been padded with trailing null-bytes so * that they could be successfully wrapped with *_ECB and *_CBC * mechanisms. Hence for unwrapping with these mechs, CKA_VALUE_LEN * must be specified. For unwrapping with other mechs, such as * *_CBC_PAD, the CKA_VALUE_LEN is not needed. */ /* Find out if template has CKA_VALUE_LEN. */ for (i = 0; i < ulAttributeCount; i++) { if (pTemplate[i].type == CKA_VALUE_LEN && pTemplate[i].pValue != NULL) { isValueLen = B_TRUE; break; } } /* Does its presence conflict with the mech type and key type? */ switch (mechtype) { case CKM_DES_ECB: case CKM_DES3_ECB: case CKM_AES_ECB: case CKM_DES_CBC: case CKM_DES3_CBC: case CKM_AES_CBC: case CKM_BLOWFISH_CBC: /* * CKA_VALUE_LEN must be specified * if keytype is CKK_RC4, CKK_AES and CKK_GENERIC_SECRET * and must not be specified otherwise */ switch (keytype) { case CKK_DES: case CKK_DES2: case CKK_DES3: if (isValueLen) return (CKR_TEMPLATE_INCONSISTENT); break; case CKK_GENERIC_SECRET: case CKK_RC4: case CKK_AES: case CKK_BLOWFISH: if (!isValueLen) return (CKR_TEMPLATE_INCOMPLETE); break; default: return (CKR_FUNCTION_NOT_SUPPORTED); } break; default: /* CKA_VALUE_LEN must not be specified */ if (isValueLen) return (CKR_TEMPLATE_INCONSISTENT); break; } return (CKR_OK); } CK_RV soft_unwrapkey(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism, soft_object_t *unwrappingkey_p, CK_BYTE_PTR pWrappedKey, CK_ULONG ulWrappedKeyLen, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey) { CK_RV rv = CKR_OK; CK_OBJECT_CLASS new_obj_class = ~0UL; int i = 0; soft_object_t *new_objp = NULL; boolean_t persistent = B_FALSE; CK_BYTE_PTR plain_data = NULL; CK_ULONG plain_len = 0; secret_key_obj_t *sck = NULL; /* Scan the attribute template for the object class. */ if (pTemplate != NULL && ulAttributeCount != 0) { for (i = 0; i < ulAttributeCount; i++) { if (pTemplate[i].type == CKA_CLASS) { new_obj_class = *((CK_OBJECT_CLASS *)pTemplate[i].pValue); break; } } if (new_obj_class == ~0UL) return (CKR_TEMPLATE_INCOMPLETE); } /* * Check if the mechanism is supported, and now that the new * object's class is known, the mechanism selected should be * capable of doing the unwrap. */ switch (pMechanism->mechanism) { case CKM_RSA_PKCS: case CKM_RSA_X_509: case CKM_DES_ECB: case CKM_DES3_ECB: case CKM_AES_ECB: case CKM_DES_CBC: case CKM_DES3_CBC: case CKM_AES_CBC: case CKM_BLOWFISH_CBC: if (new_obj_class != CKO_SECRET_KEY) return (CKR_MECHANISM_INVALID); break; case CKM_DES_CBC_PAD: case CKM_DES3_CBC_PAD: case CKM_AES_CBC_PAD: if (new_obj_class != CKO_SECRET_KEY && new_obj_class != CKO_PRIVATE_KEY) return (CKR_MECHANISM_INVALID); break; default: return (CKR_MECHANISM_INVALID); } /* Create a new object based on the attribute template. */ rv = soft_gen_keyobject(pTemplate, ulAttributeCount, &new_objp, session_p, (CK_OBJECT_CLASS)~0UL, (CK_KEY_TYPE)~0UL, 0, SOFT_UNWRAP_KEY, B_FALSE); if (rv != CKR_OK) return (rv); /* * New key will have CKA_ALWAYS_SENSITIVE and CKA_NEVER_EXTRACTABLE * both set to FALSE. CKA_EXTRACTABLE will be set _by_default_ to * true -- leaving the possibility that it may be set FALSE by the * supplied attribute template. If the precise template cannot be * supported, unwrap fails. PKCS#11 spec, Sec. 11.14, C_UnwrapKey. * * Therefore, check the new object's NEVER_EXTRACTABLE_BOOL_ON and * ALWAYS_SENSITVE_BOOL_ON; if they are TRUE, the template must * have supplied them and therefore we cannot honor the unwrap. */ if ((new_objp->bool_attr_mask & NEVER_EXTRACTABLE_BOOL_ON) || (new_objp->bool_attr_mask & ALWAYS_SENSITIVE_BOOL_ON)) { rv = CKR_TEMPLATE_INCONSISTENT; goto cleanup_unwrap; } rv = soft_decrypt_init(session_p, pMechanism, unwrappingkey_p); if (rv != CKR_OK) goto cleanup_unwrap; /* First get the length of the plain data */ rv = soft_decrypt(session_p, pWrappedKey, ulWrappedKeyLen, NULL, &plain_len); if (rv != CKR_OK) goto cleanup_unwrap; /* Allocate space for the unwrapped data */ if ((plain_data = malloc(plain_len)) == NULL) { rv = CKR_HOST_MEMORY; goto cleanup_unwrap; } (void) memset(plain_data, 0x0, plain_len); /* Perform actual decryption into the allocated space. */ rv = soft_decrypt(session_p, pWrappedKey, ulWrappedKeyLen, plain_data, &plain_len); if (rv != CKR_OK) goto cleanup_unwrap; if (new_objp->class == CKO_SECRET_KEY) { /* * Since no ASN.1 encoding is done for secret keys, check for * appropriateness and copy decrypted buffer to the key object. */ /* Check keytype and mechtype don't conflict with valuelen */ rv = soft_unwrap_secret_len_check(new_objp->key_type, pMechanism->mechanism, pTemplate, ulAttributeCount); if (rv != CKR_OK) goto cleanup_unwrap; /* * Allocate the secret key structure if not already there; * it will exist for variable length keys since CKA_VALUE_LEN * is specified and saved, but not for fixed length keys. */ if (OBJ_SEC(new_objp) == NULL) { if ((sck = calloc(1, sizeof (secret_key_obj_t))) == NULL) { rv = CKR_HOST_MEMORY; goto cleanup_unwrap; } OBJ_SEC(new_objp) = sck; } switch (new_objp->key_type) { /* Fixed length secret keys don't have CKA_VALUE_LEN */ case CKK_DES: OBJ_SEC_VALUE_LEN(new_objp) = DES_KEYSIZE; break; case CKK_DES2: OBJ_SEC_VALUE_LEN(new_objp) = DES2_KEYSIZE; break; case CKK_DES3: OBJ_SEC_VALUE_LEN(new_objp) = DES3_KEYSIZE; break; /* * Variable length secret keys. CKA_VALUE_LEN must be * provided by the template when mech is *_ECB or *_CBC, and * should already have been set during soft_gen_keyobject(). * Otherwise we don't need CKA_VALUE_LEN. */ case CKK_GENERIC_SECRET: case CKK_RC4: case CKK_AES: case CKK_BLOWFISH: break; default: rv = CKR_WRAPPED_KEY_INVALID; goto cleanup_unwrap; }; if (OBJ_SEC_VALUE_LEN(new_objp) == 0) { /* No CKA_VALUE_LEN set so set it now and save data */ OBJ_SEC_VALUE_LEN(new_objp) = plain_len; OBJ_SEC_VALUE(new_objp) = plain_data; } else if (OBJ_SEC_VALUE_LEN(new_objp) == plain_len) { /* No need to truncate, just save the data */ OBJ_SEC_VALUE(new_objp) = plain_data; } else if (OBJ_SEC_VALUE_LEN(new_objp) > plain_len) { /* Length can't be bigger than what was decrypted */ rv = CKR_WRAPPED_KEY_LEN_RANGE; goto cleanup_unwrap; } else { /* betw 0 and plain_len, hence padded */ /* Truncate the data before saving. */ OBJ_SEC_VALUE(new_objp) = realloc(plain_data, OBJ_SEC_VALUE_LEN(new_objp)); if (OBJ_SEC_VALUE(new_objp) == NULL) { rv = CKR_HOST_MEMORY; goto cleanup_unwrap; } } } else { /* BER-decode the object to be unwrapped. */ rv = soft_asn1_to_object(new_objp, plain_data, plain_len); if (rv != CKR_OK) goto cleanup_unwrap; } /* If it needs to be persistent, write it to the keystore */ if (IS_TOKEN_OBJECT(new_objp)) { persistent = B_TRUE; rv = soft_put_object_to_keystore(new_objp); if (rv != CKR_OK) goto cleanup_unwrap; } if (new_objp->class != CKO_SECRET_KEY) { /* Clear buffer before returning to memory pool. */ freezero(plain_data, plain_len); } *phKey = (CK_OBJECT_HANDLE)new_objp; return (CKR_OK); cleanup_unwrap: /* The decrypted private key buffer must be freed explicitly. */ if ((new_objp->class != CKO_SECRET_KEY) && (plain_data != NULL)) { /* Clear buffer before returning to memory pool. */ freezero(plain_data, plain_len); } /* sck and new_objp are indirectly free()d inside these functions */ if (IS_TOKEN_OBJECT(new_objp)) soft_delete_token_object(new_objp, persistent, B_FALSE); else soft_delete_object(session_p, new_objp, B_FALSE, B_FALSE); return (rv); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2020 Joyent, Inc. */ #include #include #include #include #include #include #include #include #include #include #include "softGlobal.h" #include "softCrypt.h" #include "softSession.h" #include "softObject.h" #include "softKeys.h" #include "softKeystore.h" #include "softKeystoreUtil.h" #include "softMAC.h" #include "softOps.h" soft_session_t token_session; /* * soft_gen_hashed_pin() * * Arguments: * * pPin: pointer to caller provided Pin * result: output argument which contains the address of the * pointer to the hashed pin * salt: input argument (if non-NULL), or * output argument (if NULL): * address of pointer to the "salt" of the hashed pin * * Description: * * Generate a hashed pin using system provided crypt(3C) function. * * Returns: * * 0: no error * -1: some error occurred while generating the hashed pin * */ int soft_gen_hashed_pin(CK_UTF8CHAR_PTR pPin, char **result, char **salt) { uid_t uid; struct passwd pwd, *pw; char pwdbuf[PWD_BUFFER_SIZE]; boolean_t new_salt = B_FALSE; /* * We need to get the passwd entry of the application, which is required * by the crypt_gensalt() below. */ uid = geteuid(); if (getpwuid_r(uid, &pwd, pwdbuf, PWD_BUFFER_SIZE, &pw) != 0) { return (-1); } if (*salt == NULL) { new_salt = B_TRUE; /* * crypt_gensalt() will allocate memory to store the new salt. * on return. Pass "$5" here to default to crypt_sha256 since * SHA256 is a FIPS 140-2 certified algorithm and we shouldn't * assume the system default is that strong. */ if ((*salt = crypt_gensalt("$5", pw)) == NULL) { return (-1); } } if ((*result = crypt((char *)pPin, *salt)) == NULL) { if (new_salt) { size_t saltlen = strlen(*salt) + 1; freezero(*salt, saltlen); } return (-1); } return (0); } /* * Authenticate user's PIN for C_Login. */ CK_RV soft_verify_pin(CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen) { char *user_cryptpin = NULL; char *ks_cryptpin = NULL; char *salt = NULL; uchar_t *tmp_pin = NULL; boolean_t pin_initialized = B_FALSE; CK_RV rv = CKR_OK; size_t len = 0; /* * Check to see if keystore is initialized. */ rv = soft_keystore_pin_initialized(&pin_initialized, &ks_cryptpin, B_FALSE); if (rv != CKR_OK) return (rv); /* * Authenticate user's PIN for C_Login. */ if (pin_initialized) { if (soft_keystore_get_pin_salt(&salt) < 0) { rv = CKR_FUNCTION_FAILED; goto cleanup; } /* * Generate the hashed value based on the user's supplied pin. */ tmp_pin = malloc(ulPinLen + 1); if (tmp_pin == NULL) { rv = CKR_HOST_MEMORY; goto cleanup; } (void) memcpy(tmp_pin, pPin, ulPinLen); tmp_pin[ulPinLen] = '\0'; if (soft_gen_hashed_pin(tmp_pin, &user_cryptpin, &salt) < 0) { rv = CKR_FUNCTION_FAILED; goto cleanup; } /* * Compare hash value of the user supplied PIN with * hash value of the keystore PIN. */ if (strcmp(user_cryptpin, ks_cryptpin) != 0) { rv = CKR_PIN_INCORRECT; goto cleanup; } /* * Provide the user's PIN to low-level keystore so that * it can use it to generate encryption key as needed for * encryption/decryption of the private objects in * keystore. */ if (soft_keystore_authpin(tmp_pin) != 0) { rv = CKR_FUNCTION_FAILED; } else { rv = CKR_OK; } goto cleanup; } else { /* * The PIN is not initialized in the keystore * We will let it pass the authentication anyway but set the * "userpin_change_needed" flag so that the application * will get CKR_PIN_EXPIRED by other C_functions such as * C_CreateObject, C_FindObjectInit, C_GenerateKey etc. */ soft_slot.userpin_change_needed = 1; rv = CKR_OK; } cleanup: if (salt) { len = strlen(salt) + 1; freezero(salt, len); } if (tmp_pin) { len = strlen((char *)tmp_pin) + 1; freezero(tmp_pin, len); } if (ks_cryptpin) { len = strlen(ks_cryptpin) + 1; freezero(ks_cryptpin, len); } return (rv); } /* * The second level C_SetPIN function. */ CK_RV soft_setpin(CK_UTF8CHAR_PTR pOldPin, CK_ULONG ulOldPinLen, CK_UTF8CHAR_PTR pNewPin, CK_ULONG ulNewPinLen) { char *user_cryptpin = NULL; char *ks_cryptpin = NULL; char *salt = NULL; boolean_t pin_initialized = B_FALSE; uchar_t *tmp_old_pin = NULL, *tmp_new_pin = NULL; CK_RV rv = CKR_OK; size_t len = 0; /* * Check to see if keystore is initialized. */ rv = soft_keystore_pin_initialized(&pin_initialized, &ks_cryptpin, B_FALSE); if (rv != CKR_OK) return (rv); /* * Authenticate user's PIN for C_SetPIN. */ if (pin_initialized) { /* * Generate the hashed value based on the user supplied PIN. */ if (soft_keystore_get_pin_salt(&salt) < 0) { rv = CKR_FUNCTION_FAILED; goto cleanup; } tmp_old_pin = malloc(ulOldPinLen + 1); if (tmp_old_pin == NULL) { rv = CKR_HOST_MEMORY; goto cleanup; } (void) memcpy(tmp_old_pin, pOldPin, ulOldPinLen); tmp_old_pin[ulOldPinLen] = '\0'; if (soft_gen_hashed_pin(tmp_old_pin, &user_cryptpin, &salt) < 0) { rv = CKR_FUNCTION_FAILED; goto cleanup; } /* * Compare hashed value of the user supplied PIN with the * hashed value of the keystore PIN. */ if (strcmp(user_cryptpin, ks_cryptpin) != 0) { rv = CKR_PIN_INCORRECT; goto cleanup; } } else { /* * This is the first time to setpin, the oldpin must be * "changeme". */ if (strncmp("changeme", (const char *)pOldPin, ulOldPinLen) != 0) { rv = CKR_PIN_INCORRECT; goto cleanup; } } tmp_new_pin = malloc(ulNewPinLen + 1); if (tmp_new_pin == NULL) { rv = CKR_HOST_MEMORY; goto cleanup; } (void) memcpy(tmp_new_pin, pNewPin, ulNewPinLen); tmp_new_pin[ulNewPinLen] = '\0'; /* * Set the new pin after the old pin is authenticated. */ if (soft_keystore_setpin(tmp_old_pin, tmp_new_pin, B_FALSE)) { rv = CKR_FUNCTION_FAILED; goto cleanup; } else { (void) pthread_mutex_lock(&soft_giant_mutex); soft_slot.userpin_change_needed = 0; (void) pthread_mutex_unlock(&soft_giant_mutex); rv = CKR_OK; } cleanup: if (salt) { len = strlen(salt) + 1; freezero(salt, len); } if (ks_cryptpin) { len = strlen(ks_cryptpin) + 1; freezero(ks_cryptpin, len); } if (tmp_old_pin) { len = strlen((char *)tmp_old_pin) + 1; freezero(tmp_old_pin, len); } if (tmp_new_pin) { len = strlen((char *)tmp_new_pin) + 1; freezero(tmp_new_pin, len); } return (rv); } /* * soft_keystore_pack_obj() * * Arguments: * * obj: pointer to the soft_object_t of the token object to * be packed * ks_buf: output argument which contains the address of the * pointer to the buf of the packed token object * soft_keystore_pack_obj() will allocate memory for the buf, * it is caller's responsibility to free it. * len: output argument which contains the address of the * buffer length of the packed token object * * Description: * * Pack the in-core token object into the keystore format. * * Returns: * * CKR_OK: no error * Other: some error occurred while packing the object * */ CK_RV soft_keystore_pack_obj(soft_object_t *obj, uchar_t **ks_buf, size_t *len) { ks_obj_hdr_t hdr; ks_attr_hdr_t attr_hdr; CK_ATTRIBUTE_INFO_PTR extra_attr; int num_attrs = 0; ulong_t len_attrs = 0; size_t ks_len; uchar_t *buf, *buf1; CK_RV rv; int i; (void) memset(&hdr, 0, sizeof (ks_obj_hdr_t)); /* * The first part of the packed format contains * the ks_obj_hdr_t struct. */ hdr.class = SWAP64((uint64_t)obj->class); hdr.key_type = SWAP64((uint64_t)obj->key_type); hdr.cert_type = SWAP64((uint64_t)obj->cert_type); hdr.bool_attr_mask = SWAP64(obj->bool_attr_mask); hdr.mechanism = SWAP64((uint64_t)obj->mechanism); hdr.object_type = obj->object_type; /* * The second part of the packed format contains * the attributes from the extra atrribute list. */ extra_attr = obj->extra_attrlistp; while (extra_attr) { num_attrs++; len_attrs += ROUNDUP(extra_attr->attr.ulValueLen, 8); extra_attr = extra_attr->next; } hdr.num_attrs = SWAP32(num_attrs); ks_len = soft_pack_object_size(obj); ks_len += sizeof (ks_obj_hdr_t) + len_attrs + 2 * num_attrs * sizeof (uint64_t); buf = calloc(1, ks_len); if (buf == NULL) { return (CKR_HOST_MEMORY); } (void) memcpy(buf, &hdr, sizeof (ks_obj_hdr_t)); buf1 = buf + sizeof (ks_obj_hdr_t); extra_attr = obj->extra_attrlistp; for (i = 0; i < num_attrs; i++) { attr_hdr.type = SWAP64((uint64_t)extra_attr->attr.type); attr_hdr.ulValueLen = SWAP64((uint64_t)extra_attr->attr.ulValueLen); (void) memcpy(buf1, &attr_hdr, sizeof (ks_attr_hdr_t)); buf1 = buf1 + sizeof (ks_attr_hdr_t); (void) memcpy(buf1, extra_attr->attr.pValue, extra_attr->attr.ulValueLen); buf1 = buf1 + ROUNDUP(extra_attr->attr.ulValueLen, 8); extra_attr = extra_attr->next; } /* * The third part of the packed format contains * the key itself. */ rv = soft_pack_object(obj, buf1); *len = ks_len; *ks_buf = buf; return (rv); } /* * soft_keystore_unpack_obj() * * Arguments: * * obj: pointer to the soft_object_t to store the unpacked * token object * ks_obj: input argument which contains the pointer to the * ks_obj_t struct of packed token object to be unpacked * * Description: * * Unpack the token object in keystore format to in-core soft_object_t. * * Returns: * * CKR_OK: no error * Other: some error occurred while unpacking the object * */ CK_RV soft_keystore_unpack_obj(soft_object_t *obj, ks_obj_t *ks_obj) { CK_RV rv; ks_obj_hdr_t *hdr; ks_attr_hdr_t *attr_hdr; CK_ATTRIBUTE template; int i; uchar_t *buf; /* * Unpack the common area. */ (void) strcpy((char *)obj->ks_handle.name, (char *)ks_obj->ks_handle.name); obj->ks_handle.public = ks_obj->ks_handle.public; /* LINTED: pointer alignment */ hdr = (ks_obj_hdr_t *)ks_obj->buf; obj->version = ks_obj->obj_version; obj->class = (CK_OBJECT_CLASS)(SWAP64(hdr->class)); obj->key_type = (CK_KEY_TYPE)(SWAP64(hdr->key_type)); obj->cert_type = (CK_CERTIFICATE_TYPE)(SWAP64(hdr->cert_type)); obj->bool_attr_mask = SWAP64(hdr->bool_attr_mask); obj->mechanism = (CK_MECHANISM_TYPE)(SWAP64(hdr->mechanism)); obj->object_type = hdr->object_type; /* * Initialize other stuffs which were not from keystore. */ (void) pthread_mutex_init(&obj->object_mutex, NULL); obj->magic_marker = SOFTTOKEN_OBJECT_MAGIC; obj->session_handle = (CK_SESSION_HANDLE)NULL; buf = ks_obj->buf + sizeof (ks_obj_hdr_t); /* * Unpack extra attribute list. */ for (i = 0; i < SWAP32(hdr->num_attrs); i++) { /* LINTED: pointer alignment */ attr_hdr = (ks_attr_hdr_t *)buf; (void) memset(&template, 0, sizeof (CK_ATTRIBUTE)); template.type = (CK_ATTRIBUTE_TYPE)(SWAP64(attr_hdr->type)); template.ulValueLen = (CK_ULONG)(SWAP64(attr_hdr->ulValueLen)); buf = buf + sizeof (ks_attr_hdr_t); /* Allocate storage for the value of the attribute. */ if (template.ulValueLen > 0) { template.pValue = malloc(template.ulValueLen); if (template.pValue == NULL) { return (CKR_HOST_MEMORY); } (void) memcpy(template.pValue, buf, template.ulValueLen); } rv = soft_add_extra_attr(&template, obj); freezero(template.pValue, template.ulValueLen); if (rv != CKR_OK) { return (rv); } buf = buf + ROUNDUP(template.ulValueLen, 8); } /* * Unpack the key itself. */ rv = soft_unpack_object(obj, buf); return (rv); } /* * soft_unpack_obj_attribute() * * Arguments: * * buf: contains the packed data (attributes) from keystore * key_dest: the key attribute will be unpacked and save in key_dest * cert_dest: the certificate attribute will be unpacked an * in cert_dest * offset: length of the current attribute occupies. * The caller should use this returned "offset" to * advance the buffer pointer to next attribute. * cert: TRUE for certificate (use cert_dest) * FALSE for key (use key_dest) * * Description: * * Unpack the attribute from keystore format to the big integer format. * * Returns: * * CKR_OK: no error * Other: some error occurred while unpacking the object attribute * */ CK_RV soft_unpack_obj_attribute(uchar_t *buf, biginteger_t *key_dest, cert_attr_t **cert_dest, ulong_t *offset, boolean_t cert) { CK_RV rv; CK_ATTRIBUTE template; /* LINTED: pointer alignment */ template.ulValueLen = SWAP64(*(uint64_t *)buf); buf = buf + sizeof (uint64_t); template.pValue = malloc(template.ulValueLen); if (template.pValue == NULL) { return (CKR_HOST_MEMORY); } (void) memcpy(template.pValue, buf, template.ulValueLen); if (cert) { rv = get_cert_attr_from_template(cert_dest, &template); } else { rv = get_bigint_attr_from_template(key_dest, &template); } freezero(template.pValue, template.ulValueLen); if (rv != CKR_OK) { return (rv); } *offset = sizeof (uint64_t) + template.ulValueLen; return (CKR_OK); } /* * Calculate the total buffer length required to store the * object key (the third part) in a keystore format. */ ulong_t soft_pack_object_size(soft_object_t *objp) { CK_OBJECT_CLASS class = objp->class; CK_KEY_TYPE keytype = objp->key_type; CK_CERTIFICATE_TYPE certtype = objp->cert_type; switch (class) { case CKO_PUBLIC_KEY: switch (keytype) { case CKK_RSA: /* * modulus_bits + modulus_len + modulus + * pubexpo_len + pubexpo */ return (ROUNDUP(((biginteger_t *) OBJ_PUB_RSA_MOD(objp))->big_value_len, 8) + ROUNDUP(((biginteger_t *) OBJ_PUB_RSA_PUBEXPO(objp))->big_value_len, 8) + 3 * sizeof (uint64_t)); case CKK_DSA: /* * prime_len + prime + subprime_len + subprime + * base_len + base + value_len + value */ return (ROUNDUP(((biginteger_t *) OBJ_PUB_DSA_PRIME(objp))->big_value_len, 8) + ROUNDUP(((biginteger_t *) OBJ_PUB_DSA_SUBPRIME(objp))->big_value_len, 8) + ROUNDUP(((biginteger_t *) OBJ_PUB_DSA_BASE(objp))->big_value_len, 8) + ROUNDUP(((biginteger_t *) OBJ_PUB_DSA_VALUE(objp))->big_value_len, 8) + 4 * sizeof (uint64_t)); case CKK_EC: /* * ec_point_len + ec_point */ return (ROUNDUP(((biginteger_t *) OBJ_PUB_EC_POINT(objp))->big_value_len, 8) + sizeof (uint64_t)); case CKK_DH: /* * prime_len + prime + base_len + base + * value_len + value */ return (ROUNDUP(((biginteger_t *) OBJ_PUB_DH_PRIME(objp))->big_value_len, 8) + ROUNDUP(((biginteger_t *) OBJ_PUB_DH_BASE(objp))->big_value_len, 8) + ROUNDUP(((biginteger_t *) OBJ_PUB_DH_VALUE(objp))->big_value_len, 8) + 3 * sizeof (uint64_t)); case CKK_X9_42_DH: /* * prime_len + prime + base_len + base + * subprime_len + subprime + value_len + value */ return (ROUNDUP(((biginteger_t *) OBJ_PUB_DH942_PRIME(objp))->big_value_len, 8) + ROUNDUP(((biginteger_t *) OBJ_PUB_DH942_BASE(objp))->big_value_len, 8) + ROUNDUP(((biginteger_t *) OBJ_PUB_DH942_SUBPRIME(objp))->big_value_len, 8) + ROUNDUP(((biginteger_t *) OBJ_PUB_DH942_VALUE(objp))->big_value_len, 8) + 4 * sizeof (uint64_t)); } /* keytype */ break; case CKO_PRIVATE_KEY: switch (keytype) { case CKK_RSA: /* * modulus_len + modulus + pubexpo_len + pubexpo + * priexpo_len + priexpo + prime1_len + prime1 + * prime2_len + prime2 + expo1_len + expo1 + * expo2_len + expo2 + coef_len + coef */ return (ROUNDUP(((biginteger_t *) OBJ_PRI_RSA_MOD(objp))->big_value_len, 8) + ROUNDUP(((biginteger_t *) OBJ_PRI_RSA_PUBEXPO(objp))->big_value_len, 8) + ROUNDUP(((biginteger_t *) OBJ_PRI_RSA_PRIEXPO(objp))->big_value_len, 8) + ROUNDUP(((biginteger_t *) OBJ_PRI_RSA_PRIME1(objp))->big_value_len, 8) + ROUNDUP(((biginteger_t *) OBJ_PRI_RSA_PRIME2(objp))->big_value_len, 8) + ROUNDUP(((biginteger_t *) OBJ_PRI_RSA_EXPO1(objp))->big_value_len, 8) + ROUNDUP(((biginteger_t *) OBJ_PRI_RSA_EXPO2(objp))->big_value_len, 8) + ROUNDUP(((biginteger_t *) OBJ_PRI_RSA_COEF(objp))->big_value_len, 8) + 8 * sizeof (uint64_t)); case CKK_DSA: /* * prime_len + prime + subprime_len + subprime + * base_len + base + value_len + value */ return (ROUNDUP(((biginteger_t *) OBJ_PRI_DSA_PRIME(objp))->big_value_len, 8) + ROUNDUP(((biginteger_t *) OBJ_PRI_DSA_SUBPRIME(objp))->big_value_len, 8) + ROUNDUP(((biginteger_t *) OBJ_PRI_DSA_BASE(objp))->big_value_len, 8) + ROUNDUP(((biginteger_t *) OBJ_PRI_DSA_VALUE(objp))->big_value_len, 8) + 4 * sizeof (uint64_t)); case CKK_DH: /* * value_bits + prime_len + prime + base_len + base + * value_len + value */ return (ROUNDUP(((biginteger_t *) OBJ_PRI_DH_PRIME(objp))->big_value_len, 8) + ROUNDUP(((biginteger_t *) OBJ_PRI_DH_BASE(objp))->big_value_len, 8) + ROUNDUP(((biginteger_t *) OBJ_PRI_DH_VALUE(objp))->big_value_len, 8) + 4 * sizeof (uint64_t)); case CKK_EC: /* * value_len + value */ return (ROUNDUP(((biginteger_t *) OBJ_PRI_EC_VALUE(objp))->big_value_len, 8) + sizeof (uint64_t)); case CKK_X9_42_DH: /* * prime_len + prime + base_len + base + * subprime_len + subprime + value_len + value */ return (ROUNDUP(((biginteger_t *) OBJ_PRI_DH942_PRIME(objp))->big_value_len, 8) + ROUNDUP(((biginteger_t *) OBJ_PRI_DH942_BASE(objp))->big_value_len, 8) + ROUNDUP(((biginteger_t *) OBJ_PRI_DH942_SUBPRIME(objp))->big_value_len, 8) + ROUNDUP(((biginteger_t *) OBJ_PRI_DH942_VALUE(objp))->big_value_len, 8) + 4 * sizeof (uint64_t)); } /* keytype */ break; case CKO_SECRET_KEY: /* * value_len + value */ return (ROUNDUP(OBJ_SEC_VALUE_LEN(objp), 8) + sizeof (uint64_t)); case CKO_CERTIFICATE: switch (certtype) { case CKC_X_509: /* * subject_len + subject + value_len + value */ return (ROUNDUP(((cert_attr_t *) X509_CERT_SUBJECT(objp))->length, 8) + ROUNDUP(((cert_attr_t *) X509_CERT_VALUE(objp))->length, 8) + 2 * sizeof (uint64_t)); case CKC_X_509_ATTR_CERT: /* * owner_len + owner + value_len + value */ return (ROUNDUP(((cert_attr_t *) X509_ATTR_CERT_OWNER(objp))->length, 8) + ROUNDUP(((cert_attr_t *) X509_ATTR_CERT_VALUE(objp))->length, 8) + 2 * sizeof (uint64_t)); } return (0); case CKO_DOMAIN_PARAMETERS: return (0); } return (0); } /* * Pack the object key (the third part) from the soft_object_t * into the keystore format. */ CK_RV soft_pack_object(soft_object_t *objp, uchar_t *buf) { CK_OBJECT_CLASS class = objp->class; CK_KEY_TYPE keytype = objp->key_type; CK_CERTIFICATE_TYPE certtype = objp->cert_type; uint64_t tmp_val; switch (class) { case CKO_PUBLIC_KEY: switch (keytype) { case CKK_RSA: /* modulus_bits */ tmp_val = SWAP64((uint64_t)OBJ_PUB_RSA_MOD_BITS(objp)); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); /* modulus_len + modulus */ tmp_val = SWAP64((uint64_t)(((biginteger_t *) OBJ_PUB_RSA_MOD(objp))->big_value_len)); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)(((biginteger_t *) OBJ_PUB_RSA_MOD(objp))->big_value), ((biginteger_t *) OBJ_PUB_RSA_MOD(objp))->big_value_len); buf = buf + ROUNDUP(((biginteger_t *) OBJ_PUB_RSA_MOD(objp))->big_value_len, 8); /* pubexpo_len + pubexpo */ tmp_val = SWAP64((uint64_t)((biginteger_t *) OBJ_PUB_RSA_PUBEXPO(objp))->big_value_len); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)(((biginteger_t *) OBJ_PUB_RSA_PUBEXPO(objp))->big_value), ((biginteger_t *) OBJ_PUB_RSA_PUBEXPO(objp))->big_value_len); break; case CKK_DSA: /* prime_len + prime */ tmp_val = SWAP64((uint64_t)((biginteger_t *) OBJ_PUB_DSA_PRIME(objp))->big_value_len); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)((biginteger_t *) OBJ_PUB_DSA_PRIME(objp))->big_value, ((biginteger_t *) OBJ_PUB_DSA_PRIME(objp))->big_value_len); buf = buf + ROUNDUP(((biginteger_t *) OBJ_PUB_DSA_PRIME(objp))->big_value_len, 8); /* subprime_len + subprime */ tmp_val = SWAP64((uint64_t)((biginteger_t *) OBJ_PUB_DSA_SUBPRIME(objp))->big_value_len); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)((biginteger_t *) OBJ_PUB_DSA_SUBPRIME(objp))->big_value, ((biginteger_t *) OBJ_PUB_DSA_SUBPRIME(objp))->big_value_len); buf = buf + ROUNDUP(((biginteger_t *) OBJ_PUB_DSA_SUBPRIME(objp))->big_value_len, 8); /* base_len + base */ tmp_val = SWAP64((uint64_t)((biginteger_t *) OBJ_PUB_DSA_BASE(objp))->big_value_len); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)((biginteger_t *) OBJ_PUB_DSA_BASE(objp))->big_value, ((biginteger_t *) OBJ_PUB_DSA_BASE(objp))->big_value_len); buf = buf + ROUNDUP(((biginteger_t *) OBJ_PUB_DSA_BASE(objp))->big_value_len, 8); /* value_len + value */ tmp_val = SWAP64((uint64_t)((biginteger_t *) OBJ_PUB_DSA_VALUE(objp))->big_value_len); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)((biginteger_t *) OBJ_PUB_DSA_VALUE(objp))->big_value, ((biginteger_t *) OBJ_PUB_DSA_VALUE(objp))->big_value_len); break; case CKK_EC: /* point_len + point */ tmp_val = SWAP64((uint64_t)((biginteger_t *) OBJ_PUB_EC_POINT(objp))->big_value_len); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)((biginteger_t *) OBJ_PUB_EC_POINT(objp))->big_value, ((biginteger_t *) OBJ_PUB_EC_POINT(objp))->big_value_len); break; case CKK_DH: /* prime_len + prime */ tmp_val = SWAP64((uint64_t)((biginteger_t *) OBJ_PUB_DH_PRIME(objp))->big_value_len); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)((biginteger_t *) OBJ_PUB_DH_PRIME(objp))->big_value, ((biginteger_t *) OBJ_PUB_DH_PRIME(objp))->big_value_len); buf = buf + ROUNDUP(((biginteger_t *) OBJ_PUB_DH_PRIME(objp))->big_value_len, 8); /* base_len + base */ tmp_val = SWAP64((uint64_t)((biginteger_t *) OBJ_PUB_DH_BASE(objp))->big_value_len); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)((biginteger_t *) OBJ_PUB_DH_BASE(objp))->big_value, ((biginteger_t *) OBJ_PUB_DH_BASE(objp))->big_value_len); buf = buf + ROUNDUP(((biginteger_t *) OBJ_PUB_DH_BASE(objp))->big_value_len, 8); /* value_len + value */ tmp_val = SWAP64((uint64_t)((biginteger_t *) OBJ_PUB_DH_VALUE(objp))->big_value_len); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)((biginteger_t *) OBJ_PUB_DH_VALUE(objp))->big_value, ((biginteger_t *) OBJ_PUB_DH_VALUE(objp))->big_value_len); break; case CKK_X9_42_DH: /* prime_len + prime */ tmp_val = SWAP64((uint64_t)((biginteger_t *) OBJ_PUB_DH942_PRIME(objp))->big_value_len); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)((biginteger_t *) OBJ_PUB_DH942_PRIME(objp))->big_value, ((biginteger_t *) OBJ_PUB_DH942_PRIME(objp))->big_value_len); buf = buf + ROUNDUP(((biginteger_t *) OBJ_PUB_DH942_PRIME(objp))->big_value_len, 8); /* base_len + base */ tmp_val = SWAP64((uint64_t)((biginteger_t *) OBJ_PUB_DH942_BASE(objp))->big_value_len); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)((biginteger_t *) OBJ_PUB_DH942_BASE(objp))->big_value, ((biginteger_t *) OBJ_PUB_DH942_BASE(objp))->big_value_len); buf = buf + ROUNDUP(((biginteger_t *) OBJ_PUB_DH942_BASE(objp))->big_value_len, 8); /* subprime_len + subprime */ tmp_val = SWAP64((uint64_t)((biginteger_t *) OBJ_PUB_DH942_SUBPRIME(objp))->big_value_len); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)((biginteger_t *) OBJ_PUB_DH942_SUBPRIME(objp))->big_value, ((biginteger_t *) OBJ_PUB_DH942_SUBPRIME(objp))->big_value_len); buf = buf + ROUNDUP(((biginteger_t *) OBJ_PUB_DH942_SUBPRIME(objp))->big_value_len, 8); /* value_len + value */ tmp_val = SWAP64((uint64_t)((biginteger_t *) OBJ_PUB_DH942_VALUE(objp))->big_value_len); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)((biginteger_t *) OBJ_PUB_DH942_VALUE(objp))->big_value, ((biginteger_t *) OBJ_PUB_DH942_VALUE(objp))->big_value_len); break; } /* keytype */ break; case CKO_PRIVATE_KEY: switch (keytype) { case CKK_RSA: /* modulus_len + modulus */ tmp_val = SWAP64((uint64_t)((biginteger_t *) OBJ_PRI_RSA_MOD(objp))->big_value_len); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)((biginteger_t *) OBJ_PRI_RSA_MOD(objp))->big_value, ((biginteger_t *) OBJ_PRI_RSA_MOD(objp))->big_value_len); buf = buf + ROUNDUP(((biginteger_t *) OBJ_PRI_RSA_MOD(objp))->big_value_len, 8); /* pubexpo_len + pubexpo */ tmp_val = SWAP64((uint64_t)((biginteger_t *) OBJ_PRI_RSA_PUBEXPO(objp))->big_value_len); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)((biginteger_t *) OBJ_PRI_RSA_PUBEXPO(objp))->big_value, ((biginteger_t *) OBJ_PRI_RSA_PUBEXPO(objp))->big_value_len); buf = buf + ROUNDUP(((biginteger_t *) OBJ_PRI_RSA_PUBEXPO(objp))->big_value_len, 8); /* priexpo_len + priexpo */ tmp_val = SWAP64((uint64_t)((biginteger_t *) OBJ_PRI_RSA_PRIEXPO(objp))->big_value_len); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)((biginteger_t *) OBJ_PRI_RSA_PRIEXPO(objp))->big_value, ((biginteger_t *) OBJ_PRI_RSA_PRIEXPO(objp))->big_value_len); buf = buf + ROUNDUP(((biginteger_t *) OBJ_PRI_RSA_PRIEXPO(objp))->big_value_len, 8); /* prime1_len + prime1 */ tmp_val = SWAP64((uint64_t)((biginteger_t *) OBJ_PRI_RSA_PRIME1(objp))->big_value_len); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)((biginteger_t *) OBJ_PRI_RSA_PRIME1(objp))->big_value, ((biginteger_t *) OBJ_PRI_RSA_PRIME1(objp))->big_value_len); buf = buf + ROUNDUP(((biginteger_t *) OBJ_PRI_RSA_PRIME1(objp))->big_value_len, 8); /* prime2_len + prime2 */ tmp_val = SWAP64((uint64_t)((biginteger_t *) OBJ_PRI_RSA_PRIME2(objp))->big_value_len); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)((biginteger_t *) OBJ_PRI_RSA_PRIME2(objp))->big_value, ((biginteger_t *) OBJ_PRI_RSA_PRIME2(objp))->big_value_len); buf = buf + ROUNDUP(((biginteger_t *) OBJ_PRI_RSA_PRIME2(objp))->big_value_len, 8); /* expo1_len + expo1 */ tmp_val = SWAP64((uint64_t)((biginteger_t *) OBJ_PRI_RSA_EXPO1(objp))->big_value_len); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)((biginteger_t *) OBJ_PRI_RSA_EXPO1(objp))->big_value, ((biginteger_t *) OBJ_PRI_RSA_EXPO1(objp))->big_value_len); buf = buf + ROUNDUP(((biginteger_t *) OBJ_PRI_RSA_EXPO1(objp))->big_value_len, 8); /* expo2_len + expo2 */ tmp_val = SWAP64((uint64_t)((biginteger_t *) OBJ_PRI_RSA_EXPO2(objp))->big_value_len); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)((biginteger_t *) OBJ_PRI_RSA_EXPO2(objp))->big_value, ((biginteger_t *) OBJ_PRI_RSA_EXPO2(objp))->big_value_len); buf = buf + ROUNDUP(((biginteger_t *) OBJ_PRI_RSA_EXPO2(objp))->big_value_len, 8); /* coef_len + coef */ tmp_val = SWAP64((uint64_t)((biginteger_t *) OBJ_PRI_RSA_COEF(objp))->big_value_len); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)((biginteger_t *) OBJ_PRI_RSA_COEF(objp))->big_value, ((biginteger_t *) OBJ_PRI_RSA_COEF(objp))->big_value_len); buf = buf + ROUNDUP(((biginteger_t *) OBJ_PRI_RSA_COEF(objp))->big_value_len, 8); break; case CKK_DSA: /* prime_len + prime */ tmp_val = SWAP64((uint64_t)((biginteger_t *) OBJ_PRI_DSA_PRIME(objp))->big_value_len); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)((biginteger_t *) OBJ_PRI_DSA_PRIME(objp))->big_value, ((biginteger_t *) OBJ_PRI_DSA_PRIME(objp))->big_value_len); buf = buf + ROUNDUP(((biginteger_t *) OBJ_PRI_DSA_PRIME(objp))->big_value_len, 8); /* subprime_len + subprime */ tmp_val = SWAP64((uint64_t)((biginteger_t *) OBJ_PRI_DSA_SUBPRIME(objp))->big_value_len); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)((biginteger_t *) OBJ_PRI_DSA_SUBPRIME(objp))->big_value, ((biginteger_t *) OBJ_PRI_DSA_SUBPRIME(objp))->big_value_len); buf = buf + ROUNDUP(((biginteger_t *) OBJ_PRI_DSA_SUBPRIME(objp))->big_value_len, 8); /* base_len + base */ tmp_val = SWAP64((uint64_t)((biginteger_t *) OBJ_PRI_DSA_BASE(objp))->big_value_len); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)((biginteger_t *) OBJ_PRI_DSA_BASE(objp))->big_value, ((biginteger_t *) OBJ_PRI_DSA_BASE(objp))->big_value_len); buf = buf + ROUNDUP(((biginteger_t *) OBJ_PRI_DSA_BASE(objp))->big_value_len, 8); /* value_len + value */ tmp_val = SWAP64((uint64_t)((biginteger_t *) OBJ_PRI_DSA_VALUE(objp))->big_value_len); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)((biginteger_t *) OBJ_PRI_DSA_VALUE(objp))->big_value, ((biginteger_t *) OBJ_PRI_DSA_VALUE(objp))->big_value_len); break; case CKK_EC: /* value_len + value */ tmp_val = SWAP64((uint64_t)((biginteger_t *) OBJ_PRI_EC_VALUE(objp))->big_value_len); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)((biginteger_t *) OBJ_PRI_EC_VALUE(objp))->big_value, ((biginteger_t *) OBJ_PRI_EC_VALUE(objp))->big_value_len); break; case CKK_DH: /* value_bits */ tmp_val = SWAP64((uint64_t)OBJ_PRI_DH_VAL_BITS(objp)); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); /* prime_len + prime */ tmp_val = SWAP64((uint64_t)((biginteger_t *) OBJ_PRI_DH_PRIME(objp))->big_value_len); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)((biginteger_t *) OBJ_PRI_DH_PRIME(objp))->big_value, ((biginteger_t *) OBJ_PRI_DH_PRIME(objp))->big_value_len); buf = buf + ROUNDUP(((biginteger_t *) OBJ_PRI_DH_PRIME(objp))->big_value_len, 8); /* base_len + base */ tmp_val = SWAP64((uint64_t)((biginteger_t *) OBJ_PRI_DH_BASE(objp))->big_value_len); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)((biginteger_t *) OBJ_PRI_DH_BASE(objp))->big_value, ((biginteger_t *) OBJ_PRI_DH_BASE(objp))->big_value_len); buf = buf + ROUNDUP(((biginteger_t *) OBJ_PRI_DH_BASE(objp))->big_value_len, 8); /* value_len + value */ tmp_val = SWAP64((uint64_t)((biginteger_t *) OBJ_PRI_DH_VALUE(objp))->big_value_len); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)((biginteger_t *) OBJ_PRI_DH_VALUE(objp))->big_value, ((biginteger_t *) OBJ_PRI_DH_VALUE(objp))->big_value_len); break; case CKK_X9_42_DH: /* prime_len + prime */ tmp_val = SWAP64((uint64_t)((biginteger_t *) OBJ_PRI_DH942_PRIME(objp))->big_value_len); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)((biginteger_t *) OBJ_PRI_DH942_PRIME(objp))->big_value, ((biginteger_t *) OBJ_PRI_DH942_PRIME(objp))->big_value_len); buf = buf + ROUNDUP(((biginteger_t *) OBJ_PRI_DH942_PRIME(objp))->big_value_len, 8); /* base_len + base */ tmp_val = SWAP64((uint64_t)((biginteger_t *) OBJ_PRI_DH942_BASE(objp))->big_value_len); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)((biginteger_t *) OBJ_PRI_DH942_BASE(objp))->big_value, ((biginteger_t *) OBJ_PRI_DH942_BASE(objp))->big_value_len); buf = buf + ROUNDUP(((biginteger_t *) OBJ_PRI_DH942_BASE(objp))->big_value_len, 8); /* subprime_len + subprime */ tmp_val = SWAP64((uint64_t)((biginteger_t *) OBJ_PRI_DH942_SUBPRIME(objp))->big_value_len); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)((biginteger_t *) OBJ_PRI_DH942_SUBPRIME(objp))->big_value, ((biginteger_t *) OBJ_PRI_DH942_SUBPRIME(objp))->big_value_len); buf = buf + ROUNDUP(((biginteger_t *) OBJ_PRI_DH942_SUBPRIME(objp))->big_value_len, 8); /* value_len + value */ tmp_val = SWAP64((uint64_t)((biginteger_t *) OBJ_PRI_DH942_VALUE(objp))->big_value_len); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)((biginteger_t *) OBJ_PRI_DH942_VALUE(objp))->big_value, ((biginteger_t *) OBJ_PRI_DH942_VALUE(objp))->big_value_len); break; } /* keytype */ break; case CKO_SECRET_KEY: /* value_len + value */ tmp_val = SWAP64((uint64_t)OBJ_SEC_VALUE_LEN(objp)); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); if (OBJ_SEC_VALUE_LEN(objp) > 0) { (void) memcpy(buf, (char *)OBJ_SEC_VALUE(objp), OBJ_SEC_VALUE_LEN(objp)); buf = buf + ROUNDUP(OBJ_SEC_VALUE_LEN(objp), 8); } break; case CKO_CERTIFICATE: switch (certtype) { case CKC_X_509: /* subject_len + subject */ tmp_val = SWAP64((uint64_t)(((cert_attr_t *) X509_CERT_SUBJECT(objp))->length)); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)((cert_attr_t *) X509_CERT_SUBJECT(objp))->value, ((cert_attr_t *) X509_CERT_SUBJECT(objp))->length); buf = buf + ROUNDUP(((cert_attr_t *) X509_CERT_SUBJECT(objp))->length, 8); /* value_len + value */ tmp_val = SWAP64((uint64_t)(((cert_attr_t *) X509_CERT_VALUE(objp))->length)); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)((cert_attr_t *) X509_CERT_VALUE(objp))->value, ((cert_attr_t *) X509_CERT_VALUE(objp))->length); break; case CKC_X_509_ATTR_CERT: /* owner_len + owner */ tmp_val = SWAP64((uint64_t)(((cert_attr_t *) X509_ATTR_CERT_OWNER(objp))->length)); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)((cert_attr_t *) X509_ATTR_CERT_OWNER(objp))->value, ((cert_attr_t *) X509_ATTR_CERT_OWNER(objp))->length); buf = buf + ROUNDUP(((cert_attr_t *) X509_ATTR_CERT_OWNER(objp))->length, 8); /* value_len + value */ tmp_val = SWAP64((uint64_t)(((cert_attr_t *) X509_ATTR_CERT_VALUE(objp))->length)); (void) memcpy(buf, (char *)&tmp_val, sizeof (uint64_t)); buf = buf + sizeof (uint64_t); (void) memcpy(buf, (char *)((cert_attr_t *) X509_ATTR_CERT_VALUE(objp))->value, ((cert_attr_t *) X509_ATTR_CERT_VALUE(objp))->length); break; } break; case CKO_DOMAIN_PARAMETERS: return (0); } return (CKR_OK); } /* * Unpack the object key in keystore format (the third part) * into soft_object_t. */ CK_RV soft_unpack_object(soft_object_t *objp, uchar_t *buf) { public_key_obj_t *pbk; private_key_obj_t *pvk; secret_key_obj_t *sck; certificate_obj_t *cert; CK_OBJECT_CLASS class = objp->class; CK_KEY_TYPE keytype = objp->key_type; CK_CERTIFICATE_TYPE certtype = objp->cert_type; biginteger_t modulus; biginteger_t pubexpo; biginteger_t prime; biginteger_t subprime; biginteger_t base; biginteger_t value; biginteger_t priexpo; biginteger_t prime1; biginteger_t prime2; biginteger_t expo1; biginteger_t expo2; biginteger_t coef; CK_RV rv = CKR_OK; ulong_t offset = 0; uint64_t tmp_val; /* prevent bigint_attr_cleanup from freeing invalid attr value */ (void) memset(&modulus, 0x0, sizeof (biginteger_t)); (void) memset(&pubexpo, 0x0, sizeof (biginteger_t)); (void) memset(&prime, 0x0, sizeof (biginteger_t)); (void) memset(&subprime, 0x0, sizeof (biginteger_t)); (void) memset(&base, 0x0, sizeof (biginteger_t)); (void) memset(&value, 0x0, sizeof (biginteger_t)); (void) memset(&priexpo, 0x0, sizeof (biginteger_t)); (void) memset(&prime1, 0x0, sizeof (biginteger_t)); (void) memset(&prime2, 0x0, sizeof (biginteger_t)); (void) memset(&expo1, 0x0, sizeof (biginteger_t)); (void) memset(&expo2, 0x0, sizeof (biginteger_t)); (void) memset(&coef, 0x0, sizeof (biginteger_t)); switch (class) { case CKO_PUBLIC_KEY: /* Allocate storage for Public Key Object. */ pbk = calloc(1, sizeof (public_key_obj_t)); if (pbk == NULL) { rv = CKR_HOST_MEMORY; return (rv); } objp->object_class_u.public_key = pbk; switch (keytype) { case CKK_RSA: /* modulus_bits */ (void) memcpy(&tmp_val, buf, sizeof (uint64_t)); KEY_PUB_RSA_MOD_BITS(pbk) = (CK_ULONG)(SWAP64(tmp_val)); buf = buf + sizeof (uint64_t); /* modulus */ if ((rv = soft_unpack_obj_attribute(buf, &modulus, NULL, &offset, B_FALSE)) != CKR_OK) goto pub_cleanup; copy_bigint_attr(&modulus, KEY_PUB_RSA_MOD(pbk)); buf += ROUNDUP(offset, 8); /* pubexpo */ if ((rv = soft_unpack_obj_attribute(buf, &pubexpo, NULL, &offset, B_FALSE)) != CKR_OK) goto pub_cleanup; copy_bigint_attr(&pubexpo, KEY_PUB_RSA_PUBEXPO(pbk)); break; case CKK_DSA: /* prime */ if ((rv = soft_unpack_obj_attribute(buf, &prime, NULL, &offset, B_FALSE)) != CKR_OK) goto pub_cleanup; copy_bigint_attr(&prime, KEY_PUB_DSA_PRIME(pbk)); buf += ROUNDUP(offset, 8); /* subprime */ if ((rv = soft_unpack_obj_attribute(buf, &subprime, NULL, &offset, B_FALSE)) != CKR_OK) goto pub_cleanup; copy_bigint_attr(&subprime, KEY_PUB_DSA_SUBPRIME(pbk)); buf += ROUNDUP(offset, 8); /* base */ if ((rv = soft_unpack_obj_attribute(buf, &base, NULL, &offset, B_FALSE)) != CKR_OK) goto pub_cleanup; copy_bigint_attr(&base, KEY_PUB_DSA_BASE(pbk)); buf += ROUNDUP(offset, 8); /* value */ if ((rv = soft_unpack_obj_attribute(buf, &value, NULL, &offset, B_FALSE)) != CKR_OK) goto pub_cleanup; copy_bigint_attr(&value, KEY_PUB_DSA_VALUE(pbk)); break; case CKK_DH: /* prime */ if ((rv = soft_unpack_obj_attribute(buf, &prime, NULL, &offset, B_FALSE)) != CKR_OK) goto pub_cleanup; copy_bigint_attr(&prime, KEY_PUB_DH_PRIME(pbk)); buf += ROUNDUP(offset, 8); /* base */ if ((rv = soft_unpack_obj_attribute(buf, &base, NULL, &offset, B_FALSE)) != CKR_OK) goto pub_cleanup; copy_bigint_attr(&base, KEY_PUB_DH_BASE(pbk)); buf += ROUNDUP(offset, 8); /* value */ if ((rv = soft_unpack_obj_attribute(buf, &value, NULL, &offset, B_FALSE)) != CKR_OK) goto pub_cleanup; copy_bigint_attr(&value, KEY_PUB_DH_VALUE(pbk)); break; case CKK_EC: /* ec_point */ if ((rv = soft_unpack_obj_attribute(buf, &value, NULL, &offset, B_FALSE)) != CKR_OK) goto pri_cleanup; copy_bigint_attr(&value, KEY_PUB_EC_POINT(pbk)); break; case CKK_X9_42_DH: /* prime */ if ((rv = soft_unpack_obj_attribute(buf, &prime, NULL, &offset, B_FALSE)) != CKR_OK) goto pub_cleanup; copy_bigint_attr(&prime, KEY_PUB_DH942_PRIME(pbk)); buf += ROUNDUP(offset, 8); /* base */ if ((rv = soft_unpack_obj_attribute(buf, &base, NULL, &offset, B_FALSE)) != CKR_OK) goto pub_cleanup; copy_bigint_attr(&base, KEY_PUB_DH942_BASE(pbk)); buf += ROUNDUP(offset, 8); /* subprime */ if ((rv = soft_unpack_obj_attribute(buf, &subprime, NULL, &offset, B_FALSE)) != CKR_OK) goto pub_cleanup; copy_bigint_attr(&subprime, KEY_PUB_DH942_SUBPRIME(pbk)); buf += ROUNDUP(offset, 8); /* value */ if ((rv = soft_unpack_obj_attribute(buf, &value, NULL, &offset, B_FALSE)) != CKR_OK) goto pub_cleanup; copy_bigint_attr(&value, KEY_PUB_DH942_VALUE(pbk)); break; } /* keytype */ break; case CKO_PRIVATE_KEY: /* Allocate storage for Private Key Object. */ pvk = calloc(1, sizeof (private_key_obj_t)); if (pvk == NULL) { rv = CKR_HOST_MEMORY; return (rv); } objp->object_class_u.private_key = pvk; switch (keytype) { case CKK_RSA: /* modulus */ if ((rv = soft_unpack_obj_attribute(buf, &modulus, NULL, &offset, B_FALSE)) != CKR_OK) goto pri_cleanup; copy_bigint_attr(&modulus, KEY_PRI_RSA_MOD(pvk)); buf += ROUNDUP(offset, 8); /* pubexpo */ if ((rv = soft_unpack_obj_attribute(buf, &pubexpo, NULL, &offset, B_FALSE)) != CKR_OK) goto pri_cleanup; copy_bigint_attr(&pubexpo, KEY_PRI_RSA_PUBEXPO(pvk)); buf += ROUNDUP(offset, 8); /* priexpo */ if ((rv = soft_unpack_obj_attribute(buf, &priexpo, NULL, &offset, B_FALSE)) != CKR_OK) goto pri_cleanup; copy_bigint_attr(&priexpo, KEY_PRI_RSA_PRIEXPO(pvk)); buf += ROUNDUP(offset, 8); /* prime1 */ if ((rv = soft_unpack_obj_attribute(buf, &prime1, NULL, &offset, B_FALSE)) != CKR_OK) goto pri_cleanup; copy_bigint_attr(&prime1, KEY_PRI_RSA_PRIME1(pvk)); buf += ROUNDUP(offset, 8); /* prime2 */ if ((rv = soft_unpack_obj_attribute(buf, &prime2, NULL, &offset, B_FALSE)) != CKR_OK) goto pri_cleanup; copy_bigint_attr(&prime2, KEY_PRI_RSA_PRIME2(pvk)); buf += ROUNDUP(offset, 8); /* expo1 */ if ((rv = soft_unpack_obj_attribute(buf, &expo1, NULL, &offset, B_FALSE)) != CKR_OK) goto pri_cleanup; copy_bigint_attr(&expo1, KEY_PRI_RSA_EXPO1(pvk)); buf += ROUNDUP(offset, 8); /* expo2 */ if ((rv = soft_unpack_obj_attribute(buf, &expo2, NULL, &offset, B_FALSE)) != CKR_OK) goto pri_cleanup; copy_bigint_attr(&expo2, KEY_PRI_RSA_EXPO2(pvk)); buf += ROUNDUP(offset, 8); /* coef */ if ((rv = soft_unpack_obj_attribute(buf, &coef, NULL, &offset, B_FALSE)) != CKR_OK) goto pri_cleanup; copy_bigint_attr(&coef, KEY_PRI_RSA_COEF(pvk)); break; case CKK_DSA: /* prime */ if ((rv = soft_unpack_obj_attribute(buf, &prime, NULL, &offset, B_FALSE)) != CKR_OK) goto pri_cleanup; copy_bigint_attr(&prime, KEY_PRI_DSA_PRIME(pvk)); buf += ROUNDUP(offset, 8); /* subprime */ if ((rv = soft_unpack_obj_attribute(buf, &subprime, NULL, &offset, B_FALSE)) != CKR_OK) goto pri_cleanup; copy_bigint_attr(&subprime, KEY_PRI_DSA_SUBPRIME(pvk)); buf += ROUNDUP(offset, 8); /* base */ if ((rv = soft_unpack_obj_attribute(buf, &base, NULL, &offset, B_FALSE)) != CKR_OK) goto pri_cleanup; copy_bigint_attr(&base, KEY_PRI_DSA_BASE(pvk)); buf += ROUNDUP(offset, 8); /* value */ if ((rv = soft_unpack_obj_attribute(buf, &value, NULL, &offset, B_FALSE)) != CKR_OK) goto pri_cleanup; copy_bigint_attr(&value, KEY_PRI_DSA_VALUE(pvk)); break; case CKK_DH: /* value_bits */ (void) memcpy(&tmp_val, buf, sizeof (uint64_t)); KEY_PRI_DH_VAL_BITS(pvk) = (CK_ULONG)(SWAP64(tmp_val)); buf = buf + sizeof (uint64_t); /* prime */ if ((rv = soft_unpack_obj_attribute(buf, &prime, NULL, &offset, B_FALSE)) != CKR_OK) goto pri_cleanup; copy_bigint_attr(&prime, KEY_PRI_DH_PRIME(pvk)); buf += ROUNDUP(offset, 8); /* base */ if ((rv = soft_unpack_obj_attribute(buf, &base, NULL, &offset, B_FALSE)) != CKR_OK) goto pri_cleanup; copy_bigint_attr(&base, KEY_PRI_DH_BASE(pvk)); buf += ROUNDUP(offset, 8); /* value */ if ((rv = soft_unpack_obj_attribute(buf, &value, NULL, &offset, B_FALSE)) != CKR_OK) goto pri_cleanup; copy_bigint_attr(&value, KEY_PRI_DH_VALUE(pvk)); break; case CKK_EC: /* value */ if ((rv = soft_unpack_obj_attribute(buf, &value, NULL, &offset, B_FALSE)) != CKR_OK) goto pri_cleanup; copy_bigint_attr(&value, KEY_PRI_EC_VALUE(pvk)); break; case CKK_X9_42_DH: /* prime */ if ((rv = soft_unpack_obj_attribute(buf, &prime, NULL, &offset, B_FALSE)) != CKR_OK) goto pri_cleanup; copy_bigint_attr(&prime, KEY_PRI_DH942_PRIME(pvk)); buf += ROUNDUP(offset, 8); /* base */ if ((rv = soft_unpack_obj_attribute(buf, &base, NULL, &offset, B_FALSE)) != CKR_OK) goto pri_cleanup; copy_bigint_attr(&base, KEY_PRI_DH942_BASE(pvk)); buf += ROUNDUP(offset, 8); /* subprime */ if ((rv = soft_unpack_obj_attribute(buf, &subprime, NULL, &offset, B_FALSE)) != CKR_OK) goto pri_cleanup; copy_bigint_attr(&subprime, KEY_PRI_DH942_BASE(pvk)); buf += ROUNDUP(offset, 8); /* value */ if ((rv = soft_unpack_obj_attribute(buf, &value, NULL, &offset, B_FALSE)) != CKR_OK) goto pri_cleanup; copy_bigint_attr(&value, KEY_PRI_DH942_VALUE(pvk)); break; } /* keytype */ break; case CKO_SECRET_KEY: /* Allocate storage for Secret Key Object. */ sck = calloc(1, sizeof (secret_key_obj_t)); if (sck == NULL) { return (CKR_HOST_MEMORY); } objp->object_class_u.secret_key = sck; /* value */ (void) memcpy((void *)&tmp_val, buf, sizeof (uint64_t)); OBJ_SEC_VALUE_LEN(objp) = (CK_ULONG)(SWAP64(tmp_val)); buf = buf + sizeof (uint64_t); if (OBJ_SEC_VALUE_LEN(objp) > 0) { OBJ_SEC_VALUE(objp) = malloc(OBJ_SEC_VALUE_LEN(objp)); if (OBJ_SEC_VALUE(objp) == NULL) { free(sck); return (CKR_HOST_MEMORY); } (void) memcpy(OBJ_SEC_VALUE(objp), buf, OBJ_SEC_VALUE_LEN(objp)); buf = buf + ROUNDUP(OBJ_SEC_VALUE_LEN(objp), 8); } return (rv); case CKO_CERTIFICATE: /* Allocate storage for Certificate Object. */ cert = calloc(1, sizeof (certificate_obj_t)); if (cert == NULL) { return (CKR_HOST_MEMORY); } (void) memset((void *)cert, 0, sizeof (certificate_obj_t)); cert->certificate_type = certtype; objp->object_class_u.certificate = cert; switch (certtype) { case CKC_X_509: /* subject */ if ((rv = soft_unpack_obj_attribute(buf, NULL, &cert->cert_type_u.x509.subject, &offset, B_TRUE)) != CKR_OK) { free(cert); return (rv); } buf += ROUNDUP(offset, 8); /* value */ if ((rv = soft_unpack_obj_attribute(buf, NULL, &cert->cert_type_u.x509.value, &offset, B_TRUE)) != CKR_OK) { free(cert); return (rv); } break; case CKC_X_509_ATTR_CERT: /* owner */ if ((rv = soft_unpack_obj_attribute(buf, NULL, &cert->cert_type_u.x509_attr.owner, &offset, B_TRUE)) != CKR_OK) { free(cert); return (rv); } buf += ROUNDUP(offset, 8); /* value */ if ((rv = soft_unpack_obj_attribute(buf, NULL, &cert->cert_type_u.x509_attr.value, &offset, B_TRUE)) != CKR_OK) { free(cert); return (rv); } break; } return (rv); case CKO_DOMAIN_PARAMETERS: break; } pub_cleanup: /* * cleanup the storage allocated to the local variables. */ if (rv != CKR_OK) free(pbk); bigint_attr_cleanup(&modulus); bigint_attr_cleanup(&pubexpo); bigint_attr_cleanup(&prime); bigint_attr_cleanup(&subprime); bigint_attr_cleanup(&base); bigint_attr_cleanup(&value); return (rv); pri_cleanup: /* * cleanup the storage allocated to the local variables. */ if (rv != CKR_OK) free(pvk); bigint_attr_cleanup(&modulus); bigint_attr_cleanup(&priexpo); bigint_attr_cleanup(&prime); bigint_attr_cleanup(&subprime); bigint_attr_cleanup(&base); bigint_attr_cleanup(&value); bigint_attr_cleanup(&pubexpo); bigint_attr_cleanup(&prime1); bigint_attr_cleanup(&prime2); bigint_attr_cleanup(&expo1); bigint_attr_cleanup(&expo2); bigint_attr_cleanup(&coef); return (rv); } /* * Store the token object to a keystore file. */ CK_RV soft_put_object_to_keystore(soft_object_t *objp) { uchar_t *buf; size_t len; CK_RV rv; rv = soft_keystore_pack_obj(objp, &buf, &len); if (rv != CKR_OK) return (rv); (void) pthread_mutex_lock(&soft_slot.slot_mutex); if (soft_keystore_put_new_obj(buf, len, !!(objp->object_type == TOKEN_PUBLIC), B_FALSE, &objp->ks_handle) == -1) { rv = CKR_FUNCTION_FAILED; } (void) pthread_mutex_unlock(&soft_slot.slot_mutex); freezero(buf, len); return (rv); } /* * Modify the in-core token object and then write it to * a keystore file. */ CK_RV soft_modify_object_to_keystore(soft_object_t *objp) { uchar_t *buf; size_t len; CK_RV rv; rv = soft_keystore_pack_obj(objp, &buf, &len); if (rv != CKR_OK) return (rv); /* B_TRUE: caller has held a writelock on the keystore */ if (soft_keystore_modify_obj(&objp->ks_handle, buf, len, B_TRUE) < 0) { rv = CKR_FUNCTION_FAILED; } freezero(buf, len); return (rv); } /* * Read the token object from the keystore file. */ CK_RV soft_get_token_objects_from_keystore(ks_search_type_t type) { CK_RV rv; ks_obj_t *ks_obj = NULL, *ks_obj_next; soft_object_t *new_objp = NULL; /* Load the token object from keystore based on the object type */ rv = soft_keystore_get_objs(type, &ks_obj, B_FALSE); if (rv != CKR_OK) { return (rv); } while (ks_obj) { new_objp = calloc(1, sizeof (soft_object_t)); if (new_objp == NULL) { rv = CKR_HOST_MEMORY; goto cleanup; } /* Convert the keystore format to memory format */ rv = soft_keystore_unpack_obj(new_objp, ks_obj); if (rv != CKR_OK) { if (new_objp->class == CKO_CERTIFICATE) soft_cleanup_cert_object(new_objp); else soft_cleanup_object(new_objp); goto cleanup; } soft_add_token_object_to_slot(new_objp); /* Free the ks_obj list */ ks_obj_next = ks_obj->next; freezero(ks_obj->buf, ks_obj->size); free(ks_obj); ks_obj = ks_obj_next; } return (CKR_OK); cleanup: while (ks_obj) { ks_obj_next = ks_obj->next; freezero(ks_obj->buf, ks_obj->size); free(ks_obj); ks_obj = ks_obj_next; } return (rv); } /* * soft_gen_crypt_key() * * Arguments: * * pPIN: pointer to caller provided Pin * key: output argument which contains the address of the * pointer to encryption key in the soft_object_t. * It is caller's responsibility to call soft_delete_object() * if this key is no longer in use. * saltdata: input argument (if non-NULL), or * output argument (if NULL): * address of pointer to the "salt" of the encryption key * * Description: * * Generate an encryption key of the input PIN. * * Returns: * * CKR_OK: no error * Other: some error occurred while generating the encryption key * */ CK_RV soft_gen_crypt_key(uchar_t *pPIN, soft_object_t **key, CK_BYTE **saltdata) { CK_OBJECT_CLASS class = CKO_SECRET_KEY; CK_ATTRIBUTE tmpl[5]; int attrs = 0; CK_RV rv; CK_MECHANISM Mechanism; CK_PKCS5_PBKD2_PARAMS params; CK_BYTE salt[PBKD2_SALT_SIZE]; CK_ULONG keylen = AES_MIN_KEY_BYTES; CK_KEY_TYPE keytype = CKK_AES; static CK_BBOOL truevalue = TRUE; soft_object_t *secret_key; CK_ULONG passwd_size; if (pPIN == NULL) return (CKR_FUNCTION_FAILED); tmpl[attrs].type = CKA_CLASS; tmpl[attrs].pValue = &class; tmpl[attrs].ulValueLen = sizeof (class); attrs++; tmpl[attrs].type = CKA_KEY_TYPE; tmpl[attrs].pValue = &keytype; tmpl[attrs].ulValueLen = sizeof (keytype); attrs++; tmpl[attrs].type = CKA_ENCRYPT; tmpl[attrs].pValue = &truevalue; tmpl[attrs].ulValueLen = sizeof (CK_BBOOL); attrs++; tmpl[attrs].type = CKA_DECRYPT; tmpl[attrs].pValue = &truevalue; tmpl[attrs].ulValueLen = sizeof (CK_BBOOL); attrs++; tmpl[attrs].type = CKA_VALUE_LEN; tmpl[attrs].pValue = &keylen; tmpl[attrs].ulValueLen = sizeof (keylen); attrs++; if (*saltdata == NULL) { bzero(salt, sizeof (salt)); (void) pkcs11_get_nzero_urandom(salt, sizeof (salt)); *saltdata = malloc(PBKD2_SALT_SIZE); if (*saltdata == NULL) return (CKR_HOST_MEMORY); (void) memcpy(*saltdata, salt, PBKD2_SALT_SIZE); } else { bzero(salt, sizeof (salt)); (void) memcpy(salt, *saltdata, PBKD2_SALT_SIZE); } Mechanism.mechanism = CKM_PKCS5_PBKD2; Mechanism.pParameter = ¶ms; Mechanism.ulParameterLen = sizeof (params); passwd_size = (CK_ULONG)strlen((const char *)pPIN); params.saltSource = CKZ_SALT_SPECIFIED; params.pSaltSourceData = (void *)salt; params.ulSaltSourceDataLen = sizeof (salt); params.iterations = PBKD2_ITERATIONS; params.prf = CKP_PKCS5_PBKD2_HMAC_SHA1; params.pPrfData = NULL; params.ulPrfDataLen = 0; params.pPassword = (CK_UTF8CHAR_PTR)pPIN; params.ulPasswordLen = &passwd_size; rv = soft_gen_keyobject(tmpl, attrs, &secret_key, &token_session, CKO_SECRET_KEY, CKK_AES, 0, SOFT_GEN_KEY, B_TRUE); if (rv != CKR_OK) { return (rv); } keylen = OBJ_SEC_VALUE_LEN(secret_key); if ((OBJ_SEC_VALUE(secret_key) = malloc(keylen)) == NULL) { soft_delete_object(&token_session, secret_key, B_FALSE, B_FALSE); return (CKR_HOST_MEMORY); } rv = soft_generate_pkcs5_pbkdf2_key(&token_session, &Mechanism, secret_key); if (rv != CKR_OK) soft_delete_object(&token_session, secret_key, B_FALSE, B_FALSE); else *key = secret_key; return (rv); } /* * soft_gen_hmac_key() * * Arguments: * * pPIN: pointer to caller provided Pin * key: output argument which contains the address of the * pointer to hmac key in the soft_object_t. * It is caller's responsibility to call soft_delete_object() * if this key is no longer in use. * saltdata: input argument (if non-NULL), or * output argument (if NULL): * address of pointer to the "salt" of the hmac key * * Description: * * Generate a hmac key of the input PIN. * * Returns: * * CKR_OK: no error * Other: some error occurred while generating the hmac key * */ CK_RV soft_gen_hmac_key(uchar_t *pPIN, soft_object_t **key, CK_BYTE **saltdata) { CK_OBJECT_CLASS class = CKO_SECRET_KEY; CK_ATTRIBUTE tmpl[5]; int attrs = 0; CK_RV rv; CK_MECHANISM Mechanism; CK_PKCS5_PBKD2_PARAMS params; CK_BYTE salt[PBKD2_SALT_SIZE]; CK_ULONG keylen = 16; CK_KEY_TYPE keytype = CKK_GENERIC_SECRET; static CK_BBOOL truevalue = TRUE; soft_object_t *secret_key; CK_ULONG passwd_size; if (pPIN == NULL) return (CKR_FUNCTION_FAILED); tmpl[attrs].type = CKA_CLASS; tmpl[attrs].pValue = &class; tmpl[attrs].ulValueLen = sizeof (class); attrs++; tmpl[attrs].type = CKA_KEY_TYPE; tmpl[attrs].pValue = &keytype; tmpl[attrs].ulValueLen = sizeof (keytype); attrs++; tmpl[attrs].type = CKA_SIGN; tmpl[attrs].pValue = &truevalue; tmpl[attrs].ulValueLen = sizeof (CK_BBOOL); attrs++; tmpl[attrs].type = CKA_VERIFY; tmpl[attrs].pValue = &truevalue; tmpl[attrs].ulValueLen = sizeof (CK_BBOOL); attrs++; tmpl[attrs].type = CKA_VALUE_LEN; tmpl[attrs].pValue = &keylen; tmpl[attrs].ulValueLen = sizeof (keylen); attrs++; if (*saltdata == NULL) { bzero(salt, sizeof (salt)); (void) pkcs11_get_nzero_urandom(salt, sizeof (salt)); *saltdata = malloc(PBKD2_SALT_SIZE); if (*saltdata == NULL) return (CKR_HOST_MEMORY); (void) memcpy(*saltdata, salt, PBKD2_SALT_SIZE); } else { bzero(salt, sizeof (salt)); (void) memcpy(salt, *saltdata, PBKD2_SALT_SIZE); } Mechanism.mechanism = CKM_PKCS5_PBKD2; Mechanism.pParameter = ¶ms; Mechanism.ulParameterLen = sizeof (params); passwd_size = (CK_ULONG)strlen((const char *)pPIN); params.saltSource = CKZ_SALT_SPECIFIED; params.pSaltSourceData = (void *)salt; params.ulSaltSourceDataLen = sizeof (salt); params.iterations = PBKD2_ITERATIONS; params.prf = CKP_PKCS5_PBKD2_HMAC_SHA1; params.pPrfData = NULL; params.ulPrfDataLen = 0; params.pPassword = (CK_UTF8CHAR_PTR)pPIN; params.ulPasswordLen = &passwd_size; rv = soft_gen_keyobject(tmpl, attrs, &secret_key, &token_session, CKO_SECRET_KEY, CKK_GENERIC_SECRET, 0, SOFT_GEN_KEY, B_TRUE); if (rv != CKR_OK) { return (rv); } keylen = OBJ_SEC_VALUE_LEN(secret_key); if ((OBJ_SEC_VALUE(secret_key) = malloc(keylen)) == NULL) { soft_delete_object(&token_session, secret_key, B_FALSE, B_FALSE); return (CKR_HOST_MEMORY); } rv = soft_generate_pkcs5_pbkdf2_key(&token_session, &Mechanism, secret_key); if (rv != CKR_OK) soft_delete_object(&token_session, secret_key, B_FALSE, B_FALSE); else *key = secret_key; return (rv); } /* * The token session is just a psuedo session (a place holder) * to hold some information during encryption/decryption and * sign/verify operations when writing/reading the keystore * token object. */ CK_RV soft_init_token_session(void) { token_session.magic_marker = SOFTTOKEN_SESSION_MAGIC; token_session.pApplication = NULL_PTR; token_session.Notify = NULL; token_session.flags = CKF_SERIAL_SESSION; token_session.state = CKS_RO_PUBLIC_SESSION; token_session.object_list = NULL; token_session.ses_refcnt = 0; token_session.ses_close_sync = 0; token_session.next = NULL; token_session.prev = NULL; /* Initialize the lock for the token session */ if (pthread_mutex_init(&token_session.session_mutex, NULL) != 0) { return (CKR_CANT_LOCK); } (void) pthread_cond_init(&token_session.ses_free_cond, NULL); return (CKR_OK); } void soft_destroy_token_session(void) { (void) pthread_cond_destroy(&token_session.ses_free_cond); (void) pthread_mutex_destroy(&token_session.session_mutex); } /* * Encrypt/Decrypt the private token object when dealing with the keystore. * This function only applies to the private token object. */ CK_RV soft_keystore_crypt(soft_object_t *key_p, uchar_t *ivec, boolean_t encrypt, CK_BYTE_PTR in, CK_ULONG in_len, CK_BYTE_PTR out, CK_ULONG_PTR out_len) { CK_MECHANISM mech; CK_RV rv; CK_ULONG tmplen, tmplen1; /* * The caller will pass NULL for "out" (output buffer) to find out * the output buffer size that it need to allocate for the encrption * or decryption. */ if (out == NULL) { mech.mechanism = CKM_AES_CBC_PAD; mech.pParameter = (void *)ivec; mech.ulParameterLen = AES_BLOCK_LEN; if (encrypt) rv = soft_aes_crypt_init_common(&token_session, &mech, key_p, B_TRUE); else rv = soft_aes_crypt_init_common(&token_session, &mech, key_p, B_FALSE); if (rv != CKR_OK) return (rv); /* * Since out == NULL, the soft_aes_xxcrypt_common() will * simply return the output buffer length to the caller. */ if (encrypt) { rv = soft_aes_encrypt(&token_session, in, in_len, out, out_len); } else { rv = soft_aes_decrypt(&token_session, in, in_len, out, out_len); } } else { /* * The caller has allocated the output buffer, so that we * are doing the real encryption/decryption this time. */ tmplen = *out_len; if (encrypt) { rv = soft_aes_encrypt_update(&token_session, in, in_len, out, &tmplen); if (rv == CKR_OK) { tmplen1 = *out_len - tmplen; rv = soft_encrypt_final(&token_session, out+tmplen, &tmplen1); *out_len = tmplen + tmplen1; } } else { rv = soft_aes_decrypt_update(&token_session, in, in_len, out, &tmplen); if (rv == CKR_OK) { tmplen1 = *out_len - tmplen; rv = soft_decrypt_final(&token_session, out+tmplen, &tmplen1); *out_len = tmplen + tmplen1; } } } return (rv); } /* * Sign/Verify the private token object for checking its data integrity * when dealing with the keystore. * This function only applies to the private token object. */ CK_RV soft_keystore_hmac(soft_object_t *key_p, boolean_t sign, CK_BYTE_PTR in, CK_ULONG in_len, CK_BYTE_PTR out, CK_ULONG_PTR out_len) { CK_MECHANISM mech; CK_RV rv; mech.mechanism = CKM_MD5_HMAC; mech.pParameter = NULL_PTR; mech.ulParameterLen = 0; rv = soft_hmac_sign_verify_init_common(&token_session, &mech, key_p, sign); if (rv != CKR_OK) return (rv); if (sign) { rv = soft_sign(&token_session, in, in_len, out, out_len); } else { rv = soft_verify(&token_session, in, in_len, out, *out_len); } return (rv); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SOFTKEYSTORE_H #define _SOFTKEYSTORE_H #ifdef __cplusplus extern "C" { #endif #include #include #define PBKD2_SALT_SIZE 16 #define PBKD2_ITERATIONS (1000) #define PWD_BUFFER_SIZE 1024 /* * The following structure is the object header * in the keystore. */ typedef struct ks_obj_hdr { uint64_t class; uint64_t key_type; uint64_t cert_type; uint64_t bool_attr_mask; uint64_t mechanism; uchar_t object_type; /* Extra non-boolean attribute list */ int num_attrs; } ks_obj_hdr_t; /* * This structure contains the individual attribute * (from extra_attrlistp) in the keystore. */ typedef struct ks_attr_hdr { uint64_t type; uint64_t ulValueLen; } ks_attr_hdr_t; #define ROUNDUP(x, y) roundup(x, y) /* defined in sys/sysmacros.h */ #ifdef _LITTLE_ENDIAN #define SWAP16(value) \ ((((value) & 0xff) << 8) | ((value) >> 8)) #define SWAP32(value) \ (((uint32_t)SWAP16((uint16_t)((value) & 0xffff)) << 16) | \ (uint32_t)SWAP16((uint16_t)((value) >> 16))) #define SWAP64(value) \ (((uint64_t)SWAP32((uint32_t)((value) & 0xffffffff)) \ << 32) | \ (uint64_t)SWAP32((uint32_t)((value) >> 32))) #else /* !_LITTLE_ENDIAN */ #define SWAP16(value) (value) #define SWAP32(value) (value) #define SWAP64(value) (value) #endif /* * Function Prototypes */ int soft_gen_hashed_pin(CK_UTF8CHAR_PTR pPin, char **result, char **salt); CK_RV soft_verify_pin(CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen); CK_RV soft_gen_crypt_key(uchar_t *pPIN, soft_object_t **key, CK_BYTE **saltdata); CK_RV soft_gen_hmac_key(uchar_t *pPIN, soft_object_t **key, CK_BYTE **saltdata); CK_RV soft_keystore_pack_obj(struct object *obj, uchar_t **ks_buf, size_t *len); CK_RV soft_keystore_unpack_obj(struct object *obj, ks_obj_t *ks_obj); CK_RV soft_unpack_obj_attribute(uchar_t *buf, biginteger_t *key_dest, cert_attr_t **cert_dest, ulong_t *offset, boolean_t cert); ulong_t soft_pack_object_size(struct object *objp); CK_RV soft_pack_object(struct object *objp, uchar_t *buf); CK_RV soft_unpack_object(struct object *objp, uchar_t *buf); CK_RV soft_setpin(CK_UTF8CHAR_PTR pOldPin, CK_ULONG ulOldPinLen, CK_UTF8CHAR_PTR pNewPin, CK_ULONG ulNewPinLen); CK_RV soft_put_object_to_keystore(struct object *objp); CK_RV soft_modify_object_to_keystore(struct object *objp); CK_RV soft_get_token_objects_from_keystore(ks_search_type_t type); CK_RV soft_init_token_session(void); void soft_destroy_token_session(void); CK_RV soft_keystore_crypt(soft_object_t *key_p, uchar_t *ivec, boolean_t encrypt, CK_BYTE_PTR in, CK_ULONG in_len, CK_BYTE_PTR out, CK_ULONG_PTR out_len); CK_RV soft_keystore_hmac(soft_object_t *key_p, boolean_t sign, CK_BYTE_PTR in, CK_ULONG in_len, CK_BYTE_PTR out, CK_ULONG_PTR out_len); #ifdef __cplusplus } #endif #endif /* _SOFTKEYSTORE_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2018, Joyent, Inc. */ /* * Functions used for manipulating the keystore */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "softGlobal.h" #include "softObject.h" #include "softSession.h" #include "softKeystore.h" #include "softKeystoreUtil.h" #define MAXPATHLEN 1024 #define SUNW_PATH ".sunw" /* top level Sun directory */ #define KEYSTORE_PATH "pkcs11_softtoken" /* keystore directory */ #define PUB_OBJ_DIR "public" /* directory for public objects */ #define PRI_OBJ_DIR "private" /* directory for private objects */ #define DS_FILE "objstore_info" /* keystore description file */ #define TMP_DS_FILE "t_info" /* temp name for keystore desc. file */ #define OBJ_PREFIX "obj" /* prefix of the keystore object file names */ #define OBJ_PREFIX_LEN sizeof (OBJ_PREFIX) - 1 /* length of prefix */ #define TMP_OBJ_PREFIX "t_o" /* prefix of the temp object file names */ /* * KEYSTORE DESCRIPTION FILE: * * The following describes the content of the keystore description file * * The order AND data type of the fields are very important. * All the code in this file assume that they are in the order specified * below. If either order of the fields or their data type changed, * you must make sure the ALL the pre-define values are still valid * * 1) PKCS#11 release number. It's 2.20 in this release (uchar_t[32]) * 2) keystore version number: used for synchronizing when different * processes access the keystore at the same time. It is incremented * when there is a change to the keystore. (uint_32) * 3) monotonic-counter: last counter value for name of token object file. * used for assigning unique name to each token (uint_32) * 4) salt used for generating encryption key (uint_16) * 5) salt used for generating key used for doing HMAC (uint_16) * 6) Length of salt used for generating hashed pin (length of salt * is variable) * 7) Salt used for generating hashed pin. * 8) Hashed pin len (length of hashed pin could be variable, the offset of * where this value lives in the file is calculated at run time) * 9) Hashed pin * */ /* Keystore description file pre-defined values */ #define KS_PKCS11_VER "2.20" #define KS_PKCS11_OFFSET 0 #define KS_PKCS11_VER_SIZE 32 #define KS_VER_OFFSET (KS_PKCS11_OFFSET + KS_PKCS11_VER_SIZE) #define KS_VER_SIZE 4 /* size in bytes of keystore version value */ #define KS_COUNTER_OFFSET (KS_VER_OFFSET + KS_VER_SIZE) #define KS_COUNTER_SIZE 4 /* size in bytes of the monotonic counter */ #define KS_KEY_SALT_OFFSET (KS_COUNTER_OFFSET + KS_COUNTER_SIZE) #define KS_KEY_SALT_SIZE PBKD2_SALT_SIZE #define KS_HMAC_SALT_OFFSET (KS_KEY_SALT_OFFSET + KS_KEY_SALT_SIZE) #define KS_HMAC_SALT_SIZE PBKD2_SALT_SIZE /* Salt for hashed pin */ #define KS_HASHED_PIN_SALT_LEN_OFFSET (KS_HMAC_SALT_OFFSET + KS_HMAC_SALT_SIZE) #define KS_HASHED_PIN_SALT_LEN_SIZE 8 /* stores length of hashed pin salt */ #define KS_HASHED_PIN_SALT_OFFSET \ (KS_HASHED_PIN_SALT_LEN_OFFSET + KS_HASHED_PIN_SALT_LEN_SIZE) /* * hashed pin * * hashed_pin length offset will be calculated at run time since * there's the hashed pin salt size is variable. * * The offset will be calculated at run time by calling the * function calculate_hashed_pin_offset() */ static off_t ks_hashed_pinlen_offset = -1; #define KS_HASHED_PINLEN_SIZE 8 /* End of Keystore description file pre-defined values */ /* * Metadata for each object * * The order AND data type of all the fields is very important. * All the code in this file assume that they are in the order specified * below. If either order of the fields or their data type is changed, * you must make sure the following pre-define value is still valid * Each object will have the meta data at the beginning of the object file. * * 1) object_version: used by softtoken to see if the object * has been modified since it last reads it. (uint_32) * 2) iv: initialization vector for encrypted data in the object. This * value will be 0 for public objects. (uchar_t[16]) * 3) obj_hmac: keyed hash as verifier to detect private object * being tampered this value will be 0 for public objects (uchar_t[16]) */ /* Object metadata pre-defined values */ #define OBJ_VER_OFFSET 0 #define OBJ_VER_SIZE 4 /* size of object version in bytes */ #define OBJ_IV_OFFSET (OBJ_VER_OFFSET + OBJ_VER_SIZE) #define OBJ_IV_SIZE 16 #define OBJ_HMAC_OFFSET (OBJ_IV_OFFSET + OBJ_IV_SIZE) #define OBJ_HMAC_SIZE 16 /* MD5 HMAC keyed hash */ #define OBJ_DATA_OFFSET (OBJ_HMAC_OFFSET + OBJ_HMAC_SIZE) /* End of object metadata pre-defined values */ #define ALTERNATE_KEYSTORE_PATH "SOFTTOKEN_DIR" static soft_object_t *enc_key = NULL; static soft_object_t *hmac_key = NULL; static char keystore_path[MAXPATHLEN]; static boolean_t keystore_path_initialized = B_FALSE; static int desc_fd = 0; static char * get_keystore_path() { char *home = getenv("HOME"); char *alt = getenv(ALTERNATE_KEYSTORE_PATH); if (keystore_path_initialized) { return (keystore_path); } bzero(keystore_path, sizeof (keystore_path)); /* * If it isn't set or is set to the empty string use the * default location. We need to check for the empty string * because some users "unset" environment variables by giving * them no value, this isn't the same thing as removing it * from the environment. * * We don't want that to attempt to open /.sunw/pkcs11_sofftoken */ if ((alt != NULL) && (strcmp(alt, "") != 0)) { (void) snprintf(keystore_path, MAXPATHLEN, "%s/%s", alt, KEYSTORE_PATH); keystore_path_initialized = B_TRUE; } else if ((home != NULL) && (strcmp(home, "") != 0)) { /* alternate path not specified, try user's home dir */ (void) snprintf(keystore_path, MAXPATHLEN, "%s/%s/%s", home, SUNW_PATH, KEYSTORE_PATH); keystore_path_initialized = B_TRUE; } return (keystore_path); } static char * get_pub_obj_path(char *name) { bzero(name, sizeof (name)); (void) snprintf(name, MAXPATHLEN, "%s/%s", get_keystore_path(), PUB_OBJ_DIR); return (name); } static char * get_pri_obj_path(char *name) { bzero(name, sizeof (name)); (void) snprintf(name, MAXPATHLEN, "%s/%s", get_keystore_path(), PRI_OBJ_DIR); return (name); } static char * get_desc_file_path(char *name) { bzero(name, sizeof (name)); (void) snprintf(name, MAXPATHLEN, "%s/%s", get_keystore_path(), DS_FILE); return (name); } static char * get_tmp_desc_file_path(char *name) { bzero(name, sizeof (name)); (void) snprintf(name, MAXPATHLEN, "%s/%s", get_keystore_path(), TMP_DS_FILE); return (name); } /* * Calculates the offset for hashed_pin length and hashed pin * * Returns 0 if successful, -1 if there's any error. * * If successful, global variables "ks_hashed_pinlen_offset" will be set. * */ static int calculate_hashed_pin_offset(int fd) { uint64_t salt_length; if (lseek(fd, KS_HASHED_PIN_SALT_LEN_OFFSET, SEEK_SET) != KS_HASHED_PIN_SALT_LEN_OFFSET) { return (-1); } if (readn_nointr(fd, (char *)&salt_length, KS_HASHED_PIN_SALT_LEN_SIZE) != KS_HASHED_PIN_SALT_LEN_SIZE) { return (-1); } salt_length = SWAP64(salt_length); ks_hashed_pinlen_offset = KS_HASHED_PIN_SALT_LEN_OFFSET + KS_HASHED_PIN_SALT_LEN_SIZE + salt_length; return (0); } /* * acquire or release read/write lock on a specific file * * read_lock: true for read lock; false for write lock * set_lock: true to set a lock; false to release a lock */ static int lock_file(int fd, boolean_t read_lock, boolean_t set_lock) { flock_t lock_info; int r; lock_info.l_whence = SEEK_SET; lock_info.l_start = 0; lock_info.l_len = 0; /* l_len == 0 means until end of file */ if (read_lock) { lock_info.l_type = F_RDLCK; } else { lock_info.l_type = F_WRLCK; } if (set_lock) { while ((r = fcntl(fd, F_SETLKW, &lock_info)) == -1) { if (errno != EINTR) break; } if (r == -1) { return (-1); } } else { lock_info.l_type = F_UNLCK; while ((r = fcntl(fd, F_SETLKW, &lock_info)) == -1) { if (errno != EINTR) break; } if (r == -1) { return (-1); } } return (0); } int create_keystore() { int fd, buf; uint64_t hashed_pin_len, hashed_pin_salt_len, ulong_buf; uchar_t ver_buf[KS_PKCS11_VER_SIZE]; char pub_obj_path[MAXPATHLEN], pri_obj_path[MAXPATHLEN], ks_desc_file[MAXPATHLEN]; CK_BYTE salt[KS_KEY_SALT_SIZE]; char *hashed_pin = NULL, *hashed_pin_salt = NULL; char *alt; /* keystore doesn't exist, create keystore directory */ if (mkdir(get_keystore_path(), S_IRUSR|S_IWUSR|S_IXUSR) < 0) { if (errno == EEXIST) { return (0); } if (errno == EACCES) { return (-1); } /* can't create keystore directory */ if (errno == ENOENT) { /* part of the path doesn't exist */ char keystore[MAXPATHLEN]; /* * try to create $HOME/.sunw/pkcs11_softtoken if it * doesn't exist. If it is a alternate path provided * by the user, it should have existed. Will not * create for them. */ alt = getenv(ALTERNATE_KEYSTORE_PATH); if ((alt == NULL) || (strcmp(alt, "") == 0)) { char *home = getenv("HOME"); if (home == NULL || strcmp(home, "") == 0) { return (-1); } /* create $HOME/.sunw/pkcs11_softtoken */ (void) snprintf(keystore, sizeof (keystore), "%s/%s/%s", home, SUNW_PATH, KEYSTORE_PATH); if (mkdirp(keystore, S_IRUSR|S_IWUSR|S_IXUSR) < 0) { return (-1); } } else { return (-1); } } } /* create keystore description file */ fd = open_nointr(get_desc_file_path(ks_desc_file), O_RDWR|O_CREAT|O_EXCL|O_NONBLOCK, S_IRUSR|S_IWUSR); if (fd < 0) { if (errno == EEXIST) { return (0); } else { /* can't create keystore description file */ (void) rmdir(get_keystore_path()); return (-1); } } if (lock_file(fd, B_FALSE, B_TRUE) != 0) { (void) unlink(ks_desc_file); (void) close(fd); (void) rmdir(get_keystore_path()); return (-1); } if (mkdir(get_pub_obj_path(pub_obj_path), S_IRUSR|S_IWUSR|S_IXUSR) < 0) { /* can't create directory for public objects */ (void) lock_file(fd, B_FALSE, B_FALSE); (void) unlink(ks_desc_file); (void) close(fd); (void) rmdir(get_keystore_path()); return (-1); } if (mkdir(get_pri_obj_path(pri_obj_path), S_IRUSR|S_IWUSR|S_IXUSR) < 0) { /* can't create directory for private objects */ (void) lock_file(fd, B_FALSE, B_FALSE); (void) unlink(ks_desc_file); (void) close(fd); (void) rmdir(get_keystore_path()); (void) rmdir(pub_obj_path); return (-1); } /* write file format release number */ bzero(ver_buf, sizeof (ver_buf)); (void) strcpy((char *)ver_buf, KS_PKCS11_VER); if ((writen_nointr(fd, (char *)ver_buf, sizeof (ver_buf))) != sizeof (ver_buf)) { goto cleanup; } /* write version number, version = 0 since keystore just created */ buf = SWAP32(0); if (writen_nointr(fd, (void *)&buf, KS_VER_SIZE) != KS_VER_SIZE) { goto cleanup; } /* write monotonic-counter. Counter for keystore objects start at 1 */ buf = SWAP32(1); if (writen_nointr(fd, (void *)&buf, KS_COUNTER_SIZE) != KS_COUNTER_SIZE) { goto cleanup; } /* initial encryption key salt should be all NULL */ bzero(salt, sizeof (salt)); if (writen_nointr(fd, (void *)salt, KS_KEY_SALT_SIZE) != KS_KEY_SALT_SIZE) { goto cleanup; } /* initial HMAC key salt should also be all NULL */ if (writen_nointr(fd, (void *)salt, KS_HMAC_SALT_SIZE) != KS_HMAC_SALT_SIZE) { goto cleanup; } /* generate the hashed pin salt, and MD5 hashed pin of default pin */ if (soft_gen_hashed_pin((CK_CHAR_PTR)SOFT_DEFAULT_PIN, &hashed_pin, &hashed_pin_salt) < 0) { goto cleanup; } if ((hashed_pin_salt == NULL) || (hashed_pin == NULL)) { goto cleanup; } hashed_pin_salt_len = (uint64_t)strlen(hashed_pin_salt); hashed_pin_len = (uint64_t)strlen(hashed_pin); /* write hashed pin salt length */ ulong_buf = SWAP64(hashed_pin_salt_len); if (writen_nointr(fd, (void *)&ulong_buf, KS_HASHED_PIN_SALT_LEN_SIZE) != KS_HASHED_PIN_SALT_LEN_SIZE) { goto cleanup; } if (writen_nointr(fd, (void *)hashed_pin_salt, hashed_pin_salt_len) != hashed_pin_salt_len) { goto cleanup; } /* write MD5 hashed pin of the default pin */ ulong_buf = SWAP64(hashed_pin_len); if (writen_nointr(fd, (void *)&ulong_buf, KS_HASHED_PINLEN_SIZE) != KS_HASHED_PINLEN_SIZE) { goto cleanup; } if (writen_nointr(fd, (void *)hashed_pin, hashed_pin_len) != hashed_pin_len) { goto cleanup; } (void) lock_file(fd, B_FALSE, B_FALSE); (void) close(fd); freezero(hashed_pin_salt, hashed_pin_salt_len); return (0); cleanup: (void) lock_file(fd, B_FALSE, B_FALSE); (void) unlink(ks_desc_file); (void) close(fd); (void) rmdir(get_keystore_path()); (void) rmdir(pub_obj_path); (void) rmdir(pri_obj_path); return (-1); } /* * Determines if the file referenced by "fd" has the same * inode as the file referenced by "fname". * * The argument "same" contains the result of determining * if the inode is the same or not * * Returns 0 if there's no error. * Returns 1 if there's any error with opening the file. * * */ static int is_inode_same(int fd, char *fname, boolean_t *same) { struct stat fn_stat, fd_stat; if (fstat(fd, &fd_stat) != 0) { return (-1); } if (stat(fname, &fn_stat) != 0) { return (-1); } /* It's the same file if both st_ino and st_dev match */ if ((fd_stat.st_ino == fn_stat.st_ino) && (fd_stat.st_dev == fn_stat.st_dev)) { *same = B_TRUE; } else { *same = B_FALSE; } return (0); } static int acquire_file_lock(int *fd, char *fname, mode_t mode) { boolean_t read_lock = B_TRUE, same_inode; if ((mode == O_RDWR) || (mode == O_WRONLY)) { read_lock = B_FALSE; } if (lock_file(*fd, read_lock, B_TRUE) != 0) { return (-1); } /* * make sure another process did not modify the file * while we were trying to get the lock */ if (is_inode_same(*fd, fname, &same_inode) != 0) { (void) lock_file(*fd, B_TRUE, B_FALSE); /* unlock file */ return (-1); } while (!same_inode) { /* * need to unlock file, close, re-open the file, * and re-acquire the lock */ /* unlock file */ if (lock_file(*fd, B_TRUE, B_FALSE) != 0) { return (-1); } (void) close(*fd); /* re-open */ *fd = open_nointr(fname, mode|O_NONBLOCK); if (*fd < 0) { return (-1); } /* acquire lock again */ if (lock_file(*fd, read_lock, B_TRUE) != 0) { return (-1); } if (is_inode_same(*fd, fname, &same_inode) != 0) { (void) lock_file(*fd, B_TRUE, B_FALSE); /* unlock */ return (-1); } } return (0); } /* * Open the keystore description file in the specified mode. * If the keystore doesn't exist, the "do_create_keystore" * argument determines if the keystore should be created */ static int open_and_lock_keystore_desc(mode_t mode, boolean_t do_create_keystore, boolean_t lock_held) { int fd; char *fname, ks_desc_file[MAXPATHLEN]; /* open the keystore description file in requested mode */ fname = get_desc_file_path(ks_desc_file); fd = open_nointr(fname, mode|O_NONBLOCK); if (fd < 0) { if ((errno == ENOENT) && (do_create_keystore)) { if (create_keystore() < 0) { goto done; } fd = open_nointr(fname, mode|O_NONBLOCK); if (fd < 0) { goto done; } } else { goto done; } } if (lock_held) { /* already hold the lock */ return (fd); } if (acquire_file_lock(&fd, fname, mode) != 0) { if (fd > 0) { (void) close(fd); } return (-1); } done: return (fd); } /* * Set or remove read or write lock on keystore description file * * read_lock: true for read lock, false for write lock * set_lock: true for set a lock, false to remove a lock */ static int lock_desc_file(boolean_t read_lock, boolean_t set_lock) { char ks_desc_file[MAXPATHLEN]; if (set_lock) { int oflag; /* * make sure desc_fd is not already used. If used, it means * some other lock is already set on the file */ if (desc_fd > 0) { return (-1); } (void) get_desc_file_path(ks_desc_file); if (read_lock) { oflag = O_RDONLY; } else { oflag = O_WRONLY; } if ((desc_fd = open_and_lock_keystore_desc(oflag, B_FALSE, B_FALSE)) < 0) { return (-1); } } else { /* make sure we have a valid fd */ if (desc_fd <= 0) { return (-1); } if (lock_file(desc_fd, read_lock, B_FALSE) == 1) { return (-1); } (void) close(desc_fd); desc_fd = 0; } return (0); } static int open_and_lock_object_file(ks_obj_handle_t *ks_handle, int oflag, boolean_t lock_held) { char obj_fname[MAXPATHLEN]; int fd; if (ks_handle->public) { char pub_obj_path[MAXPATHLEN]; (void) snprintf(obj_fname, MAXPATHLEN, "%s/%s", get_pub_obj_path(pub_obj_path), ks_handle->name); } else { char pri_obj_path[MAXPATHLEN]; (void) snprintf(obj_fname, MAXPATHLEN, "%s/%s", get_pri_obj_path(pri_obj_path), ks_handle->name); } fd = open_nointr(obj_fname, oflag|O_NONBLOCK); if (fd < 0) { return (-1); } if (lock_held) { /* already hold the lock */ return (fd); } if (acquire_file_lock(&fd, obj_fname, oflag) != 0) { if (fd > 0) { (void) close(fd); } return (-1); } return (fd); } /* * Update file version number in a temporary file that's * a copy of the keystore description file. * The update is NOT made to the original keystore description * file. It makes the update in a tempoary file. * * Name of the temporary file is assumed to be provided, but * the file is assumed to not exist. * * return 0 if creating temp file is successful, returns -1 otherwise */ static int create_updated_keystore_version(int fd, char *tmp_fname) { int version, tmp_fd; char buf[BUFSIZ]; size_t nread; /* first, create the tempoary file */ tmp_fd = open_nointr(tmp_fname, O_WRONLY|O_CREAT|O_EXCL|O_NONBLOCK, S_IRUSR|S_IWUSR); if (tmp_fd < 0) { return (-1); } /* * copy everything from keystore version to temp file except * the keystore version. Keystore version is updated * */ /* pkcs11 version */ if (readn_nointr(fd, buf, KS_PKCS11_VER_SIZE) != KS_PKCS11_VER_SIZE) { goto cleanup; } if (writen_nointr(tmp_fd, buf, KS_PKCS11_VER_SIZE) != KS_PKCS11_VER_SIZE) { goto cleanup; } /* version number, it needs to be updated */ /* read the current version number */ if (readn_nointr(fd, &version, KS_VER_SIZE) != KS_VER_SIZE) { goto cleanup; } version = SWAP32(version); version++; version = SWAP32(version); /* write the updated value to the tmp file */ if (writen_nointr(tmp_fd, (void *)&version, KS_VER_SIZE) != KS_VER_SIZE) { goto cleanup; } /* read rest of information, nothing needs to be updated */ nread = readn_nointr(fd, buf, BUFSIZ); while (nread > 0) { if (writen_nointr(tmp_fd, buf, nread) != nread) { goto cleanup; } nread = readn_nointr(fd, buf, BUFSIZ); } (void) close(tmp_fd); return (0); /* no error */ cleanup: (void) close(tmp_fd); (void) remove(tmp_fname); return (-1); } static CK_RV get_all_objs_in_dir(DIR *dirp, ks_obj_handle_t *ks_handle, ks_obj_t **result_obj_list, boolean_t lock_held) { struct dirent *dp; ks_obj_t *obj; CK_RV rv; while ((dp = readdir(dirp)) != NULL) { if (strncmp(dp->d_name, OBJ_PREFIX, OBJ_PREFIX_LEN) != 0) continue; (void) strcpy((char *)ks_handle->name, dp->d_name); rv = soft_keystore_get_single_obj(ks_handle, &obj, lock_held); if (rv != CKR_OK) { return (rv); } if (obj != NULL) { if (*result_obj_list == NULL) { *result_obj_list = obj; } else { obj->next = *result_obj_list; *result_obj_list = obj; } } } return (CKR_OK); } /* * This function prepares the obj data for encryption by prepending * the FULL path of the file that will be used for storing * the object. Having full path of the file as part of * of the data for the object will prevent an attacker from * copying a "bad" object into the keystore undetected. * * This function will always allocate: * MAXPATHLEN + buf_len * amount of data. If the full path of the filename doesn't occupy * the whole MAXPATHLEN, the rest of the space will just be empty. * It is the caller's responsibility to free the buffer allocated here. * * The allocated buffer is returned in the variable "prepared_buf" * if there's no error. * * Returns 0 if there's no error, -1 otherwise. */ static int prepare_data_for_encrypt(char *obj_path, unsigned char *buf, CK_ULONG buf_len, unsigned char **prepared_buf, CK_ULONG *prepared_len) { *prepared_len = MAXPATHLEN + buf_len; *prepared_buf = malloc(*prepared_len); if (*prepared_buf == NULL) { return (-1); } /* * only zero out the space for the path name. I could zero out * the whole buffer, but that will be a waste of processing * cycle since the rest of the buffer will be 100% filled all * the time */ bzero(*prepared_buf, MAXPATHLEN); (void) memcpy(*prepared_buf, obj_path, strlen(obj_path)); (void) memcpy(*prepared_buf + MAXPATHLEN, buf, buf_len); return (0); } /* * retrieves the hashed pin from the keystore */ static CK_RV get_hashed_pin(int fd, char **hashed_pin) { uint64_t hashed_pin_size; if (ks_hashed_pinlen_offset == -1) { if (calculate_hashed_pin_offset(fd) != 0) { return (CKR_FUNCTION_FAILED); } } /* first, get size of the hashed pin */ if (lseek(fd, ks_hashed_pinlen_offset, SEEK_SET) != ks_hashed_pinlen_offset) { return (CKR_FUNCTION_FAILED); } if (readn_nointr(fd, (char *)&hashed_pin_size, KS_HASHED_PINLEN_SIZE) != KS_HASHED_PINLEN_SIZE) { return (CKR_FUNCTION_FAILED); } hashed_pin_size = SWAP64(hashed_pin_size); *hashed_pin = malloc(hashed_pin_size + 1); if (*hashed_pin == NULL) { return (CKR_HOST_MEMORY); } if ((readn_nointr(fd, *hashed_pin, hashed_pin_size)) != (ssize_t)hashed_pin_size) { freezero(*hashed_pin, hashed_pin_size + 1); *hashed_pin = NULL; return (CKR_FUNCTION_FAILED); } (*hashed_pin)[hashed_pin_size] = '\0'; return (CKR_OK); } /* * FUNCTION: soft_keystore_lock * * ARGUMENTS: * set_lock: TRUE to set readlock on the keystore object file, * FALSE to remove readlock on keystore object file. * * RETURN VALUE: * * 0: success * -1: failure * * DESCRIPTION: * * set or remove readlock on the keystore description file. */ int soft_keystore_readlock(boolean_t set_lock) { return (lock_desc_file(B_TRUE, set_lock)); } /* * FUNCTION: soft_keystore_writelock * * ARGUMENTS: * set_lock: TRUE to set writelock on the keystore description file * FALSE to remove write lock on keystore description file. * * RETURN VALUE: * * 0: no error * 1: some error occurred * * DESCRIPTION: * set/reset writelock on the keystore description file. */ int soft_keystore_writelock(boolean_t set_lock) { return (lock_desc_file(B_FALSE, set_lock)); } /* * * FUNCTION: soft_keystore_lock_object * * ARGUMENTS: * * ks_handle: handle of the keystore object file to be accessed. * read_lock: TRUE to set readlock on the keystore object file, * FALSE to set writelock on keystore object file. * * RETURN VALUE: * * If no error, file descriptor of locked file will be returned * -1: some error occurred * * DESCRIPTION: * * set readlock or writelock on the keystore object file. */ int soft_keystore_lock_object(ks_obj_handle_t *ks_handle, boolean_t read_lock) { int fd; int oflag; if (read_lock) { oflag = O_RDONLY; } else { oflag = O_WRONLY; } if ((fd = open_and_lock_object_file(ks_handle, oflag, B_FALSE)) < 0) { return (-1); } return (fd); } /* * FUNCTION: soft_keystore_unlock_object * * ARGUMENTS: * fd: file descriptor returned from soft_keystore_lock_object * * RETURN VALUE: * 0: no error * 1: some error occurred while getting the pin * * DESCRIPTION: * set/reset writelock on the keystore object file. */ int soft_keystore_unlock_object(int fd) { if (lock_file(fd, B_TRUE, B_FALSE) != 0) { return (1); } (void) close(fd); return (0); } /* * FUNCTION: soft_keystore_get_version * * ARGUMENTS: * version: pointer to caller allocated memory for storing * the version of the keystore. * lock_held: TRUE if the lock is held by caller. * * RETURN VALUE: * * 0: no error * -1: some error occurred while getting the version number * * DESCRIPTION: * get the version number of the keystore from keystore * description file. */ int soft_keystore_get_version(uint_t *version, boolean_t lock_held) { int fd, ret_val = 0; uint_t buf; if ((fd = open_and_lock_keystore_desc(O_RDONLY, B_FALSE, lock_held)) < 0) { return (-1); } if (lseek(fd, KS_VER_OFFSET, SEEK_SET) != KS_VER_OFFSET) { ret_val = -1; goto cleanup; } if (readn_nointr(fd, (char *)&buf, KS_VER_SIZE) != KS_VER_SIZE) { ret_val = -1; goto cleanup; } *version = SWAP32(buf); cleanup: if (!lock_held) { if (lock_file(fd, B_TRUE, B_FALSE) < 0) { ret_val = -1; } } (void) close(fd); return (ret_val); } /* * FUNCTION: soft_keystore_get_object_version * * ARGUMENTS: * * ks_handle: handle of the key store object to be accessed. * version: * pointer to caller allocated memory for storing * the version of the object. * lock_held: TRUE if the lock is held by caller. * * RETURN VALUE: * * 0: no error * -1: some error occurred while getting the pin * * DESCRIPTION: * get the version number of the specified token object. */ int soft_keystore_get_object_version(ks_obj_handle_t *ks_handle, uint_t *version, boolean_t lock_held) { int fd, ret_val = 0; uint_t tmp; if ((fd = open_and_lock_object_file(ks_handle, O_RDONLY, lock_held)) < 0) { return (-1); } /* * read version. Version is always first item in object file * so, no need to do lseek */ if (readn_nointr(fd, (char *)&tmp, OBJ_VER_SIZE) != OBJ_VER_SIZE) { ret_val = -1; goto cleanup; } *version = SWAP32(tmp); cleanup: if (!lock_held) { if (lock_file(fd, B_TRUE, B_FALSE) < 0) { ret_val = -1; } } (void) close(fd); return (ret_val); } /* * FUNCTION: soft_keystore_getpin * * ARGUMENTS: * hashed_pin: pointer to caller allocated memory * for storing the pin to be returned. * lock_held: TRUE if the lock is held by caller. * * RETURN VALUE: * * 0: no error * -1: some error occurred while getting the pin * * DESCRIPTION: * * Reads the MD5 hash from the keystore description * file and return it to the caller in the provided * buffer. If there is no PIN in the description file * because the file is just created, this function * will get a MD5 digest of the string "changeme", * store it in the file, and also return this * string to the caller. */ int soft_keystore_getpin(char **hashed_pin, boolean_t lock_held) { int fd, ret_val = -1; CK_RV rv; if ((fd = open_and_lock_keystore_desc(O_RDONLY, B_FALSE, lock_held)) < 0) { return (-1); } rv = get_hashed_pin(fd, hashed_pin); if (rv == CKR_OK) { ret_val = 0; } if (!lock_held) { if (lock_file(fd, B_TRUE, B_FALSE) < 0) { ret_val = -1; } } (void) close(fd); return (ret_val); } /* * Generate a 16-byte Initialization Vector (IV). */ CK_RV soft_gen_iv(CK_BYTE *iv) { return (pkcs11_get_nzero_urandom(iv, 16) < 0 ? CKR_DEVICE_ERROR : CKR_OK); } /* * This function reads all the data until the end of the file, and * put the data into the "buf" in argument. Memory for buf will * be allocated in this function. It is the caller's responsibility * to free it. The number of bytes read will be returned * in the argument "bytes_read" * * returns CKR_OK if no error. Other CKR error codes if there's an error */ static CK_RV read_obj_data(int old_fd, char **buf, ssize_t *bytes_read) { ssize_t nread, loop_count; char *buf1 = NULL; *buf = malloc(BUFSIZ); if (*buf == NULL) { return (CKR_HOST_MEMORY); } nread = readn_nointr(old_fd, *buf, BUFSIZ); if (nread < 0) { free(*buf); return (CKR_FUNCTION_FAILED); } loop_count = 1; while (nread == (loop_count * BUFSIZ)) { ssize_t nread_tmp; loop_count++; /* more than BUFSIZ of data */ buf1 = realloc(*buf, loop_count * BUFSIZ); if (buf1 == NULL) { free(*buf); return (CKR_HOST_MEMORY); } *buf = buf1; nread_tmp = readn_nointr(old_fd, *buf + ((loop_count - 1) * BUFSIZ), BUFSIZ); if (nread_tmp < 0) { free(*buf); return (CKR_FUNCTION_FAILED); } nread += nread_tmp; } *bytes_read = nread; return (CKR_OK); } /* * Re-encrypt an object using the provided new_enc_key. The new HMAC * is calculated using the new_hmac_key. The global static variables * enc_key, and hmac_key will be used for decrypting the original * object, and verifying its signature. * * The re-encrypted object will be stored in the file named * in the "new_obj_name" variable. The content of the "original" * file named in "orig_obj_name" is not disturbed. * * Returns 0 if there's no error, returns -1 otherwise. * */ static int reencrypt_obj(soft_object_t *new_enc_key, soft_object_t *new_hmac_key, char *orig_obj_name, char *new_obj_name) { int old_fd, new_fd, version, ret_val = -1; CK_BYTE iv[OBJ_IV_SIZE], old_iv[OBJ_IV_SIZE]; ssize_t nread; CK_ULONG decrypted_len, encrypted_len, hmac_len; CK_BYTE hmac[OBJ_HMAC_SIZE], *decrypted_buf = NULL, *buf = NULL; old_fd = open_nointr(orig_obj_name, O_RDONLY|O_NONBLOCK); if (old_fd < 0) { return (-1); } if (acquire_file_lock(&old_fd, orig_obj_name, O_RDONLY) != 0) { if (old_fd > 0) { (void) close(old_fd); } return (-1); } new_fd = open_nointr(new_obj_name, O_WRONLY|O_CREAT|O_EXCL|O_NONBLOCK, S_IRUSR|S_IWUSR); if (new_fd < 0) { (void) close(old_fd); return (-1); } if (lock_file(new_fd, B_FALSE, B_TRUE) != 0) { /* unlock old file */ (void) lock_file(old_fd, B_TRUE, B_FALSE); (void) close(old_fd); (void) close(new_fd); return (-1); } /* read version, increment, and write to tmp file */ if (readn_nointr(old_fd, (char *)&version, OBJ_VER_SIZE) != OBJ_VER_SIZE) { goto cleanup; } version = SWAP32(version); version++; version = SWAP32(version); if (writen_nointr(new_fd, (char *)&version, OBJ_VER_SIZE) != OBJ_VER_SIZE) { goto cleanup; } /* read old iv */ if (readn_nointr(old_fd, (char *)old_iv, OBJ_IV_SIZE) != OBJ_IV_SIZE) { goto cleanup; } /* generate new IV */ if (soft_gen_iv(iv) != CKR_OK) { goto cleanup; } if (writen_nointr(new_fd, (char *)iv, OBJ_IV_SIZE) != OBJ_IV_SIZE) { goto cleanup; } /* seek to the original encrypted data, and read all of them */ if (lseek(old_fd, OBJ_DATA_OFFSET, SEEK_SET) != OBJ_DATA_OFFSET) { goto cleanup; } if (read_obj_data(old_fd, (char **)&buf, &nread) != CKR_OK) { goto cleanup; } /* decrypt data using old key */ decrypted_len = 0; if (soft_keystore_crypt(enc_key, old_iv, B_FALSE, buf, nread, NULL, &decrypted_len) != CKR_OK) { freezero(buf, nread); goto cleanup; } decrypted_buf = malloc(decrypted_len); if (decrypted_buf == NULL) { freezero(buf, nread); goto cleanup; } if (soft_keystore_crypt(enc_key, old_iv, B_FALSE, buf, nread, decrypted_buf, &decrypted_len) != CKR_OK) { freezero(buf, nread); freezero(decrypted_buf, decrypted_len); } freezero(buf, nread); /* re-encrypt with new key */ encrypted_len = 0; if (soft_keystore_crypt(new_enc_key, iv, B_TRUE, decrypted_buf, decrypted_len, NULL, &encrypted_len) != CKR_OK) { freezero(decrypted_buf, decrypted_len); goto cleanup; } buf = malloc(encrypted_len); if (buf == NULL) { freezero(decrypted_buf, decrypted_len); goto cleanup; } if (soft_keystore_crypt(new_enc_key, iv, B_TRUE, decrypted_buf, decrypted_len, buf, &encrypted_len) != CKR_OK) { freezero(buf, encrypted_len); freezero(buf, decrypted_len); goto cleanup; } freezero(decrypted_buf, decrypted_len); /* calculate hmac on re-encrypted data using new hmac key */ hmac_len = OBJ_HMAC_SIZE; if (soft_keystore_hmac(new_hmac_key, B_TRUE, buf, encrypted_len, hmac, &hmac_len) != CKR_OK) { freezero(buf, encrypted_len); goto cleanup; } /* just for sanity check */ if (hmac_len != OBJ_HMAC_SIZE) { freezero(buf, encrypted_len); goto cleanup; } /* write new hmac */ if (writen_nointr(new_fd, (char *)hmac, OBJ_HMAC_SIZE) != OBJ_HMAC_SIZE) { freezero(buf, encrypted_len); goto cleanup; } /* write re-encrypted buffer to temp file */ if (writen_nointr(new_fd, (void *)buf, encrypted_len) != encrypted_len) { freezero(buf, encrypted_len); goto cleanup; } freezero(buf, encrypted_len); ret_val = 0; cleanup: /* unlock the files */ (void) lock_file(old_fd, B_TRUE, B_FALSE); (void) lock_file(new_fd, B_FALSE, B_FALSE); (void) close(old_fd); (void) close(new_fd); if (ret_val != 0) { (void) remove(new_obj_name); } return (ret_val); } /* * FUNCTION: soft_keystore_setpin * * ARGUMENTS: * newpin: new pin entered by the user. * lock_held: TRUE if the lock is held by caller. * * RETURN VALUE: * 0: no error * -1: failure * * DESCRIPTION: * * This function does the following: * * 1) Generates crypted value of newpin and store it * in keystore description file. * 2) Dervies the new encryption key from the newpin. This key * will be used to re-encrypt the private token objects. * 3) Re-encrypt all of this user's existing private token * objects (if any). * 4) Increments the keystore version number. */ int soft_keystore_setpin(uchar_t *oldpin, uchar_t *newpin, boolean_t lock_held) { int fd, tmp_ks_fd, version, ret_val = -1; soft_object_t *new_crypt_key = NULL, *new_hmac_key = NULL; char filebuf[BUFSIZ]; DIR *pri_dirp; struct dirent *pri_ent; char pri_obj_path[MAXPATHLEN], ks_desc_file[MAXPATHLEN], tmp_ks_desc_name[MAXPATHLEN]; typedef struct priobjs { char orig_name[MAXPATHLEN]; char tmp_name[MAXPATHLEN]; struct priobjs *next; } priobjs_t; priobjs_t *pri_objs = NULL, *tmp; CK_BYTE *crypt_salt = NULL, *hmac_salt = NULL; boolean_t pin_never_set = B_FALSE, user_logged_in; char *new_hashed_pin = NULL; uint64_t hashed_pin_salt_length, new_hashed_pin_len, swaped_val; char *hashed_pin_salt = NULL; priobjs_t *obj; if ((enc_key == NULL) || (enc_key->magic_marker != SOFTTOKEN_OBJECT_MAGIC)) { user_logged_in = B_FALSE; } else { user_logged_in = B_TRUE; } if ((fd = open_and_lock_keystore_desc(O_RDWR, B_TRUE, lock_held)) < 0) { return (-1); } (void) get_desc_file_path(ks_desc_file); (void) get_tmp_desc_file_path(tmp_ks_desc_name); /* * create a tempoary file for the keystore description * file for updating version and counter information */ tmp_ks_fd = open_nointr(tmp_ks_desc_name, O_RDWR|O_CREAT|O_EXCL|O_NONBLOCK, S_IRUSR|S_IWUSR); if (tmp_ks_fd < 0) { (void) close(fd); return (-1); } /* read and write PKCS version to temp file */ if (readn_nointr(fd, filebuf, KS_PKCS11_VER_SIZE) != KS_PKCS11_VER_SIZE) { goto cleanup; } if (writen_nointr(tmp_ks_fd, filebuf, KS_PKCS11_VER_SIZE) != KS_PKCS11_VER_SIZE) { goto cleanup; } /* get version number, and write updated number to temp file */ if (readn_nointr(fd, &version, KS_VER_SIZE) != KS_VER_SIZE) { goto cleanup; } version = SWAP32(version); version++; version = SWAP32(version); if (writen_nointr(tmp_ks_fd, (void *)&version, KS_VER_SIZE) != KS_VER_SIZE) { goto cleanup; } /* read and write counter, no modification necessary */ if (readn_nointr(fd, filebuf, KS_COUNTER_SIZE) != KS_COUNTER_SIZE) { goto cleanup; } if (writen_nointr(tmp_ks_fd, filebuf, KS_COUNTER_SIZE) != KS_COUNTER_SIZE) { goto cleanup; } /* read old encryption salt */ crypt_salt = malloc(KS_KEY_SALT_SIZE); if (crypt_salt == NULL) { goto cleanup; } if (readn_nointr(fd, (char *)crypt_salt, KS_KEY_SALT_SIZE) != KS_KEY_SALT_SIZE) { goto cleanup; } /* read old hmac salt */ hmac_salt = malloc(KS_HMAC_SALT_SIZE); if (hmac_salt == NULL) { goto cleanup; } if (readn_nointr(fd, (char *)hmac_salt, KS_HMAC_SALT_SIZE) != KS_HMAC_SALT_SIZE) { goto cleanup; } /* just create some empty bytes */ bzero(filebuf, sizeof (filebuf)); if (memcmp(crypt_salt, filebuf, KS_KEY_SALT_SIZE) == 0) { /* PIN as never been set */ CK_BYTE *new_crypt_salt = NULL, *new_hmac_salt = NULL; pin_never_set = B_TRUE; if (soft_gen_crypt_key(newpin, &new_crypt_key, &new_crypt_salt) != CKR_OK) { goto cleanup; } if (writen_nointr(tmp_ks_fd, (void *)new_crypt_salt, KS_KEY_SALT_SIZE) != KS_KEY_SALT_SIZE) { freezero(new_crypt_salt, KS_KEY_SALT_SIZE); (void) soft_cleanup_object(new_crypt_key); goto cleanup; } freezero(new_crypt_salt, KS_KEY_SALT_SIZE); if (soft_gen_hmac_key(newpin, &new_hmac_key, &new_hmac_salt) != CKR_OK) { (void) soft_cleanup_object(new_crypt_key); goto cleanup; } if (writen_nointr(tmp_ks_fd, (void *)new_hmac_salt, KS_HMAC_SALT_SIZE) != KS_HMAC_SALT_SIZE) { freezero(new_hmac_salt, KS_HMAC_SALT_SIZE); goto cleanup3; } freezero(new_hmac_salt, KS_HMAC_SALT_SIZE); } else { if (soft_gen_crypt_key(newpin, &new_crypt_key, (CK_BYTE **)&crypt_salt) != CKR_OK) { goto cleanup; } /* no change to the encryption salt */ if (writen_nointr(tmp_ks_fd, (void *)crypt_salt, KS_KEY_SALT_SIZE) != KS_KEY_SALT_SIZE) { (void) soft_cleanup_object(new_crypt_key); goto cleanup; } if (soft_gen_hmac_key(newpin, &new_hmac_key, (CK_BYTE **)&hmac_salt) != CKR_OK) { (void) soft_cleanup_object(new_crypt_key); goto cleanup; } /* no change to the hmac salt */ if (writen_nointr(tmp_ks_fd, (void *)hmac_salt, KS_HMAC_SALT_SIZE) != KS_HMAC_SALT_SIZE) { goto cleanup3; } } /* * read hashed pin salt, and write to updated keystore description * file unmodified. */ if (readn_nointr(fd, (char *)&hashed_pin_salt_length, KS_HASHED_PIN_SALT_LEN_SIZE) != KS_HASHED_PIN_SALT_LEN_SIZE) { goto cleanup3; } if (writen_nointr(tmp_ks_fd, (void *)&hashed_pin_salt_length, KS_HASHED_PIN_SALT_LEN_SIZE) != KS_HASHED_PIN_SALT_LEN_SIZE) { goto cleanup3; } hashed_pin_salt_length = SWAP64(hashed_pin_salt_length); hashed_pin_salt = malloc(hashed_pin_salt_length + 1); if (hashed_pin_salt == NULL) { goto cleanup3; } if ((readn_nointr(fd, hashed_pin_salt, hashed_pin_salt_length)) != (ssize_t)hashed_pin_salt_length) { freezero(hashed_pin_salt, hashed_pin_salt_length + 1); goto cleanup3; } if ((writen_nointr(tmp_ks_fd, hashed_pin_salt, hashed_pin_salt_length)) != (ssize_t)hashed_pin_salt_length) { freezero(hashed_pin_salt, hashed_pin_salt_length + 1); goto cleanup3; } hashed_pin_salt[hashed_pin_salt_length] = '\0'; /* old hashed pin length and value can be ignored, generate new one */ if (soft_gen_hashed_pin(newpin, &new_hashed_pin, &hashed_pin_salt) < 0) { freezero(hashed_pin_salt, hashed_pin_salt_length + 1); goto cleanup3; } freezero(hashed_pin_salt, hashed_pin_salt_length + 1); if (new_hashed_pin == NULL) { goto cleanup3; } new_hashed_pin_len = strlen(new_hashed_pin); /* write new hashed pin length to file */ swaped_val = SWAP64(new_hashed_pin_len); if (writen_nointr(tmp_ks_fd, (void *)&swaped_val, KS_HASHED_PINLEN_SIZE) != KS_HASHED_PINLEN_SIZE) { goto cleanup3; } if (writen_nointr(tmp_ks_fd, (void *)new_hashed_pin, new_hashed_pin_len) != (ssize_t)new_hashed_pin_len) { goto cleanup3; } if (pin_never_set) { /* there was no private object, no need to re-encrypt them */ goto rename_desc_file; } /* re-encrypt all the private objects */ pri_dirp = opendir(get_pri_obj_path(pri_obj_path)); if (pri_dirp == NULL) { /* * this directory should exist, even if it doesn't contain * any objects. Don't want to update the pin if the * keystore is somehow messed up. */ goto cleanup3; } /* if user did not login, need to set the old pin */ if (!user_logged_in) { if (soft_keystore_authpin(oldpin) != 0) { goto cleanup3; } } while ((pri_ent = readdir(pri_dirp)) != NULL) { if ((strcmp(pri_ent->d_name, ".") == 0) || (strcmp(pri_ent->d_name, "..") == 0) || (strncmp(pri_ent->d_name, TMP_OBJ_PREFIX, strlen(TMP_OBJ_PREFIX)) == 0)) { continue; } obj = malloc(sizeof (priobjs_t)); if (obj == NULL) { goto cleanup2; } (void) snprintf(obj->orig_name, MAXPATHLEN, "%s/%s", pri_obj_path, pri_ent->d_name); (void) snprintf(obj->tmp_name, MAXPATHLEN, "%s/%s%s", pri_obj_path, TMP_OBJ_PREFIX, (pri_ent->d_name) + OBJ_PREFIX_LEN); if (reencrypt_obj(new_crypt_key, new_hmac_key, obj->orig_name, obj->tmp_name) != 0) { free(obj); goto cleanup2; } /* insert into list of file to be renamed */ if (pri_objs == NULL) { obj->next = NULL; pri_objs = obj; } else { obj->next = pri_objs; pri_objs = obj; } } /* rename all the private objects */ tmp = pri_objs; while (tmp) { (void) rename(tmp->tmp_name, tmp->orig_name); tmp = tmp->next; } rename_desc_file: /* destroy the old encryption key, and hmac key */ if ((!pin_never_set) && (user_logged_in)) { (void) soft_cleanup_object(enc_key); (void) soft_cleanup_object(hmac_key); } if (user_logged_in) { enc_key = new_crypt_key; hmac_key = new_hmac_key; } (void) rename(tmp_ks_desc_name, ks_desc_file); ret_val = 0; cleanup2: if (pri_objs != NULL) { priobjs_t *p = pri_objs; while (p) { tmp = p->next; free(p); p = tmp; } } if (!pin_never_set) { (void) closedir(pri_dirp); } if ((!user_logged_in) && (!pin_never_set)) { (void) soft_cleanup_object(enc_key); (void) soft_cleanup_object(hmac_key); enc_key = NULL; hmac_key = NULL; } cleanup3: if ((ret_val != 0) || (!user_logged_in)) { (void) soft_cleanup_object(new_crypt_key); (void) soft_cleanup_object(new_hmac_key); } cleanup: if (!lock_held) { if (lock_file(fd, B_FALSE, B_FALSE) < 0) { ret_val = 1; } } freezero(crypt_salt, KS_KEY_SALT_SIZE); freezero(hmac_salt, KS_HMAC_SALT_SIZE); (void) close(fd); (void) close(tmp_ks_fd); if (ret_val != 0) { (void) remove(tmp_ks_desc_name); } return (ret_val); } /* * FUNCTION: soft_keystore_authpin * * ARGUMENTS: * pin: pin specified by the user for logging into * the keystore. * * RETURN VALUE: * 0: if no error * -1: if there is any error * * DESCRIPTION: * * This function takes the pin specified in the argument * and generates an encryption key based on the pin. * The generated encryption key will be used for * all future encryption and decryption for private * objects. Before this function is called, none * of the keystore related interfaces is able * to decrypt/encrypt any private object. */ int soft_keystore_authpin(uchar_t *pin) { int fd; int ret_val = -1; CK_BYTE *crypt_salt = NULL, *hmac_salt; /* get the salt from the keystore description file */ if ((fd = open_and_lock_keystore_desc(O_RDONLY, B_FALSE, B_FALSE)) < 0) { return (-1); } crypt_salt = malloc(KS_KEY_SALT_SIZE); if (crypt_salt == NULL) { goto cleanup; } if (lseek(fd, KS_KEY_SALT_OFFSET, SEEK_SET) != KS_KEY_SALT_OFFSET) { goto cleanup; } if (readn_nointr(fd, (char *)crypt_salt, KS_KEY_SALT_SIZE) != KS_KEY_SALT_SIZE) { goto cleanup; } if (soft_gen_crypt_key(pin, &enc_key, (CK_BYTE **)&crypt_salt) != CKR_OK) { goto cleanup; } hmac_salt = malloc(KS_HMAC_SALT_SIZE); if (hmac_salt == NULL) { goto cleanup; } if (lseek(fd, KS_HMAC_SALT_OFFSET, SEEK_SET) != KS_HMAC_SALT_OFFSET) { goto cleanup; } if (readn_nointr(fd, (char *)hmac_salt, KS_HMAC_SALT_SIZE) != KS_HMAC_SALT_SIZE) { goto cleanup; } if (soft_gen_hmac_key(pin, &hmac_key, (CK_BYTE **)&hmac_salt) != CKR_OK) { goto cleanup; } ret_val = 0; cleanup: /* unlock the file */ (void) lock_file(fd, B_TRUE, B_FALSE); (void) close(fd); freezero(crypt_salt, KS_KEY_SALT_SIZE); freezero(hmac_salt, KS_HMAC_SALT_SIZE); return (ret_val); } /* * FUNCTION: soft_keystore_get_objs * * ARGUMENTS: * * search_type: Specify type of objects to return. * lock_held: TRUE if the lock is held by caller. * * * RETURN VALUE: * * NULL: if there are no object in the database. * * Otherwise, linked list of objects as requested * in search type. * * The linked list returned will need to be freed * by the caller. * * DESCRIPTION: * * Returns objects as requested. * * If private objects is requested, and the caller * has not previously passed in the pin or if the pin * passed in is wrong, private objects will not * be returned. * * The buffers returned for private objects are already * decrypted. */ CK_RV soft_keystore_get_objs(ks_search_type_t search_type, ks_obj_t **result_obj_list, boolean_t lock_held) { DIR *dirp; ks_obj_handle_t ks_handle; CK_RV rv; ks_obj_t *tmp; int ks_fd; *result_obj_list = NULL; /* * lock the keystore description file in "read" mode so that * objects won't get added/deleted/modified while we are * doing the search */ if ((ks_fd = open_and_lock_keystore_desc(O_RDONLY, B_FALSE, B_FALSE)) < 0) { return (CKR_FUNCTION_FAILED); } if ((search_type == ALL_TOKENOBJS) || (search_type == PUB_TOKENOBJS)) { char pub_obj_path[MAXPATHLEN]; ks_handle.public = B_TRUE; if ((dirp = opendir(get_pub_obj_path(pub_obj_path))) == NULL) { (void) lock_file(ks_fd, B_TRUE, B_FALSE); (void) close(ks_fd); return (CKR_FUNCTION_FAILED); } rv = get_all_objs_in_dir(dirp, &ks_handle, result_obj_list, lock_held); if (rv != CKR_OK) { (void) closedir(dirp); goto cleanup; } (void) closedir(dirp); } if ((search_type == ALL_TOKENOBJS) || (search_type == PRI_TOKENOBJS)) { char pri_obj_path[MAXPATHLEN]; if ((enc_key == NULL) || (enc_key->magic_marker != SOFTTOKEN_OBJECT_MAGIC)) { /* has not login - no need to go any further */ (void) lock_file(ks_fd, B_TRUE, B_FALSE); (void) close(ks_fd); return (CKR_OK); } ks_handle.public = B_FALSE; if ((dirp = opendir(get_pri_obj_path(pri_obj_path))) == NULL) { (void) lock_file(ks_fd, B_TRUE, B_FALSE); (void) close(ks_fd); return (CKR_OK); } rv = get_all_objs_in_dir(dirp, &ks_handle, result_obj_list, lock_held); if (rv != CKR_OK) { (void) closedir(dirp); goto cleanup; } (void) closedir(dirp); } /* close the keystore description file */ (void) lock_file(ks_fd, B_TRUE, B_FALSE); (void) close(ks_fd); return (CKR_OK); cleanup: /* close the keystore description file */ (void) lock_file(ks_fd, B_TRUE, B_FALSE); (void) close(ks_fd); /* free all the objects found before hitting the error */ tmp = *result_obj_list; while (tmp) { *result_obj_list = tmp->next; freezero(tmp->buf, tmp->size); free(tmp); tmp = *result_obj_list; } *result_obj_list = NULL; return (rv); } /* * FUNCTION: soft_keystore_get_single_obj * * ARGUMENTS: * ks_handle: handle of the key store object to be accessed * lock_held: TRUE if the lock is held by caller. * * RETURN VALUE: * * NULL: if handle doesn't match any object * * Otherwise, the object is returned in * the same structure used in soft_keystore_get_objs(). * The structure need to be freed by the caller. * * DESCRIPTION: * * Retrieves the object specified by the object * handle to the caller. * * If a private object is requested, and the caller * has not previously passed in the pin or if the pin * passed in is wrong, the requested private object will not * be returned. * * The buffer returned for the requested private object * is already decrypted. */ CK_RV soft_keystore_get_single_obj(ks_obj_handle_t *ks_handle, ks_obj_t **return_obj, boolean_t lock_held) { ks_obj_t *obj; uchar_t iv[OBJ_IV_SIZE], obj_hmac[OBJ_HMAC_SIZE]; uchar_t *buf, *decrypted_buf; int fd; ssize_t nread; CK_RV rv = CKR_FUNCTION_FAILED; if (!(ks_handle->public)) { if ((enc_key == NULL) || (enc_key->magic_marker != SOFTTOKEN_OBJECT_MAGIC)) { return (CKR_FUNCTION_FAILED); } } if ((fd = open_and_lock_object_file(ks_handle, O_RDONLY, lock_held)) < 0) { return (CKR_FUNCTION_FAILED); } obj = malloc(sizeof (ks_obj_t)); if (obj == NULL) { return (CKR_HOST_MEMORY); } obj->next = NULL; (void) strcpy((char *)((obj->ks_handle).name), (char *)ks_handle->name); (obj->ks_handle).public = ks_handle->public; /* 1st get the version */ if (readn_nointr(fd, &(obj->obj_version), OBJ_VER_SIZE) != OBJ_VER_SIZE) { goto cleanup; } obj->obj_version = SWAP32(obj->obj_version); /* Then, read the IV */ if (readn_nointr(fd, iv, OBJ_IV_SIZE) != OBJ_IV_SIZE) { goto cleanup; } /* Then, read the HMAC */ if (readn_nointr(fd, obj_hmac, OBJ_HMAC_SIZE) != OBJ_HMAC_SIZE) { goto cleanup; } /* read the object */ rv = read_obj_data(fd, (char **)&buf, &nread); if (rv != CKR_OK) { goto cleanup; } if (ks_handle->public) { obj->size = nread; obj->buf = buf; *return_obj = obj; } else { CK_ULONG out_len = 0, hmac_size; /* verify HMAC of the object, make sure it matches */ hmac_size = OBJ_HMAC_SIZE; if (soft_keystore_hmac(hmac_key, B_FALSE, buf, nread, obj_hmac, &hmac_size) != CKR_OK) { freezero(buf, nread); rv = CKR_FUNCTION_FAILED; goto cleanup; } /* decrypt object */ if (soft_keystore_crypt(enc_key, iv, B_FALSE, buf, nread, NULL, &out_len) != CKR_OK) { freezero(buf, nread); rv = CKR_FUNCTION_FAILED; goto cleanup; } decrypted_buf = malloc(sizeof (uchar_t) * out_len); if (decrypted_buf == NULL) { freezero(buf, nread); rv = CKR_HOST_MEMORY; goto cleanup; } if (soft_keystore_crypt(enc_key, iv, B_FALSE, buf, nread, decrypted_buf, &out_len) != CKR_OK) { freezero(buf, nread); freezero(decrypted_buf, out_len); rv = CKR_FUNCTION_FAILED; goto cleanup; } obj->size = out_len - MAXPATHLEN; /* * decrypted buf here actually contains full path name of * object plus the actual data. so, need to skip the * full pathname. * See prepare_data_for_encrypt() function in the file * to understand how and why the pathname is added. */ obj->buf = malloc(sizeof (uchar_t) * (out_len - MAXPATHLEN)); if (obj->buf == NULL) { freezero(buf, nread); freezero(decrypted_buf, out_len); rv = CKR_HOST_MEMORY; goto cleanup; } (void) memcpy(obj->buf, decrypted_buf + MAXPATHLEN, obj->size); freezero(buf, nread); freezero(decrypted_buf, out_len); *return_obj = obj; } cleanup: if (rv != CKR_OK) { free(obj); } /* unlock the file after reading */ if (!lock_held) { (void) lock_file(fd, B_TRUE, B_FALSE); } (void) close(fd); return (rv); } /* * FUNCTION: soft_keystore_put_new_obj * * ARGUMENTS: * buf: buffer containing un-encrypted data * to be stored in keystore. * len: length of data * public: TRUE if it is a public object, * FALSE if it is private obj * lock_held: TRUE if the lock is held by caller. * keyhandle: pointer to object handle to * receive keyhandle for new object * * RETURN VALUE: * 0: object successfully stored in file * -1: some error occurred, object is not stored in file. * * DESCRIPTION: * This API is used to write a newly created token object * to keystore. * * This function does the following: * * 1) Creates a token object file based on "public" parameter. * 2) Generates a new IV and stores it in obj_meta_data_t if it is * private object. * 3) Set object version number to 1. * 4) If it is a private object, it will be encrypted before * being written to the newly created keystore token object * file. * 5) Calculates the obj_chksum in obj_meta_data_t. * 6) Calculates the pin_chksum in obj_meta_data_t. * 7) Increments the keystore version number. */ int soft_keystore_put_new_obj(uchar_t *buf, size_t len, boolean_t public, boolean_t lock_held, ks_obj_handle_t *keyhandle) { int fd, tmp_ks_fd, obj_fd; unsigned int counter, version; uchar_t obj_hmac[OBJ_HMAC_SIZE]; CK_BYTE iv[OBJ_IV_SIZE]; char obj_name[MAXPATHLEN], tmp_ks_desc_name[MAXPATHLEN]; char filebuf[BUFSIZ]; char pub_obj_path[MAXPATHLEN], pri_obj_path[MAXPATHLEN], ks_desc_file[MAXPATHLEN]; CK_ULONG hmac_size; ssize_t nread; if (keyhandle == NULL) { return (-1); } /* if it is private object, make sure we have the key */ if (!public) { if ((enc_key == NULL) || (enc_key->magic_marker != SOFTTOKEN_OBJECT_MAGIC)) { return (-1); } } /* open keystore, and set write lock */ if ((fd = open_and_lock_keystore_desc(O_RDWR, B_FALSE, lock_held)) < 0) { return (-1); } (void) get_desc_file_path(ks_desc_file); (void) get_tmp_desc_file_path(tmp_ks_desc_name); /* * create a tempoary file for the keystore description * file for updating version and counter information */ tmp_ks_fd = open_nointr(tmp_ks_desc_name, O_RDWR|O_CREAT|O_EXCL|O_NONBLOCK, S_IRUSR|S_IWUSR); if (tmp_ks_fd < 0) { (void) close(fd); return (-1); } /* read and write pkcs11 version */ if (readn_nointr(fd, filebuf, KS_PKCS11_VER_SIZE) != KS_PKCS11_VER_SIZE) { goto cleanup; } if (writen_nointr(tmp_ks_fd, filebuf, KS_PKCS11_VER_SIZE) != KS_PKCS11_VER_SIZE) { goto cleanup; } /* get version number, and write updated number to temp file */ if (readn_nointr(fd, &version, KS_VER_SIZE) != KS_VER_SIZE) { goto cleanup; } version = SWAP32(version); version++; version = SWAP32(version); if (writen_nointr(tmp_ks_fd, (void *)&version, KS_VER_SIZE) != KS_VER_SIZE) { goto cleanup; } /* get object count value */ if (readn_nointr(fd, &counter, KS_COUNTER_SIZE) != KS_COUNTER_SIZE) { goto cleanup; } counter = SWAP32(counter); bzero(obj_name, sizeof (obj_name)); if (public) { (void) snprintf(obj_name, MAXPATHLEN, "%s/%s%d", get_pub_obj_path(pub_obj_path), OBJ_PREFIX, counter); } else { (void) snprintf(obj_name, MAXPATHLEN, "%s/%s%d", get_pri_obj_path(pri_obj_path), OBJ_PREFIX, counter); } /* create object file */ obj_fd = open_nointr(obj_name, O_WRONLY|O_CREAT|O_EXCL|O_NONBLOCK, S_IRUSR|S_IWUSR); if (obj_fd < 0) { /* can't create object file */ goto cleanup; } /* lock object file for writing */ if (lock_file(obj_fd, B_FALSE, B_TRUE) != 0) { (void) close(obj_fd); goto cleanup2; } /* write object meta data */ version = SWAP32(1); if (writen_nointr(obj_fd, (void *)&version, sizeof (version)) != sizeof (version)) { goto cleanup2; } if (public) { bzero(iv, sizeof (iv)); } else { /* generate an IV */ if (soft_gen_iv(iv) != CKR_OK) { goto cleanup2; } } if (writen_nointr(obj_fd, (void *)iv, sizeof (iv)) != sizeof (iv)) { goto cleanup2; } if (public) { bzero(obj_hmac, sizeof (obj_hmac)); if (writen_nointr(obj_fd, (void *)obj_hmac, sizeof (obj_hmac)) != sizeof (obj_hmac)) { goto cleanup2; } if (writen_nointr(obj_fd, (char *)buf, len) != len) { goto cleanup2; } } else { uchar_t *encrypted_buf, *prepared_buf; CK_ULONG out_len = 0, prepared_len; if (prepare_data_for_encrypt(obj_name, buf, len, &prepared_buf, &prepared_len) != 0) { goto cleanup2; } if (soft_keystore_crypt(enc_key, iv, B_TRUE, prepared_buf, prepared_len, NULL, &out_len) != CKR_OK) { freezero(prepared_buf, prepared_len); goto cleanup2; } encrypted_buf = malloc(out_len * sizeof (char)); if (encrypted_buf == NULL) { freezero(prepared_buf, prepared_len); goto cleanup2; } if (soft_keystore_crypt(enc_key, iv, B_TRUE, prepared_buf, prepared_len, encrypted_buf, &out_len) != CKR_OK) { freezero(encrypted_buf, out_len); freezero(prepared_buf, prepared_len); goto cleanup2; } freezero(prepared_buf, prepared_len); /* calculate HMAC of encrypted object */ hmac_size = OBJ_HMAC_SIZE; if (soft_keystore_hmac(hmac_key, B_TRUE, encrypted_buf, out_len, obj_hmac, &hmac_size) != CKR_OK) { freezero(encrypted_buf, out_len); goto cleanup2; } if (hmac_size != OBJ_HMAC_SIZE) { freezero(encrypted_buf, out_len); goto cleanup2; } /* write hmac */ if (writen_nointr(obj_fd, (void *)obj_hmac, sizeof (obj_hmac)) != sizeof (obj_hmac)) { freezero(encrypted_buf, out_len); goto cleanup2; } /* write encrypted object */ if (writen_nointr(obj_fd, (void *)encrypted_buf, out_len) != out_len) { freezero(encrypted_buf, out_len); goto cleanup2; } freezero(encrypted_buf, out_len); } (void) close(obj_fd); (void) snprintf((char *)keyhandle->name, sizeof (keyhandle->name), "obj%d", counter); keyhandle->public = public; /* * store new counter to temp keystore description file. */ counter++; counter = SWAP32(counter); if (writen_nointr(tmp_ks_fd, (void *)&counter, sizeof (counter)) != sizeof (counter)) { goto cleanup2; } /* read rest of keystore description file and store into temp file */ nread = readn_nointr(fd, filebuf, sizeof (filebuf)); while (nread > 0) { if (writen_nointr(tmp_ks_fd, filebuf, nread) != nread) { goto cleanup2; } nread = readn_nointr(fd, filebuf, sizeof (filebuf)); } (void) close(tmp_ks_fd); (void) rename(tmp_ks_desc_name, ks_desc_file); if (!lock_held) { /* release lock on description file */ if (lock_file(fd, B_FALSE, B_FALSE) != 0) { (void) close(fd); return (-1); } } (void) close(fd); explicit_bzero(obj_hmac, sizeof (obj_hmac)); explicit_bzero(iv, sizeof (iv)); return (0); cleanup2: /* remove object file. No need to remove lock first */ (void) unlink(obj_name); cleanup: (void) close(tmp_ks_fd); (void) remove(tmp_ks_desc_name); if (!lock_held) { /* release lock on description file */ (void) lock_file(fd, B_FALSE, B_FALSE); } (void) close(fd); explicit_bzero(obj_hmac, sizeof (obj_hmac)); explicit_bzero(iv, sizeof (iv)); return (-1); } /* * FUNCTION: soft_keystore_modify_obj * * ARGUMENTS: * ks_handle: handle of the key store object to be modified * buf: buffer containing un-encrypted data * to be modified in keystore. * len: length of data * lock_held: TRUE if the lock is held by caller. * * RETURN VALUE: * -1: if any error occurred. * Otherwise, 0 is returned. * * DESCRIPTION: * * This API is used to write a modified token object back * to keystore. This function will do the following: * * 1) If it is a private object, it will be encrypted before * being written to the corresponding keystore token * object file. * 2) Record incremented object version number. * 3) Record incremented keystore version number. */ int soft_keystore_modify_obj(ks_obj_handle_t *ks_handle, uchar_t *buf, size_t len, boolean_t lock_held) { int fd, ks_fd, tmp_fd, version; char orig_name[MAXPATHLEN], tmp_name[MAXPATHLEN], tmp_ks_name[MAXPATHLEN]; uchar_t iv[OBJ_IV_SIZE], obj_hmac[OBJ_HMAC_SIZE]; char pub_obj_path[MAXPATHLEN], pri_obj_path[MAXPATHLEN], ks_desc_file[MAXPATHLEN]; CK_ULONG hmac_size; /* if it is private object, make sure we have the key */ if (!(ks_handle->public)) { if ((enc_key == NULL) || (enc_key->magic_marker != SOFTTOKEN_OBJECT_MAGIC)) { return (-1); } } /* open and lock keystore description file */ if ((ks_fd = open_and_lock_keystore_desc(O_RDWR, B_FALSE, B_FALSE)) < 0) { return (-1); } (void) get_desc_file_path(ks_desc_file); /* update the version of for keystore file in tempoary file */ (void) get_tmp_desc_file_path(tmp_ks_name); if (create_updated_keystore_version(ks_fd, tmp_ks_name) != 0) { /* unlock keystore description file */ (void) lock_file(ks_fd, B_FALSE, B_FALSE); (void) close(ks_fd); return (-1); } /* open object file */ if ((fd = open_and_lock_object_file(ks_handle, O_RDWR, lock_held)) < 0) { goto cleanup; } /* * make the change in a temporary file. Create the temp * file in the same directory as the token object. That * way, the "rename" later will be an atomic operation */ if (ks_handle->public) { (void) snprintf(orig_name, MAXPATHLEN, "%s/%s", get_pub_obj_path(pub_obj_path), ks_handle->name); (void) snprintf(tmp_name, MAXPATHLEN, "%s/%s%s", pub_obj_path, TMP_OBJ_PREFIX, (ks_handle->name) + OBJ_PREFIX_LEN); } else { (void) snprintf(orig_name, MAXPATHLEN, "%s/%s", get_pri_obj_path(pri_obj_path), ks_handle->name); (void) snprintf(tmp_name, MAXPATHLEN, "%s/%s%s", pri_obj_path, TMP_OBJ_PREFIX, (ks_handle->name) + OBJ_PREFIX_LEN); } tmp_fd = open_nointr(tmp_name, O_WRONLY|O_CREAT|O_EXCL|O_NONBLOCK, S_IRUSR|S_IWUSR); if (tmp_fd < 0) { /* can't create tmp object file */ goto cleanup1; } /* read version, increment, and write to tmp file */ if (readn_nointr(fd, (char *)&version, OBJ_VER_SIZE) != OBJ_VER_SIZE) { goto cleanup2; } version = SWAP32(version); version++; version = SWAP32(version); if (writen_nointr(tmp_fd, (char *)&version, OBJ_VER_SIZE) != OBJ_VER_SIZE) { goto cleanup2; } /* generate a new IV for the object, old one can be ignored */ if (soft_gen_iv(iv) != CKR_OK) { goto cleanup2; } if (writen_nointr(tmp_fd, (char *)iv, OBJ_IV_SIZE) != OBJ_IV_SIZE) { goto cleanup2; } if (ks_handle->public) { /* hmac is always NULL for public objects */ bzero(obj_hmac, sizeof (obj_hmac)); if (writen_nointr(tmp_fd, (char *)obj_hmac, OBJ_HMAC_SIZE) != OBJ_HMAC_SIZE) { goto cleanup2; } /* write updated object */ if (writen_nointr(tmp_fd, (char *)buf, len) != len) { goto cleanup2; } } else { uchar_t *encrypted_buf, *prepared_buf; CK_ULONG out_len = 0, prepared_len; if (prepare_data_for_encrypt(orig_name, buf, len, &prepared_buf, &prepared_len) != 0) { goto cleanup2; } /* encrypt the data */ if (soft_keystore_crypt(enc_key, iv, B_TRUE, prepared_buf, prepared_len, NULL, &out_len) != CKR_OK) { free(prepared_buf); goto cleanup2; } encrypted_buf = malloc(out_len * sizeof (char)); if (encrypted_buf == NULL) { freezero(prepared_buf, prepared_len); goto cleanup2; } if (soft_keystore_crypt(enc_key, iv, B_TRUE, prepared_buf, prepared_len, encrypted_buf, &out_len) != CKR_OK) { freezero(prepared_buf, prepared_len); freezero(encrypted_buf, out_len); goto cleanup2; } freezero(prepared_buf, prepared_len); /* calculate hmac on encrypted buf */ hmac_size = OBJ_HMAC_SIZE; if (soft_keystore_hmac(hmac_key, B_TRUE, encrypted_buf, out_len, obj_hmac, &hmac_size) != CKR_OK) { freezero(encrypted_buf, out_len); goto cleanup2; } if (hmac_size != OBJ_HMAC_SIZE) { freezero(encrypted_buf, out_len); goto cleanup2; } if (writen_nointr(tmp_fd, (char *)obj_hmac, OBJ_HMAC_SIZE) != OBJ_HMAC_SIZE) { freezero(encrypted_buf, out_len); goto cleanup2; } if (writen_nointr(tmp_fd, (void *)encrypted_buf, out_len) != out_len) { freezero(encrypted_buf, out_len); goto cleanup2; } freezero(encrypted_buf, out_len); } (void) close(tmp_fd); /* rename updated temporary object file */ if (rename(tmp_name, orig_name) != 0) { (void) unlink(tmp_name); return (-1); } /* rename updated keystore description file */ if (rename(tmp_ks_name, ks_desc_file) != 0) { (void) unlink(tmp_name); (void) unlink(tmp_ks_name); return (-1); } /* determine need to unlock file or not */ if (!lock_held) { if (lock_file(fd, B_FALSE, B_FALSE) < 0) { (void) close(fd); (void) unlink(tmp_name); return (-1); } } /* unlock keystore description file */ if (lock_file(ks_fd, B_FALSE, B_FALSE) != 0) { (void) close(ks_fd); (void) close(fd); return (-1); } (void) close(ks_fd); (void) close(fd); explicit_bzero(iv, sizeof (iv)); explicit_bzero(obj_hmac, sizeof (obj_hmac)); return (0); /* All operations completed successfully */ cleanup2: (void) close(tmp_fd); (void) remove(tmp_name); cleanup1: (void) close(fd); cleanup: /* unlock keystore description file */ (void) lock_file(ks_fd, B_FALSE, B_FALSE); (void) close(ks_fd); (void) remove(tmp_ks_name); explicit_bzero(iv, sizeof (iv)); explicit_bzero(obj_hmac, sizeof (obj_hmac)); return (-1); } /* * FUNCTION: soft_keystore_del_obj * * ARGUMENTS: * ks_handle: handle of the key store object to be deleted * lock_held: TRUE if the lock is held by caller. * * RETURN VALUE: * -1: if any error occurred. * 0: object successfully deleted from keystore. * * DESCRIPTION: * This API is used to delete a particular token object from * the keystore. The corresponding token object file will be * removed from the file system. * Any future reference to the deleted file will * return an CKR_OBJECT_HANDLE_INVALID error. */ int soft_keystore_del_obj(ks_obj_handle_t *ks_handle, boolean_t lock_held) { char objname[MAXPATHLEN], tmp_ks_name[MAXPATHLEN]; int fd; char pub_obj_path[MAXPATHLEN], pri_obj_path[MAXPATHLEN], ks_desc_file[MAXPATHLEN]; int ret_val = -1; int obj_fd; if ((fd = open_and_lock_keystore_desc(O_RDWR, B_FALSE, lock_held)) < 0) { return (-1); } (void) get_desc_file_path(ks_desc_file); (void) get_tmp_desc_file_path(tmp_ks_name); if (create_updated_keystore_version(fd, tmp_ks_name) != 0) { goto cleanup; } if (ks_handle->public) { (void) snprintf(objname, MAXPATHLEN, "%s/%s", get_pub_obj_path(pub_obj_path), ks_handle->name); } else { (void) snprintf(objname, MAXPATHLEN, "%s/%s", get_pri_obj_path(pri_obj_path), ks_handle->name); } /* * make sure no other process is reading/writing the file * by acquiring the lock on the file */ if ((obj_fd = open_and_lock_object_file(ks_handle, O_WRONLY, B_FALSE)) < 0) { return (-1); } if (unlink(objname) != 0) { (void) lock_file(obj_fd, B_FALSE, B_FALSE); (void) close(obj_fd); goto cleanup; } (void) lock_file(obj_fd, B_FALSE, B_FALSE); (void) close(obj_fd); if (rename(tmp_ks_name, ks_desc_file) != 0) { goto cleanup; } ret_val = 0; cleanup: /* unlock keystore description file */ if (!lock_held) { if (lock_file(fd, B_FALSE, B_FALSE) != 0) { (void) close(fd); return (-1); } } (void) close(fd); return (ret_val); } /* * Get the salt used for generating hashed pin from the * keystore description file. * * The result will be stored in the provided buffer "salt" passed * in as an argument. * * Return 0 if no error, return -1 if there's any error. */ int soft_keystore_get_pin_salt(char **salt) { int fd, ret_val = -1; uint64_t hashed_pin_salt_size; if ((fd = open_and_lock_keystore_desc(O_RDONLY, B_FALSE, B_FALSE)) < 0) { return (-1); } if (lseek(fd, KS_HASHED_PIN_SALT_LEN_OFFSET, SEEK_SET) != KS_HASHED_PIN_SALT_LEN_OFFSET) { goto cleanup; } if (readn_nointr(fd, (char *)&hashed_pin_salt_size, KS_HASHED_PIN_SALT_LEN_SIZE) != KS_HASHED_PIN_SALT_LEN_SIZE) { goto cleanup; } hashed_pin_salt_size = SWAP64(hashed_pin_salt_size); *salt = malloc(hashed_pin_salt_size + 1); if (*salt == NULL) { goto cleanup; } if ((readn_nointr(fd, *salt, hashed_pin_salt_size)) != (ssize_t)hashed_pin_salt_size) { freezero(*salt, hashed_pin_salt_size + 1); goto cleanup; } (*salt)[hashed_pin_salt_size] = '\0'; ret_val = 0; cleanup: if (lock_file(fd, B_TRUE, B_FALSE) < 0) { ret_val = -1; } (void) close(fd); return (ret_val); } /* * FUNCTION: soft_keystore_pin_initialized * * ARGUMENTS: * initialized: This value will be set to true if keystore is * initialized, and false otherwise. * hashed_pin: If the keystore is initialized, this will contain * the hashed pin. It will be NULL if the keystore * pin is not initialized. Memory allocated * for the hashed pin needs to be freed by * the caller. * lock_held: TRUE if the lock is held by caller. * * RETURN VALUE: * CKR_OK: No error * any other appropriate CKR_value * * DESCRIPTION: * This API is used to determine if the PIN in the keystore * has been initialized or not. * It makes the determination using the salt for generating the * encryption key. The salt is stored in the keystore * descryption file. The salt should be all zero if * the keystore pin has not been initialized. * If the pin has been initialized, it is returned in the * hashed_pin argument. */ CK_RV soft_keystore_pin_initialized(boolean_t *initialized, char **hashed_pin, boolean_t lock_held) { int fd; CK_BYTE crypt_salt[KS_KEY_SALT_SIZE], tmp_buf[KS_KEY_SALT_SIZE]; CK_RV ret_val = CKR_OK; if ((fd = open_and_lock_keystore_desc(O_RDONLY, B_FALSE, lock_held)) < 0) { return (CKR_FUNCTION_FAILED); } if (lseek(fd, KS_KEY_SALT_OFFSET, SEEK_SET) != KS_KEY_SALT_OFFSET) { ret_val = CKR_FUNCTION_FAILED; goto cleanup; } if (readn_nointr(fd, (char *)crypt_salt, KS_KEY_SALT_SIZE) != KS_KEY_SALT_SIZE) { ret_val = CKR_FUNCTION_FAILED; goto cleanup; } (void) bzero(tmp_buf, KS_KEY_SALT_SIZE); if (memcmp(crypt_salt, tmp_buf, KS_KEY_SALT_SIZE) == 0) { *initialized = B_FALSE; hashed_pin = NULL; } else { *initialized = B_TRUE; ret_val = get_hashed_pin(fd, hashed_pin); } cleanup: if (!lock_held) { if (lock_file(fd, B_TRUE, B_FALSE) < 0) { ret_val = CKR_FUNCTION_FAILED; } } (void) close(fd); return (ret_val); } /* * This checks if the keystore file exists */ static int soft_keystore_exists() { int ret; struct stat fn_stat; char *fname, ks_desc_file[MAXPATHLEN]; fname = get_desc_file_path(ks_desc_file); ret = stat(fname, &fn_stat); if (ret == 0) return (0); return (errno); } /* * FUNCTION: soft_keystore_init * * ARGUMENTS: * desired_state: The keystore state the caller would like * it to be. * * RETURN VALUE: * Returns the state the function is in. If it succeeded, it * will be the same as the desired, if not it will be * KEYSTORE_UNAVAILABLE. * * DESCRIPTION: * This function will only load as much keystore data as is * requested at that time. This is for performace by delaying the * reading of token objects until they are needed or never at * all if they are not used. * * Primary use is from C_InitToken(). * It is also called by soft_keystore_status() when the * "desired_state" is not the the current load state of keystore. * */ int soft_keystore_init(int desired_state) { int ret; (void) pthread_mutex_lock(&soft_slot.keystore_mutex); /* * If more than one session tries to initialize the keystore, the * second and other following sessions that were waiting for the lock * will quickly exit if their requirements are satisfied. */ if (desired_state <= soft_slot.keystore_load_status) { (void) pthread_mutex_unlock(&soft_slot.keystore_mutex); return (soft_slot.keystore_load_status); } /* * With 'keystore_load_status' giving the current state of the * process, this switch will bring it up to the desired state if * possible. */ switch (soft_slot.keystore_load_status) { case KEYSTORE_UNINITIALIZED: ret = soft_keystore_exists(); if (ret == 0) soft_slot.keystore_load_status = KEYSTORE_PRESENT; else if (ret == ENOENT) if (create_keystore() == 0) soft_slot.keystore_load_status = KEYSTORE_PRESENT; else { soft_slot.keystore_load_status = KEYSTORE_UNAVAILABLE; cryptoerror(LOG_DEBUG, "pkcs11_softtoken: " "Cannot create keystore."); break; } if (desired_state <= KEYSTORE_PRESENT) break; /* FALLTHRU */ case KEYSTORE_PRESENT: if (soft_keystore_get_version(&soft_slot.ks_version, B_FALSE) != 0) { soft_slot.keystore_load_status = KEYSTORE_UNAVAILABLE; cryptoerror(LOG_DEBUG, "pkcs11_softtoken: Keystore access failed."); break; } soft_slot.keystore_load_status = KEYSTORE_LOAD; if (desired_state <= KEYSTORE_LOAD) break; /* FALLTHRU */ case KEYSTORE_LOAD: /* Load all the public token objects from keystore */ if (soft_get_token_objects_from_keystore(PUB_TOKENOBJS) != CKR_OK) { (void) soft_destroy_token_session(); soft_slot.keystore_load_status = KEYSTORE_UNAVAILABLE; cryptoerror(LOG_DEBUG, "pkcs11_softtoken: Cannot initialize keystore."); break; } soft_slot.keystore_load_status = KEYSTORE_INITIALIZED; }; (void) pthread_mutex_unlock(&soft_slot.keystore_mutex); return (soft_slot.keystore_load_status); } /* * FUNCTION: soft_keystore_status * * ARGUMENTS: * desired_state: The keystore state the caller would like * it to be. * * RETURN VALUE: * B_TRUE if keystore is ready and at the desired state. * B_FALSE if keystore had an error and is not available. * * DESCRIPTION: * The calling function wants to make sure the keystore load * status to in a state it requires. If it is not at that * state it will call the load function. * If keystore is at the desired state or has just been * loaded to that state, it will return TRUE. If there has been * load failure, it will return FALSE. * */ boolean_t soft_keystore_status(int desired_state) { if (soft_slot.keystore_load_status == KEYSTORE_UNAVAILABLE) return (B_FALSE); return ((desired_state <= soft_slot.keystore_load_status) || (soft_keystore_init(desired_state) == desired_state)); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _SOFTKEYSTOREUTIL_H #define _SOFTKEYSTOREUTIL_H /* * Structures and function prototypes for the keystore */ #ifdef __cplusplus extern "C" { #endif #include /* Keystore State values */ #define KEYSTORE_UNINITIALIZED 0 #define KEYSTORE_PRESENT 1 #define KEYSTORE_LOAD 2 #define KEYSTORE_INITIALIZED 3 #define KEYSTORE_UNAVAILABLE 4 typedef enum { ALL_TOKENOBJS = 0, PUB_TOKENOBJS = 1, PRI_TOKENOBJS = 2 } ks_search_type_t; typedef struct ks_obj_handle { unsigned char name[256]; /* obj[monotonic-counter] */ boolean_t public; /* true if public obj, false for private obj */ } ks_obj_handle_t; typedef struct ks_obj { /* handle for accessing this object */ ks_obj_handle_t ks_handle; /* version number of object file */ uint_t obj_version; /* contains decrypted binary data for obj */ uchar_t *buf; /* size of binary data */ size_t size; /* pointer to next item in list */ struct ks_obj *next; } ks_obj_t; /* * Prototype for functions in softKeystore.c */ int soft_keystore_readlock(boolean_t set_lock); int soft_keystore_writelock(boolean_t set_lock); int soft_keystore_lock_object(ks_obj_handle_t *ks_handle, boolean_t read_lock); int soft_keystore_unlock_object(int fd); int soft_keystore_get_version(uint_t *version, boolean_t lock_held); int soft_keystore_get_object_version(ks_obj_handle_t *ks_handle, uint_t *version, boolean_t lock_held); int soft_keystore_getpin(char **hashed_pin, boolean_t lock_held); int soft_keystore_setpin(uchar_t *oldpin, uchar_t *newpin, boolean_t lock_held); int soft_keystore_authpin(uchar_t *pin); CK_RV soft_keystore_get_objs(ks_search_type_t search_type, ks_obj_t **result_objs, boolean_t lock_held); CK_RV soft_keystore_get_single_obj(ks_obj_handle_t *ks_handle, ks_obj_t **result_obj, boolean_t lock_held); int soft_keystore_put_new_obj(uchar_t *buf, size_t len, boolean_t public, boolean_t lock_held, ks_obj_handle_t *keyhandle); int soft_keystore_modify_obj(ks_obj_handle_t *ks_handle, uchar_t *buf, size_t len, boolean_t lock_held); int soft_keystore_del_obj(ks_obj_handle_t *ks_handle, boolean_t lock_held); int soft_keystore_get_pin_salt(char **salt); CK_RV soft_keystore_pin_initialized(boolean_t *initialized, char **hashed_pin, boolean_t lock_held); boolean_t soft_keystore_status(int desired_state); int soft_keystore_init(int desired_state); int create_keystore(); #ifdef __cplusplus } #endif #endif /* _SOFTKEYSTOREUTIL_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright (c) 2018, Joyent, Inc. */ #include #include #include #include #include #include #include #include #include #include "softObject.h" #include "softOps.h" #include "softSession.h" #include "softMAC.h" /* * IPAD = 0x36 repeated 48 times for ssl md5, repeated 40 times for ssl sha1 * OPAD = 0x5C repeated 48 times for SSL md5, repeated 40 times for ssl sha1 */ const uint32_t md5_ssl_ipad[] = { 0x36363636, 0x36363636, 0x36363636, 0x36363636, 0x36363636, 0x36363636, 0x36363636, 0x36363636, 0x36363636, 0x36363636, 0x36363636, 0x36363636}; const uint32_t sha1_ssl_ipad[] = { 0x36363636, 0x36363636, 0x36363636, 0x36363636, 0x36363636, 0x36363636, 0x36363636, 0x36363636, 0x36363636, 0x36363636}; const uint32_t md5_ssl_opad[] = { 0x5c5c5c5c, 0x5c5c5c5c, 0x5c5c5c5c, 0x5c5c5c5c, 0x5c5c5c5c, 0x5c5c5c5c, 0x5c5c5c5c, 0x5c5c5c5c, 0x5c5c5c5c, 0x5c5c5c5c, 0x5c5c5c5c, 0x5c5c5c5c}; const uint32_t sha1_ssl_opad[] = { 0x5c5c5c5c, 0x5c5c5c5c, 0x5c5c5c5c, 0x5c5c5c5c, 0x5c5c5c5c, 0x5c5c5c5c, 0x5c5c5c5c, 0x5c5c5c5c, 0x5c5c5c5c, 0x5c5c5c5c}; /* * Allocate and initialize a HMAC context, and save the context pointer in * the session struct. For General-length HMAC, checks the length in the * parameter to see if it is in the right range. */ CK_RV soft_hmac_sign_verify_init_common(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism, soft_object_t *key_p, boolean_t sign_op) { soft_hmac_ctx_t *hmac_ctx; CK_RV rv = CKR_OK; if ((key_p->class != CKO_SECRET_KEY) || (key_p->key_type != CKK_GENERIC_SECRET)) { return (CKR_KEY_TYPE_INCONSISTENT); } hmac_ctx = malloc(sizeof (soft_hmac_ctx_t)); if (hmac_ctx == NULL) { return (CKR_HOST_MEMORY); } switch (pMechanism->mechanism) { case CKM_MD5_HMAC: hmac_ctx->hmac_len = MD5_HASH_SIZE; break; case CKM_SHA_1_HMAC: hmac_ctx->hmac_len = SHA1_HASH_SIZE; break; case CKM_SHA256_HMAC: hmac_ctx->hmac_len = SHA256_DIGEST_LENGTH; break; case CKM_SHA384_HMAC: hmac_ctx->hmac_len = SHA384_DIGEST_LENGTH; break; case CKM_SHA512_HMAC: hmac_ctx->hmac_len = SHA512_DIGEST_LENGTH; break; case CKM_MD5_HMAC_GENERAL: case CKM_SSL3_MD5_MAC: if ((pMechanism->ulParameterLen != sizeof (CK_MAC_GENERAL_PARAMS)) && (*(CK_MAC_GENERAL_PARAMS *)pMechanism->pParameter > MD5_HASH_SIZE)) { free(hmac_ctx); return (CKR_MECHANISM_PARAM_INVALID); } hmac_ctx->hmac_len = *((CK_MAC_GENERAL_PARAMS_PTR) pMechanism->pParameter); break; case CKM_SSL3_SHA1_MAC: case CKM_SHA_1_HMAC_GENERAL: if ((pMechanism->ulParameterLen != sizeof (CK_MAC_GENERAL_PARAMS)) && (*(CK_MAC_GENERAL_PARAMS *)pMechanism->pParameter > SHA1_HASH_SIZE)) { free(hmac_ctx); return (CKR_MECHANISM_PARAM_INVALID); } hmac_ctx->hmac_len = *((CK_MAC_GENERAL_PARAMS_PTR) pMechanism->pParameter); break; case CKM_SHA256_HMAC_GENERAL: if ((pMechanism->ulParameterLen != sizeof (CK_MAC_GENERAL_PARAMS)) && (*(CK_MAC_GENERAL_PARAMS *)pMechanism->pParameter > SHA256_DIGEST_LENGTH)) { free(hmac_ctx); return (CKR_MECHANISM_PARAM_INVALID); } hmac_ctx->hmac_len = *((CK_MAC_GENERAL_PARAMS_PTR) pMechanism->pParameter); break; case CKM_SHA384_HMAC_GENERAL: case CKM_SHA512_HMAC_GENERAL: if ((pMechanism->ulParameterLen != sizeof (CK_MAC_GENERAL_PARAMS)) && (*(CK_MAC_GENERAL_PARAMS *)pMechanism->pParameter > SHA512_DIGEST_LENGTH)) { free(hmac_ctx); return (CKR_MECHANISM_PARAM_INVALID); } hmac_ctx->hmac_len = *((CK_MAC_GENERAL_PARAMS_PTR) pMechanism->pParameter); break; } /* Initialize a MAC context. */ rv = mac_init_ctx(session_p, key_p, hmac_ctx, pMechanism->mechanism); if (rv != CKR_OK) return (rv); (void) pthread_mutex_lock(&session_p->session_mutex); if (sign_op) { session_p->sign.mech.mechanism = pMechanism->mechanism; session_p->sign.context = hmac_ctx; } else { session_p->verify.mech.mechanism = pMechanism->mechanism; session_p->verify.context = hmac_ctx; } (void) pthread_mutex_unlock(&session_p->session_mutex); return (CKR_OK); } /* * Initialize a HMAC context. */ CK_RV mac_init_ctx(soft_session_t *session_p, soft_object_t *key, soft_hmac_ctx_t *ctx, CK_MECHANISM_TYPE mech) { CK_RV rv = CKR_OK; switch (mech) { case CKM_SSL3_MD5_MAC: { CK_BYTE md5_ipad[MD5_SSL_PAD_AND_KEY_SIZE]; CK_BYTE md5_opad[MD5_SSL_PAD_AND_KEY_SIZE]; if (OBJ_SEC(key)->sk_value_len > MD5_SSL_PAD_AND_KEY_SIZE) { return (CKR_KEY_SIZE_RANGE); } bzero(md5_ipad, MD5_SSL_PAD_AND_KEY_SIZE); bzero(md5_opad, MD5_SSL_PAD_AND_KEY_SIZE); /* SSL MAC is HASH(key + opad + HASH(key + ipad + data)) */ (void) memcpy(md5_ipad, OBJ_SEC(key)->sk_value, OBJ_SEC(key)->sk_value_len); (void) memcpy(&md5_ipad[OBJ_SEC(key)->sk_value_len], md5_ssl_ipad, MD5_SSL_PAD_SIZE); (void) memcpy(md5_opad, OBJ_SEC(key)->sk_value, OBJ_SEC(key)->sk_value_len); (void) memcpy(&md5_opad[OBJ_SEC(key)->sk_value_len], md5_ssl_opad, MD5_SSL_PAD_SIZE); SOFT_MAC_INIT_CTX(MD5, &(ctx->hc_ctx_u.md5_ctx), md5_ipad, md5_opad, MD5_SSL_PAD_AND_KEY_SIZE); break; } case CKM_MD5_HMAC_GENERAL: case CKM_MD5_HMAC: { uint32_t md5_ipad[MD5_HMAC_INTS_PER_BLOCK]; uint32_t md5_opad[MD5_HMAC_INTS_PER_BLOCK]; CK_MECHANISM digest_mech; CK_ULONG hash_len = MD5_HASH_SIZE; bzero(md5_ipad, MD5_HMAC_BLOCK_SIZE); bzero(md5_opad, MD5_HMAC_BLOCK_SIZE); if (OBJ_SEC(key)->sk_value_len > MD5_HMAC_BLOCK_SIZE) { /* * Hash the key when it is longer than 64 bytes. */ digest_mech.mechanism = CKM_MD5; digest_mech.pParameter = NULL_PTR; digest_mech.ulParameterLen = 0; rv = soft_digest_init_internal(session_p, &digest_mech); if (rv != CKR_OK) return (rv); rv = soft_digest(session_p, OBJ_SEC(key)->sk_value, OBJ_SEC(key)->sk_value_len, (CK_BYTE_PTR)md5_ipad, &hash_len); session_p->digest.flags = 0; if (rv != CKR_OK) return (rv); (void) memcpy(md5_opad, md5_ipad, hash_len); } else { (void) memcpy(md5_ipad, OBJ_SEC(key)->sk_value, OBJ_SEC(key)->sk_value_len); (void) memcpy(md5_opad, OBJ_SEC(key)->sk_value, OBJ_SEC(key)->sk_value_len); } md5_hmac_ctx_init(&ctx->hc_ctx_u.md5_ctx, md5_ipad, md5_opad); break; } case CKM_SSL3_SHA1_MAC: { CK_BYTE sha1_ipad[SHA1_SSL_PAD_AND_KEY_SIZE]; CK_BYTE sha1_opad[SHA1_SSL_PAD_AND_KEY_SIZE]; if (OBJ_SEC(key)->sk_value_len > SHA1_HMAC_BLOCK_SIZE) { return (CKR_KEY_SIZE_RANGE); } bzero(sha1_ipad, SHA1_SSL_PAD_AND_KEY_SIZE); bzero(sha1_opad, SHA1_SSL_PAD_AND_KEY_SIZE); /* SSL MAC is HASH(key + opad + HASH(key + ipad + data)) */ (void) memcpy(sha1_ipad, OBJ_SEC(key)->sk_value, OBJ_SEC(key)->sk_value_len); (void) memcpy(&sha1_ipad[OBJ_SEC(key)->sk_value_len], sha1_ssl_ipad, SHA1_SSL_PAD_SIZE); (void) memcpy(sha1_opad, OBJ_SEC(key)->sk_value, OBJ_SEC(key)->sk_value_len); (void) memcpy(&sha1_opad[OBJ_SEC(key)->sk_value_len], sha1_ssl_opad, SHA1_SSL_PAD_SIZE); SOFT_MAC_INIT_CTX(SHA1, &(ctx->hc_ctx_u.sha1_ctx), sha1_ipad, sha1_opad, SHA1_SSL_PAD_AND_KEY_SIZE); break; } case CKM_SHA_1_HMAC_GENERAL: case CKM_SHA_1_HMAC: { uint32_t sha1_ipad[SHA1_HMAC_INTS_PER_BLOCK]; uint32_t sha1_opad[SHA1_HMAC_INTS_PER_BLOCK]; CK_MECHANISM digest_mech; CK_ULONG hash_len = SHA1_HASH_SIZE; bzero(sha1_ipad, SHA1_HMAC_BLOCK_SIZE); bzero(sha1_opad, SHA1_HMAC_BLOCK_SIZE); if (OBJ_SEC(key)->sk_value_len > SHA1_HMAC_BLOCK_SIZE) { /* * Hash the key when it is longer than 64 bytes. */ digest_mech.mechanism = CKM_SHA_1; digest_mech.pParameter = NULL_PTR; digest_mech.ulParameterLen = 0; rv = soft_digest_init_internal(session_p, &digest_mech); if (rv != CKR_OK) return (rv); rv = soft_digest(session_p, OBJ_SEC(key)->sk_value, OBJ_SEC(key)->sk_value_len, (CK_BYTE_PTR)sha1_ipad, &hash_len); session_p->digest.flags = 0; if (rv != CKR_OK) return (rv); (void) memcpy(sha1_opad, sha1_ipad, hash_len); } else { (void) memcpy(sha1_ipad, OBJ_SEC(key)->sk_value, OBJ_SEC(key)->sk_value_len); (void) memcpy(sha1_opad, OBJ_SEC(key)->sk_value, OBJ_SEC(key)->sk_value_len); } sha1_hmac_ctx_init(&ctx->hc_ctx_u.sha1_ctx, sha1_ipad, sha1_opad); break; } case CKM_SHA256_HMAC: case CKM_SHA256_HMAC_GENERAL: { uint64_t sha_ipad[SHA256_HMAC_INTS_PER_BLOCK]; uint64_t sha_opad[SHA256_HMAC_INTS_PER_BLOCK]; CK_MECHANISM digest_mech; CK_ULONG hash_len = SHA256_DIGEST_LENGTH; bzero(sha_ipad, SHA256_HMAC_BLOCK_SIZE); bzero(sha_opad, SHA256_HMAC_BLOCK_SIZE); if (OBJ_SEC(key)->sk_value_len > SHA256_HMAC_BLOCK_SIZE) { /* * Hash the key when it is longer than 64 bytes. */ digest_mech.mechanism = CKM_SHA256; digest_mech.pParameter = NULL_PTR; digest_mech.ulParameterLen = 0; rv = soft_digest_init_internal(session_p, &digest_mech); if (rv != CKR_OK) return (rv); rv = soft_digest(session_p, OBJ_SEC(key)->sk_value, OBJ_SEC(key)->sk_value_len, (CK_BYTE_PTR)sha_ipad, &hash_len); session_p->digest.flags = 0; if (rv != CKR_OK) return (rv); (void) memcpy(sha_opad, sha_ipad, hash_len); } else { (void) memcpy(sha_ipad, OBJ_SEC(key)->sk_value, OBJ_SEC(key)->sk_value_len); (void) memcpy(sha_opad, OBJ_SEC(key)->sk_value, OBJ_SEC(key)->sk_value_len); } sha2_hmac_ctx_init(CKM_TO_SHA2(mech), &ctx->hc_ctx_u.sha2_ctx, sha_ipad, sha_opad, SHA256_HMAC_INTS_PER_BLOCK, SHA256_HMAC_BLOCK_SIZE); break; } case CKM_SHA384_HMAC: case CKM_SHA384_HMAC_GENERAL: { uint64_t sha_ipad[SHA512_HMAC_INTS_PER_BLOCK]; uint64_t sha_opad[SHA512_HMAC_INTS_PER_BLOCK]; CK_MECHANISM digest_mech; CK_ULONG hash_len = SHA384_DIGEST_LENGTH; bzero(sha_ipad, SHA512_HMAC_BLOCK_SIZE); bzero(sha_opad, SHA512_HMAC_BLOCK_SIZE); if (OBJ_SEC(key)->sk_value_len > SHA512_HMAC_BLOCK_SIZE) { /* * Hash the key when it is longer than 64 bytes. */ digest_mech.mechanism = CKM_SHA384; digest_mech.pParameter = NULL_PTR; digest_mech.ulParameterLen = 0; rv = soft_digest_init_internal(session_p, &digest_mech); if (rv != CKR_OK) return (rv); rv = soft_digest(session_p, OBJ_SEC(key)->sk_value, OBJ_SEC(key)->sk_value_len, (CK_BYTE_PTR)sha_ipad, &hash_len); session_p->digest.flags = 0; if (rv != CKR_OK) return (rv); (void) memcpy(sha_opad, sha_ipad, hash_len); } else { (void) memcpy(sha_ipad, OBJ_SEC(key)->sk_value, OBJ_SEC(key)->sk_value_len); (void) memcpy(sha_opad, OBJ_SEC(key)->sk_value, OBJ_SEC(key)->sk_value_len); } sha2_hmac_ctx_init(CKM_TO_SHA2(mech), &ctx->hc_ctx_u.sha2_ctx, sha_ipad, sha_opad, SHA512_HMAC_INTS_PER_BLOCK, SHA512_HMAC_BLOCK_SIZE); break; } case CKM_SHA512_HMAC: case CKM_SHA512_HMAC_GENERAL: { uint64_t sha_ipad[SHA512_HMAC_INTS_PER_BLOCK]; uint64_t sha_opad[SHA512_HMAC_INTS_PER_BLOCK]; CK_MECHANISM digest_mech; CK_ULONG hash_len = SHA512_DIGEST_LENGTH; bzero(sha_ipad, SHA512_HMAC_BLOCK_SIZE); bzero(sha_opad, SHA512_HMAC_BLOCK_SIZE); if (OBJ_SEC(key)->sk_value_len > SHA512_HMAC_BLOCK_SIZE) { /* * Hash the key when it is longer than 64 bytes. */ digest_mech.mechanism = CKM_SHA512; digest_mech.pParameter = NULL_PTR; digest_mech.ulParameterLen = 0; rv = soft_digest_init_internal(session_p, &digest_mech); if (rv != CKR_OK) return (rv); rv = soft_digest(session_p, OBJ_SEC(key)->sk_value, OBJ_SEC(key)->sk_value_len, (CK_BYTE_PTR)sha_ipad, &hash_len); session_p->digest.flags = 0; if (rv != CKR_OK) return (rv); (void) memcpy(sha_opad, sha_ipad, hash_len); } else { (void) memcpy(sha_ipad, OBJ_SEC(key)->sk_value, OBJ_SEC(key)->sk_value_len); (void) memcpy(sha_opad, OBJ_SEC(key)->sk_value, OBJ_SEC(key)->sk_value_len); } sha2_hmac_ctx_init(CKM_TO_SHA2(mech), &ctx->hc_ctx_u.sha2_ctx, sha_ipad, sha_opad, SHA512_HMAC_INTS_PER_BLOCK, SHA512_HMAC_BLOCK_SIZE); break; } } return (rv); } /* * Called by soft_sign(), soft_sign_final(), soft_verify() or * soft_verify_final(). */ CK_RV soft_hmac_sign_verify_common(soft_session_t *session_p, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSigned, CK_ULONG_PTR pulSignedLen, boolean_t sign_op) { soft_hmac_ctx_t *hmac_ctx; CK_MECHANISM_TYPE mechanism; #ifdef __sparcv9 /* LINTED */ uint_t datalen = (uint_t)ulDataLen; #else /* __sparcv9 */ uint_t datalen = ulDataLen; #endif /* __sparcv9 */ if (sign_op) { hmac_ctx = (soft_hmac_ctx_t *)session_p->sign.context; mechanism = session_p->sign.mech.mechanism; /* * If application asks for the length of the output buffer * to hold the signature? */ if (pSigned == NULL) { *pulSignedLen = hmac_ctx->hmac_len; return (CKR_OK); } /* Is the application-supplied buffer large enough? */ if (*pulSignedLen < hmac_ctx->hmac_len) { *pulSignedLen = hmac_ctx->hmac_len; return (CKR_BUFFER_TOO_SMALL); } } else { hmac_ctx = (soft_hmac_ctx_t *)session_p->verify.context; mechanism = session_p->verify.mech.mechanism; } switch (mechanism) { case CKM_SSL3_MD5_MAC: case CKM_MD5_HMAC_GENERAL: case CKM_MD5_HMAC: if (pData != NULL) { /* Called by soft_sign() or soft_verify(). */ SOFT_MAC_UPDATE(MD5, &(hmac_ctx->hc_ctx_u.md5_ctx), pData, datalen); } SOFT_MAC_FINAL(MD5, &(hmac_ctx->hc_ctx_u.md5_ctx), pSigned); break; case CKM_SSL3_SHA1_MAC: case CKM_SHA_1_HMAC_GENERAL: case CKM_SHA_1_HMAC: if (pData != NULL) { /* Called by soft_sign() or soft_verify(). */ SOFT_MAC_UPDATE(SHA1, &(hmac_ctx->hc_ctx_u.sha1_ctx), pData, datalen); } SOFT_MAC_FINAL(SHA1, &(hmac_ctx->hc_ctx_u.sha1_ctx), pSigned); break; case CKM_SHA256_HMAC_GENERAL: case CKM_SHA256_HMAC: if (pData != NULL) /* Called by soft_sign() or soft_verify(). */ SHA2Update(&(hmac_ctx->hc_ctx_u.sha2_ctx.hc_icontext), pData, datalen); SOFT_MAC_FINAL_2(SHA256, &(hmac_ctx->hc_ctx_u.sha2_ctx), pSigned); break; case CKM_SHA384_HMAC_GENERAL: case CKM_SHA384_HMAC: if (pData != NULL) /* Called by soft_sign() or soft_verify(). */ SHA2Update(&(hmac_ctx->hc_ctx_u.sha2_ctx.hc_icontext), pData, datalen); SOFT_MAC_FINAL_2(SHA384, &(hmac_ctx->hc_ctx_u.sha2_ctx), pSigned); hmac_ctx->hmac_len = SHA384_DIGEST_LENGTH; break; case CKM_SHA512_HMAC_GENERAL: case CKM_SHA512_HMAC: if (pData != NULL) /* Called by soft_sign() or soft_verify(). */ SHA2Update(&(hmac_ctx->hc_ctx_u.sha2_ctx.hc_icontext), pData, datalen); SOFT_MAC_FINAL_2(SHA512, &(hmac_ctx->hc_ctx_u.sha2_ctx), pSigned); }; *pulSignedLen = hmac_ctx->hmac_len; (void) pthread_mutex_lock(&session_p->session_mutex); if (sign_op) { freezero(session_p->sign.context, sizeof (soft_hmac_ctx_t)); session_p->sign.context = NULL; } else { freezero(session_p->verify.context, sizeof (soft_hmac_ctx_t)); session_p->verify.context = NULL; } (void) pthread_mutex_unlock(&session_p->session_mutex); return (CKR_OK); } /* * Called by soft_sign_update() or soft_verify_update(). */ CK_RV soft_hmac_sign_verify_update(soft_session_t *session_p, CK_BYTE_PTR pPart, CK_ULONG ulPartLen, boolean_t sign_op) { soft_hmac_ctx_t *hmac_ctx; CK_MECHANISM_TYPE mechanism; #ifdef __sparcv9 /* LINTED */ uint_t partlen = (uint_t)ulPartLen; #else /* __sparcv9 */ uint_t partlen = ulPartLen; #endif /* __sparcv9 */ if (sign_op) { hmac_ctx = (soft_hmac_ctx_t *)session_p->sign.context; mechanism = session_p->sign.mech.mechanism; } else { hmac_ctx = (soft_hmac_ctx_t *)session_p->verify.context; mechanism = session_p->verify.mech.mechanism; } switch (mechanism) { case CKM_SSL3_MD5_MAC: case CKM_MD5_HMAC_GENERAL: case CKM_MD5_HMAC: SOFT_MAC_UPDATE(MD5, &(hmac_ctx->hc_ctx_u.md5_ctx), pPart, partlen); break; case CKM_SSL3_SHA1_MAC: case CKM_SHA_1_HMAC_GENERAL: case CKM_SHA_1_HMAC: SOFT_MAC_UPDATE(SHA1, &(hmac_ctx->hc_ctx_u.sha1_ctx), pPart, partlen); break; case CKM_SHA256_HMAC_GENERAL: case CKM_SHA256_HMAC: case CKM_SHA384_HMAC_GENERAL: case CKM_SHA384_HMAC: case CKM_SHA512_HMAC_GENERAL: case CKM_SHA512_HMAC: SOFT_MAC_UPDATE(SHA2, &(hmac_ctx->hc_ctx_u.sha2_ctx), pPart, partlen); break; } return (CKR_OK); } /* * The following 2 functions expect the MAC key to be alreay copied in * the ipad and opad */ void md5_hmac_ctx_init(md5_hc_ctx_t *md5_hmac_ctx, uint32_t *ipad, uint32_t *opad) { int i; /* XOR key with ipad (0x36) and opad (0x5c) */ for (i = 0; i < MD5_HMAC_INTS_PER_BLOCK; i++) { ipad[i] ^= 0x36363636; opad[i] ^= 0x5c5c5c5c; } SOFT_MAC_INIT_CTX(MD5, md5_hmac_ctx, ipad, opad, MD5_HMAC_BLOCK_SIZE); } void sha1_hmac_ctx_init(sha1_hc_ctx_t *sha1_hmac_ctx, uint32_t *ipad, uint32_t *opad) { int i; /* XOR key with ipad (0x36) and opad (0x5c) */ for (i = 0; i < SHA1_HMAC_INTS_PER_BLOCK; i++) { ipad[i] ^= 0x36363636; opad[i] ^= 0x5c5c5c5c; } SOFT_MAC_INIT_CTX(SHA1, sha1_hmac_ctx, (const uchar_t *)ipad, (const uchar_t *)opad, SHA1_HMAC_BLOCK_SIZE); } void sha2_hmac_ctx_init(uint_t mech, sha2_hc_ctx_t *ctx, uint64_t *ipad, uint64_t *opad, uint_t blocks_per_int64, uint_t block_size) { int i; /* XOR key with ipad (0x36) and opad (0x5c) */ for (i = 0; i < blocks_per_int64; i ++) { ipad[i] ^= 0x3636363636363636ULL; opad[i] ^= 0x5c5c5c5c5c5c5c5cULL; } /* perform SHA2 on ipad */ SHA2Init(mech, &ctx->hc_icontext); SHA2Update(&ctx->hc_icontext, (uint8_t *)ipad, block_size); /* perform SHA2 on opad */ SHA2Init(mech, &ctx->hc_ocontext); SHA2Update(&ctx->hc_ocontext, (uint8_t *)opad, block_size); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SOFTMAC_H #define _SOFTMAC_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include "softSession.h" #include "softObject.h" #define MD5_HASH_SIZE 16 /* MD5 digest length in bytes */ #define SHA1_HASH_SIZE 20 /* SHA_1 digest length in bytes */ #define MD5_HMAC_BLOCK_SIZE 64 /* MD5 block size */ #define MD5_HMAC_INTS_PER_BLOCK (MD5_HMAC_BLOCK_SIZE/sizeof (uint32_t)) #define SHA1_HMAC_BLOCK_SIZE 64 /* SHA1-HMAC block size */ #define SHA1_HMAC_INTS_PER_BLOCK (SHA1_HMAC_BLOCK_SIZE/sizeof (uint32_t)) #define SHA256_HMAC_INTS_PER_BLOCK \ (SHA256_HMAC_BLOCK_SIZE/sizeof (uint64_t)) #define SHA512_HMAC_INTS_PER_BLOCK \ (SHA512_HMAC_BLOCK_SIZE/sizeof (uint64_t)) #define MD5_SSL_PAD_SIZE 48 /* MD5 SSL pad length in bytes */ /* 48 (MD5 SSL pad length in bytes) + 16 (key length in bytes) = 64 */ #define MD5_SSL_PAD_AND_KEY_SIZE 64 #define SHA1_SSL_PAD_SIZE 40 /* SHA1 SSL pad length in bytes */ /* 40 (SHA1 SSL pad length in bytes) + 20 (key length in bytes) = 104 */ #define SHA1_SSL_PAD_AND_KEY_SIZE 60 /* * Context for MD5-HMAC and MD5-HMAC-GENERAL mechanisms. */ typedef struct md5_hc_ctx { MD5_CTX hc_icontext; /* inner MD5 context */ MD5_CTX hc_ocontext; /* outer MD5 context */ } md5_hc_ctx_t; /* * Context for SHA1-HMAC and SHA1-HMAC-GENERAL mechanisms. */ typedef struct sha1_hc_ctx { SHA1_CTX hc_icontext; /* inner SHA1 context */ SHA1_CTX hc_ocontext; /* outer SHA1 context */ } sha1_hc_ctx_t; typedef struct sha2_hc_ctx { SHA2_CTX hc_icontext; /* inner SHA2 context */ SHA2_CTX hc_ocontext; /* outer SHA2 context */ } sha2_hc_ctx_t; /* * Generic Context struct for HMAC. */ typedef struct soft_hmac_ctx { size_t hmac_len; /* digest len in bytes */ union { md5_hc_ctx_t md5_ctx; sha1_hc_ctx_t sha1_ctx; sha2_hc_ctx_t sha2_ctx; } hc_ctx_u; } soft_hmac_ctx_t; /* Generic MAC envelop macros. Substitute HASH with MD5, SHA1, & SHA2 mechs */ #define SOFT_MAC_INIT_CTX(HASH, mac_ctx, ipad, opad, len) \ /* Perform HASH on ipad */ \ HASH##Init(&((mac_ctx)->hc_icontext)); \ HASH##Update(&((mac_ctx)->hc_icontext), ipad, len); \ /* Perform HASH on opad */ \ HASH##Init(&((mac_ctx)->hc_ocontext)); \ HASH##Update(&((mac_ctx)->hc_ocontext), opad, len); #define SOFT_MAC_UPDATE(HASH, mac_ctx, pPart, PartLen) \ HASH##Update(&((mac_ctx)->hc_icontext), pPart, PartLen); #define SOFT_MAC_FINAL(HASH, mac_ctx, mac) \ HASH##Final((mac), &((mac_ctx)->hc_icontext)); \ HASH##Update(&((mac_ctx)->hc_ocontext), (mac), HASH##_HASH_SIZE);\ HASH##Final((mac), &((mac_ctx)->hc_ocontext)); #define SOFT_MAC_FINAL_2(HASH, mac_ctx, mac) \ SHA2Final((mac), &((mac_ctx)->hc_icontext)); \ SHA2Update(&((mac_ctx)->hc_ocontext), (mac), HASH##_DIGEST_LENGTH); \ SHA2Final((mac), &((mac_ctx)->hc_ocontext)); #define CKM_TO_SHA2(ckm_value) \ (ckm_value % 0x10) + (((ckm_value - 0x250) / 0x10) * 3) /* * Function Prototypes. */ CK_RV soft_hmac_sign_verify_init_common(soft_session_t *, CK_MECHANISM_PTR, soft_object_t *, boolean_t); CK_RV mac_init_ctx(soft_session_t *session_p, soft_object_t *, soft_hmac_ctx_t *, CK_MECHANISM_TYPE); CK_RV soft_hmac_sign_verify_common(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR, boolean_t); CK_RV soft_hmac_sign_verify_update(soft_session_t *, CK_BYTE_PTR, CK_ULONG, boolean_t); void md5_hmac_ctx_init(md5_hc_ctx_t *, uint32_t *, uint32_t *); void sha1_hmac_ctx_init(sha1_hc_ctx_t *, uint32_t *, uint32_t *); void sha2_hmac_ctx_init(uint_t mech, sha2_hc_ctx_t *, uint64_t *, uint64_t *, uint_t, uint_t); #ifdef __cplusplus } #endif #endif /* _SOFTMAC_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2020 Joyent, Inc. * Copyright 2023 RackTop Systems, Inc. */ #include #include #include #include "softGlobal.h" #include "softObject.h" #include "softSession.h" #include "softKeystore.h" #include "softKeystoreUtil.h" CK_RV C_CreateObject(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phObject) { CK_RV rv; soft_session_t *session_p; boolean_t lock_held = B_FALSE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if ((pTemplate == NULL) || (ulCount == 0) || (phObject == NULL)) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } /* Create a new object. */ rv = soft_add_object(pTemplate, ulCount, phObject, session_p); clean_exit: /* * Decrement the session reference count. * We do not hold the session lock. */ SES_REFRELE(session_p, lock_held); return (rv); } CK_RV C_CopyObject(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phNewObject) { CK_RV rv; soft_session_t *session_p; boolean_t lock_held = B_FALSE; soft_object_t *old_object, *new_object = NULL; ulong_t i; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); /* Check arguments */ if (((ulCount > 0) && (pTemplate == NULL)) || (phNewObject == NULL)) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } /* Obtain the object pointer. */ HANDLE2OBJECT(hObject, old_object, rv); if (rv != CKR_OK) { goto clean_exit; } /* * Copy the old object to a new object. * The 3rd argument with SOFT_COPY_OBJ value indicates that * everything in the object will be duplicated for C_CopyObject. * The 4th argument has the session pointer that will be * saved in the new copy of the session object. */ (void) pthread_mutex_lock(&old_object->object_mutex); rv = soft_copy_object(old_object, &new_object, SOFT_COPY_OBJECT, session_p); if ((rv != CKR_OK) || (new_object == NULL)) { /* Most likely we ran out of space. */ (void) pthread_mutex_unlock(&old_object->object_mutex); goto clean_exit1; } /* No need to hold the lock on the old object. */ (void) pthread_mutex_unlock(&old_object->object_mutex); /* Modifiy the objects if requested */ for (i = 0; i < ulCount; i++) { /* Set the requested attribute into the new object. */ rv = soft_set_attribute(new_object, &pTemplate[i], B_TRUE); if (rv != CKR_OK) { goto fail; } } rv = soft_pin_expired_check(new_object); if (rv != CKR_OK) { goto fail; } /* * Does the new object violate the creation rule or access rule? */ rv = soft_object_write_access_check(session_p, new_object); if (rv != CKR_OK) { goto fail; } /* * If the new object is a token object, it will be added * to token object list and write to disk. */ if (IS_TOKEN_OBJECT(new_object)) { new_object->version = 1; /* * Write to the keystore file. */ rv = soft_put_object_to_keystore(new_object); if (rv != CKR_OK) { goto fail; } new_object->session_handle = CK_INVALID_HANDLE; /* * Add the newly created token object to the global * token object list in the slot struct. */ soft_add_token_object_to_slot(new_object); OBJ_REFRELE(old_object); SES_REFRELE(session_p, lock_held); *phNewObject = set_objecthandle(new_object); return (CKR_OK); } *phNewObject = set_objecthandle(new_object); /* Insert new object into this session's object list */ soft_add_object_to_session(new_object, session_p); /* * Decrement the session reference count. * We do not hold the session lock. */ OBJ_REFRELE(old_object); SES_REFRELE(session_p, lock_held); return (rv); fail: soft_cleanup_object(new_object); free(new_object); clean_exit1: OBJ_REFRELE(old_object); clean_exit: SES_REFRELE(session_p, lock_held); return (rv); } CK_RV C_DestroyObject(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject) { CK_RV rv; soft_object_t *object_p; soft_session_t *session_p; boolean_t lock_held = B_FALSE; CK_SESSION_HANDLE creating_session; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); /* Obtain the object pointer. */ HANDLE2OBJECT_DESTROY(hObject, object_p, rv); if (rv != CKR_OK) { SES_REFRELE(session_p, lock_held); return (rv); } /* Obtain the session handle which object belongs to. */ creating_session = object_p->session_handle; if (creating_session == 0) { /* * This is a token object to be deleted. * For token object, there is no creating session concept, * therefore, creating_session is always NULL. */ rv = soft_pin_expired_check(object_p); if (rv != CKR_OK) { SES_REFRELE(session_p, lock_held); return (rv); } rv = soft_object_write_access_check(session_p, object_p); if (rv != CKR_OK) { SES_REFRELE(session_p, lock_held); return (rv); } /* * Set OBJECT_IS_DELETING flag so any access to this * object will be rejected. */ (void) pthread_mutex_lock(&object_p->object_mutex); if (object_p->obj_delete_sync & OBJECT_IS_DELETING) { (void) pthread_mutex_unlock(&object_p->object_mutex); SES_REFRELE(session_p, lock_held); return (CKR_OBJECT_HANDLE_INVALID); } object_p->obj_delete_sync |= OBJECT_IS_DELETING; (void) pthread_mutex_unlock(&object_p->object_mutex); SES_REFRELE(session_p, lock_held); /* * Delete a token object by calling soft_delete_token_object() * with the second argument B_TRUE indicating to delete the * object from keystore and the third argument B_FALSE * indicating that the caller does not hold the slot mutex. */ soft_delete_token_object(object_p, B_TRUE, B_FALSE); return (CKR_OK); } /* * Switch to the creating_session, which actually holds the object. * If we use the wrong session in the call to soft_delete_object(), * deletion will silently fail, and we'll leak memory until * C_CloseSession is called (which, if metaslot is active, may be * never). */ if (hSession != creating_session) { SES_REFRELE(session_p, lock_held); rv = handle2session(creating_session, &session_p); if (rv != CKR_OK) return (rv); } /* * Set OBJECT_IS_DELETING flag so any access to this * object will be rejected. */ (void) pthread_mutex_lock(&object_p->object_mutex); if (object_p->obj_delete_sync & OBJECT_IS_DELETING) { (void) pthread_mutex_unlock(&object_p->object_mutex); SES_REFRELE(session_p, lock_held); return (CKR_OBJECT_HANDLE_INVALID); } object_p->obj_delete_sync |= OBJECT_IS_DELETING; (void) pthread_mutex_unlock(&object_p->object_mutex); /* * Delete an object by calling soft_delete_object() * with a FALSE boolean argument indicating that * the caller does not hold the session lock. */ soft_delete_object(session_p, object_p, B_FALSE, B_FALSE); /* * Decrement the session reference count. * We do not hold the session lock. */ SES_REFRELE(session_p, lock_held); return (rv); } CK_RV C_GetAttributeValue(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) { CK_RV rv = CKR_OK, rv1 = CKR_OK; soft_object_t *object_p; soft_session_t *session_p; boolean_t lock_held = B_FALSE; ulong_t i; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if ((pTemplate == NULL) || (ulCount == 0)) { /* * Decrement the session reference count. * We do not hold the session lock. */ SES_REFRELE(session_p, lock_held); return (CKR_ARGUMENTS_BAD); } /* Obtain the object pointer. */ HANDLE2OBJECT(hObject, object_p, rv); if (rv != CKR_OK) { /* * Decrement the session reference count. * We do not hold the session lock. */ SES_REFRELE(session_p, lock_held); return (rv); } if (IS_TOKEN_OBJECT(object_p)) { rv = soft_keystore_load_latest_object(object_p); if (rv != CKR_OK) { OBJ_REFRELE(object_p); SES_REFRELE(session_p, lock_held); return (rv); } } /* Acquire the lock on the object. */ (void) pthread_mutex_lock(&object_p->object_mutex); for (i = 0; i < ulCount; i++) { /* * Get the value of each attribute in the template. * (We must process EVERY attribute in the template.) */ rv = soft_get_attribute(object_p, &pTemplate[i]); if (rv != CKR_OK) /* At least we catch some type of error. */ rv1 = rv; } /* Release the object lock */ (void) pthread_mutex_unlock(&object_p->object_mutex); /* * Decrement the session reference count. * We do not hold the session lock. */ OBJ_REFRELE(object_p); SES_REFRELE(session_p, lock_held); rv = rv1; return (rv); } CK_RV C_SetAttributeValue(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) { CK_RV rv = CKR_OK; soft_object_t *object_p; soft_object_t *new_object = NULL; soft_session_t *session_p; boolean_t lock_held = B_FALSE; ulong_t i; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if ((pTemplate == NULL) || (ulCount == 0)) { /* * Decrement the session reference count. * We do not hold the session lock. */ SES_REFRELE(session_p, lock_held); return (CKR_ARGUMENTS_BAD); } /* Obtain the object pointer. */ HANDLE2OBJECT(hObject, object_p, rv); if (rv != CKR_OK) { /* * Decrement the session reference count. * We do not hold the session lock. */ SES_REFRELE(session_p, lock_held); return (rv); } if (object_p->bool_attr_mask & NOT_MODIFIABLE_BOOL_ON) { rv = CKR_ATTRIBUTE_READ_ONLY; goto fail_1; } /* * Start working on the object, so we need to set the write lock so that * no one can write to it but still can read it. */ if (IS_TOKEN_OBJECT(object_p)) { rv = soft_keystore_load_latest_object(object_p); if (rv != CKR_OK) { goto fail_1; } } /* * Copy the old object to a new object. We work on the copied * version because in case of error we still keep the old one * intact. * The 3rd argument with SOFT_SET_ATTR_VALUE value indicates that * not everything will be duplicated for C_SetAttributeValue. * Information not duplicated are those attributes that are not * modifiable. */ (void) pthread_mutex_lock(&object_p->object_mutex); rv = soft_copy_object(object_p, &new_object, SOFT_SET_ATTR_VALUE, NULL); if ((rv != CKR_OK) || (new_object == NULL)) { /* Most likely we ran out of space. */ (void) pthread_mutex_unlock(&object_p->object_mutex); /* * Decrement the session reference count. * We do not hold the session lock. */ goto fail_1; } /* * No need to hold the lock on the old object, because we * will be working on the new scratch object. */ (void) pthread_mutex_unlock(&object_p->object_mutex); rv = soft_object_write_access_check(session_p, new_object); if (rv != CKR_OK) { goto fail; } for (i = 0; i < ulCount; i++) { /* Set the requested attribute into the new object. */ rv = soft_set_attribute(new_object, &pTemplate[i], B_FALSE); if (rv != CKR_OK) { goto fail; } } /* * We've successfully set all the requested attributes. * Merge the new object with the old object, then destory * the new one. The reason to do the merging is because we * have to keep the original object handle (address of object). */ (void) pthread_mutex_lock(&object_p->object_mutex); soft_merge_object(object_p, new_object); /* * The object has been modified, so we write it back to keystore. */ if (IS_TOKEN_OBJECT(object_p)) { object_p->version++; rv = soft_modify_object_to_keystore(object_p); } (void) pthread_mutex_unlock(&object_p->object_mutex); free(new_object); /* * Decrement the session reference count. * We do not hold the session lock. */ OBJ_REFRELE(object_p); SES_REFRELE(session_p, lock_held); return (rv); fail: soft_cleanup_object(new_object); free(new_object); fail_1: OBJ_REFRELE(object_p); SES_REFRELE(session_p, lock_held); return (rv); } /*ARGSUSED*/ CK_RV C_GetObjectSize(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ULONG_PTR pulSize) { if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); return (CKR_FUNCTION_NOT_SUPPORTED); } CK_RV C_FindObjectsInit(CK_SESSION_HANDLE sh, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) { CK_RV rv; soft_session_t *session_p; boolean_t lock_held = B_TRUE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(sh, &session_p); if (rv != CKR_OK) return (rv); /* Check the arguments */ if ((ulCount > 0) && (pTemplate == NULL)) { /* decrement the session count, we do not hold the lock */ lock_held = B_FALSE; SES_REFRELE(session_p, lock_held); return (CKR_ARGUMENTS_BAD); } /* Acquire the session lock */ (void) pthread_mutex_lock(&session_p->session_mutex); /* Check to see if find operation is already active */ if (session_p->find_objects.flags & CRYPTO_OPERATION_ACTIVE) { /* decrement the session count, and unlock the mutex */ SES_REFRELE(session_p, lock_held); return (CKR_OPERATION_ACTIVE); } else { /* * This active flag will remain ON until application calls * C_FindObjectsFinal. */ session_p->find_objects.flags = CRYPTO_OPERATION_ACTIVE; } (void) pthread_mutex_unlock(&session_p->session_mutex); rv = soft_find_objects_init(session_p, pTemplate, ulCount); if (rv != CKR_OK) { (void) pthread_mutex_lock(&session_p->session_mutex); session_p->find_objects.flags = 0; (void) pthread_mutex_unlock(&session_p->session_mutex); } /* decrement the session count, and unlock the mutex */ lock_held = B_FALSE; SES_REFRELE(session_p, lock_held); return (rv); } CK_RV C_FindObjects(CK_SESSION_HANDLE sh, CK_OBJECT_HANDLE_PTR phObject, CK_ULONG ulMaxObjectCount, CK_ULONG_PTR pulObjectCount) { soft_session_t *session_p; CK_RV rv = CKR_OK; boolean_t lock_held = B_TRUE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(sh, &session_p); if (rv != CKR_OK) return (rv); /* check for invalid arguments */ if (((phObject == NULL) && (ulMaxObjectCount != 0)) || (pulObjectCount == NULL)) { /* decrement the session count, we do not hold the lock */ lock_held = B_FALSE; SES_REFRELE(session_p, lock_held); return (CKR_ARGUMENTS_BAD); } if (ulMaxObjectCount == 0) { /* don't need to do anything, just return */ *pulObjectCount = 0; /* decrement the session count, we do not hold the lock */ lock_held = B_FALSE; SES_REFRELE(session_p, lock_held); return (CKR_OK); } /* Acquire the session lock */ (void) pthread_mutex_lock(&session_p->session_mutex); /* Check to see if find operation is active */ if (!(session_p->find_objects.flags & CRYPTO_OPERATION_ACTIVE)) { SES_REFRELE(session_p, lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } soft_find_objects(session_p, phObject, ulMaxObjectCount, pulObjectCount); /* decrement the session count, and release the lock */ SES_REFRELE(session_p, lock_held); return (rv); } CK_RV C_FindObjectsFinal(CK_SESSION_HANDLE sh) { soft_session_t *session_p; CK_RV rv; boolean_t lock_held = B_TRUE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(sh, &session_p); if (rv != CKR_OK) return (rv); /* Acquire the session lock */ (void) pthread_mutex_lock(&session_p->session_mutex); /* Check to see if find operation is active */ if (!(session_p->find_objects.flags & CRYPTO_OPERATION_ACTIVE)) { SES_REFRELE(session_p, lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } soft_find_objects_final(session_p); /* decrement the session count, and release the lock */ SES_REFRELE(session_p, lock_held); return (rv); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2020 Joyent, Inc. */ #ifndef _SOFTOBJECT_H #define _SOFTOBJECT_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include "softKeystoreUtil.h" #include "softSession.h" #define SOFTTOKEN_OBJECT_MAGIC 0xECF0B002 #define SOFT_CREATE_OBJ 1 #define SOFT_GEN_KEY 2 #define SOFT_DERIVE_KEY_DH 3 /* for CKM_DH_PKCS_DERIVE */ #define SOFT_DERIVE_KEY_OTHER 4 /* for CKM_MD5_KEY_DERIVATION and */ /* CKM_SHA1_KEY_DERIVATION */ #define SOFT_UNWRAP_KEY 5 #define SOFT_CREATE_OBJ_INT 6 /* internal object creation */ typedef struct biginteger { CK_BYTE *big_value; CK_ULONG big_value_len; } biginteger_t; /* * Secret key Struct */ typedef struct secret_key_obj { CK_BYTE *sk_value; CK_ULONG sk_value_len; void *key_sched; size_t keysched_len; } secret_key_obj_t; /* * PKCS11: RSA Public Key Object Attributes */ typedef struct rsa_pub_key { biginteger_t modulus; CK_ULONG modulus_bits; biginteger_t pub_exponent; } rsa_pub_key_t; /* * PKCS11: DSA Public Key Object Attributes */ typedef struct dsa_pub_key { biginteger_t prime; biginteger_t subprime; biginteger_t base; biginteger_t value; } dsa_pub_key_t; /* * PKCS11: Diffie-Hellman Public Key Object Attributes */ typedef struct dh_pub_key { biginteger_t prime; biginteger_t base; biginteger_t value; } dh_pub_key_t; /* * PKCS11: X9.42 Diffie-Hellman Public Key Object Attributes */ typedef struct dh942_pub_key { biginteger_t prime; biginteger_t base; biginteger_t subprime; biginteger_t value; } dh942_pub_key_t; /* * PKCS11: Elliptic Curve Public Key Object Attributes */ typedef struct ec_pub_key { biginteger_t param; biginteger_t point; } ec_pub_key_t; /* * Public Key Main Struct */ typedef struct public_key_obj { union { rsa_pub_key_t rsa_pub_key; /* RSA public key */ dsa_pub_key_t dsa_pub_key; /* DSA public key */ dh_pub_key_t dh_pub_key; /* DH public key */ dh942_pub_key_t dh942_pub_key; /* DH9.42 public key */ ec_pub_key_t ec_pub_key; /* Elliptic Curve public key */ } key_type_u; } public_key_obj_t; /* * PKCS11: RSA Private Key Object Attributes */ typedef struct rsa_pri_key { biginteger_t modulus; biginteger_t pub_exponent; biginteger_t pri_exponent; biginteger_t prime_1; biginteger_t prime_2; biginteger_t exponent_1; biginteger_t exponent_2; biginteger_t coefficient; } rsa_pri_key_t; /* * PKCS11: DSA Private Key Object Attributes */ typedef struct dsa_pri_key { biginteger_t prime; biginteger_t subprime; biginteger_t base; biginteger_t value; } dsa_pri_key_t; /* * PKCS11: Diffie-Hellman Private Key Object Attributes */ typedef struct dh_pri_key { biginteger_t prime; biginteger_t base; biginteger_t value; CK_ULONG value_bits; } dh_pri_key_t; /* * PKCS11: X9.42 Diffie-Hellman Private Key Object Attributes */ typedef struct dh942_pri_key { biginteger_t prime; biginteger_t base; biginteger_t subprime; biginteger_t value; } dh942_pri_key_t; /* * PKCS11: Elliptic Curve Private Key Object Attributes */ typedef struct ec_pri_key { biginteger_t param; biginteger_t value; } ec_pri_key_t; /* * Private Key Main Struct */ typedef struct private_key_obj { union { rsa_pri_key_t rsa_pri_key; /* RSA private key */ dsa_pri_key_t dsa_pri_key; /* DSA private key */ dh_pri_key_t dh_pri_key; /* DH private key */ dh942_pri_key_t dh942_pri_key; /* DH9.42 private key */ ec_pri_key_t ec_pri_key; /* Elliptic Curve private key */ } key_type_u; } private_key_obj_t; /* * PKCS11: DSA Domain Parameters Object Attributes */ typedef struct dsa_dom_key { biginteger_t prime; biginteger_t subprime; biginteger_t base; CK_ULONG prime_bits; } dsa_dom_key_t; /* * PKCS11: Diffie-Hellman Domain Parameters Object Attributes */ typedef struct dh_dom_key { biginteger_t prime; biginteger_t base; CK_ULONG prime_bits; } dh_dom_key_t; /* * PKCS11: X9.42 Diffie-Hellman Domain Parameters Object Attributes */ typedef struct dh942_dom_key { biginteger_t prime; biginteger_t base; biginteger_t subprime; CK_ULONG prime_bits; CK_ULONG subprime_bits; } dh942_dom_key_t; /* * Domain Parameters Main Struct */ typedef struct domain_obj { union { dsa_dom_key_t dsa_dom_key; /* DSA domain parameters */ dh_dom_key_t dh_dom_key; /* DH domain parameters */ dh942_dom_key_t dh942_dom_key; /* DH9.42 domain parameters */ } key_type_u; } domain_obj_t; typedef struct cert_attr_type { CK_BYTE *value; CK_ULONG length; } cert_attr_t; /* * X.509 Public Key Certificate Structure. * This structure contains only the attributes that are * NOT modifiable after creation. * ID, ISSUER, and SUBJECT attributes are kept in the extra_attrlistp * record. */ typedef struct x509_cert { cert_attr_t *subject; /* DER encoding of certificate subject name */ cert_attr_t *value; /* BER encoding of the cert */ } x509_cert_t; /* * X.509 Attribute Certificiate Structure * This structure contains only the attributes that are * NOT modifiable after creation. * AC_ISSUER, SERIAL_NUMBER, and ATTR_TYPES are kept in the * extra_attrlistp record so they may be modified. */ typedef struct x509_attr_cert { cert_attr_t *owner; /* DER encoding of attr cert subject field */ cert_attr_t *value; /* BER encoding of cert */ } x509_attr_cert_t; /* * Certificate Object Main Struct */ typedef struct certificate_obj { CK_CERTIFICATE_TYPE certificate_type; union { x509_cert_t x509; x509_attr_cert_t x509_attr; } cert_type_u; } certificate_obj_t; /* * This structure is used to hold the attributes in the * Extra Attribute List. */ typedef struct attribute_info { CK_ATTRIBUTE attr; struct attribute_info *next; } attribute_info_t; typedef attribute_info_t *CK_ATTRIBUTE_INFO_PTR; /* * This is the main structure of the Objects. */ typedef struct object { avl_node_t node; CK_OBJECT_HANDLE handle; /* Generic common fields. Always present */ uint_t version; /* for token objects only */ CK_OBJECT_CLASS class; CK_KEY_TYPE key_type; CK_CERTIFICATE_TYPE cert_type; ulong_t magic_marker; uint64_t bool_attr_mask; /* see below */ CK_MECHANISM_TYPE mechanism; uchar_t object_type; /* see below */ struct ks_obj_handle ks_handle; /* keystore handle */ /* Fields for access and arbitration */ pthread_mutex_t object_mutex; struct object *next; struct object *prev; /* Extra non-boolean attribute list */ CK_ATTRIBUTE_INFO_PTR extra_attrlistp; /* For each object, only one of these object classes is presented */ union { public_key_obj_t *public_key; private_key_obj_t *private_key; secret_key_obj_t *secret_key; domain_obj_t *domain; certificate_obj_t *certificate; } object_class_u; /* Session handle that the object belongs to */ CK_SESSION_HANDLE session_handle; uint32_t obj_refcnt; /* object reference count */ pthread_cond_t obj_free_cond; /* cond variable for signal and wait */ uint32_t obj_delete_sync; /* object delete sync flags */ } soft_object_t; typedef struct find_context { soft_object_t **objs_found; CK_ULONG num_results; CK_ULONG next_result_index; /* next result object to return */ } find_context_t; /* * The following structure is used to link the to-be-freed session * objects into a linked list. The objects on this linked list have * not yet been freed via free() after C_DestroyObject() call; instead * they are added to this list. The actual free will take place when * the number of objects queued reaches MAX_OBJ_TO_BE_FREED, at which * time the first object in the list will be freed. */ #define MAX_OBJ_TO_BE_FREED 300 typedef struct obj_to_be_freed_list { struct object *first; /* points to the first obj in the list */ struct object *last; /* points to the last obj in the list */ uint32_t count; /* current total objs in the list */ pthread_mutex_t obj_to_be_free_mutex; } obj_to_be_freed_list_t; /* * Object type */ #define SESSION_PUBLIC 0 /* CKA_TOKEN = 0, CKA_PRIVATE = 0 */ #define SESSION_PRIVATE 1 /* CKA_TOKEN = 0, CKA_PRIVATE = 1 */ #define TOKEN_PUBLIC 2 /* CKA_TOKEN = 1, CKA_PRIVATE = 0 */ #define TOKEN_PRIVATE 3 /* CKA_TOKEN = 1, CKA_PRIVATE = 1 */ #define TOKEN_OBJECT 2 #define PRIVATE_OBJECT 1 typedef enum { ALL_TOKEN = 0, PUBLIC_TOKEN = 1, PRIVATE_TOKEN = 2 } token_obj_type_t; #define IS_TOKEN_OBJECT(objp) \ ((objp->object_type == TOKEN_PUBLIC) || \ (objp->object_type == TOKEN_PRIVATE)) /* * Types associated with copying object's content */ #define SOFT_SET_ATTR_VALUE 1 /* for C_SetAttributeValue */ #define SOFT_COPY_OBJECT 2 /* for C_CopyObject */ #define SOFT_COPY_OBJ_ORIG_SH 3 /* for copying an object but keeps */ /* the original session handle */ /* * The following definitions are the shortcuts */ /* * RSA Public Key Object Attributes */ #define OBJ_PUB(o) \ ((o)->object_class_u.public_key) #define KEY_PUB_RSA(k) \ &((k)->key_type_u.rsa_pub_key) #define OBJ_PUB_RSA_MOD(o) \ &((o)->object_class_u.public_key->key_type_u.rsa_pub_key.modulus) #define KEY_PUB_RSA_MOD(k) \ &((k)->key_type_u.rsa_pub_key.modulus) #define OBJ_PUB_RSA_PUBEXPO(o) \ &((o)->object_class_u.public_key->key_type_u.rsa_pub_key.pub_exponent) #define KEY_PUB_RSA_PUBEXPO(k) \ &((k)->key_type_u.rsa_pub_key.pub_exponent) #define OBJ_PUB_RSA_MOD_BITS(o) \ ((o)->object_class_u.public_key->key_type_u.rsa_pub_key.modulus_bits) #define KEY_PUB_RSA_MOD_BITS(k) \ ((k)->key_type_u.rsa_pub_key.modulus_bits) /* * DSA Public Key Object Attributes */ #define KEY_PUB_DSA(k) \ &((k)->key_type_u.dsa_pub_key) #define OBJ_PUB_DSA_PRIME(o) \ &((o)->object_class_u.public_key->key_type_u.dsa_pub_key.prime) #define KEY_PUB_DSA_PRIME(k) \ &((k)->key_type_u.dsa_pub_key.prime) #define OBJ_PUB_DSA_SUBPRIME(o) \ &((o)->object_class_u.public_key->key_type_u.dsa_pub_key.subprime) #define KEY_PUB_DSA_SUBPRIME(k) \ &((k)->key_type_u.dsa_pub_key.subprime) #define OBJ_PUB_DSA_BASE(o) \ &((o)->object_class_u.public_key->key_type_u.dsa_pub_key.base) #define KEY_PUB_DSA_BASE(k) \ &((k)->key_type_u.dsa_pub_key.base) #define OBJ_PUB_DSA_VALUE(o) \ &((o)->object_class_u.public_key->key_type_u.dsa_pub_key.value) #define KEY_PUB_DSA_VALUE(k) \ &((k)->key_type_u.dsa_pub_key.value) /* * Diffie-Hellman Public Key Object Attributes */ #define KEY_PUB_DH(k) \ &((k)->key_type_u.dh_pub_key) #define OBJ_PUB_DH_PRIME(o) \ &((o)->object_class_u.public_key->key_type_u.dh_pub_key.prime) #define KEY_PUB_DH_PRIME(k) \ &((k)->key_type_u.dh_pub_key.prime) #define OBJ_PUB_DH_BASE(o) \ &((o)->object_class_u.public_key->key_type_u.dh_pub_key.base) #define KEY_PUB_DH_BASE(k) \ &((k)->key_type_u.dh_pub_key.base) #define OBJ_PUB_DH_VALUE(o) \ &((o)->object_class_u.public_key->key_type_u.dh_pub_key.value) #define KEY_PUB_DH_VALUE(k) \ &((k)->key_type_u.dh_pub_key.value) /* * X9.42 Diffie-Hellman Public Key Object Attributes */ #define KEY_PUB_DH942(k) \ &((k)->key_type_u.dh942_pub_key) #define OBJ_PUB_DH942_PRIME(o) \ &((o)->object_class_u.public_key->key_type_u.dh942_pub_key.prime) #define KEY_PUB_DH942_PRIME(k) \ &((k)->key_type_u.dh942_pub_key.prime) #define OBJ_PUB_DH942_BASE(o) \ &((o)->object_class_u.public_key->key_type_u.dh942_pub_key.base) #define KEY_PUB_DH942_BASE(k) \ &((k)->key_type_u.dh942_pub_key.base) #define OBJ_PUB_DH942_SUBPRIME(o) \ &((o)->object_class_u.public_key->key_type_u.dh942_pub_key.subprime) #define KEY_PUB_DH942_SUBPRIME(k) \ &((k)->key_type_u.dh942_pub_key.subprime) #define OBJ_PUB_DH942_VALUE(o) \ &((o)->object_class_u.public_key->key_type_u.dh942_pub_key.value) #define KEY_PUB_DH942_VALUE(k) \ &((k)->key_type_u.dh942_pub_key.value) /* * Elliptic Curve Public Key Object Attributes */ #define KEY_PUB_EC(k) \ &((k)->key_type_u.ec_pub_key) #define OBJ_PUB_EC_POINT(o) \ &((o)->object_class_u.public_key->key_type_u.ec_pub_key.point) #define KEY_PUB_EC_POINT(k) \ &((k)->key_type_u.ec_pub_key.point) /* * RSA Private Key Object Attributes */ #define OBJ_PRI(o) \ ((o)->object_class_u.private_key) #define KEY_PRI_RSA(k) \ &((k)->key_type_u.rsa_pri_key) #define OBJ_PRI_RSA_MOD(o) \ &((o)->object_class_u.private_key->key_type_u.rsa_pri_key.modulus) #define KEY_PRI_RSA_MOD(k) \ &((k)->key_type_u.rsa_pri_key.modulus) #define OBJ_PRI_RSA_PUBEXPO(o) \ &((o)->object_class_u.private_key->key_type_u.rsa_pri_key.pub_exponent) #define KEY_PRI_RSA_PUBEXPO(k) \ &((k)->key_type_u.rsa_pri_key.pub_exponent) #define OBJ_PRI_RSA_PRIEXPO(o) \ &((o)->object_class_u.private_key->key_type_u.rsa_pri_key.pri_exponent) #define KEY_PRI_RSA_PRIEXPO(k) \ &((k)->key_type_u.rsa_pri_key.pri_exponent) #define OBJ_PRI_RSA_PRIME1(o) \ &((o)->object_class_u.private_key->key_type_u.rsa_pri_key.prime_1) #define KEY_PRI_RSA_PRIME1(k) \ &((k)->key_type_u.rsa_pri_key.prime_1) #define OBJ_PRI_RSA_PRIME2(o) \ &((o)->object_class_u.private_key->key_type_u.rsa_pri_key.prime_2) #define KEY_PRI_RSA_PRIME2(k) \ &((k)->key_type_u.rsa_pri_key.prime_2) #define OBJ_PRI_RSA_EXPO1(o) \ &((o)->object_class_u.private_key->key_type_u.rsa_pri_key.exponent_1) #define KEY_PRI_RSA_EXPO1(k) \ &((k)->key_type_u.rsa_pri_key.exponent_1) #define OBJ_PRI_RSA_EXPO2(o) \ &((o)->object_class_u.private_key->key_type_u.rsa_pri_key.exponent_2) #define KEY_PRI_RSA_EXPO2(k) \ &((k)->key_type_u.rsa_pri_key.exponent_2) #define OBJ_PRI_RSA_COEF(o) \ &((o)->object_class_u.private_key->key_type_u.rsa_pri_key.coefficient) #define KEY_PRI_RSA_COEF(k) \ &((k)->key_type_u.rsa_pri_key.coefficient) /* * DSA Private Key Object Attributes */ #define KEY_PRI_DSA(k) \ &((k)->key_type_u.dsa_pri_key) #define OBJ_PRI_DSA_PRIME(o) \ &((o)->object_class_u.private_key->key_type_u.dsa_pri_key.prime) #define KEY_PRI_DSA_PRIME(k) \ &((k)->key_type_u.dsa_pri_key.prime) #define OBJ_PRI_DSA_SUBPRIME(o) \ &((o)->object_class_u.private_key->key_type_u.dsa_pri_key.subprime) #define KEY_PRI_DSA_SUBPRIME(k) \ &((k)->key_type_u.dsa_pri_key.subprime) #define OBJ_PRI_DSA_BASE(o) \ &((o)->object_class_u.private_key->key_type_u.dsa_pri_key.base) #define KEY_PRI_DSA_BASE(k) \ &((k)->key_type_u.dsa_pri_key.base) #define OBJ_PRI_DSA_VALUE(o) \ &((o)->object_class_u.private_key->key_type_u.dsa_pri_key.value) #define KEY_PRI_DSA_VALUE(k) \ &((k)->key_type_u.dsa_pri_key.value) /* * Diffie-Hellman Private Key Object Attributes */ #define KEY_PRI_DH(k) \ &((k)->key_type_u.dh_pri_key) #define OBJ_PRI_DH_PRIME(o) \ &((o)->object_class_u.private_key->key_type_u.dh_pri_key.prime) #define KEY_PRI_DH_PRIME(k) \ &((k)->key_type_u.dh_pri_key.prime) #define OBJ_PRI_DH_BASE(o) \ &((o)->object_class_u.private_key->key_type_u.dh_pri_key.base) #define KEY_PRI_DH_BASE(k) \ &((k)->key_type_u.dh_pri_key.base) #define OBJ_PRI_DH_VALUE(o) \ &((o)->object_class_u.private_key->key_type_u.dh_pri_key.value) #define KEY_PRI_DH_VALUE(k) \ &((k)->key_type_u.dh_pri_key.value) #define OBJ_PRI_DH_VAL_BITS(o) \ ((o)->object_class_u.private_key->key_type_u.dh_pri_key.value_bits) #define KEY_PRI_DH_VAL_BITS(k) \ ((k)->key_type_u.dh_pri_key.value_bits) /* * X9.42 Diffie-Hellman Private Key Object Attributes */ #define KEY_PRI_DH942(k) \ &((k)->key_type_u.dh942_pri_key) #define OBJ_PRI_DH942_PRIME(o) \ &((o)->object_class_u.private_key->key_type_u.dh942_pri_key.prime) #define KEY_PRI_DH942_PRIME(k) \ &((k)->key_type_u.dh942_pri_key.prime) #define OBJ_PRI_DH942_BASE(o) \ &((o)->object_class_u.private_key->key_type_u.dh942_pri_key.base) #define KEY_PRI_DH942_BASE(k) \ &((k)->key_type_u.dh942_pri_key.base) #define OBJ_PRI_DH942_SUBPRIME(o) \ &((o)->object_class_u.private_key->key_type_u.dh942_pri_key.subprime) #define KEY_PRI_DH942_SUBPRIME(k) \ &((k)->key_type_u.dh942_pri_key.subprime) #define OBJ_PRI_DH942_VALUE(o) \ &((o)->object_class_u.private_key->key_type_u.dh942_pri_key.value) #define KEY_PRI_DH942_VALUE(k) \ &((k)->key_type_u.dh942_pri_key.value) /* * Elliptic Curve Private Key Object Attributes */ #define KEY_PRI_EC(k) \ &((k)->key_type_u.ec_pri_key) #define OBJ_PRI_EC_VALUE(o) \ &((o)->object_class_u.private_key->key_type_u.ec_pri_key.value) #define KEY_PRI_EC_VALUE(k) \ &((k)->key_type_u.ec_pri_key.value) /* * DSA Domain Parameters Object Attributes */ #define OBJ_DOM(o) \ ((o)->object_class_u.domain) #define KEY_DOM_DSA(k) \ &((k)->key_type_u.dsa_dom_key) #define OBJ_DOM_DSA_PRIME(o) \ &((o)->object_class_u.domain->key_type_u.dsa_dom_key.prime) #define KEY_DOM_DSA_PRIME(k) \ &((k)->key_type_u.dsa_dom_key.prime) #define OBJ_DOM_DSA_SUBPRIME(o) \ &((o)->object_class_u.domain->key_type_u.dsa_dom_key.subprime) #define KEY_DOM_DSA_SUBPRIME(k) \ &((k)->key_type_u.dsa_dom_key.subprime) #define OBJ_DOM_DSA_BASE(o) \ &((o)->object_class_u.domain->key_type_u.dsa_dom_key.base) #define KEY_DOM_DSA_BASE(k) \ &((k)->key_type_u.dsa_dom_key.base) #define OBJ_DOM_DSA_PRIME_BITS(o) \ ((o)->object_class_u.domain->key_type_u.dsa_dom_key.prime_bits) /* * Diffie-Hellman Domain Parameters Object Attributes */ #define KEY_DOM_DH(k) \ &((k)->key_type_u.dh_dom_key) #define OBJ_DOM_DH_PRIME(o) \ &((o)->object_class_u.domain->key_type_u.dh_dom_key.prime) #define KEY_DOM_DH_PRIME(k) \ &((k)->key_type_u.dh_dom_key.prime) #define OBJ_DOM_DH_BASE(o) \ &((o)->object_class_u.domain->key_type_u.dh_dom_key.base) #define KEY_DOM_DH_BASE(k) \ &((k)->key_type_u.dh_dom_key.base) #define OBJ_DOM_DH_PRIME_BITS(o) \ ((o)->object_class_u.domain->key_type_u.dh_dom_key.prime_bits) /* * X9.42 Diffie-Hellman Domain Parameters Object Attributes */ #define KEY_DOM_DH942(k) \ &((k)->key_type_u.dh942_dom_key) #define OBJ_DOM_DH942_PRIME(o) \ &((o)->object_class_u.domain->key_type_u.dh942_dom_key.prime) #define KEY_DOM_DH942_PRIME(k) \ &((k)->key_type_u.dh942_dom_key.prime) #define OBJ_DOM_DH942_BASE(o) \ &((o)->object_class_u.domain->key_type_u.dh942_dom_key.base) #define KEY_DOM_DH942_BASE(k) \ &((k)->key_type_u.dh942_dom_key.base) #define OBJ_DOM_DH942_SUBPRIME(o) \ &((o)->object_class_u.domain->key_type_u.dh942_dom_key.subprime) #define KEY_DOM_DH942_SUBPRIME(k) \ &((k)->key_type_u.dh942_dom_key.subprime) #define OBJ_DOM_DH942_PRIME_BITS(o) \ ((o)->object_class_u.domain->key_type_u.dh942_dom_key.prime_bits) #define OBJ_DOM_DH942_SUBPRIME_BITS(o) \ ((o)->object_class_u.domain->key_type_u.dh942_dom_key.subprime_bits) /* * Secret Key Object Attributes */ #define OBJ_SEC(o) \ ((o)->object_class_u.secret_key) #define OBJ_SEC_VALUE(o) \ ((o)->object_class_u.secret_key->sk_value) #define OBJ_SEC_VALUE_LEN(o) \ ((o)->object_class_u.secret_key->sk_value_len) #define OBJ_KEY_SCHED(o) \ ((o)->object_class_u.secret_key->key_sched) #define OBJ_KEY_SCHED_LEN(o) \ ((o)->object_class_u.secret_key->keysched_len) #define OBJ_CERT(o) \ ((o)->object_class_u.certificate) /* * X.509 Key Certificate object attributes */ #define X509_CERT(o) \ ((o)->object_class_u.certificate->cert_type_u.x509) #define X509_CERT_SUBJECT(o) \ ((o)->object_class_u.certificate->cert_type_u.x509.subject) #define X509_CERT_VALUE(o) \ ((o)->object_class_u.certificate->cert_type_u.x509.value) /* * X.509 Attribute Certificate object attributes */ #define X509_ATTR_CERT(o) \ ((o)->object_class_u.certificate->cert_type_u.x509_attr) #define X509_ATTR_CERT_OWNER(o) \ ((o)->object_class_u.certificate->cert_type_u.x509_attr.owner) #define X509_ATTR_CERT_VALUE(o) \ ((o)->object_class_u.certificate->cert_type_u.x509_attr.value) /* * key related attributes with CK_BBOOL data type */ #define DERIVE_BOOL_ON 0x00000001 #define LOCAL_BOOL_ON 0x00000002 #define SENSITIVE_BOOL_ON 0x00000004 #define SECONDARY_AUTH_BOOL_ON 0x00000008 #define ENCRYPT_BOOL_ON 0x00000010 #define DECRYPT_BOOL_ON 0x00000020 #define SIGN_BOOL_ON 0x00000040 #define SIGN_RECOVER_BOOL_ON 0x00000080 #define VERIFY_BOOL_ON 0x00000100 #define VERIFY_RECOVER_BOOL_ON 0x00000200 #define WRAP_BOOL_ON 0x00000400 #define UNWRAP_BOOL_ON 0x00000800 #define TRUSTED_BOOL_ON 0x00001000 #define EXTRACTABLE_BOOL_ON 0x00002000 #define ALWAYS_SENSITIVE_BOOL_ON 0x00004000 #define NEVER_EXTRACTABLE_BOOL_ON 0x00008000 #define NOT_MODIFIABLE_BOOL_ON 0x00010000 #define PUBLIC_KEY_DEFAULT (ENCRYPT_BOOL_ON|\ WRAP_BOOL_ON|\ VERIFY_BOOL_ON|\ VERIFY_RECOVER_BOOL_ON) #define PRIVATE_KEY_DEFAULT (DECRYPT_BOOL_ON|\ UNWRAP_BOOL_ON|\ SIGN_BOOL_ON|\ SIGN_RECOVER_BOOL_ON|\ EXTRACTABLE_BOOL_ON) #define SECRET_KEY_DEFAULT (ENCRYPT_BOOL_ON|\ DECRYPT_BOOL_ON|\ WRAP_BOOL_ON|\ UNWRAP_BOOL_ON|\ SIGN_BOOL_ON|\ VERIFY_BOOL_ON|\ EXTRACTABLE_BOOL_ON) /* * MAX_KEY_ATTR_BUFLEN * The maximum buffer size needed for public or private key attributes * should be 514 bytes. Just to be safe we give a little more space. */ #define MAX_KEY_ATTR_BUFLEN 1024 /* * Flag definitions for obj_delete_sync */ #define OBJECT_IS_DELETING 1 /* Object is in a deleting state */ #define OBJECT_REFCNT_WAITING 2 /* Waiting for object reference */ /* count to become zero */ #define HANDLE2OBJECT(hObject, object_p, rv) \ rv = handle2object(hObject, &(object_p), B_TRUE); #define HANDLE2OBJECT_DESTROY(hObject, object_p, rv) \ rv = handle2object(hObject, &(object_p), B_FALSE); #define OBJ_REFRELE(object_p) { \ (void) pthread_mutex_lock(&object_p->object_mutex); \ if ((--object_p->obj_refcnt) == 0 && \ (object_p->obj_delete_sync & OBJECT_REFCNT_WAITING)) { \ (void) pthread_cond_signal(&object_p->obj_free_cond); \ } \ (void) pthread_mutex_unlock(&object_p->object_mutex); \ } extern pthread_mutex_t soft_object_mutex; extern avl_tree_t soft_object_tree; /* * Function Prototypes. */ CK_RV handle2object(CK_OBJECT_HANDLE hObject, soft_object_t **object_p, boolean_t refhold); CK_ULONG set_objecthandle(soft_object_t *obj); void soft_cleanup_object(soft_object_t *objp); CK_RV soft_add_object(CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR objecthandle_p, soft_session_t *sp); void soft_delete_object(soft_session_t *sp, soft_object_t *objp, boolean_t force, boolean_t lock_held); void soft_cleanup_extra_attr(soft_object_t *object_p); CK_RV soft_copy_extra_attr(CK_ATTRIBUTE_INFO_PTR old_attrp, soft_object_t *object_p); void soft_cleanup_object_bigint_attrs(soft_object_t *object_p); CK_RV soft_build_object(CK_ATTRIBUTE_PTR template, CK_ULONG ulAttrNum, soft_object_t *new_object); CK_RV soft_build_secret_key_object(CK_ATTRIBUTE_PTR template, CK_ULONG ulAttrNum, soft_object_t *new_object, CK_ULONG mode, CK_ULONG key_len, CK_KEY_TYPE key_type); CK_RV soft_copy_object(soft_object_t *old_object, soft_object_t **new_object, CK_ULONG object_func, soft_session_t *sp); void soft_merge_object(soft_object_t *old_object, soft_object_t *new_object); CK_RV soft_get_attribute(soft_object_t *object_p, CK_ATTRIBUTE_PTR template); CK_RV soft_set_attribute(soft_object_t *object_p, CK_ATTRIBUTE_PTR template, boolean_t copy); CK_RV soft_set_common_storage_attribute(soft_object_t *object_p, CK_ATTRIBUTE_PTR template, boolean_t copy); CK_RV soft_get_public_value(soft_object_t *, CK_ATTRIBUTE_TYPE, uchar_t *, uint32_t *); CK_RV soft_get_private_value(soft_object_t *, CK_ATTRIBUTE_TYPE, uchar_t *, uint32_t *); CK_RV get_ulong_attr_from_object(CK_ULONG value, CK_ATTRIBUTE_PTR template); void copy_bigint_attr(biginteger_t *src, biginteger_t *dst); void soft_add_object_to_session(soft_object_t *, soft_session_t *); CK_RV soft_build_key(CK_ATTRIBUTE_PTR, CK_ULONG, soft_object_t *, CK_OBJECT_CLASS, CK_KEY_TYPE, CK_ULONG, CK_ULONG); CK_RV soft_copy_public_key_attr(public_key_obj_t *old_pub_key_obj_p, public_key_obj_t **new_pub_key_obj_p, CK_KEY_TYPE key_type); CK_RV soft_copy_private_key_attr(private_key_obj_t *old_pri_key_obj_p, private_key_obj_t **new_pri_key_obj_p, CK_KEY_TYPE key_type); CK_RV soft_copy_secret_key_attr(secret_key_obj_t *old_secret_key_obj_p, secret_key_obj_t **new_secret_key_obj_p); CK_RV soft_copy_domain_attr(domain_obj_t *old_domain_obj_p, domain_obj_t **new_domain_obj_p, CK_KEY_TYPE key_type); CK_RV soft_validate_attr(CK_ATTRIBUTE_PTR template, CK_ULONG ulAttrNum, CK_OBJECT_CLASS *class); CK_RV soft_find_objects_init(soft_session_t *sp, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount); void soft_find_objects_final(soft_session_t *sp); void soft_find_objects(soft_session_t *sp, CK_OBJECT_HANDLE *obj_found, CK_ULONG max_obj_requested, CK_ULONG *found_obj_count); void soft_process_find_attr(CK_OBJECT_CLASS *pclasses, CK_ULONG *num_result_pclasses, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount); boolean_t soft_find_match_attrs(soft_object_t *obj, CK_OBJECT_CLASS *pclasses, CK_ULONG num_pclasses, CK_ATTRIBUTE *tmpl_attr, CK_ULONG num_attr); CK_ATTRIBUTE_PTR get_extra_attr(CK_ATTRIBUTE_TYPE type, soft_object_t *obj); CK_RV get_string_from_template(CK_ATTRIBUTE_PTR dest, CK_ATTRIBUTE_PTR src); void string_attr_cleanup(CK_ATTRIBUTE_PTR template); void soft_cleanup_cert_object(soft_object_t *object_p); CK_RV soft_get_certificate_attribute(soft_object_t *object_p, CK_ATTRIBUTE_PTR template); CK_RV soft_set_certificate_attribute(soft_object_t *object_p, CK_ATTRIBUTE_PTR template, boolean_t copy); CK_RV soft_copy_certificate(certificate_obj_t *old, certificate_obj_t **new, CK_CERTIFICATE_TYPE type); CK_RV get_cert_attr_from_template(cert_attr_t **dest, CK_ATTRIBUTE_PTR src); /* Token object related function prototypes */ void soft_add_token_object_to_slot(soft_object_t *objp); void soft_remove_token_object_from_slot(soft_object_t *objp, boolean_t lock_held); void soft_delete_token_object(soft_object_t *objp, boolean_t persistent, boolean_t lock_held); void soft_delete_all_in_core_token_objects(token_obj_type_t type); void soft_validate_token_objects(boolean_t validate); CK_RV soft_object_write_access_check(soft_session_t *sp, soft_object_t *objp); CK_RV soft_pin_expired_check(soft_object_t *objp); CK_RV soft_copy_to_old_object(soft_object_t *new, soft_object_t *old); CK_RV soft_keystore_load_latest_object(soft_object_t *old_obj); CK_RV refresh_token_objects(); void bigint_attr_cleanup(biginteger_t *big); CK_RV soft_add_extra_attr(CK_ATTRIBUTE_PTR template, soft_object_t *object_p); CK_RV get_bigint_attr_from_template(biginteger_t *big, CK_ATTRIBUTE_PTR template); CK_RV dup_bigint_attr(biginteger_t *bi, CK_BYTE *buf, CK_ULONG buflen); #ifdef __cplusplus } #endif #endif /* _SOFTOBJECT_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2020 Joyent, Inc. */ #include #include #include #include #include #include #include "softGlobal.h" #include "softObject.h" #include "softSession.h" #include "softKeystore.h" #include "softKeystoreUtil.h" /* * Add an object to the session's object list. * * This function will acquire the lock on the session, and release * that lock after adding the object to the session's object list. */ void soft_add_object_to_session(soft_object_t *objp, soft_session_t *sp) { /* Acquire the session lock. */ (void) pthread_mutex_lock(&sp->session_mutex); /* Insert the new object in front of session's object list. */ if (sp->object_list == NULL) { sp->object_list = objp; objp->next = NULL; objp->prev = NULL; } else { sp->object_list->prev = objp; objp->next = sp->object_list; objp->prev = NULL; sp->object_list = objp; } /* Release the session lock. */ (void) pthread_mutex_unlock(&sp->session_mutex); } /* * Clean up and release the storage allocated to the object. * * The function is called either with the object lock being held * (by caller soft_delete_object()), or there is no object lock * yet (by soft_build_XXX_object() during creating an object). */ void soft_cleanup_object(soft_object_t *objp) { /* * Free the storage allocated to big integer attributes. */ soft_cleanup_object_bigint_attrs(objp); /* * Free the storage allocated to the extra attribute list. */ soft_cleanup_extra_attr(objp); /* * Free the storage allocated to certificate attributes. */ soft_cleanup_cert_object(objp); } /* * Create a new object. Copy the attributes that can be modified * (in the boolean attribute mask field and extra attribute list) * from the old object to the new object. * * The caller of this function holds the lock on the old object. */ CK_RV soft_copy_object(soft_object_t *old_object, soft_object_t **new_object, CK_ULONG object_func, soft_session_t *sp) { CK_RV rv = CKR_OK; soft_object_t *new_objp = NULL; CK_ATTRIBUTE_INFO_PTR attrp; /* Allocate new object. */ new_objp = calloc(1, sizeof (soft_object_t)); if (new_objp == NULL) return (CKR_HOST_MEMORY); new_objp->class = old_object->class; new_objp->bool_attr_mask = old_object->bool_attr_mask; new_objp->cert_type = old_object->cert_type; new_objp->object_type = old_object->object_type; attrp = old_object->extra_attrlistp; while (attrp) { /* * Copy the attribute_info struct from the old * object to a new attribute_info struct, and add * that new struct to the extra attribute list * of the new object. */ rv = soft_copy_extra_attr(attrp, new_objp); if (rv != CKR_OK) { soft_cleanup_extra_attr(new_objp); free(new_objp); return (rv); } attrp = attrp->next; } *new_object = new_objp; if (object_func == SOFT_SET_ATTR_VALUE) { /* done with copying all information that can be modified */ return (CKR_OK); } /* * Copy the rest of the object. * Certain fields that are not appropriate for coping will be * initialized. */ new_objp->key_type = old_object->key_type; new_objp->magic_marker = old_object->magic_marker; new_objp->mechanism = old_object->mechanism; switch (object_func) { case SOFT_COPY_OBJ_ORIG_SH: new_objp->session_handle = old_object->session_handle; break; case SOFT_COPY_OBJECT: /* * Save the session handle of the C_CopyObject function * in the new copy of the session object. */ new_objp->session_handle = sp->handle; break; } (void) pthread_cond_init(&(new_objp->obj_free_cond), NULL); (void) pthread_mutex_init(&(new_objp->object_mutex), NULL); /* copy key related information */ switch (new_objp->class) { case CKO_PUBLIC_KEY: rv = soft_copy_public_key_attr(OBJ_PUB(old_object), &(OBJ_PUB(new_objp)), new_objp->key_type); break; case CKO_PRIVATE_KEY: rv = soft_copy_private_key_attr(OBJ_PRI(old_object), &(OBJ_PRI(new_objp)), new_objp->key_type); break; case CKO_SECRET_KEY: rv = soft_copy_secret_key_attr(OBJ_SEC(old_object), &(OBJ_SEC(new_objp))); break; case CKO_DOMAIN_PARAMETERS: rv = soft_copy_domain_attr(OBJ_DOM(old_object), &(OBJ_DOM(new_objp)), new_objp->key_type); break; case CKO_CERTIFICATE: rv = soft_copy_certificate(OBJ_CERT(old_object), &(OBJ_CERT(new_objp)), new_objp->cert_type); break; default: /* should never be this case */ break; } if (rv != CKR_OK) { /* * don't need to cleanup the memory from failure of copying * any key related stuff. Each individual function for * copying key attr will free the memory if it fails */ soft_cleanup_extra_attr(new_objp); free(new_objp); } return (rv); } /* * Copy the attributes (in the boolean attribute mask field and * extra attribute list) from the new object back to the original * object. Also, clean up and release all the storage in the extra * attribute list of the original object. * * The caller of this function holds the lock on the old object. */ void soft_merge_object(soft_object_t *old_object, soft_object_t *new_object) { old_object->bool_attr_mask = new_object->bool_attr_mask; soft_cleanup_extra_attr(old_object); old_object->extra_attrlistp = new_object->extra_attrlistp; } /* * Sets *object_p to the soft_object_t corresponding to hObject. If * hObject is valid, and not in the deleting state, CKR_OK is returned, * otherwise an error is returned. If hObject is valid and refhold is B_TRUE, * the object is also held. */ CK_RV handle2object(CK_OBJECT_HANDLE hObject, soft_object_t **object_p, boolean_t refhold) { soft_object_t *obj; soft_object_t node; (void) memset(&node, 0, sizeof (node)); node.handle = hObject; VERIFY0(pthread_mutex_lock(&soft_object_mutex)); if ((obj = avl_find(&soft_object_tree, &node, NULL)) == NULL || obj->magic_marker != SOFTTOKEN_OBJECT_MAGIC) { VERIFY0(pthread_mutex_unlock(&soft_object_mutex)); return (CKR_OBJECT_HANDLE_INVALID); } (void) pthread_mutex_lock(&obj->object_mutex); VERIFY0(pthread_mutex_unlock(&soft_object_mutex)); if (obj->obj_delete_sync & OBJECT_IS_DELETING) { (void) pthread_mutex_unlock(&obj->object_mutex); return (CKR_OBJECT_HANDLE_INVALID); } if (refhold) obj->obj_refcnt++; (void) pthread_mutex_unlock(&obj->object_mutex); *object_p = obj; return (CKR_OK); } CK_ULONG set_objecthandle(soft_object_t *obj) { avl_index_t where; (void) pthread_mutex_lock(&soft_object_mutex); do { arc4random_buf(&obj->handle, sizeof (obj->handle)); if (obj->handle == CK_INVALID_HANDLE) continue; } while (avl_find(&soft_object_tree, obj, &where) != NULL); avl_insert(&soft_object_tree, obj, where); (void) pthread_mutex_unlock(&soft_object_mutex); return (obj->handle); } /* * Create a new object struct, and add it to the session's object list. */ CK_RV soft_add_object(CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR objecthandle_p, soft_session_t *sp) { CK_RV rv = CKR_OK; soft_object_t *new_objp = NULL; new_objp = calloc(1, sizeof (soft_object_t)); if (new_objp == NULL) { return (CKR_HOST_MEMORY); } new_objp->extra_attrlistp = NULL; /* * Validate attribute template and fill in the attributes * in the soft_object_t. */ rv = soft_build_object(pTemplate, ulCount, new_objp); if (rv != CKR_OK) { goto fail_cleanup1; } rv = soft_pin_expired_check(new_objp); if (rv != CKR_OK) { goto fail_cleanup2; } rv = soft_object_write_access_check(sp, new_objp); if (rv != CKR_OK) { goto fail_cleanup2; } /* Initialize the rest of stuffs in soft_object_t. */ (void) pthread_cond_init(&new_objp->obj_free_cond, NULL); (void) pthread_mutex_init(&new_objp->object_mutex, NULL); new_objp->magic_marker = SOFTTOKEN_OBJECT_MAGIC; new_objp->obj_refcnt = 0; new_objp->obj_delete_sync = 0; /* Write the new token object to the keystore */ if (IS_TOKEN_OBJECT(new_objp)) { if (!soft_keystore_status(KEYSTORE_INITIALIZED)) { rv = CKR_DEVICE_REMOVED; goto fail_cleanup2; } new_objp->version = 1; rv = soft_put_object_to_keystore(new_objp); if (rv != CKR_OK) { (void) pthread_cond_destroy(&new_objp->obj_free_cond); (void) pthread_mutex_destroy(&new_objp->object_mutex); goto fail_cleanup2; } new_objp->session_handle = CK_INVALID_HANDLE; soft_add_token_object_to_slot(new_objp); *objecthandle_p = set_objecthandle(new_objp); return (CKR_OK); } new_objp->session_handle = sp->handle; /* Add the new object to the session's object list. */ soft_add_object_to_session(new_objp, sp); *objecthandle_p = set_objecthandle(new_objp); return (CKR_OK); fail_cleanup2: /* * When any error occurs after soft_build_object(), we will need to * clean up the memory allocated by the soft_build_object(). */ soft_cleanup_object(new_objp); fail_cleanup1: if (new_objp) { /* * The storage allocated inside of this object should have * been cleaned up by the soft_build_object() if it failed. * Therefore, we can safely free the object. */ free(new_objp); } return (rv); } /* * Remove an object from the session's object list. * * The caller of this function holds the session lock. */ CK_RV soft_remove_object_from_session(soft_object_t *objp, soft_session_t *sp) { soft_object_t *tmp_objp; boolean_t found = B_FALSE; /* * Remove the object from the session's object list. */ if ((sp == NULL) || (sp->magic_marker != SOFTTOKEN_SESSION_MAGIC)) { return (CKR_SESSION_HANDLE_INVALID); } if ((sp->object_list == NULL) || (objp == NULL) || (objp->magic_marker != SOFTTOKEN_OBJECT_MAGIC)) { return (CKR_OBJECT_HANDLE_INVALID); } tmp_objp = sp->object_list; while (tmp_objp) { if (tmp_objp == objp) { found = B_TRUE; break; } tmp_objp = tmp_objp->next; } if (!found) return (CKR_OBJECT_HANDLE_INVALID); if (sp->object_list == objp) { /* Object is the first one in the list. */ if (objp->next) { sp->object_list = objp->next; objp->next->prev = NULL; } else { /* Object is the only one in the list. */ sp->object_list = NULL; } } else { /* Object is not the first one in the list. */ if (objp->next) { /* Object is in the middle of the list. */ objp->prev->next = objp->next; objp->next->prev = objp->prev; } else { /* Object is the last one in the list. */ objp->prev->next = NULL; } } return (CKR_OK); } /* * This function adds the to-be-freed session object to a linked list. * When the number of objects queued in the linked list reaches the * maximum threshold MAX_OBJ_TO_BE_FREED, it will free the first * object (FIFO) in the list. */ void object_delay_free(soft_object_t *objp) { soft_object_t *tmp; (void) pthread_mutex_lock(&obj_delay_freed.obj_to_be_free_mutex); /* Add the newly deleted object at the end of the list */ objp->next = NULL; if (obj_delay_freed.first == NULL) { obj_delay_freed.last = objp; obj_delay_freed.first = objp; } else { obj_delay_freed.last->next = objp; obj_delay_freed.last = objp; } if (++obj_delay_freed.count >= MAX_OBJ_TO_BE_FREED) { /* * Free the first object in the list only if * the total count reaches maximum threshold. */ obj_delay_freed.count--; tmp = obj_delay_freed.first->next; free(obj_delay_freed.first); obj_delay_freed.first = tmp; } (void) pthread_mutex_unlock(&obj_delay_freed.obj_to_be_free_mutex); } static void soft_delete_object_cleanup(soft_object_t *objp, boolean_t force) { /* Acquire the lock on the object. */ (void) pthread_mutex_lock(&objp->object_mutex); /* * Make sure another thread hasn't freed the object. */ if (objp->magic_marker != SOFTTOKEN_OBJECT_MAGIC) { (void) pthread_mutex_unlock(&objp->object_mutex); return; } /* * The deletion of an object must be blocked when the object * reference count is not zero. This means if any object related * operation starts prior to the delete object operation gets in, * the object deleting thread must wait for the non-deleting * operation to be completed before it can proceed the delete * operation. * * Unless we are being forced to shut everything down, this only * happens if the libraries _fini() is running not of someone * explicitly called C_Finalize(). */ if (force) objp->obj_refcnt = 0; while (objp->obj_refcnt != 0) { /* * We set the OBJECT_REFCNT_WAITING flag before we put * this deleting thread in a wait state, so other non-deleting * operation thread will signal to wake it up only when * the object reference count becomes zero and this flag * is set. */ objp->obj_delete_sync |= OBJECT_REFCNT_WAITING; (void) pthread_cond_wait(&objp->obj_free_cond, &objp->object_mutex); } objp->obj_delete_sync &= ~OBJECT_REFCNT_WAITING; /* Mark object as no longer valid. */ objp->magic_marker = 0; (void) pthread_cond_destroy(&objp->obj_free_cond); /* * Cleanup the contents of this object such as free all the * storage allocated for this object. */ soft_cleanup_object(objp); (void) pthread_mutex_lock(&soft_object_mutex); avl_remove(&soft_object_tree, objp); (void) pthread_mutex_unlock(&soft_object_mutex); /* Reset OBJECT_IS_DELETING flag. */ objp->obj_delete_sync &= ~OBJECT_IS_DELETING; (void) pthread_mutex_unlock(&objp->object_mutex); /* Destroy the object lock */ (void) pthread_mutex_destroy(&objp->object_mutex); /* Free the object itself */ if (IS_TOKEN_OBJECT(objp)) free(objp); else /* * Delay freeing the session object as S1WS/NSS uses session * objects for its SSL Handshake. */ (void) object_delay_free(objp); } /* * Delete an object: * - Remove the object from the session's object list. * Holding the lock on the session which the object was created at * is needed to do this. * - Release the storage allocated to the object. * * The boolean argument lock_held is used to indicate that whether * the caller holds the session lock or not. * - When called by soft_delete_all_objects_in_session() -- the * lock_held = TRUE. * * When the caller does not hold the session lock, this function * will acquire that lock in order to proceed, and also release * that lock before returning to caller. */ void soft_delete_object(soft_session_t *sp, soft_object_t *objp, boolean_t force, boolean_t lock_held) { /* * Check to see if the caller holds the lock on the session. * If not, we need to acquire that lock in order to proceed. */ if (!lock_held) { /* Acquire the session lock. */ (void) pthread_mutex_lock(&sp->session_mutex); } /* Remove the object from the session's object list first. */ if (soft_remove_object_from_session(objp, sp) != CKR_OK) { if (!lock_held) { (void) pthread_mutex_unlock(&sp->session_mutex); } return; } if (!lock_held) { /* * If the session lock is obtained by this function, * then release that lock after removing the object * from session's object list. * We want the releasing of the object storage to * be done without holding the session lock. */ (void) pthread_mutex_unlock(&sp->session_mutex); } soft_delete_object_cleanup(objp, force); } /* * Delete all the objects in a session. The caller holds the lock * on the session. */ void soft_delete_all_objects_in_session(soft_session_t *sp, boolean_t force) { soft_object_t *objp = sp->object_list; soft_object_t *objp1; /* Delete all the objects in the session. */ while (objp) { objp1 = objp->next; /* * Delete an object by calling soft_delete_object() * with a TRUE boolean argument indicating that * the caller holds the lock on the session. */ soft_delete_object(sp, objp, force, B_TRUE); objp = objp1; } } static CK_RV add_to_search_result(soft_object_t *obj, find_context_t *fcontext, CK_ULONG *num_result_alloc) { /* * allocate space for storing results if the currently * allocated space is not enough */ if (*num_result_alloc <= fcontext->num_results) { fcontext->objs_found = realloc(fcontext->objs_found, sizeof (soft_object_t *) * (*num_result_alloc + BUFSIZ)); if (fcontext->objs_found == NULL) { return (CKR_HOST_MEMORY); } *num_result_alloc += BUFSIZ; } (fcontext->objs_found)[(fcontext->num_results)++] = obj; return (CKR_OK); } static CK_RV search_for_objects(CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, find_context_t *fcontext) { soft_session_t *session_p; soft_object_t *obj; CK_OBJECT_CLASS pclasses[6]; /* classes attrs possibly exist */ CK_ULONG num_pclasses; /* number of possible classes */ CK_ULONG num_result_alloc = 0; /* spaces allocated for results */ CK_RV rv = CKR_OK; /* whether CKA_TOKEN flag specified or not */ boolean_t token_specified = B_FALSE; /* value of CKA_TOKEN flag, if specified */ boolean_t token_flag_val = B_FALSE; CK_ULONG i; if (ulCount > 0) { /* there are some search requirement */ soft_process_find_attr(pclasses, &num_pclasses, pTemplate, ulCount); } for (i = 0; i < ulCount; i++) { if (pTemplate[i].type == CKA_PRIVATE) { (void) pthread_mutex_lock(&soft_giant_mutex); if (soft_slot.userpin_change_needed) { (void) pthread_mutex_unlock(&soft_giant_mutex); return (CKR_PIN_EXPIRED); } (void) pthread_mutex_unlock(&soft_giant_mutex); } } /* * look through template and see if it explicitly specifies * whether we need to look for token objects or not */ for (i = 0; i < ulCount; i++) { if (pTemplate[i].type == CKA_TOKEN) { token_specified = B_TRUE; token_flag_val = *((CK_BBOOL *)pTemplate[i].pValue); break; } } /* * Need go through token objects if it explicitly say so, or * it is not mentioned in the template. And this will ONLY be * done when the keystore exists. Otherwise, we will skip re-loading * the token objects. * * If a session has not logged into the token, only public * objects, if any, will be searched. If a session is logged * into the token, all public and private objects in the keystore * are searched. */ if (((token_flag_val) || (!token_specified)) && soft_keystore_status(KEYSTORE_INITIALIZED)) { /* acquire token session lock */ (void) pthread_mutex_lock(&soft_slot.slot_mutex); rv = refresh_token_objects(); if (rv != CKR_OK) { (void) pthread_mutex_unlock(&soft_slot.slot_mutex); return (rv); } obj = soft_slot.token_object_list; while (obj) { (void) pthread_mutex_lock(&obj->object_mutex); if (((token_specified) && (ulCount > 1)) || ((!token_specified) && (ulCount > 0))) { if (soft_find_match_attrs(obj, pclasses, num_pclasses, pTemplate, ulCount)) { rv = add_to_search_result( obj, fcontext, &num_result_alloc); } } else { /* no search criteria, just record the object */ rv = add_to_search_result(obj, fcontext, &num_result_alloc); } (void) pthread_mutex_unlock(&obj->object_mutex); if (rv != CKR_OK) { (void) pthread_mutex_unlock (&soft_slot.slot_mutex); return (rv); } obj = obj->next; } (void) pthread_mutex_unlock(&soft_slot.slot_mutex); } if (token_flag_val) { /* no need to look through session objects */ return (rv); } /* Acquire the global session list lock */ (void) pthread_mutex_lock(&soft_sessionlist_mutex); /* * Go through all objects in each session. * Acquire individual session lock for the session * we are searching. */ session_p = soft_session_list; while (session_p) { (void) pthread_mutex_lock(&session_p->session_mutex); obj = session_p->object_list; while (obj) { (void) pthread_mutex_lock(&obj->object_mutex); if (ulCount > 0) { if (soft_find_match_attrs(obj, pclasses, num_pclasses, pTemplate, ulCount)) { rv = add_to_search_result( obj, fcontext, &num_result_alloc); } } else { /* no search criteria, just record the object */ rv = add_to_search_result(obj, fcontext, &num_result_alloc); } (void) pthread_mutex_unlock(&obj->object_mutex); if (rv != CKR_OK) { (void) pthread_mutex_unlock( &session_p->session_mutex); goto cleanup; } obj = obj->next; } (void) pthread_mutex_unlock(&session_p->session_mutex); session_p = session_p->next; } cleanup: /* Release the global session list lock */ (void) pthread_mutex_unlock(&soft_sessionlist_mutex); return (rv); } /* * Initialize the context for C_FindObjects() calls */ CK_RV soft_find_objects_init(soft_session_t *sp, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) { CK_RV rv = CKR_OK; CK_OBJECT_CLASS class; /* for soft_validate_attr(). Value unused */ find_context_t *fcontext; if (ulCount) { rv = soft_validate_attr(pTemplate, ulCount, &class); /* Make sure all attributes in template are valid */ if (rv != CKR_OK) { return (rv); } } /* prepare the find context */ fcontext = calloc(1, sizeof (find_context_t)); if (fcontext == NULL) { return (CKR_HOST_MEMORY); } rv = search_for_objects(pTemplate, ulCount, fcontext); if (rv != CKR_OK) { free(fcontext); return (rv); } /* store the find_context in the session */ sp->find_objects.context = (CK_VOID_PTR)fcontext; return (rv); } void soft_find_objects_final(soft_session_t *sp) { find_context_t *fcontext; fcontext = sp->find_objects.context; sp->find_objects.context = NULL; sp->find_objects.flags = 0; if (fcontext->objs_found != NULL) { free(fcontext->objs_found); } free(fcontext); } void soft_find_objects(soft_session_t *sp, CK_OBJECT_HANDLE *obj_found, CK_ULONG max_obj_requested, CK_ULONG *found_obj_count) { find_context_t *fcontext; CK_ULONG num_obj_found = 0; CK_ULONG i; soft_object_t *obj; fcontext = sp->find_objects.context; for (i = fcontext->next_result_index; ((num_obj_found < max_obj_requested) && (i < fcontext->num_results)); i++) { obj = fcontext->objs_found[i]; if (obj != NULL) { (void) pthread_mutex_lock(&obj->object_mutex); /* a sanity check to make sure the obj is still valid */ if (obj->magic_marker == SOFTTOKEN_OBJECT_MAGIC) { obj_found[num_obj_found] = obj->handle; num_obj_found++; } (void) pthread_mutex_unlock(&obj->object_mutex); } } fcontext->next_result_index = i; *found_obj_count = num_obj_found; } /* * Below are the token object related functions */ void soft_add_token_object_to_slot(soft_object_t *objp) { (void) pthread_mutex_lock(&soft_slot.slot_mutex); /* Insert the new object in front of slot's token object list. */ if (soft_slot.token_object_list == NULL) { soft_slot.token_object_list = objp; objp->next = NULL; objp->prev = NULL; } else { soft_slot.token_object_list->prev = objp; objp->next = soft_slot.token_object_list; objp->prev = NULL; soft_slot.token_object_list = objp; } (void) pthread_mutex_unlock(&soft_slot.slot_mutex); } void soft_remove_token_object_from_slot(soft_object_t *objp, boolean_t lock_held) { if (!lock_held) (void) pthread_mutex_lock(&soft_slot.slot_mutex); /* * Remove the object from the slot's token object list. */ if (soft_slot.token_object_list == objp) { /* Object is the first one in the list. */ if (objp->next) { soft_slot.token_object_list = objp->next; objp->next->prev = NULL; } else { /* Object is the only one in the list. */ soft_slot.token_object_list = NULL; } } else { /* Object is not the first one in the list. */ if (objp->next) { /* Object is in the middle of the list. */ objp->prev->next = objp->next; objp->next->prev = objp->prev; } else { /* Object is the last one in the list. */ objp->prev->next = NULL; } } if (!lock_held) (void) pthread_mutex_unlock(&soft_slot.slot_mutex); } void soft_delete_token_object(soft_object_t *objp, boolean_t persistent, boolean_t lock_held) { if (!lock_held) (void) pthread_mutex_lock(&soft_slot.slot_mutex); if (persistent) /* Delete the object from the keystore. */ (void) soft_keystore_del_obj(&objp->ks_handle, B_FALSE); /* Remove the object from the slot's token object list. */ soft_remove_token_object_from_slot(objp, B_TRUE); if (!lock_held) (void) pthread_mutex_unlock(&soft_slot.slot_mutex); soft_delete_object_cleanup(objp, B_FALSE); } void soft_delete_all_in_core_token_objects(token_obj_type_t type) { soft_object_t *objp; soft_object_t *objp1; (void) pthread_mutex_lock(&soft_slot.slot_mutex); objp = soft_slot.token_object_list; switch (type) { case PRIVATE_TOKEN: while (objp) { objp1 = objp->next; if (objp->object_type == TOKEN_PRIVATE) { soft_delete_token_object(objp, B_FALSE, B_TRUE); } objp = objp1; } break; case PUBLIC_TOKEN: while (objp) { objp1 = objp->next; if (objp->object_type == TOKEN_PUBLIC) { soft_delete_token_object(objp, B_FALSE, B_TRUE); } objp = objp1; } break; case ALL_TOKEN: while (objp) { objp1 = objp->next; soft_delete_token_object(objp, B_FALSE, B_TRUE); objp = objp1; } break; } (void) pthread_mutex_unlock(&soft_slot.slot_mutex); } /* * Mark all the token objects in the global list to be valid. */ void soft_validate_token_objects(boolean_t validate) { soft_object_t *objp; (void) pthread_mutex_lock(&soft_slot.slot_mutex); objp = soft_slot.token_object_list; while (objp) { if (validate) objp->magic_marker = SOFTTOKEN_OBJECT_MAGIC; else objp->magic_marker = 0; objp = objp->next; } (void) pthread_mutex_unlock(&soft_slot.slot_mutex); } /* * Verify user's write access rule to the token object. */ CK_RV soft_object_write_access_check(soft_session_t *sp, soft_object_t *objp) { /* * This function is called by C_CreateObject, C_CopyObject, * C_DestroyObject, C_SetAttributeValue, C_GenerateKey, * C_GenerateKeyPairs, C_DeriveKey. All of them will write * the token object to the keystore. */ (void) pthread_mutex_lock(&soft_giant_mutex); if (!soft_slot.authenticated) { (void) pthread_mutex_unlock(&soft_giant_mutex); /* User is not logged in */ if (sp->flags & CKF_RW_SESSION) { /* * For R/W Public Session: * we allow write access to public session or token * object, but not for private token/session object. */ if ((objp->object_type == TOKEN_PRIVATE) || (objp->object_type == SESSION_PRIVATE)) { return (CKR_USER_NOT_LOGGED_IN); } } else { /* * For R/O Public Session: * we allow write access to public session object. */ if (objp->object_type != SESSION_PUBLIC) return (CKR_SESSION_READ_ONLY); } } else { (void) pthread_mutex_unlock(&soft_giant_mutex); /* User is logged in */ if (!(sp->flags & CKF_RW_SESSION)) { /* * For R/O User Function Session: * we allow write access to public or private * session object, but not for public or private * token object. */ if ((objp->object_type == TOKEN_PUBLIC) || (objp->object_type == TOKEN_PRIVATE)) { return (CKR_SESSION_READ_ONLY); } } } return (CKR_OK); } /* * Verify if user is required to setpin when accessing the * private token/session object. */ CK_RV soft_pin_expired_check(soft_object_t *objp) { /* * This function is called by C_CreateObject, C_CopyObject, * C_DestroyObject, C_GenerateKey, * C_GenerateKeyPairs, C_DeriveKey. * All of them will return CKR_PIN_EXPIRED if the * "userpin_change_needed" is set. * * The following functions will not be necessary to call * this routine even though CKR_PIN_EXPIRED is one of the * valid error code they might return. These functions are: * C_EncryptInit, C_DecryptInit, C_DigestInit, C_SignInit, * C_SignRecoverInit, C_VerifyInit, C_VerifyRecoverInit. * This is because they will not get the object handle * before the above functions are called. */ (void) pthread_mutex_lock(&soft_giant_mutex); if (soft_slot.userpin_change_needed) { /* * Access private token/session object but user's * PIN is expired or never set. */ if ((objp->object_type == TOKEN_PRIVATE) || (objp->object_type == SESSION_PRIVATE)) { (void) pthread_mutex_unlock(&soft_giant_mutex); return (CKR_PIN_EXPIRED); } } (void) pthread_mutex_unlock(&soft_giant_mutex); return (CKR_OK); } /* * Copy the selected fields from new token object to old * token object. */ CK_RV soft_copy_to_old_object(soft_object_t *new, soft_object_t *old) { CK_RV rv = CKR_OK; CK_ATTRIBUTE_INFO_PTR attrp; old->class = new->class; old->bool_attr_mask = new->bool_attr_mask; soft_cleanup_extra_attr(old); attrp = new->extra_attrlistp; while (attrp) { rv = soft_copy_extra_attr(attrp, old); if (rv != CKR_OK) { soft_cleanup_extra_attr(old); return (rv); } attrp = attrp->next; } /* Done with copying all information that can be modified */ return (CKR_OK); } /* * Update an existing object with new data from keystore. */ CK_RV soft_update_object(ks_obj_t *ks_obj, soft_object_t *old_obj) { soft_object_t *new_object; CK_RV rv; new_object = calloc(1, sizeof (soft_object_t)); if (new_object == NULL) return (CKR_HOST_MEMORY); rv = soft_keystore_unpack_obj(new_object, ks_obj); if (rv != CKR_OK) { soft_cleanup_object(new_object); free(new_object); return (rv); } rv = soft_copy_to_old_object(new_object, old_obj); soft_cleanup_object(new_object); free(new_object); return (CKR_OK); } CK_RV soft_keystore_load_latest_object(soft_object_t *old_obj) { uint_t version; ks_obj_t *ks_obj = NULL; CK_RV rv = CKR_OK; /* * Get the current version number from the keystore for * the specified token object. */ if (soft_keystore_get_object_version(&old_obj->ks_handle, &version, B_FALSE) == 1) return (CKR_FUNCTION_FAILED); /* * If the keystore version is newer than the in-core version, * re-read the token object from the keystore. */ if (old_obj->version != version) { rv = soft_keystore_get_single_obj(&old_obj->ks_handle, &ks_obj, B_FALSE); if (rv != CKR_OK) return (rv); old_obj->version = version; /* * Update an existing object with new data from keystore. */ rv = soft_update_object(ks_obj, old_obj); free(ks_obj->buf); free(ks_obj); } return (rv); } /* * Insert an object into a list of soft_object_t objects. It is assumed * that the object to be inserted doesn't previously belong to any list */ static void insert_into_list(soft_object_t **list, soft_object_t **end_of_list, soft_object_t *objp) { if (*list == NULL) { *list = objp; objp->next = NULL; objp->prev = NULL; *end_of_list = objp; } else { (*list)->prev = objp; objp->next = *list; objp->prev = NULL; *list = objp; } } /* * Move an object from an existing list into a new list of * soft_object_t objects. */ static void move_into_list(soft_object_t **existing_list, soft_object_t **new_list, soft_object_t **end_of_list, soft_object_t *objp) { /* first, remove object from existing list */ if (objp == *existing_list) { /* first item in list */ if (objp->next) { *existing_list = objp->next; objp->next->prev = NULL; } else { *existing_list = NULL; } } else { if (objp->next) { objp->prev->next = objp->next; objp->next->prev = objp->prev; } else { objp->prev->next = NULL; } } /* then, add into new list */ insert_into_list(new_list, end_of_list, objp); } /* * Insert "new_list" into "existing_list", new list will always be inserted * into the front of existing list */ static void insert_list_into_list(soft_object_t **existing_list, soft_object_t *new_list, soft_object_t *end_new_list) { if (new_list == NULL) { return; } if (*existing_list == NULL) { *existing_list = new_list; } else { (*existing_list)->prev = end_new_list; end_new_list->next = *existing_list; *existing_list = new_list; } } static void delete_all_objs_in_list(soft_object_t *list) { soft_object_t *objp, *objp_next; if (list == NULL) { return; } objp = list; while (objp) { objp_next = objp->next; soft_delete_object_cleanup(objp, B_FALSE); objp = objp_next; } } /* * Makes sure that the list of in-core token objects are up to date * with respect to the on disk keystore. Other process/applications * might have modified the keystore since the objects are last loaded * * If there's any error from refreshing the token object list (eg: unable * to read, unable to unpack and object...etc), the in-core list * will be restored back to the state before the refresh. An error * will be returned to indicate the failure. * * It is assumed that the caller holds the lock for the token slot */ CK_RV refresh_token_objects() { uint_t on_disk_ks_version; ks_obj_t *on_disk_list = NULL, *tmp_on_disk, *next_on_disk; soft_object_t *in_core_obj, *tmp_incore_obj, *new_objp = NULL; CK_RV rv = CKR_OK; /* deleted in-core objects */ soft_object_t *del_objs_list = NULL; soft_object_t *end_del_objs_list = NULL; /* modified in-core objects */ soft_object_t *mod_objs_list = NULL; soft_object_t *end_mod_objs_list = NULL; /* * copy of modified in-core objects, in case we need * undo the change */ soft_object_t *copy_of_mod_objs_list = NULL; soft_object_t *end_copy_of_mod_objs_list = NULL; /* objects to be added to the in-core list */ soft_object_t *added_objs_list = NULL; soft_object_t *end_added_objs_list = NULL; if (soft_keystore_get_version(&on_disk_ks_version, B_FALSE) != 0) { return (CKR_FUNCTION_FAILED); } (void) pthread_mutex_lock(&soft_giant_mutex); if (on_disk_ks_version == soft_slot.ks_version) { /* no change */ (void) pthread_mutex_unlock(&soft_giant_mutex); return (CKR_OK); } if (soft_slot.authenticated) { /* get both public and private objects */ (void) pthread_mutex_unlock(&soft_giant_mutex); rv = soft_keystore_get_objs(ALL_TOKENOBJS, &on_disk_list, B_FALSE); } else { /* get both public objects only */ (void) pthread_mutex_unlock(&soft_giant_mutex); rv = soft_keystore_get_objs(PUB_TOKENOBJS, &on_disk_list, B_FALSE); } if (rv != CKR_OK) { return (rv); } /* * The in-core tokens list will be updated as follows: * * Go through each item in the in-core tokens list. * Try to match the in-core object with one of the * objects from the on-disk list. If a match is made, * check the version number, and update in-core object * as necessary. * * If there's no match between in-core object with on-disk * object, that means the object is deleted since * last loaded. Will remove object from in-core list. * * When doing the matching of on-disk object list above, * Delete every matched on-disk object from the on-disk list * regardless the in-core object need to be deleted or not * * At the end of matching the in-core tokens list, if * any object is still left on the on-disk object list, * those are all new objects added since last load, * include all of them to the in-core list * * Since we need to be able to revert the in-core list * back to original state if there's any error with the refresh, * we need to do the following. * When an in-core object is "deleted", it is not immediately * deleted. It is moved to the list of "deleted_objects". * When an in-core object is "modified", a copy of the * unmodified object is made. After the object is modified, * it is temporarily moved to the "mod_objects" list * from the in-core list. * When the refresh is completed without any error, * the actual deleted objects and unmodified objects is deleted. */ in_core_obj = soft_slot.token_object_list; while (in_core_obj) { /* try to match object with on_disk_list */ ks_obj_t *ondisk_obj, *prev_ondisk_obj; boolean_t found = B_FALSE; soft_object_t *obj_copy; ondisk_obj = on_disk_list; prev_ondisk_obj = NULL; /* larval object that has not been written to disk */ if (in_core_obj->ks_handle.name[0] == '\0') { in_core_obj = in_core_obj->next; continue; } while ((!found) && (ondisk_obj != NULL)) { if (strcmp((char *)((ondisk_obj->ks_handle).name), (char *)((in_core_obj->ks_handle).name)) == 0) { /* found a match */ found = B_TRUE; /* update in-core obj if necessary */ if (ondisk_obj->obj_version != in_core_obj->version) { /* make a copy of before updating */ rv = soft_copy_object(in_core_obj, &obj_copy, SOFT_COPY_OBJ_ORIG_SH, NULL); if (rv != CKR_OK) { goto cleanup; } insert_into_list( ©_of_mod_objs_list, &end_copy_of_mod_objs_list, obj_copy); rv = soft_update_object(ondisk_obj, in_core_obj); if (rv != CKR_OK) { goto cleanup; } move_into_list( &(soft_slot.token_object_list), &mod_objs_list, &end_mod_objs_list, in_core_obj); } /* remove processed obj from on disk list */ if (ondisk_obj == on_disk_list) { /* first item */ on_disk_list = ondisk_obj->next; } else { prev_ondisk_obj->next = ondisk_obj->next; } free(ondisk_obj->buf); free(ondisk_obj); } else { prev_ondisk_obj = ondisk_obj; ondisk_obj = ondisk_obj->next; } } if (!found) { tmp_incore_obj = in_core_obj->next; move_into_list(&(soft_slot.token_object_list), &del_objs_list, &end_del_objs_list, in_core_obj); in_core_obj = tmp_incore_obj; } else { in_core_obj = in_core_obj->next; } } /* * At this point, if there's still anything on the on_disk_list, they * are all newly added objects since in-core list last loaded. * include all of them into the in-core list */ next_on_disk = on_disk_list; while (next_on_disk) { new_objp = calloc(1, sizeof (soft_object_t)); if (new_objp == NULL) { rv = CKR_HOST_MEMORY; goto cleanup; } /* Convert the keystore format to memory format */ rv = soft_keystore_unpack_obj(new_objp, next_on_disk); if (rv != CKR_OK) { soft_cleanup_object(new_objp); free(new_objp); goto cleanup; } insert_into_list(&added_objs_list, &end_added_objs_list, new_objp); /* free the on_disk object */ tmp_on_disk = next_on_disk; next_on_disk = tmp_on_disk->next; free(tmp_on_disk->buf); free(tmp_on_disk); } if (rv == CKR_OK) { (void) pthread_mutex_lock(&soft_giant_mutex); soft_slot.ks_version = on_disk_ks_version; (void) pthread_mutex_unlock(&soft_giant_mutex); /* add the new objects into in-core list */ insert_list_into_list(&(soft_slot.token_object_list), added_objs_list, end_added_objs_list); /* add modified objects back into the in-core list */ insert_list_into_list(&(soft_slot.token_object_list), mod_objs_list, end_mod_objs_list); /* actually remove deleted objs, and copy of modified objs */ delete_all_objs_in_list(copy_of_mod_objs_list); delete_all_objs_in_list(del_objs_list); } return (rv); cleanup: next_on_disk = on_disk_list; while (next_on_disk) { tmp_on_disk = next_on_disk; next_on_disk = tmp_on_disk->next; free(tmp_on_disk->buf); free(tmp_on_disk); } /* * restore the in-core list back to the original state by adding * copy of original objects and deleted objects back to list */ insert_list_into_list(&(soft_slot.token_object_list), del_objs_list, end_del_objs_list); insert_list_into_list(&(soft_slot.token_object_list), copy_of_mod_objs_list, end_copy_of_mod_objs_list); /* * remove the modified objects, and newly objects list */ delete_all_objs_in_list(mod_objs_list); delete_all_objs_in_list(added_objs_list); return (rv); } CK_RV dup_bigint_attr(biginteger_t *bi, CK_BYTE *buf, CK_ULONG buflen) { bi->big_value_len = buflen; if ((bi->big_value = malloc(buflen)) == NULL) { return (CKR_HOST_MEMORY); } (void) memcpy(bi->big_value, buf, buflen); return (CKR_OK); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SOFTOPS_H #define _SOFTOPS_H #ifdef __cplusplus extern "C" { #endif #include #include "softObject.h" #include "softSession.h" /* * Function Prototypes. */ CK_RV soft_digest_init(soft_session_t *, CK_MECHANISM_PTR); CK_RV soft_digest(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR); CK_RV soft_digest_update(soft_session_t *, CK_BYTE_PTR, CK_ULONG); CK_RV soft_digest_final(soft_session_t *, CK_BYTE_PTR, CK_ULONG_PTR); CK_RV soft_digest_init_internal(soft_session_t *, CK_MECHANISM_PTR); CK_RV soft_digest_key(soft_session_t *, soft_object_t *); CK_RV soft_encrypt_init(soft_session_t *, CK_MECHANISM_PTR, soft_object_t *); CK_RV soft_encrypt(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR); CK_RV soft_encrypt_update(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR); CK_RV soft_encrypt_final(soft_session_t *, CK_BYTE_PTR, CK_ULONG_PTR); CK_RV soft_encrypt_init_internal(soft_session_t *, CK_MECHANISM_PTR, soft_object_t *); CK_RV soft_decrypt_init(soft_session_t *, CK_MECHANISM_PTR, soft_object_t *); CK_RV soft_decrypt(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR); CK_RV soft_decrypt_update(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR); CK_RV soft_decrypt_final(soft_session_t *, CK_BYTE_PTR, CK_ULONG_PTR); CK_RV soft_sign_init(soft_session_t *, CK_MECHANISM_PTR, soft_object_t *); CK_RV soft_sign(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR); CK_RV soft_sign_update(soft_session_t *, CK_BYTE_PTR, CK_ULONG); CK_RV soft_sign_final(soft_session_t *, CK_BYTE_PTR, CK_ULONG_PTR); CK_RV soft_verify_init(soft_session_t *, CK_MECHANISM_PTR, soft_object_t *); CK_RV soft_verify(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG); CK_RV soft_verify_update(soft_session_t *, CK_BYTE_PTR, CK_ULONG); CK_RV soft_verify_final(soft_session_t *, CK_BYTE_PTR, CK_ULONG); CK_RV soft_sign_recover_init(soft_session_t *, CK_MECHANISM_PTR, soft_object_t *); CK_RV soft_sign_recover(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR); CK_RV soft_verify_recover_init(soft_session_t *, CK_MECHANISM_PTR, soft_object_t *); CK_RV soft_verify_recover(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR); void soft_crypt_cleanup(soft_session_t *, boolean_t, boolean_t); void soft_sign_verify_cleanup(soft_session_t *, boolean_t, boolean_t); void soft_digest_cleanup(soft_session_t *, boolean_t); #ifdef __cplusplus } #endif #endif /* _SOFTOPS_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include "softGlobal.h" #include "softSession.h" #include "softObject.h" #include "softOps.h" #include "softRSA.h" #include "softMAC.h" #include "softCrypt.h" CK_RV soft_rsa_encrypt(soft_object_t *key, CK_BYTE_PTR in, uint32_t in_len, CK_BYTE_PTR out, int realpublic) { CK_RV rv = CKR_OK; uchar_t expo[MAX_KEY_ATTR_BUFLEN]; uchar_t modulus[MAX_KEY_ATTR_BUFLEN]; uint32_t expo_len = sizeof (expo); uint32_t modulus_len = sizeof (modulus); RSAbytekey k; if (realpublic) { rv = soft_get_public_value(key, CKA_PUBLIC_EXPONENT, expo, &expo_len); if (rv != CKR_OK) { goto clean1; } } else { rv = soft_get_private_value(key, CKA_PRIVATE_EXPONENT, expo, &expo_len); if (rv != CKR_OK) { goto clean1; } } rv = soft_get_public_value(key, CKA_MODULUS, modulus, &modulus_len); if (rv != CKR_OK) { goto clean1; } k.modulus = modulus; k.modulus_bits = CRYPTO_BYTES2BITS(modulus_len); k.pubexpo = expo; k.pubexpo_bytes = expo_len; k.rfunc = NULL; rv = rsa_encrypt(&k, in, in_len, out); clean1: return (rv); } CK_RV soft_rsa_decrypt(soft_object_t *key, CK_BYTE_PTR in, uint32_t in_len, CK_BYTE_PTR out) { CK_RV rv = CKR_OK; uchar_t modulus[MAX_KEY_ATTR_BUFLEN]; uchar_t prime1[MAX_KEY_ATTR_BUFLEN]; uchar_t prime2[MAX_KEY_ATTR_BUFLEN]; uchar_t expo1[MAX_KEY_ATTR_BUFLEN]; uchar_t expo2[MAX_KEY_ATTR_BUFLEN]; uchar_t coef[MAX_KEY_ATTR_BUFLEN]; uint32_t modulus_len = sizeof (modulus); uint32_t prime1_len = sizeof (prime1); uint32_t prime2_len = sizeof (prime2); uint32_t expo1_len = sizeof (expo1); uint32_t expo2_len = sizeof (expo2); uint32_t coef_len = sizeof (coef); RSAbytekey k; rv = soft_get_private_value(key, CKA_MODULUS, modulus, &modulus_len); if (rv != CKR_OK) { goto clean1; } rv = soft_get_private_value(key, CKA_PRIME_1, prime1, &prime1_len); if ((prime1_len == 0) && (rv == CKR_OK)) { rv = soft_rsa_encrypt(key, in, in_len, out, 0); goto clean1; } else { if (rv != CKR_OK) goto clean1; } rv = soft_get_private_value(key, CKA_PRIME_2, prime2, &prime2_len); if ((prime2_len == 0) && (rv == CKR_OK)) { rv = soft_rsa_encrypt(key, in, in_len, out, 0); goto clean1; } else { if (rv != CKR_OK) goto clean1; } rv = soft_get_private_value(key, CKA_EXPONENT_1, expo1, &expo1_len); if ((expo1_len == 0) && (rv == CKR_OK)) { rv = soft_rsa_encrypt(key, in, in_len, out, 0); goto clean1; } else { if (rv != CKR_OK) goto clean1; } rv = soft_get_private_value(key, CKA_EXPONENT_2, expo2, &expo2_len); if ((expo2_len == 0) && (rv == CKR_OK)) { rv = soft_rsa_encrypt(key, in, in_len, out, 0); goto clean1; } else { if (rv != CKR_OK) goto clean1; } rv = soft_get_private_value(key, CKA_COEFFICIENT, coef, &coef_len); if ((coef_len == 0) && (rv == CKR_OK)) { rv = soft_rsa_encrypt(key, in, in_len, out, 0); goto clean1; } else { if (rv != CKR_OK) goto clean1; } k.modulus = modulus; k.modulus_bits = CRYPTO_BYTES2BITS(modulus_len); k.prime1 = prime1; k.prime1_bytes = prime1_len; k.prime2 = prime2; k.prime2_bytes = prime2_len; k.expo1 = expo1; k.expo1_bytes = expo1_len; k.expo2 = expo2; k.expo2_bytes = expo2_len; k.coeff = coef; k.coeff_bytes = coef_len; k.rfunc = NULL; rv = rsa_decrypt(&k, in, in_len, out); clean1: return (rv); } /* * Allocate a RSA context for the active encryption or decryption operation. * This function is called without the session lock held. */ CK_RV soft_rsa_crypt_init_common(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism, soft_object_t *key_p, boolean_t encrypt) { soft_rsa_ctx_t *rsa_ctx; soft_object_t *tmp_key = NULL; CK_RV rv; rsa_ctx = calloc(1, sizeof (soft_rsa_ctx_t)); if (rsa_ctx == NULL) { return (CKR_HOST_MEMORY); } /* * Make a copy of the encryption or decryption key, and save it * in the RSA crypto context since it will be used later for * encryption/decryption. We don't want to hold any object reference * on this original key while doing encryption/decryption. */ (void) pthread_mutex_lock(&key_p->object_mutex); rv = soft_copy_object(key_p, &tmp_key, SOFT_COPY_OBJ_ORIG_SH, NULL); if ((rv != CKR_OK) || (tmp_key == NULL)) { /* Most likely we ran out of space. */ (void) pthread_mutex_unlock(&key_p->object_mutex); free(rsa_ctx); return (rv); } /* No need to hold the lock on the old object. */ (void) pthread_mutex_unlock(&key_p->object_mutex); rsa_ctx->key = tmp_key; (void) pthread_mutex_lock(&session_p->session_mutex); if (encrypt) { /* Called by C_EncryptInit. */ session_p->encrypt.context = rsa_ctx; session_p->encrypt.mech.mechanism = pMechanism->mechanism; } else { /* Called by C_DecryptInit. */ session_p->decrypt.context = rsa_ctx; session_p->decrypt.mech.mechanism = pMechanism->mechanism; } (void) pthread_mutex_unlock(&session_p->session_mutex); return (CKR_OK); } CK_RV soft_rsa_encrypt_common(soft_session_t *session_p, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pEncrypted, CK_ULONG_PTR pulEncryptedLen, CK_MECHANISM_TYPE mechanism) { soft_rsa_ctx_t *rsa_ctx = session_p->encrypt.context; soft_object_t *key = rsa_ctx->key; uchar_t modulus[MAX_KEY_ATTR_BUFLEN]; uint32_t modulus_len = sizeof (modulus); CK_BYTE plain_data[MAX_RSA_KEYLENGTH_IN_BYTES]; CK_BYTE cipher_data[MAX_RSA_KEYLENGTH_IN_BYTES]; CK_RV rv = CKR_OK; rv = soft_get_public_value(key, CKA_MODULUS, modulus, &modulus_len); if (rv != CKR_OK) { goto clean_exit; } if (pEncrypted == NULL) { /* * Application asks for the length of the output buffer * to hold the ciphertext. */ *pulEncryptedLen = modulus_len; rv = CKR_OK; goto clean1; } if (mechanism == CKM_RSA_PKCS) { /* * Input data length needs to be <= * modulus length-MIN_PKCS1_PADLEN. */ if (ulDataLen > ((CK_ULONG)modulus_len - MIN_PKCS1_PADLEN)) { *pulEncryptedLen = modulus_len; rv = CKR_DATA_LEN_RANGE; goto clean_exit; } } else { /* Input data length needs to be <= modulus length. */ if (ulDataLen > (CK_ULONG)modulus_len) { *pulEncryptedLen = modulus_len; rv = CKR_DATA_LEN_RANGE; goto clean_exit; } } /* Is the application-supplied buffer large enough? */ if (*pulEncryptedLen < (CK_ULONG)modulus_len) { *pulEncryptedLen = modulus_len; rv = CKR_BUFFER_TOO_SMALL; goto clean1; } if (mechanism == CKM_RSA_PKCS) { /* * Add PKCS padding to the input data to format a block * type "02" encryption block. */ rv = pkcs1_encode(PKCS1_ENCRYPT, pData, ulDataLen, plain_data, modulus_len); if (rv != CKR_OK) goto clean_exit; } else { /* Pad zeros for the leading bytes of the input data. */ (void) memset(plain_data, 0x0, modulus_len - ulDataLen); (void) memcpy(&plain_data[modulus_len - ulDataLen], pData, ulDataLen); } rv = soft_rsa_encrypt(key, plain_data, modulus_len, cipher_data, 1); if (rv == CKR_OK) { (void) memcpy(pEncrypted, cipher_data, modulus_len); *pulEncryptedLen = modulus_len; } clean_exit: (void) pthread_mutex_lock(&session_p->session_mutex); free(session_p->encrypt.context); session_p->encrypt.context = NULL; (void) pthread_mutex_unlock(&session_p->session_mutex); soft_cleanup_object(key); free(key); clean1: return (rv); } CK_RV soft_rsa_decrypt_common(soft_session_t *session_p, CK_BYTE_PTR pEncrypted, CK_ULONG ulEncryptedLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen, CK_MECHANISM_TYPE mechanism) { soft_rsa_ctx_t *rsa_ctx = session_p->decrypt.context; soft_object_t *key = rsa_ctx->key; uchar_t modulus[MAX_KEY_ATTR_BUFLEN]; uint32_t modulus_len = sizeof (modulus); CK_BYTE plain_data[MAX_RSA_KEYLENGTH_IN_BYTES]; CK_RV rv = CKR_OK; rv = soft_get_private_value(key, CKA_MODULUS, modulus, &modulus_len); if (rv != CKR_OK) { goto clean_exit; } if (ulEncryptedLen != (CK_ULONG)modulus_len) { rv = CKR_ENCRYPTED_DATA_LEN_RANGE; goto clean_exit; } if (pData == NULL) { /* * Application asks for the length of the output buffer * to hold the recovered data. */ *pulDataLen = modulus_len; rv = CKR_OK; goto clean1; } if (mechanism == CKM_RSA_X_509) { if (*pulDataLen < (CK_ULONG)modulus_len) { *pulDataLen = modulus_len; rv = CKR_BUFFER_TOO_SMALL; goto clean1; } } rv = soft_rsa_decrypt(key, pEncrypted, modulus_len, plain_data); if (rv != CKR_OK) { goto clean_exit; } if (mechanism == CKM_RSA_PKCS) { size_t plain_len = modulus_len; size_t num_padding; /* Strip off the PKCS block formatting data. */ rv = pkcs1_decode(PKCS1_DECRYPT, plain_data, &plain_len); if (rv != CKR_OK) goto clean_exit; num_padding = modulus_len - plain_len; if (ulEncryptedLen - num_padding > *pulDataLen) { *pulDataLen = plain_len; rv = CKR_BUFFER_TOO_SMALL; goto clean1; } (void) memcpy(pData, &plain_data[num_padding], plain_len); *pulDataLen = plain_len; } else { (void) memcpy(pData, plain_data, modulus_len); *pulDataLen = modulus_len; } clean_exit: (void) pthread_mutex_lock(&session_p->session_mutex); free(session_p->decrypt.context); session_p->decrypt.context = NULL; (void) pthread_mutex_unlock(&session_p->session_mutex); soft_cleanup_object(key); free(key); clean1: return (rv); } /* * Allocate a RSA context for the active sign or verify operation. * This function is called without the session lock held. */ CK_RV soft_rsa_sign_verify_init_common(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism, soft_object_t *key_p, boolean_t sign) { CK_RV rv = CKR_OK; soft_rsa_ctx_t *rsa_ctx; CK_MECHANISM digest_mech; soft_object_t *tmp_key = NULL; if (sign) { if ((key_p->class != CKO_PRIVATE_KEY) || (key_p->key_type != CKK_RSA)) return (CKR_KEY_TYPE_INCONSISTENT); } else { if ((key_p->class != CKO_PUBLIC_KEY) || (key_p->key_type != CKK_RSA)) return (CKR_KEY_TYPE_INCONSISTENT); } switch (pMechanism->mechanism) { case CKM_MD5_RSA_PKCS: digest_mech.mechanism = CKM_MD5; rv = soft_digest_init_internal(session_p, &digest_mech); if (rv != CKR_OK) return (rv); break; case CKM_SHA1_RSA_PKCS: digest_mech.mechanism = CKM_SHA_1; digest_mech.pParameter = pMechanism->pParameter; digest_mech.ulParameterLen = pMechanism->ulParameterLen; rv = soft_digest_init_internal(session_p, &digest_mech); if (rv != CKR_OK) return (rv); break; case CKM_SHA256_RSA_PKCS: digest_mech.mechanism = CKM_SHA256; rv = soft_digest_init_internal(session_p, &digest_mech); if (rv != CKR_OK) return (rv); break; case CKM_SHA384_RSA_PKCS: digest_mech.mechanism = CKM_SHA384; rv = soft_digest_init_internal(session_p, &digest_mech); if (rv != CKR_OK) return (rv); break; case CKM_SHA512_RSA_PKCS: digest_mech.mechanism = CKM_SHA512; rv = soft_digest_init_internal(session_p, &digest_mech); if (rv != CKR_OK) return (rv); break; } rsa_ctx = malloc(sizeof (soft_rsa_ctx_t)); if (rsa_ctx == NULL) { rv = CKR_HOST_MEMORY; goto clean_exit; } (void) pthread_mutex_lock(&key_p->object_mutex); rv = soft_copy_object(key_p, &tmp_key, SOFT_COPY_OBJ_ORIG_SH, NULL); if ((rv != CKR_OK) || (tmp_key == NULL)) { /* Most likely we ran out of space. */ (void) pthread_mutex_unlock(&key_p->object_mutex); free(rsa_ctx); goto clean_exit; } /* No need to hold the lock on the old object. */ (void) pthread_mutex_unlock(&key_p->object_mutex); rsa_ctx->key = tmp_key; (void) pthread_mutex_lock(&session_p->session_mutex); if (sign) { session_p->sign.context = rsa_ctx; session_p->sign.mech.mechanism = pMechanism->mechanism; } else { session_p->verify.context = rsa_ctx; session_p->verify.mech.mechanism = pMechanism->mechanism; } (void) pthread_mutex_unlock(&session_p->session_mutex); return (CKR_OK); clean_exit: (void) pthread_mutex_lock(&session_p->session_mutex); if (session_p->digest.context != NULL) { free(session_p->digest.context); session_p->digest.context = NULL; session_p->digest.flags = 0; } (void) pthread_mutex_unlock(&session_p->session_mutex); return (rv); } CK_RV soft_rsa_sign_common(soft_session_t *session_p, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSigned, CK_ULONG_PTR pulSignedLen, CK_MECHANISM_TYPE mechanism) { CK_RV rv = CKR_OK; soft_rsa_ctx_t *rsa_ctx = session_p->sign.context; soft_object_t *key = rsa_ctx->key; uchar_t modulus[MAX_KEY_ATTR_BUFLEN]; uint32_t modulus_len = sizeof (modulus); CK_BYTE plain_data[MAX_RSA_KEYLENGTH_IN_BYTES]; CK_BYTE signed_data[MAX_RSA_KEYLENGTH_IN_BYTES]; rv = soft_get_private_value(key, CKA_MODULUS, modulus, &modulus_len); if (rv != CKR_OK) { goto clean_exit; } if (pSigned == NULL) { /* Application asks for the length of the output buffer. */ *pulSignedLen = modulus_len; rv = CKR_OK; goto clean1; } switch (mechanism) { case CKM_RSA_PKCS: /* * Input data length needs to be <= * modulus length-MIN_PKCS1_PADLEN. */ if (ulDataLen > ((CK_ULONG)modulus_len - MIN_PKCS1_PADLEN)) { *pulSignedLen = modulus_len; rv = CKR_DATA_LEN_RANGE; goto clean_exit; } break; case CKM_RSA_X_509: /* Input data length needs to be <= modulus length. */ if (ulDataLen > (CK_ULONG)modulus_len) { *pulSignedLen = modulus_len; rv = CKR_DATA_LEN_RANGE; goto clean_exit; } break; } /* Is the application-supplied buffer large enough? */ if (*pulSignedLen < (CK_ULONG)modulus_len) { *pulSignedLen = modulus_len; rv = CKR_BUFFER_TOO_SMALL; goto clean1; } switch (mechanism) { case CKM_RSA_PKCS: case CKM_MD5_RSA_PKCS: case CKM_SHA1_RSA_PKCS: case CKM_SHA256_RSA_PKCS: case CKM_SHA384_RSA_PKCS: case CKM_SHA512_RSA_PKCS: /* * Add PKCS padding to the input data to format a block * type "01" encryption block. */ rv = pkcs1_encode(PKCS1_SIGN, pData, ulDataLen, plain_data, modulus_len); if (rv != CKR_OK) { goto clean_exit; } break; case CKM_RSA_X_509: /* Pad zeros for the leading bytes of the input data. */ (void) memset(plain_data, 0x0, modulus_len - ulDataLen); (void) memcpy(&plain_data[modulus_len - ulDataLen], pData, ulDataLen); break; } /* * Perform RSA encryption with the signer's RSA private key * for signature process. */ rv = soft_rsa_decrypt(key, plain_data, modulus_len, signed_data); if (rv == CKR_OK) { (void) memcpy(pSigned, signed_data, modulus_len); *pulSignedLen = modulus_len; } clean_exit: (void) pthread_mutex_lock(&session_p->session_mutex); free(session_p->sign.context); session_p->sign.context = NULL; if (session_p->digest.context != NULL) { free(session_p->digest.context); session_p->digest.context = NULL; session_p->digest.flags = 0; } (void) pthread_mutex_unlock(&session_p->session_mutex); soft_cleanup_object(key); free(key); clean1: return (rv); } CK_RV soft_rsa_verify_common(soft_session_t *session_p, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen, CK_MECHANISM_TYPE mechanism) { CK_RV rv = CKR_OK; soft_rsa_ctx_t *rsa_ctx = session_p->verify.context; soft_object_t *key = rsa_ctx->key; uchar_t modulus[MAX_KEY_ATTR_BUFLEN]; uint32_t modulus_len = sizeof (modulus); CK_BYTE plain_data[MAX_RSA_KEYLENGTH_IN_BYTES]; rv = soft_get_public_value(key, CKA_MODULUS, modulus, &modulus_len); if (rv != CKR_OK) { goto clean_exit; } if (ulDataLen == 0) { rv = CKR_DATA_LEN_RANGE; goto clean_exit; } if (ulSignatureLen != (CK_ULONG)modulus_len) { rv = CKR_SIGNATURE_LEN_RANGE; goto clean_exit; } /* * Perform RSA decryption with the signer's RSA public key * for verification process. */ rv = soft_rsa_encrypt(key, pSignature, modulus_len, plain_data, 1); if (rv == CKR_OK) { switch (mechanism) { case CKM_RSA_PKCS: case CKM_MD5_RSA_PKCS: case CKM_SHA1_RSA_PKCS: case CKM_SHA256_RSA_PKCS: case CKM_SHA384_RSA_PKCS: case CKM_SHA512_RSA_PKCS: { /* * Strip off the encoded padding bytes in front of the * recovered data, then compare the recovered data with * the original data. */ size_t data_len = modulus_len; rv = pkcs1_decode(PKCS1_VERIFY, plain_data, &data_len); if (rv != CKR_OK) { goto clean_exit; } if ((CK_ULONG)data_len != ulDataLen) { rv = CKR_DATA_LEN_RANGE; goto clean_exit; } else if (memcmp(pData, &plain_data[modulus_len - data_len], ulDataLen) != 0) { rv = CKR_SIGNATURE_INVALID; goto clean_exit; } break; } case CKM_RSA_X_509: /* * Strip off the encoded padding bytes in front of the * recovered plain_data, then compare the input data * with the recovered data. */ if (memcmp(pData, plain_data + ulSignatureLen - ulDataLen, ulDataLen) != 0) { rv = CKR_SIGNATURE_INVALID; goto clean_exit; } break; } } if (rv == CKR_DATA_LEN_RANGE) { if ((mechanism == CKM_MD5_RSA_PKCS) || (mechanism == CKM_SHA1_RSA_PKCS) || (mechanism == CKM_SHA256_RSA_PKCS) || (mechanism == CKM_SHA384_RSA_PKCS) || (mechanism == CKM_SHA512_RSA_PKCS)) rv = CKR_SIGNATURE_INVALID; } clean_exit: (void) pthread_mutex_lock(&session_p->session_mutex); free(session_p->verify.context); session_p->verify.context = NULL; if (session_p->digest.context != NULL) { free(session_p->digest.context); session_p->digest.context = NULL; session_p->digest.flags = 0; } (void) pthread_mutex_unlock(&session_p->session_mutex); soft_cleanup_object(key); free(key); return (rv); } CK_RV soft_genRSAkey_set_attribute(soft_object_t *key, CK_ATTRIBUTE_TYPE type, uchar_t *buf, uint32_t buflen, boolean_t public) { CK_RV rv = CKR_OK; biginteger_t *dst = NULL; biginteger_t src; switch (type) { case CKA_MODULUS: if (public) dst = OBJ_PUB_RSA_MOD(key); else dst = OBJ_PRI_RSA_MOD(key); break; case CKA_PUBLIC_EXPONENT: if (public) dst = OBJ_PUB_RSA_PUBEXPO(key); else dst = OBJ_PRI_RSA_PUBEXPO(key); break; case CKA_PRIVATE_EXPONENT: dst = OBJ_PRI_RSA_PRIEXPO(key); break; case CKA_PRIME_1: dst = OBJ_PRI_RSA_PRIME1(key); break; case CKA_PRIME_2: dst = OBJ_PRI_RSA_PRIME2(key); break; case CKA_EXPONENT_1: dst = OBJ_PRI_RSA_EXPO1(key); break; case CKA_EXPONENT_2: dst = OBJ_PRI_RSA_EXPO2(key); break; case CKA_COEFFICIENT: dst = OBJ_PRI_RSA_COEF(key); break; } /* Note: no explanation found for why this is needed */ while (buf[0] == 0) { /* remove proceeding 0x00 */ buf++; buflen--; } if ((rv = dup_bigint_attr(&src, buf, buflen)) != CKR_OK) goto cleanexit; /* Copy the attribute in the key object. */ copy_bigint_attr(&src, dst); cleanexit: return (rv); } CK_RV soft_rsa_genkey_pair(soft_object_t *pubkey, soft_object_t *prikey) { CK_RV rv = CKR_OK; CK_ATTRIBUTE template; uchar_t modulus[MAX_KEY_ATTR_BUFLEN]; uint32_t modulus_len; uchar_t pub_expo[MAX_KEY_ATTR_BUFLEN]; uint32_t pub_expo_len = sizeof (pub_expo); uchar_t private_exponent[MAX_KEY_ATTR_BUFLEN]; uint32_t private_exponent_len = sizeof (private_exponent); uchar_t prime1[MAX_KEY_ATTR_BUFLEN]; uint32_t prime1_len = sizeof (prime1); uchar_t prime2[MAX_KEY_ATTR_BUFLEN]; uint32_t prime2_len = sizeof (prime2); uchar_t exponent1[MAX_KEY_ATTR_BUFLEN]; uint32_t exponent1_len = sizeof (exponent1); uchar_t exponent2[MAX_KEY_ATTR_BUFLEN]; uint32_t exponent2_len = sizeof (exponent2); uchar_t coefficient[MAX_KEY_ATTR_BUFLEN]; uint32_t coefficient_len = sizeof (coefficient); RSAbytekey k; if ((pubkey == NULL) || (prikey == NULL)) { return (CKR_ARGUMENTS_BAD); } template.pValue = malloc(sizeof (CK_ULONG)); if (template.pValue == NULL) { return (CKR_HOST_MEMORY); } template.ulValueLen = sizeof (CK_ULONG); rv = get_ulong_attr_from_object(OBJ_PUB_RSA_MOD_BITS(pubkey), &template); if (rv != CKR_OK) { free(template.pValue); goto clean0; } #ifdef __sparcv9 /* LINTED */ modulus_len = (uint32_t)(*((CK_ULONG *)(template.pValue))); #else /* !__sparcv9 */ modulus_len = *((CK_ULONG *)(template.pValue)); #endif /* __sparcv9 */ free(template.pValue); rv = soft_get_public_value(pubkey, CKA_PUBLIC_EXPONENT, pub_expo, &pub_expo_len); if (rv != CKR_OK) { goto clean0; } /* Inputs to RSA key pair generation */ k.modulus_bits = modulus_len; /* save modulus len in bits */ modulus_len = CRYPTO_BITS2BYTES(modulus_len); /* convert to bytes */ k.modulus = modulus; k.pubexpo = pub_expo; k.pubexpo_bytes = pub_expo_len; k.rfunc = (IS_TOKEN_OBJECT(pubkey) || IS_TOKEN_OBJECT(prikey)) ? pkcs11_get_random : pkcs11_get_urandom; /* Outputs from RSA key pair generation */ k.privexpo = private_exponent; k.privexpo_bytes = private_exponent_len; k.prime1 = prime1; k.prime1_bytes = prime1_len; k.prime2 = prime2; k.prime2_bytes = prime2_len; k.expo1 = exponent1; k.expo1_bytes = exponent1_len; k.expo2 = exponent2; k.expo2_bytes = exponent2_len; k.coeff = coefficient; k.coeff_bytes = coefficient_len; rv = rsa_genkey_pair(&k); if (rv != CKR_OK) { goto clean0; } /* * Add modulus in public template, and add all eight key fields * in private template. */ if ((rv = soft_genRSAkey_set_attribute(pubkey, CKA_MODULUS, modulus, CRYPTO_BITS2BYTES(k.modulus_bits), B_TRUE)) != CKR_OK) { goto clean0; } if ((rv = soft_genRSAkey_set_attribute(prikey, CKA_MODULUS, modulus, CRYPTO_BITS2BYTES(k.modulus_bits), B_FALSE)) != CKR_OK) { goto clean0; } if ((rv = soft_genRSAkey_set_attribute(prikey, CKA_PRIVATE_EXPONENT, private_exponent, k.privexpo_bytes, B_FALSE)) != CKR_OK) { goto clean0; } if ((rv = soft_genRSAkey_set_attribute(prikey, CKA_PUBLIC_EXPONENT, pub_expo, k.pubexpo_bytes, B_FALSE)) != CKR_OK) { goto clean0; } if ((rv = soft_genRSAkey_set_attribute(prikey, CKA_PRIME_1, prime1, k.prime1_bytes, B_FALSE)) != CKR_OK) { goto clean0; } if ((rv = soft_genRSAkey_set_attribute(prikey, CKA_PRIME_2, prime2, k.prime2_bytes, B_FALSE)) != CKR_OK) { goto clean0; } if ((rv = soft_genRSAkey_set_attribute(prikey, CKA_EXPONENT_1, exponent1, k.expo1_bytes, B_FALSE)) != CKR_OK) { goto clean0; } if ((rv = soft_genRSAkey_set_attribute(prikey, CKA_EXPONENT_2, exponent2, k.expo2_bytes, B_FALSE)) != CKR_OK) { goto clean0; } if ((rv = soft_genRSAkey_set_attribute(prikey, CKA_COEFFICIENT, coefficient, k.coeff_bytes, B_FALSE)) != CKR_OK) { goto clean0; } clean0: return (rv); } CK_ULONG get_rsa_sha1_prefix(CK_MECHANISM_PTR mech, CK_BYTE_PTR *prefix) { if (mech->pParameter == NULL) { *prefix = (CK_BYTE *)SHA1_DER_PREFIX; return (SHA1_DER_PREFIX_Len); } *prefix = (CK_BYTE *)SHA1_DER_PREFIX_OID; return (SHA1_DER_PREFIX_OID_Len); } CK_RV soft_rsa_digest_sign_common(soft_session_t *session_p, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSigned, CK_ULONG_PTR pulSignedLen, CK_MECHANISM_TYPE mechanism, boolean_t Final) { CK_RV rv = CKR_OK; CK_BYTE hash[SHA512_DIGEST_LENGTH]; /* space enough for all mechs */ CK_ULONG hash_len = SHA512_DIGEST_LENGTH; /* space enough for all mechs */ CK_BYTE der_data[SHA512_DIGEST_LENGTH + SHA2_DER_PREFIX_Len]; CK_ULONG der_data_len; soft_rsa_ctx_t *rsa_ctx = session_p->sign.context; soft_object_t *key = rsa_ctx->key; uchar_t modulus[MAX_KEY_ATTR_BUFLEN]; uint32_t modulus_len = sizeof (modulus); CK_ULONG der_len; CK_BYTE_PTR der_prefix; rv = soft_get_private_value(key, CKA_MODULUS, modulus, &modulus_len); if (rv != CKR_OK) { (void) pthread_mutex_lock(&session_p->session_mutex); free(session_p->digest.context); session_p->digest.context = NULL; session_p->digest.flags = 0; (void) pthread_mutex_unlock(&session_p->session_mutex); soft_cleanup_object(key); free(key); goto clean1; } /* Check arguments before performing message digest. */ if (pSigned == NULL) { /* Application asks for the length of the output buffer. */ *pulSignedLen = modulus_len; rv = CKR_OK; goto clean1; } /* Is the application-supplied buffer large enough? */ if (*pulSignedLen < (CK_ULONG)modulus_len) { *pulSignedLen = modulus_len; rv = CKR_BUFFER_TOO_SMALL; goto clean1; } if (Final) { rv = soft_digest_final(session_p, hash, &hash_len); } else { rv = soft_digest(session_p, pData, ulDataLen, hash, &hash_len); } if (rv != CKR_OK) { /* free the signature key */ soft_cleanup_object(key); free(key); goto clean_exit; } /* * Prepare the DER encoding of the DigestInfo value by setting it to: * _DER_PREFIX || H * * See rsa_impl.c for more details. */ switch (session_p->digest.mech.mechanism) { case CKM_MD5: (void) memcpy(der_data, MD5_DER_PREFIX, MD5_DER_PREFIX_Len); (void) memcpy(der_data + MD5_DER_PREFIX_Len, hash, hash_len); der_data_len = MD5_DER_PREFIX_Len + hash_len; break; case CKM_SHA_1: der_len = get_rsa_sha1_prefix(&(session_p->digest.mech), &der_prefix); (void) memcpy(der_data, der_prefix, der_len); (void) memcpy(der_data + der_len, hash, hash_len); der_data_len = der_len + hash_len; break; case CKM_SHA256: (void) memcpy(der_data, SHA256_DER_PREFIX, SHA2_DER_PREFIX_Len); (void) memcpy(der_data + SHA2_DER_PREFIX_Len, hash, hash_len); der_data_len = SHA2_DER_PREFIX_Len + hash_len; break; case CKM_SHA384: (void) memcpy(der_data, SHA384_DER_PREFIX, SHA2_DER_PREFIX_Len); (void) memcpy(der_data + SHA2_DER_PREFIX_Len, hash, hash_len); der_data_len = SHA2_DER_PREFIX_Len + hash_len; break; case CKM_SHA512: (void) memcpy(der_data, SHA512_DER_PREFIX, SHA2_DER_PREFIX_Len); (void) memcpy(der_data + SHA2_DER_PREFIX_Len, hash, hash_len); der_data_len = SHA2_DER_PREFIX_Len + hash_len; break; } /* * Now, we are ready to sign the DER_ENCODED data * soft_rsa_sign_common() will free the signature key. */ rv = soft_rsa_sign_common(session_p, der_data, der_data_len, pSigned, pulSignedLen, mechanism); clean_exit: (void) pthread_mutex_lock(&session_p->session_mutex); /* soft_digest_common() has freed the digest context */ session_p->digest.flags = 0; (void) pthread_mutex_unlock(&session_p->session_mutex); clean1: return (rv); } CK_RV soft_rsa_digest_verify_common(soft_session_t *session_p, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSigned, CK_ULONG ulSignedLen, CK_MECHANISM_TYPE mechanism, boolean_t Final) { CK_RV rv = CKR_OK; CK_BYTE hash[SHA512_DIGEST_LENGTH]; /* space for all mechs */ CK_ULONG hash_len = SHA512_DIGEST_LENGTH; CK_BYTE der_data[SHA512_DIGEST_LENGTH + SHA2_DER_PREFIX_Len]; CK_ULONG der_data_len; soft_rsa_ctx_t *rsa_ctx = session_p->verify.context; soft_object_t *key = rsa_ctx->key; CK_ULONG der_len; CK_BYTE_PTR der_prefix; if (Final) { rv = soft_digest_final(session_p, hash, &hash_len); } else { rv = soft_digest(session_p, pData, ulDataLen, hash, &hash_len); } if (rv != CKR_OK) { /* free the verification key */ soft_cleanup_object(key); free(key); goto clean_exit; } /* * Prepare the DER encoding of the DigestInfo value as follows: * MD5: MD5_DER_PREFIX || H * SHA-1: SHA1_DER_PREFIX || H * SHA2: SHA2_DER_PREFIX || H * * See rsa_impl.c for more details. */ switch (session_p->digest.mech.mechanism) { case CKM_MD5: (void) memcpy(der_data, MD5_DER_PREFIX, MD5_DER_PREFIX_Len); (void) memcpy(der_data + MD5_DER_PREFIX_Len, hash, hash_len); der_data_len = MD5_DER_PREFIX_Len + hash_len; break; case CKM_SHA_1: der_len = get_rsa_sha1_prefix(&(session_p->digest.mech), &der_prefix); (void) memcpy(der_data, der_prefix, der_len); (void) memcpy(der_data + der_len, hash, hash_len); der_data_len = der_len + hash_len; break; case CKM_SHA256: (void) memcpy(der_data, SHA256_DER_PREFIX, SHA2_DER_PREFIX_Len); (void) memcpy(der_data + SHA2_DER_PREFIX_Len, hash, hash_len); der_data_len = SHA2_DER_PREFIX_Len + hash_len; break; case CKM_SHA384: (void) memcpy(der_data, SHA384_DER_PREFIX, SHA2_DER_PREFIX_Len); (void) memcpy(der_data + SHA2_DER_PREFIX_Len, hash, hash_len); der_data_len = SHA2_DER_PREFIX_Len + hash_len; break; case CKM_SHA512: (void) memcpy(der_data, SHA512_DER_PREFIX, SHA2_DER_PREFIX_Len); (void) memcpy(der_data + SHA2_DER_PREFIX_Len, hash, hash_len); der_data_len = SHA2_DER_PREFIX_Len + hash_len; break; } /* * Now, we are ready to verify the DER_ENCODED data using signature. * soft_rsa_verify_common() will free the verification key. */ rv = soft_rsa_verify_common(session_p, der_data, der_data_len, pSigned, ulSignedLen, mechanism); clean_exit: (void) pthread_mutex_lock(&session_p->session_mutex); /* soft_digest_common() has freed the digest context */ session_p->digest.flags = 0; (void) pthread_mutex_unlock(&session_p->session_mutex); return (rv); } CK_RV soft_rsa_verify_recover(soft_session_t *session_p, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) { CK_RV rv = CKR_OK; soft_rsa_ctx_t *rsa_ctx = session_p->verify.context; CK_MECHANISM_TYPE mechanism = session_p->verify.mech.mechanism; soft_object_t *key = rsa_ctx->key; uchar_t modulus[MAX_KEY_ATTR_BUFLEN]; uint32_t modulus_len = sizeof (modulus); CK_BYTE plain_data[MAX_RSA_KEYLENGTH_IN_BYTES]; rv = soft_get_public_value(key, CKA_MODULUS, modulus, &modulus_len); if (rv != CKR_OK) { goto clean_exit; } if (ulSignatureLen != (CK_ULONG)modulus_len) { rv = CKR_SIGNATURE_LEN_RANGE; goto clean_exit; } /* * Perform RSA decryption with the signer's RSA public key * for verification process. */ rv = soft_rsa_encrypt(key, pSignature, modulus_len, plain_data, 1); if (rv == CKR_OK) { switch (mechanism) { case CKM_RSA_PKCS: { /* * Strip off the encoded padding bytes in front of the * recovered data. */ size_t data_len = modulus_len; rv = pkcs1_decode(PKCS1_VERIFY, plain_data, &data_len); if (rv != CKR_OK) { goto clean_exit; } /* * If application asks for the length of the output * buffer? */ if (pData == NULL) { *pulDataLen = data_len; rv = CKR_OK; goto clean1; } /* Is the application-supplied buffer large enough? */ if (*pulDataLen < (CK_ULONG)data_len) { *pulDataLen = data_len; rv = CKR_BUFFER_TOO_SMALL; goto clean1; } (void) memcpy(pData, &plain_data[modulus_len - data_len], data_len); *pulDataLen = data_len; break; } case CKM_RSA_X_509: /* * If application asks for the length of the output * buffer? */ if (pData == NULL) { *pulDataLen = modulus_len; rv = CKR_OK; goto clean1; } /* Is the application-supplied buffer large enough? */ if (*pulDataLen < (CK_ULONG)modulus_len) { *pulDataLen = modulus_len; rv = CKR_BUFFER_TOO_SMALL; goto clean1; } (void) memcpy(pData, plain_data, modulus_len); *pulDataLen = modulus_len; break; } } clean_exit: (void) pthread_mutex_lock(&session_p->session_mutex); free(session_p->verify.context); session_p->verify.context = NULL; (void) pthread_mutex_unlock(&session_p->session_mutex); soft_cleanup_object(key); free(key); clean1: return (rv); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _SOFTRSA_H #define _SOFTRSA_H #ifdef __cplusplus extern "C" { #endif #include #include #include "softObject.h" #include "softSession.h" #include #include typedef struct soft_rsa_ctx { soft_object_t *key; } soft_rsa_ctx_t; /* * Function Prototypes. */ /* RSA */ CK_RV soft_rsa_encrypt(soft_object_t *, CK_BYTE_PTR, uint32_t, CK_BYTE_PTR, int); CK_RV soft_rsa_decrypt(soft_object_t *, CK_BYTE_PTR, uint32_t, CK_BYTE_PTR); CK_RV soft_rsa_crypt_init_common(soft_session_t *, CK_MECHANISM_PTR, soft_object_t *, boolean_t); CK_RV soft_rsa_encrypt_common(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR, CK_MECHANISM_TYPE); CK_RV soft_rsa_decrypt_common(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR, CK_MECHANISM_TYPE); CK_RV soft_rsa_sign_verify_init_common(soft_session_t *, CK_MECHANISM_PTR, soft_object_t *, boolean_t); CK_RV soft_rsa_verify_common(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG, CK_MECHANISM_TYPE); CK_RV soft_rsa_sign_common(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR, CK_MECHANISM_TYPE); CK_RV soft_rsa_digest_sign_common(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR, CK_MECHANISM_TYPE, boolean_t); CK_RV soft_rsa_digest_verify_common(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG, CK_MECHANISM_TYPE, boolean_t); CK_RV soft_rsa_genkey_pair(soft_object_t *, soft_object_t *); CK_RV soft_rsa_verify_recover(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR); #ifdef __cplusplus } #endif #endif /* _SOFTRSA_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include "softGlobal.h" #include "softSession.h" CK_RV C_SeedRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSeed, CK_ULONG ulSeedLen) { CK_RV rv; soft_session_t *session_p; boolean_t lock_held = B_FALSE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer just for validity check. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); SES_REFRELE(session_p, lock_held); if ((pSeed == NULL) || (ulSeedLen == 0)) { return (CKR_ARGUMENTS_BAD); } if (pkcs11_seed_urandom(pSeed, ulSeedLen) < 0) { if (errno == EACCES) return (CKR_RANDOM_SEED_NOT_SUPPORTED); return (CKR_DEVICE_ERROR); } return (CKR_OK); } CK_RV C_GenerateRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pRandomData, CK_ULONG ulRandomLen) { CK_RV rv; soft_session_t *session_p; boolean_t lock_held = B_FALSE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer just for validity check. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); SES_REFRELE(session_p, lock_held); if ((pRandomData == NULL) || (ulRandomLen == 0)) { return (CKR_ARGUMENTS_BAD); } if (pkcs11_get_urandom(pRandomData, ulRandomLen) < 0) return (CKR_DEVICE_ERROR); return (CKR_OK); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2020 Joyent, Inc. */ #include #include #include #include #include #include #include #include #include "softGlobal.h" #include "softKeys.h" #include "softKeystore.h" #include "softMAC.h" #include "softObject.h" #include "softSession.h" #include "softSSL.h" /* * This files contains the implementation of the following PKCS#11 * mechanisms needed by SSL: * CKM_SSL3_MASTER_KEY_DERIVE * CKM_SSL3_MASTER_KEY_DERIVE_DH * CKM_SSL3_KEY_AND_DERIVE * CKM_TLS_MASTER_KEY_DERIVE * CKM_TLS_MASTER_KEY_DERIVE_DH * CKM_TLS_KEY_AND_DERIVE * * SSL refers to common functions between SSL v3.0 and SSL v3.1 (a.k.a TLS.) */ #define MAX_KEYBLOCK 160 /* should be plenty for all known cipherspecs */ #define MAX_DEFAULT_ATTRS 10 /* Enough for major applicarions */ static char *ssl3_const_vals[] = { "A", "BB", "CCC", "DDDD", "EEEEE", "FFFFFF", "GGGGGGG", "HHHHHHHH", "IIIIIIIII", "JJJJJJJJJJ", }; static uint_t ssl3_const_lens[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; static uchar_t TLS_MASTER_SECRET_LABEL[] = {"master secret"}; #define TLS_MASTER_SECRET_LABEL_LEN 13 static uchar_t TLS_KEY_EXPANSION_LABEL[] = {"key expansion"}; #define TLS_KEY_EXPANSION_LABEL_LEN 13 static uchar_t TLS_CLIENT_KEY_LABEL[] = {"client write key"}; #define TLS_CLIENT_KEY_LABEL_LEN 16 static uchar_t TLS_SERVER_KEY_LABEL[] = {"server write key"}; #define TLS_SERVER_KEY_LABEL_LEN 16 static uchar_t TLS_IV_BLOCK_LABEL[] = {"IV block"}; #define TLS_IV_BLOCK_LABEL_LEN 8 static void P_MD5(uchar_t *, uint_t, uchar_t *, uint_t, uchar_t *, uint_t, uchar_t *, uint_t, uchar_t *, uint_t, boolean_t); static void P_SHA1(uchar_t *, uint_t, uchar_t *, uint_t, uchar_t *, uint_t, uchar_t *, uint_t, uchar_t *, uint_t, boolean_t); static CK_RV soft_add_derived_key(CK_ATTRIBUTE_PTR, CK_ULONG, CK_OBJECT_HANDLE_PTR, soft_session_t *, soft_object_t *); static void soft_delete_derived_key(soft_session_t *, soft_object_t *); static void soft_ssl_weaken_key(CK_MECHANISM_PTR, uchar_t *, uint_t, uchar_t *, uint_t, uchar_t *, uint_t, uchar_t *, boolean_t); /* * soft_ssl3_churn() * Called for derivation of the master secret from the pre-master secret, * and for the derivation of the key_block in an SSL3 handshake * result is assumed to be larger than rounds * MD5_HASH_SIZE. */ static void soft_ssl3_churn(uchar_t *secret, uint_t secretlen, uchar_t *rand1, uint_t rand1len, uchar_t *rand2, uint_t rand2len, int rounds, uchar_t *result) { SHA1_CTX sha1_ctx; MD5_CTX md5_ctx; uchar_t sha1_digest[SHA1_HASH_SIZE]; int i; uchar_t *ms = result; for (i = 0; i < rounds; i++) { SHA1Init(&sha1_ctx); SHA1Update(&sha1_ctx, (const uint8_t *)ssl3_const_vals[i], ssl3_const_lens[i]); SHA1Update(&sha1_ctx, secret, secretlen); SHA1Update(&sha1_ctx, rand1, rand1len); SHA1Update(&sha1_ctx, rand2, rand2len); SHA1Final(sha1_digest, &sha1_ctx); MD5Init(&md5_ctx); MD5Update(&md5_ctx, secret, secretlen); MD5Update(&md5_ctx, sha1_digest, SHA1_HASH_SIZE); MD5Final(ms, &md5_ctx); ms += MD5_HASH_SIZE; } } /* * This TLS generic Pseudo Random Function expands a triplet * {secret, label, seed} into any arbitrary length string of pseudo * random bytes. * Here, it is called for the derivation of the master secret from the * pre-master secret, and for the derivation of the key_block in a TLS * handshake */ static void soft_tls_prf(uchar_t *secret, uint_t secretlen, uchar_t *label, uint_t labellen, uchar_t *rand1, uint_t rand1len, uchar_t *rand2, uint_t rand2len, uchar_t *result, uint_t resultlen) { uchar_t *S1, *S2; uchar_t md5_digested_key[MD5_HASH_SIZE]; uchar_t sha1_digested_key[SHA1_HASH_SIZE]; uint_t L_S, L_S1, L_S2; /* secret is NULL for IV's in exportable ciphersuites */ if (secret == NULL) { L_S = 0; L_S2 = L_S1 = 0; S1 = NULL; S2 = NULL; goto do_P_HASH; } L_S = roundup(secretlen, 2) / 2; L_S1 = L_S; L_S2 = L_S; S1 = secret; S2 = secret + (secretlen / 2); /* Possible overlap of S1 and S2. */ /* Reduce the half secrets if bigger than the HASH's block size */ if (L_S > MD5_HMAC_BLOCK_SIZE) { MD5_CTX md5_ctx; SHA1_CTX sha1_ctx; MD5Init(&md5_ctx); MD5Update(&md5_ctx, S1, L_S); MD5Final(md5_digested_key, &md5_ctx); S1 = md5_digested_key; L_S1 = MD5_HASH_SIZE; SHA1Init(&sha1_ctx); SHA1Update(&sha1_ctx, S2, L_S); SHA1Final(sha1_digested_key, &sha1_ctx); S2 = sha1_digested_key; L_S2 = SHA1_HASH_SIZE; } /* * PRF(secret, label, seed) = P_MD5(S1, label + seed) XOR * P_SHA-1(S2, label + seed); * the 'seed' here is rand1 + rand2 */ do_P_HASH: /* The first one writes directly to the result */ P_MD5(S1, L_S1, label, labellen, rand1, rand1len, rand2, rand2len, result, resultlen, B_FALSE); /* The second one XOR's with the result. */ P_SHA1(S2, L_S2, label, labellen, rand1, rand1len, rand2, rand2len, result, resultlen, B_TRUE); } /* * These two expansion routines are very similar. (they can merge one day). * They implement the P_HASH() function for MD5 and for SHA1, as defined in * RFC2246: * * P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) + * HMAC_hash(secret, A(2) + seed) + * HMAC_hash(secret, A(3) + seed) + ... * Where + indicates concatenation. * A() is defined as: * A(0) = seed * A(i) = HMAC_hash(secret, A(i-1)) * * The seed is the concatenation of 'babel', 'rand1', and 'rand2'. */ static void P_MD5(uchar_t *secret, uint_t secretlen, uchar_t *label, uint_t labellen, uchar_t *rand1, uint_t rand1len, uchar_t *rand2, uint_t rand2len, uchar_t *result, uint_t resultlen, boolean_t xor_it) { uint32_t md5_ipad[MD5_HMAC_INTS_PER_BLOCK]; uint32_t md5_opad[MD5_HMAC_INTS_PER_BLOCK]; uchar_t md5_hmac[MD5_HASH_SIZE]; uchar_t A[MD5_HASH_SIZE]; md5_hc_ctx_t md5_hmac_ctx; uchar_t *res, *cur; uint_t left = resultlen; int i; /* good compilers will leverage the aligment */ bzero(md5_ipad, MD5_HMAC_BLOCK_SIZE); bzero(md5_opad, MD5_HMAC_BLOCK_SIZE); if (secretlen > 0) { bcopy(secret, md5_ipad, secretlen); bcopy(secret, md5_opad, secretlen); } /* A(1) = HMAC_MD5(secret, rand1 + rand2) */ md5_hmac_ctx_init(&md5_hmac_ctx, md5_ipad, md5_opad); SOFT_MAC_UPDATE(MD5, &md5_hmac_ctx, label, labellen); SOFT_MAC_UPDATE(MD5, &md5_hmac_ctx, rand1, rand1len); SOFT_MAC_UPDATE(MD5, &md5_hmac_ctx, rand2, rand2len); SOFT_MAC_FINAL(MD5, &md5_hmac_ctx, A); if (xor_it) { res = md5_hmac; cur = result; } else { res = result; } while (left > 0) { /* * Compute HMAC_MD5(secret, A(i) + seed); * The secret is already expanded in the ictx and octx, so * we can call the SOFT_MAC_INIT_CTX() directly. */ SOFT_MAC_INIT_CTX(MD5, &md5_hmac_ctx, md5_ipad, md5_opad, MD5_HMAC_BLOCK_SIZE); SOFT_MAC_UPDATE(MD5, &md5_hmac_ctx, A, MD5_HASH_SIZE); SOFT_MAC_UPDATE(MD5, &md5_hmac_ctx, label, labellen); SOFT_MAC_UPDATE(MD5, &md5_hmac_ctx, rand1, rand1len); SOFT_MAC_UPDATE(MD5, &md5_hmac_ctx, rand2, rand2len); if (left > MD5_HASH_SIZE) { SOFT_MAC_FINAL(MD5, &md5_hmac_ctx, res); if (xor_it) { for (i = 0; i < MD5_HASH_SIZE; i++) { *cur ^= res[i]; cur++; } } else { res += MD5_HASH_SIZE; } left -= MD5_HASH_SIZE; } else { SOFT_MAC_FINAL(MD5, &md5_hmac_ctx, md5_hmac); if (xor_it) { for (i = 0; i < left; i++) { *cur ^= md5_hmac[i]; cur++; } } else { bcopy(md5_hmac, res, left); } break; } /* A(i) = HMAC_MD5(secret, A(i-1) */ SOFT_MAC_INIT_CTX(MD5, &md5_hmac_ctx, md5_ipad, md5_opad, MD5_HMAC_BLOCK_SIZE); SOFT_MAC_UPDATE(MD5, &md5_hmac_ctx, A, MD5_HASH_SIZE); SOFT_MAC_FINAL(MD5, &md5_hmac_ctx, A); } } static void P_SHA1(uchar_t *secret, uint_t secretlen, uchar_t *label, uint_t labellen, uchar_t *rand1, uint_t rand1len, uchar_t *rand2, uint_t rand2len, uchar_t *result, uint_t resultlen, boolean_t xor_it) { uint32_t sha1_ipad[SHA1_HMAC_INTS_PER_BLOCK]; uint32_t sha1_opad[SHA1_HMAC_INTS_PER_BLOCK]; uchar_t sha1_hmac[SHA1_HASH_SIZE]; uchar_t A[SHA1_HASH_SIZE]; sha1_hc_ctx_t sha1_hmac_ctx; uchar_t *res, *cur; uint_t left = resultlen; int i; /* good compilers will leverage the aligment */ bzero(sha1_ipad, SHA1_HMAC_BLOCK_SIZE); bzero(sha1_opad, SHA1_HMAC_BLOCK_SIZE); if (secretlen > 0) { bcopy(secret, sha1_ipad, secretlen); bcopy(secret, sha1_opad, secretlen); } /* A(1) = HMAC_SHA1(secret, rand1 + rand2) */ sha1_hmac_ctx_init(&sha1_hmac_ctx, sha1_ipad, sha1_opad); SOFT_MAC_UPDATE(SHA1, &sha1_hmac_ctx, label, labellen); SOFT_MAC_UPDATE(SHA1, &sha1_hmac_ctx, rand1, rand1len); SOFT_MAC_UPDATE(SHA1, &sha1_hmac_ctx, rand2, rand2len); SOFT_MAC_FINAL(SHA1, &sha1_hmac_ctx, A); if (xor_it) { res = sha1_hmac; cur = result; } else { res = result; } while (left > 0) { /* * Compute HMAC_SHA1(secret, A(i) + seed); * The secret is already expanded in the ictx and octx, so * we can call the SOFT_MAC_INIT_CTX() directly. */ SOFT_MAC_INIT_CTX(SHA1, &sha1_hmac_ctx, (const uchar_t *)sha1_ipad, (const uchar_t *)sha1_opad, SHA1_HMAC_BLOCK_SIZE); SOFT_MAC_UPDATE(SHA1, &sha1_hmac_ctx, A, SHA1_HASH_SIZE); SOFT_MAC_UPDATE(SHA1, &sha1_hmac_ctx, label, labellen); SOFT_MAC_UPDATE(SHA1, &sha1_hmac_ctx, rand1, rand1len); SOFT_MAC_UPDATE(SHA1, &sha1_hmac_ctx, rand2, rand2len); if (left > SHA1_HASH_SIZE) { SOFT_MAC_FINAL(SHA1, &sha1_hmac_ctx, res); if (xor_it) { for (i = 0; i < SHA1_HASH_SIZE; i++) { *cur ^= res[i]; cur++; } } else { res += SHA1_HASH_SIZE; } left -= SHA1_HASH_SIZE; } else { SOFT_MAC_FINAL(SHA1, &sha1_hmac_ctx, sha1_hmac); if (xor_it) { for (i = 0; i < left; i++) { *cur ^= sha1_hmac[i]; cur++; } } else { bcopy(sha1_hmac, res, left); } break; } /* A(i) = HMAC_SHA1(secret, A(i-1) */ SOFT_MAC_INIT_CTX(SHA1, &sha1_hmac_ctx, (const uchar_t *)sha1_ipad, (const uchar_t *)sha1_opad, SHA1_HMAC_BLOCK_SIZE); SOFT_MAC_UPDATE(SHA1, &sha1_hmac_ctx, A, SHA1_HASH_SIZE); SOFT_MAC_FINAL(SHA1, &sha1_hmac_ctx, A); } } /* This function handles the call from C_DeriveKey for CKM_TLS_PRF */ CK_RV derive_tls_prf(CK_TLS_PRF_PARAMS_PTR param, soft_object_t *basekey_p) { if (param->pOutput == NULL || param->pulOutputLen == 0) return (CKR_BUFFER_TOO_SMALL); (void) soft_tls_prf(OBJ_SEC_VALUE(basekey_p), OBJ_SEC_VALUE_LEN(basekey_p), param->pLabel, param->ulLabelLen, param->pSeed, param->ulSeedLen, NULL, 0, param->pOutput, *param->pulOutputLen); return (CKR_OK); } /* * soft_ssl_master_key_derive() * * Arguments: * . session_p * . mech_p: key derivation mechanism. the mechanism parameter carries the * client and master random from the Hello handshake messages. * . basekey_p: The pre-master secret key. * . pTemplate & ulAttributeCount: Any extra attributes for the key to be * created. * . phKey: store for handle to the derived key. * * Description: * Derive the SSL master secret from the pre-master secret, the client * and server random. * In SSL 3.0, master_secret = * MD5(pre_master_secret + SHA('A' + pre_master_secret + * ClientHello.random + ServerHello.random)) + * MD5(pre_master_secret + SHA('BB' + pre_master_secret + * ClientHello.random + ServerHello.random)) + * MD5(pre_master_secret + SHA('CCC' + pre_master_secret + * ClientHello.random + ServerHello.random)); * * In TLS 1.0 (a.k.a. SSL 3.1), master_secret = * PRF(pre_master_secret, "master secret", * ClientHello.random + ServerHello.random) */ CK_RV soft_ssl_master_key_derive(soft_session_t *sp, CK_MECHANISM_PTR mech, soft_object_t *basekey_p, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey) { uchar_t *pmsecret = OBJ_SEC_VALUE(basekey_p); #ifdef __sparcv9 /* LINTED */ uint_t pmlen = (uint_t)OBJ_SEC_VALUE_LEN(basekey_p); #else /* __sparcv9 */ uint_t pmlen = OBJ_SEC_VALUE_LEN(basekey_p); #endif /* __sparcv9 */ CK_SSL3_MASTER_KEY_DERIVE_PARAMS *mkd_params; CK_SSL3_RANDOM_DATA *random_data; CK_VERSION_PTR pVersion; uchar_t ssl_master_secret[48]; CK_OBJECT_CLASS class = CKO_SECRET_KEY; CK_KEY_TYPE keyType = CKK_GENERIC_SECRET; CK_BBOOL true = TRUE; CK_ATTRIBUTE obj_tmpl[MAX_DEFAULT_ATTRS]; CK_ATTRIBUTE_PTR new_tmpl; CK_ULONG newattrcount; boolean_t new_tmpl_allocated = B_FALSE, is_tls = B_FALSE; ulong_t i; CK_RV rv = CKR_OK; uint_t ClientRandomLen, ServerRandomLen; /* Check the validity of the mechanism's parameter */ mkd_params = (CK_SSL3_MASTER_KEY_DERIVE_PARAMS *)mech->pParameter; if (mkd_params == NULL || mech->ulParameterLen != sizeof (CK_SSL3_MASTER_KEY_DERIVE_PARAMS)) return (CKR_MECHANISM_PARAM_INVALID); pVersion = mkd_params->pVersion; switch (mech->mechanism) { case CKM_TLS_MASTER_KEY_DERIVE: is_tls = B_TRUE; /* FALLTHRU */ case CKM_SSL3_MASTER_KEY_DERIVE: /* Invalid pre-master key length. What else to return? */ if (pmlen != 48) return (CKR_ARGUMENTS_BAD); /* Get the SSL version number from the premaster secret */ if (pVersion == NULL_PTR) return (CKR_MECHANISM_PARAM_INVALID); bcopy(pmsecret, pVersion, sizeof (CK_VERSION)); break; case CKM_TLS_MASTER_KEY_DERIVE_DH: is_tls = B_TRUE; /* FALLTHRU */ case CKM_SSL3_MASTER_KEY_DERIVE_DH: if (pVersion != NULL_PTR) return (CKR_MECHANISM_PARAM_INVALID); } random_data = &mkd_params->RandomInfo; #ifdef __sparcv9 /* LINTED */ ClientRandomLen = (uint_t)random_data->ulClientRandomLen; /* LINTED */ ServerRandomLen = (uint_t)random_data->ulServerRandomLen; #else /* __sparcv9 */ ClientRandomLen = random_data->ulClientRandomLen; ServerRandomLen = random_data->ulServerRandomLen; #endif /* __sparcv9 */ if (random_data->pClientRandom == NULL_PTR || ClientRandomLen == 0 || random_data->pServerRandom == NULL_PTR || ServerRandomLen == 0) { return (CKR_MECHANISM_PARAM_INVALID); } /* Now the actual secret derivation */ if (!is_tls) { soft_ssl3_churn(pmsecret, pmlen, random_data->pClientRandom, ClientRandomLen, random_data->pServerRandom, ServerRandomLen, 3, ssl_master_secret); } else { soft_tls_prf(pmsecret, pmlen, TLS_MASTER_SECRET_LABEL, TLS_MASTER_SECRET_LABEL_LEN, random_data->pClientRandom, ClientRandomLen, random_data->pServerRandom, ServerRandomLen, ssl_master_secret, 48); } /* * The object creation attributes need to be in one contiguous * array. In addition to the attrs from the application supplied * pTemplates, We need to add the class, type, value, valuelen and * CKA_DERIVE. * In the most likely case, the application passes between zero and * handful of attributes, We optimize for that case by allocating * the new template on the stack. Oherwise we malloc() it. */ newattrcount = ulAttributeCount + 4; if (newattrcount > MAX_DEFAULT_ATTRS) { new_tmpl = malloc(sizeof (CK_ATTRIBUTE) * newattrcount); if (new_tmpl == NULL) return (CKR_HOST_MEMORY); new_tmpl_allocated = B_TRUE; } else new_tmpl = obj_tmpl; /* * Fill in the new template. * We put the attributes contributed by the mechanism first * so that they override the application supplied ones. */ new_tmpl[0].type = CKA_CLASS; new_tmpl[0].pValue = &class; new_tmpl[0].ulValueLen = sizeof (class); new_tmpl[1].type = CKA_KEY_TYPE; new_tmpl[1].pValue = &keyType; new_tmpl[1].ulValueLen = sizeof (keyType); new_tmpl[2].type = CKA_DERIVE; new_tmpl[2].pValue = &true; new_tmpl[2].ulValueLen = sizeof (true); new_tmpl[3].type = CKA_VALUE; new_tmpl[3].pValue = ssl_master_secret; new_tmpl[3].ulValueLen = 48; /* Any attributes left? */ if (ulAttributeCount > 0) { /* Validate the default class and type attributes */ for (i = 0; i < ulAttributeCount; i++) { /* The caller is responsible for proper alignment */ if ((pTemplate[i].type == CKA_CLASS) && (*((CK_OBJECT_CLASS *)pTemplate[i].pValue) != CKO_SECRET_KEY)) { rv = CKR_TEMPLATE_INCONSISTENT; goto out; } if ((pTemplate[i].type == CKA_KEY_TYPE) && (*((CK_KEY_TYPE *)pTemplate[i].pValue) != CKK_GENERIC_SECRET)) { rv = CKR_TEMPLATE_INCONSISTENT; goto out; } } bcopy(pTemplate, &new_tmpl[4], ulAttributeCount * sizeof (CK_ATTRIBUTE)); } rv = soft_add_derived_key(new_tmpl, newattrcount, phKey, sp, basekey_p); out: if (new_tmpl_allocated) free(new_tmpl); return (rv); } /* * soft_ssl3_key_and_mac_derive() * * Arguments: * . session_p * . mech_p: key derivation mechanism. the mechanism parameter carries the * client and mastter random from the Hello handshake messages, * the specification of the key and IV sizes, and the location * for the resulting keys and IVs. * . basekey_p: The master secret key. * . pTemplate & ulAttributeCount: Any extra attributes for the key to be * created. * * Description: * Derive the SSL key material (Client and server MAC secrets, symmetric * keys and IVs), from the master secret and the client * and server random. * First a keyblock is generated usining the following formula: * key_block = * MD5(master_secret + SHA(`A' + master_secret + * ServerHello.random + * ClientHello.random)) + * MD5(master_secret + SHA(`BB' + master_secret + * ServerHello.random + * ClientHello.random)) + * MD5(master_secret + SHA(`CCC' + master_secret + * ServerHello.random + * ClientHello.random)) + [...]; * * In TLS 1.0 (a.k.a. SSL 3.1), key_block = * PRF(master_secret, "key expansion", * ServerHello.random + ClientHello.random) * * Then the keys materials are taken from the keyblock. */ CK_RV soft_ssl_key_and_mac_derive(soft_session_t *sp, CK_MECHANISM_PTR mech, soft_object_t *basekey_p, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount) { uchar_t *msecret = OBJ_SEC_VALUE(basekey_p); #ifdef __sparcv9 /* LINTED */ uint_t mslen = (uint_t)OBJ_SEC_VALUE_LEN(basekey_p); #else /* __sparcv9 */ uint_t mslen = OBJ_SEC_VALUE_LEN(basekey_p); #endif /* __sparcv9 */ CK_SSL3_KEY_MAT_PARAMS *km_params; CK_SSL3_RANDOM_DATA *random_data; CK_SSL3_KEY_MAT_OUT *kmo; uchar_t key_block[MAX_KEYBLOCK], *kb, *export_keys = NULL; CK_OBJECT_CLASS class = CKO_SECRET_KEY; CK_KEY_TYPE keyType = CKK_GENERIC_SECRET; CK_BBOOL true = TRUE; CK_ATTRIBUTE obj_tmpl[MAX_DEFAULT_ATTRS]; CK_ATTRIBUTE_PTR new_tmpl; ulong_t newattrcount, mac_key_bytes, secret_key_bytes, iv_bytes; ulong_t extra_attr_count; uint_t size; int rounds, n = 0; boolean_t new_tmpl_allocated = B_FALSE, isExport; CK_RV rv = CKR_OK; uint_t ClientRandomLen, ServerRandomLen; /* Check the validity of the mechanism's parameter */ km_params = (CK_SSL3_KEY_MAT_PARAMS *)mech->pParameter; if (km_params == NULL || mech->ulParameterLen != sizeof (CK_SSL3_KEY_MAT_PARAMS) || (kmo = km_params->pReturnedKeyMaterial) == NULL) return (CKR_MECHANISM_PARAM_INVALID); isExport = (km_params->bIsExport == TRUE); random_data = &km_params->RandomInfo; #ifdef __sparcv9 /* LINTED */ ClientRandomLen = (uint_t)random_data->ulClientRandomLen; /* LINTED */ ServerRandomLen = (uint_t)random_data->ulServerRandomLen; #else /* __sparcv9 */ ClientRandomLen = random_data->ulClientRandomLen; ServerRandomLen = random_data->ulServerRandomLen; #endif /* __sparcv9 */ if (random_data->pClientRandom == NULL_PTR || ClientRandomLen == 0 || random_data->pServerRandom == NULL_PTR || ServerRandomLen == 0) { return (CKR_MECHANISM_PARAM_INVALID); } mac_key_bytes = km_params->ulMacSizeInBits / 8; secret_key_bytes = km_params->ulKeySizeInBits / 8; iv_bytes = km_params->ulIVSizeInBits / 8; if ((iv_bytes > 0) && ((kmo->pIVClient == NULL) || (kmo->pIVServer == NULL))) return (CKR_MECHANISM_PARAM_INVALID); /* * For exportable ciphersuites, the IV's aren't taken from the * key block. They are directly derived from the client and * server random data. * For SSL3.0: * client_write_IV = MD5(ClientHello.random + ServerHello.random); * server_write_IV = MD5(ServerHello.random + ClientHello.random); * For TLS1.0: * iv_block = PRF("", "IV block", client_random + * server_random)[0..15] * client_write_IV = iv_block[0..7] * server_write_IV = iv_block[8..15] */ if ((isExport) && (iv_bytes > 0)) { if (mech->mechanism == CKM_SSL3_KEY_AND_MAC_DERIVE) { MD5_CTX exp_md5_ctx; if (iv_bytes > MD5_HASH_SIZE) return (CKR_MECHANISM_PARAM_INVALID); MD5Init(&exp_md5_ctx); MD5Update(&exp_md5_ctx, random_data->pClientRandom, ClientRandomLen); MD5Update(&exp_md5_ctx, random_data->pServerRandom, ServerRandomLen); /* there's room in key_block. use it */ MD5Final(key_block, &exp_md5_ctx); bcopy(key_block, kmo->pIVClient, iv_bytes); MD5Init(&exp_md5_ctx); MD5Update(&exp_md5_ctx, random_data->pServerRandom, ServerRandomLen); MD5Update(&exp_md5_ctx, random_data->pClientRandom, ClientRandomLen); MD5Final(key_block, &exp_md5_ctx); bcopy(key_block, kmo->pIVServer, iv_bytes); } else { uchar_t iv_block[16]; if (iv_bytes != 8) return (CKR_MECHANISM_PARAM_INVALID); soft_tls_prf(NULL, 0, TLS_IV_BLOCK_LABEL, TLS_IV_BLOCK_LABEL_LEN, random_data->pClientRandom, ClientRandomLen, random_data->pServerRandom, ServerRandomLen, iv_block, 16); bcopy(iv_block, kmo->pIVClient, 8); bcopy(iv_block + 8, kmo->pIVServer, 8); } /* so we won't allocate a key_block bigger than needed */ iv_bytes = 0; } /* Now the actual secret derivation */ #ifdef __sparcv9 /* LINTED */ size = (uint_t)((mac_key_bytes + secret_key_bytes + iv_bytes) * 2); #else /* __sparcv9 */ size = (mac_key_bytes + secret_key_bytes + iv_bytes) * 2; #endif /* __sparcv9 */ /* Need to handle this better */ if (size > MAX_KEYBLOCK) return (CKR_MECHANISM_PARAM_INVALID); rounds = howmany(size, MD5_HASH_SIZE); kb = key_block; if (mech->mechanism == CKM_SSL3_KEY_AND_MAC_DERIVE) { soft_ssl3_churn(msecret, mslen, random_data->pServerRandom, ServerRandomLen, random_data->pClientRandom, ClientRandomLen, rounds, kb); } else { soft_tls_prf(msecret, mslen, TLS_KEY_EXPANSION_LABEL, TLS_KEY_EXPANSION_LABEL_LEN, random_data->pServerRandom, ServerRandomLen, random_data->pClientRandom, ClientRandomLen, kb, size); } /* Now create the objects */ kmo->hClientMacSecret = CK_INVALID_HANDLE; kmo->hServerMacSecret = CK_INVALID_HANDLE; kmo->hClientKey = CK_INVALID_HANDLE; kmo->hServerKey = CK_INVALID_HANDLE; /* First the MAC secrets */ if (mac_key_bytes > 0) { obj_tmpl[0].type = CKA_CLASS; obj_tmpl[0].pValue = &class; /* CKO_SECRET_KEY */ obj_tmpl[0].ulValueLen = sizeof (class); obj_tmpl[1].type = CKA_KEY_TYPE; obj_tmpl[1].pValue = &keyType; /* CKK_GENERIC_SECRET */ obj_tmpl[1].ulValueLen = sizeof (keyType); obj_tmpl[2].type = CKA_DERIVE; obj_tmpl[2].pValue = &true; obj_tmpl[2].ulValueLen = sizeof (true); obj_tmpl[3].type = CKA_SIGN; obj_tmpl[3].pValue = &true; obj_tmpl[3].ulValueLen = sizeof (true); obj_tmpl[4].type = CKA_VERIFY; obj_tmpl[4].pValue = &true; obj_tmpl[4].ulValueLen = sizeof (true); obj_tmpl[5].type = CKA_VALUE; obj_tmpl[5].pValue = kb; obj_tmpl[5].ulValueLen = mac_key_bytes; rv = soft_add_derived_key(obj_tmpl, 6, &(kmo->hClientMacSecret), sp, basekey_p); if (rv != CKR_OK) goto out_err; kb += mac_key_bytes; obj_tmpl[5].pValue = kb; rv = soft_add_derived_key(obj_tmpl, 6, &(kmo->hServerMacSecret), sp, basekey_p); if (rv != CKR_OK) goto out_err; kb += mac_key_bytes; } /* Then the symmetric ciphers keys */ extra_attr_count = (secret_key_bytes == 0) ? 6 : 5; newattrcount = ulAttributeCount + extra_attr_count; if (newattrcount > MAX_DEFAULT_ATTRS) { new_tmpl = malloc(sizeof (CK_ATTRIBUTE) * newattrcount); if (new_tmpl == NULL) return (CKR_HOST_MEMORY); new_tmpl_allocated = B_TRUE; } else new_tmpl = obj_tmpl; new_tmpl[n].type = CKA_CLASS; new_tmpl[n].pValue = &class; /* CKO_SECRET_KEY */ new_tmpl[n].ulValueLen = sizeof (class); ++n; /* * The keyType comes from the application's template, and depends * on the ciphersuite. The only exception is authentication only * ciphersuites which do not use cipher keys. */ if (secret_key_bytes == 0) { new_tmpl[n].type = CKA_KEY_TYPE; new_tmpl[n].pValue = &keyType; /* CKK_GENERIC_SECRET */ new_tmpl[n].ulValueLen = sizeof (keyType); n++; } new_tmpl[n].type = CKA_DERIVE; new_tmpl[n].pValue = &true; new_tmpl[n].ulValueLen = sizeof (true); n++; new_tmpl[n].type = CKA_ENCRYPT; new_tmpl[n].pValue = &true; new_tmpl[n].ulValueLen = sizeof (true); n++; new_tmpl[n].type = CKA_DECRYPT; new_tmpl[n].pValue = &true; new_tmpl[n].ulValueLen = sizeof (true); n++; new_tmpl[n].type = CKA_VALUE; new_tmpl[n].pValue = NULL; new_tmpl[n].ulValueLen = 0; if (secret_key_bytes > 0) { if (isExport) { if (secret_key_bytes > MD5_HASH_SIZE) { rv = CKR_MECHANISM_PARAM_INVALID; goto out_err; } if ((export_keys = malloc(2 * MD5_HASH_SIZE)) == NULL) { rv = CKR_HOST_MEMORY; goto out_err; } #ifdef __sparcv9 /* LINTED */ soft_ssl_weaken_key(mech, kb, (uint_t)secret_key_bytes, random_data->pClientRandom, ClientRandomLen, random_data->pServerRandom, ServerRandomLen, export_keys, B_TRUE); #else /* __sparcv9 */ soft_ssl_weaken_key(mech, kb, secret_key_bytes, random_data->pClientRandom, ClientRandomLen, random_data->pServerRandom, ServerRandomLen, export_keys, B_TRUE); #endif /* __sparcv9 */ new_tmpl[n].pValue = export_keys; new_tmpl[n].ulValueLen = MD5_HASH_SIZE; } else { new_tmpl[n].pValue = kb; new_tmpl[n].ulValueLen = secret_key_bytes; } } if (ulAttributeCount > 0) bcopy(pTemplate, &new_tmpl[extra_attr_count], ulAttributeCount * sizeof (CK_ATTRIBUTE)); rv = soft_add_derived_key(new_tmpl, newattrcount, &(kmo->hClientKey), sp, basekey_p); if (rv != CKR_OK) goto out_err; kb += secret_key_bytes; if (secret_key_bytes > 0) { if (isExport) { #ifdef __sparcv9 /* LINTED */ soft_ssl_weaken_key(mech, kb, (uint_t)secret_key_bytes, random_data->pServerRandom, ServerRandomLen, random_data->pClientRandom, ClientRandomLen, export_keys + MD5_HASH_SIZE, B_FALSE); #else /* __sparcv9 */ soft_ssl_weaken_key(mech, kb, secret_key_bytes, random_data->pServerRandom, ServerRandomLen, random_data->pClientRandom, ClientRandomLen, export_keys + MD5_HASH_SIZE, B_FALSE); #endif /* __sparcv9 */ new_tmpl[n].pValue = export_keys + MD5_HASH_SIZE; } else new_tmpl[n].pValue = kb; } rv = soft_add_derived_key(new_tmpl, newattrcount, &(kmo->hServerKey), sp, basekey_p); if (rv != CKR_OK) goto out_err; kb += secret_key_bytes; /* Finally, the IVs */ if (iv_bytes > 0) { bcopy(kb, kmo->pIVClient, iv_bytes); kb += iv_bytes; bcopy(kb, kmo->pIVServer, iv_bytes); } if (new_tmpl_allocated) free(new_tmpl); freezero(export_keys, 2 * MD5_HASH_SIZE); return (rv); out_err: if (kmo->hClientMacSecret != CK_INVALID_HANDLE) { (void) soft_delete_derived_key(sp, (soft_object_t *)(kmo->hClientMacSecret)); kmo->hClientMacSecret = CK_INVALID_HANDLE; } if (kmo->hServerMacSecret != CK_INVALID_HANDLE) { (void) soft_delete_derived_key(sp, (soft_object_t *)(kmo->hServerMacSecret)); kmo->hServerMacSecret = CK_INVALID_HANDLE; } if (kmo->hClientKey != CK_INVALID_HANDLE) { (void) soft_delete_derived_key(sp, (soft_object_t *)(kmo->hClientKey)); kmo->hClientKey = CK_INVALID_HANDLE; } if (kmo->hServerKey != CK_INVALID_HANDLE) { (void) soft_delete_derived_key(sp, (soft_object_t *)(kmo->hServerKey)); kmo->hServerKey = CK_INVALID_HANDLE; } if (new_tmpl_allocated) free(new_tmpl); freezero(export_keys, 2 * MD5_HASH_SIZE); return (rv); } /* * Add the derived key to the session, and, if it's a token object, * write it to the token. */ static CK_RV soft_add_derived_key(CK_ATTRIBUTE_PTR tmpl, CK_ULONG attrcount, CK_OBJECT_HANDLE_PTR phKey, soft_session_t *sp, soft_object_t *basekey_p) { CK_RV rv; soft_object_t *secret_key; if ((secret_key = calloc(1, sizeof (soft_object_t))) == NULL) { return (CKR_HOST_MEMORY); } if (((rv = soft_build_secret_key_object(tmpl, attrcount, secret_key, SOFT_CREATE_OBJ_INT, 0, (CK_KEY_TYPE)~0UL)) != CKR_OK) || ((rv = soft_pin_expired_check(secret_key)) != CKR_OK) || ((rv = soft_object_write_access_check(sp, secret_key)) != CKR_OK)) { free(secret_key); return (rv); } /* Set the sensitivity and extractability attributes as a needed */ soft_derive_enforce_flags(basekey_p, secret_key); /* Initialize the rest of stuffs in soft_object_t. */ (void) pthread_mutex_init(&secret_key->object_mutex, NULL); secret_key->magic_marker = SOFTTOKEN_OBJECT_MAGIC; /* ... and, if it needs to persist, write on the token */ if (IS_TOKEN_OBJECT(secret_key)) { secret_key->session_handle = CK_INVALID_HANDLE; soft_add_token_object_to_slot(secret_key); rv = soft_put_object_to_keystore(secret_key); if (rv != CKR_OK) { soft_delete_token_object(secret_key, B_FALSE, B_FALSE); return (rv); } *phKey = set_objecthandle(secret_key); return (CKR_OK); } /* Add the new object to the session's object list. */ soft_add_object_to_session(secret_key, sp); secret_key->session_handle = sp->handle; *phKey = set_objecthandle(secret_key); return (rv); } /* * Delete the derived key from the session, and, if it's a token object, * remove it from the token. */ static void soft_delete_derived_key(soft_session_t *sp, soft_object_t *key) { /* session_handle is the creating session. It's NULL for token objs */ if (IS_TOKEN_OBJECT(key)) soft_delete_token_object(key, B_FALSE, B_FALSE); else soft_delete_object(sp, key, B_FALSE, B_FALSE); } /* * soft_ssl_weaken_key() * Reduce the key length to an exportable size. * For SSL3.0: * final_client_write_key = MD5(client_write_key + * ClientHello.random + * ServerHello.random); * final_server_write_key = MD5(server_write_key + * ServerHello.random + * ClientHello.random); * For TLS1.0: * final_client_write_key = PRF(SecurityParameters.client_write_key, * "client write key", * SecurityParameters.client_random + * SecurityParameters.server_random)[0..15]; * final_server_write_key = PRF(SecurityParameters.server_write_key, * "server write key", * SecurityParameters.client_random + * SecurityParameters.server_random)[0..15]; */ static void soft_ssl_weaken_key(CK_MECHANISM_PTR mech, uchar_t *secret, uint_t secretlen, uchar_t *rand1, uint_t rand1len, uchar_t *rand2, uint_t rand2len, uchar_t *result, boolean_t isclient) { MD5_CTX exp_md5_ctx; uchar_t *label; uint_t labellen; if (mech->mechanism == CKM_SSL3_KEY_AND_MAC_DERIVE) { MD5Init(&exp_md5_ctx); MD5Update(&exp_md5_ctx, secret, secretlen); MD5Update(&exp_md5_ctx, rand1, rand1len); MD5Update(&exp_md5_ctx, rand2, rand2len); MD5Final(result, &exp_md5_ctx); } else { if (isclient) { label = TLS_CLIENT_KEY_LABEL; labellen = TLS_CLIENT_KEY_LABEL_LEN; soft_tls_prf(secret, secretlen, label, labellen, rand1, rand1len, rand2, rand2len, result, 16); } else { label = TLS_SERVER_KEY_LABEL; labellen = TLS_SERVER_KEY_LABEL_LEN; soft_tls_prf(secret, secretlen, label, labellen, rand2, rand2len, rand1, rand1len, result, 16); } } } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _SOFTSSL_H #define _SOFTSSL_H #ifdef __cplusplus extern "C" { #endif #include #include #include "softObject.h" #include "softSession.h" CK_RV soft_ssl_master_key_derive(soft_session_t *, CK_MECHANISM_PTR, soft_object_t *, CK_ATTRIBUTE_PTR, CK_ULONG, CK_OBJECT_HANDLE_PTR); CK_RV soft_ssl_key_and_mac_derive(soft_session_t *, CK_MECHANISM_PTR, soft_object_t *, CK_ATTRIBUTE_PTR, CK_ULONG); CK_RV derive_tls_prf(CK_TLS_PRF_PARAMS_PTR, soft_object_t *); #ifdef __cplusplus } #endif #endif /* _SOFTSSL_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include "softGlobal.h" #include "softSession.h" #include "softObject.h" #include "softKeystore.h" #include "softKeystoreUtil.h" CK_RV C_OpenSession(CK_SLOT_ID slotID, CK_FLAGS flags, CK_VOID_PTR pApplication, CK_NOTIFY Notify, CK_SESSION_HANDLE_PTR phSession) { CK_RV rv = CKR_OK; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* * For legacy reasons, the CKF_SERIAL_SESSION bit must always * be set. */ if (!(flags & CKF_SERIAL_SESSION)) return (CKR_SESSION_PARALLEL_NOT_SUPPORTED); if (slotID != SOFTTOKEN_SLOTID) return (CKR_SLOT_ID_INVALID); if (phSession == NULL) return (CKR_ARGUMENTS_BAD); /* * softtoken has no limit on the number of concurrent sessions * that the token allows. No need to check to see if the * token has too many sessions already open. */ /* Create a new session */ rv = soft_add_session(flags, pApplication, Notify, phSession); return (rv); } CK_RV C_CloseSession(CK_SESSION_HANDLE hSession) { CK_RV rv; soft_session_t *session_p; boolean_t lock_held = B_TRUE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); (void) pthread_mutex_lock(&session_p->session_mutex); /* * Set SESSION_IS_CLOSING flag so any access to this * session will be rejected. */ if (session_p->ses_close_sync & SESSION_IS_CLOSING) { SES_REFRELE(session_p, lock_held); return (CKR_SESSION_CLOSED); } session_p->ses_close_sync |= SESSION_IS_CLOSING; /* * Decrement the session reference count. * We hold the session lock, and SES_REFRELE() * will release the session lock for us. */ SES_REFRELE(session_p, lock_held); /* * Delete a session by calling soft_delete_session() with * a session pointer and a boolean arguments. Boolean * value FALSE is used to indicate that the caller does not * hold the lock on the global session list and also that * this is not a forced session close but an explicit request. * * soft_delete_session() will reset SESSION_IS_CLOSING * flag after it is done. */ rv = soft_delete_session(session_p, B_FALSE, B_FALSE); if (soft_session_cnt == 0) { /* Clean up private token objects from the token object list */ soft_delete_all_in_core_token_objects(PRIVATE_TOKEN); /* * Invalidate public token object handles instead of * deleting them. */ soft_validate_token_objects(B_FALSE); (void) pthread_mutex_lock(&soft_giant_mutex); soft_slot.authenticated = 0; soft_slot.userpin_change_needed = 0; (void) pthread_mutex_unlock(&soft_giant_mutex); } return (rv); } CK_RV C_CloseAllSessions(CK_SLOT_ID slotID) { CK_RV rv = CKR_OK; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); if (slotID != SOFTTOKEN_SLOTID) return (CKR_SLOT_ID_INVALID); /* Acquire the global session list lock */ (void) pthread_mutex_lock(&soft_sessionlist_mutex); /* * Set all_sessions_closing flag so any access to any * existing sessions will be rejected. */ all_sessions_closing = 1; (void) pthread_mutex_unlock(&soft_sessionlist_mutex); /* Delete all the sessions and release the allocated resources */ rv = soft_delete_all_sessions(B_FALSE); /* Clean up private token objects from the token object list */ soft_delete_all_in_core_token_objects(PRIVATE_TOKEN); /* Invalidate public token object handles instead of deleting them */ soft_validate_token_objects(B_FALSE); (void) pthread_mutex_lock(&soft_giant_mutex); soft_slot.authenticated = 0; soft_slot.userpin_change_needed = 0; (void) pthread_mutex_unlock(&soft_giant_mutex); (void) pthread_mutex_lock(&soft_sessionlist_mutex); /* Reset all_sessions_closing flag. */ all_sessions_closing = 0; (void) pthread_mutex_unlock(&soft_sessionlist_mutex); return (rv); } CK_RV C_GetSessionInfo(CK_SESSION_HANDLE hSession, CK_SESSION_INFO_PTR pInfo) { soft_session_t *session_p; CK_RV rv; boolean_t lock_held = B_TRUE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if (pInfo == NULL) { lock_held = B_FALSE; rv = CKR_ARGUMENTS_BAD; goto clean_exit; } (void) pthread_mutex_lock(&session_p->session_mutex); /* Provide information for the specified session */ pInfo->slotID = SOFTTOKEN_SLOTID; pInfo->state = session_p->state; pInfo->flags = session_p->flags; pInfo->ulDeviceError = 0; clean_exit: /* * Decrement the session reference count. * We hold the session lock, and SES_REFRELE() * will release the session lock for us. */ SES_REFRELE(session_p, lock_held); return (rv); } CK_RV C_GetOperationState(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState, CK_ULONG_PTR pulOperationStateLen) { soft_session_t *session_p; CK_RV rv; boolean_t lock_held = B_FALSE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); /* * Only check if pulOperationStateLen is NULL_PTR. * No need to check if pOperationState is NULL_PTR because * application might just ask for the length of buffer to hold * the OperationState. */ if (pulOperationStateLen == NULL_PTR) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } rv = soft_get_operationstate(session_p, pOperationState, pulOperationStateLen); clean_exit: SES_REFRELE(session_p, lock_held); return (rv); } CK_RV C_SetOperationState(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState, CK_ULONG ulOperationStateLen, CK_OBJECT_HANDLE hEncryptionKey, CK_OBJECT_HANDLE hAuthenticationKey) { soft_session_t *session_p; CK_RV rv; boolean_t lock_held = B_FALSE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if ((pOperationState == NULL_PTR) || (ulOperationStateLen == 0)) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } rv = soft_set_operationstate(session_p, pOperationState, ulOperationStateLen, hEncryptionKey, hAuthenticationKey); clean_exit: SES_REFRELE(session_p, lock_held); return (rv); } CK_RV C_Login(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen) { soft_session_t *session_p, *sp; CK_RV rv; boolean_t lock_held = B_FALSE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); /* Check the load status of keystore */ if (!soft_keystore_status(KEYSTORE_LOAD)) { SES_REFRELE(session_p, lock_held); return (CKR_DEVICE_REMOVED); } if (userType != CKU_USER) { SES_REFRELE(session_p, lock_held); return (CKR_USER_TYPE_INVALID); } if ((ulPinLen < MIN_PIN_LEN) || (ulPinLen > MAX_PIN_LEN)) { SES_REFRELE(session_p, lock_held); return (CKR_PIN_LEN_RANGE); } if (pPin == NULL_PTR) { /* * We don't support CKF_PROTECTED_AUTHENTICATION_PATH */ SES_REFRELE(session_p, lock_held); return (CKR_ARGUMENTS_BAD); } (void) pthread_mutex_lock(&soft_giant_mutex); if (soft_slot.authenticated) { (void) pthread_mutex_unlock(&soft_giant_mutex); SES_REFRELE(session_p, lock_held); return (CKR_USER_ALREADY_LOGGED_IN); } rv = soft_login(pPin, ulPinLen); if (rv == CKR_OK) { if (soft_slot.userpin_change_needed) { /* * This is the special case when the PIN is never * initialized in the keystore, which will always * return CKR_OK with "userpin_change_needed" set. */ (void) pthread_mutex_unlock(&soft_giant_mutex); SES_REFRELE(session_p, lock_held); return (rv); } soft_slot.authenticated = 1; (void) pthread_mutex_unlock(&soft_giant_mutex); } else { (void) pthread_mutex_unlock(&soft_giant_mutex); SES_REFRELE(session_p, lock_held); return (rv); } /* * Load all the private token objects from keystore. */ rv = soft_get_token_objects_from_keystore(PRI_TOKENOBJS); if (rv != CKR_OK) { SES_REFRELE(session_p, lock_held); return (rv); } /* Acquire the global session list lock */ (void) pthread_mutex_lock(&soft_sessionlist_mutex); sp = soft_session_list; while (sp) { (void) pthread_mutex_lock(&sp->session_mutex); if (sp->flags & CKF_RW_SESSION) { sp->state = CKS_RW_USER_FUNCTIONS; } else { sp->state = CKS_RO_USER_FUNCTIONS; } (void) pthread_mutex_unlock(&sp->session_mutex); sp = sp->next; } (void) pthread_mutex_unlock(&soft_sessionlist_mutex); SES_REFRELE(session_p, lock_held); return (rv); } CK_RV C_Logout(CK_SESSION_HANDLE hSession) { soft_session_t *session_p, *sp; CK_RV rv; boolean_t lock_held = B_FALSE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); (void) pthread_mutex_lock(&soft_giant_mutex); if (!soft_slot.authenticated) { if (!soft_slot.userpin_change_needed) { /* * Only if the PIN has been initialized in the keystore. */ (void) pthread_mutex_unlock(&soft_giant_mutex); SES_REFRELE(session_p, lock_held); return (CKR_USER_NOT_LOGGED_IN); } else { soft_slot.userpin_change_needed = 0; (void) pthread_mutex_unlock(&soft_giant_mutex); SES_REFRELE(session_p, lock_held); return (CKR_OK); } } soft_logout(); soft_slot.authenticated = 0; (void) pthread_mutex_unlock(&soft_giant_mutex); /* Acquire the global session list lock */ (void) pthread_mutex_lock(&soft_sessionlist_mutex); sp = soft_session_list; while (sp) { (void) pthread_mutex_lock(&sp->session_mutex); if (sp->flags & CKF_RW_SESSION) { sp->state = CKS_RW_PUBLIC_SESSION; } else { sp->state = CKS_RO_PUBLIC_SESSION; } (void) pthread_mutex_unlock(&sp->session_mutex); sp = sp->next; } (void) pthread_mutex_unlock(&soft_sessionlist_mutex); SES_REFRELE(session_p, lock_held); return (rv); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2020 Joyent, Inc. */ #ifndef _SOFTSESSION_H #define _SOFTSESSION_H #ifdef __cplusplus extern "C" { #endif #include #include #include #define SOFTTOKEN_SESSION_MAGIC 0xECF00002 /* * This is only used by the C_G(S)etOperationState. */ #define DIGEST_OP 1 /* * This is only used by the C_G(S)etOperationState. */ typedef struct internal_op_state { /* Holds the length of the saved state */ CK_ULONG op_len; /* crypto operation to be saved or restored */ CK_ULONG op_active; /* Holds the saved session state */ CK_STATE op_session_state; } internal_op_state_t; typedef struct crypto_active_op { CK_MECHANISM mech; void *context; uint32_t flags; } crypto_active_op_t; /* * Definition for flags in crypto_active_op_t */ #define CRYPTO_OPERATION_ACTIVE 1 /* Cryptoki operation is active */ #define CRYPTO_OPERATION_UPDATE 2 /* Cryptoki multi-part op active */ #define CRYPTO_KEY_DIGESTED 3 /* A C_DigestKey() was called */ typedef struct session { avl_node_t node; CK_SESSION_HANDLE handle; ulong_t magic_marker; /* magic # be validated for integrity */ pthread_mutex_t session_mutex; /* session's mutex lock */ pthread_cond_t ses_free_cond; /* cond variable for signal and wait */ uint32_t ses_refcnt; /* session reference count */ uint32_t ses_close_sync; /* session closing flags */ CK_STATE state; /* session state */ /* Place holder for parameters passed in the C_OpenSession */ CK_FLAGS flags; CK_NOTIFY Notify; CK_VOID_PTR pApplication; /* Pointers to form the global session list */ struct session *next; /* points to next session on the list */ struct session *prev; /* points to prev session on the list */ struct object *object_list; /* points to list of objects */ crypto_active_op_t digest; /* context of active digest operation */ crypto_active_op_t encrypt; /* context of active encrypt op */ crypto_active_op_t decrypt; /* context of active decrypt op */ crypto_active_op_t sign; /* context of active sign op */ crypto_active_op_t verify; /* context of active verify op */ /* context of active FindObjects op */ crypto_active_op_t find_objects; } soft_session_t; /* * slot_t is a global structure to be used only by the * token objects to hold the token object related * in-core information. */ typedef struct slot { uint_t ks_version; /* in-core keystore version number */ boolean_t authenticated; /* Has C_Login called */ boolean_t userpin_change_needed; /* set if PIN expired */ pthread_mutex_t slot_mutex; pthread_mutex_t keystore_mutex; /* Protects keystore_load_status */ uint_t keystore_load_status; /* Keystore load status */ /* points to in-core token object list */ struct object *token_object_list; } slot_t; /* * The following structure is used to link the to-be-freed sessions * into a linked list. The sessions on this linked list have * not yet been freed via free() after C_CloseSession() call; instead * they are added to this list. The actual free will take place when * the number of sessions queued reaches MAX_SES_TO_BE_FREED, at which * time the first session in the list will be freed. */ #define MAX_SES_TO_BE_FREED 300 typedef struct ses_to_be_freed_list { struct session *first; /* points to the first session in the list */ struct session *last; /* points to the last session in the list */ uint32_t count; /* current total sessions in the list */ pthread_mutex_t ses_to_be_free_mutex; } ses_to_be_freed_list_t; /* * Flag definitions for ses_close_sync */ #define SESSION_IS_CLOSING 1 /* Session is in a closing state */ #define SESSION_REFCNT_WAITING 2 /* Waiting for session reference */ /* count to become zero */ /* * This macro is used to decrement the session reference count by one. * * The caller of this macro uses the argument lock_held to indicate that * whether the caller holds the lock on the session or not. * * SES_REFRELE macro does the following: * 1) Get the session lock if the caller does not hold it. * 2) Decrement the session reference count by one. * 3) If the session reference count becomes zero after being decremented, * and there is a closing session thread in the wait state, then * call pthread_cond_signal() to wake up that thread who is blocked * in the session deletion routine due to non-zero reference ount. * 4) Always release the session lock. */ #define SES_REFRELE(s, lock_held) { \ if (!lock_held) \ (void) pthread_mutex_lock(&s->session_mutex); \ if ((--((s)->ses_refcnt) == 0) && \ (s->ses_close_sync & SESSION_REFCNT_WAITING)) { \ (void) pthread_mutex_unlock(&s->session_mutex); \ (void) pthread_cond_signal(&s->ses_free_cond); \ } else { \ (void) pthread_mutex_unlock(&s->session_mutex); \ } \ } extern pthread_mutex_t soft_sessionlist_mutex; extern soft_session_t *soft_session_list; extern avl_tree_t soft_session_tree; extern int all_sessions_closing; extern CK_ULONG soft_session_cnt; /* the number of opened sessions */ extern CK_ULONG soft_session_rw_cnt; /* the number of opened R/W sessions */ /* * Function Prototypes. */ CK_RV handle2session(CK_SESSION_HANDLE hSession, soft_session_t **session_p); CK_RV soft_delete_all_sessions(boolean_t force); void soft_delete_all_objects_in_session(soft_session_t *sp, boolean_t force); CK_RV soft_add_session(CK_FLAGS flags, CK_VOID_PTR pApplication, CK_NOTIFY notify, CK_ULONG *phSession); CK_RV soft_delete_session(soft_session_t *sp, boolean_t force, boolean_t lock_held); CK_RV soft_get_operationstate(soft_session_t *, CK_BYTE_PTR, CK_ULONG_PTR); CK_RV soft_set_operationstate(soft_session_t *, CK_BYTE_PTR, CK_ULONG, CK_OBJECT_HANDLE, CK_OBJECT_HANDLE); /* Token object related function prototypes. */ CK_RV soft_login(CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen); void soft_logout(void); void soft_acquire_all_session_mutexes(soft_session_t *session_p); void soft_release_all_session_mutexes(soft_session_t *session_p); #ifdef __cplusplus } #endif #endif /* _SOFTSESSION_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2020 Joyent, Inc. */ #include #include #include #include #include #include #include #include #include "softGlobal.h" #include "softSession.h" #include "softObject.h" #include "softOps.h" #include "softKeystore.h" #include "softKeystoreUtil.h" CK_ULONG soft_session_cnt = 0; /* the number of opened sessions */ CK_ULONG soft_session_rw_cnt = 0; /* the number of opened R/W sessions */ #define DIGEST_MECH_OK(_m_) ((_m_) == CKM_MD5 || (_m_) == CKM_SHA_1) /* * Delete all the sessions. First, obtain the global session * list lock. Then start to delete one session at a time. * Release the global session list lock before returning to * caller. */ CK_RV soft_delete_all_sessions(boolean_t force) { CK_RV rv = CKR_OK; CK_RV rv1; soft_session_t *session_p; soft_session_t *session_p1; /* Acquire the global session list lock */ (void) pthread_mutex_lock(&soft_sessionlist_mutex); session_p = soft_session_list; /* Delete all the sessions in the session list */ while (session_p) { session_p1 = session_p->next; /* * Delete a session by calling soft_delete_session() * with a session pointer and a boolean arguments. * Boolean value TRUE is used to indicate that the * caller holds the lock on the global session list. * */ rv1 = soft_delete_session(session_p, force, B_TRUE); /* Record the very first error code */ if (rv == CKR_OK) { rv = rv1; } session_p = session_p1; } /* No session left */ soft_session_list = NULL; /* Release the global session list lock */ (void) pthread_mutex_unlock(&soft_sessionlist_mutex); return (rv); } /* * Create a new session struct, and add it to the session linked list. * * This function will acquire the global session list lock, and release * it after adding the session to the session linked list. */ CK_RV soft_add_session(CK_FLAGS flags, CK_VOID_PTR pApplication, CK_NOTIFY notify, CK_ULONG *sessionhandle_p) { soft_session_t *new_sp = NULL; /* Allocate a new session struct */ new_sp = calloc(1, sizeof (soft_session_t)); if (new_sp == NULL) { return (CKR_HOST_MEMORY); } new_sp->magic_marker = SOFTTOKEN_SESSION_MAGIC; new_sp->pApplication = pApplication; new_sp->Notify = notify; new_sp->flags = flags; new_sp->state = CKS_RO_PUBLIC_SESSION; new_sp->object_list = NULL; new_sp->ses_refcnt = 0; new_sp->ses_close_sync = 0; (void) pthread_mutex_lock(&soft_giant_mutex); if (soft_slot.authenticated) { (void) pthread_mutex_unlock(&soft_giant_mutex); if (flags & CKF_RW_SESSION) { new_sp->state = CKS_RW_USER_FUNCTIONS; } else { new_sp->state = CKS_RO_USER_FUNCTIONS; } } else { (void) pthread_mutex_unlock(&soft_giant_mutex); if (flags & CKF_RW_SESSION) { new_sp->state = CKS_RW_PUBLIC_SESSION; } else { new_sp->state = CKS_RO_PUBLIC_SESSION; } } /* Initialize the lock for the newly created session */ if (pthread_mutex_init(&new_sp->session_mutex, NULL) != 0) { free(new_sp); return (CKR_CANT_LOCK); } (void) pthread_cond_init(&new_sp->ses_free_cond, NULL); /* Acquire the global session list lock */ (void) pthread_mutex_lock(&soft_sessionlist_mutex); /* Generate a unique session handle. */ do { arc4random_buf(&new_sp->handle, sizeof (new_sp->handle)); if (new_sp->handle == CK_INVALID_HANDLE) continue; } while (avl_find(&soft_session_tree, new_sp, NULL) != NULL); avl_add(&soft_session_tree, new_sp); *sessionhandle_p = new_sp->handle; /* Insert the new session in front of session list */ if (soft_session_list == NULL) { soft_session_list = new_sp; new_sp->next = NULL; new_sp->prev = NULL; } else { soft_session_list->prev = new_sp; new_sp->next = soft_session_list; new_sp->prev = NULL; soft_session_list = new_sp; } ++soft_session_cnt; if (flags & CKF_RW_SESSION) ++soft_session_rw_cnt; if (soft_session_cnt == 1) /* * This is the first session to be opened, so we can set * validate the public token objects in token list now. */ soft_validate_token_objects(B_TRUE); /* Release the global session list lock */ (void) pthread_mutex_unlock(&soft_sessionlist_mutex); return (CKR_OK); } /* * This function adds the to-be-freed session to a linked list. * When the number of sessions queued in the linked list reaches the * maximum threshold MAX_SES_TO_BE_FREED, it will free the first * session (FIFO) in the list. */ void session_delay_free(soft_session_t *sp) { soft_session_t *tmp; (void) pthread_mutex_lock(&ses_delay_freed.ses_to_be_free_mutex); /* Add the newly deleted session at the end of the list */ sp->next = NULL; if (ses_delay_freed.first == NULL) { ses_delay_freed.last = sp; ses_delay_freed.first = sp; } else { ses_delay_freed.last->next = sp; ses_delay_freed.last = sp; } if (++ses_delay_freed.count >= MAX_SES_TO_BE_FREED) { /* * Free the first session in the list only if * the total count reaches maximum threshold. */ ses_delay_freed.count--; tmp = ses_delay_freed.first->next; free(ses_delay_freed.first); ses_delay_freed.first = tmp; } (void) pthread_mutex_unlock(&ses_delay_freed.ses_to_be_free_mutex); } /* * Delete a session: * - Remove the session from the session linked list. * Holding the lock on the global session list is needed to do this. * - Release all the objects created by the session. * * The boolean argument lock_held is used to indicate that whether * the caller of this function holds the lock on the global session * list or not. * - When called by soft_delete_all_sessions(), which is called by * C_Finalize() or C_CloseAllSessions() -- the lock_held = TRUE. * - When called by C_CloseSession() -- the lock_held = FALSE. * * When the caller does not hold the lock on the global session * list, this function will acquire that lock in order to proceed, * and also release that lock before returning to caller. */ CK_RV soft_delete_session(soft_session_t *session_p, boolean_t force, boolean_t lock_held) { /* * Check to see if the caller holds the lock on the global * session list. If not, we need to acquire that lock in * order to proceed. */ if (!lock_held) { /* Acquire the global session list lock */ (void) pthread_mutex_lock(&soft_sessionlist_mutex); } /* * Remove the session from the session linked list first. */ if (soft_session_list == session_p) { /* Session is the first one in the list */ if (session_p->next) { soft_session_list = session_p->next; session_p->next->prev = NULL; } else { /* Session is the only one in the list */ soft_session_list = NULL; } } else { /* Session is not the first one in the list */ if (session_p->next) { /* Session is in the middle of the list */ session_p->prev->next = session_p->next; session_p->next->prev = session_p->prev; } else { /* Session is the last one in the list */ session_p->prev->next = NULL; } } avl_remove(&soft_session_tree, session_p); --soft_session_cnt; if (session_p->flags & CKF_RW_SESSION) --soft_session_rw_cnt; if (!lock_held) { /* * If the global session list lock is obtained by * this function, then release that lock after * removing the session from session linked list. * We want the releasing of the objects of the * session, and freeing of the session itself to * be done without holding the global session list * lock. */ (void) pthread_mutex_unlock(&soft_sessionlist_mutex); } /* Acquire the individual session lock */ (void) pthread_mutex_lock(&session_p->session_mutex); /* * Make sure another thread hasn't freed the session. */ if (session_p->magic_marker != SOFTTOKEN_SESSION_MAGIC) { (void) pthread_mutex_unlock(&session_p->session_mutex); return (CKR_OK); } /* * The deletion of a session must be blocked when the session * reference count is not zero. This means if any session related * operation starts prior to the session close operation gets in, * the session closing thread must wait for the non-closing * operation to be completed before it can proceed the close * operation. * * Unless we are being forced to shut everything down, this only * happens if the libraries _fini() is running not of someone * explicitly called C_Finalize(). */ if (force) session_p->ses_refcnt = 0; while (session_p->ses_refcnt != 0) { /* * We set the SESSION_REFCNT_WAITING flag before we put * this closing thread in a wait state, so other non-closing * operation thread will signal to wake it up only when * the session reference count becomes zero and this flag * is set. */ session_p->ses_close_sync |= SESSION_REFCNT_WAITING; (void) pthread_cond_wait(&session_p->ses_free_cond, &session_p->session_mutex); } session_p->ses_close_sync &= ~SESSION_REFCNT_WAITING; /* * Remove all the objects created in this session. */ soft_delete_all_objects_in_session(session_p, force); /* * Mark session as no longer valid. This can only be done after all * objects created by this session are free'd since the marker is * still needed in the process of removing objects from the session. */ session_p->magic_marker = 0; (void) pthread_cond_destroy(&session_p->ses_free_cond); /* In case application did not call Final */ if (session_p->digest.context != NULL) free(session_p->digest.context); if (session_p->encrypt.context != NULL) /* * 1st B_TRUE: encrypt * 2nd B_TRUE: caller is holding session_mutex. */ soft_crypt_cleanup(session_p, B_TRUE, B_TRUE); if (session_p->decrypt.context != NULL) /* * 1st B_FALSE: decrypt * 2nd B_TRUE: caller is holding session_mutex. */ soft_crypt_cleanup(session_p, B_FALSE, B_TRUE); if (session_p->sign.context != NULL) free(session_p->sign.context); if (session_p->verify.context != NULL) free(session_p->verify.context); if (session_p->find_objects.context != NULL) { find_context_t *fcontext; fcontext = (find_context_t *)session_p->find_objects.context; free(fcontext->objs_found); free(fcontext); } /* Reset SESSION_IS_CLOSING flag. */ session_p->ses_close_sync &= ~SESSION_IS_CLOSING; (void) pthread_mutex_unlock(&session_p->session_mutex); /* Destroy the individual session lock */ (void) pthread_mutex_destroy(&session_p->session_mutex); /* Delay freeing the session */ session_delay_free(session_p); return (CKR_OK); } /* * This function is used to type cast a session handle to a pointer to * the session struct. Also, it does the following things: * 1) Check to see if the session struct is tagged with a session * magic number. This is to detect when an application passes * a bogus session pointer. * 2) Acquire the lock on the designated session. * 3) Check to see if the session is in the closing state that another * thread is performing. * 4) Increment the session reference count by one. This is to prevent * this session from being closed by other thread. * 5) Release the lock held on the designated session. */ CK_RV handle2session(CK_SESSION_HANDLE hSession, soft_session_t **session_p) { soft_session_t *sp; soft_session_t node; /* * No need to hold soft_sessionlist_mutex as we are * just reading the value and 32-bit reads are atomic. */ if (all_sessions_closing) { return (CKR_SESSION_CLOSED); } (void) memset(&node, 0, sizeof (node)); node.handle = hSession; (void) pthread_mutex_lock(&soft_sessionlist_mutex); sp = avl_find(&soft_session_tree, &node, NULL); if ((sp == NULL) || (sp->magic_marker != SOFTTOKEN_SESSION_MAGIC)) { (void) pthread_mutex_unlock(&soft_sessionlist_mutex); return (CKR_SESSION_HANDLE_INVALID); } (void) pthread_mutex_lock(&sp->session_mutex); (void) pthread_mutex_unlock(&soft_sessionlist_mutex); if (sp->ses_close_sync & SESSION_IS_CLOSING) { (void) pthread_mutex_unlock(&sp->session_mutex); return (CKR_SESSION_CLOSED); } /* Increment session ref count. */ sp->ses_refcnt++; (void) pthread_mutex_unlock(&sp->session_mutex); *session_p = sp; return (CKR_OK); } /* * The format to be saved in the pOperationState will be: * 1. internal_op_state_t * 2. crypto_active_op_t * 3. actual context of the active operation */ CK_RV soft_get_operationstate(soft_session_t *session_p, CK_BYTE_PTR pOperationState, CK_ULONG_PTR pulOperationStateLen) { internal_op_state_t *p_op_state; CK_ULONG op_data_len = 0; CK_RV rv = CKR_OK; if (pulOperationStateLen == NULL) return (CKR_ARGUMENTS_BAD); (void) pthread_mutex_lock(&session_p->session_mutex); /* Check to see if encrypt operation is active. */ if (session_p->encrypt.flags & CRYPTO_OPERATION_ACTIVE) { rv = CKR_STATE_UNSAVEABLE; goto unlock_session; } /* Check to see if decrypt operation is active. */ if (session_p->decrypt.flags & CRYPTO_OPERATION_ACTIVE) { rv = CKR_STATE_UNSAVEABLE; goto unlock_session; } /* Check to see if sign operation is active. */ if (session_p->sign.flags & CRYPTO_OPERATION_ACTIVE) { rv = CKR_STATE_UNSAVEABLE; goto unlock_session; } /* Check to see if verify operation is active. */ if (session_p->verify.flags & CRYPTO_OPERATION_ACTIVE) { rv = CKR_STATE_UNSAVEABLE; goto unlock_session; } /* Check to see if digest operation is active. */ if (session_p->digest.flags & CRYPTO_OPERATION_ACTIVE) { op_data_len = sizeof (internal_op_state_t) + sizeof (crypto_active_op_t); switch (session_p->digest.mech.mechanism) { case CKM_MD5: op_data_len += sizeof (MD5_CTX); break; case CKM_SHA_1: op_data_len += sizeof (SHA1_CTX); break; default: rv = CKR_STATE_UNSAVEABLE; goto unlock_session; } if (pOperationState == NULL_PTR) { *pulOperationStateLen = op_data_len; goto unlock_session; } else { if (*pulOperationStateLen < op_data_len) { *pulOperationStateLen = op_data_len; rv = CKR_BUFFER_TOO_SMALL; goto unlock_session; } } /* Save internal_op_state_t */ /* LINTED E_BAD_PTR_CAST_ALIGN */ p_op_state = (internal_op_state_t *)pOperationState; p_op_state->op_len = op_data_len; p_op_state->op_active = DIGEST_OP; p_op_state->op_session_state = session_p->state; /* Save crypto_active_op_t */ (void) memcpy((CK_BYTE *)pOperationState + sizeof (internal_op_state_t), &session_p->digest, sizeof (crypto_active_op_t)); switch (session_p->digest.mech.mechanism) { case CKM_MD5: /* Save MD5_CTX for the active digest operation */ (void) memcpy((CK_BYTE *)pOperationState + sizeof (internal_op_state_t) + sizeof (crypto_active_op_t), session_p->digest.context, sizeof (MD5_CTX)); break; case CKM_SHA_1: /* Save SHA1_CTX for the active digest operation */ (void) memcpy((CK_BYTE *)pOperationState + sizeof (internal_op_state_t) + sizeof (crypto_active_op_t), session_p->digest.context, sizeof (SHA1_CTX)); break; default: rv = CKR_STATE_UNSAVEABLE; } } else { rv = CKR_OPERATION_NOT_INITIALIZED; goto unlock_session; } *pulOperationStateLen = op_data_len; unlock_session: (void) pthread_mutex_unlock(&session_p->session_mutex); return (rv); } static CK_BYTE_PTR alloc_digest(CK_ULONG mech) { CK_BYTE_PTR ret_val; switch (mech) { case CKM_MD5: ret_val = (CK_BYTE_PTR) malloc(sizeof (MD5_CTX)); break; case CKM_SHA_1: ret_val = (CK_BYTE_PTR) malloc(sizeof (SHA1_CTX)); break; default: ret_val = NULL; } return (ret_val); } /* * The format to be restored from the pOperationState will be: * 1. internal_op_state_t * 2. crypto_active_op_t * 3. actual context of the saved operation */ CK_RV soft_set_operationstate(soft_session_t *session_p, CK_BYTE_PTR pOperationState, CK_ULONG ulOperationStateLen, CK_OBJECT_HANDLE hEncryptionKey, CK_OBJECT_HANDLE hAuthenticationKey) { CK_RV rv = CKR_OK; internal_op_state_t *p_op_state; crypto_active_op_t *p_active_op; CK_ULONG offset = 0; CK_ULONG mech; void *free_it = NULL; /* LINTED E_BAD_PTR_CAST_ALIGN */ p_op_state = (internal_op_state_t *)pOperationState; if (p_op_state->op_len != ulOperationStateLen) { /* * The supplied data length does not match with * the saved data length. */ return (CKR_SAVED_STATE_INVALID); } if (p_op_state->op_active != DIGEST_OP) return (CKR_SAVED_STATE_INVALID); if ((hAuthenticationKey != 0) || (hEncryptionKey != 0)) { return (CKR_KEY_NOT_NEEDED); } offset = sizeof (internal_op_state_t); /* LINTED E_BAD_PTR_CAST_ALIGN */ p_active_op = (crypto_active_op_t *)(pOperationState + offset); offset += sizeof (crypto_active_op_t); mech = p_active_op->mech.mechanism; if (!DIGEST_MECH_OK(mech)) { return (CKR_SAVED_STATE_INVALID); } /* * We may reuse digest.context in case the digest mechanisms (the one, * which belongs to session and the operation, which we are restoring) * are the same. If digest mechanisms are different, we have to release * the digest context, which belongs to session and allocate a new one. */ (void) pthread_mutex_lock(&session_p->session_mutex); if (session_p->state != p_op_state->op_session_state) { /* * The supplied session state does not match with * the saved session state. */ rv = CKR_SAVED_STATE_INVALID; goto unlock_session; } if (session_p->digest.context && (session_p->digest.mech.mechanism != mech)) { free_it = session_p->digest.context; session_p->digest.context = NULL; } if (session_p->digest.context == NULL) { session_p->digest.context = alloc_digest(mech); if (session_p->digest.context == NULL) { /* * put back original context into session in case * allocation of new context has failed. */ session_p->digest.context = free_it; free_it = NULL; rv = CKR_HOST_MEMORY; goto unlock_session; } } /* Restore crypto_active_op_t */ session_p->digest.mech.mechanism = mech; session_p->digest.flags = p_active_op->flags; switch (mech) { case CKM_MD5: /* Restore MD5_CTX from the saved digest operation */ (void) memcpy((CK_BYTE *)session_p->digest.context, (CK_BYTE *)pOperationState + offset, sizeof (MD5_CTX)); break; case CKM_SHA_1: /* Restore SHA1_CTX from the saved digest operation */ (void) memcpy((CK_BYTE *)session_p->digest.context, (CK_BYTE *)pOperationState + offset, sizeof (SHA1_CTX)); break; default: /* never reached */ rv = CKR_SAVED_STATE_INVALID; } unlock_session: (void) pthread_mutex_unlock(&session_p->session_mutex); if (free_it != NULL) free(free_it); return (rv); } CK_RV soft_login(CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen) { /* * Authenticate the input PIN. */ return (soft_verify_pin(pPin, ulPinLen)); } void soft_logout(void) { /* * Delete all the private token objects from the "token_object_list". */ soft_delete_all_in_core_token_objects(PRIVATE_TOKEN); return; } void soft_acquire_all_session_mutexes(soft_session_t *session_p) { /* Iterate through sessions acquiring all mutexes */ while (session_p) { soft_object_t *object_p; (void) pthread_mutex_lock(&session_p->session_mutex); object_p = session_p->object_list; /* Lock also all objects related to session */ while (object_p) { (void) pthread_mutex_lock(&object_p->object_mutex); object_p = object_p->next; } session_p = session_p->next; } } void soft_release_all_session_mutexes(soft_session_t *session_p) { /* Iterate through sessions releasing all mutexes */ while (session_p) { /* * N.B. Ideally, should go in opposite order to guarantee * lock-order requirements but there is no tail pointer. */ soft_object_t *object_p = session_p->object_list; /* Unlock also all objects related to session */ while (object_p) { (void) pthread_mutex_unlock(&object_p->object_mutex); object_p = object_p->next; } (void) pthread_mutex_unlock(&session_p->session_mutex); session_p = session_p->next; } } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include "softGlobal.h" #include "softObject.h" #include "softOps.h" #include "softSession.h" CK_RV C_SignInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) { CK_RV rv; soft_session_t *session_p; soft_object_t *key_p; boolean_t lock_held = B_FALSE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if (pMechanism == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } /* Obtain the object pointer. */ HANDLE2OBJECT(hKey, key_p, rv); if (rv != CKR_OK) { goto clean_exit; } /* Check to see if key object supports signature. */ if (!(key_p->bool_attr_mask & SIGN_BOOL_ON)) { rv = CKR_KEY_FUNCTION_NOT_PERMITTED; goto clean_exit1; } (void) pthread_mutex_lock(&session_p->session_mutex); lock_held = B_TRUE; /* Check to see if sign operation is already active. */ if (session_p->sign.flags & CRYPTO_OPERATION_ACTIVE) { /* free the memory to avoid memory leak */ soft_sign_verify_cleanup(session_p, B_TRUE, B_TRUE); } /* * This active flag will remain ON until application calls either * C_Sign or C_SignFinal to actually obtain the signature. */ session_p->sign.flags = CRYPTO_OPERATION_ACTIVE; (void) pthread_mutex_unlock(&session_p->session_mutex); lock_held = B_FALSE; rv = soft_sign_init(session_p, pMechanism, key_p); if (rv != CKR_OK) { (void) pthread_mutex_lock(&session_p->session_mutex); session_p->sign.flags &= ~CRYPTO_OPERATION_ACTIVE; lock_held = B_TRUE; } clean_exit1: OBJ_REFRELE(key_p); clean_exit: SES_REFRELE(session_p, lock_held); return (rv); } CK_RV C_Sign(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) { CK_RV rv; soft_session_t *session_p; boolean_t lock_held = B_FALSE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obatin the session pointer */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if (pulSignatureLen == NULL || ((*pulSignatureLen != 0) && (pSignature == NULL))) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } (void) pthread_mutex_lock(&session_p->session_mutex); lock_held = B_TRUE; /* Application must call C_SignInit before calling C_Sign. */ if (!(session_p->sign.flags & CRYPTO_OPERATION_ACTIVE)) { SES_REFRELE(session_p, lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } /* * C_Sign must be called without intervening C_SignUpdate * calls. */ if (session_p->sign.flags & CRYPTO_OPERATION_UPDATE) { /* * C_Sign can not be used to terminate a multi-part * operation, so we'll leave the active sign operation * flag on and let the application continue with the * sign update operation. */ SES_REFRELE(session_p, lock_held); return (CKR_FUNCTION_FAILED); } (void) pthread_mutex_unlock(&session_p->session_mutex); lock_held = B_FALSE; rv = soft_sign(session_p, pData, ulDataLen, pSignature, pulSignatureLen); if ((rv == CKR_BUFFER_TOO_SMALL) || (pSignature == NULL && rv == CKR_OK)) { /* * We will not terminate the active sign operation flag, * when the application-supplied buffer is too small, or * the application asks for the length of buffer to hold * the signature. */ SES_REFRELE(session_p, lock_held); return (rv); } clean_exit: /* Clear contexts, free key, and release session counter */ soft_sign_verify_cleanup(session_p, B_TRUE, B_FALSE); return (rv); } CK_RV C_SignUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) { CK_RV rv; soft_session_t *session_p; boolean_t lock_held = B_FALSE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if (ulPartLen == 0) { SES_REFRELE(session_p, lock_held); return (CKR_OK); } if (pPart == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } (void) pthread_mutex_lock(&session_p->session_mutex); lock_held = B_TRUE; /* * Application must call C_SignInit before calling * C_SignUpdate. */ if (!(session_p->sign.flags & CRYPTO_OPERATION_ACTIVE)) { SES_REFRELE(session_p, lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } session_p->sign.flags |= CRYPTO_OPERATION_UPDATE; (void) pthread_mutex_unlock(&session_p->session_mutex); lock_held = B_FALSE; rv = soft_sign_update(session_p, pPart, ulPartLen); if (rv == CKR_OK) { SES_REFRELE(session_p, lock_held); return (rv); } clean_exit: /* After error, clear context, free key, & release session counter */ soft_sign_verify_cleanup(session_p, B_TRUE, B_FALSE); return (rv); } CK_RV C_SignFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) { CK_RV rv; soft_session_t *session_p; boolean_t lock_held = B_FALSE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if (pulSignatureLen == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } (void) pthread_mutex_lock(&session_p->session_mutex); lock_held = B_TRUE; /* * Application must call C_SignInit before calling * C_SignFinal. */ if (!(session_p->sign.flags & CRYPTO_OPERATION_ACTIVE)) { SES_REFRELE(session_p, lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } (void) pthread_mutex_unlock(&session_p->session_mutex); lock_held = B_FALSE; rv = soft_sign_final(session_p, pSignature, pulSignatureLen); if ((rv == CKR_BUFFER_TOO_SMALL) || (pSignature == NULL && rv == CKR_OK)) { /* * We will not terminate the active sign operation flag, * when the application-supplied buffer is too small, or * the application asks for the length of buffer to hold * the signature. */ SES_REFRELE(session_p, lock_held); return (rv); } clean_exit: /* Clear contexts, free key, and release session counter */ soft_sign_verify_cleanup(session_p, B_TRUE, B_FALSE); return (rv); } CK_RV C_SignRecoverInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) { CK_RV rv; soft_session_t *session_p; soft_object_t *key_p; boolean_t lock_held = B_FALSE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if (pMechanism == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } /* Obtain the object pointer. */ HANDLE2OBJECT(hKey, key_p, rv); if (rv != CKR_OK) { goto clean_exit; } /* Check to see if key object supports sign_recover. */ if (!(key_p->bool_attr_mask & SIGN_RECOVER_BOOL_ON)) { rv = CKR_KEY_FUNCTION_NOT_PERMITTED; goto clean_exit1; } (void) pthread_mutex_lock(&session_p->session_mutex); lock_held = B_TRUE; /* Check to see if sign operation is already active. */ if (session_p->sign.flags & CRYPTO_OPERATION_ACTIVE) { /* free the memory to avoid memory leak */ soft_sign_verify_cleanup(session_p, B_TRUE, B_TRUE); } /* * This active flag will remain ON until application calls either * C_SignRecover to actually obtain the signature. */ session_p->sign.flags = CRYPTO_OPERATION_ACTIVE; (void) pthread_mutex_unlock(&session_p->session_mutex); lock_held = B_FALSE; rv = soft_sign_recover_init(session_p, pMechanism, key_p); if (rv != CKR_OK) { (void) pthread_mutex_lock(&session_p->session_mutex); session_p->sign.flags &= ~CRYPTO_OPERATION_ACTIVE; lock_held = B_TRUE; } clean_exit1: OBJ_REFRELE(key_p); clean_exit: SES_REFRELE(session_p, lock_held); return (rv); } CK_RV C_SignRecover(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) { CK_RV rv; soft_session_t *session_p; boolean_t lock_held = B_FALSE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obatin the session pointer */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if ((pData == NULL) || (pulSignatureLen == NULL)) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } (void) pthread_mutex_lock(&session_p->session_mutex); lock_held = B_TRUE; /* Application must call C_SignRecoverInit before C_SignRecover. */ if (!(session_p->sign.flags & CRYPTO_OPERATION_ACTIVE)) { SES_REFRELE(session_p, lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } (void) pthread_mutex_unlock(&session_p->session_mutex); lock_held = B_FALSE; rv = soft_sign_recover(session_p, pData, ulDataLen, pSignature, pulSignatureLen); if ((rv == CKR_BUFFER_TOO_SMALL) || (pSignature == NULL && rv == CKR_OK)) { /* * We will not terminate the active sign operation flag, * when the application-supplied buffer is too small, or * the application asks for the length of buffer to hold * the signature. */ SES_REFRELE(session_p, lock_held); return (rv); } clean_exit: /* Clear contexts, free key, and release session counter */ soft_sign_verify_cleanup(session_p, B_TRUE, B_FALSE); return (rv); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2014 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2018, Joyent, Inc. */ #include #include #include #include #include "softObject.h" #include "softOps.h" #include "softSession.h" #include "softMAC.h" #include "softRSA.h" #include "softDSA.h" #include "softEC.h" #include "softCrypt.h" /* * soft_sign_init() * * Arguments: * session_p: pointer to soft_session_t struct * pMechanism: pointer to CK_MECHANISM struct provided by application * key_p: pointer to key soft_object_t struct * * Description: * called by C_SignInit(). This function calls the corresponding * sign init routine based on the mechanism. * */ CK_RV soft_sign_init(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism, soft_object_t *key_p) { switch (pMechanism->mechanism) { case CKM_SSL3_MD5_MAC: case CKM_SSL3_SHA1_MAC: case CKM_MD5_HMAC_GENERAL: case CKM_MD5_HMAC: case CKM_SHA_1_HMAC_GENERAL: case CKM_SHA_1_HMAC: case CKM_SHA256_HMAC_GENERAL: case CKM_SHA256_HMAC: case CKM_SHA384_HMAC_GENERAL: case CKM_SHA384_HMAC: case CKM_SHA512_HMAC_GENERAL: case CKM_SHA512_HMAC: return (soft_hmac_sign_verify_init_common(session_p, pMechanism, key_p, B_TRUE)); case CKM_RSA_X_509: case CKM_RSA_PKCS: case CKM_MD5_RSA_PKCS: case CKM_SHA1_RSA_PKCS: case CKM_SHA256_RSA_PKCS: case CKM_SHA384_RSA_PKCS: case CKM_SHA512_RSA_PKCS: return (soft_rsa_sign_verify_init_common(session_p, pMechanism, key_p, B_TRUE)); case CKM_DSA: case CKM_DSA_SHA1: return (soft_dsa_sign_verify_init_common(session_p, pMechanism, key_p, B_TRUE)); case CKM_ECDSA: case CKM_ECDSA_SHA1: return (soft_ecc_sign_verify_init_common(session_p, pMechanism, key_p, B_TRUE)); case CKM_DES_MAC_GENERAL: case CKM_DES_MAC: return (soft_des_sign_verify_init_common(session_p, pMechanism, key_p, B_TRUE)); case CKM_AES_CMAC_GENERAL: case CKM_AES_CMAC: case CKM_AES_GMAC: return (soft_aes_sign_verify_init_common(session_p, pMechanism, key_p, B_TRUE)); default: return (CKR_MECHANISM_INVALID); } } /* * soft_sign() * * Arguments: * session_p: pointer to soft_session_t struct * pData: pointer to the input data to be signed * ulDataLen: length of the input data * pSignature: pointer to the signature after signing * pulSignatureLen: pointer to the length of the signature * * Description: * called by C_Sign(). This function calls the corresponding * sign routine based on the mechanism. * */ CK_RV soft_sign(soft_session_t *session_p, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) { CK_MECHANISM_TYPE mechanism = session_p->sign.mech.mechanism; CK_RV rv = CKR_OK; switch (mechanism) { case CKM_SSL3_MD5_MAC: case CKM_SSL3_SHA1_MAC: case CKM_MD5_HMAC_GENERAL: case CKM_MD5_HMAC: case CKM_SHA_1_HMAC_GENERAL: case CKM_SHA_1_HMAC: case CKM_SHA256_HMAC_GENERAL: case CKM_SHA256_HMAC: case CKM_SHA384_HMAC_GENERAL: case CKM_SHA384_HMAC: case CKM_SHA512_HMAC_GENERAL: case CKM_SHA512_HMAC: { CK_BYTE hmac[SHA512_DIGEST_LENGTH]; /* use the maximum size */ if (pSignature != NULL) { /* Pass local buffer to avoid overflow. */ rv = soft_hmac_sign_verify_common(session_p, pData, ulDataLen, hmac, pulSignatureLen, B_TRUE); } else { /* Pass original pSignature, let callee to handle it. */ rv = soft_hmac_sign_verify_common(session_p, pData, ulDataLen, pSignature, pulSignatureLen, B_TRUE); } if ((rv == CKR_OK) && (pSignature != NULL)) (void) memcpy(pSignature, hmac, *pulSignatureLen); return (rv); } case CKM_DES_MAC_GENERAL: case CKM_DES_MAC: { CK_BYTE signature[DES_BLOCK_LEN]; /* use the maximum size */ if (pSignature != NULL) { /* Pass local buffer to avoid overflow. */ rv = soft_des_sign_verify_common(session_p, pData, ulDataLen, signature, pulSignatureLen, B_TRUE, B_FALSE); } else { /* Pass NULL, let callee to handle it. */ rv = soft_des_sign_verify_common(session_p, pData, ulDataLen, NULL, pulSignatureLen, B_TRUE, B_FALSE); } if ((rv == CKR_OK) && (pSignature != NULL)) (void) memcpy(pSignature, signature, *pulSignatureLen); return (rv); } case CKM_AES_CMAC_GENERAL: case CKM_AES_CMAC: case CKM_AES_GMAC: { CK_BYTE signature[AES_BLOCK_LEN]; if (pSignature != NULL) { /* Pass local buffer to avoid overflow. */ rv = soft_aes_sign_verify_common(session_p, pData, ulDataLen, signature, pulSignatureLen, B_TRUE, B_FALSE); } else { /* Pass NULL, let callee handle it. */ rv = soft_aes_sign_verify_common(session_p, pData, ulDataLen, NULL, pulSignatureLen, B_TRUE, B_FALSE); } if ((rv == CKR_OK) && (pSignature != NULL)) (void) memcpy(pSignature, signature, *pulSignatureLen); return (rv); } case CKM_RSA_X_509: case CKM_RSA_PKCS: return (soft_rsa_sign_common(session_p, pData, ulDataLen, pSignature, pulSignatureLen, mechanism)); case CKM_MD5_RSA_PKCS: case CKM_SHA1_RSA_PKCS: case CKM_SHA256_RSA_PKCS: case CKM_SHA384_RSA_PKCS: case CKM_SHA512_RSA_PKCS: return (soft_rsa_digest_sign_common(session_p, pData, ulDataLen, pSignature, pulSignatureLen, mechanism, B_FALSE)); case CKM_DSA: return (soft_dsa_sign(session_p, pData, ulDataLen, pSignature, pulSignatureLen)); case CKM_DSA_SHA1: return (soft_dsa_digest_sign_common(session_p, pData, ulDataLen, pSignature, pulSignatureLen, B_FALSE)); case CKM_ECDSA: return (soft_ecc_sign(session_p, pData, ulDataLen, pSignature, pulSignatureLen)); case CKM_ECDSA_SHA1: return (soft_ecc_digest_sign_common(session_p, pData, ulDataLen, pSignature, pulSignatureLen, B_FALSE)); default: return (CKR_MECHANISM_INVALID); } } /* * soft_sign_update() * * Arguments: * session_p: pointer to soft_session_t struct * pPart: pointer to the input data to be signed * ulPartLen: length of the input data * * Description: * called by C_SignUpdate(). This function calls the corresponding * sign update routine based on the mechanism. * */ CK_RV soft_sign_update(soft_session_t *session_p, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) { CK_MECHANISM_TYPE mechanism = session_p->sign.mech.mechanism; switch (mechanism) { case CKM_SSL3_MD5_MAC: case CKM_SSL3_SHA1_MAC: case CKM_MD5_HMAC_GENERAL: case CKM_MD5_HMAC: case CKM_SHA_1_HMAC_GENERAL: case CKM_SHA_1_HMAC: case CKM_SHA256_HMAC_GENERAL: case CKM_SHA256_HMAC: case CKM_SHA384_HMAC_GENERAL: case CKM_SHA384_HMAC: case CKM_SHA512_HMAC_GENERAL: case CKM_SHA512_HMAC: return (soft_hmac_sign_verify_update(session_p, pPart, ulPartLen, B_TRUE)); case CKM_DES_MAC_GENERAL: case CKM_DES_MAC: return (soft_des_mac_sign_verify_update(session_p, pPart, ulPartLen)); case CKM_AES_CMAC_GENERAL: case CKM_AES_CMAC: case CKM_AES_GMAC: return (soft_aes_mac_sign_verify_update(session_p, pPart, ulPartLen)); case CKM_MD5_RSA_PKCS: case CKM_SHA1_RSA_PKCS: case CKM_SHA256_RSA_PKCS: case CKM_SHA384_RSA_PKCS: case CKM_SHA512_RSA_PKCS: /* * The MD5/SHA1 digest value is accumulated in the context * of the multiple-part digesting operation. In the final * operation, the digest is encoded and then perform RSA * signing. */ case CKM_DSA_SHA1: case CKM_ECDSA_SHA1: return (soft_digest_update(session_p, pPart, ulPartLen)); default: /* PKCS11: The mechanism only supports single-part operation. */ return (CKR_MECHANISM_INVALID); } } /* * soft_sign_final() * * Arguments: * session_p: pointer to soft_session_t struct * pSignature: pointer to the signature after signing * pulSignatureLen: pointer to the length of the signature * * Description: * called by C_SignFinal(). This function calls the corresponding * sign final routine based on the mechanism. * */ CK_RV soft_sign_final(soft_session_t *session_p, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) { CK_MECHANISM_TYPE mechanism = session_p->sign.mech.mechanism; CK_RV rv = CKR_OK; switch (mechanism) { case CKM_SSL3_MD5_MAC: case CKM_SSL3_SHA1_MAC: case CKM_MD5_HMAC_GENERAL: case CKM_MD5_HMAC: case CKM_SHA_1_HMAC_GENERAL: case CKM_SHA_1_HMAC: case CKM_SHA256_HMAC_GENERAL: case CKM_SHA256_HMAC: case CKM_SHA384_HMAC_GENERAL: case CKM_SHA384_HMAC: case CKM_SHA512_HMAC_GENERAL: case CKM_SHA512_HMAC: { CK_BYTE hmac[SHA512_DIGEST_LENGTH]; /* use the maximum size */ if (pSignature != NULL) { /* Pass local buffer to avoid overflow */ rv = soft_hmac_sign_verify_common(session_p, NULL, 0, hmac, pulSignatureLen, B_TRUE); } else { /* Pass original pSignature, let callee to handle it. */ rv = soft_hmac_sign_verify_common(session_p, NULL, 0, pSignature, pulSignatureLen, B_TRUE); } if ((rv == CKR_OK) && (pSignature != NULL)) (void) memcpy(pSignature, hmac, *pulSignatureLen); return (rv); } case CKM_DES_MAC_GENERAL: case CKM_DES_MAC: { CK_BYTE signature[DES_BLOCK_LEN]; /* use the maximum size */ if (pSignature != NULL) { /* Pass local buffer to avoid overflow. */ rv = soft_des_sign_verify_common(session_p, NULL, 0, signature, pulSignatureLen, B_TRUE, B_TRUE); } else { /* Pass NULL, let callee to handle it. */ rv = soft_des_sign_verify_common(session_p, NULL, 0, NULL, pulSignatureLen, B_TRUE, B_TRUE); } if ((rv == CKR_OK) && (pSignature != NULL)) (void) memcpy(pSignature, signature, *pulSignatureLen); return (rv); } case CKM_AES_CMAC_GENERAL: case CKM_AES_CMAC: case CKM_AES_GMAC: { CK_BYTE signature[AES_BLOCK_LEN]; /* use the maximum size */ if (pSignature != NULL) { /* Pass local buffer to avoid overflow. */ rv = soft_aes_sign_verify_common(session_p, NULL, 0, signature, pulSignatureLen, B_TRUE, B_TRUE); } else { /* Pass NULL, let callee handle it. */ rv = soft_aes_sign_verify_common(session_p, NULL, 0, NULL, pulSignatureLen, B_TRUE, B_TRUE); } if ((rv == CKR_OK) && (pSignature != NULL)) (void) memcpy(pSignature, signature, *pulSignatureLen); return (rv); } case CKM_MD5_RSA_PKCS: case CKM_SHA1_RSA_PKCS: case CKM_SHA256_RSA_PKCS: case CKM_SHA384_RSA_PKCS: case CKM_SHA512_RSA_PKCS: return (soft_rsa_digest_sign_common(session_p, NULL, 0, pSignature, pulSignatureLen, mechanism, B_TRUE)); case CKM_DSA_SHA1: return (soft_dsa_digest_sign_common(session_p, NULL, 0, pSignature, pulSignatureLen, B_TRUE)); case CKM_ECDSA_SHA1: return (soft_ecc_digest_sign_common(session_p, NULL, 0, pSignature, pulSignatureLen, B_TRUE)); default: /* PKCS11: The mechanism only supports single-part operation. */ return (CKR_MECHANISM_INVALID); } } CK_RV soft_sign_recover_init(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism, soft_object_t *key_p) { switch (pMechanism->mechanism) { case CKM_RSA_X_509: case CKM_RSA_PKCS: return (soft_rsa_sign_verify_init_common(session_p, pMechanism, key_p, B_TRUE)); default: return (CKR_MECHANISM_INVALID); } } CK_RV soft_sign_recover(soft_session_t *session_p, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) { CK_MECHANISM_TYPE mechanism = session_p->sign.mech.mechanism; switch (mechanism) { case CKM_RSA_X_509: case CKM_RSA_PKCS: return (soft_rsa_sign_common(session_p, pData, ulDataLen, pSignature, pulSignatureLen, mechanism)); default: return (CKR_MECHANISM_INVALID); } } /* * This function frees the allocated active crypto context. * It is only called by the first tier of sign/verify routines * and the caller of this function may or may not hold the session mutex. */ void soft_sign_verify_cleanup(soft_session_t *session_p, boolean_t sign, boolean_t lock_held) { crypto_active_op_t *active_op; boolean_t lock_true = B_TRUE; if (!lock_held) (void) pthread_mutex_lock(&session_p->session_mutex); active_op = (sign) ? &(session_p->sign) : &(session_p->verify); switch (active_op->mech.mechanism) { case CKM_MD5_RSA_PKCS: case CKM_SHA1_RSA_PKCS: case CKM_SHA256_RSA_PKCS: case CKM_SHA384_RSA_PKCS: case CKM_SHA512_RSA_PKCS: if (session_p->digest.context != NULL) { free(session_p->digest.context); session_p->digest.context = NULL; session_p->digest.flags = 0; } /* FALLTHRU */ case CKM_RSA_PKCS: case CKM_RSA_X_509: { soft_rsa_ctx_t *rsa_ctx = (soft_rsa_ctx_t *)active_op->context; if (rsa_ctx != NULL && rsa_ctx->key != NULL) { soft_cleanup_object(rsa_ctx->key); free(rsa_ctx->key); } break; } case CKM_DSA_SHA1: if (session_p->digest.context != NULL) { free(session_p->digest.context); session_p->digest.context = NULL; session_p->digest.flags = 0; } /* FALLTHRU */ case CKM_DSA: { soft_dsa_ctx_t *dsa_ctx = (soft_dsa_ctx_t *)active_op->context; if (dsa_ctx != NULL && dsa_ctx->key != NULL) { soft_cleanup_object(dsa_ctx->key); free(dsa_ctx->key); } break; } case CKM_SSL3_MD5_MAC: case CKM_SSL3_SHA1_MAC: case CKM_MD5_HMAC_GENERAL: case CKM_MD5_HMAC: case CKM_SHA_1_HMAC_GENERAL: case CKM_SHA_1_HMAC: case CKM_SHA256_HMAC_GENERAL: case CKM_SHA256_HMAC: case CKM_SHA384_HMAC_GENERAL: case CKM_SHA384_HMAC: case CKM_SHA512_HMAC_GENERAL: case CKM_SHA512_HMAC: if (active_op->context != NULL) { explicit_bzero(active_op->context, sizeof (soft_hmac_ctx_t)); } break; case CKM_DES_MAC_GENERAL: case CKM_DES_MAC: if (session_p->encrypt.context != NULL) { free(session_p->encrypt.context); session_p->encrypt.context = NULL; session_p->encrypt.flags = 0; } if (active_op->context != NULL) { explicit_bzero(active_op->context, sizeof (soft_des_ctx_t)); } break; case CKM_AES_CMAC_GENERAL: case CKM_AES_CMAC: case CKM_AES_GMAC: if (session_p->encrypt.context != NULL) { soft_aes_free_ctx(session_p->encrypt.context); session_p->encrypt.context = NULL; session_p->encrypt.flags = 0; } if (active_op->context != NULL) { explicit_bzero(active_op->context, sizeof (soft_aes_sign_ctx_t)); } break; } if (active_op->context != NULL) { free(active_op->context); active_op->context = NULL; } active_op->flags = 0; if (!lock_held) SES_REFRELE(session_p, lock_true); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2020 Joyent, Inc. */ #include #include #include #include #include #include "softGlobal.h" #include "softSession.h" #include #include #include #include #include "softDH.h" #include "softObject.h" #include "softKeystore.h" #include "softKeystoreUtil.h" static CK_MECHANISM_TYPE soft_mechanisms[] = { CKM_DES_CBC, CKM_DES_CBC_PAD, CKM_DES_ECB, CKM_DES_KEY_GEN, CKM_DES_MAC_GENERAL, CKM_DES_MAC, CKM_DES3_CBC, CKM_DES3_CBC_PAD, CKM_DES3_ECB, CKM_DES2_KEY_GEN, CKM_DES3_KEY_GEN, CKM_AES_CBC, CKM_AES_CBC_PAD, CKM_AES_CTR, CKM_AES_CMAC_GENERAL, CKM_AES_CMAC, CKM_AES_ECB, CKM_AES_KEY_GEN, CKM_AES_GCM, CKM_AES_GMAC, CKM_AES_CCM, CKM_BLOWFISH_CBC, CKM_BLOWFISH_KEY_GEN, CKM_SHA_1, CKM_SHA_1_HMAC, CKM_SHA_1_HMAC_GENERAL, CKM_SHA256, CKM_SHA256_HMAC, CKM_SHA256_HMAC_GENERAL, CKM_SHA384, CKM_SHA384_HMAC, CKM_SHA384_HMAC_GENERAL, CKM_SHA512, CKM_SHA512_HMAC, CKM_SHA512_HMAC_GENERAL, CKM_SHA512_224, CKM_SHA512_256, CKM_SSL3_SHA1_MAC, CKM_MD5, CKM_MD5_HMAC, CKM_MD5_HMAC_GENERAL, CKM_SSL3_MD5_MAC, CKM_RC4, CKM_RC4_KEY_GEN, CKM_DSA, CKM_DSA_SHA1, CKM_DSA_KEY_PAIR_GEN, CKM_RSA_PKCS, CKM_RSA_PKCS_KEY_PAIR_GEN, CKM_RSA_X_509, CKM_MD5_RSA_PKCS, CKM_SHA1_RSA_PKCS, CKM_SHA256_RSA_PKCS, CKM_SHA384_RSA_PKCS, CKM_SHA512_RSA_PKCS, CKM_DH_PKCS_KEY_PAIR_GEN, CKM_DH_PKCS_DERIVE, CKM_MD5_KEY_DERIVATION, CKM_SHA1_KEY_DERIVATION, CKM_SHA256_KEY_DERIVATION, CKM_SHA384_KEY_DERIVATION, CKM_SHA512_KEY_DERIVATION, CKM_SHA512_224_KEY_DERIVATION, CKM_SHA512_256_KEY_DERIVATION, CKM_PBE_SHA1_RC4_128, CKM_PKCS5_PBKD2, CKM_SSL3_PRE_MASTER_KEY_GEN, CKM_TLS_PRE_MASTER_KEY_GEN, CKM_SSL3_MASTER_KEY_DERIVE, CKM_TLS_MASTER_KEY_DERIVE, CKM_SSL3_MASTER_KEY_DERIVE_DH, CKM_TLS_MASTER_KEY_DERIVE_DH, CKM_SSL3_KEY_AND_MAC_DERIVE, CKM_TLS_KEY_AND_MAC_DERIVE, CKM_TLS_PRF, CKM_EC_KEY_PAIR_GEN, CKM_ECDSA, CKM_ECDSA_SHA1, CKM_ECDH1_DERIVE }; /* * This is the table of CK_MECHANISM_INFO structs for the supported mechanisms. * The index for this table is the same as the one above for the same * mechanism. * The minimum and maximum sizes of the key for the mechanism can be measured * in bits or in bytes (i.e. mechanism-dependent). This table specifies the * supported range of key sizes in bytes; unless noted as in bits. */ static CK_MECHANISM_INFO soft_mechanism_info[] = { {DES_MINBYTES, DES_MAXBYTES, CKF_ENCRYPT|CKF_DECRYPT| CKF_WRAP|CKF_UNWRAP}, /* CKM_DES_CBC */ {DES_MINBYTES, DES_MAXBYTES, CKF_ENCRYPT|CKF_DECRYPT| CKF_WRAP|CKF_UNWRAP}, /* CKM_DES_CBC_PAD */ {DES_MINBYTES, DES_MAXBYTES, CKF_ENCRYPT|CKF_DECRYPT| CKF_WRAP|CKF_UNWRAP}, /* CKM_DES_ECB */ {DES_MINBYTES, DES_MAXBYTES, CKF_GENERATE}, /* CKM_DES_KEY_GEN */ {DES_MINBYTES, DES_MAXBYTES, CKF_SIGN|CKF_VERIFY}, /* CKM_DES_MAC_GENERAL */ {DES_MINBYTES, DES_MAXBYTES, CKF_SIGN|CKF_VERIFY}, /* CKM_DES_MAC */ {DES3_MINBYTES, DES3_MAXBYTES, CKF_ENCRYPT|CKF_DECRYPT| CKF_WRAP|CKF_UNWRAP}, /* CKM_DES3_CBC */ {DES3_MINBYTES, DES3_MAXBYTES, CKF_ENCRYPT|CKF_DECRYPT| CKF_WRAP|CKF_UNWRAP}, /* CKM_DES3_CBC_PAD */ {DES3_MINBYTES, DES3_MAXBYTES, CKF_ENCRYPT|CKF_DECRYPT| CKF_WRAP|CKF_UNWRAP}, /* CKM_DES3_ECB */ {DES2_MAXBYTES, DES2_MAXBYTES, CKF_GENERATE}, /* CKM_DES2_KEY_GEN */ {DES3_MAXBYTES, DES3_MAXBYTES, /* CKK_DES3 only */ CKF_GENERATE}, /* CKM_DES3_KEY_GEN */ {AES_MINBYTES, AES_MAXBYTES, CKF_ENCRYPT|CKF_DECRYPT| CKF_WRAP|CKF_UNWRAP}, /* CKM_AES_CBC */ {AES_MINBYTES, AES_MAXBYTES, CKF_ENCRYPT|CKF_DECRYPT| CKF_WRAP|CKF_UNWRAP}, /* CKM_AES_CBC_PAD */ {AES_MINBYTES, AES_MAXBYTES, CKF_ENCRYPT|CKF_DECRYPT| CKF_WRAP|CKF_UNWRAP}, /* CKM_AES_CTR */ {AES_MINBYTES, AES_MAXBYTES, CKF_SIGN|CKF_VERIFY}, /* CKM_AES_CMAC_GENERAL */ {AES_MINBYTES, AES_MAXBYTES, CKF_SIGN|CKF_VERIFY}, /* CKM_AES_CMAC */ {AES_MINBYTES, AES_MAXBYTES, CKF_ENCRYPT|CKF_DECRYPT| CKF_WRAP|CKF_UNWRAP}, /* CKM_AES_ECB */ {AES_MINBYTES, AES_MAXBYTES, CKF_GENERATE}, /* CKM_AES_KEY_GEN */ {AES_MINBYTES, AES_MAXBYTES, CKF_ENCRYPT|CKF_DECRYPT| CKF_WRAP|CKF_UNWRAP}, /* CKM_AES_GCM */ /* * CKM_AES_GMAC supports both MAC and encrypt/decrypt. * See: crypto-tests/tests/modes/aes/gmac/aes_gmac_enc.c */ {AES_MINBYTES, AES_MAXBYTES, CKF_ENCRYPT|CKF_DECRYPT| CKF_SIGN|CKF_VERIFY}, /* CKM_AES_GMAC */ {AES_MINBYTES, AES_MAXBYTES, CKF_ENCRYPT|CKF_DECRYPT| CKF_WRAP|CKF_UNWRAP}, /* CKM_AES_CCM */ {BLOWFISH_MINBYTES, BLOWFISH_MAXBYTES, CKF_ENCRYPT|CKF_DECRYPT| CKF_WRAP|CKF_UNWRAP}, /* CKM_BLOWFISH_ECB */ {BLOWFISH_MINBYTES, BLOWFISH_MAXBYTES, CKF_GENERATE}, /* CKM_BLOWFISH_KEY_GEN */ {0, 0, CKF_DIGEST}, /* CKM_SHA_1 */ {1, 64, CKF_SIGN|CKF_VERIFY}, /* CKM_SHA_1_HMAC */ {1, 64, CKF_SIGN|CKF_VERIFY}, /* CKM_SHA_1_HMAC_GENERAL */ {0, 0, CKF_DIGEST}, /* CKM_SHA256 */ {1, 64, CKF_SIGN|CKF_VERIFY}, /* CKM_SHA256_HMAC */ {1, 64, CKF_SIGN|CKF_VERIFY}, /* CKM_SHA256_HMAC_GENERAL */ {0, 0, CKF_DIGEST}, /* CKM_SHA384 */ {1, 128, CKF_SIGN|CKF_VERIFY}, /* CKM_SHA384_HMAC */ {1, 128, CKF_SIGN|CKF_VERIFY}, /* CKM_SHA384_HMAC_GENERAL */ {0, 0, CKF_DIGEST}, /* CKM_SHA512 */ {1, 128, CKF_SIGN|CKF_VERIFY}, /* CKM_SHA512_HMAC */ {1, 128, CKF_SIGN|CKF_VERIFY}, /* CKM_SHA512_HMAC_GENERAL */ {0, 0, CKF_DIGEST}, /* CKM_SHA512_224 */ {0, 0, CKF_DIGEST}, /* CKM_SHA512_256 */ {1, 512, CKF_SIGN|CKF_VERIFY}, /* CKM_SSL3_SHA1_MAC */ {0, 0, CKF_DIGEST}, /* CKM_MD5 */ {1, 64, CKF_SIGN|CKF_VERIFY}, /* CKM_MD5_HMAC */ {1, 64, CKF_SIGN|CKF_VERIFY}, /* CKM_MD5_HMAC_GENERAL */ {1, 512, CKF_SIGN|CKF_VERIFY}, /* CKM_SSL3_MD5_MAC */ {8, ARCFOUR_MAX_KEY_BITS, CKF_ENCRYPT|CKF_DECRYPT}, /* CKM_RC4; */ /* in bits */ {8, ARCFOUR_MAX_KEY_BITS, CKF_GENERATE }, /* CKM_RC4_KEY_GEN; in bits */ {512, 1024, CKF_SIGN|CKF_VERIFY}, /* CKM_DSA; in bits */ {512, 1024, CKF_SIGN|CKF_VERIFY}, /* CKM_DSA_SHA1; in bits */ {512, 1024, CKF_GENERATE_KEY_PAIR}, /* CKM_DSA_KEY_PAIR_GEN; */ /* in bits */ {256, 4096, CKF_ENCRYPT|CKF_DECRYPT| CKF_SIGN|CKF_SIGN_RECOVER| CKF_WRAP|CKF_UNWRAP| CKF_VERIFY|CKF_VERIFY_RECOVER}, /* CKM_RSA_PKCS; in bits */ {256, 4096, CKF_GENERATE_KEY_PAIR}, /* CKM_RSA_PKCS_KEY_PAIR_GEN; */ /* in bits */ {256, 4096, CKF_ENCRYPT|CKF_DECRYPT| CKF_SIGN|CKF_SIGN_RECOVER| CKF_WRAP|CKF_UNWRAP| CKF_VERIFY|CKF_VERIFY_RECOVER}, /* CKM_RSA_X_509 in bits */ {256, 4096, CKF_SIGN|CKF_VERIFY}, /* CKM_MD5_RSA_PKCS in bits */ {256, 4096, CKF_SIGN|CKF_VERIFY}, /* CKM_SHA1_RSA_PKCS in bits */ {256, 4096, CKF_SIGN|CKF_VERIFY}, /* CKM_SHA256_RSA_PKCS in bits */ {256, 4096, CKF_SIGN|CKF_VERIFY}, /* CKM_SHA384_RSA_PKCS in bits */ {256, 4096, CKF_SIGN|CKF_VERIFY}, /* CKM_SHA512_RSA_PKCS in bits */ {DH_MIN_KEY_LEN, DH_MAX_KEY_LEN, CKF_GENERATE_KEY_PAIR}, /* CKM_DH_PKCS_KEY_PAIR_GEN */ /* in bits */ {DH_MIN_KEY_LEN, DH_MAX_KEY_LEN, CKF_DERIVE}, /* CKM_DH_PKCS_DERIVE; */ /* in bits */ {1, 16, CKF_DERIVE}, /* CKM_MD5_KEY_DERIVATION */ {1, 20, CKF_DERIVE}, /* CKM_SHA1_KEY_DERIVATION */ {1, 32, CKF_DERIVE}, /* CKM_SHA256_KEY_DERIVATION */ {1, 48, CKF_DERIVE}, /* CKM_SHA384_KEY_DERIVATION */ {1, 64, CKF_DERIVE}, /* CKM_SHA512_KEY_DERIVATION */ {1, 28, CKF_DERIVE}, /* CKM_SHA512_224_KEY_DERIVATION */ {1, 32, CKF_DERIVE}, /* CKM_SHA512_256_KEY_DERIVATION */ {0, 0, CKF_GENERATE}, /* CKM_PBE_SHA1_RC4_128 */ {0, 0, CKF_GENERATE}, /* CKM_PKCS5_PBKD2 */ {48, 48, CKF_GENERATE}, /* CKM_SSL3_PRE_MASTER_KEY_GEN */ {48, 48, CKF_GENERATE}, /* CKM_TLS_PRE_MASTER_KEY_GEN */ {48, 48, CKF_DERIVE}, /* CKM_SSL3_MASTER_KEY_DERIVE */ {48, 48, CKF_DERIVE}, /* CKM_TLS_MASTER_KEY_DERIVE */ {48, 48, CKF_DERIVE}, /* CKM_SSL3_MASTER_KEY_DERIVE_DH */ {48, 48, CKF_DERIVE}, /* CKM_TLS_MASTER_KEY_DERIVE_DH */ {0, 0, CKF_DERIVE}, /* CKM_SSL3_KEY_AND_MAC_DERIVE */ {0, 0, CKF_DERIVE}, /* CKM_TLS_KEY_AND_MAC_DERIVE */ {0, 0, CKF_DERIVE}, /* CKM_TLS_PRF */ {EC_MIN_KEY_LEN, EC_MAX_KEY_LEN, CKF_GENERATE_KEY_PAIR}, {EC_MIN_KEY_LEN, EC_MAX_KEY_LEN, CKF_SIGN|CKF_VERIFY}, {EC_MIN_KEY_LEN, EC_MAX_KEY_LEN, CKF_SIGN|CKF_VERIFY}, {EC_MIN_KEY_LEN, EC_MAX_KEY_LEN, CKF_DERIVE} }; /* * Slot ID for softtoken is always 1. tokenPresent is ignored. * Also, only one slot is used. */ /*ARGSUSED*/ CK_RV C_GetSlotList(CK_BBOOL tokenPresent, CK_SLOT_ID_PTR pSlotList, CK_ULONG_PTR pulCount) { CK_RV rv; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); if (pulCount == NULL) { return (CKR_ARGUMENTS_BAD); } if (pSlotList == NULL) { /* * Application only wants to know the number of slots. */ *pulCount = 1; return (CKR_OK); } if ((*pulCount < 1) && (pSlotList != NULL)) { rv = CKR_BUFFER_TOO_SMALL; } else { pSlotList[0] = SOFTTOKEN_SLOTID; rv = CKR_OK; } *pulCount = 1; return (rv); } CK_RV C_GetSlotInfo(CK_SLOT_ID slotID, CK_SLOT_INFO_PTR pInfo) { if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); if (pInfo == NULL) return (CKR_ARGUMENTS_BAD); /* Make sure the slot ID is valid */ if (slotID != SOFTTOKEN_SLOTID) return (CKR_SLOT_ID_INVALID); /* Provide information about the slot in the provided buffer */ (void) strncpy((char *)pInfo->slotDescription, SOFT_SLOT_DESCRIPTION, 64); (void) strncpy((char *)pInfo->manufacturerID, SOFT_MANUFACTURER_ID, 32); pInfo->flags = CKF_TOKEN_PRESENT; pInfo->hardwareVersion.major = HARDWARE_VERSION_MAJOR; pInfo->hardwareVersion.minor = HARDWARE_VERSION_MINOR; pInfo->firmwareVersion.major = FIRMWARE_VERSION_MAJOR; pInfo->firmwareVersion.minor = FIRMWARE_VERSION_MINOR; return (CKR_OK); } CK_RV C_GetTokenInfo(CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo) { boolean_t pin_initialized = B_FALSE; char *ks_cryptpin = NULL; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Make sure the slot ID is valid */ if (slotID != SOFTTOKEN_SLOTID) return (CKR_SLOT_ID_INVALID); if (pInfo == NULL) return (CKR_ARGUMENTS_BAD); /* * It is intentional that we don't forward the error code * returned from soft_keystore_pin_initialized() to the caller */ pInfo->flags = SOFT_TOKEN_FLAGS; if (soft_slot.keystore_load_status == KEYSTORE_UNAVAILABLE) { pInfo->flags |= CKF_WRITE_PROTECTED; } else { if ((soft_keystore_pin_initialized(&pin_initialized, &ks_cryptpin, B_FALSE) == CKR_OK) && !pin_initialized) pInfo->flags |= CKF_USER_PIN_TO_BE_CHANGED; } if (ks_cryptpin != NULL) { size_t cplen = strlen(ks_cryptpin) + 1; freezero(ks_cryptpin, cplen); } /* Provide information about a token in the provided buffer */ (void) strncpy((char *)pInfo->label, SOFT_TOKEN_LABEL, 32); (void) strncpy((char *)pInfo->manufacturerID, SOFT_MANUFACTURER_ID, 32); (void) strncpy((char *)pInfo->model, TOKEN_MODEL, 16); (void) strncpy((char *)pInfo->serialNumber, SOFT_TOKEN_SERIAL, 16); pInfo->ulMaxSessionCount = CK_EFFECTIVELY_INFINITE; pInfo->ulSessionCount = soft_session_cnt; pInfo->ulMaxRwSessionCount = CK_EFFECTIVELY_INFINITE; pInfo->ulRwSessionCount = soft_session_rw_cnt; pInfo->ulMaxPinLen = MAX_PIN_LEN; pInfo->ulMinPinLen = MIN_PIN_LEN; pInfo->ulTotalPublicMemory = CK_UNAVAILABLE_INFORMATION; pInfo->ulFreePublicMemory = CK_UNAVAILABLE_INFORMATION; pInfo->ulTotalPrivateMemory = CK_UNAVAILABLE_INFORMATION; pInfo->ulFreePrivateMemory = CK_UNAVAILABLE_INFORMATION; pInfo->hardwareVersion.major = HARDWARE_VERSION_MAJOR; pInfo->hardwareVersion.minor = HARDWARE_VERSION_MINOR; pInfo->firmwareVersion.major = FIRMWARE_VERSION_MAJOR; pInfo->firmwareVersion.minor = FIRMWARE_VERSION_MINOR; (void) memset(pInfo->utcTime, ' ', 16); return (CKR_OK); } /*ARGSUSED*/ CK_RV C_WaitForSlotEvent(CK_FLAGS flags, CK_SLOT_ID_PTR pSlot, CK_VOID_PTR pReserved) { if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* * This is currently not implemented, however we could cause this * to wait for the token files to appear if soft_token_present is * false. * However there is currently no polite and portable way to do that * because we might not even be able to get to an fd to the * parent directory, so instead we don't support any slot events. */ return (CKR_FUNCTION_NOT_SUPPORTED); } CK_RV C_GetMechanismList(CK_SLOT_ID slotID, CK_MECHANISM_TYPE_PTR pMechanismList, CK_ULONG_PTR pulCount) { ulong_t i; ulong_t mechnum; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); if (slotID != SOFTTOKEN_SLOTID) return (CKR_SLOT_ID_INVALID); if (pulCount == NULL) return (CKR_ARGUMENTS_BAD); mechnum = sizeof (soft_mechanisms) / sizeof (CK_MECHANISM_TYPE); if (pMechanismList == NULL) { /* * Application only wants to know the number of * supported mechanism types. */ *pulCount = mechnum; return (CKR_OK); } if (*pulCount < mechnum) { *pulCount = mechnum; return (CKR_BUFFER_TOO_SMALL); } for (i = 0; i < mechnum; i++) { pMechanismList[i] = soft_mechanisms[i]; } *pulCount = mechnum; return (CKR_OK); } CK_RV C_GetMechanismInfo(CK_SLOT_ID slotID, CK_MECHANISM_TYPE type, CK_MECHANISM_INFO_PTR pInfo) { ulong_t i; ulong_t mechnum; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); if (slotID != SOFTTOKEN_SLOTID) return (CKR_SLOT_ID_INVALID); if (pInfo == NULL) { return (CKR_ARGUMENTS_BAD); } mechnum = sizeof (soft_mechanisms) / sizeof (CK_MECHANISM_TYPE); for (i = 0; i < mechnum; i++) { if (soft_mechanisms[i] == type) break; } if (i == mechnum) /* unsupported mechanism */ return (CKR_MECHANISM_INVALID); pInfo->ulMinKeySize = soft_mechanism_info[i].ulMinKeySize; pInfo->ulMaxKeySize = soft_mechanism_info[i].ulMaxKeySize; pInfo->flags = soft_mechanism_info[i].flags; return (CKR_OK); } /*ARGSUSED*/ CK_RV C_InitToken(CK_SLOT_ID slotID, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen, CK_UTF8CHAR_PTR pLabel) { if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); if (create_keystore() != 0) return (CKR_FUNCTION_FAILED); return (CKR_OK); } /*ARGSUSED*/ CK_RV C_InitPIN(CK_SESSION_HANDLE hSession, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen) { if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); return (CKR_FUNCTION_NOT_SUPPORTED); } CK_RV C_SetPIN(CK_SESSION_HANDLE hSession, CK_UTF8CHAR_PTR pOldPin, CK_ULONG ulOldPinLen, CK_UTF8CHAR_PTR pNewPin, CK_ULONG ulNewPinLen) { soft_session_t *session_p; CK_RV rv; boolean_t lock_held = B_FALSE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if (!soft_keystore_status(KEYSTORE_LOAD)) { SES_REFRELE(session_p, lock_held); return (CKR_DEVICE_REMOVED); } if ((ulOldPinLen < MIN_PIN_LEN) || (ulOldPinLen > MAX_PIN_LEN) || (ulNewPinLen < MIN_PIN_LEN) ||(ulNewPinLen > MAX_PIN_LEN)) { SES_REFRELE(session_p, lock_held); return (CKR_PIN_LEN_RANGE); } if ((pOldPin == NULL_PTR) || (pNewPin == NULL_PTR)) { /* * We don't support CKF_PROTECTED_AUTHENTICATION_PATH */ SES_REFRELE(session_p, lock_held); return (CKR_ARGUMENTS_BAD); } /* check the state of the session */ if ((session_p->state != CKS_RW_PUBLIC_SESSION) && (session_p->state != CKS_RW_USER_FUNCTIONS)) { SES_REFRELE(session_p, lock_held); return (CKR_SESSION_READ_ONLY); } rv = soft_setpin(pOldPin, ulOldPinLen, pNewPin, ulNewPinLen); SES_REFRELE(session_p, lock_held); return (rv); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include "softGlobal.h" #include "softObject.h" #include "softOps.h" #include "softSession.h" CK_RV C_VerifyInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) { CK_RV rv; soft_session_t *session_p; soft_object_t *key_p; boolean_t lock_held = B_FALSE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if (pMechanism == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } /* Obtain the object pointer. */ HANDLE2OBJECT(hKey, key_p, rv); if (rv != CKR_OK) { goto clean_exit; } /* Check to see if key object supports verification. */ if (!(key_p->bool_attr_mask & VERIFY_BOOL_ON)) { rv = CKR_KEY_FUNCTION_NOT_PERMITTED; goto clean_exit1; } (void) pthread_mutex_lock(&session_p->session_mutex); lock_held = B_TRUE; /* Check to see if verify operation is already active. */ if (session_p->verify.flags & CRYPTO_OPERATION_ACTIVE) { /* free the memory to avoid memory leak */ soft_sign_verify_cleanup(session_p, B_FALSE, B_TRUE); } /* * This active flag will remain ON until application calls either * C_Verify or C_VerifyFinal to verify a signature on data. */ session_p->verify.flags = CRYPTO_OPERATION_ACTIVE; (void) pthread_mutex_unlock(&session_p->session_mutex); lock_held = B_FALSE; rv = soft_verify_init(session_p, pMechanism, key_p); if (rv != CKR_OK) { (void) pthread_mutex_lock(&session_p->session_mutex); session_p->verify.flags &= ~CRYPTO_OPERATION_ACTIVE; lock_held = B_TRUE; } clean_exit1: OBJ_REFRELE(key_p); clean_exit: SES_REFRELE(session_p, lock_held); return (rv); } CK_RV C_Verify(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) { CK_RV rv; soft_session_t *session_p; boolean_t lock_held = B_FALSE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obatin the session pointer */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if (pData == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } (void) pthread_mutex_lock(&session_p->session_mutex); lock_held = B_TRUE; /* Application must call C_VerifyInit before calling C_Verify. */ if (!(session_p->verify.flags & CRYPTO_OPERATION_ACTIVE)) { SES_REFRELE(session_p, lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } /* * C_Verify must be called without intervening C_VerifyUpdate * calls. */ if (session_p->verify.flags & CRYPTO_OPERATION_UPDATE) { /* * C_Verify can not be used to terminate a multi-part * operation, so we'll leave the active verify operation * flag on and let the application continue with the * verify update operation. */ SES_REFRELE(session_p, lock_held); return (CKR_FUNCTION_FAILED); } (void) pthread_mutex_unlock(&session_p->session_mutex); lock_held = B_FALSE; rv = soft_verify(session_p, pData, ulDataLen, pSignature, ulSignatureLen); clean_exit: /* Clear context, free key, and release session counter */ soft_sign_verify_cleanup(session_p, B_FALSE, B_FALSE); return (rv); } CK_RV C_VerifyUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) { CK_RV rv; soft_session_t *session_p; boolean_t lock_held = B_FALSE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if (ulPartLen == 0) { SES_REFRELE(session_p, lock_held); return (CKR_OK); } if (pPart == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } (void) pthread_mutex_lock(&session_p->session_mutex); lock_held = B_TRUE; /* * Application must call C_VerifyInit before calling * C_VerifyUpdate. */ if (!(session_p->verify.flags & CRYPTO_OPERATION_ACTIVE)) { SES_REFRELE(session_p, lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } session_p->verify.flags |= CRYPTO_OPERATION_UPDATE; (void) pthread_mutex_unlock(&session_p->session_mutex); lock_held = B_FALSE; rv = soft_verify_update(session_p, pPart, ulPartLen); if (rv == CKR_OK) { SES_REFRELE(session_p, lock_held); return (rv); } clean_exit: /* After error, clear context, free key, & release session counter */ soft_sign_verify_cleanup(session_p, B_FALSE, B_FALSE); return (rv); } CK_RV C_VerifyFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) { CK_RV rv; soft_session_t *session_p; boolean_t lock_held = B_FALSE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); (void) pthread_mutex_lock(&session_p->session_mutex); lock_held = B_TRUE; /* * Application must call C_VerifyInit before calling * C_VerifyFinal. */ if (!(session_p->verify.flags & CRYPTO_OPERATION_ACTIVE)) { SES_REFRELE(session_p, lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } (void) pthread_mutex_unlock(&session_p->session_mutex); lock_held = B_FALSE; rv = soft_verify_final(session_p, pSignature, ulSignatureLen); /* Clear contexts, free key, and release session counter */ soft_sign_verify_cleanup(session_p, B_FALSE, B_FALSE); return (rv); } CK_RV C_VerifyRecoverInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) { CK_RV rv; soft_session_t *session_p; soft_object_t *key_p; boolean_t lock_held = B_FALSE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if (pMechanism == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } /* Obtain the object pointer. */ HANDLE2OBJECT(hKey, key_p, rv); if (rv != CKR_OK) { goto clean_exit; } /* Check to see if key object supports verify_recover. */ if (!(key_p->bool_attr_mask & VERIFY_RECOVER_BOOL_ON)) { rv = CKR_KEY_FUNCTION_NOT_PERMITTED; goto clean_exit1; } (void) pthread_mutex_lock(&session_p->session_mutex); lock_held = B_TRUE; /* Check to see if verify operation is already active. */ if (session_p->verify.flags & CRYPTO_OPERATION_ACTIVE) { /* free the memory to avoid memory leak */ soft_sign_verify_cleanup(session_p, B_FALSE, B_TRUE); } /* * This active flag will remain ON until application calls either * C_VerifyRecover to actually obtain the recovered message. */ session_p->verify.flags = CRYPTO_OPERATION_ACTIVE; (void) pthread_mutex_unlock(&session_p->session_mutex); lock_held = B_FALSE; rv = soft_verify_recover_init(session_p, pMechanism, key_p); if (rv != CKR_OK) { (void) pthread_mutex_lock(&session_p->session_mutex); session_p->verify.flags &= ~CRYPTO_OPERATION_ACTIVE; lock_held = B_TRUE; } clean_exit1: OBJ_REFRELE(key_p); clean_exit: SES_REFRELE(session_p, lock_held); return (rv); } CK_RV C_VerifyRecover(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) { CK_RV rv; soft_session_t *session_p; boolean_t lock_held = B_FALSE; if (!softtoken_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obatin the session pointer */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if ((pSignature == NULL) || (pulDataLen == NULL)) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } (void) pthread_mutex_lock(&session_p->session_mutex); lock_held = B_TRUE; if (!(session_p->verify.flags & CRYPTO_OPERATION_ACTIVE)) { SES_REFRELE(session_p, lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } (void) pthread_mutex_unlock(&session_p->session_mutex); lock_held = B_FALSE; rv = soft_verify_recover(session_p, pSignature, ulSignatureLen, pData, pulDataLen); if ((rv == CKR_BUFFER_TOO_SMALL) || (pData == NULL && rv == CKR_OK)) { /* * We will not terminate the active verify operation flag, * when the application-supplied buffer is too small, or * the application asks for the length of buffer to hold * the signature. */ SES_REFRELE(session_p, lock_held); return (rv); } clean_exit: /* Clear context, free key, and release session counter */ soft_sign_verify_cleanup(session_p, B_FALSE, B_FALSE); return (rv); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2014 Nexenta Systems, Inc. All rights reserved. * Copyright 2018, Joyent, Inc. */ #include #include #include #include #include #include "softObject.h" #include "softOps.h" #include "softSession.h" #include "softMAC.h" #include "softRSA.h" #include "softDSA.h" #include "softEC.h" #include "softCrypt.h" /* * soft_verify_init() * * Arguments: * session_p: pointer to soft_session_t struct * pMechanism: pointer to CK_MECHANISM struct provided by application * key_p: pointer to key soft_object_t struct * * Description: * called by C_VerifyInit(). This function calls the corresponding * verify init routine based on the mechanism. * */ CK_RV soft_verify_init(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism, soft_object_t *key_p) { switch (pMechanism->mechanism) { case CKM_SSL3_MD5_MAC: case CKM_SSL3_SHA1_MAC: case CKM_MD5_HMAC_GENERAL: case CKM_MD5_HMAC: case CKM_SHA_1_HMAC_GENERAL: case CKM_SHA_1_HMAC: case CKM_SHA256_HMAC_GENERAL: case CKM_SHA256_HMAC: case CKM_SHA384_HMAC_GENERAL: case CKM_SHA384_HMAC: case CKM_SHA512_HMAC_GENERAL: case CKM_SHA512_HMAC: return (soft_hmac_sign_verify_init_common(session_p, pMechanism, key_p, B_FALSE)); case CKM_RSA_X_509: case CKM_RSA_PKCS: case CKM_MD5_RSA_PKCS: case CKM_SHA1_RSA_PKCS: case CKM_SHA256_RSA_PKCS: case CKM_SHA384_RSA_PKCS: case CKM_SHA512_RSA_PKCS: return (soft_rsa_sign_verify_init_common(session_p, pMechanism, key_p, B_FALSE)); case CKM_DSA: case CKM_DSA_SHA1: return (soft_dsa_sign_verify_init_common(session_p, pMechanism, key_p, B_FALSE)); case CKM_ECDSA: case CKM_ECDSA_SHA1: return (soft_ecc_sign_verify_init_common(session_p, pMechanism, key_p, B_FALSE)); case CKM_DES_MAC_GENERAL: case CKM_DES_MAC: return (soft_des_sign_verify_init_common(session_p, pMechanism, key_p, B_FALSE)); case CKM_AES_CMAC_GENERAL: case CKM_AES_CMAC: case CKM_AES_GMAC: return (soft_aes_sign_verify_init_common(session_p, pMechanism, key_p, B_FALSE)); default: return (CKR_MECHANISM_INVALID); } } /* * soft_verify() * * Arguments: * session_p: pointer to soft_session_t struct * pData: pointer to the input data * ulDataLen: length of the input data * pSignature: pointer to the signature * ulSignatureLen: length of the signature * * Description: * called by C_Verify(). This function calls the corresponding * verify routine based on the mechanism. * */ CK_RV soft_verify(soft_session_t *session_p, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) { CK_MECHANISM_TYPE mechanism = session_p->verify.mech.mechanism; CK_RV rv = CKR_OK; switch (mechanism) { case CKM_SSL3_MD5_MAC: case CKM_SSL3_SHA1_MAC: case CKM_MD5_HMAC_GENERAL: case CKM_MD5_HMAC: case CKM_SHA_1_HMAC_GENERAL: case CKM_SHA_1_HMAC: case CKM_SHA256_HMAC_GENERAL: case CKM_SHA256_HMAC: case CKM_SHA384_HMAC_GENERAL: case CKM_SHA384_HMAC: case CKM_SHA512_HMAC_GENERAL: case CKM_SHA512_HMAC: { CK_ULONG len; CK_BYTE hmac[SHA512_DIGEST_LENGTH]; /* use the maximum size */ soft_hmac_ctx_t *hmac_ctx; hmac_ctx = (soft_hmac_ctx_t *)session_p->verify.context; len = hmac_ctx->hmac_len; rv = soft_hmac_sign_verify_common(session_p, pData, ulDataLen, hmac, &len, B_FALSE); if (rv == CKR_OK) { if (len != ulSignatureLen) { rv = CKR_SIGNATURE_LEN_RANGE; } if (memcmp(hmac, pSignature, len) != 0) { rv = CKR_SIGNATURE_INVALID; } } return (rv); } case CKM_DES_MAC_GENERAL: case CKM_DES_MAC: { CK_ULONG len; CK_BYTE signature[DES_BLOCK_LEN]; /* use the maximum size */ soft_des_ctx_t *des_ctx; des_ctx = (soft_des_ctx_t *)session_p->verify.context; len = des_ctx->mac_len; /* Pass local buffer to avoid overflow. */ rv = soft_des_sign_verify_common(session_p, pData, ulDataLen, signature, &len, B_FALSE, B_FALSE); if (rv == CKR_OK) { if (len != ulSignatureLen) { rv = CKR_SIGNATURE_LEN_RANGE; } if (memcmp(signature, pSignature, len) != 0) { rv = CKR_SIGNATURE_INVALID; } } return (rv); } case CKM_AES_CMAC_GENERAL: case CKM_AES_CMAC: case CKM_AES_GMAC: { CK_ULONG len; CK_BYTE signature[AES_BLOCK_LEN]; aes_ctx_t *aes_ctx; aes_ctx = (aes_ctx_t *)session_p->verify.context; len = aes_ctx->ac_mac_len; /* Pass local buffer to avoid overflow. */ rv = soft_aes_sign_verify_common(session_p, pData, ulDataLen, signature, &len, B_FALSE, B_FALSE); if (rv == CKR_OK) { if (len != ulSignatureLen) { rv = CKR_SIGNATURE_LEN_RANGE; } if (memcmp(signature, pSignature, len) != 0) { rv = CKR_SIGNATURE_INVALID; } } return (rv); } case CKM_RSA_X_509: case CKM_RSA_PKCS: return (soft_rsa_verify_common(session_p, pData, ulDataLen, pSignature, ulSignatureLen, mechanism)); case CKM_MD5_RSA_PKCS: case CKM_SHA1_RSA_PKCS: case CKM_SHA256_RSA_PKCS: case CKM_SHA384_RSA_PKCS: case CKM_SHA512_RSA_PKCS: return (soft_rsa_digest_verify_common(session_p, pData, ulDataLen, pSignature, ulSignatureLen, mechanism, B_FALSE)); case CKM_DSA: return (soft_dsa_verify(session_p, pData, ulDataLen, pSignature, ulSignatureLen)); case CKM_DSA_SHA1: return (soft_dsa_digest_verify_common(session_p, pData, ulDataLen, pSignature, ulSignatureLen, B_FALSE)); case CKM_ECDSA: return (soft_ecc_verify(session_p, pData, ulDataLen, pSignature, ulSignatureLen)); case CKM_ECDSA_SHA1: return (soft_ecc_digest_verify_common(session_p, pData, ulDataLen, pSignature, ulSignatureLen, B_FALSE)); default: return (CKR_MECHANISM_INVALID); } } /* * soft_verify_update() * * Arguments: * session_p: pointer to soft_session_t struct * pPart: pointer to the input data * ulPartLen: length of the input data * * Description: * called by C_VerifyUpdate(). This function calls the corresponding * verify update routine based on the mechanism. * */ CK_RV soft_verify_update(soft_session_t *session_p, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) { CK_MECHANISM_TYPE mechanism = session_p->verify.mech.mechanism; switch (mechanism) { case CKM_SSL3_MD5_MAC: case CKM_SSL3_SHA1_MAC: case CKM_MD5_HMAC_GENERAL: case CKM_MD5_HMAC: case CKM_SHA_1_HMAC_GENERAL: case CKM_SHA_1_HMAC: case CKM_SHA256_HMAC_GENERAL: case CKM_SHA256_HMAC: case CKM_SHA384_HMAC_GENERAL: case CKM_SHA384_HMAC: case CKM_SHA512_HMAC_GENERAL: case CKM_SHA512_HMAC: return (soft_hmac_sign_verify_update(session_p, pPart, ulPartLen, B_FALSE)); case CKM_DES_MAC_GENERAL: case CKM_DES_MAC: return (soft_des_mac_sign_verify_update(session_p, pPart, ulPartLen)); case CKM_AES_CMAC_GENERAL: case CKM_AES_CMAC: case CKM_AES_GMAC: return (soft_aes_mac_sign_verify_update(session_p, pPart, ulPartLen)); case CKM_MD5_RSA_PKCS: case CKM_SHA1_RSA_PKCS: case CKM_SHA256_RSA_PKCS: case CKM_SHA384_RSA_PKCS: case CKM_SHA512_RSA_PKCS: /* * The MD5/SHA1 digest value is accumulated in the context * of the multiple-part digesting operation. In the final * operation, the digest is encoded and then perform RSA * verification. */ case CKM_DSA_SHA1: case CKM_ECDSA_SHA1: return (soft_digest_update(session_p, pPart, ulPartLen)); default: /* PKCS11: The mechanism only supports single-part operation. */ return (CKR_MECHANISM_INVALID); } } /* * soft_verify_final() * * Arguments: * session_p: pointer to soft_session_t struct * pSignature: pointer to the signature * ulSignatureLen: length of the signature * * Description: * called by C_VerifyFinal(). This function calls the corresponding * verify final routine based on the mechanism. * */ CK_RV soft_verify_final(soft_session_t *session_p, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) { CK_MECHANISM_TYPE mechanism = session_p->verify.mech.mechanism; CK_RV rv = CKR_OK; switch (mechanism) { case CKM_SSL3_MD5_MAC: case CKM_SSL3_SHA1_MAC: case CKM_MD5_HMAC_GENERAL: case CKM_MD5_HMAC: case CKM_SHA_1_HMAC_GENERAL: case CKM_SHA_1_HMAC: case CKM_SHA256_HMAC_GENERAL: case CKM_SHA256_HMAC: case CKM_SHA384_HMAC_GENERAL: case CKM_SHA384_HMAC: case CKM_SHA512_HMAC_GENERAL: case CKM_SHA512_HMAC: { CK_ULONG len; CK_BYTE hmac[SHA512_DIGEST_LENGTH]; soft_hmac_ctx_t *hmac_ctx; hmac_ctx = (soft_hmac_ctx_t *)session_p->verify.context; len = hmac_ctx->hmac_len; rv = soft_hmac_sign_verify_common(session_p, NULL, 0, hmac, &len, B_FALSE); if (rv == CKR_OK) { if (len != ulSignatureLen) { rv = CKR_SIGNATURE_LEN_RANGE; } if (memcmp(hmac, pSignature, len) != 0) { rv = CKR_SIGNATURE_INVALID; } } return (rv); } case CKM_DES_MAC_GENERAL: case CKM_DES_MAC: { CK_ULONG len; CK_BYTE signature[DES_BLOCK_LEN]; /* use the maximum size */ soft_des_ctx_t *des_ctx; des_ctx = (soft_des_ctx_t *)session_p->verify.context; len = des_ctx->mac_len; /* Pass local buffer to avoid overflow. */ rv = soft_des_sign_verify_common(session_p, NULL, 0, signature, &len, B_FALSE, B_TRUE); if (rv == CKR_OK) { if (len != ulSignatureLen) { rv = CKR_SIGNATURE_LEN_RANGE; } if (memcmp(signature, pSignature, len) != 0) { rv = CKR_SIGNATURE_INVALID; } } return (rv); } case CKM_AES_CMAC_GENERAL: case CKM_AES_CMAC: case CKM_AES_GMAC: { CK_ULONG len; CK_BYTE signature[AES_BLOCK_LEN]; aes_ctx_t *aes_ctx; aes_ctx = (aes_ctx_t *)session_p->verify.context; len = aes_ctx->ac_mac_len; /* Pass local buffer to avoid overflow. */ rv = soft_aes_sign_verify_common(session_p, NULL, 0, signature, &len, B_FALSE, B_TRUE); if (rv == CKR_OK) { if (len != ulSignatureLen) { rv = CKR_SIGNATURE_LEN_RANGE; } if (memcmp(signature, pSignature, len) != 0) { rv = CKR_SIGNATURE_INVALID; } } return (rv); } case CKM_MD5_RSA_PKCS: case CKM_SHA1_RSA_PKCS: case CKM_SHA256_RSA_PKCS: case CKM_SHA384_RSA_PKCS: case CKM_SHA512_RSA_PKCS: return (soft_rsa_digest_verify_common(session_p, NULL, 0, pSignature, ulSignatureLen, mechanism, B_TRUE)); case CKM_DSA_SHA1: return (soft_dsa_digest_verify_common(session_p, NULL, 0, pSignature, ulSignatureLen, B_TRUE)); case CKM_ECDSA_SHA1: return (soft_ecc_digest_verify_common(session_p, NULL, 0, pSignature, ulSignatureLen, B_TRUE)); default: /* PKCS11: The mechanism only supports single-part operation. */ return (CKR_MECHANISM_INVALID); } } CK_RV soft_verify_recover_init(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism, soft_object_t *key_p) { switch (pMechanism->mechanism) { case CKM_RSA_X_509: case CKM_RSA_PKCS: return (soft_rsa_sign_verify_init_common(session_p, pMechanism, key_p, B_FALSE)); default: return (CKR_MECHANISM_INVALID); } } CK_RV soft_verify_recover(soft_session_t *session_p, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) { CK_MECHANISM_TYPE mechanism = session_p->verify.mech.mechanism; switch (mechanism) { case CKM_RSA_X_509: case CKM_RSA_PKCS: return (soft_rsa_verify_recover(session_p, pSignature, ulSignatureLen, pData, pulDataLen)); default: return (CKR_MECHANISM_INVALID); } }