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