commit 4f4919be38db84c32d92eab47705302c8bce1662
parent 6e13f80bcbb891b18c25194519a634da798cf0c9
Author: Armin Preiml <apreiml@strohwolke.at>
Date: Wed, 17 Nov 2021 12:30:40 +0100
add crypto::hmac
Signed-off-by: Armin Preiml <apreiml@strohwolke.at>
Diffstat:
5 files changed, 304 insertions(+), 0 deletions(-)
diff --git a/crypto/hmac/+test.ha b/crypto/hmac/+test.ha
@@ -0,0 +1,155 @@
+use bytes;
+use crypto::sha1;
+use encoding::hex;
+use hash;
+use io;
+use strings;
+
+fn assert_hmac_sha1(keystr: str, vectors: [](str, [20]u8)) void = {
+ let key = strings::toutf8(keystr);
+ let hmac = hmac(&sha1::create, key);
+ defer io::close(&hmac);
+
+ for (let i = 0z; i < len(vectors); i += 1) {
+ const vector = vectors[i];
+
+ hash::reset(&hmac);
+ hash::write(&hmac, strings::toutf8(vector.0));
+
+ let sum: [sha1::SIZE]u8 = [0...];
+ hash::sum(&hmac, sum);
+
+ assert(bytes::equal(vector.1, sum));
+ };
+};
+
+@test fn hmac_sha1_empty_key() void = {
+ const vectors: [_](str, [20]u8) = [
+ (
+ "",
+ [
+ 0xfb, 0xdb, 0x1d, 0x1b, 0x18, 0xaa, 0x6c, 0x08,
+ 0x32, 0x4b, 0x7d, 0x64, 0xb7, 0x1f, 0xb7, 0x63,
+ 0x70, 0x69, 0x0e, 0x1d,
+ ]
+ ),
+ (
+ "abc",
+ [
+ 0x9b, 0x4a, 0x91, 0x8f, 0x39, 0x8d, 0x74, 0xd3,
+ 0xe3, 0x67, 0x97, 0x0a, 0xba, 0x3c, 0xbe, 0x54,
+ 0xe4, 0xd2, 0xb5, 0xd9,
+ ]
+ ),
+ (
+ "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijkl"
+ "mnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopq"
+ "rstu",
+ [
+ 0xf4, 0x50, 0x13, 0xac, 0xc6, 0xa6, 0xa5, 0x3a,
+ 0x49, 0xbf, 0xc0, 0x7b, 0x1e, 0xd8, 0xf5, 0x39,
+ 0x5b, 0xf3, 0x9d, 0x96,
+ ]
+ ),
+ ];
+ assert_hmac_sha1("", vectors);
+};
+
+@test fn hmac_sha1() void = {
+ const vectors: [_](str, [20]u8) = [
+ (
+ "",
+ [
+ 0xc5, 0x89, 0xa2, 0x7a, 0xb0, 0xaf, 0x9f, 0xe7,
+ 0xe0, 0x38, 0x68, 0x24, 0x73, 0x5d, 0x22, 0x0d,
+ 0x04, 0x1b, 0x70, 0xf0,
+ ]
+ ),
+ (
+ "abc",
+ [
+ 0x85, 0x2d, 0x02, 0xc6, 0xfd, 0x3b, 0x51, 0xdf,
+ 0x64, 0x4a, 0xfb, 0x13, 0x81, 0x76, 0xb3, 0x07,
+ 0x61, 0xca, 0x95, 0x27,
+ ]
+ ),
+ (
+ "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijkl"
+ "mnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopq"
+ "rstu",
+ [
+ 0x41, 0x29, 0x43, 0xf0, 0x84, 0x0d, 0xc1, 0xa1,
+ 0x33, 0x8a, 0x16, 0xec, 0x3d, 0x73, 0x0b, 0x76,
+ 0x25, 0xac, 0xf0, 0xaa,
+ ]
+ ),
+ ];
+
+ assert_hmac_sha1("trustno1", vectors);
+};
+
+@test fn hmac_sha1_large_key() void = {
+ const vectors: [_](str, [20]u8) = [
+ (
+ "",
+ [
+ 0x24, 0xe4, 0x38, 0xcb, 0x07, 0x54, 0xef, 0x48,
+ 0x97, 0x20, 0x05, 0x65, 0xd5, 0xf2, 0x7f, 0x6e,
+ 0x62, 0x77, 0xfc, 0x95
+ ],
+ ),
+ (
+ "abc",
+ [
+ 0xe3, 0x1a, 0x77, 0xff, 0xf1, 0x4b, 0xbf, 0xba,
+ 0x5d, 0x2f, 0x8b, 0x44, 0xa6, 0x33, 0xd4, 0xc9,
+ 0xfc, 0x8a, 0xd0, 0xe9,
+ ]
+ ),
+ (
+ "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijkl"
+ "mnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopq"
+ "rstu",
+ [
+ 0x5a, 0x64, 0x69, 0x18, 0xa0, 0xe3, 0xaf, 0xe9,
+ 0xab, 0x45, 0x1d, 0xf3, 0xcd, 0xdd, 0x1e, 0x7e,
+ 0xb9, 0x19, 0xa7, 0x4b
+ ]
+ ),
+ ];
+
+ let key = "xxabcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhi"
+ "jklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu";
+ assert_hmac_sha1(key, vectors);
+};
+
+@test fn hmac_sha1_write_after_sum() void = {
+ const result: [2][20]u8 = [
+ [
+ 0x98, 0x66, 0x91, 0x5d, 0x9e, 0xa9, 0x29, 0x0c, 0xe3,
+ 0x07, 0x28, 0x5e, 0x6a, 0x86, 0xed, 0xe1, 0xca, 0x1a,
+ 0x0b, 0x6f,
+ ],
+ [
+ 0x30, 0x81, 0xd5, 0x60, 0xa1, 0x81, 0x21, 0x1d, 0x25,
+ 0xb3, 0xe4, 0xf3, 0xa7, 0x1e, 0x3a, 0x76, 0xae, 0x27,
+ 0xce, 0xfa,
+ ],
+ ];
+
+ let sha = sha1::sha1();
+ let hmac = hmac(&sha1::create, strings::toutf8("love"));
+
+ hash::reset(&hmac);
+ hash::write(&hmac, strings::toutf8("abcdefghbcdefghicdefghijdefghij"));
+
+ let sum: [sha1::SIZE]u8 = [0...];
+ hash::sum(&hmac, sum);
+
+ assert(bytes::equal(result[0], sum));
+
+ hash::write(&hmac, strings::toutf8("appendstuff"));
+ hash::finish(&hmac, sum);
+
+ assert(bytes::equal(result[1], sum));
+};
diff --git a/crypto/hmac/README b/crypto/hmac/README
@@ -0,0 +1,2 @@
+The hmac module computes the HMAC message authentication code algorithm defined
+by RFC 2104.
diff --git a/crypto/hmac/hmac.ha b/crypto/hmac/hmac.ha
@@ -0,0 +1,98 @@
+use crypto::math;
+use hash;
+use io;
+
+export type state = struct {
+ hash::hash,
+ hinner: *hash::hash,
+ houter: *hash::hash,
+ okeypad: []u8,
+ ikeypad: []u8,
+ sumbuf: []u8,
+};
+
+// Creates a [[hash::hash]] that computes an HMAC using the provided hash
+// creation function, e.g. [[crypto::sha256::create]].
+export fn hmac(create: *hash::createfunc, key: []u8) state = {
+ let hinner = create(), houter = create();
+ let bsz = hash::bsz(hinner);
+ let ikeypad: []u8 = bytes_alloc(bsz, 0);
+
+ // use ikeypad to store the prepared key temporarly to avoid an
+ // additional allocation.
+ if (len(key) > bsz) {
+ hash::write(hinner, key);
+ hash::sum(hinner, ikeypad);
+ hash::reset(hinner);
+ } else {
+ ikeypad[..len(key)] = key[..];
+ };
+
+ let okeypad: []u8 = alloc([], bsz);
+ for (let i = 0z; i < bsz; i += 1) {
+ append(okeypad, 0x5c ^ ikeypad[i]);
+ };
+
+ for (let i = 0z; i < bsz; i += 1) {
+ ikeypad[i] = 0x36 ^ ikeypad[i];
+ };
+
+ hash::write(hinner, ikeypad);
+
+ return state {
+ writer = &write,
+ reset = &reset,
+ sum = &sum,
+ closer = &close,
+ sz = hash::sz(hinner),
+ bsz = bsz,
+ hinner = hinner,
+ houter = houter,
+ okeypad = okeypad,
+ ikeypad = ikeypad,
+ sumbuf = bytes_alloc(hash::sz(hinner), 0),
+ ...
+ };
+};
+
+fn close(s: *io::stream) void = {
+ let h: *state = s: *state;
+ free(h.okeypad);
+ free(h.ikeypad);
+ free(h.sumbuf);
+ io::close(h.hinner);
+ io::close(h.houter);
+ free(h.hinner);
+ free(h.houter);
+};
+
+// TODO: https://todo.sr.ht/~sircmpwn/hare/285
+fn bytes_alloc(s: size, v: u8) []u8 = {
+ let b: []u8 = alloc([], s);
+ for (let i = 0z; i < s; i += 1) {
+ append(b, 0);
+ };
+ return b;
+};
+
+fn write(st: *io::stream, buf: const []u8) (size | io::error) = {
+ let h = st: *state;
+ return hash::write(h.hinner, buf);
+};
+
+fn sum(hash: *hash::hash, buf: []u8) void = {
+ let h = hash: *state;
+
+ hash::sum(h.hinner, h.sumbuf[0..h.hinner.sz]);
+
+ hash::reset(h.houter);
+ hash::write(h.houter, h.okeypad);
+ hash::write(h.houter, h.sumbuf[0..h.hinner.sz]);
+ hash::sum(h.houter, buf);
+};
+
+fn reset(hash: *hash::hash) void = {
+ let h = hash: *state;
+ hash::reset(h.hinner);
+ hash::write(h.hinner, h.ikeypad);
+};
diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib
@@ -230,6 +230,21 @@ crypto_cipher() {
gen_ssa crypto::cipher bytes crypto::math
}
+crypto_hmac() {
+ if [ $testing -eq 0 ]
+ then
+ gen_srcs crypto::hmac \
+ hmac.ha
+ gen_ssa crypto::hmac crypto::math hash io
+ else
+ gen_srcs crypto::hmac \
+ hmac.ha \
+ +test.ha
+ gen_ssa crypto::hmac bytes crypto::math crypto::sha1 \
+ encoding::hex hash io strings
+ fi
+}
+
crypto_math() {
gen_srcs crypto::math \
bits.ha
@@ -1093,6 +1108,7 @@ compress::zlib
crypto::aes
crypto::blake2b
crypto::cipher
+crypto::hmac
crypto::math
crypto::random linux freebsd
crypto::md5
diff --git a/stdlib.mk b/stdlib.mk
@@ -166,6 +166,12 @@ stdlib_deps_any+=$(stdlib_crypto_cipher_any)
stdlib_crypto_cipher_linux=$(stdlib_crypto_cipher_any)
stdlib_crypto_cipher_freebsd=$(stdlib_crypto_cipher_any)
+# gen_lib crypto::hmac (any)
+stdlib_crypto_hmac_any=$(HARECACHE)/crypto/hmac/crypto_hmac-any.o
+stdlib_deps_any+=$(stdlib_crypto_hmac_any)
+stdlib_crypto_hmac_linux=$(stdlib_crypto_hmac_any)
+stdlib_crypto_hmac_freebsd=$(stdlib_crypto_hmac_any)
+
# gen_lib crypto::math (any)
stdlib_crypto_math_any=$(HARECACHE)/crypto/math/crypto_math-any.o
stdlib_deps_any+=$(stdlib_crypto_math_any)
@@ -682,6 +688,16 @@ $(HARECACHE)/crypto/cipher/crypto_cipher-any.ssa: $(stdlib_crypto_cipher_any_src
@HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Ncrypto::cipher \
-t$(HARECACHE)/crypto/cipher/crypto_cipher.td $(stdlib_crypto_cipher_any_srcs)
+# crypto::hmac (+any)
+stdlib_crypto_hmac_any_srcs= \
+ $(STDLIB)/crypto/hmac/hmac.ha
+
+$(HARECACHE)/crypto/hmac/crypto_hmac-any.ssa: $(stdlib_crypto_hmac_any_srcs) $(stdlib_rt) $(stdlib_crypto_math_$(PLATFORM)) $(stdlib_hash_$(PLATFORM)) $(stdlib_io_$(PLATFORM))
+ @printf 'HAREC \t$@\n'
+ @mkdir -p $(HARECACHE)/crypto/hmac
+ @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Ncrypto::hmac \
+ -t$(HARECACHE)/crypto/hmac/crypto_hmac.td $(stdlib_crypto_hmac_any_srcs)
+
# crypto::math (+any)
stdlib_crypto_math_any_srcs= \
$(STDLIB)/crypto/math/bits.ha
@@ -1867,6 +1883,12 @@ testlib_deps_any+=$(testlib_crypto_cipher_any)
testlib_crypto_cipher_linux=$(testlib_crypto_cipher_any)
testlib_crypto_cipher_freebsd=$(testlib_crypto_cipher_any)
+# gen_lib crypto::hmac (any)
+testlib_crypto_hmac_any=$(TESTCACHE)/crypto/hmac/crypto_hmac-any.o
+testlib_deps_any+=$(testlib_crypto_hmac_any)
+testlib_crypto_hmac_linux=$(testlib_crypto_hmac_any)
+testlib_crypto_hmac_freebsd=$(testlib_crypto_hmac_any)
+
# gen_lib crypto::math (any)
testlib_crypto_math_any=$(TESTCACHE)/crypto/math/crypto_math-any.o
testlib_deps_any+=$(testlib_crypto_math_any)
@@ -2388,6 +2410,17 @@ $(TESTCACHE)/crypto/cipher/crypto_cipher-any.ssa: $(testlib_crypto_cipher_any_sr
@HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Ncrypto::cipher \
-t$(TESTCACHE)/crypto/cipher/crypto_cipher.td $(testlib_crypto_cipher_any_srcs)
+# crypto::hmac (+any)
+testlib_crypto_hmac_any_srcs= \
+ $(STDLIB)/crypto/hmac/hmac.ha \
+ $(STDLIB)/crypto/hmac/+test.ha
+
+$(TESTCACHE)/crypto/hmac/crypto_hmac-any.ssa: $(testlib_crypto_hmac_any_srcs) $(testlib_rt) $(testlib_bytes_$(PLATFORM)) $(testlib_crypto_math_$(PLATFORM)) $(testlib_crypto_sha1_$(PLATFORM)) $(testlib_encoding_hex_$(PLATFORM)) $(testlib_hash_$(PLATFORM)) $(testlib_io_$(PLATFORM)) $(testlib_strings_$(PLATFORM))
+ @printf 'HAREC \t$@\n'
+ @mkdir -p $(TESTCACHE)/crypto/hmac
+ @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Ncrypto::hmac \
+ -t$(TESTCACHE)/crypto/hmac/crypto_hmac.td $(testlib_crypto_hmac_any_srcs)
+
# crypto::math (+any)
testlib_crypto_math_any_srcs= \
$(STDLIB)/crypto/math/bits.ha