From 4cf8b35097a131abcfc8e0d04d35294be13943ac Mon Sep 17 00:00:00 2001 From: bigfoot547 Date: Tue, 6 Jan 2026 03:27:12 -0600 Subject: initial commit --- lib/sha1.c | 237 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 237 insertions(+) create mode 100644 lib/sha1.c (limited to 'lib/sha1.c') diff --git a/lib/sha1.c b/lib/sha1.c new file mode 100644 index 0000000..ed456c8 --- /dev/null +++ b/lib/sha1.c @@ -0,0 +1,237 @@ +#include "sha1.h" +#include + +/* for byteswap functions (TODO: avoid GNU extensions) */ +#include + +/* code adapted from https://git.figboot.dev/l2su/tree/src/digest/sha1.c */ + +#define SHA1_H0 UINT32_C(0x67452301) +#define SHA1_H1 UINT32_C(0xEFCDAB89) +#define SHA1_H2 UINT32_C(0x98BADCFE) +#define SHA1_H3 UINT32_C(0x10325476) +#define SHA1_H4 UINT32_C(0xC3D2E1F0) + +#define SHA1_K0 UINT32_C(0x5A827999) +#define SHA1_K1 UINT32_C(0x6ED9EBA1) +#define SHA1_K2 UINT32_C(0x8F1BBCDC) +#define SHA1_K3 UINT32_C(0xCA62C1D6) + +void vl_sha1_init(vl_sha1_st *st) +{ + st->vl_state[0] = SHA1_H0; + st->vl_state[1] = SHA1_H1; + st->vl_state[2] = SHA1_H2; + st->vl_state[3] = SHA1_H3; + st->vl_state[4] = SHA1_H4; + + memset(st->vl_chunk, 0, VL_SHA1_CHUNKLEN_BYTES); + st->vl_nchunk = 0; + st->vl_total = 0; +} + +static uint32_t rol(uint32_t in, uint32_t v) +{ + return (in << v) | (in >> (32 - v)); +} + +static void sha1_upd(vl_sha1_st *st) +{ + uint32_t w[80]; + uint32_t a, b, c, d, e, f, k, temp; + unsigned i; + + memcpy(w, st->vl_chunk, VL_SHA1_CHUNKLEN_BYTES); + for (i = 0; i < 16; ++i) { + w[i] = htobe32(w[i]); + } + + for (i = 16; i < 80; ++i) { + w[i] = rol(w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16], 1); + } + + a = st->vl_state[0]; + b = st->vl_state[1]; + c = st->vl_state[2]; + d = st->vl_state[3]; + e = st->vl_state[4]; + +#define UP \ + temp = rol(a, 5) + f + e + k + w[i]; \ + e = d; \ + d = c; \ + c = rol(b, 30); \ + b = a; \ + a = temp; + +#define OP(_start, _end, _k, _e) \ + for (i = _start; i < _end; ++i) { \ + f = _e; \ + k = _k; \ + UP \ + } + + OP( 0, 20, SHA1_K0, (b & c) ^ ((~b) & d)) + OP(20, 40, SHA1_K1, b ^ c ^ d) + OP(40, 60, SHA1_K2, (b & c) ^ (b & d) ^ (c & d)) + OP(60, 80, SHA1_K3, b ^ c ^ d) + +#undef OP +#undef UP + + st->vl_state[0] += a; + st->vl_state[1] += b; + st->vl_state[2] += c; + st->vl_state[3] += d; + st->vl_state[4] += e; +} + +void vl_sha1_update(vl_sha1_st *st, const void *data, size_t sz) +{ + const uint8_t *dbytes = data; + size_t rem; + st->vl_total += sz; + + while (sz >= (rem = (VL_SHA1_CHUNKLEN_BYTES - st->vl_nchunk))) { + memcpy(st->vl_chunk + st->vl_nchunk, dbytes, rem); + sha1_upd(st); + st->vl_nchunk = 0; + + dbytes += rem; + sz -= rem; + } + + if (sz > 0) { + memcpy(st->vl_chunk + st->vl_nchunk, dbytes, sz); + st->vl_nchunk += sz; + } +} + +void vl_sha1_finalize(vl_sha1_st *st, vl_sha1 out) +{ + /* SHA1 specifies a 1 bit to follow the data */ + st->vl_chunk[st->vl_nchunk] = UINT8_C(0x80); + ++st->vl_nchunk; + if (st->vl_nchunk > (VL_SHA1_CHUNKLEN_BYTES - 8)) { + /* must pad the rest of the way */ + memset(st->vl_chunk + st->vl_nchunk, 0, VL_SHA1_CHUNKLEN_BYTES - st->vl_nchunk); + sha1_upd(st); + st->vl_nchunk = 0; + } + + size_t rem = (VL_SHA1_CHUNKLEN_BYTES - st->vl_nchunk) - 8; + uint64_t lenbe = htobe64(st->vl_total * 8); + memset(st->vl_chunk + st->vl_nchunk, 0, rem); + st->vl_nchunk += rem; + + memcpy(st->vl_chunk + st->vl_nchunk, (uint8_t *)&lenbe, 8); + sha1_upd(st); + + uint32_t pout_i[VL_SHA1_STATELEN_32]; + pout_i[0] = htobe32(st->vl_state[0]); + pout_i[1] = htobe32(st->vl_state[1]); + pout_i[2] = htobe32(st->vl_state[2]); + pout_i[3] = htobe32(st->vl_state[3]); + pout_i[4] = htobe32(st->vl_state[4]); + memcpy(out, pout_i, sizeof(vl_sha1)); +} + +/* shortcut for hashing a buffer */ +void vl_sha1_buffer(vl_sha1 odigest, const void *data, size_t sz) +{ + vl_sha1_st st; + vl_sha1_init(&st); + vl_sha1_update(&st, data, sz); + vl_sha1_finalize(&st, odigest); +} + +static const char nibs[] = "0123456789abcdef"; + +/* converts digest bytes to a hex string (does NOT place a NUL byte) */ +void vl_sha1_encode(const vl_sha1 dig, char hex[VL_SHA1_DIGEST_HEX_STRLEN]) +{ + for (unsigned i = 0; i < VL_SHA1_DIGEST_BYTES; ++i) { + hex[i * 2] = nibs[dig[i] >> 4]; /* unsigned shift, dig[i] is 8 bytes */ + hex[i * 2 + 1] = nibs[dig[i] & 0xf]; + } +} + +#define NIB_BAD (~0u) + +static unsigned decode_nib(char c) +{ + switch (c) { + case '0': return 0; + case '1': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + case 'a': case 'A': return 10; + case 'b': case 'B': return 11; + case 'c': case 'C': return 12; + case 'd': case 'D': return 13; + case 'e': case 'E': return 14; + case 'f': case 'F': return 15; + default: return NIB_BAD; + } +} + +/* converts a hex string to digest bytes */ +int vl_sha1_decode(vl_sha1 odigest, const char hex[VL_SHA1_DIGEST_HEX_STRLEN]) +{ + for (unsigned i = 0; i < VL_SHA1_DIGEST_BYTES; ++i) { + unsigned nibhi = decode_nib(hex[i * 2]); + unsigned niblo = decode_nib(hex[i * 2 + 1]); + if ((nibhi | niblo) == NIB_BAD) return -1; + odigest[i] = (uint8_t)((nibhi << 4) | niblo); + } + + return 0; +} + +#ifdef SHA1_STANDALONE_TEST + +#include +#include +#include + +int main(void) +{ + unsigned char chunk[4096]; + char dig[VL_SHA1_DIGEST_HEX_STRLEN + 1]; + vl_sha1_st st; + + dig[VL_SHA1_DIGEST_HEX_STRLEN] = '\0'; + vl_sha1_init(&st); + + while (1) { + ssize_t nread = read(0, chunk, 4096); + if (nread < 0) { + fprintf(stderr, "read: %s\n", strerror(errno)); + return 1; + } else if (nread == 0) { + break; + } + + vl_sha1_update(&st, chunk, nread); + } + + vl_sha1 o; + vl_sha1_finalize(&st, o); + + vl_sha1_encode(o, dig); + printf("%s\n", dig); + + vl_sha1 o2; + vl_sha1_decode(o2, dig); + printf("cmp: %d\n", memcmp(o, o2, sizeof(vl_sha1))); + + return 0; +} + +#endif -- cgit v1.2.3-70-g09d2