Source code

Revision control

Copy as Markdown

Other Tools

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifdef FREEBL_NO_DEPEND
#include "stubs.h"
#endif
#include "rijndael.h"
#include "blapi.h"
#include "cmac.h"
#include "secerr.h"
#include "nspr.h"
struct CMACContextStr {
/* Information about the block cipher to use internally. The cipher should
* be placed in ECB mode so that we can use it to directly encrypt blocks.
*
*
* To add a new cipher, add an entry to CMACCipher, update CMAC_Init,
* cmac_Encrypt, and CMAC_Destroy methods to handle the new cipher, and
* add a new Context pointer to the cipher union with the correct type. */
CMACCipher cipherType;
union {
AESContext *aes;
} cipher;
unsigned int blockSize;
/* Internal keys which are conditionally used by the algorithm. Derived
* from encrypting the NULL block. We leave the storing of (and the
* cleanup of) the CMAC key to the underlying block cipher. */
unsigned char k1[MAX_BLOCK_SIZE];
unsigned char k2[MAX_BLOCK_SIZE];
/* When Update is called with data which isn't a multiple of the block
* size, we need a place to put it. HMAC handles this by passing it to
* the underlying hash function right away; we can't do that as the
* contract on the cipher object is different. */
unsigned int partialIndex;
unsigned char partialBlock[MAX_BLOCK_SIZE];
/* Last encrypted block. This gets xor-ed with partialBlock prior to
* encrypting it. NIST defines this to be the empty string to begin. */
unsigned char lastBlock[MAX_BLOCK_SIZE];
};
static void
cmac_ShiftLeftOne(unsigned char *out, const unsigned char *in, int length)
{
int i = 0;
for (; i < length - 1; i++) {
out[i] = in[i] << 1;
out[i] |= in[i + 1] >> 7;
}
out[i] = in[i] << 1;
}
static SECStatus
cmac_Encrypt(CMACContext *ctx, unsigned char *output,
const unsigned char *input,
unsigned int inputLen)
{
if (ctx->cipherType == CMAC_AES) {
unsigned int tmpOutputLen;
SECStatus rv = AES_Encrypt(ctx->cipher.aes, output, &tmpOutputLen,
ctx->blockSize, input, inputLen);
/* Assumption: AES_Encrypt (when in ECB mode) always returns an
* output of length equal to blockSize (what was pass as the value
* of the maxOutputLen parameter). */
PORT_Assert(tmpOutputLen == ctx->blockSize);
return rv;
}
return SECFailure;
}
/* NIST SP.800-38B, 6.1 Subkey Generation */
static SECStatus
cmac_GenerateSubkeys(CMACContext *ctx)
{
unsigned char null_block[MAX_BLOCK_SIZE] = { 0 };
unsigned char L[MAX_BLOCK_SIZE];
unsigned char v;
unsigned char i;
/* Step 1: L = AES(key, null_block) */
if (cmac_Encrypt(ctx, L, null_block, ctx->blockSize) != SECSuccess) {
return SECFailure;
}
/* In the following, some effort has been made to be constant time. Rather
* than conditioning on the value of the MSB (of L or K1), we use the loop
* to build a mask for the conditional constant. */
/* Step 2: If MSB(L) = 0, K1 = L << 1. Else, K1 = (L << 1) ^ R_b. */
cmac_ShiftLeftOne(ctx->k1, L, ctx->blockSize);
v = L[0] >> 7;
for (i = 1; i <= 7; i <<= 1) {
v |= (v << i);
}
ctx->k1[ctx->blockSize - 1] ^= (0x87 & v);
/* Step 3: If MSB(K1) = 0, K2 = K1 << 1. Else, K2 = (K1 <, 1) ^ R_b. */
cmac_ShiftLeftOne(ctx->k2, ctx->k1, ctx->blockSize);
v = ctx->k1[0] >> 7;
for (i = 1; i <= 7; i <<= 1) {
v |= (v << i);
}
ctx->k2[ctx->blockSize - 1] ^= (0x87 & v);
/* Any intermediate value in the computation of the subkey shall be
* secret. */
PORT_Memset(null_block, 0, MAX_BLOCK_SIZE);
PORT_Memset(L, 0, MAX_BLOCK_SIZE);
/* Step 4: Return the values. */
return SECSuccess;
}
/* NIST SP.800-38B, 6.2 MAC Generation step 6 */
static SECStatus
cmac_UpdateState(CMACContext *ctx)
{
if (ctx == NULL || ctx->partialIndex != ctx->blockSize) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
/* Step 6: C_i = CIPHER(key, C_{i-1} ^ M_i) for 1 <= i <= n, and
* C_0 is defined as the empty string. */
for (unsigned int index = 0; index < ctx->blockSize; index++) {
ctx->partialBlock[index] ^= ctx->lastBlock[index];
}
return cmac_Encrypt(ctx, ctx->lastBlock, ctx->partialBlock, ctx->blockSize);
}
SECStatus
CMAC_Init(CMACContext *ctx, CMACCipher type,
const unsigned char *key, unsigned int key_len)
{
if (ctx == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
return SECFailure;
}
/* We only currently support AES-CMAC. */
if (type != CMAC_AES) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
PORT_Memset(ctx, 0, sizeof(*ctx));
ctx->blockSize = AES_BLOCK_SIZE;
ctx->cipherType = CMAC_AES;
ctx->cipher.aes = AES_CreateContext(key, NULL, NSS_AES, 1, key_len,
ctx->blockSize);
if (ctx->cipher.aes == NULL) {
return SECFailure;
}
return CMAC_Begin(ctx);
}
CMACContext *
CMAC_Create(CMACCipher type, const unsigned char *key,
unsigned int key_len)
{
CMACContext *result = PORT_New(CMACContext);
if (CMAC_Init(result, type, key, key_len) != SECSuccess) {
CMAC_Destroy(result, PR_TRUE);
return NULL;
}
return result;
}
SECStatus
CMAC_Begin(CMACContext *ctx)
{
if (ctx == NULL) {
return SECFailure;
}
/* Ensure that our blockSize is less than the maximum. When this fails,
* a cipher with a larger block size was added and MAX_BLOCK_SIZE needs
* to be updated accordingly. */
PORT_Assert(ctx->blockSize <= MAX_BLOCK_SIZE);
if (cmac_GenerateSubkeys(ctx) != SECSuccess) {
return SECFailure;
}
/* Set the index to write partial blocks at to zero. This saves us from
* having to clear ctx->partialBlock. */
ctx->partialIndex = 0;
/* Step 5: Let C_0 = 0^b. */
PORT_Memset(ctx->lastBlock, 0, ctx->blockSize);
return SECSuccess;
}
/* NIST SP.800-38B, 6.2 MAC Generation */
SECStatus
CMAC_Update(CMACContext *ctx, const unsigned char *data,
unsigned int data_len)
{
unsigned int data_index = 0;
if (ctx == NULL) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
if (data == NULL || data_len == 0) {
return SECSuccess;
}
/* Copy as many bytes from data into ctx->partialBlock as we can, up to
* the maximum of the remaining data and the remaining space in
* ctx->partialBlock.
*
* Note that we swap the order (encrypt *then* copy) because the last
* block is different from the rest. If we end on an even multiple of
* the block size, we have to be able to XOR it with K1. But we won't know
* that it is the last until CMAC_Finish is called (and by then, CMAC_Update
* has already returned). */
while (data_index < data_len) {
if (ctx->partialIndex == ctx->blockSize) {
if (cmac_UpdateState(ctx) != SECSuccess) {
return SECFailure;
}
ctx->partialIndex = 0;
}
unsigned int copy_len = data_len - data_index;
if (copy_len > (ctx->blockSize - ctx->partialIndex)) {
copy_len = ctx->blockSize - ctx->partialIndex;
}
PORT_Memcpy(ctx->partialBlock + ctx->partialIndex, data + data_index, copy_len);
data_index += copy_len;
ctx->partialIndex += copy_len;
}
return SECSuccess;
}
/* NIST SP.800-38B, 6.2 MAC Generation */
SECStatus
CMAC_Finish(CMACContext *ctx, unsigned char *result,
unsigned int *result_len,
unsigned int max_result_len)
{
if (ctx == NULL || result == NULL || max_result_len == 0) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
if (max_result_len > ctx->blockSize) {
/* This is a weird situation. The PKCS #11 soft tokencode passes
* sizeof(result) here, which is hard-coded as SFTK_MAX_MAC_LENGTH.
* This later gets truncated to min(SFTK_MAX_MAC_LENGTH, requested). */
max_result_len = ctx->blockSize;
}
/* Step 4: If M_n* is a complete block, M_n = K1 ^ M_n*. Else,
* M_n = K2 ^ (M_n* || 10^j). */
if (ctx->partialIndex == ctx->blockSize) {
/* XOR in K1. */
for (unsigned int index = 0; index < ctx->blockSize; index++) {
ctx->partialBlock[index] ^= ctx->k1[index];
}
} else {
/* Use 10* padding on the partial block. */
ctx->partialBlock[ctx->partialIndex++] = 0x80;
PORT_Memset(ctx->partialBlock + ctx->partialIndex, 0,
ctx->blockSize - ctx->partialIndex);
ctx->partialIndex = ctx->blockSize;
/* XOR in K2. */
for (unsigned int index = 0; index < ctx->blockSize; index++) {
ctx->partialBlock[index] ^= ctx->k2[index];
}
}
/* Encrypt the block. */
if (cmac_UpdateState(ctx) != SECSuccess) {
return SECFailure;
}
/* Step 7 & 8: T = MSB_tlen(C_n); return T. */
PORT_Memcpy(result, ctx->lastBlock, max_result_len);
if (result_len != NULL) {
*result_len = max_result_len;
}
return SECSuccess;
}
void
CMAC_Destroy(CMACContext *ctx, PRBool free_it)
{
if (ctx == NULL) {
return;
}
if (ctx->cipherType == CMAC_AES && ctx->cipher.aes != NULL) {
AES_DestroyContext(ctx->cipher.aes, PR_TRUE);
}
/* Destroy everything in the context. This includes sensitive data in
* K1, K2, and lastBlock. */
PORT_Memset(ctx, 0, sizeof(*ctx));
if (free_it == PR_TRUE) {
PORT_Free(ctx);
}
}