cmsenvdata.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  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/. */
  4. /*
  5. * CMS envelopedData methods.
  6. */
  7. #include "cmslocal.h"
  8. #include "cert.h"
  9. #include "keyhi.h"
  10. #include "secasn1.h"
  11. #include "secitem.h"
  12. #include "secoid.h"
  13. #include "pk11func.h"
  14. #include "secerr.h"
  15. #include "secpkcs5.h"
  16. /*
  17. * NSS_CMSEnvelopedData_Create - create an enveloped data message
  18. */
  19. NSSCMSEnvelopedData *
  20. NSS_CMSEnvelopedData_Create(NSSCMSMessage *cmsg, SECOidTag algorithm, int keysize)
  21. {
  22. void *mark;
  23. NSSCMSEnvelopedData *envd;
  24. PLArenaPool *poolp;
  25. SECStatus rv;
  26. poolp = cmsg->poolp;
  27. mark = PORT_ArenaMark(poolp);
  28. envd = (NSSCMSEnvelopedData *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSEnvelopedData));
  29. if (envd == NULL)
  30. goto loser;
  31. envd->cmsg = cmsg;
  32. /* version is set in NSS_CMSEnvelopedData_Encode_BeforeStart() */
  33. rv = NSS_CMSContentInfo_SetContentEncAlg(poolp, &(envd->contentInfo),
  34. algorithm, NULL, keysize);
  35. if (rv != SECSuccess)
  36. goto loser;
  37. PORT_ArenaUnmark(poolp, mark);
  38. return envd;
  39. loser:
  40. PORT_ArenaRelease(poolp, mark);
  41. return NULL;
  42. }
  43. /*
  44. * NSS_CMSEnvelopedData_Destroy - destroy an enveloped data message
  45. */
  46. void
  47. NSS_CMSEnvelopedData_Destroy(NSSCMSEnvelopedData *edp)
  48. {
  49. NSSCMSRecipientInfo **recipientinfos;
  50. NSSCMSRecipientInfo *ri;
  51. if (edp == NULL)
  52. return;
  53. recipientinfos = edp->recipientInfos;
  54. if (recipientinfos == NULL)
  55. return;
  56. while ((ri = *recipientinfos++) != NULL)
  57. NSS_CMSRecipientInfo_Destroy(ri);
  58. NSS_CMSContentInfo_Destroy(&(edp->contentInfo));
  59. }
  60. /*
  61. * NSS_CMSEnvelopedData_GetContentInfo - return pointer to this envelopedData's contentinfo
  62. */
  63. NSSCMSContentInfo *
  64. NSS_CMSEnvelopedData_GetContentInfo(NSSCMSEnvelopedData *envd)
  65. {
  66. return &(envd->contentInfo);
  67. }
  68. /*
  69. * NSS_CMSEnvelopedData_AddRecipient - add a recipientinfo to the enveloped data msg
  70. *
  71. * rip must be created on the same pool as edp - this is not enforced, though.
  72. */
  73. SECStatus
  74. NSS_CMSEnvelopedData_AddRecipient(NSSCMSEnvelopedData *edp, NSSCMSRecipientInfo *rip)
  75. {
  76. void *mark;
  77. SECStatus rv;
  78. /* XXX compare pools, if not same, copy rip into edp's pool */
  79. PR_ASSERT(edp != NULL);
  80. PR_ASSERT(rip != NULL);
  81. mark = PORT_ArenaMark(edp->cmsg->poolp);
  82. rv = NSS_CMSArray_Add(edp->cmsg->poolp, (void ***)&(edp->recipientInfos), (void *)rip);
  83. if (rv != SECSuccess) {
  84. PORT_ArenaRelease(edp->cmsg->poolp, mark);
  85. return SECFailure;
  86. }
  87. PORT_ArenaUnmark(edp->cmsg->poolp, mark);
  88. return SECSuccess;
  89. }
  90. /*
  91. * NSS_CMSEnvelopedData_Encode_BeforeStart - prepare this envelopedData for encoding
  92. *
  93. * at this point, we need
  94. * - recipientinfos set up with recipient's certificates
  95. * - a content encryption algorithm (if none, 3DES will be used)
  96. *
  97. * this function will generate a random content encryption key (aka bulk key),
  98. * initialize the recipientinfos with certificate identification and wrap the bulk key
  99. * using the proper algorithm for every certificiate.
  100. * it will finally set the bulk algorithm and key so that the encode step can find it.
  101. */
  102. SECStatus
  103. NSS_CMSEnvelopedData_Encode_BeforeStart(NSSCMSEnvelopedData *envd)
  104. {
  105. int version;
  106. NSSCMSRecipientInfo **recipientinfos;
  107. NSSCMSContentInfo *cinfo;
  108. PK11SymKey *bulkkey = NULL;
  109. SECOidTag bulkalgtag;
  110. CK_MECHANISM_TYPE type;
  111. PK11SlotInfo *slot;
  112. SECStatus rv;
  113. SECItem *dummy;
  114. PLArenaPool *poolp;
  115. extern const SEC_ASN1Template NSSCMSRecipientInfoTemplate[];
  116. void *mark = NULL;
  117. int i;
  118. poolp = envd->cmsg->poolp;
  119. cinfo = &(envd->contentInfo);
  120. if (cinfo == NULL) {
  121. PORT_SetError(SEC_ERROR_BAD_DATA);
  122. goto loser;
  123. }
  124. recipientinfos = envd->recipientInfos;
  125. if (recipientinfos == NULL) {
  126. PORT_SetError(SEC_ERROR_BAD_DATA);
  127. #if 0
  128. PORT_SetErrorString("Cannot find recipientinfos to encode.");
  129. #endif
  130. goto loser;
  131. }
  132. version = NSS_CMS_ENVELOPED_DATA_VERSION_REG;
  133. if (envd->originatorInfo != NULL || envd->unprotectedAttr != NULL) {
  134. version = NSS_CMS_ENVELOPED_DATA_VERSION_ADV;
  135. } else {
  136. for (i = 0; recipientinfos[i] != NULL; i++) {
  137. if (NSS_CMSRecipientInfo_GetVersion(recipientinfos[i]) != 0) {
  138. version = NSS_CMS_ENVELOPED_DATA_VERSION_ADV;
  139. break;
  140. }
  141. }
  142. }
  143. dummy = SEC_ASN1EncodeInteger(poolp, &(envd->version), version);
  144. if (dummy == NULL)
  145. goto loser;
  146. /* now we need to have a proper content encryption algorithm
  147. * on the SMIME level, we would figure one out by looking at SMIME capabilities
  148. * we cannot do that on our level, so if none is set already, we'll just go
  149. * with one of the mandatory algorithms (3DES) */
  150. if ((bulkalgtag = NSS_CMSContentInfo_GetContentEncAlgTag(cinfo)) == SEC_OID_UNKNOWN) {
  151. rv = NSS_CMSContentInfo_SetContentEncAlg(poolp, cinfo, SEC_OID_DES_EDE3_CBC, NULL, 168);
  152. if (rv != SECSuccess)
  153. goto loser;
  154. bulkalgtag = SEC_OID_DES_EDE3_CBC;
  155. }
  156. /* generate a random bulk key suitable for content encryption alg */
  157. type = PK11_AlgtagToMechanism(bulkalgtag);
  158. slot = PK11_GetBestSlot(type, envd->cmsg->pwfn_arg);
  159. if (slot == NULL)
  160. goto loser; /* error has been set by PK11_GetBestSlot */
  161. /* this is expensive... */
  162. bulkkey = PK11_KeyGen(slot, type, NULL,
  163. NSS_CMSContentInfo_GetBulkKeySize(cinfo) / 8,
  164. envd->cmsg->pwfn_arg);
  165. PK11_FreeSlot(slot);
  166. if (bulkkey == NULL)
  167. goto loser; /* error has been set by PK11_KeyGen */
  168. mark = PORT_ArenaMark(poolp);
  169. /* Encrypt the bulk key with the public key of each recipient. */
  170. for (i = 0; recipientinfos[i] != NULL; i++) {
  171. rv = NSS_CMSRecipientInfo_WrapBulkKey(recipientinfos[i], bulkkey, bulkalgtag);
  172. if (rv != SECSuccess)
  173. goto loser; /* error has been set by NSS_CMSRecipientInfo_EncryptBulkKey */
  174. /* could be: alg not supported etc. */
  175. }
  176. /* the recipientinfos are all finished. now sort them by DER for SET OF encoding */
  177. rv = NSS_CMSArray_SortByDER((void **)envd->recipientInfos,
  178. NSSCMSRecipientInfoTemplate, NULL);
  179. if (rv != SECSuccess)
  180. goto loser; /* error has been set by NSS_CMSArray_SortByDER */
  181. /* store the bulk key in the contentInfo so that the encoder can find it */
  182. NSS_CMSContentInfo_SetBulkKey(cinfo, bulkkey);
  183. PORT_ArenaUnmark(poolp, mark);
  184. PK11_FreeSymKey(bulkkey);
  185. return SECSuccess;
  186. loser:
  187. if (mark != NULL)
  188. PORT_ArenaRelease(poolp, mark);
  189. if (bulkkey)
  190. PK11_FreeSymKey(bulkkey);
  191. return SECFailure;
  192. }
  193. /*
  194. * NSS_CMSEnvelopedData_Encode_BeforeData - set up encryption
  195. *
  196. * it is essential that this is called before the contentEncAlg is encoded, because
  197. * setting up the encryption may generate IVs and thus change it!
  198. */
  199. SECStatus
  200. NSS_CMSEnvelopedData_Encode_BeforeData(NSSCMSEnvelopedData *envd)
  201. {
  202. NSSCMSContentInfo *cinfo;
  203. PK11SymKey *bulkkey;
  204. SECAlgorithmID *algid;
  205. SECStatus rv;
  206. cinfo = &(envd->contentInfo);
  207. /* find bulkkey and algorithm - must have been set by NSS_CMSEnvelopedData_Encode_BeforeStart */
  208. bulkkey = NSS_CMSContentInfo_GetBulkKey(cinfo);
  209. if (bulkkey == NULL)
  210. return SECFailure;
  211. algid = NSS_CMSContentInfo_GetContentEncAlg(cinfo);
  212. if (algid == NULL)
  213. return SECFailure;
  214. rv = NSS_CMSContentInfo_Private_Init(cinfo);
  215. if (rv != SECSuccess) {
  216. return SECFailure;
  217. }
  218. /* this may modify algid (with IVs generated in a token).
  219. * it is essential that algid is a pointer to the contentEncAlg data, not a
  220. * pointer to a copy! */
  221. cinfo->privateInfo->ciphcx = NSS_CMSCipherContext_StartEncrypt(envd->cmsg->poolp, bulkkey, algid);
  222. PK11_FreeSymKey(bulkkey);
  223. if (cinfo->privateInfo->ciphcx == NULL)
  224. return SECFailure;
  225. return SECSuccess;
  226. }
  227. /*
  228. * NSS_CMSEnvelopedData_Encode_AfterData - finalize this envelopedData for encoding
  229. */
  230. SECStatus
  231. NSS_CMSEnvelopedData_Encode_AfterData(NSSCMSEnvelopedData *envd)
  232. {
  233. if (envd->contentInfo.privateInfo && envd->contentInfo.privateInfo->ciphcx) {
  234. NSS_CMSCipherContext_Destroy(envd->contentInfo.privateInfo->ciphcx);
  235. envd->contentInfo.privateInfo->ciphcx = NULL;
  236. }
  237. /* nothing else to do after data */
  238. return SECSuccess;
  239. }
  240. /*
  241. * NSS_CMSEnvelopedData_Decode_BeforeData - find our recipientinfo,
  242. * derive bulk key & set up our contentinfo
  243. */
  244. SECStatus
  245. NSS_CMSEnvelopedData_Decode_BeforeData(NSSCMSEnvelopedData *envd)
  246. {
  247. NSSCMSRecipientInfo *ri;
  248. PK11SymKey *bulkkey = NULL;
  249. SECOidTag bulkalgtag;
  250. SECAlgorithmID *bulkalg;
  251. SECStatus rv = SECFailure;
  252. NSSCMSContentInfo *cinfo;
  253. NSSCMSRecipient **recipient_list = NULL;
  254. NSSCMSRecipient *recipient;
  255. int rlIndex;
  256. if (NSS_CMSArray_Count((void **)envd->recipientInfos) == 0) {
  257. PORT_SetError(SEC_ERROR_BAD_DATA);
  258. #if 0
  259. PORT_SetErrorString("No recipient data in envelope.");
  260. #endif
  261. goto loser;
  262. }
  263. /* look if one of OUR cert's issuerSN is on the list of recipients, and if so, */
  264. /* get the cert and private key for it right away */
  265. recipient_list = nss_cms_recipient_list_create(envd->recipientInfos);
  266. if (recipient_list == NULL)
  267. goto loser;
  268. /* what about multiple recipientInfos that match?
  269. * especially if, for some reason, we could not produce a bulk key with the first match?!
  270. * we could loop & feed partial recipient_list to PK11_FindCertAndKeyByRecipientList...
  271. * maybe later... */
  272. rlIndex = PK11_FindCertAndKeyByRecipientListNew(recipient_list, envd->cmsg->pwfn_arg);
  273. /* if that fails, then we're not an intended recipient and cannot decrypt */
  274. if (rlIndex < 0) {
  275. PORT_SetError(SEC_ERROR_NOT_A_RECIPIENT);
  276. #if 0
  277. PORT_SetErrorString("Cannot decrypt data because proper key cannot be found.");
  278. #endif
  279. goto loser;
  280. }
  281. recipient = recipient_list[rlIndex];
  282. if (!recipient->cert || !recipient->privkey) {
  283. /* XXX should set an error code ?!? */
  284. goto loser;
  285. }
  286. /* get a pointer to "our" recipientinfo */
  287. ri = envd->recipientInfos[recipient->riIndex];
  288. cinfo = &(envd->contentInfo);
  289. bulkalgtag = NSS_CMSContentInfo_GetContentEncAlgTag(cinfo);
  290. if (bulkalgtag == SEC_OID_UNKNOWN) {
  291. PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
  292. } else
  293. bulkkey =
  294. NSS_CMSRecipientInfo_UnwrapBulkKey(ri, recipient->subIndex,
  295. recipient->cert,
  296. recipient->privkey,
  297. bulkalgtag);
  298. if (bulkkey == NULL) {
  299. /* no success finding a bulk key */
  300. goto loser;
  301. }
  302. NSS_CMSContentInfo_SetBulkKey(cinfo, bulkkey);
  303. bulkalg = NSS_CMSContentInfo_GetContentEncAlg(cinfo);
  304. rv = NSS_CMSContentInfo_Private_Init(cinfo);
  305. if (rv != SECSuccess) {
  306. goto loser;
  307. }
  308. rv = SECFailure;
  309. cinfo->privateInfo->ciphcx = NSS_CMSCipherContext_StartDecrypt(bulkkey, bulkalg);
  310. if (cinfo->privateInfo->ciphcx == NULL)
  311. goto loser; /* error has been set by NSS_CMSCipherContext_StartDecrypt */
  312. rv = SECSuccess;
  313. loser:
  314. if (bulkkey)
  315. PK11_FreeSymKey(bulkkey);
  316. if (recipient_list != NULL)
  317. nss_cms_recipient_list_destroy(recipient_list);
  318. return rv;
  319. }
  320. /*
  321. * NSS_CMSEnvelopedData_Decode_AfterData - finish decrypting this envelopedData's content
  322. */
  323. SECStatus
  324. NSS_CMSEnvelopedData_Decode_AfterData(NSSCMSEnvelopedData *envd)
  325. {
  326. if (envd && envd->contentInfo.privateInfo && envd->contentInfo.privateInfo->ciphcx) {
  327. NSS_CMSCipherContext_Destroy(envd->contentInfo.privateInfo->ciphcx);
  328. envd->contentInfo.privateInfo->ciphcx = NULL;
  329. }
  330. return SECSuccess;
  331. }
  332. /*
  333. * NSS_CMSEnvelopedData_Decode_AfterEnd - finish decoding this envelopedData
  334. */
  335. SECStatus
  336. NSS_CMSEnvelopedData_Decode_AfterEnd(NSSCMSEnvelopedData *envd)
  337. {
  338. /* apply final touches */
  339. return SECSuccess;
  340. }