| ... | ... | @@ -0,0 +1,281 @@ |
| 1 | const mem = @import("../mem.zig"); |
| 2 | const math = @import("../math/index.zig"); |
| 3 | const endian = @import("../endian.zig"); |
| 4 | const debug = @import("../debug/index.zig"); |
| 5 | const builtin = @import("builtin"); |
| 6 | const htest = @import("test.zig"); |
| 7 | |
| 8 | pub const Sha3_224 = Keccak(224, 0x06); |
| 9 | pub const Sha3_256 = Keccak(256, 0x06); |
| 10 | pub const Sha3_384 = Keccak(384, 0x06); |
| 11 | pub const Sha3_512 = Keccak(512, 0x06); |
| 12 | |
| 13 | fn Keccak(comptime bits: usize, comptime delim: u8) -> type { return struct { |
| 14 | const Self = this; |
| 15 | const block_size = 200; |
| 16 | const digest_size = bits / 8; |
| 17 | |
| 18 | s: [200]u8, |
| 19 | offset: usize, |
| 20 | rate: usize, |
| 21 | |
| 22 | pub fn init() -> Self { |
| 23 | var d: Self = undefined; |
| 24 | d.reset(); |
| 25 | return d; |
| 26 | } |
| 27 | |
| 28 | pub fn reset(d: &Self) { |
| 29 | mem.set(u8, d.s[0..], 0); |
| 30 | d.offset = 0; |
| 31 | d.rate = 200 - (bits / 4); |
| 32 | } |
| 33 | |
| 34 | pub fn hash(b: []const u8, out: []u8) { |
| 35 | var d = Self.init(); |
| 36 | d.update(b); |
| 37 | d.final(out); |
| 38 | } |
| 39 | |
| 40 | pub fn update(d: &Self, b: []const u8) { |
| 41 | var ip: usize = 0; |
| 42 | var len = b.len; |
| 43 | var rate = d.rate - d.offset; |
| 44 | var offset = d.offset; |
| 45 | |
| 46 | // absorb |
| 47 | while (len >= rate) { |
| 48 | for (d.s[offset .. offset + rate]) |*r, i| |
| 49 | *r ^= b[ip..][i]; |
| 50 | |
| 51 | keccak_f(1600, d.s[0..]); |
| 52 | |
| 53 | ip += rate; |
| 54 | len -= rate; |
| 55 | rate = d.rate; |
| 56 | offset = 0; |
| 57 | } |
| 58 | |
| 59 | for (d.s[offset .. offset + len]) |*r, i| |
| 60 | *r ^= b[ip..][i]; |
| 61 | |
| 62 | d.offset = offset + len; |
| 63 | } |
| 64 | |
| 65 | pub fn final(d: &Self, out: []u8) { |
| 66 | // padding |
| 67 | d.s[d.offset] ^= delim; |
| 68 | d.s[d.rate - 1] ^= 0x80; |
| 69 | |
| 70 | keccak_f(1600, d.s[0..]); |
| 71 | |
| 72 | // squeeze |
| 73 | var op: usize = 0; |
| 74 | var len: usize = bits / 8; |
| 75 | |
| 76 | while (len >= d.rate) { |
| 77 | mem.copy(u8, out[op..], d.s[0..d.rate]); |
| 78 | keccak_f(1600, d.s[0..]); |
| 79 | op += d.rate; |
| 80 | len -= d.rate; |
| 81 | } |
| 82 | |
| 83 | mem.copy(u8, out[op..], d.s[0..len]); |
| 84 | } |
| 85 | };} |
| 86 | |
| 87 | const RC = []const u64 { |
| 88 | 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000, |
| 89 | 0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009, |
| 90 | 0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a, |
| 91 | 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003, |
| 92 | 0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a, |
| 93 | 0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008, |
| 94 | }; |
| 95 | |
| 96 | const ROTC = []const usize { |
| 97 | 1, 3, 6, 10, 15, 21, 28, 36, |
| 98 | 45, 55, 2, 14, 27, 41, 56, 8, |
| 99 | 25, 43, 62, 18, 39, 61, 20, 44 |
| 100 | }; |
| 101 | |
| 102 | const PIL = []const usize { |
| 103 | 10, 7, 11, 17, 18, 3, 5, 16, |
| 104 | 8, 21, 24, 4, 15, 23, 19, 13, |
| 105 | 12, 2, 20, 14, 22, 9, 6, 1 |
| 106 | }; |
| 107 | |
| 108 | const M5 = []const usize { |
| 109 | 0, 1, 2, 3, 4, 0, 1, 2, 3, 4 |
| 110 | }; |
| 111 | |
| 112 | fn keccak_f(comptime F: usize, d: []u8) { |
| 113 | debug.assert(d.len == F / 8); |
| 114 | |
| 115 | const B = F / 25; |
| 116 | const no_rounds = comptime x: { break :x 12 + 2 * math.log2(B); }; |
| 117 | |
| 118 | var s = []const u64 {0} ** 25; |
| 119 | var t = []const u64 {0} ** 1; |
| 120 | var c = []const u64 {0} ** 5; |
| 121 | |
| 122 | for (s) |*r, i| { |
| 123 | *r = mem.readIntLE(u64, d[8*i .. 8*i + 8]); |
| 124 | } |
| 125 | |
| 126 | var x: usize = 0; |
| 127 | var y: usize = 0; |
| 128 | // TODO: Cannot unroll all loops here due to comptime differences. |
| 129 | inline for (RC[0..no_rounds]) |round| { |
| 130 | // theta |
| 131 | x = 0; while (x < 5) : (x += 1) { |
| 132 | c[x] = s[x] ^ s[x+5] ^ s[x+10] ^ s[x+15] ^ s[x+20]; |
| 133 | } |
| 134 | x = 0; while (x < 5) : (x += 1) { |
| 135 | t[0] = c[M5[x+4]] ^ math.rotl(u64, c[M5[x+1]], usize(1)); |
| 136 | y = 0; while (y < 5) : (y += 1) { |
| 137 | s[x + y*5] ^= t[0]; |
| 138 | } |
| 139 | } |
| 140 | |
| 141 | // rho+pi |
| 142 | t[0] = s[1]; |
| 143 | x = 0; while (x < 24) : (x += 1) { |
| 144 | c[0] = s[PIL[x]]; |
| 145 | s[PIL[x]] = math.rotl(u64, t[0], ROTC[x]); |
| 146 | t[0] = c[0]; |
| 147 | } |
| 148 | |
| 149 | // chi |
| 150 | y = 0; while (y < 5) : (y += 1) { |
| 151 | x = 0; while (x < 5) : (x += 1) { |
| 152 | c[x] = s[x + y*5]; |
| 153 | } |
| 154 | x = 0; while (x < 5) : (x += 1) { |
| 155 | s[x + y*5] = c[x] ^ (~c[M5[x+1]] & c[M5[x+2]]); |
| 156 | } |
| 157 | } |
| 158 | |
| 159 | // iota |
| 160 | s[0] ^= round; |
| 161 | } |
| 162 | |
| 163 | for (s) |r, i| { |
| 164 | mem.writeInt(d[8*i .. 8*i + 8], r, builtin.Endian.Little); |
| 165 | } |
| 166 | } |
| 167 | |
| 168 | |
| 169 | test "sha3-224 single" { |
| 170 | htest.assertEqualHash(Sha3_224, "6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7", ""); |
| 171 | htest.assertEqualHash(Sha3_224, "e642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf", "abc"); |
| 172 | htest.assertEqualHash(Sha3_224, "543e6868e1666c1a643630df77367ae5a62a85070a51c14cbf665cbc", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); |
| 173 | } |
| 174 | |
| 175 | test "sha3-224 streaming" { |
| 176 | var h = Sha3_224.init(); |
| 177 | var out: [28]u8 = undefined; |
| 178 | |
| 179 | h.final(out[0..]); |
| 180 | htest.assertEqual("6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7", out[0..]); |
| 181 | |
| 182 | h.reset(); |
| 183 | h.update("abc"); |
| 184 | h.final(out[0..]); |
| 185 | htest.assertEqual("e642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf", out[0..]); |
| 186 | |
| 187 | h.reset(); |
| 188 | h.update("a"); |
| 189 | h.update("b"); |
| 190 | h.update("c"); |
| 191 | h.final(out[0..]); |
| 192 | htest.assertEqual("e642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf", out[0..]); |
| 193 | } |
| 194 | |
| 195 | test "sha3-256 single" { |
| 196 | htest.assertEqualHash(Sha3_256, "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a" , ""); |
| 197 | htest.assertEqualHash(Sha3_256, "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532", "abc"); |
| 198 | htest.assertEqualHash(Sha3_256, "916f6061fe879741ca6469b43971dfdb28b1a32dc36cb3254e812be27aad1d18", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); |
| 199 | } |
| 200 | |
| 201 | test "sha3-256 streaming" { |
| 202 | var h = Sha3_256.init(); |
| 203 | var out: [32]u8 = undefined; |
| 204 | |
| 205 | h.final(out[0..]); |
| 206 | htest.assertEqual("a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a", out[0..]); |
| 207 | |
| 208 | h.reset(); |
| 209 | h.update("abc"); |
| 210 | h.final(out[0..]); |
| 211 | htest.assertEqual("3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532", out[0..]); |
| 212 | |
| 213 | h.reset(); |
| 214 | h.update("a"); |
| 215 | h.update("b"); |
| 216 | h.update("c"); |
| 217 | h.final(out[0..]); |
| 218 | htest.assertEqual("3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532", out[0..]); |
| 219 | } |
| 220 | |
| 221 | test "sha3-384 single" { |
| 222 | const h1 = "0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004"; |
| 223 | htest.assertEqualHash(Sha3_384, h1 , ""); |
| 224 | const h2 = "ec01498288516fc926459f58e2c6ad8df9b473cb0fc08c2596da7cf0e49be4b298d88cea927ac7f539f1edf228376d25"; |
| 225 | htest.assertEqualHash(Sha3_384, h2, "abc"); |
| 226 | const h3 = "79407d3b5916b59c3e30b09822974791c313fb9ecc849e406f23592d04f625dc8c709b98b43b3852b337216179aa7fc7"; |
| 227 | htest.assertEqualHash(Sha3_384, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); |
| 228 | } |
| 229 | |
| 230 | test "sha3-384 streaming" { |
| 231 | var h = Sha3_384.init(); |
| 232 | var out: [48]u8 = undefined; |
| 233 | |
| 234 | const h1 = "0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004"; |
| 235 | h.final(out[0..]); |
| 236 | htest.assertEqual(h1, out[0..]); |
| 237 | |
| 238 | const h2 = "ec01498288516fc926459f58e2c6ad8df9b473cb0fc08c2596da7cf0e49be4b298d88cea927ac7f539f1edf228376d25"; |
| 239 | h.reset(); |
| 240 | h.update("abc"); |
| 241 | h.final(out[0..]); |
| 242 | htest.assertEqual(h2, out[0..]); |
| 243 | |
| 244 | h.reset(); |
| 245 | h.update("a"); |
| 246 | h.update("b"); |
| 247 | h.update("c"); |
| 248 | h.final(out[0..]); |
| 249 | htest.assertEqual(h2, out[0..]); |
| 250 | } |
| 251 | |
| 252 | test "sha3-512 single" { |
| 253 | const h1 = "a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26"; |
| 254 | htest.assertEqualHash(Sha3_512, h1 , ""); |
| 255 | const h2 = "b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0"; |
| 256 | htest.assertEqualHash(Sha3_512, h2, "abc"); |
| 257 | const h3 = "afebb2ef542e6579c50cad06d2e578f9f8dd6881d7dc824d26360feebf18a4fa73e3261122948efcfd492e74e82e2189ed0fb440d187f382270cb455f21dd185"; |
| 258 | htest.assertEqualHash(Sha3_512, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); |
| 259 | } |
| 260 | |
| 261 | test "sha3-512 streaming" { |
| 262 | var h = Sha3_512.init(); |
| 263 | var out: [64]u8 = undefined; |
| 264 | |
| 265 | const h1 = "a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26"; |
| 266 | h.final(out[0..]); |
| 267 | htest.assertEqual(h1, out[0..]); |
| 268 | |
| 269 | const h2 = "b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0"; |
| 270 | h.reset(); |
| 271 | h.update("abc"); |
| 272 | h.final(out[0..]); |
| 273 | htest.assertEqual(h2, out[0..]); |
| 274 | |
| 275 | h.reset(); |
| 276 | h.update("a"); |
| 277 | h.update("b"); |
| 278 | h.update("c"); |
| 279 | h.final(out[0..]); |
| 280 | htest.assertEqual(h2, out[0..]); |
| 281 | } |