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