Closed Bug 1311950 Opened 5 years ago Closed 5 years ago

NSS does not check if token supports RSA-PSS before using it to sign

Categories

(NSS :: Libraries, defect)

3.27
defect
Not set
normal

Tracking

(Not tracked)

RESOLVED FIXED

People

(Reporter: hkario, Assigned: ttaubert)

Details

User Agent: Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:49.0) Gecko/20100101 Firefox/49.0
Build ID: 20160919122641

Steps to reproduce:

Configure server (openldap-2.4.44-1.fc24) to use an external token for storage of private key (nss-pem-1.0.2-2.fc24) that does not support RSASSA-PSS algorithm. Connect to it using client that does advertise support for RSASSA-PSS in signature_algorithms extension of Client Hello (nss-3.27.0-1.1.fc24).


Actual results:

The server aborts connection with a handshake_failure alert at point in which it should send Server Key Exchange message because the token in which the private key resides in does not support the signature algorithm that NSS selected for use (RSASSA-PSS).


Expected results:

NSS should inspect capabilities of the token before trying to create a signature and select algorithm that the token does support (in particular, abort early if the token supports only signature algorithms that the client did not advertise - see section 7.4.3. of RFC 5246). In this particular case it means it should sign Server Key Exchange with RSASSA-PKCS#1 v1.5.
Status: UNCONFIRMED → NEW
Ever confirmed: true
The failure occurred in function PK11_SignWithMechanism, on this line:
  crv = PK11_GETTAB(slot)->C_SignInit(session,&mech,key->pkcs11ID);

mechanism was CKM_RSA_PKCS_PSS

That mechanism wasn't implemented by the slot that stored the private key, and we got error CKR_MECHANISM_INVALID.
Tim, I'm about to have some time off, any chance you could look at this?  If not, it's pretty easy to fix (if perhaps a bit annoying).
Yeah, will take a look asap.
Tim suggested that we use function call PK11_TokenExists(CKM_RSA_PKCS_PSS) to check if RSA-PSS can be used or not.

IIUC, that call simply checks if ANY token exists that supports the mechanism.

I'm guessing that's insufficient, because NSS specifically asks the token that contain the private key to perform the RSA-PSS signature, and we fail if it doesn't.

NSS doesn't try to find another token to perform the signature. I'm guessing, that strategy won't reliably work in all scenarios: If a private key is stored in a hardware token, then with most devices, it isn't possible to transfer the private key to a different token for the purpose of creating an RSA-PSS signature, right?

If my assumption is correct, then I believe the following approach must be used to fix this bug:

Whenever a private key could potentially be associated with an operation, NSS should query all available tokens for their abilities. Only signature schemes should be offered, that are supported by all tokens potentially private keys.

For the server side, it might be sufficient to query all tokens that hold the private keys for all configured certificates for a given socket, and the intersection of the schemes supported by all tokens can be used.

For the client side, we don't know if the server will ask us to perform client authentication or not. IIUC, the client offers a list of schemes it supports. The server requests a scheme to be used by the client for creating a client auth signature (is this correct?). If the client cannot produce the requested signature scheme, the authentication will fail.

If my understanding is correct, then only those schemes should be offered by the client, which are support by all private key storing tokens that are present.
Right. So there are a couple of separate types of checks:

PK11_TokenExists() tells you whether or not we can even do the operation. If no tokens exists, we don't offer the option/cipher suite.

-> This will solve any RHEL based failures because non of our tokens will be able to do PSS.

Kai is correct that this may not be sufficient. This is fine for *verifying* signatures as we can always ask to token that advertises this capability to do the verification for us (if you are calling the high level PK11 calls -- that is ones without specific slots, pk11wrap will automatically handle this for you. If not, you can use the PK11_GetBestSlot() and friends to get a slot that can handle the mechanism).

For operations where the key is already in a token, then we can only do the mechanism associated with the token where the key lives. For most symetric operations, pk11wrap will do it's best to move a key to a token that can do the operation, and it's usually successful (even with FIPS tokens... it uses key exchanges if necessary). Private keys are another matter. The private key determines everything. For servers, we have a lot of this handled when we start up. We only advertise ciphers for which the keys we hold. If we treat an RSA PSS private key separately, then the existing scheme will work. We would just have some keys that could work for RSA and RSA PSS cipher suites.

--> Kai bug report is this kind of failure. The system Kai is using has a modern softoken which supports PSS, but it also has an external module in which the server's keys are installed. Softoken can do PSS, but the module the server's keys lives in can't. This would also cause failures in hardware tokens as well.

Solving both of the above issues will get is 90% there.




> For the client side, we don't know if the server will ask us to perform client authentication or not. IIUC,
> the client offers a list of schemes it supports. The server requests a scheme to be used by the client for
> creating a client auth signature (is this correct?). If the client cannot produce the requested signature
> scheme, the authentication will fail.

I think it's the other way around. The server advertises the schemes it accepts and the client provides one.That is already the case for the difference between RSA and ECC. The client just needs to select the most appropriate authentication scheme for the given private key it selects. It needs to check the token before it decides to use PSS in the signature scheme.

This is necessary today in Firefox because many smart card tokens will break because they can't use PSS.
Bob, I assume that the real fix is to:

a) work out which slot the key is in (PK11_GetSlotFromPrivateKey?)
b) ask if that slot supports PSS (PK11_DoesMechanism?)
c) skip PSS if the slot isn't OK with it

Is that the right solution here?
Flags: needinfo?(rrelyea)
(In reply to Robert Relyea from comment #5)
> > For the client side, we don't know if the server will ask us to perform client authentication or not. IIUC,
> > the client offers a list of schemes it supports. The server requests a scheme to be used by the client for
> > creating a client auth signature (is this correct?). If the client cannot produce the requested signature
> > scheme, the authentication will fail.
> 
> I think it's the other way around. The server advertises the schemes it
> accepts and the client provides one.That is already the case for the
> difference between RSA and ECC. The client just needs to select the most
> appropriate authentication scheme for the given private key it selects. It
> needs to check the token before it decides to use PSS in the signature
> scheme.

yes, the signature algorithms advertised in Client Hello are explicitly distinct from the algorithms that the server can advertise in Certificate Request.

The client still should advertise in Client Hello only the mechanisms it can actually verify so calling PK11_TokenExists(CKM_RSA_PKCS_PSS) may actually be valid for sending Client Hello
There are 3 separate fixes:

1) add the PK11_TokenDoesMechanism() call for PSS on the cipher suite itself to disable PSS cipher suites (assumes there are PSS specific cipher suites).
2) create rsa_pss auth and kea key types as appropriate. When we add an rsa key we check to see if the token supports PSS or pkcs and add the key to the correct kea and auth slot. Check that that slot is filled on servers before enabling a given cipher suite (we should already have that code in place for rsa/ecc types.
3) for client auth we only advertise PSS if we have PK11_TokenDoesMechanism(), and we only supply it if our chosen key is in a token that does PSS and the server accepts PSS auth.

Basically treat rsa_pss as a different auth/kea up front rather than as just rsa and try to recover in the back end.

Our immediate need is for #1 because  that will get us going on RHEL systems (since we have no PSS tokens), but we already have a problem with upstream Firefox and Client auth (which is the hardest to solve), so we do have to move down the list here.
Flags: needinfo?(rrelyea)
https://nss-review.dev.mozaws.net/D11

https://hg.mozilla.org/projects/nss/rev/0cccc59d04dd
Assignee: nobody → ttaubert
Status: NEW → ASSIGNED
Target Milestone: --- → 3.28
(In reply to Robert Relyea from comment #8)
> 1) add the PK11_TokenDoesMechanism() call for PSS on the cipher suite itself
> to disable PSS cipher suites (assumes there are PSS specific cipher suites).

Currently, there are no PSS-only cipher suites. We have RSA-authenticated cipher suites that in TLS 1.2 can be either PKCS1.5 or PSS, and must be PSS in TLS 1.3 (although that restriction isn't implemented in NSS yet).

So we need to add the PK11_DoesMechanism() check to the cipher suite negotiation code once we start enforcing PSS in TLS 1.3. Ideally we would, in ssl3_config_match_init(), check whether the RSA certificate's private key slot supports either PSS or PKCS1.5, or we could disable all RSA cipher suites right away if it doesn't support any of the two (rather unlikely).

The check I landed above applies only when negotiating a signature scheme in TLS 1.2+. When we encounter a PSS scheme we check whether the cert's private key slot supports that mechanism. We don't currently check whether the slot supports only PSS, that shouldn't be an issue for a while I assume.

> 2) create rsa_pss auth and kea key types as appropriate. When we add an rsa
> key we check to see if the token supports PSS or pkcs and add the key to the
> correct kea and auth slot. Check that that slot is filled on servers before
> enabling a given cipher suite (we should already have that code in place for
> rsa/ecc types.

We have an ssl_auth_rsa_pss type already, but the certificate configuration needs more cleanup. Then we should definitely add mechanism checks to avoid problems with non-soft-tokens and simplify other places.

> 3) for client auth we only advertise PSS if we have
> PK11_TokenDoesMechanism(), and we only supply it if our chosen key is in a
> token that does PSS and the server accepts PSS auth.

My patch addressed client auth as well, so a client won't try to sign its certificate with a PSS signature if the token doesn't support that.

However, as Hubert mentioned before, I _think_ we're fine always sending PSS signature schemes in a ClientHello or a CertificateRequest. All we need is _a_ token that supports verifying these signatures, and that will always be the softoken, right?
(In reply to Tim Taubert [:ttaubert] from comment #10)
> However, as Hubert mentioned before, I _think_ we're fine always sending PSS
> signature schemes in a ClientHello or a CertificateRequest. All we need is
> _a_ token that supports verifying these signatures, and that will always be
> the softoken, right?

In RHEL we're using the softokn that was FIPS certified, that means it is older than the NSS version. So yes, there can be situation where NSS is used with a softokn that doesn't know how to verify RSA-PSS signatures.
That's a good point. I'll come up with another patch for this.
Tim, Taubert, thanks a lot for your work. 

I'd like to encourage you to add at least a few words of text, in addition to just links to external things.

I believe the status as of the previous comment was 
  "bug should be fixed, please test and give feedback".

I had failed to conclude that status from the links, and I had falsely assumed we're still waiting for someone to work on the fix.
Tim, Martin, after some testing, it seems the issue is fixed. Thanks a lot.

I want to describe how I tested the fix, just for reference purposes.

It would be more complicated to provide complete automated tests for this issue, because we currently don't have tstclnt/selfserv commands that would allow us to restrict the set of allowed signature mechanisms, and inside of NSS we don't have a token available with limited amount of signature mechanisms.

I set up a local environment, which uses the nss-pem module available on Fedora.

I've configured NSS databases, which contain only the CA, but no other certs. I configured selfserv to load the server from nss-pem, and tstclnt to load a client auth cert from nss-pem, and I used selfserv -r -r to require client auth.

Those tests fail with NSS 3.27, and they work with NSS trunk.

I think this bug can be marked fixed.
Status: ASSIGNED → RESOLVED
Closed: 5 years ago
Resolution: --- → FIXED
Thanks for testing Kai.
You need to log in before you can comment on or make changes to this bug.