Welcome to petlib’s documentation!¶
Pre-requisites¶
On Ubuntu / debian use apt-get to install package libssl-dev
. Ensure you also install libffi-dev
and python-dev
:
sudo apt-get install python-dev
sudo apt-get install libssl-dev
sudo apt-get install libffi-dev
On MacOS, install OpenSSL 1.1.x using homebrew:
brew install openssl@1.1
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).
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
.
Quick install¶
If you have pip
installed the following command should install petlib
:
pip install petlib
Test your installation:
python -c "import petlib; petlib.run_tests()"
Testing and Packaging¶
You will need a working Python 2.7 and 3.6 environemnt with pytest:
sudo apt-get install python-pytest
sudo apt-get install python3-pytest
sudo apt-get install python-sphinx
sudo pip install Mock
To build the distribution, create a venv for tests and run all tests (including the examples):
paver
- Specific paver targets include:
unit_tests
: runs the unit tests.build
: builds a distribution bundle in thedist
directory.make_docs
: builds the html documentation indocs/_build/html
make_env
: initialized a virtualenv with a fresh petlib in foldertest_env/pltest
.big_test
: runs all the examples in a virtual environment.test
: runs all tests.
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:
py.test --doctest-modules --cov petlib petlib/*.py
To generate an HTML report of lines not covered by tests run:
py.test --doctest-modules --cov-report html --cov petlib petlib/*.py
To build the Sphinx HTML documentation go into docs
and run:
make html
To build the source distribution run (and add upload
if you wish to upload it to pypi):
python setup.py sdist
petlib Introduction & Examples¶
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
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).
Besides petlib
Python offers a number of modules in the standard library that are necessary to support modern cryptography:
- The
hashlib
module offers cryptographic hash functions such assha256
, andsha512
.- The
os
module offers theurandom
cryptographically strong random number generator.- The
hmac
module offers keyed hash-based message authentication codes as well as facilities for constant time comparison.
- Examples of use of
petlib
for implementing historical as well as state of the art PETs include: - A toy raw-RSA implementation, illustrates the use of
petlib.bn
. - An additively homomorphic encryption system (AHEG) based on EC El-Gamal, illustrating the use of
petlib.ec
. - A Schnorr zero-knowledge proof of knowledge or a discrete logarithm in EC groups.
- 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.
- 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. - A reference implementation of the Anonymous Credentials Light scheme by Baldimtsi, Foteini, and Anna Lysyanskaya (ACM CCS 2013).
- A toy raw-RSA implementation, illustrates the use of
petlib Modules¶
Module petlib.bn¶
-
class
petlib.bn.
Bn
(num=0)[source]¶ The core Big Number class. It supports all comparisons (<, <=, ==, !=, >=, >), arithmetic operations (+, -, %, /, divmod, pow) and copy operations (copy and deep copy). The right-hand side operand may be a small native python integer (<2^64).
-
static
from_decimal
(sdec)[source]¶ Creates a Big Number from a decimal string.
- Args:
- sdec (string): numeric string possibly starting with minus.
- See Also:
- str() produces a decimal string from a big number.
- Example:
>>> hundred = Bn.from_decimal("100") >>> str(hundred) '100'
-
static
from_hex
(shex)[source]¶ Creates a Big Number from a hexadecimal string.
- Args:
- shex (string): hex (0-F) string possibly starting with minus.
- See Also:
- hex() produces a hexadecimal representation of a big number.
- Example:
>>> Bn.from_hex("FF") 255
-
static
from_binary
(sbin)[source]¶ Creates a Big Number from a byte sequence representing the number in Big-endian 8 byte atoms. Only positive values can be represented as byte sequence, and the library user should store the sign bit separately.
- Args:
- sbin (string): a byte sequence.
- Example:
>>> byte_seq = unhexlify(b"010203") >>> Bn.from_binary(byte_seq) 66051 >>> (1 * 256**2) + (2 * 256) + 3 66051
-
static
get_prime
(bits, safe=1)[source]¶ Builds a prime Big Number of length bits.
- Args:
- bits (int) – the number of bits. safe (int) – 1 for a safe prime, otherwise 0.
-
binary
()[source]¶ Returns a byte sequence storing the absolute value of the Big Number in Big-Endian format (with 8 bit atoms). You need to extact the sign separately.
- Example:
>>> bin = Bn(66051).binary() >>> hexlify(bin) == b'010203' True
-
random
()[source]¶ Returns a cryptographically strong random number 0 <= rnd < self.
- Example:
>>> r = Bn(100).random() >>> 0 <= r < 100 True
-
int_neg
()[source]¶ Returns the negative of this number. Synonym with -self.
Example:
>>> one100 = Bn(100) >>> one100.int_neg() -100 >>> -one100 -100
-
int_add
(other)[source]¶ Returns the sum of this number with another. Synonym for self + other.
Example:
>>> one100 = Bn(100) >>> two100 = Bn(200) >>> two100.int_add(one100) # Function syntax 300 >>> two100 + one100 # Operator syntax 300
-
int_sub
(other)[source]¶ Returns the difference between this number and another. Synonym for self - other.
Example:
>>> one100 = Bn(100) >>> two100 = Bn(200) >>> two100.int_sub(one100) # Function syntax 100 >>> two100 - one100 # Operator syntax 100
-
int_mul
(other)[source]¶ Returns the product of this number with another. Synonym for self * other.
Example:
>>> one100 = Bn(100) >>> two100 = Bn(200) >>> one100.int_mul(two100) # Function syntax 20000 >>> one100 * two100 # Operator syntax 20000
-
mod_add
(other, m)[source]¶ Returns the sum of self and other modulo m.
Example:
>>> Bn(10).mod_add(Bn(2), Bn(11)) # Only function notation available 1
-
mod_sub
(other, m)[source]¶ Returns the difference of self and other modulo m.
Example:
>>> Bn(10).mod_sub(Bn(2), Bn(11)) # Only function notation available 8
-
mod_mul
(other, m)[source]¶ Return the product of self and other modulo m.
Example:
>>> Bn(10).mod_mul(Bn(2), Bn(11)) # Only function notation available 9
-
mod_inverse
(m)[source]¶ Compute the inverse mod m, such that self * res == 1 mod m.
Example:
>>> Bn(10).mod_inverse(m = Bn(11)) # Only function notation available 10 >>> Bn(10).mod_mul(Bn(10), m = Bn(11)) == Bn(1) True
-
mod_pow
(other, m, ctx=None)[source]¶ Performs the modular exponentiation of self ** other % m.
- Example:
>>> one100 = Bn(100) >>> one100.mod_pow(2, 3) # Modular exponentiation 1
-
divmod
(other)[source]¶ Returns the integer division and remainder of this number by another. Synonym for (div, mod) = divmod(self, other)
-
int_div
(other)[source]¶ Returns the integer division of this number by another. Synonym of self / other.
Example:
>>> one100 = Bn(100) >>> two100 = Bn(200) >>> two100.int_div(one100) # Function syntax 2 >>> two100 / one100 # Operator syntax 2
-
mod
(other)[source]¶ Returns the remainder of this number modulo another. Synonym for self % other.
Example:
>>> one100 = Bn(100) >>> two100 = Bn(200) >>> two100.mod(one100) # Function syntax 0 >>> two100 % one100 # Operator syntax 0
-
pow
(other, modulo=None, ctx=None)[source]¶ Returns the number raised to the power other optionally modulo a third number. Synonym with pow(self, other, modulo).
Example:
>>> one100 = Bn(100) >>> one100.pow(2) # Function syntax 10000 >>> one100 ** 2 # Operator syntax 10000 >>> one100.pow(2, 3) # Modular exponentiation 1
-
static
Module petlib.ec¶
-
class
petlib.ec.
EcGroup
(nid=713, optimize_mult=True)[source]¶ -
static
list_curves
()[source]¶ Return a dictionary of name id (int) to curve names (str).
- Example:
>>> curves = EcGroup.list_curves() >>> curves[713] 'NIST/SECG curve over a 224 bit prime field'
-
parameters
()[source]¶ Returns a dictionary with the parameters (a,b and p) of the curve.
- Example:
>>> params = EcGroup(713).parameters() >>> params["a"] 26959946667150639794667015087019630673557916260026308143510066298878 >>> params["b"] 18958286285566608000408668544493926415504680968679321075787234672564 >>> params["p"] 26959946667150639794667015087019630673557916260026308143510066298881
-
infinite
()[source]¶ Returns a point at infinity.
- Example:
>>> G = EcGroup() >>> G.generator() + G.infinite() == G.generator() ## Should hold. True
-
order
()[source]¶ Returns the order of the group as a Big Number.
- Example:
>>> G = EcGroup() >>> G.order() * G.generator() == G.infinite() ## Should hold. True
-
wsum
(weights, elems)[source]¶ Sum efficiently a number of elements each multiplied by a bn in weights
-
static
-
class
petlib.ec.
EcPt
(group)[source]¶ An EC point, supporting point addition, doubling and multiplication with a scalar
-
static
from_binary
(sbin, group)[source]¶ Create a point from a byte sequence.
- Example:
>>> G = EcGroup() >>> byte_string = G.generator().export() # Export EC point as byte string >>> EcPt.from_binary(byte_string, G) == G.generator() # Import EC point from binary string True
-
pt_add
(other)[source]¶ Adds two points together. Synonym with self + other.
- Example:
>>> g = EcGroup().generator() >>> g.pt_add(g) == (g + g) == (2 * g) == g.pt_double() # Equivalent formulations True
-
pt_neg
()[source]¶ Returns the negative of the point. Synonym with -self.
- Example:
>>> G = EcGroup() >>> g = G.generator() >>> g + (-g) == G.infinite() # Unary negative operator. True >>> g - g == G.infinite() # Binary negative operator. True
-
pt_mul
(scalar)[source]¶ Returns the product of the point with a scalar (not commutative). Synonym with scalar * self.
- Example:
>>> G = EcGroup() >>> g = G.generator() >>> 100 * g == g.pt_mul(100) # Operator and function notation mean the same True >>> G.order() * g == G.infinite() # Scalar mul. by the order returns the identity element. True
-
pt_mul_inplace
(scalar)[source]¶ Multiplies a scalar with a point and mutates the point to hold the result.
-
pt_eq
(other)[source]¶ Returns a boolean denoting whether the points are equal. Synonym with self == other.
- Example:
>>> G = EcGroup() >>> g = G.generator() >>> 40 * g + 60 * g == 100 * g True >>> g == 2 * g False
-
export
(form=POINT_CONVERSION_COMPRESSED)[source]¶ Returns a string binary representation of the point in compressed coordinates.
- Example:
>>> G = EcGroup() >>> byte_string = G.generator().export() >>> print(hexlify(byte_string).decode("utf8")) 02b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21
-
is_infinite
()[source]¶ Returns True if this point is at infinity, otherwise False.
- Example:
>>> G = EcGroup() >>> g, o = G.generator(), G.order() >>> (o * g).is_infinite() True
-
get_affine
()[source]¶ Return the affine coordinates (x,y) of this EC Point.
- Example:
>>> G = EcGroup() >>> g = G.generator() >>> x, y = g.get_affine() >>> x 19277929113566293071110308034699488026831934219452440156649784352033 >>> y 19926808758034470970197974370888749184205991990603949537637343198772
-
static
Module petlib.cipher¶
-
class
petlib.cipher.
Cipher
(name, _alg=None)[source]¶ A class representing a symmetric cipher and mode.
- Example:
An example of encryption and decryption using AES in counter mode.
>>> from os import urandom >>> aes = Cipher("AES-128-CTR") # Init AES in Counter mode >>> key = urandom(16) >>> iv = urandom(16) >>> >>> # Get a CipherOperation object for encryption >>> enc = aes.enc(key, iv) >>> ref = b"Hello World" >>> ciphertext = enc.update(ref) >>> ciphertext += enc.finalize() >>> >>> # Get a CipherOperation object for decryption >>> dec = aes.dec(key, iv) >>> plaintext = dec.update(ciphertext) >>> plaintext += dec.finalize() >>> plaintext == ref # Check resulting plaintest matches referece one. True
-
op
(key, iv, enc=1)[source]¶ Initializes a cipher operation, either encrypt or decrypt and returns a CipherOperation object
- Args:
- key (str): the block cipher symmetric key. Length depends on block cipher choice. iv (str): an Initialization Vector of up to the block size. (Can be shorter.) enc (int): set to 1 to perform encryption, or 0 to perform decryption.
-
enc
(key, iv)[source]¶ Initializes an encryption engine with the cipher with a specific key and Initialization Vector (IV). Returns the CipherOperation engine.
- Args:
- key (str): the block cipher symmetric key. Length depends on block cipher choice. iv (str): an Initialization Vector of up to the block size. (Can be shorter.)
-
dec
(key, iv)[source]¶ Initializes a decryption engine with the cipher with a specific key and Initialization Vector (IV). Returns the CipherOperation engine.
- Args:
- key (str): the block cipher symmetric key. Length depends on block cipher choice. iv (str): an Initialization Vector of up to the block size. (Can be shorter.)
-
quick_gcm_enc
(key, iv, msg, assoc=None, tagl=16)[source]¶ One operation GCM encryption.
- Args:
- key (str): the AES symmetric key. Length depends on block cipher choice. iv (str): an Initialization Vector of up to the block size. (Can be shorter.) msg (str): the message to encrypt. assoc (str): associated data that will be integrity protected, but not encrypted. tagl (int): the length of the tag, up to the block length.
- Example:
Use of quick_gcm_enc and quick_gcm_dec for AES-GCM operations.
>>> from os import urandom # Secure OS random source >>> aes = Cipher("aes-128-gcm") # Initialize AES-GCM with 128 bit keys >>> iv = urandom(16) >>> key = urandom(16) >>> # Encryption using AES-GCM returns a ciphertext and a tag >>> ciphertext, tag = aes.quick_gcm_enc(key, iv, b"Hello") >>> # Decrytion using AES-GCM >>> p = aes.quick_gcm_dec(key, iv, ciphertext, tag) >>> assert p == b'Hello'
-
quick_gcm_dec
(key, iv, cip, tag, assoc=None)[source]¶ One operation GCM decrypt. See usage example in “quick_gcm_enc”. Throws an exception on failure of decryption
- Args:
- key (str): the AES symmetric key. Length depends on block cipher choice. iv (str): an Initialization Vector of up to the block size. (Can be shorter.) cip (str): the ciphertext to decrypt. tag (int): the integrity tag. assoc (str): associated data that will be integrity protected, but not encrypted.
-
class
petlib.cipher.
CipherOperation
(xenc)[source]¶ -
-
finalize
()[source]¶ Finalizes the operation and may return some additional data. Throws an exception if the authenticator tag is different from the expected value.
- Example:
Example of the exception thrown when an invalid tag is provided.
>>> from os import urandom >>> aes = Cipher.aes_128_gcm() # Define an AES-GCM cipher >>> iv = urandom(16) >>> key = urandom(16) >>> ciphertext, tag = aes.quick_gcm_enc(key, iv, b"Hello") >>> >>> dec = aes.dec(key, iv) # Get a decryption CipherOperation >>> dec.set_tag(urandom(len(tag))) # Provide an invalid tag. >>> plaintext = dec.update(ciphertext) # Feed in the ciphertext for decryption. >>> try: ... dec.finalize() # Check and Finalize. ... except: ... print("Failure") Failure
Throws an exception since integrity check fails due to the invalid tag.
-
get_tag
(tag_len=16)[source]¶ Get the GCM authentication tag. Execute after finalizing the encryption.
- Example:
AES-GCM encryption usage:
>>> from os import urandom >>> aes = Cipher.aes_128_gcm() # Initialize AES cipher >>> key = urandom(16) >>> iv = urandom(16) >>> enc = aes.enc(key, iv) # Get an encryption CipherOperation >>> enc.update_associated(b"Hello") # Include some associated data >>> ciphertext = enc.update(b"World!") # Include some plaintext >>> nothing = enc.finalize() # Finalize >>> tag = enc.get_tag(16) # Get the AES-GCM tag
-
set_tag
(tag)[source]¶ Specify the GCM authenticator tag. Must be done before finalizing decryption
- Example:
AES-GCM decryption and check:
>>> aes = Cipher.aes_128_gcm() # Define an AES-GCM cipher >>> ciphertext, tag = (b'dV\xb9:\xd0\xbe', b'pA\xbe?\xfc\xd1&\x03\x1438\xc5\xf8In\xaa') >>> dec = aes.dec(key=b"A"*16, iv=b"A"*16) # Get a decryption CipherOperation >>> dec.update_associated(b"Hello") # Feed in the non-secret assciated data. >>> plaintext = dec.update(ciphertext) # Feed in the ciphertext for decryption. >>> dec.set_tag(tag) # Provide the AES-GCM tag for integrity. >>> nothing = dec.finalize() # Check and finalize. >>> assert plaintext == b'World!'
-
Module petlib.hmac¶
-
class
petlib.hmac.
Hmac
(name, key)[source]¶ Initialize the HMAC by name with a key.
- Args:
- name: the name of the hash function to be used. key: the cryptographic symmetric key of the HMAC.
- Returns:
- An HMAC instance, ready to accept data to MAC.
Example:
>>> h = Hmac(b"sha512", b"Jefe") >>> h.update(b"what do ya want ") >>> h.update(b"for nothing?") >>> d = h.digest() >>> len(d) 64 >>> hexlify(d)[:10] == b"164b7a7bfc" True
Module petlib.ecdsa¶
A library providing signature and verification functions for the ECDSA scheme.
- Example:
How to use
do_ecdsa_sign
anddo_ecdsa_verify
to sign and verify a string:>>> from hashlib import sha1 >>> # Generate a signature / verification key pair. >>> G = EcGroup() >>> sig_key = G.order().random() >>> ver_key = sig_key * G.generator() >>> # Hash the (potentially long) message into a short digest. >>> digest = sha1(b"Hello World!").digest() >>> # Sign and verify signature >>> sig = do_ecdsa_sign(G, sig_key, digest) >>> do_ecdsa_verify(G, ver_key, sig, digest) True
Fast signatures can be constructed using
do_ecdsa_setup
:>>> from hashlib import sha1 >>> # Generate a signature / verification key pair. >>> G = EcGroup() >>> sig_key = G.order().random() >>> ver_key = sig_key * G.generator() >>> # Hash the (potentially long) message into a short digest. >>> digest = sha1(b"Hello World!").digest() >>> # Sign and verify signature >>> kinv_rp = do_ecdsa_setup(G, sig_key) >>> sig = do_ecdsa_sign(G, sig_key, digest, kinv_rp = kinv_rp) >>> do_ecdsa_verify(G, ver_key, sig, digest) True
-
petlib.ecdsa.
do_ecdsa_setup
(G, priv)[source]¶ Compute the parameters kinv and rp to (optionally) speed up ECDSA signing.
-
petlib.ecdsa.
do_ecdsa_sign
(G, priv, data, kinv_rp=None)[source]¶ A quick function to ECDSA sign a hash.
- Args:
- G (EcGroup): the group in which math is done. priv (Bn): the secret key. data (str): the string to sign. kinv_rp (opaque): optional setup parameters.
- Returns:
- Bn, Bn: The (r, s) signature
-
petlib.ecdsa.
do_ecdsa_verify
(G, pub, sig, data)[source]¶ A quick function to ECDSA verify a hash.
- Args:
- G (EcGroup): the group in which math is done. pub (EcPt): the secret key sig (Bn, Bn): the (r,s) signature data (str): the string to sign
- Returns:
- bool: A Boolean indicating whether the signature verifies.
Module petlib.pack¶
The module provides functions to pack and unpack petlib Bn, EcGroup, and EcPt strucures.
- Example:
>>> # Define a custom class, encoder and decoder >>> class CustomType: ... def __eq__(self, other): ... return isinstance(other, CustomType) >>> >>> def enc_custom(obj): ... return b'' >>> >>> def dec_custom(data): ... return CustomType() >>> >>> register_coders(CustomType, 10, enc_custom, dec_custom) >>> >>> # Define a structure >>> G = EcGroup() >>> custom_obj = CustomType() >>> test_data = [G, G.generator(), G.order(), custom_obj] >>> >>> # Encode and decode custom structure >>> packed = encode(test_data) >>> x = decode(packed) >>> assert x == test_data
Indices and tables¶
License¶
Petlib is licensed under the following terms:
Copyright (c) 2014, George Danezis (UCL) All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- 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.
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.