#include <security_asn1/secport.h>
#include <Security/SecAsn1Templates.h>
+#if USE_CDSA_CRYPTO
+#include <Security/cssmapi.h>
+#include <Security/cssmapple.h>
+#include <Security/SecKeyPriv.h>
+#else
#include <Security/SecRandom.h>
#include <CommonCrypto/CommonCryptor.h>
+#endif
/*
* -------------------------------------------------------------------
{ 0 }
};
+// TODO: get rid of this?
+#if USE_CDSA_CRYPTO
+/*
+** 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(SecAsn1Item *it)
+{
+ long ival = 0;
+ unsigned 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(SecAsn1Item *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;
+}
+#endif /* USE_CDSA_CRYPTO */
+
/* default IV size in bytes */
#define DEFAULT_IV_SIZE 8
/* IV/block size for AES */
/* max IV size in bytes */
#define MAX_IV_SIZE AES_BLOCK_SIZE
+#if !USE_CDSA_CRYPTO
#ifndef kCCKeySizeMaxRC2
#define kCCKeySizeMaxRC2 16
#endif
#ifndef kCCBlockSizeRC2
#define kCCBlockSizeRC2 8
#endif
+#endif
static SecCmsCipherContextRef
SecCmsCipherContextStart(PRArenaPool *poolp, SecSymmetricKeyRef key, SECAlgorithmID *algid, Boolean encrypt)
OSStatus rv;
uint8_t ivbuf[MAX_IV_SIZE];
SecAsn1Item initVector = { DEFAULT_IV_SIZE, ivbuf };
+#if USE_CDSA_CRYPTO
+ CSSM_CC_HANDLE ciphercc = 0;
+ CSSM_ALGORITHMS algorithm;
+ CSSM_PADDING padding = CSSM_PADDING_PKCS7;
+ CSSM_ENCRYPT_MODE mode;
+ CSSM_CSP_HANDLE cspHandle;
+ const CSSM_KEY *cssmKey;
+ //CSSM_CONTEXT_ATTRIBUTE contextAttribute = { CSSM_ATTRIBUTE_ALG_PARAMS, sizeof(SecAsn1Item *) };
+#else
CCCryptorRef ciphercc = NULL;
CCOptions cipheroptions = kCCOptionPKCS7Padding;
int cipher_blocksize = 0;
+#endif
+
+#if USE_CDSA_CRYPTO
+ rv = SecKeyGetCSPHandle(key, &cspHandle);
+ if (rv)
+ goto loser;
+ rv = SecKeyGetCSSMKey(key, &cssmKey);
+ if (rv)
+ goto loser;
+#endif
+
// @@@ Add support for PBE based stuff
oidData = SECOID_FindOID(&algid->algorithm);
if (!oidData)
goto loser;
algtag = oidData->offset;
+#if USE_CDSA_CRYPTO
+ 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;
+ }
+#else
CCAlgorithm alg = -1;
switch (algtag) {
case SEC_OID_DES_CBC:
default:
goto loser;
}
+#endif
if (encrypt)
{
+#if USE_CDSA_CRYPTO
+ CSSM_CC_HANDLE randomcc;
+ //SecAsn1Item *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);
+#else
if (SecRandomCopyBytes(kSecRandomDefault,
initVector.Length, initVector.Data))
goto loser;
+#endif
// Put IV into algid.parameters
switch (algtag)
goto loser;
break;
case SEC_OID_RC2_CBC:
+#if USE_CDSA_CRYPTO
+ {
+ sec_rc2cbcParameter rc2 = {};
+ unsigned long rc2version;
+ SecAsn1Item *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;
+ }
+#endif
case SEC_OID_RC5_CBC_PAD:
default:
// @@@ Implement rc5 params stuff.
break;
}
case SEC_OID_RC2_CBC:
+#if USE_CDSA_CRYPTO
+ {
+ 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;
+ }
+#endif
case SEC_OID_RC5_CBC_PAD:
default:
// @@@ Implement rc5 params stuff.
}
}
+#if USE_CDSA_CRYPTO
+ 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;
+#else
if (CCCryptorCreate(encrypt ? kCCEncrypt : kCCDecrypt,
alg, cipheroptions, CFDataGetBytePtr(key), CFDataGetLength(key),
initVector.Data, &ciphercc))
goto loser;
+#endif
cc = (SecCmsCipherContextRef)PORT_ZAlloc(sizeof(SecCmsCipherContext));
if (cc == NULL)
cc->cc = ciphercc;
cc->encrypt = encrypt;
+#if !USE_CDSA_CRYPTO
cc->block_size =cipher_blocksize;
+#endif
return cc;
loser:
if (ciphercc)
+#if USE_CDSA_CRYPTO
+ CSSM_DeleteContext(ciphercc);
+#else
CCCryptorRelease(ciphercc);
+#endif
return NULL;
}
SecCmsCipherContextStartDecrypt(SecSymmetricKeyRef key, SECAlgorithmID *algid)
{
return SecCmsCipherContextStart(NULL, key, algid, PR_FALSE);
+#if 0
+ SecCmsCipherContextRef cc;
+ void *ciphercx;
+ CK_MECHANISM_TYPE mechanism;
+ SecAsn1Item * param;
+ PK11SlotInfo *slot;
+ SECOidTag algtag;
+
+ algtag = SECOID_GetAlgorithmTag(algid);
+
+ /* set param and mechanism */
+ if (SEC_PKCS5IsAlgorithmPBEAlg(algid)) {
+ CK_MECHANISM pbeMech, cryptoMech;
+ SecAsn1Item * 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 = (SecAsn1Item *)PORT_ZAlloc(sizeof(SecAsn1Item))) == 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(PRArenaPool *poolp, SecSymmetricKeyRef key, SECAlgorithmID *algid)
{
return SecCmsCipherContextStart(poolp, key, algid, PR_TRUE);
+#if 0
+ SecCmsCipherContextRef cc;
+ void *ciphercx;
+ SecAsn1Item * 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;
+ SecAsn1Item * 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 = (SecAsn1Item *)PORT_ZAlloc(sizeof(SecAsn1Item))) == 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
PORT_Assert(cc != NULL);
if (cc == NULL)
return;
-
+#if USE_CDSA_CRYPTO
+ CSSM_DeleteContext(cc->cc);
+#else
CCCryptorRelease(cc->cc);
-
+#endif
PORT_Free(cc);
}
static unsigned int
SecCmsCipherContextLength(SecCmsCipherContextRef cc, unsigned int input_len, Boolean final, Boolean encrypt)
{
+#if USE_CDSA_CRYPTO
+ 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;
+#else
return ((input_len + cc->block_size - 1) / cc->block_size * cc->block_size) + (final ? cc->block_size : 0);
+#endif
}
/*
unsigned int
SecCmsCipherContextDecryptLength(SecCmsCipherContextRef cc, unsigned int input_len, Boolean final)
{
+#if 1
return SecCmsCipherContextLength(cc, 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
}
/*
unsigned int
SecCmsCipherContextEncryptLength(SecCmsCipherContextRef cc, unsigned int input_len, Boolean final)
{
+#if 1
return SecCmsCipherContextLength(cc, 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
}
if (input_len)
{
+
+#if USE_CDSA_CRYPTO
+ SecAsn1Item inputBuf = { input_len, (uint8_t *)input };
+ SecAsn1Item outputBuf = { max_output_len, output };
+ 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);
+#else
rv = CCCryptorUpdate(cc->cc, input, input_len, output, max_output_len, &bytes_output);
+#endif
}
if (!rv && final)
{
+#if USE_CDSA_CRYPTO
+ SecAsn1Item 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;
+#else
size_t bytes_output_final = 0;
rv = CCCryptorFinal(cc->cc, output+bytes_output, max_output_len-bytes_output, &bytes_output_final);
bytes_output += bytes_output_final;
+#endif
}
-
if (rv)
PORT_SetError(SEC_ERROR_BAD_DATA);
else if (output_len_p)
const unsigned char *input, unsigned int 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
}
/*
const unsigned char *input, unsigned int 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
}