Merge pull request #648 from cconlon/keywrap

add AES key wrap support, RFC 3394
This commit is contained in:
toddouska 2016-12-09 13:23:39 -08:00 committed by GitHub
commit ad2b0810c6
7 changed files with 398 additions and 0 deletions

View File

@ -2876,6 +2876,19 @@ then
fi
# AES key wrap
AC_ARG_ENABLE([aeskeywrap],
[AS_HELP_STRING([--enable-aeskeywrap],[Enable AES key wrap support (default: disabled)])],
[ ENABLED_AESKEYWRAP=$enableval ],
[ ENABLED_AESKEYWRAP=no ]
)
if test "$ENABLED_AESKEYWRAP" = "yes"
then
AM_CFLAGS="$AM_CFLAGS -DHAVE_AES_KEYWRAP -DWOLFSSL_AES_DIRECT"
fi
# check if PSK was enabled for conditionally running psk.test script
AM_CONDITIONAL([BUILD_PSK], [test "x$ENABLED_PSK" = "xyes"])
@ -3324,6 +3337,7 @@ echo " * Fast RSA: $ENABLED_FAST_RSA"
echo " * Async Crypto: $ENABLED_ASYNCCRYPT"
echo " * Cavium: $ENABLED_CAVIUM"
echo " * ARM ASM: $ENABLED_ARMASM"
echo " * AES Key Wrap: $ENABLED_AESKEYWRAP"
echo ""
echo "---"

View File

@ -4715,6 +4715,178 @@ int wc_AesCcmDecrypt(Aes* aes, byte* out, const byte* in, word32 inSz,
#endif /* HAVE_AESCCM */
#ifdef HAVE_AES_KEYWRAP
/* Initialize key wrap counter with value */
static INLINE void InitKeyWrapCounter(byte* inOutCtr, word32 value)
{
int i;
word32 bytes;
bytes = sizeof(word32);
for (i = 0; i < (int)sizeof(word32); i++) {
inOutCtr[i+sizeof(word32)] = (value >> ((bytes - 1) * 8)) & 0xFF;
bytes--;
}
}
/* Increment key wrap counter */
static INLINE void IncrementKeyWrapCounter(byte* inOutCtr)
{
int i;
/* in network byte order so start at end and work back */
for (i = KEYWRAP_BLOCK_SIZE - 1; i >= 0; i--) {
if (++inOutCtr[i]) /* we're done unless we overflow */
return;
}
}
/* Decrement key wrap counter */
static INLINE void DecrementKeyWrapCounter(byte* inOutCtr)
{
int i;
for (i = KEYWRAP_BLOCK_SIZE - 1; i >= 0; i--) {
if (--inOutCtr[i] != 0xFF) /* we're done unless we underflow */
return;
}
}
/* perform AES key wrap (RFC3394), return out sz on success, negative on err */
int wc_AesKeyWrap(const byte* key, word32 keySz, const byte* in, word32 inSz,
byte* out, word32 outSz, const byte* iv)
{
Aes aes;
byte* r;
word32 i;
int ret, j;
byte t[KEYWRAP_BLOCK_SIZE];
byte tmp[AES_BLOCK_SIZE];
/* n must be at least 2, output size is n + 8 bytes */
if (key == NULL || in == NULL || inSz < 2 ||
out == NULL || outSz < (inSz + KEYWRAP_BLOCK_SIZE))
return BAD_FUNC_ARG;
/* input must be multiple of 64-bits */
if (inSz % KEYWRAP_BLOCK_SIZE != 0)
return BAD_FUNC_ARG;
/* user IV is optional */
if (iv == NULL) {
XMEMSET(tmp, 0xA6, KEYWRAP_BLOCK_SIZE);
} else {
XMEMCPY(tmp, iv, KEYWRAP_BLOCK_SIZE);
}
r = out + 8;
XMEMCPY(r, in, inSz);
XMEMSET(t, 0, sizeof(t));
ret = wc_AesSetKey(&aes, key, keySz, NULL, AES_ENCRYPTION);
if (ret != 0)
return ret;
for (j = 0; j <= 5; j++) {
for (i = 1; i <= inSz / KEYWRAP_BLOCK_SIZE; i++) {
/* load R[i] */
XMEMCPY(tmp + KEYWRAP_BLOCK_SIZE, r, KEYWRAP_BLOCK_SIZE);
wc_AesEncryptDirect(&aes, tmp, tmp);
/* calculate new A */
IncrementKeyWrapCounter(t);
xorbuf(tmp, t, KEYWRAP_BLOCK_SIZE);
/* save R[i] */
XMEMCPY(r, tmp + KEYWRAP_BLOCK_SIZE, KEYWRAP_BLOCK_SIZE);
r += KEYWRAP_BLOCK_SIZE;
}
r = out + KEYWRAP_BLOCK_SIZE;
}
/* C[0] = A */
XMEMCPY(out, tmp, KEYWRAP_BLOCK_SIZE);
return inSz + KEYWRAP_BLOCK_SIZE;
}
int wc_AesKeyUnWrap(const byte* key, word32 keySz, const byte* in, word32 inSz,
byte* out, word32 outSz, const byte* iv)
{
(void)iv;
Aes aes;
byte* r;
word32 i, n;
int ret, j;
byte t[KEYWRAP_BLOCK_SIZE];
byte tmp[AES_BLOCK_SIZE];
const byte* expIv;
const byte defaultIV[] = {
0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6
};
if (key == NULL || in == NULL || inSz < 3 ||
out == NULL || outSz < (inSz - KEYWRAP_BLOCK_SIZE))
return BAD_FUNC_ARG;
/* input must be multiple of 64-bits */
if (inSz % KEYWRAP_BLOCK_SIZE != 0)
return BAD_FUNC_ARG;
/* user IV optional */
if (iv != NULL) {
expIv = iv;
} else {
expIv = defaultIV;
}
/* A = C[0], R[i] = C[i] */
XMEMCPY(tmp, in, KEYWRAP_BLOCK_SIZE);
XMEMCPY(out, in + KEYWRAP_BLOCK_SIZE, inSz - KEYWRAP_BLOCK_SIZE);
XMEMSET(t, 0, sizeof(t));
ret = wc_AesSetKey(&aes, key, keySz, NULL, AES_DECRYPTION);
if (ret != 0)
return ret;
/* initialize counter to 6n */
n = (inSz - 1) / KEYWRAP_BLOCK_SIZE;
InitKeyWrapCounter(t, 6 * n);
for (j = 5; j >= 0; j--) {
for (i = n; i >= 1; i--) {
/* calculate A */
xorbuf(tmp, t, KEYWRAP_BLOCK_SIZE);
DecrementKeyWrapCounter(t);
/* load R[i], starting at end of R */
r = out + ((i - 1) * KEYWRAP_BLOCK_SIZE);
XMEMCPY(tmp + KEYWRAP_BLOCK_SIZE, r, KEYWRAP_BLOCK_SIZE);
wc_AesDecryptDirect(&aes, tmp, tmp);
/* save R[i] */
XMEMCPY(r, tmp + KEYWRAP_BLOCK_SIZE, KEYWRAP_BLOCK_SIZE);
}
}
/* verify IV */
if (XMEMCMP(tmp, expIv, KEYWRAP_BLOCK_SIZE) != 0)
return BAD_KEYWRAP_IV_E;
return inSz - KEYWRAP_BLOCK_SIZE;
}
#endif /* HAVE_AES_KEYWRAP */
#ifdef WOLFSSL_ASYNC_CRYPT
/* Initialize Aes for use with Nitrox device */

View File

@ -401,6 +401,9 @@ const char* wc_GetErrorString(int error)
case ASN_PATHLEN_INV_E:
return "ASN CA path length larger than signer error";
case BAD_KEYWRAP_IV_E:
return "Decrypted AES key wrap IV does not match expected";
default:
return "unknown error number";

View File

@ -198,6 +198,7 @@ int poly1305_test(void);
int aesgcm_test(void);
int gmac_test(void);
int aesccm_test(void);
int aeskeywrap_test(void);
int camellia_test(void);
int rsa_test(void);
int dh_test(void);
@ -556,6 +557,12 @@ int wolfcrypt_test(void* args)
else
printf( "AES-CCM test passed!\n");
#endif
#ifdef HAVE_AES_KEYWRAP
if ( (ret = aeskeywrap_test()) != 0)
return err_sys("AES Key Wrap test failed!\n", ret);
else
printf( "AES Key Wrap test passed!\n");
#endif
#endif
#ifdef HAVE_CAMELLIA
@ -3526,6 +3533,189 @@ int aesccm_test(void)
#endif /* HAVE_AESCCM */
#ifdef HAVE_AES_KEYWRAP
#define MAX_KEYWRAP_TEST_OUTLEN 40
#define MAX_KEYWRAP_TEST_PLAINLEN 32
typedef struct keywrapVector {
const byte* kek;
const byte* data;
const byte* verify;
word32 kekLen;
word32 dataLen;
word32 verifyLen;
} keywrapVector;
int aeskeywrap_test(void)
{
int wrapSz, plainSz, testSz, i;
/* test vectors from RFC 3394 (kek, data, verify) */
/* Wrap 128 bits of Key Data with a 128-bit KEK */
const byte k1[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
};
const byte d1[] = {
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
};
const byte v1[] = {
0x1F, 0xA6, 0x8B, 0x0A, 0x81, 0x12, 0xB4, 0x47,
0xAE, 0xF3, 0x4B, 0xD8, 0xFB, 0x5A, 0x7B, 0x82,
0x9D, 0x3E, 0x86, 0x23, 0x71, 0xD2, 0xCF, 0xE5
};
/* Wrap 128 bits of Key Data with a 192-bit KEK */
const byte k2[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17
};
const byte d2[] = {
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
};
const byte v2[] = {
0x96, 0x77, 0x8B, 0x25, 0xAE, 0x6C, 0xA4, 0x35,
0xF9, 0x2B, 0x5B, 0x97, 0xC0, 0x50, 0xAE, 0xD2,
0x46, 0x8A, 0xB8, 0xA1, 0x7A, 0xD8, 0x4E, 0x5D
};
/* Wrap 128 bits of Key Data with a 256-bit KEK */
const byte k3[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
};
const byte d3[] = {
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
};
const byte v3[] = {
0x64, 0xE8, 0xC3, 0xF9, 0xCE, 0x0F, 0x5B, 0xA2,
0x63, 0xE9, 0x77, 0x79, 0x05, 0x81, 0x8A, 0x2A,
0x93, 0xC8, 0x19, 0x1E, 0x7D, 0x6E, 0x8A, 0xE7
};
/* Wrap 192 bits of Key Data with a 192-bit KEK */
const byte k4[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17
};
const byte d4[] = {
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
};
const byte v4[] = {
0x03, 0x1D, 0x33, 0x26, 0x4E, 0x15, 0xD3, 0x32,
0x68, 0xF2, 0x4E, 0xC2, 0x60, 0x74, 0x3E, 0xDC,
0xE1, 0xC6, 0xC7, 0xDD, 0xEE, 0x72, 0x5A, 0x93,
0x6B, 0xA8, 0x14, 0x91, 0x5C, 0x67, 0x62, 0xD2
};
/* Wrap 192 bits of Key Data with a 256-bit KEK */
const byte k5[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
};
const byte d5[] = {
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
};
const byte v5[] = {
0xA8, 0xF9, 0xBC, 0x16, 0x12, 0xC6, 0x8B, 0x3F,
0xF6, 0xE6, 0xF4, 0xFB, 0xE3, 0x0E, 0x71, 0xE4,
0x76, 0x9C, 0x8B, 0x80, 0xA3, 0x2C, 0xB8, 0x95,
0x8C, 0xD5, 0xD1, 0x7D, 0x6B, 0x25, 0x4D, 0xA1
};
/* Wrap 256 bits of Key Data with a 256-bit KEK */
const byte k6[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
};
const byte d6[] = {
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
};
const byte v6[] = {
0x28, 0xC9, 0xF4, 0x04, 0xC4, 0xB8, 0x10, 0xF4,
0xCB, 0xCC, 0xB3, 0x5C, 0xFB, 0x87, 0xF8, 0x26,
0x3F, 0x57, 0x86, 0xE2, 0xD8, 0x0E, 0xD3, 0x26,
0xCB, 0xC7, 0xF0, 0xE7, 0x1A, 0x99, 0xF4, 0x3B,
0xFB, 0x98, 0x8B, 0x9B, 0x7A, 0x02, 0xDD, 0x21
};
byte output[MAX_KEYWRAP_TEST_OUTLEN];
byte plain [MAX_KEYWRAP_TEST_PLAINLEN];
const keywrapVector test_wrap[] =
{
{k1, d1, v1, sizeof(k1), sizeof(d1), sizeof(v1)},
{k2, d2, v2, sizeof(k2), sizeof(d2), sizeof(v2)},
{k3, d3, v3, sizeof(k3), sizeof(d3), sizeof(v3)},
{k4, d4, v4, sizeof(k4), sizeof(d4), sizeof(v4)},
{k5, d5, v5, sizeof(k5), sizeof(d5), sizeof(v5)},
{k6, d6, v6, sizeof(k6), sizeof(d6), sizeof(v6)}
};
testSz = sizeof(test_wrap) / sizeof(keywrapVector);
XMEMSET(output, 0, sizeof(output));
XMEMSET(plain, 0, sizeof(plain));
for (i = 0; i < testSz; i++) {
wrapSz = wc_AesKeyWrap(test_wrap[i].kek, test_wrap[i].kekLen,
test_wrap[i].data, test_wrap[i].dataLen,
output, sizeof(output), NULL);
if ( (wrapSz < 0) || (wrapSz != (int)test_wrap[i].verifyLen) )
return -101;
if (XMEMCMP(output, test_wrap[i].verify, test_wrap[i].verifyLen) != 0)
return -102;
plainSz = wc_AesKeyUnWrap((byte*)test_wrap[i].kek, test_wrap[i].kekLen,
output, wrapSz,
plain, sizeof(plain), NULL);
if ( (plainSz < 0) || (plainSz != (int)test_wrap[i].dataLen) )
return -103;
if (XMEMCMP(plain, test_wrap[i].data, test_wrap[i].dataLen) != 0)
return -104;
}
return 0;
}
#endif /* HAVE_AES_KEYWRAP */
#endif /* NO_AES */

View File

@ -64,6 +64,7 @@ enum {
AES_ENC_TYPE = 1, /* cipher unique type */
AES_ENCRYPTION = 0,
AES_DECRYPTION = 1,
KEYWRAP_BLOCK_SIZE = 8,
AES_BLOCK_SIZE = 16
};
@ -166,6 +167,16 @@ WOLFSSL_API int wc_AesCbcDecrypt(Aes* aes, byte* out,
const byte* authTag, word32 authTagSz,
const byte* authIn, word32 authInSz);
#endif /* HAVE_AESCCM */
#ifdef HAVE_AES_KEYWRAP
WOLFSSL_API int wc_AesKeyWrap(const byte* key, word32 keySz,
const byte* in, word32 inSz,
byte* out, word32 outSz,
const byte* iv);
WOLFSSL_API int wc_AesKeyUnWrap(const byte* key, word32 keySz,
const byte* in, word32 inSz,
byte* out, word32 outSz,
const byte* iv);
#endif /* HAVE_AES_KEYWRAP */
WOLFSSL_API int wc_AesGetKeySize(Aes* aes, word32* keySize);

View File

@ -178,6 +178,8 @@ enum {
ASN_PATHLEN_SIZE_E = -237, /* ASN CA path length too large error */
ASN_PATHLEN_INV_E = -238, /* ASN CA path length inversion error */
BAD_KEYWRAP_IV_E = -239, /* Decrypted AES key wrap IV incorrect */
MIN_CODE_E = -300 /* errors -101 - -299 */
/* add new companion error id strings for any new error codes

View File

@ -1442,6 +1442,12 @@ static char *fgets(char *buff, int sz, FILE *fp)
#endif
#endif /* WOLFSSL_STATIC_MEMORY */
#ifdef HAVE_AES_KEYWRAP
#ifndef WOLFSSL_AES_DIRECT
#error AES key wrap requires AES direct please define WOLFSSL_AES_DIRECT
#endif
#endif
/* Place any other flags or defines here */