Skip to content

Commit

Permalink
Merge pull request #19 from hpicrypto/update/readme
Browse files Browse the repository at this point in the history
Update/readme
  • Loading branch information
j-hellenberg committed Feb 28, 2023
2 parents 36a5b84 + 276718a commit cff8b09
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 23 deletions.
145 changes: 143 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,150 @@
# Proof of Knowledge, Stateful Hash Object

Python implementation of [poksho](https://github.com/signalapp/libsignal/tree/f71e1c6693486cb082124ba9c929d88f2193f585/rust/poksho).
A python implementation for Non-Interactive Zero-Knowledge Proofs (NIZK) of
Arbitrary Linear Relations and
[Stateful Hash Objects](https://github.com/noiseprotocol/sho_spec/blob/master/sho.md).

## Disclaimer

This module was developed as part of the 2022/23 *Current Topics in Group Messaging* seminar at the
[Hasso-Plattner-Institute (HPI)](https://hpi.de) trying to reproduce the Signal Private Group system described
by the paper ["The Signal Private Group System and Anonymous Credentials Supporting Efficient Verifiable Encryption" by
Chase et al.](https://eprint.iacr.org/2019/1416.pdf) Therefore, it is supposed to solely fulfill academic purposes.

**THE AUTHORS DO NOT ASSUME RESPONSIBILITY FOR THE CORRECTNESS OF THE PERFORMED CRYPTOGRAPHIC OPERATIONS.
THERE WAS NO REVIEW PERFORMED BY AN EXPERT.
DO NOT USE THIS PROJECT IN A PRODUCTION ENVIRONMENT.**

## Installation

This project can be installed in multiple ways:

`requirements.txt`: Add `git+https://github.com/hpicrypto/poksho.git@0.1.0#egg=poksho`.

`pip(env)`: Run `pip(env) install git+https://github.com/hpicrypto/poksho.git@0.1.0#egg=poksho`.

`poetry`: Run `poetry add git+https://github.com/hpicrypto/poksho.git@0.1.0`.

## Usage

### NIZK

```python
from poksho.equation import Element, Exponent, make_elements, make_exponents
from poksho.statement import Statement, Equation

# NIZKs can be performed on any group implementing the
# poksho.group.group.GroupType interface.
from poksho.group.ristretto import Group as RistrettoGroup
group = RistrettoGroup

# Create statement variables as symbolic group elements (without a value for now).
# The variable names are not really used for anything, only for printing expressions.
U = Element("U")
V = Element("V")
W = Element("W")

# Helper for creating multiple variables. There is also a make_exponents helper that
# can be used for symbolic exponent variables.
G, H, I = make_elements("G", "H", "I")

# Create a symbolic exponent.
x = Exponent("x")
# Bind a value to the already created exponent variable.
# Note that in a real use case, the "real" values for the variables need to be input here.
# The random values in this example are just for demonstration purposes.
x.bind(group.exponent_cls.random())

# Value can also be bound at time of creation.
y = Exponent("y", group.exponent_cls.random())

# Also bind the other values.
G.bind(group.element_cls.random())
H.bind(group.element_cls.random())
I.bind(group.element_cls.random())

# Create a statement. This involves specifying the group implementation to use.
# Equations that should be contained in the statement can be composed
# from symbolic constants.
statement = Statement(group, Equation(U, G**x * H**y), Equation(V, I**x))

# Equations can also be added to a statement later.
statement.add_equation(Equation(W, G**y))

# Values can also be bound after they were used to describe a statement.
U.bind((G**x * H**y).value)
V.bind((I**x).value)
W.bind((G**y).value)

# Statement.prove() generates a NIZK proof for the statement using the
# currently bound values.
proof = statement.prove()

# The same statement can be used to verify the proof.
assert statement.verify(proof)

# We can also create the same statement from completely new variables.
G_ = Element("G_", G.value)
H_ = Element("H_", H.value)
I_ = Element("I_", I.value)
U_ = Element("U_", U.value)
V_ = Element("V_", V.value)
W_ = Element("W_", W.value)
# For verification, secrets (the exponents) don't have to have a value bound to them.
x_, y_ = make_exponents("x_", "y_")
statement_ = Statement(group,
Equation(U_, G_**x_ * H_**y_),
Equation(V_, I_**x_),
Equation(W_, G_**y_)
)

# Verify the proof for a statement when the secret values are not bound.
assert statement_.verify(proof) is True
```

### SHO

```python
from poksho.sho import SHO

# Create a SHO object
sho = SHO(
customization_label=b'label',
hash_func='sha256', # refer to sho.ALLOWED_HASH_FUNCTIONS
use_hmac=True
)

# Absorbing randomness (like a sponge).
# Generating this randomness is out of scope for this module.
sho.absorb(b"abc")
sho.absorb_and_ratchet(b"def")
sho.absorb(b"ghi")

# The ratchet step must always be performed after we have absorbed values
# and before extracting values from the SHO
sho.ratchet()

# Clone the sho if we want to perform operations on it but still keep the current SHO
# state for another place in the code
sho2 = sho.clone()

# Deterministically "squeeze out" random output based on the random input
# we provided earlier.
# We can specify how long the output we receive should be
output_length = 16
assert sho.squeeze_and_ratchet(output_length) == sho2.squeeze_and_ratchet(output_length)
output = sho2.squeeze_and_ratchet(output_length)
# output = sho2.squeeze() # --> Not supported by our implementation, raises NotImplementedError

# If we forget to ratchet after absorb...
sho.absorb(b'jkl')
# sho.squeeze_and_ratchet(output_length) # --> raises ValueError here
sho.ratchet()
sho.squeeze_and_ratchet(output_length)
```

## Testing

```
PYTHONPATH=. pytest tests
PYTHONPATH+=":$PWD" pytest tests/
```
2 changes: 1 addition & 1 deletion poksho/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
__version__ = "0.0.1"
__version__ = "0.1.0"

from .sho import *
6 changes: 4 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@
version=poksho.__version__,
packages=find_packages(exclude=['tests']),
package_data={'poksho': ['py.typed']},
install_requires=[],
install_requires=[
'curve25519-dalek @ git+https://github.com/tgalal/python-curve25519-dalek@0.0.4#egg=curve25519-dalek'
],
tests_require=['pytest'],
license='GNU Affero General Public License v3.0',
author='',
author_email='',
description='',
long_description='',
platforms='any',
classifiers=['Development Status :: 1 - Planning',
classifiers=['Development Status :: 3 - Alpha',
'Intended Audience :: Developers',
'License :: OSI Approved :: GNU Affero General Public License v3.0',
'Natural Language :: English',
Expand Down
39 changes: 21 additions & 18 deletions tests/test_statement.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,19 +69,21 @@ def group(self, request):
def test_create_statement_example(self, group):
# pylint: disable=too-many-locals

# Create symbolic group elements (without a value for now).
# The names are not really used for anything, only for printing expressions.
# Create statement variables as symbolic group elements (without a value for now).
# The variable names are not really used for anything, only for printing expressions.
U = e.Element("U")
V = e.Element("V")
W = e.Element("W")

# Helper for creating multiple values. make_exponents can also be used
# for symbolic exponents.
# Helper for creating multiple variables. There is also a make_exponents helper that
# can be used for symbolic exponent variables.
G, H, I = e.make_elements("G", "H", "I")

# Create a symbolic exponent.
x = e.Exponent("x")
# Bind a value to the already created exponent.
# Bind a value to the already created exponent variable.
# Note that in a real use case, the "real" values for the variables need to be input here.
# The random values in this example are just for demonstration purposes.
x.bind(group.exponent_cls.random())

# Value can also be bound at time of creation.
Expand All @@ -95,15 +97,15 @@ def test_create_statement_example(self, group):
# Create a statement. This involves specifying the group implementation to use.
# Equations that should be contained in the statement can be composed
# from symbolic constants.
statement = s.Statement(group, s.Equation(U, G**x * H**y), s.Equation(V, I**x))
statement = s.Statement(group, s.Equation(U, G ** x * H ** y), s.Equation(V, I ** x))

# Equations can be added to a statement later.
statement.add_equation(s.Equation(W, G**y))
# Equations can also be added to a statement later.
statement.add_equation(s.Equation(W, G ** y))

# Values can be bound after they were used to describe a statement.
U.bind((G**x * H**y).value)
V.bind((I**x).value)
W.bind((G**y).value)
# Values can also be bound after they were used to describe a statement.
U.bind((G ** x * H ** y).value)
V.bind((I ** x).value)
W.bind((G ** y).value)

# Statement.prove() generates a NIZK proof for the statement using the
# currently bound values.
Expand All @@ -113,18 +115,19 @@ def test_create_statement_example(self, group):
assert statement.verify(proof)

# We can also create the same statement from completely new variables.
# For verification, secrets don"t have to have a value bound to them.
x_, y_ = e.make_exponents("x_", "y_")
G_ = e.Element("G_", G.value)
H_ = e.Element("H_", H.value)
I_ = e.Element("I_", I.value)
U_ = e.Element("U_", U.value)
V_ = e.Element("V_", V.value)
W_ = e.Element("W_", W.value)
statement_ = s.Statement(group,
s.Equation(U_, G_**x_ * H_**y_),
s.Equation(V_, I_**x_),
s.Equation(W_, G_**y_)
# For verification, secrets (the exponents) don't have to have a value bound to them.
x_, y_ = e.make_exponents("x_", "y_")
statement_ = s.Statement(
group,
s.Equation(U_, G_ ** x_ * H_ ** y_),
s.Equation(V_, I_ ** x_),
s.Equation(W_, G_ ** y_)
)

# Verify the proof for a statement when the secret values are not bound.
Expand Down

0 comments on commit cff8b09

Please sign in to comment.