diff --git a/Makefile b/Makefile index 9efbde9..affe811 100644 --- a/Makefile +++ b/Makefile @@ -43,6 +43,9 @@ CPPFLAGS?= -D_GNU_SOURCE ifndef SYSLIB CPPFLAGS+= -Iinclude/usr/include endif +ifdef NO_B64 +CPPFLAGS+= -DNO_B64 +endif CDIAGFLAGS+= -Wall CDIAGFLAGS+= -Wextra @@ -90,13 +93,15 @@ LIBQUARK_DEPS+= $(EEBPF_FILES) include endif LIBQUARK_DEPS:= $(filter-out manpages.h, $(LIBQUARK_DEPS)) LIBQUARK_SRCS:= \ + base64.c \ bpf_queue.c \ btf.c \ btfhub.c \ compat.c \ kprobe_queue.c \ quark.c \ - qutil.c + qutil.c \ + sha256.c LIBQUARK_OBJS:= $(patsubst %.c,%.o,$(LIBQUARK_SRCS)) LIBQUARK_STATIC:= libquark.a LIBQUARK_STATIC_BIG:= libquark_big.a @@ -109,6 +114,11 @@ LIBQUARK_TARGET=$(LIBQUARK_STATIC) EXTRA_LDFLAGS+= -lbpf endif +# for b64_ntop() +ifndef NO_B64 +EXTRA_LDFLAGS+= -lresolv +endif + # ZLIB ZLIB_SRC:= zlib ZLIB_FILES:= $(shell find $(ZLIB_SRC) \(\ @@ -287,7 +297,7 @@ alpine: alpine-image clean-all $(call msg,ALPINE-DOCKER-RUN,Dockerfile) $(Q)$(DOCKER) run \ $(ALPINE_RUN_ARGS) $(SHELL) \ - -c "make -C $(PWD) all initramfs.gz EXTRA_LDFLAGS=-lfts" + -c "make -C $(PWD) all initramfs.gz EXTRA_LDFLAGS=-lfts NO_B64=y" alpine-image: clean-all $(call msg,ALPINE-IMAGE,Dockerfile.alpine) diff --git a/base64.c b/base64.c new file mode 100644 index 0000000..1c2c8eb --- /dev/null +++ b/base64.c @@ -0,0 +1,307 @@ +/* $OpenBSD: base64.c,v 1.5 2006/10/21 09:55:03 otto Exp $ */ + +/* + * Copyright (c) 1996 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Portions Copyright (c) 1995 by International Business Machines, Inc. + * + * International Business Machines, Inc. (hereinafter called IBM) grants + * permission under its copyrights to use, copy, modify, and distribute this + * Software with or without fee, provided that the above copyright notice and + * all paragraphs of this notice appear in all copies, and that the name of IBM + * not be used in connection with the marketing of any product incorporating + * the Software or modifications thereof, without specific, written prior + * permission. + * + * To the extent it has a right to do so, IBM grants an immunity from suit + * under its patents, if any, for the use, sale or manufacture of products to + * the extent that such products are used for performing Domain Name System + * dynamic updates in TCP/IP networks by means of the Software. No immunity is + * granted for any product per se or for any other function of any product. + * + * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, + * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN + * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +/* OPENBSD ORIGINAL: lib/libc/net/base64.c */ + +#ifdef NO_B64 + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "compat.h" + +static const char Base64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char Pad64 = '='; + +/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) + The following encoding technique is taken from RFC 1521 by Borenstein + and Freed. It is reproduced here in a slightly edited form for + convenience. + + A 65-character subset of US-ASCII is used, enabling 6 bits to be + represented per printable character. (The extra 65th character, "=", + is used to signify a special processing function.) + + The encoding process represents 24-bit groups of input bits as output + strings of 4 encoded characters. Proceeding from left to right, a + 24-bit input group is formed by concatenating 3 8-bit input groups. + These 24 bits are then treated as 4 concatenated 6-bit groups, each + of which is translated into a single digit in the base64 alphabet. + + Each 6-bit group is used as an index into an array of 64 printable + characters. The character referenced by the index is placed in the + output string. + + Table 1: The Base64 Alphabet + + Value Encoding Value Encoding Value Encoding Value Encoding + 0 A 17 R 34 i 51 z + 1 B 18 S 35 j 52 0 + 2 C 19 T 36 k 53 1 + 3 D 20 U 37 l 54 2 + 4 E 21 V 38 m 55 3 + 5 F 22 W 39 n 56 4 + 6 G 23 X 40 o 57 5 + 7 H 24 Y 41 p 58 6 + 8 I 25 Z 42 q 59 7 + 9 J 26 a 43 r 60 8 + 10 K 27 b 44 s 61 9 + 11 L 28 c 45 t 62 + + 12 M 29 d 46 u 63 / + 13 N 30 e 47 v + 14 O 31 f 48 w (pad) = + 15 P 32 g 49 x + 16 Q 33 h 50 y + + Special processing is performed if fewer than 24 bits are available + at the end of the data being encoded. A full encoding quantum is + always completed at the end of a quantity. When fewer than 24 input + bits are available in an input group, zero bits are added (on the + right) to form an integral number of 6-bit groups. Padding at the + end of the data is performed using the '=' character. + + Since all base64 input is an integral number of octets, only the + ------------------------------------------------- + following cases can arise: + + (1) the final quantum of encoding input is an integral + multiple of 24 bits; here, the final unit of encoded + output will be an integral multiple of 4 characters + with no "=" padding, + (2) the final quantum of encoding input is exactly 8 bits; + here, the final unit of encoded output will be two + characters followed by two "=" padding characters, or + (3) the final quantum of encoding input is exactly 16 bits; + here, the final unit of encoded output will be three + characters followed by one "=" padding character. + */ + +int +b64_ntop(u_char const *src, size_t srclength, char *target, size_t targsize) +{ + size_t datalength = 0; + u_char input[3]; + u_char output[4]; + u_int i; + + while (2 < srclength) { + input[0] = *src++; + input[1] = *src++; + input[2] = *src++; + srclength -= 3; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + output[3] = input[2] & 0x3f; + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + target[datalength++] = Base64[output[2]]; + target[datalength++] = Base64[output[3]]; + } + + /* Now we worry about padding. */ + if (0 != srclength) { + /* Get what's left. */ + input[0] = input[1] = input[2] = '\0'; + for (i = 0; i < srclength; i++) + input[i] = *src++; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + if (srclength == 1) + target[datalength++] = Pad64; + else + target[datalength++] = Base64[output[2]]; + target[datalength++] = Pad64; + } + if (datalength >= targsize) + return (-1); + target[datalength] = '\0'; /* Returned value doesn't count \0. */ + return (datalength); +} + +/* skips all whitespace anywhere. + converts characters, four at a time, starting at (or after) + src from base - 64 numbers into three 8 bit bytes in the target area. + it returns the number of data bytes stored at the target, or -1 on error. + */ + +int +b64_pton(char const *src, u_char *target, size_t targsize) +{ + u_int tarindex, state; + int ch; + char *pos; + + state = 0; + tarindex = 0; + + while ((ch = *src++) != '\0') { + if (isspace(ch)) /* Skip whitespace anywhere. */ + continue; + + if (ch == Pad64) + break; + + pos = strchr(Base64, ch); + if (pos == 0) /* A non-base64 character. */ + return (-1); + + switch (state) { + case 0: + if (target) { + if (tarindex >= targsize) + return (-1); + target[tarindex] = (pos - Base64) << 2; + } + state = 1; + break; + case 1: + if (target) { + if (tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 4; + target[tarindex+1] = ((pos - Base64) & 0x0f) + << 4 ; + } + tarindex++; + state = 2; + break; + case 2: + if (target) { + if (tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 2; + target[tarindex+1] = ((pos - Base64) & 0x03) + << 6; + } + tarindex++; + state = 3; + break; + case 3: + if (target) { + if (tarindex >= targsize) + return (-1); + target[tarindex] |= (pos - Base64); + } + tarindex++; + state = 0; + break; + } + } + + /* + * We are done decoding Base-64 chars. Let's see if we ended + * on a byte boundary, and/or with erroneous trailing characters. + */ + + if (ch == Pad64) { /* We got a pad char. */ + ch = *src++; /* Skip it, get next. */ + switch (state) { + case 0: /* Invalid = in first position */ + case 1: /* Invalid = in second position */ + return (-1); + + case 2: /* Valid, means one byte of info */ + /* Skip any number of spaces. */ + for (; ch != '\0'; ch = *src++) + if (!isspace(ch)) + break; + /* Make sure there is another trailing = sign. */ + if (ch != Pad64) + return (-1); + ch = *src++; /* Skip the = */ + /* Fall through to "single trailing =" case. */ + /* FALLTHROUGH */ + + case 3: /* Valid, means two bytes of info */ + /* + * We know this char is an =. Is there anything but + * whitespace after it? + */ + for (; ch != '\0'; ch = *src++) + if (!isspace(ch)) + return (-1); + + /* + * Now make sure for cases 2 and 3 that the "extra" + * bits that slopped past the last full byte were + * zeros. If we don't check them, they become a + * subliminal channel. + */ + if (target && target[tarindex] != 0) + return (-1); + } + } else { + /* + * We ended by seeing the end of the string. Make sure we + * have no partial bytes lying around. + */ + if (state != 0) + return (-1); + } + + return (tarindex); +} + +#endif /* NO_B64 */ diff --git a/compat.h b/compat.h index 0fcbe59..0a3bfab 100644 --- a/compat.h +++ b/compat.h @@ -70,4 +70,21 @@ long long strtonum(const char *, long long, long long, const char **); */ void sshbuf_dump_data(const void *, size_t, FILE *); +/* + * Musl + */ +#ifdef NO_B64 +int b64_ntop(u_char const *src, size_t srclength, char *target, + size_t targsize); +int b64_pton(char const *src, u_char *target, size_t targsize); +#else +#include +#endif /* NO_B64 */ + +/* + * Linkining with the one billion versions of openssl is a pain, we need sha256 + * for a dozen bytes only. + */ +#include "sha256.h" + #endif /* _COMPAT_H */ diff --git a/quark.c b/quark.c index 34d14f1..f50e4a1 100644 --- a/quark.c +++ b/quark.c @@ -62,6 +62,7 @@ RB_GENERATE(raw_event_by_pidtime, raw_event, struct quark { unsigned int hz; u64 boottime; + char machine_id[1024]; } quark; struct raw_event * @@ -447,6 +448,49 @@ process_by_pid_cmp(struct quark_process *a, struct quark_process *b) return (0); } +static void +process_entity_id(struct quark_queue *qq, struct quark_process *qp) +{ + sha256_context ctx; + u64 pid64_le, start_le, ns; + u8 digest[SHA256_SIZE_BYTES]; + char digest_p[45]; + size_t machine_len; + + /* No proc_time_boot, bail */ + if ((qp->flags & QUARK_F_PROC) == 0) + return; + /* Already known, bail */ + if (qp->flags & QUARK_F_ENTITYID) + return; + /* No machine_id, bail */ + if ((machine_len = strlen(quark.machine_id)) == 0) + return; + + /* + * Kill precision + */ + ns = qp->proc_time_boot; + ns -= ns % ((NS_PER_S) / quark.hz); + + /* Historically little endian */ + pid64_le = htole64(qp->pid); + start_le = htole64(ns); + + sha256_init(&ctx); + sha256_hash(&ctx, quark.machine_id, machine_len); + sha256_hash(&ctx, &pid64_le, sizeof(pid64_le)); + sha256_hash(&ctx, &start_le, sizeof(start_le)); + sha256_done(&ctx, digest); + + b64_ntop(digest, sizeof(digest), digest_p, sizeof(digest_p)); + digest_p[sizeof(digest_p) - 1] = 0; + + /* Let it truncate, entity_id is only 12 bytes */ + (void)strlcpy(qp->entity_id, digest_p, sizeof(qp->entity_id)); + qp->flags |= QUARK_F_ENTITYID; +} + /* * Socket stuff */ @@ -585,6 +629,8 @@ event_flag_str(u64 flag) return "CWD"; case QUARK_F_CGROUP: return "CGRP"; + case QUARK_F_ENTITYID: + return "EID"; default: return "?"; } @@ -1017,6 +1063,10 @@ quark_event_dump(const struct quark_event *qev, FILE *f) entry_leader_type_str(qp->proc_entry_leader_type), qp->proc_entry_leader); } + if (qp->flags & QUARK_F_ENTITYID) { + flagname = event_flag_str(QUARK_F_ENTITYID); + P(" %.4s\tentity_id=%s\n", flagname, qp->entity_id); + } if (qp->flags & QUARK_F_CWD) { flagname = event_flag_str(QUARK_F_CWD); P(" %.4s\tcwd=%s\n", flagname, qp->cwd); @@ -1268,12 +1318,16 @@ raw_event_process(struct quark_queue *qq, struct raw_event *src) /* Don't set cwd as it's not valid on exit */ comm = raw_task->comm; + if (raw_task->cgroup != NULL) { qp->flags |= QUARK_F_CGROUP; free(qp->cgroup); qp->cgroup = raw_task->cgroup; raw_task->cgroup = NULL; } + + /* Depends on QUARK_F_PROC, idempotent */ + process_entity_id(qq, qp); } if (raw_comm != NULL) comm = raw_comm->comm; /* raw_comm always overrides */ @@ -2036,6 +2090,39 @@ fetch_boottime(void) return (btime * NS_PER_S); } +static int +fetch_machine_id(char *buf, size_t len) +{ + int fd; + char *id; + const char **path, *all_paths[] = { + "/etc/machine-id", + "/var/lib/dbus/machine-id", + "/var/db/dbus/machine-id", + NULL + }; + + for (fd = -1, path = all_paths; *path != NULL; path++) { + fd = open("/etc/machine-id", O_RDONLY); + if (fd != -1) + break; + } + if (fd == -1) + return (-1); + /* Historically the final newline is included, so keep it. */ + if ((id = load_file_nostat(fd, NULL)) == NULL) { + close(fd); + warnx("can't load machine_id"); + return (-1); + } + close(fd); + + if (strlcpy(buf, id, len) >= len) + warnx("machine_id truncated, ignoring"); + + return (0); +} + /* * Aggregation is a relationship between a parent event and a child event. In a * fork+exec cenario, fork is the parent, exec is the child. @@ -2096,6 +2183,12 @@ quark_init(void) qwarn("can't fetch btime"); return (-1); } + if (fetch_machine_id(quark.machine_id, + sizeof(quark.machine_id)) == -1) { + qwarn("can't fetch machine id, ignoring"); + quark.machine_id[0] = 0; + } + quark.hz = hz; quark.boottime = boottime; diff --git a/quark.h b/quark.h index 7b15a5e..afda8d6 100644 --- a/quark.h +++ b/quark.h @@ -384,6 +384,7 @@ struct quark_process { #define QUARK_F_CMDLINE (1 << 4) #define QUARK_F_CWD (1 << 5) #define QUARK_F_CGROUP (1 << 6) +#define QUARK_F_ENTITYID (1 << 7) u64 flags; /* QUARK_F_PROC */ @@ -424,6 +425,8 @@ struct quark_process { char *cwd; /* QUARK_F_CGROUP */ char *cgroup; + /* QUARK_F_ENTITY_ID */ + char entity_id[13]; }; struct quark_process_iter { diff --git a/sha256.c b/sha256.c new file mode 100644 index 0000000..9f7bf2f --- /dev/null +++ b/sha256.c @@ -0,0 +1,328 @@ +//usr/bin/env clang -Ofast -Wall -Wextra -pedantic ${0} -o ${0%%.c*} $* ;exit $? +// +// SHA-256 implementation, Mark 2 +// +// Copyright (c) 2010,2014 Literatecode, http://www.literatecode.com +// Copyright (c) 2022 Ilia Levin (ilia@levin.sg) +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// + +#include "sha256.h" + +#ifndef _cbmc_ +#define __CPROVER_assume(...) do {} while(0) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define FN_ static inline __attribute__((const)) + +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 +}; + + +// ----------------------------------------------------------------------------- +FN_ uint8_t _shb(uint32_t x, uint32_t n) +{ + return ((x >> (n & 31)) & 0xff); +} // _shb + + +// ----------------------------------------------------------------------------- +FN_ uint32_t _shw(uint32_t x, uint32_t n) +{ + return ((x << (n & 31)) & 0xffffffff); +} // _shw + + +// ----------------------------------------------------------------------------- +FN_ uint32_t _r(uint32_t x, uint8_t n) +{ + return ((x >> n) | _shw(x, 32 - n)); +} // _r + + +// ----------------------------------------------------------------------------- +FN_ uint32_t _Ch(uint32_t x, uint32_t y, uint32_t z) +{ + return ((x & y) ^ ((~x) & z)); +} // _Ch + + +// ----------------------------------------------------------------------------- +FN_ uint32_t _Ma(uint32_t x, uint32_t y, uint32_t z) +{ + return ((x & y) ^ (x & z) ^ (y & z)); +} // _Ma + + +// ----------------------------------------------------------------------------- +FN_ uint32_t _S0(uint32_t x) +{ + return (_r(x, 2) ^ _r(x, 13) ^ _r(x, 22)); +} // _S0 + + +// ----------------------------------------------------------------------------- +FN_ uint32_t _S1(uint32_t x) +{ + return (_r(x, 6) ^ _r(x, 11) ^ _r(x, 25)); +} // _S1 + + +// ----------------------------------------------------------------------------- +FN_ uint32_t _G0(uint32_t x) +{ + return (_r(x, 7) ^ _r(x, 18) ^ (x >> 3)); +} // _G0 + + +// ----------------------------------------------------------------------------- +FN_ uint32_t _G1(uint32_t x) +{ + return (_r(x, 17) ^ _r(x, 19) ^ (x >> 10)); +} // _G1 + + +// ----------------------------------------------------------------------------- +FN_ uint32_t _word(uint8_t *c) +{ + return (_shw(c[0], 24) | _shw(c[1], 16) | _shw(c[2], 8) | (c[3])); +} // _word + + +// ----------------------------------------------------------------------------- +static void _addbits(sha256_context *ctx, uint32_t n) +{ + __CPROVER_assume(__CPROVER_DYNAMIC_OBJECT(ctx)); + + if (ctx->bits[0] > (0xffffffff - n)) { + ctx->bits[1] = (ctx->bits[1] + 1) & 0xFFFFFFFF; + } + ctx->bits[0] = (ctx->bits[0] + n) & 0xFFFFFFFF; +} // _addbits + + +// ----------------------------------------------------------------------------- +static void _hash(sha256_context *ctx) +{ + __CPROVER_assume(__CPROVER_DYNAMIC_OBJECT(ctx)); + + register uint32_t a, b, c, d, e, f, g, h; + uint32_t t[2]; + + a = ctx->hash[0]; + b = ctx->hash[1]; + c = ctx->hash[2]; + d = ctx->hash[3]; + e = ctx->hash[4]; + f = ctx->hash[5]; + g = ctx->hash[6]; + h = ctx->hash[7]; + + for (uint32_t i = 0; i < 64; i++) { + if (i < 16) { + ctx->W[i] = _word(&ctx->buf[_shw(i, 2)]); + } else { + ctx->W[i] = _G1(ctx->W[i - 2]) + ctx->W[i - 7] + + _G0(ctx->W[i - 15]) + ctx->W[i - 16]; + } + + t[0] = h + _S1(e) + _Ch(e, f, g) + K[i] + ctx->W[i]; + t[1] = _S0(a) + _Ma(a, b, c); + h = g; + g = f; + f = e; + e = d + t[0]; + d = c; + c = b; + b = a; + a = t[0] + t[1]; + } + + ctx->hash[0] += a; + ctx->hash[1] += b; + ctx->hash[2] += c; + ctx->hash[3] += d; + ctx->hash[4] += e; + ctx->hash[5] += f; + ctx->hash[6] += g; + ctx->hash[7] += h; +} // _hash + + +// ----------------------------------------------------------------------------- +void sha256_init(sha256_context *ctx) +{ + if (ctx != NULL) { + ctx->bits[0] = ctx->bits[1] = ctx->len = 0; + ctx->hash[0] = 0x6a09e667; + ctx->hash[1] = 0xbb67ae85; + ctx->hash[2] = 0x3c6ef372; + ctx->hash[3] = 0xa54ff53a; + ctx->hash[4] = 0x510e527f; + ctx->hash[5] = 0x9b05688c; + ctx->hash[6] = 0x1f83d9ab; + ctx->hash[7] = 0x5be0cd19; + } +} // sha256_init + + +// ----------------------------------------------------------------------------- +void sha256_hash(sha256_context *ctx, const void *data, size_t len) +{ + const uint8_t *bytes = (const uint8_t *)data; + + if ((ctx != NULL) && (bytes != NULL) && (ctx->len < sizeof(ctx->buf))) { + __CPROVER_assume(__CPROVER_DYNAMIC_OBJECT(bytes)); + __CPROVER_assume(__CPROVER_DYNAMIC_OBJECT(ctx)); + for (size_t i = 0; i < len; i++) { + ctx->buf[ctx->len++] = bytes[i]; + if (ctx->len == sizeof(ctx->buf)) { + _hash(ctx); + _addbits(ctx, sizeof(ctx->buf) * 8); + ctx->len = 0; + } + } + } +} // sha256_hash + + +// ----------------------------------------------------------------------------- +void sha256_done(sha256_context *ctx, uint8_t *hash) +{ + register uint32_t i, j; + + if (ctx != NULL) { + j = ctx->len % sizeof(ctx->buf); + ctx->buf[j] = 0x80; + for (i = j + 1; i < sizeof(ctx->buf); i++) { + ctx->buf[i] = 0x00; + } + + if (ctx->len > 55) { + _hash(ctx); + for (j = 0; j < sizeof(ctx->buf); j++) { + ctx->buf[j] = 0x00; + } + } + + _addbits(ctx, ctx->len * 8); + ctx->buf[63] = _shb(ctx->bits[0], 0); + ctx->buf[62] = _shb(ctx->bits[0], 8); + ctx->buf[61] = _shb(ctx->bits[0], 16); + ctx->buf[60] = _shb(ctx->bits[0], 24); + ctx->buf[59] = _shb(ctx->bits[1], 0); + ctx->buf[58] = _shb(ctx->bits[1], 8); + ctx->buf[57] = _shb(ctx->bits[1], 16); + ctx->buf[56] = _shb(ctx->bits[1], 24); + _hash(ctx); + + if (hash != NULL) { + for (i = 0, j = 24; i < 4; i++, j -= 8) { + hash[i + 0] = _shb(ctx->hash[0], j); + hash[i + 4] = _shb(ctx->hash[1], j); + hash[i + 8] = _shb(ctx->hash[2], j); + hash[i + 12] = _shb(ctx->hash[3], j); + hash[i + 16] = _shb(ctx->hash[4], j); + hash[i + 20] = _shb(ctx->hash[5], j); + hash[i + 24] = _shb(ctx->hash[6], j); + hash[i + 28] = _shb(ctx->hash[7], j); + } + } + } +} // sha256_done + + +// ----------------------------------------------------------------------------- +void sha256(const void *data, size_t len, uint8_t *hash) +{ + sha256_context ctx; + + sha256_init(&ctx); + sha256_hash(&ctx, data, len); + sha256_done(&ctx, hash); +} // sha256 + + +#if 0 +#pragma mark - Self Test +#endif +#ifdef SHA256_SELF_TEST__ +#include +#include + +int main(void) +{ + char *buf[] = { + "", + "e3b0c442 98fc1c14 9afbf4c8 996fb924 27ae41e4 649b934c a495991b 7852b855", + + "abc", + "ba7816bf 8f01cfea 414140de 5dae2223 b00361a3 96177a9c b410ff61 f20015ad", + + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + "248d6a61 d20638b8 e5c02693 0c3e6039 a33ce459 64ff2167 f6ecedd4 19db06c1", + + "The quick brown fox jumps over the lazy dog", + "d7a8fbb3 07d78094 69ca9abc b0082e4f 8d5651e4 6d3cdb76 2d02d0bf 37c9e592", + + "The quick brown fox jumps over the lazy cog", // avalanche effect test + "e4c4d8f3 bf76b692 de791a17 3e053211 50f7a345 b46484fe 427f6acc 7ecc81be", + + "bhn5bjmoniertqea40wro2upyflkydsibsk8ylkmgbvwi420t44cq034eou1szc1k0mk46oeb7ktzmlxqkbte2sy", + "9085df2f 02e0cc45 5928d0f5 1b27b4bf 1d9cd260 a66ed1fd a11b0a3f f5756d99" + }; + const size_t tests_total = sizeof(buf) / sizeof(buf[0]); + uint8_t hash[SHA256_SIZE_BYTES]; + + if (0 != (tests_total % 2)) { + return printf("invalid tests\n"); + } + + for (size_t i = 0; i < tests_total; i += 2) { + sha256(buf[i], strlen(buf[i]), hash); + printf("input = '%s'\ndigest: %s\nresult: ", buf[i], buf[i + 1]); + for (size_t j = 0; j < SHA256_SIZE_BYTES; j++) { + printf("%02x%s", hash[j], ((j % 4) == 3) ? " " : ""); + } + printf("\n\n"); + } + + return 0; +} // main + +#endif // def SHA256_SELF_TEST__ + +#ifdef __cplusplus +} +#endif diff --git a/sha256.h b/sha256.h new file mode 100644 index 0000000..6df116b --- /dev/null +++ b/sha256.h @@ -0,0 +1,52 @@ +// +// SHA-256 implementation, Mark 2 +// +// Copyright (c) 2010,2014 Literatecode, http://www.literatecode.com +// Copyright (c) 2022 Ilia Levin (ilia@levin.sg) +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// + +#ifndef SHA256_H_ +#define SHA256_H_ + +#include +#include + +#define SHA256_SIZE_BYTES (32) + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct { + uint8_t buf[64]; + uint32_t hash[8]; + uint32_t bits[2]; + uint32_t len; + uint32_t rfu__; + uint32_t W[64]; +} sha256_context; + +void sha256_init(sha256_context *ctx); +void sha256_hash(sha256_context *ctx, const void *data, size_t len); +void sha256_done(sha256_context *ctx, uint8_t *hash); + +void sha256(const void *data, size_t len, uint8_t *hash); + +#ifdef __cplusplus +} +#endif + +#endif