commit da6f0568b6b8bb393233107ef374da3d3cb40921 Author: Sandy Mossgrave Date: Sun May 21 21:02:25 2023 +0000 Initial commit. Works. Takes a line of input from stdin, then outputs its SHA256 hash. Verbosity cannot be changed. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d5abfe3 --- /dev/null +++ b/Makefile @@ -0,0 +1,128 @@ +#NEWPROJECT requires refactor.sh sources +# Taken from https://stackoverflow.com/questions/30573481/how-to-write-a-makefile-with-separate-source-and-header-directories + +# Always build targets and unit tests together. This avoids the scenario wherein the targets are built, then the source files are edited, then the unit tests are built and, accidentally, outdated targets are packaged and shipped. I say this knowing full well that I never ship my targets to anyone ever. + +PREFIX := +SUFFIX := + +SRCDIR := sources +MAINDIR := $(SRCDIR)/main +INCDIR := $(SRCDIR)/include +OBJDIR := objects +MAINOD := $(OBJDIR)/main +BINDIR := build + +INSTDIR := /usr/local/bin + +REFACTOR := refactor.h + +TESTCFLAGS:= $(pkg-config --cflags criterion) +TESTLIBS := $(pkg-config --libs criterion) +TESTDIR := $(SRCDIR)/test +TESTOD := $(OBJDIR)/test + +HEADERS := $(wildcard $(INCDIR)/*.h) + +MAINS := $(wildcard $(MAINDIR)/*.c) +MAINOBJS := $(patsubst $(MAINDIR)/%.c, $(MAINOD)/%.o, $(MAINS)) + +TESTS := $(wildcard $(TESTDIR)/*.c) +TESTOBJS := $(patsubst $(TESTDIR)/%.c, $(TESTOD)/%.o, $(TESTS)) + +UNITTESTS := $(patsubst $(TESTDIR)/%.c, $(BINDIR)/%, $(TESTS)) + +TARGETS := $(patsubst $(MAINDIR)/%.c, $(BINDIR)/%, $(MAINS)) + + +SOURCES := $(wildcard $(SRCDIR)/*.c) + +OBJECTS := $(patsubst $(SRCDIR)/%.c, $(OBJDIR)/%.o, $(SOURCES)) + +CPPFLAGS := -I$(abspath $(INCDIR)) -MMD -MP -DUSE_LIBNOTIFY +CFLAGS := -Wall -Werror -Wpedantic $(shell pkg-config --cflags libnotify) +LDFLAGS := #-Lmath or whatever +LDLIBS := $(shell pkg-config --libs libnotify libcrypto) #-lm or whatever + +define MANGLE = + $(dir $(1))$(PREFIX)$(notdir $(1))$(SUFFIX) +endef + +.PHONY: all clean + +all: $(TARGETS) $(UNITTESTS) + @echo "Executables built: " $^ + +refactor: + ./refactor.sh + +$(UNITTESTS): $(OBJECTS) $(TESTOBJS) | $(BINDIR) $(OBJDIR) $(TESTOD) + @echo "Linking unit tests..." + @echo "Object files for tests is " $(TESTOBJS) + $(CC) $(LDFLAGS) $(OBJECTS) $(patsubst $(BINDIR)/%, $(TESTOD)/%.o, $@) $(LDLIBS) $(TESTLIBS) -o $(dir $@)$(PREFIX)$(notdir $@)$(SUFFIX) + @echo "...Done linking unit tests" + +$(TARGETS): $(OBJECTS) $(MAINOBJS) | $(BINDIR) $(OBJDIR) $(MAINOD) + @echo "Linking targets..." + $(CC) $(LDFLAGS) $(OBJECTS) $(patsubst $(BINDIR)/%, $(MAINOD)/%.o, $@) $(LDLIBS) -o $(dir $@)$(PREFIX)$(notdir $@)$(SUFFIX) + @echo "...Done linking targets" + +$(BINDIR) $(OBJDIR) $(MAINOD) $(TESTOD): + mkdir --parents $@ + +$(OBJDIR)/%.o: $(SRCDIR)/%.c $(HEADERS) | $(OBJDIR) + $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ + +$(MAINOD)/%.o: $(MAINDIR)/%.c $(HEADERS) | $(MAINOD) + $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ + +$(TESTOD)/%.o: $(TESTDIR)/%.c $(HEADERS) | $(TESTOD) + $(CC) $(CPPFLAGS) $(CFLAGS) $(TESTCFLAGS) -c $< -o $@ + +# $| evaluates to the order-only prerequisites, in this case: $(TARGETS) +# The $(foreach ...) function will evaluate to a semicolon-separated list of 'cp $instdir' +# Putting this $(foreach ...) into the recipe means that its result +# cp a /usr/local/bin; cp b /usr/local/bin; +# will be run as a shell command. +# +# Shamelessly derived from: +# https://stackoverflow.com/questions/7918511/make-execute-an-action-for-each-prerequisite + +install: | $(TARGETS) + @echo "Installing executables to $(INSTDIR)" + @$(foreach target, $|, cp -v $(call MANGLE, $(target)) $(INSTDIR);) + +uninstall: | $(TARGETS) + @echo "Removing executables from $(INSTDIR)" + $(foreach target, $(notdir $(call MANGLE, $|)), rm -v $(INSTDIR)/$(target);) + +clean: + $(RM) -rv $(BINDIR) $(OBJDIR) + +# Lazy kludge to run the first executable specified in $(TARGETS) +# Could also be accomplished by running $< but this is easier to read +run: all + $(eval MANGLEDNAME=$(call MANGLE, $(word 1,$(TARGETS)))) + @echo "Running " $(MANGLEDNAME) + @$(MANGLEDNAME) +# @$(dir $(word 1,$(TARGETS)))$(PREFIX)$(notdir $(word 1,$(TARGETS)))$(SUFFIX) + +# unit testing! +test: $(TARGETS) $(UNITTESTS) + @echo "Testing " $(word 1,$(UNITTESTS)) + @$(dir $(word 1,$(UNITTESTS)))$(PREFIX)$(notdir $(word 1,$(UNITTESTS)))$(SUFFIX) + +.PHONY: variable +variable: all + $(eval mangle2=$(dir $(word 1,$(UNITTESTS)))$(PREFIX)$(notdir $(word 1,$(UNITTESTS)))$(SUFFIX)) + $(eval mangle3=$(call MANGLE, $(UNITTESTS))) + @echo "variable declared within a recipe: "$(mangle2) + @echo "variable declared within a recipe using a canned recipe: "$(mangle3) + +# Honestly still not sure what this directive does. +# It's an include directive, which processes all .d files that correspond to members of $(OBJ), +# with the hyphen in front meaning that it suppresses/ignores errors (from nonexistent .d files) +# Seems to be included under the stipulation that GNU make will auto-generate .d files based on +# a given file's #include directives? +# https://stackoverflow.com/questions/19114410/what-is-d-file-after-building-with-make +-include $(OBJ:.o=.d) diff --git a/sources/hasher.c b/sources/hasher.c new file mode 100644 index 0000000..7210af5 --- /dev/null +++ b/sources/hasher.c @@ -0,0 +1,70 @@ +#include +#include +#include "log.h" + +EVP_MD_CTX * md_ctx = NULL; +EVP_MD * md = NULL; + +static int hasher_initialized = 0; + +int init_hasher(void){ + if(hasher_initialized){ + printf("hasher already initialized"); + return 0; + } + else{ + md = EVP_MD_fetch(NULL, "SHA256", NULL); + if(md == NULL){ + ERR_print_errors_fp(stderr); + return 1; + } + md_ctx = EVP_MD_CTX_new(); + /*TODO: see whether EVP_MD_CTX_new() ever fails?*/ + if(md_ctx == NULL){ + ERR_print_errors_fp(stderr); + return 1; + } + + } + return 0; +} + +int deinit_hasher(void){ + if(md != NULL){ + EVP_MD_free(md); + } + + if(md_ctx != NULL){ + EVP_MD_CTX_free(md_ctx); + } + return 0; +} + +int rounduptoblocksize(int a){ + return a; +} + +unsigned char * digest(const unsigned char *in, size_t inlen, unsigned int * outlen){ + + if(!EVP_DigestInit_ex(md_ctx, md, NULL)){ + log("Problem encountered while initializing digest"); + ERR_print_errors_fp(stderr); + return NULL; + } + + if(!EVP_DigestUpdate(md_ctx, in, inlen)){ + log("Problem encountered while adding data to digest"); + ERR_print_errors_fp(stderr); + return NULL; + } + + unsigned char *out = OPENSSL_malloc(EVP_MD_get_size(md)); + + if(!EVP_DigestFinal_ex(md_ctx, out, outlen)){ + log("Problem encountered while retrieving digest"); + ERR_print_errors_fp(stderr); + return NULL; + } + + return out; +} diff --git a/sources/include/hasher.h b/sources/include/hasher.h new file mode 100644 index 0000000..cc23e6f --- /dev/null +++ b/sources/include/hasher.h @@ -0,0 +1,7 @@ +#include + +extern EVP_MD * md; + +int init_hasher(void); +int deinit_hasher(void); +unsigned char * digest(const unsigned char *in, size_t inlen, unsigned int * outlen); diff --git a/sources/include/log.h b/sources/include/log.h new file mode 100644 index 0000000..17db208 --- /dev/null +++ b/sources/include/log.h @@ -0,0 +1,19 @@ +#ifndef LDAP_LOG +#define LDAP_LOG + +const char * currtime(void); +/* + Great, I remember why I chose to use a macro. + There's no good paradigm for passing the variadic arguments of printf + through another function. + + I can use a paradigm wherein you just call the timestamper immediately + before printf()-ing your message, but like, + I dunno. + + Let's see whether I can split the log(X) macro into this and a + function in log.h that supplies the time + */ +#define log(X) printf("%s %s", currtime(), X) + +#endif diff --git a/sources/log.c b/sources/log.c new file mode 100644 index 0000000..20751b2 --- /dev/null +++ b/sources/log.c @@ -0,0 +1,26 @@ +#include +#include +#include + + +#define TIMEBUFLEN 48 +static char timebuf[TIMEBUFLEN]; +const char * currtime(void) { + time_t tttt; + time(&tttt); + struct tm timeparts; +#ifdef USE_LOCALTIME + localtime_r(&tttt, &timeparts); +#endif +#ifndef USE_LOCALTIME + gmtime_r(&tttt, &timeparts); +#endif + sprintf(timebuf, "%02d-%02d-%d %02d:%02d:%02d", + timeparts.tm_mday, + timeparts.tm_mon + 1, + timeparts.tm_year + 1900, + timeparts.tm_hour, + timeparts.tm_min, + timeparts.tm_sec); + return (const char *)timebuf; +} diff --git a/sources/main/main.c b/sources/main/main.c new file mode 100644 index 0000000..aa8ec22 --- /dev/null +++ b/sources/main/main.c @@ -0,0 +1,47 @@ +#include +#include +#include "hasher.h" +#include "log.h" + +#define BUFLEN 2048 + +int main(void){ + log("hi\n"); + + if(init_hasher()){ + log("Error encountered while initializing hasher"); + } + + unsigned char * in = (unsigned char*)malloc(sizeof(unsigned char) * BUFLEN); + size_t inlen = 0; + char * signin = (char*)malloc(sizeof(char) * BUFLEN); + ssize_t bytesread = getline(&signin, &inlen, stdin); + memcpy(in, signin, bytesread); + + printf("Input:\n\t"); + for(int i = 0; i < bytesread; i++){ + printf("%02x ", in[i]); + } + printf("\n\t"); + for(int i = 0; i < bytesread; i++){ + printf(" %c ", in[i]); + } + printf("\n"); + + unsigned int len; + unsigned char * a = digest(in, bytesread, &len); + char * aa = (char*)malloc(sizeof(char) * 2 * len); + printf("Output:\n\t"); + for(int i = 0; i < len; i++){ + sprintf(aa + (2 * i), "%02x", a[i]); + } + printf("%s\n", aa); + + OPENSSL_free(a); + free(aa); + if(deinit_hasher()){ + log("Error encountered while deinitializing hasher"); + } + + return 0; +}