]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_smime/lib/cmssiginfo.c
Security-58286.1.32.tar.gz
[apple/security.git] / OSX / libsecurity_smime / lib / cmssiginfo.c
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"
44 #include "secitem.h"
45 #include "secoid.h"
46 #include "cryptohi.h"
47
48 #include <security_asn1/secasn1.h>
49 #include <security_asn1/secerr.h>
50 #include <Security/SecKeychain.h>
51 #include <Security/SecIdentity.h>
52 #include <Security/SecCertificatePriv.h>
53 #include <Security/SecKeyPriv.h>
54 #include <CoreFoundation/CFTimeZone.h>
55 #include <utilities/SecCFWrappers.h>
56 #include <AssertMacros.h>
57 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
58 #include <Security/SecPolicyPriv.h>
59 #include <Security/SecItem.h>
60
61 #include "tsaSupport.h"
62 #include "tsaSupportPriv.h"
63
64 #include <syslog.h>
65
66 #define HIDIGIT(v) (((v) / 10) + '0')
67 #define LODIGIT(v) (((v) % 10) + '0')
68
69 #define ISDIGIT(dig) (((dig) >= '0') && ((dig) <= '9'))
70 #define CAPTURE(var,p,label) \
71 { \
72 if (!ISDIGIT((p)[0]) || !ISDIGIT((p)[1])) goto label; \
73 (var) = ((p)[0] - '0') * 10 + ((p)[1] - '0'); \
74 }
75
76 #ifndef NDEBUG
77 #define SIGINFO_DEBUG 1
78 #endif
79
80 #if SIGINFO_DEBUG
81 #define dprintf(args...) fprintf(stderr, args)
82 #else
83 #define dprintf(args...)
84 #endif
85
86 #if RELEASECOUNTDEBUG
87 #define dprintfRC(args...) dprintf(args)
88 #else
89 #define dprintfRC(args...)
90 #endif
91
92 static OSStatus
93 DER_UTCTimeToCFDate(const CSSM_DATA_PTR utcTime, CFAbsoluteTime *date)
94 {
95 CFGregorianDate gdate;
96 char *string = (char *)utcTime->Data;
97 long year, month, mday, hour, minute, second, hourOff, minOff;
98 CFTimeZoneRef timeZone;
99
100 /* Verify time is formatted properly and capture information */
101 second = 0;
102 hourOff = 0;
103 minOff = 0;
104 CAPTURE(year,string+0,loser);
105 if (year < 50) {
106 /* ASSUME that year # is in the 2000's, not the 1900's */
107 year += 100;
108 }
109 CAPTURE(month,string+2,loser);
110 if ((month == 0) || (month > 12)) goto loser;
111 CAPTURE(mday,string+4,loser);
112 if ((mday == 0) || (mday > 31)) goto loser;
113 CAPTURE(hour,string+6,loser);
114 if (hour > 23) goto loser;
115 CAPTURE(minute,string+8,loser);
116 if (minute > 59) goto loser;
117 if (ISDIGIT(string[10])) {
118 CAPTURE(second,string+10,loser);
119 if (second > 59) goto loser;
120 string += 2;
121 }
122 if (string[10] == '+') {
123 CAPTURE(hourOff,string+11,loser);
124 if (hourOff > 23) goto loser;
125 CAPTURE(minOff,string+13,loser);
126 if (minOff > 59) goto loser;
127 } else if (string[10] == '-') {
128 CAPTURE(hourOff,string+11,loser);
129 if (hourOff > 23) goto loser;
130 hourOff = -hourOff;
131 CAPTURE(minOff,string+13,loser);
132 if (minOff > 59) goto loser;
133 minOff = -minOff;
134 } else if (string[10] != 'Z') {
135 goto loser;
136 }
137
138 gdate.year = (SInt32)(year + 1900);
139 gdate.month = month;
140 gdate.day = mday;
141 gdate.hour = hour;
142 gdate.minute = minute;
143 gdate.second = second;
144
145 if (hourOff == 0 && minOff == 0)
146 timeZone = NULL; /* GMT */
147 else
148 {
149 timeZone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, (hourOff * 60 + minOff) * 60);
150 }
151
152 *date = CFGregorianDateGetAbsoluteTime(gdate, timeZone);
153 if (timeZone)
154 CFRelease(timeZone);
155
156 return SECSuccess;
157
158 loser:
159 return SECFailure;
160 }
161
162 static OSStatus
163 DER_CFDateToUTCTime(CFAbsoluteTime date, CSSM_DATA_PTR utcTime)
164 {
165 CFGregorianDate gdate = CFAbsoluteTimeGetGregorianDate(date, NULL /* GMT */);
166 unsigned char *d;
167 SInt8 second;
168
169 utcTime->Length = 13;
170 utcTime->Data = d = PORT_Alloc(13);
171 if (!utcTime->Data)
172 return SECFailure;
173
174 /* UTC time does not handle the years before 1950 */
175 if (gdate.year < 1950)
176 return SECFailure;
177
178 /* remove the century since it's added to the year by the
179 CFAbsoluteTimeGetGregorianDate routine, but is not needed for UTC time */
180 gdate.year %= 100;
181 second = gdate.second + 0.5;
182
183 d[0] = HIDIGIT(gdate.year);
184 d[1] = LODIGIT(gdate.year);
185 d[2] = HIDIGIT(gdate.month);
186 d[3] = LODIGIT(gdate.month);
187 d[4] = HIDIGIT(gdate.day);
188 d[5] = LODIGIT(gdate.day);
189 d[6] = HIDIGIT(gdate.hour);
190 d[7] = LODIGIT(gdate.hour);
191 d[8] = HIDIGIT(gdate.minute);
192 d[9] = LODIGIT(gdate.minute);
193 d[10] = HIDIGIT(second);
194 d[11] = LODIGIT(second);
195 d[12] = 'Z';
196 return SECSuccess;
197 }
198
199 /* =============================================================================
200 * SIGNERINFO
201 */
202 SecCmsSignerInfoRef
203 nss_cmssignerinfo_create(SecCmsMessageRef cmsg, SecCmsSignerIDSelector type, SecCertificateRef cert, CSSM_DATA_PTR subjKeyID, SecPublicKeyRef pubKey, SecPrivateKeyRef signingKey, SECOidTag digestalgtag);
204
205 SecCmsSignerInfoRef
206 SecCmsSignerInfoCreateWithSubjKeyID(SecCmsMessageRef cmsg, CSSM_DATA_PTR subjKeyID, SecPublicKeyRef pubKey, SecPrivateKeyRef signingKey, SECOidTag digestalgtag)
207 {
208 return nss_cmssignerinfo_create(cmsg, SecCmsSignerIDSubjectKeyID, NULL, subjKeyID, pubKey, signingKey, digestalgtag);
209 }
210
211 SecCmsSignerInfoRef
212 SecCmsSignerInfoCreate(SecCmsMessageRef cmsg, SecIdentityRef identity, SECOidTag digestalgtag)
213 {
214 SecCmsSignerInfoRef signerInfo = NULL;
215 SecCertificateRef cert = NULL;
216 SecPrivateKeyRef signingKey = NULL;
217 CFDictionaryRef keyAttrs = NULL;
218
219 if (SecIdentityCopyCertificate(identity, &cert))
220 goto loser;
221 if (SecIdentityCopyPrivateKey(identity, &signingKey))
222 goto loser;
223
224 /* In some situations, the "Private Key" in the identity is actually a public key. */
225 keyAttrs = SecKeyCopyAttributes(signingKey);
226 if (!keyAttrs)
227 goto loser;
228 CFTypeRef class = CFDictionaryGetValue(keyAttrs, kSecAttrKeyClass);
229 if (!class || (CFGetTypeID(class) != CFStringGetTypeID()) || !CFEqual(class, kSecAttrKeyClassPrivate))
230 goto loser;
231
232
233 signerInfo = nss_cmssignerinfo_create(cmsg, SecCmsSignerIDIssuerSN, cert, NULL, NULL, signingKey, digestalgtag);
234
235 loser:
236 if (cert)
237 CFRelease(cert);
238 if (signingKey)
239 CFRelease(signingKey);
240 if (keyAttrs)
241 CFRelease(keyAttrs);
242
243 return signerInfo;
244 }
245
246 SecCmsSignerInfoRef
247 nss_cmssignerinfo_create(SecCmsMessageRef cmsg, SecCmsSignerIDSelector type, SecCertificateRef cert, CSSM_DATA_PTR subjKeyID, SecPublicKeyRef pubKey, SecPrivateKeyRef signingKey, SECOidTag digestalgtag)
248 {
249 void *mark;
250 SecCmsSignerInfoRef signerinfo;
251 int version;
252 PLArenaPool *poolp;
253
254 poolp = cmsg->poolp;
255
256 mark = PORT_ArenaMark(poolp);
257
258 signerinfo = (SecCmsSignerInfoRef)PORT_ArenaZAlloc(poolp, sizeof(SecCmsSignerInfo));
259 if (signerinfo == NULL) {
260 PORT_ArenaRelease(poolp, mark);
261 return NULL;
262 }
263
264
265 signerinfo->cmsg = cmsg;
266
267 switch(type) {
268 case SecCmsSignerIDIssuerSN:
269 signerinfo->signerIdentifier.identifierType = SecCmsSignerIDIssuerSN;
270 if ((signerinfo->cert = CERT_DupCertificate(cert)) == NULL)
271 goto loser;
272 if ((signerinfo->signerIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert)) == NULL)
273 goto loser;
274 dprintfRC("nss_cmssignerinfo_create: SecCmsSignerIDIssuerSN: cert.rc %d\n",
275 (int)CFGetRetainCount(signerinfo->cert));
276 break;
277 case SecCmsSignerIDSubjectKeyID:
278 signerinfo->signerIdentifier.identifierType = SecCmsSignerIDSubjectKeyID;
279 PORT_Assert(subjKeyID);
280 if (!subjKeyID)
281 goto loser;
282 signerinfo->signerIdentifier.id.subjectKeyID = PORT_ArenaNew(poolp, CSSM_DATA);
283 if (SECITEM_CopyItem(poolp, signerinfo->signerIdentifier.id.subjectKeyID,
284 subjKeyID)) {
285 goto loser;
286 }
287 signerinfo->pubKey = SECKEY_CopyPublicKey(pubKey);
288 if (!signerinfo->pubKey)
289 goto loser;
290 break;
291 default:
292 goto loser;
293 }
294
295 if (!signingKey)
296 goto loser;
297
298 signerinfo->signingKey = SECKEY_CopyPrivateKey(signingKey);
299 if (!signerinfo->signingKey)
300 goto loser;
301
302 /* set version right now */
303 version = SEC_CMS_SIGNER_INFO_VERSION_ISSUERSN;
304 /* RFC2630 5.3 "version is the syntax version number. If the .... " */
305 if (signerinfo->signerIdentifier.identifierType == SecCmsSignerIDSubjectKeyID)
306 version = SEC_CMS_SIGNER_INFO_VERSION_SUBJKEY;
307 (void)SEC_ASN1EncodeInteger(poolp, &(signerinfo->version), (long)version);
308
309 if (SECOID_SetAlgorithmID(poolp, &signerinfo->digestAlg, digestalgtag, NULL) != SECSuccess)
310 goto loser;
311
312 PORT_ArenaUnmark(poolp, mark);
313 return signerinfo;
314
315 loser:
316 PORT_ArenaRelease(poolp, mark);
317 return NULL;
318 }
319
320 /*
321 * SecCmsSignerInfoDestroy - destroy a SignerInfo data structure
322 */
323 void
324 SecCmsSignerInfoDestroy(SecCmsSignerInfoRef si)
325 {
326 if (si->cert != NULL) {
327 dprintfRC("SecCmsSignerInfoDestroy top: certp %p cert.rc %d\n",
328 si->cert, (int)CFGetRetainCount(si->cert));
329 CERT_DestroyCertificate(si->cert);
330 }
331 if (si->certList != NULL) {
332 dprintfRC("SecCmsSignerInfoDestroy top: certList.rc %d\n",
333 (int)CFGetRetainCount(si->certList));
334 CFRelease(si->certList);
335 }
336 if (si->timestampCertList != NULL) {
337 dprintfRC("SecCmsSignerInfoDestroy top: timestampCertList.rc %d\n",
338 (int)CFGetRetainCount(si->timestampCertList));
339 CFRelease(si->timestampCertList);
340 }
341 if (si->hashAgilityAttrValue != NULL) {
342 dprintfRC("SecCmsSignerInfoDestroy top: hashAgilityAttrValue.rc %d\n",
343 (int)CFGetRetainCount(si->hashAgilityAttrValue));
344 CFRelease(si->hashAgilityAttrValue);
345 }
346 /* XXX storage ??? */
347 }
348
349 /*
350 * SecCmsSignerInfoSign - sign something
351 *
352 */
353 OSStatus
354 SecCmsSignerInfoSign(SecCmsSignerInfoRef signerinfo, CSSM_DATA_PTR digest, CSSM_DATA_PTR contentType)
355 {
356 SecCertificateRef cert;
357 SecPrivateKeyRef privkey = NULL;
358 SECOidTag digestalgtag;
359 SECOidTag pubkAlgTag;
360 CSSM_DATA signature = { 0 };
361 OSStatus rv;
362 PLArenaPool *poolp, *tmppoolp = NULL;
363 const SECAlgorithmID *algID;
364 SECAlgorithmID freeAlgID;
365 //CERTSubjectPublicKeyInfo *spki;
366
367 PORT_Assert (digest != NULL);
368
369 poolp = signerinfo->cmsg->poolp;
370
371 switch (signerinfo->signerIdentifier.identifierType) {
372 case SecCmsSignerIDIssuerSN:
373 privkey = signerinfo->signingKey;
374 signerinfo->signingKey = NULL;
375 cert = signerinfo->cert;
376 if (SecCertificateGetAlgorithmID(cert,&algID)) {
377 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
378 goto loser;
379 }
380 break;
381 case SecCmsSignerIDSubjectKeyID:
382 privkey = signerinfo->signingKey;
383 signerinfo->signingKey = NULL;
384 #if 0
385 spki = SECKEY_CreateSubjectPublicKeyInfo(signerinfo->pubKey);
386 SECKEY_DestroyPublicKey(signerinfo->pubKey);
387 signerinfo->pubKey = NULL;
388 SECOID_CopyAlgorithmID(NULL, &freeAlgID, &spki->algorithm);
389 SECKEY_DestroySubjectPublicKeyInfo(spki);
390 algID = &freeAlgID;
391 #else
392
393 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR))
394 if (SecKeyGetAlgorithmID(signerinfo->pubKey,&algID)) {
395 #else
396 /* TBD: Unify this code. Currently, iOS has an incompatible
397 * SecKeyGetAlgorithmID implementation. */
398 if (true) {
399 #endif
400 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
401 goto loser;
402 }
403 CFRelease(signerinfo->pubKey);
404 signerinfo->pubKey = NULL;
405 #endif
406 break;
407 default:
408 PORT_SetError(SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE);
409 goto loser;
410 }
411 digestalgtag = SecCmsSignerInfoGetDigestAlgTag(signerinfo);
412 /*
413 * XXX I think there should be a cert-level interface for this,
414 * so that I do not have to know about subjectPublicKeyInfo...
415 */
416 pubkAlgTag = SECOID_GetAlgorithmTag(algID);
417 if (signerinfo->signerIdentifier.identifierType == SecCmsSignerIDSubjectKeyID) {
418 SECOID_DestroyAlgorithmID(&freeAlgID, PR_FALSE);
419 }
420
421 #if 0
422 // @@@ Not yet
423 /* Fortezza MISSI have weird signature formats.
424 * Map them to standard DSA formats
425 */
426 pubkAlgTag = PK11_FortezzaMapSig(pubkAlgTag);
427 #endif
428
429 if (signerinfo->authAttr != NULL) {
430 CSSM_DATA encoded_attrs;
431
432 /* find and fill in the message digest attribute. */
433 rv = SecCmsAttributeArraySetAttr(poolp, &(signerinfo->authAttr),
434 SEC_OID_PKCS9_MESSAGE_DIGEST, digest, PR_FALSE);
435 if (rv != SECSuccess)
436 goto loser;
437
438 if (contentType != NULL) {
439 /* if the caller wants us to, find and fill in the content type attribute. */
440 rv = SecCmsAttributeArraySetAttr(poolp, &(signerinfo->authAttr),
441 SEC_OID_PKCS9_CONTENT_TYPE, contentType, PR_FALSE);
442 if (rv != SECSuccess)
443 goto loser;
444 }
445
446 if ((tmppoolp = PORT_NewArena (1024)) == NULL) {
447 PORT_SetError(SEC_ERROR_NO_MEMORY);
448 goto loser;
449 }
450
451 /*
452 * Before encoding, reorder the attributes so that when they
453 * are encoded, they will be conforming DER, which is required
454 * to have a specific order and that is what must be used for
455 * the hash/signature. We do this here, rather than building
456 * it into EncodeAttributes, because we do not want to do
457 * such reordering on incoming messages (which also uses
458 * EncodeAttributes) or our old signatures (and other "broken"
459 * implementations) will not verify. So, we want to guarantee
460 * that we send out good DER encodings of attributes, but not
461 * to expect to receive them.
462 */
463 if (SecCmsAttributeArrayReorder(signerinfo->authAttr) != SECSuccess)
464 goto loser;
465
466 encoded_attrs.Data = NULL;
467 encoded_attrs.Length = 0;
468 if (SecCmsAttributeArrayEncode(tmppoolp, &(signerinfo->authAttr),
469 &encoded_attrs) == NULL)
470 goto loser;
471
472 rv = SEC_SignData(&signature, encoded_attrs.Data, (int)encoded_attrs.Length,
473 privkey, digestalgtag, pubkAlgTag);
474 PORT_FreeArena(tmppoolp, PR_FALSE); /* awkward memory management :-( */
475 tmppoolp = 0;
476 } else {
477 rv = SGN_Digest(privkey, digestalgtag, pubkAlgTag, &signature, digest);
478 }
479 SECKEY_DestroyPrivateKey(privkey);
480 privkey = NULL;
481
482 if (rv != SECSuccess)
483 goto loser;
484
485 if (SECITEM_CopyItem(poolp, &(signerinfo->encDigest), &signature)
486 != SECSuccess)
487 goto loser;
488
489 SECITEM_FreeItem(&signature, PR_FALSE);
490
491 if(pubkAlgTag == SEC_OID_EC_PUBLIC_KEY) {
492 /*
493 * RFC 3278 section section 2.1.1 states that the signatureAlgorithm
494 * field contains the full ecdsa-with-SHA1 OID, not plain old ecPublicKey
495 * as would appear in other forms of signed datas. However Microsoft doesn't
496 * do this, it puts ecPublicKey there, and if we put ecdsa-with-SHA1 there,
497 * MS can't verify - presumably because it takes the digest of the digest
498 * before feeding it to ECDSA.
499 * We handle this with a preference; default if it's not there is
500 * "Microsoft compatibility mode".
501 */
502 if(!SecCmsMsEcdsaCompatMode()) {
503 pubkAlgTag = SEC_OID_ECDSA_WithSHA1;
504 }
505 /* else violating the spec for compatibility */
506 }
507
508 if (SECOID_SetAlgorithmID(poolp, &(signerinfo->digestEncAlg), pubkAlgTag,
509 NULL) != SECSuccess)
510 goto loser;
511
512 return SECSuccess;
513
514 loser:
515 if (signature.Length != 0)
516 SECITEM_FreeItem (&signature, PR_FALSE);
517 if (privkey)
518 SECKEY_DestroyPrivateKey(privkey);
519 if (tmppoolp)
520 PORT_FreeArena(tmppoolp, PR_FALSE);
521 return SECFailure;
522 }
523
524 OSStatus
525 SecCmsSignerInfoVerifyCertificate(SecCmsSignerInfoRef signerinfo, SecKeychainRef keychainOrArray,
526 CFTypeRef policies, SecTrustRef *trustRef)
527 {
528 SecCertificateRef cert;
529 CFAbsoluteTime stime;
530 OSStatus rv;
531 CSSM_DATA_PTR *otherCerts;
532
533 if ((cert = SecCmsSignerInfoGetSigningCertificate(signerinfo, keychainOrArray)) == NULL) {
534 dprintf("SecCmsSignerInfoVerifyCertificate: no signing cert\n");
535 signerinfo->verificationStatus = SecCmsVSSigningCertNotFound;
536 return SECFailure;
537 }
538
539 /*
540 * Get and convert the signing time; if available, it will be used
541 * both on the cert verification and for importing the sender
542 * email profile.
543 */
544 CFTypeRef timeStampPolicies=SecPolicyCreateAppleTimeStampingAndRevocationPolicies(policies);
545 if (SecCmsSignerInfoGetTimestampTimeWithPolicy(signerinfo, timeStampPolicies, &stime) != SECSuccess)
546 if (SecCmsSignerInfoGetSigningTime(signerinfo, &stime) != SECSuccess)
547 stime = CFAbsoluteTimeGetCurrent();
548 CFReleaseSafe(timeStampPolicies);
549
550 rv = SecCmsSignedDataRawCerts(signerinfo->sigd, &otherCerts);
551 if(rv) {
552 return rv;
553 }
554 rv = CERT_VerifyCert(keychainOrArray, cert, otherCerts, policies, stime, trustRef);
555 dprintfRC("SecCmsSignerInfoVerifyCertificate after vfy: certp %p cert.rc %d\n",
556 cert, (int)CFGetRetainCount(cert));
557 if (rv || !trustRef)
558 {
559 if (PORT_GetError() == SEC_ERROR_UNTRUSTED_CERT)
560 {
561 /* Signature or digest level verificationStatus errors should supercede certificate level errors, so only change the verificationStatus if the status was GoodSignature. */
562 if (signerinfo->verificationStatus == SecCmsVSGoodSignature)
563 signerinfo->verificationStatus = SecCmsVSSigningCertNotTrusted;
564 }
565 }
566 /* FIXME isn't this leaking the cert? */
567 dprintf("SecCmsSignerInfoVerifyCertificate: CertVerify rtn %d\n", (int)rv);
568 return rv;
569 }
570
571 static void debugShowSigningCertificate(SecCmsSignerInfoRef signerinfo)
572 {
573 #if SIGINFO_DEBUG
574 CFStringRef cn = SecCmsSignerInfoGetSignerCommonName(signerinfo);
575 if (cn)
576 {
577 char *ccn = cfStringToChar(cn);
578 if (ccn)
579 {
580 dprintf("SecCmsSignerInfoVerify: cn: %s\n", ccn);
581 free(ccn);
582 }
583 CFRelease(cn);
584 }
585 #endif
586 }
587
588 /*
589 * SecCmsSignerInfoVerify - verify the signature of a single SignerInfo
590 *
591 * Just verifies the signature. The assumption is that verification of the certificate
592 * is done already.
593 */
594 OSStatus
595 SecCmsSignerInfoVerify(SecCmsSignerInfoRef signerinfo, CSSM_DATA_PTR digest, CSSM_DATA_PTR contentType)
596 {
597 return SecCmsSignerInfoVerifyWithPolicy(signerinfo,NULL, digest,contentType);
598 }
599
600 OSStatus
601 SecCmsSignerInfoVerifyWithPolicy(SecCmsSignerInfoRef signerinfo,CFTypeRef timeStampPolicy, CSSM_DATA_PTR digest, CSSM_DATA_PTR contentType)
602 {
603 SecPublicKeyRef publickey = NULL;
604 SecCmsAttribute *attr = NULL;
605 CSSM_DATA encoded_attrs;
606 SecCertificateRef cert = NULL;
607 SecCmsVerificationStatus vs = SecCmsVSUnverified;
608 PLArenaPool *poolp = NULL;
609 SECOidTag digestAlgTag, digestEncAlgTag;
610
611 if (signerinfo == NULL)
612 return SECFailure;
613
614 /* SecCmsSignerInfoGetSigningCertificate will fail if 2nd parm is NULL and */
615 /* cert has not been verified */
616 if ((cert = SecCmsSignerInfoGetSigningCertificate(signerinfo, NULL)) == NULL) {
617 dprintf("SecCmsSignerInfoVerify: no signing cert\n");
618 vs = SecCmsVSSigningCertNotFound;
619 goto loser;
620 }
621
622 dprintfRC("SecCmsSignerInfoVerify top: cert %p cert.rc %d\n", cert, (int)CFGetRetainCount(cert));
623
624 debugShowSigningCertificate(signerinfo);
625
626 OSStatus status;
627 if ((status = SecCertificateCopyPublicKey(cert, &publickey))) {
628 syslog(LOG_ERR, "SecCmsSignerInfoVerifyWithPolicy: copy public key failed %d", (int)status);
629 vs = SecCmsVSProcessingError;
630 goto loser;
631 }
632
633 digestAlgTag = SECOID_GetAlgorithmTag(&(signerinfo->digestAlg));
634 digestEncAlgTag = SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg));
635
636 /*
637 * Gross hack necessitated by RFC 3278 section 2.1.1, which states
638 * that the signature algorithm (here, digestEncAlg) contains ecdsa_with-SHA1,
639 * *not* (as in all other algorithms) the raw signature algorithm, e.g.
640 * pkcs1RSAEncryption.
641 */
642 if(digestEncAlgTag == SEC_OID_ECDSA_WithSHA1) {
643 digestEncAlgTag = SEC_OID_EC_PUBLIC_KEY;
644 }
645
646 if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr)) {
647 if (contentType) {
648 /*
649 * Check content type
650 *
651 * RFC2630 sez that if there are any authenticated attributes,
652 * then there must be one for content type which matches the
653 * content type of the content being signed, and there must
654 * be one for message digest which matches our message digest.
655 * So check these things first.
656 */
657 if ((attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr,
658 SEC_OID_PKCS9_CONTENT_TYPE, PR_TRUE)) == NULL)
659 {
660 vs = SecCmsVSMalformedSignature;
661 goto loser;
662 }
663
664 if (SecCmsAttributeCompareValue(attr, contentType) == PR_FALSE) {
665 vs = SecCmsVSMalformedSignature;
666 goto loser;
667 }
668 }
669
670 /*
671 * Check digest
672 */
673 if ((attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr, SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE)) == NULL)
674 {
675 vs = SecCmsVSMalformedSignature;
676 goto loser;
677 }
678 if (SecCmsAttributeCompareValue(attr, digest) == PR_FALSE) {
679 vs = SecCmsVSDigestMismatch;
680 goto loser;
681 }
682
683 if ((poolp = PORT_NewArena (1024)) == NULL) {
684 vs = SecCmsVSProcessingError;
685 goto loser;
686 }
687
688 /*
689 * Check signature
690 *
691 * The signature is based on a digest of the DER-encoded authenticated
692 * attributes. So, first we encode and then we digest/verify.
693 * we trust the decoder to have the attributes in the right (sorted) order
694 */
695 encoded_attrs.Data = NULL;
696 encoded_attrs.Length = 0;
697
698 if (SecCmsAttributeArrayEncode(poolp, &(signerinfo->authAttr), &encoded_attrs) == NULL ||
699 encoded_attrs.Data == NULL || encoded_attrs.Length == 0)
700 {
701 vs = SecCmsVSProcessingError;
702 goto loser;
703 }
704
705 vs = (VFY_VerifyData (encoded_attrs.Data, (int)encoded_attrs.Length,
706 publickey, &(signerinfo->encDigest),
707 digestAlgTag, digestEncAlgTag,
708 signerinfo->cmsg->pwfn_arg) != SECSuccess) ? SecCmsVSBadSignature : SecCmsVSGoodSignature;
709
710 dprintf("VFY_VerifyData (authenticated attributes): %s\n",
711 (vs == SecCmsVSGoodSignature)?"SecCmsVSGoodSignature":"SecCmsVSBadSignature");
712
713 PORT_FreeArena(poolp, PR_FALSE); /* awkward memory management :-( */
714
715 } else {
716 CSSM_DATA_PTR sig;
717
718 /* No authenticated attributes. The signature is based on the plain message digest. */
719 sig = &(signerinfo->encDigest);
720 if (sig->Length == 0)
721 goto loser;
722
723 vs = (VFY_VerifyDigest(digest, publickey, sig,
724 digestAlgTag, digestEncAlgTag,
725 signerinfo->cmsg->pwfn_arg) != SECSuccess) ? SecCmsVSBadSignature : SecCmsVSGoodSignature;
726
727 dprintf("VFY_VerifyData (plain message digest): %s\n",
728 (vs == SecCmsVSGoodSignature)?"SecCmsVSGoodSignature":"SecCmsVSBadSignature");
729 }
730
731 if (!SecCmsArrayIsEmpty((void **)signerinfo->unAuthAttr))
732 {
733 dprintf("found an unAuthAttr\n");
734 OSStatus rux = SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(signerinfo,timeStampPolicy);
735 dprintf("SecCmsSignerInfoVerifyUnAuthAttrs Status: %ld\n", (long)rux);
736 if (rux) {
737 goto loser;
738 }
739 }
740
741 if (vs == SecCmsVSBadSignature) {
742 /*
743 * XXX Change the generic error into our specific one, because
744 * in that case we get a better explanation out of the Security
745 * Advisor. This is really a bug in our error strings (the
746 * "generic" error has a lousy/wrong message associated with it
747 * which assumes the signature verification was done for the
748 * purposes of checking the issuer signature on a certificate)
749 * but this is at least an easy workaround and/or in the
750 * Security Advisor, which specifically checks for the error
751 * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation
752 * in that case but does not similarly check for
753 * SEC_ERROR_BAD_SIGNATURE. It probably should, but then would
754 * probably say the wrong thing in the case that it *was* the
755 * certificate signature check that failed during the cert
756 * verification done above. Our error handling is really a mess.
757 */
758 if (PORT_GetError() == SEC_ERROR_BAD_SIGNATURE)
759 PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
760 }
761
762 if (publickey != NULL)
763 CFRelease(publickey);
764
765 signerinfo->verificationStatus = vs;
766 dprintfRC("SecCmsSignerInfoVerify end: cerp %p cert.rc %d\n",
767 cert, (int)CFGetRetainCount(cert));
768
769 dprintf("verificationStatus: %d\n", vs);
770
771 return (vs == SecCmsVSGoodSignature) ? SECSuccess : SECFailure;
772
773 loser:
774 if (publickey != NULL)
775 SECKEY_DestroyPublicKey (publickey);
776
777 dprintf("verificationStatus2: %d\n", vs);
778 signerinfo->verificationStatus = vs;
779
780 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
781 return SECFailure;
782 }
783
784 OSStatus
785 SecCmsSignerInfoVerifyUnAuthAttrs(SecCmsSignerInfoRef signerinfo) {
786 return SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(signerinfo, NULL);
787 }
788
789 OSStatus
790 SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(SecCmsSignerInfoRef signerinfo,CFTypeRef timeStampPolicy)
791 {
792 /*
793 unAuthAttr is an array of attributes; we expect to
794 see just one: the timestamp blob. If we have an unAuthAttr,
795 but don't see a timestamp, return an error since we have
796 no other cases where this would be present.
797 */
798
799 SecCmsAttribute *attr = NULL;
800 OSStatus status = SECFailure;
801
802 require(signerinfo, xit);
803 attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->unAuthAttr,
804 SEC_OID_PKCS9_TIMESTAMP_TOKEN, PR_TRUE);
805 if (attr == NULL)
806 {
807 status = errSecTimestampMissing;
808 goto xit;
809 }
810
811 dprintf("found an id-ct-TSTInfo\n");
812 // Don't check the nonce in this case
813 status = decodeTimeStampTokenWithPolicy(signerinfo, timeStampPolicy, (attr->values)[0], &signerinfo->encDigest, 0);
814 xit:
815 return status;
816 }
817
818 CSSM_DATA *
819 SecCmsSignerInfoGetEncDigest(SecCmsSignerInfoRef signerinfo)
820 {
821 return &signerinfo->encDigest;
822 }
823
824 SecCmsVerificationStatus
825 SecCmsSignerInfoGetVerificationStatus(SecCmsSignerInfoRef signerinfo)
826 {
827 return signerinfo->verificationStatus;
828 }
829
830 SECOidData *
831 SecCmsSignerInfoGetDigestAlg(SecCmsSignerInfoRef signerinfo)
832 {
833 return SECOID_FindOID (&(signerinfo->digestAlg.algorithm));
834 }
835
836 SECOidTag
837 SecCmsSignerInfoGetDigestAlgTag(SecCmsSignerInfoRef signerinfo)
838 {
839 SECOidData *algdata;
840
841 algdata = SECOID_FindOID (&(signerinfo->digestAlg.algorithm));
842 if (algdata != NULL)
843 return algdata->offset;
844 else
845 return SEC_OID_UNKNOWN;
846 }
847
848 CFArrayRef
849 SecCmsSignerInfoGetCertList(SecCmsSignerInfoRef signerinfo)
850 {
851 dprintfRC("SecCmsSignerInfoGetCertList: certList.rc %d\n",
852 (int)CFGetRetainCount(signerinfo->certList));
853 return signerinfo->certList;
854 }
855
856 CFArrayRef
857 SecCmsSignerInfoGetTimestampCertList(SecCmsSignerInfoRef signerinfo)
858 {
859 dprintfRC("SecCmsSignerInfoGetCertList: timestampCertList.rc %d\n",
860 (int)CFGetRetainCount(signerinfo->timestampCertList));
861 return signerinfo->timestampCertList;
862 }
863
864
865
866 int
867 SecCmsSignerInfoGetVersion(SecCmsSignerInfoRef signerinfo)
868 {
869 unsigned long version;
870
871 /* always take apart the CSSM_DATA */
872 if (SEC_ASN1DecodeInteger(&(signerinfo->version), &version) != SECSuccess)
873 return 0;
874 else
875 return (int)version;
876 }
877
878 /*
879 * SecCmsSignerInfoGetSigningTime - return the signing time,
880 * in UTCTime format, of a CMS signerInfo.
881 *
882 * sinfo - signerInfo data for this signer
883 *
884 * Returns a pointer to XXXX (what?)
885 * A return value of NULL is an error.
886 */
887 OSStatus
888 SecCmsSignerInfoGetSigningTime(SecCmsSignerInfoRef sinfo, CFAbsoluteTime *stime)
889 {
890 SecCmsAttribute *attr;
891 CSSM_DATA_PTR value;
892
893 if (sinfo == NULL)
894 return paramErr;
895
896 if (sinfo->signingTime != 0) {
897 *stime = sinfo->signingTime; /* cached copy */
898 return SECSuccess;
899 }
900
901 attr = SecCmsAttributeArrayFindAttrByOidTag(sinfo->authAttr, SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE);
902 /* XXXX multi-valued attributes NIH */
903 if (attr == NULL || (value = SecCmsAttributeGetValue(attr)) == NULL)
904 return errSecSigningTimeMissing;
905 if (DER_UTCTimeToCFDate(value, stime) != SECSuccess)
906 return errSecSigningTimeMissing;
907 sinfo->signingTime = *stime; /* make cached copy */
908 return SECSuccess;
909 }
910
911 OSStatus
912 SecCmsSignerInfoGetTimestampTime(SecCmsSignerInfoRef sinfo, CFAbsoluteTime *stime)
913 {
914 return SecCmsSignerInfoGetTimestampTimeWithPolicy(sinfo, NULL, stime);
915 }
916
917 OSStatus
918 SecCmsSignerInfoGetTimestampTimeWithPolicy(SecCmsSignerInfoRef sinfo, CFTypeRef timeStampPolicy, CFAbsoluteTime *stime)
919 {
920 OSStatus status = paramErr;
921
922 require(sinfo && stime, xit);
923
924 if (sinfo->timestampTime != 0)
925 {
926 *stime = sinfo->timestampTime; /* cached copy */
927 return noErr;
928 }
929
930 // A bit heavyweight if haven't already called verify
931 status = SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(sinfo,timeStampPolicy);
932 *stime = sinfo->timestampTime;
933 xit:
934 return status;
935 }
936
937 /*!
938 @function
939 @abstract Return the data in the signed Codesigning Hash Agility attribute.
940 @param sinfo SignerInfo data for this signer, pointer to a CFDataRef for attribute value
941 @discussion Returns a CFDataRef containing the value of the attribute
942 @result A return value of errSecInternal is an error trying to look up the oid.
943 A status value of success with null result data indicates the attribute was not present.
944 */
945 OSStatus
946 SecCmsSignerInfoGetAppleCodesigningHashAgility(SecCmsSignerInfoRef sinfo, CFDataRef *sdata)
947 {
948 SecCmsAttribute *attr;
949 CSSM_DATA_PTR value;
950
951 if (sinfo == NULL || sdata == NULL)
952 return paramErr;
953
954 *sdata = NULL;
955
956 if (sinfo->hashAgilityAttrValue != NULL) {
957 *sdata = sinfo->hashAgilityAttrValue; /* cached copy */
958 return SECSuccess;
959 }
960
961 attr = SecCmsAttributeArrayFindAttrByOidTag(sinfo->authAttr, SEC_OID_APPLE_HASH_AGILITY, PR_TRUE);
962
963 /* attribute not found */
964 if (attr == NULL || (value = SecCmsAttributeGetValue(attr)) == NULL)
965 return SECSuccess;
966
967 sinfo->hashAgilityAttrValue = CFDataCreate(NULL, value->Data, value->Length); /* make cached copy */
968 if (sinfo->hashAgilityAttrValue) {
969 *sdata = sinfo->hashAgilityAttrValue;
970 return SECSuccess;
971 }
972 return errSecAllocate;
973 }
974
975 /*
976 * Return the signing cert of a CMS signerInfo.
977 *
978 * the certs in the enclosing SignedData must have been imported already
979 */
980 SecCertificateRef
981 SecCmsSignerInfoGetSigningCertificate(SecCmsSignerInfoRef signerinfo, SecKeychainRef keychainOrArray)
982 {
983 SecCertificateRef cert;
984 SecCmsSignerIdentifier *sid;
985 OSStatus ortn;
986 CSSM_DATA_PTR *rawCerts;
987
988 if (signerinfo->cert != NULL) {
989 dprintfRC("SecCmsSignerInfoGetSigningCertificate top: cert %p cert.rc %d\n",
990 signerinfo->cert, (int)CFGetRetainCount(signerinfo->cert));
991 return signerinfo->cert;
992 }
993 ortn = SecCmsSignedDataRawCerts(signerinfo->sigd, &rawCerts);
994 if(ortn) {
995 return NULL;
996 }
997 dprintf("SecCmsSignerInfoGetSigningCertificate: numRawCerts %d\n",
998 SecCmsArrayCount((void **)rawCerts));
999
1000 /*
1001 * This cert will also need to be freed, but since we save it
1002 * in signerinfo for later, we do not want to destroy it when
1003 * we leave this function -- we let the clean-up of the entire
1004 * cinfo structure later do the destroy of this cert.
1005 */
1006 sid = &signerinfo->signerIdentifier;
1007 switch (sid->identifierType) {
1008 case SecCmsSignerIDIssuerSN:
1009 cert = CERT_FindCertByIssuerAndSN(keychainOrArray, rawCerts, signerinfo->sigd->certs, signerinfo->cmsg->poolp,
1010 sid->id.issuerAndSN);
1011 break;
1012 case SecCmsSignerIDSubjectKeyID:
1013 cert = CERT_FindCertBySubjectKeyID(keychainOrArray, rawCerts, signerinfo->sigd->certs, sid->id.subjectKeyID);
1014 break;
1015 default:
1016 cert = NULL;
1017 break;
1018 }
1019
1020 /* cert can be NULL at that point */
1021 signerinfo->cert = cert; /* earmark it */
1022 dprintfRC("SecCmsSignerInfoGetSigningCertificate end: certp %p cert.rc %d\n",
1023 signerinfo->cert, (int)CFGetRetainCount(signerinfo->cert));
1024
1025 return cert;
1026 }
1027
1028 /*
1029 * SecCmsSignerInfoGetSignerCommonName - return the common name of the signer
1030 *
1031 * sinfo - signerInfo data for this signer
1032 *
1033 * Returns a CFStringRef containing the common name of the signer.
1034 * A return value of NULL is an error.
1035 */
1036 CFStringRef
1037 SecCmsSignerInfoGetSignerCommonName(SecCmsSignerInfoRef sinfo)
1038 {
1039 SecCertificateRef signercert;
1040 CFStringRef commonName = NULL;
1041
1042 /* will fail if cert is not verified */
1043 if ((signercert = SecCmsSignerInfoGetSigningCertificate(sinfo, NULL)) == NULL)
1044 return NULL;
1045
1046 if (errSecSuccess != SecCertificateCopyCommonName(signercert, &commonName)) {
1047 return NULL;
1048 }
1049
1050 return commonName;
1051 }
1052
1053 /*
1054 * SecCmsSignerInfoGetSignerEmailAddress - return the email address of the signer
1055 *
1056 * sinfo - signerInfo data for this signer
1057 *
1058 * Returns a CFStringRef containing the name of the signer.
1059 * A return value of NULL is an error.
1060 */
1061 CFStringRef
1062 SecCmsSignerInfoGetSignerEmailAddress(SecCmsSignerInfoRef sinfo)
1063 {
1064 SecCertificateRef signercert;
1065 CFStringRef emailAddress = NULL;
1066
1067 if ((signercert = SecCmsSignerInfoGetSigningCertificate(sinfo, NULL)) == NULL)
1068 return NULL;
1069
1070 SecCertificateGetEmailAddress(signercert, &emailAddress);
1071
1072 return emailAddress;
1073 }
1074
1075
1076 /*
1077 * SecCmsSignerInfoAddAuthAttr - add an attribute to the
1078 * authenticated (i.e. signed) attributes of "signerinfo".
1079 */
1080 OSStatus
1081 SecCmsSignerInfoAddAuthAttr(SecCmsSignerInfoRef signerinfo, SecCmsAttribute *attr)
1082 {
1083 return SecCmsAttributeArrayAddAttr(signerinfo->cmsg->poolp, &(signerinfo->authAttr), attr);
1084 }
1085
1086 /*
1087 * SecCmsSignerInfoAddUnauthAttr - add an attribute to the
1088 * unauthenticated attributes of "signerinfo".
1089 */
1090 OSStatus
1091 SecCmsSignerInfoAddUnauthAttr(SecCmsSignerInfoRef signerinfo, SecCmsAttribute *attr)
1092 {
1093 return SecCmsAttributeArrayAddAttr(signerinfo->cmsg->poolp, &(signerinfo->unAuthAttr), attr);
1094 }
1095
1096 /*
1097 * SecCmsSignerInfoAddSigningTime - add the signing time to the
1098 * authenticated (i.e. signed) attributes of "signerinfo".
1099 *
1100 * This is expected to be included in outgoing signed
1101 * messages for email (S/MIME) but is likely useful in other situations.
1102 *
1103 * This should only be added once; a second call will do nothing.
1104 *
1105 * XXX This will probably just shove the current time into "signerinfo"
1106 * but it will not actually get signed until the entire item is
1107 * processed for encoding. Is this (expected to be small) delay okay?
1108 */
1109 OSStatus
1110 SecCmsSignerInfoAddSigningTime(SecCmsSignerInfoRef signerinfo, CFAbsoluteTime t)
1111 {
1112 SecCmsAttribute *attr;
1113 CSSM_DATA stime;
1114 void *mark;
1115 PLArenaPool *poolp;
1116
1117 poolp = signerinfo->cmsg->poolp;
1118
1119 mark = PORT_ArenaMark(poolp);
1120
1121 /* create new signing time attribute */
1122 if (DER_CFDateToUTCTime(t, &stime) != SECSuccess)
1123 goto loser;
1124
1125 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_PKCS9_SIGNING_TIME, &stime, PR_FALSE)) == NULL) {
1126 SECITEM_FreeItem (&stime, PR_FALSE);
1127 goto loser;
1128 }
1129
1130 SECITEM_FreeItem (&stime, PR_FALSE);
1131
1132 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
1133 goto loser;
1134
1135 PORT_ArenaUnmark (poolp, mark);
1136
1137 return SECSuccess;
1138
1139 loser:
1140 PORT_ArenaRelease (poolp, mark);
1141 return SECFailure;
1142 }
1143
1144 /*
1145 * SecCmsSignerInfoAddSMIMECaps - add a SMIMECapabilities attribute to the
1146 * authenticated (i.e. signed) attributes of "signerinfo".
1147 *
1148 * This is expected to be included in outgoing signed
1149 * messages for email (S/MIME).
1150 */
1151 OSStatus
1152 SecCmsSignerInfoAddSMIMECaps(SecCmsSignerInfoRef signerinfo)
1153 {
1154 SecCmsAttribute *attr;
1155 CSSM_DATA_PTR smimecaps = NULL;
1156 void *mark;
1157 PLArenaPool *poolp;
1158
1159 poolp = signerinfo->cmsg->poolp;
1160
1161 mark = PORT_ArenaMark(poolp);
1162
1163 smimecaps = SECITEM_AllocItem(poolp, NULL, 0);
1164 if (smimecaps == NULL)
1165 goto loser;
1166
1167 /* create new signing time attribute */
1168 #if 1
1169 // @@@ We don't do Fortezza yet.
1170 if (SecSMIMECreateSMIMECapabilities((SecArenaPoolRef)poolp, smimecaps, PR_FALSE) != SECSuccess)
1171 #else
1172 if (SecSMIMECreateSMIMECapabilities(poolp, smimecaps,
1173 PK11_FortezzaHasKEA(signerinfo->cert)) != SECSuccess)
1174 #endif
1175 goto loser;
1176
1177 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_PKCS9_SMIME_CAPABILITIES, smimecaps, PR_TRUE)) == NULL)
1178 goto loser;
1179
1180 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
1181 goto loser;
1182
1183 PORT_ArenaUnmark (poolp, mark);
1184 return SECSuccess;
1185
1186 loser:
1187 PORT_ArenaRelease (poolp, mark);
1188 return SECFailure;
1189 }
1190
1191 /*
1192 * SecCmsSignerInfoAddSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
1193 * authenticated (i.e. signed) attributes of "signerinfo".
1194 *
1195 * This is expected to be included in outgoing signed messages for email (S/MIME).
1196 */
1197 OSStatus
1198 SecCmsSignerInfoAddSMIMEEncKeyPrefs(SecCmsSignerInfoRef signerinfo, SecCertificateRef cert, SecKeychainRef keychainOrArray)
1199 {
1200 SecCmsAttribute *attr;
1201 CSSM_DATA_PTR smimeekp = NULL;
1202 void *mark;
1203 PLArenaPool *poolp;
1204
1205 #if 0
1206 CFTypeRef policy;
1207
1208 /* verify this cert for encryption */
1209 policy = CERT_PolicyForCertUsage(certUsageEmailRecipient);
1210 if (CERT_VerifyCert(keychainOrArray, cert, policy, CFAbsoluteTimeGetCurrent(), NULL) != SECSuccess) {
1211 CFRelease(policy);
1212 return SECFailure;
1213 }
1214 CFRelease(policy);
1215 #endif
1216
1217 poolp = signerinfo->cmsg->poolp;
1218 mark = PORT_ArenaMark(poolp);
1219
1220 smimeekp = SECITEM_AllocItem(poolp, NULL, 0);
1221 if (smimeekp == NULL)
1222 goto loser;
1223
1224 /* create new signing time attribute */
1225 if (SecSMIMECreateSMIMEEncKeyPrefs((SecArenaPoolRef)poolp, smimeekp, cert) != SECSuccess)
1226 goto loser;
1227
1228 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL)
1229 goto loser;
1230
1231 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
1232 goto loser;
1233
1234 PORT_ArenaUnmark (poolp, mark);
1235 return SECSuccess;
1236
1237 loser:
1238 PORT_ArenaRelease (poolp, mark);
1239 return SECFailure;
1240 }
1241
1242 /*
1243 * SecCmsSignerInfoAddMSSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
1244 * authenticated (i.e. signed) attributes of "signerinfo", using the OID prefered by Microsoft.
1245 *
1246 * This is expected to be included in outgoing signed messages for email (S/MIME),
1247 * if compatibility with Microsoft mail clients is wanted.
1248 */
1249 OSStatus
1250 SecCmsSignerInfoAddMSSMIMEEncKeyPrefs(SecCmsSignerInfoRef signerinfo, SecCertificateRef cert, SecKeychainRef keychainOrArray)
1251 {
1252 SecCmsAttribute *attr;
1253 CSSM_DATA_PTR smimeekp = NULL;
1254 void *mark;
1255 PLArenaPool *poolp;
1256
1257 #if 0
1258 CFTypeRef policy;
1259
1260 /* verify this cert for encryption */
1261 policy = CERT_PolicyForCertUsage(certUsageEmailRecipient);
1262 if (CERT_VerifyCert(keychainOrArray, cert, policy, CFAbsoluteTimeGetCurrent(), NULL) != SECSuccess) {
1263 CFRelease(policy);
1264 return SECFailure;
1265 }
1266 CFRelease(policy);
1267 #endif
1268
1269 poolp = signerinfo->cmsg->poolp;
1270 mark = PORT_ArenaMark(poolp);
1271
1272 smimeekp = SECITEM_AllocItem(poolp, NULL, 0);
1273 if (smimeekp == NULL)
1274 goto loser;
1275
1276 /* create new signing time attribute */
1277 if (SecSMIMECreateMSSMIMEEncKeyPrefs((SecArenaPoolRef)poolp, smimeekp, cert) != SECSuccess)
1278 goto loser;
1279
1280 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_MS_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL)
1281 goto loser;
1282
1283 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
1284 goto loser;
1285
1286 PORT_ArenaUnmark (poolp, mark);
1287 return SECSuccess;
1288
1289 loser:
1290 PORT_ArenaRelease (poolp, mark);
1291 return SECFailure;
1292 }
1293
1294 /*
1295 * SecCmsSignerInfoAddTimeStamp - add time stamp to the
1296 * unauthenticated (i.e. unsigned) attributes of "signerinfo".
1297 *
1298 * This will initially be used for time stamping signed applications
1299 * by using a Time Stamping Authority. It may also be included in outgoing signed
1300 * messages for email (S/MIME), and may be useful in other situations.
1301 *
1302 * This should only be added once; a second call will do nothing.
1303 *
1304 */
1305
1306 /*
1307 Countersignature attribute values have ASN.1 type Countersignature:
1308 Countersignature ::= SignerInfo
1309 Countersignature values have the same meaning as SignerInfo values
1310 for ordinary signatures, except that:
1311 1. The signedAttributes field MUST NOT contain a content-type
1312 attribute; there is no content type for countersignatures.
1313 2. The signedAttributes field MUST contain a message-digest
1314 attribute if it contains any other attributes.
1315 3. The input to the message-digesting process is the contents octets
1316 of the DER encoding of the signatureValue field of the SignerInfo
1317 value with which the attribute is associated.
1318 */
1319
1320 /*!
1321 @function
1322 @abstract Create a timestamp unsigned attribute with a TimeStampToken.
1323 */
1324
1325 OSStatus
1326 SecCmsSignerInfoAddTimeStamp(SecCmsSignerInfoRef signerinfo, CSSM_DATA *tstoken)
1327 {
1328 SecCmsAttribute *attr;
1329 PLArenaPool *poolp = signerinfo->cmsg->poolp;
1330 void *mark = PORT_ArenaMark(poolp);
1331
1332 // We have already encoded this ourselves, so last param is PR_TRUE
1333 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_PKCS9_TIMESTAMP_TOKEN, tstoken, PR_TRUE)) == NULL)
1334 goto loser;
1335
1336 if (SecCmsSignerInfoAddUnauthAttr(signerinfo, attr) != SECSuccess)
1337 goto loser;
1338
1339 PORT_ArenaUnmark (poolp, mark);
1340
1341 return SECSuccess;
1342
1343 loser:
1344 PORT_ArenaRelease (poolp, mark);
1345 return SECFailure;
1346 }
1347
1348 /*
1349 * SecCmsSignerInfoAddCounterSignature - countersign a signerinfo
1350 *
1351 * 1. digest the DER-encoded signature value of the original signerinfo
1352 * 2. create new signerinfo with correct version, sid, digestAlg
1353 * 3. add message-digest authAttr, but NO content-type
1354 * 4. sign the authAttrs
1355 * 5. DER-encode the new signerInfo
1356 * 6. add the whole thing to original signerInfo's unAuthAttrs
1357 * as a SEC_OID_PKCS9_COUNTER_SIGNATURE attribute
1358 *
1359 * XXXX give back the new signerinfo?
1360 */
1361 OSStatus
1362 SecCmsSignerInfoAddCounterSignature(SecCmsSignerInfoRef signerinfo,
1363 SECOidTag digestalg, SecIdentityRef identity)
1364 {
1365 /* XXXX TBD XXXX */
1366 return SECFailure;
1367 }
1368
1369 /*!
1370 @function
1371 @abstract Add the Apple Codesigning Hash Agility attribute to the authenticated (i.e. signed) attributes of "signerinfo".
1372 @discussion This is expected to be included in outgoing signed Apple code signatures.
1373 */
1374 OSStatus
1375 SecCmsSignerInfoAddAppleCodesigningHashAgility(SecCmsSignerInfoRef signerinfo, CFDataRef attrValue)
1376 {
1377 SecCmsAttribute *attr;
1378 PLArenaPool *poolp = signerinfo->cmsg->poolp;
1379 void *mark = PORT_ArenaMark(poolp);
1380 OSStatus status = SECFailure;
1381
1382 /* The value is required for this attribute. */
1383 if (!attrValue) {
1384 status = errSecParam;
1385 goto loser;
1386 }
1387
1388 /*
1389 * SecCmsAttributeCreate makes a copy of the data in value, so
1390 * we don't need to copy into the CSSM_DATA struct.
1391 */
1392 CSSM_DATA value;
1393 value.Length = CFDataGetLength(attrValue);
1394 value.Data = (uint8_t *)CFDataGetBytePtr(attrValue);
1395
1396 if ((attr = SecCmsAttributeCreate(poolp,
1397 SEC_OID_APPLE_HASH_AGILITY,
1398 &value,
1399 PR_FALSE)) == NULL) {
1400 status = errSecAllocate;
1401 goto loser;
1402 }
1403
1404 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess) {
1405 status = errSecInternalError;
1406 goto loser;
1407 }
1408
1409 PORT_ArenaUnmark(poolp, mark);
1410 return SECSuccess;
1411
1412 loser:
1413 PORT_ArenaRelease(poolp, mark);
1414 return status;
1415 }
1416
1417 SecCertificateRef SecCmsSignerInfoCopyCertFromEncryptionKeyPreference(SecCmsSignerInfoRef signerinfo) {
1418 SecCertificateRef cert = NULL;
1419 SecCmsAttribute *attr;
1420 CSSM_DATA_PTR ekp;
1421 SecKeychainRef keychainOrArray;
1422
1423 (void)SecKeychainCopyDefault(&keychainOrArray);
1424
1425 /* sanity check - see if verification status is ok (unverified does not count...) */
1426 if (signerinfo->verificationStatus != SecCmsVSGoodSignature)
1427 return NULL;
1428
1429 /* find preferred encryption cert */
1430 if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr) &&
1431 (attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr,
1432 SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, PR_TRUE)) != NULL)
1433 { /* we have a SMIME_ENCRYPTION_KEY_PREFERENCE attribute! Find the cert. */
1434 ekp = SecCmsAttributeGetValue(attr);
1435 if (ekp == NULL)
1436 return NULL;
1437
1438 CSSM_DATA_PTR *rawCerts = NULL;
1439 if (signerinfo->sigd) {
1440 rawCerts = signerinfo->sigd->rawCerts;
1441 }
1442 cert = SecSMIMEGetCertFromEncryptionKeyPreference(keychainOrArray, rawCerts, ekp);
1443 }
1444 return cert;
1445 }
1446
1447 /*
1448 * XXXX the following needs to be done in the S/MIME layer code
1449 * after signature of a signerinfo is verified
1450 */
1451 OSStatus
1452 SecCmsSignerInfoSaveSMIMEProfile(SecCmsSignerInfoRef signerinfo)
1453 {
1454 SecCertificateRef cert = NULL;
1455 CSSM_DATA_PTR profile = NULL;
1456 SecCmsAttribute *attr;
1457 CSSM_DATA_PTR utc_stime = NULL;
1458 CSSM_DATA_PTR ekp;
1459 int save_error;
1460 OSStatus rv;
1461 Boolean must_free_cert = PR_FALSE;
1462 OSStatus status;
1463 SecKeychainRef keychainOrArray;
1464
1465 status = SecKeychainCopyDefault(&keychainOrArray);
1466
1467 /* sanity check - see if verification status is ok (unverified does not count...) */
1468 if (signerinfo->verificationStatus != SecCmsVSGoodSignature)
1469 return SECFailure;
1470
1471 /* find preferred encryption cert */
1472 if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr) &&
1473 (attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr,
1474 SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, PR_TRUE)) != NULL)
1475 { /* we have a SMIME_ENCRYPTION_KEY_PREFERENCE attribute! */
1476 ekp = SecCmsAttributeGetValue(attr);
1477 if (ekp == NULL)
1478 return SECFailure;
1479
1480 /* we assume that all certs coming with the message have been imported to the */
1481 /* temporary database */
1482 cert = SecSMIMEGetCertFromEncryptionKeyPreference(keychainOrArray, NULL, ekp);
1483 if (cert == NULL)
1484 return SECFailure;
1485 must_free_cert = PR_TRUE;
1486 }
1487
1488 if (cert == NULL) {
1489 /* no preferred cert found?
1490 * find the cert the signerinfo is signed with instead */
1491 CFStringRef emailAddress=NULL;
1492
1493 cert = SecCmsSignerInfoGetSigningCertificate(signerinfo, keychainOrArray);
1494 if (cert == NULL)
1495 return SECFailure;
1496 if (SecCertificateGetEmailAddress(cert,&emailAddress))
1497 return SECFailure;
1498 }
1499
1500 /* verify this cert for encryption (has been verified for signing so far) */ /* don't verify this cert for encryption. It may just be a signing cert.
1501 * that's OK, we can still save the S/MIME profile. The encryption cert
1502 * should have already been saved */
1503 #ifdef notdef
1504 if (CERT_VerifyCert(keychainOrArray, cert, certUsageEmailRecipient, CFAbsoluteTimeGetCurrent(), NULL) != SECSuccess) {
1505 if (must_free_cert)
1506 CERT_DestroyCertificate(cert);
1507 return SECFailure;
1508 }
1509 #endif
1510
1511 /* XXX store encryption cert permanently? */
1512
1513 /*
1514 * Remember the current error set because we do not care about
1515 * anything set by the functions we are about to call.
1516 */
1517 save_error = PORT_GetError();
1518
1519 if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr)) {
1520 attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr,
1521 SEC_OID_PKCS9_SMIME_CAPABILITIES,
1522 PR_TRUE);
1523 profile = SecCmsAttributeGetValue(attr);
1524 attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr,
1525 SEC_OID_PKCS9_SIGNING_TIME,
1526 PR_TRUE);
1527 utc_stime = SecCmsAttributeGetValue(attr);
1528 }
1529
1530 rv = CERT_SaveSMimeProfile (cert, profile, utc_stime);
1531 if (must_free_cert)
1532 CERT_DestroyCertificate(cert);
1533
1534 /*
1535 * Restore the saved error in case the calls above set a new
1536 * one that we do not actually care about.
1537 */
1538 PORT_SetError (save_error);
1539
1540 return rv;
1541 }
1542
1543 /*
1544 * SecCmsSignerInfoIncludeCerts - set cert chain inclusion mode for this signer
1545 */
1546 OSStatus
1547 SecCmsSignerInfoIncludeCerts(SecCmsSignerInfoRef signerinfo, SecCmsCertChainMode cm, SECCertUsage usage)
1548 {
1549 if (signerinfo->cert == NULL)
1550 return SECFailure;
1551
1552 /* don't leak if we get called twice */
1553 if (signerinfo->certList != NULL) {
1554 CFRelease(signerinfo->certList);
1555 signerinfo->certList = NULL;
1556 }
1557
1558 switch (cm) {
1559 case SecCmsCMNone:
1560 signerinfo->certList = NULL;
1561 break;
1562 case SecCmsCMCertOnly:
1563 signerinfo->certList = CERT_CertListFromCert(signerinfo->cert);
1564 break;
1565 case SecCmsCMCertChain:
1566 signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_FALSE);
1567 break;
1568 case SecCmsCMCertChainWithRoot:
1569 signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_TRUE);
1570 break;
1571 }
1572
1573 if (cm != SecCmsCMNone && signerinfo->certList == NULL)
1574 return SECFailure;
1575
1576 return SECSuccess;
1577 }