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.
C_Signis a function
CK_BYTEis an unsigned char
CKF_HW_SLOTis a bit flag
CKK_RSAis a key type
CKR_TOKEN_NOT_RECOGNIZEDis 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_Finalize functions as defined by the Cryptoki API as shown below. Let us take a closer look at both of them.
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
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
0x6A(7-bit address is 0x35)
- The slots
1, 2 and 3are 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.
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
$ 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_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.
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