Commit 879dde48 authored by Alexander Scheel's avatar Alexander Scheel

Bug 1570501 - Add AES-CMAC implementation to freebl, r=mt

Differential Revision: https://phabricator.services.mozilla.com/D40120

--HG--
extra : rebase_source : b3c131cfdc4beeb207be39117c37a5f717cb0d8d
parent 138e05d1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef freebl_scoped_ptrs_h__
#define freebl_scoped_ptrs_h__
#include <memory>
#include "blapi.h"
struct ScopedDelete {
void operator()(CMACContext* ctx) { CMAC_Destroy(ctx, PR_TRUE); }
};
template <class T>
struct ScopedMaybeDelete {
void operator()(T* ptr) {
if (ptr) {
ScopedDelete del;
del(ptr);
}
}
};
#define SCOPED(x) typedef std::unique_ptr<x, ScopedMaybeDelete<x> > Scoped##x
SCOPED(CMACContext);
#undef SCOPED
#endif // freebl_scoped_ptrs_h__
// 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/.
#include "gtest/gtest.h"
#include <stdint.h>
#include <memory>
#include "blapi.h"
#include "secitem.h"
#include "freebl_scoped_ptrs.h"
class CmacAesTest : public ::testing::Test {
protected:
bool Compare(const uint8_t *actual, const uint8_t *expected,
unsigned int length) {
return strncmp((const char *)actual, (const char *)expected, length) == 0;
}
};
TEST_F(CmacAesTest, CreateInvalidSize) {
uint8_t key[1] = {0x00};
ScopedCMACContext ctx(CMAC_Create(CMAC_AES, key, sizeof(key)));
ASSERT_EQ(ctx, nullptr);
}
TEST_F(CmacAesTest, CreateRightSize) {
uint8_t *key = PORT_NewArray(uint8_t, AES_128_KEY_LENGTH);
ScopedCMACContext ctx(CMAC_Create(CMAC_AES, key, AES_128_KEY_LENGTH));
ASSERT_NE(ctx, nullptr);
PORT_Free(key);
}
// The following tests were taken from NIST's Cryptographic Standards and
// Guidelines page for AES-CMAC Examples with Intermediate Values. These same
// test vectors for AES-128 can be found in RFC 4493, Section 4.
static const uint8_t kNistKeys[][AES_256_KEY_LENGTH] = {
{0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15,
0x88, 0x09, 0xCF, 0x4F, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
{0x8E, 0x73, 0xB0, 0xF7, 0xDA, 0x0E, 0x64, 0x52, 0xC8, 0x10, 0xF3,
0x2B, 0x80, 0x90, 0x79, 0xE5, 0x62, 0xF8, 0xEA, 0xD2, 0x52, 0x2C,
0x6B, 0x7B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
{0x60, 0x3D, 0xEB, 0x10, 0x15, 0xCA, 0x71, 0xBE, 0x2B, 0x73, 0xAE,
0xF0, 0x85, 0x7D, 0x77, 0x81, 0x1F, 0x35, 0x2C, 0x07, 0x3B, 0x61,
0x08, 0xD7, 0x2D, 0x98, 0x10, 0xA3, 0x09, 0x14, 0xDF, 0xF4}};
static const size_t kNistKeyLengthsCount = PR_ARRAY_SIZE(kNistKeys);
static const unsigned int kNistKeyLengths[kNistKeyLengthsCount] = {
AES_128_KEY_LENGTH, AES_192_KEY_LENGTH, AES_256_KEY_LENGTH};
static const uint8_t kNistPlaintext[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};
static const unsigned int kNistPlaintextLengths[] = {0, 16, 20, 64};
static const size_t kNistPlaintextLengthsCount =
PR_ARRAY_SIZE(kNistPlaintextLengths);
// This table contains the result of a CMAC over kNistPlaintext using keys from
// kNistKeys. For each key, there are kNistPlaintextLengthsCount answers, all
// listed one after the other as the input is truncated to the different sizes
// in kNistPlaintextLengths.
static const uint8_t kNistKnown[][AES_BLOCK_SIZE] = {
{0xBB, 0x1D, 0x69, 0x29, 0xE9, 0x59, 0x37, 0x28, 0x7F, 0xA3, 0x7D, 0x12,
0x9B, 0x75, 0x67, 0x46},
{0x07, 0x0A, 0x16, 0xB4, 0x6B, 0x4D, 0x41, 0x44, 0xF7, 0x9B, 0xDD, 0x9D,
0xD0, 0x4A, 0x28, 0x7C},
{0x7D, 0x85, 0x44, 0x9E, 0xA6, 0xEA, 0x19, 0xC8, 0x23, 0xA7, 0xBF, 0x78,
0x83, 0x7D, 0xFA, 0xDE},
{0x51, 0xF0, 0xBE, 0xBF, 0x7E, 0x3B, 0x9D, 0x92, 0xFC, 0x49, 0x74, 0x17,
0x79, 0x36, 0x3C, 0xFE},
{0xD1, 0x7D, 0xDF, 0x46, 0xAD, 0xAA, 0xCD, 0xE5, 0x31, 0xCA, 0xC4, 0x83,
0xDE, 0x7A, 0x93, 0x67},
{0x9E, 0x99, 0xA7, 0xBF, 0x31, 0xE7, 0x10, 0x90, 0x06, 0x62, 0xF6, 0x5E,
0x61, 0x7C, 0x51, 0x84},
{0x3D, 0x75, 0xC1, 0x94, 0xED, 0x96, 0x07, 0x04, 0x44, 0xA9, 0xFA, 0x7E,
0xC7, 0x40, 0xEC, 0xF8},
{0xA1, 0xD5, 0xDF, 0x0E, 0xED, 0x79, 0x0F, 0x79, 0x4D, 0x77, 0x58, 0x96,
0x59, 0xF3, 0x9A, 0x11},
{0x02, 0x89, 0x62, 0xF6, 0x1B, 0x7B, 0xF8, 0x9E, 0xFC, 0x6B, 0x55, 0x1F,
0x46, 0x67, 0xD9, 0x83},
{0x28, 0xA7, 0x02, 0x3F, 0x45, 0x2E, 0x8F, 0x82, 0xBD, 0x4B, 0xF2, 0x8D,
0x8C, 0x37, 0xC3, 0x5C},
{0x15, 0x67, 0x27, 0xDC, 0x08, 0x78, 0x94, 0x4A, 0x02, 0x3C, 0x1F, 0xE0,
0x3B, 0xAD, 0x6D, 0x93},
{0xE1, 0x99, 0x21, 0x90, 0x54, 0x9F, 0x6E, 0xD5, 0x69, 0x6A, 0x2C, 0x05,
0x6C, 0x31, 0x54, 0x10}};
PR_STATIC_ASSERT(PR_ARRAY_SIZE(kNistKnown) ==
kNistKeyLengthsCount * kNistPlaintextLengthsCount);
TEST_F(CmacAesTest, AesNistAligned) {
for (unsigned int key_index = 0; key_index < kNistKeyLengthsCount;
key_index++) {
ScopedCMACContext ctx(CMAC_Create(CMAC_AES, kNistKeys[key_index],
kNistKeyLengths[key_index]));
ASSERT_NE(ctx, nullptr);
for (unsigned int plaintext_index = 0;
plaintext_index < kNistPlaintextLengthsCount; plaintext_index++) {
CMAC_Begin(ctx.get());
unsigned int known_index =
(key_index * kNistPlaintextLengthsCount) + plaintext_index;
CMAC_Update(ctx.get(), kNistPlaintext,
kNistPlaintextLengths[plaintext_index]);
uint8_t output[AES_BLOCK_SIZE];
CMAC_Finish(ctx.get(), output, NULL, AES_BLOCK_SIZE);
ASSERT_TRUE(Compare(output, kNistKnown[known_index], AES_BLOCK_SIZE));
}
}
}
TEST_F(CmacAesTest, AesNistUnaligned) {
for (unsigned int key_index = 0; key_index < kNistKeyLengthsCount;
key_index++) {
unsigned int key_length = kNistKeyLengths[key_index];
ScopedCMACContext ctx(
CMAC_Create(CMAC_AES, kNistKeys[key_index], key_length));
ASSERT_NE(ctx, nullptr);
// Skip the zero-length test.
for (unsigned int plaintext_index = 1;
plaintext_index < kNistPlaintextLengthsCount; plaintext_index++) {
unsigned int known_index =
(key_index * kNistPlaintextLengthsCount) + plaintext_index;
unsigned int plaintext_length = kNistPlaintextLengths[plaintext_index];
// Test all possible offsets and make sure that misaligned updates
// produce the desired result. That is, do two updates:
// 0 ... offset
// offset ... len - offset
// and ensure the result is the same as doing one update.
for (unsigned int offset = 1; offset < plaintext_length; offset++) {
CMAC_Begin(ctx.get());
CMAC_Update(ctx.get(), kNistPlaintext, offset);
CMAC_Update(ctx.get(), kNistPlaintext + offset,
plaintext_length - offset);
uint8_t output[AES_BLOCK_SIZE];
CMAC_Finish(ctx.get(), output, NULL, AES_BLOCK_SIZE);
ASSERT_TRUE(Compare(output, kNistKnown[known_index], AES_BLOCK_SIZE));
}
}
}
}
TEST_F(CmacAesTest, AesNistTruncated) {
for (unsigned int key_index = 0; key_index < kNistKeyLengthsCount;
key_index++) {
unsigned int key_length = kNistKeyLengths[key_index];
ScopedCMACContext ctx(
CMAC_Create(CMAC_AES, kNistKeys[key_index], key_length));
ASSERT_TRUE(ctx != nullptr);
// Skip the zero-length test.
for (unsigned int plaintext_index = 1;
plaintext_index < kNistPlaintextLengthsCount; plaintext_index++) {
unsigned int known_index =
(key_index * kNistPlaintextLengthsCount) + plaintext_index;
unsigned int plaintext_length = kNistPlaintextLengths[plaintext_index];
// Test truncated outputs to ensure that we always get the desired values.
for (unsigned int out_len = 1; out_len < AES_BLOCK_SIZE; out_len++) {
CMAC_Begin(ctx.get());
CMAC_Update(ctx.get(), kNistPlaintext, plaintext_length);
unsigned int actual_out_len = 0;
uint8_t output[AES_BLOCK_SIZE];
CMAC_Finish(ctx.get(), output, &actual_out_len, out_len);
ASSERT_TRUE(actual_out_len == out_len);
ASSERT_TRUE(Compare(output, kNistKnown[known_index], out_len));
}
}
}
}
......@@ -35,6 +35,7 @@
'ecl_unittest.cc',
'ghash_unittest.cc',
'rsa_unittest.cc',
'cmac_unittests.cc',
'<(DEPTH)/gtests/common/gtests.cc'
],
'dependencies': [
......
......@@ -10,6 +10,7 @@
#include "blapit.h"
#include "hasht.h"
#include "cmac.h"
#include "alghmac.h"
SEC_BEGIN_PROTOS
......
/* 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;
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;
if (AES_InitContext(&ctx->cipher.aes, key, key_len, NULL, NSS_AES, 1,
ctx->blockSize) != SECSuccess) {
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)
{
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) {
AES_DestroyContext(&ctx->cipher.aes, PR_FALSE);
}
/* 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);
}
}
/* 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/. */
#ifndef _CMAC_H_
#define _CMAC_H_
typedef struct CMACContextStr CMACContext;
SEC_BEGIN_PROTOS
/* Enum for identifying the underlying block cipher we're using internally. */
typedef enum {
CMAC_AES = 0
} CMACCipher;
/* Initialize an existing CMACContext struct. */
SECStatus CMAC_Init(CMACContext *ctx, CMACCipher type,
const unsigned char *key, unsigned int key_len);
/* Allocate and initialize a new CMAC context with the specified cipher and
* key. */
CMACContext *CMAC_Create(CMACCipher type, const unsigned char *key,
unsigned int key_len);
/* Called automatically by CMAC_*{Create,Init}(...). Only useful for restarting
* an already-started CMAC instance. */
SECStatus CMAC_Begin(CMACContext *ctx);
/* Add the specified bytes into the CMAC state. */
SECStatus CMAC_Update(CMACContext *ctx, const unsigned char *data,
unsigned int data_len);
/* Finalize the CMAC state and return the result. */
SECStatus CMAC_Finish(CMACContext *ctx, unsigned char *result,
unsigned int *result_len,
unsigned int max_result_len);
/* Note: CMAC_Clone isn't implemented here because AES doesn't expose a
* context-cloning operation. */
/* Destroy a CMAC context, optionally freeing it. */
void CMAC_Destroy(CMACContext *ctx, PRBool free_it);
SEC_END_PROTOS
#endif
......@@ -27,6 +27,7 @@
},
{
'files': [
'cmac.h',
'alghmac.h',
'blapi.h',
'blake2b.h',
......
......@@ -5,6 +5,7 @@
'sources': [
'aeskeywrap.c',
'alg2268.c',
'cmac.c',
'alghmac.c',
'arcfive.c',
'arcfour.c',
......
......@@ -10,6 +10,7 @@ extern int FREEBL_InitStubs(void);
#endif
#include "loader.h"
#include "cmac.h"
#include "alghmac.h"
#include "hmacct.h"
#include "blapii.h"
......@@ -317,10 +318,18 @@ static const struct FREEBLVectorStr vector =
/* End of Version 3.020 */
ChaCha20_Xor
ChaCha20_Xor,
/* End of version 3.021 */
CMAC_Init,
CMAC_Create,
CMAC_Begin,
CMAC_Update,
CMAC_Finish,
CMAC_Destroy
/* End of version 3.022 */
};
const FREEBLVector*
......
......@@ -2245,3 +2245,54 @@ BLAKE2B_Resurrect(unsigned char *space, void *arg)
}
return (vector->p_BLAKE2B_Resurrect)(space, arg);
}
/* == New for CMAC == */
SECStatus
CMAC_Init(CMACContext *ctx, CMACCipher type, const unsigned char *key,
unsigned int key_len)
{
if (!vector && PR_SUCCESS != freebl_RunLoaderOnce())
return SECFailure;
return (vector->p_CMAC_Init)(ctx, type, key, key_len);
}
CMACContext *
CMAC_Create(CMACCipher type, const unsigned char *key, unsigned int key_len)
{
if (!vector && PR_SUCCESS != freebl_RunLoaderOnce())
return NULL;
return (vector->p_CMAC_Create)(type, key, key_len);
}
SECStatus
CMAC_Begin(CMACContext *ctx)
{
if (!vector && PR_SUCCESS != freebl_RunLoaderOnce())
return SECFailure;
return (vector->p_CMAC_Begin)(ctx);
}
SECStatus
CMAC_Update(CMACContext *ctx, const unsigned char *data, unsigned int data_len)
{
if (!vector && PR_SUCCESS != freebl_RunLoaderOnce())
return SECFailure;
return (vector->p_CMAC_Update)(ctx, data, data_len);
}
SECStatus
CMAC_Finish(CMACContext *ctx, unsigned char *result, unsigned int *result_len,
unsigned int max_result_len)
{
if (!vector && PR_SUCCESS != freebl_RunLoaderOnce())
return SECFailure;
return (vector->p_CMAC_Finish)(ctx, result, result_len, max_result_len);
}
void
CMAC_Destroy(CMACContext *ctx, PRBool free_it)
{
if (!vector && PR_SUCCESS != freebl_RunLoaderOnce())
return;
(vector->p_CMAC_Destroy)(ctx, free_it);
}
......@@ -10,7 +10,7 @@
#include "blapi.h"
#define FREEBL_VERSION 0x0315
#define FREEBL_VERSION 0x0316
struct FREEBLVectorStr {
......@@ -765,6 +765,20 @@ struct FREEBLVectorStr {
/* Version 3.021 came to here */
SECStatus (*p_CMAC_Init)(CMACContext *ctx, CMACCipher type,
const unsigned char *key, unsigned int key_len);
CMACContext *(*p_CMAC_Create)(CMACCipher type, const unsigned char *key,
unsigned int key_len);
SECStatus (*p_CMAC_Begin)(CMACContext *ctx);
SECStatus (*p_CMAC_Update)(CMACContext *ctx, const unsigned char *data,
unsigned int data_len);
SECStatus (*p_CMAC_Finish)(CMACContext *ctx, unsigned char *result,