#include "crypto_int.h"
#ifdef K5_BUILTIN_CMAC
#define BLOCK_SIZE 16
static unsigned char const_Rb[BLOCK_SIZE] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87
};
static void
xor_128(unsigned char *a, unsigned char *b, unsigned char *out)
{
int z;
for (z = 0; z < BLOCK_SIZE / 4; z++) {
unsigned char *aptr = &a[z * 4];
unsigned char *bptr = &b[z * 4];
unsigned char *outptr = &out[z * 4];
store_32_n(load_32_n(aptr) ^ load_32_n(bptr), outptr);
}
}
static void
leftshift_onebit(unsigned char *input, unsigned char *output)
{
int i;
unsigned char overflow = 0;
for (i = BLOCK_SIZE - 1; i >= 0; i--) {
output[i] = input[i] << 1;
output[i] |= overflow;
overflow = (input[i] & 0x80) ? 1 : 0;
}
}
static krb5_error_code
generate_subkey(const struct krb5_enc_provider *enc,
krb5_key key,
unsigned char *K1,
unsigned char *K2)
{
unsigned char L[BLOCK_SIZE];
unsigned char tmp[BLOCK_SIZE];
krb5_data d;
krb5_error_code ret;
memset(L, 0, sizeof(L));
d = make_data(L, BLOCK_SIZE);
ret = encrypt_block(enc, key, &d);
if (ret != 0)
return ret;
if ((L[0] & 0x80) == 0) {
leftshift_onebit(L, K1);
} else {
leftshift_onebit(L, tmp);
xor_128(tmp, const_Rb, K1);
}
if ((K1[0] & 0x80) == 0) {
leftshift_onebit(K1, K2);
} else {
leftshift_onebit(K1, tmp);
xor_128(tmp, const_Rb, K2);
}
return 0;
}
static void
padding(unsigned char *lastb, unsigned char *pad, int length)
{
int j;
for (j = 0; j < BLOCK_SIZE; j++) {
if (j < length) {
pad[j] = lastb[j];
} else if (j == length) {
pad[j] = 0x80;
} else {
pad[j] = 0x00;
}
}
}
krb5_error_code
krb5int_cmac_checksum(const struct krb5_enc_provider *enc, krb5_key key,
const krb5_crypto_iov *data, size_t num_data,
krb5_data *output)
{
unsigned char Y[BLOCK_SIZE], M_last[BLOCK_SIZE], padded[BLOCK_SIZE];
unsigned char K1[BLOCK_SIZE], K2[BLOCK_SIZE];
unsigned char input[BLOCK_SIZE];
unsigned int n, i, flag;
krb5_error_code ret;
struct iov_cursor cursor;
size_t length;
krb5_crypto_iov iov[1];
krb5_data d;
assert(enc->cbc_mac != NULL);
if (enc->block_size != BLOCK_SIZE)
return KRB5_BAD_MSIZE;
length = iov_total_length(data, num_data, TRUE);
ret = generate_subkey(enc, key, K1, K2);
if (ret != 0)
return ret;
n = (length + BLOCK_SIZE - 1) / BLOCK_SIZE;
if (n == 0) {
n = 1;
flag = 0;
} else {
flag = ((length % BLOCK_SIZE) == 0);
}
iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
iov[0].data = make_data(input, BLOCK_SIZE);
memset(Y, 0, BLOCK_SIZE);
d = make_data(Y, BLOCK_SIZE);
k5_iov_cursor_init(&cursor, data, num_data, BLOCK_SIZE, TRUE);
for (i = 0; i < n - 1; i++) {
k5_iov_cursor_get(&cursor, input);
ret = enc->cbc_mac(key, iov, 1, &d, &d);
if (ret != 0)
return ret;
}
k5_iov_cursor_get(&cursor, input);
if (flag) {
xor_128(input, K1, M_last);
} else {
padding(input, padded, length % BLOCK_SIZE);
xor_128(padded, K2, M_last);
}
iov[0].data = make_data(M_last, BLOCK_SIZE);
ret = enc->cbc_mac(key, iov, 1, &d, &d);
if (ret != 0)
return ret;
assert(output->length >= d.length);
output->length = d.length;
memcpy(output->data, d.data, d.length);
return 0;
}
#endif