diff --git a/crypto/Makefile b/crypto/Makefile index cb3ebe80f..f51de750e 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -64,6 +64,7 @@ SRCS += nem.c SRCS += segwit_addr.c cash_addr.c SRCS += memzero.c SRCS += shamir.c +SRCS += hmac_drbg.c OBJS = $(SRCS:.c=.o) diff --git a/crypto/hmac_drbg.c b/crypto/hmac_drbg.c new file mode 100644 index 000000000..fe53dc7ff --- /dev/null +++ b/crypto/hmac_drbg.c @@ -0,0 +1,126 @@ +/** + * Copyright (c) 2019 Andrew R. Kozlik + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include "hmac_drbg.h" +#include "memzero.h" +#include "sha2.h" + +static void update_k(HMAC_DRBG_CTX *ctx, uint8_t domain, const uint8_t *data, + size_t len) { + // Computes K = HMAC(K, V || domain || data). + + // First hash operation of HMAC. + uint32_t h[SHA256_BLOCK_LENGTH / sizeof(uint32_t)] = {0}; + if (data == NULL) { + ctx->v[8] = 0x00800000; + ctx->v[15] = (SHA256_BLOCK_LENGTH + SHA256_DIGEST_LENGTH + 1) * 8; + sha256_Transform(ctx->idig, ctx->v, h); + ctx->v[8] = 0x80000000; + ctx->v[15] = (SHA256_BLOCK_LENGTH + SHA256_DIGEST_LENGTH) * 8; + } else { + SHA256_CTX sha_ctx; + memcpy(sha_ctx.state, ctx->idig, SHA256_DIGEST_LENGTH); + for (size_t i = 0; i < SHA256_DIGEST_LENGTH / sizeof(uint32_t); i++) { +#if BYTE_ORDER == LITTLE_ENDIAN + REVERSE32(ctx->v[i], sha_ctx.buffer[i]); +#else + sha_ctx.buffer[i] = ctx->v[i]; +#endif + } + ((uint8_t *)sha_ctx.buffer)[SHA256_DIGEST_LENGTH] = domain; + sha_ctx.bitcount = (SHA256_BLOCK_LENGTH + SHA256_DIGEST_LENGTH + 1) * 8; + sha256_Update(&sha_ctx, data, len); + sha256_Final(&sha_ctx, (uint8_t *)h); +#if BYTE_ORDER == LITTLE_ENDIAN + for (size_t i = 0; i < SHA256_DIGEST_LENGTH / sizeof(uint32_t); i++) + REVERSE32(h[i], h[i]); +#endif + } + + // Second hash operation of HMAC. + h[8] = 0x80000000; + h[15] = (SHA256_BLOCK_LENGTH + SHA256_DIGEST_LENGTH) * 8; + sha256_Transform(ctx->odig, h, h); + + // Precompute the inner digest and outer digest of K. + h[8] = 0; + h[15] = 0; + for (size_t i = 0; i < SHA256_BLOCK_LENGTH / sizeof(uint32_t); i++) { + h[i] ^= 0x36363636; + } + sha256_Transform(sha256_initial_hash_value, h, ctx->idig); + + for (size_t i = 0; i < SHA256_BLOCK_LENGTH / sizeof(uint32_t); i++) { + h[i] = h[i] ^ 0x36363636 ^ 0x5c5c5c5c; + } + sha256_Transform(sha256_initial_hash_value, h, ctx->odig); + memzero(h, sizeof(h)); +} + +static void update_v(HMAC_DRBG_CTX *ctx) { + sha256_Transform(ctx->idig, ctx->v, ctx->v); + sha256_Transform(ctx->odig, ctx->v, ctx->v); +} + +void hmac_drbg_init(HMAC_DRBG_CTX *ctx, const uint8_t *entropy, size_t len) { + uint32_t h[SHA256_BLOCK_LENGTH / sizeof(uint32_t)]; + + // Precompute the inner digest and outer digest of K = 0x00 ... 0x00. + memset(h, 0x36, sizeof(h)); + sha256_Transform(sha256_initial_hash_value, h, ctx->idig); + memset(h, 0x5c, sizeof(h)); + sha256_Transform(sha256_initial_hash_value, h, ctx->odig); + + // Let V = 0x01 ... 0x01. + memset(ctx->v, 1, SHA256_DIGEST_LENGTH); + for (size_t i = 9; i < 15; i++) ctx->v[i] = 0; + ctx->v[8] = 0x80000000; + ctx->v[15] = (SHA256_BLOCK_LENGTH + SHA256_DIGEST_LENGTH) * 8; + + hmac_drbg_reseed(ctx, entropy, len); + + memzero(h, sizeof(h)); +} + +void hmac_drbg_reseed(HMAC_DRBG_CTX *ctx, const uint8_t *entropy, size_t len) { + update_k(ctx, 0, entropy, len); + update_v(ctx); + if (len == 0) return; + update_k(ctx, 1, entropy, len); + update_v(ctx); +} + +void hmac_drbg_generate(HMAC_DRBG_CTX *ctx, uint8_t *buf, size_t len) { + size_t i = 0; + while (i < len) { + update_v(ctx); + for (size_t j = 0; j < 8; j++) { + uint32_t r = ctx->v[j]; + for (int k = 24; k >= 0; k -= 8) { + buf[i++] = (r >> k) & 0xFF; + } + } + } + update_k(ctx, 0, NULL, 0); + update_v(ctx); +} diff --git a/crypto/hmac_drbg.h b/crypto/hmac_drbg.h new file mode 100644 index 000000000..0bba18901 --- /dev/null +++ b/crypto/hmac_drbg.h @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2019 Andrew R. Kozlik + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __HMAC_DRBG_H__ +#define __HMAC_DRBG_H__ + +#include +#include + +// HMAC based Deterministic Random Bit Generator with SHA-256 + +typedef struct _HMAC_DRBG_CTX { + uint32_t odig[SHA256_DIGEST_LENGTH / sizeof(uint32_t)]; + uint32_t idig[SHA256_DIGEST_LENGTH / sizeof(uint32_t)]; + uint32_t v[SHA256_BLOCK_LENGTH / sizeof(uint32_t)]; +} HMAC_DRBG_CTX; + +void hmac_drbg_init(HMAC_DRBG_CTX *ctx, const uint8_t *buf, size_t len); +void hmac_drbg_reseed(HMAC_DRBG_CTX *ctx, const uint8_t *buf, size_t len); +void hmac_drbg_generate(HMAC_DRBG_CTX *ctx, uint8_t *buf, size_t len); + +#endif diff --git a/crypto/tests/test_check.c b/crypto/tests/test_check.c index 9a007bed6..85f106cc8 100644 --- a/crypto/tests/test_check.c +++ b/crypto/tests/test_check.c @@ -53,6 +53,7 @@ #include "ed25519-donna/ed25519-donna.h" #include "ed25519-donna/ed25519-keccak.h" #include "ed25519-donna/ed25519.h" +#include "hmac_drbg.h" #include "memzero.h" #include "monero/monero.h" #include "nem.h" @@ -4624,6 +4625,34 @@ START_TEST(test_pbkdf2_hmac_sha512) { } END_TEST +START_TEST(test_hmac_drbg) { + char entropy[] = + "06032cd5eed33f39265f49ecb142c511da9aff2af71203bffaf34a9ca5bd9c0d0e66f71e" + "dc43e42a45ad3c6fc6cdc4df"; + char reseed[] = + "01920a4e669ed3a85ae8a33b35a74ad7fb2a6bb4cf395ce00334a9c9a5a5d552"; + char expected[] = + "76fc79fe9b50beccc991a11b5635783a83536add03c157fb30645e611c2898bb2b1bc215" + "000209208cd506cb28da2a51bdb03826aaf2bd2335d576d519160842e7158ad0949d1a9e" + "c3e66ea1b1a064b005de914eac2e9d4f2d72a8616a80225422918250ff66a41bd2f864a6" + "a38cc5b6499dc43f7f2bd09e1e0f8f5885935124"; + uint8_t result[128]; + + HMAC_DRBG_CTX ctx; + hmac_drbg_init(&ctx, fromhex(entropy), strlen(entropy) / 2); + hmac_drbg_reseed(&ctx, fromhex(reseed), strlen(reseed) / 2); + hmac_drbg_generate(&ctx, result, sizeof(result)); + hmac_drbg_generate(&ctx, result, sizeof(result)); + ck_assert_mem_eq(result, fromhex(expected), sizeof(result)); + + hmac_drbg_init(&ctx, fromhex(entropy), strlen(entropy) / 2); + hmac_drbg_reseed(&ctx, fromhex(reseed), strlen(reseed) / 2); + hmac_drbg_generate(&ctx, result, sizeof(result) - 13); + hmac_drbg_generate(&ctx, result, sizeof(result) - 17); + ck_assert_mem_eq(result, fromhex(expected), sizeof(result) - 17); +} +END_TEST + START_TEST(test_mnemonic) { static const char *vectors[] = { "00000000000000000000000000000000", @@ -8628,6 +8657,10 @@ Suite *test_suite(void) { tcase_add_test(tc, test_pbkdf2_hmac_sha512); suite_add_tcase(s, tc); + tc = tcase_create("hmac_drbg"); + tcase_add_test(tc, test_hmac_drbg); + suite_add_tcase(s, tc); + tc = tcase_create("bip39"); tcase_add_test(tc, test_mnemonic); tcase_add_test(tc, test_mnemonic_check);