/security/nss/lib/pkcs7/p7create.c

http://github.com/zpao/v8monkey · C · 1321 lines · 815 code · 189 blank · 317 comment · 244 complexity · 5a3719ac7dea45a48e1d20922c484377 MD5 · raw file

  1. /* ***** BEGIN LICENSE BLOCK *****
  2. * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3. *
  4. * The contents of this file are subject to the Mozilla Public License Version
  5. * 1.1 (the "License"); you may not use this file except in compliance with
  6. * the License. You may obtain a copy of the License at
  7. * http://www.mozilla.org/MPL/
  8. *
  9. * Software distributed under the License is distributed on an "AS IS" basis,
  10. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11. * for the specific language governing rights and limitations under the
  12. * License.
  13. *
  14. * The Original Code is the Netscape security libraries.
  15. *
  16. * The Initial Developer of the Original Code is
  17. * Netscape Communications Corporation.
  18. * Portions created by the Initial Developer are Copyright (C) 1994-2000
  19. * the Initial Developer. All Rights Reserved.
  20. *
  21. * Contributor(s):
  22. *
  23. * Alternatively, the contents of this file may be used under the terms of
  24. * either the GNU General Public License Version 2 or later (the "GPL"), or
  25. * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  26. * in which case the provisions of the GPL or the LGPL are applicable instead
  27. * of those above. If you wish to allow use of your version of this file only
  28. * under the terms of either the GPL or the LGPL, and not to allow others to
  29. * use your version of this file under the terms of the MPL, indicate your
  30. * decision by deleting the provisions above and replace them with the notice
  31. * and other provisions required by the GPL or the LGPL. If you do not delete
  32. * the provisions above, a recipient may use your version of this file under
  33. * the terms of any one of the MPL, the GPL or the LGPL.
  34. *
  35. * ***** END LICENSE BLOCK ***** */
  36. /*
  37. * PKCS7 creation.
  38. *
  39. * $Id: p7create.c,v 1.9 2008/02/03 06:08:48 nelson%bolyard.com Exp $
  40. */
  41. #include "p7local.h"
  42. #include "cert.h"
  43. #include "secasn1.h"
  44. #include "secitem.h"
  45. #include "secoid.h"
  46. #include "pk11func.h"
  47. #include "prtime.h"
  48. #include "secerr.h"
  49. #include "secder.h"
  50. #include "secpkcs5.h"
  51. static SECStatus
  52. sec_pkcs7_init_content_info (SEC_PKCS7ContentInfo *cinfo, PRArenaPool *poolp,
  53. SECOidTag kind, PRBool detached)
  54. {
  55. void *thing;
  56. int version;
  57. SECItem *versionp;
  58. SECStatus rv;
  59. PORT_Assert (cinfo != NULL && poolp != NULL);
  60. if (cinfo == NULL || poolp == NULL)
  61. return SECFailure;
  62. cinfo->contentTypeTag = SECOID_FindOIDByTag (kind);
  63. PORT_Assert (cinfo->contentTypeTag
  64. && cinfo->contentTypeTag->offset == kind);
  65. rv = SECITEM_CopyItem (poolp, &(cinfo->contentType),
  66. &(cinfo->contentTypeTag->oid));
  67. if (rv != SECSuccess)
  68. return rv;
  69. if (detached)
  70. return SECSuccess;
  71. switch (kind) {
  72. default:
  73. case SEC_OID_PKCS7_DATA:
  74. thing = PORT_ArenaZAlloc (poolp, sizeof(SECItem));
  75. cinfo->content.data = (SECItem*)thing;
  76. versionp = NULL;
  77. version = -1;
  78. break;
  79. case SEC_OID_PKCS7_DIGESTED_DATA:
  80. thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7DigestedData));
  81. cinfo->content.digestedData = (SEC_PKCS7DigestedData*)thing;
  82. versionp = &(cinfo->content.digestedData->version);
  83. version = SEC_PKCS7_DIGESTED_DATA_VERSION;
  84. break;
  85. case SEC_OID_PKCS7_ENCRYPTED_DATA:
  86. thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7EncryptedData));
  87. cinfo->content.encryptedData = (SEC_PKCS7EncryptedData*)thing;
  88. versionp = &(cinfo->content.encryptedData->version);
  89. version = SEC_PKCS7_ENCRYPTED_DATA_VERSION;
  90. break;
  91. case SEC_OID_PKCS7_ENVELOPED_DATA:
  92. thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7EnvelopedData));
  93. cinfo->content.envelopedData =
  94. (SEC_PKCS7EnvelopedData*)thing;
  95. versionp = &(cinfo->content.envelopedData->version);
  96. version = SEC_PKCS7_ENVELOPED_DATA_VERSION;
  97. break;
  98. case SEC_OID_PKCS7_SIGNED_DATA:
  99. thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7SignedData));
  100. cinfo->content.signedData =
  101. (SEC_PKCS7SignedData*)thing;
  102. versionp = &(cinfo->content.signedData->version);
  103. version = SEC_PKCS7_SIGNED_DATA_VERSION;
  104. break;
  105. case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
  106. thing = PORT_ArenaZAlloc(poolp,sizeof(SEC_PKCS7SignedAndEnvelopedData));
  107. cinfo->content.signedAndEnvelopedData =
  108. (SEC_PKCS7SignedAndEnvelopedData*)thing;
  109. versionp = &(cinfo->content.signedAndEnvelopedData->version);
  110. version = SEC_PKCS7_SIGNED_AND_ENVELOPED_DATA_VERSION;
  111. break;
  112. }
  113. if (thing == NULL)
  114. return SECFailure;
  115. if (versionp != NULL) {
  116. SECItem *dummy;
  117. PORT_Assert (version >= 0);
  118. dummy = SEC_ASN1EncodeInteger (poolp, versionp, version);
  119. if (dummy == NULL)
  120. return SECFailure;
  121. PORT_Assert (dummy == versionp);
  122. }
  123. return SECSuccess;
  124. }
  125. static SEC_PKCS7ContentInfo *
  126. sec_pkcs7_create_content_info (SECOidTag kind, PRBool detached,
  127. SECKEYGetPasswordKey pwfn, void *pwfn_arg)
  128. {
  129. SEC_PKCS7ContentInfo *cinfo;
  130. PRArenaPool *poolp;
  131. SECStatus rv;
  132. poolp = PORT_NewArena (1024); /* XXX what is right value? */
  133. if (poolp == NULL)
  134. return NULL;
  135. cinfo = (SEC_PKCS7ContentInfo*)PORT_ArenaZAlloc (poolp, sizeof(*cinfo));
  136. if (cinfo == NULL) {
  137. PORT_FreeArena (poolp, PR_FALSE);
  138. return NULL;
  139. }
  140. cinfo->poolp = poolp;
  141. cinfo->pwfn = pwfn;
  142. cinfo->pwfn_arg = pwfn_arg;
  143. cinfo->created = PR_TRUE;
  144. cinfo->refCount = 1;
  145. rv = sec_pkcs7_init_content_info (cinfo, poolp, kind, detached);
  146. if (rv != SECSuccess) {
  147. PORT_FreeArena (poolp, PR_FALSE);
  148. return NULL;
  149. }
  150. return cinfo;
  151. }
  152. /*
  153. * Add a signer to a PKCS7 thing, verifying the signature cert first.
  154. * Any error returns SECFailure.
  155. *
  156. * XXX Right now this only adds the *first* signer. It fails if you try
  157. * to add a second one -- this needs to be fixed.
  158. */
  159. static SECStatus
  160. sec_pkcs7_add_signer (SEC_PKCS7ContentInfo *cinfo,
  161. CERTCertificate * cert,
  162. SECCertUsage certusage,
  163. CERTCertDBHandle * certdb,
  164. SECOidTag digestalgtag,
  165. SECItem * digestdata)
  166. {
  167. SEC_PKCS7SignerInfo *signerinfo, **signerinfos, ***signerinfosp;
  168. SECAlgorithmID *digestalg, **digestalgs, ***digestalgsp;
  169. SECItem *digest, **digests, ***digestsp;
  170. SECItem * dummy;
  171. void * mark;
  172. SECStatus rv;
  173. SECOidTag kind;
  174. kind = SEC_PKCS7ContentType (cinfo);
  175. switch (kind) {
  176. case SEC_OID_PKCS7_SIGNED_DATA:
  177. {
  178. SEC_PKCS7SignedData *sdp;
  179. sdp = cinfo->content.signedData;
  180. digestalgsp = &(sdp->digestAlgorithms);
  181. digestsp = &(sdp->digests);
  182. signerinfosp = &(sdp->signerInfos);
  183. }
  184. break;
  185. case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
  186. {
  187. SEC_PKCS7SignedAndEnvelopedData *saedp;
  188. saedp = cinfo->content.signedAndEnvelopedData;
  189. digestalgsp = &(saedp->digestAlgorithms);
  190. digestsp = &(saedp->digests);
  191. signerinfosp = &(saedp->signerInfos);
  192. }
  193. break;
  194. default:
  195. return SECFailure; /* XXX set an error? */
  196. }
  197. /*
  198. * XXX I think that CERT_VerifyCert should do this if *it* is passed
  199. * a NULL database.
  200. */
  201. if (certdb == NULL) {
  202. certdb = CERT_GetDefaultCertDB();
  203. if (certdb == NULL)
  204. return SECFailure; /* XXX set an error? */
  205. }
  206. if (CERT_VerifyCert (certdb, cert, PR_TRUE, certusage, PR_Now(),
  207. cinfo->pwfn_arg, NULL) != SECSuccess)
  208. {
  209. /* XXX Did CERT_VerifyCert set an error? */
  210. return SECFailure;
  211. }
  212. /*
  213. * XXX This is the check that we do not already have a signer.
  214. * This is not what we really want -- we want to allow this
  215. * and *add* the new signer.
  216. */
  217. PORT_Assert (*signerinfosp == NULL
  218. && *digestalgsp == NULL && *digestsp == NULL);
  219. if (*signerinfosp != NULL || *digestalgsp != NULL || *digestsp != NULL)
  220. return SECFailure;
  221. mark = PORT_ArenaMark (cinfo->poolp);
  222. signerinfo = (SEC_PKCS7SignerInfo*)PORT_ArenaZAlloc (cinfo->poolp,
  223. sizeof(SEC_PKCS7SignerInfo));
  224. if (signerinfo == NULL) {
  225. PORT_ArenaRelease (cinfo->poolp, mark);
  226. return SECFailure;
  227. }
  228. dummy = SEC_ASN1EncodeInteger (cinfo->poolp, &signerinfo->version,
  229. SEC_PKCS7_SIGNER_INFO_VERSION);
  230. if (dummy == NULL) {
  231. PORT_ArenaRelease (cinfo->poolp, mark);
  232. return SECFailure;
  233. }
  234. PORT_Assert (dummy == &signerinfo->version);
  235. signerinfo->cert = CERT_DupCertificate (cert);
  236. if (signerinfo->cert == NULL) {
  237. PORT_ArenaRelease (cinfo->poolp, mark);
  238. return SECFailure;
  239. }
  240. signerinfo->issuerAndSN = CERT_GetCertIssuerAndSN (cinfo->poolp, cert);
  241. if (signerinfo->issuerAndSN == NULL) {
  242. PORT_ArenaRelease (cinfo->poolp, mark);
  243. return SECFailure;
  244. }
  245. rv = SECOID_SetAlgorithmID (cinfo->poolp, &signerinfo->digestAlg,
  246. digestalgtag, NULL);
  247. if (rv != SECSuccess) {
  248. PORT_ArenaRelease (cinfo->poolp, mark);
  249. return SECFailure;
  250. }
  251. /*
  252. * Okay, now signerinfo is all set. We just need to put it and its
  253. * companions (another copy of the digest algorithm, and the digest
  254. * itself if given) into the main structure.
  255. *
  256. * XXX If we are handling more than one signer, the following code
  257. * needs to look through the digest algorithms already specified
  258. * and see if the same one is there already. If it is, it does not
  259. * need to be added again. Also, if it is there *and* the digest
  260. * is not null, then the digest given should match the digest already
  261. * specified -- if not, that is an error. Finally, the new signerinfo
  262. * should be *added* to the set already found.
  263. */
  264. signerinfos = (SEC_PKCS7SignerInfo**)PORT_ArenaAlloc (cinfo->poolp,
  265. 2 * sizeof(SEC_PKCS7SignerInfo *));
  266. if (signerinfos == NULL) {
  267. PORT_ArenaRelease (cinfo->poolp, mark);
  268. return SECFailure;
  269. }
  270. signerinfos[0] = signerinfo;
  271. signerinfos[1] = NULL;
  272. digestalg = PORT_ArenaZAlloc (cinfo->poolp, sizeof(SECAlgorithmID));
  273. digestalgs = PORT_ArenaAlloc (cinfo->poolp, 2 * sizeof(SECAlgorithmID *));
  274. if (digestalg == NULL || digestalgs == NULL) {
  275. PORT_ArenaRelease (cinfo->poolp, mark);
  276. return SECFailure;
  277. }
  278. rv = SECOID_SetAlgorithmID (cinfo->poolp, digestalg, digestalgtag, NULL);
  279. if (rv != SECSuccess) {
  280. PORT_ArenaRelease (cinfo->poolp, mark);
  281. return SECFailure;
  282. }
  283. digestalgs[0] = digestalg;
  284. digestalgs[1] = NULL;
  285. if (digestdata != NULL) {
  286. digest = (SECItem*)PORT_ArenaAlloc (cinfo->poolp, sizeof(SECItem));
  287. digests = (SECItem**)PORT_ArenaAlloc (cinfo->poolp,
  288. 2 * sizeof(SECItem *));
  289. if (digest == NULL || digests == NULL) {
  290. PORT_ArenaRelease (cinfo->poolp, mark);
  291. return SECFailure;
  292. }
  293. rv = SECITEM_CopyItem (cinfo->poolp, digest, digestdata);
  294. if (rv != SECSuccess) {
  295. PORT_ArenaRelease (cinfo->poolp, mark);
  296. return SECFailure;
  297. }
  298. digests[0] = digest;
  299. digests[1] = NULL;
  300. } else {
  301. digests = NULL;
  302. }
  303. *signerinfosp = signerinfos;
  304. *digestalgsp = digestalgs;
  305. *digestsp = digests;
  306. PORT_ArenaUnmark(cinfo->poolp, mark);
  307. return SECSuccess;
  308. }
  309. /*
  310. * Helper function for creating an empty signedData.
  311. */
  312. static SEC_PKCS7ContentInfo *
  313. sec_pkcs7_create_signed_data (SECKEYGetPasswordKey pwfn, void *pwfn_arg)
  314. {
  315. SEC_PKCS7ContentInfo *cinfo;
  316. SEC_PKCS7SignedData *sigd;
  317. SECStatus rv;
  318. cinfo = sec_pkcs7_create_content_info (SEC_OID_PKCS7_SIGNED_DATA, PR_FALSE,
  319. pwfn, pwfn_arg);
  320. if (cinfo == NULL)
  321. return NULL;
  322. sigd = cinfo->content.signedData;
  323. PORT_Assert (sigd != NULL);
  324. /*
  325. * XXX Might we want to allow content types other than data?
  326. * If so, via what interface?
  327. */
  328. rv = sec_pkcs7_init_content_info (&(sigd->contentInfo), cinfo->poolp,
  329. SEC_OID_PKCS7_DATA, PR_TRUE);
  330. if (rv != SECSuccess) {
  331. SEC_PKCS7DestroyContentInfo (cinfo);
  332. return NULL;
  333. }
  334. return cinfo;
  335. }
  336. /*
  337. * Start a PKCS7 signing context.
  338. *
  339. * "cert" is the cert that will be used to sign the data. It will be
  340. * checked for validity.
  341. *
  342. * "certusage" describes the signing usage (e.g. certUsageEmailSigner)
  343. * XXX Maybe SECCertUsage should be split so that our caller just says
  344. * "email" and *we* add the "signing" part -- otherwise our caller
  345. * could be lying about the usage; we do not want to allow encryption
  346. * certs for signing or vice versa.
  347. *
  348. * "certdb" is the cert database to use for verifying the cert.
  349. * It can be NULL if a default database is available (like in the client).
  350. *
  351. * "digestalg" names the digest algorithm (e.g. SEC_OID_SHA1).
  352. *
  353. * "digest" is the actual digest of the data. It must be provided in
  354. * the case of detached data or NULL if the content will be included.
  355. *
  356. * The return value can be passed to functions which add things to
  357. * it like attributes, then eventually to SEC_PKCS7Encode() or to
  358. * SEC_PKCS7EncoderStart() to create the encoded data, and finally to
  359. * SEC_PKCS7DestroyContentInfo().
  360. *
  361. * An error results in a return value of NULL and an error set.
  362. * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
  363. */
  364. SEC_PKCS7ContentInfo *
  365. SEC_PKCS7CreateSignedData (CERTCertificate *cert,
  366. SECCertUsage certusage,
  367. CERTCertDBHandle *certdb,
  368. SECOidTag digestalg,
  369. SECItem *digest,
  370. SECKEYGetPasswordKey pwfn, void *pwfn_arg)
  371. {
  372. SEC_PKCS7ContentInfo *cinfo;
  373. SECStatus rv;
  374. cinfo = sec_pkcs7_create_signed_data (pwfn, pwfn_arg);
  375. if (cinfo == NULL)
  376. return NULL;
  377. rv = sec_pkcs7_add_signer (cinfo, cert, certusage, certdb,
  378. digestalg, digest);
  379. if (rv != SECSuccess) {
  380. SEC_PKCS7DestroyContentInfo (cinfo);
  381. return NULL;
  382. }
  383. return cinfo;
  384. }
  385. static SEC_PKCS7Attribute *
  386. sec_pkcs7_create_attribute (PRArenaPool *poolp, SECOidTag oidtag,
  387. SECItem *value, PRBool encoded)
  388. {
  389. SEC_PKCS7Attribute *attr;
  390. SECItem **values;
  391. void *mark;
  392. PORT_Assert (poolp != NULL);
  393. mark = PORT_ArenaMark (poolp);
  394. attr = (SEC_PKCS7Attribute*)PORT_ArenaAlloc (poolp,
  395. sizeof(SEC_PKCS7Attribute));
  396. if (attr == NULL)
  397. goto loser;
  398. attr->typeTag = SECOID_FindOIDByTag (oidtag);
  399. if (attr->typeTag == NULL)
  400. goto loser;
  401. if (SECITEM_CopyItem (poolp, &(attr->type),
  402. &(attr->typeTag->oid)) != SECSuccess)
  403. goto loser;
  404. values = (SECItem**)PORT_ArenaAlloc (poolp, 2 * sizeof(SECItem *));
  405. if (values == NULL)
  406. goto loser;
  407. if (value != NULL) {
  408. SECItem *copy;
  409. copy = (SECItem*)PORT_ArenaAlloc (poolp, sizeof(SECItem));
  410. if (copy == NULL)
  411. goto loser;
  412. if (SECITEM_CopyItem (poolp, copy, value) != SECSuccess)
  413. goto loser;
  414. value = copy;
  415. }
  416. values[0] = value;
  417. values[1] = NULL;
  418. attr->values = values;
  419. attr->encoded = encoded;
  420. PORT_ArenaUnmark (poolp, mark);
  421. return attr;
  422. loser:
  423. PORT_Assert (mark != NULL);
  424. PORT_ArenaRelease (poolp, mark);
  425. return NULL;
  426. }
  427. static SECStatus
  428. sec_pkcs7_add_attribute (SEC_PKCS7ContentInfo *cinfo,
  429. SEC_PKCS7Attribute ***attrsp,
  430. SEC_PKCS7Attribute *attr)
  431. {
  432. SEC_PKCS7Attribute **attrs;
  433. SECItem *ct_value;
  434. void *mark;
  435. PORT_Assert (SEC_PKCS7ContentType (cinfo) == SEC_OID_PKCS7_SIGNED_DATA);
  436. if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA)
  437. return SECFailure;
  438. attrs = *attrsp;
  439. if (attrs != NULL) {
  440. int count;
  441. /*
  442. * We already have some attributes, and just need to add this
  443. * new one.
  444. */
  445. /*
  446. * We should already have the *required* attributes, which were
  447. * created/added at the same time the first attribute was added.
  448. */
  449. PORT_Assert (sec_PKCS7FindAttribute (attrs,
  450. SEC_OID_PKCS9_CONTENT_TYPE,
  451. PR_FALSE) != NULL);
  452. PORT_Assert (sec_PKCS7FindAttribute (attrs,
  453. SEC_OID_PKCS9_MESSAGE_DIGEST,
  454. PR_FALSE) != NULL);
  455. for (count = 0; attrs[count] != NULL; count++)
  456. ;
  457. attrs = (SEC_PKCS7Attribute**)PORT_ArenaGrow (cinfo->poolp, attrs,
  458. (count + 1) * sizeof(SEC_PKCS7Attribute *),
  459. (count + 2) * sizeof(SEC_PKCS7Attribute *));
  460. if (attrs == NULL)
  461. return SECFailure;
  462. attrs[count] = attr;
  463. attrs[count+1] = NULL;
  464. *attrsp = attrs;
  465. return SECSuccess;
  466. }
  467. /*
  468. * This is the first time an attribute is going in.
  469. * We need to create and add the required attributes, and then
  470. * we will also add in the one our caller gave us.
  471. */
  472. /*
  473. * There are 2 required attributes, plus the one our caller wants
  474. * to add, plus we always end with a NULL one. Thus, four slots.
  475. */
  476. attrs = (SEC_PKCS7Attribute**)PORT_ArenaAlloc (cinfo->poolp,
  477. 4 * sizeof(SEC_PKCS7Attribute *));
  478. if (attrs == NULL)
  479. return SECFailure;
  480. mark = PORT_ArenaMark (cinfo->poolp);
  481. /*
  482. * First required attribute is the content type of the data
  483. * being signed.
  484. */
  485. ct_value = &(cinfo->content.signedData->contentInfo.contentType);
  486. attrs[0] = sec_pkcs7_create_attribute (cinfo->poolp,
  487. SEC_OID_PKCS9_CONTENT_TYPE,
  488. ct_value, PR_FALSE);
  489. /*
  490. * Second required attribute is the message digest of the data
  491. * being signed; we leave the value NULL for now (just create
  492. * the place for it to go), and the encoder will fill it in later.
  493. */
  494. attrs[1] = sec_pkcs7_create_attribute (cinfo->poolp,
  495. SEC_OID_PKCS9_MESSAGE_DIGEST,
  496. NULL, PR_FALSE);
  497. if (attrs[0] == NULL || attrs[1] == NULL) {
  498. PORT_ArenaRelease (cinfo->poolp, mark);
  499. return SECFailure;
  500. }
  501. attrs[2] = attr;
  502. attrs[3] = NULL;
  503. *attrsp = attrs;
  504. PORT_ArenaUnmark (cinfo->poolp, mark);
  505. return SECSuccess;
  506. }
  507. /*
  508. * Add the signing time to the authenticated (i.e. signed) attributes
  509. * of "cinfo". This is expected to be included in outgoing signed
  510. * messages for email (S/MIME) but is likely useful in other situations.
  511. *
  512. * This should only be added once; a second call will either do
  513. * nothing or replace an old signing time with a newer one.
  514. *
  515. * XXX This will probably just shove the current time into "cinfo"
  516. * but it will not actually get signed until the entire item is
  517. * processed for encoding. Is this (expected to be small) delay okay?
  518. *
  519. * "cinfo" should be of type signedData (the only kind of pkcs7 data
  520. * that is allowed authenticated attributes); SECFailure will be returned
  521. * if it is not.
  522. */
  523. SECStatus
  524. SEC_PKCS7AddSigningTime (SEC_PKCS7ContentInfo *cinfo)
  525. {
  526. SEC_PKCS7SignerInfo **signerinfos;
  527. SEC_PKCS7Attribute *attr;
  528. SECItem stime;
  529. SECStatus rv;
  530. int si;
  531. PORT_Assert (SEC_PKCS7ContentType (cinfo) == SEC_OID_PKCS7_SIGNED_DATA);
  532. if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA)
  533. return SECFailure;
  534. signerinfos = cinfo->content.signedData->signerInfos;
  535. /* There has to be a signer, or it makes no sense. */
  536. if (signerinfos == NULL || signerinfos[0] == NULL)
  537. return SECFailure;
  538. rv = DER_EncodeTimeChoice(NULL, &stime, PR_Now());
  539. if (rv != SECSuccess)
  540. return rv;
  541. attr = sec_pkcs7_create_attribute (cinfo->poolp,
  542. SEC_OID_PKCS9_SIGNING_TIME,
  543. &stime, PR_FALSE);
  544. SECITEM_FreeItem (&stime, PR_FALSE);
  545. if (attr == NULL)
  546. return SECFailure;
  547. rv = SECSuccess;
  548. for (si = 0; signerinfos[si] != NULL; si++) {
  549. SEC_PKCS7Attribute *oattr;
  550. oattr = sec_PKCS7FindAttribute (signerinfos[si]->authAttr,
  551. SEC_OID_PKCS9_SIGNING_TIME, PR_FALSE);
  552. PORT_Assert (oattr == NULL);
  553. if (oattr != NULL)
  554. continue; /* XXX or would it be better to replace it? */
  555. rv = sec_pkcs7_add_attribute (cinfo, &(signerinfos[si]->authAttr),
  556. attr);
  557. if (rv != SECSuccess)
  558. break; /* could try to continue, but may as well give up now */
  559. }
  560. return rv;
  561. }
  562. /*
  563. * Add the specified attribute to the authenticated (i.e. signed) attributes
  564. * of "cinfo" -- "oidtag" describes the attribute and "value" is the
  565. * value to be associated with it. NOTE! "value" must already be encoded;
  566. * no interpretation of "oidtag" is done. Also, it is assumed that this
  567. * signedData has only one signer -- if we ever need to add attributes
  568. * when there is more than one signature, we need a way to specify *which*
  569. * signature should get the attribute.
  570. *
  571. * XXX Technically, a signed attribute can have multiple values; if/when
  572. * we ever need to support an attribute which takes multiple values, we
  573. * either need to change this interface or create an AddSignedAttributeValue
  574. * which can be called subsequently, and would then append a value.
  575. *
  576. * "cinfo" should be of type signedData (the only kind of pkcs7 data
  577. * that is allowed authenticated attributes); SECFailure will be returned
  578. * if it is not.
  579. */
  580. SECStatus
  581. SEC_PKCS7AddSignedAttribute (SEC_PKCS7ContentInfo *cinfo,
  582. SECOidTag oidtag,
  583. SECItem *value)
  584. {
  585. SEC_PKCS7SignerInfo **signerinfos;
  586. SEC_PKCS7Attribute *attr;
  587. PORT_Assert (SEC_PKCS7ContentType (cinfo) == SEC_OID_PKCS7_SIGNED_DATA);
  588. if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA)
  589. return SECFailure;
  590. signerinfos = cinfo->content.signedData->signerInfos;
  591. /*
  592. * No signature or more than one means no deal.
  593. */
  594. if (signerinfos == NULL || signerinfos[0] == NULL || signerinfos[1] != NULL)
  595. return SECFailure;
  596. attr = sec_pkcs7_create_attribute (cinfo->poolp, oidtag, value, PR_TRUE);
  597. if (attr == NULL)
  598. return SECFailure;
  599. return sec_pkcs7_add_attribute (cinfo, &(signerinfos[0]->authAttr), attr);
  600. }
  601. /*
  602. * Mark that the signer certificates and their issuing chain should
  603. * be included in the encoded data. This is expected to be used
  604. * in outgoing signed messages for email (S/MIME).
  605. *
  606. * "certdb" is the cert database to use for finding the chain.
  607. * It can be NULL, meaning use the default database.
  608. *
  609. * "cinfo" should be of type signedData or signedAndEnvelopedData;
  610. * SECFailure will be returned if it is not.
  611. */
  612. SECStatus
  613. SEC_PKCS7IncludeCertChain (SEC_PKCS7ContentInfo *cinfo,
  614. CERTCertDBHandle *certdb)
  615. {
  616. SECOidTag kind;
  617. SEC_PKCS7SignerInfo *signerinfo, **signerinfos;
  618. kind = SEC_PKCS7ContentType (cinfo);
  619. switch (kind) {
  620. case SEC_OID_PKCS7_SIGNED_DATA:
  621. signerinfos = cinfo->content.signedData->signerInfos;
  622. break;
  623. case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
  624. signerinfos = cinfo->content.signedAndEnvelopedData->signerInfos;
  625. break;
  626. default:
  627. return SECFailure; /* XXX set an error? */
  628. }
  629. if (signerinfos == NULL) /* no signer, no certs? */
  630. return SECFailure; /* XXX set an error? */
  631. if (certdb == NULL) {
  632. certdb = CERT_GetDefaultCertDB();
  633. if (certdb == NULL) {
  634. PORT_SetError (SEC_ERROR_BAD_DATABASE);
  635. return SECFailure;
  636. }
  637. }
  638. /* XXX Should it be an error if we find no signerinfo or no certs? */
  639. while ((signerinfo = *signerinfos++) != NULL) {
  640. if (signerinfo->cert != NULL)
  641. /* get the cert chain. don't send the root to avoid contamination
  642. * of old clients with a new root that they don't trust
  643. */
  644. signerinfo->certList = CERT_CertChainFromCert (signerinfo->cert,
  645. certUsageEmailSigner,
  646. PR_FALSE);
  647. }
  648. return SECSuccess;
  649. }
  650. /*
  651. * Helper function to add a certificate chain for inclusion in the
  652. * bag of certificates in a signedData.
  653. */
  654. static SECStatus
  655. sec_pkcs7_add_cert_chain (SEC_PKCS7ContentInfo *cinfo,
  656. CERTCertificate *cert,
  657. CERTCertDBHandle *certdb)
  658. {
  659. SECOidTag kind;
  660. CERTCertificateList *certlist, **certlists, ***certlistsp;
  661. int count;
  662. kind = SEC_PKCS7ContentType (cinfo);
  663. switch (kind) {
  664. case SEC_OID_PKCS7_SIGNED_DATA:
  665. {
  666. SEC_PKCS7SignedData *sdp;
  667. sdp = cinfo->content.signedData;
  668. certlistsp = &(sdp->certLists);
  669. }
  670. break;
  671. case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
  672. {
  673. SEC_PKCS7SignedAndEnvelopedData *saedp;
  674. saedp = cinfo->content.signedAndEnvelopedData;
  675. certlistsp = &(saedp->certLists);
  676. }
  677. break;
  678. default:
  679. return SECFailure; /* XXX set an error? */
  680. }
  681. if (certdb == NULL) {
  682. certdb = CERT_GetDefaultCertDB();
  683. if (certdb == NULL) {
  684. PORT_SetError (SEC_ERROR_BAD_DATABASE);
  685. return SECFailure;
  686. }
  687. }
  688. certlist = CERT_CertChainFromCert (cert, certUsageEmailSigner, PR_FALSE);
  689. if (certlist == NULL)
  690. return SECFailure;
  691. certlists = *certlistsp;
  692. if (certlists == NULL) {
  693. count = 0;
  694. certlists = (CERTCertificateList**)PORT_ArenaAlloc (cinfo->poolp,
  695. 2 * sizeof(CERTCertificateList *));
  696. } else {
  697. for (count = 0; certlists[count] != NULL; count++)
  698. ;
  699. PORT_Assert (count); /* should be at least one already */
  700. certlists = (CERTCertificateList**)PORT_ArenaGrow (cinfo->poolp,
  701. certlists,
  702. (count + 1) * sizeof(CERTCertificateList *),
  703. (count + 2) * sizeof(CERTCertificateList *));
  704. }
  705. if (certlists == NULL) {
  706. CERT_DestroyCertificateList (certlist);
  707. return SECFailure;
  708. }
  709. certlists[count] = certlist;
  710. certlists[count + 1] = NULL;
  711. *certlistsp = certlists;
  712. return SECSuccess;
  713. }
  714. /*
  715. * Helper function to add a certificate for inclusion in the bag of
  716. * certificates in a signedData.
  717. */
  718. static SECStatus
  719. sec_pkcs7_add_certificate (SEC_PKCS7ContentInfo *cinfo,
  720. CERTCertificate *cert)
  721. {
  722. SECOidTag kind;
  723. CERTCertificate **certs, ***certsp;
  724. int count;
  725. kind = SEC_PKCS7ContentType (cinfo);
  726. switch (kind) {
  727. case SEC_OID_PKCS7_SIGNED_DATA:
  728. {
  729. SEC_PKCS7SignedData *sdp;
  730. sdp = cinfo->content.signedData;
  731. certsp = &(sdp->certs);
  732. }
  733. break;
  734. case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
  735. {
  736. SEC_PKCS7SignedAndEnvelopedData *saedp;
  737. saedp = cinfo->content.signedAndEnvelopedData;
  738. certsp = &(saedp->certs);
  739. }
  740. break;
  741. default:
  742. return SECFailure; /* XXX set an error? */
  743. }
  744. cert = CERT_DupCertificate (cert);
  745. if (cert == NULL)
  746. return SECFailure;
  747. certs = *certsp;
  748. if (certs == NULL) {
  749. count = 0;
  750. certs = (CERTCertificate**)PORT_ArenaAlloc (cinfo->poolp,
  751. 2 * sizeof(CERTCertificate *));
  752. } else {
  753. for (count = 0; certs[count] != NULL; count++)
  754. ;
  755. PORT_Assert (count); /* should be at least one already */
  756. certs = (CERTCertificate**)PORT_ArenaGrow (cinfo->poolp, certs,
  757. (count + 1) * sizeof(CERTCertificate *),
  758. (count + 2) * sizeof(CERTCertificate *));
  759. }
  760. if (certs == NULL) {
  761. CERT_DestroyCertificate (cert);
  762. return SECFailure;
  763. }
  764. certs[count] = cert;
  765. certs[count + 1] = NULL;
  766. *certsp = certs;
  767. return SECSuccess;
  768. }
  769. /*
  770. * Create a PKCS7 certs-only container.
  771. *
  772. * "cert" is the (first) cert that will be included.
  773. *
  774. * "include_chain" specifies whether the entire chain for "cert" should
  775. * be included.
  776. *
  777. * "certdb" is the cert database to use for finding the chain.
  778. * It can be NULL in when "include_chain" is false, or when meaning
  779. * use the default database.
  780. *
  781. * More certs and chains can be added via AddCertificate and AddCertChain.
  782. *
  783. * An error results in a return value of NULL and an error set.
  784. * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
  785. */
  786. SEC_PKCS7ContentInfo *
  787. SEC_PKCS7CreateCertsOnly (CERTCertificate *cert,
  788. PRBool include_chain,
  789. CERTCertDBHandle *certdb)
  790. {
  791. SEC_PKCS7ContentInfo *cinfo;
  792. SECStatus rv;
  793. cinfo = sec_pkcs7_create_signed_data (NULL, NULL);
  794. if (cinfo == NULL)
  795. return NULL;
  796. if (include_chain)
  797. rv = sec_pkcs7_add_cert_chain (cinfo, cert, certdb);
  798. else
  799. rv = sec_pkcs7_add_certificate (cinfo, cert);
  800. if (rv != SECSuccess) {
  801. SEC_PKCS7DestroyContentInfo (cinfo);
  802. return NULL;
  803. }
  804. return cinfo;
  805. }
  806. /*
  807. * Add "cert" and its entire chain to the set of certs included in "cinfo".
  808. *
  809. * "certdb" is the cert database to use for finding the chain.
  810. * It can be NULL, meaning use the default database.
  811. *
  812. * "cinfo" should be of type signedData or signedAndEnvelopedData;
  813. * SECFailure will be returned if it is not.
  814. */
  815. SECStatus
  816. SEC_PKCS7AddCertChain (SEC_PKCS7ContentInfo *cinfo,
  817. CERTCertificate *cert,
  818. CERTCertDBHandle *certdb)
  819. {
  820. SECOidTag kind;
  821. kind = SEC_PKCS7ContentType (cinfo);
  822. if (kind != SEC_OID_PKCS7_SIGNED_DATA
  823. && kind != SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA)
  824. return SECFailure; /* XXX set an error? */
  825. return sec_pkcs7_add_cert_chain (cinfo, cert, certdb);
  826. }
  827. /*
  828. * Add "cert" to the set of certs included in "cinfo".
  829. *
  830. * "cinfo" should be of type signedData or signedAndEnvelopedData;
  831. * SECFailure will be returned if it is not.
  832. */
  833. SECStatus
  834. SEC_PKCS7AddCertificate (SEC_PKCS7ContentInfo *cinfo, CERTCertificate *cert)
  835. {
  836. SECOidTag kind;
  837. kind = SEC_PKCS7ContentType (cinfo);
  838. if (kind != SEC_OID_PKCS7_SIGNED_DATA
  839. && kind != SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA)
  840. return SECFailure; /* XXX set an error? */
  841. return sec_pkcs7_add_certificate (cinfo, cert);
  842. }
  843. static SECStatus
  844. sec_pkcs7_init_encrypted_content_info (SEC_PKCS7EncryptedContentInfo *enccinfo,
  845. PRArenaPool *poolp,
  846. SECOidTag kind, PRBool detached,
  847. SECOidTag encalg, int keysize)
  848. {
  849. SECStatus rv;
  850. PORT_Assert (enccinfo != NULL && poolp != NULL);
  851. if (enccinfo == NULL || poolp == NULL)
  852. return SECFailure;
  853. /*
  854. * XXX Some day we may want to allow for other kinds. That needs
  855. * more work and modifications to the creation interface, etc.
  856. * For now, allow but notice callers who pass in other kinds.
  857. * They are responsible for creating the inner type and encoding,
  858. * if it is other than DATA.
  859. */
  860. PORT_Assert (kind == SEC_OID_PKCS7_DATA);
  861. enccinfo->contentTypeTag = SECOID_FindOIDByTag (kind);
  862. PORT_Assert (enccinfo->contentTypeTag
  863. && enccinfo->contentTypeTag->offset == kind);
  864. rv = SECITEM_CopyItem (poolp, &(enccinfo->contentType),
  865. &(enccinfo->contentTypeTag->oid));
  866. if (rv != SECSuccess)
  867. return rv;
  868. /* Save keysize and algorithm for later. */
  869. enccinfo->keysize = keysize;
  870. enccinfo->encalg = encalg;
  871. return SECSuccess;
  872. }
  873. /*
  874. * Add a recipient to a PKCS7 thing, verifying their cert first.
  875. * Any error returns SECFailure.
  876. */
  877. static SECStatus
  878. sec_pkcs7_add_recipient (SEC_PKCS7ContentInfo *cinfo,
  879. CERTCertificate *cert,
  880. SECCertUsage certusage,
  881. CERTCertDBHandle *certdb)
  882. {
  883. SECOidTag kind;
  884. SEC_PKCS7RecipientInfo *recipientinfo, **recipientinfos, ***recipientinfosp;
  885. SECItem *dummy;
  886. void *mark;
  887. int count;
  888. kind = SEC_PKCS7ContentType (cinfo);
  889. switch (kind) {
  890. case SEC_OID_PKCS7_ENVELOPED_DATA:
  891. {
  892. SEC_PKCS7EnvelopedData *edp;
  893. edp = cinfo->content.envelopedData;
  894. recipientinfosp = &(edp->recipientInfos);
  895. }
  896. break;
  897. case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
  898. {
  899. SEC_PKCS7SignedAndEnvelopedData *saedp;
  900. saedp = cinfo->content.signedAndEnvelopedData;
  901. recipientinfosp = &(saedp->recipientInfos);
  902. }
  903. break;
  904. default:
  905. return SECFailure; /* XXX set an error? */
  906. }
  907. /*
  908. * XXX I think that CERT_VerifyCert should do this if *it* is passed
  909. * a NULL database.
  910. */
  911. if (certdb == NULL) {
  912. certdb = CERT_GetDefaultCertDB();
  913. if (certdb == NULL)
  914. return SECFailure; /* XXX set an error? */
  915. }
  916. if (CERT_VerifyCert (certdb, cert, PR_TRUE, certusage, PR_Now(),
  917. cinfo->pwfn_arg, NULL) != SECSuccess)
  918. {
  919. /* XXX Did CERT_VerifyCert set an error? */
  920. return SECFailure;
  921. }
  922. mark = PORT_ArenaMark (cinfo->poolp);
  923. recipientinfo = (SEC_PKCS7RecipientInfo*)PORT_ArenaZAlloc (cinfo->poolp,
  924. sizeof(SEC_PKCS7RecipientInfo));
  925. if (recipientinfo == NULL) {
  926. PORT_ArenaRelease (cinfo->poolp, mark);
  927. return SECFailure;
  928. }
  929. dummy = SEC_ASN1EncodeInteger (cinfo->poolp, &recipientinfo->version,
  930. SEC_PKCS7_RECIPIENT_INFO_VERSION);
  931. if (dummy == NULL) {
  932. PORT_ArenaRelease (cinfo->poolp, mark);
  933. return SECFailure;
  934. }
  935. PORT_Assert (dummy == &recipientinfo->version);
  936. recipientinfo->cert = CERT_DupCertificate (cert);
  937. if (recipientinfo->cert == NULL) {
  938. PORT_ArenaRelease (cinfo->poolp, mark);
  939. return SECFailure;
  940. }
  941. recipientinfo->issuerAndSN = CERT_GetCertIssuerAndSN (cinfo->poolp, cert);
  942. if (recipientinfo->issuerAndSN == NULL) {
  943. PORT_ArenaRelease (cinfo->poolp, mark);
  944. return SECFailure;
  945. }
  946. /*
  947. * Okay, now recipientinfo is all set. We just need to put it into
  948. * the main structure.
  949. *
  950. * If this is the first recipient, allocate a new recipientinfos array;
  951. * otherwise, reallocate the array, making room for the new entry.
  952. */
  953. recipientinfos = *recipientinfosp;
  954. if (recipientinfos == NULL) {
  955. count = 0;
  956. recipientinfos = (SEC_PKCS7RecipientInfo **)PORT_ArenaAlloc (
  957. cinfo->poolp,
  958. 2 * sizeof(SEC_PKCS7RecipientInfo *));
  959. } else {
  960. for (count = 0; recipientinfos[count] != NULL; count++)
  961. ;
  962. PORT_Assert (count); /* should be at least one already */
  963. recipientinfos = (SEC_PKCS7RecipientInfo **)PORT_ArenaGrow (
  964. cinfo->poolp, recipientinfos,
  965. (count + 1) * sizeof(SEC_PKCS7RecipientInfo *),
  966. (count + 2) * sizeof(SEC_PKCS7RecipientInfo *));
  967. }
  968. if (recipientinfos == NULL) {
  969. PORT_ArenaRelease (cinfo->poolp, mark);
  970. return SECFailure;
  971. }
  972. recipientinfos[count] = recipientinfo;
  973. recipientinfos[count + 1] = NULL;
  974. *recipientinfosp = recipientinfos;
  975. PORT_ArenaUnmark (cinfo->poolp, mark);
  976. return SECSuccess;
  977. }
  978. /*
  979. * Start a PKCS7 enveloping context.
  980. *
  981. * "cert" is the cert for the recipient. It will be checked for validity.
  982. *
  983. * "certusage" describes the encryption usage (e.g. certUsageEmailRecipient)
  984. * XXX Maybe SECCertUsage should be split so that our caller just says
  985. * "email" and *we* add the "recipient" part -- otherwise our caller
  986. * could be lying about the usage; we do not want to allow encryption
  987. * certs for signing or vice versa.
  988. *
  989. * "certdb" is the cert database to use for verifying the cert.
  990. * It can be NULL if a default database is available (like in the client).
  991. *
  992. * "encalg" specifies the bulk encryption algorithm to use (e.g. SEC_OID_RC2).
  993. *
  994. * "keysize" specifies the bulk encryption key size, in bits.
  995. *
  996. * The return value can be passed to functions which add things to
  997. * it like more recipients, then eventually to SEC_PKCS7Encode() or to
  998. * SEC_PKCS7EncoderStart() to create the encoded data, and finally to
  999. * SEC_PKCS7DestroyContentInfo().
  1000. *
  1001. * An error results in a return value of NULL and an error set.
  1002. * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
  1003. */
  1004. extern SEC_PKCS7ContentInfo *
  1005. SEC_PKCS7CreateEnvelopedData (CERTCertificate *cert,
  1006. SECCertUsage certusage,
  1007. CERTCertDBHandle *certdb,
  1008. SECOidTag encalg,
  1009. int keysize,
  1010. SECKEYGetPasswordKey pwfn, void *pwfn_arg)
  1011. {
  1012. SEC_PKCS7ContentInfo *cinfo;
  1013. SEC_PKCS7EnvelopedData *envd;
  1014. SECStatus rv;
  1015. cinfo = sec_pkcs7_create_content_info (SEC_OID_PKCS7_ENVELOPED_DATA,
  1016. PR_FALSE, pwfn, pwfn_arg);
  1017. if (cinfo == NULL)
  1018. return NULL;
  1019. rv = sec_pkcs7_add_recipient (cinfo, cert, certusage, certdb);
  1020. if (rv != SECSuccess) {
  1021. SEC_PKCS7DestroyContentInfo (cinfo);
  1022. return NULL;
  1023. }
  1024. envd = cinfo->content.envelopedData;
  1025. PORT_Assert (envd != NULL);
  1026. /*
  1027. * XXX Might we want to allow content types other than data?
  1028. * If so, via what interface?
  1029. */
  1030. rv = sec_pkcs7_init_encrypted_content_info (&(envd->encContentInfo),
  1031. cinfo->poolp,
  1032. SEC_OID_PKCS7_DATA, PR_FALSE,
  1033. encalg, keysize);
  1034. if (rv != SECSuccess) {
  1035. SEC_PKCS7DestroyContentInfo (cinfo);
  1036. return NULL;
  1037. }
  1038. /* XXX Anything more to do here? */
  1039. return cinfo;
  1040. }
  1041. /*
  1042. * Add another recipient to an encrypted message.
  1043. *
  1044. * "cinfo" should be of type envelopedData or signedAndEnvelopedData;
  1045. * SECFailure will be returned if it is not.
  1046. *
  1047. * "cert" is the cert for the recipient. It will be checked for validity.
  1048. *
  1049. * "certusage" describes the encryption usage (e.g. certUsageEmailRecipient)
  1050. * XXX Maybe SECCertUsage should be split so that our caller just says
  1051. * "email" and *we* add the "recipient" part -- otherwise our caller
  1052. * could be lying about the usage; we do not want to allow encryption
  1053. * certs for signing or vice versa.
  1054. *
  1055. * "certdb" is the cert database to use for verifying the cert.
  1056. * It can be NULL if a default database is available (like in the client).
  1057. */
  1058. SECStatus
  1059. SEC_PKCS7AddRecipient (SEC_PKCS7ContentInfo *cinfo,
  1060. CERTCertificate *cert,
  1061. SECCertUsage certusage,
  1062. CERTCertDBHandle *certdb)
  1063. {
  1064. return sec_pkcs7_add_recipient (cinfo, cert, certusage, certdb);
  1065. }
  1066. /*
  1067. * Create an empty PKCS7 data content info.
  1068. *
  1069. * An error results in a return value of NULL and an error set.
  1070. * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
  1071. */
  1072. SEC_PKCS7ContentInfo *
  1073. SEC_PKCS7CreateData (void)
  1074. {
  1075. return sec_pkcs7_create_content_info (SEC_OID_PKCS7_DATA, PR_FALSE,
  1076. NULL, NULL);
  1077. }
  1078. /*
  1079. * Create an empty PKCS7 encrypted content info.
  1080. *
  1081. * "algorithm" specifies the bulk encryption algorithm to use.
  1082. *
  1083. * An error results in a return value of NULL and an error set.
  1084. * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
  1085. */
  1086. SEC_PKCS7ContentInfo *
  1087. SEC_PKCS7CreateEncryptedData (SECOidTag algorithm, int keysize,
  1088. SECKEYGetPasswordKey pwfn, void *pwfn_arg)
  1089. {
  1090. SEC_PKCS7ContentInfo *cinfo;
  1091. SECAlgorithmID *algid;
  1092. SEC_PKCS7EncryptedData *enc_data;
  1093. SECStatus rv;
  1094. cinfo = sec_pkcs7_create_content_info (SEC_OID_PKCS7_ENCRYPTED_DATA,
  1095. PR_FALSE, pwfn, pwfn_arg);
  1096. if (cinfo == NULL)
  1097. return NULL;
  1098. enc_data = cinfo->content.encryptedData;
  1099. algid = &(enc_data->encContentInfo.contentEncAlg);
  1100. if (!SEC_PKCS5IsAlgorithmPBEAlgTag(algorithm)) {
  1101. rv = SECOID_SetAlgorithmID (cinfo->poolp, algid, algorithm, NULL);
  1102. } else {
  1103. /* Assume password-based-encryption.
  1104. * Note: we can't generate pkcs5v2 from this interface.
  1105. * PK11_CreateBPEAlgorithmID generates pkcs5v2 by accepting
  1106. * non-PBE oids and assuming that they are pkcs5v2 oids, but
  1107. * NSS_CMSEncryptedData_Create accepts non-PBE oids as regular
  1108. * CMS encrypted data, so we can't tell SEC_PKCS7CreateEncryptedtedData
  1109. * to create pkcs5v2 PBEs */
  1110. SECAlgorithmID *pbe_algid;
  1111. pbe_algid = PK11_CreatePBEAlgorithmID (algorithm, 1, NULL);
  1112. if (pbe_algid == NULL) {
  1113. rv = SECFailure;
  1114. } else {
  1115. rv = SECOID_CopyAlgorithmID (cinfo->poolp, algid, pbe_algid);
  1116. SECOID_DestroyAlgorithmID (pbe_algid, PR_TRUE);
  1117. }
  1118. }
  1119. if (rv != SECSuccess) {
  1120. SEC_PKCS7DestroyContentInfo (cinfo);
  1121. return NULL;
  1122. }
  1123. rv = sec_pkcs7_init_encrypted_content_info (&(enc_data->encContentInfo),
  1124. cinfo->poolp,
  1125. SEC_OID_PKCS7_DATA, PR_FALSE,
  1126. algorithm, keysize);
  1127. if (rv != SECSuccess) {
  1128. SEC_PKCS7DestroyContentInfo (cinfo);
  1129. return NULL;
  1130. }
  1131. return cinfo;
  1132. }