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/
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.
12 * The Original Code is the Netscape security libraries.
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
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
35 * CMS signerInfo methods.
38 #include <Security/SecCmsSignerInfo.h>
39 #include "SecSMIMEPriv.h"
48 #include <security_asn1/secasn1.h>
49 #include <security_asn1/secerr.h>
50 #include <Security/SecKeychain.h>
51 #include <Security/SecIdentity.h>
52 #include <Security/SecCertificatePriv.h>
53 #include <Security/SecKeyPriv.h>
54 #include <CoreFoundation/CFTimeZone.h>
55 #include <utilities/SecCFWrappers.h>
56 #include <utilities/debugging.h>
57 #include <AssertMacros.h>
58 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
59 #include <Security/SecPolicyPriv.h>
60 #include <Security/SecItem.h>
62 #include "tsaSupport.h"
63 #include "tsaSupportPriv.h"
67 #define HIDIGIT(v) (((v) / 10) + '0')
68 #define LODIGIT(v) (((v) % 10) + '0')
70 #define ISDIGIT(dig) (((dig) >= '0') && ((dig) <= '9'))
71 #define CAPTURE(var,p,label) \
73 if (!ISDIGIT((p)[0]) || !ISDIGIT((p)[1])) goto label; \
74 (var) = ((p)[0] - '0') * 10 + ((p)[1] - '0'); \
78 #define SIGINFO_DEBUG 1
82 #define dprintf(args...) fprintf(stderr, args)
84 #define dprintf(args...)
88 #define dprintfRC(args...) dprintf(args)
90 #define dprintfRC(args...)
94 DER_UTCTimeToCFDate(const CSSM_DATA_PTR utcTime
, CFAbsoluteTime
*date
)
96 CFGregorianDate gdate
;
97 char *string
= (char *)utcTime
->Data
;
98 long year
, month
, mday
, hour
, minute
, second
, hourOff
, minOff
;
99 CFTimeZoneRef timeZone
;
101 /* Verify time is formatted properly and capture information */
105 CAPTURE(year
,string
+0,loser
);
107 /* ASSUME that year # is in the 2000's, not the 1900's */
110 CAPTURE(month
,string
+2,loser
);
111 if ((month
== 0) || (month
> 12)) goto loser
;
112 CAPTURE(mday
,string
+4,loser
);
113 if ((mday
== 0) || (mday
> 31)) goto loser
;
114 CAPTURE(hour
,string
+6,loser
);
115 if (hour
> 23) goto loser
;
116 CAPTURE(minute
,string
+8,loser
);
117 if (minute
> 59) goto loser
;
118 if (ISDIGIT(string
[10])) {
119 CAPTURE(second
,string
+10,loser
);
120 if (second
> 59) goto loser
;
123 if (string
[10] == '+') {
124 CAPTURE(hourOff
,string
+11,loser
);
125 if (hourOff
> 23) goto loser
;
126 CAPTURE(minOff
,string
+13,loser
);
127 if (minOff
> 59) goto loser
;
128 } else if (string
[10] == '-') {
129 CAPTURE(hourOff
,string
+11,loser
);
130 if (hourOff
> 23) goto loser
;
132 CAPTURE(minOff
,string
+13,loser
);
133 if (minOff
> 59) goto loser
;
135 } else if (string
[10] != 'Z') {
139 gdate
.year
= (SInt32
)(year
+ 1900);
143 gdate
.minute
= minute
;
144 gdate
.second
= second
;
146 if (hourOff
== 0 && minOff
== 0)
147 timeZone
= NULL
; /* GMT */
150 timeZone
= CFTimeZoneCreateWithTimeIntervalFromGMT(NULL
, (hourOff
* 60 + minOff
) * 60);
153 *date
= CFGregorianDateGetAbsoluteTime(gdate
, timeZone
);
164 DER_CFDateToUTCTime(CFAbsoluteTime date
, CSSM_DATA_PTR utcTime
)
166 CFGregorianDate gdate
= CFAbsoluteTimeGetGregorianDate(date
, NULL
/* GMT */);
170 utcTime
->Length
= 13;
171 utcTime
->Data
= d
= PORT_Alloc(13);
175 /* UTC time does not handle the years before 1950 */
176 if (gdate
.year
< 1950)
179 /* remove the century since it's added to the year by the
180 CFAbsoluteTimeGetGregorianDate routine, but is not needed for UTC time */
182 second
= gdate
.second
+ 0.5;
184 d
[0] = HIDIGIT(gdate
.year
);
185 d
[1] = LODIGIT(gdate
.year
);
186 d
[2] = HIDIGIT(gdate
.month
);
187 d
[3] = LODIGIT(gdate
.month
);
188 d
[4] = HIDIGIT(gdate
.day
);
189 d
[5] = LODIGIT(gdate
.day
);
190 d
[6] = HIDIGIT(gdate
.hour
);
191 d
[7] = LODIGIT(gdate
.hour
);
192 d
[8] = HIDIGIT(gdate
.minute
);
193 d
[9] = LODIGIT(gdate
.minute
);
194 d
[10] = HIDIGIT(second
);
195 d
[11] = LODIGIT(second
);
200 /* =============================================================================
204 nss_cmssignerinfo_create(SecCmsMessageRef cmsg
, SecCmsSignerIDSelector type
, SecCertificateRef cert
, CSSM_DATA_PTR subjKeyID
, SecPublicKeyRef pubKey
, SecPrivateKeyRef signingKey
, SECOidTag digestalgtag
);
207 SecCmsSignerInfoCreateWithSubjKeyID(SecCmsMessageRef cmsg
, CSSM_DATA_PTR subjKeyID
, SecPublicKeyRef pubKey
, SecPrivateKeyRef signingKey
, SECOidTag digestalgtag
)
209 return nss_cmssignerinfo_create(cmsg
, SecCmsSignerIDSubjectKeyID
, NULL
, subjKeyID
, pubKey
, signingKey
, digestalgtag
);
213 SecCmsSignerInfoCreate(SecCmsMessageRef cmsg
, SecIdentityRef identity
, SECOidTag digestalgtag
)
215 SecCmsSignerInfoRef signerInfo
= NULL
;
216 SecCertificateRef cert
= NULL
;
217 SecPrivateKeyRef signingKey
= NULL
;
218 CFDictionaryRef keyAttrs
= NULL
;
220 if (SecIdentityCopyCertificate(identity
, &cert
))
222 if (SecIdentityCopyPrivateKey(identity
, &signingKey
))
225 /* In some situations, the "Private Key" in the identity is actually a public key. */
226 keyAttrs
= SecKeyCopyAttributes(signingKey
);
229 CFTypeRef
class = CFDictionaryGetValue(keyAttrs
, kSecAttrKeyClass
);
230 if (!class || (CFGetTypeID(class) != CFStringGetTypeID()) || !CFEqual(class, kSecAttrKeyClassPrivate
))
234 signerInfo
= nss_cmssignerinfo_create(cmsg
, SecCmsSignerIDIssuerSN
, cert
, NULL
, NULL
, signingKey
, digestalgtag
);
240 CFRelease(signingKey
);
248 nss_cmssignerinfo_create(SecCmsMessageRef cmsg
, SecCmsSignerIDSelector type
, SecCertificateRef cert
, CSSM_DATA_PTR subjKeyID
, SecPublicKeyRef pubKey
, SecPrivateKeyRef signingKey
, SECOidTag digestalgtag
)
251 SecCmsSignerInfoRef signerinfo
;
257 mark
= PORT_ArenaMark(poolp
);
259 signerinfo
= (SecCmsSignerInfoRef
)PORT_ArenaZAlloc(poolp
, sizeof(SecCmsSignerInfo
));
260 if (signerinfo
== NULL
) {
261 PORT_ArenaRelease(poolp
, mark
);
266 signerinfo
->cmsg
= cmsg
;
269 case SecCmsSignerIDIssuerSN
:
270 signerinfo
->signerIdentifier
.identifierType
= SecCmsSignerIDIssuerSN
;
271 if ((signerinfo
->cert
= CERT_DupCertificate(cert
)) == NULL
)
273 if ((signerinfo
->signerIdentifier
.id
.issuerAndSN
= CERT_GetCertIssuerAndSN(poolp
, cert
)) == NULL
)
275 dprintfRC("nss_cmssignerinfo_create: SecCmsSignerIDIssuerSN: cert.rc %d\n",
276 (int)CFGetRetainCount(signerinfo
->cert
));
278 case SecCmsSignerIDSubjectKeyID
:
279 signerinfo
->signerIdentifier
.identifierType
= SecCmsSignerIDSubjectKeyID
;
280 PORT_Assert(subjKeyID
);
283 signerinfo
->signerIdentifier
.id
.subjectKeyID
= PORT_ArenaNew(poolp
, CSSM_DATA
);
284 if (SECITEM_CopyItem(poolp
, signerinfo
->signerIdentifier
.id
.subjectKeyID
,
288 signerinfo
->pubKey
= SECKEY_CopyPublicKey(pubKey
);
289 if (!signerinfo
->pubKey
)
299 signerinfo
->signingKey
= SECKEY_CopyPrivateKey(signingKey
);
300 if (!signerinfo
->signingKey
)
303 /* set version right now */
304 version
= SEC_CMS_SIGNER_INFO_VERSION_ISSUERSN
;
305 /* RFC2630 5.3 "version is the syntax version number. If the .... " */
306 if (signerinfo
->signerIdentifier
.identifierType
== SecCmsSignerIDSubjectKeyID
)
307 version
= SEC_CMS_SIGNER_INFO_VERSION_SUBJKEY
;
308 (void)SEC_ASN1EncodeInteger(poolp
, &(signerinfo
->version
), (long)version
);
310 if (SECOID_SetAlgorithmID(poolp
, &signerinfo
->digestAlg
, digestalgtag
, NULL
) != SECSuccess
)
313 PORT_ArenaUnmark(poolp
, mark
);
317 PORT_ArenaRelease(poolp
, mark
);
322 * SecCmsSignerInfoDestroy - destroy a SignerInfo data structure
325 SecCmsSignerInfoDestroy(SecCmsSignerInfoRef si
)
327 if (si
->cert
!= NULL
) {
328 dprintfRC("SecCmsSignerInfoDestroy top: certp %p cert.rc %d\n",
329 si
->cert
, (int)CFGetRetainCount(si
->cert
));
330 CERT_DestroyCertificate(si
->cert
);
332 if (si
->certList
!= NULL
) {
333 dprintfRC("SecCmsSignerInfoDestroy top: certList.rc %d\n",
334 (int)CFGetRetainCount(si
->certList
));
335 CFRelease(si
->certList
);
337 if (si
->timestampCertList
!= NULL
) {
338 dprintfRC("SecCmsSignerInfoDestroy top: timestampCertList.rc %d\n",
339 (int)CFGetRetainCount(si
->timestampCertList
));
340 CFRelease(si
->timestampCertList
);
342 if (si
->timestampCert
!= NULL
) {
343 dprintfRC("SecCmsSignerInfoDestroy top: timestampCert.rc %d\n",
344 (int)CFGetRetainCount(si
->timestampCert
));
345 CFRelease(si
->timestampCert
);
347 if (si
->hashAgilityAttrValue
!= NULL
) {
348 dprintfRC("SecCmsSignerInfoDestroy top: hashAgilityAttrValue.rc %d\n",
349 (int)CFGetRetainCount(si
->hashAgilityAttrValue
));
350 CFRelease(si
->hashAgilityAttrValue
);
352 if (si
->hashAgilityV2AttrValues
!= NULL
) {
353 dprintfRC("SecCmsSignerInfoDestroy top: hashAgilityV2AttrValues.rc %d\n",
354 (int)CFGetRetainCount(si
->hashAgilityV2AttrValues
));
355 CFRelease(si
->hashAgilityV2AttrValues
);
357 /* XXX storage ??? */
361 * SecCmsSignerInfoSign - sign something
365 SecCmsSignerInfoSign(SecCmsSignerInfoRef signerinfo
, CSSM_DATA_PTR digest
, CSSM_DATA_PTR contentType
)
367 SecCertificateRef cert
;
368 SecPrivateKeyRef privkey
= NULL
;
369 SECOidTag digestalgtag
;
370 SECOidTag pubkAlgTag
;
371 CSSM_DATA signature
= { 0 };
373 PLArenaPool
*poolp
, *tmppoolp
= NULL
;
374 const SECAlgorithmID
*algID
;
375 SECAlgorithmID freeAlgID
;
376 //CERTSubjectPublicKeyInfo *spki;
378 PORT_Assert (digest
!= NULL
);
380 poolp
= signerinfo
->cmsg
->poolp
;
382 switch (signerinfo
->signerIdentifier
.identifierType
) {
383 case SecCmsSignerIDIssuerSN
:
384 privkey
= signerinfo
->signingKey
;
385 signerinfo
->signingKey
= NULL
;
386 cert
= signerinfo
->cert
;
387 if (SecCertificateGetAlgorithmID(cert
,&algID
)) {
388 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM
);
392 case SecCmsSignerIDSubjectKeyID
:
393 privkey
= signerinfo
->signingKey
;
394 signerinfo
->signingKey
= NULL
;
396 spki
= SECKEY_CreateSubjectPublicKeyInfo(signerinfo
->pubKey
);
397 SECKEY_DestroyPublicKey(signerinfo
->pubKey
);
398 signerinfo
->pubKey
= NULL
;
399 SECOID_CopyAlgorithmID(NULL
, &freeAlgID
, &spki
->algorithm
);
400 SECKEY_DestroySubjectPublicKeyInfo(spki
);
405 if (SecKeyGetAlgorithmID(signerinfo
->pubKey
,&algID
)) {
407 /* TBD: Unify this code. Currently, iOS has an incompatible
408 * SecKeyGetAlgorithmID implementation. */
411 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM
);
414 CFRelease(signerinfo
->pubKey
);
415 signerinfo
->pubKey
= NULL
;
419 PORT_SetError(SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE
);
422 digestalgtag
= SecCmsSignerInfoGetDigestAlgTag(signerinfo
);
424 * XXX I think there should be a cert-level interface for this,
425 * so that I do not have to know about subjectPublicKeyInfo...
427 pubkAlgTag
= SECOID_GetAlgorithmTag(algID
);
428 if (signerinfo
->signerIdentifier
.identifierType
== SecCmsSignerIDSubjectKeyID
) {
429 SECOID_DestroyAlgorithmID(&freeAlgID
, PR_FALSE
);
434 /* Fortezza MISSI have weird signature formats.
435 * Map them to standard DSA formats
437 pubkAlgTag
= PK11_FortezzaMapSig(pubkAlgTag
);
440 if (signerinfo
->authAttr
!= NULL
) {
441 CSSM_DATA encoded_attrs
;
443 /* find and fill in the message digest attribute. */
444 rv
= SecCmsAttributeArraySetAttr(poolp
, &(signerinfo
->authAttr
),
445 SEC_OID_PKCS9_MESSAGE_DIGEST
, digest
, PR_FALSE
);
446 if (rv
!= SECSuccess
)
449 if (contentType
!= NULL
) {
450 /* if the caller wants us to, find and fill in the content type attribute. */
451 rv
= SecCmsAttributeArraySetAttr(poolp
, &(signerinfo
->authAttr
),
452 SEC_OID_PKCS9_CONTENT_TYPE
, contentType
, PR_FALSE
);
453 if (rv
!= SECSuccess
)
457 if ((tmppoolp
= PORT_NewArena (1024)) == NULL
) {
458 PORT_SetError(SEC_ERROR_NO_MEMORY
);
463 * Before encoding, reorder the attributes so that when they
464 * are encoded, they will be conforming DER, which is required
465 * to have a specific order and that is what must be used for
466 * the hash/signature. We do this here, rather than building
467 * it into EncodeAttributes, because we do not want to do
468 * such reordering on incoming messages (which also uses
469 * EncodeAttributes) or our old signatures (and other "broken"
470 * implementations) will not verify. So, we want to guarantee
471 * that we send out good DER encodings of attributes, but not
472 * to expect to receive them.
474 if (SecCmsAttributeArrayReorder(signerinfo
->authAttr
) != SECSuccess
)
477 encoded_attrs
.Data
= NULL
;
478 encoded_attrs
.Length
= 0;
479 if (SecCmsAttributeArrayEncode(tmppoolp
, &(signerinfo
->authAttr
),
480 &encoded_attrs
) == NULL
)
483 rv
= SEC_SignData(&signature
, encoded_attrs
.Data
, (int)encoded_attrs
.Length
,
484 privkey
, digestalgtag
, pubkAlgTag
);
485 PORT_FreeArena(tmppoolp
, PR_FALSE
); /* awkward memory management :-( */
488 rv
= SGN_Digest(privkey
, digestalgtag
, pubkAlgTag
, &signature
, digest
);
490 SECKEY_DestroyPrivateKey(privkey
);
493 if (rv
!= SECSuccess
)
496 if (SECITEM_CopyItem(poolp
, &(signerinfo
->encDigest
), &signature
)
500 SECITEM_FreeItem(&signature
, PR_FALSE
);
502 if(pubkAlgTag
== SEC_OID_EC_PUBLIC_KEY
) {
504 * RFC 3278 section section 2.1.1 states that the signatureAlgorithm
505 * field contains the full ecdsa-with-SHA1 OID, not plain old ecPublicKey
506 * as would appear in other forms of signed datas. However Microsoft doesn't
507 * do this, it puts ecPublicKey there, and if we put ecdsa-with-SHA1 there,
508 * MS can't verify - presumably because it takes the digest of the digest
509 * before feeding it to ECDSA.
510 * We handle this with a preference; default if it's not there is
511 * "Microsoft compatibility mode".
513 if(!SecCmsMsEcdsaCompatMode()) {
514 pubkAlgTag
= SEC_OID_ECDSA_WithSHA1
;
516 /* else violating the spec for compatibility */
519 if (SECOID_SetAlgorithmID(poolp
, &(signerinfo
->digestEncAlg
), pubkAlgTag
,
526 if (signature
.Length
!= 0)
527 SECITEM_FreeItem (&signature
, PR_FALSE
);
529 SECKEY_DestroyPrivateKey(privkey
);
531 PORT_FreeArena(tmppoolp
, PR_FALSE
);
536 SecCmsSignerInfoVerifyCertificate(SecCmsSignerInfoRef signerinfo
, SecKeychainRef keychainOrArray
,
537 CFTypeRef policies
, SecTrustRef
*trustRef
)
539 SecCertificateRef cert
;
540 CFAbsoluteTime stime
;
542 CSSM_DATA_PTR
*otherCerts
;
544 if ((cert
= SecCmsSignerInfoGetSigningCertificate(signerinfo
, keychainOrArray
)) == NULL
) {
545 dprintf("SecCmsSignerInfoVerifyCertificate: no signing cert\n");
546 signerinfo
->verificationStatus
= SecCmsVSSigningCertNotFound
;
551 * Get and convert the signing time; if available, it will be used
552 * both on the cert verification and for importing the sender
555 CFTypeRef timeStampPolicies
=SecPolicyCreateAppleTimeStampingAndRevocationPolicies(policies
);
556 if (SecCmsSignerInfoGetTimestampTimeWithPolicy(signerinfo
, timeStampPolicies
, &stime
) != SECSuccess
)
557 if (SecCmsSignerInfoGetSigningTime(signerinfo
, &stime
) != SECSuccess
)
558 stime
= CFAbsoluteTimeGetCurrent();
559 CFReleaseSafe(timeStampPolicies
);
561 rv
= SecCmsSignedDataRawCerts(signerinfo
->sigd
, &otherCerts
);
565 rv
= CERT_VerifyCert(keychainOrArray
, cert
, otherCerts
, policies
, stime
, trustRef
);
566 dprintfRC("SecCmsSignerInfoVerifyCertificate after vfy: certp %p cert.rc %d\n",
567 cert
, (int)CFGetRetainCount(cert
));
570 if (PORT_GetError() == SEC_ERROR_UNTRUSTED_CERT
)
572 /* Signature or digest level verificationStatus errors should supercede certificate level errors, so only change the verificationStatus if the status was GoodSignature. */
573 if (signerinfo
->verificationStatus
== SecCmsVSGoodSignature
)
574 signerinfo
->verificationStatus
= SecCmsVSSigningCertNotTrusted
;
577 /* FIXME isn't this leaking the cert? */
578 dprintf("SecCmsSignerInfoVerifyCertificate: CertVerify rtn %d\n", (int)rv
);
582 static void debugShowSigningCertificate(SecCmsSignerInfoRef signerinfo
)
585 CFStringRef cn
= SecCmsSignerInfoGetSignerCommonName(signerinfo
);
588 char *ccn
= cfStringToChar(cn
);
591 dprintf("SecCmsSignerInfoVerify: cn: %s\n", ccn
);
600 * SecCmsSignerInfoVerify - verify the signature of a single SignerInfo
602 * Just verifies the signature. The assumption is that verification of the certificate
606 SecCmsSignerInfoVerify(SecCmsSignerInfoRef signerinfo
, CSSM_DATA_PTR digest
, CSSM_DATA_PTR contentType
)
608 return SecCmsSignerInfoVerifyWithPolicy(signerinfo
,NULL
, digest
,contentType
);
612 SecCmsSignerInfoVerifyWithPolicy(SecCmsSignerInfoRef signerinfo
,CFTypeRef timeStampPolicy
, CSSM_DATA_PTR digest
, CSSM_DATA_PTR contentType
)
614 SecPublicKeyRef publickey
= NULL
;
615 SecCmsAttribute
*attr
= NULL
;
616 CSSM_DATA encoded_attrs
;
617 SecCertificateRef cert
= NULL
;
618 SecCmsVerificationStatus vs
= SecCmsVSUnverified
;
619 PLArenaPool
*poolp
= NULL
;
620 SECOidTag digestAlgTag
, digestEncAlgTag
;
622 if (signerinfo
== NULL
)
625 /* SecCmsSignerInfoGetSigningCertificate will fail if 2nd parm is NULL and */
626 /* cert has not been verified */
627 if ((cert
= SecCmsSignerInfoGetSigningCertificate(signerinfo
, NULL
)) == NULL
) {
628 dprintf("SecCmsSignerInfoVerify: no signing cert\n");
629 vs
= SecCmsVSSigningCertNotFound
;
633 dprintfRC("SecCmsSignerInfoVerify top: cert %p cert.rc %d\n", cert
, (int)CFGetRetainCount(cert
));
635 debugShowSigningCertificate(signerinfo
);
637 if (NULL
== (publickey
= SecCertificateCopyKey(cert
))) {
638 vs
= SecCmsVSProcessingError
;
642 digestAlgTag
= SECOID_GetAlgorithmTag(&(signerinfo
->digestAlg
));
643 digestEncAlgTag
= SECOID_GetAlgorithmTag(&(signerinfo
->digestEncAlg
));
646 * Gross hack necessitated by RFC 3278 section 2.1.1, which states
647 * that the signature algorithm (here, digestEncAlg) contains ecdsa_with-SHA1,
648 * *not* (as in all other algorithms) the raw signature algorithm, e.g.
649 * pkcs1RSAEncryption.
651 if(digestEncAlgTag
== SEC_OID_ECDSA_WithSHA1
) {
652 digestEncAlgTag
= SEC_OID_EC_PUBLIC_KEY
;
655 if (!SecCmsArrayIsEmpty((void **)signerinfo
->authAttr
)) {
660 * RFC2630 sez that if there are any authenticated attributes,
661 * then there must be one for content type which matches the
662 * content type of the content being signed, and there must
663 * be one for message digest which matches our message digest.
664 * So check these things first.
666 if ((attr
= SecCmsAttributeArrayFindAttrByOidTag(signerinfo
->authAttr
,
667 SEC_OID_PKCS9_CONTENT_TYPE
, PR_TRUE
)) == NULL
)
669 vs
= SecCmsVSMalformedSignature
;
673 if (SecCmsAttributeCompareValue(attr
, contentType
) == PR_FALSE
) {
674 vs
= SecCmsVSMalformedSignature
;
682 if ((attr
= SecCmsAttributeArrayFindAttrByOidTag(signerinfo
->authAttr
, SEC_OID_PKCS9_MESSAGE_DIGEST
, PR_TRUE
)) == NULL
)
684 vs
= SecCmsVSMalformedSignature
;
687 if (SecCmsAttributeCompareValue(attr
, digest
) == PR_FALSE
) {
688 vs
= SecCmsVSDigestMismatch
;
692 if ((poolp
= PORT_NewArena (1024)) == NULL
) {
693 vs
= SecCmsVSProcessingError
;
700 * The signature is based on a digest of the DER-encoded authenticated
701 * attributes. So, first we encode and then we digest/verify.
702 * we trust the decoder to have the attributes in the right (sorted) order
704 encoded_attrs
.Data
= NULL
;
705 encoded_attrs
.Length
= 0;
707 if (SecCmsAttributeArrayEncode(poolp
, &(signerinfo
->authAttr
), &encoded_attrs
) == NULL
||
708 encoded_attrs
.Data
== NULL
|| encoded_attrs
.Length
== 0)
710 vs
= SecCmsVSProcessingError
;
714 vs
= (VFY_VerifyData (encoded_attrs
.Data
, (int)encoded_attrs
.Length
,
715 publickey
, &(signerinfo
->encDigest
),
716 digestAlgTag
, digestEncAlgTag
,
717 signerinfo
->cmsg
->pwfn_arg
) != SECSuccess
) ? SecCmsVSBadSignature
: SecCmsVSGoodSignature
;
719 dprintf("VFY_VerifyData (authenticated attributes): %s\n",
720 (vs
== SecCmsVSGoodSignature
)?"SecCmsVSGoodSignature":"SecCmsVSBadSignature");
722 PORT_FreeArena(poolp
, PR_FALSE
); /* awkward memory management :-( */
727 /* No authenticated attributes. The signature is based on the plain message digest. */
728 sig
= &(signerinfo
->encDigest
);
729 if (sig
->Length
== 0)
732 vs
= (VFY_VerifyDigest(digest
, publickey
, sig
,
733 digestAlgTag
, digestEncAlgTag
,
734 signerinfo
->cmsg
->pwfn_arg
) != SECSuccess
) ? SecCmsVSBadSignature
: SecCmsVSGoodSignature
;
736 dprintf("VFY_VerifyData (plain message digest): %s\n",
737 (vs
== SecCmsVSGoodSignature
)?"SecCmsVSGoodSignature":"SecCmsVSBadSignature");
740 if (!SecCmsArrayIsEmpty((void **)signerinfo
->unAuthAttr
))
742 dprintf("found an unAuthAttr\n");
743 OSStatus rux
= SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(signerinfo
,timeStampPolicy
);
744 dprintf("SecCmsSignerInfoVerifyUnAuthAttrs Status: %ld\n", (long)rux
);
750 if (vs
== SecCmsVSBadSignature
) {
752 * XXX Change the generic error into our specific one, because
753 * in that case we get a better explanation out of the Security
754 * Advisor. This is really a bug in our error strings (the
755 * "generic" error has a lousy/wrong message associated with it
756 * which assumes the signature verification was done for the
757 * purposes of checking the issuer signature on a certificate)
758 * but this is at least an easy workaround and/or in the
759 * Security Advisor, which specifically checks for the error
760 * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation
761 * in that case but does not similarly check for
762 * SEC_ERROR_BAD_SIGNATURE. It probably should, but then would
763 * probably say the wrong thing in the case that it *was* the
764 * certificate signature check that failed during the cert
765 * verification done above. Our error handling is really a mess.
767 if (PORT_GetError() == SEC_ERROR_BAD_SIGNATURE
)
768 PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE
);
771 if (publickey
!= NULL
)
772 CFRelease(publickey
);
774 signerinfo
->verificationStatus
= vs
;
775 dprintfRC("SecCmsSignerInfoVerify end: cerp %p cert.rc %d\n",
776 cert
, (int)CFGetRetainCount(cert
));
778 dprintf("verificationStatus: %d\n", vs
);
780 return (vs
== SecCmsVSGoodSignature
) ? SECSuccess
: SECFailure
;
783 if (publickey
!= NULL
)
784 SECKEY_DestroyPublicKey (publickey
);
786 dprintf("verificationStatus2: %d\n", vs
);
787 signerinfo
->verificationStatus
= vs
;
789 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE
);
794 SecCmsSignerInfoVerifyUnAuthAttrs(SecCmsSignerInfoRef signerinfo
) {
795 return SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(signerinfo
, NULL
);
799 SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(SecCmsSignerInfoRef signerinfo
,CFTypeRef timeStampPolicy
)
802 unAuthAttr is an array of attributes; we expect to
803 see just one: the timestamp blob. If we have an unAuthAttr,
804 but don't see a timestamp, return an error since we have
805 no other cases where this would be present.
808 SecCmsAttribute
*attr
= NULL
;
809 OSStatus status
= SECFailure
;
811 require(signerinfo
, xit
);
812 attr
= SecCmsAttributeArrayFindAttrByOidTag(signerinfo
->unAuthAttr
,
813 SEC_OID_PKCS9_TIMESTAMP_TOKEN
, PR_TRUE
);
816 status
= errSecTimestampMissing
;
820 dprintf("found an id-ct-TSTInfo\n");
821 // Don't check the nonce in this case
822 status
= decodeTimeStampTokenWithPolicy(signerinfo
, timeStampPolicy
, (attr
->values
)[0], &signerinfo
->encDigest
, 0);
823 if (status
!= errSecSuccess
) {
824 secerror("timestamp verification failed: %d", (int)status
);
832 SecCmsSignerInfoGetEncDigest(SecCmsSignerInfoRef signerinfo
)
834 return &signerinfo
->encDigest
;
837 SecCmsVerificationStatus
838 SecCmsSignerInfoGetVerificationStatus(SecCmsSignerInfoRef signerinfo
)
840 return signerinfo
->verificationStatus
;
844 SecCmsSignerInfoGetDigestAlg(SecCmsSignerInfoRef signerinfo
)
846 return SECOID_FindOID (&(signerinfo
->digestAlg
.algorithm
));
850 SecCmsSignerInfoGetDigestAlgTag(SecCmsSignerInfoRef signerinfo
)
854 algdata
= SECOID_FindOID (&(signerinfo
->digestAlg
.algorithm
));
856 return algdata
->offset
;
858 return SEC_OID_UNKNOWN
;
862 SecCmsSignerInfoGetCertList(SecCmsSignerInfoRef signerinfo
)
864 dprintfRC("SecCmsSignerInfoGetCertList: certList.rc %d\n",
865 (int)CFGetRetainCount(signerinfo
->certList
));
866 return signerinfo
->certList
;
870 SecCmsSignerInfoGetTimestampCertList(SecCmsSignerInfoRef signerinfo
)
872 dprintfRC("SecCmsSignerInfoGetTimestampCertList: timestampCertList.rc %d\n",
873 (int)CFGetRetainCount(signerinfo
->timestampCertList
));
874 return signerinfo
->timestampCertList
;
878 SecCmsSignerInfoGetTimestampSigningCert(SecCmsSignerInfoRef signerinfo
)
880 dprintfRC("SecCmsSignerInfoGetTimestampSigningCert: timestampCert.rc %d\n",
881 (int)CFGetRetainCount(signerinfo
->timestampCert
));
882 return signerinfo
->timestampCert
;
886 SecCmsSignerInfoGetVersion(SecCmsSignerInfoRef signerinfo
)
888 unsigned long version
;
890 /* always take apart the CSSM_DATA */
891 if (SEC_ASN1DecodeInteger(&(signerinfo
->version
), &version
) != SECSuccess
)
898 * SecCmsSignerInfoGetSigningTime - return the signing time,
899 * in UTCTime format, of a CMS signerInfo.
901 * sinfo - signerInfo data for this signer
903 * Returns a pointer to XXXX (what?)
904 * A return value of NULL is an error.
907 SecCmsSignerInfoGetSigningTime(SecCmsSignerInfoRef sinfo
, CFAbsoluteTime
*stime
)
909 SecCmsAttribute
*attr
;
915 if (sinfo
->signingTime
!= 0) {
916 *stime
= sinfo
->signingTime
; /* cached copy */
920 attr
= SecCmsAttributeArrayFindAttrByOidTag(sinfo
->authAttr
, SEC_OID_PKCS9_SIGNING_TIME
, PR_TRUE
);
921 /* XXXX multi-valued attributes NIH */
922 if (attr
== NULL
|| (value
= SecCmsAttributeGetValue(attr
)) == NULL
)
923 return errSecSigningTimeMissing
;
924 if (DER_UTCTimeToCFDate(value
, stime
) != SECSuccess
)
925 return errSecSigningTimeMissing
;
926 sinfo
->signingTime
= *stime
; /* make cached copy */
931 SecCmsSignerInfoGetTimestampTime(SecCmsSignerInfoRef sinfo
, CFAbsoluteTime
*stime
)
933 return SecCmsSignerInfoGetTimestampTimeWithPolicy(sinfo
, NULL
, stime
);
937 SecCmsSignerInfoGetTimestampTimeWithPolicy(SecCmsSignerInfoRef sinfo
, CFTypeRef timeStampPolicy
, CFAbsoluteTime
*stime
)
939 OSStatus status
= paramErr
;
941 require(sinfo
&& stime
, xit
);
943 if (sinfo
->timestampTime
!= 0)
945 *stime
= sinfo
->timestampTime
; /* cached copy */
949 // A bit heavyweight if haven't already called verify
950 status
= SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(sinfo
,timeStampPolicy
);
951 *stime
= sinfo
->timestampTime
;
958 @abstract Return the data in the signed Codesigning Hash Agility attribute.
959 @param sinfo SignerInfo data for this signer, pointer to a CFDataRef for attribute value
960 @discussion Returns a CFDataRef containing the value of the attribute
961 @result A return value of errSecInternal is an error trying to look up the oid.
962 A status value of success with null result data indicates the attribute was not present.
965 SecCmsSignerInfoGetAppleCodesigningHashAgility(SecCmsSignerInfoRef sinfo
, CFDataRef
*sdata
)
967 SecCmsAttribute
*attr
;
970 if (sinfo
== NULL
|| sdata
== NULL
)
975 if (sinfo
->hashAgilityAttrValue
!= NULL
) {
976 *sdata
= sinfo
->hashAgilityAttrValue
; /* cached copy */
980 attr
= SecCmsAttributeArrayFindAttrByOidTag(sinfo
->authAttr
, SEC_OID_APPLE_HASH_AGILITY
, PR_TRUE
);
982 /* attribute not found */
983 if (attr
== NULL
|| (value
= SecCmsAttributeGetValue(attr
)) == NULL
)
986 sinfo
->hashAgilityAttrValue
= CFDataCreate(NULL
, value
->Data
, value
->Length
); /* make cached copy */
987 if (sinfo
->hashAgilityAttrValue
) {
988 *sdata
= sinfo
->hashAgilityAttrValue
;
991 return errSecAllocate
;
994 /* AgileHash ::= SEQUENCE {
995 hashType OBJECT IDENTIFIER,
996 hashValues OCTET STRING }
999 SecAsn1Item digestOID
;
1000 SecAsn1Item digestValue
;
1001 } CMSAppleAgileHash
;
1003 static const SecAsn1Template CMSAppleAgileHashTemplate
[] = {
1004 { SEC_ASN1_SEQUENCE
,
1005 0, NULL
, sizeof(CMSAppleAgileHash
) },
1006 { SEC_ASN1_OBJECT_ID
,
1007 offsetof(CMSAppleAgileHash
, digestOID
), },
1008 { SEC_ASN1_OCTET_STRING
,
1009 offsetof(CMSAppleAgileHash
, digestValue
), },
1013 static OSStatus
CMSAddAgileHashToDictionary(CFMutableDictionaryRef dictionary
, SecAsn1Item
*DERAgileHash
) {
1014 PLArenaPool
*tmppoolp
= NULL
;
1015 OSStatus status
= errSecSuccess
;
1016 CMSAppleAgileHash agileHash
;
1017 CFDataRef digestValue
= NULL
;
1018 CFNumberRef digestTag
= NULL
;
1020 tmppoolp
= PORT_NewArena(1024);
1021 if (tmppoolp
== NULL
) {
1022 return errSecAllocate
;
1025 if ((status
= SEC_ASN1DecodeItem(tmppoolp
, &agileHash
, CMSAppleAgileHashTemplate
, DERAgileHash
)) != errSecSuccess
) {
1029 int64_t tag
= SECOID_FindOIDTag(&agileHash
.digestOID
);
1030 digestTag
= CFNumberCreate(NULL
, kCFNumberSInt64Type
, &tag
);
1031 digestValue
= CFDataCreate(NULL
, agileHash
.digestValue
.Data
, agileHash
.digestValue
.Length
);
1032 CFDictionaryAddValue(dictionary
, digestTag
, digestValue
);
1035 CFReleaseNull(digestValue
);
1036 CFReleaseNull(digestTag
);
1038 PORT_FreeArena(tmppoolp
, PR_FALSE
);
1045 @abstract Return the data in the signed Codesigning Hash Agility V2 attribute.
1046 @param sinfo SignerInfo data for this signer, pointer to a CFDictionaryRef for attribute values
1047 @discussion Returns a CFDictionaryRef containing the values of the attribute
1048 @result A return value of errSecInternal is an error trying to look up the oid.
1049 A status value of success with null result data indicates the attribute was not present.
1052 SecCmsSignerInfoGetAppleCodesigningHashAgilityV2(SecCmsSignerInfoRef sinfo
, CFDictionaryRef
*sdict
)
1054 SecCmsAttribute
*attr
;
1056 if (sinfo
== NULL
|| sdict
== NULL
) {
1062 if (sinfo
->hashAgilityV2AttrValues
!= NULL
) {
1063 *sdict
= sinfo
->hashAgilityV2AttrValues
; /* cached copy */
1067 attr
= SecCmsAttributeArrayFindAttrByOidTag(sinfo
->authAttr
, SEC_OID_APPLE_HASH_AGILITY_V2
, PR_TRUE
);
1069 /* attribute not found */
1074 /* attrValues SET OF AttributeValue
1075 * AttributeValue ::= ANY
1077 CSSM_DATA_PTR
*values
= attr
->values
;
1078 if (values
== NULL
) { /* There must be values */
1079 return errSecDecode
;
1082 CFMutableDictionaryRef agileHashValues
= CFDictionaryCreateMutable(NULL
, SecCmsArrayCount((void **)values
),
1083 &kCFTypeDictionaryKeyCallBacks
,
1084 &kCFTypeDictionaryValueCallBacks
);
1085 while (*values
!= NULL
) {
1086 (void)CMSAddAgileHashToDictionary(agileHashValues
, *values
++);
1088 if (CFDictionaryGetCount(agileHashValues
) != SecCmsArrayCount((void **)attr
->values
)) {
1089 CFReleaseNull(agileHashValues
);
1090 return errSecDecode
;
1093 sinfo
->hashAgilityV2AttrValues
= agileHashValues
; /* make cached copy */
1094 if (sinfo
->hashAgilityV2AttrValues
) {
1095 *sdict
= sinfo
->hashAgilityV2AttrValues
;
1098 return errSecAllocate
;
1102 * SecCmsSignerInfoGetAppleExpirationTime - return the expiration time,
1103 * in UTCTime format, of a CMS signerInfo.
1105 * sinfo - signerInfo data for this signer
1107 * Returns a pointer to XXXX (what?)
1108 * A return value of NULL is an error.
1111 SecCmsSignerInfoGetAppleExpirationTime(SecCmsSignerInfoRef sinfo
, CFAbsoluteTime
*etime
)
1113 SecCmsAttribute
*attr
= NULL
;
1114 SecAsn1Item
* value
= NULL
;
1116 if (sinfo
== NULL
|| etime
== NULL
) {
1120 if (sinfo
->expirationTime
!= 0) {
1121 *etime
= sinfo
->expirationTime
; /* cached copy */
1125 attr
= SecCmsAttributeArrayFindAttrByOidTag(sinfo
->authAttr
, SEC_OID_APPLE_EXPIRATION_TIME
, PR_TRUE
);
1126 if (attr
== NULL
|| (value
= SecCmsAttributeGetValue(attr
)) == NULL
) {
1129 if (DER_UTCTimeToCFDate(value
, etime
) != SECSuccess
) {
1132 sinfo
->expirationTime
= *etime
; /* make cached copy */
1137 * Return the signing cert of a CMS signerInfo.
1139 * the certs in the enclosing SignedData must have been imported already
1142 SecCmsSignerInfoGetSigningCertificate(SecCmsSignerInfoRef signerinfo
, SecKeychainRef keychainOrArray
)
1144 SecCertificateRef cert
;
1145 SecCmsSignerIdentifier
*sid
;
1147 CSSM_DATA_PTR
*rawCerts
;
1149 if (signerinfo
->cert
!= NULL
) {
1150 dprintfRC("SecCmsSignerInfoGetSigningCertificate top: cert %p cert.rc %d\n",
1151 signerinfo
->cert
, (int)CFGetRetainCount(signerinfo
->cert
));
1152 return signerinfo
->cert
;
1154 ortn
= SecCmsSignedDataRawCerts(signerinfo
->sigd
, &rawCerts
);
1158 dprintf("SecCmsSignerInfoGetSigningCertificate: numRawCerts %d\n",
1159 SecCmsArrayCount((void **)rawCerts
));
1162 * This cert will also need to be freed, but since we save it
1163 * in signerinfo for later, we do not want to destroy it when
1164 * we leave this function -- we let the clean-up of the entire
1165 * cinfo structure later do the destroy of this cert.
1167 sid
= &signerinfo
->signerIdentifier
;
1168 switch (sid
->identifierType
) {
1169 case SecCmsSignerIDIssuerSN
:
1170 cert
= CERT_FindCertByIssuerAndSN(keychainOrArray
, rawCerts
, signerinfo
->sigd
->certs
, signerinfo
->cmsg
->poolp
,
1171 sid
->id
.issuerAndSN
);
1173 case SecCmsSignerIDSubjectKeyID
:
1174 cert
= CERT_FindCertBySubjectKeyID(keychainOrArray
, rawCerts
, signerinfo
->sigd
->certs
, sid
->id
.subjectKeyID
);
1181 /* cert can be NULL at that point */
1182 signerinfo
->cert
= cert
; /* earmark it */
1183 dprintfRC("SecCmsSignerInfoGetSigningCertificate end: certp %p cert.rc %d\n",
1184 signerinfo
->cert
, (int)CFGetRetainCount(signerinfo
->cert
));
1190 * SecCmsSignerInfoGetSignerCommonName - return the common name of the signer
1192 * sinfo - signerInfo data for this signer
1194 * Returns a CFStringRef containing the common name of the signer.
1195 * A return value of NULL is an error.
1198 SecCmsSignerInfoGetSignerCommonName(SecCmsSignerInfoRef sinfo
)
1200 SecCertificateRef signercert
;
1201 CFStringRef commonName
= NULL
;
1203 /* will fail if cert is not verified */
1204 if ((signercert
= SecCmsSignerInfoGetSigningCertificate(sinfo
, NULL
)) == NULL
)
1207 if (errSecSuccess
!= SecCertificateCopyCommonName(signercert
, &commonName
)) {
1215 * SecCmsSignerInfoGetSignerEmailAddress - return the email address of the signer
1217 * sinfo - signerInfo data for this signer
1219 * Returns a CFStringRef containing the name of the signer.
1220 * A return value of NULL is an error.
1223 SecCmsSignerInfoGetSignerEmailAddress(SecCmsSignerInfoRef sinfo
)
1225 SecCertificateRef signercert
;
1226 CFStringRef emailAddress
= NULL
;
1228 if ((signercert
= SecCmsSignerInfoGetSigningCertificate(sinfo
, NULL
)) == NULL
)
1231 SecCertificateGetEmailAddress(signercert
, &emailAddress
);
1233 return CFRetainSafe(emailAddress
);
1238 * SecCmsSignerInfoAddAuthAttr - add an attribute to the
1239 * authenticated (i.e. signed) attributes of "signerinfo".
1242 SecCmsSignerInfoAddAuthAttr(SecCmsSignerInfoRef signerinfo
, SecCmsAttribute
*attr
)
1244 return SecCmsAttributeArrayAddAttr(signerinfo
->cmsg
->poolp
, &(signerinfo
->authAttr
), attr
);
1248 * SecCmsSignerInfoAddUnauthAttr - add an attribute to the
1249 * unauthenticated attributes of "signerinfo".
1252 SecCmsSignerInfoAddUnauthAttr(SecCmsSignerInfoRef signerinfo
, SecCmsAttribute
*attr
)
1254 return SecCmsAttributeArrayAddAttr(signerinfo
->cmsg
->poolp
, &(signerinfo
->unAuthAttr
), attr
);
1258 * SecCmsSignerInfoAddSigningTime - add the signing time to the
1259 * authenticated (i.e. signed) attributes of "signerinfo".
1261 * This is expected to be included in outgoing signed
1262 * messages for email (S/MIME) but is likely useful in other situations.
1264 * This should only be added once; a second call will do nothing.
1266 * XXX This will probably just shove the current time into "signerinfo"
1267 * but it will not actually get signed until the entire item is
1268 * processed for encoding. Is this (expected to be small) delay okay?
1271 SecCmsSignerInfoAddSigningTime(SecCmsSignerInfoRef signerinfo
, CFAbsoluteTime t
)
1273 SecCmsAttribute
*attr
;
1278 poolp
= signerinfo
->cmsg
->poolp
;
1280 mark
= PORT_ArenaMark(poolp
);
1282 /* create new signing time attribute */
1283 if (DER_CFDateToUTCTime(t
, &stime
) != SECSuccess
)
1286 if ((attr
= SecCmsAttributeCreate(poolp
, SEC_OID_PKCS9_SIGNING_TIME
, &stime
, PR_FALSE
)) == NULL
) {
1287 SECITEM_FreeItem (&stime
, PR_FALSE
);
1291 SECITEM_FreeItem (&stime
, PR_FALSE
);
1293 if (SecCmsSignerInfoAddAuthAttr(signerinfo
, attr
) != SECSuccess
)
1296 PORT_ArenaUnmark (poolp
, mark
);
1301 PORT_ArenaRelease (poolp
, mark
);
1306 * SecCmsSignerInfoAddSMIMECaps - add a SMIMECapabilities attribute to the
1307 * authenticated (i.e. signed) attributes of "signerinfo".
1309 * This is expected to be included in outgoing signed
1310 * messages for email (S/MIME).
1313 SecCmsSignerInfoAddSMIMECaps(SecCmsSignerInfoRef signerinfo
)
1315 SecCmsAttribute
*attr
;
1316 CSSM_DATA_PTR smimecaps
= NULL
;
1320 poolp
= signerinfo
->cmsg
->poolp
;
1322 mark
= PORT_ArenaMark(poolp
);
1324 smimecaps
= SECITEM_AllocItem(poolp
, NULL
, 0);
1325 if (smimecaps
== NULL
)
1328 /* create new signing time attribute */
1330 // @@@ We don't do Fortezza yet.
1331 if (SecSMIMECreateSMIMECapabilities((SecArenaPoolRef
)poolp
, smimecaps
, PR_FALSE
) != SECSuccess
)
1333 if (SecSMIMECreateSMIMECapabilities(poolp
, smimecaps
,
1334 PK11_FortezzaHasKEA(signerinfo
->cert
)) != SECSuccess
)
1338 if ((attr
= SecCmsAttributeCreate(poolp
, SEC_OID_PKCS9_SMIME_CAPABILITIES
, smimecaps
, PR_TRUE
)) == NULL
)
1341 if (SecCmsSignerInfoAddAuthAttr(signerinfo
, attr
) != SECSuccess
)
1344 PORT_ArenaUnmark (poolp
, mark
);
1348 PORT_ArenaRelease (poolp
, mark
);
1353 * SecCmsSignerInfoAddSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
1354 * authenticated (i.e. signed) attributes of "signerinfo".
1356 * This is expected to be included in outgoing signed messages for email (S/MIME).
1359 SecCmsSignerInfoAddSMIMEEncKeyPrefs(SecCmsSignerInfoRef signerinfo
, SecCertificateRef cert
, SecKeychainRef keychainOrArray
)
1361 SecCmsAttribute
*attr
;
1362 CSSM_DATA_PTR smimeekp
= NULL
;
1369 /* verify this cert for encryption */
1370 policy
= CERT_PolicyForCertUsage(certUsageEmailRecipient
);
1371 if (CERT_VerifyCert(keychainOrArray
, cert
, policy
, CFAbsoluteTimeGetCurrent(), NULL
) != SECSuccess
) {
1378 poolp
= signerinfo
->cmsg
->poolp
;
1379 mark
= PORT_ArenaMark(poolp
);
1381 smimeekp
= SECITEM_AllocItem(poolp
, NULL
, 0);
1382 if (smimeekp
== NULL
)
1385 /* create new signing time attribute */
1386 if (SecSMIMECreateSMIMEEncKeyPrefs((SecArenaPoolRef
)poolp
, smimeekp
, cert
) != SECSuccess
)
1389 if ((attr
= SecCmsAttributeCreate(poolp
, SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE
, smimeekp
, PR_TRUE
)) == NULL
)
1392 if (SecCmsSignerInfoAddAuthAttr(signerinfo
, attr
) != SECSuccess
)
1395 PORT_ArenaUnmark (poolp
, mark
);
1399 PORT_ArenaRelease (poolp
, mark
);
1404 * SecCmsSignerInfoAddMSSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
1405 * authenticated (i.e. signed) attributes of "signerinfo", using the OID preferred by Microsoft.
1407 * This is expected to be included in outgoing signed messages for email (S/MIME),
1408 * if compatibility with Microsoft mail clients is wanted.
1411 SecCmsSignerInfoAddMSSMIMEEncKeyPrefs(SecCmsSignerInfoRef signerinfo
, SecCertificateRef cert
, SecKeychainRef keychainOrArray
)
1413 SecCmsAttribute
*attr
;
1414 CSSM_DATA_PTR smimeekp
= NULL
;
1421 /* verify this cert for encryption */
1422 policy
= CERT_PolicyForCertUsage(certUsageEmailRecipient
);
1423 if (CERT_VerifyCert(keychainOrArray
, cert
, policy
, CFAbsoluteTimeGetCurrent(), NULL
) != SECSuccess
) {
1430 poolp
= signerinfo
->cmsg
->poolp
;
1431 mark
= PORT_ArenaMark(poolp
);
1433 smimeekp
= SECITEM_AllocItem(poolp
, NULL
, 0);
1434 if (smimeekp
== NULL
)
1437 /* create new signing time attribute */
1438 if (SecSMIMECreateMSSMIMEEncKeyPrefs((SecArenaPoolRef
)poolp
, smimeekp
, cert
) != SECSuccess
)
1441 if ((attr
= SecCmsAttributeCreate(poolp
, SEC_OID_MS_SMIME_ENCRYPTION_KEY_PREFERENCE
, smimeekp
, PR_TRUE
)) == NULL
)
1444 if (SecCmsSignerInfoAddAuthAttr(signerinfo
, attr
) != SECSuccess
)
1447 PORT_ArenaUnmark (poolp
, mark
);
1451 PORT_ArenaRelease (poolp
, mark
);
1456 * SecCmsSignerInfoAddTimeStamp - add time stamp to the
1457 * unauthenticated (i.e. unsigned) attributes of "signerinfo".
1459 * This will initially be used for time stamping signed applications
1460 * by using a Time Stamping Authority. It may also be included in outgoing signed
1461 * messages for email (S/MIME), and may be useful in other situations.
1463 * This should only be added once; a second call will do nothing.
1468 Countersignature attribute values have ASN.1 type Countersignature:
1469 Countersignature ::= SignerInfo
1470 Countersignature values have the same meaning as SignerInfo values
1471 for ordinary signatures, except that:
1472 1. The signedAttributes field MUST NOT contain a content-type
1473 attribute; there is no content type for countersignatures.
1474 2. The signedAttributes field MUST contain a message-digest
1475 attribute if it contains any other attributes.
1476 3. The input to the message-digesting process is the contents octets
1477 of the DER encoding of the signatureValue field of the SignerInfo
1478 value with which the attribute is associated.
1483 @abstract Create a timestamp unsigned attribute with a TimeStampToken.
1487 SecCmsSignerInfoAddTimeStamp(SecCmsSignerInfoRef signerinfo
, CSSM_DATA
*tstoken
)
1489 SecCmsAttribute
*attr
;
1490 PLArenaPool
*poolp
= signerinfo
->cmsg
->poolp
;
1491 void *mark
= PORT_ArenaMark(poolp
);
1493 // We have already encoded this ourselves, so last param is PR_TRUE
1494 if ((attr
= SecCmsAttributeCreate(poolp
, SEC_OID_PKCS9_TIMESTAMP_TOKEN
, tstoken
, PR_TRUE
)) == NULL
)
1497 if (SecCmsSignerInfoAddUnauthAttr(signerinfo
, attr
) != SECSuccess
)
1500 PORT_ArenaUnmark (poolp
, mark
);
1505 PORT_ArenaRelease (poolp
, mark
);
1510 * SecCmsSignerInfoAddCounterSignature - countersign a signerinfo
1512 * 1. digest the DER-encoded signature value of the original signerinfo
1513 * 2. create new signerinfo with correct version, sid, digestAlg
1514 * 3. add message-digest authAttr, but NO content-type
1515 * 4. sign the authAttrs
1516 * 5. DER-encode the new signerInfo
1517 * 6. add the whole thing to original signerInfo's unAuthAttrs
1518 * as a SEC_OID_PKCS9_COUNTER_SIGNATURE attribute
1520 * XXXX give back the new signerinfo?
1523 SecCmsSignerInfoAddCounterSignature(SecCmsSignerInfoRef signerinfo
,
1524 SECOidTag digestalg
, SecIdentityRef identity
)
1532 @abstract Add the Apple Codesigning Hash Agility attribute to the authenticated (i.e. signed) attributes of "signerinfo".
1533 @discussion This is expected to be included in outgoing Apple code signatures.
1536 SecCmsSignerInfoAddAppleCodesigningHashAgility(SecCmsSignerInfoRef signerinfo
, CFDataRef attrValue
)
1538 SecCmsAttribute
*attr
;
1539 PLArenaPool
*poolp
= signerinfo
->cmsg
->poolp
;
1540 void *mark
= PORT_ArenaMark(poolp
);
1541 OSStatus status
= SECFailure
;
1543 /* The value is required for this attribute. */
1545 status
= errSecParam
;
1550 * SecCmsAttributeCreate makes a copy of the data in value, so
1551 * we don't need to copy into the CSSM_DATA struct.
1554 value
.Length
= CFDataGetLength(attrValue
);
1555 value
.Data
= (uint8_t *)CFDataGetBytePtr(attrValue
);
1557 if ((attr
= SecCmsAttributeCreate(poolp
,
1558 SEC_OID_APPLE_HASH_AGILITY
,
1560 PR_FALSE
)) == NULL
) {
1561 status
= errSecAllocate
;
1565 if (SecCmsSignerInfoAddAuthAttr(signerinfo
, attr
) != SECSuccess
) {
1566 status
= errSecInternalError
;
1570 PORT_ArenaUnmark(poolp
, mark
);
1574 PORT_ArenaRelease(poolp
, mark
);
1578 static OSStatus
CMSAddAgileHashToAttribute(PLArenaPool
*poolp
, SecCmsAttribute
*attr
, CFNumberRef cftag
, CFDataRef value
) {
1579 PLArenaPool
*tmppoolp
= NULL
;
1581 SECOidData
*digestOid
= NULL
;
1582 CMSAppleAgileHash agileHash
;
1583 SecAsn1Item attrValue
= { .Data
= NULL
, .Length
= 0 };
1584 OSStatus status
= errSecSuccess
;
1586 memset(&agileHash
, 0, sizeof(agileHash
));
1588 if(!CFNumberGetValue(cftag
, kCFNumberSInt64Type
, &tag
)) {
1591 digestOid
= SECOID_FindOIDByTag((SECOidTag
)tag
);
1593 agileHash
.digestValue
.Data
= (uint8_t *)CFDataGetBytePtr(value
);
1594 agileHash
.digestValue
.Length
= CFDataGetLength(value
);
1595 agileHash
.digestOID
.Data
= digestOid
->oid
.Data
;
1596 agileHash
.digestOID
.Length
= digestOid
->oid
.Length
;
1598 tmppoolp
= PORT_NewArena(1024);
1599 if (tmppoolp
== NULL
) {
1600 return errSecAllocate
;
1603 if (SEC_ASN1EncodeItem(tmppoolp
, &attrValue
, &agileHash
, CMSAppleAgileHashTemplate
) == NULL
) {
1604 status
= errSecParam
;
1608 status
= SecCmsAttributeAddValue(poolp
, attr
, &attrValue
);
1612 PORT_FreeArena(tmppoolp
, PR_FALSE
);
1619 @abstract Add the Apple Codesigning Hash Agility attribute to the authenticated (i.e. signed) attributes of "signerinfo".
1620 @discussion This is expected to be included in outgoing Apple code signatures.
1623 SecCmsSignerInfoAddAppleCodesigningHashAgilityV2(SecCmsSignerInfoRef signerinfo
, CFDictionaryRef attrValues
)
1625 __block SecCmsAttribute
*attr
;
1626 __block PLArenaPool
*poolp
= signerinfo
->cmsg
->poolp
;
1627 void *mark
= PORT_ArenaMark(poolp
);
1628 OSStatus status
= SECFailure
;
1630 /* The value is required for this attribute. */
1632 status
= errSecParam
;
1636 if ((attr
= SecCmsAttributeCreate(poolp
, SEC_OID_APPLE_HASH_AGILITY_V2
,
1637 NULL
, PR_TRUE
)) == NULL
) {
1638 status
= errSecAllocate
;
1642 CFDictionaryForEach(attrValues
, ^(const void *key
, const void *value
) {
1643 if (!isNumber(key
) || !isData(value
)) {
1646 (void)CMSAddAgileHashToAttribute(poolp
, attr
, (CFNumberRef
)key
, (CFDataRef
)value
);
1649 if (SecCmsSignerInfoAddAuthAttr(signerinfo
, attr
) != SECSuccess
) {
1650 status
= errSecInternal
;
1654 PORT_ArenaUnmark(poolp
, mark
);
1658 PORT_ArenaRelease(poolp
, mark
);
1663 * SecCmsSignerInfoAddAppleExpirationTime - add the expiration time to the
1664 * authenticated (i.e. signed) attributes of "signerinfo".
1666 * This is expected to be included in outgoing signed
1667 * messages for Asset Receipts but is likely useful in other situations.
1669 * This should only be added once; a second call will do nothing.
1672 SecCmsSignerInfoAddAppleExpirationTime(SecCmsSignerInfoRef signerinfo
, CFAbsoluteTime t
)
1674 SecCmsAttribute
*attr
= NULL
;
1675 PLArenaPool
*poolp
= signerinfo
->cmsg
->poolp
;
1676 void *mark
= PORT_ArenaMark(poolp
);
1678 /* create new expiration time attribute */
1680 if (DER_CFDateToUTCTime(t
, &etime
) != SECSuccess
) {
1684 if ((attr
= SecCmsAttributeCreate(poolp
, SEC_OID_APPLE_EXPIRATION_TIME
, &etime
, PR_FALSE
)) == NULL
) {
1685 SECITEM_FreeItem (&etime
, PR_FALSE
);
1689 SECITEM_FreeItem(&etime
, PR_FALSE
);
1691 if (SecCmsSignerInfoAddAuthAttr(signerinfo
, attr
) != SECSuccess
) {
1695 PORT_ArenaUnmark(poolp
, mark
);
1699 PORT_ArenaRelease(poolp
, mark
);
1703 SecCertificateRef
SecCmsSignerInfoCopyCertFromEncryptionKeyPreference(SecCmsSignerInfoRef signerinfo
) {
1704 SecCertificateRef cert
= NULL
;
1705 SecCmsAttribute
*attr
;
1707 SecKeychainRef keychainOrArray
;
1709 (void)SecKeychainCopyDefault(&keychainOrArray
);
1711 /* sanity check - see if verification status is ok (unverified does not count...) */
1712 if (signerinfo
->verificationStatus
!= SecCmsVSGoodSignature
)
1715 /* Prep the raw certs */
1716 CSSM_DATA_PTR
*rawCerts
= NULL
;
1717 if (signerinfo
->sigd
) {
1718 rawCerts
= signerinfo
->sigd
->rawCerts
;
1721 /* find preferred encryption cert */
1722 if (!SecCmsArrayIsEmpty((void **)signerinfo
->authAttr
) &&
1723 (attr
= SecCmsAttributeArrayFindAttrByOidTag(signerinfo
->authAttr
,
1724 SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE
, PR_TRUE
)) != NULL
)
1725 { /* we have a SMIME_ENCRYPTION_KEY_PREFERENCE attribute! Find the cert. */
1726 ekp
= SecCmsAttributeGetValue(attr
);
1729 cert
= SecSMIMEGetCertFromEncryptionKeyPreference(keychainOrArray
, rawCerts
, ekp
);
1731 if(cert
) return cert
;
1733 if (!SecCmsArrayIsEmpty((void **)signerinfo
->authAttr
) &&
1734 (attr
= SecCmsAttributeArrayFindAttrByOidTag(signerinfo
->authAttr
,
1735 SEC_OID_MS_SMIME_ENCRYPTION_KEY_PREFERENCE
, PR_TRUE
)) != NULL
)
1736 { /* we have a MS_SMIME_ENCRYPTION_KEY_PREFERENCE attribute! Find the cert. */
1737 ekp
= SecCmsAttributeGetValue(attr
);
1740 cert
= SecSMIMEGetCertFromEncryptionKeyPreference(keychainOrArray
, rawCerts
, ekp
);
1746 * XXXX the following needs to be done in the S/MIME layer code
1747 * after signature of a signerinfo is verified
1750 SecCmsSignerInfoSaveSMIMEProfile(SecCmsSignerInfoRef signerinfo
)
1752 SecCertificateRef cert
= NULL
;
1753 CSSM_DATA_PTR profile
= NULL
;
1754 SecCmsAttribute
*attr
;
1755 CSSM_DATA_PTR utc_stime
= NULL
;
1759 Boolean must_free_cert
= PR_FALSE
;
1761 SecKeychainRef keychainOrArray
;
1763 status
= SecKeychainCopyDefault(&keychainOrArray
);
1765 /* sanity check - see if verification status is ok (unverified does not count...) */
1766 if (signerinfo
->verificationStatus
!= SecCmsVSGoodSignature
)
1769 /* find preferred encryption cert */
1770 if (!SecCmsArrayIsEmpty((void **)signerinfo
->authAttr
) &&
1771 (attr
= SecCmsAttributeArrayFindAttrByOidTag(signerinfo
->authAttr
,
1772 SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE
, PR_TRUE
)) != NULL
)
1773 { /* we have a SMIME_ENCRYPTION_KEY_PREFERENCE attribute! */
1774 ekp
= SecCmsAttributeGetValue(attr
);
1778 /* we assume that all certs coming with the message have been imported to the */
1779 /* temporary database */
1780 cert
= SecSMIMEGetCertFromEncryptionKeyPreference(keychainOrArray
, NULL
, ekp
);
1783 must_free_cert
= PR_TRUE
;
1787 /* no preferred cert found?
1788 * find the cert the signerinfo is signed with instead */
1789 CFStringRef emailAddress
=NULL
;
1791 cert
= SecCmsSignerInfoGetSigningCertificate(signerinfo
, keychainOrArray
);
1794 if (SecCertificateGetEmailAddress(cert
,&emailAddress
))
1798 /* verify this cert for encryption (has been verified for signing so far) */ /* don't verify this cert for encryption. It may just be a signing cert.
1799 * that's OK, we can still save the S/MIME profile. The encryption cert
1800 * should have already been saved */
1802 if (CERT_VerifyCert(keychainOrArray
, cert
, certUsageEmailRecipient
, CFAbsoluteTimeGetCurrent(), NULL
) != SECSuccess
) {
1804 CERT_DestroyCertificate(cert
);
1809 /* XXX store encryption cert permanently? */
1812 * Remember the current error set because we do not care about
1813 * anything set by the functions we are about to call.
1815 save_error
= PORT_GetError();
1817 if (!SecCmsArrayIsEmpty((void **)signerinfo
->authAttr
)) {
1818 attr
= SecCmsAttributeArrayFindAttrByOidTag(signerinfo
->authAttr
,
1819 SEC_OID_PKCS9_SMIME_CAPABILITIES
,
1821 profile
= SecCmsAttributeGetValue(attr
);
1822 attr
= SecCmsAttributeArrayFindAttrByOidTag(signerinfo
->authAttr
,
1823 SEC_OID_PKCS9_SIGNING_TIME
,
1825 utc_stime
= SecCmsAttributeGetValue(attr
);
1828 rv
= CERT_SaveSMimeProfile (cert
, profile
, utc_stime
);
1830 CERT_DestroyCertificate(cert
);
1833 * Restore the saved error in case the calls above set a new
1834 * one that we do not actually care about.
1836 PORT_SetError (save_error
);
1842 * SecCmsSignerInfoIncludeCerts - set cert chain inclusion mode for this signer
1845 SecCmsSignerInfoIncludeCerts(SecCmsSignerInfoRef signerinfo
, SecCmsCertChainMode cm
, SECCertUsage usage
)
1847 if (signerinfo
->cert
== NULL
) {
1851 /* don't leak if we get called twice */
1852 if (signerinfo
->certList
!= NULL
) {
1853 CFRelease(signerinfo
->certList
);
1854 signerinfo
->certList
= NULL
;
1859 signerinfo
->certList
= NULL
;
1861 case SecCmsCMCertOnly
:
1862 signerinfo
->certList
= CERT_CertListFromCert(signerinfo
->cert
);
1864 case SecCmsCMCertChain
:
1865 signerinfo
->certList
= CERT_CertChainFromCert(signerinfo
->cert
, usage
, PR_FALSE
, PR_FALSE
);
1867 case SecCmsCMCertChainWithRoot
:
1868 signerinfo
->certList
= CERT_CertChainFromCert(signerinfo
->cert
, usage
, PR_TRUE
, PR_FALSE
);
1870 case SecCmsCMCertChainWithRootOrFail
:
1871 signerinfo
->certList
= CERT_CertChainFromCert(signerinfo
->cert
, usage
, PR_TRUE
, PR_TRUE
);
1874 if (cm
!= SecCmsCMNone
&& signerinfo
->certList
== NULL
) {