voxabdo

Implementation of the voxabdo cipher in c
git clone git://git.noxz.tech/voxabdo
Log | Files | Refs

commit 2344ca3f8f43ee8154fada4c0a3d6d399fff0962
Author: Chris Noxz <chris@noxz.tech>
Date:   Sat, 29 Feb 2020 11:37:43 +0100

initial commit

Diffstat:
A.gitignore | 2++
AMakefile | 37+++++++++++++++++++++++++++++++++++++
Aconfig.mk | 8++++++++
Asha256.c | 159+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asha256.h | 34++++++++++++++++++++++++++++++++++
Avoxabdo.c | 290+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 530 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,2 @@ +voxabdo +*.o diff --git a/Makefile b/Makefile @@ -0,0 +1,37 @@ +.POSIX: + +include config.mk + +SRC = voxabdo.c sha256.c +OBJ = $(SRC:.c=.o) + +all: options voxabdo + +options: + @echo voxabdo build options: + @echo "VERSION = $(VERSION)" + @echo "PREFIX = $(PREFIX)" + @echo "CFLAGS = $(STCFLAGS)" + @echo "CC = $(CC)" + +.c.o: + $(CC) $(STCFLAGS) -c $< + +$(OBJ): config.mk + +voxabdo: $(OBJ) + $(CC) -o $@ $(OBJ) $(STCFLAGS) + +clean: + rm -f voxabdo $(OBJ) + +install: voxabdo + @echo installing executable to ${PREFIX}/bin + mkdir -p $(PREFIX)/bin + cp -f voxabdo $(PREFIX)/bin + chmod 755 $(PREFIX)/bin/voxabdo + +uninstall: + rm -f $(PREFIX)/bin/voxabdo + +.PHONY: all options clean install uninstall diff --git a/config.mk b/config.mk @@ -0,0 +1,8 @@ +VERSION = 0.0.1 + +PREFIX = /usr/local +MANPREFIX = $(PREFIX)/share/man + +CFLAGS = -Wall -pedantic -std=c99 +CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 +STCFLAGS = $(CPPFLAGS) $(CFLAGS) diff --git a/sha256.c b/sha256.c @@ -0,0 +1,159 @@ +/********************************************************************* +* Filename: sha256.c +* Author: Brad Conte (brad AT bradconte.com) +* Copyright: +* Disclaimer: This code is presented "as is" without any guarantees. +* Details: Implementation of the SHA-256 hashing algorithm. + SHA-256 is one of the three algorithms in the SHA2 + specification. The others, SHA-384 and SHA-512, are not + offered in this implementation. + Algorithm specification can be found here: +* http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf + This implementation uses little endian byte order. +*********************************************************************/ + +/*************************** HEADER FILES ***************************/ +#include <stdlib.h> +#include <memory.h> +#include "sha256.h" + +/****************************** MACROS ******************************/ +#define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b)))) +#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b)))) + +#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z))) +#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) +#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22)) +#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25)) +#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3)) +#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10)) + +/**************************** VARIABLES *****************************/ +static const WORD 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 +}; + +/*********************** FUNCTION DEFINITIONS ***********************/ +void sha256_transform(SHA256_CTX *ctx, const BYTE data[]) +{ + WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; + + for (i = 0, j = 0; i < 16; ++i, j += 4) + m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]); + for ( ; i < 64; ++i) + m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16]; + + a = ctx->state[0]; + b = ctx->state[1]; + c = ctx->state[2]; + d = ctx->state[3]; + e = ctx->state[4]; + f = ctx->state[5]; + g = ctx->state[6]; + h = ctx->state[7]; + + for (i = 0; i < 64; ++i) { + t1 = h + EP1(e) + CH(e,f,g) + k[i] + m[i]; + t2 = EP0(a) + MAJ(a,b,c); + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + } + + ctx->state[0] += a; + ctx->state[1] += b; + ctx->state[2] += c; + ctx->state[3] += d; + ctx->state[4] += e; + ctx->state[5] += f; + ctx->state[6] += g; + ctx->state[7] += h; +} + +void sha256_init(SHA256_CTX *ctx) +{ + ctx->datalen = 0; + ctx->bitlen = 0; + ctx->state[0] = 0x6a09e667; + ctx->state[1] = 0xbb67ae85; + ctx->state[2] = 0x3c6ef372; + ctx->state[3] = 0xa54ff53a; + ctx->state[4] = 0x510e527f; + ctx->state[5] = 0x9b05688c; + ctx->state[6] = 0x1f83d9ab; + ctx->state[7] = 0x5be0cd19; +} + +void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len) +{ + WORD i; + + for (i = 0; i < len; ++i) { + ctx->data[ctx->datalen] = data[i]; + ctx->datalen++; + if (ctx->datalen == 64) { + sha256_transform(ctx, ctx->data); + ctx->bitlen += 512; + ctx->datalen = 0; + } + } +} + +void sha256_final(SHA256_CTX *ctx, BYTE hash[]) +{ + WORD i; + + i = ctx->datalen; + + // Pad whatever data is left in the buffer. + if (ctx->datalen < 56) { + ctx->data[i++] = 0x80; + while (i < 56) + ctx->data[i++] = 0x00; + } + else { + ctx->data[i++] = 0x80; + while (i < 64) + ctx->data[i++] = 0x00; + sha256_transform(ctx, ctx->data); + memset(ctx->data, 0, 56); + } + + // Append to the padding the total message's length in bits and transform. + ctx->bitlen += ctx->datalen * 8; + ctx->data[63] = ctx->bitlen; + ctx->data[62] = ctx->bitlen >> 8; + ctx->data[61] = ctx->bitlen >> 16; + ctx->data[60] = ctx->bitlen >> 24; + ctx->data[59] = ctx->bitlen >> 32; + ctx->data[58] = ctx->bitlen >> 40; + ctx->data[57] = ctx->bitlen >> 48; + ctx->data[56] = ctx->bitlen >> 56; + sha256_transform(ctx, ctx->data); + + // Since this implementation uses little endian byte ordering and SHA uses + // big endian, + // reverse all the bytes when copying the final state to the output hash. + for (i = 0; i < 4; ++i) { + hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff; + hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff; + hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff; + hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff; + hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff; + hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff; + hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff; + hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff; + } +} diff --git a/sha256.h b/sha256.h @@ -0,0 +1,34 @@ +/********************************************************************* +* Filename: sha256.h +* Author: Brad Conte (brad AT bradconte.com) +* Copyright: +* Disclaimer: This code is presented "as is" without any guarantees. +* Details: Defines the API for the corresponding SHA1 implementation. +*********************************************************************/ + +#ifndef SHA256_H +#define SHA256_H + +/*************************** HEADER FILES ***************************/ +#include <stddef.h> + +/****************************** MACROS ******************************/ +#define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest + +/**************************** DATA TYPES ****************************/ +typedef unsigned char BYTE; // 8-bit byte +typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines + +typedef struct { + BYTE data[64]; + WORD datalen; + unsigned long long bitlen; + WORD state[8]; +} SHA256_CTX; + +/*********************** FUNCTION DECLARATIONS **********************/ +void sha256_init(SHA256_CTX *ctx); +void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len); +void sha256_final(SHA256_CTX *ctx, BYTE hash[]); + +#endif // SHA256_H diff --git a/voxabdo.c b/voxabdo.c @@ -0,0 +1,290 @@ +#include <getopt.h> +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <time.h> + +/* Brad Contes implementation of sha256 */ +#include "sha256.h" + +#define SEED_LENGTH 8 +#define RAND_K1 1103515245 +#define RAND_K2 12345 +/* just a simpler way of writing putc(c, stdout) */ +#define PUT(x) (putc((x), stdout)) + +void die(const char*, ...); /* print message and exit */ +int idx(const BYTE[], const BYTE); /* first index of byte in byte array */ +int uniq(const BYTE*); /* checks if bytes are unique in byte array */ +int msrand(int n); /* implementation of rand with support for multiple seeds */ +void seed_init(const BYTE*); /* initializes 8 seeds using a sha256 hash */ +void seed_password(const char*); /* converts a password into seeds */ +void generate_key(BYTE**); /* generates a 16 byte random key (based on seeds) */ +void encode(BYTE*[]); /* encodes input */ +void decode(BYTE*[]); /* decodes input */ + +static const char *usage = "usage: voxabdo [-dh] [-p password] [-x key -y key]"; +static int seed[SEED_LENGTH] = { 0 }; + +void +die(const char *msg, ...) +{ + va_list arguments; + + va_start(arguments, msg); + if (msg) + vfprintf(stderr, msg, arguments); + va_end(arguments); + + exit(1); +} + +int +idx(const BYTE key[], const BYTE c) +{ + int i; + + for (i = 0; i < 16; i++) { + if (key[i] == c) + return i; + } + + die("index out of bounds\n"); + return -1; /* will never be reached */ +} + +int +uniq(const BYTE *str) +{ + int i, j; + + for (i = 0; i < 16; i++) { + for (j = 0; j < 16; j++) { + if (i == j) + continue; + if (str[i] == str[j]) + return 0; + } + } + return 1; +} + +int +msrand(int n) +{ + unsigned int next = seed[n]; + int result; + + next *= RAND_K1; + next += RAND_K2; + result = (unsigned int) (next / 65536) % 2048; + + next *= RAND_K1; + next += RAND_K2; + result <<= 10; + result ^= (unsigned int) (next / 65536) % 1024; + + next *= RAND_K1; + next += RAND_K2; + result <<= 10; + result ^= (unsigned int) (next / 65536) % 1024; + + seed[n] = next; + return result; +} + +void +seed_init(const BYTE *hash) +{ + int i; + + for (i = (SHA256_BLOCK_SIZE - 1) * 8; i >= 0; i--) + seed[i / 32] |= (1U & (hash[i / 8] >> (i % 8))) << (i % 32); +} + +void +seed_password(const char *password) +{ + BYTE buffer[SHA256_BLOCK_SIZE]; + SHA256_CTX ctx; + + sha256_init(&ctx); + sha256_update(&ctx, (BYTE *)password, strlen(password)); + sha256_final(&ctx, buffer); + seed_init(buffer); +} + +void +generate_key(BYTE **key) +{ + int i, j, s; + BYTE c; + + (*key) = (BYTE*)malloc(16 * sizeof(char)); + + for (i = 0, s = 0; i < 16; i++) { + (*key)[i] = (c = (BYTE)(msrand(s++) % 256)); + + /* circulate seed counter */ + if (s == SEED_LENGTH) + s = 0; + + /* if first byte, don't check uniqueness */ + if (i == 0) + continue; + + /* check if byte allready exist, and if so redo generation (i--) */ + for (j = 0; j < i; j++) { + if ((*key)[j] == c) { + i--; + continue; + } + } + } +} + +void +encode(BYTE *key[]) +{ + int i; + BYTE c, x, y; + BYTE *ikey[2] = {NULL}; + + /* randomize seed */ + srand(time(NULL)); + for (i = 0; i < SEED_LENGTH; i++) + seed[i] = rand(); + + /* create inner keys */ + generate_key(&(ikey[0])); + generate_key(&(ikey[1])); + + /* output the inner keys encoded by the outer keys */ + for (i = 0; i < 32; i++) { + c = ikey[(i >= 16)][i % 16]; + PUT(key[0][c / 16]); + PUT(key[1][c % 16]); + } + + /* output input encoded first by the inner keys, then by the outer keys */ + for (;;) { + c = getc(stdin); + if (feof(stdin)) + break; + x = ikey[0][c / 16]; + PUT(key[0][x / 16]); + PUT(key[1][x % 16]); + + y = ikey[1][c % 16]; + PUT(key[0][y / 16]); + PUT(key[1][y % 16]); + } + free(ikey[0]); + free(ikey[1]); +} + +void +decode(BYTE *key[]) +{ + int i; + BYTE c, x, y; + BYTE ikey[2][16] = {0}; + + for (i = 0;; i++) { + c = getc(stdin); + if (feof(stdin)) + break; + if (i < 64) { /* extract inner keys */ + ikey[i >= 32][(i / 2) % 16] += + (i % 2 == 0) + ? idx(key[0], c) * 16 + : idx(key[1], c); + continue; + } + if (i == 64 && (!uniq(ikey[0]) || !uniq(ikey[1]))) + die("keys are not unique\n"); + switch (i % 4) { /* extract all parts of input and decode it */ + case 0: + x = idx(key[0], c) * 16; + break; + case 1: + x += idx(key[1], c); + break; + case 2: + y = idx(key[0], c) * 16; + break; + case 3: + y += idx(key[1], c); + PUT(idx(ikey[0], x) * 16 + idx(ikey[1], y)); + break; + } + } +} + +int +main(int argc, char *argv[]) +{ + char c; + char *password = NULL; + BYTE *key[2] = {NULL}; + int doencode = 1; + + while ((c = getopt(argc, argv, "dhp:x:y:")) != -1) { + switch (c) { + case 'd': + doencode = 0; + break; + case 'p': + password = (char*)malloc((strlen(optarg) + 1) * sizeof(char)); + memmove(password, optarg, strlen(optarg)); + break; + case 'x': + case 'y': + if (strlen(optarg) != 16) + die("The '%c' key must contain exactly 16 bytes\n", c); + key[c == 'y'] = (BYTE*)malloc(16 * sizeof(char)); + memmove(key[c == 'y'], optarg, 16); + if (!uniq(key[c == 'y'])) + die("All bytes in the '%c' key must be unique\n", c); + break; + case 'h': + fprintf(stderr, "%s\n", usage); + fprintf(stderr, " -d decode input\n"); + fprintf(stderr, " -h print this help text and exit\n"); + fprintf(stderr, " -p PASSWORD specify a PASSWORD\n"); + fprintf(stderr, " -x KEY specify 16 unique bytes as key\n"); + fprintf(stderr, " -y KEY specify 16 unique bytes as key\n"); + exit(0); + break; + default: + fprintf(stderr, "%s\n", usage); + exit(1); + break; + } + } + + /* validate input */ + if (password && (key[0] || key[1])) + die("You cannot specify both password and keys\n"); + if (!password && !(key[0] || key[1])) + die("You must specify either a password or keys\n"); + if (!password && (!key[0] || !key[1])) + die("You must specify both 'x' and 'y' keys\n"); + + if (password) { + seed_password(password); + generate_key(&(key[0])); + generate_key(&(key[1])); + free(password); + } + + if (doencode) + encode(key); + else + decode(key); + + free(key[0]); + free(key[1]); + return 0; +}