Skip to content

Instantly share code, notes, and snippets.

@tell
Last active August 29, 2015 14:24
Show Gist options
  • Save tell/70fcf481b3130be16d3c to your computer and use it in GitHub Desktop.
Save tell/70fcf481b3130be16d3c to your computer and use it in GitHub Desktop.
An exercise for C99
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <openssl/sha.h>
#include "genhash.h"
const named_hash_func_t predefined_hash_funcs[] = {
{"SHA1", {SHA1, SHA_DIGEST_LENGTH}},
{"SHA224", {SHA224, SHA224_DIGEST_LENGTH}},
{"SHA256", {SHA256, SHA256_DIGEST_LENGTH}},
{"SHA384", {SHA384, SHA384_DIGEST_LENGTH}},
{"SHA512", {SHA512, SHA512_DIGEST_LENGTH}},
};
const size_t nhash = sizeof(predefined_hash_funcs)/sizeof(predefined_hash_funcs[0]);
hash_func_t select_hash_func(const char *name)
{
const size_t N = 128;
for (size_t i = 0; i < nhash; i++) {
if (strncmp(name, predefined_hash_funcs[i].name, N) == 0) {
return predefined_hash_funcs[i].hash;
}
}
errno = EINVAL;
return (hash_func_t){NULL, 0};
}
typedef union {
SHA_CTX SHA1;
SHA256_CTX SHA224;
SHA256_CTX SHA256;
SHA512_CTX SHA384;
SHA512_CTX SHA512;
} ctx_sha_family_t;
typedef struct {
ctx_sha_family_t base;
int status;
} ctx_sha_t;
#define DECLARE_HASH_INIT(name) \
static int name##_init_(ctx_hash_func_t * ctx_) { \
ctx_sha_t *ctx = (ctx_sha_t *)ctx_->ctx; \
if ((ctx->status = name##_Init(&(ctx->base.name))) == 1) { \
return E_OK; \
} else { \
return E_INTERNAL; \
} \
}
#define DECLARE_HASH_UPDATE(name) \
static int name##_update_(ctx_hash_func_t * ctx_, const void *data, size_t len) { \
ctx_sha_t *ctx = (ctx_sha_t *)ctx_->ctx; \
if ((ctx->status = name##_Update(&(ctx->base.name), data, len)) == 1) { \
return E_OK; \
} else { \
return E_INTERNAL; \
} \
}
#define DECLARE_HASH_FINAL(name) \
static int name##_final_(ctx_hash_func_t * ctx_, unsigned char *md) { \
ctx_sha_t *ctx = (ctx_sha_t *)ctx_->ctx; \
if ((ctx->status = name##_Final(md, &(ctx->base.name))) == 1) { \
return E_OK; \
} else { \
return E_INTERNAL; \
} \
}
#define DECLARE_HASH(name) \
DECLARE_HASH_INIT(name) \
DECLARE_HASH_UPDATE(name) \
DECLARE_HASH_FINAL(name)
#define SHA1_DIGEST_LENGTH SHA_DIGEST_LENGTH
#define RECORD_HASH(name) { \
{name##_DIGEST_LENGTH, name##_init_, name##_update_, name##_final_}, #name, \
}
DECLARE_HASH(SHA1)
DECLARE_HASH(SHA224)
DECLARE_HASH(SHA256)
DECLARE_HASH(SHA384)
DECLARE_HASH(SHA512)
typedef struct {
const ctx_hash_funcs_vtbl_t vtbl;
const char *name;
} named_ctx_hash_funcs_vtbl_t;
static const named_ctx_hash_funcs_vtbl_t predefined_ctx_hash_funcs[] = {
RECORD_HASH(SHA1),
RECORD_HASH(SHA224),
RECORD_HASH(SHA256),
RECORD_HASH(SHA384),
RECORD_HASH(SHA512),
};
static const ctx_hash_funcs_vtbl_t null_vtbl = {0, NULL, NULL, NULL};
const ctx_hash_funcs_vtbl_t *select_ctx_hash_func(const char *name)
{
const size_t N = 128;
for (size_t i = 0; i < nhash; i++) {
if (strncmp(name, predefined_ctx_hash_funcs[i].name, N) == 0) {
return &predefined_ctx_hash_funcs[i].vtbl;
}
}
errno = EINVAL;
return &null_vtbl;
}
errno_t hash_init(ctx_hash_func_t *ctx, const char *name)
{
assert(ctx != NULL);
assert(ctx->ctx == NULL);
if ((ctx->vtbl = select_ctx_hash_func(name))->init == NULL) {
return E_INVAL;
}
if ((ctx->ctx = malloc(sizeof(ctx_sha_t))) == NULL) {
return E_NOMEM;
}
if (ctx->vtbl->init(ctx) != E_OK) {
free(ctx->ctx);
return E_FAIL;
}
return E_OK;
}
errno_t hash_update(ctx_hash_func_t *ctx, const void *data, size_t len)
{
assert(ctx != NULL);
assert(data != NULL);
assert(ctx->vtbl->update != NULL);
if (ctx->vtbl->update(ctx, data, len) != E_OK) {
return E_FAIL;
}
return E_OK;
}
errno_t hash_final(ctx_hash_func_t *ctx, unsigned char *md)
{
assert(ctx != NULL);
assert(ctx->vtbl->final != NULL);
int status = E_OK;
if (md != NULL) {
status = ctx->vtbl->final(ctx, md);
}
free(ctx->ctx);
if (status != E_OK) {
return E_FAIL;
} else {
return E_OK;
}
}
errno_t hash_apply(const char *name, const char *in, size_t bytes, unsigned char *out)
{
ctx_hash_func_t h = make_ctx_hash();
int status;
if ((status = hash_init(&h, name)) != E_OK) {
return status;
}
if ((status = hash_update(&h, in, bytes)) != E_OK) {
return status;
}
return hash_final(&h, out);
}
#pragma once
#include <assert.h>
#include <stddef.h>
#include <stdbool.h>
#include <errno.h>
/**
* To detect that the C11 Annex K is used or not.
* */
#if !defined(__STDC_WANT_LIB_EXT1__) || (__STDC_WANT_LIB_EXT1__ < 1)
#pragma message "The C11 Annex K is not used."
typedef int errno_t;
#endif
typedef struct {
unsigned char *(*func)(const unsigned char *in, size_t butes, unsigned char *out);
size_t digest_bytes;
} hash_func_t;
hash_func_t select_hash_func(const char *name);
typedef struct {
const char *name;
const hash_func_t hash;
} named_hash_func_t;
extern const named_hash_func_t predefined_hash_funcs[];
extern const size_t nhash;
/* ==== */
struct ctx_hash_func_t;
typedef struct {
const size_t digest_bytes;
int (*init)(struct ctx_hash_func_t *);
int (*update)(struct ctx_hash_func_t *, const void *data, size_t len);
int (*final)(struct ctx_hash_func_t *, unsigned char *md);
} ctx_hash_funcs_vtbl_t;
typedef struct ctx_hash_func_t {
const ctx_hash_funcs_vtbl_t *vtbl;
void *ctx;
errno_t status;
} ctx_hash_func_t;
enum {
E_OK = 0,
E_FAIL,
E_INVAL,
E_NOMEM,
E_INTERNAL,
};
const ctx_hash_funcs_vtbl_t *select_ctx_hash_func(const char *name);
#define make_ctx_hash() {.ctx = NULL}
/**
* hash_init
* This function tries to allocate a memory.
* This allocation is succeeded if E_OK is returned.
* */
errno_t hash_init(ctx_hash_func_t *ctx, const char *name);
errno_t hash_update(ctx_hash_func_t *ctx, const void *data, size_t len);
errno_t hash_final(ctx_hash_func_t *ctx, unsigned char *md);
errno_t hash_apply(const char *name, const char *in, size_t bytes, unsigned char *out);
static inline size_t hash_digest_bytes(const ctx_hash_func_t *ctx)
{
assert(ctx != NULL);
assert(ctx->vtbl != NULL);
return ctx->vtbl->digest_bytes;
}
static inline bool hash_is_valid_ctx(const ctx_hash_func_t *ctx)
{
assert(ctx != NULL);
assert(ctx->ctx != NULL);
return ctx->ctx != NULL;
}
#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include "genhash.h"
#include "siphash.h"
static void print_chars(const unsigned char *cary, size_t n)
{
for (size_t i = 0; i < n; i++) {
printf("%02x ", cary[i]);
}
printf("\n");
}
static const size_t MAXLEN = 100;
static int test0(const char *name, const char *in)
{
puts(__func__);
const hash_func_t h = select_hash_func(name);
if (h.func) {
unsigned char md[h.digest_bytes];
h.func((const unsigned char *)in, strnlen(in, MAXLEN), md);
print_chars(md, h.digest_bytes);
} else {
perror("Not found hash function");
printf("Available hash functions:\n");
for (size_t i = 0; i < nhash; i++) {
printf("\t%s\n", predefined_hash_funcs[i].name);
}
return 1;
}
return 0;
}
static int test1(const char *name, const char *in)
{
puts(__func__);
ctx_hash_func_t h = make_ctx_hash();
hash_init(&h, name);
if (hash_is_valid_ctx(&h)) {
const char *cur = in;
size_t l;
while ((l = strnlen(cur, MAXLEN)) != 0) {
hash_update(&h, cur, l);
cur += l;
}
unsigned char md[hash_digest_bytes(&h)];
hash_final(&h, md);
print_chars(md, hash_digest_bytes(&h));
} else {
return 1;
}
return 0;
}
static int test2(void)
{
puts(__func__);
ctx_siphash_t h = make_ctx_siphash(0, 1);
ctx_siphash_t h2 = make_ctx_siphash_by_chs(((unsigned char[]){[15] = 0, 1}));
return 0;
}
int main(int argc, char **argv)
{
int retcode = 0;
if (argc > 2) {
retcode |= test0(argv[1], argv[2]);
retcode |= test1(argv[1], argv[2]);
retcode |= test2();
}
return retcode;
}
CPPFLAGS = -std=c11 -g -pedantic
CFLAGS = -Wall -Wextra -O4 -MMD -MP -D__STDC_WANT_LIB_EXT1__=1
LOADLIBES = -lcrypto
LDLIBS = libs.a
SRCS = $(shell ls -1 *.c)
TARGETS = libs.a hash.exe
ARCMEMS = $(filter-out $(addsuffix .o,$(basename $(filter %.exe,$(TARGETS)))),$(SRCS:%.c=%.o))
.PHONY: all clean check
.PRECIOUS: %.o
all: $(TARGETS)
check:
for name in SHA1 SHA224 SHA256 SHA384 SHA512; do \
valgrind -q --error-exitcode=1 ./hash.exe $$name "$(shell date)"; \
done
clean:
$(RM) -r $(TARGETS) *~ .*~ *.o *.d *.dSYM
%.exe: %.o
$(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@
libs.a: $(ARCMEMS)
$(RM) $@
$(AR) $(ARFLAGS) $@ $^
-include $(SRCS:%.c=%.d)
#include "siphash.h"
errno_t siphash_init(ctx_hash_func_t *pCtx_)
{
ctx_siphash_t *pCtx = (ctx_hash_func_t *)pCtx_;
pCtx->base.ctx = (void *)pCtx->v;
return E_OK;
}
typedef union {
unsigned char md[sizeof(uint64_t)];
uint64_t v;
} siphash_output_t;
errno_t siphash_final(ctx_hash_func_t *pCtx_, unsigned char *md)
{
ctx_siphash_t *pCtx = (ctx_hash_func_t *)pCtx_;
pCtx->v[2] ^= 0xff;
// TODO: not implemented yet.
siphash_output_t r;
r.v = pCtx->v[0] ^ pCtx->v[1] ^ pCtx->v[2] ^ pCtx->v[3];
// TODO: endian.
for (size_t i = 0; i < sizeof(uint64_t); i++) {
md[i] = r.md[i];
}
return E_OK;
}
#pragma once
#include <stdint.h>
#include "genhash.h"
extern const ctx_hash_funcs_vtbl_t siphash_vtbl;
typedef struct {
ctx_hash_func_t base;
uint64_t v[4];
} ctx_siphash_t;
inline uint64_t uc8_to_ui64(const unsigned char cs[])
{
return *(uint64_t *)cs;
}
#define SIPHASH_INIT_V0 (0x736f6d6570736575ULL)
#define SIPHASH_INIT_V1 (0x646f72616e646f6dULL)
#define SIPHASH_INIT_V2 (0x6c7967656e657261ULL)
#define SIPHASH_INIT_V3 (0x7465646279746573ULL)
#define make_ctx_siphash(k0, k1) { \
{&siphash_vtbl, NULL, E_OK}, \
{ \
k0 ^ SIPHASH_INIT_V0, \
k1 ^ SIPHASH_INIT_V1, \
k0 ^ SIPHASH_INIT_V2, \
k1 ^ SIPHASH_INIT_V3, \
}, \
}
#define make_ctx_siphash_by_chs(chs) { \
{&siphash_vtbl, NULL, E_OK}, \
{ \
uc8_to_ui64((chs)) ^ SIPHASH_INIT_V0, \
uc8_to_ui64((chs + 8)) ^ SIPHASH_INIT_V1, \
uc8_to_ui64((chs)) ^ SIPHASH_INIT_V2, \
uc8_to_ui64((chs + 8)) ^ SIPHASH_INIT_V3, \
}, \
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment