hare

The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

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:
Acrypto/hmac/+test.ha | 155+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acrypto/hmac/README | 2++
Acrypto/hmac/hmac.ha | 98+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mscripts/gen-stdlib | 16++++++++++++++++
Mstdlib.mk | 33+++++++++++++++++++++++++++++++++
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