Index: mozilla/security/nss/lib/pkcs7/secmime.c |
=================================================================== |
--- mozilla/security/nss/lib/pkcs7/secmime.c (revision 191424) |
+++ mozilla/security/nss/lib/pkcs7/secmime.c (working copy) |
@@ -1,824 +0,0 @@ |
-/* This Source Code Form is subject to the terms of the Mozilla Public |
- * License, v. 2.0. If a copy of the MPL was not distributed with this |
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
- |
-/* |
- * Stuff specific to S/MIME policy and interoperability. |
- * Depends on PKCS7, but there should be no dependency the other way around. |
- * |
- * $Id: secmime.c,v 1.6 2012/04/25 14:50:06 gerv%gerv.net Exp $ |
- */ |
- |
-#include "secmime.h" |
-#include "secoid.h" |
-#include "pk11func.h" |
-#include "ciferfam.h" /* for CIPHER_FAMILY symbols */ |
-#include "secasn1.h" |
-#include "secitem.h" |
-#include "cert.h" |
-#include "key.h" |
-#include "secerr.h" |
- |
-typedef struct smime_cipher_map_struct { |
- unsigned long cipher; |
- SECOidTag algtag; |
- SECItem *parms; |
-} smime_cipher_map; |
- |
-/* |
- * These are macros because I think some subsequent parameters, |
- * like those for RC5, will want to use them, too, separately. |
- */ |
-#define SMIME_DER_INTVAL_16 SEC_ASN1_INTEGER, 0x01, 0x10 |
-#define SMIME_DER_INTVAL_40 SEC_ASN1_INTEGER, 0x01, 0x28 |
-#define SMIME_DER_INTVAL_64 SEC_ASN1_INTEGER, 0x01, 0x40 |
-#define SMIME_DER_INTVAL_128 SEC_ASN1_INTEGER, 0x02, 0x00, 0x80 |
- |
-#ifdef SMIME_DOES_RC5 /* will be needed; quiet unused warning for now */ |
-static unsigned char smime_int16[] = { SMIME_DER_INTVAL_16 }; |
-#endif |
-static unsigned char smime_int40[] = { SMIME_DER_INTVAL_40 }; |
-static unsigned char smime_int64[] = { SMIME_DER_INTVAL_64 }; |
-static unsigned char smime_int128[] = { SMIME_DER_INTVAL_128 }; |
- |
-static SECItem smime_rc2p40 = { siBuffer, smime_int40, sizeof(smime_int40) }; |
-static SECItem smime_rc2p64 = { siBuffer, smime_int64, sizeof(smime_int64) }; |
-static SECItem smime_rc2p128 = { siBuffer, smime_int128, sizeof(smime_int128) }; |
- |
-static smime_cipher_map smime_cipher_maps[] = { |
- { SMIME_RC2_CBC_40, SEC_OID_RC2_CBC, &smime_rc2p40 }, |
- { SMIME_RC2_CBC_64, SEC_OID_RC2_CBC, &smime_rc2p64 }, |
- { SMIME_RC2_CBC_128, SEC_OID_RC2_CBC, &smime_rc2p128 }, |
-#ifdef SMIME_DOES_RC5 |
- { SMIME_RC5PAD_64_16_40, SEC_OID_RC5_CBC_PAD, &smime_rc5p40 }, |
- { SMIME_RC5PAD_64_16_64, SEC_OID_RC5_CBC_PAD, &smime_rc5p64 }, |
- { SMIME_RC5PAD_64_16_128, SEC_OID_RC5_CBC_PAD, &smime_rc5p128 }, |
-#endif |
- { SMIME_DES_CBC_56, SEC_OID_DES_CBC, NULL }, |
- { SMIME_DES_EDE3_168, SEC_OID_DES_EDE3_CBC, NULL } |
-}; |
- |
-/* |
- * Note, the following value really just needs to be an upper bound |
- * on the ciphers. |
- */ |
-static const int smime_symmetric_count = sizeof(smime_cipher_maps) |
- / sizeof(smime_cipher_map); |
- |
-static unsigned long *smime_prefs, *smime_newprefs; |
-static int smime_current_pref_index = 0; |
-static PRBool smime_prefs_complete = PR_FALSE; |
-static PRBool smime_prefs_changed = PR_TRUE; |
- |
-static unsigned long smime_policy_bits = 0; |
- |
- |
-static int |
-smime_mapi_by_cipher (unsigned long cipher) |
-{ |
- int i; |
- |
- for (i = 0; i < smime_symmetric_count; i++) { |
- if (smime_cipher_maps[i].cipher == cipher) |
- break; |
- } |
- |
- if (i == smime_symmetric_count) |
- return -1; |
- |
- return i; |
-} |
- |
- |
-/* |
- * this function locally records the user's preference |
- */ |
-SECStatus |
-SECMIME_EnableCipher(long which, int on) |
-{ |
- unsigned long mask; |
- |
- if (smime_newprefs == NULL || smime_prefs_complete) { |
- /* |
- * This is either the very first time, or we are starting over. |
- */ |
- smime_newprefs = (unsigned long*)PORT_ZAlloc (smime_symmetric_count |
- * sizeof(*smime_newprefs)); |
- if (smime_newprefs == NULL) |
- return SECFailure; |
- smime_current_pref_index = 0; |
- smime_prefs_complete = PR_FALSE; |
- } |
- |
- mask = which & CIPHER_FAMILYID_MASK; |
- if (mask == CIPHER_FAMILYID_MASK) { |
- /* |
- * This call signifies that all preferences have been set. |
- * Move "newprefs" over, after checking first whether or |
- * not the new ones are different from the old ones. |
- */ |
- if (smime_prefs != NULL) { |
- if (PORT_Memcmp (smime_prefs, smime_newprefs, |
- smime_symmetric_count * sizeof(*smime_prefs)) == 0) |
- smime_prefs_changed = PR_FALSE; |
- else |
- smime_prefs_changed = PR_TRUE; |
- PORT_Free (smime_prefs); |
- } |
- |
- smime_prefs = smime_newprefs; |
- smime_prefs_complete = PR_TRUE; |
- return SECSuccess; |
- } |
- |
- PORT_Assert (mask == CIPHER_FAMILYID_SMIME); |
- if (mask != CIPHER_FAMILYID_SMIME) { |
- /* XXX set an error! */ |
- return SECFailure; |
- } |
- |
- if (on) { |
- PORT_Assert (smime_current_pref_index < smime_symmetric_count); |
- if (smime_current_pref_index >= smime_symmetric_count) { |
- /* XXX set an error! */ |
- return SECFailure; |
- } |
- |
- smime_newprefs[smime_current_pref_index++] = which; |
- } |
- |
- return SECSuccess; |
-} |
- |
- |
-/* |
- * this function locally records the export policy |
- */ |
-SECStatus |
-SECMIME_SetPolicy(long which, int on) |
-{ |
- unsigned long mask; |
- |
- PORT_Assert ((which & CIPHER_FAMILYID_MASK) == CIPHER_FAMILYID_SMIME); |
- if ((which & CIPHER_FAMILYID_MASK) != CIPHER_FAMILYID_SMIME) { |
- /* XXX set an error! */ |
- return SECFailure; |
- } |
- |
- which &= ~CIPHER_FAMILYID_MASK; |
- |
- PORT_Assert (which < 32); /* bits in the long */ |
- if (which >= 32) { |
- /* XXX set an error! */ |
- return SECFailure; |
- } |
- |
- mask = 1UL << which; |
- |
- if (on) { |
- smime_policy_bits |= mask; |
- } else { |
- smime_policy_bits &= ~mask; |
- } |
- |
- return SECSuccess; |
-} |
- |
- |
-/* |
- * Based on the given algorithm (including its parameters, in some cases!) |
- * and the given key (may or may not be inspected, depending on the |
- * algorithm), find the appropriate policy algorithm specification |
- * and return it. If no match can be made, -1 is returned. |
- */ |
-static long |
-smime_policy_algorithm (SECAlgorithmID *algid, PK11SymKey *key) |
-{ |
- SECOidTag algtag; |
- |
- algtag = SECOID_GetAlgorithmTag (algid); |
- switch (algtag) { |
- case SEC_OID_RC2_CBC: |
- { |
- unsigned int keylen_bits; |
- |
- keylen_bits = PK11_GetKeyStrength (key, algid); |
- switch (keylen_bits) { |
- case 40: |
- return SMIME_RC2_CBC_40; |
- case 64: |
- return SMIME_RC2_CBC_64; |
- case 128: |
- return SMIME_RC2_CBC_128; |
- default: |
- break; |
- } |
- } |
- break; |
- case SEC_OID_DES_CBC: |
- return SMIME_DES_CBC_56; |
- case SEC_OID_DES_EDE3_CBC: |
- return SMIME_DES_EDE3_168; |
-#ifdef SMIME_DOES_RC5 |
- case SEC_OID_RC5_CBC_PAD: |
- PORT_Assert (0); /* XXX need to pull out parameters and match */ |
- break; |
-#endif |
- default: |
- break; |
- } |
- |
- return -1; |
-} |
- |
- |
-static PRBool |
-smime_cipher_allowed (unsigned long which) |
-{ |
- unsigned long mask; |
- |
- which &= ~CIPHER_FAMILYID_MASK; |
- PORT_Assert (which < 32); /* bits per long (min) */ |
- if (which >= 32) |
- return PR_FALSE; |
- |
- mask = 1UL << which; |
- if ((mask & smime_policy_bits) == 0) |
- return PR_FALSE; |
- |
- return PR_TRUE; |
-} |
- |
- |
-PRBool |
-SECMIME_DecryptionAllowed(SECAlgorithmID *algid, PK11SymKey *key) |
-{ |
- long which; |
- |
- which = smime_policy_algorithm (algid, key); |
- if (which < 0) |
- return PR_FALSE; |
- |
- return smime_cipher_allowed ((unsigned long)which); |
-} |
- |
- |
-/* |
- * Does the current policy allow *any* S/MIME encryption (or decryption)? |
- * |
- * This tells whether or not *any* S/MIME encryption can be done, |
- * according to policy. Callers may use this to do nicer user interface |
- * (say, greying out a checkbox so a user does not even try to encrypt |
- * a message when they are not allowed to) or for any reason they want |
- * to check whether S/MIME encryption (or decryption, for that matter) |
- * may be done. |
- * |
- * It takes no arguments. The return value is a simple boolean: |
- * PR_TRUE means encryption (or decryption) is *possible* |
- * (but may still fail due to other reasons, like because we cannot |
- * find all the necessary certs, etc.; PR_TRUE is *not* a guarantee) |
- * PR_FALSE means encryption (or decryption) is not permitted |
- * |
- * There are no errors from this routine. |
- */ |
-PRBool |
-SECMIME_EncryptionPossible (void) |
-{ |
- if (smime_policy_bits != 0) |
- return PR_TRUE; |
- |
- return PR_FALSE; |
-} |
- |
- |
-/* |
- * XXX Would like the "parameters" field to be a SECItem *, but the |
- * encoder is having trouble with optional pointers to an ANY. Maybe |
- * once that is fixed, can change this back... |
- */ |
-typedef struct smime_capability_struct { |
- unsigned long cipher; /* local; not part of encoding */ |
- SECOidTag capIDTag; /* local; not part of encoding */ |
- SECItem capabilityID; |
- SECItem parameters; |
-} smime_capability; |
- |
-static const SEC_ASN1Template smime_capability_template[] = { |
- { SEC_ASN1_SEQUENCE, |
- 0, NULL, sizeof(smime_capability) }, |
- { SEC_ASN1_OBJECT_ID, |
- offsetof(smime_capability,capabilityID), }, |
- { SEC_ASN1_OPTIONAL | SEC_ASN1_ANY, |
- offsetof(smime_capability,parameters), }, |
- { 0, } |
-}; |
- |
-static const SEC_ASN1Template smime_capabilities_template[] = { |
- { SEC_ASN1_SEQUENCE_OF, 0, smime_capability_template } |
-}; |
- |
- |
- |
-static void |
-smime_fill_capability (smime_capability *cap) |
-{ |
- unsigned long cipher; |
- SECOidTag algtag; |
- int i; |
- |
- algtag = SECOID_FindOIDTag (&(cap->capabilityID)); |
- |
- for (i = 0; i < smime_symmetric_count; i++) { |
- if (smime_cipher_maps[i].algtag != algtag) |
- continue; |
- /* |
- * XXX If SECITEM_CompareItem allowed NULLs as arguments (comparing |
- * 2 NULLs as equal and NULL and non-NULL as not equal), we could |
- * use that here instead of all of the following comparison code. |
- */ |
- if (cap->parameters.data != NULL) { |
- if (smime_cipher_maps[i].parms == NULL) |
- continue; |
- if (cap->parameters.len != smime_cipher_maps[i].parms->len) |
- continue; |
- if (PORT_Memcmp (cap->parameters.data, |
- smime_cipher_maps[i].parms->data, |
- cap->parameters.len) == 0) |
- break; |
- } else if (smime_cipher_maps[i].parms == NULL) { |
- break; |
- } |
- } |
- |
- if (i == smime_symmetric_count) |
- cipher = 0; |
- else |
- cipher = smime_cipher_maps[i].cipher; |
- |
- cap->cipher = cipher; |
- cap->capIDTag = algtag; |
-} |
- |
- |
-static long |
-smime_choose_cipher (CERTCertificate *scert, CERTCertificate **rcerts) |
-{ |
- PRArenaPool *poolp; |
- long chosen_cipher; |
- int *cipher_abilities; |
- int *cipher_votes; |
- int strong_mapi; |
- int rcount, mapi, max; |
- |
- if (smime_policy_bits == 0) { |
- PORT_SetError (SEC_ERROR_BAD_EXPORT_ALGORITHM); |
- return -1; |
- } |
- |
- chosen_cipher = SMIME_RC2_CBC_40; /* the default, LCD */ |
- |
- poolp = PORT_NewArena (1024); /* XXX what is right value? */ |
- if (poolp == NULL) |
- goto done; |
- |
- cipher_abilities = (int*)PORT_ArenaZAlloc (poolp, |
- smime_symmetric_count * sizeof(int)); |
- if (cipher_abilities == NULL) |
- goto done; |
- |
- cipher_votes = (int*)PORT_ArenaZAlloc (poolp, |
- smime_symmetric_count * sizeof(int)); |
- if (cipher_votes == NULL) |
- goto done; |
- |
- /* |
- * XXX Should have a #define somewhere which specifies default |
- * strong cipher. (Or better, a way to configure.) |
- */ |
- |
- /* Make triple-DES the strong cipher. */ |
- strong_mapi = smime_mapi_by_cipher (SMIME_DES_EDE3_168); |
- |
- PORT_Assert (strong_mapi >= 0); |
- |
- for (rcount = 0; rcerts[rcount] != NULL; rcount++) { |
- SECItem *profile; |
- smime_capability **caps; |
- int capi, pref; |
- SECStatus dstat; |
- |
- pref = smime_symmetric_count; |
- profile = CERT_FindSMimeProfile (rcerts[rcount]); |
- if (profile != NULL && profile->data != NULL && profile->len > 0) { |
- caps = NULL; |
- dstat = SEC_QuickDERDecodeItem (poolp, &caps, |
- smime_capabilities_template, |
- profile); |
- if (dstat == SECSuccess && caps != NULL) { |
- for (capi = 0; caps[capi] != NULL; capi++) { |
- smime_fill_capability (caps[capi]); |
- mapi = smime_mapi_by_cipher (caps[capi]->cipher); |
- if (mapi >= 0) { |
- cipher_abilities[mapi]++; |
- cipher_votes[mapi] += pref; |
- --pref; |
- } |
- } |
- } |
- } else { |
- SECKEYPublicKey *key; |
- unsigned int pklen_bits; |
- |
- /* |
- * XXX This is probably only good for RSA keys. What I would |
- * really like is a function to just say; Is the public key in |
- * this cert an export-length key? Then I would not have to |
- * know things like the value 512, or the kind of key, or what |
- * a subjectPublicKeyInfo is, etc. |
- */ |
- key = CERT_ExtractPublicKey (rcerts[rcount]); |
- if (key != NULL) { |
- pklen_bits = SECKEY_PublicKeyStrength (key) * 8; |
- SECKEY_DestroyPublicKey (key); |
- |
- if (pklen_bits > 512) { |
- cipher_abilities[strong_mapi]++; |
- cipher_votes[strong_mapi] += pref; |
- } |
- } |
- } |
- if (profile != NULL) |
- SECITEM_FreeItem (profile, PR_TRUE); |
- } |
- |
- max = 0; |
- for (mapi = 0; mapi < smime_symmetric_count; mapi++) { |
- if (cipher_abilities[mapi] != rcount) |
- continue; |
- if (! smime_cipher_allowed (smime_cipher_maps[mapi].cipher)) |
- continue; |
- if (cipher_votes[mapi] > max) { |
- chosen_cipher = smime_cipher_maps[mapi].cipher; |
- max = cipher_votes[mapi]; |
- } /* XXX else if a tie, let scert break it? */ |
- } |
- |
-done: |
- if (poolp != NULL) |
- PORT_FreeArena (poolp, PR_FALSE); |
- |
- return chosen_cipher; |
-} |
- |
- |
-/* |
- * XXX This is a hack for now to satisfy our current interface. |
- * Eventually, with more parameters needing to be specified, just |
- * looking up the keysize is not going to be sufficient. |
- */ |
-static int |
-smime_keysize_by_cipher (unsigned long which) |
-{ |
- int keysize; |
- |
- switch (which) { |
- case SMIME_RC2_CBC_40: |
- keysize = 40; |
- break; |
- case SMIME_RC2_CBC_64: |
- keysize = 64; |
- break; |
- case SMIME_RC2_CBC_128: |
- keysize = 128; |
- break; |
-#ifdef SMIME_DOES_RC5 |
- case SMIME_RC5PAD_64_16_40: |
- case SMIME_RC5PAD_64_16_64: |
- case SMIME_RC5PAD_64_16_128: |
- /* XXX See comment above; keysize is not enough... */ |
- PORT_Assert (0); |
- PORT_SetError (SEC_ERROR_INVALID_ALGORITHM); |
- keysize = -1; |
- break; |
-#endif |
- case SMIME_DES_CBC_56: |
- case SMIME_DES_EDE3_168: |
- /* |
- * These are special; since the key size is fixed, we actually |
- * want to *avoid* specifying a key size. |
- */ |
- keysize = 0; |
- break; |
- default: |
- keysize = -1; |
- break; |
- } |
- |
- return keysize; |
-} |
- |
- |
-/* |
- * Start an S/MIME encrypting context. |
- * |
- * "scert" is the cert for the sender. It will be checked for validity. |
- * "rcerts" are the certs for the recipients. They will also be checked. |
- * |
- * "certdb" is the cert database to use for verifying the certs. |
- * It can be NULL if a default database is available (like in the client). |
- * |
- * This function already does all of the stuff specific to S/MIME protocol |
- * and local policy; the return value just needs to be passed to |
- * SEC_PKCS7Encode() or to SEC_PKCS7EncoderStart() to create the encoded data, |
- * and finally to SEC_PKCS7DestroyContentInfo(). |
- * |
- * An error results in a return value of NULL and an error set. |
- * (Retrieve specific errors via PORT_GetError()/XP_GetError().) |
- */ |
-SEC_PKCS7ContentInfo * |
-SECMIME_CreateEncrypted(CERTCertificate *scert, |
- CERTCertificate **rcerts, |
- CERTCertDBHandle *certdb, |
- SECKEYGetPasswordKey pwfn, |
- void *pwfn_arg) |
-{ |
- SEC_PKCS7ContentInfo *cinfo; |
- long cipher; |
- SECOidTag encalg; |
- int keysize; |
- int mapi, rci; |
- |
- cipher = smime_choose_cipher (scert, rcerts); |
- if (cipher < 0) |
- return NULL; |
- |
- mapi = smime_mapi_by_cipher (cipher); |
- if (mapi < 0) |
- return NULL; |
- |
- /* |
- * XXX This is stretching it -- CreateEnvelopedData should probably |
- * take a cipher itself of some sort, because we cannot know what the |
- * future will bring in terms of parameters for each type of algorithm. |
- * For example, just an algorithm and keysize is *not* sufficient to |
- * fully specify the usage of RC5 (which also needs to know rounds and |
- * block size). Work this out into a better API! |
- */ |
- encalg = smime_cipher_maps[mapi].algtag; |
- keysize = smime_keysize_by_cipher (cipher); |
- if (keysize < 0) |
- return NULL; |
- |
- cinfo = SEC_PKCS7CreateEnvelopedData (scert, certUsageEmailRecipient, |
- certdb, encalg, keysize, |
- pwfn, pwfn_arg); |
- if (cinfo == NULL) |
- return NULL; |
- |
- for (rci = 0; rcerts[rci] != NULL; rci++) { |
- if (rcerts[rci] == scert) |
- continue; |
- if (SEC_PKCS7AddRecipient (cinfo, rcerts[rci], certUsageEmailRecipient, |
- NULL) != SECSuccess) { |
- SEC_PKCS7DestroyContentInfo (cinfo); |
- return NULL; |
- } |
- } |
- |
- return cinfo; |
-} |
- |
- |
-static smime_capability **smime_capabilities; |
-static SECItem *smime_encoded_caps; |
- |
- |
-static SECStatus |
-smime_init_caps (void) |
-{ |
- smime_capability *cap; |
- smime_cipher_map *map; |
- SECOidData *oiddata; |
- SECStatus rv; |
- int i; |
- |
- if (smime_encoded_caps != NULL && (! smime_prefs_changed)) |
- return SECSuccess; |
- |
- if (smime_encoded_caps != NULL) { |
- SECITEM_FreeItem (smime_encoded_caps, PR_TRUE); |
- smime_encoded_caps = NULL; |
- } |
- |
- if (smime_capabilities == NULL) { |
- smime_capabilities = (smime_capability**)PORT_ZAlloc ( |
- (smime_symmetric_count + 1) |
- * sizeof(smime_capability *)); |
- if (smime_capabilities == NULL) |
- return SECFailure; |
- } |
- |
- rv = SECFailure; |
- |
- /* |
- The process of creating the encoded PKCS7 cipher capability list |
- involves two basic steps: |
- |
- (a) Convert our internal representation of cipher preferences |
- (smime_prefs) into an array containing cipher OIDs and |
- parameter data (smime_capabilities). This step is |
- performed here. |
- |
- (b) Encode, using ASN.1, the cipher information in |
- smime_capabilities, leaving the encoded result in |
- smime_encoded_caps. |
- |
- (In the process of performing (a), Lisa put in some optimizations |
- which allow us to avoid needlessly re-populating elements in |
- smime_capabilities as we walk through smime_prefs.) |
- */ |
- for (i = 0; i < smime_current_pref_index; i++) { |
- int mapi; |
- |
- /* Get the next cipher preference in smime_prefs. */ |
- mapi = smime_mapi_by_cipher (smime_prefs[i]); |
- if (mapi < 0) |
- break; |
- |
- /* Find the corresponding entry in the cipher map. */ |
- PORT_Assert (mapi < smime_symmetric_count); |
- map = &(smime_cipher_maps[mapi]); |
- |
- /* |
- * Convert the next preference found in smime_prefs into an |
- * smime_capability. |
- */ |
- |
- cap = smime_capabilities[i]; |
- if (cap == NULL) { |
- cap = (smime_capability*)PORT_ZAlloc (sizeof(smime_capability)); |
- if (cap == NULL) |
- break; |
- smime_capabilities[i] = cap; |
- } else if (cap->cipher == smime_prefs[i]) { |
- continue; /* no change to this one */ |
- } |
- |
- cap->capIDTag = map->algtag; |
- oiddata = SECOID_FindOIDByTag (map->algtag); |
- if (oiddata == NULL) |
- break; |
- |
- if (cap->capabilityID.data != NULL) { |
- SECITEM_FreeItem (&(cap->capabilityID), PR_FALSE); |
- cap->capabilityID.data = NULL; |
- cap->capabilityID.len = 0; |
- } |
- |
- rv = SECITEM_CopyItem (NULL, &(cap->capabilityID), &(oiddata->oid)); |
- if (rv != SECSuccess) |
- break; |
- |
- if (map->parms == NULL) { |
- cap->parameters.data = NULL; |
- cap->parameters.len = 0; |
- } else { |
- cap->parameters.data = map->parms->data; |
- cap->parameters.len = map->parms->len; |
- } |
- |
- cap->cipher = smime_prefs[i]; |
- } |
- |
- if (i != smime_current_pref_index) |
- return rv; |
- |
- while (i < smime_symmetric_count) { |
- cap = smime_capabilities[i]; |
- if (cap != NULL) { |
- SECITEM_FreeItem (&(cap->capabilityID), PR_FALSE); |
- PORT_Free (cap); |
- } |
- smime_capabilities[i] = NULL; |
- i++; |
- } |
- smime_capabilities[i] = NULL; |
- |
- smime_encoded_caps = SEC_ASN1EncodeItem (NULL, NULL, &smime_capabilities, |
- smime_capabilities_template); |
- if (smime_encoded_caps == NULL) |
- return SECFailure; |
- |
- return SECSuccess; |
-} |
- |
- |
-static SECStatus |
-smime_add_profile (CERTCertificate *cert, SEC_PKCS7ContentInfo *cinfo) |
-{ |
- PORT_Assert (smime_prefs_complete); |
- if (! smime_prefs_complete) |
- return SECFailure; |
- |
- /* For that matter, if capabilities haven't been initialized yet, |
- do so now. */ |
- if (smime_encoded_caps == NULL || smime_prefs_changed) { |
- SECStatus rv; |
- |
- rv = smime_init_caps(); |
- if (rv != SECSuccess) |
- return rv; |
- |
- PORT_Assert (smime_encoded_caps != NULL); |
- } |
- |
- return SEC_PKCS7AddSignedAttribute (cinfo, SEC_OID_PKCS9_SMIME_CAPABILITIES, |
- smime_encoded_caps); |
-} |
- |
- |
-/* |
- * Start an S/MIME signing context. |
- * |
- * "scert" is the cert that will be used to sign the data. It will be |
- * checked for validity. |
- * |
- * "ecert" is the signer's encryption cert. If it is different from |
- * scert, then it will be included in the signed message so that the |
- * recipient can save it for future encryptions. |
- * |
- * "certdb" is the cert database to use for verifying the cert. |
- * It can be NULL if a default database is available (like in the client). |
- * |
- * "digestalg" names the digest algorithm (e.g. SEC_OID_SHA1). |
- * XXX There should be SECMIME functions for hashing, or the hashing should |
- * be built into this interface, which we would like because we would |
- * support more smartcards that way, and then this argument should go away.) |
- * |
- * "digest" is the actual digest of the data. It must be provided in |
- * the case of detached data or NULL if the content will be included. |
- * |
- * This function already does all of the stuff specific to S/MIME protocol |
- * and local policy; the return value just needs to be passed to |
- * SEC_PKCS7Encode() or to SEC_PKCS7EncoderStart() to create the encoded data, |
- * and finally to SEC_PKCS7DestroyContentInfo(). |
- * |
- * An error results in a return value of NULL and an error set. |
- * (Retrieve specific errors via PORT_GetError()/XP_GetError().) |
- */ |
- |
-SEC_PKCS7ContentInfo * |
-SECMIME_CreateSigned (CERTCertificate *scert, |
- CERTCertificate *ecert, |
- CERTCertDBHandle *certdb, |
- SECOidTag digestalg, |
- SECItem *digest, |
- SECKEYGetPasswordKey pwfn, |
- void *pwfn_arg) |
-{ |
- SEC_PKCS7ContentInfo *cinfo; |
- SECStatus rv; |
- |
- /* See note in header comment above about digestalg. */ |
- /* Doesn't explain this. PORT_Assert (digestalg == SEC_OID_SHA1); */ |
- |
- cinfo = SEC_PKCS7CreateSignedData (scert, certUsageEmailSigner, |
- certdb, digestalg, digest, |
- pwfn, pwfn_arg); |
- if (cinfo == NULL) |
- return NULL; |
- |
- if (SEC_PKCS7IncludeCertChain (cinfo, NULL) != SECSuccess) { |
- SEC_PKCS7DestroyContentInfo (cinfo); |
- return NULL; |
- } |
- |
- /* if the encryption cert and the signing cert differ, then include |
- * the encryption cert too. |
- */ |
- /* it is ok to compare the pointers since we ref count, and the same |
- * cert will always have the same pointer |
- */ |
- if ( ( ecert != NULL ) && ( ecert != scert ) ) { |
- rv = SEC_PKCS7AddCertificate(cinfo, ecert); |
- if ( rv != SECSuccess ) { |
- SEC_PKCS7DestroyContentInfo (cinfo); |
- return NULL; |
- } |
- } |
- /* |
- * Add the signing time. But if it fails for some reason, |
- * may as well not give up altogether -- just assert. |
- */ |
- rv = SEC_PKCS7AddSigningTime (cinfo); |
- PORT_Assert (rv == SECSuccess); |
- |
- /* |
- * Add the email profile. Again, if it fails for some reason, |
- * may as well not give up altogether -- just assert. |
- */ |
- rv = smime_add_profile (ecert, cinfo); |
- PORT_Assert (rv == SECSuccess); |
- |
- return cinfo; |
-} |