X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/5dd5f9ec28f304ca377c42fd7f711d6cf12b90e1..5c19dc3ae3bd8e40a9c028b0deddd50ff337692c:/Security/libsecurity_smime/lib/cmscipher.c?ds=sidebyside diff --git a/Security/libsecurity_smime/lib/cmscipher.c b/Security/libsecurity_smime/lib/cmscipher.c deleted file mode 100644 index 02ec0873..00000000 --- a/Security/libsecurity_smime/lib/cmscipher.c +++ /dev/null @@ -1,1199 +0,0 @@ -/* - * 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. - */ - -/* - * Encryption/decryption routines for CMS implementation, none of which are exported. - * - */ - -#include "cmslocal.h" - -#include "secoid.h" -#include -#include -#include -#include -#include -#include - -/* - * ------------------------------------------------------------------- - * Cipher stuff. - */ - -#if 0 -typedef OSStatus (*nss_cms_cipher_function) (void *, unsigned char *, unsigned int *, - unsigned int, const unsigned char *, unsigned int); -typedef OSStatus (*nss_cms_cipher_destroy) (void *, Boolean); -#endif - -#define BLOCK_SIZE 4096 - -struct SecCmsCipherContextStr { -#if 1 - CSSM_CC_HANDLE cc; /* CSP CONTEXT */ - Boolean encrypt; /* encrypt / decrypt switch */ -#else - void * cx; /* PK11 cipher context */ - nss_cms_cipher_function doit; - nss_cms_cipher_destroy destroy; - Boolean encrypt; /* encrypt / decrypt switch */ - int block_size; /* block & pad sizes for cipher */ - int pad_size; - int pending_count; /* pending data (not yet en/decrypted */ - unsigned char pending_buf[BLOCK_SIZE];/* because of blocking */ -#endif -}; - -typedef struct sec_rc2cbcParameterStr { - SECItem rc2ParameterVersion; - SECItem iv; -} sec_rc2cbcParameter; - -static const SecAsn1Template sec_rc2cbc_parameter_template[] = { - { SEC_ASN1_SEQUENCE, - 0, NULL, sizeof(sec_rc2cbcParameter) }, - { SEC_ASN1_INTEGER | SEC_ASN1_SIGNED_INT, - offsetof(sec_rc2cbcParameter,rc2ParameterVersion) }, - { SEC_ASN1_OCTET_STRING, - offsetof(sec_rc2cbcParameter,iv) }, - { 0 } -}; - -/* -** Convert a der encoded *signed* integer into a machine integral value. -** If an underflow/overflow occurs, sets error code and returns min/max. -*/ -static long -DER_GetInteger(SECItem *it) -{ - long ival = 0; - CSSM_SIZE len = it->Length; - unsigned char *cp = it->Data; - unsigned long overflow = 0x1ffUL << (((sizeof(ival) - 1) * 8) - 1); - unsigned long ofloinit; - - if (*cp & 0x80) - ival = -1L; - ofloinit = ival & overflow; - - while (len) { - if ((ival & overflow) != ofloinit) { - PORT_SetError(SEC_ERROR_BAD_DER); - if (ival < 0) { - return LONG_MIN; - } - return LONG_MAX; - } - ival = ival << 8; - ival |= *cp++; - --len; - } - return ival; -} - -/* S/MIME picked id values to represent differnt keysizes */ -/* I do have a formula, but it ain't pretty, and it only works because you - * can always match three points to a parabola:) */ -static unsigned char rc2_map(SECItem *version) -{ - long x; - - x = DER_GetInteger(version); - - switch (x) { - case 58: return 128; - case 120: return 64; - case 160: return 40; - } - return 128; -} - -static unsigned long rc2_unmap(unsigned long x) -{ - switch (x) { - case 128: return 58; - case 64: return 120; - case 40: return 160; - } - return 58; -} - -/* default IV size in bytes */ -#define DEFAULT_IV_SIZE 8 -/* IV/block size for AES */ -#define AES_BLOCK_SIZE 16 -/* max IV size in bytes */ -#define MAX_IV_SIZE AES_BLOCK_SIZE - -static SecCmsCipherContextRef -SecCmsCipherContextStart(PRArenaPool *poolp, SecSymmetricKeyRef key, SECAlgorithmID *algid, Boolean encrypt) -{ - SecCmsCipherContextRef cc; - CSSM_CC_HANDLE ciphercc = 0; - SECOidData *oidData; - SECOidTag algtag; - CSSM_ALGORITHMS algorithm; - CSSM_PADDING padding = CSSM_PADDING_PKCS7; - CSSM_ENCRYPT_MODE mode; - CSSM_CSP_HANDLE cspHandle; - const CSSM_KEY *cssmKey; - OSStatus rv; - uint8 ivbuf[MAX_IV_SIZE]; - CSSM_DATA initVector = { DEFAULT_IV_SIZE, ivbuf }; - //CSSM_CONTEXT_ATTRIBUTE contextAttribute = { CSSM_ATTRIBUTE_ALG_PARAMS, sizeof(CSSM_DATA_PTR) }; - - rv = SecKeyGetCSPHandle(key, &cspHandle); - if (rv) - goto loser; - rv = SecKeyGetCSSMKey(key, &cssmKey); - if (rv) - goto loser; - - // @@@ Add support for PBE based stuff - - oidData = SECOID_FindOID(&algid->algorithm); - if (!oidData) - goto loser; - algtag = oidData->offset; - algorithm = oidData->cssmAlgorithm; - if (!algorithm) - goto loser; - - switch (algtag) - { - case SEC_OID_RC2_CBC: - case SEC_OID_RC4: - case SEC_OID_DES_EDE3_CBC: - case SEC_OID_DES_EDE: - case SEC_OID_DES_CBC: - case SEC_OID_RC5_CBC_PAD: - case SEC_OID_FORTEZZA_SKIPJACK: - mode = CSSM_ALGMODE_CBCPadIV8; - break; - - /* RFC 3565 says that these sizes refer to key size, NOT block size */ - case SEC_OID_AES_128_CBC: - case SEC_OID_AES_192_CBC: - case SEC_OID_AES_256_CBC: - initVector.Length = AES_BLOCK_SIZE; - mode = CSSM_ALGMODE_CBCPadIV8; - break; - - case SEC_OID_DES_ECB: - case SEC_OID_AES_128_ECB: - case SEC_OID_AES_192_ECB: - case SEC_OID_AES_256_ECB: - mode = CSSM_ALGMODE_ECBPad; - break; - - case SEC_OID_DES_OFB: - mode = CSSM_ALGMODE_OFBPadIV8; - break; - - case SEC_OID_DES_CFB: - mode = CSSM_ALGMODE_CFBPadIV8; - break; - - default: - goto loser; - } - - if (encrypt) - { - CSSM_CC_HANDLE randomcc; - //SECItem *parameters; - - // Generate random initVector - if (CSSM_CSP_CreateRandomGenContext(cspHandle, - CSSM_ALGID_APPLE_YARROW, - NULL, /* seed*/ - initVector.Length, - &randomcc)) - goto loser; - - if (CSSM_GenerateRandom(randomcc, &initVector)) - goto loser; - CSSM_DeleteContext(randomcc); - - // Put IV into algid.parameters - switch (algtag) - { - case SEC_OID_RC4: - case SEC_OID_DES_EDE3_CBC: - case SEC_OID_DES_EDE: - case SEC_OID_DES_CBC: - case SEC_OID_AES_128_CBC: - case SEC_OID_AES_192_CBC: - case SEC_OID_AES_256_CBC: - case SEC_OID_FORTEZZA_SKIPJACK: - case SEC_OID_DES_ECB: - case SEC_OID_AES_128_ECB: - case SEC_OID_AES_192_ECB: - case SEC_OID_AES_256_ECB: - case SEC_OID_DES_OFB: - case SEC_OID_DES_CFB: - /* Just encode the initVector as an octet string. */ - if (!SEC_ASN1EncodeItem(poolp, &algid->parameters, - &initVector, kSecAsn1OctetStringTemplate)) - goto loser; - break; - - case SEC_OID_RC2_CBC: - { - sec_rc2cbcParameter rc2 = {}; - unsigned long rc2version; - SECItem *newParams; - - rc2.iv = initVector; - rc2version = rc2_unmap(cssmKey->KeyHeader.LogicalKeySizeInBits); - if (!SEC_ASN1EncodeUnsignedInteger (NULL, &(rc2.rc2ParameterVersion), - rc2version)) - goto loser; - newParams = SEC_ASN1EncodeItem (poolp, &algid->parameters, &rc2, - sec_rc2cbc_parameter_template); - PORT_Free(rc2.rc2ParameterVersion.Data); - if (newParams == NULL) - goto loser; - break; - } - case SEC_OID_RC5_CBC_PAD: - default: - // @@@ Implement rc5 params stuff. - goto loser; - break; - } - } - else - { - // Extract IV from algid.parameters - // Put IV into algid.parameters - switch (algtag) - { - case SEC_OID_RC4: - case SEC_OID_DES_EDE3_CBC: - case SEC_OID_DES_EDE: - case SEC_OID_DES_CBC: - case SEC_OID_AES_128_CBC: - case SEC_OID_AES_192_CBC: - case SEC_OID_AES_256_CBC: - case SEC_OID_FORTEZZA_SKIPJACK: - case SEC_OID_DES_ECB: - case SEC_OID_AES_128_ECB: - case SEC_OID_AES_192_ECB: - case SEC_OID_AES_256_ECB: - case SEC_OID_DES_OFB: - case SEC_OID_DES_CFB: - { - CSSM_DATA iv = {}; - /* Just decode the initVector from an octet string. */ - rv = SEC_ASN1DecodeItem(NULL, &iv, kSecAsn1OctetStringTemplate, &(algid->parameters)); - if (rv) - goto loser; - if (initVector.Length != iv.Length) { - PORT_Free(iv.Data); - goto loser; - } - memcpy(initVector.Data, iv.Data, initVector.Length); - PORT_Free(iv.Data); - break; - } - case SEC_OID_RC2_CBC: - { - sec_rc2cbcParameter rc2 = {}; - unsigned long ulEffectiveBits; - - rv = SEC_ASN1DecodeItem(NULL, &rc2 ,sec_rc2cbc_parameter_template, - &(algid->parameters)); - if (rv) - goto loser; - - if (initVector.Length != rc2.iv.Length) { - PORT_Free(rc2.iv.Data); - PORT_Free(rc2.rc2ParameterVersion.Data); - goto loser; - } - memcpy(initVector.Data, rc2.iv.Data, initVector.Length); - PORT_Free(rc2.iv.Data); - - ulEffectiveBits = rc2_map(&rc2.rc2ParameterVersion); - PORT_Free(rc2.rc2ParameterVersion.Data); - if (ulEffectiveBits != cssmKey->KeyHeader.LogicalKeySizeInBits) - goto loser; - break; - } - case SEC_OID_RC5_CBC_PAD: - default: - // @@@ Implement rc5 params stuff. - goto loser; - break; - } - } - - if (CSSM_CSP_CreateSymmetricContext(cspHandle, - algorithm, - mode, - NULL, /* accessCred */ - cssmKey, - &initVector, - padding, - NULL, /* reserved */ - &ciphercc)) - goto loser; - - if (encrypt) - rv = CSSM_EncryptDataInit(ciphercc); - else - rv = CSSM_DecryptDataInit(ciphercc); - if (rv) - goto loser; - - cc = (SecCmsCipherContextRef)PORT_ZAlloc(sizeof(SecCmsCipherContext)); - if (cc == NULL) - goto loser; - - cc->cc = ciphercc; - cc->encrypt = encrypt; - - return cc; -loser: - if (ciphercc) - CSSM_DeleteContext(ciphercc); - - return NULL; -} - -/* - * 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. - */ -SecCmsCipherContextRef -SecCmsCipherContextStartDecrypt(SecSymmetricKeyRef key, SECAlgorithmID *algid) -{ - return SecCmsCipherContextStart(NULL, key, algid, PR_FALSE); -#if 0 - SecCmsCipherContextRef cc; - void *ciphercx; - CK_MECHANISM_TYPE mechanism; - CSSM_DATA_PTR param; - PK11SlotInfo *slot; - SECOidTag algtag; - - algtag = SECOID_GetAlgorithmTag(algid); - - /* set param and mechanism */ - if (SEC_PKCS5IsAlgorithmPBEAlg(algid)) { - CK_MECHANISM pbeMech, cryptoMech; - CSSM_DATA_PTR 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_PTR)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 = (SecCmsCipherContextRef)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 -} - -/* - * 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. - */ -SecCmsCipherContextRef -SecCmsCipherContextStartEncrypt(PRArenaPool *poolp, SecSymmetricKeyRef key, SECAlgorithmID *algid) -{ - return SecCmsCipherContextStart(poolp, key, algid, PR_TRUE); -#if 0 - SecCmsCipherContextRef cc; - void *ciphercx; - CSSM_DATA_PTR param; - OSStatus rv; - CK_MECHANISM_TYPE mechanism; - PK11SlotInfo *slot; - Boolean needToEncodeAlgid = PR_FALSE; - SECOidTag algtag = SECOID_GetAlgorithmTag(algid); - - /* set param and mechanism */ - if (SEC_PKCS5IsAlgorithmPBEAlg(algid)) { - CK_MECHANISM pbeMech, cryptoMech; - CSSM_DATA_PTR 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_PTR)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 = (SecCmsCipherContextRef)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 -} - -void -SecCmsCipherContextDestroy(SecCmsCipherContextRef cc) -{ - PORT_Assert(cc != NULL); - if (cc == NULL) - return; - CSSM_DeleteContext(cc->cc); - PORT_Free(cc); -} - -static unsigned int -SecCmsCipherContextLength(SecCmsCipherContextRef cc, unsigned int input_len, Boolean final, Boolean encrypt) -{ - CSSM_QUERY_SIZE_DATA dataBlockSize[2] = { { input_len, 0 }, { input_len, 0 } }; - /* Hack CDSA treats the last block as the final one. So unless we are being asked to report the final size we ask for 2 block and ignore the second (final) one. */ - OSStatus rv = CSSM_QuerySize(cc->cc, cc->encrypt, final ? 1 : 2, dataBlockSize); - if (rv) - { - PORT_SetError(rv); - return 0; - } - - return dataBlockSize[0].SizeOutputBlock; -} - -/* - * SecCmsCipherContextDecryptLength - find the output length of the next call to decrypt. - * - * cc - the cipher context - * input_len - number of bytes used as input - * final - true if this is the final chunk of data - * - * 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. - */ -size_t -SecCmsCipherContextDecryptLength(SecCmsCipherContextRef cc, size_t input_len, Boolean final) -{ -#if 1 - return SecCmsCipherContextLength(cc, (unsigned int)input_len, final, PR_FALSE); -#else - int blocks, block_size; - - PORT_Assert (! cc->encrypt); - - block_size = cc->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 cc->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 = (cc->pending_count + input_len - 1) / block_size; - return blocks * block_size; -#endif -} - -/* - * SecCmsCipherContextEncryptLength - find the output length of the next call to encrypt. - * - * cc - the cipher context - * input_len - number of bytes used as input - * final - true if this is the final chunk of data - * - * 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. - */ -size_t -SecCmsCipherContextEncryptLength(SecCmsCipherContextRef cc, size_t input_len, Boolean final) -{ -#if 1 - return SecCmsCipherContextLength(cc, (unsigned int)input_len, final, PR_TRUE); -#else - int blocks, block_size; - int pad_size; - - PORT_Assert (cc->encrypt); - - block_size = cc->block_size; - pad_size = cc->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 cc->pending_count + input_len; - } else { - blocks = (cc->pending_count + input_len) / pad_size; - blocks++; - return blocks*pad_size; - } - } - - /* - * Now, count the number of complete blocks of data we have. - */ - blocks = (cc->pending_count + input_len) / block_size; - - - return blocks * block_size; -#endif -} - - -static OSStatus -SecCmsCipherContextCrypt(SecCmsCipherContextRef cc, unsigned char *output, - size_t *output_len_p, size_t max_output_len, - const unsigned char *input, size_t input_len, - Boolean final, Boolean encrypt) -{ - CSSM_DATA outputBuf = { max_output_len, output }; - CSSM_SIZE bytes_output = 0; - OSStatus rv = 0; - - if (input_len) - { - CSSM_DATA inputBuf = { input_len, (uint8 *)input }; - - if (encrypt) - rv = CSSM_EncryptDataUpdate(cc->cc, &inputBuf, 1, &outputBuf, 1, &bytes_output); - else - rv = CSSM_DecryptDataUpdate(cc->cc, &inputBuf, 1, &outputBuf, 1, &bytes_output); - } - - if (!rv && final) - { - CSSM_DATA remainderBuf = { max_output_len - bytes_output, output + bytes_output }; - if (encrypt) - rv = CSSM_EncryptDataFinal(cc->cc, &remainderBuf); - else - rv = CSSM_DecryptDataFinal(cc->cc, &remainderBuf); - - bytes_output += remainderBuf.Length; - } - - if (rv) - PORT_SetError(SEC_ERROR_BAD_DATA); - else if (output_len_p) - *output_len_p = bytes_output; - - return rv; -} - -/* - * SecCmsCipherContextDecrypt - do the decryption - * - * cc - the cipher context - * output - buffer for decrypted result bytes - * output_len_p - number of bytes in output - * max_output_len - upper bound on bytes to put into output - * input - pointer to input bytes - * input_len - number of input bytes - * final - true if this is the final chunk of data - * - * Decrypts 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". - * "cc" is the return value from SecCmsCipherStartDecrypt. - * 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 "cc". - * 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.) - */ -OSStatus -SecCmsCipherContextDecrypt(SecCmsCipherContextRef cc, unsigned char *output, - size_t *output_len_p, size_t max_output_len, - const unsigned char *input, size_t input_len, - Boolean final) -{ -#if 1 - return SecCmsCipherContextCrypt(cc, output, - output_len_p, max_output_len, - input, input_len, - final, PR_FALSE); -#else - int blocks, bsize, pcount, padsize; - unsigned int max_needed, ifraglen, ofraglen, output_len; - unsigned char *pbuf; - OSStatus rv; - - PORT_Assert (! cc->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 = SecCmsCipherContextDecryptLength(cc, input_len, final); - PORT_Assert (max_output_len >= max_needed); - if (max_output_len < max_needed) { - /* PORT_SetError (XXX); */ - return SECFailure; - } - - /* - * hardware encryption does not like small decryption sizes here, so we - * allow both blocking and padding. - */ - bsize = cc->block_size; - padsize = cc->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 (* cc->doit) (cc->cx, output, output_len_p, max_output_len, - input, input_len); - } - - pcount = cc->pending_count; - pbuf = cc->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) { - cc->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. - */ - if ((padsize != 0) && (pcount % padsize) != 0) { - PORT_Assert (final); - PORT_SetError (SEC_ERROR_BAD_DATA); - return SECFailure; - } - /* - * Decrypt the block. - */ - rv = (*cc->doit)(cc->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 SecCmsCipherContextDecryptLength 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); - cc->pending_count = pcount; - } - - if (ifraglen) { - rv = (* cc->doit)(cc->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); - - 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; -#endif -} - -/* - * SecCmsCipherContextEncrypt - do the encryption - * - * cc - the cipher context - * output - buffer for decrypted result bytes - * output_len_p - number of bytes in output - * max_output_len - upper bound on bytes to put into output - * input - pointer to input bytes - * input_len - number of input bytes - * final - true if this is the final chunk of data - * - * Encrypts 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". - * "cc" is the return value from SecCmsCipherStartEncrypt. - * 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 "cc". - * 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. - */ -OSStatus -SecCmsCipherContextEncrypt(SecCmsCipherContextRef cc, unsigned char *output, - size_t *output_len_p, size_t max_output_len, - const unsigned char *input, size_t input_len, - Boolean final) -{ -#if 1 - return SecCmsCipherContextCrypt(cc, output, - output_len_p, max_output_len, - input, input_len, - final, PR_TRUE); -#else - int blocks, bsize, padlen, pcount, padsize; - unsigned int max_needed, ifraglen, ofraglen, output_len; - unsigned char *pbuf; - OSStatus rv; - - PORT_Assert (cc->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 = SecCmsCipherContextEncryptLength (cc, input_len, final); - PORT_Assert (max_output_len >= max_needed); - if (max_output_len < max_needed) { - /* PORT_SetError (XXX); */ - return SECFailure; - } - - bsize = cc->block_size; - padsize = cc->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 (*cc->doit)(cc->cx, output, output_len_p, max_output_len, - input, input_len); - } - - pcount = cc->pending_count; - pbuf = cc->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) { - cc->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 = (* cc->doit) (cc->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 = (* cc->doit) (cc->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 = (* cc->doit) (cc->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 { - cc->pending_count = pcount; - } - - PORT_Assert (output_len_p != NULL || output_len == 0); - if (output_len_p != NULL) - *output_len_p = output_len; - - return SECSuccess; -#endif -}