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 + -
显示快捷键?