{"id":42315118,"url":"https://github.com/nolai/libpep","last_synced_at":"2026-01-29T13:00:35.341Z","repository":{"id":209001464,"uuid":"721074406","full_name":"NOLAI/libpep","owner":"NOLAI","description":"Library for polymorphic pseudonymization and encryption","archived":false,"fork":false,"pushed_at":"2026-01-27T15:27:29.000Z","size":1169,"stargazers_count":1,"open_issues_count":2,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-01-27T22:49:50.011Z","etag":null,"topics":["pseudonymisation","pseudonymization","rust","rust-library","wasm"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/NOLAI.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-11-20T09:59:19.000Z","updated_at":"2026-01-27T15:28:26.000Z","dependencies_parsed_at":"2026-01-29T13:00:30.732Z","dependency_job_id":null,"html_url":"https://github.com/NOLAI/libpep","commit_stats":null,"previous_names":["jobdoesburg/libpep","nolai/libpep"],"tags_count":32,"template":false,"template_full_name":null,"purl":"pkg:github/NOLAI/libpep","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NOLAI%2Flibpep","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NOLAI%2Flibpep/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NOLAI%2Flibpep/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NOLAI%2Flibpep/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/NOLAI","download_url":"https://codeload.github.com/NOLAI/libpep/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NOLAI%2Flibpep/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28877887,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-29T10:31:27.438Z","status":"ssl_error","status_checked_at":"2026-01-29T10:31:01.017Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["pseudonymisation","pseudonymization","rust","rust-library","wasm"],"created_at":"2026-01-27T12:16:56.181Z","updated_at":"2026-01-29T13:00:35.335Z","avatar_url":"https://github.com/NOLAI.png","language":"Rust","readme":"# `libpep`: Library for polymorphic encryption and pseudonymization\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)](https://crates.io/crates/libpep)\n[![PyPI](https://img.shields.io/pypi/v/libpep-py)](https://pypi.org/project/libpep-py/)\n[![Downloads](https://img.shields.io/pypi/dm/libpep-py)](https://pypi.org/project/libpep-py/)\n[![npm](https://img.shields.io/npm/v/@nolai/libpep-wasm)](https://www.npmjs.com/package/@nolai/libpep-wasm)\n[![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.\nIt can be used to encrypt data and re-encrypt it for different keys without decrypting the data, while pseudonymizing encrypted identifiers in the data.\n\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 (polymorphic): 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\nWith these three operations, encrypted data can be re-encrypted for different keys without decrypting the data, while pseudonymizing encrypted identifiers by reshuffling them with a user-specific factor.\nThe core idea behind 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 non-interactive asynchronous end-to-end encryption with built-in pseudonymisation.\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\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\n## Installation\n\nInstall from crates.io using cargo:\n```\ncargo install libpep\n```\n\nor add as a dependency in your `Cargo.toml`:\n```toml\n[dependencies]\nlibpep = \u003clatest-version\u003e\n```\n\nRun the `peppy` CLI using cargo:\n```\ncargo run --bin peppy\n```\n\nApart from a Rust crate, this library provides bindings for multiple platforms:\n\n### Python\n\nInstall from PyPI:\n```bash\npip install libpep-py\n```\n\n### WebAssembly (WASM)\n\nInstall from npm:\n```bash\nnpm install @nolai/libpep-wasm\n```\n\n## API Structure\n\nThe library is organized into the following main modules, each providing a different level of abstraction and functionality for working with PEP:\n\n| Module | Description |\n|--------|-------------|\n| `arithmetic` | Basic arithmetic operations on scalars and group elements using Curve25519 |\n| `core` | Low-level ElGamal encryption/decryption and PEP primitives (`rekey`, `reshuffle`, `rerandomize`) |\n| `data` | Data types: `Pseudonym`, `Attribute`, JSON structures, long data support, and padding |\n| `keys` | Key management: global keys, session keys, key generation, and distributed key setup |\n| `factors` | Cryptographic factors: secrets, rekey/reshuffle/rerandomize factors, and derivation functions |\n| `transcryptor` | Transcryptor for pseudonymization and rekeying operations |\n| `client` | Client-side encryption and decryption using session keys |\n\n### Keys Module (`keys`)\n\n- `keys::types` - Key type definitions (GlobalPublicKeys, SessionKeys, etc.)\n- `keys::generation` - Functions for generating global and session keys\n- `keys::traits` - Traits for key types\n- `keys::distribution` - Distributed key generation and setup for multi-party transcryptors\n\n### Factors Module (`factors`)\n\n- `factors::types` - Factor types (ReshuffleFactor, RekeyFactor, RerandomizeFactor) and Info type aliases\n- `factors::secrets` - Secret types and derivation functions (PseudonymizationSecret, EncryptionSecret)\n- `factors::contexts` - Context types (PseudonymizationDomain, EncryptionContext)\n\n### Data Module (`data`)\n\n- `data::simple` - Simple `Pseudonym` and `Attribute` types (up to 15 bytes)\n- `data::padding` - Padding utilities for data types\n- `data::long` - Long pseudonyms and attributes (over 15 bytes with PKCS#7 padding) (requires `long` feature)\n- `data::records` - Record types for batch operations\n- `data::json` - JSON structured data with nested pseudonyms and attributes (requires `json` feature)\n- `data::traits` - Common traits for data types\n\n### Prelude\n\nThe library provides convenient prelude modules for common operations:\n\n- `prelude::client` - Re-exports for client-side encryption/decryption operations\n- `prelude::transcryptor` - Re-exports for transcryptor operations (pseudonymization, rekeying, transcryption)\n\n### Distributed Transcryptors\n\nThe library supports distributed n-PEP operations where multiple transcryptors cooperatively perform pseudonymization and rekeying without any single party having access to the global secret keys:\n\n- Key distribution setup is in `keys::distribution`\n- The distributed transcryptor implementation can be found in distributed server/client components\n\nFor detailed API documentation, see [docs.rs/libpep](https://docs.rs/libpep)\n\nBoth Python and WASM bindings mirror the Rust API structure with the same modules and organization.\n\n### Features\n\nThe following features are available:\n\n**Default features** (included unless you use `--no-default-features`):\n- `long`: enables support for long pseudonyms and attributes over 15 bytes using PKCS#7 padding.\n- `offline`: enables offline encryption towards global keys (instead of only session keys).\n- `batch`: enables batch transcryption operations with reordering to prevent linkability.\n- `serde`: enables serialization/deserialization support via Serde.\n- `json`: enables PEP json structured data types.\n- `build-binary`: builds the `peppy` command-line tool.\n\n**Optional features:**\n- `python`: enables Python bindings via PyO3 (mutually exclusive with `wasm`).\n- `wasm`: enables WebAssembly bindings via wasm-bindgen (mutually exclusive with `python`).\n- `elgamal3`: enables ElGamal triple encryption, including the recipient's public key in message encoding. This provides additional security verification but is less efficient.\n- `legacy`: enables compatibility with the legacy PEP repository implementation, which uses a different function to derive scalars from domains, contexts, and secrets.\n- `insecure`: enables methods that expose global secret keys, to be used with care for testing or special use cases.\n- `global-pseudonyms`: enables global pseudonyms (which are insecure).\n\n**Note:** The `python` and `wasm` features are mutually exclusive because PyO3 (Python bindings) builds a cdylib that links to the Python interpreter, while wasm-bindgen builds a cdylib targeting WebAssembly.\nThese have incompatible linking requirements and cannot coexist in the same build.\n\n## Security and Implementation\n\nThis library uses Ristretto encoding on Curve25519, implemented in the [`curve25519-dalek` crate](https://docs.rs/curve25519-dalek/latest/curve25519_dalek/).\n\n### Security Considerations\n- All cryptographic operations use constant-time algorithms to prevent timing attacks\n- Random number generation uses cryptographically secure sources\n- The library has been designed for production use but hasn't yet undergone formal security auditing\n- Users should properly secure private keys and avoid exposing sensitive cryptographic material\n\n### Arithmetic Rules\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## Development\n\n### Prerequisites\n- Rust 1.70+ (MSRV)\n- Node.js 18+ (for WASM bindings)\n- Python 3.8+ (for Python bindings)\n\n### Building and Testing\n\nBuild and test the core Rust library:\n```bash\ncargo build\ncargo test\ncargo clippy\ncargo doc --no-deps\n```\n\nRun tests with different feature combinations:\n```bash\ncargo test --features elgamal3\ncargo test --features legacy\n```\n\n### Building Bindings\n\n#### Python\n\nTo build and test Python bindings:\n```bash\npython -m venv .venv\nsource .venv/bin/activate\npip install maturin pytest\nmaturin develop --features python\npython -m unittest discover tests/python/ -v\n```\n\nTo build a wheel for distribution:\n```bash\nmaturin build --release --features python\n```\n\n#### WASM\n\nTo build and test WASM bindings:\n```bash\nnpm install\nnpm run build  # Builds both Node.js and web targets\nnpm test\n```\n\nTo build for a specific target:\n```bash\nwasm-pack build --target nodejs --features wasm  # For Node.js\nwasm-pack build --target web --features wasm     # For browsers\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","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnolai%2Flibpep","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnolai%2Flibpep","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnolai%2Flibpep/lists"}