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 * Stuff specific to S/MIME policy and interoperability.
41 #include "SecAsn1Item.h"
43 #include "SecSMIMEPriv.h"
45 #include <security_asn1/secasn1.h>
46 #include <security_asn1/secerr.h>
47 #include <Security/SecSMIME.h>
48 #include <Security/SecKeyPriv.h>
49 #include <Security/SecCertificatePriv.h>
51 SEC_ASN1_MKSUB(CERT_IssuerAndSNTemplate
)
52 SEC_ASN1_MKSUB(SEC_OctetStringTemplate
)
53 SEC_ASN1_CHOOSER_DECLARE(CERT_IssuerAndSNTemplate
)
55 /* various integer's ASN.1 encoding */
56 static unsigned char asn1_int40
[] = { SEC_ASN1_INTEGER
, 0x01, 0x28 };
57 static unsigned char asn1_int64
[] = { SEC_ASN1_INTEGER
, 0x01, 0x40 };
58 static unsigned char asn1_int128
[] = { SEC_ASN1_INTEGER
, 0x02, 0x00, 0x80 };
60 /* RC2 algorithm parameters (used in smime_cipher_map) */
61 static SecAsn1Item param_int40
= { sizeof(asn1_int40
), asn1_int40
};
62 static SecAsn1Item param_int64
= { sizeof(asn1_int64
), asn1_int64
};
63 static SecAsn1Item param_int128
= { sizeof(asn1_int128
), asn1_int128
};
66 * XXX Would like the "parameters" field to be a SecAsn1Item * , but the
67 * encoder is having trouble with optional pointers to an ANY. Maybe
68 * once that is fixed, can change this back...
71 SecAsn1Item capabilityID
;
72 SecAsn1Item parameters
;
73 long cipher
; /* optimization */
76 static const SecAsn1Template NSSSMIMECapabilityTemplate
[] = {
78 0, NULL
, sizeof(NSSSMIMECapability
) },
80 offsetof(NSSSMIMECapability
,capabilityID
), },
81 { SEC_ASN1_OPTIONAL
| SEC_ASN1_ANY
,
82 offsetof(NSSSMIMECapability
,parameters
), },
86 static const SecAsn1Template NSSSMIMECapabilitiesTemplate
[] = {
87 { SEC_ASN1_SEQUENCE_OF
, 0, NSSSMIMECapabilityTemplate
}
91 * NSSSMIMEEncryptionKeyPreference - if we find one of these, it needs to prompt us
92 * to store this and only this certificate permanently for the sender email address.
95 NSSSMIMEEncryptionKeyPref_IssuerSN
,
96 NSSSMIMEEncryptionKeyPref_RKeyID
,
97 NSSSMIMEEncryptionKeyPref_SubjectKeyID
98 } NSSSMIMEEncryptionKeyPrefSelector
;
101 NSSSMIMEEncryptionKeyPrefSelector selector
;
103 SecCmsIssuerAndSN
*issuerAndSN
;
104 SecCmsRecipientKeyIdentifier
*recipientKeyID
;
105 SecAsn1Item
*subjectKeyID
;
107 } NSSSMIMEEncryptionKeyPreference
;
109 extern const SecAsn1Template SecCmsRecipientKeyIdentifierTemplate
[];
111 static const SecAsn1Template smime_encryptionkeypref_template
[] = {
113 offsetof(NSSSMIMEEncryptionKeyPreference
,selector
), NULL
,
114 sizeof(NSSSMIMEEncryptionKeyPreference
) },
115 { SEC_ASN1_POINTER
| SEC_ASN1_CONTEXT_SPECIFIC
| SEC_ASN1_XTRN
| 0,
116 offsetof(NSSSMIMEEncryptionKeyPreference
,id
.issuerAndSN
),
117 SEC_ASN1_SUB(SecCmsIssuerAndSNTemplate
),
118 NSSSMIMEEncryptionKeyPref_IssuerSN
},
119 { SEC_ASN1_POINTER
| SEC_ASN1_CONTEXT_SPECIFIC
| 1,
120 offsetof(NSSSMIMEEncryptionKeyPreference
,id
.recipientKeyID
),
121 SecCmsRecipientKeyIdentifierTemplate
,
122 NSSSMIMEEncryptionKeyPref_IssuerSN
},
123 { SEC_ASN1_POINTER
| SEC_ASN1_CONTEXT_SPECIFIC
| SEC_ASN1_XTRN
| 2,
124 offsetof(NSSSMIMEEncryptionKeyPreference
,id
.subjectKeyID
),
125 SEC_ASN1_SUB(kSecAsn1OctetStringTemplate
),
126 NSSSMIMEEncryptionKeyPref_SubjectKeyID
},
130 /* smime_cipher_map - map of SMIME symmetric "ciphers" to algtag & parameters */
132 unsigned long cipher
;
135 Boolean enabled
; /* in the user's preferences */
136 Boolean allowed
; /* per export policy */
137 } smime_cipher_map_entry
;
139 /* global: list of supported SMIME symmetric ciphers, ordered roughly by increasing strength */
140 static smime_cipher_map_entry smime_cipher_map
[] = {
141 /* cipher algtag parms enabled allowed */
142 /* ---------------------------------------------------------------------------------- */
143 { SMIME_RC2_CBC_40
, SEC_OID_RC2_CBC
, ¶m_int40
, PR_FALSE
, PR_FALSE
},
144 { SMIME_DES_CBC_56
, SEC_OID_DES_CBC
, NULL
, PR_TRUE
, PR_FALSE
},
145 { SMIME_RC2_CBC_64
, SEC_OID_RC2_CBC
, ¶m_int64
, PR_FALSE
, PR_FALSE
},
146 { SMIME_RC2_CBC_128
, SEC_OID_RC2_CBC
, ¶m_int128
, PR_FALSE
, PR_FALSE
},
147 { SMIME_DES_EDE3_168
, SEC_OID_DES_EDE3_CBC
, NULL
, PR_TRUE
, PR_TRUE
},
148 { SMIME_AES_CBC_128
, SEC_OID_AES_128_CBC
, NULL
, PR_TRUE
, PR_TRUE
},
149 { SMIME_AES_CBC_192
, SEC_OID_AES_192_CBC
, NULL
, PR_TRUE
, PR_TRUE
},
150 { SMIME_AES_CBC_256
, SEC_OID_AES_256_CBC
, NULL
, PR_TRUE
, PR_TRUE
},
151 { SMIME_FORTEZZA
, SEC_OID_FORTEZZA_SKIPJACK
, NULL
, PR_TRUE
, PR_TRUE
}
153 static const int smime_cipher_map_count
= sizeof(smime_cipher_map
) / sizeof(smime_cipher_map_entry
);
156 * smime_mapi_by_cipher - find index into smime_cipher_map by cipher
159 smime_mapi_by_cipher(unsigned long cipher
)
163 for (i
= 0; i
< smime_cipher_map_count
; i
++) {
164 if (smime_cipher_map
[i
].cipher
== cipher
)
165 return i
; /* bingo */
167 return -1; /* should not happen if we're consistent, right? */
171 * NSS_SMIME_EnableCipher - this function locally records the user's preference
174 SecSMIMEEnableCipher(unsigned long which
, Boolean on
)
179 mask
= which
& CIPHER_FAMILYID_MASK
;
181 PORT_Assert (mask
== CIPHER_FAMILYID_SMIME
);
182 if (mask
!= CIPHER_FAMILYID_SMIME
)
183 /* XXX set an error! */
186 mapi
= smime_mapi_by_cipher(which
);
188 /* XXX set an error */
191 /* do we try to turn on a forbidden cipher? */
192 if (!smime_cipher_map
[mapi
].allowed
&& on
) {
193 PORT_SetError (SEC_ERROR_BAD_EXPORT_ALGORITHM
);
197 smime_cipher_map
[mapi
].enabled
= on
;
204 * this function locally records the export policy
207 SecSMIMEAllowCipher(unsigned long which
, Boolean on
)
212 mask
= which
& CIPHER_FAMILYID_MASK
;
214 PORT_Assert (mask
== CIPHER_FAMILYID_SMIME
);
215 if (mask
!= CIPHER_FAMILYID_SMIME
)
216 /* XXX set an error! */
219 mapi
= smime_mapi_by_cipher(which
);
221 /* XXX set an error */
224 smime_cipher_map
[mapi
].allowed
= on
;
230 * Based on the given algorithm (including its parameters, in some cases!)
231 * and the given key (may or may not be inspected, depending on the
232 * algorithm), find the appropriate policy algorithm specification
233 * and return it. If no match can be made, -1 is returned.
236 nss_smime_get_cipher_for_alg_and_key(SECAlgorithmID
*algid
, SecSymmetricKeyRef key
, unsigned long *cipher
)
242 algtag
= SECOID_GetAlgorithmTag(algid
);
244 case SEC_OID_RC2_CBC
:
246 if (SecKeyGetStrengthInBits(key
, algid
, &keylen_bits
))
249 keylen_bits
= CFDataGetLength((CFDataRef
)key
) * 8;
251 switch (keylen_bits
) {
253 c
= SMIME_RC2_CBC_40
;
256 c
= SMIME_RC2_CBC_64
;
259 c
= SMIME_RC2_CBC_128
;
265 case SEC_OID_DES_CBC
:
266 c
= SMIME_DES_CBC_56
;
268 case SEC_OID_FORTEZZA_SKIPJACK
:
271 case SEC_OID_DES_EDE3_CBC
:
272 c
= SMIME_DES_EDE3_168
;
274 case SEC_OID_AES_128_CBC
:
275 c
= SMIME_AES_CBC_128
;
277 case SEC_OID_AES_192_CBC
:
278 c
= SMIME_AES_CBC_192
;
280 case SEC_OID_AES_256_CBC
:
281 c
= SMIME_AES_CBC_256
;
291 nss_smime_cipher_allowed(unsigned long which
)
295 mapi
= smime_mapi_by_cipher(which
);
298 return smime_cipher_map
[mapi
].allowed
;
302 SecSMIMEDecryptionAllowed(SECAlgorithmID
*algid
, SecSymmetricKeyRef key
)
306 if (nss_smime_get_cipher_for_alg_and_key(algid
, key
, &which
) != SECSuccess
)
309 return nss_smime_cipher_allowed(which
);
314 * NSS_SMIME_EncryptionPossible - check if any encryption is allowed
316 * This tells whether or not *any* S/MIME encryption can be done,
317 * according to policy. Callers may use this to do nicer user interface
318 * (say, greying out a checkbox so a user does not even try to encrypt
319 * a message when they are not allowed to) or for any reason they want
320 * to check whether S/MIME encryption (or decryption, for that matter)
323 * It takes no arguments. The return value is a simple boolean:
324 * PR_TRUE means encryption (or decryption) is *possible*
325 * (but may still fail due to other reasons, like because we cannot
326 * find all the necessary certs, etc.; PR_TRUE is *not* a guarantee)
327 * PR_FALSE means encryption (or decryption) is not permitted
329 * There are no errors from this routine.
332 SecSMIMEEncryptionPossible(void)
336 for (i
= 0; i
< smime_cipher_map_count
; i
++) {
337 if (smime_cipher_map
[i
].allowed
)
345 nss_SMIME_FindCipherForSMIMECap(NSSSMIMECapability
*cap
)
350 /* we need the OIDTag here */
351 capIDTag
= SECOID_FindOIDTag(&(cap
->capabilityID
));
353 /* go over all the SMIME ciphers we know and see if we find a match */
354 for (i
= 0; i
< smime_cipher_map_count
; i
++) {
355 if (smime_cipher_map
[i
].algtag
!= capIDTag
)
358 * XXX If SECITEM_CompareItem allowed NULLs as arguments (comparing
359 * 2 NULLs as equal and NULL and non-NULL as not equal), we could
360 * use that here instead of all of the following comparison code.
362 if (cap
->parameters
.Data
== NULL
&& smime_cipher_map
[i
].parms
== NULL
)
363 break; /* both empty: bingo */
365 if (cap
->parameters
.Data
!= NULL
&& smime_cipher_map
[i
].parms
!= NULL
&&
366 cap
->parameters
.Length
== smime_cipher_map
[i
].parms
->Length
&&
367 PORT_Memcmp (cap
->parameters
.Data
, smime_cipher_map
[i
].parms
->Data
,
368 cap
->parameters
.Length
) == 0)
370 break; /* both not empty, same length & equal content: bingo */
374 if (i
== smime_cipher_map_count
)
375 return 0; /* no match found */
377 return smime_cipher_map
[i
].cipher
; /* match found, point to cipher */
380 static int smime_keysize_by_cipher (unsigned long which
);
383 * smime_choose_cipher - choose a cipher that works for all the recipients
385 * "scert" - sender's certificate
386 * "rcerts" - recipient's certificates
389 smime_choose_cipher(SecCertificateRef scert
, SecCertificateRef
*rcerts
)
394 int *cipher_abilities
;
398 int rcount
, mapi
, max
, i
;
400 // @@@ We Don't support Fortezza yet.
401 Boolean scert_is_fortezza
= PR_FALSE
;
403 Boolean scert_is_fortezza
= (scert
== NULL
) ? PR_FALSE
: PK11_FortezzaHasKEA(scert
);
406 chosen_cipher
= SMIME_DES_CBC_56
; /* the default, LCD */
407 weak_mapi
= smime_mapi_by_cipher(chosen_cipher
);
409 poolp
= PORT_NewArena (1024); /* XXX what is right value? */
413 cipher_abilities
= (int *)PORT_ArenaZAlloc(poolp
, smime_cipher_map_count
* sizeof(int));
414 cipher_votes
= (int *)PORT_ArenaZAlloc(poolp
, smime_cipher_map_count
* sizeof(int));
415 if (cipher_votes
== NULL
|| cipher_abilities
== NULL
)
418 /* If the user has the Fortezza preference turned on, make
419 * that the strong cipher. Otherwise, use triple-DES. */
420 strong_mapi
= smime_mapi_by_cipher (SMIME_DES_EDE3_168
);
421 if (scert_is_fortezza
) {
422 mapi
= smime_mapi_by_cipher(SMIME_FORTEZZA
);
423 if (mapi
>= 0 && smime_cipher_map
[mapi
].enabled
)
427 /* walk all the recipient's certs */
428 for (rcount
= 0; rcerts
[rcount
] != NULL
; rcount
++) {
429 SecAsn1Item
*profile
;
430 NSSSMIMECapability
**caps
;
433 /* the first cipher that matches in the user's SMIME profile gets
434 * "smime_cipher_map_count" votes; the next one gets "smime_cipher_map_count" - 1
435 * and so on. If every cipher matches, the last one gets 1 (one) vote */
436 pref
= smime_cipher_map_count
;
438 /* find recipient's SMIME profile */
439 profile
= CERT_FindSMimeProfile(rcerts
[rcount
]);
441 if (profile
!= NULL
&& profile
->Data
!= NULL
&& profile
->Length
> 0) {
442 /* we have a profile (still DER-encoded) */
445 if (SEC_ASN1DecodeItem(poolp
, &caps
, NSSSMIMECapabilitiesTemplate
, profile
) == SECSuccess
&&
448 /* walk the SMIME capabilities for this recipient */
449 for (i
= 0; caps
[i
] != NULL
; i
++) {
450 cipher
= nss_SMIME_FindCipherForSMIMECap(caps
[i
]);
451 mapi
= smime_mapi_by_cipher(cipher
);
453 /* found the cipher */
454 cipher_abilities
[mapi
]++;
455 cipher_votes
[mapi
] += pref
;
461 /* no profile found - so we can only assume that the user can do
462 * the mandatory algorithms which is RC2-40 (weak crypto) and 3DES (strong crypto) */
467 * if recipient's public key length is > 512, vote for a strong cipher
468 * please not that the side effect of this is that if only one recipient
469 * has an export-level public key, the strong cipher is disabled.
471 * XXX This is probably only good for RSA keys. What I would
472 * really like is a function to just say; Is the public key in
473 * this cert an export-length key? Then I would not have to
474 * know things like the value 512, or the kind of key, or what
475 * a subjectPublicKeyInfo is, etc.
477 key
= CERT_ExtractPublicKey(rcerts
[rcount
]);
481 SecKeyGetStrengthInBits(key
, NULL
, &pklen_bits
);
483 pklen_bits
= SecKeyGetSize(key
, kSecKeyKeySizeInBits
);
485 SECKEY_DestroyPublicKey (key
);
488 if (pklen_bits
> 512) {
489 /* cast votes for the strong algorithm */
490 cipher_abilities
[strong_mapi
]++;
491 cipher_votes
[strong_mapi
] += pref
;
495 /* always cast (possibly less) votes for the weak algorithm */
496 cipher_abilities
[weak_mapi
]++;
497 cipher_votes
[weak_mapi
] += pref
;
500 SECITEM_FreeItem(profile
, PR_TRUE
);
503 /* find cipher that is agreeable by all recipients and that has the most votes */
505 for (mapi
= 0; mapi
< smime_cipher_map_count
; mapi
++) {
506 /* if not all of the recipients can do this, forget it */
507 if (cipher_abilities
[mapi
] != rcount
)
509 /* if cipher is not enabled or not allowed by policy, forget it */
510 if (!smime_cipher_map
[mapi
].enabled
|| !smime_cipher_map
[mapi
].allowed
)
512 /* if we're not doing fortezza, but the cipher is fortezza, forget it */
513 if (!scert_is_fortezza
&& (smime_cipher_map
[mapi
].cipher
== SMIME_FORTEZZA
))
515 /* now see if this one has more votes than the last best one */
516 if (cipher_votes
[mapi
] >= max
) {
517 /* if equal number of votes, prefer the ones further down in the list */
518 /* with the expectation that these are higher rated ciphers */
519 chosen_cipher
= smime_cipher_map
[mapi
].cipher
;
520 max
= cipher_votes
[mapi
];
523 /* if no common cipher was found, chosen_cipher stays at the default */
527 PORT_FreeArena (poolp
, PR_FALSE
);
529 if (smime_keysize_by_cipher(chosen_cipher
) < 128) {
530 /* you're going to use strong(er) crypto whether you like it or not */
531 chosen_cipher
= SMIME_DES_EDE3_168
;
533 return chosen_cipher
;
537 * XXX This is a hack for now to satisfy our current interface.
538 * Eventually, with more parameters needing to be specified, just
539 * looking up the keysize is not going to be sufficient.
542 smime_keysize_by_cipher (unsigned long which
)
547 case SMIME_RC2_CBC_40
:
550 case SMIME_RC2_CBC_64
:
553 case SMIME_RC2_CBC_128
:
556 case SMIME_DES_CBC_56
:
559 case SMIME_DES_EDE3_168
:
564 * This is special; since the key size is fixed, we actually
565 * want to *avoid* specifying a key size.
578 * SecSMIMEFindBulkAlgForRecipients - find bulk algorithm suitable for all recipients
580 * it would be great for UI purposes if there would be a way to find out which recipients
581 * prevented a strong cipher from being used...
584 SecSMIMEFindBulkAlgForRecipients(SecCertificateRef
*rcerts
, SECOidTag
*bulkalgtag
, int *keysize
)
586 unsigned long cipher
;
589 cipher
= smime_choose_cipher(NULL
, rcerts
);
590 mapi
= smime_mapi_by_cipher(cipher
);
595 *bulkalgtag
= smime_cipher_map
[mapi
].algtag
;
596 *keysize
= smime_keysize_by_cipher(smime_cipher_map
[mapi
].cipher
);
602 * SecSMIMECreateSMIMECapabilities - get S/MIME capabilities for this instance of NSS
604 * scans the list of allowed and enabled ciphers and construct a PKCS9-compliant
605 * S/MIME capabilities attribute value.
607 * XXX Please note that, in contradiction to RFC2633 2.5.2, the capabilities only include
608 * symmetric ciphers, NO signature algorithms or key encipherment algorithms.
610 * "poolp" - arena pool to create the S/MIME capabilities data on
611 * "dest" - SecAsn1Item to put the data in
612 * "includeFortezzaCiphers" - PR_TRUE if fortezza ciphers should be included
615 SecSMIMECreateSMIMECapabilities(PLArenaPool
*poolp
, SecAsn1Item
*dest
, Boolean includeFortezzaCiphers
)
617 NSSSMIMECapability
*cap
;
618 NSSSMIMECapability
**smime_capabilities
;
619 smime_cipher_map_entry
*map
;
624 /* if we have an old NSSSMIMECapability array, we'll reuse it (has the right size) */
625 /* smime_cipher_map_count + 1 is an upper bound - we might end up with less */
626 smime_capabilities
= (NSSSMIMECapability
**)PORT_ZAlloc((smime_cipher_map_count
+ 1)
627 * sizeof(NSSSMIMECapability
*));
628 if (smime_capabilities
== NULL
)
633 /* Add all the symmetric ciphers
634 * We walk the cipher list backwards, as it is ordered by increasing strength,
635 * we prefer the stronger cipher over a weaker one, and we have to list the
636 * preferred algorithm first */
637 for (i
= smime_cipher_map_count
- 1; i
>= 0; i
--) {
638 /* Find the corresponding entry in the cipher map. */
639 map
= &(smime_cipher_map
[i
]);
643 /* If we're using a non-Fortezza cert, only advertise non-Fortezza
644 capabilities. (We advertise all capabilities if we have a
646 if ((!includeFortezzaCiphers
) && (map
->cipher
== SMIME_FORTEZZA
))
649 /* get next SMIME capability */
650 cap
= (NSSSMIMECapability
*)PORT_ZAlloc(sizeof(NSSSMIMECapability
));
653 smime_capabilities
[capIndex
++] = cap
;
655 oiddata
= SECOID_FindOIDByTag(map
->algtag
);
659 cap
->capabilityID
.Data
= oiddata
->oid
.Data
;
660 cap
->capabilityID
.Length
= oiddata
->oid
.Length
;
661 cap
->parameters
.Data
= map
->parms
? map
->parms
->Data
: NULL
;
662 cap
->parameters
.Length
= map
->parms
? map
->parms
->Length
: 0;
663 cap
->cipher
= smime_cipher_map
[i
].cipher
;
666 /* XXX add signature algorithms */
667 /* XXX add key encipherment algorithms */
669 smime_capabilities
[capIndex
] = NULL
; /* last one - now encode */
670 dummy
= SEC_ASN1EncodeItem(poolp
, dest
, &smime_capabilities
, NSSSMIMECapabilitiesTemplate
);
672 /* now that we have the proper encoded SMIMECapabilities (or not),
673 * free the work data */
674 for (i
= 0; smime_capabilities
[i
] != NULL
; i
++)
675 PORT_Free(smime_capabilities
[i
]);
676 PORT_Free(smime_capabilities
);
678 return (dummy
== NULL
) ? SECFailure
: SECSuccess
;
682 * SecSMIMECreateSMIMEEncKeyPrefs - create S/MIME encryption key preferences attr value
684 * "poolp" - arena pool to create the attr value on
685 * "dest" - SecAsn1Item to put the data in
686 * "cert" - certificate that should be marked as preferred encryption key
687 * cert is expected to have been verified for EmailRecipient usage.
690 SecSMIMECreateSMIMEEncKeyPrefs(PLArenaPool
*poolp
, SecAsn1Item
*dest
, SecCertificateRef cert
)
692 NSSSMIMEEncryptionKeyPreference ekp
;
693 SecAsn1Item
*dummy
= NULL
;
694 PLArenaPool
*tmppoolp
= NULL
;
699 tmppoolp
= PORT_NewArena(1024);
700 if (tmppoolp
== NULL
)
703 /* XXX hardcoded IssuerSN choice for now */
704 ekp
.selector
= NSSSMIMEEncryptionKeyPref_IssuerSN
;
705 ekp
.id
.issuerAndSN
= CERT_GetCertIssuerAndSN(tmppoolp
, cert
);
706 if (ekp
.id
.issuerAndSN
== NULL
)
709 dummy
= SEC_ASN1EncodeItem(poolp
, dest
, &ekp
, smime_encryptionkeypref_template
);
712 if (tmppoolp
) PORT_FreeArena(tmppoolp
, PR_FALSE
);
714 return (dummy
== NULL
) ? SECFailure
: SECSuccess
;
718 * SecSMIMECreateSMIMEEncKeyPrefs - create S/MIME encryption key preferences attr value using MS oid
720 * "poolp" - arena pool to create the attr value on
721 * "dest" - SecAsn1Item to put the data in
722 * "cert" - certificate that should be marked as preferred encryption key
723 * cert is expected to have been verified for EmailRecipient usage.
726 SecSMIMECreateMSSMIMEEncKeyPrefs(PLArenaPool
*poolp
, SecAsn1Item
*dest
, SecCertificateRef cert
)
728 SecAsn1Item
*dummy
= NULL
;
729 PLArenaPool
*tmppoolp
= NULL
;
730 SecCmsIssuerAndSN
*isn
;
735 tmppoolp
= PORT_NewArena(1024);
736 if (tmppoolp
== NULL
)
739 isn
= CERT_GetCertIssuerAndSN(tmppoolp
, cert
);
743 dummy
= SEC_ASN1EncodeItem(poolp
, dest
, isn
, SEC_ASN1_GET(SecCmsIssuerAndSNTemplate
));
746 if (tmppoolp
) PORT_FreeArena(tmppoolp
, PR_FALSE
);
748 return (dummy
== NULL
) ? SECFailure
: SECSuccess
;
751 static CFArrayRef CF_RETURNS_RETAINED
copyCertsFromRawCerts(SecAsn1Item
**rawCerts
) {
752 CFMutableArrayRef certs
= NULL
;
753 SecCertificateRef certificate
= NULL
;
754 int numRawCerts
= SecCmsArrayCount((void **)rawCerts
);
757 certs
= CFArrayCreateMutable(NULL
, numRawCerts
, &kCFTypeArrayCallBacks
);
759 for(dex
=0; dex
<numRawCerts
; dex
++) {
760 certificate
= SecCertificateCreateWithBytes(NULL
, rawCerts
[dex
]->Data
, rawCerts
[dex
]->Length
);
762 CFArrayAppendValue(certs
, certificate
);
763 CFRelease(certificate
);
768 if (CFArrayGetCount(certs
) == 0) {
776 * SecSMIMEGetCertFromEncryptionKeyPreference -
777 * find cert marked by EncryptionKeyPreference attribute
779 * "keychainOrArray" - handle for the cert database to look in
780 * "DERekp" - DER-encoded value of S/MIME Encryption Key Preference attribute
782 * if certificate is supposed to be found among the message's included certificates,
783 * they are assumed to have been imported already.
786 SecSMIMEGetCertFromEncryptionKeyPreference(SecAsn1Item
**rawCerts
, SecAsn1Item
*DERekp
)
788 PLArenaPool
*tmppoolp
= NULL
;
789 SecCertificateRef cert
= NULL
;
790 NSSSMIMEEncryptionKeyPreference ekp
;
791 CFArrayRef certs
= NULL
;
793 tmppoolp
= PORT_NewArena(1024);
794 if (tmppoolp
== NULL
)
798 if (SEC_ASN1DecodeItem(tmppoolp
, &ekp
, smime_encryptionkeypref_template
, DERekp
) != SECSuccess
)
801 certs
= copyCertsFromRawCerts(rawCerts
);
804 switch (ekp
.selector
) {
805 case NSSSMIMEEncryptionKeyPref_IssuerSN
:
806 cert
= CERT_FindCertificateByIssuerAndSN(certs
, ekp
.id
.issuerAndSN
);
808 case NSSSMIMEEncryptionKeyPref_RKeyID
:
809 case NSSSMIMEEncryptionKeyPref_SubjectKeyID
:
810 cert
= CERT_FindCertificateBySubjectKeyID(certs
, ekp
.id
.subjectKeyID
);
816 if (tmppoolp
) PORT_FreeArena(tmppoolp
, PR_FALSE
);
817 if (certs
) CFRelease(certs
);
822 extern const char __nss_smime_rcsid
[];
823 extern const char __nss_smime_sccsid
[];
826 NSSSMIME_VersionCheck(const char *importedVersion
)
832 * This is the secret handshake algorithm.
834 * This release has a simple version compatibility
835 * check algorithm. This release is not backward
836 * compatible with previous major releases. It is
837 * not compatible with future major, minor, or
840 volatile char c
; /* force a reference that won't get optimized away */
842 c
= __nss_smime_rcsid
[0] + __nss_smime_sccsid
[0];
844 return NSS_VersionCheck(importedVersion
);