Skip to content
This repository has been archived by the owner on Feb 18, 2021. It is now read-only.

Latest commit

 

History

History
79 lines (49 loc) · 4.52 KB

1a.md

File metadata and controls

79 lines (49 loc) · 4.52 KB

Cipher Set 1a

This is a minimum lightweight profile to support embedded devices and low resource environments. It has additional constraints in place to minimize the code size and bytes on the wire (prioritized over speed) while maintaining a modern level of privacy.

The base algorithms used in this set are chosen to be readily implementable on embedded hardware (8bit 16mhz 32k AVR in < 1 second) and are considered minimum-grade security:

  • ECC secp160r1 - small key sizes, balance of relatively strong crypto and still supportable with low CPU
  • HMAC-SHA256 - common implementations available for embedded environments
  • AES-128-CTR - low impact streaming cipher, many implementations including hardware ones

Keys

When generating an endpoint including CS1a, the public/private keypair is an ECC secp160r1 curve and the binary public key format is compressed, 21 bytes in length.

One key is generated permanently to identify the local endpoint, and one ephemeral key is generated on demand for every exchange created.

Message BODY

The BODY of any message packet is binary and defined with the following byte sections in sequential order:

  • KEY - 21 bytes, the sender's ephemeral exchange public key in compressed format
  • IV - 4 bytes, a random but unique value determined by the sender
  • INNER - the AES-128-CTR encrypted inner packet ciphertext
  • HMAC - 4 bytes, the calculated HMAC of all of the previous KEY+IV+INNER bytes

By performing ECDH with the received ephemeral key and the recipient's identity key, the resulting 20 byte secret is SHA-256 hashed and folded once to create the 16 byte AES-128 key along with the given IV (right-zero-padded to 16 bytes).

Another ECDH is performed with the sender and recipients identity key, and that 20 byte secret is combined with the given IV and input to HMAC-SHA256 of the entire BODY bytes (KEY+IV+INNER) minus the HMAC, then folded three times to get the 4 byte verification value that must match/be the last 4 bytes in the BODY.

Channel Setup

An exchange can generate or process channel packets once it has received a valid handshake. The channel encryption is generated by using ECDH with the sent and received ephemeral keys from the handshakes, then performing a SHA-256 hash of the resulting 20 byte secret combined with the 21 byte compressed key values in each direction, and folding the 32 byte digests once to get the required 16 byte encryption and decryption key values for AES-128.

  • channel encryption key: SHA256(secret, sent-KEY, received-KEY) / 2
  • channel decryption key: SHA256(secret, received-KEY, sent-KEY) / 2

Channel BODY

CS1a channel packets are designed to be very lightweight with minimum overhead for use on networks such as 802.15.4 where there is a very low MTU. The BODY is binary and defined as:

  • TOKEN - 16 bytes, from the handshake, required for all channel packets
  • IV - 4 bytes, incremented sequence
  • INNER - the AES-128-CTR encrypted inner packet ciphertext
  • HMAC - 4 bytes, the SHA-256 HMAC folded three times

The IV must be initialized to 4 random bytes during channel setup to make the individual channel packets less identifiable, and then incremented for every new channel packet created.

Using the correct channel 16 byte key with the given IV (right-zero-padded to the required 16 bytes), the channel packet can be encrypted/decrypted with AES-128-CTR.

The ciphertext then has a HMAC value calculated, with the input key being the same 16 byte key used for AES concatenated with the 4 byte IV to be a 20 byte secret key, and the body being the output of AES.

Folding

In order to minimize the over-the-wire footprint and match key sizes while using the same SHA-256 hashing algorithm that is required elsewhere, a 32-byte hash is folded once into a 16-byte value for usage as the fingerprint and as the input key for AES-128, and the 32-byte HMAC digest is folded three times into a smaller 4-byte value for usage as the MAC on message and channel packets.

The folding is a simple XOR of the lower half bytes with the upper ones, as in this pseudocode example:

var digest = sha256("foo");
var folded = digest.slice(0,16);
for(i = 0; i < 16; i++) folded[i] = folded[i] ^ digest[i+16];

The triple fold uses progressively smaller chunks of the input, as in this C code

void fold3(uint8_t in[32], uint8_t out[4])
{
  uint8_t i, buf[16];
  for(i=0;i<16;i++) buf[i] = in[i] ^ in[i+16];
  for(i=0;i<8;i++) buf[i] ^= buf[i+8];
  for(i=0;i<4;i++) out[i] = buf[i] ^ buf[i+4];
}