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> 
  50 SEC_ASN1_MKSUB(CERT_IssuerAndSNTemplate
) 
  51 SEC_ASN1_MKSUB(SEC_OctetStringTemplate
) 
  52 SEC_ASN1_CHOOSER_DECLARE(CERT_IssuerAndSNTemplate
) 
  54 /* various integer's ASN.1 encoding */ 
  55 static unsigned char asn1_int40
[] = { SEC_ASN1_INTEGER
, 0x01, 0x28 }; 
  56 static unsigned char asn1_int64
[] = { SEC_ASN1_INTEGER
, 0x01, 0x40 }; 
  57 static unsigned char asn1_int128
[] = { SEC_ASN1_INTEGER
, 0x02, 0x00, 0x80 }; 
  59 /* RC2 algorithm parameters (used in smime_cipher_map) */ 
  60 static SecAsn1Item param_int40 
= { sizeof(asn1_int40
), asn1_int40 
}; 
  61 static SecAsn1Item param_int64 
= { sizeof(asn1_int64
), asn1_int64 
}; 
  62 static SecAsn1Item param_int128 
= { sizeof(asn1_int128
), asn1_int128 
}; 
  65  * XXX Would like the "parameters" field to be a SecAsn1Item * , but the 
  66  * encoder is having trouble with optional pointers to an ANY.  Maybe 
  67  * once that is fixed, can change this back... 
  70     SecAsn1Item capabilityID
; 
  71     SecAsn1Item parameters
; 
  72     long cipher
;                /* optimization */ 
  75 static const SecAsn1Template NSSSMIMECapabilityTemplate
[] = { 
  77           0, NULL
, sizeof(NSSSMIMECapability
) }, 
  79           offsetof(NSSSMIMECapability
,capabilityID
), }, 
  80     { SEC_ASN1_OPTIONAL 
| SEC_ASN1_ANY
, 
  81           offsetof(NSSSMIMECapability
,parameters
), }, 
  85 static const SecAsn1Template NSSSMIMECapabilitiesTemplate
[] = { 
  86     { SEC_ASN1_SEQUENCE_OF
, 0, NSSSMIMECapabilityTemplate 
} 
  90  * NSSSMIMEEncryptionKeyPreference - if we find one of these, it needs to prompt us 
  91  *  to store this and only this certificate permanently for the sender email address. 
  94     NSSSMIMEEncryptionKeyPref_IssuerSN
, 
  95     NSSSMIMEEncryptionKeyPref_RKeyID
, 
  96     NSSSMIMEEncryptionKeyPref_SubjectKeyID
 
  97 } NSSSMIMEEncryptionKeyPrefSelector
; 
 100     NSSSMIMEEncryptionKeyPrefSelector selector
; 
 102         SecCmsIssuerAndSN               
*issuerAndSN
; 
 103         SecCmsRecipientKeyIdentifier    
*recipientKeyID
; 
 104         SecAsn1Item 
*subjectKeyID
; 
 106 } NSSSMIMEEncryptionKeyPreference
; 
 108 extern const SecAsn1Template SecCmsRecipientKeyIdentifierTemplate
[]; 
 110 static const SecAsn1Template smime_encryptionkeypref_template
[] = { 
 112           offsetof(NSSSMIMEEncryptionKeyPreference
,selector
), NULL
, 
 113           sizeof(NSSSMIMEEncryptionKeyPreference
) }, 
 114     { SEC_ASN1_POINTER 
| SEC_ASN1_CONTEXT_SPECIFIC 
| SEC_ASN1_XTRN 
| 0, 
 115           offsetof(NSSSMIMEEncryptionKeyPreference
,id
.issuerAndSN
), 
 116           SEC_ASN1_SUB(SecCmsIssuerAndSNTemplate
), 
 117           NSSSMIMEEncryptionKeyPref_IssuerSN 
}, 
 118     { SEC_ASN1_POINTER 
| SEC_ASN1_CONTEXT_SPECIFIC 
| 1, 
 119           offsetof(NSSSMIMEEncryptionKeyPreference
,id
.recipientKeyID
), 
 120           SecCmsRecipientKeyIdentifierTemplate
, 
 121           NSSSMIMEEncryptionKeyPref_IssuerSN 
}, 
 122     { SEC_ASN1_POINTER 
| SEC_ASN1_CONTEXT_SPECIFIC 
| SEC_ASN1_XTRN 
| 2, 
 123           offsetof(NSSSMIMEEncryptionKeyPreference
,id
.subjectKeyID
), 
 124           SEC_ASN1_SUB(kSecAsn1OctetStringTemplate
), 
 125           NSSSMIMEEncryptionKeyPref_SubjectKeyID 
}, 
 129 /* smime_cipher_map - map of SMIME symmetric "ciphers" to algtag & parameters */ 
 131     unsigned long cipher
; 
 134     Boolean enabled
;    /* in the user's preferences */ 
 135     Boolean allowed
;    /* per export policy */ 
 136 } smime_cipher_map_entry
; 
 138 /* global: list of supported SMIME symmetric ciphers, ordered roughly by increasing strength */ 
 139 static smime_cipher_map_entry smime_cipher_map
[] = { 
 140 /*    cipher                    algtag                  parms           enabled  allowed */ 
 141 /*    ---------------------------------------------------------------------------------- */ 
 142     { SMIME_RC2_CBC_40
,         SEC_OID_RC2_CBC
,        ¶m_int40
,   PR_FALSE
, PR_FALSE 
}, 
 143     { SMIME_DES_CBC_56
,         SEC_OID_DES_CBC
,        NULL
,                   PR_TRUE
, PR_FALSE 
}, 
 144     { SMIME_RC2_CBC_64
,         SEC_OID_RC2_CBC
,        ¶m_int64
,   PR_FALSE
, PR_FALSE 
}, 
 145     { SMIME_RC2_CBC_128
,        SEC_OID_RC2_CBC
,        ¶m_int128
,  PR_FALSE
, PR_FALSE 
}, 
 146     { SMIME_DES_EDE3_168
,       SEC_OID_DES_EDE3_CBC
,   NULL
,           PR_TRUE
, PR_TRUE 
}, 
 147     { SMIME_AES_CBC_128
,        SEC_OID_AES_128_CBC
,    NULL
,           PR_TRUE
, PR_TRUE 
}, 
 148     { SMIME_AES_CBC_192
,        SEC_OID_AES_192_CBC
,    NULL
,           PR_TRUE
, PR_TRUE 
}, 
 149     { SMIME_AES_CBC_256
,        SEC_OID_AES_256_CBC
,    NULL
,           PR_TRUE
, PR_TRUE 
}, 
 150     { SMIME_FORTEZZA
,           SEC_OID_FORTEZZA_SKIPJACK
, NULL
,        PR_TRUE
, PR_TRUE 
} 
 152 static const int smime_cipher_map_count 
= sizeof(smime_cipher_map
) / sizeof(smime_cipher_map_entry
); 
 155  * smime_mapi_by_cipher - find index into smime_cipher_map by cipher 
 158 smime_mapi_by_cipher(unsigned long cipher
) 
 162     for (i 
= 0; i 
< smime_cipher_map_count
; i
++) { 
 163         if (smime_cipher_map
[i
].cipher 
== cipher
) 
 164             return i
;   /* bingo */ 
 166     return -1;          /* should not happen if we're consistent, right? */ 
 170  * NSS_SMIME_EnableCipher - this function locally records the user's preference 
 173 SecSMIMEEnableCipher(unsigned long which
, Boolean on
) 
 178     mask 
= which 
& CIPHER_FAMILYID_MASK
; 
 180     PORT_Assert (mask 
== CIPHER_FAMILYID_SMIME
); 
 181     if (mask 
!= CIPHER_FAMILYID_SMIME
) 
 182         /* XXX set an error! */ 
 185     mapi 
= smime_mapi_by_cipher(which
); 
 187         /* XXX set an error */ 
 190     /* do we try to turn on a forbidden cipher? */ 
 191     if (!smime_cipher_map
[mapi
].allowed 
&& on
) { 
 192         PORT_SetError (SEC_ERROR_BAD_EXPORT_ALGORITHM
); 
 196     if (smime_cipher_map
[mapi
].enabled 
!= on
) 
 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     if (smime_cipher_map
[mapi
].allowed 
!= on
) 
 225         smime_cipher_map
[mapi
].allowed 
= on
; 
 231  * Based on the given algorithm (including its parameters, in some cases!) 
 232  * and the given key (may or may not be inspected, depending on the 
 233  * algorithm), find the appropriate policy algorithm specification 
 234  * and return it.  If no match can be made, -1 is returned. 
 237 nss_smime_get_cipher_for_alg_and_key(SECAlgorithmID 
*algid
, SecSymmetricKeyRef key
, unsigned long *cipher
) 
 243     algtag 
= SECOID_GetAlgorithmTag(algid
); 
 245     case SEC_OID_RC2_CBC
: 
 247         if (SecKeyGetStrengthInBits(key
, algid
, &keylen_bits
)) 
 250         keylen_bits 
= CFDataGetLength((CFDataRef
)key
) * 8; 
 252         switch (keylen_bits
) { 
 254             c 
= SMIME_RC2_CBC_40
; 
 257             c 
= SMIME_RC2_CBC_64
; 
 260             c 
= SMIME_RC2_CBC_128
; 
 266     case SEC_OID_DES_CBC
: 
 267         c 
= SMIME_DES_CBC_56
; 
 269     case SEC_OID_FORTEZZA_SKIPJACK
: 
 272     case SEC_OID_DES_EDE3_CBC
: 
 273         c 
= SMIME_DES_EDE3_168
; 
 275     case SEC_OID_AES_128_CBC
: 
 276         c 
= SMIME_AES_CBC_128
; 
 278     case SEC_OID_AES_192_CBC
: 
 279         c 
= SMIME_AES_CBC_192
; 
 281     case SEC_OID_AES_256_CBC
: 
 282         c 
= SMIME_AES_CBC_256
; 
 292 nss_smime_cipher_allowed(unsigned long which
) 
 296     mapi 
= smime_mapi_by_cipher(which
); 
 299     return smime_cipher_map
[mapi
].allowed
; 
 303 SecSMIMEDecryptionAllowed(SECAlgorithmID 
*algid
, SecSymmetricKeyRef key
) 
 307     if (nss_smime_get_cipher_for_alg_and_key(algid
, key
, &which
) != SECSuccess
) 
 310     return nss_smime_cipher_allowed(which
); 
 315  * NSS_SMIME_EncryptionPossible - check if any encryption is allowed 
 317  * This tells whether or not *any* S/MIME encryption can be done, 
 318  * according to policy.  Callers may use this to do nicer user interface 
 319  * (say, greying out a checkbox so a user does not even try to encrypt 
 320  * a message when they are not allowed to) or for any reason they want 
 321  * to check whether S/MIME encryption (or decryption, for that matter) 
 324  * It takes no arguments.  The return value is a simple boolean: 
 325  *   PR_TRUE means encryption (or decryption) is *possible* 
 326  *      (but may still fail due to other reasons, like because we cannot 
 327  *      find all the necessary certs, etc.; PR_TRUE is *not* a guarantee) 
 328  *   PR_FALSE means encryption (or decryption) is not permitted 
 330  * There are no errors from this routine. 
 333 SecSMIMEEncryptionPossible(void) 
 337     for (i 
= 0; i 
< smime_cipher_map_count
; i
++) { 
 338         if (smime_cipher_map
[i
].allowed
) 
 346 nss_SMIME_FindCipherForSMIMECap(NSSSMIMECapability 
*cap
) 
 351     /* we need the OIDTag here */ 
 352     capIDTag 
= SECOID_FindOIDTag(&(cap
->capabilityID
)); 
 354     /* go over all the SMIME ciphers we know and see if we find a match */ 
 355     for (i 
= 0; i 
< smime_cipher_map_count
; i
++) { 
 356         if (smime_cipher_map
[i
].algtag 
!= capIDTag
) 
 359          * XXX If SECITEM_CompareItem allowed NULLs as arguments (comparing 
 360          * 2 NULLs as equal and NULL and non-NULL as not equal), we could 
 361          * use that here instead of all of the following comparison code. 
 363         if (cap
->parameters
.Data 
== NULL 
&& smime_cipher_map
[i
].parms 
== NULL
) 
 364             break;      /* both empty: bingo */ 
 366         if (cap
->parameters
.Data 
!= NULL 
&& smime_cipher_map
[i
].parms 
!= NULL 
&& 
 367             cap
->parameters
.Length 
== smime_cipher_map
[i
].parms
->Length 
&& 
 368             PORT_Memcmp (cap
->parameters
.Data
, smime_cipher_map
[i
].parms
->Data
, 
 369                              cap
->parameters
.Length
) == 0) 
 371             break;      /* both not empty, same length & equal content: bingo */ 
 375     if (i 
== smime_cipher_map_count
) 
 376         return 0;                               /* no match found */ 
 378         return smime_cipher_map
[i
].cipher
;      /* match found, point to cipher */ 
 381 static int smime_keysize_by_cipher (unsigned long which
); 
 384  * smime_choose_cipher - choose a cipher that works for all the recipients 
 386  * "scert"  - sender's certificate 
 387  * "rcerts" - recipient's certificates 
 390 smime_choose_cipher(SecCertificateRef scert
, SecCertificateRef 
*rcerts
) 
 395     int *cipher_abilities
; 
 399     int rcount
, mapi
, max
, i
; 
 401     // @@@ We Don't support Fortezza yet. 
 402     Boolean scert_is_fortezza  
= PR_FALSE
; 
 404     Boolean scert_is_fortezza 
= (scert 
== NULL
) ? PR_FALSE 
: PK11_FortezzaHasKEA(scert
); 
 407     chosen_cipher 
= SMIME_DES_CBC_56
;           /* the default, LCD */ 
 408     weak_mapi 
= smime_mapi_by_cipher(chosen_cipher
); 
 410     poolp 
= PORT_NewArena (1024);               /* XXX what is right value? */ 
 414     cipher_abilities 
= (int *)PORT_ArenaZAlloc(poolp
, smime_cipher_map_count 
* sizeof(int)); 
 415     cipher_votes     
= (int *)PORT_ArenaZAlloc(poolp
, smime_cipher_map_count 
* sizeof(int)); 
 416     if (cipher_votes 
== NULL 
|| cipher_abilities 
== NULL
) 
 419     /* If the user has the Fortezza preference turned on, make 
 420      *  that the strong cipher. Otherwise, use triple-DES. */ 
 421     strong_mapi 
= smime_mapi_by_cipher (SMIME_DES_EDE3_168
); 
 422     if (scert_is_fortezza
) { 
 423         mapi 
= smime_mapi_by_cipher(SMIME_FORTEZZA
); 
 424         if (mapi 
>= 0 && smime_cipher_map
[mapi
].enabled
) 
 428     /* walk all the recipient's certs */ 
 429     for (rcount 
= 0; rcerts
[rcount
] != NULL
; rcount
++) { 
 430         SecAsn1Item 
*profile
; 
 431         NSSSMIMECapability 
**caps
; 
 434         /* the first cipher that matches in the user's SMIME profile gets 
 435          * "smime_cipher_map_count" votes; the next one gets "smime_cipher_map_count" - 1 
 436          * and so on. If every cipher matches, the last one gets 1 (one) vote */ 
 437         pref 
= smime_cipher_map_count
; 
 439         /* find recipient's SMIME profile */ 
 440         profile 
= CERT_FindSMimeProfile(rcerts
[rcount
]); 
 442         if (profile 
!= NULL 
&& profile
->Data 
!= NULL 
&& profile
->Length 
> 0) { 
 443             /* we have a profile (still DER-encoded) */ 
 446             if (SEC_ASN1DecodeItem(poolp
, &caps
, NSSSMIMECapabilitiesTemplate
, profile
) == SECSuccess 
&& 
 449                 /* walk the SMIME capabilities for this recipient */ 
 450                 for (i 
= 0; caps
[i
] != NULL
; i
++) { 
 451                     cipher 
= nss_SMIME_FindCipherForSMIMECap(caps
[i
]); 
 452                     mapi 
= smime_mapi_by_cipher(cipher
); 
 454                         /* found the cipher */ 
 455                         cipher_abilities
[mapi
]++; 
 456                         cipher_votes
[mapi
] += pref
; 
 462             /* no profile found - so we can only assume that the user can do 
 463              * the mandatory algorithms which is RC2-40 (weak crypto) and 3DES (strong crypto) */ 
 468              * if recipient's public key length is > 512, vote for a strong cipher 
 469              * please not that the side effect of this is that if only one recipient 
 470              * has an export-level public key, the strong cipher is disabled. 
 472              * XXX This is probably only good for RSA keys.  What I would 
 473              * really like is a function to just say;  Is the public key in 
 474              * this cert an export-length key?  Then I would not have to 
 475              * know things like the value 512, or the kind of key, or what 
 476              * a subjectPublicKeyInfo is, etc. 
 478             key 
= CERT_ExtractPublicKey(rcerts
[rcount
]); 
 482                 SecKeyGetStrengthInBits(key
, NULL
, &pklen_bits
); 
 484                 pklen_bits 
= SecKeyGetSize(key
, kSecKeyKeySizeInBits
); 
 486                 SECKEY_DestroyPublicKey (key
); 
 489             if (pklen_bits 
> 512) { 
 490                 /* cast votes for the strong algorithm */ 
 491                 cipher_abilities
[strong_mapi
]++; 
 492                 cipher_votes
[strong_mapi
] += pref
; 
 496             /* always cast (possibly less) votes for the weak algorithm */ 
 497             cipher_abilities
[weak_mapi
]++; 
 498             cipher_votes
[weak_mapi
] += pref
; 
 501             SECITEM_FreeItem(profile
, PR_TRUE
); 
 504     /* find cipher that is agreeable by all recipients and that has the most votes */ 
 506     for (mapi 
= 0; mapi 
< smime_cipher_map_count
; mapi
++) { 
 507         /* if not all of the recipients can do this, forget it */ 
 508         if (cipher_abilities
[mapi
] != rcount
) 
 510         /* if cipher is not enabled or not allowed by policy, forget it */ 
 511         if (!smime_cipher_map
[mapi
].enabled 
|| !smime_cipher_map
[mapi
].allowed
) 
 513         /* if we're not doing fortezza, but the cipher is fortezza, forget it */ 
 514         if (!scert_is_fortezza  
&& (smime_cipher_map
[mapi
].cipher 
== SMIME_FORTEZZA
)) 
 516         /* now see if this one has more votes than the last best one */ 
 517         if (cipher_votes
[mapi
] >= max
) { 
 518             /* if equal number of votes, prefer the ones further down in the list */ 
 519             /* with the expectation that these are higher rated ciphers */ 
 520             chosen_cipher 
= smime_cipher_map
[mapi
].cipher
; 
 521             max 
= cipher_votes
[mapi
]; 
 524     /* if no common cipher was found, chosen_cipher stays at the default */ 
 528         PORT_FreeArena (poolp
, PR_FALSE
); 
 530     if (smime_keysize_by_cipher(chosen_cipher
) < 128) { 
 531         /* you're going to use strong(er) crypto whether you like it or not */ 
 532         chosen_cipher 
= SMIME_DES_EDE3_168
; 
 534     return chosen_cipher
; 
 538  * XXX This is a hack for now to satisfy our current interface. 
 539  * Eventually, with more parameters needing to be specified, just 
 540  * looking up the keysize is not going to be sufficient. 
 543 smime_keysize_by_cipher (unsigned long which
) 
 548       case SMIME_RC2_CBC_40
: 
 551       case SMIME_RC2_CBC_64
: 
 554       case SMIME_RC2_CBC_128
: 
 557       case SMIME_DES_CBC_56
: 
 560       case SMIME_DES_EDE3_168
: 
 565          * This is special; since the key size is fixed, we actually 
 566          * want to *avoid* specifying a key size. 
 579  * SecSMIMEFindBulkAlgForRecipients - find bulk algorithm suitable for all recipients 
 581  * it would be great for UI purposes if there would be a way to find out which recipients 
 582  * prevented a strong cipher from being used... 
 585 SecSMIMEFindBulkAlgForRecipients(SecCertificateRef 
*rcerts
, SECOidTag 
*bulkalgtag
, int *keysize
) 
 587     unsigned long cipher
; 
 590     cipher 
= smime_choose_cipher(NULL
, rcerts
); 
 591     mapi 
= smime_mapi_by_cipher(cipher
); 
 593     *bulkalgtag 
= smime_cipher_map
[mapi
].algtag
; 
 594     *keysize 
= smime_keysize_by_cipher(smime_cipher_map
[mapi
].cipher
); 
 600  * SecSMIMECreateSMIMECapabilities - get S/MIME capabilities for this instance of NSS 
 602  * scans the list of allowed and enabled ciphers and construct a PKCS9-compliant 
 603  * S/MIME capabilities attribute value. 
 605  * XXX Please note that, in contradiction to RFC2633 2.5.2, the capabilities only include 
 606  * symmetric ciphers, NO signature algorithms or key encipherment algorithms. 
 608  * "poolp" - arena pool to create the S/MIME capabilities data on 
 609  * "dest" - SecAsn1Item to put the data in 
 610  * "includeFortezzaCiphers" - PR_TRUE if fortezza ciphers should be included 
 613 SecSMIMECreateSMIMECapabilities(PLArenaPool 
*poolp
, SecAsn1Item 
*dest
, Boolean includeFortezzaCiphers
) 
 615     NSSSMIMECapability 
*cap
; 
 616     NSSSMIMECapability 
**smime_capabilities
; 
 617     smime_cipher_map_entry 
*map
; 
 622     /* if we have an old NSSSMIMECapability array, we'll reuse it (has the right size) */ 
 623     /* smime_cipher_map_count + 1 is an upper bound - we might end up with less */ 
 624     smime_capabilities 
= (NSSSMIMECapability 
**)PORT_ZAlloc((smime_cipher_map_count 
+ 1) 
 625                                       * sizeof(NSSSMIMECapability 
*)); 
 626     if (smime_capabilities 
== NULL
) 
 631     /* Add all the symmetric ciphers 
 632      * We walk the cipher list backwards, as it is ordered by increasing strength, 
 633      * we prefer the stronger cipher over a weaker one, and we have to list the 
 634      * preferred algorithm first */ 
 635     for (i 
= smime_cipher_map_count 
- 1; i 
>= 0; i
--) { 
 636         /* Find the corresponding entry in the cipher map. */ 
 637         map 
= &(smime_cipher_map
[i
]); 
 641         /* If we're using a non-Fortezza cert, only advertise non-Fortezza 
 642            capabilities. (We advertise all capabilities if we have a  
 644         if ((!includeFortezzaCiphers
) && (map
->cipher 
== SMIME_FORTEZZA
)) 
 647         /* get next SMIME capability */ 
 648         cap 
= (NSSSMIMECapability 
*)PORT_ZAlloc(sizeof(NSSSMIMECapability
)); 
 651         smime_capabilities
[capIndex
++] = cap
; 
 653         oiddata 
= SECOID_FindOIDByTag(map
->algtag
); 
 657         cap
->capabilityID
.Data 
= oiddata
->oid
.Data
; 
 658         cap
->capabilityID
.Length 
= oiddata
->oid
.Length
; 
 659         cap
->parameters
.Data 
= map
->parms 
? map
->parms
->Data 
: NULL
; 
 660         cap
->parameters
.Length 
= map
->parms 
? map
->parms
->Length 
: 0; 
 661         cap
->cipher 
= smime_cipher_map
[i
].cipher
; 
 664     /* XXX add signature algorithms */ 
 665     /* XXX add key encipherment algorithms */ 
 667     smime_capabilities
[capIndex
] = NULL
;        /* last one - now encode */ 
 668     dummy 
= SEC_ASN1EncodeItem(poolp
, dest
, &smime_capabilities
, NSSSMIMECapabilitiesTemplate
); 
 670     /* now that we have the proper encoded SMIMECapabilities (or not), 
 671      * free the work data */ 
 672     for (i 
= 0; smime_capabilities
[i
] != NULL
; i
++) 
 673         PORT_Free(smime_capabilities
[i
]); 
 674     PORT_Free(smime_capabilities
); 
 676     return (dummy 
== NULL
) ? SECFailure 
: SECSuccess
; 
 680  * SecSMIMECreateSMIMEEncKeyPrefs - create S/MIME encryption key preferences attr value 
 682  * "poolp" - arena pool to create the attr value on 
 683  * "dest" - SecAsn1Item to put the data in 
 684  * "cert" - certificate that should be marked as preferred encryption key 
 685  *          cert is expected to have been verified for EmailRecipient usage. 
 688 SecSMIMECreateSMIMEEncKeyPrefs(PLArenaPool 
*poolp
, SecAsn1Item 
*dest
, SecCertificateRef cert
) 
 690     NSSSMIMEEncryptionKeyPreference ekp
; 
 691     SecAsn1Item 
*dummy 
= NULL
; 
 692     PLArenaPool 
*tmppoolp 
= NULL
; 
 697     tmppoolp 
= PORT_NewArena(1024); 
 698     if (tmppoolp 
== NULL
) 
 701     /* XXX hardcoded IssuerSN choice for now */ 
 702     ekp
.selector 
= NSSSMIMEEncryptionKeyPref_IssuerSN
; 
 703     ekp
.id
.issuerAndSN 
= CERT_GetCertIssuerAndSN(tmppoolp
, cert
); 
 704     if (ekp
.id
.issuerAndSN 
== NULL
) 
 707     dummy 
= SEC_ASN1EncodeItem(poolp
, dest
, &ekp
, smime_encryptionkeypref_template
); 
 710     if (tmppoolp
) PORT_FreeArena(tmppoolp
, PR_FALSE
); 
 712     return (dummy 
== NULL
) ? SECFailure 
: SECSuccess
; 
 716  * SecSMIMECreateSMIMEEncKeyPrefs - create S/MIME encryption key preferences attr value using MS oid 
 718  * "poolp" - arena pool to create the attr value on 
 719  * "dest" - SecAsn1Item to put the data in 
 720  * "cert" - certificate that should be marked as preferred encryption key 
 721  *          cert is expected to have been verified for EmailRecipient usage. 
 724 SecSMIMECreateMSSMIMEEncKeyPrefs(PLArenaPool 
*poolp
, SecAsn1Item 
*dest
, SecCertificateRef cert
) 
 726     SecAsn1Item 
*dummy 
= NULL
; 
 727     PLArenaPool 
*tmppoolp 
= NULL
; 
 728     SecCmsIssuerAndSN 
*isn
; 
 733     tmppoolp 
= PORT_NewArena(1024); 
 734     if (tmppoolp 
== NULL
) 
 737     isn 
= CERT_GetCertIssuerAndSN(tmppoolp
, cert
); 
 741     dummy 
= SEC_ASN1EncodeItem(poolp
, dest
, isn
, SEC_ASN1_GET(SecCmsIssuerAndSNTemplate
)); 
 744     if (tmppoolp
) PORT_FreeArena(tmppoolp
, PR_FALSE
); 
 746     return (dummy 
== NULL
) ? SECFailure 
: SECSuccess
; 
 751  * SecSMIMEGetCertFromEncryptionKeyPreference - 
 752  *                              find cert marked by EncryptionKeyPreference attribute 
 754  * "keychainOrArray" - handle for the cert database to look in 
 755  * "DERekp" - DER-encoded value of S/MIME Encryption Key Preference attribute 
 757  * if certificate is supposed to be found among the message's included certificates, 
 758  * they are assumed to have been imported already. 
 761 SecSMIMEGetCertFromEncryptionKeyPreference(SecKeychainRef keychainOrArray
, SecAsn1Item 
*DERekp
) 
 763     PLArenaPool 
*tmppoolp 
= NULL
; 
 764     SecCertificateRef cert 
= NULL
; 
 765     NSSSMIMEEncryptionKeyPreference ekp
; 
 767     tmppoolp 
= PORT_NewArena(1024); 
 768     if (tmppoolp 
== NULL
) 
 772     if (SEC_ASN1DecodeItem(tmppoolp
, &ekp
, smime_encryptionkeypref_template
, DERekp
) != SECSuccess
) 
 776     switch (ekp
.selector
) { 
 777     case NSSSMIMEEncryptionKeyPref_IssuerSN
: 
 778         cert 
= CERT_FindCertByIssuerAndSN(keychainOrArray
, ekp
.id
.issuerAndSN
); 
 780     case NSSSMIMEEncryptionKeyPref_RKeyID
: 
 781     case NSSSMIMEEncryptionKeyPref_SubjectKeyID
: 
 782         /* XXX not supported yet - we need to be able to look up certs by SubjectKeyID */ 
 788     if (tmppoolp
) PORT_FreeArena(tmppoolp
, PR_FALSE
); 
 795 extern const char __nss_smime_rcsid
[]; 
 796 extern const char __nss_smime_sccsid
[]; 
 799 NSSSMIME_VersionCheck(const char *importedVersion
) 
 805      * This is the secret handshake algorithm. 
 807      * This release has a simple version compatibility 
 808      * check algorithm.  This release is not backward 
 809      * compatible with previous major releases.  It is 
 810      * not compatible with future major, minor, or 
 813     volatile char c
; /* force a reference that won't get optimized away */ 
 815     c 
= __nss_smime_rcsid
[0] + __nss_smime_sccsid
[0];  
 817     return NSS_VersionCheck(importedVersion
);