sigmastate.interpreter.ProverInterpreter.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sigma-state_2.12 Show documentation
Show all versions of sigma-state_2.12 Show documentation
Interpreter of a Sigma-State language
The newest version!
package sigmastate.interpreter
import sigma.kiama.rewriting.Rewriter.{everywherebu, everywheretd, rule}
import sigma.util.CollectionUtil._
import sigma.VersionContext
import sigma.kiama.rewriting.Strategy
import sigma.data.TrivialProp.{FalseProp, TrueProp}
import sigma.ast._
import sigma.VersionContext.MaxSupportedScriptVersion
import sigmastate._
import sigmastate.crypto.DLogProtocol._
import sigmastate.crypto.VerifierMessage.Challenge
import sigmastate.crypto._
import sigmastate.crypto.{GF2_192, GF2_192_Poly}
import sigmastate.utils.Helpers
import sigma.Coll
import sigma.Extensions.ArrayOps
import sigma.crypto.CryptoConstants
import sigma.data.{CAND, COR, CTHRESHOLD, ProveDHTuple, ProveDlog, SigmaBoolean}
import sigma.exceptions.InterpreterException
import sigma.interpreter.CostedProverResult
import sigma.serialization.SigSerializer
import sigma.util.CollectionUtil
import java.math.BigInteger
import scala.util.Try
// TODO ProverResult was moved from here, compare with new-eval after merge
/**
* Interpreter with enhanced functionality to prove statements.
*/
trait ProverInterpreter extends Interpreter with ProverUtils {
import sigma.crypto.CryptoConstants.secureRandomBytes
import Interpreter._
override type ProofT = UncheckedTree
/** All secrets available for this prover. */
def secrets: Seq[SigmaProtocolPrivateInput[_]]
/**
* Public keys of prover's secrets. This operation can be costly if there are many
* secrets the prover knows, consider re-implementation of this field then.
*/
def publicKeys: Seq[SigmaBoolean] = secrets.map(_.publicImage.asInstanceOf[SigmaBoolean])
/**
* Generate commitments for given ergo tree for prover's secrets.
* The prover is reducing the given tree to crypto-tree by using the given context,
* and then generates commitments.
*/
def generateCommitments(ergoTree: ErgoTree, ctx: CTX): HintsBag = {
generateCommitmentsFor(ergoTree, ctx, publicKeys)
}
/**
* Generate commitments for given crypto-tree (sigma-tree) for prover's secrets.
*/
def generateCommitments(sigmaTree: SigmaBoolean): HintsBag = {
generateCommitmentsFor(sigmaTree, publicKeys)
}
/**
* The comments in this section are taken from the algorithm for the
* Sigma-protocol prover as described in the ErgoScript white-paper
* https://ergoplatform.org/docs/ErgoScript.pdf , Appendix A
*
*/
// TODO: if we are concerned about timing attacks against the prover, we should make sure that this code
// takes the same amount of time regardless of which nodes are real and which nodes are simulated
// In particular, we should avoid the use of exists and forall, because they short-circuit the evaluation
// once the right value is (or is not) found. We should also make all loops look similar, the same
// amount of copying is done regardless of what's real or simulated,
// real vs. simulated computations take the same time, etc.
protected def prove(unprovenTree: UnprovenTree, message: Array[Byte], hintsBag: HintsBag): ProofT = {
// Prover Step 1: Mark as real everything the prover can prove
val step1 = markReal(hintsBag)(unprovenTree).get.asInstanceOf[UnprovenTree]
// Prover Step 2: If the root of the tree is marked "simulated" then the prover does not have enough witnesses
// to perform the proof. Abort.
assert(step1.real, s"Tree root should be real but was $step1")
// Prover Step 3: Change some "real" nodes to "simulated" to make sure each node
// has the right number of simulated children.
val step3 = polishSimulated(step1).get.asInstanceOf[UnprovenTree]
// Prover Steps 4, 5, and 6 together: find challenges for simulated nodes; simulate simulated leaves;
// compute commitments for real leaves
val step6 = simulateAndCommit(hintsBag)(step3).get.asInstanceOf[UnprovenTree]
// Prover Steps 7: convert the relevant information in the tree (namely, tree structure, node types,
// the statements being proven and commitments at the leaves) to a bitstring.
// This bitstring corresponding to a proposition to prove is needed for Strong Fiat-Shamir transformation.
// See [BPW12] paper on Strong vs Weak Fiat-Shamir,
// (https://link.springer.com/content/pdf/10.1007/978-3-642-34961-4_38.pdf)
val propBytes = FiatShamirTree.toBytes(step6)(null/* prove is not profiled */)
// Prover Step 8: compute the challenge for the root of the tree as the Fiat-Shamir hash of propBytes
// and the message being signed.
val rootChallenge = Challenge @@ CryptoFunctions.hashFn(CollectionUtil.concatArrays(propBytes, message)).toColl
val step8 = step6.withChallenge(rootChallenge)
// Prover Step 9: complete the proof by computing challenges at real nodes and additionally responses at real leaves
val step9 = proving(hintsBag)(step8).get.asInstanceOf[ProofTree]
// Syntactic step that performs a type conversion only
convertToUnchecked(step9)
}
def prove(ergoTree: ErgoTree,
context: CTX,
message: Array[Byte],
hintsBag: HintsBag): Try[CostedProverResult] =
prove(emptyEnv, ergoTree, context, message, hintsBag)
def prove(ergoTree: ErgoTree,
context: CTX,
message: Array[Byte]): Try[CostedProverResult] =
prove(emptyEnv, ergoTree, context, message, HintsBag.empty)
def prove(env: ScriptEnv,
ergoTree: ErgoTree,
context: CTX,
message: Array[Byte],
hintsBag: HintsBag = HintsBag.empty): Try[CostedProverResult] = Try {
checkSoftForkCondition(ergoTree, context) match {
case Some(_) =>
throw new InterpreterException(
s"Both ErgoTree version ${ergoTree.version} and activated version " +
s"${context.activatedScriptVersion} is greater than MaxSupportedScriptVersion $MaxSupportedScriptVersion")
case None => // proceed normally
}
VersionContext.withVersions(context.activatedScriptVersion, ergoTree.version) {
val (resValue, resCost) = {
val reduced = fullReduction(ergoTree, context, env)
val fullCost = addCryptoCost(reduced.value, reduced.cost, context.costLimit)
(reduced.value, fullCost)
}
val proof = generateProof(resValue, message, hintsBag)
CostedProverResult(proof, context.extension, resCost)
}
}
def generateProof(sb: SigmaBoolean,
message: Array[Byte],
hintsBag: HintsBag): Array[Byte] = {
val proofTree = sb match {
case TrueProp => NoProof
case FalseProp => syntax.error("Script reduced to false")
case sigmaTree =>
val unprovenTree = convertToUnproven(sigmaTree)
prove(unprovenTree, message, hintsBag)
}
// Prover Step 10: output the right information into the proof
val proof = SigSerializer.toProofBytes(proofTree)
proof
}
/**
* Prover Step 1: This step will mark as "real" every node for which the prover can produce a real proof.
* This step may mark as "real" more nodes than necessary if the prover has more than the minimal
* necessary number of witnesses (for example, more than one child of an OR).
* This will be corrected in the next step.
* In a bottom-up traversal of the tree, do the following for each node:
*
*/
def markReal(hintsBag: HintsBag): Strategy = everywherebu(rule[Any] {
case and: CAndUnproven =>
// If the node is AND, mark it "real" if all of its children are marked real; else mark it "simulated"
val simulated = and.children.exists(_.asInstanceOf[UnprovenTree].simulated)
and.copy(simulated = simulated)
case or: COrUnproven =>
// If the node is OR, mark it "real" if at least one child is marked real; else mark it "simulated"
val simulated = or.children.forall(_.asInstanceOf[UnprovenTree].simulated)
or.copy(simulated = simulated)
case t: CThresholdUnproven =>
// If the node is THRESHOLD(k), mark it "real" if at least k of its children are marked real; else mark it "simulated"
val realCount = t.children.foldLeft(0) { (count, child) =>
count + (if (child.asInstanceOf[UnprovenTree].simulated) 0 else 1)
}
t.copy(simulated = realCount < t.k)
// UnprovenSchnorr | UnprovenDiffieHellmanTuple case
case ul: UnprovenLeaf =>
// If the node is a leaf, mark it "real'' if either the witness for it is available or a hint shows the secret
// is known to an external participant in multi-signing;
// else mark it "simulated"
val isReal = hintsBag.realImages.contains(ul.proposition) || secrets.exists {
case in: SigmaProtocolPrivateInput[_] => in.publicImage == ul.proposition
}
ul.withSimulated(!isReal)
case t: UnprovenTree =>
syntax.error(s"Don't know how to markReal($t)")
})
/**
* Set positions for children of a unproven inner node (conjecture, so AND/OR/THRESHOLD)
*/
protected def setPositions(uc: UnprovenConjecture): UnprovenConjecture = {
val updChildren = uc.children.zipWithIndex.map { case (pt, idx) =>
pt.asInstanceOf[UnprovenTree].withPosition(uc.position.child(idx))
}
uc match {
case and: CAndUnproven => and.copy(children = updChildren)
case or: COrUnproven => or.copy(children = updChildren)
case threshold: CThresholdUnproven => threshold.copy(children = updChildren)
}
}
/**
* Prover Step 3: This step will change some "real" nodes to "simulated" to make sure each node has
* the right number of simulated children. Also, children will get proper position set during this step.
* In a top-down traversal of the tree, do the following for each node:
*/
val polishSimulated: Strategy = everywheretd(rule[Any] {
case and: CAndUnproven =>
// If the node is marked "simulated", mark all of its children "simulated"
val a = if (and.simulated) {
and.copy(children = and.children.map(_.asInstanceOf[UnprovenTree].withSimulated(true)))
} else {
and
}
setPositions(a)
case or: COrUnproven =>
// If the node is marked "simulated", mark all of its children "simulated"
val o = if (or.simulated) {
or.copy(children = or.children.map(_.asInstanceOf[UnprovenTree].withSimulated(true)))
} else {
// If the node is OR marked "real", mark all but one of its children "simulated"
// (the node is guaranteed by step 1 to have at least one "real" child).
// Which particular child is left "real" is not important for security;
// the choice can be guided by efficiency or convenience considerations.
val newChildren = or.children.foldLeft((Seq[UnprovenTree](), false)) { case ((children, realFound), child) =>
val cut = child.asInstanceOf[UnprovenTree]
(realFound, cut.real) match {
case (true, true) => (children :+ cut.withSimulated(true), true)
case (true, false) => (children :+ cut, true)
case (false, true) => (children :+ cut, true)
case (false, false) => (children :+ cut, false)
}
}._1
or.copy(children = newChildren)
}
setPositions(o)
case t: CThresholdUnproven =>
// If the node is marked "simulated", mark all of its children "simulated"
val th = if (t.simulated) {
t.copy(children = t.children.map(_.asInstanceOf[UnprovenTree].withSimulated(true)))
} else {
// If the node is THRESHOLD(k) marked "real", mark all but k of its children "simulated"
// (the node is guaranteed, by the previous step, to have at least k "real" children).
// Which particular ones are left "real" is not important for security;
// the choice can be guided by efficiency or convenience considerations.
//
// We'll mark the first k real ones real
val newChildren = t.children.foldLeft((Seq[UnprovenTree](), 0)) { case ((children, countOfReal), child) =>
val kid = child.asInstanceOf[UnprovenTree]
val (newKid, newCountOfReal) = if (kid.real) {
( { if (countOfReal >= t.k) kid.withSimulated(true) else kid }, countOfReal + 1)
} else {
(kid, countOfReal)
}
(children :+ newKid, newCountOfReal)
}._1
t.copy(children = newChildren)
}
setPositions(th)
case su: UnprovenSchnorr => su
case dhu: UnprovenDiffieHellmanTuple => dhu
case _: UnprovenTree => ???
})
/**
* Prover Step 4: In a top-down traversal of the tree, compute the challenges e for simulated children of every node
* Prover Step 5: For every leaf marked "simulated", use the simulator of the Sigma-protocol for that leaf
* to compute the commitment $a$ and the response z, given the challenge e that is already stored in the leaf.
* Prover Step 6: For every leaf marked "real", use the first prover step of the Sigma-protocol for that leaf to
* compute the commitment a.
*/
def simulateAndCommit(hintsBag: HintsBag): Strategy = everywheretd(rule[Any] {
// Step 4 part 1: If the node is marked "real", then each of its simulated children gets a fresh uniformly
// random challenge in {0,1}^t.
case and: CAndUnproven if and.real => and // A real AND node has no simulated children
//real OR or Threshold case
case uc: UnprovenConjecture if uc.real =>
val newChildren = uc.children.cast[UnprovenTree].map(c =>
if (c.real) {
c
} else {
// take challenge from previously done proof stored in the hints bag,
// or generate random challenge for simulated child
val newChallenge = hintsBag.proofs.find(_.position == c.position).map(_.challenge).getOrElse(
Challenge @@ secureRandomBytes(CryptoFunctions.soundnessBytes).toColl
)
c.withChallenge(newChallenge)
}
)
uc match {
case or: COrUnproven => or.copy(children = newChildren)
case t: CThresholdUnproven => t.copy(children = newChildren)
case _ => ???
}
// Step 4 part 2: If the node is marked "simulated", let e_0 be the challenge computed for it.
// All of its children are simulated, and thus we compute challenges for all
// of them, as follows:
case and: CAndUnproven if and.simulated =>
// If the node is AND, then all of its children get e_0 as the challenge
assert(and.challengeOpt.isDefined)
val challenge = and.challengeOpt.get
val newChildren = and.children.cast[UnprovenTree].map(_.withChallenge(challenge))
and.copy(children = newChildren)
case or: COrUnproven if or.simulated =>
// If the node is OR, then each of its children except one gets a fresh uniformly random
// challenge in {0,1}^t. The remaining child gets a challenge computed as an XOR of the challenges of all
// the other children and e_0.
assert(or.challengeOpt.isDefined)
val unprovenChildren = or.children.cast[UnprovenTree]
val t = unprovenChildren.tail.map(
_.withChallenge(Challenge @@ secureRandomBytes(CryptoFunctions.soundnessBytes).toColl)
)
val toXor: Seq[Coll[Byte]] = or.challengeOpt.get +: t.map(_.challengeOpt.get)
val xoredChallenge = Challenge @@ Helpers.xor(toXor: _*)
val h = unprovenChildren.head.withChallenge(xoredChallenge)
or.copy(children = h +: t)
case t: CThresholdUnproven if t.simulated =>
// The faster algorithm is as follows. Pick n-k fresh uniformly random values
// q_1, ..., q_{n-k} from {0,1}^t and let q_0=e_0.
// Viewing 1, 2, ..., n and q_0, ..., q_{n-k} as elements of GF(2^t),
// evaluate the polynomial Q(x) = sum {q_i x^i} over GF(2^t) at points 1, 2, ..., n
// to get challenges for child 1, 2, ..., n, respectively.
assert(t.challengeOpt.isDefined)
val n = t.children.length
val unprovenChildren = t.children.cast[UnprovenTree]
val q = GF2_192_Poly.fromByteArray(t.challengeOpt.get.toArray, secureRandomBytes(CryptoFunctions.soundnessBytes * (n - t.k)))
val newChildren = unprovenChildren.foldLeft((Seq[UnprovenTree](), 1)) {
case ((childSeq, childIndex), child) =>
(childSeq :+ child.withChallenge(Challenge @@ q.evaluate(childIndex.toByte).toByteArray.toColl), childIndex + 1)
}._1
t.withPolynomial(q).copy(children = newChildren)
// The algorithm with better resistance to timing attacks is as follows.
// Pick n-k fresh uniformly random values e_1, ..., e_{n-k}
// as challenges for the children number 1, ..., n-k.
// Let i_0 = 0. Viewing 0, 1, 2, ..., n and e_0, ..., e_{n-k} as elements of GF(2^t),
// find (via polynomial interpolation) the
// lowest-degree polynomial Q(x)=sum_{i=0}^{n-k} a_i x^i over GF(2^t) that is equal to e_j at j for each j
// from 0 to n-k (this polynomial will have n-k+1 coefficients, and the lowest coefficient will be e_0).
// Set the challenge at child j for n-k
val (newPoints, newValues) =
if (count <= n - t.k) {
(p :+ count.toByte, v :+ new GF2_192(child.challengeOpt.get))
}
else (p, v)
(newPoints, newValues, count + 1)
}
val q = GF2_192_Poly.interpolate(points, values, new GF2_192(t.challengeOpt.get))
val newChildren = unprovenChildren.slice(n-t.k, n).foldLeft((childrenWithRandomChallenges, n-t.k+1)) {
case ((childSeq, childIndex), child) =>
(childSeq :+ child.withChallenge(Challenge @@ q.evaluate(childIndex.toByte).toByteArray), childIndex + 1)
}._1
t.withPolynomial(q).copy(children=newChildren)
*/
case su: UnprovenSchnorr =>
// Steps 5 & 6: first try pulling out commitment from the hints bag. If it exists proceed with it,
// otherwise, compute the commitment (if the node is real) or simulate it (if the node is simulated)
// Step 6 (real leaf -- compute the commitment a or take it from the hints bag)
hintsBag.commitments.find(_.position == su.position).map { cmtHint =>
su.copy(commitmentOpt = Some(cmtHint.commitment.asInstanceOf[FirstDLogProverMessage]))
}.getOrElse {
if (su.simulated) {
// Step 5 (simulated leaf -- complete the simulation)
assert(su.challengeOpt.isDefined)
val (fm, sm) = DLogProver.simulate(su.proposition, su.challengeOpt.get)
UncheckedSchnorr(su.proposition, Some(fm), su.challengeOpt.get, sm)
} else {
// Step 6 -- compute the commitment
val (r, commitment) = DLogProver.firstMessage()
su.copy(commitmentOpt = Some(commitment), randomnessOpt = Some(r))
}
}
case dhu: UnprovenDiffieHellmanTuple =>
//Steps 5 & 6: pull out commitment from the hints bag, otherwise, compute the commitment(if the node is real),
// or simulate it (if the node is simulated)
// Step 6 (real leaf -- compute the commitment a or take it from the hints bag)
hintsBag.commitments.find(_.position == dhu.position).map { cmtHint =>
dhu.copy(commitmentOpt = Some(cmtHint.commitment.asInstanceOf[FirstDHTupleProverMessage]))
}.getOrElse {
if (dhu.simulated) {
// Step 5 (simulated leaf -- complete the simulation)
assert(dhu.challengeOpt.isDefined)
val (fm, sm) = DiffieHellmanTupleProver.simulate(dhu.proposition, dhu.challengeOpt.get)
UncheckedDiffieHellmanTuple(dhu.proposition, Some(fm), dhu.challengeOpt.get, sm)
} else {
// Step 6 -- compute the commitment
val (r, fm) = DiffieHellmanTupleProver.firstMessage(dhu.proposition)
dhu.copy(commitmentOpt = Some(fm), randomnessOpt = Some(r))
}
}
case t: ProofTree => syntax.error(s"Don't know how to challengeSimulated($t)")
})
private def extractChallenge(pt: ProofTree): Option[Challenge] = pt match {
case upt: UnprovenTree => upt.challengeOpt
case sn: UncheckedSchnorr => Some(sn.challenge)
case dh: UncheckedDiffieHellmanTuple => Some(dh.challenge)
case _ => syntax.error(s"Cannot extractChallenge($pt)")
}
/**
* Prover Step 9: Perform a top-down traversal of only the portion of the tree marked "real" in order to compute
* the challenge e for every node marked "real" below the root and, additionally, the response z for every leaf
* marked "real"
*/
def proving(hintsBag: HintsBag): Strategy = everywheretd(rule[Any] {
// If the node is a non-leaf marked real whose challenge is e_0, proceed as follows:
case and: CAndUnproven if and.real =>
assert(and.challengeOpt.isDefined)
// If the node is AND, let each of its children have the challenge e_0
val andChallenge = and.challengeOpt.get
and.copy(children = and.children.map(_.asInstanceOf[UnprovenTree].withChallenge(andChallenge)))
case or: COrUnproven if or.real =>
// If the node is OR, it has only one child marked "real".
// Let this child have the challenge equal to the XOR of the challenges of all the other children and e_0
assert(or.challengeOpt.isDefined)
val rootChallenge = or.challengeOpt.get
val challenge = Challenge @@ Helpers.xor(rootChallenge +: or.children.flatMap(extractChallenge): _*)
or.copy(children = or.children.map {
case r: UnprovenTree if r.real => r.withChallenge(challenge)
case p: ProofTree => p
})
case t: CThresholdUnproven if t.real =>
// If the node is THRESHOLD(k), number its children from 1 to no. Let i_1,..., i_{n-k}
// be the indices of the children marked `"simulated" and e_1, ..., e_{n-k} be their corresponding challenges.
// Let i_0 = 0. Viewing 0, 1, 2, ..., n and e_0, ..., e_{n-k} as elements of GF(2^t),
// find (via polynomial interpolation) the lowest-degree polynomial
// Q(x)=sum_{i=0}^{n-k} a_i x^i over GF(2^t) that is equal to e_j at i_j for each f from 0 to n-k
// (this polynomial will have n-k+1 coefficients, and the lowest coefficient will be e_0). For child number
// i of the node, if the child is marked "real", compute its challenge as Q(i) (if the child is marked
// "simulated", its challenge is already Q(i), by construction of Q).
assert(t.challengeOpt.isDefined)
val (points, values, _) = t.children.foldLeft(Array[Byte](), Array[GF2_192](), 1) {
case ((p, v, count), child) =>
val (newPoints, newValues) = {
// This is the easiest way to find out whether a child is simulated -- just to check if it alread
// has a challenge. Other ways are more of a pain because the children can be of different types
val challengeOpt = extractChallenge(child)
if (challengeOpt.isEmpty) (p, v)
else (p :+ count.toByte, v :+ new GF2_192(challengeOpt.get.toArray))
}
(newPoints, newValues, count + 1)
}
val q = GF2_192_Poly.interpolate(points, values, new GF2_192(t.challengeOpt.get.toArray))
val newChildren = t.children.foldLeft(Seq[ProofTree](), 1) {
case ((s, count), child) =>
val newChild = child match {
case r: UnprovenTree if r.real => r.withChallenge(Challenge @@ q.evaluate(count.toByte).toByteArray.toColl)
case p: ProofTree => p
}
(s :+ newChild, count + 1)
}._1
t.withPolynomial(q).copy(children = newChildren)
// If the node is a leaf marked "real", compute its response according to the second prover step
// of the Sigma-protocol given the commitment, challenge, and witness, or pull response from the hints bag
case su: UnprovenSchnorr if su.real =>
assert(su.challengeOpt.isDefined, s"Real UnprovenSchnorr $su should have challenge defined")
val privKeyOpt = secrets
.filter(_.isInstanceOf[DLogProverInput])
.find(_.asInstanceOf[DLogProverInput].publicImage == su.proposition)
val z = privKeyOpt match {
case Some(privKey: DLogProverInput) =>
hintsBag.ownCommitments.find(_.position == su.position).map { oc =>
DLogProver.secondMessage(
privKey,
oc.secretRandomness,
su.challengeOpt.get)
}.getOrElse {
DLogProver.secondMessage(
privKey,
su.randomnessOpt.get,
su.challengeOpt.get)
}
case _ =>
hintsBag.realProofs.find(_.position == su.position).map { proof =>
val provenSchnorr = proof.uncheckedTree.asInstanceOf[UncheckedSchnorr]
provenSchnorr.secondMessage
}.getOrElse {
val bs = secureRandomBytes(32)
SecondDLogProverMessage(new BigInteger(1, bs).mod(CryptoConstants.groupOrder))
}
}
UncheckedSchnorr(su.proposition, None, su.challengeOpt.get, z)
// If the node is a leaf marked "real", compute its response according to the second prover step
// of the Sigma-protocol given the commitment, challenge, and witness, or pull response from the hints bag
case dhu: UnprovenDiffieHellmanTuple if dhu.real =>
assert(dhu.challengeOpt.isDefined, s"Real UnprovenDiffieHellmanTuple $dhu should have challenge defined")
val privKeyOpt = secrets
.filter(_.isInstanceOf[DiffieHellmanTupleProverInput])
.find(_.asInstanceOf[DiffieHellmanTupleProverInput].publicImage == dhu.proposition)
val z = privKeyOpt match {
case Some(privKey) =>
hintsBag.ownCommitments.find(_.position == dhu.position).map { oc =>
DiffieHellmanTupleProver.secondMessage(
privKey.asInstanceOf[DiffieHellmanTupleProverInput],
oc.secretRandomness,
dhu.challengeOpt.get)
}.getOrElse {
DiffieHellmanTupleProver.secondMessage(
privKey.asInstanceOf[DiffieHellmanTupleProverInput],
dhu.randomnessOpt.get,
dhu.challengeOpt.get)
}
case None =>
hintsBag.realProofs.find(_.position == dhu.position).map { proof =>
val provenSchnorr = proof.uncheckedTree.asInstanceOf[UncheckedDiffieHellmanTuple]
provenSchnorr.secondMessage
}.getOrElse {
val bs = secureRandomBytes(32)
SecondDHTupleProverMessage(new BigInteger(1, bs).mod(CryptoConstants.groupOrder))
}
}
UncheckedDiffieHellmanTuple(dhu.proposition, None, dhu.challengeOpt.get, z)
// if the simulated node is proven by someone else, take it from hints bag
case su: UnprovenLeaf if su.simulated =>
hintsBag.simulatedProofs.find(_.image == su.proposition).map { proof =>
proof.uncheckedTree
}.getOrElse(su)
case sn: UncheckedSchnorr => sn
case dh: UncheckedDiffieHellmanTuple => dh
case ut: UnprovenTree => ut
case t: ProofTree =>
logMessage(s"Wrong input in prove(): $t");
???
})
//converts SigmaTree => UnprovenTree
def convertToUnproven(sigmaTree: SigmaBoolean): UnprovenTree = sigmaTree match {
case and@CAND(sigmaTrees) =>
CAndUnproven(and, None, simulated = false, sigmaTrees.map(convertToUnproven))
case or@COR(children) =>
COrUnproven(or, None, simulated = false, children.map(convertToUnproven))
case threshold@CTHRESHOLD(k, children) =>
CThresholdUnproven(threshold, None, simulated = false, k, children.map(convertToUnproven), None)
case ci: ProveDlog =>
UnprovenSchnorr(ci, None, None, None, simulated = false)
case dh: ProveDHTuple =>
UnprovenDiffieHellmanTuple(dh, None, None, None, simulated = false)
case _ =>
syntax.error(s"Cannot convertToUnproven($sigmaTree)")
}
//converts ProofTree => UncheckedSigmaTree
def convertToUnchecked(proofTree: ProofTree): UncheckedSigmaTree = proofTree match {
case and: CAndUnproven =>
CAndUncheckedNode(and.challengeOpt.get, and.children.map(convertToUnchecked))
case or: COrUnproven =>
COrUncheckedNode(or.challengeOpt.get, or.children.map(convertToUnchecked))
case t: CThresholdUnproven =>
CThresholdUncheckedNode(t.challengeOpt.get, t.children.map(convertToUnchecked), t.k, t.polynomialOpt)
case s: UncheckedSchnorr => s
case d: UncheckedDiffieHellmanTuple => d
case a: Any =>
syntax.error(s"Cannot convertToUnproven($a)")
}
/**
*
* Sign arbitrary message under a key representing a statement provable via a sigma-protocol.
*
* @param sigmaTree - public key
* @param message - message to sign
* @param hintsBag - additional hints for a signer (useful for distributed signing)
* @return - signature or error
*/
def signMessage(sigmaTree: SigmaBoolean,
message: Array[Byte],
hintsBag: HintsBag): Try[Array[Byte]] = Try {
val unprovenTree = convertToUnproven(sigmaTree)
val proofTree = prove(unprovenTree, message, hintsBag)
SigSerializer.toProofBytes(proofTree)
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy