authorgravatar for marc@tiehu.isMarc Tiehuis <marc@tiehu.is> 2018-01-17 19:40:50+13:00
committergravatar for marc@tiehu.isMarc Tiehuis <marc@tiehu.is> 2018-01-17 21:19:45+13:00
log7a3fd89d253993cbb73c6890974aa6d3f1f01f22
treeb2db42c5be0eab3ea43b4d503b10900ddc214ade
parentdfd53634941ef560971d80dee4b3028e41c9174e

Add Sha3 hashing functions

These are on the slower side and could be improved. No performance optimizations yet have been done. ``` Cpu: Intel(R) Core(TM) i5-6500 CPU @ 3.20GHz ``` -- Sha3-256 ``` Zig --release-fast 93 Mb/s Zig --release-safe 99 Mb/s Zig 4 Mb/s ``` -- Sha3-512 ``` Zig --release-fast 49 Mb/s Zig --release-safe 54 Mb/s Zig 2 Mb/s ``` Interestingly, release-safe is producing slightly better code than release-fast.

3 files changed, 289 insertions(+), 0 deletions(-)

CMakeLists.txt+1
......@@ -368,6 +368,7 @@ set(ZIG_STD_FILES
368368 "crypto/md5.zig"
369369 "crypto/sha1.zig"
370370 "crypto/sha2.zig"
371 "crypto/sha3.zig"
371372 "crypto/blake2.zig"
372373 "cstr.zig"
373374 "debug/failing_allocator.zig"
std/crypto/index.zig+7
......@@ -7,6 +7,12 @@ pub const Sha256 = sha2.Sha256;
77pub const Sha384 = sha2.Sha384;
88pub const Sha512 = sha2.Sha512;
99
10const sha3 = @import("sha3.zig");
11pub const Sha3_224 = sha3.Sha3_224;
12pub const Sha3_256 = sha3.Sha3_256;
13pub const Sha3_384 = sha3.Sha3_384;
14pub const Sha3_512 = sha3.Sha3_512;
15
1016const blake2 = @import("blake2.zig");
1117pub const Blake2s224 = blake2.Blake2s224;
1218pub const Blake2s256 = blake2.Blake2s256;
......@@ -17,5 +23,6 @@ test "crypto" {
1723 _ = @import("md5.zig");
1824 _ = @import("sha1.zig");
1925 _ = @import("sha2.zig");
26 _ = @import("sha3.zig");
2027 _ = @import("blake2.zig");
2128}
std/crypto/sha3.zig created+281
......@@ -0,0 +1,281 @@
1const mem = @import("../mem.zig");
2const math = @import("../math/index.zig");
3const endian = @import("../endian.zig");
4const debug = @import("../debug/index.zig");
5const builtin = @import("builtin");
6const htest = @import("test.zig");
7
8pub const Sha3_224 = Keccak(224, 0x06);
9pub const Sha3_256 = Keccak(256, 0x06);
10pub const Sha3_384 = Keccak(384, 0x06);
11pub const Sha3_512 = Keccak(512, 0x06);
12
13fn 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
87const 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
96const 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
102const 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
108const M5 = []const usize {
109 0, 1, 2, 3, 4, 0, 1, 2, 3, 4
110};
111
112fn 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
169test "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
175test "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
195test "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
201test "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
221test "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
230test "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
252test "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
261test "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}