PKCS#11

PKCS#11 is a standard API for cryptographic devices. It provides commands to generate, configure and use cryptographic keys in smartcard, USB crypto-tokens and Hardware Security Modules (HSMs).

A typical example is a signature token. PKCS#11 provides a command for generating a key pair for signature. The private key is kept into the device. Any time we want to sign a document we can invoke a suitable PKCS#11 command that takes the document as input and returns the signature. In this way sensitive keys are never exposed outside the device. In order to use keys stored in the token PKCS#11 provides handles, i.e., pointers to the actual keys. For example to sign a document we have to provide the document and the handle to the signing key.

opencryptoki is an open-source software emulation of a PKCS#11 device. It implements most of the standard and permits to try writing PKCS#11 application with no need of a physical device. We illustrate its usage with some examples in C. Notice that there exist wrappers for other languages. Java, for example, is supported by the IAIK library.

opencryptoki is installed on testbed. User PIN is ‘12345’. The full documentation of PKCS#11 is available here.

The following code initialises the token and logs into a user session using a pin.

To compile you need the pkcs11-headers. You can find the header files on testbed under the /opt/shared/p11/headers directory. Additionally, you need to link to the opencryptoki library:

Notice that all PKCS#11 functions are prefixed by C_. They all return a value of type CK_RV which reports error in case it is different from CKR_OK. Error codes in hexadecimal, are listed in pkcs11t.h all prefixed with CKR_.

Key attributes

Once we are in a session we can perform cryptographic operations. The following code shows how to define a set of attributes for a key. We want to create a sensitive key that can be used to encrypt and decrypt data.

It is an array in which each entry specifies the attribute name, the value (notice we need to pass a pointer) and the size of the value. This generality is important for attributes with complex values.

We can now generate a key with such attributes:

Notice that we pass to C_GenerateKey the session ID, a mechanism, the attribute template, its size (3 entries in this case) and the address of the key handle to be returned. The mechanism is a triple giving the name, a pointer to a possible parameter (NULL in this case) and the length of the parameter.

We can read key attributes (including the value) as follows. In particular here we try to read the value (CKA_VALUE) of the key. This fails since we have generated a sensitive key:

When we execute it we get

Since the key is sensitive, its value cannot be read.
In case of success the value would be stored in the array ‘thekey’ specified inside ‘thevalue’. We can retrieve many attributes at the same time by specifying many attributes in the array.

Now that we have a key we can perform operations with it. We show how to perform encryption of a message using the ECB mode of DES.

To encrypt a steam of byte we need to initialise the operation through C_EncryptInit. This function takes the current session, the mechanism (DES_ECB in this case) and the encryption key. Actual encryption is then performed using C_Encrypt which takes an array of bytes, its length and returns the ciphertext into a second array of byte (cipher in this case) with a specified length.

Once we have used a key it is good to remove it so to avoid filling up token memory. Here is how to do that

Exercise 1

There are analogous functions to decrypt. Complete the code above so to decrypt the ciphertext and print the obtained plaintext. Then, try to decrypt just the first or second 8 bytes (first block) of ECB DES. Since ECB encrypt all blocks independently you should correctly recover the first/second 8 bytes of the plaintext.

Storing keys in the token

There is a special attribute CKA_TOKEN that specifies whether the key should be permanently stored in the token or destroyed once the session is closed. For experiments you might want to set CKA_TOKEN to false (which is the default usually).

In order to use keys stored in the token when reconnecting in a new session, we need a way to find them. This is done by matching with given attributes. The following example code finds a key with a certain ‘label’ of length ‘len’ (CKA_LABEL is another key attribute that can be set when generating a key):

We ask for finding 1 matching key which whose handle is stored in ‘key’. In case we ask for more than 1 it is useful to know how many matches we have. This is returned into ‘how_many’.

Attacks on PKCS#11

Even if PKCS#11 is designed to preserve key confidentiality, there are attacks that can be mounted. The simplest is the so-called wrap-and-decrypt. key wrapping is a special key management operation that allows for encrypting a key under another key so that it can be security exported outside the token. In order to enable this operation the attribute CKA_WRAP should be set.

Exercise 2: wrap and decrypt

Implement the wrap-and-decrypt by creating a key with CKA_WRAP and CKA_DECRYPT set. This allows to leak a sensitive key very easily. Try to implement an attack that leaks the value of a freshly generated sensitive key.

You can refer to the previous class and to this paper for more detail on API-level attacks.

Leave a Reply

Your email address will not be published. Required fields are marked *