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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions