<commit_before>import shapeless._
import nat._
import syntax.sized._
import ops.nat._
import LT._
import com.github.nscala_time.time.Imports._
import com.typesafe.config.ConfigFactory
import scala.concurrent._
import scala.concurrent.ExecutionContext.Implicits.global

import ch.bfh.unicrypt.math.algebra.multiplicative.classes.GStarModSafePrime
import ch.bfh.unicrypt.crypto.schemes.encryption.classes.ElGamalEncryptionScheme
import ch.bfh.unicrypt.math.algebra.general.interfaces.Element
import ch.bfh.unicrypt.math.algebra.general.classes.Pair
import ch.bfh.unicrypt.math.algebra.general.classes.Tuple
import ch.bfh.unicrypt.crypto.encoder.classes.ZModPrimeToGStarModSafePrime
import ch.bfh.unicrypt.crypto.encoder.interfaces.Encoder
import mpservice.MPBridgeS
import mpservice.MPBridge


/**
 * An election process DEMO
 *
 * Simulates the steps in the election from public key generation all the way to decryption
 *
 * Things that are included in this demo are:
 *
 * - A typed purely functional data structure modeling the election process and bulletin board (see below)
 *
 * - Cryptography for
 *
 *  a) encrypting votes
 *  b) creating keyshares, proofs and verification
 *  c) shuffling votes, proofs and verification
 *  d) joint (partial) decryption, proofs and verification
 *
 * - Not included
 *
 * Remoting (everything simulated with method calls)
 * Signatures and authentication
 * Error handling
 * Proofs of knowledge of plaintext and verification in vote casting
 *
 *
 * An election is modeled as a typed, purely functional sequential state machine. We use shapeless
 * encoding of natural numbers to provide length-typed lists (aka dependent types), that way we get:
 *
 * 1) The election process logic is captured by types, so illegal transitions
 *    are caught by the compiler and inconsistent states are not possible, for example
 *
 *    It is a compile-time error to try to construct the public key without all the shares
 *    It is a compile-time error to add more shares,shuffles or decryptions than expected
 *    It is a compile-error to start an election with no public key
 *    It is a compile-time error to decrypt without shuffling
 *    etc.
 *
 * 2) Because the election is purely functional, the entire history of the election
 *    can be reconstructed or replayed. A purely functional data structure is in this
 *    sense a general case of an immutable log
 *
 *
 * This demo uses two trustees, ElectionTest3 below shows how number of trustees generalizes
 */
object ElectionTest extends App {

  val config = ConfigFactory.load()
  val useGmp = config.getBoolean("use-gmp")
  MPBridge.gmpModPow = useGmp
  MPBridgeS.init

  val totalVotes = args.toList.lift(0).getOrElse("100").toInt

  // create the keymakers
  // these are responsible for distributed key generation and joint decryption
  val k1 = new KeyMakerTrustee("keymaker one")
  val k2 = new KeyMakerTrustee("keymaker two")

  // create the mixers
  // these are responsible for shuffling the votes
  val m1 = new MixerTrustee("mixer one")
  val m2 = new MixerTrustee("mixer two")

  // create the election,
  // we are using privacy level 2, two trustees of each kind
  // we are 2048 bits for the size of the group modulus
  val start = Election.create[_2]("my election", 2048)

  // the election is now ready to receive key shares
  val readyForShares = Election.startShares(start)

  // each keymaker creates the shares and their proofs, these are added to the election
  val oneShare = Election.addShare(readyForShares, k1.createKeyShare(readyForShares), k1.id)
  val twoShares = Election.addShare(oneShare, k2.createKeyShare(readyForShares), k2.id)

  // combine the shares from the keymaker trustees, this produces the election public key
  val combined = Election.combineShares(twoShares)

  // since we are storing information in election as if it were a bulletin board, all
  // the data is stored in a wire-compatible format, that is strings/jsons whatever
  // we reconstruct the public key as if it had been read from such a format
  val publicKey = Util.getPublicKeyFromString(combined.state.publicKey, combined.state.cSettings.generator)

  // open the election period
  val startVotes = Election.startVotes(combined)

  // generate dummy votes
  val plaintexts = Seq.fill(totalVotes)(scala.util.Random.nextInt(10))

  // encrypt the votes with the public key of the election
  val votes = Util.encryptVotes(plaintexts, combined.state.cSettings, publicKey)
  println(votes.length)

  // add the votes to the election
  var electionGettingVotes = startVotes
  votes.foreach { v =>
    electionGettingVotes = Election.addVotes(electionGettingVotes, v.convertToString)
  }

  // we are only timing the mixing phase
  val mixingStart = System.currentTimeMillis()
  MPBridge.total = 0;  
  // wait for keystroke, this allows us to attach a profiler at the right time
  // println("Hit return to start")
  // Console.in.read()

  // stop the voting period
  val stopVotes = Election.stopVotes(electionGettingVotes)

  // we can start preshuffling for both mixers
  // that way the second preshuffle will be concurrent with the first mix
  val (predata1, proof1) = m1.preShuffleVotes(stopVotes)
  val (predata2, proof2) = m2.preShuffleVotes(stopVotes)

  // prepare for mixing
  val startMix = Election.startMixing(stopVotes)

  // each mixing trustee extracts the needed information from the election
  // and performs the shuffle and proofs
  val shuffle1 = m2.shuffleVotes(startMix, predata1, proof1)
  
  val mixing = shuffle1.map { shuffle =>  
    // the proof is verified and the shuffle is then added to the election, advancing its state
    Election.addMix(startMix, shuffle, m1.id)
  }.flatMap { mixOne =>
    
    // each mixing trustee extracts the needed information from the election
    // and performs the shuffle and proofs
    m2.shuffleVotes(mixOne, predata2, proof2).map { shuffle => 
      // the proof is verified and the shuffle is then added to the election, advancing its state
      Election.addMix(mixOne, shuffle, m2.id)
    }
  }

  mixing.flatMap { mixTwo =>

    // we are done mixing
    val stopMix = Election.stopMixing(mixTwo)

    val mixingEnd = System.currentTimeMillis()
    
    // start the partial decryptions
    // if we tried to do this before the mixing was completed, the compiler would protest
    val startDecryptions = Election.startDecryptions(stopMix)

    // each keymaker trustee extracts the votes from the last shuffle from the election and
    // uses their private keys to do the partial decryption and create proofs
    val pd1Future = Future { k1.partialDecryption(startDecryptions) }
    val pd2Future = Future { k2.partialDecryption(startDecryptions) }

    // the two decryption futures execute in parallel
    val decryptions = for {
      pd1 <- pd1Future
      pd2 <- pd2Future
    } yield(pd1, pd2)
    
    decryptions.map { case (pd1, pd2) => 
      val partialOne = Election.addDecryption(startDecryptions, pd1, k1.id)
      val partialTwo = Election.addDecryption(partialOne, pd2, k2.id)

      // the partial decryptions are combined, yielding the plaintexts
      val electionDone = Election.combineDecryptions(partialTwo)

      // lets check that everything went well
      println(s"Plaintexts $plaintexts")
      println(s"Decrypted ${electionDone.state.decrypted}")
      println("ok: " + (plaintexts.sorted == electionDone.state.decrypted.map(_.toInt).sorted))

      val mixTime = (mixingEnd - mixingStart) / 1000.0
      val totalTime = (System.currentTimeMillis() - mixingStart) / 1000.0
<commit_msg>timings<commit_after>import shapeless._
import nat._
import syntax.sized._
import ops.nat._
import LT._
import com.github.nscala_time.time.Imports._
import com.typesafe.config.ConfigFactory
import scala.concurrent._
import scala.concurrent.ExecutionContext.Implicits.global

import ch.bfh.unicrypt.math.algebra.multiplicative.classes.GStarModSafePrime
import ch.bfh.unicrypt.crypto.schemes.encryption.classes.ElGamalEncryptionScheme
import ch.bfh.unicrypt.math.algebra.general.interfaces.Element
import ch.bfh.unicrypt.math.algebra.general.classes.Pair
import ch.bfh.unicrypt.math.algebra.general.classes.Tuple
import ch.bfh.unicrypt.crypto.encoder.classes.ZModPrimeToGStarModSafePrime
import ch.bfh.unicrypt.crypto.encoder.interfaces.Encoder
import mpservice.MPBridgeS
import mpservice.MPBridge


/**
 * An election process DEMO
 *
 * Simulates the steps in the election from public key generation all the way to decryption
 *
 * Things that are included in this demo are:
 *
 * - A typed purely functional data structure modeling the election process and bulletin board (see below)
 *
 * - Cryptography for
 *
 *  a) encrypting votes
 *  b) creating keyshares, proofs and verification
 *  c) shuffling votes, proofs and verification
 *  d) joint (partial) decryption, proofs and verification
 *
 * - Not included
 *
 * Remoting (everything simulated with method calls)
 * Signatures and authentication
 * Error handling
 * Proofs of knowledge of plaintext and verification in vote casting
 *
 *
 * An election is modeled as a typed, purely functional sequential state machine. We use shapeless
 * encoding of natural numbers to provide length-typed lists (aka dependent types), that way we get:
 *
 * 1) The election process logic is captured by types, so illegal transitions
 *    are caught by the compiler and inconsistent states are not possible, for example
 *
 *    It is a compile-time error to try to construct the public key without all the shares
 *    It is a compile-time error to add more shares,shuffles or decryptions than expected
 *    It is a compile-error to start an election with no public key
 *    It is a compile-time error to decrypt without shuffling
 *    etc.
 *
 * 2) Because the election is purely functional, the entire history of the election
 *    can be reconstructed or replayed. A purely functional data structure is in this
 *    sense a general case of an immutable log
 *
 *
 * This demo uses two trustees, ElectionTest3 below shows how number of trustees generalizes
 */
object ElectionTest extends App {

  val config = ConfigFactory.load()
  val useGmp = config.getBoolean("use-gmp")
  MPBridge.gmpModPow = useGmp
  MPBridgeS.init

  val totalVotes = args.toList.lift(0).getOrElse("100").toInt

  // create the keymakers
  // these are responsible for distributed key generation and joint decryption
  val k1 = new KeyMakerTrustee("keymaker one")
  val k2 = new KeyMakerTrustee("keymaker two")

  // create the mixers
  // these are responsible for shuffling the votes
  val m1 = new MixerTrustee("mixer one")
  val m2 = new MixerTrustee("mixer two")

  // create the election,
  // we are using privacy level 2, two trustees of each kind
  // we are 2048 bits for the size of the group modulus
  val start = Election.create[_2]("my election", 2048)

  // the election is now ready to receive key shares
  val readyForShares = Election.startShares(start)

  // each keymaker creates the shares and their proofs, these are added to the election
  val oneShare = Election.addShare(readyForShares, k1.createKeyShare(readyForShares), k1.id)
  val twoShares = Election.addShare(oneShare, k2.createKeyShare(readyForShares), k2.id)

  // combine the shares from the keymaker trustees, this produces the election public key
  val combined = Election.combineShares(twoShares)

  // since we are storing information in election as if it were a bulletin board, all
  // the data is stored in a wire-compatible format, that is strings/jsons whatever
  // we reconstruct the public key as if it had been read from such a format
  val publicKey = Util.getPublicKeyFromString(combined.state.publicKey, combined.state.cSettings.generator)

  // open the election period
  val startVotes = Election.startVotes(combined)

  // generate dummy votes
  val plaintexts = Seq.fill(totalVotes)(scala.util.Random.nextInt(10))

  // encrypt the votes with the public key of the election
  val votes = Util.encryptVotes(plaintexts, combined.state.cSettings, publicKey)
  println(votes.length)

  // add the votes to the election
  var electionGettingVotes = startVotes
  votes.foreach { v =>
    electionGettingVotes = Election.addVotes(electionGettingVotes, v.convertToString)
  }

  // we are only timing the mixing phase
  val mixingStart = System.currentTimeMillis()
  MPBridge.total = 0;  
  // wait for keystroke, this allows us to attach a profiler at the right time
  // println("Hit return to start")
  // Console.in.read()

  // stop the voting period
  val stopVotes = Election.stopVotes(electionGettingVotes)

  // we can start preshuffling for both mixers
  // that way the second preshuffle will be concurrent with the first mix
  val (predata1, proof1) = m1.preShuffleVotes(stopVotes)
  val (predata2, proof2) = m2.preShuffleVotes(stopVotes)

  // prepare for mixing
  val startMix = Election.startMixing(stopVotes)

  // each mixing trustee extracts the needed information from the election
  // and performs the shuffle and proofs
  val shuffle1 = m2.shuffleVotes(startMix, predata1, proof1)
  
  val mixing = shuffle1.map { shuffle =>  
    // the proof is verified and the shuffle is then added to the election, advancing its state
    Election.addMix(startMix, shuffle, m1.id)
  }.flatMap { mixOne =>
    
    // each mixing trustee extracts the needed information from the election
    // and performs the shuffle and proofs
    m2.shuffleVotes(mixOne, predata2, proof2).map { shuffle => 
      // the proof is verified and the shuffle is then added to the election, advancing its state
      Election.addMix(mixOne, shuffle, m2.id)
    }
  }

  mixing.flatMap { mixTwo =>

    // we are done mixing
    val stopMix = Election.stopMixing(mixTwo)

    val mixingEnd = System.currentTimeMillis()
    
    // start the partial decryptions
    // if we tried to do this before the mixing was completed, the compiler would protest
    val startDecryptions = Election.startDecryptions(stopMix)

    // each keymaker trustee extracts the votes from the last shuffle from the election and
    // uses their private keys to do the partial decryption and create proofs
    val pd1Future = Future { k1.partialDecryption(startDecryptions) }
    val pd2Future = Future { k2.partialDecryption(startDecryptions) }

    // the two decryption futures execute in parallel
    val decryptions = for {
      pd1 <- pd1Future
      pd2 <- pd2Future
    } yield(pd1, pd2)
    
    decryptions.map { case (pd1, pd2) => 
      val partialOne = Election.addDecryption(startDecryptions, pd1, k1.id)
      val partialTwo = Election.addDecryption(partialOne, pd2, k2.id)

      // the partial decryptions are combined, yielding the plaintexts
      val electionDone = Election.combineDecryptions(partialTwo)

      // lets check that everything went well
      // println(s"Plaintexts $plaintexts")
      // println(s"Decrypted ${electionDone.state.decrypted}")
      // println("ok: " + (plaintexts.sorted == electionDone.state.decrypted.map(_.toInt).sorted))

      val mixTime = (mixingEnd - mixingStart) / 1000.0
      val totalTime = (System.currentTimeMillis() - mixingStart) / 1000.0

{'fraction_non_alphanumeric': 0.07473192553121505, 'fraction_numerical': 0.009209920399973686, 'mean_word_length': 3.718187461204221, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5254696', 'n_tokens_mistral': 4565, 'n_tokens_neox': 4297, 'n_words': 1592}