diff -up ./automation/taskcluster/scripts/build_softoken.sh.cmac ./automation/taskcluster/scripts/build_softoken.sh --- ./automation/taskcluster/scripts/build_softoken.sh.cmac 2019-05-10 14:14:18.000000000 -0700 +++ ./automation/taskcluster/scripts/build_softoken.sh 2019-11-06 16:49:57.878508650 -0800 @@ -20,8 +20,9 @@ export NSS_BUILD_SOFTOKEN_ONLY=1 rm -rf dist make -C nss-softoken nss_build_all -mv dist/private/nss/blapi.h dist/public/nss -mv dist/private/nss/alghmac.h dist/public/nss +for i in blapi alghmac cmac; do + mv "dist/private/nss/${i}.h" dist/public/nss +done # Package. test -d artifacts || mkdir artifacts diff -up ./cmd/lib/pk11table.c.cmac ./cmd/lib/pk11table.c --- ./cmd/lib/pk11table.c.cmac 2019-05-10 14:14:18.000000000 -0700 +++ ./cmd/lib/pk11table.c 2019-11-06 16:49:57.879508660 -0800 @@ -333,6 +333,8 @@ const Constant _consts[] = { mkEntry(CKM_SHA512, Mechanism), mkEntry(CKM_SHA512_HMAC_GENERAL, Mechanism), mkEntry(CKM_SHA512_HMAC, Mechanism), + mkEntry(CKM_AES_CMAC, Mechanism), + mkEntry(CKM_AES_CMAC_GENERAL, Mechanism), mkEntry(CKM_CAST_KEY_GEN, Mechanism), mkEntry(CKM_CAST_ECB, Mechanism), mkEntry(CKM_CAST_CBC, Mechanism), diff -up ./cpputil/freebl_scoped_ptrs.h.cmac ./cpputil/freebl_scoped_ptrs.h --- ./cpputil/freebl_scoped_ptrs.h.cmac 2019-11-06 16:49:35.504266721 -0800 +++ ./cpputil/freebl_scoped_ptrs.h 2019-11-06 16:49:35.504266721 -0800 @@ -0,0 +1,33 @@ +/* -*- 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 +#include "blapi.h" + +struct ScopedDelete { + void operator()(CMACContext* ctx) { CMAC_Destroy(ctx, PR_TRUE); } +}; + +template +struct ScopedMaybeDelete { + void operator()(T* ptr) { + if (ptr) { + ScopedDelete del; + del(ptr); + } + } +}; + +#define SCOPED(x) typedef std::unique_ptr > Scoped##x + +SCOPED(CMACContext); + +#undef SCOPED + +#endif // freebl_scoped_ptrs_h__ diff -up ./gtests/freebl_gtest/cmac_unittests.cc.cmac ./gtests/freebl_gtest/cmac_unittests.cc --- ./gtests/freebl_gtest/cmac_unittests.cc.cmac 2019-11-06 16:49:35.504266721 -0800 +++ ./gtests/freebl_gtest/cmac_unittests.cc 2019-11-06 16:49:35.504266721 -0800 @@ -0,0 +1,187 @@ +// 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 +#include + +#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)); + } + } + } +} diff -up ./gtests/freebl_gtest/freebl_gtest.gyp.cmac ./gtests/freebl_gtest/freebl_gtest.gyp --- ./gtests/freebl_gtest/freebl_gtest.gyp.cmac 2019-05-10 14:14:18.000000000 -0700 +++ ./gtests/freebl_gtest/freebl_gtest.gyp 2019-11-06 16:49:35.617267943 -0800 @@ -34,6 +34,7 @@ 'ecl_unittest.cc', 'ghash_unittest.cc', 'rsa_unittest.cc', + 'cmac_unittests.cc', '<(DEPTH)/gtests/common/gtests.cc' ], 'dependencies': [ diff -up ./gtests/pk11_gtest/manifest.mn.cmac ./gtests/pk11_gtest/manifest.mn diff -up ./gtests/pk11_gtest/pk11_aes_cmac_unittest.cc.cmac ./gtests/pk11_gtest/pk11_aes_cmac_unittest.cc --- ./gtests/pk11_gtest/pk11_aes_cmac_unittest.cc.cmac 2019-11-06 16:49:57.879508660 -0800 +++ ./gtests/pk11_gtest/pk11_aes_cmac_unittest.cc 2019-11-06 16:49:57.879508660 -0800 @@ -0,0 +1,91 @@ +/* -*- 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/. */ + +#include +#include "nss.h" +#include "pk11pub.h" +#include "secerr.h" +#include "sechash.h" + +#include "blapi.h" + +#include "gtest/gtest.h" +#include "nss_scoped_ptrs.h" +#include "util.h" + +namespace nss_test { + +class Pkcs11AesCmacTest : public ::testing::Test { + protected: + ScopedPK11SymKey ImportKey(CK_MECHANISM_TYPE mech, SECItem *key_item) { + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + if (!slot) { + ADD_FAILURE() << "Can't get slot"; + return nullptr; + } + + ScopedPK11SymKey result(PK11_ImportSymKey( + slot.get(), mech, PK11_OriginUnwrap, CKA_SIGN, key_item, nullptr)); + + return result; + } + + void RunTest(uint8_t *key, unsigned int key_len, uint8_t *data, + unsigned int data_len, uint8_t *expected, + unsigned int expected_len, CK_ULONG mechanism) { + // Create SECItems for everything... + std::vector output(expected_len); + SECItem key_item = {siBuffer, key, key_len}; + SECItem output_item = {siBuffer, output.data(), expected_len}; + SECItem data_item = {siBuffer, data, data_len}; + SECItem expected_item = {siBuffer, expected, expected_len}; + + // Do the PKCS #11 stuff... + ScopedPK11SymKey p11_key = ImportKey(mechanism, &key_item); + ASSERT_NE(nullptr, p11_key.get()); + + SECStatus ret = PK11_SignWithSymKey(p11_key.get(), CKM_AES_CMAC, NULL, + &output_item, &data_item); + + // Verify the result... + ASSERT_EQ(SECSuccess, ret); + ASSERT_EQ(0, SECITEM_CompareItem(&output_item, &expected_item)); + } +}; + +// Sanity check of the PKCS #11 API only. Extensive tests for correctness of +// underling CMAC implementation conducted in the following file: +// gtests/freebl_gtest/cmac_unittests.cc + +TEST_F(Pkcs11AesCmacTest, Aes128NistExample1) { + uint8_t key[AES_128_KEY_LENGTH] = {0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, + 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, + 0x09, 0xCF, 0x4F, 0x3C}; + uint8_t known[AES_BLOCK_SIZE] = {0xBB, 0x1D, 0x69, 0x29, 0xE9, 0x59, + 0x37, 0x28, 0x7F, 0xA3, 0x7D, 0x12, + 0x9B, 0x75, 0x67, 0x46}; + + RunTest(key, AES_128_KEY_LENGTH, NULL, 0, known, AES_BLOCK_SIZE, + CKM_AES_CMAC); +} + +TEST_F(Pkcs11AesCmacTest, General) { + uint8_t key[AES_128_KEY_LENGTH] = {0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, + 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, + 0x09, 0xCF, 0x4F, 0x3C}; + uint8_t known[4] = {0xBB, 0x1D, 0x69, 0x29}; + + RunTest(key, AES_128_KEY_LENGTH, NULL, 0, known, 4, CKM_AES_CMAC_GENERAL); +} + +TEST_F(Pkcs11AesCmacTest, InvalidKeySize) { + uint8_t key[4] = {0x00, 0x00, 0x00, 0x00}; + SECItem key_item = {siBuffer, key, 4}; + + ScopedPK11SymKey result = ImportKey(CKM_AES_CMAC, &key_item); + ASSERT_EQ(nullptr, result.get()); +} +} diff -up ./gtests/pk11_gtest/pk11_gtest.gyp.cmac ./gtests/pk11_gtest/pk11_gtest.gyp --- ./gtests/pk11_gtest/pk11_gtest.gyp.cmac 2019-11-06 16:49:57.880508671 -0800 +++ ./gtests/pk11_gtest/pk11_gtest.gyp 2019-11-06 17:12:07.541834483 -0800 @@ -12,6 +12,7 @@ 'type': 'executable', 'sources': [ 'pk11_aeskeywrap_unittest.cc', + 'pk11_aes_cmac_unittest.cc', 'pk11_aes_gcm_unittest.cc', 'pk11_chacha20poly1305_unittest.cc', 'pk11_cipherop_unittest.cc', diff -up ./lib/freebl/blapi.h.cmac ./lib/freebl/blapi.h --- ./lib/freebl/blapi.h.cmac 2019-05-10 14:14:18.000000000 -0700 +++ ./lib/freebl/blapi.h 2019-11-06 16:49:35.618267953 -0800 @@ -10,6 +10,7 @@ #include "blapit.h" #include "hasht.h" +#include "cmac.h" #include "alghmac.h" SEC_BEGIN_PROTOS diff -up ./lib/freebl/cmac.c.cmac ./lib/freebl/cmac.c --- ./lib/freebl/cmac.c.cmac 2019-11-06 16:49:35.620267975 -0800 +++ ./lib/freebl/cmac.c 2019-11-06 16:49:35.620267975 -0800 @@ -0,0 +1,322 @@ +/* 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); + } +} diff -up ./lib/freebl/cmac.h.cmac ./lib/freebl/cmac.h --- ./lib/freebl/cmac.h.cmac 2019-11-06 16:49:35.621267986 -0800 +++ ./lib/freebl/cmac.h 2019-11-06 16:49:35.621267986 -0800 @@ -0,0 +1,47 @@ +/* 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 diff -up ./lib/freebl/exports.gyp.cmac ./lib/freebl/exports.gyp --- ./lib/freebl/exports.gyp.cmac 2019-05-10 14:14:18.000000000 -0700 +++ ./lib/freebl/exports.gyp 2019-11-06 16:49:35.621267986 -0800 @@ -27,6 +27,7 @@ }, { 'files': [ + 'cmac.h', 'alghmac.h', 'blapi.h', 'blake2b.h', diff -up ./lib/freebl/freebl_base.gypi.cmac ./lib/freebl/freebl_base.gypi --- ./lib/freebl/freebl_base.gypi.cmac 2019-05-10 14:14:18.000000000 -0700 +++ ./lib/freebl/freebl_base.gypi 2019-11-06 16:49:35.622267997 -0800 @@ -5,6 +5,7 @@ 'sources': [ 'aeskeywrap.c', 'alg2268.c', + 'cmac.c', 'alghmac.c', 'arcfive.c', 'arcfour.c', diff -up ./lib/freebl/ldvector.c.cmac ./lib/freebl/ldvector.c --- ./lib/freebl/ldvector.c.cmac 2019-05-10 14:14:18.000000000 -0700 +++ ./lib/freebl/ldvector.c 2019-11-06 16:49:35.623268007 -0800 @@ -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 vect /* 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* diff -up ./lib/freebl/loader.c.cmac ./lib/freebl/loader.c --- ./lib/freebl/loader.c.cmac 2019-05-10 14:14:18.000000000 -0700 +++ ./lib/freebl/loader.c 2019-11-06 16:49:35.624268018 -0800 @@ -2245,3 +2245,54 @@ BLAKE2B_Resurrect(unsigned char *space, } 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); +} diff -up ./lib/freebl/loader.h.cmac ./lib/freebl/loader.h --- ./lib/freebl/loader.h.cmac 2019-05-10 14:14:18.000000000 -0700 +++ ./lib/freebl/loader.h 2019-11-06 16:49:35.625268029 -0800 @@ -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, + unsigned int *result_len, + unsigned int max_result_len); + void (*p_CMAC_Destroy)(CMACContext *ctx, PRBool free_it); + + /* Version 3.022 came to here */ + /* Add new function pointers at the end of this struct and bump * FREEBL_VERSION at the beginning of this file. */ }; diff -up ./lib/freebl/manifest.mn.cmac ./lib/freebl/manifest.mn --- ./lib/freebl/manifest.mn.cmac 2019-05-10 14:14:18.000000000 -0700 +++ ./lib/freebl/manifest.mn 2019-11-06 16:49:35.626268040 -0800 @@ -85,6 +85,7 @@ EXPORTS = \ $(NULL) PRIVATE_EXPORTS = \ + cmac.h \ alghmac.h \ blake2b.h \ blapi.h \ @@ -119,6 +120,7 @@ CSRCS = \ md2.c \ md5.c \ sha512.c \ + cmac.c \ alghmac.c \ rawhash.c \ alg2268.c \ @@ -162,6 +164,7 @@ CSRCS = \ ALL_CSRCS := $(CSRCS) ALL_HDRS = \ + cmac.h \ alghmac.h \ blake2b.h \ blapi.h \ diff -up ./lib/pk11wrap/debug_module.c.cmac ./lib/pk11wrap/debug_module.c --- ./lib/pk11wrap/debug_module.c.cmac 2019-05-10 14:14:18.000000000 -0700 +++ ./lib/pk11wrap/debug_module.c 2019-11-06 16:49:57.881508682 -0800 @@ -376,6 +376,8 @@ print_mechanism(CK_MECHANISM_PTR m) CASE(CKM_AES_KEY_GEN); CASE(CKM_AES_MAC); CASE(CKM_AES_MAC_GENERAL); + CASE(CKM_AES_CMAC); + CASE(CKM_AES_CMAC_GENERAL); CASE(CKM_CAMELLIA_CBC); CASE(CKM_CAMELLIA_CBC_ENCRYPT_DATA); CASE(CKM_CAMELLIA_CBC_PAD); diff -up ./lib/pk11wrap/pk11mech.c.cmac ./lib/pk11wrap/pk11mech.c --- ./lib/pk11wrap/pk11mech.c.cmac 2019-11-06 16:49:20.284102148 -0800 +++ ./lib/pk11wrap/pk11mech.c 2019-11-06 16:49:57.882508693 -0800 @@ -236,6 +236,8 @@ PK11_GetKeyType(CK_MECHANISM_TYPE type, case CKM_AES_GCM: case CKM_AES_MAC: case CKM_AES_MAC_GENERAL: + case CKM_AES_CMAC: + case CKM_AES_CMAC_GENERAL: case CKM_AES_CBC_PAD: case CKM_AES_KEY_GEN: case CKM_NETSCAPE_AES_KEY_WRAP: @@ -453,6 +455,8 @@ PK11_GetKeyGenWithSize(CK_MECHANISM_TYPE case CKM_AES_GCM: case CKM_AES_MAC: case CKM_AES_MAC_GENERAL: + case CKM_AES_CMAC: + case CKM_AES_CMAC_GENERAL: case CKM_AES_CBC_PAD: case CKM_AES_KEY_GEN: return CKM_AES_KEY_GEN; diff -up ./lib/softoken/pkcs11c.c.cmac ./lib/softoken/pkcs11c.c --- ./lib/softoken/pkcs11c.c.cmac 2019-11-06 16:49:20.315102483 -0800 +++ ./lib/softoken/pkcs11c.c 2019-11-06 16:49:57.887508747 -0800 @@ -30,6 +30,7 @@ #include "lowpbe.h" /* We do PBE below */ #include "pkcs11t.h" #include "secoid.h" +#include "cmac.h" #include "alghmac.h" #include "softoken.h" #include "secasn1.h" @@ -1971,6 +1972,84 @@ sftk_doHMACInit(SFTKSessionContext *cont } /* + * common CMAC initialization routine + */ +static CK_RV +sftk_doCMACInit(SFTKSessionContext *session, CMACCipher type, + SFTKObject *key, CK_ULONG mac_size) +{ + SFTKAttribute *keyval; + CMACContext *cmacContext; + CK_ULONG *intpointer; + + /* Unlike HMAC, CMAC doesn't need to check key sizes as the underlying + * block cipher does this for us: block ciphers support only a single + * key size per variant. + * + * To introduce support for a CMAC based on a new block cipher, first add + * support for the relevant block cipher to CMAC in the freebl layer. Then + * update the switch statement at the end of this function. Also remember + * to update the switch statement in NSC_SignInit with the PKCS#11 + * mechanism constants. + */ + + keyval = sftk_FindAttribute(key, CKA_VALUE); + if (keyval == NULL) { + return CKR_KEY_SIZE_RANGE; + } + + /* Create the underlying CMACContext and associate it with the + * SFTKSessionContext's hashInfo field */ + cmacContext = CMAC_Create(type, + (const unsigned char *)keyval->attrib.pValue, + keyval->attrib.ulValueLen); + sftk_FreeAttribute(keyval); + + if (cmacContext == NULL) { + if (PORT_GetError() == SEC_ERROR_INVALID_ARGS) { + return CKR_KEY_SIZE_RANGE; + } + + return CKR_HOST_MEMORY; + } + session->hashInfo = cmacContext; + + /* MACs all behave roughly the same. However, CMAC can fail because + * the underlying cipher can fail. In practice, this shouldn't occur + * because we're not using any chaining modes, letting us safely ignore + * the return value. */ + session->multi = PR_TRUE; + session->hashUpdate = (SFTKHash)CMAC_Update; + session->end = (SFTKEnd)CMAC_Finish; + session->hashdestroy = (SFTKDestroy)CMAC_Destroy; + + intpointer = PORT_New(CK_ULONG); + if (intpointer == NULL) { + return CKR_HOST_MEMORY; + } + *intpointer = mac_size; + session->cipherInfo = intpointer; + + /* Since we're only "hashing", copy the result from session->end to the + * caller using sftk_SignCopy. */ + session->update = (SFTKCipher)sftk_SignCopy; + session->verify = (SFTKVerify)sftk_HMACCmp; + session->destroy = (SFTKDestroy)sftk_Space; + + /* Will need to be updated for additional block ciphers in the future. */ + switch (type) { + case CMAC_AES: + session->maxLen = AES_BLOCK_SIZE; + break; + default: + PORT_Assert(0); + return CKR_KEY_SIZE_RANGE; + } + + return CKR_OK; +} + +/* * SSL Macing support. SSL Macs are inited, then update with the base * hashing algorithm, then finalized in sign and verify */ @@ -2729,7 +2808,7 @@ NSC_SignInit(CK_SESSION_HANDLE hSession, case CKM_SHA_1_HMAC_GENERAL: PORT_Assert(pMechanism->pParameter); - if (!pMechanism->pParameter) { + if (!pMechanism->pParameter || pMechanism->ulParameterLen != sizeof(CK_MAC_GENERAL_PARAMS)) { crv = CKR_MECHANISM_PARAM_INVALID; break; } @@ -2739,7 +2818,17 @@ NSC_SignInit(CK_SESSION_HANDLE hSession, case CKM_SHA_1_HMAC: crv = sftk_doHMACInit(context, HASH_AlgSHA1, key, SHA1_LENGTH); break; - + case CKM_AES_CMAC_GENERAL: + PORT_Assert(pMechanism->pParameter); + if (!pMechanism->pParameter || pMechanism->ulParameterLen != sizeof(CK_MAC_GENERAL_PARAMS)) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + crv = sftk_doCMACInit(context, CMAC_AES, key, *(CK_ULONG *)pMechanism->pParameter); + break; + case CKM_AES_CMAC: + crv = sftk_doCMACInit(context, CMAC_AES, key, AES_BLOCK_SIZE); + break; case CKM_SSL3_MD5_MAC: PORT_Assert(pMechanism->pParameter); if (!pMechanism->pParameter) { diff -up ./lib/softoken/pkcs11.c.cmac ./lib/softoken/pkcs11.c --- ./lib/softoken/pkcs11.c.cmac 2019-11-06 16:49:57.884508714 -0800 +++ ./lib/softoken/pkcs11.c 2019-11-06 16:51:37.330584008 -0800 @@ -324,6 +324,8 @@ static const struct mechanismList mechan { CKM_AES_CBC, { 16, 32, CKF_EN_DE_WR_UN }, PR_TRUE }, { CKM_AES_MAC, { 16, 32, CKF_SN_VR }, PR_TRUE }, { CKM_AES_MAC_GENERAL, { 16, 32, CKF_SN_VR }, PR_TRUE }, + { CKM_AES_CMAC, { 16, 32, CKF_SN_VR }, PR_TRUE }, + { CKM_AES_CMAC_GENERAL, { 16, 32, CKF_SN_VR }, PR_TRUE }, { CKM_AES_CBC_PAD, { 16, 32, CKF_EN_DE_WR_UN }, PR_TRUE }, { CKM_AES_CTS, { 16, 32, CKF_EN_DE }, PR_TRUE }, { CKM_AES_CTR, { 16, 32, CKF_EN_DE }, PR_TRUE }, diff -up ./lib/util/pkcs11t.h.cmac ./lib/util/pkcs11t.h --- ./lib/util/pkcs11t.h.cmac 2019-11-06 16:49:20.289102202 -0800 +++ ./lib/util/pkcs11t.h 2019-11-06 16:49:57.887508747 -0800 @@ -882,6 +882,9 @@ typedef CK_ULONG CK_MECHANISM_TYPE; #define CKM_AES_GCM 0x00001087 #define CKM_AES_CCM 0x00001088 #define CKM_AES_CTS 0x00001089 +/* AES-CMAC values copied from v2.40 errata 1 header file */ +#define CKM_AES_CMAC_GENERAL 0x0000108A +#define CKM_AES_CMAC 0x0000108B #define CKM_AES_XCBC_MAC 0x0000108C #define CKM_AES_XCBC_MAC_96 0x0000108D