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>
61 #include <libDER/asn1Types.h>
63 #include "tsaSupport.h"
64 #include "tsaSupportPriv.h"
68 #define HIDIGIT(v) (((v) / 10) + '0')
69 #define LODIGIT(v) (((v) % 10) + '0')
71 #define ISDIGIT(dig) (((dig) >= '0') && ((dig) <= '9'))
72 #define CAPTURE(var,p,label) \
74 if (!ISDIGIT((p)[0]) || !ISDIGIT((p)[1])) goto label; \
75 (var) = ((p)[0] - '0') * 10 + ((p)[1] - '0'); \
79 #define SIGINFO_DEBUG 1
83 #define dprintf(args...) fprintf(stderr, args)
85 #define dprintf(args...)
89 #define dprintfRC(args...) dprintf(args)
91 #define dprintfRC(args...)
95 DER_UTCTimeToCFDate(const CSSM_DATA_PTR utcTime
, CFAbsoluteTime
*date
)
97 CFErrorRef error
= NULL
;
98 /* <rdar://problem/55316705> CMS attributes don't correctly encode/decode times (always use UTCTime) */
99 CFAbsoluteTime result
= SecAbsoluteTimeFromDateContentWithError(ASN1_UTC_TIME
, utcTime
->Data
, utcTime
->Length
, &error
);
101 CFReleaseNull(error
);
112 DER_CFDateToUTCTime(CFAbsoluteTime date
, CSSM_DATA_PTR utcTime
)
116 utcTime
->Length
= 13;
117 utcTime
->Data
= d
= PORT_Alloc(13);
118 if (!utcTime
->Data
) {
122 __block
int year
= 0, month
= 0, day
= 0, hour
= 0, minute
= 0, second
= 0;
124 SecCFCalendarDoWithZuluCalendar(^(CFCalendarRef zuluCalendar
) {
125 result
= CFCalendarDecomposeAbsoluteTime(zuluCalendar
, date
, "yMdHms", &year
, &month
, &day
, &hour
, &minute
, &second
);
131 /* UTC time does not handle the years before 1950 or after 2049 */
132 /* <rdar://problem/55316705> CMS attributes don't correctly encode/decode times (always use UTCTime) */
133 if (year
< 1950 || year
> 2049) {
137 /* remove the century since it's added to the year by the
138 CFAbsoluteTimeGetGregorianDate routine, but is not needed for UTC time */
141 d
[0] = HIDIGIT(year
);
142 d
[1] = LODIGIT(year
);
143 d
[2] = HIDIGIT(month
);
144 d
[3] = LODIGIT(month
);
147 d
[6] = HIDIGIT(hour
);
148 d
[7] = LODIGIT(hour
);
149 d
[8] = HIDIGIT(minute
);
150 d
[9] = LODIGIT(minute
);
151 d
[10] = HIDIGIT(second
);
152 d
[11] = LODIGIT(second
);
157 /* =============================================================================
161 nss_cmssignerinfo_create(SecCmsMessageRef cmsg
, SecCmsSignerIDSelector type
, SecCertificateRef cert
, CSSM_DATA_PTR subjKeyID
, SecPublicKeyRef pubKey
, SecPrivateKeyRef signingKey
, SECOidTag digestalgtag
);
164 SecCmsSignerInfoCreateWithSubjKeyID(SecCmsMessageRef cmsg
, CSSM_DATA_PTR subjKeyID
, SecPublicKeyRef pubKey
, SecPrivateKeyRef signingKey
, SECOidTag digestalgtag
)
166 return nss_cmssignerinfo_create(cmsg
, SecCmsSignerIDSubjectKeyID
, NULL
, subjKeyID
, pubKey
, signingKey
, digestalgtag
);
170 SecCmsSignerInfoCreate(SecCmsMessageRef cmsg
, SecIdentityRef identity
, SECOidTag digestalgtag
)
172 SecCmsSignerInfoRef signerInfo
= NULL
;
173 SecCertificateRef cert
= NULL
;
174 SecPrivateKeyRef signingKey
= NULL
;
175 CFDictionaryRef keyAttrs
= NULL
;
177 if (SecIdentityCopyCertificate(identity
, &cert
))
179 if (SecIdentityCopyPrivateKey(identity
, &signingKey
))
182 /* In some situations, the "Private Key" in the identity is actually a public key. */
183 keyAttrs
= SecKeyCopyAttributes(signingKey
);
186 CFTypeRef
class = CFDictionaryGetValue(keyAttrs
, kSecAttrKeyClass
);
187 if (!class || (CFGetTypeID(class) != CFStringGetTypeID()) || !CFEqual(class, kSecAttrKeyClassPrivate
))
191 signerInfo
= nss_cmssignerinfo_create(cmsg
, SecCmsSignerIDIssuerSN
, cert
, NULL
, NULL
, signingKey
, digestalgtag
);
197 CFRelease(signingKey
);
205 nss_cmssignerinfo_create(SecCmsMessageRef cmsg
, SecCmsSignerIDSelector type
, SecCertificateRef cert
, CSSM_DATA_PTR subjKeyID
, SecPublicKeyRef pubKey
, SecPrivateKeyRef signingKey
, SECOidTag digestalgtag
)
208 SecCmsSignerInfoRef signerinfo
;
214 mark
= PORT_ArenaMark(poolp
);
216 signerinfo
= (SecCmsSignerInfoRef
)PORT_ArenaZAlloc(poolp
, sizeof(SecCmsSignerInfo
));
217 if (signerinfo
== NULL
) {
218 PORT_ArenaRelease(poolp
, mark
);
223 signerinfo
->cmsg
= cmsg
;
226 case SecCmsSignerIDIssuerSN
:
227 signerinfo
->signerIdentifier
.identifierType
= SecCmsSignerIDIssuerSN
;
228 if ((signerinfo
->cert
= CERT_DupCertificate(cert
)) == NULL
)
230 if ((signerinfo
->signerIdentifier
.id
.issuerAndSN
= CERT_GetCertIssuerAndSN(poolp
, cert
)) == NULL
)
232 dprintfRC("nss_cmssignerinfo_create: SecCmsSignerIDIssuerSN: cert.rc %d\n",
233 (int)CFGetRetainCount(signerinfo
->cert
));
235 case SecCmsSignerIDSubjectKeyID
:
236 signerinfo
->signerIdentifier
.identifierType
= SecCmsSignerIDSubjectKeyID
;
237 PORT_Assert(subjKeyID
);
240 signerinfo
->signerIdentifier
.id
.subjectKeyID
= PORT_ArenaNew(poolp
, CSSM_DATA
);
241 if (SECITEM_CopyItem(poolp
, signerinfo
->signerIdentifier
.id
.subjectKeyID
,
245 signerinfo
->pubKey
= SECKEY_CopyPublicKey(pubKey
);
246 if (!signerinfo
->pubKey
)
256 signerinfo
->signingKey
= SECKEY_CopyPrivateKey(signingKey
);
257 if (!signerinfo
->signingKey
)
260 /* set version right now */
261 version
= SEC_CMS_SIGNER_INFO_VERSION_ISSUERSN
;
262 /* RFC2630 5.3 "version is the syntax version number. If the .... " */
263 if (signerinfo
->signerIdentifier
.identifierType
== SecCmsSignerIDSubjectKeyID
)
264 version
= SEC_CMS_SIGNER_INFO_VERSION_SUBJKEY
;
265 (void)SEC_ASN1EncodeInteger(poolp
, &(signerinfo
->version
), (long)version
);
267 if (SECOID_SetAlgorithmID(poolp
, &signerinfo
->digestAlg
, digestalgtag
, NULL
) != SECSuccess
)
270 PORT_ArenaUnmark(poolp
, mark
);
274 PORT_ArenaRelease(poolp
, mark
);
279 * SecCmsSignerInfoDestroy - destroy a SignerInfo data structure
282 SecCmsSignerInfoDestroy(SecCmsSignerInfoRef si
)
284 if (si
->cert
!= NULL
) {
285 dprintfRC("SecCmsSignerInfoDestroy top: certp %p cert.rc %d\n",
286 si
->cert
, (int)CFGetRetainCount(si
->cert
));
287 CERT_DestroyCertificate(si
->cert
);
289 if (si
->certList
!= NULL
) {
290 dprintfRC("SecCmsSignerInfoDestroy top: certList.rc %d\n",
291 (int)CFGetRetainCount(si
->certList
));
292 CFRelease(si
->certList
);
294 if (si
->timestampCertList
!= NULL
) {
295 dprintfRC("SecCmsSignerInfoDestroy top: timestampCertList.rc %d\n",
296 (int)CFGetRetainCount(si
->timestampCertList
));
297 CFRelease(si
->timestampCertList
);
299 if (si
->timestampCert
!= NULL
) {
300 dprintfRC("SecCmsSignerInfoDestroy top: timestampCert.rc %d\n",
301 (int)CFGetRetainCount(si
->timestampCert
));
302 CFRelease(si
->timestampCert
);
304 if (si
->hashAgilityAttrValue
!= NULL
) {
305 dprintfRC("SecCmsSignerInfoDestroy top: hashAgilityAttrValue.rc %d\n",
306 (int)CFGetRetainCount(si
->hashAgilityAttrValue
));
307 CFRelease(si
->hashAgilityAttrValue
);
309 if (si
->hashAgilityV2AttrValues
!= NULL
) {
310 dprintfRC("SecCmsSignerInfoDestroy top: hashAgilityV2AttrValues.rc %d\n",
311 (int)CFGetRetainCount(si
->hashAgilityV2AttrValues
));
312 CFRelease(si
->hashAgilityV2AttrValues
);
314 /* XXX storage ??? */
318 * SecCmsSignerInfoSign - sign something
322 SecCmsSignerInfoSign(SecCmsSignerInfoRef signerinfo
, CSSM_DATA_PTR digest
, CSSM_DATA_PTR contentType
)
324 SecCertificateRef cert
;
325 SecPrivateKeyRef privkey
= NULL
;
326 SECOidTag digestalgtag
;
327 SECOidTag pubkAlgTag
;
328 CSSM_DATA signature
= { 0 };
330 PLArenaPool
*poolp
, *tmppoolp
= NULL
;
331 const SECAlgorithmID
*algID
;
332 SECAlgorithmID freeAlgID
;
333 //CERTSubjectPublicKeyInfo *spki;
335 PORT_Assert (digest
!= NULL
);
337 poolp
= signerinfo
->cmsg
->poolp
;
339 switch (signerinfo
->signerIdentifier
.identifierType
) {
340 case SecCmsSignerIDIssuerSN
:
341 privkey
= signerinfo
->signingKey
;
342 signerinfo
->signingKey
= NULL
;
343 cert
= signerinfo
->cert
;
344 if (SecCertificateGetAlgorithmID(cert
,&algID
)) {
345 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM
);
349 case SecCmsSignerIDSubjectKeyID
:
350 privkey
= signerinfo
->signingKey
;
351 signerinfo
->signingKey
= NULL
;
353 spki
= SECKEY_CreateSubjectPublicKeyInfo(signerinfo
->pubKey
);
354 SECKEY_DestroyPublicKey(signerinfo
->pubKey
);
355 signerinfo
->pubKey
= NULL
;
356 SECOID_CopyAlgorithmID(NULL
, &freeAlgID
, &spki
->algorithm
);
357 SECKEY_DestroySubjectPublicKeyInfo(spki
);
362 if (SecKeyGetAlgorithmID(signerinfo
->pubKey
,&algID
)) {
364 /* TBD: Unify this code. Currently, iOS has an incompatible
365 * SecKeyGetAlgorithmID implementation. */
368 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM
);
371 CFRelease(signerinfo
->pubKey
);
372 signerinfo
->pubKey
= NULL
;
376 PORT_SetError(SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE
);
379 digestalgtag
= SecCmsSignerInfoGetDigestAlgTag(signerinfo
);
381 * XXX I think there should be a cert-level interface for this,
382 * so that I do not have to know about subjectPublicKeyInfo...
384 pubkAlgTag
= SECOID_GetAlgorithmTag(algID
);
385 if (signerinfo
->signerIdentifier
.identifierType
== SecCmsSignerIDSubjectKeyID
) {
386 SECOID_DestroyAlgorithmID(&freeAlgID
, PR_FALSE
);
391 /* Fortezza MISSI have weird signature formats.
392 * Map them to standard DSA formats
394 pubkAlgTag
= PK11_FortezzaMapSig(pubkAlgTag
);
397 if (signerinfo
->authAttr
!= NULL
) {
398 CSSM_DATA encoded_attrs
;
400 /* find and fill in the message digest attribute. */
401 rv
= SecCmsAttributeArraySetAttr(poolp
, &(signerinfo
->authAttr
),
402 SEC_OID_PKCS9_MESSAGE_DIGEST
, digest
, PR_FALSE
);
403 if (rv
!= SECSuccess
)
406 if (contentType
!= NULL
) {
407 /* if the caller wants us to, find and fill in the content type attribute. */
408 rv
= SecCmsAttributeArraySetAttr(poolp
, &(signerinfo
->authAttr
),
409 SEC_OID_PKCS9_CONTENT_TYPE
, contentType
, PR_FALSE
);
410 if (rv
!= SECSuccess
)
414 if ((tmppoolp
= PORT_NewArena (1024)) == NULL
) {
415 PORT_SetError(SEC_ERROR_NO_MEMORY
);
420 * Before encoding, reorder the attributes so that when they
421 * are encoded, they will be conforming DER, which is required
422 * to have a specific order and that is what must be used for
423 * the hash/signature. We do this here, rather than building
424 * it into EncodeAttributes, because we do not want to do
425 * such reordering on incoming messages (which also uses
426 * EncodeAttributes) or our old signatures (and other "broken"
427 * implementations) will not verify. So, we want to guarantee
428 * that we send out good DER encodings of attributes, but not
429 * to expect to receive them.
431 if (SecCmsAttributeArrayReorder(signerinfo
->authAttr
) != SECSuccess
)
434 encoded_attrs
.Data
= NULL
;
435 encoded_attrs
.Length
= 0;
436 if (SecCmsAttributeArrayEncode(tmppoolp
, &(signerinfo
->authAttr
),
437 &encoded_attrs
) == NULL
)
440 rv
= SEC_SignData(&signature
, encoded_attrs
.Data
, (int)encoded_attrs
.Length
,
441 privkey
, digestalgtag
, pubkAlgTag
);
442 PORT_FreeArena(tmppoolp
, PR_FALSE
); /* awkward memory management :-( */
445 rv
= SGN_Digest(privkey
, digestalgtag
, pubkAlgTag
, &signature
, digest
);
447 SECKEY_DestroyPrivateKey(privkey
);
450 if (rv
!= SECSuccess
)
453 if (SECITEM_CopyItem(poolp
, &(signerinfo
->encDigest
), &signature
)
457 SECITEM_FreeItem(&signature
, PR_FALSE
);
459 if(pubkAlgTag
== SEC_OID_EC_PUBLIC_KEY
) {
461 * RFC 3278 section section 2.1.1 states that the signatureAlgorithm
462 * field contains the full ecdsa-with-SHA1 OID, not plain old ecPublicKey
463 * as would appear in other forms of signed datas. However Microsoft doesn't
464 * do this, it puts ecPublicKey there, and if we put ecdsa-with-SHA1 there,
465 * MS can't verify - presumably because it takes the digest of the digest
466 * before feeding it to ECDSA.
467 * We handle this with a preference; default if it's not there is
468 * "Microsoft compatibility mode".
470 if(!SecCmsMsEcdsaCompatMode()) {
471 pubkAlgTag
= SEC_OID_ECDSA_WithSHA1
;
473 /* else violating the spec for compatibility */
476 if (SECOID_SetAlgorithmID(poolp
, &(signerinfo
->digestEncAlg
), pubkAlgTag
,
483 if (signature
.Length
!= 0)
484 SECITEM_FreeItem (&signature
, PR_FALSE
);
486 SECKEY_DestroyPrivateKey(privkey
);
488 PORT_FreeArena(tmppoolp
, PR_FALSE
);
493 SecCmsSignerInfoVerifyCertificate(SecCmsSignerInfoRef signerinfo
, SecKeychainRef keychainOrArray
,
494 CFTypeRef policies
, SecTrustRef
*trustRef
)
496 SecCertificateRef cert
;
497 CFAbsoluteTime stime
;
499 CSSM_DATA_PTR
*otherCerts
;
501 if ((cert
= SecCmsSignerInfoGetSigningCertificate(signerinfo
, keychainOrArray
)) == NULL
) {
502 dprintf("SecCmsSignerInfoVerifyCertificate: no signing cert\n");
503 signerinfo
->verificationStatus
= SecCmsVSSigningCertNotFound
;
508 * Get and convert the signing time; if available, it will be used
509 * both on the cert verification and for importing the sender
512 CFTypeRef timeStampPolicies
=SecPolicyCreateAppleTimeStampingAndRevocationPolicies(policies
);
513 if (SecCmsSignerInfoGetTimestampTimeWithPolicy(signerinfo
, timeStampPolicies
, &stime
) != SECSuccess
)
514 if (SecCmsSignerInfoGetSigningTime(signerinfo
, &stime
) != SECSuccess
)
515 stime
= CFAbsoluteTimeGetCurrent();
516 CFReleaseSafe(timeStampPolicies
);
518 rv
= SecCmsSignedDataRawCerts(signerinfo
->sigd
, &otherCerts
);
522 rv
= CERT_VerifyCert(keychainOrArray
, cert
, otherCerts
, policies
, stime
, trustRef
);
523 dprintfRC("SecCmsSignerInfoVerifyCertificate after vfy: certp %p cert.rc %d\n",
524 cert
, (int)CFGetRetainCount(cert
));
527 if (PORT_GetError() == SEC_ERROR_UNTRUSTED_CERT
)
529 /* Signature or digest level verificationStatus errors should supercede certificate level errors, so only change the verificationStatus if the status was GoodSignature. */
530 if (signerinfo
->verificationStatus
== SecCmsVSGoodSignature
)
531 signerinfo
->verificationStatus
= SecCmsVSSigningCertNotTrusted
;
534 /* FIXME isn't this leaking the cert? */
535 dprintf("SecCmsSignerInfoVerifyCertificate: CertVerify rtn %d\n", (int)rv
);
539 static void debugShowSigningCertificate(SecCmsSignerInfoRef signerinfo
)
542 CFStringRef cn
= SecCmsSignerInfoGetSignerCommonName(signerinfo
);
545 char *ccn
= cfStringToChar(cn
);
548 dprintf("SecCmsSignerInfoVerify: cn: %s\n", ccn
);
557 * SecCmsSignerInfoVerify - verify the signature of a single SignerInfo
559 * Just verifies the signature. The assumption is that verification of the certificate
563 SecCmsSignerInfoVerify(SecCmsSignerInfoRef signerinfo
, CSSM_DATA_PTR digest
, CSSM_DATA_PTR contentType
)
565 return SecCmsSignerInfoVerifyWithPolicy(signerinfo
,NULL
, digest
,contentType
);
569 SecCmsSignerInfoVerifyWithPolicy(SecCmsSignerInfoRef signerinfo
,CFTypeRef timeStampPolicy
, CSSM_DATA_PTR digest
, CSSM_DATA_PTR contentType
)
571 SecPublicKeyRef publickey
= NULL
;
572 SecCmsAttribute
*attr
= NULL
;
573 CSSM_DATA encoded_attrs
;
574 SecCertificateRef cert
= NULL
;
575 SecCmsVerificationStatus vs
= SecCmsVSUnverified
;
576 PLArenaPool
*poolp
= NULL
;
577 SECOidTag digestAlgTag
, digestEncAlgTag
;
579 if (signerinfo
== NULL
)
582 /* SecCmsSignerInfoGetSigningCertificate will fail if 2nd parm is NULL and */
583 /* cert has not been verified */
584 if ((cert
= SecCmsSignerInfoGetSigningCertificate(signerinfo
, NULL
)) == NULL
) {
585 dprintf("SecCmsSignerInfoVerify: no signing cert\n");
586 vs
= SecCmsVSSigningCertNotFound
;
590 dprintfRC("SecCmsSignerInfoVerify top: cert %p cert.rc %d\n", cert
, (int)CFGetRetainCount(cert
));
592 debugShowSigningCertificate(signerinfo
);
594 if (NULL
== (publickey
= SecCertificateCopyKey(cert
))) {
595 vs
= SecCmsVSProcessingError
;
599 digestAlgTag
= SECOID_GetAlgorithmTag(&(signerinfo
->digestAlg
));
600 digestEncAlgTag
= SECOID_GetAlgorithmTag(&(signerinfo
->digestEncAlg
));
603 * Gross hack necessitated by RFC 3278 section 2.1.1, which states
604 * that the signature algorithm (here, digestEncAlg) contains ecdsa_with-SHA1,
605 * *not* (as in all other algorithms) the raw signature algorithm, e.g.
606 * pkcs1RSAEncryption.
608 if(digestEncAlgTag
== SEC_OID_ECDSA_WithSHA1
) {
609 digestEncAlgTag
= SEC_OID_EC_PUBLIC_KEY
;
612 if (!SecCmsArrayIsEmpty((void **)signerinfo
->authAttr
)) {
617 * RFC2630 sez that if there are any authenticated attributes,
618 * then there must be one for content type which matches the
619 * content type of the content being signed, and there must
620 * be one for message digest which matches our message digest.
621 * So check these things first.
623 if ((attr
= SecCmsAttributeArrayFindAttrByOidTag(signerinfo
->authAttr
,
624 SEC_OID_PKCS9_CONTENT_TYPE
, PR_TRUE
)) == NULL
)
626 vs
= SecCmsVSMalformedSignature
;
630 if (SecCmsAttributeCompareValue(attr
, contentType
) == PR_FALSE
) {
631 vs
= SecCmsVSMalformedSignature
;
639 if ((attr
= SecCmsAttributeArrayFindAttrByOidTag(signerinfo
->authAttr
, SEC_OID_PKCS9_MESSAGE_DIGEST
, PR_TRUE
)) == NULL
)
641 vs
= SecCmsVSMalformedSignature
;
644 if (SecCmsAttributeCompareValue(attr
, digest
) == PR_FALSE
) {
645 vs
= SecCmsVSDigestMismatch
;
649 if ((poolp
= PORT_NewArena (1024)) == NULL
) {
650 vs
= SecCmsVSProcessingError
;
657 * The signature is based on a digest of the DER-encoded authenticated
658 * attributes. So, first we encode and then we digest/verify.
659 * we trust the decoder to have the attributes in the right (sorted) order
661 encoded_attrs
.Data
= NULL
;
662 encoded_attrs
.Length
= 0;
664 if (SecCmsAttributeArrayEncode(poolp
, &(signerinfo
->authAttr
), &encoded_attrs
) == NULL
||
665 encoded_attrs
.Data
== NULL
|| encoded_attrs
.Length
== 0)
667 vs
= SecCmsVSProcessingError
;
671 vs
= (VFY_VerifyData (encoded_attrs
.Data
, (int)encoded_attrs
.Length
,
672 publickey
, &(signerinfo
->encDigest
),
673 digestAlgTag
, digestEncAlgTag
,
674 signerinfo
->cmsg
->pwfn_arg
) != SECSuccess
) ? SecCmsVSBadSignature
: SecCmsVSGoodSignature
;
676 dprintf("VFY_VerifyData (authenticated attributes): %s\n",
677 (vs
== SecCmsVSGoodSignature
)?"SecCmsVSGoodSignature":"SecCmsVSBadSignature");
679 PORT_FreeArena(poolp
, PR_FALSE
); /* awkward memory management :-( */
684 /* No authenticated attributes. The signature is based on the plain message digest. */
685 sig
= &(signerinfo
->encDigest
);
686 if (sig
->Length
== 0)
689 vs
= (VFY_VerifyDigest(digest
, publickey
, sig
,
690 digestAlgTag
, digestEncAlgTag
,
691 signerinfo
->cmsg
->pwfn_arg
) != SECSuccess
) ? SecCmsVSBadSignature
: SecCmsVSGoodSignature
;
693 dprintf("VFY_VerifyData (plain message digest): %s\n",
694 (vs
== SecCmsVSGoodSignature
)?"SecCmsVSGoodSignature":"SecCmsVSBadSignature");
697 if (!SecCmsArrayIsEmpty((void **)signerinfo
->unAuthAttr
))
699 dprintf("found an unAuthAttr\n");
700 OSStatus rux
= SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(signerinfo
,timeStampPolicy
);
701 dprintf("SecCmsSignerInfoVerifyUnAuthAttrs Status: %ld\n", (long)rux
);
707 if (vs
== SecCmsVSBadSignature
) {
709 * XXX Change the generic error into our specific one, because
710 * in that case we get a better explanation out of the Security
711 * Advisor. This is really a bug in our error strings (the
712 * "generic" error has a lousy/wrong message associated with it
713 * which assumes the signature verification was done for the
714 * purposes of checking the issuer signature on a certificate)
715 * but this is at least an easy workaround and/or in the
716 * Security Advisor, which specifically checks for the error
717 * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation
718 * in that case but does not similarly check for
719 * SEC_ERROR_BAD_SIGNATURE. It probably should, but then would
720 * probably say the wrong thing in the case that it *was* the
721 * certificate signature check that failed during the cert
722 * verification done above. Our error handling is really a mess.
724 if (PORT_GetError() == SEC_ERROR_BAD_SIGNATURE
)
725 PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE
);
728 if (publickey
!= NULL
)
729 CFRelease(publickey
);
731 signerinfo
->verificationStatus
= vs
;
732 dprintfRC("SecCmsSignerInfoVerify end: cerp %p cert.rc %d\n",
733 cert
, (int)CFGetRetainCount(cert
));
735 dprintf("verificationStatus: %d\n", vs
);
737 return (vs
== SecCmsVSGoodSignature
) ? SECSuccess
: SECFailure
;
740 if (publickey
!= NULL
)
741 SECKEY_DestroyPublicKey (publickey
);
743 dprintf("verificationStatus2: %d\n", vs
);
744 signerinfo
->verificationStatus
= vs
;
746 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE
);
751 SecCmsSignerInfoVerifyUnAuthAttrs(SecCmsSignerInfoRef signerinfo
) {
752 return SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(signerinfo
, NULL
);
756 SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(SecCmsSignerInfoRef signerinfo
,CFTypeRef timeStampPolicy
)
759 unAuthAttr is an array of attributes; we expect to
760 see just one: the timestamp blob. If we have an unAuthAttr,
761 but don't see a timestamp, return an error since we have
762 no other cases where this would be present.
765 SecCmsAttribute
*attr
= NULL
;
766 OSStatus status
= SECFailure
;
768 require(signerinfo
, xit
);
769 attr
= SecCmsAttributeArrayFindAttrByOidTag(signerinfo
->unAuthAttr
,
770 SEC_OID_PKCS9_TIMESTAMP_TOKEN
, PR_TRUE
);
773 status
= errSecTimestampMissing
;
777 dprintf("found an id-ct-TSTInfo\n");
778 // Don't check the nonce in this case
779 status
= decodeTimeStampTokenWithPolicy(signerinfo
, timeStampPolicy
, (attr
->values
)[0], &signerinfo
->encDigest
, 0);
780 if (status
!= errSecSuccess
) {
781 secerror("timestamp verification failed: %d", (int)status
);
789 SecCmsSignerInfoGetEncDigest(SecCmsSignerInfoRef signerinfo
)
791 return &signerinfo
->encDigest
;
794 SecCmsVerificationStatus
795 SecCmsSignerInfoGetVerificationStatus(SecCmsSignerInfoRef signerinfo
)
797 return signerinfo
->verificationStatus
;
801 SecCmsSignerInfoGetDigestAlg(SecCmsSignerInfoRef signerinfo
)
803 return SECOID_FindOID (&(signerinfo
->digestAlg
.algorithm
));
807 SecCmsSignerInfoGetDigestAlgTag(SecCmsSignerInfoRef signerinfo
)
811 algdata
= SECOID_FindOID (&(signerinfo
->digestAlg
.algorithm
));
813 return algdata
->offset
;
815 return SEC_OID_UNKNOWN
;
819 SecCmsSignerInfoGetCertList(SecCmsSignerInfoRef signerinfo
)
821 dprintfRC("SecCmsSignerInfoGetCertList: certList.rc %d\n",
822 (int)CFGetRetainCount(signerinfo
->certList
));
823 return signerinfo
->certList
;
827 SecCmsSignerInfoGetTimestampCertList(SecCmsSignerInfoRef signerinfo
)
829 dprintfRC("SecCmsSignerInfoGetTimestampCertList: timestampCertList.rc %d\n",
830 (int)CFGetRetainCount(signerinfo
->timestampCertList
));
831 return signerinfo
->timestampCertList
;
835 SecCmsSignerInfoGetTimestampSigningCert(SecCmsSignerInfoRef signerinfo
)
837 dprintfRC("SecCmsSignerInfoGetTimestampSigningCert: timestampCert.rc %d\n",
838 (int)CFGetRetainCount(signerinfo
->timestampCert
));
839 return signerinfo
->timestampCert
;
843 SecCmsSignerInfoGetVersion(SecCmsSignerInfoRef signerinfo
)
845 unsigned long version
;
847 /* always take apart the CSSM_DATA */
848 if (SEC_ASN1DecodeInteger(&(signerinfo
->version
), &version
) != SECSuccess
)
855 * SecCmsSignerInfoGetSigningTime - return the signing time,
856 * in UTCTime format, of a CMS signerInfo.
858 * sinfo - signerInfo data for this signer
860 * Returns a pointer to XXXX (what?)
861 * A return value of NULL is an error.
864 SecCmsSignerInfoGetSigningTime(SecCmsSignerInfoRef sinfo
, CFAbsoluteTime
*stime
)
866 SecCmsAttribute
*attr
;
872 if (sinfo
->signingTime
!= 0) {
873 *stime
= sinfo
->signingTime
; /* cached copy */
877 attr
= SecCmsAttributeArrayFindAttrByOidTag(sinfo
->authAttr
, SEC_OID_PKCS9_SIGNING_TIME
, PR_TRUE
);
878 /* XXXX multi-valued attributes NIH */
879 if (attr
== NULL
|| (value
= SecCmsAttributeGetValue(attr
)) == NULL
)
880 return errSecSigningTimeMissing
;
881 if (DER_UTCTimeToCFDate(value
, stime
) != SECSuccess
)
882 return errSecSigningTimeMissing
;
883 sinfo
->signingTime
= *stime
; /* make cached copy */
888 SecCmsSignerInfoGetTimestampTime(SecCmsSignerInfoRef sinfo
, CFAbsoluteTime
*stime
)
890 return SecCmsSignerInfoGetTimestampTimeWithPolicy(sinfo
, NULL
, stime
);
894 SecCmsSignerInfoGetTimestampTimeWithPolicy(SecCmsSignerInfoRef sinfo
, CFTypeRef timeStampPolicy
, CFAbsoluteTime
*stime
)
896 OSStatus status
= paramErr
;
898 require(sinfo
&& stime
, xit
);
900 if (sinfo
->timestampTime
!= 0)
902 *stime
= sinfo
->timestampTime
; /* cached copy */
906 // A bit heavyweight if haven't already called verify
907 status
= SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(sinfo
,timeStampPolicy
);
908 *stime
= sinfo
->timestampTime
;
915 @abstract Return the data in the signed Codesigning Hash Agility attribute.
916 @param sinfo SignerInfo data for this signer, pointer to a CFDataRef for attribute value
917 @discussion Returns a CFDataRef containing the value of the attribute
918 @result A return value of errSecInternal is an error trying to look up the oid.
919 A status value of success with null result data indicates the attribute was not present.
922 SecCmsSignerInfoGetAppleCodesigningHashAgility(SecCmsSignerInfoRef sinfo
, CFDataRef
*sdata
)
924 SecCmsAttribute
*attr
;
927 if (sinfo
== NULL
|| sdata
== NULL
)
932 if (sinfo
->hashAgilityAttrValue
!= NULL
) {
933 *sdata
= sinfo
->hashAgilityAttrValue
; /* cached copy */
937 attr
= SecCmsAttributeArrayFindAttrByOidTag(sinfo
->authAttr
, SEC_OID_APPLE_HASH_AGILITY
, PR_TRUE
);
939 /* attribute not found */
940 if (attr
== NULL
|| (value
= SecCmsAttributeGetValue(attr
)) == NULL
)
943 sinfo
->hashAgilityAttrValue
= CFDataCreate(NULL
, value
->Data
, value
->Length
); /* make cached copy */
944 if (sinfo
->hashAgilityAttrValue
) {
945 *sdata
= sinfo
->hashAgilityAttrValue
;
948 return errSecAllocate
;
951 /* AgileHash ::= SEQUENCE {
952 hashType OBJECT IDENTIFIER,
953 hashValues OCTET STRING }
956 SecAsn1Item digestOID
;
957 SecAsn1Item digestValue
;
960 static const SecAsn1Template CMSAppleAgileHashTemplate
[] = {
962 0, NULL
, sizeof(CMSAppleAgileHash
) },
963 { SEC_ASN1_OBJECT_ID
,
964 offsetof(CMSAppleAgileHash
, digestOID
), },
965 { SEC_ASN1_OCTET_STRING
,
966 offsetof(CMSAppleAgileHash
, digestValue
), },
970 static OSStatus
CMSAddAgileHashToDictionary(CFMutableDictionaryRef dictionary
, SecAsn1Item
*DERAgileHash
) {
971 PLArenaPool
*tmppoolp
= NULL
;
972 OSStatus status
= errSecSuccess
;
973 CMSAppleAgileHash agileHash
;
974 CFDataRef digestValue
= NULL
;
975 CFNumberRef digestTag
= NULL
;
977 tmppoolp
= PORT_NewArena(1024);
978 if (tmppoolp
== NULL
) {
979 return errSecAllocate
;
982 if ((status
= SEC_ASN1DecodeItem(tmppoolp
, &agileHash
, CMSAppleAgileHashTemplate
, DERAgileHash
)) != errSecSuccess
) {
986 int64_t tag
= SECOID_FindOIDTag(&agileHash
.digestOID
);
987 digestTag
= CFNumberCreate(NULL
, kCFNumberSInt64Type
, &tag
);
988 digestValue
= CFDataCreate(NULL
, agileHash
.digestValue
.Data
, agileHash
.digestValue
.Length
);
989 CFDictionaryAddValue(dictionary
, digestTag
, digestValue
);
992 CFReleaseNull(digestValue
);
993 CFReleaseNull(digestTag
);
995 PORT_FreeArena(tmppoolp
, PR_FALSE
);
1002 @abstract Return the data in the signed Codesigning Hash Agility V2 attribute.
1003 @param sinfo SignerInfo data for this signer, pointer to a CFDictionaryRef for attribute values
1004 @discussion Returns a CFDictionaryRef containing the values of the attribute
1005 @result A return value of errSecInternal is an error trying to look up the oid.
1006 A status value of success with null result data indicates the attribute was not present.
1009 SecCmsSignerInfoGetAppleCodesigningHashAgilityV2(SecCmsSignerInfoRef sinfo
, CFDictionaryRef
*sdict
)
1011 SecCmsAttribute
*attr
;
1013 if (sinfo
== NULL
|| sdict
== NULL
) {
1019 if (sinfo
->hashAgilityV2AttrValues
!= NULL
) {
1020 *sdict
= sinfo
->hashAgilityV2AttrValues
; /* cached copy */
1024 attr
= SecCmsAttributeArrayFindAttrByOidTag(sinfo
->authAttr
, SEC_OID_APPLE_HASH_AGILITY_V2
, PR_TRUE
);
1026 /* attribute not found */
1031 /* attrValues SET OF AttributeValue
1032 * AttributeValue ::= ANY
1034 CSSM_DATA_PTR
*values
= attr
->values
;
1035 if (values
== NULL
) { /* There must be values */
1036 return errSecDecode
;
1039 CFMutableDictionaryRef agileHashValues
= CFDictionaryCreateMutable(NULL
, SecCmsArrayCount((void **)values
),
1040 &kCFTypeDictionaryKeyCallBacks
,
1041 &kCFTypeDictionaryValueCallBacks
);
1042 while (*values
!= NULL
) {
1043 (void)CMSAddAgileHashToDictionary(agileHashValues
, *values
++);
1045 if (CFDictionaryGetCount(agileHashValues
) != SecCmsArrayCount((void **)attr
->values
)) {
1046 CFReleaseNull(agileHashValues
);
1047 return errSecDecode
;
1050 sinfo
->hashAgilityV2AttrValues
= agileHashValues
; /* make cached copy */
1051 if (sinfo
->hashAgilityV2AttrValues
) {
1052 *sdict
= sinfo
->hashAgilityV2AttrValues
;
1055 return errSecAllocate
;
1059 * SecCmsSignerInfoGetAppleExpirationTime - return the expiration time,
1060 * in UTCTime format, of a CMS signerInfo.
1062 * sinfo - signerInfo data for this signer
1064 * Returns a pointer to XXXX (what?)
1065 * A return value of NULL is an error.
1068 SecCmsSignerInfoGetAppleExpirationTime(SecCmsSignerInfoRef sinfo
, CFAbsoluteTime
*etime
)
1070 SecCmsAttribute
*attr
= NULL
;
1071 SecAsn1Item
* value
= NULL
;
1073 if (sinfo
== NULL
|| etime
== NULL
) {
1077 if (sinfo
->expirationTime
!= 0) {
1078 *etime
= sinfo
->expirationTime
; /* cached copy */
1082 attr
= SecCmsAttributeArrayFindAttrByOidTag(sinfo
->authAttr
, SEC_OID_APPLE_EXPIRATION_TIME
, PR_TRUE
);
1083 if (attr
== NULL
|| (value
= SecCmsAttributeGetValue(attr
)) == NULL
) {
1086 if (DER_UTCTimeToCFDate(value
, etime
) != SECSuccess
) {
1089 sinfo
->expirationTime
= *etime
; /* make cached copy */
1094 * Return the signing cert of a CMS signerInfo.
1096 * the certs in the enclosing SignedData must have been imported already
1099 SecCmsSignerInfoGetSigningCertificate(SecCmsSignerInfoRef signerinfo
, SecKeychainRef keychainOrArray
)
1101 SecCertificateRef cert
;
1102 SecCmsSignerIdentifier
*sid
;
1104 CSSM_DATA_PTR
*rawCerts
;
1106 if (signerinfo
->cert
!= NULL
) {
1107 dprintfRC("SecCmsSignerInfoGetSigningCertificate top: cert %p cert.rc %d\n",
1108 signerinfo
->cert
, (int)CFGetRetainCount(signerinfo
->cert
));
1109 return signerinfo
->cert
;
1111 ortn
= SecCmsSignedDataRawCerts(signerinfo
->sigd
, &rawCerts
);
1115 dprintf("SecCmsSignerInfoGetSigningCertificate: numRawCerts %d\n",
1116 SecCmsArrayCount((void **)rawCerts
));
1119 * This cert will also need to be freed, but since we save it
1120 * in signerinfo for later, we do not want to destroy it when
1121 * we leave this function -- we let the clean-up of the entire
1122 * cinfo structure later do the destroy of this cert.
1124 sid
= &signerinfo
->signerIdentifier
;
1125 switch (sid
->identifierType
) {
1126 case SecCmsSignerIDIssuerSN
:
1127 cert
= CERT_FindCertByIssuerAndSN(keychainOrArray
, rawCerts
, signerinfo
->sigd
->certs
, signerinfo
->cmsg
->poolp
,
1128 sid
->id
.issuerAndSN
);
1130 case SecCmsSignerIDSubjectKeyID
:
1131 cert
= CERT_FindCertBySubjectKeyID(keychainOrArray
, rawCerts
, signerinfo
->sigd
->certs
, sid
->id
.subjectKeyID
);
1138 /* cert can be NULL at that point */
1139 signerinfo
->cert
= cert
; /* earmark it */
1140 dprintfRC("SecCmsSignerInfoGetSigningCertificate end: certp %p cert.rc %d\n",
1141 signerinfo
->cert
, (int)CFGetRetainCount(signerinfo
->cert
));
1147 * SecCmsSignerInfoGetSignerCommonName - return the common name of the signer
1149 * sinfo - signerInfo data for this signer
1151 * Returns a CFStringRef containing the common name of the signer.
1152 * A return value of NULL is an error.
1155 SecCmsSignerInfoGetSignerCommonName(SecCmsSignerInfoRef sinfo
)
1157 SecCertificateRef signercert
;
1158 CFStringRef commonName
= NULL
;
1160 /* will fail if cert is not verified */
1161 if ((signercert
= SecCmsSignerInfoGetSigningCertificate(sinfo
, NULL
)) == NULL
)
1164 if (errSecSuccess
!= SecCertificateCopyCommonName(signercert
, &commonName
)) {
1172 * SecCmsSignerInfoGetSignerEmailAddress - return the email address of the signer
1174 * sinfo - signerInfo data for this signer
1176 * Returns a CFStringRef containing the name of the signer.
1177 * A return value of NULL is an error.
1180 SecCmsSignerInfoGetSignerEmailAddress(SecCmsSignerInfoRef sinfo
)
1182 SecCertificateRef signercert
;
1183 CFStringRef emailAddress
= NULL
;
1185 if ((signercert
= SecCmsSignerInfoGetSigningCertificate(sinfo
, NULL
)) == NULL
)
1188 SecCertificateGetEmailAddress(signercert
, &emailAddress
);
1190 return CFRetainSafe(emailAddress
);
1195 * SecCmsSignerInfoAddAuthAttr - add an attribute to the
1196 * authenticated (i.e. signed) attributes of "signerinfo".
1199 SecCmsSignerInfoAddAuthAttr(SecCmsSignerInfoRef signerinfo
, SecCmsAttribute
*attr
)
1201 return SecCmsAttributeArrayAddAttr(signerinfo
->cmsg
->poolp
, &(signerinfo
->authAttr
), attr
);
1205 * SecCmsSignerInfoAddUnauthAttr - add an attribute to the
1206 * unauthenticated attributes of "signerinfo".
1209 SecCmsSignerInfoAddUnauthAttr(SecCmsSignerInfoRef signerinfo
, SecCmsAttribute
*attr
)
1211 return SecCmsAttributeArrayAddAttr(signerinfo
->cmsg
->poolp
, &(signerinfo
->unAuthAttr
), attr
);
1215 * SecCmsSignerInfoAddSigningTime - add the signing time to the
1216 * authenticated (i.e. signed) attributes of "signerinfo".
1218 * This is expected to be included in outgoing signed
1219 * messages for email (S/MIME) but is likely useful in other situations.
1221 * This should only be added once; a second call will do nothing.
1223 * XXX This will probably just shove the current time into "signerinfo"
1224 * but it will not actually get signed until the entire item is
1225 * processed for encoding. Is this (expected to be small) delay okay?
1228 SecCmsSignerInfoAddSigningTime(SecCmsSignerInfoRef signerinfo
, CFAbsoluteTime t
)
1230 SecCmsAttribute
*attr
;
1235 poolp
= signerinfo
->cmsg
->poolp
;
1237 mark
= PORT_ArenaMark(poolp
);
1239 /* create new signing time attribute */
1240 if (DER_CFDateToUTCTime(t
, &stime
) != SECSuccess
)
1243 if ((attr
= SecCmsAttributeCreate(poolp
, SEC_OID_PKCS9_SIGNING_TIME
, &stime
, PR_FALSE
)) == NULL
) {
1244 SECITEM_FreeItem (&stime
, PR_FALSE
);
1248 SECITEM_FreeItem (&stime
, PR_FALSE
);
1250 if (SecCmsSignerInfoAddAuthAttr(signerinfo
, attr
) != SECSuccess
)
1253 PORT_ArenaUnmark (poolp
, mark
);
1258 PORT_ArenaRelease (poolp
, mark
);
1263 * SecCmsSignerInfoAddSMIMECaps - add a SMIMECapabilities attribute to the
1264 * authenticated (i.e. signed) attributes of "signerinfo".
1266 * This is expected to be included in outgoing signed
1267 * messages for email (S/MIME).
1270 SecCmsSignerInfoAddSMIMECaps(SecCmsSignerInfoRef signerinfo
)
1272 SecCmsAttribute
*attr
;
1273 CSSM_DATA_PTR smimecaps
= NULL
;
1277 poolp
= signerinfo
->cmsg
->poolp
;
1279 mark
= PORT_ArenaMark(poolp
);
1281 smimecaps
= SECITEM_AllocItem(poolp
, NULL
, 0);
1282 if (smimecaps
== NULL
)
1285 /* create new signing time attribute */
1287 // @@@ We don't do Fortezza yet.
1288 if (SecSMIMECreateSMIMECapabilities((SecArenaPoolRef
)poolp
, smimecaps
, PR_FALSE
) != SECSuccess
)
1290 if (SecSMIMECreateSMIMECapabilities(poolp
, smimecaps
,
1291 PK11_FortezzaHasKEA(signerinfo
->cert
)) != SECSuccess
)
1295 if ((attr
= SecCmsAttributeCreate(poolp
, SEC_OID_PKCS9_SMIME_CAPABILITIES
, smimecaps
, PR_TRUE
)) == NULL
)
1298 if (SecCmsSignerInfoAddAuthAttr(signerinfo
, attr
) != SECSuccess
)
1301 PORT_ArenaUnmark (poolp
, mark
);
1305 PORT_ArenaRelease (poolp
, mark
);
1310 * SecCmsSignerInfoAddSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
1311 * authenticated (i.e. signed) attributes of "signerinfo".
1313 * This is expected to be included in outgoing signed messages for email (S/MIME).
1316 SecCmsSignerInfoAddSMIMEEncKeyPrefs(SecCmsSignerInfoRef signerinfo
, SecCertificateRef cert
, SecKeychainRef keychainOrArray
)
1318 SecCmsAttribute
*attr
;
1319 CSSM_DATA_PTR smimeekp
= NULL
;
1326 /* verify this cert for encryption */
1327 policy
= CERT_PolicyForCertUsage(certUsageEmailRecipient
);
1328 if (CERT_VerifyCert(keychainOrArray
, cert
, policy
, CFAbsoluteTimeGetCurrent(), NULL
) != SECSuccess
) {
1335 poolp
= signerinfo
->cmsg
->poolp
;
1336 mark
= PORT_ArenaMark(poolp
);
1338 smimeekp
= SECITEM_AllocItem(poolp
, NULL
, 0);
1339 if (smimeekp
== NULL
)
1342 /* create new signing time attribute */
1343 if (SecSMIMECreateSMIMEEncKeyPrefs((SecArenaPoolRef
)poolp
, smimeekp
, cert
) != SECSuccess
)
1346 if ((attr
= SecCmsAttributeCreate(poolp
, SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE
, smimeekp
, PR_TRUE
)) == NULL
)
1349 if (SecCmsSignerInfoAddAuthAttr(signerinfo
, attr
) != SECSuccess
)
1352 PORT_ArenaUnmark (poolp
, mark
);
1356 PORT_ArenaRelease (poolp
, mark
);
1361 * SecCmsSignerInfoAddMSSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
1362 * authenticated (i.e. signed) attributes of "signerinfo", using the OID preferred by Microsoft.
1364 * This is expected to be included in outgoing signed messages for email (S/MIME),
1365 * if compatibility with Microsoft mail clients is wanted.
1368 SecCmsSignerInfoAddMSSMIMEEncKeyPrefs(SecCmsSignerInfoRef signerinfo
, SecCertificateRef cert
, SecKeychainRef keychainOrArray
)
1370 SecCmsAttribute
*attr
;
1371 CSSM_DATA_PTR smimeekp
= NULL
;
1378 /* verify this cert for encryption */
1379 policy
= CERT_PolicyForCertUsage(certUsageEmailRecipient
);
1380 if (CERT_VerifyCert(keychainOrArray
, cert
, policy
, CFAbsoluteTimeGetCurrent(), NULL
) != SECSuccess
) {
1387 poolp
= signerinfo
->cmsg
->poolp
;
1388 mark
= PORT_ArenaMark(poolp
);
1390 smimeekp
= SECITEM_AllocItem(poolp
, NULL
, 0);
1391 if (smimeekp
== NULL
)
1394 /* create new signing time attribute */
1395 if (SecSMIMECreateMSSMIMEEncKeyPrefs((SecArenaPoolRef
)poolp
, smimeekp
, cert
) != SECSuccess
)
1398 if ((attr
= SecCmsAttributeCreate(poolp
, SEC_OID_MS_SMIME_ENCRYPTION_KEY_PREFERENCE
, smimeekp
, PR_TRUE
)) == NULL
)
1401 if (SecCmsSignerInfoAddAuthAttr(signerinfo
, attr
) != SECSuccess
)
1404 PORT_ArenaUnmark (poolp
, mark
);
1408 PORT_ArenaRelease (poolp
, mark
);
1413 * SecCmsSignerInfoAddTimeStamp - add time stamp to the
1414 * unauthenticated (i.e. unsigned) attributes of "signerinfo".
1416 * This will initially be used for time stamping signed applications
1417 * by using a Time Stamping Authority. It may also be included in outgoing signed
1418 * messages for email (S/MIME), and may be useful in other situations.
1420 * This should only be added once; a second call will do nothing.
1425 Countersignature attribute values have ASN.1 type Countersignature:
1426 Countersignature ::= SignerInfo
1427 Countersignature values have the same meaning as SignerInfo values
1428 for ordinary signatures, except that:
1429 1. The signedAttributes field MUST NOT contain a content-type
1430 attribute; there is no content type for countersignatures.
1431 2. The signedAttributes field MUST contain a message-digest
1432 attribute if it contains any other attributes.
1433 3. The input to the message-digesting process is the contents octets
1434 of the DER encoding of the signatureValue field of the SignerInfo
1435 value with which the attribute is associated.
1440 @abstract Create a timestamp unsigned attribute with a TimeStampToken.
1444 SecCmsSignerInfoAddTimeStamp(SecCmsSignerInfoRef signerinfo
, CSSM_DATA
*tstoken
)
1446 SecCmsAttribute
*attr
;
1447 PLArenaPool
*poolp
= signerinfo
->cmsg
->poolp
;
1448 void *mark
= PORT_ArenaMark(poolp
);
1450 // We have already encoded this ourselves, so last param is PR_TRUE
1451 if ((attr
= SecCmsAttributeCreate(poolp
, SEC_OID_PKCS9_TIMESTAMP_TOKEN
, tstoken
, PR_TRUE
)) == NULL
)
1454 if (SecCmsSignerInfoAddUnauthAttr(signerinfo
, attr
) != SECSuccess
)
1457 PORT_ArenaUnmark (poolp
, mark
);
1462 PORT_ArenaRelease (poolp
, mark
);
1467 * SecCmsSignerInfoAddCounterSignature - countersign a signerinfo
1469 * 1. digest the DER-encoded signature value of the original signerinfo
1470 * 2. create new signerinfo with correct version, sid, digestAlg
1471 * 3. add message-digest authAttr, but NO content-type
1472 * 4. sign the authAttrs
1473 * 5. DER-encode the new signerInfo
1474 * 6. add the whole thing to original signerInfo's unAuthAttrs
1475 * as a SEC_OID_PKCS9_COUNTER_SIGNATURE attribute
1477 * XXXX give back the new signerinfo?
1480 SecCmsSignerInfoAddCounterSignature(SecCmsSignerInfoRef signerinfo
,
1481 SECOidTag digestalg
, SecIdentityRef identity
)
1489 @abstract Add the Apple Codesigning Hash Agility attribute to the authenticated (i.e. signed) attributes of "signerinfo".
1490 @discussion This is expected to be included in outgoing Apple code signatures.
1493 SecCmsSignerInfoAddAppleCodesigningHashAgility(SecCmsSignerInfoRef signerinfo
, CFDataRef attrValue
)
1495 SecCmsAttribute
*attr
;
1496 PLArenaPool
*poolp
= signerinfo
->cmsg
->poolp
;
1497 void *mark
= PORT_ArenaMark(poolp
);
1498 OSStatus status
= SECFailure
;
1500 /* The value is required for this attribute. */
1502 status
= errSecParam
;
1507 * SecCmsAttributeCreate makes a copy of the data in value, so
1508 * we don't need to copy into the CSSM_DATA struct.
1511 value
.Length
= CFDataGetLength(attrValue
);
1512 value
.Data
= (uint8_t *)CFDataGetBytePtr(attrValue
);
1514 if ((attr
= SecCmsAttributeCreate(poolp
,
1515 SEC_OID_APPLE_HASH_AGILITY
,
1517 PR_FALSE
)) == NULL
) {
1518 status
= errSecAllocate
;
1522 if (SecCmsSignerInfoAddAuthAttr(signerinfo
, attr
) != SECSuccess
) {
1523 status
= errSecInternalError
;
1527 PORT_ArenaUnmark(poolp
, mark
);
1531 PORT_ArenaRelease(poolp
, mark
);
1535 static OSStatus
CMSAddAgileHashToAttribute(PLArenaPool
*poolp
, SecCmsAttribute
*attr
, CFNumberRef cftag
, CFDataRef value
) {
1536 PLArenaPool
*tmppoolp
= NULL
;
1538 SECOidData
*digestOid
= NULL
;
1539 CMSAppleAgileHash agileHash
;
1540 SecAsn1Item attrValue
= { .Data
= NULL
, .Length
= 0 };
1541 OSStatus status
= errSecSuccess
;
1543 memset(&agileHash
, 0, sizeof(agileHash
));
1545 if(!CFNumberGetValue(cftag
, kCFNumberSInt64Type
, &tag
)) {
1548 digestOid
= SECOID_FindOIDByTag((SECOidTag
)tag
);
1550 agileHash
.digestValue
.Data
= (uint8_t *)CFDataGetBytePtr(value
);
1551 agileHash
.digestValue
.Length
= CFDataGetLength(value
);
1552 agileHash
.digestOID
.Data
= digestOid
->oid
.Data
;
1553 agileHash
.digestOID
.Length
= digestOid
->oid
.Length
;
1555 tmppoolp
= PORT_NewArena(1024);
1556 if (tmppoolp
== NULL
) {
1557 return errSecAllocate
;
1560 if (SEC_ASN1EncodeItem(tmppoolp
, &attrValue
, &agileHash
, CMSAppleAgileHashTemplate
) == NULL
) {
1561 status
= errSecParam
;
1565 status
= SecCmsAttributeAddValue(poolp
, attr
, &attrValue
);
1569 PORT_FreeArena(tmppoolp
, PR_FALSE
);
1576 @abstract Add the Apple Codesigning Hash Agility attribute to the authenticated (i.e. signed) attributes of "signerinfo".
1577 @discussion This is expected to be included in outgoing Apple code signatures.
1580 SecCmsSignerInfoAddAppleCodesigningHashAgilityV2(SecCmsSignerInfoRef signerinfo
, CFDictionaryRef attrValues
)
1582 __block SecCmsAttribute
*attr
;
1583 __block PLArenaPool
*poolp
= signerinfo
->cmsg
->poolp
;
1584 void *mark
= PORT_ArenaMark(poolp
);
1585 OSStatus status
= SECFailure
;
1587 /* The value is required for this attribute. */
1589 status
= errSecParam
;
1593 if ((attr
= SecCmsAttributeCreate(poolp
, SEC_OID_APPLE_HASH_AGILITY_V2
,
1594 NULL
, PR_TRUE
)) == NULL
) {
1595 status
= errSecAllocate
;
1599 CFDictionaryForEach(attrValues
, ^(const void *key
, const void *value
) {
1600 if (!isNumber(key
) || !isData(value
)) {
1603 (void)CMSAddAgileHashToAttribute(poolp
, attr
, (CFNumberRef
)key
, (CFDataRef
)value
);
1606 if (SecCmsSignerInfoAddAuthAttr(signerinfo
, attr
) != SECSuccess
) {
1607 status
= errSecInternal
;
1611 PORT_ArenaUnmark(poolp
, mark
);
1615 PORT_ArenaRelease(poolp
, mark
);
1620 * SecCmsSignerInfoAddAppleExpirationTime - add the expiration time to the
1621 * authenticated (i.e. signed) attributes of "signerinfo".
1623 * This is expected to be included in outgoing signed
1624 * messages for Asset Receipts but is likely useful in other situations.
1626 * This should only be added once; a second call will do nothing.
1629 SecCmsSignerInfoAddAppleExpirationTime(SecCmsSignerInfoRef signerinfo
, CFAbsoluteTime t
)
1631 SecCmsAttribute
*attr
= NULL
;
1632 PLArenaPool
*poolp
= signerinfo
->cmsg
->poolp
;
1633 void *mark
= PORT_ArenaMark(poolp
);
1635 /* create new expiration time attribute */
1637 if (DER_CFDateToUTCTime(t
, &etime
) != SECSuccess
) {
1641 if ((attr
= SecCmsAttributeCreate(poolp
, SEC_OID_APPLE_EXPIRATION_TIME
, &etime
, PR_FALSE
)) == NULL
) {
1642 SECITEM_FreeItem (&etime
, PR_FALSE
);
1646 SECITEM_FreeItem(&etime
, PR_FALSE
);
1648 if (SecCmsSignerInfoAddAuthAttr(signerinfo
, attr
) != SECSuccess
) {
1652 PORT_ArenaUnmark(poolp
, mark
);
1656 PORT_ArenaRelease(poolp
, mark
);
1660 SecCertificateRef
SecCmsSignerInfoCopyCertFromEncryptionKeyPreference(SecCmsSignerInfoRef signerinfo
) {
1661 SecCertificateRef cert
= NULL
;
1662 SecCmsAttribute
*attr
;
1664 SecKeychainRef keychainOrArray
;
1666 (void)SecKeychainCopyDefault(&keychainOrArray
);
1668 /* sanity check - see if verification status is ok (unverified does not count...) */
1669 if (signerinfo
->verificationStatus
!= SecCmsVSGoodSignature
)
1672 /* Prep the raw certs */
1673 CSSM_DATA_PTR
*rawCerts
= NULL
;
1674 if (signerinfo
->sigd
) {
1675 rawCerts
= signerinfo
->sigd
->rawCerts
;
1678 /* find preferred encryption cert */
1679 if (!SecCmsArrayIsEmpty((void **)signerinfo
->authAttr
) &&
1680 (attr
= SecCmsAttributeArrayFindAttrByOidTag(signerinfo
->authAttr
,
1681 SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE
, PR_TRUE
)) != NULL
)
1682 { /* we have a SMIME_ENCRYPTION_KEY_PREFERENCE attribute! Find the cert. */
1683 ekp
= SecCmsAttributeGetValue(attr
);
1686 cert
= SecSMIMEGetCertFromEncryptionKeyPreference(keychainOrArray
, rawCerts
, ekp
);
1688 if(cert
) return cert
;
1690 if (!SecCmsArrayIsEmpty((void **)signerinfo
->authAttr
) &&
1691 (attr
= SecCmsAttributeArrayFindAttrByOidTag(signerinfo
->authAttr
,
1692 SEC_OID_MS_SMIME_ENCRYPTION_KEY_PREFERENCE
, PR_TRUE
)) != NULL
)
1693 { /* we have a MS_SMIME_ENCRYPTION_KEY_PREFERENCE attribute! Find the cert. */
1694 ekp
= SecCmsAttributeGetValue(attr
);
1697 cert
= SecSMIMEGetCertFromEncryptionKeyPreference(keychainOrArray
, rawCerts
, ekp
);
1703 * XXXX the following needs to be done in the S/MIME layer code
1704 * after signature of a signerinfo is verified
1707 SecCmsSignerInfoSaveSMIMEProfile(SecCmsSignerInfoRef signerinfo
)
1709 SecCertificateRef cert
= NULL
;
1710 CSSM_DATA_PTR profile
= NULL
;
1711 SecCmsAttribute
*attr
;
1712 CSSM_DATA_PTR utc_stime
= NULL
;
1716 Boolean must_free_cert
= PR_FALSE
;
1718 SecKeychainRef keychainOrArray
;
1720 status
= SecKeychainCopyDefault(&keychainOrArray
);
1722 /* sanity check - see if verification status is ok (unverified does not count...) */
1723 if (signerinfo
->verificationStatus
!= SecCmsVSGoodSignature
)
1726 /* find preferred encryption cert */
1727 if (!SecCmsArrayIsEmpty((void **)signerinfo
->authAttr
) &&
1728 (attr
= SecCmsAttributeArrayFindAttrByOidTag(signerinfo
->authAttr
,
1729 SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE
, PR_TRUE
)) != NULL
)
1730 { /* we have a SMIME_ENCRYPTION_KEY_PREFERENCE attribute! */
1731 ekp
= SecCmsAttributeGetValue(attr
);
1735 /* we assume that all certs coming with the message have been imported to the */
1736 /* temporary database */
1737 cert
= SecSMIMEGetCertFromEncryptionKeyPreference(keychainOrArray
, NULL
, ekp
);
1740 must_free_cert
= PR_TRUE
;
1744 /* no preferred cert found?
1745 * find the cert the signerinfo is signed with instead */
1746 CFStringRef emailAddress
=NULL
;
1748 cert
= SecCmsSignerInfoGetSigningCertificate(signerinfo
, keychainOrArray
);
1751 if (SecCertificateGetEmailAddress(cert
,&emailAddress
))
1755 /* 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.
1756 * that's OK, we can still save the S/MIME profile. The encryption cert
1757 * should have already been saved */
1759 if (CERT_VerifyCert(keychainOrArray
, cert
, certUsageEmailRecipient
, CFAbsoluteTimeGetCurrent(), NULL
) != SECSuccess
) {
1761 CERT_DestroyCertificate(cert
);
1766 /* XXX store encryption cert permanently? */
1769 * Remember the current error set because we do not care about
1770 * anything set by the functions we are about to call.
1772 save_error
= PORT_GetError();
1774 if (!SecCmsArrayIsEmpty((void **)signerinfo
->authAttr
)) {
1775 attr
= SecCmsAttributeArrayFindAttrByOidTag(signerinfo
->authAttr
,
1776 SEC_OID_PKCS9_SMIME_CAPABILITIES
,
1778 profile
= SecCmsAttributeGetValue(attr
);
1779 attr
= SecCmsAttributeArrayFindAttrByOidTag(signerinfo
->authAttr
,
1780 SEC_OID_PKCS9_SIGNING_TIME
,
1782 utc_stime
= SecCmsAttributeGetValue(attr
);
1785 rv
= CERT_SaveSMimeProfile (cert
, profile
, utc_stime
);
1787 CERT_DestroyCertificate(cert
);
1790 * Restore the saved error in case the calls above set a new
1791 * one that we do not actually care about.
1793 PORT_SetError (save_error
);
1799 * SecCmsSignerInfoIncludeCerts - set cert chain inclusion mode for this signer
1802 SecCmsSignerInfoIncludeCerts(SecCmsSignerInfoRef signerinfo
, SecCmsCertChainMode cm
, SECCertUsage usage
)
1804 if (signerinfo
->cert
== NULL
) {
1808 /* don't leak if we get called twice */
1809 if (signerinfo
->certList
!= NULL
) {
1810 CFRelease(signerinfo
->certList
);
1811 signerinfo
->certList
= NULL
;
1816 signerinfo
->certList
= NULL
;
1818 case SecCmsCMCertOnly
:
1819 signerinfo
->certList
= CERT_CertListFromCert(signerinfo
->cert
);
1821 case SecCmsCMCertChain
:
1822 signerinfo
->certList
= CERT_CertChainFromCert(signerinfo
->cert
, usage
, PR_FALSE
, PR_FALSE
);
1824 case SecCmsCMCertChainWithRoot
:
1825 signerinfo
->certList
= CERT_CertChainFromCert(signerinfo
->cert
, usage
, PR_TRUE
, PR_FALSE
);
1827 case SecCmsCMCertChainWithRootOrFail
:
1828 signerinfo
->certList
= CERT_CertChainFromCert(signerinfo
->cert
, usage
, PR_TRUE
, PR_TRUE
);
1831 if (cm
!= SecCmsCMNone
&& signerinfo
->certList
== NULL
) {