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 contentInfo methods.
36 */
37
38#include <Security/SecCmsContentInfo.h>
39
40#include <Security/SecCmsDigestContext.h>
41#include <Security/SecCmsDigestedData.h>
42#include <Security/SecCmsEncryptedData.h>
43#include <Security/SecCmsEnvelopedData.h>
44#include <Security/SecCmsSignedData.h>
45
46#include "cmslocal.h"
47
48//#include "pk11func.h"
49#include "secoid.h"
50#include "SecAsn1Item.h"
51
52#include <security_asn1/secerr.h>
53#include <security_asn1/secport.h>
54
55#include <Security/SecBase.h>
56
57/*
58 * SecCmsContentInfoDestroy - destroy a CMS contentInfo and all of its sub-pieces.
59 */
60void
61SecCmsContentInfoDestroy(SecCmsContentInfoRef cinfo)
62{
63    SECOidTag kind;
64
65    kind = SecCmsContentInfoGetContentTypeTag(cinfo);
66    switch (kind) {
67    case SEC_OID_PKCS7_ENVELOPED_DATA:
68	SecCmsEnvelopedDataDestroy(cinfo->content.envelopedData);
69	break;
70      case SEC_OID_PKCS7_SIGNED_DATA:
71	SecCmsSignedDataDestroy(cinfo->content.signedData);
72	break;
73      case SEC_OID_PKCS7_ENCRYPTED_DATA:
74	SecCmsEncryptedDataDestroy(cinfo->content.encryptedData);
75	break;
76      case SEC_OID_PKCS7_DIGESTED_DATA:
77	SecCmsDigestedDataDestroy(cinfo->content.digestedData);
78	break;
79      default:
80	/* XXX Anything else that needs to be "manually" freed/destroyed? */
81	break;
82    }
83    if (cinfo->digcx) {
84	/* must destroy digest objects */
85	SecCmsDigestContextCancel(cinfo->digcx);
86	cinfo->digcx = NULL;
87    }
88    if (cinfo->bulkkey)
89	CFRelease(cinfo->bulkkey);
90    /* @@@ private key is only here as a workaround for 3401088.  Note this *must* be released after bulkkey */
91    if (cinfo->privkey)
92	CFRelease(cinfo->privkey);
93
94    if (cinfo->ciphcx) {
95	SecCmsCipherContextDestroy(cinfo->ciphcx);
96	cinfo->ciphcx = NULL;
97    }
98
99    /* we live in a pool, so no need to worry about storage */
100}
101
102/*
103 * SecCmsContentInfoGetChildContentInfo - get content's contentInfo (if it exists)
104 */
105SecCmsContentInfoRef
106SecCmsContentInfoGetChildContentInfo(SecCmsContentInfoRef cinfo)
107{
108    void *ptr = NULL;
109    SecCmsContentInfoRef ccinfo = NULL;
110    SECOidTag tag = SecCmsContentInfoGetContentTypeTag(cinfo);
111    switch (tag) {
112    case SEC_OID_PKCS7_SIGNED_DATA:
113	ptr = (void *)cinfo->content.signedData;
114	ccinfo = &(cinfo->content.signedData->contentInfo);
115	break;
116    case SEC_OID_PKCS7_ENVELOPED_DATA:
117	ptr = (void *)cinfo->content.envelopedData;
118	ccinfo = &(cinfo->content.envelopedData->contentInfo);
119	break;
120    case SEC_OID_PKCS7_DIGESTED_DATA:
121	ptr = (void *)cinfo->content.digestedData;
122	ccinfo = &(cinfo->content.digestedData->contentInfo);
123	break;
124    case SEC_OID_PKCS7_ENCRYPTED_DATA:
125	ptr = (void *)cinfo->content.encryptedData;
126	ccinfo = &(cinfo->content.encryptedData->contentInfo);
127	break;
128    case SEC_OID_PKCS7_DATA:
129    default:
130	break;
131    }
132    return (ptr ? ccinfo : NULL);
133}
134
135/*
136 * SecCmsContentInfoSetContent - set content type & content
137 */
138OSStatus
139SecCmsContentInfoSetContent(SecCmsContentInfoRef cinfo, SECOidTag type, void *ptr)
140{
141    OSStatus rv;
142
143    cinfo->contentTypeTag = SECOID_FindOIDByTag(type);
144    if (cinfo->contentTypeTag == NULL)
145	return errSecParam;
146
147    /* do not copy the oid, just create a reference */
148    rv = SECITEM_CopyItem (cinfo->cmsg->poolp, &(cinfo->contentType), &(cinfo->contentTypeTag->oid));
149    if (rv != SECSuccess)
150	return errSecAllocate;
151
152    cinfo->content.pointer = ptr;
153
154    if (type != SEC_OID_PKCS7_DATA) {
155	/* as we always have some inner data,
156	 * we need to set it to something, just to fool the encoder enough to work on it
157	 * and get us into nss_cms_encoder_notify at that point */
158	cinfo->rawContent = SECITEM_AllocItem(cinfo->cmsg->poolp, NULL, 1);
159	if (cinfo->rawContent == NULL) {
160	    PORT_SetError(SEC_ERROR_NO_MEMORY);
161	    return errSecAllocate;
162	}
163    }
164
165    return errSecSuccess;
166}
167
168/*
169 * SecCmsContentInfoSetContentXXXX - typesafe wrappers for SecCmsContentInfoSetContent
170 */
171
172/*
173 * data == NULL -> pass in data via SecCmsEncoderUpdate
174 * data != NULL -> take this data
175 */
176OSStatus
177SecCmsContentInfoSetContentData(SecCmsContentInfoRef cinfo, CFDataRef dataRef, Boolean detached)
178{
179    SecAsn1Item * data = NULL;
180    if (dataRef) {
181	/* @@@ Fixme CFRetain the passed in data rather than
182	   always copying it for performance. */
183	data = PORT_ArenaAlloc(cinfo->cmsg->poolp, sizeof(SecAsn1Item));
184	data->Length = CFDataGetLength(dataRef);
185	if (data->Length) {
186	    data->Data = PORT_ArenaAlloc(cinfo->cmsg->poolp, data->Length);
187	    memcpy(data->Data, CFDataGetBytePtr(dataRef), data->Length);
188	}
189	else
190	    data->Data = NULL;
191    }
192
193    if (SecCmsContentInfoSetContent(cinfo, SEC_OID_PKCS7_DATA, (void *)data) != SECSuccess)
194	return PORT_GetError();
195    cinfo->rawContent = (detached) ?
196			    NULL : (data) ?
197				data : SECITEM_AllocItem(cinfo->cmsg->poolp, NULL, 1);
198    return errSecSuccess;
199}
200
201OSStatus
202SecCmsContentInfoSetContentSignedData(SecCmsContentInfoRef cinfo, SecCmsSignedDataRef sigd)
203{
204    return SecCmsContentInfoSetContent(cinfo, SEC_OID_PKCS7_SIGNED_DATA, (void *)sigd);
205}
206
207OSStatus
208SecCmsContentInfoSetContentEnvelopedData(SecCmsContentInfoRef cinfo, SecCmsEnvelopedDataRef envd)
209{
210    return SecCmsContentInfoSetContent(cinfo, SEC_OID_PKCS7_ENVELOPED_DATA, (void *)envd);
211}
212
213OSStatus
214SecCmsContentInfoSetContentDigestedData(SecCmsContentInfoRef cinfo, SecCmsDigestedDataRef digd)
215{
216    return SecCmsContentInfoSetContent(cinfo, SEC_OID_PKCS7_DIGESTED_DATA, (void *)digd);
217}
218
219OSStatus
220SecCmsContentInfoSetContentEncryptedData(SecCmsContentInfoRef cinfo, SecCmsEncryptedDataRef encd)
221{
222    return SecCmsContentInfoSetContent(cinfo, SEC_OID_PKCS7_ENCRYPTED_DATA, (void *)encd);
223}
224
225/*
226 * SecCmsContentInfoGetContent - get pointer to inner content
227 *
228 * needs to be casted...
229 */
230void *
231SecCmsContentInfoGetContent(SecCmsContentInfoRef cinfo)
232{
233    SECOidTag tag = (cinfo && cinfo->contentTypeTag)
234		     ? cinfo->contentTypeTag->offset
235		     : SEC_OID_UNKNOWN;
236    switch (tag) {
237    case SEC_OID_PKCS7_DATA:
238    case SEC_OID_PKCS7_SIGNED_DATA:
239    case SEC_OID_PKCS7_ENVELOPED_DATA:
240    case SEC_OID_PKCS7_DIGESTED_DATA:
241    case SEC_OID_PKCS7_ENCRYPTED_DATA:
242	return cinfo->content.pointer;
243    default:
244	return NULL;
245    }
246}
247
248/*
249 * SecCmsContentInfoGetInnerContent - get pointer to innermost content
250 *
251 * this is typically only called by SecCmsMessageGetContent()
252 */
253const SecAsn1Item *
254SecCmsContentInfoGetInnerContent(SecCmsContentInfoRef cinfo)
255{
256    SecCmsContentInfoRef ccinfo;
257    SECOidTag tag;
258    SecAsn1Item * pItem;
259
260    tag = SecCmsContentInfoGetContentTypeTag(cinfo);
261    switch (tag) {
262    case SEC_OID_PKCS7_DATA:
263	/* end of recursion - every message has to have a data cinfo */
264	pItem = cinfo->content.data;
265	break;
266    case SEC_OID_PKCS7_DIGESTED_DATA:
267    case SEC_OID_PKCS7_ENCRYPTED_DATA:
268    case SEC_OID_PKCS7_ENVELOPED_DATA:
269    case SEC_OID_PKCS7_SIGNED_DATA:
270	ccinfo = SecCmsContentInfoGetChildContentInfo(cinfo);
271	if (ccinfo == NULL)
272	    pItem = NULL;
273	else
274	    pItem = SecCmsContentInfoGetContent(ccinfo);
275	break;
276    default:
277	PORT_Assert(0);
278	pItem = NULL;
279	break;
280    }
281    return pItem;
282}
283
284/*
285 * SecCmsContentInfoGetContentType{Tag,OID} - find out (saving pointer to lookup result
286 * for future reference) and return the inner content type.
287 */
288SECOidTag
289SecCmsContentInfoGetContentTypeTag(SecCmsContentInfoRef cinfo)
290{
291    if (cinfo->contentTypeTag == NULL)
292	cinfo->contentTypeTag = SECOID_FindOID(&(cinfo->contentType));
293
294    if (cinfo->contentTypeTag == NULL)
295	return SEC_OID_UNKNOWN;
296
297    return cinfo->contentTypeTag->offset;
298}
299
300SecAsn1Oid *
301SecCmsContentInfoGetContentTypeOID(SecCmsContentInfoRef cinfo)
302{
303    if (cinfo->contentTypeTag == NULL)
304	cinfo->contentTypeTag = SECOID_FindOID(&(cinfo->contentType));
305
306    if (cinfo->contentTypeTag == NULL)
307	return NULL;
308
309    return &(cinfo->contentTypeTag->oid);
310}
311
312/*
313 * SecCmsContentInfoGetContentEncAlgTag - find out (saving pointer to lookup result
314 * for future reference) and return the content encryption algorithm tag.
315 */
316SECOidTag
317SecCmsContentInfoGetContentEncAlgTag(SecCmsContentInfoRef cinfo)
318{
319    if (cinfo->contentEncAlgTag == SEC_OID_UNKNOWN)
320	cinfo->contentEncAlgTag = SECOID_GetAlgorithmTag(&(cinfo->contentEncAlg));
321
322    return cinfo->contentEncAlgTag;
323}
324
325/*
326 * SecCmsContentInfoGetContentEncAlg - find out and return the content encryption algorithm tag.
327 */
328SECAlgorithmID *
329SecCmsContentInfoGetContentEncAlg(SecCmsContentInfoRef cinfo)
330{
331    return &(cinfo->contentEncAlg);
332}
333
334OSStatus
335SecCmsContentInfoSetContentEncAlg(SecCmsContentInfoRef cinfo,
336				  SECOidTag bulkalgtag, const SecAsn1Item *parameters, int keysize)
337{
338    PLArenaPool *poolp = cinfo->cmsg->poolp;
339    OSStatus rv;
340
341    rv = SECOID_SetAlgorithmID(poolp, &(cinfo->contentEncAlg), bulkalgtag, parameters);
342    if (rv != SECSuccess)
343	return SECFailure;
344    cinfo->keysize = keysize;
345    return SECSuccess;
346}
347
348OSStatus
349SecCmsContentInfoSetContentEncAlgID(SecCmsContentInfoRef cinfo,
350				    SECAlgorithmID *algid, int keysize)
351{
352    PLArenaPool *poolp = cinfo->cmsg->poolp;
353    OSStatus rv;
354
355    rv = SECOID_CopyAlgorithmID(poolp, &(cinfo->contentEncAlg), algid);
356    if (rv != SECSuccess)
357	return SECFailure;
358    if (keysize >= 0)
359	cinfo->keysize = keysize;
360    return SECSuccess;
361}
362
363void
364SecCmsContentInfoSetBulkKey(SecCmsContentInfoRef cinfo, SecSymmetricKeyRef bulkkey)
365{
366#ifdef USE_CDSA_CRYPTO
367    const CSSM_KEY *cssmKey = NULL;
368#endif
369    cinfo->bulkkey = bulkkey;
370    CFRetain(cinfo->bulkkey);
371#ifdef USE_CDSA_CRYPTO
372    SecKeyGetCSSMKey(cinfo->bulkkey, &cssmKey);
373    cinfo->keysize = cssmKey ? cssmKey->KeyHeader.LogicalKeySizeInBits : 0;
374#else
375    /* This cast should be always safe, there should be SecSymmetricKeyRef API to get the size anyway */
376    cinfo->keysize = (int)CFDataGetLength((CFDataRef)bulkkey) * 8;
377#endif
378}
379
380SecSymmetricKeyRef
381SecCmsContentInfoGetBulkKey(SecCmsContentInfoRef cinfo)
382{
383    if (cinfo->bulkkey == NULL)
384	return NULL;
385
386    CFRetain(cinfo->bulkkey);
387    return cinfo->bulkkey;
388}
389
390int
391SecCmsContentInfoGetBulkKeySize(SecCmsContentInfoRef cinfo)
392{
393    return cinfo->keysize;
394}
395