commit: 2344ca3f8f43ee8154fada4c0a3d6d399fff0962
parent:
author: Chris Noxz <chris@noxz.tech>
date: Sat, 29 Feb 2020 11:37:43 +0100
initial commit
6 files changed, 530 insertions(+)
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;
+}