Skinny-C
 All Data Structures Files Functions Variables Groups Pages
Using Skinny-C

CTR mode

The API for Skinny-C provides CTR mode for bulk encryption of arbitrarily-sized data blocks. The first step is to initialize the CTR control structure with the key. For this example we will use a 256-bit key with Skinny128:

unsigned char key[32] = ...;
skinny128_ctr_set_key(&ctr, key, 32);

The skinny128_ctr_init() function chooses the best CTR mode implementation for your platform at runtime. For example, the AVX2 backend will be used if your CPU supports AVX2 and the library was compiled with AVX2 support enabled.

The next step is to set the counter value for the current data block. Counter values are typically formed by combining a packet sequence number with an offset into the packet. It is very important that the counter values be different from one packet to the next.

The following is an example of setting the initial counter value based on a 32-bit packet sequence number:

uint32_t seqnum = ...;
unsigned char counter[16];
memset(counter, 0, sizeof(counter));
counter[0] = (unsigned char)(seqnum >> 24);
counter[1] = (unsigned char)(seqnum >> 16);
counter[2] = (unsigned char)(seqnum >> 8);
counter[3] = (unsigned char)seqnum;
skinny128_ctr_set_counter(&ctr, counter, sizeof(counter));

The bytes at the end of the counter value will be incremented each time CTR mode needs a new 128-bit block of encrypted data. In this example there are 12 bytes available for incrementing.

If your application processes large amounts of data, then there is a risk that the sequence number might roll around. If this is the case, then you should use a 64-bit sequence number, or abort the connection when the sequence number rolls around, or both.

Encryption of packets is accomplished as follows:

unsigned char packet[MAX_PACKET_SIZE] = ...;
skinny128_ctr_encrypt(packet, packet, size, &ctr);

The first two parameters are the output buffer for the ciphertext and the input buffer for the plaintext. In this example we are encrypting the data in-place within the same buffer but they can be different.

To encrypt the next packet, increment the sequence number and then call skinny128_ctr_set_counter() and skinny128_ctr_encrypt() again.

In CTR mode, decryption and encryption are identical operations, so there is no "decrypt" function in the CTR API. Use skinny128_ctr_encrypt().

Once you have finished encrypting all packets, you should clean up the CTR control structure to free any dyanmically-allocated memory that was allocated by skinny128_ctr_init():

CTR mode with per-packet tweaks

SKINNY is a tweakable block cipher, so it is possible to encrypt packets with CTR mode in a slightly different way. Instead of passing the sequence number in as part of the counter block, the sequence number can be provided as a per-packet tweak.

This is definitely useful when encrypting packets with Skinny64 or Mantis as the 64-bit counter value is not large enough to contain both a 64-bit sequence number and a packet offset.

As before, we start by initializing the 128-bit key and the CTR control structure with Skinny64:

unsigned char key[16] = ...;

For each packet, the sequence number is provided as the tweak and the counter value is reset to all-zeroes:

uint64_t seqnum = ...;
unsigned char seqbuf[8];
seqbuf[0] = (unsigned char)(seqnum >> 56);
seqbuf[1] = (unsigned char)(seqnum >> 48);
seqbuf[2] = (unsigned char)(seqnum >> 40);
seqbuf[3] = (unsigned char)(seqnum >> 32);
seqbuf[4] = (unsigned char)(seqnum >> 24);
seqbuf[5] = (unsigned char)(seqnum >> 16);
seqbuf[6] = (unsigned char)(seqnum >> 8);
seqbuf[7] = (unsigned char)seqnum;
skinny64_ctr_set_tweak(&ctr, seqbuf, sizeof(seqbuf));
skinny64_ctr_set_counter(&ctr, NULL, 0);

Packet encryption is similar to before:

unsigned char packet[MAX_PACKET_SIZE] = ...;
skinny64_ctr_encrypt(packet, packet, size, &ctr);

Finally, clean up when the session ends:

ECB mode

Note
ECB mode can be unsafe when used to directly encrypt bulk data if you don't know what you are doing. Applications should use higher-level modes such as CTR, CBC, EAX, GCM, etc when handling bulk data. Failure to do this may compromise the security of the application.

The API for Skinny-C provides ECB encryption on a block-by-block basis. The first step is to initialize the key schedule using the key. For this example we will use a 256-bit key with Skinny128:

unsigned char key[32] = ...;
skinny128_set_key(&ks, key, 32);

Each block of the plaintext is then encrypted as follows:

unsigned char plaintext[16] = ...;
unsigned char ciphertext[16];
skinny128_ecb_encrypt(ciphertext, plaintext, &ks);

To decrypt a ciphertext back to the original plaintext:

unsigned char ciphertext[16] = ...;
unsigned char plaintext[16];
skinny128_ecb_decrypt(plaintext, ciphertext, &ks);

Parallel ECB mode

The API for Skinny-C provides an alternative ECB interface that encrypts multiple blocks in parallel on platforms that have SIMD instruction support. This can be useful for implementing block cipher modes like OCB that operate on blocks in parallel.

Note
CTR mode also encrypts multiple blocks in parallel when SIMD support is available. There is dedicated support for CTR mode in the library so it isn't necessary to use the parallel ECB API for CTR.

To perform parallel encryption, we start by initializing the parallel ECB context and then set the key:

unsigned char key[32] = ...;

The skinny128_parallel_ecb_init() function chooses the best parallel ECB implementation for your platform at runtime. For example, the AVX2 backend will be used if your CPU supports AVX2 and the library was compiled with AVX2 support enabled.

Encryption of eight blocks at a time is accomplished as follows (decryption is similar):

unsigned char blocks[8 * SKINNY128_BLOCK_SIZE] = ...;
skinny128_parallel_ecb_encrypt(blocks, blocks, sizeof(blocks), &ecb);

The first two parameters are the output buffer for the ciphertext and the input buffer for the plaintext. In this example we are encrypting the data in-place within the same buffer but they can be different.

The amount of parallelism is dependent upon the specific back end implementation. The best parallel block size (in bytes) is given by ecb.parallel_size. This can be used to tune the parallel block size at runtime.

Once you have finished encrypting or decrypting data, you should clean up the parallel ECB control structure to free any dyanmically-allocated memory that was allocated by skinny128_parallel_ecb_init():