diff --git a/docs/GettingStarted.md b/docs/GettingStarted.md index d9fee95..dadcd32 100644 --- a/docs/GettingStarted.md +++ b/docs/GettingStarted.md @@ -74,6 +74,8 @@ You should find that the library jar file is placed into: ## Building the Verificatum library (optional) +While the C libraries in VEC, VECJ and GMP are optional, they are needed for good performance. + ### Installing or Building the GMP library 1. You can check to see if there is a pre-built gmp library available for your machine. @@ -83,11 +85,8 @@ version is probably fine. 3. Install into one of the library paths, usually /usr/lib. - ### Building the Verificatum Elliptic Curve library (VEC) -While the C libraries in VEC, VECJ and GMP are optional, they are needed for good performance. - ``` cd devhome git clone https://github.com/verificatum/verificatum-vec.git diff --git a/src/main/kotlin/org/cryptobiotic/eg/cli/RunShowSystem.kt b/src/main/kotlin/org/cryptobiotic/eg/cli/RunShowSystem.kt index c51b782..85740ec 100644 --- a/src/main/kotlin/org/cryptobiotic/eg/cli/RunShowSystem.kt +++ b/src/main/kotlin/org/cryptobiotic/eg/cli/RunShowSystem.kt @@ -59,14 +59,14 @@ class RunShowSystem { } if (showSet.has("hasVEC")) { - if (testVecDirectExp()) + if (testVecNative()) println("VECJ, VEC and GMP are installed") else println("VECJ, VEC and GMP are not installed") } } - fun testVecDirectExp(): Boolean { + fun testVecNative(): Boolean { try { val group = productionGroup("P-256") as EcGroupContext if (!(group.vecGroup is VecGroupNative)) { diff --git a/src/main/kotlin/org/cryptobiotic/eg/core/ecgroup/VecGroups.kt b/src/main/kotlin/org/cryptobiotic/eg/core/ecgroup/VecGroups.kt index 2164311..dd68965 100644 --- a/src/main/kotlin/org/cryptobiotic/eg/core/ecgroup/VecGroups.kt +++ b/src/main/kotlin/org/cryptobiotic/eg/core/ecgroup/VecGroups.kt @@ -81,7 +81,6 @@ class VecGroups( init { // NIST - NAMED_PARAMS["prime192v3"] = VecGroups( "fffffffffffffffffffffffffffffffeffffffffffffffff", "fffffffffffffffffffffffffffffffefffffffffffffffc", @@ -329,6 +328,13 @@ class VecGroups( return res } + var hasNativeLibrary = true + + fun hasNativeLibrary(): Boolean { + getEcGroup("P-256", true) + return hasNativeLibrary + } + // BTW, setting LD_LIBRARY_PATH to /usr/local/lib doesnt work when debugging, add to /usr/lib directly. // Ok when running from CLI, though. fun getEcGroup(name: String, useNative: Boolean = true): VecGroup { @@ -336,12 +342,13 @@ class VecGroups( if (params == null) { throw RuntimeException("Unknown named curve! ($name)") } else { - return if (useNative) { + return if (hasNativeLibrary && useNative) { val haveNative = try { VEC.getCurve(name) true } catch (t: Throwable) { logger.warn { "VECJ not installed, using non-native library" } + hasNativeLibrary = false false } if (haveNative) diff --git a/src/test/data/workflow/allAvailableEc.zip b/src/test/data/workflow/allAvailableEc.zip new file mode 100644 index 0000000..0adfae7 Binary files /dev/null and b/src/test/data/workflow/allAvailableEc.zip differ diff --git a/src/test/kotlin/org/cryptobiotic/eg/core/SchnorrTest.kt b/src/test/kotlin/org/cryptobiotic/eg/core/SchnorrTest.kt index c17f030..770d703 100644 --- a/src/test/kotlin/org/cryptobiotic/eg/core/SchnorrTest.kt +++ b/src/test/kotlin/org/cryptobiotic/eg/core/SchnorrTest.kt @@ -9,7 +9,6 @@ import io.kotest.property.Arb import io.kotest.property.arbitrary.int import io.kotest.property.checkAll import org.cryptobiotic.eg.core.ecgroup.EcGroupContext -import org.cryptobiotic.eg.core.productionGroup class SchnorrTest : WordSpec({ val intGroup = productionGroup() diff --git a/src/test/kotlin/org/cryptobiotic/eg/core/ecgroup/TestAgainstNative.kt b/src/test/kotlin/org/cryptobiotic/eg/core/ecgroup/TestAgainstNative.kt index d315b11..f041cda 100644 --- a/src/test/kotlin/org/cryptobiotic/eg/core/ecgroup/TestAgainstNative.kt +++ b/src/test/kotlin/org/cryptobiotic/eg/core/ecgroup/TestAgainstNative.kt @@ -1,7 +1,6 @@ package org.cryptobiotic.eg.core.ecgroup import org.cryptobiotic.eg.core.ElementModP -import java.math.BigInteger import kotlin.test.Ignore import kotlin.test.Test import kotlin.test.assertEquals @@ -11,23 +10,25 @@ class TestAgainstNative { @Test fun testSqrt3() { - val group = EcGroupContext("P-256", false) - val vecGroup = group.vecGroup - val vecGroupN = EcGroupContext("P-256", true).vecGroup - assertTrue(vecGroupN is VecGroupNative) + if (VecGroups.hasNativeLibrary()) { + val group = EcGroupContext("P-256", false) + val vecGroup = group.vecGroup + val vecGroupN = EcGroupContext("P-256", true).vecGroup + assertTrue(vecGroupN is VecGroupNative) - // randomElementModP seems to always be the case when p = 3 mod 4 - repeat(100) { - val elemP = group.randomElementModP(2).ec - val elemPy2 = vecGroupN.equationf(elemP.x) + // randomElementModP seems to always be the case when p = 3 mod 4 + repeat(100) { + val elemP = group.randomElementModP(2).ec + val elemPy2 = vecGroupN.equationf(elemP.x) - val elemPy = vecGroup.sqrt(elemPy2) - assertEquals(elemP.y, elemPy) + val elemPy = vecGroup.sqrt(elemPy2) + assertEquals(elemP.y, elemPy) - val elemPyt = vecGroupN.sqrt(elemPy2) - assertEquals(elemP.y, elemPyt) + val elemPyt = vecGroupN.sqrt(elemPy2) + assertEquals(elemP.y, elemPyt) - assertEquals(elemPy, elemPyt) + assertEquals(elemPy, elemPyt) + } } } @@ -55,16 +56,18 @@ class TestAgainstNative { @Test fun testProdPowers() { - val group = EcGroupContext("P-256", false) - val groupN = EcGroupContext("P-256", true) - assertTrue(groupN.vecGroup is VecGroupNative) + if (VecGroups.hasNativeLibrary()) { + val group = EcGroupContext("P-256", false) + val groupN = EcGroupContext("P-256", true) + assertTrue(groupN.vecGroup is VecGroupNative) - val n = 100 - val bases = List(n) { group.randomElementModP() } - val nonces = List(n) { group.randomElementModQ() } - val prodpow: ElementModP = group.prodPowers(bases, nonces) - val prodpowN: ElementModP = groupN.prodPowers(bases, nonces) - assertEquals(prodpow, prodpowN) + val n = 100 + val bases = List(n) { group.randomElementModP() } + val nonces = List(n) { group.randomElementModQ() } + val prodpow: ElementModP = group.prodPowers(bases, nonces) + val prodpowN: ElementModP = groupN.prodPowers(bases, nonces) + assertEquals(prodpow, prodpowN) + } } } \ No newline at end of file diff --git a/src/test/kotlin/org/cryptobiotic/eg/core/ecgroup/TestElem.kt b/src/test/kotlin/org/cryptobiotic/eg/core/ecgroup/TestElem.kt index 601b0f2..f77b1fa 100644 --- a/src/test/kotlin/org/cryptobiotic/eg/core/ecgroup/TestElem.kt +++ b/src/test/kotlin/org/cryptobiotic/eg/core/ecgroup/TestElem.kt @@ -114,19 +114,21 @@ class TestElem { @Test fun testSqrt() { - val group = productionGroup("P-256") as EcGroupContext - val vecGroupN = group.vecGroup as VecGroupNative + if (VecGroups.hasNativeLibrary()) { + val group = productionGroup("P-256") as EcGroupContext + val vecGroupN = group.vecGroup as VecGroupNative - repeat (100) { - val elemP = group.randomElementModP(2).ec - val elemPx = elemP.x - val elemPy2 = vecGroupN.equationf(elemPx) + repeat(100) { + val elemP = group.randomElementModP(2).ec + val elemPx = elemP.x + val elemPy2 = vecGroupN.equationf(elemPx) - val elemPy = vecGroupN.sqrt(elemPy2) - assertEquals(elemP.y, elemPy) + val elemPy = vecGroupN.sqrt(elemPy2) + assertEquals(elemP.y, elemPy) - val elemPyt = vecGroupN.sqrt(elemPy2) - assertEquals(elemP.y, elemPyt) + val elemPyt = vecGroupN.sqrt(elemPy2) + assertEquals(elemP.y, elemPyt) + } } } diff --git a/src/test/kotlin/org/cryptobiotic/eg/core/ecgroup/TestSerialize.kt b/src/test/kotlin/org/cryptobiotic/eg/core/ecgroup/TestSerialize.kt index 201b9ee..07e790b 100644 --- a/src/test/kotlin/org/cryptobiotic/eg/core/ecgroup/TestSerialize.kt +++ b/src/test/kotlin/org/cryptobiotic/eg/core/ecgroup/TestSerialize.kt @@ -19,9 +19,11 @@ class TestSerialize { checkAll( elementsModP(group), ) { p -> - assertTrue (p is EcElementModP) + assertTrue(p is EcElementModP) val ec = (p as EcElementModP).ec - assertTrue (ec is VecElementPnative) + if (VecGroups.hasNativeLibrary()) { + assertTrue(ec is VecElementPnative) + } } } } @@ -56,15 +58,15 @@ class TestSerialize { ) { p -> val ec = (p as EcElementModP).ec val vecGroup = ec.pGroup - val convert2 : VecElementP? = vecGroup.elementFromByteArray2(ec.toByteArray2()) + val convert2: VecElementP? = vecGroup.elementFromByteArray2(ec.toByteArray2()) assertNotNull(convert2) assertEquals(ec, convert2) - val convert12 : VecElementP? = vecGroup.elementFromByteArray1from2(ec.toByteArray2()) + val convert12: VecElementP? = vecGroup.elementFromByteArray1from2(ec.toByteArray2()) assertNotNull(convert12) assertEquals(ec, convert12) - val convert1 : VecElementP? = vecGroup.elementFromByteArray1(ec.toByteArray1()) + val convert1: VecElementP? = vecGroup.elementFromByteArray1(ec.toByteArray1()) assertNotNull(convert1) assertEquals(ec, convert1) } diff --git a/src/test/kotlin/org/cryptobiotic/eg/publish/TestZippedJson.kt b/src/test/kotlin/org/cryptobiotic/eg/publish/TestZippedJson.kt index c4feb34..e986cac 100644 --- a/src/test/kotlin/org/cryptobiotic/eg/publish/TestZippedJson.kt +++ b/src/test/kotlin/org/cryptobiotic/eg/publish/TestZippedJson.kt @@ -19,16 +19,15 @@ import kotlin.test.assertNotNull import org.cryptobiotic.eg.publish.json.* import org.cryptobiotic.util.ErrorMessages -import org.cryptobiotic.util.Testing -// run verifier on zipped JSON record, only supported on JVM +// run verifier on zipped JSON record @OptIn(ExperimentalSerializationApi::class) class TestZippedJson { val jsonReader = Json { explicitNulls = false; ignoreUnknownKeys = true; prettyPrint = true } val inputDir = "src/test/data/workflow/allAvailableEc" - val zippedJson = "${Testing.testOut}/allAvailableJson.zip" + val zippedJson = "src/test/data/workflow/allAvailableEc.zip" val fs: FileSystem val fsp: FileSystemProvider diff --git a/src/test/kotlin/org/cryptobiotic/gmp/VecTest.kt b/src/test/kotlin/org/cryptobiotic/gmp/VecTest.kt index 2bb5e39..ec7e2e8 100644 --- a/src/test/kotlin/org/cryptobiotic/gmp/VecTest.kt +++ b/src/test/kotlin/org/cryptobiotic/gmp/VecTest.kt @@ -20,20 +20,23 @@ class VecTest { } @Test - fun testIfLoaded() { - val names = VEC.getCurveNames() - names.forEach{ + fun testCurveNames() { + VecGroups.curveNames.forEach { println(it) } } @Test fun testVecParams() { - val names = arrayOf("modulus","a","b<","gx","gy","n") - val curve_ptr: ByteArray = VEC.getCurve("P-256") - val params : Array = VEC.getCurveParams(curve_ptr) - params.forEachIndexed{ idx, it -> - println("${names[idx]} = ${it.toString(16)}") + try { + val names = arrayOf("modulus", "a", "b", "gx", "gy", "n") + val curve_ptr: ByteArray = VEC.getCurve("P-256") + val params: Array = VEC.getCurveParams(curve_ptr) + params.forEachIndexed { idx, it -> + println("${names[idx]} = ${it.toString(16)}") + } + } catch (t: Throwable) { + println("VEC not installed") } } @@ -45,11 +48,11 @@ class VecTest { timeVecJavaExp(group, 100) } - fun timeVecJavaExp(group: GroupContext, n:Int) { + fun timeVecJavaExp(group: GroupContext, n: Int) { val nonces = List(n) { group.randomElementModQ() } val h = group.gPowP(group.randomElementModQ()) - var stopwatch = Stopwatch() + val stopwatch = Stopwatch() repeat(n) { h powP nonces[it] } println("timeVecJavaExp ${stopwatch.tookPer(n, "exps")}") } @@ -62,62 +65,64 @@ class VecTest { timeVecExp(group, 1000) } - fun timeVecExp(group: GroupContext, n:Int) { + fun timeVecExp(group: GroupContext, n: Int) { val nonces = List(n) { group.randomElementModQ() } val h = group.gPowP(group.randomElementModQ()) - var stopwatch = Stopwatch() + val stopwatch = Stopwatch() repeat(n) { h powP nonces[it] } - println("timeVecExp ${stopwatch.tookPer(n, "exps")}") + println("timeVecExp ${stopwatch.tookPer(n, "exps")} native = ${VecGroups.hasNativeLibrary()}") } @Test // time using VEC directly fun testVecDirectExp() { - val group = productionGroup("P-256") as EcGroupContext - val namedCurve: ByteArray = VEC.getCurve("P-256") - testVecDirectExp(group, namedCurve, 100) - testVecDirectExp(group, namedCurve, 100) + if (VecGroups.hasNativeLibrary()) { + val group = productionGroup("P-256") as EcGroupContext + val namedCurve: ByteArray = VEC.getCurve("P-256") + testVecDirectExp(group, namedCurve, 100) + testVecDirectExp(group, namedCurve, 100) + } } - fun testVecDirectExp(group: EcGroupContext, curvePtr: ByteArray, n:Int) { - val nonces = List(n) { group.randomElementModQ() } - val h = group.gPowP(group.randomElementModQ()) as EcElementModP - val hx = h.ec.x - val hy = h.ec.y - - // public static BigInteger[] mul(final byte[] curve_ptr, - // final BigInteger x, - // final BigInteger y, - // final BigInteger scalar) { - var stopwatch = Stopwatch() - nonces.forEach { - val scalar = (it as EcElementModQ).element - val result: Array = VEC.mul(curvePtr, hx, hy, scalar) - val resultElem = EcElementModP(group, VecElementP(group.vecGroup, result[0], result[1])) + fun testVecDirectExp(group: EcGroupContext, curvePtr: ByteArray, n: Int) { + if (VecGroups.hasNativeLibrary()) { + val nonces = List(n) { group.randomElementModQ() } + val h = group.gPowP(group.randomElementModQ()) as EcElementModP + val hx = h.ec.x + val hy = h.ec.y + + val stopwatch = Stopwatch() + nonces.forEach { + val scalar = (it as EcElementModQ).element + val result: Array = VEC.mul(curvePtr, hx, hy, scalar) + EcElementModP(group, VecElementP(group.vecGroup, result[0], result[1])) + } + println("testVecDirectExp ${stopwatch.tookPer(n, "VEC.mul")}") } - println("testVecDirectExp ${stopwatch.tookPer(n, "VEC.mul")}") } @Test // test java vs native agreement fun testAgreePowp() { - val group = productionGroup("P-256", false) - val groupN = productionGroup("P-256", true) - val n = 100 - val nonces = List(n) { group.randomElementModQ() } - val rexp = group.randomElementModQ() - val h = group.gPowP(rexp) - val hn = groupN.gPowP(rexp) - - assertTrue(h is EcElementModP) - assertTrue(hn is EcElementModP) - assertTrue((h as EcElementModP).ec is VecElementP) - assertTrue((hn as EcElementModP).ec is VecElementPnative) - - val prodpow : ElementModP = nonces.map { h powP it }.reduce{ a, b -> a * b } - val prodpowN : ElementModP = nonces.map { hn powP it }.reduce{ a, b -> a * b } - assertTrue (prodpow.byteArray().contentEquals(prodpowN.byteArray())) + if (VecGroups.hasNativeLibrary()) { + val group = productionGroup("P-256", false) + val groupN = productionGroup("P-256", true) + val n = 100 + val nonces = List(n) { group.randomElementModQ() } + val rexp = group.randomElementModQ() + val h = group.gPowP(rexp) + val hn = groupN.gPowP(rexp) + + assertTrue(h is EcElementModP) + assertTrue(hn is EcElementModP) + assertTrue((h as EcElementModP).ec is VecElementP) + assertTrue((hn as EcElementModP).ec is VecElementPnative) + + val prodpow: ElementModP = nonces.map { h powP it }.reduce { a, b -> a * b } + val prodpowN: ElementModP = nonces.map { hn powP it }.reduce { a, b -> a * b } + assertTrue(prodpow.byteArray().contentEquals(prodpowN.byteArray())) + } } @Test @@ -127,8 +132,8 @@ class VecTest { val bases = List(n) { groupN.randomElementModP() } val nonces = List(n) { groupN.randomElementModQ() } - var stopwatch = Stopwatch() - val prodpow : ElementModP = groupN.prodPowers(bases, nonces) + val stopwatch = Stopwatch() + val prodpow: ElementModP = groupN.prodPowers(bases, nonces) println("testProdPowers ${stopwatch.tookPer(n, "exps")}") } @@ -139,14 +144,14 @@ class VecTest { val bases = List(n) { groupN.randomElementModP() } val nonces = List(n) { groupN.randomElementModQ() } - var stopwatch = Stopwatch() - val prodPowers : ElementModP = groupN.prodPowers(bases, nonces) + val stopwatch = Stopwatch() + val prodPowers: ElementModP = groupN.prodPowers(bases, nonces) val prodPowerTime = stopwatch.stop() //println("prodPowerTime ${Stopwatch.perRow(prodPowerTime, n)}") // call for each exp separately but use native stopwatch.start() - val pows = List( nonces.size) { bases[it].powP(nonces[it]) } + val pows = List(nonces.size) { bases[it].powP(nonces[it]) } val prodPow = pows.reduce { a, b -> (a * b) } val prodPowTime = stopwatch.stop() //println("prodPowTime ${Stopwatch.perRow(prodPowTime, n)}")