[SHA256 of security algorithm] C language source code implementation of SHA256 abstract operation

Keywords: C Algorithm security Information Security

summary

As we all know, the summary algorithm is also a particularly important existence in the security field, and SHA256 is one of the most common summary algorithms. Its characteristic is that it has low computational complexity. The equal length summary value can be obtained by inputting the original text of unequal length data, which is fixed at 32 bytes. Because of this particularity, SHA256 can be seen in many important data integrity verification fields. In some security authentication, the algorithm level of digest operation is at least equal to or greater than the security level of SHA256, which is enough to prove the importance of SHA256.
Today we bring you the C source code version of SHA256. Welcome to further study and discuss.

Header file definition

The header file is defined as follows. It mainly defines the context structure of SHA256 and the three exported API s:

#ifndef __SHA256_H__
#define __SHA256_H__

#include <stdint.h>

#define SHA256_DIGEST_LEN 32         	// SHA256 outputs a 32 byte digest

typedef uint8_t 	BYTE;             	// 8-bit byte
typedef uint32_t  	WORD;             	// 32-bit word, change to "long" for 16-bit machines

typedef struct _sha256_ctx_t {
	uint8_t 			data[64];
	uint32_t 			data_len;
	unsigned long long 	bit_len;
	uint32_t 			state[8];
} sha256_ctx_t;

void crypto_sha256_init(sha256_ctx_t *ctx);
void crypto_sha256_update(sha256_ctx_t *ctx, const uint8_t *data, uint32_t len);
void crypto_sha256_final(sha256_ctx_t *ctx, uint8_t *digest);

#endif   // __SHA256_H__

C language version of the implementation source code

The following is the C language version implementation of SHA256, which mainly focuses on the three API s exported:

#include <stdlib.h>
#include <string.h>

#include "sha256.h"

#define ROTLEFT(a,b)    (((a) << (b)) | ((a) >> (32-(b))))
#define ROTRIGHT(a,b)   (((a) >> (b)) | ((a) << (32-(b))))
#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))

static 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,
};

static void local_sha256_transform(sha256_ctx_t *ctx, const uint8_t *data)
{
	uint32_t a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];

	for (i = 0, j = 0; i < 16; ++i, j += 4) {
		m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]);
	}

	for ( ; i < 64; ++i) {
		m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16];
	}

	a = ctx->state[0];
	b = ctx->state[1];
	c = ctx->state[2];
	d = ctx->state[3];
	e = ctx->state[4];
	f = ctx->state[5];
	g = ctx->state[6];
	h = ctx->state[7];

	for (i = 0; i < 64; ++i) {
		t1 = h + EP1(e) + CH(e,f,g) + k[i] + m[i];
		t2 = EP0(a) + MAJ(a,b,c);
		h = g;
		g = f;
		f = e;
		e = d + t1;
		d = c;
		c = b;
		b = a;
		a = t1 + t2;
	}

	ctx->state[0] += a;
	ctx->state[1] += b;
	ctx->state[2] += c;
	ctx->state[3] += d;
	ctx->state[4] += e;
	ctx->state[5] += f;
	ctx->state[6] += g;
	ctx->state[7] += h;
}

void crypto_sha256_init(sha256_ctx_t *ctx)
{
	ctx->data_len = 0;
	ctx->bit_len  = 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;
}

void crypto_sha256_update(sha256_ctx_t *ctx, const uint8_t *data, uint32_t len)
{
	uint32_t i;

	for (i = 0; i < len; ++i) {
		ctx->data[ctx->data_len] = data[i];
		ctx->data_len++;
		if (ctx->data_len == 64) {
			local_sha256_transform(ctx, ctx->data);
			ctx->bit_len += 512;
			ctx->data_len = 0;
		}
	}
}

void crypto_sha256_final(sha256_ctx_t *ctx, uint8_t *digest)
{
	uint32_t i;

	i = ctx->data_len;

	// Pad whatever data is left in the buffer.
	if (ctx->data_len < 56) {
		ctx->data[i++] = 0x80;
		while (i < 56) {
			ctx->data[i++] = 0x00;
		}
	} else {
		ctx->data[i++] = 0x80;
		while (i < 64) {
			ctx->data[i++] = 0x00;
		}
		local_sha256_transform(ctx, ctx->data);
		memset(ctx->data, 0, 56);
	}

	// Append to the padding the total message's length in bits and transform.
	ctx->bit_len += ctx->data_len * 8;
	ctx->data[63] = ctx->bit_len;
	ctx->data[62] = ctx->bit_len >> 8;
	ctx->data[61] = ctx->bit_len >> 16;
	ctx->data[60] = ctx->bit_len >> 24;
	ctx->data[59] = ctx->bit_len >> 32;
	ctx->data[58] = ctx->bit_len >> 40;
	ctx->data[57] = ctx->bit_len >> 48;
	ctx->data[56] = ctx->bit_len >> 56;
	local_sha256_transform(ctx, ctx->data);

	// Since this implementation uses little endian byte ordering and SHA uses big endian,
	// reverse all the bytes when copying the final state to the output digest.
	for (i = 0; i < 4; ++i) {
		digest[i]      = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;
		digest[i + 4]  = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff;
		digest[i + 8]  = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff;
		digest[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff;
		digest[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff;
		digest[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff;
		digest[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff;
		digest[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff;
	}
}

test case

For the three interfaces exported by SHA256, I wrote the following test cases:

#include <stdio.h>
#include <string.h>

#include "sha256.h"
#include "convert.h"

int log_hexdump(const char *title, const unsigned char *data, int len)
{
    char str[160], octet[10];
    int ofs, i, k, d;
    const unsigned char *buf = (const unsigned char *)data;
    const char dimm[] = "+------------------------------------------------------------------------------+";

    printf("%s (%d bytes):\r\n", title, len);
    printf("%s\r\n", dimm);
    printf("| Offset  : 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F   0123456789ABCDEF |\r\n");
    printf("%s\r\n", dimm);

    for (ofs = 0; ofs < (int)len; ofs += 16) {
        d = snprintf( str, sizeof(str), "| %08X: ", ofs );

        for (i = 0; i < 16; i++) {
            if ((i + ofs) < (int)len) {
                snprintf( octet, sizeof(octet), "%02X ", buf[ofs + i] );
            } else {
                snprintf( octet, sizeof(octet), "   " );
            }

            d += snprintf( &str[d], sizeof(str) - d, "%s", octet );
        }
        d += snprintf( &str[d], sizeof(str) - d, "  " );
        k = d;

        for (i = 0; i < 16; i++) {
            if ((i + ofs) < (int)len) {
                str[k++] = (0x20 <= (buf[ofs + i]) &&  (buf[ofs + i]) <= 0x7E) ? buf[ofs + i] : '.';
            } else {
                str[k++] = ' ';
            }
        }

        str[k] = '\0';
        printf("%s |\r\n", str);
    }

    printf("%s\r\n", dimm);

    return 0;
}

int main(int argc, const char *argv[])
{
	const char *data = "C1D0F8FB4958670DBA40AB1F3752EF0D";
    const char *digest_exp_str = "97B7437DF061F15182974B18E62B3D8AAFE333DCBDD2074CB8D4916509B4AD23";
	uint8_t digest_calc[SHA256_DIGEST_LEN];
    uint8_t digest_exp_hex[SHA256_DIGEST_LEN];
	sha256_ctx_t ctx;
	const char *p_calc = data;
	uint8_t data_bytes[128];
	uint16_t len_bytes;
	char data_str[128];

	if (argc > 1) {
		p_calc = argv[1];
	}

	utils_hex_string_2_bytes(data, data_bytes, &len_bytes);
	log_hexdump("data_bytes", data_bytes, len_bytes);
	utils_bytes_2_hex_string(data_bytes, len_bytes, data_str);
	printf("data_str: %s\n", data_str);
	if (!strcmp(data, data_str)) {
		printf("hex string - bytes convert OK\n");
	} else {
		printf("hex string - bytes convert FAIL\n");
	}

	crypto_sha256_init(&ctx);
	crypto_sha256_update(&ctx, (uint8_t *)p_calc, strlen(p_calc));
	crypto_sha256_final(&ctx, digest_calc);

    utils_hex_string_2_bytes(digest_exp_str, digest_exp_hex, &len_bytes);
	if (len_bytes == sizeof(digest_calc) && !memcmp(digest_calc, digest_exp_hex, sizeof(digest_calc))) {
		printf("SHA256 digest test OK\n");
        log_hexdump("digest_calc", digest_calc, sizeof(digest_calc));
	} else {
		log_hexdump("digest_calc", digest_calc, sizeof(digest_calc));
		log_hexdump("digest_exp", digest_exp_hex, sizeof(digest_exp_hex));
		printf("SHA256 digest test FAIL\n");
	}

	return 0;
}

The test case is relatively simple. It is to perform SHA1 operation on the string C1D0F8FB4958670DBA40AB1F3752EF0D. The hexstring of the expected summary result is 97B7437DF061F15182974B18E62B3D8AAFE333DCBDD2074CB8D4916509B4AD23. This expected value is calculated by algorithm tools.
First use the API interface to calculate the summary value, and then compare it with the expected value. Here is a conversion of hexstring to byte. If the comparison is consistent, it means that the API calculation is OK; Otherwise, the interface calculation fails.
At the same time, you are also welcome to design and provide more test case codes.

github warehouse

For the above code, test cases, compilation and operation, please refer to my github warehouse , there is a detailed process introduction. Welcome to exchange and discuss. If it helps you, remember to help light up a star.

More reference links

[1] [github warehouse of security algorithm]
[2] [overview of security algorithms] this article gives you a brief understanding of common security algorithms
[3] [Base64 of security algorithm] C language source code implementation of base64 encryption and decryption
[4] [MD5 of security algorithm] C language source code implementation of MD5 abstract operation
[5] [SHA1 of security algorithm] C language source code implementation of SHA1 abstract operation
[6] [SHA224 of security algorithm] C language source code implementation of SHA224 abstract operation
[7] [SHA256 of security algorithm] C language source code implementation of SHA256 abstract operation
[8] [SHA384 of security algorithm] C language source code implementation of SHA384 abstract operation
[9] [SHA512 of security algorithm] C language source code implementation of SHA512 abstract operation

Posted by stuartc1 on Fri, 22 Oct 2021 20:04:40 -0700