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