]> git.saurik.com Git - apple/security.git/blame - libsecurity_smime/lib/cmssiginfo.c
Security-57740.60.18.tar.gz
[apple/security.git] / libsecurity_smime / lib / cmssiginfo.c
CommitLineData
b1ab9ed8
A
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 signerInfo methods.
36 */
37
38#include <Security/SecCmsSignerInfo.h>
39#include "SecSMIMEPriv.h"
40
41#include "cmslocal.h"
42
43#include "cert.h"
d8f41ccd 44#include "SecAsn1Item.h"
b1ab9ed8
A
45#include "secoid.h"
46#include "cryptohi.h"
47
48#include <security_asn1/secasn1.h>
49#include <security_asn1/secerr.h>
d8f41ccd
A
50#include <security_asn1/secport.h>
51
b1ab9ed8 52#include <Security/SecIdentity.h>
d8f41ccd
A
53#include <Security/SecCertificateInternal.h>
54#include <Security/SecInternal.h>
b1ab9ed8 55#include <Security/SecKeyPriv.h>
d8f41ccd 56#include <utilities/SecCFWrappers.h>
b1ab9ed8 57#include <CoreFoundation/CFTimeZone.h>
e3d460c9 58#include <Security/SecBasePriv.h>
b1ab9ed8 59
b1ab9ed8
A
60
61#define HIDIGIT(v) (((v) / 10) + '0')
62#define LODIGIT(v) (((v) % 10) + '0')
63
64#define ISDIGIT(dig) (((dig) >= '0') && ((dig) <= '9'))
65#define CAPTURE(var,p,label) \
66{ \
67 if (!ISDIGIT((p)[0]) || !ISDIGIT((p)[1])) goto label; \
68 (var) = ((p)[0] - '0') * 10 + ((p)[1] - '0'); \
69}
70
b1ab9ed8
A
71
72static OSStatus
d8f41ccd 73DER_UTCTimeToCFDate(const SecAsn1Item * utcTime, CFAbsoluteTime *date)
b1ab9ed8 74{
b1ab9ed8 75 char *string = (char *)utcTime->Data;
d8f41ccd 76 int year, month, mday, hour, minute, second, hourOff, minOff;
b1ab9ed8
A
77
78 /* Verify time is formatted properly and capture information */
79 second = 0;
80 hourOff = 0;
81 minOff = 0;
82 CAPTURE(year,string+0,loser);
83 if (year < 50) {
84 /* ASSUME that year # is in the 2000's, not the 1900's */
d8f41ccd
A
85 year += 2000;
86 } else {
87 year += 1900;
b1ab9ed8
A
88 }
89 CAPTURE(month,string+2,loser);
90 if ((month == 0) || (month > 12)) goto loser;
91 CAPTURE(mday,string+4,loser);
92 if ((mday == 0) || (mday > 31)) goto loser;
93 CAPTURE(hour,string+6,loser);
94 if (hour > 23) goto loser;
95 CAPTURE(minute,string+8,loser);
96 if (minute > 59) goto loser;
97 if (ISDIGIT(string[10])) {
98 CAPTURE(second,string+10,loser);
99 if (second > 59) goto loser;
100 string += 2;
101 }
102 if (string[10] == '+') {
103 CAPTURE(hourOff,string+11,loser);
104 if (hourOff > 23) goto loser;
105 CAPTURE(minOff,string+13,loser);
106 if (minOff > 59) goto loser;
107 } else if (string[10] == '-') {
108 CAPTURE(hourOff,string+11,loser);
109 if (hourOff > 23) goto loser;
110 hourOff = -hourOff;
111 CAPTURE(minOff,string+13,loser);
112 if (minOff > 59) goto loser;
113 minOff = -minOff;
114 } else if (string[10] != 'Z') {
115 goto loser;
116 }
117
d8f41ccd
A
118 if (hourOff == 0 && minOff == 0) {
119 *date = CFAbsoluteTimeForGregorianZuluMoment(year, month, mday, hour, minute, second);
120 } else {
121 CFTimeZoneRef tz = CFTimeZoneCreateWithTimeIntervalFromGMT(kCFAllocatorDefault, (hourOff * 60 + minOff) * 60);
122 *date = CFAbsoluteTimeForGregorianMoment(tz, year, month, mday, hour, minute, second);
123 CFReleaseSafe(tz);
b1ab9ed8
A
124 }
125
b1ab9ed8
A
126 return SECSuccess;
127
128loser:
129 return SECFailure;
130}
131
132static OSStatus
d8f41ccd 133DER_CFDateToUTCTime(CFAbsoluteTime date, SecAsn1Item * utcTime)
b1ab9ed8 134{
b1ab9ed8 135 unsigned char *d;
b1ab9ed8
A
136
137 utcTime->Length = 13;
138 utcTime->Data = d = PORT_Alloc(13);
139 if (!utcTime->Data)
140 return SECFailure;
d8f41ccd 141
d87e1158
A
142 __block int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0;
143 __block bool result;
144 SecCFCalendarDoWithZuluCalendar(^(CFCalendarRef zuluCalendar) {
145 result = CFCalendarDecomposeAbsoluteTime(zuluCalendar, date, "yMdHms", &year, &month, &day, &hour, &minute, &second);
146 });
147 if (!result)
d8f41ccd
A
148 return SECFailure;
149
b1ab9ed8 150 /* UTC time does not handle the years before 1950 */
d8f41ccd
A
151 if (year < 1950)
152 return SECFailure;
b1ab9ed8
A
153
154 /* remove the century since it's added to the year by the
155 CFAbsoluteTimeGetGregorianDate routine, but is not needed for UTC time */
d8f41ccd
A
156 year %= 100;
157
158 d[0] = HIDIGIT(year);
159 d[1] = LODIGIT(year);
160 d[2] = HIDIGIT(month);
161 d[3] = LODIGIT(month);
162 d[4] = HIDIGIT(day);
163 d[5] = LODIGIT(day);
164 d[6] = HIDIGIT(hour);
165 d[7] = LODIGIT(hour);
166 d[8] = HIDIGIT(minute);
167 d[9] = LODIGIT(minute);
b1ab9ed8
A
168 d[10] = HIDIGIT(second);
169 d[11] = LODIGIT(second);
170 d[12] = 'Z';
171 return SECSuccess;
172}
173
174/* =============================================================================
175 * SIGNERINFO
176 */
177SecCmsSignerInfoRef
d8f41ccd 178nss_cmssignerinfo_create(SecCmsSignedDataRef sigd, SecCmsSignerIDSelector type, SecCertificateRef cert, const SecAsn1Item *subjKeyID, SecPublicKeyRef pubKey, SecPrivateKeyRef signingKey, SECOidTag digestalgtag);
b1ab9ed8
A
179
180SecCmsSignerInfoRef
d8f41ccd 181SecCmsSignerInfoCreateWithSubjKeyID(SecCmsSignedDataRef sigd, const SecAsn1Item *subjKeyID, SecPublicKeyRef pubKey, SecPrivateKeyRef signingKey, SECOidTag digestalgtag)
b1ab9ed8 182{
d8f41ccd 183 return nss_cmssignerinfo_create(sigd, SecCmsSignerIDSubjectKeyID, NULL, subjKeyID, pubKey, signingKey, digestalgtag);
b1ab9ed8
A
184}
185
186SecCmsSignerInfoRef
d8f41ccd 187SecCmsSignerInfoCreate(SecCmsSignedDataRef sigd, SecIdentityRef identity, SECOidTag digestalgtag)
b1ab9ed8
A
188{
189 SecCmsSignerInfoRef signerInfo = NULL;
190 SecCertificateRef cert = NULL;
191 SecPrivateKeyRef signingKey = NULL;
192
193 if (SecIdentityCopyCertificate(identity, &cert))
194 goto loser;
195 if (SecIdentityCopyPrivateKey(identity, &signingKey))
196 goto loser;
197
d8f41ccd 198 signerInfo = nss_cmssignerinfo_create(sigd, SecCmsSignerIDIssuerSN, cert, NULL, NULL, signingKey, digestalgtag);
b1ab9ed8
A
199
200loser:
201 if (cert)
202 CFRelease(cert);
203 if (signingKey)
204 CFRelease(signingKey);
205
206 return signerInfo;
207}
208
209SecCmsSignerInfoRef
d8f41ccd 210nss_cmssignerinfo_create(SecCmsSignedDataRef sigd, SecCmsSignerIDSelector type, SecCertificateRef cert, const SecAsn1Item *subjKeyID, SecPublicKeyRef pubKey, SecPrivateKeyRef signingKey, SECOidTag digestalgtag)
b1ab9ed8
A
211{
212 void *mark;
213 SecCmsSignerInfoRef signerinfo;
214 int version;
215 PLArenaPool *poolp;
216
d8f41ccd 217 poolp = sigd->contentInfo.cmsg->poolp;
b1ab9ed8
A
218
219 mark = PORT_ArenaMark(poolp);
220
221 signerinfo = (SecCmsSignerInfoRef)PORT_ArenaZAlloc(poolp, sizeof(SecCmsSignerInfo));
222 if (signerinfo == NULL) {
223 PORT_ArenaRelease(poolp, mark);
224 return NULL;
225 }
226
227
d8f41ccd 228 signerinfo->signedData = sigd;
b1ab9ed8
A
229
230 switch(type) {
231 case SecCmsSignerIDIssuerSN:
232 signerinfo->signerIdentifier.identifierType = SecCmsSignerIDIssuerSN;
233 if ((signerinfo->cert = CERT_DupCertificate(cert)) == NULL)
234 goto loser;
235 if ((signerinfo->signerIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert)) == NULL)
236 goto loser;
b1ab9ed8
A
237 break;
238 case SecCmsSignerIDSubjectKeyID:
239 signerinfo->signerIdentifier.identifierType = SecCmsSignerIDSubjectKeyID;
240 PORT_Assert(subjKeyID);
241 if (!subjKeyID)
242 goto loser;
d8f41ccd 243 signerinfo->signerIdentifier.id.subjectKeyID = PORT_ArenaNew(poolp, SecAsn1Item);
6b200bc3
A
244 if (SECITEM_CopyItem(poolp, signerinfo->signerIdentifier.id.subjectKeyID,
245 subjKeyID)) {
246 goto loser;
247 }
b1ab9ed8
A
248 signerinfo->pubKey = SECKEY_CopyPublicKey(pubKey);
249 if (!signerinfo->pubKey)
250 goto loser;
251 break;
252 default:
253 goto loser;
254 }
255
256 if (!signingKey)
257 goto loser;
258
259 signerinfo->signingKey = SECKEY_CopyPrivateKey(signingKey);
260 if (!signerinfo->signingKey)
261 goto loser;
262
263 /* set version right now */
264 version = SEC_CMS_SIGNER_INFO_VERSION_ISSUERSN;
265 /* RFC2630 5.3 "version is the syntax version number. If the .... " */
266 if (signerinfo->signerIdentifier.identifierType == SecCmsSignerIDSubjectKeyID)
267 version = SEC_CMS_SIGNER_INFO_VERSION_SUBJKEY;
268 (void)SEC_ASN1EncodeInteger(poolp, &(signerinfo->version), (long)version);
269
270 if (SECOID_SetAlgorithmID(poolp, &signerinfo->digestAlg, digestalgtag, NULL) != SECSuccess)
271 goto loser;
272
d8f41ccd
A
273 if (SecCmsSignedDataAddSignerInfo(sigd, signerinfo))
274 goto loser;
275
b1ab9ed8
A
276 PORT_ArenaUnmark(poolp, mark);
277 return signerinfo;
278
279loser:
280 PORT_ArenaRelease(poolp, mark);
281 return NULL;
282}
283
284/*
285 * SecCmsSignerInfoDestroy - destroy a SignerInfo data structure
286 */
287void
288SecCmsSignerInfoDestroy(SecCmsSignerInfoRef si)
289{
d8f41ccd 290 if (si->cert != NULL)
b1ab9ed8 291 CERT_DestroyCertificate(si->cert);
d8f41ccd
A
292
293 if (si->certList != NULL)
b1ab9ed8 294 CFRelease(si->certList);
d8f41ccd 295
b1ab9ed8
A
296 /* XXX storage ??? */
297}
298
d8f41ccd
A
299static SecAsn1AlgId SecCertificateGetPublicKeyAlgorithmID(SecCertificateRef cert)
300{
301 const DERAlgorithmId *length_data_swapped = SecCertificateGetPublicKeyAlgorithm(cert);
302 SecAsn1AlgId temp = {
303 { length_data_swapped->oid.length, length_data_swapped->oid.data },
304 { length_data_swapped->params.length, length_data_swapped->params.data } };
305
306 return temp;
307}
308
b1ab9ed8
A
309/*
310 * SecCmsSignerInfoSign - sign something
311 *
312 */
313OSStatus
d8f41ccd 314SecCmsSignerInfoSign(SecCmsSignerInfoRef signerinfo, SecAsn1Item * digest, SecAsn1Item * contentType)
b1ab9ed8
A
315{
316 SecCertificateRef cert;
317 SecPrivateKeyRef privkey = NULL;
318 SECOidTag digestalgtag;
319 SECOidTag pubkAlgTag;
d8f41ccd 320 SecAsn1Item signature = { 0 };
b1ab9ed8 321 OSStatus rv;
60c433a9 322 PLArenaPool *poolp, *tmppoolp = NULL;
d8f41ccd 323 const SECAlgorithmID *algID = NULL;
b1ab9ed8
A
324 //CERTSubjectPublicKeyInfo *spki;
325
326 PORT_Assert (digest != NULL);
327
d8f41ccd 328 poolp = signerinfo->signedData->contentInfo.cmsg->poolp;
b1ab9ed8 329
fa7225c8
A
330 SecAsn1AlgId _algID;
331
b1ab9ed8
A
332 switch (signerinfo->signerIdentifier.identifierType) {
333 case SecCmsSignerIDIssuerSN:
334 privkey = signerinfo->signingKey;
335 signerinfo->signingKey = NULL;
336 cert = signerinfo->cert;
fa7225c8 337 _algID = SecCertificateGetPublicKeyAlgorithmID(cert);
d8f41ccd 338 algID = &_algID;
b1ab9ed8
A
339 break;
340 case SecCmsSignerIDSubjectKeyID:
341 privkey = signerinfo->signingKey;
342 signerinfo->signingKey = NULL;
b1ab9ed8
A
343 CFRelease(signerinfo->pubKey);
344 signerinfo->pubKey = NULL;
b1ab9ed8
A
345 break;
346 default:
347 PORT_SetError(SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE);
348 goto loser;
349 }
350 digestalgtag = SecCmsSignerInfoGetDigestAlgTag(signerinfo);
b1ab9ed8 351 pubkAlgTag = SECOID_GetAlgorithmTag(algID);
fa7225c8
A
352
353 /* we no longer support signing with MD5 */
354 if (digestalgtag == SEC_OID_MD5) {
355 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
356 goto loser;
357 }
358
b1ab9ed8 359 if (signerinfo->authAttr != NULL) {
d8f41ccd 360 SecAsn1Item encoded_attrs;
b1ab9ed8
A
361
362 /* find and fill in the message digest attribute. */
363 rv = SecCmsAttributeArraySetAttr(poolp, &(signerinfo->authAttr),
364 SEC_OID_PKCS9_MESSAGE_DIGEST, digest, PR_FALSE);
365 if (rv != SECSuccess)
366 goto loser;
367
368 if (contentType != NULL) {
369 /* if the caller wants us to, find and fill in the content type attribute. */
370 rv = SecCmsAttributeArraySetAttr(poolp, &(signerinfo->authAttr),
371 SEC_OID_PKCS9_CONTENT_TYPE, contentType, PR_FALSE);
372 if (rv != SECSuccess)
373 goto loser;
374 }
375
376 if ((tmppoolp = PORT_NewArena (1024)) == NULL) {
377 PORT_SetError(SEC_ERROR_NO_MEMORY);
378 goto loser;
379 }
380
381 /*
382 * Before encoding, reorder the attributes so that when they
383 * are encoded, they will be conforming DER, which is required
384 * to have a specific order and that is what must be used for
385 * the hash/signature. We do this here, rather than building
386 * it into EncodeAttributes, because we do not want to do
387 * such reordering on incoming messages (which also uses
388 * EncodeAttributes) or our old signatures (and other "broken"
389 * implementations) will not verify. So, we want to guarantee
390 * that we send out good DER encodings of attributes, but not
391 * to expect to receive them.
392 */
393 if (SecCmsAttributeArrayReorder(signerinfo->authAttr) != SECSuccess)
394 goto loser;
395
396 encoded_attrs.Data = NULL;
397 encoded_attrs.Length = 0;
398 if (SecCmsAttributeArrayEncode(tmppoolp, &(signerinfo->authAttr),
399 &encoded_attrs) == NULL)
400 goto loser;
401
d8f41ccd
A
402 signature.Length = SecKeyGetSize(privkey, kSecKeySignatureSize);
403 signature.Data = PORT_ZAlloc(signature.Length);
404 if (!signature.Data) {
405 signature.Length = 0;
406 goto loser;
407 }
408 rv = SecKeyDigestAndSign(privkey, &signerinfo->digestAlg, encoded_attrs.Data, encoded_attrs.Length, signature.Data, &signature.Length);
409 if (rv) {
410 PORT_ZFree(signature.Data, signature.Length);
411 signature.Length = 0;
412 }
d8f41ccd 413
b1ab9ed8 414 PORT_FreeArena(tmppoolp, PR_FALSE); /* awkward memory management :-( */
60c433a9 415 tmppoolp = 0;
b1ab9ed8 416 } else {
d8f41ccd
A
417 signature.Length = SecKeyGetSize(privkey, kSecKeySignatureSize);
418 signature.Data = PORT_ZAlloc(signature.Length);
419 if (!signature.Data) {
420 signature.Length = 0;
421 goto loser;
422 }
423 rv = SecKeySignDigest(privkey, &signerinfo->digestAlg, digest->Data, digest->Length,
424 signature.Data, &signature.Length);
425 if (rv) {
426 PORT_ZFree(signature.Data, signature.Length);
427 signature.Length = 0;
428 }
b1ab9ed8
A
429 }
430 SECKEY_DestroyPrivateKey(privkey);
431 privkey = NULL;
432
433 if (rv != SECSuccess)
434 goto loser;
435
436 if (SECITEM_CopyItem(poolp, &(signerinfo->encDigest), &signature)
437 != SECSuccess)
438 goto loser;
439
440 SECITEM_FreeItem(&signature, PR_FALSE);
441
b1ab9ed8
A
442 if (SECOID_SetAlgorithmID(poolp, &(signerinfo->digestEncAlg), pubkAlgTag,
443 NULL) != SECSuccess)
444 goto loser;
445
446 return SECSuccess;
447
448loser:
449 if (signature.Length != 0)
450 SECITEM_FreeItem (&signature, PR_FALSE);
451 if (privkey)
452 SECKEY_DestroyPrivateKey(privkey);
60c433a9
A
453 if (tmppoolp)
454 PORT_FreeArena(tmppoolp, PR_FALSE);
b1ab9ed8
A
455 return SECFailure;
456}
457
d8f41ccd
A
458static CFArrayRef
459SecCmsSignerInfoCopySigningCertificates(SecCmsSignerInfoRef signerinfo)
460{
461 CFMutableArrayRef certs = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
462 SecAsn1Item **cert_datas = signerinfo->signedData->rawCerts;
463 SecAsn1Item *cert_data;
464 if (cert_datas) while ((cert_data = *cert_datas) != NULL) {
465 SecCertificateRef cert = SecCertificateCreateWithBytes(NULL, cert_data->Data, cert_data->Length);
466 if (cert) {
467 switch (signerinfo->signerIdentifier.identifierType) {
468 case SecCmsSignerIDIssuerSN:
469 if (CERT_CheckIssuerAndSerial(cert,
470 &(signerinfo->signerIdentifier.id.issuerAndSN->derIssuer),
471 &(signerinfo->signerIdentifier.id.issuerAndSN->serialNumber)))
472 CFArrayInsertValueAtIndex(certs, 0, cert);
473 else
474 CFArrayAppendValue(certs, cert);
475 break;
476 case SecCmsSignerIDSubjectKeyID:
477 {
478 CFDataRef cert_keyid = SecCertificateGetSubjectKeyID(cert);
479 SecAsn1Item *tbf_keyid = signerinfo->signerIdentifier.id.subjectKeyID;
480 if (tbf_keyid->Length == (size_t)CFDataGetLength(cert_keyid) &&
481 !memcmp(tbf_keyid->Data, CFDataGetBytePtr(cert_keyid), tbf_keyid->Length))
482 CFArrayInsertValueAtIndex(certs, 0, cert);
483 else
484 CFArrayAppendValue(certs, cert);
485 break;
486 }
487 }
488 CFReleaseNull(cert);
489 }
490 cert_datas++;
491 }
492
493 if ((CFArrayGetCount(certs) == 0) &&
494 (signerinfo->signerIdentifier.identifierType == SecCmsSignerIDIssuerSN))
495 {
496 SecCertificateRef cert = CERT_FindCertificateByIssuerAndSN(signerinfo->signedData->certs, signerinfo->signerIdentifier.id.issuerAndSN);
497 if (cert) {
498 CFArrayAppendValue(certs, cert);
499 CFRelease(cert);
500 }
501 }
502 return certs;
503}
d8f41ccd 504
b1ab9ed8
A
505OSStatus
506SecCmsSignerInfoVerifyCertificate(SecCmsSignerInfoRef signerinfo, SecKeychainRef keychainOrArray,
507 CFTypeRef policies, SecTrustRef *trustRef)
508{
b1ab9ed8
A
509 CFAbsoluteTime stime;
510 OSStatus rv;
d8f41ccd 511
d8f41ccd
A
512 CFArrayRef certs;
513
514 if ((certs = SecCmsSignerInfoCopySigningCertificates(signerinfo)) == NULL) {
b1ab9ed8
A
515 signerinfo->verificationStatus = SecCmsVSSigningCertNotFound;
516 return SECFailure;
517 }
b1ab9ed8
A
518 /*
519 * Get and convert the signing time; if available, it will be used
520 * both on the cert verification and for importing the sender
521 * email profile.
522 */
d8f41ccd
A
523 if (SecCmsSignerInfoGetSigningTime(signerinfo, &stime) != SECSuccess)
524 stime = CFAbsoluteTimeGetCurrent();
525
d8f41ccd
A
526 rv = CERT_VerifyCert(keychainOrArray, certs, policies, stime, trustRef);
527 CFRelease(certs);
b1ab9ed8
A
528 if (rv || !trustRef)
529 {
530 if (PORT_GetError() == SEC_ERROR_UNTRUSTED_CERT)
531 {
5c19dc3a 532 /* Signature or digest level verificationStatus errors should supercede certificate level errors, so only change the verificationStatus if the status was GoodSignature. */
5c19dc3a
A
533 if (signerinfo->verificationStatus == SecCmsVSGoodSignature)
534 signerinfo->verificationStatus = SecCmsVSSigningCertNotTrusted;
b1ab9ed8
A
535 }
536 }
b1ab9ed8 537
d8f41ccd 538 return rv;
b1ab9ed8
A
539}
540
541/*
542 * SecCmsSignerInfoVerify - verify the signature of a single SignerInfo
543 *
544 * Just verifies the signature. The assumption is that verification of the certificate
545 * is done already.
546 */
547OSStatus
d8f41ccd 548SecCmsSignerInfoVerify(SecCmsSignerInfoRef signerinfo, SecAsn1Item * digest, SecAsn1Item * contentType)
b1ab9ed8
A
549{
550 SecPublicKeyRef publickey = NULL;
551 SecCmsAttribute *attr;
d8f41ccd 552 SecAsn1Item encoded_attrs;
b1ab9ed8
A
553 SecCertificateRef cert;
554 SecCmsVerificationStatus vs = SecCmsVSUnverified;
555 PLArenaPool *poolp;
d8f41ccd 556
b1ab9ed8
A
557 if (signerinfo == NULL)
558 return SECFailure;
d8f41ccd 559
b1ab9ed8
A
560 /* SecCmsSignerInfoGetSigningCertificate will fail if 2nd parm is NULL and */
561 /* cert has not been verified */
562 if ((cert = SecCmsSignerInfoGetSigningCertificate(signerinfo, NULL)) == NULL) {
b1ab9ed8
A
563 vs = SecCmsVSSigningCertNotFound;
564 goto loser;
565 }
566
d8f41ccd
A
567 publickey = SecCertificateCopyPublicKey(cert);
568 if (publickey == NULL)
569 goto loser;
b1ab9ed8 570
b1ab9ed8
A
571 if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr)) {
572 if (contentType) {
573 /*
574 * Check content type
575 *
576 * RFC2630 sez that if there are any authenticated attributes,
577 * then there must be one for content type which matches the
578 * content type of the content being signed, and there must
579 * be one for message digest which matches our message digest.
580 * So check these things first.
581 */
582 if ((attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr,
583 SEC_OID_PKCS9_CONTENT_TYPE, PR_TRUE)) == NULL)
584 {
585 vs = SecCmsVSMalformedSignature;
586 goto loser;
587 }
588
589 if (SecCmsAttributeCompareValue(attr, contentType) == PR_FALSE) {
590 vs = SecCmsVSMalformedSignature;
591 goto loser;
592 }
593 }
594
595 /*
596 * Check digest
597 */
598 if ((attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr, SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE)) == NULL)
599 {
600 vs = SecCmsVSMalformedSignature;
601 goto loser;
602 }
603 if (SecCmsAttributeCompareValue(attr, digest) == PR_FALSE) {
604 vs = SecCmsVSDigestMismatch;
605 goto loser;
606 }
607
608 if ((poolp = PORT_NewArena (1024)) == NULL) {
609 vs = SecCmsVSProcessingError;
610 goto loser;
611 }
612
613 /*
614 * Check signature
615 *
616 * The signature is based on a digest of the DER-encoded authenticated
617 * attributes. So, first we encode and then we digest/verify.
618 * we trust the decoder to have the attributes in the right (sorted) order
619 */
620 encoded_attrs.Data = NULL;
621 encoded_attrs.Length = 0;
622
623 if (SecCmsAttributeArrayEncode(poolp, &(signerinfo->authAttr), &encoded_attrs) == NULL ||
624 encoded_attrs.Data == NULL || encoded_attrs.Length == 0)
625 {
626 vs = SecCmsVSProcessingError;
627 goto loser;
628 }
d8f41ccd
A
629 if (errSecSuccess == SecKeyDigestAndVerify(publickey, &signerinfo->digestAlg, encoded_attrs.Data, encoded_attrs.Length, signerinfo->encDigest.Data, signerinfo->encDigest.Length))
630 vs = SecCmsVSGoodSignature;
631 else
632 vs = SecCmsVSBadSignature;
b1ab9ed8
A
633
634 PORT_FreeArena(poolp, PR_FALSE); /* awkward memory management :-( */
635
636 } else {
d8f41ccd 637 SecAsn1Item * sig;
b1ab9ed8
A
638
639 /* No authenticated attributes. The signature is based on the plain message digest. */
640 sig = &(signerinfo->encDigest);
641 if (sig->Length == 0)
642 goto loser;
643
d8f41ccd
A
644 if (SecKeyVerifyDigest(publickey, &signerinfo->digestAlg, digest->Data, digest->Length, sig->Data, sig->Length))
645 vs = SecCmsVSBadSignature;
646 else
647 vs = SecCmsVSGoodSignature;
b1ab9ed8
A
648 }
649
650 if (vs == SecCmsVSBadSignature) {
651 /*
652 * XXX Change the generic error into our specific one, because
653 * in that case we get a better explanation out of the Security
654 * Advisor. This is really a bug in our error strings (the
655 * "generic" error has a lousy/wrong message associated with it
656 * which assumes the signature verification was done for the
657 * purposes of checking the issuer signature on a certificate)
658 * but this is at least an easy workaround and/or in the
659 * Security Advisor, which specifically checks for the error
660 * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation
661 * in that case but does not similarly check for
662 * SEC_ERROR_BAD_SIGNATURE. It probably should, but then would
663 * probably say the wrong thing in the case that it *was* the
664 * certificate signature check that failed during the cert
665 * verification done above. Our error handling is really a mess.
666 */
667 if (PORT_GetError() == SEC_ERROR_BAD_SIGNATURE)
668 PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
669 }
670
671 if (publickey != NULL)
672 CFRelease(publickey);
673
674 signerinfo->verificationStatus = vs;
b1ab9ed8
A
675
676 return (vs == SecCmsVSGoodSignature) ? SECSuccess : SECFailure;
677
678loser:
679 if (publickey != NULL)
680 SECKEY_DestroyPublicKey (publickey);
681
b1ab9ed8
A
682 signerinfo->verificationStatus = vs;
683
684 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
685 return SECFailure;
686}
687
b1ab9ed8
A
688SecCmsVerificationStatus
689SecCmsSignerInfoGetVerificationStatus(SecCmsSignerInfoRef signerinfo)
690{
691 return signerinfo->verificationStatus;
692}
693
694SECOidData *
695SecCmsSignerInfoGetDigestAlg(SecCmsSignerInfoRef signerinfo)
696{
697 return SECOID_FindOID (&(signerinfo->digestAlg.algorithm));
698}
699
700SECOidTag
701SecCmsSignerInfoGetDigestAlgTag(SecCmsSignerInfoRef signerinfo)
702{
703 SECOidData *algdata;
704
705 algdata = SECOID_FindOID (&(signerinfo->digestAlg.algorithm));
706 if (algdata != NULL)
707 return algdata->offset;
708 else
709 return SEC_OID_UNKNOWN;
710}
711
712CFArrayRef
713SecCmsSignerInfoGetCertList(SecCmsSignerInfoRef signerinfo)
714{
b1ab9ed8
A
715 return signerinfo->certList;
716}
717
b1ab9ed8
A
718int
719SecCmsSignerInfoGetVersion(SecCmsSignerInfoRef signerinfo)
720{
721 unsigned long version;
722
d8f41ccd 723 /* always take apart the SecAsn1Item */
b1ab9ed8
A
724 if (SEC_ASN1DecodeInteger(&(signerinfo->version), &version) != SECSuccess)
725 return 0;
726 else
727 return (int)version;
728}
729
730/*
731 * SecCmsSignerInfoGetSigningTime - return the signing time,
732 * in UTCTime format, of a CMS signerInfo.
733 *
734 * sinfo - signerInfo data for this signer
735 *
736 * Returns a pointer to XXXX (what?)
737 * A return value of NULL is an error.
738 */
739OSStatus
740SecCmsSignerInfoGetSigningTime(SecCmsSignerInfoRef sinfo, CFAbsoluteTime *stime)
741{
742 SecCmsAttribute *attr;
d8f41ccd 743 SecAsn1Item * value;
b1ab9ed8
A
744
745 if (sinfo == NULL)
d8f41ccd 746 return SECFailure;
b1ab9ed8
A
747
748 if (sinfo->signingTime != 0) {
749 *stime = sinfo->signingTime; /* cached copy */
750 return SECSuccess;
751 }
752
753 attr = SecCmsAttributeArrayFindAttrByOidTag(sinfo->authAttr, SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE);
754 /* XXXX multi-valued attributes NIH */
755 if (attr == NULL || (value = SecCmsAttributeGetValue(attr)) == NULL)
d8f41ccd 756 return SECFailure;
b1ab9ed8 757 if (DER_UTCTimeToCFDate(value, stime) != SECSuccess)
d8f41ccd 758 return SECFailure;
b1ab9ed8
A
759 sinfo->signingTime = *stime; /* make cached copy */
760 return SECSuccess;
761}
762
e3d460c9
A
763/*!
764 @function
765 @abstract Return the data in the signed Codesigning Hash Agility attribute.
766 @param sinfo SignerInfo data for this signer, pointer to a CFDataRef for attribute value
767 @discussion Returns a CFDataRef containing the value of the attribute
768 @result A return value of errSecInternal is an error trying to look up the oid.
769 A status value of success with null result data indicates the attribute was not present.
770 */
771OSStatus
772SecCmsSignerInfoGetAppleCodesigningHashAgility(SecCmsSignerInfoRef sinfo, CFDataRef *sdata)
773{
774 SecCmsAttribute *attr;
775 SecAsn1Item *value;
776
777 if (sinfo == NULL || sdata == NULL)
778 return errSecParam;
779
780 *sdata = NULL;
781
782 if (sinfo->hashAgilityAttrValue != NULL) {
783 *sdata = sinfo->hashAgilityAttrValue; /* cached copy */
784 return SECSuccess;
785 }
786
787 attr = SecCmsAttributeArrayFindAttrByOidTag(sinfo->authAttr, SEC_OID_APPLE_HASH_AGILITY, PR_TRUE);
788
789 /* attribute not found */
790 if (attr == NULL || (value = SecCmsAttributeGetValue(attr)) == NULL)
791 return SECSuccess;
792
793 sinfo->hashAgilityAttrValue = CFDataCreate(NULL, value->Data, value->Length); /* make cached copy */
794 if (sinfo->hashAgilityAttrValue) {
795 *sdata = sinfo->hashAgilityAttrValue;
796 return SECSuccess;
797 }
798 return errSecAllocate;
799}
800
b1ab9ed8
A
801/*
802 * Return the signing cert of a CMS signerInfo.
803 *
804 * the certs in the enclosing SignedData must have been imported already
805 */
806SecCertificateRef
807SecCmsSignerInfoGetSigningCertificate(SecCmsSignerInfoRef signerinfo, SecKeychainRef keychainOrArray)
808{
d8f41ccd
A
809 SecCertificateRef cert = NULL;
810
811 if (signerinfo->cert != NULL)
b1ab9ed8 812 return signerinfo->cert;
d8f41ccd
A
813
814 /* @@@ Make sure we search though all the certs in the cms message itself as well, it's silly
815 to require them to be added to a keychain first. */
816
d8f41ccd
A
817 SecAsn1Item **cert_datas = signerinfo->signedData->rawCerts;
818 SecAsn1Item *cert_data;
819 if (cert_datas) while ((cert_data = *cert_datas) != NULL) {
820 cert = SecCertificateCreateWithBytes(NULL, cert_data->Data, cert_data->Length);
821 if (cert) {
822 switch (signerinfo->signerIdentifier.identifierType) {
823 case SecCmsSignerIDIssuerSN:
824 if (CERT_CheckIssuerAndSerial(cert,
825 &(signerinfo->signerIdentifier.id.issuerAndSN->derIssuer),
826 &(signerinfo->signerIdentifier.id.issuerAndSN->serialNumber)))
827 signerinfo->cert = cert;
828 break;
829 case SecCmsSignerIDSubjectKeyID: {
830 CFDataRef cert_keyid = SecCertificateGetSubjectKeyID(cert);
831 SecAsn1Item *tbf_keyid = signerinfo->signerIdentifier.id.subjectKeyID;
832 if (tbf_keyid->Length == (size_t)CFDataGetLength(cert_keyid) &&
833 !memcmp(tbf_keyid->Data, CFDataGetBytePtr(cert_keyid), tbf_keyid->Length))
834 signerinfo->cert = cert;
835 }
836 }
837 if (signerinfo->cert)
838 break;
839 CFReleaseNull(cert);
840 }
841 cert_datas++;
842 }
843
844 if (!signerinfo->cert && (signerinfo->signerIdentifier.identifierType == SecCmsSignerIDIssuerSN)) {
845 cert = CERT_FindCertificateByIssuerAndSN(signerinfo->signedData->certs, signerinfo->signerIdentifier.id.issuerAndSN);
846 signerinfo->cert = cert;
847 }
b1ab9ed8
A
848
849 return cert;
850}
851
d8f41ccd 852
b1ab9ed8
A
853/*
854 * SecCmsSignerInfoGetSignerCommonName - return the common name of the signer
855 *
856 * sinfo - signerInfo data for this signer
857 *
858 * Returns a CFStringRef containing the common name of the signer.
859 * A return value of NULL is an error.
860 */
861CFStringRef
862SecCmsSignerInfoGetSignerCommonName(SecCmsSignerInfoRef sinfo)
863{
864 SecCertificateRef signercert;
865 CFStringRef commonName = NULL;
866
867 /* will fail if cert is not verified */
868 if ((signercert = SecCmsSignerInfoGetSigningCertificate(sinfo, NULL)) == NULL)
869 return NULL;
870
d8f41ccd
A
871 CFArrayRef commonNames = SecCertificateCopyCommonNames(signercert);
872 if (commonNames) {
873 /* SecCertificateCopyCommonNames doesn't return empty arrays */
874 commonName = (CFStringRef)CFArrayGetValueAtIndex(commonNames, CFArrayGetCount(commonNames) - 1);
875 CFRetain(commonName);
876 CFRelease(commonNames);
877 }
b1ab9ed8
A
878
879 return commonName;
880}
881
882/*
883 * SecCmsSignerInfoGetSignerEmailAddress - return the email address of the signer
884 *
885 * sinfo - signerInfo data for this signer
886 *
887 * Returns a CFStringRef containing the name of the signer.
888 * A return value of NULL is an error.
889 */
890CFStringRef
891SecCmsSignerInfoGetSignerEmailAddress(SecCmsSignerInfoRef sinfo)
892{
893 SecCertificateRef signercert;
894 CFStringRef emailAddress = NULL;
895
896 if ((signercert = SecCmsSignerInfoGetSigningCertificate(sinfo, NULL)) == NULL)
897 return NULL;
898
d8f41ccd
A
899 CFArrayRef names = SecCertificateCopyRFC822Names(signercert);
900 if (names) {
901 if (CFArrayGetCount(names) > 0)
902 emailAddress = (CFStringRef)CFArrayGetValueAtIndex(names, 0);
903 if (emailAddress)
904 CFRetain(emailAddress);
905 CFRelease(names);
906 }
b1ab9ed8
A
907 return emailAddress;
908}
909
910
911/*
912 * SecCmsSignerInfoAddAuthAttr - add an attribute to the
913 * authenticated (i.e. signed) attributes of "signerinfo".
914 */
915OSStatus
916SecCmsSignerInfoAddAuthAttr(SecCmsSignerInfoRef signerinfo, SecCmsAttribute *attr)
917{
d8f41ccd 918 return SecCmsAttributeArrayAddAttr(signerinfo->signedData->contentInfo.cmsg->poolp, &(signerinfo->authAttr), attr);
b1ab9ed8
A
919}
920
921/*
922 * SecCmsSignerInfoAddUnauthAttr - add an attribute to the
923 * unauthenticated attributes of "signerinfo".
924 */
925OSStatus
926SecCmsSignerInfoAddUnauthAttr(SecCmsSignerInfoRef signerinfo, SecCmsAttribute *attr)
927{
d8f41ccd 928 return SecCmsAttributeArrayAddAttr(signerinfo->signedData->contentInfo.cmsg->poolp, &(signerinfo->unAuthAttr), attr);
b1ab9ed8
A
929}
930
931/*
932 * SecCmsSignerInfoAddSigningTime - add the signing time to the
933 * authenticated (i.e. signed) attributes of "signerinfo".
934 *
935 * This is expected to be included in outgoing signed
936 * messages for email (S/MIME) but is likely useful in other situations.
937 *
938 * This should only be added once; a second call will do nothing.
939 *
940 * XXX This will probably just shove the current time into "signerinfo"
941 * but it will not actually get signed until the entire item is
942 * processed for encoding. Is this (expected to be small) delay okay?
943 */
944OSStatus
945SecCmsSignerInfoAddSigningTime(SecCmsSignerInfoRef signerinfo, CFAbsoluteTime t)
946{
947 SecCmsAttribute *attr;
d8f41ccd 948 SecAsn1Item stime;
b1ab9ed8
A
949 void *mark;
950 PLArenaPool *poolp;
951
d8f41ccd 952 poolp = signerinfo->signedData->contentInfo.cmsg->poolp;
b1ab9ed8
A
953
954 mark = PORT_ArenaMark(poolp);
955
956 /* create new signing time attribute */
957 if (DER_CFDateToUTCTime(t, &stime) != SECSuccess)
958 goto loser;
959
960 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_PKCS9_SIGNING_TIME, &stime, PR_FALSE)) == NULL) {
961 SECITEM_FreeItem (&stime, PR_FALSE);
962 goto loser;
963 }
964
965 SECITEM_FreeItem (&stime, PR_FALSE);
966
967 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
968 goto loser;
969
970 PORT_ArenaUnmark (poolp, mark);
971
972 return SECSuccess;
973
974loser:
975 PORT_ArenaRelease (poolp, mark);
976 return SECFailure;
977}
978
979/*
980 * SecCmsSignerInfoAddSMIMECaps - add a SMIMECapabilities attribute to the
981 * authenticated (i.e. signed) attributes of "signerinfo".
982 *
983 * This is expected to be included in outgoing signed
984 * messages for email (S/MIME).
985 */
986OSStatus
987SecCmsSignerInfoAddSMIMECaps(SecCmsSignerInfoRef signerinfo)
988{
989 SecCmsAttribute *attr;
d8f41ccd 990 SecAsn1Item * smimecaps = NULL;
b1ab9ed8
A
991 void *mark;
992 PLArenaPool *poolp;
993
d8f41ccd 994 poolp = signerinfo->signedData->contentInfo.cmsg->poolp;
b1ab9ed8
A
995
996 mark = PORT_ArenaMark(poolp);
997
998 smimecaps = SECITEM_AllocItem(poolp, NULL, 0);
999 if (smimecaps == NULL)
1000 goto loser;
1001
1002 /* create new signing time attribute */
1003#if 1
1004 // @@@ We don't do Fortezza yet.
d8f41ccd 1005 if (SecSMIMECreateSMIMECapabilities(poolp, smimecaps, PR_FALSE) != SECSuccess)
b1ab9ed8
A
1006#else
1007 if (SecSMIMECreateSMIMECapabilities(poolp, smimecaps,
1008 PK11_FortezzaHasKEA(signerinfo->cert)) != SECSuccess)
1009#endif
1010 goto loser;
1011
1012 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_PKCS9_SMIME_CAPABILITIES, smimecaps, PR_TRUE)) == NULL)
1013 goto loser;
1014
1015 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
1016 goto loser;
1017
1018 PORT_ArenaUnmark (poolp, mark);
1019 return SECSuccess;
1020
1021loser:
1022 PORT_ArenaRelease (poolp, mark);
1023 return SECFailure;
1024}
1025
1026/*
1027 * SecCmsSignerInfoAddSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
1028 * authenticated (i.e. signed) attributes of "signerinfo".
1029 *
1030 * This is expected to be included in outgoing signed messages for email (S/MIME).
1031 */
1032OSStatus
1033SecCmsSignerInfoAddSMIMEEncKeyPrefs(SecCmsSignerInfoRef signerinfo, SecCertificateRef cert, SecKeychainRef keychainOrArray)
1034{
1035 SecCmsAttribute *attr;
d8f41ccd 1036 SecAsn1Item * smimeekp = NULL;
b1ab9ed8
A
1037 void *mark;
1038 PLArenaPool *poolp;
1039
1040#if 0
1041 CFTypeRef policy;
1042
1043 /* verify this cert for encryption */
1044 policy = CERT_PolicyForCertUsage(certUsageEmailRecipient);
1045 if (CERT_VerifyCert(keychainOrArray, cert, policy, CFAbsoluteTimeGetCurrent(), NULL) != SECSuccess) {
1046 CFRelease(policy);
1047 return SECFailure;
1048 }
1049 CFRelease(policy);
1050#endif
1051
d8f41ccd 1052 poolp = signerinfo->signedData->contentInfo.cmsg->poolp;
b1ab9ed8
A
1053 mark = PORT_ArenaMark(poolp);
1054
1055 smimeekp = SECITEM_AllocItem(poolp, NULL, 0);
1056 if (smimeekp == NULL)
1057 goto loser;
1058
1059 /* create new signing time attribute */
d8f41ccd 1060 if (SecSMIMECreateSMIMEEncKeyPrefs(poolp, smimeekp, cert) != SECSuccess)
b1ab9ed8
A
1061 goto loser;
1062
1063 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL)
1064 goto loser;
1065
1066 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
1067 goto loser;
1068
1069 PORT_ArenaUnmark (poolp, mark);
1070 return SECSuccess;
1071
1072loser:
1073 PORT_ArenaRelease (poolp, mark);
1074 return SECFailure;
1075}
1076
1077/*
1078 * SecCmsSignerInfoAddMSSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
1079 * authenticated (i.e. signed) attributes of "signerinfo", using the OID prefered by Microsoft.
1080 *
1081 * This is expected to be included in outgoing signed messages for email (S/MIME),
1082 * if compatibility with Microsoft mail clients is wanted.
1083 */
1084OSStatus
1085SecCmsSignerInfoAddMSSMIMEEncKeyPrefs(SecCmsSignerInfoRef signerinfo, SecCertificateRef cert, SecKeychainRef keychainOrArray)
1086{
1087 SecCmsAttribute *attr;
d8f41ccd 1088 SecAsn1Item * smimeekp = NULL;
b1ab9ed8
A
1089 void *mark;
1090 PLArenaPool *poolp;
1091
1092#if 0
1093 CFTypeRef policy;
1094
1095 /* verify this cert for encryption */
1096 policy = CERT_PolicyForCertUsage(certUsageEmailRecipient);
1097 if (CERT_VerifyCert(keychainOrArray, cert, policy, CFAbsoluteTimeGetCurrent(), NULL) != SECSuccess) {
1098 CFRelease(policy);
1099 return SECFailure;
1100 }
1101 CFRelease(policy);
1102#endif
1103
d8f41ccd 1104 poolp = signerinfo->signedData->contentInfo.cmsg->poolp;
b1ab9ed8
A
1105 mark = PORT_ArenaMark(poolp);
1106
1107 smimeekp = SECITEM_AllocItem(poolp, NULL, 0);
1108 if (smimeekp == NULL)
1109 goto loser;
1110
1111 /* create new signing time attribute */
d8f41ccd 1112 if (SecSMIMECreateMSSMIMEEncKeyPrefs(poolp, smimeekp, cert) != SECSuccess)
b1ab9ed8
A
1113 goto loser;
1114
1115 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_MS_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL)
1116 goto loser;
1117
1118 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
1119 goto loser;
1120
1121 PORT_ArenaUnmark (poolp, mark);
1122 return SECSuccess;
1123
1124loser:
1125 PORT_ArenaRelease (poolp, mark);
1126 return SECFailure;
1127}
1128
b1ab9ed8
A
1129/*
1130 * SecCmsSignerInfoAddCounterSignature - countersign a signerinfo
1131 *
1132 * 1. digest the DER-encoded signature value of the original signerinfo
1133 * 2. create new signerinfo with correct version, sid, digestAlg
1134 * 3. add message-digest authAttr, but NO content-type
1135 * 4. sign the authAttrs
1136 * 5. DER-encode the new signerInfo
1137 * 6. add the whole thing to original signerInfo's unAuthAttrs
1138 * as a SEC_OID_PKCS9_COUNTER_SIGNATURE attribute
1139 *
1140 * XXXX give back the new signerinfo?
1141 */
1142OSStatus
1143SecCmsSignerInfoAddCounterSignature(SecCmsSignerInfoRef signerinfo,
1144 SECOidTag digestalg, SecIdentityRef identity)
1145{
1146 /* XXXX TBD XXXX */
1147 return SECFailure;
1148}
1149
e3d460c9
A
1150/*!
1151 @function
1152 @abstract Add the Apple Codesigning Hash Agility attribute to the authenticated (i.e. signed) attributes of "signerinfo".
1153 @discussion This is expected to be included in outgoing signed Apple code signatures.
1154 */
1155OSStatus
1156SecCmsSignerInfoAddAppleCodesigningHashAgility(SecCmsSignerInfoRef signerinfo, CFDataRef attrValue)
1157{
1158 SecCmsAttribute *attr;
1159 PLArenaPool *poolp = signerinfo->signedData->contentInfo.cmsg->poolp;
1160 void *mark = PORT_ArenaMark(poolp);
1161 OSStatus status = SECFailure;
1162
1163 /* The value is required for this attribute. */
1164 if (!attrValue) {
1165 status = errSecParam;
1166 goto loser;
1167 }
1168
1169 /*
1170 * SecCmsAttributeCreate makes a copy of the data in value, so
1171 * we don't need to copy into the CSSM_DATA struct.
1172 */
1173 SecAsn1Item value;
1174 value.Length = CFDataGetLength(attrValue);
1175 value.Data = (uint8_t *)CFDataGetBytePtr(attrValue);
1176
1177 if ((attr = SecCmsAttributeCreate(poolp,
1178 SEC_OID_APPLE_HASH_AGILITY,
1179 &value,
1180 PR_FALSE)) == NULL) {
1181 status = errSecAllocate;
1182 goto loser;
1183 }
1184
1185 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess) {
1186 status = errSecInternal;
1187 goto loser;
1188 }
1189
1190 PORT_ArenaUnmark(poolp, mark);
1191 return SECSuccess;
1192
1193loser:
1194 PORT_ArenaRelease(poolp, mark);
1195 return status;
1196}
1197
b1ab9ed8
A
1198/*
1199 * XXXX the following needs to be done in the S/MIME layer code
1200 * after signature of a signerinfo is verified
1201 */
1202OSStatus
1203SecCmsSignerInfoSaveSMIMEProfile(SecCmsSignerInfoRef signerinfo)
1204{
d8f41ccd 1205 return -4 /*unImp*/;
b1ab9ed8
A
1206}
1207
1208/*
1209 * SecCmsSignerInfoIncludeCerts - set cert chain inclusion mode for this signer
1210 */
1211OSStatus
1212SecCmsSignerInfoIncludeCerts(SecCmsSignerInfoRef signerinfo, SecCmsCertChainMode cm, SECCertUsage usage)
1213{
1214 if (signerinfo->cert == NULL)
1215 return SECFailure;
1216
1217 /* don't leak if we get called twice */
1218 if (signerinfo->certList != NULL) {
1219 CFRelease(signerinfo->certList);
1220 signerinfo->certList = NULL;
1221 }
1222
1223 switch (cm) {
1224 case SecCmsCMNone:
1225 signerinfo->certList = NULL;
1226 break;
1227 case SecCmsCMCertOnly:
1228 signerinfo->certList = CERT_CertListFromCert(signerinfo->cert);
1229 break;
1230 case SecCmsCMCertChain:
1231 signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_FALSE);
1232 break;
1233 case SecCmsCMCertChainWithRoot:
1234 signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_TRUE);
1235 break;
1236 }
1237
1238 if (cm != SecCmsCMNone && signerinfo->certList == NULL)
1239 return SECFailure;
1240
1241 return SECSuccess;
1242}