libpep-py


Namelibpep-py JSON
Version 0.6.9 PyPI version JSON
download
home_pagehttps://github.com/NOLAI/libpep
SummaryPython bindings for libpep - implementation of PEP primitives, offering pseudonymization and encryption interfaces
upload_time2025-07-30 11:20:27
maintainerNone
docs_urlNone
authorNone
requires_python>=3.8
licenseApache-2.0
keywords crypto pep pseudonymization
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # `libpep`: Library for polymorphic pseudonymization and encryption
[![Crates.io](https://img.shields.io/crates/v/libpep.svg)](https://crates.io/crates/libpep)
[![Downloads](https://img.shields.io/crates/d/libpep.svg)](https://crates.io/crates/libpep)
[![PyPI](https://img.shields.io/pypi/v/libpep.svg)](https://pypi.org/project/libpep/)
[![PyPI Downloads](https://img.shields.io/pypi/dm/libpep.svg)](https://pypi.org/project/libpep/)
[![npm](https://img.shields.io/npm/v/@nolai/libpep-wasm.svg)](https://www.npmjs.com/package/@nolai/libpep-wasm)
[![npm Downloads](https://img.shields.io/npm/dm/@nolai/libpep-wasm.svg)](https://www.npmjs.com/package/@nolai/libpep-wasm)
[![License](https://img.shields.io/crates/l/libpep.svg)](https://crates.io/crates/libpep)
[![Documentation](https://docs.rs/libpep/badge.svg)](https://docs.rs/libpep)
[![Dependencies](https://deps.rs/repo/github/NOLAI/libpep/status.svg)](https://deps.rs/repo/github/NOLAI/libpep)

This library implements PEP cryptography based on ElGamal encrypted messages.
In the ElGamal scheme, a message `M` can be encrypted for a receiver which has public key `Y` associated with it, belonging to secret key `y`. 
This encryption is random: every time a different random `b` is used, results in different ciphertexts (encrypted messages).
We represent this encryption function as `Enc(b, M, Y)`.

The library supports three homomorphic operations on ciphertext `in` (= `Enc(b, M, Y)`, encrypting message `M` for public key `Y` with random `b`):
- `out = rekey(in, k)`: if `in` can be decrypted by secret key `y`, then `out` can be decrypted by secret key `k*y`.
   Decryption will both result in message `M`. Specifically, `in = Enc(r, M, Y)` is transformed to `out = Enc(r, M, k*Y)`.
- `out = reshuffle(in, s)`: modifies a ciphertext `in` (an encrypted form of `M`), so that after decryption of `out` the decrypted message will be equal to `s*M`.
  Specifically, `in = Enc(r, M, Y)` is transformed to `out = Enc(r, n*M, Y)`.
- `o = rerandomize(in, r)`: scrambles a ciphertext.
  Both `in` and `out` can be decrypted by the same secret key `y`, both resulting in the same decrypted message `M`.
  However, the binary form of `in` and `out` differs. Spec: `in = Enc(b, M, Y)` is transformed to `out = Enc(r+b, M, Y)`;

The `reshuffle(in, n)` and `rekey(in, k)` can be combined in a slightly more efficient `rsk(in, k, n)`.

Additionally, `reshuffle2(in, n_from, n_to)` and `rekey2(in, k_from, k_to)`, as well as `rsk2(...)`, can be used for bidirectional transformations between two keys, effectively applying `k = k_from^-1 * k_to` and `n = n_from^-1 * n_to`.

The key idea behind this form of cryptography is that the pseudonymization and rekeying operations are applied on *encrypted* data.
This means that during initial encryption, the ultimate receiver(s) do(es) not yet need to be known.
Data can initially be encrypted for one key, and later rekeyed and potentially reshuffled (in case of identifiers) for another key, leading to asynchronous end-to-end encryption with built-in pseudonymisation.

Apart from a Rust crate, this library provides bindings for multiple platforms:

## Language Bindings

### Python

Install from PyPI:
```bash
pip install libpep-py
```

Use with direct imports from submodules:
```python
from libpep.high_level import Pseudonym, DataPoint, make_global_keys
from libpep.arithmetic import GroupElement, ScalarNonZero

# Generate keys
keys = make_global_keys()

# Create and work with pseudonyms
pseudonym = Pseudonym.random()
print(f"Pseudonym: {pseudonym.as_hex()}")

# Create data points
data = DataPoint.random()
print(f"Data point: {data.as_hex()}")
```

### WebAssembly (WASM)

Install from npm:
```bash
npm install @nolai/libpep-wasm
```

Use in Node.js or browser applications:
```javascript
import * as libpep from '@nolai/libpep-wasm';

// Generate keys
const keys = libpep.make_global_keys();

// Create and work with pseudonyms
const pseudonym = libpep.Pseudonym.random();
console.log(`Pseudonym: ${pseudonym.as_hex()}`);

// Create data points
const data = libpep.DataPoint.random();
console.log(`Data point: ${data.as_hex()}`);
```

### API Structure

Both Python and WASM bindings mirror the Rust API structure with the same modules:
- `arithmetic` - Basic arithmetic operations on scalars and group elements
- `elgamal` - ElGamal encryption and decryption
- `primitives` - Core PEP operations (`rekey`, `reshuffle`, `rerandomize`)
- `high_level` - User-friendly API with `Pseudonym` and `DataPoint` classes
- `distributed` - Distributed n-PEP operations with multiple servers

## Applications

For pseudonymization, the core operation is *reshuffle* with `s`.
It modifies a main pseudonym with a factor `s` that is specific to a user (or user group) receiving the pseudonym.
After applying a user specific factor `s`, a pseudonym is called a *local pseudonym*.
The factor `s` is typically tied to the *access group* or *domain of a user*, which we call the *pseudonymization domain*.

Using only a reshuffle is insufficient, as the pseudonym is still encrypted for a key the user does not possess.
To allow a user to decrypt the encrypted pseudonym, a *rekey* with `k` is needed, in combination with a protocol to hand the user the secret key `k*y`.
The factor `k` is typically tied to the *current session of a user*, which we call the *encryption context*.

When the same encrypted pseudonym is used multiple times, rerandomize is applied every time.
This way a binary compare of the encrypted pseudonym will not leak any information.

## Implementation

This library is using the Ristretto encoding on Curve25519, implemented in the [`curve25519-dalek` crate](https://docs.rs/curve25519-dalek/latest/curve25519_dalek/), but with [patches by Signal](https://github.com/signalapp/curve25519-dalek) for _lizard_ encoding of arbitrary 16 byte values into ristretto points. 
There are a number of arithmetic rules for scalars and group elements: group elements can be added and subtracted from each other.
Scalars support addition, subtraction, and multiplication.
Division can be done by multiplying with the inverse (using `s.invert()` for non-zero scalar `s`).
A scalar can be converted to a group element (by multiplying with the special generator `G`), but not the other way around.
Group elements can also be multiplied by a scalar.

Group elements have an *almost* 32 byte range (top bit is always zero, and some other values are invalid).
Group elements can be generated by `GroupElement::random(..)` or `GroupElement::from_hash(..)`.
Scalars are also 32 bytes, and can be generated with `Scalar::random(..)` or `Scalar::from_hash(..)`.
There are specific classes for `ScalarNonZero` and `ScalarCanBeZero`, since for almost all PEP operations, the scalar should be non-zero.

## API

We offer APIs at different abstraction levels.

0. The `arithmetic` module (internal API) offers the basic arithmetic operations on scalars and group elements and the `elgamal` module offers the ElGamal encryption and decryption operations.
1. The `primitives` module implements the basic PEP operations such as `rekey`, `reshuffle`, and `rerandomize` and the extended `rekey2` and `reshuffle2` variants, as well as a combined `rsk` and `rsk2` operation.
2. The `high_level` module offer a more user-friendly API with many high level data types such as `Pseudonyms` and `DataPoints`.
3. The `distributed` module additionally provides a high-level API for distributed scenarios, where multiple servers are involved in the rekeying and reshuffling operations and keys are derived from multiple master keys.

Depending on the use case, you can choose the appropriate level of abstraction.

## Development

Build and test the core Rust library:
```bash
cargo build
cargo test
cargo doc --no-deps
```

## Building Bindings

### Python

To build Python bindings for testing:
```bash
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
maturin develop --features python
python -m pytest tests/python/ -v
```

### WASM

To build WASM bindings for testing:
```bash
npm install
npm run build  # Builds both Node.js and web targets
npm test
```

The following features are available:
- `python`: enables the Python bindings.
- `wasm`: enables the WASM library.
- `elgamal3`: enables longer ElGamal for debugging purposes or backward compatibility, but with being less efficient.
- `legacy-pep-repo-compatible`: enables the legacy PEP repository compatible mode, which uses a different function to derive scalars from domains, contexts and secrets.
- `insecure-methods`: enables insecure methods, to be used with care.
- `build-binary`: builds the `peppy` command-line tool to interact with the library (not recommended for production use).

## Install

Install using
```
cargo install libpep
```

Run `peppy` using cargo:
```
cargo run --bin peppy
```

## License
- Authors: Bernard van Gastel and Job Doesburg
- License: Apache License 2.0

## Background

Based on the article by Eric Verheul and Bart Jacobs, *Polymorphic Encryption and Pseudonymisation in Identity Management and Medical Research*. In **Nieuw Archief voor Wiskunde (NAW)**, 5/18, nr. 3, 2017, p. 168-172.


            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/NOLAI/libpep",
    "name": "libpep-py",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": "crypto, pep, pseudonymization",
    "author": null,
    "author_email": "Bernard van Gastel <bvgastel@bitpowder.com>, Job Doesburg <job@jobdoesburg.nl>",
    "download_url": null,
    "platform": null,
    "description": "# `libpep`: Library for polymorphic pseudonymization and encryption\n[![Crates.io](https://img.shields.io/crates/v/libpep.svg)](https://crates.io/crates/libpep)\n[![Downloads](https://img.shields.io/crates/d/libpep.svg)](https://crates.io/crates/libpep)\n[![PyPI](https://img.shields.io/pypi/v/libpep.svg)](https://pypi.org/project/libpep/)\n[![PyPI Downloads](https://img.shields.io/pypi/dm/libpep.svg)](https://pypi.org/project/libpep/)\n[![npm](https://img.shields.io/npm/v/@nolai/libpep-wasm.svg)](https://www.npmjs.com/package/@nolai/libpep-wasm)\n[![npm Downloads](https://img.shields.io/npm/dm/@nolai/libpep-wasm.svg)](https://www.npmjs.com/package/@nolai/libpep-wasm)\n[![License](https://img.shields.io/crates/l/libpep.svg)](https://crates.io/crates/libpep)\n[![Documentation](https://docs.rs/libpep/badge.svg)](https://docs.rs/libpep)\n[![Dependencies](https://deps.rs/repo/github/NOLAI/libpep/status.svg)](https://deps.rs/repo/github/NOLAI/libpep)\n\nThis library implements PEP cryptography based on ElGamal encrypted messages.\nIn the ElGamal scheme, a message `M` can be encrypted for a receiver which has public key `Y` associated with it, belonging to secret key `y`. \nThis encryption is random: every time a different random `b` is used, results in different ciphertexts (encrypted messages).\nWe represent this encryption function as `Enc(b, M, Y)`.\n\nThe library supports three homomorphic operations on ciphertext `in` (= `Enc(b, M, Y)`, encrypting message `M` for public key `Y` with random `b`):\n- `out = rekey(in, k)`: if `in` can be decrypted by secret key `y`, then `out` can be decrypted by secret key `k*y`.\n   Decryption will both result in message `M`. Specifically, `in = Enc(r, M, Y)` is transformed to `out = Enc(r, M, k*Y)`.\n- `out = reshuffle(in, s)`: modifies a ciphertext `in` (an encrypted form of `M`), so that after decryption of `out` the decrypted message will be equal to `s*M`.\n  Specifically, `in = Enc(r, M, Y)` is transformed to `out = Enc(r, n*M, Y)`.\n- `o = rerandomize(in, r)`: scrambles a ciphertext.\n  Both `in` and `out` can be decrypted by the same secret key `y`, both resulting in the same decrypted message `M`.\n  However, the binary form of `in` and `out` differs. Spec: `in = Enc(b, M, Y)` is transformed to `out = Enc(r+b, M, Y)`;\n\nThe `reshuffle(in, n)` and `rekey(in, k)` can be combined in a slightly more efficient `rsk(in, k, n)`.\n\nAdditionally, `reshuffle2(in, n_from, n_to)` and `rekey2(in, k_from, k_to)`, as well as `rsk2(...)`, can be used for bidirectional transformations between two keys, effectively applying `k = k_from^-1 * k_to` and `n = n_from^-1 * n_to`.\n\nThe key idea behind this form of cryptography is that the pseudonymization and rekeying operations are applied on *encrypted* data.\nThis means that during initial encryption, the ultimate receiver(s) do(es) not yet need to be known.\nData can initially be encrypted for one key, and later rekeyed and potentially reshuffled (in case of identifiers) for another key, leading to asynchronous end-to-end encryption with built-in pseudonymisation.\n\nApart from a Rust crate, this library provides bindings for multiple platforms:\n\n## Language Bindings\n\n### Python\n\nInstall from PyPI:\n```bash\npip install libpep-py\n```\n\nUse with direct imports from submodules:\n```python\nfrom libpep.high_level import Pseudonym, DataPoint, make_global_keys\nfrom libpep.arithmetic import GroupElement, ScalarNonZero\n\n# Generate keys\nkeys = make_global_keys()\n\n# Create and work with pseudonyms\npseudonym = Pseudonym.random()\nprint(f\"Pseudonym: {pseudonym.as_hex()}\")\n\n# Create data points\ndata = DataPoint.random()\nprint(f\"Data point: {data.as_hex()}\")\n```\n\n### WebAssembly (WASM)\n\nInstall from npm:\n```bash\nnpm install @nolai/libpep-wasm\n```\n\nUse in Node.js or browser applications:\n```javascript\nimport * as libpep from '@nolai/libpep-wasm';\n\n// Generate keys\nconst keys = libpep.make_global_keys();\n\n// Create and work with pseudonyms\nconst pseudonym = libpep.Pseudonym.random();\nconsole.log(`Pseudonym: ${pseudonym.as_hex()}`);\n\n// Create data points\nconst data = libpep.DataPoint.random();\nconsole.log(`Data point: ${data.as_hex()}`);\n```\n\n### API Structure\n\nBoth Python and WASM bindings mirror the Rust API structure with the same modules:\n- `arithmetic` - Basic arithmetic operations on scalars and group elements\n- `elgamal` - ElGamal encryption and decryption\n- `primitives` - Core PEP operations (`rekey`, `reshuffle`, `rerandomize`)\n- `high_level` - User-friendly API with `Pseudonym` and `DataPoint` classes\n- `distributed` - Distributed n-PEP operations with multiple servers\n\n## Applications\n\nFor pseudonymization, the core operation is *reshuffle* with `s`.\nIt modifies a main pseudonym with a factor `s` that is specific to a user (or user group) receiving the pseudonym.\nAfter applying a user specific factor `s`, a pseudonym is called a *local pseudonym*.\nThe factor `s` is typically tied to the *access group* or *domain of a user*, which we call the *pseudonymization domain*.\n\nUsing only a reshuffle is insufficient, as the pseudonym is still encrypted for a key the user does not possess.\nTo allow a user to decrypt the encrypted pseudonym, a *rekey* with `k` is needed, in combination with a protocol to hand the user the secret key `k*y`.\nThe factor `k` is typically tied to the *current session of a user*, which we call the *encryption context*.\n\nWhen the same encrypted pseudonym is used multiple times, rerandomize is applied every time.\nThis way a binary compare of the encrypted pseudonym will not leak any information.\n\n## Implementation\n\nThis library is using the Ristretto encoding on Curve25519, implemented in the [`curve25519-dalek` crate](https://docs.rs/curve25519-dalek/latest/curve25519_dalek/), but with [patches by Signal](https://github.com/signalapp/curve25519-dalek) for _lizard_ encoding of arbitrary 16 byte values into ristretto points. \nThere are a number of arithmetic rules for scalars and group elements: group elements can be added and subtracted from each other.\nScalars support addition, subtraction, and multiplication.\nDivision can be done by multiplying with the inverse (using `s.invert()` for non-zero scalar `s`).\nA scalar can be converted to a group element (by multiplying with the special generator `G`), but not the other way around.\nGroup elements can also be multiplied by a scalar.\n\nGroup elements have an *almost* 32 byte range (top bit is always zero, and some other values are invalid).\nGroup elements can be generated by `GroupElement::random(..)` or `GroupElement::from_hash(..)`.\nScalars are also 32 bytes, and can be generated with `Scalar::random(..)` or `Scalar::from_hash(..)`.\nThere are specific classes for `ScalarNonZero` and `ScalarCanBeZero`, since for almost all PEP operations, the scalar should be non-zero.\n\n## API\n\nWe offer APIs at different abstraction levels.\n\n0. The `arithmetic` module (internal API) offers the basic arithmetic operations on scalars and group elements and the `elgamal` module offers the ElGamal encryption and decryption operations.\n1. The `primitives` module implements the basic PEP operations such as `rekey`, `reshuffle`, and `rerandomize` and the extended `rekey2` and `reshuffle2` variants, as well as a combined `rsk` and `rsk2` operation.\n2. The `high_level` module offer a more user-friendly API with many high level data types such as `Pseudonyms` and `DataPoints`.\n3. The `distributed` module additionally provides a high-level API for distributed scenarios, where multiple servers are involved in the rekeying and reshuffling operations and keys are derived from multiple master keys.\n\nDepending on the use case, you can choose the appropriate level of abstraction.\n\n## Development\n\nBuild and test the core Rust library:\n```bash\ncargo build\ncargo test\ncargo doc --no-deps\n```\n\n## Building Bindings\n\n### Python\n\nTo build Python bindings for testing:\n```bash\npython -m venv .venv\nsource .venv/bin/activate\npip install -e \".[dev]\"\nmaturin develop --features python\npython -m pytest tests/python/ -v\n```\n\n### WASM\n\nTo build WASM bindings for testing:\n```bash\nnpm install\nnpm run build  # Builds both Node.js and web targets\nnpm test\n```\n\nThe following features are available:\n- `python`: enables the Python bindings.\n- `wasm`: enables the WASM library.\n- `elgamal3`: enables longer ElGamal for debugging purposes or backward compatibility, but with being less efficient.\n- `legacy-pep-repo-compatible`: enables the legacy PEP repository compatible mode, which uses a different function to derive scalars from domains, contexts and secrets.\n- `insecure-methods`: enables insecure methods, to be used with care.\n- `build-binary`: builds the `peppy` command-line tool to interact with the library (not recommended for production use).\n\n## Install\n\nInstall using\n```\ncargo install libpep\n```\n\nRun `peppy` using cargo:\n```\ncargo run --bin peppy\n```\n\n## License\n- Authors: Bernard van Gastel and Job Doesburg\n- License: Apache License 2.0\n\n## Background\n\nBased on the article by Eric Verheul and Bart Jacobs, *Polymorphic Encryption and Pseudonymisation in Identity Management and Medical Research*. In **Nieuw Archief voor Wiskunde (NAW)**, 5/18, nr. 3, 2017, p. 168-172.\n\n",
    "bugtrack_url": null,
    "license": "Apache-2.0",
    "summary": "Python bindings for libpep - implementation of PEP primitives, offering pseudonymization and encryption interfaces",
    "version": "0.6.9",
    "project_urls": {
        "Documentation": "https://docs.rs/libpep",
        "Homepage": "https://github.com/NOLAI/libpep",
        "Repository": "https://github.com/NOLAI/libpep"
    },
    "split_keywords": [
        "crypto",
        " pep",
        " pseudonymization"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "51b0c1988bb9dc2509630181eb36a6c8d3009605cfb03ce82e0c99cc77fa72e7",
                "md5": "62463acd2eee9511394d868dbafb3f23",
                "sha256": "e7e6fe99a28518e08e9be8db9d6a5366fb71020a9169939bc3feb583e1ffc7ef"
            },
            "downloads": -1,
            "filename": "libpep_py-0.6.9-cp310-cp310-macosx_11_0_arm64.whl",
            "has_sig": false,
            "md5_digest": "62463acd2eee9511394d868dbafb3f23",
            "packagetype": "bdist_wheel",
            "python_version": "cp310",
            "requires_python": ">=3.8",
            "size": 397169,
            "upload_time": "2025-07-30T11:20:27",
            "upload_time_iso_8601": "2025-07-30T11:20:27.681715Z",
            "url": "https://files.pythonhosted.org/packages/51/b0/c1988bb9dc2509630181eb36a6c8d3009605cfb03ce82e0c99cc77fa72e7/libpep_py-0.6.9-cp310-cp310-macosx_11_0_arm64.whl",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-07-30 11:20:27",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "NOLAI",
    "github_project": "libpep",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "libpep-py"
}
        
Elapsed time: 0.92602s