Skip to content

🐞 CiphertextTallySelection uses a shared default ElGamalCiphertext instance #809

@mdevolde

Description

@mdevolde

Is there an existing issue for this?

  • I have searched the existing issues

Current Behavior

CiphertextTallySelection defines its ciphertext field with a default instance:

ciphertext: ElGamalCiphertext = field(
    default=ElGamalCiphertext(ONE_MOD_P, ONE_MOD_P)
)

This means the default ElGamalCiphertext object is created once at class definition time and then shared across all CiphertextTallySelection instances that do not receive an explicit ciphertext value.

As a result, multiple tally selections can unintentionally reference the same default ciphertext object. Any in-place mutation of that object can leak across instances.

This pattern also causes Python 3.11+ dataclass validation to fail because mutable defaults must be provided through default_factory.

Expected Behavior

Each CiphertextTallySelection should receive its own ElGamalCiphertext instance initialized at instance initialization, and not at class definition (thanks to default_factory instead of default).

The default value should represent the same initial ciphertext content for every instance, but it should not be the same shared object in memory.

Using a per-instance default avoids unintended shared state and keeps the field compatible with Python 3.11+ dataclass validation rules.

Steps To Reproduce

  1. Create two CiphertextTallySelection instances without passing an explicit ciphertext:
from electionguard.tally import CiphertextTallySelection
from electionguard.group import ONE_MOD_Q, TWO_MOD_P

first = CiphertextTallySelection("selection-1", 1, ONE_MOD_Q)
second = CiphertextTallySelection("selection-2", 2, ONE_MOD_Q)
  1. Observe that both instances share the same default object:
print(first.ciphertext is second.ciphertext)
  1. Mutate the ciphertext of one instance in place:
first.ciphertext.pad = TWO_MOD_P
  1. Observe that the mutation is visible from the second instance as well:
print(second.ciphertext.pad == TWO_MOD_P)
  1. On Python 3.11+, importing electionguard will raise an error, making the lib unusable:
    ValueError: mutable default <class 'electionguard.elgamal.ElGamalCiphertext'> for field ciphertext is not allowed: use default_factory

Environment

- OS: Windows 11

Anything else?

https://docs.python.org/3/library/dataclasses.html#mutable-default-values

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingtriageWaiting to be triaged

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions