]>
Commit | Line | Data |
---|---|---|
d8f41ccd 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" | |
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> | |
866f8763 | 59 | #include <Security/SecItem.h> |
d8f41ccd A |
60 | |
61 | #include "tsaSupport.h" | |
62 | #include "tsaSupportPriv.h" | |
63 | ||
822b670c A |
64 | #include <syslog.h> |
65 | ||
d8f41ccd A |
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 | |
5c19dc3a | 81 | #define dprintf(args...) fprintf(stderr, args) |
d8f41ccd A |
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; | |
866f8763 | 217 | CFDictionaryRef keyAttrs = NULL; |
d8f41ccd A |
218 | |
219 | if (SecIdentityCopyCertificate(identity, &cert)) | |
220 | goto loser; | |
221 | if (SecIdentityCopyPrivateKey(identity, &signingKey)) | |
222 | goto loser; | |
223 | ||
866f8763 A |
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 | ||
d8f41ccd A |
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); | |
866f8763 A |
240 | if (keyAttrs) |
241 | CFRelease(keyAttrs); | |
d8f41ccd A |
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); | |
6b200bc3 A |
283 | if (SECITEM_CopyItem(poolp, signerinfo->signerIdentifier.id.subjectKeyID, |
284 | subjKeyID)) { | |
285 | goto loser; | |
286 | } | |
d8f41ccd A |
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 | } | |
e3d460c9 A |
341 | if (si->hashAgilityAttrValue != NULL) { |
342 | dprintfRC("SecCmsSignerInfoDestroy top: hashAgilityAttrValue.rc %d\n", | |
343 | (int)CFGetRetainCount(si->hashAgilityAttrValue)); | |
344 | CFRelease(si->hashAgilityAttrValue); | |
345 | } | |
d8f41ccd A |
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; | |
60c433a9 | 362 | PLArenaPool *poolp, *tmppoolp = NULL; |
d8f41ccd A |
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 | |
5c19dc3a A |
392 | |
393 | #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR)) | |
d8f41ccd | 394 | if (SecKeyGetAlgorithmID(signerinfo->pubKey,&algID)) { |
5c19dc3a A |
395 | #else |
396 | /* TBD: Unify this code. Currently, iOS has an incompatible | |
397 | * SecKeyGetAlgorithmID implementation. */ | |
398 | if (true) { | |
399 | #endif | |
d8f41ccd A |
400 | PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); |
401 | goto loser; | |
5c19dc3a | 402 | } |
d8f41ccd | 403 | CFRelease(signerinfo->pubKey); |
5c19dc3a | 404 | signerinfo->pubKey = NULL; |
d8f41ccd A |
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 :-( */ | |
60c433a9 | 475 | tmppoolp = 0; |
d8f41ccd A |
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); | |
60c433a9 A |
519 | if (tmppoolp) |
520 | PORT_FreeArena(tmppoolp, PR_FALSE); | |
d8f41ccd A |
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; | |
866f8763 | 604 | SecCmsAttribute *attr = NULL; |
d8f41ccd | 605 | CSSM_DATA encoded_attrs; |
866f8763 | 606 | SecCertificateRef cert = NULL; |
d8f41ccd | 607 | SecCmsVerificationStatus vs = SecCmsVSUnverified; |
866f8763 | 608 | PLArenaPool *poolp = NULL; |
d8f41ccd A |
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); | |
e3d460c9 A |
625 | |
626 | OSStatus status; | |
627 | if ((status = SecCertificateCopyPublicKey(cert, &publickey))) { | |
628 | syslog(LOG_ERR, "SecCmsSignerInfoVerifyWithPolicy: copy public key failed %d", (int)status); | |
d8f41ccd A |
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 | ||
e3d460c9 | 705 | vs = (VFY_VerifyData (encoded_attrs.Data, (int)encoded_attrs.Length, |
d8f41ccd A |
706 | publickey, &(signerinfo->encDigest), |
707 | digestAlgTag, digestEncAlgTag, | |
e3d460c9 | 708 | signerinfo->cmsg->pwfn_arg) != SECSuccess) ? SecCmsVSBadSignature : SecCmsVSGoodSignature; |
d8f41ccd A |
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 | ||
e3d460c9 | 723 | vs = (VFY_VerifyDigest(digest, publickey, sig, |
d8f41ccd | 724 | digestAlgTag, digestEncAlgTag, |
e3d460c9 | 725 | signerinfo->cmsg->pwfn_arg) != SECSuccess) ? SecCmsVSBadSignature : SecCmsVSGoodSignature; |
d8f41ccd A |
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); | |
822b670c | 736 | if (rux) { |
d8f41ccd | 737 | goto loser; |
822b670c | 738 | } |
d8f41ccd A |
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 | ||
e3d460c9 A |
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 | ||
d8f41ccd A |
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: | |
866f8763 | 1009 | cert = CERT_FindCertByIssuerAndSN(keychainOrArray, rawCerts, signerinfo->sigd->certs, signerinfo->cmsg->poolp, |
d8f41ccd A |
1010 | sid->id.issuerAndSN); |
1011 | break; | |
1012 | case SecCmsSignerIDSubjectKeyID: | |
866f8763 | 1013 | cert = CERT_FindCertBySubjectKeyID(keychainOrArray, rawCerts, signerinfo->sigd->certs, sid->id.subjectKeyID); |
d8f41ccd A |
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 | ||
866f8763 A |
1046 | if (errSecSuccess != SecCertificateCopyCommonName(signercert, &commonName)) { |
1047 | return NULL; | |
1048 | } | |
d8f41ccd A |
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 | ||
e3d460c9 A |
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 | ||
866f8763 A |
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 | ||
d8f41ccd A |
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 */ | |
866f8763 | 1482 | cert = SecSMIMEGetCertFromEncryptionKeyPreference(keychainOrArray, NULL, ekp); |
d8f41ccd A |
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 | } |