security/nss/lib/pkcs7/p7common.c

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 /*
     6  * PKCS7 implementation -- the exported parts that are used whether
     7  * creating or decoding.
     8  */
    10 #include "p7local.h"
    12 #include "cert.h"
    13 #include "secitem.h"
    14 #include "secoid.h"
    15 #include "pk11func.h"
    17 /*
    18  * Find out (saving pointer to lookup result for future reference)
    19  * and return the inner content type.
    20  */
    21 SECOidTag
    22 SEC_PKCS7ContentType (SEC_PKCS7ContentInfo *cinfo)
    23 {
    24     if (cinfo->contentTypeTag == NULL)
    25 	cinfo->contentTypeTag = SECOID_FindOID(&(cinfo->contentType));
    27     if (cinfo->contentTypeTag == NULL)
    28 	return SEC_OID_UNKNOWN;
    30     return cinfo->contentTypeTag->offset;
    31 }
    34 /*
    35  * Destroy a PKCS7 contentInfo and all of its sub-pieces.
    36  */
    37 void
    38 SEC_PKCS7DestroyContentInfo(SEC_PKCS7ContentInfo *cinfo)
    39 {
    40     SECOidTag kind;
    41     CERTCertificate **certs;
    42     CERTCertificateList **certlists;
    43     SEC_PKCS7SignerInfo **signerinfos;
    44     SEC_PKCS7RecipientInfo **recipientinfos;
    46     PORT_Assert (cinfo->refCount > 0);
    47     if (cinfo->refCount <= 0)
    48 	return;
    50     cinfo->refCount--;
    51     if (cinfo->refCount > 0)
    52 	return;
    54     certs = NULL;
    55     certlists = NULL;
    56     recipientinfos = NULL;
    57     signerinfos = NULL;
    59     kind = SEC_PKCS7ContentType (cinfo);
    60     switch (kind) {
    61       case SEC_OID_PKCS7_ENVELOPED_DATA:
    62 	{
    63 	    SEC_PKCS7EnvelopedData *edp;
    65 	    edp = cinfo->content.envelopedData;
    66 	    if (edp != NULL) {
    67 		recipientinfos = edp->recipientInfos;
    68 	    }
    69 	}
    70 	break;
    71       case SEC_OID_PKCS7_SIGNED_DATA:
    72 	{
    73 	    SEC_PKCS7SignedData *sdp;
    75 	    sdp = cinfo->content.signedData;
    76 	    if (sdp != NULL) {
    77 		certs = sdp->certs;
    78 		certlists = sdp->certLists;
    79 		signerinfos = sdp->signerInfos;
    80 	    }
    81 	}
    82 	break;
    83       case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
    84 	{
    85 	    SEC_PKCS7SignedAndEnvelopedData *saedp;
    87 	    saedp = cinfo->content.signedAndEnvelopedData;
    88 	    if (saedp != NULL) {
    89 		certs = saedp->certs;
    90 		certlists = saedp->certLists;
    91 		recipientinfos = saedp->recipientInfos;
    92 		signerinfos = saedp->signerInfos;
    93 		if (saedp->sigKey != NULL)
    94 		    PK11_FreeSymKey (saedp->sigKey);
    95 	    }
    96 	}
    97 	break;
    98       default:
    99 	/* XXX Anything else that needs to be "manually" freed/destroyed? */
   100 	break;
   101     }
   103     if (certs != NULL) {
   104 	CERTCertificate *cert;
   106 	while ((cert = *certs++) != NULL) {
   107 	    CERT_DestroyCertificate (cert);
   108 	}
   109     }
   111     if (certlists != NULL) {
   112 	CERTCertificateList *certlist;
   114 	while ((certlist = *certlists++) != NULL) {
   115 	    CERT_DestroyCertificateList (certlist);
   116 	}
   117     }
   119     if (recipientinfos != NULL) {
   120 	SEC_PKCS7RecipientInfo *ri;
   122 	while ((ri = *recipientinfos++) != NULL) {
   123 	    if (ri->cert != NULL)
   124 		CERT_DestroyCertificate (ri->cert);
   125 	}
   126     }
   128     if (signerinfos != NULL) {
   129 	SEC_PKCS7SignerInfo *si;
   131 	while ((si = *signerinfos++) != NULL) {
   132 	    if (si->cert != NULL)
   133 		CERT_DestroyCertificate (si->cert);
   134 	    if (si->certList != NULL)
   135 		CERT_DestroyCertificateList (si->certList);
   136 	}
   137     }
   139     if (cinfo->poolp != NULL) {
   140 	PORT_FreeArena (cinfo->poolp, PR_FALSE);	/* XXX clear it? */
   141     }
   142 }
   145 /*
   146  * Return a copy of the given contentInfo.  The copy may be virtual
   147  * or may be real -- either way, the result needs to be passed to
   148  * SEC_PKCS7DestroyContentInfo later (as does the original).
   149  */
   150 SEC_PKCS7ContentInfo *
   151 SEC_PKCS7CopyContentInfo(SEC_PKCS7ContentInfo *cinfo)
   152 {
   153     if (cinfo == NULL)
   154 	return NULL;
   156     PORT_Assert (cinfo->refCount > 0);
   158     if (cinfo->created) {
   159 	/*
   160 	 * Want to do a real copy of these; otherwise subsequent
   161 	 * changes made to either copy are likely to be a surprise.
   162 	 * XXX I suspect that this will not actually be called for yet,
   163 	 * which is why the assert, so to notice if it is...
   164 	 */
   165 	PORT_Assert (0);
   166 	/*
   167 	 * XXX Create a new pool here, and copy everything from
   168 	 * within.  For cert stuff, need to call the appropriate
   169 	 * copy functions, etc.
   170 	 */
   171     }
   173     cinfo->refCount++;
   174     return cinfo;
   175 }
   178 /*
   179  * Return a pointer to the actual content.  In the case of those types
   180  * which are encrypted, this returns the *plain* content.
   181  * XXX Needs revisiting if/when we handle nested encrypted types.
   182  */
   183 SECItem *
   184 SEC_PKCS7GetContent(SEC_PKCS7ContentInfo *cinfo)
   185 {
   186     SECOidTag kind;
   188     kind = SEC_PKCS7ContentType (cinfo);
   189     switch (kind) {
   190       case SEC_OID_PKCS7_DATA:
   191 	return cinfo->content.data;
   192       case SEC_OID_PKCS7_DIGESTED_DATA:
   193 	{
   194 	    SEC_PKCS7DigestedData *digd;
   196 	    digd = cinfo->content.digestedData;
   197 	    if (digd == NULL)
   198 		break;
   199 	    return SEC_PKCS7GetContent (&(digd->contentInfo));
   200 	}
   201       case SEC_OID_PKCS7_ENCRYPTED_DATA:
   202 	{
   203 	    SEC_PKCS7EncryptedData *encd;
   205 	    encd = cinfo->content.encryptedData;
   206 	    if (encd == NULL)
   207 		break;
   208 	    return &(encd->encContentInfo.plainContent);
   209 	}
   210       case SEC_OID_PKCS7_ENVELOPED_DATA:
   211 	{
   212 	    SEC_PKCS7EnvelopedData *envd;
   214 	    envd = cinfo->content.envelopedData;
   215 	    if (envd == NULL)
   216 		break;
   217 	    return &(envd->encContentInfo.plainContent);
   218 	}
   219       case SEC_OID_PKCS7_SIGNED_DATA:
   220 	{
   221 	    SEC_PKCS7SignedData *sigd;
   223 	    sigd = cinfo->content.signedData;
   224 	    if (sigd == NULL)
   225 		break;
   226 	    return SEC_PKCS7GetContent (&(sigd->contentInfo));
   227 	}
   228       case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
   229 	{
   230 	    SEC_PKCS7SignedAndEnvelopedData *saed;
   232 	    saed = cinfo->content.signedAndEnvelopedData;
   233 	    if (saed == NULL)
   234 		break;
   235 	    return &(saed->encContentInfo.plainContent);
   236 	}
   237       default:
   238 	PORT_Assert(0);
   239 	break;
   240     }
   242     return NULL;
   243 }
   246 /*
   247  * XXX Fix the placement and formatting of the
   248  * following routines (i.e. make them consistent with the rest of
   249  * the pkcs7 code -- I think some/many belong in other files and
   250  * they all need a formatting/style rehaul)
   251  */
   253 /* retrieve the algorithm identifier for encrypted data.  
   254  * the identifier returned is a copy of the algorithm identifier
   255  * in the content info and needs to be freed after being used.
   256  *
   257  *   cinfo is the content info for which to retrieve the
   258  *     encryption algorithm.
   259  *
   260  * if the content info is not encrypted data or an error 
   261  * occurs NULL is returned.
   262  */
   263 SECAlgorithmID *
   264 SEC_PKCS7GetEncryptionAlgorithm(SEC_PKCS7ContentInfo *cinfo)
   265 {
   266   SECAlgorithmID *alg = 0;
   267   switch (SEC_PKCS7ContentType(cinfo))
   268     {
   269     case SEC_OID_PKCS7_ENCRYPTED_DATA:
   270       alg = &cinfo->content.encryptedData->encContentInfo.contentEncAlg;
   271       break;
   272     case SEC_OID_PKCS7_ENVELOPED_DATA:
   273       alg = &cinfo->content.envelopedData->encContentInfo.contentEncAlg;
   274       break;
   275     case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
   276       alg = &cinfo->content.signedAndEnvelopedData
   277 	->encContentInfo.contentEncAlg;
   278       break;
   279     default:
   280       alg = 0;
   281       break;
   282     }
   284     return alg;
   285 }
   287 /* set the content of the content info.  For data content infos,
   288  * the data is set.  For encrytped content infos, the plainContent
   289  * is set, and is expected to be encrypted later.
   290  *  
   291  * cinfo is the content info where the data will be set
   292  *
   293  * buf is a buffer of the data to set
   294  *
   295  * len is the length of the data being set.
   296  *
   297  * in the event of an error, SECFailure is returned.  SECSuccess 
   298  * indicates the content was successfully set.
   299  */
   300 SECStatus 
   301 SEC_PKCS7SetContent(SEC_PKCS7ContentInfo *cinfo,
   302 		    const char *buf, 
   303 		    unsigned long len)
   304 {
   305     SECOidTag cinfo_type;
   306     SECStatus rv;
   307     SECItem content;
   308     SECOidData *contentTypeTag = NULL;
   310     content.type = siBuffer;
   311     content.data = (unsigned char *)buf;
   312     content.len = len;
   314     cinfo_type = SEC_PKCS7ContentType(cinfo);
   316     /* set inner content */
   317     switch(cinfo_type)
   318     {
   319 	case SEC_OID_PKCS7_SIGNED_DATA:
   320 	    if(content.len > 0) {
   321 		/* we "leak" the old content here, but as it's all in the pool */
   322 		/* it does not really matter */
   324 		/* create content item if necessary */
   325 		if (cinfo->content.signedData->contentInfo.content.data == NULL)
   326 		    cinfo->content.signedData->contentInfo.content.data = SECITEM_AllocItem(cinfo->poolp, NULL, 0);
   327 		rv = SECITEM_CopyItem(cinfo->poolp, 
   328 			cinfo->content.signedData->contentInfo.content.data,
   329 			&content);
   330 	    } else {
   331 		cinfo->content.signedData->contentInfo.content.data->data = NULL;
   332 		cinfo->content.signedData->contentInfo.content.data->len = 0;
   333 		rv = SECSuccess;
   334 	    }
   335 	    if(rv == SECFailure)
   336 		goto loser;
   338 	    break;
   339 	case SEC_OID_PKCS7_ENCRYPTED_DATA:
   340 	    /* XXX this forces the inner content type to be "data" */
   341 	    /* do we really want to override without asking or reason? */
   342 	    contentTypeTag = SECOID_FindOIDByTag(SEC_OID_PKCS7_DATA);
   343 	    if(contentTypeTag == NULL)
   344 		goto loser;
   345 	    rv = SECITEM_CopyItem(cinfo->poolp, 
   346 		&(cinfo->content.encryptedData->encContentInfo.contentType),
   347 		&(contentTypeTag->oid));
   348 	    if(rv == SECFailure)
   349 		goto loser;
   350 	    if(content.len > 0) {
   351 		rv = SECITEM_CopyItem(cinfo->poolp, 
   352 			&(cinfo->content.encryptedData->encContentInfo.plainContent),
   353 			&content);
   354 	    } else {
   355 		cinfo->content.encryptedData->encContentInfo.plainContent.data = NULL;
   356 		cinfo->content.encryptedData->encContentInfo.encContent.data = NULL;
   357 		cinfo->content.encryptedData->encContentInfo.plainContent.len = 0;
   358 		cinfo->content.encryptedData->encContentInfo.encContent.len = 0;
   359 		rv = SECSuccess;
   360 	    }
   361 	    if(rv == SECFailure)
   362 		goto loser;
   363 	    break;
   364 	case SEC_OID_PKCS7_DATA:
   365 	    cinfo->content.data = (SECItem *)PORT_ArenaZAlloc(cinfo->poolp,
   366 		sizeof(SECItem));
   367 	    if(cinfo->content.data == NULL)
   368 		goto loser;
   369 	    if(content.len > 0) {
   370 		rv = SECITEM_CopyItem(cinfo->poolp,
   371 			cinfo->content.data, &content);
   372 	    } else {
   373 	    	/* handle case with NULL content */
   374 		rv = SECSuccess;
   375 	    }
   376 	    if(rv == SECFailure)
   377 		goto loser;
   378 	    break;
   379 	default:
   380 	    goto loser;
   381     }
   383     return SECSuccess;
   385 loser:
   387     return SECFailure;
   388 }
   390 /* the content of an encrypted data content info is encrypted.
   391  * it is assumed that for encrypted data, that the data has already
   392  * been set and is in the "plainContent" field of the content info.
   393  *
   394  * cinfo is the content info to encrypt
   395  *
   396  * key is the key with which to perform the encryption.  if the
   397  *     algorithm is a password based encryption algorithm, the
   398  *     key is actually a password which will be processed per
   399  *     PKCS #5.
   400  * 
   401  * in the event of an error, SECFailure is returned.  SECSuccess
   402  * indicates a success.
   403  */
   404 SECStatus 
   405 SEC_PKCS7EncryptContents(PLArenaPool *poolp,
   406 			 SEC_PKCS7ContentInfo *cinfo,
   407 			 SECItem *key,
   408 			 void *wincx)
   409 {
   410     SECAlgorithmID *algid 	= NULL;
   411     SECItem *       result 	= NULL;
   412     SECItem *       src;
   413     SECItem *       dest;
   414     SECItem *       blocked_data = NULL;
   415     void *          mark;
   416     void *          cx;
   417     PK11SymKey *    eKey 	= NULL;
   418     PK11SlotInfo *  slot 	= NULL;
   420     CK_MECHANISM_TYPE cryptoMechType;
   421     int             bs;
   422     SECStatus       rv 		= SECFailure;
   423     SECItem         *c_param = NULL;
   425     if((cinfo == NULL) || (key == NULL))
   426 	return SECFailure;
   428     if(SEC_PKCS7ContentType(cinfo) != SEC_OID_PKCS7_ENCRYPTED_DATA)
   429 	return SECFailure;
   431     algid = SEC_PKCS7GetEncryptionAlgorithm(cinfo);	
   432     if(algid == NULL)
   433 	return SECFailure;
   435     if(poolp == NULL)
   436 	poolp = cinfo->poolp;
   438     mark = PORT_ArenaMark(poolp);
   440     src = &cinfo->content.encryptedData->encContentInfo.plainContent;
   441     dest = &cinfo->content.encryptedData->encContentInfo.encContent;
   442     dest->data = (unsigned char*)PORT_ArenaZAlloc(poolp, (src->len + 64));
   443     dest->len = (src->len + 64);
   444     if(dest->data == NULL) {
   445 	rv = SECFailure;
   446 	goto loser;
   447     }
   449     slot = PK11_GetInternalKeySlot();
   450     if(slot == NULL) {
   451 	rv = SECFailure;
   452 	goto loser;
   453     }
   455     eKey = PK11_PBEKeyGen(slot, algid, key, PR_FALSE, wincx);
   456     if(eKey == NULL) {
   457 	rv = SECFailure;
   458 	goto loser;
   459     }
   461     cryptoMechType = PK11_GetPBECryptoMechanism(algid, &c_param, key);
   462     if (cryptoMechType == CKM_INVALID_MECHANISM) {
   463 	rv = SECFailure;
   464 	goto loser;
   465     }
   467     /* block according to PKCS 8 */
   468     bs = PK11_GetBlockSize(cryptoMechType, c_param);
   469     rv = SECSuccess;
   470     if(bs) {
   471 	char pad_char;
   472 	pad_char = (char)(bs - (src->len % bs));
   473 	if(src->len % bs) {
   474 	    rv = SECSuccess;
   475 	    blocked_data = PK11_BlockData(src, bs);
   476 	    if(blocked_data) {
   477 		PORT_Memset((blocked_data->data + blocked_data->len 
   478 			    - (int)pad_char), 
   479 			    pad_char, (int)pad_char);
   480 	    } else {
   481 		rv = SECFailure;
   482 		goto loser;
   483 	    }
   484 	} else {
   485 	    blocked_data = SECITEM_DupItem(src);
   486 	    if(blocked_data) {
   487 		blocked_data->data = (unsigned char*)PORT_Realloc(
   488 						  blocked_data->data,
   489 						  blocked_data->len + bs);
   490 		if(blocked_data->data) {
   491 		    blocked_data->len += bs;
   492 		    PORT_Memset((blocked_data->data + src->len), (char)bs, bs);
   493 		} else {
   494 		    rv = SECFailure;
   495 		    goto loser;
   496 		}
   497 	    } else {
   498 		rv = SECFailure;
   499 		goto loser;
   500 	    }
   501 	 }
   502     } else {
   503 	blocked_data = SECITEM_DupItem(src);
   504 	if(!blocked_data) {
   505 	    rv = SECFailure;
   506 	    goto loser;
   507 	}
   508     }
   510     cx = PK11_CreateContextBySymKey(cryptoMechType, CKA_ENCRYPT,
   511 		    		    eKey, c_param);
   512     if(cx == NULL) {
   513 	rv = SECFailure;
   514 	goto loser;
   515     }
   517     rv = PK11_CipherOp((PK11Context*)cx, dest->data, (int *)(&dest->len), 
   518 		       (int)(src->len + 64), blocked_data->data, 
   519 		       (int)blocked_data->len);
   520     PK11_DestroyContext((PK11Context*)cx, PR_TRUE);
   522 loser:
   523     /* let success fall through */
   524     if(blocked_data != NULL)
   525 	SECITEM_ZfreeItem(blocked_data, PR_TRUE);
   527     if(result != NULL)
   528 	SECITEM_ZfreeItem(result, PR_TRUE);
   530     if(rv == SECFailure)
   531 	PORT_ArenaRelease(poolp, mark);
   532     else 
   533 	PORT_ArenaUnmark(poolp, mark);
   535     if(eKey != NULL)
   536 	PK11_FreeSymKey(eKey);
   538     if(slot != NULL)
   539 	PK11_FreeSlot(slot);
   541     if(c_param != NULL) 
   542 	SECITEM_ZfreeItem(c_param, PR_TRUE);
   544     return rv;
   545 }
   547 /* the content of an encrypted data content info is decrypted.
   548  * it is assumed that for encrypted data, that the data has already
   549  * been set and is in the "encContent" field of the content info.
   550  *
   551  * cinfo is the content info to decrypt
   552  *
   553  * key is the key with which to perform the decryption.  if the
   554  *     algorithm is a password based encryption algorithm, the
   555  *     key is actually a password which will be processed per
   556  *     PKCS #5.
   557  * 
   558  * in the event of an error, SECFailure is returned.  SECSuccess
   559  * indicates a success.
   560  */
   561 SECStatus 
   562 SEC_PKCS7DecryptContents(PLArenaPool *poolp,
   563 			 SEC_PKCS7ContentInfo *cinfo,
   564 			 SECItem *key,
   565 			 void *wincx)
   566 {
   567     SECAlgorithmID *algid = NULL;
   568     SECStatus rv = SECFailure;
   569     SECItem *result = NULL, *dest, *src;
   570     void *mark;
   572     PK11SymKey *eKey = NULL;
   573     PK11SlotInfo *slot = NULL;
   574     CK_MECHANISM_TYPE cryptoMechType;
   575     void *cx;
   576     SECItem *c_param = NULL;
   577     int bs;
   579     if((cinfo == NULL) || (key == NULL))
   580 	return SECFailure;
   582     if(SEC_PKCS7ContentType(cinfo) != SEC_OID_PKCS7_ENCRYPTED_DATA)
   583 	return SECFailure;
   585     algid = SEC_PKCS7GetEncryptionAlgorithm(cinfo);	
   586     if(algid == NULL)
   587 	return SECFailure;
   589     if(poolp == NULL)
   590 	poolp = cinfo->poolp;
   592     mark = PORT_ArenaMark(poolp);
   594     src = &cinfo->content.encryptedData->encContentInfo.encContent;
   595     dest = &cinfo->content.encryptedData->encContentInfo.plainContent;
   596     dest->data = (unsigned char*)PORT_ArenaZAlloc(poolp, (src->len + 64));
   597     dest->len = (src->len + 64);
   598     if(dest->data == NULL) {
   599 	rv = SECFailure;
   600 	goto loser;
   601     }
   603     slot = PK11_GetInternalKeySlot();
   604     if(slot == NULL) {
   605 	rv = SECFailure;
   606 	goto loser;
   607     }
   609     eKey = PK11_PBEKeyGen(slot, algid, key, PR_FALSE, wincx);
   610     if(eKey == NULL) {
   611 	rv = SECFailure;
   612 	goto loser;
   613     }
   615     cryptoMechType = PK11_GetPBECryptoMechanism(algid, &c_param, key);
   616     if (cryptoMechType == CKM_INVALID_MECHANISM) {
   617 	rv = SECFailure;
   618 	goto loser;
   619     }
   621     cx = PK11_CreateContextBySymKey(cryptoMechType, CKA_DECRYPT,
   622 		    		    eKey, c_param);
   623     if(cx == NULL) {
   624 	rv = SECFailure;
   625 	goto loser;
   626     }
   628     rv = PK11_CipherOp((PK11Context*)cx, dest->data, (int *)(&dest->len), 
   629 		       (int)(src->len + 64), src->data, (int)src->len);
   630     PK11_DestroyContext((PK11Context *)cx, PR_TRUE);
   632     bs = PK11_GetBlockSize(cryptoMechType, c_param);
   633     if(bs) {
   634 	/* check for proper badding in block algorithms.  this assumes
   635 	 * RC2 cbc or a DES cbc variant.  and the padding is thus defined
   636 	 */
   637 	if(((int)dest->data[dest->len-1] <= bs) && 
   638 	   ((int)dest->data[dest->len-1] > 0)) {
   639 	    dest->len -= (int)dest->data[dest->len-1];
   640 	} else {
   641 	    rv = SECFailure;
   642 	    /* set an error ? */
   643 	}
   644     } 
   646 loser:
   647     /* let success fall through */
   648     if(result != NULL)
   649 	SECITEM_ZfreeItem(result, PR_TRUE);
   651     if(rv == SECFailure)
   652 	PORT_ArenaRelease(poolp, mark);
   653     else
   654 	PORT_ArenaUnmark(poolp, mark);
   656     if(eKey != NULL)
   657 	PK11_FreeSymKey(eKey);
   659     if(slot != NULL)
   660 	PK11_FreeSlot(slot);
   662     if(c_param != NULL) 
   663 	SECITEM_ZfreeItem(c_param, PR_TRUE);
   665     return rv;
   666 }
   668 SECItem **
   669 SEC_PKCS7GetCertificateList(SEC_PKCS7ContentInfo *cinfo)
   670 {
   671     switch(SEC_PKCS7ContentType(cinfo))
   672     {
   673 	case SEC_OID_PKCS7_SIGNED_DATA:
   674 	    return cinfo->content.signedData->rawCerts;
   675 	    break;
   676 	default:
   677 	    return NULL;
   678 	    break;
   679     }
   680 }
   683 int
   684 SEC_PKCS7GetKeyLength(SEC_PKCS7ContentInfo *cinfo)
   685 {
   686   if (cinfo->contentTypeTag->offset == SEC_OID_PKCS7_ENVELOPED_DATA)
   687     return cinfo->content.envelopedData->encContentInfo.keysize;
   688   else
   689     return 0;
   690 }

mercurial