/** * @brief Initialise a context for decrypting arbitrary data using the given key. * @note If *ctx is NULL, a apr_crypto_block_t will be created from a pool. If * *ctx is not NULL, *ctx must point at a previously created structure. * @param ctx The block context returned, see note. * @param blockSize The block size of the cipher. * @param iv Optional initialisation vector. If the buffer pointed to is NULL, * an IV will be created at random, in space allocated from the pool. * If the buffer is not NULL, the IV in the buffer will be used. * @param key The key structure. * @param p The pool to use. * @return Returns APR_ENOIV if an initialisation vector is required but not specified. * Returns APR_EINIT if the backend failed to initialise the context. Returns * APR_ENOTIMPL if not implemented. */ static apr_status_t crypto_block_decrypt_init(apr_crypto_block_t **ctx, apr_size_t *blockSize, const unsigned char *iv, const apr_crypto_key_t *key, apr_pool_t *p) { PRErrorCode perr; SECItem * secParam; apr_crypto_block_t *block = *ctx; if (!block) { *ctx = block = apr_pcalloc(p, sizeof(apr_crypto_block_t)); } if (!block) { return APR_ENOMEM; } block->f = key->f; block->pool = p; block->provider = key->provider; apr_pool_cleanup_register(p, block, crypto_block_cleanup_helper, apr_pool_cleanup_null); if (key->ivSize) { SECItem ivItem; if (iv == NULL) { return APR_ENOIV; /* Cannot initialise without an IV */ } ivItem.data = (unsigned char*) iv; ivItem.len = key->ivSize; secParam = PK11_ParamFromIV(key->cipherMech, &ivItem); } else { secParam = PK11_GenerateNewParam(key->cipherMech, key->symKey); } block->blockSize = PK11_GetBlockSize(key->cipherMech, secParam); block->ctx = PK11_CreateContextBySymKey(key->cipherMech, CKA_DECRYPT, key->symKey, secParam); /* did an error occur? */ perr = PORT_GetError(); if (perr || !block->ctx) { key->f->result->rc = perr; key->f->result->msg = PR_ErrorToName(perr); return APR_EINIT; } if (blockSize) { *blockSize = PK11_GetBlockSize(key->cipherMech, secParam); } return APR_SUCCESS; }
/* * NSS_CMSCipherContext_StartDecrypt - create a cipher context to do decryption * based on the given bulk encryption key and algorithm identifier (which * may include an iv). * * XXX Once both are working, it might be nice to combine this and the * function below (for starting up encryption) into one routine, and just * have two simple cover functions which call it. */ NSSCMSCipherContext * NSS_CMSCipherContext_StartDecrypt(PK11SymKey *key, SECAlgorithmID *algid) { NSSCMSCipherContext *cc; void *ciphercx; CK_MECHANISM_TYPE cryptoMechType; PK11SlotInfo *slot; SECOidTag algtag; SECItem *param = NULL; algtag = SECOID_GetAlgorithmTag(algid); /* set param and mechanism */ if (SEC_PKCS5IsAlgorithmPBEAlg(algid)) { SECItem *pwitem; pwitem = PK11_GetSymKeyUserData(key); if (!pwitem) return NULL; cryptoMechType = PK11_GetPBECryptoMechanism(algid, ¶m, pwitem); if (cryptoMechType == CKM_INVALID_MECHANISM) { SECITEM_FreeItem(param,PR_TRUE); return NULL; } } else { cryptoMechType = PK11_AlgtagToMechanism(algtag); if ((param = PK11_ParamFromAlgid(algid)) == NULL) return NULL; } cc = (NSSCMSCipherContext *)PORT_ZAlloc(sizeof(NSSCMSCipherContext)); if (cc == NULL) { SECITEM_FreeItem(param,PR_TRUE); return NULL; } /* figure out pad and block sizes */ cc->pad_size = PK11_GetBlockSize(cryptoMechType, param); slot = PK11_GetSlotFromKey(key); cc->block_size = PK11_IsHW(slot) ? BLOCK_SIZE : cc->pad_size; PK11_FreeSlot(slot); /* create PK11 cipher context */ ciphercx = PK11_CreateContextBySymKey(cryptoMechType, CKA_DECRYPT, key, param); SECITEM_FreeItem(param, PR_TRUE); if (ciphercx == NULL) { PORT_Free (cc); return NULL; } cc->cx = ciphercx; cc->doit = (nss_cms_cipher_function) PK11_CipherOp; cc->destroy = (nss_cms_cipher_destroy) PK11_DestroyContext; cc->encrypt = PR_FALSE; cc->pending_count = 0; return cc; }
/* * NSS_CMSCipherContext_StartEncrypt - create a cipher object to do encryption, * based on the given bulk encryption key and algorithm tag. Fill in the * algorithm identifier (which may include an iv) appropriately. * * XXX Once both are working, it might be nice to combine this and the * function above (for starting up decryption) into one routine, and just * have two simple cover functions which call it. */ NSSCMSCipherContext * NSS_CMSCipherContext_StartEncrypt(PLArenaPool *poolp, PK11SymKey *key, SECAlgorithmID *algid) { NSSCMSCipherContext *cc; void *ciphercx; SECStatus rv; CK_MECHANISM_TYPE cryptoMechType; PK11SlotInfo *slot; SECItem *param = NULL; PRBool needToEncodeAlgid = PR_FALSE; SECOidTag algtag = SECOID_GetAlgorithmTag(algid); /* set param and mechanism */ if (SEC_PKCS5IsAlgorithmPBEAlg(algid)) { SECItem *pwitem; pwitem = PK11_GetSymKeyUserData(key); if (!pwitem) return NULL; cryptoMechType = PK11_GetPBECryptoMechanism(algid, ¶m, pwitem); if (cryptoMechType == CKM_INVALID_MECHANISM) { SECITEM_FreeItem(param,PR_TRUE); return NULL; } } else { cryptoMechType = PK11_AlgtagToMechanism(algtag); if ((param = PK11_GenerateNewParam(cryptoMechType, key)) == NULL) return NULL; needToEncodeAlgid = PR_TRUE; } cc = (NSSCMSCipherContext *)PORT_ZAlloc(sizeof(NSSCMSCipherContext)); if (cc == NULL) { goto loser; } /* now find pad and block sizes for our mechanism */ cc->pad_size = PK11_GetBlockSize(cryptoMechType, param); slot = PK11_GetSlotFromKey(key); cc->block_size = PK11_IsHW(slot) ? BLOCK_SIZE : cc->pad_size; PK11_FreeSlot(slot); /* and here we go, creating a PK11 cipher context */ ciphercx = PK11_CreateContextBySymKey(cryptoMechType, CKA_ENCRYPT, key, param); if (ciphercx == NULL) { PORT_Free(cc); cc = NULL; goto loser; } /* * These are placed after the CreateContextBySymKey() because some * mechanisms have to generate their IVs from their card (i.e. FORTEZZA). * Don't move it from here. * XXX is that right? the purpose of this is to get the correct algid * containing the IVs etc. for encoding. this means we need to set this up * BEFORE encoding the algid in the contentInfo, right? */ if (needToEncodeAlgid) { rv = PK11_ParamToAlgid(algtag, param, poolp, algid); if(rv != SECSuccess) { PORT_Free(cc); cc = NULL; goto loser; } } cc->cx = ciphercx; cc->doit = (nss_cms_cipher_function)PK11_CipherOp; cc->destroy = (nss_cms_cipher_destroy)PK11_DestroyContext; cc->encrypt = PR_TRUE; cc->pending_count = 0; loser: SECITEM_FreeItem(param, PR_TRUE); return cc; }
/* * Create a cipher object to do decryption, based on the given bulk * encryption key and algorithm identifier (which may include an iv). * * XXX This interface, or one similar, would be really nice available * in general... I tried to keep the pkcs7-specific stuff (mostly * having to do with padding) out of here. * * XXX Once both are working, it might be nice to combine this and the * function below (for starting up encryption) into one routine, and just * have two simple cover functions which call it. */ sec_PKCS7CipherObject * sec_PKCS7CreateDecryptObject (PK11SymKey *key, SECAlgorithmID *algid) { sec_PKCS7CipherObject *result; SECOidTag algtag; void *ciphercx; CK_MECHANISM_TYPE cryptoMechType; PK11SlotInfo *slot; SECItem *param = NULL; result = (struct sec_pkcs7_cipher_object*) PORT_ZAlloc (sizeof(struct sec_pkcs7_cipher_object)); if (result == NULL) return NULL; ciphercx = NULL; algtag = SECOID_GetAlgorithmTag (algid); if (SEC_PKCS5IsAlgorithmPBEAlg(algid)) { SECItem *pwitem; pwitem = (SECItem *)PK11_GetSymKeyUserData(key); if (!pwitem) { PORT_Free(result); return NULL; } cryptoMechType = PK11_GetPBECryptoMechanism(algid, ¶m, pwitem); if (cryptoMechType == CKM_INVALID_MECHANISM) { PORT_Free(result); SECITEM_FreeItem(param,PR_TRUE); return NULL; } } else { cryptoMechType = PK11_AlgtagToMechanism(algtag); param = PK11_ParamFromAlgid(algid); if (param == NULL) { PORT_Free(result); return NULL; } } result->pad_size = PK11_GetBlockSize(cryptoMechType, param); slot = PK11_GetSlotFromKey(key); result->block_size = PK11_IsHW(slot) ? BLOCK_SIZE : result->pad_size; PK11_FreeSlot(slot); ciphercx = PK11_CreateContextBySymKey(cryptoMechType, CKA_DECRYPT, key, param); SECITEM_FreeItem(param,PR_TRUE); if (ciphercx == NULL) { PORT_Free (result); return NULL; } result->cx = ciphercx; result->doit = (sec_pkcs7_cipher_function) PK11_CipherOp; result->destroy = (sec_pkcs7_cipher_destroy) PK11_DestroyContext; result->encrypt = PR_FALSE; result->pending_count = 0; return result; }
/* * Create a cipher object to do encryption, based on the given bulk * encryption key and algorithm tag. Fill in the algorithm identifier * (which may include an iv) appropriately. * * XXX This interface, or one similar, would be really nice available * in general... I tried to keep the pkcs7-specific stuff (mostly * having to do with padding) out of here. * * XXX Once both are working, it might be nice to combine this and the * function above (for starting up decryption) into one routine, and just * have two simple cover functions which call it. */ sec_PKCS7CipherObject * sec_PKCS7CreateEncryptObject (PRArenaPool *poolp, PK11SymKey *key, SECOidTag algtag, SECAlgorithmID *algid) { sec_PKCS7CipherObject *result; void *ciphercx; SECStatus rv; CK_MECHANISM_TYPE cryptoMechType; PK11SlotInfo *slot; SECItem *param = NULL; PRBool needToEncodeAlgid = PR_FALSE; result = (struct sec_pkcs7_cipher_object*) PORT_ZAlloc (sizeof(struct sec_pkcs7_cipher_object)); if (result == NULL) return NULL; ciphercx = NULL; if (SEC_PKCS5IsAlgorithmPBEAlg(algid)) { SECItem *pwitem; pwitem = (SECItem *)PK11_GetSymKeyUserData(key); if (!pwitem) { PORT_Free(result); return NULL; } cryptoMechType = PK11_GetPBECryptoMechanism(algid, ¶m, pwitem); if (cryptoMechType == CKM_INVALID_MECHANISM) { PORT_Free(result); SECITEM_FreeItem(param,PR_TRUE); return NULL; } } else { cryptoMechType = PK11_AlgtagToMechanism(algtag); param = PK11_GenerateNewParam(cryptoMechType, key); if (param == NULL) { PORT_Free(result); return NULL; } needToEncodeAlgid = PR_TRUE; } result->pad_size = PK11_GetBlockSize(cryptoMechType,param); slot = PK11_GetSlotFromKey(key); result->block_size = PK11_IsHW(slot) ? BLOCK_SIZE : result->pad_size; PK11_FreeSlot(slot); ciphercx = PK11_CreateContextBySymKey(cryptoMechType, CKA_ENCRYPT, key, param); if (ciphercx == NULL) { PORT_Free (result); SECITEM_FreeItem(param,PR_TRUE); return NULL; } /* * These are placed after the CreateContextBySymKey() because some * mechanisms have to generate their IVs from their card (i.e. FORTEZZA). * Don't move it from here. */ if (needToEncodeAlgid) { rv = PK11_ParamToAlgid(algtag,param,poolp,algid); if(rv != SECSuccess) { PORT_Free (result); SECITEM_FreeItem(param,PR_TRUE); return NULL; } } SECITEM_FreeItem(param,PR_TRUE); result->cx = ciphercx; result->doit = (sec_pkcs7_cipher_function) PK11_CipherOp; result->destroy = (sec_pkcs7_cipher_destroy) PK11_DestroyContext; result->encrypt = PR_TRUE; result->pending_count = 0; return result; }
/* * CreatePk11PinStore */ int CreatePk11PinStore(Pk11PinStore **out, const char *tokenName, const char *pin) { int err = PIN_SUCCESS; Pk11PinStore *store; do { store = (Pk11PinStore*)malloc(sizeof(Pk11PinStore)); if (store == 0) { err = PIN_NOMEMORY; break; } /* Low-level init */ store->key = 0; store->params = 0; store->crypt = 0; /* Use the tokenName to find a PKCS11 slot */ store->slot = PK11_FindSlotByName((char *)tokenName); if (store->slot == 0) { err = PIN_NOSUCHTOKEN; break; } /* Check the password/PIN. This allows access to the token */ { SECStatus rv = PK11_CheckUserPassword(store->slot, (char *)pin); if (rv == SECSuccess) ; else if (rv == SECWouldBlock) { /* NSS returns a blocking error when the pin is wrong */ err = PIN_INCORRECTPW; break; } else { err = PIN_SYSTEMERROR; break; } } /* Find the mechanism that this token can do */ { const mech_item *tp; store->mech = 0; for(tp = table;tp < &table[MECH_TABLE_SIZE];tp++) { if (PK11_DoesMechanism(store->slot, tp->type)) { store->mech = (mech_item *)tp; break; } } /* Default to a mechanism (probably on the internal token */ if (store->mech == 0) { store->mech = &dflt_mech; } } /* Generate a key and parameters to do the encryption */ #if NSS_VMAJOR >= 3 && (NSS_VMINOR <= 9 || (NSS_VMINOR <= 10 && NSS_VPATCH == 0)) store->key = PK11_KeyGen(store->slot, store->mech->type, 0, 0, 0); #else store->key = PK11_TokenKeyGenWithFlags(store->slot, store->mech->type, NULL, 0, NULL, CKF_ENCRYPT|CKF_DECRYPT, PR_FALSE, NULL); #endif if (store->key == 0) { /* PR_SetError(xxx); */ err = PIN_SYSTEMERROR; break; } store->params = PK11_GenerateNewParam(store->mech->type, store->key); if (store->params == 0) { err = PIN_SYSTEMERROR; break; } /* Compute the size of the encrypted data including necessary padding */ { int blocksize = PK11_GetBlockSize(store->mech->type, 0); store->length = strlen(pin)+1; /* Compute padded size - 0 means stream cipher */ if (blocksize != 0) { store->length += blocksize - (store->length % blocksize); } store->crypt = (unsigned char *)malloc(store->length); if (!store->crypt) { err = PIN_NOMEMORY; break; } } /* Encrypt */ { unsigned char *plain; PK11Context *ctx; SECStatus rv; int outLen; plain = (unsigned char *)malloc(store->length); if (!plain) { err = PIN_NOMEMORY; break; } /* Pad with 0 bytes */ memset(plain, 0, store->length); strcpy((char *)plain, pin); ctx = PK11_CreateContextBySymKey(store->mech->type, CKA_ENCRYPT, store->key, store->params); if (!ctx) { err = PIN_SYSTEMERROR; break; } do { rv = PK11_CipherOp(ctx, store->crypt, &outLen, store->length, plain, store->length); if (rv) break; rv = PK11_Finalize(ctx); } while(0); PK11_DestroyContext(ctx, PR_TRUE); memset(plain, 0, store->length); free(plain); if (rv) err = PIN_SYSTEMERROR; } } while(0); if (err) { DestroyPk11PinStore(store); store = 0; } *out = store; return err; }
/* the content of an encrypted data content info is decrypted. * it is assumed that for encrypted data, that the data has already * been set and is in the "encContent" field of the content info. * * cinfo is the content info to decrypt * * key is the key with which to perform the decryption. if the * algorithm is a password based encryption algorithm, the * key is actually a password which will be processed per * PKCS #5. * * in the event of an error, SECFailure is returned. SECSuccess * indicates a success. */ SECStatus SEC_PKCS7DecryptContents(PRArenaPool *poolp, SEC_PKCS7ContentInfo *cinfo, SECItem *key, void *wincx) { SECAlgorithmID *algid = NULL; SECStatus rv = SECFailure; SECItem *result = NULL, *dest, *src; void *mark; PK11SymKey *eKey = NULL; PK11SlotInfo *slot = NULL; CK_MECHANISM_TYPE cryptoMechType; void *cx; SECItem *c_param = NULL; int bs; if((cinfo == NULL) || (key == NULL)) return SECFailure; if(SEC_PKCS7ContentType(cinfo) != SEC_OID_PKCS7_ENCRYPTED_DATA) return SECFailure; algid = SEC_PKCS7GetEncryptionAlgorithm(cinfo); if(algid == NULL) return SECFailure; if(poolp == NULL) poolp = cinfo->poolp; mark = PORT_ArenaMark(poolp); src = &cinfo->content.encryptedData->encContentInfo.encContent; dest = &cinfo->content.encryptedData->encContentInfo.plainContent; dest->data = (unsigned char*)PORT_ArenaZAlloc(poolp, (src->len + 64)); dest->len = (src->len + 64); if(dest->data == NULL) { rv = SECFailure; goto loser; } slot = PK11_GetInternalKeySlot(); if(slot == NULL) { rv = SECFailure; goto loser; } eKey = PK11_PBEKeyGen(slot, algid, key, PR_FALSE, wincx); if(eKey == NULL) { rv = SECFailure; goto loser; } cryptoMechType = PK11_GetPBECryptoMechanism(algid, &c_param, key); if (cryptoMechType == CKM_INVALID_MECHANISM) { rv = SECFailure; goto loser; } cx = PK11_CreateContextBySymKey(cryptoMechType, CKA_DECRYPT, eKey, c_param); if(cx == NULL) { rv = SECFailure; goto loser; } rv = PK11_CipherOp((PK11Context*)cx, dest->data, (int *)(&dest->len), (int)(src->len + 64), src->data, (int)src->len); PK11_DestroyContext((PK11Context *)cx, PR_TRUE); bs = PK11_GetBlockSize(cryptoMechType, c_param); if(bs) { /* check for proper badding in block algorithms. this assumes * RC2 cbc or a DES cbc variant. and the padding is thus defined */ if(((int)dest->data[dest->len-1] <= bs) && ((int)dest->data[dest->len-1] > 0)) { dest->len -= (int)dest->data[dest->len-1]; } else { rv = SECFailure; /* set an error ? */ } } loser: /* let success fall through */ if(result != NULL) SECITEM_ZfreeItem(result, PR_TRUE); if(rv == SECFailure) PORT_ArenaRelease(poolp, mark); else PORT_ArenaUnmark(poolp, mark); if(eKey != NULL) PK11_FreeSymKey(eKey); if(slot != NULL) PK11_FreeSlot(slot); if(c_param != NULL) SECITEM_ZfreeItem(c_param, PR_TRUE); return rv; }
/* the content of an encrypted data content info is encrypted. * it is assumed that for encrypted data, that the data has already * been set and is in the "plainContent" field of the content info. * * cinfo is the content info to encrypt * * key is the key with which to perform the encryption. if the * algorithm is a password based encryption algorithm, the * key is actually a password which will be processed per * PKCS #5. * * in the event of an error, SECFailure is returned. SECSuccess * indicates a success. */ SECStatus SEC_PKCS7EncryptContents(PRArenaPool *poolp, SEC_PKCS7ContentInfo *cinfo, SECItem *key, void *wincx) { SECAlgorithmID *algid = NULL; SECItem * result = NULL; SECItem * src; SECItem * dest; SECItem * blocked_data = NULL; void * mark; void * cx; PK11SymKey * eKey = NULL; PK11SlotInfo * slot = NULL; CK_MECHANISM_TYPE cryptoMechType; int bs; SECStatus rv = SECFailure; SECItem *c_param = NULL; if((cinfo == NULL) || (key == NULL)) return SECFailure; if(SEC_PKCS7ContentType(cinfo) != SEC_OID_PKCS7_ENCRYPTED_DATA) return SECFailure; algid = SEC_PKCS7GetEncryptionAlgorithm(cinfo); if(algid == NULL) return SECFailure; if(poolp == NULL) poolp = cinfo->poolp; mark = PORT_ArenaMark(poolp); src = &cinfo->content.encryptedData->encContentInfo.plainContent; dest = &cinfo->content.encryptedData->encContentInfo.encContent; dest->data = (unsigned char*)PORT_ArenaZAlloc(poolp, (src->len + 64)); dest->len = (src->len + 64); if(dest->data == NULL) { rv = SECFailure; goto loser; } slot = PK11_GetInternalKeySlot(); if(slot == NULL) { rv = SECFailure; goto loser; } eKey = PK11_PBEKeyGen(slot, algid, key, PR_FALSE, wincx); if(eKey == NULL) { rv = SECFailure; goto loser; } cryptoMechType = PK11_GetPBECryptoMechanism(algid, &c_param, key); if (cryptoMechType == CKM_INVALID_MECHANISM) { rv = SECFailure; goto loser; } /* block according to PKCS 8 */ bs = PK11_GetBlockSize(cryptoMechType, c_param); rv = SECSuccess; if(bs) { char pad_char; pad_char = (char)(bs - (src->len % bs)); if(src->len % bs) { rv = SECSuccess; blocked_data = PK11_BlockData(src, bs); if(blocked_data) { PORT_Memset((blocked_data->data + blocked_data->len - (int)pad_char), pad_char, (int)pad_char); } else { rv = SECFailure; goto loser; } } else { blocked_data = SECITEM_DupItem(src); if(blocked_data) { blocked_data->data = (unsigned char*)PORT_Realloc( blocked_data->data, blocked_data->len + bs); if(blocked_data->data) { blocked_data->len += bs; PORT_Memset((blocked_data->data + src->len), (char)bs, bs); } else { rv = SECFailure; goto loser; } } else { rv = SECFailure; goto loser; } } } else { blocked_data = SECITEM_DupItem(src); if(!blocked_data) { rv = SECFailure; goto loser; } } cx = PK11_CreateContextBySymKey(cryptoMechType, CKA_ENCRYPT, eKey, c_param); if(cx == NULL) { rv = SECFailure; goto loser; } rv = PK11_CipherOp((PK11Context*)cx, dest->data, (int *)(&dest->len), (int)(src->len + 64), blocked_data->data, (int)blocked_data->len); PK11_DestroyContext((PK11Context*)cx, PR_TRUE); loser: /* let success fall through */ if(blocked_data != NULL) SECITEM_ZfreeItem(blocked_data, PR_TRUE); if(result != NULL) SECITEM_ZfreeItem(result, PR_TRUE); if(rv == SECFailure) PORT_ArenaRelease(poolp, mark); else PORT_ArenaUnmark(poolp, mark); if(eKey != NULL) PK11_FreeSymKey(eKey); if(slot != NULL) PK11_FreeSlot(slot); if(c_param != NULL) SECITEM_ZfreeItem(c_param, PR_TRUE); return rv; }
/* * SecCmsCipherContextStartEncrypt - create a cipher object to do encryption, * based on the given bulk encryption key and algorithm tag. Fill in the algorithm * identifier (which may include an iv) appropriately. * * XXX Once both are working, it might be nice to combine this and the * function above (for starting up decryption) into one routine, and just * have two simple cover functions which call it. */ SecCmsCipherContext * SecCmsCipherContextStartEncrypt(PRArenaPool *poolp, SecSymmetricKeyRef key, SECAlgorithmID *algid) { return SecCmsCipherContextStart(poolp, key, algid, PR_TRUE); #if 0 SecCmsCipherContext *cc; void *ciphercx; CSSM_DATA *param; OSStatus rv; CK_MECHANISM_TYPE mechanism; PK11SlotInfo *slot; PRBool needToEncodeAlgid = PR_FALSE; SECOidTag algtag = SECOID_GetAlgorithmTag(algid); /* set param and mechanism */ if (SEC_PKCS5IsAlgorithmPBEAlg(algid)) { CK_MECHANISM pbeMech, cryptoMech; CSSM_DATA *pbeParams; SEC_PKCS5KeyAndPassword *keyPwd; PORT_Memset(&pbeMech, 0, sizeof(CK_MECHANISM)); PORT_Memset(&cryptoMech, 0, sizeof(CK_MECHANISM)); /* HACK ALERT! * in this case, key is not actually a SecSymmetricKeyRef, but a SEC_PKCS5KeyAndPassword * */ keyPwd = (SEC_PKCS5KeyAndPassword *)key; key = keyPwd->key; /* find correct PK11 mechanism and parameters to initialize pbeMech */ pbeMech.mechanism = PK11_AlgtagToMechanism(algtag); pbeParams = PK11_ParamFromAlgid(algid); if (!pbeParams) return NULL; pbeMech.pParameter = pbeParams->Data; pbeMech.ulParameterLen = pbeParams->Length; /* now map pbeMech to cryptoMech */ if (PK11_MapPBEMechanismToCryptoMechanism(&pbeMech, &cryptoMech, keyPwd->pwitem, PR_FALSE) != CKR_OK) { SECITEM_ZfreeItem(pbeParams, PR_TRUE); return NULL; } SECITEM_ZfreeItem(pbeParams, PR_TRUE); /* and use it to initialize param & mechanism */ if ((param = (CSSM_DATA *)PORT_ZAlloc(sizeof(CSSM_DATA))) == NULL) return NULL; param->Data = (unsigned char *)cryptoMech.pParameter; param->Length = cryptoMech.ulParameterLen; mechanism = cryptoMech.mechanism; } else { mechanism = PK11_AlgtagToMechanism(algtag); if ((param = PK11_GenerateNewParam(mechanism, key)) == NULL) return NULL; needToEncodeAlgid = PR_TRUE; } cc = (SecCmsCipherContext *)PORT_ZAlloc(sizeof(SecCmsCipherContext)); if (cc == NULL) return NULL; /* now find pad and block sizes for our mechanism */ cc->pad_size = PK11_GetBlockSize(mechanism,param); slot = PK11_GetSlotFromKey(key); cc->block_size = PK11_IsHW(slot) ? BLOCK_SIZE : cc->pad_size; PK11_FreeSlot(slot); /* and here we go, creating a PK11 cipher context */ ciphercx = PK11_CreateContextBySymKey(mechanism, CKA_ENCRYPT, key, param); if (ciphercx == NULL) { PORT_Free(cc); cc = NULL; goto loser; } /* * These are placed after the CreateContextBySymKey() because some * mechanisms have to generate their IVs from their card (i.e. FORTEZZA). * Don't move it from here. * XXX is that right? the purpose of this is to get the correct algid * containing the IVs etc. for encoding. this means we need to set this up * BEFORE encoding the algid in the contentInfo, right? */ if (needToEncodeAlgid) { rv = PK11_ParamToAlgid(algtag, param, poolp, algid); if(rv != SECSuccess) { PORT_Free(cc); cc = NULL; goto loser; } } cc->cx = ciphercx; cc->doit = (nss_cms_cipher_function)PK11_CipherOp; cc->destroy = (nss_cms_cipher_destroy)PK11_DestroyContext; cc->encrypt = PR_TRUE; cc->pending_count = 0; loser: SECITEM_FreeItem(param, PR_TRUE); return cc; #endif }
/* * SecCmsCipherContextStartDecrypt - create a cipher context to do decryption * based on the given bulk * encryption key and algorithm identifier (which may include an iv). * * XXX Once both are working, it might be nice to combine this and the * function below (for starting up encryption) into one routine, and just * have two simple cover functions which call it. */ SecCmsCipherContext * SecCmsCipherContextStartDecrypt(SecSymmetricKeyRef key, SECAlgorithmID *algid) { return SecCmsCipherContextStart(NULL, key, algid, PR_FALSE); #if 0 SecCmsCipherContext *cc; void *ciphercx; CK_MECHANISM_TYPE mechanism; CSSM_DATA *param; PK11SlotInfo *slot; SECOidTag algtag; algtag = SECOID_GetAlgorithmTag(algid); /* set param and mechanism */ if (SEC_PKCS5IsAlgorithmPBEAlg(algid)) { CK_MECHANISM pbeMech, cryptoMech; CSSM_DATA *pbeParams; SEC_PKCS5KeyAndPassword *keyPwd; PORT_Memset(&pbeMech, 0, sizeof(CK_MECHANISM)); PORT_Memset(&cryptoMech, 0, sizeof(CK_MECHANISM)); /* HACK ALERT! * in this case, key is not actually a SecSymmetricKeyRef, but a SEC_PKCS5KeyAndPassword * */ keyPwd = (SEC_PKCS5KeyAndPassword *)key; key = keyPwd->key; /* find correct PK11 mechanism and parameters to initialize pbeMech */ pbeMech.mechanism = PK11_AlgtagToMechanism(algtag); pbeParams = PK11_ParamFromAlgid(algid); if (!pbeParams) return NULL; pbeMech.pParameter = pbeParams->Data; pbeMech.ulParameterLen = pbeParams->Length; /* now map pbeMech to cryptoMech */ if (PK11_MapPBEMechanismToCryptoMechanism(&pbeMech, &cryptoMech, keyPwd->pwitem, PR_FALSE) != CKR_OK) { SECITEM_ZfreeItem(pbeParams, PR_TRUE); return NULL; } SECITEM_ZfreeItem(pbeParams, PR_TRUE); /* and use it to initialize param & mechanism */ if ((param = (CSSM_DATA *)PORT_ZAlloc(sizeof(CSSM_DATA))) == NULL) return NULL; param->Data = (unsigned char *)cryptoMech.pParameter; param->Length = cryptoMech.ulParameterLen; mechanism = cryptoMech.mechanism; } else { mechanism = PK11_AlgtagToMechanism(algtag); if ((param = PK11_ParamFromAlgid(algid)) == NULL) return NULL; } cc = (SecCmsCipherContext *)PORT_ZAlloc(sizeof(SecCmsCipherContext)); if (cc == NULL) { SECITEM_FreeItem(param,PR_TRUE); return NULL; } /* figure out pad and block sizes */ cc->pad_size = PK11_GetBlockSize(mechanism, param); slot = PK11_GetSlotFromKey(key); cc->block_size = PK11_IsHW(slot) ? BLOCK_SIZE : cc->pad_size; PK11_FreeSlot(slot); /* create PK11 cipher context */ ciphercx = PK11_CreateContextBySymKey(mechanism, CKA_DECRYPT, key, param); SECITEM_FreeItem(param, PR_TRUE); if (ciphercx == NULL) { PORT_Free (cc); return NULL; } cc->cx = ciphercx; cc->doit = (nss_cms_cipher_function) PK11_CipherOp; cc->destroy = (nss_cms_cipher_destroy) PK11_DestroyContext; cc->encrypt = PR_FALSE; cc->pending_count = 0; return cc; #endif }
/** * @brief Initialise a context for encrypting arbitrary data using the given key. * @note If *ctx is NULL, a apr_crypto_block_t will be created from a pool. If * *ctx is not NULL, *ctx must point at a previously created structure. * @param ctx The block context returned, see note. * @param iv Optional initialisation vector. If the buffer pointed to is NULL, * an IV will be created at random, in space allocated from the pool. * If the buffer pointed to is not NULL, the IV in the buffer will be * used. * @param key The key structure. * @param blockSize The block size of the cipher. * @param p The pool to use. * @return Returns APR_ENOIV if an initialisation vector is required but not specified. * Returns APR_EINIT if the backend failed to initialise the context. Returns * APR_ENOTIMPL if not implemented. */ static apr_status_t crypto_block_encrypt_init(apr_crypto_block_t **ctx, const unsigned char **iv, const apr_crypto_key_t *key, apr_size_t *blockSize, apr_pool_t *p) { PRErrorCode perr; SECItem * secParam; SECItem ivItem; unsigned char * usedIv; apr_crypto_block_t *block = *ctx; if (!block) { *ctx = block = apr_pcalloc(p, sizeof(apr_crypto_block_t)); } if (!block) { return APR_ENOMEM; } block->f = key->f; block->pool = p; block->provider = key->provider; apr_pool_cleanup_register(p, block, crypto_block_cleanup_helper, apr_pool_cleanup_null); if (key->ivSize) { if (iv == NULL) { return APR_ENOIV; } if (*iv == NULL) { SECStatus s; usedIv = apr_pcalloc(p, key->ivSize); if (!usedIv) { return APR_ENOMEM; } apr_crypto_clear(p, usedIv, key->ivSize); s = PK11_GenerateRandom(usedIv, key->ivSize); if (s != SECSuccess) { return APR_ENOIV; } *iv = usedIv; } else { usedIv = (unsigned char *) *iv; } ivItem.data = usedIv; ivItem.len = key->ivSize; secParam = PK11_ParamFromIV(key->cipherMech, &ivItem); } else { secParam = PK11_GenerateNewParam(key->cipherMech, key->symKey); } block->blockSize = PK11_GetBlockSize(key->cipherMech, secParam); block->ctx = PK11_CreateContextBySymKey(key->cipherMech, CKA_ENCRYPT, key->symKey, secParam); /* did an error occur? */ perr = PORT_GetError(); if (perr || !block->ctx) { key->f->result->rc = perr; key->f->result->msg = PR_ErrorToName(perr); return APR_EINIT; } if (blockSize) { *blockSize = PK11_GetBlockSize(key->cipherMech, secParam); } return APR_SUCCESS; }
/* * SVRCORE_CreatePk11PinStore */ SVRCOREError SVRCORE_CreatePk11PinStore( SVRCOREPk11PinStore **out, const char *tokenName, const char *pin) { SVRCOREError err; SVRCOREPk11PinStore *store; do { err = SVRCORE_Success; store = (SVRCOREPk11PinStore*)malloc(sizeof *store); if (store == 0) { err = SVRCORE_NoMemory_Error; break; } /* Low-level init */ store->slot = 0; store->key = 0; store->params = 0; store->crypt = 0; /* Use the tokenName to find a PKCS11 slot */ store->slot = PK11_FindSlotByName((char *)tokenName); if (store->slot == 0) { err = SVRCORE_NoSuchToken_Error; break; } /* Check the password/PIN. This allows access to the token */ { SECStatus rv = PK11_CheckUserPassword(store->slot, (char *)pin); if (rv == SECSuccess) ; else if (rv == SECWouldBlock) { err = SVRCORE_IncorrectPassword_Error; break; } else { err = SVRCORE_System_Error; break; } } /* Find the mechanism that this token can do */ { const mech_item *tp; store->mech = 0; for(tp = table;tp < &table[MECH_TABLE_SIZE];tp++) { if (PK11_DoesMechanism(store->slot, tp->type)) { store->mech = tp; break; } } /* Default to a mechanism (probably on the internal token */ if (store->mech == 0) store->mech = &dflt_mech; } /* Generate a key and parameters to do the encryption */ store->key = PK11_TokenKeyGenWithFlags(store->slot, store->mech->type, 0, 0, 0, CKF_ENCRYPT|CKF_DECRYPT, 0, 0); if (store->key == 0) { /* PR_SetError(xxx); */ err = SVRCORE_System_Error; break; } store->params = PK11_GenerateNewParam(store->mech->type, store->key); if (store->params == 0) { err = SVRCORE_System_Error; break; } /* Compute the size of the encrypted data including necessary padding */ { int blocksize = PK11_GetBlockSize(store->mech->type, 0); store->length = strlen(pin)+1; /* Compute padded size - 0 means stream cipher */ if (blocksize != 0) { store->length += blocksize - (store->length % blocksize); } store->crypt = (unsigned char *)malloc(store->length); if (!store->crypt) { err = SVRCORE_NoMemory_Error; break; } } /* Encrypt */ { unsigned char *plain; PK11Context *ctx; SECStatus rv; int outLen; plain = (unsigned char *)malloc(store->length); if (!plain) { err = SVRCORE_NoMemory_Error; break; } /* Pad with 0 bytes */ memset(plain, 0, store->length); strcpy((char *)plain, pin); ctx = PK11_CreateContextBySymKey(store->mech->type, CKA_ENCRYPT, store->key, store->params); if (!ctx) { err = SVRCORE_System_Error; break; } do { rv = PK11_CipherOp(ctx, store->crypt, &outLen, store->length, plain, store->length); if (rv) break; rv = PK11_Finalize(ctx); } while(0); PK11_DestroyContext(ctx, PR_TRUE); memset(plain, 0, store->length); free(plain); if (rv) err = SVRCORE_System_Error; } } while(0); if (err) { SVRCORE_DestroyPk11PinStore(store); store = 0; } *out = store; return err; }
int nsscrypto_init( knet_handle_t knet_h, struct knet_handle_crypto_cfg *knet_handle_crypto_cfg) { struct nsscrypto_instance *nsscrypto_instance = NULL; log_debug(knet_h, KNET_SUB_NSSCRYPTO, "Initizializing nss crypto module [%s/%s]", knet_handle_crypto_cfg->crypto_cipher_type, knet_handle_crypto_cfg->crypto_hash_type); knet_h->crypto_instance->model_instance = malloc(sizeof(struct nsscrypto_instance)); if (!knet_h->crypto_instance->model_instance) { log_err(knet_h, KNET_SUB_NSSCRYPTO, "Unable to allocate memory for nss model instance"); return -1; } nsscrypto_instance = knet_h->crypto_instance->model_instance; memset(nsscrypto_instance, 0, sizeof(struct nsscrypto_instance)); nsscrypto_instance->crypto_cipher_type = string_to_crypto_cipher_type(knet_handle_crypto_cfg->crypto_cipher_type); if (nsscrypto_instance->crypto_cipher_type < 0) { log_err(knet_h, KNET_SUB_NSSCRYPTO, "unknown crypto cipher type requested"); goto out_err; } nsscrypto_instance->crypto_hash_type = string_to_crypto_hash_type(knet_handle_crypto_cfg->crypto_hash_type); if (nsscrypto_instance->crypto_hash_type < 0) { log_err(knet_h, KNET_SUB_NSSCRYPTO, "unknown crypto hash type requested"); goto out_err; } if ((nsscrypto_instance->crypto_cipher_type > 0) && (nsscrypto_instance->crypto_hash_type == 0)) { log_err(knet_h, KNET_SUB_NSSCRYPTO, "crypto communication requires hash specified"); goto out_err; } nsscrypto_instance->private_key = knet_handle_crypto_cfg->private_key; nsscrypto_instance->private_key_len = knet_handle_crypto_cfg->private_key_len; if (init_nss(knet_h) < 0) { goto out_err; } knet_h->sec_header_size = 0; if (nsscrypto_instance->crypto_hash_type > 0) { knet_h->sec_header_size += hash_len[nsscrypto_instance->crypto_hash_type]; knet_h->sec_hash_size = hash_len[nsscrypto_instance->crypto_hash_type]; } if (nsscrypto_instance->crypto_cipher_type > 0) { int block_size; if (cypher_block_len[nsscrypto_instance->crypto_cipher_type]) { block_size = cypher_block_len[nsscrypto_instance->crypto_cipher_type]; } else { block_size = PK11_GetBlockSize(nsscrypto_instance->crypto_cipher_type, NULL); if (block_size < 0) { goto out_err; } } knet_h->sec_header_size += (block_size * 2); knet_h->sec_header_size += SALT_SIZE; knet_h->sec_salt_size = SALT_SIZE; knet_h->sec_block_size = block_size; } return 0; out_err: nsscrypto_fini(knet_h); return -1; }