├── docs ├── requirements.txt ├── Makefile ├── index.rst └── conf.py ├── MANIFEST.in ├── requirements.txt ├── petlib ├── _cffi_src │ ├── install_openssl_v1_1.sh │ └── openssl │ │ ├── openssl_v1_1.c │ │ ├── openssl_v1_0.c │ │ ├── openssl_v1_1.h │ │ └── openssl_v1_0.h ├── __init__.py ├── _compat.py ├── bindings.py ├── compile.py ├── hmac.py ├── ecdsa.py ├── pack.py └── cipher.py ├── utils ├── ignoretest.py └── pavement.py ├── .travis.yml ├── CONTRIBUTORS.txt ├── tox.ini ├── contrib ├── ec_timing.py ├── CountSketch.py └── cred_server.py ├── examples ├── EcGroup_timing.py ├── toyrsa.py ├── blogsample.py ├── zkp.py ├── amacs.py ├── pouf.py ├── AHEG.py ├── openIDblind.py ├── amacscredsext.py ├── GK15ringsig.py ├── BLcred.py └── genzkp.py ├── LICENSE.txt ├── setup.py ├── README.rst ├── .gitignore ├── pavement.py └── pylintrc /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | mock>=0.7 -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | graft petlib 2 | graft contrib 3 | 4 | include LICENSE* 5 | 6 | recursive-exclude docs/_build * 7 | global-exclude *.py[co] 8 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | cffi>=1.4 2 | pycparser>=2.10 3 | future>=0.14.3 4 | pytest>=2.5.0 5 | pytest-cov>=1.8.1 6 | msgpack-python>=0.4.6 7 | tox>=2.9.0 -------------------------------------------------------------------------------- /petlib/_cffi_src/install_openssl_v1_1.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | sudo apt-get remove -y -qq libssl libssl-dev 4 | wget https://www.openssl.org/source/openssl-1.1.0f.tar.gz 5 | tar xzvf openssl-1.1.0f.tar.gz 6 | cd openssl-1.1.0f 7 | ./config -Wl,--enable-new-dtags,-rpath,'$(LIBRPATH)' 8 | make 9 | sudo make install 10 | sudo ldconfig 11 | -------------------------------------------------------------------------------- /utils/ignoretest.py: -------------------------------------------------------------------------------- 1 | ## Usage: 2 | # export PYTHONPATH=`pwd`:$PYTHONPATH 3 | # pylint --load-plugins ignoretest examples/kulan.py 4 | 5 | from astroid import MANAGER 6 | from astroid import scoped_nodes 7 | 8 | def register(linter): 9 | pass 10 | 11 | def transform(modu): 12 | for m in list(modu): 13 | try: 14 | if m.startswith("test_") and modu[m].is_function: 15 | print("Ignore function: %s" % m) 16 | modu.body.remove(modu[m]) 17 | except: 18 | continue 19 | 20 | MANAGER.register_transform(scoped_nodes.Module, transform) -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | python: 4 | - "2.7" 5 | - "3.6" 6 | 7 | env: 8 | - OPENSSL_VERSION=1.0 9 | - OPENSSL_VERSION=1.1 10 | 11 | before_install: 12 | - sudo apt-get update -qq 13 | - sudo apt-get install -qq python-dev libssl-dev libffi-dev 14 | - wget https://bootstrap.pypa.io/ez_setup.py -O - | sudo python 15 | 16 | # command to install dependencies 17 | install: 18 | - if [ "$OPENSSL_VERSION" = "1.1" ]; then sh petlib/_cffi_src/install_openssl_v1_1.sh > /dev/null 2>&1; fi 19 | - pip install tox-travis 20 | - pip install -r requirements.txt 21 | 22 | # command to run tests 23 | script: tox 24 | -------------------------------------------------------------------------------- /petlib/__init__.py: -------------------------------------------------------------------------------- 1 | # The petlib version 2 | VERSION = '0.0.45' 3 | 4 | 5 | __all__ = ["bindings", "bn", "cipher", "compile", "ecdsa", "ec", "hmac", "pack"] 6 | 7 | def run_tests(): 8 | # These are only needed in case we test 9 | import pytest 10 | import os.path 11 | import glob 12 | 13 | # List all petlib files in the directory 14 | petlib_dir = os.path.dirname(os.path.realpath(__file__)) 15 | pyfiles = glob.glob(os.path.join(petlib_dir, '*.py')) 16 | 17 | # Run the test suite 18 | print("Directory: %s" % pyfiles) 19 | res = pytest.main(["-v", "-x"] + pyfiles) 20 | print("Result: %s" % res) 21 | 22 | # Return exit result 23 | return res 24 | -------------------------------------------------------------------------------- /CONTRIBUTORS.txt: -------------------------------------------------------------------------------- 1 | CONTRIBUTORS 2 | 3 | George Danezis (https://github.com/gdanezis) 4 | Ben Laurie (https://github.com/benlaurie) 5 | Vasilios Mavroudis (https://github.com/mavroudisv) 6 | Salman Khalifa (https://github.com/The-Skas) 7 | Tariq Elahi (https://github.com/TariqEE) 8 | Panos Louridas (https://github.com/louridas) 9 | Bogdan Kulynych (https://github.com/bogdan-kulynych) 10 | Wouter Lueks (https://github.com/wouterl) 11 | Ravi Rahman (https://github.com/ravirahman) 12 | 13 | FUNDING 14 | 15 | PANORAMIX (Project ID: 653497) Funded under: H2020-EU.3.7. - Secure societies - Protecting freedom and security of Europe and its citizens 16 | NEXTLEAP (project ID: 688722) Funded under: H2020-EU.2.1.1. - INDUSTRIAL LEADERSHIP - Leadership in enabling and industrial technologies 17 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | # Tox (http://tox.testrun.org/) is a tool for running tests 2 | # in multiple virtualenvs. This configuration file will run the 3 | # test suite on all supported python versions. To use it, "pip install tox" 4 | # and then run "tox" from this directory. 5 | 6 | [tox] 7 | envlist = py27, py36 8 | 9 | [testenv] 10 | commands = 11 | python setup.py build_ext --inplace 12 | pytest -sv --doctest-modules petlib/bindings.py petlib/compile.py petlib/pack.py petlib/bn.py petlib/ecdsa.py petlib/hmac.py petlib/cipher.py petlib/ec.py petlib/__init__.py 13 | pytest -sv --doctest-modules examples/AHEG.py examples/BLcred.py examples/GK15ringsig.py examples/amacscredsext.py examples/blogsample.py examples/toyrsa.py examples/amacscreds.py examples/EcGroup_timing.py examples/openIDblind.py examples/zkp.py examples/amacs.py examples/genzkp.py examples/pouf.py 14 | 15 | deps = 16 | pytest 17 | -------------------------------------------------------------------------------- /contrib/ec_timing.py: -------------------------------------------------------------------------------- 1 | from petlib.ec import EcGroup, EcPt 2 | from petlib.bn import Bn 3 | import time 4 | 5 | timings = [] 6 | curves = EcGroup.list_curves() 7 | 8 | for gid in curves: 9 | G = EcGroup(gid) 10 | gx = G.order().random() * G.generator() 11 | 12 | rnd = [G.order().random() for _ in range(100)] 13 | 14 | t0 = time.clock() 15 | for r in rnd: 16 | dud = r * gx 17 | t1 = time.clock() 18 | 19 | repreats = 1000 20 | t = [] 21 | for x in [2, 200]: 22 | o = Bn(2) ** x 23 | tests = [o.random() for _ in range(repreats)] 24 | 25 | tx = time.clock() 26 | for y in tests: 27 | dud = y * gx 28 | t += [time.clock() - tx] 29 | # print(x, t[-1] / repreats) 30 | if abs(t[0] - t[-1]) < 5.0 / 100: 31 | const = "CONST" 32 | else: 33 | const = "NOCONST" 34 | 35 | timings += [((t1-t0)*1000.0/100.0, gid, const)] 36 | 37 | 38 | timings = sorted(timings) 39 | for t in timings: 40 | if "binary" not in curves[t[1]]: 41 | print(" %.2fms (%s): (%s) %s" % (t[0], t[2], t[1], curves[t[1]])) -------------------------------------------------------------------------------- /examples/EcGroup_timing.py: -------------------------------------------------------------------------------- 1 | from petlib.ec import EcGroup, EcPt 2 | from petlib.bn import Bn 3 | 4 | import time 5 | 6 | if __name__ == "__main__": 7 | fails = 0 8 | print("List of curves passing the constant-time scalar mult test:") 9 | for (nid, name) in sorted(EcGroup.list_curves().items()): 10 | G = EcGroup(nid) 11 | g = G.generator() 12 | order = G.order() 13 | h = order.random() * g 14 | 15 | repreats = 100 16 | t = [] 17 | for x in range(0, 400, 100): 18 | o = Bn(2) ** x 19 | tests = [o.random() for _ in range(repreats)] 20 | 21 | t0 = time.clock() 22 | for y in tests: 23 | y * h 24 | t += [time.clock() - t0] 25 | # print x, t[-1] / repreats 26 | res = abs(t[0] - t[-1]) < 1.0 / 100 27 | if res: 28 | 29 | ps = 1.0 / (t[-1] / repreats) 30 | res = ["FAIL", "PASS"][res] 31 | print("%3d\t%s\t%2.1f/s\t%s" % (nid, res, ps, name)) 32 | else: 33 | fails += 1 34 | 35 | print("%d fails" % fails) -------------------------------------------------------------------------------- /examples/toyrsa.py: -------------------------------------------------------------------------------- 1 | from petlib.bn import Bn 2 | from os import urandom 3 | 4 | import pytest 5 | 6 | def gen_key(): 7 | """Example naive RSA key generation""" 8 | p = Bn.get_prime(512) 9 | q = Bn.get_prime(512) 10 | m = p * q 11 | phi = (p - 1) * (q - 1) 12 | e = Bn(2**16 + 1) 13 | d = e.mod_inverse(phi) 14 | pub = (e, m) 15 | priv = (d,) 16 | return pub, priv 17 | 18 | def enc(pub, plaintext): 19 | """Naive RSA encryption""" 20 | e, m = pub 21 | plain = Bn.from_binary(plaintext) 22 | assert 1 < plain < m 23 | cipher = pow(plain, e, m) 24 | return cipher.binary() 25 | 26 | def dec(pub, priv, ciphertext): 27 | """Naive RSA decryption. NOT const. time.""" 28 | _, m = pub 29 | d, = priv 30 | cipher = Bn.from_binary(ciphertext) 31 | assert 1 < cipher < m 32 | plain = pow(cipher, d, m) 33 | return plain.binary() 34 | 35 | def test_toyrsa(): 36 | pub, priv = gen_key() 37 | c = enc(pub, b"Hello World!") 38 | p = dec(pub, priv, c) 39 | assert p == b"Hello World!" 40 | print(p) 41 | 42 | if __name__ == "__main__": 43 | test_toyrsa() 44 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, George Danezis (UCL) 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /examples/blogsample.py: -------------------------------------------------------------------------------- 1 | from petlib.ec import EcGroup 2 | from genzkp import ZKProof, ZKEnv, ConstGen, Sec 3 | 4 | def test_blog_post(): 5 | # Define an EC group 6 | G = EcGroup(713) 7 | print (EcGroup.list_curves()[713]) 8 | order = G.order() 9 | 10 | ## Define the Zero-Knowledge proof statement 11 | zk = ZKProof(G) 12 | g, h = zk.get(ConstGen, ["g", "h"]) 13 | x, o = zk.get(Sec, ["x", "o"]) 14 | Cxo = zk.get(ConstGen, "Cxo") 15 | zk.add_proof(Cxo, x*g + o*h) 16 | 17 | ## Render the proof statement in Latex 18 | print(zk.render_proof_statement()) 19 | 20 | # A concrete Pedersen commitment 21 | ec_g = G.generator() 22 | ec_h = order.random() * ec_g 23 | bn_x = order.random() 24 | bn_o = order.random() 25 | ec_Cxo = bn_x * ec_g + bn_o * ec_h 26 | 27 | ## Bind the concrete variables to the Proof 28 | env = ZKEnv(zk) 29 | env.g, env.h = ec_g, ec_h 30 | env.Cxo = ec_Cxo 31 | env.x = bn_x 32 | env.o = bn_o 33 | 34 | # Create the Non-Interactive Zero-Knowledge (NIZK) proof 35 | sig = zk.build_proof(env.get()) 36 | 37 | # Execute the verification on the proof 'sig' 38 | env = ZKEnv(zk) 39 | env.g, env.h = ec_g, ec_h 40 | env.Cxo = ec_Cxo 41 | 42 | assert zk.verify_proof(env.get(), sig) 43 | 44 | if __name__ == "__main__": 45 | test_blog_post() 46 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from setuptools import setup 4 | 5 | import petlib 6 | 7 | setup(name='petlib', 8 | version=petlib.VERSION, 9 | description='A library implementing a number of Privacy Enhancing Technologies (PETs)', 10 | author='George Danezis', 11 | author_email='g.danezis@ucl.ac.uk', 12 | url=r'https://pypi.python.org/pypi/petlib/', 13 | packages=['petlib'], 14 | license="2-clause BSD", 15 | long_description="""A library wrapping Open SSL low-level cryptographic libraries to build Privacy Enhancing Technologies (PETs)""", 16 | 17 | setup_requires=["cffi>=1.0.0", 18 | "pytest >= 2.6.4"], 19 | package_data = { 20 | "": ["_cffi_src/openssl/*"] 21 | }, 22 | include_package_data = True, 23 | tests_require = [ 24 | "cffi >= 1.0.0", 25 | "pycparser >= 2.10", 26 | "future >= 0.14.3", 27 | "pytest >= 2.5.0", 28 | "pytest-cov >= 1.8.1", 29 | "msgpack-python >= 0.4.6", 30 | ], 31 | cffi_modules=["petlib/compile.py:_FFI"], 32 | install_requires=[ 33 | "cffi >= 1.0.0", 34 | "pycparser >= 2.10", 35 | "future >= 0.14.3", 36 | "pytest >= 2.5.0", 37 | "pytest-cov >= 1.8.1", 38 | "msgpack-python >= 0.4.6", 39 | ], 40 | zip_safe=False, 41 | ) 42 | -------------------------------------------------------------------------------- /petlib/_compat.py: -------------------------------------------------------------------------------- 1 | import cffi 2 | import warnings 3 | 4 | 5 | class OpenSSLVersion: 6 | V1_0 = "1_0" 7 | V1_1 = "1_1" 8 | 9 | 10 | def get_abi_lib(): 11 | ffi = cffi.FFI() 12 | ffi.cdef("unsigned long OpenSSL_version_num();") 13 | ffi.cdef("unsigned long SSLeay();") 14 | lib = ffi.dlopen("crypto") 15 | return lib 16 | 17 | 18 | def get_openssl_version(lib=None, warn=False): 19 | """Returns the OpenSSL version that is used for bindings.""" 20 | 21 | if lib is None: 22 | lib = get_abi_lib() 23 | 24 | try: 25 | full_version = lib.OpenSSL_version_num() 26 | except AttributeError: 27 | full_version = lib.SSLeay() 28 | 29 | version = full_version >> 20 30 | if version == 0x101: 31 | return OpenSSLVersion.V1_1 32 | elif version == 0x100: 33 | if warn: 34 | warnings.warn( 35 | "Support for the system OpenSSL version (0x%x) is pending deprecation. " 36 | "Please upgrade to OpenSSL v1.1" % version) 37 | return OpenSSLVersion.V1_0 38 | else: 39 | # If the version is not 1.0 or 1.1, assume its a later one, and optimistically 40 | # assume it doesn't horribly break the interface this time. 41 | if warn: 42 | warnings.warn( 43 | "System OpenSSL version is not supported: %d. " 44 | "Attempting to use in OpenSSL v1.1 mode." % version) 45 | return OpenSSLVersion.V1_1 46 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | petlib 2 | ====== 3 | 4 | A python library that implements a number of Privacy Enhancing Technologies (PETs). 5 | 6 | * The full petlib documentation: http://petlib.readthedocs.org/en/latest/ 7 | * Package petlib on pypi: https://pypi.python.org/pypi/petlib/ 8 | * Git repository: https://github.com/gdanezis/petlib 9 | * Travis CI report: https://travis-ci.org/gdanezis/petlib 10 | 11 | .. docs-include-marker-begin-do-not-remove 12 | 13 | Pre-requisites 14 | -------------- 15 | 16 | On *Ubuntu / debian* use `apt-get` to install package ``libssl-dev``. Ensure you also install ``libffi-dev`` and ``python-dev``:: 17 | 18 | sudo apt-get install python-dev 19 | sudo apt-get install libssl-dev 20 | sudo apt-get install libffi-dev 21 | 22 | On *MacOS*, install OpenSSL 1.1.x using homebrew: 23 | 24 | brew install openssl@1.1 25 | 26 | On *Windows*, install 32 bit or 64 bit OpenSSL binary edition matching your Python installation. Ensure ``libeay32.dll`` is on the system ``PATH`` (https://www.openssl.org/related/binaries.html). 27 | Configure the path variables of Microsoft VS compilers for 32 bit or 64 bit architectures, by executing the command ``vcvars32.bat`` or ``vcvarsx86_amd64.bat``. 28 | 29 | 30 | Quick install 31 | ------------- 32 | 33 | If you have ``pip`` installed the following command should install ``petlib``:: 34 | 35 | pip install petlib 36 | 37 | Test your installation:: 38 | 39 | python -c "import petlib; petlib.run_tests()" 40 | 41 | 42 | .. docs-include-marker-end-do-not-remove 43 | 44 | Build & Test 45 | ------------ 46 | 47 | You may use ``setuptools`` and ``tox`` to build and test the library:: 48 | 49 | python setup.py build_ext 50 | 51 | To run all tests simply do:: 52 | 53 | tox 54 | -------------------------------------------------------------------------------- /petlib/_cffi_src/openssl/openssl_v1_1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | 16 | #include 17 | 18 | 19 | #define BN_num_bytes(a) ((BN_num_bits(a)+7)/8) 20 | 21 | int bn_num_bytes(BIGNUM * a){ 22 | return BN_num_bytes(a); 23 | } 24 | 25 | int bn_is_odd(BIGNUM * a){ 26 | return BN_is_odd(a); 27 | } 28 | 29 | void init_ciphers(void){ 30 | OPENSSL_config(NULL); 31 | } 32 | 33 | void cleanup_ciphers(void){ 34 | 35 | /* Removes all digests and ciphers */ 36 | EVP_cleanup(); 37 | 38 | /* if you omit the next, a small leak may be left when you make use of the BIO (low level API) for e.g. base64 transformations */ 39 | CRYPTO_cleanup_all_ex_data(); 40 | 41 | /* Remove error strings */ 42 | } 43 | 44 | /* This line is borrowed from pyca/cryptography. 45 | 46 | https://github.com/pyca/cryptography/commit/bc1667791eedfe9d77d56dd9014e26481f571ff5 47 | */ 48 | int (*setup_ssl_threads)(void) = NULL; 49 | 50 | /* Version of ECDSA_SIG_set0 that does not take ownership 51 | * of passed variables r and s. */ 52 | int ECDSA_SIG_set0_petlib(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s) { 53 | BIGNUM *rnew = BN_new(); 54 | BIGNUM *snew = BN_new(); 55 | 56 | BN_copy(rnew, r); 57 | BN_copy(snew, s); 58 | 59 | return ECDSA_SIG_set0(sig, rnew, snew); 60 | // NOTE: new values rnew, snew not freed because signature 61 | // takes ownership. 62 | } 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | petlib/_petlib.o 9 | petlib/_petlib.c 10 | 11 | # Distribution / packaging 12 | .Python 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | .hypothesis/ 50 | 51 | # Translations 52 | *.mo 53 | *.pot 54 | 55 | # Django stuff: 56 | *.log 57 | .static_storage/ 58 | .media/ 59 | local_settings.py 60 | 61 | # Flask stuff: 62 | instance/ 63 | .webassets-cache 64 | 65 | # Scrapy stuff: 66 | .scrapy 67 | 68 | # Sphinx documentation 69 | docs/_build/ 70 | 71 | # PyBuilder 72 | target/ 73 | 74 | # Jupyter Notebook 75 | .ipynb_checkpoints 76 | 77 | # pyenv 78 | .python-version 79 | 80 | # celery beat schedule file 81 | celerybeat-schedule 82 | 83 | # SageMath parsed files 84 | *.sage.py 85 | 86 | # Environments 87 | .env 88 | .venv 89 | env/ 90 | venv/ 91 | ENV/ 92 | env.bak/ 93 | venv.bak/ 94 | 95 | # Spyder project settings 96 | .spyderproject 97 | .spyproject 98 | 99 | # Rope project settings 100 | .ropeproject 101 | 102 | # mkdocs documentation 103 | /site 104 | 105 | # mypy 106 | .mypy_cache/ 107 | -------------------------------------------------------------------------------- /examples/zkp.py: -------------------------------------------------------------------------------- 1 | ## An example of the simple Schnorr sigma protocol 2 | ## to prove that one knows x, such that h = g^x for 3 | ## a public generator h and g. 4 | 5 | from petlib.bn import Bn 6 | from petlib.ec import EcGroup, EcPt 7 | 8 | from hashlib import sha256 9 | 10 | def challenge(elements): 11 | """Packages a challenge in a bijective way""" 12 | elem = [len(elements)] + elements 13 | elem_str = map(str, elem) 14 | elem_len = map(lambda x: "%s||%s" % (len(x) , x), elem_str) 15 | state = "|".join(elem_len) 16 | H = sha256() 17 | H.update(state.encode("utf8")) 18 | return H.digest() 19 | 20 | 21 | def setup(): 22 | G = EcGroup(713) 23 | g = G.generator() 24 | o = G.order() 25 | return G, g, o 26 | 27 | def prove(params, h, g, x, m=""): 28 | """Schnorr proof of the statement ZK(x ; h = g^x)""" 29 | assert x * g == h 30 | G, _, o = params 31 | w = o.random() 32 | W = w * g 33 | 34 | state = ['schnorr', G.nid(), g, h, m, W] 35 | hash_c = challenge(state) 36 | c = Bn.from_binary(hash_c) % o 37 | r = (w - c * x) % o 38 | return (c, r) 39 | 40 | def verify(params, h, g, proof, m=""): 41 | """Verify the statement ZK(x ; h = g^x)""" 42 | G, _, o = params 43 | c, r = proof 44 | W = (r * g + c * h) 45 | 46 | state = ['schnorr', G.nid(), g, h, m, W] 47 | hash_c = challenge(state) 48 | c2 = Bn.from_binary(hash_c) % o 49 | return c == c2 50 | 51 | 52 | def test_zkp(): 53 | params = setup() 54 | G, g, o = params 55 | x = o.random() 56 | h = x * g 57 | 58 | ## Use it as a Zk proof 59 | proof = prove(params, h, g, x) 60 | assert verify(params, h, g, proof) 61 | assert not verify(params, g, h, proof) 62 | 63 | ## Use it as a signature scheme 64 | proofm = prove(params, h, g, x, m = "Hello World!") 65 | assert verify(params, h, g, proofm, m = "Hello World!") 66 | assert not verify(params, h, g, proofm, m = "Other String") 67 | -------------------------------------------------------------------------------- /pavement.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import os 3 | import re 4 | import fnmatch 5 | 6 | from paver.tasks import task, cmdopts 7 | from paver.easy import sh, needs, pushd 8 | from paver.virtual import * 9 | 10 | 11 | def tell(x): 12 | print() 13 | print(("-"*10)+ str(x) + ("-"*10)) 14 | print() 15 | 16 | @task 17 | def build(quiet=True): 18 | """ Builds the petlib distribution, ready to be uploaded to pypi. """ 19 | tell("Build dist") 20 | sh('python setup.py sdist', capture=quiet) 21 | 22 | @task 23 | def win(quiet=True): 24 | """ Builds the petlib binary distribution for windows. """ 25 | tell("Build windows distribution") 26 | sh('python setup.py build bdist_wininst', capture=quiet) 27 | 28 | @task 29 | def upload(quiet=False): 30 | """ Uploads the latest distribution to pypi. """ 31 | 32 | lib = open(os.path.join("petlib", "__init__.py")).read() 33 | v = re.findall("VERSION.*=.*['\"](.*)['\"]", lib)[0] 34 | 35 | tell("upload dist %s" % v) 36 | sh('git tag -a v%s -m "Distribution versio v%s"' % (v, v)) 37 | sh('python setup.py sdist upload', capture=quiet) 38 | tell('Remember to upload tags using "git push --tags"') 39 | 40 | @task 41 | def lint(quiet=False): 42 | """ Run the python linter on petlib with project specific options (see pylintrc petlib). """ 43 | tell("Run pylint on the library") 44 | sh('pylint --rcfile=pylintrc petlib', capture=quiet) 45 | 46 | 47 | @task 48 | def make_docs(quiet=True): 49 | """ Build the petlib documentation. """ 50 | tell("Making Docs") 51 | with pushd('docs') as old_dir: 52 | sh('make html', capture=quiet) 53 | 54 | @task 55 | def wc(quiet=False): 56 | """ Count the petlib library and example code lines. """ 57 | tell("Counting code lines") 58 | 59 | print("\nLibrary code:") 60 | sh('wc -l petlib/*.py', capture=quiet) 61 | 62 | print("\nExample code:") 63 | sh('wc -l examples/*.py', capture=quiet) 64 | 65 | print("\nAdministration code:") 66 | sh('wc -l pavement.py setup.py docs/conf.py utils/ignoretest.py', capture=quiet) 67 | 68 | -------------------------------------------------------------------------------- /examples/amacs.py: -------------------------------------------------------------------------------- 1 | # This is a reference implementation of the algebraic 2 | # Message Authentication Code construction by Chase, Meiklejohn 3 | # and Zaverucha (see "Algebraic MACs and Keyed-Veriffcation 4 | # Anonymous Credentials", at ACM CCS 2014). 5 | 6 | from petlib.ec import EcGroup 7 | import pytest 8 | 9 | def setup_ggm(nid = 713): 10 | """Generates the parameters for an EC group nid""" 11 | G = EcGroup(nid) 12 | g = G.hash_to_point(b"g") 13 | h = G.hash_to_point(b"h") 14 | o = G.order() 15 | return (G, g, h, o) 16 | 17 | def keyGen_ggm(params, n): 18 | """Secret key setup and parameter setup for issuer""" 19 | (_, _, h, o) = params 20 | sk = [o.random() for _ in range(n+1)] 21 | iparams = [s * h for s in sk[1:]] 22 | return sk, iparams 23 | 24 | def Hx(sk, messages): 25 | """A helper function Hx""" 26 | assert len(messages) == len(sk) - 1 27 | total = sk[0] 28 | for xi, mi in zip(sk[1:], messages): 29 | total = total + (xi * mi) 30 | return total 31 | 32 | def mac_ggm(params, sk, messages): 33 | """Compute the mac on messages""" 34 | _, g, _, o = params 35 | u = o.random() * g 36 | uprime = Hx(sk, messages) * u 37 | return (u, uprime) 38 | 39 | def verify_ggm(params, sk, messages, sig): 40 | """Verify the mac on messages""" 41 | u, uprime = sig 42 | G, _, _, _ = params 43 | 44 | if u == G.infinite(): 45 | raise Exception("Invalid MAC: u point at infinity.") 46 | 47 | if uprime == Hx(sk, messages) * u: 48 | return True 49 | return False 50 | 51 | def rerandomize_sig_ggm(params, sig): 52 | _, _, _, o = params 53 | u, up = sig 54 | r = o.random() 55 | return r * u, r * up 56 | 57 | def test_mac(): 58 | """Test basic GGM amac""" 59 | params = setup_ggm() 60 | sk, iparams = keyGen_ggm(params, 2) 61 | sig = mac_ggm(params, sk, [10, 20]) 62 | assert verify_ggm(params, sk, [10, 20], sig) 63 | assert not verify_ggm(params, sk, [10, 30], sig) 64 | 65 | sig2 = rerandomize_sig_ggm(params, sig) 66 | assert verify_ggm(params, sk, [10, 20], sig2) 67 | -------------------------------------------------------------------------------- /examples/pouf.py: -------------------------------------------------------------------------------- 1 | ## An implementation of a Parallel Oblivious Unpredictable Function (POUF) 2 | # Stanislaw Jarecki, Xiaomin Liu: Fast Secure Computation of Set Intersection. 3 | # Published in SCN 2010: 418-435 4 | 5 | from petlib.ec import EcGroup 6 | import pytest 7 | 8 | def POUF_setup(nid=713): 9 | """Parameters for the group""" 10 | G = EcGroup(nid) 11 | g = G.generator() 12 | o = G.order() 13 | return (G, g, o) 14 | 15 | def POUF_keyGen(params): 16 | """Generate the secret key k""" 17 | G, g, o = params 18 | return o.random() 19 | 20 | def POUF_blind(params, messages): 21 | """Blind the messages input to the POUF""" 22 | G, g, o = params 23 | hi = [G.hash_to_point(m) for m in messages] 24 | ai = [o.random() for _ in messages] 25 | yi = [a * h for a,h in zip(ai, hi)] 26 | return ai, yi 27 | 28 | def POUF_mac(params, k, yi): 29 | """ Apply the unpredctable function to the messages """ 30 | return [k * y for y in yi] 31 | 32 | def POUF_unblind(params, ai, zi): 33 | """ Unblind the messages to recover the raw outputs of the POUF """ 34 | G, g, o = params 35 | xi = [a.mod_inverse(o) * z for a,z in zip(ai, zi)] 36 | return xi 37 | 38 | 39 | ### ----------- TESTS --------------- 40 | 41 | def test_setup(): 42 | params = POUF_setup() 43 | k = POUF_keyGen(params) 44 | assert k 45 | 46 | def test_blind(): 47 | params = POUF_setup() 48 | ai, yi = POUF_blind(params, [b"1", b"2"]) 49 | assert len(ai) == 2 50 | assert len(yi) == 2 51 | 52 | def test_mac(): 53 | params = POUF_setup() 54 | k = POUF_keyGen(params) 55 | 56 | ai, yi = POUF_blind(params, [b"1", b"2"]) 57 | zi = POUF_mac(params, k, yi) 58 | assert len(zi) == len(ai) 59 | 60 | def test_unblind(): 61 | params = POUF_setup() 62 | G, g, o = params 63 | k = POUF_keyGen(params) 64 | 65 | ai, yi = POUF_blind(params, [b"1", b"2"]) 66 | zi = POUF_mac(params, k, yi) 67 | 68 | # Make sure unblinding works 69 | fi = POUF_unblind(params, ai, zi) 70 | 71 | hi = [G.hash_to_point(m) for m in [b"1", b"2"]] 72 | assert fi == [k * h for h in hi] -------------------------------------------------------------------------------- /examples/AHEG.py: -------------------------------------------------------------------------------- 1 | ## An implementation of an additivelly homomorphic 2 | ## ECC El-Gamal scheme, used in Privex. 3 | 4 | from petlib.ec import EcGroup 5 | import pytest 6 | 7 | def params_gen(nid=713): 8 | """Generates the AHEG for an EC group nid""" 9 | G = EcGroup(nid) 10 | g = G.generator() 11 | o = G.order() 12 | return (G, g, o) 13 | 14 | def key_gen(params): 15 | """Generates a fresh key pair""" 16 | _, g, o = params 17 | priv = o.random() 18 | pub = priv * g 19 | return (pub, priv) 20 | 21 | def enc(params, pub, counter): 22 | """Encrypts the values of a small counter""" 23 | assert -2**8 < counter < 2**8 24 | G, g, o = params 25 | 26 | k = o.random() 27 | a = k * g 28 | b = k * pub + counter * g 29 | return (a, b) 30 | 31 | def enc_side(params, pub, counter): 32 | """Encrypts the values of a small counter""" 33 | assert -2**8 < counter < 2**8 34 | G, g, o = params 35 | 36 | k = o.random() 37 | a = k * g 38 | b = k * pub + counter * g 39 | return (a, b, k) 40 | 41 | 42 | def add(c1, c2): 43 | """Add two encrypted counters""" 44 | a1, b1 = c1 45 | a2, b2 = c2 46 | return (a1 + a2, b1 + b2) 47 | 48 | def mul(c1, val): 49 | """Multiplies an encrypted counter by a public value""" 50 | a1, b1 = c1 51 | return (val*a1, val*b1) 52 | 53 | def randomize(params, pub, c1): 54 | """Rerandomize an encrypted counter""" 55 | zero = enc(params, pub, 0) 56 | return add(c1, zero) 57 | 58 | def make_table(params): 59 | """Make a decryption table""" 60 | _, g, o = params 61 | table = {} 62 | for i in range(-1000, 1000): 63 | table[i * g] = i 64 | return table 65 | 66 | def dec(params, table, priv, c1): 67 | """Decrypt an encrypted counter""" 68 | _, g, o = params 69 | a, b = c1 70 | plain = b + (-priv * a) 71 | return table[plain] 72 | 73 | def test_AHEG(): 74 | params = params_gen() 75 | (pub, priv) = key_gen(params) 76 | table = make_table(params) 77 | 78 | # Check encryption and decryption 79 | one = enc(params, pub, 1) 80 | assert dec(params, table, priv, one) == 1 81 | 82 | # Check addition 83 | tmp = add(one, one) 84 | two = randomize(params, pub, tmp) 85 | assert dec(params, table, priv, two) == 2 86 | 87 | # Check multiplication 88 | tmp1 = mul(two, 2) 89 | four = randomize(params, pub, tmp1) 90 | assert dec(params, table, priv, four) == 4 91 | -------------------------------------------------------------------------------- /contrib/CountSketch.py: -------------------------------------------------------------------------------- 1 | from array import array 2 | from struct import pack 3 | from hashlib import sha512 4 | from random import gauss 5 | 6 | ## Use the frequency Count sketch for Tor metrics 7 | ## See section 1.3.2 of http://people.cs.umass.edu/~mcgregor/711S12/sketches1.pdf 8 | 9 | def hashes(item, d): 10 | codes = [] 11 | i = 0 12 | while len(codes) < d: 13 | codes += list(array('I', sha512(pack("I", i) + item.encode("utf8")).digest())) 14 | i += 1 15 | return codes[:d] 16 | 17 | class CountSketch(object): 18 | def __init__(self, w, d): 19 | assert isinstance(w, int) and w > 0 20 | assert isinstance(d, int) and d > 0 21 | 22 | self.d, self.w = d, w 23 | self.store = [ [0] * w for _ in range(d) ] 24 | 25 | def insert(self, item): 26 | h = hashes(item, self.d) 27 | for di in range(self.d): 28 | self.store[di][h[di] % self.w] += 1 29 | 30 | def estimate(self, item): 31 | h = hashes(item, self.d) 32 | h2 = [] 33 | for hi in h: 34 | v = hi - 1 if hi % 2 == 0 else hi + 1 35 | h2 += [v] 36 | estimates = 0 37 | for i, [hi, hpi] in enumerate(zip(h, h2)): 38 | v1 = self.store[i][hi % self.w] 39 | v2 = self.store[i][hpi % self.w] 40 | estimates += v1 - v2 41 | return float(estimates) / self.d 42 | 43 | def test_hash(): 44 | print(len(hashes("hello", 20))) 45 | assert len(hashes("hello", 20)) == 20 46 | 47 | def test_inserter(): 48 | cs = CountSketch(50, 7) 49 | for x in range(100): 50 | assert sum(cs.store[0]) == x 51 | cs.insert("%s" % x) 52 | 53 | def test_estimate(): 54 | cs = CountSketch(50, 5) 55 | for x in range(100): 56 | cs.insert("%s" % x) 57 | 58 | e = 0 59 | for x in range(100): 60 | e += cs.estimate("%s" % x) 61 | assert round(e / 100) == 1.0 62 | 63 | e = 0 64 | for x in range(100): 65 | e += (cs.estimate("xx%s" % x)) 66 | assert round(e / 100) == 0.0 67 | 68 | def test_median(): 69 | vals = [gauss(300, 25) for _ in range (100)] 70 | vals += [gauss(300, 200) for _ in range (10)] 71 | vals = sorted([int(v) for v in vals]) 72 | 73 | ## Compute the mediam on clear data 74 | median = vals[int(len(vals) / 2)] 75 | print(median) 76 | 77 | ## Build a CountSketch for the job 78 | cs = CountSketch(50, 5) 79 | for x in vals: 80 | cs.insert("%s" % x) 81 | 82 | vals2 = [] 83 | for x in range(1000): 84 | vals2 += [(x, cs.estimate("%s" % x))] 85 | 86 | # Median estimation using the count sketch 87 | total = sum([vx for _, vx in vals2]) 88 | t = 0 89 | i = 0 90 | while t < total / 2: 91 | t += vals2[i][1] 92 | res = vals2[i][0] 93 | i += 1 94 | print(res) 95 | 96 | # assert abs(res - median) < 30 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /petlib/bindings.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import platform 5 | import cffi 6 | import sys 7 | 8 | try: 9 | from ._petlib import ffi, lib 10 | _FFI = ffi 11 | _C = lib 12 | except BaseException: 13 | print("Support not loading the library to build docs without compiling.") 14 | _C = None 15 | _FFI = None 16 | 17 | 18 | from ._compat import get_openssl_version, OpenSSLVersion # pylint: disable=unused-import 19 | try: 20 | _OPENSSL_VERSION = get_openssl_version(_C) 21 | except: 22 | _OPENSSL_VERSION = OpenSSLVersion.V1_1 23 | 24 | 25 | # Store constants 26 | class Const: 27 | POINT_CONVERSION_COMPRESSED = 2, 28 | POINT_CONVERSION_UNCOMPRESSED = 4, 29 | POINT_CONVERSION_HYBRID = 6 30 | 31 | 32 | _inited = False 33 | 34 | 35 | def version(): 36 | if _OPENSSL_VERSION == OpenSSLVersion.V1_0: 37 | cstr = _C.SSLeay_version(_C.SSLEAY_VERSION) 38 | else: 39 | cstr = _C.OpenSSL_version(_C.OPENSSL_VERSION) 40 | 41 | return str(_FFI.string(cstr)) 42 | 43 | 44 | def get_errors(): 45 | errors = [] 46 | err = _C.ERR_get_error() 47 | while err != 0: 48 | errors += [err] 49 | err = _C.ERR_get_error() 50 | assert isinstance(errors, list) 51 | return errors 52 | 53 | 54 | class InitCiphers(object): 55 | # pylint: disable=global-statement 56 | 57 | def __init__(self): 58 | global _inited 59 | self.on = False 60 | self._C = _C 61 | if not _inited: 62 | _C.OPENSSL_init() 63 | _C.init_ciphers() 64 | _inited = True 65 | self.on = True 66 | 67 | def __del__(self): 68 | global _inited 69 | if _inited and self.on and self._C: 70 | _inited = False 71 | self._C.cleanup_ciphers() 72 | 73 | 74 | if _C and _FFI: 75 | _ciphers = InitCiphers() 76 | if _OPENSSL_VERSION == OpenSSLVersion.V1_0: 77 | if _C.CRYPTO_get_locking_callback() == _FFI.NULL: 78 | _C.setup_ssl_threads() 79 | 80 | 81 | def test_double_load(): 82 | _c2 = InitCiphers() 83 | del _c2 84 | # Nothing bad should happen 85 | 86 | 87 | def test_version(): 88 | print(version()) 89 | assert version() 90 | 91 | 92 | def test_errors(): 93 | assert get_errors() == [] 94 | 95 | 96 | def test_locks(): 97 | if _OPENSSL_VERSION == OpenSSLVersion.V1_0: 98 | assert _C.CRYPTO_get_locking_callback() != _FFI.NULL 99 | 100 | 101 | def test_multithread(): 102 | import threading 103 | from .ec import EcPt, EcGroup 104 | 105 | G = EcGroup() 106 | g = G.generator() 107 | o = G.order() 108 | g2 = o.random() * g 109 | g2_s = g2.export() 110 | 111 | def worker(): 112 | for _ in range(100): 113 | EcPt.from_binary(g2_s, G) 114 | 115 | threads = [] 116 | for _ in range(100): 117 | t = threading.Thread(target=worker) 118 | threads.append(t) 119 | t.start() 120 | -------------------------------------------------------------------------------- /examples/openIDblind.py: -------------------------------------------------------------------------------- 1 | from amacscreds import cred_setup, cred_CredKeyge, cred_UserKeyge, cred_secret_issue_user, cred_secret_issue, cred_secret_issue_user_decrypt, cred_show, cred_show_check, cred_secret_issue_user_check 2 | from genzkp import * 3 | from petlib.pack import encode, decode 4 | 5 | def test_openID_blind(): 6 | ## Setup from credential issuer. 7 | params = cred_setup() 8 | (G, g, h, o) = params 9 | data = encode(params) 10 | params = decode(data) 11 | 12 | ## Derive a fresh, long term secret for user 13 | LT_user_ID = o.random() 14 | timeout = 100 15 | 16 | ## Attriutes we want to encode 17 | public_attr = [ timeout ] 18 | private_attr = [ LT_user_ID ] 19 | n = len(public_attr) + len(private_attr) 20 | 21 | ipub, isec = cred_CredKeyge(params, n) 22 | 23 | ## User generates keys and encrypts some secret attributes 24 | # the secret attributes are [10, 20] 25 | keypair = cred_UserKeyge(params) 26 | keypair = decode(encode(keypair)) 27 | 28 | user_token = cred_secret_issue_user(params, keypair, private_attr) 29 | pub, EGenc, sig_u = decode(encode(user_token)) 30 | 31 | ## The issuer checks the secret attributes and encrypts a amac 32 | # It also includes some public attributes, namely [30, 40]. 33 | assert cred_secret_issue_user_check(params, pub, EGenc, sig_u) 34 | cred_issued = cred_secret_issue(params, pub, EGenc, ipub, isec, public_attr) 35 | u, EncE, sig_s = decode(encode(cred_issued)) 36 | 37 | ## The user decrypts the amac 38 | mac = cred_secret_issue_user_decrypt(params, keypair, u, EncE, ipub, public_attr, EGenc, sig_s) 39 | mac = decode(encode(mac)) 40 | 41 | ## User Shows back full credential to issuer 42 | (creds, sig_o, zis) = cred_show(params, ipub, mac, sig_s, public_attr + private_attr, export_zi=True) 43 | 44 | ## The credential contains a number of commitments to the attributes 45 | (u, Cmis, Cup) = creds 46 | 47 | assert len(Cmis) == 2 48 | assert Cmis[0] == timeout * u + zis[0] * h 49 | assert Cmis[1] == LT_user_ID * u + zis[1] * h 50 | 51 | # Derive a service specific User ID 52 | Gid = G.hash_to_point(b"ServiceNameRP") 53 | Uid = LT_user_ID * Gid 54 | 55 | # Define the statements to be proved 56 | zk = define_proof(G) 57 | 58 | # Define the proof environemnt 59 | env = ZKEnv(zk) 60 | env.u, env.h = u, h 61 | env.Cm0p, env.Cm1 = Cmis[0] - (timeout * u), Cmis[1] 62 | env.Uid, env.Gid = Uid, Gid 63 | env.LT_ID = LT_user_ID 64 | env.z0, env.z1 = zis[0], zis[1] 65 | 66 | sig_openID = zk.build_proof(env.get()) 67 | 68 | creds, sig_o, sig_openID = decode(encode( (creds, sig_o, sig_openID) )) 69 | 70 | ## Issuer / IdP checks the credential is valid 71 | assert cred_show_check(params, ipub, isec, creds, sig_o) 72 | 73 | # Execute the verification on the proof 'sig' 74 | env2 = ZKEnv(zk) 75 | env2.u, env2.h = u, h 76 | env2.Cm0p, env2.Cm1 = Cmis[0] - (timeout * u), Cmis[1] 77 | env2.Uid, env2.Gid = Uid, Gid 78 | 79 | assert zk.verify_proof(env2.get(), sig_openID) 80 | 81 | def define_proof(G): 82 | zk = ZKProof(G) 83 | u, h = zk.get(ConstGen, ["u", "h"]) 84 | LT_ID, z0, z1 = zk.get(Sec, ["LT_ID", "z0", "z1"]) 85 | Cm0p = zk.get(ConstGen, "Cm0p") 86 | Cm1 = zk.get(ConstGen, "Cm1") 87 | Uid = zk.get(ConstGen, "Uid") 88 | Gid = zk.get(ConstGen, "Gid") 89 | 90 | zk.add_proof(Cm0p, z0 * h) 91 | zk.add_proof(Cm1, LT_ID*u + z1 * h) 92 | zk.add_proof(Uid, LT_ID * Gid) 93 | 94 | return zk 95 | 96 | -------------------------------------------------------------------------------- /petlib/compile.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import platform 5 | import cffi 6 | 7 | 8 | CURRENT_PATH = os.path.abspath(os.path.dirname(__file__)) 9 | OPENSSL_BINDINGS_PATH = os.path.join(CURRENT_PATH, '_cffi_src/openssl') 10 | COMPAT_FILE_PATH = os.path.join(CURRENT_PATH, '_compat.py') 11 | 12 | 13 | # Load the OpenSSL version utility. 14 | with open(COMPAT_FILE_PATH) as compat_file: 15 | exec(compat_file.read()) # pylint: disable=exec-used 16 | 17 | 18 | if platform.system() == "Windows": 19 | # Windows building instructions: 20 | # * Ensure you compile with a 64bit lib and toolchain 21 | # (run vcvarsx86_amd64.bat) 22 | # * Ensure the OpenSSL 64 bit lib is on the path. 23 | # (PATH=C:\OpenSSL-Win64\bin;%PATH%) 24 | libraries = ["libeay32"] 25 | include_dirs = [r"."] 26 | extra_compile_args = [] 27 | 28 | # if "VCINSTALLDIR" not in os.environ: 29 | # raise Exception(r"Cannot find the Visual Studio %VCINSTALLDIR% variable. Ensure you ran the appropriate vcvars.bat script.") 30 | 31 | # if "OPENSSL_CONF" not in os.environ: 32 | # raise Exception(r"Cannot find the Visual Studio %OPENSSL_CONF% variable. Ensure you install OpenSSL for Windows.") 33 | 34 | openssl_conf = os.environ["OPENSSL_CONF"] 35 | openssl_bin, conf_name = os.path.split(openssl_conf) 36 | openssl_base, bin_name = os.path.split(openssl_bin) 37 | assert bin_name == "bin" 38 | include_dirs += [os.path.join(openssl_base, "include")] 39 | library_dirs = [ 40 | openssl_base, os.path.join( 41 | openssl_base, "lib"), os.path.join( 42 | openssl_base, "bin")] 43 | link_args = [] 44 | 45 | else: 46 | # Asume we are running on a posix system 47 | # LINUX: libraries=["crypto"], 48 | # extra_compile_args=['-Wno-deprecated-declarations'] 49 | link_args = [] 50 | libraries = ["crypto"] 51 | extra_compile_args = ['-Wno-deprecated-declarations'] 52 | if platform.system() == "Darwin": 53 | include_dirs = ['/usr/local/opt/openssl@1.1/include', 54 | '/usr/local/opt/openssl/include', 55 | '/usr/local/ssl/include'] 56 | library_dirs = ['/usr/local/opt/openssl@1.1/lib', 57 | '/usr/local/opt/openssl/lib', 58 | '/usr/local/ssl/lib'] 59 | 60 | # Ensure that our dynamic ABI-based version finding code in 61 | # _compat.get_openssl_version also tries to load the 62 | # brew-installed openssl libraries first. 63 | os.environ["DYLD_LIBRARY_PATH"] = str.join(":", library_dirs) 64 | else: 65 | include_dirs = [] 66 | library_dirs = [] 67 | # link_args = ['libcrypto.so'] 68 | 69 | 70 | def get_openssl_bindings(filename): 71 | src_path = os.path.join(OPENSSL_BINDINGS_PATH, filename) 72 | with open(src_path) as src_file: 73 | return src_file.read() 74 | 75 | 76 | openssl_version_code = get_openssl_version(warn=True) # pylint: disable=undefined-variable 77 | openssl_bindings_defs = get_openssl_bindings( 78 | 'openssl_v%s.h' % openssl_version_code) 79 | openssl_bindings_src = get_openssl_bindings( 80 | 'openssl_v%s.c' % openssl_version_code) 81 | 82 | _FFI = cffi.FFI() 83 | _FFI.set_source("petlib._petlib", openssl_bindings_src, 84 | libraries=libraries, 85 | extra_compile_args=extra_compile_args, 86 | include_dirs=include_dirs, 87 | library_dirs=library_dirs, 88 | extra_link_args=link_args) 89 | _FFI.cdef(openssl_bindings_defs) 90 | 91 | 92 | if __name__ == "__main__": 93 | print("Compiling petlib for OpenSSL version %s..." % openssl_version_code) 94 | _FFI.compile() 95 | -------------------------------------------------------------------------------- /petlib/_cffi_src/openssl/openssl_v1_0.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | 14 | #include 15 | 16 | 17 | #define BN_num_bytes(a) ((BN_num_bits(a)+7)/8) 18 | 19 | int bn_num_bytes(BIGNUM * a){ 20 | return BN_num_bytes(a); 21 | } 22 | 23 | int bn_is_odd(BIGNUM * a){ 24 | return BN_is_odd(a); 25 | } 26 | 27 | size_t hmac_ctx_size(void){ 28 | return sizeof(HMAC_CTX); 29 | } 30 | 31 | 32 | extern void ERR_load_crypto_strings(void); 33 | extern void OPENSSL_config(void*); 34 | extern void ERR_free_strings(void); 35 | 36 | void init_ciphers(void){ 37 | 38 | /* Load the human readable error strings for libcrypto */ 39 | ERR_load_crypto_strings(); 40 | 41 | /* Load all digest and cipher algorithms */ 42 | OpenSSL_add_all_algorithms(); 43 | 44 | /* Load config file, and other important initialisation */ 45 | OPENSSL_config(NULL); 46 | 47 | } 48 | 49 | void cleanup_ciphers(void){ 50 | 51 | /* Removes all digests and ciphers */ 52 | EVP_cleanup(); 53 | 54 | /* if you omit the next, a small leak may be left when you make use of the BIO (low level API) for e.g. base64 transformations */ 55 | CRYPTO_cleanup_all_ex_data(); 56 | 57 | /* Remove error strings */ 58 | ERR_free_strings(); 59 | 60 | } 61 | 62 | /* This code is derived from the locking code found in the Python _ssl module's 63 | locking callback for OpenSSL. 64 | 65 | Copyright 2001-2016 Python Software Foundation; All Rights Reserved. 66 | 67 | Adapted from: cryptography/master/src/_cffi_src/openssl/callbacks.py 68 | */ 69 | 70 | static unsigned int _ssl_locks_count = 0; 71 | static PyThread_type_lock *_ssl_locks = NULL; 72 | 73 | static void _ssl_thread_locking_function(int mode, int n, const char *file, 74 | int line) { 75 | /* this function is needed to perform locking on shared data 76 | structures. (Note that OpenSSL uses a number of global data 77 | structures that will be implicitly shared whenever multiple 78 | threads use OpenSSL.) Multi-threaded applications will 79 | crash at random if it is not set. 80 | 81 | locking_function() must be able to handle up to 82 | CRYPTO_num_locks() different mutex locks. It sets the n-th 83 | lock if mode & CRYPTO_LOCK, and releases it otherwise. 84 | 85 | file and line are the file number of the function setting the 86 | lock. They can be useful for debugging. 87 | */ 88 | 89 | if ((_ssl_locks == NULL) || 90 | (n < 0) || ((unsigned)n >= _ssl_locks_count)) { 91 | return; 92 | } 93 | 94 | if (mode & CRYPTO_LOCK) { 95 | PyThread_acquire_lock(_ssl_locks[n], 1); 96 | } else { 97 | PyThread_release_lock(_ssl_locks[n]); 98 | } 99 | } 100 | 101 | int setup_ssl_threads(void) { 102 | unsigned int i; 103 | 104 | if (_ssl_locks == NULL) { 105 | _ssl_locks_count = CRYPTO_num_locks(); 106 | _ssl_locks = PyMem_New(PyThread_type_lock, _ssl_locks_count); 107 | if (_ssl_locks == NULL) { 108 | PyErr_NoMemory(); 109 | return 0; 110 | } 111 | memset(_ssl_locks, 0, sizeof(PyThread_type_lock) * _ssl_locks_count); 112 | for (i = 0; i < _ssl_locks_count; i++) { 113 | _ssl_locks[i] = PyThread_allocate_lock(); 114 | if (_ssl_locks[i] == NULL) { 115 | unsigned int j; 116 | for (j = 0; j < i; j++) { 117 | PyThread_free_lock(_ssl_locks[j]); 118 | } 119 | PyMem_Free(_ssl_locks); 120 | return 0; 121 | } 122 | } 123 | CRYPTO_set_locking_callback(_ssl_thread_locking_function); 124 | } 125 | return 1; 126 | } 127 | -------------------------------------------------------------------------------- /petlib/hmac.py: -------------------------------------------------------------------------------- 1 | from .bindings import _FFI, _C, _OPENSSL_VERSION, OpenSSLVersion 2 | 3 | from binascii import hexlify 4 | 5 | import pytest 6 | 7 | 8 | def _check(return_val): 9 | """Checks the return code of the C calls""" 10 | if isinstance(return_val, int) and return_val == 1: 11 | return 12 | if isinstance(return_val, bool) and return_val == True: 13 | return 14 | 15 | raise Exception("HMAC exception") 16 | 17 | 18 | def secure_compare(a1, a2): 19 | """A constant-time comparison function. Returns True if the two strings are equal and False otherwise. 20 | 21 | Args: 22 | a1 (str): the first string 23 | a2 (str): the second string 24 | 25 | Returns: 26 | bool: whether the two stings are equal. 27 | """ 28 | _check(isinstance(a1, type(a2))) 29 | 30 | if len(a1) != len(a2): 31 | return False 32 | 33 | x = _C.CRYPTO_memcmp(a1, a2, len(a1)) 34 | if int(x) == 0: 35 | return True 36 | 37 | return False 38 | 39 | 40 | class Hmac(object): 41 | """Initialize the HMAC by name with a key. 42 | 43 | Args: 44 | name: the name of the hash function to be used. 45 | key: the cryptographic symmetric key of the HMAC. 46 | 47 | Returns: 48 | An HMAC instance, ready to accept data to MAC. 49 | 50 | Example: 51 | 52 | >>> h = Hmac(b"sha512", b"Jefe") 53 | >>> h.update(b"what do ya want ") 54 | >>> h.update(b"for nothing?") 55 | >>> d = h.digest() 56 | >>> len(d) 57 | 64 58 | >>> hexlify(d)[:10] == b"164b7a7bfc" 59 | True 60 | 61 | """ 62 | 63 | def __init__(self, name, key): 64 | self.mac_ctx = None 65 | md = _C.EVP_get_digestbyname(name) 66 | if md == _FFI.NULL: 67 | raise Exception("HMAC Error loading function %s" % name) 68 | 69 | self.outsize = _C.EVP_MD_size(md) 70 | if _OPENSSL_VERSION == OpenSSLVersion.V1_0: 71 | self.mac_ctx = _FFI.new("HMAC_CTX *") 72 | _C.HMAC_CTX_init(self.mac_ctx) 73 | else: 74 | self.mac_ctx = _C.HMAC_CTX_new() 75 | 76 | _check(_C.HMAC_Init_ex(self.mac_ctx, key, len(key), md, _FFI.NULL)) 77 | self.active = True 78 | 79 | def update(self, data): 80 | """Update the HMAC with some data to authenticate. 81 | 82 | Args: 83 | data: the data to add to the MAC. 84 | 85 | Note: you must not call update after you finalize the HMAC. 86 | 87 | Raises: 88 | Exception: if called after the HMAC has been finalized. 89 | """ 90 | if not self.active: 91 | raise Exception("HMAC already finalized!") 92 | _check(_C.HMAC_Update(self.mac_ctx, data, len(data))) 93 | 94 | def digest(self): 95 | """Output the HMAC digest as a binary string. 96 | 97 | Returns: 98 | The digest as a binary data string. 99 | """ 100 | if not self.active: 101 | raise Exception("HMAC already finalized!") 102 | 103 | self.active = False 104 | out_md = _FFI.new("unsigned char[]", self.outsize) 105 | out_len = _FFI.new("unsigned int *") 106 | _check(_C.HMAC_Final(self.mac_ctx, out_md, out_len)) 107 | 108 | if int(out_len[0]) != self.outsize: 109 | raise Exception("HMAC Unexpected length") 110 | 111 | return bytes(_FFI.buffer(out_md)[:]) 112 | 113 | def __del__(self): 114 | if self.mac_ctx is not None and _OPENSSL_VERSION == OpenSSLVersion.V1_1: 115 | _C.HMAC_CTX_free(self.mac_ctx) 116 | 117 | 118 | def test_init(): 119 | h = Hmac(b"md5", b"Hello") 120 | h.update(b"hello") 121 | d = h.digest() 122 | assert d 123 | assert len(d) == 128 / 8 124 | 125 | 126 | def test_vectors(): 127 | """ 128 | Key = 4a656665 ("Jefe") 129 | Data = 7768617420646f2079612077616e7420 ("what do ya want ") 130 | 666f72206e6f7468696e673f ("for nothing?") 131 | 132 | HMAC-SHA-224 = a30e01098bc6dbbf45690f3a7e9e6d0f 133 | 8bbea2a39e6148008fd05e44 134 | HMAC-SHA-256 = 5bdcc146bf60754e6a042426089575c7 135 | 5a003f089d2739839dec58b964ec3843 136 | HMAC-SHA-384 = af45d2e376484031617f78d2b58a6b1b 137 | 9c7ef464f5a01b47e42ec3736322445e 138 | 8e2240ca5e69e2c78b3239ecfab21649 139 | HMAC-SHA-512 = 164b7a7bfcf819e2e395fbe73b56e0a3 140 | 87bd64222e831fd610270cd7ea250554 141 | 9758bf75c05a994a6d034f65f8f0e6fd 142 | caeab1a34d4a6b4b636e070a38bce737 143 | """ 144 | 145 | h = Hmac(b"sha512", b"Jefe") 146 | assert 512 / 8 == h.outsize 147 | h.update(b"what do ya want ") 148 | h.update(b"for nothing?") 149 | d = h.digest() 150 | 151 | with pytest.raises(Exception) as excinfo: 152 | h.update(b"some more") 153 | assert 'finalized' in str(excinfo.value) 154 | 155 | with pytest.raises(Exception) as excinfo: 156 | h.digest() 157 | assert 'finalized' in str(excinfo.value) 158 | 159 | with pytest.raises(Exception) as excinfo: 160 | h = Hmac(b"sha999", b"Jefe") 161 | assert 'Error' in str(excinfo.value) 162 | 163 | ans1 = hexlify(d) 164 | ans2 = b"164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737" 165 | 166 | assert len(ans1) == len(ans2) 167 | assert ans1 == ans2 168 | 169 | 170 | def test_cmp(): 171 | assert secure_compare(b"Hello", b"Hello") 172 | assert not secure_compare(b"Hello", b"Hellx") 173 | assert not secure_compare(b"Hello", b"Hell") 174 | 175 | with pytest.raises(Exception) as excinfo: 176 | assert not secure_compare(b"Hello", 2) 177 | assert 'HMAC' in str(excinfo.value) 178 | -------------------------------------------------------------------------------- /examples/amacscredsext.py: -------------------------------------------------------------------------------- 1 | from amacscreds import * 2 | 3 | 4 | def _internal_ckeck(keypair, u, EncE, secrets, all_attribs): 5 | """ Check the invariant that the ciphertexts are the encrypted attributes """ 6 | 7 | ## First do decryption 8 | priv, pub = keypair 9 | (a, b) = EncE 10 | Cred = b - (priv * a) 11 | 12 | sk, _ = secrets 13 | v = Hx(sk, all_attribs) 14 | assert Cred == v * u 15 | 16 | 17 | def _check_enc(params, keypair, EGenc, attrib): 18 | G, g, h, o = params 19 | priv, pub = keypair 20 | for (a, b, atr) in zip(EGenc[0], EGenc[1], attrib): 21 | assert (b - (priv * a)) == (atr * g) 22 | 23 | 24 | def cred_cert_proof(params, n): 25 | G, _, _, _ = params 26 | 27 | # Contruct the proof 28 | zk = ZKProof(G) 29 | 30 | ## The variables 31 | g, h, u = zk.get(ConstGen, ["g", "h", "u"]) 32 | Cmis = zk.get_array(ConstGen, "Cmi", n) 33 | Cx0, VplusCup = zk.get(ConstGen, ["Cx0","VplusCup"]) 34 | x0, x0_bar = zk.get(Sec, ["x0", "x0_bar"]) 35 | xis = zk.get_array(Sec, "xi", n) 36 | Xis = zk.get_array(ConstGen, "Xi", n) 37 | 38 | ## Proof of knowing the secret of MAC 39 | zk.add_proof(Cx0, x0 * g + x0_bar * h) 40 | 41 | ## Proof of correct Xi's 42 | for (xi, Xi) in zip(xis, Xis): 43 | zk.add_proof(Xi, xi * h) 44 | 45 | # Define the relations to prove 46 | Vp = x0 * u 47 | for xi, Cmi in zip(xis, Cmis): 48 | Vp = Vp + (xi * Cmi) 49 | zk.add_proof(VplusCup, Vp) 50 | 51 | return zk 52 | 53 | 54 | def cred_show_check_cert(params, publics, secrets, creds, sig, cred_show_proof=cred_show_proof, xenv={}): 55 | 56 | # Parse the inputs 57 | G, g, h, _ = params 58 | sk, x0_bar = secrets 59 | Cx0, iparams = publics 60 | (u, Cmis, Cup) = creds 61 | 62 | n = len(iparams) 63 | 64 | ## Recompute a V 65 | V = sk[0] * u + (- Cup) 66 | for xi, Cmi in zip(sk[1:], Cmis): 67 | V = V + xi * Cmi 68 | 69 | # Define the proof, and instanciate it with variables 70 | zk = cred_show_proof(params, n) 71 | 72 | env = ZKEnv(zk) 73 | env.u = u 74 | env.g, env.h = g, h 75 | env.V = V 76 | env.minus1 = -Bn(1) 77 | 78 | env.Xi = iparams 79 | env.Cmi = Cmis 80 | 81 | if xenv: 82 | xenv(env) 83 | 84 | 85 | Correct = zk.verify_proof(env.get(), sig) 86 | 87 | if not Correct: 88 | return False, None 89 | 90 | # Define the cert proof 91 | zkcert = cred_cert_proof(params, n) 92 | env = ZKEnv(zkcert) 93 | env.VplusCup = V + Cup 94 | env.u = u 95 | env.g, env.h = g, h 96 | env.Cmi = Cmis 97 | env.Cx0 = Cx0 98 | env.x0 = sk[0] 99 | env.x0_bar = x0_bar 100 | env.xi = sk[1:] 101 | env.Xi = iparams 102 | 103 | 104 | sig = zkcert.build_proof(env.get()) 105 | if __debug__: 106 | assert zkcert.verify_proof(env.get(), sig, strict=False) 107 | 108 | 109 | # Return the result of the verification 110 | return Correct, (V, sig) 111 | 112 | def public_check_cert(params, publics, cert, creds, sig, cred_show_proof=cred_show_proof, xenv={}): 113 | # Parse the inputs 114 | G, g, h, _ = params 115 | Cx0, iparams = publics 116 | (u, Cmis, Cup) = creds 117 | 118 | n = len(iparams) 119 | 120 | ## Recompute a V 121 | V, sig2 = cert 122 | 123 | # Define the proof, and instanciate it with variables 124 | zk = cred_show_proof(params, n) 125 | 126 | env = ZKEnv(zk) 127 | env.u = u 128 | env.g, env.h = g, h 129 | env.V = V 130 | env.minus1 = -Bn(1) 131 | env.Xi = iparams 132 | env.Cmi = Cmis 133 | 134 | if xenv: 135 | xenv(env) 136 | 137 | Correct = zk.verify_proof(env.get(), sig) 138 | if not Correct: 139 | return False 140 | 141 | # Define the cert proof 142 | zkcert = cred_cert_proof(params, n) 143 | env = ZKEnv(zkcert) 144 | 145 | # zk.VplusCup = ConstGen 146 | env.VplusCup = V + Cup 147 | env.u = u 148 | env.g, env.h = g, h 149 | env.Cmi = Cmis 150 | env.Cx0 = Cx0 151 | env.Xi = iparams 152 | 153 | Correct = zkcert.verify_proof(env.get(), sig2, strict=True) 154 | if not Correct: 155 | return False 156 | 157 | return True 158 | 159 | def test_secret_creds_ext(): 160 | ## Setup from credential issuer. 161 | params = cred_setup() 162 | 163 | ## Attriutes we want to encode 164 | public_attr = [30, 40] 165 | private_attr = [10, 20] 166 | n = len(public_attr) + len(private_attr) 167 | 168 | ipub, isec = cred_CredKeyge(params, n) 169 | 170 | ## User generates keys and encrypts some secret attributes 171 | # the secret attributes are [10, 20] 172 | keypair = cred_UserKeyge(params) 173 | pub, EGenc, sig = cred_secret_issue_user(params, keypair, private_attr) 174 | 175 | if __debug__: 176 | _check_enc(params, keypair, EGenc, private_attr) 177 | 178 | ## The issuer checks the secret attributes and encrypts a amac 179 | # It also includes some public attributes, namely [30, 40]. 180 | assert cred_secret_issue_user_check(params, pub, EGenc, sig) 181 | u, EncE, sig = cred_secret_issue(params, pub, EGenc, ipub, isec, public_attr) 182 | 183 | if __debug__: 184 | _internal_ckeck(keypair, u, EncE, isec, public_attr + private_attr) 185 | 186 | ## The user decrypts the amac 187 | mac = cred_secret_issue_user_decrypt(params, keypair, u, EncE, ipub, public_attr, EGenc, sig) 188 | 189 | ## The show protocol using the decrypted amac 190 | # The proof just proves knowledge of the attributes, but any other 191 | # ZK statement is also possible by augmenting the proof. 192 | (creds, sig) = cred_show(params, ipub, mac, sig, public_attr + private_attr) 193 | res, cert = cred_show_check_cert(params, ipub, isec, creds, sig) 194 | assert res 195 | 196 | ## Check the public certificate that is returned by the verification step 197 | public_check_cert(params, ipub, cert, creds, sig) 198 | -------------------------------------------------------------------------------- /utils/pavement.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import os 3 | import re 4 | import fnmatch 5 | 6 | 7 | def match_files(directory="petlib", pattern="*.py"): 8 | files = [] 9 | for file in os.listdir(directory): 10 | if fnmatch.fnmatch(file, pattern): 11 | files += [os.path.join(directory, file)] 12 | return files 13 | 14 | 15 | from paver.tasks import task, cmdopts 16 | from paver.easy import sh, needs, pushd 17 | from paver.virtual import * 18 | 19 | 20 | def tell(x): 21 | print() 22 | print(("-"*10)+ str(x) + ("-"*10)) 23 | print() 24 | 25 | 26 | @task 27 | def unit_tests(): 28 | """ Run all the unit tests in a Python 2.7 py.test context, and produce coverage report. """ 29 | tell("Unit tests") 30 | files = " ".join(match_files()) 31 | sh('python2 petlib/compile.py') 32 | # sh('py.test-2.7 -v --doctest-modules --cov-report html --cov petlib ' + files) 33 | sh('py.test-2.7 -v --doctest-modules ' + files) 34 | 35 | @task 36 | def generic_unit_tests(): 37 | """ Run all the unit tests in a generic py.test context. """ 38 | tell("Generic Unit tests") 39 | files = " ".join(match_files()) 40 | sh('python petlib/compile.py') 41 | # sh('py.test-2.7 -v --doctest-modules --cov-report html --cov petlib ' + files) 42 | sh('py.test -vs --doctest-modules ' + files) 43 | 44 | 45 | @task 46 | def test3(): 47 | """ Run all the unit tests in a Python 3.4 py.test context, and produce coverage report. """ 48 | tell("Unit tests for python 3") 49 | files = " ".join(match_files()) 50 | sh('python3 petlib/compile.py') 51 | # sh('py.test-3.4 -v --doctest-modules --cov-report html --cov petlib ' + files) 52 | sh('py.test-3.4 -v --doctest-modules ' + files) 53 | 54 | @task 55 | @cmdopts([ 56 | ('file=', 'f', 'File to test.') 57 | ]) 58 | def testf(options): 59 | """Test a specific file for Python 2/3 with all flags turned on.""" 60 | tell("Unit tests for specific file") 61 | print(options) 62 | sh('py.test-3.4 -vs --doctest-modules --cov %s %s' % (options.file, options.file)) 63 | sh('py.test-2.7 -vs --doctest-modules --cov-report html --cov %s %s' % (options.file, options.file)) 64 | 65 | 66 | @task 67 | def build(quiet=True): 68 | """ Builds the petlib distribution, ready to be uploaded to pypi. """ 69 | tell("Build dist") 70 | sh('python setup.py sdist', capture=quiet) 71 | 72 | @task 73 | def win(quiet=True): 74 | """ Builds the petlib binary distribution for windows. """ 75 | tell("Build windows distribution") 76 | sh('python setup.py build bdist_wininst', capture=quiet) 77 | 78 | @task 79 | def upload(quiet=False): 80 | """ Uploads the latest distribution to pypi. """ 81 | 82 | lib = file(os.path.join("petlib", "__init__.py")).read() 83 | v = re.findall("VERSION.*=.*['\"](.*)['\"]", lib)[0] 84 | 85 | tell("upload dist %s" % v) 86 | sh('git tag -a v%s -m "Distribution versio v%s"' % (v, v)) 87 | sh('python setup.py sdist upload', capture=quiet) 88 | tell('Remember to upload tags using "git push --tags"') 89 | 90 | @task 91 | def lintlib(quiet=False): 92 | """ Run the python linter on petlib with project specific options (see pylintrc petlib). """ 93 | tell("Run pylint on the library") 94 | sh('pylint --rcfile=pylintrc petlib', capture=quiet) 95 | 96 | @needs("lintlib", "lintexamples") 97 | @task 98 | def lint(): 99 | """ Lint all petlib library code and examples. """ 100 | pass 101 | 102 | @task 103 | def make_docs(quiet=True): 104 | """ Build the petlib documentation. """ 105 | tell("Making Docs") 106 | with pushd('docs') as old_dir: 107 | sh('make html', capture=quiet) 108 | 109 | @task 110 | def wc(quiet=False): 111 | """ Count the petlib library and example code lines. """ 112 | tell("Counting code lines") 113 | 114 | print("\nLibrary code:") 115 | sh('wc -l petlib/*.py', capture=quiet) 116 | 117 | print("\nExample code:") 118 | sh('wc -l examples/*.py', capture=quiet) 119 | 120 | print("\nAdministration code:") 121 | sh('wc -l pavement.py setup.py docs/conf.py utils/ignoretest.py', capture=quiet) 122 | 123 | 124 | def get_latest_dist(): 125 | lib = file(os.path.join("petlib", "__init__.py")).read() 126 | v = re.findall("VERSION.*=.*['\"](.*)['\"]", lib)[0] 127 | return os.path.join("dist","petlib-%s.tar.gz" % v) 128 | 129 | 130 | @needs('build') 131 | @task 132 | def make_env(quiet=True): 133 | """ Build a virtual environment with petlib installed. """ 134 | tell("Make a virtualenv") 135 | if os.path.exists("test_env"): 136 | return 137 | os.mkdir("test_env") 138 | with pushd('test_env') as old_dir: 139 | sh("virtualenv pltest", capture=quiet) 140 | 141 | 142 | @needs("make_env") 143 | @task 144 | @virtualenv(dir=r"test_env/pltest") 145 | def lintexamples(quiet=True): 146 | """ Run the python linter on the petlib examples. """ 147 | tell("Run Lint on example code") 148 | sh("pip install %s --upgrade" % get_latest_dist(), capture=quiet) 149 | files = " ".join(match_files("examples", "*.py")) 150 | sh('export PYTHONPATH=$PYHTONPATH:./utils; pylint --rcfile=pylintrc --load-plugins ignoretest ' + files, capture=quiet) 151 | 152 | 153 | @task 154 | @virtualenv(dir=r"test_env/pltest") 155 | def venv_unit_tests(quiet=False): 156 | """ Run all the unit tests in a Python 2.7 venv py.test context, and produce coverage report. """ 157 | tell("venv Unit tests") 158 | files = " ".join(match_files()) 159 | # sh('py.test-2.7 -v --doctest-modules --cov-report html --cov petlib ' + files) 160 | # sh('py.test-2.7 -v --doctest-modules ' + files) 161 | sh("pip install %s --upgrade" % get_latest_dist(), capture=quiet) 162 | sh("paver unit_tests") 163 | 164 | 165 | @needs("build", "make_env", "venv_unit_tests") 166 | @task 167 | def venvut(quiet=False): 168 | pass 169 | # sh("rm -rf test_env") 170 | 171 | 172 | @task 173 | @virtualenv(dir=os.path.join(r"test_env",r"pltest")) 174 | def big_tests(quiet=False): 175 | """ Run all example unit_tests in a fresh python 2.7 context. """ 176 | tell("Run acceptance tests (big examples)") 177 | # sh("pip install -r requirements.txt", capture=quiet) 178 | sh("pip install %s --upgrade" % get_latest_dist(), capture=quiet) 179 | files = " ".join(match_files("examples", "*.py")) 180 | sh("py.test-2.7 -v " + files) 181 | 182 | @needs("build", "make_env", "big_tests") 183 | @task 184 | def venvbt(quiet=False): 185 | pass 186 | 187 | 188 | @task 189 | def local_big_tests(quiet=True): 190 | """ Run example tests using local install. """ 191 | tell("Run acceptance tests (big examples) using local install.") 192 | files = " ".join(match_files("examples", "*.py")) 193 | sh("py.test-2.7 -v " + files) 194 | 195 | 196 | @needs('unit_tests', 'big_tests') 197 | @task 198 | def test(): 199 | """ Run all tests. """ 200 | pass 201 | 202 | 203 | @needs('unit_tests', 'test3', 'build', 'make_docs', 'make_env', 'big_tests') 204 | @task 205 | def default(): 206 | """ Run all default tasks to test, and build lib and docs. """ 207 | pass -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 21 | 22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 23 | 24 | help: 25 | @echo "Please use \`make ' where is one of" 26 | @echo " html to make standalone HTML files" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 36 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 38 | @echo " text to make text files" 39 | @echo " man to make manual pages" 40 | @echo " texinfo to make Texinfo files" 41 | @echo " info to make Texinfo files and run them through makeinfo" 42 | @echo " gettext to make PO message catalogs" 43 | @echo " changes to make an overview of all changed/added/deprecated items" 44 | @echo " xml to make Docutils-native XML files" 45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 46 | @echo " linkcheck to check all external links for integrity" 47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 48 | 49 | clean: 50 | rm -rf $(BUILDDIR)/* 51 | 52 | html: 53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 54 | @echo 55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 56 | 57 | dirhtml: 58 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 59 | @echo 60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 61 | 62 | singlehtml: 63 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 64 | @echo 65 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 66 | 67 | pickle: 68 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 69 | @echo 70 | @echo "Build finished; now you can process the pickle files." 71 | 72 | json: 73 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 74 | @echo 75 | @echo "Build finished; now you can process the JSON files." 76 | 77 | htmlhelp: 78 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 79 | @echo 80 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 81 | ".hhp project file in $(BUILDDIR)/htmlhelp." 82 | 83 | qthelp: 84 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 85 | @echo 86 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 87 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 88 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/petlib.qhcp" 89 | @echo "To view the help file:" 90 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/petlib.qhc" 91 | 92 | devhelp: 93 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 94 | @echo 95 | @echo "Build finished." 96 | @echo "To view the help file:" 97 | @echo "# mkdir -p $$HOME/.local/share/devhelp/petlib" 98 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/petlib" 99 | @echo "# devhelp" 100 | 101 | epub: 102 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 103 | @echo 104 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 105 | 106 | latex: 107 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 108 | @echo 109 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 110 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 111 | "(use \`make latexpdf' here to do that automatically)." 112 | 113 | latexpdf: 114 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 115 | @echo "Running LaTeX files through pdflatex..." 116 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 117 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 118 | 119 | latexpdfja: 120 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 121 | @echo "Running LaTeX files through platex and dvipdfmx..." 122 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 123 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 124 | 125 | text: 126 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 127 | @echo 128 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 129 | 130 | man: 131 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 132 | @echo 133 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 134 | 135 | texinfo: 136 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 137 | @echo 138 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 139 | @echo "Run \`make' in that directory to run these through makeinfo" \ 140 | "(use \`make info' here to do that automatically)." 141 | 142 | info: 143 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 144 | @echo "Running Texinfo files through makeinfo..." 145 | make -C $(BUILDDIR)/texinfo info 146 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 147 | 148 | gettext: 149 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 150 | @echo 151 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 152 | 153 | changes: 154 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 155 | @echo 156 | @echo "The overview file is in $(BUILDDIR)/changes." 157 | 158 | linkcheck: 159 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 160 | @echo 161 | @echo "Link check complete; look for any errors in the above output " \ 162 | "or in $(BUILDDIR)/linkcheck/output.txt." 163 | 164 | doctest: 165 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 166 | @echo "Testing of doctests in the sources finished, look at the " \ 167 | "results in $(BUILDDIR)/doctest/output.txt." 168 | 169 | xml: 170 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 171 | @echo 172 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 173 | 174 | pseudoxml: 175 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 176 | @echo 177 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 178 | -------------------------------------------------------------------------------- /petlib/ecdsa.py: -------------------------------------------------------------------------------- 1 | """ A library providing signature and verification functions for the ECDSA scheme. 2 | 3 | Example: 4 | How to use ``do_ecdsa_sign`` and ``do_ecdsa_verify`` to sign and verify a string: 5 | 6 | >>> from hashlib import sha1 7 | >>> # Generate a signature / verification key pair. 8 | >>> G = EcGroup() 9 | >>> sig_key = G.order().random() 10 | >>> ver_key = sig_key * G.generator() 11 | >>> # Hash the (potentially long) message into a short digest. 12 | >>> digest = sha1(b"Hello World!").digest() 13 | >>> # Sign and verify signature 14 | >>> sig = do_ecdsa_sign(G, sig_key, digest) 15 | >>> do_ecdsa_verify(G, ver_key, sig, digest) 16 | True 17 | 18 | Fast signatures can be constructed using ``do_ecdsa_setup``: 19 | 20 | >>> from hashlib import sha1 21 | >>> # Generate a signature / verification key pair. 22 | >>> G = EcGroup() 23 | >>> sig_key = G.order().random() 24 | >>> ver_key = sig_key * G.generator() 25 | >>> # Hash the (potentially long) message into a short digest. 26 | >>> digest = sha1(b"Hello World!").digest() 27 | >>> # Sign and verify signature 28 | >>> kinv_rp = do_ecdsa_setup(G, sig_key) 29 | >>> sig = do_ecdsa_sign(G, sig_key, digest, kinv_rp = kinv_rp) 30 | >>> do_ecdsa_verify(G, ver_key, sig, digest) 31 | True 32 | 33 | """ 34 | 35 | 36 | from .bindings import _FFI, _C, _OPENSSL_VERSION, OpenSSLVersion 37 | 38 | from .ec import EcGroup, _check 39 | from .bn import Bn, get_ctx 40 | 41 | 42 | def do_ecdsa_setup(G, priv): 43 | """Compute the parameters kinv and rp to (optionally) speed up ECDSA signing.""" 44 | 45 | ec_key = _C.EC_KEY_new() 46 | _check(_C.EC_KEY_set_group(ec_key, G.ecg)) 47 | _check(_C.EC_KEY_set_private_key(ec_key, priv.bn)) 48 | 49 | ptr_kinv = _FFI.new("BIGNUM **") 50 | ptr_rp = _FFI.new("BIGNUM **") 51 | 52 | _check(_C.ECDSA_sign_setup(ec_key, get_ctx().bnctx, ptr_kinv, ptr_rp)) 53 | 54 | kinv = Bn() 55 | _C.BN_copy(kinv.bn, ptr_kinv[0]) 56 | _C.BN_clear_free(ptr_kinv[0]) 57 | 58 | rp = Bn() 59 | _C.BN_copy(rp.bn, ptr_rp[0]) 60 | _C.BN_clear_free(ptr_rp[0]) 61 | 62 | return kinv, rp 63 | 64 | 65 | def do_ecdsa_sign(G, priv, data, kinv_rp=None): 66 | """A quick function to ECDSA sign a hash. 67 | 68 | Args: 69 | G (EcGroup): the group in which math is done. 70 | priv (Bn): the secret key. 71 | data (str): the string to sign. 72 | kinv_rp (opaque): optional setup parameters. 73 | 74 | Returns: 75 | Bn, Bn: The (r, s) signature 76 | 77 | """ 78 | ec_key = _C.EC_KEY_new() 79 | _check(_C.EC_KEY_set_group(ec_key, G.ecg)) 80 | _check(_C.EC_KEY_set_private_key(ec_key, priv.bn)) 81 | # _check( _C.EC_KEY_precompute_mult(ec_key, _FFI.NULL) ) 82 | 83 | if kinv_rp is None: 84 | ecdsa_sig = _C.ECDSA_do_sign(data, len(data), ec_key) 85 | 86 | else: 87 | kinv, rp = kinv_rp 88 | ecdsa_sig = _C.ECDSA_do_sign_ex( 89 | data, len(data), kinv.bn, rp.bn, ec_key) 90 | 91 | r = Bn() 92 | s = Bn() 93 | 94 | if _OPENSSL_VERSION == OpenSSLVersion.V1_0: 95 | _C.BN_copy(r.bn, ecdsa_sig.r) 96 | _C.BN_copy(s.bn, ecdsa_sig.s) 97 | else: 98 | rptr = _FFI.new("BIGNUM **") 99 | sptr = _FFI.new("BIGNUM **") 100 | 101 | _C.ECDSA_SIG_get0(ecdsa_sig, rptr, sptr) 102 | _C.BN_copy(r.bn, rptr[0]) 103 | _C.BN_copy(s.bn, sptr[0]) 104 | 105 | _C.ECDSA_SIG_free(ecdsa_sig) 106 | _C.EC_KEY_free(ec_key) 107 | 108 | return (r, s) 109 | 110 | 111 | def do_ecdsa_verify(G, pub, sig, data): 112 | """A quick function to ECDSA verify a hash. 113 | 114 | Args: 115 | G (EcGroup): the group in which math is done. 116 | pub (EcPt): the secret key 117 | sig (Bn, Bn): the (r,s) signature 118 | data (str): the string to sign 119 | 120 | Returns: 121 | bool: A Boolean indicating whether the signature verifies. 122 | """ 123 | 124 | r, s = sig 125 | 126 | ec_key = _C.EC_KEY_new() 127 | _check(_C.EC_KEY_set_group(ec_key, G.ecg)) 128 | _check(_C.EC_KEY_set_public_key(ec_key, pub.pt)) 129 | _check(_C.EC_KEY_precompute_mult(ec_key, get_ctx().bnctx)) 130 | 131 | ec_sig = _C.ECDSA_SIG_new() 132 | 133 | if _OPENSSL_VERSION == OpenSSLVersion.V1_0: 134 | _C.BN_copy(ec_sig.r, r.bn) 135 | _C.BN_copy(ec_sig.s, s.bn) 136 | else: 137 | _check(_C.ECDSA_SIG_set0_petlib(ec_sig, r.bn, s.bn)) 138 | 139 | try: 140 | result = int(_C.ECDSA_do_verify(data, len(data), ec_sig, ec_key)) 141 | if result == -1: 142 | raise Exception("ECDSA Error") 143 | 144 | finally: 145 | _C.EC_KEY_free(ec_key) 146 | _C.ECDSA_SIG_free(ec_sig) 147 | 148 | return bool(result) 149 | 150 | 151 | def get_ecdsa_keys(G, sig, data): 152 | """ Returns the two possible public keys corresponding to this signature. 153 | 154 | Args: 155 | G (EcGroup): the group in which math is done. 156 | data (str): the string to sign 157 | sign (Bn, Bn): the (r,s) signature 158 | 159 | Returns: 160 | pub1, pub2: The two candidate public keys 161 | 162 | """ 163 | 164 | (r, s) = sig 165 | g = G.generator() 166 | R1, R2 = G.get_points_from_x(r) 167 | 168 | r_inv = r.mod_inverse(G.order()) 169 | z = Bn.from_binary(data) 170 | 171 | if not (0 < z < G.order()): 172 | raise Exception("Digest too long") 173 | 174 | K1 = r_inv * (s * R1 - z * g) 175 | K2 = r_inv * (s * R2 - z * g) 176 | 177 | return K1, K2 178 | 179 | 180 | def test_ecdsa(): 181 | G = EcGroup() 182 | priv = G.order().random() 183 | g = G.generator() 184 | sig = do_ecdsa_sign(G, priv, b"Hello") 185 | assert do_ecdsa_verify(G, priv * g, sig, b"Hello") 186 | 187 | 188 | def test_get_ecdsa_keys(): 189 | G = EcGroup() 190 | g = G.generator() 191 | 192 | priv = G.order().random() 193 | pub = priv * g 194 | 195 | sig = do_ecdsa_sign(G, priv, b"Hello") 196 | assert do_ecdsa_verify(G, priv * g, sig, b"Hello") 197 | 198 | pk1, pk2 = get_ecdsa_keys(G, sig, b"Hello") 199 | assert pk1 == pub or pk2 == pub 200 | 201 | 202 | def test_ecdsa_fail(): 203 | G = EcGroup() 204 | priv = G.order().random() 205 | g = G.generator() 206 | sig = do_ecdsa_sign(G, priv, b"Hello") 207 | 208 | assert not do_ecdsa_verify(G, priv * g, sig, b"Hellx") 209 | 210 | 211 | def test_ecdsa_timing(): 212 | repreats = 1000 213 | 214 | from hashlib import sha1 215 | import time 216 | 217 | G = EcGroup() 218 | sig_key = G.order().random() 219 | ver_key = sig_key * G.generator() 220 | 221 | digest = sha1(b"Hello World!").digest() 222 | 223 | print 224 | t = [] 225 | 226 | x = "Sign. plain" 227 | t0 = time.clock() 228 | for y in range(repreats): 229 | sig = do_ecdsa_sign(G, sig_key, digest) 230 | t += [time.clock() - t0] 231 | 232 | print("%s:\t%2.2f/sec" % (x, 1.0 / (float(t[-1]) / repreats))) 233 | 234 | x = "Sign. with setup" 235 | t0 = time.clock() 236 | kinv_rp = do_ecdsa_setup(G, sig_key) 237 | for y in range(repreats): 238 | sig = do_ecdsa_sign(G, sig_key, digest, kinv_rp=kinv_rp) 239 | t += [time.clock() - t0] 240 | 241 | print("%s:\t%2.2f/sec" % (x, 1.0 / (float(t[-1]) / repreats))) 242 | 243 | x = "Verification" 244 | t0 = time.clock() 245 | kinv_rp = do_ecdsa_setup(G, sig_key) 246 | for y in range(repreats): 247 | do_ecdsa_verify(G, ver_key, sig, digest) 248 | t += [time.clock() - t0] 249 | 250 | print("%s:\t%2.2f/sec" % (x, 1.0 / (float(t[-1]) / repreats))) 251 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. petlib documentation master file, created by 2 | sphinx-quickstart on Sun Nov 23 00:41:16 2014. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to petlib's documentation! 7 | ================================== 8 | 9 | .. image:: https://travis-ci.org/gdanezis/petlib.svg?branch=master 10 | :target: https://travis-ci.org/gdanezis/petlib 11 | 12 | .. image:: https://readthedocs.org/projects/petlib/badge/?version=latest 13 | :target: https://readthedocs.org/projects/petlib/?badge=latest 14 | :alt: Documentation Status 15 | 16 | .. include:: ../README.rst 17 | :start-after: docs-include-marker-begin-do-not-remove 18 | :end-before: docs-include-marker-end-do-not-remove 19 | 20 | Testing and Packaging 21 | --------------------- 22 | 23 | You will need a working Python 2.7 and 3.6 environemnt with pytest:: 24 | 25 | sudo apt-get install python-pytest 26 | sudo apt-get install python3-pytest 27 | sudo apt-get install python-sphinx 28 | sudo pip install Mock 29 | 30 | To build the distribution, create a venv for tests and run all tests (including the examples):: 31 | 32 | paver 33 | 34 | Specific paver targets include: 35 | * ``unit_tests``: runs the unit tests. 36 | * ``build``: builds a distribution bundle in the ``dist`` directory. 37 | * ``make_docs``: builds the html documentation in ``docs/_build/html`` 38 | * ``make_env``: initialized a virtualenv with a fresh petlib in folder ``test_env/pltest``. 39 | * ``big_test``: runs all the examples in a virtual environment. 40 | * ``test``: runs all tests. 41 | 42 | **Under the hood.** Petlib uses `py.test`_ for managing and running unit tests, and the `pytest-cov`_ module for test coverage. For running all tests and generating a code coverage report run:: 43 | 44 | py.test --doctest-modules --cov petlib petlib/*.py 45 | 46 | .. _py.test: http://pytest.org 47 | .. _pytest-cov: https://pypi.python.org/pypi/pytest-cov 48 | 49 | To generate an HTML report of lines not covered by tests run:: 50 | 51 | py.test --doctest-modules --cov-report html --cov petlib petlib/*.py 52 | 53 | To build the Sphinx_ HTML documentation go into ``docs`` and run:: 54 | 55 | make html 56 | 57 | .. _Sphinx: http://sphinx-doc.org/ 58 | 59 | To build the source distribution run (and add ``upload`` if you wish to upload it to pypi):: 60 | 61 | python setup.py sdist 62 | 63 | petlib Introduction & Examples 64 | ------------------------------ 65 | 66 | The ``petlib`` library is designed as a one-stop-shop library to prototype advanced Privacy Enhancing Technologies (PETs) in Python. It abstracts a number of OpenSSL APIs supporting operations on 67 | big numbers (bn), elliptic curves (ec), ciphers such as AES in a variety of modes of operation (CTM, GCM, ...) (ciphers), message authentication codes (hmac) and ECDSA signatures (ecdsa). 68 | 69 | Besides ``petlib`` Python offers a number of modules in the standard library that are necessary to support modern cryptography: 70 | 71 | * The ``hashlib`` module offers cryptographic hash functions such as ``sha256``, and ``sha512``. 72 | * The ``os`` module offers the ``urandom`` cryptographically strong random number generator. 73 | * The ``hmac`` module offers keyed hash-based message authentication codes as well as facilities for constant time comparison. 74 | 75 | Examples of use of ``petlib`` for implementing historical as well as state of the art PETs include: 76 | * A `toy raw-RSA`_ implementation, illustrates the use of ``petlib.bn``. 77 | * An `additively homomorphic encryption system (AHEG)`_ based on EC El-Gamal, illustrating the use of ``petlib.ec``. 78 | * A `Schnorr zero-knowledge proof`_ of knowledge or a discrete logarithm in EC groups. 79 | * An `engine to prove and verify in zero-knowledge statements about Discrete Logarithm representations`_ in EC groups. This is a building blocks for a number of advanced protocols. 80 | * The `algebraic message authentication code`_ scheme by Chase, Meiklejohn and Zaverucha (ACM CCS 2014). Illustrates how the generic proof framework of ``genzkp.py`` may be used to easily construct zero-knowledge proofs. 81 | * A reference implementation of the `Anonymous Credentials Light`_ scheme by Baldimtsi, Foteini, and Anna Lysyanskaya (ACM CCS 2013). 82 | 83 | .. _toy raw-RSA: https://github.com/gdanezis/petlib/blob/master/examples/toyrsa.py 84 | .. _additively homomorphic encryption system (AHEG): https://github.com/gdanezis/petlib/blob/master/examples/AHEG.py 85 | .. _Schnorr zero-knowledge proof: https://github.com/gdanezis/petlib/blob/master/examples/zkp.py 86 | .. _engine to prove and verify in zero-knowledge statements about Discrete Logarithm representations: https://github.com/gdanezis/petlib/blob/master/examples/genzkp.py 87 | .. _algebraic message authentication code: https://github.com/gdanezis/petlib/blob/master/examples/genzkp.py 88 | .. _Anonymous Credentials Light: https://github.com/gdanezis/petlib/blob/master/examples/BLcred.py 89 | 90 | 91 | petlib Modules 92 | ============== 93 | 94 | .. automodule:: petlib 95 | 96 | Module petlib.bn 97 | ---------------- 98 | 99 | .. autoclass:: petlib.bn.Bn 100 | :members: 101 | 102 | Module petlib.ec 103 | ---------------- 104 | 105 | .. autoclass:: petlib.ec.EcGroup 106 | :members: 107 | 108 | .. autoclass:: petlib.ec.EcPt 109 | :members: 110 | 111 | Module petlib.cipher 112 | -------------------- 113 | 114 | .. autoclass:: petlib.cipher.Cipher 115 | :members: 116 | 117 | .. autoclass:: petlib.cipher.CipherOperation 118 | :members: 119 | 120 | Module petlib.hmac 121 | ------------------ 122 | 123 | .. autoclass:: petlib.hmac.Hmac 124 | :members: 125 | 126 | .. autofunction:: petlib.hmac.secure_compare 127 | 128 | Module petlib.ecdsa 129 | ------------------- 130 | 131 | .. automodule:: petlib.ecdsa 132 | 133 | .. autofunction:: petlib.ecdsa.do_ecdsa_setup 134 | 135 | .. autofunction:: petlib.ecdsa.do_ecdsa_sign 136 | 137 | .. autofunction:: petlib.ecdsa.do_ecdsa_verify 138 | 139 | Module petlib.pack 140 | ------------------ 141 | 142 | .. automodule:: petlib.pack 143 | 144 | .. autofunction:: petlib.pack.encode 145 | 146 | .. autofunction:: petlib.pack.decode 147 | 148 | 149 | 150 | Indices and tables 151 | ================== 152 | 153 | * :ref:`genindex` 154 | * :ref:`modindex` 155 | * :ref:`search` 156 | 157 | License 158 | ======= 159 | 160 | Petlib is licensed under the following terms: 161 | 162 | Copyright (c) 2014, George Danezis (UCL) 163 | All rights reserved. 164 | 165 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 166 | 167 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 168 | 169 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 170 | 171 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 172 | -------------------------------------------------------------------------------- /petlib/pack.py: -------------------------------------------------------------------------------- 1 | """The module provides functions to pack and unpack petlib Bn, EcGroup, and EcPt 2 | strucures. 3 | 4 | Example: 5 | >>> # Define a custom class, encoder and decoder 6 | >>> class CustomType: 7 | ... def __eq__(self, other): 8 | ... return isinstance(other, CustomType) 9 | >>> 10 | >>> def enc_custom(obj): 11 | ... return b'' 12 | >>> 13 | >>> def dec_custom(data): 14 | ... return CustomType() 15 | >>> 16 | >>> register_coders(CustomType, 10, enc_custom, dec_custom) 17 | >>> 18 | >>> # Define a structure 19 | >>> G = EcGroup() 20 | >>> custom_obj = CustomType() 21 | >>> test_data = [G, G.generator(), G.order(), custom_obj] 22 | >>> 23 | >>> # Encode and decode custom structure 24 | >>> packed = encode(test_data) 25 | >>> x = decode(packed) 26 | >>> assert x == test_data 27 | 28 | """ 29 | 30 | try: 31 | import msgpack 32 | except: 33 | print("Generate Documentation") 34 | 35 | from .ec import EcGroup, EcPt 36 | from .bn import Bn 37 | 38 | __all__ = ["encode", "decode", "register_coders"] 39 | 40 | _pack_reg = {} 41 | _unpack_reg = {} 42 | 43 | 44 | def register_coders(cls, num, enc_func, dec_func): 45 | """ Register a new type for encoding and decoding. 46 | Take a class type, a number, an encoding and a decoding function.""" 47 | 48 | if num in _unpack_reg or cls in _pack_reg: 49 | raise Exception("Class or number already in use.") 50 | 51 | coders = (cls, num, enc_func, dec_func) 52 | _pack_reg[cls] = coders 53 | _unpack_reg[num] = coders 54 | 55 | 56 | def bn_enc(obj): 57 | if obj < 0: 58 | neg = b"-" 59 | data = (-obj).binary() 60 | else: 61 | neg = b"+" 62 | data = obj.binary() 63 | return neg + data 64 | 65 | 66 | def bn_dec(data): 67 | num = Bn.from_binary(data[1:]) 68 | # Accomodate both Python 2 and Python 3 69 | if data[0] == ord("-") or data[0] == "-": 70 | return -num 71 | return num 72 | 73 | 74 | def ecg_enc(obj): 75 | # Serialize EcGroup objects 76 | nid = obj.nid() 77 | packed_nid = msgpack.packb(nid) 78 | return packed_nid 79 | 80 | 81 | def ecg_dec(data): 82 | # Decode EcGroup 83 | nid = msgpack.unpackb(data) 84 | return EcGroup(nid) 85 | 86 | 87 | def ecpt_enc(obj): 88 | # Serialize EcPt objects 89 | nid = obj.group.nid() 90 | data = obj.export() 91 | packed_data = msgpack.packb((nid, data)) 92 | return packed_data 93 | 94 | 95 | def ecpt_dec(data): 96 | # Decode EcPt 97 | nid, ptdata = msgpack.unpackb(data) 98 | return EcPt.from_binary(ptdata, EcGroup(nid)) 99 | 100 | 101 | def _init_coders(): 102 | global _pack_reg, _unpack_reg 103 | _pack_reg, _unpack_reg = {}, {} 104 | register_coders(Bn, 0, bn_enc, bn_dec) 105 | register_coders(EcGroup, 1, ecg_enc, ecg_dec) 106 | register_coders(EcPt, 2, ecpt_enc, ecpt_dec) 107 | 108 | 109 | # Register default coders 110 | try: 111 | _init_coders() 112 | except: 113 | print("Generate documentation") 114 | 115 | 116 | def default(obj): 117 | # Serialize Bn objects 118 | for T in _pack_reg: 119 | if isinstance(obj, T): 120 | _, num, enc, _ = _pack_reg[T] 121 | return msgpack.ExtType(num, enc(obj)) 122 | 123 | raise TypeError("Unknown type: %r" % (type(obj),)) 124 | 125 | 126 | def make_encoder(out_encoder=None): 127 | if out_encoder is None: 128 | return default 129 | else: 130 | def new_encoder(obj): 131 | try: 132 | encoded = default(obj) 133 | return encoded 134 | except BaseException: 135 | return out_encoder(obj) 136 | return new_encoder 137 | 138 | 139 | def ext_hook(code, data): 140 | if code in _unpack_reg: 141 | _, _, _, dec = _unpack_reg[code] 142 | return dec(data) 143 | 144 | # Other 145 | return msgpack.ExtType(code, data) 146 | 147 | 148 | def make_decoder(custom_decoder=None): 149 | if custom_decoder is None: 150 | return ext_hook 151 | else: 152 | def new_decoder(code, data): 153 | out = ext_hook(code, data) 154 | if not isinstance(out, msgpack.ExtType): 155 | return out 156 | else: 157 | return custom_decoder(code, data) 158 | return new_decoder 159 | 160 | 161 | def encode(structure, custom_encoder=None): 162 | """ Encode a structure containing petlib objects to a binary format. May define a custom encoder for user classes. """ 163 | encoder = make_encoder(custom_encoder) 164 | packed_data = msgpack.packb(structure, default=encoder, use_bin_type=True) 165 | return packed_data 166 | 167 | 168 | def decode(packed_data, custom_decoder=None): 169 | """ Decode a binary byte sequence into a structure containing pelib objects. May define a custom decoder for custom classes. """ 170 | decoder = make_decoder(custom_decoder) 171 | structure = msgpack.unpackb( 172 | packed_data, 173 | ext_hook=decoder, 174 | encoding='utf-8') 175 | return structure 176 | 177 | # --- TESTS --- 178 | 179 | 180 | def test_basic(): 181 | x = [b'spam', u'egg'] 182 | packed = msgpack.packb(x, use_bin_type=True) 183 | y = msgpack.unpackb(packed, encoding='utf-8') 184 | assert x == y 185 | 186 | 187 | def test_bn(): 188 | bn1, bn2 = Bn(1), Bn(2) 189 | test_data = [bn1, bn2, -bn1, -bn2] 190 | packed = msgpack.packb(test_data, default=default, use_bin_type=True) 191 | x = msgpack.unpackb(packed, ext_hook=ext_hook, encoding='utf-8') 192 | assert x == test_data 193 | 194 | 195 | def test_ecgroup(): 196 | G = EcGroup() 197 | test_data = [G] 198 | packed = msgpack.packb(test_data, default=default, use_bin_type=True) 199 | x = msgpack.unpackb(packed, ext_hook=ext_hook, encoding='utf-8') 200 | assert x == test_data 201 | 202 | 203 | def test_ecpt(): 204 | G = EcGroup() 205 | test_data = [G.generator()] 206 | packed = msgpack.packb(test_data, default=default, use_bin_type=True) 207 | x = msgpack.unpackb(packed, ext_hook=ext_hook, encoding='utf-8') 208 | assert x == test_data 209 | 210 | 211 | def test_mixed(): 212 | G = EcGroup() 213 | test_data = [G, G.generator(), G.order()] 214 | packed = msgpack.packb(test_data, default=default, use_bin_type=True) 215 | x = msgpack.unpackb(packed, ext_hook=ext_hook, encoding='utf-8') 216 | assert x == test_data 217 | 218 | 219 | def test_enc_dec(): 220 | G = EcGroup() 221 | test_data = [G, G.generator(), G.order()] 222 | packed = encode(test_data) 223 | x = decode(packed) 224 | assert x == test_data 225 | 226 | 227 | def test_enc_dec_dict(): 228 | G = EcGroup() 229 | # , G.generator():"1", "2":G.order()} 230 | test_data = {G.order(): [G, G.generator()]} 231 | packed = encode(test_data) 232 | x = decode(packed) 233 | assert x[G.order()] == test_data[G.order()] 234 | 235 | 236 | def test_enc_dec_custom(): 237 | 238 | # Define a custom class, encoder and decoder 239 | class CustomClass: 240 | def __eq__(self, other): 241 | return isinstance(other, CustomClass) 242 | 243 | def enc_CustomClass(obj): 244 | if isinstance(obj, CustomClass): 245 | return msgpack.ExtType(11, b'') 246 | raise TypeError("Unknown type: %r" % (obj,)) 247 | 248 | def dec_CustomClass(code, data): 249 | if code == 11: 250 | return CustomClass() 251 | 252 | return msgpack.ExtType(code, data) 253 | 254 | # Define a structure 255 | G = EcGroup() 256 | custom_obj = CustomClass() 257 | test_data = [G, G.generator(), G.order(), custom_obj] 258 | 259 | # Encode and decode custom structure 260 | packed = encode(test_data, enc_CustomClass) 261 | x = decode(packed, dec_CustomClass) 262 | assert x == test_data 263 | 264 | 265 | def test_streaming(): 266 | 267 | # Define a custom class, encoder and decoder 268 | class CustomClass2: 269 | def __eq__(self, other): 270 | return isinstance(other, CustomClass2) 271 | 272 | def enc_CustomClass(obj): 273 | if isinstance(obj, CustomClass2): 274 | return msgpack.ExtType(12, b'') 275 | raise TypeError("Unknown type: %r" % (obj,)) 276 | 277 | def dec_CustomClass(code, data): 278 | if code == 12: 279 | return CustomClass2() 280 | 281 | return msgpack.ExtType(code, data) 282 | 283 | # Define a structure 284 | G = EcGroup() 285 | custom_obj = CustomClass2() 286 | test_data = [G, G.generator(), G.order(), custom_obj] 287 | packed1 = encode(test_data, enc_CustomClass) 288 | packed2 = encode(test_data, enc_CustomClass) 289 | 290 | data = packed1 + packed2 291 | 292 | decoder = make_decoder(dec_CustomClass) 293 | Up = msgpack.Unpacker(ext_hook=decoder) 294 | Up.feed(data) 295 | for o in Up: 296 | assert o == test_data 297 | 298 | 299 | def test_docstring(): 300 | # Define a custom class, encoder and decoder 301 | class CustomType: 302 | def __eq__(self, other): 303 | return isinstance(other, CustomType) 304 | 305 | def enc_custom(obj): 306 | return b'' 307 | 308 | def dec_custom(data): 309 | return CustomType() 310 | 311 | _init_coders() 312 | register_coders(CustomType, 14, enc_custom, dec_custom) 313 | assert CustomType in _pack_reg 314 | 315 | # Define a structure 316 | G = EcGroup() 317 | custom_obj = CustomType() 318 | test_data = [G, G.generator(), G.order(), custom_obj] 319 | 320 | # Encode and decode custom structure 321 | packed = encode(test_data) 322 | x = decode(packed) 323 | assert x == test_data 324 | _init_coders() 325 | -------------------------------------------------------------------------------- /pylintrc: -------------------------------------------------------------------------------- 1 | [MASTER] 2 | 3 | # Specify a configuration file. 4 | #rcfile= 5 | 6 | # Python code to execute, usually for sys.path manipulation such as 7 | # pygtk.require(). 8 | #init-hook= 9 | 10 | # Profiled execution. 11 | profile=no 12 | 13 | # Add files or directories to the blacklist. They should be base names, not 14 | # paths. 15 | ignore=CVS 16 | 17 | # Pickle collected data for later comparisons. 18 | persistent=yes 19 | 20 | # List of plugins (as comma separated values of python modules names) to load, 21 | # usually to register additional checkers. 22 | load-plugins= 23 | 24 | 25 | [MESSAGES CONTROL] 26 | 27 | # Enable the message, report, category or checker with the given id(s). You can 28 | # either give multiple identifier separated by comma (,) or put this option 29 | # multiple time. See also the "--disable" option for examples. 30 | #enable= 31 | 32 | # Disable the message, report, category or checker with the given id(s). You 33 | # can either give multiple identifiers separated by comma (,) or put this 34 | # option multiple times (only on the command line, not in the configuration 35 | # file where it should appear only once).You can also use "--disable=all" to 36 | # disable everything first and then reenable specific checks. For example, if 37 | # you want to run only the similarities checker, you can use "--disable=all 38 | # --enable=similarities". If you want to run only the classes checker, but have 39 | # no Warning level messages displayed, use"--disable=all --enable=classes 40 | # --disable=W" 41 | disable=I,C,R,bad-indentation 42 | 43 | 44 | [REPORTS] 45 | 46 | # Set the output format. Available formats are text, parseable, colorized, msvs 47 | # (visual studio) and html. You can also give a reporter class, eg 48 | # mypackage.mymodule.MyReporterClass. 49 | output-format=text 50 | 51 | # Put messages in a separate file for each module / package specified on the 52 | # command line instead of printing them on stdout. Reports (if any) will be 53 | # written in a file name "pylint_global.[txt|html]". 54 | files-output=no 55 | 56 | # Tells whether to display a full report or only the messages 57 | reports=no 58 | 59 | # Python expression which should return a note less than 10 (10 is the highest 60 | # note). You have access to the variables errors warning, statement which 61 | # respectively contain the number of errors / warnings messages and the total 62 | # number of statements analyzed. This is used by the global evaluation report 63 | # (RP0004). 64 | evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) 65 | 66 | # Add a comment according to your evaluation note. This is used by the global 67 | # evaluation report (RP0004). 68 | comment=no 69 | 70 | # Template used to display messages. This is a python new-style format string 71 | # used to format the message information. See doc for all details 72 | #msg-template= 73 | 74 | 75 | [TYPECHECK] 76 | 77 | # Tells whether missing members accessed in mixin class should be ignored. A 78 | # mixin class is detected if its name ends with "mixin" (case insensitive). 79 | ignore-mixin-members=yes 80 | 81 | # List of classes names for which member attributes should not be checked 82 | # (useful for classes with attributes dynamically set). 83 | ignored-classes=SQLObject,pytest,FFILibrary 84 | 85 | # When zope mode is activated, add a predefined set of Zope acquired attributes 86 | # to generated-members. 87 | zope=no 88 | 89 | # List of members which are set dynamically and missed by pylint inference 90 | # system, and so shouldn't trigger E0201 when accessed. Python regular 91 | # expressions are accepted. 92 | generated-members=REQUEST,acl_users,aq_parent 93 | 94 | 95 | [FORMAT] 96 | 97 | # Maximum number of characters on a single line. 98 | max-line-length=80 99 | 100 | # Regexp for a line that is allowed to be longer than the limit. 101 | ignore-long-lines=^\s*(# )??$ 102 | 103 | # Allow the body of an if to be on the same line as the test if there is no 104 | # else. 105 | single-line-if-stmt=no 106 | 107 | # List of optional constructs for which whitespace checking is disabled 108 | no-space-check=trailing-comma,dict-separator 109 | 110 | # Maximum number of lines in a module 111 | max-module-lines=1000 112 | 113 | # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 114 | # tab). 115 | indent-string=' ' 116 | 117 | 118 | [SIMILARITIES] 119 | 120 | # Minimum lines number of a similarity. 121 | min-similarity-lines=4 122 | 123 | # Ignore comments when computing similarities. 124 | ignore-comments=yes 125 | 126 | # Ignore docstrings when computing similarities. 127 | ignore-docstrings=yes 128 | 129 | # Ignore imports when computing similarities. 130 | ignore-imports=no 131 | 132 | 133 | [VARIABLES] 134 | 135 | # Tells whether we should check for unused import in __init__ files. 136 | init-import=no 137 | 138 | # A regular expression matching the beginning of the name of dummy variables 139 | # (i.e. not used). 140 | dummy-variables-rgx=_$|dummy 141 | 142 | # List of additional names supposed to be defined in builtins. Remember that 143 | # you should avoid to define new builtins when possible. 144 | additional-builtins= 145 | 146 | 147 | [MISCELLANEOUS] 148 | 149 | # List of note tags to take in consideration, separated by a comma. 150 | notes=FIXME,XXX,TODO 151 | 152 | 153 | [BASIC] 154 | 155 | # Required attributes for module, separated by a comma 156 | required-attributes= 157 | 158 | # List of builtins function names that should not be used, separated by a comma 159 | bad-functions=map,filter,apply,input 160 | 161 | # Regular expression which should only match correct module names 162 | module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ 163 | 164 | # Regular expression which should only match correct module level names 165 | const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ 166 | 167 | # Regular expression which should only match correct class names 168 | class-rgx=[A-Z_][a-zA-Z0-9]+$ 169 | 170 | # Regular expression which should only match correct function names 171 | function-rgx=[a-z_][a-z0-9_]{2,30}$ 172 | 173 | # Regular expression which should only match correct method names 174 | method-rgx=[a-z_][a-z0-9_]{2,30}$ 175 | 176 | # Regular expression which should only match correct instance attribute names 177 | attr-rgx=[a-z_][a-z0-9_]{2,30}$ 178 | 179 | # Regular expression which should only match correct argument names 180 | argument-rgx=[a-z_][a-z0-9_]{2,30}$ 181 | 182 | # Regular expression which should only match correct variable names 183 | variable-rgx=[a-z_][a-z0-9_]{2,30}$ 184 | 185 | # Regular expression which should only match correct attribute names in class 186 | # bodies 187 | class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ 188 | 189 | # Regular expression which should only match correct list comprehension / 190 | # generator expression variable names 191 | inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ 192 | 193 | # Good variable names which should always be accepted, separated by a comma 194 | good-names=iv,i,j,k,ex,Run,_,G,g,h,t,x,o 195 | 196 | # Bad variable names which should always be refused, separated by a comma 197 | bad-names=foo,bar,baz,toto,tutu,tata 198 | 199 | # Regular expression which should only match function or class names that do 200 | # not require a docstring. 201 | no-docstring-rgx=__.*__ 202 | 203 | # Minimum line length for functions/classes that require docstrings, shorter 204 | # ones are exempt. 205 | docstring-min-length=-1 206 | 207 | 208 | [DESIGN] 209 | 210 | # Maximum number of arguments for function / method 211 | max-args=5 212 | 213 | # Argument names that match this expression will be ignored. Default to name 214 | # with leading underscore 215 | ignored-argument-names=_.* 216 | 217 | # Maximum number of locals for functionf / method body 218 | max-locals=15 219 | 220 | # Maximum number of return / yield for function / method body 221 | max-returns=6 222 | 223 | # Maximum number of branch for function / method body 224 | max-branches=12 225 | 226 | # Maximum number of statements in function / method body 227 | max-statements=50 228 | 229 | # Maximum number of parents for a class (see R0901). 230 | max-parents=7 231 | 232 | # Maximum number of attributes for a class (see R0902). 233 | max-attributes=7 234 | 235 | # Minimum number of public methods for a class (see R0903). 236 | min-public-methods=2 237 | 238 | # Maximum number of public methods for a class (see R0904). 239 | max-public-methods=20 240 | 241 | 242 | [IMPORTS] 243 | 244 | # Deprecated modules which should not be used, separated by a comma 245 | deprecated-modules=regsub,TERMIOS,Bastion,rexec 246 | 247 | # Create a graph of every (i.e. internal and external) dependencies in the 248 | # given file (report RP0402 must not be disabled) 249 | import-graph= 250 | 251 | # Create a graph of external dependencies in the given file (report RP0402 must 252 | # not be disabled) 253 | ext-import-graph= 254 | 255 | # Create a graph of internal dependencies in the given file (report RP0402 must 256 | # not be disabled) 257 | int-import-graph= 258 | 259 | 260 | [CLASSES] 261 | 262 | # List of interface methods to ignore, separated by a comma. This is used for 263 | # instance to not check methods defines in Zope's Interface base class. 264 | ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by 265 | 266 | # List of method names used to declare (i.e. assign) instance attributes. 267 | defining-attr-methods=__init__,__new__,setUp 268 | 269 | # List of valid names for the first argument in a class method. 270 | valid-classmethod-first-arg=cls 271 | 272 | # List of valid names for the first argument in a metaclass class method. 273 | valid-metaclass-classmethod-first-arg=mcs 274 | 275 | 276 | [EXCEPTIONS] 277 | 278 | # Exceptions that will emit a warning when being caught. Defaults to 279 | # "Exception" 280 | overgeneral-exceptions=Exception 281 | -------------------------------------------------------------------------------- /petlib/_cffi_src/openssl/openssl_v1_1.h: -------------------------------------------------------------------------------- 1 | /* 2 | Generic OpenSSL functions. 3 | */ 4 | 5 | void OPENSSL_init(void); 6 | void OPENSSL_free(void*); 7 | unsigned long OpenSSL_version_num(void); 8 | const char *OpenSSL_version(int t); 9 | #define OPENSSL_VERSION ... 10 | 11 | // The constant-time compare functions 12 | int CRYPTO_memcmp(const void *a, const void *b, size_t len); 13 | 14 | typedef enum foo { 15 | /* values as defined in X9.62 (ECDSA) and elsewhere */ 16 | POINT_CONVERSION_COMPRESSED = 2, 17 | POINT_CONVERSION_UNCOMPRESSED = 4, 18 | POINT_CONVERSION_HYBRID = 6 19 | } point_conversion_form_t; 20 | 21 | 22 | /* Locking functions */ 23 | 24 | int CRYPTO_num_locks(void); 25 | 26 | /* 27 | ECC OpenSSL functions. 28 | */ 29 | 30 | typedef ... EC_GROUP; 31 | typedef ... EC_POINT; 32 | typedef ... BN_CTX; 33 | typedef ... BIGNUM; 34 | typedef ... BN_GENCB; 35 | 36 | 37 | EC_GROUP *EC_GROUP_new_by_curve_name(int nid); 38 | void EC_GROUP_free(EC_GROUP* x); 39 | void EC_GROUP_clear_free(EC_GROUP *); 40 | 41 | int EC_GROUP_get_curve_GFp(const EC_GROUP *group, BIGNUM *p, BIGNUM *a, BIGNUM *b, BN_CTX *ctx); 42 | 43 | int EC_GROUP_cmp(const EC_GROUP *a, const EC_GROUP *b, BN_CTX *ctx); 44 | const EC_POINT *EC_GROUP_get0_generator(const EC_GROUP *); 45 | int EC_GROUP_get_order(const EC_GROUP *, BIGNUM *order, BN_CTX *); 46 | int EC_GROUP_get_cofactor(const EC_GROUP *, BIGNUM *cofactor, BN_CTX *); 47 | int EC_GROUP_get_curve_name(const EC_GROUP *group); 48 | 49 | EC_POINT *EC_POINT_new(const EC_GROUP *); 50 | void EC_POINT_free(EC_POINT *); 51 | void EC_POINT_clear_free(EC_POINT *); 52 | int EC_POINT_copy(EC_POINT *, const EC_POINT *); 53 | EC_POINT *EC_POINT_dup(const EC_POINT *, const EC_GROUP *); 54 | 55 | int EC_POINT_set_to_infinity(const EC_GROUP *, EC_POINT *); 56 | int EC_POINT_add(const EC_GROUP *, EC_POINT *r, const EC_POINT *a, const EC_POINT *b, BN_CTX *); 57 | int EC_POINT_dbl(const EC_GROUP *, EC_POINT *r, const EC_POINT *a, BN_CTX *); 58 | int EC_POINT_invert(const EC_GROUP *, EC_POINT *, BN_CTX *); 59 | 60 | int EC_POINT_is_at_infinity(const EC_GROUP *, const EC_POINT *); 61 | int EC_POINT_is_on_curve(const EC_GROUP *, const EC_POINT *, BN_CTX *); 62 | 63 | int EC_POINT_cmp(const EC_GROUP *, const EC_POINT *a, const EC_POINT *b, BN_CTX *); 64 | 65 | int EC_POINT_make_affine(const EC_GROUP *, EC_POINT *, BN_CTX *); 66 | int EC_POINTs_make_affine(const EC_GROUP *, size_t num, EC_POINT *[], BN_CTX *); 67 | 68 | 69 | int EC_POINTs_mul(const EC_GROUP *, EC_POINT *r, const BIGNUM *, size_t num, const EC_POINT *[], const BIGNUM *[], BN_CTX *); 70 | int EC_POINT_mul(const EC_GROUP *, EC_POINT *r, const BIGNUM *, const EC_POINT *, const BIGNUM *, BN_CTX *); 71 | 72 | int EC_GROUP_precompute_mult(EC_GROUP *, BN_CTX *); 73 | int EC_GROUP_have_precompute_mult(const EC_GROUP *); 74 | 75 | int EC_POINT_get_affine_coordinates_GFp(const EC_GROUP *group, 76 | const EC_POINT *p, BIGNUM *x, BIGNUM *y, BN_CTX *ctx); 77 | int EC_POINT_set_compressed_coordinates_GFp(const EC_GROUP *group, EC_POINT *p, 78 | const BIGNUM *x, int y_bit, BN_CTX *ctx); 79 | 80 | size_t EC_POINT_point2oct(const EC_GROUP *, const EC_POINT *, point_conversion_form_t form, 81 | unsigned char *buf, size_t len, BN_CTX *); 82 | int EC_POINT_oct2point(const EC_GROUP *, EC_POINT *, 83 | const unsigned char *buf, size_t len, BN_CTX *); 84 | 85 | typedef struct { 86 | int nid; 87 | const char *comment; 88 | } EC_builtin_curve; 89 | 90 | size_t EC_get_builtin_curves(EC_builtin_curve *r, size_t nitems); 91 | 92 | /* 93 | Big Number (BN) OpenSSL functions. 94 | */ 95 | 96 | typedef unsigned int BN_ULONG; 97 | 98 | BN_CTX *BN_CTX_new(void); 99 | void BN_CTX_free(BN_CTX *c); 100 | 101 | BIGNUM* BN_new(void); 102 | void BN_clear_free(BIGNUM *a); 103 | BIGNUM* BN_copy(BIGNUM *a, const BIGNUM *b); 104 | void BN_swap(BIGNUM *a, BIGNUM *b); 105 | 106 | int BN_cmp(const BIGNUM *a, const BIGNUM *b); 107 | int BN_set_word(BIGNUM *a, BN_ULONG w); 108 | 109 | void BN_set_negative(BIGNUM *b, int n); 110 | 111 | int BN_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b); 112 | int BN_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b); 113 | 114 | int BN_mul(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx); 115 | int BN_div(BIGNUM *dv, BIGNUM *rem, const BIGNUM *m, const BIGNUM *d, BN_CTX *ctx); 116 | 117 | int BN_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p,BN_CTX *ctx); 118 | int BN_mod_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, const BIGNUM *m,BN_CTX *ctx); 119 | BIGNUM* BN_mod_inverse(BIGNUM *ret, const BIGNUM *a, const BIGNUM *n,BN_CTX *ctx); 120 | 121 | int BN_nnmod(BIGNUM *r, const BIGNUM *a, const BIGNUM *m, BN_CTX *ctx); 122 | int BN_mod_add(BIGNUM *r, BIGNUM *a, BIGNUM *b, const BIGNUM *m, 123 | BN_CTX *ctx); 124 | int BN_mod_sub(BIGNUM *r, BIGNUM *a, BIGNUM *b, const BIGNUM *m, 125 | BN_CTX *ctx); 126 | int BN_mod_mul(BIGNUM *r, BIGNUM *a, BIGNUM *b, const BIGNUM *m, 127 | BN_CTX *ctx); 128 | 129 | int bn_num_bytes(BIGNUM * a); 130 | int BN_num_bits(const BIGNUM *a); 131 | char * BN_bn2dec(const BIGNUM *a); 132 | char * BN_bn2hex(const BIGNUM *a); 133 | int BN_hex2bn(BIGNUM **a, const char *str); 134 | int BN_dec2bn(BIGNUM **a, const char *str); 135 | BIGNUM* BN_bin2bn(const unsigned char *s,int len,BIGNUM *ret); 136 | int BN_bn2bin(const BIGNUM *a, unsigned char *to); 137 | 138 | int BN_generate_prime_ex(BIGNUM *ret,int bits,int safe, const BIGNUM *add, 139 | const BIGNUM *rem, BN_GENCB *cb); 140 | int BN_is_prime_ex(const BIGNUM *p,int nchecks, BN_CTX *ctx, BN_GENCB *cb); 141 | 142 | int BN_rand_range(BIGNUM *rnd, const BIGNUM *range); 143 | 144 | 145 | int bn_is_odd(BIGNUM * a); 146 | 147 | 148 | int BN_is_bit_set(const BIGNUM *a, int n); 149 | 150 | 151 | /* 152 | EVP Ciphers 153 | */ 154 | 155 | typedef ... EVP_CIPHER; 156 | typedef ... EVP_CIPHER_CTX; 157 | 158 | const EVP_CIPHER * EVP_aes_128_gcm(void); 159 | const EVP_CIPHER * EVP_aes_192_gcm(void); 160 | const EVP_CIPHER * EVP_aes_256_gcm(void); 161 | 162 | typedef ... ENGINE; // Ignore details of the engine. 163 | 164 | // Cipher context operations 165 | 166 | void EVP_CIPHER_CTX_init(EVP_CIPHER_CTX *a); 167 | int EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *a); 168 | EVP_CIPHER_CTX *EVP_CIPHER_CTX_new(void); 169 | void EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *a); 170 | int EVP_CIPHER_CTX_set_key_length(EVP_CIPHER_CTX *x, int keylen); 171 | int EVP_CIPHER_CTX_set_padding(EVP_CIPHER_CTX *c, int pad); 172 | int EVP_CIPHER_CTX_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr); 173 | int EVP_CIPHER_CTX_rand_key(EVP_CIPHER_CTX *ctx, unsigned char *key); 174 | 175 | // Cipher operations 176 | 177 | const EVP_CIPHER *EVP_get_cipherbyname(const char *name); 178 | 179 | int EVP_CipherInit_ex(EVP_CIPHER_CTX *ctx,const EVP_CIPHER *cipher, ENGINE *impl, 180 | const unsigned char *key,const unsigned char *iv, int enc); 181 | int EVP_CipherUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, 182 | int *outl, const unsigned char *in, int inl); 183 | int EVP_CipherFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *outm, int *outl); 184 | 185 | // The control codes for ciphers 186 | 187 | #define EVP_CTRL_INIT ... 188 | #define EVP_CTRL_SET_KEY_LENGTH ... 189 | #define EVP_CTRL_GET_RC2_KEY_BITS ... 190 | #define EVP_CTRL_SET_RC2_KEY_BITS ... 191 | #define EVP_CTRL_GET_RC5_ROUNDS ... 192 | #define EVP_CTRL_SET_RC5_ROUNDS ... 193 | #define EVP_CTRL_RAND_KEY ... 194 | #define EVP_CTRL_PBE_PRF_NID ... 195 | #define EVP_CTRL_COPY ... 196 | #define EVP_CTRL_GCM_SET_IVLEN ... 197 | #define EVP_CTRL_GCM_GET_TAG ... 198 | #define EVP_CTRL_GCM_SET_TAG ... 199 | #define EVP_CTRL_GCM_SET_IV_FIXED ... 200 | #define EVP_CTRL_GCM_IV_GEN ... 201 | #define EVP_CTRL_CCM_SET_IVLEN ... 202 | #define EVP_CTRL_CCM_GET_TAG ... 203 | #define EVP_CTRL_CCM_SET_TAG ... 204 | #define EVP_CTRL_CCM_SET_L ... 205 | #define EVP_CTRL_CCM_SET_MSGLEN ... 206 | #define EVP_CTRL_AEAD_TLS1_AAD ... 207 | #define EVP_CTRL_AEAD_SET_MAC_KEY ... 208 | #define EVP_CTRL_GCM_SET_IV_INV ... 209 | 210 | int EVP_CIPHER_nid(const EVP_CIPHER *e); 211 | int EVP_CIPHER_block_size(const EVP_CIPHER *e); 212 | int EVP_CIPHER_key_length(const EVP_CIPHER *e); 213 | int EVP_CIPHER_iv_length(const EVP_CIPHER *e); 214 | unsigned long EVP_CIPHER_flags(const EVP_CIPHER *e); 215 | unsigned long EVP_CIPHER_mode(const EVP_CIPHER *e); 216 | int EVP_CIPHER_type(const EVP_CIPHER *ctx); 217 | 218 | int EVP_EncryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, 219 | ENGINE *impl, unsigned char *key, unsigned char *iv); 220 | int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, 221 | int *outl, unsigned char *in, int inl); 222 | int EVP_EncryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out, 223 | int *outl); 224 | 225 | int EVP_DecryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, 226 | ENGINE *impl, unsigned char *key, unsigned char *iv); 227 | int EVP_DecryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, 228 | int *outl, unsigned char *in, int inl); 229 | int EVP_DecryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *outm, 230 | int *outl); 231 | 232 | void init_ciphers(); 233 | void cleanup_ciphers(); 234 | int setup_ssl_threads(void); 235 | 236 | // The HMAC interface 237 | 238 | typedef ... HMAC_CTX; 239 | typedef ... EVP_MD; 240 | 241 | int EVP_MD_size(const EVP_MD *md); 242 | int EVP_MD_block_size(const EVP_MD *md); 243 | const EVP_MD *EVP_get_digestbyname(const char *name); 244 | 245 | 246 | HMAC_CTX *HMAC_CTX_new(void); 247 | int HMAC_CTX_reset(HMAC_CTX *ctx); 248 | 249 | int HMAC_Init_ex(HMAC_CTX *ctx, const void *key, int key_len, 250 | const EVP_MD *md, ENGINE *impl); 251 | int HMAC_Update(HMAC_CTX *ctx, const unsigned char *data, int len); 252 | int HMAC_Final(HMAC_CTX *ctx, unsigned char *md, unsigned int *len); 253 | 254 | void HMAC_CTX_free(HMAC_CTX *ctx); 255 | 256 | // The ECDSA interface 257 | 258 | typedef ... ECDSA_SIG; 259 | 260 | void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps); 261 | int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s); 262 | int ECDSA_SIG_set0_petlib(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s); 263 | 264 | typedef ... EC_KEY; 265 | 266 | ECDSA_SIG* ECDSA_SIG_new(void); 267 | void ECDSA_SIG_free(ECDSA_SIG *sig); 268 | 269 | ECDSA_SIG* ECDSA_do_sign(const unsigned char *dgst, int dgst_len, 270 | EC_KEY *eckey); 271 | int ECDSA_do_verify(const unsigned char *dgst, int dgst_len, 272 | const ECDSA_SIG *sig, EC_KEY* eckey); 273 | int ECDSA_size(const EC_KEY *eckey); 274 | 275 | 276 | ECDSA_SIG* ECDSA_do_sign_ex(const unsigned char *dgst, int dgstlen, 277 | const BIGNUM *kinv, const BIGNUM *rp, 278 | EC_KEY *eckey); 279 | 280 | int ECDSA_sign_setup(EC_KEY *eckey, BN_CTX *ctx, 281 | BIGNUM **kinv, BIGNUM **rp); 282 | 283 | 284 | EC_KEY *EC_KEY_new(void); 285 | void EC_KEY_free(EC_KEY *key); 286 | 287 | int EC_KEY_set_group(EC_KEY *key, const EC_GROUP *group); 288 | int EC_KEY_set_private_key(EC_KEY *key, const BIGNUM *prv); 289 | int EC_KEY_set_public_key(EC_KEY *key, const EC_POINT *pub); 290 | int EC_KEY_precompute_mult(EC_KEY *key, BN_CTX *ctx); 291 | 292 | #define SSLEAY_VERSION ... 293 | 294 | const char *SSLeay_version(int type); 295 | 296 | unsigned long ERR_get_error(void); 297 | 298 | -------------------------------------------------------------------------------- /petlib/_cffi_src/openssl/openssl_v1_0.h: -------------------------------------------------------------------------------- 1 | /* 2 | Generic OpenSSL functions. 3 | */ 4 | 5 | void OPENSSL_init(void); 6 | void OPENSSL_free(void*); 7 | 8 | // The constant-time compare functions 9 | int CRYPTO_memcmp(const void *a, const void *b, size_t len); 10 | 11 | typedef enum foo { 12 | /* values as defined in X9.62 (ECDSA) and elsewhere */ 13 | POINT_CONVERSION_COMPRESSED = 2, 14 | POINT_CONVERSION_UNCOMPRESSED = 4, 15 | POINT_CONVERSION_HYBRID = 6 16 | } point_conversion_form_t; 17 | 18 | 19 | /* Locking functions */ 20 | 21 | int CRYPTO_num_locks(void); 22 | void CRYPTO_lock(int mode, int type, const char *file, int line); 23 | void CRYPTO_set_locking_callback(void (*func) (int mode, int type, const char *file, int line)); 24 | void (*CRYPTO_get_locking_callback(void)) (int mode, int type, const char *file, int line); 25 | void CRYPTO_set_add_lock_callback(int (*func) (int *num, int mount, int type, 26 | const char *file, int line)); 27 | int (*CRYPTO_get_add_lock_callback(void)) (int *num, int mount, int type, const char *file, int line); 28 | 29 | 30 | /* 31 | ECC OpenSSL functions. 32 | */ 33 | 34 | typedef ... EC_GROUP; 35 | typedef ... EC_POINT; 36 | typedef ... BN_CTX; 37 | typedef ... BIGNUM; 38 | typedef ... BN_GENCB; 39 | 40 | 41 | EC_GROUP *EC_GROUP_new_by_curve_name(int nid); 42 | void EC_GROUP_free(EC_GROUP* x); 43 | void EC_GROUP_clear_free(EC_GROUP *); 44 | 45 | int EC_GROUP_get_curve_GFp(const EC_GROUP *group, BIGNUM *p, BIGNUM *a, BIGNUM *b, BN_CTX *ctx); 46 | 47 | int EC_GROUP_cmp(const EC_GROUP *a, const EC_GROUP *b, BN_CTX *ctx); 48 | const EC_POINT *EC_GROUP_get0_generator(const EC_GROUP *); 49 | int EC_GROUP_get_order(const EC_GROUP *, BIGNUM *order, BN_CTX *); 50 | int EC_GROUP_get_cofactor(const EC_GROUP *, BIGNUM *cofactor, BN_CTX *); 51 | int EC_GROUP_get_curve_name(const EC_GROUP *group); 52 | 53 | EC_POINT *EC_POINT_new(const EC_GROUP *); 54 | void EC_POINT_free(EC_POINT *); 55 | void EC_POINT_clear_free(EC_POINT *); 56 | int EC_POINT_copy(EC_POINT *, const EC_POINT *); 57 | EC_POINT *EC_POINT_dup(const EC_POINT *, const EC_GROUP *); 58 | 59 | int EC_POINT_set_to_infinity(const EC_GROUP *, EC_POINT *); 60 | int EC_POINT_add(const EC_GROUP *, EC_POINT *r, const EC_POINT *a, const EC_POINT *b, BN_CTX *); 61 | int EC_POINT_dbl(const EC_GROUP *, EC_POINT *r, const EC_POINT *a, BN_CTX *); 62 | int EC_POINT_invert(const EC_GROUP *, EC_POINT *, BN_CTX *); 63 | 64 | int EC_POINT_is_at_infinity(const EC_GROUP *, const EC_POINT *); 65 | int EC_POINT_is_on_curve(const EC_GROUP *, const EC_POINT *, BN_CTX *); 66 | 67 | int EC_POINT_cmp(const EC_GROUP *, const EC_POINT *a, const EC_POINT *b, BN_CTX *); 68 | 69 | int EC_POINT_make_affine(const EC_GROUP *, EC_POINT *, BN_CTX *); 70 | int EC_POINTs_make_affine(const EC_GROUP *, size_t num, EC_POINT *[], BN_CTX *); 71 | 72 | 73 | int EC_POINTs_mul(const EC_GROUP *, EC_POINT *r, const BIGNUM *, size_t num, const EC_POINT *[], const BIGNUM *[], BN_CTX *); 74 | int EC_POINT_mul(const EC_GROUP *, EC_POINT *r, const BIGNUM *, const EC_POINT *, const BIGNUM *, BN_CTX *); 75 | 76 | /* EC_GROUP_precompute_mult() stores multiples of generator for faster point multiplication */ 77 | int EC_GROUP_precompute_mult(EC_GROUP *, BN_CTX *); 78 | /* EC_GROUP_have_precompute_mult() reports whether such precomputation has been done */ 79 | int EC_GROUP_have_precompute_mult(const EC_GROUP *); 80 | 81 | int EC_POINT_get_affine_coordinates_GFp(const EC_GROUP *group, 82 | const EC_POINT *p, BIGNUM *x, BIGNUM *y, BN_CTX *ctx); 83 | int EC_POINT_set_compressed_coordinates_GFp(const EC_GROUP *group, EC_POINT *p, 84 | const BIGNUM *x, int y_bit, BN_CTX *ctx); 85 | 86 | size_t EC_POINT_point2oct(const EC_GROUP *, const EC_POINT *, point_conversion_form_t form, 87 | unsigned char *buf, size_t len, BN_CTX *); 88 | int EC_POINT_oct2point(const EC_GROUP *, EC_POINT *, 89 | const unsigned char *buf, size_t len, BN_CTX *); 90 | 91 | typedef struct { 92 | int nid; 93 | const char *comment; 94 | } EC_builtin_curve; 95 | 96 | size_t EC_get_builtin_curves(EC_builtin_curve *r, size_t nitems); 97 | 98 | /* 99 | Big Number (BN) OpenSSL functions. 100 | */ 101 | 102 | typedef unsigned int BN_ULONG; 103 | 104 | BN_CTX *BN_CTX_new(void); 105 | void BN_CTX_free(BN_CTX *c); 106 | 107 | BIGNUM* BN_new(void); 108 | void BN_init(BIGNUM *); 109 | void BN_clear_free(BIGNUM *a); 110 | BIGNUM* BN_copy(BIGNUM *a, const BIGNUM *b); 111 | void BN_swap(BIGNUM *a, BIGNUM *b); 112 | 113 | int BN_cmp(const BIGNUM *a, const BIGNUM *b); 114 | int BN_set_word(BIGNUM *a, BN_ULONG w); 115 | 116 | void BN_set_negative(BIGNUM *b, int n); 117 | 118 | int BN_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b); 119 | int BN_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b); 120 | 121 | int BN_mul(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx); 122 | int BN_div(BIGNUM *dv, BIGNUM *rem, const BIGNUM *m, const BIGNUM *d, BN_CTX *ctx); 123 | 124 | int BN_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p,BN_CTX *ctx); 125 | int BN_mod_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, const BIGNUM *m,BN_CTX *ctx); 126 | BIGNUM* BN_mod_inverse(BIGNUM *ret, const BIGNUM *a, const BIGNUM *n,BN_CTX *ctx); 127 | 128 | int BN_nnmod(BIGNUM *r, const BIGNUM *a, const BIGNUM *m, BN_CTX *ctx); 129 | int BN_mod_add(BIGNUM *r, BIGNUM *a, BIGNUM *b, const BIGNUM *m, 130 | BN_CTX *ctx); 131 | int BN_mod_sub(BIGNUM *r, BIGNUM *a, BIGNUM *b, const BIGNUM *m, 132 | BN_CTX *ctx); 133 | int BN_mod_mul(BIGNUM *r, BIGNUM *a, BIGNUM *b, const BIGNUM *m, 134 | BN_CTX *ctx); 135 | 136 | int bn_num_bytes(BIGNUM * a); 137 | int BN_num_bits(const BIGNUM *a); 138 | char * BN_bn2dec(const BIGNUM *a); 139 | char * BN_bn2hex(const BIGNUM *a); 140 | int BN_hex2bn(BIGNUM **a, const char *str); 141 | int BN_dec2bn(BIGNUM **a, const char *str); 142 | BIGNUM* BN_bin2bn(const unsigned char *s,int len,BIGNUM *ret); 143 | int BN_bn2bin(const BIGNUM *a, unsigned char *to); 144 | 145 | int BN_generate_prime_ex(BIGNUM *ret,int bits,int safe, const BIGNUM *add, 146 | const BIGNUM *rem, BN_GENCB *cb); 147 | int BN_is_prime_ex(const BIGNUM *p,int nchecks, BN_CTX *ctx, BN_GENCB *cb); 148 | 149 | int BN_rand_range(BIGNUM *rnd, const BIGNUM *range); 150 | 151 | 152 | int bn_is_odd(BIGNUM * a); 153 | // int BN_is_odd(const BIGNUM *a); 154 | 155 | 156 | int BN_is_bit_set(const BIGNUM *a, int n); 157 | 158 | 159 | /* 160 | 161 | EVP Ciphers 162 | 163 | */ 164 | 165 | typedef struct evp_cipher_st 166 | { 167 | int nid; 168 | int block_size; 169 | int key_len; /* Default value for variable length ciphers */ 170 | int iv_len; 171 | unsigned long flags; /* Various flags */ 172 | ...; 173 | } EVP_CIPHER; 174 | 175 | typedef struct evp_cipher_ctx_st 176 | { 177 | const EVP_CIPHER *cipher; 178 | int encrypt; /* encrypt or decrypt */ 179 | int buf_len; /* number we have left */ 180 | int num; /* used by cfb/ofb/ctr mode */ 181 | int key_len; /* May change for variable length cipher */ 182 | unsigned long flags; /* Various flags */ 183 | int final_used; 184 | int block_mask; 185 | ...; 186 | } EVP_CIPHER_CTX; 187 | 188 | const EVP_CIPHER * EVP_aes_128_gcm(void); 189 | const EVP_CIPHER * EVP_aes_192_gcm(void); 190 | const EVP_CIPHER * EVP_aes_256_gcm(void); 191 | 192 | typedef ... ENGINE; // Ignore details of the engine. 193 | 194 | // Cipher context operations 195 | 196 | void EVP_CIPHER_CTX_init(EVP_CIPHER_CTX *a); 197 | int EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *a); 198 | EVP_CIPHER_CTX *EVP_CIPHER_CTX_new(void); 199 | void EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *a); 200 | int EVP_CIPHER_CTX_set_key_length(EVP_CIPHER_CTX *x, int keylen); 201 | int EVP_CIPHER_CTX_set_padding(EVP_CIPHER_CTX *c, int pad); 202 | int EVP_CIPHER_CTX_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr); 203 | int EVP_CIPHER_CTX_rand_key(EVP_CIPHER_CTX *ctx, unsigned char *key); 204 | 205 | // Cipher operations 206 | 207 | const EVP_CIPHER *EVP_get_cipherbyname(const char *name); 208 | 209 | int EVP_CipherInit_ex(EVP_CIPHER_CTX *ctx,const EVP_CIPHER *cipher, ENGINE *impl, 210 | const unsigned char *key,const unsigned char *iv, int enc); 211 | int EVP_CipherUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, 212 | int *outl, const unsigned char *in, int inl); 213 | int EVP_CipherFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *outm, int *outl); 214 | 215 | // The control codes for ciphers 216 | 217 | #define EVP_CTRL_INIT ... 218 | #define EVP_CTRL_SET_KEY_LENGTH ... 219 | #define EVP_CTRL_GET_RC2_KEY_BITS ... 220 | #define EVP_CTRL_SET_RC2_KEY_BITS ... 221 | #define EVP_CTRL_GET_RC5_ROUNDS ... 222 | #define EVP_CTRL_SET_RC5_ROUNDS ... 223 | #define EVP_CTRL_RAND_KEY ... 224 | #define EVP_CTRL_PBE_PRF_NID ... 225 | #define EVP_CTRL_COPY ... 226 | #define EVP_CTRL_GCM_SET_IVLEN ... 227 | #define EVP_CTRL_GCM_GET_TAG ... 228 | #define EVP_CTRL_GCM_SET_TAG ... 229 | #define EVP_CTRL_GCM_SET_IV_FIXED ... 230 | #define EVP_CTRL_GCM_IV_GEN ... 231 | #define EVP_CTRL_CCM_SET_IVLEN ... 232 | #define EVP_CTRL_CCM_GET_TAG ... 233 | #define EVP_CTRL_CCM_SET_TAG ... 234 | #define EVP_CTRL_CCM_SET_L ... 235 | #define EVP_CTRL_CCM_SET_MSGLEN ... 236 | #define EVP_CTRL_AEAD_TLS1_AAD ... 237 | #define EVP_CTRL_AEAD_SET_MAC_KEY ... 238 | #define EVP_CTRL_GCM_SET_IV_INV ... 239 | 240 | int EVP_EncryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, 241 | ENGINE *impl, unsigned char *key, unsigned char *iv); 242 | int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, 243 | int *outl, unsigned char *in, int inl); 244 | int EVP_EncryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out, 245 | int *outl); 246 | 247 | int EVP_DecryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, 248 | ENGINE *impl, unsigned char *key, unsigned char *iv); 249 | int EVP_DecryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, 250 | int *outl, unsigned char *in, int inl); 251 | int EVP_DecryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *outm, 252 | int *outl); 253 | 254 | void init_ciphers(); 255 | void cleanup_ciphers(); 256 | int setup_ssl_threads(); 257 | 258 | // The HMAC interface 259 | 260 | 261 | typedef struct { ...; } HMAC_CTX; 262 | typedef ... EVP_MD; 263 | 264 | size_t hmac_ctx_size(); 265 | 266 | int EVP_MD_size(const EVP_MD *md); 267 | int EVP_MD_block_size(const EVP_MD *md); 268 | const EVP_MD *EVP_get_digestbyname(const char *name); 269 | 270 | 271 | void HMAC_CTX_init(HMAC_CTX *ctx); 272 | 273 | int HMAC_Init_ex(HMAC_CTX *ctx, const void *key, int key_len, 274 | const EVP_MD *md, ENGINE *impl); 275 | int HMAC_Update(HMAC_CTX *ctx, const unsigned char *data, int len); 276 | int HMAC_Final(HMAC_CTX *ctx, unsigned char *md, unsigned int *len); 277 | 278 | void HMAC_CTX_cleanup(HMAC_CTX *ctx); 279 | 280 | 281 | // The ECDSA interface 282 | 283 | 284 | typedef struct ECDSA_SIG_st 285 | { 286 | BIGNUM * r; 287 | BIGNUM * s; 288 | } ECDSA_SIG; 289 | 290 | typedef ... EC_KEY; 291 | 292 | ECDSA_SIG* ECDSA_SIG_new(void); 293 | void ECDSA_SIG_free(ECDSA_SIG *sig); 294 | 295 | ECDSA_SIG* ECDSA_do_sign(const unsigned char *dgst, int dgst_len, 296 | EC_KEY *eckey); 297 | int ECDSA_do_verify(const unsigned char *dgst, int dgst_len, 298 | const ECDSA_SIG *sig, EC_KEY* eckey); 299 | int ECDSA_size(const EC_KEY *eckey); 300 | 301 | 302 | ECDSA_SIG* ECDSA_do_sign_ex(const unsigned char *dgst, int dgstlen, 303 | const BIGNUM *kinv, const BIGNUM *rp, 304 | EC_KEY *eckey); 305 | 306 | int ECDSA_sign_setup(EC_KEY *eckey, BN_CTX *ctx, 307 | BIGNUM **kinv, BIGNUM **rp); 308 | 309 | 310 | 311 | 312 | EC_KEY *EC_KEY_new(void); 313 | void EC_KEY_free(EC_KEY *key); 314 | 315 | int EC_KEY_set_group(EC_KEY *key, const EC_GROUP *group); 316 | int EC_KEY_set_private_key(EC_KEY *key, const BIGNUM *prv); 317 | int EC_KEY_set_public_key(EC_KEY *key, const EC_POINT *pub); 318 | int EC_KEY_precompute_mult(EC_KEY *key, BN_CTX *ctx); 319 | 320 | #define SSLEAY_VERSION ... 321 | 322 | unsigned long SSLeay(); 323 | const char *SSLeay_version(int type); 324 | 325 | unsigned long ERR_get_error(void); 326 | 327 | -------------------------------------------------------------------------------- /examples/GK15ringsig.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path = ["."] + sys.path 3 | 4 | from petlib.ec import EcGroup 5 | from petlib.bn import Bn 6 | 7 | from hashlib import sha256 8 | import math 9 | 10 | ## ###################################################### 11 | ## An implementation of the ring signature scheme in 12 | ## 13 | ## Jens Groth and Markulf Kohlweiss. "One-out-of-Many Proofs: 14 | ## Or How to Leak a Secret and Spend a Coin" 15 | ## Cryptology ePrint Archive: Report 2014/764 16 | ## 17 | ## https://eprint.iacr.org/2014/764 18 | ## ###################################################### 19 | 20 | def challenge(elements): 21 | """Packages a challenge in a bijective way""" 22 | elem = [len(elements)] + elements 23 | elem_str = list(map(str, elem)) 24 | elem_len = list(map(lambda x: "%s||%s" % (len(x) , x), elem_str)) 25 | state = "|".join(elem_len) 26 | H = sha256() 27 | H.update(state.encode("utf8")) 28 | return Bn.from_binary(H.digest()) 29 | 30 | def setup(): 31 | """ Generates parameters for Commitments """ 32 | G = EcGroup() 33 | g = G.hash_to_point(b'g') 34 | h = G.hash_to_point(b'h') 35 | o = G.order() 36 | return (G, g, h, o) 37 | 38 | def Com(ck, m, k): 39 | """ Pedersen Commitment. """ 40 | (G, g, h, o) = ck 41 | return m * g + k * h 42 | 43 | 44 | def ProveZeroOne(ck, c, m, r): 45 | """ Simple proof that a Commitment c = Com(m,r) is either 0 or 1 """ 46 | assert Com(ck, m, r) == c 47 | (G, g, h, o) = ck 48 | a, s, t = o.random(), o.random(), o.random() 49 | ca = Com(ck, a, s) 50 | cb = Com(ck, a*m, t) 51 | x = challenge([g, h, ca, cb]) % o 52 | f = (x * m + a) % o 53 | za = (r * x + s) % o 54 | zb = (r * (x - f) + t) % o 55 | return (x, f, za, zb) 56 | 57 | def VerifyZeroOne(ck, c, proof): 58 | """ Verify that a Commitment c = Com(m,r) is either 0 or 1 """ 59 | (G, g, h, o) = ck 60 | (x, f, za, zb) = proof 61 | 62 | assert 0 < x < o 63 | assert 0 < f < o 64 | assert 0 < za < o 65 | assert 0 < zb < o 66 | 67 | ca = Com(ck,f,za) - x * c 68 | cb = Com(ck, 0, zb) - (x-f) * c 69 | xp = challenge([g, h, ca, cb]) % o 70 | return xp == x 71 | 72 | 73 | def ProveOneOfN(ck, cis, el, r, message = ""): 74 | """ NIZK Proof that Com(0; r) is within Cis. 75 | The fact that it is the el'th commitmtnet is not revealed. 76 | + Ring signature on "message". """ 77 | n = int(math.ceil(math.log(len(cis)) / math.log(2))) 78 | assert Com(ck, 0, r) == cis[el] 79 | (G, g, h, o) = ck 80 | 81 | ## Commit to the bits of the index 82 | el = Bn(el) 83 | eli = [Bn(int(el.is_bit_set(i))) for i in range(n)] 84 | 85 | ri = [o.random() for i in range(n)] 86 | ai = [o.random() for i in range(n)] 87 | si = [o.random() for i in range(n)] 88 | ti = [o.random() for i in range(n)] 89 | 90 | Celi = [Com(ck, elix, rix) for elix, rix in zip(eli, ri)] 91 | Cai = [Com(ck, a, s) for a, s in zip(ai, si)] 92 | Cbi = [Com(ck, l * a , s) for l, a, s in zip(eli, ai, ti)] 93 | 94 | # Compute p_idxi(x) 95 | p_idx_i = [] 96 | for idx in range(len(cis)): 97 | idx = Bn(idx) 98 | idxi = [Bn(int(idx.is_bit_set(i))) for i in range(n)] 99 | 100 | p = [Bn(1)] 101 | for j, idxi_j in enumerate(idxi): 102 | if idxi_j == 0: 103 | p = poly_mul(o, p, [ -ai[j] , - eli[j] + 1] ) 104 | else: 105 | p = poly_mul(o, p, [ ai[j] , eli[j] ]) 106 | 107 | p_idx_i += [p] 108 | 109 | # Compute all Cdi's 110 | roi = [] 111 | cdi = [] 112 | for i in range(n): 113 | roi_i = o.random() 114 | roi += [ roi_i ] 115 | # cdi_i = Com(ck, 0, roi_i) 116 | 117 | wis = [] 118 | 119 | for idx, cidx in enumerate(cis): 120 | wis += [ p_idx_i[idx][i] ] 121 | # cdi_i += p_idx_i[idx][i] * cidx 122 | 123 | # assert G.wsum(wis, cis) + Com(ck, 0, roi_i) == cdi_i 124 | cdi_i = G.wsum(wis, cis) + Com(ck, 0, roi_i) 125 | 126 | cdi += [ cdi_i ] 127 | 128 | ## The challenge 129 | x = challenge(list(ck) + cis + Celi + Cai + Cbi + cdi + [ message ]) 130 | 131 | ## The responses 132 | fi = [(elj * x + aj) % o for elj, aj in zip(eli, ai)] 133 | zai = [(rj * x + sj) % o for rj, sj in zip(ri, si)] 134 | zbi = [(rj * (x - fj) + tj) % o for rj, fj, tj in zip(ri, fi, ti)] 135 | 136 | zd = r * pow(x, n, o) % o 137 | for k in range(n): 138 | zd = (zd - roi[k] * pow(x, k, o)) % o 139 | 140 | proof = (Celi, Cai, Cbi, cdi, fi, zai, zbi, zd) 141 | 142 | return proof 143 | 144 | def VerifyOneOfN(ck, cis, proof, message = ""): 145 | """ Verify the ring signature on message """ 146 | 147 | n = int(math.ceil(math.log(len(cis)) / math.log(2))) 148 | (G, g, h, o) = ck 149 | 150 | (Celi, Cai, Cbi, cdi, fi, zai, zbi, zd) = proof 151 | 152 | ## Check all parts of the proof are in the right groups 153 | assert 0 <= zd < o 154 | for k in range(n): 155 | assert 0 <= fi[k] < o 156 | assert 0 <= zai[k] < o 157 | assert 0 <= zbi[k] < o 158 | 159 | assert G.check_point(Celi[k]) 160 | assert G.check_point(Cai[k]) 161 | assert G.check_point(Cbi[k]) 162 | assert G.check_point(cdi[k]) 163 | 164 | # Recompute the challenge 165 | x = challenge(list(ck) + cis + Celi + Cai + Cbi + cdi + [ message ]) 166 | 167 | 168 | ret = True 169 | 170 | for i in range(n): 171 | ret &= x * Celi[i] + Cai[i] == Com(ck, fi[i], zai[i]) 172 | ret &= (x - fi[i]) * Celi[i] + Cbi[i] == Com(ck, Bn(0), zbi[i]) 173 | 174 | # acc = G.infinite() 175 | 176 | bases = [] 177 | expons = [] 178 | 179 | for idx, ci in enumerate(cis): 180 | idx = Bn(idx) 181 | idxi = [Bn(int(idx.is_bit_set(i))) for i in range(n)] 182 | 183 | acc_exp = Bn(1) 184 | for k, ij in enumerate(idxi): 185 | if ij == 0: 186 | acc_exp = acc_exp.mod_mul(x - fi[k], o) 187 | else: 188 | acc_exp = acc_exp.mod_mul(fi[k], o) 189 | 190 | bases += [ ci ] 191 | expons += [ acc_exp ] 192 | 193 | # acc = acc + acc_exp * ci 194 | 195 | for k in range(n): 196 | expi = (- pow(x,k,o)) 197 | # acc = acc + expi * cdi[k] 198 | bases += [ cdi[k] ] 199 | expons += [ expi ] 200 | 201 | # assert G.wsum(expons, bases) == acc 202 | acc = G.wsum(expons, bases) 203 | 204 | ret &= acc == Com(ck, 0, zd) 205 | 206 | return ret 207 | 208 | 209 | ## ###################################### 210 | ## Naive polynomial arithmetic 211 | zero = Bn(0) 212 | 213 | def poly_expand(o, poly, size): 214 | global zero 215 | 216 | assert len(poly) <= size 217 | # zero = Bn(0) 218 | new_poly = [zero for _ in range(size)] 219 | for i in range(len(poly)): 220 | new_poly[i] = poly[i] 221 | return new_poly 222 | 223 | def poly_add(o, poly1, poly2): 224 | size = max(len(poly1), len(poly2)) 225 | p1 = poly_expand(o, poly1, size) 226 | p2 = poly_expand(o, poly2, size) 227 | 228 | pout = poly_expand(o, [], size) 229 | for i, (c1, c2) in enumerate(zip(p1, p2)): 230 | pout[i] = c1.mod_add( c2, o) 231 | 232 | return pout 233 | 234 | def poly_mul(o, poly1, poly2): 235 | global zero 236 | p = [ zero ] 237 | for i, c1 in enumerate(poly1): 238 | p2 = ([ zero ] * i) + [(c1.mod_mul(c2, o)) for c2 in poly2] 239 | p = poly_add(o, p2, p) 240 | return p 241 | 242 | ################################################### 243 | # ---------------- TESTS ----------------------- # 244 | ################################################### 245 | 246 | import pytest 247 | 248 | def test_poly_expand(): 249 | ck = setup() 250 | (G, g, h, o) = ck 251 | p1 = [Bn(1), Bn(2)] 252 | p2 = poly_expand(o, p1, 10) 253 | assert len(p2) == 10 254 | assert p2[:2] == p1 255 | 256 | def test_poly_add(): 257 | ck = setup() 258 | (G, g, h, o) = ck 259 | p1 = [Bn(1), Bn(2)] 260 | p2 = poly_add(o, p1, p1) 261 | assert len(p2) == len(p1) 262 | assert p2 == [2, 4] 263 | 264 | def test_poly_mul(): 265 | ck = setup() 266 | (G, g, h, o) = ck 267 | p1 = [Bn(1), Bn(2)] 268 | p2 = poly_mul(o, p1, p1) 269 | assert p2 == [1, 4, 4] 270 | 271 | def test_setup(): 272 | ck = setup() 273 | 274 | def test_proof(): 275 | ck = setup() 276 | (G, g, h, o) = ck 277 | m, r = 1, o.random() 278 | c = Com(ck, m, r) 279 | ProveZeroOne(ck, c, m, r) 280 | 281 | @pytest.mark.parametrize("input,expected", [ 282 | (1, True), 283 | (0, True), 284 | (2, False), 285 | ]) 286 | def test_verify(input,expected): 287 | ck = setup() 288 | (G, g, h, o) = ck 289 | m, r = input, o.random() 290 | c = Com(ck, m, r) 291 | 292 | proof = ProveZeroOne(ck, c, m, r) 293 | assert VerifyZeroOne(ck, c, proof) == expected 294 | 295 | def test_prove_n(): 296 | ck = setup() 297 | (G, g, h, o) = ck 298 | c0 = Com(ck, 1, o.random()) 299 | c1 = Com(ck, 1, o.random()) 300 | c2 = Com(ck, 1, o.random()) 301 | c3 = Com(ck, 1, o.random()) 302 | r = o.random() 303 | cr = Com(ck,0, r) 304 | 305 | cis = [c0, c1, c2, c3, cr] 306 | proof = ProveOneOfN(ck, cis, 4, r, message="Hello World!") 307 | ret = VerifyOneOfN(ck, cis, proof, message="Hello World!") 308 | assert ret 309 | 310 | def notest_timing(upper=101): 311 | ck = setup() 312 | (G, g, h, o) = ck 313 | c0 = Com(ck, 1, o.random()) 314 | 315 | r = o.random() 316 | cr = Com(ck,0, r) 317 | 318 | import time 319 | 320 | 321 | repeats = 10 322 | 323 | all_sizes = range(10, upper, 10) 324 | prove_time = [] 325 | verify_time = [] 326 | for size in all_sizes: 327 | cis = [c0] * (size + 1) + [cr] 328 | 329 | t0 = time.clock() 330 | for _ in range(repeats): 331 | proof = ProveOneOfN(ck, cis, len(cis)-1, r, message="Hello World!") 332 | t1 = time.clock() 333 | 334 | dt = (t1-t0) / repeats 335 | prove_time += [ dt ] 336 | print( "Proof time: %s - %2.4f" % (size, dt) ) 337 | 338 | t0 = time.clock() 339 | for _ in range(repeats): 340 | ret = VerifyOneOfN(ck, cis, proof, message="Hello World!") 341 | assert ret 342 | t1 = time.clock() 343 | 344 | dt = (t1-t0) / repeats 345 | verify_time += [ dt ] 346 | print( "Verify time: %s - %2.4f" % (size, dt) ) 347 | 348 | return all_sizes, prove_time, verify_time 349 | 350 | 351 | if __name__ == "__main__": 352 | 353 | import argparse 354 | 355 | parser = argparse.ArgumentParser(description='Test and time the Tor median statistics.') 356 | parser.add_argument('--time', action='store_true', help='Run timing tests') 357 | parser.add_argument('--lprof', action='store_true', help='Run the line profiler') 358 | parser.add_argument('--cprof', action='store_true', help='Run the c profiler') 359 | parser.add_argument('--plot', action='store_true', help='Upload time plot to plotly') 360 | 361 | 362 | args = parser.parse_args() 363 | 364 | if args.time: 365 | notest_timing(31) 366 | 367 | 368 | if args.cprof: 369 | import cProfile 370 | cProfile.run("notest_timing(51)", sort="tottime") 371 | 372 | 373 | if args.lprof: 374 | from line_profiler import LineProfiler 375 | 376 | profile = LineProfiler(VerifyOneOfN, ProveOneOfN, Bn.__init__, Bn.__del__) 377 | profile.run("notest_timing(31)") 378 | profile.print_stats() 379 | 380 | 381 | if args.plot: 382 | 383 | all_sizes, prove_time, verify_time = notest_timing() 384 | 385 | import plotly.plotly as py 386 | from plotly.graph_objs import * 387 | 388 | trace0 = Scatter( 389 | x=all_sizes, 390 | y=prove_time, 391 | name='Proving', 392 | ) 393 | trace1 = Scatter( 394 | x=all_sizes, 395 | y=verify_time, 396 | name='Verification', 397 | ) 398 | data = Data([trace0, trace1]) 399 | 400 | layout = Layout( 401 | title='Timing for GK15 Proof and Verification using petlib', 402 | xaxis=XAxis( 403 | title='Size of ring (no. commits)', 404 | showgrid=False, 405 | zeroline=False 406 | ), 407 | yaxis=YAxis( 408 | title='time (sec)', 409 | showline=False 410 | ) 411 | ) 412 | fig = Figure(data=data, layout=layout) 413 | 414 | unique_url = py.plot(fig, filename = 'GK15-petlib-timing') -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # petlib documentation build configuration file, created by 4 | # sphinx-quickstart on Sun Nov 23 00:41:16 2014. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | import sys 16 | import os 17 | 18 | from mock import Mock as MagicMock 19 | 20 | try: 21 | import alabaster 22 | except: 23 | print("No alabaster theme found?") 24 | pass 25 | 26 | class Mock(MagicMock): 27 | @classmethod 28 | def __getattr__(cls, name): 29 | return Mock() 30 | 31 | MOCK_MODULES = ['cffi', 'pytest'] 32 | sys.modules.update((mod_name, Mock()) for mod_name in MOCK_MODULES) 33 | 34 | # If extensions (or modules to document with autodoc) are in another directory, 35 | # add these directories to sys.path here. If the directory is relative to the 36 | # documentation root, use os.path.abspath to make it absolute, like shown here. 37 | #sys.path.insert(0, os.path.abspath('.')) 38 | 39 | sys.path.insert(0,os.path.abspath(r"..")) 40 | import petlib 41 | 42 | # -- General configuration ------------------------------------------------ 43 | 44 | # If your documentation needs a minimal Sphinx version, state it here. 45 | #needs_sphinx = '1.0' 46 | 47 | # Add any Sphinx extension module names here, as strings. They can be 48 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 49 | # ones. 50 | try: 51 | import sphinxcontrib.napoleon 52 | extensions = [ 53 | 'alabaster', 54 | 'sphinx.ext.autodoc', 55 | 'sphinx.ext.coverage', 56 | 'sphinx.ext.mathjax', 57 | 'sphinx.ext.viewcode', 58 | 'sphinxcontrib.napoleon', 59 | ] 60 | except: 61 | extensions = [ 62 | 'alabaster', 63 | 'sphinx.ext.autodoc', 64 | 'sphinx.ext.coverage', 65 | 'sphinx.ext.mathjax', 66 | 'sphinx.ext.viewcode', 67 | ] 68 | 69 | 70 | # Add any paths that contain templates here, relative to this directory. 71 | templates_path = ['_templates'] 72 | 73 | # The suffix of source filenames. 74 | source_suffix = '.rst' 75 | 76 | # The encoding of source files. 77 | #source_encoding = 'utf-8-sig' 78 | 79 | # The master toctree document. 80 | master_doc = 'index' 81 | 82 | # General information about the project. 83 | project = u'petlib' 84 | copyright = u'2014, George Danezis' 85 | 86 | # The version info for the project you're documenting, acts as replacement for 87 | # |version| and |release|, also used in various other places throughout the 88 | # built documents. 89 | # 90 | # The short X.Y version. 91 | version = petlib.VERSION # '0.0.21' 92 | # The full version, including alpha/beta/rc tags. 93 | release = petlib.VERSION # '0.0.21' 94 | 95 | # The language for content autogenerated by Sphinx. Refer to documentation 96 | # for a list of supported languages. 97 | #language = None 98 | 99 | # There are two options for replacing |today|: either, you set today to some 100 | # non-false value, then it is used: 101 | #today = '' 102 | # Else, today_fmt is used as the format for a strftime call. 103 | #today_fmt = '%B %d, %Y' 104 | 105 | # List of patterns, relative to source directory, that match files and 106 | # directories to ignore when looking for source files. 107 | exclude_patterns = ['_build'] 108 | 109 | # The reST default role (used for this markup: `text`) to use for all 110 | # documents. 111 | #default_role = None 112 | 113 | # If true, '()' will be appended to :func: etc. cross-reference text. 114 | #add_function_parentheses = True 115 | 116 | # If true, the current module name will be prepended to all description 117 | # unit titles (such as .. function::). 118 | #add_module_names = True 119 | 120 | # If true, sectionauthor and moduleauthor directives will be shown in the 121 | # output. They are ignored by default. 122 | #show_authors = False 123 | 124 | # The name of the Pygments (syntax highlighting) style to use. 125 | pygments_style = 'sphinx' 126 | 127 | # A list of ignored prefixes for module index sorting. 128 | #modindex_common_prefix = [] 129 | 130 | # If true, keep warnings as "system message" paragraphs in the built documents. 131 | #keep_warnings = False 132 | 133 | 134 | # -- Options for HTML output ---------------------------------------------- 135 | 136 | # The theme to use for HTML and HTML Help pages. See the documentation for 137 | # a list of builtin themes. 138 | html_theme = 'alabaster' 139 | 140 | # Theme options are theme-specific and customize the look and feel of a theme 141 | # further. For a list of options available for each theme, see the 142 | # documentation. 143 | #html_theme_options = {} 144 | 145 | # Add any paths that contain custom themes here, relative to this directory. 146 | #html_theme_path = [] 147 | 148 | # The name for this set of Sphinx documents. If None, it defaults to 149 | # " v documentation". 150 | #html_title = None 151 | 152 | # A shorter title for the navigation bar. Default is the same as html_title. 153 | #html_short_title = None 154 | 155 | # The name of an image file (relative to this directory) to place at the top 156 | # of the sidebar. 157 | #html_logo = None 158 | 159 | # The name of an image file (within the static path) to use as favicon of the 160 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 161 | # pixels large. 162 | #html_favicon = None 163 | 164 | # Add any paths that contain custom static files (such as style sheets) here, 165 | # relative to this directory. They are copied after the builtin static files, 166 | # so a file named "default.css" will overwrite the builtin "default.css". 167 | html_static_path = ['_static'] 168 | 169 | # Add any extra paths that contain custom files (such as robots.txt or 170 | # .htaccess) here, relative to this directory. These files are copied 171 | # directly to the root of the documentation. 172 | #html_extra_path = [] 173 | 174 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 175 | # using the given strftime format. 176 | #html_last_updated_fmt = '%b %d, %Y' 177 | 178 | # If true, SmartyPants will be used to convert quotes and dashes to 179 | # typographically correct entities. 180 | #html_use_smartypants = True 181 | 182 | # Custom sidebar templates, maps document names to template names. 183 | #html_sidebars = {} 184 | 185 | # Additional templates that should be rendered to pages, maps page names to 186 | # template names. 187 | #html_additional_pages = {} 188 | 189 | # If false, no module index is generated. 190 | #html_domain_indices = True 191 | 192 | # If false, no index is generated. 193 | #html_use_index = True 194 | 195 | # If true, the index is split into individual pages for each letter. 196 | #html_split_index = False 197 | 198 | # If true, links to the reST sources are added to the pages. 199 | #html_show_sourcelink = True 200 | 201 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 202 | #html_show_sphinx = True 203 | 204 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 205 | #html_show_copyright = True 206 | 207 | # If true, an OpenSearch description file will be output, and all pages will 208 | # contain a tag referring to it. The value of this option must be the 209 | # base URL from which the finished HTML is served. 210 | #html_use_opensearch = '' 211 | 212 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 213 | #html_file_suffix = None 214 | 215 | # Output file base name for HTML help builder. 216 | htmlhelp_basename = 'petlibdoc' 217 | 218 | 219 | # -- Options for LaTeX output --------------------------------------------- 220 | 221 | latex_elements = { 222 | # The paper size ('letterpaper' or 'a4paper'). 223 | 'papersize': 'a4paper', 224 | 225 | # The font size ('10pt', '11pt' or '12pt'). 226 | #'pointsize': '10pt', 227 | 228 | # Additional stuff for the LaTeX preamble. 229 | 'preamble': '\setcounter{tocdepth}{2}', 230 | } 231 | 232 | # Grouping the document tree into LaTeX files. List of tuples 233 | # (source start file, target name, title, 234 | # author, documentclass [howto, manual, or own class]). 235 | latex_documents = [ 236 | ('index', 'petlib.tex', u'petlib Documentation', 237 | u'George Danezis', 'manual'), 238 | ] 239 | 240 | # The name of an image file (relative to this directory) to place at the top of 241 | # the title page. 242 | #latex_logo = None 243 | 244 | # For "manual" documents, if this is true, then toplevel headings are parts, 245 | # not chapters. 246 | #latex_use_parts = False 247 | 248 | # If true, show page references after internal links. 249 | #latex_show_pagerefs = False 250 | 251 | # If true, show URL addresses after external links. 252 | #latex_show_urls = False 253 | 254 | # Documents to append as an appendix to all manuals. 255 | #latex_appendices = [] 256 | 257 | # If false, no module index is generated. 258 | #latex_domain_indices = True 259 | 260 | 261 | # -- Options for manual page output --------------------------------------- 262 | 263 | # One entry per manual page. List of tuples 264 | # (source start file, name, description, authors, manual section). 265 | man_pages = [ 266 | ('index', 'petlib', u'petlib Documentation', 267 | [u'George Danezis'], 1) 268 | ] 269 | 270 | # If true, show URL addresses after external links. 271 | #man_show_urls = False 272 | 273 | 274 | # -- Options for Texinfo output ------------------------------------------- 275 | 276 | # Grouping the document tree into Texinfo files. List of tuples 277 | # (source start file, target name, title, author, 278 | # dir menu entry, description, category) 279 | texinfo_documents = [ 280 | ('index', 'petlib', u'petlib Documentation', 281 | u'George Danezis', 'petlib', 'One line description of project.', 282 | 'Miscellaneous'), 283 | ] 284 | 285 | # Documents to append as an appendix to all manuals. 286 | #texinfo_appendices = [] 287 | 288 | # If false, no module index is generated. 289 | #texinfo_domain_indices = True 290 | 291 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 292 | #texinfo_show_urls = 'footnote' 293 | 294 | # If true, do not generate a @detailmenu in the "Top" node's menu. 295 | #texinfo_no_detailmenu = False 296 | 297 | 298 | # -- Options for Epub output ---------------------------------------------- 299 | 300 | # Bibliographic Dublin Core info. 301 | epub_title = u'petlib' 302 | epub_author = u'George Danezis' 303 | epub_publisher = u'George Danezis' 304 | epub_copyright = u'2014, George Danezis' 305 | 306 | # The basename for the epub file. It defaults to the project name. 307 | #epub_basename = u'petlib' 308 | 309 | # The HTML theme for the epub output. Since the default themes are not optimized 310 | # for small screen space, using the same theme for HTML and epub output is 311 | # usually not wise. This defaults to 'epub', a theme designed to save visual 312 | # space. 313 | #epub_theme = 'epub' 314 | 315 | # The language of the text. It defaults to the language option 316 | # or en if the language is not set. 317 | #epub_language = '' 318 | 319 | # The scheme of the identifier. Typical schemes are ISBN or URL. 320 | #epub_scheme = '' 321 | 322 | # The unique identifier of the text. This can be a ISBN number 323 | # or the project homepage. 324 | #epub_identifier = '' 325 | 326 | # A unique identification for the text. 327 | #epub_uid = '' 328 | 329 | # A tuple containing the cover image and cover page html template filenames. 330 | #epub_cover = () 331 | 332 | # A sequence of (type, uri, title) tuples for the guide element of content.opf. 333 | #epub_guide = () 334 | 335 | # HTML files that should be inserted before the pages created by sphinx. 336 | # The format is a list of tuples containing the path and title. 337 | #epub_pre_files = [] 338 | 339 | # HTML files shat should be inserted after the pages created by sphinx. 340 | # The format is a list of tuples containing the path and title. 341 | #epub_post_files = [] 342 | 343 | # A list of files that should not be packed into the epub file. 344 | epub_exclude_files = ['search.html'] 345 | 346 | # The depth of the table of contents in toc.ncx. 347 | #epub_tocdepth = 3 348 | 349 | # Allow duplicate toc entries. 350 | #epub_tocdup = True 351 | 352 | # Choose between 'default' and 'includehidden'. 353 | #epub_tocscope = 'default' 354 | 355 | # Fix unsupported image types using the PIL. 356 | #epub_fix_images = False 357 | 358 | # Scale large images. 359 | #epub_max_image_width = 0 360 | 361 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 362 | #epub_show_urls = 'inline' 363 | 364 | # If false, no index is generated. 365 | #epub_use_index = True 366 | 367 | # Group methods by source file order 368 | autodoc_member_order = 'bysource' -------------------------------------------------------------------------------- /examples/BLcred.py: -------------------------------------------------------------------------------- 1 | # The Baldimtsi-Lysyanskaya Anonymous Credentials Light scheme 2 | # See: 3 | # Baldimtsi, Foteini, and Anna Lysyanskaya. "Anonymous credentials light." 4 | # Proceedings of the 2013 ACM SIGSAC conference on Computer & communications security. 5 | # ACM, 2013. 6 | 7 | from hashlib import sha256 8 | from base64 import b64encode 9 | 10 | import pytest 11 | 12 | from petlib.bn import Bn 13 | from petlib.ec import EcGroup, EcPt 14 | 15 | from genzkp import * 16 | 17 | class StateHolder(object): 18 | pass 19 | 20 | def BL_setup(Gid = 713): 21 | # Parameters of the BL schemes 22 | G = EcGroup(713) 23 | q = G.order() 24 | 25 | g = G.hash_to_point(b"g") 26 | h = G.hash_to_point(b"h") 27 | z = G.hash_to_point(b"z") 28 | hs = [G.hash_to_point(("h%s" % i).encode("utf8")) for i in range(100)] 29 | 30 | return (G, q, g, h, z, hs) 31 | 32 | def BL_user_setup(params, attributes): 33 | (G, q, g, h, z, hs) = params 34 | 35 | # Inputs from user 36 | R = q.random() 37 | C = R * hs[0] # + L1 * hs[1] + L2 * hs[2] 38 | 39 | for (i, attrib_i) in enumerate(attributes): 40 | C = C + attrib_i * hs[1+i] 41 | 42 | user_state = StateHolder() 43 | user_state.params = params 44 | user_state.attributes = attributes 45 | user_state.C = C 46 | user_state.R = R 47 | 48 | return user_state, (C, ) 49 | 50 | def BL_issuer_keys(params): 51 | (G, q, g, h, z, hs) = params 52 | 53 | x = q.random() 54 | y = x * g 55 | 56 | issuer_state = StateHolder() 57 | issuer_state.params = params 58 | issuer_state.x = x 59 | issuer_state.y = y 60 | 61 | return issuer_state, (y, ) 62 | 63 | 64 | def BL_issuer_preparation(issuer_state, user_commit): 65 | (G, q, g, h, z, hs) = issuer_state.params 66 | (x, y) = (issuer_state.x, issuer_state.y) 67 | 68 | (C, ) = user_commit 69 | 70 | # Preparation 71 | rnd = q.random() 72 | z1 = C + rnd * g 73 | z2 = z + (-z1) 74 | 75 | ## Send: (rnd,) to user 76 | if rnd % q == 0: 77 | raise 78 | 79 | issuer_state.rnd = rnd 80 | issuer_state.z1 = z1 81 | issuer_state.z2 = z2 82 | 83 | message_to_user = (rnd, ) 84 | 85 | return message_to_user 86 | 87 | def BL_user_preparation(user_state, msg_from_issuer): 88 | (G, q, g, h, z, hs) = user_state.params 89 | (rnd, ) = msg_from_issuer 90 | C = user_state.C 91 | 92 | z1 = C + rnd * g 93 | gam = q.random() 94 | zet = gam * z 95 | zet1 = gam * z1 96 | zet2 = zet + (-zet1) 97 | tau = q.random() 98 | eta = tau * z 99 | 100 | user_state.z1 = z1 101 | user_state.gam = gam 102 | user_state.zet = zet 103 | user_state.zet1 = zet1 104 | user_state.zet2 = zet2 105 | user_state.tau = tau 106 | user_state.eta = eta 107 | 108 | user_state.rnd = rnd 109 | 110 | def BL_issuer_validation(issuer_state): 111 | (G, q, g, h, z, hs) = issuer_state.params 112 | 113 | u, r1p, r2p, cp = [q.random() for _ in range(4)] 114 | a = u * g 115 | a1p = r1p * g + cp * issuer_state.z1 116 | a2p = r2p * h + cp * issuer_state.z2 117 | 118 | issuer_state.u = u 119 | issuer_state.r1p = r1p 120 | issuer_state.r2p = r2p 121 | issuer_state.cp = cp 122 | 123 | return (a, a1p, a2p) 124 | 125 | def BL_user_validation(user_state, issuer_pub, msg_to_user, message=b''): 126 | (G, q, g, h, z, hs) = user_state.params 127 | # (z1, gam, zet, zet1, zet2, tau, eta) = user_private_state 128 | (a, a1p, a2p) = msg_to_user 129 | (y,) = issuer_pub 130 | 131 | assert G.check_point(a) 132 | assert G.check_point(a1p) 133 | assert G.check_point(a2p) 134 | 135 | t1,t2,t3,t4,t5 = [q.random() for _ in range(5)] 136 | alph = a + t1 * g + t2 * y 137 | alph1 = user_state.gam * a1p + t3 * g + t4 * user_state.zet1 138 | alph2 = user_state.gam * a2p + t5 * h + t4 * user_state.zet2 139 | 140 | # Make epsilon 141 | H = [user_state.zet, user_state.zet1, alph, alph1, alph2, user_state.eta] 142 | Hstr = list(map(EcPt.export, H)) + [message] 143 | Hhex = b"|".join(map(b64encode, Hstr)) 144 | epsilon = Bn.from_binary(sha256(Hhex).digest()) % q 145 | 146 | e = epsilon.mod_sub(t2,q).mod_sub(t4, q) 147 | 148 | user_state.ts = [t1,t2,t3,t4,t5] 149 | user_state.message = message 150 | 151 | msg_to_issuer = e 152 | return msg_to_issuer 153 | 154 | def BL_issuer_validation_2(issuer_state, msg_from_user): 155 | (G, q, g, h, z, hs) = issuer_state.params 156 | # x, y = key_pair 157 | # (u, r1p, r2p, cp) = issuer_val_private 158 | e = msg_from_user 159 | 160 | ## Send: (e,) to Issuer 161 | c = e.mod_sub(issuer_state.cp, q) 162 | r = issuer_state.u.mod_sub((c * issuer_state.x), q) 163 | 164 | msg_to_user = (c, r, issuer_state.cp, issuer_state.r1p, issuer_state.r2p) 165 | return msg_to_user 166 | 167 | def BL_user_validation2(user_state, msg_from_issuer): 168 | (G, q, g, h, z, hs) = user_state.params 169 | (c, r, cp, r1p, r2p) = msg_from_issuer 170 | (t1,t2,t3,t4,t5), m = user_state.ts, user_state.message 171 | 172 | # (z1, gam, zet, zet1, zet2, tau, eta) = user_private_state 173 | 174 | gam = user_state.gam 175 | 176 | ro = r.mod_add(t1,q) 177 | om = c.mod_add(t2,q) 178 | ro1p = (gam * r1p + t3) % q 179 | ro2p = (gam * r2p + t5) % q 180 | omp = (cp + t4) % q 181 | mu = (user_state.tau - omp * gam) % q 182 | 183 | signature = (m, user_state.zet, 184 | user_state.zet1, 185 | user_state.zet2, om, omp, ro, ro1p, ro2p, mu) 186 | 187 | return signature 188 | 189 | def BL_check_signature(params, issuer_pub, signature): 190 | (G, q, g, h, z, hs) = params 191 | (y,) = issuer_pub 192 | (m, zet, zet1, zet2, om, omp, ro, ro1p, ro2p, mu) = signature 193 | 194 | lhs = (om + omp) % q 195 | rhs_h = [zet, zet1, 196 | ro * g + om * y, 197 | ro1p * g + omp * zet1, 198 | ro2p * h + omp * zet2, ## problem 199 | mu * z + omp * zet] 200 | 201 | Hstr = list(map(EcPt.export, rhs_h)) + [m] 202 | Hhex = b"|".join(map(b64encode, Hstr)) 203 | rhs = Bn.from_binary(sha256(Hhex).digest()) % q 204 | 205 | if rhs == lhs: 206 | return m 207 | else: 208 | return False 209 | 210 | def BL_cred_proof(user_state): 211 | (G, q, g, h, z, hs) = user_state.params 212 | gam = user_state.gam 213 | 214 | assert user_state.zet == user_state.gam * z 215 | gam_hs = [gam * hsi for hsi in hs] 216 | gam_g = gam * g 217 | 218 | Cnew = user_state.rnd * gam_g + user_state.R * gam_hs[0] 219 | for i, attr in enumerate(user_state.attributes): 220 | Cnew = Cnew + attr * gam_hs[1+i] 221 | 222 | assert Cnew == user_state.zet1 223 | 224 | def BL_show_zk_proof(params, num_attrib): 225 | (G, _, _, _, _, _) = params 226 | 227 | # Contruct the proof 228 | zk = ZKProof(G) 229 | 230 | ## The variables 231 | 232 | gam, rnd, R = zk.get(Sec, ["gam", "rnd", "R"]) 233 | attrib = zk.get_array(Sec, "attrib", num_attrib, 0) 234 | 235 | g, z, zet, zet1 = zk.get(ConstGen, ["g", "z", "zet", "zet1"]) 236 | hs = zk.get_array(ConstGen, "hs", num_attrib+1, 0) 237 | 238 | zk.add_proof(zet, gam * z) 239 | 240 | gam_g = zk.get(Gen, "gamg") 241 | zk.add_proof(gam_g, gam * g) 242 | 243 | gam_hs = zk.get_array(Gen, "gamhs", num_attrib+1, 0) 244 | 245 | for gam_hsi, hsi in zip(gam_hs, hs): 246 | zk.add_proof(gam_hsi, gam * hsi) 247 | 248 | Cnew = rnd * gam_g + R * gam_hs[0] 249 | for i, attr in enumerate(attrib): 250 | Cnew = Cnew + attr * gam_hs[1+i] 251 | 252 | zk.add_proof(zet1, Cnew) 253 | return zk 254 | 255 | def BL_user_prove_cred(user_state): 256 | (G, q, g, h, z, hs) = user_state.params 257 | zk = BL_show_zk_proof(user_state.params, len(user_state.attributes)) 258 | 259 | env = ZKEnv(zk) 260 | 261 | # The secrets 262 | env.gam = user_state.gam 263 | env.rnd = user_state.rnd 264 | env.R = user_state.R 265 | env.attrib = user_state.attributes 266 | 267 | # Constants 268 | env.g = g 269 | env.z = z 270 | env.zet = user_state.zet 271 | env.zet1 = user_state.zet1 272 | env.hs = hs[:len(user_state.attributes) + 1] 273 | 274 | # The stored generators 275 | env.gamg = user_state.gam * g 276 | env.gamhs = gam_hs = [user_state.gam * hsi for hsi in hs[:len(user_state.attributes) + 1]] 277 | 278 | ## Extract the proof 279 | sig = zk.build_proof(env.get()) 280 | if __debug__: 281 | assert zk.verify_proof(env.get(), sig, strict=False) 282 | 283 | return sig 284 | 285 | def BL_verify_cred(params, issuer_pub, num_attributes, signature, sig): 286 | m = BL_check_signature(params, issuer_pub, signature) 287 | assert m != False 288 | 289 | (G, q, g, h, z, hs) = params 290 | (m, zet, zet1, zet2, om, omp, ro, ro1p, ro2p, mu) = signature 291 | 292 | zk = BL_show_zk_proof(params, num_attributes) 293 | 294 | env = ZKEnv(zk) 295 | 296 | # Constants 297 | env.g = g 298 | env.z = z 299 | env.zet = zet 300 | env.zet1 = zet1 301 | env.hs = hs[:num_attributes + 1] 302 | 303 | ## Extract the proof 304 | res = zk.verify_proof(env.get(), sig) 305 | assert res 306 | 307 | return m 308 | 309 | def test_modular(): 310 | # Establish the global parameters 311 | params = BL_setup() 312 | 313 | # Generate a key pair (inc. public key) for the issuer 314 | LT_issuer_state, issuer_pub = BL_issuer_keys(params) 315 | LT_user_state, user_commit = BL_user_setup(params, [10, 20]) 316 | 317 | # Preparation phase 318 | msg_to_user = BL_issuer_preparation(LT_issuer_state, user_commit) 319 | BL_user_preparation(LT_user_state, msg_to_user) 320 | 321 | # Validation phase 322 | msg_to_user = BL_issuer_validation(LT_issuer_state) 323 | msg_to_issuer = epsilon = BL_user_validation(LT_user_state, issuer_pub, msg_to_user) 324 | 325 | msg_to_user = BL_issuer_validation_2(LT_issuer_state, msg_to_issuer) 326 | signature = BL_user_validation2(LT_user_state, msg_to_user) 327 | 328 | # Build a ZK proof of a valid signature 329 | sig = BL_user_prove_cred(LT_user_state) 330 | 331 | # Check signature and ZK proof 332 | m = BL_verify_cred(params, issuer_pub, 2, signature, sig) 333 | assert m != False 334 | 335 | 336 | def test_protocol(): 337 | # Parameters of the BL schemes 338 | G = EcGroup(713) 339 | q = G.order() 340 | 341 | g = G.hash_to_point(b"g") 342 | h = G.hash_to_point(b"h") 343 | z = G.hash_to_point(b"z") 344 | hs = [G.hash_to_point(("h%s" % i).encode("utf8")) for i in range(100)] 345 | 346 | # Inputs from user 347 | R = q.random() 348 | L1 = 10 349 | L2 = 20 350 | C = R * hs[0] + L1 * hs[1] + L2 * hs[2] 351 | m = b"Hello World!" 352 | 353 | # Inputs from the Issuer 354 | # TODO: check ZK on C 355 | x = q.random() 356 | y = x * g 357 | 358 | # Preparation 359 | rnd = q.random() 360 | z1 = C + rnd * g 361 | z2 = z + (-z1) 362 | 363 | ## Send: (rnd,) to user 364 | if rnd % q == 0: 365 | raise 366 | 367 | z1 = C + rnd * g 368 | gam = q.random() 369 | zet = gam * z 370 | zet1 = gam * z1 371 | zet2 = zet + (-zet1) 372 | tau = q.random() 373 | eta = tau * z 374 | 375 | # Validation: Issuer 376 | u, r1p, r2p, cp = [q.random() for _ in range(4)] 377 | a = u * g 378 | a1p = r1p * g + cp * z1 379 | a2p = r2p * h + cp * z2 380 | 381 | ## Send(a, ap = (a1p, a2p)) 382 | # User side 383 | 384 | assert G.check_point(a) 385 | assert G.check_point(a1p) 386 | assert G.check_point(a2p) 387 | 388 | t1,t2,t3,t4,t5 = [q.random() for _ in range(5)] 389 | alph = a + t1 * g + t2 * y 390 | alph1 = gam * a1p + t3 * g + t4 * zet1 391 | alph2 = gam * a2p + t5 * h + t4 * zet2 392 | 393 | # Make epsilon 394 | H = [zet, zet1, alph, alph1, alph2, eta] 395 | Hstr = list(map(EcPt.export, H)) + [m] 396 | Hhex = b"|".join(map(b64encode, Hstr)) 397 | epsilon = Bn.from_binary(sha256(Hhex).digest()) % q 398 | 399 | e = epsilon.mod_sub(t2,q).mod_sub(t4, q) 400 | 401 | ## Send: (e,) to Issuer 402 | c = e.mod_sub(cp, q) 403 | r = u.mod_sub((c * x), q) 404 | 405 | ## Send: (c,r, cp, rp = (r1p, r2p)) to User 406 | ro = r.mod_add(t1,q) 407 | om = c.mod_add(t2,q) 408 | ro1p = (gam * r1p + t3) % q 409 | ro2p = (gam * r2p + t5) % q 410 | omp = (cp + t4) % q 411 | mu = (tau - omp * gam) % q 412 | 413 | signature = (m, zet, zet1, zet2, om, omp, ro, ro1p, ro2p) 414 | 415 | # Check verification equation 416 | lhs = (om + omp) % q 417 | rhs_h = [zet, zet1, 418 | ro * g + om * y, 419 | ro1p * g + omp * zet1, 420 | ro2p * h + omp * zet2, ## problem 421 | mu * z + omp * zet] 422 | 423 | Hstr = list(map(EcPt.export, rhs_h)) + [m] 424 | Hhex = b"|".join(map(b64encode, Hstr)) 425 | rhs = Bn.from_binary(sha256(Hhex).digest()) % q 426 | 427 | # Check the (future) ZK proof 428 | assert zet == gam * z 429 | gam_hs = [gam * hsi for hsi in hs] 430 | gam_g = gam * g 431 | assert rnd * gam_g + R * gam_hs[0] + L1 * gam_hs[1] + L2 * gam_hs[2] == zet1 432 | 433 | 434 | print(rhs == lhs) 435 | -------------------------------------------------------------------------------- /contrib/cred_server.py: -------------------------------------------------------------------------------- 1 | # This is a simple implementation of a credential server using 2 | # the python 3 async framework. It is meant to be used as a 3 | # backend to generate and clear credentials. 4 | 5 | # The general imports 6 | 7 | import sys 8 | sys.path += ["../examples"] 9 | 10 | import asyncio 11 | from io import BytesIO 12 | from msgpack import Unpacker 13 | 14 | from petlib.pack import encode, decode, make_encoder, make_decoder 15 | 16 | class SReader(): 17 | """ Define an asyncio msgpack stream decoder. """ 18 | 19 | def __init__(self, reader, writer): 20 | """ Pass ina stream reader to unmarshall msgpack objects from. """ 21 | self.reader = reader 22 | self.writer = writer 23 | self.decoder = make_decoder() 24 | self.unpacker = Unpacker(ext_hook=self.decoder, encoding="utf8") 25 | self.obj_buf = [] 26 | 27 | 28 | @asyncio.coroutine 29 | def get(self): 30 | """ The co-routine providing objects. """ 31 | 32 | while len(self.obj_buf) == 0: 33 | buf = yield from self.reader.read(1000) 34 | 35 | self.unpacker.feed(buf) 36 | for o in self.unpacker: 37 | self.obj_buf.append(o) 38 | 39 | return self.obj_buf.pop(0) 40 | 41 | 42 | def put(self, obj): 43 | """ Write an object to the channel. """ 44 | self.writer.write(encode(obj)) 45 | 46 | 47 | ## The crypto imports 48 | 49 | from amacscreds import cred_setup, cred_CredKeyge, cred_UserKeyge, cred_secret_issue_user, cred_secret_issue, cred_secret_issue_user_decrypt, cred_show, cred_show_check, cred_secret_issue_user_check 50 | from genzkp import * 51 | from petlib.pack import encode, decode 52 | 53 | 54 | class CredentialServer(): 55 | def __init__(self, num_pub = 3, num_priv = 1): 56 | self.n = num_pub + num_priv 57 | self.params = cred_setup() 58 | self.ipub, self.isec = cred_CredKeyge(self.params, self.n) 59 | 60 | 61 | @asyncio.coroutine 62 | def handle_cmd(self, reader, writer): 63 | try: 64 | sr = SReader(reader, writer) 65 | 66 | CMD = yield from sr.get() 67 | 68 | print("Executing: %s" % CMD) 69 | if CMD == "INFO": 70 | yield from self.handle_info(sr) 71 | elif CMD == "ISSUE": 72 | yield from self.handle_issue(sr) 73 | elif CMD == "SHOW": 74 | yield from self.handle_show(sr) 75 | 76 | except Exception as e: 77 | print(e) 78 | sr.put("Error") 79 | 80 | 81 | @asyncio.coroutine 82 | def handle_info(self, sr): 83 | try: 84 | # Part 1. First we write the params and the ipub values 85 | sr.put( (self.params, self.ipub) ) 86 | 87 | except Exception as e: 88 | print(e) 89 | sr.put("Error") 90 | 91 | finally: 92 | sr.writer.close() 93 | 94 | 95 | @asyncio.coroutine 96 | def handle_issue(self, sr): 97 | 98 | try: 99 | 100 | # Part 2. Get the public key an Encrypted cred from user 101 | (pub, EGenc, sig_u), public_attr = yield from sr.get() 102 | k, v, timeout = public_attr 103 | 104 | # Part 3. Check and generate the credential 105 | if not cred_secret_issue_user_check(self.params, pub, EGenc, sig_u): 106 | raise Exception("Error: Issuing checks failed") 107 | 108 | cred_issued = cred_secret_issue(self.params, pub, EGenc, self.ipub, self.isec, public_attr) 109 | sr.put(cred_issued) 110 | 111 | except Exception as e: 112 | print(e) 113 | sr.put("Error") 114 | 115 | 116 | @asyncio.coroutine 117 | def handle_show(self, sr): 118 | 119 | try: 120 | (G, g, h, o) = self.params 121 | 122 | # Part 4. Get a blinded credential & check it 123 | creds, sig_o, sig_openID, Service_name, Uid, public_attr = yield from sr.get() 124 | (u, Cmis, Cup) = creds 125 | 126 | [ key, value, timeout ] = public_attr 127 | 128 | if not cred_show_check(self.params, self.ipub, self.isec, creds, sig_o): 129 | raise Exception("Error: aMAC failed") 130 | 131 | # Execute the verification on the proof 'sig' 132 | Gid = G.hash_to_point(Service_name) 133 | 134 | zk = define_proof(G) 135 | env2 = ZKEnv(zk) 136 | env2.u, env2.h = u, h 137 | env2.Cm0p = Cmis[0] - (key * u) 138 | env2.Cm1p = Cmis[1] - (value * u) 139 | env2.Cm2p = Cmis[2] - (timeout * u) 140 | 141 | env2.Cm3 = Cmis[3] 142 | 143 | assert len(Cmis) == 4 144 | env2.Uid, env2.Gid = Uid, Gid 145 | 146 | if not zk.verify_proof(env2.get(), sig_openID): 147 | raise Exception("Error: ZKP failed") 148 | 149 | sr.put("SUCCESS") 150 | 151 | yield from sr.writer.drain() 152 | sr.writer.close() 153 | 154 | except Exception as e: 155 | import traceback 156 | traceback.print_exc() 157 | 158 | print(e) 159 | sr.put("Error") 160 | 161 | 162 | def define_proof(G): 163 | zk = ZKProof(G) 164 | u, h = zk.get(ConstGen, ["u", "h"]) 165 | LT_ID, z0, z1, z2, z3 = zk.get(Sec, ["LT_ID", "z0", "z1", "z2", "z3"]) 166 | Cm0p = zk.get(ConstGen, "Cm0p") 167 | Cm1p = zk.get(ConstGen, "Cm1p") 168 | Cm2p = zk.get(ConstGen, "Cm2p") 169 | 170 | Cm3 = zk.get(ConstGen, "Cm3") 171 | Uid = zk.get(ConstGen, "Uid") 172 | Gid = zk.get(ConstGen, "Gid") 173 | 174 | zk.add_proof(Cm0p, z0 * h) 175 | zk.add_proof(Cm1p, z1 * h) 176 | zk.add_proof(Cm2p, z2 * h) 177 | 178 | zk.add_proof(Cm3, LT_ID*u + z3 * h) 179 | zk.add_proof(Uid, LT_ID * Gid) 180 | 181 | return zk 182 | 183 | 184 | import pytest # requires pytest-asyncio! 185 | 186 | @asyncio.coroutine 187 | def info_client(ip, port, loop): 188 | """ Implement a client for the INFO command. """ 189 | 190 | ## Setup the channel 191 | reader, writer = yield from asyncio.open_connection( 192 | ip, port, loop=loop) 193 | sr = SReader(reader, writer) 194 | 195 | # Send the FULL command 196 | sr.put("INFO") 197 | 198 | # Part 1. Get the params and the ipub 199 | (params, ipub) = yield from sr.get() 200 | (G, g, h, o) = params 201 | 202 | return params, ipub 203 | 204 | import time 205 | 206 | @asyncio.coroutine 207 | def issue_client(ip, port, params, ipub, keypair, public_attr, private_attr, loop, repeat=1): 208 | """ Implements a client for the ISSUE protocol. """ 209 | 210 | # Part 2. Send the encrypted attributes to server 211 | user_token = cred_secret_issue_user(params, keypair, private_attr) 212 | (pub, EGenc, sig_u) = user_token 213 | 214 | t0 = time.monotonic() 215 | for _ in range(repeat): 216 | ## Setup the channel 217 | reader, writer = yield from asyncio.open_connection( 218 | ip, port, loop=loop) 219 | sr = SReader(reader, writer) 220 | 221 | # Send the FULL command 222 | sr.put("ISSUE") 223 | 224 | sr.put( (user_token, public_attr) ) 225 | 226 | # Part 3. Get the credential back 227 | cred = yield from sr.get() 228 | t1 = time.monotonic() 229 | if repeat > 1: 230 | print("CORE ISSUE time (1): %.3f sec (repeat=%s)" % ((t1-t0) / repeat, repeat)) 231 | 232 | 233 | (u, EncE, sig_s) = cred 234 | mac = cred_secret_issue_user_decrypt(params, keypair, u, EncE, ipub, public_attr, EGenc, sig_s) 235 | 236 | return mac, user_token, cred 237 | 238 | @asyncio.coroutine 239 | def show_client(ip, port, params, ipub, mac, sig_s, public_attr, private_attr, Service_name, loop, repeat=1): 240 | """ Implements a client for the SHOW command. """ 241 | 242 | # Part 1. Get the params and the ipub 243 | (G, g, h, o) = params 244 | 245 | ## User Shows back full credential to issuer 246 | (creds, sig_o, zis) = cred_show(params, ipub, mac, sig_s, public_attr + private_attr, export_zi=True) 247 | 248 | [ LT_user_ID ] = private_attr 249 | [ key, value, timeout ] = public_attr 250 | 251 | ## The credential contains a number of commitments to the attributes 252 | (u, Cmis, Cup) = creds 253 | 254 | assert len(Cmis) == 4 255 | assert Cmis[0] == key * u + zis[0] * h 256 | assert Cmis[1] == value * u + zis[1] * h 257 | assert Cmis[2] == timeout * u + zis[2] * h 258 | 259 | assert Cmis[3] == LT_user_ID * u + zis[3] * h 260 | 261 | # Derive a service specific User ID 262 | Gid = G.hash_to_point(Service_name) 263 | Uid = LT_user_ID * Gid 264 | 265 | # Define the statements to be proved 266 | zk = define_proof(G) 267 | 268 | # Define the proof environemnt 269 | env = ZKEnv(zk) 270 | env.u, env.h = u, h 271 | 272 | env.Cm0p = Cmis[0] - (key * u) 273 | env.Cm1p = Cmis[1] - (value * u) 274 | env.Cm2p = Cmis[2] - (timeout * u) 275 | 276 | env.Cm3 = Cmis[3] 277 | 278 | env.Uid, env.Gid = Uid, Gid 279 | env.LT_ID = LT_user_ID 280 | env.z0, env.z1, env.z2, env.z3 = zis[0], zis[1], zis[2], zis[3] 281 | 282 | sig_openID = zk.build_proof(env.get()) 283 | 284 | t0 = time.monotonic() 285 | for _ in range(repeat): 286 | reader, writer = yield from asyncio.open_connection( 287 | ip, port, loop=loop) 288 | sr = SReader(reader, writer) 289 | 290 | # Send the FULL command 291 | sr.put("SHOW") 292 | sr.put( (creds, sig_o, sig_openID, Service_name, Uid , public_attr) ) 293 | 294 | # Check status 295 | resp = yield from sr.get() 296 | writer.close() 297 | t1 = time.monotonic() 298 | if repeat > 1: 299 | print("CORE SHOW time (1): %.3f sec (repeat=%s)" % ((t1-t0) / repeat, repeat)) 300 | 301 | 302 | return resp 303 | 304 | 305 | def test_info_server(event_loop, unused_tcp_port): 306 | cs = CredentialServer() 307 | coro = asyncio.start_server(cs.handle_cmd, 308 | '127.0.0.1', unused_tcp_port, loop=event_loop) 309 | 310 | event_loop.create_task(coro) 311 | resp = event_loop.run_until_complete(info_client('127.0.0.1', unused_tcp_port, event_loop)) 312 | 313 | assert tuple(resp[0]) == tuple(cs.params) 314 | 315 | 316 | def test_issue_server(event_loop, unused_tcp_port): 317 | cs = CredentialServer() 318 | coro = asyncio.start_server(cs.handle_cmd, 319 | '127.0.0.1', unused_tcp_port, loop=event_loop) 320 | 321 | event_loop.create_task(coro) 322 | (G, g, h, o) = cs.params 323 | 324 | # User creates a public / private key pair 325 | keypair = cred_UserKeyge(cs.params) 326 | 327 | # User packages credentials 328 | LT_user_ID = o.random() 329 | timeout = 100 330 | key = 200 331 | value = 300 332 | public_attr = [ key, value, timeout ] 333 | private_attr = [ LT_user_ID ] 334 | 335 | resp = event_loop.run_until_complete(issue_client('127.0.0.1', unused_tcp_port, 336 | cs.params, cs.ipub, keypair, public_attr, private_attr, event_loop)) 337 | 338 | 339 | def test_show_server(event_loop, unused_tcp_port): 340 | cs = CredentialServer() 341 | coro = asyncio.start_server(cs.handle_cmd, 342 | '127.0.0.1', unused_tcp_port, loop=event_loop) 343 | 344 | event_loop.create_task(coro) 345 | 346 | (G, g, h, o) = cs.params 347 | 348 | # User creates a public / private key pair 349 | keypair = cred_UserKeyge(cs.params) 350 | 351 | # User packages credentials 352 | LT_user_ID = o.random() 353 | timeout = 100 354 | key = 200 355 | value = 300 356 | 357 | public_attr = [ key, value, timeout ] 358 | private_attr = [ LT_user_ID ] 359 | 360 | resp = event_loop.run_until_complete(issue_client('127.0.0.1', unused_tcp_port, 361 | cs.params, cs.ipub, keypair, public_attr, private_attr, event_loop)) 362 | mac, user_token, cred = resp 363 | 364 | pub, EGenc, sig_u = user_token 365 | u, EncE, sig_s = cred 366 | 367 | Service_name = b"TestService" 368 | resp = event_loop.run_until_complete(show_client('127.0.0.1', unused_tcp_port, 369 | cs.params, cs.ipub, mac, sig_s, public_attr, private_attr, Service_name, 370 | event_loop)) 371 | 372 | assert resp == "SUCCESS" 373 | 374 | def test__server_timing(event_loop, unused_tcp_port): 375 | cs = CredentialServer() 376 | coro = asyncio.start_server(cs.handle_cmd, 377 | '127.0.0.1', unused_tcp_port, loop=event_loop) 378 | 379 | event_loop.create_task(coro) 380 | 381 | (G, g, h, o) = cs.params 382 | 383 | # User creates a public / private key pair 384 | keypair = cred_UserKeyge(cs.params) 385 | 386 | # User packages credentials 387 | LT_user_ID = o.random() 388 | timeout = 100 389 | key = 200 390 | value = 300 391 | 392 | public_attr = [ key, value, timeout ] 393 | private_attr = [ LT_user_ID ] 394 | 395 | import time 396 | 397 | t0 = time.monotonic() 398 | for _ in range(10): 399 | resp = event_loop.run_until_complete(issue_client('127.0.0.1', unused_tcp_port, 400 | cs.params, cs.ipub, keypair, public_attr, private_attr, event_loop)) 401 | t1 = time.monotonic() 402 | print("ISSUE time (1): %.3f sec" % ((t1-t0) / 10)) 403 | 404 | t0 = time.monotonic() 405 | coros = [issue_client('127.0.0.1', unused_tcp_port, 406 | cs.params, cs.ipub, keypair, public_attr, private_attr, event_loop) for _ in range(10)] 407 | G = asyncio.gather(loop=event_loop, *(tuple(coros))) 408 | event_loop.run_until_complete(G) 409 | t1 = time.monotonic() 410 | print("ISSUE time (2): %.3f sec" % ((t1-t0) / 10)) 411 | 412 | mac, user_token, cred = resp 413 | 414 | pub, EGenc, sig_u = user_token 415 | u, EncE, sig_s = cred 416 | 417 | Service_name = b"TestService" 418 | 419 | t0 = time.monotonic() 420 | for _ in range(10): 421 | resp = event_loop.run_until_complete(show_client('127.0.0.1', unused_tcp_port, 422 | cs.params, cs.ipub, mac, sig_s, public_attr, private_attr, Service_name, 423 | event_loop)) 424 | t1 = time.monotonic() 425 | print("SHOW time (1): %.3f sec" % ((t1-t0) / 10)) 426 | 427 | t0 = time.monotonic() 428 | coros = [show_client('127.0.0.1', unused_tcp_port, 429 | cs.params, cs.ipub, mac, sig_s, public_attr, private_attr, Service_name, 430 | event_loop) for _ in range(10)] 431 | G = asyncio.gather(loop=event_loop, *(tuple(coros))) 432 | event_loop.run_until_complete(G) 433 | t1 = time.monotonic() 434 | print("SHOW time (2): %.3f sec" % ((t1-t0) / 10)) 435 | 436 | def test__server_timing_core(event_loop, unused_tcp_port): 437 | cs = CredentialServer() 438 | coro = asyncio.start_server(cs.handle_cmd, 439 | '127.0.0.1', unused_tcp_port, loop=event_loop) 440 | 441 | event_loop.create_task(coro) 442 | 443 | (G, g, h, o) = cs.params 444 | 445 | # User creates a public / private key pair 446 | keypair = cred_UserKeyge(cs.params) 447 | 448 | # User packages credentials 449 | LT_user_ID = o.random() 450 | timeout = 100 451 | key = 200 452 | value = 300 453 | 454 | public_attr = [ key, value, timeout ] 455 | private_attr = [ LT_user_ID ] 456 | 457 | import time 458 | 459 | resp = event_loop.run_until_complete(issue_client('127.0.0.1', unused_tcp_port, 460 | cs.params, cs.ipub, keypair, public_attr, private_attr, event_loop, repeat=10)) 461 | 462 | mac, user_token, cred = resp 463 | 464 | pub, EGenc, sig_u = user_token 465 | u, EncE, sig_s = cred 466 | 467 | Service_name = b"TestService" 468 | 469 | resp = event_loop.run_until_complete(show_client('127.0.0.1', unused_tcp_port, 470 | cs.params, cs.ipub, mac, sig_s, public_attr, private_attr, Service_name, 471 | event_loop, repeat=10)) 472 | 473 | 474 | assert resp == "SUCCESS" 475 | 476 | print(define_proof(G).render_proof_statement()) 477 | 478 | 479 | 480 | if __name__ == "__main__": 481 | loop = asyncio.get_event_loop() 482 | coro = asyncio.start_server(handle_cmd, '127.0.0.1', 8888, loop=loop) 483 | server = loop.run_until_complete(coro) 484 | 485 | # Serve requests until Ctrl+C is pressed 486 | print('Serving on {}'.format(server.sockets[0].getsockname())) 487 | try: 488 | loop.run_forever() 489 | except KeyboardInterrupt: 490 | pass 491 | 492 | # Close the server 493 | server.close() 494 | loop.run_until_complete(server.wait_closed()) 495 | loop.close() 496 | -------------------------------------------------------------------------------- /petlib/cipher.py: -------------------------------------------------------------------------------- 1 | from .bindings import _FFI, _C, _OPENSSL_VERSION, OpenSSLVersion 2 | 3 | import pytest 4 | 5 | 6 | def _check(return_val): 7 | """Checks the return code of the C calls""" 8 | if isinstance(return_val, int) and return_val == 1: 9 | return 10 | if isinstance(return_val, bool) and return_val == True: 11 | return 12 | 13 | raise Exception( 14 | "Cipher exception: Unknown type %s or value %s" % 15 | (str( 16 | type(return_val)), 17 | str(return_val))) 18 | 19 | 20 | class Cipher(object): 21 | """A class representing a symmetric cipher and mode. 22 | 23 | Example: 24 | An example of encryption and decryption using AES in counter mode. 25 | 26 | >>> from os import urandom 27 | >>> aes = Cipher("AES-128-CTR") # Init AES in Counter mode 28 | >>> key = urandom(16) 29 | >>> iv = urandom(16) 30 | >>> 31 | >>> # Get a CipherOperation object for encryption 32 | >>> enc = aes.enc(key, iv) 33 | >>> ref = b"Hello World" 34 | >>> ciphertext = enc.update(ref) 35 | >>> ciphertext += enc.finalize() 36 | >>> 37 | >>> # Get a CipherOperation object for decryption 38 | >>> dec = aes.dec(key, iv) 39 | >>> plaintext = dec.update(ciphertext) 40 | >>> plaintext += dec.finalize() 41 | >>> plaintext == ref # Check resulting plaintest matches referece one. 42 | True 43 | 44 | """ 45 | 46 | __slots__ = ["alg", "gcm"] 47 | 48 | def __init__(self, name, _alg=None): 49 | """Initialize the cipher by name.""" 50 | 51 | if _alg: 52 | self.alg = _alg 53 | self.gcm = True 54 | return 55 | else: 56 | 57 | self.alg = _C.EVP_get_cipherbyname(name.encode("utf8")) 58 | self.gcm = False 59 | if self.alg == _FFI.NULL: 60 | raise Exception("Unknown cipher: %s" % name) 61 | 62 | if "gcm" in name.lower(): 63 | self.gcm = True 64 | 65 | if "ccm" in name.lower(): 66 | raise Exception("CCM mode not supported") 67 | 68 | def len_IV(self): 69 | """Return the Initialization Vector length in bytes.""" 70 | if _OPENSSL_VERSION == OpenSSLVersion.V1_0: 71 | return int(self.alg.iv_len) 72 | else: 73 | return int(_C.EVP_CIPHER_iv_length(self.alg)) 74 | 75 | def len_key(self): 76 | """Return the secret key length in bytes.""" 77 | if _OPENSSL_VERSION == OpenSSLVersion.V1_0: 78 | return int(self.alg.key_len) 79 | else: 80 | return int(_C.EVP_CIPHER_key_length(self.alg)) 81 | 82 | def len_block(self): 83 | """Return the block size in bytes.""" 84 | if _OPENSSL_VERSION == OpenSSLVersion.V1_0: 85 | return int(self.alg.block_size) 86 | else: 87 | return int(_C.EVP_CIPHER_block_size(self.alg)) 88 | 89 | def get_nid(self): 90 | """Return the OpenSSL nid of the cipher and mode.""" 91 | if _OPENSSL_VERSION == OpenSSLVersion.V1_0: 92 | return int(self.alg.nid) 93 | else: 94 | return int(_C.EVP_CIPHER_nid(self.alg)) 95 | 96 | def op(self, key, iv, enc=1): 97 | """Initializes a cipher operation, either encrypt or decrypt 98 | and returns a CipherOperation object 99 | 100 | Args: 101 | key (str): the block cipher symmetric key. Length depends on block cipher choice. 102 | iv (str): an Initialization Vector of up to the block size. (Can be shorter.) 103 | enc (int): set to 1 to perform encryption, or 0 to perform decryption. 104 | 105 | """ 106 | c_op = CipherOperation(enc) 107 | _check(len(key) == self.len_key()) 108 | _check(enc in [0, 1]) 109 | 110 | if not self.gcm: 111 | _check(len(iv) == self.len_IV()) 112 | _check(_C.EVP_CipherInit_ex(c_op.ctx, 113 | self.alg, _FFI.NULL, key, iv, enc)) 114 | 115 | else: 116 | _check(_C.EVP_CipherInit_ex(c_op.ctx, 117 | self.alg, _FFI.NULL, _FFI.NULL, _FFI.NULL, enc)) 118 | 119 | # assert len(iv) <= self.len_block() 120 | 121 | _check(_C.EVP_CIPHER_CTX_ctrl(c_op.ctx, 122 | _C.EVP_CTRL_GCM_SET_IVLEN, len(iv), _FFI.NULL)) 123 | 124 | _C.EVP_CIPHER_CTX_ctrl( 125 | c_op.ctx, _C.EVP_CTRL_GCM_SET_IV_FIXED, -1, iv) 126 | _C.EVP_CIPHER_CTX_ctrl(c_op.ctx, _C.EVP_CTRL_GCM_IV_GEN, 0, iv) 127 | 128 | _check(_C.EVP_CipherInit_ex(c_op.ctx, 129 | _FFI.NULL, _FFI.NULL, key, iv, enc)) 130 | 131 | c_op.cipher = self 132 | return c_op 133 | 134 | def enc(self, key, iv): 135 | """Initializes an encryption engine with the cipher with a specific key and Initialization Vector (IV). 136 | Returns the CipherOperation engine. 137 | 138 | Args: 139 | key (str): the block cipher symmetric key. Length depends on block cipher choice. 140 | iv (str): an Initialization Vector of up to the block size. (Can be shorter.) 141 | 142 | """ 143 | return self.op(key, iv, enc=1) 144 | 145 | def dec(self, key, iv): 146 | """Initializes a decryption engine with the cipher with a specific key and Initialization Vector (IV). 147 | Returns the CipherOperation engine. 148 | 149 | Args: 150 | key (str): the block cipher symmetric key. Length depends on block cipher choice. 151 | iv (str): an Initialization Vector of up to the block size. (Can be shorter.) 152 | 153 | """ 154 | return self.op(key, iv, enc=0) 155 | 156 | def __del__(self): 157 | pass 158 | 159 | # --------- AES GCM special functions --------------- 160 | 161 | @staticmethod 162 | def aes_128_gcm(): 163 | """Returns a pre-initalized AES-GCM cipher with 128 bits key size""" 164 | return Cipher(None, _C.EVP_aes_128_gcm()) 165 | 166 | @staticmethod 167 | def aes_192_gcm(): 168 | """Returns a pre-initalized AES-GCM cipher with 192 bits key size""" 169 | return Cipher(None, _C.EVP_aes_192_gcm()) 170 | 171 | @staticmethod 172 | def aes_256_gcm(): 173 | """Returns a pre-initalized AES-GCM cipher with 256 bits key size""" 174 | return Cipher(None, _C.EVP_aes_256_gcm()) 175 | 176 | def quick_gcm_enc(self, key, iv, msg, assoc=None, tagl=16): 177 | """One operation GCM encryption. 178 | 179 | Args: 180 | key (str): the AES symmetric key. Length depends on block cipher choice. 181 | iv (str): an Initialization Vector of up to the block size. (Can be shorter.) 182 | msg (str): the message to encrypt. 183 | assoc (str): associated data that will be integrity protected, but not encrypted. 184 | tagl (int): the length of the tag, up to the block length. 185 | 186 | Example: 187 | Use of `quick_gcm_enc` and `quick_gcm_dec` for AES-GCM operations. 188 | 189 | >>> from os import urandom # Secure OS random source 190 | >>> aes = Cipher("aes-128-gcm") # Initialize AES-GCM with 128 bit keys 191 | >>> iv = urandom(16) 192 | >>> key = urandom(16) 193 | >>> # Encryption using AES-GCM returns a ciphertext and a tag 194 | >>> ciphertext, tag = aes.quick_gcm_enc(key, iv, b"Hello") 195 | >>> # Decrytion using AES-GCM 196 | >>> p = aes.quick_gcm_dec(key, iv, ciphertext, tag) 197 | >>> assert p == b'Hello' 198 | 199 | """ 200 | enc = self.enc(key, iv) 201 | if assoc: 202 | enc.update_associated(assoc) 203 | ciphertext = enc.update(msg) 204 | ciphertext += enc.finalize() 205 | tag = enc.get_tag(tagl) 206 | 207 | return (ciphertext, tag) 208 | 209 | def quick_gcm_dec(self, key, iv, cip, tag, assoc=None): 210 | """One operation GCM decrypt. See usage example in "quick_gcm_enc". 211 | Throws an exception on failure of decryption 212 | 213 | Args: 214 | key (str): the AES symmetric key. Length depends on block cipher choice. 215 | iv (str): an Initialization Vector of up to the block size. (Can be shorter.) 216 | cip (str): the ciphertext to decrypt. 217 | tag (int): the integrity tag. 218 | assoc (str): associated data that will be integrity protected, but not encrypted. 219 | 220 | """ 221 | dec = self.dec(key, iv) 222 | if assoc: 223 | dec.update_associated(assoc) 224 | 225 | dec.set_tag(tag) 226 | plain = dec.update(cip) 227 | 228 | try: 229 | plain += dec.finalize() 230 | except BaseException: 231 | raise Exception("Cipher: decryption failed.") 232 | return plain 233 | 234 | 235 | class CipherOperation(object): 236 | 237 | __slots__ = ["ctx", "cipher", "xenc"] 238 | 239 | def __init__(self, xenc): 240 | self.ctx = _C.EVP_CIPHER_CTX_new() 241 | self.cipher = None 242 | self.xenc = xenc 243 | 244 | def update(self, data): 245 | """Processes some data, and returns a partial result.""" 246 | block_len = self.cipher.len_block() 247 | alloc_len = len(data) + block_len + 1 248 | outl = _FFI.new("int *") 249 | outl[0] = alloc_len 250 | out = _FFI.new("unsigned char[]", alloc_len) 251 | 252 | _check(_C.EVP_CipherUpdate(self.ctx, out, outl, data, len(data))) 253 | 254 | ret = bytes(_FFI.buffer(out)[:int(outl[0])]) 255 | return ret 256 | 257 | def finalize(self): 258 | """Finalizes the operation and may return some additional data. 259 | Throws an exception if the authenticator tag is different from the expected value. 260 | 261 | Example: 262 | Example of the exception thrown when an invalid tag is provided. 263 | 264 | >>> from os import urandom 265 | >>> aes = Cipher.aes_128_gcm() # Define an AES-GCM cipher 266 | >>> iv = urandom(16) 267 | >>> key = urandom(16) 268 | >>> ciphertext, tag = aes.quick_gcm_enc(key, iv, b"Hello") 269 | >>> 270 | >>> dec = aes.dec(key, iv) # Get a decryption CipherOperation 271 | >>> dec.set_tag(urandom(len(tag))) # Provide an invalid tag. 272 | >>> plaintext = dec.update(ciphertext) # Feed in the ciphertext for decryption. 273 | >>> try: 274 | ... dec.finalize() # Check and Finalize. 275 | ... except: 276 | ... print("Failure") 277 | Failure 278 | 279 | Throws an exception since integrity check fails due to the invalid tag. 280 | 281 | """ 282 | block_len = self.cipher.len_block() 283 | alloc_len = block_len 284 | outl = _FFI.new("int *") 285 | outl[0] = alloc_len 286 | out = _FFI.new("unsigned char[]", alloc_len) 287 | 288 | try: 289 | _check(_C.EVP_CipherFinal_ex(self.ctx, out, outl)) 290 | if outl[0] == 0: 291 | return b'' 292 | 293 | ret = bytes(_FFI.buffer(out)[:int(outl[0])]) 294 | return ret 295 | except BaseException: 296 | raise Exception("Cipher: decryption failed.") 297 | 298 | def update_associated(self, data): 299 | """Processes some GCM associated data, and returns nothing.""" 300 | 301 | if self.xenc == 0: 302 | self.set_tag(b"\00" * 16) 303 | 304 | outl = _FFI.new("int *") 305 | _check(_C.EVP_CipherUpdate(self.ctx, _FFI.NULL, outl, data, len(data))) 306 | _check(outl[0] == len(data)) 307 | 308 | def get_tag(self, tag_len=16): 309 | """Get the GCM authentication tag. Execute after finalizing the encryption. 310 | 311 | Example: 312 | AES-GCM encryption usage: 313 | 314 | >>> from os import urandom 315 | >>> aes = Cipher.aes_128_gcm() # Initialize AES cipher 316 | >>> key = urandom(16) 317 | >>> iv = urandom(16) 318 | >>> enc = aes.enc(key, iv) # Get an encryption CipherOperation 319 | >>> enc.update_associated(b"Hello") # Include some associated data 320 | >>> ciphertext = enc.update(b"World!") # Include some plaintext 321 | >>> nothing = enc.finalize() # Finalize 322 | >>> tag = enc.get_tag(16) # Get the AES-GCM tag 323 | 324 | """ 325 | tag = _FFI.new("unsigned char []", tag_len) 326 | ret = _C.EVP_CIPHER_CTX_ctrl( 327 | self.ctx, _C.EVP_CTRL_GCM_GET_TAG, tag_len, tag) 328 | _check(ret) 329 | s = bytes(_FFI.buffer(tag)[:]) 330 | return s 331 | 332 | def set_tag(self, tag): 333 | """Specify the GCM authenticator tag. Must be done before finalizing decryption 334 | 335 | Example: 336 | AES-GCM decryption and check: 337 | 338 | >>> aes = Cipher.aes_128_gcm() # Define an AES-GCM cipher 339 | >>> ciphertext, tag = (b'dV\\xb9:\\xd0\\xbe', b'pA\\xbe?\\xfc\\xd1&\\x03\\x1438\\xc5\\xf8In\\xaa') 340 | >>> dec = aes.dec(key=b"A"*16, iv=b"A"*16) # Get a decryption CipherOperation 341 | >>> dec.update_associated(b"Hello") # Feed in the non-secret assciated data. 342 | >>> plaintext = dec.update(ciphertext) # Feed in the ciphertext for decryption. 343 | >>> dec.set_tag(tag) # Provide the AES-GCM tag for integrity. 344 | >>> nothing = dec.finalize() # Check and finalize. 345 | >>> assert plaintext == b'World!' 346 | 347 | """ 348 | _check( 349 | _C.EVP_CIPHER_CTX_ctrl( 350 | self.ctx, 351 | _C.EVP_CTRL_GCM_SET_TAG, 352 | len(tag), 353 | tag)) 354 | 355 | def __del__(self): 356 | _C.EVP_CIPHER_CTX_free(self.ctx) 357 | 358 | 359 | # When testing ignore extra variables 360 | # pylint: disable=unused-variable,redefined-outer-name 361 | 362 | def test_aes_init(): 363 | aes = Cipher("AES-128-CBC") 364 | assert aes.alg != _FFI.NULL 365 | assert aes.len_IV() == 16 366 | assert aes.len_block() == 16 367 | assert aes.len_key() == 16 368 | assert aes.get_nid() == 419 369 | del aes 370 | 371 | 372 | def test_errors(): 373 | with pytest.raises(Exception) as excinfo: 374 | aes = Cipher("AES-128-XXF") 375 | assert 'Unknown' in str(excinfo.value) 376 | 377 | 378 | def test_aes_enc(): 379 | aes = Cipher("AES-128-CBC") 380 | enc = aes.op(key=b"A" * 16, iv=b"A" * 16) 381 | 382 | ref = b"Hello World" * 10000 383 | 384 | ciphertext = enc.update(ref) 385 | ciphertext += enc.finalize() 386 | 387 | dec = aes.op(key=b"A" * 16, iv=b"A" * 16, enc=0) 388 | plaintext = dec.update(ciphertext) 389 | plaintext += dec.finalize() 390 | assert plaintext == ref 391 | 392 | 393 | def test_aes_ctr(): 394 | aes = Cipher("AES-128-CTR") 395 | enc = aes.op(key=b"A" * 16, iv=b"A" * 16) 396 | 397 | ref = b"Hello World" * 10000 398 | 399 | ciphertext = enc.update(ref) 400 | ciphertext += enc.finalize() 401 | 402 | dec = aes.op(key=b"A" * 16, iv=b"A" * 16, enc=0) 403 | plaintext = dec.update(ciphertext) 404 | plaintext += dec.finalize() 405 | assert plaintext == ref 406 | 407 | 408 | def test_aes_ops(): 409 | aes = Cipher("AES-128-CTR") 410 | enc = aes.enc(key=b"A" * 16, iv=b"A" * 16) 411 | 412 | ref = b"Hello World" * 10000 413 | 414 | ciphertext = enc.update(ref) 415 | ciphertext += enc.finalize() 416 | 417 | dec = aes.dec(key=b"A" * 16, iv=b"A" * 16) 418 | plaintext = dec.update(ciphertext) 419 | plaintext += dec.finalize() 420 | assert plaintext == ref 421 | 422 | 423 | def test_aes_gcm_encrypt(): 424 | aes = Cipher.aes_128_gcm() 425 | assert aes.gcm 426 | 427 | enc = aes.op(key=b"A" * 16, iv=b"A" * 16) 428 | 429 | enc.update_associated(b"Hello") 430 | ciphertext = enc.update(b"World!") 431 | c2 = enc.finalize() 432 | assert c2 == b'' 433 | 434 | tag = enc.get_tag(16) 435 | assert len(tag) == 16 436 | 437 | 438 | def test_aes_gcm_encrypt_192(): 439 | aes = Cipher.aes_192_gcm() 440 | assert aes.gcm 441 | 442 | enc = aes.op(key=b"A" * 24, iv=b"A" * 16) 443 | 444 | enc.update_associated(b"Hello") 445 | ciphertext = enc.update(b"World!") 446 | c2 = enc.finalize() 447 | assert c2 == b'' 448 | 449 | tag = enc.get_tag(16) 450 | assert len(tag) == 16 451 | 452 | 453 | def test_aes_gcm_encrypt_256(): 454 | aes = Cipher.aes_256_gcm() 455 | assert aes.gcm 456 | 457 | enc = aes.op(key=b"A" * 32, iv=b"A" * 16) 458 | 459 | enc.update_associated(b"Hello") 460 | ciphertext = enc.update(b"World!") 461 | c2 = enc.finalize() 462 | assert c2 == b'' 463 | 464 | tag = enc.get_tag(16) 465 | assert len(tag) == 16 466 | 467 | 468 | @pytest.fixture 469 | def aesenc(): 470 | aes = Cipher.aes_128_gcm() 471 | assert aes.gcm 472 | 473 | enc = aes.op(key=b"A" * 16, iv=b"A" * 16) 474 | 475 | enc.update_associated(b"Hello") 476 | ciphertext = enc.update(b"World!") 477 | c2 = enc.finalize() 478 | assert c2 == b'' 479 | 480 | tag = enc.get_tag(16) 481 | assert len(tag) == 16 482 | 483 | return (aes, enc, ciphertext, tag) 484 | 485 | 486 | def test_gcm_dec(aesenc): 487 | aes, enc, ciphertext, tag = aesenc 488 | dec = aes.dec(key=b"A" * 16, iv=b"A" * 16) 489 | dec.update_associated(b"Hello") 490 | plaintext = dec.update(ciphertext) 491 | 492 | dec.set_tag(tag) 493 | 494 | dec.finalize() 495 | 496 | assert plaintext == b"World!" 497 | 498 | 499 | def test_gcm_dec_badassoc(aesenc): 500 | aes, enc, ciphertext, tag = aesenc 501 | 502 | dec = aes.dec(key=b"A" * 16, iv=b"A" * 16) 503 | dec.update_associated(b"H4llo") 504 | plaintext = dec.update(ciphertext) 505 | 506 | dec.set_tag(tag) 507 | 508 | with pytest.raises(Exception) as excinfo: 509 | dec.finalize() 510 | assert "Cipher" in str(excinfo.value) 511 | 512 | 513 | def test_gcm_dec_badkey(aesenc): 514 | aes, enc, ciphertext, tag = aesenc 515 | 516 | dec = aes.dec(key=b"B" * 16, iv=b"A" * 16) 517 | dec.update_associated(b"Hello") 518 | plaintext = dec.update(ciphertext) 519 | 520 | dec.set_tag(tag) 521 | 522 | with pytest.raises(Exception) as excinfo: 523 | dec.finalize() 524 | assert "Cipher" in str(excinfo.value) 525 | 526 | 527 | def test_gcm_dec_badiv(aesenc): 528 | aes, enc, ciphertext, tag = aesenc 529 | dec = aes.dec(key=b"A" * 16, iv=b"B" * 16) 530 | dec.update_associated(b"Hello") 531 | plaintext = dec.update(ciphertext) 532 | 533 | dec.set_tag(tag) 534 | 535 | with pytest.raises(Exception) as excinfo: 536 | dec.finalize() 537 | assert "Cipher" in str(excinfo.value) 538 | 539 | 540 | def test_aes_gcm_byname(): 541 | aes = Cipher("aes-128-gcm") 542 | assert aes.gcm 543 | 544 | enc = aes.op(key=b"A" * 16, iv=b"A" * 16) 545 | 546 | enc.update_associated(b"Hello") 547 | ciphertext = enc.update(b"World!") 548 | c2 = enc.finalize() 549 | assert c2 == b'' 550 | 551 | tag = enc.get_tag(16) 552 | assert len(tag) == 16 553 | 554 | dec = aes.dec(key=b"A" * 16, iv=b"A" * 16) 555 | dec.update_associated(b"Hello") 556 | plaintext = dec.update(ciphertext) 557 | 558 | dec.set_tag(tag) 559 | 560 | dec.finalize() 561 | 562 | assert plaintext == b"World!" 563 | 564 | 565 | def test_aes_gcm_different_IV(): 566 | aes = Cipher("aes-128-gcm") 567 | 568 | enc = aes.op(key=b"A" * 16, iv=b"A" * 16) 569 | enc.update_associated(b"Hello") 570 | ciphertext = enc.update(b"World!") 571 | c2 = enc.finalize() 572 | tag = enc.get_tag(16) 573 | 574 | enc = aes.op(key=b"A" * 16, iv=b"A" * 16) 575 | enc.update_associated(b"Hello") 576 | ciphertext2 = enc.update(b"World!") 577 | c2 = enc.finalize() 578 | tag2 = enc.get_tag(16) 579 | 580 | enc = aes.op(key=b"A" * 16, iv=b"B" * 16) 581 | enc.update_associated(b"Hello") 582 | ciphertext3 = enc.update(b"World!") 583 | c2 = enc.finalize() 584 | tag3 = enc.get_tag(16) 585 | 586 | assert ciphertext == ciphertext2 587 | assert ciphertext != ciphertext3 588 | 589 | 590 | def test_quick(): 591 | aes = Cipher("aes-128-gcm") 592 | c, t = aes.quick_gcm_enc(b"A" * 16, b"A" * 16, b"Hello") 593 | p = aes.quick_gcm_dec(b"A" * 16, b"A" * 16, c, t) 594 | assert p == b"Hello" 595 | 596 | 597 | def test_quick_assoc(): 598 | aes = Cipher("aes-128-gcm") 599 | c, t = aes.quick_gcm_enc(b"A" * 16, b"A" * 16, b"Hello", assoc=b"blah") 600 | p = aes.quick_gcm_dec(b"A" * 16, b"A" * 16, c, t, assoc=b"blah") 601 | assert p == b"Hello" 602 | 603 | # pylint: enable=unused-variable,redefined-outer-name 604 | -------------------------------------------------------------------------------- /examples/genzkp.py: -------------------------------------------------------------------------------- 1 | ## An example of how to implement an engine for Zero-Knowledge 2 | # Proof of Discrete Log representations using Brands' and 3 | # Camenisch's extensions to the basic Schnor proof. 4 | # 5 | # For details of what is doing on see Chapter 3: 6 | # "Rethinking Public Key Infrastructures and Digital Certificates 7 | # Building in Privacy" By Stefan Brands, MIT Press (2000) 8 | # On-line: http://www.credentica.com/the_mit_pressbook.html 9 | 10 | 11 | from petlib.ec import EcGroup 12 | from petlib.bn import Bn 13 | from hashlib import sha256 14 | 15 | import pytest 16 | 17 | def challenge(elements): 18 | """Packages a challenge in a bijective way""" 19 | elem = [len(elements)] + elements 20 | elem_str = map(str, elem) 21 | elem_len = map(lambda x: "%s||%s" % (len(x) , x), elem_str) 22 | state = "|".join(elem_len) 23 | H = sha256() 24 | H.update(state.encode("utf8")) 25 | return H.digest() 26 | 27 | class Val: 28 | """A common ansestor for all values""" 29 | def val(self, env): 30 | return env[self.name] 31 | 32 | def tex(self): 33 | return "{%s}" % tex_encode(self.name) 34 | 35 | class Pub(Val): 36 | """Defines a public value, given by the prover""" 37 | def __init__(self, zkp, name): 38 | self.name = name 39 | self.zkp = zkp 40 | assert name not in zkp.Pub 41 | zkp.Pub[name] = self 42 | 43 | def tex(self): 44 | return r"{\mathrm{%s}}" % tex_encode(self.name) 45 | 46 | 47 | class ConstPub(Pub): 48 | """Defines a public value from the environment""" 49 | def __init__(self, zkp, name): 50 | self.name = name 51 | self.zkp = zkp 52 | assert name not in zkp.Const 53 | zkp.Const[name] = self 54 | 55 | class Sec(Val): 56 | """Defines a secret value of the prover""" 57 | def __init__(self, zkp, name): 58 | self.name = name 59 | self.zkp = zkp 60 | assert name not in zkp.Sec 61 | zkp.Sec[name] = self 62 | 63 | class Gen(object): 64 | """Represents a public generator, given by the prover""" 65 | def __init__(self, zkp, name=None, prove=False, constuction=None): 66 | self.name = name 67 | self.zkp = zkp 68 | 69 | if name: 70 | assert name not in zkp.Pub 71 | zkp.Pub[name] = self 72 | 73 | self.prove = prove 74 | self.constuction = constuction 75 | 76 | def get_repr(self): 77 | if self.name or self.constuction[0] == "Gen*": 78 | return [self] 79 | elif self.constuction[0] == "Gen+": 80 | return self.constuction[1:] 81 | 82 | raise Exception("Unknown Gen type") 83 | 84 | def __add__(self, other): 85 | assert isinstance(other, Gen) 86 | assert self.zkp == other.zkp 87 | assert self.prove == other.prove 88 | 89 | prove = self.prove or other.prove 90 | c = ["Gen+"] + self.get_repr() + other.get_repr() 91 | newG = Gen(self.zkp, prove=prove, constuction=c) 92 | return newG 93 | 94 | def __rmul__(self, other): 95 | assert isinstance(other, Val) 96 | assert not self.prove 97 | assert self.zkp == other.zkp 98 | 99 | prove = isinstance(other, Sec) 100 | if self.constuction and self.constuction[0] == "Gen*": 101 | c = self.constuction + [other] 102 | else: 103 | c = ["Gen*", self, other] 104 | 105 | return Gen(self.zkp, constuction=c, prove=prove) 106 | 107 | def tex(self): 108 | ## In case of a named value just return it. 109 | if self.name: 110 | return r"{\mathrm{%s}}" % (tex_encode(self.name)) 111 | 112 | if self.constuction[0] == "Gen+": 113 | gather = [v.tex() for v in self.constuction[1:]] 114 | Sum = " \cdot ".join(gather) 115 | return "{%s}" % Sum 116 | 117 | if self.constuction[0] == "Gen*": 118 | base = self.constuction[1].tex() 119 | exps = [v.tex() for v in self.constuction[2:]] 120 | exps = " \cdot ".join(exps) 121 | ret = "{%s}^{%s}" % (base, exps) 122 | return ret 123 | 124 | 125 | 126 | def val(self, env): 127 | """Returns the value of this variable""" 128 | 129 | ## In case of a named value just return it. 130 | if self.name: 131 | return env[self.name] 132 | 133 | ## In case of a "+" add all parts 134 | if self.constuction[0] == "Gen+": 135 | gather = [v.val(env) for v in self.constuction[1:]] 136 | Sum = None 137 | for v in gather: 138 | if Sum is None: 139 | Sum = v 140 | else: 141 | Sum = v + Sum 142 | return Sum 143 | 144 | if self.constuction[0] == "Gen*": 145 | base = self.constuction[1].val(env) 146 | exps = [v.val(env) for v in self.constuction[2:]] 147 | Prod = 1 148 | for v in exps: 149 | Prod = v * Prod 150 | return Prod * base 151 | 152 | raise Exception("Unknown case") 153 | 154 | class ConstGen(Gen): 155 | """Represents a generator constant in the environment""" 156 | def __init__(self, zkp, name): 157 | Gen.__init__(self, zkp, name=None) 158 | 159 | self.name = name 160 | assert name not in self.zkp.Const 161 | self.zkp.Const[name] = self 162 | 163 | class ZKProof(object): 164 | """A class representing a number of associated ZK Proofs.""" 165 | 166 | def __init__(self, G): 167 | """Define a proof object, and the group in which the proof 168 | is to be carried.""" 169 | 170 | self.locked = False 171 | self.G = G 172 | 173 | self.Const = {} 174 | self.Pub = {} 175 | self.Sec = {} 176 | self.proofs = [] 177 | 178 | self.arrays = {} 179 | 180 | self.locked = True 181 | 182 | def add_proof(self, lhs, rhs): 183 | """Adds a proof obligation to show the rhs is the representation of the lhs""" 184 | assert isinstance(lhs, Gen) 185 | assert lhs.prove == False 186 | assert isinstance(rhs, Gen) 187 | assert rhs.prove == True 188 | assert self == lhs.zkp == rhs.zkp 189 | 190 | self.proofs.append((lhs, rhs)) 191 | 192 | def _check_name_ok(self, name): 193 | if __debug__: 194 | import re 195 | a = re.compile("^[a-zA-Z][a-zA-Z0-9_]*$") 196 | if a.match(name) is not None: 197 | return True 198 | return False 199 | 200 | def get(self, vtype, name, ignore_check = False): 201 | """Returns a number of proof variables of a certain type""" 202 | assert vtype in [Gen, ConstGen, Sec, Pub, ConstPub] 203 | 204 | if isinstance(name, str): 205 | assert self._check_name_ok(name) or ignore_check 206 | return self._get(vtype, name, ignore_check) 207 | 208 | if isinstance(name, list): 209 | assert all(map(self._check_name_ok, name)) or ignore_check 210 | return [self._get(vtype, n, ignore_check) for n in name] 211 | 212 | raise Exception("Wrong type of names: str or list(str)") 213 | 214 | 215 | def _get(self, vtype, name, ignore_check = False): 216 | assert isinstance(name, str) 217 | 218 | for D in [self.Const, self.Pub, self.Sec]: 219 | if name in D: 220 | assert isinstance(D[name], vtype) 221 | return D[name] 222 | 223 | return vtype(self, name) 224 | 225 | 226 | def __setattr__(self, name, value): 227 | if hasattr(self, "locked") and self.locked: 228 | assert name not in self.__dict__ 229 | 230 | # Add the name to the zk proof 231 | v = self.get(value, name) 232 | object.__setattr__(self, name, v) 233 | else: 234 | # implement *my* __setattr__ 235 | object.__setattr__(self, name, value) 236 | 237 | 238 | def get_array(self, vtype, name, number, start=0): 239 | """Returns an array of variables""" 240 | assert vtype in [Gen, ConstGen, Sec, Pub, ConstPub] 241 | assert isinstance(name, str) 242 | assert self._check_name_ok(name) 243 | 244 | if name in self.arrays: 245 | assert self.arrays[name] == (number, start) 246 | else: 247 | self.arrays[name] = (number, start) 248 | 249 | names = ["%s[%i]" % (name,i) for i in range(start, start+number)] 250 | return self.get(vtype, names, True) 251 | 252 | def all_vars(self): 253 | variables = list(self.Const) \ 254 | + list(self.Pub) \ 255 | + list(self.Sec) 256 | return set(variables) 257 | 258 | def _check_env(self, env): 259 | if __debug__: 260 | variables = self.all_vars() 261 | 262 | for v in variables: 263 | if not v in env: 264 | raise Exception("Could not find variable %s in the environment.\n%s" % (repr(v), repr(variables))) 265 | 266 | def render_proof_statement(self): 267 | s = r'$' 268 | 269 | if len(self.Const) > 0: 270 | variables = [] 271 | for con in sorted(list(self.Const)): 272 | variables += ["{\mathrm{%s}}" % tex_encode(con)] 273 | s += r"\text{Constants: } %s \\" % (', '.join(variables)) 274 | 275 | 276 | if len(self.Pub) > 0: 277 | variables = [] 278 | for pub in sorted(list(self.Pub)): 279 | variables += ["{\mathrm{%s}}" % tex_encode(pub)] 280 | s += r"\text{Public: } %s \\" % (', '.join(variables)) 281 | 282 | s += r"\text{NIZK}\{" + """(""" 283 | variables = [] 284 | for sec in sorted(list(self.Sec)): 285 | variables += ["{%s}" % tex_encode(sec)] 286 | variables = ', '.join(variables) 287 | s += variables 288 | 289 | s += r"): \\ \qquad " 290 | 291 | formulas = [] 292 | for base, expr in self.proofs: 293 | formulas += ["%s = %s" % (base.tex(), expr.tex())] 294 | 295 | s += r" \wedge \\ \qquad ".join(formulas) 296 | 297 | s+= r'\}$' 298 | return s 299 | 300 | 301 | def build_proof(self, env, message=""): 302 | """Generates a proof within an environment of assigned public and secret variables.""" 303 | 304 | self._check_env(env) 305 | 306 | # Do sanity check on the proofs 307 | if __debug__: 308 | for base, expr in self.proofs: 309 | xGen = base.val(env) 310 | xExpr = expr.val(env) 311 | try: 312 | assert xGen == xExpr 313 | except: 314 | raise Exception("Proof about '%s' does not hold." % base.name) 315 | 316 | G = self.G 317 | order = G.order() 318 | 319 | ## Make a list of all the public state 320 | state = ['ZKP', G.nid(), message] 321 | 322 | for v in sorted(self.Const.keys()): 323 | state += [env[v]] 324 | for v in sorted(self.Pub.keys()): 325 | state += [env[v]] 326 | 327 | ## Set witnesses for all secrets 328 | witnesses = dict(env.items()) 329 | for w in self.Sec.keys(): 330 | assert w in witnesses 331 | witnesses[w] = order.random() 332 | 333 | ## Compute the first message and add it to the state 334 | for base, expr in self.proofs: 335 | Cw = expr.val(witnesses) 336 | state += [Cw] 337 | 338 | ## Compute the challenge using all the state 339 | hash_c = challenge(state) 340 | c = Bn.from_binary(hash_c) % order 341 | 342 | ## Compute all the resources 343 | responses = dict(env.items()) 344 | for w in self.Sec.keys(): 345 | responses[w] = (witnesses[w] - c * env[w]) % order 346 | 347 | for v in self.Const: 348 | del responses[v] 349 | 350 | return (c, responses) 351 | 352 | def verify_proof(self, env, sig, message="", strict=True): 353 | """Verifies a proof within an environment of assigned public only variables.""" 354 | 355 | ## Select the constants for the env 356 | env_l = [(k,v) for k,v in env.items() if k in self.Const] 357 | 358 | if __debug__ and strict: 359 | env_not = [k for k,v in env.items() if k not in self.Const] 360 | if len(env_not): 361 | raise Exception("Did not check: " + (", ".join(env_not))) 362 | 363 | c, responses = sig 364 | responses = dict(list(responses.items()) + env_l) 365 | 366 | ## Ensure all variables we need are here 367 | self._check_env(responses) 368 | 369 | ## Define the maths group we work in 370 | G = self.G 371 | order = G.order() 372 | 373 | ## Make a list of all the public state 374 | state = ['ZKP', G.nid(), message] 375 | for v in sorted(self.Const.keys()): 376 | state += [responses[v]] 377 | for v in sorted(self.Pub.keys()): 378 | state += [responses[v]] 379 | 380 | ## Compute the first message and add it to the state 381 | for base, expr in self.proofs: 382 | Cr = expr.val(responses) 383 | Cx = base.val(responses) 384 | Cw = Cr + c * Cx 385 | state += [Cw] 386 | 387 | ## Compute the challenge using all the state 388 | hash_c = challenge(state) 389 | c_prime = Bn.from_binary(hash_c) % order 390 | 391 | ## Check equality 392 | return (c == c_prime) 393 | 394 | class ZKEnv(object): 395 | """ A class that passes all the ZK environment 396 | state to the proof or verification. 397 | """ 398 | 399 | def __init__(self, zkp): 400 | """ Initializes and ties to a specific proof. """ 401 | ## Watch out for recursive calls, given we 402 | # redefined __setattr__ 403 | object.__setattr__(self, "zkp", zkp) 404 | object.__setattr__(self, "env", {}) 405 | 406 | def __setattr__(self, name, value): 407 | """ Store into a special dictionary """ 408 | if isinstance(value, list): 409 | assert name in self.zkp.arrays 410 | number, start = self.zkp.arrays[name] 411 | assert len(value) == number 412 | 413 | for i, v in enumerate(value): 414 | n = "%s[%i]" % (name,start+i) 415 | self._set_var(n, v) 416 | 417 | else: 418 | self._set_var(name, value) 419 | 420 | def _set_var(self, name, value): 421 | if not name in self.zkp.all_vars(): 422 | raise Exception("Variable name '%s' not known." % name) 423 | self.env[name] = value 424 | 425 | def __getattr__(self, name): 426 | if not name in self.zkp.all_vars(): 427 | raise Exception("Variable name '%s' not known." % name) 428 | return self.env[name] 429 | 430 | def get(self): 431 | """ Get the environement. """ 432 | return self.env 433 | 434 | import re 435 | def tex_encode(name): 436 | m = re.match(r"^(.+)i\[([0-9]+)\]$", name) 437 | if m != None: 438 | return r"{%s}_{%s}" % (tex_encode(m.group(1)), m.group(2)) 439 | 440 | m = re.match(r"^(.+)_prime$", name) 441 | if m != None: 442 | return r"{%s'}" % (tex_encode(m.group(1))) 443 | 444 | m = re.match(r"^(.+)_bar$", name) 445 | if m != None: 446 | return r"{\overline{%s}}" % (tex_encode(m.group(1))) 447 | 448 | return name 449 | 450 | def test_tex(): 451 | assert "}_{" in tex_encode("helloi[10]") 452 | assert "'}" in tex_encode("hello_prime") 453 | assert r"\overline" in tex_encode("hello_bar") 454 | 455 | 456 | def test_basic(): 457 | zk = ZKProof(None) 458 | 459 | g = zk.get(ConstGen, "g") 460 | 461 | # Test: ok to call twice 462 | g2 = zk.get(ConstGen, "g") 463 | # return same object 464 | assert g == g2 465 | 466 | # Test: need to be of same type! 467 | with pytest.raises(Exception) as excinfo: 468 | zk.get(Pub, "g") 469 | print(str(excinfo.value)) 470 | assert "isinstance" in str(excinfo.value) 471 | 472 | 473 | h = zk.get(ConstGen, "h") 474 | Gone = zk.get(ConstGen, "d1") 475 | x = zk.get(Sec, "x") 476 | o = zk.get(Sec, "o") 477 | y = zk.get(Pub, "y") 478 | one = zk.get(ConstPub, "d1g") 479 | 480 | Cx = zk.get(Gen, "Cx") 481 | 482 | Cxp = x*g + o*(y * h) 483 | 484 | zk.add_proof(Cx, Cxp) 485 | 486 | print(zk.Const.keys()) 487 | print(zk.Pub.keys()) 488 | print(zk.Sec.keys()) 489 | 490 | def test_Pedersen(): 491 | 492 | # Define an EC group 493 | G = EcGroup(713) 494 | order = G.order() 495 | 496 | ## Proof definitions 497 | zk = ZKProof(G) 498 | g, h = zk.get(ConstGen, ["g", "h"]) 499 | x, o = zk.get(Sec, ["x", "o"]) 500 | Cxo = zk.get(Gen, "Cxo") 501 | zk.add_proof(Cxo, x*g + o*h) 502 | 503 | # A concrete Pedersen commitment 504 | ec_g = G.generator() 505 | ec_h = order.random() * ec_g 506 | bn_x = order.random() 507 | bn_o = order.random() 508 | ec_Cxo = bn_x * ec_g + bn_o * ec_h 509 | 510 | # Execute the proof 511 | env = { 512 | "g": ec_g, 513 | "h": ec_h, 514 | "Cxo": ec_Cxo, 515 | "x": bn_x, 516 | "o": bn_o 517 | } 518 | sig = zk.build_proof(env) 519 | 520 | # Execute the verification 521 | env_verify = { 522 | "g": ec_g, 523 | "h": ec_h 524 | } 525 | 526 | assert zk.verify_proof(env_verify, sig) 527 | 528 | def test_Pedersen_Env(): 529 | 530 | # Define an EC group 531 | G = EcGroup(713) 532 | order = G.order() 533 | 534 | ## Proof definitions 535 | zk = ZKProof(G) 536 | g, h = zk.get(ConstGen, ["g", "h"]) 537 | x, o = zk.get(Sec, ["x", "o"]) 538 | Cxo = zk.get(Gen, "Cxo") 539 | zk.add_proof(Cxo, x*g + o*h) 540 | 541 | print(zk.render_proof_statement()) 542 | 543 | # A concrete Pedersen commitment 544 | ec_g = G.generator() 545 | ec_h = order.random() * ec_g 546 | bn_x = order.random() 547 | bn_o = order.random() 548 | ec_Cxo = bn_x * ec_g + bn_o * ec_h 549 | 550 | env = ZKEnv(zk) 551 | env.g, env.h = ec_g, ec_h 552 | env.Cxo = ec_Cxo 553 | env.x = bn_x 554 | env.o = bn_o 555 | 556 | sig = zk.build_proof(env.get()) 557 | 558 | # Execute the verification 559 | env = ZKEnv(zk) 560 | env.g, env.h = ec_g, ec_h 561 | 562 | assert zk.verify_proof(env.get(), sig) 563 | 564 | def test_Pedersen_Shorthand(): 565 | 566 | # Define an EC group 567 | G = EcGroup(713) 568 | order = G.order() 569 | 570 | ## Proof definitions 571 | zk = ZKProof(G) 572 | zk.g, zk.h = ConstGen, ConstGen 573 | zk.x, zk.o = Sec, Sec 574 | zk.Cxo = Gen 575 | zk.add_proof(zk.Cxo, zk.x*zk.g + zk.o*zk.h) 576 | 577 | print(zk.render_proof_statement()) 578 | 579 | # A concrete Pedersen commitment 580 | ec_g = G.generator() 581 | ec_h = order.random() * ec_g 582 | bn_x = order.random() 583 | bn_o = order.random() 584 | ec_Cxo = bn_x * ec_g + bn_o * ec_h 585 | 586 | env = ZKEnv(zk) 587 | env.g, env.h = ec_g, ec_h 588 | env.Cxo = ec_Cxo 589 | env.x = bn_x 590 | env.o = bn_o 591 | 592 | sig = zk.build_proof(env.get()) 593 | 594 | # Execute the verification 595 | env = ZKEnv(zk) 596 | env.g, env.h = ec_g, ec_h 597 | 598 | assert zk.verify_proof(env.get(), sig) 599 | 600 | 601 | def test_Pedersen_Env_missing(): 602 | 603 | # Define an EC group 604 | G = EcGroup(713) 605 | order = G.order() 606 | 607 | ## Proof definitions 608 | zk = ZKProof(G) 609 | g, h = zk.get(ConstGen, ["g", "h"]) 610 | x, o = zk.get(Sec, ["x", "o"]) 611 | Cxo = zk.get(Gen, "Cxo") 612 | zk.add_proof(Cxo, x*g + o*h) 613 | 614 | # A concrete Pedersen commitment 615 | ec_g = G.generator() 616 | ec_h = order.random() * ec_g 617 | bn_x = order.random() 618 | bn_o = order.random() 619 | ec_Cxo = bn_x * ec_g + bn_o * ec_h 620 | 621 | env = ZKEnv(zk) 622 | env.g, env.h = ec_g, ec_h 623 | env.Cxo = ec_Cxo 624 | env.x = bn_x 625 | # env.o = bn_o ## MISSING THIS ONE 626 | 627 | with pytest.raises(Exception) as excinfo: 628 | env.NOTEXISTING = bn_x 629 | assert "Variable name 'NOTEXISTING' not known" in str(excinfo.value) 630 | 631 | ## Ensure we catch missing variables 632 | with pytest.raises(Exception) as excinfo: 633 | zk.build_proof(env.get()) 634 | assert 'Could not find variable' in str(excinfo.value) 635 | 636 | ## Ensure we catch false statements 637 | env.o = bn_o + 1 638 | with pytest.raises(Exception) as excinfo: 639 | zk.build_proof(env.get()) 640 | assert "Proof about 'Cxo' does not hold" in str(excinfo.value) 641 | 642 | def test_latex_print(): 643 | 644 | # Define an EC group 645 | G = EcGroup(713) 646 | order = G.order() 647 | 648 | ## Proof definitions 649 | zk = ZKProof(G) 650 | g, h = zk.get(ConstGen, ["g", "h"]) 651 | x, o = zk.get(Sec, ["x", "o"]) 652 | Cxo = zk.get(Gen, "Cxo") 653 | zk.add_proof(Cxo, x*g + o*h) 654 | 655 | print(zk.render_proof_statement()) 656 | --------------------------------------------------------------------------------