<commit_before>package org.nvotes.trustee

import io.circe._, io.circe.generic.auto._, io.circe.parser._, io.circe.syntax._

import ch.bfh.unicrypt.math.algebra.multiplicative.classes.GStarModSafePrime
import org.nvotes.libmix._

import java.nio.charset.StandardCharsets
import scala.collection.mutable.ListBuffer

import org.slf4j.Logger
import org.slf4j.LoggerFactory

/** The configuration for a protocol run, typically for an election
 *
 *  Some of these are parameterized by authority and item, needing
 *  methods.
 *
 *  The trustees and ballotbox field are public keys. They must be formatted without
 *  spaces, using \n as markers for newlines. Read by Crypto.ReadPublicRSA
 *  (sublime -> replace \n with \\n)
 */
case class Config(id: String, name: String, bits: Int, items: Int, ballotbox: String, trustees: Array[String]) {
  override def toString() = s"Config($id $name $bits $items)"
}

/** A share of the distributed key.
 *
 *  The public part is stored as an nMix EncryptionKeyShareDTO , which
 *  contains the share and the proof of knowledge.
 *
 *  The private part is aes encrypted by the authority.
 *
 */
case class Share(share: EncryptionKeyShareDTO, encryptedPrivateKey: String)

/** Ballots provided by the ballotbox in unicrypt format. Encrypted */
case class Ballots(ballots: Seq[String])

/** Plaintexts jointly encrypted by authorities after mixing, in unicrypt format */
case class Plaintexts(plaintexts: Seq[String])

/** Convenience class to pass around relevant data  */
case class Context(config: Config, section: BoardSection, trusteeCfg: TrusteeConfig, position: Int, cSettings: CryptoSettings)

/** Implements the cryptographic protocol through stateless, reactive and
 *  choreographed actors.
 *
 *  At a high level the steps are
 *
 *  1. Public key generation
 *  2. Ballot casting (provided externally)
 *  3. Mixing
 *  4. Joint decryption
 *
 *  The protocol is implemented with rules of the form
 *
 *    Condition => Action
 *
 *  which operate on bulletin board data.
 *
 *  Because actors are stateless, Actions are responsible
 *  for verifying correctness of the execution by validating
 *  the bulletin board data from scratch at each execution.
 */
object Protocol extends Names {

  val logger = LoggerFactory.getLogger(Protocol.getClass)

  /** Executes one run of the protocol rules for one board section
   *
   *  The bulletin board is first updated (sync). Then rules are then evaluated against
   *  the updated data.
   *
   *  Global rules depend on data that is global to the section
   *  Item rules depend only on item data
   */
  def execute(section: BoardSection, trusteeCfg: TrusteeConfig): Unit = {

    logger.info(s"Begin executing protocol for section '${section.name}'")
    logger.info(s"Syncing...'${section.name}'")

    section.sync()
    val files = section.getFileSet

    if(!files.contains(CONFIG)) {
      logger.error(s"Section ${section.name} does not have a config")
      // FIXME cause a real error
      return
    }

    val configString = section.getConfig.get
    val config = decode[Config](configString).right.get
    logger.info(s"Found config: $config")
    val position = getMyPosition(config, trusteeCfg)

    if(position == 0) {
      logger.info(s"could not find self in list of trustees for config $config")
      // FIXME cause a real error
      return
    }
    // FIXME this info should be in config, not just the bits
    val group = GStarModSafePrime.getFirstInstance(config.bits)
    val generator = group.getDefaultGenerator()
    val cSettings = CryptoSettings(group, generator)

    val ctx = Context(config, section, trusteeCfg, position, cSettings)

    logger.info(s"Evaluating global rules..")
    val rules = globalRules(ctx)
    // first rule that matches is executed
    val hit = rules.find{ case (c, a) =>
      c.eval(files)
    }.map(_._2)

    logger.info(s"Executing global hits: $hit")
    val result = hit.map(_.execute()).getOrElse(Ok)

    if(result != Ok) {
      logger.warn(s"Got pause or error signal: $result")
      return
    }
    logger.info(s"Global rules result: $result")

    val items = config.items

    logger.info(s"Evaluating per-item rules..")
    // val irules = (1 to items).map(i => itemRules(ctx, i, files))
    // using permutation
    val irules = (1 to items).map(i => itemRules(ctx, i, files))

    // get first rule that matches for each item, then
    // collect Action's into list and sort them by priority
    val hits = irules.flatMap { rules =>
      rules.find{ case (c, a) =>
        c.eval(files)
      }.map(_._2)
    }.sorted

    logger.info(s"Per-item hits: ${hits.map(_.getClass)}")<commit_msg>Faster random ballot generation<commit_after>package org.nvotes.trustee

import io.circe._, io.circe.generic.auto._, io.circe.parser._, io.circe.syntax._

import ch.bfh.unicrypt.math.algebra.multiplicative.classes.GStarModSafePrime
import org.nvotes.libmix._

import java.nio.charset.StandardCharsets
import scala.collection.mutable.ListBuffer

import org.slf4j.Logger
import org.slf4j.LoggerFactory

/** The configuration for a protocol run, typically for an election
 *
 *  Some of these are parameterized by authority and item, needing
 *  methods.
 *
 *  The trustees and ballotbox field are public keys. They must be formatted without
 *  spaces, using \n as markers for newlines. Read by Crypto.ReadPublicRSA
 *  (sublime -> replace \n with \\n)
 */
case class Config(id: String, name: String, bits: Int, items: Int, ballotbox: String, trustees: Array[String]) {
  override def toString() = s"Config($id $name $bits $items)"
}

/** A share of the distributed key.
 *
 *  The public part is stored as an nMix EncryptionKeyShareDTO , which
 *  contains the share and the proof of knowledge.
 *
 *  The private part is aes encrypted by the authority.
 *
 */
case class Share(share: EncryptionKeyShareDTO, encryptedPrivateKey: String)

/** Ballots provided by the ballotbox in unicrypt format. Encrypted */
case class Ballots(ballots: Seq[String])

/** Plaintexts jointly encrypted by authorities after mixing, in unicrypt format */
case class Plaintexts(plaintexts: Seq[String])

/** Convenience class to pass around relevant data  */
case class Context(config: Config, section: BoardSection, trusteeCfg: TrusteeConfig, position: Int, cSettings: CryptoSettings)

/** Implements the cryptographic protocol through stateless, reactive and
 *  choreographed actors.
 *
 *  At a high level the steps are
 *
 *  1. Public key generation
 *  2. Ballot casting (provided externally)
 *  3. Mixing
 *  4. Joint decryption
 *
 *  The protocol is implemented with rules of the form
 *
 *    Condition => Action
 *
 *  which operate on bulletin board data.
 *
 *  Because actors are stateless, Actions are responsible
 *  for verifying correctness of the execution by validating
 *  the bulletin board data from scratch at each execution.
 */
object Protocol extends Names {

  val logger = LoggerFactory.getLogger(Protocol.getClass)

  /** Executes one run of the protocol rules for one board section
   *
   *  The bulletin board is first updated (sync). Then rules are then evaluated against
   *  the updated data.
   *
   *  Global rules depend on data that is global to the section
   *  Item rules depend only on item data
   */
  def execute(section: BoardSection, trusteeCfg: TrusteeConfig): Unit = {

    logger.info(s"Begin executing protocol for section '${section.name}'")
    logger.info(s"Syncing...'${section.name}'")

    section.sync()
    val files = section.getFileSet

    if(!files.contains(CONFIG)) {
      logger.error(s"Section ${section.name} does not have a config")
      // FIXME cause a real error
      return
    }

    val configString = section.getConfig.get
    val config = decode[Config](configString).right.get
    logger.info(s"Found config: $config")
    val position = getMyPosition(config, trusteeCfg)

    if(position == 0) {
      logger.info(s"could not find self in list of trustees for config $config")
      // FIXME cause a real error
      return
    }
    // FIXME this info should be in config, not just the bits
    val group = GStarModSafePrime.getFirstInstance(config.bits)
    val generator = group.getDefaultGenerator()
    val cSettings = CryptoSettings(group, generator)

    val ctx = Context(config, section, trusteeCfg, position, cSettings)

    logger.info(s"Evaluating global rules..")
    val rules = globalRules(ctx)
    // first rule that matches is executed
    val hit = rules.find{ case (c, a) =>
      c.eval(files)
    }.map(_._2)

    logger.info(s"Executing global hits: $hit")
    val result = hit.map(_.execute()).getOrElse(Ok)

    if(result != Ok) {
      logger.warn(s"Got pause or error signal: $result")
      return
    }
    logger.info(s"Global rules result: $result")

    val items = config.items

    logger.info(s"Evaluating per-item rules..")
    // val irules = (1 to items).map(i => itemRules(ctx, i, files))
    // using permutation
    val irules = (1 to items).map(i => itemRulesAlt(ctx, i, files))

    // get first rule that matches for each item, then
    // collect Action's into list and sort them by priority
    val hits = irules.flatMap { rules =>
      rules.find{ case (c, a) =>
        c.eval(files)
      }.map(_._2)
    }.sorted

    logger.info(s"Per-item hits: ${hits.map(_.getClass)}")
{'fraction_non_alphanumeric': 0.10050251256281408, 'fraction_numerical': 0.002352186464236074, 'mean_word_length': 3.4122641509433964, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 17, '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': '5691802', 'n_tokens_mistral': 2803, 'n_tokens_neox': 2701, 'n_words': 972}