Giter Site home page Giter Site logo

crypto11's Introduction

Crypto11

GoDoc Build Status

This is an implementation of the standard Golang crypto interfaces that uses PKCS#11 as a backend. The supported features are:

  • Generation and retrieval of RSA, DSA and ECDSA keys.
  • Importing and retrieval of x509 certificates
  • PKCS#1 v1.5 signing.
  • PKCS#1 PSS signing.
  • PKCS#1 v1.5 decryption
  • PKCS#1 OAEP decryption
  • ECDSA signing.
  • DSA signing.
  • Random number generation.
  • AES and DES3 encryption and decryption.
  • HMAC support.

Signing is done through the crypto.Signer interface and decryption through crypto.Decrypter.

To verify signatures or encrypt messages, retrieve the public key and do it in software.

See the documentation for details of various limitations, especially regarding symmetric crypto.

Installation

Since v1.0.0, crypto11 requires Go v1.11+. Install the library by running:

go get github.com/ThalesIgnite/crypto11

The crypto11 library needs to be configured with information about your PKCS#11 installation. This is either done programmatically (see the Config struct in the documentation) or via a configuration file. The configuration file is a JSON representation of the Config struct.

A minimal configuration file looks like this:

{
  "Path" : "/usr/lib/softhsm/libsofthsm2.so",
  "TokenLabel": "token1",
  "Pin" : "password"
}
  • Path points to the library from your PKCS#11 vendor.
  • TokenLabel is the CKA_LABEL of the token you wish to use.
  • Pin is the password for the CKU_USER user.

Testing Guidance

Disabling tests

To disable specific tests, set the environment variable CRYPTO11_SKIP=<flags> where <flags> is a comma-separated list of the following options:

  • CERTS - disables certificate-related tests. Needed for AWS CloudHSM, which doesn't support certificates.
  • OAEP_LABEL - disables RSA OAEP encryption tests that use source data encoding parameter (also known as a 'label' in some crypto libraries). Needed for AWS CloudHSM.
  • DSA - disables DSA tests. Needed for AWS CloudHSM (and any other tokens not supporting DSA).

Testing with Thales Luna HSM

Testing with AWS CloudHSM

A minimal configuration file for CloudHSM will look like this:

{
  "Path" : "/opt/cloudhsm/lib/libcloudhsm_pkcs11_standard.so",
  "TokenLabel": "cavium",
  "Pin" : "username:password",
  "UseGCMIVFromHSM" : true,
}

To run the test suite you must skip unsupported tests:

CRYPTO11_SKIP=CERTS,OAEP_LABEL,DSA go test -v

Be sure to take note of the supported mechanisms, key types and other idiosyncrasies described at https://docs.aws.amazon.com/cloudhsm/latest/userguide/pkcs11-library.html. Here's a collection of things we noticed when testing with the v2.0.4 PKCS#11 library:

  • 1024-bit RSA keys don't appear to be supported, despite what C_GetMechanismInfo tells you.
  • The CKM_RSA_PKCS_OAEP mechanism doesn't support source data. I.e. when constructing a CK_RSA_PKCS_OAEP_PARAMS, one must set pSourceData to NULL and ulSourceDataLen to zero.
  • CloudHSM will generate it's own IV for GCM mode. This is described in their documentation, see footnote 4 on https://docs.aws.amazon.com/cloudhsm/latest/userguide/pkcs11-mechanisms.html.
  • It appears that CKA_ID values must be unique, otherwise you get a CKR_ATTRIBUTE_VALUE_INVALID error.
  • Very rapid session opening can trigger the following error:
    C_OpenSession failed with error CKR_ARGUMENTS_BAD : 0x00000007
    HSM error 8c: HSM Error: Already maximum number of sessions are issued
    

Testing with SoftHSM2

To set up a slot:

$ cat softhsm2.conf
directories.tokendir = /home/rjk/go/src/github.com/ThalesIgnite/crypto11/tokens
objectstore.backend = file
log.level = INFO
$ mkdir tokens
$ export SOFTHSM2_CONF=`pwd`/softhsm2.conf
$ softhsm2-util --init-token --slot 0 --label test
=== SO PIN (4-255 characters) ===
Please enter SO PIN: ********
Please reenter SO PIN: ********
=== User PIN (4-255 characters) ===
Please enter user PIN: ********
Please reenter user PIN: ********
The token has been initialized.

The configuration looks like this:

$ cat config
{
  "Path" : "/usr/lib/softhsm/libsofthsm2.so",
  "TokenLabel": "test",
  "Pin" : "password"
}

(At time of writing) OAEP is only partial and HMAC is unsupported, so expect test skips.

Testing with nCipher nShield

In all cases, it's worth enabling nShield PKCS#11 log output:

export CKNFAST_DEBUG=2

To protect keys with a 1/N operator cardset:

$ cat config
{
  "Path" : "/opt/nfast/toolkits/pkcs11/libcknfast.so",
  "TokenLabel": "rjk",
  "Pin" : "password"
}

You can also identify the token by serial number, which in this case means the first 16 hex digits of the operator cardset's token hash:

$ cat config
{
  "Path" : "/opt/nfast/toolkits/pkcs11/libcknfast.so",
  "TokenSerial": "1d42780caa22efd5",
  "Pin" : "password"
}

A card from the cardset must be in the slot when you run go test.

To protect keys with the module only, use the 'accelerator' token:

$ cat config
{
  "Path" : "/opt/nfast/toolkits/pkcs11/libcknfast.so",
  "TokenLabel": "accelerator",
  "Pin" : "password"
}

(At time of writing) GCM is not implemented, so expect test skips.

Limitations

  • The PKCS1v15DecryptOptions SessionKeyLen field is not implemented and an error is returned if it is nonzero. The reason for this is that it is not possible for crypto11 to guarantee the constant-time behavior in the specification. See issue #5 for further discussion.
  • Symmetric crypto support via cipher.Block is very slow. You can use the BlockModeCloser API (over 400 times as fast on my computer) but you must call the Close() interface (not found in cipher.BlockMode). See issue #6 for further discussion.

Contributions

Contributions are gratefully received. Before beginning work on sizeable changes, please open an issue first to discuss.

Here are some topics we'd like to cover:

  • Full test instructions for additional PKCS#11 implementations.

crypto11's People

Contributors

prosaicsatsuma avatar jls5177 avatar solcates avatar cbroglie avatar dmjones avatar cristiklein avatar jossef avatar nickrmc83 avatar optnfast avatar sitano avatar bernard-wagner avatar kailun-qin avatar maraino avatar

Stargazers

Hatter Jiang avatar H avatar Alessandro Rinaldi avatar Nemezo avatar Cleilson avatar Carlos Santiago Yanzon avatar Malte Poll avatar Tof avatar lihnux avatar PIKCO avatar Elliot Courant avatar ren-hq123 avatar ik5 avatar Odinysus avatar  avatar John Wang avatar Matt A avatar Abdulhamit Kumru avatar Tuan Anh avatar  avatar Stanislav Láznička avatar Chris Speidel avatar  avatar afrizaloky avatar Curith avatar Socheat Sok avatar sean avatar Patrick Gaubatz avatar phial3 avatar Xiaofan Hu avatar Florent Viel avatar  avatar David Schneider avatar Andrey Gulitsky avatar Tom Thorogood avatar  avatar  avatar Nicolas Limage avatar wiviam avatar Sergio Acosta avatar Olivier Cedric Barbier avatar Min Deng avatar  avatar Matt Schultz avatar Savely Krasovsky avatar Weiguo Kong avatar 0xZensh avatar Christian Sakshaug avatar 虫子樱桃 avatar ELTAYEB ALI aka Fadie avatar Osama Adil avatar Ronnie Flathers avatar ollcp avatar Dimitar Tomov avatar Ionuț Mihalcea avatar Reinaldo Borges avatar  avatar Goe Joe avatar  avatar Joern Barthel avatar  avatar Marcelo Henrique Neppel avatar Andy avatar  avatar  avatar Evgeny avatar warm3snow avatar Chen Shi avatar  avatar yyp_1561 avatar Michel Oosterhof avatar Ties de Kock avatar Tomasz Maczukin avatar Antonio Henrique de Melo Costa avatar Rogelio Morrell avatar Brandon Weeks avatar KSomi avatar Hüseyin Mert avatar Dovy Bartkevicius avatar Jhonny Escame avatar Francesco Furiani avatar Mariano Kunzi avatar  avatar lee preimesberger avatar Florian Harwoeck avatar Sathaphorn Phansiri avatar  avatar Brian Wojtczak avatar Michael Malone avatar Justin Case avatar Ralph Bragg avatar  avatar  avatar Sergey Treinis avatar chris avatar rtee+ avatar BEREZA KIRILL avatar  avatar Herman Slatman avatar Csoban Kesmarki avatar

Watchers

Mikal avatar  avatar James Cloos avatar Rafael Escrich avatar Ryan Hurst avatar  avatar Denis Issoupov avatar lee preimesberger avatar  avatar KSomi avatar phial3 avatar  avatar

crypto11's Issues

BenchmarkCBC returns CKR_DEVICE_ERROR on Luna HSM

Hi,

I am testing this library on Luna PCIe HSM s790. But I run into the following error when running the BenchmarkCBC test.

I don't have the same issue when running the PKCS#11 Java library or the C lib.

The Luna HSM S790 's token info as

Sessions (count/max) -> 3457 / 0 Read/Write Sessions (count/max) -> 3457 / 0 PIN Length (min - max) -> 7 - 255 Public Memory (free / total) -> 33524624 - 33544784 Private Memory (free / total) -> 33524624 - 33544784 Hardware Version -> 2.0 Firmware Version -> 7.0.3

go test -bench=BenchmarkCBC -run=^a
goos: linux
goarch: amd64
pkg: github.com/ThalesIgnite/crypto11
BenchmarkCBC/Native-16         	panic: pkcs11: 0x30: CKR_DEVICE_ERROR

goroutine 53 [running]:
github.com/ThalesIgnite/crypto11.(*SecretKey).Encrypt(0xc0002165c0, 0xc0002360c0, 0x10, 0xbf40, 0xc0002360c0, 0x10, 0xbf40)
	/tmp/crypto11/block.go:87 +0x120
crypto/cipher.(*cbcEncrypter).CryptBlocks(0xc00007c050, 0xc000232000, 0xbf40, 0x10000, 0xc0002260c0, 0xbf40, 0xbf40)
	/usr/local/go/src/crypto/cipher/cbc.go:73 +0x192
github.com/ThalesIgnite/crypto11.BenchmarkCBC.func2(0xc000164380)
	/tmp/crypto11/symmetric_test.go:284 +0xba
testing.(*B).runN(0xc000164380, 0x1)
	/usr/local/go/src/testing/benchmark.go:191 +0xe8
testing.(*B).run1.func1(0xc000164380)
	/usr/local/go/src/testing/benchmark.go:231 +0x57
created by testing.(*B).run1
	/usr/local/go/src/testing/benchmark.go:224 +0x7d
exit status 2
FAIL	github.com/ThalesIgnite/crypto11	2.500s

Consistent error behavior in Configure()

As of 2018-06-25, the Configure method issues a log message for some but not all errors. It ought to have more consistent behavior. My inclination is to log for everything, since some of the fixed Err... returns don't have all the information you might reasonably want, but I'm open to other opinions.

FindAllKeyPairs breaks on unsupported key type

Hello,

I'm using this awesome library to read a certificate and get the corresponding signer from a smartcard.
The smartcard contains 3 proper certificates (RSA).
However, the following code returns an empty slice:

allKeyPairs, err := c.FindAllKeyPairs()
if err != nil {
	fmt.Errorf("error finding all key pairs", err)
}

fmt.Println("Number of key pairs ", len(allKeyPairs))

Now, I've found that for some reason there's a case where there is an unsupported key type (in file keys.go on line 209).

On the smartcard I can see a total of 13 objects (but I'm not a PKCS#11 expert so I don't really know what is going on in depth).

This unsupported key type in turn causes the loop in the function FindKeyPairsWithAttributes to return on line 232.

Replacing this return statement with a continue statement keeps the loop running and leads to the desired result (in my case a slice with 3 signer interfaces).

err = c.withSession(func(session *pkcs11Session) error {
	// Add the private key class to the template to find the private half
	privAttributes := attributes.Copy()
	err = privAttributes.Set(CkaClass, pkcs11.CKO_PRIVATE_KEY)
	if err != nil {
		return err
	}

	privHandles, err := findKeysWithAttributes(session, privAttributes.ToSlice())
	if err != nil {
		return err
	}

	for _, privHandle := range privHandles {
		k, err := c.makeKeyPair(session, &privHandle)

		if err == errNoCkaId || err == errNoPublicHalf {
			continue
		}
		if err != nil {
                           // keep looping in case of an unsupported key type
			continue
			//return err
		}

		keys = append(keys, k)
	}

	return nil
})

I can't judge if this should be fixed in this lib. If not, are there any workarounds? I basically need to read all certificates from the smartcard, choose the one with usage of clientauth and then get the corresponding singer to use the cert and the signer to perform a http client request.

Cheers,

Jan

How to use Signer to decrypt from Encrypted data ?

My application uses HSM connect uisng PKCS#11.

I create pairs using method

```GenerateRSAKeyPairWithLabel(id, label, 2048)``

I receive a public key, later i using public key for Encrypt.

I want decrypt from Encrypt data. I am following the flow.

I Find key by using method FindKeyPairs(id, label).
I receive a Signer
image

How to use Signer to decrypt from Encrypted data ?

Thanks

Tests failing with CloudHSM (Client V5)

Hi,

I'm trying to use crypto11 with AWS CloudHSM. To start, I'm just trying to run the tests in this repo, but facing the following errors:

=== RUN   TestSymmetricRequiredArgs
    require.go:794: 
                Error Trace:    symmetric_test.go:323
                Error:          Received unexpected error:
                                pkcs11: 0x190: CKR_CRYPTOKI_NOT_INITIALIZED
                                failed to list PKCS#11 slots
                Test:           TestSymmetricRequiredArgs
--- FAIL: TestSymmetricRequiredArgs (0.00s)

Looking at the CloudHSM logs, I see something like the following with a login issue:

... Failed to login to HSM ... User is already logged in

Is there any way to work around this?

Thanks,
Chad

Crypto11 fails to perform decrypt operation

Hi,
I’m currently using crypto11 to perform TLS handshakes with an RSA2048 private key stored in a TPM (using TrouSerS). When I try to make a TLS connection, using the TLS_RSA_WITH_AES_128_CBC_SHA cipher suite, crypto11 fails to perform decrypt and errors out at this location (SessionKeyLen > 0).
I tried other cipher suites and it succeeds with any that use RSA+ECDHE (e.g. TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA) as they don’t require the decrypt operation and only use the sign operation.
Is this a known limitation of crpyto11, or are we doing something wrong? The same private key and cipher suite combination works when using GoLang's internal stack (i.e.. without TPM / Crypto11).


Thanks!

Varun

SECP256K1 Support

Does it supports curve secp256k1 to be used in AWS Cloud HSM? If it is, then could you please tell me as to what exactly the curve name I need to pass?

Below are the curve details, I am using:

curve := elliptic.P256()
crypto.SHA256 // hash function

YubiHSM Support

I'm trying to get this up and running to communicate with a YubiHSM2 via https://developers.yubico.com/YubiHSM2/Component_Reference/PKCS_11/ but, the test always fails with a panic.

exact error output is here:

2019/01/18 11:20:47 Failed to initialize PKCS#11 library: pkcs11: 0x6: CKR_FUNCTION_FAILED
--- FAIL: TestClose (0.00s)
    close_test.go:36: pkcs11: 0x6: CKR_FUNCTION_FAILED
2019/01/18 11:20:47 PKCS#11 library already configured
2019/01/18 11:20:47 PKCS#11 library already configured
2019/01/18 11:20:47 PKCS#11 library already configured
2019/01/18 11:20:47 PKCS#11 library already configured
--- FAIL: TestLoginContext (0.00s)
    --- FAIL: TestLoginContext/key_identity_with_login (0.00s)
        crypto11_test.go:57: crypto11.GenerateDSAKeyPair: pkcs11: 0x190: CKR_CRYPTOKI_NOT_INITIALIZED
    --- FAIL: TestLoginContext/key_identity_with_expiration (0.00s)
        crypto11_test.go:100: crypto11.GenerateDSAKeyPair: pkcs11: 0x190: CKR_CRYPTOKI_NOT_INITIALIZED
    --- FAIL: TestLoginContext/login_context_shared_between_sessions (0.00s)
        crypto11_test.go:134: crypto11.GenerateDSAKeyPair: pkcs11: 0x190: CKR_CRYPTOKI_NOT_INITIALIZED
2019/01/18 11:20:47 PKCS#11 library already configured
--- FAIL: TestIdentityExpiration (0.00s)
    crypto11_test.go:178: crypto11.GenerateDSAKeyPair: pkcs11: 0x190: CKR_CRYPTOKI_NOT_INITIALIZED
2019/01/18 11:20:47 PKCS#11 library already configured
--- FAIL: TestHardDSA (0.00s)
    dsa_test.go:109: crypto11.GenerateDSAKeyPair: pkcs11: 0x190: CKR_CRYPTOKI_NOT_INITIALIZED
2019/01/18 11:20:47 PKCS#11 library already configured
--- FAIL: TestHardECDSA (0.00s)
    ecdsa_test.go:67: GenerateECDSAKeyPair: pkcs11: 0x190: CKR_CRYPTOKI_NOT_INITIALIZED
2019/01/18 11:20:47 PKCS#11 library already configured
--- FAIL: TestHmac (0.00s)
    hmac_test.go:36: GetInfo: pkcs11: 0x190: CKR_CRYPTOKI_NOT_INITIALIZED
2019/01/18 11:20:47 PKCS#11 library already configured
2019/01/18 11:20:48 PKCS#11 library already configured
2019/01/18 11:20:48 PKCS#11 library already configured
2019/01/18 11:20:50 PKCS#11 library already configured
--- FAIL: TestPoolTimeout (3.01s)
    --- FAIL: TestPoolTimeout/first_login,_exp_0s (1.00s)
        pool_test.go:51: failed to generate a key, unexpected error: pkcs11: 0x190: CKR_CRYPTOKI_NOT_INITIALIZED
    --- FAIL: TestPoolTimeout/reuse_expired_handle,_exp_0s (0.00s)
        pool_test.go:66: failed to generate a key: pkcs11: 0x190: CKR_CRYPTOKI_NOT_INITIALIZED
    --- FAIL: TestPoolTimeout/first_login,_exp_1s (2.00s)
        pool_test.go:51: failed to generate a key, unexpected error: pkcs11: 0x190: CKR_CRYPTOKI_NOT_INITIALIZED
    --- FAIL: TestPoolTimeout/reuse_expired_handle,_exp_1s (0.00s)
        pool_test.go:66: failed to generate a key: pkcs11: 0x190: CKR_CRYPTOKI_NOT_INITIALIZED
2019/01/18 11:20:50 PKCS#11 library already configured
--- FAIL: TestRandomReader (0.00s)
    rand_test.go:36: crypto11.PKCS11RandRead.Read: pkcs11: 0x190: CKR_CRYPTOKI_NOT_INITIALIZED
2019/01/18 11:20:50 PKCS#11 library already configured
--- FAIL: TestNativeRSA (0.13s)
    --- FAIL: TestNativeRSA/1024 (0.02s)
        --- FAIL: TestNativeRSA/1024/Encrypt (0.00s)
            --- FAIL: TestNativeRSA/1024/Encrypt/OAEPSHA1 (0.00s)
                rsa_test.go:244: GetInfo: pkcs11: 0x190: CKR_CRYPTOKI_NOT_INITIALIZED
            --- FAIL: TestNativeRSA/1024/Encrypt/OAEPSHA224 (0.00s)
                rsa_test.go:244: GetInfo: pkcs11: 0x190: CKR_CRYPTOKI_NOT_INITIALIZED
            --- FAIL: TestNativeRSA/1024/Encrypt/OAEPSHA256 (0.00s)
                rsa_test.go:244: GetInfo: pkcs11: 0x190: CKR_CRYPTOKI_NOT_INITIALIZED
            --- FAIL: TestNativeRSA/1024/Encrypt/OAEPSHA384 (0.00s)
                rsa_test.go:244: GetInfo: pkcs11: 0x190: CKR_CRYPTOKI_NOT_INITIALIZED
            --- FAIL: TestNativeRSA/1024/Encrypt/OAEPSHA1Label (0.00s)
                rsa_test.go:244: GetInfo: pkcs11: 0x190: CKR_CRYPTOKI_NOT_INITIALIZED
            --- FAIL: TestNativeRSA/1024/Encrypt/OAEPSHA224Label (0.00s)
                rsa_test.go:244: GetInfo: pkcs11: 0x190: CKR_CRYPTOKI_NOT_INITIALIZED
            --- FAIL: TestNativeRSA/1024/Encrypt/OAEPSHA256Label (0.00s)
                rsa_test.go:244: GetInfo: pkcs11: 0x190: CKR_CRYPTOKI_NOT_INITIALIZED
            --- FAIL: TestNativeRSA/1024/Encrypt/OAEPSHA384Label (0.00s)
                rsa_test.go:244: GetInfo: pkcs11: 0x190: CKR_CRYPTOKI_NOT_INITIALIZED
    --- FAIL: TestNativeRSA/2048 (0.11s)
        --- FAIL: TestNativeRSA/2048/Encrypt (0.00s)
            --- FAIL: TestNativeRSA/2048/Encrypt/OAEPSHA1 (0.00s)
                rsa_test.go:244: GetInfo: pkcs11: 0x190: CKR_CRYPTOKI_NOT_INITIALIZED
            --- FAIL: TestNativeRSA/2048/Encrypt/OAEPSHA224 (0.00s)
                rsa_test.go:244: GetInfo: pkcs11: 0x190: CKR_CRYPTOKI_NOT_INITIALIZED
            --- FAIL: TestNativeRSA/2048/Encrypt/OAEPSHA256 (0.00s)
                rsa_test.go:244: GetInfo: pkcs11: 0x190: CKR_CRYPTOKI_NOT_INITIALIZED
            --- FAIL: TestNativeRSA/2048/Encrypt/OAEPSHA384 (0.00s)
                rsa_test.go:244: GetInfo: pkcs11: 0x190: CKR_CRYPTOKI_NOT_INITIALIZED
            --- FAIL: TestNativeRSA/2048/Encrypt/OAEPSHA512 (0.00s)
                rsa_test.go:244: GetInfo: pkcs11: 0x190: CKR_CRYPTOKI_NOT_INITIALIZED
            --- FAIL: TestNativeRSA/2048/Encrypt/OAEPSHA1Label (0.00s)
                rsa_test.go:244: GetInfo: pkcs11: 0x190: CKR_CRYPTOKI_NOT_INITIALIZED
            --- FAIL: TestNativeRSA/2048/Encrypt/OAEPSHA224Label (0.00s)
                rsa_test.go:244: GetInfo: pkcs11: 0x190: CKR_CRYPTOKI_NOT_INITIALIZED
            --- FAIL: TestNativeRSA/2048/Encrypt/OAEPSHA256Label (0.00s)
                rsa_test.go:244: GetInfo: pkcs11: 0x190: CKR_CRYPTOKI_NOT_INITIALIZED
            --- FAIL: TestNativeRSA/2048/Encrypt/OAEPSHA384Label (0.00s)
                rsa_test.go:244: GetInfo: pkcs11: 0x190: CKR_CRYPTOKI_NOT_INITIALIZED
            --- FAIL: TestNativeRSA/2048/Encrypt/OAEPSHA512Label (0.00s)
                rsa_test.go:244: GetInfo: pkcs11: 0x190: CKR_CRYPTOKI_NOT_INITIALIZED
2019/01/18 11:20:50 PKCS#11 library already configured
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
	panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x8 pc=0x590c1b]

goroutine 100 [running]:
testing.tRunner.func1(0xc000386300)
	/usr/local/go/src/testing/testing.go:792 +0x387
panic(0x5cb280, 0x951b40)
	/usr/local/go/src/runtime/panic.go:513 +0x1b9
github.com/thalesignite/crypto11.TestHardRSA.func1.2(0xc000386300)
	/home/wspears/go/src/github.com/thalesignite/crypto11/rsa_test.go:84 +0x2b
testing.tRunner(0xc000386300, 0xc0003d4120)
	/usr/local/go/src/testing/testing.go:827 +0xbf
created by testing.(*T).Run
	/usr/local/go/src/testing/testing.go:878 +0x353
exit status 2
FAIL	github.com/thalesignite/crypto11	3.424s

Ideas? Or pointers in the right direction?

Support importing and finding certificates

Current it is not possible to load certificates as x509.Certificates from an HSM. It would be beneficial to be able to import and find certificates for supported PKCS11 devices.

CK_OBJECT_CLASS class = CKO_CERTIFICATE;
CK_CERTIFICATE_TYPE certType = CKC_X_509;
CK_UTF8CHAR label[] = “A certificate object”;
CK_BYTE subject[] = {...};
CK_BYTE id[] = {123};
CK_BYTE certificate[] = {...};
CK_BBOOL true = CK_TRUE;
CK_ATTRIBUTE template[] = {
  {CKA_CLASS, &class, sizeof(class)},
  {CKA_CERTIFICATE_TYPE, &certType, sizeof(certType)};
  {CKA_TOKEN, &true, sizeof(true)},
  {CKA_LABEL, label, sizeof(label)-1},
  {CKA_SUBJECT, subject, sizeof(subject)},
  {CKA_ID, id, sizeof(id)},
  {CKA_VALUE, certificate, sizeof(certificate)}
};

AWS CloudHSM Test Failures

:~/go/src/github.com/ThalesIgnite/crypto11# go test
        SDK Version: 2.03

C_GenerateKeyPair failed with error CKR_ATTRIBUTE_VALUE_INVALID : 0x00000013
--- FAIL: TestClose (0.16s)
    close_test.go:40: crypto11.GenerateDSAKeyPair: pkcs11: 0x13: CKR_ATTRIBUTE_VALUE_INVALID
2018/12/14 08:49:50 PKCS#11 library already configured
        SDK Version: 2.03

C_GenerateKeyPair failed with error CKR_ATTRIBUTE_VALUE_INVALID : 0x00000013
        SDK Version: 2.03

C_GenerateKeyPair failed with error CKR_ATTRIBUTE_VALUE_INVALID : 0x00000013
        SDK Version: 2.03

C_GenerateKeyPair failed with error CKR_ATTRIBUTE_VALUE_INVALID : 0x00000013
--- FAIL: TestLoginContext (0.64s)
    --- FAIL: TestLoginContext/key_identity_with_login (0.21s)
        crypto11_test.go:57: crypto11.GenerateDSAKeyPair: pkcs11: 0x13: CKR_ATTRIBUTE_VALUE_INVALID
    --- FAIL: TestLoginContext/key_identity_with_expiration (0.21s)
        crypto11_test.go:100: crypto11.GenerateDSAKeyPair: pkcs11: 0x13: CKR_ATTRIBUTE_VALUE_INVALID
    --- FAIL: TestLoginContext/login_context_shared_between_sessions (0.21s)
        crypto11_test.go:134: crypto11.GenerateDSAKeyPair: pkcs11: 0x13: CKR_ATTRIBUTE_VALUE_INVALID
        SDK Version: 2.03

C_GenerateKeyPair failed with error CKR_ATTRIBUTE_VALUE_INVALID : 0x00000013
--- FAIL: TestIdentityExpiration (0.21s)
    crypto11_test.go:178: crypto11.GenerateDSAKeyPair: pkcs11: 0x13: CKR_ATTRIBUTE_VALUE_INVALID
        SDK Version: 2.03

C_GenerateKeyPair failed with error CKR_ATTRIBUTE_VALUE_INVALID : 0x00000013
--- FAIL: TestHardDSA (0.16s)
    dsa_test.go:109: crypto11.GenerateDSAKeyPair: pkcs11: 0x13: CKR_ATTRIBUTE_VALUE_INVALID
2018/12/14 08:49:51 PKCS#11 library already configured

C_SignInit failed with error CKR_KEY_SIZE_RANGE : 0x00000062

C_SignInit failed with error CKR_KEY_SIZE_RANGE : 0x00000062

C_SignInit failed with error CKR_KEY_SIZE_RANGE : 0x00000062

C_SignInit failed with error CKR_KEY_SIZE_RANGE : 0x00000062

C_SignInit failed with error CKR_KEY_SIZE_RANGE : 0x00000062

C_SignInit failed with error CKR_KEY_SIZE_RANGE : 0x00000062

C_SignInit failed with error CKR_KEY_SIZE_RANGE : 0x00000062

C_SignInit failed with error CKR_KEY_SIZE_RANGE : 0x00000062

C_SignInit failed with error CKR_KEY_SIZE_RANGE : 0x00000062

C_SignInit failed with error CKR_KEY_SIZE_RANGE : 0x00000062

C_SignInit failed with error CKR_KEY_SIZE_RANGE : 0x00000062

C_SignInit failed with error CKR_KEY_SIZE_RANGE : 0x00000062

C_SignInit failed with error CKR_KEY_SIZE_RANGE : 0x00000062

C_SignInit failed with error CKR_KEY_SIZE_RANGE : 0x00000062
--- FAIL: TestHardECDSA (17.49s)
    ecdsa_test.go:108: ECDSA Sign (hash 3): pkcs11: 0x62: CKR_KEY_SIZE_RANGE
    ecdsa_test.go:108: ECDSA Sign (hash 4): pkcs11: 0x62: CKR_KEY_SIZE_RANGE
    ecdsa_test.go:108: ECDSA Sign (hash 5): pkcs11: 0x62: CKR_KEY_SIZE_RANGE
    ecdsa_test.go:108: ECDSA Sign (hash 6): pkcs11: 0x62: CKR_KEY_SIZE_RANGE
    ecdsa_test.go:108: ECDSA Sign (hash 7): pkcs11: 0x62: CKR_KEY_SIZE_RANGE
    ecdsa_test.go:108: ECDSA Sign (hash 5): pkcs11: 0x62: CKR_KEY_SIZE_RANGE
    ecdsa_test.go:108: ECDSA Sign (hash 6): pkcs11: 0x62: CKR_KEY_SIZE_RANGE
    ecdsa_test.go:108: ECDSA Sign (hash 3): pkcs11: 0x62: CKR_KEY_SIZE_RANGE
    ecdsa_test.go:108: ECDSA Sign (hash 4): pkcs11: 0x62: CKR_KEY_SIZE_RANGE
    ecdsa_test.go:108: ECDSA Sign (hash 5): pkcs11: 0x62: CKR_KEY_SIZE_RANGE
    ecdsa_test.go:108: ECDSA Sign (hash 6): pkcs11: 0x62: CKR_KEY_SIZE_RANGE
    ecdsa_test.go:108: ECDSA Sign (hash 7): pkcs11: 0x62: CKR_KEY_SIZE_RANGE
    ecdsa_test.go:108: ECDSA Sign (hash 5): pkcs11: 0x62: CKR_KEY_SIZE_RANGE
    ecdsa_test.go:108: ECDSA Sign (hash 6): pkcs11: 0x62: CKR_KEY_SIZE_RANGE
        SDK Version: 2.03

C_OpenSession failed with error CKR_DEVICE_MEMORY : 0x00000031

C_OpenSession failed with error CKR_DEVICE_MEMORY : 0x00000031

C_OpenSession failed with error CKR_DEVICE_MEMORY : 0x00000031

C_OpenSession failed with error CKR_DEVICE_MEMORY : 0x00000031
--- FAIL: TestHmac (0.12s)
    --- FAIL: TestHmac/HMACSHA1 (0.00s)
        --- FAIL: TestHmac/HMACSHA1/Generate (0.00s)
            hmac_test.go:59: crypto11.GenerateSecretKey: pkcs11: 0x31: CKR_DEVICE_MEMORY
    --- FAIL: TestHmac/HMACSHA1General (0.00s)
        --- FAIL: TestHmac/HMACSHA1General/Generate (0.00s)
            hmac_test.go:59: crypto11.GenerateSecretKey: pkcs11: 0x31: CKR_DEVICE_MEMORY
    --- FAIL: TestHmac/HMACSHA256 (0.00s)
        --- FAIL: TestHmac/HMACSHA256/Generate (0.00s)
            hmac_test.go:59: crypto11.GenerateSecretKey: pkcs11: 0x31: CKR_DEVICE_MEMORY
        SDK Version: 2.03
        SDK Version: 2.03
        SDK Version: 2.03
        SDK Version: 2.03
        SDK Version: 2.03

C_GenerateRandom failed with error CKR_DATA_LEN_RANGE : 0x00000021
--- FAIL: TestRandomReader (0.26s)
    rand_test.go:36: crypto11.PKCS11RandRead.Read: pkcs11: 0x21: CKR_DATA_LEN_RANGE
2018/12/14 08:50:18 PKCS#11 library already configured
        SDK Version: 2.03

C_OpenSession failed with error CKR_DEVICE_MEMORY : 0x00000031

C_OpenSession failed with error CKR_DEVICE_MEMORY : 0x00000031
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
        panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x8 pc=0x590cdb]

goroutine 114 [running]:
testing.tRunner.func1(0xc0000dde00)
        /usr/local/go/src/testing/testing.go:792 +0x387
panic(0x5cb300, 0x950b60)
        /usr/local/go/src/runtime/panic.go:513 +0x1b9
github.com/ThalesIgnite/crypto11.TestHardRSA.func1.2(0xc0000dde00)
        /root/go/src/github.com/ThalesIgnite/crypto11/rsa_test.go:84 +0x2b
testing.tRunner(0xc0000dde00, 0xc000099c20)
        /usr/local/go/src/testing/testing.go:827 +0xbf
created by testing.(*T).Run
        /usr/local/go/src/testing/testing.go:878 +0x353
exit status 2
FAIL    github.com/ThalesIgnite/crypto11        29.216s

CloudHSM Config

Can you provide an example config for connecting to AWS CloudHSM?

For example, what value should go into "TokenLabel"?

Here is my attempt

 {
  "Path" : "/opt/cloudhsm/lib/libcloudhsm_pkcs11_standard.so",
  "TokenLabel": "cluster-fooobahhh",
  "Pin" : "username:password"
}

Test returns:

Failed to find Token in any Slot: crypto11: could not find PKCS#11 token --- FAIL: TestClose (0.05s) close_test.go:36: crypto11: could not find PKCS#11 token

AWS CloudHSM - could not find PKCS#11 token

Is AWS CloudHSM working with this library?

I've used Client SDK 5 and have run configure-pkcs11 this is working fine

cloudhsm-pkcs11.cfg:

{
  "clusters": [
    {
      "type": "hsm1",
      "cluster": {
        "client_cert_path": "/opt/cloudhsm/etc/ssl-client.crt",
        "client_key_path": "/opt/cloudhsm/etc/ssl-client.key",
        "hsm_ca_file": "/opt/cloudhsm/etc/customerCA.crt",
        "cluster_id": "cluster-RANDOMNUMBERS",
        "servers": [
          {
            "hostname": "172.31.12.139",
            "port": 2223,
            "enable": true
          },
          {
            "hostname": "172.31.1.43",
            "port": 2223,
            "enable": true
          }
        ]
      }
    }
  ],
  "logging": {
    "log_type": "file",
    "log_file": "/opt/cloudhsm/run/cloudhsm-pkcs11.log",
    "log_level": "info",
    "log_interval": "daily"
  }
}

Application:

  • It's expected to initialize the HSM with the correct token and pin, then just adds an ECDSA Key Pair

I am unable to connect to the HSM

# ./pkcs11_wrapper

2021/09/15 13:52:21 hsm.New:  aws-HSM
2021/09/15 13:52:21 hsm.createHSMCtx
panic: could not find PKCS#11 token

goroutine 1 [running]:
main.main()
        /home/ec2-user/pkcs11_wrapper/cmd/pkcs11_wrapper/main.go:31 +0x216

Code:

func createHSMCtx() (*crypto11.Context, error) {
	log.Println("hsm.createHSMCtx")

	PKCS11Configuration := new(crypto11.Config)
	PKCS11Configuration.Path = "/opt/cloudhsm/lib/libcloudhsm_pkcs11.so"
	PKCS11Configuration.TokenLabel = "cavium"
	PKCS11Configuration.Pin = "user1:abc123" // using these creds... I am able to login using the AWS tools fine.
	PKCS11Configuration.UseGCMIVFromHSM = true
	hsmCtx, err := crypto11.Configure(PKCS11Configuration)
	if err != nil {
		return nil, errors.WithStack(err)
	}
	log.Println("hsm.createHSMCtx: Created!")
	return hsmCtx, nil
}

Further logs see fine:

  • It initializes the connection with a response "Handshake is successful" and "Version handshake with server succeeded."
    The failed response is:
2021-09-15T12:05:30.079Z WARN  [1254] ThreadId(1) [cloudhsm_provider::hsm1::hsm_connection::dispatcher_strategy] UX000: Discarding HSM response because it does not match a pending request.

I'm not sure if the configurations are correct? Am I missing anything?

Configure() fails when using Tokens that do not require a login

I noticed the latest v1.x changes now always attempts to login to the token regardless if the token states it requires a login or not. The previous code was checking this flag before calling login.

There are a few proposals here:

  1. Always perform the login and only error out when the CKF_LOGIN_REQUIRED flag is set on the token
  2. Add a configuration field to optionally login to the token
  3. Only login when the TokenPin is non-empty

Reproduction Steps
Select the module/accelerator token on a Thales HSM. This token does not require a login and will return an error code when we attempt to login.

Stop generating labels with NUL bytes at the end

If users don't pass a label or ID to the key generation functions, one is generated for them. This value will typically have ~20 NUL bytes at the end, which causes problems if users convert to/from strings using string(<some bytes>) and []byte(<the string>). This kind of simple string conversions will discard NUL bytes, resulting in a different label or ID.

We should adjust this behaviour and return non-NUL data. I would suggest generating random bytes, converting to hexadecimal characters, then returning the bytes of that hexadecimal string.

Access to attributes (CKA_*)

Is it somehow possible to access the attributes of an object? E.g. the attributes CKA_ID and CKA_LABEL of a certificate.

Using Client Certificate in IIS Server

I have an api running on IIS, configured to accept client certificate, but the certificate is not being recognized when I send it through the code below:

`config := crypto11.Config{
Path: "C:\Windows\System32\aetpkss1.dll",
TokenLabel: "TOKEN",
Pin: "2903",
}

context, err := crypto11.Configure(&config)
if err != nil {
	log.Fatalln(err)
}

certificates, err := context.FindAllPairedCertificates()
if err != nil {
	log.Fatalln(err)
}

fmt.Println("total certificates: ", len(certificates))

cert := certificates[0]

caCert, _ := ioutil.ReadFile("ca.crt")
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)

client := &http.Client{
	Transport: &http.Transport{
		TLSClientConfig: &tls.Config{
			Certificates:       []tls.Certificate{cert},
			Renegotiation:      tls.RenegotiateOnceAsClient,
			RootCAs:            caCertPool,
			InsecureSkipVerify: true,
		},
	},
}

req, err := http.NewRequest("GET", "https://192.168.15.38:9443/api/teste", nil)
if err != nil {
	log.Fatalln(err)
}

req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36")

resp, err := client.Do(req)
if err != nil {
	log.Fatalln(err)
}

fmt.Println("status code: ", resp.StatusCode)`

The strange thing is that if I send it through Python or Java or .net code, the certificate is recognized normally.

FindAllKeyPairs for keys that their public key is in a X509 certificate

I'm dealing with a token issued in a crooked way - instead of public key object, the issuer have used a X509 certificate only.

I'd like to construct a KeyPair and use the public key from the X509 certificate object. current implementation skips such keys.
I'm going to patch it for myself, I can submit a PR to support this case (thought about extending the Config struct)

func (c *Context) FindAllKeyPairs() ([]Signer, error)
FindAllKeyPairs retrieves all existing asymmetric key pairs, or a nil slice if none can be found.

If a private key is found, but the corresponding public key is not, the key is not returned because we cannot implement crypto.Signer without the public key.

image


package main

import "fmt"
import "github.com/ThalesIgnite/crypto11"

func main() {
	config := crypto11.Config{
		Path: "C:\\Windows\\System32\\my-vendors-pkcs11.dll",
		TokenSerial: "my-token-serial",
		Pin: "my-token-pin",
	}

	context, err := crypto11.Configure(&config)
	if err != nil{
		fmt.Println(err);
		return
	}

	items, err := context.FindAllKeyPairs()
	if err != nil{
		fmt.Println(err);
		return
	}

	fmt.Println("length: ", len(items))
}

length: 0

Thoughts on a library redesign for release 1.0.0

We plan to redesign crypto11 to address architectural flaws and to simplify its usage. This would be a backwards-incompatible change, leading to a v1.0.0 release.

I'm cc-ing non-Thales folks who have contributed or commented on the library before, in the hope of some feedback or suggestions: @optnfast, @sitano, @cbroglie, @bernard-wagner.

Abandon the singleton pattern

Each instance of crypto11 will have its own configuration. We will extend the configuration object so tokens can be selected by slot number.

Stop randomly generating attribute values

Passing nil for id or label to the key generation functions will no longer generate random values. Instead, it will result in CKA_ID or CKA_LABEL being unset.

Add ...WithAttributes function variants

This is addresses my concerns in #15. All key generation functions will have a WithAttributes variant, which accepts a slice of attributes to apply to the secret key. For key pair functions, a separate slice of attributes will apply to the public key.

There will be a corresponding FindKeyPairWithAttributes function.

Remove ...WithSession functions

To manage the thread safety aspects of crypto11, we assume sessions are managed by our pool and are for the exclusive use of crypto11. Accepting sessions from the caller could easily break that guarantee.

If anyone is actively using these functions, it would be great to understand the use case.

Remove ...OnSlot functions

We can specify a slot number in the config, so we don't need these functions any more.

Don't provide access to the PKCS11 context

The current library returns the underlying miekg/pkcs11 context object. For the reasons listed above, relating to thread safety, I think this should be removed.

In fact, I'd suggest the public API doesn't rely on any structures from miekg/pkcs11. It's confusing for new users to mix and match data structures from two libraries, only one of which is under our control.

Add all the certificate functions from #20

We can add all the certificate finding and importing code from #20. (Don't worry @bernard-wagner, I can refactor this time!).

I'm no longer concerned about supporting importing certificates, because a WithAttributes variant can be supported.

Keep session pools as they are now

Looking at the comments in #9, I think everyone is now happy with the session pool configuration options. The move away from a singleton pattern should allow callers to set sensible values for each instance of crypto11. Comments very welcome on this, though.

panic when MaxSessions is 1

If MaxSessions is set to 1, the following panic occurs:

panic: invalid/out of range capacity

goroutine 1 [running]:
github.com/thales-e-security/pool.NewResourcePool(0xc00629c030, 0x0, 0x0, 0x0, 0x0, 0x0)
	/Users/cbroglie/.../vendor/github.com/thales-e-security/pool/resource_pool.go:87 +0x481
github.com/ThalesIgnite/crypto11.Configure(0xc0000caba0, 0x0, 0x0, 0x0)
	/Users/cbroglie/.../vendor/github.com/ThalesIgnite/crypto11/crypto11.go:327 +0x4bd

This is due to:

// We will use one session to keep state alive, so the pool gets maxSessions - 1
instance.pool = pool.NewResourcePool(instance.resourcePoolFactoryFunc, maxSessions-1, maxSessions-1, 0, 0)

FindKeyPair assumes that the public and private keys have the same labels (YubiKey)

I have a YubiKey 5 Nano, which I provision using yubico-piv-tool. For reasons, we generate our key pair with openssl, and then use yubico-piv-tool --slot=9a --action=import-certificate --input=path/to/public.crt and yubico-piv-tool --slot=9a --action=import-key --input=path/to/private.pem to import the public and private keys.

This CLI tool appears to set its own hard-coded labels; there might or might not be a Good Reason for that. Either way, it sets the labels of the private key and the public key to "Private key for PIV Authentication" and "Public key for PIV Authentication", respectively, though it keeps the object IDs of the keys equal. (This is documented here)

This vendor decision breaks the assumption in makeKeyPair that the private and public keys must have the same labels. This means that even if I happen to know that, for example, the ID of both keys is [1], calling ctx.FindKeyPair([]byte{1}, nil) will return nil, nil. (If I comment a few lines out of the findKeys helper earlier in that file, FindKeyPair is able to retrieve a valid key pair.) Unfortunately, this issue can be hard to diagnose because this block within FindKeyPairsWithAttributes swallows the error indicating that it couldn't find the public key.

If this is useful information, I'm using the PKCS11 shared module (libykcs11.dylib on my Mac) that comes bundled with yubico-piv-tool.

I'm fairly new to this PKCS#11 world, so I don't know if this is an overly-strict assumption, if the vendor (Yubico) made an unconventional decision, or if my team has an unconventional workflow.

go get github.com/ThalesIgnite/crypto11 exits with error

$ go get github.com/ThalesIgnite/crypto11
package golang.org/x/net/context: unrecognized import path "golang.org/x/net/context" (https fetch: Get https://golang.org/x/net/context?go-get=1: proxyconnect tcp: EOF)
package vitess.io/vitess/go/cache: unrecognized import path "vitess.io/vitess/go/cache" (https fetch: Get https://vitess.io/vitess/go/cache?go-get=1: proxyconnect tcp: EOF)
package vitess.io/vitess/go/sync2: unrecognized import path "vitess.io/vitess/go/sync2" (https fetch: Get https://vitess.io/vitess/go/sync2?go-get=1: proxyconnect tcp: EOF)
package vitess.io/vitess/go/timer: unrecognized import path "vitess.io/vitess/go/timer" (https fetch: Get https://vitess.io/vitess/go/timer?go-get=1: proxyconnect tcp: EOF)

Finding keys with non unique labels

The find functions assume only one result will be returned when querying a label, however, labels are not always unique.

As an example, it is possible that more than one keypair exists with the label "testing", but without knowing the ID you will always only get the first entry.

signer, err := crypto11.FindKeyPair(nil, []byte("testing")) 

Suggestion would be to either change the function to always return a slice, or to add functions that return a slice.

FindKeyPair(id []byte, label []byte) ([]Signer, error)
FindKeyPairs(label []byte) ([]Signer, error)

about demo of crypto11

could you please provide a demon for how to use crypto11 lib with tls standard lib。 my use case is , when i do the tls handshake with the tls library, how can i use crypto11 lib to validate if the certificate is signed and how to decrypt the received data

Add error return to PKCS11Session.Close (breaking change)

During the work for #24, it became clear that PKCS11Session.Close() ought to return an error but doesn't.

This would be a breaking change, so the interim fix was to add an alternative PKCS11Session.CloseSession() method that does return the error. The PKCS11Session.Close() was deprecated.

During the next breaking change release, we should either remove the Close() method or change its signature.

Add ...WithAttributes functions

As part of the v1 rewrite discussions (see #36), it was suggested to add ...WithAttributes variants of the key generation functions. This would allow generation of keys with arbitrary attributes.

I began implementing this in the with-attributes branch, but decided to pause the work. It's not clear if anyone using this library needs that functionality, so it feels premature to add it.

I'll leave this issue open as a reminder of the potential feature and to see if anyone has interest in it.

Requires cgo ?

Can the library only build with cgo?

$ export CGO_ENABLED=0
$ go get github.com/ThalesIgnite/crypto11
# github.com/ThalesIgnite/crypto11
../testgopath/src/github.com/ThalesIgnite/crypto11/crypto11.go:119:9: undefined: pkcs11.ObjectHandle

whereas if I set CGO_ENABLED=1 it works.

Is the CGO dependency within this project or one of the dependencies?

Generic FindKey

Currently the FindKey... functions require you to know in advance whether you're looking for a symmetric or asymmetric key. Instead they should be independent of key type.

How to use this library

Can you please make one demo, How exactly i can use this library. I am not sure how do i run this can you please help

Problem to run in i386 Ubuntu

go version go1.16 linux/386

fatal error: unexpected signal during runtime execution
[signal SIGSEGV: segmentation violation code=0x1 addr=0x3f8 pc=0x81435ad]

runtime stack:
runtime.throw(0x8183185, 0x2a)
/usr/local/go/src/runtime/panic.go:1117 +0x6a
runtime.sigpanic()
/usr/local/go/src/runtime/signal_unix.go:718 +0x23f

goroutine 1 [syscall]:
runtime.cgocall(0x8145a30, 0x8ce3ec4, 0x8160da0)
/usr/local/go/src/runtime/cgocall.go:154 +0x67 fp=0x8ce3eb0 sp=0x8ce3e98 pc=0x804d8b7
github.com/miekg/pkcs11._Cfunc_New(0x8ad1420, 0x0)
_cgo_gotypes.go:1246 +0x37 fp=0x8ce3ec4 sp=0x8ce3eb0 pc=0x8102ef7
github.com/miekg/pkcs11.New(0x818175d, 0x23, 0x0)
/home/administrador/work/pkg/mod/github.com/miekg/[email protected]/pkcs11.go:787 +0x82 fp=0x8ce3ee4 sp=0x8ce3ec4 pc=0x8103bf2
github.com/ThalesIgnite/crypto11.Configure(0x8cb20c0, 0x0, 0x0, 0x0)
/home/administrador/work/pkg/mod/github.com/!thales!ignite/[email protected]/crypto11.go:320 +0x12e fp=0x8ce3f68 sp=0x8ce3ee4 pc=0x813b23e
main.main()
/home/administrador/GoLuksTeste/teste4.go:35 +0x98 fp=0x8ce3fc8 sp=0x8ce3f68 pc=0x8143078
runtime.main()
/usr/local/go/src/runtime/proc.go:225 +0x253 fp=0x8ce3ff0 sp=0x8ce3fc8 pc=0x807cd63
runtime.goexit()
/usr/local/go/src/runtime/asm_386.s:1315 +0x1 fp=0x8ce3ff4 sp=0x8ce3ff0 pc=0x80a7561
exit status 2

ecdsa.unmarshalEcPoint expects ASN.1 data, but should be raw

Hello! I've encountered this issue while integrating the library with an HSM.

It seems that ecdsa.unmarshalEcPoint expects an ASN.1 encoded EC Point, but from what I've understood from PKCS#11 spec on CKA_EC_POINT (link, see section 2.3.3), it should only be a "DER-encoding of ANSI X9.62 ECPoint", which would not contain a ASN.1 header. The absence of the ASN.1 header makes ecdsa.unmarshalEcPoint fail when it calls asn1.Unmarshal.

I thank you in advance for your attention.

Symmetric MAC support

At present crypto11 only supports asymmetric keys. We would like to extend it to support symmetric ciphers too. This issue covers the relevant integrity interfaces and the issues that they raise.

  • For confidentiality, see #6.
  • For the implementation, see the experimental symmetric branch.

Interfaces

hash.Hash

crypto/hmac models HMACs as keyed hashes, using hash.Hash. There are three nontrivial methods:

  • Write(). Adds input data to the MAC.
  • Sum(). Retrieves the current MAC (or hash), but leaves it open for further updates.
  • Reset(). Resets the MAC state.

Creating MACs

We have two options for creating MACs using an HSM-protected key. One is to buffer all the data and do a C_SignInit followed by C_Sign. This will behave badly for large messages.

The other is to do a C_SignInit followed by multiple C_SignUpdate call on each Write() and a C_SignFinal call when Sum() or Reset() is called. The possibility of getting intermediate values with multiple calls to Sum() would be lost (I don't think this is a big deal).

Verifying MACs

Go offers no interface for verifying a MAC without having the MAC of the input data in hand within the Go process. In other words it's not possible to use the C_Verify.... functions. This isn't an insurmountable problem but it does mean that processes that only verify must still have sign permission on MAC keys, reducing some of the value of protecting such keys with an HSM.

References

PCKS11 new kmdata/local keydata files import

I'm attempting to use a shared drive for the kmdata/local folder and have multiple crypto microservices using the shared drive. However when one crypto service generates a new key (GenerateSecretKeyWithLabel), none of the other microservices will use it till they are each completely closed and brought back up. Is there a way to import the new generated kmdata local files to each of the other microservices that I'm missing?

Symmetric Ciphers

At present crypto11 only supports asymmetric keys. We would like to extend it to support symmetric ciphers too. This issue covers the relevant confidentiality interfaces and the issues that they raise.

  • For MACs, see #7.
  • For the implementation, see the experimental symmetric branch.

Throughout I will discuss encryption, with decryption left implicit, unless it's actually different in some relevant way.

Interfaces

cipher.Block

Encrypts or decrypts single blocks in a stateless way. This could be implemented by calling C_EncryptInit and C_Encrypt with a single block and a CKM_*_ECB mechanism.

The alternative approach of using C_EncryptInit once and multiple calls to C_EncryptUpdate will not work; this sequence requires a final C_EncryptFinal but the interface does not include any way for the caller to signal that it is finished.

cipher.BlockMode

Encrypts or decrypts multiple blocks in a stateful way. We would like to turn each call to CryptBlocks into a call to C_EncryptUpdate but again we face the issue that we have no idea when to call C_EncryptFinal. Without this our only option is to fall back to the native block modes (cipher.NewGCMEncrypter etc) with the cipher.Block discussed above. See below for a discussion of the performance impact.

cipher.AEAD

Encrypts or decrypts whole messages. This is much more promising, we can use the PKCS#11 functions in an idiomatic way and don't have to guess when to call C_EncryptFinal.

cipher.Stream

Encrypts or decrypts message fragments in a stateful way. Here we face the same issue as with BlockMode that we do not know when to call C_EncryptFinal , but without the slow implementation of cipher.Block to fall back on.

Performance Issues

On my experimental branch, the native version (using Go's native cipher.NewCBCEncrypter, so making a call to the HSM with each block) takes hundreds of times as long as an idiomatic implementation. For instance encrypting 64Kbyte messages using an emulated nShield HSM:

BenchmarkCBC/Native-8         	       2	 534374921 ns/op
BenchmarkCBC/Idiomatic-8      	    1000	   1155002 ns/op

Or using SoftHSM2:

BenchmarkCBC/Native-8         	      10	 127608341 ns/op
BenchmarkCBC/Idiomatic-8      	   10000	    384101 ns/op

Our issues could, superficially, be solved if cipher.Block, cipher.BlockMode and cipher.Stream had Close() methods. However, given that this isn't already true, deployment of a change could be painful, as all code using these would have to be updated to call the new method, before it could correctly use crypto11.

See golang/go#26787 for further discussion on this point.

References

Keys without id values not found

FindKeyPair only looks for keys that has an id value here
https://github.com/ThalesIgnite/crypto11/blob/master/keys.go#L122

In my case, the tpm-backed key has a label but not an id

# pkcs11-tool --module /usr/lib/x86_64-linux-gnu/libtpm2_pkcs11.so.1 --list-slots
Available slots:
Slot 0 (0x1): token1                          GOOG
  token label        : token1
  token manufacturer : GOOG
  token model        : vTPM
  token flags        : login required, rng, token initialized, PIN initialized
  hardware version   : 1.42
  firmware version   : 22.17
  serial num         : 0000000000000000
  pin min/max        : 0/128
Slot 1 (0x2):                                 GOOG
  token state:   uninitialized


# pkcs11-tool --module /usr/lib/x86_64-linux-gnu/libtpm2_pkcs11.so.1 --list-objects
Using slot 0 with a present token (0x1)
Public Key Object; RSA 2048 bits
  label:      keylabel1
  Usage:      encrypt, verify
  Access:     local

which still works with openssl with used with modules

/usr/lib/x86_64-linux-gnu/engines-1.1/libpkcs11.so
/usr/lib/x86_64-linux-gnu/libtpm2_pkcs11.so.1
export PKCS11_PUBLIC_KEY="pkcs11:model=vTPM;manufacturer=GOOG;serial=0000000000000000;token=token1;type=public;object=keylabel1?pin-value=mynewpin"
export PKCS11_PRIVATE_KEY="pkcs11:model=vTPM;manufacturer=GOOG;serial=0000000000000000;token=token1;type=private;object=keylabel1?pin-value=mynewpin"

$openssl rsa -engine pkcs11  -inform engine -in "$PKCS11_PUBLIC_KEY" -pubout
engine "pkcs11" set.
writing RSA key
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAny6DoDA8cuf44wWsfwKM
PxBcsmW0igzyqf5DmiD9fADrUOXO4suEfdTMMP1mT3laXd0yGfyzd98zKEIhPGwf
vJa2EaeuHrq51AdcT/AjQKXp4O2epZ4+/m0OfruBPieGwQDdx446KuLPDcR4ubs3
U71AtMWwYhBznqm2b5toEH2x6XKFBkFija+ddnKMpLhiMucj+DWnb09MKD6g19SL
an1A6op006HZWesW2uMSp5uoasRH2QNSrLyF+Nxv4rIIAXLUKxLitniQ+v2lbvtW
xMw2HMGsVynyqNWBBgqrmxtoTlQrIz8BAE1KzSKwTGVkmzNKQ7yAeOyG7e0tVlH2
dwIDAQAB
-----END PUBLIC KEY-----

$ echo "sig data" > "data.txt"
$ openssl rsa -engine pkcs11  -inform engine -in "$PKCS11_PUBLIC_KEY" -pubout -out pub.pem
engine "pkcs11" set.
writing RSA key

$ openssl pkeyutl -engine pkcs11 -keyform engine -inkey $PKCS11_PRIVATE_KEY -sign -in data.txt -out data.sig
engine "pkcs11" set.

$ openssl pkeyutl -pubin -inkey pub.pem -verify -in data.txt -sigfile data.sig
Signature Verified Successfully

i do not know if specs requires it or not but the fix used was to check for either

var errNoCkaIdOrLabel = errors.New("private key has no CKA_ID or CKA_LABEL")

	// Ensure the private key actually has a non-empty CKA_ID to match on
	if (id == nil || len(id) == 0) && (label == nil || len(label) == 0) {
		return nil, nil, errNoCkaIdOrLabel
	}

Tag first release

As this project has matured we should tag an initial release once symmetric crypto support has been accepted.

Error "unsupported key type: 2E37323100000000"

Background

I've deployed an application that uses the Crypto11 package with a Thales nShield Connect XC HSM. On a daily basis I see the following error with the value changing between each occurence:

unsupported key type: 2E37323100000000

Investigation

This error occurs when calling into FindKeyPair() to get a Signer object for any key (the key doesn't matter for the error). Debugging the PKCS#11 library I am able to see the call to GetAttributeValue returns 4 bytes:

2020-02-17 07:44:24 [3472] t1344: pkcs11: 0000091C >>   C_GetAttributeValue
2020-02-17 07:44:24 [3472] t1344: pkcs11: 0000091C >    hSession 0x0000091C
2020-02-17 07:44:24 [3472] t1344: pkcs11: 0000091C >    hObject 0x000004D0
2020-02-17 07:44:24 [3472] t1344: pkcs11: 00000000 D    NFC__hash_session 0x0000091C
2020-02-17 07:44:24 [3472] t1344: pkcs11: 0000091C D    hashmap lookup hash E795601D probe 29 step 97
2020-02-17 07:44:24 [3472] t1344: pkcs11: 0000091C D    lookup try hashmap[29] hash E795601D value 00000000038E6110
2020-02-17 07:44:24 [3472] t1344: pkcs11: 0000091C D    found hashmap[29] value 00000000038E6110
2020-02-17 07:44:24 [3472] t1344: pkcs11: 00000000 D    NFC__hash_object_handle 0x000004D0
2020-02-17 07:44:24 [3472] t1344: pkcs11: 0000091C D    hashmap lookup hash 073501C1 probe 193 step 1
2020-02-17 07:44:24 [3472] t1344: pkcs11: 0000091C D    lookup try hashmap[193] hash 073501C1 value 00000000040381D0
2020-02-17 07:44:24 [3472] t1344: pkcs11: 0000091C D    found hashmap[193] value 00000000040381D0
2020-02-17 07:44:24 [3472] t1344: pkcs11: 0000091C D    NFC__check_object_keyfile NFKM_Key.ident uae6bc6dededf16af9ef1a01f314072846e3649201
2020-02-17 07:44:24 [3472] t1344: pkcs11: 0000091C D    NFC__check_object_keyfile 0x000004D0 NFKM_findkey pkcs11 uae6bc6dededf16af9ef1a01f314072846e3649201
2020-02-17 07:44:24 [3472] t1344: pkcs11: 0000091C D    NFC__get_kmfile_modtime call
2020-02-17 07:44:24 [3472] t1344: pkcs11: 0000091C D    Existing object, check token object hasn't been changed on disk
2020-02-17 07:44:24 [3472] t1344: pkcs11: 0000091C D    NFC__get_kmfile_modtime call
2020-02-17 07:44:24 [3472] t1344: pkcs11: 0000091C D    NFC__GetAttributeValuePrivKey, ckktype 0x00000000
2020-02-17 07:44:24 [3472] t1344: pkcs11: 0000091C D    NFC__check_attributes class 0x00000003 (CKO_PRIVATE_KEY) type 0x00000000 ulCount 1
2020-02-17 07:44:24 [3472] t1344: pkcs11: 0000091C D    CKA_KEY_TYPE 0x00000100
2020-02-17 07:44:24 [3472] t1344: pkcs11: 0000091C D    NFC__check_attributes class 0x00000003 (CKO_PRIVATE_KEY) type 0x00000000 ulCount 1
2020-02-17 07:44:24 [3472] t1344: pkcs11: 0000091C <    CKA_KEY_TYPE length 4
2020-02-17 07:44:24 [3472] t1344: pkcs11: 0000091C <    rv 0x00000000 (CKR_OK)

In addition, I verified the length of the attributes[0].Value slice matches the PKCS#11 library, 4 bytes. So this leads me to the line of code that actually converts from a byte slice to a uint data type:

keyType := bytesToUlong(attributes[0].Value)
func bytesToUlong(bs []byte) (n uint) {
	return *(*uint)(unsafe.Pointer(&bs[0])) // ugh
}

Root Cause

Looking at the way bytesToUlong() is written it blindly casts the byte slice to an unsigned int. Ignoring the actual # of bytes within the slice. The issue occurs due to the way Go allocates and stores slices. Here is the data structure for a slice:

type SliceHeader struct {
    Data uintptr
    Len  int
    Cap  int
}

The root cause of the issue is the byte slice is allocated by the miekg/pkcs11 package with a capacity that matches the size of the data. If the size is less than C.sizeof_ulong then this unsafe pointer cast to a uint will grab the adjacent data in memory.

Going back to the key type value returned we can see the lower 4 bytes are 0x00000000 which is the value of pkcs11.CKK_RSA but the upper bytes look like a memory address . If we were to truncate the data to just the 4 bytes returned in the attributes[0].Value slice then we would have a valid key type.

Proposed fix

I've rewritten the bytesToUlong() function to take into account the # of bytes in the slice and truncating the data using a bitmask. Using a simple test I was able to confirm just the bytes present in the slice are returned (instead of the surrounding data in memory).

Here is the new implementation:

func bytesToUlong(bs []byte) (n uint) {
	sliceSize := len(bs)
	if sliceSize == 0 {
		return 0
	}

	value := *(*uint)(unsafe.Pointer(&bs[0]))
	if sliceSize > C.sizeof_ulong {
		return value
	}

	// truncate the value to the # of bits present in the byte slice since
	// the unsafe pointer will always grab/convert ULONG # of bytes
	var mask uint
	for i := 0; i < sliceSize; i++ {
		mask |= 0xff << uint(i * 8)
	}
	return value & mask
}

Here is the test code:

	ulongData := uint(0x33221100ddccbbaa)
	ulongSlice := ulongToBytes(ulongData)
	fmt.Printf("0: %X\n", bytesToUlong(ulongSlice[0:0]))
	fmt.Printf("1: %X\n", bytesToUlong(ulongSlice[0:1]))
	fmt.Printf("2: %X\n", bytesToUlong(ulongSlice[0:2]))
	fmt.Printf("3: %X\n", bytesToUlong(ulongSlice[0:3]))
	fmt.Printf("4: %X\n", bytesToUlong(ulongSlice[0:4]))
	fmt.Printf("5: %X\n", bytesToUlong(ulongSlice[0:5]))
	fmt.Printf("6: %X\n", bytesToUlong(ulongSlice[0:6]))
	fmt.Printf("7: %X\n", bytesToUlong(ulongSlice[0:7]))
	fmt.Printf("8: %X\n", bytesToUlong(ulongSlice[0:8]))

Running this code on the current implementation returns the following. Note: I had to remove the empty slice case as the current code asserts due to no length checks before dereferencing the first element in the slice :(

1: 33221100DDCCBBAA
2: 33221100DDCCBBAA
3: 33221100DDCCBBAA
4: 33221100DDCCBBAA
5: 33221100DDCCBBAA
6: 33221100DDCCBBAA
7: 33221100DDCCBBAA
8: 33221100DDCCBBAA

Here is the output for the update function:

0: 0
1: AA
2: BBAA
3: CCBBAA
4: DDCCBBAA
5: DDCCBBAA
6: 1100DDCCBBAA
7: 221100DDCCBBAA
8: 33221100DDCCBBAA

RFE: converting pkcs11.ObjectHandle to KeyPair

Currently, I use miekg/pkcs11 for working with certificates and part of your code for converting loaded privHandle and pubHandle (pkcs11.ObjectHandle) into crypto.Signer. It would be nice to have a function like:

func (c *Context) BuildKeyPair(pubHandle *pkcs11.ObjectHandle, privHandle *pkcs11.ObjectHandle) (Signer, error)

I may try to implement this function if it doesn't contradict the project's idea

Add possibility to find certificate chain

For TLS or other certificate verification operations we need not a single certificate but rather the certificate chain. The certificate chain is built by following the issuer->subject path. With the current API the certificate chain can be imported to PKCS11 device but there is no possibility to export it back from PKCS11 device as the current API supports finding only one specific certificate by id, by label, or by serial. It is impossible to find other chained certificates by issuer->subject path.

To solve the issue the find certificate API should be extended to allow finding the certificate by other criteria such as subject and key ID. Or new API that performs the finding certificate chain by issuer->subject path internally should be added to the library.

I've added the proposal PR #83 that implements the second approach (new API to find the certificate chain internally).

pools.NewResourcePool function signature divergence

Cannot initialize more than one slot with a PKCS DLL

Problem
I have a usecase where I need to access 2 slots ("accelerator" and an OCS) within the same application (a long running service) to be able to access module protected (any public objects) keys and private (OCS protected) keys.

Root Cause
The issue is due to the way the PKCS11 module loads a given DLL as it does not provide any guards to prevent a user from calling New() multiple times for the same DLL.

Possible Fixes
The PKCS11 module already solved this issue in their P11 submodule. We can do one of the following:

  1. We can mirror what the PKCS11 P11 module does and provide a singleton object that tracks a Module which will only load a DLL file once.

  2. Just use the P11 module for the configuration process

Option 1 seems like the simplest solution and would require the least amount of code changes. I am willing to put up a pull request but wanted to make sure we aligned on the best solution.

Regression in session handling for PIN protected enclaves

  1. _, err := crypto11.Configure(&crypto11.PKCS11Config{Path: config.HSMTokenPath, TokenLabel: config.TokenLabel, Pin: config.HSMPin}) is doing fine
  2. Inside of the Configure, the
func setupSessions(ctx* pkcs11.Ctx, slot uint) error {
	return pool.PutIfAbsent(slot, pools.NewResourcePool(
		func() (pools.Resource, error) {
			return newSession(ctx, slot)
		},
		maxSessions,
		maxSessions,
		idleTimeout,
	))
}

sets an idle timeout of 30 seconds for the sessions to be wiped out off the pool.
3. Then Configure uses withSession to login for currently created session.
4. After 30 seconds pool closes a session OR after a 30 seconds of used sessions pool closes them.
5. After that user is unable to work with the library because for all further operations it returns either pkcs11: 0x82: CKR_OBJECT_HANDLE_INVALID or pkcs11: 0x101: CKR_USER_NOT_LOGGED_IN.

More coherent slot support

Currently:

  • Via Configure you can specify a token by label or serial, but you can't get the slot number where that token resides back out.
  • You can generate a key on the configured token, but you can't specify the new key's label or ID.
  • You can generate a key on a numbered slot, but you have to find the slot yourself.
  • You can generate a key on a specific session, but you have to open and close the session yourself.

None of this is insurmountable but it does leave a lot of work for the callers of the key generation APIs.

(The situation is better for finding keys, but see #16).

I propose the following changes:

  1. GenerateDSAKeyPair, GenerateECDSAKeyPair, GenerateRSAKeyPair and GenerateSecretKey all get id and label arguments, consistent with the other Generate... functions. This is an incompatible change, but since the functions are almost useless I don't foresee much downstream fallout.
  2. withSession will become WithSession, allowing applications to play nicely with the library's own session management.
  3. WithSession will take a PIN argument rather than using the configured PIN. The present callers of withSession will use the configured PIN.
  4. The logic in Configure to find a slot given a token label/id will be exposed as a public function.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.