Repository navigation
Tighten ECDH derive input checks and derived key state - #233
Merged
Merged
Conversation
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Several feature-gated tests incorrectly report PASS instead of SKIP, and the description omits substantial behavioral changes.
4 open findings
What changed in this PR
Hardens ECDH derivation and also introduces broader PKCS#11 state, persistence, login, and operation-lifecycle fixes.
Changes:
- Validates ECDH inputs, derived-key state, locking, and login requirements.
- Improves token storage, PIN state, operation cancellation, and mechanism metadata.
- Adds extensive regression tests and CI configurations.
| File | Description |
|---|---|
.github/workflows/unit-test.yml |
Adds TPM-debug and no-environment CI jobs. |
src/crypto.c |
Hardens derive and operation lifecycle behavior. |
src/internal.c |
Updates storage, locking, login, and session internals. |
src/slot.c |
Corrects token metadata, PIN handling, and mechanisms. |
src/wolfpkcs11.c |
Reports finalization failures and pads metadata. |
tests/ec_derive_test.c |
Adds ECDH regression coverage. |
tests/include.am |
Registers new test programs. |
tests/info_mech_table_test.c |
Tests metadata and mechanism consistency. |
tests/login_pin_state_test.c |
Tests login, PIN, and concurrency state. |
tests/object_policy_integrity_test.c |
Tests persisted object-policy integrity. |
tests/object_store_decode_test.c |
Tests malformed stored-object handling. |
tests/pkcs11mtt.c |
Updates v3 operation-cancellation expectations. |
tests/pkcs11test.c |
Updates clock and cancellation expectations. |
tests/session_op_lifecycle_test.c |
Tests operation cleanup and cancellation. |
tests/token_store_test.c |
Tests transactional token persistence. |
wolfpkcs11/internal.h |
Declares new limits and internal helpers. |
wolfpkcs11/pkcs11.h |
Adds the operation-cancellation error constant. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
aidangarske
force-pushed
the
fenrir-fixes-10396-1610
branch
3 times, most recently
from
October 10, 2026 03:20
97ed5c1 to
aafebc1
Compare
Frauschi
reviewed
Oct 13, 2026
Frauschi
left a comment
Member
There was a problem hiding this comment.
Looks good, one test request inline.
aidangarske
force-pushed
the
fenrir-fixes-10396-1610
branch
from
October 13, 2026 22:19
aafebc1 to
a653795
Compare
aidangarske
force-pushed
the
fenrir-fixes-10396-1610
branch
3 times, most recently
from
October 13, 2026 23:44
916da34 to
5beb906
Compare
aidangarske
force-pushed
the
fenrir-fixes-10396-1610
branch
2 times, most recently
from
October 15, 2026 16:51
0677400 to
02f611d
Compare
Frauschi
previously approved these changes
Oct 15, 2026
aidangarske
dismissed
Frauschi’s stale review
October 15, 2026 20:36
The merge-base changed after approval.
aidangarske
force-pushed
the
fenrir-fixes-10396-1610
branch
from
October 15, 2026 21:01
02f611d to
128a0da
Compare
AlexLaroche
added a commit
to KerckhoffsLabs/KerckhoffsLabs.Security.Cryptography.Pkcs11
that referenced
this pull request
Oct 30, 2026
AlgorithmConformanceTestCases.Assert_Ecdh_ParameterOutOfRange_IsRefused, one row per fault, for CKM_ECDH1_DERIVE with CKD_NULL from the SP 800-56A P-256 vector, through the raw dispatch (the library's parameter type refuses the first three): - CKD_NULL with shared data: PKCS#11 v3.2 §6.3.16, "pSharedData must be NULL and ulSharedDataLen must be zero"; - a 1 KiB peer value (a raw point padded with zeros), and a DER OCTET STRING whose length byte promises 65 bytes and has 40 (§6.3.16: the peer's point, raw or DER); each refused with CKR_MECHANISM_PARAM_INVALID (§5.1.6); - CKA_VALUE_LEN 33 for a generic secret, one byte more than the secret the mechanism can only truncate (§6.3.17): CKR_ATTRIBUTE_VALUE_INVALID, CKR_TEMPLATE_INCONSISTENT (§4.1) or CKR_KEY_SIZE_RANGE (§5.18.5). None makes a key (found by label), and the same session then derives Z, checked through an HMAC under it where the module has HMAC-SHA256. From wolfSSL/wolfPKCS11#233, #234 Pass: Craton HSM (every row). Gated, each confirmed by the run and, for the keys made, a probe reading them back: - wolfPKCS11 answers the lying DER length with CKR_FUNCTION_FAILED (WolfPkcs11RefusesAMalformedEcdhPeerAsInvalid, tripwire); - Kryoptic answers it with CKR_DEVICE_ERROR (KryopticRefusesAMalformedEcdhPeerAsInvalid); - SoftHSM2 answers both peer rows with CKR_GENERAL_ERROR (SoftHsmRefusesAMalformedEcdhPeerAsInvalid) and CKA_VALUE_LEN 33 with CKR_FUNCTION_FAILED (SoftHsmRefusesAnEcdhValueLenAboveTheSecret); - BouncyHsm ignores the shared data and derives Z (BouncyHsmRefusesSharedDataWithCkdNull, tripwire) and makes 00 || Z for CKA_VALUE_LEN 33 (BouncyHsmRefusesAnEcdhValueLenAboveTheSecret, tripwire); its peer rows pass; - FreeHSM departs on every row: it ignores the shared data, makes a 32-byte key for CKA_VALUE_LEN 33 and refuses both peers with CKR_ATTRIBUTE_VALUE_INVALID (FreeHsmChecksTheEcdhParameters). The other rows pass on each. NSS and opencryptoki run in CI only.
AlexLaroche
added a commit
to KerckhoffsLabs/KerckhoffsLabs.Security.Cryptography.Pkcs11
that referenced
this pull request
Oct 31, 2026
…very backend (#293) * test: refuse a private object without a login on every backend SessionConformanceTestCases.Assert_PrivateObjectCreation_WithoutALogin_IsUserNotLoggedIn, one row per call: with nobody logged in, C_CreateObject, C_CopyObject, C_GenerateKey, C_GenerateKeyPair, C_DeriveKey (HKDF), C_UnwrapKey (an AES key wrap), C_EncapsulateKey and C_DecapsulateKey (ML-KEM-768) with CKA_PRIVATE = TRUE in the template answer CKR_USER_NOT_LOGGED_IN (PKCS#11 v3.2 §5.1.6) and create nothing. Every input is a public session object made while logged in, which the C_Logout leaves in place (§5.6.10). C_CopyObject may also answer CKR_ATTRIBUTE_READ_ONLY or CKR_TEMPLATE_INCONSISTENT: §4.1.3 lets a token refuse to change CKA_PRIVATE in a copy. From wolfSSL/wolfPKCS11#187, #189, #207, #233, #243 Pass: SoftHSM2, BouncyHsm, FreeHSM, wolfPKCS11 (each row its mechanisms allow). NSS skips (no login). Two gates: - Kryoptic checks the login only for token objects, so every call makes a private session object with nobody logged in (KryopticRefusesPrivateObjectsWithoutALogin). - Craton HSM refuses without a login only CKO_PRIVATE_KEY and CKO_SECRET_KEY in C_CreateObject and never looks at CKA_PRIVATE: a private data object, a copy, a generated key or key pair and an unwrapped key are all made (CratonHsmRefusesPrivateObjectsWithoutALogin, with a tripwire in CratonHsmQuirkTests). opencryptoki runs in CI only. * test: invalidate private handles on C_Logout on every backend SessionConformanceTestCases.Assert_Logout_InvalidatesPrivateHandles: log in, make a private token AES key, a private and a public session object, log out (PKCS#11 v3.2 §5.6.10). With nobody logged in, C_GetAttributeValue on the token key and on the private session object answers CKR_OBJECT_HANDLE_INVALID, C_EncryptInit with the token key is refused (CKR_KEY_HANDLE_INVALID or CKR_USER_NOT_LOGGED_IN, which §5.8.1 lists, or CKR_OBJECT_HANDLE_INVALID, which SoftHSM2 and wolfPKCS11 answer), and the public session object keeps its handle. After a new login the private session object's handle is still invalid and no search finds it. The token key's old handle is not checked after the new login: a module may give it the same number again. From wolfSSL/wolfPKCS11#179, #196, #227 Pass: SoftHSM2, wolfPKCS11, Kryoptic. Kryoptic answers C_EncryptInit with CKR_GENERAL_ERROR, recorded through the case's parameter (KryopticRefusesAPrivateKeyAfterLogout). NSS skips (no login). Gated: - BouncyHsm and Craton HSM keep private session objects after C_Logout (a new login finds them under their old handles), and while nobody is logged in answer a private object's handle with CKR_USER_NOT_LOGGED_IN, which §5.7.5 does not list for C_GetAttributeValue (BouncyHsmDestroysPrivateSessionObjectsOnLogout, CratonHsmDestroysPrivateSessionObjectsOnLogout, each with a tripwire). - FreeHSM keeps private session objects readable after C_Logout (C_GetAttributeValue answers CKR_OK with nobody logged in) and starts C_EncryptInit with a private token key whose attributes it refuses to read (FreeHsmDestroysPrivateSessionObjectsOnLogout). opencryptoki runs in CI only. * test: keep the SO and the user apart on every backend Two SessionConformanceTestCases: - Assert_SoAndUser_ExcludeEachOther: with the SO logged in, C_Login (CKU_USER) from its session or another answers CKR_USER_ANOTHER_ALREADY_LOGGED_IN, and with the user logged in so does C_Login(CKU_SO) (PKCS#11 v3.2 §5.1.6); the sessions stay in the state of whoever is logged in. - Assert_So_CreatesNoPrivateObject: with the SO logged in, C_CreateObject with CKA_PRIVATE = TRUE answers CKR_USER_NOT_LOGGED_IN and makes nothing (§5.7.1: only public objects unless the normal user is logged in). Both log the SO in with the right PIN from read/write sessions only and leave nobody logged in whatever fails. Whether the SO may see the user's private objects is left out: only [PKCS11-UG] says so, and Kryoptic, BouncyHsm, Craton HSM and FreeHSM all show them to the SO. From wolfSSL/wolfPKCS11#194, #212 Pass: SoftHSM2, wolfPKCS11 (both); Kryoptic, Craton HSM, FreeHSM (the login one). NSS skips (no login). Gated: - BouncyHsm logs the user in over the SO with CKR_OK (BouncyHsmKeepsTheSoAndTheUserApart) and lets the SO make a private object (BouncyHsmRefusesPrivateObjectsToTheSo), each with a tripwire. - FreeHSM lets the SO make a private object (FreeHsmRefusesPrivateObjectsToTheSo). - Kryoptic and Craton HSM let the SO make one too: their existing private-object gates now say so. opencryptoki runs in CI only. * test: refuse token-object writes in a read-only session on every backend SessionConformanceTestCases.Assert_ReadOnlySession_RefusesTokenObjectWrites, one row per call: in a read-only session with the user logged in, C_GenerateKey, C_GenerateKeyPair (either half on the token), C_DeriveKey (HKDF, or ECDH where there is no HKDF), C_UnwrapKey, C_CopyObject (of a token object, or of a session object into one), C_SetAttributeValue and C_DestroyObject of a token object answer CKR_SESSION_READ_ONLY (PKCS#11 v3.2 §5.7.1-§5.7.3, §5.7.6, §5.18), and the same call on session objects succeeds. The token object is made in a read/write session; the inputs are session objects of the read-only session itself. Nothing new carries CKA_TOKEN = TRUE afterwards and the token object keeps its label. From wolfSSL/wolfPKCS11#177, #186, #197 Pass: SoftHSM2, wolfPKCS11, BouncyHsm (every row); Kryoptic and FreeHSM (the rows below aside). NSS skips (no token objects). Gated: - Kryoptic copies a token object whose template does not name CKA_TOKEN into a new token object, and refuses C_DestroyObject of a token object with CKR_ACTION_PROHIBITED (KryopticRefusesTokenObjectWritesInReadOnlySessions, CopyObject and DestroyObject rows). - FreeHSM copies a token object the same way (FreeHsmRefusesTokenObjectCopiesInReadOnlySessions, CopyObject row). - Craton HSM refuses a session object in a read-only session too (CratonHsmMakesSessionObjectsInReadOnlySessions, with a tripwire); the existing ReadOnlySession_RefusesTokenObjects wrapper, which skipped for the same reason in its body, now uses the gate. opencryptoki runs in CI only. * test: refuse a PIN outside the token's length range on every backend SessionConformanceTestCases.Assert_PinOutsideTheLengthRange_IsRefused: the token's [ulMinPinLen, ulMaxPinLen] holds the user PIN it was given (PKCS#11 v3.2 §3.2); a PIN one byte shorter than the minimum and one byte longer than the maximum (where these are reported, from 1 to 1024 bytes) are refused by C_Login with CKR_PIN_LEN_RANGE or CKR_PIN_INCORRECT, the session stays public, and the right PIN logs in at once; C_SetPIN to either answers CKR_PIN_LEN_RANGE (§5.5.9, §5.1.6) and the old PIN still logs in. C_Login gets both codes: §5.1.6 says CKR_PIN_LEN_RANGE applies to the functions that set a PIN, and §5.6.8 lists neither. No wrong PIN of a valid length is presented, and a C_SetPIN the token takes is undone. From wolfSSL/wolfPKCS11#178, #194 Pass: SoftHSM2, wolfPKCS11, FreeHSM. NSS skips (no login). Gated: - Craton HSM does not check a PIN's length at C_Login and counts the short PIN as a failed login; the right PIN right after it is refused with CKR_PIN_LOCKED for a backoff window, and so are the rest of the collection's logins (CratonHsmTakesTheRightPinRightAfterARefusedOne; no tripwire, which would lock the collection out). - Kryoptic reports ulMinPinLen 8 but C_SetPIN sets a 7-byte PIN (KryopticEnforcesItsMinimumPinLength). - BouncyHsm's native client copies MaxPinLen into ulMinPinLen, so it reports [512, 512] and takes a 511-byte PIN in C_SetPIN (BouncyHsmReportsItsMinimumPinLength, with a tripwire). opencryptoki runs in CI only. * test: report the mechanism count on a short list on every backend SessionConformanceTestCases.Assert_MechanismList_OneShort_IsBufferTooSmall: C_GetMechanismList into a list one entry short answers CKR_BUFFER_TOO_SMALL and sets *pulCount to the number of mechanisms (PKCS#11 v3.2 §5.5.5), and the retry at that count returns the whole list. The C_GetSlotList half of the group needs a module with two slots and is left for later. From wolfSSL/wolfPKCS11#186, #207 Pass: SoftHSM2, Kryoptic, Craton HSM, FreeHSM, wolfPKCS11. Gated: BouncyHsm answers CKR_BUFFER_TOO_SMALL but leaves *pulCount as the caller gave it: its server sends no data with the error (BouncyHsmReportsTheMechanismCountOnAShortList, with a tripwire). NSS and opencryptoki run in CI only. * test: hold the token to what it advertises on every backend Three SessionConformanceTestCases: - Assert_RsaMechanisms_AdvertiseThe2048BitKeysTheyTake: on a token that makes an RSA-2048 key, every §6.1 RSA mechanism it lists (key pair generation, PKCS#1 v1.5, X.509, OAEP, PSS and the hash-and-sign ones) reports a key size range, in modulus bits, that contains 2048 (PKCS#11 v3.2 §6.1.4-§6.1.10), where it reports one. - Assert_AdvertisedWrapMechanism_WrapsAnAesKey: CKM_AES_KEY_WRAP, _PAD, _KWP, _PKCS7 and CKM_RSA_PKCS_OAEP (SHA-256), where listed with CKF_WRAP, wrap an extractable AES-256 key, and where also listed with CKF_UNWRAP unwrap it to a key that decrypts what the original encrypts (§5.5.6). The modes that wrap by encrypting, RSA PKCS#1 v1.5 and raw RSA are left to the wrap and mechanism cases. - Assert_ClockOnToken_ReportsAWellFormedUtcTime: with CKF_CLOCK_ON_TOKEN, utcTime is YYYYMMDDhhmmss00 and a valid time (§3.2). From wolfSSL/wolfPKCS11#168, #231 Pass everywhere the token has the mechanism, the flag or the clock: SoftHSM2, Kryoptic, BouncyHsm, Craton HSM, FreeHSM, wolfPKCS11 (the clock: BouncyHsm and wolfPKCS11; the others have none). No gate. NSS and opencryptoki run in CI only. * test: hide a sensitive private key's secret parts on every backend ObjectConformanceTestCases.Assert_SensitivePrivateKey_HidesItsSecretParts, one row per key type (RSA-2048, EC P-256, ML-DSA-44, ML-KEM-768): on a private key generated with CKA_SENSITIVE = TRUE, C_GetAttributeValue of each secret part alone (RSA CKA_PRIVATE_EXPONENT and the CRT values, EC CKA_VALUE, ML-DSA and ML-KEM CKA_VALUE and CKA_SEED), with pValue NULL and with a buffer large enough, answers CKR_ATTRIBUTE_SENSITIVE, sets ulValueLen to CK_UNAVAILABLE_INFORMATION and writes nothing into the buffer (PKCS#11 v3.2 §5.7.5 case 1; footnote 7 of §6.1.3, §6.3.4, §6.67.3, §6.68.3). The parts the tables do not require (the CRT values, the seeds) may also answer CKR_ATTRIBUTE_TYPE_INVALID (case 2). One read of every secret part, an attribute the key type lacks, CKA_LABEL and CKA_KEY_TYPE answers one of those codes, marks the first ones unavailable and fills the last two. Through the raw call, so the module's own code and lengths are seen. From wolfSSL/wolfPKCS11#187, #197, #237 Pass: SoftHSM2, Kryoptic, wolfPKCS11, Craton HSM (every row the token has a mechanism for: no ML-KEM on SoftHSM2 or Craton HSM); BouncyHsm (RSA, EC); FreeHSM (EC, ML-DSA, ML-KEM). Gated: - BouncyHsm returns the CKA_VALUE of a sensitive ML-DSA private key and of a sensitive ML-KEM private key, and the ML-DSA CKA_SEED, with CKR_OK: its ML-DSA, ML-KEM and SLH-DSA private key classes do not override IsSensitiveAttribute (BouncyHsmHidesSensitivePqcPrivateKeys, with a tripwire). - FreeHSM answers each secret part of a sensitive RSA key with CKR_ATTRIBUTE_TYPE_INVALID, the value unrevealed (FreeHsmReportsRsaPrivatePartsAsSensitive). NSS and opencryptoki run in CI only. * test: refuse a template missing a required attribute on every backend ObjectConformanceTestCases.Assert_IncompleteTemplate_IsTemplateIncomplete, one row per template: C_GenerateKeyPair of an RSA key pair without CKA_MODULUS_BITS and of an EC key pair without CKA_EC_PARAMS (footnote 3, PKCS#11 v3.2 §6.1.2, §6.3.3), and C_CreateObject of an RSA public or private key without CKA_MODULUS, an EC public or private key without CKA_EC_PARAMS and a secret key without CKA_KEY_TYPE (footnote 1, §6.1.2, §6.1.3, §6.3.3, §6.3.4, §4.8) answer CKR_TEMPLATE_INCOMPLETE (§4.1.1 point 4) and leave nothing with the case's label. Every other value in the templates is valid. From wolfSSL/wolfPKCS11#194, #236, #237 Pass: Kryoptic, wolfPKCS11 (every row). Recorded through the case's leavesAnObjectBehind parameter: SoftHSM2 answers the four CreateObject key rows with CKR_TEMPLATE_INCOMPLETE but leaves the object behind, as it does for an AES key with no value. Gated: - Craton HSM generates an RSA key pair without CKA_MODULUS_BITS and creates the RSA public, EC and secret keys; it refuses the RSA private key with CKR_TEMPLATE_INCONSISTENT (CratonHsmRequiresTheKeyAttributes). - FreeHSM generates both key pairs without the sizing attribute and refuses the EC private key with CKR_TEMPLATE_INCONSISTENT (FreeHsmRequiresTheKeyAttributes; the other rows pass). - BouncyHsm answers the four CreateObject key rows with CKR_ATTRIBUTE_VALUE_INVALID (BouncyHsmReportsAMissingKeyAttributeAsIncomplete) and creates the secret key without CKA_KEY_TYPE (BouncyHsmRefusesASecretKeyWithoutAKeyType), each with a tripwire; the two key pair rows pass. NSS and opencryptoki run in CI only. * test: refuse a template whose class or key type contradicts on every backend ObjectConformanceTestCases.Assert_InconsistentTemplate_IsTemplateInconsistent, one row per template: C_GenerateKey(CKM_AES_KEY_GEN) with CKK_GENERIC_SECRET or CKO_DATA and C_GenerateKeyPair(CKM_EC_KEY_PAIR_GEN) with CKK_RSA for the private key or CKO_PRIVATE_KEY for the public one answer CKR_TEMPLATE_INCONSISTENT (PKCS#11 v3.2 §5.18.1, §5.18.2); C_CreateObject of a CKO_SECRET_KEY of type CKK_EC or a CKO_PRIVATE_KEY of type CKK_AES (§4.1.1 point 5) and C_DeriveKey(CKM_HKDF_DERIVE) asking for CKO_DATA (§6.62.3: the mechanism contributes the class; CKM_HKDF_DATA is the one that makes data, §6.62.4) answer CKR_TEMPLATE_INCONSISTENT or CKR_ATTRIBUTE_VALUE_INVALID, since neither function names the code and §4.1.1 point 2 can be read to cover it. Nothing carries the case's label afterwards. From wolfSSL/wolfPKCS11#216, #263 Pass: Kryoptic (every row). Gated: - wolfPKCS11 v2.2.0 makes a CKO_PRIVATE_KEY of type CKK_AES (WolfPkcs11RefusesAPrivateKeyOfASecretKeyType, with a tripwire; #263, after v2.2.0, fixes it). The other rows pass; the HKDF one answers CKR_ATTRIBUTE_VALUE_INVALID. - SoftHSM2 answers a class the generation mechanism does not make with CKR_ATTRIBUTE_VALUE_INVALID (SoftHsmReportsAContradictoryClassAsInconsistent); it has no CKM_HKDF_DERIVE. - BouncyHsm generates an EC key pair with CKK_RSA in the private template or CKO_PRIVATE_KEY in the public one (BouncyHsmChecksTheKeyPairTemplatesClassAndKeyType, with a tripwire); the other rows pass. - Craton HSM and FreeHSM take every template here with CKR_OK (CratonHsmChecksTheTemplatesClassAndKeyType, FreeHsmChecksTheTemplatesClassAndKeyType). NSS and opencryptoki run in CI only. * test: keep read-only attributes read-only on every backend Three new ObjectConformanceTestCases and three new rows: - Assert_UniqueId_IsTheTokensAndReadOnly: two data objects and a copy of one have non-empty, distinct CKA_UNIQUE_IDs, and a CKA_UNIQUE_ID in C_CreateObject, C_CopyObject or C_SetAttributeValue answers CKR_ATTRIBUTE_READ_ONLY, makes nothing and changes nothing (PKCS#11 v3.2 §4.4.1). Skips a token that does not report it. - Assert_KeyComponent_IsReadOnly: C_SetAttributeValue of an AES key's CKA_VALUE, an RSA public key's CKA_MODULUS or CKA_PUBLIC_EXPONENT, an EC public key's CKA_EC_POINT, and C_CopyObject with a new CKA_VALUE, answer CKR_ATTRIBUTE_READ_ONLY and the value reads as before (no footnote 8 in §6.7, §6.1.2, §6.3.3; §4.1.2, §4.1.3, §4.1.1 point 3). - Assert_ProvenanceAttributeInATemplate_IsReadOnly: CKA_ALWAYS_SENSITIVE or CKA_NEVER_EXTRACTABLE in C_CreateObject or C_GenerateKey, each the opposite of what the token would record, answers CKR_ATTRIBUTE_READ_ONLY and makes nothing (§4.11 footnotes 2 and 4, §4.1.1 points 3 and 6). - ReadOnlyKeyAttributes gets CKA_TOKEN, CKA_PRIVATE and CKA_MODIFIABLE, which change only in a copy (§4.4, §4.1.3); Kryoptic's own list too. From wolfSSL/wolfPKCS11#213, #235, #236 Pass: wolfPKCS11 (everything); SoftHSM2, Craton HSM and FreeHSM skip the CKA_UNIQUE_ID case (not reported). SoftHSM2 refuses the provenance templates with CKR_ATTRIBUTE_READ_ONLY but leaves the object behind, recorded through the case's leavesAnObjectBehind parameter. Gated: - BouncyHsm gives a copy the original's CKA_UNIQUE_ID (BouncyHsmGivesACopyItsOwnUniqueId), changes key material through C_SetAttributeValue and C_CopyObject, every row (BouncyHsmFixesKeyMaterial), and changes CKA_PRIVATE and CKA_MODIFIABLE (BouncyHsmFixesThePrivateAndModifiableFlags), each with a tripwire. It passes the provenance case. - Kryoptic refuses a caller's CKA_UNIQUE_ID with CKR_ATTRIBUTE_TYPE_INVALID (create) and CKR_ACTION_PROHIBITED (set) (KryopticRefusesAUniqueIdWithReadOnly), changes an AES key's CKA_VALUE through C_SetAttributeValue and C_CopyObject (KryopticFixesASecretKeysValue), and takes the provenance attributes in C_GenerateKey, refusing them in C_CreateObject with CKR_ATTRIBUTE_TYPE_INVALID (KryopticRefusesProvenanceAttributesInATemplate). - FreeHSM answers a change to RSA or EC public key material with CKR_ATTRIBUTE_TYPE_INVALID and a new CKA_VALUE in a copy with CKR_TEMPLATE_INCONSISTENT (FreeHsmReportsKeyMaterialAsReadOnly), and takes the provenance attributes in a template (FreeHsmRefusesProvenanceAttributesInATemplate). - Craton HSM takes the provenance attributes in a template (CratonHsmRefusesProvenanceAttributesInATemplate). NSS and opencryptoki run in CI only. * test: refuse a malformed attribute value without harm on every backend ObjectConformanceTestCases.Assert_MalformedAttribute_IsRefusedHarmlessly, one row per template, through the raw call: C_CreateObject of a data object with a 4-byte CKA_TOKEN answers CKR_ATTRIBUTE_VALUE_INVALID (PKCS#11 v3.2 §4.1.1 point 2), and with CKA_TOKEN {NULL, 1} or CKA_VALUE {NULL, 16} CKR_ATTRIBUTE_VALUE_INVALID or CKR_ARGUMENTS_BAD, creating nothing; C_FindObjectsInit with CKA_LABEL {NULL, 4} or a 4-byte CKA_TOKEN refuses it with one of those codes or starts a search that matches nothing (§5.7.7). The module still answers C_GetSessionInfo and the session still finds an object afterwards. From wolfSSL/wolfPKCS11#213, #235 Two backends crash the process (SIGSEGV, exit 139) on C_CreateObject with CKA_TOKEN {NULL, 1}; the row is skipped there and has no tripwire, which would take the test host down: - Kryoptic (KryopticRefusesANullAttributeValue, which also covers its CKR_OK and empty value for CKA_VALUE {NULL, 16}). - BouncyHsm (BouncyHsmSurvivesANullAttributeValue). Pass: wolfPKCS11 (every row). Recorded through the case's leavesAnObjectBehind parameter: SoftHSM2 refuses the three CreateObject rows with the right code but leaves the object behind. Gated: - BouncyHsm answers the other rows with CKR_GENERAL_ERROR or CKR_DEVICE_ERROR (BouncyHsmRefusesMalformedAttributeValues, with a tripwire). - Craton HSM creates the three data objects and its search with a 4-byte CKA_TOKEN finds a session object (CratonHsmRefusesMalformedAttributeValues). - FreeHSM's search with a 4-byte CKA_TOKEN finds a session object (FreeHsmMatchesAWrongLengthBoolean; the other rows pass). NSS and opencryptoki run in CI only. * test: hold a copy to what it keeps on every backend Four ObjectConformanceTestCases: - Assert_CopyObject_KeepsCkaToken: with a template that names only CKA_LABEL, the copy of a token data object is a token object and the copy of a session object a session object (PKCS#11 v3.2 §5.7.2). The token objects are destroyed whatever happens. - Assert_CopyObject_KeepsADataObjectsFields: the original and its copy report the CKA_VALUE, CKA_APPLICATION and CKA_OBJECT_ID the original was given (Table 20), and only the label changes. - Assert_UncopyableObject_StaysUncopyable: on an object created with CKA_COPYABLE = FALSE, C_SetAttributeValue(CKA_COPYABLE = TRUE) answers CKR_ATTRIBUTE_READ_ONLY or leaves it FALSE ("Can't be set to TRUE once it is set to FALSE", Table 19), and C_CopyObject still answers CKR_ACTION_PROHIBITED (§4.1.3). - Assert_StorageControls_DefaultToTrue: an object made without CKA_MODIFIABLE, CKA_COPYABLE and CKA_DESTROYABLE reads all three TRUE (Table 19) and can be copied, changed and destroyed. Every copy here names a label: SoftHSM2 answers C_CopyObject with a NULL template CKR_ARGUMENTS_BAD before it looks at the object. From wolfSSL/wolfPKCS11#185, #198, #215 Pass: SoftHSM2, Kryoptic, wolfPKCS11 (all four); BouncyHsm, Craton HSM, FreeHSM (the rest). NSS skips the token object case. Gated: - BouncyHsm sets CKA_COPYABLE back to TRUE (BouncyHsmKeepsCkaCopyableFalse, with a tripwire). - Craton HSM ignores CKA_COPYABLE (CratonHsmKeepsCkaCopyable); the existing UncopyableObject_RefusesCopies wrapper, which skipped for the same reason in its body, now uses the gate. - FreeHSM takes a data object's CKA_OBJECT_ID and does not report it, on the object or its copy (FreeHsmKeepsCkaObjectId). NSS and opencryptoki run in CI only. * test: leave nothing behind a refused key pair generation on every backend ObjectConformanceTestCases.Assert_RefusedKeyPairGeneration_LeavesNothing, one row per refusal, through the raw C_GenerateKeyPair (the library would refuse the curve itself and retries CKR_FUNCTION_FAILED): a 256-bit RSA modulus, below every advertised minimum; an EC curve named by an OID no curve has (1.2.3.4); and a CKA_UNIQUE_ID in the private key's template only (§4.4.1), so that a token which makes the public key first has to undo it. Each call is refused, whatever the code, and no object carries the label both templates give (PKCS#11 v3.2 §5.18.2: "it will fail and return without creating any key objects"). The refused multi-attribute C_SetAttributeValue half of the group is left for tier 2. From wolfSSL/wolfPKCS11#177, #235 Pass: wolfPKCS11, BouncyHsm (every row); SoftHSM2, Kryoptic, Craton HSM, FreeHSM (the RSA and EC rows). Recorded through the case's leavesAnObjectBehind parameter: SoftHSM2 refuses the CKA_UNIQUE_ID row with CKR_ATTRIBUTE_TYPE_INVALID (it has no such attribute) after making the public key, and leaves that key behind. Gated: - Kryoptic generates the pair with the CKA_UNIQUE_ID in the template; its existing KryopticRefusesAUniqueIdWithReadOnly gate now says so. - Craton HSM and FreeHSM generate the pair too, though they do not report CKA_UNIQUE_ID (CratonHsmRefusesAUniqueIdInATemplate, FreeHsmRefusesAUniqueIdInATemplate). NSS and opencryptoki run in CI only. * test: record a key's provenance through PBKDF2, derives and copies on every backend Three KeyProvenanceTestCases: - Assert_CopyOfAKey_KeepsCkaLocal: the copy of a generated AES key is CKA_LOCAL and the copy of an imported one is not (PKCS#11 v3.2 §4.8, Table 26). - Assert_Pbkdf2Key_RecordsItsGeneration: a key from C_GenerateKey(CKM_PKCS5_PBKD2), made sensitive and unextractable, is CKA_LOCAL, has CKA_KEY_GEN_MECHANISM = CKM_PKCS5_PBKD2 and is CKA_ALWAYS_SENSITIVE and CKA_NEVER_EXTRACTABLE (§4.8, §4.11), each of the last three where the token reports it. - Assert_DerivedKey_TakesItsHistoryFromTheBase, one row per mechanism (HKDF, ECDH): from a generated base and from an imported one, both sensitive and unextractable, a key derived sensitive and unextractable is CKA_ALWAYS_SENSITIVE only if the base is and CKA_NEVER_EXTRACTABLE only if the base is (§6.62.3, §6.3.17), applied to what the token reports for each base. ECDH runs with CKD_NULL under the insecure policy: the case is about the attributes. From wolfSSL/wolfPKCS11#178, #233, #237 Pass: wolfPKCS11, BouncyHsm, FreeHSM (every case the token has the mechanism for: no PBKDF2 on BouncyHsm); SoftHSM2 (no HKDF, no PBKDF2); Kryoptic (all but the ECDH row). Craton HSM skips: no CKA_LOCAL (its existing LocalAttributeAvailable gate) and no CKA_ALWAYS_SENSITIVE. Gated: - Kryoptic makes a key derived with ECDH from an imported base (whose CKA_ALWAYS_SENSITIVE is FALSE) CKA_ALWAYS_SENSITIVE (KryopticDerivesAnEcdhKeysHistoryFromTheBase); HKDF follows the rule. NSS (its existing LocalOnGeneratedKeysAvailable gate for the CKA_LOCAL cases) and opencryptoki run in CI only. * test: hold signatures with message recovery to the operation rules on every backend CKM_RSA_PKCS with C_SignRecover and C_VerifyRecover, where the token advertises CKF_SIGN_RECOVER or CKF_VERIFY_RECOVER, with a BCL RSA key imported for the purpose (TestKeys.ImportRsaRecoveryKeyPair), so that a result can be checked against the BCL's PKCS#1 v1.5 signature: - OperationStateTestCases.CallsWithoutInit gets C_SignRecover, C_VerifyRecover, and C_VerifyRecover while an ECDSA C_SignInit is active: CKR_OPERATION_NOT_INITIALIZED (PKCS#11 v3.2 §5.2, §5.13.6, §5.15.6), and the signature still completes. - SecondInits gets SignRecover and VerifyRecover: CKR_OPERATION_ACTIVE, and the first operation returns the BCL's result. - KeysOfTheWrongType gets verify-recover with an AES key or an RSA private key and sign-recover with an RSA public key: CKR_KEY_TYPE_INCONSISTENT or CKR_KEY_FUNCTION_NOT_PERMITTED (§5.1.6). The theory now asks only for the mechanisms its row uses. - UsesOfADestroyedKey gets C_SignRecoverInit and C_VerifyRecoverInit with a destroyed RSA key: CKR_KEY_HANDLE_INVALID or CKR_OBJECT_HANDLE_INVALID. - KeyCapabilityEnforcementTestCases.Assert_Recover_WithRecoverFalse_IsKeyFunctionNotPermitted: a key with CKA_SIGN (CKA_VERIFY) but CKA_SIGN_RECOVER (CKA_VERIFY_RECOVER) FALSE is refused by the recover Init with CKR_KEY_FUNCTION_NOT_PERMITTED (§5.13.5, §5.15.5; the key is of the mechanism's type, so CKR_KEY_TYPE_INCONSISTENT does not apply). - OperationContractTestCases.BufferTooSmallOperations gets RSA sign-recover and verify-recover: a 1-byte buffer answers CKR_BUFFER_TOO_SMALL with a usable length and the retry returns the BCL's result. From wolfSSL/wolfPKCS11#177, #178, #194, #201 Only BouncyHsm (sign and verify recovery) and wolfPKCS11 (verify recovery only) advertise either flag here; SoftHSM2, Kryoptic, Craton HSM and FreeHSM skip every row. Pass: BouncyHsm and wolfPKCS11, every row they run, but for two gated: - wolfPKCS11 answers C_VerifyRecover under an active ECDSA sign with CKR_MECHANISM_INVALID (WolfPkcs11ReportsAVerifyRecoverNeverStarted, with a tripwire). - BouncyHsm answers an RSA key of the wrong class with CKR_KEY_HANDLE_INVALID (BouncyHsmReportsAKeyOfTheWrongClassAsInconsistent, with a tripwire); the AES key row passes. NSS and opencryptoki run in CI only. * test: refuse a key its attributes do not allow with CKR_KEY_FUNCTION_NOT_PERMITTED on every backend KeyCapabilityEnforcementTestCases now asks for the one code PKCS#11 v3.2 §5.1.6 gives for a key of the right type whose attributes do not allow the use, CKR_KEY_FUNCTION_NOT_PERMITTED (CKR_KEY_TYPE_INCONSISTENT, which outranks it, is for a key of the wrong type); it accepted any refusal before. The four existing cases (CKA_SIGN, CKA_VERIFY on an HMAC key; CKA_ENCRYPT, CKA_DECRYPT on an AES key) are tightened, and three new ones go through the raw calls, so the refusal is the module's: - Assert_Derive_WithDeriveFalse_IsKeyFunctionNotPermitted, one row per mechanism (ECDH with CKD_NULL from an EC private key, HKDF from a generic secret): C_DeriveKey from a base key with CKA_DERIVE = FALSE (§4.8 Table 26, §5.18.5) is refused and makes no key. - Assert_Kem_WithCapabilityFalse_IsKeyFunctionNotPermitted, for C_EncapsulateKey with CKA_ENCAPSULATE = FALSE and C_DecapsulateKey with CKA_DECAPSULATE = FALSE (ML-KEM-768; §5.18.8, §5.18.9: "MUST be CK_TRUE"): refused, no key. From wolfSSL/wolfPKCS11#185, #197 Pass: wolfPKCS11, BouncyHsm, FreeHSM (everything); SoftHSM2 (no HKDF; its ML-KEM key-size range skips ML-KEM-768); Craton HSM (no HMAC, HKDF or ML-KEM). Gated: - Kryoptic refuses an HMAC key without CKA_SIGN or CKA_VERIFY with CKR_KEY_TYPE_INCONSISTENT (KryopticRefusesAnUnpermittedMacKeyAsNotPermitted, check_and_fetch_key in src/hmac.rs); the AES, derive and ML-KEM cases pass. - NSS does the same for HMAC keys (SupportsFunctionNotPermittedForMacKeys), as recorded when the class was first run on it. NSS and opencryptoki run in CI only. * test: refuse a key of the wrong type and an object that is no key on every backend - OperationStateTestCases.KeysOfTheWrongType gets two derive rows: C_DeriveKey(CKM_ECDH1_DERIVE) from an AES key and C_DeriveKey(CKM_HKDF_DERIVE) from an EC private key, each base key with CKA_DERIVE so that only its type is wrong (§6.3.17, §6.62.3), answer CKR_KEY_TYPE_INCONSISTENT or CKR_KEY_FUNCTION_NOT_PERMITTED (PKCS#11 v3.2 §5.1.6) and make no key. - Assert_NonKeyObject_IsRefusedAsAKey, one row per use: a session data object or X.509 certificate given to C_SignInit (HMAC) or C_EncryptInit (AES-CBC), as the wrapping key of C_WrapKey or the unwrapping key of C_UnwrapKey (AES key wrap), is refused, nothing is unwrapped and the session still digests. The spec singles out no code: §5.1.6 has CKR_KEY_HANDLE_INVALID cover a handle to an object that is not a key, but CKR_KEY_TYPE_INCONSISTENT and CKR_KEY_FUNCTION_NOT_PERMITTED fit too, so the Init rows accept the three; the wrap rows accept CKR_(UN)WRAPPING_KEY_HANDLE_INVALID, CKR_(UN)WRAPPING_KEY_TYPE_INCONSISTENT (§5.18.3, §5.18.4) or CKR_KEY_FUNCTION_NOT_PERMITTED. From wolfSSL/wolfPKCS11#194, #263 Pass: wolfPKCS11, FreeHSM (every row); SoftHSM2 (no HKDF). Gated: - BouncyHsm answers every non-key row with CKR_OBJECT_HANDLE_INVALID (BouncyHsmRefusesANonKeyObjectAsAKey, with a tripwire); the derive rows pass. - Kryoptic answers a non-key wrapping or unwrapping key with CKR_GENERAL_ERROR (KryopticRefusesANonKeyObjectAsAWrappingKey) and an HKDF base key of the wrong type with CKR_KEY_HANDLE_INVALID (KryopticReportsAnHkdfBaseKeyOfTheWrongTypeAsInconsistent). - Craton HSM derives an ECDH key from an AES key, using its value as the private scalar (CratonHsmChecksTheDeriveBaseKeyType); it has no HKDF or HMAC, and the other non-key rows pass. NSS and opencryptoki run in CI only. * test: keep the session usable when an active operation's key is destroyed on every backend OperationStateTestCases.Assert_KeyDestroyedMidOperation_SessionStaysUsable, one row per operation: after C_SignInit (ECDSA), or C_EncryptInit (AES-GCM) and one C_EncryptUpdate, C_DestroyObject of the key, then the call that continues the operation (C_Sign; C_EncryptUpdate and C_EncryptFinal). PKCS#11 v3.2 does not say what happens (§5.7.3 does not mention active operations), so the case asserts only that every call returns, that a result returned is right (the signature verifies, the ciphertext is the BCL's), and that the session stays usable: the final ends the operation whatever it returns (§5.2), so a new Init with another key succeeds, and the session still digests. A module that used the freed key would crash or return a wrong result. Each row was run on its own first; no backend crashed. From wolfSSL/wolfPKCS11#201, #230 Pass: SoftHSM2, Kryoptic, FreeHSM, wolfPKCS11 (both rows); Craton HSM (ECDSA; it runs AES-GCM single-part only). Recorded through the case's keepsTheOperation parameter: BouncyHsm answers C_Sign with CKR_GENERAL_ERROR once the key is gone and keeps the signature active, which C_SessionCancel ends. NSS skips the AES-GCM row (single-part only). NSS and opencryptoki run in CI only. * test: hold output buffers to the length conventions, and guard past them, on every backend PKCS#11 v3.2 §5.2 and §5.7.5 through the raw calls. Every short buffer below is the start of a larger one filled with a canary, room for the whole output and more, and the bytes past the declared length must come back unchanged, so a module that ignores the length writes into memory the case owns. - BufferConventionTestCases.MultipartFinals gets AES-CBC-PAD encrypt and decrypt: C_EncryptFinal / C_DecryptFinal into a 1-byte buffer answers CKR_BUFFER_TOO_SMALL with a usable length and keeps the operation, and the retry completes the BCL's result. Every row now checks the canary. - Assert_CbcPadEncrypt_LengthQuery_CoversThePaddingBlock (16, 32, 48 bytes): the C_Encrypt length query covers the extra padding block and a buffer of exactly that size takes the ciphertext. - Assert_Digest_LengthQuery_KeepsTheOperation: C_Digest with data and a NULL output answers CKR_OK with 32 and keeps the operation. - Assert_GetAttributeValue_ShortBuffer_WritesNothingPastIt: CKA_LABEL, CKA_EC_POINT (1 byte, and 65, the raw point's length), CKA_EC_PARAMS and CKA_MODULUS into a short buffer answer CKR_BUFFER_TOO_SMALL, write nothing past it and then read in full. ulValueLen stays the existing ShortBuffer_IsUnavailableInformation case's. - Assert_GetAttributeValue_LargeBuffer_SetsTheExactLength: CKA_LABEL, CKA_EC_PARAMS and an ML-DSA key's CKA_PARAMETER_SET into a larger buffer set ulValueLen to the exact length. - OperationContractTestCases.BufferTooSmallOperations gets SHA-256 C_Digest, AES-CBC-PAD C_Decrypt and C_DecryptUpdate (which may also hold every block back and answer CKR_OK with nothing), and AES-KW and AES-KWP C_Decrypt; every row now checks the canary. - Assert_CbcPad_MultipartEncrypt_MatchesBcl: C_EncryptUpdate in 7-byte pieces, then C_EncryptFinal, whose length query covers the last part and whose output completes the BCL's ciphertext. - Assert_AesCtr_LengthQuery_ReportsTheInputLength: the C_Encrypt and C_Decrypt length queries for CKM_AES_CTR (a CK_AES_CTR_PARAMS with a 128-bit counter, §6.11.2) report the input's length, and the result matches a CTR built from the BCL's AES-ECB. From wolfSSL/wolfPKCS11#179, #189, #194, #212, #237, #238, #241 No backend wrote past a buffer. Pass: SoftHSM2 (no AES-KW/KWP for C_Decrypt), wolfPKCS11 (no AES-CTR or KWP), everything else on every backend the mechanisms exist for, but for these, gated: - wolfPKCS11 answers a CBC-PAD C_Decrypt into a buffer too small with CKR_BUFFER_TOO_SMALL and the length of all blocks but the last (32 for 43 bytes of plaintext) (WolfPkcs11ReportsTheWholeCbcPadPlaintextLength, with a tripwire). - Kryoptic answers a CBC-PAD C_DecryptFinal into a buffer too small with CKR_ENCRYPTED_DATA_INVALID and ends the operation (KryopticKeepsACbcPadDecryptFinalOnAShortBuffer). - FreeHSM answers a public key's CKA_MODULUS, CKA_EC_POINT or CKA_EC_PARAMS read into a buffer too small with CKR_OK (FreeHsmReportsAShortBufferForKeyAttributes); the label row passes. - BouncyHsm and Craton HSM take a bare 16-byte counter block for CKM_AES_CTR, not a CK_AES_CTR_PARAMS (BouncyHsmTakesAesCtrParams, with a tripwire; CratonHsmTakesAesCtrParams). NSS and opencryptoki run in CI only. * test: refuse a ciphertext or wrapped key invalid by its length, and a tampered key-wrap blob, on every backend - OperationStateTestCases.Assert_ShortCiphertext_Decrypt_IsRefused, one row per ciphertext: AES-GCM of 0 and 15 bytes (shorter than the tag, §6.13.2), AES-CBC-PAD of 0 and 15 bytes, AES-KW and AES-KWP of 8 and 20 bytes (under two semiblocks, or not a whole number of them). The length query, then the call into a buffer if the query answered CKR_OK, is refused with CKR_ENCRYPTED_DATA_LEN_RANGE or CKR_ENCRYPTED_DATA_INVALID, which PKCS#11 v3.2 §5.10.2 leaves to the module, and the refusal ends the operation (§5.2), so the next C_DecryptInit succeeds. - AlgorithmConformanceTestCases.Assert_AesKeyWrap_TamperedBlob_IsRefused now asks for CKR_WRAPPED_KEY_INVALID for a blob that fails RFC 3394's integrity check (it accepted any refusal before), the truncated 32-byte blob included, and gets an 8-byte and a 20-byte row that ask for CKR_WRAPPED_KEY_LEN_RANGE, which outranks it for a blob invalid by its length alone (§5.1.6, §5.18.4). - Assert_RsaOaep_UnwrapOfABlobNotKBytes_IsWrappedKeyLenRange: an OAEP blob one byte short of a 2048-bit modulus, or one byte over, is refused with CKR_WRAPPED_KEY_LEN_RANGE (RFC 8017 §7.1.2), and the k-byte blob then unwraps. From wolfSSL/wolfPKCS11#169, #189, #197, #243 Pass: FreeHSM (every row it has the mechanisms for); wolfPKCS11 (no AES-KWP, no OAEP unwrap) but for one row, gated. Recorded through the short-ciphertext case's keepsTheOperation parameter: BouncyHsm keeps the decryption active after the refusal, as it does for a partial block. Gated: - wolfPKCS11 answers a 20-byte AES-KW unwrap with CKR_WRAPPED_KEY_INVALID, the C_Decrypt code it maps (WolfPkcs11RefusesAKeyWrapBlobByItsLength, with a tripwire). - BouncyHsm decrypts an empty CBC-PAD ciphertext to nothing with CKR_OK (BouncyHsmRefusesAnEmptyCbcPadCiphertext, with a tripwire) and answers an OAEP blob of the wrong length with CKR_GENERAL_ERROR (BouncyHsmRefusesAnOaepBlobByItsLength, with a tripwire). - SoftHSM2 answers a blob that fails the integrity check with CKR_GENERAL_ERROR (SoftHsmReportsATamperedKeyWrapBlobAsInvalid; the length rows pass) and an OAEP blob of the wrong length the same way (SoftHsmRefusesAnOaepBlobByItsLength). It has no AES-KW C_Decrypt. - Kryoptic answers a tampered or 8-byte AES-KW blob with CKR_DEVICE_ERROR and a 20-byte one with CKR_DATA_LEN_RANGE (KryopticReportsATamperedKeyWrapBlobAsInvalid), a one-semiblock AES-KW or AES-KWP C_Decrypt with CKR_DEVICE_ERROR after a length query that answered CKR_OK (KryopticRefusesAOneSemiblockKeyWrapCiphertext), and a short OAEP blob with CKR_DEVICE_ERROR (KryopticRefusesAnOaepBlobByItsLength). - Craton HSM answers a tampered AES-KW blob with CKR_ENCRYPTED_DATA_INVALID and a short one with CKR_DATA_LEN_RANGE (CratonHsmReportsATamperedKeyWrapBlobAsInvalid); it has no AES-KW C_Decrypt or OAEP unwrap, and its short-ciphertext rows pass. NSS and opencryptoki run in CI only. * test: run AES-GCM in odd pieces, and refuse C_Verify after C_VerifyUpdate, on every backend - OperationContractTestCases.Assert_AesGcm_MultipartInOddPieces_MatchesBcl, one row per piece length (1, 15, 17, 4097): AES-GCM with AAD, fed through several C_EncryptUpdate (C_DecryptUpdate) calls of that length, none a whole number of blocks, and a shorter last one, then the final, gives the BCL's ciphertext and tag (plaintext), whatever the module releases at each update (PKCS#11 v3.2 §6.13.2). - OperationStateMixingTestCases gets the Verify kind the class lacked: after C_VerifyInit(HMAC-SHA256) and C_VerifyUpdate, the single-part C_Verify "cannot be used to terminate a multi-part operation" (§5.15.2). Assert_Verify_SinglePartAfterUpdate_Throws refuses it whether the MAC is over both parts or the second only; the TreatsAsContinuation and DiscardsTheUpdate variants pin what a backend does instead, as for Digest, Sign and Encrypt, and the class remarks' matrix gets the Verify row. From wolfSSL/wolfPKCS11#198, #240 GCM: pass on SoftHSM2, Kryoptic, BouncyHsm and wolfPKCS11; Craton HSM and FreeHSM run AES-GCM single-part only (SupportsMultipartAesGcm), and NSS has the existing MultipartClassicAesGcmAvailable gate. Verify: SoftHSM2 and Kryoptic refuse it (Throws). BouncyHsm and wolfPKCS11 verify over both parts (TreatsAsContinuation); FreeHSM verifies the second part only and ends the operation (DiscardsTheUpdate). Craton HSM has no HMAC and skips. NSS is wired as a continuation, as its Sign is, unconfirmed. NSS and opencryptoki run in CI only. * test: hold raw RSA verification to the data it signs on every backend Two AlgorithmConformanceTestCases for CKM_RSA_X_509, through the raw dispatch (raw RSA is outside the library's policy), with a 2048-bit key pair and a full 256-byte block m whose top byte is 0x01: - Assert_RsaX509_Verify_RequiresTheLeadingBytes: the signature over m verifies m and, with CKR_SIGNATURE_INVALID, not m[1..]. Raw RSA compares the zero-padded data with the whole recovered block (PKCS#11 v3.2 §6.1.12), so only leading zero bytes may be left out. - Assert_RsaX509_Verify_OfDataLongerThanTheModulus_IsRefused: under a token key, C_Verify of 257 bytes (more than k, §6.1.12 Table 45) is refused with CKR_DATA_LEN_RANGE, or CKR_SIGNATURE_INVALID from a module that checks the signature against the data as given; the token key then verifies m and signs it again at once, so the module did not leave the key locked on the error. From wolfSSL/wolfPKCS11#163, #240 Pass: Kryoptic (both); SoftHSM2 and wolfPKCS11 (the leading-bytes case). BouncyHsm, Craton HSM and FreeHSM do not offer CKM_RSA_X_509 for signatures and skip. Gated: - SoftHSM2 ends the test process inside C_Verify of the 257 bytes: AsymVerify in src/lib/SoftHSM.cpp zero-pads with data.wipe(size - ulDataLen), which wraps around (SoftHsmSurvivesAnOverlongRawRsaVerify). Confirmed by running the case alone and by a probe around the call. - wolfPKCS11 answers it with CKR_FUNCTION_FAILED; the token key works afterwards (WolfPkcs11RefusesOverlongRawRsaDataByItsLength, with a tripwire that also checks the key). NSS and opencryptoki run in CI only. * test: refuse C_DigestKey of a private key, and without a digest, on every backend - AlgorithmConformanceTestCases.Assert_DigestKey_OfAPrivateKey_IsKeyIndigestible: after C_DigestInit(SHA-256) and C_DigestUpdate, C_DigestKey of an EC private key answers CKR_KEY_INDIGESTIBLE (PKCS#11 v3.2 §5.12.4 digests "the value of a secret key"; §5.1.6: "perhaps the key isn't a secret key"). The digest then either goes on, and C_DigestFinal gives the digest of the data fed before, or was ended (CKR_OPERATION_NOT_ INITIALIZED); either way the next C_DigestInit succeeds. - OperationStateTestCases.CallsWithoutInit gets "C_DigestKey": with no digest active, C_DigestKey of a secret key answers CKR_OPERATION_NOT_INITIALIZED (§5.2, §5.12.4). From wolfSSL/wolfPKCS11#178, #185, #241 Pass: wolfPKCS11, BouncyHsm, Craton HSM, FreeHSM (both); SoftHSM2 and Kryoptic (the CallsWithoutInit row). Gated: - SoftHSM2 digests the CKA_VALUE of any object, a sensitive EC private key's scalar included, and answers CKR_OK: confirmed with an imported P-256 key, whose digest is SHA-256 of its scalar (SoftHsmRefusesToDigestAPrivateKey). - Kryoptic answers CKR_KEY_HANDLE_INVALID for a key that is not a secret key (KryopticReportsAPrivateKeyAsIndigestible). NSS and opencryptoki run in CI only. * test: refuse the misuses of saving and restoring an operation's state on every backend AlgorithmConformanceTestCases.Assert_OperationState_Misuse_IsRefused, next to DigestState_SavedAndRestored, one row per misuse, for a SHA-256 digest's state saved after one part and restored into the session that saved it: - C_GetOperationState with no operation active answers CKR_OPERATION_NOT_INITIALIZED (PKCS#11 v3.2 §5.6.6); - C_SetOperationState of random bytes of the state's length, or of the state cut by its last byte, answers CKR_SAVED_STATE_INVALID (§5.6.7); - C_SetOperationState with an encryption key or an authentication key, which a digest does not use, answers CKR_KEY_NOT_NEEDED (§5.1.6). Both functions are optional, so CKR_FUNCTION_NOT_SUPPORTED is taken; the rows that need a saved state skip where the module cannot save one (CKR_STATE_UNSAVEABLE), and the random-bytes row then sends 64 bytes. Whether a refused restore leaves the operation as it was is not stated, so only the code is asserted. From wolfSSL/wolfPKCS11#230 Pass: wolfPKCS11 (every row); SoftHSM2, Kryoptic, BouncyHsm and FreeHSM (no operation, random bytes; they cannot save a digest's state); Craton HSM but for two rows, gated: it restores the state given a key it does not need and answers CKR_OK (CratonHsmRefusesAKeyNotNeeded). NSS and opencryptoki run in CI only. * test: refuse out-of-range AES-GCM and AES-CCM parameters on every backend New MechanismParameterTestCases, with a wrapper per backend, for parameters the library's own types refuse before any native call: each case lays the CK_*_PARAMS struct out itself (RawParameter.From, with NativeBuffers for its pointers) and sends it through the raw dispatch. Assert_AeadParameter_OutOfRange_IsMechanismParamInvalid, one row per fault: CK_GCM_PARAMS with a NULL pIv behind ulIvLen 12, ulIvLen 0, a NULL pAAD behind ulAADLen 9, ulTagBits 136; CK_CCM_PARAMS with a NULL pNonce, a nonce of 6 or 14 bytes, a NULL pAAD, ulMACLen 2, 5 or 18. PKCS#11 v3.2 §6.13.7 gives the ranges (IV 1..2^32-1 bytes, tag 0..128 bits, nonce 7..13 bytes, MAC 4..16 even) and §6.13.2, §6.13.4 let pAAD be NULL only for ulAADLen 0. C_EncryptInit and C_DecryptInit answer CKR_MECHANISM_PARAM_INVALID (§5.1.6), or CKR_ARGUMENTS_BAD for a NULL pointer; after each refusal the same session runs the Init with good parameters and gets the BCL's ciphertext (plaintext). From wolfSSL/wolfPKCS11#186, #194, #238 Pass: wolfPKCS11 (every row); Craton HSM (the GCM rows; no CCM). Gated: - SoftHSM2 ends the test process on a NULL pIv or pAAD behind a length: SymEncryptInit and SymDecryptInit in src/lib/SoftHSM.cpp memcpy from the pointer unchecked (SoftHsmSurvivesANullGcmPointer; each row run alone). It takes ulIvLen 0 and refuses ulTagBits 136 with CKR_ARGUMENTS_BAD (SoftHsmRefusesAnOutOfRangeGcmParameterAsInvalid). It has no CCM. - Kryoptic takes a NULL pAAD behind a length as no AAD, for GCM and CCM (KryopticRefusesANullAadPointer; a probe's ciphertext and tag were the BCL's with empty AAD), and refuses a NULL CCM pNonce with CKR_DEVICE_ERROR (KryopticRefusesANullCcmNonceAsInvalid). - BouncyHsm refuses every out-of-range field with CKR_GENERAL_ERROR (BouncyHsmRefusesAnOutOfRangeAeadParameterAsInvalid) and takes a NULL pAAD as no AAD (BouncyHsmRefusesANullAadPointer), each with a tripwire. - FreeHSM refuses ulTagBits 136 with CKR_ARGUMENTS_BAD (FreeHsmRefusesAnOutOfRangeGcmTagAsInvalid); the other rows pass. NSS (GCM rows behind RequireClassicAesGcm) and opencryptoki run in CI only. * test: refuse an AES-CTR counter width out of range, and a counter overflow, on every backend Two MechanismParameterTestCases for CKM_AES_CTR, through the raw dispatch: - Assert_AesCtr_CounterBitsOutOfRange_IsMechanismParamInvalid (0, 129): PKCS#11 v3.2 §6.11.2 has ulCounterBits in 0 < n <= 128 and "for any values outside this range the mechanism shall return CKR_MECHANISM_PARAM_INVALID"; checked at C_EncryptInit and C_DecryptInit, each followed in the same session by an Init with a 128-bit counter whose result is the BCL's. - Assert_AesCtr_CounterOverflow_IsDataLenRange: §6.11.3's own example. With ulCounterBits 2 and the counter bits starting at 1, three blocks encrypt to the BCL's CTR and a fourth is refused with CKR_DATA_LEN_RANGE ("an attempt ... which will cause an overflow of the counter block's counter bits"), rather than wrapping or carrying into the nonce; the next C_EncryptInit succeeds. From wolfSSL/wolfPKCS11#197, #212 Pass: SoftHSM2 (both). wolfPKCS11 has no AES-CTR. BouncyHsm and Craton HSM take a bare counter block, not CK_AES_CTR_PARAMS, and keep their AesCtrParamsAvailable gates. Gated (the counter-bits rows pass on both): - Kryoptic refuses the three-block message with CKR_DATA_LEN_RANGE: its final step in src/ossl/aes.rs refuses once blockctr reaches maxblocks, so the last counter value is never used (KryopticEncryptsTheLastCounterValue). - FreeHSM encrypts the four blocks with CKR_OK: it checks ulCounterBits for 1..128 and runs a 128-bit counter (src/fhsm_pkcs11.c), carrying into the nonce (FreeHsmRefusesAnAesCtrCounterOverflow). NSS and opencryptoki run in CI only. * test: refuse MAC and signature parameters out of range, and a NULL OAEP label, on every backend Through the raw dispatch, as the library's parameter types take none of these: - MechanismParameterTestCases.Assert_MacGeneral_LengthOutOfRange_IsMechanismParamInvalid: C_SignInit with CKM_SHA256_HMAC_GENERAL and a MAC length of 33, or CKM_AES_CMAC_GENERAL and 17, is refused with CKR_MECHANISM_PARAM_INVALID (PKCS#11 v3.2 §6.22.3: 1-32; §6.14.3, Table 121: 1 to the block size; §5.1.6), and so are 2^32 + 16 and 2^32 + 8 where CK_ULONG is 64 bits, which a module that narrows the length takes for 16 and 8. A length in range then gives the start of the full MAC (the BCL's HMAC-SHA256, RFC 4493's CMAC). - AlgorithmConformanceTestCases.Assert_RsaPss_ParameterOutOfRange_IsMechanismParamInvalid: CKM_SHA256_RSA_PKCS_PSS with hashAlg CKM_SHA384 (§6.1.9: it "must match the hash algorithm indicated by the signature mechanism") or an sLen of 2^32 + 32 (§6.1.15: at most k* - 2 - hLen; 64-bit CK_ULONG only) is refused by C_SignInit and C_VerifyInit with CKR_MECHANISM_PARAM_INVALID, or taken by the Init and refused by C_Sign (§5.13.2 names no code there) and not verified; nothing is signed. SHA-256 with a 32-byte salt then signs and verifies. - EncryptRsaOaepLabelTestCases.Assert_OaepNullLabelWithLength_IsRefused: a NULL pSourceData behind ulSourceDataLen 8 (§6.1.7: the length "must be 0" for the default label) is refused by C_EncryptInit and C_DecryptInit with CKR_MECHANISM_PARAM_INVALID or CKR_ARGUMENTS_BAD; the empty label then round-trips. From wolfSSL/wolfPKCS11#238, #240 Pass: wolfPKCS11 (everything it has; no HMAC_GENERAL); FreeHSM and Craton HSM (OAEP; neither has HMAC_GENERAL or CMAC_GENERAL); SoftHSM2 (OAEP; no general-length MACs); Kryoptic (MACs, PSS hashAlg). Craton HSM's PSS rows run through the existing ignoresPssParameters: it ignores hashAlg too, and signs as with SHA-256 and a 32-byte salt. Gated: - BouncyHsm cuts a CK_ULONG parameter length to 32 bits: HMAC_GENERAL with 2^32 + 16 signs 16 bytes, CMAC_GENERAL with 2^32 + 8 is taken, PSS with sLen 2^32 + 32 signs with a 32-byte salt (BouncyHsmRefusesAParameterLengthAbove32Bits, tripwire), and it takes a NULL OAEP label as the empty one (BouncyHsmRefusesANullOaepLabel, tripwire). Its 33 and 17 rows and the hashAlg row pass. - Kryoptic takes a NULL OAEP label as the empty one (KryopticRefusesANullOaepLabel) and refuses sLen 2^32 + 32 with CKR_GENERAL_ERROR (KryopticRefusesAnOverlongPssSaltAsInvalid). - SoftHSM2 refuses the PSS hashAlg with CKR_ARGUMENTS_BAD and the sLen with CKR_MECHANISM_INVALID (SoftHsmRefusesAnOutOfRangePssParameterAsInvalid). - FreeHSM takes hashAlg CKM_SHA384 and signs with SHA-256 (FreeHsmRefusesAMismatchedPssHash); its sLen row passes. Each behaviour was confirmed by a probe of the call. NSS and opencryptoki run in CI only. * test: refuse ECDH parameters out of range on every backend AlgorithmConformanceTestCases.Assert_Ecdh_ParameterOutOfRange_IsRefused, one row per fault, for CKM_ECDH1_DERIVE with CKD_NULL from the SP 800-56A P-256 vector, through the raw dispatch (the library's parameter type refuses the first three): - CKD_NULL with shared data: PKCS#11 v3.2 §6.3.16, "pSharedData must be NULL and ulSharedDataLen must be zero"; - a 1 KiB peer value (a raw point padded with zeros), and a DER OCTET STRING whose length byte promises 65 bytes and has 40 (§6.3.16: the peer's point, raw or DER); each refused with CKR_MECHANISM_PARAM_INVALID (§5.1.6); - CKA_VALUE_LEN 33 for a generic secret, one byte more than the secret the mechanism can only truncate (§6.3.17): CKR_ATTRIBUTE_VALUE_INVALID, CKR_TEMPLATE_INCONSISTENT (§4.1) or CKR_KEY_SIZE_RANGE (§5.18.5). None makes a key (found by label), and the same session then derives Z, checked through an HMAC under it where the module has HMAC-SHA256. From wolfSSL/wolfPKCS11#233, #234 Pass: Craton HSM (every row). Gated, each confirmed by the run and, for the keys made, a probe reading them back: - wolfPKCS11 answers the lying DER length with CKR_FUNCTION_FAILED (WolfPkcs11RefusesAMalformedEcdhPeerAsInvalid, tripwire); - Kryoptic answers it with CKR_DEVICE_ERROR (KryopticRefusesAMalformedEcdhPeerAsInvalid); - SoftHSM2 answers both peer rows with CKR_GENERAL_ERROR (SoftHsmRefusesAMalformedEcdhPeerAsInvalid) and CKA_VALUE_LEN 33 with CKR_FUNCTION_FAILED (SoftHsmRefusesAnEcdhValueLenAboveTheSecret); - BouncyHsm ignores the shared data and derives Z (BouncyHsmRefusesSharedDataWithCkdNull, tripwire) and makes 00 || Z for CKA_VALUE_LEN 33 (BouncyHsmRefusesAnEcdhValueLenAboveTheSecret, tripwire); its peer rows pass; - FreeHSM departs on every row: it ignores the shared data, makes a 32-byte key for CKA_VALUE_LEN 33 and refuses both peers with CKR_ATTRIBUTE_VALUE_INVALID (FreeHsmChecksTheEcdhParameters). The other rows pass on each. NSS and opencryptoki run in CI only. * test: refuse an HKDF output past RFC 5869's limit, and a salt key that is no secret key, on every backend HkdfTestCases.Assert_Hkdf_ParameterOutOfRange_IsRefused, through the raw dispatch (the library's HKDF API refuses these before any native call), from a 32-byte generic secret with extract and expand under SHA-256: - CKA_VALUE_LEN 8161 (255 x 32 + 1), past what RFC 5869's expand gives (PKCS#11 v3.2 §6.62.3), and 2^32 + 1 where CK_ULONG is 64 bits (a module that narrows it makes a 1-byte key): CKR_ATTRIBUTE_VALUE_INVALID, CKR_TEMPLATE_INCONSISTENT (§4.1) or CKR_KEY_SIZE_RANGE (§5.18.5); - CKF_HKDF_SALT_KEY with an EC public key as hSaltKey: §6.62.2 names no code, so CKR_MECHANISM_PARAM_INVALID, CKR_KEY_HANDLE_INVALID or CKR_KEY_TYPE_INCONSISTENT. None makes a key (found by label); the same session then derives the BCL's HKDF output, checked through an HMAC under it. Left out: CKA_VALUE_LEN 0 or missing with bExpand. §6.62.3 says only that CKA_VALUE_LEN "should be set", and CKA_VALUE_LEN is not required for C_DeriveKey by Table 13, so no refusal can be asserted. From wolfSSL/wolfPKCS11#173, #201, #234 Pass: FreeHSM (every row). SoftHSM2, opencryptoki and Craton HSM have no HKDF (the class has no SoftHSM2 or opencryptoki wrapper). Gated: - wolfPKCS11 refuses 8161 with CKR_FUNCTION_FAILED (WolfPkcs11RefusesAnHkdfOutputAboveTheRfcLimit, tripwire); - Kryoptic refuses it with CKR_DEVICE_ERROR (KryopticRefusesAnHkdfOutputAboveTheRfcLimit); - BouncyHsm refuses 8161 with CKR_GENERAL_ERROR and makes a 1-byte key for 2^32 + 1 (BouncyHsmRefusesAnHkdfOutputAboveTheRfcLimit, tripwire; confirmed by reading CKA_VALUE_LEN back), and refuses the salt key with CKR_OBJECT_HANDLE_INVALID (BouncyHsmReportsAnHkdfSaltKeyOfTheWrongTypeAsInconsistent, tripwire). NSS runs in CI only. * test: hold key generation to the sizes, exponent and curve asked on every backend Through the raw dispatch, as the library's key APIs refuse these templates before any native call: - MechanismParameterTestCases.Assert_GenericSecretKeyGen_LengthIsBoundedInBits: CKM_GENERIC_SECRET_KEY_GEN's ulMinKeySize and ulMaxKeySize are in bits (PKCS#11 v3.2 §6.8.3), so C_GenerateKey makes keys of ceil(min / 8) and max / 8 bytes, each reading back that CKA_VALUE_LEN, and refuses max / 8 + 1 and, where CK_ULONG is 64 bits, 2^32 + 16 (CKR_ATTRIBUTE_VALUE_INVALID, CKR_TEMPLATE_INCONSISTENT or CKR_KEY_SIZE_RANGE), making no key. Max rows run up to 16 MiB. - GenerateRsaKeyPairTestCases.Assert_RsaKeyGen_ModulusBitsAboveTheMaximum_IsRefused: CKA_MODULUS_BITS ulMaxKeySize + 8 (where the maximum is at most 8192) and 2^32 + 2048 are refused with those codes (§6.1.4) and never make a key. - GenerateRsaKeyPairTestCases.Assert_RsaKeyGen_PublicExponent_IsTheOneAsked: a pair made with CKA_PUBLIC_EXPONENT 65539 (01 00 03) or 2^64 + 65537 reads that integer back and its PSS signature verifies under the BCL with (n, e); a refusal is CKR_TEMPLATE_INCONSISTENT, as §6.1.4 says ("shall be returned if the implementation cannot use the supplied exponent value"), and skips for 65539. - GenerateEcKeyPairTestCases.Assert_EcKeyGen_CurveOidNotACurve_IsRefused: CKA_EC_PARAMS of the P-256 OID with an arc added or its last arc dropped is refused with CKR_CURVE_NOT_SUPPORTED, CKR_DOMAIN_PARAMS_INVALID or CKR_ATTRIBUTE_VALUE_INVALID. - AlgorithmConformanceTestCases.Assert_Derive_AesKeyOfAnInvalidLength_IsRefused: C_DeriveKey(CKM_ECDH1_DERIVE) of a 13-byte CKK_AES key (§6.10.2) is refused with CKR_ATTRIBUTE_VALUE_INVALID, CKR_TEMPLATE_INCONSISTENT or CKR_KEY_SIZE_RANGE and makes no key; 16 bytes then derives. From wolfSSL/wolfPKCS11#171, #173, #201, #231, #233, #236, #243 Pass: every row each backend has the mechanism for, but for these, gated: - Kryoptic, CKA_VALUE_LEN 2^32 + 16: it sets out to make the 4 GiB key and the kernel's OOM killer ends the test host (exit 137; row run alone, kernel log) (KryopticBoundsAGenericSecretLength). It reports a generic-secret range of [0, 0], so the range rows skip. It refuses the OIDs with CKR_GENERAL_ERROR (KryopticRefusesAnOidThatIsNoCurveAsNotSupported). - BouncyHsm cuts CKA_MODULUS_BITS and CKA_VALUE_LEN to 32 bits: 2^32 + 2048 makes a 2048-bit RSA key, 2^32 + 16 a 16-byte secret (BouncyHsmRefusesAKeyLengthAttributeAbove32Bits, tripwire); it makes a secret past its ulMaxKeySize (BouncyHsmBoundsAGenericSecretByItsMaximum) and refuses the 13-byte AES key with CKR_GENERAL_ERROR (BouncyHsmRefusesAnAesKeyOfAnInvalidLengthAsInvalid, tripwire). - Craton HSM makes a 2048-bit key for 2^32 + 2048 (CratonHsmRefusesAModulusAbove32Bits) and every key with exponent 65537 (CratonHsmKeepsThePublicExponentAsked). No generic secret gen. - FreeHSM makes every key with exponent 65537 (FreeHsmKeepsThePublicExponentAsked), a 9-byte secret under a reported ulMaxKeySize of 64 (FreeHsmBoundsAGenericSecretByItsMaximum) and a 13-byte AES key (FreeHsmRefusesAnAesKeyOfAnInvalidLength). - SoftHSM2 makes a secret past its ulMaxKeySize (SoftHsmBoundsAGenericSecretByItsMaximum), and answers CKR_GENERAL_ERROR for the OIDs (SoftHsmRefusesAnOidThatIsNoCurveAsNotSupported) and for CKA_MODULUS_BITS 2^32 + 2048 and the nine-byte exponent (SoftHsmRefusesAnRsaKeyGenAttributeAsInvalid). - wolfPKCS11 answers CKR_FUNCTION_FAILED for the nine-byte exponent (WolfPkcs11RefusesAnRsaExponentAsInconsistent, tripwire) and for a generic secret past its maximum (WolfPkcs11RefusesAGenericSecretLengthAsInvalid, tripwire). NSS and opencryptoki run in CI only. * test: skip the NULL AES-GCM pointer rows that end the NSS test host Fixes the review finding (critical) that NSS would crash on MechanismParameterTestCases' AEAD rows "GCM pIv NULL" and "GCM pAAD NULL". Run alone against a softoken built from vendor/nss at the pinned 34716626, each row ends the test host with SIGSEGV (exit code 139): sftk_CryptInit hands both pointers on unchecked and freebl's gcm_InitCounter copies the IV and hashes the AAD from them. NssBackendFixture.NssSurvivesANullGcmPointer (false) records it, and the NSS wrapper skips the two rows, as SoftHsmSurvivesANullGcmPointer does for SoftHSM2. The other NULL-pointer-with-length rows of the new cases were run alone on NSS too: CCM pNonce/pAAD NULL skip (no CKM_AES_CCM), the OAEP NULL label case passes, and the malformed-attribute NULL rows fail without a crash (gated with their class). * test: run the over-long raw RSA verify with a session key pair where there are no token objects Fixes the review finding that Assert_RsaX509_Verify_OfDataLongerThanTheModulus_IsRefused generated a token key pair unconditionally: on NSS, whose token is write-protected, C_GenerateKeyPair failed with CKR_TOKEN_WRITE_PROTECTED before the case reached its check. It now asks for a token pair only where backend.SupportsTokenObjects, as its neighbours do. No other case added since b221bdd1 creates a token object without that check (the others skip, or are wolfPKCS11 tripwires). On NSS the case now reaches C_Verify and fails there (CKR_DEVICE_ERROR), gated with the AlgorithmConformance NSS departures. Unchanged elsewhere: Kryoptic passes; the backends gated for it before still are. * test: take a restore of random bytes of a saved state's length as valid Fixes the review finding on Assert_OperationState_Misuse_IsRefused's "restore random bytes" row: PKCS#11 v3.2 §5.6.7 asks for CKR_SAVED_STATE_INVALID only of a state the module "can determine is not valid", so a module whose saved state carries no integrity check may take random bytes of the right length. The row now accepts CKR_OK too, and ends the digest the bytes restored with C_DigestFinal. The truncated state row and the 64-byte row (where no state can be saved) stay strict. No gate was added for that row in 21d5e874 (its only gate, CratonHsmRefusesAKeyNotNeeded, is for the key-not-needed rows), so none is removed. No local backend takes the random bytes: wolfPKCS11, Craton HSM and NSS, which save a digest's state, refuse them; SoftHSM2, Kryoptic, BouncyHsm and FreeHSM take the 64-byte row. All pass. * test: run the rows the Craton HSM template gates and the NSS CKA_LOCAL gate hid Fixes the review finding that whole-theory gates hid rows their flag does not describe: - ObjectConformanceTests_CratonHsm.IncompleteTemplate_IsTemplateIncomplete (CratonHsmRequiresTheKeyAttributes): "EC key pair without CKA_EC_PARAMS" passes on Craton HSM (refused with CKR_TEMPLATE_INCOMPLETE), so the gate is a body check on the other six rows, which still fail as the flag says. - ObjectConformanceTests_CratonHsm.MalformedAttribute_IsRefusedHarmlessly (CratonHsmRefusesMalformedAttributeValues): "C_FindObjectsInit with CKA_LABEL NULL and ulValueLen 4" passes, so it runs; the other four rows fail as documented. - KeyProvenanceTests_Nss.Pbkdf2Key_RecordsItsGeneration (SupportsLocalOnGeneratedKeys): the case takes checkLocal, and NSS runs it without the CKA_LOCAL check; its CKA_KEY_GEN_MECHANISM, CKA_ALWAYS_SENSITIVE and CKA_NEVER_EXTRACTABLE checks pass there. Both Craton HSM flags' doc comments say which row runs. Pass: Craton HSM (the two rows), NSS (Pbkdf2), and Pbkdf2 where it ran before (Kryoptic, FreeHSM, wolfPKCS11). * test: gate the NSS departures MechanismParameterTestCases finds Fixes the review finding that the new cases had never run on NSS: run against a softoken built from vendor/nss at the pinned 34716626, these rows fail, each a departure from PKCS#11 v3.2 recorded on NssBackendFixture (false while it stands): - "GCM ulIvLen 0", "GCM ulTagBits 136": CKR_HOST_MEMORY (§6.13.7) (NssRefusesAnOutOfRangeGcmParameterAsInvalid); - AesCtr_CounterBitsOutOfRange 0 and 129: CKR_HOST_MEMORY (§6.11.2) (NssRefusesAnOutOfRangeCtrCounterWidthAsInvalid); - AesCtr_CounterOverflow: three blocks under a 2-bit counter starting at 1, §6.11.3's own example, are refused with CKR_DATA_LEN_RANGE; freebl allows 2^counterBits - 2 blocks (NssRunsTheCtrBlocksItsCounterAllows); - MacGeneral, every row: CKM_SHA256_HMAC_GENERAL and CKM_AES_CMAC_GENERAL refuse every length, the good one too, with CKR_MECHANISM_PARAM_INVALID: sftk_doMACInit hands the _GENERAL mechanism to sftk_MAC_InitRaw, which knows only the full-length ones (NssRunsTheGeneralLengthMacs); - GenericSecretKeyGen "ulMaxKeySize / 8 + 1": a 5-byte key past the ulMaxKeySize 32 it reports (§6.8.3) (NssBoundsAGenericSecretByItsMaximum). The reviewer's predictions from nss-dev master differ at the pin for MacGeneral (refused, then the good length refused too) and GenericSecretKeyGen 2^32 + 16 (refused, passes). The CCM rows skip (no CKM_AES_CCM). Every other row passes on NSS. * test: gate the NSS departures AlgorithmConformanceTestCases finds Fixes the review finding that the new cases had never run on NSS: run against a softoken built from vendor/nss at the pinned 34716626, these fail, each recorded on NssBackendFixture (false while it stands): - DigestKey_OfAPrivateKey: CKR_KEY_TYPE_INCONSISTENT, not CKR_KEY_INDIGESTIBLE (§5.1.6) (NssRefusesToDigestAPrivateKeyAsIndigestible); - Ecdh_ParameterOutOfRange: a 1 KiB peer is CKR_ARGUMENTS_BAD and a lying DER length CKR_KEY_SIZE_RANGE (§6.3.16) (NssRefusesAMalformedEcdhPeerAsInvalid); CKA_VALUE_LEN 33 derives (§6.3.17) (NssRefusesAnEcdhValueLenAboveTheSecret); - RsaOaep_UnwrapOfABlobNotKBytes and AesKeyWrap_TamperedBlob "8 bytes", "20 bytes": CKR_DATA_LEN_RANGE, not CKR_WRAPPED_KEY_LEN_RANGE (§5.18.4) (NssRefusesAShortCiphertextAsEncryptedDataLenRange); - RsaPss "sLen 2^32 + 32": narrowed, and signs (§6.1.9) (NssRefusesAnOutOfRangePssParameterAsInvalid); - OperationState_Misuse: a state cut by its last byte restores (NssChecksTheLengthOfARestoredState), and so does one given an encryption or authentication key (NssRefusesAKeyNotNeeded) (§5.6.7); - RsaX509_Verify_RequiresTheLeadingBytes: m[1..] verifies under a signature over m (§6.1.12) (NssVerifiesRawRsaOverTheWholeBlock); - RsaX509_Verify_OfDataLongerThanTheModulus: CKR_DEVICE_ERROR (§6.1.12) (NssRefusesOverlongRawRsaVerifyData). Every other AlgorithmConformance case passes or skips for a mechanism NSS does not offer. * test: gate NSS's attribute copy past a short buffer Fixes the review finding that the new cases had never run on NSS: run against a softoken built from vendor/nss at the pinned 34716626, GetAttributeValue_ShortBuffer_WritesNothingPastIt fails on every row: C_GetAttributeValue of a session object's attribute into a buffer shorter than the value copies the whole value past the caller's buffer and …
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


F-10396- d904877 - C_DeriveKey ECDH now returns CKR_MECHANISM_PARAM_INVALID when shared data is supplied with CKD_NULL.F-4272- e879a0b - ECDH derive now rejects public data longer than 2 * MAX_ECC_BYTES + 4 and passes the length as word32.F-1610- 64809c7 - The raw point fallback in WP11_EC_Derive now resets its index and skips the DER length re-parse, so an unsupported DER length form is treated as a raw point.F-5159- 451c81e - ECDH derive now requires the encoded point length to fit in what remains of the supplied public data, returning BUFFER_E otherwise.F-10383- 800b60c - WP11_EC_Derive now clears the key's RNG association before freeing its stack RNG, and token keys take the key write lock for the whole derive.F-6959- a1f3613 - C_DeriveKey now calls CheckPrivateLogin after the CKA_DERIVE check, as C_GenerateKey does, so deriving a private key object requires a user login.F-13818- 0ff14c3 - C_DeriveKey now sets the initial states before AddObject and frees the object if AddObject fails.F-13816- 955c000 - New SetDerivedStates sets ALWAYS_SENSITIVE and NEVER_EXTRACTABLE on a derived key from the base key's history and its own SENSITIVE and EXTRACTABLE values.F-3403- 97ed5c1 - A SymmKeyLen failure in C_DeriveKey now returns CKR_FUNCTION_FAILED instead of a raw wolfCrypt error code.