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