256-Hash Function
256-Hash Function
This code consists of several hash functions, including a simple one (djb2), a slightly more
complex one (FNV-1a), and the cryptographically secure SHA-256 implementation. I'll go
through each section in detail:
1. djb2_hash Function
This is a simple hash function developed by Daniel J. Bernstein. The purpose of this function
is to take a string and return a 32-bit hash.
c
Copy code
unsigned long djb2_hash(const unsigned char *str) {
unsigned long hash = 5381;
int c;
while ((c = *str++))
hash = ((hash << 5) + hash) + c; // hash * 33 + c
return hash;
}
• Initial value (5381): The hash variable starts with a constant seed value of 5381,
which is empirically chosen for its performance and distribution characteristics.
• Loop through each character (while ((c = *str++)): The string is processed
character by character, updating the hash.
• Hash computation (hash * 33 + c): The hash is updated by multiplying it by 33
and adding the current character (c). The bit-shifting (hash << 5) is a faster way of
doing multiplication by 32.
2. fnv1a_hash Function
FNV-1a is a non-cryptographic hash function that processes arbitrary data, such as byte
arrays. It uses a combination of XOR and multiplication for good distribution.
c
Copy code
#define FNV_PRIME 0x100000001b3
#define FNV_OFFSET 0xcbf29ce484222325
3. SHA-256 Implementation
This section implements the cryptographic hash function SHA-256. It is more complex and
follows the official specification with careful bitwise operations to achieve security.
SHA-256 relies heavily on bitwise operations and logical functions. These macros simplify
the implementation:
c
Copy code
#define ROTRIGHT(word,bits) (((word) >> (bits)) | ((word) << (32-(bits))))
#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z)))
#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22))
#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25))
#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3))
#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10))
b. SHA256_CTX Structure
This structure maintains the state of the SHA-256 computation across different stages:
c
Copy code
typedef struct {
uint8_t data[64]; // Data buffer (512 bits)
uint32_t datalen; // Length of the current data
uint64_t bitlen; // Total number of bits processed
uint32_t state[8]; // Intermediate hash value (256 bits)
} SHA256_CTX;
• data: A buffer to hold blocks of 512 bits (64 bytes) for processing.
• datalen: Keeps track of how much data is currently in the buffer.
• bitlen: A counter for the total number of bits processed so far.
• state: An array of 8 integers (each 32 bits) that holds the current intermediate hash
value.
c. sha256_transform Function
This function processes a 512-bit chunk of data (64 bytes) and updates the hash state:
c
Copy code
void sha256_transform(SHA256_CTX *ctx, const uint8_t data[]) {
// ... (complex operations on the state)
}
• Input scheduling: First, the input is expanded from 16 32-bit words to 64 words
using the SIG0 and SIG1 functions.
• Main loop: The core of the function is a loop that processes each of the 64 rounds of
the SHA-256 algorithm. In each round, the state is updated using logical functions
like CH, MAJ, EP0, and EP1.
• Final state update: After processing the block, the state is updated by adding the
values computed in the loop to the current state.
d. sha256_init Function
This initializes the SHA-256 context with predefined initial hash values:
c
Copy code
void sha256_init(SHA256_CTX *ctx) {
ctx->datalen = 0;
ctx->bitlen = 0;
ctx->state[0] = 0x6a09e667;
ctx->state[1] = 0xbb67ae85;
ctx->state[2] = 0x3c6ef372;
ctx->state[3] = 0xa54ff53a;
ctx->state[4] = 0x510e527f;
ctx->state[5] = 0x9b05688c;
ctx->state[6] = 0x1f83d9ab;
ctx->state[7] = 0x5be0cd19;
}
• These constants are defined in the SHA-256 standard as the initial hash values.
e. sha256_update Function
This processes the input data in chunks of 512 bits (64 bytes). If the data buffer is full, the
function calls sha256_transform to process it.
c
Copy code
void sha256_update(SHA256_CTX *ctx, const uint8_t data[], size_t len) {
// Handles filling the buffer and calling sha256_transform when full
}
f. sha256_final Function
This function pads the final block of data and processes any remaining bytes to finalize the
hash.
c
Copy code
void sha256_final(SHA256_CTX *ctx, uint8_t hash[]) {
// Padding and final transformation
}
• Padding: Ensures the data is padded to the correct length according to SHA-256
specifications, adding a 1 bit followed by enough zeros to fill out the block.
• Final hash value: The final 256-bit hash is written to the hash[] array.
4. main Function
c
Copy code
int main() {
const char *input = "Hello, PhD candidate!";
// djb2 hash
printf("djb2 hash: %lu\n", djb2_hash((const unsigned char *)input));
// FNV-1a hash
printf("FNV-1a hash: %llu\n", fnv1a_hash((const unsigned char *)input,
strlen(input)));
// SHA-256 hash
SHA256_CTX ctx;
uint8_t hash[SHA256_BLOCK_SIZE];
sha256_init(&ctx);
sha256_update(&ctx, (const uint8_t *)input, strlen(input));
sha256_final(&ctx, hash);
return 0;
}
• Input: The string "Hello, PhD candidate!" is hashed using each function.
• djb2: Simple, fast hash function.
• FNV-1a: Slightly more complex but still non-cryptographic.
• SHA-256: Secure, cryptographic hash function providing a 256-bit result.
Conclusion
This code provides an excellent demonstration of different types of hash functions, from
simple to cryptographically secure. Each function has its purpose, and the SHA-256
implementation illustrates a complete cryptographic hash function designed for security.