diff --git a/library/mac/api/mac.api b/library/mac/api/mac.api index a3ad97e..2fa7758 100644 --- a/library/mac/api/mac.api +++ b/library/mac/api/mac.api @@ -2,6 +2,7 @@ public abstract class org/kotlincrypto/core/mac/Mac : javax/crypto/Mac, org/kotl protected fun (Ljava/lang/String;Lorg/kotlincrypto/core/mac/Mac$Engine;)V protected fun (Lorg/kotlincrypto/core/mac/Mac;)V public final fun algorithm ()Ljava/lang/String; + public final fun clearKey ()V public final fun equals (Ljava/lang/Object;)Z public final fun hashCode ()I public final fun macLength ()I diff --git a/library/mac/api/mac.klib.api b/library/mac/api/mac.klib.api index bdd8679..3eac3bc 100644 --- a/library/mac/api/mac.klib.api +++ b/library/mac/api/mac.klib.api @@ -11,6 +11,7 @@ abstract class org.kotlincrypto.core.mac/Mac : org.kotlincrypto.core/Algorithm, constructor (org.kotlincrypto.core.mac/Mac) // org.kotlincrypto.core.mac/Mac.|(org.kotlincrypto.core.mac.Mac){}[0] final fun algorithm(): kotlin/String // org.kotlincrypto.core.mac/Mac.algorithm|algorithm(){}[0] + final fun clearKey() // org.kotlincrypto.core.mac/Mac.clearKey|clearKey(){}[0] final fun doFinal(): kotlin/ByteArray // org.kotlincrypto.core.mac/Mac.doFinal|doFinal(){}[0] final fun doFinal(kotlin/ByteArray): kotlin/ByteArray // org.kotlincrypto.core.mac/Mac.doFinal|doFinal(kotlin.ByteArray){}[0] final fun equals(kotlin/Any?): kotlin/Boolean // org.kotlincrypto.core.mac/Mac.equals|equals(kotlin.Any?){}[0] diff --git a/library/mac/src/commonMain/kotlin/org/kotlincrypto/core/mac/Mac.kt b/library/mac/src/commonMain/kotlin/org/kotlincrypto/core/mac/Mac.kt index eb6838e..95987d8 100644 --- a/library/mac/src/commonMain/kotlin/org/kotlincrypto/core/mac/Mac.kt +++ b/library/mac/src/commonMain/kotlin/org/kotlincrypto/core/mac/Mac.kt @@ -106,12 +106,20 @@ public expect abstract class Mac: Algorithm, Copyable, Resettable, Updatabl /** * Resets the [Mac] and will reinitialize it with the provided key. * - * This is useful if wanting to clear the key before de-referencing. + * This is useful if wanting to zero out the key before de-referencing. * - * @throws [IllegalArgumentException] if [newKey] is empty. + * @see [clearKey] + * @throws [IllegalArgumentException] if [newKey] is empty, or of a length + * inappropriate for the [Mac] implementation. * */ public fun reset(newKey: ByteArray) + /** + * Helper function that will call [reset] with a blank key in order + * to zero it out. + * */ + public fun clearKey() + /** * Core abstraction for powering a [Mac] implementation. * @@ -147,6 +155,17 @@ public expect abstract class Mac: Algorithm, Copyable, Resettable, Updatabl // See Updatable interface documentation public override fun update(input: ByteArray) + /** + * Resets the [Engine] and will reinitialize it with the provided key. + * + * **NOTE:** [newKey] is checked to be non-empty by the [Mac] abstraction + * before passing it here. Implementations should ensure any old key material + * is zeroed out. + * + * @throws [IllegalArgumentException] if [newKey] is a length inappropriate + * for the [Mac] implementation. + * */ + @Throws(IllegalArgumentException::class) public abstract fun reset(newKey: ByteArray) /** @suppress */ diff --git a/library/mac/src/commonMain/kotlin/org/kotlincrypto/core/mac/internal/-CommonPlatform.kt b/library/mac/src/commonMain/kotlin/org/kotlincrypto/core/mac/internal/-CommonPlatform.kt index e8b4b7a..1e35429 100644 --- a/library/mac/src/commonMain/kotlin/org/kotlincrypto/core/mac/internal/-CommonPlatform.kt +++ b/library/mac/src/commonMain/kotlin/org/kotlincrypto/core/mac/internal/-CommonPlatform.kt @@ -13,19 +13,33 @@ * See the License for the specific language governing permissions and * limitations under the License. **/ -@file:Suppress("KotlinRedundantDiagnosticSuppress") +@file:Suppress("KotlinRedundantDiagnosticSuppress", "NOTHING_TO_INLINE") package org.kotlincrypto.core.mac.internal import org.kotlincrypto.core.mac.Mac +private val SINGLE_0BYTE_KEY = ByteArray(1) { 0 } + +@Suppress("UnusedReceiverParameter") @Throws(IllegalArgumentException::class) -@Suppress("NOTHING_TO_INLINE", "UnusedReceiverParameter") internal inline fun Mac.commonInit(algorithm: String) { require(algorithm.isNotBlank()) { "algorithm cannot be blank" } } -@Suppress("NOTHING_TO_INLINE") internal inline fun Mac.commonToString(): String { return "Mac[${algorithm()}]@${hashCode()}" } + +internal inline fun Mac.commonClearKey(engineReset: (ByteArray) -> Unit) { + try { + engineReset(SINGLE_0BYTE_KEY) + } catch (e1: IllegalArgumentException) { + try { + engineReset(ByteArray(macLength())) + } catch (e2: IllegalArgumentException) { + e2.addSuppressed(e1) + throw e2 + } + } +} diff --git a/library/mac/src/commonTest/kotlin/org/kotlincrypto/core/mac/MacUnitTest.kt b/library/mac/src/commonTest/kotlin/org/kotlincrypto/core/mac/MacUnitTest.kt index 51ad2de..9b3fbf2 100644 --- a/library/mac/src/commonTest/kotlin/org/kotlincrypto/core/mac/MacUnitTest.kt +++ b/library/mac/src/commonTest/kotlin/org/kotlincrypto/core/mac/MacUnitTest.kt @@ -80,4 +80,18 @@ class MacUnitTest { assertEquals(2, doFinalCount) assertEquals(2, resetCount) } + + @Test + fun givenMac_whenClearKey_thenSingle0ByteKeyPassedToEngine() { + var zeroKey: ByteArray? = null + + TestMac( + ByteArray(5), + "test rekey", + rekey = { zeroKey = it } + ).clearKey() + + assertNotNull(zeroKey) + assertContentEquals(ByteArray(1) { 0 }, zeroKey) + } } diff --git a/library/mac/src/jvmMain/kotlin/org/kotlincrypto/core/mac/Mac.kt b/library/mac/src/jvmMain/kotlin/org/kotlincrypto/core/mac/Mac.kt index c3d7a80..033114b 100644 --- a/library/mac/src/jvmMain/kotlin/org/kotlincrypto/core/mac/Mac.kt +++ b/library/mac/src/jvmMain/kotlin/org/kotlincrypto/core/mac/Mac.kt @@ -18,9 +18,7 @@ package org.kotlincrypto.core.mac import org.kotlincrypto.core.* -import org.kotlincrypto.core.mac.internal.AndroidApi21to23MacSpiProvider -import org.kotlincrypto.core.mac.internal.commonInit -import org.kotlincrypto.core.mac.internal.commonToString +import org.kotlincrypto.core.mac.internal.* import java.nio.ByteBuffer import java.security.InvalidKeyException import java.security.Key @@ -109,15 +107,23 @@ public actual abstract class Mac: javax.crypto.Mac, Algorithm, Copyable, Re /** * Resets the [Mac] and will reinitialize it with the provided key. * - * This is useful if wanting to clear the key before de-referencing. + * This is useful if wanting to zero out the key before de-referencing. * - * @throws [IllegalArgumentException] if [newKey] is empty. + * @see [clearKey] + * @throws [IllegalArgumentException] if [newKey] is empty, or of a length + * inappropriate for the [Mac] implementation. * */ public actual fun reset(newKey: ByteArray) { require(newKey.isNotEmpty()) { "newKey cannot be empty" } engine.reset(newKey) } + /** + * Helper function that will call [reset] with a blank key in order + * to zero it out. + * */ + public actual fun clearKey() { commonClearKey(engine::reset) } + /** * Core abstraction for powering a [Mac] implementation. Extends * Java's [MacSpi] for compatibility. @@ -157,8 +163,16 @@ public actual abstract class Mac: javax.crypto.Mac, Algorithm, Copyable, Re public actual override fun update(input: ByteArray) { update(input, 0, input.size) } /** - * Resets the [Engine] and will reinitialize it with the newly provided key. + * Resets the [Engine] and will reinitialize it with the provided key. + * + * **NOTE:** [newKey] is checked to be non-empty by the [Mac] abstraction + * before passing it here. Implementations should ensure any old key material + * is zeroed out. + * + * @throws [IllegalArgumentException] if [newKey] is a length inappropriate + * for the [Mac] implementation. * */ + @Throws(IllegalArgumentException::class) public actual abstract fun reset(newKey: ByteArray) // MacSpi diff --git a/library/mac/src/nonJvmMain/kotlin/org/kotlincrypto/core/mac/Mac.kt b/library/mac/src/nonJvmMain/kotlin/org/kotlincrypto/core/mac/Mac.kt index f5e117a..a1c5c9e 100644 --- a/library/mac/src/nonJvmMain/kotlin/org/kotlincrypto/core/mac/Mac.kt +++ b/library/mac/src/nonJvmMain/kotlin/org/kotlincrypto/core/mac/Mac.kt @@ -18,8 +18,7 @@ package org.kotlincrypto.core.mac import org.kotlincrypto.core.* -import org.kotlincrypto.core.mac.internal.commonInit -import org.kotlincrypto.core.mac.internal.commonToString +import org.kotlincrypto.core.mac.internal.* /** * Core abstraction for Message Authentication Code implementations. @@ -132,15 +131,23 @@ public actual abstract class Mac: Algorithm, Copyable, Resettable, Updatabl /** * Resets the [Mac] and will reinitialize it with the provided key. * - * This is useful if wanting to clear the key before de-referencing. + * This is useful if wanting to zero out the key before de-referencing. * - * @throws [IllegalArgumentException] if [newKey] is empty. + * @see [clearKey] + * @throws [IllegalArgumentException] if [newKey] is empty, or of a length + * inappropriate for the [Mac] implementation. * */ public actual fun reset(newKey: ByteArray) { require(newKey.isNotEmpty()) { "newKey cannot be empty" } engine.reset(newKey) } + /** + * Helper function that will call [reset] with a blank key in order + * to zero it out. + * */ + public actual fun clearKey() { commonClearKey(engine::reset) } + /** * Core abstraction for powering a [Mac] implementation. * @@ -179,8 +186,16 @@ public actual abstract class Mac: Algorithm, Copyable, Resettable, Updatabl public actual override fun update(input: ByteArray) { update(input, 0, input.size) } /** - * Resets the [Engine] and will reinitialize it with the newly provided key. + * Resets the [Engine] and will reinitialize it with the provided key. + * + * **NOTE:** [newKey] is checked to be non-empty by the [Mac] abstraction + * before passing it here. Implementations should ensure any old key material + * is zeroed out. + * + * @throws [IllegalArgumentException] if [newKey] is a length inappropriate + * for the [Mac] implementation. * */ + @Throws(IllegalArgumentException::class) public actual abstract fun reset(newKey: ByteArray) private val code = Any()