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:
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 = ©
+
+ // 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 \