HKDF.c (4910B)
1 /* 2 * HKDF Implementation 3 * RFC 5869 - HMAC-based Extract-and-Expand Key Derivation Function 4 */ 5 6 #include "HKDF.h" 7 #include <string.h> 8 9 /* Use existing SHA-256 implementation */ 10 typedef struct { 11 uint32_t state[8]; 12 uint64_t count; 13 uint8_t buffer[64]; 14 } sha256_context; 15 16 extern void sha256_init(sha256_context *ctx); 17 extern void sha256_update(sha256_context *ctx, const uint8_t *data, size_t len); 18 extern void sha256_final(sha256_context *ctx, uint8_t digest[32]); 19 extern void sha256(const uint8_t *data, size_t len, uint8_t digest[32]); 20 21 #define SHA256_BLOCK_SIZE 64 22 #define SHA256_DIGEST_SIZE 32 23 24 /* HMAC-SHA256 context for incremental updates */ 25 typedef struct { 26 sha256_context inner; 27 uint8_t o_key_pad[SHA256_BLOCK_SIZE]; 28 } hmac_sha256_context; 29 30 static void hmac_sha256_init(hmac_sha256_context *ctx, const uint8_t *key, size_t key_len) { 31 uint8_t k_pad[SHA256_BLOCK_SIZE]; 32 uint8_t tk[SHA256_DIGEST_SIZE]; 33 34 /* If key is longer than block size, hash it first */ 35 if (key_len > SHA256_BLOCK_SIZE) { 36 sha256(key, key_len, tk); 37 key = tk; 38 key_len = SHA256_DIGEST_SIZE; 39 } 40 41 /* Prepare padded key */ 42 memset(k_pad, 0, sizeof(k_pad)); 43 memcpy(k_pad, key, key_len); 44 45 /* Store outer key pad for finalization */ 46 for (int i = 0; i < SHA256_BLOCK_SIZE; i++) { 47 ctx->o_key_pad[i] = k_pad[i] ^ 0x5c; 48 k_pad[i] ^= 0x36; 49 } 50 51 /* Initialize inner hash with i_key_pad */ 52 sha256_init(&ctx->inner); 53 sha256_update(&ctx->inner, k_pad, SHA256_BLOCK_SIZE); 54 55 memset(k_pad, 0, sizeof(k_pad)); 56 } 57 58 static void hmac_sha256_update(hmac_sha256_context *ctx, const uint8_t *data, size_t len) { 59 sha256_update(&ctx->inner, data, len); 60 } 61 62 static void hmac_sha256_final(hmac_sha256_context *ctx, uint8_t output[32]) { 63 uint8_t inner_hash[SHA256_DIGEST_SIZE]; 64 65 /* Finalize inner hash */ 66 sha256_final(&ctx->inner, inner_hash); 67 68 /* Compute outer hash: H(o_key_pad || inner_hash) */ 69 sha256_context outer; 70 sha256_init(&outer); 71 sha256_update(&outer, ctx->o_key_pad, SHA256_BLOCK_SIZE); 72 sha256_update(&outer, inner_hash, SHA256_DIGEST_SIZE); 73 sha256_final(&outer, output); 74 75 memset(inner_hash, 0, sizeof(inner_hash)); 76 } 77 78 /* Single-shot HMAC-SHA256 */ 79 static void hmac_sha256(const uint8_t *key, size_t key_len, 80 const uint8_t *data, size_t data_len, 81 uint8_t output[32]) { 82 hmac_sha256_context ctx; 83 hmac_sha256_init(&ctx, key, key_len); 84 hmac_sha256_update(&ctx, data, data_len); 85 hmac_sha256_final(&ctx, output); 86 } 87 88 /* HKDF-Extract using HMAC-SHA256 */ 89 void hkdf_sha256_extract(const uint8_t *salt, size_t salt_len, 90 const uint8_t *ikm, size_t ikm_len, 91 uint8_t prk[32]) { 92 /* If no salt provided, use zero salt */ 93 uint8_t zero_salt[32] = {0}; 94 if (!salt || salt_len == 0) { 95 salt = zero_salt; 96 salt_len = 32; 97 } 98 99 /* PRK = HMAC-SHA256(salt, IKM) */ 100 hmac_sha256(salt, salt_len, ikm, ikm_len, prk); 101 } 102 103 /* HKDF-Expand using HMAC-SHA256 */ 104 int hkdf_sha256_expand(const uint8_t prk[32], 105 const uint8_t *info, size_t info_len, 106 uint8_t *okm, size_t okm_len) { 107 /* Max output length: 255 * hash_len = 255 * 32 = 8160 bytes */ 108 if (okm_len > 255 * 32) { 109 return -1; 110 } 111 112 if (!info) { 113 info_len = 0; 114 } 115 116 uint8_t T[32] = {0}; /* Previous block */ 117 size_t T_len = 0; 118 uint8_t counter = 1; 119 size_t offset = 0; 120 121 while (offset < okm_len) { 122 /* Compute T(i) = HMAC(PRK, T(i-1) | info | i) */ 123 hmac_sha256_context ctx; 124 hmac_sha256_init(&ctx, prk, 32); 125 126 /* Add T(i-1) if not first iteration */ 127 if (T_len > 0) { 128 hmac_sha256_update(&ctx, T, T_len); 129 } 130 131 /* Add info */ 132 if (info_len > 0) { 133 hmac_sha256_update(&ctx, info, info_len); 134 } 135 136 /* Add counter */ 137 hmac_sha256_update(&ctx, &counter, 1); 138 139 /* Finalize */ 140 hmac_sha256_final(&ctx, T); 141 T_len = 32; 142 143 /* Copy to output */ 144 size_t to_copy = okm_len - offset; 145 if (to_copy > 32) { 146 to_copy = 32; 147 } 148 memcpy(okm + offset, T, to_copy); 149 offset += to_copy; 150 151 counter++; 152 } 153 154 /* Clean up */ 155 memset(T, 0, sizeof(T)); 156 157 return 0; 158 } 159 160 /* HKDF (Extract-then-Expand) */ 161 int hkdf_sha256(const uint8_t *salt, size_t salt_len, 162 const uint8_t *ikm, size_t ikm_len, 163 const uint8_t *info, size_t info_len, 164 uint8_t *okm, size_t okm_len) { 165 uint8_t prk[32]; 166 167 /* Extract */ 168 hkdf_sha256_extract(salt, salt_len, ikm, ikm_len, prk); 169 170 /* Expand */ 171 int result = hkdf_sha256_expand(prk, info, info_len, okm, okm_len); 172 173 /* Clean up */ 174 memset(prk, 0, sizeof(prk)); 175 176 return result; 177 }