Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue with CKU_CONTEXT_SPECIFIC on Safenet HSM module #160

Closed
mirozitnansky opened this issue May 16, 2017 · 52 comments
Closed

Issue with CKU_CONTEXT_SPECIFIC on Safenet HSM module #160

mirozitnansky opened this issue May 16, 2017 · 52 comments

Comments

@mirozitnansky
Copy link

First of all, you are doing great job guys, many thanks for time and energy you are putting into this project.

I am pretty noob with pkcs11 stuff, so please have patience with me.
I spent quite some time trying to implement OpenSSL certification authority based on Sefenet HSM module which should store CA private keys. CA key is than used for singing client/server certificates.
Using libp11 as OpenSSL engine, configured with pkcs11 lib supplied with HSM, I was able to make OpenSSL work correctly... on some conditions.
Our HSM can work in few modes. Safenet's default (we are currently using this mode), FIPS-140 and others.
We are planning to switch our device from current, default mode, into FIPS-140 mode. This means, among other things, device will be switched to No Public Crypto mode – user have to be always logged into token, if he is going to use key for crypto operation.
In current mode, there no need to be logged in token to do the sign operation in OpenSSL.
On the other hand, object in token can be public or private (I am not reffering to RSA pulic/private key). Private objects are only accessible after loggin in.

First problem I encountered lied on having private object while disabled No Public Crypto flag. Then even if I try to set pin for token, engine won’t log in and simply says there is no such object (because it was set private).

I have pkcs11 communication logged so pc_private_list.txt is list using safenet utility.

[root@mzcentos ~]# ctkmu l -s 0
ProtectToolkit C Key Management Utility 5.2.0
Copyright (c) Safenet, Inc. 2009-2016

logger: s_ctData->ft = 0x0x1dd5360
logger: s_ctData->ft->C_Initialize = 0x0x7f97d9acae20
Do you wish to view private (user) objects [y/N]: y

Please enter User's PIN for the token in slot 0: 

Manufacturer      = SafeNet Inc.
Label             = OPENSSL                         
Flags             = 0x649 (RNG USER-PIN-INIT CLOCK TOKEN-INIT DUAL-CRYPTO)

Public and Private Objects:
CA                               - PUBLIC_KEY      RSA         
CA                               - PRIVATE_KEY     RSA         

and pc_private_sign.txt is OpenSSL sign attempt which end up failed:

[root@mzcentos ca_ptk]# openssl req -config openssl.cnf -key "pkcs11:model=0000;manufacturer=SafeNet%20Inc.;serial=0000%3a84324;token=OPENSSL;id=%42%19%72%91%10%73%e4%b1%e0%7f%67%fe%2f%31%a8%6c%e6%5c%ca%66;object=CA;object-type=private?pin-value=1111" -keyform engine -engine pkcs11 -new -x509 -days 7300 -sha256 -extensions v3_ca -out certs/ca.cert.pem
logger: s_ctData->ft = 0x0x21241a0
logger: s_ctData->ft->C_Initialize = 0x0x7f9fb5b44e20
engine "pkcs11" set.
No private keys found.
No private keys found.
PKCS11_get_private_key returned NULL
cannot load Private Key from engine
140323973031840:error:26096080:engine routines:ENGINE_load_private_key:failed loading private key:eng_pkey.c:126:
unable to load Private Key

In this case, I assume, if OpenSSL logged into token, sing operation would worked fine.
So I set No public crypto flag to true, OpenSSL logs in correctly, finds private key and do the sing operation. Npc_private_sign.txt

Second thing, it seems to me, is some lack of implementation on side of Safenet. It looks like, they have not implemented CKU_CONTEXT_SPECIFIC user type in pkcs11 at all. When I have set No public crypto true, but my object is Public, OpenSSL finds it, then request pin (ignoring pin in URL), but then sing fails on

[root@mzcentos ca_ptk]# openssl req -config openssl.cnf -key "pkcs11:model=0000;manufacturer=SafeNet%20Inc.;serial=0000%3a84324;token=OPENSSL;id=%42%19%72%91%10%73%e4%b1%e0%7f%67%fe%2f%31%a8%6c%e6%5c%ca%66;object=CA;object-type=private?pin-value=1111" -keyform engine -engine pkcs11 -new -x509 -days 7300 -sha256 -extensions v3_ca -out certs/ca.cert.pem
logger: s_ctData->ft = 0x0xd571a0
logger: s_ctData->ft->C_Initialize = 0x0x7f80d2c5be20
engine "pkcs11" set.
Missing CKA_ALWAYS_AUTHENTICATE attribute
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [SK]:
State or Province Name [Bratislavsky kraj]:
Locality Name [Bratislava]:
Organization Name [company]:
Organizational Unit Name [IKT]:
Common Name []:
Email Address [ikt@company.eu]:
Enter PKCS#11 key PIN for CA:
140191316727712:error:80009103:Vendor defined:PKCS11_rsa_encrypt:User type invalid:p11_rsa.c:120:
140191316727712:error:0D0DC006:asn1 encoding routines:ASN1_item_sign_ctx:EVP lib:a_sign.c:314:

This seems to be a problem in pkcs11 log Npc_publicobj_sign.txt:

pid(14918) tid(139744935835712) time(16/05/2017,23:02:12.252) 	> C_Login hSession=0x00000002 userType=2{?} pPin=0x0x7ffd53d62830 pinLen=4
    pin: (4Bytes)
    31 31 31 31 

pid(14918) tid(139744935835712) time(16/05/2017,23:02:12.252) 	<< C_Login rv=0x00000103{user type invalid}

As I mentioned before, we plan to switch mode of our device to FIPS (with No public crypto flag), and we have public objects in token, so after switch we won’t be able to use them, because sing will fail as in previous case. If I am able to force login into token as standard user, crypto operations shoud work correctly even after mode switch.
I noticed Missing CKA_ALWAYS_AUTHENTICATE attribute message in OpenSSL operations, but I am not sure if this has something to do with my situation.

All tests I am doing, are done on software HSM emulation, but hardware HSM works the same.
Do you think, if is there any way to overcome this user type invalid issue?

Thanks in advance.
If there is any test/log I can provide, I'll be happy to.

@dengert
Copy link
Member

dengert commented May 17, 2017

As you have noted in https://github.com/OpenSC/libp11/files/1005875/Npc_publicobj_sign.txt
line 357-360 the PKCS#11 module does not understand 202 (CKA_ALWAYS_AUTHENTICATE) that that looks like the source of the "Missing CKA_ALWAYS_AUTHENTICATE attribute" The module should support the attribute and return FALSE.

In lines 364 to 370 a C_SignInit returns USER_NOT_LOGED_IN. Libp11 is assuming that in this case the the module is expecting a C_Login with CKU_CONTEXT_SPECIFIC which is what would be expected to work if the CKA_ALWAYS_AUTHENTICATE=True. So this again is a problem with the module, it does not even understand CKU_CONTEXT_SPECIFIC.

But to be fair, the PKCS#11 standards say C_SignInit can return CKR_USER_NOT_LOGGED_IN but does not limit it to private keys with CKA_ALWAYS_AUTHENTICATE=True. So the caller can not be sure if it should do a CKU_CONTEXT_SPECIFIC or normal C_Login with CKU_USER.

Not recognizing standard attributes and CKU__CONTEXT_SPECIFIC is a bug in the Sefenet HSM module.

Libp11 could make the assumption CKA_ALWAYS_AUTHENTICATE missing means False, and only do CKU_CONTEXT_SPECIFIC C_Login if CKA_ALWAYS_AUTHENTICATE=True.

The next question is what other attributes does Sefenet HSM module not support correctly?

@mouse07410
Copy link
Contributor

I support treating CKA_ALWAYS_AUTHENTICATE missing as False.

mtrojnar added a commit that referenced this issue May 17, 2017
@mirozitnansky
Copy link
Author

I went over documentation again and find this

Compliance
This application expects PKCS#11 V 2.10 compliant implementation and will use SafeNet extensions (see the next section) if they are available.

Isn't it that CKA_ALWAYS_AUTHENTICATE was added in later versions of pkcs11?
I checked 2.1 specification and there is no mention of attributes.

What looks very artsy to me, that Safenet didn't update their pkcs11 imeplentation even on devices released in 2015.
Should I try compile and run new master?

@dengert
Copy link
Member

dengert commented May 17, 2017

CKA_ALWAYS_AUTHENTICATE is in PKCS#11-2.20 from 2004. Section "10.9 Private key objects" in Table 15. The default is CK_FALSE. CK__CONTEXT_SPECIFIC is also in 2.20.

In https://github.com/OpenSC/libp11/files/1005875/Npc_publicobj_sign.txt
lines 29 to 31 shows the C_GetInfo:

pid(14918) tid(139744935835712) time(16/05/2017,23:02:07.225) < C_GetInfo rv=0x00000000{success} pInfo=0x0x7ffd53d62920
pInfo->cryptokiVersion: (2Bytes)
02 14

The SafeNet module appears to return version 2.20
Thus the Safenet module is not meeting the PKCS#11 standards it claims is supports.

@mirozitnansky
Copy link
Author

Thanks for clarification, I will raise issue to support, but to be honest, I don't believe they will change anything...

@mirozitnansky
Copy link
Author

I tried to run test on lastest version of ProtectServer toolkit (pkcs11 lib for hsm) which should be v2.20 compliant, it failed same as older version. I reported this to gemalto support.

I am not sure what to do next. I tried to compile lastest master from github, where I saw commits for this issue, but it won't compile. I ran bootstrap then configure, but it keeps running check and fails on missing autoconf build-master.txt.
Should I wait for official release?

Thanks.

@mtrojnar
Copy link
Member

make: Warning: File `Makefile.am' has modification time 7403 s in the future

Try configuring the correct timezone on your machine.

Alternatively, use "git clone" instead of downloading the auto-generated tarball.

@mirozitnansky
Copy link
Author

thanks, I had date few days back

I ran test with NO PUBLIC CRYPTO and PUBLIC OBJECT again, now I end up user not logged

[root@mzcentos ca_ptk]# openssl req -config openssl.cnf -key "pkcs11:model=0000;manufacturer=SafeNet%20Inc.;serial=0000%3a84324;token=OPENSSL;id=%42%19%72%91%10%73%e4%b1%e0%7f%67%fe%2f%31%a8%6c%e6%5c%ca%66;object=CA;object-type=private?pin-value=1111" -keyform engine -engine pkcs11 -new -x509 -days 7300 -sha256 -extensions v3_ca -out certs/ca.cert.pem
logger: s_ctData->ft = 0x0x1f661a0
logger: s_ctData->ft->C_Initialize = 0x0x7f477b051fc0
engine "pkcs11" set.
Missing CKA_ALWAYS_AUTHENTICATE attribute
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [SK]:
State or Province Name [Bratislavsky kraj]:
Locality Name [Bratislava]:
Organization Name [company]:
Organizational Unit Name [IKT]:
Common Name []:
Email Address [ikt@company.eu]:
139945031366560:error:80009101:Vendor defined:PKCS11_rsa_encrypt:User not logged in:p11_rsa.c:120:
139945031366560:error:0D0DC006:asn1 encoding routines:ASN1_item_sign_ctx:EVP lib:a_sign.c:314:
[root@mzcentos ca_ptk]# 

NPC_PO.txt

in no public crypto mode, hsm requires to log in on every crypto operation

@dengert
Copy link
Member

dengert commented May 20, 2017

C_Login is never called.

I am also surprised that the private key was found without logging in. See lines 214-222.
PKCS#11 has the CKA_PRIVATE which says a user may not access the object until the user has authenticated to the token. Access to a private key object includes doing a crypto operation.

(Note CKO_PRIVATE_KEY is a private key object. All objects have a CKA_PRIVATE attribute.)

PKCS#1 2.20 says: "The object search operation will only find objects that the session can view. For
example, an object search in an “R/W Public Session” will not find any private objects
(even if one of the attributes in the search template specifies that the search is for private
objects)."

The SafeNet module appears to violate the above.

(It is possible to have a private key with CKA_PRIVATE=False. Which would then allow for it to be found and used (for example with C_SignInit) without a login. But it looks like that is not what was intended,
as it was found but could not be used. )

So the libp11 assumed it was found without a login and could be used without a login.

Looks like c71401f does not address the problems of the SafeNet implementation.

With previous code, pkcs11_authenticate would have been called, but pkcs11_authenticate has:
if (!kpriv->always_authenticate)
return 0;
but it will only do CKU_CONTEXT_SPECIFIC type of C_Login

But there are times when the token could loose the login state because of interference from other processes or even a powered down USB reader. pkcs11_authenticate could be passed another parameter indication it should try a CKU_USER login and/or a CKU_CONTEXT_SPECIFIC login depending on why it was being called.

@dengert
Copy link
Member

dengert commented May 20, 2017

One more thing. What version of openssl are you using?

Since you are trying to create the CA certificate that is self signed, you may want to look at the CA.sh or CA.pl scripts to see how OpenSSL creates a CA self signed certificate.
They actually do a two step process using openssl req and openssl ca -selfsign Thus there is only one sign operation in each step.

@mirozitnansky
Copy link
Author

I am using 1.0.1e release 60.el7_3.1, same for openssl-devel.

Private key actually is not private object, list from safenet's key generation utility

[root@mzcentos ~]# ctkmu l -s 0 -v
ProtectToolkit C Key Management Utility 5.3.0
Copyright (c) Safenet, Inc. 2009-2016

logger: s_ctData->ft = 0x0x1fa7390
logger: s_ctData->ft->C_Initialize = 0x0x7f503a16efc0
Do you wish to view private (user) objects [y/N]: n

Manufacturer      = SafeNet Inc.
Label             = OPENSSL                         
Flags             = 0x649 (RNG USER-PIN-INIT CLOCK TOKEN-INIT DUAL-CRYPTO)

Public Objects:
CA                               - PUBLIC_KEY      RSA         

Attribute list for object CA, slot 0
Private      = 0
Sensitive    = 0
Modifiable   = 0
Import       = 0
Export       = 0
Wrap         = 0
Unwrap       = 0
Exportable   = 0
Extractable  = 0
Derive       = 0
Encrypt      = 0
Decrypt      = 0
Sign         = 0
Verify       = 0
Key Size     = 2048 bits

CA                               - PRIVATE_KEY     RSA         

Attribute list for object CA, slot 0
Private      = 0
Sensitive    = 1
Modifiable   = 0
Import       = 0
Export       = 0
Wrap         = 0
Unwrap       = 0
Exportable   = 0
Extractable  = 1
Derive       = 0
Encrypt      = 0
Decrypt      = 0
Sign         = 1
Verify       = 0
Sign Local   = 0
Key Size     = 2048 bits

list.txt
I set it not to be private in generation process.

That is because while we are running our HSM in production environment (and I am simulating this setting) with NO_PUBLIC_CRYPTO disabled, openssl engine won't Login, and if key is private object, it cannot see it. I have to generate it as public, so it's visible for engine.
In this combination engine works just fine. NPC_PO_OK.txt

Now I try to simulate future event, switching HSM to FIPS mode (NO_PUBLIC_CRYPTO enabled) then the problem with missing login in sign operation arises, because key is visible, but HSM requires Login to Sign with it.

I was also trying different approach, generating CA key as private object. But then, while NO_PUBLIC_CRYPTO disabled (as current production enviroment state), engine is not doing Login, therefore won't find private object. Probably because of missing CKA_ALWAYS_AUTHENTICATE attribute?
In this scenarion, after switching NO_PUBLIC_CRYPTO enabled, all start to work fine, because engine start to login.
This is log from such scenario, from the point of generating private object.
line 322 - key generation
line 361 - failed Sign attempt

[root@mzcentos ca_ptk]# openssl req -config openssl.cnf -key "pkcs11:model=0000;manufacturer=SafeNet%20Inc.;serial=0000%3a84324;token=OPENSSL;id=%42%19%72%91%10%73%e4%b1%e0%7f%67%fe%2f%31%a8%6c%e6%5c%ca%66;object=CA;object-type=private?pin-value=1111" -keyform engine -engine pkcs11 -new -x509 -days 7300 -sha256 -extensions v3_ca -out certs/ca.cert.pem
logger: s_ctData->ft = 0x0x19ee1a0
logger: s_ctData->ft->C_Initialize = 0x0x7fbf3da81fc0
engine "pkcs11" set.
No private keys found.
PKCS11_get_private_key returned NULL
cannot load Private Key from engine
140459397937056:error:26096080:engine routines:ENGINE_load_private_key:failed loading private key:eng_pkey.c:126:
unable to load Private Key

line 690 - enable NO_PUBLIC_CRYPTO
line 1316 - successful Sing attempt

logger: s_ctData->ft = 0x0xfa51a0
logger: s_ctData->ft->C_Initialize = 0x0x7f4865242fc0
engine "pkcs11" set.
Missing CKA_ALWAYS_AUTHENTICATE attribute
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [SK]:
State or Province Name [Bratislavsky kraj]:
Locality Name [Bratislava]:
Organization Name [company]:
Organizational Unit Name [IKT]:
Common Name []:
Email Address [ikt@company.eu]:

PRIVATEOBJ_FULL.txt

At first step, I thought that this issue might be resolved, treating missing CKA_ALWAYS_AUTHENTICATE as CKA_ALWAYS_AUTHENTICATE = true. But this can have more behind it what I cannot understand, because of my little knowledge.

I understand, problem lies in different mode when generating key, and switch mode with already generated keys. But that's something I have to live with. I cannot change mode now, when generating key, because production systems (which are using HSM for various crypto operations with symmetric keys) are relaying on not having to Login for public object key using. They will have to be modified in future, before switch to FIPS mode.
But I need to start using CA now, not in future.

Self signing is only first step. As I mentioned, I successfully used openssl engine for singing CA, intermediate CAs and client certificates, but I can't proceed forward knowing whole mechanism stops working in future when HSM will be switched. So I am trying to find a way.
To be honest, I am trying to resolve this issue for quite a long time now. Writing here, bothering you is my last resort before giving up on using openssl CA with our HSM (what cannot easily accept after spending so much time with it).

@mirozitnansky
Copy link
Author

Documentantion defines NO PUBLIC CRYPTO as follows:

No Public Crypto
When this flag is TRUE, each token will have the CKF_LOGIN_REQUIRED flag set and all the cryptographic C_xxxInit functions and key operation functions: C_GenerateKey, C_GenerateKeyPair, C_WrapKey, C_UnwrapKey, C_DeriveKey, C_DigestKey will fail unless the session state is in a User mode (that is, either the USER or SO must be logged in).

If the session state is not in a User mode, any attempt to write to a token will fail (that is, using the functions C_CreateObject, C_DestroyObject and C_SetAttributeValue).

@dengert
Copy link
Member

dengert commented May 20, 2017

I have not used libp11 recently either. Trying to build it from master c71401f

@mtrojnar
Running ./bootstrap, I get libtoolize: Remember to add 'LT_INIT' to configure.ac.
Then since I am building is a separate directory from the source,
I get these messages from configure:

../src/configure: line 13750: src/libp11.map: No such file or directory
../src/configure: line 13751: src/libp11.map: No such file or directory
../src/configure: line 13752: src/libp11.map: No such file or directory
../src/configure: line 13753: src/libp11.exports: No such file or directory
../src/configure: line 13754: src/libp11.map: No such file or directory
../src/configure: line 13755: src/libp11.map: No such file or directory
../src/configure: line 13756: src/libp11.map: No such file or directory
../src/configure: line 13757: src/libp11.map: No such file or directory
chmod: cannot access 'src/libp11.map': No such file or directory

This looks like the libp11.map is being created in the $(srcdir) directory, rather then the $(builddir)
But it builds anyway.
../src/configure --prefix=/opt/ossl-1.0.2 --with-pkcs11-module=opensc-pkcs11.so --disable-ld-version-script

@mirozitnansky
I have never used SafeNet, but it sounds like the it is taking advantage of what I said earlier:
"But to be fair, the PKCS#11 standards say C_SignInit can return CKR_USER_NOT_LOGGED_IN but does not limit it to private keys with CKA_ALWAYS_AUTHENTICATE=True. So the caller can not be sure if it should do a CKU_CONTEXT_SPECIFIC or normal C_Login with CKU_USER."

@mtrojnar I also said:
"But there are times when the token could loose the login state because of interference from other processes or even a powered down USB reader. pkcs11_authenticate could be passed another parameter indication it should try a CKU_USER login and/or a CKU_CONTEXT_SPECIFIC login depending on why it was being called."
This should solve the problem when SafeNet with the NO_PUBLIC_CRYPTO option. i.e. a normal C_Login could be done from pkcs11_authenticate.

@mirozitnansky
Copy link
Author

If I may ask, how CKF_LOGIN_REQUIRED should be correctly treated?
Specification says True if there are some cryptographic functions that a user MUST be logged in to perform
At which point of communication should it be taken into consideration?

@dengert
Copy link
Member

dengert commented May 20, 2017

What most applications do is find all the certificates and private keys before doing any login. Then if a private key is needed because they are usually CKA_PRIVATE=True, the application does a C_Login.

libp11 looks at the flag and sets loginRequired and uses it in a number of places.
src/eng_back.c:ctx_login for example. I don't know why it is not using the pin-value=1111 and doing a C_Login at this point.

It could be that ctx_load_privkey is called and the SafeNet has it marked as CKA_PRIVATE=False
line 852 worksm and no login is ever done.
852 pk = ctx_load_key(ctx, s_key_id, ui_method, callback_data, 1, 0);
853 if (pk == NULL) /* Try again with login */
854 pk = ctx_load_key(ctx, s_key_id, ui_method, callback_data, 1, 1);

But if @mtrojnar fixes pkcs11_autuenticate to do a C_Login(CKU_USER) that might work too.

You could try gdb or other debugger to see what is going on.

mtrojnar added a commit that referenced this issue May 21, 2017
@mtrojnar
Copy link
Member

../src/configure: line 13750: src/libp11.map: No such file or directory

Looks like 20c7b58. @nmav?

@mtrojnar
Copy link
Member

mtrojnar commented May 21, 2017

But there are times when the token could loose the login state because of interference from other processes or even a powered down USB reader.

There is nothing we can do at this point. Those objects are gone. Logging in again won't restore them. It will create new objects.

pkcs11_authenticate could be passed another parameter indication it should try a CKU_USER login and/or a CKU_CONTEXT_SPECIFIC login depending on why it was being called.

No, re-login would destroy the previously acquired object handles. (this sentence has been edited for a more clear wording)

@dengert
Copy link
Member

dengert commented May 21, 2017

You said: "would destroy all the previously acquired handles" I don't understand. Which handles?

PKCS#11-2.20 says:
"3. Call C_Login to log the user into the token. Since all sessions an application has
with a token have a shared login state, C_Login only needs to be called for one of the
sessions."

"When the user type is either CKU_SO or CKU_USER, if the call succeeds, each of the
application's sessions will enter either the "R/W SO Functions" state, the "R/W User
Functions" state, or the "R/O User Functions" state."

Are you saying this is something to do with pkcs11_authenticate() destroying the handles?

@mtrojnar
Copy link
Member

mtrojnar commented May 22, 2017

@dengert What I'm saying is that pkcs_authenticate() was not designed to mess with CKU_SO or CKU_USER logins, as they modify the list of available object handles.

@dengert
Copy link
Member

dengert commented May 22, 2017

In @mirozitnansky 's case , the private key is already available and already has a handle so there is no messing with available object handles.

It looks like SafeNet was implemented to PKCS#11-2.10 which does not have CKA_ALWAYS_AUTHENTICATE or CKU_CONTEXT_SPECIFIC and the SafeNet's "No Public Crypto" option is their attempt to force a C_Login(CKU_USER) login.

@mirozitnansky
Copy link
Author

@mtrojnar I tried latest commit and it works as expected. When ?pin-value=1111 is present in URL, Sing work well even with public object in FIPS mode.
But this has one downside.

I successfully created CA pair. Now I want to have set all needed in openssl.cnf, including URL for key, so user who is signing client certificate request, doesn't have to understand details of pkcs11 URL format.
So I have set, among other things

[ ca ]
# `man ca`
default_ca = CA_default

[ CA_default ]
# Directory and file locations.
dir               = .
certs             = $dir/certs
crl_dir           = $dir/crl
new_certs_dir     = $dir/newcerts
database          = $dir/index.txt
serial            = $dir/serial
RANDFILE          = $dir/private/.rand

# The root key and root certificate.
private_key	= "pkcs11:serial=0000%3a84324;token=OPENSSL;object=CA;object-type=private?pin-value=1111"

certificate       = $dir/certs/ca.cert.pem

then users call only

openssl ca -config openssl.cnf -extensions usr_cert -notext -md sha256 -in csr/${filename}.csr.pem -out certs/${filename}.cert.pem -engine pkcs11 -keyform engine

without URL part.

Problem with this setup is, everyone who can read openssl.cnf knows the pin for the token with key.
I can possibly make openssl.cnf readable only for user who knows pin, but then I cannot enforce dual-control policy, when 2 people know 2 parts of pin and enter it one after another when Signing.
But these are thing I can imagine to live without.

Maybe last thing, couldn't it be somehow implemented, at start of pksc11 session when GetInfo is called, to remember (or Login right away) that CKF_LOGIN_REQUIRED=1, and request pin when crypto operation is called same as when object is private and NO PUBLIC CRYPTO IS is enabled?

I can imagine it's easier said than done. :)
Thanks.

@dengert
Copy link
Member

dengert commented May 22, 2017

Are you saying any user can get the CA to sign a certificate? You let users on your CA machine?

with regard to: "Maybe last thing, couldn't it be somehow implemented, at start of pksc11 session when GetInfo is called, to remember (or Login right away) that CKF_LOGIN_REQUIRED=1, and request pin when crypto operation is called same as when object is private and NO PUBLIC CRYPTO IS is enabled?"

As I said above if you made your private keys CKA_PRIVATE=True, it would do a C_Login early:

"It could be that ctx_load_privkey is called and the SafeNet has it marked as CKA_PRIVATE=False
line 852 works and no login is ever done.
852 pk = ctx_load_key(ctx, s_key_id, ui_method, callback_data, 1, 0);
853 if (pk == NULL) /* Try again with login */
854 pk = ctx_load_key(ctx, s_key_id, ui_method, callback_data, 1, 1);

@mirozitnansky
Copy link
Author

mirozitnansky commented May 23, 2017

Our HSM is external, network accessed (under IPtables rules), device locked in in own dedicated rack.
There are servers (ha cluster), which are configured to access and use HSM over network, for different tasks than CA signing. These have restricted access, so I planned to use them also as CA machine. But they are operated by different people than those responsible for CA siging.
If there is secret pin (which is not when pin is written in config file) based access on token which store CA key (one of many others in HSM), it would be secure to put CA on the same machine, because only selected people would be pin custodians.

I am not saying this is ideal scenario, I am just trying to use existing infrastructure and this would be secure "enough" for our use cases.

Those lines of code you put in here, that is something that could be done in future? Because now, when device has NO_PUBLIC_CRYPTO disabled and key is private, it's not found by engine.

@mtrojnar
Copy link
Member

PKCS#11 v2.20 says:

CKF_LOGIN_REQUIRED 0x00000004 True if there are some cryptographic functions that a user must be logged in to perform

CKF_LOGIN_REQUIRED never meant that all cryptographic functions of the device require the user to be logged in.

@mirozitnansky
Copy link
Author

I understand, didn't mean to play smartass, just trying to find way. It seems to as game with words, when who is the one who decided which operations are some...
Can you imagine implement engine parameter FORCE_LOGIN=1 which would work similarly as when pin-value is present in URL, and would force user to Login at beginning of the session?

Anyway, you've both done a lot to help me, thanks for your time. If there won't be any other solution from me as having pin-value in URL to force login, I am still thankful.

@mtrojnar
Copy link
Member

It seems to as game with words, when who is the one who decided which operations are some...

As @dengert wrote, this is exactly what CKA_PRIVATE is used for...

@mtrojnar
Copy link
Member

Can you imagine implement engine parameter FORCE_LOGIN=1 which would work similarly as when pin-value is present in URL, and would force user to Login at beginning of the session?

Yes, a new engine parameter is definitely feasible. Give me a few days.

@mirozitnansky
Copy link
Author

That's great, it should resolve all my issues. I will be checking new commits.

@nmav
Copy link
Contributor

nmav commented May 27, 2017

To put my two cents in, the safenet hsms are too popular today to handle them as special cases. I kind of like the suggestion of Doug, though in the end introduces quite some complexity.

gnutlsmirror pushed a commit to gnutls/gnutls that referenced this issue May 27, 2017
…y operation

These HSMs do not support CKA_ALWAYS_AUTHENTICATE, but rather return
CKR_USER_NOT_LOGGED_IN on every operation. Try to discover that state
by utilizing the fact that CKA_ALWAYS_AUTHENTICATE is not supported
by the HSM. See discussion in OpenSC/libp11#160

Signed-off-by: Nikos Mavrogiannopoulos <nmav@gnutls.org>
@mirozitnansky
Copy link
Author

mirozitnansky commented May 27, 2017

BTW, received response from gemalto support, they explained that module supports algorithms of 2.20 but nothing else. v2.10 compliance is required to work with their module.
In this specific case, NO_PUBLIC_CRYPTO enabled / public object, they suggest to detect CKF_LOGIN_REQUIRED from token and call C_LOGIN regardless of following acticities as CK_USER based on this flag.
In the other words, nothing we have discussed here ... and no help with issue itself at all.

@nmav we chose Protectserver model because of it's ability to run custom made C application (they call FM module) within HSM module security domain, so it can access clear keys. That is fairly unique as far as I know. To be fair here, safenet's second product range called LUNA SA support OpenSSL integration out of the box. But we needed FM module at first place, CA keys and OpenSSL integration comes second. That's why was exited when found libp11 project.

@mtrojnar
Copy link
Member

FORCE_LOGIN=1

According to the manual (https://www.openssl.org/docs/man1.0.2/apps/config.html) the proper invocation is:
FORCE_LOGIN = EMPTY

@dengert
Copy link
Member

dengert commented May 27, 2017

A few questions about SafeNet:

  • Does SafeNet provide any vendor provided attributes? For example to find out if NO PUBLIC CRYPTO is enabled.
  • Are there multiple versions of the SafeNet module? Could code use the CK_TOKEN_INFO or CK_SLOT_INFO to determine if NO PUBLIC CRYPTO is enabled? Do they all have manufacturer="SafeNet Inc."?
  • Does the NO PUBLIC CRYPTO require the C_Login before the C_SignInit? So if a C_SignInit fails with CKR_USER_NOT_LOGGED_IN, is it expecting a C_Login and second call to C_SignInit? Or does it do something like complete the C_SignInit and return CKR_USER_NOT_LOGGED_IN, expecting a C_Login and then C_Sign, C_SignFinal or C_SignUpdate. It must be keeping some state internal that a C_Login was done as expected.

@mirozitnansky
Copy link
Author

mirozitnansky commented May 27, 2017

I swear I didn't understood that explanation this way... to use EMPTY as actual value...

Anyway thanks. I tested it, now it correctly ask for pin when having public object and no public crypto enabled.
But I thought, you'll implement it way, FORCE_LOGIN will call C_Login regardless activity/configuration. Now when having private object and no public crypt disabled, it just didn't find the key, which it would if user is logged.

[root@mzcentos ca_ptk]# openssl req -config openssl.cnf -key "pkcs11:model=0000;manufacturer=SafeNet%20Inc.;serial=0000%3a84324;token=OPENSSL;id=%42%19%72%91%10%73%e4%b1%e0%7f%67%fe%2f%31%a8%6c%e6%5c%ca%66;object=CA;object-type=private" -keyform engine -engine pkcs11 -new -x509 -days 7300 -sha256 -extensions v3_ca -out certs/ca.cert.pem
PKCS#11: Initializing the engine
logger: s_ctData->ft = 0x0xabd2b0
logger: s_ctData->ft->C_Initialize = 0x0x7fe02e3f2e20
Found 2 slots
engine "pkcs11" set.
Loading private key "pkcs11:model=0000;manufacturer=SafeNet%20Inc.;serial=0000%3a84324;token=OPENSSL;id=%42%19%72%91%10%73%e4%b1%e0%7f%67%fe%2f%31%a8%6c%e6%5c%ca%66;object=CA;object-type=private"
Looking in slot -1 for key: id=421972ffffff911073ffffffe4ffffffb1ffffffe07f67fffffffe2f31ffffffa86cffffffe65cffffffca66 label=CA
[0] SafeNet Software Only.:84                    (OPENSSL)
[1] SafeNet Software Only.     login             (AdminToken (0000))
Found slot:  SafeNet Software Only.:84324
Found token: OPENSSL
Found 0 certificate:
No private keys found.
PKCS11_get_private_key returned NULL
cannot load Private Key from engine
140600873306016:error:26096080:engine routines:ENGINE_load_private_key:failed loading private key:eng_pkey.c:126:

PC_PRVOBJ.txt
Is this somehow solvable?

Just to be sure, I am really THANKFULL even for current solution. Only downside is, until hsm mode is switched to FIPS, it won't be required to enter pin when signing the certification request.

@mirozitnansky
Copy link
Author

mirozitnansky commented May 27, 2017

@dengert

Does SafeNet provide any vendor provided attributes? For example to find out if NO PUBLIC CRYPTO is enabled.

There are some, but nothing can be used for NPC mode identification. They are saying CKF_LOGIN_REQUIRED should be sufficient to detect login requirement. Everything documentation says I posted before #160 (comment)

Are there multiple versions of the SafeNet module? Could code use the CK_TOKEN_INFO or CK_SLOT_INFO to determine if NO PUBLIC CRYPTO is enabled? Do they all have manufacturer="SafeNet Inc."?

I can't tell for sure. Currently I think manufacturer="SafeNet Inc." could be also used for LUNA SA hsms. But, as Safenet was 2 years ago acquired by Gemalto, it's quite possible they will change manufacturer name to Gemalto in future firmwares. So this is completely unreliable.

Does the NO PUBLIC CRYPTO require the C_Login before the C_SignInit? So if a C_SignInit fails with CKR_USER_NOT_LOGGED_IN, is it expecting a C_Login and second call to C_SignInit? Or does it do something like complete the C_SignInit and return CKR_USER_NOT_LOGGED_IN, expecting a C_Login and then C_Sign, C_SignFinal or C_SignUpdate. It must be keeping some state internal that a C_Login was done as expected.

#160 (comment) says
all the cryptographic C_xxxInit

In second document I found this definition.

No Public Crypto
The No Public Crypto flag, when set, indicates that no user can perform a cryptographic operation without having first authenticated themselves.
When this flag is set, each token in the system will have the PKCS #11 CKF_LOGIN_REQUIRED flag set to indicate that applications must authenticate before operations are allowed. Note that this security flag does not affect the Admin token which always requires authentication for access.
NOTE: This flag does not imply that public key cryptography is not allowed. Setting this flag will not prevent RSA processing.

Utilities that come with HSM for certification management always require to enter pin before C_Sign, regardless mode so I have quick way to test it.

@dengert
Copy link
Member

dengert commented May 28, 2017

So it sounds like they interpret CKF_LOGIN_REQUIRED to mean do a C_Login up front before looking for keys?

It looks like the ctx_load_privkey has be modified to test for if (!ctx->force_login)
which should have fixed the issue from #160 (comment)

What I was also asking about: Does SafeNet require C_Login to be called for every C_xxxInit call or
can a single C_Login be done once for multiple crypto operations.

@mirozitnansky
Copy link
Author

@dengert you call C_Login once, than repeat crypto operations how many times you wish in same session, untis it closes.

@nmav
Copy link
Contributor

nmav commented May 28, 2017

@mirozitnansky So it the only issue that this HSM doesn't support the CKU_CONTEXT_SPECIFIC? If C_Login falls back to using CKU_USER in the case CKR_USER_TYPE_INVALID is returned, would it address the issue?

btw. I'm trying to replicate that behavior in mock pkcs11 module
https://gitlab.com/gnutls/gnutls/merge_requests/398/

@mirozitnansky
Copy link
Author

Yes, I suppose. That would address compatibility with pkcs v2.10, which hasn't CKU_CONTEXT_SPECIFIC specified. But this is my option, I am no expert in this.

But setting FORCE_LOGIN, already overcome issue by forces engine to login before C_Sign CKU_USER. In my last comment to latest patch, I was just asking, if FORCE_LOGIN could work for every session, regardless of activity.

@dengert
Copy link
Member

dengert commented May 28, 2017

So would some pseudo code like this work:
pkcs11_authenticate takes and extra argument, the user_type for C_Login.
CKA_ALWAYS_AUTHENTICATE use of pkcs11_authenticate would pass CKU_CONTEXT_SPECIFIC

rv = C_XXXXInit(...)
if (rv == CKR_USER_NOT_LOGGED_IN) {
rv = pkcs11_authenticate(key, CKR_USER);
if (rv == CKR_OK)
rv = C_XXXXInit(...)
}
(rv is either OK or that last failure.)

replace every crypto Init call with something like the above. (Could be a macro)
No need for force login. Above should work for every module and catch the first or every time module wants a new login.

@mtrojnar
Copy link
Member

I was just asking, if FORCE_LOGIN could work for every session, regardless of activity.

It does. It just needs​ to be enabled, which you forgot to do.

@mirozitnansky
Copy link
Author

mirozitnansky commented May 28, 2017

So I ran same test again

list of slot 0

[root@mzcentos ~]# ctkmu l --slot-num=0 -v
ProtectToolkit C Key Management Utility 5.2.0
Copyright (c) Safenet, Inc. 2009-2016

logger: s_ctData->ft = 0x0x1c49390
logger: s_ctData->ft->C_Initialize = 0x0x7fa2ea647e20
Do you wish to view private (user) objects [y/N]: y   

Please enter User's PIN for the token in slot 0: 

Manufacturer      = SafeNet Inc.
Label             = OPENSSL                         
Flags             = 0x649 (RNG USER-PIN-INIT CLOCK TOKEN-INIT DUAL-CRYPTO)

Public and Private Objects:
CA_v1                            - PUBLIC_KEY      RSA         

Attribute list for object CA_v1, slot 0
Private      = 0
Sensitive    = 0
Modifiable   = 1
Import       = 0
Export       = 0
Wrap         = 0
Unwrap       = 0
Exportable   = 0
Extractable  = 0
Derive       = 0
Encrypt      = 0
Decrypt      = 0
Sign         = 0
Verify       = 0
Key Size     = 3072 bits

CA_v1                            - PRIVATE_KEY     RSA         

Attribute list for object CA_v1, slot 0
Private      = 1
Sensitive    = 1
Modifiable   = 1
Import       = 0
Export       = 0
Wrap         = 0
Unwrap       = 0
Exportable   = 0
Extractable  = 0
Derive       = 0
Encrypt      = 0
Decrypt      = 0
Sign         = 1
Verify       = 0
Sign Local   = 0
Key Size     = 3072 bits

private key is private object. NO_PUBLIC_CRYPTO is disabled

print of used openssl config - engine.cnf and sign attempt

[root@mzcentos ca_ptk]# cat engine.cnf
openssl_conf = openssl_def

[ openssl_def ]
engines = engine_section

[ engine_section ]
pkcs11 = pkcs11_section

[ pkcs11_section ]
engine_id = pkcs11

dynamic_path = /root/git/libp11/src/.libs/pkcs11.so
MODULE_PATH = /opt/safenet/protecttoolkit5/ptk/lib/libcryptoki.so
FORCE_LOGIN = EMPTY
VERBOSE = EMPTY
default_algorithms = ALL
init = 0
[root@mzcentos ca_ptk]# openssl req -config engine.cnf -key "pkcs11:token=OPENSSL;object=CA_v1;object-type=private" -keyform engine -engine pkcs11 -new -x509 -days 7300 -sha384 -out certs/ca.cert.pem
PKCS#11: Initializing the engine
logger: s_ctData->ft = 0x0x240ab20
logger: s_ctData->ft->C_Initialize = 0x0x7feff7cafe20
Found 2 slots
engine "pkcs11" set.
Loading private key "pkcs11:token=OPENSSL;object=CA_v1;object-type=private"
Looking in slot -1 for key: label=CA_v1
[0] SafeNet Software Only.:84                    (OPENSSL)
[1] SafeNet Software Only.     login             (AdminToken (0000))
Found slot:  SafeNet Software Only.:84324
Found token: OPENSSL
Found 0 certificate:
No private keys found.
PKCS11_get_private_key returned NULL
cannot load Private Key from engine
140668679198624:error:26096080:engine routines:ENGINE_load_private_key:failed loading private key:eng_pkey.c:126:
unable to load Private Key

pkcs11 log
PC_PRIVOBJ.txt

there is no C_Login

@mirozitnansky
Copy link
Author

mirozitnansky commented May 28, 2017

It's actually pretty similar behavior to p11tool, even if is set parameter --login to force user login, key is still not found

[root@mzcentos ca_ptk]# GNUTLS_PIN=1111 p11tool -d 9999 --provider /opt/safenet/protecttoolkit5/ptk/lib/libcryptoki.so --list-all  "pkcs11:token=OPENSSL" --login
Setting log level to 9999
|<2>| p11: Initializing module: /opt/safenet/protecttoolkit5/ptk/lib/libcryptoki.so
logger: s_ctData->ft = 0x0x11363a0
logger: s_ctData->ft->C_Initialize = 0x0x7ffabb2a3e20
Trusted: no
Wrap: no
CA: no
Login: yes
SO Login: no
Detailed URLs: no

|<2>| Initializing PKCS #11 modules
|<3>| ASSERT: pkcs11.c:2257
|<2>| p11: No login required.
|<3>| ASSERT: pkcs11.c:1647
|<3>| ASSERT: pkcs11.c:2466
Object 0:
	URL: pkcs11:model=0000;manufacturer=SafeNet%20Inc.;serial=0000%3a84324;token=OPENSSL;id=%63%b0%56%d5%c8%90%57%89%87%79%11%0a%04%60%2a%85%2b%de%f8%52;object=CA_v1;object-type=public
	Type: Public key
	Label: CA_v1
	ID: 63:b0:56:d5:c8:90:57:89:87:79:11:0a:04:60:2a:85:2b:de:f8:52

@nmav
Copy link
Contributor

nmav commented May 29, 2017

It's actually pretty similar behavior to p11tool, even if is set parameter --login to force user login, key is still not found

That is because the CKF_LOGIN_REQUIRED is not set for that token. When this flag is absent p11tool will not attempt to login. I'm starting to think that this hsm has its own version of pkcs11 implemented.

However, could that been a key that was generated with "no public crypto" disabled and listed when "no public crypto" is enabled?

@mirozitnansky
Copy link
Author

NO_PUBLIC_CRYPTO is one of the security flags which can be turned on/off for entire HSM, is has nothing to do with specific token or key generation process.

The following values are valid for security mode:
flag Description
F Fips 140-2 Mode (equivalent to -faclntu)
a Only allow FIPS Approved Algorithms
c No Public Crypto
d Des Keys Even Parity Allowed
e Entrust compliant
E User Specified ECC Domain Parameters Allowed
i Increased Security Level
l Lock Security Mode
n No Clear Pins allowed
N Full Secure Messaging Encryption
p Pure PKCS#11 Mode
t Tamper before upgrade
u Authentication Protection
U Full Secure Messaging Signing
0 All flags cleared

If object in token is private or public, is a attribute of that object set in time of creation.

So I generate public object in when NO_PUBLIC_CRYPTO is set off (CKF_LOGIN_REQUIRED=0), then I set hsm flag NO_PUBLIC_CRYPTO on (that leads to CKF_LOGIN_REQUIRED=1 on every token), but object itself stays public. Same with private object.
Private object which require call C_Login to be found, can exists even if NO_PUBLIC_CRYOTO is diabled. which is this case.

That's why I was asking to FORCE_LOGIN regardless of activity/setting/flags. In other words, if FORCE_LOGIN than always call C_Login second to C_Initialize.
If force login works only when some conditions are met, than that's not really a FORCE.

But again, I am layman here who doesn't have in-depth knowledge of the topic.

@mtrojnar
Copy link
Member

Okay. I'll fix it.

@mtrojnar
Copy link
Member

@mirozitnansky Can we close this issue now?

@mirozitnansky
Copy link
Author

@mtrojnar, I am sorry I didn't have time to test it earlier.
It is working now as expected in FIPS and default mode, always asking for pin.
Once again, thank you very much for you support and all effort you are putting into this.

gnutlsmirror pushed a commit to gnutls/gnutls that referenced this issue Jun 1, 2017
These HSMs do not support CKA_ALWAYS_AUTHENTICATE, nor understand CKU_CONTEXT_SPECIFIC,
but rather return CKR_USER_NOT_LOGGED_IN on the first private key operation.
Try to discover that state by calling C_Login when CKR_USER_NOT_LOGGED_IN
is seen, and retrying with CKU_USER after CKU_CONTEXT_SPECIFIC login fails.
See discussion in OpenSC/libp11#160

Signed-off-by: Nikos Mavrogiannopoulos <nmav@gnutls.org>
gnutlsmirror pushed a commit to gnutls/gnutls that referenced this issue Jun 16, 2017
These HSMs do not support CKA_ALWAYS_AUTHENTICATE, nor understand CKU_CONTEXT_SPECIFIC,
but rather return CKR_USER_NOT_LOGGED_IN on the first private key operation.
Try to discover that state by calling C_Login when CKR_USER_NOT_LOGGED_IN
is seen, and retrying with CKU_USER after CKU_CONTEXT_SPECIFIC login fails.
See discussion in OpenSC/libp11#160

Signed-off-by: Nikos Mavrogiannopoulos <nmav@gnutls.org>
gnutlsmirror pushed a commit to gnutls/gnutls that referenced this issue Jun 17, 2017
These HSMs return CKR_USER_NOT_LOGGED_IN on the first private key operation,
instead of using CKA_ALWAYS_AUTHENTICATE or similar. Detect that state
and retry login with CKU_USER.

See discussion in OpenSC/libp11#160

Signed-off-by: Nikos Mavrogiannopoulos <nmav@gnutls.org>
gnutlsmirror pushed a commit to gnutls/gnutls that referenced this issue Jun 19, 2017
These HSMs return CKR_USER_NOT_LOGGED_IN on the first private key operation,
instead of using CKA_ALWAYS_AUTHENTICATE or similar. Detect that state
and retry login with CKU_USER.

See discussion in OpenSC/libp11#160

Signed-off-by: Nikos Mavrogiannopoulos <nmav@gnutls.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants