diff --git a/.gitignore b/.gitignore index e4e5f6c..6587b2f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -*~ \ No newline at end of file +*~ +*.o +test diff --git a/Makefile b/Makefile index dc1b47a..fd721b2 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,5 @@ +CFLAGS = -Wall -Wextra -Wpedantic + test: test.o sha-256.o .PHONY: clean diff --git a/README.md b/README.md index c0cfd7f..966db6e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,27 @@ # sha-2 -SHA-2 algorithm implementations + +## Contents + +SHA-2 algorithm implementations. + +At the moment, only a SHA-256 is implemented. + +## Design criteria + +- Easy to test, include in any project, compile and link. + +- ANSI C with as little specific C99 as possible (e.g. extended + integer types are used, but not bool). + +- Portable. Makes no assumptions on the target system's endianess or + word size. + +- The SHA-256 implementation is a straightforward implementation of + the algorithm specified on + [Wikipedia](https://en.wikipedia.org/wiki/SHA-2). At the moment, + no effort at all has been put in optimization. + +## License + +This repository is made available in the public domain. See [LICENSE +FILE](LICENSE). diff --git a/sha-256.c b/sha-256.c index 852ff30..26c7698 100644 --- a/sha-256.c +++ b/sha-256.c @@ -71,6 +71,8 @@ static int calc_chunk(uint8_t chunk[CHUNK_SIZE], struct buffer_state * state) memcpy(chunk, state->p, state->len); chunk += state->len; space_in_chunk = CHUNK_SIZE - state->len; + state->p += state->len; + state->len = 0; /* If we are here, space_in_chunk is one at minimum. */ if (!state->single_one_delivered) { @@ -83,21 +85,39 @@ static int calc_chunk(uint8_t chunk[CHUNK_SIZE], struct buffer_state * state) * Now: * - either there is enough space left for the total length, and we can conclude, * - or there is too little space left, and we have to pad the rest of this chunk with zeroes. - * In the latter case, we will conclude at the next pass. + * In the latter case, we will conclude at the next invokation of this function. */ if (space_in_chunk >= TOTAL_LEN_LEN) { const size_t left = space_in_chunk - TOTAL_LEN_LEN; - const size_t len = state->total_len; + const size_t len = state->total_len * 8; memset(chunk, 0x00, left); chunk += left; - chunk[0] = (uint8_t) (len >> 56); - chunk[1] = (uint8_t) (len >> 48); - chunk[2] = (uint8_t) (len >> 40); - chunk[3] = (uint8_t) (len >> 32); - chunk[4] = (uint8_t) (len >> 24); - chunk[5] = (uint8_t) (len >> 16); - chunk[6] = (uint8_t) (len >> 8); + if (sizeof len > 4) { + chunk[0] = (uint8_t) (len >> 56); + chunk[1] = (uint8_t) (len >> 48); + chunk[2] = (uint8_t) (len >> 40); + chunk[3] = (uint8_t) (len >> 32); + } else { + chunk[0] = 0; + chunk[1] = 0; + chunk[2] = 0; + chunk[3] = 0; + } + if (sizeof len > 2) { + chunk[4] = (uint8_t) (len >> 24); + chunk[5] = (uint8_t) (len >> 16); + } else { + chunk[4] = 0; + chunk[5] = 0; + } + + if (sizeof len > 1) { + chunk[6] = (uint8_t) (len >> 8); + } else { + chunk[6] = 0; + } + chunk[7] = (uint8_t) len; state->total_len_delivered = 1; @@ -108,6 +128,13 @@ static int calc_chunk(uint8_t chunk[CHUNK_SIZE], struct buffer_state * state) return 1; } +/* + * Limitations: + * - len must be small enough for (8 * len) to fit in len. Otherwise, the results are unpredictable. + * - sizeof size_t is assumed to be either 8, 16, 32 or 64. Otherwise, the results are unpredictable. + * - Since input is a pointer in RAM, the data to hash should be in RAM, which could be a problem + * for large data sizes. + */ void calc_sha_256(uint8_t hash[32], const void * input, size_t len) { /* @@ -161,8 +188,8 @@ void calc_sha_256(uint8_t hash[32], const void * input, size_t len) /* Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array: */ for (int i = 16; i < 64; i++) { - const uint32_t s0 = right_rot(w[i - 15], 7) ^ right_rot(w[i - 15], 18) ^ w[i - 15] >> 3; - const uint32_t s1 = right_rot(w[i - 2], 17) ^ right_rot(w[i - 2], 19) ^ w[i - 2] >> 10; + const uint32_t s0 = right_rot(w[i - 15], 7) ^ right_rot(w[i - 15], 18) ^ (w[i - 15] >> 3); + const uint32_t s1 = right_rot(w[i - 2], 17) ^ right_rot(w[i - 2], 19) ^ (w[i - 2] >> 10); w[i] = w[i - 16] + s0 + w[i - 7] + s1; } @@ -179,10 +206,10 @@ void calc_sha_256(uint8_t hash[32], const void * input, size_t len) /* Compression function main loop: */ for (int i = 0; i < 64; i++) { const uint32_t s1 = right_rot(e, 6) ^ right_rot(e, 11) ^ right_rot(e, 25); - const uint32_t ch = e & f ^ ~e & g; + const uint32_t ch = (e & f) ^ (~e & g); const uint32_t temp1 = h + s1 + ch + k[i] + w[i]; - const uint32_t s0 = right_rot(a, 2) ^right_rot(a, 13) ^ right_rot(a, 22); - const uint32_t maj = a & b ^ a & c ^ b & c; + const uint32_t s0 = right_rot(a, 2) ^ right_rot(a, 13) ^ right_rot(a, 22); + const uint32_t maj = (a & b) ^ (a & c) ^ (b & c); const uint32_t temp2 = s0 + maj; h = g; diff --git a/test.c b/test.c index c4f843a..5acec4c 100644 --- a/test.c +++ b/test.c @@ -1,14 +1,75 @@ #include #include +#include #include "sha-256.h" -int main() +struct vector { + const char *input; + const char *output; +}; + +static const struct vector VECTORS[] = { + { + "", + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "abc", + "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" + }, + { + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", + "a8ae6e6ee929abea3afcfc5258c8ccd6f85273e0d4626d26c7279f3250f77c8e" + }, + { + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde", + "057ee79ece0b9a849552ab8d3c335fe9a5f1c46ef5f1d9b190c295728628299c" + }, + { + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0", + "2a6ad82f3620d3ebe9d678c812ae12312699d673240d5be8fac0910a70000d93" + }, + { + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1" + }, + { + "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoi" + "jklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", + "cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1" + } +}; + +static void hash_to_string(char string[65], const uint8_t hash[32]) +{ + for (size_t i = 0; i < 32; i++) { + string += sprintf(string, "%02x", hash[i]); + } +} + +static void test(const char input[], const char output[]) { uint8_t hash[32]; - calc_sha_256(hash, "hello\n", 6); - for (size_t i = 0; i < sizeof hash; i++) { - printf("%02x", hash[i]); - } - printf("\n"); + char hash_string[65]; + calc_sha_256(hash, input, strlen(input)); + hash_to_string(hash_string, hash); + printf("input: %s\n", input); + printf("hash : %s\n", hash_string); + if (strcmp(output, hash_string)) { + printf("FAILURE!\n\n"); + } else { + printf("SUCCESS!\n\n"); + } +} + + +int main(void) +{ + printf("%zu\n", sizeof (size_t)); + for (size_t i = 0; i < (sizeof VECTORS / sizeof (struct vector)); i++) { + const struct vector *vector = &VECTORS[i]; + test(vector->input, vector->output); + } + return 0; } diff --git a/tmp.txt b/tmp.txt deleted file mode 100644 index ce01362..0000000 --- a/tmp.txt +++ /dev/null @@ -1 +0,0 @@ -hello