Initial commit. Works.

Takes a line of input from stdin, then outputs its SHA256 hash.
Verbosity cannot be changed.
This commit is contained in:
Sandy Mossgrave 2023-05-21 21:02:25 +00:00
commit da6f0568b6
6 changed files with 297 additions and 0 deletions

128
Makefile Normal file
View File

@ -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 <target> $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)

70
sources/hasher.c Normal file
View File

@ -0,0 +1,70 @@
#include <openssl/evp.h>
#include <openssl/err.h>
#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;
}

7
sources/include/hasher.h Normal file
View File

@ -0,0 +1,7 @@
#include <openssl/evp.h>
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);

19
sources/include/log.h Normal file
View File

@ -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

26
sources/log.c Normal file
View File

@ -0,0 +1,26 @@
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#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;
}

47
sources/main/main.c Normal file
View File

@ -0,0 +1,47 @@
#include <stdio.h>
#include <string.h>
#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;
}