Created
July 19, 2023 03:20
-
-
Save aabiji/02aa6982252859ab93ade8f9e6474497 to your computer and use it in GitHub Desktop.
A SHA256 implementatoin written in C.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#include <stdint.h> | |
// K[0] to K[63] are initialized as the first 32 bits of the fractional | |
// parts of the cube roots of the first 64 primes | |
const uint32_t k[64] = { | |
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, | |
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, | |
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, | |
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, | |
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, | |
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, | |
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, | |
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, | |
}; | |
typedef struct { | |
uint32_t h[8]; | |
} Hash; | |
void hash_init(Hash *hash) { | |
// h0 to h7 are initialized as the first 32 bits of the | |
// fractional part of the square roots of the first 8 primes | |
hash->h[0] = 0x6a09e667; | |
hash->h[1] = 0xbb67ae85; | |
hash->h[2] = 0x3c6ef372; | |
hash->h[3] = 0xa54ff53a; | |
hash->h[4] = 0x510e527f; | |
hash->h[5] = 0x9b05688c; | |
hash->h[6] = 0x1f83d9ab; | |
hash->h[7] = 0x5be0cd19; | |
} | |
int asprintf(char **strp, const char *fmt, ...); | |
char *hash_hexdigest(Hash *hash) { | |
char *str; | |
// *Output* What we computed | |
asprintf(&str, "%08x%08x%08x%08x%08x%08x%08x%08x", | |
hash->h[0], hash->h[1], hash->h[2], hash->h[3], hash->h[4], hash->h[5], hash->h[6], hash->h[7]); | |
return str; | |
} | |
// Rightrotate n by m bits | |
uint32_t rightrotate(uint32_t n, uint32_t m) { | |
return (n >> m) | (n << (32 - m)); | |
} | |
uint32_t pack32(int *idx, uint8_t chunk[]) { | |
uint32_t val = 0; | |
val = ((chunk[*idx] << 24) | (chunk[(*idx) + 1] << 16) | (chunk[(*idx) + 2] << 8) | chunk[(*idx) + 3]); | |
*idx += 4; | |
return val; | |
} | |
Hash hash_sha256(uint8_t bytes[], uint64_t size) { | |
Hash hash; | |
hash_init(&hash); | |
int total_size = ((size / 512) + 1) * 512; // In bits | |
int chunk_count = total_size / 512; // Each chunk is 512 bits (64 bytes) | |
int total_bytes = total_size / 8; | |
uint8_t *padded_bytes = calloc(total_bytes, sizeof(uint8_t)); | |
int size_bytes = size / 8; // Size in bytes not bits | |
memcpy(padded_bytes, bytes, size_bytes); | |
padded_bytes[size_bytes] = 0x80; // Append a single 1 | |
padded_bytes[total_bytes - 8] = (size >> 56) & 0xFF; | |
padded_bytes[total_bytes - 7] = (size >> 48) & 0xFF; | |
padded_bytes[total_bytes - 6] = (size >> 40) & 0xFF; | |
padded_bytes[total_bytes - 5] = (size >> 32) & 0xFF; | |
padded_bytes[total_bytes - 4] = (size >> 24) & 0xFF; | |
padded_bytes[total_bytes - 3] = (size >> 16) & 0xFF; | |
padded_bytes[total_bytes - 2] = (size >> 8) & 0xFF; | |
padded_bytes[total_bytes - 1] = size & 0xFF; | |
for (int idx = 0; idx < chunk_count; idx++) { | |
uint32_t w[64]; // Message schedule | |
// Copy the 512 bit chunk into the first 16 elements of the message block | |
int offset = 0; | |
uint8_t *chunk = malloc(64 * sizeof(uint8_t)); | |
memcpy(chunk, padded_bytes + (idx * 64), 64); | |
for (int i = 0; i < 16; i++) | |
w[i] = pack32(&offset, chunk); | |
free(chunk); | |
// Extend the first 16 elements to the 48 others | |
for (int i = 16; i < 64; i++) { | |
uint32_t s0 = rightrotate(w[i - 15], 7) ^ rightrotate(w[i - 15], 18) ^ (w[i - 15] >> 3); | |
uint32_t s1 = rightrotate(w[i - 2], 17) ^ rightrotate(w[i - 2], 19) ^ (w[i - 2] >> 10); | |
w[i] = w[i - 16] + s0 + w[i - 7] + s1; | |
} | |
// Main compression loop: | |
uint32_t a = hash.h[0]; | |
uint32_t b = hash.h[1]; | |
uint32_t c = hash.h[2]; | |
uint32_t d = hash.h[3]; | |
uint32_t e = hash.h[4]; | |
uint32_t f = hash.h[5]; | |
uint32_t g = hash.h[6]; | |
uint32_t h = hash.h[7]; | |
for (int i = 0; i < 64; i++) { | |
uint32_t s0 = rightrotate(a, 2) ^ rightrotate(a, 13) ^ rightrotate(a, 22); | |
uint32_t maj = (a & b) ^ (a & c) ^ (b & c); | |
uint32_t t2 = s0 + maj; | |
uint32_t s1 = rightrotate(e, 6) ^ rightrotate(e, 11) ^ rightrotate(e, 25); | |
uint32_t ch = (e & f) ^ (~e & g); | |
uint32_t t1 = h + s1 + ch + k[i] + w[i]; | |
h = g; | |
g = f; | |
f = e; | |
e = d + t1; | |
d = c; | |
c = b; | |
b = a; | |
a = t1 + t2; | |
} | |
hash.h[0] += a; | |
hash.h[1] += b; | |
hash.h[2] += c; | |
hash.h[3] += d; | |
hash.h[4] += e; | |
hash.h[5] += f; | |
hash.h[6] += g; | |
hash.h[7] += h; | |
} | |
free(padded_bytes); | |
return hash; | |
} | |
int main() { | |
// "Hello world" utf8 encoeded bytes | |
uint8_t hello_world[11] = {0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64}; | |
char *hello_world_hash = "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"; | |
Hash h = hash_sha256(hello_world, 11 * 8); // Size is provided in bits | |
char *hashdigest = hash_hexdigest(&h); | |
if (strcmp(hashdigest, hello_world_hash) == 0) | |
printf("Working SHA256 implementation!\n"); | |
free(hashdigest); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment