security/nss/lib/pkcs7/p7encode.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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 encoding.
     7  */
     9 #include "p7local.h"
    11 #include "cert.h"
    12 #include "cryptohi.h"
    13 #include "keyhi.h"
    14 #include "secasn1.h"
    15 #include "secoid.h"
    16 #include "secitem.h"
    17 #include "pk11func.h"
    18 #include "secerr.h"
    19 #include "sechash.h"	/* for HASH_GetHashObject() */
    21 struct sec_pkcs7_encoder_output {
    22     SEC_PKCS7EncoderOutputCallback outputfn;
    23     void *outputarg;
    24 };
    26 struct SEC_PKCS7EncoderContextStr {
    27     SEC_ASN1EncoderContext *ecx;
    28     SEC_PKCS7ContentInfo *cinfo;
    29     struct sec_pkcs7_encoder_output output;
    30     sec_PKCS7CipherObject *encryptobj;
    31     const SECHashObject *digestobj;
    32     void *digestcx;
    33 };
    36 /*
    37  * The little output function that the ASN.1 encoder calls to hand
    38  * us bytes which we in turn hand back to our caller (via the callback
    39  * they gave us).
    40  */
    41 static void
    42 sec_pkcs7_encoder_out(void *arg, const char *buf, unsigned long len,
    43 		      int depth, SEC_ASN1EncodingPart data_kind)
    44 {
    45     struct sec_pkcs7_encoder_output *output;
    47     output = (struct sec_pkcs7_encoder_output*)arg;
    48     output->outputfn (output->outputarg, buf, len);
    49 }
    51 static sec_PKCS7CipherObject *
    52 sec_pkcs7_encoder_start_encrypt (SEC_PKCS7ContentInfo *cinfo,
    53 						 PK11SymKey *orig_bulkkey)
    54 {
    55     SECOidTag kind;
    56     sec_PKCS7CipherObject *encryptobj;
    57     SEC_PKCS7RecipientInfo **recipientinfos, *ri;
    58     SEC_PKCS7EncryptedContentInfo *enccinfo;
    59     SECKEYPublicKey *publickey = NULL;
    60     SECKEYPrivateKey *ourPrivKey = NULL;
    61     PK11SymKey  *bulkkey;
    62     void *mark, *wincx;
    63     int i;
    64     PLArenaPool *arena = NULL;
    66     /* Get the context in case we need it below. */
    67     wincx = cinfo->pwfn_arg;
    69     kind = SEC_PKCS7ContentType (cinfo);
    70     switch (kind) {
    71       default:
    72       case SEC_OID_PKCS7_DATA:
    73       case SEC_OID_PKCS7_DIGESTED_DATA:
    74       case SEC_OID_PKCS7_SIGNED_DATA:
    75 	recipientinfos = NULL;
    76 	enccinfo = NULL;
    77 	break;
    78       case SEC_OID_PKCS7_ENCRYPTED_DATA:
    79 	{
    80 	    SEC_PKCS7EncryptedData *encdp;
    82 	    /* To do EncryptedData we *must* be given a bulk key. */
    83 	    PORT_Assert (orig_bulkkey != NULL);
    84 	    if (orig_bulkkey == NULL) {
    85 		/* XXX error? */
    86 		return NULL;
    87 	    }
    89 	    encdp = cinfo->content.encryptedData;
    90 	    recipientinfos = NULL;
    91 	    enccinfo = &(encdp->encContentInfo);
    92 	}
    93 	break;
    94       case SEC_OID_PKCS7_ENVELOPED_DATA:
    95 	{
    96 	    SEC_PKCS7EnvelopedData *envdp;
    98 	    envdp = cinfo->content.envelopedData;
    99 	    recipientinfos = envdp->recipientInfos;
   100 	    enccinfo = &(envdp->encContentInfo);
   101 	}
   102 	break;
   103       case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
   104 	{
   105 	    SEC_PKCS7SignedAndEnvelopedData *saedp;
   107 	    saedp = cinfo->content.signedAndEnvelopedData;
   108 	    recipientinfos = saedp->recipientInfos;
   109 	    enccinfo = &(saedp->encContentInfo);
   110 	}
   111 	break;
   112     }
   114     if (enccinfo == NULL)
   115 	return NULL;
   117     bulkkey = orig_bulkkey;
   118     if (bulkkey == NULL) {
   119 	CK_MECHANISM_TYPE type = PK11_AlgtagToMechanism(enccinfo->encalg);
   120 	PK11SlotInfo *slot;
   123 	slot = PK11_GetBestSlot(type,cinfo->pwfn_arg);
   124 	if (slot == NULL) {
   125 	    return NULL;
   126 	}
   127 	bulkkey = PK11_KeyGen(slot,type,NULL, enccinfo->keysize/8,
   128 			      cinfo->pwfn_arg);
   129 	PK11_FreeSlot(slot);
   130 	if (bulkkey == NULL) {
   131 	    return NULL;
   132 	}
   133     }
   135     encryptobj = NULL;
   136     mark = PORT_ArenaMark (cinfo->poolp);
   138     /*
   139      * Encrypt the bulk key with the public key of each recipient.
   140      */
   141     for (i = 0; recipientinfos && (ri = recipientinfos[i]) != NULL; i++) {
   142 	CERTCertificate *cert;
   143 	SECOidTag certalgtag, encalgtag;
   144 	SECStatus rv;
   145 	int data_len;
   146 	SECItem *params = NULL;
   148 	cert = ri->cert;
   149 	PORT_Assert (cert != NULL);
   150 	if (cert == NULL)
   151 	    continue;
   153 	/*
   154 	 * XXX Want an interface that takes a cert and some data and
   155 	 * fills in an algorithmID and encrypts the data with the public
   156 	 * key from the cert.  Or, give me two interfaces -- one which
   157 	 * gets the algorithm tag from a cert (I should not have to go
   158 	 * down into the subjectPublicKeyInfo myself) and another which
   159 	 * takes a public key and algorithm tag and data and encrypts
   160 	 * the data.  Or something like that.  The point is that all
   161 	 * of the following hardwired RSA stuff should be done elsewhere.
   162 	 */
   164 	certalgtag=SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
   166 	switch (certalgtag) {
   167 	case SEC_OID_PKCS1_RSA_ENCRYPTION:
   168 	    encalgtag = certalgtag;
   169 	    publickey = CERT_ExtractPublicKey (cert);
   170 	    if (publickey == NULL) goto loser;
   172 	    data_len = SECKEY_PublicKeyStrength(publickey);
   173 	    ri->encKey.data = 
   174 	        (unsigned char*)PORT_ArenaAlloc(cinfo->poolp ,data_len);
   175 	    ri->encKey.len = data_len;
   176 	    if (ri->encKey.data == NULL) goto loser;
   178 	    rv = PK11_PubWrapSymKey(PK11_AlgtagToMechanism(certalgtag),publickey,
   179 				bulkkey,&ri->encKey);
   181 	    SECKEY_DestroyPublicKey(publickey);
   182 	    publickey = NULL;
   183 	    if (rv != SECSuccess) goto loser;
   184 	    params = NULL; /* paranoia */
   185 	    break;
   186 	default:
   187 	    PORT_SetError (SEC_ERROR_INVALID_ALGORITHM);
   188 	    goto loser;
   189 	}
   191 	rv = SECOID_SetAlgorithmID(cinfo->poolp, &ri->keyEncAlg, encalgtag, 
   192 			params);
   193 	if (rv != SECSuccess)
   194 	    goto loser;
   195 	if (arena) PORT_FreeArena(arena,PR_FALSE);
   196 	arena = NULL;
   197     }
   199     encryptobj = sec_PKCS7CreateEncryptObject (cinfo->poolp, bulkkey,
   200 					       enccinfo->encalg,
   201 					       &(enccinfo->contentEncAlg));
   202     if (encryptobj != NULL) {
   203 	PORT_ArenaUnmark (cinfo->poolp, mark);
   204 	mark = NULL;		/* good one; do not want to release */
   205     }
   206     /* fallthru */
   208 loser:
   209     if (arena) {
   210 	PORT_FreeArena(arena, PR_FALSE);
   211     }
   212     if (publickey) {
   213         SECKEY_DestroyPublicKey(publickey);
   214     }
   215     if (ourPrivKey) {
   216         SECKEY_DestroyPrivateKey(ourPrivKey);
   217     }
   218     if (mark != NULL) {
   219 	PORT_ArenaRelease (cinfo->poolp, mark);
   220     }
   221     if (orig_bulkkey == NULL) {
   222 	if (bulkkey) PK11_FreeSymKey(bulkkey);
   223     }
   225     return encryptobj;
   226 }
   229 static void
   230 sec_pkcs7_encoder_notify (void *arg, PRBool before, void *dest, int depth)
   231 {
   232     SEC_PKCS7EncoderContext *p7ecx;
   233     SEC_PKCS7ContentInfo *cinfo;
   234     SECOidTag kind;
   235     PRBool before_content;
   237     /*
   238      * We want to notice just before the content field.  After fields are
   239      * not interesting to us.
   240      */
   241     if (!before)
   242 	return;
   244     p7ecx = (SEC_PKCS7EncoderContext*)arg;
   245     cinfo = p7ecx->cinfo;
   247     before_content = PR_FALSE;
   249     /*
   250      * Watch for the content field, at which point we want to instruct
   251      * the ASN.1 encoder to start taking bytes from the buffer.
   252      *
   253      * XXX The following assumes the inner content type is data;
   254      * if/when we want to handle fully nested types, this will have
   255      * to recurse until reaching the innermost data content.
   256      */
   257     kind = SEC_PKCS7ContentType (cinfo);
   258     switch (kind) {
   259       default:
   260       case SEC_OID_PKCS7_DATA:
   261 	if (dest == &(cinfo->content.data))
   262 	    before_content = PR_TRUE;
   263 	break;
   265       case SEC_OID_PKCS7_DIGESTED_DATA:
   266 	{
   267 	    SEC_PKCS7DigestedData *digd;
   269 	    digd = cinfo->content.digestedData;
   270 	    if (digd == NULL)
   271 		break;
   273 	    if (dest == &(digd->contentInfo.content))
   274 		before_content = PR_TRUE;
   275 	}
   276 	break;
   278       case SEC_OID_PKCS7_ENCRYPTED_DATA:
   279 	{
   280 	    SEC_PKCS7EncryptedData *encd;
   282 	    encd = cinfo->content.encryptedData;
   283 	    if (encd == NULL)
   284 		break;
   286 	    if (dest == &(encd->encContentInfo.encContent))
   287 		before_content = PR_TRUE;
   288 	}
   289 	break;
   291       case SEC_OID_PKCS7_ENVELOPED_DATA:
   292 	{
   293 	    SEC_PKCS7EnvelopedData *envd;
   295 	    envd = cinfo->content.envelopedData;
   296 	    if (envd == NULL)
   297 		break;
   299 	    if (dest == &(envd->encContentInfo.encContent))
   300 		before_content = PR_TRUE;
   301 	}
   302 	break;
   304       case SEC_OID_PKCS7_SIGNED_DATA:
   305 	{
   306 	    SEC_PKCS7SignedData *sigd;
   308 	    sigd = cinfo->content.signedData;
   309 	    if (sigd == NULL)
   310 		break;
   312 	    if (dest == &(sigd->contentInfo.content))
   313 		before_content = PR_TRUE;
   314 	}
   315 	break;
   317       case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
   318 	{
   319 	    SEC_PKCS7SignedAndEnvelopedData *saed;
   321 	    saed = cinfo->content.signedAndEnvelopedData;
   322 	    if (saed == NULL)
   323 		break;
   325 	    if (dest == &(saed->encContentInfo.encContent))
   326 		before_content = PR_TRUE;
   327 	}
   328 	break;
   329     }
   331     if (before_content) {
   332 	/*
   333 	 * This will cause the next SEC_ASN1EncoderUpdate to take the
   334 	 * contents bytes from the passed-in buffer.
   335 	 */
   336 	SEC_ASN1EncoderSetTakeFromBuf (p7ecx->ecx);
   337 	/*
   338 	 * And that is all we needed this notify function for.
   339 	 */
   340 	SEC_ASN1EncoderClearNotifyProc (p7ecx->ecx);
   341     }
   342 }
   345 static SEC_PKCS7EncoderContext *
   346 sec_pkcs7_encoder_start_contexts (SEC_PKCS7ContentInfo *cinfo,
   347 				  PK11SymKey *bulkkey)
   348 {
   349     SEC_PKCS7EncoderContext *p7ecx;
   350     SECOidTag kind;
   351     PRBool encrypt;
   352     SECItem **digests;
   353     SECAlgorithmID *digestalg, **digestalgs;
   355     p7ecx = 
   356       (SEC_PKCS7EncoderContext*)PORT_ZAlloc (sizeof(SEC_PKCS7EncoderContext));
   357     if (p7ecx == NULL)
   358 	return NULL;
   360     digests = NULL;
   361     digestalg = NULL;
   362     digestalgs = NULL;
   363     encrypt = PR_FALSE;
   365     kind = SEC_PKCS7ContentType (cinfo);
   366     switch (kind) {
   367       default:
   368       case SEC_OID_PKCS7_DATA:
   369 	break;
   370       case SEC_OID_PKCS7_DIGESTED_DATA:
   371 	digestalg = &(cinfo->content.digestedData->digestAlg);
   372 	break;
   373       case SEC_OID_PKCS7_SIGNED_DATA:
   374 	digests = cinfo->content.signedData->digests;
   375 	digestalgs = cinfo->content.signedData->digestAlgorithms;
   376 	break;
   377       case SEC_OID_PKCS7_ENCRYPTED_DATA:
   378       case SEC_OID_PKCS7_ENVELOPED_DATA:
   379 	encrypt = PR_TRUE;
   380 	break;
   381       case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
   382 	digests = cinfo->content.signedAndEnvelopedData->digests;
   383 	digestalgs = cinfo->content.signedAndEnvelopedData->digestAlgorithms;
   384 	encrypt = PR_TRUE;
   385 	break;
   386     }
   388     if (encrypt) {
   389 	p7ecx->encryptobj = sec_pkcs7_encoder_start_encrypt (cinfo, bulkkey);
   390 	if (p7ecx->encryptobj == NULL) {
   391 	    PORT_Free (p7ecx);
   392 	    return NULL;
   393 	}
   394     }
   396     if (digestalgs != NULL) {
   397 	if (digests != NULL) {
   398 	    /* digests already created (probably for detached data) */
   399 	    digestalg = NULL;
   400 	} else {
   401 	    /*
   402 	     * XXX Some day we should handle multiple digests; for now,
   403 	     * assume only one will be done.
   404 	     */
   405 	    PORT_Assert (digestalgs[0] != NULL && digestalgs[1] == NULL);
   406 	    digestalg = digestalgs[0];
   407 	}
   408     }
   410     if (digestalg != NULL) {
   411 	SECOidTag  oidTag = SECOID_FindOIDTag(&(digestalg->algorithm));
   413 	p7ecx->digestobj = HASH_GetHashObjectByOidTag(oidTag);
   414 	if (p7ecx->digestobj != NULL) {
   415 	    p7ecx->digestcx = (* p7ecx->digestobj->create) ();
   416 	    if (p7ecx->digestcx == NULL)
   417 		p7ecx->digestobj = NULL;
   418 	    else
   419 		(* p7ecx->digestobj->begin) (p7ecx->digestcx);
   420 	}
   421 	if (p7ecx->digestobj == NULL) {
   422 	    if (p7ecx->encryptobj != NULL)
   423 		sec_PKCS7DestroyEncryptObject (p7ecx->encryptobj);
   424 	    PORT_Free (p7ecx);
   425 	    return NULL;
   426 	}
   427     }
   429     p7ecx->cinfo = cinfo;
   430     return p7ecx;
   431 }
   434 SEC_PKCS7EncoderContext *
   435 SEC_PKCS7EncoderStart (SEC_PKCS7ContentInfo *cinfo,
   436 		       SEC_PKCS7EncoderOutputCallback outputfn,
   437 		       void *outputarg,
   438 		       PK11SymKey *bulkkey)
   439 {
   440     SEC_PKCS7EncoderContext *p7ecx;
   441     SECStatus rv;
   443     p7ecx = sec_pkcs7_encoder_start_contexts (cinfo, bulkkey);
   444     if (p7ecx == NULL)
   445 	return NULL;
   447     p7ecx->output.outputfn = outputfn;
   448     p7ecx->output.outputarg = outputarg;
   450     /*
   451      * Initialize the BER encoder.
   452      */
   453     p7ecx->ecx = SEC_ASN1EncoderStart (cinfo, sec_PKCS7ContentInfoTemplate,
   454 				       sec_pkcs7_encoder_out, &(p7ecx->output));
   455     if (p7ecx->ecx == NULL) {
   456 	PORT_Free (p7ecx);
   457 	return NULL;
   458     }
   460     /*
   461      * Indicate that we are streaming.  We will be streaming until we
   462      * get past the contents bytes.
   463      */
   464     SEC_ASN1EncoderSetStreaming (p7ecx->ecx);
   466     /*
   467      * The notify function will watch for the contents field.
   468      */
   469     SEC_ASN1EncoderSetNotifyProc (p7ecx->ecx, sec_pkcs7_encoder_notify, p7ecx);
   471     /*
   472      * This will encode everything up to the content bytes.  (The notify
   473      * function will then cause the encoding to stop there.)  Then our
   474      * caller can start passing contents bytes to our Update, which we
   475      * will pass along.
   476      */
   477     rv = SEC_ASN1EncoderUpdate (p7ecx->ecx, NULL, 0);
   478     if (rv != SECSuccess) {
   479 	PORT_Free (p7ecx);
   480 	return NULL;
   481     }
   483     return p7ecx;
   484 }
   487 /*
   488  * XXX If/when we support nested contents, this needs to be revised.
   489  */
   490 static SECStatus
   491 sec_pkcs7_encoder_work_data (SEC_PKCS7EncoderContext *p7ecx, SECItem *dest,
   492 			     const unsigned char *data, unsigned long len,
   493 			     PRBool final)
   494 {
   495     unsigned char *buf = NULL;
   496     SECStatus rv;
   499     rv = SECSuccess;		/* may as well be optimistic */
   501     /*
   502      * We should really have data to process, or we should be trying
   503      * to finish/flush the last block.  (This is an overly paranoid
   504      * check since all callers are in this file and simple inspection
   505      * proves they do it right.  But it could find a bug in future
   506      * modifications/development, that is why it is here.)
   507      */
   508     PORT_Assert ((data != NULL && len) || final);
   510     /*
   511      * Update the running digest.
   512      * XXX This needs modification if/when we handle multiple digests.
   513      */
   514     if (len && p7ecx->digestobj != NULL) {
   515 	(* p7ecx->digestobj->update) (p7ecx->digestcx, data, len);
   516     }
   518     /*
   519      * Encrypt this chunk.
   520      */
   521     if (p7ecx->encryptobj != NULL) {
   522 	/* XXX the following lengths should all be longs? */
   523 	unsigned int inlen;	/* length of data being encrypted */
   524 	unsigned int outlen;	/* length of encrypted data */
   525 	unsigned int buflen;	/* length available for encrypted data */
   527 	inlen = len;
   528 	buflen = sec_PKCS7EncryptLength (p7ecx->encryptobj, inlen, final);
   529 	if (buflen == 0) {
   530 	    /*
   531 	     * No output is expected, but the input data may be buffered
   532 	     * so we still have to call Encrypt.
   533 	     */
   534 	    rv = sec_PKCS7Encrypt (p7ecx->encryptobj, NULL, NULL, 0,
   535 				   data, inlen, final);
   536 	    if (final) {
   537 		len = 0;
   538 		goto done;
   539 	    }
   540 	    return rv;
   541 	}
   543 	if (dest != NULL)
   544 	    buf = (unsigned char*)PORT_ArenaAlloc(p7ecx->cinfo->poolp, buflen);
   545 	else
   546 	    buf = (unsigned char*)PORT_Alloc (buflen);
   548 	if (buf == NULL) {
   549 	    rv = SECFailure;
   550 	} else {
   551 	    rv = sec_PKCS7Encrypt (p7ecx->encryptobj, buf, &outlen, buflen,
   552 				   data, inlen, final);
   553 	    data = buf;
   554 	    len = outlen;
   555 	}
   556 	if (rv != SECSuccess) {
   557 	    if (final)
   558 		goto done;
   559 	    return rv;
   560 	}
   561     }
   563     if (p7ecx->ecx != NULL) {
   564 	/*
   565 	 * Encode the contents bytes.
   566 	 */
   567 	if(len) {
   568 	    rv = SEC_ASN1EncoderUpdate (p7ecx->ecx, (const char *)data, len);
   569 	}
   570     }
   572 done:
   573     if (p7ecx->encryptobj != NULL) {
   574 	if (final)
   575 	    sec_PKCS7DestroyEncryptObject (p7ecx->encryptobj);
   576 	if (dest != NULL) {
   577 	    dest->data = buf;
   578 	    dest->len = len;
   579 	} else if (buf != NULL) {
   580 	    PORT_Free (buf);
   581 	}
   582     }
   584     if (final && p7ecx->digestobj != NULL) {
   585 	SECItem *digest, **digests, ***digestsp;
   586 	unsigned char *digdata;
   587 	SECOidTag kind;
   589 	kind = SEC_PKCS7ContentType (p7ecx->cinfo);
   590 	switch (kind) {
   591 	  default:
   592 	    PORT_Assert (0);
   593 	    return SECFailure;
   594 	  case SEC_OID_PKCS7_DIGESTED_DATA:
   595 	    digest = &(p7ecx->cinfo->content.digestedData->digest);
   596 	    digestsp = NULL;
   597 	    break;
   598 	  case SEC_OID_PKCS7_SIGNED_DATA:
   599 	    digest = NULL;
   600 	    digestsp = &(p7ecx->cinfo->content.signedData->digests);
   601 	    break;
   602 	  case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
   603 	    digest = NULL;
   604 	    digestsp = &(p7ecx->cinfo->content.signedAndEnvelopedData->digests);
   605 	    break;
   606 	}
   608 	digdata = (unsigned char*)PORT_ArenaAlloc (p7ecx->cinfo->poolp,
   609 				   p7ecx->digestobj->length);
   610 	if (digdata == NULL)
   611 	    return SECFailure;
   613 	if (digestsp != NULL) {
   614 	    PORT_Assert (digest == NULL);
   616 	    digest = (SECItem*)PORT_ArenaAlloc (p7ecx->cinfo->poolp, 
   617 						sizeof(SECItem));
   618 	    digests = (SECItem**)PORT_ArenaAlloc (p7ecx->cinfo->poolp,
   619 				       2 * sizeof(SECItem *));
   620 	    if (digests == NULL || digest == NULL)
   621 		return SECFailure;
   623 	    digests[0] = digest;
   624 	    digests[1] = NULL;
   626 	    *digestsp = digests;
   627 	}
   629 	PORT_Assert (digest != NULL);
   631 	digest->data = digdata;
   632 	digest->len = p7ecx->digestobj->length;
   634 	(* p7ecx->digestobj->end) (p7ecx->digestcx, digest->data,
   635 				   &(digest->len), digest->len);
   636 	(* p7ecx->digestobj->destroy) (p7ecx->digestcx, PR_TRUE);
   637     }
   639     return rv;
   640 }
   643 SECStatus
   644 SEC_PKCS7EncoderUpdate (SEC_PKCS7EncoderContext *p7ecx,
   645 			const char *data, unsigned long len)
   646 {
   647     /* XXX Error handling needs help.  Return what?  Do "Finish" on failure? */
   648     return sec_pkcs7_encoder_work_data (p7ecx, NULL,
   649 					(const unsigned char *)data, len,
   650 					PR_FALSE);
   651 }
   653 static SECStatus
   654 sec_pkcs7_encoder_sig_and_certs (SEC_PKCS7ContentInfo *cinfo,
   655 				 SECKEYGetPasswordKey pwfn, void *pwfnarg)
   656 {
   657     SECOidTag kind;
   658     CERTCertificate **certs;
   659     CERTCertificateList **certlists;
   660     SECAlgorithmID **digestalgs;
   661     SECItem **digests;
   662     SEC_PKCS7SignerInfo *signerinfo, **signerinfos;
   663     SECItem **rawcerts, ***rawcertsp;
   664     PLArenaPool *poolp;
   665     int certcount;
   666     int ci, cli, rci, si;
   668     kind = SEC_PKCS7ContentType (cinfo);
   669     switch (kind) {
   670       default:
   671       case SEC_OID_PKCS7_DATA:
   672       case SEC_OID_PKCS7_DIGESTED_DATA:
   673       case SEC_OID_PKCS7_ENCRYPTED_DATA:
   674       case SEC_OID_PKCS7_ENVELOPED_DATA:
   675 	certs = NULL;
   676 	certlists = NULL;
   677 	digestalgs = NULL;
   678 	digests = NULL;
   679 	signerinfos = NULL;
   680 	rawcertsp = NULL;
   681 	break;
   682       case SEC_OID_PKCS7_SIGNED_DATA:
   683 	{
   684 	    SEC_PKCS7SignedData *sdp;
   686 	    sdp = cinfo->content.signedData;
   687 	    certs = sdp->certs;
   688 	    certlists = sdp->certLists;
   689 	    digestalgs = sdp->digestAlgorithms;
   690 	    digests = sdp->digests;
   691 	    signerinfos = sdp->signerInfos;
   692 	    rawcertsp = &(sdp->rawCerts);
   693 	}
   694 	break;
   695       case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
   696 	{
   697 	    SEC_PKCS7SignedAndEnvelopedData *saedp;
   699 	    saedp = cinfo->content.signedAndEnvelopedData;
   700 	    certs = saedp->certs;
   701 	    certlists = saedp->certLists;
   702 	    digestalgs = saedp->digestAlgorithms;
   703 	    digests = saedp->digests;
   704 	    signerinfos = saedp->signerInfos;
   705 	    rawcertsp = &(saedp->rawCerts);
   706 	}
   707 	break;
   708     }
   710     if (certs == NULL && certlists == NULL && signerinfos == NULL)
   711 	return SECSuccess;		/* nothing for us to do! */
   713     poolp = cinfo->poolp;
   714     certcount = 0;
   716     if (signerinfos != NULL) {
   717 	SECOidTag digestalgtag;
   718 	int di;
   719 	SECStatus rv;
   720 	CERTCertificate *cert;
   721 	SECKEYPrivateKey *privkey;
   722 	SECItem signature;
   723 	SECOidTag signalgtag;
   725 	PORT_Assert (digestalgs != NULL && digests != NULL);
   727 	/*
   728 	 * If one fails, we bail right then.  If we want to continue and
   729 	 * try to do subsequent signatures, this loop, and the departures
   730 	 * from it, will need to be reworked.
   731 	 */
   732 	for (si = 0; signerinfos[si] != NULL; si++) {
   734 	    signerinfo = signerinfos[si];
   736 	    /* find right digest */
   737 	    digestalgtag = SECOID_GetAlgorithmTag (&(signerinfo->digestAlg));
   738 	    for (di = 0; digestalgs[di] != NULL; di++) {
   739 		/* XXX Should I be comparing more than the tag? */
   740 		if (digestalgtag == SECOID_GetAlgorithmTag (digestalgs[di]))
   741 		    break;
   742 	    }
   743 	    if (digestalgs[di] == NULL) {
   744 		/* XXX oops; do what? set an error? */
   745 		return SECFailure;
   746 	    }
   747 	    PORT_Assert (digests[di] != NULL);
   749 	    cert = signerinfo->cert;
   750 	    privkey = PK11_FindKeyByAnyCert (cert, pwfnarg);
   751 	    if (privkey == NULL)
   752 		return SECFailure;
   754 	    /*
   755 	     * XXX I think there should be a cert-level interface for this,
   756 	     * so that I do not have to know about subjectPublicKeyInfo...
   757 	     */
   758 	    signalgtag = SECOID_GetAlgorithmTag (&(cert->subjectPublicKeyInfo.algorithm));
   760 	    if (signerinfo->authAttr != NULL) {
   761 		SEC_PKCS7Attribute *attr;
   762 		SECItem encoded_attrs;
   763 		SECItem *dummy;
   764 		SECOidTag algid;
   766 		/*
   767 		 * First, find and fill in the message digest attribute.
   768 		 */
   769 		attr = sec_PKCS7FindAttribute (signerinfo->authAttr,
   770 					       SEC_OID_PKCS9_MESSAGE_DIGEST,
   771 					       PR_TRUE);
   772 		PORT_Assert (attr != NULL);
   773 		if (attr == NULL) {
   774 		    SECKEY_DestroyPrivateKey (privkey);
   775 		    return SECFailure;
   776 		}
   778 		/*
   779 		 * XXX The second half of the following assertion prevents
   780 		 * the encoder from being called twice on the same content.
   781 		 * Either just remove the second half the assertion, or
   782 		 * change the code to check if the value already there is
   783 		 * the same as digests[di], whichever seems more right.
   784 		 */
   785 		PORT_Assert (attr->values != NULL && attr->values[0] == NULL);
   786 		attr->values[0] = digests[di];
   788 		/*
   789 		 * Before encoding, reorder the attributes so that when they
   790 		 * are encoded, they will be conforming DER, which is required
   791 		 * to have a specific order and that is what must be used for
   792 		 * the hash/signature.  We do this here, rather than building
   793 		 * it into EncodeAttributes, because we do not want to do
   794 		 * such reordering on incoming messages (which also uses
   795 		 * EncodeAttributes) or our old signatures (and other "broken"
   796 		 * implementations) will not verify.  So, we want to guarantee
   797 		 * that we send out good DER encodings of attributes, but not
   798 		 * to expect to receive them.
   799 		 */
   800 		rv = sec_PKCS7ReorderAttributes (signerinfo->authAttr);
   801 		if (rv != SECSuccess) {
   802 		    SECKEY_DestroyPrivateKey (privkey);
   803 		    return SECFailure;
   804 		}
   806 		encoded_attrs.data = NULL;
   807 		encoded_attrs.len = 0;
   808 		dummy = sec_PKCS7EncodeAttributes (NULL, &encoded_attrs,
   809 						   &(signerinfo->authAttr));
   810 		if (dummy == NULL) {
   811 		    SECKEY_DestroyPrivateKey (privkey);
   812 		    return SECFailure;
   813 		}
   815 	        algid = SEC_GetSignatureAlgorithmOidTag(privkey->keyType,
   816  							digestalgtag);
   817 		if (algid == SEC_OID_UNKNOWN) {
   818 		    PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
   819 		    SECKEY_DestroyPrivateKey (privkey);
   820 		    return SECFailure;
   821 		}
   822 		rv = SEC_SignData (&signature,
   823 				   encoded_attrs.data, encoded_attrs.len,
   824 				   privkey,
   825 				   algid);
   826 		SECITEM_FreeItem (&encoded_attrs, PR_FALSE);
   827 	    } else {
   828 		rv = SGN_Digest (privkey, digestalgtag, &signature,
   829 				 digests[di]);
   830 	    }
   832 	    SECKEY_DestroyPrivateKey (privkey);
   834 	    if (rv != SECSuccess)
   835 		return rv;
   837 	    rv = SECITEM_CopyItem (poolp, &(signerinfo->encDigest), &signature);
   838 	    if (rv != SECSuccess)
   839 		return rv;
   841 	    SECITEM_FreeItem (&signature, PR_FALSE);
   843 	    rv = SECOID_SetAlgorithmID (poolp, &(signerinfo->digestEncAlg),
   844 					signalgtag, NULL);
   845 	    if (rv != SECSuccess)
   846 		return SECFailure;
   848 	    /*
   849 	     * Count the cert chain for this signer.
   850 	     */
   851 	    if (signerinfo->certList != NULL)
   852 		certcount += signerinfo->certList->len;
   853 	}
   854     }
   856     if (certs != NULL) {
   857 	for (ci = 0; certs[ci] != NULL; ci++)
   858 	    certcount++;
   859     }
   861     if (certlists != NULL) {
   862 	for (cli = 0; certlists[cli] != NULL; cli++)
   863 	    certcount += certlists[cli]->len;
   864     }
   866     if (certcount == 0)
   867 	return SECSuccess;		/* signing done; no certs */
   869     /*
   870      * Combine all of the certs and cert chains into rawcerts.
   871      * Note: certcount is an upper bound; we may not need that many slots
   872      * but we will allocate anyway to avoid having to do another pass.
   873      * (The temporary space saving is not worth it.)
   874      */
   875     rawcerts = (SECItem**)PORT_ArenaAlloc (poolp, 
   876 					(certcount + 1) * sizeof(SECItem *));
   877     if (rawcerts == NULL)
   878 	return SECFailure;
   880     /*
   881      * XXX Want to check for duplicates and not add *any* cert that is
   882      * already in the set.  This will be more important when we start
   883      * dealing with larger sets of certs, dual-key certs (signing and
   884      * encryption), etc.  For the time being we can slide by...
   885      */
   886     rci = 0;
   887     if (signerinfos != NULL) {
   888 	for (si = 0; signerinfos[si] != NULL; si++) {
   889 	    signerinfo = signerinfos[si];
   890 	    for (ci = 0; ci < signerinfo->certList->len; ci++)
   891 		rawcerts[rci++] = &(signerinfo->certList->certs[ci]);
   892 	}
   894     }
   896     if (certs != NULL) {
   897 	for (ci = 0; certs[ci] != NULL; ci++)
   898 	    rawcerts[rci++] = &(certs[ci]->derCert);
   899     }
   901     if (certlists != NULL) {
   902 	for (cli = 0; certlists[cli] != NULL; cli++) {
   903 	    for (ci = 0; ci < certlists[cli]->len; ci++)
   904 		rawcerts[rci++] = &(certlists[cli]->certs[ci]);
   905 	}
   906     }
   908     rawcerts[rci] = NULL;
   909     *rawcertsp = rawcerts;
   911     return SECSuccess;
   912 }
   915 SECStatus
   916 SEC_PKCS7EncoderFinish (SEC_PKCS7EncoderContext *p7ecx,
   917 			SECKEYGetPasswordKey pwfn, void *pwfnarg)
   918 {
   919     SECStatus rv;
   921     /*
   922      * Flush out any remaining data.
   923      */
   924     rv = sec_pkcs7_encoder_work_data (p7ecx, NULL, NULL, 0, PR_TRUE);
   926     /*
   927      * Turn off streaming stuff.
   928      */
   929     SEC_ASN1EncoderClearTakeFromBuf (p7ecx->ecx);
   930     SEC_ASN1EncoderClearStreaming (p7ecx->ecx);
   932     if (rv != SECSuccess)
   933 	goto loser;
   935     rv = sec_pkcs7_encoder_sig_and_certs (p7ecx->cinfo, pwfn, pwfnarg);
   936     if (rv != SECSuccess)
   937 	goto loser;
   939     rv = SEC_ASN1EncoderUpdate (p7ecx->ecx, NULL, 0);
   941 loser:
   942     SEC_ASN1EncoderFinish (p7ecx->ecx);
   943     PORT_Free (p7ecx);
   944     return rv;
   945 }
   947 /*
   948  * Abort the ASN.1 stream. Used by pkcs 12
   949  */
   950 void
   951 SEC_PKCS7EncoderAbort(SEC_PKCS7EncoderContext *p7ecx, int error)
   952 {
   953     PORT_Assert(p7ecx);
   954     SEC_ASN1EncoderAbort(p7ecx->ecx, error);
   955 }
   957 /*
   958  * After this routine is called, the entire PKCS7 contentInfo is ready
   959  * to be encoded.  This is used internally, but can also be called from
   960  * elsewhere for those who want to be able to just have pointers to
   961  * the ASN1 template for pkcs7 contentInfo built into their own encodings.
   962  */
   963 SECStatus
   964 SEC_PKCS7PrepareForEncode (SEC_PKCS7ContentInfo *cinfo,
   965 			   PK11SymKey *bulkkey,
   966 			   SECKEYGetPasswordKey pwfn,
   967 			   void *pwfnarg)
   968 {
   969     SEC_PKCS7EncoderContext *p7ecx;
   970     SECItem *content, *enc_content;
   971     SECStatus rv;
   973     p7ecx = sec_pkcs7_encoder_start_contexts (cinfo, bulkkey);
   974     if (p7ecx == NULL)
   975 	return SECFailure;
   977     content = SEC_PKCS7GetContent (cinfo);
   979     if (p7ecx->encryptobj != NULL) {
   980 	SECOidTag kind;
   981 	SEC_PKCS7EncryptedContentInfo *enccinfo;
   983 	kind = SEC_PKCS7ContentType (p7ecx->cinfo);
   984 	switch (kind) {
   985 	  default:
   986 	    PORT_Assert (0);
   987 	    rv = SECFailure;
   988 	    goto loser;
   989 	  case SEC_OID_PKCS7_ENCRYPTED_DATA:
   990 	    enccinfo = &(p7ecx->cinfo->content.encryptedData->encContentInfo);
   991 	    break;
   992 	  case SEC_OID_PKCS7_ENVELOPED_DATA:
   993 	    enccinfo = &(p7ecx->cinfo->content.envelopedData->encContentInfo);
   994 	    break;
   995 	  case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
   996 	    enccinfo = &(p7ecx->cinfo->content.signedAndEnvelopedData->encContentInfo);
   997 	    break;
   998 	}
   999 	enc_content = &(enccinfo->encContent);
  1000     } else {
  1001 	enc_content = NULL;
  1004     if (content != NULL && content->data != NULL && content->len) {
  1005 	rv = sec_pkcs7_encoder_work_data (p7ecx, enc_content,
  1006 					  content->data, content->len, PR_TRUE);
  1007 	if (rv != SECSuccess)
  1008 	    goto loser;
  1011     rv = sec_pkcs7_encoder_sig_and_certs (cinfo, pwfn, pwfnarg);
  1013 loser:
  1014     PORT_Free (p7ecx);
  1015     return rv;
  1019 /*
  1020  * Encode a PKCS7 object, in one shot.  All necessary components
  1021  * of the object must already be specified.  Either the data has
  1022  * already been included (via SetContent), or the data is detached,
  1023  * or there is no data at all (certs-only).
  1025  * "cinfo" specifies the object to be encoded.
  1027  * "outputfn" is where the encoded bytes will be passed.
  1029  * "outputarg" is an opaque argument to the above callback.
  1031  * "bulkkey" specifies the bulk encryption key to use.   This argument
  1032  * can be NULL if no encryption is being done, or if the bulk key should
  1033  * be generated internally (usually the case for EnvelopedData but never
  1034  * for EncryptedData, which *must* provide a bulk encryption key).
  1036  * "pwfn" is a callback for getting the password which protects the
  1037  * private key of the signer.  This argument can be NULL if it is known
  1038  * that no signing is going to be done.
  1040  * "pwfnarg" is an opaque argument to the above callback.
  1041  */
  1042 SECStatus
  1043 SEC_PKCS7Encode (SEC_PKCS7ContentInfo *cinfo,
  1044 		 SEC_PKCS7EncoderOutputCallback outputfn,
  1045 		 void *outputarg,
  1046 		 PK11SymKey *bulkkey,
  1047 		 SECKEYGetPasswordKey pwfn,
  1048 		 void *pwfnarg)
  1050     SECStatus rv;
  1052     rv = SEC_PKCS7PrepareForEncode (cinfo, bulkkey, pwfn, pwfnarg);
  1053     if (rv == SECSuccess) {
  1054 	struct sec_pkcs7_encoder_output outputcx;
  1056 	outputcx.outputfn = outputfn;
  1057 	outputcx.outputarg = outputarg;
  1059 	rv = SEC_ASN1Encode (cinfo, sec_PKCS7ContentInfoTemplate,
  1060 			     sec_pkcs7_encoder_out, &outputcx);
  1063     return rv;
  1067 /*
  1068  * Encode a PKCS7 object, in one shot.  All necessary components
  1069  * of the object must already be specified.  Either the data has
  1070  * already been included (via SetContent), or the data is detached,
  1071  * or there is no data at all (certs-only).  The output, rather than
  1072  * being passed to an output function as is done above, is all put
  1073  * into a SECItem.
  1075  * "pool" specifies a pool from which to allocate the result.
  1076  * It can be NULL, in which case memory is allocated generically.
  1078  * "dest" specifies a SECItem in which to put the result data.
  1079  * It can be NULL, in which case the entire item is allocated, too.
  1081  * "cinfo" specifies the object to be encoded.
  1083  * "bulkkey" specifies the bulk encryption key to use.   This argument
  1084  * can be NULL if no encryption is being done, or if the bulk key should
  1085  * be generated internally (usually the case for EnvelopedData but never
  1086  * for EncryptedData, which *must* provide a bulk encryption key).
  1088  * "pwfn" is a callback for getting the password which protects the
  1089  * private key of the signer.  This argument can be NULL if it is known
  1090  * that no signing is going to be done.
  1092  * "pwfnarg" is an opaque argument to the above callback.
  1093  */
  1094 SECItem *
  1095 SEC_PKCS7EncodeItem (PLArenaPool *pool,
  1096 		     SECItem *dest,
  1097 		     SEC_PKCS7ContentInfo *cinfo,
  1098 		     PK11SymKey *bulkkey,
  1099 		     SECKEYGetPasswordKey pwfn,
  1100 		     void *pwfnarg)
  1102     SECStatus rv;
  1104     rv = SEC_PKCS7PrepareForEncode (cinfo, bulkkey, pwfn, pwfnarg);
  1105     if (rv != SECSuccess)
  1106 	return NULL;
  1108     return SEC_ASN1EncodeItem (pool, dest, cinfo, sec_PKCS7ContentInfoTemplate);

mercurial