1/*
2 * The contents of this file are subject to the Mozilla Public
3 * License Version 1.1 (the "License"); you may not use this file
4 * except in compliance with the License. You may obtain a copy of
5 * the License at http://www.mozilla.org/MPL/
6 *
7 * Software distributed under the License is distributed on an "AS
8 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
9 * implied. See the License for the specific language governing
10 * rights and limitations under the License.
11 *
12 * The Original Code is the Netscape security libraries.
13 *
14 * The Initial Developer of the Original Code is Netscape
15 * Communications Corporation.  Portions created by Netscape are
16 * Copyright (C) 1994-2000 Netscape Communications Corporation.  All
17 * Rights Reserved.
18 *
19 * Contributor(s):
20 *
21 * Alternatively, the contents of this file may be used under the
22 * terms of the GNU General Public License Version 2 or later (the
23 * "GPL"), in which case the provisions of the GPL are applicable
24 * instead of those above.  If you wish to allow use of your
25 * version of this file only under the terms of the GPL and not to
26 * allow others to use your version of this file under the MPL,
27 * indicate your decision by deleting the provisions above and
28 * replace them with the notice and other provisions required by
29 * the GPL.  If you do not delete the provisions above, a recipient
30 * may use your version of this file under either the MPL or the
31 * GPL.
32 */
33
34/*
35 * CMS encryptedData methods.
36 */
37
38#include <Security/SecCmsEncryptedData.h>
39
40#include <Security/SecCmsContentInfo.h>
41
42#include "cmslocal.h"
43
44#include "secitem.h"
45#include "secoid.h"
46#include <security_asn1/secasn1.h>
47#include <security_asn1/secerr.h>
48
49/*
50 * SecCmsEncryptedDataCreate - create an empty encryptedData object.
51 *
52 * "algorithm" specifies the bulk encryption algorithm to use.
53 * "keysize" is the key size.
54 *
55 * An error results in a return value of NULL and an error set.
56 * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
57 */
58SecCmsEncryptedDataRef
59SecCmsEncryptedDataCreate(SecCmsMessageRef cmsg, SECOidTag algorithm, int keysize)
60{
61    void *mark;
62    SecCmsEncryptedDataRef encd;
63    PLArenaPool *poolp;
64    SECAlgorithmID *pbe_algid;
65    OSStatus rv;
66
67    poolp = cmsg->poolp;
68
69    mark = PORT_ArenaMark(poolp);
70
71    encd = (SecCmsEncryptedDataRef)PORT_ArenaZAlloc(poolp, sizeof(SecCmsEncryptedData));
72    if (encd == NULL)
73	goto loser;
74
75    encd->cmsg = cmsg;
76
77    /* version is set in SecCmsEncryptedDataEncodeBeforeStart() */
78
79    switch (algorithm) {
80    /* XXX hmmm... hardcoded algorithms? */
81    case SEC_OID_RC2_CBC:
82    case SEC_OID_DES_EDE3_CBC:
83    case SEC_OID_DES_CBC:
84	rv = SecCmsContentInfoSetContentEncAlg((SecArenaPoolRef)poolp, &(encd->contentInfo), algorithm, NULL, keysize);
85	break;
86    default:
87	/* Assume password-based-encryption.  At least, try that. */
88#if 1
89	// @@@ Fix me
90	pbe_algid = NULL;
91#else
92	pbe_algid = PK11_CreatePBEAlgorithmID(algorithm, 1, NULL);
93#endif
94	if (pbe_algid == NULL) {
95	    rv = SECFailure;
96	    break;
97	}
98	rv = SecCmsContentInfoSetContentEncAlgID((SecArenaPoolRef)poolp, &(encd->contentInfo), pbe_algid, keysize);
99	SECOID_DestroyAlgorithmID (pbe_algid, PR_TRUE);
100	break;
101    }
102    if (rv != SECSuccess)
103	goto loser;
104
105    PORT_ArenaUnmark(poolp, mark);
106    return encd;
107
108loser:
109    PORT_ArenaRelease(poolp, mark);
110    return NULL;
111}
112
113/*
114 * SecCmsEncryptedDataDestroy - destroy an encryptedData object
115 */
116void
117SecCmsEncryptedDataDestroy(SecCmsEncryptedDataRef encd)
118{
119    /* everything's in a pool, so don't worry about the storage */
120    SecCmsContentInfoDestroy(&(encd->contentInfo));
121    return;
122}
123
124/*
125 * SecCmsEncryptedDataGetContentInfo - return pointer to encryptedData object's contentInfo
126 */
127SecCmsContentInfoRef
128SecCmsEncryptedDataGetContentInfo(SecCmsEncryptedDataRef encd)
129{
130    return &(encd->contentInfo);
131}
132
133/*
134 * SecCmsEncryptedDataEncodeBeforeStart - do all the necessary things to a EncryptedData
135 *     before encoding begins.
136 *
137 * In particular:
138 *  - set the correct version value.
139 *  - get the encryption key
140 */
141OSStatus
142SecCmsEncryptedDataEncodeBeforeStart(SecCmsEncryptedDataRef encd)
143{
144    int version;
145    SecSymmetricKeyRef bulkkey = NULL;
146    CSSM_DATA_PTR dummy;
147    SecCmsContentInfoRef cinfo = &(encd->contentInfo);
148
149    if (SecCmsArrayIsEmpty((void **)encd->unprotectedAttr))
150	version = SEC_CMS_ENCRYPTED_DATA_VERSION;
151    else
152	version = SEC_CMS_ENCRYPTED_DATA_VERSION_UPATTR;
153
154    dummy = SEC_ASN1EncodeInteger (encd->cmsg->poolp, &(encd->version), version);
155    if (dummy == NULL)
156	return SECFailure;
157
158    /* now get content encryption key (bulk key) by using our cmsg callback */
159    if (encd->cmsg->decrypt_key_cb)
160	bulkkey = (*encd->cmsg->decrypt_key_cb)(encd->cmsg->decrypt_key_cb_arg,
161		    SecCmsContentInfoGetContentEncAlg(cinfo));
162    if (bulkkey == NULL)
163	return SECFailure;
164
165    /* store the bulk key in the contentInfo so that the encoder can find it */
166    SecCmsContentInfoSetBulkKey(cinfo, bulkkey);
167    CFRelease(bulkkey); /* This assumes the decrypt_key_cb hands us a copy of the key --mb */
168
169    return SECSuccess;
170}
171
172/*
173 * SecCmsEncryptedDataEncodeBeforeData - set up encryption
174 */
175OSStatus
176SecCmsEncryptedDataEncodeBeforeData(SecCmsEncryptedDataRef encd)
177{
178    SecCmsContentInfoRef cinfo;
179    SecSymmetricKeyRef bulkkey;
180    SECAlgorithmID *algid;
181
182    cinfo = &(encd->contentInfo);
183
184    /* find bulkkey and algorithm - must have been set by SecCmsEncryptedDataEncodeBeforeStart */
185    bulkkey = SecCmsContentInfoGetBulkKey(cinfo);
186    if (bulkkey == NULL)
187	return SECFailure;
188    algid = SecCmsContentInfoGetContentEncAlg(cinfo);
189    if (algid == NULL)
190	return SECFailure;
191
192    /* this may modify algid (with IVs generated in a token).
193     * it is therefore essential that algid is a pointer to the "real" contentEncAlg,
194     * not just to a copy */
195    cinfo->ciphcx = SecCmsCipherContextStartEncrypt(encd->cmsg->poolp, bulkkey, algid);
196    CFRelease(bulkkey);
197    if (cinfo->ciphcx == NULL)
198	return SECFailure;
199
200    return SECSuccess;
201}
202
203/*
204 * SecCmsEncryptedDataEncodeAfterData - finalize this encryptedData for encoding
205 */
206OSStatus
207SecCmsEncryptedDataEncodeAfterData(SecCmsEncryptedDataRef encd)
208{
209    if (encd->contentInfo.ciphcx) {
210	SecCmsCipherContextDestroy(encd->contentInfo.ciphcx);
211	encd->contentInfo.ciphcx = NULL;
212    }
213
214    /* nothing to do after data */
215    return SECSuccess;
216}
217
218
219/*
220 * SecCmsEncryptedDataDecodeBeforeData - find bulk key & set up decryption
221 */
222OSStatus
223SecCmsEncryptedDataDecodeBeforeData(SecCmsEncryptedDataRef encd)
224{
225    SecSymmetricKeyRef bulkkey = NULL;
226    SecCmsContentInfoRef cinfo;
227    SECAlgorithmID *bulkalg;
228    OSStatus rv = SECFailure;
229
230    cinfo = &(encd->contentInfo);
231
232    bulkalg = SecCmsContentInfoGetContentEncAlg(cinfo);
233
234    if (encd->cmsg->decrypt_key_cb == NULL)	/* no callback? no key../ */
235	goto loser;
236
237    bulkkey = (*encd->cmsg->decrypt_key_cb)(encd->cmsg->decrypt_key_cb_arg, bulkalg);
238    if (bulkkey == NULL)
239	/* no success finding a bulk key */
240	goto loser;
241
242    SecCmsContentInfoSetBulkKey(cinfo, bulkkey);
243
244    cinfo->ciphcx = SecCmsCipherContextStartDecrypt(bulkkey, bulkalg);
245    if (cinfo->ciphcx == NULL)
246	goto loser;		/* error has been set by SecCmsCipherContextStartDecrypt */
247
248#if 1
249    // @@@ Not done yet
250#else
251    /*
252     * HACK ALERT!!
253     * For PKCS5 Encryption Algorithms, the bulkkey is actually a different
254     * structure.  Therefore, we need to set the bulkkey to the actual key
255     * prior to freeing it.
256     */
257    if (SEC_PKCS5IsAlgorithmPBEAlg(bulkalg)) {
258	SEC_PKCS5KeyAndPassword *keyPwd = (SEC_PKCS5KeyAndPassword *)bulkkey;
259	bulkkey = keyPwd->key;
260    }
261#endif
262
263    /* we are done with (this) bulkkey now. */
264    CFRelease(bulkkey);
265
266    rv = SECSuccess;
267
268loser:
269    return rv;
270}
271
272/*
273 * SecCmsEncryptedDataDecodeAfterData - finish decrypting this encryptedData's content
274 */
275OSStatus
276SecCmsEncryptedDataDecodeAfterData(SecCmsEncryptedDataRef encd)
277{
278    if (encd->contentInfo.ciphcx) {
279	SecCmsCipherContextDestroy(encd->contentInfo.ciphcx);
280	encd->contentInfo.ciphcx = NULL;
281    }
282
283    return SECSuccess;
284}
285
286/*
287 * SecCmsEncryptedDataDecodeAfterEnd - finish decoding this encryptedData
288 */
289OSStatus
290SecCmsEncryptedDataDecodeAfterEnd(SecCmsEncryptedDataRef encd)
291{
292    /* apply final touches */
293    return SECSuccess;
294}
295