Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,24 @@ environment:
@echo 🔧 ENVIRONMENT SETUP
make install-gmp
python3 -m pip install -U pip
pip3 install 'poetry==1.8.3'
pip3 install 'poetry==2.2.1'
poetry config virtualenvs.in-project true
poetry config installer.modern-installation false
printf "Cython<3\n" > /tmp/pip-constraints.txt
PIP_CONSTRAINT=/tmp/pip-constraints.txt poetry install
poetry run pip install 'setuptools<81'
poetry run pip install 'setuptools<83'
@echo 🚨 Be sure to add poetry to PATH
make fetch-sample-data

install:
@echo 🔧 INSTALL
poetry install
poetry run pip install 'setuptools<81'
poetry run pip install 'setuptools<83'

build:
@echo 🔨 BUILD
poetry build
poetry install
poetry run pip install 'setuptools<81'
poetry run pip install 'setuptools<83'

openssl-fix:
export LDFLAGS=-L/usr/local/opt/openssl/lib
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ ElectionGuard supports a variety of use cases. The Primary use case is to genera
- [GNU Make](https://www.gnu.org/software/make/manual/make.html) is used to simplify the commands and GitHub Actions. This approach is recommended to simplify the command line experience. This is built in for MacOS and Linux. For Windows, setup is simpler with [Chocolatey](https://chocolatey.org/install) and installing the provided [make package](https://chocolatey.org/packages/make). The other Windows option is [manually installing make](http://gnuwin32.sourceforge.net/packages/make.htm).
- [Gmpy2](https://gmpy2.readthedocs.io/en/latest/) is used for [Arbitrary-precision arithmetic](https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic) which
has its own [installation requirements (native C libraries)](https://gmpy2.readthedocs.io/en/latest/intro.html#installation) on Linux and MacOS. **⚠️ Note:** _This is not required for Windows since the gmpy2 precompiled libraries are provided._
- [poetry 1.1.13](https://python-poetry.org/) is used to configure the python environment. Installation instructions can be found [here](https://python-poetry.org/docs/#installation).
- [poetry 2.2.1](https://python-poetry.org/) is used to configure the python environment. Installation instructions can be found [here](https://python-poetry.org/docs/#installation).

## 🚀 Quick Start

Expand Down
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ ElectionGuard supports a variety of use cases. The Primary use case is to genera
- [GNU Make](https://www.gnu.org/software/make/manual/make.html) is used to simplify the commands and GitHub Actions. This approach is recommended to simplify the command line experience. This is built in for MacOS and Linux. For Windows, setup is simpler with [Chocolatey](https://chocolatey.org/install) and installing the provided [make package](https://chocolatey.org/packages/make). The other Windows option is [manually installing make](http://gnuwin32.sourceforge.net/packages/make.htm).
- [Gmpy2](https://gmpy2.readthedocs.io/en/latest/) is used for [Arbitrary-precision arithmetic](https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic) which
has its own [installation requirements (native C libraries)](https://gmpy2.readthedocs.io/en/latest/intro.html#installation) on Linux and MacOS. **⚠️ Note:** _This is not required for Windows since the gmpy2 precompiled libraries are provided._
- [poetry 1.1.10](https://python-poetry.org/) is used to configure the python environment. Installation instructions can be found [here](https://python-poetry.org/docs/#installation).
- [poetry 2.2.1](https://python-poetry.org/) is used to configure the python environment. Installation instructions can be found [here](https://python-poetry.org/docs/#installation).

## 🚀 Quick Start

Expand Down
5,329 changes: 3,596 additions & 1,733 deletions poetry.lock

Large diffs are not rendered by default.

73 changes: 38 additions & 35 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,76 +1,77 @@
[tool.poetry]
[project]
name = "electionguard"
version = "1.4.0"
requires-python = ">=3.9.5,<4.0.0"
description = "ElectionGuard: Support for e2e verified elections."
license = "MIT"
authors = ["Microsoft <electionguard@microsoft.com>"]
authors = [{"name" = "Microsoft", "email" = "electionguard@microsoft.com"}]
maintainers = []
readme = "README.md"
homepage = "https://microsoft.github.io/electionguard-python"
repository = "https://github.com/microsoft/electionguard-python"
documentation = "https://microsoft.github.io/electionguard-python"
keywords = []
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: Unix",
"Operating System :: POSIX",
"Operating System :: MacOS",
"Operating System :: Microsoft :: Windows",
"Programming Language :: Python",
"Topic :: Utilities"]
packages = [
{ include = "electionguard", from = "src" },
{ include = "electionguard_tools", from = "src" },
{ include = "electionguard_cli", from = "src" },
{ include = "electionguard_gui", from = "src" },
"Topic :: Utilities"
]
dependencies = [
"gmpy2>=2.0.8,<3.0.0",
"psutil>=5.7.2",
"pydantic==2.13.0",
"click>=8.1.0,<9.0.0",
"dacite>=1.6.0,<2.0.0",
"python-dateutil>=2.8.2,<3.0.0",
"types-python-dateutil>=2.8.14,<3.0.0",
"Eel[jinja2]>=0.14.0,<1.0.0",
"pymongo>=4.1.1,<5.0.0",
"dependency-injector>=4.39.1,<5.0.0",
"pytest-mock>=3.8.2,<4.0.0",
]


[tool.poetry.urls]
[project.urls]
homepage = "https://microsoft.github.io/electionguard-python"
repository = "https://github.com/microsoft/electionguard-python"
documentation = "https://microsoft.github.io/electionguard-python"
"GitHub Pages" = "https://microsoft.github.io/electionguard-python"
"Read the Docs" = "https://electionguard-python.readthedocs.io"
"Releases" = "https://github.com/microsoft/electionguard-python/releases"
"Milestones" = "https://github.com/microsoft/electionguard-python/milestones"
"Issue Tracker" = "https://github.com/microsoft/electionguard-python/issues"

[tool.poetry.dependencies]
python = "^3.9.5"
gmpy2 = "^2.0.8"
psutil = ">=5.7.2"
pydantic = "1.9.0"
click = "^8.1.0"
dacite = "^1.6.0"
python-dateutil = "^2.8.2"
types-python-dateutil = "^2.8.14"
Eel = {extras = ["jinja2"], version = "^0.14.0"}
pymongo = "^4.1.1"
dependency-injector = "^4.39.1"
pytest-mock = "^3.8.2"
[tool.poetry]
packages = [
{ include = "electionguard", from = "src" },
{ include = "electionguard_tools", from = "src" },
{ include = "electionguard_cli", from = "src" },
{ include = "electionguard_gui", from = "src" },
]

[tool.poetry.dev-dependencies]
[tool.poetry.group.dev.dependencies]
atomicwrites = "*"
black = "22.3.0"
black = "25.11.0"
coverage = "*"
docutils = "*"
hypothesis = ">=5.15.1"
ipython = "^7.31.1"
ipykernel = "^6.4.1"
jeepney = "*"
jupyter-black = "^0.3.1"
mkdocs = "^1.3.0"
mkdocs-jupyter = "^0.20.1"
mkdocs = "^1.6.1"
mkdocs-jupyter = "^0.26.2"
mkinit = "^0.3.3"
mypy = "^0.910"
mypy = "1.19.1"
pydeps = "*"
pylint = "*"
pytest = "*"
secretstorage = "*"
twine = "*"
typish = '*'

[tool.poetry.scripts]
[project.scripts]
eg = 'electionguard_cli.start:cli'
egui = 'electionguard_gui.start:run'

Expand All @@ -93,13 +94,15 @@ disable = '''
missing-function-docstring,
no-value-for-parameter,
redefined-builtin,
broad-exception-raised,
too-few-public-methods,
too-many-arguments,
too-many-branches,
too-many-function-args,
too-many-lines,
too-many-locals,
too-many-nested-blocks,
too-many-positional-arguments,
unnecessary-lambda,
'''

Expand All @@ -111,11 +114,11 @@ source = ["src/electionguard"]
directory = "coverage_html_report"

[build-system]
requires = ["poetry-core>=1.0.0"]
requires = ["poetry-core>=2.0.0"]
build-backend = "poetry.core.masonry.api"

[tool.mypy]
python_version = 3.9
python_version = "3.9"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
Expand Down
4 changes: 2 additions & 2 deletions src/electionguard/ballot.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from dataclasses import dataclass, field, replace
from datetime import datetime
from datetime import datetime, timezone
from enum import Enum
from functools import cached_property, reduce
from typing import (
Expand Down Expand Up @@ -1018,7 +1018,7 @@ def make_ciphertext_submitted_ballot(
contest_hashes = [contest.crypto_hash for contest in sequence_order_sort(contests)]
contest_hash = hash_elems(object_id, manifest_hash, *contest_hashes)

timestamp = to_ticks(datetime.utcnow()) if timestamp is None else timestamp
timestamp = to_ticks(datetime.now(timezone.utc)) if timestamp is None else timestamp
if code_seed is None:
code_seed = manifest_hash
if ballot_code is None:
Expand Down
3 changes: 2 additions & 1 deletion src/electionguard/byte_padding.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from enum import IntEnum
from typing import Literal


_PAD_BYTE = b"\x00"
_BYTE_ORDER = "big"
_BYTE_ORDER: Literal["little", "big"] = "big"
_PAD_INDICATOR_SIZE = 2


Expand Down
19 changes: 15 additions & 4 deletions src/electionguard/constants.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Creating and managing mathematic constants for the election."""

from os import getenv

from dataclasses import dataclass
Expand Down Expand Up @@ -77,7 +78,17 @@ def get_constants() -> ElectionConstants:
return option_map.get(option) or STANDARD_CONSTANTS


get_large_prime = lambda: int(get_constants().large_prime.value)
get_small_prime = lambda: int(get_constants().small_prime.value)
get_cofactor = lambda: int(get_constants().cofactor.value)
get_generator = lambda: int(get_constants().generator.value)
def get_large_prime() -> int:
return int(get_constants().large_prime.value)


def get_small_prime() -> int:
return int(get_constants().small_prime.value)


def get_cofactor() -> int:
return int(get_constants().cofactor.value)


def get_generator() -> int:
return int(get_constants().generator.value)
34 changes: 17 additions & 17 deletions src/electionguard/decryption.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,21 +289,21 @@ def compute_compensated_decryption_share_for_contest(

selections: Dict[SelectionId, CiphertextCompensatedDecryptionSelection] = {}

selection_decryptions: List[
Optional[CiphertextCompensatedDecryptionSelection]
] = scheduler.schedule(
compute_compensated_decryption_share_for_selection,
[
(
missing_guardian_coordinate,
present_guardian_key,
missing_guardian_key,
selection,
context,
)
for selection in contest.selections
],
with_shared_resources=True,
selection_decryptions: List[Optional[CiphertextCompensatedDecryptionSelection]] = (
scheduler.schedule(
compute_compensated_decryption_share_for_selection,
[
(
missing_guardian_coordinate,
present_guardian_key,
missing_guardian_key,
selection,
context,
)
for selection in contest.selections
],
with_shared_resources=True,
)
)

for decryption in selection_decryptions:
Expand Down Expand Up @@ -425,7 +425,7 @@ def partially_decrypt(
key_pair: ElectionKeyPair,
elgamal: ElGamalCiphertext,
extended_base_hash: ElementModQ,
nonce_seed: ElementModQ = None,
nonce_seed: Optional[ElementModQ] = None,
) -> Tuple[ElementModP, ChaumPedersenProof]:
"""
Compute a partial decryption of an elgamal encryption
Expand Down Expand Up @@ -488,7 +488,7 @@ def decrypt_with_threshold(
coordinate: ElementModQ,
ciphertext: ElGamalCiphertext,
extended_base_hash: ElementModQ,
nonce_seed: ElementModQ = None,
nonce_seed: Optional[ElementModQ] = None,
) -> Optional[Tuple[ElementModP, ChaumPedersenProof]]:
"""
Compute a compensated partial decryption of an elgamal encryption
Expand Down
2 changes: 1 addition & 1 deletion src/electionguard/decryption_mediator.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def announce(
self,
guardian_key: ElectionPublicKey,
tally_share: DecryptionShare,
ballot_shares: Dict[BallotId, Optional[DecryptionShare]] = None,
ballot_shares: Optional[Dict[BallotId, Optional[DecryptionShare]]] = None,
) -> None:
"""
Announce that a Guardian is present and participating in the decryption.
Expand Down
10 changes: 4 additions & 6 deletions src/electionguard/discrete_log.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# support for computing discrete logs, with a cache so they're never recomputed

import asyncio
from typing import Dict, Tuple
from typing import Dict, Tuple, Optional

from .constants import get_generator
from .singleton import Singleton
Expand Down Expand Up @@ -91,7 +91,7 @@ async def compute_discrete_log_async(


def precompute_discrete_log_cache(
max_exponent: int, cache: DiscreteLogCache = None
max_exponent: int, cache: Optional[DiscreteLogCache] = None
) -> DiscreteLogCache:
"""
Precompute the discrete log by the max exponent.
Expand Down Expand Up @@ -169,14 +169,12 @@ def set_lazy_evaluation(self, lazy_evaluation: bool) -> None:
self._lazy_evaluation = lazy_evaluation

def precompute_cache(self, exponent: int) -> None:
if exponent > self._max_exponent:
exponent = self._max_exponent
exponent = min(exponent, self._max_exponent)

precompute_discrete_log_cache(exponent, self._cache)

async def precompute_cache_async(self, exponent: int) -> None:
if exponent > self._max_exponent:
exponent = self._max_exponent
exponent = min(exponent, self._max_exponent)

async with self._mutex:
precompute_discrete_log_cache(exponent)
Expand Down
8 changes: 4 additions & 4 deletions src/electionguard/election_polynomial.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from dataclasses import dataclass
from typing import Dict, List
from typing import Dict, List, Optional

from .elgamal import ElGamalKeyPair
from .group import (
Expand Down Expand Up @@ -61,7 +61,7 @@ def get_proofs(self) -> List[SchnorrProof]:


def generate_polynomial(
number_of_coefficients: int, nonce: ElementModQ = None
number_of_coefficients: int, nonce: Optional[ElementModQ] = None
) -> ElectionPolynomial:
"""
Generates a polynomial for sharing election keys
Expand Down Expand Up @@ -99,7 +99,7 @@ def compute_polynomial_coordinate(
exponent_modifier_mod_q = ElementModQ(exponent_modifier)

computed_value = ZERO_MOD_Q
for (i, coefficient) in enumerate(polynomial.coefficients):
for i, coefficient in enumerate(polynomial.coefficients):
exponent = pow_q(exponent_modifier_mod_q, i)
factor = mult_q(coefficient.value, exponent)
computed_value = add_q(computed_value, factor)
Expand Down Expand Up @@ -148,7 +148,7 @@ def verify_polynomial_coordinate(
exponent_modifier_mod_q = ElementModQ(exponent_modifier)

commitment_output = ONE_MOD_P
for (i, commitment) in enumerate(commitments):
for i, commitment in enumerate(commitments):
exponent = pow_p(exponent_modifier_mod_q, i)
factor = pow_p(commitment, exponent)
commitment_output = mult_p(commitment_output, factor)
Expand Down
5 changes: 2 additions & 3 deletions src/electionguard/encrypt.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from datetime import datetime
from datetime import datetime, timezone
from dataclasses import dataclass, field
from typing import Dict, List, Optional, Type, TypeVar
from uuid import getnode
Expand Down Expand Up @@ -86,12 +86,11 @@ def get_hash(self) -> ElementModQ:
self.device_id, self.session_id, self.launch_code, self.location
)

# pylint: disable=no-self-use
def get_timestamp(self) -> int:
"""
Get the current timestamp in utc
"""
return int(datetime.utcnow().timestamp())
return int(datetime.now(timezone.utc).timestamp())


class EncryptionMediator:
Expand Down
Loading
Loading