From 1c456b55c264babe701825b6f76f9e8c83090764 Mon Sep 17 00:00:00 2001 From: Leon Lambert Date: Sun, 24 Apr 2022 16:37:47 +0800 Subject: [PATCH] feat(app): add ECC calculator --- app/build.gradle.kts | 2 +- app/src/main/kotlin/me/leon/Config.kt | 4 +- .../kotlin/me/leon/ext/crypto/ECCurves.kt | 46 ++++++++ .../kotlin/me/leon/view/BigIntFragment.kt | 1 + .../kotlin/me/leon/view/ECCurveCalculator.kt | 102 ++++++++++++++++++ .../test/kotlin/me/leon/asymmetric/Sm2Test.kt | 24 ++++- 6 files changed, 174 insertions(+), 5 deletions(-) create mode 100644 app/src/main/kotlin/me/leon/ext/crypto/ECCurves.kt create mode 100644 app/src/main/kotlin/me/leon/view/ECCurveCalculator.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 0fe13ee4e2..47c944ab5d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,5 +1,5 @@ group = "me.leon.tools" -version = "1.12.4.beta03" +version = "1.12.4.beta04" plugins { application diff --git a/app/src/main/kotlin/me/leon/Config.kt b/app/src/main/kotlin/me/leon/Config.kt index 89edf3fea1..6391b4ffa1 100644 --- a/app/src/main/kotlin/me/leon/Config.kt +++ b/app/src/main/kotlin/me/leon/Config.kt @@ -2,8 +2,8 @@ package me.leon import java.io.File -const val VERSION = "1.12.4.beta03" -const val BUILD_DATE = "2022-04-20" +const val VERSION = "1.12.4.beta04" +const val BUILD_DATE = "2022-04-24" const val REPO_URL = "https://github.com/Leon406/ToolsFx" const val REPO_ISSUE = "https://github.com/Leon406/ToolsFx/issues/new" const val PJ52_URL = "https://www.52pojie.cn/thread-1501153-1-1.html" diff --git a/app/src/main/kotlin/me/leon/ext/crypto/ECCurves.kt b/app/src/main/kotlin/me/leon/ext/crypto/ECCurves.kt new file mode 100644 index 0000000000..2b3792409b --- /dev/null +++ b/app/src/main/kotlin/me/leon/ext/crypto/ECCurves.kt @@ -0,0 +1,46 @@ +package me.leon.ext.crypto + +import java.math.BigInteger +import me.leon.ext.toHex +import org.bouncycastle.asn1.sec.SECNamedCurves +import org.bouncycastle.crypto.ec.CustomNamedCurves +import org.bouncycastle.math.ec.ECCurve + +val allCurves = + (CustomNamedCurves.getNames().toList().map { it.toString() } + + SECNamedCurves.getNames().toList().map { it.toString() }) + .distinct() + .sorted() + +val String.curve: ECCurve + get() = (CustomNamedCurves.getByName(this) ?: SECNamedCurves.getByName(this)).curve + +fun String.curveMultiply(x: BigInteger, y: BigInteger, k: BigInteger): Pair { + with(curve.createPoint(x, y).multiply(k).getEncoded(false).toHex()) { + return Pair(substring(2, 66), substring(66)) + } +} + +fun String.curveAdd( + x: BigInteger, + y: BigInteger, + x2: BigInteger, + y2: BigInteger +): Pair { + with(curve) { + val hex = createPoint(x, y).add(createPoint(x2, y2)).getEncoded(false).toHex() + return Pair(hex.substring(2, 66), hex.substring(66)) + } +} + +fun String.curveSubtract( + x: BigInteger, + y: BigInteger, + x2: BigInteger, + y2: BigInteger +): Pair { + with(curve) { + val hex = createPoint(x, y).subtract(createPoint(x2, y2)).getEncoded(false).toHex() + return Pair(hex.substring(2, 66), hex.substring(66)) + } +} diff --git a/app/src/main/kotlin/me/leon/view/BigIntFragment.kt b/app/src/main/kotlin/me/leon/view/BigIntFragment.kt index cc95478c6e..0bf8dd0958 100644 --- a/app/src/main/kotlin/me/leon/view/BigIntFragment.kt +++ b/app/src/main/kotlin/me/leon/view/BigIntFragment.kt @@ -98,6 +98,7 @@ class BigIntFragment : Fragment("BigInt") { enableWhen(!isProcessing) action { calculate() } } + button("ECC Calculator") { action { find().openWindow() } } } outputLayout(this) } diff --git a/app/src/main/kotlin/me/leon/view/ECCurveCalculator.kt b/app/src/main/kotlin/me/leon/view/ECCurveCalculator.kt new file mode 100644 index 0000000000..29d539e3b0 --- /dev/null +++ b/app/src/main/kotlin/me/leon/view/ECCurveCalculator.kt @@ -0,0 +1,102 @@ +package me.leon.view + +import javafx.beans.property.SimpleBooleanProperty +import javafx.beans.property.SimpleStringProperty +import javafx.geometry.Pos +import javafx.scene.control.RadioButton +import javafx.scene.control.TextField +import me.leon.ext.DEFAULT_SPACING +import me.leon.ext.cast +import me.leon.ext.crypto.* +import tornadofx.* + +class ECCurveCalculator : View("ECCurveCalculator") { + private val selectedCurve = SimpleStringProperty(allCurves.last()) + private val isShowY2 = SimpleBooleanProperty(false) + private var tfX1: TextField by singleAssign() + private var tfY1: TextField by singleAssign() + private var tfX2: TextField by singleAssign() + private var tfY2: TextField by singleAssign() + private var tfX: TextField by singleAssign() + private var tfY: TextField by singleAssign() + private var fPoint2: Field by singleAssign() + private var isMultiply = false + private var eccMethod = "multiply" + private var radix = 16 + + override val root = form { + fieldset { + field("x1:") { tfX1 = textfield() } + field("y1:") { tfY1 = textfield() } + } + fieldset { + fPoint2 = field("k:") { tfX2 = textfield() } + field("y2:") { + tfY2 = textfield() + visibleWhen(isShowY2) + } + } + + hbox { + alignment = Pos.CENTER + spacing = DEFAULT_SPACING + togglegroup { + radiobutton("add") + radiobutton("subtract") + radiobutton("multiply") { isSelected = true } + selectedToggleProperty().addListener { _, _, newValue -> + isMultiply = + newValue.cast().text.also { eccMethod = it } == "multiply" + fPoint2.text = if (isMultiply) "k:" else "y2:" + isShowY2.value = !isMultiply + } + } + combobox(selectedCurve, allCurves) + button(messages["run"]) { action { calculate() } } + } + fieldset { + field("x:") { tfX = textfield() } + field("y:") { tfY = textfield() } + } + } + + private fun calculate() { + runAsync { + run { + when (eccMethod) { + "add" -> + selectedCurve + .get() + .curveAdd( + tfX1.text.also { println(it) }.toBigInteger(radix), + tfY1.text.toBigInteger(radix), + tfX2.text.toBigInteger(radix), + tfY2.text.toBigInteger(radix) + ) + "subtract" -> + selectedCurve + .get() + .curveSubtract( + tfX1.text.toBigInteger(radix), + tfY1.text.toBigInteger(radix), + tfX2.text.toBigInteger(radix), + tfY2.text.toBigInteger(radix) + ) + "multiply" -> + selectedCurve + .get() + .curveMultiply( + tfX1.text.toBigInteger(radix), + tfY1.text.toBigInteger(radix), + tfX2.text.toBigInteger(radix) + ) + else -> throw IllegalArgumentException("Unknown method: $eccMethod") + } + } + } ui + { + tfX.text = it.first.uppercase() + tfY.text = it.second.uppercase() + } + } +} diff --git a/app/src/test/kotlin/me/leon/asymmetric/Sm2Test.kt b/app/src/test/kotlin/me/leon/asymmetric/Sm2Test.kt index 15a5475c67..73e07aa48e 100644 --- a/app/src/test/kotlin/me/leon/asymmetric/Sm2Test.kt +++ b/app/src/test/kotlin/me/leon/asymmetric/Sm2Test.kt @@ -4,13 +4,15 @@ import cn.hutool.core.util.StrUtil import cn.hutool.crypto.SecureUtil import cn.hutool.crypto.SmUtil import cn.hutool.crypto.asymmetric.KeyType -import java.security.* import me.leon.encode.base.base64 -import org.bouncycastle.crypto.params.* +import me.leon.ext.crypto.allCurves +import me.leon.ext.crypto.curveMultiply import org.junit.Test +import kotlin.test.assertEquals class Sm2Test { + // for check result @Test fun hutool() { val text = "我是一段测试aaaa" @@ -28,4 +30,22 @@ class Sm2Test { val decryptStr = StrUtil.utf8Str(sm2.decryptFromBcd(encryptStr, KeyType.PrivateKey)) println(decryptStr) } + + @Test + fun dotTimes() { + val dot1X = + "09F9DF311E5421A150DD7D161E4BC5C672179FAD1833FC076BB08FF356F35020".toBigInteger(16) + val dot1Y = + "CCEA490CE26775A52DC6EA718CC1AA600AED05FBF35E084A6632F6072DA9AD13".toBigInteger(16) + val k = "59276E27D506861A16680F3AD9C02DCCEF3CC1FA3CDBE4CE6D54B80DEAC1BC21".toBigInteger(16) + + val expectedX = "335e18d751e51f040e27d468138b7ab1dc86ad7f981d7d416222fd6ab3ed230d" + val expectedY = "ab743ebcfb22d64f7b6ab791f70658f25b48fa93e54064fdbfbed3f0bd847ac9" + println(allCurves.joinToString("\n")) + "sm2p256v1".curveMultiply(dot1X, dot1Y, k).also { + println(it) + assertEquals(expectedX, it.first) + assertEquals(expectedY, it.second) + } + } }