1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/nss/lib/smime/cmsencode.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,748 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +/* 1.9 + * CMS encoding. 1.10 + */ 1.11 + 1.12 +#include "cmslocal.h" 1.13 + 1.14 +#include "cert.h" 1.15 +#include "key.h" 1.16 +#include "secasn1.h" 1.17 +#include "secoid.h" 1.18 +#include "secitem.h" 1.19 +#include "pk11func.h" 1.20 +#include "secerr.h" 1.21 + 1.22 +struct nss_cms_encoder_output { 1.23 + NSSCMSContentCallback outputfn; 1.24 + void *outputarg; 1.25 + PLArenaPool *destpoolp; 1.26 + SECItem *dest; 1.27 +}; 1.28 + 1.29 +struct NSSCMSEncoderContextStr { 1.30 + SEC_ASN1EncoderContext * ecx; /* ASN.1 encoder context */ 1.31 + PRBool ecxupdated; /* true if data was handed in */ 1.32 + NSSCMSMessage * cmsg; /* pointer to the root message */ 1.33 + SECOidTag type; /* type tag of the current content */ 1.34 + NSSCMSContent content; /* pointer to current content */ 1.35 + struct nss_cms_encoder_output output; /* output function */ 1.36 + int error; /* error code */ 1.37 + NSSCMSEncoderContext * childp7ecx; /* link to child encoder context */ 1.38 +}; 1.39 + 1.40 +static SECStatus nss_cms_before_data(NSSCMSEncoderContext *p7ecx); 1.41 +static SECStatus nss_cms_after_data(NSSCMSEncoderContext *p7ecx); 1.42 +static SECStatus nss_cms_encoder_update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len); 1.43 +static SECStatus nss_cms_encoder_work_data(NSSCMSEncoderContext *p7ecx, SECItem *dest, 1.44 + const unsigned char *data, unsigned long len, 1.45 + PRBool final, PRBool innermost); 1.46 + 1.47 +extern const SEC_ASN1Template NSSCMSMessageTemplate[]; 1.48 + 1.49 +/* 1.50 + * The little output function that the ASN.1 encoder calls to hand 1.51 + * us bytes which we in turn hand back to our caller (via the callback 1.52 + * they gave us). 1.53 + */ 1.54 +static void 1.55 +nss_cms_encoder_out(void *arg, const char *buf, unsigned long len, 1.56 + int depth, SEC_ASN1EncodingPart data_kind) 1.57 +{ 1.58 + struct nss_cms_encoder_output *output = (struct nss_cms_encoder_output *)arg; 1.59 + unsigned char *dest; 1.60 + unsigned long offset; 1.61 + 1.62 +#ifdef CMSDEBUG 1.63 + int i; 1.64 + const char *data_name = "unknown"; 1.65 + 1.66 + switch (data_kind) { 1.67 + case SEC_ASN1_Identifier: 1.68 + data_name = "identifier"; 1.69 + break; 1.70 + case SEC_ASN1_Length: 1.71 + data_name = "length"; 1.72 + break; 1.73 + case SEC_ASN1_Contents: 1.74 + data_name = "contents"; 1.75 + break; 1.76 + case SEC_ASN1_EndOfContents: 1.77 + data_name = "end-of-contents"; 1.78 + break; 1.79 + } 1.80 + fprintf(stderr, "kind = %s, depth = %d, len = %d\n", data_name, depth, len); 1.81 + for (i=0; i < len; i++) { 1.82 + fprintf(stderr, " %02x%s", (unsigned int)buf[i] & 0xff, ((i % 16) == 15) ? "\n" : ""); 1.83 + } 1.84 + if ((i % 16) != 0) 1.85 + fprintf(stderr, "\n"); 1.86 +#endif 1.87 + 1.88 + if (output->outputfn != NULL) 1.89 + /* call output callback with DER data */ 1.90 + output->outputfn(output->outputarg, buf, len); 1.91 + 1.92 + if (output->dest != NULL) { 1.93 + /* store DER data in SECItem */ 1.94 + offset = output->dest->len; 1.95 + if (offset == 0) { 1.96 + dest = (unsigned char *)PORT_ArenaAlloc(output->destpoolp, len); 1.97 + } else { 1.98 + dest = (unsigned char *)PORT_ArenaGrow(output->destpoolp, 1.99 + output->dest->data, 1.100 + output->dest->len, 1.101 + output->dest->len + len); 1.102 + } 1.103 + if (dest == NULL) 1.104 + /* oops */ 1.105 + return; 1.106 + 1.107 + output->dest->data = dest; 1.108 + output->dest->len += len; 1.109 + 1.110 + /* copy it in */ 1.111 + PORT_Memcpy(output->dest->data + offset, buf, len); 1.112 + } 1.113 +} 1.114 + 1.115 +/* 1.116 + * nss_cms_encoder_notify - ASN.1 encoder callback 1.117 + * 1.118 + * this function is called by the ASN.1 encoder before and after the encoding of 1.119 + * every object. here, it is used to keep track of data structures, set up 1.120 + * encryption and/or digesting and possibly set up child encoders. 1.121 + */ 1.122 +static void 1.123 +nss_cms_encoder_notify(void *arg, PRBool before, void *dest, int depth) 1.124 +{ 1.125 + NSSCMSEncoderContext *p7ecx; 1.126 + NSSCMSContentInfo *rootcinfo, *cinfo; 1.127 + PRBool after = !before; 1.128 + PLArenaPool *poolp; 1.129 + SECOidTag childtype; 1.130 + SECItem *item; 1.131 + 1.132 + p7ecx = (NSSCMSEncoderContext *)arg; 1.133 + PORT_Assert(p7ecx != NULL); 1.134 + 1.135 + rootcinfo = &(p7ecx->cmsg->contentInfo); 1.136 + poolp = p7ecx->cmsg->poolp; 1.137 + 1.138 +#ifdef CMSDEBUG 1.139 + fprintf(stderr, "%6.6s, dest = 0x%08x, depth = %d\n", before ? "before" : "after", dest, depth); 1.140 +#endif 1.141 + 1.142 + /* 1.143 + * Watch for the content field, at which point we want to instruct 1.144 + * the ASN.1 encoder to start taking bytes from the buffer. 1.145 + */ 1.146 + if (NSS_CMSType_IsData(p7ecx->type)) { 1.147 + cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type); 1.148 + if (before && dest == &(cinfo->rawContent)) { 1.149 + /* just set up encoder to grab from user - no encryption or digesting */ 1.150 + if ((item = cinfo->content.data) != NULL) 1.151 + (void)nss_cms_encoder_work_data(p7ecx, NULL, item->data, item->len, PR_TRUE, PR_TRUE); 1.152 + else 1.153 + SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx); 1.154 + SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx); /* no need to get notified anymore */ 1.155 + } 1.156 + } else if (NSS_CMSType_IsWrapper(p7ecx->type)) { 1.157 + /* when we know what the content is, we encode happily until we reach the inner content */ 1.158 + cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type); 1.159 + childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo); 1.160 + 1.161 + if (after && dest == &(cinfo->contentType)) { 1.162 + /* we're right before encoding the data (if we have some or not) */ 1.163 + /* (for encrypted data, we're right before the contentEncAlg which may change */ 1.164 + /* in nss_cms_before_data because of IV calculation when setting up encryption) */ 1.165 + if (nss_cms_before_data(p7ecx) != SECSuccess) 1.166 + p7ecx->error = PORT_GetError(); 1.167 + } 1.168 + if (before && dest == &(cinfo->rawContent)) { 1.169 + if (p7ecx->childp7ecx == NULL) { 1.170 + if ((NSS_CMSType_IsData(childtype) && (item = cinfo->content.data) != NULL)) { 1.171 + /* we are the innermost non-data and we have data - feed it in */ 1.172 + (void)nss_cms_encoder_work_data(p7ecx, NULL, item->data, item->len, PR_TRUE, PR_TRUE); 1.173 + } else { 1.174 + /* else we'll have to get data from user */ 1.175 + SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx); 1.176 + } 1.177 + } else { 1.178 + /* if we have a nested encoder, wait for its data */ 1.179 + SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx); 1.180 + } 1.181 + } 1.182 + if (after && dest == &(cinfo->rawContent)) { 1.183 + if (nss_cms_after_data(p7ecx) != SECSuccess) 1.184 + p7ecx->error = PORT_GetError(); 1.185 + SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx); /* no need to get notified anymore */ 1.186 + } 1.187 + } else { 1.188 + /* we're still in the root message */ 1.189 + if (after && dest == &(rootcinfo->contentType)) { 1.190 + /* got the content type OID now - so find out the type tag */ 1.191 + p7ecx->type = NSS_CMSContentInfo_GetContentTypeTag(rootcinfo); 1.192 + /* set up a pointer to our current content */ 1.193 + p7ecx->content = rootcinfo->content; 1.194 + } 1.195 + } 1.196 +} 1.197 + 1.198 +/* 1.199 + * nss_cms_before_data - setup the current encoder to receive data 1.200 + */ 1.201 +static SECStatus 1.202 +nss_cms_before_data(NSSCMSEncoderContext *p7ecx) 1.203 +{ 1.204 + SECStatus rv; 1.205 + SECOidTag childtype; 1.206 + NSSCMSContentInfo *cinfo; 1.207 + PLArenaPool *poolp; 1.208 + NSSCMSEncoderContext *childp7ecx; 1.209 + const SEC_ASN1Template *template; 1.210 + 1.211 + poolp = p7ecx->cmsg->poolp; 1.212 + 1.213 + /* call _Encode_BeforeData handlers */ 1.214 + switch (p7ecx->type) { 1.215 + case SEC_OID_PKCS7_SIGNED_DATA: 1.216 + /* we're encoding a signedData, so set up the digests */ 1.217 + rv = NSS_CMSSignedData_Encode_BeforeData(p7ecx->content.signedData); 1.218 + break; 1.219 + case SEC_OID_PKCS7_DIGESTED_DATA: 1.220 + /* we're encoding a digestedData, so set up the digest */ 1.221 + rv = NSS_CMSDigestedData_Encode_BeforeData(p7ecx->content.digestedData); 1.222 + break; 1.223 + case SEC_OID_PKCS7_ENVELOPED_DATA: 1.224 + rv = NSS_CMSEnvelopedData_Encode_BeforeData(p7ecx->content.envelopedData); 1.225 + break; 1.226 + case SEC_OID_PKCS7_ENCRYPTED_DATA: 1.227 + rv = NSS_CMSEncryptedData_Encode_BeforeData(p7ecx->content.encryptedData); 1.228 + break; 1.229 + default: 1.230 + if (NSS_CMSType_IsWrapper(p7ecx->type)) { 1.231 + rv = NSS_CMSGenericWrapperData_Encode_BeforeData(p7ecx->type, p7ecx->content.genericData); 1.232 + } else { 1.233 + rv = SECFailure; 1.234 + } 1.235 + } 1.236 + if (rv != SECSuccess) 1.237 + return SECFailure; 1.238 + 1.239 + /* ok, now we have a pointer to cinfo */ 1.240 + /* find out what kind of data is encapsulated */ 1.241 + 1.242 + cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type); 1.243 + childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo); 1.244 + 1.245 + if (NSS_CMSType_IsWrapper(childtype)) { 1.246 + /* in these cases, we need to set up a child encoder! */ 1.247 + /* create new encoder context */ 1.248 + childp7ecx = PORT_ZAlloc(sizeof(NSSCMSEncoderContext)); 1.249 + if (childp7ecx == NULL) 1.250 + return SECFailure; 1.251 + 1.252 + /* the CHILD encoder needs to hand its encoded data to the CURRENT encoder 1.253 + * (which will encrypt and/or digest it) 1.254 + * this needs to route back into our update function 1.255 + * which finds the lowest encoding context & encrypts and computes digests */ 1.256 + childp7ecx->type = childtype; 1.257 + childp7ecx->content = cinfo->content; 1.258 + /* use the non-recursive update function here, of course */ 1.259 + childp7ecx->output.outputfn = (NSSCMSContentCallback)nss_cms_encoder_update; 1.260 + childp7ecx->output.outputarg = p7ecx; 1.261 + childp7ecx->output.destpoolp = NULL; 1.262 + childp7ecx->output.dest = NULL; 1.263 + childp7ecx->cmsg = p7ecx->cmsg; 1.264 + childp7ecx->ecxupdated = PR_FALSE; 1.265 + childp7ecx->childp7ecx = NULL; 1.266 + 1.267 + template = NSS_CMSUtil_GetTemplateByTypeTag(childtype); 1.268 + if (template == NULL) 1.269 + goto loser; /* cannot happen */ 1.270 + 1.271 + /* now initialize the data for encoding the first third */ 1.272 + switch (childp7ecx->type) { 1.273 + case SEC_OID_PKCS7_SIGNED_DATA: 1.274 + rv = NSS_CMSSignedData_Encode_BeforeStart(cinfo->content.signedData); 1.275 + break; 1.276 + case SEC_OID_PKCS7_ENVELOPED_DATA: 1.277 + rv = NSS_CMSEnvelopedData_Encode_BeforeStart(cinfo->content.envelopedData); 1.278 + break; 1.279 + case SEC_OID_PKCS7_DIGESTED_DATA: 1.280 + rv = NSS_CMSDigestedData_Encode_BeforeStart(cinfo->content.digestedData); 1.281 + break; 1.282 + case SEC_OID_PKCS7_ENCRYPTED_DATA: 1.283 + rv = NSS_CMSEncryptedData_Encode_BeforeStart(cinfo->content.encryptedData); 1.284 + break; 1.285 + default: 1.286 + rv = NSS_CMSGenericWrapperData_Encode_BeforeStart(childp7ecx->type, cinfo->content.genericData); 1.287 + break; 1.288 + } 1.289 + if (rv != SECSuccess) 1.290 + goto loser; 1.291 + 1.292 + /* 1.293 + * Initialize the BER encoder. 1.294 + */ 1.295 + childp7ecx->ecx = SEC_ASN1EncoderStart(cinfo->content.pointer, template, 1.296 + nss_cms_encoder_out, &(childp7ecx->output)); 1.297 + if (childp7ecx->ecx == NULL) 1.298 + goto loser; 1.299 + 1.300 + /* 1.301 + * Indicate that we are streaming. We will be streaming until we 1.302 + * get past the contents bytes. 1.303 + */ 1.304 + if (!cinfo->privateInfo || !cinfo->privateInfo->dontStream) 1.305 + SEC_ASN1EncoderSetStreaming(childp7ecx->ecx); 1.306 + 1.307 + /* 1.308 + * The notify function will watch for the contents field. 1.309 + */ 1.310 + p7ecx->childp7ecx = childp7ecx; 1.311 + SEC_ASN1EncoderSetNotifyProc(childp7ecx->ecx, nss_cms_encoder_notify, childp7ecx); 1.312 + 1.313 + /* please note that we are NOT calling SEC_ASN1EncoderUpdate here to kick off the */ 1.314 + /* encoding process - we'll do that from the update function instead */ 1.315 + /* otherwise we'd be encoding data from a call of the notify function of the */ 1.316 + /* parent encoder (which would not work) */ 1.317 + 1.318 + } else if (NSS_CMSType_IsData(childtype)) { 1.319 + p7ecx->childp7ecx = NULL; 1.320 + } else { 1.321 + /* we do not know this type */ 1.322 + p7ecx->error = SEC_ERROR_BAD_DER; 1.323 + } 1.324 + 1.325 + return SECSuccess; 1.326 + 1.327 +loser: 1.328 + if (childp7ecx) { 1.329 + if (childp7ecx->ecx) 1.330 + SEC_ASN1EncoderFinish(childp7ecx->ecx); 1.331 + PORT_Free(childp7ecx); 1.332 + p7ecx->childp7ecx = NULL; 1.333 + } 1.334 + return SECFailure; 1.335 +} 1.336 + 1.337 +static SECStatus 1.338 +nss_cms_after_data(NSSCMSEncoderContext *p7ecx) 1.339 +{ 1.340 + SECStatus rv = SECFailure; 1.341 + 1.342 + switch (p7ecx->type) { 1.343 + case SEC_OID_PKCS7_SIGNED_DATA: 1.344 + /* this will finish the digests and sign */ 1.345 + rv = NSS_CMSSignedData_Encode_AfterData(p7ecx->content.signedData); 1.346 + break; 1.347 + case SEC_OID_PKCS7_ENVELOPED_DATA: 1.348 + rv = NSS_CMSEnvelopedData_Encode_AfterData(p7ecx->content.envelopedData); 1.349 + break; 1.350 + case SEC_OID_PKCS7_DIGESTED_DATA: 1.351 + rv = NSS_CMSDigestedData_Encode_AfterData(p7ecx->content.digestedData); 1.352 + break; 1.353 + case SEC_OID_PKCS7_ENCRYPTED_DATA: 1.354 + rv = NSS_CMSEncryptedData_Encode_AfterData(p7ecx->content.encryptedData); 1.355 + break; 1.356 + default: 1.357 + if (NSS_CMSType_IsWrapper(p7ecx->type)) { 1.358 + rv = NSS_CMSGenericWrapperData_Encode_AfterData(p7ecx->type, p7ecx->content.genericData); 1.359 + } else { 1.360 + rv = SECFailure; 1.361 + } 1.362 + break; 1.363 + } 1.364 + return rv; 1.365 +} 1.366 + 1.367 +/* 1.368 + * nss_cms_encoder_work_data - process incoming data 1.369 + * 1.370 + * (from the user or the next encoding layer) 1.371 + * Here, we need to digest and/or encrypt, then pass it on 1.372 + */ 1.373 +static SECStatus 1.374 +nss_cms_encoder_work_data(NSSCMSEncoderContext *p7ecx, SECItem *dest, 1.375 + const unsigned char *data, unsigned long len, 1.376 + PRBool final, PRBool innermost) 1.377 +{ 1.378 + unsigned char *buf = NULL; 1.379 + SECStatus rv; 1.380 + NSSCMSContentInfo *cinfo; 1.381 + 1.382 + rv = SECSuccess; /* may as well be optimistic */ 1.383 + 1.384 + /* 1.385 + * We should really have data to process, or we should be trying 1.386 + * to finish/flush the last block. (This is an overly paranoid 1.387 + * check since all callers are in this file and simple inspection 1.388 + * proves they do it right. But it could find a bug in future 1.389 + * modifications/development, that is why it is here.) 1.390 + */ 1.391 + PORT_Assert ((data != NULL && len) || final); 1.392 + 1.393 + /* we got data (either from the caller, or from a lower level encoder) */ 1.394 + cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type); 1.395 + if (!cinfo) { 1.396 + /* The original programmer didn't expect this to happen */ 1.397 + p7ecx->error = SEC_ERROR_LIBRARY_FAILURE; 1.398 + return SECFailure; 1.399 + } 1.400 + 1.401 + /* Update the running digest. */ 1.402 + if (len && cinfo->privateInfo && cinfo->privateInfo->digcx != NULL) 1.403 + NSS_CMSDigestContext_Update(cinfo->privateInfo->digcx, data, len); 1.404 + 1.405 + /* Encrypt this chunk. */ 1.406 + if (cinfo->privateInfo && cinfo->privateInfo->ciphcx != NULL) { 1.407 + unsigned int inlen; /* length of data being encrypted */ 1.408 + unsigned int outlen; /* length of encrypted data */ 1.409 + unsigned int buflen; /* length available for encrypted data */ 1.410 + 1.411 + inlen = len; 1.412 + buflen = NSS_CMSCipherContext_EncryptLength(cinfo->privateInfo->ciphcx, inlen, final); 1.413 + if (buflen == 0) { 1.414 + /* 1.415 + * No output is expected, but the input data may be buffered 1.416 + * so we still have to call Encrypt. 1.417 + */ 1.418 + rv = NSS_CMSCipherContext_Encrypt(cinfo->privateInfo->ciphcx, NULL, NULL, 0, 1.419 + data, inlen, final); 1.420 + if (final) { 1.421 + len = 0; 1.422 + goto done; 1.423 + } 1.424 + return rv; 1.425 + } 1.426 + 1.427 + if (dest != NULL) 1.428 + buf = (unsigned char*)PORT_ArenaAlloc(p7ecx->cmsg->poolp, buflen); 1.429 + else 1.430 + buf = (unsigned char*)PORT_Alloc(buflen); 1.431 + 1.432 + if (buf == NULL) { 1.433 + rv = SECFailure; 1.434 + } else { 1.435 + rv = NSS_CMSCipherContext_Encrypt(cinfo->privateInfo->ciphcx, buf, &outlen, buflen, 1.436 + data, inlen, final); 1.437 + data = buf; 1.438 + len = outlen; 1.439 + } 1.440 + if (rv != SECSuccess) 1.441 + /* encryption or malloc failed? */ 1.442 + return rv; 1.443 + } 1.444 + 1.445 + 1.446 + /* 1.447 + * at this point (data,len) has everything we'd like to give to the CURRENT encoder 1.448 + * (which will encode it, then hand it back to the user or the parent encoder) 1.449 + * We don't encode the data if we're innermost and we're told not to include the data 1.450 + */ 1.451 + if (p7ecx->ecx != NULL && len && (!innermost || cinfo->rawContent != cinfo->content.pointer)) 1.452 + rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, (const char *)data, len); 1.453 + 1.454 +done: 1.455 + 1.456 + if (cinfo->privateInfo && cinfo->privateInfo->ciphcx != NULL) { 1.457 + if (dest != NULL) { 1.458 + dest->data = buf; 1.459 + dest->len = len; 1.460 + } else if (buf != NULL) { 1.461 + PORT_Free (buf); 1.462 + } 1.463 + } 1.464 + return rv; 1.465 +} 1.466 + 1.467 +/* 1.468 + * nss_cms_encoder_update - deliver encoded data to the next higher level 1.469 + * 1.470 + * no recursion here because we REALLY want to end up at the next higher encoder! 1.471 + */ 1.472 +static SECStatus 1.473 +nss_cms_encoder_update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len) 1.474 +{ 1.475 + /* XXX Error handling needs help. Return what? Do "Finish" on failure? */ 1.476 + return nss_cms_encoder_work_data (p7ecx, NULL, (const unsigned char *)data, len, PR_FALSE, PR_FALSE); 1.477 +} 1.478 + 1.479 +/* 1.480 + * NSS_CMSEncoder_Start - set up encoding of a CMS message 1.481 + * 1.482 + * "cmsg" - message to encode 1.483 + * "outputfn", "outputarg" - callback function for delivery of DER-encoded output 1.484 + * will not be called if NULL. 1.485 + * "dest" - if non-NULL, pointer to SECItem that will hold the DER-encoded output 1.486 + * "destpoolp" - pool to allocate DER-encoded output in 1.487 + * "pwfn", pwfn_arg" - callback function for getting token password 1.488 + * "decrypt_key_cb", "decrypt_key_cb_arg" - callback function for getting bulk key for encryptedData 1.489 + * "detached_digestalgs", "detached_digests" - digests from detached content 1.490 + */ 1.491 +NSSCMSEncoderContext * 1.492 +NSS_CMSEncoder_Start(NSSCMSMessage *cmsg, 1.493 + NSSCMSContentCallback outputfn, void *outputarg, 1.494 + SECItem *dest, PLArenaPool *destpoolp, 1.495 + PK11PasswordFunc pwfn, void *pwfn_arg, 1.496 + NSSCMSGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg, 1.497 + SECAlgorithmID **detached_digestalgs, SECItem **detached_digests) 1.498 +{ 1.499 + NSSCMSEncoderContext *p7ecx; 1.500 + SECStatus rv; 1.501 + NSSCMSContentInfo *cinfo; 1.502 + SECOidTag tag; 1.503 + 1.504 + NSS_CMSMessage_SetEncodingParams(cmsg, pwfn, pwfn_arg, decrypt_key_cb, decrypt_key_cb_arg, 1.505 + detached_digestalgs, detached_digests); 1.506 + 1.507 + p7ecx = (NSSCMSEncoderContext *)PORT_ZAlloc(sizeof(NSSCMSEncoderContext)); 1.508 + if (p7ecx == NULL) { 1.509 + PORT_SetError(SEC_ERROR_NO_MEMORY); 1.510 + return NULL; 1.511 + } 1.512 + 1.513 + p7ecx->cmsg = cmsg; 1.514 + p7ecx->output.outputfn = outputfn; 1.515 + p7ecx->output.outputarg = outputarg; 1.516 + p7ecx->output.dest = dest; 1.517 + p7ecx->output.destpoolp = destpoolp; 1.518 + p7ecx->type = SEC_OID_UNKNOWN; 1.519 + 1.520 + cinfo = NSS_CMSMessage_GetContentInfo(cmsg); 1.521 + 1.522 + tag = NSS_CMSContentInfo_GetContentTypeTag(cinfo); 1.523 + switch (tag) { 1.524 + case SEC_OID_PKCS7_SIGNED_DATA: 1.525 + rv = NSS_CMSSignedData_Encode_BeforeStart(cinfo->content.signedData); 1.526 + break; 1.527 + case SEC_OID_PKCS7_ENVELOPED_DATA: 1.528 + rv = NSS_CMSEnvelopedData_Encode_BeforeStart(cinfo->content.envelopedData); 1.529 + break; 1.530 + case SEC_OID_PKCS7_DIGESTED_DATA: 1.531 + rv = NSS_CMSDigestedData_Encode_BeforeStart(cinfo->content.digestedData); 1.532 + break; 1.533 + case SEC_OID_PKCS7_ENCRYPTED_DATA: 1.534 + rv = NSS_CMSEncryptedData_Encode_BeforeStart(cinfo->content.encryptedData); 1.535 + break; 1.536 + default: 1.537 + if (NSS_CMSType_IsWrapper(tag)) { 1.538 + rv = NSS_CMSGenericWrapperData_Encode_BeforeStart(tag, 1.539 + p7ecx->content.genericData); 1.540 + } else { 1.541 + rv = SECFailure; 1.542 + } 1.543 + break; 1.544 + } 1.545 + if (rv != SECSuccess) { 1.546 + PORT_Free(p7ecx); 1.547 + return NULL; 1.548 + } 1.549 + 1.550 + /* Initialize the BER encoder. 1.551 + * Note that this will not encode anything until the first call to SEC_ASN1EncoderUpdate */ 1.552 + p7ecx->ecx = SEC_ASN1EncoderStart(cmsg, NSSCMSMessageTemplate, 1.553 + nss_cms_encoder_out, &(p7ecx->output)); 1.554 + if (p7ecx->ecx == NULL) { 1.555 + PORT_Free (p7ecx); 1.556 + return NULL; 1.557 + } 1.558 + p7ecx->ecxupdated = PR_FALSE; 1.559 + 1.560 + /* 1.561 + * Indicate that we are streaming. We will be streaming until we 1.562 + * get past the contents bytes. 1.563 + */ 1.564 + if (!cinfo->privateInfo || !cinfo->privateInfo->dontStream) 1.565 + SEC_ASN1EncoderSetStreaming(p7ecx->ecx); 1.566 + 1.567 + /* 1.568 + * The notify function will watch for the contents field. 1.569 + */ 1.570 + SEC_ASN1EncoderSetNotifyProc(p7ecx->ecx, nss_cms_encoder_notify, p7ecx); 1.571 + 1.572 + /* this will kick off the encoding process & encode everything up to the content bytes, 1.573 + * at which point the notify function sets streaming mode (and possibly creates 1.574 + * a child encoder). */ 1.575 + p7ecx->ecxupdated = PR_TRUE; 1.576 + if (SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0) != SECSuccess) { 1.577 + PORT_Free (p7ecx); 1.578 + return NULL; 1.579 + } 1.580 + 1.581 + return p7ecx; 1.582 +} 1.583 + 1.584 +/* 1.585 + * NSS_CMSEncoder_Update - take content data delivery from the user 1.586 + * 1.587 + * "p7ecx" - encoder context 1.588 + * "data" - content data 1.589 + * "len" - length of content data 1.590 + * 1.591 + * need to find the lowest level (and call SEC_ASN1EncoderUpdate on the way down), 1.592 + * then hand the data to the work_data fn 1.593 + */ 1.594 +SECStatus 1.595 +NSS_CMSEncoder_Update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len) 1.596 +{ 1.597 + SECStatus rv; 1.598 + NSSCMSContentInfo *cinfo; 1.599 + SECOidTag childtype; 1.600 + 1.601 + if (p7ecx->error) 1.602 + return SECFailure; 1.603 + 1.604 + /* hand data to the innermost decoder */ 1.605 + if (p7ecx->childp7ecx) { 1.606 + /* tell the child to start encoding, up to its first data byte, if it 1.607 + * hasn't started yet */ 1.608 + if (!p7ecx->childp7ecx->ecxupdated) { 1.609 + p7ecx->childp7ecx->ecxupdated = PR_TRUE; 1.610 + if (SEC_ASN1EncoderUpdate(p7ecx->childp7ecx->ecx, NULL, 0) != SECSuccess) 1.611 + return SECFailure; 1.612 + } 1.613 + /* recursion here */ 1.614 + rv = NSS_CMSEncoder_Update(p7ecx->childp7ecx, data, len); 1.615 + } else { 1.616 + /* we are at innermost decoder */ 1.617 + /* find out about our inner content type - must be data */ 1.618 + cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type); 1.619 + if (!cinfo) { 1.620 + /* The original programmer didn't expect this to happen */ 1.621 + p7ecx->error = SEC_ERROR_LIBRARY_FAILURE; 1.622 + return SECFailure; 1.623 + } 1.624 + 1.625 + childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo); 1.626 + if (!NSS_CMSType_IsData(childtype)) 1.627 + return SECFailure; 1.628 + /* and we must not have preset data */ 1.629 + if (cinfo->content.data != NULL) 1.630 + return SECFailure; 1.631 + 1.632 + /* hand it the data so it can encode it (let DER trickle up the chain) */ 1.633 + rv = nss_cms_encoder_work_data(p7ecx, NULL, (const unsigned char *)data, len, PR_FALSE, PR_TRUE); 1.634 + } 1.635 + return rv; 1.636 +} 1.637 + 1.638 +/* 1.639 + * NSS_CMSEncoder_Cancel - stop all encoding 1.640 + * 1.641 + * we need to walk down the chain of encoders and the finish them from the innermost out 1.642 + */ 1.643 +SECStatus 1.644 +NSS_CMSEncoder_Cancel(NSSCMSEncoderContext *p7ecx) 1.645 +{ 1.646 + SECStatus rv = SECFailure; 1.647 + 1.648 + /* XXX do this right! */ 1.649 + 1.650 + /* 1.651 + * Finish any inner decoders before us so that all the encoded data is flushed 1.652 + * This basically finishes all the decoders from the innermost to the outermost. 1.653 + * Finishing an inner decoder may result in data being updated to the outer decoder 1.654 + * while we are already in NSS_CMSEncoder_Finish, but that's allright. 1.655 + */ 1.656 + if (p7ecx->childp7ecx) { 1.657 + rv = NSS_CMSEncoder_Cancel(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */ 1.658 + /* remember rv for now */ 1.659 + } 1.660 + 1.661 + /* 1.662 + * On the way back up, there will be no more data (if we had an 1.663 + * inner encoder, it is done now!) 1.664 + * Flush out any remaining data and/or finish digests. 1.665 + */ 1.666 + rv = nss_cms_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE, (p7ecx->childp7ecx == NULL)); 1.667 + if (rv != SECSuccess) 1.668 + goto loser; 1.669 + 1.670 + p7ecx->childp7ecx = NULL; 1.671 + 1.672 + /* kick the encoder back into working mode again. 1.673 + * We turn off streaming stuff (which will cause the encoder to continue 1.674 + * encoding happily, now that we have all the data (like digests) ready for it). 1.675 + */ 1.676 + SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx); 1.677 + SEC_ASN1EncoderClearStreaming(p7ecx->ecx); 1.678 + 1.679 + /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */ 1.680 + rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0); 1.681 + 1.682 +loser: 1.683 + SEC_ASN1EncoderFinish(p7ecx->ecx); 1.684 + PORT_Free (p7ecx); 1.685 + return rv; 1.686 +} 1.687 + 1.688 +/* 1.689 + * NSS_CMSEncoder_Finish - signal the end of data 1.690 + * 1.691 + * we need to walk down the chain of encoders and the finish them from the innermost out 1.692 + */ 1.693 +SECStatus 1.694 +NSS_CMSEncoder_Finish(NSSCMSEncoderContext *p7ecx) 1.695 +{ 1.696 + SECStatus rv = SECFailure; 1.697 + NSSCMSContentInfo *cinfo; 1.698 + 1.699 + /* 1.700 + * Finish any inner decoders before us so that all the encoded data is flushed 1.701 + * This basically finishes all the decoders from the innermost to the outermost. 1.702 + * Finishing an inner decoder may result in data being updated to the outer decoder 1.703 + * while we are already in NSS_CMSEncoder_Finish, but that's allright. 1.704 + */ 1.705 + if (p7ecx->childp7ecx) { 1.706 + /* tell the child to start encoding, up to its first data byte, if it 1.707 + * hasn't yet */ 1.708 + if (!p7ecx->childp7ecx->ecxupdated) { 1.709 + p7ecx->childp7ecx->ecxupdated = PR_TRUE; 1.710 + rv = SEC_ASN1EncoderUpdate(p7ecx->childp7ecx->ecx, NULL, 0); 1.711 + if (rv != SECSuccess) { 1.712 + NSS_CMSEncoder_Finish(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */ 1.713 + goto loser; 1.714 + } 1.715 + } 1.716 + rv = NSS_CMSEncoder_Finish(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */ 1.717 + if (rv != SECSuccess) 1.718 + goto loser; 1.719 + } 1.720 + 1.721 + /* 1.722 + * On the way back up, there will be no more data (if we had an 1.723 + * inner encoder, it is done now!) 1.724 + * Flush out any remaining data and/or finish digests. 1.725 + */ 1.726 + rv = nss_cms_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE, (p7ecx->childp7ecx == NULL)); 1.727 + if (rv != SECSuccess) 1.728 + goto loser; 1.729 + 1.730 + p7ecx->childp7ecx = NULL; 1.731 + 1.732 + cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type); 1.733 + if (!cinfo) { 1.734 + /* The original programmer didn't expect this to happen */ 1.735 + p7ecx->error = SEC_ERROR_LIBRARY_FAILURE; 1.736 + rv = SECFailure; 1.737 + goto loser; 1.738 + } 1.739 + SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx); 1.740 + SEC_ASN1EncoderClearStreaming(p7ecx->ecx); 1.741 + /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */ 1.742 + rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0); 1.743 + 1.744 + if (p7ecx->error) 1.745 + rv = SECFailure; 1.746 + 1.747 +loser: 1.748 + SEC_ASN1EncoderFinish(p7ecx->ecx); 1.749 + PORT_Free (p7ecx); 1.750 + return rv; 1.751 +}