summaryrefslogtreecommitdiffstats
path: root/lib/sha1.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sha1.c')
-rw-r--r--lib/sha1.c237
1 files changed, 237 insertions, 0 deletions
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 <string.h>
+
+/* for byteswap functions (TODO: avoid GNU extensions) */
+#include <endian.h>
+
+/* 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 <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+
+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