From 33e840b01b4e9ec57b83e68bac8f180d489c2f92 Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Mon, 5 Dec 2016 15:38:42 -0700 Subject: [PATCH] add AES key wrap support, RFC 3394 --- configure.ac | 14 +++ wolfcrypt/src/aes.c | 172 +++++++++++++++++++++++++++++ wolfcrypt/src/error.c | 3 + wolfcrypt/test/test.c | 190 ++++++++++++++++++++++++++++++++ wolfssl/wolfcrypt/aes.h | 11 ++ wolfssl/wolfcrypt/error-crypt.h | 2 + wolfssl/wolfcrypt/settings.h | 6 + 7 files changed, 398 insertions(+) diff --git a/configure.ac b/configure.ac index ee8d9822c..5fbc83f3b 100644 --- a/configure.ac +++ b/configure.ac @@ -2870,6 +2870,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"]) @@ -3318,6 +3331,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 "---" diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index 9837e9f29..8f5ebedcd 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -4504,6 +4504,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 */ diff --git a/wolfcrypt/src/error.c b/wolfcrypt/src/error.c index f247c61e8..9a030bdc6 100644 --- a/wolfcrypt/src/error.c +++ b/wolfcrypt/src/error.c @@ -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"; diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 9a00a8539..ef7e4756e 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -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 */ diff --git a/wolfssl/wolfcrypt/aes.h b/wolfssl/wolfcrypt/aes.h index 046f81b0d..b7fb2c776 100644 --- a/wolfssl/wolfcrypt/aes.h +++ b/wolfssl/wolfcrypt/aes.h @@ -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); diff --git a/wolfssl/wolfcrypt/error-crypt.h b/wolfssl/wolfcrypt/error-crypt.h index c6825ce26..62bad6f83 100644 --- a/wolfssl/wolfcrypt/error-crypt.h +++ b/wolfssl/wolfcrypt/error-crypt.h @@ -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 diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index cd269517a..9a229028f 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -1404,6 +1404,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 */