1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
* License, v. 2.0. If a copy of the MPL was not distributed with this
3
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
* Stuff specific to S/MIME policy and interoperability.
12
#include "ciferfam.h" /* for CIPHER_FAMILY symbols */
21
SEC_ASN1_MKSUB(CERT_IssuerAndSNTemplate)
22
SEC_ASN1_MKSUB(SEC_OctetStringTemplate)
23
SEC_ASN1_CHOOSER_DECLARE(CERT_IssuerAndSNTemplate)
25
/* various integer's ASN.1 encoding */
26
static unsigned char asn1_int40[] = { SEC_ASN1_INTEGER, 0x01, 0x28 };
27
static unsigned char asn1_int64[] = { SEC_ASN1_INTEGER, 0x01, 0x40 };
28
static unsigned char asn1_int128[] = { SEC_ASN1_INTEGER, 0x02, 0x00, 0x80 };
30
/* RC2 algorithm parameters (used in smime_cipher_map) */
31
static SECItem param_int40 = { siBuffer, asn1_int40, sizeof(asn1_int40) };
32
static SECItem param_int64 = { siBuffer, asn1_int64, sizeof(asn1_int64) };
33
static SECItem param_int128 = { siBuffer, asn1_int128, sizeof(asn1_int128) };
36
* XXX Would like the "parameters" field to be a SECItem *, but the
37
* encoder is having trouble with optional pointers to an ANY. Maybe
38
* once that is fixed, can change this back...
43
long cipher; /* optimization */
46
static const SEC_ASN1Template NSSSMIMECapabilityTemplate[] = {
48
0, NULL, sizeof(NSSSMIMECapability) },
50
offsetof(NSSSMIMECapability,capabilityID), },
51
{ SEC_ASN1_OPTIONAL | SEC_ASN1_ANY,
52
offsetof(NSSSMIMECapability,parameters), },
56
static const SEC_ASN1Template NSSSMIMECapabilitiesTemplate[] = {
57
{ SEC_ASN1_SEQUENCE_OF, 0, NSSSMIMECapabilityTemplate }
61
* NSSSMIMEEncryptionKeyPreference - if we find one of these, it needs to prompt us
62
* to store this and only this certificate permanently for the sender email address.
65
NSSSMIMEEncryptionKeyPref_IssuerSN,
66
NSSSMIMEEncryptionKeyPref_RKeyID,
67
NSSSMIMEEncryptionKeyPref_SubjectKeyID
68
} NSSSMIMEEncryptionKeyPrefSelector;
71
NSSSMIMEEncryptionKeyPrefSelector selector;
73
CERTIssuerAndSN *issuerAndSN;
74
NSSCMSRecipientKeyIdentifier *recipientKeyID;
75
SECItem *subjectKeyID;
77
} NSSSMIMEEncryptionKeyPreference;
79
extern const SEC_ASN1Template NSSCMSRecipientKeyIdentifierTemplate[];
81
static const SEC_ASN1Template smime_encryptionkeypref_template[] = {
83
offsetof(NSSSMIMEEncryptionKeyPreference,selector), NULL,
84
sizeof(NSSSMIMEEncryptionKeyPreference) },
85
{ SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0
86
| SEC_ASN1_CONSTRUCTED,
87
offsetof(NSSSMIMEEncryptionKeyPreference,id.issuerAndSN),
88
SEC_ASN1_SUB(CERT_IssuerAndSNTemplate),
89
NSSSMIMEEncryptionKeyPref_IssuerSN },
90
{ SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | 1
91
| SEC_ASN1_CONSTRUCTED,
92
offsetof(NSSSMIMEEncryptionKeyPreference,id.recipientKeyID),
93
NSSCMSRecipientKeyIdentifierTemplate,
94
NSSSMIMEEncryptionKeyPref_RKeyID },
95
{ SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 2
96
| SEC_ASN1_CONSTRUCTED,
97
offsetof(NSSSMIMEEncryptionKeyPreference,id.subjectKeyID),
98
SEC_ASN1_SUB(SEC_OctetStringTemplate),
99
NSSSMIMEEncryptionKeyPref_SubjectKeyID },
103
/* smime_cipher_map - map of SMIME symmetric "ciphers" to algtag & parameters */
105
unsigned long cipher;
108
PRBool enabled; /* in the user's preferences */
109
PRBool allowed; /* per export policy */
110
} smime_cipher_map_entry;
112
/* global: list of supported SMIME symmetric ciphers, ordered roughly by increasing strength */
113
static smime_cipher_map_entry smime_cipher_map[] = {
114
/* cipher algtag parms enabled allowed */
115
/* ---------------------------------------------------------------------------------- */
116
{ SMIME_RC2_CBC_40, SEC_OID_RC2_CBC, ¶m_int40, PR_TRUE, PR_TRUE },
117
{ SMIME_DES_CBC_56, SEC_OID_DES_CBC, NULL, PR_TRUE, PR_TRUE },
118
{ SMIME_RC2_CBC_64, SEC_OID_RC2_CBC, ¶m_int64, PR_TRUE, PR_TRUE },
119
{ SMIME_RC2_CBC_128, SEC_OID_RC2_CBC, ¶m_int128, PR_TRUE, PR_TRUE },
120
{ SMIME_DES_EDE3_168, SEC_OID_DES_EDE3_CBC, NULL, PR_TRUE, PR_TRUE },
121
{ SMIME_AES_CBC_128, SEC_OID_AES_128_CBC, NULL, PR_TRUE, PR_TRUE },
122
{ SMIME_AES_CBC_256, SEC_OID_AES_256_CBC, NULL, PR_TRUE, PR_TRUE }
124
static const int smime_cipher_map_count = sizeof(smime_cipher_map) / sizeof(smime_cipher_map_entry);
127
* smime_mapi_by_cipher - find index into smime_cipher_map by cipher
130
smime_mapi_by_cipher(unsigned long cipher)
134
for (i = 0; i < smime_cipher_map_count; i++) {
135
if (smime_cipher_map[i].cipher == cipher)
136
return i; /* bingo */
138
return -1; /* should not happen if we're consistent, right? */
142
* NSS_SMIME_EnableCipher - this function locally records the user's preference
145
NSS_SMIMEUtil_EnableCipher(unsigned long which, PRBool on)
150
mask = which & CIPHER_FAMILYID_MASK;
152
PORT_Assert (mask == CIPHER_FAMILYID_SMIME);
153
if (mask != CIPHER_FAMILYID_SMIME)
154
/* XXX set an error! */
157
mapi = smime_mapi_by_cipher(which);
159
/* XXX set an error */
162
/* do we try to turn on a forbidden cipher? */
163
if (!smime_cipher_map[mapi].allowed && on) {
164
PORT_SetError (SEC_ERROR_BAD_EXPORT_ALGORITHM);
168
if (smime_cipher_map[mapi].enabled != on)
169
smime_cipher_map[mapi].enabled = on;
176
* this function locally records the export policy
179
NSS_SMIMEUtil_AllowCipher(unsigned long which, PRBool on)
184
mask = which & CIPHER_FAMILYID_MASK;
186
PORT_Assert (mask == CIPHER_FAMILYID_SMIME);
187
if (mask != CIPHER_FAMILYID_SMIME)
188
/* XXX set an error! */
191
mapi = smime_mapi_by_cipher(which);
193
/* XXX set an error */
196
if (smime_cipher_map[mapi].allowed != on)
197
smime_cipher_map[mapi].allowed = on;
203
* Based on the given algorithm (including its parameters, in some cases!)
204
* and the given key (may or may not be inspected, depending on the
205
* algorithm), find the appropriate policy algorithm specification
206
* and return it. If no match can be made, -1 is returned.
209
nss_smime_get_cipher_for_alg_and_key(SECAlgorithmID *algid, PK11SymKey *key, unsigned long *cipher)
212
unsigned int keylen_bits;
215
algtag = SECOID_GetAlgorithmTag(algid);
217
case SEC_OID_RC2_CBC:
218
keylen_bits = PK11_GetKeyStrength(key, algid);
219
switch (keylen_bits) {
221
c = SMIME_RC2_CBC_40;
224
c = SMIME_RC2_CBC_64;
227
c = SMIME_RC2_CBC_128;
233
case SEC_OID_DES_CBC:
234
c = SMIME_DES_CBC_56;
236
case SEC_OID_DES_EDE3_CBC:
237
c = SMIME_DES_EDE3_168;
239
case SEC_OID_AES_128_CBC:
240
c = SMIME_AES_CBC_128;
242
case SEC_OID_AES_256_CBC:
243
c = SMIME_AES_CBC_256;
246
PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
254
nss_smime_cipher_allowed(unsigned long which)
258
mapi = smime_mapi_by_cipher(which);
261
return smime_cipher_map[mapi].allowed;
265
NSS_SMIMEUtil_DecryptionAllowed(SECAlgorithmID *algid, PK11SymKey *key)
269
if (nss_smime_get_cipher_for_alg_and_key(algid, key, &which) != SECSuccess)
272
return nss_smime_cipher_allowed(which);
277
* NSS_SMIME_EncryptionPossible - check if any encryption is allowed
279
* This tells whether or not *any* S/MIME encryption can be done,
280
* according to policy. Callers may use this to do nicer user interface
281
* (say, greying out a checkbox so a user does not even try to encrypt
282
* a message when they are not allowed to) or for any reason they want
283
* to check whether S/MIME encryption (or decryption, for that matter)
286
* It takes no arguments. The return value is a simple boolean:
287
* PR_TRUE means encryption (or decryption) is *possible*
288
* (but may still fail due to other reasons, like because we cannot
289
* find all the necessary certs, etc.; PR_TRUE is *not* a guarantee)
290
* PR_FALSE means encryption (or decryption) is not permitted
292
* There are no errors from this routine.
295
NSS_SMIMEUtil_EncryptionPossible(void)
299
for (i = 0; i < smime_cipher_map_count; i++) {
300
if (smime_cipher_map[i].allowed)
308
nss_SMIME_FindCipherForSMIMECap(NSSSMIMECapability *cap)
313
/* we need the OIDTag here */
314
capIDTag = SECOID_FindOIDTag(&(cap->capabilityID));
316
/* go over all the SMIME ciphers we know and see if we find a match */
317
for (i = 0; i < smime_cipher_map_count; i++) {
318
if (smime_cipher_map[i].algtag != capIDTag)
321
* XXX If SECITEM_CompareItem allowed NULLs as arguments (comparing
322
* 2 NULLs as equal and NULL and non-NULL as not equal), we could
323
* use that here instead of all of the following comparison code.
325
if (!smime_cipher_map[i].parms) {
326
if (!cap->parameters.data || !cap->parameters.len)
327
break; /* both empty: bingo */
328
if (cap->parameters.len == 2 &&
329
cap->parameters.data[0] == SEC_ASN1_NULL &&
330
cap->parameters.data[1] == 0)
331
break; /* DER NULL == NULL, bingo */
332
} else if (cap->parameters.data != NULL &&
333
cap->parameters.len == smime_cipher_map[i].parms->len &&
334
PORT_Memcmp (cap->parameters.data, smime_cipher_map[i].parms->data,
335
cap->parameters.len) == 0)
337
break; /* both not empty, same length & equal content: bingo */
341
if (i == smime_cipher_map_count)
342
return 0; /* no match found */
343
return smime_cipher_map[i].cipher; /* match found, point to cipher */
347
* smime_choose_cipher - choose a cipher that works for all the recipients
349
* "scert" - sender's certificate
350
* "rcerts" - recipient's certificates
353
smime_choose_cipher(CERTCertificate *scert, CERTCertificate **rcerts)
358
int *cipher_abilities;
364
int rcount, mapi, max, i;
366
chosen_cipher = SMIME_RC2_CBC_40; /* the default, LCD */
367
weak_mapi = smime_mapi_by_cipher(chosen_cipher);
368
aes128_mapi = smime_mapi_by_cipher(SMIME_AES_CBC_128);
369
aes256_mapi = smime_mapi_by_cipher(SMIME_AES_CBC_256);
371
poolp = PORT_NewArena (1024); /* XXX what is right value? */
375
cipher_abilities = (int *)PORT_ArenaZAlloc(poolp, smime_cipher_map_count * sizeof(int));
376
cipher_votes = (int *)PORT_ArenaZAlloc(poolp, smime_cipher_map_count * sizeof(int));
377
if (cipher_votes == NULL || cipher_abilities == NULL)
380
/* Make triple-DES the strong cipher. */
381
strong_mapi = smime_mapi_by_cipher (SMIME_DES_EDE3_168);
383
/* walk all the recipient's certs */
384
for (rcount = 0; rcerts[rcount] != NULL; rcount++) {
386
NSSSMIMECapability **caps;
389
/* the first cipher that matches in the user's SMIME profile gets
390
* "smime_cipher_map_count" votes; the next one gets "smime_cipher_map_count" - 1
391
* and so on. If every cipher matches, the last one gets 1 (one) vote */
392
pref = smime_cipher_map_count;
394
/* find recipient's SMIME profile */
395
profile = CERT_FindSMimeProfile(rcerts[rcount]);
397
if (profile != NULL && profile->data != NULL && profile->len > 0) {
398
/* we have a profile (still DER-encoded) */
401
if (SEC_QuickDERDecodeItem(poolp, &caps,
402
NSSSMIMECapabilitiesTemplate, profile) == SECSuccess &&
405
/* walk the SMIME capabilities for this recipient */
406
for (i = 0; caps[i] != NULL; i++) {
407
cipher = nss_SMIME_FindCipherForSMIMECap(caps[i]);
408
mapi = smime_mapi_by_cipher(cipher);
410
/* found the cipher */
411
cipher_abilities[mapi]++;
412
cipher_votes[mapi] += pref;
418
/* no profile found - so we can only assume that the user can do
419
* the mandatory algorithms which are RC2-40 (weak crypto) and
420
* 3DES (strong crypto), unless the user has an elliptic curve
421
* key. For elliptic curve keys, RFC 5753 mandates support
422
* for AES 128 CBC. */
423
SECKEYPublicKey *key;
424
unsigned int pklen_bits;
428
* if recipient's public key length is > 512, vote for a strong cipher
429
* please not that the side effect of this is that if only one recipient
430
* has an export-level public key, the strong cipher is disabled.
432
* XXX This is probably only good for RSA keys. What I would
433
* really like is a function to just say; Is the public key in
434
* this cert an export-length key? Then I would not have to
435
* know things like the value 512, or the kind of key, or what
436
* a subjectPublicKeyInfo is, etc.
438
key = CERT_ExtractPublicKey(rcerts[rcount]);
441
pklen_bits = SECKEY_PublicKeyStrengthInBits (key);
442
key_type = SECKEY_GetPublicKeyType(key);
443
SECKEY_DestroyPublicKey (key);
446
if (key_type == ecKey) {
447
/* While RFC 5753 mandates support for AES-128 CBC, should use
448
* AES 256 if user's key provides more than 128 bits of
449
* security strength so that symmetric key is not weak link. */
451
/* RC2-40 is not compatible with elliptic curve keys. */
452
chosen_cipher = SMIME_DES_EDE3_168;
453
if (pklen_bits > 256) {
454
cipher_abilities[aes256_mapi]++;
455
cipher_votes[aes256_mapi] += pref;
458
cipher_abilities[aes128_mapi]++;
459
cipher_votes[aes128_mapi] += pref;
461
cipher_abilities[strong_mapi]++;
462
cipher_votes[strong_mapi] += pref;
465
if (pklen_bits > 512) {
466
/* cast votes for the strong algorithm */
467
cipher_abilities[strong_mapi]++;
468
cipher_votes[strong_mapi] += pref;
472
/* always cast (possibly less) votes for the weak algorithm */
473
cipher_abilities[weak_mapi]++;
474
cipher_votes[weak_mapi] += pref;
478
SECITEM_FreeItem(profile, PR_TRUE);
481
/* find cipher that is agreeable by all recipients and that has the most votes */
483
for (mapi = 0; mapi < smime_cipher_map_count; mapi++) {
484
/* if not all of the recipients can do this, forget it */
485
if (cipher_abilities[mapi] != rcount)
487
/* if cipher is not enabled or not allowed by policy, forget it */
488
if (!smime_cipher_map[mapi].enabled || !smime_cipher_map[mapi].allowed)
490
/* now see if this one has more votes than the last best one */
491
if (cipher_votes[mapi] >= max) {
492
/* if equal number of votes, prefer the ones further down in the list */
493
/* with the expectation that these are higher rated ciphers */
494
chosen_cipher = smime_cipher_map[mapi].cipher;
495
max = cipher_votes[mapi];
498
/* if no common cipher was found, chosen_cipher stays at the default */
502
PORT_FreeArena (poolp, PR_FALSE);
504
return chosen_cipher;
508
* XXX This is a hack for now to satisfy our current interface.
509
* Eventually, with more parameters needing to be specified, just
510
* looking up the keysize is not going to be sufficient.
513
smime_keysize_by_cipher (unsigned long which)
518
case SMIME_RC2_CBC_40:
521
case SMIME_RC2_CBC_64:
524
case SMIME_RC2_CBC_128:
525
case SMIME_AES_CBC_128:
528
case SMIME_AES_CBC_256:
531
case SMIME_DES_CBC_56:
532
case SMIME_DES_EDE3_168:
534
* These are special; since the key size is fixed, we actually
535
* want to *avoid* specifying a key size.
548
* NSS_SMIMEUtil_FindBulkAlgForRecipients - find bulk algorithm suitable for all recipients
550
* it would be great for UI purposes if there would be a way to find out which recipients
551
* prevented a strong cipher from being used...
554
NSS_SMIMEUtil_FindBulkAlgForRecipients(CERTCertificate **rcerts, SECOidTag *bulkalgtag, int *keysize)
556
unsigned long cipher;
559
cipher = smime_choose_cipher(NULL, rcerts);
560
mapi = smime_mapi_by_cipher(cipher);
562
*bulkalgtag = smime_cipher_map[mapi].algtag;
563
*keysize = smime_keysize_by_cipher(smime_cipher_map[mapi].cipher);
569
* NSS_SMIMEUtil_CreateSMIMECapabilities - get S/MIME capabilities for this instance of NSS
571
* scans the list of allowed and enabled ciphers and construct a PKCS9-compliant
572
* S/MIME capabilities attribute value.
574
* XXX Please note that, in contradiction to RFC2633 2.5.2, the capabilities only include
575
* symmetric ciphers, NO signature algorithms or key encipherment algorithms.
577
* "poolp" - arena pool to create the S/MIME capabilities data on
578
* "dest" - SECItem to put the data in
581
NSS_SMIMEUtil_CreateSMIMECapabilities(PLArenaPool *poolp, SECItem *dest)
583
NSSSMIMECapability *cap;
584
NSSSMIMECapability **smime_capabilities;
585
smime_cipher_map_entry *map;
590
/* if we have an old NSSSMIMECapability array, we'll reuse it (has the right size) */
591
/* smime_cipher_map_count + 1 is an upper bound - we might end up with less */
592
smime_capabilities = (NSSSMIMECapability **)PORT_ZAlloc((smime_cipher_map_count + 1)
593
* sizeof(NSSSMIMECapability *));
594
if (smime_capabilities == NULL)
599
/* Add all the symmetric ciphers
600
* We walk the cipher list backwards, as it is ordered by increasing strength,
601
* we prefer the stronger cipher over a weaker one, and we have to list the
602
* preferred algorithm first */
603
for (i = smime_cipher_map_count - 1; i >= 0; i--) {
604
/* Find the corresponding entry in the cipher map. */
605
map = &(smime_cipher_map[i]);
609
/* get next SMIME capability */
610
cap = (NSSSMIMECapability *)PORT_ZAlloc(sizeof(NSSSMIMECapability));
613
smime_capabilities[capIndex++] = cap;
615
oiddata = SECOID_FindOIDByTag(map->algtag);
619
cap->capabilityID.data = oiddata->oid.data;
620
cap->capabilityID.len = oiddata->oid.len;
621
cap->parameters.data = map->parms ? map->parms->data : NULL;
622
cap->parameters.len = map->parms ? map->parms->len : 0;
623
cap->cipher = smime_cipher_map[i].cipher;
626
/* XXX add signature algorithms */
627
/* XXX add key encipherment algorithms */
629
smime_capabilities[capIndex] = NULL; /* last one - now encode */
630
dummy = SEC_ASN1EncodeItem(poolp, dest, &smime_capabilities, NSSSMIMECapabilitiesTemplate);
632
/* now that we have the proper encoded SMIMECapabilities (or not),
633
* free the work data */
634
for (i = 0; smime_capabilities[i] != NULL; i++)
635
PORT_Free(smime_capabilities[i]);
636
PORT_Free(smime_capabilities);
638
return (dummy == NULL) ? SECFailure : SECSuccess;
642
* NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs - create S/MIME encryption key preferences attr value
644
* "poolp" - arena pool to create the attr value on
645
* "dest" - SECItem to put the data in
646
* "cert" - certificate that should be marked as preferred encryption key
647
* cert is expected to have been verified for EmailRecipient usage.
650
NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs(PLArenaPool *poolp, SECItem *dest, CERTCertificate *cert)
652
NSSSMIMEEncryptionKeyPreference ekp;
653
SECItem *dummy = NULL;
654
PLArenaPool *tmppoolp = NULL;
659
tmppoolp = PORT_NewArena(1024);
660
if (tmppoolp == NULL)
663
/* XXX hardcoded IssuerSN choice for now */
664
ekp.selector = NSSSMIMEEncryptionKeyPref_IssuerSN;
665
ekp.id.issuerAndSN = CERT_GetCertIssuerAndSN(tmppoolp, cert);
666
if (ekp.id.issuerAndSN == NULL)
669
dummy = SEC_ASN1EncodeItem(poolp, dest, &ekp, smime_encryptionkeypref_template);
672
if (tmppoolp) PORT_FreeArena(tmppoolp, PR_FALSE);
674
return (dummy == NULL) ? SECFailure : SECSuccess;
678
* NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs - create S/MIME encryption key preferences attr value using MS oid
680
* "poolp" - arena pool to create the attr value on
681
* "dest" - SECItem to put the data in
682
* "cert" - certificate that should be marked as preferred encryption key
683
* cert is expected to have been verified for EmailRecipient usage.
686
NSS_SMIMEUtil_CreateMSSMIMEEncKeyPrefs(PLArenaPool *poolp, SECItem *dest, CERTCertificate *cert)
688
SECItem *dummy = NULL;
689
PLArenaPool *tmppoolp = NULL;
690
CERTIssuerAndSN *isn;
695
tmppoolp = PORT_NewArena(1024);
696
if (tmppoolp == NULL)
699
isn = CERT_GetCertIssuerAndSN(tmppoolp, cert);
703
dummy = SEC_ASN1EncodeItem(poolp, dest, isn, SEC_ASN1_GET(CERT_IssuerAndSNTemplate));
706
if (tmppoolp) PORT_FreeArena(tmppoolp, PR_FALSE);
708
return (dummy == NULL) ? SECFailure : SECSuccess;
712
* NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference -
713
* find cert marked by EncryptionKeyPreference attribute
715
* "certdb" - handle for the cert database to look in
716
* "DERekp" - DER-encoded value of S/MIME Encryption Key Preference attribute
718
* if certificate is supposed to be found among the message's included certificates,
719
* they are assumed to have been imported already.
722
NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference(CERTCertDBHandle *certdb, SECItem *DERekp)
724
PLArenaPool *tmppoolp = NULL;
725
CERTCertificate *cert = NULL;
726
NSSSMIMEEncryptionKeyPreference ekp;
728
tmppoolp = PORT_NewArena(1024);
729
if (tmppoolp == NULL)
733
if (SEC_QuickDERDecodeItem(tmppoolp, &ekp, smime_encryptionkeypref_template,
734
DERekp) != SECSuccess)
738
switch (ekp.selector) {
739
case NSSSMIMEEncryptionKeyPref_IssuerSN:
740
cert = CERT_FindCertByIssuerAndSN(certdb, ekp.id.issuerAndSN);
742
case NSSSMIMEEncryptionKeyPref_RKeyID:
743
case NSSSMIMEEncryptionKeyPref_SubjectKeyID:
744
/* XXX not supported yet - we need to be able to look up certs by SubjectKeyID */
750
if (tmppoolp) PORT_FreeArena(tmppoolp, PR_FALSE);
755
extern const char __nss_smime_rcsid[];
756
extern const char __nss_smime_sccsid[];
759
NSSSMIME_VersionCheck(const char *importedVersion)
762
* This is the secret handshake algorithm.
764
* This release has a simple version compatibility
765
* check algorithm. This release is not backward
766
* compatible with previous major releases. It is
767
* not compatible with future major, minor, or
770
volatile char c; /* force a reference that won't get optimized away */
772
c = __nss_smime_rcsid[0] + __nss_smime_sccsid[0];
774
return NSS_VersionCheck(importedVersion);
778
NSSSMIME_GetVersion(void)