Skip to content

Commit 14e5458

Browse files
author
Denis Rekalov
authored
CORDA-4076: Fix SecureHash compatibility with previous versions (corda#6801)
1 parent d9f905c commit 14e5458

File tree

6 files changed

+66
-10
lines changed

6 files changed

+66
-10
lines changed

core/src/main/kotlin/net/corda/core/crypto/SecureHash.kt

+12-7
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,7 @@ import java.util.function.Supplier
2222
*/
2323
@KeepForDJVM
2424
@CordaSerializable
25-
sealed class SecureHash(val algorithm: String, bytes: ByteArray) : OpaqueBytes(bytes) {
26-
constructor(bytes: ByteArray) : this(SHA2_256, bytes)
27-
25+
sealed class SecureHash(bytes: ByteArray) : OpaqueBytes(bytes) {
2826
/** SHA-256 is part of the SHA-2 hash function family. Generated hash is fixed size, 256-bits (32-bytes). */
2927
class SHA256(bytes: ByteArray) : SecureHash(bytes) {
3028
init {
@@ -52,7 +50,7 @@ sealed class SecureHash(val algorithm: String, bytes: ByteArray) : OpaqueBytes(b
5250
}
5351
}
5452

55-
class HASH(algorithm: String, bytes: ByteArray) : SecureHash(algorithm, bytes) {
53+
class HASH(val algorithm: String, bytes: ByteArray) : SecureHash(bytes) {
5654
override fun equals(other: Any?): Boolean {
5755
return when {
5856
this === other -> true
@@ -63,16 +61,18 @@ sealed class SecureHash(val algorithm: String, bytes: ByteArray) : OpaqueBytes(b
6361

6462
override fun hashCode() = ByteBuffer.wrap(bytes).int
6563

64+
override fun toString(): String {
65+
return "$algorithm$DELIMITER${toHexString()}"
66+
}
67+
6668
override fun generate(data: ByteArray): SecureHash {
6769
return HASH(algorithm, digestAs(algorithm, data))
6870
}
6971
}
7072

7173
fun toHexString(): String = bytes.toHexString()
7274

73-
override fun toString(): String {
74-
return "$algorithm$DELIMITER${toHexString()}"
75-
}
75+
override fun toString(): String = bytes.toHexString()
7676

7777
/**
7878
* Returns the first [prefixLen] hexadecimal digits of the [SecureHash] value.
@@ -354,6 +354,11 @@ fun ByteArray.hashAs(algorithm: String): SecureHash = SecureHash.hashAs(algorith
354354
*/
355355
fun OpaqueBytes.hashAs(algorithm: String): SecureHash = SecureHash.hashAs(algorithm, bytes)
356356

357+
/**
358+
* Hash algorithm.
359+
*/
360+
val SecureHash.algorithm: String get() = if (this is SecureHash.HASH) algorithm else SecureHash.SHA2_256
361+
357362
/**
358363
* Hide the [FastThreadLocal] class behind a [Supplier] interface
359364
* so that we can remove it for core-deterministic.

core/src/main/kotlin/net/corda/core/internal/TransactionUtils.kt

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import net.corda.core.KeepForDJVM
44
import net.corda.core.contracts.*
55
import net.corda.core.crypto.DigestService
66
import net.corda.core.crypto.SecureHash
7+
import net.corda.core.crypto.algorithm
78
import net.corda.core.crypto.hashAs
89
import net.corda.core.crypto.internal.DigestAlgorithmFactory
910
import net.corda.core.flows.FlowLogic

core/src/test/kotlin/net/corda/core/crypto/Blake2s256DigestServiceTest.kt

+5-2
Original file line numberDiff line numberDiff line change
@@ -33,20 +33,23 @@ class Blake2s256DigestServiceTest {
3333
@Test(timeout = 300_000)
3434
fun testBlankHash() {
3535
assertEquals(
36-
"C59F682376D137F3F255E671E207D1F2374EBE504E9314208A52D9F88D69E8C8",
37-
service.hash(byteArrayOf()).toHexString()
36+
"BLAKE_TEST:C59F682376D137F3F255E671E207D1F2374EBE504E9314208A52D9F88D69E8C8",
37+
service.hash(byteArrayOf()).toString()
3838
)
39+
assertEquals("C59F682376D137F3F255E671E207D1F2374EBE504E9314208A52D9F88D69E8C8", service.hash(byteArrayOf()).toHexString())
3940
}
4041

4142
@Test(timeout = 300_000)
4243
fun testHashBytes() {
4344
val hash = service.hash(byteArrayOf(0x64, -0x13, 0x42, 0x3a))
45+
assertEquals("BLAKE_TEST:9EEA14092257E759ADAA56539A7A88DA1F68F03ABE3D9552A21D4731F4E6ECA0", hash.toString())
4446
assertEquals("9EEA14092257E759ADAA56539A7A88DA1F68F03ABE3D9552A21D4731F4E6ECA0", hash.toHexString())
4547
}
4648

4749
@Test(timeout = 300_000)
4850
fun testHashString() {
4951
val hash = service.hash("test")
52+
assertEquals("BLAKE_TEST:AB76E8F7EEA1968C183D343B756EC812E47D4BC7A3F061F4DDE8948B3E05DAF2", hash.toString())
5053
assertEquals("AB76E8F7EEA1968C183D343B756EC812E47D4BC7A3F061F4DDE8948B3E05DAF2", hash.toHexString())
5154
}
5255

node/src/main/kotlin/net/corda/notary/common/BatchSigning.kt

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import net.corda.core.crypto.SecureHash
77
import net.corda.core.crypto.SignableData
88
import net.corda.core.crypto.SignatureMetadata
99
import net.corda.core.crypto.TransactionSignature
10+
import net.corda.core.crypto.algorithm
1011
import net.corda.core.flows.NotaryError
1112
import net.corda.core.internal.digestService
1213
import net.corda.core.node.ServiceHub
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package net.corda.serialization.internal
2+
3+
import net.corda.core.crypto.SecureHash
4+
import net.corda.core.crypto.SecureHash.Companion.SHA2_256
5+
import net.corda.core.crypto.SecureHash.Companion.SHA2_512
6+
import net.corda.core.crypto.algorithm
7+
import net.corda.core.serialization.SerializationDefaults
8+
import net.corda.core.serialization.deserialize
9+
import net.corda.core.serialization.serialize
10+
import net.corda.testing.core.SerializationEnvironmentRule
11+
import org.junit.Assert.assertArrayEquals
12+
import org.junit.Rule
13+
import org.junit.Test
14+
import kotlin.test.assertEquals
15+
import kotlin.test.assertTrue
16+
17+
class SecureHashSerializationTest {
18+
@Rule
19+
@JvmField
20+
val testSerialization = SerializationEnvironmentRule()
21+
22+
@Test(timeout = 300_000)
23+
fun `serialize and deserialize SHA-256`() {
24+
val before = SecureHash.randomSHA256()
25+
val bytes = before.serialize(context = SerializationDefaults.P2P_CONTEXT.withoutReferences()).bytes
26+
val after = bytes.deserialize<SecureHash>()
27+
assertEquals(before, after)
28+
assertArrayEquals(before.bytes, after.bytes)
29+
assertEquals(before.algorithm, after.algorithm)
30+
assertEquals(after.algorithm, SHA2_256)
31+
assertTrue(after is SecureHash.SHA256)
32+
}
33+
34+
@Test(timeout = 300_000)
35+
fun `serialize and deserialize SHA-512`() {
36+
val before = SecureHash.random(SHA2_512)
37+
val bytes = before.serialize(context = SerializationDefaults.P2P_CONTEXT.withoutReferences()).bytes
38+
val after = bytes.deserialize<SecureHash>()
39+
assertEquals(before, after)
40+
assertArrayEquals(before.bytes, after.bytes)
41+
assertEquals(before.algorithm, after.algorithm)
42+
assertEquals(after.algorithm, SHA2_512)
43+
assertTrue(after is SecureHash.HASH)
44+
}
45+
}

tools/shell/src/main/java/net/corda/tools/shell/HashLookupShellCommand.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package net.corda.tools.shell;
22

33
import net.corda.core.crypto.SecureHash;
4+
import net.corda.core.crypto.SecureHashKt;
45
import net.corda.core.internal.VisibleForTesting;
56
import net.corda.core.messaging.CordaRPCOps;
67
import net.corda.core.messaging.StateMachineTransactionMapping;
@@ -60,7 +61,7 @@ protected static void hashLookup(PrintWriter out, CordaRPCOps proxy, String txId
6061
Optional<SecureHash> match = mapping.stream()
6162
.map(StateMachineTransactionMapping::getTransactionId)
6263
.filter(
63-
txId -> txId.equals(txIdHashParsed) || SecureHash.hashAs(txIdHashParsed.getAlgorithm(), txId.getBytes()).equals(txIdHashParsed)
64+
txId -> txId.equals(txIdHashParsed) || SecureHash.hashAs(SecureHashKt.getAlgorithm(txIdHashParsed), txId.getBytes()).equals(txIdHashParsed)
6465
)
6566
.findFirst();
6667

0 commit comments

Comments
 (0)