hash.c (3446B)
1 /* Public hashing API: one-shot and streaming SHA-256, BLAKE2b-256, and CRC-32. 2 * A thin composition over src/core/{sha256,crc32} and src/dist/blake2b. See 3 * <kit/hash.h>. */ 4 5 #include <kit/hash.h> 6 #include <string.h> 7 8 #include "core/crc32.h" 9 #include "core/sha256.h" 10 #include "dist/blake2b.h" 11 12 /* sha256_update takes a 32-bit length; feed large inputs in bounded chunks. */ 13 #define HASH_SHA_CHUNK 0x40000000u /* 1 GiB */ 14 15 struct KitHasher { 16 const KitContext* ctx; 17 KitHashAlgo algo; 18 union { 19 Sha256 sha; 20 DistBlake2b b2; 21 uint32_t crc; 22 } st; 23 }; 24 25 size_t kit_hash_len(KitHashAlgo algo) { 26 switch (algo) { 27 case KIT_HASH_SHA256: 28 return KIT_SHA256_LEN; 29 case KIT_HASH_BLAKE2B: 30 return KIT_BLAKE2B_LEN; 31 case KIT_HASH_CRC32: 32 return KIT_CRC32_LEN; 33 } 34 return 0; 35 } 36 37 /* Initialize the per-algo running state (shared by new + the one-shot path). */ 38 static void hasher_begin(KitHasher* h) { 39 switch (h->algo) { 40 case KIT_HASH_SHA256: 41 sha256_init(&h->st.sha); 42 break; 43 case KIT_HASH_BLAKE2B: 44 dist_blake2b_init(&h->st.b2, KIT_BLAKE2B_LEN); 45 break; 46 case KIT_HASH_CRC32: 47 h->st.crc = 0; 48 break; 49 } 50 } 51 52 void kit_hasher_update(KitHasher* h, const uint8_t* data, size_t len) { 53 if (!h || (!data && len)) return; 54 switch (h->algo) { 55 case KIT_HASH_SHA256: 56 while (len) { 57 uint32_t n = len > HASH_SHA_CHUNK ? HASH_SHA_CHUNK : (uint32_t)len; 58 sha256_update(&h->st.sha, data, n); 59 data += n; 60 len -= n; 61 } 62 break; 63 case KIT_HASH_BLAKE2B: 64 dist_blake2b_update(&h->st.b2, data, len); 65 break; 66 case KIT_HASH_CRC32: 67 h->st.crc = kit_crc32(h->st.crc, data, len); 68 break; 69 } 70 } 71 72 KitStatus kit_hasher_final(KitHasher* h, uint8_t* out, size_t* out_len) { 73 if (!h || !out) return KIT_INVALID; 74 switch (h->algo) { 75 case KIT_HASH_SHA256: 76 sha256_final(&h->st.sha, out); 77 break; 78 case KIT_HASH_BLAKE2B: 79 dist_blake2b_final(&h->st.b2, out); 80 break; 81 case KIT_HASH_CRC32: 82 out[0] = (uint8_t)(h->st.crc >> 24); 83 out[1] = (uint8_t)(h->st.crc >> 16); 84 out[2] = (uint8_t)(h->st.crc >> 8); 85 out[3] = (uint8_t)(h->st.crc); 86 break; 87 } 88 if (out_len) *out_len = kit_hash_len(h->algo); 89 return KIT_OK; 90 } 91 92 KitStatus kit_hasher_new(const KitContext* ctx, KitHashAlgo algo, 93 KitHasher** out) { 94 KitHeap* heap; 95 KitHasher* h; 96 if (!out) return KIT_INVALID; 97 *out = NULL; 98 if (!ctx || !ctx->heap) return KIT_INVALID; 99 if (kit_hash_len(algo) == 0) return KIT_INVALID; 100 heap = ctx->heap; 101 h = (KitHasher*)heap->alloc(heap, sizeof(*h), _Alignof(KitHasher)); 102 if (!h) return KIT_NOMEM; 103 memset(h, 0, sizeof(*h)); 104 h->ctx = ctx; 105 h->algo = algo; 106 hasher_begin(h); 107 *out = h; 108 return KIT_OK; 109 } 110 111 void kit_hasher_free(KitHasher* h) { 112 KitHeap* heap; 113 if (!h) return; 114 heap = h->ctx->heap; 115 heap->free(heap, h, sizeof(*h)); 116 } 117 118 KitStatus kit_hash(KitHashAlgo algo, const uint8_t* data, size_t len, 119 uint8_t* out, size_t* out_len) { 120 KitHasher tmp; 121 if (!out || (!data && len)) return KIT_INVALID; 122 if (kit_hash_len(algo) == 0) return KIT_INVALID; 123 /* No heap needed: the running state lives on the stack and neither update 124 * nor final touches tmp.ctx. */ 125 memset(&tmp, 0, sizeof tmp); 126 tmp.algo = algo; 127 hasher_begin(&tmp); 128 kit_hasher_update(&tmp, data, len); 129 return kit_hasher_final(&tmp, out, out_len); 130 }