/** * 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 "hmac_drbg.h" #include #include "memzero.h" #include "sha2.h" static void update_k(HMAC_DRBG_CTX *ctx, uint8_t domain, const uint8_t *data1, size_t len1, const uint8_t *data2, size_t len2) { // Computes K = HMAC(K, V || domain || data1 || data 2). // First hash operation of HMAC. uint32_t h[SHA256_BLOCK_LENGTH / sizeof(uint32_t)] = {0}; if (len1 + len2 == 0) { 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, data1, len1); sha256_Update(&sha_ctx, data2, len2); 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 entropy_len, const uint8_t *nonce, size_t nonce_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, entropy_len, nonce, nonce_len); memzero(h, sizeof(h)); } void hmac_drbg_reseed(HMAC_DRBG_CTX *ctx, const uint8_t *entropy, size_t len, const uint8_t *addin, size_t addin_len) { update_k(ctx, 0, entropy, len, addin, addin_len); update_v(ctx); if (len == 0) return; update_k(ctx, 1, entropy, len, addin, addin_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, NULL, 0); update_v(ctx); }