From ef6e1d50cbcd1756e16fc9b78c0e3a879262f1e0 Mon Sep 17 00:00:00 2001 From: Torben Ewert Date: Sat, 29 Feb 2020 13:45:45 +0100 Subject: [PATCH] TASK: Implement Sha1 --- README.md | 9 +- __tests__/ReCrypt_test.re | 26 +++++ lib/js/__tests__/ReCrypt_test.bs.js | 25 +++++ lib/js/src/ReCrypt.bs.js | 4 + lib/js/src/ReCrypt/ReCrypt_Constants.bs.js | 15 +++ lib/js/src/ReCrypt/ReCrypt_Functions.bs.js | 59 ++++++++--- lib/js/src/ReCrypt/ReCrypt_Preprocess.bs.js | 4 +- lib/js/src/ReCrypt/ReCrypt_Sha1.bs.js | 69 +++++++++++++ lib/js/src/ReCrypt/ReCrypt_Sha224.bs.js | 2 +- lib/js/src/ReCrypt/ReCrypt_Sha256.bs.js | 2 +- src/ReCrypt.re | 4 + src/ReCrypt.rei | 5 + src/ReCrypt/ReCrypt_Constants.re | 10 ++ src/ReCrypt/ReCrypt_Constants.rei | 1 + src/ReCrypt/ReCrypt_Functions.re | 37 ++++++- src/ReCrypt/ReCrypt_Functions.rei | 8 +- src/ReCrypt/ReCrypt_Preprocess.re | 2 +- src/ReCrypt/ReCrypt_Preprocess.rei | 2 +- src/ReCrypt/ReCrypt_Sha1.re | 109 ++++++++++++++++++++ src/ReCrypt/ReCrypt_Sha1.rei | 4 + src/ReCrypt/ReCrypt_Sha224.re | 2 +- src/ReCrypt/ReCrypt_Sha256.re | 2 +- 22 files changed, 367 insertions(+), 34 deletions(-) create mode 100644 lib/js/src/ReCrypt/ReCrypt_Sha1.bs.js create mode 100644 src/ReCrypt/ReCrypt_Sha1.re create mode 100644 src/ReCrypt/ReCrypt_Sha1.rei diff --git a/README.md b/README.md index 3957c4a..e79b43a 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,9 @@ Secure Hash Algorithms implemented in ReasonML based on # What state is it in? -**Incomplete** +**Complete** -- [ ] SHA-1 +- [x] SHA-1 - [x] SHA224 - [x] SHA256 - [x] SHA-384 @@ -37,7 +37,10 @@ Then add `@ewert-online/re-crypt` as a dependency to `bsconfig.json`: ### SHA-1 -_Not yet implemented_ +```reason +let hash = ReCrypt.Sha1.make("unicorn"); +/* 84de6753b298abd027fcd1d790eade2413eafb5a */ +``` ### SHA224 diff --git a/__tests__/ReCrypt_test.re b/__tests__/ReCrypt_test.re index 241f5aa..dfc4ac8 100644 --- a/__tests__/ReCrypt_test.re +++ b/__tests__/ReCrypt_test.re @@ -3,6 +3,7 @@ open Expect; type test = { input: string, + sha1: string, sha224: string, sha256: string, sha384: string, @@ -12,6 +13,7 @@ type test = { let tests = [| { input: "unicorn", + sha1: "84de6753b298abd027fcd1d790eade2413eafb5a", sha224: "983d515094574856a57db3a13741f0a65509bb640bfa551e78fa01d9", sha256: "c6cb50e7eea0df1fd3eaf52ada2358f5423afd7c0b5ee2395231a9b3208ffcaf", sha384: "de41efa2be0844783ea107630a79246fb1f7b3ab97b35c5e4b70130804f876f64b645c1064a775507a7ac3be457539f2", @@ -19,6 +21,7 @@ let tests = [| }, { input: "", + sha1: "da39a3ee5e6b4b0d3255bfef95601890afd80709", sha224: "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", sha256: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", sha384: "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b", @@ -26,6 +29,7 @@ let tests = [| }, { input: "Some pretty long string which probably exeed the length of one message block (now it really does it...aaaaaaaaaaaaaaaaaaaaaaah)", + sha1: "87fd7e086d40019262d6fc1ceccd9d731afa6be3", sha224: "935f26cf156817962231e82021da64b808b3c68fb9ff9f53ce9c75cc", sha256: "69c22a11ab4c21beeb184e4fa40556d41bc4d8fe431618b21715352fac67e9ec", sha384: "72fc789c77cce0797d67d62d27f93c0fe550b48e3069d4a354430dac61992946b25c37257eaf0df33ea319202de5767e", @@ -33,6 +37,7 @@ let tests = [| }, { input: "This message is exactly one bit longer than one 128bit word: f49932d6b40a4e4d8424196af7c3477397818e59e748f722186", + sha1: "b0aec6e4ac57547d24be7b38768a7c29c9456a7b", sha224: "19f7516f806e661dd2b72326bdfee7b4805560122becadb3801d8da7", sha256: "1a99b57fe8b12fcc8294f9238d1c1e893d19d798bd20a45a19f1d45d2543f891", sha384: "53cf6f7f2520d951de56c82d09954b61ec04e502ac49831a7e0e3cde0a15b096f48e70eb599b26ca4d4f608f36ada480", @@ -40,6 +45,7 @@ let tests = [| }, { input: "This message is exactly one bit longer than a 64bit word", + sha1: "042a302c0fb5ee74143d99a795bcc1036683f602", sha224: "a671f59e0187aef4a1812bb7380e3988b2ed9f41fd2e8a1db91ebbbe", sha256: "15cba6d94c28070f62aa1a16e51d44c0514f0acd5272d482ae3cfdb9a4f79f29", sha384: "db5889404ed7f067781fcb61f800d58bed3791b7f4eef2ca16984501db5549708bd0170dcf3c9bd2877bc571af65dbf9", @@ -47,6 +53,26 @@ let tests = [| }, |]; +describe("Sha1", () => { + describe("make()", () => { + test("\"" ++ tests[0].input ++ "\"", () => + expect(ReCrypt.Sha1.make(tests[0].input)) |> toBe(tests[0].sha1) + ); + test("\"" ++ tests[1].input ++ "\"", () => + expect(ReCrypt.Sha1.make(tests[1].input)) |> toBe(tests[1].sha1) + ); + test("\"" ++ tests[2].input ++ "\"", () => + expect(ReCrypt.Sha1.make(tests[2].input)) |> toBe(tests[2].sha1) + ); + test("\"" ++ tests[3].input ++ "\"", () => + expect(ReCrypt.Sha1.make(tests[3].input)) |> toBe(tests[3].sha1) + ); + test("\"" ++ tests[4].input ++ "\"", () => + expect(ReCrypt.Sha1.make(tests[4].input)) |> toBe(tests[4].sha1) + ); + }) +}); + describe("Sha224", () => { describe("make()", () => { test("\"" ++ tests[0].input ++ "\"", () => diff --git a/lib/js/__tests__/ReCrypt_test.bs.js b/lib/js/__tests__/ReCrypt_test.bs.js index 7961e92..95ed9cc 100644 --- a/lib/js/__tests__/ReCrypt_test.bs.js +++ b/lib/js/__tests__/ReCrypt_test.bs.js @@ -9,6 +9,7 @@ var Caml_array = require("bs-platform/lib/js/caml_array.js"); var tests = [ { input: "unicorn", + sha1: "84de6753b298abd027fcd1d790eade2413eafb5a", sha224: "983d515094574856a57db3a13741f0a65509bb640bfa551e78fa01d9", sha256: "c6cb50e7eea0df1fd3eaf52ada2358f5423afd7c0b5ee2395231a9b3208ffcaf", sha384: "de41efa2be0844783ea107630a79246fb1f7b3ab97b35c5e4b70130804f876f64b645c1064a775507a7ac3be457539f2", @@ -16,6 +17,7 @@ var tests = [ }, { input: "", + sha1: "da39a3ee5e6b4b0d3255bfef95601890afd80709", sha224: "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", sha256: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", sha384: "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b", @@ -23,6 +25,7 @@ var tests = [ }, { input: "Some pretty long string which probably exeed the length of one message block (now it really does it...aaaaaaaaaaaaaaaaaaaaaaah)", + sha1: "87fd7e086d40019262d6fc1ceccd9d731afa6be3", sha224: "935f26cf156817962231e82021da64b808b3c68fb9ff9f53ce9c75cc", sha256: "69c22a11ab4c21beeb184e4fa40556d41bc4d8fe431618b21715352fac67e9ec", sha384: "72fc789c77cce0797d67d62d27f93c0fe550b48e3069d4a354430dac61992946b25c37257eaf0df33ea319202de5767e", @@ -30,6 +33,7 @@ var tests = [ }, { input: "This message is exactly one bit longer than one 128bit word: f49932d6b40a4e4d8424196af7c3477397818e59e748f722186", + sha1: "b0aec6e4ac57547d24be7b38768a7c29c9456a7b", sha224: "19f7516f806e661dd2b72326bdfee7b4805560122becadb3801d8da7", sha256: "1a99b57fe8b12fcc8294f9238d1c1e893d19d798bd20a45a19f1d45d2543f891", sha384: "53cf6f7f2520d951de56c82d09954b61ec04e502ac49831a7e0e3cde0a15b096f48e70eb599b26ca4d4f608f36ada480", @@ -37,6 +41,7 @@ var tests = [ }, { input: "This message is exactly one bit longer than a 64bit word", + sha1: "042a302c0fb5ee74143d99a795bcc1036683f602", sha224: "a671f59e0187aef4a1812bb7380e3988b2ed9f41fd2e8a1db91ebbbe", sha256: "15cba6d94c28070f62aa1a16e51d44c0514f0acd5272d482ae3cfdb9a4f79f29", sha384: "db5889404ed7f067781fcb61f800d58bed3791b7f4eef2ca16984501db5549708bd0170dcf3c9bd2877bc571af65dbf9", @@ -44,6 +49,26 @@ var tests = [ } ]; +Jest.describe("Sha1", (function (param) { + return Jest.describe("make()", (function (param) { + Jest.test("\"" + (Caml_array.caml_array_get(tests, 0).input + "\""), (function (param) { + return Jest.Expect.toBe(Caml_array.caml_array_get(tests, 0).sha1, Jest.Expect.expect(Curry._1(ReCrypt.Sha1.make, Caml_array.caml_array_get(tests, 0).input))); + })); + Jest.test("\"" + (Caml_array.caml_array_get(tests, 1).input + "\""), (function (param) { + return Jest.Expect.toBe(Caml_array.caml_array_get(tests, 1).sha1, Jest.Expect.expect(Curry._1(ReCrypt.Sha1.make, Caml_array.caml_array_get(tests, 1).input))); + })); + Jest.test("\"" + (Caml_array.caml_array_get(tests, 2).input + "\""), (function (param) { + return Jest.Expect.toBe(Caml_array.caml_array_get(tests, 2).sha1, Jest.Expect.expect(Curry._1(ReCrypt.Sha1.make, Caml_array.caml_array_get(tests, 2).input))); + })); + Jest.test("\"" + (Caml_array.caml_array_get(tests, 3).input + "\""), (function (param) { + return Jest.Expect.toBe(Caml_array.caml_array_get(tests, 3).sha1, Jest.Expect.expect(Curry._1(ReCrypt.Sha1.make, Caml_array.caml_array_get(tests, 3).input))); + })); + return Jest.test("\"" + (Caml_array.caml_array_get(tests, 4).input + "\""), (function (param) { + return Jest.Expect.toBe(Caml_array.caml_array_get(tests, 4).sha1, Jest.Expect.expect(Curry._1(ReCrypt.Sha1.make, Caml_array.caml_array_get(tests, 4).input))); + })); + })); + })); + Jest.describe("Sha224", (function (param) { return Jest.describe("make()", (function (param) { Jest.test("\"" + (Caml_array.caml_array_get(tests, 0).input + "\""), (function (param) { diff --git a/lib/js/src/ReCrypt.bs.js b/lib/js/src/ReCrypt.bs.js index a91fe1f..dbbbd9e 100644 --- a/lib/js/src/ReCrypt.bs.js +++ b/lib/js/src/ReCrypt.bs.js @@ -1,11 +1,14 @@ // Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE 'use strict'; +var ReCrypt_Sha1 = require("./ReCrypt/ReCrypt_Sha1.bs.js"); var ReCrypt_Sha224 = require("./ReCrypt/ReCrypt_Sha224.bs.js"); var ReCrypt_Sha256 = require("./ReCrypt/ReCrypt_Sha256.bs.js"); var ReCrypt_Sha384 = require("./ReCrypt/ReCrypt_Sha384.bs.js"); var ReCrypt_Sha512 = require("./ReCrypt/ReCrypt_Sha512.bs.js"); +var Sha1 = ReCrypt_Sha1; + var Sha224 = ReCrypt_Sha224; var Sha256 = ReCrypt_Sha256; @@ -14,6 +17,7 @@ var Sha384 = ReCrypt_Sha384; var Sha512 = ReCrypt_Sha512; +exports.Sha1 = Sha1; exports.Sha224 = Sha224; exports.Sha256 = Sha256; exports.Sha384 = Sha384; diff --git a/lib/js/src/ReCrypt/ReCrypt_Constants.bs.js b/lib/js/src/ReCrypt/ReCrypt_Constants.bs.js index 606399d..81d9145 100644 --- a/lib/js/src/ReCrypt/ReCrypt_Constants.bs.js +++ b/lib/js/src/ReCrypt/ReCrypt_Constants.bs.js @@ -2,6 +2,20 @@ 'use strict'; +function sha1(t) { + if (t >= 0 && t <= 19) { + return 1518500249; + } else if (t >= 20 && t <= 39) { + return 1859775393; + } else if (t >= 40 && t <= 59) { + return -1894007588; + } else if (t >= 60 && t <= 79) { + return -899497514; + } else { + return 0; + } +} + var sha224 = [ 1116352408, 1899447441, @@ -782,6 +796,7 @@ var sha512 = [ ] ]; +exports.sha1 = sha1; exports.sha224 = sha224; exports.sha256 = sha256; exports.sha384 = sha384; diff --git a/lib/js/src/ReCrypt/ReCrypt_Functions.bs.js b/lib/js/src/ReCrypt/ReCrypt_Functions.bs.js index 468f2f0..057f215 100644 --- a/lib/js/src/ReCrypt/ReCrypt_Functions.bs.js +++ b/lib/js/src/ReCrypt/ReCrypt_Functions.bs.js @@ -5,6 +5,34 @@ var Int32 = require("bs-platform/lib/js/int32.js"); var Int64 = require("bs-platform/lib/js/int64.js"); var Caml_int64 = require("bs-platform/lib/js/caml_int64.js"); +function rotl(x, n) { + return (x << n) | (x >>> (64 - n | 0)) | 0; +} + +function parity(x, y, z) { + return x ^ y ^ z; +} + +function f(t, x, y, z) { + if (t >= 0 && t <= 19) { + var x$1 = x; + var y$1 = y; + var z$1 = z; + return x$1 & y$1 ^ Int32.lognot(x$1) & z$1; + } else if (t >= 20 && t <= 39) { + return parity(x, y, z); + } else if (t >= 40 && t <= 59) { + var x$2 = x; + var y$2 = y; + var z$2 = z; + return x$2 & y$2 ^ x$2 & z$2 ^ y$2 & z$2; + } else if (t >= 60 && t <= 79) { + return parity(x, y, z); + } else { + return 0; + } +} + function rotr(x, n) { return (x >>> n) | 0 | (x << (64 - n | 0)); } @@ -17,10 +45,6 @@ function majority(x, y, z) { return x & y ^ x & z ^ y & z; } -function parity(x, y, z) { - return x ^ y ^ z; -} - function sum0(x) { return rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22); } @@ -37,17 +61,6 @@ function sigma1(x) { return rotr(x, 17) ^ rotr(x, 19) ^ (x >>> 10); } -var Sha224_256 = { - rotr: rotr, - change: change, - majority: majority, - parity: parity, - sum0: sum0, - sum1: sum1, - sigma0: sigma0, - sigma1: sigma1 -}; - function rotr$1(x, n) { return Caml_int64.or_(Caml_int64.lsr_(x, n), Caml_int64.lsl_(x, 64 - n | 0)); } @@ -76,8 +89,21 @@ function sigma1$1(x) { return Caml_int64.xor(Caml_int64.xor(rotr$1(x, 19), rotr$1(x, 61)), Caml_int64.lsr_(x, 6)); } +var Sha1 = { + f: f, + rotl: rotl +}; + +var Sha224_256 = { + change: change, + majority: majority, + sum0: sum0, + sum1: sum1, + sigma0: sigma0, + sigma1: sigma1 +}; + var Sha384_512 = { - rotr: rotr$1, change: change$1, majority: majority$1, sum0: sum0$1, @@ -86,6 +112,7 @@ var Sha384_512 = { sigma1: sigma1$1 }; +exports.Sha1 = Sha1; exports.Sha224_256 = Sha224_256; exports.Sha384_512 = Sha384_512; /* No side effect */ diff --git a/lib/js/src/ReCrypt/ReCrypt_Preprocess.bs.js b/lib/js/src/ReCrypt/ReCrypt_Preprocess.bs.js index 5cdd1f2..6f53f50 100644 --- a/lib/js/src/ReCrypt/ReCrypt_Preprocess.bs.js +++ b/lib/js/src/ReCrypt/ReCrypt_Preprocess.bs.js @@ -24,7 +24,7 @@ function pad(msgBytes) { return ReCrypt_Utils.toBytes((length << 3), bytes); } -var Sha224_256 = { +var Sha1_224_256 = { pad: pad }; @@ -50,6 +50,6 @@ var Sha384_512 = { pad: pad$1 }; -exports.Sha224_256 = Sha224_256; +exports.Sha1_224_256 = Sha1_224_256; exports.Sha384_512 = Sha384_512; /* No side effect */ diff --git a/lib/js/src/ReCrypt/ReCrypt_Sha1.bs.js b/lib/js/src/ReCrypt/ReCrypt_Sha1.bs.js new file mode 100644 index 0000000..acc9110 --- /dev/null +++ b/lib/js/src/ReCrypt/ReCrypt_Sha1.bs.js @@ -0,0 +1,69 @@ +// Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE +'use strict'; + +var Char = require("bs-platform/lib/js/char.js"); +var Bytes = require("bs-platform/lib/js/bytes.js"); +var Int32 = require("bs-platform/lib/js/int32.js"); +var Caml_array = require("bs-platform/lib/js/caml_array.js"); +var ReCrypt_Utils = require("./ReCrypt_Utils.bs.js"); +var ReCrypt_Constants = require("./ReCrypt_Constants.bs.js"); +var ReCrypt_Functions = require("./ReCrypt_Functions.bs.js"); +var ReCrypt_Preprocess = require("./ReCrypt_Preprocess.bs.js"); + +var initialHash = [ + 1732584193, + -271733879, + -1732584194, + 271733878, + -1009589776 +]; + +function make(message) { + var messageBytes = Bytes.of_string(message); + var message$1 = ReCrypt_Preprocess.Sha1_224_256.pad(messageBytes); + var chunks = ((message$1.length + 8 | 0) >> 6); + var a = Caml_array.caml_array_get(initialHash, 0); + var b = Caml_array.caml_array_get(initialHash, 1); + var c = Caml_array.caml_array_get(initialHash, 2); + var d = Caml_array.caml_array_get(initialHash, 3); + var e = Caml_array.caml_array_get(initialHash, 4); + for(var i = 0 ,i_finish = chunks - 1 | 0; i <= i_finish; ++i){ + var w = Caml_array.caml_make_vect(80, Int32.zero); + for(var t = 0; t <= 15; ++t){ + Caml_array.caml_array_set(w, t, (message$1[(i << 6) + (t << 2) | 0] << 24) & -16777216); + Caml_array.caml_array_set(w, t, Caml_array.caml_array_get(w, t) | (message$1[((i << 6) + (t << 2) | 0) + 1 | 0] << 16) & 16711680); + Caml_array.caml_array_set(w, t, Caml_array.caml_array_get(w, t) | (message$1[((i << 6) + (t << 2) | 0) + 2 | 0] << 8) & 65280 | message$1[((i << 6) + (t << 2) | 0) + 3 | 0] & 255); + } + for(var t$1 = 16; t$1 <= 79; ++t$1){ + Caml_array.caml_array_set(w, t$1, ReCrypt_Functions.Sha1.rotl(Caml_array.caml_array_get(w, t$1 - 3 | 0) ^ Caml_array.caml_array_get(w, t$1 - 8 | 0) ^ Caml_array.caml_array_get(w, t$1 - 14 | 0) ^ Caml_array.caml_array_get(w, t$1 - 16 | 0), 1)); + } + var originalA = a; + var originalB = b; + var originalC = c; + var originalD = d; + var originalE = e; + for(var t$2 = 0; t$2 <= 79; ++t$2){ + var temp = (((ReCrypt_Functions.Sha1.rotl(a, 5) + ReCrypt_Functions.Sha1.f(t$2, b, c, d) | 0) + e | 0) + ReCrypt_Constants.sha1(t$2) | 0) + Caml_array.caml_array_get(w, t$2) | 0; + e = d; + d = c; + c = ReCrypt_Functions.Sha1.rotl(b, 30); + b = a; + a = temp; + } + a = originalA + a | 0; + b = originalB + b | 0; + c = originalC + c | 0; + d = originalD + d | 0; + e = originalE + e | 0; + } + var sha1raw = Bytes.make(20, Char.chr(0)); + ReCrypt_Utils.append32(sha1raw, a, 0); + ReCrypt_Utils.append32(sha1raw, b, 4); + ReCrypt_Utils.append32(sha1raw, c, 8); + ReCrypt_Utils.append32(sha1raw, d, 12); + ReCrypt_Utils.append32(sha1raw, e, 16); + return ReCrypt_Utils.stringToHex(Bytes.to_string(sha1raw)); +} + +exports.make = make; +/* No side effect */ diff --git a/lib/js/src/ReCrypt/ReCrypt_Sha224.bs.js b/lib/js/src/ReCrypt/ReCrypt_Sha224.bs.js index 61bc6d4..8f2fb6d 100644 --- a/lib/js/src/ReCrypt/ReCrypt_Sha224.bs.js +++ b/lib/js/src/ReCrypt/ReCrypt_Sha224.bs.js @@ -23,7 +23,7 @@ var initialHash = [ function make(message) { var messageBytes = Bytes.of_string(message); - var message$1 = ReCrypt_Preprocess.Sha224_256.pad(messageBytes); + var message$1 = ReCrypt_Preprocess.Sha1_224_256.pad(messageBytes); var chunks = ((message$1.length + 8 | 0) >> 6); var a = Caml_array.caml_array_get(initialHash, 0); var b = Caml_array.caml_array_get(initialHash, 1); diff --git a/lib/js/src/ReCrypt/ReCrypt_Sha256.bs.js b/lib/js/src/ReCrypt/ReCrypt_Sha256.bs.js index c05bff2..2f2a81d 100644 --- a/lib/js/src/ReCrypt/ReCrypt_Sha256.bs.js +++ b/lib/js/src/ReCrypt/ReCrypt_Sha256.bs.js @@ -23,7 +23,7 @@ var initialHash = [ function make(message) { var messageBytes = Bytes.of_string(message); - var message$1 = ReCrypt_Preprocess.Sha224_256.pad(messageBytes); + var message$1 = ReCrypt_Preprocess.Sha1_224_256.pad(messageBytes); var chunks = ((message$1.length + 8 | 0) >> 6); var a = Caml_array.caml_array_get(initialHash, 0); var b = Caml_array.caml_array_get(initialHash, 1); diff --git a/src/ReCrypt.re b/src/ReCrypt.re index 24de453..74171cf 100644 --- a/src/ReCrypt.re +++ b/src/ReCrypt.re @@ -1,3 +1,7 @@ +module Sha1 = { + include ReCrypt_Sha1; +}; + module Sha224 = { include ReCrypt_Sha224; }; diff --git a/src/ReCrypt.rei b/src/ReCrypt.rei index 21808a4..00dad6c 100644 --- a/src/ReCrypt.rei +++ b/src/ReCrypt.rei @@ -1,5 +1,10 @@ /* BASED ON: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf */ +/** + Module for creating SHA1 hashes + */ +module Sha1: {let make: string => string;}; + /** Module for creating SHA224 hashes */ diff --git a/src/ReCrypt/ReCrypt_Constants.re b/src/ReCrypt/ReCrypt_Constants.re index 99a663a..acd5191 100644 --- a/src/ReCrypt/ReCrypt_Constants.re +++ b/src/ReCrypt/ReCrypt_Constants.re @@ -2,6 +2,16 @@ * Constants [§4.2] */ +let sha1 = t => { + switch (t) { + | t when t >= 0 && t <= 19 => 0x5A827999l + | t when t >= 20 && t <= 39 => 0x6ED9EBA1l + | t when t >= 40 && t <= 59 => 0x8F1BBCDCl + | t when t >= 60 && t <= 79 => 0xCA62C1D6l + | _ => 0x00l + }; +}; + let sha224 = [| 0x428a2f98l, 0x71374491l, diff --git a/src/ReCrypt/ReCrypt_Constants.rei b/src/ReCrypt/ReCrypt_Constants.rei index cf69c56..d82fa14 100644 --- a/src/ReCrypt/ReCrypt_Constants.rei +++ b/src/ReCrypt/ReCrypt_Constants.rei @@ -1,3 +1,4 @@ +let sha1: int => int32; let sha224: array(int32); let sha256: array(int32); let sha384: array(int64); diff --git a/src/ReCrypt/ReCrypt_Functions.re b/src/ReCrypt/ReCrypt_Functions.re index 5c41aa1..d4943b5 100644 --- a/src/ReCrypt/ReCrypt_Functions.re +++ b/src/ReCrypt/ReCrypt_Functions.re @@ -1,11 +1,11 @@ /** * Logical functions [§4.1] */ -module Sha224_256 = { - let rotr = (x, n) => { +module Sha1 = { + let rotl = (x, n) => { Int32.logor( - Int32.shift_right_logical(x, n), - Int32.shift_left(x, 64 - n), + Int32.shift_left(x, n), + Int32.shift_right_logical(x, 64 - n), ); }; @@ -23,6 +23,35 @@ module Sha224_256 = { x->Int32.logxor(y)->Int32.logxor(z); }; + let f = (t, x, y, z) => { + switch (t) { + | t when t >= 0 && t <= 19 => change(x, y, z) + | t when t >= 20 && t <= 39 => parity(x, y, z) + | t when t >= 40 && t <= 59 => majority(x, y, z) + | t when t >= 60 && t <= 79 => parity(x, y, z) + | _ => 0x00l + }; + }; +}; + +module Sha224_256 = { + let rotr = (x, n) => { + Int32.logor( + Int32.shift_right_logical(x, n), + Int32.shift_left(x, 64 - n), + ); + }; + + let change = (x, y, z) => { + Int32.logand(x, y)->Int32.logxor(Int32.logand(Int32.lognot(x), z)); + }; + + let majority = (x, y, z) => { + Int32.logand(x, y) + ->Int32.logxor(Int32.logand(x, z)) + ->Int32.logxor(Int32.logand(y, z)); + }; + let sum0 = x => { rotr(x, 2)->Int32.logxor(rotr(x, 13))->Int32.logxor(rotr(x, 22)); }; diff --git a/src/ReCrypt/ReCrypt_Functions.rei b/src/ReCrypt/ReCrypt_Functions.rei index 919a1f5..00f213a 100644 --- a/src/ReCrypt/ReCrypt_Functions.rei +++ b/src/ReCrypt/ReCrypt_Functions.rei @@ -1,8 +1,11 @@ +module Sha1: { + let f: (int, int32, int32, int32) => int32; + let rotl: (int32, int) => int32; +}; + module Sha224_256: { - let rotr: (int32, int) => int32; let change: (int32, int32, int32) => int32; let majority: (int32, int32, int32) => int32; - let parity: (int32, int32, int32) => int32; let sum0: int32 => int32; let sum1: int32 => int32; let sigma0: int32 => int32; @@ -10,7 +13,6 @@ module Sha224_256: { }; module Sha384_512: { - let rotr: (int64, int) => int64; let change: (int64, int64, int64) => int64; let majority: (int64, int64, int64) => int64; let sum0: int64 => int64; diff --git a/src/ReCrypt/ReCrypt_Preprocess.re b/src/ReCrypt/ReCrypt_Preprocess.re index deb01d2..0cad9ee 100644 --- a/src/ReCrypt/ReCrypt_Preprocess.re +++ b/src/ReCrypt/ReCrypt_Preprocess.re @@ -1,7 +1,7 @@ /** * Preprocessing [§5] */ -module Sha224_256 = { +module Sha1_224_256 = { let pad = (msgBytes: Bytes.t) => { let size = ref(Bytes.length(msgBytes) + 8 + 1); while (size^ mod 64 != 0) { diff --git a/src/ReCrypt/ReCrypt_Preprocess.rei b/src/ReCrypt/ReCrypt_Preprocess.rei index 4430d0d..cba3fa8 100644 --- a/src/ReCrypt/ReCrypt_Preprocess.rei +++ b/src/ReCrypt/ReCrypt_Preprocess.rei @@ -1,2 +1,2 @@ -module Sha224_256: {let pad: Bytes.t => bytes;}; +module Sha1_224_256: {let pad: Bytes.t => bytes;}; module Sha384_512: {let pad: Bytes.t => bytes;}; \ No newline at end of file diff --git a/src/ReCrypt/ReCrypt_Sha1.re b/src/ReCrypt/ReCrypt_Sha1.re new file mode 100644 index 0000000..2c0902c --- /dev/null +++ b/src/ReCrypt/ReCrypt_Sha1.re @@ -0,0 +1,109 @@ +/* constants [§4.2.3] */ +let initialHash = [| + 0x67452301l, + 0xEFCDAB89l, + 0x98BADCFEl, + 0x10325476l, + 0xC3D2E1F0l, +|]; + +let processMessage = (message: Bytes.t) => { + let chunks = (Bytes.length(message) + 8) asr 6; + + let a = ref(initialHash[0]); + let b = ref(initialHash[1]); + let c = ref(initialHash[2]); + let d = ref(initialHash[3]); + let e = ref(initialHash[4]); + + for (i in 0 to chunks - 1) { + let w = Array.make(80, Int32.zero); + + for (t in 0 to 15) { + w[t] = + Bytes.unsafe_get(message, i * 64 + 4 * t) + ->Char.code + ->Int32.of_int + ->Int32.shift_left(24) + ->Int32.logand(-0x1000000l); + + w[t] = + w[t] + ->Int32.logor( + Bytes.unsafe_get(message, i * 64 + 4 * t + 1) + ->Char.code + ->Int32.of_int + ->Int32.shift_left(16) + ->Int32.logand(0x00FF0000l), + ); + w[t] = + w[t] + ->Int32.logor( + Bytes.unsafe_get(message, i * 64 + 4 * t + 2) + ->Char.code + ->Int32.of_int + ->Int32.shift_left(8) + ->Int32.logand(0xFF00l) + ->Int32.logor( + Bytes.unsafe_get(message, i * 64 + 4 * t + 3) + ->Char.code + ->Int32.of_int + ->Int32.logand(0xFFl), + ), + ); + }; + + for (t in 16 to 79) { + w[t] = + ReCrypt_Functions.Sha1.rotl( + w[t - 3] + ->Int32.logxor(w[t - 8]) + ->Int32.logxor(w[t - 14]) + ->Int32.logxor(w[t - 16]), + 1, + ); + }; + + let originalA = a^; + let originalB = b^; + let originalC = c^; + let originalD = d^; + let originalE = e^; + + for (t in 0 to 79) { + let temp = + ReCrypt_Functions.Sha1.rotl(a^, 5) + ->Int32.add(ReCrypt_Functions.Sha1.f(t, b^, c^, d^)) + ->Int32.add(e^) + ->Int32.add(ReCrypt_Constants.sha1(t)) + ->Int32.add(w[t]); + + e := d^; + d := c^; + c := ReCrypt_Functions.Sha1.rotl(b^, 30); + b := a^; + a := temp; + }; + + a := Int32.add(originalA, a^); + b := Int32.add(originalB, b^); + c := Int32.add(originalC, c^); + d := Int32.add(originalD, d^); + e := Int32.add(originalE, e^); + }; + + let sha1raw = Bytes.make(20, Char.chr(0)); + ReCrypt_Utils.append32(sha1raw, a^, 0); + ReCrypt_Utils.append32(sha1raw, b^, 4); + ReCrypt_Utils.append32(sha1raw, c^, 8); + ReCrypt_Utils.append32(sha1raw, d^, 12); + ReCrypt_Utils.append32(sha1raw, e^, 16); + + Bytes.to_string(sha1raw)->ReCrypt_Utils.stringToHex; +}; + +let make = message => { + let messageBytes = Bytes.of_string(message); + let paddedMessage = ReCrypt_Preprocess.Sha1_224_256.pad(messageBytes); + processMessage(paddedMessage); +}; \ No newline at end of file diff --git a/src/ReCrypt/ReCrypt_Sha1.rei b/src/ReCrypt/ReCrypt_Sha1.rei new file mode 100644 index 0000000..546362e --- /dev/null +++ b/src/ReCrypt/ReCrypt_Sha1.rei @@ -0,0 +1,4 @@ +/** + Compute a SHA1 hash based on the given string + */ +let make: string => string; \ No newline at end of file diff --git a/src/ReCrypt/ReCrypt_Sha224.re b/src/ReCrypt/ReCrypt_Sha224.re index 5df0f48..989537d 100644 --- a/src/ReCrypt/ReCrypt_Sha224.re +++ b/src/ReCrypt/ReCrypt_Sha224.re @@ -124,6 +124,6 @@ let processMessage = (message: Bytes.t) => { let make = message => { let messageBytes = Bytes.of_string(message); - let paddedMessage = ReCrypt_Preprocess.Sha224_256.pad(messageBytes); + let paddedMessage = ReCrypt_Preprocess.Sha1_224_256.pad(messageBytes); processMessage(paddedMessage); }; \ No newline at end of file diff --git a/src/ReCrypt/ReCrypt_Sha256.re b/src/ReCrypt/ReCrypt_Sha256.re index 8f50b0f..6bb9161 100644 --- a/src/ReCrypt/ReCrypt_Sha256.re +++ b/src/ReCrypt/ReCrypt_Sha256.re @@ -125,6 +125,6 @@ let processMessage = (message: Bytes.t) => { let make = message => { let messageBytes = Bytes.of_string(message); - let paddedMessage = ReCrypt_Preprocess.Sha224_256.pad(messageBytes); + let paddedMessage = ReCrypt_Preprocess.Sha1_224_256.pad(messageBytes); processMessage(paddedMessage); }; \ No newline at end of file -- 2.51.2