{"id":329,"date":"2026-06-10T10:00:00","date_gmt":"2026-06-10T10:00:00","guid":{"rendered":"https:\/\/neodata.com.uy\/labs\/the-handle-is-not-the-key\/"},"modified":"2026-09-07T15:41:31","modified_gmt":"2026-09-07T15:41:31","slug":"the-handle-is-not-the-key","status":"publish","type":"post","link":"https:\/\/neodata.com.uy\/labs\/the-handle-is-not-the-key\/","title":{"rendered":"The handle is not the key."},"content":{"rendered":"<p><!--\nWORDPRESS CONTENT: editorial notes removed\n--><\/p>\n<p class=\"nlabs-kicker\">CRYPTOGRAPHIC TOKENS<\/p>\n<h2 id=\"what-pkcs-11-abstracts-and-what-it-leaves-to-the-token\">What PKCS #11 abstracts, and what it leaves to the token.<\/h2>\n<p>An application asks a PKCS #11 module to sign with a key handle. The call succeeds. No private value crosses the interface.<\/p>\n<p>That compact description is one of the standard&#8217;s strengths. It is also where many incorrect assumptions begin.<\/p>\n<p>The handle is not the key. It is a temporary reference into a logical object model exposed by a particular Cryptoki library. Behind it may be an HSM cluster, a smart card, a USB token, a remote service or a software implementation. The same function call can reach all of them, but it does not make their policies, failure modes or operational behaviour identical.<\/p>\n<p>PKCS #11 standardises a cryptographic interface. Using it well requires understanding where that abstraction ends.<\/p>\n<h2 id=\"a-portable-view-not-an-identical-device\">A portable view, not an identical device<\/h2>\n<p>Cryptoki presents applications with slots, tokens, sessions and objects. A slot provides access to a token. A session creates a logical connection to it. Objects represent data, certificates and keys. Mechanisms identify cryptographic operations and their parameters.<\/p>\n<p>This vocabulary lets an application discover capabilities and request operations without speaking a device-specific command protocol. It deliberately hides whether a token has object storage, fixed key positions, several internal partitions or no physical hardware at all.<\/p>\n<p>The mapping is therefore constructive, not merely descriptive. A device with fixed key locations may be presented as a collection of key objects. An HSM cluster may look like one token. A smart card with strict command sequencing may be adapted to a session-oriented API.<\/p>\n<p>Portability exists at the logical boundary. Everything that affects the mapping can still vary:<\/p>\n<ul>\n<li>which mechanisms are available;<\/li>\n<li>which parameters a mechanism accepts;<\/li>\n<li>which attributes can be set or changed;<\/li>\n<li>how login state is shared across sessions;<\/li>\n<li>how many concurrent operations are permitted;<\/li>\n<li>what survives a restart or failover;<\/li>\n<li>which errors collapse several internal causes into one result;<\/li>\n<li>which vendor extensions are needed for essential functions.<\/li>\n<\/ul>\n<p>A portable application cannot assume the intersection of those behaviours is larger than it has tested.<\/p>\n<h2 id=\"handles-are-references-not-identities\">Handles are references, not identities<\/h2>\n<p>An object handle is meaningful only within the context in which the module returned it. It should not be stored as the durable identity of a key, passed between unrelated processes or expected to remain stable after the module is reinitialised.<\/p>\n<p>This matters because applications often need two distinct concepts:<\/p>\n<ol type=\"1\">\n<li>a runtime reference used to perform an operation;<\/li>\n<li>a durable identifier used to locate the intended object again.<\/li>\n<\/ol>\n<p>PKCS #11 attributes such as <code>CKA_ID<\/code>, <code>CKA_LABEL<\/code>, key type and object class can participate in a search template. None is automatically a globally unique identity. Labels may be duplicated. IDs may be assigned according to a local convention. A certificate, public key and private key may intentionally share an ID so that an application can associate them.<\/p>\n<p>Search therefore needs an explicit naming model. The application should define which attributes establish identity, what uniqueness is expected within a token and what happens when zero or several objects match.<\/p>\n<p>The PKCS #11 URI scheme adds a standard way to describe token and object selection, but it does not eliminate ambiguity in the underlying inventory. A precise URI cannot repair an imprecise provisioning policy.<\/p>\n<h2 id=\"attributes-are-policy-not-decoration\">Attributes are policy, not decoration<\/h2>\n<p>A key object is more than key material. Its attributes constrain what the token permits an application to do with it.<\/p>\n<p>For a secret or private key, relevant questions include:<\/p>\n<ul>\n<li>Can it encrypt, decrypt, sign, verify, wrap, unwrap or derive?<\/li>\n<li>Is it marked sensitive?<\/li>\n<li>Is it extractable?<\/li>\n<li>Was it always sensitive or always non-extractable?<\/li>\n<li>Which mechanisms may use it?<\/li>\n<li>Does use require authentication for every operation?<\/li>\n<li>Can the object be copied, modified or destroyed?<\/li>\n<li>Is it persistent on the token or limited to one session?<\/li>\n<\/ul>\n<p>These are not annotations added after key creation. They form part of the key&#8217;s effective policy.<\/p>\n<p>Some attributes become read-only once an object exists. Some values are imposed by the token. Some combinations are rejected. Others are silently normalised according to device policy. A template that works on a software token may fail on a certified HSM because the hardware refuses a weaker lifecycle.<\/p>\n<p>An application should treat object creation as policy negotiation with a strict postcondition: after creation, read back every attribute that is observable and verify that the resulting object has the intended capabilities. A successful handle is not sufficient evidence.<\/p>\n<h2 id=\"sensitive-and-non-extractable-are-different-claims\">Sensitive and non-extractable are different claims<\/h2>\n<p><code>CKA_SENSITIVE<\/code> and <code>CKA_EXTRACTABLE<\/code> are frequently compressed into the phrase &#8220;the key cannot leave the HSM.&#8221; The actual model is more precise.<\/p>\n<p>Sensitivity controls whether secret key material may be revealed through attribute access. Extractability controls whether the key may be exported through a wrapping operation. A non-extractable key should not become extractable later. Historical attributes such as <code>CKA_ALWAYS_SENSITIVE<\/code> and <code>CKA_NEVER_EXTRACTABLE<\/code> help describe whether those protections have held throughout the object&#8217;s lifetime.<\/p>\n<p>Even then, the statement is bounded by the token implementation and its authorised operations. A key that cannot be exported may still be used to decrypt attacker-selected ciphertexts, derive other keys or wrap objects under an inappropriate mechanism. Preventing raw extraction does not make every permitted use safe.<\/p>\n<p>The security boundary is the combination of key attributes, mechanisms, authentication state, application authorisation and token policy.<\/p>\n<h2 id=\"generation-and-import-create-different-histories\">Generation and import create different histories<\/h2>\n<p>Two objects may expose the same attributes today while having different provenance.<\/p>\n<p>A key generated inside the token may never have existed outside its boundary. An imported key necessarily existed elsewhere, at least during provisioning. A key unwrapped inside the token arrived as a protected blob, but its earlier custody depends on the wrapping system and the controls under which the blob was created.<\/p>\n<p>PKCS #11 can express parts of this history through attributes such as whether a key was locally generated. It cannot attest to an entire ceremony by itself.<\/p>\n<p>For meaningful assurance, the application needs an external record of:<\/p>\n<ul>\n<li>who requested the key;<\/li>\n<li>which token and partition created or received it;<\/li>\n<li>which template was applied;<\/li>\n<li>which wrapping key and mechanism were used;<\/li>\n<li>which approvals were required;<\/li>\n<li>whether duplicates or backups exist;<\/li>\n<li>how the key is to be retired.<\/li>\n<\/ul>\n<p>The object handle points to current capability. It does not narrate provenance.<\/p>\n<h2 id=\"wrapping-is-a-protocol\">Wrapping is a protocol<\/h2>\n<p>Key wrapping is sometimes treated as generic encrypted export. In practice it is a protocol between a source token, a destination token and the systems that assign policy on both sides.<\/p>\n<p>The parties must agree on the wrapping mechanism, parameters, key type, transported attributes and destination template. They also need a way to bind the wrapped value to the intended environment and reject substitution or rollback.<\/p>\n<p>Several questions follow:<\/p>\n<ol type=\"1\">\n<li>Which attributes travel with the wrapped material?<\/li>\n<li>Which are supplied by the destination application?<\/li>\n<li>Can the destination weaken the source policy?<\/li>\n<li>Does the wrapping construction provide integrity as well as confidentiality?<\/li>\n<li>Can the same blob be replayed into another partition?<\/li>\n<li>How is the wrapping key rotated without making backups unreadable?<\/li>\n<\/ol>\n<p>A mechanism that can wrap a key is not automatically a migration design. As with <a href=\"https:\/\/neodata.com.uy\/labs\/algorithm-agility-is-an-operational-property\/\">algorithm agility<\/a>, the difficult part is completing the transition while preserving policy and recoverability.<\/p>\n<h2 id=\"a-mechanism-name-is-not-a-complete-contract\">A mechanism name is not a complete contract<\/h2>\n<p>A mechanism identifies an operation, but the surrounding contract may include parameters, input formatting and multi-part behaviour.<\/p>\n<p>With RSA, an application may choose a mechanism that performs raw modular exponentiation, one that applies a signature encoding, or one that performs hashing and encoding inside the token. Those choices determine which bytes cross the interface and which component is responsible for constructing the signed statement.<\/p>\n<p>With ECDSA, implementations may differ in supported curves, signature encoding at the API boundary and treatment of pre-hashed input. With authenticated encryption, parameter structures carry nonces, associated data and tag lengths whose validation can vary between versions and vendors.<\/p>\n<p>Mechanism discovery through <code>C_GetMechanismList<\/code> and <code>C_GetMechanismInfo<\/code> is necessary, but it is not a substitute for operation-level tests. A token can advertise a mechanism while rejecting the particular parameter set, key size or object policy the application needs.<\/p>\n<p>The relevant capability is not &#8220;supports ECDSA&#8221; or &#8220;supports AES-GCM.&#8221; It is a tested tuple of mechanism, parameters, key attributes, message size, session state and expected output encoding.<\/p>\n<h2 id=\"sessions-make-the-interface-stateful\">Sessions make the interface stateful<\/h2>\n<p>Cryptographic operations occur through sessions. A session may be read-only or read\/write, public or authenticated, and capable of holding temporary objects and active operation state.<\/p>\n<p>This creates behaviour that is easy to miss behind higher-level language bindings:<\/p>\n<ul>\n<li>closing a session destroys its session objects;<\/li>\n<li>login can affect all sessions belonging to an application;<\/li>\n<li>a single session generally performs only one operation at a time;<\/li>\n<li>handles from one application are not valid in another;<\/li>\n<li>module initialisation and finalisation have process-level consequences;<\/li>\n<li>failover can invalidate sessions and require object rediscovery.<\/li>\n<\/ul>\n<p>A connection pool for a database and a session pool for an HSM do not have equivalent semantics. The latter carries cryptographic state, authentication state and object visibility rules.<\/p>\n<p>Concurrency should therefore be designed at the PKCS #11 layer rather than left to an incidental wrapper. Each operation needs clear ownership of its session. Recovery needs to distinguish a retryable transport failure from an operation whose completion is unknown.<\/p>\n<h2 id=\"errors-are-part-of-the-boundary\">Errors are part of the boundary<\/h2>\n<p>Cryptographic APIs are often evaluated by successful test vectors. Operational reliability depends equally on their failures.<\/p>\n<p>A robust integration should exercise:<\/p>\n<ol type=\"1\">\n<li>invalid and stale handles;<\/li>\n<li>removed or unavailable tokens;<\/li>\n<li>expired authentication state;<\/li>\n<li>templates with missing, conflicting or forbidden attributes;<\/li>\n<li>mechanism and parameter mismatches;<\/li>\n<li>insufficient token memory;<\/li>\n<li>concurrent use of one session;<\/li>\n<li>partial multi-part operations;<\/li>\n<li>failover before and after an irreversible operation;<\/li>\n<li>vendor-specific return codes.<\/li>\n<\/ol>\n<p>Retrying every failure is unsafe. A signature request may have completed even if the response was lost. Repeating a key-generation or unwrap operation can create duplicate objects. Reopening a session does not restore the state of an interrupted multi-part operation.<\/p>\n<p>The application needs an error model tied to the semantics of each operation.<\/p>\n<h2 id=\"profiles-make-portability-testable\">Profiles make portability testable<\/h2>\n<p>The complete PKCS #11 specification contains more behaviour than most applications or tokens implement. Portability becomes tractable when a deployment defines a narrower profile.<\/p>\n<p>A useful application profile states:<\/p>\n<ul>\n<li>required PKCS #11 version;<\/li>\n<li>required mechanisms and parameters;<\/li>\n<li>supported key types and sizes;<\/li>\n<li>object templates and naming rules;<\/li>\n<li>login and session assumptions;<\/li>\n<li>required attributes and permitted defaults;<\/li>\n<li>expected encodings;<\/li>\n<li>concurrency and performance limits;<\/li>\n<li>backup, failover and recovery behaviour;<\/li>\n<li>negative tests and accepted error codes.<\/li>\n<\/ul>\n<p>This profile can be executed against a software token during development and against each target device before deployment. Differences then become explicit compatibility results rather than production surprises.<\/p>\n<p>Software emulation remains useful, but it cannot prove hardware properties. It validates application behaviour at the interface. Claims about tamper resistance, non-exportability and key generation inside a certified boundary require evidence from the actual module and its configuration.<\/p>\n<h2 id=\"the-boundary-behind-the-handle\">The boundary behind the handle<\/h2>\n<p>PKCS #11 succeeds because it gives very different cryptographic devices a common shape. Applications can locate objects, request operations and keep private material behind a defined interface.<\/p>\n<p>The common shape should not be mistaken for identical semantics.<\/p>\n<p>A handle is a reference. An attribute template is a policy request. A mechanism is an operation contract. A session is state. A token is an implementation of a logical boundary whose guarantees still need to be discovered, tested and operated.<\/p>\n<p>The key may remain inside the device. The meaning of using it remains the responsibility of the system outside.<\/p>\n<hr \/>\n<h2 id=\"references\">References<\/h2>\n<ol type=\"1\">\n<li><a href=\"https:\/\/docs.oasis-open.org\/pkcs11\/pkcs11-spec\/v3.2\/pkcs11-spec-v3.2.html\">OASIS PKCS #11 Cryptographic Token Interface Specification Version 3.2<\/a><\/li>\n<li><a href=\"https:\/\/docs.oasis-open.org\/pkcs11\/pkcs11-ug\/v3.2\/pkcs11-ug-v3.2.html\">OASIS PKCS #11 Cryptographic Token Interface Usage Guide Version 3.2<\/a><\/li>\n<li><a href=\"https:\/\/docs.oasis-open.org\/pkcs11\/pkcs11-profiles\/v3.2\/pkcs11-profiles-v3.2.html\">OASIS PKCS #11 Profiles Version 3.2<\/a><\/li>\n<li><a href=\"https:\/\/www.rfc-editor.org\/rfc\/rfc7512.html\">RFC 7512: The PKCS #11 URI Scheme<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/softhsm\/SoftHSMv2\">SoftHSM version 2 source repository<\/a><\/li>\n<\/ol>\n","protected":false},"excerpt":{"rendered":"<p>PKCS #11 gives applications a common view of cryptographic devices. The key&#8217;s policy, lifecycle and operational meaning still depend on what lies behind the handle.<\/p>\n","protected":false},"author":2,"featured_media":328,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","nlabs_series_part":1},"categories":[10],"tags":[68,69,72],"nlabs_series":[64],"nlabs_article_type":[36],"class_list":["post-329","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-research","tag-algorithms-primitives","tag-applied-cryptography","tag-key-management","nlabs_series-pkcs-11-key-policy","nlabs_article_type-primer"],"_links":{"self":[{"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/posts\/329","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/comments?post=329"}],"version-history":[{"count":1,"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/posts\/329\/revisions"}],"predecessor-version":[{"id":224740,"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/posts\/329\/revisions\/224740"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/media\/328"}],"wp:attachment":[{"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/media?parent=329"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/categories?post=329"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/tags?post=329"},{"taxonomy":"nlabs_series","embeddable":true,"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/nlabs_series?post=329"},{"taxonomy":"nlabs_article_type","embeddable":true,"href":"https:\/\/neodata.com.uy\/labs\/wp-json\/wp\/v2\/nlabs_article_type?post=329"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}