808bits

SoftHSMv2

read from source and observed · 103 mechanisms observed · 2.7.0 development · BSD-2-Clause · read at 884cb38, 2026-09-03

SoftHSMv2 is the software token most PKCS#11 code is first tested against, and the return values it hands back are chosen by one C++ file with a few habits the specification would not predict. The vague code with teeth is its everyday code. Slot numbers move the moment a token is initialised. The PIN counter never trips. None of that is a bug, and all of it is a surprise the first time a test that passed on SoftHSM meets a real device.

One code for almost everything

The library’s PKCS#11 entry points live in a single file, and the way it reports a failure is to return CKR_GENERAL_ERROR. That line appears 206 times in SoftHSM.cpp at the commit read. It is what comes back when the crypto backend refuses an operation, when a session exists but its token does not, and in two places where the code has a more specific answer and declines to use it. In C_GetAttributeValue and C_DigestKey an unauthenticated read of a private object produces CKR_USER_NOT_LOGGED_IN internally, and a comment at line 2084 explains that the specification does not list that code for the function, so the caller gets the general error instead. The specification’s recoverable failure, CKR_FUNCTION_FAILED, is reserved for the object store. Ninety-eight of its hundred appearances are rv = CKR_FUNCTION_FAILED after an attribute could not be written to the token’s storage, and the other two follow a failed destroyObject. So on this library the two codes mean roughly the opposite of what the specification suggests. The general error is the ordinary one and the library is fine afterwards. The function failure means the files on disk did not take a write, which is the one situation worth stopping for.

Slot 0 is a moving target

A fresh installation has one slot, numbered 0, with no token in it. Initialise a token there and the token is not in slot 0 any more. SlotManager numbers each token’s slot from the last eight hex digits of the token’s serial, masked to 31 bits so that a Java signed int survives it, then appends one empty slot numbered by the count of tokens. I ran it on this machine. softhsm2-util --init-token --free printed that the token “is reassigned to slot 1613085303”, the empty slot became slot 1, and opening a session on slot 0 returned CKR_SLOT_ID_INVALID. The serial is random, so the number differs on every machine and in every CI run. Code that hard-codes a slot survives until the first --init-token and no longer. --token-label and --serial exist for this reason.

The PIN never locks

Token::loginUser compares the PIN, sets the CKF_USER_PIN_COUNT_LOW flag on a miss, and returns CKR_PIN_INCORRECT. There is no counter behind the flag and nothing in the library ever returns CKR_PIN_LOCKED. Five wrong PINs in a row on the token above produced five CKR_PIN_INCORRECT, and the right PIN then logged in. A retry loop that is harmless here will brick a production token. That is the single largest gap between what SoftHSM lets a test suite get away with and what an HSM will.

Mechanisms are a config setting

The mechanism list is built at C_Initialize from a fixed table of 112 names, then filtered by slots.mechanisms in the configuration file, which takes either a positive list or a leading minus and a list to remove (lines 928 to 967). Mechanisms the build cannot support are dropped before that. The table below is from a build with GOST disabled against OpenSSL 3.0, and it has 103 entries with EdDSA present. A mechanism missing from the list is CKR_MECHANISM_INVALID at use time, and the reason may be a line in softhsm2.conf rather than anything in the code. The list is the same on the empty slot and on a token, which is not true of hardware. None of the 103 sets CKF_HW, which is the honest answer.

Mechanisms the token actually exposes

Read from a live token rather than from a document. The library returns a list of integers, then names each one's key size range and the operations it is good for. That last part lives in C_GetMechanismInfo rather than in the mechanism list, and most vendor status tools leave it out. OpenSC's pkcs11-tool --list-mechanisms is the exception. The same rows sit beside every other library's on the mechanism table.

103 mechanisms, 0 of them vendor-defined. 0 set CKF_HW, which claims the operation is done in hardware.

MechanismHexKeysOperations
CKM_RSA_PKCS_KEY_PAIR_GEN0x00000000512–16384generate_key_pair
CKM_RSA_PKCS0x00000001512–16384encrypt, decrypt, sign, verify, wrap, unwrap
CKM_RSA_X_5090x00000003512–16384encrypt, decrypt, sign, verify
CKM_MD5_RSA_PKCS0x00000005512–16384sign, verify
CKM_SHA1_RSA_PKCS0x00000006512–16384sign, verify
CKM_RSA_PKCS_OAEP0x00000009512–16384encrypt, decrypt, wrap, unwrap
CKM_RSA_PKCS_PSS0x0000000D512–16384sign, verify
CKM_SHA1_RSA_PKCS_PSS0x0000000E512–16384sign, verify
CKM_DSA_KEY_PAIR_GEN0x00000010512–1024generate_key_pair
CKM_DSA0x00000011512–1024sign, verify
CKM_DSA_SHA10x00000012512–1024sign, verify
CKM_DSA_SHA2240x00000013512–1024sign, verify
CKM_DSA_SHA2560x00000014512–1024sign, verify
CKM_DSA_SHA3840x00000015512–1024sign, verify
CKM_DSA_SHA5120x00000016512–1024sign, verify
CKM_DSA_SHA3_2240x00000018512–1024sign, verify
CKM_DSA_SHA3_2560x00000019512–1024sign, verify
CKM_DSA_SHA3_3840x0000001A512–1024sign, verify
CKM_DSA_SHA3_5120x0000001B512–1024sign, verify
CKM_DH_PKCS_KEY_PAIR_GEN0x00000020512–10000generate_key_pair
CKM_DH_PKCS_DERIVE0x00000021512–10000derive
CKM_SHA256_RSA_PKCS0x00000040512–16384sign, verify
CKM_SHA384_RSA_PKCS0x00000041512–16384sign, verify
CKM_SHA512_RSA_PKCS0x00000042512–16384sign, verify
CKM_SHA256_RSA_PKCS_PSS0x00000043512–16384sign, verify
CKM_SHA384_RSA_PKCS_PSS0x00000044512–16384sign, verify
CKM_SHA512_RSA_PKCS_PSS0x00000045512–16384sign, verify
CKM_SHA224_RSA_PKCS0x00000046512–16384sign, verify
CKM_SHA224_RSA_PKCS_PSS0x00000047512–16384sign, verify
CKM_SHA3_256_RSA_PKCS0x00000060512–16384sign, verify
CKM_SHA3_384_RSA_PKCS0x00000061512–16384sign, verify
CKM_SHA3_512_RSA_PKCS0x00000062512–16384sign, verify
CKM_SHA3_256_RSA_PKCS_PSS0x00000063512–16384sign, verify
CKM_SHA3_384_RSA_PKCS_PSS0x00000064512–16384sign, verify
CKM_SHA3_512_RSA_PKCS_PSS0x00000065512–16384sign, verify
CKM_SHA3_224_RSA_PKCS0x00000066512–16384sign, verify
CKM_SHA3_224_RSA_PKCS_PSS0x00000067512–16384sign, verify
CKM_DES_KEY_GEN0x00000120-generate
CKM_DES_ECB0x00000121-encrypt, decrypt, wrap
CKM_DES_CBC0x00000122-encrypt, decrypt, wrap
CKM_DES_CBC_PAD0x00000125-encrypt, decrypt, wrap, unwrap
CKM_DES2_KEY_GEN0x00000130-generate
CKM_DES3_KEY_GEN0x00000131-generate
CKM_DES3_ECB0x00000132-encrypt, decrypt
CKM_DES3_CBC0x00000133-encrypt, decrypt, wrap
CKM_DES3_CBC_PAD0x00000136-encrypt, decrypt, wrap, unwrap
CKM_DES3_CMAC0x00000138-sign, verify
CKM_MD50x00000210-digest
CKM_MD5_HMAC0x0000021116–512sign, verify
CKM_SHA_10x00000220-digest
CKM_SHA_1_HMAC0x0000022120–512sign, verify
CKM_SHA2560x00000250-digest
CKM_SHA256_HMAC0x0000025132–512sign, verify
CKM_SHA2240x00000255-digest
CKM_SHA224_HMAC0x0000025628–512sign, verify
CKM_SHA3840x00000260-digest
CKM_SHA384_HMAC0x0000026148–512sign, verify
CKM_SHA5120x00000270-digest
CKM_SHA512_HMAC0x0000027164–512sign, verify
CKM_SHA3_2560x000002B0-digest
CKM_SHA3_256_HMAC0x000002B132–512sign, verify
CKM_SHA3_2240x000002B5-digest
CKM_SHA3_224_HMAC0x000002B628–512sign, verify
CKM_SHA3_3840x000002C0-digest
CKM_SHA3_384_HMAC0x000002C148–512sign, verify
CKM_SHA3_5120x000002D0-digest
CKM_SHA3_512_HMAC0x000002D164–512sign, verify
CKM_GENERIC_SECRET_KEY_GEN0x000003501–134217728generate
CKM_CONCATENATE_BASE_AND_KEY0x000003601–512derive
CKM_CONCATENATE_BASE_AND_DATA0x000003621–512derive
CKM_CONCATENATE_DATA_AND_BASE0x000003631–512derive
CKM_ECDSA_KEY_PAIR_GEN0x00001040112–521generate_key_pair, ec_f_p, ec_oid, ec_uncompress
CKM_ECDSA0x00001041112–521sign, verify, ec_f_p, ec_oid, ec_uncompress
CKM_ECDSA_SHA10x00001042112–521sign, verify, ec_f_p, ec_oid, ec_uncompress
CKM_ECDSA_SHA2240x00001043112–521sign, verify, ec_f_p, ec_oid, ec_uncompress
CKM_ECDSA_SHA2560x00001044112–521sign, verify, ec_f_p, ec_oid, ec_uncompress
CKM_ECDSA_SHA3840x00001045112–521sign, verify, ec_f_p, ec_oid, ec_uncompress
CKM_ECDSA_SHA5120x00001046112–521sign, verify, ec_f_p, ec_oid, ec_uncompress
CKM_ECDSA_SHA3_2240x00001047112–521sign, verify, ec_f_p, ec_oid, ec_uncompress
CKM_ECDSA_SHA3_2560x00001048112–521sign, verify, ec_f_p, ec_oid, ec_uncompress
CKM_ECDSA_SHA3_3840x00001049112–521sign, verify, ec_f_p, ec_oid, ec_uncompress
CKM_ECDSA_SHA3_5120x0000104A112–521sign, verify, ec_f_p, ec_oid, ec_uncompress
CKM_ECDH1_DERIVE0x00001050112–521derive
CKM_RSA_AES_KEY_WRAP0x00001054512–16384wrap, unwrap
CKM_EC_EDWARDS_KEY_PAIR_GEN0x00001055255–448generate_key_pair
CKM_EDDSA0x00001057255–448sign, verify
CKM_AES_KEY_GEN0x0000108016–32generate
CKM_AES_ECB0x0000108116–32encrypt, decrypt
CKM_AES_CBC0x0000108216–32encrypt, decrypt, wrap, unwrap
CKM_AES_CBC_PAD0x0000108516–32encrypt, decrypt, wrap, unwrap
CKM_AES_CTR0x0000108616–32encrypt, decrypt
CKM_AES_GCM0x0000108716–32encrypt, decrypt
CKM_AES_CMAC0x0000108A16–32sign, verify
CKM_DES_ECB_ENCRYPT_DATA0x00001100-derive
CKM_DES_CBC_ENCRYPT_DATA0x00001101-derive
CKM_DES3_ECB_ENCRYPT_DATA0x00001102-derive
CKM_DES3_CBC_ENCRYPT_DATA0x00001103-derive
CKM_AES_ECB_ENCRYPT_DATA0x00001104-derive
CKM_AES_CBC_ENCRYPT_DATA0x00001105-derive
CKM_DSA_PARAMETER_GEN0x00002000512–1024generate
CKM_DH_PKCS_PARAMETER_GEN0x00002001512–10000generate
CKM_AES_KEY_WRAP0x0000210916–2147483648wrap, unwrap
CKM_AES_KEY_WRAP_PAD0x0000210A1–2147483648wrap, unwrap

observed · Implementation of PKCS11 2.7 · Cryptoki 3.2 · slot 1613085303, token "demo" · C_GetMechanismInfo · 2026-09-10

Return values with a note for this library

  • CKR_SLOT_ID_INVALID · The library does not have that slot. Usually a number hard-coded from a machine where the enumeration came out differently.
  • CKR_GENERAL_ERROR · Not a failed call but a broken library. The specification says nothing further will work, so retrying it is wasted effort.
  • CKR_FUNCTION_FAILED · A recoverable failure. The library is still usable and the token unchanged, so this one is worth investigating rather than restarting.
  • CKR_MECHANISM_INVALID · The mechanism is unknown here, or not valid for the operation you used it in. Check the slot's own list rather than the header you compiled against.
  • CKR_PIN_INCORRECT · The credential was wrong. The important part is what your code does next, because automatic retries are how tokens get locked.
  • CKR_PIN_LOCKED · The token has locked the PIN after too many failed attempts. On most HSMs this is not something you can wait out, and on some it destroys the partition.
  • CKR_TOKEN_NOT_PRESENT · Better news than it looks: the slot identifier was right. On a network HSM this usually means the client cannot reach the partition rather than a missing card.
  • CKR_USER_NOT_LOGGED_IN · A private object needs an authenticated session. Often a login that happened on a different session, or one that a token event quietly ended.

How this page was made

Read from the source at the commit linked above, with every claim pinned to a line. Counts of return sites are from a search over the library's own code and are only as exact as a search can be. The mechanism table is from a build of that commit on this machine, read with the same probe as the other pages here. It opens no session and never logs in.

Scope. Read from the SoftHSMv2 repository at the commit named above, the main branch a week before this page was written, and from a build of that commit on this machine with the OpenSSL backend. Counts of return sites are from searching the source text. The mechanism table depends on the crypto backend and the configure flags, so another build will differ. Nothing here is from a packaged release, and distribution packages lag the repository by a year or more.

Sources

  • SoftHSMv2 repository at 884cb38, softhsm on GitHub. Read 2026-09-10. Used for every claim about which code is returned when, and the build behind the mechanism table.
  • softhsm2.conf(5), SoftHSM project. Read 2026-09-10. Used for the slots.mechanisms and slots.removable settings.