Skip to content

Commit 602a3e9

Browse files
committed
Use the strongest available random source when shuffling
We need to provide cryptographically secure random shuffling. Looking at Python's implementation of random.shuffle()[1], we see that (a) it is a straightforward implementation of Durstenfeld's shuffle algorithm[2] (which is as unbiased as its random number source), and (b) it allows for a custom `random` function to be passed in. Hence, AFAICS, we should get a cryptographically secure shuffle as long as we pass in a `random` function that itself is cryptographically secure. Quoting the documentation of the secrets module: The secrets module provides access to the most secure source of randomness that your operating system provides. class secrets.SystemRandom A class for generating random numbers using the highest-quality sources provided by the operating system. Therefore, the best we can do is AFAICS to simply use the `random` function available from secrets.SystemRandom class. [1]: https://github.com/python/cpython/blob/v3.6.10rc1/Lib/random.py#L263 [2]: https://en.wikipedia.org/wiki/Fisher–Yates_shuffle#The_modern_algorithm [3]: https://docs.python.org/3.6/library/secrets.html#random-numbers
1 parent 1d167bd commit 602a3e9

1 file changed

Lines changed: 15 additions & 4 deletions

File tree

dp3t/protocols/lowcost.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,18 @@ def batch_start_from_time(time):
7777
return (int(time.timestamp()) // SECONDS_PER_BATCH) * SECONDS_PER_BATCH
7878

7979

80+
def secure_shuffle(items):
81+
"""Perform a cryptographically secure shuffling of the given items
82+
83+
Args:
84+
items (obj:list): A list of items to be shuffled
85+
86+
Returns:
87+
Nothing. Items are shuffled in place
88+
"""
89+
random.shuffle(items, secrets.SystemRandom().random)
90+
91+
8092
#########################################
8193
### BASIC CRYPTOGRAPHIC FUNCTIONALITY ###
8294
#########################################
@@ -130,8 +142,7 @@ def generate_ephids_for_day(current_day_key, shuffle=True):
130142

131143
# Shuffle the resulting ephids
132144
if shuffle:
133-
# WARNING: replace with a secure shuffle
134-
random.shuffle(ephids)
145+
secure_shuffle(ephids)
135146

136147
return ephids
137148

@@ -310,7 +321,7 @@ def add_observation(self, ephid, time):
310321
self.observations[batch_start].append(ephid)
311322

312323
# Shuffle observations to hide receive order
313-
random.shuffle(self.observations[batch_start])
324+
secure_shuffle(self.observations[batch_start])
314325

315326
def get_tracing_information(
316327
self,
@@ -451,4 +462,4 @@ def housekeeping_after_batch(self, batch):
451462
self.observations[day_time].extend(observations)
452463

453464
# Reshuffle to make sure we do not store ordering data
454-
random.shuffle(self.observations[day_time])
465+
secure_shuffle(self.observations[day_time])

0 commit comments

Comments
 (0)