p7local.c

来自「支持SSL v2/v3, TLS, PKCS #5, PKCS #7, PKCS」· C语言 代码 · 共 1,432 行 · 第 1/3 页

C
1,432
字号
/* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ *  * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. *  * The Original Code is the Netscape security libraries. *  * The Initial Developer of the Original Code is Netscape * Communications Corporation.  Portions created by Netscape are  * Copyright (C) 1994-2000 Netscape Communications Corporation.  All * Rights Reserved. *  * Contributor(s): *  * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License Version 2 or later (the * "GPL"), in which case the provisions of the GPL are applicable  * instead of those above.  If you wish to allow use of your  * version of this file only under the terms of the GPL and not to * allow others to use your version of this file under the MPL, * indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by * the GPL.  If you do not delete the provisions above, a recipient * may use your version of this file under either the MPL or the * GPL. *//* * Support routines for PKCS7 implementation, none of which are exported. * This file should only contain things that are needed by both the * encoding/creation side *and* the decoding/decryption side.  Anything * else should be static routines in the appropriate file. * * $Id: p7local.c,v 1.1 2000/03/31 19:16:06 relyea%netscape.com Exp $ */#include "p7local.h"#include "cryptohi.h" #include "secasn1.h"#include "secoid.h"#include "secitem.h"#include "pk11func.h"#include "secpkcs5.h"#include "secerr.h"/* * ------------------------------------------------------------------- * Cipher stuff. */typedef SECStatus (*sec_pkcs7_cipher_function) (void *,						unsigned char *,						unsigned *,						unsigned int,						const unsigned char *,						unsigned int);typedef SECStatus (*sec_pkcs7_cipher_destroy) (void *, PRBool);#define BLOCK_SIZE 4096struct sec_pkcs7_cipher_object {    void *cx;    sec_pkcs7_cipher_function doit;    sec_pkcs7_cipher_destroy destroy;    PRBool encrypt;    int block_size;    int pad_size;    int pending_count;    unsigned char pending_buf[BLOCK_SIZE];};/* * 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 mechanism;    SECItem *param;    PK11SlotInfo *slot;    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)) {	CK_MECHANISM pbeMech, cryptoMech;	SECItem *pbeParams, *pwitem;	SEC_PKCS5KeyAndPassword *keyPwd;	keyPwd = (SEC_PKCS5KeyAndPassword *)key;	key = keyPwd->key;	pwitem = keyPwd->pwitem;	pbeMech.mechanism = PK11_AlgtagToMechanism(algtag);	pbeParams = PK11_ParamFromAlgid(algid);	if (!pbeParams) {	    PORT_Free(result);	    return NULL;	}	pbeMech.pParameter = pbeParams->data;	pbeMech.ulParameterLen = pbeParams->len;	if (PK11_MapPBEMechanismToCryptoMechanism(&pbeMech, &cryptoMech, pwitem,						  PR_FALSE) != CKR_OK) { 	    PORT_Free(result);	    SECITEM_ZfreeItem(pbeParams, PR_TRUE);	    return NULL;	}	SECITEM_ZfreeItem(pbeParams, PR_TRUE);	param = (SECItem *)PORT_ZAlloc(sizeof(SECItem));	if(!param) {	     PORT_Free(result);	     return NULL;	}	param->data = (unsigned char *)cryptoMech.pParameter;	param->len = cryptoMech.ulParameterLen;	mechanism = cryptoMech.mechanism;    } else {	mechanism = PK11_AlgtagToMechanism(algtag);	param = PK11_ParamFromAlgid(algid);	if (param == NULL) {	    PORT_Free(result);	    return NULL;	}    }    result->pad_size = PK11_GetBlockSize(mechanism,param);    slot = PK11_GetSlotFromKey(key);    result->block_size = PK11_IsHW(slot) ? BLOCK_SIZE : result->pad_size;    PK11_FreeSlot(slot);    ciphercx = PK11_CreateContextBySymKey(mechanism, 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;    SECItem *param;    SECStatus rv;    CK_MECHANISM_TYPE mechanism;    PRBool needToEncodeAlgid = PR_FALSE;    PK11SlotInfo *slot;    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)) {	CK_MECHANISM pbeMech, cryptoMech;	SECItem *pbeParams;	SEC_PKCS5KeyAndPassword *keyPwd;	PORT_Memset(&pbeMech, 0, sizeof(CK_MECHANISM));	PORT_Memset(&cryptoMech, 0, sizeof(CK_MECHANISM));	pbeMech.mechanism = PK11_AlgtagToMechanism(algtag);	pbeParams = PK11_ParamFromAlgid(algid);	if(!pbeParams) {	    PORT_Free(result);	    return NULL;	}	keyPwd = (SEC_PKCS5KeyAndPassword *)key;	key = keyPwd->key;	pbeMech.pParameter = pbeParams->data;	pbeMech.ulParameterLen = pbeParams->len;	if(PK11_MapPBEMechanismToCryptoMechanism(&pbeMech, &cryptoMech, 					 keyPwd->pwitem, PR_FALSE) != CKR_OK) {	    PORT_Free(result);	    SECITEM_ZfreeItem(pbeParams, PR_TRUE);	    return NULL;	}	SECITEM_ZfreeItem(pbeParams, PR_TRUE);	param = (SECItem *)PORT_ZAlloc(sizeof(SECItem));	if(!param) {	     PORT_Free(result);	     return NULL;	}	param->data = (unsigned char *)cryptoMech.pParameter;	param->len = cryptoMech.ulParameterLen;	mechanism = cryptoMech.mechanism;    } else {	mechanism = PK11_AlgtagToMechanism(algtag);	param = PK11_GenerateNewParam(mechanism,key);	if (param == NULL) {	    PORT_Free(result);	    return NULL;	}	needToEncodeAlgid = PR_TRUE;    }    result->pad_size = PK11_GetBlockSize(mechanism,param);    slot = PK11_GetSlotFromKey(key);    result->block_size = PK11_IsHW(slot) ? BLOCK_SIZE : result->pad_size;    PK11_FreeSlot(slot);    ciphercx = PK11_CreateContextBySymKey(mechanism, 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) {	    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;}/* * Destroy the cipher object. */static voidsec_pkcs7_destroy_cipher (sec_PKCS7CipherObject *obj){    (* obj->destroy) (obj->cx, PR_TRUE);    PORT_Free (obj);}voidsec_PKCS7DestroyDecryptObject (sec_PKCS7CipherObject *obj){    PORT_Assert (obj != NULL);    if (obj == NULL)	return;    PORT_Assert (! obj->encrypt);    sec_pkcs7_destroy_cipher (obj);}voidsec_PKCS7DestroyEncryptObject (sec_PKCS7CipherObject *obj){    PORT_Assert (obj != NULL);    if (obj == NULL)	return;    PORT_Assert (obj->encrypt);    sec_pkcs7_destroy_cipher (obj);}/* * XXX I think all of the following lengths should be longs instead * of ints, but our current crypto interface uses ints, so I did too. *//* * What will be the output length of the next call to decrypt? * Result can be used to perform memory allocations.  Note that the amount * is exactly accurate only when not doing a block cipher or when final * is false, otherwise it is an upper bound on the amount because until * we see the data we do not know how many padding bytes there are * (always between 1 and bsize). * * Note that this can return zero, which does not mean that the decrypt * operation can be skipped!  (It simply means that there are not enough * bytes to make up an entire block; the bytes will be reserved until * there are enough to encrypt/decrypt at least one block.)  However, * if zero is returned it *does* mean that no output buffer need be * passed in to the subsequent decrypt operation, as no output bytes * will be stored. */unsigned intsec_PKCS7DecryptLength (sec_PKCS7CipherObject *obj, unsigned int input_len,			PRBool final){    int blocks, block_size;    PORT_Assert (! obj->encrypt);    block_size = obj->block_size;    /*     * If this is not a block cipher, then we always have the same     * number of output bytes as we had input bytes.     */    if (block_size == 0)	return input_len;    /*     * On the final call, we will always use up all of the pending     * bytes plus all of the input bytes, *but*, there will be padding     * at the end and we cannot predict how many bytes of padding we     * will end up removing.  The amount given here is actually known     * to be at least 1 byte too long (because we know we will have     * at least 1 byte of padding), but seemed clearer/better to me.     */    if (final)	return obj->pending_count + input_len;    /*     * Okay, this amount is exactly what we will output on the     * next cipher operation.  We will always hang onto the last     * 1 - block_size bytes for non-final operations.  That is,     * we will do as many complete blocks as we can *except* the     * last block (complete or partial).  (This is because until     * we know we are at the end, we cannot know when to interpret     * and removing the padding byte(s), which are guaranteed to     * be there.)     */    blocks = (obj->pending_count + input_len - 1) / block_size;    return blocks * block_size;}/* * What will be the output length of the next call to encrypt? * Result can be used to perform memory allocations. * * Note that this can return zero, which does not mean that the encrypt * operation can be skipped!  (It simply means that there are not enough * bytes to make up an entire block; the bytes will be reserved until * there are enough to encrypt/decrypt at least one block.)  However, * if zero is returned it *does* mean that no output buffer need be * passed in to the subsequent encrypt operation, as no output bytes * will be stored. */unsigned intsec_PKCS7EncryptLength (sec_PKCS7CipherObject *obj, unsigned int input_len,			PRBool final){    int blocks, block_size;    int pad_size;    PORT_Assert (obj->encrypt);    block_size = obj->block_size;    pad_size = obj->pad_size;    /*     * If this is not a block cipher, then we always have the same     * number of output bytes as we had input bytes.     */    if (block_size == 0)	return input_len;    /*     * On the final call, we only send out what we need for     * remaining bytes plus the padding.  (There is always padding,     * so even if we have an exact number of blocks as input, we     * will add another full block that is just padding.)     */    if (final) {	if (pad_size == 0) {    	    return obj->pending_count + input_len;	} else {    	    blocks = (obj->pending_count + input_len) / pad_size;	    blocks++;	    return blocks*pad_size;	}    }    /*     * Now, count the number of complete blocks of data we have.     */    blocks = (obj->pending_count + input_len) / block_size;    return blocks * block_size;}/* * Decrypt a given length of input buffer (starting at "input" and * containing "input_len" bytes), placing the decrypted bytes in * "output" and storing the output length in "*output_len_p". * "obj" is the return value from sec_PKCS7CreateDecryptObject. * When "final" is true, this is the last of the data to be decrypted. * * This is much more complicated than it sounds when the cipher is * a block-type, meaning that the decryption function will only * operate on whole blocks.  But our caller is operating stream-wise, * and can pass in any number of bytes.  So we need to keep track * of block boundaries.  We save excess bytes between calls in "obj". * We also need to determine which bytes are padding, and remove * them from the output.  We can only do this step when we know we * have the final block of data.  PKCS #7 specifies that the padding * used for a block cipher is a string of bytes, each of whose value is * the same as the length of the padding, and that all data is padded. * (Even data that starts out with an exact multiple of blocks gets * added to it another block, all of which is padding.) */ SECStatussec_PKCS7Decrypt (sec_PKCS7CipherObject *obj, unsigned char *output,		  unsigned int *output_len_p, unsigned int max_output_len,		  const unsigned char *input, unsigned int input_len,		  PRBool final){    int blocks, bsize, pcount, padsize;    unsigned int max_needed, ifraglen, ofraglen, output_len;    unsigned char *pbuf;    SECStatus rv;    PORT_Assert (! obj->encrypt);    /*     * Check that we have enough room for the output.  Our caller should     * already handle this; failure is really an internal error (i.e. bug).     */    max_needed = sec_PKCS7DecryptLength (obj, input_len, final);    PORT_Assert (max_output_len >= max_needed);    if (max_output_len < max_needed) {	/* PORT_SetError (XXX); */	return SECFailure;    }

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?