Chapter 5: Cryptography—Secure communications

Abstract

As we saw in the last chapter, a modern block cipher will allow two stations to communicate in private across an insecure channel. If both stations are able to agree on a secret key in advance through a secure channel, then establishing an encrypted channel can be straight forward although as we will see later, it is not without its problems. However, in many systems, the use of a “preshared key” is impractical. This means that a session key must be agreed between both stations over the insecure channel. This is a nontrivial problem that is addressed by the Transport Layer Security (TLS) protocol.

Keywords

Cryptography; Ciphers; Malleability; Security; Encrypt; Decrypt

Introduction

As we saw in the last chapter, a modern block cipher will allow two stations to communicate in private across an insecure channel. If both stations are able to agree on a secret key in advance through a secure channel, then establishing an encrypted channel can be straight forward although as we will see later, it is not without its problems. However, in many systems, the use of a “preshared key” is impractical. This means that a session key must be agreed between both stations over the insecure channel. This is a nontrivial problem that is addressed by the Transport Layer Security (TLS) protocol (Fig. 5.1).

Fig. 5.1
Fig. 5.1 Public key infrastructure. In addition to asymmetric encryption the TLS protocol defines a broad public key infrastructure. No permission required.

If we want to communicate with a new station, we need to agree on a session key to be used by a symmetrical cipher. Now we are faced with having to transfer a secret between the two stations with an adversary watching. In this chapter, we will see how this can be achieved with the second class of ciphers called asymmetrical ciphers. We will then have a look at how the broader TLS protocol builds a practical solution to this key distribution problem.

Asymmetric ciphers

Unlike a symmetrical cipher, a generic asymmetric cipher has two keys. The first is a freely distributed public key, which is used to encrypt data but cannot be used to decrypt any message. The second key is the private key that the owning entity must keep secret. The private key is used to decrypt messages that have been encrypted by the public key (Fig. 5.2).

Fig. 5.2
Fig. 5.2 Public key cryptography. Asymmetrical encryption algorithms have two keys. A public key for encryption and a private key used for decryption. No permission required.

Apart from the number of keys, there are several big differences between symmetrical and asymmetrical ciphers. First, unlike a symmetrical cipher, we cannot just randomly select encryption keys. In an asymmetrical system, the key pair has to be calculated. While this is only done once, it can be time-consuming, especially if done within the microcontroller. Second, a typical asymmetrical cipher requires a very large key size to achieve the same level of security as a symmetrical cipher. An asymmetrical cipher key will need to be 3072 bits to achieve the same security level as a 128 bit AES cipher and should not be smaller than 2048 bits. This also means that asymmetrical ciphers are much slower to compute than symmetrical ciphers. In a practical system, they are only used to transfer small amounts of data, such as keying material for symmetrical ciphers. Finally, the use of very large keys means that asymmetrical ciphers use very large numbers, which cannot be represented with standard “C” variable types. An asymmetrical cipher will need a dedicated big number library that uses multiprecision integers (MPI) that can support the range of values required. There are a number of asymmetrical cryptosystems in existence, but the majority of communication systems use one of two algorithms; either the RSA cryptosystem or the Diffie Hellman Key Exchange. In this section, we will look at both the RSA and Diffie Hellman systems.

RSA

The RSA algorithm was developed by Ron Rivest, Adi Shamir, and Leonard Adleman in 1977. Today, it is the most popular asymmetric algorithm, being used in around 90% of systems. The RSA algorithm is based on the “integer factorization problem.” While it is easy to multiply two primes together, it is very time-consuming to factor a large number into its constituent primes. This allows us to create a one-way function that is easy to compute, but the resulting product is hard to factorize.

The mathematical RSA identity is shown below:

medmodn=m

si1_e

From this, we can derive functions to encrypt and decrypt the plain text:

memodn=c

si2_e

cdmodn=m

si3_e

  • m Plain text message
  • d private key
  • e & n public key
  • c is the ciphertext

Here, we can see that the encryption and decryption function is identical. We just need to use the appropriate values for each operation.

The size of the public modulus n is important for two reasons. First, the values for the plaintext n and cyphertext c must be within the integer ring (the modulus) defined by n. Secondly the maximum bit size of the message is equal to the bitsize of n. Finally, the public and private key values e and d must also exist within the integer ring defined by n. For our cryptosystem to be secure, n must contain many key pairs to be invulnerable to brute force attacks. In ever-increasing computing power and advances in factorizing algorithms, a safe value for n needs to be a minimum of 2048 bits.

Unlike a symmetrical cipher, we cannot just pick an encryption key at random. Since the public and private keys are related, they have to be calculated.

RSA key generation uses the following algorithm:

  • First we select two large primes p and q
  • We can then calculate n = p.q
  • Next calculate φ(n) = (p-1).(q-1) which is known as the Totient
  • Now we must select e such that it is a prime less than φ(n) – 1
  • Now d.e = mod φ(n)
  • Hence d = e−  1 mod φ(n)

We can select values for p and q using a random number generator then test to see if the numbers are prime. Two methods to do this are the Millar Raban primality test and the Fermat primality test as shown in Fig. 5.3.

Fig. 5.3
Fig. 5.3 Primality test. A primality test is used to determine if a random number is prime. No permission required.

We now have to select e, the public exponent. Again, this must be prime and within the cyclic group defined by “n.” This gives us an opportunity to improve the performance of the RSA algorithm. Here, we can select the value 65537. This value is useful because it is prime and is 2^16 + 1 or Hexadecimal 10001. This improves the RSA encryption algorithm's performance by reducing the number of multiplies. While “e” can be a relatively small value, “d” the private key is the same bit size as n (2048 bit), so decryption is many times slower.

Exercise: RSA small numbers

This exercise demonstrates using the mbedTLS big number library to implement a demonstration version of the RSA algorithm. The values used in this example are very small and give a weak cipher, which should not be used in any real application.

  • In the Pack Installer and select Exercise 5.1.

The bignum library is enabled in mbedTLS_config.h

          #define MBEDTLS_BIGNUM_C

The example code declares a set of multiprecision integer to be used for the RSA values:

          mbedtls_mpi P, E, N,Q,D,M,C, X;

Each value is initialized and Prime values are loaded into P,Q,E and M:

          void initLargeNum(void)

We can then compute the RSA key pair with void computeKeys(void)

This is done by first calculation the Totient:

   mbedtls_mpi_mul_mpi( &N, &P, &Q );   mbedtls_mpi_sub_int(&Q, &Q, 1 );   mbedtls_mpi_sub_int(&P, &P, 1 );   mbedtls_mpi_mul_mpi( &L, &P, &Q );

We can then derive the key pair using an inverse modulus calculation, which is part of the bignum library:

          mbedtls_mpi_inv_mod( &D, &E, &L );

Once the key pair has been calculated, we can use two functions to encrypt and decrypt plain text:

   void computeRSA(void){mbedtls_mpi_exp_mod( &X, &C, &D, &N, NULL );   }   void computeMessage(void){mbedtls_mpi_exp_mod( &C, &M, &E, &N, NULL );   }

As you can see, the encrypt and decrypt functions contain the same “mod a log” code but the type of operation depends on which key you use.

  • Build the code and start the debugger.
  • Start the Tera Term console.
  • Examine the functions, then run the code.
  • Observe the output in the Tera Term console (Fig. 5.4).
    Fig. 5.4
    Fig. 5.4 RSA results. The exercise calculations and results are shown in the console window. No permission required.

Malleability

In the last exercise, we used the “school book” version of RSA. Even if we used large numbers, this is not a fully fledged cryptosystem. The core RSA algorithm is malleable. In other words, the cypher text can be modified by an attacker, and it will still decrypt to a meaningful value. We can see this by attacking our previous example if we take an integer S and apply this to the cypher text c.

Se·c

si4_e

Then, when we decrypt

Se·cd=S·mmodn

si5_e

If S = 2, an attacker has managed to double the value of m and the receiving station will accept this as valid data.

Exercise: RSA malleability

  • Go back to the project in exercise 5.1.
  • Uncomment the schoolBookAttack() function in main.c.

Here, we are taking the value “2” and raising it to the power of the public exponent e and then multiplying the cyphertext:

   void schoolBookAttack (void){mbedtls_mpi S;
mbedtls_mpi_init( &S );  mbedtls_mpi_read_string( &S, 16,"00002" );  mbedtls_mpi_exp_mod( &S, &S, &E, &N, NULL );  mbedtls_mpi_mul_mpi( &C, &C, &S );     }
  • Build the code and rerun the example (Fig. 5.5).
    Fig. 5.5
    Fig. 5.5 RSA malleability results. The results of the malleability attack are shown in the console window. No permission required.

The attacker has successfully modified the encrypted message from 0x55 to double its value 0xAA. There has been a banking error in your favor.

RSA padding

We can overcome this malleability weakness by adding a padding scheme to the plaintext data. There are a number of different padding schemes, but the idea is to place a pattern alongside the plaintext prior to encryption. When we decrypt, we can check the padding to detect any manipulation of the cyphertext. This has the effect of armoring the ciphertext to make it resistant to any malleability attacks.

PKCS 1_1.5

The original form of padding for the RSA cryptosystem was described in the PKCS1_1.5 document published by the RSA corporation. In this scheme, the total plaintext size is defined by the modulus n. The start of the plaintext contains the padding as a header followed by the data. The header starts with two bytes that define the block padding scheme. For PKCS1_1.5 the header value is set to 0x02. The remainder of the header is filled with random data until the start of the message data with a null byte delimiter between the random values and the message data. There must be a minimum of eight bytes of random data. The format is shown in Fig. 5.6.

Fig. 5.6
Fig. 5.6 PKCS_1 1.5 padding. The PKCS_1 1.5 padding completes the RSA crypto system. No permission required.

While this scheme is still widely used it is subject to a number of protocol attacks, most notably one called Bleichenbacher's Attack.

Optimal Asymmetric Encryption Padding

A more up-to-date alternative to PKCS1_1.5 padding is Optimal Asymmetric Encryption Padding (OAEP). This scheme creates a padded header and encoded message packet but requires much more computation than PKCS1_1.5.

The scheme consists of a two-byte block type field the same as PKCS1_1.5, which for OAEP is set to 0x0000. This is followed by a masked seed field and then the encoded message.

The OAEP encoding uses a Mask Generation Function (MGF()). In most systems, this will be a hashing algorithm, typically SHA-1.

The OAEP padding is calculated as follows:

  • N is the size of the modulus n in bytes
  • H is the size of the Hash message digest in bytes
  • L is a custom label if not defined it will be a string on NUL bytes
  • M is the message data
  • PS is a string of NULL bytes. The length of PS is N - 2H -2
  • Now:
  • Data Block DB = Hash(L)||PS  ||0x01  ||M
  • dbMask = MGF(seed,N-H-1)
  • maskedDB = DB ^ dbmasked
  • then:
  • seedMask = MGF(maskedDB,H)
  • maskedSeed = seed ^ seedMask
  • and:
  • Encoded Message EM = 0x00  ||maskedSeed  ||maskedDB as shown in Fig. 5.7
    Fig. 5.7
    Fig. 5.7 Optimal Asymmetric Encryption Padding (OAEP). The OAEP scheme solves some weaknesses within PKCS_1 1.5. No permission required.

When using the RSA cipher, the padding scheme is not an option. It must be used to create a working cryptosystem. There are no exceptions.

The RSA functions provided by mbedTLS are shown in Table 5.1, and we will use these functions in the next exercise.

Table 5.1

RSA functions.
FunctionDescription
mbedtls_rsa:check_privkeyCheck a private key for validity
mbedtls_rsa:check_pub_privCheck a key pair for validity
mbedtls_rsa:check_pubkeyCheck a public key for validity
mbedtls_rsa:copyCopy a context
mbedtls_rsa:freeFree the context
mbedtls_rsa:gen_keyGenerate a key pair
mbedtls_rsa:initInitialize the context
mbedtls_rsa:pkcs1_decryptDecrypt with PKCS 1 padding
mbedtls_rsa:pkcs1_encryptEncrypt with PKCS 1 padding
mbedtls_rsa:rsaes_oaep_decryptDecrypt with PKCS 1 OAEP padding
mbedtls_rsa:rsaes_oaep_encryptEncrypt with PKCS 1 OAEP padding
mbedtls_rsa:rsaes_pkcs1_v15_decryptDecrypt with PKCS 1.5 padding
mbedtls_rsa:rsaes_pkcs1_v15_encryptEncrypt with PKCS 1.5 padding
mbedtls_rsa:set_paddingSet the context padding

Exercise: mbedTLS RSA key generation and cipher

In this exercise, we will generate a pair of RSA keys and then use them to encrypt and decrypt a symmetrical encryption key. The keys will be stored in keyfiles within the evaluation boards file system.

  • In the Pack Installer and select Exercise 5.2.

To generate the RSA keys, we create a context for the RSA cipher along with contexts for a random number generator and entropy pool.

   mbedtls_rsa:context rsa;mbedtls_entropy_context entropy;   mbedtls_ctr_drbg_context ctr_drbg;

The example code will initialize the RNG and open files to store the public and private keys.

Then we can initialize the RSA cipher with its context and our choice of padding system.

          mbedtls_rsa:init( &rsa, MBEDTLS_RSA_PKCS_V15, 0 );

We can generate the RSA keys with a single function call. This uses random values to search for suitable primes. A weakness in our RNG implementation would result in predictability, which could be exploited by an adversary.

   mbedtls_rsa:gen_key( &rsa,mbedtls_ctr_drbg_random,&ctr_drbg,KEY_SIZE,EXPONENT )

The definitions used in the function call define the required bit size for the private key and the value to be used for the public exponent “e.”

   #define KEY_SIZE 2048   #define EXPONENT 6557

Once generated we can check both keys:

   mbedtls_rsa:check_pubkey( &rsa )   mbedtls_rsa:check_privkey( &rsa )

and then use the public key to encrypt an array holding some plain text such as a symmetric cipher encryption key.

   mbedtls_rsa:pkcs1_encrypt( &rsa,mbedtls_ctr_drbg_random,NULL,MBEDTLS_RSA_PUBLIC,PT_LEN,rsa:plaintext,rsa:ciphertext )

We can then decrypt the cipher text with the private key and check it matches the original plaintext.

   mbedtls_rsa:pkcs1_decrypt( &rsa,mbedtls_ctr_drbg_random,NULL, MBEDTLS_RSA_PRIVATE,&len,rsa:ciphertext,rsa:decrypted,sizeof(rsa:decrypted) );
  • Run the code and observe the diagnostic messages displayed on the terminal.
  • Also make a note of the time taken to run the example. We will compare RSA to other algorithms later in this chapter.

RSA problems

One inherent problem with RSA is that if the private key is ever compromised, an adversary will be able to decode any previous and future messages that have been encrypted with the public key. There is a second public-key cryptosystem that avoids this problem and provides “perfect forward secrecy.”

The Diffie Hellman (DH) Key agreement system

Like RSA, the Diffie Hellman (DH) system is based on a mathematical one-way function. While RSA is based on the integer factorization problem, DH is based on the discrete logarithm problem. That is, exponentiation can be used to create a one-way function, and that exponentiation is commutative.

K=αxy=αyxαxymodp

si6_e

DH uses this identity to create a key exchange protocol. To set up, we need to select a large prime p, then both stations with a large integer alpha to be our domain parameters, which are public values. The value of alpha must be within the cyclic group (modulus integer ring) of p-2.

At the beginning of communication, the master station shares its domain parameters. Then both stations select a session private key, which is a second integer within the cyclic group of p-2. They then both compute their respective public key:

Kpr=elementincyclicgroupofp2

si7_e

Kpub=αKprmodp

si8_e

Both sides can now exchange their public keys and can compute a shared secret as follows:

Kpub,BKpr,A=KAB=Kpub,AKpr,B

si9_e

The DH algorithm is not a cryptosystem like RSA. It is used to agree on a shared secret which will be different for every set of domain and session parameters. This means that if any station's private key is compromised, it will be of no use in recalculating the shared secret of other sessions, hence the concept of perfect forward secrecy.

Exercise: DH small numbers

This exercise demonstrates using the mbedTLS big number library to implement a demonstration version of the DH algorithm. Like the RSA, small numbers exercise, the values used in this example are very small and give a weak cipher which should not be used in any real application.

  • In the Pack Installer and select Exercise 5.3.
  • Open Main.c

The code initializes a set of MPI numbers and initializes the domain values Alpha and P along with private key values for two stations a and b.

   mbedtls_mpi_init( &Pub_A );   mbedtls_mpi_init( &Pub_B );   mbedtls_mpi_init( &a );   mbedtls_mpi_init( &b );
   mbedtls_mpi_init( &Alpha );   mbedtls_mpi_init( &P );   mbedtls_mpi_init( &Secret_A );   mbedtls_mpi_init( &Secret_B );   mbedtls_mpi_read_string( &P, 16,"0001D" );   mbedtls_mpi_read_string( &Alpha, 16,"00002");   mbedtls_mpi_read_string( &a, 16,"00005");   mbedtls_mpi_read_string( &b, 16,"0000C");

Using the domain values, we can then compute the public key for each station.

   mbedtls_mpi_exp_mod( &Pub_A, &Alpha, &a, &P, NULL );   mbedtls_mpi_exp_mod( &Pub_B, &Alpha, &b, &P, NULL );

By exchanging public keys each station can now compute the shared secret.

   mbedtls_mpi_exp_mod( &Secret_A, &Pub_B, &a, &P, NULL );   mbedtls_mpi_exp_mod( &Secret_B, &Pub_A, &b, &P, NULL );
  • Build the code.
  • Start the debugger and run the code (Fig. 5.8).
    Fig. 5.8
    Fig. 5.8 Console display of DH key agreement. The DH key agreement process will be displayed in the debugger console window. No permission required.
  • The results of each calculation are displayed in the debugger console window.

In a practical system, the initial value for the prime p is chosen using a random number generator and one of the prime verifying algorithms we saw with the RSA algorithm. The integers used to compute the public keys should also be selected using a random number generator to prevent the selection of weak values which an adversary could guess (Table 5.2).

Table 5.2

DH functions.
FunctionDescription
mbedtls_dhm_calc_secretDerive and export the shared secret
mbedtls_dhm_freeFree the context
mbedtls_dhm_initInitialize the context
mbedtls_dhm_make_paramsSetup and write the key server parameters
mbedtls_dhm_make_publicCreate private value and export public value
mbedtls_dhm_read_paramsRead the key server exchange parameters
mbedtls_dhm_read_publicRead the peer’s public value

Exercise: Diffie Hellman Key agreement

In this exercise, we will configure the evaluation board as a server and allow the PC to connect over a WiFi connection. A client on the PC will then request the DH domain parameters along with its public key. The client will use these values to calculate its public key and will then send this to the server. Both the server and client will calculate a shared secret. The server will use the shared secret to encrypt a text message which is sent to the client for decryption.

  • In the Pack Installer select Exercise 5.4.
  • Set the server project as the active project.

The embedded node is designed to run as a server with the address 192.168.0.10. You may need to adjust this to work with your WiFi network. When the PC client starts it will ask you to input the server address.

In the server we first declare the necessary contexts for the DH, AES ciphers and random number generator. We then initialize them as well as starting the local file system.

   mbedtls_dhm_context dhm;   mbedtls_aes_context aes;   mbedtls_dhm_init( &dhm );   mbedtls_aes_init( &aes );

The full example code initializes the filesystem, networking, and random number generator before loading the servers DH parameters from the embedded file system.

   f = fopen( "dh_prime.txt", "rb" ) ) == NULL )   mbedtls_mpi_read_file( &dhm.P, 16, f )   mbedtls_mpi_read_file( &dhm.G, 16, f )   fclose( f );

We then calculate the session DH parameters and extract the public parameters into a buffer ready to be sent over the network.

   mbedtls_dhm_make_params( &dhm,(int) mbedtls_mpi_size( &dhm.P ),buf,
&n,  mbedtls_ctr_drbg_random,  &ctr_drbg )

The server then opens a network port and waits for a remote connection.

The client connects to the server then sends its DH public parameter. Next, the client will reply with its public parameter.

The server can then load the local context with the clients public DP parameters and calculate the shared secret.

   mbedtls_dhm_read_public( &dhm, buf, dhm.len );   mbedtls_dhm_calc_secret( &dhm,buf,sizeof( buf ),&n,mbedtls_ctr_drbg_random,&ctr_drbg );

Once we have the shared secret, it can be used as the encryption key to wrap the plaintext reply.

   mbedtls_aes_setkey_enc( &aes, buf, 256 );   memcpy( buf, PLAINTEXT, 16 );   mbedtls_aes_crypt_ecb( &aes, MBEDTLS_AES_ENCRYPT,buf,buf );

The server will then send the encrypted packet back to the client.

The client-side follows a similar set of API calls to generate the session DH parameters and exchange public values with the server. The major difference is that it creates an ephemeral session key pair and shares the public values with the server.

   mbedtls_dhm_make_public( &dhm,(int) dhm.len,buf,n,mbedtls_ctr_drbg_random,&ctr_drbg )

Once the client has received the servers’ public values, it is able to calculate the same shared secret as the server. When the ciphertext packet arrives from the server, it can use the shared secret to decrypt the message back to the original plaintext.

  • In the server project copy the DH_prime.txt file from the PC_app folder to the SD card and place this into the evaluation board.
  • Build the server project and start the debugger.
  • Run the server code so it is waiting for a connection.
  • Open a Windows command window and run the dh_client executable located in the PC_app folder (Fig. 5.9).
    Fig. 5.9
    Fig. 5.9 DH Key agreement over a network. The PC application and the embedded node negotiate a shared secret over the local WiFi network. No permission required.

The two stations will display their progress as they negotiate a secure connection.

Like RSA, the DH algorithm uses very large numbers and needs the use of the mbedTLS “big number library.” However, unlike RSA, the DH algorithm can be implemented with a different style of number line which greatly improves its performance.

Elliptic curve cryptography

So far, all of our asymmetric ciphers are based on the “integer factorization problem” or the “discrete logarithm problem” and a standard (linear) number line. In order to get a good level of security, we need to use very large numbers. This, in turn, results in a large amount of compute time. However, it is also possible to build a discrete logarithm problem by using an alternative number line and arithmetic based on elliptic curves. If you have been brought up on a diet of engineering math, this all looks a bit arbitrary; if you want to take a deeper look at the underlying math, I have listed a number of open source books in the bibliography. Everyone needs an obscure hobby.

Elliptic curve cryptography replaces our standard linear number line with a curve based on the identity.

y2x3+a.x+b

si10_e

We can plot the curve as a set of real numbers, which gives us the textbook curve, as shown in Fig. 5.10. An important feature of an EC curve is that it is symmetrical about the X-axis.

Fig. 5.10
Fig. 5.10 Elliptic curve. A textbook elliptic curve which is symmetrical around the X-axis. No permission required.

For cryptographic use, we want to create a finite field Chapter 4. We first need to create an integer ring, which can be done by adding a modulus operation. If we make p a prime, we can create a cyclic group in which each element is known as a primitive element.

y2x3+a.x+bmodp

si11_e

Addition

While doing algebraic operations on a curve may seem a bit odd, it is possible to perform addition of two points as shown in Fig. 5.11.

Fig. 5.11
Fig. 5.11 Elliptic curve addition. Elliptic curve addition is performed by bisecting the curve and then reflecting over the X-axis. No permission required.

To add two points, P and Q, we bisect them with a line and extend this line until it cuts the curve at a third point. If we then reflect this point across the X-axis, we get a result R = (P + Q).

Point doubling

We can also calculate P + P by extending the tangent of point P until it cuts the curve at a new point. Again this point is reflected across the x-axis to give us a result of 2P, as shown in Fig. 5.12.

Fig. 5.12
Fig. 5.12 Elliptic curve point doubling. Point doubling takes a tangent and then reflects across the X-axis. No permission required.

Group element

For our integer ring to become a cyclic group we also need a neutral element φ such that

P+φ=P

si12_e

On a standard number line, the neutral element would be zero, but on our elliptic curve, it is not clear what we can use. In practice, we can define a point at “plus” infinity along the Y-axis as the neutral element. So we can consider φ to be asymtotic to each point on our curve

P+P=φ

si13_e

If we use our point addition method to calculate −  P it turns out that the inverse of P is its mirror across the X-axis as shown in Fig. 5.13.

Fig. 5.13
Fig. 5.13 Elliptic curve inversion. A modular inversion is calculated by a reflection across the X-axis. No permission required.

To create a discrete logarithmic problem on an elliptic curve, we use another property of elliptic curve addition: adding a point P to itself is the equivalent of raising P by a power on a linear number line.

So in effect:

Pd=T

si14_e

is expressed as

P+P+P+P+P=T

si15_e

Addition d times

Which can written as

dP=T

si16_e

We can then take our base point P and calculate its powers and use these as the basis for a discrete logarithm problem. As we will see in the next exercise, the public key is T and d is the private key.

To create an elliptic curve cryptosystem, we need to define a suitable curve (not easy). In practice, the EC curves supported by mbedTLS come from NIST, the Internet Engineering Task Force or the original researcher, Neil Koblitz. It should be noted that because of their underlying structure, the NIST curves have a higher performance than the IETF Brainpool curves (Table 5.3).

Table 5.3

mbedTLS elliptic curves.
mbedTLS EC curveDescription
MBEDTLS_ECP_DP_NONE
MBEDTLS_ECP_DP_SECP192R1192-bits NIST curve
MBEDTLS_ECP_DP_SECP224R1224-bits NIST curve
MBEDTLS_ECP_DP_SECP256R1256-bits NIST curve
MBEDTLS_ECP_DP_SECP384R1384-bits NIST curve
MBEDTLS_ECP_DP_SECP521R1521-bits NIST curve
MBEDTLS_ECP_DP_BP256R1256-bits Brainpool curve
MBEDTLS_ECP_DP_BP384R1384-bits Brainpool curve
MBEDTLS_ECP_DP_BP512R1512-bits Brainpool curve
MBEDTLS_ECP_DP_CURVE25519Curve25519
MBEDTLS_ECP_DP_SECP192K1192-bits “Koblitz” curve
MBEDTLS_ECP_DP_SECP224K1224-bits “Koblitz” curve
MBEDTLS_ECP_DP_SECP256K1256-bits “Koblitz” curve

Exercise: Elliptic Curve Diffie Hellman

In this exercise, we replace the standard DH algorithm with the elliptic curve version and compute the server and client keys and shared secret. In the elliptic curve version of the DH algorithm, the public and private keys are points on the elliptic curve. In the code below, the values in the ECDH context are as follows:

  •  grpThe elliptic curve to use
  •  QThe public key
  •  Qpthe peer public key
  •  dThe private key
  •  Xa coordinate on the elliptic curve
  •  zThe shared secret
  • In the Pack Installer select Exercise 5.5.
  • Build the project then download it using the debugger

The project has code that first creates and initializes a context for both the client and server stages:

   mbedtls_ecdh_context ctx_cli, ctx_srv;   mbedtls_ecdh_init( &ctx_cli );   mbedtls_ecdh_init( &ctx_srv );

The next few function calls are symmetrical for both the client and server here we will look at the client version.

For the client we first load the common elliptic curve.

   mbedtls_ecp_group_load( &ctx_cli.grp,MBEDTLS_ECP_DP_CURVE25519 );

We can then generate the public and private keys using a random number as a seed.

   mbedtls_ecdh_gen_public( &ctx_cli.grp,&ctx_cli.d,&ctx_cli.Q,mbedtls_ctr_drbg_random,&ctr_drbg );

The public key MPI is then exported as a binary string to be shared with the server code.

   unsigned char cli_to_srv[32];   mbedtls_mpi_write_binary( &ctx_cli.Q.X, cli_to_srv, 32 );

The public key can then be read by the peer, in this case the server.

mbedtls_mpi_read_binary( &ctx_srv.Qp.X, cli_to_srv, 32 )

This process is repeated by the server code.

Once the public keys have been exchanged both the server and the client can compute a shared secret z.

   mbedtls_ecdh_compute_shared( &ctx_srv.grp,&ctx_srv.z,&ctx_srv.Qp,&ctx_srv.d,mbedtls_ctr_drbg_random, &ctr_drbg );
  • Run the code and the process will be printed to the console window

Both the server and client will calculate the same shared secret. This exercise is comparable to Exercise 5.2, which uses RSA. Here we can see that the EC version is much faster.

Message signing

In the last chapter, we saw how to calculate Message Authentication Code (MAC), which can be used to authenticate data through the use of a password. This password has to be shared through a secure channel between all stations that need to communicate.

Unfortunately, this is not a practical approach if we try to establish secure communications between different stations over a public internet connection. Fortunately, we can use public-key cryptography to authenticate data through a digital signing technique.

RSA signing

The RSA approach to signing data is very straight forward and elegant. If we want to sign a message to authenticate it as originating from us, we go through the following 2-step process. First, compute the hash of the plain text we want to sign. Second, we encrypt the hash using the RSA algorithm, but we use our private key. The resulting ciphertext is the signature of our plaintext. We can send the plaintext and signature to any other station along with our public key. The receiving station can authenticate the plain text by decrypting the signature using our public key. This will reveal the per calculated hash of the plaintext. They can now compute the hash of the plaintext, which should match the signature hash. If this is the case, we have confirmed the integrity of the plaintext and also its origin, as the signature could only have been created with our private key (Fig. 5.14).

Fig. 5.14
Fig. 5.14 RSA signing digital data. RSA can be used to sign data by taking its hash and then encrypting using the private key. No permission required.

In practice, the process is slightly more difficult as we have to add padding to the signature to prevent an existential attack. Like the RSA encryption and decryption functions, the different padding strategies are directly implemented by the mbedTLS signing API calls.

Exercise: RSA signature

In this exercise we will calculate and verify the signature of a file of plaintext data using the RSA algorithm.

  • In the Pack Installer select Exercise 5.6
  • Open main.c

At the start of the code, we define each of the RSA public and private key numbers as an ASCII string:

   #define RSA_N"9292758453063D803DD603D5E777D788"      "8ED1D5BF35786190FA2F23EBC0848AEA" ..............     "5E94BB77B07507233A0BC7BAC8F90F79"

We then define a multiprecision integer and initialize it along with the RSA context:

   mbedtls_mpi K;   mbedtls_mpi_init( &K );   mbedtls_rsa:init( &rsa, MBEDTLS_RSA_PKCS_V15, 0 );

We can then set the RSA public and private keys by reading in each MPI number:

   MBEDTLS_MPI_CHK( mbedtls_mpi_read_string( &K, 16, RSA_N ) );   MBEDTLS_MPI_CHK( mbedtls_rsa:import( &rsa,&K,NULL,NULL,NULL,NULL ) );

To create the signature, we first calculate the HASH of the plaintext file:

          mbedtls_sha1_ret( rsa:plaintext, PT_LEN, sha1sum )

Then, we compute the signature using the private key and use the pkcs1 padding scheme:

   mbedtls_rsa:pkcs1_sign( &rsa,myrand,NULL,MBEDTLS_RSA_PRIVATE,MBEDTLS_MD_SHA1,0,       sha1sum_0,rsa:ciphertext );

Once the signature has been computed, a receiving station can recompute the hash and then verify the integrity of the plaintext using the sending stations public key:

   mbedtls_rsa:pkcs1_verify( &rsa,NULL,NULL,MBEDTLS_RSA_PUBLIC,MBEDTLS_MD_SHA1,0,sha1sum_1,rsa:ciphertext );
  • Build the project.
  • Place the SD card with the helloBig.txt file into the evaluation board.
  • Run the code to generate and verify the signature.

Elliptic Curve Digital Signature Algorithm

An alternative to signing messages with RSA is the Digital Signature Algorithm, which is a NIST standard FIPS 128 - 3.

The ECDSA algorithm is more complex than RSA and has separate sign and verify algorithms. The ECDSA algorithm can also be implemented using elliptic curve cryptography, which is becoming more widely used for small embedded systems.

To sign a message, we need to select and compute a range of variables as shown in Table 5.4.

Table 5.4

ECDSA parameters.
ParameterDescription
CURVEAn elliptic curve
pModulus or CURVE
a bCoefficients of CURVE
AA point which generates a cyclic group of prime order q
dA random integer in the range 0 < d < q
BComputed as dA
Public key kpubp,a,b,A,B
Private key kprD
Random ephemeral session key keWhere 0 < ke < q
xThe message

Key generation

To compute a set of keys for ECDSA, we need to select a random integer d and compute B = dA. This will now give us the two keys, as shown in Table 5.4. This process is much faster than generating an RSA key pair.

ECDSA signing

Signing a message follows the same approach as RSA. We first need to calculate a hash of the data to be signed. The ECDSA algorithm is certified for use with the SHA-1 and SHA-2 algorithms. Once the hash is calculated, we can calculate the final ECDSA signature. The “C” pseudo code is shown below:

R=keA

si17_e

r=xR

si18_e

s=hashx+d.rke1%q

si19_e

where r and s are the signature.

ECDSA verification

We can then verify the signature with the following calculations:

w=s1modqz=hashmessagesizeofqu1=zwmodqu2=rwmodqP=u1A+u2B

si20_e

If xp is the x coordinate of P, the signature is valid if xp = r mod q

Exercise: ECDSA

In this exercise, we will generate an ECDSA key pair and then compute and verify an ECDSA signature.

  • In the Pack Installer, select Exercise 5.7

We can first select an elliptic curve from our library.

          #define ECPARAMS MBEDTLS_ECP_DP_SECP192R1

Then create a context for the signing and verifying algorithms and initialize.

   mbedtls_ecdsa:context ctx_sign, ctx_verify;   mbedtls_ecdsa:init( &ctx_sign );   mbedtls_ecdsa:init( &ctx_verify );

In addition, we must create a random number generator so that we can generate a key pair.

   mbedtls_ecdsa:genkey( &ctx_sign,ECPARAMS,mbedtls_ctr_drbg_random,&ctr_drbg );

Before calculating the signature we must first calculate the message hash.

  •           unsigned char hash[] = = {0xFa,0xce,0x33......};

Now we can calculate the signature.

   unsigned char sig[512];   mbedtls_ecdsa:write_signature( &ctx_sign,MBEDTLS_MD_SHA256,hash,sizeof( hash ),sig,&sig_len,mbedtls_ctr_drbg_random,&ctr_drbg );

Now we have the signature we can verify it is correct. Our receiving station must setup the same elliptic curve and also copy the signing public key.

          &

Now we can verify the signature:

   mbedtls_ecdsa:read_signature( &ctx_verify,hash,sizeof( hash ),sig, sig_len );
  • Run the code and the results will be printed to the console window.

You will also see that the process of key generation, signing, and verification is significantly faster than the RSA examples.

Using asymmetrical ciphers

If we can give our public key to anyone, this enables them to encrypt and send a message that can only be decrypted using our private key. At first sight, it would appear possible to exchange keying material for a symmetrical cipher through a simple request and reply protocol similar to our DH key agreement exercise above. Unfortunately, this naïve use of asymmetrical encryption is prone to many attacks, not least of these being a “Man in the Middle” attack.

Man in the Middle

If we have two stations trying to establish a secure communication channel, they are traditionally called “Bob” and “Alice.” There is a possibility that a malicious third party can intercept their messages, traditionally called “Mallory” (Fig. 5.15).

Fig. 5.15
Fig. 5.15 Man in the Middle attack. A Man in the Middle attack allows “Mallory” to intercept communication between Alice and Bob. No permission required.

In this scenario, when Alice tries to send her public key to Bob, it can be intercepted by Mallory, who substitutes his public key and sends it on to Bob. If Bob tries to send his public key to Alice, Mallory is again able to capture Bob's key and again send his public key to Alice. If Bob or Alice use Mallory's public key to encrypt a message, Mallory will be able to intercept it and decrypt it with his private key and then re-encrypt it with the relevant public key before retransmitting the message. This allows Mallory to read all messages sent between Alice and Bob without either of them being aware of the interception.

Public key infrastructure

In order to prevent a “Man in the Middle” and similar attacks, asymmetrical ciphers are used within a scheme called the public key infrastructure (PKI). This scheme allows two stations to securely exchange public keys. The PKI rests on having a trusted third party called a certificate authority (CA) (Fig. 5.16).

Fig. 5.16
Fig. 5.16 Certificate authority. A certificate authority is a trusted 3rd party who signs certificates for Alice and Bob. No permission required.

Before they can communicate, Alice and Bob must send their public keys and credentials, including the name of their station. This can be a URL, hostname, or even an IP address. The certificate authority will then create a certificate that contains this information in a standard format. The data in the certificate are signed by the private key of the certificate authority and then returned to their owners. The certificate authority also publishes its own certificate, which contains its public key. The CA’s certificate is available to everyone, and it typically stored within all the participating stations. When Alice and Bob want to establish a secure channel, they can exchange their station certificates. When they receive a certificate, they can ensure its authenticity by using the certificate authorities public key extracted from the CA certificate to validate the signature of the received certificate. Once we have validated the received certificate, we have the public key of the station we want to communicate with. Since the certificate also contains credentials such as a URL or hostname, it is bound to its originating station. Thus, we can be sure that a malicious certificate has not been substituted by a Man in the Middle.

X.509 certificates

The certificates used in this scheme are held in a format called X.509. In addition to the senders’ public key, the certificate contains a subject field that identifies its origin. This includes the sender's name and location along with a field called the “common name.” The common name field is a text string and does not have a well-defined format. In fact, you can really put just about anything in here as it is just tested for equality by the receiving station. In practice, it will contain either a hostname or a URL. You could put in a raw IP address, but this is considered bad practice and should be avoided (Fig. 5.17).

Fig. 5.17
Fig. 5.17 X.509 certificate. An X.509 certificate is an structured array of data encoded using ASN.1 notation and signed by the CA private key. No permission required.

The X.509 certificate contains additional fields for the X.509 version, a certificate serial number, the issuing certificate authority, and its period of validity as a start and end date. The certificate also contains fields that define the algorithms used to sign the certificate. In order to sign the certificate, the certificate authority will calculate a hash of the certificate fields and then calculate the signature of the certificate by encrypting the hash using an asymmetrical cipher and the certificate authorities private key.

Certificate validation

Every station in our system will have the CA’s certificate and hence the CA’s public key. When we receive a new certificate, we can extract the calculated hash value from its signature using the CA’s public key. Next, we can calculate the certificate's hash and compare it to the signature hash. If the two match, then the certificate is valid. To fully trust it, we would next check its period of validity and whether the common name contains the hostname or URL of the station we are trying to communicate with. If we are satisfied with all of this information, then we can start to communicate with the remote station using its public key stored in the certificate.

Certificate lifetime

The mbedTLS library supports certificates being stored in a standard file system and allows them to be stored as arrays in FLASH memory. The “Period of Validity” field in the X.509 certificate gives every certificate a fixed lifetime and an expiration date. If you are designing a system where each node will have a long lifetime, you will likely have to update the certificates held in the IoT device. Since the CA certificate is public information, it does not need to be held in secure storage. This would allow you to either update the certificate directly if it is held in the file system or do a firmware update if the certificate is stored in FLASH as part of the application image.

Certificate revocation list

While a certificate will naturally expire and have to be replaced, it is likely that at some point, we will need to forcibly remove the rights of a node to connect to our system. The Transport Layer Security (TLS) protocol supports two methods of doing this. The first is a certificate revocation list (CRL). Here we can maintain a list of device certificates that are no longer allowed to join our system. The CRL is loaded by the server and acts as a blacklist of prohibited devices. In the broader internet, the certificate authority will maintain a CRL, which can be periodically downloaded to ensure that no revoked device can connect to the server. While CRLs are widely supported, a newer revocation method called online certificate status protocol (OCSP) is becoming the preferred method of checking the status of a certificate. The online certificate status protocol allows a node to directly query the current status of a certificate with the CA before allowing it to connect.

Certificate encoding

An X.509 certificate is a collection of strings and arrays that can be held as a C object. However, to enable data interchange between many diverse systems, we need to define a standard interchange format. The encoding rules for digital certificates are defined by a telecoms standard called Abstract Syntax Notation One or ASN.1. The ASN.1 standard is defined by the International Telecommunication Union and is an interface description language that is widely used in telecoms and computer networking systems. The ASN.1 standard defines a set of Basic Encoding Rules (BER), which provide a flexible system for encoding data. There is also a subset of rules called the Distinguished Encoding Rules (DER), which define an “unequivocal transfer syntax” or one way to encode and decode a given set of data. An X.509 certificate is encoded using the ASN.1 format with DER encoding. The output of this encoding will be a block of binary data. A certificate may be further encoded into a base 64 format which encodes the binary data as ASCII characters. This ASCII format was originally developed to send binary data as email attachments and is known as Privacy Enhanced Mail or PEM. When we store a certificate, it will either be in a binary format (.der) or an ASCII format (.pem), but either format contains the same information. The ASN.1 format can be described as fiendishly complicated, but fortunately, we do not need to work with it directly. We can use mbedTLS and other tools to generate and parse X.509 certificates.

Certificate authority selection

On the broader internet, there are many clients in the form of PCs, tablets, and mobile phones that want to connect to secure web servers. In this situation, we need the CA to be a trusted third party who can provide certificates to public websites and have their certificate stored in general users' devices. However, if you are going to design a closed system that only contains your own servers and devices, it is possible and usually desirable to be your own certificate authority. This creates a “walled garden” where only authenticated devices can connect to the server. Most commercial IoT cloud systems act as their own CA.

We can further improve the security of the system by issuing certificates to devices. In this case, the server and device exchange and validate each other's certificates. This form of mutual authentication provides a higher level of security and should be used for IoT systems whenever possible. We will set up both server authentication and mutual server/device authentication as we look at the TLS protocol. If you are using a commercial cloud system it should enforce mutual authentication.

Certificate chain

In the real world one, CA does not rule them all. There are a small number of root CA's who may sign the certificates for a regional or industry CA. This local CA can, in turn, sign an end-entity certificate. This creates a chain of certificates or certificate path. If you are working with a cloud server or commercial certificate authority, you may be issued a single certificate or a chain of certificates. In the case of a certificate chain, you will need to parse each certificate until you find one you can trust. This is fully managed by the mbedTLS library, so you don't need to develop any further code to parse the certificate chain.

Exercise: Creating X.509 certificates

In this exercise, we will look at how to create a set of X.509 certificates which are used to validate the credentials of both servers and clients. We will use XCA, which is an open-source tool to create the certificates. XCA has a graphical user interface which is useful when learning.

  • Download and install the XCA application.
  • In the Pack Installer select Exercise 5.8.
  • Start the XCA program.
  • Select Fileopen database and select the database in the Exercise 5.8 Creating X509 CertificatesXCA DB directory.
  • Enter the database password, which is of course, “password.”

The database contains three templates, which we can use to create a set of certificates for our network of IoT devices and servers.

Certificate authority

First we will create a “certificate authority” certificate, which will be used to sign the server and client certificates.

  • In the templates tab select the “Certificate Authority” template, right click and select “Create Certificate” (Fig. 5.18).
    Fig. 5.18
    Fig. 5.18 XCA user interface. Create a root certificate authority. No permission required.

Select the source tab This certificate will be our “Root of Trust” and, as such, will be self-signed. The algorithm used to sign the certificate is SHA256. This is the current minimum standard you should use to sign a certificate (Fig. 5.19).

  • Select the subject tab (Fig. 5.20).
    Fig. 5.19
    Fig. 5.19 Create the CA certificate. Select the signing algorithm. Use SHA256 and a minimum. No permission required.
    Fig. 5.20
    Fig. 5.20 Certificate subject. Set the certificate common name to IoT_CA. No permission required.

Enter an internal name and a common name. These can be different, but in this case, I have used IoT_CA. The template has set the other distinguished name fields, but you can change them to match your needs.

  • Select the Extensions tab (Fig. 5.21).
    Fig. 5.21
    Fig. 5.21 Certificate extensions. Here, we can set the certificate as a CA cert and also define its lifetime. No permission required.

Here, the type of certificate is configured as “certificate authority” and there is a default time range of 20 years.

  • Select the Key Usage tab.

This dialog allows you to limit the properties of the certificate. By leaving everything blank, we are not adding any restrictions to our basic certificate. Once you have a working system, you can experiment with these fields.

  • Go back to the subject tab (Fig. 5.22).
    Fig. 5.22
    Fig. 5.22 Generate key. Once we have defined the certificate we can generate the key pair. No permission required.
  • Press the create button to generate the key (Fig. 5.23).
    Fig. 5.23
    Fig. 5.23 Generate key size and type. Next we select the key type and size. No permission required.

Here, we will generate a 2048 bit RSA key

  • Finally, press OK on the subject dialog to create the certificate.

The new certificate and key can be viewed in the certificates window (Fig. 5.24).

Fig. 5.24
Fig. 5.24 Root certificate. Once the certificate is generated, it will be shown as a root certificate. No permission required.

and the private keys window (Fig. 5.25).

Fig. 5.25
Fig. 5.25 Generated private key. The private key icon is displayed, but its value is hidden until it is exported. No permission required.

The certificate authorities private key is securely held in the XCA database and must always be kept secret as it is used to sign the server and client certificates.

Server certificate

  • Go back to the templates tab and this time select the “broker” template and create a new certificate (Fig. 5.26).
    Fig. 5.26
    Fig. 5.26 Server certificate. Create a new certificate using the broker (IoT server) template. No permission required.

Here we are using the term Broker as another name for Server. We will see the role of a “broker” in the next chapter.

  • In the source tab select the IoT_CA certificate as the signing source (Fig. 5.27).
    Fig. 5.27
    Fig. 5.27 Signing the server certificate. The server certificate is signed by the CA private key. No permission required.
  • Select the subject tab (Fig. 5.28) and set the common name to IoT_Broker
    Fig. 5.28
    Fig. 5.28 Server subject. The server subject dialog sets the common name to IoT_Broker. No permission required.
  • Select the extensions tab (Fig. 5.29) and set the certificate type to end entity.
    Fig. 5.29
    Fig. 5.29 Certificate extensions dialog. Here, we can set the certificate type to End entity and also set a lifetime. No permission required.

This time the type of certificate is set to “End Entity” and the life time of the certificate is limited to 10 years.

  • Return to the subject tab and create the key and certificate as we did for the “certificate authority” certificate.

Device certificate

Now we are going to repeat the process one more time to create a device certificate. This will then give us all the certificate and keys we need to build a secure networked system.

  • In the templates tab select the device template, right click and select “Create Certificate” (Fig. 5.30).
    Fig. 5.30
    Fig. 5.30 Create Certificate. Create a new certificate using the device template. No permission required.
  • In the source field, we must again use the IoT_CA certificate for signing (Fig. 5.31).
    Fig. 5.31
    Fig. 5.31 Certificate signing. The device certificate is signed by the root CA private key. No permission required.
  • Select the subject dialog.
  • Fill out the internal and common name fields with your selected device name (Fig. 5.32).
    Fig. 5.32
    Fig. 5.32 Device certificate subject. The device certificate common name is set to IoT_Device. No permission required.
  • Here, I have used IoT_Device for the common name.
  • Select the certificates window (Fig. 5.33).
    Fig. 5.33
    Fig. 5.33 Final certificates. The XCA workspace shows the certificates and their hierarchy. No permission required.

Now in the certificate window, we can see the certificate hierarchy. The IoT_CA certificate is our “Root of Trust,” and it has signed both the broker and the device certificates. If you have the IoT_CA certificate installed, it can be used to check the signature of either the broker or the device certificate and authenticate the data in the certificate.

  • Select each certificate, right click and select export to file (Fig. 5.34).
    Fig. 5.34
    Fig. 5.34 Certificate export. Select a certificate and open the export certificate dialog. No permission required.

The certificate information is encoded in “Abstract Syntax Notation 1” (ASIN1) using the “Distinguished Encoding Rules” (DER). Here, the heavy lifting is done for us by XCA. When we save the certificate, the binary data is further transformed into a format called “Privacy Enhanced Mail” (PEM), which is base64 encoded binary with a short additional header.

  • Leave the export format as PEM and press OK (Fig. 5.35).
    Fig. 5.35
    Fig. 5.35 Certificate export dialog. Expert the certificates in PEM format. No permission required.
  • Select the Private Keys dialog.

Now we have three keys stored. We need to export the server and device keys so they can be stored in their respective devices. The certificate authority key must never be exposed outside of the XCA database (Fig. 5.36).

  • Select the IoT Server key right click and select export to file and save it in a PEM format. Repeat this for the device key

Fig. 5.36
Fig. 5.36 Certificate keys database. The keys database allows us to manage the private keys. No permission required.

Now in our certs directory, we should have five files: three certificates and two key files (Fig. 5.37). We will use these certificates for server and device authentication later in this chapter.

Fig. 5.37
Fig. 5.37 The network credentials. We now have a collection of certificates and private keys. No permission required.

Certificate and key storage

mbedTLS provides functions to load certificates and keys from static arrays stored in the microcontroller flash memory or from an embedded file system (Table 5.5). The certificates and keys can be stored in DER or PEM format. Keys can be exported and stored in a standard keyfile format that provides password-based encryption with symmetrical ciphers. As we will see in later chapters, the PSA Trusted Firmware creates a Secure Internal Storage volume to hold device secrets. In addition, a Cortex-M33 based microcontroller designed to be an IoT device is likely to provide a key store mechanism, and we will also look at these features in the coming chapters.

Table 5.5

Certificate and key storage.
FunctionDescription
mbedtls_x509_crt_parse()Load a certificate from an array
mbedtls_x509_crt_parse_file()Load a certificate from a filesystem
mbedtls_pk_parse_key()Load a key from an array with optional password
mbedtls_pk_parse_keyfile()Load a key from a filesystem stored as a keyfile with optional password

Exercise: Parsing X.509 certificates and keys

Here, we will look at how to load and parse stored certificates within our embedded microcontroller using the standard mbedTLS functions.

  • Open the pack installer, select project 5.9 and press the Copy button.
  • Open main.c

As usual, we start by declaring the necessary contexts that we will need in this example.

Here, we have a context for a certificate authority and a client certificate. We also have a public key context to load our private key.

   mbedtls_x509_crt cacert;   mbedtls_x509_crt clicert;   mbedtls_pk_context pkey;   mbedtls_x509_crt_init( &clicert );

We can then parse anX.509 certificate.

   mbedtls_x509_crt_parse( &clicert,(const unsigned char *) mbedtls_test_cli_crt,mbedtls_test_cli_crt_len );

Since this information is public information, we do not need to store it securely. It can be placed in the microcontroller flash memory as a “C” array.

  •           #define TEST_CA_CRT_EC                         
  •           "-----BEGIN CERTIFICATE----- "
  •           "          MIICUjCCAdegAwIBAgIJAMFD4n5iQ8zoMAoGCCqGSM49BAMCMD4xCzAJBgNVBAYT "
  •           "Ak5MMREwDwYDVQQKEwhQb2xhclNTTDEcMBoGA1UEAxMTUG9sYXJzc2wgVGVzdCBF "
  •           ………………………………..
  •           "uCjn8pwUOkABXK8Mss90fzCfCEOtIA  == "                  
  •           "-----END CERTIFICATE----- "

Alternatively, the certificate can be stored in the embedded file system and loaded from a file.

          mbedtls_x509_crt_parse_file (&cacert,"iot_ca.crt");

Once the certificates are loaded, we can verify the validity of the certificates.

   ret = mbedtls_x509_crt_verify( &clicert,&cacert,NULL,NULL,&flags,NULL,NULL );

Similarly, an RSA private key can be loaded directly from an array stored in flash memory.

   mbedtls_pk_parse_key( &pkey,(const unsigned char *) mbedtls_test_srv_key,mbedtls_test_srv_key_len,NULL,0 );

or loaded from a keyfile:

          int mbedtls_pk_parse_keyfile(&pkey,&keyfile,&password);

Once loaded we can test the RSA keys:

          mbedtls_pk_verify

  • Build the code and start the debugger.

The code will parse the certificates and display their details to the tera term console window.

Putting it all together

Over the last two chapters, we have looked at the essential cryptographic algorithms and the public key infrastructure. We can now look at how to establish a secure communications channel across the internet with the Transport Layer Security (TLS).

The TLS protocol is designed to be encapsulated in Transport Control Protocol packets, which in turn ride inside an Internet Protocol packet to provide a reliable streaming communications channel (Fig. 5.38).

Fig. 5.38
Fig. 5.38 TCPIP packet structure. The TLS packet rides inside the TCP frame. No permission required.

The TLS packet consists of the actual message data prefixed by a header that contains the protocol version, the length of the message data in bytes, and the content type, which is a code for the type of TLS packet being sent (Table 5.6).

Table 5.6

TLS content type.
Content typeType
20Change cipher spec
21Alert
22Handshake
23Application
24Heartbeat

When a TLS connection is established, further encrypted packets will be sent in application frames. If there is an error or if the connection needs to be shut down, an Alert frame will be sent. If you are sending infrequent message application packets but need to keep the connection from timing out, it is possible to send a heartbeat frame. When the connection is first negotiated, the server and client exchange a list of available ciphers. During a communication session, it is possible to switch ciphers suites by sending a change cipher spec message. A TLS session will always start with an initial handshake where the client and server authenticate each other and establish a shared secret which can then be used as the basis for a symmetrical encryption key.

The handshake packet has a further code that denotes the handshake content type for each stage of the TLS handshake (Table 5.7).

Table 5.7

Handshake types.
Message typeDescription
0Hello Request
1Client Hello
2Server Hello
4New Session Ticket
11Certificate
12Server Key Exchange
13Certificate Request
14Server Hello Done
15Certificate Verify
16Client Key Exchange
20Finished

At the start of a TLS session, both the client and server will have the CA certificate. In the case of a system that is setup for server authentication, the server will have its own certificate, which has been signed by the CA, and it will also have its own private key (Fig. 5.39).

Fig. 5.39
Fig. 5.39 TLS client hello. The TLS handshake starts with a client hello message to the server. No permission required.

At the start of the TLS handshake, the client will send a client hello message to the server. The client hello message from the client to the server will contain a random number which is sent in plain text along with a session ID, a list of cipher suites, and compression methods. The cipher suites are predefined groups of asymmetrical and symmetrical ciphers along with hash and MAC algorithms. A small sample set of these cipher suites is shown in Table 5.8.

Table 5.8

Cipher suite.
IDCipher suite (key exchange, signing, cipher, hash)
0x2FTLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
0x30TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
0x67TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
0x69TLS_DHE_RSA_WITH_AES_256_CBC_SHA256

The server will reply with its own server hello message that also contains a random number again sent in plain text.

The server will next send its X.509 certificate which can be verified by the client using the public key in the CA certificate (Fig. 5.40).

Fig. 5.40
Fig. 5.40 TLS server certificate. The server sends its certificate for verification using the CA certificate stored in the device. No permission required.

Once the certificate has been validated, it tells the client to authenticate its origin through the certificate common name. In the case of a website, the common name will be a URL that should match the address that the client is expecting to connect to. It is less clear what to use for an IoT device common name. Typical choices are the device IP address or its internal hostname. The certificate also provides the client with the servers’ public key.

Next, the client will generate another random number called a premaster secret, and this is again sent to the server, but this time, it is encrypted and hashed using the servers public key (Fig. 5.41).

Fig. 5.41
Fig. 5.41 Pre master secret exchange. The device sends an encrypted pre master secret to the server. No permission required.

Now both the server and client have two agreed random numbers which have been sent in plain text and a further random number that has been exchanged in secret. Using these numbers, we can create a master secret which will be the basis for our symmetrical encryption key. The master secret is derived through a special pseudo-random function (Fig. 5.42).

Fig. 5.42
Fig. 5.42 Master secret generation. The random and premaster secrets are used to generate a master secret. No permission required.

The two plain text random numbers are concatenated together and passed into the pseudo-random function along with the premaster secret. The output value from the pseudo-random function is then hashed using both an MD5 and an SHA-1 hash algorithm. The two message digests are finally added together modulo32 to provide the final master secret.

Once both sides have agreed on a master secret, the client will send a change cipher spec message to select a cipher suite followed by a finish handshake message. The server will reply with a matching change cipher spec and finish message (Fig. 5.43).

Fig. 5.43
Fig. 5.43 End of the TLS handshake. The TLS handshake ends with a change cipher spec to select the active cipher suite. No permission required.

Now both sides will switch to encrypted communication using the chosen cipher suite with the master secret as the agreed key. Your session data will be encrypted with a MAC tag and placed in a TLS application frame (Fig. 5.44).

Fig. 5.44
Fig. 5.44 Encrypted and MACed frame. The master secret is used to encrypt and MAC streaming data. No permission required.

Now both the server and the client have established a secure communication channel, and all further data packets will be encrypted. If an error occurs, either end can send an alert frame to notify its partner that there is a problem. The alert frame has an alert level field that can be set to a warning level or fatal level and a description field with 31 codes to describe the alert condition (Fig. 5.45). In practice, if an alert frame is sent, the session will usually terminate. An alert frame is also used to end a TLS session.

Fig. 5.45
Fig. 5.45 TLS alert frame. A TLS alert frame is used to signal errors and also finish a session. No permission required.

Establishing a TLS connection

We can create both a server and client with the mbedTLS library. To create a secure server, we first need to declare and initialize contexts for the SSL configuration and the SSL operating layer.

TLS server

   mbedtls_ssl_context ssl;   mbedtls_ssl_config conf;   mbedtls_ssl_config conf;   mbedtls_ssl_context ssl;

We also need to declare and initialize contexts for entropy, random, public key, X509 certificate, and network components in a similar fashion.

At the start of the example code, the server has to load the CA certificate and the servers’ certificate. At this point, we could also load a certificate revocation list.

  •           mbedtls_x509_crt_parse( &srvcert,
  • (const unsigned char *) mbedtls_test_srv_crt,
  • mbedtls_test_srv_crt_len );
  •           mbedtls_x509_crt_parse( &srvcert,
  • (const unsigned char *)
  • mbedtls_test_cas_pem,
  • mbedtls_test_cas_pem);

Next we can load the server private key.

   mbedtls_pk_parse_key( &pkey,(const unsigned char *) mbedtls_test_srv_key,mbedtls_test_srv_key_len,NULL,0 );

Now we can bind an IP port to the TCP protocol.

   mbedtls_net_bind( &listen_fd,NULL,"4433",MBEDTLS_NET_PROTO_TCP ) )

Our next step is to setup the ssl-config context with its default settings and to act as a server.

   mbedtls_ssl_config_defaults( &conf,MBEDTLS_SSL_IS_SERVER,MBEDTLS_SSL_TRANSPORT_STREAM,MBEDTLS_SSL_PRESET_DEFAULT )

Then we can configure and add a random number generator.

   mbedtls_ctr_drbg_seed( &ctr_drbg,mbedtls_entropy_func,&entropy,(const unsigned char *) pers,strlen( pers ) ) ) != 0 )   mbedtls_ssl_conf_rng( &conf,mbedtls_ctr_drbg_random,&ctr_drbg );

The TLS stack is instrumented to produce debug messages as it executes. The SSL Config context can be passed a user callback function to manage these messages. Normally, it can be written to a debug UART or the ITM, but it is also possible to log the debug data to a file.

          mbedtls_ssl_conf_dbg( &conf, my_debug, stdout );

During runtime, the callback function is passed the debug message, which consists of a debug code, the file, and line of origin and an informational text string.

   static void my_debug( void *ctx, int level,const char *file,
int line,  const char *str )

There are five levels of debug message and we can dynamically control the message threshold level (Table 5.9).

Table 5.9

Debug message level.
Threshold levelDescription
0None
1Error
2State Change
3Informational
4Verbose

mbedtls_debug_set_threshold( DEBUG_LEVEL );

Next we can add the certificates to the ssl configuration context.

          mbedtls_ssl_conf_ca_chain( &conf, srvcert.next, NULL );

Once the configuration structure is fully populated we can configure the working ssl context.

          mbedtls_ssl_setup( &ssl, &conf );

The mbedTLS library can be connected to the underlying transport by defining the send and receive callback functions.

mbedtls_ssl_set_bio( &ssl,&client_fd,mbedtls_net_send,mbedtls_net_recv,NULL );

Both the send and receive functions are designed to map onto a typical BSD sockets interface.

   int mbedtls_net_send( void *ctx,const unsignedchar *buf,size_t len ){   send (SOCK(ctx), (const char *)buf, len, 0);}   int mbedtls_net_recv (void *ctx,unsigned char *buf,size_t len){   recv (SOCK(ctx), (char *)buf, len, 0);}

Once all this is configured we can accept connections from clients.

          mbedtls_net_accept( &listen_fd, &client_fd, NULL, 0, NULL );

When a client accepts, we can process the TLS handshake to establish a secure connection.

          mbedtls_ssl_handshake( &ssl )

If the handshake is successful, we can now read and write plaintext application packets to and from the TLS library.

   mbedtls_ssl_write(&ssl, buf, len));   mbedtls_ssl_read(&ssl, buf, len);

If an error or other condition causes the server to shutdown, it must release each of the contexts.

   mbedtls_ssl_free( &ssl );   mbedtls_ssl_config_free( &conf );

TLS client

The Client code follows a very similar process, but there are a few key differences. The SSL_configuration context is set to be a client:

   mbedtls_ssl_config_defaults( &conf,MBEDTLS_SSL_IS_CLIENT,MBEDTLS_SSL_TRANSPORT_STREAM,MBEDTLS_SSL_PRESET_DEFAULT ) ;

During the SSL configuration, we need to set the authentication mode. This will tell the stack how to proceed if verification of the certificate fails. In the example code, VERIFY_OPTIONAL is used so that we will always establish a connection when using test certificates (Table 5.10).

Table 5.10

Authentication mode.
Authentication modeDescription
MBEDTLS_SSL_VERIFY_NONENo certificate verification is required
MBEDTLS_SSL_VERIFY_OPTIONALVerify the certificate but continue if verification fails
MBEDTLS_SSL_VERIFY_REQUIREDVerify the certificate and abort if verification fails
   mbedtls_ssl_conf_authmode( &conf,MBEDTLS_SSL_VERIFY_OPTIONAL );

We must also set the hostname of the server. This must match the Common Name on the servers X.509 certificate. While the certificate is public information, only the server has the matching private key.

          mbedtls_ssl_set_hostname( &ssl, "mbed Test Server 1" );

Once the SSL context is configured, we can connect to the server and perform the handshake.

   mbedtls_net_connect( &server_fd,SERVER_NAME,SERVER_PORT,MBEDTLS_NET_PROTO_TCP );   mbedtls_ssl_handshake( &ssl );

Once we have finished the handshake, we can verify the certificate.

          result = mbedtls_ssl_get_verify_result( &ssl );

If everything is OK, we can start to communicate to the server using the same ssl_read and write functions.

When the client has finished its session, we must close the TLS session and then free the contexts.

mbedtls_ssl_close_notify( &ssl );

Exercise: TLS server authentication

This exercise will configure the evaluation board as a client and use a PC server application based on mbedTLS as the server. This will give us a simple local network that can be used to test and experiment with x.509 certificates and the mbedTLS networking API.

  • Open the pack installer, select project 5.10 and press the copy button

The exercise directory contains an mbedTLS client project for the LPC55S69 evaluation board. This example uses the mbedTLS test certificates, which are provided as header files. The certificates will fail verification in a couple of ways which is useful to see.

  • Open and build the MDK-Arm project.
  • Start the debugger and run the code.

When the device connects to the WiFi Network, it will print out its IP address to the console window.

  • Make a note of the IP address and press the user button to continue.

The debugger console window will show the TLS connection initializing and then waiting for a connection (Fig. 5.46).

  • Now start a web browser and connect to the evaluation boards IP address and port 4433 using the following syntaxhttps://192.168.0.10: 4433
Fig. 5.46
Fig. 5.46 Console diagnostic messages. The TLS handshake session will be displayed as diagnostic messages within the tera term console window. No permission required.

The HTTPS is important as it tells the browser to establish a secure connection. The browser will report a bad certificate, but you can force it to proceed. It will connect to the evaluation board, and a HTML message will be sent to the browser (Fig. 5.47). The CA certificate has not been installed within the browser, we have just used the browser to show that the certificate is faulty.

  • In Explorer navigate to the project sub directory <  project  >PC_Client
  • Now start the ssl_client.exe.
Fig. 5.47
Fig. 5.47 Web browser connected to the evaluation board. A web browser will establish a connection to the evaluation board but will encounter some problems. No permission required.

This will connect to the same port and display more detailed diagnostic messages.

Again we can see that the certificate has failed. Two reasons are given: first, the Common Name is wrong, and second, the certificate has been signed an unacceptable hash, in this case, a deprecated SHA-1 algorithm was used (Fig. 5.48).

Fig. 5.48
Fig. 5.48 ssl_client console screen. The ssl_client will find the same problems with the X.509 certificate and print diagnostic messages. No permission required.

If the authentication mode is changed to “verification required” then the communication session would be terminated when the handshake fails.

   mbedtls_ssl_conf_authmode( &conf,MBEDTLS_SSL_VERIFY_REQUIRED);

Server and client authentication

So far, we have looked at a standard TLS handshake which is typically used when a web browser establishes a secure connection to a website. This system has the attraction that the client only needs to hold the CA certificate, which is public information and can have a long lifetime. However, for an IoT system, we really need to go a bit further and use a client certificate that will allow the server to authenticate the client. This mutual authentication provides a much higher level of security and can remove the need for a further sign-on password. However, it does mean that the client has to be provisioned with its own individual certificate and private key.

In such a system, the server will demand a client certificate once it has sent its certificate. Once the client has validated the server certificate, it will send its certificate to the server. The server will validate the client certificate and reply with a hash of all the existing handshake messages signed with the client's public key (Fig. 5.49).

Fig. 5.49
Fig. 5.49 TLS handshake with client certificate. The server can authenticate the client by requesting its certificate. No permission required.

In our existing server code, we can force the server to authenticate the client by setting an authentication mode:

   mbedtls_ssl_conf_authmode( &conf,MBEDTLS_SSL_VERIFY_REQUIRED );

The server will now request and validate the client certificate as part of the TLS handshake.

On the client side, in addition to the CA certificate, we need to load the client certificate:

   ret = mbedtls_x509_crt_parse( &clicert,(const unsigned char *) mbedtls_test_cli_crt,mbedtls_test_cli_crt_len );

Along with the client private key which must also be added to the TLS configuration context.

   ret = mbedtls_pk_parse_key( &pkey,(const unsigned char *) mbedtls_test_cli_key,mbedtls_test_cli_key_len,                              NULL,                              0 );   ret = mbedtls_ssl_conf_own_cert( &conf, &clicert, &pkey ) ;

Exercise: TLS server and client authentication

In this exercise, we will configure our network client and server to perform mutual authentication. The code is basically the same as the last example but has been extended to provision the client with a device certificate and private key. The server is configured to request the device certificate, which will be validated by the servers CA certificate.

  • In this exercise you can use the certificates created in exercise 5.9 or use the ones provided in the project directories.
  • Open the pack installer, select project 5.11 and press the copy button.
  • In explorer navigate to the subdirectory <  project  >PC_Server

Copy the CA and broker certificates created in project 5.9 plus the broker private key to the PC_Server directory or use the existing files.

  • Start the tls_server.exe project.
  • In explorer navigate to the <  project  >Embedded Clientcerts.
  • Copy the IoT_Device.crt certificate and IoT_Device.pem keyfile on to the SD card. Again use the existing files or the ones created in exercise 5.9
  • Place the SD card back into the Xpresso board.
  • Open the project in <  project  >Embedded Clientproject.
  • Examine main.c
  • Build the project and download.
  • Run the code.
  • Examine the debug console window.

This will establish a secure connection which mutually authenticates both the Server and the Client.

Conclusion

In the last two chapters, we have covered the main encryption algorithms that you are likely to need when developing an IoT device. While the focus here has been on secure communication, the same algorithms are used to provide protection for data at rest and also to sign and verify device images to establish trust in the integrity of a device's firmware. To finish the communication section, the next chapter will introduce protocols and data formats commonly used with IoT devices. We will also look at connecting our device to both a local server and a commercial cloud server.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.239.76.211