Skip to content

Commit 5cbbaa8

Browse files
authored
Add helper function for clearing Mac key (#101)
1 parent bde12bc commit 5cbbaa8

File tree

7 files changed

+94
-16
lines changed

7 files changed

+94
-16
lines changed

library/mac/api/mac.api

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ public abstract class org/kotlincrypto/core/mac/Mac : javax/crypto/Mac, org/kotl
22
protected fun <init> (Ljava/lang/String;Lorg/kotlincrypto/core/mac/Mac$Engine;)V
33
protected fun <init> (Lorg/kotlincrypto/core/mac/Mac;)V
44
public final fun algorithm ()Ljava/lang/String;
5+
public final fun clearKey ()V
56
public final fun equals (Ljava/lang/Object;)Z
67
public final fun hashCode ()I
78
public final fun macLength ()I

library/mac/api/mac.klib.api

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ abstract class org.kotlincrypto.core.mac/Mac : org.kotlincrypto.core/Algorithm,
1111
constructor <init>(org.kotlincrypto.core.mac/Mac) // org.kotlincrypto.core.mac/Mac.<init>|<init>(org.kotlincrypto.core.mac.Mac){}[0]
1212

1313
final fun algorithm(): kotlin/String // org.kotlincrypto.core.mac/Mac.algorithm|algorithm(){}[0]
14+
final fun clearKey() // org.kotlincrypto.core.mac/Mac.clearKey|clearKey(){}[0]
1415
final fun doFinal(): kotlin/ByteArray // org.kotlincrypto.core.mac/Mac.doFinal|doFinal(){}[0]
1516
final fun doFinal(kotlin/ByteArray): kotlin/ByteArray // org.kotlincrypto.core.mac/Mac.doFinal|doFinal(kotlin.ByteArray){}[0]
1617
final fun equals(kotlin/Any?): kotlin/Boolean // org.kotlincrypto.core.mac/Mac.equals|equals(kotlin.Any?){}[0]

library/mac/src/commonMain/kotlin/org/kotlincrypto/core/mac/Mac.kt

+21-2
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,20 @@ public expect abstract class Mac: Algorithm, Copyable<Mac>, Resettable, Updatabl
106106
/**
107107
* Resets the [Mac] and will reinitialize it with the provided key.
108108
*
109-
* This is useful if wanting to clear the key before de-referencing.
109+
* This is useful if wanting to zero out the key before de-referencing.
110110
*
111-
* @throws [IllegalArgumentException] if [newKey] is empty.
111+
* @see [clearKey]
112+
* @throws [IllegalArgumentException] if [newKey] is empty, or of a length
113+
* inappropriate for the [Mac] implementation.
112114
* */
113115
public fun reset(newKey: ByteArray)
114116

117+
/**
118+
* Helper function that will call [reset] with a blank key in order
119+
* to zero it out.
120+
* */
121+
public fun clearKey()
122+
115123
/**
116124
* Core abstraction for powering a [Mac] implementation.
117125
*
@@ -147,6 +155,17 @@ public expect abstract class Mac: Algorithm, Copyable<Mac>, Resettable, Updatabl
147155
// See Updatable interface documentation
148156
public override fun update(input: ByteArray)
149157

158+
/**
159+
* Resets the [Engine] and will reinitialize it with the provided key.
160+
*
161+
* **NOTE:** [newKey] is checked to be non-empty by the [Mac] abstraction
162+
* before passing it here. Implementations should ensure any old key material
163+
* is zeroed out.
164+
*
165+
* @throws [IllegalArgumentException] if [newKey] is a length inappropriate
166+
* for the [Mac] implementation.
167+
* */
168+
@Throws(IllegalArgumentException::class)
150169
public abstract fun reset(newKey: ByteArray)
151170

152171
/** @suppress */

library/mac/src/commonMain/kotlin/org/kotlincrypto/core/mac/internal/-CommonPlatform.kt

+17-3
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,33 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
**/
16-
@file:Suppress("KotlinRedundantDiagnosticSuppress")
16+
@file:Suppress("KotlinRedundantDiagnosticSuppress", "NOTHING_TO_INLINE")
1717

1818
package org.kotlincrypto.core.mac.internal
1919

2020
import org.kotlincrypto.core.mac.Mac
2121

22+
private val SINGLE_0BYTE_KEY = ByteArray(1) { 0 }
23+
24+
@Suppress("UnusedReceiverParameter")
2225
@Throws(IllegalArgumentException::class)
23-
@Suppress("NOTHING_TO_INLINE", "UnusedReceiverParameter")
2426
internal inline fun Mac.commonInit(algorithm: String) {
2527
require(algorithm.isNotBlank()) { "algorithm cannot be blank" }
2628
}
2729

28-
@Suppress("NOTHING_TO_INLINE")
2930
internal inline fun Mac.commonToString(): String {
3031
return "Mac[${algorithm()}]@${hashCode()}"
3132
}
33+
34+
internal inline fun Mac.commonClearKey(engineReset: (ByteArray) -> Unit) {
35+
try {
36+
engineReset(SINGLE_0BYTE_KEY)
37+
} catch (e1: IllegalArgumentException) {
38+
try {
39+
engineReset(ByteArray(macLength()))
40+
} catch (e2: IllegalArgumentException) {
41+
e2.addSuppressed(e1)
42+
throw e2
43+
}
44+
}
45+
}

library/mac/src/commonTest/kotlin/org/kotlincrypto/core/mac/MacUnitTest.kt

+14
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,18 @@ class MacUnitTest {
8080
assertEquals(2, doFinalCount)
8181
assertEquals(2, resetCount)
8282
}
83+
84+
@Test
85+
fun givenMac_whenClearKey_thenSingle0ByteKeyPassedToEngine() {
86+
var zeroKey: ByteArray? = null
87+
88+
TestMac(
89+
ByteArray(5),
90+
"test rekey",
91+
rekey = { zeroKey = it }
92+
).clearKey()
93+
94+
assertNotNull(zeroKey)
95+
assertContentEquals(ByteArray(1) { 0 }, zeroKey)
96+
}
8397
}

library/mac/src/jvmMain/kotlin/org/kotlincrypto/core/mac/Mac.kt

+20-6
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@
1818
package org.kotlincrypto.core.mac
1919

2020
import org.kotlincrypto.core.*
21-
import org.kotlincrypto.core.mac.internal.AndroidApi21to23MacSpiProvider
22-
import org.kotlincrypto.core.mac.internal.commonInit
23-
import org.kotlincrypto.core.mac.internal.commonToString
21+
import org.kotlincrypto.core.mac.internal.*
2422
import java.nio.ByteBuffer
2523
import java.security.InvalidKeyException
2624
import java.security.Key
@@ -109,15 +107,23 @@ public actual abstract class Mac: javax.crypto.Mac, Algorithm, Copyable<Mac>, Re
109107
/**
110108
* Resets the [Mac] and will reinitialize it with the provided key.
111109
*
112-
* This is useful if wanting to clear the key before de-referencing.
110+
* This is useful if wanting to zero out the key before de-referencing.
113111
*
114-
* @throws [IllegalArgumentException] if [newKey] is empty.
112+
* @see [clearKey]
113+
* @throws [IllegalArgumentException] if [newKey] is empty, or of a length
114+
* inappropriate for the [Mac] implementation.
115115
* */
116116
public actual fun reset(newKey: ByteArray) {
117117
require(newKey.isNotEmpty()) { "newKey cannot be empty" }
118118
engine.reset(newKey)
119119
}
120120

121+
/**
122+
* Helper function that will call [reset] with a blank key in order
123+
* to zero it out.
124+
* */
125+
public actual fun clearKey() { commonClearKey(engine::reset) }
126+
121127
/**
122128
* Core abstraction for powering a [Mac] implementation. Extends
123129
* Java's [MacSpi] for compatibility.
@@ -157,8 +163,16 @@ public actual abstract class Mac: javax.crypto.Mac, Algorithm, Copyable<Mac>, Re
157163
public actual override fun update(input: ByteArray) { update(input, 0, input.size) }
158164

159165
/**
160-
* Resets the [Engine] and will reinitialize it with the newly provided key.
166+
* Resets the [Engine] and will reinitialize it with the provided key.
167+
*
168+
* **NOTE:** [newKey] is checked to be non-empty by the [Mac] abstraction
169+
* before passing it here. Implementations should ensure any old key material
170+
* is zeroed out.
171+
*
172+
* @throws [IllegalArgumentException] if [newKey] is a length inappropriate
173+
* for the [Mac] implementation.
161174
* */
175+
@Throws(IllegalArgumentException::class)
162176
public actual abstract fun reset(newKey: ByteArray)
163177

164178
// MacSpi

library/mac/src/nonJvmMain/kotlin/org/kotlincrypto/core/mac/Mac.kt

+20-5
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@
1818
package org.kotlincrypto.core.mac
1919

2020
import org.kotlincrypto.core.*
21-
import org.kotlincrypto.core.mac.internal.commonInit
22-
import org.kotlincrypto.core.mac.internal.commonToString
21+
import org.kotlincrypto.core.mac.internal.*
2322

2423
/**
2524
* Core abstraction for Message Authentication Code implementations.
@@ -132,15 +131,23 @@ public actual abstract class Mac: Algorithm, Copyable<Mac>, Resettable, Updatabl
132131
/**
133132
* Resets the [Mac] and will reinitialize it with the provided key.
134133
*
135-
* This is useful if wanting to clear the key before de-referencing.
134+
* This is useful if wanting to zero out the key before de-referencing.
136135
*
137-
* @throws [IllegalArgumentException] if [newKey] is empty.
136+
* @see [clearKey]
137+
* @throws [IllegalArgumentException] if [newKey] is empty, or of a length
138+
* inappropriate for the [Mac] implementation.
138139
* */
139140
public actual fun reset(newKey: ByteArray) {
140141
require(newKey.isNotEmpty()) { "newKey cannot be empty" }
141142
engine.reset(newKey)
142143
}
143144

145+
/**
146+
* Helper function that will call [reset] with a blank key in order
147+
* to zero it out.
148+
* */
149+
public actual fun clearKey() { commonClearKey(engine::reset) }
150+
144151
/**
145152
* Core abstraction for powering a [Mac] implementation.
146153
*
@@ -179,8 +186,16 @@ public actual abstract class Mac: Algorithm, Copyable<Mac>, Resettable, Updatabl
179186
public actual override fun update(input: ByteArray) { update(input, 0, input.size) }
180187

181188
/**
182-
* Resets the [Engine] and will reinitialize it with the newly provided key.
189+
* Resets the [Engine] and will reinitialize it with the provided key.
190+
*
191+
* **NOTE:** [newKey] is checked to be non-empty by the [Mac] abstraction
192+
* before passing it here. Implementations should ensure any old key material
193+
* is zeroed out.
194+
*
195+
* @throws [IllegalArgumentException] if [newKey] is a length inappropriate
196+
* for the [Mac] implementation.
183197
* */
198+
@Throws(IllegalArgumentException::class)
184199
public actual abstract fun reset(newKey: ByteArray)
185200

186201
private val code = Any()

0 commit comments

Comments
 (0)