* Simple CLI

* Ability to parse ballots

* Encrypt plaintext and output to out dir

* Submit ballots

* Fix compiler warnings

* Create output directory if it doesn't exist

* Add .NET 6 to github actions

* Upgrade to Visual Studio 2022

* Try to always install .NET 6

* Ensure .NET 5 AND 6 are both installed

* Upgrade .NET 5 to 6

* Only install .NET 6

* Remove 2nd command per PR
This commit is contained in:
Lee Richardson 2022-06-07 17:24:38 -04:00 коммит произвёл GitHub
Родитель 171b97809a
Коммит 5180c1b20a
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
12 изменённых файлов: 579 добавлений и 366 удалений

16
.github/workflows/pull-request.yml поставляемый
Просмотреть файл

@ -28,8 +28,8 @@ jobs:
"ubuntu-18.04-gcc-9.3.0",
"ubuntu-18.04-clang-10.0.0",
"macos-10.15-xcode-12.0",
"windows-2019-gcc-9.2.0",
"windows-2019-msvc-latest",
"windows-2022-gcc-9.2.0",
"windows-2022-msvc-latest",
]
include:
- name: ubuntu-20.04-clang-10.0.0
@ -48,12 +48,12 @@ jobs:
os: macOS-10.15
compiler: xcode
version: "12"
- name: windows-2019-gcc-9.2.0
os: windows-2019
- name: windows-2022-gcc-9.2.0
os: windows-2022
compiler: gcc
version: "9"
- name: windows-2019-msvc-latest
os: windows-2019
- name: windows-2022-msvc-latest
os: windows-2022
compiler: msvc
version: "latest"
@ -62,6 +62,10 @@ jobs:
uses: actions/checkout@v2
- name: Change Directory
run: cd ${{ github.workspace }}
- name: Install .NET 6
uses: actions/setup-dotnet@v2
with:
dotnet-version: 6.0.x
- name: Update Environment
if: runner.os == 'Linux'
run: |

2
.gitignore поставляемый
Просмотреть файл

@ -151,3 +151,5 @@ bindings/netframework/ElectionGuard.NetFramework/packages/
sample-data.zip
sample-data-container.zip
bindings/netstandard/ElectionGuard/*.user

Просмотреть файл

@ -88,11 +88,11 @@ endif
build-msvc:
@echo 🖥️ BUILD MSVC
ifeq ($(OPERATING_SYSTEM),Windows)
cmake -S . -B $(ELECTIONGUARD_BUILD_LIBS_DIR)/msvc/Win32 -G "Visual Studio 16 2019" -A Win32 \
cmake -S . -B $(ELECTIONGUARD_BUILD_LIBS_DIR)/msvc/Win32 -G "Visual Studio 17 2022" -A Win32 \
-DCMAKE_BUILD_TYPE=$(TARGET) \
-DBUILD_SHARED_LIBS=ON \
-DCPM_SOURCE_CACHE=$(CPM_SOURCE_CACHE)
cmake -S . -B $(ELECTIONGUARD_BUILD_LIBS_DIR)/msvc/x64 -G "Visual Studio 16 2019" -A x64 \
cmake -S . -B $(ELECTIONGUARD_BUILD_LIBS_DIR)/msvc/x64 -G "Visual Studio 17 2022" -A x64 \
-DCMAKE_BUILD_TYPE=$(TARGET) \
-DBUILD_SHARED_LIBS=ON \
-DCAN_USE_VECTOR_INTRINSICS=ON \
@ -231,7 +231,7 @@ sanitize: sanitize-asan sanitize-tsan
sanitize-asan:
@echo 🧼 SANITIZE ADDRESS AND UNDEFINED
ifeq ($(OPERATING_SYSTEM),Windows)
cmake -S . -B $(ELECTIONGUARD_BUILD_LIBS_DIR)/msvc/x64 -G "Visual Studio 16 2019" -A x64 \
cmake -S . -B $(ELECTIONGUARD_BUILD_LIBS_DIR)/msvc/x64 -G "Visual Studio 17 2022" -A x64 \
-DCMAKE_BUILD_TYPE=Debug \
-DBUILD_SHARED_LIBS=ON \
-DEXPORT_INTERNALS=ON \
@ -319,12 +319,12 @@ endif
bench-netstandard: build-netstandard
@echo 🧪 BENCHMARK
@echo net 5.0 x86
./bindings/netstandard/ElectionGuard/ElectionGuard.Encryption.Bench/bin/x86/$(TARGET)/net5.0/ElectionGuard.Encryption.Bench
@echo net 6.0 x86
./bindings/netstandard/ElectionGuard/ElectionGuard.Encryption.Bench/bin/x86/$(TARGET)/net6.0/ElectionGuard.Encryption.Bench
@echo net 4.8 x86
./bindings/netstandard/ElectionGuard/ElectionGuard.Encryption.Bench/bin/x86/$(TARGET)/net48/ElectionGuard.Encryption.Bench
@echo net 5.0 x64
./bindings/netstandard/ElectionGuard/ElectionGuard.Encryption.Bench/bin/x64/$(TARGET)/net5.0/ElectionGuard.Encryption.Bench
@echo net 6.0 x64
./bindings/netstandard/ElectionGuard/ElectionGuard.Encryption.Bench/bin/x64/$(TARGET)/net6.0/ElectionGuard.Encryption.Bench
@echo net 4.8 x64
./bindings/netstandard/ElectionGuard/ElectionGuard.Encryption.Bench/bin/x64/$(TARGET)/net48/ElectionGuard.Encryption.Bench
@ -359,7 +359,7 @@ endif
test-msvc:
@echo 🧪 TEST MSVC
ifeq ($(OPERATING_SYSTEM),Windows)
cmake -S . -B $(ELECTIONGUARD_BUILD_LIBS_DIR)/msvc/x64 -G "Visual Studio 16 2019" -A x64 \
cmake -S . -B $(ELECTIONGUARD_BUILD_LIBS_DIR)/msvc/x64 -G "Visual Studio 17 2022" -A x64 \
-DCMAKE_BUILD_TYPE=$(TARGET) \
-DBUILD_SHARED_LIBS=ON \
-DEXPORT_INTERNALS=ON \

Просмотреть файл

@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net5.0;net48</TargetFrameworks>
<TargetFrameworks>net6.0;net48</TargetFrameworks>
<IsPackable>false</IsPackable>
<Platforms>x64;x86</Platforms>
</PropertyGroup>

Просмотреть файл

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<StartupObject>ElectionGuard.Encryption.Cli.Program</StartupObject>
<AssemblyName>eg</AssemblyName>
<Platforms>AnyCPU;x64</Platforms>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.9.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ElectionGuard.Encryption\ElectionGuard.Encryption.csproj" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,94 @@
namespace ElectionGuard.Encryption.Cli.Encrypt
{
internal class EncryptCommand
{
public static Task Encrypt(EncryptOptions encryptOptions)
{
try
{
var encryptCommand = new EncryptCommand();
return encryptCommand.EncryptInternal(encryptOptions);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
throw;
}
}
private async Task EncryptInternal(EncryptOptions encryptOptions)
{
encryptOptions.Validate();
if (string.IsNullOrEmpty(encryptOptions.Context)) throw new ArgumentNullException(nameof(encryptOptions.Context));
if (string.IsNullOrEmpty(encryptOptions.Manifest)) throw new ArgumentNullException(nameof(encryptOptions.Manifest));
if (string.IsNullOrEmpty(encryptOptions.BallotsDir)) throw new ArgumentNullException(nameof(encryptOptions.BallotsDir));
var encryptionMediator = await GetEncryptionMediator(encryptOptions.Context, encryptOptions.Manifest);
var ballotFiles = GetBallotFiles(encryptOptions.BallotsDir);
foreach (var ballotFile in ballotFiles)
{
Console.WriteLine($"Parsing: {ballotFile}");
var plaintextBallot = await GetPlaintextBallot(ballotFile);
var submittedBallot = EncryptAndSubmit(encryptionMediator, plaintextBallot);
await WriteSubmittedBallot(encryptOptions, ballotFile, submittedBallot);
}
Console.WriteLine("Parsing Complete");
}
private static SubmittedBallot EncryptAndSubmit(EncryptionMediator encryptionMediator, PlaintextBallot plaintextBallot)
{
var ciphertextBallot = encryptionMediator.Encrypt(plaintextBallot);
// todo: spoil?
var submittedBallot = new SubmittedBallot(ciphertextBallot, BallotBoxState.Cast);
return submittedBallot;
}
private static async Task<EncryptionMediator> GetEncryptionMediator(string contextFile, string manifestFile)
{
var context = await GetContext(contextFile);
var internalManifest = await GetInternalManifest(manifestFile);
var device = GetDevice();
var encryptionMediator = new EncryptionMediator(internalManifest, context, device);
return encryptionMediator;
}
private static IEnumerable<string> GetBallotFiles(string directory)
{
return Directory.EnumerateFiles(directory);
}
private static async Task<PlaintextBallot> GetPlaintextBallot(string ballotFile)
{
var ballot = await File.ReadAllTextAsync(ballotFile);
var plaintextBallot = new PlaintextBallot(ballot);
return plaintextBallot;
}
private static async Task WriteSubmittedBallot(EncryptOptions encryptOptions, string ballotFile,
SubmittedBallot submittedBallot)
{
var ballotJson = submittedBallot.ToJson();
var outFile = Path.Join(encryptOptions.OutDir, Path.GetFileName(ballotFile));
await File.WriteAllTextAsync(outFile, ballotJson);
}
private static EncryptionDevice GetDevice()
{
return new EncryptionDevice(12345UL, 23456UL, 34567UL, "Location");
}
private static async Task<InternalManifest> GetInternalManifest(string manifestFile)
{
var manifestJson = await File.ReadAllTextAsync(manifestFile);
var manifest = new Manifest(manifestJson);
return new InternalManifest(manifest);
}
private static async Task<CiphertextElectionContext> GetContext(string contextFile)
{
var contextJson = await File.ReadAllTextAsync(contextFile);
return new CiphertextElectionContext(contextJson);
}
}
}

Просмотреть файл

@ -0,0 +1,47 @@
using CommandLine;
namespace ElectionGuard.Encryption.Cli.Encrypt;
[Verb("encrypt", HelpText = "Encrypt ballots using a context.")]
internal class EncryptOptions
{
[Option('c', "context", Required = true, HelpText = "Json file containing an ElectionGuard context that contains encryption details.")]
public string? Context { get; set; }
[Option('m', "manifest", Required = true, HelpText = "Json file containing an ElectionGuard manifest that contains election details.")]
public string? Manifest { get; set; }
[Option('b', "ballots", Required = true, HelpText = "File folder containing ballots to encrypt.")]
public string? BallotsDir { get; set; }
[Option('o', "out", Required = true, HelpText = "File folder in which to place encrypted ballots.")]
public string? OutDir { get; set; }
public void Validate()
{
ValidateFiles();
ValidateDirectories();
}
private void ValidateDirectories()
{
if (string.IsNullOrEmpty(BallotsDir)) throw new ArgumentNullException(nameof(BallotsDir));
if (string.IsNullOrEmpty(OutDir)) throw new ArgumentNullException(nameof(OutDir));
if (!Directory.Exists(BallotsDir)) throw new ArgumentException($"ballots directory does not exist"); ;
if (!Directory.Exists(OutDir))
{
Directory.CreateDirectory(OutDir);
}
}
private void ValidateFiles()
{
var requiredFiles = new[] { Context, Manifest };
var missingFiles = requiredFiles.Where(f => !File.Exists(f));
foreach (var file in missingFiles)
{
throw new ArgumentException($"{file} does not exist");
}
}
}

Просмотреть файл

@ -0,0 +1,13 @@
using CommandLine;
using ElectionGuard.Encryption.Cli.Encrypt;
namespace ElectionGuard.Encryption.Cli;
class Program
{
static async Task Main(string[] args)
{
await Parser.Default.ParseArguments<EncryptOptions>(args)
.WithParsedAsync(EncryptCommand.Encrypt);
}
}

Просмотреть файл

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net5.0;net48</TargetFrameworks>
<TargetFrameworks>net6.0;net48</TargetFrameworks>
<IsPackable>false</IsPackable>
<Platforms>x64;x86</Platforms>
</PropertyGroup>

Просмотреть файл

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net5.0;net48</TargetFrameworks>
<TargetFrameworks>net6.0;net48</TargetFrameworks>
<IsPackable>false</IsPackable>
<Platforms>x64;x86</Platforms>
</PropertyGroup>

Просмотреть файл

@ -1,347 +1,347 @@
using System;
using System.Runtime.InteropServices;
namespace ElectionGuard
{
using NativeElementModQ = NativeInterface.ElementModQ.ElementModQHandle;
using NativeEncryptionDevice = NativeInterface.EncryptionDevice.EncryptionDeviceHandle;
using NativeEncryptionMediator = NativeInterface.EncryptionMediator.EncryptionMediatorHandle;
using NativeCiphertextBallot = NativeInterface.CiphertextBallot.CiphertextBallotHandle;
using NativeCiphertextBallotContest = NativeInterface.CiphertextBallotContest.CiphertextBallotContestHandle;
using NativeCiphertextBallotSelection = NativeInterface.CiphertextBallotSelection.CiphertextBallotSelectionHandle;
using NativeCompactCiphertextBallot = NativeInterface.CompactCiphertextBallot.CompactCiphertextBallotHandle;
/// <summary>
/// Metadata for encryption device
///
/// The encryption device is a stateful container that represents abstract hardware
/// authorized to participate in a specific election.
///
/// </summary>
public class EncryptionDevice : DisposableBase
{
internal unsafe NativeEncryptionDevice Handle;
/// <summary>
/// Create a new EncryptionDevice
/// </summary>
/// <param name="deviceUuid">a unique identifier tied to the device hardware</param>
/// <param name="sessionUuid">a unique identifier tied to the runtime session</param>
/// <param name="launchCode">a unique identifer tied to the election</param>
/// <param name="location">an arbitrary string meaningful to the external system
/// such as a friendly name, description, or some other value</param>
public unsafe EncryptionDevice(
ulong deviceUuid,
ulong sessionUuid,
ulong launchCode,
string location)
{
var status = NativeInterface.EncryptionDevice.New(
deviceUuid, sessionUuid, launchCode, location, out Handle);
status.ThrowIfError();
}
/// <summary>
/// Get a new hash value
///
/// <return>An `ElementModQ`</return>
/// </summary>
public unsafe ElementModQ GetHash()
{
var status = NativeInterface.EncryptionDevice.GetHash(Handle, out NativeElementModQ value);
return new ElementModQ(value);
}
/// <summary>
/// produces encription device when given json
/// </summary>
/// <param name="json"></param>
public unsafe EncryptionDevice(string json)
{
var status = NativeInterface.EncryptionDevice.FromJson(json, out Handle);
status.ThrowIfError();
}
/// <Summary>
/// Export the encryptiondevice representation as JSON
/// </Summary>
public unsafe string ToJson()
{
var status = NativeInterface.EncryptionDevice.ToJson(Handle, out IntPtr pointer, out ulong size);
status.ThrowIfError();
var json = Marshal.PtrToStringAnsi(pointer);
return json;
}
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
protected override unsafe void DisposeUnmanaged()
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
{
base.DisposeUnmanaged();
if (Handle == null || Handle.IsInvalid) return;
Handle.Dispose();
Handle = null;
}
}
/// <summary>
/// An object for caching election and encryption state.
///
/// the encryption mediator composes ballots by querying the encryption device
/// for a hash of its metadata and incremental timestamps/
///
/// this is a convenience wrapper around the encrypt methods
/// and may not be suitable for all use cases.
/// </summary>
public class EncryptionMediator : DisposableBase
{
internal unsafe NativeEncryptionMediator Handle;
/// <summary>
/// Create an `EncryptionMediator` object
/// </summary>
/// <param name="manifest"></param>
/// <param name="context"></param>
/// <param name="device"></param>
public unsafe EncryptionMediator(
InternalManifest manifest,
CiphertextElectionContext context,
EncryptionDevice device)
{
var status = NativeInterface.EncryptionMediator.New(
manifest.Handle, context.Handle, device.Handle, out Handle);
status.ThrowIfError();
}
/// <summary>
/// Encrypt the specified ballot using the cached election context.
/// </summary>
public unsafe CiphertextBallot Encrypt(
PlaintextBallot plaintext, bool verifyProofs = false)
{
if (verifyProofs)
{
var status = NativeInterface.EncryptionMediator.EncryptAndVerify(
Handle, plaintext.Handle, out NativeCiphertextBallot ciphertext);
status.ThrowIfError();
return new CiphertextBallot(ciphertext);
}
else
{
var status = NativeInterface.EncryptionMediator.Encrypt(
Handle, plaintext.Handle, out NativeCiphertextBallot ciphertext);
status.ThrowIfError();
return new CiphertextBallot(ciphertext);
}
}
/// <summary>
/// Encrypt the specified ballot into its compact form using the cached election context.
/// </summary>
public unsafe CompactCiphertextBallot CompactEncrypt(
PlaintextBallot plaintext, bool verifyProofs = false)
{
if (verifyProofs)
{
var status = NativeInterface.EncryptionMediator.CompactEncryptAndVerify(
Handle, plaintext.Handle, out NativeCompactCiphertextBallot ciphertext);
status.ThrowIfError();
return new CompactCiphertextBallot(ciphertext);
}
else
{
var status = NativeInterface.EncryptionMediator.CompactEncrypt(
Handle, plaintext.Handle, out NativeCompactCiphertextBallot ciphertext);
status.ThrowIfError();
return new CompactCiphertextBallot(ciphertext);
}
}
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
protected override unsafe void DisposeUnmanaged()
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
{
base.DisposeUnmanaged();
if (Handle == null || Handle.IsInvalid) return;
Handle.Dispose();
Handle = null;
}
}
/// <summary>
/// Metadata for encryption
///
/// The encrypt object is used for encrypting ballots.
///
/// </summary>
public class Encrypt
{
/// <summary>
/// Encrypt a specific `BallotSelection` in the context of a specific `BallotContest`
/// </summary>
/// <param name="plaintext">the selection in the valid input form</param>
/// <param name="description">the `SelectionDescription` from the `ContestDescription`
/// which defines this selection's structure</param>
/// <param name="elgamalPublicKey">the public key (K) used to encrypt the ballot</param>
/// <param name="cryptoExtendedBaseHash">the extended base hash of the election</param>
/// <param name="nonceSeed">an `ElementModQ` used as a header to seed the `Nonce` generated
/// for this selection. this value can be (or derived from) the
/// Contest nonce, but no relationship is required</param>
/// <param name="shouldVerifyProofs">specify if the proofs should be verified prior to returning (default True)</param>
/// <returns>A `CiphertextBallotSelection`</returns>
public static unsafe CiphertextBallotSelection Selection(
PlaintextBallotSelection plaintext,
SelectionDescription description,
ElementModP elgamalPublicKey,
ElementModQ cryptoExtendedBaseHash,
ElementModQ nonceSeed,
bool shouldVerifyProofs = true
)
{
var status = NativeInterface.Encrypt.Selection(
plaintext.Handle, description.Handle, elgamalPublicKey.Handle,
cryptoExtendedBaseHash.Handle, nonceSeed.Handle, shouldVerifyProofs,
out NativeCiphertextBallotSelection ciphertext);
status.ThrowIfError();
return new CiphertextBallotSelection(ciphertext);
}
/// <summary>
/// Encrypt a specific `BallotContest` in the context of a specific `Ballot`
///
/// This method accepts a contest representation that only includes `True` selections.
/// It will fill missing selections for a contest with `False` values, and generate `placeholder`
/// selections to represent the number of seats available for a given contest. By adding `placeholder`
/// votes
/// </summary>
/// <param name="plaintext">the selection in the valid input form</param>
/// <param name="description">the `ContestDescriptionWithPlaceholders` from the `ContestDescription`
/// which defines this contest's structure</param>
/// <param name="elgamalPublicKey">the public key (K) used to encrypt the ballot</param>
/// <param name="cryptoExtendedBaseHash">the extended base hash of the election</param>
/// <param name="nonceSeed">an `ElementModQ` used as a header to seed the `Nonce` generated
/// for this contest. this value can be (or derived from) the
/// Ballot nonce, but no relationship is required</param>
/// <param name="shouldVerifyProofs">specify if the proofs should be verified prior to returning (default True)</param>
/// <returns>A `CiphertextBallotContest`</returns>
public static unsafe CiphertextBallotContest Contest(
PlaintextBallotContest plaintext,
ContestDescription description,
ElementModP elgamalPublicKey,
ElementModQ cryptoExtendedBaseHash,
ElementModQ nonceSeed,
bool shouldVerifyProofs = true
)
{
var status = NativeInterface.Encrypt.Contest(
plaintext.Handle, description.Handle, elgamalPublicKey.Handle,
cryptoExtendedBaseHash.Handle, nonceSeed.Handle, shouldVerifyProofs,
out NativeCiphertextBallotContest ciphertext);
status.ThrowIfError();
return new CiphertextBallotContest(ciphertext);
}
/// <summary>
/// Encrypt a specific `Ballot` in the context of a specific `CiphertextElectionContext`
///
/// This method accepts a ballot representation that only includes `True` selections.
/// It will fill missing selections for a contest with `False` values, and generate `placeholder`
/// selections to represent the number of seats available for a given contest. By adding `placeholder`
/// votes
///
/// This method also allows for ballots to exclude passing contests for which the voter made no selections.
/// It will fill missing contests with `False` selections and generate `placeholder` selections that are marked `True`.
/// </summary>
/// <param name="ballot">the selection in the valid input form</param>
/// <param name="internalManifest">the `InternalManifest` which defines this ballot's structure</param>
/// <param name="context">all the cryptographic context for the election</param>
/// <param name="ballotCodeSeed">Hash from previous ballot or hash from device</param>
/// <param name="nonce">an optional value used to seed the `Nonce` generated for this ballot
/// if this value is not provided, the secret generating mechanism of the OS provides its own</param>
/// <param name="shouldVerifyProofs">specify if the proofs should be verified prior to returning (default True)</param>
/// <returns>A `CiphertextBallot`</returns>
public static unsafe CiphertextBallot Ballot(
PlaintextBallot ballot,
InternalManifest internalManifest,
CiphertextElectionContext context,
ElementModQ ballotCodeSeed,
ElementModQ nonce = null,
bool shouldVerifyProofs = true)
{
if (nonce == null)
{
var status = NativeInterface.Encrypt.Ballot(
ballot.Handle, internalManifest.Handle, context.Handle,
ballotCodeSeed.Handle, shouldVerifyProofs,
out NativeCiphertextBallot ciphertext);
status.ThrowIfError();
return new CiphertextBallot(ciphertext);
}
else
{
var status = NativeInterface.Encrypt.Ballot(
ballot.Handle, internalManifest.Handle, context.Handle,
ballotCodeSeed.Handle, nonce.Handle, shouldVerifyProofs,
out NativeCiphertextBallot ciphertext);
status.ThrowIfError();
return new CiphertextBallot(ciphertext);
}
}
/// <summary>
/// Encrypt a specific `Ballot` in the context of a specific `CiphertextElectionContext`
///
/// This method accepts a ballot representation that only includes `True` selections.
/// It will fill missing selections for a contest with `False` values, and generate `placeholder`
/// selections to represent the number of seats available for a given contest. By adding `placeholder`
/// votes
///
/// This method also allows for ballots to exclude passing contests for which the voter made no selections.
/// It will fill missing contests with `False` selections and generate `placeholder` selections that are marked `True`.
///
/// This version of the encrypt method returns a `compact` version of the ballot that includes a minimal representation
/// of the plaintext ballot along with the crypto parameters that are required to expand the ballot
/// </summary>
/// <param name="ballot">the selection in the valid input form</param>
/// <param name="internalManifest">the `InternalManifest` which defines this ballot's structure</param>
/// <param name="context">all the cryptographic context for the election</param>
/// <param name="ballotCodeSeed">Hash from previous ballot or hash from device</param>
/// <param name="nonce">an optional value used to seed the `Nonce` generated for this ballot
/// if this value is not provided, the secret generating mechanism of the OS provides its own</param>
/// <param name="shouldVerifyProofs">specify if the proofs should be verified prior to returning (default True)</param>
/// <returns>A `CiphertextBallot`</returns>
public static unsafe CompactCiphertextBallot CompactBallot(
PlaintextBallot ballot,
InternalManifest internalManifest,
CiphertextElectionContext context,
ElementModQ ballotCodeSeed,
ElementModQ nonce = null,
bool shouldVerifyProofs = true)
{
if (nonce == null)
{
var status = NativeInterface.Encrypt.CompactBallot(
ballot.Handle, internalManifest.Handle, context.Handle,
ballotCodeSeed.Handle, shouldVerifyProofs,
out NativeCompactCiphertextBallot ciphertext);
status.ThrowIfError();
return new CompactCiphertextBallot(ciphertext);
}
else
{
var status = NativeInterface.Encrypt.CompactBallot(
ballot.Handle, internalManifest.Handle, context.Handle,
ballotCodeSeed.Handle, nonce.Handle, shouldVerifyProofs,
out NativeCompactCiphertextBallot ciphertext);
status.ThrowIfError();
return new CompactCiphertextBallot(ciphertext);
}
}
}
}
using System;
using System.Runtime.InteropServices;
namespace ElectionGuard
{
using NativeElementModQ = NativeInterface.ElementModQ.ElementModQHandle;
using NativeEncryptionDevice = NativeInterface.EncryptionDevice.EncryptionDeviceHandle;
using NativeEncryptionMediator = NativeInterface.EncryptionMediator.EncryptionMediatorHandle;
using NativeCiphertextBallot = NativeInterface.CiphertextBallot.CiphertextBallotHandle;
using NativeCiphertextBallotContest = NativeInterface.CiphertextBallotContest.CiphertextBallotContestHandle;
using NativeCiphertextBallotSelection = NativeInterface.CiphertextBallotSelection.CiphertextBallotSelectionHandle;
using NativeCompactCiphertextBallot = NativeInterface.CompactCiphertextBallot.CompactCiphertextBallotHandle;
/// <summary>
/// Metadata for encryption device
///
/// The encryption device is a stateful container that represents abstract hardware
/// authorized to participate in a specific election.
///
/// </summary>
public class EncryptionDevice : DisposableBase
{
internal unsafe NativeEncryptionDevice Handle;
/// <summary>
/// Create a new EncryptionDevice
/// </summary>
/// <param name="deviceUuid">a unique identifier tied to the device hardware</param>
/// <param name="sessionUuid">a unique identifier tied to the runtime session</param>
/// <param name="launchCode">a unique identifer tied to the election</param>
/// <param name="location">an arbitrary string meaningful to the external system
/// such as a friendly name, description, or some other value</param>
public unsafe EncryptionDevice(
ulong deviceUuid,
ulong sessionUuid,
ulong launchCode,
string location)
{
var status = NativeInterface.EncryptionDevice.New(
deviceUuid, sessionUuid, launchCode, location, out Handle);
status.ThrowIfError();
}
/// <summary>
/// Get a new hash value
///
/// <return>An `ElementModQ`</return>
/// </summary>
public unsafe ElementModQ GetHash()
{
var status = NativeInterface.EncryptionDevice.GetHash(Handle, out NativeElementModQ value);
return new ElementModQ(value);
}
/// <summary>
/// produces encription device when given json
/// </summary>
/// <param name="json"></param>
public unsafe EncryptionDevice(string json)
{
var status = NativeInterface.EncryptionDevice.FromJson(json, out Handle);
status.ThrowIfError();
}
/// <Summary>
/// Export the encryptiondevice representation as JSON
/// </Summary>
public unsafe string ToJson()
{
var status = NativeInterface.EncryptionDevice.ToJson(Handle, out IntPtr pointer, out ulong size);
status.ThrowIfError();
var json = Marshal.PtrToStringAnsi(pointer);
return json;
}
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
protected override unsafe void DisposeUnmanaged()
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
{
base.DisposeUnmanaged();
if (Handle == null || Handle.IsInvalid) return;
Handle.Dispose();
Handle = null;
}
}
/// <summary>
/// An object for caching election and encryption state.
///
/// the encryption mediator composes ballots by querying the encryption device
/// for a hash of its metadata and incremental timestamps/
///
/// this is a convenience wrapper around the encrypt methods
/// and may not be suitable for all use cases.
/// </summary>
public class EncryptionMediator : DisposableBase
{
internal unsafe NativeEncryptionMediator Handle;
/// <summary>
/// Create an `EncryptionMediator` object
/// </summary>
/// <param name="manifest"></param>
/// <param name="context"></param>
/// <param name="device"></param>
public unsafe EncryptionMediator(
InternalManifest manifest,
CiphertextElectionContext context,
EncryptionDevice device)
{
var status = NativeInterface.EncryptionMediator.New(
manifest.Handle, context.Handle, device.Handle, out Handle);
status.ThrowIfError();
}
/// <summary>
/// Encrypt the specified ballot using the cached election context.
/// </summary>
public unsafe CiphertextBallot Encrypt(
PlaintextBallot plaintext, bool verifyProofs = false)
{
if (verifyProofs)
{
var status = NativeInterface.EncryptionMediator.EncryptAndVerify(
Handle, plaintext.Handle, out NativeCiphertextBallot ciphertext);
status.ThrowIfError();
return new CiphertextBallot(ciphertext);
}
else
{
var status = NativeInterface.EncryptionMediator.Encrypt(
Handle, plaintext.Handle, out NativeCiphertextBallot ciphertext);
status.ThrowIfError();
return new CiphertextBallot(ciphertext);
}
}
/// <summary>
/// Encrypt the specified ballot into its compact form using the cached election context.
/// </summary>
public unsafe CompactCiphertextBallot CompactEncrypt(
PlaintextBallot plaintext, bool verifyProofs = false)
{
if (verifyProofs)
{
var status = NativeInterface.EncryptionMediator.CompactEncryptAndVerify(
Handle, plaintext.Handle, out NativeCompactCiphertextBallot ciphertext);
status.ThrowIfError();
return new CompactCiphertextBallot(ciphertext);
}
else
{
var status = NativeInterface.EncryptionMediator.CompactEncrypt(
Handle, plaintext.Handle, out NativeCompactCiphertextBallot ciphertext);
status.ThrowIfError();
return new CompactCiphertextBallot(ciphertext);
}
}
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
protected override unsafe void DisposeUnmanaged()
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
{
base.DisposeUnmanaged();
if (Handle == null || Handle.IsInvalid) return;
Handle.Dispose();
Handle = null;
}
}
/// <summary>
/// Metadata for encryption
///
/// The encrypt object is used for encrypting ballots.
///
/// </summary>
public class Encrypt
{
/// <summary>
/// Encrypt a specific `BallotSelection` in the context of a specific `BallotContest`
/// </summary>
/// <param name="plaintext">the selection in the valid input form</param>
/// <param name="description">the `SelectionDescription` from the `ContestDescription`
/// which defines this selection's structure</param>
/// <param name="elgamalPublicKey">the public key (K) used to encrypt the ballot</param>
/// <param name="cryptoExtendedBaseHash">the extended base hash of the election</param>
/// <param name="nonceSeed">an `ElementModQ` used as a header to seed the `Nonce` generated
/// for this selection. this value can be (or derived from) the
/// Contest nonce, but no relationship is required</param>
/// <param name="shouldVerifyProofs">specify if the proofs should be verified prior to returning (default True)</param>
/// <returns>A `CiphertextBallotSelection`</returns>
public static unsafe CiphertextBallotSelection Selection(
PlaintextBallotSelection plaintext,
SelectionDescription description,
ElementModP elgamalPublicKey,
ElementModQ cryptoExtendedBaseHash,
ElementModQ nonceSeed,
bool shouldVerifyProofs = true
)
{
var status = NativeInterface.Encrypt.Selection(
plaintext.Handle, description.Handle, elgamalPublicKey.Handle,
cryptoExtendedBaseHash.Handle, nonceSeed.Handle, shouldVerifyProofs,
out NativeCiphertextBallotSelection ciphertext);
status.ThrowIfError();
return new CiphertextBallotSelection(ciphertext);
}
/// <summary>
/// Encrypt a specific `BallotContest` in the context of a specific `Ballot`
///
/// This method accepts a contest representation that only includes `True` selections.
/// It will fill missing selections for a contest with `False` values, and generate `placeholder`
/// selections to represent the number of seats available for a given contest. By adding `placeholder`
/// votes
/// </summary>
/// <param name="plaintext">the selection in the valid input form</param>
/// <param name="description">the `ContestDescriptionWithPlaceholders` from the `ContestDescription`
/// which defines this contest's structure</param>
/// <param name="elgamalPublicKey">the public key (K) used to encrypt the ballot</param>
/// <param name="cryptoExtendedBaseHash">the extended base hash of the election</param>
/// <param name="nonceSeed">an `ElementModQ` used as a header to seed the `Nonce` generated
/// for this contest. this value can be (or derived from) the
/// Ballot nonce, but no relationship is required</param>
/// <param name="shouldVerifyProofs">specify if the proofs should be verified prior to returning (default True)</param>
/// <returns>A `CiphertextBallotContest`</returns>
public static unsafe CiphertextBallotContest Contest(
PlaintextBallotContest plaintext,
ContestDescription description,
ElementModP elgamalPublicKey,
ElementModQ cryptoExtendedBaseHash,
ElementModQ nonceSeed,
bool shouldVerifyProofs = true
)
{
var status = NativeInterface.Encrypt.Contest(
plaintext.Handle, description.Handle, elgamalPublicKey.Handle,
cryptoExtendedBaseHash.Handle, nonceSeed.Handle, shouldVerifyProofs,
out NativeCiphertextBallotContest ciphertext);
status.ThrowIfError();
return new CiphertextBallotContest(ciphertext);
}
/// <summary>
/// Encrypt a specific `Ballot` in the context of a specific `CiphertextElectionContext`
///
/// This method accepts a ballot representation that only includes `True` selections.
/// It will fill missing selections for a contest with `False` values, and generate `placeholder`
/// selections to represent the number of seats available for a given contest. By adding `placeholder`
/// votes
///
/// This method also allows for ballots to exclude passing contests for which the voter made no selections.
/// It will fill missing contests with `False` selections and generate `placeholder` selections that are marked `True`.
/// </summary>
/// <param name="ballot">the selection in the valid input form</param>
/// <param name="internalManifest">the `InternalManifest` which defines this ballot's structure</param>
/// <param name="context">all the cryptographic context for the election</param>
/// <param name="ballotCodeSeed">Hash from previous ballot or hash from device</param>
/// <param name="nonce">an optional value used to seed the `Nonce` generated for this ballot
/// if this value is not provided, the secret generating mechanism of the OS provides its own</param>
/// <param name="shouldVerifyProofs">specify if the proofs should be verified prior to returning (default True)</param>
/// <returns>A `CiphertextBallot`</returns>
public static unsafe CiphertextBallot Ballot(
PlaintextBallot ballot,
InternalManifest internalManifest,
CiphertextElectionContext context,
ElementModQ ballotCodeSeed,
ElementModQ nonce = null,
bool shouldVerifyProofs = true)
{
if (nonce == null)
{
var status = NativeInterface.Encrypt.Ballot(
ballot.Handle, internalManifest.Handle, context.Handle,
ballotCodeSeed.Handle, shouldVerifyProofs,
out NativeCiphertextBallot ciphertext);
status.ThrowIfError();
return new CiphertextBallot(ciphertext);
}
else
{
var status = NativeInterface.Encrypt.Ballot(
ballot.Handle, internalManifest.Handle, context.Handle,
ballotCodeSeed.Handle, nonce.Handle, shouldVerifyProofs,
out NativeCiphertextBallot ciphertext);
status.ThrowIfError();
return new CiphertextBallot(ciphertext);
}
}
/// <summary>
/// Encrypt a specific `Ballot` in the context of a specific `CiphertextElectionContext`
///
/// This method accepts a ballot representation that only includes `True` selections.
/// It will fill missing selections for a contest with `False` values, and generate `placeholder`
/// selections to represent the number of seats available for a given contest. By adding `placeholder`
/// votes
///
/// This method also allows for ballots to exclude passing contests for which the voter made no selections.
/// It will fill missing contests with `False` selections and generate `placeholder` selections that are marked `True`.
///
/// This version of the encrypt method returns a `compact` version of the ballot that includes a minimal representation
/// of the plaintext ballot along with the crypto parameters that are required to expand the ballot
/// </summary>
/// <param name="ballot">the selection in the valid input form</param>
/// <param name="internalManifest">the `InternalManifest` which defines this ballot's structure</param>
/// <param name="context">all the cryptographic context for the election</param>
/// <param name="ballotCodeSeed">Hash from previous ballot or hash from device</param>
/// <param name="nonce">an optional value used to seed the `Nonce` generated for this ballot
/// if this value is not provided, the secret generating mechanism of the OS provides its own</param>
/// <param name="shouldVerifyProofs">specify if the proofs should be verified prior to returning (default True)</param>
/// <returns>A `CiphertextBallot`</returns>
public static unsafe CompactCiphertextBallot CompactBallot(
PlaintextBallot ballot,
InternalManifest internalManifest,
CiphertextElectionContext context,
ElementModQ ballotCodeSeed,
ElementModQ nonce = null,
bool shouldVerifyProofs = true)
{
if (nonce == null)
{
var status = NativeInterface.Encrypt.CompactBallot(
ballot.Handle, internalManifest.Handle, context.Handle,
ballotCodeSeed.Handle, shouldVerifyProofs,
out NativeCompactCiphertextBallot ciphertext);
status.ThrowIfError();
return new CompactCiphertextBallot(ciphertext);
}
else
{
var status = NativeInterface.Encrypt.CompactBallot(
ballot.Handle, internalManifest.Handle, context.Handle,
ballotCodeSeed.Handle, nonce.Handle, shouldVerifyProofs,
out NativeCompactCiphertextBallot ciphertext);
status.ThrowIfError();
return new CompactCiphertextBallot(ciphertext);
}
}
}
}

Просмотреть файл

@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31613.86
# Visual Studio Version 17
VisualStudioVersion = 17.2.32519.379
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ElectionGuard.Encryption", "ElectionGuard.Encryption\ElectionGuard.Encryption.csproj", "{A4437B27-1101-4C91-AEBE-F97606990E2B}"
EndProject
@ -16,46 +16,78 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ElectionGuard.Encryption.Be
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ElectionGuard.Encryption.Utils", "ElectionGuard.Encryption.Utils\ElectionGuard.Encryption.Utils.csproj", "{6AC780D0-1862-4680-8B47-AD6CCF97B66C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ElectionGuard.Encryption.Cli", "ElectionGuard.Encryption.Cli\ElectionGuard.Encryption.Cli.csproj", "{A7289C54-F777-45EA-A6E8-9C7605706CBE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A4437B27-1101-4C91-AEBE-F97606990E2B}.Debug|Any CPU.ActiveCfg = Debug|x64
{A4437B27-1101-4C91-AEBE-F97606990E2B}.Debug|Any CPU.Build.0 = Debug|x64
{A4437B27-1101-4C91-AEBE-F97606990E2B}.Debug|x64.ActiveCfg = Debug|x64
{A4437B27-1101-4C91-AEBE-F97606990E2B}.Debug|x64.Build.0 = Debug|x64
{A4437B27-1101-4C91-AEBE-F97606990E2B}.Debug|x86.ActiveCfg = Debug|x86
{A4437B27-1101-4C91-AEBE-F97606990E2B}.Debug|x86.Build.0 = Debug|x86
{A4437B27-1101-4C91-AEBE-F97606990E2B}.Release|Any CPU.ActiveCfg = Release|x64
{A4437B27-1101-4C91-AEBE-F97606990E2B}.Release|Any CPU.Build.0 = Release|x64
{A4437B27-1101-4C91-AEBE-F97606990E2B}.Release|x64.ActiveCfg = Release|x64
{A4437B27-1101-4C91-AEBE-F97606990E2B}.Release|x64.Build.0 = Release|x64
{A4437B27-1101-4C91-AEBE-F97606990E2B}.Release|x86.ActiveCfg = Release|x86
{A4437B27-1101-4C91-AEBE-F97606990E2B}.Release|x86.Build.0 = Release|x86
{111BE643-7978-4CDB-9FDC-0A198139A488}.Debug|Any CPU.ActiveCfg = Debug|x64
{111BE643-7978-4CDB-9FDC-0A198139A488}.Debug|Any CPU.Build.0 = Debug|x64
{111BE643-7978-4CDB-9FDC-0A198139A488}.Debug|x64.ActiveCfg = Debug|x64
{111BE643-7978-4CDB-9FDC-0A198139A488}.Debug|x64.Build.0 = Debug|x64
{111BE643-7978-4CDB-9FDC-0A198139A488}.Debug|x86.ActiveCfg = Debug|x86
{111BE643-7978-4CDB-9FDC-0A198139A488}.Debug|x86.Build.0 = Debug|x86
{111BE643-7978-4CDB-9FDC-0A198139A488}.Release|Any CPU.ActiveCfg = Release|x64
{111BE643-7978-4CDB-9FDC-0A198139A488}.Release|Any CPU.Build.0 = Release|x64
{111BE643-7978-4CDB-9FDC-0A198139A488}.Release|x64.ActiveCfg = Release|x64
{111BE643-7978-4CDB-9FDC-0A198139A488}.Release|x64.Build.0 = Release|x64
{111BE643-7978-4CDB-9FDC-0A198139A488}.Release|x86.ActiveCfg = Release|x86
{111BE643-7978-4CDB-9FDC-0A198139A488}.Release|x86.Build.0 = Release|x86
{06B6E480-D964-4351-8FF3-E62F652B8D4C}.Debug|Any CPU.ActiveCfg = Debug|x64
{06B6E480-D964-4351-8FF3-E62F652B8D4C}.Debug|Any CPU.Build.0 = Debug|x64
{06B6E480-D964-4351-8FF3-E62F652B8D4C}.Debug|x64.ActiveCfg = Debug|x64
{06B6E480-D964-4351-8FF3-E62F652B8D4C}.Debug|x64.Build.0 = Debug|x64
{06B6E480-D964-4351-8FF3-E62F652B8D4C}.Debug|x86.ActiveCfg = Debug|x86
{06B6E480-D964-4351-8FF3-E62F652B8D4C}.Debug|x86.Build.0 = Debug|x86
{06B6E480-D964-4351-8FF3-E62F652B8D4C}.Release|Any CPU.ActiveCfg = Release|x64
{06B6E480-D964-4351-8FF3-E62F652B8D4C}.Release|Any CPU.Build.0 = Release|x64
{06B6E480-D964-4351-8FF3-E62F652B8D4C}.Release|x64.ActiveCfg = Release|x64
{06B6E480-D964-4351-8FF3-E62F652B8D4C}.Release|x64.Build.0 = Release|x64
{06B6E480-D964-4351-8FF3-E62F652B8D4C}.Release|x86.ActiveCfg = Release|x86
{06B6E480-D964-4351-8FF3-E62F652B8D4C}.Release|x86.Build.0 = Release|x86
{6AC780D0-1862-4680-8B47-AD6CCF97B66C}.Debug|Any CPU.ActiveCfg = Debug|x64
{6AC780D0-1862-4680-8B47-AD6CCF97B66C}.Debug|Any CPU.Build.0 = Debug|x64
{6AC780D0-1862-4680-8B47-AD6CCF97B66C}.Debug|x64.ActiveCfg = Debug|x64
{6AC780D0-1862-4680-8B47-AD6CCF97B66C}.Debug|x64.Build.0 = Debug|x64
{6AC780D0-1862-4680-8B47-AD6CCF97B66C}.Debug|x86.ActiveCfg = Debug|x86
{6AC780D0-1862-4680-8B47-AD6CCF97B66C}.Debug|x86.Build.0 = Debug|x86
{6AC780D0-1862-4680-8B47-AD6CCF97B66C}.Release|Any CPU.ActiveCfg = Release|x64
{6AC780D0-1862-4680-8B47-AD6CCF97B66C}.Release|Any CPU.Build.0 = Release|x64
{6AC780D0-1862-4680-8B47-AD6CCF97B66C}.Release|x64.ActiveCfg = Release|x64
{6AC780D0-1862-4680-8B47-AD6CCF97B66C}.Release|x64.Build.0 = Release|x64
{6AC780D0-1862-4680-8B47-AD6CCF97B66C}.Release|x86.ActiveCfg = Release|x86
{6AC780D0-1862-4680-8B47-AD6CCF97B66C}.Release|x86.Build.0 = Release|x86
{A7289C54-F777-45EA-A6E8-9C7605706CBE}.Debug|Any CPU.ActiveCfg = Debug|x64
{A7289C54-F777-45EA-A6E8-9C7605706CBE}.Debug|Any CPU.Build.0 = Debug|x64
{A7289C54-F777-45EA-A6E8-9C7605706CBE}.Debug|x64.ActiveCfg = Debug|Any CPU
{A7289C54-F777-45EA-A6E8-9C7605706CBE}.Debug|x64.Build.0 = Debug|Any CPU
{A7289C54-F777-45EA-A6E8-9C7605706CBE}.Debug|x86.ActiveCfg = Debug|Any CPU
{A7289C54-F777-45EA-A6E8-9C7605706CBE}.Debug|x86.Build.0 = Debug|Any CPU
{A7289C54-F777-45EA-A6E8-9C7605706CBE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A7289C54-F777-45EA-A6E8-9C7605706CBE}.Release|Any CPU.Build.0 = Release|Any CPU
{A7289C54-F777-45EA-A6E8-9C7605706CBE}.Release|x64.ActiveCfg = Release|Any CPU
{A7289C54-F777-45EA-A6E8-9C7605706CBE}.Release|x64.Build.0 = Release|Any CPU
{A7289C54-F777-45EA-A6E8-9C7605706CBE}.Release|x86.ActiveCfg = Release|Any CPU
{A7289C54-F777-45EA-A6E8-9C7605706CBE}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE