added support for RNGs from oqs/rand.h added support for RNGs from oqs/rand.h added support for RNGs from oqs/rand.h
diff --git a/.gitignore b/.gitignore index 3e067e9..6894774 100644 --- a/.gitignore +++ b/.gitignore
@@ -2,4 +2,119 @@ __pycache__ liboqs.so *.pyc -test-results \ No newline at end of file +test-results + +.swp +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# dotenv +.env + +# virtualenv +.venv +venv/ +ENV/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + +# PyCharm & virtualenv +.idea +bin/ +include/ +lib/ +spell/ +pip-selfcheck.json +pyvenv.cfg + +# vim +*.swp \ No newline at end of file
diff --git a/CHANGES.txt b/CHANGES.txt new file mode 100644 index 0000000..d77a849 --- /dev/null +++ b/CHANGES.txt
@@ -0,0 +1,11 @@ +Version 0.2.1 - January xx, 2020 + - Added a signature example + - Added partial support for RNGs from <oqs/rand.h> + - Added an RNG example + +Version 0.2.0 - October 8, 2019 + - This release updates for compatibility with liboqs 0.2.0, which contains + new/updated algorithms based on NIST Round 2 submissions. + +Version 0.1.0 - April 23, 2019 + - Initial release
diff --git a/LICENSE.txt b/LICENSE.txt index 21b15ef..07bc6e8 100644 --- a/LICENSE.txt +++ b/LICENSE.txt
@@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018 Open Quantum Safe +Copyright (c) 2018-2020 Open Quantum Safe Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md index b1acdac..4606a95 100644 --- a/README.md +++ b/README.md
@@ -33,25 +33,29 @@ The project contains the following files: - - **`oqs/wrapper.py`: a Python 3 module wrapper for the liboqs C library.** - - `examples/example.py`: illustrates the usage of the liboqs-python wrapper. - - `tests/test_*.py`: unit tests for the liboqs-python wrapper. + - **`oqs/oqs.py`: a Python 3 module wrapper for the liboqs C library.** + - `oqs/rand.py`: a Python 3 module supporting RNGs from `<oqs/rand.h>` + - `examples/kem.py`: key encapsulation example + - `examples/rand.py`: RNG example + - `examples/sig.py`: signature example + - `tests`: unit tests Usage ----- -liboqs-python defines two main classes: `KeyEncapsulation` and `Signature`, providing post-quantum key encapsulation and signture mechanisms, respectively. Each must be instantiated with a string identifying one of mechanisms supported by liboqs; these can be enumerated using the `get_enabled_KEM_mechanisms` and `get_enabled_sig_mechanisms` functions. The `example.py` file demonstrates the wrapper's API. +liboqs-python defines two main classes: `KeyEncapsulation` and `Signature`, providing post-quantum key encapsulation and signture mechanisms, respectively. Each must be instantiated with a string identifying one of mechanisms supported by liboqs; these can be enumerated using the `get_enabled_KEM_mechanisms` and `get_enabled_sig_mechanisms` functions. The files in `examples/` demonstrate the wrapper's API. +Support for alternative RNGs is provided via the `randombytes[*]` functions. liboqs installation ------------------- liboqs-python depends on the liboqs C library; it must be compiled as a Linux/macOS library or Windows DLL, and installed in one of: -- any file path specified by the `LIBOQS_INSTALL_PATH` environment variable (e.g. `LIBOQS_INSTALL_PATH="/path/to/liboqs.so"`) +- any file path specified by the `LIBOQS_INSTALL_PATH` environment variable (e.g. `LIBOQS_INSTALL_PATH="/usr/local/bin/liboqs.so"`; **do not forget to specify `liboqs.so` at the end**) - system-wide folder - the liboqs Python module's current folder -`wrapper.py` checks the above locations in that order. At present, only liboqs master branch can be installed; see the [liboqs project](https://github.com/open-quantum-safe/liboqs/) for installation instructions. +`oqs/oqs.py` checks the above locations in that order. At present, only liboqs master branch can be installed; see the [liboqs project](https://github.com/open-quantum-safe/liboqs/) for installation instructions. liboqs-python does not depend on any other Python packages. The package isn't hosted on PyPI yet, but can be installed into a virtualenv using: @@ -71,9 +75,9 @@ As any python module, liboqs wrapper components can be imported into python programs with `import oqs`. -To run the example program: +To run an example program: - python3 examples/example.py + python3 examples/kem.py To run the unit tests with a test runner (e.g. nose or rednose (`apt install python3-nose python3-rednose` or `pip3 install nose rednose`)): @@ -113,6 +117,7 @@ - Ben Davies (University of Waterloo) - Christian Paquin (Microsoft Research) +- Vlad Gheorghiu (evolutionQ, University of Waterloo) ### Support
diff --git a/RELEASE.md b/RELEASE.md index ed92794..ff74517 100644 --- a/RELEASE.md +++ b/RELEASE.md
@@ -1,23 +1,25 @@ -liboqs-python version 0.2.0 +liboqs-python version 0.2.1 =========================== About ----- -The **Open Quantum Safe (OQS) project** has the goal of developing and prototyping quantum-resistant cryptography. More information on OQS can be found on our website: https://openquantumsafe.org/ and on Github at https://github.com/open-quantum-safe/. +The **Open Quantum Safe (OQS) project** has the goal of developing and prototyping quantum-resistant cryptography. More information on OQS can be found on our website: https://openquantumsafe.org/ and on Github at https://github.com/open-quantum-safe/. -**liboqs** is an open source C library for quantum-resistant cryptographic algorithms. See more about liboqs at [https://github.com/open-quantum-safe/liboqs/](https://github.com/open-quantum-safe/liboqs/), including a list of supported algorithms. +**liboqs** is an open source C library for quantum-resistant cryptographic algorithms. See more about liboqs at [https://github.com/open-quantum-safe/liboqs/](https://github.com/open-quantum-safe/liboqs/), including a list of supported algorithms. -**liboqs-python** is an open source Python 3 wrapper for the liboqs C library for quantum-resistant cryptographic algorithms. Details about liboqs-python can be found in [README.md](https://github.com/open-quantum-safe/liboqs-python/blob/master/README.md). See in particular limitations on intended use. +**liboqs-python** is an open source Python 3 wrapper for the liboqs C library for quantum-resistant cryptographic algorithms. Details about liboqs-python can be found in [README.md](https://github.com/open-quantum-safe/liboqs-python/blob/master/README.md). See in particular limitations on intended use. Release notes ============= -This release of liboqs-python was released on October 8, 2019. Its release page on GitHub is https://github.com/open-quantum-safe/liboqs-python/releases/tag/0.2.0. +This release of liboqs-python was released on January xx, 2020. Its release page on GitHub is https://github.com/open-quantum-safe/liboqs-python/releases/tag/0.2.1. What's New ---------- -This is the second release of liboqs-python. +This is the third release of liboqs-python. +This release added partial support for RNGs from `<oqs/rand.h>`, together +with a signature example and an RNG example. +For a list of changes see [CHANGES.txt](https://github.com/open-quantum-safe/liboqs-python/blob/master/CHANGES.txt). -This release updates for compatibility with liboqs 0.2.0, which contains new/updated algorithms based on NIST Round 2 submissions.
diff --git a/examples/example.py b/examples/example.py deleted file mode 100644 index 68c79ca..0000000 --- a/examples/example.py +++ /dev/null
@@ -1,80 +0,0 @@ -# illustrates how to use the python OQS wrapper - -from pprint import pprint -import oqs - - -####################################################################### -# KEM example -####################################################################### - -kems = oqs.get_enabled_KEM_mechanisms() - -print('Enabled KEM mechanisms:') -pprint(kems) - -# create client and server with default KEM mechanism -kemalg = "DEFAULT" -with oqs.KeyEncapsulation(kemalg) as client: - with oqs.KeyEncapsulation(kemalg) as server: - print("\nStarting key encapsulation") - pprint(client.details) - - # client generates its keypair - public_key = client.generate_keypair() - # optionally, the secret key can be obtained by calling export_secret_key() - # and the client can later be reinstantiated with the key pair: - # secret_key = client.export_secret_key() - # store key pair, wait... (session resumption): - # client = oqs.OQS_KEM(kemalg, secret_key) - - # the server encapsulates its secret using the client's public key - ciphertext, shared_secret_server = server.encap_secret(public_key) - - # the client decapsulates the the server's ciphertext to obtain the shared secret - shared_secret_client = client.decap_secret(ciphertext) - - if shared_secret_client == shared_secret_server: - print("success: shared secrets are equal") - else: - print("error: shared secrets are NOT equal") - -print() - -####################################################################### -# Signature example -####################################################################### - -sigs = oqs.get_enabled_sig_mechanisms() - -print('Enabled Signature mechanisms:') -pprint(sigs) - -# create signer and verifier with default signature mechanism -sigalg = "DEFAULT" -with oqs.Signature(sigalg) as signer: - with oqs.Signature(sigalg) as verifier: - print("\nStarting signature") - pprint(signer.details) - - # the signer generates its keypair - signer_public_key = signer.generate_keypair() - # optionally, the secret key can be obtained by calling export_secret_key() - # and the signer can later be reinstantiated with the key pair: - # signer_secret_key = signer.export_secret_key() - # store key pair, wait... (session resumption): - # signer = oqs.OQS_SIG(sigalg, signer_secret_key) - - # the message to sign - message = b'This is the message to sign' - - # the signer signs the message - signature = signer.sign(message) - - # the verifier verifies the signature on the message - is_valid = verifier.verify(message, signature, signer_public_key) - - if is_valid: - print("signature is valid") - else: - print("signature is invalid")
diff --git a/examples/kem.py b/examples/kem.py new file mode 100644 index 0000000..2890de7 --- /dev/null +++ b/examples/kem.py
@@ -0,0 +1,36 @@ +# key encapsulation Python example + +from pprint import pprint +import oqs + +####################################################################### +# KEM example +####################################################################### + +kems = oqs.get_enabled_KEM_mechanisms() + +print('Enabled KEM mechanisms:') +pprint(kems) + +# create client and server with default KEM mechanisms +kemalg = "DEFAULT" +with oqs.KeyEncapsulation(kemalg) as client: + with oqs.KeyEncapsulation(kemalg) as server: + print("\nKey encapsulation details:") + pprint(client.details) + + # client generates its keypair + public_key = client.generate_keypair() + # optionally, the secret key can be obtained by calling export_secret_key() + # and the client can later be re-instantiated with the key pair: + # secret_key = client.export_secret_key() + # store key pair, wait... (session resumption): + # client = oqs.KeyEncapsulation(kemalg, secret_key) + + # the server encapsulates its secret using the client's public key + ciphertext, shared_secret_server = server.encap_secret(public_key) + + # the client decapsulates the the server's ciphertext to obtain the shared secret + shared_secret_client = client.decap_secret(ciphertext) + + print("\nShared secretes coincide:", shared_secret_client == shared_secret_server)
diff --git a/examples/rand.py b/examples/rand.py new file mode 100644 index 0000000..2a3c93d --- /dev/null +++ b/examples/rand.py
@@ -0,0 +1,23 @@ +# various RNGs Python example + +import oqs.rand as oqsrand # must be explicitly imported + +####################################################################### +# randomness example +####################################################################### + +# set the entropy seed to some random values +entropy_seed = [0] * 48 +entropy_seed[0] = 100 +entropy_seed[20] = 200 +entropy_seed[47] = 150 + +oqsrand.randombytes_nist_kat_init(bytes(entropy_seed)) +oqsrand.randombytes_switch_algorithm('NIST-KAT') +print('{:17s}'.format("NIST-KAT:"), ' '.join('{:02X}'.format(x) for x in oqsrand.randombytes(32))) + +oqsrand.randombytes_switch_algorithm("OpenSSL") +print('{:17s}'.format("OpenSSL:"), ' '.join('{:02X}'.format(x) for x in oqsrand.randombytes(32))) + +oqsrand.randombytes_switch_algorithm("system") +print('{:17s}'.format("System (default):"), ' '.join('{:02X}'.format(x) for x in oqsrand.randombytes(32)))
diff --git a/examples/sig.py b/examples/sig.py new file mode 100644 index 0000000..77860aa --- /dev/null +++ b/examples/sig.py
@@ -0,0 +1,38 @@ +# signature Python example + +from pprint import pprint +import oqs + +####################################################################### +# signature example +####################################################################### + +sigs = oqs.get_enabled_sig_mechanisms() + +print('Enabled signature mechanisms:') +pprint(sigs) + +message = "This is the message to sign".encode() + +# create signer and verifier with default signature mechanisms +sigalg = "DEFAULT" +with oqs.Signature(sigalg) as signer: + with oqs.Signature(sigalg) as verifier: + print("\nSignature details:") + pprint(signer.details) + + # signer generates its keypair + signer_public_key = signer.generate_keypair() + # optionally, the secret key can be obtained by calling export_secret_key() + # and the signer can later be re-instantiated with the key pair: + # secret_key = signer.export_secret_key() + # store key pair, wait... (session resumption): + # signer = oqs.Signature(sigalg, secret_key) + + # signer signs the message + signature = signer.sign(message) + + # verifier verifies the signature + is_valid = verifier.verify(message, signature, signer_public_key) + + print("\nValid signature?", is_valid)
diff --git a/oqs/__init__.py b/oqs/__init__.py index c7db430..f82845b 100644 --- a/oqs/__init__.py +++ b/oqs/__init__.py
@@ -1,5 +1 @@ -from oqs.wrapper import KeyEncapsulation -from oqs.wrapper import Signature -from oqs.wrapper import MechanismNotEnabledError, MechanismNotSupportedError -from oqs.wrapper import get_enabled_KEM_mechanisms, get_supported_KEM_mechanisms -from oqs.wrapper import get_enabled_sig_mechanisms, get_supported_sig_mechanisms +from oqs.oqs import *
diff --git a/oqs/wrapper.py b/oqs/oqs.py similarity index 85% rename from oqs/wrapper.py rename to oqs/oqs.py index c9739ba..9488e06 100644 --- a/oqs/wrapper.py +++ b/oqs/oqs.py
@@ -15,13 +15,12 @@ # import platform to learn the OS we're on import platform - -LIBOQS_SHARED_OBJ = 'oqs' if platform.system() == 'Windows' else 'liboqs.so' -LIBOQS_INSTALL_PATH = 'LIBOQS_INSTALL_PATH' - +_LIBOQS_SHARED_OBJ = 'oqs' if platform.system() == 'Windows' else 'liboqs.so' +_LIBOQS_INSTALL_PATH = 'LIBOQS_INSTALL_PATH' # expected return value from native OQS functions -_OQS_SUCCESS = 0 +OQS_SUCCESS = 0 +OQS_ERROR = -1 def _load_shared_obj(): @@ -29,11 +28,11 @@ paths = [] # try custom env var first - if LIBOQS_INSTALL_PATH in os.environ: - paths.append(os.environ[LIBOQS_INSTALL_PATH]) + if _LIBOQS_INSTALL_PATH in os.environ: + paths.append(os.environ[_LIBOQS_INSTALL_PATH]) # search typical locations too - paths += [ctu.find_library('oqs'), os.path.join(os.curdir, LIBOQS_SHARED_OBJ)] + paths += [ctu.find_library('oqs'), os.path.join(os.curdir, _LIBOQS_SHARED_OBJ)] dll = ct.windll if platform.system() == 'Windows' else ct.cdll for path in paths: @@ -59,20 +58,18 @@ def __init__(self, alg_name): """ - :param alg_name: requested algorithm name + :param alg_name: requested algorithm name. """ self.alg_name = alg_name self.message = alg_name + ' is not supported by OQS' class MechanismNotEnabledError(MechanismNotSupportedError): - """ - Exception raised when an algorithm is not supported but not enabled by OQS. - """ + """Exception raised when an algorithm is not supported but not enabled by OQS.""" def __init__(self, alg_name): """ - :param alg_name: requested algorithm name + :param alg_name: requested algorithm name. """ self.alg_name = alg_name self.message = alg_name + ' is not supported but not enabled by OQS' @@ -108,9 +105,11 @@ def __init__(self, alg_name, secret_key=None): """ - Create new KeyEncapsulation with the given algorithm. - :param alg_name: KEM mechanism algorithm name - :param secret_key: optional if generating by generate_keypair() later + Creates new KeyEncapsulation with the given algorithm. + + :param alg_name: KEM mechanism algorithm name. Enabled KEM mechanisms can be obtained with + get_enabled_KEM_mechanisms(). + :param secret_key: optional if generating by generate_keypair() later. """ self.alg_name = alg_name if alg_name not in _enabled_KEMs: @@ -126,7 +125,7 @@ 'name': self._kem.contents.method_name.decode(), 'version': self._kem.contents.alg_version.decode(), 'claimed_nist_level': int(self._kem.contents.claimed_nist_level), - 'is_ind_cca' : bool(self._kem.contents.ind_cca), + 'is_ind_cca': bool(self._kem.contents.ind_cca), 'length_public_key': int(self._kem.contents.length_public_key), 'length_secret_key': int(self._kem.contents.length_secret_key), 'length_ciphertext': int(self._kem.contents.length_ciphertext), @@ -150,7 +149,7 @@ public_key = ct.create_string_buffer(self._kem.contents.length_public_key) self.secret_key = ct.create_string_buffer(self._kem.contents.length_secret_key) rv = liboqs.OQS_KEM_keypair(self._kem, ct.byref(public_key), ct.byref(self.secret_key)) - return bytes(public_key) if rv == _OQS_SUCCESS else 0 + return bytes(public_key) if rv == OQS_SUCCESS else 0 def export_secret_key(self): """Exports the secret key.""" @@ -166,7 +165,7 @@ ciphertext = ct.create_string_buffer(self._kem.contents.length_ciphertext) shared_secret = ct.create_string_buffer(self._kem.contents.length_shared_secret) rv = liboqs.OQS_KEM_encaps(self._kem, ct.byref(ciphertext), ct.byref(shared_secret), my_public_key) - return bytes(ciphertext), bytes(shared_secret) if rv == _OQS_SUCCESS else 0 + return bytes(ciphertext), bytes(shared_secret) if rv == OQS_SUCCESS else 0 def decap_secret(self, ciphertext): """ @@ -177,7 +176,7 @@ my_ciphertext = ct.create_string_buffer(ciphertext, self._kem.contents.length_ciphertext) shared_secret = ct.create_string_buffer(self._kem.contents.length_shared_secret) rv = liboqs.OQS_KEM_decaps(self._kem, ct.byref(shared_secret), my_ciphertext, self.secret_key) - return bytes(shared_secret) if rv == _OQS_SUCCESS else 0 + return bytes(shared_secret) if rv == OQS_SUCCESS else 0 def free(self): """Releases the native resources.""" @@ -186,7 +185,7 @@ liboqs.OQS_MEM_cleanse(ct.byref(self.secret_key), self._kem.contents.length_secret_key) def __repr__(self): - return "Key encapsulation mechanism: " + self._kem.contents.method_name.decode() + return 'Key encapsulation mechanism: ' + self._kem.contents.method_name.decode() liboqs.OQS_KEM_new.restype = ct.POINTER(KeyEncapsulation) @@ -194,9 +193,10 @@ def is_KEM_enabled(alg_name): - """Returns True if the KEM algorithm is enabled. + """ + Returns True if the KEM algorithm is enabled. - :param alg_name: a KEM mechanism algorithm name + :param alg_name: a KEM mechanism algorithm name. """ return liboqs.OQS_KEM_alg_is_enabled(ct.create_string_buffer(alg_name.encode())) @@ -212,7 +212,7 @@ def get_supported_KEM_mechanisms(): - """Returns list of supported KEM mechanisms.""" + """Returns the list of supported KEM mechanisms.""" return _supported_KEMs @@ -243,12 +243,13 @@ ("verify_cb", ct.c_void_p) ] - def __init__(self, alg_name, secret_key = None): + def __init__(self, alg_name, secret_key=None): """ - Create new Signature with the given algorithm. - :param alg_name: a signature mechanism algorithm name. Enabled signature - mechanisms can be obtained with get_enabled_KEM_mechanisms(). - :param secret_key: optional, if generated by generate_keypair() + Creates new Signature with the given algorithm. + + :param alg_name: a signature mechanism algorithm name. Enabled signature mechanisms can be obtained with + get_enabled_sig_mechanisms(). + :param secret_key: optional, if generated by generate_keypair(). """ if alg_name not in _enabled_sigs: # perhaps it's a supported but not enabled alg @@ -257,7 +258,7 @@ else: raise MechanismNotSupportedError(alg_name) - self._sig = liboqs.OQS_SIG_new( ct.create_string_buffer(alg_name.encode())) + self._sig = liboqs.OQS_SIG_new(ct.create_string_buffer(alg_name.encode())) self.details = { 'name': self._sig.contents.method_name.decode(), 'version': self._sig.contents.alg_version.decode(), @@ -285,7 +286,7 @@ public_key = ct.create_string_buffer(self._sig.contents.length_public_key) self.secret_key = ct.create_string_buffer(self._sig.contents.length_secret_key) rv = liboqs.OQS_SIG_keypair(self._sig, ct.byref(public_key), ct.byref(self.secret_key)) - return bytes(public_key) if rv == _OQS_SUCCESS else 0 + return bytes(public_key) if rv == OQS_SUCCESS else 0 def export_secret_key(self): """Exports the secret key.""" @@ -301,12 +302,12 @@ my_message = ct.create_string_buffer(message, len(message)) message_len = ct.c_int(len(my_message)) signature = ct.create_string_buffer(self._sig.contents.length_signature) - sig_len = ct.c_int(self._sig.contents.length_signature) # initialize to maximum signature size + sig_len = ct.c_int(self._sig.contents.length_signature) # initialize to maximum signature size rv = liboqs.OQS_SIG_sign(self._sig, ct.byref(signature), ct.byref(sig_len), my_message, message_len, self.secret_key) - return bytes(signature[:sig_len.value]) if rv == _OQS_SUCCESS else 0 + return bytes(signature[:sig_len.value]) if rv == OQS_SUCCESS else 0 def verify(self, message, signature, public_key): """ @@ -326,7 +327,7 @@ my_public_key = ct.create_string_buffer(public_key, self._sig.contents.length_public_key) rv = liboqs.OQS_SIG_verify(self._sig, my_message, message_len, my_signature, sig_len, my_public_key) - return True if rv == _OQS_SUCCESS else False + return True if rv == OQS_SUCCESS else False def free(self): """Releases the native resources.""" @@ -335,7 +336,7 @@ liboqs.OQS_MEM_cleanse(ct.byref(self.secret_key), self._sig.contents.length_secret_key) def __repr__(self): - return "Signature mechanism: " + self._sig.contents.method_name.decode() + return 'Signature mechanism: ' + self._sig.contents.method_name.decode() liboqs.OQS_SIG_new.restype = ct.POINTER(Signature) @@ -343,9 +344,10 @@ def is_sig_enabled(alg_name): - """Returns True if the signature algorithm is enabled. + """ + Returns True if the signature algorithm is enabled. - :param alg_name: a signature mechanism algorithm name + :param alg_name: a signature mechanism algorithm name. """ return liboqs.OQS_SIG_alg_is_enabled(ct.create_string_buffer(alg_name.encode())) @@ -361,5 +363,5 @@ def get_supported_sig_mechanisms(): - """Returns list of supported signature mechanisms.""" + """Returns the list of supported signature mechanisms.""" return _supported_sigs
diff --git a/oqs/rand.py b/oqs/rand.py new file mode 100644 index 0000000..d6959c4 --- /dev/null +++ b/oqs/rand.py
@@ -0,0 +1,58 @@ +""" +Open Quantum Safe (OQS) Python Wrapper for liboqs + +The liboqs project provides post-quantum public key cryptography algorithms: +https://github.com/open-quantum-safe/liboqs + +This module provides a Python 3 interface to libOQS <oqs/rand.h> RNGs. +""" + +import oqs + + +def randombytes(bytes_to_read): + """ + Generates random bytes. This implementation uses either the default RNG algorithm ("system"), or whichever + algorithm has been selected by random_bytes_switch_algorithm(). + + :param bytes_to_read: the number of random bytes to generate. + :return: random bytes. + """ + result = oqs.ct.create_string_buffer(bytes_to_read) + oqs.liboqs.OQS_randombytes(result, oqs.ct.c_int(bytes_to_read)) + return bytes(result) + + +def randombytes_switch_algorithm(alg_name): + """ + Switches the core OQS_randombytes to use the specified algorithm. See <oqs/rand.h> liboqs headers for more details. + + :param alg_name: algorithm name, possible values are "system", "NIST-KAT", "OpenSSL". + """ + if oqs.liboqs.OQS_randombytes_switch_algorithm(oqs.ct.create_string_buffer(alg_name.encode())) != oqs.OQS_SUCCESS: + raise RuntimeError('Can not switch algorithm') + + +def randombytes_nist_kat_init(entropy_input, personalization_string=None): + """ + Initializes the NIST DRBG with the an entropy seed. + + :param entropy_input: entropy input seed, must be exactly 48 bytes long. + :param personalization_string: optional personalization string, which, if present, must be at least 48 bytes long. + """ + if len(entropy_input) != 48: + raise ValueError('The entropy source must be exactly 48 bytes long') + + if personalization_string is not None: + if len(personalization_string) < 48: + raise ValueError('The personalization string must be either empty or at least 48 bytes long') + oqs.liboqs.OQS_randombytes_nist_kat_init(oqs.ct.create_string_buffer(entropy_input), + oqs.ct.create_string_buffer(personalization_string), 256) + + oqs.liboqs.OQS_randombytes_nist_kat_init(oqs.ct.create_string_buffer(entropy_input), 0, 256) + +# def randombytes_in_place(random_array, bytes_to_read): +# raise NotImplementedError + +# def randombytes_custom_algorithm(fun): +# raise NotImplementedError
diff --git a/setup.py b/setup.py index 67f8b0e..edd43dd 100644 --- a/setup.py +++ b/setup.py
@@ -4,7 +4,7 @@ setup( name='liboqs-python', - version='0.2.0', + version='0.2.1', author='liboqs team', author_email='[email protected]', packages=find_packages(exclude=('tests', 'docs', 'examples')),
diff --git a/tests/test_kem.py b/tests/test_kem.py index 4f0d728..c85273a 100644 --- a/tests/test_kem.py +++ b/tests/test_kem.py
@@ -1,9 +1,11 @@ import oqs import random + def test_correctness(): for alg_name in oqs.get_enabled_KEM_mechanisms(): - yield (check_correctness, alg_name) + yield check_correctness, alg_name + def check_correctness(alg_name): kem = oqs.KeyEncapsulation(alg_name) @@ -13,9 +15,11 @@ assert shared_secret_client == shared_secret_server kem.free() + def test_wrong_ciphertext(): for alg_name in oqs.get_enabled_KEM_mechanisms(): - yield (check_wrong_ciphertext, alg_name) + yield check_wrong_ciphertext, alg_name + def check_wrong_ciphertext(alg_name): kem = oqs.KeyEncapsulation(alg_name) @@ -26,6 +30,7 @@ assert shared_secret_client != shared_secret_server kem.free() + def test_not_supported(): try: kem = oqs.KeyEncapsulation('bogus') @@ -34,7 +39,7 @@ pass except E: raise AssertionError("An unexpected exception was raised. " + E) - + def test_not_enabled(): # TODO: test broken as the compiled lib determines which algorithms are @@ -50,10 +55,13 @@ except E: raise AssertionError("An unexpected exception was raised. " + E) + if __name__ == '__main__': try: import nose2 + nose2.main() except ImportError: import nose - nose.runmodule() \ No newline at end of file + + nose.runmodule()
diff --git a/tests/test_sig.py b/tests/test_sig.py index 359b2ad..2cdaf49 100644 --- a/tests/test_sig.py +++ b/tests/test_sig.py
@@ -1,9 +1,11 @@ import oqs import random + def test_correctness(): for alg_name in oqs.get_enabled_sig_mechanisms(): - yield (check_correctness, alg_name) + yield check_correctness, alg_name + def check_correctness(alg_name): message = bytes(random.getrandbits(8) for _ in range(100)) @@ -13,9 +15,11 @@ assert sig.verify(message, signature, public_key) sig.free() + def test_wrong_message(): for alg_name in oqs.get_enabled_sig_mechanisms(): - yield (check_wrong_message, alg_name) + yield check_wrong_message, alg_name + def check_wrong_message(alg_name): message = bytes(random.getrandbits(8) for _ in range(100)) @@ -23,12 +27,14 @@ public_key = sig.generate_keypair() signature = sig.sign(message) wrong_message = bytes(random.getrandbits(8) for _ in range(100)) - assert not(sig.verify(wrong_message, signature, public_key)) + assert not (sig.verify(wrong_message, signature, public_key)) sig.free() + def test_wrong_signature(): for alg_name in oqs.get_enabled_sig_mechanisms(): - yield (check_wrong_signature, alg_name) + yield check_wrong_signature, alg_name + def check_wrong_signature(alg_name): message = bytes(random.getrandbits(8) for _ in range(100)) @@ -36,12 +42,14 @@ public_key = sig.generate_keypair() signature = sig.sign(message) wrong_signature = bytes(random.getrandbits(8) for _ in range(sig.details['length_signature'])) - assert not(sig.verify(message, wrong_signature, public_key)) + assert not (sig.verify(message, wrong_signature, public_key)) sig.free() + def test_wrong_public_key(): for alg_name in oqs.get_enabled_sig_mechanisms(): - yield (check_wrong_public_key, alg_name) + yield check_wrong_public_key, alg_name + def check_wrong_public_key(alg_name): message = bytes(random.getrandbits(8) for _ in range(100)) @@ -49,9 +57,10 @@ public_key = sig.generate_keypair() signature = sig.sign(message) wrong_public_key = bytes(random.getrandbits(8) for _ in range(sig.details['length_public_key'])) - assert not(sig.verify(message, signature, wrong_public_key)) + assert not (sig.verify(message, signature, wrong_public_key)) sig.free() + def test_not_supported(): try: sig = oqs.Signature('bogus') @@ -76,10 +85,13 @@ except E: raise AssertionError("An unexpected exception was raised. " + E) + if __name__ == '__main__': try: import nose2 + nose2.main() except ImportError: import nose - nose.runmodule() \ No newline at end of file + + nose.runmodule()