commit 97a78b10a3e71f69a7b3ef9ae99c9985d9f27c23
Author: Santtu Lakkala <inz@inz.fi>
Date: Tue, 9 Mar 2021 09:52:15 +0200
Initial import
Diffstat:
A | .gitmodules | | | 3 | +++ |
A | Makefile | | | 20 | ++++++++++++++++++++ |
A | arg.h | | | 50 | ++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | main.c | | | 694 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | sha1.c | | | 126 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | sha1.h | | | 23 | +++++++++++++++++++++++ |
A | sha256.c | | | 170 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | sha256.h | | | 39 | +++++++++++++++++++++++++++++++++++++++ |
A | sha512.c | | | 183 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | sha512.h | | | 40 | ++++++++++++++++++++++++++++++++++++++++ |
A | tiny-AES-c | | | 1 | + |
11 files changed, 1349 insertions(+), 0 deletions(-)
diff --git a/.gitmodules b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "tiny-AES-c"]
+ path = tiny-AES-c
+ url = https://github.com/kokke/tiny-AES-c
diff --git a/Makefile b/Makefile
@@ -0,0 +1,20 @@
+CFLAGS := -g -W -Wall -std=c99
+AES_CFLAGS += -DECB=0 -DCBC=1 -DCTR=0 -DAES256=1
+SOURCES := sha1.c sha256.c sha512.c tiny-AES-c/aes.c main.c util.c
+OBJS := $(patsubst %.c,%.o,$(SOURCES))
+TEST_SOURCES := sha1.c sha256.c sha512.c util.c test.c
+TEST_OBJS := $(patsubst %.c,%.o,$(TEST_SOURCES))
+
+all: totp
+
+totp: $(OBJS)
+ $(CC) -o $@ $(OBJS) $(LDFLAGS)
+
+test: $(TEST_OBJS);
+ $(CC) -o $@ $(TEST_OBJS) $(LDFLAGS)
+
+%.o: %.c
+ $(CC) -c $< -o $@ $(CFLAGS) $(AES_CFLAGS)
+
+clean:
+ rm $(OBJS)
diff --git a/arg.h b/arg.h
@@ -0,0 +1,50 @@
+/*
+ * Copy me if you can.
+ * by 20h
+ */
+
+#ifndef ARG_H__
+#define ARG_H__
+
+extern char *argv0;
+
+/* use main(int argc, char *argv[]) */
+#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\
+ argv[0] && argv[0][0] == '-'\
+ && argv[0][1];\
+ argc--, argv++) {\
+ char argc_;\
+ char **argv_;\
+ int brk_;\
+ if (argv[0][1] == '-' && argv[0][2] == '\0') {\
+ argv++;\
+ argc--;\
+ break;\
+ }\
+ int i_;\
+ for (i_ = 1, brk_ = 0, argv_ = argv;\
+ argv[0][i_] && !brk_;\
+ i_++) {\
+ if (argv_ != argv)\
+ break;\
+ argc_ = argv[0][i_];\
+ switch (argc_)
+
+#define ARGEND }\
+ }
+
+#define ARGC() argc_
+
+#define EARGF(x) ((argv[0][i_+1] == '\0' && argv[1] == NULL)?\
+ ((x), abort(), (char *)0) :\
+ (brk_ = 1, (argv[0][i_+1] != '\0')?\
+ (&argv[0][i_+1]) :\
+ (argc--, argv++, argv[0])))
+
+#define ARGF() ((argv[0][i_+1] == '\0' && argv[1] == NULL)?\
+ (char *)0 :\
+ (brk_ = 1, (argv[0][i_+1] != '\0')?\
+ (&argv[0][i_+1]) :\
+ (argc--, argv++, argv[0])))
+
+#endif
diff --git a/main.c b/main.c
@@ -0,0 +1,694 @@
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <fnmatch.h>
+#include <inttypes.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <arpa/inet.h>
+#include <sys/stat.h>
+
+#include "sha1.h"
+#include "sha256.h"
+#include "sha512.h"
+#include "tiny-AES-c/aes.h"
+#include "arg.h"
+#include "util.h"
+
+#define SECRET_DB_PATH ".local/share/totp"
+#define SECRET_DB_FILE "secrets.db"
+#define SECRET_DB_NEW_SUFFIX ".new"
+
+char *argv0;
+
+enum digest {
+ DIGEST_SHA1 = 0,
+ DIGEST_SHA224,
+ DIGEST_SHA256,
+ DIGEST_SHA384,
+ DIGEST_SHA512,
+};
+
+static const char *digest_names[] = {
+ "SHA1",
+ "SHA224",
+ "SHA256",
+ "SHA384",
+ "SHA512",
+};
+
+static void (*digest_hmacs[])(const void *key, size_t keylen,
+ const void *data, size_t datalen,
+ void *h) = {
+ sha1_hmac,
+ sha224_hmac,
+ sha256_hmac,
+ sha384_hmac,
+ sha512_hmac,
+};
+
+static size_t digest_sizes[] = {
+ SHA1_HASHSIZE,
+ SHA224_HASHSIZE,
+ SHA256_HASHSIZE,
+ SHA384_HASHSIZE,
+ SHA512_HASHSIZE,
+};
+
+uint8_t get_digest(const char *s, size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < sizeof(digest_names) / sizeof(*digest_names); i++)
+ if (!strncmp(s, digest_names[i], len) &&
+ !digest_names[i][len])
+ return i;
+
+ fprintf(stderr, "Unknown digest \"%.*s\", assuming %s\n",
+ (int)len, s, digest_names[DIGEST_SHA1]);
+ return DIGEST_SHA1;
+}
+
+void print_base32(const uint8_t *buffer, size_t len)
+{
+ const char *chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
+ uint16_t v = 0;
+ size_t b = 0;
+
+ while (len--) {
+ v = v << 8 | *buffer++;
+ b += 8;
+ while (b >= 5) {
+ printf("%c", chars[(v >> (b - 5)) & 31]);
+ b -= 5;
+ }
+ }
+ if (b)
+ printf("%c", chars[(v << (5 - b)) & 31]);
+}
+
+char *_if_prefix(char *s, const char *prefix, size_t prefixlen)
+{
+ if (strncmp(s, prefix, prefixlen))
+ return NULL;
+ return s + prefixlen;
+}
+
+#define if_prefix(s, p) _if_prefix(s, p, sizeof(p) - 1)
+
+struct header {
+ uint8_t magic[4];
+ uint8_t version;
+};
+
+bool verify_db(int fd, struct AES_ctx *c)
+{
+ uint8_t rbuf[AES_BLOCKLEN];
+ int r;
+ size_t rused = 0;
+ struct header *h;
+
+ while ((r = read(fd, rbuf + rused, sizeof(rbuf) - rused)) > 0)
+ rused += r;
+
+ if (rused < sizeof(rbuf))
+ return false;
+
+ AES_CBC_decrypt_buffer(c, rbuf, sizeof(rbuf));
+ h = (struct header *)(rbuf + rbuf[0] % (sizeof(rbuf) - sizeof(*h) - 1) + 1);
+
+ if (h->magic[0] != 'T' ||
+ h->magic[1] != 'O' ||
+ h->magic[2] != 'T' ||
+ h->magic[3] != 'P' ||
+ h->version != 1)
+ croak("Secret database decryption failed, check passphrase");
+
+ return true;
+}
+
+void write_header(int fd, struct AES_ctx *c)
+{
+ uint8_t wbuf[AES_BLOCKLEN];
+ int w;
+ size_t written = 0;
+ size_t i;
+ struct header *h;
+
+ for (i = 0; i < sizeof(wbuf); i++)
+ wbuf[i] = rand();
+
+ h = (struct header *)(wbuf + wbuf[0] % (sizeof(wbuf) - sizeof(*h) - 1) + 1);
+
+ h->magic[0] = 'T';
+ h->magic[1] = 'O';
+ h->magic[2] = 'T';
+ h->magic[3] = 'P';
+ h->version = 1;
+
+ AES_CBC_encrypt_buffer(c, wbuf, sizeof(wbuf));
+
+ while (written < sizeof(wbuf) && (w = write(fd, wbuf + written, sizeof(wbuf) - written)) > 0)
+ written += w;
+}
+
+struct totpkey {
+ uint64_t t0;
+ uint8_t digest;
+ uint8_t digits;
+ uint8_t period;
+ uint8_t keylen;
+ uint8_t desclen;
+ uint8_t issuerlen;
+ uint8_t filler1;
+ uint8_t filler2;
+};
+
+void read_keys(int fd, struct AES_ctx *c,
+ void (*key_cb)(uint8_t digest,
+ uint8_t digits,
+ uint8_t period,
+ time_t t0,
+ const uint8_t *key, size_t keylen,
+ const char *desc, size_t desclen,
+ const char *issuer, size_t issuerlen,
+ void *data),
+ void *cb_data)
+{
+ uint8_t decbuf[512];
+ uint8_t rbuf[AES_BLOCKLEN];
+ size_t dused = 0;
+ size_t rused = 0;
+ int r;
+
+ while ((r = read(fd, rbuf + rused, sizeof(rbuf) - rused)) > 0) {
+ struct totpkey *kh = (struct totpkey *)decbuf;
+ if ((rused += r) < sizeof(rbuf))
+ continue;
+ AES_CBC_decrypt_buffer(c, rbuf, sizeof(rbuf));
+ if (dused + sizeof(rbuf) >= sizeof(decbuf))
+ break;
+ memcpy(decbuf + dused, rbuf, sizeof(rbuf));
+ rused = 0;
+ dused += sizeof(rbuf);
+
+ if (dused < sizeof(*kh) +
+ kh->keylen + kh->desclen + kh->issuerlen)
+ continue;
+
+ key_cb(kh->digest,
+ kh->digits,
+ kh->period,
+ _ntohll(kh->t0),
+ decbuf + sizeof(*kh), kh->keylen,
+ (const char *)(decbuf + sizeof(*kh) + kh->keylen),
+ kh->desclen,
+ (const char *)(decbuf + sizeof(*kh) + kh->keylen + kh->desclen),
+ kh->issuerlen,
+ cb_data);
+ dused = 0;
+ }
+}
+
+int write_key(int fd, struct AES_ctx *c,
+ uint8_t digest,
+ uint8_t digits,
+ uint8_t period,
+ time_t t0,
+ const uint8_t *key, size_t keylen,
+ const char *desc, size_t desclen,
+ const char *issuer, size_t issuerlen)
+{
+ size_t ksz = sizeof(struct totpkey) + keylen + desclen + issuerlen;
+ size_t i;
+ int w;
+
+ ksz += AES_BLOCKLEN - 1 - ((ksz - 1) % AES_BLOCKLEN);
+
+ if (keylen > UINT8_MAX || desclen > UINT8_MAX)
+ return -EINVAL;
+
+ uint8_t buffer[ksz];
+ memcpy(buffer, &(struct totpkey){
+ .t0 = _htonll(t0),
+ .digest = digest,
+ .digits = digits,
+ .period = period,
+ .keylen = keylen,
+ .desclen = desclen,
+ .issuerlen = issuerlen }, sizeof(struct totpkey));
+ memcpy(buffer + sizeof(struct totpkey), key, keylen);
+ memcpy(buffer + sizeof(struct totpkey) + keylen, desc, desclen);
+ memcpy(buffer + sizeof(struct totpkey) + keylen + desclen, issuer, issuerlen);
+ memset(buffer + sizeof(struct totpkey) + keylen + desclen + issuerlen, 0,
+ ksz - sizeof(struct totpkey) - keylen - desclen - issuerlen);
+
+ for (i = 0; i < ksz; i += AES_BLOCKLEN)
+ AES_CBC_encrypt_buffer(c, buffer + i, AES_BLOCKLEN);
+ i = 0;
+
+ while ((w = write(fd, buffer + i, ksz - i)) > 0)
+ i += w;
+
+ if (w < 0)
+ return -errno;
+ return i != ksz;
+}
+
+void print_key(uint8_t digest,
+ uint8_t digits,
+ uint8_t period,
+ time_t t0,
+ const uint8_t *key, size_t keylen,
+ const char *desc, size_t desclen,
+ const char *issuer, size_t issuerlen,
+ void *data)
+{
+ (void)digest;
+ (void)digits;
+ (void)period;
+ (void)key;
+ (void)keylen;
+ (void)issuer;
+ (void)issuerlen;
+ (void)t0;
+
+ (void)data;
+
+ printf("%.*s by %.*s\n", (int)desclen, desc, (int)issuerlen, issuer);
+}
+
+static void print_uriencode(const char *buf, size_t len, bool getarg)
+{
+ const char *escape = ":/@+% &?";
+ while (len && *buf) {
+ size_t pass = strncspn(buf, len, escape);
+ printf("%.*s", (int)pass, buf);
+ buf += pass;
+ len -= pass;
+
+ while (len && *buf && strchr(escape, *buf)) {
+ if (*buf == ' ' && getarg)
+ printf("+");
+ else
+ printf("%%%02" PRIx8, *(uint8_t *)buf);
+ buf++;
+ len--;
+ }
+ }
+}
+
+void print_keyuri(uint8_t digest,
+ uint8_t digits,
+ uint8_t period,
+ time_t t0,
+ const uint8_t *key, size_t keylen,
+ const char *desc, size_t desclen,
+ const char *issuer, size_t issuerlen,
+ void *data)
+{
+ (void)t0;
+ (void)data;
+ printf("otpauth://totp/");
+ print_uriencode(desc, desclen, false);
+ printf("?secret=");
+ print_base32(key, keylen);
+ if (issuerlen) {
+ printf("&issuer=");
+ print_uriencode(issuer, issuerlen, true);
+ }
+ printf("&algorithm=%s&digits=%" PRIu8 "&period=%" PRIu8 "\n",
+ digest_names[digest],
+ digits,
+ period);
+}
+
+struct generate_data {
+ const char *filter;
+ bool found;
+};
+
+void generate_token(uint8_t digest,
+ uint8_t digits,
+ uint8_t period,
+ time_t t0,
+ const uint8_t *key, size_t keylen,
+ const char *desc, size_t desclen,
+ const char *issuer, size_t issuerlen,
+ void *data)
+{
+ struct generate_data *d = data;
+ uint32_t modulo = 1;
+ uint8_t i;
+ char descbuf[desclen + 1];
+
+ (void)issuer;
+ (void)issuerlen;
+
+ memcpy(descbuf, desc, desclen);
+ descbuf[desclen] = '\0';
+
+ if (fnmatch(d->filter, descbuf, FNM_NOESCAPE))
+ return;
+
+ d->found = true;
+ for (i = 0; i < digits; i++)
+ modulo *= 10;
+
+ printf("%0*" PRIu32 "\n", (int)digits,
+ totp(key, keylen, time(NULL), period, t0, digest_hmacs[digest], digest_sizes[digest]) % modulo);
+}
+
+struct write_filter_data {
+ int fd;
+ const char *filter;
+ struct AES_ctx *c;
+};
+
+void write_filter_key(uint8_t digest,
+ uint8_t digits,
+ uint8_t period,
+ time_t t0,
+ const uint8_t *key, size_t keylen,
+ const char *desc, size_t desclen,
+ const char *issuer, size_t issuerlen,
+ void *data)
+{
+ struct write_filter_data *d = data;
+
+ if (d->filter) {
+ char descbuf[desclen + 1];
+
+ memcpy(descbuf, desc, desclen);
+ descbuf[desclen] = '\0';
+
+ if (!fnmatch(d->filter, descbuf, FNM_NOESCAPE))
+ return;
+ }
+
+ write_key(d->fd, d->c, digest, digits, period, t0,
+ key, keylen,
+ desc, desclen,
+ issuer, issuerlen);
+}
+
+enum cmd {
+ CMD_TOK,
+ CMD_LIST,
+ CMD_ADD,
+ CMD_DEL,
+ CMD_EXP
+};
+
+void usage()
+{
+ fprintf(stderr,
+ "Usage: totp [OPTIONS]\n"
+ "-l\tlist known secrets\n"
+ "-a <uri>\tadd uri to secrets\n"
+ "-d <filter>\tremove secrets matching filter\n"
+ "-t <filter>\tgenerate tokens for secrets matching filter\n"
+ "-e\texport secrets\n");
+ exit(1);
+}
+
+static inline char dehex(const char *s)
+{
+ if ((*s < '0' ||
+ (*s > '9' && (*s & ~0x20) < 'A') ||
+ (*s & ~0x20) > 'F') ||
+ (s[1] < '0' ||
+ (s[1] > '9' && (s[1] & ~0x20) < 'A') ||
+ (s[1] & ~0x20) > 'F'))
+ return '?';
+ return (*s < 'A' ? *s - '0' : (*s & ~0x20) - 'A' + 10) << 4 |
+ (s[1] < 'A' ? s[1] - '0' : (s[1] & ~0x20) - 'A' + 10);
+}
+
+static size_t uridecode(char *buf, size_t len, bool getarg)
+{
+ char *w = buf;
+ const char *r = buf;
+
+ while (r - buf < (ptrdiff_t)len) {
+ if (*r == '%') {
+ if (r - buf + 2 >= (ptrdiff_t)len)
+ break;
+ *w++ = dehex(++r);
+ r += 2;
+ } else if (getarg && *r == '+') {
+ *w++ = ' ';
+ r++;
+ } else
+ *w++ = *r++;
+ }
+
+ return w - buf;
+}
+
+int main(int argc, char *argv[])
+{
+ int fd;
+ int r;
+ struct sha1 d;
+ enum cmd cmd;
+ char *totpuri;
+ const char *key = NULL;
+ const char *keyfile = NULL;
+ const char *keyquery = NULL;
+ char *secretfile = NULL;
+ char *newsecretfile = NULL;
+ bool free_secretfile = true;
+ uint8_t keybuf[AES_KEYLEN + AES_BLOCKLEN];
+ size_t keylen = 0;
+ struct generate_data gd = { NULL, false };
+ char *t;
+
+ ARGBEGIN {
+ case 'l':
+ cmd = CMD_LIST;
+ break;
+ case 'a':
+ cmd = CMD_ADD;
+ totpuri = EARGF(usage());
+ break;
+ case 'd':
+ cmd = CMD_DEL;
+ keyquery = EARGF(usage());
+ break;
+ case 't':
+ cmd = CMD_TOK;
+ keyquery = EARGF(usage());
+ break;
+ case 'e':
+ cmd = CMD_EXP;
+ break;
+ case 'k':
+ key = EARGF(usage());
+ break;
+ case 'K':
+ keyfile = EARGF(usage());
+ break;
+ case 'f':
+ secretfile = EARGF(usage());
+ free_secretfile = false;
+ break;
+ default:
+ usage();
+ break;
+
+ } ARGEND
+
+ sha1_init(&d);
+
+ if (key) {
+ sha1_update(&d, key, strlen(key));
+ } else if (keyfile) {
+ fd = open(keyfile, O_RDONLY);
+ if (fd < 0)
+ exit(1);
+ while ((r = read(fd, d.buffer + (d.len & 63),
+ sizeof(d.buffer) - (d.len & 63))) > 0) {
+ d.len += r;
+ if (!(d.len & 63))
+ sha1_update(&d, d.buffer, sizeof(d.buffer));
+ }
+ close(fd);
+ }
+
+ sha1_finish(&d);
+
+ while (keylen + sizeof(d.h) < sizeof(keybuf)) {
+ memcpy(keybuf + keylen, d.h, sizeof(d.h));
+ memcpy(d.buffer, d.h, sizeof(d.h));
+ sha1_init(&d);
+ sha1_update(&d, d.buffer, sizeof(d.h));
+ sha1_finish(&d);
+ keylen += sizeof(d.h);
+ }
+ memcpy(keybuf + keylen, d.h, sizeof(keybuf) - keylen);
+
+ struct AES_ctx c;
+ AES_init_ctx_iv(&c,
+ (uint8_t *)keybuf, (uint8_t *)keybuf + AES_KEYLEN);
+
+ struct AES_ctx wc;
+ AES_init_ctx_iv(&wc,
+ (uint8_t *)keybuf, (uint8_t *)keybuf + AES_KEYLEN);
+
+ int wfd;
+
+ srand(time(NULL));
+
+ if (!secretfile) {
+ const char *home = getenv("HOME");
+ secretfile = malloc(strlen(home) + sizeof(SECRET_DB_PATH) + sizeof(SECRET_DB_FILE) + 1);
+ sprintf(secretfile, "%s/%s/%s", home, SECRET_DB_PATH, SECRET_DB_FILE);
+ }
+
+ newsecretfile = malloc(strlen(secretfile) + sizeof(SECRET_DB_NEW_SUFFIX));
+ sprintf(newsecretfile, "%s%s", secretfile, SECRET_DB_NEW_SUFFIX);
+
+ for (t = strtok(secretfile + 1, "/"); (t = strtok(NULL, "/")); ) {
+ if (mkdir(secretfile, 0700) && errno != EEXIST)
+ croak("Could not create secret db dir: %s", strerror(errno));
+ t[-1] = '/';
+ }
+
+ switch (cmd) {
+ case CMD_LIST:
+ free(newsecretfile);
+ fd = open(secretfile, O_RDONLY);
+ if (free_secretfile)
+ free(secretfile);
+ if (fd < 0)
+ break;
+ if (!verify_db(fd, &c))
+ croak("Unable to open database, check passphrase");
+ read_keys(fd, &c, print_key, NULL);
+ close(fd);
+ break;
+
+ case CMD_ADD: {
+ size_t kl = 0;
+ size_t dl = 0;
+ char *i;
+ char *key;
+ char *desc;
+ uint8_t digest = DIGEST_SHA1;
+ uint8_t digits = 6;
+ uint8_t period = 30;
+ uint8_t issuerlen = 0;
+ time_t t0 = 0;
+ char *issuer;
+
+ if (!(desc = if_prefix(totpuri, "otpauth://totp/")))
+ usage();
+
+ i = strchr(desc, '?');
+ if (!i)
+ usage();
+
+ dl = uridecode(desc, i - desc, false);
+
+ while (*i++) {
+ char *v;
+ if ((v = if_prefix(i, "secret="))) {
+ i = v + strcspn(v, "&");
+ kl = debase32(key = v, i - v);
+ } else if ((v = if_prefix(i, "digits="))) {
+ digits = strtoul(v, &i, 10);
+ } else if ((v = if_prefix(i, "period="))) {
+ period = strtoul(v, &i, 10);
+ } else if ((v = if_prefix(i, "issuer="))) {
+ i = v + strcspn(v, "&");
+ issuerlen = uridecode(issuer = v, i - v, true);
+ } else if ((v = if_prefix(i, "algorithm="))) {
+ i = v + strcspn(v, "&");
+ digest = get_digest(v, i - v);
+ } else {
+ i += strcspn(i, "&");
+ }
+ }
+
+ fd = open(secretfile, O_RDONLY, 0600);
+ if (fd >= 0)
+ verify_db(fd, &c);
+
+ wfd = open(newsecretfile,
+ O_WRONLY | O_TRUNC | O_CREAT, 0600);
+ write_header(wfd, &wc);
+ if (fd >= 0) {
+ read_keys(fd, &c, write_filter_key,
+ &(struct write_filter_data){
+ .fd = wfd, .c = &wc });
+ close(fd);
+ }
+ write_key(wfd, &wc,
+ digest, digits, period, t0,
+ (uint8_t *)key, kl, desc, dl,
+ issuer, issuerlen);
+ close(wfd);
+
+ rename(newsecretfile, secretfile);
+ free(newsecretfile);
+ free(secretfile);
+
+ break;
+ }
+
+ case CMD_TOK:
+ free(newsecretfile);
+ fd = open(secretfile, O_RDONLY);
+ free(secretfile);
+ if (fd >= 0) {
+ verify_db(fd, &c);
+ gd.filter = keyquery;
+ read_keys(fd, &c, generate_token, &gd);
+ close(fd);
+ }
+ if (!gd.found)
+ croak("No secrets matching filter found");
+ break;
+
+ case CMD_DEL: {
+ fd = open(secretfile, O_RDONLY);
+ if (fd < 0)
+ exit(1);
+ wfd = open(newsecretfile,
+ O_WRONLY | O_TRUNC | O_CREAT, 0600);
+ verify_db(fd, &c);
+ write_header(wfd, &wc);
+ read_keys(fd, &c, write_filter_key,
+ &(struct write_filter_data){
+ .fd = wfd, .filter = keyquery,
+ .c = &wc
+ });
+ close(wfd);
+ close(fd);
+ rename(newsecretfile, secretfile);
+ free(newsecretfile);
+ free(secretfile);
+ break;
+
+ case CMD_EXP:
+ free(newsecretfile);
+ fd = open(secretfile, O_RDONLY);
+ free(secretfile);
+ if (fd < 0)
+ break;
+ verify_db(fd, &c);
+ read_keys(fd, &c, print_keyuri, NULL);
+ close(fd);
+ break;
+ }
+ }
+
+ return 0;
+}
diff --git a/sha1.c b/sha1.c
@@ -0,0 +1,126 @@
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <arpa/inet.h>
+
+#include "sha1.h"
+#include "util.h"
+
+static inline uint32_t rotl32(uint32_t x, uint8_t n)
+{
+ return x << n | x >> (32 - n);
+}
+
+static inline void add5(uint32_t *dest, const uint32_t *src)
+{
+ size_t i;
+
+ for (i = 0; i < 5; i++)
+ dest[i] += src[i];
+}
+
+static inline void rotmod5(uint32_t *a, uint32_t f, uint32_t k, uint32_t w)
+{
+ uint32_t t = rotl32(a[0], 5) + f + a[4] + k + w;
+ memmove(a + 1, a, 4 * sizeof(*a));
+ a[2] = rotl32(a[2], 30);
+ a[0] = t;
+}
+
+static inline uint32_t getnw(uint32_t *w, size_t i)
+{
+ return w[i & 15] = rotl32(w[(i + 13) & 15] ^ w[(i + 8) & 15] ^ w[(i + 2) & 15] ^ w[i & 15], 1);
+}
+
+void sha1_init(struct sha1 *s)
+{
+ memcpy(s->h, (uint32_t[]){ 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 }, 5 * sizeof(*s->h));
+ s->len = 0;
+}
+
+static inline void _sha1_update(uint32_t *h, const void *data)
+{
+ const uint32_t k[4] = { 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6 };
+ const uint32_t *d = data;
+
+ uint32_t w[16];
+ size_t i;
+
+ uint32_t wr[5];
+ memcpy(wr, h, sizeof(wr));
+
+ for (i = 0; i < 16; i++)
+ rotmod5(wr, (wr[1] & wr[2]) | (~wr[1] & wr[3]), k[0], w[i] = ntohl(d[i]));
+ for (; i < 20; i++)
+ rotmod5(wr, (wr[1] & wr[2]) | (~wr[1] & wr[3]), k[0], getnw(w, i));
+ for (; i < 40; i++)
+ rotmod5(wr, wr[1] ^ wr[2] ^ wr[3], k[1], getnw(w, i));
+ for (; i < 60; i++)
+ rotmod5(wr, (wr[1] & wr[2]) | (wr[1] & wr[3]) | (wr[2] & wr[3]), k[2], getnw(w, i));
+ for (; i < 80; i++)
+ rotmod5(wr, wr[1] ^ wr[2] ^ wr[3], k[3], getnw(w, i));
+
+ add5(h, wr);
+}
+
+void sha1_update(struct sha1 *s, const void *data, size_t len)
+{
+ if ((s->len & 63) + len >= 64) {
+ const char *d = data;
+ if (s->len & 63) {
+ memcpy((uint8_t *)s->buffer + (s->len & 63), d, 64 - (s->len & 63));
+ _sha1_update(s->h, s->buffer);
+ d += 64 - (s->len & 63);
+ s->len += 64 - (s->len & 63);
+ len -= 64 - (s->len & 63);
+ }
+ while (len >= 64) {
+ _sha1_update(s->h, d);
+ d += 64;
+ s->len += 64;
+ len -= 64;
+ }
+ memmove(s->buffer, d, len);
+ } else {
+ memmove(s->buffer + (s->len & 63), data, len);
+ }
+ s->len += len;
+}
+
+void sha1_finish(struct sha1 *s)
+{
+ size_t i;
+
+ ((uint8_t *)s->buffer)[s->len & 63] = 0x80;
+ if ((s->len & 63) > 55) {
+ memset((uint8_t *)s->buffer + (s->len & 63) + 1, 0, 63 - (s->len & 63));
+ _sha1_update(s->h, s->buffer);
+ memset(s->buffer, 0, (s->len & 63) + 1);
+ } else {
+ memset((uint8_t *)s->buffer + (s->len & 63) + 1, 0, 55 - (s->len & 63));
+ }
+ s->buffer[14] = htonl(s->len >> 29);
+ s->buffer[15] = htonl(s->len << 3);
+ _sha1_update(s->h, s->buffer);
+
+ for (i = 0; i < sizeof(s->h) / sizeof(*s->h); i++)
+ s->h[i] = htonl(s->h[i]);
+}
+
+void sha1_hmac(const void *key, size_t keylen,
+ const void *data, size_t datalen,
+ void *h)
+{
+ hmac(key, keylen,
+ data, datalen,
+ (digest_init)sha1_init,
+ (digest_update)sha1_update,
+ (digest_finish)sha1_finish,
+ sizeof(struct sha1),
+ sizeof(((struct sha1 *)0)->buffer),
+ sizeof(((struct sha1 *)0)->h),
+ (ptrdiff_t)&((struct sha1 *)0)->buffer,
+ (ptrdiff_t)&((struct sha1 *)0)->h,
+ h);
+}
diff --git a/sha1.h b/sha1.h
@@ -0,0 +1,23 @@
+#ifndef SHA1_H
+#define SHA1_H
+
+#include <stdlib.h>
+#include <stdint.h>
+
+#define SHA1_HASHSIZE 20
+
+struct sha1 {
+ uint32_t buffer[16];
+ uint32_t h[5];
+ uint64_t len;
+};
+
+void sha1_init(struct sha1 *s);
+void sha1_update(struct sha1 *s, const void *data, size_t len);
+void sha1_finish(struct sha1 *s);
+
+void sha1_hmac(const void *key, size_t keylen,
+ const void *data, size_t datalen,
+ void *h);
+
+#endif
diff --git a/sha256.c b/sha256.c
@@ -0,0 +1,170 @@
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <arpa/inet.h>
+
+#include "sha256.h"
+#include "util.h"
+
+static inline uint32_t rotr32(uint32_t x, uint8_t n)
+{
+ return x >> n | x << (32 - n);
+}
+
+static inline void add8(uint32_t *dest, const uint32_t *src)
+{
+ size_t i;
+
+ for (i = 0; i < 8; i++)
+ dest[i] += src[i];
+}
+
+static inline void rotmod8(uint32_t *a, uint32_t k, uint32_t w)
+{
+ uint32_t t1 = a[7] + (rotr32(a[4], 6) ^ rotr32(a[4], 11) ^ rotr32(a[4], 25)) + ((a[4] & a[5]) ^ (~a[4] & a[6])) + k + w;
+ uint32_t t2 = (rotr32(a[0], 2) ^ rotr32(a[0], 13) ^ rotr32(a[0], 22)) + ((a[0] & a[1]) ^ (a[0] & a[2]) ^ (a[1] & a[2]));
+
+ memmove(a + 1, a, 7 * sizeof(*a));
+
+ a[4] += t1;
+ a[0] = t1 + t2;
+}
+
+static inline uint32_t getnw(uint32_t *w, size_t i)
+{
+ return w[i & 15] +=
+ (rotr32(w[(i + 1) & 15], 7) ^ rotr32(w[(i + 1) & 15], 18) ^ (w[(i + 1) & 15] >> 3)) +
+ w[(i + 9) & 15] +
+ (rotr32(w[(i + 14) & 15], 17) ^ rotr32(w[(i + 14) & 15], 19) ^ (w[(i + 14) & 15] >> 10));
+}
+
+void sha256_init(struct sha256 *s)
+{
+ memcpy(s->h, (uint32_t[]){ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 }, sizeof(s->h));
+ s->len = 0;
+}
+
+static inline void _sha256_update(uint32_t *h, const void *data)
+{
+ const uint32_t k[] = {
+ 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
+ };
+ const uint32_t *d = data;
+
+ uint32_t w[16];
+ size_t i;
+
+ uint32_t wr[8];
+ memcpy(wr, h, sizeof(wr));
+
+ for (i = 0; i < 16; i++)
+ rotmod8(wr, k[i], w[i] = ntohl(d[i]));
+
+ for (; i < 64; i++)
+ rotmod8(wr, k[i], getnw(w, i));
+
+ add8(h, wr);
+}
+
+void sha256_update(struct sha256 *s, const void *data, size_t len)
+{
+ if ((s->len & 63) + len >= 64) {
+ const char *d = data;
+ if (s->len & 63) {
+ memcpy(s->buffer + (s->len & 63), d, 64 - (s->len & 63));
+ _sha256_update(s->h, s->buffer);
+ d += 64 - (s->len & 63);
+ s->len += 64 - (s->len & 63);
+ len -= 64 - (s->len & 63);
+ }
+ while (len >= 64) {
+ _sha256_update(s->h, d);
+ d += 64;
+ s->len += 64;
+ len -= 64;
+ }
+ memmove(s->buffer, d, len);
+ } else {
+ memmove(s->buffer + (s->len & 63), data, len);
+ }
+ s->len += len;
+}
+
+void sha256_finish(struct sha256 *s)
+{
+ size_t i;
+
+ s->buffer[s->len & 63] = 0x80;
+ if ((s->len & 63) > 55) {
+ memset(s->buffer + (s->len & 63) + 1, 0, 63 - (s->len & 63));
+ _sha256_update(s->h, s->buffer);
+ memset(s->buffer, 0, (s->len & 63) + 1);
+ } else {
+ memset(s->buffer + (s->len & 63) + 1, 0, 55 - (s->len & 63));
+ }
+ ((uint32_t *)s->buffer)[14] = htonl(s->len >> 29);
+ ((uint32_t *)s->buffer)[15] = htonl(s->len << 3);
+ _sha256_update(s->h, s->buffer);
+
+ for (i = 0; i < sizeof(s->h) / sizeof(*s->h); i++)
+ s->h[i] = htonl(s->h[i]);
+}
+
+void sha224_init(struct sha224 *s)
+{
+ struct sha256 *s256 = (struct sha256 *)s;
+ memcpy(s256->h, (uint32_t[]){ 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4 }, sizeof(s256->h));
+ s->len = 0;
+}
+
+void sha224_update(struct sha224 *s, const void *data, size_t len)
+{
+ sha256_update((struct sha256 *)s, data, len);
+}
+
+void sha224_finish(struct sha224 *s)
+{
+ sha256_finish((struct sha256 *)s);
+}
+
+void sha256_hmac(const void *key, size_t keylen,
+ const void *data, size_t datalen,
+ void *h)
+{
+ hmac(key, keylen,
+ data, datalen,
+ (digest_init)sha256_init,
+ (digest_update)sha256_update,
+ (digest_finish)sha256_finish,
+ sizeof(struct sha256),
+ sizeof(((struct sha256 *)0)->buffer),
+ sizeof(((struct sha256 *)0)->h),
+ (ptrdiff_t)&((struct sha256 *)0)->buffer,
+ (ptrdiff_t)&((struct sha256 *)0)->h,
+ h);
+}
+
+void sha224_hmac(const void *key, size_t keylen,
+ const void *data, size_t datalen,
+ void *h)
+{
+ hmac(key, keylen,
+ data, datalen,
+ (digest_init)sha224_init,
+ (digest_update)sha224_update,
+ (digest_finish)sha224_finish,
+ sizeof(struct sha224),
+ sizeof(((struct sha224 *)0)->buffer),
+ sizeof(((struct sha224 *)0)->h),
+ (ptrdiff_t)&((struct sha224 *)0)->buffer,
+ (ptrdiff_t)&((struct sha224 *)0)->h,
+ h);
+}
diff --git a/sha256.h b/sha256.h
@@ -0,0 +1,39 @@
+#ifndef SHA256_H
+#define SHA256_H
+
+#include <stdlib.h>
+#include <stdint.h>
+
+#define SHA256_HASHSIZE 32
+#define SHA224_HASHSIZE 28
+
+struct sha256 {
+ uint8_t buffer[64];
+ uint32_t h[8];
+ uint64_t len;
+};
+
+struct sha224 {
+ uint8_t buffer[64];
+ uint32_t h[7];
+ uint32_t h7;
+ uint64_t len;
+};
+
+void sha256_init(struct sha256 *s);
+void sha256_update(struct sha256 *s, const void *data, size_t len);
+void sha256_finish(struct sha256 *s);
+
+void sha256_hmac(const void *key, size_t keylen,
+ const void *data, size_t datalen,
+ void *h);
+
+void sha224_init(struct sha224 *s);
+void sha224_update(struct sha224 *s, const void *data, size_t len);
+void sha224_finish(struct sha224 *s);
+
+void sha224_hmac(const void *key, size_t keylen,
+ const void *data, size_t datalen,
+ void *h);
+
+#endif
diff --git a/sha512.c b/sha512.c
@@ -0,0 +1,183 @@
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <arpa/inet.h>
+
+#include "sha512.h"
+#include "util.h"
+
+static inline uint64_t rotr64(uint64_t x, uint8_t n)
+{
+ return x >> n | x << (64 - n);
+}
+
+static inline void add8(uint64_t *dest, const uint64_t *src)
+{
+ size_t i;
+
+ for (i = 0; i < 8; i++)
+ dest[i] += src[i];
+}
+
+static inline void rotmod8(uint64_t *a, uint64_t k, uint64_t w)
+{
+ uint64_t t1 = a[7] + (rotr64(a[4], 14) ^ rotr64(a[4], 18) ^ rotr64(a[4], 41)) + ((a[4] & a[5]) ^ (~a[4] & a[6])) + k + w;
+ uint64_t t2 = (rotr64(a[0], 28) ^ rotr64(a[0], 34) ^ rotr64(a[0], 39)) + ((a[0] & a[1]) ^ (a[0] & a[2]) ^ (a[1] & a[2]));
+
+ memmove(a + 1, a, 7 * sizeof(*a));
+
+ a[4] += t1;
+ a[0] = t1 + t2;
+}
+
+static inline uint64_t getnw(uint64_t *w, size_t i)
+{
+ return w[i & 15] +=
+ (rotr64(w[(i + 1) & 15], 1) ^ rotr64(w[(i + 1) & 15], 8) ^ (w[(i + 1) & 15] >> 7)) +
+ w[(i + 9) & 15] +
+ (rotr64(w[(i + 14) & 15], 19) ^ rotr64(w[(i + 14) & 15], 61) ^ (w[(i + 14) & 15] >> 6));
+}
+
+void sha512_init(struct sha512 *s)
+{
+ memcpy(s->h, (uint64_t[]){
+ 0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
+ 0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179
+ }, sizeof(s->h));
+ s->len = 0;
+}
+
+static inline void _sha512_update(uint64_t *h, const void *data)
+{
+ const uint64_t k[] = {
+ 0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, 0x3956c25bf348b538,
+ 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, 0xd807aa98a3030242, 0x12835b0145706fbe,
+ 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235,
+ 0xc19bf174cf692694, 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
+ 0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5, 0x983e5152ee66dfab,
+ 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4, 0xc6e00bf33da88fc2, 0xd5a79147930aa725,
+ 0x06ca6351e003826f, 0x142929670a0e6e70, 0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed,
+ 0x53380d139d95b3df, 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b,
+ 0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30, 0xd192e819d6ef5218,
+ 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8, 0x19a4c116b8d2d0c8, 0x1e376c085141ab53,
+ 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8, 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373,
+ 0x682e6ff3d6b2b8a3, 0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
+ 0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b, 0xca273eceea26619c,
+ 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, 0x06f067aa72176fba, 0x0a637dc5a2c898a6,
+ 0x113f9804bef90dae, 0x1b710b35131c471b, 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc,
+ 0x431d67c49c100d4c, 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817
+ };
+ const uint64_t *d = data;
+
+ uint64_t w[16];
+ size_t i;
+
+ uint64_t wr[8];
+ memcpy(wr, h, sizeof(wr));
+
+ for (i = 0; i < sizeof(w) / sizeof(*w); i++)
+ rotmod8(wr, k[i], w[i] = _ntohll(d[i]));
+
+ for (; i < sizeof(k) / sizeof(*k); i++)
+ rotmod8(wr, k[i], getnw(w, i));
+
+ add8(h, wr);
+}
+
+void sha512_update(struct sha512 *s, const void *data, size_t len)
+{
+ if ((s->len & 127) + len >= 128) {
+ const char *d = data;
+ if (s->len & 128) {
+ memcpy(s->buffer + (s->len & 127), d, 128 - (s->len & 127));
+ _sha512_update(s->h, s->buffer);
+ d += 128 - (s->len & 127);
+ s->len += 128 - (s->len & 127);
+ len -= 128 - (s->len & 127);
+ }
+ while (len >= 128) {
+ _sha512_update(s->h, d);
+ d += 128;
+ s->len += 128;
+ len -= 128;
+ }
+ memmove(s->buffer, d, len);
+ } else {
+ memmove(s->buffer + (s->len & 127), data, len);
+ }
+ s->len += len;
+}
+
+void sha512_finish(struct sha512 *s)
+{
+ size_t i;
+
+ s->buffer[s->len & 127] = 0x80;
+ if ((s->len & 127) > 111) {
+ memset(s->buffer + (s->len & 127) + 1, 0, 127 - (s->len & 127));
+ _sha512_update(s->h, s->buffer);
+ memset(s->buffer, 0, (s->len & 127) + 1);
+ } else {
+ memset(s->buffer + (s->len & 127) + 1, 0, 119 - (s->len & 127));
+ }
+ ((uint64_t *)s->buffer)[15] = _htonll(s->len << 3);
+ _sha512_update(s->h, s->buffer);
+
+ for (i = 0; i < sizeof(s->h) / sizeof(*s->h); i++)
+ s->h[i] = _htonll(s->h[i]);
+}
+
+void sha384_init(struct sha384 *s)
+{
+ struct sha512 *s5 = (struct sha512 *)s;
+ memcpy(s5->h, (uint64_t[]){
+ 0xcbbb9d5dc1059ed8, 0x629a292a367cd507, 0x9159015a3070dd17, 0x152fecd8f70e5939,
+ 0x67332667ffc00b31, 0x8eb44a8768581511, 0xdb0c2e0d64f98fa7, 0x47b5481dbefa4fa4
+ }, sizeof(s5->h));
+ s5->len = 0;
+}
+
+void sha384_update(struct sha384 *s, const void *data, size_t len)
+{
+ sha512_update((struct sha512 *)s, data, len);
+}
+
+void sha384_finish(struct sha384 *s)
+{
+ sha512_finish((struct sha512 *)s);
+}
+
+void sha512_hmac(const void *key, size_t keylen,
+ const void *data, size_t datalen,
+ void *h)
+{
+ hmac(key, keylen,
+ data, datalen,
+ (digest_init)sha512_init,
+ (digest_update)sha512_update,
+ (digest_finish)sha512_finish,
+ sizeof(struct sha512),
+ sizeof(((struct sha512 *)0)->buffer),
+ sizeof(((struct sha512 *)0)->h),
+ (ptrdiff_t)&((struct sha512 *)0)->buffer,
+ (ptrdiff_t)&((struct sha512 *)0)->h,
+ h);
+}
+
+void sha384_hmac(const void *key, size_t keylen,
+ const void *data, size_t datalen,
+ void *h)
+{
+ hmac(key, keylen,
+ data, datalen,
+ (digest_init)sha384_init,
+ (digest_update)sha384_update,
+ (digest_finish)sha384_finish,
+ sizeof(struct sha384),
+ sizeof(((struct sha384 *)0)->buffer),
+ sizeof(((struct sha384 *)0)->h),
+ (ptrdiff_t)&((struct sha384 *)0)->buffer,
+ (ptrdiff_t)&((struct sha384 *)0)->h,
+ h);
+}
diff --git a/sha512.h b/sha512.h
@@ -0,0 +1,40 @@
+#ifndef SHA512_H
+#define SHA512_H
+
+#include <stdlib.h>
+#include <stdint.h>
+
+#define SHA512_HASHSIZE 64
+#define SHA384_HASHSIZE 48
+
+struct sha512 {
+ uint8_t buffer[128];
+ uint64_t h[8];
+ uint64_t len;
+};
+
+struct sha384 {
+ uint8_t buffer[128];
+ uint64_t h[6];
+ uint64_t h6;
+ uint64_t h7;
+ uint64_t len;
+};
+
+void sha512_init(struct sha512 *s);
+void sha512_update(struct sha512 *s, const void *data, size_t len);
+void sha512_finish(struct sha512 *s);
+
+void sha512_hmac(const void *key, size_t keylen,
+ const void *data, size_t datalen,
+ void *h);
+
+void sha384_init(struct sha384 *s);
+void sha384_update(struct sha384 *s, const void *data, size_t len);
+void sha384_finish(struct sha384 *s);
+
+void sha384_hmac(const void *key, size_t keylen,
+ const void *data, size_t datalen,
+ void *h);
+
+#endif
diff --git a/tiny-AES-c b/tiny-AES-c
@@ -0,0 +1 @@
+Subproject commit 12e7744b4919e9d55de75b7ab566326a1c8e7a67