| author | |
| committer | |
| log | 51fdbf7f8c282c7b91a688c9069b363e90bedf6e |
| tree | d509b0494776da54c78aa299d4679cf4f17180e0 |
| parent | 304f6f1d0165f9bdacb7d80479298ca0acff1c27 |
Some performance comparisons to C.
We take the fastest time measurement taken across multiple runs.
The block hashing functions use the same md5/sha1 methods.
```
Cpu: Intel(R) Core(TM) i5-6500 CPU @ 3.20GHz
Gcc: 7.2.1 20171224
Clang: 5.0.1
Zig: 0.1.1.304f6f1d
```
See https://www.nayuki.io/page/fast-md5-hash-implementation-in-x86-assembly:
```
gcc -O2
661 Mb/s
clang -O2
490 Mb/s
zig --release-fast and zig --release-safe
570 Mb/s
zig
50 Mb/s
```
See https://www.nayuki.io/page/fast-sha1-hash-implementation-in-x86-assembly:
```
gcc -O2
588 Mb/s
clang -O2
563 Mb/s
zig --release-fast and zig --release-safe
610 Mb/s
zig
21 Mb/s
```
In short, zig provides pretty useful tools for writing this sort of
code. We are in the lead against clang (which uses the same LLVM
backend) with us being slower only against md5 with GCC.5 files changed, 544 insertions(+), 0 deletions(-)
CMakeLists.txt+3| ... | ... | @@ -364,6 +364,9 @@ set(ZIG_STD_FILES |
| 364 | 364 | "c/index.zig" |
| 365 | 365 | "c/linux.zig" |
| 366 | 366 | "c/windows.zig" |
| 367 | "crypto/index.zig" | |
| 368 | "crypto/md5.zig" | |
| 369 | "crypto/sha1.zig" | |
| 367 | 370 | "cstr.zig" |
| 368 | 371 | "debug/failing_allocator.zig" |
| 369 | 372 | "debug/index.zig" |
std/crypto/index.zig created+7| ... | ... | @@ -0,0 +1,7 @@ |
| 1 | pub const Sha1 = @import("md5.zig").Sha1; | |
| 2 | pub const Md5 = @import("sha1.zig").Md5; | |
| 3 | ||
| 4 | test "crypto" { | |
| 5 | _ = @import("md5.zig"); | |
| 6 | _ = @import("sha1.zig"); | |
| 7 | } |
std/crypto/md5.zig created+252| ... | ... | @@ -0,0 +1,252 @@ |
| 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 | ||
| 6 | const RoundParam = struct { | |
| 7 | a: u32, b: u32, c: u32, d: u32, | |
| 8 | k: u32, s: u32, t: u32 | |
| 9 | }; | |
| 10 | ||
| 11 | fn Rp(a: u32, b: u32, c: u32, d: u32, k: u32, s: u5, t: u32) -> RoundParam { | |
| 12 | return RoundParam { .a = a, .b = b, .c = c, .d = d, .k = k, .s = s, .t = t }; | |
| 13 | } | |
| 14 | ||
| 15 | /// const hash1 = Md5.hash("my input"); | |
| 16 | /// | |
| 17 | /// const hasher = Md5.init(); | |
| 18 | /// hasher.update("my "); | |
| 19 | /// hasher.update("input"); | |
| 20 | /// const hash2 = hasher.final(); | |
| 21 | pub const Md5 = struct { | |
| 22 | const Self = this; | |
| 23 | ||
| 24 | s: [4]u32, | |
| 25 | // Streaming Cache | |
| 26 | buf: [64]u8, | |
| 27 | buf_len: u8, | |
| 28 | total_len: u64, | |
| 29 | ||
| 30 | pub fn init() -> Self { | |
| 31 | var d: Self = undefined; | |
| 32 | d.reset(); | |
| 33 | return d; | |
| 34 | } | |
| 35 | ||
| 36 | pub fn reset(d: &Self) { | |
| 37 | d.s[0] = 0x67452301; | |
| 38 | d.s[1] = 0xEFCDAB89; | |
| 39 | d.s[2] = 0x98BADCFE; | |
| 40 | d.s[3] = 0x10325476; | |
| 41 | d.buf_len = 0; | |
| 42 | d.total_len = 0; | |
| 43 | } | |
| 44 | ||
| 45 | pub fn hash(b: []const u8) -> u128 { | |
| 46 | var d = Md5.init(); | |
| 47 | d.update(b); | |
| 48 | return d.final(); | |
| 49 | } | |
| 50 | ||
| 51 | pub fn update(d: &Self, b: []const u8) { | |
| 52 | var off: usize = 0; | |
| 53 | ||
| 54 | // Partial buffer exists from previous update. Copy into buffer then hash. | |
| 55 | if (d.buf_len != 0 and d.buf_len + b.len > 64) { | |
| 56 | off += 64 - d.buf_len; | |
| 57 | mem.copy(u8, d.buf[d.buf_len..], b[0..off]); | |
| 58 | ||
| 59 | d.round(d.buf[0..]); | |
| 60 | d.buf_len = 0; | |
| 61 | } | |
| 62 | ||
| 63 | // Full middle blocks. | |
| 64 | while (off + 64 < b.len) : (off += 64) { | |
| 65 | d.round(b[off..off + 64]); | |
| 66 | } | |
| 67 | ||
| 68 | // Copy any remainder for next pass. | |
| 69 | mem.copy(u8, d.buf[d.buf_len..], b[off..]); | |
| 70 | d.buf_len += u8(b[off..].len); | |
| 71 | ||
| 72 | d.total_len += b.len; | |
| 73 | } | |
| 74 | ||
| 75 | pub fn final(d: &Self) -> u128 { | |
| 76 | // The buffer here will never be completely full. | |
| 77 | mem.set(u8, d.buf[d.buf_len..], 0); | |
| 78 | ||
| 79 | // Append padding bits. | |
| 80 | d.buf[d.buf_len] = 0x80; | |
| 81 | d.buf_len += 1; | |
| 82 | ||
| 83 | // > 448 mod 512 so need to add an extra round to wrap around. | |
| 84 | if (64 - d.buf_len < 8) { | |
| 85 | d.round(d.buf[0..]); | |
| 86 | mem.set(u8, d.buf[0..], 0); | |
| 87 | } | |
| 88 | ||
| 89 | // Append message length. | |
| 90 | var i: usize = 1; | |
| 91 | var len = d.total_len >> 5; | |
| 92 | d.buf[56] = u8(d.total_len & 0x1f) << 3; | |
| 93 | while (i < 8) : (i += 1) { | |
| 94 | d.buf[56 + i] = u8(len & 0xff); | |
| 95 | len >>= 8; | |
| 96 | } | |
| 97 | ||
| 98 | d.round(d.buf[0..]); | |
| 99 | ||
| 100 | const r = | |
| 101 | (u128(d.s[3]) << 96) | | |
| 102 | (u128(d.s[2]) << 64) | | |
| 103 | (u128(d.s[1]) << 32) | | |
| 104 | (u128(d.s[0]) << 0); | |
| 105 | ||
| 106 | return endian.swapIfLe(u128, r); | |
| 107 | } | |
| 108 | ||
| 109 | fn round(d: &Self, b: []const u8) { | |
| 110 | debug.assert(b.len == 64); | |
| 111 | ||
| 112 | var s: [16]u32 = undefined; | |
| 113 | ||
| 114 | // ERROR: cannot unroll this at comptime | |
| 115 | var i: usize = 0; | |
| 116 | while (i < 16) : (i += 1) { | |
| 117 | // NOTE: Performing or's separately improves perf by ~10% | |
| 118 | s[i] = 0; | |
| 119 | s[i] |= u32(b[i*4+0]); | |
| 120 | s[i] |= u32(b[i*4+1]) << 8; | |
| 121 | s[i] |= u32(b[i*4+2]) << 16; | |
| 122 | s[i] |= u32(b[i*4+3]) << 24; | |
| 123 | } | |
| 124 | ||
| 125 | var v: [4]u32 = []u32 { | |
| 126 | d.s[0], d.s[1], d.s[2], d.s[3], | |
| 127 | }; | |
| 128 | ||
| 129 | const round0 = comptime []RoundParam { | |
| 130 | Rp(0, 1, 2, 3, 0, 7, 0xD76AA478), | |
| 131 | Rp(3, 0, 1, 2, 1, 12, 0xE8C7B756), | |
| 132 | Rp(2, 3, 0, 1, 2, 17, 0x242070DB), | |
| 133 | Rp(1, 2, 3, 0, 3, 22, 0xC1BDCEEE), | |
| 134 | Rp(0, 1, 2, 3, 4, 7, 0xF57C0FAF), | |
| 135 | Rp(3, 0, 1, 2, 5, 12, 0x4787C62A), | |
| 136 | Rp(2, 3, 0, 1, 6, 17, 0xA8304613), | |
| 137 | Rp(1, 2, 3, 0, 7, 22, 0xFD469501), | |
| 138 | Rp(0, 1, 2, 3, 8, 7, 0x698098D8), | |
| 139 | Rp(3, 0, 1, 2, 9, 12, 0x8B44F7AF), | |
| 140 | Rp(2, 3, 0, 1, 10, 17, 0xFFFF5BB1), | |
| 141 | Rp(1, 2, 3, 0, 11, 22, 0x895CD7BE), | |
| 142 | Rp(0, 1, 2, 3, 12, 7, 0x6B901122), | |
| 143 | Rp(3, 0, 1, 2, 13, 12, 0xFD987193), | |
| 144 | Rp(2, 3, 0, 1, 14, 17, 0xA679438E), | |
| 145 | Rp(1, 2, 3, 0, 15, 22, 0x49B40821), | |
| 146 | }; | |
| 147 | inline for (round0) |r| { | |
| 148 | v[r.a] = v[r.a] +% (v[r.d] ^ (v[r.b] & (v[r.c] ^ v[r.d]))) +% r.t +% s[r.k]; | |
| 149 | v[r.a] = v[r.b] +% math.rotl(u32, v[r.a], r.s); | |
| 150 | } | |
| 151 | ||
| 152 | const round1 = comptime []RoundParam { | |
| 153 | Rp(0, 1, 2, 3, 1, 5, 0xF61E2562), | |
| 154 | Rp(3, 0, 1, 2, 6, 9, 0xC040B340), | |
| 155 | Rp(2, 3, 0, 1, 11, 14, 0x265E5A51), | |
| 156 | Rp(1, 2, 3, 0, 0, 20, 0xE9B6C7AA), | |
| 157 | Rp(0, 1, 2, 3, 5, 5, 0xD62F105D), | |
| 158 | Rp(3, 0, 1, 2, 10, 9, 0x02441453), | |
| 159 | Rp(2, 3, 0, 1, 15, 14, 0xD8A1E681), | |
| 160 | Rp(1, 2, 3, 0, 4, 20, 0xE7D3FBC8), | |
| 161 | Rp(0, 1, 2, 3, 9, 5, 0x21E1CDE6), | |
| 162 | Rp(3, 0, 1, 2, 14, 9, 0xC33707D6), | |
| 163 | Rp(2, 3, 0, 1, 3, 14, 0xF4D50D87), | |
| 164 | Rp(1, 2, 3, 0, 8, 20, 0x455A14ED), | |
| 165 | Rp(0, 1, 2, 3, 13, 5, 0xA9E3E905), | |
| 166 | Rp(3, 0, 1, 2, 2, 9, 0xFCEFA3F8), | |
| 167 | Rp(2, 3, 0, 1, 7, 14, 0x676F02D9), | |
| 168 | Rp(1, 2, 3, 0, 12, 20, 0x8D2A4C8A), | |
| 169 | }; | |
| 170 | inline for (round1) |r| { | |
| 171 | v[r.a] = v[r.a] +% (v[r.c] ^ (v[r.d] & (v[r.b] ^ v[r.c]))) +% r.t +% s[r.k]; | |
| 172 | v[r.a] = v[r.b] +% math.rotl(u32, v[r.a], r.s); | |
| 173 | } | |
| 174 | ||
| 175 | const round2 = comptime []RoundParam { | |
| 176 | Rp(0, 1, 2, 3, 5, 4, 0xFFFA3942), | |
| 177 | Rp(3, 0, 1, 2, 8, 11, 0x8771F681), | |
| 178 | Rp(2, 3, 0, 1, 11, 16, 0x6D9D6122), | |
| 179 | Rp(1, 2, 3, 0, 14, 23, 0xFDE5380C), | |
| 180 | Rp(0, 1, 2, 3, 1, 4, 0xA4BEEA44), | |
| 181 | Rp(3, 0, 1, 2, 4, 11, 0x4BDECFA9), | |
| 182 | Rp(2, 3, 0, 1, 7, 16, 0xF6BB4B60), | |
| 183 | Rp(1, 2, 3, 0, 10, 23, 0xBEBFBC70), | |
| 184 | Rp(0, 1, 2, 3, 13, 4, 0x289B7EC6), | |
| 185 | Rp(3, 0, 1, 2, 0, 11, 0xEAA127FA), | |
| 186 | Rp(2, 3, 0, 1, 3, 16, 0xD4EF3085), | |
| 187 | Rp(1, 2, 3, 0, 6, 23, 0x04881D05), | |
| 188 | Rp(0, 1, 2, 3, 9, 4, 0xD9D4D039), | |
| 189 | Rp(3, 0, 1, 2, 12, 11, 0xE6DB99E5), | |
| 190 | Rp(2, 3, 0, 1, 15, 16, 0x1FA27CF8), | |
| 191 | Rp(1, 2, 3, 0, 2, 23, 0xC4AC5665), | |
| 192 | }; | |
| 193 | inline for (round2) |r| { | |
| 194 | v[r.a] = v[r.a] +% (v[r.b] ^ v[r.c] ^ v[r.d]) +% r.t +% s[r.k]; | |
| 195 | v[r.a] = v[r.b] +% math.rotl(u32, v[r.a], r.s); | |
| 196 | } | |
| 197 | ||
| 198 | const round3 = comptime []RoundParam { | |
| 199 | Rp(0, 1, 2, 3, 0, 6, 0xF4292244), | |
| 200 | Rp(3, 0, 1, 2, 7, 10, 0x432AFF97), | |
| 201 | Rp(2, 3, 0, 1, 14, 15, 0xAB9423A7), | |
| 202 | Rp(1, 2, 3, 0, 5, 21, 0xFC93A039), | |
| 203 | Rp(0, 1, 2, 3, 12, 6, 0x655B59C3), | |
| 204 | Rp(3, 0, 1, 2, 3, 10, 0x8F0CCC92), | |
| 205 | Rp(2, 3, 0, 1, 10, 15, 0xFFEFF47D), | |
| 206 | Rp(1, 2, 3, 0, 1, 21, 0x85845DD1), | |
| 207 | Rp(0, 1, 2, 3, 8, 6, 0x6FA87E4F), | |
| 208 | Rp(3, 0, 1, 2, 15, 10, 0xFE2CE6E0), | |
| 209 | Rp(2, 3, 0, 1, 6, 15, 0xA3014314), | |
| 210 | Rp(1, 2, 3, 0, 13, 21, 0x4E0811A1), | |
| 211 | Rp(0, 1, 2, 3, 4, 6, 0xF7537E82), | |
| 212 | Rp(3, 0, 1, 2, 11, 10, 0xBD3AF235), | |
| 213 | Rp(2, 3, 0, 1, 2, 15, 0x2AD7D2BB), | |
| 214 | Rp(1, 2, 3, 0, 9, 21, 0xEB86D391), | |
| 215 | }; | |
| 216 | inline for (round3) |r| { | |
| 217 | v[r.a] = v[r.a] +% (v[r.c] ^ (v[r.b] | ~v[r.d])) +% r.t +% s[r.k]; | |
| 218 | v[r.a] = v[r.b] +% math.rotl(u32, v[r.a], r.s); | |
| 219 | } | |
| 220 | ||
| 221 | d.s[0] +%= v[0]; | |
| 222 | d.s[1] +%= v[1]; | |
| 223 | d.s[2] +%= v[2]; | |
| 224 | d.s[3] +%= v[3]; | |
| 225 | } | |
| 226 | }; | |
| 227 | ||
| 228 | test "md5 single" { | |
| 229 | debug.assert(0xd41d8cd98f00b204e9800998ecf8427e == Md5.hash("")); | |
| 230 | debug.assert(0x0cc175b9c0f1b6a831c399e269772661 == Md5.hash("a")); | |
| 231 | debug.assert(0x900150983cd24fb0d6963f7d28e17f72 == Md5.hash("abc")); | |
| 232 | debug.assert(0xf96b697d7cb7938d525a2f31aaf161d0 == Md5.hash("message digest")); | |
| 233 | debug.assert(0xc3fcd3d76192e4007dfb496cca67e13b == Md5.hash("abcdefghijklmnopqrstuvwxyz")); | |
| 234 | debug.assert(0xd174ab98d277d9f5a5611c2c9f419d9f == Md5.hash("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")); | |
| 235 | debug.assert(0x57edf4a22be3c955ac49da2e2107b67a == Md5.hash("12345678901234567890123456789012345678901234567890123456789012345678901234567890")); | |
| 236 | } | |
| 237 | ||
| 238 | test "md5 streaming" { | |
| 239 | var h = Md5.init(); | |
| 240 | ||
| 241 | debug.assert(0xd41d8cd98f00b204e9800998ecf8427e == h.final()); | |
| 242 | ||
| 243 | h.reset(); | |
| 244 | h.update("abc"); | |
| 245 | debug.assert(0x900150983cd24fb0d6963f7d28e17f72 == h.final()); | |
| 246 | ||
| 247 | h.reset(); | |
| 248 | h.update("a"); | |
| 249 | h.update("b"); | |
| 250 | h.update("c"); | |
| 251 | debug.assert(0x900150983cd24fb0d6963f7d28e17f72 == h.final()); | |
| 252 | } |
std/crypto/sha1.zig created+280| ... | ... | @@ -0,0 +1,280 @@ |
| 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 | ||
| 6 | pub const u160 = @IntType(false, 160); | |
| 7 | ||
| 8 | const RoundParam = struct { | |
| 9 | a: u32, b: u32, c: u32, d: u32, e: u32, i: u32, | |
| 10 | }; | |
| 11 | ||
| 12 | fn Rp(a: u32, b: u32, c: u32, d: u32, e: u32, i: u32) -> RoundParam { | |
| 13 | return RoundParam { .a = a, .b = b, .c = c, .d = d, .e = e, .i = i }; | |
| 14 | } | |
| 15 | ||
| 16 | pub const Sha1 = struct { | |
| 17 | const Self = this; | |
| 18 | ||
| 19 | s: [5]u32, | |
| 20 | // Streaming Cache | |
| 21 | buf: [64]u8, | |
| 22 | buf_len: u8, | |
| 23 | total_len: u64, | |
| 24 | ||
| 25 | pub fn init() -> Self { | |
| 26 | var d: Self = undefined; | |
| 27 | d.reset(); | |
| 28 | return d; | |
| 29 | } | |
| 30 | ||
| 31 | pub fn reset(d: &Self) { | |
| 32 | d.s[0] = 0x67452301; | |
| 33 | d.s[1] = 0xEFCDAB89; | |
| 34 | d.s[2] = 0x98BADCFE; | |
| 35 | d.s[3] = 0x10325476; | |
| 36 | d.s[4] = 0xC3D2E1F0; | |
| 37 | d.buf_len = 0; | |
| 38 | d.total_len = 0; | |
| 39 | } | |
| 40 | ||
| 41 | pub fn hash(b: []const u8) -> u160 { | |
| 42 | var d = Sha1.init(); | |
| 43 | d.update(b); | |
| 44 | return d.final(); | |
| 45 | } | |
| 46 | ||
| 47 | pub fn update(d: &Self, b: []const u8) { | |
| 48 | var off: usize = 0; | |
| 49 | ||
| 50 | // Partial buffer exists from previous update. Copy into buffer then hash. | |
| 51 | if (d.buf_len != 0 and d.buf_len + b.len > 64) { | |
| 52 | off += 64 - d.buf_len; | |
| 53 | mem.copy(u8, d.buf[d.buf_len..], b[0..off]); | |
| 54 | ||
| 55 | d.round(d.buf[0..]); | |
| 56 | d.buf_len = 0; | |
| 57 | } | |
| 58 | ||
| 59 | // Full middle blocks. | |
| 60 | while (off + 64 < b.len) : (off += 64) { | |
| 61 | d.round(b[off..off + 64]); | |
| 62 | } | |
| 63 | ||
| 64 | // Copy any remainder for next pass. | |
| 65 | mem.copy(u8, d.buf[d.buf_len..], b[off..]); | |
| 66 | d.buf_len += u8(b[off..].len); | |
| 67 | ||
| 68 | d.total_len += b.len; | |
| 69 | } | |
| 70 | ||
| 71 | pub fn final(d: &Self) -> u160 { | |
| 72 | // The buffer here will never be completely full. | |
| 73 | mem.set(u8, d.buf[d.buf_len..], 0); | |
| 74 | ||
| 75 | // Append padding bits. | |
| 76 | d.buf[d.buf_len] = 0x80; | |
| 77 | d.buf_len += 1; | |
| 78 | ||
| 79 | // > 448 mod 512 so need to add an extra round to wrap around. | |
| 80 | if (64 - d.buf_len < 8) { | |
| 81 | d.round(d.buf[0..]); | |
| 82 | mem.set(u8, d.buf[0..], 0); | |
| 83 | } | |
| 84 | ||
| 85 | // Append message length. | |
| 86 | var i: usize = 1; | |
| 87 | var len = d.total_len >> 5; | |
| 88 | d.buf[63] = u8(d.total_len & 0x1f) << 3; | |
| 89 | while (i < 8) : (i += 1) { | |
| 90 | d.buf[63 - i] = u8(len & 0xff); | |
| 91 | len >>= 8; | |
| 92 | } | |
| 93 | ||
| 94 | d.round(d.buf[0..]); | |
| 95 | ||
| 96 | const r = | |
| 97 | (u160(d.s[0]) << 128) | | |
| 98 | (u160(d.s[1]) << 96) | | |
| 99 | (u160(d.s[2]) << 64) | | |
| 100 | (u160(d.s[3]) << 32) | | |
| 101 | (u160(d.s[4]) << 0); | |
| 102 | ||
| 103 | return endian.swapIfBe(u160, r); | |
| 104 | } | |
| 105 | ||
| 106 | fn round(d: &Self, b: []const u8) { | |
| 107 | debug.assert(b.len == 64); | |
| 108 | ||
| 109 | var s: [16]u32 = undefined; | |
| 110 | ||
| 111 | var v: [5]u32 = []u32 { | |
| 112 | d.s[0], d.s[1], d.s[2], d.s[3], d.s[4], | |
| 113 | }; | |
| 114 | ||
| 115 | const round0a = comptime []RoundParam { | |
| 116 | Rp(0, 1, 2, 3, 4, 0), | |
| 117 | Rp(4, 0, 1, 2, 3, 1), | |
| 118 | Rp(3, 4, 0, 1, 2, 2), | |
| 119 | Rp(2, 3, 4, 0, 1, 3), | |
| 120 | Rp(1, 2, 3, 4, 0, 4), | |
| 121 | Rp(0, 1, 2, 3, 4, 5), | |
| 122 | Rp(4, 0, 1, 2, 3, 6), | |
| 123 | Rp(3, 4, 0, 1, 2, 7), | |
| 124 | Rp(2, 3, 4, 0, 1, 8), | |
| 125 | Rp(1, 2, 3, 4, 0, 9), | |
| 126 | Rp(0, 1, 2, 3, 4, 10), | |
| 127 | Rp(4, 0, 1, 2, 3, 11), | |
| 128 | Rp(3, 4, 0, 1, 2, 12), | |
| 129 | Rp(2, 3, 4, 0, 1, 13), | |
| 130 | Rp(1, 2, 3, 4, 0, 14), | |
| 131 | Rp(0, 1, 2, 3, 4, 15), | |
| 132 | }; | |
| 133 | inline for (round0a) |r| { | |
| 134 | s[r.i] = (u32(b[r.i * 4 + 0]) << 24) | | |
| 135 | (u32(b[r.i * 4 + 1]) << 16) | | |
| 136 | (u32(b[r.i * 4 + 2]) << 8) | | |
| 137 | (u32(b[r.i * 4 + 3]) << 0); | |
| 138 | ||
| 139 | v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0x5A827999 +% s[r.i & 0xf] | |
| 140 | +% ((v[r.b] & v[r.c]) | (~v[r.b] & v[r.d])); | |
| 141 | v[r.b] = math.rotl(u32, v[r.b], u32(30)); | |
| 142 | } | |
| 143 | ||
| 144 | const round0b = comptime []RoundParam { | |
| 145 | Rp(4, 0, 1, 2, 3, 16), | |
| 146 | Rp(3, 4, 0, 1, 2, 17), | |
| 147 | Rp(2, 3, 4, 0, 1, 18), | |
| 148 | Rp(1, 2, 3, 4, 0, 19), | |
| 149 | }; | |
| 150 | inline for (round0b) |r| { | |
| 151 | const t = s[(r.i-3) & 0xf] ^ s[(r.i-8) & 0xf] ^ s[(r.i-14) & 0xf] ^ s[(r.i-16) & 0xf]; | |
| 152 | s[r.i & 0xf] = math.rotl(u32, t, u32(1)); | |
| 153 | ||
| 154 | v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0x5A827999 +% s[r.i & 0xf] | |
| 155 | +% ((v[r.b] & v[r.c]) | (~v[r.b] & v[r.d])); | |
| 156 | v[r.b] = math.rotl(u32, v[r.b], u32(30)); | |
| 157 | } | |
| 158 | ||
| 159 | const round1 = comptime []RoundParam { | |
| 160 | Rp(0, 1, 2, 3, 4, 20), | |
| 161 | Rp(4, 0, 1, 2, 3, 21), | |
| 162 | Rp(3, 4, 0, 1, 2, 22), | |
| 163 | Rp(2, 3, 4, 0, 1, 23), | |
| 164 | Rp(1, 2, 3, 4, 0, 24), | |
| 165 | Rp(0, 1, 2, 3, 4, 25), | |
| 166 | Rp(4, 0, 1, 2, 3, 26), | |
| 167 | Rp(3, 4, 0, 1, 2, 27), | |
| 168 | Rp(2, 3, 4, 0, 1, 28), | |
| 169 | Rp(1, 2, 3, 4, 0, 29), | |
| 170 | Rp(0, 1, 2, 3, 4, 30), | |
| 171 | Rp(4, 0, 1, 2, 3, 31), | |
| 172 | Rp(3, 4, 0, 1, 2, 32), | |
| 173 | Rp(2, 3, 4, 0, 1, 33), | |
| 174 | Rp(1, 2, 3, 4, 0, 34), | |
| 175 | Rp(0, 1, 2, 3, 4, 35), | |
| 176 | Rp(4, 0, 1, 2, 3, 36), | |
| 177 | Rp(3, 4, 0, 1, 2, 37), | |
| 178 | Rp(2, 3, 4, 0, 1, 38), | |
| 179 | Rp(1, 2, 3, 4, 0, 39), | |
| 180 | }; | |
| 181 | inline for (round1) |r| { | |
| 182 | const t = s[(r.i-3) & 0xf] ^ s[(r.i-8) & 0xf] ^ s[(r.i-14) & 0xf] ^ s[(r.i-16) & 0xf]; | |
| 183 | s[r.i & 0xf] = math.rotl(u32, t, u32(1)); | |
| 184 | ||
| 185 | v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0x6ED9EBA1 +% s[r.i & 0xf] | |
| 186 | +% (v[r.b] ^ v[r.c] ^ v[r.d]); | |
| 187 | v[r.b] = math.rotl(u32, v[r.b], u32(30)); | |
| 188 | } | |
| 189 | ||
| 190 | const round2 = comptime []RoundParam { | |
| 191 | Rp(0, 1, 2, 3, 4, 40), | |
| 192 | Rp(4, 0, 1, 2, 3, 41), | |
| 193 | Rp(3, 4, 0, 1, 2, 42), | |
| 194 | Rp(2, 3, 4, 0, 1, 43), | |
| 195 | Rp(1, 2, 3, 4, 0, 44), | |
| 196 | Rp(0, 1, 2, 3, 4, 45), | |
| 197 | Rp(4, 0, 1, 2, 3, 46), | |
| 198 | Rp(3, 4, 0, 1, 2, 47), | |
| 199 | Rp(2, 3, 4, 0, 1, 48), | |
| 200 | Rp(1, 2, 3, 4, 0, 49), | |
| 201 | Rp(0, 1, 2, 3, 4, 50), | |
| 202 | Rp(4, 0, 1, 2, 3, 51), | |
| 203 | Rp(3, 4, 0, 1, 2, 52), | |
| 204 | Rp(2, 3, 4, 0, 1, 53), | |
| 205 | Rp(1, 2, 3, 4, 0, 54), | |
| 206 | Rp(0, 1, 2, 3, 4, 55), | |
| 207 | Rp(4, 0, 1, 2, 3, 56), | |
| 208 | Rp(3, 4, 0, 1, 2, 57), | |
| 209 | Rp(2, 3, 4, 0, 1, 58), | |
| 210 | Rp(1, 2, 3, 4, 0, 59), | |
| 211 | }; | |
| 212 | inline for (round2) |r| { | |
| 213 | const t = s[(r.i-3) & 0xf] ^ s[(r.i-8) & 0xf] ^ s[(r.i-14) & 0xf] ^ s[(r.i-16) & 0xf]; | |
| 214 | s[r.i & 0xf] = math.rotl(u32, t, u32(1)); | |
| 215 | ||
| 216 | v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0x8F1BBCDC +% s[r.i & 0xf] | |
| 217 | +% ((v[r.b] & v[r.c]) ^ (v[r.b] & v[r.d]) ^ (v[r.c] & v[r.d])); | |
| 218 | v[r.b] = math.rotl(u32, v[r.b], u32(30)); | |
| 219 | } | |
| 220 | ||
| 221 | const round3 = comptime []RoundParam { | |
| 222 | Rp(0, 1, 2, 3, 4, 60), | |
| 223 | Rp(4, 0, 1, 2, 3, 61), | |
| 224 | Rp(3, 4, 0, 1, 2, 62), | |
| 225 | Rp(2, 3, 4, 0, 1, 63), | |
| 226 | Rp(1, 2, 3, 4, 0, 64), | |
| 227 | Rp(0, 1, 2, 3, 4, 65), | |
| 228 | Rp(4, 0, 1, 2, 3, 66), | |
| 229 | Rp(3, 4, 0, 1, 2, 67), | |
| 230 | Rp(2, 3, 4, 0, 1, 68), | |
| 231 | Rp(1, 2, 3, 4, 0, 69), | |
| 232 | Rp(0, 1, 2, 3, 4, 70), | |
| 233 | Rp(4, 0, 1, 2, 3, 71), | |
| 234 | Rp(3, 4, 0, 1, 2, 72), | |
| 235 | Rp(2, 3, 4, 0, 1, 73), | |
| 236 | Rp(1, 2, 3, 4, 0, 74), | |
| 237 | Rp(0, 1, 2, 3, 4, 75), | |
| 238 | Rp(4, 0, 1, 2, 3, 76), | |
| 239 | Rp(3, 4, 0, 1, 2, 77), | |
| 240 | Rp(2, 3, 4, 0, 1, 78), | |
| 241 | Rp(1, 2, 3, 4, 0, 79), | |
| 242 | }; | |
| 243 | inline for (round3) |r| { | |
| 244 | const t = s[(r.i-3) & 0xf] ^ s[(r.i-8) & 0xf] ^ s[(r.i-14) & 0xf] ^ s[(r.i-16) & 0xf]; | |
| 245 | s[r.i & 0xf] = math.rotl(u32, t, u32(1)); | |
| 246 | ||
| 247 | v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0xCA62C1D6 +% s[r.i & 0xf] | |
| 248 | +% (v[r.b] ^ v[r.c] ^ v[r.d]); | |
| 249 | v[r.b] = math.rotl(u32, v[r.b], u32(30)); | |
| 250 | } | |
| 251 | ||
| 252 | d.s[0] +%= v[0]; | |
| 253 | d.s[1] +%= v[1]; | |
| 254 | d.s[2] +%= v[2]; | |
| 255 | d.s[3] +%= v[3]; | |
| 256 | d.s[4] +%= v[4]; | |
| 257 | } | |
| 258 | }; | |
| 259 | ||
| 260 | test "sha1 single" { | |
| 261 | debug.assert(0xda39a3ee5e6b4b0d3255bfef95601890afd80709 == Sha1.hash("")); | |
| 262 | debug.assert(0xa9993e364706816aba3e25717850c26c9cd0d89d == Sha1.hash("abc")); | |
| 263 | debug.assert(0xa49b2446a02c645bf419f995b67091253a04a259 == Sha1.hash("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu")); | |
| 264 | } | |
| 265 | ||
| 266 | test "sha1 streaming" { | |
| 267 | var h = Sha1.init(); | |
| 268 | ||
| 269 | debug.assert(0xda39a3ee5e6b4b0d3255bfef95601890afd80709 == h.final()); | |
| 270 | ||
| 271 | h.reset(); | |
| 272 | h.update("abc"); | |
| 273 | debug.assert(0xa9993e364706816aba3e25717850c26c9cd0d89d == h.final()); | |
| 274 | ||
| 275 | h.reset(); | |
| 276 | h.update("a"); | |
| 277 | h.update("b"); | |
| 278 | h.update("c"); | |
| 279 | debug.assert(0xa9993e364706816aba3e25717850c26c9cd0d89d == h.final()); | |
| 280 | } |
std/index.zig+2| ... | ... | @@ -10,6 +10,7 @@ pub const LinkedList = @import("linked_list.zig").LinkedList; |
| 10 | 10 | pub const base64 = @import("base64.zig"); |
| 11 | 11 | pub const build = @import("build.zig"); |
| 12 | 12 | pub const c = @import("c/index.zig"); |
| 13 | pub const crypto = @import("crypto/index.zig"); | |
| 13 | 14 | pub const cstr = @import("cstr.zig"); |
| 14 | 15 | pub const debug = @import("debug/index.zig"); |
| 15 | 16 | pub const dwarf = @import("dwarf.zig"); |
| ... | ... | @@ -39,6 +40,7 @@ test "std" { |
| 39 | 40 | _ = @import("base64.zig"); |
| 40 | 41 | _ = @import("build.zig"); |
| 41 | 42 | _ = @import("c/index.zig"); |
| 43 | _ = @import("crypto/index.zig"); | |
| 42 | 44 | _ = @import("cstr.zig"); |
| 43 | 45 | _ = @import("debug/index.zig"); |
| 44 | 46 | _ = @import("dwarf.zig"); |