0% found this document useful (0 votes)
16 views

256-Hash Function

Hash functions
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
16 views

256-Hash Function

Hash functions
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 4

Step-by-Step Breakdown of the Code

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

uint64_t fnv1a_hash(const unsigned char *data, size_t len) {


uint64_t hash = FNV_OFFSET;
for (size_t i = 0; i < len; i++) {
hash ^= data[i]; // XOR with the current byte
hash *= FNV_PRIME; // Multiply by a large prime number
}
return hash;
}

• FNV_OFFSET: A large constant that serves as the initial hash value.


• FNV_PRIME: A prime number used in the multiplication step to ensure a good
distribution of the hash values.
• XOR with each byte: The current byte is XOR'd into the hash value (hash ^=
data[i]), mixing the input into the hash.
• Multiply by prime: After each XOR, the hash is multiplied by a prime to further
randomize the bits.

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.

a. Constants and Macros

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))

• ROTRIGHT: Performs a circular right bit-shift.


• CH and MAJ: Logical functions specific to SHA-256 for mixing bits of input.
• EP0, EP1, SIG0, and SIG1: Functions for rotating and shifting bits, as specified in the
SHA-256 algorithm.

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

This demonstrates the usage of the three hash functions:

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);

printf("SHA-256 hash: ");


for (int i = 0; i < SHA256_BLOCK_SIZE; i++) {
printf("%02x", hash[i]);
}
printf("\n");

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.

You might also like