CORDA-4076: Fix SecureHash compatibility with previous versions (#6801)

This commit is contained in:
Denis Rekalov 2020-11-12 09:39:57 +00:00 committed by GitHub
parent d9f905cb81
commit 14e545826c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 66 additions and 10 deletions

View File

@ -22,9 +22,7 @@ import java.util.function.Supplier
*/
@KeepForDJVM
@CordaSerializable
sealed class SecureHash(val algorithm: String, bytes: ByteArray) : OpaqueBytes(bytes) {
constructor(bytes: ByteArray) : this(SHA2_256, bytes)
sealed class SecureHash(bytes: ByteArray) : OpaqueBytes(bytes) {
/** SHA-256 is part of the SHA-2 hash function family. Generated hash is fixed size, 256-bits (32-bytes). */
class SHA256(bytes: ByteArray) : SecureHash(bytes) {
init {
@ -52,7 +50,7 @@ sealed class SecureHash(val algorithm: String, bytes: ByteArray) : OpaqueBytes(b
}
}
class HASH(algorithm: String, bytes: ByteArray) : SecureHash(algorithm, bytes) {
class HASH(val algorithm: String, bytes: ByteArray) : SecureHash(bytes) {
override fun equals(other: Any?): Boolean {
return when {
this === other -> true
@ -63,6 +61,10 @@ sealed class SecureHash(val algorithm: String, bytes: ByteArray) : OpaqueBytes(b
override fun hashCode() = ByteBuffer.wrap(bytes).int
override fun toString(): String {
return "$algorithm$DELIMITER${toHexString()}"
}
override fun generate(data: ByteArray): SecureHash {
return HASH(algorithm, digestAs(algorithm, data))
}
@ -70,9 +72,7 @@ sealed class SecureHash(val algorithm: String, bytes: ByteArray) : OpaqueBytes(b
fun toHexString(): String = bytes.toHexString()
override fun toString(): String {
return "$algorithm$DELIMITER${toHexString()}"
}
override fun toString(): String = bytes.toHexString()
/**
* Returns the first [prefixLen] hexadecimal digits of the [SecureHash] value.
@ -354,6 +354,11 @@ fun ByteArray.hashAs(algorithm: String): SecureHash = SecureHash.hashAs(algorith
*/
fun OpaqueBytes.hashAs(algorithm: String): SecureHash = SecureHash.hashAs(algorithm, bytes)
/**
* Hash algorithm.
*/
val SecureHash.algorithm: String get() = if (this is SecureHash.HASH) algorithm else SecureHash.SHA2_256
/**
* Hide the [FastThreadLocal] class behind a [Supplier] interface
* so that we can remove it for core-deterministic.

View File

@ -4,6 +4,7 @@ import net.corda.core.KeepForDJVM
import net.corda.core.contracts.*
import net.corda.core.crypto.DigestService
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.algorithm
import net.corda.core.crypto.hashAs
import net.corda.core.crypto.internal.DigestAlgorithmFactory
import net.corda.core.flows.FlowLogic

View File

@ -33,20 +33,23 @@ class Blake2s256DigestServiceTest {
@Test(timeout = 300_000)
fun testBlankHash() {
assertEquals(
"C59F682376D137F3F255E671E207D1F2374EBE504E9314208A52D9F88D69E8C8",
service.hash(byteArrayOf()).toHexString()
"BLAKE_TEST:C59F682376D137F3F255E671E207D1F2374EBE504E9314208A52D9F88D69E8C8",
service.hash(byteArrayOf()).toString()
)
assertEquals("C59F682376D137F3F255E671E207D1F2374EBE504E9314208A52D9F88D69E8C8", service.hash(byteArrayOf()).toHexString())
}
@Test(timeout = 300_000)
fun testHashBytes() {
val hash = service.hash(byteArrayOf(0x64, -0x13, 0x42, 0x3a))
assertEquals("BLAKE_TEST:9EEA14092257E759ADAA56539A7A88DA1F68F03ABE3D9552A21D4731F4E6ECA0", hash.toString())
assertEquals("9EEA14092257E759ADAA56539A7A88DA1F68F03ABE3D9552A21D4731F4E6ECA0", hash.toHexString())
}
@Test(timeout = 300_000)
fun testHashString() {
val hash = service.hash("test")
assertEquals("BLAKE_TEST:AB76E8F7EEA1968C183D343B756EC812E47D4BC7A3F061F4DDE8948B3E05DAF2", hash.toString())
assertEquals("AB76E8F7EEA1968C183D343B756EC812E47D4BC7A3F061F4DDE8948B3E05DAF2", hash.toHexString())
}

View File

@ -7,6 +7,7 @@ import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.SignableData
import net.corda.core.crypto.SignatureMetadata
import net.corda.core.crypto.TransactionSignature
import net.corda.core.crypto.algorithm
import net.corda.core.flows.NotaryError
import net.corda.core.internal.digestService
import net.corda.core.node.ServiceHub

View File

@ -0,0 +1,45 @@
package net.corda.serialization.internal
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.SecureHash.Companion.SHA2_256
import net.corda.core.crypto.SecureHash.Companion.SHA2_512
import net.corda.core.crypto.algorithm
import net.corda.core.serialization.SerializationDefaults
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
import net.corda.testing.core.SerializationEnvironmentRule
import org.junit.Assert.assertArrayEquals
import org.junit.Rule
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class SecureHashSerializationTest {
@Rule
@JvmField
val testSerialization = SerializationEnvironmentRule()
@Test(timeout = 300_000)
fun `serialize and deserialize SHA-256`() {
val before = SecureHash.randomSHA256()
val bytes = before.serialize(context = SerializationDefaults.P2P_CONTEXT.withoutReferences()).bytes
val after = bytes.deserialize<SecureHash>()
assertEquals(before, after)
assertArrayEquals(before.bytes, after.bytes)
assertEquals(before.algorithm, after.algorithm)
assertEquals(after.algorithm, SHA2_256)
assertTrue(after is SecureHash.SHA256)
}
@Test(timeout = 300_000)
fun `serialize and deserialize SHA-512`() {
val before = SecureHash.random(SHA2_512)
val bytes = before.serialize(context = SerializationDefaults.P2P_CONTEXT.withoutReferences()).bytes
val after = bytes.deserialize<SecureHash>()
assertEquals(before, after)
assertArrayEquals(before.bytes, after.bytes)
assertEquals(before.algorithm, after.algorithm)
assertEquals(after.algorithm, SHA2_512)
assertTrue(after is SecureHash.HASH)
}
}

View File

@ -1,6 +1,7 @@
package net.corda.tools.shell;
import net.corda.core.crypto.SecureHash;
import net.corda.core.crypto.SecureHashKt;
import net.corda.core.internal.VisibleForTesting;
import net.corda.core.messaging.CordaRPCOps;
import net.corda.core.messaging.StateMachineTransactionMapping;
@ -60,7 +61,7 @@ public class HashLookupShellCommand extends CordaRpcOpsShellCommand {
Optional<SecureHash> match = mapping.stream()
.map(StateMachineTransactionMapping::getTransactionId)
.filter(
txId -> txId.equals(txIdHashParsed) || SecureHash.hashAs(txIdHashParsed.getAlgorithm(), txId.getBytes()).equals(txIdHashParsed)
txId -> txId.equals(txIdHashParsed) || SecureHash.hashAs(SecureHashKt.getAlgorithm(txIdHashParsed), txId.getBytes()).equals(txIdHashParsed)
)
.findFirst();