commit ba577b46f19095c5c86057119e5b9e594f3f8e9b
parent 1a5b5acc814fbd49f9b090809615063960f4db96
Author: MikoĊaj Lenczewski <mikolaj@lenczewski.org>
Date: Wed, 16 Sep 2026 01:18:29 +0100
Add sha512 implementation
Diffstat:
| M | sha.c | | | 111 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- |
| M | sha.h | | | 7 | +++---- |
| A | test-sha512.c | | | 40 | ++++++++++++++++++++++++++++++++++++++++ |
3 files changed, 149 insertions(+), 9 deletions(-)
diff --git a/sha.c b/sha.c
@@ -95,12 +95,12 @@ _sha256_block(struct sha256_ctx *ctx)
for (t = 0; t < SHA256_SCHEDULE_WORDS; t++) {
t1 = h
+ SHA256_S1(e)
- + SHA256_Ch(e, f, g)
+ + SHA_Ch(e, f, g)
+ _sha256_constants[t]
+ ctx->schedule[t];
t2 = SHA256_S0(a)
- + SHA256_Maj(a, b, c);
+ + SHA_Maj(a, b, c);
h = g;
g = f;
@@ -199,6 +199,8 @@ sha256(void const *buf, size_t len, unsigned char digest[static SHA256_DIGEST_SI
void
sha512_init(struct sha512_ctx *ctx)
{
+ memset(ctx, 0, sizeof *ctx);
+
ctx->digest[0] = 0x6a09e667f3bcc908;
ctx->digest[1] = 0xbb67ae8584caa73b;
ctx->digest[2] = 0x3c6ef372fe94f82b;
@@ -210,25 +212,124 @@ sha512_init(struct sha512_ctx *ctx)
}
static inline void
-_sha512_block()
+_sha512_block(struct sha512_ctx *ctx)
{
+ sha512_word_t a, b, c, d, e, f, g, h, t, t1, t2;
+
+ // load block words
+ for (t = 0; t < SHA512_BLOCK_WORDS; t++) {
+ ctx->schedule[t] = htobe64(ctx->schedule[t]);
+ }
+
+ // expand out block words into full schedule
+ for (; t < SHA512_SCHEDULE_WORDS; t++) {
+ ctx->schedule[t] = SHA512_s1(ctx->schedule[t - 2])
+ + ctx->schedule[t - 7]
+ + SHA512_s0(ctx->schedule[t - 15])
+ + ctx->schedule[t - 16];
+ }
+
+ // initialise working variables
+ a = ctx->digest[0];
+ b = ctx->digest[1];
+ c = ctx->digest[2];
+ d = ctx->digest[3];
+ e = ctx->digest[4];
+ f = ctx->digest[5];
+ g = ctx->digest[6];
+ h = ctx->digest[7];
+
+ // perform rounds of sha512 compression (as many as words in our schedule)
+ for (t = 0; t < SHA512_SCHEDULE_WORDS; t++) {
+ t1 = h
+ + SHA512_S1(e)
+ + SHA_Ch(e, f, g)
+ + _sha512_constants[t]
+ + ctx->schedule[t];
+
+ t2 = SHA512_S0(a)
+ + SHA_Maj(a, b, c);
+
+ h = g;
+ g = f;
+ f = e;
+ e = d + t1;
+ d = c;
+ c = b;
+ b = a;
+ a = t1 + t2;
+ }
+
+ // save intermediate hash
+ ctx->digest[0] += a;
+ ctx->digest[1] += b;
+ ctx->digest[2] += c;
+ ctx->digest[3] += d;
+ ctx->digest[4] += e;
+ ctx->digest[5] += f;
+ ctx->digest[6] += g;
+ ctx->digest[7] += h;
}
void
sha512_push(struct sha512_ctx *ctx, void const *buf, size_t len)
{
+ unsigned char const *ptr = buf, *end = ptr + len;
+ unsigned char *block = ((unsigned char *) ctx->schedule);
+
+ while (ptr < end) {
+ size_t idx = ctx->ctr++ & SHA512_BLOCK_MASK;
+ block[idx] = *ptr++;
+
+ if (idx == SHA512_BLOCK_MASK) { // filled a block; hash it
+ _sha512_block(ctx);
+ }
+ }
}
static inline void
-_sha512_digest_bswap(struct sha512_ctx *ctx, size_t len)
+_sha512_digest_bswap(struct sha512_ctx *ctx, size_t words)
{
- for (size_t i = 0; i < len; i++)
+ for (size_t i = 0; i < words; i++)
ctx->digest[i] = htobe64(ctx->digest[i]);
}
void
sha512_dgst(struct sha512_ctx *ctx)
{
+ unsigned char *block = ((unsigned char *) ctx->schedule);
+ unsigned char *cur = block + (ctx->ctr & SHA512_BLOCK_MASK);
+ unsigned char *end = block + SHA512_BLOCK_SIZE;
+ unsigned char *len = end - (128 / CHAR_BIT);
+
+ // TODO: handle 128-bit overflow here
+ assert(ctx->ctr < (UINT64_MAX / CHAR_BIT));
+ __uint128_t bits = ctx->ctr * CHAR_BIT;
+
+ // append the 1 bit
+ *cur++ = 0x80;
+
+ // can we fit our message bit length? if not, add an extra padding block
+ if (len < cur) {
+ memset(cur, 0, end - cur);
+ _sha512_block(ctx);
+
+ cur = block;
+ }
+
+ // pad out final block
+ memset(cur, 0, len - cur);
+
+ // write out message bit length
+ __uint128_t hi = (__uint128_t) htobe64(bits & UINT64_MAX);
+ __uint128_t lo = (__uint128_t) htobe64(bits >> 64);
+ ((__uint128_t *) len)[0] = (hi << 64) | (lo);
+
+ // hash final block
+ _sha512_block(ctx);
+
+ // write out internally host-endian hash as big-endian hash
+ _sha512_digest_bswap(ctx, SHA512_DIGEST_WORDS);
}
void
diff --git a/sha.h b/sha.h
@@ -88,15 +88,14 @@ struct sha512_ctx {
// sha functions
// ===========================================================================
-#define SHA256_Ch(x, y, z) (((x) & (y)) ^ ((~(x)) & (z)))
-#define SHA256_Maj(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+#define SHA_Ch(x, y, z) (((x) & (y)) ^ ((~(x)) & (z)))
+#define SHA_Maj(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+
#define SHA256_S0(x) (SHA_ROTR32((x), 2) ^ SHA_ROTR32((x), 13) ^ SHA_ROTR32((x), 22))
#define SHA256_S1(x) (SHA_ROTR32((x), 6) ^ SHA_ROTR32((x), 11) ^ SHA_ROTR32((x), 25))
#define SHA256_s0(x) (SHA_ROTR32((x), 7) ^ SHA_ROTR32((x), 18) ^ SHA_SHR((x), 3))
#define SHA256_s1(x) (SHA_ROTR32((x), 17) ^ SHA_ROTR32((x), 19) ^ SHA_SHR((x), 10))
-#define SHA512_Ch(x, y, z) (((x) & (y)) ^ ((~(x)) & (z)))
-#define SHA512_Maj(x, y, z) (((x) ^ (y)) ^ ((x) & (z)) ^ ((y) & (z)))
#define SHA512_S0(x) (SHA_ROTR64((x), 28) ^ SHA_ROTR64((x), 34) ^ SHA_ROTR64((x), 39))
#define SHA512_S1(x) (SHA_ROTR64((x), 14) ^ SHA_ROTR64((x), 18) ^ SHA_ROTR64((x), 41))
#define SHA512_s0(x) (SHA_ROTR64((x), 1) ^ SHA_ROTR64((x), 8) ^ SHA_SHR((x), 7))
diff --git a/test-sha512.c b/test-sha512.c
@@ -0,0 +1,40 @@
+#include "sha.h"
+#include <stdio.h>
+
+int
+main(void)
+{
+ char msg[] = "abc";
+
+ uint8_t dgst[SHA512_DIGEST_SIZE];
+ sha512(msg, strlen(msg), dgst);
+
+ uint8_t expected[SHA512_DIGEST_SIZE] = {
+ 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba,
+ 0xcc, 0x41, 0x73, 0x49, 0xae, 0x20, 0x41, 0x31,
+ 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2,
+ 0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a,
+ 0x21, 0x92, 0x99, 0x2a, 0x27, 0x4f, 0xc1, 0xa8,
+ 0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd,
+ 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e,
+ 0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, 0x9f,
+ };
+
+ printf("msg: \"%.*s\" (%lu bytes)\n", sizeof msg, msg, strlen(msg));
+
+ printf("dgst: ");
+ for (size_t i = 0; i < sizeof dgst; i++) {
+ printf("%.2x", dgst[i]);
+ }
+ printf("\n");
+
+ printf("exp.: ");
+ for (size_t i = 0; i < sizeof expected; i++) {
+ printf("%.2x", expected[i]);
+ }
+ printf("\n");
+
+ return memcmp(expected, dgst, SHA512_DIGEST_SIZE);
+}
+
+#include "sha.c"