Raspberry Pi + ATECC608: Part 2 – About PKCS#11 and Testing Mutual TLS Authentication

TLDR: The PKCS#11 standard defines an API called “Cryptoki”. This API allows the implementation of a powerful alternative to software-only security in IoT end nodes. For example, you can use a secure element like ATECC608 in IoT end nodes as a PKCS#11 engine which ensures a hardened TLS handshake with a server.

In the previous part, we talked about the ATECC608 chip and how its variants help to improve the security in an IoT end node without you really having to invest a lot of time and effort into using it. Towards the end, we also saw how we can build cryptoauthlib – the host library needed to talk to these chips. Check out this post now in case you missed it.

A Brief Recap of Part 1

By end of part 1, we had built and installed the cryptoauthlib in our Raspberry Pi. We also built and installed the libp11 which is a library that simplifies the usage of the ATECC608 chip as a PKCS11 engine.

We still need a couple of configuration steps to be performed before we do our first secure TLS handshake using ATECC608 on Raspberry Pi!

But before that, let us take a closer look at the PKCS#11 standard.

What is PKCS#11?

The PKCS#11 standard is a recognized PKCS standard that specifies an application programming interface (API), called Cryptoki, for devices that hold cryptographic information and perform cryptographic functions. Cryptoki follows a simple object based approach, addressing the goals of technology independence (any kind of device) and resource sharing (multiple applications accessing multiple devices), presenting to applications a common, logical view of the device called a cryptographic token.

Why should you use Cryptoki API in your designs?

We live in a world where we are spoilt for choices. There are numerous options for almost all aspects of a design – power components, signal chain, processors, memories and so on. The world of IoT cybersecurity is no stranger to this. By using a PKCS#11 cryptographic token callable using Cryptoki API in your design, you make your system hardware-agnostic. This way, you can easily switch the security provider in your system without having to re-write a lot of code. Why is this important? Because this is often the most critical portion of a system as it is security sensitive.

Simply put, using a PKCS#11 provider in your system reduces the risk of new vulnerabilities creeping into the system due to design changes.

What happens if you don’t use Cryptoki API in your designs?

The world does not end if you don’t use the this API. You can still do your security implementations. However, you may have longer design cycles in case you decide to change security implementations in your system or need to add features to an existing system.

Can I write my own Cryptoki API implementation?

Yes, absolutely! You can find generic headers on OASIS’ github profile. You just need to implement the functions declared in these headers! It is a one-time effort – but definitely something that will make future developments simpler and easier to upgrade as well as modify!

What are the prefix rules in Cryptoki API?

The following image shows the prefix rules in Cryptoki API. These prefix rules make the Cryptoki implementation very readable and identifiable.

Taken from PKCS#11 v3.0 spec

For example:

  • C_Sign is a function
  • CK_BYTE is an unsigned char
  • CKF_HW_SLOT is a bit flag
  • CKK_RSA is a key type
  • CKR_TOKEN_NOT_RECOGNIZED is a return value of a function
  • and so on…

Cryptoki API has so many functions! Do I need all of them?

NO, not at all!

If you are writing your own software implementation, you only need to implement the functions that you need.

If you are writing an implementation for a hardware, then it makes sense to implement only those features that the hardware supports properly.

For all others, keep the functions unimplemented safely. For example, check out what Microchip has done with their pkcs11 implementation inside cryptoauthlib.

What does a bare-minimum Cryptoki implementation look like?

Every application that intends to use the Cryptoki API has to – at the very least – Initialize and Finalize the PKCS#11 token. This means it has to – at the very least – call the C_Initialize and C_Finalize functions as defined by the Cryptoki API as shown below. Let us take a closer look at both of them.

Taken from PKCS#11 v3.0 spec
Taken from PKCS#11 v3.0 spec

But before you implement these, you should first have the generic Cryptoki API headers in your source code folder.

NOTE: If you closely look at the pkcs11.h file, it requires you to first implement 5 steps before you actually proceed to include the other headers. Do them correctly! Feeling lazy? Check out my skeleton Cryptoki implementation here. Please feel free to fork it and write your own PKCS#11 cryptographic token code!

How can I access ATECC608 from Raspberry Pi?

The good folks at Microchip have written a full-fledged PKCS#11 provider module inside their cryptoauthlib library. In our last post, we had already built as well as installed cryptoauthlib and libp11 for usage with Raspberry Pi.

Let us now do the remaining configurations to be able to access ATECC608 over Raspberry Pi’s I2C bus.

PKCS#11 Slot 0 configuration

When we installed the cryptoauthlib in our last post, among other things, one of the things that the install script did was provide us with a template for a slot configuration. In linux systems, the slot configuration is a file that the PKCS#11 provider code looks at for guidance about what is to be done when the host requests a functionality.

In the case of cryptoauthlib, this file is located at /var/lib/cryptoauthlib. The file is named <slot number>.conf.

Let us go ahead and create a 0.conf at the same path file that looks something like this.

# Reserved Configuration for a device
# The objects in this file will be created and marked as undeletable
# These are processed in order. Configuration parameters must be comma
# delimited and may not contain spaces

interface = i2c,0x6A,1
freeslots = 1,2,3
device = ATECC608-TNGTLS

# Slot 0 is the primary private key
#object = private,device,0

# Slot 10 is the certificate data for the device's public key
#object = certificate,device,10

# Slot 12 is the intermedate/signer certificate data
#object = certificate,signer,12

# Slot 15 is a public key
#object = public,root,15

This configuration is based on the template configuration and is very simple to understand. We provide the following inputs to the system.

  • The cryptographic device can be found on the i2c bus number 1 at address 0x6A (7-bit address is 0x35)
  • The slots 1, 2 and 3 are free. In short, there is no 1.conf, 2.conf and so on.
  • The cryptographic device is the Trust&GO variant of ATECC608. This is a great short-cut to tell the cryptoauthlib what initializations and configurations to correctly locate the device and signer credentials. Alternatively, we can describe the private key, device and signer certificate locations in the same configuration file (refer commented configuration).
    To know more about this device, check out the previous post.

Hardware connections to ATECC608-TNGTLS

The I2C bus number 1 on the Raspberry Pi will be used to connect to the ATECC608. The hardware we use is the DT100104 kit that contains all Trust variants of the ATECC608.

Raspberry Pi <=> ATECC608 hardware connections

Accessing ATECC608-TNGTLS credentials using p11tool

We are all set. As a part of the software pre-requisites in the last post, we had installed a package called gnutls-bin that provides us with a nifty command-line tool called p11tool to talk to PKCS#11 tokens.

p11tool just needs to be told where to find the library that provides the PKCS#11 functionality. In this case, it is the cryptoauthlib. The installation path of the cryptoauthlib library is /usr/lib. To list the various cryptographic entities, we shall execute p11tool with the --list-all argument.

$ p11tool --provider /usr/lib/libcryptoauthlib.so --list-all

If all goes well, the output you get should look something like below.

Object 0:
	URL: pkcs11:model=ATECC608A;manufacturer=Microchip%20Technology%20Inc;serial=231606F750596A01;token=00ABC;object=device;type=private
	Type: Private key (EC/ECDSA-SECP256R1)
	Label: device
	Flags: CKA_PRIVATE; CKA_SENSITIVE; 
	ID: 

Object 1:
	URL: pkcs11:model=ATECC608A;manufacturer=Microchip%20Technology%20Inc;serial=231606F750596A01;token=00ABC;object=device;type=public
	Type: Public key (EC/ECDSA-SECP256R1)
	Label: device
	ID: 

Object 2:
	URL: pkcs11:model=ATECC608A;manufacturer=Microchip%20Technology%20Inc;serial=231606F750596A01;token=00ABC;object=device;type=cert
	Type: X.509 Certificate (EC/ECDSA-SECP256R1)
	Expires: Fri Jan  1 04:53:23 2038
	Label: device
	ID: 

Object 3:
	URL: pkcs11:model=ATECC608A;manufacturer=Microchip%20Technology%20Inc;serial=231606F750596A01;token=00ABC;object=signer;type=cert
	Type: X.509 Certificate (EC/ECDSA-SECP256R1)
	Expires: Fri Jan  1 04:53:23 2038
	Label: signer
	Flags: CKA_CERTIFICATE_CATEGORY=CA; CKA_TRUSTED; 
	ID: 

NOTE: The URL that we see in the above outputs is the complete URL of the resource. However, when we use the URL below, we use a shortened version for brevity.

Achieving mutual TLS authentication using ATECC608 as PKCS#11 cryptographic token

We shall use OpenSSL to test out mutual authentication. We will use s_client and s_server utilities inside openssl. But before that, we will quickly generate keys and certificates for the server and the client (ATECC608 Trust&GO chip).

OpenSSL configuration to use PKCS#11 engine

To enable usage of the cryptoauthlib as an OpenSSL engine, we need to hand-modify the OpenSSL configuration to tell it how and where to find the relevant libraries. To locate openssl.cnf, execute the below and observe the path you get.

$ openssl version -a | grep OPENSSLDIR:
OPENSSLDIR: "/usr/lib/ssl"

First add the below line to the top of openssl.cnf before any other section is configured.

openssl_conf = openssl_init

Now, add the below lines at the end of openssl.cnf.

[openssl_init]
engines=engine_section

[engine_section]
pkcs11 = pkcs11_section

[pkcs11_section]
engine_id = pkcs11
# Wherever the engine installed by libp11 is. For example, for the Raspberry Pi it could be:
dynamic_path = /usr/lib/arm-linux-gnueabihf/engines-1.1/libpkcs11.so  
MODULE_PATH = /usr/lib/libcryptoauth.so
init = 0

We are all set… OpenSSL can now use cryptoauthlib as a PKCS#11 provider.

Generating keys and certificates for mutual TLS

Execute the below commands to generate the server key and certificate (self-signed).

$ openssl ecparam -genkey -name prime256v1 -out server_key.pem
$ openssl req -new -sha256 -key server_key.pem -out server_csr.csr
$ openssl req -x509 -sha256 -days 365 -key server_key.pem -in server_csr.csr -out server_cert.pem

Execute the below commands to generate a certificate signing request (CSR) using the ATECC608 Trust&GO device and subsequently generate the device certificate that will be used during mutual TLS authentication.

$ openssl req -engine pkcs11 -keyform engine -key "pkcs11:token=00ABC;object=device;type=private" -new -out client_csr.csr -subj "/CN=KE CLIENT"
$ openssl req -x509 -sha256 -days 365 -key server_key.pem -in client_csr.csr -out client_cert.pem

Performing mutual TLS using OpenSSL’s s_server and s_client

Open two terminals – one for the server, one for the client.

Terminal 1 (Server)

$ openssl s_server -accept 127.0.0.1:4433 -key server_key.pem -cert server_cert.pem -CAfile server_cert.pem -debug -Verify 2

Terminal 2 (Client)

$ openssl s_client -connect 127.0.0.1:4433 -engine pkcs11 -keyform engine -key "pkcs11:token=00ABC;object=device;type=private" -cert client_cert.pem -CAfile server_cert.pem -debug

You should now see a secure TLS session established between the server and the client where the client furnishes a certificate during the TLS handshake and also performs an ECDSA signature to provide proof of possession of private key. This operation is done by the ATECC608 chip.

Summary

In this rather long post, we saw the following topics.

  • What PKCS#11 standard and the Cryptoki API are and how they help in implementing a standard cryptographic interface in computing systems
  • How we can ourselves write a very basic PKCS#11 provider library that can be be consumed by any host application linked with the library
  • How we can use ATECC608 as a PKCS#11 token on Raspberry Pi and similar systems
  • We saw a real-world use-case namely mutual TLS authentication between a server and a client where the client uses the ATECC608 chip as a PKCS#11 cryptographic token to perform the TLS handshake

Leave a Reply