Skip to content

Commit 56a1dcb

Browse files
authored
Implement Mac.doFinalInto for all modules (#71)
1 parent 4aef214 commit 56a1dcb

File tree

5 files changed

+85
-24
lines changed
  • library
    • blake2/src/commonMain/kotlin/org/kotlincrypto/macs/blake2
    • hmac/hmac/src/commonMain/kotlin/org/kotlincrypto/macs/hmac
    • kmac/src/commonMain/kotlin/org/kotlincrypto/macs/kmac
  • tools/testing/src

5 files changed

+85
-24
lines changed

library/blake2/src/commonMain/kotlin/org/kotlincrypto/macs/blake2/BLAKE2Mac.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,15 @@ public sealed class BLAKE2Mac: Mac {
9595

9696
override fun update(input: ByteArray, offset: Int, len: Int) { digest.update(input, offset, len) }
9797

98-
override fun doFinal(): ByteArray = digest.digest()
98+
override fun doFinal(): ByteArray {
99+
val final = ByteArray(digest.digestLength())
100+
doFinalInto(final, 0)
101+
return final
102+
}
103+
104+
override fun doFinalInto(dest: ByteArray, destOffset: Int) {
105+
digest.digestInto(dest, destOffset)
106+
}
99107

100108
override fun reset() {
101109
digest.reset()

library/hmac/hmac/src/commonMain/kotlin/org/kotlincrypto/macs/hmac/Hmac.kt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,16 @@ public abstract class Hmac: Mac {
9191
override fun update(input: ByteArray, offset: Int, len: Int) { digest.update(input, offset, len) }
9292

9393
override fun doFinal(): ByteArray {
94-
val iFinal = digest.digest()
94+
val final = ByteArray(digest.digestLength())
95+
doFinalInto(final, 0)
96+
return final
97+
}
98+
99+
override fun doFinalInto(dest: ByteArray, destOffset: Int) {
100+
val inner = digest.digest()
95101
digest.update(oKey)
96-
return digest.digest(iFinal)
102+
digest.update(inner)
103+
digest.digestInto(dest, destOffset)
97104
}
98105

99106
override fun reset() {

library/kmac/src/commonMain/kotlin/org/kotlincrypto/macs/kmac/Kmac.kt

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,14 @@ public sealed class Kmac: Mac, ReKeyableXofAlgorithm {
116116
override fun copy(): DigestEngine = DigestEngine(this)
117117

118118
override fun doFinal(): ByteArray {
119+
val final = ByteArray(source.digestLength())
120+
doFinalInto(final, 0)
121+
return final
122+
}
123+
124+
override fun doFinalInto(dest: ByteArray, destOffset: Int) {
119125
padFinal()
120-
return source.digest()
126+
source.digestInto(dest, destOffset)
121127
}
122128
}
123129

@@ -162,8 +168,10 @@ public sealed class Kmac: Mac, ReKeyableXofAlgorithm {
162168
return source.reader(resetXof = true)
163169
}
164170

165-
// Never called
166-
override fun doFinal(): ByteArray = error("Xof Mode")
171+
// Never called. outputLength is 0, so.
172+
override fun doFinal(): ByteArray = ZERO_BYTES
173+
174+
private companion object { private val ZERO_BYTES = ByteArray(0) }
167175
}
168176

169177
@OptIn(InternalKotlinCryptoApi::class)

tools/testing/src/commonMain/kotlin/org/kotlincrypto/macs/MacUnitTest.kt

Lines changed: 53 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ abstract class MacUnitTest {
5252
return copyOf(max)
5353
}
5454

55+
private fun ByteArray.hex(): String = encodeToString(TestData.base16)
56+
5557
open val maxKeyLength: Int? = null
5658
abstract val expectedResetSmallHash: String
5759
abstract val expectedResetMediumHash: String
@@ -72,53 +74,86 @@ abstract class MacUnitTest {
7274
open fun givenMac_whenResetSmallKey_thenDoFinalReturnsExpected() {
7375
assertExpectedHashes
7476
val mac = mac(KEY_SMALL.toMaxKeyLen())
75-
val empty = mac.doFinal().encodeToString(TestData.base16)
77+
val empty = mac.doFinal().hex()
7678
updateSmall(mac)
7779
mac.reset()
78-
val actual = mac.doFinal().encodeToString(TestData.base16)
80+
val actual = mac.doFinal().hex()
7981
assertEquals(empty, actual)
8082
assertEquals(expectedResetSmallHash, actual)
83+
84+
updateSmall(mac)
85+
mac.reset()
86+
val into = ByteArray(mac.macLength() + 16)
87+
mac.doFinalInto(into, 16)
88+
for (i in 0..<16) {
89+
assertEquals(0, into[i])
90+
}
91+
assertEquals(expectedResetSmallHash, into.copyInto(ByteArray(mac.macLength()), startIndex = 16).hex())
8192
}
8293

8394
open fun givenMac_whenResetMediumKey_thenDoFinalReturnsExpected() {
8495
assertExpectedHashes
8596
val mac = mac(KEY_MEDIUM.toMaxKeyLen())
86-
val empty = mac.doFinal().encodeToString(TestData.base16)
97+
val empty = mac.doFinal().hex()
8798
updateSmall(mac)
8899
mac.reset()
89-
val actual = mac.doFinal().encodeToString(TestData.base16)
100+
val actual = mac.doFinal().hex()
90101
assertEquals(empty, actual)
91102
assertEquals(expectedResetMediumHash, actual)
103+
104+
updateSmall(mac)
105+
mac.reset()
106+
val into = ByteArray(mac.macLength() + 16)
107+
mac.doFinalInto(into, 0)
108+
for (i in (into.size - 16) until into.size) {
109+
assertEquals(0, into[i])
110+
}
111+
assertEquals(expectedResetMediumHash, into.copyInto(ByteArray(mac.macLength()), endIndex = into.size - 16).hex())
92112
}
93113

94114
open fun givenMac_whenResetLargeKey_thenDoFinalReturnsExpected() {
95115
assertExpectedHashes
96116
val mac = mac(KEY_LARGE.toMaxKeyLen())
97-
val empty = mac.doFinal().encodeToString(TestData.base16)
117+
val empty = mac.doFinal().hex()
98118
updateSmall(mac)
99119
mac.reset()
100-
val actual = mac.doFinal().encodeToString(TestData.base16)
120+
val actual = mac.doFinal().hex()
101121
assertEquals(empty, actual)
102122
assertEquals(expectedResetLargeHash, actual)
123+
124+
updateSmall(mac)
125+
mac.reset()
126+
val into = ByteArray(mac.macLength())
127+
mac.doFinalInto(into, 0)
128+
assertEquals(expectedResetLargeHash, into.hex())
103129
}
104130

105131
open fun givenMac_whenUpdatedSmall_thenDoFinalReturnsExpected() {
106132
assertExpectedHashes
107133
val mac = mac(KEY_SMALL.toMaxKeyLen())
108134
mac.doFinal()
109135
updateSmall(mac)
110-
val actual = mac.doFinal().encodeToString(TestData.base16)
136+
val actual = mac.doFinal().hex()
111137
assertEquals(expectedUpdateSmallHash, actual)
138+
139+
updateSmall(mac)
140+
val into = ByteArray(mac.macLength())
141+
mac.doFinalInto(into, 0)
142+
assertEquals(expectedUpdateSmallHash, into.hex())
112143
}
113144

114145
open fun givenMac_whenUpdatedMedium_thenDoFinalReturnsExpected() {
115146
assertExpectedHashes
116147
val mac = mac(KEY_MEDIUM.toMaxKeyLen())
117148
mac.doFinal()
118149
updateMedium(mac)
119-
120-
val actual = mac.doFinal().encodeToString(TestData.base16)
150+
val actual = mac.doFinal().hex()
121151
assertEquals(expectedUpdateMediumHash, actual)
152+
153+
updateMedium(mac)
154+
val into = ByteArray(mac.macLength())
155+
mac.doFinalInto(into, 0)
156+
assertEquals(expectedUpdateMediumHash, into.hex())
122157
}
123158

124159
open fun givenMac_whenCopied_thenIsDifferentInstance() {
@@ -128,30 +163,30 @@ abstract class MacUnitTest {
128163

129164
val copy = mac.copy()
130165
assertNotEquals(copy, mac)
131-
assertEquals(expectedUpdateSmallHash, copy.doFinal().encodeToString(TestData.base16))
132-
assertEquals(expectedResetSmallHash, copy.doFinal().encodeToString(TestData.base16))
166+
assertEquals(expectedUpdateSmallHash, copy.doFinal().hex())
167+
assertEquals(expectedResetSmallHash, copy.doFinal().hex())
133168

134169
updateSmall(copy)
135-
assertEquals(expectedUpdateSmallHash, copy.doFinal().encodeToString(TestData.base16))
136-
assertEquals(expectedUpdateSmallHash, mac.doFinal().encodeToString(TestData.base16))
170+
assertEquals(expectedUpdateSmallHash, copy.doFinal().hex())
171+
assertEquals(expectedUpdateSmallHash, mac.doFinal().hex())
137172
}
138173

139174
open fun givenMac_whenDoFinal_thenLengthMatchesOutput() {
140175
assertExpectedHashes
141-
assertEquals(mac(KEY_SMALL.toMaxKeyLen()).doFinal().encodeToString(TestData.base16).length, expectedResetSmallHash.length)
176+
assertEquals(mac(KEY_SMALL.toMaxKeyLen()).doFinal().hex().length, expectedResetSmallHash.length)
142177
}
143178

144179
open fun givenMac_whenInstanceResetWithNewKey_thenDoFinalReturnsExpected() {
145180
assertExpectedHashes
146181
val mac = mac(KEY_SMALL.toMaxKeyLen())
147182
updateSmall(mac)
148-
assertEquals(mac.doFinal().encodeToString(TestData.base16), expectedUpdateSmallHash)
149-
assertEquals(mac.doFinal().encodeToString(TestData.base16), expectedResetSmallHash)
183+
assertEquals(mac.doFinal().hex(), expectedUpdateSmallHash)
184+
assertEquals(mac.doFinal().hex(), expectedResetSmallHash)
150185

151186
mac.reset(KEY_MEDIUM.toMaxKeyLen())
152187
updateMedium(mac)
153-
assertEquals(mac.doFinal().encodeToString(TestData.base16), expectedUpdateMediumHash)
154-
assertEquals(mac.doFinal().encodeToString(TestData.base16), expectedResetMediumHash)
188+
assertEquals(mac.doFinal().hex(), expectedUpdateMediumHash)
189+
assertEquals(mac.doFinal().hex(), expectedResetMediumHash)
155190
}
156191

157192
protected companion object {

tools/testing/src/jvmMain/kotlin/org/kotlincrypto/macs/TestJvmMac.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ class TestJvmMac: Mac {
5555
override fun update(input: ByteArray, offset: Int, len: Int) { delegate.update(input, offset, len) }
5656

5757
override fun doFinal(): ByteArray = delegate.doFinal()
58+
override fun doFinalInto(dest: ByteArray, destOffset: Int) {
59+
delegate.doFinal(dest, destOffset)
60+
}
5861

5962
override fun reset() { delegate.reset() }
6063
override fun reset(newKey: ByteArray) { delegate.init(SecretKeySpec(newKey, delegate.algorithm)) }

0 commit comments

Comments
 (0)