Skip to content

Commit d92f228

Browse files
authored
Optimize MD5 & SHA1 (#77)
1 parent 04226aa commit d92f228

File tree

11 files changed

+463
-367
lines changed

11 files changed

+463
-367
lines changed

benchmarks/src/commonMain/kotlin/org/kotlincrypto/hash/benchmarks/SHA2Opts.kt

+18
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,31 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
**/
16+
@file:Suppress("ClassName")
17+
1618
package org.kotlincrypto.hash.benchmarks
1719

1820
import kotlinx.benchmark.*
21+
import org.kotlincrypto.hash.md.MD5
22+
import org.kotlincrypto.hash.sha1.SHA1
1923
import org.kotlincrypto.hash.sha2.SHA256
2024
import org.kotlincrypto.hash.sha2.SHA512
2125
import org.kotlincrypto.hash.sha2.SHA512_224
2226

27+
@State(Scope.Benchmark)
28+
@BenchmarkMode(Mode.AverageTime)
29+
@OutputTimeUnit(BenchmarkTimeUnit.NANOSECONDS)
30+
@Warmup(iterations = ITERATIONS, time = TIME_WARMUP)
31+
@Measurement(iterations = ITERATIONS, time = TIME_MEASURE)
32+
open class MD5Benchmark: DigestBenchmarkBase(MD5())
33+
34+
@State(Scope.Benchmark)
35+
@BenchmarkMode(Mode.AverageTime)
36+
@OutputTimeUnit(BenchmarkTimeUnit.NANOSECONDS)
37+
@Warmup(iterations = ITERATIONS, time = TIME_WARMUP)
38+
@Measurement(iterations = ITERATIONS, time = TIME_MEASURE)
39+
open class SHA1Benchmark: DigestBenchmarkBase(SHA1())
40+
2341
@State(Scope.Benchmark)
2442
@BenchmarkMode(Mode.AverageTime)
2543
@OutputTimeUnit(BenchmarkTimeUnit.NANOSECONDS)

benchmarks/src/commonMain/kotlin/org/kotlincrypto/hash/benchmarks/SHA3Opts.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
**/
16+
@file:Suppress("ClassName")
17+
1618
package org.kotlincrypto.hash.benchmarks
1719

1820
import kotlinx.benchmark.*
19-
import org.kotlincrypto.core.digest.Digest
2021
import org.kotlincrypto.hash.sha3.ParallelHash128
2122
import org.kotlincrypto.hash.sha3.SHA3_256
2223
import org.kotlincrypto.hash.sha3.SHAKE128

library/md/src/commonMain/kotlin/org/kotlincrypto/hash/md/MD5.kt

+71-61
Original file line numberDiff line numberDiff line change
@@ -45,54 +45,54 @@ public class MD5: Digest {
4545
val s = S
4646

4747
val x = x
48+
val state = state
4849

4950
var a = state[0]
5051
var b = state[1]
5152
var c = state[2]
5253
var d = state[3]
5354

54-
for (i in 0..<blockSize()) {
55-
when {
56-
i < 16 -> {
57-
var j = (i * 4) + offset
58-
x[i] =
59-
((input[j++].toInt() and 0xff) ) or
60-
((input[j++].toInt() and 0xff) shl 8) or
61-
((input[j++].toInt() and 0xff) shl 16) or
62-
((input[j ].toInt() and 0xff) shl 24)
63-
64-
val g = i + 0
65-
val f = ((b and c) or (b.inv() and d)) + a + k[i] + x[g]
66-
a = d
67-
d = c
68-
c = b
69-
b += f rotateLeft s[i]
70-
}
71-
i < 32 -> {
72-
val g = ((5 * i) + 1) % 16
73-
val f = ((d and b) or (d.inv() and c)) + a + k[i] + x[g]
74-
a = d
75-
d = c
76-
c = b
77-
b += f rotateLeft s[i]
78-
}
79-
i < 48 -> {
80-
val g = ((3 * i) + 5) % 16
81-
val f = (b xor c xor d) + a + k[i] + x[g]
82-
a = d
83-
d = c
84-
c = b
85-
b += f rotateLeft s[i]
86-
}
87-
else -> {
88-
val g = (7 * i) % 16
89-
val f = (c xor (b or d.inv())) + a + k[i] + x[g]
90-
a = d
91-
d = c
92-
c = b
93-
b += f rotateLeft s[i]
94-
}
95-
}
55+
var j = offset
56+
for (i in 0..<16) {
57+
x[i] =
58+
((input[j++].toInt() and 0xff) ) or
59+
((input[j++].toInt() and 0xff) shl 8) or
60+
((input[j++].toInt() and 0xff) shl 16) or
61+
((input[j++].toInt() and 0xff) shl 24)
62+
63+
val g = i + 0
64+
val f = ((b and c) or (b.inv() and d)) + a + k[i] + x[g]
65+
a = d
66+
d = c
67+
c = b
68+
b += f.rotateLeft(s[i])
69+
}
70+
71+
for (i in 16..<32) {
72+
val g = ((5 * i) + 1) % 16
73+
val f = ((d and b) or (d.inv() and c)) + a + k[i] + x[g]
74+
a = d
75+
d = c
76+
c = b
77+
b += f.rotateLeft(s[i])
78+
}
79+
80+
for (i in 32..<48) {
81+
val g = ((3 * i) + 5) % 16
82+
val f = (b xor c xor d) + a + k[i] + x[g]
83+
a = d
84+
d = c
85+
c = b
86+
b += f.rotateLeft(s[i])
87+
}
88+
89+
for (i in 48..<64) {
90+
val g = (7 * i) % 16
91+
val f = (c xor (b or d.inv())) + a + k[i] + x[g]
92+
a = d
93+
d = c
94+
c = b
95+
b += f.rotateLeft(s[i])
9696
}
9797

9898
state[0] += a
@@ -113,17 +113,21 @@ public class MD5: Digest {
113113
buffer.fill(0, size, 56)
114114
}
115115

116-
buffer[56] = (bitLength ).toByte()
117-
buffer[57] = (bitLength ushr 8).toByte()
118-
buffer[58] = (bitLength ushr 16).toByte()
119-
buffer[59] = (bitLength ushr 24).toByte()
120-
buffer[60] = (bitLength ushr 32).toByte()
121-
buffer[61] = (bitLength ushr 40).toByte()
122-
buffer[62] = (bitLength ushr 48).toByte()
123-
buffer[63] = (bitLength ushr 56).toByte()
116+
val lo = bitLength.toInt()
117+
val hi = bitLength.rotateLeft(32).toInt()
118+
119+
buffer[56] = (lo ).toByte()
120+
buffer[57] = (lo ushr 8).toByte()
121+
buffer[58] = (lo ushr 16).toByte()
122+
buffer[59] = (lo ushr 24).toByte()
123+
buffer[60] = (hi ).toByte()
124+
buffer[61] = (hi ushr 8).toByte()
125+
buffer[62] = (hi ushr 16).toByte()
126+
buffer[63] = (hi ushr 24).toByte()
124127

125128
compress(buffer, 0)
126129

130+
val state = state
127131
val a = state[0]
128132
val b = state[1]
129133
val c = state[2]
@@ -150,16 +154,14 @@ public class MD5: Digest {
150154
}
151155

152156
protected override fun resetDigest() {
157+
val state = state
153158
x.fill(0)
154159
state[0] = 1732584193
155160
state[1] = -271733879
156161
state[2] = -1732584194
157162
state[3] = 271733878
158163
}
159164

160-
@Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress")
161-
private inline infix fun Int.rotateLeft(n: Int): Int = (this shl n) or (this ushr (32 - n))
162-
163165
private companion object {
164166
private val S = intArrayOf(
165167
// round 1 left rotates
@@ -186,17 +188,25 @@ public class MD5: Digest {
186188

187189
private val K = intArrayOf(
188190
// round 1
189-
-680876936, -389564586, 606105819, -1044525330, -176418897, 1200080426, -1473231341, -45705983,
190-
1770035416, -1958414417, -42063, -1990404162, 1804603682, -40341101, -1502002290, 1236535329,
191+
-680876936, -389564586, 606105819, -1044525330,
192+
-176418897, 1200080426, -1473231341, -45705983,
193+
1770035416, -1958414417, -42063, -1990404162,
194+
1804603682, -40341101, -1502002290, 1236535329,
191195
// round 2
192-
-165796510, -1069501632, 643717713, -373897302, -701558691, 38016083, -660478335, -405537848,
193-
568446438, -1019803690, -187363961, 1163531501, -1444681467, -51403784, 1735328473, -1926607734,
196+
-165796510, -1069501632, 643717713, -373897302,
197+
-701558691, 38016083, -660478335, -405537848,
198+
568446438, -1019803690, -187363961, 1163531501,
199+
-1444681467, -51403784, 1735328473, -1926607734,
194200
// round 3
195-
-378558, -2022574463, 1839030562, -35309556, -1530992060, 1272893353, -155497632, -1094730640,
196-
681279174, -358537222, -722521979, 76029189, -640364487, -421815835, 530742520, -995338651,
201+
-378558, -2022574463, 1839030562, -35309556,
202+
-1530992060, 1272893353, -155497632, -1094730640,
203+
681279174, -358537222, -722521979, 76029189,
204+
-640364487, - 421815835, 530742520, -995338651,
197205
// round 4
198-
-198630844, 1126891415, -1416354905, -57434055, 1700485571, -1894986606, -1051523, -2054922799,
199-
1873313359, -30611744, -1560198380, 1309151649, -145523070, -1120210379, 718787259, -343485551,
206+
-198630844, 1126891415, -1416354905, -57434055,
207+
1700485571, -1894986606, -1051523, -2054922799,
208+
1873313359, -30611744, -1560198380, 1309151649,
209+
-145523070, -1120210379, 718787259, -343485551,
200210
)
201211
}
202212
}

library/sha1/src/commonMain/kotlin/org/kotlincrypto/hash/sha1/SHA1.kt

+52-36
Original file line numberDiff line numberDiff line change
@@ -53,42 +53,56 @@ public class SHA1: Digest {
5353
}
5454

5555
for (i in 16..<80) {
56-
x[i] = (x[i - 3] xor x[i - 8] xor x[i - 14] xor x[i - 16]) rotateLeft 1
56+
x[i] = (x[i - 3] xor x[i - 8] xor x[i - 14] xor x[i - 16]).rotateLeft(1)
5757
}
5858

59+
val state = state
5960
var a = state[0]
6061
var b = state[1]
6162
var c = state[2]
6263
var d = state[3]
6364
var e = state[4]
6465

65-
for (i in 0..<80) {
66-
val a2 = when {
67-
i < 20 -> {
68-
val f = d xor (b and (c xor d))
69-
val k = 1518500249
70-
(a rotateLeft 5) + f + e + k + x[i]
71-
}
72-
i < 40 -> {
73-
val f = b xor c xor d
74-
val k = 1859775393
75-
(a rotateLeft 5) + f + e + k + x[i]
76-
}
77-
i < 60 -> {
78-
val f = (b and c) or (b and d) or (c and d)
79-
val k = -1894007588
80-
(a rotateLeft 5) + f + e + k + x[i]
81-
}
82-
else -> {
83-
val f = b xor c xor d
84-
val k = -899497514
85-
(a rotateLeft 5) + f + e + k + x[i]
86-
}
87-
}
66+
for (i in 0..<20) {
67+
val f = d xor (b and (c xor d))
68+
val k = 1518500249
69+
val a2 = (a.rotateLeft(5)) + f + e + k + x[i]
70+
e = d
71+
d = c
72+
c = b.rotateLeft(30)
73+
b = a
74+
a = a2
75+
}
8876

77+
for (i in 20..<40) {
78+
val f = b xor c xor d
79+
val k = 1859775393
80+
val a2 = (a.rotateLeft(5)) + f + e + k + x[i]
8981
e = d
9082
d = c
91-
c = b rotateLeft 30
83+
c = b.rotateLeft(30)
84+
b = a
85+
a = a2
86+
}
87+
88+
for (i in 40..<60) {
89+
val f = (b and c) or (b and d) or (c and d)
90+
val k = -1894007588
91+
val a2 = (a.rotateLeft(5)) + f + e + k + x[i]
92+
e = d
93+
d = c
94+
c = b.rotateLeft(30)
95+
b = a
96+
a = a2
97+
}
98+
99+
for (i in 60..<80) {
100+
val f = b xor c xor d
101+
val k = -899497514
102+
val a2 = (a.rotateLeft(5)) + f + e + k + x[i]
103+
e = d
104+
d = c
105+
c = b.rotateLeft(30)
92106
b = a
93107
a = a2
94108
}
@@ -112,17 +126,21 @@ public class SHA1: Digest {
112126
buffer.fill(0, size, 56)
113127
}
114128

115-
buffer[56] = (bitLength ushr 56).toByte()
116-
buffer[57] = (bitLength ushr 48).toByte()
117-
buffer[58] = (bitLength ushr 40).toByte()
118-
buffer[59] = (bitLength ushr 32).toByte()
119-
buffer[60] = (bitLength ushr 24).toByte()
120-
buffer[61] = (bitLength ushr 16).toByte()
121-
buffer[62] = (bitLength ushr 8).toByte()
122-
buffer[63] = (bitLength ).toByte()
129+
val lo = bitLength.toInt()
130+
val hi = bitLength.rotateLeft(32).toInt()
131+
132+
buffer[56] = (hi ushr 24).toByte()
133+
buffer[57] = (hi ushr 16).toByte()
134+
buffer[58] = (hi ushr 8).toByte()
135+
buffer[59] = (hi ).toByte()
136+
buffer[60] = (lo ushr 24).toByte()
137+
buffer[61] = (lo ushr 16).toByte()
138+
buffer[62] = (lo ushr 8).toByte()
139+
buffer[63] = (lo ).toByte()
123140

124141
compress(buffer, 0)
125142

143+
val state = state
126144
val a = state[0]
127145
val b = state[1]
128146
val c = state[2]
@@ -154,14 +172,12 @@ public class SHA1: Digest {
154172
}
155173

156174
protected override fun resetDigest() {
175+
val state = state
157176
x.fill(0)
158177
state[0] = 1732584193
159178
state[1] = -271733879
160179
state[2] = -1732584194
161180
state[3] = 271733878
162181
state[4] = -1009589776
163182
}
164-
165-
@Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress")
166-
private inline infix fun Int.rotateLeft(n: Int): Int = (this shl n) or (this ushr (32 - n))
167183
}

0 commit comments

Comments
 (0)