diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5398146f45..db3427969c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -103,7 +103,7 @@ jobs: key: ${{ runner.os }}-sbt-cache-v2-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }} - name: Runs tests and collect coverage - run: sbt -jvm-opts ci/ci.jvmopts ++${{ matrix.scala }} commonJS/test corelibJS/test interpreterJS/test graphirJS/test sdkJS/test + run: sbt -jvm-opts ci/ci.jvmopts ++${{ matrix.scala }} commonJS/test corelibJS/test interpreterJS/test graphirJS/test sdkJS/test scJS/test - name: Publish a JVM snapshot ${{ github.ref }} if: env.HAS_SECRETS == 'true' diff --git a/build.sbt b/build.sbt index 7053fd6555..371536017e 100644 --- a/build.sbt +++ b/build.sbt @@ -186,7 +186,7 @@ lazy val commonDependenies2 = libraryDependencies ++= Seq( "org.scala-lang.modules" %%% "scala-collection-compat" % "2.7.0" ) -val sigmajsCryptoFacadeVersion = "0.0.6" +val sigmajsCryptoFacadeVersion = "0.0.7" lazy val common = crossProject(JVMPlatform, JSPlatform) .in(file("common")) diff --git a/interpreter/js/src/main/scala/sigmastate/crypto/Platform.scala b/interpreter/js/src/main/scala/sigmastate/crypto/Platform.scala index 780b74f9a0..d23407e7d7 100644 --- a/interpreter/js/src/main/scala/sigmastate/crypto/Platform.scala +++ b/interpreter/js/src/main/scala/sigmastate/crypto/Platform.scala @@ -100,14 +100,23 @@ object Platform { def multiplyPoints(p1: Ecp, p2: Ecp): Ecp = new Ecp(CryptoFacadeJs.addPoint(p1.point, p2.point)) /** Exponentiate a point p. + * The implementation mimics the Java implementation of `AbstractECMultiplier.multiply` + * from BouncyCastle library. * * @param p point to exponentiate * @param n exponent * @return p to the power of n (`p^n`) i.e. `p + p + ... + p` (n times) */ def exponentiatePoint(p: Ecp, n: BigInteger): Ecp = { - val scalar = Convert.bigIntegerToBigInt(n) - new Ecp(CryptoFacadeJs.multiplyPoint(p.point, scalar)) + val sign = n.signum() + if (sign == 0 || isInfinityPoint(p)) { + val ctx = new CryptoContextJs() + return new Ecp(ctx.getInfinity()) + } + val scalar = Convert.bigIntegerToBigInt(n.abs()) + val positive = CryptoFacadeJs.multiplyPoint(p.point, scalar) + val result = if (sign > 0) positive else CryptoFacadeJs.negatePoint(positive) + new Ecp(result) } /** Check if a point is infinity. */ @@ -120,7 +129,7 @@ object Platform { class Curve // TODO JS: Use JS library for secure source of randomness - type SecureRandom = Random + type SecureRandom = sigmastate.crypto.SecureRandomJS /** Opaque point type. */ @js.native @@ -198,7 +207,7 @@ object Platform { } /** Create JS specific source of secure randomness. */ - def createSecureRandom(): Random = new Random() + def createSecureRandom(): SecureRandom = new SecureRandomJS /** Computes HMAC-SHA512 hash of the given data using the specified key. * diff --git a/interpreter/js/src/main/scala/sigmastate/crypto/SigmaJsCryptoFacade.scala b/interpreter/js/src/main/scala/sigmastate/crypto/SigmaJsCryptoFacade.scala index 3b19095b6a..fa60015ed2 100644 --- a/interpreter/js/src/main/scala/sigmastate/crypto/SigmaJsCryptoFacade.scala +++ b/interpreter/js/src/main/scala/sigmastate/crypto/SigmaJsCryptoFacade.scala @@ -1,5 +1,7 @@ package sigmastate.crypto +import debox.cfor + import scala.scalajs.js import scala.scalajs.js.annotation.JSImport import scala.scalajs.js.typedarray.Uint8Array @@ -109,6 +111,25 @@ object CryptoFacadeJs extends js.Object { def generatePbkdf2Key( normalizedMnemonic: String, normalizedPass: String): Uint8Array = js.native + + /** Creates a random array of bytes of the given length. */ + def getRandomBytes(length: Int): Uint8Array = js.native +} + +/** This class provides a cryptographically strong generator of byte arrays. */ +class SecureRandomJS { + /** + * Generates a user-specified number of random bytes. + * + * @param bytes the array to be filled in with random bytes. + */ + def nextBytes(bytes: Array[Byte]): Unit = { + val len = bytes.length + val arr = CryptoFacadeJs.getRandomBytes(len) + cfor(0)(_ < len, _ + 1) { i => + bytes(i) = arr(i).toByte + } + } } /** Represents imported Point class from `sigmajs-crypto-facade` JS libarary. */ diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/OracleExamplesSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/OracleExamplesSpecification.scala index 5dabe5114a..384f0d4b63 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/OracleExamplesSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/OracleExamplesSpecification.scala @@ -15,7 +15,7 @@ import sigmastate.helpers.TestingHelpers._ import org.ergoplatform._ import org.ergoplatform.dsl.{ContractSpec, SigmaContractSyntax, StdContracts, TestContractSpec} import sigmastate.basics.CryptoConstants -import sigmastate.crypto.CryptoFacade +import sigmastate.crypto.{BigIntegers, CryptoFacade} import sigmastate.eval.Extensions.ArrayOps import sigmastate.interpreter.Interpreter.{ScriptNameProp, emptyEnv} import sigmastate.utxo._ @@ -89,7 +89,8 @@ class OracleExamplesSpecification extends CompilerTestingCommons val temperature: Long = 18 - val r = BigInt.apply(128, CryptoFacade.createSecureRandom()) //128 bits random number + //create 128 bits random number + val r = BigInt(BigIntegers.createRandomBigInteger(128, CryptoFacade.createSecureRandom())) val a = group.exponentiate(group.generator, r.bigInteger) val ts = System.currentTimeMillis()