/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* lib/crypto/krb/cmac.c */ /* * Copyright 2010 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. */ #include "crypto_int.h" #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; } } /* Generate subkeys K1 and K2 as described in RFC 4493 figure 2.2. */ 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; /* L := encrypt(K, const_Zero) */ memset(L, 0, sizeof(L)); d = make_data(L, BLOCK_SIZE); ret = encrypt_block(enc, key, &d); if (ret != 0) return ret; /* K1 := (MSB(L) == 0) ? L << 1 : (L << 1) XOR const_Rb */ if ((L[0] & 0x80) == 0) { leftshift_onebit(L, K1); } else { leftshift_onebit(L, tmp); xor_128(tmp, const_Rb, K1); } /* K2 := (MSB(K1) == 0) ? K1 << 1 : (K1 << 1) XOR const_Rb */ if ((K1[0] & 0x80) == 0) { leftshift_onebit(K1, K2); } else { leftshift_onebit(K1, tmp); xor_128(tmp, const_Rb, K2); } return 0; } /* Pad out lastb with a 1 bit followed by 0 bits, placing the result in pad. */ static void padding(unsigned char *lastb, unsigned char *pad, int length) { int j; /* original last block */ 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; } } } /* * Implementation of CMAC algorithm. When used with AES, this function * is compatible with RFC 4493 figure 2.3. */ 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); /* Step 1. */ ret = generate_subkey(enc, key, K1, K2); if (ret != 0) return ret; /* Step 2. */ n = (length + BLOCK_SIZE - 1) / BLOCK_SIZE; /* Step 3. */ 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); /* Step 5 (we'll do step 4 in a bit). */ memset(Y, 0, BLOCK_SIZE); d = make_data(Y, BLOCK_SIZE); /* Step 6 (all but last block). */ 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; } /* Step 4. */ k5_iov_cursor_get(&cursor, input); if (flag) { /* last block is complete block */ xor_128(input, K1, M_last); } else { padding(input, padded, length % BLOCK_SIZE); xor_128(padded, K2, M_last); } /* Step 6 (last block). */ 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; }