1
votes

I need to do AES-128/CTR encryption/decryption using Intel TinyCrypt (written in C). I took a look at the test case provided by the library, but still have a few questions on how to use it. Here's the code of the test case (see the complete source code here):

/*
 * NIST SP 800-38a CTR Test for encryption and decryption.
 */
uint32_t test_1_and_2(void)
{
    const uint8_t key[16] = {
        0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88,
        0x09, 0xcf, 0x4f, 0x3c
    };
    uint8_t ctr[16] = {
        0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb,
        0xfc, 0xfd, 0xfe, 0xff
    };
    const uint8_t plaintext[64] = {
        0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11,
        0x73, 0x93, 0x17, 0x2a, 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
        0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, 0x30, 0xc8, 0x1c, 0x46,
        0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
        0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b,
        0xe6, 0x6c, 0x37, 0x10
    };
    const uint8_t ciphertext[80] = {
        0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb,
        0xfc, 0xfd, 0xfe, 0xff, 0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26,
        0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce, 0x98, 0x06, 0xf6, 0x6b,
        0x79, 0x70, 0xfd, 0xff, 0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff,
        0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e, 0x5b, 0x4f, 0x09, 0x02,
        0x0d, 0xb0, 0x3e, 0xab, 0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1,
        0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee
    };
    struct tc_aes_key_sched_struct sched;
    uint8_t out[80];
    uint8_t decrypted[64];
    uint32_t result = TC_PASS;

    TC_PRINT("CTR test #1 (encryption SP 800-38a tests):\n");
    (void)tc_aes128_set_encrypt_key(&sched, key);

    (void)memcpy(out, ctr, sizeof(ctr));
    if (tc_ctr_mode(&out[TC_AES_BLOCK_SIZE], sizeof(plaintext),
            plaintext, sizeof(plaintext), ctr, &sched) == 0) {
        TC_ERROR("CTR test #1 (encryption SP 800-38a tests) failed in %s.\n", __func__);
        result = TC_FAIL;
        goto exitTest1;
    }

    result = check_result(1, ciphertext, sizeof(out), out, sizeof(out));
    TC_END_RESULT(result);

    TC_PRINT("CTR test #2 (decryption SP 800-38a tests):\n");
    (void) memcpy(ctr, out, sizeof(ctr));
    if (tc_ctr_mode(decrypted, sizeof(decrypted), &out[TC_AES_BLOCK_SIZE],
            sizeof(decrypted), ctr, &sched) == 0) {
        TC_ERROR("CTR test #2 (decryption SP 800-38a tests) failed in %s.\n", __func__);
        result = TC_FAIL;
        goto exitTest1;
    }

    result = check_result(2, plaintext, sizeof(plaintext),
            decrypted, sizeof(plaintext));

exitTest1:
    TC_END_RESULT(result);
    return result;
}

My input data is a double pointer (2D array) and I'd like to keep it that way:

uint8_t ** mydata = gen_rand_data(const size_t height, const size_t length);

Here's my understanding (correct me if I'm wrong):

  1. AES-128 is a block cipher, with 128-bit key.
  2. CTR mode makes the AES-128 a stream cipher and lets encrypt/decrypt data with arbitrary size.
  3. Having AES-128/CTR, the output (ciphertext) has the same length as plaintext.
  4. To improve the security, it's better to use 128-bit IV (nonce) for encryption.

Now here's my questions:

  1. Can I say ctr (initial counter, right?) is our IV?
  2. Why the ciphertext (expected value) and out (calculated value) are 128-bit bigger than plaintext?
  3. Why the code copies ctr value in the beginning of out?
  4. To encrypt mydata (2D array), I just need to loop over it and simple feed bytes from mydata to tc_ctr_mode, regardless of the dimension of mydata. Is this correct?

Thanks in advance.

1
Beware of CTR mode, it is absolutely necessary to never use the same key/counter combination twice.zaph
Why have you chosen to use counter mode?zaph
@zaph I'm working on a library, so the client has to be worry about it. Thanks for the note.Rad
@zaph, regarding why CTR, the options provided by TinyCrypt are "CBC", "CCM" and "CTR". The first two are block cipher and need some sort of padding. Do you have any comment?Rad
The usual mode is CBC with PKCS#7 padding. It seems the library does not support padding so you would have to add and remove the padding in your code, that is not difficult. Or choose a library that supports the standard encryption options such as padding. Note: CTR mode is very easy to get wrong, Microsoft has made that mistake in mainline applications a couple of times.zaph

1 Answers

0
votes
  1. The counter is generally supplied as the iv parameter if there is no specific counter parameter.

  2. The output is 16-bytes longer because the counter is prepended, the counter is block sized and AES has a 16-byte block size..

  3. The counter value is not secret and is needed for decryption. Since the same key/counter combination can never be reused if the key is going to be reused (a common situation) the counter must be unique and thus not pre-shared. The general answer is to pre-pend the counter value to the encrypted data.

  4. Yes providing the decryption end knows a priori the array dimensions. But is is probably better to use some form of marshaling to preserve the array shape.