commit 709074f8d5efb2afcc13f4ca98712905277682d2
parent ca676107d5735204b3b420f1524dd6e9c8556967
Author: Santtu Lakkala <santtu.lakkala@digital14.com>
Date: Thu, 14 Sep 2023 17:51:07 +0300
More refactoring, add simple tests
Refactor struct bytes to use end pointer instead of length. Add very
simple system tests using the RFC 6238 data, and a -T flag to request
token at custom time.
Diffstat:
M | Makefile | | | 18 | +++++++++++------- |
A | algotest.c | | | 430 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | db.c | | | 16 | ++++++++-------- |
M | main.c | | | 31 | +++++++++++++++++-------------- |
D | test.c | | | 430 | ------------------------------------------------------------------------------- |
A | test.sh | | | 67 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | token.c | | | 18 | ++++++++---------- |
M | util.c | | | 4 | ++-- |
M | util.h | | | 19 | ++++++++++++++----- |
9 files changed, 557 insertions(+), 476 deletions(-)
diff --git a/Makefile b/Makefile
@@ -1,8 +1,8 @@
-CFLAGS = -W -Wall -std=c99 -g -Os
+CFLAGS = -W -Wall -Wextra -pedantic -std=c99 -Os
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 db.c token.c
OBJS = ${SOURCES:.c=.o}
-TEST_SOURCES = sha1.c sha256.c sha512.c util.c test.c
+TEST_SOURCES = sha1.c sha256.c sha512.c util.c algotest.c
TEST_OBJS = ${TEST_SOURCES:.c=.o}
VERSION = 0.1
@@ -17,9 +17,13 @@ all: ${NAME}
${NAME}: ${OBJS}
${CC} ${CFLAGS} -o $@ ${OBJS} ${LDFLAGS}
-test: ${TEST_OBJS};
+algotest: ${TEST_OBJS};
${CC} ${CFLAGS} -o $@ ${TEST_OBJS} ${LDFLAGS}
+test: algotest ${NAME}
+ ./algotest
+ ./test.sh ./${NAME}
+
.c.o:
${CC} -c $< -o $@ ${CFLAGS} ${AES_CFLAGS}
@@ -52,10 +56,10 @@ sha256.o: sha256.h
sha256.o: util.h
sha512.o: sha512.h
sha512.o: util.h
-test.o: sha1.h
-test.o: sha256.h
-test.o: sha512.h
-test.o: util.h
+algotest.o: sha1.h
+algotest.o: sha256.h
+algotest.o: sha512.h
+algotest.o: util.h
token.o: token.h
token.o: util.h
util.o: util.h
diff --git a/algotest.c b/algotest.c
@@ -0,0 +1,430 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+
+#include "util.h"
+#include "sha1.h"
+#include "sha256.h"
+#include "sha512.h"
+
+void hexdump(FILE *f, const void *data, size_t len)
+{
+ const uint8_t *d;
+
+ for (d = data; len--; d++)
+ fprintf(f, "%02x", *d);
+}
+
+void test_sha1(void)
+{
+ const char *test_datas[] = {
+ "abc",
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ };
+
+ const uint8_t test_hashes[][SHA1_HASHSIZE] = {
+ { 0xA9, 0x99, 0x3E, 0x36, 0x47, 0x06, 0x81, 0x6A, 0xBA, 0x3E,
+ 0x25, 0x71, 0x78, 0x50, 0xC2, 0x6C, 0x9C, 0xD0, 0xD8, 0x9D, },
+ { 0x84, 0x98, 0x3E, 0x44, 0x1C, 0x3B, 0xD2, 0x6E, 0xBA, 0xAE,
+ 0x4A, 0xA1, 0xF9, 0x51, 0x29, 0xE5, 0xE5, 0x46, 0x70, 0xF1, },
+ };
+
+ struct sha1 s;
+ size_t i;
+
+ for (i = 0; i < sizeof(test_datas) / sizeof(*test_datas); i++) {
+ sha1_init(&s);
+ sha1_update(&s, test_datas[i], strlen(test_datas[i]));
+ sha1_finish(&s);
+
+ if (memcmp(s.h, test_hashes[i], sizeof(s.h))) {
+ fprintf(stderr, "%s: hash %zu mismatch, got:\n\t",
+ __FUNCTION__, i);
+ hexdump(stderr, s.h, sizeof(s.h));
+ fprintf(stderr, "\n, expected:\n\t");
+ hexdump(stderr,
+ test_hashes[i], sizeof(test_hashes[i]));
+ fprintf(stderr, "\n");
+ }
+ }
+}
+
+void test_hmac_sha1(void)
+{
+ const char *test_datas[] = {
+ "Sample message for keylen=blocklen",
+ "Sample message for keylen<blocklen",
+ "Sample message for keylen=blocklen",
+ "Sample message for keylen<blocklen, with truncated tag"
+ };
+
+ uint8_t keybuf[128];
+ const size_t keylens[] = {
+ 64, 20, 100, 49
+ };
+
+ const size_t taglens[] = {
+ 20, 20, 20, 12
+ };
+
+ const uint8_t test_tags[][SHA1_HASHSIZE] = {
+ { 0x5F, 0xD5, 0x96, 0xEE, 0x78, 0xD5, 0x55, 0x3C, 0x8F, 0xF4,
+ 0xE7, 0x2D, 0x26, 0x6D, 0xFD, 0x19, 0x23, 0x66, 0xDA, 0x29, },
+ { 0x4C, 0x99, 0xFF, 0x0C, 0xB1, 0xB3, 0x1B, 0xD3, 0x3F, 0x84,
+ 0x31, 0xDB, 0xAF, 0x4D, 0x17, 0xFC, 0xD3, 0x56, 0xA8, 0x07, },
+ { 0x2D, 0x51, 0xB2, 0xF7, 0x75, 0x0E, 0x41, 0x05, 0x84, 0x66,
+ 0x2E, 0x38, 0xF1, 0x33, 0x43, 0x5F, 0x4C, 0x4F, 0xD4, 0x2A, },
+ { 0xFE, 0x35, 0x29, 0x56, 0x5C, 0xD8, 0xE2, 0x8C, 0x5F, 0xA7,
+ 0x9E, 0xAC, },
+ };
+
+ size_t i;
+
+ for (i = 0; i < sizeof(keybuf); i++)
+ keybuf[i] = i;
+
+ for (i = 0; i < sizeof(test_datas) / sizeof(*test_datas); i++) {
+ uint8_t hmacbuf[SHA1_HASHSIZE];
+ sha1_hmac(keybuf, keylens[i], test_datas[i], strlen(test_datas[i]), hmacbuf);
+
+ if (memcmp(hmacbuf, test_tags[i], taglens[i])) {
+ fprintf(stderr, "%s: HMAC %zu mismatch, got:\n\t", __FUNCTION__, i);
+ hexdump(stderr, hmacbuf, taglens[i]);
+ fprintf(stderr, "\n, expected:\n\t");
+ hexdump(stderr, test_tags[i], taglens[i]);
+ fprintf(stderr, "\n");
+ }
+ }
+}
+
+void test_sha224(void)
+{
+ const char *test_datas[] = {
+ "abc",
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+ };
+
+ const uint8_t test_hashes[][SHA224_HASHSIZE] = {
+ { 0x23, 0x09, 0x7D, 0x22, 0x34, 0x05, 0xD8, 0x22, 0x86, 0x42, 0xA4, 0x77, 0xBD, 0xA2,
+ 0x55, 0xB3, 0x2A, 0xAD, 0xBC, 0xE4, 0xBD, 0xA0, 0xB3, 0xF7, 0xE3, 0x6C, 0x9D, 0xA7, },
+ { 0x75, 0x38, 0x8B, 0x16, 0x51, 0x27, 0x76, 0xCC, 0x5D, 0xBA, 0x5D, 0xA1, 0xFD, 0x89,
+ 0x01, 0x50, 0xB0, 0xC6, 0x45, 0x5C, 0xB4, 0xF5, 0x8B, 0x19, 0x52, 0x52, 0x25, 0x25, },
+ };
+
+ struct sha224 s;
+ size_t i;
+
+ for (i = 0; i < sizeof(test_datas) / sizeof(*test_datas); i++) {
+ sha224_init(&s);
+ sha224_update(&s, test_datas[i], strlen(test_datas[i]));
+ sha224_finish(&s);
+
+ if (memcmp(s.h, test_hashes[i], sizeof(s.h))) {
+ fprintf(stderr, "%s: hash %zu mismatch, got:\n\t", __FUNCTION__, i);
+ hexdump(stderr, s.h, sizeof(s.h));
+ fprintf(stderr, "\n, expected:\n\t");
+ hexdump(stderr, test_hashes[i], sizeof(test_hashes[i]));
+ fprintf(stderr, "\n");
+ }
+ }
+}
+
+void test_sha256(void)
+{
+ const char *test_datas[] = {
+ "abc",
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ };
+
+ const uint8_t test_hashes[][SHA256_HASHSIZE] = {
+ { 0xBA, 0x78, 0x16, 0xBF, 0x8F, 0x01, 0xCF, 0xEA, 0x41, 0x41, 0x40, 0xDE, 0x5D, 0xAE, 0x22, 0x23,
+ 0xB0, 0x03, 0x61, 0xA3, 0x96, 0x17, 0x7A, 0x9C, 0xB4, 0x10, 0xFF, 0x61, 0xF2, 0x00, 0x15, 0xAD, },
+ { 0x24, 0x8D, 0x6A, 0x61, 0xD2, 0x06, 0x38, 0xB8, 0xE5, 0xC0, 0x26, 0x93, 0x0C, 0x3E, 0x60, 0x39,
+ 0xA3, 0x3C, 0xE4, 0x59, 0x64, 0xFF, 0x21, 0x67, 0xF6, 0xEC, 0xED, 0xD4, 0x19, 0xDB, 0x06, 0xC1, },
+ };
+
+ struct sha256 s;
+ size_t i;
+
+ for (i = 0; i < sizeof(test_datas) / sizeof(*test_datas); i++) {
+ sha256_init(&s);
+ sha256_update(&s, test_datas[i], strlen(test_datas[i]));
+ sha256_finish(&s);
+
+ if (memcmp(s.h, test_hashes[i], sizeof(s.h)))
+ fprintf(stderr, "%s: hash %zu mismatch\n", __FUNCTION__, i);
+ }
+}
+
+void test_hmac_sha256(void)
+{
+ const char *test_datas[] = {
+ "Sample message for keylen=blocklen",
+ "Sample message for keylen<blocklen",
+ "Sample message for keylen=blocklen",
+ "Sample message for keylen<blocklen, with truncated tag"
+ };
+
+ uint8_t keybuf[128];
+ const size_t keylens[] = {
+ 64, 32, 100, 49
+ };
+
+ const size_t taglens[] = {
+ 32, 32, 32, 16
+ };
+
+ const uint8_t test_tags[][SHA256_HASHSIZE] = {
+ { 0x8B, 0xB9, 0xA1, 0xDB, 0x98, 0x06, 0xF2, 0x0D, 0xF7, 0xF7, 0x7B, 0x82, 0x13, 0x8C, 0x79, 0x14, 0xD1, 0x74, 0xD5, 0x9E, 0x13, 0xDC, 0x4D, 0x01, 0x69, 0xC9, 0x05, 0x7B, 0x13, 0x3E, 0x1D, 0x62, },
+ { 0xA2, 0x8C, 0xF4, 0x31, 0x30, 0xEE, 0x69, 0x6A, 0x98, 0xF1, 0x4A, 0x37, 0x67, 0x8B, 0x56, 0xBC, 0xFC, 0xBD, 0xD9, 0xE5, 0xCF, 0x69, 0x71, 0x7F, 0xEC, 0xF5, 0x48, 0x0F, 0x0E, 0xBD, 0xF7, 0x90, },
+ { 0xBD, 0xCC, 0xB6, 0xC7, 0x2D, 0xDE, 0xAD, 0xB5, 0x00, 0xAE, 0x76, 0x83, 0x86, 0xCB, 0x38, 0xCC, 0x41, 0xC6, 0x3D, 0xBB, 0x08, 0x78, 0xDD, 0xB9, 0xC7, 0xA3, 0x8A, 0x43, 0x1B, 0x78, 0x37, 0x8D, },
+ { 0x27, 0xA8, 0xB1, 0x57, 0x83, 0x9E, 0xFE, 0xAC, 0x98, 0xDF, 0x07, 0x0B, 0x33, 0x1D, 0x59, 0x36, },
+ };
+
+ size_t i;
+
+ for (i = 0; i < sizeof(keybuf); i++)
+ keybuf[i] = i;
+
+ for (i = 0; i < sizeof(test_datas) / sizeof(*test_datas); i++) {
+ uint8_t hmacbuf[SHA256_HASHSIZE];
+ sha256_hmac(keybuf, keylens[i], test_datas[i], strlen(test_datas[i]), hmacbuf);
+
+ if (memcmp(hmacbuf, test_tags[i], taglens[i])) {
+ fprintf(stderr, "%s: HMAC %zu mismatch, got:\n\t", __FUNCTION__, i);
+ hexdump(stderr, hmacbuf, taglens[i]);
+ fprintf(stderr, "\n, expected:\n\t");
+ hexdump(stderr, test_tags[i], taglens[i]);
+ fprintf(stderr, "\n");
+ }
+ }
+}
+
+void test_sha384(void)
+{
+ const char *test_datas[] = {
+ "abc",
+ "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
+ };
+
+ const uint8_t test_hashes[][SHA384_HASHSIZE] = {
+ { 0xCB, 0x00, 0x75, 0x3F, 0x45, 0xA3, 0x5E, 0x8B, 0xB5, 0xA0, 0x3D, 0x69, 0x9A, 0xC6, 0x50, 0x07,
+ 0x27, 0x2C, 0x32, 0xAB, 0x0E, 0xDE, 0xD1, 0x63, 0x1A, 0x8B, 0x60, 0x5A, 0x43, 0xFF, 0x5B, 0xED,
+ 0x80, 0x86, 0x07, 0x2B, 0xA1, 0xE7, 0xCC, 0x23, 0x58, 0xBA, 0xEC, 0xA1, 0x34, 0xC8, 0x25, 0xA7, },
+ { 0x09, 0x33, 0x0C, 0x33, 0xF7, 0x11, 0x47, 0xE8, 0x3D, 0x19, 0x2F, 0xC7, 0x82, 0xCD, 0x1B, 0x47,
+ 0x53, 0x11, 0x1B, 0x17, 0x3B, 0x3B, 0x05, 0xD2, 0x2F, 0xA0, 0x80, 0x86, 0xE3, 0xB0, 0xF7, 0x12,
+ 0xFC, 0xC7, 0xC7, 0x1A, 0x55, 0x7E, 0x2D, 0xB9, 0x66, 0xC3, 0xE9, 0xFA, 0x91, 0x74, 0x60, 0x39, },
+ };
+
+ struct sha384 s;
+ size_t i;
+
+ for (i = 0; i < sizeof(test_datas) / sizeof(*test_datas); i++) {
+ sha384_init(&s);
+ sha384_update(&s, test_datas[i], strlen(test_datas[i]));
+ sha384_finish(&s);
+
+ if (memcmp(s.h, test_hashes[i], sizeof(s.h)))
+ fprintf(stderr, "%s: hash %zu mismatch\n", __FUNCTION__, i);
+ }
+}
+
+void test_sha512(void)
+{
+ const char *test_datas[] = {
+ "abc",
+ "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
+ };
+
+ const uint8_t test_hashes[][SHA512_HASHSIZE] = {
+ { 0xDD, 0xAF, 0x35, 0xA1, 0x93, 0x61, 0x7A, 0xBA, 0xCC, 0x41, 0x73, 0x49, 0xAE, 0x20, 0x41, 0x31,
+ 0x12, 0xE6, 0xFA, 0x4E, 0x89, 0xA9, 0x7E, 0xA2, 0x0A, 0x9E, 0xEE, 0xE6, 0x4B, 0x55, 0xD3, 0x9A,
+ 0x21, 0x92, 0x99, 0x2A, 0x27, 0x4F, 0xC1, 0xA8, 0x36, 0xBA, 0x3C, 0x23, 0xA3, 0xFE, 0xEB, 0xBD,
+ 0x45, 0x4D, 0x44, 0x23, 0x64, 0x3C, 0xE8, 0x0E, 0x2A, 0x9A, 0xC9, 0x4F, 0xA5, 0x4C, 0xA4, 0x9F, },
+ { 0x8E, 0x95, 0x9B, 0x75, 0xDA, 0xE3, 0x13, 0xDA, 0x8C, 0xF4, 0xF7, 0x28, 0x14, 0xFC, 0x14, 0x3F,
+ 0x8F, 0x77, 0x79, 0xC6, 0xEB, 0x9F, 0x7F, 0xA1, 0x72, 0x99, 0xAE, 0xAD, 0xB6, 0x88, 0x90, 0x18,
+ 0x50, 0x1D, 0x28, 0x9E, 0x49, 0x00, 0xF7, 0xE4, 0x33, 0x1B, 0x99, 0xDE, 0xC4, 0xB5, 0x43, 0x3A,
+ 0xC7, 0xD3, 0x29, 0xEE, 0xB6, 0xDD, 0x26, 0x54, 0x5E, 0x96, 0xE5, 0x5B, 0x87, 0x4B, 0xE9, 0x09, },
+ };
+
+ struct sha512 s;
+ size_t i;
+
+ for (i = 0; i < sizeof(test_datas) / sizeof(*test_datas); i++) {
+ sha512_init(&s);
+ sha512_update(&s, test_datas[i], strlen(test_datas[i]));
+ sha512_finish(&s);
+
+ if (memcmp(s.h, test_hashes[i], sizeof(s.h)))
+ fprintf(stderr, "%s: hash %zu mismatch\n", __FUNCTION__, i);
+ }
+}
+
+void test_hmac_sha512(void)
+{
+ const char *test_datas[] = {
+ "Sample message for keylen=blocklen",
+ "Sample message for keylen<blocklen",
+ "Sample message for keylen=blocklen",
+ "Sample message for keylen<blocklen, with truncated tag"
+ };
+
+ uint8_t keybuf[256];
+ const size_t keylens[] = {
+ 128, 64, 200, 49
+ };
+
+ const size_t taglens[] = {
+ 64, 64, 64, 32
+ };
+
+ const uint8_t test_tags[][SHA512_HASHSIZE] = {
+ { 0xFC, 0x25, 0xE2, 0x40, 0x65, 0x8C, 0xA7, 0x85, 0xB7, 0xA8, 0x11, 0xA8, 0xD3, 0xF7, 0xB4, 0xCA, 0x48, 0xCF, 0xA2, 0x6A, 0x8A, 0x36, 0x6B, 0xF2, 0xCD, 0x1F, 0x83, 0x6B, 0x05, 0xFC, 0xB0, 0x24, 0xBD, 0x36, 0x85, 0x30, 0x81, 0x81, 0x1D, 0x6C, 0xEA, 0x42, 0x16, 0xEB, 0xAD, 0x79, 0xDA, 0x1C, 0xFC, 0xB9, 0x5E, 0xA4, 0x58, 0x6B, 0x8A, 0x0C, 0xE3, 0x56, 0x59, 0x6A, 0x55, 0xFB, 0x13, 0x47, },
+ { 0xFD, 0x44, 0xC1, 0x8B, 0xDA, 0x0B, 0xB0, 0xA6, 0xCE, 0x0E, 0x82, 0xB0, 0x31, 0xBF, 0x28, 0x18, 0xF6, 0x53, 0x9B, 0xD5, 0x6E, 0xC0, 0x0B, 0xDC, 0x10, 0xA8, 0xA2, 0xD7, 0x30, 0xB3, 0x63, 0x4D, 0xE2, 0x54, 0x5D, 0x63, 0x9B, 0x0F, 0x2C, 0xF7, 0x10, 0xD0, 0x69, 0x2C, 0x72, 0xA1, 0x89, 0x6F, 0x1F, 0x21, 0x1C, 0x2B, 0x92, 0x2D, 0x1A, 0x96, 0xC3, 0x92, 0xE0, 0x7E, 0x7E, 0xA9, 0xFE, 0xDC, },
+ { 0xD9, 0x3E, 0xC8, 0xD2, 0xDE, 0x1A, 0xD2, 0xA9, 0x95, 0x7C, 0xB9, 0xB8, 0x3F, 0x14, 0xE7, 0x6A, 0xD6, 0xB5, 0xE0, 0xCC, 0xE2, 0x85, 0x07, 0x9A, 0x12, 0x7D, 0x3B, 0x14, 0xBC, 0xCB, 0x7A, 0xA7, 0x28, 0x6D, 0x4A, 0xC0, 0xD4, 0xCE, 0x64, 0x21, 0x5F, 0x2B, 0xC9, 0xE6, 0x87, 0x0B, 0x33, 0xD9, 0x74, 0x38, 0xBE, 0x4A, 0xAA, 0x20, 0xCD, 0xA5, 0xC5, 0xA9, 0x12, 0xB4, 0x8B, 0x8E, 0x27, 0xF3, },
+ { 0x00, 0xF3, 0xE9, 0xA7, 0x7B, 0xB0, 0xF0, 0x6D, 0xE1, 0x5F, 0x16, 0x06, 0x03, 0xE4, 0x2B, 0x50, 0x28, 0x75, 0x88, 0x08, 0x59, 0x66, 0x64, 0xC0, 0x3E, 0x1A, 0xB8, 0xFB, 0x2B, 0x07, 0x67, 0x78, },
+ };
+
+ size_t i;
+
+ for (i = 0; i < sizeof(keybuf); i++)
+ keybuf[i] = i;
+
+ for (i = 0; i < sizeof(test_datas) / sizeof(*test_datas); i++) {
+ uint8_t hmacbuf[SHA512_HASHSIZE];
+ sha512_hmac(keybuf, keylens[i], test_datas[i], strlen(test_datas[i]), hmacbuf);
+
+ if (memcmp(hmacbuf, test_tags[i], taglens[i])) {
+ fprintf(stderr, "%s: HMAC %zu mismatch, got:\n\t", __FUNCTION__, i);
+ hexdump(stderr, hmacbuf, taglens[i]);
+ fprintf(stderr, "\n, expected:\n\t");
+ hexdump(stderr, test_tags[i], taglens[i]);
+ fprintf(stderr, "\n");
+ }
+ }
+}
+
+void test_totp_sha1(void)
+{
+ /* Test vectors from RFC 6238 appendix B */
+ const char *key = "12345678901234567890";
+ const uint8_t period = 30;
+ const time_t t0 = 0;
+
+ const time_t times[] = {
+ 59, 1111111109, 1111111111, 1234567890, 2000000000, 20000000000
+ };
+ const uint32_t totps[] = {
+ 94287082, /*0*/7081804, 14050471, 89005924, 69279037, 65353130
+ };
+ const uint32_t modulo = 100000000;
+ size_t i;
+
+ for (i = 0; i < sizeof(times) / sizeof(*times); i++) {
+ uint32_t token = totp(key, strlen(key), times[i], period, t0, sha1_hmac, SHA1_HASHSIZE);
+
+ if (token % modulo != totps[i])
+ fprintf(stderr, "%s: token %zu mismatch, got %08" PRIu32 ", expected %08" PRIu32 "\n",
+ __FUNCTION__, i, token % modulo, totps[i]);
+ }
+}
+
+void test_totp_sha256(void)
+{
+ /* Test vectors from RFC 6238 appendix B but key/seed from appendix A */
+ const char *key = "12345678901234567890123456789012";
+ const uint8_t period = 30;
+ const time_t t0 = 0;
+
+ const time_t times[] = {
+ 59, 1111111109, 1111111111, 1234567890, 2000000000, 20000000000
+ };
+ const uint32_t totps[] = {
+ 46119246, 68084774, 67062674, 91819424, 90698825, 77737706
+ };
+ const uint32_t modulo = 100000000;
+ size_t i;
+
+ for (i = 0; i < sizeof(times) / sizeof(*times); i++) {
+ uint32_t token = totp(key, strlen(key), times[i], period, t0, sha256_hmac, SHA256_HASHSIZE);
+
+ if (token % modulo != totps[i])
+ fprintf(stderr, "%s: token %zu mismatch, got %08" PRIu32 ", expected %08" PRIu32 "\n",
+ __FUNCTION__, i, token % modulo, totps[i]);
+ }
+}
+
+void test_totp_sha512(void)
+{
+ /* Test vectors from RFC 6238 appendix B but key/seed from appendix A */
+ const char *key = "1234567890123456789012345678901234567890123456789012345678901234";
+ const uint8_t period = 30;
+ const time_t t0 = 0;
+
+ const time_t times[] = {
+ 59, 1111111109, 1111111111, 1234567890, 2000000000, 20000000000
+ };
+ const uint32_t totps[] = {
+ 90693936, 25091201, 99943326, 93441116, 38618901, 47863826
+ };
+ const uint32_t modulo = 100000000;
+ size_t i;
+
+ for (i = 0; i < sizeof(times) / sizeof(*times); i++) {
+ uint32_t token = totp(key, strlen(key), times[i], period, t0, sha512_hmac, SHA512_HASHSIZE);
+
+ if (token % modulo != totps[i])
+ fprintf(stderr, "%s: token %zu mismatch, got %08" PRIu32 ", expected %08" PRIu32 "\n",
+ __FUNCTION__, i, token % modulo, totps[i]);
+ }
+}
+
+void test_debase32(void)
+{
+ const char *base32s[] = {
+ "MFRGG",
+ "MFRGGZDFMZTWQ2LKNM",
+ "MFRGGZDF",
+ };
+
+ const char *plaintext[] = {
+ "abc",
+ "abcdefghijk",
+ "abcde"
+ };
+
+ size_t i;
+
+ for (i = 0; i < sizeof(base32s) / sizeof(*base32s); i++) {
+ char buffer[64];
+ int len = sprintf(buffer, "%s", base32s[i]);
+ *(char *)debase32(bytesnc(buffer, len)).end = '\0';
+
+ if (strcmp(buffer, plaintext[i]))
+ fprintf(stderr, "%s: plaintext mismatch, got %s, expected %s\n",
+ __FUNCTION__, buffer, plaintext[i]);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ (void)argc;
+ (void)argv;
+
+ test_sha1();
+ test_sha224();
+ test_sha256();
+ test_sha384();
+ test_sha512();
+
+ test_hmac_sha1();
+ test_hmac_sha256();
+ test_hmac_sha512();
+
+ test_totp_sha1();
+ test_totp_sha256();
+ test_totp_sha512();
+
+ test_debase32();
+}
diff --git a/db.c b/db.c
@@ -137,9 +137,9 @@ void db_foreach(int fd, struct AES_ctx *c,
decbuf + sizeof(*kh) + kh->keylen + kh->desclen + kh->issuerlen > dp)
continue;
- struct bytes key = { decbuf + sizeof(*kh), kh->keylen };
- struct bytes desc = { key.data + key.len, kh->desclen };
- struct bytes issuer = { desc.data + desc.len, kh->issuerlen };
+ struct bytes key = bytesnc(&kh[1], kh->keylen);
+ struct bytes desc = bytesnc(key.end, kh->desclen);
+ struct bytes issuer = bytesnc(desc.end, kh->issuerlen);
key_cb(&(struct token){
key, desc, issuer,
kh->digest, readbeu64(kh->t0),
@@ -154,13 +154,13 @@ void db_foreach(int fd, struct AES_ctx *c,
int db_add_key(int fd, struct AES_ctx *c,
struct token *token)
{
- size_t ksz = sizeof(struct totpkey) + token->key.len + token->desc.len + token->issuer.len;
+ size_t ksz = sizeof(struct totpkey) + bytes_len(token->key) + bytes_len(token->desc) + bytes_len(token->issuer);
size_t i;
int w;
ksz = (ksz + AES_BLOCKLEN - 1) / AES_BLOCKLEN * AES_BLOCKLEN;
- if (token->key.len > UINT8_MAX || token->desc.len > UINT8_MAX || token->issuer.len > UINT8_MAX) {
+ if (bytes_len(token->key) > UINT8_MAX || bytes_len(token->desc) > UINT8_MAX || bytes_len(token->issuer) > UINT8_MAX) {
errno = EMSGSIZE;
return -1;
}
@@ -173,9 +173,9 @@ int db_add_key(int fd, struct AES_ctx *c,
.digest = token->digest,
.digits = token->digits,
.period = token->period,
- .keylen = token->key.len,
- .desclen = token->desc.len,
- .issuerlen = token->issuer.len }, sizeof(struct totpkey));
+ .keylen = bytes_len(token->key),
+ .desclen = bytes_len(token->desc),
+ .issuerlen = bytes_len(token->issuer) }, sizeof(struct totpkey));
wp = mempushb(wp, token->key);
wp = mempushb(wp, token->desc);
wp = mempushb(wp, token->issuer);
diff --git a/main.c b/main.c
@@ -51,11 +51,10 @@ static void print_base32(FILE *stream, struct bytes data)
{
const char *chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
uint8_t *buffer = data.data;
- size_t len = data.len;
uint16_t v = 0;
size_t b = 0;
- while (len--) {
+ while (buffer < data.end) {
v = v << 8 | *buffer++;
b += 8;
while (b >= 5) {
@@ -73,14 +72,14 @@ void print_key(struct token *token, void *data)
(void)data;
- fprintf(stream, "%.*s by %.*s\n", (int)token->desc.len, token->desc.data, (int)token->issuer.len, token->issuer.data);
+ fprintf(stream, "%.*s by %.*s\n", (int)bytes_len(token->desc), token->desc.data, (int)bytes_len(token->issuer), token->issuer.data);
}
static void print_uriencode(FILE *stream, struct bytes data, bool getarg)
{
const char *escape = ":/@+% &?";
- const char *buf = (const char *)data.data;
- const char *end = buf + data.len;
+ const char *buf = (const void *)data.data;
+ const char *end = (const void *)data.end;
while (buf < end && *buf) {
size_t pass = strncspn(buf, end - buf, escape);
printf("%.*s", (int)pass, buf);
@@ -102,14 +101,14 @@ void print_keyuri(struct token *token,
FILE *stream = data;
fputs("otpauth://totp/", stream);
- if (token->issuer.len) {
+ if (bytes_len(token->issuer)) {
print_uriencode(stream, token->issuer, false);
fputc(':', stream);
}
print_uriencode(stream, token->desc, false);
fputs("?secret=", stream);
print_base32(stream, token->key);
- if (token->issuer.len) {
+ if (bytes_len(token->issuer)) {
fputs("&issuer=", stream);
print_uriencode(stream, token->issuer, true);
}
@@ -122,6 +121,7 @@ void print_keyuri(struct token *token,
struct generate_data {
const char *filter;
bool found;
+ time_t time;
};
void generate_token(struct token *token, void *data)
@@ -132,11 +132,11 @@ void generate_token(struct token *token, void *data)
char descbuf[512];
char *dp = descbuf;
- if (token->issuer.len) {
- dp = mempush(dp, token->issuer.data, token->issuer.len);
+ if (bytes_len(token->issuer)) {
+ dp = mempushb(dp, token->issuer);
*dp++ = ':';
}
- dp = mempush(dp, token->desc.data, token->desc.len);
+ dp = mempushb(dp, token->desc);
*dp = '\0';
if (fnmatch(d->filter, descbuf, FNM_NOESCAPE))
@@ -147,7 +147,7 @@ void generate_token(struct token *token, void *data)
modulo *= 10;
printf("%0*" PRIu32 "\n", (int)token->digits,
- totp(token->key.data, token->key.len, time(NULL), token->period, token->t0, digest_hmacs[token->digest], digest_sizes[token->digest]) % modulo);
+ totp(token->key.data, bytes_len(token->key), d->time, token->period, token->t0, digest_hmacs[token->digest], digest_sizes[token->digest]) % modulo);
}
struct write_filter_data {
@@ -164,8 +164,7 @@ void write_filter_key(struct token *token,
if (d->filter) {
char descbuf[UINT8_MAX + 1];
- memcpy(descbuf, token->desc.data, token->desc.len);
- descbuf[token->desc.len] = '\0';
+ *(char *)mempushb(descbuf, token->desc) = '\0';
if (!fnmatch(d->filter, descbuf, FNM_NOESCAPE))
return;
@@ -190,6 +189,7 @@ void usage()
"-f <file>\tuse file as database\n"
"-k <pass>\tpassphrase for database encryption\n"
"-K <file>\tread encryption passphrase from file\n"
+ "-T <time>\tunix time for token generation\n"
"-l\tlist known secrets\n"
"-a <uri>\tadd uri to secrets\n"
"-d <filter>\tremove secrets matching filter\n"
@@ -226,7 +226,7 @@ int main(int argc, char *argv[])
bool free_secretfile = true;
uint8_t keybuf[AES_KEYLEN + AES_BLOCKLEN];
size_t keylen = 0;
- struct generate_data gd = { NULL, false };
+ struct generate_data gd = { NULL, false, time(NULL) };
struct token token;
char *t;
@@ -246,6 +246,9 @@ int main(int argc, char *argv[])
cmd = CMD_TOK;
keyquery = EARGF(usage());
break;
+ case 'T':
+ gd.time = strtoull(EARGF(usage()), NULL, 10);
+ break;
case 'e':
cmd = CMD_EXP;
break;
diff --git a/test.c b/test.c
@@ -1,430 +0,0 @@
-#include <stdio.h>
-#include <string.h>
-#include <stdint.h>
-#include <inttypes.h>
-
-#include "util.h"
-#include "sha1.h"
-#include "sha256.h"
-#include "sha512.h"
-
-void hexdump(FILE *f, const void *data, size_t len)
-{
- const uint8_t *d;
-
- for (d = data; len--; d++)
- fprintf(f, "%02x", *d);
-}
-
-void test_sha1(void)
-{
- const char *test_datas[] = {
- "abc",
- "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
- };
-
- const uint8_t test_hashes[][SHA1_HASHSIZE] = {
- { 0xA9, 0x99, 0x3E, 0x36, 0x47, 0x06, 0x81, 0x6A, 0xBA, 0x3E,
- 0x25, 0x71, 0x78, 0x50, 0xC2, 0x6C, 0x9C, 0xD0, 0xD8, 0x9D, },
- { 0x84, 0x98, 0x3E, 0x44, 0x1C, 0x3B, 0xD2, 0x6E, 0xBA, 0xAE,
- 0x4A, 0xA1, 0xF9, 0x51, 0x29, 0xE5, 0xE5, 0x46, 0x70, 0xF1, },
- };
-
- struct sha1 s;
- size_t i;
-
- for (i = 0; i < sizeof(test_datas) / sizeof(*test_datas); i++) {
- sha1_init(&s);
- sha1_update(&s, test_datas[i], strlen(test_datas[i]));
- sha1_finish(&s);
-
- if (memcmp(s.h, test_hashes[i], sizeof(s.h))) {
- fprintf(stderr, "%s: hash %zu mismatch, got:\n\t",
- __FUNCTION__, i);
- hexdump(stderr, s.h, sizeof(s.h));
- fprintf(stderr, "\n, expected:\n\t");
- hexdump(stderr,
- test_hashes[i], sizeof(test_hashes[i]));
- fprintf(stderr, "\n");
- }
- }
-}
-
-void test_hmac_sha1(void)
-{
- const char *test_datas[] = {
- "Sample message for keylen=blocklen",
- "Sample message for keylen<blocklen",
- "Sample message for keylen=blocklen",
- "Sample message for keylen<blocklen, with truncated tag"
- };
-
- uint8_t keybuf[128];
- const size_t keylens[] = {
- 64, 20, 100, 49
- };
-
- const size_t taglens[] = {
- 20, 20, 20, 12
- };
-
- const uint8_t test_tags[][SHA1_HASHSIZE] = {
- { 0x5F, 0xD5, 0x96, 0xEE, 0x78, 0xD5, 0x55, 0x3C, 0x8F, 0xF4,
- 0xE7, 0x2D, 0x26, 0x6D, 0xFD, 0x19, 0x23, 0x66, 0xDA, 0x29, },
- { 0x4C, 0x99, 0xFF, 0x0C, 0xB1, 0xB3, 0x1B, 0xD3, 0x3F, 0x84,
- 0x31, 0xDB, 0xAF, 0x4D, 0x17, 0xFC, 0xD3, 0x56, 0xA8, 0x07, },
- { 0x2D, 0x51, 0xB2, 0xF7, 0x75, 0x0E, 0x41, 0x05, 0x84, 0x66,
- 0x2E, 0x38, 0xF1, 0x33, 0x43, 0x5F, 0x4C, 0x4F, 0xD4, 0x2A, },
- { 0xFE, 0x35, 0x29, 0x56, 0x5C, 0xD8, 0xE2, 0x8C, 0x5F, 0xA7,
- 0x9E, 0xAC, },
- };
-
- size_t i;
-
- for (i = 0; i < sizeof(keybuf); i++)
- keybuf[i] = i;
-
- for (i = 0; i < sizeof(test_datas) / sizeof(*test_datas); i++) {
- uint8_t hmacbuf[SHA1_HASHSIZE];
- sha1_hmac(keybuf, keylens[i], test_datas[i], strlen(test_datas[i]), hmacbuf);
-
- if (memcmp(hmacbuf, test_tags[i], taglens[i])) {
- fprintf(stderr, "%s: HMAC %zu mismatch, got:\n\t", __FUNCTION__, i);
- hexdump(stderr, hmacbuf, taglens[i]);
- fprintf(stderr, "\n, expected:\n\t");
- hexdump(stderr, test_tags[i], taglens[i]);
- fprintf(stderr, "\n");
- }
- }
-}
-
-void test_sha224(void)
-{
- const char *test_datas[] = {
- "abc",
- "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
- };
-
- const uint8_t test_hashes[][SHA224_HASHSIZE] = {
- { 0x23, 0x09, 0x7D, 0x22, 0x34, 0x05, 0xD8, 0x22, 0x86, 0x42, 0xA4, 0x77, 0xBD, 0xA2,
- 0x55, 0xB3, 0x2A, 0xAD, 0xBC, 0xE4, 0xBD, 0xA0, 0xB3, 0xF7, 0xE3, 0x6C, 0x9D, 0xA7, },
- { 0x75, 0x38, 0x8B, 0x16, 0x51, 0x27, 0x76, 0xCC, 0x5D, 0xBA, 0x5D, 0xA1, 0xFD, 0x89,
- 0x01, 0x50, 0xB0, 0xC6, 0x45, 0x5C, 0xB4, 0xF5, 0x8B, 0x19, 0x52, 0x52, 0x25, 0x25, },
- };
-
- struct sha224 s;
- size_t i;
-
- for (i = 0; i < sizeof(test_datas) / sizeof(*test_datas); i++) {
- sha224_init(&s);
- sha224_update(&s, test_datas[i], strlen(test_datas[i]));
- sha224_finish(&s);
-
- if (memcmp(s.h, test_hashes[i], sizeof(s.h))) {
- fprintf(stderr, "%s: hash %zu mismatch, got:\n\t", __FUNCTION__, i);
- hexdump(stderr, s.h, sizeof(s.h));
- fprintf(stderr, "\n, expected:\n\t");
- hexdump(stderr, test_hashes[i], sizeof(test_hashes[i]));
- fprintf(stderr, "\n");
- }
- }
-}
-
-void test_sha256(void)
-{
- const char *test_datas[] = {
- "abc",
- "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
- };
-
- const uint8_t test_hashes[][SHA256_HASHSIZE] = {
- { 0xBA, 0x78, 0x16, 0xBF, 0x8F, 0x01, 0xCF, 0xEA, 0x41, 0x41, 0x40, 0xDE, 0x5D, 0xAE, 0x22, 0x23,
- 0xB0, 0x03, 0x61, 0xA3, 0x96, 0x17, 0x7A, 0x9C, 0xB4, 0x10, 0xFF, 0x61, 0xF2, 0x00, 0x15, 0xAD, },
- { 0x24, 0x8D, 0x6A, 0x61, 0xD2, 0x06, 0x38, 0xB8, 0xE5, 0xC0, 0x26, 0x93, 0x0C, 0x3E, 0x60, 0x39,
- 0xA3, 0x3C, 0xE4, 0x59, 0x64, 0xFF, 0x21, 0x67, 0xF6, 0xEC, 0xED, 0xD4, 0x19, 0xDB, 0x06, 0xC1, },
- };
-
- struct sha256 s;
- size_t i;
-
- for (i = 0; i < sizeof(test_datas) / sizeof(*test_datas); i++) {
- sha256_init(&s);
- sha256_update(&s, test_datas[i], strlen(test_datas[i]));
- sha256_finish(&s);
-
- if (memcmp(s.h, test_hashes[i], sizeof(s.h)))
- fprintf(stderr, "%s: hash %zu mismatch\n", __FUNCTION__, i);
- }
-}
-
-void test_hmac_sha256(void)
-{
- const char *test_datas[] = {
- "Sample message for keylen=blocklen",
- "Sample message for keylen<blocklen",
- "Sample message for keylen=blocklen",
- "Sample message for keylen<blocklen, with truncated tag"
- };
-
- uint8_t keybuf[128];
- const size_t keylens[] = {
- 64, 32, 100, 49
- };
-
- const size_t taglens[] = {
- 32, 32, 32, 16
- };
-
- const uint8_t test_tags[][SHA256_HASHSIZE] = {
- { 0x8B, 0xB9, 0xA1, 0xDB, 0x98, 0x06, 0xF2, 0x0D, 0xF7, 0xF7, 0x7B, 0x82, 0x13, 0x8C, 0x79, 0x14, 0xD1, 0x74, 0xD5, 0x9E, 0x13, 0xDC, 0x4D, 0x01, 0x69, 0xC9, 0x05, 0x7B, 0x13, 0x3E, 0x1D, 0x62, },
- { 0xA2, 0x8C, 0xF4, 0x31, 0x30, 0xEE, 0x69, 0x6A, 0x98, 0xF1, 0x4A, 0x37, 0x67, 0x8B, 0x56, 0xBC, 0xFC, 0xBD, 0xD9, 0xE5, 0xCF, 0x69, 0x71, 0x7F, 0xEC, 0xF5, 0x48, 0x0F, 0x0E, 0xBD, 0xF7, 0x90, },
- { 0xBD, 0xCC, 0xB6, 0xC7, 0x2D, 0xDE, 0xAD, 0xB5, 0x00, 0xAE, 0x76, 0x83, 0x86, 0xCB, 0x38, 0xCC, 0x41, 0xC6, 0x3D, 0xBB, 0x08, 0x78, 0xDD, 0xB9, 0xC7, 0xA3, 0x8A, 0x43, 0x1B, 0x78, 0x37, 0x8D, },
- { 0x27, 0xA8, 0xB1, 0x57, 0x83, 0x9E, 0xFE, 0xAC, 0x98, 0xDF, 0x07, 0x0B, 0x33, 0x1D, 0x59, 0x36, },
- };
-
- size_t i;
-
- for (i = 0; i < sizeof(keybuf); i++)
- keybuf[i] = i;
-
- for (i = 0; i < sizeof(test_datas) / sizeof(*test_datas); i++) {
- uint8_t hmacbuf[SHA256_HASHSIZE];
- sha256_hmac(keybuf, keylens[i], test_datas[i], strlen(test_datas[i]), hmacbuf);
-
- if (memcmp(hmacbuf, test_tags[i], taglens[i])) {
- fprintf(stderr, "%s: HMAC %zu mismatch, got:\n\t", __FUNCTION__, i);
- hexdump(stderr, hmacbuf, taglens[i]);
- fprintf(stderr, "\n, expected:\n\t");
- hexdump(stderr, test_tags[i], taglens[i]);
- fprintf(stderr, "\n");
- }
- }
-}
-
-void test_sha384(void)
-{
- const char *test_datas[] = {
- "abc",
- "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
- };
-
- const uint8_t test_hashes[][SHA384_HASHSIZE] = {
- { 0xCB, 0x00, 0x75, 0x3F, 0x45, 0xA3, 0x5E, 0x8B, 0xB5, 0xA0, 0x3D, 0x69, 0x9A, 0xC6, 0x50, 0x07,
- 0x27, 0x2C, 0x32, 0xAB, 0x0E, 0xDE, 0xD1, 0x63, 0x1A, 0x8B, 0x60, 0x5A, 0x43, 0xFF, 0x5B, 0xED,
- 0x80, 0x86, 0x07, 0x2B, 0xA1, 0xE7, 0xCC, 0x23, 0x58, 0xBA, 0xEC, 0xA1, 0x34, 0xC8, 0x25, 0xA7, },
- { 0x09, 0x33, 0x0C, 0x33, 0xF7, 0x11, 0x47, 0xE8, 0x3D, 0x19, 0x2F, 0xC7, 0x82, 0xCD, 0x1B, 0x47,
- 0x53, 0x11, 0x1B, 0x17, 0x3B, 0x3B, 0x05, 0xD2, 0x2F, 0xA0, 0x80, 0x86, 0xE3, 0xB0, 0xF7, 0x12,
- 0xFC, 0xC7, 0xC7, 0x1A, 0x55, 0x7E, 0x2D, 0xB9, 0x66, 0xC3, 0xE9, 0xFA, 0x91, 0x74, 0x60, 0x39, },
- };
-
- struct sha384 s;
- size_t i;
-
- for (i = 0; i < sizeof(test_datas) / sizeof(*test_datas); i++) {
- sha384_init(&s);
- sha384_update(&s, test_datas[i], strlen(test_datas[i]));
- sha384_finish(&s);
-
- if (memcmp(s.h, test_hashes[i], sizeof(s.h)))
- fprintf(stderr, "%s: hash %zu mismatch\n", __FUNCTION__, i);
- }
-}
-
-void test_sha512(void)
-{
- const char *test_datas[] = {
- "abc",
- "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
- };
-
- const uint8_t test_hashes[][SHA512_HASHSIZE] = {
- { 0xDD, 0xAF, 0x35, 0xA1, 0x93, 0x61, 0x7A, 0xBA, 0xCC, 0x41, 0x73, 0x49, 0xAE, 0x20, 0x41, 0x31,
- 0x12, 0xE6, 0xFA, 0x4E, 0x89, 0xA9, 0x7E, 0xA2, 0x0A, 0x9E, 0xEE, 0xE6, 0x4B, 0x55, 0xD3, 0x9A,
- 0x21, 0x92, 0x99, 0x2A, 0x27, 0x4F, 0xC1, 0xA8, 0x36, 0xBA, 0x3C, 0x23, 0xA3, 0xFE, 0xEB, 0xBD,
- 0x45, 0x4D, 0x44, 0x23, 0x64, 0x3C, 0xE8, 0x0E, 0x2A, 0x9A, 0xC9, 0x4F, 0xA5, 0x4C, 0xA4, 0x9F, },
- { 0x8E, 0x95, 0x9B, 0x75, 0xDA, 0xE3, 0x13, 0xDA, 0x8C, 0xF4, 0xF7, 0x28, 0x14, 0xFC, 0x14, 0x3F,
- 0x8F, 0x77, 0x79, 0xC6, 0xEB, 0x9F, 0x7F, 0xA1, 0x72, 0x99, 0xAE, 0xAD, 0xB6, 0x88, 0x90, 0x18,
- 0x50, 0x1D, 0x28, 0x9E, 0x49, 0x00, 0xF7, 0xE4, 0x33, 0x1B, 0x99, 0xDE, 0xC4, 0xB5, 0x43, 0x3A,
- 0xC7, 0xD3, 0x29, 0xEE, 0xB6, 0xDD, 0x26, 0x54, 0x5E, 0x96, 0xE5, 0x5B, 0x87, 0x4B, 0xE9, 0x09, },
- };
-
- struct sha512 s;
- size_t i;
-
- for (i = 0; i < sizeof(test_datas) / sizeof(*test_datas); i++) {
- sha512_init(&s);
- sha512_update(&s, test_datas[i], strlen(test_datas[i]));
- sha512_finish(&s);
-
- if (memcmp(s.h, test_hashes[i], sizeof(s.h)))
- fprintf(stderr, "%s: hash %zu mismatch\n", __FUNCTION__, i);
- }
-}
-
-void test_hmac_sha512(void)
-{
- const char *test_datas[] = {
- "Sample message for keylen=blocklen",
- "Sample message for keylen<blocklen",
- "Sample message for keylen=blocklen",
- "Sample message for keylen<blocklen, with truncated tag"
- };
-
- uint8_t keybuf[256];
- const size_t keylens[] = {
- 128, 64, 200, 49
- };
-
- const size_t taglens[] = {
- 64, 64, 64, 32
- };
-
- const uint8_t test_tags[][SHA512_HASHSIZE] = {
- { 0xFC, 0x25, 0xE2, 0x40, 0x65, 0x8C, 0xA7, 0x85, 0xB7, 0xA8, 0x11, 0xA8, 0xD3, 0xF7, 0xB4, 0xCA, 0x48, 0xCF, 0xA2, 0x6A, 0x8A, 0x36, 0x6B, 0xF2, 0xCD, 0x1F, 0x83, 0x6B, 0x05, 0xFC, 0xB0, 0x24, 0xBD, 0x36, 0x85, 0x30, 0x81, 0x81, 0x1D, 0x6C, 0xEA, 0x42, 0x16, 0xEB, 0xAD, 0x79, 0xDA, 0x1C, 0xFC, 0xB9, 0x5E, 0xA4, 0x58, 0x6B, 0x8A, 0x0C, 0xE3, 0x56, 0x59, 0x6A, 0x55, 0xFB, 0x13, 0x47, },
- { 0xFD, 0x44, 0xC1, 0x8B, 0xDA, 0x0B, 0xB0, 0xA6, 0xCE, 0x0E, 0x82, 0xB0, 0x31, 0xBF, 0x28, 0x18, 0xF6, 0x53, 0x9B, 0xD5, 0x6E, 0xC0, 0x0B, 0xDC, 0x10, 0xA8, 0xA2, 0xD7, 0x30, 0xB3, 0x63, 0x4D, 0xE2, 0x54, 0x5D, 0x63, 0x9B, 0x0F, 0x2C, 0xF7, 0x10, 0xD0, 0x69, 0x2C, 0x72, 0xA1, 0x89, 0x6F, 0x1F, 0x21, 0x1C, 0x2B, 0x92, 0x2D, 0x1A, 0x96, 0xC3, 0x92, 0xE0, 0x7E, 0x7E, 0xA9, 0xFE, 0xDC, },
- { 0xD9, 0x3E, 0xC8, 0xD2, 0xDE, 0x1A, 0xD2, 0xA9, 0x95, 0x7C, 0xB9, 0xB8, 0x3F, 0x14, 0xE7, 0x6A, 0xD6, 0xB5, 0xE0, 0xCC, 0xE2, 0x85, 0x07, 0x9A, 0x12, 0x7D, 0x3B, 0x14, 0xBC, 0xCB, 0x7A, 0xA7, 0x28, 0x6D, 0x4A, 0xC0, 0xD4, 0xCE, 0x64, 0x21, 0x5F, 0x2B, 0xC9, 0xE6, 0x87, 0x0B, 0x33, 0xD9, 0x74, 0x38, 0xBE, 0x4A, 0xAA, 0x20, 0xCD, 0xA5, 0xC5, 0xA9, 0x12, 0xB4, 0x8B, 0x8E, 0x27, 0xF3, },
- { 0x00, 0xF3, 0xE9, 0xA7, 0x7B, 0xB0, 0xF0, 0x6D, 0xE1, 0x5F, 0x16, 0x06, 0x03, 0xE4, 0x2B, 0x50, 0x28, 0x75, 0x88, 0x08, 0x59, 0x66, 0x64, 0xC0, 0x3E, 0x1A, 0xB8, 0xFB, 0x2B, 0x07, 0x67, 0x78, },
- };
-
- size_t i;
-
- for (i = 0; i < sizeof(keybuf); i++)
- keybuf[i] = i;
-
- for (i = 0; i < sizeof(test_datas) / sizeof(*test_datas); i++) {
- uint8_t hmacbuf[SHA512_HASHSIZE];
- sha512_hmac(keybuf, keylens[i], test_datas[i], strlen(test_datas[i]), hmacbuf);
-
- if (memcmp(hmacbuf, test_tags[i], taglens[i])) {
- fprintf(stderr, "%s: HMAC %zu mismatch, got:\n\t", __FUNCTION__, i);
- hexdump(stderr, hmacbuf, taglens[i]);
- fprintf(stderr, "\n, expected:\n\t");
- hexdump(stderr, test_tags[i], taglens[i]);
- fprintf(stderr, "\n");
- }
- }
-}
-
-void test_totp_sha1(void)
-{
- /* Test vectors from RFC 6238 appendix B */
- const char *key = "12345678901234567890";
- const uint8_t period = 30;
- const time_t t0 = 0;
-
- const time_t times[] = {
- 59, 1111111109, 1111111111, 1234567890, 2000000000, 20000000000
- };
- const uint32_t totps[] = {
- 94287082, /*0*/7081804, 14050471, 89005924, 69279037, 65353130
- };
- const uint32_t modulo = 100000000;
- size_t i;
-
- for (i = 0; i < sizeof(times) / sizeof(*times); i++) {
- uint32_t token = totp(key, strlen(key), times[i], period, t0, sha1_hmac, SHA1_HASHSIZE);
-
- if (token % modulo != totps[i])
- fprintf(stderr, "%s: token %zu mismatch, got %08" PRIu32 ", expected %08" PRIu32 "\n",
- __FUNCTION__, i, token % modulo, totps[i]);
- }
-}
-
-void test_totp_sha256(void)
-{
- /* Test vectors from RFC 6238 appendix B but key/seed from appendix A */
- const char *key = "12345678901234567890123456789012";
- const uint8_t period = 30;
- const time_t t0 = 0;
-
- const time_t times[] = {
- 59, 1111111109, 1111111111, 1234567890, 2000000000, 20000000000
- };
- const uint32_t totps[] = {
- 46119246, 68084774, 67062674, 91819424, 90698825, 77737706
- };
- const uint32_t modulo = 100000000;
- size_t i;
-
- for (i = 0; i < sizeof(times) / sizeof(*times); i++) {
- uint32_t token = totp(key, strlen(key), times[i], period, t0, sha256_hmac, SHA256_HASHSIZE);
-
- if (token % modulo != totps[i])
- fprintf(stderr, "%s: token %zu mismatch, got %08" PRIu32 ", expected %08" PRIu32 "\n",
- __FUNCTION__, i, token % modulo, totps[i]);
- }
-}
-
-void test_totp_sha512(void)
-{
- /* Test vectors from RFC 6238 appendix B but key/seed from appendix A */
- const char *key = "1234567890123456789012345678901234567890123456789012345678901234";
- const uint8_t period = 30;
- const time_t t0 = 0;
-
- const time_t times[] = {
- 59, 1111111109, 1111111111, 1234567890, 2000000000, 20000000000
- };
- const uint32_t totps[] = {
- 90693936, 25091201, 99943326, 93441116, 38618901, 47863826
- };
- const uint32_t modulo = 100000000;
- size_t i;
-
- for (i = 0; i < sizeof(times) / sizeof(*times); i++) {
- uint32_t token = totp(key, strlen(key), times[i], period, t0, sha512_hmac, SHA512_HASHSIZE);
-
- if (token % modulo != totps[i])
- fprintf(stderr, "%s: token %zu mismatch, got %08" PRIu32 ", expected %08" PRIu32 "\n",
- __FUNCTION__, i, token % modulo, totps[i]);
- }
-}
-
-void test_debase32(void)
-{
- const char *base32s[] = {
- "MFRGG",
- "MFRGGZDFMZTWQ2LKNM",
- "MFRGGZDF",
- };
-
- const char *plaintext[] = {
- "abc",
- "abcdefghijk",
- "abcde"
- };
-
- size_t i;
-
- for (i = 0; i < sizeof(base32s) / sizeof(*base32s); i++) {
- char buffer[64];
- int len = sprintf(buffer, "%s", base32s[i]);
- buffer[debase32(buffer, len)] = '\0';
-
- if (strcmp(buffer, plaintext[i]))
- fprintf(stderr, "%s: plaintext mismatch, got %s, expected %s\n",
- __FUNCTION__, buffer, plaintext[i]);
- }
-}
-
-int main(int argc, char **argv)
-{
- (void)argc;
- (void)argv;
-
- test_sha1();
- test_sha224();
- test_sha256();
- test_sha384();
- test_sha512();
-
- test_hmac_sha1();
- test_hmac_sha256();
- test_hmac_sha512();
-
- test_totp_sha1();
- test_totp_sha256();
- test_totp_sha512();
-
- test_debase32();
-}
diff --git a/test.sh b/test.sh
@@ -0,0 +1,67 @@
+#!/bin/sh
+
+BIN="$1"
+
+trap "rm \$DB" EXIT
+PASS="KhxvbPvY4dbyZ/zkXY+c/PCJ4lU"
+DB="$(mktemp)"
+rm "$DB"
+RESULT=true
+
+# Secret = "12345678901234567890", algo SHA-1, period 30 seconds, 8 digits
+"$BIN" -k "$PASS" -f "$DB" -a "otpauth://totp/RFC6238:SHA1?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ&issuer=RFC6238&algorithm=SHA1&digits=8&period=30"
+# Secret = "12345678901234567890123456789012", algo SHA-256, period 30 seconds, 8 digits
+"$BIN" -k "$PASS" -f "$DB" -a "otpauth://totp/RFC6238:SHA256?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZA&issuer=RFC6238&algorithm=SHA256&digits=8&period=30"
+# Secret = "1234567890123456789012345678901234567890123456789012345678901234", algo SHA-256, period 30 seconds, 8 digits
+"$BIN" -k "$PASS" -f "$DB" -a "otpauth://totp/RFC6238:SHA512?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNA&issuer=RFC6238&algorithm=SHA512&digits=8&period=30"
+
+while IFS='|' read _ stamp _ _ token algo _; do
+ algo="$(echo $algo)"
+ token="$(echo $token)"
+ stamp="$(echo $stamp)"
+ gentok="$("$BIN" -k "$PASS" -f "$DB" -T "$stamp" -t "RFC6238:$algo")"
+ if ! test "$token" = "$gentok"; then
+ echo "Token generation failed for $algo at time $stamp, got $gentok, expected $token" >&2
+ RESULT=false
+ fi
+ read _ || break
+# Test data from RFC 6238
+done <<FOO
+| 59 | 1970-01-01 | 0000000000000001 | 94287082 | SHA1 |
+| | 00:00:59 | | | |
+| 59 | 1970-01-01 | 0000000000000001 | 46119246 | SHA256 |
+| | 00:00:59 | | | |
+| 59 | 1970-01-01 | 0000000000000001 | 90693936 | SHA512 |
+| | 00:00:59 | | | |
+| 1111111109 | 2005-03-18 | 00000000023523EC | 07081804 | SHA1 |
+| | 01:58:29 | | | |
+| 1111111109 | 2005-03-18 | 00000000023523EC | 68084774 | SHA256 |
+| | 01:58:29 | | | |
+| 1111111109 | 2005-03-18 | 00000000023523EC | 25091201 | SHA512 |
+| | 01:58:29 | | | |
+| 1111111111 | 2005-03-18 | 00000000023523ED | 14050471 | SHA1 |
+| | 01:58:31 | | | |
+| 1111111111 | 2005-03-18 | 00000000023523ED | 67062674 | SHA256 |
+| | 01:58:31 | | | |
+| 1111111111 | 2005-03-18 | 00000000023523ED | 99943326 | SHA512 |
+| | 01:58:31 | | | |
+| 1234567890 | 2009-02-13 | 000000000273EF07 | 89005924 | SHA1 |
+| | 23:31:30 | | | |
+| 1234567890 | 2009-02-13 | 000000000273EF07 | 91819424 | SHA256 |
+| | 23:31:30 | | | |
+| 1234567890 | 2009-02-13 | 000000000273EF07 | 93441116 | SHA512 |
+| | 23:31:30 | | | |
+| 2000000000 | 2033-05-18 | 0000000003F940AA | 69279037 | SHA1 |
+| | 03:33:20 | | | |
+| 2000000000 | 2033-05-18 | 0000000003F940AA | 90698825 | SHA256 |
+| | 03:33:20 | | | |
+| 2000000000 | 2033-05-18 | 0000000003F940AA | 38618901 | SHA512 |
+| | 03:33:20 | | | |
+| 20000000000 | 2603-10-11 | 0000000027BC86AA | 65353130 | SHA1 |
+| | 11:33:20 | | | |
+| 20000000000 | 2603-10-11 | 0000000027BC86AA | 77737706 | SHA256 |
+| | 11:33:20 | | | |
+| 20000000000 | 2603-10-11 | 0000000027BC86AA | 47863826 | SHA512 |
+| | 11:33:20 | | | |
+FOO
+$RESULT
diff --git a/token.c b/token.c
@@ -33,12 +33,11 @@ static inline uint8_t dehex(const uint8_t *s)
static struct bytes uridecode(struct bytes data, bool getarg)
{
uint8_t *w = data.data;
- uint8_t *end = w + data.len;
const uint8_t *r = w;
- while (r < end) {
+ while (r < data.end) {
if (*r == '%') {
- if (r + 2 >= end)
+ if (r + 2 >= data.end)
return (struct bytes){ 0 };
*w++ = dehex(++r);
if (!w[-1])
@@ -51,7 +50,7 @@ static struct bytes uridecode(struct bytes data, bool getarg)
*w++ = *r++;
}
- return (struct bytes){ data.data, w - data.data };
+ return (struct bytes){ data.data, w };
}
@@ -93,14 +92,14 @@ struct token token_parse_uri(char *data) {
rv.desc = uridecode(bytesnc(str, i - str), false);
- if ((v = memchr(rv.desc.data, ':', rv.desc.len))) {
- rv.issuer = (struct bytes){ rv.desc.data, pdiff(rv.desc.data, v++) };
+ if ((v = memchr(rv.desc.data, ':', bytes_len(rv.desc)))) {
+ rv.issuer = bytesec(rv.desc.data, v++);
rv.desc = bytesnc(v, i - v);
}
while (*i++) {
if ((v = if_prefix(i, "secret="))) {
- if (rv.key.len)
+ if (bytes_len(rv.key))
croak("Multiple secrets in URI");
i = v + strcspn(v, "&");
rv.key = debase32(bytesnc(v, i - v));
@@ -113,8 +112,7 @@ struct token token_parse_uri(char *data) {
} else if ((v = if_prefix(i, "issuer="))) {
i = v + strcspn(v, "&");
struct bytes newiss = uridecode(bytesnc(v, i - v), true);
- if (rv.issuer.len && (newiss.len != rv.issuer.len ||
- !bytesequal(rv.issuer, newiss))) {
+ if (bytes_len(rv.issuer) && !bytesequal(rv.issuer, newiss)) {
errno = EINVAL;
return rv;
}
@@ -132,7 +130,7 @@ struct token token_parse_uri(char *data) {
}
}
- if (rv.key.len && rv.desc.len)
+ if (bytes_len(rv.key) && bytes_len(rv.desc))
rv.valid = true;
else
errno = EINVAL;
diff --git a/util.c b/util.c
@@ -155,7 +155,7 @@ struct bytes debase32(struct bytes data)
['5'] = 30, ['6'] = 31, ['7'] = 32
};
- for (rp = data.data; rp < data.data + data.len && *rp && *rp != '='; rp++) {
+ for (rp = data.data; rp < data.end && *rp && *rp != '='; rp++) {
uint8_t c = val[*rp];
if (!c)
return (struct bytes){ 0 };
@@ -167,7 +167,7 @@ struct bytes debase32(struct bytes data)
}
}
- return (struct bytes){ data.data, wp - data.data };
+ return (struct bytes){ data.data, wp };
}
void randmem(void *mem, size_t n)
diff --git a/util.h b/util.h
@@ -9,7 +9,7 @@
struct bytes {
uint8_t *data;
- size_t len;
+ uint8_t *end;
};
typedef void (*digest_init)(void *c);
@@ -81,12 +81,12 @@ static inline void *mempush(void *dst, const void *src, size_t n)
static inline void *mempushb(void *dst, struct bytes data)
{
- return mempush(dst, data.data, data.len);
+ return mempush(dst, data.data, data.end - data.data);
}
static inline int bytesequal(struct bytes a, struct bytes b)
{
- return a.len == b.len && !memcmp(a.data, b.data, a.len);
+ return a.end - a.data == b.end - b.data && !memcmp(a.data, b.data, a.end - a.data);
}
static inline char *if_prefix(const char *s, const char *prefix)
@@ -104,9 +104,18 @@ static inline ptrdiff_t pdiff(const void *a, const void *b) {
return (const char *)b - (const char *)a;
}
-static inline struct bytes bytesnc(char *data, size_t len)
+static inline struct bytes bytesec(void *data, void *end)
{
- return (struct bytes){ (void *)data, len };
+ return (struct bytes){ data, end };
+}
+
+static inline struct bytes bytesnc(void *data, size_t n)
+{
+ return (struct bytes){ data, (uint8_t *)data + n };
+}
+
+static inline ptrdiff_t bytes_len(struct bytes bytes) {
+ return bytes.end - bytes.data;
}
#endif