diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..3356f23 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,27 @@ +## Contributing + +This project welcomes contributions and suggestions. Before you get started, you should read the [readme](README.md) and [ElectionGuard Repository](https://github.com/microsoft/electionguard). + +- 🤔 **CONSIDER** adding a unit test if your PR resolves an issue. +- ✅ **DO** check open PR's to avoid duplicates. +- ✅ **DO** keep pull requests small so they can be easily reviewed. +- ✅ **DO** build locally before pushing. +- ✅ **DO** make sure tests pass. +- ✅ **DO** make sure any new changes are documented. +- ✅ **DO** make sure not to introduce any compiler warnings. +- ❌**AVOID** breaking the continuous integration build. +- ❌**AVOID** making significant changes to the overall architecture. + +### Creating a Pull Request + +All pull requests should have an accompanying issue. Create one if there is not one matching your code. The code will be checked by continuous integration. Once this CI passes, the code will be reviewed, ideally approved, then merged. + +### CLA + +Open source contributions require you to agree to a standard Microsoft Contributor License Agreement (CLA) declaring that you grant us the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. + +When you submit a pull request, a CLA bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA. + +### Code of Conduct + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. diff --git a/README.md b/README.md index 5cd7cec..9e7fb47 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,30 @@ +![Microsoft Defending Democracy Program: ElectionGuard Tools][banner image] + +# 🗳 ElectionGuard Tools + +[![ElectionGuard Specification 0.95.0](https://img.shields.io/badge/🗳%20ElectionGuard%20Specification-0.95.0-green)](https://www.electionguard.vote) ![Github Package Action](https://github.com/microsoft/electionguard-python/workflows/Release%20Build/badge.svg) [![](https://img.shields.io/pypi/v/electionguard)](https://pypi.org/project/electionguard/) [![](https://img.shields.io/pypi/dm/electionguard)](https://pypi.org/project/electionguard/) [![Language grade: Python](https://img.shields.io/lgtm/grade/python/g/microsoft/electionguard-python.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/microsoft/electionguard-python/context:python) [![Total alerts](https://img.shields.io/lgtm/alerts/g/microsoft/electionguard-python.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/microsoft/electionguard-python/alerts/) [![Documentation Status](https://readthedocs.org/projects/electionguard-python/badge/?version=latest)](https://electionguard-python.readthedocs.io) [![license](https://img.shields.io/github/license/microsoft/electionguard)](https://github.com/microsoft/electionguard-python/blob/main/LICENSE) + +This repository is a "reference implementation" of ElectionGuard written in Python 3. This implementation can be used to conduct End-to-End Verifiable Elections as well as privacy-enhanced risk-limiting audits. Components of this library can also be used to construct "Verifiers" to validate the results of an ElectionGuard election. + +## 📁 In This Repository + +| File/folder | Description | +| ------------------------------------------------------- | ---------------------------------------------- | +| [docs](/docs) | Documentation for using the tools. | +| [src/GenerateVerifierTests](/src/GenerateVerifierTests) | Verifier Test Generator. | +| [CONTRIBUTING.md](/CONTRIBUTING.md) | Guidelines for contributing. | +| [README.md](/README.md) | This README file. | +| [LICENSE](/LICENSE) | The license for ElectionGuard-Tools. | + +## ❓ What Is ElectionGuard? + +ElectionGuard is an open source software development kit (SDK) that makes voting more secure, transparent and accessible. The ElectionGuard SDK leverages homomorphic encryption to ensure that votes recorded by electronic systems of any type remain encrypted, secure, and secret. Meanwhile, ElectionGuard also allows verifiable and accurate tallying of ballots by any 3rd party organization without compromising secrecy or security. + +Learn More in the [ElectionGuard Repository](https://github.com/microsoft/electionguard) + +## 🦸 How Can I use ElectionGuard? + +ElectionGuard supports a variety of use cases. The Primary use case is to generate verifiable end-to-end (E2E) encrypted elections. The ElectionGuard process can also be used for other use cases such as privacy enhanced risk-limiting audits (RLAs). # Project > This repo has been populated by an initial template to help get you started. Please @@ -12,22 +39,41 @@ As the maintainer of this project, please make a few updates: ## Contributing -This project welcomes contributions and suggestions. Most contributions require you to agree to a -Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us -the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. +This project encourages community contributions for development, testing, documentation, code review, and performance analysis, etc. For more information on how to contribute, see [the contribution guidelines][contributing] + +### Code of Conduct + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + +### Reporting Issues + +Please report any bugs, feature requests, or enhancements using the [GitHub Issue Tracker](https://github.com/microsoft/electionguard-python/issues). Please do not report any security vulnerabilities using the Issue Tracker. Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). See the [Security Documentation][security] for more information. + +### Have Questions? + +Electionguard would love for you to ask questions out in the open using GitHub Issues. If you really want to email the ElectionGuard team, reach out at electionguard@microsoft.com. +## License + +This repository is licensed under the [MIT License] -When you submit a pull request, a CLA bot will automatically determine whether you need to provide -a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions -provided by the bot. You will only need to do this once across all repos using our CLA. +## Thanks! 🎉 -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). -For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or -contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. +A huge thank you to those who helped to contribute to this project so far, including: -## Trademarks + -This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft -trademarks or logos is subject to and must follow -[Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general). -Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. -Any use of third-party trademarks or logos are subject to those third-party's policies. +[banner image]: https://raw.githubusercontent.com/microsoft/electionguard-python/main/images/electionguard-banner.svg +[pull request workflow]: https://github.com/microsoft/electionguard-python/blob/main/.github/workflows/pull_request.yml +[contributing]: https://github.com/microsoft/electionguard-python/blob/main/CONTRIBUTING.md +[security]: https://github.com/microsoft/electionguard-python/blob/main/SECURITY.md +[design and architecture]: https://github.com/microsoft/electionguard-python/blob/main/docs/Design_and_Architecture.md +[build and run]: https://github.com/microsoft/electionguard-python/blob/main/docs/Build_and_Run.md +[project workflow]: https://github.com/microsoft/electionguard-python/blob/main/docs/Project_Workflow.md +[election manifest]: https://github.com/microsoft/electionguard-python/blob/main/docs/Election_Manifest.md +[configure election]: https://github.com/microsoft/electionguard-python/blob/main/docs/0_Configure_Election.md +[key ceremony]: https://github.com/microsoft/electionguard-python/blob/main/docs/1_Key_Ceremony.md +[encrypt ballots]: https://github.com/microsoft/electionguard-python/blob/main/docs/2_Encrypt_Ballots.md +[cast and spoil]: https://github.com/microsoft/electionguard-python/blob/main/docs/3_Cast_and_Spoil.md +[decrypt tally]: https://github.com/microsoft/electionguard-python/blob/main/docs/4_Decrypt_Tally.md +[publish and verify]: https://github.com/microsoft/electionguard-python/blob/main/docs/5_Publish_and_Verify.md +[mit license]: https://github.com/microsoft/electionguard-python/blob/main/LICENSE \ No newline at end of file diff --git a/src/GenerateVerifierTests/GenerateVerifierTests.sln b/src/GenerateVerifierTests/GenerateVerifierTests.sln new file mode 100644 index 0000000..48dcca8 --- /dev/null +++ b/src/GenerateVerifierTests/GenerateVerifierTests.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.2.32602.215 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GenerateVerifierTests", "GenerateVerifierTests\GenerateVerifierTests.csproj", "{C4CA5089-8F2F-44B6-9486-9EBA26B2E912}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C4CA5089-8F2F-44B6-9486-9EBA26B2E912}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C4CA5089-8F2F-44B6-9486-9EBA26B2E912}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C4CA5089-8F2F-44B6-9486-9EBA26B2E912}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C4CA5089-8F2F-44B6-9486-9EBA26B2E912}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F94E78AC-B4AD-4FD6-8AE4-E6C03FD8ECB6} + EndGlobalSection +EndGlobal diff --git a/src/GenerateVerifierTests/GenerateVerifierTests/GenerateVerifierTests.csproj b/src/GenerateVerifierTests/GenerateVerifierTests/GenerateVerifierTests.csproj new file mode 100644 index 0000000..40c60dd --- /dev/null +++ b/src/GenerateVerifierTests/GenerateVerifierTests/GenerateVerifierTests.csproj @@ -0,0 +1,10 @@ + + + + Exe + net6.0 + enable + enable + + + diff --git a/src/GenerateVerifierTests/GenerateVerifierTests/Program.cs b/src/GenerateVerifierTests/GenerateVerifierTests/Program.cs new file mode 100644 index 0000000..9aef12d --- /dev/null +++ b/src/GenerateVerifierTests/GenerateVerifierTests/Program.cs @@ -0,0 +1,964 @@ +// See https://aka.ms/new-console-template for more information + +using System.Diagnostics; +using System.Text.Json; + +var path = @"f:\mitre2"; + +var dirs = Directory.GetDirectories(path); +int cnt = 0; +foreach (var dir in dirs) +{ + var folder = Path.GetFileName(dir); + cnt++; + switch (folder) + { + case "Good": + break; + + #region change random + case "test1-large": + ChangeConstants(dir, "large_prime", 'F', '6'); + break; + case "test1-small": + ChangeConstants(dir, "small_prime", 'F', '3'); + break; + case "test1-cofactor": + ChangeConstants(dir, "cofactor", '0', '9'); + break; + + case "test1-generator": + ChangeConstants(dir, "generator", '8', '7'); + break; + + case "test2a": + ChangeGuardianProofs(dir, "challenge", '9', '0'); + break; + + case "test2b": + ChangeGuardianProofs(dir, "response", '7', '4'); + break; + + case "test2a2b": + ChangeGuardianProofs(dir, "commitment", '0', '8'); + break; + + case "test3aelection": + ChangeGuardian(dir, "election_public_key", '4', '5'); + break; + + case "test3aelgamal": + ChangeContext(dir, "elgamal_public_key", '7', '0'); + break; + + case "test3bbasehash": + ChangeContext(dir, "crypto_base_hash", '8', '5'); + ChangeContext(dir, "crypto_base_hash", '4', 'A'); + break; + + case "test3bextended": + ChangeContext(dir, "crypto_extended_base_hash", 'D', '1'); + ChangeContext(dir, "crypto_extended_base_hash", 'A', '2'); + break; + + case "test4b4e4f5b7": + { + (JsonDocument doc, string data, string filename) = GetSummittedBallot(dir); + var randomContest = GetRandomContest(doc.RootElement); + var randomBallot = GetRandomBallotSelection(randomContest); + var dataField = GetProperty(randomBallot, "ciphertext", "pad"); + var outData = ReplaceString(data, dataField, '0', '2'); + File.WriteAllText(filename, outData); + } + break; + + case "test4b4g4h5b7": + { + (JsonDocument doc, string data, string filename) = GetSummittedBallot(dir); + var randomContest = GetRandomContest(doc.RootElement); + var randomBallot = GetRandomBallotSelection(randomContest); + var dataField = GetProperty(randomBallot, "ciphertext", "data"); + var outData = ReplaceString(data, dataField, '3', '1'); + File.WriteAllText(filename, outData); + } + break; + + case "test4b": + { + (JsonDocument doc, string data, string filename) = GetSummittedBallot(dir); + var randomContest = GetRandomContest(doc.RootElement); + var randomBallot = GetRandomBallotSelection(randomContest); + var dataField = GetProperty(randomBallot, "proof", "challenge"); + var outData = ReplaceString(data, dataField, '6', '7'); + File.WriteAllText(filename, outData); + } + break; + + case "test4d4e4g": + { + (JsonDocument doc, string data, string filename) = GetSummittedBallot(dir); + var randomContest = GetRandomContest(doc.RootElement); + var randomBallot = GetRandomBallotSelection(randomContest); + var dataField = GetProperty(randomBallot, "proof", "proof_zero_challenge"); + var outData = ReplaceString(data, dataField, '9', '3'); + File.WriteAllText(filename, outData); + } + break; + + case "test4d4f4h": + { + (JsonDocument doc, string data, string filename) = GetSummittedBallot(dir); + var randomContest = GetRandomContest(doc.RootElement); + var randomBallot = GetRandomBallotSelection(randomContest); + var dataField = GetProperty(randomBallot, "proof", "proof_one_challenge"); + var outData = ReplaceString(data, dataField, '4', '1'); + File.WriteAllText(filename, outData); + } + break; + + case "test6a7": + { + (JsonDocument doc, string data, string filename) = GetSummittedBallot(dir); + var randomContest = GetRandomContest(doc.RootElement); + var randomBallot = GetRandomBallotSelection(randomContest); + var dataField = GetProperty(randomBallot, "object_id", string.Empty); + var outData = ReplaceString(data, dataField, '5', '0'); + File.WriteAllText(filename, outData); + } + break; + + case "test5b535f": + { + (JsonDocument doc, string data, string filename) = GetSummittedBallot(dir); + var randomContest = GetRandomContest(doc.RootElement); + var dataField = GetProperty(randomContest, "ciphertext_accumulation", "pad"); + var outData = ReplaceString(data, dataField, '6', '4'); + File.WriteAllText(filename, outData); + } + break; + + case "test5b5e5g": + { + (JsonDocument doc, string data, string filename) = GetSummittedBallot(dir); + var randomContest = GetRandomContest(doc.RootElement); + var dataField = GetProperty(randomContest, "ciphertext_accumulation", "data"); + var outData = ReplaceString(data, dataField, '6', '2'); + File.WriteAllText(filename, outData); + } + break; + + case "test5e5f": + { + (JsonDocument doc, string data, string filename) = GetSummittedBallot(dir); + var randomContest = GetRandomContest(doc.RootElement); + var dataField = GetProperty(randomContest, "proof", "pad"); + var outData = ReplaceString(data, dataField, '3', '1'); + File.WriteAllText(filename, outData); + } + break; + + case "test5e5g": + { + (JsonDocument doc, string data, string filename) = GetSummittedBallot(dir); + var randomContest = GetRandomContest(doc.RootElement); + var dataField = GetProperty(randomContest, "proof", "data"); + var outData = ReplaceString(data, dataField, '1', '6'); + File.WriteAllText(filename, outData); + } + break; + + case "test5e5f5g": + { + (JsonDocument doc, string data, string filename) = GetSummittedBallot(dir); + var randomContest = GetRandomContest(doc.RootElement); + var dataField = GetProperty(randomContest, "proof", "challenge"); + var outData = ReplaceString(data, dataField, '0', '9'); + File.WriteAllText(filename, outData); + } + break; + + case "test5a5g6a7": + { + (JsonDocument doc, string data, string filename) = GetSummittedBallot(dir); + var randomContest = GetRandomContest(doc.RootElement); + var dataField = GetProperty(randomContest, "object_id", string.Empty); + var outData = ReplaceString(data, dataField, '6', '3'); + File.WriteAllText(filename, outData); + } + break; + + case "test6acontestdescription": + { + (JsonDocument doc, string data, string filename) = GetSummittedBallot(dir); + var randomContest = GetRandomContest(doc.RootElement); + var dataField = GetProperty(randomContest, "description_hash", string.Empty); + var outData = ReplaceString(data, dataField, '7', '8'); + File.WriteAllText(filename, outData); + } + break; + + case "test6aselectiondescription": + { + (JsonDocument doc, string data, string filename) = GetSummittedBallot(dir); + var randomContest = GetRandomContest(doc.RootElement); + var randomBallot = GetRandomBallotSelection(randomContest); + var dataField = GetProperty(randomBallot, "description_hash", string.Empty); + var outData = ReplaceString(data, dataField, '5', '3'); + File.WriteAllText(filename, outData); + } + break; + case "test7selectionmessagepad": + { + (JsonDocument doc, string data, string filename) = GetTally(dir); + var firstContest = GetFirstContest(doc.RootElement); + var firstBallot = GetFirstBallotSelection(firstContest); + var dataField = GetProperty(firstBallot, "message", "pad"); + var outData = ReplaceString(data, dataField, '0', '3'); + File.WriteAllText(filename, outData); + } + break; + case "test7selectionmessagedata": + { + (JsonDocument doc, string data, string filename) = GetTally(dir); + var firstContest = GetFirstContest(doc.RootElement); + var firstBallot = GetFirstBallotSelection(firstContest); + var dataField = GetProperty(firstBallot, "message", "data"); + var outData = ReplaceString(data, dataField, '2', '1'); + File.WriteAllText(filename, outData); + } + break; + case "test8c8dtally": + { + (JsonDocument doc, string data, string filename) = GetTally(dir); + var firstContest = GetFirstContest(doc.RootElement); + var firstBallot = GetFirstBallotSelection(firstContest); + var share = GetRandomShare(firstBallot); + var dataField = GetProperty(share, "proof", "pad"); + var outData = ReplaceString(data, dataField, '0', '4'); + File.WriteAllText(filename, outData); + } + break; + case "test8c8e11aselectionsharesproofdata": + { + (JsonDocument doc, string data, string filename) = GetTally(dir); + var firstContest = GetFirstContest(doc.RootElement); + var firstBallot = GetFirstBallotSelection(firstContest); + var share = GetRandomShare(firstBallot); + var dataField = GetProperty(share, "proof", "data"); + var outData = ReplaceString(data, dataField, '5', '7'); + File.WriteAllText(filename, outData); + } + break; + case "test8d8e": + { + (JsonDocument doc, string data, string filename) = GetTally(dir); + var firstContest = GetFirstContest(doc.RootElement); + var firstBallot = GetFirstBallotSelection(firstContest); + var share = GetRandomShare(firstBallot); + var dataField = GetProperty(share, "proof", "response"); + var outData = ReplaceString(data, dataField, '5', '8'); + File.WriteAllText(filename, outData); + } + break; + case "test8c8d8e": + { + (JsonDocument doc, string data, string filename) = GetTally(dir); + var firstContest = GetFirstContest(doc.RootElement); + var firstBallot = GetFirstBallotSelection(firstContest); + var share = GetRandomShare(firstBallot); + var dataField = GetProperty(share, "proof", "challenge"); + var outData = ReplaceString(data, dataField, '9', '7'); + File.WriteAllText(filename, outData); + } + break; + case "test8c8e11aselectionsharesshare": + { + (JsonDocument doc, string data, string filename) = GetTally(dir); + var firstContest = GetFirstContest(doc.RootElement); + var firstBallot = GetFirstBallotSelection(firstContest); + var share = GetRandomShare(firstBallot); + var dataField = GetProperty(share, "share", string.Empty); + var outData = ReplaceString(data, dataField, '3', '4'); + File.WriteAllText(filename, outData); + } + break; + case "test11a11b": + { + (JsonDocument doc, string data, string filename) = GetTally(dir); + var firstContest = GetFirstContest(doc.RootElement); + var firstBallot = GetFirstBallotSelection(firstContest); + var dataField = GetProperty(firstBallot, "value", string.Empty); + var outData = ReplaceString(data, dataField, '3', '8'); + File.WriteAllText(filename, outData); + } + break; + case "test11b": + { + (JsonDocument doc, string data, string filename) = GetTally(dir); + var firstContest = GetFirstContest(doc.RootElement); + var firstBallot = GetFirstBallotSelection(firstContest); + var dataField = GetProperty(firstBallot, "tally", string.Empty); + var outData = ReplaceTally(data, dataField); + File.WriteAllText(filename, outData); + } + break; + case "test9c9d": + break; + case "test9c9e": + break; + case "test9d9e": + break; + case "test9c9d9e": + break; + case "test10": + { + var filename = Path.Combine(dir, "coefficients.json"); + var data = File.ReadAllText(filename); + JsonDocument doc = JsonDocument.Parse(data); + var dataField = GetProperty(doc.RootElement, "coefficients", "2"); + var outData = ReplaceString(data, dataField, 'F', '2'); + var dataField2 = GetProperty(doc.RootElement, "coefficients", "1"); + var outData2 = ReplaceString(outData, dataField2, '0', 'A'); + File.WriteAllText(filename, outData2); + } + break; + case "test13c13d": + { + (JsonDocument doc, string data, string filename) = GetSpoiledBallot(dir); + if (filename == string.Empty) + continue; + var randomContest = GetFirstContest(doc.RootElement); + var randomBallot = GetFirstBallotSelection(randomContest); + var randomShare = GetRandomShare(randomBallot); + var dataField = GetProperty(randomShare, "proof", "pad"); + var outData = ReplaceString(data, dataField, '1', '3'); + File.WriteAllText(filename, outData); + } + break; + case "test13c13e16aselectionsharesproofdata": + { + (JsonDocument doc, string data, string filename) = GetSpoiledBallot(dir); + if (filename == string.Empty) + continue; + var randomContest = GetFirstContest(doc.RootElement); + var randomBallot = GetFirstBallotSelection(randomContest); + var randomShare = GetRandomShare(randomBallot); + var dataField = GetProperty(randomShare, "proof", "data"); + var outData = ReplaceString(data, dataField, '5', '4'); + File.WriteAllText(filename, outData); + } + break; + case "test13d13e": + { + (JsonDocument doc, string data, string filename) = GetSpoiledBallot(dir); + if (filename == string.Empty) + continue; + var randomContest = GetFirstContest(doc.RootElement); + var randomBallot = GetFirstBallotSelection(randomContest); + var randomShare = GetRandomShare(randomBallot); + var dataField = GetProperty(randomShare, "proof", "response"); + var outData = ReplaceString(data, dataField, '8', '2'); + File.WriteAllText(filename, outData); + } + break; + case "test13c13d13e": + { + (JsonDocument doc, string data, string filename) = GetSpoiledBallot(dir); + if (filename == string.Empty) + continue; + var randomContest = GetFirstContest(doc.RootElement); + var randomBallot = GetFirstBallotSelection(randomContest); + var randomShare = GetRandomShare(randomBallot); + var dataField = GetProperty(randomShare, "proof", "challenge"); + var outData = ReplaceString(data, dataField, '4', '0'); + File.WriteAllText(filename, outData); + } + break; + case "test13c13e16aselectionsharesshare": + { + (JsonDocument doc, string data, string filename) = GetSpoiledBallot(dir); + if (filename == string.Empty) + continue; + var randomContest = GetFirstContest(doc.RootElement); + var randomBallot = GetFirstBallotSelection(randomContest); + var randomShare = GetRandomShare(randomBallot); + var dataField = GetProperty(randomShare, "share", string.Empty); + var outData = ReplaceString(data, dataField, '8', '9'); + File.WriteAllText(filename, outData); + } + break; + case "test16a16b": + { + (JsonDocument doc, string data, string filename) = GetSpoiledBallot(dir); + if (filename == string.Empty) + continue; + var randomContest = GetFirstContest(doc.RootElement); + var randomBallot = GetFirstBallotSelection(randomContest); + var dataField = GetProperty(randomBallot, "value", string.Empty); + var outData = ReplaceString(data, dataField, '1', '4'); + File.WriteAllText(filename, outData); + } + break; + case "test16b": + { + (JsonDocument doc, string data, string filename) = GetSpoiledBallot(dir); + if (filename == string.Empty) + continue; + var randomContest = GetFirstContest(doc.RootElement); + var randomBallot = GetFirstBallotSelection(randomContest); + var dataField = GetProperty(randomBallot, "tally", string.Empty); + var outData = ReplaceTally(data, dataField); + File.WriteAllText(filename, outData); + } + break; + case "test14c14d": + break; + case "test14c14e": + break; + case "test14d14e": + break; + case "test14c14d14e": + break; + #endregion + + #region append zeros + case "test4czeroc": + { + // get a ballot + (JsonDocument doc, string data, string filename) = GetSummittedBallot(dir); + var randomContest = GetRandomContest(doc.RootElement); + var randomBallot = GetRandomBallotSelection(randomContest); + var dataField = GetProperty(randomBallot, "proof", "proof_zero_challenge"); + var outData = AddZerosString(data, dataField); + File.WriteAllText(filename, outData); + } + break; + case "test4conec": + { + // get a ballot + (JsonDocument doc, string data, string filename) = GetSummittedBallot(dir); + var randomContest = GetRandomContest(doc.RootElement); + var randomBallot = GetRandomBallotSelection(randomContest); + var dataField = GetProperty(randomBallot, "proof", "proof_one_challenge"); + var outData = AddZerosString(data, dataField); + File.WriteAllText(filename, outData); + } + break; + case "test4czeror": + { + // get a ballot + (JsonDocument doc, string data, string filename) = GetSummittedBallot(dir); + var randomContest = GetRandomContest(doc.RootElement); + var randomBallot = GetRandomBallotSelection(randomContest); + var dataField = GetProperty(randomBallot, "proof", "proof_zero_response"); + var outData = AddZerosString(data, dataField); + File.WriteAllText(filename, outData); + } + break; + case "test4coner": + { + // get a ballot + (JsonDocument doc, string data, string filename) = GetSummittedBallot(dir); + var randomContest = GetRandomContest(doc.RootElement); + var randomBallot = GetRandomBallotSelection(randomContest); + var dataField = GetProperty(randomBallot, "proof", "proof_one_response"); + var outData = AddZerosString(data, dataField); + File.WriteAllText(filename, outData); + } + break; + case "test5c": + { + // get a ballot + (JsonDocument doc, string data, string filename) = GetSummittedBallot(dir); + var randomContest = GetRandomContest(doc.RootElement); + var dataField = GetProperty(randomContest, "proof", "response"); + var outData = AddZerosString(data, dataField); + File.WriteAllText(filename, outData); + } + break; + case "test8a": + { + (JsonDocument doc, string data, string filename) = GetTally(dir); + var contest = GetFirstContest(doc.RootElement); + var selection = GetFirstBallotSelection(contest); + var randomShare = GetRandomShare(selection); + var dataField = GetProperty(randomShare, "proof", "response"); + var outData = AddZerosString(data, dataField); + File.WriteAllText(filename, outData); + } + break; + case "test9a": + break; + case "test13a": + { + (JsonDocument doc, string data, string filename) = GetSpoiledBallot(dir); + if (filename == string.Empty) + continue; + var randomContest = GetFirstContest(doc.RootElement); + var randomBallot = GetFirstBallotSelection(randomContest); + var randomShare = GetRandomShare(randomBallot); + var dataField = GetProperty(randomShare, "proof", "response"); + var outData = AddZerosString(data, dataField); + File.WriteAllText(filename, outData); + } + break; + case "test14a": + break; + #endregion + + #region change to zero + case "test4a-ciphertextpad": + { + // get a ballot + (JsonDocument doc, string data, string filename) = GetSummittedBallot(dir); + var randomContest = GetRandomContest(doc.RootElement); + var randomBallot = GetRandomBallotSelection(randomContest); + var dataField = GetProperty(randomBallot, "ciphertext", "pad"); + var outData = ZeroString(data, dataField); + File.WriteAllText(filename, outData); + } + break; + case "test4a-ciphertextdata": + { + (JsonDocument doc, string data, string filename) = GetSummittedBallot(dir); + var randomContest = GetRandomContest(doc.RootElement); + var randomBallot = GetRandomBallotSelection(randomContest); + var dataField = GetProperty(randomBallot, "ciphertext", "data"); + var outData = ZeroString(data, dataField); + File.WriteAllText(filename, outData); + } + break; + case "test4a-proofzeropad": + { + // get a ballot + (JsonDocument doc, string data, string filename) = GetSummittedBallot(dir); + var randomContest = GetRandomContest(doc.RootElement); + var randomBallot = GetRandomBallotSelection(randomContest); + var dataField = GetProperty(randomBallot, "proof", "proof_zero_pad"); + var outData = ZeroString(data, dataField); + File.WriteAllText(filename, outData); + } + break; + case "test4a-proofzerodata": + { + // get a ballot + (JsonDocument doc, string data, string filename) = GetSummittedBallot(dir); + var randomContest = GetRandomContest(doc.RootElement); + var randomBallot = GetRandomBallotSelection(randomContest); + var proof = randomBallot.GetProperty("proof"); + var dataField = proof.GetProperty("proof_zero_data"); + var outData = ZeroString(data, dataField); + File.WriteAllText(filename, outData); + } + break; + case "test4a-proofonepad": + { + // get a ballot + (JsonDocument doc, string data, string filename) = GetSummittedBallot(dir); + var randomContest = GetRandomContest(doc.RootElement); + var randomBallot = GetRandomBallotSelection(randomContest); + var proof = randomBallot.GetProperty("proof"); + var dataField = proof.GetProperty("proof_one_pad"); + var outData = ZeroString(data, dataField); + File.WriteAllText(filename, outData); + } + break; + case "test4a-proofonedata": + { + // get a ballot + (JsonDocument doc, string data, string filename) = GetSummittedBallot(dir); + var randomContest = GetRandomContest(doc.RootElement); + var randomBallot = GetRandomBallotSelection(randomContest); + var proof = randomBallot.GetProperty("proof"); + var dataField = proof.GetProperty("proof_one_data"); + var outData = ZeroString(data, dataField); + File.WriteAllText(filename, outData); + } + break; + case "test5d-contestproofpad": + { + // get a ballot + (JsonDocument doc, string data, string filename) = GetSummittedBallot(dir); + var randomContest = GetRandomContest(doc.RootElement); + var proof = randomContest.GetProperty("proof"); + var dataField = proof.GetProperty("pad"); + var outData = ZeroString(data, dataField); + File.WriteAllText(filename, outData); + } + break; + case "test5d-contestproofdata": + { + // get a ballot + (JsonDocument doc, string data, string filename) = GetSummittedBallot(dir); + var randomContest = GetRandomContest(doc.RootElement); + var proof = randomContest.GetProperty("proof"); + var dataField = proof.GetProperty("data"); + var outData = ZeroString(data, dataField); + File.WriteAllText(filename, outData); + } + break; + case "test8b-tallycontestselectionproofpad": + { + // get a ballot + (JsonDocument doc, string data, string filename) = GetTally(dir); + var contest = GetFirstContest(doc.RootElement); + var selection = GetFirstBallotSelection(contest); + var randomShare = GetRandomShare(selection); + var dataField = GetProperty(randomShare, "proof", "pad"); + var outData = ZeroString(data, dataField); + File.WriteAllText(filename, outData); + } + break; + case "test8b-tallycontestselectionproofdata": + { + // get a ballot + (JsonDocument doc, string data, string filename) = GetTally(dir); + var contest = GetFirstContest(doc.RootElement); + var selection = GetFirstBallotSelection(contest); + var randomShare = GetRandomShare(selection); + var dataField = GetProperty(randomShare, "proof", "data"); + var outData = ZeroString(data, dataField); + File.WriteAllText(filename, outData); + } + break; + case "test9b-tallycontestselectionrecoveredproofpad": + { + // get a ballot + (JsonDocument doc, string data, string filename) = GetTally(dir); + var contests = doc.RootElement.GetProperty("contests"); + JsonElement dataField = new JsonElement(); + bool found = false; + foreach (var item in contests.EnumerateObject()) + { + var contest = item.Value; + var selections = contest.GetProperty("selections"); + foreach (var sitem in selections.EnumerateObject()) + { + var shares = sitem.Value.GetProperty("shares"); + var sharecnt = shares.GetArrayLength(); + for (int i = 0; i < sharecnt; i++) + { + var share = shares[i]; + var proof = share.GetProperty("recovered_parts"); + if (proof.ValueKind != JsonValueKind.Null) + { + dataField = proof.GetProperty("pad"); + found = true; + } + } + break; + } + break; + } + if (found) + { + var outData = ZeroString(data, dataField); + File.WriteAllText(filename, outData); + } + } + break; + case "test9b-tallycontestselectionrecoveredproofdata": + { + // get a ballot + (JsonDocument doc, string data, string filename) = GetTally(dir); + var contests = doc.RootElement.GetProperty("contests"); + JsonElement dataField = new JsonElement(); + bool found = false; + foreach (var item in contests.EnumerateObject()) + { + var contest = item.Value; + var selections = contest.GetProperty("selections"); + foreach (var sitem in selections.EnumerateObject()) + { + var shares = sitem.Value.GetProperty("shares"); + var sharecnt = shares.GetArrayLength(); + for (int i = 0; i < sharecnt; i++) + { + var share = shares[i]; + var proof = share.GetProperty("recovered_parts"); + if (proof.ValueKind != JsonValueKind.Null) + { + dataField = proof.GetProperty("data"); + found = true; + } + } + break; + } + break; + } + if (found) + { + var outData = ZeroString(data, dataField); + File.WriteAllText(filename, outData); + } + } + break; + case "test13b-contestselectionproofpad": + { + // get a spoiled ballot + (JsonDocument doc, string data, string filename) = GetSpoiledBallot(dir); + if (filename == string.Empty) + continue; + var randomContest = GetFirstContest(doc.RootElement); + var randomBallot = GetFirstBallotSelection(randomContest); + var randomShare = GetRandomShare(randomBallot); + var dataField = GetProperty(randomShare, "proof", "pad"); + var outData = ZeroString(data, dataField); + File.WriteAllText(filename, outData); + } + break; + case "test13b-contestselectionproofdata": + { + // get a spoiled ballot + (JsonDocument doc, string data, string filename) = GetSpoiledBallot(dir); + if (filename == string.Empty) + continue; + var contest = GetFirstContest(doc.RootElement); + var selection = GetFirstBallotSelection(contest); + var randomShare = GetRandomShare(selection); + var dataField = GetProperty(randomShare, "proof", "data"); + var outData = ZeroString(data, dataField); + File.WriteAllText(filename, outData); + } + break; + case "test14b-contestselectionrecoveredproofpad": + { + // get a spoiled ballot + var files = Directory.GetFiles(Path.Combine(dir, "spoiled_ballots")); + if (files.Count() == 0) + continue; + foreach (var filename in files) + { + bool found = false; + var data = File.ReadAllText(filename); + JsonDocument doc = JsonDocument.Parse(data); + var contests = doc.RootElement.GetProperty("contests"); + JsonElement dataField = new JsonElement(); + foreach (var item in contests.EnumerateObject()) + { + var contest = item.Value; + var selections = contest.GetProperty("selections"); + foreach (var sitem in selections.EnumerateObject()) + { + var shares = sitem.Value.GetProperty("shares"); + var sharecnt = shares.GetArrayLength(); + for (int i = 0; i < sharecnt; i++) + { + var share = shares[i]; + var proof = share.GetProperty("recovered_parts"); + if (proof.ValueKind != JsonValueKind.Null) + { + dataField = proof.GetProperty("pad"); + found = true; + } + } + } + } + if (found) + { + var outData = ZeroString(data, dataField); + File.WriteAllText(filename, outData); + } + } + } + break; + case "test14b-contestselectionrecoveredproofdata": + { + // get a spoiled ballot + var files = Directory.GetFiles(Path.Combine(dir, "spoiled_ballots")); + if (files.Count() == 0) + continue; + foreach (var filename in files) + { + bool found = false; + var data = File.ReadAllText(filename); + JsonDocument doc = JsonDocument.Parse(data); + var contests = doc.RootElement.GetProperty("contests"); + JsonElement dataField = new JsonElement(); + foreach (var item in contests.EnumerateObject()) + { + var contest = item.Value; + var selections = contest.GetProperty("selections"); + foreach (var sitem in selections.EnumerateObject()) + { + var shares = sitem.Value.GetProperty("shares"); + var sharecnt = shares.GetArrayLength(); + for (int i = 0; i < sharecnt; i++) + { + var share = shares[i]; + var proof = share.GetProperty("recovered_parts"); + if (proof.ValueKind != JsonValueKind.Null) + { + dataField = proof.GetProperty("data"); + found = true; + } + } + } + } + if (found) + { + var outData = ZeroString(data, dataField); + File.WriteAllText(filename, outData); + } + } + } + break; + #endregion + + default: + break; + } +} +Console.WriteLine($"{cnt} vote packages customized"); + +JsonElement GetRandomShare(JsonElement root) +{ + var shares = root.GetProperty("shares"); + var sharecnt = shares.GetArrayLength(); + return shares[Random.Shared.Next(sharecnt)]; +} + +string ReplaceString(string data, JsonElement dataField, char find, char replace) +{ + return data.Replace(dataField.GetString(), dataField.GetString().Replace(find, replace)); +} + +string AddZerosString(string data, JsonElement dataField) +{ + return data.Replace(dataField.GetString(), dataField.GetString() + "000"); +} + +string ZeroString(string data, JsonElement dataField) +{ + return data.Replace(dataField.GetString(), "0"); +} + +string ReplaceTally(string data, JsonElement dataField) +{ + int tally = dataField.GetInt32(); + return data.Replace($"\"tally\": {tally},", $"\"tally\": {tally+1},"); +} + +JsonElement GetProperty(JsonElement root, string top, string child) +{ + var parent = root.GetProperty(top); + if (child != string.Empty) + return parent.GetProperty(child); + else + return parent; +} + +JsonElement GetInt(JsonElement root, string top) +{ + var parent = root.GetProperty(top); + return parent; +} + +JsonElement GetFirstContest(JsonElement root) +{ + JsonElement ret = new JsonElement(); + var contests = root.GetProperty("contests"); + foreach (var item in contests.EnumerateObject()) + { + ret = item.Value; + break; + } + return ret; +} + +JsonElement GetRandomContest(JsonElement root) +{ + var contests = root.GetProperty("contests"); + var cnt = contests.GetArrayLength(); + return contests[Random.Shared.Next(cnt)]; +} + +JsonElement GetRandomBallotSelection(JsonElement root) +{ + var ballotSelections = root.GetProperty("ballot_selections"); + var cntBallots = ballotSelections.GetArrayLength(); + return ballotSelections[Random.Shared.Next(cntBallots)]; +} + +JsonElement GetFirstBallotSelection(JsonElement root) +{ + JsonElement ret = new JsonElement(); + var ballotSelections = root.GetProperty("selections"); + foreach (var item in ballotSelections.EnumerateObject()) + { + ret = item.Value; + break; + } + return ret; +} + +(JsonDocument, string, string) GetSummittedBallot(string dir) +{ + var files = Directory.GetFiles(Path.Combine(dir, "submitted_ballots")); + var filename = files[Random.Shared.Next(files.Count())]; + var data = File.ReadAllText(filename); + return (JsonDocument.Parse(data), data, filename); +} + +(JsonDocument, string, string) GetTally(string dir) +{ + var filename = Path.Combine(dir, "tally.json"); + var data = File.ReadAllText(filename); + return (JsonDocument.Parse(data), data, filename); +} + + +(JsonDocument, string, string) GetSpoiledBallot(string dir) +{ + var files = Directory.GetFiles(Path.Combine(dir, "spoiled_ballots")); + if (files.Count() == 0) + return (JsonDocument.Parse(""), string.Empty, string.Empty); + var filename = files[Random.Shared.Next(files.Count())]; + var data = File.ReadAllText(filename); + return (JsonDocument.Parse(data), data, filename); +} + + +void ChangeConstants(string dir, string field, char find, char replace) +{ + var filename = Path.Combine(dir, "constants.json"); + var data = File.ReadAllText(filename); + JsonDocument doc = JsonDocument.Parse(data); + var dataField = doc.RootElement.GetProperty(field); + var outData = data.Replace(dataField.GetString(), dataField.GetString().Replace(find, replace)); + File.WriteAllText(filename, outData); +} + +void ChangeContext(string dir, string field, char find, char replace) +{ + var filename = Path.Combine(dir, "context.json"); + var data = File.ReadAllText(filename); + JsonDocument doc = JsonDocument.Parse(data); + var dataField = doc.RootElement.GetProperty(field); + var outData = data.Replace(dataField.GetString(), dataField.GetString().Replace(find, replace)); + File.WriteAllText(filename, outData); +} + +void ChangeGuardian(string dir, string field, char find, char replace) +{ + var files = Directory.GetFiles(Path.Combine(dir, "guardians")); + var filename = files[Random.Shared.Next(files.Count())]; + var data = File.ReadAllText(filename); + JsonDocument doc = JsonDocument.Parse(data); + var dataField = doc.RootElement.GetProperty(field); + var outData = data.Replace(dataField.GetString(), dataField.GetString().Replace(find, replace)); + File.WriteAllText(filename, outData); +} +void ChangeGuardianProofs(string dir, string field, char find, char replace) +{ + var files = Directory.GetFiles(Path.Combine(dir, "guardians")); + var filename = files[Random.Shared.Next(files.Count())]; + var data = File.ReadAllText(filename); + JsonDocument doc = JsonDocument.Parse(data); + var proofs = doc.RootElement.GetProperty("election_proofs"); + var cnt = proofs.GetArrayLength(); + var proof = proofs[Random.Shared.Next(cnt)]; + var dataField = proof.GetProperty(field); + var outData = data.Replace(dataField.GetString(), dataField.GetString().Replace(find, replace)); + File.WriteAllText(filename, outData); +} \ No newline at end of file