diff --git a/.flake8 b/.flake8 index d997b64..697d024 100644 --- a/.flake8 +++ b/.flake8 @@ -1,5 +1,6 @@ [flake8] -ignore = E203, E266, E501, W503, F403, F401 +exclude = .??*, venv +ignore = E203, E266, E501 max-line-length = 88 max-complexity = 18 -select = B,C,E,F,W,T4,B9 \ No newline at end of file +select = B,C,E,F,W,T4,B9 diff --git a/README.md b/README.md index 8ae4ba7..3fce6b3 100644 --- a/README.md +++ b/README.md @@ -71,17 +71,17 @@ Bluetooth devices or backend services. You'll need to install the project. For example as follows: -``` +```bash python3 -m venv venv source venv/bin/activate -pip3 install -e "." +pip3 install -e . ``` ## Running the example After installing the project you can run the examples: -``` +```bash examples/run_lowcost.py examples/run_unlinkable.py ``` @@ -90,7 +90,7 @@ examples/run_unlinkable.py After installing the project you can obtain test vectors by running: -``` +```bash utils/testvectors_lowcost.py utils/testvectors_unlinkable.py ``` @@ -105,7 +105,7 @@ venv/bin/ativate`) to ensure that the paths are picked up correctly. Please install the proper pre-commit-hooks so that the files stay formatted: -``` +```bash pip install pre-commit pre-commit install ``` diff --git a/dp3t/protocols/lowcost.py b/dp3t/protocols/lowcost.py index 7107e39..fcaa25c 100644 --- a/dp3t/protocols/lowcost.py +++ b/dp3t/protocols/lowcost.py @@ -77,6 +77,18 @@ def batch_start_from_time(time): return (int(time.timestamp()) // SECONDS_PER_BATCH) * SECONDS_PER_BATCH +def secure_shuffle(items): + """Perform a cryptographically secure shuffling of the given items + + Args: + items (obj:list): A list of items to be shuffled + + Returns: + Nothing. Items are shuffled in place + """ + random.shuffle(items, secrets.SystemRandom().random) + + ######################################### ### BASIC CRYPTOGRAPHIC FUNCTIONALITY ### ######################################### @@ -130,8 +142,7 @@ def generate_ephids_for_day(current_day_key, shuffle=True): # Shuffle the resulting ephids if shuffle: - # WARNING: replace with a secure shuffle - random.shuffle(ephids) + secure_shuffle(ephids) return ephids @@ -310,7 +321,7 @@ def add_observation(self, ephid, time): self.observations[batch_start].append(ephid) # Shuffle observations to hide receive order - random.shuffle(self.observations[batch_start]) + secure_shuffle(self.observations[batch_start]) def get_tracing_information( self, @@ -451,4 +462,4 @@ def housekeeping_after_batch(self, batch): self.observations[day_time].extend(observations) # Reshuffle to make sure we do not store ordering data - random.shuffle(self.observations[day_time]) + secure_shuffle(self.observations[day_time]) diff --git a/examples/run_lowcost.py b/examples/run_lowcost.py index 3cc24ef..55c8575 100755 --- a/examples/run_lowcost.py +++ b/examples/run_lowcost.py @@ -125,6 +125,7 @@ def main(): print(" * CORRECT: Alice's phone concludes she is at risk") else: print(" * ERROR: Alice's phone does not conclude she is at risk") + raise RuntimeError("Example code failed!") print("\n[Alice] Runs housekeeping to update her observation store") alice.housekeeping_after_batch(batch) diff --git a/examples/run_unlinkable.py b/examples/run_unlinkable.py index 48823dc..b37b2a3 100755 --- a/examples/run_unlinkable.py +++ b/examples/run_unlinkable.py @@ -127,6 +127,7 @@ def main(): print(" * CORRECT: Alice's phone concludes she is at risk") else: print(" * ERROR: Alice's phone does not conclude she is at risk") + raise RuntimeError("Example code failed!") if __name__ == "__main__": diff --git a/setup.py b/setup.py index f336c3b..b21ec59 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,7 @@ "License :: OSI Approved :: Apache Software License" "Operating System :: OS Independent", ], - install_requires=["pycryptodomex", "scalable-cuckoo-filter"], - test_requires=["pytest"], python_requires=">=3.6", + install_requires=["pycryptodomex", "scalable-cuckoo-filter"], + extras_require={"dev": ["black", "flake8", "pre-commit"], "test": ["pytest"]}, ) diff --git a/tests/test_examples.py b/tests/test_examples.py new file mode 100644 index 0000000..f882b82 --- /dev/null +++ b/tests/test_examples.py @@ -0,0 +1,27 @@ +__copyright__ = """ + Copyright 2020 EPFL + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +__license__ = "Apache 2.0" + +from pathlib import Path +import pytest +import subprocess + +example_scripts = (Path(__file__).parent.parent / "examples").glob("*.py") + + +@pytest.mark.parametrize("script", example_scripts) +def test_example_script(script): + subprocess.run([script], check=True) diff --git a/utils/testvectors_lowcost.py b/utils/testvectors_lowcost.py index 6151c39..e071d98 100755 --- a/utils/testvectors_lowcost.py +++ b/utils/testvectors_lowcost.py @@ -19,8 +19,6 @@ """ __license__ = "Apache 2.0" -from datetime import datetime, timezone - from dp3t.protocols.lowcost import next_day_key, generate_ephids_for_day KEY0 = bytes.fromhex("0000000000000000000000000000000000000000000000000000000000000000") diff --git a/utils/testvectors_unlinkable.py b/utils/testvectors_unlinkable.py index 3c133ca..16a14e0 100755 --- a/utils/testvectors_unlinkable.py +++ b/utils/testvectors_unlinkable.py @@ -19,7 +19,7 @@ """ __license__ = "Apache 2.0" -from binascii import hexlify, unhexlify +from binascii import unhexlify from datetime import datetime, timezone from dp3t.protocols.unlinkable import (