Support for Vendor Defined Key Types and mechanisms
Is there any plan to add support to vendor defined key types and mechanisms? Right now the openssl engine rejects any key that is not RSA or EC. Is this really necessary? You could simply pass plainly the CKK_VENDOR_DEFINED specific value to C_Sign (for example).
Maybe this should be more flexible looking for future grand modifications in the cryptography landscape like the one we are living with PQC.
Do you have any suggestions for how we should test this feature?
What I am using is the Quantum Protect simulator from Utimaco, but it's based on propietary software (it's free to use). I don't know any other pkcs11 providers with Vendor Defined Mechanisms, but I can try to research on it if the first option isn't enough.
Google for "Quantum" "PKCS11" The Google AI points at articles from wolfssl and IBM. Also listed some work being done by others including osias-open.org that maintains the PKCS11 standards.
Hey folks,
If you've found some software that's free to use and works well, feel free to update your replies with links to its homepage or download page. I can always search by name, but it's helpful to know I'm looking at the exact same thing you tried and recommend.
BTW, an AI once recommended me a library that never existed—so yeah, links are appreciated.
P.S. Libp11 is mainly used to with OpenSSL engine or provider so it cannot do much without OpenSSL support for any mechanisms not supported by OpenSSL.
The OpenSC pkcs11-tool is a utility and example for how to load and call PKCS11 modules So if you can find a Pkcs11 module with some PQC mechanism even if vendor provided, that would be a place to start.
https://github.com/OpenSC/OpenSC/blob/master/src/tools/pkcs11-tool.c#L357C2-L357C3 shows how to enter a vendor CKM as hex.
If you've found some software that's free to use and works well, feel free to update your replies with links to its homepage I will let @sandevins do the hard work, as it looks there is not much out there, but a lot of talk. The Oasis community appears to be trying to define PKCS11 mechanisms for PQC, rather the leaving it up to every vendor.
I just responded as libp11 can not do anything without OpenSSL and a PKCS11 module that supports some PQC mech.
I looked into our code, and I'm not so sure anymore. I think it might be possible to implement private key operations on EVP_PKEY objects without special OpenSSL support—even for keys that aren't RSA, DH, or EC. We would just need a suitable PKCS#11 module to test with.
If @sandevins requested support for CKK_VENDOR_DEFINED, I assume he already has a PKCS#11 module that provides such keys.
https://openssl-library.org/post/2025-04-08-openssl-35-final-release/
https://developer.ibm.com/tutorials/awb-quantum-safe-openssl/#step-4-install-open-quantum-safe-provider-for-openssl-35 (from February)
Google: "PKCS11" module with "PQC" Shows even more info.
Thank you for the links. How exactly are OpenSSL providers useful for testing CKK_VENDOR_DEFINED functionality of PKCS#11 modules?
Thank you for the links. How exactly are OpenSSL providers useful for testing CKK_VENDOR_DEFINED functionality of PKCS#11 modules?
Probably, not usefully, unless some PKCS11 module is implementing one of the OpenSSL PQC algorithms and Oasis has not defined a CKM_ for it yet along with all the other PKCS11 stuff.
Looks like OpenSSL supports some PQC algorithms in software as a provider. Now @sandevins needs to find a PKCS11 module that supports ona mechanisms. (I am assuming @sandevins wants to use libp11 because it uses pkcs11 with OpenSSL to use keys on a smart card or HSM where so crypto is done smart card or HSM.) The only time I use libp11 is to sign a CSR with a key generated on a smart card and not exportable. If @sandevins is willing to use one of the OpenSSL supported PQC in software and does not need to use PKCS11, then there is no need libp11.
Libp11 is the last hurdle in the race, that helps OpenSSL to use hardware for keys and crypto.
Indeed, I need to do cryptographic operations inside an HSM through a PKCS#11 provider. That is the reason I need libp11. The HSM supports PQC algorithms, OpenSSL supports PQC algorithms in its version 3.5.0, but there is still no support for PQC algorithms in the PKCS#11 standard.
Indeed, I need to do cryptographic operations inside an HSM through a PKCS#11 provider. That is the reason I need libp11. The HSM supports PQC algorithms, OpenSSL supports PQC algorithms in its version 3.5.0, but there is still no support for PQC algorithms in the PKCS#11 standard.
Great. Do you have such a PKCS#11 module that we could use for testing of the requested feature? Send us the software (with hardware if required).
@sandevins If you have the specs for the device, it might be possible to add a driver to https://github.com/OpenSC/OpenSC but there are PKCS11 issues for example what is the format for a PQC Public key, What OIDs are assigned, And you should try and follow any guidelines from Oasis. Adding a new mechanism to OpenSC could be done, but it is not easy. (I add the EC mechanism to OpenSC in the early 2000's for PIV card, but PKCS11 specs included EC, making it easier.)
Post you question on https://github.com/OpenSC/OpenSC, maybe someone is already looking at the problem.
It is not clear if you are doing this on your own, or you are employed by the HSM vendor. If you can make the device available to OpenSC developer, (not me) that would also help.
What I am using right now is the Utimaco HSM Simulator along with its PKCS#11 Connector
https://support.hsm.utimaco.com/hsm-simulator
I am not a direct employee of Utimaco so I cannot provide the hardware but that simulator can be used freely.
I am surprised that the addition of mechanisms is not a direct task, as libp11 should only redirect the mechanism and keytype as it is to the provider it's using, right?
So this simulator has a pkcs11 module? i.e. is that the connector?
If so can you run these commands using the OpenSC pkcs11-tool:
pkcs11-tool --module path-to-simulator-module -I pkcs11-tool --module path-to-simulator-module -L pkcs11-tool --module path-to-simulator-module -M pkcs11-tool --module path-to-simulator-module -O
PKCS11SPY=/usr/lib/pkcs11/opensc-pkcs11.so
PKCS11SPY_OUTPUT=logfile
pkcs11-tool --module path-to-simulator-module --list-interfaces
I believe with -M pkcs11-tool may list an M which it does not know. Win -O it may list some pubkeys and may have trouble if not RSA or EC.
What would help to see the PKCS11 calls is the OpenSC SPY https://github.com/OpenSC/OpenSC/wiki/Using-OpenSC#pkcs-11-spy
for example:
export PKCS11SPY=path-to-simulator-module
export PKCS11SPY_OUTPUT=logfile
pkcs11-tool --module pkcs11-spy.so -M
The log file will have all the calls to pkcs11 module with in and out values.
I am surprised that the addition of mechanisms is not a direct task, as libp11 should only redirect the mechanism and keytype as it is to the provider it's using, right?
No, libp11 also types to use OpenSSL EVP_KEYS and add a hook to have OpenSSL call libp11 when it needs to d a crypto operation to use the external PKCS11 module. As @mtrojnar pointed out it might be possible to do this if the PQC mechanisms are supported by OpenSSL version 3.5.
There is not a lot of information with this output as the identifiers are defined by the backend crypto module instead of the pkcs11 module. In any case I will post here the information.
This is the pkcs11-tool -M
pkcs11-tool --module /usr/lib/x86_64-linux-gnu/pkcs11/pkcs11-spy.so -M
Using slot 0 with a present token (0x0)
Supported mechanisms:
DES-ECB, keySize={8,24}, hw, encrypt, decrypt, wrap, unwrap
DES3-ECB, keySize={8,24}, hw, encrypt, decrypt, wrap, unwrap
DES-CBC, keySize={8,24}, hw, encrypt, decrypt, wrap, unwrap
DES3-CBC, keySize={8,24}, hw, encrypt, decrypt, wrap, unwrap
DES-CBC-PAD, keySize={8,24}, hw, encrypt, decrypt, wrap, unwrap
DES3-CBC-PAD, keySize={8,24}, hw, encrypt, decrypt, wrap, unwrap
mechtype-0x80003001, keySize={8,8}, hw, wrap, unwrap
AES-ECB, keySize={16,32}, hw, encrypt, decrypt, wrap, unwrap
AES-CBC, keySize={16,32}, hw, encrypt, decrypt, wrap, unwrap
AES-CBC-PAD, keySize={16,32}, hw, encrypt, decrypt, wrap, unwrap
AES-CTR, keySize={16,32}, hw, encrypt, decrypt
mechtype-0x80003002, keySize={16,16}, hw, wrap, unwrap
mechtype-0x2104, keySize={16,32}, hw, encrypt, decrypt, wrap, unwrap
MD5, hw, digest
SHA-1, hw, digest
SHA224, hw, digest
SHA256, hw, digest
SHA384, hw, digest
SHA512, hw, digest
SHA3-224, hw, digest
SHA3-256, hw, digest
SHA3-384, hw, digest
SHA3-512, hw, digest
RIPEMD160, hw, digest
DES-MAC, keySize={8,24}, hw, sign, verify
DES-MAC-GENERAL, keySize={8,24}, hw, sign, verify
DES3-MAC, keySize={8,24}, hw, sign, verify
DES3-MAC-GENERAL, keySize={8,24}, hw, sign, verify
mechtype-0x80000135, keySize={8,24}, hw, sign, verify
AES-MAC, keySize={16,32}, hw, sign, verify
AES-MAC-GENERAL, keySize={16,32}, hw, sign, verify
AES-CMAC, keySize={16,32}, hw, sign, verify
mechtype-0x80000136, keySize={16,32}, hw, sign, verify
mechtype-0x108E, keySize={16,32}, hw, sign, verify
SHA-1-HMAC, keySize={1,1024}, hw, sign, verify
SHA-1-HMAC-GENERAL, keySize={1,1024}, hw, sign, verify
MD5-HMAC, keySize={1,1024}, hw, sign, verify
MD5-HMAC-GENERAL, keySize={1,1024}, hw, sign, verify
RIPEMD160-HMAC, keySize={1,1024}, hw, sign, verify
RIPEMD160-HMAC-GENERAL, keySize={1,1024}, hw, sign, verify
SHA256-HMAC, keySize={1,1024}, hw, sign, verify
mechtype-0x252, keySize={1,1024}, hw, sign, verify
SHA384-HMAC, keySize={1,1024}, hw, sign, verify
mechtype-0x262, keySize={1,1024}, hw, sign, verify
SHA512-HMAC, keySize={1,1024}, hw, sign, verify
mechtype-0x272, keySize={1,1024}, hw, sign, verify
SHA224-HMAC, keySize={1,1024}, hw, sign, verify
mechtype-0x257, keySize={1,1024}, hw, sign, verify
SHA3-256-HMAC, keySize={1,1024}, hw, sign, verify
mechtype-0x2B2, keySize={1,1024}, hw, sign, verify
SHA3-384-HMAC, keySize={1,1024}, hw, sign, verify
mechtype-0x2C2, keySize={1,1024}, hw, sign, verify
SHA3-512-HMAC, keySize={1,1024}, hw, sign, verify
mechtype-0x2D2, keySize={1,1024}, hw, sign, verify
SHA3-224-HMAC, keySize={1,1024}, hw, sign, verify
mechtype-0x2B7, keySize={1,1024}, hw, sign, verify
RSA-PKCS, keySize={512,8192}, hw, encrypt, decrypt, sign, sign_recover, verify, verify_recover, wrap, unwrap
RSA-X-509, keySize={512,8192}, hw, encrypt, decrypt, sign, sign_recover, verify, verify_recover
mechtype-0x80000003, keySize={512,4096}, hw, sign
RSA-X9-31, keySize={512,8192}, hw, sign, verify
RSA-PKCS-OAEP, keySize={512,8192}, hw, encrypt, decrypt, wrap, unwrap
RSA-PKCS-PSS, keySize={512,8192}, hw, sign, verify
mechtype-0x80000001, keySize={512,4096}, hw, sign
SHA1-RSA-PKCS, keySize={512,8192}, hw, sign, verify
SHA1-RSA-PKCS-PSS, keySize={512,8192}, hw, sign, verify
SHA1-RSA-X9-31, keySize={512,8192}, hw, sign, verify
SHA224-RSA-PKCS, keySize={512,8192}, hw, sign, verify
SHA224-RSA-PKCS-PSS, keySize={512,8192}, hw, sign, verify
SHA256-RSA-PKCS, keySize={512,8192}, hw, sign, verify
SHA256-RSA-PKCS-PSS, keySize={512,8192}, hw, sign, verify
SHA384-RSA-PKCS, keySize={512,8192}, hw, sign, verify
SHA384-RSA-PKCS-PSS, keySize={512,8192}, hw, sign, verify
SHA512-RSA-PKCS, keySize={512,8192}, hw, sign, verify
SHA512-RSA-PKCS-PSS, keySize={512,8192}, hw, sign, verify
SHA3-224-RSA-PKCS, keySize={512,8192}, hw, sign, verify
SHA3-224-RSA-PKCS-PSS, keySize={512,8192}, hw, sign, verify
SHA3-256-RSA-PKCS, keySize={512,8192}, hw, sign, verify
SHA3-256-RSA-PKCS-PSS, keySize={512,8192}, hw, sign, verify
SHA3-384-RSA-PKCS, keySize={512,8192}, hw, sign, verify
SHA3-384-RSA-PKCS-PSS, keySize={512,8192}, hw, sign, verify
SHA3-512-RSA-PKCS, keySize={512,8192}, hw, sign, verify
SHA3-512-RSA-PKCS-PSS, keySize={512,8192}, hw, sign, verify
RIPEMD160-RSA-PKCS, keySize={512,8192}, hw, sign, verify
MD5-RSA-PKCS, keySize={512,8192}, hw, sign, verify
ECDSA, keySize={112,571}, hw, sign, verify, EC F_P, EC F_2M, EC parameters, EC OID, EC uncompressed, EC compressed
ECDSA-SHA1, keySize={112,571}, hw, sign, verify, EC F_P, EC F_2M, EC parameters, EC OID, EC uncompressed, EC compressed
ECDSA-SHA224, keySize={112,571}, hw, sign, verify, EC F_P, EC F_2M, EC parameters, EC OID, EC uncompressed, EC compressed
mechtype-0x80001042, keySize={112,571}, hw, sign, verify, EC F_P, EC F_2M, EC parameters, EC OID, EC uncompressed, EC compressed
ECDSA-SHA256, keySize={112,571}, hw, sign, verify, EC F_P, EC F_2M, EC parameters, EC OID, EC uncompressed, EC compressed
mechtype-0x80001043, keySize={112,571}, hw, sign, verify, EC F_P, EC F_2M, EC parameters, EC OID, EC uncompressed, EC compressed
ECDSA-SHA384, keySize={112,571}, hw, sign, verify, EC F_P, EC F_2M, EC parameters, EC OID, EC uncompressed, EC compressed
mechtype-0x80001044, keySize={112,571}, hw, sign, verify, EC F_P, EC F_2M, EC parameters, EC OID, EC uncompressed, EC compressed
ECDSA-SHA512, keySize={112,571}, hw, sign, verify, EC F_P, EC F_2M, EC parameters, EC OID, EC uncompressed, EC compressed
mechtype-0x80001045, keySize={112,571}, hw, sign, verify, EC F_P, EC F_2M, EC parameters, EC OID, EC uncompressed, EC compressed
ECDSA-SHA3-224, keySize={112,571}, hw, sign, verify, EC F_P, EC F_2M, EC parameters, EC OID, EC uncompressed, EC compressed
ECDSA-SHA3-256, keySize={112,571}, hw, sign, verify, EC F_P, EC F_2M, EC parameters, EC OID, EC uncompressed, EC compressed
ECDSA-SHA3-384, keySize={112,571}, hw, sign, verify, EC F_P, EC F_2M, EC parameters, EC OID, EC uncompressed, EC compressed
ECDSA-SHA3-512, keySize={112,571}, hw, sign, verify, EC F_P, EC F_2M, EC parameters, EC OID, EC uncompressed, EC compressed
mechtype-0x8000104A, keySize={112,571}, hw, sign, verify
mechtype-0x80001401, keySize={112,571}, hw, sign
mechtype-0x80001101, keySize={112,571}, hw
mechtype-0x80001201, keySize={112,571}, hw, encrypt, decrypt
mechtype-0x800D0001, keySize={112,571}, hw, sign, verify, EC F_P, EC F_2M, EC parameters, EC OID, EC uncompressed, EC compressed
EDDSA, keySize={256,456}, hw, sign, verify, EC F_P, EC F_2M, EC parameters, EC OID, EC uncompressed, EC compressed
DSA, keySize={512,4096}, hw, sign, verify
DSA-SHA1, keySize={512,4096}, hw, sign, verify
DSA-SHA224, keySize={512,4096}, hw, sign, verify
mechtype-0x80002042, keySize={512,4096}, hw, sign, verify
DSA-SHA256, keySize={512,4096}, hw, sign, verify
mechtype-0x80002043, keySize={512,4096}, hw, sign, verify
DSA-SHA384, keySize={512,4096}, hw, sign, verify
mechtype-0x80002044, keySize={512,4096}, hw, sign, verify
DSA-SHA512, keySize={512,4096}, hw, sign, verify
mechtype-0x80002045, keySize={512,4096}, hw, sign, verify
mechtype-0x18, keySize={512,4096}, hw, sign, verify
mechtype-0x19, keySize={512,4096}, hw, sign, verify
mechtype-0x1A, keySize={512,4096}, hw, sign, verify
mechtype-0x1B, keySize={512,4096}, hw, sign, verify
mechtype-0x8000204A, keySize={512,4096}, hw, sign, verify
DES-KEY-GEN, keySize={8,8}, hw, generate
DES2-KEY-GEN, keySize={16,16}, hw, generate
DES3-KEY-GEN, keySize={24,24}, hw, generate
AES-KEY-GEN, keySize={16,32}, hw, generate
GENERIC-SECRET-KEY-GEN, keySize={8,8192}, hw, generate
RSA-PKCS-KEY-PAIR-GEN, keySize={512,8192}, hw, generate_key_pair
RSA-X9-31-KEY-PAIR-GEN, keySize={512,8192}, hw, generate_key_pair
ECDSA-KEY-PAIR-GEN, keySize={112,521}, hw, generate_key_pair, EC F_P, EC F_2M, EC parameters, EC OID, EC uncompressed, EC compressed
EC-EDWARDS-KEY-PAIR-GEN, keySize={256,456}, hw, generate_key_pair, EC F_P, EC F_2M, EC parameters, EC OID, EC uncompressed, EC compressed
DSA-KEY-PAIR-GEN, keySize={512,4096}, hw, generate_key_pair
DH-PKCS-KEY-PAIR-GEN, keySize={512,4096}, hw, generate_key_pair
X9-42-DH-KEY-PAIR-GEN, keySize={512,4096}, hw, generate_key_pair
DSA-PARAMETER-GEN, keySize={512,4096}, hw, generate
X9-42-DH-PARAMETER-GEN, keySize={512,4096}, hw, generate
DES-ECB-ENCRYPT-DATA, hw, derive
DES3-ECB-ENCRYPT-DATA, hw, derive
DES-CBC-ENCRYPT-DATA, hw, derive
DES3-CBC-ENCRYPT-DATA, hw, derive
AES-ECB-ENCRYPT-DATA, hw, derive
AES-CBC-ENCRYPT-DATA, hw, derive
SHA1-KEY-DERIVATION, hw, derive
mechtype-0x396, hw, derive
mechtype-0x393, hw, derive
mechtype-0x394, hw, derive
mechtype-0x395, hw, derive
mechtype-0x398, hw, derive
mechtype-0x397, hw, derive
mechtype-0x399, hw, derive
mechtype-0x39A, hw, derive
MD5-KEY-DERIVATION, hw, derive
ECDH1-DERIVE, keySize={112,571}, hw, derive
ECDH1-COFACTOR-DERIVE, keySize={112,571}, hw, derive
DH-PKCS-DERIVE, keySize={512,4096}, hw, derive
X9-42-DH-DERIVE, keySize={512,4096}, hw, derive
CONCATENATE-BASE-AND-DATA, hw, derive
CONCATENATE-DATA-AND-BASE, hw, derive
CONCATENATE-BASE-AND-KEY, hw, derive
EXTRACT-KEY-FROM-KEY, hw, derive
XOR-BASE-AND-DATA, hw, derive
AES-GCM, keySize={16,32}, hw, encrypt, decrypt, wrap, unwrap
mechtype-0x1088, keySize={16,32}, hw, encrypt, decrypt, wrap, unwrap
AES-KEY-WRAP, keySize={16,32}, hw, encrypt, decrypt, wrap, unwrap
mechtype-0x210A, keySize={16,32}, hw, encrypt, decrypt, wrap, unwrap
mechtype-0x210B, keySize={16,32}, hw, encrypt, decrypt, wrap, unwrap
And this is the relevant information of pkcs11-tool -O
pkcs11-tool --module /usr/lib/x86_64-linux-gnu/pkcs11/pkcs11-spy.so --login -O
Using slot 0 with a present token (0x0)
Logging in to "PQCToken".
Please enter User PIN:
Private Key Object; RSA
label: TestRSAKey
Usage: decrypt, sign, signRecover, unwrap
Access: sensitive, always sensitive, never extractable, local
Unique ID: >D�(BϏ�T����
Public Key Object; unknown key algorithm 2147483648
label: TestMLDSAKey2
ID: 3030
Usage: encrypt, verify, verifyRecover, wrap
Access: local
Unique ID: J)�ת�J��k���^q
Public Key Object; RSA 2048 bits
label: RSA Public Key
Usage: encrypt, verify, verifyRecover, wrap
Access: local
Unique ID: c���5eH����S���
Public Key Object; RSA 2048 bits
label: TestRSAKey
Usage: encrypt, verify, verifyRecover, wrap
Access: local
Unique ID: ���y�"Jʗ����ۏ
Private Key Object; unknown key algorithm 2147483648
label: TestMLDSAKey
warning: PKCS11 function C_GetAttributeValue(SIGN_RECOVER) failed: rv = CKR_ATTRIBUTE_TYPE_INVALID (0x12)
Usage: decrypt, sign, unwrap
warning: PKCS11 function C_GetAttributeValue(ALWAYS_AUTHENTICATE) failed: rv = CKR_ATTRIBUTE_TYPE_INVALID (0x12)
Access: sensitive, always sensitive, never extractable, local
Unique ID: #SH�]�G�H�
���
Private Key Object; unknown key algorithm 2147483648
label: TestMLDSAKey2
ID: 3131
warning: PKCS11 function C_GetAttributeValue(SIGN_RECOVER) failed: rv = CKR_ATTRIBUTE_TYPE_INVALID (0x12)
Usage: decrypt, sign, unwrap
warning: PKCS11 function C_GetAttributeValue(ALWAYS_AUTHENTICATE) failed: rv = CKR_ATTRIBUTE_TYPE_INVALID (0x12)
Access: sensitive, always sensitive, never extractable, local
���[�ȹ�e ID: |Z���8F
Private Key Object; RSA
label: RSA Private Key
Usage: decrypt, sign, signRecover, unwrap
Access: sensitive, always sensitive, never extractable, local
Unique ID: 'K�O�F��� l�(�
Public Key Object; unknown key algorithm 2147483648
label: TestMLDSAKey
Usage: encrypt, verify, verifyRecover, wrap
Access: local
Unique ID: [�d]`�Ny�i�}��
And this is the log of what happens when trying to sign using openssl.
openssl req -engine pkcs11 -new -key "pkcs11:token=PQCToken;object=TestMLDSAKey" -verbose -keyform engine -out TestMLDSA.csr
Engine "pkcs11" set.
Using configuration from /etc/utimaco/openssl/openssl.cfg
PKCS#11: Initializing the module: /usr/lib/x86_64-linux-gnu/pkcs11/pkcs11-spy.so
Found 10 slots
- [0] [email protected] - CLUSTER_ login (PQCToken)
- [1] [email protected] - CLUSTER_ uninitialized, login (no label)
- [2] [email protected] - CLUSTER_ uninitialized, login (no label)
- [3] [email protected] - CLUSTER_ uninitialized, login (no label)
- [4] [email protected] - CLUSTER_ uninitialized, login (no label)
- [5] [email protected] - CLUSTER_ uninitialized, login (no label)
- [6] [email protected] - CLUSTER_ uninitialized, login (no label)
- [7] [email protected] - CLUSTER_ uninitialized, login (no label)
- [8] [email protected] - CLUSTER_ uninitialized, login (no label)
- [9] [email protected] - CLUSTER_ uninitialized, login (no label)
Searching slots without login for an initialized token containing private key label=TestMLDSAKey
Found slot: [email protected] - CLUSTER_0000 - SLOT_0000
Found slot: [email protected] - CLUSTER_0000 - SLOT_0000
Found initialized token: PQCToken
No private key found.
Searching slots with login for an initialized token containing private key label=TestMLDSAKey
Found slot: [email protected] - CLUSTER_0000 - SLOT_0000
Found initialized token: PQCToken
Found slot: [email protected] - CLUSTER_0000 - SLOT_0000
Found initialized token: PQCToken
Enter PKCS#11 token PIN for PQCToken:
Unable to enumerate private keys
Searching slots without login for an uninitialized token containing private key label=TestMLDSAKey
Found slot: [email protected] - CLUSTER_0000 - SLOT_0000
Skipped initialized token: [email protected] - CLUSTER_0000 - SLOT_0000
No matching slots found
The private key was not found at: pkcs11:token=PQCToken;object=TestMLDSAKey
PKCS11_get_private_key returned NULL
Could not find private key from org.openssl.engine:pkcs11:pkcs11:token=PQCToken;object=TestMLDSAKey
80BB4717A37F0000:error:40000065:pkcs11 engine:ERR_ENG_error:object not found:eng_back.c:198:
80BB4717A37F0000:error:13000080:engine routines:ENGINE_load_private_key:failed loading private key:crypto/engine/eng_pkey.c:79:
This is the log from pkcs11_spy:
This is the log from the provider:
As you can see the identifier is 0x80000000
Is there any additional information I can provide that may result useful? The important keys are the TestMLDSA.
And for more information this is the output of a custom application I have for creating the keys.
PKCS11_SPY:
Provider log:
PKCS11 also comes with header files as listed in their dos for example: https://docs.oasis-open.org/pkcs11/pkcs11-spec/v3.1/csd01/include/pkcs11-v3.1/
And https://github.com/oasis-tcs/pkcs11 appears to have some not yet released versions of the header files.
Any vendor who wants to add "vendor provided" definitions should also document the mappings in some header file for example see: https://github.com/OpenSC/OpenSC/blob/master/src/pkcs11/pkcs11-opensc.h Here to make the"vendor provided" definitions somewhat unique, OpenSC (following Netscape example) used the first few bytes as CK*_VENDOR_DEFINED | "OSC"`. (From the output you sent, looks like Utimaco made no attempt at uniqueness.)
Utimaco should also have some header files with some defines that give a clue to their meaning. See what you can find.
OpenSC and SPY use the mapping that are understood from current Oasis standards. If an unknown value is found they will print it in hex as you can see. pkcs11-tool can also accept some mechanisms as hex.
Looks like the IETF has been active too: https://github.com/IETF-Hackathon/pqc-certificates/blob/master/docs/oid_mapping.md
A certificate contains the public key (SPKI) with OIDs Hopefully Utimaco is following the IETF when generating certificates. You can also try generating a key and then get the public key) then use OpenSSL asn1parse or https://lapo.it/asn1js/ to parse the ASN.1.
This goes with OpenSSL too, see if they are using the IETF OIDs.
This discussion should be on OpenSC.
You may find this interesting too: https://www.redhat.com/en/blog/post-quantum-cryptography-red-hat-enterprise-linux-10
The thing is that the PKCS11 Connector doesnt have context of those identifiers, is a thing managed by the backend HSM (the PQC support is handled by a module installed on the HSM). OpenSSL 3.5.0 supports the keys and mechanisms (it's MLDSA). When calling C_Sign, the mechanisms that are defined are the following.
#define MECH_ML_VDM ((0xC << 28) | (0xA5 << 16))
#define MECH_VDM_SIGN (0x3 << 12)
#define CKM_MECH_MLDSA_SIGN (MECH_ML_VDM | MECH_VDM_SIGN | 1)
With that it should be enough to call C_Sign, right? I can try to implement that in a fork before a standard comes out.
Where did you find the defines for MECH_ML_VDM, MECH_VDM_SIGN and CKM_MECH_MLDSA_SIGN?
Only CKM_MECH_MLDSA_SIGN looks like a PKCS11 mechanism.
PKCS11 Mechanisms are usually assigned sequentially. And (MECH_ML_VDM | MECH_VDM_SIGN | 1) would be ~~0x0CA50301 which as far as I can tell is not a standard mechanism and is not a vendor provided one, which would start with the high order bit set.~~ 0xC0A53001 which has the vendor provided bit set.
in pkcs11_spy_createkey.log from above some comments:
Looks like the PKCS11 module is treating the size of pMechanism a CK_ULONG
PKCS11 3.1 says:
/* an unsigned value, at least 32 bits long */
typedef unsigned long int CK_ULONG;
and it looks like on your system that is 64 bits. So the pMechanism->type = 0xFFFFFFFFC0A50001
is sign extended to 64 bits, but it should be unsigned and be only vendor provided mechanism is 0xC0A50001.
PKCS11 applications need to associate certs, pubkeys and private keys, which is usually done by using the same CKA_ID for all three. If not some use a common label for the three. The GenerateKeyPair below uses different CKA_ID and CKA_LABEL attributes. Thi cold be a problem when trying to use them. Libp11 is the application calling the pkcs11 module and depends on matching the two PKCS11 keys as OpenSSL has both the public and private keys in a single OpenSSL EVP_KEY.
Libp11 inserts callbacks into the EVP_PKEY so when OpenSSL requests a crypto operation libp11 calls PKCS11 module to do the crypto and then passes the results back to OpenSSL. This requires libp11 to have some knowledge of the type of key and supported by OpenSSL.
13: C_GenerateKeyPair
P:409; T:0x140530460501824 2025-07-09 06:40:05.137
[in] hSession = 0x2
[in] pMechanism->type = 0xFFFFFFFFC0A50001
[in] pMechanism->pParameter[ulParameterLen] 000055d809e5ceb0 / 10
00000000 00 00 00 01 00 00 00 03 00 00 ..........
[in] pPublicKeyTemplate[6]:
CKA_CLASS CKO_PUBLIC_KEY
CKA_KEY_TYPE Value FFFFFFFFC0A50001 not found for type CK_KEY_TYPE
CKA_TOKEN True
CKA_VERIFY True
CKA_LABEL 00007ffc6765a5b0 / 15
4D795465 73744D4C 4453415F 707562
M y T e s t M L D S A _ p u b
CKA_ID 00007ffc6765a5ac / 2
00000000 30 30 00
[in] pPrivateKeyTemplate[9]:
CKA_CLASS CKO_PRIVATE_KEY
CKA_KEY_TYPE Value FFFFFFFFC0A50001 not found for type CK_KEY_TYPE
CKA_TOKEN True
CKA_SIGN True
CKA_PRIVATE True
CKA_SENSITIVE True
CKA_EXTRACTABLE False
CKA_LABEL 00007ffc6765a6b0 / 15
4D795465 73744D4C 4453415F 707276
M y T e s t M L D S A _ p r v
CKA_ID 00007ffc6765a5ae / 2
00000000 31 31 11
[out] hPublicKey = 0x1
[out] hPrivateKey = 0x2
Returned: 0 CKR_OK
Where did you find the defines for MECH_ML_VDM, MECH_VDM_SIGN and CKM_MECH_MLDSA_SIGN?
Only CKM_MECH_MLDSA_SIGN looks like a PKCS11 mechanism.
PKCS11 Mechanisms are usually assigned sequentially. And (MECH_ML_VDM | MECH_VDM_SIGN | 1) would be ~
0x0CA50301which as far as I can tell is not a standard mechanism and is not a vendor provided one, which would start with the high order bit set.~0xC0A53001which has the vendor provided bit set.
This is from a sample application to manage the PQC keys in the HSM.
That's true, the keys should have the same CKA_LABEL. That is managed by the sample application mentioned above. I can change it, so now they have the same label.
Can you give me some insights to start modifying libp11 to try to make it work with the Vendor Defined Mechanisms for the signing part?
Can you give me some insights to start modifying libp11 to try to make it work with the Vendor Defined Mechanisms for the signing part?
@mtrojnar would be the one to ask.
Looks like MLDSA is being used. So you need to see how to add support for another type of key, and see how libp11 handles RSA and EC hooks and do the same for MLDSA.
Other option is look at what Redhat is doing, as they are all in for PQC. Look at https://github.com/latchset/pkcs11-provider Note the developer works for Redhat.
Note Osais Chair is from Redhat: Robert Relyea ([email protected]), Red Hat
Got to go for now, Golf tee time in an hour.
Can you give me some insights to start modifying libp11 to try to make it work with the Vendor Defined Mechanisms for the signing part?
I guess you might start with adding support for a separate NID in src/p11_pkey.c. The PKCS11_pkey_meths() function currently supports EVP_PKEY_RSA and EVP_PKEY_EC:
int PKCS11_pkey_meths(ENGINE *e, EVP_PKEY_METHOD **pmeth,
const int **nids, int nid)
{
static int pkey_nids[] = {
EVP_PKEY_RSA,
#ifndef OPENSSL_NO_EC
EVP_PKEY_EC,
#endif /* OPENSSL_NO_EC */
0
};
static EVP_PKEY_METHOD *pkey_method_rsa = NULL;
#ifndef OPENSSL_NO_EC
static EVP_PKEY_METHOD *pkey_method_ec = NULL;
#endif /* OPENSSL_NO_EC */
(void)e; /* squash the unused parameter warning */
/* all PKCS#11 engines currently share the same pkey_meths */
if (!pmeth) { /* get the list of supported nids */
*nids = pkey_nids;
return sizeof(pkey_nids) / sizeof(int) - 1;
}
/* get the EVP_PKEY_METHOD */
switch (nid) {
case EVP_PKEY_RSA:
if (!pkey_method_rsa)
pkey_method_rsa = pkcs11_pkey_method_rsa();
if (!pkey_method_rsa)
return 0;
*pmeth = pkey_method_rsa;
return 1; /* success */
#ifndef OPENSSL_NO_EC
case EVP_PKEY_EC:
if (!pkey_method_ec)
pkey_method_ec = pkcs11_pkey_method_ec();
if (!pkey_method_ec)
return 0;
*pmeth = pkey_method_ec;
return 1; /* success */
#endif /* OPENSSL_NO_EC */
}
*pmeth = NULL;
return 0;
}
I started creating a p11_mldsa.c so I have a PKCS11_OBJECT_ops. I tried to make the static EVP_PKEY *pkcs11_get_evp_key_mldsa in a non-deprecated way (as I dont have types like RSA).
static EVP_PKEY *pkcs11_get_evp_key_mldsa(PKCS11_OBJECT_private *key)
{
EVP_PKEY_CTX *pctx = NULL;
EVP_PKEY *pkey = EVP_PKEY_new();
OSSL_PARAM params[1];
PKCS11_SLOT_private *slot = key->slot;
PKCS11_CTX_private *ctx = slot->ctx;
CK_OBJECT_HANDLE object = key->object;
CK_KEY_TYPE key_type = -1;
CK_SESSION_HANDLE session;
if (pkcs11_get_session(slot, 0, &session))
return NULL;
/* Retrieve the modulus */
if (pkcs11_getattr_val(ctx, session, object, CKA_KEY_TYPE, (CK_BYTE *)&key_type, sizeof(key_type)))
goto cleanup;
pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_ML_DSA_44, NULL);
if (!pctx) {
goto cleanup;
}
if (EVP_PKEY_fromdata_init(pctx) <= 0)
goto cleanup;
params[0] = OSSL_PARAM_construct_end();
if (key->object_class == CKO_PRIVATE_KEY) {
int res = EVP_PKEY_fromdata(pctx, &pkey, EVP_PKEY_KEYPAIR, params);
if (res <= 0)
goto cleanup;
}
int key_type_pkey = EVP_PKEY_get_base_id(pkey);
cleanup:
EVP_PKEY_CTX_free(pctx);
return pkey;
}
But EVP_PKEY_fromdata returns a 0 (probably because I cannot give the OSSL_PARAMS because it requires having the key. I'm kind of stuck in that part because the OpenSSL documentation is not very helpful in this matter.
What I'm trying is to get past the first time the programs get stuck in the pkcs11_object_from_handle call. But if I try this way with an empty EVP_PKEY object, it breaks in pkcs11_get_key because EVP_PKEY_base_id(key->evp_key) fails for not having a key type.
Any help would be much appreciated. I will try also starting from where you pointed out.
Thanks.
@mtrojnar I got to advance a little more and started following the indications you gave me.
Right now I'm facing the following problem.
I made a equivalent for MLDSA-44 for the function pkcs11_pkey_method...
static EVP_PKEY_METHOD *pkcs11_pkey_method_mldsa_44(void)
{
EVP_PKEY_METHOD *orig_meth, *new_meth;
orig_meth = (EVP_PKEY_METHOD *)EVP_PKEY_meth_find(EVP_PKEY_ML_DSA_44);
EVP_PKEY_meth_get_sign(orig_meth,
&orig_pkey_mldsa_sign_init, &orig_pkey_mldsa_sign);
new_meth = EVP_PKEY_meth_new(EVP_PKEY_ML_DSA_44, 0);
EVP_PKEY_meth_copy(new_meth, orig_meth);
EVP_PKEY_meth_set_sign(new_meth,
orig_pkey_mldsa_sign_init, pkcs11_pkey_mldsa_sign);
return new_meth;
}
The issue is that EVP_PKEY_meth_find(EVP_PKEY_ML_DSA_44) returns a NULL probably because is a deprecated function. I didn't find anything equivalent as the use of engines in general is deprecated.
Do you now any workaround to advance on this matter. Any help would be appreciated.
Thank you in advance.
OpenSSL's source code tends to be the best documentation for the OpenSSL API. Try reading it. Maybe you can find a workaround. Maybe you'll need to patch OpenSSL. While the OpenSSL project is unlikely to accept PRs against deprecated functionality, maybe you can find a sponsor that could change their mind about it.
Rather than using the libp11 engine, could the libp11 provider be modified instead? Or is the libp11 provider still using the deprecated functions in: https://docs.openssl.org/3.5/man3/EVP_PKEY_meth_new/#methods Could the libp11 provider use EVP_KEYMGT?
Also look at: https://docs.oasis-open.org/pkcs11/pkcs11-spec/v3.2/pkcs11-spec-v3.2.html https://docs.oasis-open.org/pkcs11/pkcs11-spec/v3.2/csd01/include/pkcs11-v3.2/pkcs11t.h which are draft, but have defines for PQC attributes. The check with the PKCS11 module to see if they are going to support these in place of (or additional to there vendor provided attributes.
Also look at https://github.com/softhsm/SoftHSMv2/pull/798 which adds ML-KEM and ML-DSA. to their PKCS11 module. This was closed within 2 days, which is strange but softhsm2 is dormant, and was based on older OpenSSL, and Botan. But it might give you some clues.
Is there a libp11 provider? I only know about the engine.
Yes, reading is hard.