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 {