Closed Bug 269581 Opened 20 years ago Closed 20 years ago

NSS calls C_GetAttributeValue unnecessarily when token is logged in


(NSS :: Libraries, defect, P1)



(Not tracked)



(Reporter: julien.pierre, Assigned: julien.pierre)



(1 file, 5 obsolete files)

Some changes were implemented recently in NSS 3.9.2 to allow using smartcards
that had RSA private keys without the CKA_PRIVATE attribute, ie. they didn't
need the token to be logged in . The checks look like :

    if (!PK11_HasAttributeSet(slot,wrappingKey->pkcs11ID,CKA_PRIVATE)) {

This has a significant performance impact in the SSL server-side codepath,
because C_GetAttributeValue is now being called on the RSA private key for each
full SSL handshake to check if we need to login to the token to use the key,
even though the token is already logged in in most cases.

The optimization is to check PK11_IsLoggedIn() first, and if true, skip the
password check. This needs to be done in a number of places.
Attached patch patch for the tip (obsolete) — Splinter Review
Attached patch patch for NSS_3_9_BRANCH (obsolete) — Splinter Review
Priority: -- → P2
Target Milestone: --- → 3.9.5
Attachment #165767 - Flags: review?(nelson)
The logic of these patches appears OK to me, but they seem not to follow 
the usual NSS coding style conventions, e.g. lines much longer than 80 
columns and if statements with the opening brace on the following line.
So, please fix the coding style of the patches.  
And then please ask Bob Relyea to review them.  Thanks.
Attachment #165767 - Flags: superreview?(rrelyea0264)
Comment on attachment 165767 [details] [diff] [review]
patch for the tip

The change to pk11akey.c is fine, but the changes in pk11obj.c and pk11skey.c
change the semantics of NSS. PK11_HandlePasswordCheck may require a new
authentication of the password has 'timed out'. 

A better idea might be to cache the value of CKA_PRIVATE in the private key
structure in PK11_MakePrivKey and only checked that cached value.

Attachment #165767 - Flags: superreview?(rrelyea0264) → superreview-
OK, now that I think about it, servers never allow reauthentication anyway
(typically all the tokens are authenticated on startup with no provision for
reauthentication later). We could really improve things by setting an NSS
'attribute' called 'no reauthentication'. In that case we would never check
'isLoggedIn' in NSS, never call PK11_Authenticate, (and won't need to check
CKA_PRIVATE in the places identified in this patch).


Actually, it may still be that a logout & reauthentication happens in the server
case, accidentally, say, through an NSAPI plug-in, or java servlet running
inside the server logging out the token ... But it isn't a common case and I
don't know if we want to support it.
I agree the optimization you suggested would be good to have if we could
guarantee that logins never need to be done again, and that the state of the
cached information in the private key structure never changes, which would
require additional locking for the object.

Nelson : would we have a way at the SSL API level to know this upfront so we
could set that bit without somewhere needing the user to call a new API to tell us ?

Another possible solution would be to just try using the key, and only login if
we get CKR_USER_NOT_LOGGED_IN, rather than trying to check if we are logged in
upfront. But that may break compatibility for modules that return the wrong
error code ...
Answering my own question originalyl for Nelson here :

I noticed that SSL_ConfigSecureServer is making a copy of the private key during
setup. One optimization is to add a bit to that copy of SECKEYPrivateKey, which
can then be looked up later at use time to skip the check. I'm going to add a
patch that uses this optimization.

FYI, the cost of a C_GetAttributeValue with the NSS softoken is insignificant,
but with another softoken I am testing with, which is multiprocess safe for
read/write, the cost of C_GetAttributeValue is very significant due to disk I/O,
and it makes that token completely unusable for SSL, so we need to avoid this
call in the SSL server codepath.
However, I found the call to PK11_IsLoggedIn is also not free and has negative
impact when using the NSS softoken of about 3%, so some sort of caching would be
This patch could still be improved to login in case we get
CKR_USER_NOT_LOGGED_IN from the following operation.
Attachment #165767 - Attachment is obsolete: true
Attachment #165768 - Attachment is obsolete: true
Attachment #165767 - Flags: review?(nelson)
Attachment #166072 - Flags: superreview?(rrelyea0264)
Attachment #166072 - Flags: review?(nelson)
> FYI, the cost of a C_GetAttributeValue with the NSS softoken is insignificant,
> but with another softoken ... the cost of C_GetAttributeValue is very
> significant...

 I expected that was the impedious behind the change. That is way I thought caching

> However, I found the call to PK11_IsLoggedIn is also not free and has negative
> impact when using the NSS softoken of about 3%

I was also afraid of that. In general I would expect IsLoggedIn (though
IsLoggedIn caches it's last known state for about 1-3 seconds just like
IsPresent does) to be as expensive or more expensive than a GetAttributeValue
call. In the server case, the HasAttributeSet call will always return 'true', so
we then drop into PK11_HandlePasswordCheck which, among other things, calls
IsLoggedIn again. Your current patch is probably the simplest and most efficient
way for servers to operate.

I need to double check that we've handled all the required paths for adding a
new member to PK11PrivateKey before I give it a r+. Currently my only nit would
be I'd like the name of the bool to be something like "never reauthenticates"
which we can then later add a call to set or clear the value on the key from the
application (In case we have client apps which never reauthenticates, or in case
we have server apps that need to).

Comment on attachment 166072 [details] [diff] [review]
new optimization for tip . Relies on SSL_ConfigSecureServer

This patch changes the behavior of libSSL for ALL programs that use it to act
an SSL server.	It changes those products so that they will no longer attempt
to (re)authenticate to the token if the token decides it is no longer in a 
logged-in state at any time after SSL_ConfigSecureServer returns.

That may be an acceptable change for commercial https server products running 
with commercial-grade RSA accelerators.  But consider a program that acts as a
low-volume server, and uses an inexpensive personal crypto token that happens
to require re-authentication to the token every 5 minutes.  Such a program
would work OK with NSS as it is today, and would stop working with this patch.

So, I think this is an unacceptable semantic change to libSSL.
Attachment #166072 - Flags: review?(nelson) → review-

Unfortunately, continuing to support the smartcard SSL server case is quite
complicated. There are 2 approaches :

1) have a configurable NSS setting for this optimization

This would have the disadvantage that it's one more application call to make.
And what's the right default ? Prior to 3.9.2, none of this code that we are
trying to optimize here existed .

The setting could be a global one (for all of pk11wrap), or for individual SSL
server sockets.

2) first try the operation without login, and then if it fails with 

CKR_USER_NOT_LOGGED_IN, login and try again .

Looking at the current code, most functions involved here do not return a CK_RV
. There is a lot of work needed to propagate the need to look for this error all
the way from the top layer of pk11wrap to the bottom function . Then we have to
do this twice, once for the tip and another for the 3.9 branch, which have
already diverged because of the 3 files vs 1 . I wish we hadn't put the
smartcard changes into the 3.9 branch in the first place, but I was trying to be
nice to everybody and did not veto them .

This is not a foolproof solution, as it requires the PKCS#11 vendor modules to
give us the specific error we expect, which may not always be the case. If a
different error is returned by some vendors for this case, we wouldn't try
again, and so the SSL server on a smartcard could still fail if a token requires
Juliens plan for trying first then logging won't work without a lot of futzing

1) The functions we are calling not only check and authenticate if necessary,
they also force authentication in the case of timeouts, or 'ask every'
semantics. These functions need to be called before we attempt the operation on
the token.

2) In some of the cases we can't tell the difference between failure and
non-existant keys. C_FindObjects* does not return CKR_USER_NOT_LOGGED_IN if you
call it when you aren't logged in and looking for private keys. Private keys are
just not visible if you aren't logged in (the PKCS #11 module may not even know
if the private keys exist until you log into the token).


We can alieviate the performance issue that prompted this bug by caching the
value of CKA_PRIVATE in the private key. On servers the private key object is a
long-lived object, so we'll get the full benefit from doing the caching.

Longer term, I still see benefit from identifying that the given application
does not do reauthentication and cutting out all our authentication checks. New
servers can access this performance improvement by calling a new function to set
this option or adding a new flag to NSS_Init (I think I prefer the latter).

If we try to "remember" that a token is authenticated in the private key
structure, and the token spontaneously enters an unauthenticated state,
without removal and insertion, then the "cached" information becomes wrong
and leads to failures in cases that today recover transparently and 
automatically.  Remember that not all servers are "enterprise" servers.
Some are AIM clients.
Nelson, Bob, we need to come to an agreement on a resolution for this problem.
At the very least we need to agree on the default behavior.
The problem is technically a side effect of the changes that went in 3.9.2 . The
module was written before those changes and worked properly then. 
If it requires a configuration option to select the proper behavior, this will
require changes to existing applications, including possible calls to new APIs,
which is a big no-no at Sun in patch releases. So, I would advocate that the
default behavior should be the pre-3.9.2 one, and the re-login should be enabled
explicitly in SSL servers, only for those few applications that truly need it.
Attachment #166072 - Attachment is obsolete: true
Attachment #166072 - Flags: superreview?(rrelyea)
I spoke to Nelson about this. The issue is that we don't want to always cache
login state as many tokens could log themselves out, even in SSL servers . So, I
marked the last patch as obsolete. I think it should still be possible to do
this still, but not by default. This could perhaps be a new SSL socket option,
as it avoids the heavy cost of trying to login again, for those tokens that the
user knows won't log themselves out . I will open an RFE on this .

Regarding the issue of C_GetAttributeValue, we should be able to cache the value
of the CKA_PRIVATE attribute of the server's private key, so that we don't call
C_GetAttributeValue again during the SSL code path .

However, it seems that we are copying the private key for each server socket in
SSL_ImportFD, and thus get different PKCS#11 key ojects, and would need to call
C_GetAttributeValue again on this new key object once per socket, so we wouldn't
have won anything. This is a much more serious problem that's related to bug
274518 . More investigation on this issue is needed .
Priority: P2 → P1
Nelson, actually I was talking about caching the CKA_PRIVATE value (as Julien
suggested), not the is logged-in. The CKA_PRIVATE value almost certainly lives
as long as the key, and is independent of token removal and reinsertion.

The copy is only an issue if we actually copy the underlying keys (which doesn't
happen for token keys). For session keys, it would be an excellent idea to avoid
the C_CopyObject, which I guarrentee is much slower than the C_GetAttribute call;).

I am now implementing Bob's suggestion to cache the value of CKA_PRIVATE . I
have verified that it works with the token in question .
Attachment #174553 - Flags: superreview?(nelson)
Attachment #174553 - Flags: review?(rrelyea)
Target Milestone: 3.9.5 → 3.10
Comment on attachment 174553 [details] [diff] [review]
cache the CKA_PRIVATE attribute on private keys

Here are some preliminary comments on the patch to just one of the 
source files:

>Index: cryptohi/keythi.h

>+typedef enum {
>+    KeyUndetermined,
>+    KeyNotPrivate,
>+    KeyPrivate
>+} PrivateFlagInfo;

Since this is a public enum in a public header, all the 
constants should have numeric values affixed.  
Also comments would be good.  , e.g. 

>+    KeyUndetermined = 0,
>+    KeyNotPrivate   = 1,  /* CKA_PRIVATE not CK_TRUE */
>+    KeyPrivate      = 2   /* CKA_PRIVATE is CK_TRUE */

> /*
> ** A generic key structure
> */ 
> struct SECKEYPrivateKeyStr {
>     PLArenaPool *arena;
>     KeyType keyType;
>     PK11SlotInfo *pkcs11Slot;	/* pkcs11 slot this key lives in */
>     CK_OBJECT_HANDLE pkcs11ID;  /* ID of pkcs11 object */
>     PRBool pkcs11IsTemp;	/* temp pkcs11 object, delete it when done */
>     void *wincx;		/* context for errors and pw prompts */
>+    PrivateFlagInfo privateflag;  

Suggested comment:  /* cached value of CKA_PRIVATE */

While we're at it ... we can anticipate the need to know the value
of the CKA_SENSITIVE attribute also.  Should we cache it too at 
this time?  Instead of an enum for each new boolean, should we 
perhaps have a PRUint32 that contains individual bits that 
show the state of sensitive, private, and a bit that means 
"this cache of attributes is valid"?

Thanks for the comments. I agree with your proposed changes about the enum
explicit values and the comment descriptions of the fields and values.

On the subject of caching other attribute values, we can do it with a bit field,
as long as we use two bits for each attribute. We really need 1 bit to specify
whether there is a cache, and another bit to specify the value . Thus, with a 32
bit integer, we could cache 16 attributes. That's probably enough for the
foreseeable future. I will produce a new patch that uses a bit field with only
the first 2 bits specified, instead of the enum with 3 defined values.

We can add the other attributes, such as CKA_SENSITIVE, at the time we find that
we need to cache them, by defining additional bits. Currently, with SSL3 and the
RC4 with SHA1 cipher suite, my patch causes all C_GetAttributeValue calls to be
eliminated, so it has met its purpose.
Comment on attachment 174553 [details] [diff] [review]
cache the CKA_PRIVATE attribute on private keys

Canceling obsolete review requests
Attachment #174553 - Flags: superreview?(nelson)
Attachment #174553 - Flags: review?(rrelyea)
Comment on attachment 174636 [details] [diff] [review]
more generic patch, to allow caching more attributes in the future. Uses bitfield

Bob, Nelson, please review.

While writing this patch, I preserved the logic in pk11skey.c which calls
PK11_PassWordCheck if CKA_PRIVATE is not set.

However, I can't help but think that the logic is reversed, and we should do
the opposite, ie. call the password check function if it's CK_TRUE.
Attachment #174636 - Flags: superreview?(nelson)
Attachment #174636 - Flags: review?(rrelyea)
Comment on attachment 174636 [details] [diff] [review]
more generic patch, to allow caching more attributes in the future. Uses bitfield

In general, I like the idea of the patch. I have the following concerns:

Must fix: 

1) Around lines 195 in keythi.h.

2^x returns a bit-wise XOR, not a power of 2. (1<<(x)) will do want you want.

Please Fix:

1) around line 666 and similiar lines in pk11obj.c.

It would be nice to colapse all the if statements into a single function or
macro. Obviously to colapse it to a single function would require extensively
changing the patch, so a macro would be fine.

Thing about:

We really only need one "isCached" bit. We should read all anticipated
attributes at once (making a single PKCS #11 call is more effecient than

Attachment #174636 - Flags: review?(rrelyea) → review-
RE comment 22.
CKA_PRIVATE is a required attribute. If it doesn't exist should we prompt or
not? I think I agree with Julien that the answer is we should prompt.

Comment on attachment 174636 [details] [diff] [review]
more generic patch, to allow caching more attributes in the future. Uses bitfield

The CachedAttribute and CacheAttribute macros are
only used once.  Why do you define them?

The way you use ## in the four macros is unusual.
This may become a maintenance issue.

The four macros have names that do not look like
macros in NSS code (whose names usually use all
capital letters).  People may be surprised when
they step through the code in a debugger or browse
the code in LXR.

>+#define IsAttributeCached(key,attribute) (0 == (key->staticflags & attribute##_Cached) ? \
>+                                        PR_FALSE : PR_TRUE)
>+#define IsAttributeSet(key,attribute) (0 == (key->staticflags & attribute##_Set) ? \
>+                                        PR_FALSE : PR_TRUE)

You can define these two macros without using the "? :"
re comment 25

Wan-Teh, I think Julien is right making these macros for two reasons: 1) making
them macros makes the code self-documenting, and 2) he's looking to the (near)
future when we may want to add additional bits.

I agree with Wan-Teh's comment about the macro names (Convention in NSS is
supposed to be ALL_CAPS for macros, though I bet we could find exceptions ;).


Re: comment 23

Thanks, I will fix the bitmask macro. I will also use only one cache bit.
I will also add a third macro to simplify the code in pk11obj and pk11skey.

Re: comment 24 

My comment about the logic wasn't about the case where CKA_PRIVATE is missing,
but case where it is set and CK_TRUE . There are really three cases:
1) CKA_PRIVATE not set

The existing function PK11_HasAttributeSet returns CK_FALSE for the first two
cases, and CK_TRUE for the third case .

The existing tests, however, are written as :

    if ( !PK11_HasAttributeSet(slot, key->pkcs11ID,CKA_PRIVATE) {
	PK11_HandlePasswordCheck(slot, key->wincx);

Ie., we try to login only if CKA_PRIVATE is missing or unset.
Doesn't the presence of CKA_PRIVATE with a value of CK_TRUE mean that we need to
login in order to use the key ? And its presence with a value of CK_FALSE that
we shouldn't login ?

Please clarify so I can create the next patch.


Re: comment 25

As Bob already answered, I defined the macros in anticipation of caching more

How else would you suggest I use ## ? I believe only pre-ANSI C compilers will
have a problem with this (they require /**/ ).

I will make the macros uppercase to distinguish them from functions.

I know I could define them without the ? operator. However, I meant for them to
return a value which can be tested against PRBool values (PR_TRUE, PR_FALSE), so
I didn't want to just mask the bit and test for non-zero. Perhaps I should use
CK_TRUE and CK_FALSE instead, though.
re: sense of PK11_HasAttributeSet

You are quite right. The existing code is wrong.


I'm not suggesting that you use ## in a different
way.  I'm suggesting that you not use ##.  The
reason is not that some compilers may not support
##.  The reason is that average C programmers may
not know what ## does, so it makes our code harder
to understand.  But this is just a my personal

PR_TRUE and PR_FALSE are macros with values 1 and
0.  A comparison with 0 ("0 == x" or "0 != x")
evalutes to 1 or 0.  So no type conversion is
needed.  (This does depend on how PR_TRUE and
PR_FALSE are defined, but their definitions will
not change.)
Attachment #174636 - Flags: superreview?(nelson)
Attachment #175370 - Flags: superreview?(rrelyea)
Attachment #175370 - Flags: review?(nelson)
Comment on attachment 175370 [details] [diff] [review]
fixes integrating Bob and Wan-Teh's review comments. Also fix bogus logic

looks good!

Attachment #175370 - Flags: superreview?(rrelyea) → superreview+
Thanks, Bob. I checked in this patch on the tip.

Checking in sslsecur.c;
/cvsroot/mozilla/security/nss/lib/ssl/sslsecur.c,v  <--  sslsecur.c
new revision: 1.26; previous revision: 1.25

Checking in pk11obj.c;
/cvsroot/mozilla/security/nss/lib/pk11wrap/pk11obj.c,v  <--  pk11obj.c
new revision: 1.4; previous revision: 1.3
Checking in pk11skey.c;
/cvsroot/mozilla/security/nss/lib/pk11wrap/pk11skey.c,v  <--  pk11skey.c
new revision: 1.90; previous revision: 1.89

Checking in nss.def;
/cvsroot/mozilla/security/nss/lib/nss/nss.def,v  <--  nss.def
new revision: 1.141; previous revision: 1.140

Checking in keyhi.h;
/cvsroot/mozilla/security/nss/lib/cryptohi/keyhi.h,v  <--  keyhi.h
new revision: 1.12; previous revision: 1.11
Checking in keythi.h;
/cvsroot/mozilla/security/nss/lib/cryptohi/keythi.h,v  <--  keythi.h
new revision: 1.8; previous revision: 1.7
Checking in seckey.c;
/cvsroot/mozilla/security/nss/lib/cryptohi/seckey.c,v  <--  seckey.c
new revision: 1.34; previous revision: 1.33
Closed: 20 years ago
Resolution: --- → FIXED
Attachment #175370 - Flags: review?(nelson)
You need to log in before you can comment on or make changes to this bug.