#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