Skip to content

Commit fdea6b5

Browse files
authored
Throw exception when DigestState consumed more than once (#79)
1 parent ea9c226 commit fdea6b5

File tree

5 files changed

+67
-8
lines changed

5 files changed

+67
-8
lines changed

library/digest/src/commonMain/kotlin/org/kotlincrypto/core/digest/Digest.kt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ public expect abstract class Digest: Algorithm, Copyable<Digest>, Resettable, Up
7070
* }
7171
*
7272
* @see [DigestState]
73+
* @throws [IllegalStateException] If [DigestState] has already been used to instantiate
74+
* another instance of [Digest]
7375
* */
7476
protected constructor(state: DigestState)
7577

@@ -123,8 +125,12 @@ public expect abstract class Digest: Algorithm, Copyable<Digest>, Resettable, Up
123125
protected fun compressions(): Long
124126

125127
/**
126-
* Called by the public [copy] function which produces the
127-
* [DigestState] needed to create a wholly new instance.
128+
* Called by the public [copy] function which produces the [DigestState]
129+
* needed to create a wholly new instance.
130+
*
131+
* **NOTE:** [DigestState] can only be consumed once and should **NOT**
132+
* be held on to. Attempting to instantiate multiple [Digest] instances
133+
* with a single [DigestState] will raise an [IllegalStateException].
128134
* */
129135
protected abstract fun copy(state: DigestState): Digest
130136

library/digest/src/commonMain/kotlin/org/kotlincrypto/core/digest/internal/-Buffer.kt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,23 @@ internal value class Buffer private constructor(internal val value: ByteArray) {
5353
compressCount,
5454
compressCountMultiplier,
5555
) {
56-
val buf = Buffer(buf.value.copyOf())
56+
@Volatile
57+
var buf: Buffer? = Buffer(buf.value.copyOf())
5758
}
5859

5960
internal companion object {
6061

6162
@JvmSynthetic
62-
internal fun DigestState.buf(): Buffer = Buffer((this as State).buf.value.copyOf())
63+
@Throws(IllegalStateException::class)
64+
internal fun DigestState.buf(): Buffer {
65+
val state = this as State
66+
val buf = state.buf
67+
state.buf = null
68+
check(buf != null) {
69+
"DigestState cannot be consumed more than once. Call copy again."
70+
}
71+
return buf
72+
}
6373

6474
@JvmSynthetic
6575
@Throws(IllegalArgumentException::class)

library/digest/src/jvmMain/kotlin/org/kotlincrypto/core/digest/Digest.kt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ public actual abstract class Digest: MessageDigest, Algorithm, Cloneable, Copyab
8989
* }
9090
*
9191
* @see [DigestState]
92+
* @throws [IllegalStateException] If [DigestState] has already been used to instantiate
93+
* another instance of [Digest]
9294
* */
9395
protected actual constructor(state: DigestState): super(state.algorithm) {
9496
this.digestLength = state.digestLength
@@ -175,8 +177,12 @@ public actual abstract class Digest: MessageDigest, Algorithm, Cloneable, Copyab
175177
protected actual fun compressions(): Long = compressCount.commonCalculateCompressions(compressCountMultiplier)
176178

177179
/**
178-
* Called by the public [copy] function which produces the
179-
* [DigestState] needed to create a wholly new instance.
180+
* Called by the public [copy] function which produces the [DigestState]
181+
* needed to create a wholly new instance.
182+
*
183+
* **NOTE:** [DigestState] can only be consumed once and should **NOT**
184+
* be held on to. Attempting to instantiate multiple [Digest] instances
185+
* with a single [DigestState] will raise an [IllegalStateException].
180186
* */
181187
protected actual abstract fun copy(state: DigestState): Digest
182188

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright (c) 2025 Matthew Nelson
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
**/
16+
package org.kotlincrypto.core.digest.internal
17+
18+
import org.kotlincrypto.core.digest.internal.Buffer.Companion.buf
19+
import kotlin.test.Test
20+
import kotlin.test.assertFailsWith
21+
22+
class BufferUnitTest {
23+
24+
@Test
25+
fun givenBufferDigestState_whenAlreadyConsumed_thenFails() {
26+
val buf = Buffer.initialize("test", 8, 0)
27+
val state = buf.toState("test", 0, 0, 0, 0)
28+
state.buf()
29+
assertFailsWith<IllegalStateException> { state.buf() }
30+
}
31+
}

library/digest/src/nonJvmMain/kotlin/org/kotlincrypto/core/digest/Digest.kt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ public actual abstract class Digest: Algorithm, Copyable<Digest>, Resettable, Up
8787
* }
8888
*
8989
* @see [DigestState]
90+
* @throws [IllegalStateException] If [DigestState] has already been used to instantiate
91+
* another instance of [Digest]
9092
* */
9193
protected actual constructor(state: DigestState) {
9294
this.algorithm = state.algorithm
@@ -174,8 +176,12 @@ public actual abstract class Digest: Algorithm, Copyable<Digest>, Resettable, Up
174176
protected actual fun compressions(): Long = compressCount.commonCalculateCompressions(compressCountMultiplier)
175177

176178
/**
177-
* Called by the public [copy] function which produces the
178-
* [DigestState] needed to create a wholly new instance.
179+
* Called by the public [copy] function which produces the [DigestState]
180+
* needed to create a wholly new instance.
181+
*
182+
* **NOTE:** [DigestState] can only be consumed once and should **NOT**
183+
* be held on to. Attempting to instantiate multiple [Digest] instances
184+
* with a single [DigestState] will raise an [IllegalStateException].
179185
* */
180186
protected actual abstract fun copy(state: DigestState): Digest
181187

0 commit comments

Comments
 (0)