Skip to content

Commit 779ad74

Browse files
Use consistent tracking code format (#213)
* Use consistent tracking code format * Fix LGTM errors * fix linter error
1 parent 86bdede commit 779ad74

File tree

4 files changed

+50
-33
lines changed

4 files changed

+50
-33
lines changed

src/electionguard/group.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# made about timing or other side-channels.
44

55
from typing import Any, Final, NamedTuple, Optional, Union
6+
from base64 import b16decode
67
from secrets import randbelow
78
from gmpy2 import mpz, powmod, invert, to_binary, from_binary
89

@@ -23,12 +24,19 @@ class ElementModQ(NamedTuple):
2324

2425
elem: mpz
2526

27+
def to_bytes(self) -> bytes:
28+
"""
29+
Converts from the element to the representation of bytes by first going through hex.
30+
This is preferable to directly accessing `elem`, whose representation might change.
31+
"""
32+
return b16decode(self.to_hex())
33+
2634
def to_hex(self) -> str:
2735
"""
2836
Converts from the element to the hex representation of bytes. This is preferable to directly
2937
accessing `elem`, whose representation might change.
3038
"""
31-
h = format(self.elem, "02x")
39+
h = format(self.elem, "02X")
3240
if len(h) % 2:
3341
h = "0" + h
3442
return h

src/electionguard/tracker.py

Lines changed: 24 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
from typing import List, Optional
2-
32
from .hash import hash_elems
4-
from .group import ElementModQ, q_to_bytes, bytes_to_q
5-
from .words import get_word, get_index_from_word
3+
from .group import ElementModQ
4+
from .words import get_word
65

76
DEFAULT_SEPERATOR = "-"
87

@@ -39,32 +38,29 @@ def tracker_hash_to_words(
3938
:return: Human readable tracker string or None
4039
"""
4140

42-
segments = q_to_bytes(tracker_hash)
41+
segments = tracker_hash.to_bytes()
4342
words: List[str] = []
44-
for value in segments:
45-
word = get_word(value)
46-
if word is None:
47-
return None
48-
words.append(word)
49-
# FIXME ISSUE #82 Minimize length of tracker
50-
return seperator.join(words)
43+
for i in range(0, len(segments), 4):
44+
# Select 4 bytes for the segment
45+
first = segments[i]
46+
second = segments[i + 1]
47+
third = segments[i + 2]
48+
fourth = segments[i + 3]
5149

50+
# word is byte(1) + 1/2 of byte(2)
51+
word_part = get_word((first * 16 + (second >> 4)))
5252

53-
def tracker_words_to_hash(
54-
tracker_words: str, seperator: str = DEFAULT_SEPERATOR
55-
) -> Optional[ElementModQ]:
56-
"""
57-
Convert tracker from human readable / friendly words to hash
58-
:param tracker_words: Tracker words
59-
:param seperator: Seperator used between words
60-
:return: Tracker hash or None
61-
"""
62-
words = tracker_words.split(seperator)
63-
int_values: List[int] = []
64-
for word in words:
65-
index = get_index_from_word(word)
66-
if index is None:
53+
# hex is other 1/2 of byte(2) + byte(3) + byte(4)
54+
hex_part = [
55+
format((second & 0x0F) >> 0, "1X"),
56+
format((third & 0xF0) >> 4, "1X"),
57+
format((third & 0x0F) >> 0, "1X"),
58+
format((fourth & 0xF0) >> 4, "1X"),
59+
format((fourth & 0x0F) >> 0, "1X"),
60+
]
61+
if word_part is None:
6762
return None
68-
int_values.append(index)
69-
value = bytes(int_values)
70-
return bytes_to_q(value)
63+
words.append(word_part)
64+
words.append("".join(hex_part))
65+
# FIXME ISSUE #82 Minimize length of tracker
66+
return seperator.join(words)

src/electionguard/words.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def get_word(index: int) -> Optional[str]:
1414
return None
1515
if index > MAX_INDEX:
1616
return None
17-
return words[index]
17+
return words[index & 0xFFF]
1818

1919

2020
def get_index_from_word(word: str) -> Optional[int]:

tests/test_tracker.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
from electionguard.tracker import (
88
tracker_hash_to_words,
9-
tracker_words_to_hash,
109
get_rotating_tracker_hash,
1110
get_hash_for_device,
1211
)
@@ -56,11 +55,25 @@ def test_tracker_converts_to_words(self):
5655
device_words = tracker_hash_to_words(device_hash)
5756
tracker_words = tracker_hash_to_words(tracker_hash)
5857
tracker_different_words = tracker_hash_to_words(tracker_hash_different)
59-
tracker_hash_from_words = tracker_words_to_hash(tracker_words)
6058

6159
# Assert
6260
self.assertIsNotNone(device_words)
6361
self.assertIsNotNone(tracker_words)
6462
self.assertNotEqual(device_words, tracker_words)
6563
self.assertNotEqual(tracker_different_words, tracker_words)
66-
self.assertEqual(tracker_hash, tracker_hash_from_words)
64+
65+
def test_tracker_converts_to_known_words(self):
66+
expected_hash = (
67+
"325AB2622D35311DB0320C9F3B421EE93017D16B9E4C7FEF06704EDA4FA5E30B"
68+
)
69+
expected_words = "change-AB262-cart-5311D-ladder-20C9F-cloudburst-21EE9-cellar-7D16B-illegal-C7FEF-alias-04EDA-curriculum-5E30B"
70+
71+
device_hash = ONE_MOD_Q
72+
ballot_hash = TWO_MOD_Q
73+
timestamp = 1000
74+
75+
tracker_hash = get_rotating_tracker_hash(device_hash, timestamp, ballot_hash)
76+
tracker_words = tracker_hash_to_words(tracker_hash)
77+
78+
self.assertEqual(tracker_hash.to_hex(), expected_hash)
79+
self.assertEqual(tracker_words, expected_words)

0 commit comments

Comments
 (0)