diff --git a/build.sbt b/build.sbt
index 2a4c9da3a6..7fd9727c9a 100644
--- a/build.sbt
+++ b/build.sbt
@@ -17,12 +17,12 @@ lazy val commonSettings = Seq(
scalaVersion := scala212,
scalacOptions ++= {
CrossVersion.partialVersion(scalaVersion.value) match {
- case Some((2, 13)) => Seq("-Ywarn-unused:_,imports", "-Ywarn-unused:imports")
- case Some((2, 12)) => Seq("-Ywarn-unused:_,imports", "-Ywarn-unused:imports")
+ case Some((2, 13)) => Seq("-Ywarn-unused:_,imports", "-Ywarn-unused:imports", "-release", "8")
+ case Some((2, 12)) => Seq("-Ywarn-unused:_,imports", "-Ywarn-unused:imports", "-release", "8")
case Some((2, 11)) => Seq()
case _ => sys.error("Unsupported scala version")
}
- } ++ scalacReleaseOption,
+ },
javacOptions ++= javacReleaseOption,
resolvers += Resolver.sonatypeRepo("public"),
licenses := Seq("CC0" -> url("https://creativecommons.org/publicdomain/zero/1.0/legalcode")),
@@ -56,14 +56,6 @@ lazy val commonSettings = Seq(
),
)
-def scalacReleaseOption = {
- if (System.getProperty("java.version").startsWith("1."))
- // java <9 "-release" is not supported
- Seq()
- else
- Seq("-release", "8") // this is passed to javac as `javac -release 8`
-}
-
def javacReleaseOption = {
if (System.getProperty("java.version").startsWith("1."))
// java <9 "--release" is not supported
diff --git a/sigma-api/src/main/scala/special/sigma/SigmaDsl.scala b/sigma-api/src/main/scala/special/sigma/SigmaDsl.scala
index 76c283c3e6..38ef31700c 100644
--- a/sigma-api/src/main/scala/special/sigma/SigmaDsl.scala
+++ b/sigma-api/src/main/scala/special/sigma/SigmaDsl.scala
@@ -2,7 +2,6 @@ package special.sigma
import java.math.BigInteger
-import org.bouncycastle.math.ec.ECPoint
import special.collection._
import scalan._
import scorex.crypto.authds.{ADDigest, ADValue}
@@ -200,7 +199,8 @@ trait BigInt {
@scalan.Liftable
@WithMethodCallRecognizers
trait GroupElement {
- def isInfinity: Boolean
+ /** Checks if the provided element is an identity element. */
+ def isIdentity: Boolean
/** Exponentiate this GroupElement
to the given number.
* @param k The power.
diff --git a/sigma-api/src/main/scala/special/sigma/package.scala b/sigma-api/src/main/scala/special/sigma/package.scala
index b02b187ea5..dcbb71ef64 100644
--- a/sigma-api/src/main/scala/special/sigma/package.scala
+++ b/sigma-api/src/main/scala/special/sigma/package.scala
@@ -2,7 +2,6 @@ package special
import java.math.BigInteger
-import org.bouncycastle.math.ec.ECPoint
import scalan.RType
import scalan.RType.GeneralType
@@ -30,5 +29,4 @@ package object sigma {
implicit val SigmaDslBuilderRType: RType[SigmaDslBuilder] = RType.fromClassTag(classTag[SigmaDslBuilder])
implicit val BigIntegerRType: RType[BigInteger] = GeneralType(classTag[BigInteger])
- implicit val ECPointRType: RType[ECPoint] = GeneralType(classTag[ECPoint])
}
\ No newline at end of file
diff --git a/sigmastate/src/main/scala/org/ergoplatform/ErgoAddress.scala b/sigmastate/src/main/scala/org/ergoplatform/ErgoAddress.scala
index 0a9fdfb49c..1a2b5b2878 100644
--- a/sigmastate/src/main/scala/org/ergoplatform/ErgoAddress.scala
+++ b/sigmastate/src/main/scala/org/ergoplatform/ErgoAddress.scala
@@ -123,7 +123,7 @@ object P2PKAddress {
/** Constructs [[P2PKAddress]] instance using the public key of the given [[ProveDlog]]. */
def apply(pubkey: ProveDlog)(implicit encoder: ErgoAddressEncoder): P2PKAddress = {
- val bs = GroupElementSerializer.toBytes(pubkey.h)
+ val bs = GroupElementSerializer.toBytes(pubkey.value)
new P2PKAddress(pubkey, bs)
}
}
diff --git a/sigmastate/src/main/scala/sigmastate/SigSerializer.scala b/sigmastate/src/main/scala/sigmastate/SigSerializer.scala
index d7f3875337..045274e75b 100644
--- a/sigmastate/src/main/scala/sigmastate/SigSerializer.scala
+++ b/sigmastate/src/main/scala/sigmastate/SigSerializer.scala
@@ -2,7 +2,7 @@ package sigmastate
import com.typesafe.scalalogging.LazyLogging
import gf2t.GF2_192_Poly
-import org.bouncycastle.util.BigIntegers
+import sigmastate.crypto.BigIntegers
import scorex.util.encode.Base16
import sigmastate.Values.SigmaBoolean
import sigmastate.basics.DLogProtocol.{ProveDlog, SecondDLogProverMessage}
diff --git a/sigmastate/src/main/scala/sigmastate/basics/BcDlogGroup.scala b/sigmastate/src/main/scala/sigmastate/basics/BcDlogGroup.scala
index 2fb7583a67..f4b9b4c8f3 100644
--- a/sigmastate/src/main/scala/sigmastate/basics/BcDlogGroup.scala
+++ b/sigmastate/src/main/scala/sigmastate/basics/BcDlogGroup.scala
@@ -1,28 +1,29 @@
package sigmastate.basics
import java.math.BigInteger
-import org.bouncycastle.asn1.x9.X9ECParameters
-import org.bouncycastle.crypto.ec.CustomNamedCurves
-import org.bouncycastle.math.ec.custom.sec.SecP256K1Point
-import org.bouncycastle.math.ec.ECPoint
-import org.bouncycastle.util.BigIntegers
+import sigmastate.crypto.BigIntegers
import debox.cfor
+import sigmastate.crypto.{CryptoContext, CryptoFacade}
-import scala.collection.{Seq, mutable}
-import scala.util.Try
+import scala.collection.mutable
-abstract class BcDlogGroup[ElemType <: ECPoint](val x9params: X9ECParameters) extends DlogGroup[ElemType] {
- lazy val curve = x9params.getCurve
+/** Base class for EC-based groups where DLOG problem is hard (with bouncycastle-like interface).
+ * @param ctx context which abstracts basic operations with curve and elements.
+ */
+abstract class BcDlogGroup(val ctx: CryptoContext) extends DlogGroup {
+ /** Characteristic of the finite field of the underlying curve. */
+ lazy val p: BigInteger = ctx.fieldCharacteristic
- //modulus of the field
- lazy val p: BigInteger = curve.getField.getCharacteristic
-
- //order of the group
- lazy val q = x9params.getN
+ /** Order of the group as defined in ASN.1 def for Elliptic-Curve ECParameters structure.
+ * See X9.62, for further details.
+ * For reference implementation see `org.bouncycastle.asn1.x9.X9ECParameters.getN`.
+ */
+ lazy val q: BigInteger = ctx.order
- //Now that we have p, we can calculate k which is the maximum length in bytes
- // of a string to be converted to a Group Element of this group.
+ /** Now that we have p, we can calculate k which is the maximum length in bytes
+ * of a string to be converted to a Group Element of this group.
+ */
lazy val k = calcK(p)
/**
@@ -31,15 +32,15 @@ abstract class BcDlogGroup[ElemType <: ECPoint](val x9params: X9ECParameters) ex
* It is composed of two main elements. The group element for which the optimized computations
* are built for, called the base and a vector of group elements that are the result of
* exponentiations of order 1,2,4,8,
- */
- private class GroupElementsExponentiations(base: ElemType) //group element for which the optimized computations are built for
- /**
+ *
* The constructor creates a map structure in memory.
* Then calculates the exponentiations of order 1,2,4,8 for the given base and save them in the map.
*
- * @param base
+ * @param base group element for which the optimized computations are built for
* @throws IllegalArgumentException
- */ { // build new vector of exponentiations
+ *
+ */
+ private class GroupElementsExponentiations(base: ElemType) {
private val exponentiations = new mutable.ListBuffer[ElemType]()
@@ -99,22 +100,11 @@ abstract class BcDlogGroup[ElemType <: ECPoint](val x9params: X9ECParameters) ex
private val exponentiationsCache = mutable.Map[ElemType, GroupElementsExponentiations]()
- //Create the generator
- //Assume that (x,y) are the coordinates of a point that is indeed a generator but check that (x,y) are the coordinates of a point.
- override lazy val generator: ElemType = x9params.getG.asInstanceOf[ElemType]
-
- /**
- * Checks if the given x and y represent a valid point on the given curve,
- * i.e. if the point (x, y) is a solution of the curves equation.
- *
- * @param x coefficient of the point
- * @param y coefficient of the point
- * @return true if the given x and y represented a valid point on the given curve
+ /** Creates the generator.
+ * Assume that (x,y) are the coordinates of a point that is indeed a generator but
+ * check that (x,y) are the coordinates of a point.
*/
- def checkCurveMembership(x: BigInteger, y: BigInteger): Boolean = {
- Try(curve.validatePoint(x, y)).isSuccess
- }
-
+ override lazy val generator: ElemType = ctx.generator
/**
* This function calculates k, the maximum length in bytes of a string to be converted to a Group Element of this group.
@@ -132,16 +122,14 @@ abstract class BcDlogGroup[ElemType <: ECPoint](val x9params: X9ECParameters) ex
}
/**
- *
* @return the order of this Dlog group
*/
- override lazy val order: BigInteger = x9params.getN
+ override lazy val order: BigInteger = ctx.order
/**
- *
* @return the identity of this Dlog group
*/
- override lazy val identity: ElemType = curve.getInfinity.asInstanceOf[ElemType]
+ override lazy val identity: ElemType = ctx.infinity.asInstanceOf[ElemType]
/**
* Calculates the inverse of the given GroupElement.
@@ -151,7 +139,7 @@ abstract class BcDlogGroup[ElemType <: ECPoint](val x9params: X9ECParameters) ex
* @throws IllegalArgumentException
**/
override def inverseOf(groupElement: ElemType): ElemType =
- groupElement.negate().asInstanceOf[ElemType]
+ CryptoFacade.negatePoint(groupElement)
/**
* Raises the base GroupElement to the exponent. The result is another GroupElement.
@@ -163,16 +151,12 @@ abstract class BcDlogGroup[ElemType <: ECPoint](val x9params: X9ECParameters) ex
*/
override def exponentiate(base: ElemType, exponent: BigInteger): ElemType = {
//infinity remains the same after any exponentiate
- if (base.isInfinity) return base
+ if (CryptoFacade.isInfinityPoint(base)) return base
//If the exponent is negative, convert it to be the exponent modulus q.
val exp = if (exponent.compareTo(BigInteger.ZERO) < 0) exponent.mod(order) else exponent
- /*
- * BC treats EC as additive group while we treat that as multiplicative group.
- * Therefore, exponentiate point is multiply.
- */
- base.multiply(exp).asInstanceOf[ElemType]
+ CryptoFacade.exponentiatePoint(base, exp)
}
@@ -186,7 +170,7 @@ abstract class BcDlogGroup[ElemType <: ECPoint](val x9params: X9ECParameters) ex
//However, if a specific Dlog Group has a more efficient implementation then is it advised to override this function in that concrete
//Dlog group. For example we do so in CryptoPpDlogZpSafePrime.
val one = BigInteger.ONE
- val qMinusOne = x9params.getN.subtract(one)
+ val qMinusOne = ctx.order.subtract(one)
// choose a random number x in Zq*
val randNum = BigIntegers.createRandomInRange(one, qMinusOne, secureRandom)
// compute g^x to get a new element
@@ -213,22 +197,7 @@ abstract class BcDlogGroup[ElemType <: ECPoint](val x9params: X9ECParameters) ex
* @throws IllegalArgumentException
*/
override def multiplyGroupElements(groupElement1: ElemType, groupElement2: ElemType): ElemType =
- groupElement1.add(groupElement2).asInstanceOf[ElemType]
-
-
- /**
- * Computes the product of several exponentiations with distinct bases
- * and distinct exponents.
- * Instead of computing each part separately, an optimization is used to
- * compute it simultaneously.
- *
- * @param groupElements
- * @param exponentiations
- * @return the exponentiation result
- */
- override def simultaneousMultipleExponentiations(groupElements: Array[ElemType],
- exponentiations: Array[BigInteger]): ElemType =
- computeLL(groupElements, exponentiations)
+ CryptoFacade.multiplyPoints(groupElement1, groupElement2)
/**
* Computes the product of several exponentiations of the same base
@@ -273,113 +242,7 @@ abstract class BcDlogGroup[ElemType <: ECPoint](val x9params: X9ECParameters) ex
*/
override lazy val maxLengthOfByteArrayForEncoding: Int = k
-
- /*
- * Computes the simultaneousMultiplyExponentiate using a naive algorithm
- */
- protected def computeNaive(groupElements: Array[ElemType], exponentiations: Array[BigInteger]): ElemType =
- groupElements.zip(exponentiations)
- .iterator
- .map { case (base, exp) => exponentiate(base, exp) }
- .foldLeft(identity) { case (r, elem) => multiplyGroupElements(elem, r) }
-
-
- /*
- * Compute the simultaneousMultiplyExponentiate by LL algorithm.
- * The code is taken from the pseudo code of LL algorithm in http://dasan.sejong.ac.kr/~chlim/pub/multi_exp.ps.
- */
- protected def computeLL(groupElements: Array[ElemType], exponentiations: Array[BigInteger]): ElemType = {
- val n = groupElements.length
- //get the biggest exponent
- val bigExp = exponentiations.max
- val t = bigExp.bitLength //num bits of the biggest exponent.
- val w = getLLW(t) //window size, choose it according to the value of t
-
- //h = n/w
- val h = if ((n % w) == 0) n / w else (n / w) + 1
-
- //create pre computation table
- val preComp = createLLPreCompTable(groupElements, w, h)
-
- //holds the computation result
- var result: ElemType = computeLoop(exponentiations, w, h, preComp, identity, t - 1)
- //computes the first loop of the algorithm. This loop returns in the next part of the algorithm with one single tiny change.
-
- //computes the third part of the algorithm
- (t - 2).to(0, -1).foreach { j =>
- //Y = Y^2
- result = exponentiate(result, new BigInteger("2"))
- //computes the inner loop
- result = computeLoop(exponentiations, w, h, preComp, result, j)
- }
- result
- }
-
- /*
- * Computes the loop the repeats in the algorithm.
- * for k=0 to h-1
- * e=0
- * for i=kw to kw+w-1
- * if the bitIndex bit in ci is set:
- * calculate e += 2^(i-kw)
- * result = result *preComp[k][e]
- *
- */
- private def computeLoop(exponentiations: Array[BigInteger], w: Int, h: Int, preComp: Seq[Seq[ElemType]], result: ElemType, bitIndex: Int) = {
- var res = result
- cfor(0)(_ < h, _ + 1) { k =>
- var e = 0
- cfor(k * w)(_ < (k * w + w), _ + 1) { i =>
- if (i < exponentiations.length) { //if the bit is set, change the e value
- if (exponentiations(i).testBit(bitIndex)) {
- val twoPow = Math.pow(2, i - k * w).toInt
- e += twoPow
- }
- }
- }
- res = multiplyGroupElements(res, preComp(k)(e))
- }
- res
- }
-
- /*
- * Creates the preComputation table.
- */
- private def createLLPreCompTable(groupElements: Array[ElemType], w: Int, h: Int) = {
- val twoPowW = Math.pow(2, w).toInt
- //create the pre-computation table of size h*(2^(w))
- val preComp: Seq[mutable.Seq[ElemType]] = Seq.fill(h)(mutable.Seq.fill(twoPowW)(identity))
-
- cfor(0)(_ < h, _ + 1) { k =>
- cfor(0)(_ < twoPowW, _ + 1) { e =>
- cfor(0)(_ < w, _ + 1) { i =>
- val baseIndex = k * w + i
- if (baseIndex < groupElements.length) {
- val base = groupElements(baseIndex)
- //if bit i in e is set, change preComp[k][e]
- if ((e & (1 << i)) != 0) { //bit i is set
- preComp(k)(e) = multiplyGroupElements(preComp(k)(e), base)
- }
- }
- }
- }
- }
- preComp
- }
-
- /*
- * returns the w value according to the given t
- */
- private def getLLW(t: Int): Int = {
- if (t <= 10) 2
- else if (t <= 24) 3
- else if (t <= 60) 4
- else if (t <= 144) 5
- else if (t <= 342) 6
- else if (t <= 797) 7
- else if (t <= 1828) 8
- else 9
- }
}
-object SecP256K1 extends BcDlogGroup[SecP256K1Point](CustomNamedCurves.getByName("secp256k1"))
\ No newline at end of file
+/** Implementation of [[BcDlogGroup]] using SecP256K1 curve. */
+object SecP256K1Group extends BcDlogGroup(CryptoFacade.createCryptoContext())
\ No newline at end of file
diff --git a/sigmastate/src/main/scala/sigmastate/basics/DLogProtocol.scala b/sigmastate/src/main/scala/sigmastate/basics/DLogProtocol.scala
index 75db9c69d2..38cfe1d8cb 100644
--- a/sigmastate/src/main/scala/sigmastate/basics/DLogProtocol.scala
+++ b/sigmastate/src/main/scala/sigmastate/basics/DLogProtocol.scala
@@ -2,7 +2,7 @@ package sigmastate.basics
import java.math.BigInteger
-import org.bouncycastle.util.BigIntegers
+import sigmastate.crypto.BigIntegers
import sigmastate.Values._
import Value.PropositionCode
import scorex.util.encode.Base16
@@ -27,8 +27,8 @@ object DLogProtocol {
extends SigmaProofOfKnowledgeLeaf[DLogSigmaProtocol, DLogProverInput] {
override def size: Int = 1
override val opCode: OpCode = OpCodes.ProveDlogCode
- lazy val h: EcPointType = value
- lazy val pkBytes: Array[Byte] = GroupElementSerializer.toBytes(h)
+ /** Serialized bytes of the elliptic curve point (using GroupElementSerializer). */
+ lazy val pkBytes: Array[Byte] = GroupElementSerializer.toBytes(value)
}
object ProveDlog {
@@ -102,6 +102,7 @@ object DLogProtocol {
SecondDLogProverMessage(z)
}
+ /** Simulation of sigma protocol. */
def simulate(publicInput: ProveDlog, challenge: Challenge): (FirstDLogProverMessage, SecondDLogProverMessage) = {
val qMinusOne = dlogGroup.order.subtract(BigInteger.ONE)
@@ -111,7 +112,7 @@ object DLogProtocol {
//COMPUTE a = g^z*h^(-e) (where -e here means -e mod q)
val e: BigInteger = new BigInteger(1, challenge)
val minusE = dlogGroup.order.subtract(e)
- val hToE = dlogGroup.exponentiate(publicInput.h, minusE)
+ val hToE = dlogGroup.exponentiate(publicInput.value, minusE)
val gToZ = dlogGroup.exponentiate(dlogGroup.generator, z)
val a = dlogGroup.multiplyGroupElements(gToZ, hToE)
FirstDLogProverMessage(a) -> SecondDLogProverMessage(z)
@@ -133,7 +134,7 @@ object DLogProtocol {
challenge: Challenge,
secondMessage: SecondDLogProverMessage): EcPointType = {
val g = dlogGroup.generator
- val h = proposition.h
+ val h = proposition.value
dlogGroup.multiplyGroupElements(
dlogGroup.exponentiate(g, secondMessage.z.underlying()),
diff --git a/sigmastate/src/main/scala/sigmastate/basics/DiffieHellmanTupleProtocol.scala b/sigmastate/src/main/scala/sigmastate/basics/DiffieHellmanTupleProtocol.scala
index 5316bc70c9..9122cc78cd 100644
--- a/sigmastate/src/main/scala/sigmastate/basics/DiffieHellmanTupleProtocol.scala
+++ b/sigmastate/src/main/scala/sigmastate/basics/DiffieHellmanTupleProtocol.scala
@@ -2,7 +2,7 @@ package sigmastate.basics
import java.math.BigInteger
-import org.bouncycastle.util.BigIntegers
+import sigmastate.crypto.BigIntegers
import sigmastate.Values.Value.PropositionCode
import sigmastate._
import sigmastate.basics.VerifierMessage.Challenge
diff --git a/sigmastate/src/main/scala/sigmastate/basics/DlogGroup.scala b/sigmastate/src/main/scala/sigmastate/basics/DlogGroup.scala
index 8df0f87e00..f7df1c8e3c 100644
--- a/sigmastate/src/main/scala/sigmastate/basics/DlogGroup.scala
+++ b/sigmastate/src/main/scala/sigmastate/basics/DlogGroup.scala
@@ -2,7 +2,7 @@ package sigmastate.basics
import java.math.BigInteger
import java.security.SecureRandom
-import org.bouncycastle.math.ec.ECPoint
+import sigmastate.crypto.{CryptoFacade, Ecp}
/**
@@ -19,7 +19,9 @@ import org.bouncycastle.math.ec.ECPoint
*
* @tparam ElemType is concrete type
*/
-trait DlogGroup[ElemType <: ECPoint] {
+trait DlogGroup {
+ /** The type of the elements of this Dlog group */
+ type ElemType = Ecp
val secureRandom = new SecureRandom()
@@ -95,23 +97,12 @@ trait DlogGroup[ElemType <: ECPoint] {
// if the given element is the identity, get a new random element
while ( {
- randGen.isInfinity
+ CryptoFacade.isInfinityPoint(randGen)
}) randGen = createRandomElement()
randGen
}
- /**
- * Computes the product of several exponentiations with distinct bases
- * and distinct exponents.
- * Instead of computing each part separately, an optimization is used to
- * compute it simultaneously.
- * @param groupElements
- * @param exponentiations
- * @return the exponentiation result
- */
- def simultaneousMultipleExponentiations(groupElements: Array[ElemType], exponentiations: Array[BigInteger]): ElemType
-
/**
* Computes the product of several exponentiations of the same base
* and distinct exponents.
diff --git a/sigmastate/src/main/scala/sigmastate/basics/SigmaProtocolFunctions.scala b/sigmastate/src/main/scala/sigmastate/basics/SigmaProtocolFunctions.scala
index 199cdd1bf8..3327d94ebf 100644
--- a/sigmastate/src/main/scala/sigmastate/basics/SigmaProtocolFunctions.scala
+++ b/sigmastate/src/main/scala/sigmastate/basics/SigmaProtocolFunctions.scala
@@ -1,15 +1,7 @@
package sigmastate.basics
-import java.security.SecureRandom
-
-import sigmastate.basics.VerifierMessage.Challenge
-import sigmastate.interpreter.CryptoConstants
-import sigmastate.{SigmaProofOfKnowledgeLeaf, UncheckedTree}
import supertagged.TaggedType
-import scala.concurrent.Future
-
-
/*
Abstracting Sigma protocols
Functionality to get:
@@ -59,93 +51,10 @@ trait SigmaProtocol[SP <: SigmaProtocol[SP]] {
trait SigmaProtocolCommonInput[SP <: SigmaProtocol[SP]] {
- val soundness: Int = CryptoConstants.soundnessBits
}
trait SigmaProtocolPrivateInput[SP <: SigmaProtocol[SP], CI <: SigmaProtocolCommonInput[SP]] {
def publicImage: CI
}
-/**
- * common interface for both Prover and Verifier
- */
-trait Party[SP <: SigmaProtocol[SP], CI <: SigmaProtocolCommonInput[SP]] {
- val publicInput: CI
-}
-
-//implement it as a FSM-DSL waitfor - then - action - then - waitfor - etc
-trait InteractiveParty
-
-trait Prover[SP <: SigmaProtocol[SP],
-CI <: SigmaProtocolCommonInput[SP],
-PI <: SigmaProtocolPrivateInput[SP, CI]] extends Party[SP, CI] {
- val privateInputOpt: Option[PI]
-}
-
-
-trait InteractiveProver[SP <: SigmaProtocol[SP], CI <: SigmaProtocolCommonInput[SP], PI <: SigmaProtocolPrivateInput[SP, CI]]
- extends Prover[SP, CI, PI] with InteractiveParty {
-
- def firstMessage: SP#A
- def secondMessage(challenge: Challenge): SP#Z
-
- def simulate(challenge: Challenge): (SP#A, SP#Z)
-}
-
-trait SimulatingProver[SP <: SigmaProtocol[SP], CI <: SigmaProtocolCommonInput[SP]] {
- val challenge: Challenge
-}
-
-
-trait ZeroKnowledgeProofOfKnowledge[SP <: SigmaProtocol[SP]]
-
-trait NonInteractiveProver[SP <: SigmaProtocol[SP],
- PI <: SigmaProtocolPrivateInput[SP, CI],
- CI <: SigmaProofOfKnowledgeLeaf[SP, PI],
- P <: UncheckedTree]
- extends Prover[SP, CI, PI] {
-
- def prove(challenge: Array[Byte]): P
-}
-
-trait Verifier[SP <: SigmaProtocol[SP], CI <: SigmaProtocolCommonInput[SP]] extends Party[SP, CI] {
- type P <: Prover[SP, CI, _]
- type ST <: SigmaProtocolTranscript[SP, CI]
-
- lazy val challenge = Challenge({
- val ch = new Array[Byte](publicInput.soundness / 8)
- new SecureRandom().nextBytes(ch) //modifies ch
- ch
- })
-
- val publicInput: CI
-
- def prover: P
-
- def transcript: Future[Option[ST]]
-}
-
-/**
- * Sigma Protocol transcript enough for verification
- *
- * @tparam SP
- * @tparam CI
- */
-trait SigmaProtocolTranscript[SP <: SigmaProtocol[SP], CI <: SigmaProtocolCommonInput[SP]] {
-
- /** Common input known to both prover and verifier. */
- val x: CI
-
- /** First prover message */
- val a: SP#A
-
- /** Challenge created by verifier and sent to prover */
- val e: Challenge
-
- /** Second prover message - response to the challenge */
- val z: SP#Z
-
- /** Returns true if the verifier has accepted the prover's reponse to the challenge. */
- def accepted: Boolean
-}
diff --git a/sigmastate/src/main/scala/sigmastate/crypto/BigIntegers.scala b/sigmastate/src/main/scala/sigmastate/crypto/BigIntegers.scala
new file mode 100644
index 0000000000..0e55f12532
--- /dev/null
+++ b/sigmastate/src/main/scala/sigmastate/crypto/BigIntegers.scala
@@ -0,0 +1,104 @@
+package sigmastate.crypto
+
+import java.math.BigInteger
+import java.security.SecureRandom
+
+/** Re-implementation in Scala of select set of utility methods from
+ * org.bouncycastle.util.BigIntegers.
+ */
+object BigIntegers {
+
+ /** The value 0 as a BigInteger. */
+ val ZERO: BigInteger = BigInteger.valueOf(0)
+
+ private val MAX_ITERATIONS = 1000
+
+ /** Create the given number of random bits.
+ * @param bitLength the number of random bits to create.
+ * @param random a source of randomness.
+ * @return a byte array containing random bits.
+ */
+ @throws[IllegalArgumentException]
+ def createRandom(bitLength: Int, random: SecureRandom): Array[Byte] = {
+ if (bitLength < 1) throw new IllegalArgumentException("bitLength must be at least 1")
+ val nBytes = (bitLength + 7) / 8
+ val rv = new Array[Byte](nBytes)
+ random.nextBytes(rv)
+
+ // strip off any excess bits in the MSB
+ val xBits = 8 * nBytes - bitLength
+ rv(0) = (rv(0) & 255 >>> xBits).toByte
+ rv
+ }
+
+ /**
+ * Return a positive BigInteger in the range of 0 to 2**bitLength - 1.
+ *
+ * @param bitLength maximum bit length for the generated BigInteger.
+ * @param random a source of randomness.
+ * @return a positive BigInteger
+ */
+ def createRandomBigInteger(
+ bitLength: Int,
+ random: SecureRandom): BigInteger = {
+ new BigInteger(1, createRandom(bitLength, random))
+ }
+
+ /**
+ * Return a random BigInteger not less than 'min' and not greater than 'max'
+ *
+ * @param min the least value that may be generated
+ * @param max the greatest value that may be generated
+ * @param random the source of randomness
+ * @return a random BigInteger value in the range [min,max]
+ */
+ def createRandomInRange(
+ min: BigInteger,
+ max: BigInteger,
+ random: SecureRandom): BigInteger = {
+ val cmp = min.compareTo(max)
+ if (cmp >= 0) {
+ if (cmp > 0) throw new IllegalArgumentException("'min' may not be greater than 'max'")
+ return min
+ }
+
+ if (min.bitLength > max.bitLength / 2)
+ return createRandomInRange(ZERO, max.subtract(min), random).add(min)
+
+ for ( i <- 0 until MAX_ITERATIONS ) {
+ val x = createRandomBigInteger(max.bitLength, random)
+ if (x.compareTo(min) >= 0 && x.compareTo(max) <= 0) return x
+ }
+ // fall back to a faster (restricted) method
+ createRandomBigInteger(max.subtract(min).bitLength - 1, random).add(min)
+ }
+
+ /**
+ * Return the passed in value as an unsigned byte array of the specified length, padded with
+ * leading zeros as necessary..
+ *
+ * @param length the fixed length of the result
+ * @param value the value to be converted.
+ * @return a byte array padded to a fixed length with leading zeros.
+ */
+ def asUnsignedByteArray(length: Int, value: BigInteger): Array[Byte] = {
+ val bytes = value.toByteArray
+ if (bytes.length == length) return bytes
+ val start = if (bytes(0) == 0) 1 else 0
+
+ val count = bytes.length - start
+ if (count > length)
+ throw new IllegalArgumentException("standard length exceeded for value")
+
+ val tmp = new Array[Byte](length)
+ System.arraycopy(bytes, start, tmp, tmp.length - count, count)
+ tmp
+ }
+
+ /** Converts a byte array to a BigInteger, treating the array as bits of the unsigned
+ * integer.
+ * @param buf the byte array to convert
+ * @return the resulting positive BigInteger
+ */
+ def fromUnsignedByteArray(buf: Array[Byte]) = new BigInteger(1, buf)
+}
diff --git a/sigmastate/src/main/scala/sigmastate/crypto/CryptoContext.scala b/sigmastate/src/main/scala/sigmastate/crypto/CryptoContext.scala
new file mode 100644
index 0000000000..b5aceec849
--- /dev/null
+++ b/sigmastate/src/main/scala/sigmastate/crypto/CryptoContext.scala
@@ -0,0 +1,30 @@
+package sigmastate.crypto
+
+import java.math.BigInteger
+
+/** A context for cryptographic operations. */
+abstract class CryptoContext {
+ /** The characteristics of the underlying finite field. */
+ def fieldCharacteristic: BigInteger
+
+ /** The order of the underlying group. */
+ def order: BigInteger
+
+ /** Validates a point.
+ * @param x the x-coordinate of the point
+ * @param y the y-coordinate of the point
+ * @return the point if it is valid
+ * @throws IllegalArgumentException if the coordinates do not represent a valid point
+ */
+ def validatePoint(x: BigInteger, y: BigInteger): Ecp
+
+ /** The point at infinity. */
+ def infinity(): Ecp
+
+ /** Decodes a point from its byte representation. */
+ def decodePoint(encoded: Array[Byte]): Ecp
+
+ /** The generator of the underlying group. */
+ def generator: Ecp
+}
+
diff --git a/sigmastate/src/main/scala/sigmastate/crypto/CryptoContextJvm.scala b/sigmastate/src/main/scala/sigmastate/crypto/CryptoContextJvm.scala
new file mode 100644
index 0000000000..6a3420242f
--- /dev/null
+++ b/sigmastate/src/main/scala/sigmastate/crypto/CryptoContextJvm.scala
@@ -0,0 +1,30 @@
+package sigmastate.crypto
+
+import org.bouncycastle.asn1.x9.X9ECParameters
+
+import java.math.BigInteger
+
+/** JVM implementation of context for cryptographic operations using Bouncycastle. */
+class CryptoContextJvm(x9params: X9ECParameters) extends CryptoContext {
+ private lazy val curve = x9params.getCurve
+
+ override def fieldCharacteristic: BigInteger = curve.getField.getCharacteristic
+
+ override def order: BigInteger = x9params.getN
+
+ override def generator: Ecp = {
+ Platform.Ecp(x9params.getG)
+ }
+
+ override def validatePoint(x: BigInteger, y: BigInteger): Ecp = {
+ Platform.Ecp(curve.validatePoint(x, y))
+ }
+
+ override def infinity(): Ecp = {
+ Platform.Ecp(curve.getInfinity)
+ }
+
+ override def decodePoint(encoded: Array[Byte]): Ecp = {
+ Platform.Ecp(curve.decodePoint(encoded))
+ }
+}
diff --git a/sigmastate/src/main/scala/sigmastate/crypto/CryptoFacade.scala b/sigmastate/src/main/scala/sigmastate/crypto/CryptoFacade.scala
new file mode 100644
index 0000000000..54ec07940c
--- /dev/null
+++ b/sigmastate/src/main/scala/sigmastate/crypto/CryptoFacade.scala
@@ -0,0 +1,86 @@
+package sigmastate.crypto
+
+import java.math.BigInteger
+
+/** A facade for cryptographic operations. The concrete implementation is delegated to the
+ * Platform object, which is resolved by the compiler to either JVM or JS implementation.
+ * Cross-platform code should use this facade instead of the Platform object directly.
+ */
+object CryptoFacade {
+ /** Create a new context for cryptographic operations. */
+ def createCryptoContext(): CryptoContext = Platform.createContext()
+
+ /** * Normalization ensures that any projective coordinate is 1, and therefore that the x, y
+ * coordinates reflect those of the equivalent point in an affine coordinate system.
+ *
+ * @return a new ECPoint instance representing the same point, but with normalized coordinates
+ */
+ def normalizePoint(p: Ecp): Ecp = Platform.normalizePoint(p)
+
+ /** Negate a point. */
+ def negatePoint(p: Ecp): Ecp = Platform.negatePoint(p)
+
+ /** Check if a point is infinity. */
+ def isInfinityPoint(p: Ecp): Boolean = Platform.isInfinityPoint(p)
+
+ /** Exponentiate a point.
+ *
+ * @param p point to exponentiate
+ * @param n exponent
+ * @return p to the power of n (`p^n`)
+ */
+ def exponentiatePoint(p: Ecp, n: BigInteger): Ecp = Platform.exponentiatePoint(p, n)
+
+ /** Multiply two points.
+ *
+ * @param p1 first point
+ * @param p2 second point
+ * @return group multiplication (p1 * p2)
+ */
+ def multiplyPoints(p1: Ecp, p2: Ecp): Ecp = Platform.multiplyPoints(p1, p2)
+
+ /** Return simplified string representation of the point (used only for debugging) */
+ def showPoint(p: Ecp): String = Platform.showPoint(p)
+
+ /** Returns the sign of the field element. */
+ def signOf(p: ECFieldElem): Boolean = Platform.signOf(p)
+
+ /** Returns byte representation of the given field element. */
+ def encodeFieldElem(p: ECFieldElem): Array[Byte] = Platform.encodeFieldElem(p)
+
+ /** Returns the x-coordinate.
+ *
+ * Caution: depending on the curve's coordinate system, this may not be the same value as in an
+ * affine coordinate system; use normalize() to get a point where the coordinates have their
+ * affine values, or use getAffineXCoord() if you expect the point to already have been
+ * normalized.
+ *
+ * @return the x-coordinate of this point
+ */
+ def getXCoord(p: Ecp): ECFieldElem = Platform.getXCoord(p)
+
+ /** Returns the y-coordinate.
+ *
+ * Caution: depending on the curve's coordinate system, this may not be the same value as in an
+ * affine coordinate system; use normalize() to get a point where the coordinates have their
+ * affine values, or use getAffineYCoord() if you expect the point to already have been
+ * normalized.
+ *
+ * @return the y-coordinate of this point
+ */
+ def getYCoord(p: Ecp): ECFieldElem = Platform.getYCoord(p)
+
+ /** Returns the affine x-coordinate after checking that this point is normalized.
+ *
+ * @return The affine x-coordinate of this point
+ * @throws IllegalStateException if the point is not normalized
+ */
+ def getAffineXCoord(p: Ecp): ECFieldElem = Platform.getAffineXCoord(p)
+
+ /** Returns the affine y-coordinate after checking that this point is normalized
+ *
+ * @return The affine y-coordinate of this point
+ * @throws IllegalStateException if the point is not normalized
+ */
+ def getAffineYCoord(p: Ecp): ECFieldElem = Platform.getAffineYCoord(p)
+}
diff --git a/sigmastate/src/main/scala/sigmastate/crypto/Platform.scala b/sigmastate/src/main/scala/sigmastate/crypto/Platform.scala
new file mode 100644
index 0000000000..5cb4a9f345
--- /dev/null
+++ b/sigmastate/src/main/scala/sigmastate/crypto/Platform.scala
@@ -0,0 +1,106 @@
+package sigmastate.crypto
+
+import org.bouncycastle.crypto.ec.CustomNamedCurves
+import org.bouncycastle.math.ec.{ECFieldElement, ECPoint}
+
+import java.math.BigInteger
+
+/** JVM specific implementation of crypto methods*/
+object Platform {
+ /** Returns the x-coordinate.
+ *
+ * Caution: depending on the curve's coordinate system, this may not be the same value as in an
+ * affine coordinate system; use normalize() to get a point where the coordinates have their
+ * affine values, or use getAffineXCoord() if you expect the point to already have been
+ * normalized.
+ *
+ * @return the x-coordinate of this point
+ */
+ def getXCoord(p: Ecp): ECFieldElem = ECFieldElem(p.value.getXCoord)
+
+ /** Returns the y-coordinate.
+ *
+ * Caution: depending on the curve's coordinate system, this may not be the same value as in an
+ * affine coordinate system; use normalize() to get a point where the coordinates have their
+ * affine values, or use getAffineYCoord() if you expect the point to already have been
+ * normalized.
+ *
+ * @return the y-coordinate of this point
+ */
+ def getYCoord(p: Ecp): ECFieldElem = ECFieldElem(p.value.getYCoord)
+
+ /** Returns the affine x-coordinate after checking that this point is normalized.
+ *
+ * @return The affine x-coordinate of this point
+ * @throws IllegalStateException if the point is not normalized
+ */
+ def getAffineXCoord(p: Ecp): ECFieldElem = ECFieldElem(p.value.getAffineXCoord)
+
+ /** Returns the affine y-coordinate after checking that this point is normalized
+ *
+ * @return The affine y-coordinate of this point
+ * @throws IllegalStateException if the point is not normalized
+ */
+ def getAffineYCoord(p: Ecp): ECFieldElem = ECFieldElem(p.value.getAffineYCoord)
+
+ /** Returns byte representation of the given field element. */
+ def encodeFieldElem(p: ECFieldElem): Array[Byte] = p.value.getEncoded
+
+ /** Returns the value of bit 0 in BigInteger representation of this point. */
+ def signOf(p: ECFieldElem): Boolean = p.value.testBitZero()
+
+ /** * Normalization ensures that any projective coordinate is 1, and therefore that the x, y
+ * coordinates reflect those of the equivalent point in an affine coordinate system.
+ *
+ * @return a new ECPoint instance representing the same point, but with normalized coordinates
+ */
+ def normalizePoint(p: Ecp): Ecp = Ecp(p.value.normalize())
+
+ /** Return simplified string representation of the point (used only for debugging) */
+ def showPoint(p: Ecp): String = {
+ val rawX = p.value.getRawXCoord.toString.substring(0, 6)
+ val rawY = p.value.getRawYCoord.toString.substring(0, 6)
+ s"ECPoint($rawX,$rawY,...)"
+ }
+
+ /** Multiply two points.
+ * @param p1 first point
+ * @param p2 second point
+ * @return group multiplication (p1 * p2)
+ */
+ def multiplyPoints(p1: Ecp, p2: Ecp): Ecp = {
+ /*
+ * BC treats EC as additive group while we treat that as multiplicative group.
+ */
+ Ecp(p1.value.add(p2.value))
+ }
+
+ /** Exponentiate a point.
+ * @param p point to exponentiate
+ * @param n exponent
+ * @return p to the power of n (p^n)
+ */
+ def exponentiatePoint(p: Ecp, n: BigInteger): Ecp = {
+ /*
+ * BC treats EC as additive group while we treat that as multiplicative group.
+ * Therefore, exponentiate point is multiply.
+ */
+ Ecp(p.value.multiply(n))
+ }
+
+ /** Check if a point is infinity. */
+ def isInfinityPoint(p: Ecp): Boolean = p.value.isInfinity
+
+ /** Negate a point. */
+ def negatePoint(p: Ecp): Ecp = Ecp(p.value.negate())
+
+ /** Wrapper for point type. */
+ case class Ecp(private[crypto] val value: ECPoint)
+
+ /** Wrapper for field element type. */
+ case class ECFieldElem(value: ECFieldElement)
+
+ /** Create a new context for cryptographic operations. */
+ def createContext(): CryptoContext = new CryptoContextJvm(CustomNamedCurves.getByName("secp256k1"))
+
+}
diff --git a/sigmastate/src/main/scala/sigmastate/crypto/package.scala b/sigmastate/src/main/scala/sigmastate/crypto/package.scala
new file mode 100644
index 0000000000..24d797be6f
--- /dev/null
+++ b/sigmastate/src/main/scala/sigmastate/crypto/package.scala
@@ -0,0 +1,8 @@
+package sigmastate
+
+package object crypto {
+ /** Instance of Elliptic Curve point. */
+ type Ecp = Platform.Ecp
+ /** Instance of Elliptic Curve field element. */
+ type ECFieldElem = Platform.ECFieldElem
+}
diff --git a/sigmastate/src/main/scala/sigmastate/eval/CostingDataContext.scala b/sigmastate/src/main/scala/sigmastate/eval/CostingDataContext.scala
index c152cb1716..09912c7588 100644
--- a/sigmastate/src/main/scala/sigmastate/eval/CostingDataContext.scala
+++ b/sigmastate/src/main/scala/sigmastate/eval/CostingDataContext.scala
@@ -4,7 +4,6 @@ import com.google.common.primitives.{Ints, Longs}
import java.math.BigInteger
import java.util.Arrays
-import org.bouncycastle.math.ec.ECPoint
import org.ergoplatform.{ErgoBox, SigmaConstants}
import org.ergoplatform.validation.ValidationRules
import scalan.OverloadHack.Overloaded1
@@ -27,6 +26,7 @@ import scorex.crypto.hash.{Blake2b256, Digest32, Sha256}
import sigmastate.Values.ErgoTree.EmptyConstants
import sigmastate.basics.DLogProtocol.ProveDlog
import sigmastate.basics.ProveDHTuple
+import sigmastate.crypto.{CryptoFacade, Ecp}
import sigmastate.lang.TransformingSigmaBuilder
import sigmastate.serialization.ErgoTreeSerializer.DefaultSerializer
import sigmastate.serialization.{GroupElementSerializer, SigmaSerializer}
@@ -96,7 +96,7 @@ case class CBigInt(override val wrappedValue: BigInteger) extends BigInt with Wr
/** A default implementation of [[GroupElement]] interface.
* @see [[GroupElement]] for detailed descriptions
*/
-case class CGroupElement(override val wrappedValue: EcPointType) extends GroupElement with WrapperOf[ECPoint] {
+case class CGroupElement(override val wrappedValue: Ecp) extends GroupElement with WrapperOf[Ecp] {
val dsl = CostingSigmaDslBuilder
override def toString: String = s"GroupElement(${Extensions.showECPoint(wrappedValue)})"
@@ -104,16 +104,16 @@ case class CGroupElement(override val wrappedValue: EcPointType) extends GroupEl
override def getEncoded: Coll[Byte] =
dsl.Colls.fromArray(GroupElementSerializer.toBytes(wrappedValue))
- override def isInfinity: Boolean = wrappedValue.isInfinity
+ override def isIdentity: Boolean = CryptoFacade.isInfinityPoint(wrappedValue)
override def exp(k: BigInt): GroupElement =
- dsl.GroupElement(wrappedValue.multiply(k.asInstanceOf[CBigInt].wrappedValue))
+ dsl.GroupElement(CryptoFacade.exponentiatePoint(wrappedValue, k.asInstanceOf[CBigInt].wrappedValue))
override def multiply(that: GroupElement): GroupElement =
- dsl.GroupElement(wrappedValue.add(that.asInstanceOf[CGroupElement].wrappedValue))
+ dsl.GroupElement(CryptoFacade.multiplyPoints(wrappedValue, that.asInstanceOf[CGroupElement].wrappedValue))
override def negate: GroupElement =
- dsl.GroupElement(wrappedValue.negate())
+ dsl.GroupElement(CryptoFacade.negatePoint(wrappedValue))
}
/** A default implementation of [[SigmaProp]] interface.
@@ -131,6 +131,7 @@ case class CSigmaProp(sigmaTree: SigmaBoolean) extends SigmaProp with WrapperOf[
override def propBytes: Coll[Byte] = {
// in order to have comparisons like `box.propositionBytes == pk.propBytes` we need to make sure
// the same serialization method is used in both cases
+ // TODO v6.0: add `pk.propBytes(version)`
val root = sigmaTree.toSigmaProp
val ergoTree = new ErgoTree(ErgoTree.DefaultHeader, EmptyConstants, Right(root), 0, null, None)
val bytes = DefaultSerializer.serializeErgoTree(ergoTree)
@@ -509,7 +510,8 @@ class CostingSigmaDslBuilder extends SigmaDslBuilder { dsl =>
override def toBigInteger(n: BigInt): BigInteger = n.asInstanceOf[CBigInt].wrappedValue
- def GroupElement(p: ECPoint): GroupElement = p match {
+ /** Wraps the given elliptic curve point into GroupElement type. */
+ def GroupElement(p: Ecp): GroupElement = p match {
case ept: EcPointType => CGroupElement(ept)
case m => sys.error(s"Point of type ${m.getClass} is not supported")
}
@@ -523,9 +525,12 @@ class CostingSigmaDslBuilder extends SigmaDslBuilder { dsl =>
/** Extract `sigmastate.AvlTreeData` from DSL's `AvlTree` type. */
def toAvlTreeData(p: AvlTree): AvlTreeData = p.asInstanceOf[CAvlTree].treeData
- /** Extract `org.bouncycastle.math.ec.ECPoint` from DSL's `GroupElement` type. */
- def toECPoint(ge: GroupElement): ECPoint = ge.asInstanceOf[CGroupElement].wrappedValue
+ /** Extract `sigmastate.crypto.Ecp` from DSL's `GroupElement` type. */
+ def toECPoint(ge: GroupElement): Ecp = ge.asInstanceOf[CGroupElement].wrappedValue
+ /** Creates a new AvlTree instance with the given parameters.
+ * @see AvlTreeData for details
+ */
override def avlTree(operationFlags: Byte, digest: Coll[Byte], keyLength: Int, valueLengthOpt: Option[Int]): CAvlTree = {
val treeData = AvlTreeData(ADDigest @@ digest.toArray, AvlTreeFlags(operationFlags), keyLength, valueLengthOpt)
CAvlTree(treeData)
diff --git a/sigmastate/src/main/scala/sigmastate/eval/Evaluation.scala b/sigmastate/src/main/scala/sigmastate/eval/Evaluation.scala
index 830d6817d8..d54c2e8f7a 100644
--- a/sigmastate/src/main/scala/sigmastate/eval/Evaluation.scala
+++ b/sigmastate/src/main/scala/sigmastate/eval/Evaluation.scala
@@ -1,6 +1,5 @@
package sigmastate.eval
-import org.bouncycastle.math.ec.ECPoint
import org.ergoplatform._
import scalan.RType
import scalan.RType._
@@ -99,7 +98,6 @@ object Evaluation {
case BigIntegerRType => SBigInt
case BigIntRType => SBigInt
- case ECPointRType => SGroupElement
case GroupElementRType => SGroupElement
case AvlTreeRType => SAvlTree
@@ -147,7 +145,6 @@ object Evaluation {
case _: BigInteger => BigIntegerRType
case _: special.sigma.BigInt => BigIntRType
- case _: ECPoint => ECPointRType
case _: GroupElement => GroupElementRType
case _: ErgoBox => ErgoBoxRType
diff --git a/sigmastate/src/main/scala/sigmastate/eval/Extensions.scala b/sigmastate/src/main/scala/sigmastate/eval/Extensions.scala
index bf982e3269..a5d93ba25c 100644
--- a/sigmastate/src/main/scala/sigmastate/eval/Extensions.scala
+++ b/sigmastate/src/main/scala/sigmastate/eval/Extensions.scala
@@ -1,7 +1,5 @@
package sigmastate.eval
-import org.bouncycastle.math.ec.ECPoint
-
import java.math.BigInteger
import scalan.RType
import sigmastate.{SCollection, SCollectionType, SType}
@@ -13,6 +11,7 @@ import sigmastate.SType.AnyOps
import org.ergoplatform.ErgoBox
import debox.{Buffer => DBuffer}
import debox.cfor
+import sigmastate.crypto.{CryptoFacade, Ecp}
object Extensions {
private val Colls = CostingSigmaDslBuilder.Colls
@@ -89,14 +88,12 @@ object Extensions {
}
/** Shortened String representation of `source` GroupElement. */
- def showECPoint(p: ECPoint): String = {
- if (p.isInfinity) {
- "INF"
+ def showECPoint(p: Ecp): String = {
+ if (p.isIdentity) {
+ "IDENTITY"
}
else {
- val rawX = p.getRawXCoord.toString.substring(0, 6)
- val rawY = p.getRawYCoord.toString.substring(0, 6)
- s"ECPoint($rawX,$rawY,...)"
+ CryptoFacade.showPoint(p)
}
}
diff --git a/sigmastate/src/main/scala/sigmastate/interpreter/CryptoConstants.scala b/sigmastate/src/main/scala/sigmastate/interpreter/CryptoConstants.scala
index 1da6c8c27e..1d4a3a2d28 100644
--- a/sigmastate/src/main/scala/sigmastate/interpreter/CryptoConstants.scala
+++ b/sigmastate/src/main/scala/sigmastate/interpreter/CryptoConstants.scala
@@ -2,17 +2,21 @@ package sigmastate.interpreter
import java.math.BigInteger
import java.security.SecureRandom
+import sigmastate.basics.{BcDlogGroup, SecP256K1Group}
+import sigmastate.crypto.Ecp
-import org.bouncycastle.math.ec.custom.sec.SecP256K1Point
-import sigmastate.basics.{BcDlogGroup, SecP256K1}
-
+/** Constants used in crypto operations implementation. */
object CryptoConstants {
- type EcPointType = SecP256K1Point
+ /** Type of group elements used in the signature scheme. */
+ type EcPointType = Ecp
+ /** Length of encoded group element in bytes. */
val EncodedGroupElementLength: Byte = 33
- val dlogGroup: BcDlogGroup[EcPointType] = SecP256K1
+ /** Group used in the signature scheme. */
+ val dlogGroup: BcDlogGroup = SecP256K1Group
+ /** Secure random generator used in the signature scheme. */
val secureRandom: SecureRandom = dlogGroup.secureRandom
/** Size of the binary representation of any group element (2 ^ groupSizeBits == ) */
@@ -27,6 +31,7 @@ object CryptoConstants {
/** Length of hash function used in the signature scheme. Blake2b hash function is used. */
val hashLengthBits = 256
+ /** Length of hash in bytes. */
val hashLength: Int = hashLengthBits / 8
/** A size of challenge in Sigma protocols, in bits.
@@ -37,6 +42,10 @@ object CryptoConstants {
*/
implicit val soundnessBits: Int = 192.ensuring(_ < groupSizeBits, "2^t < q condition is broken!")
+ /** Generates random bytes using secure random generator.
+ * @param howMany number of bytes to generate
+ * @return generated bytes in a new array
+ */
def secureRandomBytes(howMany: Int): Array[Byte] = {
val bytes = new Array[Byte](howMany)
secureRandom.nextBytes(bytes)
diff --git a/sigmastate/src/main/scala/sigmastate/serialization/GroupElementSerializer.scala b/sigmastate/src/main/scala/sigmastate/serialization/GroupElementSerializer.scala
index d004d42566..b8523cb430 100644
--- a/sigmastate/src/main/scala/sigmastate/serialization/GroupElementSerializer.scala
+++ b/sigmastate/src/main/scala/sigmastate/serialization/GroupElementSerializer.scala
@@ -1,5 +1,6 @@
package sigmastate.serialization
+import sigmastate.crypto.CryptoFacade
import sigmastate.interpreter.CryptoConstants
import sigmastate.interpreter.CryptoConstants.EcPointType
import sigmastate.util.safeNewArray
@@ -21,12 +22,12 @@ object GroupElementSerializer extends SigmaSerializer[EcPointType, EcPointType]
private lazy val identityPointEncoding = Array.fill(encodingSize)(0: Byte)
override def serialize(point: EcPointType, w: SigmaByteWriter): Unit = {
- val bytes = if (point.isInfinity) {
+ val bytes = if (CryptoFacade.isInfinityPoint(point)) {
identityPointEncoding
} else {
- val normed = point.normalize()
- val ySign = normed.getAffineYCoord.testBitZero()
- val X = normed.getXCoord.getEncoded
+ val normed = CryptoFacade.normalizePoint(point)
+ val ySign = CryptoFacade.signOf(CryptoFacade.getAffineYCoord(normed))
+ val X = CryptoFacade.encodeFieldElem(CryptoFacade.getXCoord(normed))
val PO = safeNewArray[Byte](X.length + 1)
PO(0) = (if (ySign) 0x03 else 0x02).toByte
System.arraycopy(X, 0, PO, 1, X.length)
@@ -38,7 +39,7 @@ object GroupElementSerializer extends SigmaSerializer[EcPointType, EcPointType]
override def parse(r: SigmaByteReader): EcPointType = {
val encoded = r.getBytes(encodingSize)
if (encoded(0) != 0) {
- curve.curve.decodePoint(encoded).asInstanceOf[EcPointType]
+ curve.ctx.decodePoint(encoded)
} else {
curve.identity
}
diff --git a/sigmastate/src/test/scala/org/ergoplatform/JsonSerializationSpec.scala b/sigmastate/src/test/scala/org/ergoplatform/JsonSerializationSpec.scala
index ee33061c68..8fbd80987f 100644
--- a/sigmastate/src/test/scala/org/ergoplatform/JsonSerializationSpec.scala
+++ b/sigmastate/src/test/scala/org/ergoplatform/JsonSerializationSpec.scala
@@ -123,7 +123,7 @@ class JsonSerializationSpec extends SigmaTestingCommons with SerializationSpecif
val minerPkHex = "0326df75ea615c18acc6bb4b517ac82795872f388d5d180aac90eaa84de750b942"
val minerPk = Base16.decode(minerPkHex).map { point =>
ProveDlog(
- CryptoConstants.dlogGroup.curve.decodePoint(point).asInstanceOf[CryptoConstants.EcPointType]
+ CryptoConstants.dlogGroup.ctx.decodePoint(point).asInstanceOf[CryptoConstants.EcPointType]
)
}.get
val regs = Map(
diff --git a/sigmastate/src/test/scala/sigmastate/crypto/GroupLawsSpecification.scala b/sigmastate/src/test/scala/sigmastate/crypto/GroupLawsSpecification.scala
index b77d3c5b0a..b5b0b8b33b 100644
--- a/sigmastate/src/test/scala/sigmastate/crypto/GroupLawsSpecification.scala
+++ b/sigmastate/src/test/scala/sigmastate/crypto/GroupLawsSpecification.scala
@@ -1,11 +1,11 @@
package sigmastate.crypto
import java.math.BigInteger
-
import org.scalacheck.Gen
-import sigmastate.helpers.SigmaTestingCommons
+import sigmastate.helpers.{SigmaPPrint, SigmaTestingCommons}
import sigmastate.interpreter.CryptoConstants
import sigmastate.interpreter.CryptoConstants.EcPointType
+import sigmastate.utils.Helpers
import scala.util.Random
@@ -54,4 +54,52 @@ class GroupLawsSpecification extends SigmaTestingCommons {
}
}
+ private def printPoints(points: Seq[(String, Any)]) = {
+ points.foreach { case (name, p) =>
+ println(s"val $name = ${SigmaPPrint.apply(p).plainText}")
+ }
+ }
+
+// uncommment to generate new test vectors
+//
+// property("generate initial points") {
+// printPoints(Seq(
+// "identity" -> group.identity,
+// "order" -> group.order,
+// "p1" -> group.createRandomElement(),
+// "p2" -> group.createRandomElement(),
+// "p3" -> group.createRandomElement()
+// ))
+// }
+
+ val p1: Ecp = Helpers.decodeECPoint("0381c5275b1d50c39a0c36c4561c3a37bff1d87e37a9ad69eab029e426c0b1a8ac")
+ val p2 = Helpers.decodeECPoint("02198064ec24024bb8b300e20dd18e33cc1fccb0fea73940bd9a1d3d9d6c3ddd8f")
+ val p3 = Helpers.decodeECPoint("02e135f5f905fb843698d48959c6c792b2c6f9605b90be2280d53b4b69ef23e8a9")
+
+// uncommment to generate new test vectors
+ property("generate op results") {
+ printPoints(Seq(
+ "p1" -> p1,
+ "p2" -> p2,
+ "p3" -> p3,
+ "p1.add(p2)" -> CryptoFacade.multiplyPoints(p1, p2),
+ "p1.multiply(order)" -> CryptoFacade.exponentiatePoint(p1, group.order),
+ "p1.multiply(order + 1)" -> CryptoFacade.exponentiatePoint(p1, group.order.add(BigInteger.ONE)),
+ "p1.inverse" -> CryptoFacade.negatePoint(p1)
+ ))
+ }
+
+ property("check test vectors") {
+ CryptoFacade.multiplyPoints(p1, p2) shouldBe
+ Helpers.decodeECPoint("03de5e9c2806c05cd45a57d18c469743f42a0d2c84370b6662eb39d8a2990abed8")
+
+ CryptoFacade.exponentiatePoint(p1, group.order) shouldBe
+ Helpers.decodeECPoint("000000000000000000000000000000000000000000000000000000000000000000")
+
+ CryptoFacade.exponentiatePoint(p1, group.order.add(BigInteger.ONE)) shouldBe
+ Helpers.decodeECPoint("0381c5275b1d50c39a0c36c4561c3a37bff1d87e37a9ad69eab029e426c0b1a8ac")
+
+ CryptoFacade.negatePoint(p1) shouldBe
+ Helpers.decodeECPoint("0281c5275b1d50c39a0c36c4561c3a37bff1d87e37a9ad69eab029e426c0b1a8ac")
+ }
}
diff --git a/sigmastate/src/test/scala/sigmastate/eval/BasicOpsTests.scala b/sigmastate/src/test/scala/sigmastate/eval/BasicOpsTests.scala
index 4d024fc33d..58da469c82 100644
--- a/sigmastate/src/test/scala/sigmastate/eval/BasicOpsTests.scala
+++ b/sigmastate/src/test/scala/sigmastate/eval/BasicOpsTests.scala
@@ -1,9 +1,9 @@
package sigmastate.eval
import java.math.BigInteger
-import org.bouncycastle.crypto.ec.CustomNamedCurves
import org.scalatest.{FunSuite, Matchers}
import sigmastate.TrivialProp
+import sigmastate.basics.SecP256K1Group
import special.sigma.{ContractsTestkit, SigmaDslBuilder, SigmaProp}
import scala.language.implicitConversions
@@ -33,7 +33,7 @@ class BasicOpsTests extends FunSuite with ContractsTestkit with Matchers {
// TODO this is valid for BigIntModQ type (https://github.com/ScorexFoundation/sigmastate-interpreter/issues/554)
ignore("ByteArrayToBigInt should always produce big int less than dlog group order") {
- val groupOrder = CustomNamedCurves.getByName("secp256k1").getN
+ val groupOrder = SecP256K1Group.ctx.order
SigmaDsl.byteArrayToBigInt(
Colls.fromArray(groupOrder.subtract(BigInteger.ONE).toByteArray)
diff --git a/sigmastate/src/test/scala/sigmastate/lang/LangTests.scala b/sigmastate/src/test/scala/sigmastate/lang/LangTests.scala
index 945816aab2..e6889e9c32 100644
--- a/sigmastate/src/test/scala/sigmastate/lang/LangTests.scala
+++ b/sigmastate/src/test/scala/sigmastate/lang/LangTests.scala
@@ -5,7 +5,6 @@ import sigmastate.Values.{LongConstant, SValue, Value, SigmaBoolean, ConcreteCol
import sigmastate._
import java.math.BigInteger
-import org.bouncycastle.math.ec.ECPoint
import org.scalatest.Matchers
import sigmastate.basics.DLogProtocol.ProveDlog
import sigmastate.SCollection.SByteArray
@@ -39,10 +38,10 @@ trait LangTests extends Matchers with NegativeTesting {
val ecp2 = dlog.multiplyGroupElements(ecp1, ecp1)
val ecp3 = dlog.multiplyGroupElements(ecp2, ecp2)
val ecp4 = dlog.multiplyGroupElements(ecp3, ecp3)
- val g1 = CostingSigmaDslBuilder.GroupElement(ecp1.asInstanceOf[ECPoint])
- val g2 = CostingSigmaDslBuilder.GroupElement(ecp2.asInstanceOf[ECPoint])
- val g3 = CostingSigmaDslBuilder.GroupElement(ecp3.asInstanceOf[ECPoint])
- val g4 = CostingSigmaDslBuilder.GroupElement(ecp4.asInstanceOf[ECPoint])
+ val g1 = CostingSigmaDslBuilder.GroupElement(ecp1)
+ val g2 = CostingSigmaDslBuilder.GroupElement(ecp2)
+ val g3 = CostingSigmaDslBuilder.GroupElement(ecp3)
+ val g4 = CostingSigmaDslBuilder.GroupElement(ecp4)
protected val n1: BigInt = BigInt(10).underlying()
protected val n2: BigInt = BigInt(20).underlying()
diff --git a/sigmastate/src/test/scala/sigmastate/serialization/GroupElementSerializerSpecification.scala b/sigmastate/src/test/scala/sigmastate/serialization/GroupElementSerializerSpecification.scala
index dd17ce069f..5be6cc5ff9 100644
--- a/sigmastate/src/test/scala/sigmastate/serialization/GroupElementSerializerSpecification.scala
+++ b/sigmastate/src/test/scala/sigmastate/serialization/GroupElementSerializerSpecification.scala
@@ -1,5 +1,6 @@
package sigmastate.serialization
+import sigmastate.crypto.CryptoFacade
import sigmastate.interpreter.CryptoConstants
import sigmastate.eval._
@@ -12,7 +13,7 @@ class GroupElementSerializerSpecification extends SerializationSpecification {
val bytes = GroupElementSerializer.toBytes(identity)
bytes.length shouldBe CryptoConstants.EncodedGroupElementLength
bytes.forall(_ == 0) shouldBe true
- GroupElementSerializer.parse(SigmaSerializer.startReader(bytes, 0)).isInfinity shouldBe true
+ GroupElementSerializer.parse(SigmaSerializer.startReader(bytes, 0)).isIdentity shouldBe true
}
property("point roundtrip") {
@@ -20,8 +21,10 @@ class GroupElementSerializerSpecification extends SerializationSpecification {
val bytes = GroupElementSerializer.toBytes(ge.value)
bytes.length shouldBe CryptoConstants.EncodedGroupElementLength
val restored = GroupElementSerializer.parse(SigmaSerializer.startReader(bytes, 0))
- restored.normalize().getAffineXCoord shouldBe ge.value.normalize().getAffineXCoord
- restored.normalize().getAffineYCoord shouldBe ge.value.normalize().getAffineYCoord
+ CryptoFacade.getAffineXCoord(CryptoFacade.normalizePoint(restored)) shouldBe
+ CryptoFacade.getAffineXCoord(CryptoFacade.normalizePoint(ge.value))
+ CryptoFacade.getAffineYCoord(CryptoFacade.normalizePoint(restored)) shouldBe
+ CryptoFacade.getAffineYCoord(CryptoFacade.normalizePoint(ge.value))
}
}
}
diff --git a/sigmastate/src/test/scala/sigmastate/utxo/ProverSpecification.scala b/sigmastate/src/test/scala/sigmastate/utxo/ProverSpecification.scala
index d5f76503b4..88b29bac4f 100644
--- a/sigmastate/src/test/scala/sigmastate/utxo/ProverSpecification.scala
+++ b/sigmastate/src/test/scala/sigmastate/utxo/ProverSpecification.scala
@@ -5,7 +5,7 @@ import scorex.crypto.hash.Blake2b256
import sigmastate.Values.SigmaBoolean
import sigmastate._
import sigmastate.basics.DLogProtocol.FirstDLogProverMessage
-import sigmastate.basics.{FirstDiffieHellmanTupleProverMessage, SecP256K1}
+import sigmastate.basics.{FirstDiffieHellmanTupleProverMessage, SecP256K1Group}
import sigmastate.helpers.{ErgoLikeTestProvingInterpreter, SigmaTestingCommons}
import sigmastate.interpreter.{HintsBag, ProverInterpreter}
import sigmastate.lang.exceptions.InterpreterException
@@ -38,7 +38,7 @@ class ProverSpecification extends SigmaTestingCommons {
val r = h.ownCommitments.head.secretRandomness
// g^r == a
- SecP256K1.exponentiate(SecP256K1.generator, r) shouldBe a.ecData
+ SecP256K1Group.exponentiate(SecP256K1Group.generator, r) shouldBe a.ecData
val h2 = prover.generateCommitmentsFor(pk3, Seq(pk))
h2.hints.size shouldBe 2
diff --git a/sigmastate/src/test/scala/sigmastate/utxo/examples/DHTupleExampleSpecification.scala b/sigmastate/src/test/scala/sigmastate/utxo/examples/DHTupleExampleSpecification.scala
index 8cd017d6e9..657beb98ab 100644
--- a/sigmastate/src/test/scala/sigmastate/utxo/examples/DHTupleExampleSpecification.scala
+++ b/sigmastate/src/test/scala/sigmastate/utxo/examples/DHTupleExampleSpecification.scala
@@ -35,7 +35,7 @@ class DHTupleExampleSpecification extends SigmaTestingCommons
val x:BigInteger = alice.dlogSecrets.head.w // x is Alice's private key
- val g_x = alicePubKey.h // g_x is Alice's public key (g_x = g^x)
+ val g_x = alicePubKey.value // g_x is Alice's public key (g_x = g^x)
val env = Map(
ScriptNameProp -> "env",
@@ -63,7 +63,7 @@ class DHTupleExampleSpecification extends SigmaTestingCommons
val y:BigInteger = bob.dlogSecrets.head.w // y is Bob's private key
val bobPubKey: ProveDlog = bob.dlogSecrets.head.publicImage
- val g_y = GroupElementConstant(bobPubKey.h) // Bob's public key g^y
+ val g_y = GroupElementConstant(bobPubKey.value) // Bob's public key g^y
val g_xy = GroupElementConstant(dlogGroup.exponentiate(g_x, y)) // g^xy
diff --git a/sigmastate/src/test/scala/sigmastate/utxo/examples/MixExampleSpecification.scala b/sigmastate/src/test/scala/sigmastate/utxo/examples/MixExampleSpecification.scala
index b626751312..99806ca35b 100644
--- a/sigmastate/src/test/scala/sigmastate/utxo/examples/MixExampleSpecification.scala
+++ b/sigmastate/src/test/scala/sigmastate/utxo/examples/MixExampleSpecification.scala
@@ -30,7 +30,7 @@ class MixExampleSpecification extends SigmaTestingCommons
val x: BigInteger = alice.dlogSecrets.head.w // x is Alice's private key
- val gX = alicePubKey.h // g_x is Alice's public key (g_x = g^x)
+ val gX = alicePubKey.value // g_x is Alice's public key (g_x = g^x)
// Alternative 1:
// val g_x = alicePubKey.value
// Alternative 2:
@@ -118,7 +118,7 @@ class MixExampleSpecification extends SigmaTestingCommons
val y: BigInteger = bob.dlogSecrets.head.w // y is Bob's private key
- val gY = GroupElementConstant(bobPubKey.h) // g^y
+ val gY = GroupElementConstant(bobPubKey.value) // g^y
val gY_alt = GroupElementConstant(dlogGroup.exponentiate(g, y))
gY shouldBe gY_alt