Back to index

lightning-sunbird  0.9+nobinonly
Typedefs | Functions | Variables
p7local.h File Reference
#include "secpkcs7.h"
#include "secasn1t.h"
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Typedefs

typedef struct sec_pkcs7_cipher_object

Functions

SEC_BEGIN_PROTOS
SEC_PKCS7Attribute * 
sec_PKCS7FindAttribute (SEC_PKCS7Attribute **attrs, SECOidTag oidtag, PRBool only)
SECItem * sec_PKCS7AttributeValue (SEC_PKCS7Attribute *attr)
SECItem * sec_PKCS7EncodeAttributes (PRArenaPool *poolp, SECItem *dest, void *src)
SECStatus sec_PKCS7ReorderAttributes (SEC_PKCS7Attribute **attrs)
sec_PKCS7CipherObject * sec_PKCS7CreateDecryptObject (PK11SymKey *key, SECAlgorithmID *algid)
sec_PKCS7CipherObject * sec_PKCS7CreateEncryptObject (PRArenaPool *poolp, PK11SymKey *key, SECOidTag algtag, SECAlgorithmID *algid)
void sec_PKCS7DestroyDecryptObject (sec_PKCS7CipherObject *obj)
void sec_PKCS7DestroyEncryptObject (sec_PKCS7CipherObject *obj)
unsigned int sec_PKCS7DecryptLength (sec_PKCS7CipherObject *obj, unsigned int input_len, PRBool final)
unsigned int sec_PKCS7EncryptLength (sec_PKCS7CipherObject *obj, unsigned int input_len, PRBool final)
SECStatus sec_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)
SECStatus sec_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)
const SEC_ASN1Templatesec_pkcs7_get_kea_template (SECKEATemplateSelector whichTemplate)

Variables

const SEC_ASN1Template sec_PKCS7ContentInfoTemplate []

Typedef Documentation

typedef struct sec_pkcs7_cipher_object

Definition at line 60 of file p7local.h.


Function Documentation

Definition at line 1391 of file p7local.c.

{
       const SEC_ASN1Template *returnVal = NULL;

       switch(whichTemplate)
       {
       case SECKEAUsesNonSkipjack:
              returnVal = SEC_SMIMEKEAParamTemplateNoSkipjack;
              break;
       case SECKEAUsesSkipjack:
              returnVal = SEC_SMIMEKEAParamTemplateSkipjack;
              break;
       case SECKEAUsesNonSkipjackWithPaddedEncKey:
       default:
              returnVal = SEC_SMIMEKEAParamTemplateAllParams;
              break;
       }
       return returnVal;
}
SECItem* sec_PKCS7AttributeValue ( SEC_PKCS7Attribute *  attr)

Definition at line 885 of file p7local.c.

{
    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;
}
sec_PKCS7CipherObject* sec_PKCS7CreateDecryptObject ( PK11SymKey *  key,
SECAlgorithmID *  algid 
)

Definition at line 101 of file p7local.c.

{
    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;

       pwitem = (SECItem *)PK11_GetSymKeyUserData(key);
       if (!pwitem) {
           PORT_Free(result);
           return NULL;
       }

       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;
}
sec_PKCS7CipherObject* sec_PKCS7CreateEncryptObject ( PRArenaPool poolp,
PK11SymKey *  key,
SECOidTag  algtag,
SECAlgorithmID *  algid 
)

Definition at line 196 of file p7local.c.

{
    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, *pwitem;

       PORT_Memset(&pbeMech, 0, sizeof(CK_MECHANISM));
       PORT_Memset(&cryptoMech, 0, sizeof(CK_MECHANISM));

       pwitem = (SECItem *)PK11_GetSymKeyUserData(key);
       if (!pwitem) {
           PORT_Free(result);
           return NULL;
       }

       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_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) {
           PORT_Free (result);
            SECITEM_FreeItem(param,PR_TRUE);
           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;
}
SECStatus sec_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 
)

Definition at line 471 of file p7local.c.

{
    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;
    }

    /*
     * 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;
}
unsigned int sec_PKCS7DecryptLength ( sec_PKCS7CipherObject *  obj,
unsigned int  input_len,
PRBool  final 
)

Definition at line 352 of file p7local.c.

{
    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;
}
void sec_PKCS7DestroyDecryptObject ( sec_PKCS7CipherObject *  obj)

Definition at line 309 of file p7local.c.

{
    PORT_Assert (obj != NULL);
    if (obj == NULL)
       return;
    PORT_Assert (! obj->encrypt);
    sec_pkcs7_destroy_cipher (obj);
}
void sec_PKCS7DestroyEncryptObject ( sec_PKCS7CipherObject *  obj)

Definition at line 319 of file p7local.c.

{
    PORT_Assert (obj != NULL);
    if (obj == NULL)
       return;
    PORT_Assert (obj->encrypt);
    sec_pkcs7_destroy_cipher (obj);
}
SECItem* sec_PKCS7EncodeAttributes ( PRArenaPool poolp,
SECItem *  dest,
void src 
)

Definition at line 1003 of file p7local.c.

SECStatus sec_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 
)

Definition at line 673 of file p7local.c.

{
    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;
}
unsigned int sec_PKCS7EncryptLength ( sec_PKCS7CipherObject *  obj,
unsigned int  input_len,
PRBool  final 
)

Definition at line 406 of file p7local.c.

{
    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;
}
SEC_BEGIN_PROTOS SEC_PKCS7Attribute* sec_PKCS7FindAttribute ( SEC_PKCS7Attribute **  attrs,
SECOidTag  oidtag,
PRBool  only 
)

Definition at line 839 of file p7local.c.

{
    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;
}
SECStatus sec_PKCS7ReorderAttributes ( SEC_PKCS7Attribute **  attrs)

Definition at line 1015 of file p7local.c.

{
    PRArenaPool *poolp;
    int num_attrs, i, pass, besti;
    unsigned int j;
    SECItem **enc_attrs;
    SEC_PKCS7Attribute **new_attrs;

    /*
     * I think we should not be called with NULL.  But if we are,
     * call it a success anyway, because the order *is* okay.
     */
    PORT_Assert (attrs != NULL);
    if (attrs == NULL)
       return SECSuccess;

    /*
     * Count how many attributes we are dealing with here.
     */
    num_attrs = 0;
    while (attrs[num_attrs] != NULL)
       num_attrs++;

    /*
     * Again, I think we should have some attributes here.
     * But if we do not, or if there is only one, then call it
     * a success because it also already has a fine order.
     */
    PORT_Assert (num_attrs);
    if (num_attrs == 0 || num_attrs == 1)
       return SECSuccess;

    /*
     * Allocate an arena for us to work with, so it is easy to
     * clean up all of the memory (fairly small pieces, really).
     */
    poolp = PORT_NewArena (1024);  /* XXX what is right value? */
    if (poolp == NULL)
       return SECFailure;          /* no memory; nothing we can do... */

    /*
     * Allocate arrays to hold the individual encodings which we will use
     * for comparisons and the reordered attributes as they are sorted.
     */
    enc_attrs=(SECItem**)PORT_ArenaZAlloc(poolp, num_attrs*sizeof(SECItem *));
    new_attrs = (SEC_PKCS7Attribute**)PORT_ArenaZAlloc (poolp,
                              num_attrs * sizeof(SEC_PKCS7Attribute *));
    if (enc_attrs == NULL || new_attrs == NULL) {
       PORT_FreeArena (poolp, PR_FALSE);
       return SECFailure;
    }

    /*
     * DER encode each individual attribute.
     */
    for (i = 0; i < num_attrs; i++) {
       enc_attrs[i] = SEC_ASN1EncodeItem (poolp, NULL, attrs[i],
                                      sec_pkcs7_attribute_template);
       if (enc_attrs[i] == NULL) {
           PORT_FreeArena (poolp, PR_FALSE);
           return SECFailure;
       }
    }

    /*
     * Now compare and sort them; this is not the most efficient sorting
     * method, but it is just fine for the problem at hand, because the
     * number of attributes is (always) going to be small.
     */
    for (pass = 0; pass < num_attrs; pass++) {
       /*
        * Find the first not-yet-accepted attribute.  (Once one is
        * sorted into the other array, it is cleared from enc_attrs.)
        */
       for (i = 0; i < num_attrs; i++) {
           if (enc_attrs[i] != NULL)
              break;
       }
       PORT_Assert (i < num_attrs);
       besti = i;

       /*
        * Find the lowest (lexigraphically) encoding.  One that is
        * shorter than all the rest is known to be "less" because each
        * attribute is of the same type (a SEQUENCE) and so thus the
        * first octet of each is the same, and the second octet is
        * the length (or the length of the length with the high bit
        * set, followed by the length, which also works out to always
        * order the shorter first).  Two (or more) that have the
        * same length need to be compared byte by byte until a mismatch
        * is found.
        */
       for (i = besti + 1; i < num_attrs; i++) {
           if (enc_attrs[i] == NULL)      /* slot already handled */
              continue;

           if (enc_attrs[i]->len != enc_attrs[besti]->len) {
              if (enc_attrs[i]->len < enc_attrs[besti]->len)
                  besti = i;
              continue;
           }

           for (j = 0; j < enc_attrs[i]->len; j++) {
              if (enc_attrs[i]->data[j] < enc_attrs[besti]->data[j]) {
                  besti = i;
                  break;
              }
           }

           /*
            * For this not to be true, we would have to have encountered     
            * two *identical* attributes, which I think we should not see.
            * So assert if it happens, but even if it does, let it go
            * through; the ordering of the two does not matter.
            */
           PORT_Assert (j < enc_attrs[i]->len);
       }

       /*
        * Now we have found the next-lowest one; copy it over and
        * remove it from enc_attrs.
        */
       new_attrs[pass] = attrs[besti];
       enc_attrs[besti] = NULL;
    }

    /*
     * Now new_attrs has the attributes in the order we want;
     * copy them back into the attrs array we started with.
     */
    for (i = 0; i < num_attrs; i++)
       attrs[i] = new_attrs[i];

    PORT_FreeArena (poolp, PR_FALSE);
    return SECSuccess;
}

Variable Documentation

Definition at line 1169 of file p7local.c.