Skip to content

Commit

Permalink
CORDA-3175 Remove dependency on 3rd party javax.xml.bind library for …
Browse files Browse the repository at this point in the history
…simple hex parsing/printing. (corda#5412)

* Remove dependency on 2rd party javax.xml.bind library for simple hex parsing/printing.

* Added unit test.
  • Loading branch information
josecoll authored Aug 30, 2019
1 parent 6202165 commit 136600c
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 6 deletions.
52 changes: 49 additions & 3 deletions core/src/main/kotlin/net/corda/core/utilities/ByteArrays.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import java.io.OutputStream
import java.lang.Math.max
import java.lang.Math.min
import java.nio.ByteBuffer
import javax.xml.bind.DatatypeConverter

/**
* An abstraction of a byte array, with offset and size that does no copying of bytes unless asked to.
Expand Down Expand Up @@ -179,13 +178,60 @@ fun ByteArray.sequence(offset: Int = 0, size: Int = this.size) = ByteSequence.of
/**
* Converts this [ByteArray] into a [String] of hexadecimal digits.
*/
fun ByteArray.toHexString(): String = DatatypeConverter.printHexBinary(this)
fun ByteArray.toHexString(): String = printHexBinary(this)

private val hexCode = "0123456789ABCDEF".toCharArray()
private fun printHexBinary(data: ByteArray): String {
val r = StringBuilder(data.size * 2)
for (b in data) {
r.append(hexCode[(b.toInt() shr 4) and 0xF])
r.append(hexCode[b.toInt() and 0xF])
}
return r.toString()
}

/**
* Converts this [String] of hexadecimal digits into a [ByteArray].
* @throws IllegalArgumentException if the [String] contains incorrectly-encoded characters.
*/
fun String.parseAsHex(): ByteArray = DatatypeConverter.parseHexBinary(this)
fun String.parseAsHex(): ByteArray = parseHexBinary(this)

private fun parseHexBinary(s: String): ByteArray {
val len = s.length

// "111" is not a valid hex encoding.
if (len % 2 != 0) {
throw IllegalArgumentException("hexBinary needs to be even-length: $s")
}

val out = ByteArray(len / 2)

fun hexToBin(ch: Char): Int {
if (ch in '0'..'9') {
return ch - '0'
}
if (ch in 'A'..'F') {
return ch - 'A' + 10
}
return if (ch in 'a'..'f') {
ch - 'a' + 10
} else -1
}

var i = 0
while (i < len) {
val h = hexToBin(s[i])
val l = hexToBin(s[i + 1])
if (h == -1 || l == -1) {
throw IllegalArgumentException("contains illegal character for hexBinary: $s")
}

out[i / 2] = (h * 16 + l).toByte()
i += 2
}

return out
}

/**
* Class is public for serialization purposes.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import net.corda.core.internal.hash
import java.nio.charset.Charset
import java.security.PublicKey
import java.util.*
import javax.xml.bind.DatatypeConverter

// This file includes useful encoding methods and extension functions for the most common encoding/decoding operations.

Expand All @@ -31,7 +30,7 @@ fun ByteArray.toBase58(): String = Base58.encode(this)
fun ByteArray.toBase64(): String = Base64.getEncoder().encodeToString(this)

/** Convert a byte array to a hex (Base16) capitalized encoded [String]. */
fun ByteArray.toHex(): String = DatatypeConverter.printHexBinary(this)
fun ByteArray.toHex(): String = toHexString()

// [String] encoders and decoders

Expand All @@ -49,7 +48,7 @@ fun String.base58ToByteArray(): ByteArray = Base58.decode(this)
fun String.base64ToByteArray(): ByteArray = Base64.getDecoder().decode(this)

/** Hex-String to [ByteArray]. Accept any hex form (capitalized, lowercase, mixed). */
fun String.hexToByteArray(): ByteArray = DatatypeConverter.parseHexBinary(this)
fun String.hexToByteArray(): ByteArray = parseAsHex()

// Encoding changers

Expand Down
16 changes: 16 additions & 0 deletions core/src/test/kotlin/net/corda/core/utilities/ByteArraysTest.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package net.corda.core.utilities

import net.corda.core.contracts.StateRef
import net.corda.core.crypto.SecureHash
import net.corda.core.internal.declaredField
import org.assertj.core.api.Assertions.catchThrowable
import org.junit.Assert
import org.junit.Assert.assertSame
import org.junit.Test
import java.nio.ByteBuffer
Expand Down Expand Up @@ -42,4 +45,17 @@ class ByteArraysTest {
check(byteArrayOf(), seq.slice(2, 2))
check(byteArrayOf(), seq.slice(2, 1))
}

@Test
fun `test hex parsing strictly uppercase`() {
val HEX_REGEX = "^[0-9A-F]+\$".toRegex()

val privacySalt = net.corda.core.contracts.PrivacySalt()
val privacySaltAsHexString = privacySalt.bytes.toHexString()
Assert.assertTrue(privacySaltAsHexString.matches(HEX_REGEX))

val stateRef = StateRef(SecureHash.randomSHA256(), 0)
val txhashAsHexString = stateRef.txhash.bytes.toHexString()
Assert.assertTrue(txhashAsHexString.matches(HEX_REGEX))
}
}

0 comments on commit 136600c

Please sign in to comment.