■ PKCS #11: Cryptographic Token Interface Standard
This standard specifies an application programming interface (API),
called "Cryptoki", to devices which hold cryptographic information
and perform cryptographic functions. Cryptoki, short for "cryptographic
token interface", follows a simple object-based approach, addressing
the goals of technology independence and resource sharing, presenting
to applications a common, logical view of the device called a "cryptographic
token".
Cryptoki isolates an application from the details of the cryptographic
device. The application does not have to change to interface to
a different type of device or to run in a different environment;
thus, the application is portable. How Cryptoki provides this isolation
is beyond the scope of this document, although some conventions
for the support of multiple types of device will be addressed here
and possibly in a separate document.
A number of cryptographic mechanisms (algorithms) are supported
in this version. In addition, new mechanisms can be added later
without changing the general interface. It is possible that additional
mechanisms will be published from time to time in separate documents;
it is also possible for token vendors to define their own mechanisms.
Design goals
Cryptoki was intended from the beginning to be an interface between
applications and all kinds of portable cryptographic devices, such
as those based on smart cards, PCMCIA cards, and smart diskettes.
There are already standards (de facto or official) for interfacing
to these devices at some level. For instance, the mechanical characteristics
and electrical connections are well-defined, as are the methods
for supplying commands and receiving results.
What remained to be defined were particular commands for performing
cryptography. It would not be enough simply to define command sets
for each kind of device, as that would not solve the general problem
of an application interface independent of the device. To do so
is still a long-term goal, and would certainly contribute to interoperability.
The primary goal of Cryptoki was a lower-level programming interface
that abstracts the details of the devices, and presents to the application
a common model of the cryptographic device, called a "cryptographic
token" (or simply "token").
A secondary goal was resource-sharing. As desktop multi-tasking
operating systems become more popular, a single device should be
shared between more than one application. In addition, an application
should be able to interface to more than one device at a given time.
It is not the goal of Cryptoki to be a generic interface to cryptographic
operations or security services, although one certainly could build
such operations and services with the functions that Cryptoki provides.
Cryptoki is intended to complement, not compete with, such emerging
and evolving interfaces as "Generic Security Services Application
Programming Interface" (RFC 1508 and RFC 1509) and "Generic
Cryptographic Service API" (GCS-API) from X/Open.
General model
Cryptoki's general model is illustrated in the following figure.
The model begins with one or more applications that need to perform
certain cryptographic operations, and ends with one or more cryptographic
devices, on which some or all of the operations are actually performed.
A user may or may not be associated with an application.
Figure 1 General Cryptoki Model
Cryptoki provides an interface to one or more cryptographic devices
that are active in the system through a number of "slots".
Each slot, which corresponds to a physical reader or other device
interface, may contain a token. A token is typically "present
in the slot" when a cryptographic device is present in the
reader. Of course, since Cryptoki provides a logical view of slots
and tokens, there may be other physical interpretations. It is possible
that multiple slots may share the same physical reader. The point
is that a system has some number of slots, and applications can
connect to tokens in any or all of those slots.
A cryptographic device can perform some cryptographic operations,
following a certain command set; these commands are typically passed
through standard device drivers, for instance PCMCIA card services
or socket services. Cryptoki makes each cryptographic device look
logically like every other device, regardless of the implementation
technology. Thus the application need not interface directly to
the device drivers (or even know which ones are involved); Cryptoki
hides these details. Indeed, the underlying "device" may
be implemented entirely in software (for instance, as a process
running on a server)—no special hardware is necessary.
Cryptoki is likely to be implemented as a library supporting the
functions in the interface, and applications will be linked to the
library. An application may be linked to Cryptoki directly; alternatively,
Cryptoki can be a so-called "shared" library (or dynamic
link library), in which case the application would link the library
dynamically. Shared libraries are fairly straightforward to produce
in operating systems such as Microsoft Windows and OS/2, and can
be achieved without too much difficulty in UNIX and DOS systems.
The dynamic approach certainly has advantages as new libraries
are made available, but from a security perspective, there are some
drawbacks. In particular, if a library is easily replaced, then
there is the possibility that an attacker can substitute a rogue
library that intercepts a user's PIN. From a security perspective,
therefore, direct linking is generally preferable, although code-signing
techniques can prevent many of the security risks of dynamic linking.
In any case, whether the linking is direct or dynamic, the programming
interface between the application and a Cryptoki library remains
the same.
The kinds of devices and capabilities supported will depend on
the particular Cryptoki library. This standard specifies only the
interface to the library, not its features. In particular, not all
libraries will support all the mechanisms (algorithms) defined in
this interface (since not all tokens are expected to support all
the mechanisms), and libraries will likely support only a subset
of all the kinds of cryptographic devices that are available. (The
more kinds, the better, of course, and it is anticipated that libraries
will be developed supporting multiple kinds of token, rather than
just those from a single vendor.) It is expected that as applications
are developed that interface to Cryptoki, standard library and token
"profiles" will emerge.
Logical view of a token
Cryptoki's logical view of a token is a device that stores objects
and can perform cryptographic functions. Cryptoki defines three
classes of object: data, certificates, and keys. A data object is
defined by an application. A certificate object stores a certificate.
A key object stores a cryptographic key. The key may be a public
key, a private key, or a secret key; each of these types of keys
has subtypes for use in specific mechanisms. This view is illustrated
in the following figure:
Figure 2 Object Hierarchy
Objects are also classified according to their lifetime and visibility.
"Token objects" are visible to all applications connected
to the token that have sufficient permission, and remain on the
token even after the "sessions" (connections between an
application and the token) are closed and the token is removed from
its slot. "Session objects" are more temporary: whenever
a session is closed by any means, all session objects created by
that session are automatically destroyed. In addition, session objects
are only visible to the application which created them.
Further classification defines access requirements. Applications
are not required to log into the token to view "public objects";
however, to view "private objects", a user must be authenticated
to the token by a PIN or some other token-dependent method (for
example, a biometric device).
A token can create and destroy objects, manipulate them, and search
for them. It can also perform cryptographic functions with objects.
A token may have an internal random number generator.
It is important to distinguish between the logical view of a token
and the actual implementation, because not all cryptographic devices
will have this concept of "objects," or be able to perform
every kind of cryptographic function. Many devices will simply have
fixed storage places for keys of a fixed algorithm, and be able
to do a limited set of operations. Cryptoki's role is to translate
this into the logical view, mapping attributes to fixed storage
elements and so on. Not all Cryptoki libraries and tokens need to
support every object type. It is expected that standard "profiles"
will be developed, specifying sets of algorithms to be supported.
"Attributes" are characteristics that distinguish an
instance of an object. In Cryptoki, there are general attributes,
such as whether the object is private or public. There are also
attributes that are specific to a particular type of object, such
as a modulus or exponent for RSA keys.
Users
This version of Cryptoki recognizes two token user types. One type
is a Security Officer (SO). The other type is the normal user. Only
the normal user is allowed access to private objects on the token,
and that access is granted only after the normal user has been authenticated.
Some tokens may also require that a user be authenticated before
any cryptographic function can be performed on the token, whether
or not it involves private objects. The role of the SO is to initialize
a token and to set the normal user's PIN (or otherwise define, by
some method outside the scope of this version of Cryptoki, how the
normal user may be authenticated), and possibly to manipulate some
public objects. The normal user cannot log in until the SO has set
the normal user's PIN.
Other than the support for two types of user, Cryptoki does not
address the relationship between the SO and a community of users.
In particular, the SO and the normal user may be the same person
or may be different, but such matters are outside the scope of this
standard.
With respect to PINs that are entered through an application,
Cryptoki assumes only that they are variable-length strings . Any
translation to the device's requirements is left to the Cryptoki
library. The following issues are beyond the scope of Cryptoki:
- Any padding of PINs.
- How the PINs are generated (by the user, by the application,
or by some other means).
PINs that are supplied by some means other than through an application
(e.g., PINs entered via a PINpad on the token) are even more abstract.
Cryptoki knows how to wait (if need be) for such a PIN to be supplied
and used, and little more.
Applications and their use of Cryptoki
To Cryptoki, an application consists of a single address space
and all the threads of control running in it. An application becomes
a "Cryptoki application" by calling the Cryptoki function
C_Initialize from one of its threads; after this
call is made, the application can call other Cryptoki functions.
When the application is done using Cryptoki, it calls the Cryptoki
function C_Finalize and ceases to be a Cryptoki
application.
Applications and processes
In general, on most platforms, the previous paragraph means that
an application consists of a single process.
Consider a UNIX process P which becomes a Cryptoki
application by calling C_Initialize, and then uses
the fork() system call to create a child process C.
Since P and C have separate address
spaces (or will when one of them performs a write operation, if
the operating system follows the copy-on-write paradigm), they are
not part of the same application. Therefore, if C
needs to use Cryptoki, it needs to perform its own C_Initialize
call. Furthermore, if C needs to be logged into
the token(s) that it will access via Cryptoki, it needs to log into
them even if P already logged in, since P
and C are completely separate applications.
In this particular case (when C is the child
of a process which is a Cryptoki application), the behavior of Cryptoki
is undefined if C tries to use it without its own
C_Initialize call. Ideally, such an attempt would
return the value CKR_CRYPTOKI_NOT_INITIALIZED; however, because
of the way fork() works, insisting on this return value might have
a bad impact on the performance of libraries. Therefore, the behavior
of Cryptoki in this situation is left undefined. Applications should
definitely not attempt to take advantage of any potential "shortcuts"
which might (or might not!) be available because of this.
In the scenario specified above, C should actually
call C_Initialize whether or not it needs to use
Cryptoki; if it has no need to use Cryptoki, it should then call
C_Finalize immediately thereafter. This (having
the child immediately call C_Initialize and then
call C_Finalize if the parent is using Cryptoki)
is considered to be good Cryptoki programming practice, since it
can prevent the existence of dangling duplicate resources that were
created at the time of the fork() call; however, it is not required
by Cryptoki.
Applications and threads
Some applications will access a Cryptoki library in a multi-threaded
fashion. Cryptoki enables applications to provide information to
libraries so that they can give appropriate support for multi-threading.
In particular, when an application initializes a Cryptoki library
with a call to C_Initialize, it can specify one
of four possible multi-threading behaviors for the library:
- The application can specify that it will not be accessing the
library concurrently from multiple threads, and so the library
need not worry about performing any type of locking for the sake
of thread-safety.
- The application can specify that it will be accessing the library
concurrently from multiple threads, and the library must be able
to use native operation system synchronization primitives to ensure
proper thread-safe behavior.
- The application can specify that it will be accessing the library
concurrently from multiple threads, and the library must use a
set of application-supplied synchronization primitives to ensure
proper thread-safe behavior.
- The application can specify that it will be accessing the library
concurrently from multiple threads, and the library must use either
the native operation system synchronization primitives or a set
of application-supplied synchronization primitives to ensure proper
thread-safe behavior.
The 3rd and 4th types of behavior listed above are appropriate
for multi-threaded applications which are not using the native operating
system thread model. The application-supplied synchronization primitives
consist of four functions for handling mutex (mutual exclusion)
objects in the application’s threading model. Mutex objects are
simple objects which can be in either of two states at any given
time: unlocked or locked. If a call is made by a thread to lock
a mutex which is already locked, that thread blocks (waits) until
the mutex is unlocked; then it locks it and the call returns. If
more than one thread is blocking on a particular mutex, and that
mutex becomes unlocked, then exactly one of those threads will get
the lock on the mutex and return control to the caller (the other
blocking threads will continue to block and wait for their turn).
In addition to providing the above thread-handling information
to a Cryptoki library at initialization time, an application can
also specify whether or not application threads executing library
calls may use native operating system calls to spawn new threads.
Function overview
The Cryptoki API consists of a number of functions, spanning slot
and token management and object management, as well as cryptographic
functions. These functions are presented in the following table:
Table 1 Summary of Cryptoki
Functions
| Category |
Function |
Description |
| General purpose functions |
C_Initialize |
initializes Cryptoki |
| C_Finalize |
clean up miscellaneous Cryptoki-associated resources |
| C_GetInfo |
obtains general information about Cryptoki |
| C_GetFunctionList |
obtains entry points of Cryptoki library functions |
| Slot and token management functions |
C_GetSlotList |
obtains a list of slots in the system |
| C_GetSlotInfo |
obtains information about a particular slot |
| C_GetTokenInfo |
obtains information about a particular token |
| C_WaitForSlotEvent |
waits for a slot event (token insertion, removal, etc.) to
occur |
| C_GetMechanismList |
obtains a list of mechanisms supported by a token |
| C_GetMechanismInfo |
obtains information about a particular mechanism |
| C_InitToken |
initializes a token |
| C_InitPIN |
initializes the normal user’s PIN |
| C_SetPIN |
modifies the PIN of the current user |
| Session management functions |
C_OpenSession |
opens a connection between an application and a particular
token or sets up an application callback for token insertion |
| C_CloseSession |
closes a session |
| C_CloseAllSessions |
closes all sessions with a token |
| C_GetSessionInfo |
obtains information about the session |
| C_GetOperationState |
obtains the cryptographic operations state of a session |
| C_SetOperationState |
sets the cryptographic operations state of a session |
| C_Login |
logs into a token |
| C_Logout |
logs out from a token |
| Object management functions |
C_CreateObject |
creates an object |
| C_CopyObject |
creates a copy of an object |
| C_DestroyObject |
destroys an object |
| C_GetObjectSize |
obtains the size of an object in bytes |
| C_GetAttributeValue |
obtains an attribute value of an object |
| C_SetAttributeValue |
modifies an attribute value of an object |
| C_FindObjectsInit |
initializes an object search operation |
| C_FindObjects |
continues an object search operation |
| C_FindObjectsFinal |
finishes an object search operation |
| Encryption functions |
C_EncryptInit |
initializes an encryption operation |
| C_Encrypt |
encrypts single-part data |
| C_EncryptUpdate |
continues a multiple-part encryption operation |
| C_EncryptFinal |
finishes a multiple-part encryption operation |
| Decryption functions |
C_DecryptInit |
initializes a decryption operation |
| C_Decrypt |
decrypts single-part encrypted data |
| C_DecryptUpdate |
continues a multiple-part decryption operation |
| C_DecryptFinal |
finishes a multiple-part decryption operation |
| Message digesting functions |
C_DigestInit |
initializes a message-digesting operation |
| C_Digest |
digests single-part data |
| C_DigestUpdate |
continues a multiple-part digesting operation |
| C_DigestKey |
digests a key |
| C_DigestFinal |
finishes a multiple-part digesting operation |
| Signing and MACing functions |
C_SignInit |
initializes a signature operation |
| C_Sign |
signs single-part data |
| C_SignUpdate |
continues a multiple-part signature operation |
| C_SignFinal |
finishes a multiple-part signature operation |
| C_SignRecoverInit |
initializes a signature operation, where the data can be recovered
from the signature |
| C_SignRecover |
signs single-part data, where the data can be recovered from
the signature |
| Functions for verifying signatures and MACs |
C_VerifyInit |
initializes a verification operation |
| C_Verify |
verifies a signature on single-part data |
| C_VerifyUpdate |
continues a multiple-part verification operation |
| C_VerifyFinal |
finishes a multiple-part verification operation |
| C_VerifyRecoverInit |
initializes a verification operation where the data is recovered
from the signature |
| C_VerifyRecover |
verifies a signature on single-part data, where the data is
recovered from the signature |
| Dual-purpose cryptographic functions |
C_DigestEncryptUpdate |
continues simultaneous multiple-part digesting and encryption
operations |
| C_DecryptDigestUpdate |
continues simultaneous multiple-part decryption and digesting
operations |
| C_SignEncryptUpdate |
continues simultaneous multiple-part signature and encryption
operations |
| C_DecryptVerifyUpdate |
continues simultaneous multiple-part decryption and verification
operations |
| Key management functions |
C_GenerateKey |
generates a secret key |
| C_GenerateKeyPair |
generates a public-key/private-key pair |
| C_WrapKey |
wraps (encrypts) a key |
| C_UnwrapKey |
unwraps (decrypts) a key |
| C_DeriveKey |
derives a key from a base key |
| Random number eneration functions |
C_SeedRandom |
mixes in additional seed material to the random number generator |
| C_GenerateRandom |
generates random data |
| Parallel function anagement functions |
C_GetFunctionStatus |
legacy function which always returns CKR_FUNCTION_NOT_PARALLEL |
| C_CancelFunction |
legacy function which always returns CKR_FUNCTION_NOT_PARALLEL |
|
Security considerations
As an interface to cryptographic devices, Cryptoki provides a basis
for security in a computer or communications system. Two of the
particular features of the interface that facilitate such security
are the following:
- Access to private objects on the token, and possibly to cryptographic
functions and/or certificates on the token as well, requires a
PIN. Thus, possessing the cryptographic device that implements
the token may not be sufficient to use it; the PIN may also be
needed.
- Additional protection can be given to private keys and secret
keys by marking them as "sensitive" or "unextractable".
Sensitive keys cannot be revealed in plaintext off the token,
and unextractable keys cannot be revealed off the token even when
encrypted (though they can still be used as keys).
It is expected that access to private, sensitive, or unextractable
objects by means other than Cryptoki (e.g., other programming interfaces,
or reverse engineering of the device) would be difficult.
If a device does not have a tamper-proof environment or protected
memory in which to store private and sensitive objects, the device
may encrypt the objects with a master key which is perhaps derived
from the user's PIN. The particular mechanism for protecting private
objects is left to the device implementation, however.
Based on these features it should be possible to design applications
in such a way that the token can provide adequate security for the
objects the applications manage.
Of course, cryptography is only one element of security, and the
token is only one component in a system. While the token itself
may be secure, one must also consider the security of the operating
system by which the application interfaces to it, especially since
the PIN may be passed through the operating system. This can make
it easy for a rogue application on the operating system to obtain
the PIN; it is also possible that other devices monitoring communication
lines to the cryptographic device can obtain the PIN. Rogue applications
and devices may also change the commands sent to the cryptographic
device to obtain services other than what the application requested.
It is important to be sure that the system is secure against such
attack. Cryptoki may well play a role here; for instance, a token
may be involved in the "booting up" of the system.
We note that none of the attacks just described can compromise
keys marked "sensitive," since a key that is sensitive
will always remain sensitive. Similarly, a key that is unextractable
cannot be modified to be extractable.
An application may also want to be sure that the token is "legitimate"
in some sense (for a variety of reasons, including export restrictions
and basic security). This is outside the scope of the present standard,
but it can be achieved by distributing the token with a built-in,
certified public/private-key pair, by which the token can prove
its identity. The certificate would be signed by an authority (presumably
the one indicating that the token is "legitimate") whose
public key is known to the application. The application would verify
the certificate and challenge the token to prove its identity by
signing a time-varying message with its built-in private key.
Once a normal user has been authenticated to the token, Cryptoki
does not restrict which cryptographic operations the user may perform;
the user may perform any operation supported by the token. Some
tokens may not even require any type of authentication to make use
of its cryptographic functions.
© 1994-2004 RSA Security Inc. All rights reserved.
|