feat: half working sha2

This commit is contained in:
cscherr 2025-07-17 12:25:11 +02:00
parent 009ed1fb50
commit 65690e7f43
Signed by: cscherrNT
GPG key ID: 8E2B45BC51A27EA7
16 changed files with 597 additions and 16 deletions

View file

@ -8,3 +8,5 @@
./src/hash/
-I
./src/
-I
./test/support/

View file

@ -2,7 +2,7 @@
:which_ceedling: gem
:ceedling_version: 1.0.1
:use_mocks: FALSE
:use_backtrace: :simple # options are :none, :simple, or :gdb
:use_backtrace: :gdb # options are :none, :simple, or :gdb
:use_decorators: :auto # decorate Ceedling's output text. options are :auto, :all, or :none
:build_root: build
:test_file_prefix: test_

View file

@ -1,6 +1,6 @@
#ifndef HASH_H
#define HASH_H
#include "sha2.h"
#endif // HASH_H

View file

@ -0,0 +1,431 @@
#include "sha2.h"
#include "trace.h"
#include <stddef.h>
#include <stdint.h>
#include <threads.h>
#define err_handler(RES) \
if (RES) { \
return RES; \
}
static uint32_t addTemp = 0;
#define sha2_256_add_length(context, length) \
(addTemp = (context)->length_low, \
(context)->corrupted = (((context)->length_low += (length)) < addTemp) && \
(++(context)->length_high == 0) \
? shaInputTooLong \
: (context)->corrupted)
/* Define the SHA shift, rotate left, and rotate right macros */
#define SHA256_SHR(bits, word) ((word) >> (bits))
#define SHA256_ROTL(bits, word) (((word) << (bits)) | ((word) >> (32 - (bits))))
#define SHA256_ROTR(bits, word) (((word) >> (bits)) | ((word) << (32 - (bits))))
/* Define the SHA SIGMA and sigma macros */
#define SHA256_SIGMA0(word) \
(SHA256_ROTR(2, word) ^ SHA256_ROTR(13, word) ^ SHA256_ROTR(22, word))
#define SHA256_SIGMA1(word) \
(SHA256_ROTR(6, word) ^ SHA256_ROTR(11, word) ^ SHA256_ROTR(25, word))
#define SHA256_sigma0(word) \
(SHA256_ROTR(7, word) ^ SHA256_ROTR(18, word) ^ SHA256_SHR(3, word))
#define SHA256_sigma1(word) \
(SHA256_ROTR(17, word) ^ SHA256_ROTR(19, word) ^ SHA256_SHR(10, word))
#define SHA_Ch(x, y, z) (((x) & ((y) ^ (z))) ^ (z))
#define SHA_Maj(x, y, z) (((x) & ((y) | (z))) | ((y) & (z)))
#define sha2_256_show_internal_state(context) \
TRACE("Internal State:\n\tcorrupted: %d\n\tintermediate_hash: ", \
context->corrupted); \
dump_data((uint8_t *)context->intermediate_hash, SHA2_256_HashBytes); \
TRACE("\tmessage_block_index: %d\n\tmessage_block: ", \
context->message_block_index); \
dump_data((uint8_t *)context->message_block, SHA2_256_BlockSize); \
TRACE("\tcorrupted: %d\n\tcomputed: %d\n", context->corrupted, \
context->computed);
static const uint32_t H[SHA2_256_HashParts] = {
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19};
static const uint32_t K[SHA2_256_BlockSize] = {
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};
/*
* Description:
* This helper function will process the next 512 bits of the
* message stored in the message_block array.
*
* Parameters:
* context: [in/out]
* The SHA context to update.
*
* Returns:
* Nothing.
*
* Comments:
* Many of the variable names in this code, especially the
* single character names, were used because those were the
* names used in the Secure Hash Standard.
*/
static void sha2_256_process_message_block(SHA2Context *context) {
#define dump_letter(X) TRACE("%s: %08x\n", STR(X), X)
#define dump_alphabet(A, B, C, D, E, F, G, H) \
dump_letter(A) dump_letter(B) dump_letter(C) dump_letter(D) dump_letter(E) \
dump_letter(F) dump_letter(G) dump_letter(H)
TRACELN("before process message block");
sha2_256_show_internal_state(context);
uint8_t t, t4; /* Loop counter */
uint32_t temp1, temp2; /* Temporary word value */
uint32_t W[SHA2_256_BlockSize]; /* Word sequence */
uint32_t A, B, C, D, E, F, G, H; /* Word buffers */
/*
* Initialize the first 16 words in the array W
*/
for (t = t4 = 0; t < 16; t++, t4 += 4) {
W[t] = (((uint32_t)context->message_block[t4]) << 24) |
(((uint32_t)context->message_block[t4 + 1]) << 16) |
(((uint32_t)context->message_block[t4 + 2]) << 8) |
(((uint32_t)context->message_block[t4 + 3]));
}
for (t = 16; t < SHA2_256_BlockSize; t++) {
W[t] = SHA256_sigma1(W[t - 2]) + W[t - 7] + SHA256_sigma0(W[t - 15]) +
W[t - 16];
}
A = context->intermediate_hash[0];
B = context->intermediate_hash[1];
C = context->intermediate_hash[2];
D = context->intermediate_hash[3];
E = context->intermediate_hash[4];
F = context->intermediate_hash[5];
G = context->intermediate_hash[6];
H = context->intermediate_hash[7];
for (t = 0; t < SHA2_256_BlockSize; t++) {
temp1 = H + SHA256_SIGMA1(E) + SHA_Ch(E, F, G) + K[t] + W[t];
temp2 = SHA256_SIGMA0(A) + SHA_Maj(A, B, C);
H = G;
G = F;
F = E;
E = D + temp1;
D = C;
C = B;
B = A;
A = temp1 + temp2;
}
context->intermediate_hash[0] += A;
context->intermediate_hash[1] += B;
context->intermediate_hash[2] += C;
context->intermediate_hash[3] += D;
context->intermediate_hash[4] += E;
context->intermediate_hash[5] += F;
context->intermediate_hash[6] += G;
context->intermediate_hash[7] += H;
context->message_block_index = 0;
TRACELN("after process message block");
sha2_256_show_internal_state(context);
}
/*
* Description:
* According to the standard, the message must be padded to the next
* even multiple of 512 bits. The first padding bit must be a '1'.
* The last 64 bits represent the length of the original message.
* All bits in between should be 0. This helper function will pad
* the message according to those rules by filling the
* Message_Block array accordingly. When it returns, it can be
* assumed that the message digest has been computed.
*
* Parameters:
* context: [in/out]
* The context to pad.
* Pad_Byte: [in]
* The last byte to add to the message block before the 0-padding
* and length. This will contain the last bits of the message
* followed by another single bit. If the message was an
* exact multiple of 8-bits long, Pad_Byte will be 0x80.
*
* Returns:
* Nothing.
*/
static void sha2_256_pad_message(SHA2Context *context, uint8_t pad_byte) {
TRACELN("pad message");
/*
* Check to see if the current message block is too small to hold
* the initial padding bits and length. If so, we will pad the
* block, process it, and then continue padding into a second
* block.
*/
if (context->message_block_index >= (SHA2_256_BlockSize - 8)) {
context->message_block[context->message_block_index++] = pad_byte;
while (context->message_block_index < SHA2_256_BlockSize)
context->message_block[context->message_block_index++] = 0;
sha2_256_process_message_block(context);
} else
context->message_block[context->message_block_index++] = pad_byte;
while (context->message_block_index < (SHA2_256_BlockSize - 8))
context->message_block[context->message_block_index++] = 0;
/*
* store the message length as the last 8 octets
*/
context->message_block[56] = (uint8_t)(context->length_high >> 24);
context->message_block[57] = (uint8_t)(context->length_high >> 16);
context->message_block[58] = (uint8_t)(context->length_high >> 8);
context->message_block[59] = (uint8_t)(context->length_high);
context->message_block[60] = (uint8_t)(context->length_low >> 24);
context->message_block[61] = (uint8_t)(context->length_low >> 16);
context->message_block[62] = (uint8_t)(context->length_low >> 8);
context->message_block[63] = (uint8_t)(context->length_low);
sha2_256_process_message_block(context);
TRACELN("After pad message:");
sha2_256_show_internal_state(context);
}
static void sha2_256_finalize(SHA2Context *context, uint8_t pad_byte) {
size_t i;
sha2_256_pad_message(context, pad_byte);
/* message may be sensitive, so clear it out */
for (i = 0; i < SHA2_256_BlockSize; ++i)
context->message_block[i] = 0;
context->length_high = 0; /* and clear length */
context->length_low = 0;
context->computed = true;
}
/*
* SHA256FinalBits
*
* Description:
* This function will add in any final bits of the message.
*
* Parameters:
* context: [in/out]
* The SHA context to update.
* message_bits: [in]
* The final bits of the message, in the upper portion of the
* byte. (Use 0b###00000 instead of 0b00000### to input the
* three bits ###.)
* length: [in]
* The number of bits in message_bits, between 1 and 7.
*
* Returns:
* sha Error Code.
*/
static SHA2Result sha2_256_final_bits(SHA2Context *context,
uint8_t message_bits, size_t len) {
static uint8_t masks[8] = {
/* 0 0b00000000 */ 0x00, /* 1 0b10000000 */ 0x80,
/* 2 0b11000000 */ 0xC0, /* 3 0b11100000 */ 0xE0,
/* 4 0b11110000 */ 0xF0, /* 5 0b11111000 */ 0xF8,
/* 6 0b11111100 */ 0xFC, /* 7 0b11111110 */ 0xFE};
static uint8_t markbit[8] = {
/* 0 0b10000000 */ 0x80, /* 1 0b01000000 */ 0x40,
/* 2 0b00100000 */ 0x20, /* 3 0b00010000 */ 0x10,
/* 4 0b00001000 */ 0x08, /* 5 0b00000100 */ 0x04,
/* 6 0b00000010 */ 0x02, /* 7 0b00000001 */ 0x01};
if (!context)
return shaNull;
if (!len)
return shaSuccess;
if (context->corrupted)
return context->corrupted;
if (context->computed)
return context->corrupted = shaStateError;
if (len >= 8)
return context->corrupted = shaBadArg;
sha2_256_add_length(context, len);
sha2_256_finalize(context,
(uint8_t)((message_bits & masks[len]) | markbit[len]));
return context->corrupted;
}
/*
* sha2_reset
*
* Description:
* This function will initialize the SHA2Context in preparation
* for computing a new SHA2 message digest.
*
* Parameters:
* context: [in/out]
* The context to reset.
*
* Returns:
* SHA2 Error Code.
*
*/
SHA2Result sha2_256_reset(SHA2Context *context) {
size_t i;
if (!context) {
return shaNull;
}
context->length_low = 0;
context->length_high = 0;
context->message_block_index = 0;
for (i = 0; i < SHA2_256_BlockSize; i++) {
context->message_block[i] = 0;
}
for (i = 0; i < SHA2_256_HashParts; i++) {
context->intermediate_hash[i] = H[i];
}
context->corrupted = 0;
context->computed = 0;
return shaSuccess;
}
/*
* sha2_input
*
* Description:
* This function accepts an array of bytes as the next portion
* of the message.
*
* Parameters:
* context: [in/out]
* The SHA context to update
* data: [in]
* An array of characters representing the next portion of
* the message.
* length: [in]
* The length of the message in message_array
*
* Returns:
* sha Error Code.
*
*/
SHA2Result sha2_256_input(SHA2Context *context, const uint8_t data[],
size_t len) {
TRACE("Processing input: ");
dump_data(data, len);
sha2_256_show_internal_state(context);
if (!context) {
TRACELN("Context was NULL");
return shaNull;
}
if (!len)
return shaSuccess;
if (!data) {
TRACELN("Data was NULL");
return shaNull;
}
if (context->computed) {
TRACELN("context was already computed");
context->corrupted = shaStateError;
}
if (context->corrupted) {
TRACELN("context is corrupted");
return context->corrupted;
}
while (len--) {
context->message_block[context->message_block_index++] = *data;
if ((sha2_256_add_length(context, 8) == shaSuccess) &&
(context->message_block_index == SHA2_256_BlockSize)) {
TRACELN("processing the message block after inputting")
sha2_256_process_message_block(context);
} else {
TRACELN("not processing the message block");
}
data++;
}
return context->corrupted;
}
/*
* sha2_result
*
* Description:
* This function will return the 256-bit message digest
* into the digest array provided by the caller.
* NOTE:
* The first octet of hash is stored in the element with index 0,
* the last octet of hash in the element with index 31.
*
* Parameters:
* context: [in/out]
* The context to use to calculate the SHA hash.
* digest: [out]
* Where the digest is returned.
*
* Returns:
* sha Error Code.
*
*/
SHA2Result sha2_256_result(SHA2Context *context, SHA2Digest digest) {
size_t i;
if (!context)
return shaNull;
if (!digest)
return shaNull;
if (context->corrupted)
return context->corrupted;
if (!context->computed) {
sha2_256_finalize(context, 0x80);
}
for (i = 0; i < SHA2_256_HashParts; i++)
digest[i] = context->intermediate_hash[i];
return shaSuccess;
}
SHA2Result sha2_256_oneshot(const uint8_t *data, const size_t len,
SHA2Digest digest) {
SHA2Context context;
SHA2Result res;
TRACELN("SHA2 reset");
res = sha2_256_reset(&context);
err_handler(res);
sha2_256_show_internal_state((&context));
TRACELN("SHA2 input");
res = sha2_256_input(&context, data, len);
err_handler(res);
sha2_256_show_internal_state((&context));
TRACELN("SHA2 result");
res = sha2_256_result(&context, digest);
err_handler(res);
sha2_256_show_internal_state((&context));
TRACELN("SHA2 return");
return res;
}

View file

@ -0,0 +1,52 @@
/*
* This implementation of SHA2 is somewhat similar to that in RFC6234, thanks
* for publishing that!
*/
#ifndef SHA2_H
#define SHA2_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#define SHA2_256_HashBits 256
#define SHA2_256_HashBytes SHA2_256_HashBits / 8
#define SHA2_256_HashParts SHA2_256_HashBytes / 4
#define SHA2_256_BlockSize 2 * SHA2_256_HashBytes
typedef enum {
shaSuccess = 0,
shaNull, /* Null pointer parameter */
shaInputTooLong, /* input data too long */
shaStateError, /* called Input after Result */
shaBadArg /* Bad argument was given to a function */
} SHA2Result;
typedef uint32_t SHA2Digest[SHA2_256_HashParts];
/*
* This structure will hold context information for the SHA-1
* hashing operation
*/
typedef struct SHA2Context {
SHA2Digest intermediate_hash; /* Message Digest */
uint32_t length_low; /* Message length in bits */
uint32_t length_high; /* Message length in bits */
/* Index into message block array */
int_least16_t message_block_index;
uint8_t message_block[SHA2_256_BlockSize]; /* 512-bit message blocks */
bool computed; /* Is the digest computed? */
bool corrupted; /* Is the message digest corrupted? */
} SHA2Context;
/** data should always be null terminated */
extern SHA2Result sha2_256_oneshot(const uint8_t *data, const size_t len,
SHA2Digest digest);
extern SHA2Result sha2_256_reset(SHA2Context *context);
extern SHA2Result sha2_256_input(SHA2Context *context, const uint8_t data[],
size_t len);
extern SHA2Result sha2_256_result(SHA2Context *context, SHA2Digest digest);
#endif // !SHA2_H

View file

@ -0,0 +1,12 @@
#include "trace.h"
#ifdef TEST
void dump_data(const uint8_t *data, size_t len) {
for (size_t i = 0; i < len; i++) {
TRACE("%02x", data[i])
}
TRACELN("")
}
#endif // TEST

View file

@ -0,0 +1,27 @@
#ifndef TRACE_H
#define TRACE_H
#define STR(a) #a
#ifdef TEST
#include "stdio.h"
#include <stddef.h>
#include <stdint.h>
#define TRACE(...) fprintf(stderr, __VA_ARGS__);
#define TRACELN(MSG) TRACE(MSG "\n")
extern void dump_data(const uint8_t *data, size_t len);
#endif // TEST
// just do nothing if TEST is not defined
#ifndef TEST
#define TRACE(...)
#define TRACELN(MSG)
#define dump_data(DATA_PTR, LEN)
#endif // !TEST
//
#endif // !TRACE_H

View file

@ -1,2 +1,3 @@
#include "crc_common.h"
const char CRC_CHECK_DATA[CRC_CHECK_DATA_LEN] = {"123456789"};
const char CRC_CHECK_DATA[CRC_CHECK_DATA_LEN] = "123456789";

View file

@ -8,7 +8,7 @@ void setUp(void) {}
void tearDown(void) {}
void test_crc32_check(void) {
TEST_ASSERT_EQUAL_INT(crc32_checksum(CRC_CHECK_DATA, CRC_CHECK_DATA_LEN),
TEST_ASSERT_EQUAL_HEX(crc32_checksum(CRC_CHECK_DATA, CRC_CHECK_DATA_LEN),
0xCBF43926);
TEST_ASSERT_EQUAL_INT(crc32_checksum("Hello World", 11), 1243066710);
TEST_ASSERT_EQUAL_HEX(crc32_checksum("Hello World", 11), 0x4a17b156);
}

View file

@ -1,11 +0,0 @@
#include "unity.h"
#include "hash.h"
void setUp(void) {}
void tearDown(void) {}
void test_hash_NeedToImplement(void) {
TEST_IGNORE_MESSAGE("Need to Implement hash");
}

View file

@ -0,0 +1,60 @@
#include "sha2.h"
#include "trace.h"
#include "unity.h"
#include <stddef.h>
#include <stdint.h>
#include <string.h>
void setUp(void) {}
void tearDown(void) {}
#define HEXARR(...) \
(uint8_t[SHA2_256_HashBytes]) { __VA_ARGS__ }
// BUG: may drop the null at end of strings
#define SAMPLE(INPUT, ...) {(uint8_t[]){INPUT "\0"}, HEXARR(__VA_ARGS__)}
#define TEST_VALUES_LEN 3
uint8_t *test_values[TEST_VALUES_LEN][2] = {
SAMPLE("AAAA", 0x63, 0xc1, 0xdd, 0x95, 0x1f, 0xfe, 0xdf, 0x6f, 0x7f, 0xd9,
0x68, 0xad, 0x4e, 0xfa, 0x39, 0xb8, 0xed, 0x58, 0x4f, 0x16, 0x2f,
0x46, 0xe7, 0x15, 0x11, 0x4e, 0xe1, 0x84, 0xf8, 0xde, 0x92, 0x01, ),
SAMPLE("", 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4,
0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b,
0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55, ),
SAMPLE("BAAA", 0x49, 0xe3, 0xcd, 0x45, 0x27, 0xc9, 0x6c, 0xdc, 0x01, 0x01,
0x60, 0xff, 0x08, 0x52, 0x0e, 0x0c, 0xb6, 0x3c, 0x6e, 0xf8, 0xc4,
0xe7, 0xd4, 0x86, 0x08, 0x99, 0x53, 0x43, 0x7f, 0x83, 0xa1, 0x59, )};
void test_sha2_check(void) {
SHA2Digest digest;
SHA2Result res;
uint8_t *input;
size_t len;
size_t i;
for (i = 0; i < TEST_VALUES_LEN; i++) {
TRACE("Hash iteration %d\n", i);
input = test_values[i][0];
len = strlen((char *)input);
TRACELN("Input:");
dump_data(input, len);
TRACELN("Hash oneshot");
res = sha2_256_oneshot(input, len, digest);
TEST_ASSERT_EQUAL_MESSAGE(
shaSuccess, res,
"sha2_256_oneshot exited with a code that is not shaSuccess");
// NOTE: If it segfaults here, then the sha2_oneshot function is at fault
// anyways!!!!!!!!!!!!
TRACELN("Hash should be:");
dump_data(test_values[i][1], SHA2_256_HashBytes);
TRACELN("Hash is:");
dump_data((uint8_t *)digest, SHA2_256_HashBytes);
TEST_ASSERT_EQUAL_MEMORY(test_values[i][1], digest, SHA2_256_HashBytes);
}
TRACELN("ALL TESTS DONE")
}

View file

@ -0,0 +1,2 @@
mod sha1;
pub use sha1::*;

View file

View file

@ -0,0 +1,4 @@
pub mod ffi;
mod sha1;
pub use sha1::*;

View file

View file

@ -10,6 +10,7 @@
#![cfg_attr(not(test), no_std)]
pub mod crc;
pub mod hash;
pub(crate) mod ffi;
pub use ffi::ffi_is_loaded;