#include "cmslocal.h"
#include "secoid.h"
#include <security_asn1/secerr.h>
#include <security_asn1/secasn1.h>
#include <Security/SecAsn1Templates.h>
#include <Security/cssmapi.h>
#include <Security/cssmapple.h>
#include <Security/SecKeyPriv.h>
#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;
Boolean encrypt;
#else
void * cx;
nss_cms_cipher_function doit;
nss_cms_cipher_destroy destroy;
Boolean encrypt;
int block_size;
int pad_size;
int pending_count;
unsigned char pending_buf[BLOCK_SIZE];
#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 }
};
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;
}
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;
}
#define DEFAULT_IV_SIZE 8
#define AES_BLOCK_SIZE 16
#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 };
rv = SecKeyGetCSPHandle(key, &cspHandle);
if (rv)
goto loser;
rv = SecKeyGetCSSMKey(key, &cssmKey);
if (rv)
goto loser;
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;
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;
if (CSSM_CSP_CreateRandomGenContext(cspHandle,
CSSM_ALGID_APPLE_YARROW,
NULL,
initVector.Length,
&randomcc))
goto loser;
if (CSSM_GenerateRandom(randomcc, &initVector))
goto loser;
CSSM_DeleteContext(randomcc);
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:
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);
rc2.rc2ParameterVersion.Data = NULL;
if (newParams == NULL)
goto loser;
break;
}
case SEC_OID_RC5_CBC_PAD:
default:
goto loser;
break;
}
}
else
{
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 = {};
rv = SEC_ASN1DecodeItem(NULL, &iv, kSecAsn1OctetStringTemplate, &(algid->parameters));
if (rv)
goto loser;
if (initVector.Length != iv.Length) {
PORT_Free(iv.Data);
iv.Data = NULL;
goto loser;
}
memcpy(initVector.Data, iv.Data, initVector.Length);
PORT_Free(iv.Data);
iv.Data = NULL;
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);
rc2.iv.Data = NULL;
PORT_Free(rc2.rc2ParameterVersion.Data);
rc2.rc2ParameterVersion.Data = NULL;
goto loser;
}
memcpy(initVector.Data, rc2.iv.Data, initVector.Length);
PORT_Free(rc2.iv.Data);
rc2.iv.Data = NULL;
ulEffectiveBits = rc2_map(&rc2.rc2ParameterVersion);
PORT_Free(rc2.rc2ParameterVersion.Data);
rc2.rc2ParameterVersion.Data = NULL;
if (ulEffectiveBits != cssmKey->KeyHeader.LogicalKeySizeInBits)
goto loser;
break;
}
case SEC_OID_RC5_CBC_PAD:
default:
goto loser;
break;
}
}
if (CSSM_CSP_CreateSymmetricContext(cspHandle,
algorithm,
mode,
NULL,
cssmKey,
&initVector,
padding,
NULL,
&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;
}
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);
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));
keyPwd = (SEC_PKCS5KeyAndPassword *)key;
key = keyPwd->key;
pbeMech.mechanism = PK11_AlgtagToMechanism(algtag);
pbeParams = PK11_ParamFromAlgid(algid);
if (!pbeParams)
return NULL;
pbeMech.pParameter = pbeParams->Data;
pbeMech.ulParameterLen = pbeParams->Length;
if (PK11_MapPBEMechanismToCryptoMechanism(&pbeMech, &cryptoMech, keyPwd->pwitem,
PR_FALSE) != CKR_OK) {
SECITEM_ZfreeItem(pbeParams, PR_TRUE);
return NULL;
}
SECITEM_ZfreeItem(pbeParams, PR_TRUE);
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;
}
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);
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
}
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);
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));
keyPwd = (SEC_PKCS5KeyAndPassword *)key;
key = keyPwd->key;
pbeMech.mechanism = PK11_AlgtagToMechanism(algtag);
pbeParams = PK11_ParamFromAlgid(algid);
if (!pbeParams)
return NULL;
pbeMech.pParameter = pbeParams->Data;
pbeMech.ulParameterLen = pbeParams->Length;
if (PK11_MapPBEMechanismToCryptoMechanism(&pbeMech, &cryptoMech, keyPwd->pwitem,
PR_FALSE) != CKR_OK) {
SECITEM_ZfreeItem(pbeParams, PR_TRUE);
return NULL;
}
SECITEM_ZfreeItem(pbeParams, PR_TRUE);
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;
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);
ciphercx = PK11_CreateContextBySymKey(mechanism, CKA_ENCRYPT, key, param);
if (ciphercx == NULL) {
PORT_Free(cc);
cc = NULL;
goto loser;
}
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 } };
OSStatus rv = CSSM_QuerySize(cc->cc, cc->encrypt, final ? 1 : 2, dataBlockSize);
if (rv)
{
PORT_SetError(rv);
return 0;
}
return dataBlockSize[0].SizeOutputBlock;
}
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 (block_size == 0)
return input_len;
if (final)
return cc->pending_count + input_len;
blocks = (cc->pending_count + input_len - 1) / block_size;
return blocks * block_size;
#endif
}
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 (block_size == 0)
return input_len;
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;
}
}
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;
}
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);
max_needed = SecCmsCipherContextDecryptLength(cc, input_len, final);
PORT_Assert (max_output_len >= max_needed);
if (max_output_len < max_needed) {
return SECFailure;
}
bsize = cc->block_size;
padsize = cc->pad_size;
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) {
while (input_len && pcount < bsize) {
pbuf[pcount++] = *input++;
input_len--;
}
if (input_len == 0 && !final) {
cc->pending_count = pcount;
if (output_len_p)
*output_len_p = 0;
return SECSuccess;
}
if ((padsize != 0) && (pcount % padsize) != 0) {
PORT_Assert (final);
PORT_SetError (SEC_ERROR_BAD_DATA);
return SECFailure;
}
rv = (*cc->doit)(cc->cx, output, &ofraglen, max_output_len,
pbuf, pcount);
if (rv != SECSuccess)
return rv;
PORT_Assert(ofraglen == pcount);
max_output_len -= ofraglen;
output_len += ofraglen;
output += ofraglen;
}
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;
PORT_Assert (ifraglen == ofraglen);
if (ifraglen != ofraglen) {
PORT_SetError(SEC_ERROR_BAD_DATA);
return SECFailure;
}
output_len += ofraglen;
} else {
ofraglen = 0;
}
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
}
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);
max_needed = SecCmsCipherContextEncryptLength (cc, input_len, final);
PORT_Assert (max_output_len >= max_needed);
if (max_output_len < max_needed) {
return SECFailure;
}
bsize = cc->block_size;
padsize = cc->pad_size;
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) {
while (input_len && pcount < bsize) {
pbuf[pcount++] = *input++;
input_len--;
}
if (pcount < bsize && !final) {
cc->pending_count = pcount;
if (output_len_p != NULL)
*output_len_p = 0;
return SECSuccess;
}
if ((padsize == 0) || (pcount % padsize) == 0) {
rv = (* cc->doit) (cc->cx, output, &ofraglen, max_output_len,
pbuf, pcount);
if (rv != SECSuccess)
return rv;
PORT_Assert (ofraglen == pcount);
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;
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;
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
}