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"
44 #include "SecAsn1Item.h"
48 #include <security_asn1/secasn1.h>
49 #include <security_asn1/secerr.h>
50 #include <security_asn1/secport.h>
53 #include <Security/SecKeychain.h>
56 #include <Security/SecIdentity.h>
57 #include <Security/SecCertificateInternal.h>
58 #include <Security/SecInternal.h>
59 #include <Security/SecKeyPriv.h>
60 #include <utilities/SecCFWrappers.h>
61 #include <CoreFoundation/CFTimeZone.h>
62 #include <Security/SecBasePriv.h>
65 #define HIDIGIT(v) (((v) / 10) + '0')
66 #define LODIGIT(v) (((v) % 10) + '0')
68 #define ISDIGIT(dig) (((dig) >= '0') && ((dig) <= '9'))
69 #define CAPTURE(var,p,label) \
71 if (!ISDIGIT((p)[0]) || !ISDIGIT((p)[1])) goto label; \
72 (var) = ((p)[0] - '0') * 10 + ((p)[1] - '0'); \
77 DER_UTCTimeToCFDate(const SecAsn1Item
* utcTime
, CFAbsoluteTime
*date
)
79 char *string
= (char *)utcTime
->Data
;
80 int year
, month
, mday
, hour
, minute
, second
, hourOff
, minOff
;
82 /* Verify time is formatted properly and capture information */
86 CAPTURE(year
,string
+0,loser
);
88 /* ASSUME that year # is in the 2000's, not the 1900's */
93 CAPTURE(month
,string
+2,loser
);
94 if ((month
== 0) || (month
> 12)) goto loser
;
95 CAPTURE(mday
,string
+4,loser
);
96 if ((mday
== 0) || (mday
> 31)) goto loser
;
97 CAPTURE(hour
,string
+6,loser
);
98 if (hour
> 23) goto loser
;
99 CAPTURE(minute
,string
+8,loser
);
100 if (minute
> 59) goto loser
;
101 if (ISDIGIT(string
[10])) {
102 CAPTURE(second
,string
+10,loser
);
103 if (second
> 59) goto loser
;
106 if (string
[10] == '+') {
107 CAPTURE(hourOff
,string
+11,loser
);
108 if (hourOff
> 23) goto loser
;
109 CAPTURE(minOff
,string
+13,loser
);
110 if (minOff
> 59) goto loser
;
111 } else if (string
[10] == '-') {
112 CAPTURE(hourOff
,string
+11,loser
);
113 if (hourOff
> 23) goto loser
;
115 CAPTURE(minOff
,string
+13,loser
);
116 if (minOff
> 59) goto loser
;
118 } else if (string
[10] != 'Z') {
122 if (hourOff
== 0 && minOff
== 0) {
123 *date
= CFAbsoluteTimeForGregorianZuluMoment(year
, month
, mday
, hour
, minute
, second
);
125 CFTimeZoneRef tz
= CFTimeZoneCreateWithTimeIntervalFromGMT(kCFAllocatorDefault
, (hourOff
* 60 + minOff
) * 60);
126 *date
= CFAbsoluteTimeForGregorianMoment(tz
, year
, month
, mday
, hour
, minute
, second
);
137 DER_CFDateToUTCTime(CFAbsoluteTime date
, SecAsn1Item
* utcTime
)
141 utcTime
->Length
= 13;
142 utcTime
->Data
= d
= PORT_Alloc(13);
146 __block
int year
= 0, month
= 0, day
= 0, hour
= 0, minute
= 0, second
= 0;
148 SecCFCalendarDoWithZuluCalendar(^(CFCalendarRef zuluCalendar
) {
149 result
= CFCalendarDecomposeAbsoluteTime(zuluCalendar
, date
, "yMdHms", &year
, &month
, &day
, &hour
, &minute
, &second
);
154 /* UTC time does not handle the years before 1950 */
158 /* remove the century since it's added to the year by the
159 CFAbsoluteTimeGetGregorianDate routine, but is not needed for UTC time */
162 d
[0] = HIDIGIT(year
);
163 d
[1] = LODIGIT(year
);
164 d
[2] = HIDIGIT(month
);
165 d
[3] = LODIGIT(month
);
168 d
[6] = HIDIGIT(hour
);
169 d
[7] = LODIGIT(hour
);
170 d
[8] = HIDIGIT(minute
);
171 d
[9] = LODIGIT(minute
);
172 d
[10] = HIDIGIT(second
);
173 d
[11] = LODIGIT(second
);
178 /* =============================================================================
182 nss_cmssignerinfo_create(SecCmsSignedDataRef sigd
, SecCmsSignerIDSelector type
, SecCertificateRef cert
, const SecAsn1Item
*subjKeyID
, SecPublicKeyRef pubKey
, SecPrivateKeyRef signingKey
, SECOidTag digestalgtag
);
185 SecCmsSignerInfoCreateWithSubjKeyID(SecCmsSignedDataRef sigd
, const SecAsn1Item
*subjKeyID
, SecPublicKeyRef pubKey
, SecPrivateKeyRef signingKey
, SECOidTag digestalgtag
)
187 return nss_cmssignerinfo_create(sigd
, SecCmsSignerIDSubjectKeyID
, NULL
, subjKeyID
, pubKey
, signingKey
, digestalgtag
);
191 SecCmsSignerInfoCreate(SecCmsSignedDataRef sigd
, SecIdentityRef identity
, SECOidTag digestalgtag
)
193 SecCmsSignerInfoRef signerInfo
= NULL
;
194 SecCertificateRef cert
= NULL
;
195 SecPrivateKeyRef signingKey
= NULL
;
197 if (SecIdentityCopyCertificate(identity
, &cert
))
199 if (SecIdentityCopyPrivateKey(identity
, &signingKey
))
202 signerInfo
= nss_cmssignerinfo_create(sigd
, SecCmsSignerIDIssuerSN
, cert
, NULL
, NULL
, signingKey
, digestalgtag
);
208 CFRelease(signingKey
);
214 nss_cmssignerinfo_create(SecCmsSignedDataRef sigd
, SecCmsSignerIDSelector type
, SecCertificateRef cert
, const SecAsn1Item
*subjKeyID
, SecPublicKeyRef pubKey
, SecPrivateKeyRef signingKey
, SECOidTag digestalgtag
)
217 SecCmsSignerInfoRef signerinfo
;
221 poolp
= sigd
->contentInfo
.cmsg
->poolp
;
223 mark
= PORT_ArenaMark(poolp
);
225 signerinfo
= (SecCmsSignerInfoRef
)PORT_ArenaZAlloc(poolp
, sizeof(SecCmsSignerInfo
));
226 if (signerinfo
== NULL
) {
227 PORT_ArenaRelease(poolp
, mark
);
232 signerinfo
->signedData
= sigd
;
235 case SecCmsSignerIDIssuerSN
:
236 signerinfo
->signerIdentifier
.identifierType
= SecCmsSignerIDIssuerSN
;
237 if ((signerinfo
->cert
= CERT_DupCertificate(cert
)) == NULL
)
239 if ((signerinfo
->signerIdentifier
.id
.issuerAndSN
= CERT_GetCertIssuerAndSN(poolp
, cert
)) == NULL
)
242 case SecCmsSignerIDSubjectKeyID
:
243 signerinfo
->signerIdentifier
.identifierType
= SecCmsSignerIDSubjectKeyID
;
244 PORT_Assert(subjKeyID
);
247 signerinfo
->signerIdentifier
.id
.subjectKeyID
= PORT_ArenaNew(poolp
, SecAsn1Item
);
248 SECITEM_CopyItem(poolp
, signerinfo
->signerIdentifier
.id
.subjectKeyID
,
250 signerinfo
->pubKey
= SECKEY_CopyPublicKey(pubKey
);
251 if (!signerinfo
->pubKey
)
261 signerinfo
->signingKey
= SECKEY_CopyPrivateKey(signingKey
);
262 if (!signerinfo
->signingKey
)
265 /* set version right now */
266 version
= SEC_CMS_SIGNER_INFO_VERSION_ISSUERSN
;
267 /* RFC2630 5.3 "version is the syntax version number. If the .... " */
268 if (signerinfo
->signerIdentifier
.identifierType
== SecCmsSignerIDSubjectKeyID
)
269 version
= SEC_CMS_SIGNER_INFO_VERSION_SUBJKEY
;
270 (void)SEC_ASN1EncodeInteger(poolp
, &(signerinfo
->version
), (long)version
);
272 if (SECOID_SetAlgorithmID(poolp
, &signerinfo
->digestAlg
, digestalgtag
, NULL
) != SECSuccess
)
275 if (SecCmsSignedDataAddSignerInfo(sigd
, signerinfo
))
278 PORT_ArenaUnmark(poolp
, mark
);
282 PORT_ArenaRelease(poolp
, mark
);
287 * SecCmsSignerInfoDestroy - destroy a SignerInfo data structure
290 SecCmsSignerInfoDestroy(SecCmsSignerInfoRef si
)
292 if (si
->cert
!= NULL
)
293 CERT_DestroyCertificate(si
->cert
);
295 if (si
->certList
!= NULL
)
296 CFRelease(si
->certList
);
298 /* XXX storage ??? */
301 static SecAsn1AlgId
SecCertificateGetPublicKeyAlgorithmID(SecCertificateRef cert
)
303 const DERAlgorithmId
*length_data_swapped
= SecCertificateGetPublicKeyAlgorithm(cert
);
304 SecAsn1AlgId temp
= {
305 { length_data_swapped
->oid
.length
, length_data_swapped
->oid
.data
},
306 { length_data_swapped
->params
.length
, length_data_swapped
->params
.data
} };
312 * SecCmsSignerInfoSign - sign something
316 SecCmsSignerInfoSign(SecCmsSignerInfoRef signerinfo
, SecAsn1Item
* digest
, SecAsn1Item
* contentType
)
318 SecCertificateRef cert
;
319 SecPrivateKeyRef privkey
= NULL
;
320 SECOidTag digestalgtag
;
321 SECOidTag pubkAlgTag
;
322 SecAsn1Item signature
= { 0 };
324 PLArenaPool
*poolp
, *tmppoolp
= NULL
;
325 const SECAlgorithmID
*algID
= NULL
;
326 //CERTSubjectPublicKeyInfo *spki;
328 PORT_Assert (digest
!= NULL
);
330 poolp
= signerinfo
->signedData
->contentInfo
.cmsg
->poolp
;
334 switch (signerinfo
->signerIdentifier
.identifierType
) {
335 case SecCmsSignerIDIssuerSN
:
336 privkey
= signerinfo
->signingKey
;
337 signerinfo
->signingKey
= NULL
;
338 cert
= signerinfo
->cert
;
340 if (SecCertificateGetAlgorithmID(cert
,&algID
)) {
341 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM
);
345 _algID
= SecCertificateGetPublicKeyAlgorithmID(cert
);
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
);
361 if (SecKeyGetAlgorithmID(signerinfo
->pubKey
,&algID
)) {
362 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM
);
367 CFRelease(signerinfo
->pubKey
);
368 signerinfo
->pubKey
= NULL
;
371 PORT_SetError(SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE
);
374 digestalgtag
= SecCmsSignerInfoGetDigestAlgTag(signerinfo
);
375 pubkAlgTag
= SECOID_GetAlgorithmTag(algID
);
377 /* we no longer support signing with MD5 */
378 if (digestalgtag
== SEC_OID_MD5
) {
379 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM
);
384 if (signerinfo
->signerIdentifier
.identifierType
== SecCmsSignerIDSubjectKeyID
) {
385 SECOID_DestroyAlgorithmID(&freeAlgID
, PR_FALSE
);
390 /* Fortezza MISSI have weird signature formats.
391 * Map them to standard DSA formats
393 pubkAlgTag
= PK11_FortezzaMapSig(pubkAlgTag
);
396 if (signerinfo
->authAttr
!= NULL
) {
397 SecAsn1Item encoded_attrs
;
399 /* find and fill in the message digest attribute. */
400 rv
= SecCmsAttributeArraySetAttr(poolp
, &(signerinfo
->authAttr
),
401 SEC_OID_PKCS9_MESSAGE_DIGEST
, digest
, PR_FALSE
);
402 if (rv
!= SECSuccess
)
405 if (contentType
!= NULL
) {
406 /* if the caller wants us to, find and fill in the content type attribute. */
407 rv
= SecCmsAttributeArraySetAttr(poolp
, &(signerinfo
->authAttr
),
408 SEC_OID_PKCS9_CONTENT_TYPE
, contentType
, PR_FALSE
);
409 if (rv
!= SECSuccess
)
413 if ((tmppoolp
= PORT_NewArena (1024)) == NULL
) {
414 PORT_SetError(SEC_ERROR_NO_MEMORY
);
419 * Before encoding, reorder the attributes so that when they
420 * are encoded, they will be conforming DER, which is required
421 * to have a specific order and that is what must be used for
422 * the hash/signature. We do this here, rather than building
423 * it into EncodeAttributes, because we do not want to do
424 * such reordering on incoming messages (which also uses
425 * EncodeAttributes) or our old signatures (and other "broken"
426 * implementations) will not verify. So, we want to guarantee
427 * that we send out good DER encodings of attributes, but not
428 * to expect to receive them.
430 if (SecCmsAttributeArrayReorder(signerinfo
->authAttr
) != SECSuccess
)
433 encoded_attrs
.Data
= NULL
;
434 encoded_attrs
.Length
= 0;
435 if (SecCmsAttributeArrayEncode(tmppoolp
, &(signerinfo
->authAttr
),
436 &encoded_attrs
) == NULL
)
440 rv
= SEC_SignData(&signature
, encoded_attrs
.Data
, encoded_attrs
.Length
,
441 privkey
, digestalgtag
, pubkAlgTag
);
443 signature
.Length
= SecKeyGetSize(privkey
, kSecKeySignatureSize
);
444 signature
.Data
= PORT_ZAlloc(signature
.Length
);
445 if (!signature
.Data
) {
446 signature
.Length
= 0;
449 rv
= SecKeyDigestAndSign(privkey
, &signerinfo
->digestAlg
, encoded_attrs
.Data
, encoded_attrs
.Length
, signature
.Data
, &signature
.Length
);
451 PORT_ZFree(signature
.Data
, signature
.Length
);
452 signature
.Length
= 0;
456 PORT_FreeArena(tmppoolp
, PR_FALSE
); /* awkward memory management :-( */
459 signature
.Length
= SecKeyGetSize(privkey
, kSecKeySignatureSize
);
460 signature
.Data
= PORT_ZAlloc(signature
.Length
);
461 if (!signature
.Data
) {
462 signature
.Length
= 0;
465 rv
= SecKeySignDigest(privkey
, &signerinfo
->digestAlg
, digest
->Data
, digest
->Length
,
466 signature
.Data
, &signature
.Length
);
468 PORT_ZFree(signature
.Data
, signature
.Length
);
469 signature
.Length
= 0;
472 SECKEY_DestroyPrivateKey(privkey
);
475 if (rv
!= SECSuccess
)
478 if (SECITEM_CopyItem(poolp
, &(signerinfo
->encDigest
), &signature
)
482 SECITEM_FreeItem(&signature
, PR_FALSE
);
484 if (SECOID_SetAlgorithmID(poolp
, &(signerinfo
->digestEncAlg
), pubkAlgTag
,
491 if (signature
.Length
!= 0)
492 SECITEM_FreeItem (&signature
, PR_FALSE
);
494 SECKEY_DestroyPrivateKey(privkey
);
496 PORT_FreeArena(tmppoolp
, PR_FALSE
);
502 SecCmsSignerInfoCopySigningCertificates(SecCmsSignerInfoRef signerinfo
)
504 CFMutableArrayRef certs
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
505 SecAsn1Item
**cert_datas
= signerinfo
->signedData
->rawCerts
;
506 SecAsn1Item
*cert_data
;
507 if (cert_datas
) while ((cert_data
= *cert_datas
) != NULL
) {
508 SecCertificateRef cert
= SecCertificateCreateWithBytes(NULL
, cert_data
->Data
, cert_data
->Length
);
510 switch (signerinfo
->signerIdentifier
.identifierType
) {
511 case SecCmsSignerIDIssuerSN
:
512 if (CERT_CheckIssuerAndSerial(cert
,
513 &(signerinfo
->signerIdentifier
.id
.issuerAndSN
->derIssuer
),
514 &(signerinfo
->signerIdentifier
.id
.issuerAndSN
->serialNumber
)))
515 CFArrayInsertValueAtIndex(certs
, 0, cert
);
517 CFArrayAppendValue(certs
, cert
);
519 case SecCmsSignerIDSubjectKeyID
:
521 CFDataRef cert_keyid
= SecCertificateGetSubjectKeyID(cert
);
522 SecAsn1Item
*tbf_keyid
= signerinfo
->signerIdentifier
.id
.subjectKeyID
;
523 if (tbf_keyid
->Length
== (size_t)CFDataGetLength(cert_keyid
) &&
524 !memcmp(tbf_keyid
->Data
, CFDataGetBytePtr(cert_keyid
), tbf_keyid
->Length
))
525 CFArrayInsertValueAtIndex(certs
, 0, cert
);
527 CFArrayAppendValue(certs
, cert
);
536 if ((CFArrayGetCount(certs
) == 0) &&
537 (signerinfo
->signerIdentifier
.identifierType
== SecCmsSignerIDIssuerSN
))
539 SecCertificateRef cert
= CERT_FindCertificateByIssuerAndSN(signerinfo
->signedData
->certs
, signerinfo
->signerIdentifier
.id
.issuerAndSN
);
541 CFArrayAppendValue(certs
, cert
);
550 SecCmsSignerInfoVerifyCertificate(SecCmsSignerInfoRef signerinfo
, SecKeychainRef keychainOrArray
,
551 CFTypeRef policies
, SecTrustRef
*trustRef
)
553 CFAbsoluteTime stime
;
557 SecCertificateRef cert
;
559 if ((cert
= SecCmsSignerInfoGetSigningCertificate(signerinfo
, keychainOrArray
)) == NULL
) {
563 if ((certs
= SecCmsSignerInfoCopySigningCertificates(signerinfo
)) == NULL
) {
565 signerinfo
->verificationStatus
= SecCmsVSSigningCertNotFound
;
569 * Get and convert the signing time; if available, it will be used
570 * both on the cert verification and for importing the sender
573 if (SecCmsSignerInfoGetSigningTime(signerinfo
, &stime
) != SECSuccess
)
574 stime
= CFAbsoluteTimeGetCurrent();
577 rv
= CERT_VerifyCert(keychainOrArray
, cert
, policies
, stime
, trustRef
);
579 rv
= CERT_VerifyCert(keychainOrArray
, certs
, policies
, stime
, trustRef
);
584 if (PORT_GetError() == SEC_ERROR_UNTRUSTED_CERT
)
586 /* Signature or digest level verificationStatus errors should supercede certificate level errors, so only change the verificationStatus if the status was GoodSignature. */
588 #warning DEBUG - SecCmsSignerInfoVerifyCertificate trusts everything!
589 if (signerinfo
->verificationStatus
== SecCmsVSGoodSignature
) {
590 syslog(LOG_ERR
, "SecCmsSignerInfoVerifyCertificate ignoring SEC_ERROR_UNTRUSTED_CERT");
594 if (signerinfo
->verificationStatus
== SecCmsVSGoodSignature
)
595 signerinfo
->verificationStatus
= SecCmsVSSigningCertNotTrusted
;
604 * SecCmsSignerInfoVerify - verify the signature of a single SignerInfo
606 * Just verifies the signature. The assumption is that verification of the certificate
610 SecCmsSignerInfoVerify(SecCmsSignerInfoRef signerinfo
, SecAsn1Item
* digest
, SecAsn1Item
* contentType
)
612 SecPublicKeyRef publickey
= NULL
;
613 SecCmsAttribute
*attr
;
614 SecAsn1Item encoded_attrs
;
615 SecCertificateRef cert
;
616 SecCmsVerificationStatus vs
= SecCmsVSUnverified
;
618 SECOidTag digestAlgTag
, digestEncAlgTag
;
620 if (signerinfo
== NULL
)
623 /* SecCmsSignerInfoGetSigningCertificate will fail if 2nd parm is NULL and */
624 /* cert has not been verified */
625 if ((cert
= SecCmsSignerInfoGetSigningCertificate(signerinfo
, NULL
)) == NULL
) {
626 vs
= SecCmsVSSigningCertNotFound
;
631 if (SecCertificateCopyPublicKey(cert
, &publickey
)) {
632 vs
= SecCmsVSProcessingError
;
636 publickey
= SecCertificateCopyPublicKey(cert
);
637 if (publickey
== NULL
)
641 digestAlgTag
= SECOID_GetAlgorithmTag(&(signerinfo
->digestAlg
));
642 digestEncAlgTag
= SECOID_GetAlgorithmTag(&(signerinfo
->digestEncAlg
));
643 if (!SecCmsArrayIsEmpty((void **)signerinfo
->authAttr
)) {
648 * RFC2630 sez that if there are any authenticated attributes,
649 * then there must be one for content type which matches the
650 * content type of the content being signed, and there must
651 * be one for message digest which matches our message digest.
652 * So check these things first.
654 if ((attr
= SecCmsAttributeArrayFindAttrByOidTag(signerinfo
->authAttr
,
655 SEC_OID_PKCS9_CONTENT_TYPE
, PR_TRUE
)) == NULL
)
657 vs
= SecCmsVSMalformedSignature
;
661 if (SecCmsAttributeCompareValue(attr
, contentType
) == PR_FALSE
) {
662 vs
= SecCmsVSMalformedSignature
;
670 if ((attr
= SecCmsAttributeArrayFindAttrByOidTag(signerinfo
->authAttr
, SEC_OID_PKCS9_MESSAGE_DIGEST
, PR_TRUE
)) == NULL
)
672 vs
= SecCmsVSMalformedSignature
;
675 if (SecCmsAttributeCompareValue(attr
, digest
) == PR_FALSE
) {
676 vs
= SecCmsVSDigestMismatch
;
680 if ((poolp
= PORT_NewArena (1024)) == NULL
) {
681 vs
= SecCmsVSProcessingError
;
688 * The signature is based on a digest of the DER-encoded authenticated
689 * attributes. So, first we encode and then we digest/verify.
690 * we trust the decoder to have the attributes in the right (sorted) order
692 encoded_attrs
.Data
= NULL
;
693 encoded_attrs
.Length
= 0;
695 if (SecCmsAttributeArrayEncode(poolp
, &(signerinfo
->authAttr
), &encoded_attrs
) == NULL
||
696 encoded_attrs
.Data
== NULL
|| encoded_attrs
.Length
== 0)
698 vs
= SecCmsVSProcessingError
;
701 if (errSecSuccess
== SecKeyDigestAndVerify(publickey
, &signerinfo
->digestAlg
, encoded_attrs
.Data
, encoded_attrs
.Length
, signerinfo
->encDigest
.Data
, signerinfo
->encDigest
.Length
))
702 vs
= SecCmsVSGoodSignature
;
704 vs
= SecCmsVSBadSignature
;
706 PORT_FreeArena(poolp
, PR_FALSE
); /* awkward memory management :-( */
711 /* No authenticated attributes. The signature is based on the plain message digest. */
712 sig
= &(signerinfo
->encDigest
);
713 if (sig
->Length
== 0)
716 if (SecKeyVerifyDigest(publickey
, &signerinfo
->digestAlg
, digest
->Data
, digest
->Length
, sig
->Data
, sig
->Length
))
717 vs
= SecCmsVSBadSignature
;
719 vs
= SecCmsVSGoodSignature
;
722 if (vs
== SecCmsVSBadSignature
) {
724 * XXX Change the generic error into our specific one, because
725 * in that case we get a better explanation out of the Security
726 * Advisor. This is really a bug in our error strings (the
727 * "generic" error has a lousy/wrong message associated with it
728 * which assumes the signature verification was done for the
729 * purposes of checking the issuer signature on a certificate)
730 * but this is at least an easy workaround and/or in the
731 * Security Advisor, which specifically checks for the error
732 * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation
733 * in that case but does not similarly check for
734 * SEC_ERROR_BAD_SIGNATURE. It probably should, but then would
735 * probably say the wrong thing in the case that it *was* the
736 * certificate signature check that failed during the cert
737 * verification done above. Our error handling is really a mess.
739 if (PORT_GetError() == SEC_ERROR_BAD_SIGNATURE
)
740 PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE
);
743 if (publickey
!= NULL
)
744 CFRelease(publickey
);
746 signerinfo
->verificationStatus
= vs
;
748 return (vs
== SecCmsVSGoodSignature
) ? SECSuccess
: SECFailure
;
751 if (publickey
!= NULL
)
752 SECKEY_DestroyPublicKey (publickey
);
754 signerinfo
->verificationStatus
= vs
;
756 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE
);
760 SecCmsVerificationStatus
761 SecCmsSignerInfoGetVerificationStatus(SecCmsSignerInfoRef signerinfo
)
763 return signerinfo
->verificationStatus
;
767 SecCmsSignerInfoGetDigestAlg(SecCmsSignerInfoRef signerinfo
)
769 return SECOID_FindOID (&(signerinfo
->digestAlg
.algorithm
));
773 SecCmsSignerInfoGetDigestAlgTag(SecCmsSignerInfoRef signerinfo
)
777 algdata
= SECOID_FindOID (&(signerinfo
->digestAlg
.algorithm
));
779 return algdata
->offset
;
781 return SEC_OID_UNKNOWN
;
785 SecCmsSignerInfoGetCertList(SecCmsSignerInfoRef signerinfo
)
787 return signerinfo
->certList
;
791 SecCmsSignerInfoGetVersion(SecCmsSignerInfoRef signerinfo
)
793 unsigned long version
;
795 /* always take apart the SecAsn1Item */
796 if (SEC_ASN1DecodeInteger(&(signerinfo
->version
), &version
) != SECSuccess
)
803 * SecCmsSignerInfoGetSigningTime - return the signing time,
804 * in UTCTime format, of a CMS signerInfo.
806 * sinfo - signerInfo data for this signer
808 * Returns a pointer to XXXX (what?)
809 * A return value of NULL is an error.
812 SecCmsSignerInfoGetSigningTime(SecCmsSignerInfoRef sinfo
, CFAbsoluteTime
*stime
)
814 SecCmsAttribute
*attr
;
820 if (sinfo
->signingTime
!= 0) {
821 *stime
= sinfo
->signingTime
; /* cached copy */
825 attr
= SecCmsAttributeArrayFindAttrByOidTag(sinfo
->authAttr
, SEC_OID_PKCS9_SIGNING_TIME
, PR_TRUE
);
826 /* XXXX multi-valued attributes NIH */
827 if (attr
== NULL
|| (value
= SecCmsAttributeGetValue(attr
)) == NULL
)
829 if (DER_UTCTimeToCFDate(value
, stime
) != SECSuccess
)
831 sinfo
->signingTime
= *stime
; /* make cached copy */
837 @abstract Return the data in the signed Codesigning Hash Agility attribute.
838 @param sinfo SignerInfo data for this signer, pointer to a CFDataRef for attribute value
839 @discussion Returns a CFDataRef containing the value of the attribute
840 @result A return value of errSecInternal is an error trying to look up the oid.
841 A status value of success with null result data indicates the attribute was not present.
844 SecCmsSignerInfoGetAppleCodesigningHashAgility(SecCmsSignerInfoRef sinfo
, CFDataRef
*sdata
)
846 SecCmsAttribute
*attr
;
849 if (sinfo
== NULL
|| sdata
== NULL
)
854 if (sinfo
->hashAgilityAttrValue
!= NULL
) {
855 *sdata
= sinfo
->hashAgilityAttrValue
; /* cached copy */
859 attr
= SecCmsAttributeArrayFindAttrByOidTag(sinfo
->authAttr
, SEC_OID_APPLE_HASH_AGILITY
, PR_TRUE
);
861 /* attribute not found */
862 if (attr
== NULL
|| (value
= SecCmsAttributeGetValue(attr
)) == NULL
)
865 sinfo
->hashAgilityAttrValue
= CFDataCreate(NULL
, value
->Data
, value
->Length
); /* make cached copy */
866 if (sinfo
->hashAgilityAttrValue
) {
867 *sdata
= sinfo
->hashAgilityAttrValue
;
870 return errSecAllocate
;
874 * Return the signing cert of a CMS signerInfo.
876 * the certs in the enclosing SignedData must have been imported already
879 SecCmsSignerInfoGetSigningCertificate(SecCmsSignerInfoRef signerinfo
, SecKeychainRef keychainOrArray
)
881 SecCertificateRef cert
= NULL
;
883 if (signerinfo
->cert
!= NULL
)
884 return signerinfo
->cert
;
886 /* @@@ Make sure we search though all the certs in the cms message itself as well, it's silly
887 to require them to be added to a keychain first. */
890 SecCmsSignerIdentifier
*sid
;
893 * This cert will also need to be freed, but since we save it
894 * in signerinfo for later, we do not want to destroy it when
895 * we leave this function -- we let the clean-up of the entire
896 * cinfo structure later do the destroy of this cert.
898 sid
= &signerinfo
->signerIdentifier
;
899 switch (sid
->identifierType
) {
900 case SecCmsSignerIDIssuerSN
:
901 cert
= CERT_FindCertByIssuerAndSN(keychainOrArray
, sid
->id
.issuerAndSN
);
903 case SecCmsSignerIDSubjectKeyID
:
904 cert
= CERT_FindCertBySubjectKeyID(keychainOrArray
, sid
->id
.subjectKeyID
);
911 /* cert can be NULL at that point */
912 signerinfo
->cert
= cert
; /* earmark it */
914 SecAsn1Item
**cert_datas
= signerinfo
->signedData
->rawCerts
;
915 SecAsn1Item
*cert_data
;
916 if (cert_datas
) while ((cert_data
= *cert_datas
) != NULL
) {
917 cert
= SecCertificateCreateWithBytes(NULL
, cert_data
->Data
, cert_data
->Length
);
919 switch (signerinfo
->signerIdentifier
.identifierType
) {
920 case SecCmsSignerIDIssuerSN
:
921 if (CERT_CheckIssuerAndSerial(cert
,
922 &(signerinfo
->signerIdentifier
.id
.issuerAndSN
->derIssuer
),
923 &(signerinfo
->signerIdentifier
.id
.issuerAndSN
->serialNumber
)))
924 signerinfo
->cert
= cert
;
926 case SecCmsSignerIDSubjectKeyID
: {
927 CFDataRef cert_keyid
= SecCertificateGetSubjectKeyID(cert
);
928 SecAsn1Item
*tbf_keyid
= signerinfo
->signerIdentifier
.id
.subjectKeyID
;
929 if (tbf_keyid
->Length
== (size_t)CFDataGetLength(cert_keyid
) &&
930 !memcmp(tbf_keyid
->Data
, CFDataGetBytePtr(cert_keyid
), tbf_keyid
->Length
))
931 signerinfo
->cert
= cert
;
934 if (signerinfo
->cert
)
941 if (!signerinfo
->cert
&& (signerinfo
->signerIdentifier
.identifierType
== SecCmsSignerIDIssuerSN
)) {
942 cert
= CERT_FindCertificateByIssuerAndSN(signerinfo
->signedData
->certs
, signerinfo
->signerIdentifier
.id
.issuerAndSN
);
943 signerinfo
->cert
= cert
;
952 * SecCmsSignerInfoGetSignerCommonName - return the common name of the signer
954 * sinfo - signerInfo data for this signer
956 * Returns a CFStringRef containing the common name of the signer.
957 * A return value of NULL is an error.
960 SecCmsSignerInfoGetSignerCommonName(SecCmsSignerInfoRef sinfo
)
962 SecCertificateRef signercert
;
963 CFStringRef commonName
= NULL
;
965 /* will fail if cert is not verified */
966 if ((signercert
= SecCmsSignerInfoGetSigningCertificate(sinfo
, NULL
)) == NULL
)
970 SecCertificateGetCommonName(signercert
, &commonName
);
972 CFArrayRef commonNames
= SecCertificateCopyCommonNames(signercert
);
974 /* SecCertificateCopyCommonNames doesn't return empty arrays */
975 commonName
= (CFStringRef
)CFArrayGetValueAtIndex(commonNames
, CFArrayGetCount(commonNames
) - 1);
976 CFRetain(commonName
);
977 CFRelease(commonNames
);
985 * SecCmsSignerInfoGetSignerEmailAddress - return the email address of the signer
987 * sinfo - signerInfo data for this signer
989 * Returns a CFStringRef containing the name of the signer.
990 * A return value of NULL is an error.
993 SecCmsSignerInfoGetSignerEmailAddress(SecCmsSignerInfoRef sinfo
)
995 SecCertificateRef signercert
;
996 CFStringRef emailAddress
= NULL
;
998 if ((signercert
= SecCmsSignerInfoGetSigningCertificate(sinfo
, NULL
)) == NULL
)
1002 SecCertificateGetEmailAddress(signercert
, &emailAddress
);
1004 CFArrayRef names
= SecCertificateCopyRFC822Names(signercert
);
1006 if (CFArrayGetCount(names
) > 0)
1007 emailAddress
= (CFStringRef
)CFArrayGetValueAtIndex(names
, 0);
1009 CFRetain(emailAddress
);
1013 return emailAddress
;
1018 * SecCmsSignerInfoAddAuthAttr - add an attribute to the
1019 * authenticated (i.e. signed) attributes of "signerinfo".
1022 SecCmsSignerInfoAddAuthAttr(SecCmsSignerInfoRef signerinfo
, SecCmsAttribute
*attr
)
1024 return SecCmsAttributeArrayAddAttr(signerinfo
->signedData
->contentInfo
.cmsg
->poolp
, &(signerinfo
->authAttr
), attr
);
1028 * SecCmsSignerInfoAddUnauthAttr - add an attribute to the
1029 * unauthenticated attributes of "signerinfo".
1032 SecCmsSignerInfoAddUnauthAttr(SecCmsSignerInfoRef signerinfo
, SecCmsAttribute
*attr
)
1034 return SecCmsAttributeArrayAddAttr(signerinfo
->signedData
->contentInfo
.cmsg
->poolp
, &(signerinfo
->unAuthAttr
), attr
);
1038 * SecCmsSignerInfoAddSigningTime - add the signing time to the
1039 * authenticated (i.e. signed) attributes of "signerinfo".
1041 * This is expected to be included in outgoing signed
1042 * messages for email (S/MIME) but is likely useful in other situations.
1044 * This should only be added once; a second call will do nothing.
1046 * XXX This will probably just shove the current time into "signerinfo"
1047 * but it will not actually get signed until the entire item is
1048 * processed for encoding. Is this (expected to be small) delay okay?
1051 SecCmsSignerInfoAddSigningTime(SecCmsSignerInfoRef signerinfo
, CFAbsoluteTime t
)
1053 SecCmsAttribute
*attr
;
1058 poolp
= signerinfo
->signedData
->contentInfo
.cmsg
->poolp
;
1060 mark
= PORT_ArenaMark(poolp
);
1062 /* create new signing time attribute */
1063 if (DER_CFDateToUTCTime(t
, &stime
) != SECSuccess
)
1066 if ((attr
= SecCmsAttributeCreate(poolp
, SEC_OID_PKCS9_SIGNING_TIME
, &stime
, PR_FALSE
)) == NULL
) {
1067 SECITEM_FreeItem (&stime
, PR_FALSE
);
1071 SECITEM_FreeItem (&stime
, PR_FALSE
);
1073 if (SecCmsSignerInfoAddAuthAttr(signerinfo
, attr
) != SECSuccess
)
1076 PORT_ArenaUnmark (poolp
, mark
);
1081 PORT_ArenaRelease (poolp
, mark
);
1086 * SecCmsSignerInfoAddSMIMECaps - add a SMIMECapabilities attribute to the
1087 * authenticated (i.e. signed) attributes of "signerinfo".
1089 * This is expected to be included in outgoing signed
1090 * messages for email (S/MIME).
1093 SecCmsSignerInfoAddSMIMECaps(SecCmsSignerInfoRef signerinfo
)
1095 SecCmsAttribute
*attr
;
1096 SecAsn1Item
* smimecaps
= NULL
;
1100 poolp
= signerinfo
->signedData
->contentInfo
.cmsg
->poolp
;
1102 mark
= PORT_ArenaMark(poolp
);
1104 smimecaps
= SECITEM_AllocItem(poolp
, NULL
, 0);
1105 if (smimecaps
== NULL
)
1108 /* create new signing time attribute */
1110 // @@@ We don't do Fortezza yet.
1111 if (SecSMIMECreateSMIMECapabilities(poolp
, smimecaps
, PR_FALSE
) != SECSuccess
)
1113 if (SecSMIMECreateSMIMECapabilities(poolp
, smimecaps
,
1114 PK11_FortezzaHasKEA(signerinfo
->cert
)) != SECSuccess
)
1118 if ((attr
= SecCmsAttributeCreate(poolp
, SEC_OID_PKCS9_SMIME_CAPABILITIES
, smimecaps
, PR_TRUE
)) == NULL
)
1121 if (SecCmsSignerInfoAddAuthAttr(signerinfo
, attr
) != SECSuccess
)
1124 PORT_ArenaUnmark (poolp
, mark
);
1128 PORT_ArenaRelease (poolp
, mark
);
1133 * SecCmsSignerInfoAddSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
1134 * authenticated (i.e. signed) attributes of "signerinfo".
1136 * This is expected to be included in outgoing signed messages for email (S/MIME).
1139 SecCmsSignerInfoAddSMIMEEncKeyPrefs(SecCmsSignerInfoRef signerinfo
, SecCertificateRef cert
, SecKeychainRef keychainOrArray
)
1141 SecCmsAttribute
*attr
;
1142 SecAsn1Item
* smimeekp
= NULL
;
1149 /* verify this cert for encryption */
1150 policy
= CERT_PolicyForCertUsage(certUsageEmailRecipient
);
1151 if (CERT_VerifyCert(keychainOrArray
, cert
, policy
, CFAbsoluteTimeGetCurrent(), NULL
) != SECSuccess
) {
1158 poolp
= signerinfo
->signedData
->contentInfo
.cmsg
->poolp
;
1159 mark
= PORT_ArenaMark(poolp
);
1161 smimeekp
= SECITEM_AllocItem(poolp
, NULL
, 0);
1162 if (smimeekp
== NULL
)
1165 /* create new signing time attribute */
1166 if (SecSMIMECreateSMIMEEncKeyPrefs(poolp
, smimeekp
, cert
) != SECSuccess
)
1169 if ((attr
= SecCmsAttributeCreate(poolp
, SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE
, smimeekp
, PR_TRUE
)) == NULL
)
1172 if (SecCmsSignerInfoAddAuthAttr(signerinfo
, attr
) != SECSuccess
)
1175 PORT_ArenaUnmark (poolp
, mark
);
1179 PORT_ArenaRelease (poolp
, mark
);
1184 * SecCmsSignerInfoAddMSSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
1185 * authenticated (i.e. signed) attributes of "signerinfo", using the OID prefered by Microsoft.
1187 * This is expected to be included in outgoing signed messages for email (S/MIME),
1188 * if compatibility with Microsoft mail clients is wanted.
1191 SecCmsSignerInfoAddMSSMIMEEncKeyPrefs(SecCmsSignerInfoRef signerinfo
, SecCertificateRef cert
, SecKeychainRef keychainOrArray
)
1193 SecCmsAttribute
*attr
;
1194 SecAsn1Item
* smimeekp
= NULL
;
1201 /* verify this cert for encryption */
1202 policy
= CERT_PolicyForCertUsage(certUsageEmailRecipient
);
1203 if (CERT_VerifyCert(keychainOrArray
, cert
, policy
, CFAbsoluteTimeGetCurrent(), NULL
) != SECSuccess
) {
1210 poolp
= signerinfo
->signedData
->contentInfo
.cmsg
->poolp
;
1211 mark
= PORT_ArenaMark(poolp
);
1213 smimeekp
= SECITEM_AllocItem(poolp
, NULL
, 0);
1214 if (smimeekp
== NULL
)
1217 /* create new signing time attribute */
1218 if (SecSMIMECreateMSSMIMEEncKeyPrefs(poolp
, smimeekp
, cert
) != SECSuccess
)
1221 if ((attr
= SecCmsAttributeCreate(poolp
, SEC_OID_MS_SMIME_ENCRYPTION_KEY_PREFERENCE
, smimeekp
, PR_TRUE
)) == NULL
)
1224 if (SecCmsSignerInfoAddAuthAttr(signerinfo
, attr
) != SECSuccess
)
1227 PORT_ArenaUnmark (poolp
, mark
);
1231 PORT_ArenaRelease (poolp
, mark
);
1236 * SecCmsSignerInfoAddCounterSignature - countersign a signerinfo
1238 * 1. digest the DER-encoded signature value of the original signerinfo
1239 * 2. create new signerinfo with correct version, sid, digestAlg
1240 * 3. add message-digest authAttr, but NO content-type
1241 * 4. sign the authAttrs
1242 * 5. DER-encode the new signerInfo
1243 * 6. add the whole thing to original signerInfo's unAuthAttrs
1244 * as a SEC_OID_PKCS9_COUNTER_SIGNATURE attribute
1246 * XXXX give back the new signerinfo?
1249 SecCmsSignerInfoAddCounterSignature(SecCmsSignerInfoRef signerinfo
,
1250 SECOidTag digestalg
, SecIdentityRef identity
)
1258 @abstract Add the Apple Codesigning Hash Agility attribute to the authenticated (i.e. signed) attributes of "signerinfo".
1259 @discussion This is expected to be included in outgoing signed Apple code signatures.
1262 SecCmsSignerInfoAddAppleCodesigningHashAgility(SecCmsSignerInfoRef signerinfo
, CFDataRef attrValue
)
1264 SecCmsAttribute
*attr
;
1265 PLArenaPool
*poolp
= signerinfo
->signedData
->contentInfo
.cmsg
->poolp
;
1266 void *mark
= PORT_ArenaMark(poolp
);
1267 OSStatus status
= SECFailure
;
1269 /* The value is required for this attribute. */
1271 status
= errSecParam
;
1276 * SecCmsAttributeCreate makes a copy of the data in value, so
1277 * we don't need to copy into the CSSM_DATA struct.
1280 value
.Length
= CFDataGetLength(attrValue
);
1281 value
.Data
= (uint8_t *)CFDataGetBytePtr(attrValue
);
1283 if ((attr
= SecCmsAttributeCreate(poolp
,
1284 SEC_OID_APPLE_HASH_AGILITY
,
1286 PR_FALSE
)) == NULL
) {
1287 status
= errSecAllocate
;
1291 if (SecCmsSignerInfoAddAuthAttr(signerinfo
, attr
) != SECSuccess
) {
1292 status
= errSecInternal
;
1296 PORT_ArenaUnmark(poolp
, mark
);
1300 PORT_ArenaRelease(poolp
, mark
);
1305 * XXXX the following needs to be done in the S/MIME layer code
1306 * after signature of a signerinfo is verified
1309 SecCmsSignerInfoSaveSMIMEProfile(SecCmsSignerInfoRef signerinfo
)
1311 return -4 /*unImp*/;
1315 * SecCmsSignerInfoIncludeCerts - set cert chain inclusion mode for this signer
1318 SecCmsSignerInfoIncludeCerts(SecCmsSignerInfoRef signerinfo
, SecCmsCertChainMode cm
, SECCertUsage usage
)
1320 if (signerinfo
->cert
== NULL
)
1323 /* don't leak if we get called twice */
1324 if (signerinfo
->certList
!= NULL
) {
1325 CFRelease(signerinfo
->certList
);
1326 signerinfo
->certList
= NULL
;
1331 signerinfo
->certList
= NULL
;
1333 case SecCmsCMCertOnly
:
1334 signerinfo
->certList
= CERT_CertListFromCert(signerinfo
->cert
);
1336 case SecCmsCMCertChain
:
1337 signerinfo
->certList
= CERT_CertChainFromCert(signerinfo
->cert
, usage
, PR_FALSE
);
1339 case SecCmsCMCertChainWithRoot
:
1340 signerinfo
->certList
= CERT_CertChainFromCert(signerinfo
->cert
, usage
, PR_TRUE
);
1344 if (cm
!= SecCmsCMNone
&& signerinfo
->certList
== NULL
)