hare

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

commit f6df72bc29911f287f8b32c09872f1f188951fe1
parent 75231b4510e10cb3def577c213d1142e0e593d5c
Author: Thomas Bracht Laumann Jespersen <t@laumann.xyz>
Date:   Fri,  2 Apr 2021 18:03:42 +0200

crypto: Add MD5

Diffstat:
Acrypto/md5/+test.ha | 41+++++++++++++++++++++++++++++++++++++++++
Acrypto/md5/md5.ha | 232+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mscripts/gen-stdlib | 12++++++++++++
Mstdlib.mk | 27+++++++++++++++++++++++++++
4 files changed, 312 insertions(+), 0 deletions(-)

diff --git a/crypto/md5/+test.ha b/crypto/md5/+test.ha @@ -0,0 +1,41 @@ +use fmt; +use hash; +use strings; +use strio; +use io; + +@test fn md5() void = { + let md5 = md5(); + defer hash::finish(md5); + + const vectors = [ + ("", "d41d8cd98f00b204e9800998ecf8427e"), + ("abc", "900150983cd24fb0d6963f7d28e17f72"), + ("hello world", "5eb63bbbe01eeed093cb22bb8f5acdc3"), + ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", "8215ef0796a20bcaaae116d3876c664a"), + ("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", "03dd8807a93175fb062dfb55dc7d359c"), + ("Hare is a cool language", "391601a424a3331251b7fbaaa073458d"), + ("'UNIX was not designed to stop its users from doing stupid things, as that would also stop them from doing clever things' - Doug Gwyn", "e0d32da5235e0375e887d5dbff3bf44b"), + ("'Life is too short to run proprietary software' - Bdale Garbee", "09f9326df2fde54e53a15a6482c2bc26"), + ("'The central enemy of reliability is complexity.' - Geer et al", "839182c45d66fd79e8be1d9faacc1b00"), + ]; + + for (let i = 0z; i < len(vectors); i += 1) { + const vector = vectors[i]; + hash::reset(md5); + hash::write(md5, strings::toutf8(vector.0)); + let sum = hash::sum(md5); + defer free(sum); + + let hex = strio::dynamic(); + defer io::close(hex); + for (let j = 0z; j < SIZE; j += 1) { + fmt::fprintf(hex, "{:02x}", sum[j]); + }; + if (strio::string(hex) != vector.1) { + fmt::errorfln("Vector {}: {} != {}", + i, strio::string(hex), vector.1); + abort(); + }; + }; +}; diff --git a/crypto/md5/md5.ha b/crypto/md5/md5.ha @@ -0,0 +1,232 @@ +use hash; +use io; +use crypto::math; +use endian; + +// The size, in bytes, of a MD5 digest. +export def SIZE: size = 16; + +def chunk: size = 64; +def init0: u32 = 0x67452301; +def init1: u32 = 0xEFCDAB89; +def init2: u32 = 0x98BADCFE; +def init3: u32 = 0x10325476; + +type digest = struct { + hash: hash::hash, + h: [4]u32, + x: [chunk]u8, + nx: size, + ln: size, +}; + +// Creates a [hash::hash] which computes a MD5 hash as defined in RFC 1321. Note +// that MD5 is cryptographically broken and should not be used for secure +// applications. Where possible, applications are encouraged to use [sha256] or +// [sha512] instead. +export fn md5() *hash::hash = { + let md5 = alloc(digest { + hash = hash::hash { + stream = io::stream { + writer = &write, + closer = &close, + ... + }, + sum = &sum, + reset = &reset, + sz = SIZE, + ... + }, + }); + let hash = &md5.hash; + hash::reset(hash); + return hash; +}; + +fn write(st: *io::stream, buf: const []u8) (size | io::error) = { + let h = st: *digest; + let b: []u8 = buf; + let nn = len(buf); + + h.ln += nn; + + if (h.nx > 0) { + // Compute how many bytes can be copied into h.x + let r = len(h.x) - h.nx; + let n = if (nn > r) r else nn; + h.x[h.nx..] = b[..n]; + h.nx += n; + if (h.nx == chunk) { + block(h, h.x[..]); + h.nx = 0; + }; + b = b[n..]; + }; + if (len(b) >= chunk) { + let n = len(b) & ~(chunk - 1); + block(h, b[..n]); + b = b[n..]; + }; + if (len(b) > 0) { + let n = len(b); + h.x[..n] = b[..]; + h.nx = n; + }; + return nn; +}; + +fn close(st: *io::stream) void = free(st); + +fn reset(h: *hash::hash) void = { + let h = h: *digest; + h.h[0] = init0; + h.h[1] = init1; + h.h[2] = init2; + h.h[3] = init3; + h.nx = 0; + h.ln = 0; +}; + +fn sum(h: *hash::hash) []u8 = { + let h = h: *digest; + let copy = *h; + let h = &copy; + + // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. + let ln = h.ln; + let tmp: [1 + 63 + 8]u8 = [0x80, 0...]; + const pad = (55 - ln) % 64; + endian::leputu32(tmp[1+pad..], (ln << 3) : u32); + write(&h.hash.stream, tmp[..1+pad+8]); // append the length in bits + + assert(h.nx == 0); + + // Where we write the digest + let d: [SIZE]u8 = [0...]; + endian::leputu32(d[0..], h.h[0]); + endian::leputu32(d[4..], h.h[1]); + endian::leputu32(d[8..], h.h[2]); + endian::leputu32(d[12..], h.h[3]); + + let slice: []u8 = alloc([], SIZE); + append(slice, ...d); + return slice; +}; + +// A generic, pure Hare version of the MD5 block step +fn block(h: *digest, p: []u8) void = { + // load state + let a = h.h[0]; + let b = h.h[1]; + let c = h.h[2]; + let d = h.h[3]; + + for (len(p) >= chunk; p = p[chunk..]) { + + // save current state + let aa = a, bb = b, cc = c, dd = d; + + // load input block + let x0 = endian::legetu32(p[4 * 0x0..]); + let x0 = endian::legetu32(p[4 * 0x0..]); + let x1 = endian::legetu32(p[4 * 0x1..]); + let x2 = endian::legetu32(p[4 * 0x2..]); + let x3 = endian::legetu32(p[4 * 0x3..]); + let x4 = endian::legetu32(p[4 * 0x4..]); + let x5 = endian::legetu32(p[4 * 0x5..]); + let x6 = endian::legetu32(p[4 * 0x6..]); + let x7 = endian::legetu32(p[4 * 0x7..]); + let x8 = endian::legetu32(p[4 * 0x8..]); + let x9 = endian::legetu32(p[4 * 0x9..]); + let xa = endian::legetu32(p[4 * 0xa..]); + let xb = endian::legetu32(p[4 * 0xb..]); + let xc = endian::legetu32(p[4 * 0xc..]); + let xd = endian::legetu32(p[4 * 0xd..]); + let xe = endian::legetu32(p[4 * 0xe..]); + let xf = endian::legetu32(p[4 * 0xf..]); + + // round 1 + a = b + math::rotl32((((c^d)&b)^d)+a+x0+0xd76aa478, 7); + d = a + math::rotl32((((b^c)&a)^c)+d+x1+0xe8c7b756, 12); + c = d + math::rotl32((((a^b)&d)^b)+c+x2+0x242070db, 17); + b = c + math::rotl32((((d^a)&c)^a)+b+x3+0xc1bdceee, 22); + a = b + math::rotl32((((c^d)&b)^d)+a+x4+0xf57c0faf, 7); + d = a + math::rotl32((((b^c)&a)^c)+d+x5+0x4787c62a, 12); + c = d + math::rotl32((((a^b)&d)^b)+c+x6+0xa8304613, 17); + b = c + math::rotl32((((d^a)&c)^a)+b+x7+0xfd469501, 22); + a = b + math::rotl32((((c^d)&b)^d)+a+x8+0x698098d8, 7); + d = a + math::rotl32((((b^c)&a)^c)+d+x9+0x8b44f7af, 12); + c = d + math::rotl32((((a^b)&d)^b)+c+xa+0xffff5bb1, 17); + b = c + math::rotl32((((d^a)&c)^a)+b+xb+0x895cd7be, 22); + a = b + math::rotl32((((c^d)&b)^d)+a+xc+0x6b901122, 7); + d = a + math::rotl32((((b^c)&a)^c)+d+xd+0xfd987193, 12); + c = d + math::rotl32((((a^b)&d)^b)+c+xe+0xa679438e, 17); + b = c + math::rotl32((((d^a)&c)^a)+b+xf+0x49b40821, 22); + + // round 2 + a = b + math::rotl32((((b^c)&d)^c)+a+x1+0xf61e2562, 5); + d = a + math::rotl32((((a^b)&c)^b)+d+x6+0xc040b340, 9); + c = d + math::rotl32((((d^a)&b)^a)+c+xb+0x265e5a51, 14); + b = c + math::rotl32((((c^d)&a)^d)+b+x0+0xe9b6c7aa, 20); + a = b + math::rotl32((((b^c)&d)^c)+a+x5+0xd62f105d, 5); + d = a + math::rotl32((((a^b)&c)^b)+d+xa+0x02441453, 9); + c = d + math::rotl32((((d^a)&b)^a)+c+xf+0xd8a1e681, 14); + b = c + math::rotl32((((c^d)&a)^d)+b+x4+0xe7d3fbc8, 20); + a = b + math::rotl32((((b^c)&d)^c)+a+x9+0x21e1cde6, 5); + d = a + math::rotl32((((a^b)&c)^b)+d+xe+0xc33707d6, 9); + c = d + math::rotl32((((d^a)&b)^a)+c+x3+0xf4d50d87, 14); + b = c + math::rotl32((((c^d)&a)^d)+b+x8+0x455a14ed, 20); + a = b + math::rotl32((((b^c)&d)^c)+a+xd+0xa9e3e905, 5); + d = a + math::rotl32((((a^b)&c)^b)+d+x2+0xfcefa3f8, 9); + c = d + math::rotl32((((d^a)&b)^a)+c+x7+0x676f02d9, 14); + b = c + math::rotl32((((c^d)&a)^d)+b+xc+0x8d2a4c8a, 20); + + // round 3 + a = b + math::rotl32((b^c^d)+a+x5+0xfffa3942, 4); + d = a + math::rotl32((a^b^c)+d+x8+0x8771f681, 11); + c = d + math::rotl32((d^a^b)+c+xb+0x6d9d6122, 16); + b = c + math::rotl32((c^d^a)+b+xe+0xfde5380c, 23); + a = b + math::rotl32((b^c^d)+a+x1+0xa4beea44, 4); + d = a + math::rotl32((a^b^c)+d+x4+0x4bdecfa9, 11); + c = d + math::rotl32((d^a^b)+c+x7+0xf6bb4b60, 16); + b = c + math::rotl32((c^d^a)+b+xa+0xbebfbc70, 23); + a = b + math::rotl32((b^c^d)+a+xd+0x289b7ec6, 4); + d = a + math::rotl32((a^b^c)+d+x0+0xeaa127fa, 11); + c = d + math::rotl32((d^a^b)+c+x3+0xd4ef3085, 16); + b = c + math::rotl32((c^d^a)+b+x6+0x04881d05, 23); + a = b + math::rotl32((b^c^d)+a+x9+0xd9d4d039, 4); + d = a + math::rotl32((a^b^c)+d+xc+0xe6db99e5, 11); + c = d + math::rotl32((d^a^b)+c+xf+0x1fa27cf8, 16); + b = c + math::rotl32((c^d^a)+b+x2+0xc4ac5665, 23); + + // round 4 + a = b + math::rotl32((c^(b|~d))+a+x0+0xf4292244, 6); + d = a + math::rotl32((b^(a|~c))+d+x7+0x432aff97, 10); + c = d + math::rotl32((a^(d|~b))+c+xe+0xab9423a7, 15); + b = c + math::rotl32((d^(c|~a))+b+x5+0xfc93a039, 21); + a = b + math::rotl32((c^(b|~d))+a+xc+0x655b59c3, 6); + d = a + math::rotl32((b^(a|~c))+d+x3+0x8f0ccc92, 10); + c = d + math::rotl32((a^(d|~b))+c+xa+0xffeff47d, 15); + b = c + math::rotl32((d^(c|~a))+b+x1+0x85845dd1, 21); + a = b + math::rotl32((c^(b|~d))+a+x8+0x6fa87e4f, 6); + d = a + math::rotl32((b^(a|~c))+d+xf+0xfe2ce6e0, 10); + c = d + math::rotl32((a^(d|~b))+c+x6+0xa3014314, 15); + b = c + math::rotl32((d^(c|~a))+b+xd+0x4e0811a1, 21); + a = b + math::rotl32((c^(b|~d))+a+x4+0xf7537e82, 6); + d = a + math::rotl32((b^(a|~c))+d+xb+0xbd3af235, 10); + c = d + math::rotl32((a^(d|~b))+c+x2+0x2ad7d2bb, 15); + b = c + math::rotl32((d^(c|~a))+b+x9+0xeb86d391, 21); + + // add saved state + a += aa; + b += bb; + c += cc; + d += dd; + }; + + // save state + h.h[0] = a; + h.h[1] = b; + h.h[2] = c; + h.h[3] = d; +}; diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib @@ -182,6 +182,17 @@ crypto_sha512() { fi } +crypto_md5() { + if [ $testing -eq 0 ] + then + gen_srcs crypto::md5 md5.ha + gen_ssa crypto::md5 hash io endian + else + gen_srcs crypto::md5 md5.ha +test.ha + gen_ssa crypto::md5 hash io endian fmt strio strings + fi +} + dirs() { gen_srcs dirs \ xdg.ha @@ -568,6 +579,7 @@ bufio bytes crypto_math crypto_random +crypto_md5 crypto_sha1 crypto_sha256 crypto_sha512 diff --git a/stdlib.mk b/stdlib.mk @@ -84,6 +84,9 @@ hare_stdlib_deps+=$(stdlib_crypto_math) stdlib_crypto_random=$(HARECACHE)/crypto/random/crypto_random.o hare_stdlib_deps+=$(stdlib_crypto_random) +stdlib_crypto_md5=$(HARECACHE)/crypto/md5/crypto_md5.o +hare_stdlib_deps+=$(stdlib_crypto_md5) + stdlib_crypto_sha1=$(HARECACHE)/crypto/sha1/crypto_sha1.o hare_stdlib_deps+=$(stdlib_crypto_sha1) @@ -264,6 +267,16 @@ $(HARECACHE)/crypto/random/crypto_random.ssa: $(stdlib_crypto_random_srcs) $(std @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Ncrypto::random \ -t$(HARECACHE)/crypto/random/crypto_random.td $(stdlib_crypto_random_srcs) +# crypto::md5 +stdlib_crypto_md5_srcs= \ + $(STDLIB)/crypto/md5/md5.ha + +$(HARECACHE)/crypto/md5/crypto_md5.ssa: $(stdlib_crypto_md5_srcs) $(stdlib_rt) $(stdlib_hash) $(stdlib_io) $(stdlib_endian) + @printf 'HAREC \t$@\n' + @mkdir -p $(HARECACHE)/crypto/md5 + @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Ncrypto::md5 \ + -t$(HARECACHE)/crypto/md5/crypto_md5.td $(stdlib_crypto_md5_srcs) + # crypto::sha1 stdlib_crypto_sha1_srcs= \ $(STDLIB)/crypto/sha1/sha1.ha @@ -834,6 +847,9 @@ hare_testlib_deps+=$(testlib_crypto_math) testlib_crypto_random=$(TESTCACHE)/crypto/random/crypto_random.o hare_testlib_deps+=$(testlib_crypto_random) +testlib_crypto_md5=$(TESTCACHE)/crypto/md5/crypto_md5.o +hare_testlib_deps+=$(testlib_crypto_md5) + testlib_crypto_sha1=$(TESTCACHE)/crypto/sha1/crypto_sha1.o hare_testlib_deps+=$(testlib_crypto_sha1) @@ -1014,6 +1030,17 @@ $(TESTCACHE)/crypto/random/crypto_random.ssa: $(testlib_crypto_random_srcs) $(te @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Ncrypto::random \ -t$(TESTCACHE)/crypto/random/crypto_random.td $(testlib_crypto_random_srcs) +# crypto::md5 +testlib_crypto_md5_srcs= \ + $(STDLIB)/crypto/md5/md5.ha \ + $(STDLIB)/crypto/md5/+test.ha + +$(TESTCACHE)/crypto/md5/crypto_md5.ssa: $(testlib_crypto_md5_srcs) $(testlib_rt) $(testlib_hash) $(testlib_io) $(testlib_endian) $(testlib_fmt) $(testlib_strio) $(testlib_strings) + @printf 'HAREC \t$@\n' + @mkdir -p $(TESTCACHE)/crypto/md5 + @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Ncrypto::md5 \ + -t$(TESTCACHE)/crypto/md5/crypto_md5.td $(testlib_crypto_md5_srcs) + # crypto::sha1 testlib_crypto_sha1_srcs= \ $(STDLIB)/crypto/sha1/sha1.ha \