diff --git a/lib/src/main/kotlin/me/tb/cashuclient/Utilities.kt b/lib/src/main/kotlin/me/tb/cashuclient/Utilities.kt index 3bb7af0..b97c2dd 100644 --- a/lib/src/main/kotlin/me/tb/cashuclient/Utilities.kt +++ b/lib/src/main/kotlin/me/tb/cashuclient/Utilities.kt @@ -8,6 +8,8 @@ package me.tb.cashuclient import fr.acinq.bitcoin.PublicKey import fr.acinq.bitcoin.crypto.Digest import me.tb.cashuclient.types.SwapRequired +import java.nio.ByteBuffer +import java.nio.ByteOrder import java.security.SecureRandom import kotlin.math.pow @@ -33,19 +35,29 @@ public fun decomposeAmount(value: ULong): List { */ public fun hashToCurve(message: ByteArray): PublicKey { var point: PublicKey? = null - var msgToHash: ByteArray = message + val domainSeparator: ByteArray = "Secp256k1_HashToCurve_Cashu_".toByteArray() + val msgToHash: ByteArray = Digest.sha256().hash(domainSeparator + message) + var counter: UInt = 0u while (point == null || !point.isValid()) { - val hash: ByteArray = Digest.sha256().hash(msgToHash) + val counterBytes = ByteBuffer.allocate(Long.SIZE_BYTES) + .order(ByteOrder.LITTLE_ENDIAN) + .putLong(counter.toLong()) + .array() + .take(4) + .toByteArray() + + val hash: ByteArray = Digest.sha256().hash(msgToHash + counterBytes) // The point on the curve will always have an even y-coordinate (0x02 prefix) // For more information as to why this doesn't impact security, see: https://github.com/cashubtc/nuts/issues/24 point = PublicKey(byteArrayOf(0x02) + hash) - // If the hash of the message did not produce a valid point, we hash the hash and try again + // If the hash of the message did not produce a valid point, we bump the counter and try again if (!point.isValid()) { - msgToHash = hash + counter++ } + // TODO: If we've tried 2^32 times and still haven't found a valid point, we throw an error } return point } diff --git a/lib/src/test/kotlin/me/tb/cashuclient/NUTsTestVectors.kt b/lib/src/test/kotlin/me/tb/cashuclient/NUTsTestVectors.kt index 3f191ac..e3ddbf1 100644 --- a/lib/src/test/kotlin/me/tb/cashuclient/NUTsTestVectors.kt +++ b/lib/src/test/kotlin/me/tb/cashuclient/NUTsTestVectors.kt @@ -20,21 +20,21 @@ class NUTsTestVectors { val message1: ByteArray = Hex.decode("0000000000000000000000000000000000000000000000000000000000000000") val point1: PublicKey = hashToCurve(message1) assertEquals( - expected = "0266687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925", + expected = "024cce997d3b518f739663b757deaec95bcd9473c30a14ac2fd04023a739d1a725", actual = point1.toHex() ) val message2: ByteArray = Hex.decode("0000000000000000000000000000000000000000000000000000000000000001") val point2: PublicKey = hashToCurve(message2) assertEquals( - expected = "02ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5", + expected = "022e7158e11c9506f1aa4248bf531298daa7febd6194f003edcd9b93ade6253acf", actual = point2.toHex() ) val message3: ByteArray = Hex.decode("0000000000000000000000000000000000000000000000000000000000000002") val point3: PublicKey = hashToCurve(message3) assertEquals( - expected = "02076c988b353fcbb748178ecb286bc9d0b4acf474d4ba31ba62334e46c97c416a", + expected = "026cdbe15362df59cd1dd3c9c11de8aedac2106eca69236ecd9fbe117af897be4f", actual = point3.toHex() ) }