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