p7local.c

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

C
1,432
字号
    /*     * hardware encryption does not like small decryption sizes here, so we     * allow both blocking and padding.     */    bsize = obj->block_size;    padsize = obj->pad_size;    /*     * When no blocking or padding work to do, we can simply call the     * cipher function and we are done.     */    if (bsize == 0) {	return (* obj->doit) (obj->cx, output, output_len_p, max_output_len,			      input, input_len);    }    pcount = obj->pending_count;    pbuf = obj->pending_buf;    output_len = 0;    if (pcount) {	/*	 * Try to fill in an entire block, starting with the bytes	 * we already have saved away.	 */	while (input_len && pcount < bsize) {	    pbuf[pcount++] = *input++;	    input_len--;	}	/*	 * If we have at most a whole block and this is not our last call,	 * then we are done for now.  (We do not try to decrypt a lone	 * single block because we cannot interpret the padding bytes	 * until we know we are handling the very last block of all input.)	 */	if (input_len == 0 && !final) {	    obj->pending_count = pcount;	    if (output_len_p)		*output_len_p = 0;	    return SECSuccess;	}	/*	 * Given the logic above, we expect to have a full block by now.	 * If we do not, there is something wrong, either with our own	 * logic or with (length of) the data given to us.	 */	PORT_Assert ((padsize == 0) || (pcount % padsize) == 0);	if ((padsize != 0) && (pcount % padsize) != 0) {	    PORT_Assert (final);		    PORT_SetError (SEC_ERROR_BAD_DATA);	    return SECFailure;	}	/*	 * Decrypt the block.	 */	rv = (* obj->doit) (obj->cx, output, &ofraglen, max_output_len,			    pbuf, pcount);	if (rv != SECSuccess)	    return rv;	/*	 * For now anyway, all of our ciphers have the same number of	 * bytes of output as they do input.  If this ever becomes untrue,	 * then sec_PKCS7DecryptLength needs to be made smarter!	 */	PORT_Assert (ofraglen == pcount);	/*	 * Account for the bytes now in output.	 */	max_output_len -= ofraglen;	output_len += ofraglen;	output += ofraglen;    }    /*     * If this is our last call, we expect to have an exact number of     * blocks left to be decrypted; we will decrypt them all.     *      * If not our last call, we always save between 1 and bsize bytes     * until next time.  (We must do this because we cannot be sure     * that none of the decrypted bytes are padding bytes until we     * have at least another whole block of data.  You cannot tell by     * looking -- the data could be anything -- you can only tell by     * context, knowing you are looking at the last block.)  We could     * decrypt a whole block now but it is easier if we just treat it     * the same way we treat partial block bytes.     */    if (final) {	if (padsize) {	    blocks = input_len / padsize;	    ifraglen = blocks * padsize;	} else ifraglen = input_len;	PORT_Assert (ifraglen == input_len);	if (ifraglen != input_len) {	    PORT_SetError (SEC_ERROR_BAD_DATA);	    return SECFailure;	}    } else {	blocks = (input_len - 1) / bsize;	ifraglen = blocks * bsize;	PORT_Assert (ifraglen < input_len);	pcount = input_len - ifraglen;	PORT_Memcpy (pbuf, input + ifraglen, pcount);	obj->pending_count = pcount;    }    if (ifraglen) {	rv = (* obj->doit) (obj->cx, output, &ofraglen, max_output_len,			    input, ifraglen);	if (rv != SECSuccess)	    return rv;	/*	 * For now anyway, all of our ciphers have the same number of	 * bytes of output as they do input.  If this ever becomes untrue,	 * then sec_PKCS7DecryptLength needs to be made smarter!	 */	PORT_Assert (ifraglen == ofraglen);	if (ifraglen != ofraglen) {	    PORT_SetError (SEC_ERROR_BAD_DATA);	    return SECFailure;	}	output_len += ofraglen;    } else {	ofraglen = 0;    }    /*     * If we just did our very last block, "remove" the padding by     * adjusting the output length.     */    if (final && (padsize != 0)) {	unsigned int padlen = *(output + ofraglen - 1);	PORT_Assert (padlen > 0 && padlen <= padsize);	if (padlen == 0 || padlen > padsize) {	    PORT_SetError (SEC_ERROR_BAD_DATA);	    return SECFailure;	}	output_len -= padlen;    }    PORT_Assert (output_len_p != NULL || output_len == 0);    if (output_len_p != NULL)	*output_len_p = output_len;    return SECSuccess;}/* * Encrypt a given length of input buffer (starting at "input" and * containing "input_len" bytes), placing the encrypted bytes in * "output" and storing the output length in "*output_len_p". * "obj" is the return value from sec_PKCS7CreateEncryptObject. * When "final" is true, this is the last of the data to be encrypted. * * This is much more complicated than it sounds when the cipher is * a block-type, meaning that the encryption 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 add padding bytes at the end.  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.) * * XXX I would kind of like to combine this with the function above * which does decryption, since they have a lot in common.  But the * tricky parts about padding and filling blocks would be much * harder to read that way, so I left them separate.  At least for * now until it is clear that they are right. */ SECStatussec_PKCS7Encrypt (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, padlen, 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_PKCS7EncryptLength (obj, input_len, final);    PORT_Assert (max_output_len >= max_needed);    if (max_output_len < max_needed) {	/* PORT_SetError (XXX); */	return SECFailure;    }    bsize = obj->block_size;    padsize = obj->pad_size;    /*     * When no blocking and padding work to do, we can simply call the     * cipher function and we are done.     */    if (bsize == 0) {	return (* obj->doit) (obj->cx, output, output_len_p, max_output_len,			      input, input_len);    }    pcount = obj->pending_count;    pbuf = obj->pending_buf;    output_len = 0;    if (pcount) {	/*	 * Try to fill in an entire block, starting with the bytes	 * we already have saved away.	 */	while (input_len && pcount < bsize) {	    pbuf[pcount++] = *input++;	    input_len--;	}	/*	 * If we do not have a full block and we know we will be	 * called again, then we are done for now.	 */	if (pcount < bsize && !final) {	    obj->pending_count = pcount;	    if (output_len_p != NULL)		*output_len_p = 0;	    return SECSuccess;	}	/*	 * If we have a whole block available, encrypt it.	 */	if ((padsize == 0) || (pcount % padsize) == 0) {	    rv = (* obj->doit) (obj->cx, output, &ofraglen, max_output_len,				pbuf, pcount);	    if (rv != SECSuccess)		return rv;	    /*	     * For now anyway, all of our ciphers have the same number of	     * bytes of output as they do input.  If this ever becomes untrue,	     * then sec_PKCS7EncryptLength needs to be made smarter!	     */	    PORT_Assert (ofraglen == pcount);	    /*	     * Account for the bytes now in output.	     */	    max_output_len -= ofraglen;	    output_len += ofraglen;	    output += ofraglen;	    pcount = 0;	}    }    if (input_len) {	PORT_Assert (pcount == 0);	blocks = input_len / bsize;	ifraglen = blocks * bsize;	if (ifraglen) {	    rv = (* obj->doit) (obj->cx, output, &ofraglen, max_output_len,				input, ifraglen);	    if (rv != SECSuccess)		return rv;	    /*	     * For now anyway, all of our ciphers have the same number of	     * bytes of output as they do input.  If this ever becomes untrue,	     * then sec_PKCS7EncryptLength needs to be made smarter!	     */	    PORT_Assert (ifraglen == ofraglen);	    max_output_len -= ofraglen;	    output_len += ofraglen;	    output += ofraglen;	}	pcount = input_len - ifraglen;	PORT_Assert (pcount < bsize);	if (pcount)	    PORT_Memcpy (pbuf, input + ifraglen, pcount);    }    if (final) {	padlen = padsize - (pcount % padsize);	PORT_Memset (pbuf + pcount, padlen, padlen);	rv = (* obj->doit) (obj->cx, output, &ofraglen, max_output_len,			    pbuf, pcount+padlen);	if (rv != SECSuccess)	    return rv;	/*	 * For now anyway, all of our ciphers have the same number of	 * bytes of output as they do input.  If this ever becomes untrue,	 * then sec_PKCS7EncryptLength needs to be made smarter!	 */	PORT_Assert (ofraglen == (pcount+padlen));	output_len += ofraglen;    } else {	obj->pending_count = pcount;    }    PORT_Assert (output_len_p != NULL || output_len == 0);    if (output_len_p != NULL)	*output_len_p = output_len;    return SECSuccess;}/* * End of cipher stuff. * ------------------------------------------------------------------- *//* * ------------------------------------------------------------------- * XXX The following Attribute stuff really belongs elsewhere. * The Attribute type is *not* part of pkcs7 but rather X.501. * But for now, since PKCS7 is the only customer of attributes, * we define them here.  Once there is a use outside of PKCS7, * then change the attribute types and functions from internal * to external naming convention, and move them elsewhere! *//* * Look through a set of attributes and find one that matches the * specified object ID.  If "only" is true, then make sure that * there is not more than one attribute of the same type.  Otherwise, * just return the first one found. (XXX Does anybody really want * that first-found behavior?  It was like that when I found it...) */SEC_PKCS7Attribute *sec_PKCS7FindAttribute (SEC_PKCS7Attribute **attrs, SECOidTag oidtag,			PRBool only){    SECOidData *oid;    SEC_PKCS7Attribute *attr1, *attr2;    if (attrs == NULL)	return NULL;    oid = SECOID_FindOIDByTag(oidtag);    if (oid == NULL)	return NULL;    while ((attr1 = *attrs++) != NULL) {	if (attr1->type.len == oid->oid.len && PORT_Memcmp (attr1->type.data,							    oid->oid.data,							    oid->oid.len) == 0)	    break;    }    if (attr1 == NULL)	return NULL;    if (!only)	return attr1;    while ((attr2 = *attrs++) != NULL) {	if (attr2->type.len == oid->oid.len && PORT_Memcmp (attr2->type.data,							    oid->oid.data,							    oid->oid.len) == 0)	    break;    }    if (attr2 != NULL)	return NULL;    return attr1;}/* * Return the single attribute value, doing some sanity checking first: * - Multiple values are *not* expected. * - Empty values are *not* expected. */SECItem *sec_PKCS7AttributeValue(SEC_PKCS7Attribute *attr){    SECItem *value;    if (attr == NULL)	return NULL;    value = attr->values[0];    if (value == NULL || value->data == NULL || value->len == 0)	return NULL;    if (attr->values[1] != NULL)	return NULL;    return value;}static const SEC_ASN1Template *sec_attr_choose_attr_value_template(void *src_or_dest, PRBool encoding){    const SEC_ASN1Template *theTemplate;    SEC_PKCS7Attribute *attribute;    SECOidData *oiddata;    PRBool encoded;    PORT_Assert (src_or_dest != NULL);    if (src_or_dest == NULL)	return NULL;    attribute = (SEC_PKCS7Attribute*)src_or_dest;    if (encoding && attribute->encoded)	return SEC_AnyTemplate;    oiddata = attribute->typeTag;    if (oiddata == NULL) {	oiddata = SECOID_FindOID(&attribute->type);	attribute->typeTag = oiddata;    }    if (oiddata == NULL) {	encoded = PR_TRUE;	theTemplate = SEC_AnyTemplate;    } else {	switch (oiddata->offset) {	  default:	    encoded = PR_TRUE;	    theTemplate = SEC_AnyTemplate;	    break;	  case SEC_OID_PKCS9_EMAIL_ADDRESS:	  case SEC_OID_RFC1274_MAIL:	  case SEC_OID_PKCS9_UNSTRUCTURED_NAME:	    encoded = PR_FALSE;	    theTemplate = SEC_IA5StringTemplate;	    break;	  case SEC_OID_PKCS9_CONTENT_TYPE:	    encoded = PR_FALSE;	    theTemplate = SEC_ObjectIDTemplate;	    break;	  case SEC_OID_PKCS9_MESSAGE_DIGEST:	    encoded = PR_FALSE;	    theTemplate = SEC_OctetStringTemplate;	    break;	  case SEC_OID_PKCS9_SIGNING_TIME:	    encoded = PR_FALSE;	    theTemplate = SEC_UTCTimeTemplate;	    break;	  /* XXX Want other types here, too */	}    }    if (encoding) {	/*	 * If we are encoding and we think we have an already-encoded value,	 * then the code which initialized this attribute should have set	 * the "encoded" property to true (and we would have returned early,	 * up above).  No devastating error, but that code should be fixed.	 * (It could indicate that the resulting encoded bytes are wrong.)	 */	PORT_Assert (!encoded);    } else {	/*	 * We are decoding; record whether the resulting value is	 * still encoded or not.	 */	attribute->encoded = encoded;

⌨️ 快捷键说明

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