Wed, 31 Dec 2014 06:55:50 +0100
Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | |
michael@0 | 5 | /* |
michael@0 | 6 | * CMS encryptedData methods. |
michael@0 | 7 | */ |
michael@0 | 8 | |
michael@0 | 9 | #include "cmslocal.h" |
michael@0 | 10 | |
michael@0 | 11 | #include "key.h" |
michael@0 | 12 | #include "secasn1.h" |
michael@0 | 13 | #include "secitem.h" |
michael@0 | 14 | #include "secoid.h" |
michael@0 | 15 | #include "pk11func.h" |
michael@0 | 16 | #include "prtime.h" |
michael@0 | 17 | #include "secerr.h" |
michael@0 | 18 | #include "secpkcs5.h" |
michael@0 | 19 | |
michael@0 | 20 | /* |
michael@0 | 21 | * NSS_CMSEncryptedData_Create - create an empty encryptedData object. |
michael@0 | 22 | * |
michael@0 | 23 | * "algorithm" specifies the bulk encryption algorithm to use. |
michael@0 | 24 | * "keysize" is the key size. |
michael@0 | 25 | * |
michael@0 | 26 | * An error results in a return value of NULL and an error set. |
michael@0 | 27 | * (Retrieve specific errors via PORT_GetError()/XP_GetError().) |
michael@0 | 28 | */ |
michael@0 | 29 | NSSCMSEncryptedData * |
michael@0 | 30 | NSS_CMSEncryptedData_Create(NSSCMSMessage *cmsg, SECOidTag algorithm, |
michael@0 | 31 | int keysize) |
michael@0 | 32 | { |
michael@0 | 33 | void *mark; |
michael@0 | 34 | NSSCMSEncryptedData *encd; |
michael@0 | 35 | PLArenaPool *poolp; |
michael@0 | 36 | SECAlgorithmID *pbe_algid; |
michael@0 | 37 | SECStatus rv; |
michael@0 | 38 | |
michael@0 | 39 | poolp = cmsg->poolp; |
michael@0 | 40 | |
michael@0 | 41 | mark = PORT_ArenaMark(poolp); |
michael@0 | 42 | |
michael@0 | 43 | encd = PORT_ArenaZNew(poolp, NSSCMSEncryptedData); |
michael@0 | 44 | if (encd == NULL) |
michael@0 | 45 | goto loser; |
michael@0 | 46 | |
michael@0 | 47 | encd->cmsg = cmsg; |
michael@0 | 48 | |
michael@0 | 49 | /* version is set in NSS_CMSEncryptedData_Encode_BeforeStart() */ |
michael@0 | 50 | |
michael@0 | 51 | if (!SEC_PKCS5IsAlgorithmPBEAlgTag(algorithm)) { |
michael@0 | 52 | rv = NSS_CMSContentInfo_SetContentEncAlg(poolp, &(encd->contentInfo), |
michael@0 | 53 | algorithm, NULL, keysize); |
michael@0 | 54 | } else { |
michael@0 | 55 | /* Assume password-based-encryption. |
michael@0 | 56 | * Note: we can't generate pkcs5v2 from this interface. |
michael@0 | 57 | * PK11_CreateBPEAlgorithmID generates pkcs5v2 by accepting |
michael@0 | 58 | * non-PBE oids and assuming that they are pkcs5v2 oids, but |
michael@0 | 59 | * NSS_CMSEncryptedData_Create accepts non-PBE oids as regular |
michael@0 | 60 | * CMS encrypted data, so we can't tell NSS_CMS_EncryptedData_Create |
michael@0 | 61 | * to create pkcs5v2 PBEs */ |
michael@0 | 62 | pbe_algid = PK11_CreatePBEAlgorithmID(algorithm, 1, NULL); |
michael@0 | 63 | if (pbe_algid == NULL) { |
michael@0 | 64 | rv = SECFailure; |
michael@0 | 65 | } else { |
michael@0 | 66 | rv = NSS_CMSContentInfo_SetContentEncAlgID(poolp, |
michael@0 | 67 | &(encd->contentInfo), pbe_algid, keysize); |
michael@0 | 68 | SECOID_DestroyAlgorithmID (pbe_algid, PR_TRUE); |
michael@0 | 69 | } |
michael@0 | 70 | } |
michael@0 | 71 | if (rv != SECSuccess) |
michael@0 | 72 | goto loser; |
michael@0 | 73 | |
michael@0 | 74 | PORT_ArenaUnmark(poolp, mark); |
michael@0 | 75 | return encd; |
michael@0 | 76 | |
michael@0 | 77 | loser: |
michael@0 | 78 | PORT_ArenaRelease(poolp, mark); |
michael@0 | 79 | return NULL; |
michael@0 | 80 | } |
michael@0 | 81 | |
michael@0 | 82 | /* |
michael@0 | 83 | * NSS_CMSEncryptedData_Destroy - destroy an encryptedData object |
michael@0 | 84 | */ |
michael@0 | 85 | void |
michael@0 | 86 | NSS_CMSEncryptedData_Destroy(NSSCMSEncryptedData *encd) |
michael@0 | 87 | { |
michael@0 | 88 | /* everything's in a pool, so don't worry about the storage */ |
michael@0 | 89 | NSS_CMSContentInfo_Destroy(&(encd->contentInfo)); |
michael@0 | 90 | return; |
michael@0 | 91 | } |
michael@0 | 92 | |
michael@0 | 93 | /* |
michael@0 | 94 | * NSS_CMSEncryptedData_GetContentInfo - return pointer to encryptedData object's contentInfo |
michael@0 | 95 | */ |
michael@0 | 96 | NSSCMSContentInfo * |
michael@0 | 97 | NSS_CMSEncryptedData_GetContentInfo(NSSCMSEncryptedData *encd) |
michael@0 | 98 | { |
michael@0 | 99 | return &(encd->contentInfo); |
michael@0 | 100 | } |
michael@0 | 101 | |
michael@0 | 102 | /* |
michael@0 | 103 | * NSS_CMSEncryptedData_Encode_BeforeStart - do all the necessary things to a EncryptedData |
michael@0 | 104 | * before encoding begins. |
michael@0 | 105 | * |
michael@0 | 106 | * In particular: |
michael@0 | 107 | * - set the correct version value. |
michael@0 | 108 | * - get the encryption key |
michael@0 | 109 | */ |
michael@0 | 110 | SECStatus |
michael@0 | 111 | NSS_CMSEncryptedData_Encode_BeforeStart(NSSCMSEncryptedData *encd) |
michael@0 | 112 | { |
michael@0 | 113 | int version; |
michael@0 | 114 | PK11SymKey *bulkkey = NULL; |
michael@0 | 115 | SECItem *dummy; |
michael@0 | 116 | NSSCMSContentInfo *cinfo = &(encd->contentInfo); |
michael@0 | 117 | |
michael@0 | 118 | if (NSS_CMSArray_IsEmpty((void **)encd->unprotectedAttr)) |
michael@0 | 119 | version = NSS_CMS_ENCRYPTED_DATA_VERSION; |
michael@0 | 120 | else |
michael@0 | 121 | version = NSS_CMS_ENCRYPTED_DATA_VERSION_UPATTR; |
michael@0 | 122 | |
michael@0 | 123 | dummy = SEC_ASN1EncodeInteger (encd->cmsg->poolp, &(encd->version), version); |
michael@0 | 124 | if (dummy == NULL) |
michael@0 | 125 | return SECFailure; |
michael@0 | 126 | |
michael@0 | 127 | /* now get content encryption key (bulk key) by using our cmsg callback */ |
michael@0 | 128 | if (encd->cmsg->decrypt_key_cb) |
michael@0 | 129 | bulkkey = (*encd->cmsg->decrypt_key_cb)(encd->cmsg->decrypt_key_cb_arg, |
michael@0 | 130 | NSS_CMSContentInfo_GetContentEncAlg(cinfo)); |
michael@0 | 131 | if (bulkkey == NULL) |
michael@0 | 132 | return SECFailure; |
michael@0 | 133 | |
michael@0 | 134 | /* store the bulk key in the contentInfo so that the encoder can find it */ |
michael@0 | 135 | NSS_CMSContentInfo_SetBulkKey(cinfo, bulkkey); |
michael@0 | 136 | PK11_FreeSymKey (bulkkey); |
michael@0 | 137 | |
michael@0 | 138 | return SECSuccess; |
michael@0 | 139 | } |
michael@0 | 140 | |
michael@0 | 141 | /* |
michael@0 | 142 | * NSS_CMSEncryptedData_Encode_BeforeData - set up encryption |
michael@0 | 143 | */ |
michael@0 | 144 | SECStatus |
michael@0 | 145 | NSS_CMSEncryptedData_Encode_BeforeData(NSSCMSEncryptedData *encd) |
michael@0 | 146 | { |
michael@0 | 147 | NSSCMSContentInfo *cinfo; |
michael@0 | 148 | PK11SymKey *bulkkey; |
michael@0 | 149 | SECAlgorithmID *algid; |
michael@0 | 150 | SECStatus rv; |
michael@0 | 151 | |
michael@0 | 152 | cinfo = &(encd->contentInfo); |
michael@0 | 153 | |
michael@0 | 154 | /* find bulkkey and algorithm - must have been set by NSS_CMSEncryptedData_Encode_BeforeStart */ |
michael@0 | 155 | bulkkey = NSS_CMSContentInfo_GetBulkKey(cinfo); |
michael@0 | 156 | if (bulkkey == NULL) |
michael@0 | 157 | return SECFailure; |
michael@0 | 158 | algid = NSS_CMSContentInfo_GetContentEncAlg(cinfo); |
michael@0 | 159 | if (algid == NULL) |
michael@0 | 160 | return SECFailure; |
michael@0 | 161 | |
michael@0 | 162 | rv = NSS_CMSContentInfo_Private_Init(cinfo); |
michael@0 | 163 | if (rv != SECSuccess) { |
michael@0 | 164 | return SECFailure; |
michael@0 | 165 | } |
michael@0 | 166 | /* this may modify algid (with IVs generated in a token). |
michael@0 | 167 | * it is therefore essential that algid is a pointer to the "real" contentEncAlg, |
michael@0 | 168 | * not just to a copy */ |
michael@0 | 169 | cinfo->privateInfo->ciphcx = NSS_CMSCipherContext_StartEncrypt(encd->cmsg->poolp, bulkkey, algid); |
michael@0 | 170 | PK11_FreeSymKey(bulkkey); |
michael@0 | 171 | if (cinfo->privateInfo->ciphcx == NULL) |
michael@0 | 172 | return SECFailure; |
michael@0 | 173 | |
michael@0 | 174 | return SECSuccess; |
michael@0 | 175 | } |
michael@0 | 176 | |
michael@0 | 177 | /* |
michael@0 | 178 | * NSS_CMSEncryptedData_Encode_AfterData - finalize this encryptedData for encoding |
michael@0 | 179 | */ |
michael@0 | 180 | SECStatus |
michael@0 | 181 | NSS_CMSEncryptedData_Encode_AfterData(NSSCMSEncryptedData *encd) |
michael@0 | 182 | { |
michael@0 | 183 | if (encd->contentInfo.privateInfo && encd->contentInfo.privateInfo->ciphcx) { |
michael@0 | 184 | NSS_CMSCipherContext_Destroy(encd->contentInfo.privateInfo->ciphcx); |
michael@0 | 185 | encd->contentInfo.privateInfo->ciphcx = NULL; |
michael@0 | 186 | } |
michael@0 | 187 | |
michael@0 | 188 | /* nothing to do after data */ |
michael@0 | 189 | return SECSuccess; |
michael@0 | 190 | } |
michael@0 | 191 | |
michael@0 | 192 | |
michael@0 | 193 | /* |
michael@0 | 194 | * NSS_CMSEncryptedData_Decode_BeforeData - find bulk key & set up decryption |
michael@0 | 195 | */ |
michael@0 | 196 | SECStatus |
michael@0 | 197 | NSS_CMSEncryptedData_Decode_BeforeData(NSSCMSEncryptedData *encd) |
michael@0 | 198 | { |
michael@0 | 199 | PK11SymKey *bulkkey = NULL; |
michael@0 | 200 | NSSCMSContentInfo *cinfo; |
michael@0 | 201 | SECAlgorithmID *bulkalg; |
michael@0 | 202 | SECStatus rv = SECFailure; |
michael@0 | 203 | |
michael@0 | 204 | cinfo = &(encd->contentInfo); |
michael@0 | 205 | |
michael@0 | 206 | bulkalg = NSS_CMSContentInfo_GetContentEncAlg(cinfo); |
michael@0 | 207 | |
michael@0 | 208 | if (encd->cmsg->decrypt_key_cb == NULL) /* no callback? no key../ */ |
michael@0 | 209 | goto loser; |
michael@0 | 210 | |
michael@0 | 211 | bulkkey = (*encd->cmsg->decrypt_key_cb)(encd->cmsg->decrypt_key_cb_arg, bulkalg); |
michael@0 | 212 | if (bulkkey == NULL) |
michael@0 | 213 | /* no success finding a bulk key */ |
michael@0 | 214 | goto loser; |
michael@0 | 215 | |
michael@0 | 216 | NSS_CMSContentInfo_SetBulkKey(cinfo, bulkkey); |
michael@0 | 217 | |
michael@0 | 218 | rv = NSS_CMSContentInfo_Private_Init(cinfo); |
michael@0 | 219 | if (rv != SECSuccess) { |
michael@0 | 220 | goto loser; |
michael@0 | 221 | } |
michael@0 | 222 | rv = SECFailure; |
michael@0 | 223 | |
michael@0 | 224 | cinfo->privateInfo->ciphcx = NSS_CMSCipherContext_StartDecrypt(bulkkey, bulkalg); |
michael@0 | 225 | if (cinfo->privateInfo->ciphcx == NULL) |
michael@0 | 226 | goto loser; /* error has been set by NSS_CMSCipherContext_StartDecrypt */ |
michael@0 | 227 | |
michael@0 | 228 | |
michael@0 | 229 | /* we are done with (this) bulkkey now. */ |
michael@0 | 230 | PK11_FreeSymKey(bulkkey); |
michael@0 | 231 | |
michael@0 | 232 | rv = SECSuccess; |
michael@0 | 233 | |
michael@0 | 234 | loser: |
michael@0 | 235 | return rv; |
michael@0 | 236 | } |
michael@0 | 237 | |
michael@0 | 238 | /* |
michael@0 | 239 | * NSS_CMSEncryptedData_Decode_AfterData - finish decrypting this encryptedData's content |
michael@0 | 240 | */ |
michael@0 | 241 | SECStatus |
michael@0 | 242 | NSS_CMSEncryptedData_Decode_AfterData(NSSCMSEncryptedData *encd) |
michael@0 | 243 | { |
michael@0 | 244 | if (encd->contentInfo.privateInfo && encd->contentInfo.privateInfo->ciphcx) { |
michael@0 | 245 | NSS_CMSCipherContext_Destroy(encd->contentInfo.privateInfo->ciphcx); |
michael@0 | 246 | encd->contentInfo.privateInfo->ciphcx = NULL; |
michael@0 | 247 | } |
michael@0 | 248 | |
michael@0 | 249 | return SECSuccess; |
michael@0 | 250 | } |
michael@0 | 251 | |
michael@0 | 252 | /* |
michael@0 | 253 | * NSS_CMSEncryptedData_Decode_AfterEnd - finish decoding this encryptedData |
michael@0 | 254 | */ |
michael@0 | 255 | SECStatus |
michael@0 | 256 | NSS_CMSEncryptedData_Decode_AfterEnd(NSSCMSEncryptedData *encd) |
michael@0 | 257 | { |
michael@0 | 258 | /* apply final touches */ |
michael@0 | 259 | return SECSuccess; |
michael@0 | 260 | } |