Ver código fonte

Support 3DES-CMAC.

cryptography.io depends on this. Specifically, it assumes that any time
a CBC-mode cipher is defined, CMAC is also defined. This is incorrect;
CMAC also requires an irreducible polynomial to represent GF(2^b).
However, one is indeed defined for 64-bit block ciphers such as 3DES.

Import tests from CAVP to test it. I've omitted the 65536-byte inputs
because they're huge and FileTest doesn't like lines that long.

Change-Id: I35b1e4975f61c757c70616f9b372b91746fc7e4a
Reviewed-on: https://boringssl-review.googlesource.com/28466
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
kris/onging/CECPQ3_patch15
David Benjamin 6 anos atrás
pai
commit
9db1a0017a
7 arquivos alterados com 9381 adições e 25 exclusões
  1. +2207
    -0
      crypto/cmac/cavp_3des_cmac_tests.txt
  2. +2165
    -0
      crypto/cmac/cavp_aes128_cmac_tests.txt
  3. +2707
    -0
      crypto/cmac/cavp_aes192_cmac_tests.txt
  4. +2165
    -0
      crypto/cmac/cavp_aes256_cmac_tests.txt
  5. +51
    -25
      crypto/cmac/cmac.c
  6. +82
    -0
      crypto/cmac/cmac_test.cc
  7. +4
    -0
      sources.cmake

+ 2207
- 0
crypto/cmac/cavp_3des_cmac_tests.txt
Diferenças do arquivo suprimidas por serem muito extensas
Ver arquivo


+ 2165
- 0
crypto/cmac/cavp_aes128_cmac_tests.txt
Diferenças do arquivo suprimidas por serem muito extensas
Ver arquivo


+ 2707
- 0
crypto/cmac/cavp_aes192_cmac_tests.txt
Diferenças do arquivo suprimidas por serem muito extensas
Ver arquivo


+ 2165
- 0
crypto/cmac/cavp_aes256_cmac_tests.txt
Diferenças do arquivo suprimidas por serem muito extensas
Ver arquivo


+ 51
- 25
crypto/cmac/cmac.c Ver arquivo

@@ -135,12 +135,11 @@ int CMAC_CTX_copy(CMAC_CTX *out, const CMAC_CTX *in) {
return 1;
}

// binary_field_mul_x treats the 128 bits at |in| as an element of GF(2¹²⁸)
// with a hard-coded reduction polynomial and sets |out| as x times the
// input.
// binary_field_mul_x_128 treats the 128 bits at |in| as an element of GF(2¹²⁸)
// with a hard-coded reduction polynomial and sets |out| as x times the input.
//
// See https://tools.ietf.org/html/rfc4493#section-2.3
static void binary_field_mul_x(uint8_t out[16], const uint8_t in[16]) {
static void binary_field_mul_x_128(uint8_t out[16], const uint8_t in[16]) {
unsigned i;

// Shift |in| to left, including carry.
@@ -153,23 +152,46 @@ static void binary_field_mul_x(uint8_t out[16], const uint8_t in[16]) {
out[i] = (in[i] << 1) ^ ((0 - carry) & 0x87);
}

// binary_field_mul_x_64 behaves like |binary_field_mul_x_128| but acts on an
// element of GF(2⁶⁴).
//
// See https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38b.pdf
static void binary_field_mul_x_64(uint8_t out[8], const uint8_t in[8]) {
unsigned i;

// Shift |in| to left, including carry.
for (i = 0; i < 7; i++) {
out[i] = (in[i] << 1) | (in[i+1] >> 7);
}

// If MSB set fixup with R.
const uint8_t carry = in[0] >> 7;
out[i] = (in[i] << 1) ^ ((0 - carry) & 0x1b);
}

static const uint8_t kZeroIV[AES_BLOCK_SIZE] = {0};

int CMAC_Init(CMAC_CTX *ctx, const void *key, size_t key_len,
const EVP_CIPHER *cipher, ENGINE *engine) {
uint8_t scratch[AES_BLOCK_SIZE];

if (EVP_CIPHER_block_size(cipher) != AES_BLOCK_SIZE ||
size_t block_size = EVP_CIPHER_block_size(cipher);
if ((block_size != AES_BLOCK_SIZE && block_size != 8 /* 3-DES */) ||
EVP_CIPHER_key_length(cipher) != key_len ||
!EVP_EncryptInit_ex(&ctx->cipher_ctx, cipher, NULL, key, kZeroIV) ||
!EVP_Cipher(&ctx->cipher_ctx, scratch, kZeroIV, AES_BLOCK_SIZE) ||
!EVP_Cipher(&ctx->cipher_ctx, scratch, kZeroIV, block_size) ||
// Reset context again ready for first data.
!EVP_EncryptInit_ex(&ctx->cipher_ctx, NULL, NULL, NULL, kZeroIV)) {
return 0;
}

binary_field_mul_x(ctx->k1, scratch);
binary_field_mul_x(ctx->k2, ctx->k1);
if (block_size == AES_BLOCK_SIZE) {
binary_field_mul_x_128(ctx->k1, scratch);
binary_field_mul_x_128(ctx->k2, ctx->k1);
} else {
binary_field_mul_x_64(ctx->k1, scratch);
binary_field_mul_x_64(ctx->k2, ctx->k1);
}
ctx->block_used = 0;

return 1;
@@ -181,10 +203,12 @@ int CMAC_Reset(CMAC_CTX *ctx) {
}

int CMAC_Update(CMAC_CTX *ctx, const uint8_t *in, size_t in_len) {
size_t block_size = EVP_CIPHER_CTX_block_size(&ctx->cipher_ctx);
assert(block_size <= AES_BLOCK_SIZE);
uint8_t scratch[AES_BLOCK_SIZE];

if (ctx->block_used > 0) {
size_t todo = AES_BLOCK_SIZE - ctx->block_used;
size_t todo = block_size - ctx->block_used;
if (in_len < todo) {
todo = in_len;
}
@@ -195,28 +219,28 @@ int CMAC_Update(CMAC_CTX *ctx, const uint8_t *in, size_t in_len) {
ctx->block_used += todo;

// If |in_len| is zero then either |ctx->block_used| is less than
// |AES_BLOCK_SIZE|, in which case we can stop here, or |ctx->block_used|
// is exactly |AES_BLOCK_SIZE| but there's no more data to process. In the
// latter case we don't want to process this block now because it might be
// the last block and that block is treated specially.
// |block_size|, in which case we can stop here, or |ctx->block_used| is
// exactly |block_size| but there's no more data to process. In the latter
// case we don't want to process this block now because it might be the last
// block and that block is treated specially.
if (in_len == 0) {
return 1;
}

assert(ctx->block_used == AES_BLOCK_SIZE);
assert(ctx->block_used == block_size);

if (!EVP_Cipher(&ctx->cipher_ctx, scratch, ctx->block, AES_BLOCK_SIZE)) {
if (!EVP_Cipher(&ctx->cipher_ctx, scratch, ctx->block, block_size)) {
return 0;
}
}

// Encrypt all but one of the remaining blocks.
while (in_len > AES_BLOCK_SIZE) {
if (!EVP_Cipher(&ctx->cipher_ctx, scratch, in, AES_BLOCK_SIZE)) {
while (in_len > block_size) {
if (!EVP_Cipher(&ctx->cipher_ctx, scratch, in, block_size)) {
return 0;
}
in += AES_BLOCK_SIZE;
in_len -= AES_BLOCK_SIZE;
in += block_size;
in_len -= block_size;
}

OPENSSL_memcpy(ctx->block, in, in_len);
@@ -226,27 +250,29 @@ int CMAC_Update(CMAC_CTX *ctx, const uint8_t *in, size_t in_len) {
}

int CMAC_Final(CMAC_CTX *ctx, uint8_t *out, size_t *out_len) {
*out_len = AES_BLOCK_SIZE;
size_t block_size = EVP_CIPHER_CTX_block_size(&ctx->cipher_ctx);
assert(block_size <= AES_BLOCK_SIZE);

*out_len = block_size;
if (out == NULL) {
return 1;
}

const uint8_t *mask = ctx->k1;

if (ctx->block_used != AES_BLOCK_SIZE) {
if (ctx->block_used != block_size) {
// If the last block is incomplete, terminate it with a single 'one' bit
// followed by zeros.
ctx->block[ctx->block_used] = 0x80;
OPENSSL_memset(ctx->block + ctx->block_used + 1, 0,
AES_BLOCK_SIZE - (ctx->block_used + 1));
block_size - (ctx->block_used + 1));

mask = ctx->k2;
}

unsigned i;
for (i = 0; i < AES_BLOCK_SIZE; i++) {
for (unsigned i = 0; i < block_size; i++) {
out[i] = ctx->block[i] ^ mask[i];
}

return EVP_Cipher(&ctx->cipher_ctx, out, out, AES_BLOCK_SIZE);
return EVP_Cipher(&ctx->cipher_ctx, out, out, block_size);
}

+ 82
- 0
crypto/cmac/cmac_test.cc Ver arquivo

@@ -183,3 +183,85 @@ TEST(CMACTest, Wycheproof) {
}
});
}

static void RunCAVPTest(const char *path, const EVP_CIPHER *cipher,
bool is_3des) {
FileTestGTest(path, [&](FileTest *t) {
t->IgnoreAttribute("Count");
t->IgnoreAttribute("Klen");
std::string t_len, m_len, result;
ASSERT_TRUE(t->GetAttribute(&t_len, "Tlen"));
ASSERT_TRUE(t->GetAttribute(&m_len, "Mlen"));
ASSERT_TRUE(t->GetAttribute(&result, "Result"));
std::vector<uint8_t> key, msg, mac;
if (is_3des) {
std::vector<uint8_t> key2, key3;
ASSERT_TRUE(t->GetBytes(&key, "Key1"));
ASSERT_TRUE(t->GetBytes(&key2, "Key2"));
ASSERT_TRUE(t->GetBytes(&key3, "Key3"));
key.insert(key.end(), key2.begin(), key2.end());
key.insert(key.end(), key3.begin(), key3.end());
} else {
ASSERT_TRUE(t->GetBytes(&key, "Key"));
}
ASSERT_TRUE(t->GetBytes(&msg, "Msg"));
ASSERT_TRUE(t->GetBytes(&mac, "Mac"));

// CAVP's uses a non-empty Msg attribute and zero Mlen for the empty string.
if (atoi(m_len.c_str()) == 0) {
msg.clear();
} else {
EXPECT_EQ(static_cast<size_t>(atoi(m_len.c_str())), msg.size());
}

size_t tag_len = static_cast<size_t>(atoi(t_len.c_str()));

uint8_t out[16];
bssl::UniquePtr<CMAC_CTX> ctx(CMAC_CTX_new());
ASSERT_TRUE(ctx);
ASSERT_TRUE(CMAC_Init(ctx.get(), key.data(), key.size(), cipher, NULL));
ASSERT_TRUE(CMAC_Update(ctx.get(), msg.data(), msg.size()));
size_t out_len;
ASSERT_TRUE(CMAC_Final(ctx.get(), out, &out_len));
// Truncate the tag, if requested.
out_len = std::min(out_len, tag_len);

ASSERT_FALSE(result.empty());
if (result[0] == 'P') {
EXPECT_EQ(Bytes(mac), Bytes(out, out_len));

// Test the streaming API as well.
ASSERT_TRUE(CMAC_Reset(ctx.get()));
for (uint8_t b : msg) {
ASSERT_TRUE(CMAC_Update(ctx.get(), &b, 1));
}
ASSERT_TRUE(CMAC_Final(ctx.get(), out, &out_len));
out_len = std::min(out_len, tag_len);
EXPECT_EQ(Bytes(mac), Bytes(out, out_len));
} else {
// CAVP's invalid tests assume the implementation internally does the
// comparison, whereas our API only computes the tag. Check that they're
// not equal, but these tests are mostly not useful for us.
EXPECT_NE(Bytes(mac), Bytes(out, out_len));
}
});
}

TEST(CMACTest, CAVPAES128) {
RunCAVPTest("crypto/cmac/cavp_aes128_cmac_tests.txt", EVP_aes_128_cbc(),
false);
}

TEST(CMACTest, CAVPAES192) {
RunCAVPTest("crypto/cmac/cavp_aes192_cmac_tests.txt", EVP_aes_192_cbc(),
false);
}

TEST(CMACTest, CAVPAES256) {
RunCAVPTest("crypto/cmac/cavp_aes256_cmac_tests.txt", EVP_aes_256_cbc(),
false);
}

TEST(CMACTest, CAVP3DES) {
RunCAVPTest("crypto/cmac/cavp_3des_cmac_tests.txt", EVP_des_ede3_cbc(), true);
}

+ 4
- 0
sources.cmake Ver arquivo

@@ -39,6 +39,10 @@ set(
crypto/cipher_extra/test/nist_cavp/tdes_cbc.txt
crypto/cipher_extra/test/nist_cavp/tdes_ecb.txt
crypto/curve25519/ed25519_tests.txt
crypto/cmac/cavp_3des_cmac_tests.txt
crypto/cmac/cavp_aes128_cmac_tests.txt
crypto/cmac/cavp_aes192_cmac_tests.txt
crypto/cmac/cavp_aes256_cmac_tests.txt
crypto/ecdh/ecdh_tests.txt
crypto/evp/evp_tests.txt
crypto/evp/scrypt_tests.txt


Carregando…
Cancelar
Salvar