@@ -116,6 +116,7 @@ public actual abstract class Mac: javax.crypto.Mac, Algorithm, Copyable<Mac>, Re
116
116
public actual fun doFinalInto (dest : ByteArray , destOffset : Int ): Int = commonDoFinalInto(
117
117
dest = dest,
118
118
destOffset = destOffset,
119
+ engineResetOnDoFinal = engine.resetOnDoFinal,
119
120
engineDoFinalInto = engine::doFinalInto,
120
121
engineReset = engine::reset,
121
122
)
@@ -150,19 +151,49 @@ public actual abstract class Mac: javax.crypto.Mac, Algorithm, Copyable<Mac>, Re
150
151
protected actual abstract class Engine : MacSpi , Cloneable , Copyable <Engine >, Resettable , Updatable {
151
152
152
153
/* *
153
- * Initializes a new [Engine] with the provided [key].
154
+ * Most [Mac.Engine] are backed by a `Digest`, whereby calling [reset] after
155
+ * [doFinal] will cause a double reset (because `Digest.digest` does this inherently).
156
+ * By setting this value to `false`, [Engine.reset] will **not** be called whenever
157
+ * [doFinal] gets invoked.
154
158
*
155
- * @throws [IllegalArgumentException] if [key] is empty.
159
+ * **NOTE:** Implementations taking ownership of the automatic reset functionality
160
+ * by setting this to `false` must ensure that whatever re-initialization steps were
161
+ * taken in their [Engine.reset] function body are executed before their [doFinal]
162
+ * and [doFinalInto] implementations return.
163
+ * */
164
+ @JvmField
165
+ public actual val resetOnDoFinal: Boolean
166
+
167
+ /* *
168
+ * Initializes a new [Engine] with the provided [key] with the default [resetOnDoFinal]
169
+ * value of `true` (i.e. [Engine.reset] will be called automatically after [Engine.doFinal]
170
+ * or [Engine.doFinalInto] have been invoked).
171
+ *
172
+ * @param [key] The key that this [Engine] instance will use to apply its function to
173
+ * @throws [IllegalArgumentException] if [key] is empty
174
+ * */
175
+ @Throws(IllegalArgumentException ::class )
176
+ public actual constructor (key: ByteArray ): this (key, resetOnDoFinal = true )
177
+
178
+ /* *
179
+ * Initializes a new [Engine] with the provided [key] and [resetOnDoFinal] configuration.
180
+ *
181
+ * @param [key] the key that this [Engine] instance will use to apply its function to
182
+ * @param [resetOnDoFinal] See [Engine.resetOnDoFinal] documentation
183
+ * @throws [IllegalArgumentException] if [key] is empty
156
184
* */
157
185
@Throws(IllegalArgumentException ::class )
158
- public actual constructor (key: ByteArray ) {
186
+ public actual constructor (key: ByteArray , resetOnDoFinal : Boolean ) {
159
187
require(key.isNotEmpty()) { " key cannot be empty" }
188
+ this .resetOnDoFinal = resetOnDoFinal
160
189
}
161
190
162
191
/* *
163
192
* Creates a new [Engine] from [other], copying its state.
164
193
* */
165
- protected actual constructor (other: Engine )
194
+ protected actual constructor (other: Engine ) {
195
+ this .resetOnDoFinal = other.resetOnDoFinal
196
+ }
166
197
167
198
/* *
168
199
* The number of bytes the implementation returns when [doFinal] is called.
@@ -212,6 +243,10 @@ public actual abstract class Mac: javax.crypto.Mac, Algorithm, Copyable<Mac>, Re
212
243
@Throws(IllegalArgumentException ::class )
213
244
public actual abstract fun reset (newKey : ByteArray )
214
245
246
+ // Gets set in engineDoFinal if resetOnDoFinal is set to false. Subsequent
247
+ // engineReset call will then change it back to false and return early.
248
+ private var consumeNextEngineReset = false
249
+
215
250
// MacSpi
216
251
/* * @suppress */
217
252
@Deprecated(" Do not use. Will be marked as ERROR in a later release" )
@@ -230,7 +265,13 @@ public actual abstract class Mac: javax.crypto.Mac, Algorithm, Copyable<Mac>, Re
230
265
}
231
266
/* * @suppress */
232
267
@Deprecated(" Do not use. Will be marked as ERROR in a later release" )
233
- protected final override fun engineReset () { reset() }
268
+ protected final override fun engineReset () {
269
+ if (consumeNextEngineReset) {
270
+ consumeNextEngineReset = false
271
+ return
272
+ }
273
+ reset()
274
+ }
234
275
/* * @suppress */
235
276
@Deprecated(" Do not use. Will be marked as ERROR in a later release" )
236
277
protected final override fun engineGetMacLength (): Int = macLength()
@@ -257,11 +298,14 @@ public actual abstract class Mac: javax.crypto.Mac, Algorithm, Copyable<Mac>, Re
257
298
/* * @suppress */
258
299
@Deprecated(" Do not use. Will be marked as ERROR in a later release" )
259
300
protected final override fun engineDoFinal (): ByteArray {
301
+ if (! resetOnDoFinal) consumeNextEngineReset = true
302
+
260
303
val b = doFinal()
261
304
262
305
// Android API 23 and below javax.crypto.Mac does not call engineReset()
306
+ @Suppress(" DEPRECATION" )
263
307
@OptIn(InternalKotlinCryptoApi ::class )
264
- KC_ANDROID_SDK_INT ?. let { if (it <= 23 ) reset() }
308
+ if (( KC_ANDROID_SDK_INT ? : 24 ) < 24 ) engineReset()
265
309
266
310
return b
267
311
}
0 commit comments