diff --git a/bindings/netstandard/ElectionGuard/ElectionGuard.ElectionSetup.Tests/Extensions.cs b/bindings/netstandard/ElectionGuard/ElectionGuard.ElectionSetup.Tests/Extensions.cs
index 14516e1ad..3b8860128 100644
--- a/bindings/netstandard/ElectionGuard/ElectionGuard.ElectionSetup.Tests/Extensions.cs
+++ b/bindings/netstandard/ElectionGuard/ElectionGuard.ElectionSetup.Tests/Extensions.cs
@@ -6,26 +6,27 @@ public static class CoefficientExtensions
/// Generate a Coefficient for testing purposes only.
/// In a production environment, nonce should be null and a random value will be generated.
///
- public static Coefficient GenerateCoefficient(ElementModQ nonce, int index)
+ public static Coefficient GenerateCoefficient(ulong offset, int index, ElementModQ parameterHash, ElementModQ seed)
{
Console.WriteLine("WARNING: GenerateCoefficient using a predetermined nonce for testing purposes only. This should not be used in production.");
- var value = BigMath.AddModQ(nonce, (ulong)index);
- return new(value);
+ var secret = BigMath.AddModQ(seed, (ulong)index);
+ return new(offset, index, parameterHash, secret);
}
///
/// Generate a Coefficient for testing purposes only.
/// In a production environment, nonce should be null and a random value will be generated.
///
- /// A predetermined nonce parameter for testing purposes only.
+ /// A predetermined nonce parameter for testing purposes only.
/// The index of the coefficient
/// A predetermined seed for testing purposes only.
- public static Coefficient GenerateCoefficient(ElementModQ nonce, ElementModQ seed, int index)
+ public static Coefficient GenerateCoefficient(
+ ulong offset, int index, ElementModQ parameterHash, ElementModQ nonce, ElementModQ seed)
{
Console.WriteLine("WARNING: GenerateCoefficient using a predetermined nonce for testing purposes only. This should not be used in production.");
- var value = BigMath.AddModQ(nonce, (ulong)index);
- Console.WriteLine($"value: {value}");
- return new(value, seed);
+ var secret = BigMath.AddModQ(nonce, (ulong)index);
+ Console.WriteLine($"value: {secret}");
+ return new(offset, index, parameterHash, secret, seed);
}
}
@@ -35,15 +36,19 @@ public static class PolynomialExtensions
/// Generate a Polynomial for testing purposes only.
/// In a production environment, nonce should be null and a random value will be generated.
///
- /// A predetermined nonce parameter for testing purposes only.
+ /// A predetermined nonce parameter for testing purposes only.
/// The number of coefficients in the polynomial
- public static ElectionPolynomial GeneratePolynomial(ElementModQ nonce, int quorum)
+ public static ElectionPolynomial GeneratePolynomial(ulong sequenceOrder, int quorum, ElementModQ secret)
{
Console.WriteLine("WARNING: GeneratePolynomial using a predetermined nonce for testing purposes only. This should not be used in production.");
var coefficients = new List();
for (var i = 0; i < quorum; i++)
{
- coefficients.Add(CoefficientExtensions.GenerateCoefficient(nonce, i));
+ coefficients.Add(CoefficientExtensions.GenerateCoefficient(
+ sequenceOrder,
+ i,
+ CiphertextElectionContext.ParameterBaseHash,
+ secret));
}
return new(coefficients);
}
@@ -52,16 +57,21 @@ public static ElectionPolynomial GeneratePolynomial(ElementModQ nonce, int quoru
/// Generate a Polynomial for testing purposes only.
/// In a production environment, nonce should be null and a random value will be generated.
///
- /// A predetermined nonce parameter for testing purposes only.
+ /// A predetermined nonce parameter for testing purposes only.
/// The number of coefficients in the polynomial
/// A predetermined seed for testing purposes only.
- public static ElectionPolynomial GeneratePolynomial(ElementModQ nonce, ElementModQ seed, int quorum)
+ public static ElectionPolynomial GeneratePolynomial(ulong sequenceOrder, int quorum, ElementModQ secret, ElementModQ seed)
{
Console.WriteLine("WARNING: GeneratePolynomial using a predetermined nonce for testing purposes only. This should not be used in production.");
var coefficients = new List();
for (var i = 0; i < quorum; i++)
{
- coefficients.Add(CoefficientExtensions.GenerateCoefficient(nonce, seed, i));
+ coefficients.Add(CoefficientExtensions.GenerateCoefficient(
+ sequenceOrder,
+ i,
+ CiphertextElectionContext.ParameterBaseHash,
+ secret,
+ seed));
}
return new(coefficients);
}
diff --git a/bindings/netstandard/ElectionGuard/ElectionGuard.ElectionSetup.Tests/Generators/KeyCeremonyGenerator.cs b/bindings/netstandard/ElectionGuard/ElectionGuard.ElectionSetup.Tests/Generators/KeyCeremonyGenerator.cs
index ef8b6e7dd..af1cd5fda 100644
--- a/bindings/netstandard/ElectionGuard/ElectionGuard.ElectionSetup.Tests/Generators/KeyCeremonyGenerator.cs
+++ b/bindings/netstandard/ElectionGuard/ElectionGuard.ElectionSetup.Tests/Generators/KeyCeremonyGenerator.cs
@@ -60,6 +60,7 @@ public static List GenerateGuardians(
sequenceOrder,
keyCeremony.NumberOfGuardians,
keyCeremony.Quorum,
+ CiphertextElectionContext.ParameterBaseHash,
keyCeremony.Id,
random);
guardians.Add(guardian);
diff --git a/bindings/netstandard/ElectionGuard/ElectionGuard.ElectionSetup.Tests/KeyCeremony/TestElectionPolynomial.cs b/bindings/netstandard/ElectionGuard/ElectionGuard.ElectionSetup.Tests/KeyCeremony/TestElectionPolynomial.cs
index fe2a335cc..f3e269e23 100644
--- a/bindings/netstandard/ElectionGuard/ElectionGuard.ElectionSetup.Tests/KeyCeremony/TestElectionPolynomial.cs
+++ b/bindings/netstandard/ElectionGuard/ElectionGuard.ElectionSetup.Tests/KeyCeremony/TestElectionPolynomial.cs
@@ -6,10 +6,12 @@ public class TestElectionPolynomial
public void Test_ElectionPolynomial_Constructs_WithDegrees()
{
// Arrange
+ var sequenceOrder = 2UL;
var degrees = 2;
+ var parameterHash = Constants.TWO_MOD_Q;
// Act
- var result = new ElectionPolynomial(degrees);
+ var result = new ElectionPolynomial(sequenceOrder, degrees, parameterHash);
// Assert
Assert.That(result, Is.Not.Null);
@@ -20,11 +22,13 @@ public void Test_ElectionPolynomial_Constructs_WithDegrees()
public void Test_ElectionPolynomial_Constructs_WithSecretKey()
{
// Arrange
+ var sequenceOrder = 2UL;
var degrees = 2;
+ var parameterHash = Constants.TWO_MOD_Q;
var secretKey = Constants.TWO_MOD_Q;
// Act
- var result = new ElectionPolynomial(degrees, secretKey);
+ var result = new ElectionPolynomial(sequenceOrder, degrees, parameterHash, secretKey);
// Assert
Assert.That(result, Is.Not.Null);
@@ -36,12 +40,14 @@ public void Test_ElectionPolynomial_Constructs_WithSecretKey()
public void Test_ElectionPolynomial_Constructs_WithKeyPair()
{
// Arrange
+ var sequenceOrder = 2UL;
var degrees = 3;
+ var parameterHash = Constants.TWO_MOD_Q;
using var secretKey = BigMath.RandQ();
using var keyPair = new ElGamalKeyPair(secretKey);
// Act
- using var result = new ElectionPolynomial(degrees, keyPair);
+ using var result = new ElectionPolynomial(sequenceOrder, degrees, parameterHash, keyPair);
// Assert
Assert.That(result, Is.Not.Null);
@@ -55,11 +61,12 @@ public void Test_ElectionPolynomial_Constructs_WithKeyPair()
public void Test_ElectionPolynomial_ComputeCoordinate()
{
// Arrange
+ var offset = 0UL;
var coordinate = 1UL;
var degrees = 2;
var nonce = Constants.ONE_MOD_Q;
var seed = Constants.TWO_MOD_Q;
- var polynomial = PolynomialExtensions.GeneratePolynomial(nonce, seed, degrees);
+ var polynomial = PolynomialExtensions.GeneratePolynomial(offset, degrees, nonce, seed);
// Act
var result = polynomial.ComputeCoordinate(coordinate);
@@ -73,9 +80,11 @@ public void Test_ElectionPolynomial_ComputeCoordinate()
public void Test_ElectionPolynomial_VerifyCoordinate()
{
// Arrange
+ var sequenceOrder = 2UL;
var coordinate = 1UL;
var degrees = 2UL;
- var polynomial = new ElectionPolynomial((int)degrees);
+ var parameterHash = Constants.TWO_MOD_Q;
+ var polynomial = new ElectionPolynomial(sequenceOrder, (int)degrees, parameterHash);
var expected = polynomial.ComputeCoordinate(coordinate);
// Act
diff --git a/bindings/netstandard/ElectionGuard/ElectionGuard.ElectionSetup.Tests/KeyCeremony/TestKeyCeremony.cs b/bindings/netstandard/ElectionGuard/ElectionGuard.ElectionSetup.Tests/KeyCeremony/TestKeyCeremony.cs
index e55fc6c62..14c0b40bc 100644
--- a/bindings/netstandard/ElectionGuard/ElectionGuard.ElectionSetup.Tests/KeyCeremony/TestKeyCeremony.cs
+++ b/bindings/netstandard/ElectionGuard/ElectionGuard.ElectionSetup.Tests/KeyCeremony/TestKeyCeremony.cs
@@ -24,8 +24,11 @@ public void Test_KeyCeremony()
var guardianId = sequenceOrder.ToString();
var random = new Random((int)sequenceOrder);
var guardian = new Guardian(
- guardianId, sequenceOrder, numberOfGuardians,
- quorum, keyCeremony.Id,
+ guardianId, sequenceOrder,
+ numberOfGuardians,
+ quorum,
+ CiphertextElectionContext.ParameterBaseHash,
+ keyCeremony.Id,
random);
guardians.Add(guardian);
}
diff --git a/bindings/netstandard/ElectionGuard/ElectionGuard.ElectionSetup/Coefficient.cs b/bindings/netstandard/ElectionGuard/ElectionGuard.ElectionSetup/Coefficient.cs
index 0232a26e5..bebc19ae8 100644
--- a/bindings/netstandard/ElectionGuard/ElectionGuard.ElectionSetup/Coefficient.cs
+++ b/bindings/netstandard/ElectionGuard/ElectionGuard.ElectionSetup/Coefficient.cs
@@ -7,6 +7,16 @@ namespace ElectionGuard.ElectionSetup;
///
public class Coefficient : DisposableBase
{
+ ///
+ /// The offset of the secret value in the polynomial (i in the spec)
+ ///
+ public ulong Offset { get; set; }
+
+ ///
+ /// The index of the proof corresponding to the coefficient (j in the spec)
+ ///
+ public int Index { get; set; }
+
///
/// The key pair associated with the coefficient
///
@@ -31,72 +41,85 @@ public class Coefficient : DisposableBase
public SchnorrProof Proof { get; private set; } = default!;
///
- /// Generate a coefficient with a predetermined value
+ /// Generate a coefficient with a predetermined secret
///
- public Coefficient(ElementModQ value)
+ public Coefficient(
+ ulong offset,
+ int index,
+ ElementModQ parameterHash,
+ ElementModQ secret)
{
- var keyPair = ElGamalKeyPair.FromSecret(value);
- Proof = new SchnorrProof(keyPair);
+ Offset = offset;
+ Index = index;
+ var keyPair = ElGamalKeyPair.FromSecret(secret);
+ Proof = new SchnorrProof(offset, index, parameterHash, keyPair);
KeyPair = new ElGamalKeyPair(keyPair);
}
///
- /// Generate a coefficient with a predetermined value and proof seed
+ /// Generate a coefficient with a predetermined secret and proof seed
///
- public Coefficient(ElementModQ value, ElementModQ seed)
+ public Coefficient(
+ ulong offset,
+ int index,
+ ElementModQ parameterHash,
+ ElementModQ secret, ElementModQ seed)
{
- var keyPair = ElGamalKeyPair.FromSecret(value);
- Proof = new SchnorrProof(keyPair, seed);
+ Offset = offset;
+ Index = index;
+ var keyPair = ElGamalKeyPair.FromSecret(secret);
+ Proof = new SchnorrProof(offset, index, parameterHash, keyPair, seed);
KeyPair = new ElGamalKeyPair(keyPair);
}
///
- /// Generate a coefficient with a predetermined value and proof
+ /// Generate a coefficient with a predetermined secret and proof
///
- public Coefficient(ElementModQ value, SchnorrProof proof)
+ public Coefficient(
+ ulong offset,
+ int index,
+ ElementModQ parameterHash,
+ ElementModQ secret, SchnorrProof proof)
{
- if (!proof.IsValid())
+ if (!proof.IsValid(offset, index, parameterHash).Success)
{
throw new ArgumentException("Invalid proof");
}
- var keyPair = ElGamalKeyPair.FromSecret(value);
+ var keyPair = ElGamalKeyPair.FromSecret(secret);
if (!proof.PublicKey.Equals(keyPair.PublicKey))
{
throw new ArgumentException("Proof does not match key pair");
}
+ Offset = offset;
+ Index = index;
KeyPair = new ElGamalKeyPair(keyPair);
Proof = new(proof);
}
[JsonConstructor]
- public Coefficient(ElGamalKeyPair keyPair, SchnorrProof proof)
+ public Coefficient(
+ ulong offset,
+ int index,
+ ElGamalKeyPair keyPair, SchnorrProof proof)
{
- if (!proof.IsValid())
- {
- throw new ArgumentException("Invalid proof");
- }
-
+ // Dos not check the proof for validity when serializing from json
if (!proof.PublicKey.Equals(keyPair.PublicKey))
{
throw new ArgumentException("Proof does not match key pair");
}
-
+ Offset = offset;
+ Index = index;
KeyPair = new ElGamalKeyPair(keyPair);
Proof = new(proof);
}
- public Coefficient(Coefficient that)
-
- {
- KeyPair = new ElGamalKeyPair(that.KeyPair);
- Proof = new SchnorrProof(that.Proof);
- }
+ public Coefficient(Coefficient other)
- public bool IsValid()
{
- return Proof.IsValid() && Proof.PublicKey.Equals(KeyPair.PublicKey);
+ KeyPair = new ElGamalKeyPair(other.KeyPair);
+ Proof = new SchnorrProof(other.Proof);
}
protected override void DisposeUnmanaged()
diff --git a/bindings/netstandard/ElectionGuard/ElectionGuard.ElectionSetup/ElectionKeyPair.cs b/bindings/netstandard/ElectionGuard/ElectionGuard.ElectionSetup/ElectionKeyPair.cs
index 5a9fe89be..5558d145a 100644
--- a/bindings/netstandard/ElectionGuard/ElectionGuard.ElectionSetup/ElectionKeyPair.cs
+++ b/bindings/netstandard/ElectionGuard/ElectionGuard.ElectionSetup/ElectionKeyPair.cs
@@ -44,13 +44,14 @@ public string OwnerId
public ElectionKeyPair(
string ownerId,
ulong sequenceOrder,
- int quorum)
+ int quorum,
+ ElementModQ parameterHash)
{
GuardianId = ownerId;
SequenceOrder = sequenceOrder;
using var randQ = BigMath.RandQ();
KeyPair = new(randQ);
- Polynomial = new ElectionPolynomial(quorum, KeyPair);
+ Polynomial = new ElectionPolynomial(sequenceOrder, quorum, parameterHash, KeyPair);
}
///
@@ -62,25 +63,27 @@ public ElectionKeyPair(
string ownerId,
ulong sequenceOrder,
int quorum,
+ ElementModQ parameterHash,
ElGamalKeyPair keyPair)
{
GuardianId = ownerId;
SequenceOrder = sequenceOrder;
KeyPair = new(keyPair);
- Polynomial = new(quorum, keyPair);
+ Polynomial = new(sequenceOrder, quorum, parameterHash, keyPair);
}
public ElectionKeyPair(
string ownerId,
ulong sequenceOrder,
int quorum,
+ ElementModQ parameterHash,
ElGamalKeyPair keyPair,
Random random)
{
GuardianId = ownerId;
SequenceOrder = sequenceOrder;
KeyPair = new(keyPair);
- Polynomial = new(quorum, keyPair, random);
+ Polynomial = new(sequenceOrder, quorum, parameterHash, keyPair, random);
}
///
@@ -118,8 +121,6 @@ public ElectionKeyPair(
SequenceOrder = sequenceOrder;
KeyPair = new(keyPair.SecretKey, keyPair.PublicKey);
Polynomial = new(polynomial);
-
- // TODO: verify the polynomial is valid for the keypair
}
public ElectionKeyPair(ElectionKeyPair other)
@@ -128,8 +129,6 @@ public ElectionKeyPair(ElectionKeyPair other)
SequenceOrder = other.SequenceOrder;
KeyPair = new(other.KeyPair.SecretKey, other.KeyPair.PublicKey);
Polynomial = new(other.Polynomial);
-
- // TODO: verify the polynomial is valid for the keypair
}
public ElectionPublicKey Share()
diff --git a/bindings/netstandard/ElectionGuard/ElectionGuard.ElectionSetup/ElectionPolynomial.cs b/bindings/netstandard/ElectionGuard/ElectionGuard.ElectionSetup/ElectionPolynomial.cs
index 20220c870..ca5f38053 100644
--- a/bindings/netstandard/ElectionGuard/ElectionGuard.ElectionSetup/ElectionPolynomial.cs
+++ b/bindings/netstandard/ElectionGuard/ElectionGuard.ElectionSetup/ElectionPolynomial.cs
@@ -15,13 +15,14 @@ public class ElectionPolynomial : DisposableBase
{
///
/// A collection of coefficients corresponding to an exponential order of the polynomial
- /// the zero-index coefficient is used for a secret key
+ /// the zero-index coefficient is used for a secret key.
///
public List Coefficients { get; init; }
///
/// Access the list of public keys generated from secret coefficient.
- /// πΎπ,j = g^ππ,j mod p in the spec (7)
+ /// πΎπ,j = g^ππ,j mod p in the spec
+ /// Section 3.2.2 Details of key generation (10)
///
[JsonIgnore]
public List Commitments => Coefficients
@@ -40,14 +41,19 @@ public class ElectionPolynomial : DisposableBase
/// Generates a polynomial for sharing election keys.
/// Each coefficient is an exponential order for polynomial and the guardian secret key is the 0-index coefficient.
///
+ /// offset used to calculate the Coefficient
/// Number of coefficients of polynomial, typically the quorum count of guardians
+ /// parameter used to create proof
public ElectionPolynomial(
- [Range(1, int.MaxValue)] int numberOfCoefficients)
+ ulong sequenceOrder,
+ [Range(1, int.MaxValue)] int numberOfCoefficients,
+ ElementModQ parameterHash)
{
Coefficients = new List();
for (var i = 0; i < numberOfCoefficients; i++)
{
- Coefficients.Add(new Coefficient(BigMath.RandQ()));
+ using var secret = BigMath.RandQ();
+ Coefficients.Add(new Coefficient(sequenceOrder, i, parameterHash, secret));
}
}
@@ -55,14 +61,24 @@ public ElectionPolynomial(
/// Generates a polynomial for sharing election keys using the provided secret key as the zero-index coefficient.
/// Each coefficient is an exponential order for the polynomial and the guardian secret key is the 0-index coefficient.
///
+ /// offset used to calculate the Coefficient
/// Number of coefficients of polynomial, typically the quorum count of guardians
+ /// parameter used to create proof
+ /// secret used to make Coefficients
public ElectionPolynomial(
- [Range(1, int.MaxValue)] int numberOfCoefficients, ElementModQ secretKey)
+ ulong sequenceOrder,
+ [Range(1, int.MaxValue)] int numberOfCoefficients,
+ ElementModQ parameterHash,
+ ElementModQ secretKey)
{
- Coefficients = new List { new(secretKey) };
+ Coefficients = new List
+ {
+ new(sequenceOrder, 0, parameterHash, secretKey)
+ };
for (var i = 1; i < numberOfCoefficients; i++)
{
- Coefficients.Add(new Coefficient(BigMath.RandQ()));
+ using var secret = BigMath.RandQ();
+ Coefficients.Add(new Coefficient(sequenceOrder, i, parameterHash, secret));
}
}
@@ -70,14 +86,24 @@ public ElectionPolynomial(
/// Generates a polynomial for sharing election keys using the provided key pair as the zero-index coefficient.
/// Each coefficient is an exponential order for the polynomial and the guardian secret key is the 0-index coefficient.
///
+ /// offset used to calculate the Coefficient
/// Number of coefficients of polynomial, typically the quorum count of guardians
+ /// parameter used to create proof
+ /// keyPair with secret used to make Coefficients
public ElectionPolynomial(
- [Range(1, int.MaxValue)] int numberOfCoefficients, ElGamalKeyPair keyPair)
+ ulong sequenceOrder,
+ [Range(1, int.MaxValue)] int numberOfCoefficients,
+ ElementModQ parameterHash,
+ ElGamalKeyPair keyPair)
{
- Coefficients = new List { new(keyPair.SecretKey) };
+ Coefficients = new List
+ {
+ new(sequenceOrder, 0, parameterHash, keyPair.SecretKey)
+ };
for (var i = 1; i < numberOfCoefficients; i++)
{
- Coefficients.Add(new Coefficient(BigMath.RandQ()));
+ using var secret = BigMath.RandQ();
+ Coefficients.Add(new Coefficient(sequenceOrder, i, parameterHash, secret));
}
}
@@ -85,15 +111,27 @@ public ElectionPolynomial(
/// Generates a polynomial for sharing election keys using the provided key pair as the zero-index coefficient.
/// Each coefficient is an exponential order for the polynomial and the guardian secret key is the 0-index coefficient.
///
+ /// offset used to calculate the Coefficient
/// Number of coefficients of polynomial, typically the quorum count of guardians
+ /// parameter used to create proof
+ /// keyPair with secret used to make Coefficients
+ /// random number generator
public ElectionPolynomial(
+ ulong sequenceOrder,
[Range(1, int.MaxValue)] int numberOfCoefficients,
- ElGamalKeyPair keyPair, Random random)
+ ElementModQ parameterHash,
+ ElGamalKeyPair keyPair,
+ Random random)
{
- Coefficients = new List { new(keyPair.SecretKey) };
+ Coefficients = new List
+ {
+ new(sequenceOrder, 0, parameterHash, keyPair.SecretKey)
+ };
for (var i = 1; i < numberOfCoefficients; i++)
{
- Coefficients.Add(new Coefficient(random.NextElementModQ(), random.NextElementModQ()));
+ using var secret = random.NextElementModQ();
+ using var seed = random.NextElementModQ();
+ Coefficients.Add(new Coefficient(sequenceOrder, i, parameterHash, secret, seed));
}
}
@@ -104,10 +142,6 @@ public ElectionPolynomial(
[JsonConstructor]
public ElectionPolynomial(List coefficients)
{
- if (coefficients.Any(i => !i.IsValid()))
- {
- throw new ArgumentException("Invalid coefficients provided");
- }
Coefficients = coefficients.Select(i => new Coefficient(i)).ToList();
}
@@ -117,10 +151,6 @@ public ElectionPolynomial(List coefficients)
///
public ElectionPolynomial(ElectionPolynomial other)
{
- if (other.Coefficients.Any(i => !i.IsValid()))
- {
- throw new ArgumentException("Invalid coefficients provided");
- }
Coefficients = new List();
for (var i = 0; i < other.Coefficients.Count; i++)
{
diff --git a/bindings/netstandard/ElectionGuard/ElectionGuard.ElectionSetup/Guardian/Guardian.cs b/bindings/netstandard/ElectionGuard/ElectionGuard.ElectionSetup/Guardian/Guardian.cs
index d19f16240..2b7ef4c42 100644
--- a/bindings/netstandard/ElectionGuard/ElectionGuard.ElectionSetup/Guardian/Guardian.cs
+++ b/bindings/netstandard/ElectionGuard/ElectionGuard.ElectionSetup/Guardian/Guardian.cs
@@ -91,12 +91,13 @@ public Guardian(
ulong sequenceOrder,
int numberOfGuardians,
int quorum,
+ ElementModQ parameterHash,
string keyCeremonyId)
{
GuardianId = guardianId;
SequenceOrder = sequenceOrder;
- _myElectionKeys = new(guardianId, sequenceOrder, quorum);
+ _myElectionKeys = new(guardianId, sequenceOrder, quorum, parameterHash);
_commitmentSeed = BigMath.RandQ();
CeremonyDetails = new(keyCeremonyId, numberOfGuardians, quorum);
@@ -113,6 +114,7 @@ public Guardian(
ulong sequenceOrder,
int numberOfGuardians,
int quorum,
+ ElementModQ parameterHash,
string keyCeremonyId,
Random random)
{
@@ -121,7 +123,7 @@ public Guardian(
using var nextRandom = random.NextElementModQ();
var keyPair = new ElGamalKeyPair(nextRandom);
- _myElectionKeys = new(guardianId, sequenceOrder, quorum, keyPair, random);
+ _myElectionKeys = new(guardianId, sequenceOrder, quorum, parameterHash, keyPair, random);
_commitmentSeed = keyPair.SecretKey;
CeremonyDetails = new(keyCeremonyId, numberOfGuardians, quorum);
@@ -134,6 +136,7 @@ public Guardian(
public Guardian(
string guardianId,
ulong sequenceOrder,
+ ElementModQ parameterHash,
CeremonyDetails ceremonyDetails,
ElementModQ secretKey)
{
@@ -141,7 +144,7 @@ public Guardian(
SequenceOrder = sequenceOrder;
var keyPair = new ElGamalKeyPair(secretKey);
- _myElectionKeys = new(guardianId, sequenceOrder, ceremonyDetails.Quorum, keyPair);
+ _myElectionKeys = new(guardianId, sequenceOrder, ceremonyDetails.Quorum, parameterHash, keyPair);
_commitmentSeed = BigMath.RandQ();
CeremonyDetails = ceremonyDetails;
@@ -155,13 +158,14 @@ public Guardian(
string guardianId,
ulong sequenceOrder,
CeremonyDetails ceremonyDetails,
+ ElementModQ parameterHash,
ElGamalKeyPair elGamalKeyPair,
ElementModQ commitmentSeed)
{
GuardianId = guardianId;
SequenceOrder = sequenceOrder;
- _myElectionKeys = new(guardianId, sequenceOrder, ceremonyDetails.Quorum, elGamalKeyPair);
+ _myElectionKeys = new(guardianId, sequenceOrder, ceremonyDetails.Quorum, parameterHash, elGamalKeyPair);
_commitmentSeed = new(commitmentSeed);
CeremonyDetails = ceremonyDetails;
diff --git a/bindings/netstandard/ElectionGuard/ElectionGuard.ElectionSetup/KeyCeremonyMediator.cs b/bindings/netstandard/ElectionGuard/ElectionGuard.ElectionSetup/KeyCeremonyMediator.cs
index 1b99e8829..634282983 100644
--- a/bindings/netstandard/ElectionGuard/ElectionGuard.ElectionSetup/KeyCeremonyMediator.cs
+++ b/bindings/netstandard/ElectionGuard/ElectionGuard.ElectionSetup/KeyCeremonyMediator.cs
@@ -671,6 +671,7 @@ private async Task RunStep1()
sequenceOrder,
CeremonyDetails.NumberOfGuardians,
CeremonyDetails.Quorum,
+ CiphertextElectionContext.ParameterBaseHash,
keyCeremonyId);
// save guardian to local drive / yubikey
diff --git a/bindings/netstandard/ElectionGuard/ElectionGuard.Encryption/Ballot/BallotValidationExtensions.cs b/bindings/netstandard/ElectionGuard/ElectionGuard.Encryption/Ballot/BallotValidationExtensions.cs
index 65fc7feac..9a8387bf1 100644
--- a/bindings/netstandard/ElectionGuard/ElectionGuard.Encryption/Ballot/BallotValidationExtensions.cs
+++ b/bindings/netstandard/ElectionGuard/ElectionGuard.Encryption/Ballot/BallotValidationExtensions.cs
@@ -9,11 +9,26 @@ namespace ElectionGuard.Ballot
///
public class BallotValidationResult
{
+ ///
+ /// Was the ballot validation valid
+ ///
public bool IsValid { get; set; }
+
+ ///
+ /// Message to use for errors
+ ///
public string Message { get; set; }
+ ///
+ /// Heirarchy for the children validations
+ ///
public List Children { get; set; }
+ ///
+ /// Constructor for BallotValidationResult
+ ///
+ /// was the check valid or not
+ /// a list of children results
public BallotValidationResult(
bool isValid,
List children = null)
@@ -23,6 +38,11 @@ public BallotValidationResult(
Children = children ?? new List();
}
+ ///
+ /// Constructor for BallotValidationResult
+ ///
+ /// message to provide the user
+ /// a list of children results
public BallotValidationResult(
string message,
List children = null)
@@ -32,6 +52,12 @@ public BallotValidationResult(
Children = children ?? new List();
}
+ ///
+ /// Constructor for BallotValidationResult
+ ///
+ /// was the check valid or not
+ /// message to provide the user
+ /// a list of children results
public BallotValidationResult(
bool isValid,
string message,
@@ -42,6 +68,9 @@ public BallotValidationResult(
Children = children ?? new List();
}
+ ///
+ /// Get the string describing the results
+ ///
public override string ToString()
{
if (IsValid)
diff --git a/bindings/netstandard/ElectionGuard/ElectionGuard.Encryption/BigMath.cs b/bindings/netstandard/ElectionGuard/ElectionGuard.Encryption/BigMath.cs
index 196cd9bbc..df22f1b96 100644
--- a/bindings/netstandard/ElectionGuard/ElectionGuard.Encryption/BigMath.cs
+++ b/bindings/netstandard/ElectionGuard/ElectionGuard.Encryption/BigMath.cs
@@ -89,6 +89,11 @@ public static ElementModP PowModP(ElementModP b, ElementModQ e)
return value.IsInvalid ? null : new ElementModP(value);
}
+ ///
+ /// Raise a ElementModP value to a long exponent
+ ///
+ /// base value for the calculation
+ /// exponent to raise the base by
public static ElementModP PowModP(ElementModP b, ulong e)
{
using (var eQ = new ElementModQ(e, true))
@@ -98,6 +103,11 @@ public static ElementModP PowModP(ElementModP b, ulong e)
}
}
+ ///
+ /// Raise a ElementModQ value to a long exponent
+ ///
+ /// base value for the calculation
+ /// exponent to raise the base by
public static ElementModP PowModP(ElementModQ b, ulong e)
{
using (var bP = new ElementModP(b))
@@ -243,6 +253,20 @@ public static ElementModQ APlusBMulCModQ(ElementModQ a, ElementModQ b, ElementMo
return value.IsInvalid ? null : new ElementModQ(value);
}
+ ///
+ /// Calculate the formula a-b*c mod Q
+ ///
+ /// Value to subtract
+ /// First parameter to multiply
+ /// Second parameter to multiply
+ public static ElementModQ AMinusBMulCModQ(ElementModQ a, ElementModQ b, ElementModQ c)
+ {
+ var status = External.AMinusBMulCModQ(a.Handle, b.Handle, c.Handle,
+ out var value);
+ status.ThrowIfError();
+ return value.IsInvalid ? null : new ElementModQ(value);
+ }
+
///
/// Generate random number between 0 and Q.
///
@@ -408,6 +432,18 @@ internal static extern Status APlusBMulCModQ(
out NativeInterface.ElementModQ.ElementModQHandle handle
);
+ [DllImport(
+ NativeInterface.DllName,
+ EntryPoint = "eg_element_mod_q_a_minus_b_mul_c_mod_q",
+ CallingConvention = CallingConvention.Cdecl,
+ SetLastError = true)]
+ internal static extern Status AMinusBMulCModQ(
+ NativeInterface.ElementModQ.ElementModQHandle a,
+ NativeInterface.ElementModQ.ElementModQHandle b,
+ NativeInterface.ElementModQ.ElementModQHandle c,
+ out NativeInterface.ElementModQ.ElementModQHandle handle
+ );
+
[DllImport(
NativeInterface.DllName,
EntryPoint = "eg_element_mod_q_rand_q_new",
diff --git a/bindings/netstandard/ElectionGuard/ElectionGuard.Encryption/CiphertextElectionContext.cs b/bindings/netstandard/ElectionGuard/ElectionGuard.Encryption/CiphertextElectionContext.cs
index 50f79cabb..e5d2ef367 100644
--- a/bindings/netstandard/ElectionGuard/ElectionGuard.Encryption/CiphertextElectionContext.cs
+++ b/bindings/netstandard/ElectionGuard/ElectionGuard.Encryption/CiphertextElectionContext.cs
@@ -14,6 +14,23 @@ namespace ElectionGuard
///
public class CiphertextElectionContext : DisposableBase, IEquatable
{
+ ///
+ /// the `parameter hash HP = H(ver;0x00,p,q,g)` of the parameters compiled into the software.
+ ///
+ /// This static instance variable is populated when the software is compiled
+ /// and may be different than the parameters used in an election.
+ ///
+ public static ElementModQ ParameterBaseHash
+ {
+ get
+ {
+ var status = NativeInterface.CiphertextElectionContext.ParameterBaseHash(
+ out var value);
+ status.ThrowIfError();
+ return value.IsInvalid ? null : new ElementModQ(value);
+ }
+ }
+
///
/// Get a linked list containing the extended data of the election.
///
@@ -72,6 +89,23 @@ public ElementModP ElGamalPublicKey
}
}
+ ///
+ /// the `parameter hash HP = H(ver;0x00,p,q,g)` of the parameters used in the election.
+ ///
+ /// This instance variable is populated when the `CiphertextElectionContext` is created
+ /// and may use different parameters than the version that is compiled into the software.
+ ///
+ public ElementModQ ParameterHash
+ {
+ get
+ {
+ var status = NativeInterface.CiphertextElectionContext.GetParameterHash(
+ Handle, out var value);
+ status.ThrowIfError();
+ return value.IsInvalid ? null : new ElementModQ(value);
+ }
+ }
+
///
/// the `commitment hash H(K 1,0 , K 2,0 ... , K n,0 )` of the public commitments
/// guardians make to each other in the [ElectionGuard Spec](https://github.com/microsoft/electionguard/wiki)
@@ -143,7 +177,6 @@ public LinkedList ExtendedData
}
}
-
internal NativeInterface.CiphertextElectionContext.CiphertextElectionContextHandle Handle;
///
diff --git a/bindings/netstandard/ElectionGuard/ElectionGuard.Encryption/ElGamalKeyPair.cs b/bindings/netstandard/ElectionGuard/ElectionGuard.Encryption/ElGamalKeyPair.cs
index 05b701ff8..eea34b1d9 100644
--- a/bindings/netstandard/ElectionGuard/ElectionGuard.Encryption/ElGamalKeyPair.cs
+++ b/bindings/netstandard/ElectionGuard/ElectionGuard.Encryption/ElGamalKeyPair.cs
@@ -2,12 +2,12 @@
namespace ElectionGuard
{
///
- /// An exponential ElGamal key pair
+ /// An exponential ElGamal key pair computed as K = g^a mod p.
///
public class ElGamalKeyPair : DisposableBase
{
///
- /// The ElGamal Public Key.
+ /// The ElGamal Public Key (K in the spec).
///
public ElementModP PublicKey
{
@@ -21,7 +21,7 @@ public ElementModP PublicKey
}
///
- /// The ElGamal Secret Key.
+ /// The ElGamal Secret Key (a,k, or s in the spec).
///
public ElementModQ SecretKey
{
@@ -62,7 +62,7 @@ public ElGamalKeyPair(ElGamalKeyPair other) : this(other.SecretKey, other.Public
}
///
- /// Make an elgamal key pair from a secret.
+ /// Make an elgamal key pair from a secret computed as K = g^a mod p.
///
public static ElGamalKeyPair FromSecret(ElementModQ secretKey)
{
@@ -86,7 +86,11 @@ protected override void DisposeUnmanaged()
{
base.DisposeUnmanaged();
- if (Handle == null || Handle.IsInvalid) return;
+ if (Handle == null || Handle.IsInvalid)
+ {
+ return;
+ }
+
Handle.Dispose();
Handle = null;
}
diff --git a/bindings/netstandard/ElectionGuard/ElectionGuard.Encryption/Hash.cs b/bindings/netstandard/ElectionGuard/ElectionGuard.Encryption/Hash.cs
index a34a9e6fb..9dd45a45d 100644
--- a/bindings/netstandard/ElectionGuard/ElectionGuard.Encryption/Hash.cs
+++ b/bindings/netstandard/ElectionGuard/ElectionGuard.Encryption/Hash.cs
@@ -48,7 +48,7 @@ public static string Prefix_BaseHash
}
///
- /// H = (HP ; 10, i, j, Ki,j , hi,j ). Guardin Share proof challenge 3.2.2
+ /// H = (HP;10,i,j,Ki,j,hi,j). Guardin Share proof challenge 3.2.2
///
public static string Prefix_GuardianShareChallenge
{
diff --git a/bindings/netstandard/ElectionGuard/ElectionGuard.Encryption/NativeInterface.cs b/bindings/netstandard/ElectionGuard/ElectionGuard.Encryption/NativeInterface.cs
index 4f247f100..2f0da5dd9 100644
--- a/bindings/netstandard/ElectionGuard/ElectionGuard.Encryption/NativeInterface.cs
+++ b/bindings/netstandard/ElectionGuard/ElectionGuard.Encryption/NativeInterface.cs
@@ -2182,6 +2182,12 @@ protected override bool Free()
}
}
+ [DllImport(DllName,
+ EntryPoint = "eg_election_constant_parameter_hash",
+ CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
+ internal static extern Status ParameterBaseHash(
+ out ElementModQ.ElementModQHandle parameter_hash);
+
[DllImport(DllName, EntryPoint = "eg_ciphertext_election_context_free",
CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
internal static extern Status Free(CiphertextElectionType* handle);
@@ -2214,6 +2220,13 @@ internal static extern Status GetElGamalPublicKey(
CiphertextElectionContextHandle handle,
out ElementModP.ElementModPHandle elgamal_public_key);
+ [DllImport(DllName,
+ EntryPoint = "eg_ciphertext_election_context_get_parameter_hash",
+ CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
+ internal static extern Status GetParameterHash(
+ CiphertextElectionContextHandle handle,
+ out ElementModQ.ElementModQHandle parameter_hash);
+
[DllImport(DllName,
EntryPoint = "eg_ciphertext_election_context_get_commitment_hash",
CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
diff --git a/bindings/netstandard/ElectionGuard/ElectionGuard.Encryption/Proofs/SchnorrProof.cs b/bindings/netstandard/ElectionGuard/ElectionGuard.Encryption/Proofs/SchnorrProof.cs
index 21d67fedb..afd732639 100644
--- a/bindings/netstandard/ElectionGuard/ElectionGuard.Encryption/Proofs/SchnorrProof.cs
+++ b/bindings/netstandard/ElectionGuard/ElectionGuard.Encryption/Proofs/SchnorrProof.cs
@@ -1,3 +1,4 @@
+using System.Collections.Generic;
using Newtonsoft.Json;
namespace ElectionGuard.Proofs
@@ -8,8 +9,10 @@ namespace ElectionGuard.Proofs
///
public class SchnorrProof : DisposableBase
{
+
+
///
- /// k in the spec
+ /// K in the spec
///
public ElementModP PublicKey { get; private set; }
@@ -24,7 +27,7 @@ public class SchnorrProof : DisposableBase
public ElementModQ Challenge { get; private set; }
///
- /// u in the spec
+ /// v in the spec
///
public ElementModQ Response { get; private set; }
@@ -46,56 +49,74 @@ public SchnorrProof(
///
/// Create a new instance of a Schnorr proof using the provided secret and a random seed.
///
- public SchnorrProof(ElementModQ secretKey)
+ public SchnorrProof(
+ ulong offset,
+ int index,
+ ElementModQ parameterHash,
+ ElementModQ secretKey)
{
using (var keyPair = ElGamalKeyPair.FromSecret(secretKey))
using (var seed = BigMath.RandQ())
{
PublicKey = new ElementModP(keyPair.PublicKey);
Commitment = BigMath.GPowP(seed);
- Challenge = Hash.HashElems(PublicKey, Commitment);
- Response = BigMath.APlusBMulCModQ(seed, keyPair.SecretKey, Challenge);
+ Challenge = ComputeChallenge(parameterHash, offset, index, PublicKey, Commitment);
+ Response = BigMath.AMinusBMulCModQ(seed, keyPair.SecretKey, Challenge);
}
}
///
/// Create a new instance of a Schnorr proof using the provided key pair and a random seed.
///
- public SchnorrProof(ElGamalKeyPair keyPair)
+ public SchnorrProof(
+ ulong offset,
+ int index,
+ ElementModQ parameterHash,
+ ElGamalKeyPair keyPair)
{
using (var seed = BigMath.RandQ())
{
PublicKey = new ElementModP(keyPair.PublicKey);
Commitment = BigMath.GPowP(seed);
- Challenge = Hash.HashElems(PublicKey, Commitment);
- Response = BigMath.APlusBMulCModQ(seed, keyPair.SecretKey, Challenge);
+ Challenge = ComputeChallenge(parameterHash, offset, index, PublicKey, Commitment);
+ Response = BigMath.AMinusBMulCModQ(seed, keyPair.SecretKey, Challenge);
}
}
///
/// Create a new instance of a Schnorr proof using the provided secret and seed.
///
- public SchnorrProof(ElementModQ secretKey, ElementModQ seed)
+ public SchnorrProof(
+ ulong offset,
+ int index,
+ ElementModQ parameterHash,
+ ElementModQ secretKey,
+ ElementModQ seed)
{
using (var keyPair = ElGamalKeyPair.FromSecret(secretKey))
{
PublicKey = new ElementModP(keyPair.PublicKey);
Commitment = BigMath.GPowP(seed);
- Challenge = Hash.HashElems(PublicKey, Commitment);
- Response = BigMath.APlusBMulCModQ(seed, keyPair.SecretKey, Challenge);
+ Challenge = ComputeChallenge(parameterHash, offset, index, PublicKey, Commitment);
+ Response = BigMath.AMinusBMulCModQ(seed, keyPair.SecretKey, Challenge);
}
}
///
/// Create a new instance of a Schnorr proof using the provided key pair and seed.
///
- public SchnorrProof(ElGamalKeyPair keyPair, ElementModQ seed)
+ public SchnorrProof(
+ ulong offset,
+ int index,
+ ElementModQ parameterHash,
+ ElGamalKeyPair keyPair,
+ ElementModQ seed)
{
PublicKey = new ElementModP(keyPair.PublicKey);
Commitment = BigMath.GPowP(seed);
- Challenge = Hash.HashElems(PublicKey, Commitment);
- Response = BigMath.APlusBMulCModQ(seed, keyPair.SecretKey, Challenge);
+ Challenge = ComputeChallenge(parameterHash, offset, index, PublicKey, Commitment);
+ Response = BigMath.AMinusBMulCModQ(seed, keyPair.SecretKey, Challenge);
}
public SchnorrProof(SchnorrProof other)
@@ -107,49 +128,43 @@ public SchnorrProof(SchnorrProof other)
}
///
- /// Check validity of the `proof` for proving possession of the secret key corresponding to the public key
+ /// Verification 2 (Guardian public-key validation).
+ /// Check validity of the `proof` for proving possession of the secret key
+ /// corresponding to the public key.
///
- public bool IsValid()
+ public ValidationResult IsValid(
+ ulong offset,
+ int index,
+ ElementModQ parameterHash
+ )
{
- var k = PublicKey;
- var h = Commitment;
- var u = Response;
- var validPublicKey = k.IsValidResidue();
- var inBoundsH = h.IsInBounds();
- var inBoundsU = u.IsInBounds();
#pragma warning disable IDE0063 // Use simple 'using' statement. Need to support Net Standard 2.0, which doesn't have this.
- using (var c = Hash.HashElems(k, h))
- using (var gp = BigMath.GPowP(u))
- using (var pp = BigMath.PowModP(k, c))
- using (var mp = BigMath.MultModP(h, pp))
+ using (var challenge = ComputeChallenge(parameterHash, offset, index, PublicKey, Commitment))
+ using (var gv = BigMath.GPowP(Response)) // G^v mod p
+ using (var kc = BigMath.PowModP(PublicKey, challenge)) // K^c mod p
+ using (var gvkc = BigMath.MultModP(gv, kc)) // G^v * K^c mod p
#pragma warning restore IDE0063
{
-
- var validChallenge = c.Equals(Challenge);
- var validProof = gp.Equals(mp);
-
- var success = validPublicKey && inBoundsH && inBoundsU && validChallenge && validProof;
+ var validCommitment = Commitment.Equals(gvkc); // hi,j = gvi,j Β· Kci,j mod p
+ var validChallenge = Challenge.Equals(challenge); // ci,j = H (HP;0x10,i,j,Kij,hij)
+
+ var messages = new List();
+ var success = PublicKey.IsValidResidue()
+ && Commitment.IsInBounds()
+ && Response.IsInBounds()
+ && validCommitment && validChallenge;
if (success is false)
{
- #region commented Code
- // TODO: result
- //log_warning(
- // "found an invalid Schnorr proof: %s",
- // str(
- // {
- // "in_bounds_h": in_bounds_h,
- // "in_bounds_u": in_bounds_u,
- // "valid_public_key": valid_public_key,
- // "valid_challenge": valid_challenge,
- // "valid_proof": valid_proof,
- // "proof": self,
- // }
- // ),
- // )
- #endregion
+
+ messages.Add("found an invalid Schnorr proof");
+ messages.Add($"in_bounds_h: {Commitment.IsInBounds()}");
+ messages.Add($"in_bounds_v: {Response.IsInBounds()}");
+ messages.Add($"valid_public_key: {PublicKey.IsValidResidue()}");
+ messages.Add($"valid_commitment: {validCommitment}");
+ messages.Add($"valid_challenge: {validChallenge}");
}
- return success;
+ return new ValidationResult() { Success = success, Error = messages };
}
}
@@ -162,5 +177,26 @@ protected override void DisposeUnmanaged()
Challenge.Dispose();
Response.Dispose();
}
+
+ ///
+ /// H = (HP;10,i,j,Ki,j,hi,j). Guardin Share proof challenge 3.2.2
+ ///
+ private ElementModQ ComputeChallenge(
+ ElementModQ parameterHash,
+ ulong offset,
+ int index,
+ ElementModP publicKey,
+ ElementModP commitment)
+ {
+ using (var offsetElement = new ElementModQ(offset))
+ using (var indexElement = new ElementModQ((ulong)index))
+ {
+ return Hash.HashElems(
+ parameterHash,
+ Hash.Prefix_GuardianShareChallenge,
+ offsetElement, indexElement,
+ publicKey, commitment);
+ }
+ }
}
}
diff --git a/bindings/netstandard/ElectionGuard/ElectionGuard.Encryption/ValidationResult.cs b/bindings/netstandard/ElectionGuard/ElectionGuard.Encryption/ValidationResult.cs
new file mode 100644
index 000000000..5800b6e8c
--- /dev/null
+++ b/bindings/netstandard/ElectionGuard/ElectionGuard.Encryption/ValidationResult.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+namespace ElectionGuard
+{
+ [StructLayout(LayoutKind.Sequential)]
+ public struct ValidationResult
+ {
+ public bool Success { get; set; }
+ public List Error { get; set; }
+ }
+}
diff --git a/include/electionguard/election.h b/include/electionguard/election.h
index 0731337f7..ec40e11b0 100644
--- a/include/electionguard/election.h
+++ b/include/electionguard/election.h
@@ -13,6 +13,13 @@
extern "C" {
#endif
+#ifndef Election Constants
+
+EG_API eg_electionguard_status_t
+eg_election_constant_parameter_hash(eg_element_mod_q_t **out_constant_ref);
+
+#endif
+
#ifndef ContextConfiguration
struct eg_context_configuration_s;
@@ -41,15 +48,6 @@ typedef struct eg_ciphertext_election_context_s eg_ciphertext_election_context_t
EG_API eg_electionguard_status_t
eg_ciphertext_election_context_free(eg_ciphertext_election_context_t *handle);
-/**
- * The `joint public key (K)` in the [ElectionGuard Spec](https://github.com/microsoft/electionguard/wiki)
- *
- * @param[out] out_elgamal_public_key_ref An opaque pointer to the public key.
- * The value is a reference and is not owned by the caller
- */
-EG_API eg_electionguard_status_t eg_ciphertext_election_context_get_elgamal_public_key(
- eg_ciphertext_election_context_t *handle, eg_element_mod_p_t **out_elgamal_public_key_ref);
-
/**
* The `joint public key (K)` in the [ElectionGuard Spec](https://github.com/microsoft/electionguard/wiki)
*
@@ -75,6 +73,24 @@ EG_API eg_electionguard_status_t eg_ciphertext_election_context_get_number_of_gu
EG_API eg_electionguard_status_t eg_ciphertext_election_context_get_quorum(
eg_ciphertext_election_context_t *handle, uint64_t *out_quorum);
+/**
+ * The `joint public key (K)` in the [ElectionGuard Spec](https://github.com/microsoft/electionguard/wiki)
+ *
+ * @param[out] out_elgamal_public_key_ref An opaque pointer to the public key.
+ * The value is a reference and is not owned by the caller
+ */
+EG_API eg_electionguard_status_t eg_ciphertext_election_context_get_elgamal_public_key(
+ eg_ciphertext_election_context_t *handle, eg_element_mod_p_t **out_elgamal_public_key_ref);
+
+/**
+ * the `parameter hash HP = H(ver;0x00,p,q,g)` of the parameters used in the election.
+ *
+ * @param[out] out_parameter_hash_ref An opaque pointer to the parameter hash.
+ * The value is a reference and is not owned by the caller
+ */
+EG_API eg_electionguard_status_t eg_ciphertext_election_context_get_parameter_hash(
+ eg_ciphertext_election_context_t *handle, eg_element_mod_q_t **out_parameter_hash_ref);
+
/**
* The `commitment hash H(K 1,0 , K 2,0 ... , K n,0 )` of the public commitments
* guardians make to each other in the [ElectionGuard Spec](https://github.com/microsoft/electionguard/wiki)
diff --git a/include/electionguard/election.hpp b/include/electionguard/election.hpp
index e3ef6c610..e48ce4f6b 100644
--- a/include/electionguard/election.hpp
+++ b/include/electionguard/election.hpp
@@ -16,6 +16,14 @@ using std::make_unique;
namespace electionguard
{
+ ///
+ /// The prime modulus p, the subgroup order q, and the subgroup generator g are hashed using the ElectionGuard hash function H.
+ /// HP = H(HV ;00,p,q,g). Parameter Hash 3.1.2
+ ///
+ /// This static version is compiled into the software and may be updated from time to time according to the ElectionGuard Specification
+ ///
+ EG_API const ElementModQ &PARAMETER_BASE_HASH();
+
///
/// Structure for supporting the configuration settings inside of the Electioncontext
///
@@ -62,17 +70,40 @@ namespace electionguard
CiphertextElectionContext(const CiphertextElectionContext &&other);
explicit CiphertextElectionContext(const uint64_t numberOfGuardians, const uint64_t quorum,
std::unique_ptr elGamalPublicKey,
+ std::unique_ptr parameterHash,
std::unique_ptr commitmentHash,
std::unique_ptr manifestHash,
std::unique_ptr cryptoBaseHash,
std::unique_ptr cryptoExtendedBaseHash,
std::unique_ptr config);
+ explicit CiphertextElectionContext(const uint64_t numberOfGuardians, const uint64_t quorum,
+ std::unique_ptr elGamalPublicKey,
+ std::unique_ptr commitmentHash,
+ std::unique_ptr manifestHash,
+ std::unique_ptr cryptoBaseHash,
+ std::unique_ptr cryptoExtendedBaseHash,
+ std::unique_ptr config);
+ explicit CiphertextElectionContext(const uint64_t numberOfGuardians, const uint64_t quorum,
+ std::unique_ptr elGamalPublicKey,
+ std::unique_ptr parameterHash,
+ std::unique_ptr commitmentHash,
+ std::unique_ptr manifestHash,
+ std::unique_ptr cryptoBaseHash,
+ std::unique_ptr cryptoExtendedBaseHash);
explicit CiphertextElectionContext(const uint64_t numberOfGuardians, const uint64_t quorum,
std::unique_ptr elGamalPublicKey,
std::unique_ptr commitmentHash,
std::unique_ptr manifestHash,
std::unique_ptr cryptoBaseHash,
std::unique_ptr cryptoExtendedBaseHash);
+ explicit CiphertextElectionContext(
+ const uint64_t numberOfGuardians, const uint64_t quorum,
+ std::unique_ptr elGamalPublicKey, std::unique_ptr parameterHash,
+ std::unique_ptr commitmentHash, std::unique_ptr manifestHash,
+ std::unique_ptr cryptoBaseHash,
+ std::unique_ptr cryptoExtendedBaseHash,
+ std::unique_ptr config,
+ std::unordered_map extendedData);
explicit CiphertextElectionContext(
const uint64_t numberOfGuardians, const uint64_t quorum,
std::unique_ptr elGamalPublicKey,
@@ -81,6 +112,13 @@ namespace electionguard
std::unique_ptr cryptoExtendedBaseHash,
std::unique_ptr config,
std::unordered_map extendedData);
+ explicit CiphertextElectionContext(
+ const uint64_t numberOfGuardians, const uint64_t quorum,
+ std::unique_ptr elGamalPublicKey, std::unique_ptr parameterHash,
+ std::unique_ptr commitmentHash, std::unique_ptr manifestHash,
+ std::unique_ptr cryptoBaseHash,
+ std::unique_ptr cryptoExtendedBaseHash,
+ std::unordered_map extendedData);
explicit CiphertextElectionContext(
const uint64_t numberOfGuardians, const uint64_t quorum,
std::unique_ptr elGamalPublicKey,
@@ -115,6 +153,14 @@ namespace electionguard
const ElementModP &getElGamalPublicKeyRef() const;
+ ///
+ /// the `parameter hash HP = H(ver;0x00,p,q,g)` of the parameters used in the election.
+ ///
+ /// This member function holds the parameter hash values used for the election,
+ /// which may be different than the values compiled into this version of the software.
+ ///
+ const ElementModQ *getParameterHash() const;
+
///
/// the `commitment hash H(K 1,0 , K 2,0 ... , K n,0 )` of the public commitments
/// guardians make to each other in the [ElectionGuard Spec](https://github.com/microsoft/electionguard/wiki)
diff --git a/include/electionguard/elgamal.hpp b/include/electionguard/elgamal.hpp
index 17d5a389d..d523bd71e 100644
--- a/include/electionguard/elgamal.hpp
+++ b/include/electionguard/elgamal.hpp
@@ -11,7 +11,7 @@
namespace electionguard
{
///
- /// An exponential ElGamal keypair.
+ /// An exponential ElGamal keypair computed as K = g^a mod p.
///
class EG_API ElGamalKeyPair
{
@@ -26,27 +26,27 @@ namespace electionguard
ElGamalKeyPair &operator=(ElGamalKeyPair &&rhs);
///
- /// The ElGamal Secret Key.
+ /// The ElGamal Secret Key (a,k, or s in the spec).
///
ElementModQ *getSecretKey();
///
- /// The ElGamal Secret Key.
+ /// The ElGamal Secret Key (a,k, or s in the spec).
///
ElementModQ *getSecretKey() const;
///
- /// The ElGamal Public Key.
+ /// The ElGamal Public Key (K in the spec).
///
ElementModP *getPublicKey();
///
- /// The ElGamal Public Key.
+ /// The ElGamal Public Key (K in the spec).
///
ElementModP *getPublicKey() const;
///
- /// Make an elgamal keypair from a secret.
+ /// Make an elgamal keypair from a secret by computing K = g^a mod p.
///
static std::unique_ptr fromSecret(const ElementModQ &secretKey,
bool isFixedBase = true);
diff --git a/include/electionguard/group.h b/include/electionguard/group.h
index b8ebefce4..5569ff991 100644
--- a/include/electionguard/group.h
+++ b/include/electionguard/group.h
@@ -190,6 +190,10 @@ EG_API eg_electionguard_status_t
eg_element_mod_q_a_plus_b_mul_c_mod_q(eg_element_mod_q_t *a, eg_element_mod_q_t *b,
eg_element_mod_q_t *c, eg_element_mod_q_t **out_handle);
+EG_API eg_electionguard_status_t
+eg_element_mod_q_a_minus_b_mul_c_mod_q(eg_element_mod_q_t *a, eg_element_mod_q_t *b,
+ eg_element_mod_q_t *c, eg_element_mod_q_t **out_handle);
+
EG_API eg_electionguard_status_t eg_element_mod_q_rand_q_new(eg_element_mod_q_t **out_handle);
#endif
diff --git a/src/electionguard/election.cpp b/src/electionguard/election.cpp
index df4c2cc27..680cd3798 100644
--- a/src/electionguard/election.cpp
+++ b/src/electionguard/election.cpp
@@ -27,6 +27,15 @@ using ContextSerializer = electionguard::Serialize::CiphertextElectionContext;
namespace electionguard
{
+ const ElementModQ &PARAMETER_BASE_HASH()
+ {
+ static auto versionCode = string_to_fixed_width_bytes<32>("v2.0.0");
+ static auto parameterHash = hash_elems(
+ {versionCode, HashPrefix::get_prefix_parameter_hash(), &const_cast(P()),
+ &const_cast(Q()), &const_cast(G())});
+ static ElementModQ instance{parameterHash->ref(), true};
+ return instance;
+ }
#pragma region CiphertextElectionContext
@@ -34,6 +43,7 @@ namespace electionguard
uint64_t numberOfGuardians;
uint64_t quorum;
unique_ptr elGamalPublicKey;
+ unique_ptr parameterHash;
unique_ptr commitmentHash;
unique_ptr manifestHash;
unique_ptr cryptoBaseHash;
@@ -42,10 +52,12 @@ namespace electionguard
unique_ptr configuration;
Impl(uint64_t numberOfGuardians, uint64_t quorum, unique_ptr elGamalPublicKey,
- unique_ptr commitmentHash, unique_ptr manifestHash,
- unique_ptr cryptoBaseHash, unique_ptr cryptoExtendedBaseHash)
- : elGamalPublicKey(move(elGamalPublicKey)), commitmentHash(move(commitmentHash)),
- manifestHash(move(manifestHash)), cryptoBaseHash(move(cryptoBaseHash)),
+ unique_ptr parameterHash, unique_ptr commitmentHash,
+ unique_ptr manifestHash, unique_ptr cryptoBaseHash,
+ unique_ptr cryptoExtendedBaseHash)
+ : elGamalPublicKey(move(elGamalPublicKey)), parameterHash(move(parameterHash)),
+ commitmentHash(move(commitmentHash)), manifestHash(move(manifestHash)),
+ cryptoBaseHash(move(cryptoBaseHash)),
cryptoExtendedBaseHash(move(cryptoExtendedBaseHash))
{
this->numberOfGuardians = numberOfGuardians;
@@ -55,11 +67,13 @@ namespace electionguard
}
Impl(uint64_t numberOfGuardians, uint64_t quorum, unique_ptr elGamalPublicKey,
- unique_ptr commitmentHash, unique_ptr manifestHash,
- unique_ptr cryptoBaseHash, unique_ptr cryptoExtendedBaseHash,
+ unique_ptr parameterHash, unique_ptr commitmentHash,
+ unique_ptr manifestHash, unique_ptr cryptoBaseHash,
+ unique_ptr cryptoExtendedBaseHash,
unique_ptr config)
- : Impl(numberOfGuardians, quorum, move(elGamalPublicKey), move(commitmentHash),
- move(manifestHash), move(cryptoBaseHash), move(cryptoExtendedBaseHash))
+ : Impl(numberOfGuardians, quorum, move(elGamalPublicKey), move(parameterHash),
+ move(commitmentHash), move(manifestHash), move(cryptoBaseHash),
+ move(cryptoExtendedBaseHash))
{
this->numberOfGuardians = numberOfGuardians;
@@ -69,11 +83,13 @@ namespace electionguard
}
Impl(uint64_t numberOfGuardians, uint64_t quorum, unique_ptr elGamalPublicKey,
- unique_ptr commitmentHash, unique_ptr manifestHash,
- unique_ptr cryptoBaseHash, unique_ptr cryptoExtendedBaseHash,
+ unique_ptr parameterHash, unique_ptr commitmentHash,
+ unique_ptr manifestHash, unique_ptr cryptoBaseHash,
+ unique_ptr cryptoExtendedBaseHash,
unordered_map extendedData)
- : elGamalPublicKey(move(elGamalPublicKey)), commitmentHash(move(commitmentHash)),
- manifestHash(move(manifestHash)), cryptoBaseHash(move(cryptoBaseHash)),
+ : elGamalPublicKey(move(elGamalPublicKey)), parameterHash(move(parameterHash)),
+ commitmentHash(move(commitmentHash)), manifestHash(move(manifestHash)),
+ cryptoBaseHash(move(cryptoBaseHash)),
cryptoExtendedBaseHash(move(cryptoExtendedBaseHash)), extendedData(move(extendedData))
{
this->numberOfGuardians = numberOfGuardians;
@@ -82,12 +98,13 @@ namespace electionguard
}
Impl(uint64_t numberOfGuardians, uint64_t quorum, unique_ptr elGamalPublicKey,
- unique_ptr commitmentHash, unique_ptr manifestHash,
- unique_ptr cryptoBaseHash, unique_ptr cryptoExtendedBaseHash,
+ unique_ptr parameterHash, unique_ptr commitmentHash,
+ unique_ptr manifestHash, unique_ptr cryptoBaseHash,
+ unique_ptr cryptoExtendedBaseHash,
unique_ptr config, unordered_map extendedData)
- : Impl(numberOfGuardians, quorum, move(elGamalPublicKey), move(commitmentHash),
- move(manifestHash), move(cryptoBaseHash), move(cryptoExtendedBaseHash),
- move(extendedData))
+ : Impl(numberOfGuardians, quorum, move(elGamalPublicKey), move(parameterHash),
+ move(commitmentHash), move(manifestHash), move(cryptoBaseHash),
+ move(cryptoExtendedBaseHash), move(extendedData))
{
this->numberOfGuardians = numberOfGuardians;
this->quorum = quorum;
@@ -97,40 +114,85 @@ namespace electionguard
// Lifecycle Methods
+ CiphertextElectionContext::CiphertextElectionContext(
+ uint64_t numberOfGuardians, uint64_t quorum, unique_ptr elGamalPublicKey,
+ unique_ptr parameterHash, unique_ptr commitmentHash,
+ unique_ptr manifestHash, unique_ptr cryptoBaseHash,
+ unique_ptr cryptoExtendedBaseHash)
+ : pimpl(new Impl(numberOfGuardians, quorum, move(elGamalPublicKey), move(parameterHash),
+ move(commitmentHash), move(manifestHash), move(cryptoBaseHash),
+ move(cryptoExtendedBaseHash)))
+ {
+ }
CiphertextElectionContext::CiphertextElectionContext(
uint64_t numberOfGuardians, uint64_t quorum, unique_ptr elGamalPublicKey,
unique_ptr commitmentHash, unique_ptr manifestHash,
unique_ptr cryptoBaseHash, unique_ptr cryptoExtendedBaseHash)
- : pimpl(new Impl(numberOfGuardians, quorum, move(elGamalPublicKey), move(commitmentHash),
+ : pimpl(new Impl(numberOfGuardians, quorum, move(elGamalPublicKey),
+ move(PARAMETER_BASE_HASH().clone()), move(commitmentHash),
move(manifestHash), move(cryptoBaseHash), move(cryptoExtendedBaseHash)))
{
}
+ CiphertextElectionContext::CiphertextElectionContext(
+ uint64_t numberOfGuardians, uint64_t quorum, unique_ptr elGamalPublicKey,
+ unique_ptr parameterHash, unique_ptr commitmentHash,
+ unique_ptr manifestHash, unique_ptr cryptoBaseHash,
+ unique_ptr cryptoExtendedBaseHash, unique_ptr config)
+ : pimpl(new Impl(numberOfGuardians, quorum, move(elGamalPublicKey), move(parameterHash),
+ move(commitmentHash), move(manifestHash), move(cryptoBaseHash),
+ move(cryptoExtendedBaseHash), move(config)))
+ {
+ }
CiphertextElectionContext::CiphertextElectionContext(
uint64_t numberOfGuardians, uint64_t quorum, unique_ptr elGamalPublicKey,
unique_ptr commitmentHash, unique_ptr manifestHash,
unique_ptr cryptoBaseHash, unique_ptr cryptoExtendedBaseHash,
unique_ptr config)
- : pimpl(new Impl(numberOfGuardians, quorum, move(elGamalPublicKey), move(commitmentHash),
+ : pimpl(new Impl(numberOfGuardians, quorum, move(elGamalPublicKey),
+ move(PARAMETER_BASE_HASH().clone()), move(commitmentHash),
move(manifestHash), move(cryptoBaseHash), move(cryptoExtendedBaseHash),
move(config)))
{
}
+ CiphertextElectionContext::CiphertextElectionContext(
+ uint64_t numberOfGuardians, uint64_t quorum, unique_ptr elGamalPublicKey,
+ unique_ptr parameterHash, unique_ptr commitmentHash,
+ unique_ptr manifestHash, unique_ptr cryptoBaseHash,
+ unique_ptr cryptoExtendedBaseHash, unordered_map extendedData)
+ : pimpl(new Impl(numberOfGuardians, quorum, move(elGamalPublicKey), move(parameterHash),
+ move(commitmentHash), move(manifestHash), move(cryptoBaseHash),
+ move(cryptoExtendedBaseHash), move(extendedData)))
+ {
+ }
CiphertextElectionContext::CiphertextElectionContext(
uint64_t numberOfGuardians, uint64_t quorum, unique_ptr elGamalPublicKey,
unique_ptr commitmentHash, unique_ptr manifestHash,
unique_ptr cryptoBaseHash, unique_ptr cryptoExtendedBaseHash,
unordered_map extendedData)
- : pimpl(new Impl(numberOfGuardians, quorum, move(elGamalPublicKey), move(commitmentHash),
+ : pimpl(new Impl(numberOfGuardians, quorum, move(elGamalPublicKey),
+ move(PARAMETER_BASE_HASH().clone()), move(commitmentHash),
move(manifestHash), move(cryptoBaseHash), move(cryptoExtendedBaseHash),
move(extendedData)))
{
}
+ CiphertextElectionContext::CiphertextElectionContext(
+ uint64_t numberOfGuardians, uint64_t quorum, unique_ptr elGamalPublicKey,
+ unique_ptr parameterHash, unique_ptr commitmentHash,
+ unique_ptr manifestHash, unique_ptr cryptoBaseHash,
+ unique_ptr cryptoExtendedBaseHash, unique_ptr config,
+ unordered_map extendedData)
+ : pimpl(new Impl(numberOfGuardians, quorum, move(elGamalPublicKey), move(parameterHash),
+ move(commitmentHash), move(manifestHash), move(cryptoBaseHash),
+ move(cryptoExtendedBaseHash), move(config), move(extendedData)))
+ {
+ }
CiphertextElectionContext::CiphertextElectionContext(
uint64_t numberOfGuardians, uint64_t quorum, unique_ptr elGamalPublicKey,
unique_ptr commitmentHash, unique_ptr manifestHash,
unique_ptr cryptoBaseHash, unique_ptr cryptoExtendedBaseHash,
unique_ptr config, unordered_map extendedData)
- : pimpl(new Impl(numberOfGuardians, quorum, move(elGamalPublicKey), move(commitmentHash),
+ : pimpl(new Impl(numberOfGuardians, quorum, move(elGamalPublicKey),
+ move(PARAMETER_BASE_HASH().clone()), move(commitmentHash),
move(manifestHash), move(cryptoBaseHash), move(cryptoExtendedBaseHash),
move(config), move(extendedData)))
{
@@ -164,6 +226,10 @@ namespace electionguard
{
return *pimpl->elGamalPublicKey.get();
}
+ const ElementModQ *CiphertextElectionContext::getParameterHash() const
+ {
+ return pimpl->parameterHash.get();
+ }
const ElementModQ *CiphertextElectionContext::getCommitmentHash() const
{
return pimpl->commitmentHash.get();
@@ -211,20 +277,12 @@ namespace electionguard
uint64_t numberOfGuardians, uint64_t quorum, unique_ptr elGamalPublicKey,
unique_ptr commitmentHash, unique_ptr manifestHash)
{
- // TODO: configurable version code
-
- // HP = H(HV ;00,p,q,g). Parameter Hash 3.1.2
- auto versionCode = string_to_fixed_width_bytes<32>("v2.0");
- auto parameterHash = hash_elems(
- {versionCode, HashPrefix::get_prefix_parameter_hash(), &const_cast(P()),
- &const_cast(Q()), &const_cast(G())});
-
// HM = H(HP;01,manifest). Manifest Hash 3.1.4
auto manifestDigest = hash_elems(
- {parameterHash.get(), HashPrefix::get_prefix_manifest_hash(), manifestHash.get()});
+ {PARAMETER_BASE_HASH(), HashPrefix::get_prefix_manifest_hash(), manifestHash.get()});
// HB =(HP;02,n,k,date,info,HM). Election Base Hash 3.1.5
- auto cryptoBaseHash = hash_elems({parameterHash.get(), HashPrefix::get_prefix_base_hash(),
+ auto cryptoBaseHash = hash_elems({PARAMETER_BASE_HASH(), HashPrefix::get_prefix_base_hash(),
manifestDigest.get(), numberOfGuardians, quorum});
// HE = H(HB;12,K,K1,0,K1,1,...,K1,kβ1,K2,0,...,Kn,kβ2,Kn,kβ1). // Extended Base Hash 3.2.3
@@ -236,8 +294,9 @@ namespace electionguard
elGamalPublicKey->setIsFixedBase(true);
return make_unique(
- numberOfGuardians, quorum, move(elGamalPublicKey), move(commitmentHash),
- move(manifestHash), move(cryptoBaseHash), move(cryptoExtendedBaseHash));
+ numberOfGuardians, quorum, move(elGamalPublicKey), PARAMETER_BASE_HASH().clone(),
+ move(commitmentHash), move(manifestHash), move(cryptoBaseHash),
+ move(cryptoExtendedBaseHash));
}
unique_ptr CiphertextElectionContext::make(
@@ -245,21 +304,13 @@ namespace electionguard
unique_ptr commitmentHash, unique_ptr manifestHash,
unique_ptr config)
{
- // TODO: configurable version code
-
- // HP = H(HV ;00,p,q,g). Parameter Hash 3.1.2
- auto versionCode = string_to_fixed_width_bytes<32>("v2.0");
- auto parameterHash = hash_elems(
- {versionCode, HashPrefix::get_prefix_parameter_hash(), &const_cast(P()),
- &const_cast(Q()), &const_cast(G())});
-
// HM = H(HP;01,manifest). Manifest Hash 3.1.4
auto manifestDigest = hash_elems(
- {parameterHash.get(), HashPrefix::get_prefix_manifest_hash(), manifestHash.get()});
+ {PARAMETER_BASE_HASH(), HashPrefix::get_prefix_manifest_hash(), manifestHash.get()});
// TODO: complete according to spec
// HB =(HP;02,n,k,date,info,HM). Election Base Hash 3.1.5
- auto cryptoBaseHash = hash_elems({parameterHash.get(), HashPrefix::get_prefix_base_hash(),
+ auto cryptoBaseHash = hash_elems({PARAMETER_BASE_HASH(), HashPrefix::get_prefix_base_hash(),
numberOfGuardians, quorum, manifestDigest.get()});
// HE = H(HB;12,K,K1,0,K1,1,...,K1,kβ1,K2,0,...,Kn,kβ2,Kn,kβ1). // Extended Base Hash 3.2.3
@@ -271,8 +322,9 @@ namespace electionguard
elGamalPublicKey->setIsFixedBase(true);
return make_unique(
- numberOfGuardians, quorum, move(elGamalPublicKey), move(commitmentHash),
- move(manifestHash), move(cryptoBaseHash), move(cryptoExtendedBaseHash), move(config));
+ numberOfGuardians, quorum, move(elGamalPublicKey), PARAMETER_BASE_HASH().clone(),
+ move(commitmentHash), move(manifestHash), move(cryptoBaseHash),
+ move(cryptoExtendedBaseHash), move(config));
}
unique_ptr CiphertextElectionContext::make(
@@ -280,21 +332,13 @@ namespace electionguard
unique_ptr commitmentHash, unique_ptr manifestHash,
std::unordered_map extendedData)
{
- // TODO: configurable version code
-
- // HP = H(HV ;00,p,q,g). Parameter Hash 3.1.2
- auto versionCode = string_to_fixed_width_bytes<32>("v2.0");
- auto parameterHash = hash_elems(
- {versionCode, HashPrefix::get_prefix_parameter_hash(), &const_cast(P()),
- &const_cast(Q()), &const_cast(G())});
-
// HM = H(HP;01,manifest). Manifest Hash 3.1.4
auto manifestDigest = hash_elems(
- {parameterHash.get(), HashPrefix::get_prefix_manifest_hash(), manifestHash.get()});
+ {PARAMETER_BASE_HASH(), HashPrefix::get_prefix_manifest_hash(), manifestHash.get()});
// TODO: complete according to spec
// HB =(HP;02,n,k,date,info,HM). Election Base Hash 3.1.5
- auto cryptoBaseHash = hash_elems({parameterHash.get(), HashPrefix::get_prefix_base_hash(),
+ auto cryptoBaseHash = hash_elems({PARAMETER_BASE_HASH(), HashPrefix::get_prefix_base_hash(),
numberOfGuardians, quorum, manifestDigest.get()});
// HE = H(HB;12,K,K1,0,K1,1,...,K1,kβ1,K2,0,...,Kn,kβ2,Kn,kβ1). // Extended Base Hash 3.2.3
@@ -306,9 +350,9 @@ namespace electionguard
elGamalPublicKey->setIsFixedBase(true);
return make_unique(
- numberOfGuardians, quorum, move(elGamalPublicKey), move(commitmentHash),
- move(manifestHash), move(cryptoBaseHash), move(cryptoExtendedBaseHash),
- move(extendedData));
+ numberOfGuardians, quorum, move(elGamalPublicKey), PARAMETER_BASE_HASH().clone(),
+ move(commitmentHash), move(manifestHash), move(cryptoBaseHash),
+ move(cryptoExtendedBaseHash), move(extendedData));
}
unique_ptr CiphertextElectionContext::make(
@@ -317,21 +361,13 @@ namespace electionguard
unique_ptr config,
std::unordered_map extendedData)
{
- // TODO: configurable version code
-
- // HP = H(HV ;00,p,q,g). Parameter Hash 3.1.2
- auto versionCode = string_to_fixed_width_bytes<32>("v2.0");
- auto parameterHash = hash_elems(
- {versionCode, HashPrefix::get_prefix_parameter_hash(), &const_cast(P()),
- &const_cast(Q()), &const_cast(G())});
-
// HM = H(HP;01,manifest). Manifest Hash 3.1.4
auto manifestDigest = hash_elems(
- {parameterHash.get(), HashPrefix::get_prefix_manifest_hash(), manifestHash.get()});
+ {PARAMETER_BASE_HASH(), HashPrefix::get_prefix_manifest_hash(), manifestHash.get()});
// TODO: complete according to spec
// HB =(HP;02,n,k,date,info,HM). Election Base Hash 3.1.5
- auto cryptoBaseHash = hash_elems({parameterHash.get(), HashPrefix::get_prefix_base_hash(),
+ auto cryptoBaseHash = hash_elems({PARAMETER_BASE_HASH(), HashPrefix::get_prefix_base_hash(),
numberOfGuardians, quorum, manifestDigest.get()});
// HE = H(HB;12,K,K1,0,K1,1,...,K1,kβ1,K2,0,...,Kn,kβ2,Kn,kβ1). // Extended Base Hash 3.2.3
@@ -343,9 +379,9 @@ namespace electionguard
elGamalPublicKey->setIsFixedBase(true);
return make_unique(
- numberOfGuardians, quorum, move(elGamalPublicKey), move(commitmentHash),
- move(manifestHash), move(cryptoBaseHash), move(cryptoExtendedBaseHash), move(config),
- move(extendedData));
+ numberOfGuardians, quorum, move(elGamalPublicKey), PARAMETER_BASE_HASH().clone(),
+ move(commitmentHash), move(manifestHash), move(cryptoBaseHash),
+ move(cryptoExtendedBaseHash), move(config), move(extendedData));
}
unique_ptr CiphertextElectionContext::make(
diff --git a/src/electionguard/facades/election.cpp b/src/electionguard/facades/election.cpp
index 296701ebc..86dcc47e5 100644
--- a/src/electionguard/facades/election.cpp
+++ b/src/electionguard/facades/election.cpp
@@ -22,6 +22,7 @@ using electionguard::dynamicCopy;
using electionguard::ElementModP;
using electionguard::ElementModQ;
using electionguard::Log;
+using electionguard::PARAMETER_BASE_HASH;
using std::make_unique;
using std::string;
@@ -29,10 +30,22 @@ using std::unique_ptr;
using std::unordered_map;
using std::vector;
+#pragma region Election Constants
+
+eg_electionguard_status_t eg_election_constant_parameter_hash(eg_element_mod_q_t **out_constant_ref)
+{
+ *out_constant_ref =
+ AS_TYPE(eg_element_mod_q_t, const_cast(&PARAMETER_BASE_HASH()));
+ return ELECTIONGUARD_STATUS_SUCCESS;
+}
+
+#pragma endregion
+
#pragma region CiphertextElectionContextConfiguration
-EG_API eg_electionguard_status_t eg_ciphertext_election_context_config_get_allowed_overvotes(
- eg_context_configuration_t *handle, bool *out_allowed_overvotes)
+eg_electionguard_status_t
+eg_ciphertext_election_context_config_get_allowed_overvotes(eg_context_configuration_t *handle,
+ bool *out_allowed_overvotes)
{
if (handle == nullptr) {
return ELECTIONGUARD_STATUS_ERROR_INVALID_ARGUMENT;
@@ -43,8 +56,9 @@ EG_API eg_electionguard_status_t eg_ciphertext_election_context_config_get_allow
return ELECTIONGUARD_STATUS_SUCCESS;
}
-EG_API eg_electionguard_status_t eg_ciphertext_election_context_config_get_max_ballots(
- eg_context_configuration_t *handle, uint64_t *out_max_ballots)
+eg_electionguard_status_t
+eg_ciphertext_election_context_config_get_max_ballots(eg_context_configuration_t *handle,
+ uint64_t *out_max_ballots)
{
if (handle == nullptr) {
return ELECTIONGUARD_STATUS_ERROR_INVALID_ARGUMENT;
@@ -98,14 +112,6 @@ eg_ciphertext_election_context_free(eg_ciphertext_election_context_t *handle)
return ELECTIONGUARD_STATUS_SUCCESS;
}
-eg_electionguard_status_t eg_ciphertext_election_context_get_elgamal_public_key(
- eg_ciphertext_election_context_t *handle, eg_element_mod_p_t **out_elgamal_public_key_ref)
-{
- const auto *pointer = AS_TYPE(CiphertextElectionContext, handle)->getElGamalPublicKey();
- *out_elgamal_public_key_ref = AS_TYPE(eg_element_mod_p_t, const_cast(pointer));
- return ELECTIONGUARD_STATUS_SUCCESS;
-}
-
eg_electionguard_status_t
eg_ciphertext_election_context_get_configuration(eg_ciphertext_election_context_t *handle,
eg_context_configuration_t **out_config)
@@ -131,6 +137,23 @@ eg_ciphertext_election_context_get_quorum(eg_ciphertext_election_context_t *hand
return ELECTIONGUARD_STATUS_SUCCESS;
}
+eg_electionguard_status_t eg_ciphertext_election_context_get_elgamal_public_key(
+ eg_ciphertext_election_context_t *handle, eg_element_mod_p_t **out_elgamal_public_key_ref)
+{
+ const auto *pointer = AS_TYPE(CiphertextElectionContext, handle)->getElGamalPublicKey();
+ *out_elgamal_public_key_ref = AS_TYPE(eg_element_mod_p_t, const_cast(pointer));
+ return ELECTIONGUARD_STATUS_SUCCESS;
+}
+
+eg_electionguard_status_t
+eg_ciphertext_election_context_get_parameter_hash(eg_ciphertext_election_context_t *handle,
+ eg_element_mod_q_t **out_parameter_hash_ref)
+{
+ const auto *pointer = AS_TYPE(CiphertextElectionContext, handle)->getParameterHash();
+ *out_parameter_hash_ref = AS_TYPE(eg_element_mod_q_t, const_cast(pointer));
+ return ELECTIONGUARD_STATUS_SUCCESS;
+}
+
eg_electionguard_status_t
eg_ciphertext_election_context_get_commitment_hash(eg_ciphertext_election_context_t *handle,
eg_element_mod_q_t **out_commitment_hash_ref)
diff --git a/src/electionguard/facades/group.cpp b/src/electionguard/facades/group.cpp
index ee01856eb..866e773da 100644
--- a/src/electionguard/facades/group.cpp
+++ b/src/electionguard/facades/group.cpp
@@ -684,9 +684,8 @@ EG_API eg_electionguard_status_t eg_element_mod_q_pow_mod_q(eg_element_mod_q_t *
}
// TODO: rename to eg_element_mod_q_pow_mod_q_with_long_exp
-EG_API eg_electionguard_status_t eg_element_long_pow_mod_q(eg_element_mod_q_t *base,
- uint64_t exponent,
- eg_element_mod_q_t **out_handle)
+eg_electionguard_status_t eg_element_long_pow_mod_q(eg_element_mod_q_t *base, uint64_t exponent,
+ eg_element_mod_q_t **out_handle)
{
try {
auto *b = AS_TYPE(ElementModQ, base);
@@ -701,9 +700,10 @@ EG_API eg_electionguard_status_t eg_element_long_pow_mod_q(eg_element_mod_q_t *b
}
}
-EG_API eg_electionguard_status_t
-eg_element_mod_q_a_plus_b_mul_c_mod_q(eg_element_mod_q_t *a, eg_element_mod_q_t *b,
- eg_element_mod_q_t *c, eg_element_mod_q_t **out_handle)
+eg_electionguard_status_t eg_element_mod_q_a_plus_b_mul_c_mod_q(eg_element_mod_q_t *a,
+ eg_element_mod_q_t *b,
+ eg_element_mod_q_t *c,
+ eg_element_mod_q_t **out_handle)
{
try {
auto *a_local = AS_TYPE(ElementModQ, a);
@@ -719,6 +719,25 @@ eg_element_mod_q_a_plus_b_mul_c_mod_q(eg_element_mod_q_t *a, eg_element_mod_q_t
}
}
+eg_electionguard_status_t eg_element_mod_q_a_minus_b_mul_c_mod_q(eg_element_mod_q_t *a,
+ eg_element_mod_q_t *b,
+ eg_element_mod_q_t *c,
+ eg_element_mod_q_t **out_handle)
+{
+ try {
+ auto *a_local = AS_TYPE(ElementModQ, a);
+ auto *b_local = AS_TYPE(ElementModQ, b);
+ auto *c_local = AS_TYPE(ElementModQ, c);
+ auto result = a_minus_bc_mod_q(*a_local, *b_local, *c_local);
+
+ *out_handle = AS_TYPE(eg_element_mod_q_t, result.release());
+ return ELECTIONGUARD_STATUS_SUCCESS;
+ } catch (const exception &e) {
+ Log::error(__func__, e);
+ return ELECTIONGUARD_STATUS_ERROR_BAD_ALLOC;
+ }
+}
+
eg_electionguard_status_t eg_element_mod_q_rand_q_new(eg_element_mod_q_t **out_handle)
{
try {
diff --git a/src/electionguard/serialize.hpp b/src/electionguard/serialize.hpp
index 2688c81e1..7c371760f 100644
--- a/src/electionguard/serialize.hpp
+++ b/src/electionguard/serialize.hpp
@@ -774,14 +774,16 @@ namespace electionguard
{"max_votes", serializable.getConfiguration()->getMaxNumberOfBallots()}};
json j = {
- {"crypto_base_hash", serializable.getCryptoBaseHash()->toHex()},
- {"crypto_extended_base_hash", serializable.getCryptoExtendedBaseHash()->toHex()},
- {"commitment_hash", serializable.getCommitmentHash()->toHex()},
- {"manifest_hash", serializable.getManifestHash()->toHex()},
- {"elgamal_public_key", serializable.getElGamalPublicKey()->toHex()},
{"number_of_guardians", serializable.getNumberOfGuardians()},
{"quorum", serializable.getQuorum()},
- {"configuration", config}};
+ {"configuration", config},
+ {"elgamal_public_key", serializable.getElGamalPublicKey()->toHex()},
+ {"parameter_hash", serializable.getParameterHash()->toHex()},
+ {"commitment_hash", serializable.getCommitmentHash()->toHex()},
+ {"manifest_hash", serializable.getManifestHash()->toHex()},
+ {"crypto_base_hash", serializable.getCryptoBaseHash()->toHex()},
+ {"crypto_extended_base_hash", serializable.getCryptoExtendedBaseHash()->toHex()}};
+
if (serializable.getExtendedData().size() > 0) {
j["extended_data"] = extendedData;
}
@@ -790,13 +792,18 @@ namespace electionguard
}
static unique_ptr toObject(json j)
{
- auto crypto_base_hash = j["crypto_base_hash"].get();
- auto crypto_extended_base_hash = j["crypto_extended_base_hash"].get();
- auto commitment_hash = j["commitment_hash"].get();
- auto manifest_hash = j["manifest_hash"].get();
- auto elgamal_public_key = j["elgamal_public_key"].get();
auto number_of_guardians = j["number_of_guardians"].get();
auto quorum = j["quorum"].get();
+ auto elgamal_public_key = j["elgamal_public_key"].get();
+ auto commitment_hash = j["commitment_hash"].get();
+ auto manifest_hash = j["manifest_hash"].get();
+ auto crypto_base_hash = j["crypto_base_hash"].get();
+ auto crypto_extended_base_hash = j["crypto_extended_base_hash"].get();
+
+ auto parameter_hash = j.contains("parameter_hash") && !j["parameter_hash"].is_null()
+ ? j["parameter_hash"].get()
+ : PARAMETER_BASE_HASH().toHex();
+
bool allows_overvotes = true;
uint64_t max_ballots = DEFAULT_MAX_BALLOTS;
if (j.contains("configuration") && !j["configuration"].is_null()) {
@@ -804,11 +811,12 @@ namespace electionguard
max_ballots = j["configuration"]["max_votes"].get();
}
+ auto elGamalPublicKey = ElementModP::fromHex(elgamal_public_key);
+ auto parameterHash = ElementModQ::fromHex(parameter_hash);
auto commitmentHash = ElementModQ::fromHex(commitment_hash);
auto manifestHash = ElementModQ::fromHex(manifest_hash);
auto cryptoBaseHash = ElementModQ::fromHex(crypto_base_hash);
auto cryptoExtendedBaseHash = ElementModQ::fromHex(crypto_extended_base_hash);
- auto elGamalPublicKey = ElementModP::fromHex(elgamal_public_key);
// ensure the elgamal public key instance is set as a fixed base
elGamalPublicKey->setIsFixedBase(true);
@@ -823,15 +831,15 @@ namespace electionguard
}
return make_unique(
- number_of_guardians, quorum, move(elGamalPublicKey), move(commitmentHash),
- move(manifestHash), move(cryptoBaseHash), move(cryptoExtendedBaseHash),
- move(config), move(extendedDataMap));
+ number_of_guardians, quorum, move(elGamalPublicKey), move(parameterHash),
+ move(commitmentHash), move(manifestHash), move(cryptoBaseHash),
+ move(cryptoExtendedBaseHash), move(config), move(extendedDataMap));
}
return make_unique(
- number_of_guardians, quorum, move(elGamalPublicKey), move(commitmentHash),
- move(manifestHash), move(cryptoBaseHash), move(cryptoExtendedBaseHash),
- move(config));
+ number_of_guardians, quorum, move(elGamalPublicKey), move(parameterHash),
+ move(commitmentHash), move(manifestHash), move(cryptoBaseHash),
+ move(cryptoExtendedBaseHash), move(config));
}
public:
diff --git a/src/electionguard/wasm/election.cpp b/src/electionguard/wasm/election.cpp
index 3b772b75f..414b419b7 100644
--- a/src/electionguard/wasm/election.cpp
+++ b/src/electionguard/wasm/election.cpp
@@ -24,8 +24,14 @@ EMSCRIPTEN_BINDINGS(electionguard)
.function("getElGamalPublicKey", &CiphertextElectionContext::getElGamalPublicKey,
allow_raw_pointers())
.function("getElGamalPublicKeyRef", &CiphertextElectionContext::getElGamalPublicKeyRef)
+ .function("getParameterHash", &CiphertextElectionContext::getParameterHash,
+ allow_raw_pointers())
+ .function("getCommitmentHash", &CiphertextElectionContext::getCommitmentHash,
+ allow_raw_pointers())
.function("getManifestHash", &CiphertextElectionContext::getManifestHash,
allow_raw_pointers())
+ .function("getCryptoBaseHash", &CiphertextElectionContext::getCryptoBaseHash,
+ allow_raw_pointers())
.function("getCryptoExtendedBaseHash", &CiphertextElectionContext::getCryptoExtendedBaseHash,
allow_raw_pointers())
.function("toJson", &CiphertextElectionContext::toJson)
diff --git a/test/electionguard/test_election.cpp b/test/electionguard/test_election.cpp
index 00a731f15..dd6df0720 100644
--- a/test/electionguard/test_election.cpp
+++ b/test/electionguard/test_election.cpp
@@ -1,6 +1,8 @@
+#include "../../src/electionguard/convert.hpp"
#include "../../src/electionguard/log.hpp"
#include "generators/election.hpp"
#include "generators/manifest.hpp"
+#include "utils/byte_logger.hpp"
#include
#include
@@ -12,6 +14,30 @@ using namespace electionguard;
using namespace electionguard::tools::generators;
using namespace std;
+TEST_CASE("Can Construct CiphertextElectionContext")
+{
+ // Arrange
+ auto keypair = ElGamalKeyPair::fromSecret(TWO_MOD_Q());
+ auto manifest = ManifestGenerator::getJeffersonCountyManifest_Minimal();
+ auto internal = make_unique(*manifest);
+ auto context = make_unique(
+ 1UL, 1UL, keypair->getPublicKey()->clone(), TWO_MOD_Q().clone(),
+ internal->getManifestHash()->clone(), TWO_MOD_Q().clone(), TWO_MOD_Q().clone());
+ auto json = context->toJson();
+ auto bson = context->toBson();
+
+ Log::debug(json);
+
+ // Act
+ auto fromJson = CiphertextElectionContext::fromJson(json);
+ auto fromBson = CiphertextElectionContext::fromBson(bson);
+
+ // Assert
+ CHECK(fromJson->getManifestHash()->toHex() == context->getManifestHash()->toHex());
+ CHECK(fromBson->getManifestHash()->toHex() == context->getManifestHash()->toHex());
+ CHECK(fromJson->getParameterHash()->toHex() == PARAMETER_BASE_HASH().toHex());
+}
+
TEST_CASE("Can serialize CiphertextElectionContext")
{
// Arrange
@@ -29,11 +55,69 @@ TEST_CASE("Can serialize CiphertextElectionContext")
auto fromBson = CiphertextElectionContext::fromBson(bson);
// Assert
- // validate against manifest->getManifestHash()
CHECK(fromJson->getManifestHash()->toHex() == context->getManifestHash()->toHex());
CHECK(fromBson->getManifestHash()->toHex() == context->getManifestHash()->toHex());
}
+TEST_CASE("Can serialize CiphertextElectionContext without ParameterHash")
+{
+ // Arrange
+ auto json =
+ "{\"commitment_hash\":\"02\",\"crypto_base_hash\":\"02\",\"crypto_extended_base_hash\":"
+ "\"02\",\"elgamal_public_key\":"
+ "\"A1DC7898431B0FD330161794512DF02CACE0A3848B0AE54DF215AE9A703F592A11ADFF4DC7E80157A8A33A"
+ "15BB4319A94D76FD23419AA1DF03A85A018381FB5455AE70D296C76E11CAB43AB0289CB8FCC4394D87E25BB7"
+ "0A88C47A385C1D6D870076A539D8F1CA411FE187195465278D925E0D46214F7A4A0CED063124503CEDD6B4D9"
+ "61ADD48789951E2B350F4A760F1D8568C74E201753BA4835B19B73221EC226FFB81BC4796C2E64F47D3AAE4A"
+ "4E31316E5596855A9C3868E00DCBF1E1F43FB10B94C36B1CFDA9FBA3D59424089B287C5BA8FEAE8AFA5191C0"
+ "7F864C386AFA5C4CB8E0D6C95DB7819C7AC4060759AF96E7151054AAE8D4B6DA4BA5E919638F4CE574D5DDE1"
+ "E3BF6AF1A796393E5851006E59F136A9BF73EA50BB40998E7A23D7C5A0236281095C044A4E8C65310CA6D644"
+ "489F013AD0ACADCF354A9FC78B14E5FDE250D8F1576ECF2F7861FAF32101B2BE28590E59E7C3E8B2F9DCB4D0"
+ "5F4483229CF5836879E45E76C56FB190BFB1877B9ED364BCDC8C3A2120356127540F8358ECC075FFC2FFD17E"
+ "4B9383AF479DC834E4F5C453645180E9E633F218F6EE4D603E9F87FD63C2E86E0791B2F59ECEA042845A8C7B"
+ "481E46DFF2E74D090DE9D61B895858FB9B0D167F9139620BD5FE6231879018560FC3BA7610CA32BB30D6B8C0"
+ "D58666856761E727D2C9E42ED44C9727C7C5EAECA88EBE219D0A57BAC6\",\"manifest_hash\":"
+ "\"E3045AFD00109A538BF9518B8F203042E8AA27810FEB68FD78A24DBDA168BF2E\",\"number_of_"
+ "guardians\":1,\"quorum\":1}";
+
+ Log::debug(json);
+
+ // Act
+ auto fromJson = CiphertextElectionContext::fromJson(json);
+
+ // Assert
+ CHECK(fromJson->getParameterHash()->toHex() == PARAMETER_BASE_HASH().toHex());
+}
+
+TEST_CASE("Version Code in CiphertextElectionContext Matches Constant")
+{
+ // Arrange
+ auto versionCode = string_to_fixed_width_bytes<32>("v2.0.0");
+
+ // Act
+ auto versionElement = bytes_to_q(versionCode, true);
+
+ // Assert
+ CHECK(versionElement->toHex() ==
+ "76322E302E300000000000000000000000000000000000000000000000000000");
+}
+
+TEST_CASE("Parameter Hash in CiphertextElectionContext Matches Constant")
+{
+ // Arrange
+
+ // Act
+ auto context = CiphertextElectionContext::make(3UL, 2UL, TWO_MOD_P().clone(),
+ TWO_MOD_Q().clone(), TWO_MOD_Q().clone());
+
+ // Assert
+ CHECK(context->getParameterHash()->toHex() == PARAMETER_BASE_HASH().toHex());
+
+ // TODO: update the constant check when hasing is updated to E.G. 2.0 spec.
+ // CHECK(context->getParameterHash()->toHex() ==
+ // "0x2B3B025E50E09C119CBA7E9448ACD1CABC9447EF39BF06327D81C665CDD86296");
+}
+
TEST_CASE("Assign ExtraData to CiphertextElectionContext")
{
// Arrange
diff --git a/test/electionguard/test_encrypt.cpp b/test/electionguard/test_encrypt.cpp
index 3cfbecc9c..6efd597fb 100644
--- a/test/electionguard/test_encrypt.cpp
+++ b/test/electionguard/test_encrypt.cpp
@@ -1441,7 +1441,7 @@ TEST_CASE("Create EncryptionMediator with same manifest hash")
auto manifest = ManifestGenerator::getJeffersonCountyManifest_Minimal();
auto internal = make_unique(*manifest);
auto context = make_unique(
- 1UL, 1UL, keypair->getPublicKey()->clone(), Q().clone(),
+ 1UL, 1UL, keypair->getPublicKey()->clone(), PARAMETER_BASE_HASH().clone(), Q().clone(),
internal.get()->getManifestHash()->clone(), Q().clone(), Q().clone());
auto device = make_unique(12345UL, 23456UL, 34567UL, "Location");
@@ -1455,9 +1455,9 @@ TEST_CASE("Create EncryptionMediator with different manifest hash")
auto keypair = ElGamalKeyPair::fromSecret(*secret);
auto manifest = ManifestGenerator::getJeffersonCountyManifest_Minimal();
auto internal = make_unique(*manifest);
- auto context =
- make_unique(1UL, 1UL, keypair->getPublicKey()->clone(),
- Q().clone(), Q().clone(), Q().clone(), Q().clone());
+ auto context = make_unique(
+ 1UL, 1UL, keypair->getPublicKey()->clone(), PARAMETER_BASE_HASH().clone(), Q().clone(),
+ Q().clone(), Q().clone(), Q().clone());
auto device = make_unique(12345UL, 23456UL, 34567UL, "Location");
try {