packages feed

KpqC-0.1.0: cbits/test/aes.c

/* Test-only AES-256 implementation for the CTR-DRBG known-answer-test hook. */
#include "aes.h"

#include <string.h>

#define AES256_ROUNDS 14

static uint8_t xtime(uint8_t value) {
    return (uint8_t)((uint8_t)(value << 1) ^
                     (uint8_t)(((uint8_t)(value >> 7)) * 0x1bu));
}

static uint8_t gf_multiply(uint8_t left, uint8_t right) {
    uint8_t product = 0;
    for (unsigned int bit = 0; bit < 8; ++bit) {
        if ((right & 1u) != 0) product ^= left;
        left = xtime(left);
        right >>= 1;
    }
    return product;
}

static uint8_t gf_inverse(uint8_t value) {
    uint8_t result = 1;
    uint8_t power = value;
    unsigned int exponent = 254;

    if (value == 0) return 0;
    while (exponent != 0) {
        if ((exponent & 1u) != 0) result = gf_multiply(result, power);
        power = gf_multiply(power, power);
        exponent >>= 1;
    }
    return result;
}

static uint8_t rotate_left(uint8_t value, unsigned int count) {
    return (uint8_t)((uint8_t)(value << count) |
                     (uint8_t)(value >> (8u - count)));
}

static uint8_t substitute(uint8_t value) {
    uint8_t inverse = gf_inverse(value);
    return (uint8_t)(inverse ^ rotate_left(inverse, 1) ^
                     rotate_left(inverse, 2) ^ rotate_left(inverse, 3) ^
                     rotate_left(inverse, 4) ^ 0x63u);
}

static void add_round_key(uint8_t state[AES_BLOCKBYTES],
                          const uint8_t *round_key) {
    for (size_t index = 0; index < AES_BLOCKBYTES; ++index) {
        state[index] ^= round_key[index];
    }
}

static void substitute_bytes(uint8_t state[AES_BLOCKBYTES]) {
    for (size_t index = 0; index < AES_BLOCKBYTES; ++index) {
        state[index] = substitute(state[index]);
    }
}

static void shift_rows(uint8_t state[AES_BLOCKBYTES]) {
    uint8_t shifted[AES_BLOCKBYTES];

    shifted[0] = state[0];
    shifted[1] = state[5];
    shifted[2] = state[10];
    shifted[3] = state[15];
    shifted[4] = state[4];
    shifted[5] = state[9];
    shifted[6] = state[14];
    shifted[7] = state[3];
    shifted[8] = state[8];
    shifted[9] = state[13];
    shifted[10] = state[2];
    shifted[11] = state[7];
    shifted[12] = state[12];
    shifted[13] = state[1];
    shifted[14] = state[6];
    shifted[15] = state[11];

    memcpy(state, shifted, sizeof(shifted));
}

static void mix_columns(uint8_t state[AES_BLOCKBYTES]) {
    for (size_t column = 0; column < AES_BLOCKBYTES; column += 4) {
        uint8_t first = state[column];
        uint8_t combined = (uint8_t)(state[column] ^ state[column + 1] ^
                                     state[column + 2] ^ state[column + 3]);

        state[column] ^= (uint8_t)(combined ^
                                   xtime((uint8_t)(state[column] ^
                                                   state[column + 1])));
        state[column + 1] ^=
            (uint8_t)(combined ^ xtime((uint8_t)(state[column + 1] ^
                                                  state[column + 2])));
        state[column + 2] ^=
            (uint8_t)(combined ^ xtime((uint8_t)(state[column + 2] ^
                                                  state[column + 3])));
        state[column + 3] ^=
            (uint8_t)(combined ^ xtime((uint8_t)(state[column + 3] ^ first)));
    }
}

static void aes_secure_zero(void *value, size_t length) {
    volatile uint8_t *bytes = (volatile uint8_t *)value;
    while (length-- != 0) *bytes++ = 0;
}

void aes256_ecb_keyexp(aes256ctx *ctx, const uint8_t key[AES256_KEYBYTES]) {
    uint8_t temporary[4];
    size_t generated = AES256_KEYBYTES;
    uint8_t round_constant = 1;

    memcpy(ctx->round_keys, key, AES256_KEYBYTES);
    while (generated < AES256_ROUND_KEYBYTES) {
        memcpy(temporary, ctx->round_keys + generated - sizeof(temporary),
               sizeof(temporary));

        if (generated % AES256_KEYBYTES == 0) {
            uint8_t first = temporary[0];
            temporary[0] = substitute(temporary[1]);
            temporary[1] = substitute(temporary[2]);
            temporary[2] = substitute(temporary[3]);
            temporary[3] = substitute(first);
            temporary[0] ^= round_constant;
            round_constant = xtime(round_constant);
        } else if (generated % AES256_KEYBYTES == 16) {
            for (size_t index = 0; index < sizeof(temporary); ++index) {
                temporary[index] = substitute(temporary[index]);
            }
        }

        for (size_t index = 0;
             index < sizeof(temporary) && generated < AES256_ROUND_KEYBYTES;
             ++index, ++generated) {
            ctx->round_keys[generated] =
                (uint8_t)(ctx->round_keys[generated - AES256_KEYBYTES] ^
                          temporary[index]);
        }
    }
    aes_secure_zero(temporary, sizeof(temporary));
}

static void encrypt_block(uint8_t output[AES_BLOCKBYTES],
                          const uint8_t input[AES_BLOCKBYTES],
                          const aes256ctx *ctx) {
    uint8_t state[AES_BLOCKBYTES];

    memcpy(state, input, sizeof(state));
    add_round_key(state, ctx->round_keys);
    for (size_t round = 1; round < AES256_ROUNDS; ++round) {
        substitute_bytes(state);
        shift_rows(state);
        mix_columns(state);
        add_round_key(state, ctx->round_keys + round * AES_BLOCKBYTES);
    }
    substitute_bytes(state);
    shift_rows(state);
    add_round_key(state,
                  ctx->round_keys + AES256_ROUNDS * AES_BLOCKBYTES);
    memcpy(output, state, sizeof(state));
    aes_secure_zero(state, sizeof(state));
}

void aes256_ecb(uint8_t *output, const uint8_t *input, size_t blocks,
                const aes256ctx *ctx) {
    for (size_t block = 0; block < blocks; ++block) {
        encrypt_block(output + block * AES_BLOCKBYTES,
                      input + block * AES_BLOCKBYTES, ctx);
    }
}

void aes256_ctx_release(aes256ctx *ctx) {
    aes_secure_zero(ctx, sizeof(*ctx));
}