WF(M)200 Series Secure Link
Introduction
Silicon Labs WF(M)200 chips have the ability to encrypt the SPI or SDIO serial link between the Host and the Device.
The Secure Link capability is only available for trusted chips.
The application note covers the following topics:
- Secure Link Basic Concepts
- Encryption Bitmap
- First Time Configuration for Enforced Mode
- Startup Sequence for Enforced Mode Device
- Startup Sequence for Evaluation Mode Device
- Session Key Computation Algorithm
- Host Software Implementation Guidelines
- Frequently Asked Questions
Secure Link Basic Concepts
- When the Secure Link feature is available and initialized, all messages exchanged between Host and Device may be encrypted (according to the encryption bitmap), except for TX/RX data packets .
- Whether or not a given message should be encrypted depends on the value of the Secure Link Encryption Bitmap . This bitmap is set by the Host during the startup sequence .
- An encrypted command consists in a special header format followed by an AES-CCM encrypted payload .
- The cryptographic key used to encrypt messages is called Session Key . It is dynamically computed during the startup sequence , has a maximum lifetime and can be recomputed at the Host initiative if required. Two different cryptographic algorithms are provided for the Session Key computation that differs in 'Security strength' vs 'Host CPU horsepower' criterions.
- When enabled, the Secure Link feature is set by default in an evaluation mode. Devices intended to go on-field should be configured in enforced mode .
- TX Data traffic encryption is not supported by the Secure Link and should be handled at the Application Level if required.
Encryption Bitmap
The encryption bitmap defines which messages should be exchanged as encrypted and which ones should remain clear.
It consists in a 32-byte array (i.e., 256 bits), little-endian encoded:
- Bit 0 of the bitmap is the bit 0 at address 0
- Bit 255 of the bitmap is the bit 7 at address 31
Each bit defines the required encryption state of a message type, based on its message id . For example, bit 11 defines the encryption state for WSM_HI_JOIN_REQ_BODY request (Id = 0x0b):
- 1 : the request should be exchanged encrypted
- 0 : the request should be exchanged clear
The whole bitmap can be set globally using a dedicated command HI_SL_CONFIGURE_REQ_BODY and therefore defines the encryption type for all messages (requests, confirmations, indications).
Some messages are never encrypted. The corresponding bit in the bitmap is meaningless for the following messages:
- HI_STARTUP_IND_BODY
- HI_SL_EXCHANGE_PUB_KEYS_REQ_BODY, HI_SL_EXCHANGE_PUB_KEYS_CNF_BODY and HI_SL_EXCHANGE_PUB_KEYS_IND_BODY
- HI_SET_SL_MAC_KEY_REQ_BODY and HI_SET_SL_MAC_KEY_CNF_BODY
- HI_PREVENT_ROLLBACK_REQ_BODY and HI_PREVENT_ROLLBACK_CNF_BODY
- HI_ERROR_IND_BODY and HI_EXCEPTION_IND_BODY
- WSM_HI_TX_REQ_BODY and WSM_HI_MULTI_TRANSMIT_CNF_BODY
Depending on the Secure Link mode of the device, the bit corresponding to HI_SL_CONFIGURE_REQ_BODY must OR must not be set in the bitmap:
- For Secure Link enforced devices, the bit must always be set since the device will always return an encrypted confirmation for this message.
- For Secure Link evaluation devices, the bit must always be cleared since the device will always return a clear confirmation for this message. If the bit is not properly set, it will be ignored by the device firmware that will return the confirmation as previously described.
First Time Configuration for Enforced Mode
To prevent a way to bypass the message encryption required by the Host, configure an On-field Device with Secure Link capability to enforced mode by sending the HI_SET_SL_MAC_KEY_REQ_BODY message to the Device just after Reset. This message carries the value of the Secure Link MAC Key , which will be burned in Device OTP memory once and for all. This key is used in the process of the Session Key Computation to authenticate peers during the key exchange and prevent Man-In-The-Middle attacks.
Secure Link MAC Key is a shared secret that must be stored in the Host non-volatile memory as well.
Once burned, the Device is definitely configured in enforced mode.
Startup Sequence for Enforced Mode Device
The following diagram presents the mandatory startup sequence, which must occur before any encrypted message can be sent or received.
-
After reset, Device sends a Startup Indication to the Host, indicating (among other information) its Secure Link capability (e.g., Secure Link in Enforced mode).
-
Upon reception of the Startup indication , Host must send a HI_SL_EXCHANGE_PUB_KEYS_REQ_BODY request which includes its curve25519 public key and an associated MAC ( Message Authentication Code ). Device acknowledges the request immediately by sending a confirmation, and starts computing its own curve25519 key pair and the resulting session key. After completion, Device sends back its own public key and MAC in a dedicated indication. The Host is in turn able to compute the session key , as explained in this section .
-
After Session Key is computed, the Encryption Bitmap must be set to specify which messages should be encrypted or not. Host will issue a HI_SL_CONFIGURE_REQ_BODY request with the required bitmap as parameter. Device will acknowledge the new bitmap by sending it back in the confirmation message. In Enforced mode, this confirmation must be sent encrypted.
After the Encryption Bitmap is set, the Secure Link is fully configured and Host and Device can exchange encrypted messages.
Secure Link Enforced Diagram
Startup Sequence for Evaluation Mode Device
The following diagram presents the mandatory startup sequence for a chip in Evaluation mode. In this mode, Host is required to set-up a Secure Link MAC Key in the RAM of the Device by sending a Set SL MAC Key request. Once done, the key exchange procedure can continue as usual. On the contrary to Enforced mode, the HI_SL_CONFIGURE_REQ_BODY request must be sent in clear format.
Secure Link Evaluation Diagram
Session Key Computation Algorithm
HI_SL_EXCHANGE_PUB_KEYS_REQ_BODY message has a special Algorithm flag that is used by the Host to specify which cryptographic algorithm should be ran to compute the session key:
- Elliptic Curve Diffie-hellman (ECDH) curve25519
- Key Derivation Function (KDF) based on SHA256
The first algorithm provides strong security particularly by providing forward secrecy , but assumes that the Host processor embeds some cryptographic hardware capabilities, since curve25519 software implementations can take several seconds to compute on small MCUs. KDF on the other hand is very lightweight from a computational point of view and is particularly well suited for small MCUs without hardware support for elliptic curve. The counterpart is the loss of the forward secrecy property.
Host Software Implementation Guidelines
The following sections provide guidelines and code examples to help developing a Host driver with Secure Link capabilities.
Session Key Computation
The next sections present Session Key computation for curve25519 and KDF algorithms, which includes 3 main steps
- Host Public Key generation (curve25519) / Random number generation (KDF)
- Device Public Key (curve25519)/Random number (KDF) authentication checking
- Session Key computation
Host Public Key Generation (Curve25519 Algorithm)
Prior to the mandatory key exchange, Host must generate a curve25519 key pair. The public key will be sent as part of the HI_SL_EXCHANGE_PUB_KEYS_REQ_BODY payload. The other requested parameter is the MAC of the key, which is computed using SHA512 -based HMAC algorithm. The key used for HMAC is the SL MAC Key .
Warning : Device is expecting the Host Public Key to be sent in little-endian format.
The following code gives an example of Public Key and MAC generation using mbed TLS library.
#define SL_MAC_KEY_SIZE (256/8)
#define HOST_PUB_KEY_MAC_SIZE (512/8)
#define HOST_PUB_KEY_SIZE (256/8)
status_t exchange_sl_keys(const uint8_t* sl_mac_key)
{
// Public Key + MAC buffers
uint8_t HostPubKey[HOST_PUB_KEY_SIZE];
uint8_t HostPubKeyMac[HOST_PUB_KEY_MAC_SIZE];
// mbedtls objects declaration
mbedtls_ecdh_context host_context;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context host_drbg_context;
mbedtls_md_context_t host_message_digest_context;
mbedtls_md_type_t md_type = MBEDTLS_MD_SHA512;
// mbedtls objects init
mbedtls_ecdh_init(&host_context);
mbedtls_ctr_drbg_init(&host_drbg_context);
mbedtls_entropy_init(&entropy);
// Compute curve25519 key pair
result |= mbedtls_ctr_drbg_seed(&host_drbg_context, mbedtls_entropy_func, &entropy, (const unsigned char *)identifier, sizeof(identifier));
result |= mbedtls_ecp_group_load(&host_context.grp, MBEDTLS_ECP_DP_CURVE25519);
result |= mbedtls_ecdh_gen_public(&host_context.grp, &host_context.d, &host_context.Q, mbedtls_ctr_drbg_random, &host_drbg_context);
result |= mbedtls_mpi_write_binary(&host_context.Q.X, HostPubKey, HOST_PUB_KEY_SIZE);
ERROR_CHECK( result );
// Reverse key endianess to match Device requirement (little-endian)
reverse_bytes(HostPubKey, HOST_PUB_KEY_SIZE);
// Generate SHA512 HMAC of public key
mbedtls_md_init(&host_message_digest_context);
result |= mbedtls_md_setup(&host_message_digest_context, mbedtls_md_info_from_type(md_type), 1); //use hmac
result |= mbedtls_md_hmac_starts(&host_message_digest_context, sl_mac_key, SL_MAC_KEY_SIZE);
result |= mbedtls_md_hmac_update(&host_message_digest_context, HostPubKey, HOST_PUB_KEY_SIZE);
result |= mbedtls_md_hmac_finish(&host_message_digest_context, HostPubKeyMac);
ERROR_CHECK( result );
....
Host Random Number Generation (KDF Algorithm)
For KDF algorithm , Session key generation relies on two 256-bit random numbers generated by each peer of the Secure Link. So Host needs to generate a pure random 256-bit number that will be sent to the Device by HI_SL_EXCHANGE_PUB_KEYS_REQ_BODY Host Public Key parameter.
The other requested parameter is the MAC of the key, which is computed using SHA512 -based HMAC algorithm as for curve25519. See previous section for a code example.
Device Public Key / Random Number Checking
When HI_SL_EXCHANGE_PUB_KEYS_IND_BODY gets back, Host must check the received Device Public Key (or Random number in case of KDF algorithm) by computing MAC on the received key and compare the result to the MAC received in the indication.
Warning : for curve25519, Device is sending its Public Key in little-endian format.
The following code is an example of Public Key checking. NcpPubKey and NcpPubKeyMac buffers are supposed to hold the values from the received indication.
// Temporary buffer
uint8_t temp_buffer[512/8];
// Compute the Hash and verify the public key/hashing
mbedtls_md_context_t device_hmac_context;
mbedtls_md_init(&device_hmac_context);
mbedtls_md_setup(&device_hmac_context, mbedtls_md_info_from_type(md_type), 1); //use hmac
mbedtls_md_hmac_starts(&device_hmac_context, sl_mac_key, SL_MAC_KEY_SIZE);
mbedtls_md_hmac_update(&device_hmac_context, NcpPubKey, NCP_PUB_KEY_SIZE);
mbedtls_md_hmac_finish(&device_hmac_context, temp_buffer);
// Check HMAC
if (memcmp(temp_buffer, NcpPubKeyMac, NCP_PUB_KEY_MAC_SIZE) == 0)
{
// Received key seems OK -> go on with session key computation
...
Session Key Computation (Curve25519 Algorithm)
After the Device Public key has been received and checked, it can be used to compute the Session Key. The following code is an example of Session Key computation based on the Device Public Key and Host Private Key. Note that a last pass of SHA256 is ran on the result of the curve25519 shared secret. The 16 lower bytes are extracted to form the final Session key.
// Calculate session key if public key/SHA512 digest matches
mbedtls_mpi_lset(&host_context.Qp.Z, 1);
// Revert endianess of Device key since mbedtls works in big-endian
reverse_bytes(NcpPubKey, NCP_PUB_KEY_SIZE);
mbedtls_mpi_read_binary(&host_context.Qp.X, NcpPubKey, NCP_PUB_KEY_SIZE);
// Compute curve25519 shared secret
if (mbedtls_ecdh_compute_shared(&host_context.grp, &host_context.z, &host_context.Qp, &host_context.d, mbedtls_ctr_drbg_random, &host_drbg_context) == 0)
{
// Generate session key
uint8_t shared_key_digest[256/8];
mbedtls_mpi_write_binary(&host_context.z, temp_buffer, HOST_PUB_KEY_SIZE);
// Revert bytes to match endianess of the Device
reverse_bytes(temp_buffer, HOST_PUB_KEY_SIZE);
// Apply sha256 on shared secret
mbedtls_sha256(temp_buffer, HOST_PUB_KEY_SIZE, shared_key_digest, 0);
// Get only the lower 16 bytes
memcpy(secure_link_session_key, shared_key_digest, 16); // Use the lower 16 bytes of the sha256
....
....
.....
// At then end, do not forget to clean-up all mbedtls objects...
mbedtls_ecdh_free(&host_context);
mbedtls_ctr_drbg_free(&host_drbg_context);
mbedtls_entropy_free(&entropy);
Session Key Computation (KDF Algorithm)
Once Device random number has been extracted and checked from SL_EXCHANGE_PUB_KEYS_IND_BODY, the Session Key can be computed. KDF algorithm is based on HMAC-SHA256 with the following inputs:
- Key: Secure Link MAC Key
-
HMAC input: concatenation of the following sub-fields
- '1' value encoded on 16-bits, little-endian format
- constant label equal to Secure Link. Key derivation string encoded in UTF-8 (24 bytes)
- Host random number (32 bytes)
- Device random number (32 bytes)
- Output key bit length (128) encoded on 16-bits, little-endian format
Only the first 16 bytes of the HMAC result are extracted to get the final Session Key .
The following Python code is an example implementation.
label = bytes("SecureLink!KeyDerivation", 'utf-8')
hmac_input = bytes(int2bytelist(1, 2, ENDIANNESS.LITTLE_ENDIAN)) +
label +
bytes(secure_link.host_pub_key) +
bytes(secure_link.ineo_pub_key) +
bytes(int2bytelist(128, 2, ENDIANNESS.LITTLE_ENDIAN))
session_key = hmac.new(bytes(secure_link.sl_mac_key), hmac_input, 'sha256').digest()[:16]
Message Encryption
When the Host has to send an encrypted request to the device, apply the following operations to the clear request message before sending:
- Insert a Secure Link header in front of the clear request header
- Let the MsgLen field of the HIF requets header in clear
- Encrypt the second half of the request header plus the request payload using AES-CCM algorithm
Secure Link Header
This 32-bit length header includes the following information:
- Bits [29-0]: Nonce counter value
- Bits [31-30]: Nonce counter type
Immediately after reset, the Nonce counter value should be initialized at 0 then be incremented after each request sending. In the context of Host sending request to Device, the Nonce counter type must be set to b01 to indicate that a TX counter is sent.
The following table shows the encoding of bits[31:30]:
Bits[31:30] | Meaning |
---|---|
b00 | The message is not encrypted |
b01 | The message is encrypted - Nonce counter type is TX |
b10 | The message is encrypted - Nonce counter type is RX |
b11 | The message is encrypted - Nonce counter type is HP (Reserved) |
The header is encoded as little-endian - bit 0 is the first bit of the lower address byte.
Clear Message Encryption
Encrypt both header and payload of the clear request using AES-CCM . Prior to running the encryption, the request must be padded with 0x00 so that the whole header+payload packet is 16-byte aligned. Note that an additional 16-byte CCM tag must be appended after the encrypted payload to allow message authentication by the Device.
AES-CCM Nonce Format
AES-CCM requires a 12-byte length buffer to be passed as input. Depending on the kind of the encrypted message (request, confirmation, indication), the Nonce structure should be as follows:
Message Type | Nonce structure |
---|---|
TX (Requests) | 0x00000000, 0x00000000, TX Counter (4 bytes, Little-endian) |
RX (Confirmations/Indications) | 0x00000000, RX Counter (4 bytes, Little-endian), 0x00000000 |
HP (reserved) | HP Counter (4 bytes, Little-endian), 0x00000000, 0x00000000 |
Encryption Code Example
The following code gives an example of an AES-CCM encryption using mbed TLS library.
#define SECURE_LINK_CCM_TAG_SIZE (16)
#define SECURE_LINK_SESSION_KEY_LENGTH (16)
#define SECURE_LINK_SESSION_KEY_BIT_COUNT (SECURE_LINK_SESSION_KEY_LENGTH * 8)
#define SECURE_LINK_NONCE_SIZE_BYTES (12)
#define HDR_MSG_LEN_SIZE (2)
void encrypt_message( uint8_t* msg, uint8_t msg_length, uint8_t* session_key, uint8_t* nonce )
{
mbedtls_ccm_context* ccm_context;
uint8_t *encrypted_msg;
uint16_t encrypted_msg_length;
ccm_context = malloc( sizeof(mbedtls_ccm_context) );
mbedtls_ccm_init( ccm_context );
encrypted_msg_length = msg_length + SECURE_LINK_CCM_TAG_SIZE - HDR_MSG_LEN_SIZE;
encrypted_msg = malloc( encrypted_msg_length );
mbedtls_ccm_setkey( ccm_context, MBEDTLS_CIPHER_ID_AES, session_key, SECURE_LINK_SESSION_KEY_BIT_COUNT )
mbedtls_ccm_encrypt_and_tag( ccm_context, msg_length, nonce, SECURE_LINK_NONCE_SIZE_BYTES, NULL, 0, msg + HDR_MSG_LEN_SIZE, encrypted_msg, encrypted_msg + msg_length - HDR_MSG_LEN_SIZE, SECURE_LINK_CCM_TAG_SIZE );
// Copy back to same location (+ 2 bytes) to leave room for Secure Link header
memcpy( msg + SECURE_LINK_HEADER_SIZE + HDR_MSG_LEN_SIZE, encrypted_msg, encrypted_msg_length );
.... Send encrypted message ....
free( encrypted_msg );
free( ccm_context );
}
Message Decryption
When the Host has to receive an encrypted confirmation/indication from the device, apply the following operations to retrieve the clear message:
-
Extract and check information from the
Secure Link header
- In the context of the Host receiving a message from Device, the first parameter to check is the value of header[31:30] bits. 'b00' means an non-encrypted message. 'b10' means an encrypted RX message. Other values are meaningless in this context and should be ignored.
- Build the AES-CCM Nonce using the received counter.
- Decrypt and check the encrypted message using AES-CCM algorithm
AES-CCM Nonce Building
If the header[31:30] bits are equal to 'b01', the header[29:0] remaining bits encode the value of the RX counter. This value should be extracted and used to form the whole 12-byte AES Nonce as described in this table .
Clear Message Decryption
After AES Nonce is created, the message payload can be decrypted and checked using AES-CCM .
After decryption, some CCM padding bytes might be removed if message encryption length is not a multiple of 16.
Decryption Code Example
The following code is an example of an AES-CCM decryption using mbed TLS library.
In the following code, msg is assumed pointing on the first encrypted byte (i.e., the second half of the request header) and msg_length is assumed to give the encryption length (which includes the CCM tag size)
status_t decrypt_message(uint8_t* msg, uint8_t msg_length, uint8_t* session_key, uint8_t* nonce)
{
mbedtls_ccm_context* ccm_context;
uint8_t* unencrypted_msg;
status_t status = FAILURE;
ccm_context = malloc( sizeof(mbedtls_ccm_context) );
unencrypted_msg = malloc( msg_length );
mbedtls_ccm_init( ccm_context );
if ( mbedtls_ccm_setkey( ccm_context, MBEDTLS_CIPHER_ID_AES, session_key, SECURE_LINK_SESSION_KEY_BIT_COUNT ) == 0 )
{
if(mbedtls_ccm_auth_decrypt( ccm_context, msg_length - SECURE_LINK_CCM_TAG_SIZE, nonce, SECURE_LINK_NONCE_SIZE_BYTES, NULL, 0, msg, unencrypted_msg, ( &msg[msg_length] ) - SECURE_LINK_CCM_TAG_SIZE, SECURE_LINK_CCM_TAG_SIZE ) != 0)
{
// Something failed ... (Authentication ?)
status = FAILURE;
} else {
// Copy clear message back, without the CCM Tag
memcpy( msg, unencrypted_msg, msg_length - SECURE_LINK_CCM_TAG_SIZE );
status = SUCCESS;
}
}
.... Process message ....
// Free memory
free( unencrypted_msg );
free( ccm_context );
return status;
}
Session Key Renegotiation
The Session key should be renegotiated before Nonce counter values exceed the max limit (2^30-1). The Host should track Nonce counter values and request Session Key renegotiation when needed.
Unless DisableSessionKeyProtection was set to 1 when configuring the Secure Link, the Device is checking Nonce limit violation on its side and will emit an error indication when detected.
The Host should use the following sequence to renegotiate the Session Key.
During the transition period when the key exchange request has been sent and indication has not arrived, some RX traffic may be sent by the Device which is encrypted with the current key. It means that Host should keep the current key value until the key exchange indication arrives. Also, Host should stop sending any new encrypted requests since Device is in the process of exchanging its key.
After the indication has arrived, all new RX traffic from Device will be encrypted with the new key . Since Host has to compute the new key on its side, it should buffer all RX traffic until the new key is computed. Then, it will be able to decrypt already received messages and resume sending new requests encrypted with the new key .
Contrary to the Startup sequence, sending an SL Configure request after exchanging the new keys is not mandatory since the original encryption bitmap has not been changed by the renegotiation process and remains valid.
Frequently Asked Questions
Device Trusted or Not
Upon Firmware startup, the Device is sending a first startup indication message. The Startup Indication includes a capabilities.sl_mode sub-field whose value indicates if the device is trusted (i.e., the Secure Link feature is available) or not, and its mode ( Evaluation or Enforced ).
Evaluation vs Enforced Modes
Evaluation Mode
Evaluation mode is mostly dedicated to Development Kits devices. In Evaluation mode, the OTP memory does not contain the Secure Link MAC Key value. A temporary key can be loaded in the RAM memory of the Device (using HI_SET_SL_MAC_KEY_REQ_BODY with RAM as key destination) that will be used to generate a Session Key for the current Power Cycle . This way, the Host can encrypt/decrypt messages to/from the Device and evaluate the Secure Link capability. In this mode, the default value for the Encryption Bitmap just after Reset is 0x0000...0, meaning that no encryption is required by default.
Enforced Mode
Enforced mode is mostly dedicated to On-field production devices. In Enforced mode, the value of the Secure Link MAC Key has already been burned in OTP memory (using HI_SET_SL_MAC_KEY_REQ_BODY with OTP as key destination) and cannot be modified. The default value for the Encryption Bitmap just after Reset requires all messages to be encrypted except for these exceptions .