Embed Charm-Crypto's powerful cryptographic schemes directly into your C/C++ applications.
This API allows native applications to use Charm's attribute-based encryption (ABE), identity-based encryption (IBE), digital signatures, and other cryptographic primitives by embedding the Python interpreter.
- Quick Start — Get running in 5 minutes
- Requirements — What you need before building
- Installation — Platform-specific build instructions
- API Reference — Functions and usage patterns
- Examples — Complete working code
- Troubleshooting — Common issues and solutions
- Additional Resources
For experienced developers who want to get running immediately:
# 1. Install dependencies (choose your platform)
# Linux: sudo apt-get install build-essential python3-dev libgmp-dev libpbc-dev
# macOS: brew install gmp pbc
# 2. Configure and build (from charm root directory)
./configure.sh --enable-darwin # macOS only: add --enable-darwin
cd embed/
make
# 3. Run the test
PYTHONPATH=.. ./testExpected output:
DEBUG: cpabe initialized.
DEBUG: hyb_abe initialized.
DEBUG: setup ok.
DEBUG: keygen ok.
DEBUG: encrypt ok.
DEBUG: decrypt ok.
original msg :=> 'this is a test message.'
rec msg :=>
bytes :=> 'this is a test message.'
If you see this output, the embed API is working correctly. Continue reading for detailed instructions and API documentation.
| Dependency | Version | Purpose | Required |
|---|---|---|---|
| Python | 3.8 - 3.11 | Runtime interpreter | ✅ Yes |
| Python dev headers | Same as Python | Python.h for compilation |
✅ Yes |
| GMP | 5.0+ | Big number arithmetic | ✅ Yes |
| PBC | 1.0.0 | Pairing-based cryptography | ✅ Yes |
| OpenSSL | 3.x | Elliptic curves, hashing | Optional |
| GCC/Clang | C99 compatible | Compiler | ✅ Yes |
| Make | Any | Build system | ✅ Yes |
| Platform | Architecture | Status |
|---|---|---|
| Linux (Ubuntu 20.04+, Debian 11+) | x86_64, arm64 | ✅ Fully supported |
| Linux (RHEL 8+, Fedora 35+) | x86_64, arm64 | ✅ Fully supported |
| macOS (11 Big Sur+) | Intel x86_64 | ✅ Fully supported |
| macOS (11 Big Sur+) | Apple Silicon arm64 | ✅ Fully supported |
| Windows (MSYS2/MinGW) | x86_64 |
Choose your platform below. Each section includes dependency installation, build commands, and verification steps.
Click to expand Ubuntu/Debian instructions
sudo apt-get update
sudo apt-get install -y \
build-essential \
python3-dev \
libgmp-dev \
libpbc-dev \
libssl-dev# From the charm root directory
./configure.shExpected output (last few lines):
python /usr/bin/python3
libgmp found yes
libpbc found yes
cd embed/
makeExpected output:
Compiling charm_embed_api.c...
Compiling test.c...
Linking test for linux (x86_64)...
Build complete: test
# Run from the embed/ directory
PYTHONPATH=.. ./testExpected output: See Quick Start section above.
Click to expand RHEL/Fedora instructions
# Fedora / RHEL 8+
sudo dnf install -y \
gcc \
make \
python3-devel \
gmp-devel \
pbc-devel \
openssl-develNote: On older CentOS/RHEL, use
yuminstead ofdnf.
./configure.sh
cd embed/
makePYTHONPATH=.. ./testClick to expand macOS Intel instructions
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"brew install gmp pbc openssl@3# The --enable-darwin flag is REQUIRED on macOS
./configure.sh --enable-darwincd embed/
makePYTHONPATH=.. ./testLibrary Paths: On Intel Macs, Homebrew installs to
/usr/local/. The Makefile detects this automatically.
Click to expand macOS Apple Silicon instructions
brew install gmp pbc openssl@3./configure.sh --enable-darwincd embed/
makePYTHONPATH=.. ./testLibrary Paths: On Apple Silicon, Homebrew installs to
/opt/homebrew/. The Makefile detects this automatically based onuname -m.
If your terminal runs under Rosetta (x86_64 emulation) but your Python and libraries are native arm64, you may encounter architecture mismatch errors.
To check your terminal architecture:
uname -m
# Should output: arm64 (native) or x86_64 (Rosetta)To force native arm64 execution:
arch -arm64 make clean
arch -arm64 make
arch -arm64 ./testClick to expand Windows instructions
⚠️ Warning: Windows support is experimental. Some features may not work correctly.
Download and install from: https://www.msys2.org/
Open "MSYS2 MinGW 64-bit" (not "MSYS2 MSYS" or "UCRT").
# Update package database
pacman -Syu
# Install build tools
pacman -S --noconfirm \
mingw-w64-x86_64-gcc \
mingw-w64-x86_64-make \
mingw-w64-x86_64-python \
mingw-w64-x86_64-python-pip \
mingw-w64-x86_64-gmp \
mingw-w64-x86_64-opensslPBC is not available in MSYS2 packages. You must build it from source:
# Download PBC
wget https://crypto.stanford.edu/pbc/files/pbc-0.5.14.tar.gz
tar xzf pbc-0.5.14.tar.gz
cd pbc-0.5.14
# Configure and build
./configure --prefix=/mingw64
make
make install./configure.sh --build-win-exe
cd embed/
makePYTHONPATH=.. ./test.exeTo see what the Makefile detected about your system:
cd embed/
make infoExample output:
============================================
Charm Embed API Build Configuration
============================================
Platform: macos
Architecture: arm64
Compiler: gcc
Executable: test
Homebrew prefix: /opt/homebrew
============================================
This is useful for debugging build issues.
These functions manage the Python interpreter lifecycle.
#include "charm_embed_api.h"
// Initialize the Python interpreter. Call once at program start.
int InitializeCharm(void);
// Cleanup and finalize Python. Call once at program end.
void CleanupCharm(void);Create mathematical groups for cryptographic operations.
// Create a pairing group for pairing-based crypto (ABE, IBE, etc.)
// curve: "BN254" (128-bit security, recommended) or "SS512" (80-bit, legacy)
Charm_t *InitPairingGroup(Charm_t *pModule, const char *curve);
// Create an elliptic curve group for EC-based crypto
// curve_id: OpenSSL curve NID (e.g., NID_secp256k1)
Charm_t *InitECGroup(Charm_t *pModule, int curve_id);
// Create an integer group for RSA-style crypto
// bits: Key size in bits (e.g., 2048)
Charm_t *InitIntegerGroup(Charm_t *pModule, int bits);Load Charm cryptographic schemes and adapters.
// Load a cryptographic scheme class
// class_file: Python module path (e.g., "charm.schemes.abenc.abenc_bsw07")
// class_name: Class name (e.g., "CPabe_BSW07")
// pObject: Group object from InitPairingGroup/InitECGroup
Charm_t *InitScheme(const char *class_file, const char *class_name, Charm_t *pObject);
// Load an adapter (wraps a scheme with additional functionality)
// pObject1: The underlying scheme
// pObject2: The group object
Charm_t *InitAdapter(const char *class_file, const char *class_name,
Charm_t *pObject1, Charm_t *pObject2);Call methods on Python objects with type-safe argument passing.
// Call a method on a Python object
// func_name: Method name (e.g., "setup", "encrypt", "decrypt")
// types: Format string specifying argument types (see table below)
// ...: Arguments matching the format string
Charm_t *CallMethod(Charm_t *pObject, const char *func_name, char *types, ...);| Specifier | C Type | Python Type | Example |
|---|---|---|---|
%O |
Charm_t* |
Any object | CallMethod(obj, "foo", "%O", other_obj) |
%s |
char* |
str |
CallMethod(obj, "foo", "%s", "hello") |
%b |
char* |
bytes |
CallMethod(obj, "foo", "%b", "data") |
%i |
int |
int |
CallMethod(obj, "foo", "%i", 42) |
%I |
char* |
Group element type | CallMethod(grp, "random", "%I", GT) |
%A |
char* |
Attribute list | CallMethod(obj, "keygen", "%A", "[A, B]") |
Group element type constants: ZR, G1, G2, GT, G
Extract values from Python containers.
// Get item from tuple or list by index
// Returns a NEW reference — you must call Free() when done
Charm_t *GetIndex(Charm_t *pObject, int index);
// Get item from dictionary by key
// Returns a NEW reference — you must call Free() when done
Charm_t *GetDict(Charm_t *pObject, char *key);Convert objects to/from bytes for storage or transmission.
// Serialize a Charm object to bytes
Charm_t *objectToBytes(Charm_t *object, Charm_t *group);
// Deserialize bytes back to a Charm object
Charm_t *bytesToObject(Charm_t *object, Charm_t *group);// Release a Python object reference
// Always call this when you're done with an object
#define Free(obj) Py_XDECREF(obj)
⚠️ Important: Every object returned byGetIndex(),GetDict(),CallMethod(),InitScheme(), etc. must be freed withFree()to prevent memory leaks.
The simplest possible program using the embed API:
#include "charm_embed_api.h"
int main(void) {
// Initialize Python
InitializeCharm();
// Create a pairing group with 128-bit security
Charm_t *group = InitPairingGroup(NULL, "BN254");
if (group == NULL) {
printf("Failed to initialize pairing group\n");
return 1;
}
// Generate a random group element
Charm_t *element = CallMethod(group, "random", "%I", G1);
// Print it
printf("Random G1 element: ");
PyObject_Print(element, stdout, 0);
printf("\n");
// Cleanup
Free(element);
Free(group);
CleanupCharm();
return 0;
}Full attribute-based encryption with key generation, encryption, and decryption:
#include "charm_embed_api.h"
#include <stdio.h>
int main(void) {
// ========================================
// Step 1: Initialize
// ========================================
InitializeCharm();
// Create pairing group (BN254 = 128-bit security)
Charm_t *group = InitPairingGroup(NULL, "BN254");
if (group == NULL) {
printf("ERROR: Failed to create pairing group\n");
return 1;
}
// Load the CP-ABE scheme (Bethencourt-Sahai-Waters 2007)
Charm_t *abe = InitScheme(
"charm.schemes.abenc.abenc_bsw07", // Python module
"CPabe_BSW07", // Class name
group // Pairing group
);
if (abe == NULL) {
printf("ERROR: Failed to load ABE scheme\n");
return 1;
}
// Wrap with hybrid adapter for encrypting arbitrary data
Charm_t *hybrid = InitAdapter(
"charm.adapters.abenc_adapt_hybrid",
"HybridABEnc",
abe, // The underlying ABE scheme
group // The pairing group
);
if (hybrid == NULL) {
printf("ERROR: Failed to load hybrid adapter\n");
return 1;
}
// ========================================
// Step 2: Setup (generate master keys)
// ========================================
Charm_t *keys = CallMethod(hybrid, "setup", "");
Charm_t *public_key = GetIndex(keys, 0); // Public parameters
Charm_t *master_key = GetIndex(keys, 1); // Master secret key
printf("Setup complete. Keys generated.\n");
// ========================================
// Step 3: Key Generation (for a user)
// ========================================
// User has attributes: DEPARTMENT_ENGINEERING and CLEARANCE_SECRET
char *user_attributes = "[DEPARTMENT_ENGINEERING, CLEARANCE_SECRET]";
Charm_t *user_key = CallMethod(
hybrid, "keygen",
"%O%O%A", // Format: object, object, attribute list
public_key,
master_key,
user_attributes
);
printf("User key generated for attributes: %s\n", user_attributes);
// ========================================
// Step 4: Encryption
// ========================================
// Policy: Must have ENGINEERING dept AND (SECRET or TOP_SECRET clearance)
char *policy = "(DEPARTMENT_ENGINEERING and (CLEARANCE_SECRET or CLEARANCE_TOP_SECRET))";
char *message = "This is a classified engineering document.";
Charm_t *ciphertext = CallMethod(
hybrid, "encrypt",
"%O%b%s", // Format: object, bytes, string
public_key,
message,
policy
);
printf("Message encrypted under policy: %s\n", policy);
// ========================================
// Step 5: Decryption
// ========================================
Charm_t *decrypted = CallMethod(
hybrid, "decrypt",
"%O%O%O", // Format: three objects
public_key,
user_key,
ciphertext
);
// Print the decrypted message
printf("\nOriginal: %s\n", message);
printf("Decrypted: ");
// Get the bytes from the Python bytes object
char *result = PyBytes_AsString(decrypted);
if (result) {
printf("%s\n", result);
}
// ========================================
// Step 6: Cleanup (IMPORTANT!)
// ========================================
Free(decrypted);
Free(ciphertext);
Free(user_key);
Free(master_key);
Free(public_key);
Free(keys);
Free(hybrid);
Free(abe);
Free(group);
CleanupCharm();
printf("\nSuccess! All resources cleaned up.\n");
return 0;
}Save and load cryptographic objects:
// Serialize a secret key to bytes (for storage)
Charm_t *sk_bytes = objectToBytes(secret_key, group);
// Get the raw bytes
char *data = PyBytes_AsString(sk_bytes);
Py_ssize_t length = PyBytes_Size(sk_bytes);
// ... save 'data' to file or database ...
// Later: deserialize back to an object
Charm_t *restored_key = bytesToObject(sk_bytes, group);
Free(sk_bytes);
Free(restored_key);Run these commands to diagnose common issues:
# Check build configuration
make info
# Check binary architecture (macOS)
file ./test
# Check linked libraries (Linux)
ldd ./test
# Check linked libraries (macOS)
otool -L ./test
# Check Python path
python3 -c "import charm; print(charm.__file__)"Cause: Python development headers not installed.
Solution:
# Ubuntu/Debian
sudo apt-get install python3-dev
# RHEL/Fedora
sudo dnf install python3-devel
# macOS (Homebrew Python)
brew install python3
# Headers are included automaticallyCause: GMP or PBC development libraries not installed.
Solution:
# Ubuntu/Debian
sudo apt-get install libgmp-dev libpbc-dev
# macOS
brew install gmp pbcAlternative: Specify include paths manually:
make CPPFLAGS="-I/path/to/gmp/include -I/path/to/pbc/include"Cause: Linker can't find library files.
Solution: Specify library paths:
make LDFLAGS="-L/path/to/gmp/lib -L/path/to/pbc/lib"Cause: Python library not in linker path.
Solution (macOS):
# Find Python library location
python3-config --ldflags --embed
# Add to LDFLAGS if needed
make LDFLAGS="-L$(python3 -c 'import sys; print(sys.prefix)')/lib"Cause: Python can't find the Charm package.
Solution: Set PYTHONPATH to the Charm root directory:
# If running from embed/ directory
PYTHONPATH=.. ./test
# If running from charm root directory
PYTHONPATH=. embed/test
# Or use absolute path
PYTHONPATH=/full/path/to/charm ./testCause: Python shared library not in runtime library path.
Solution (Linux):
export LD_LIBRARY_PATH=$(python3 -c 'import sys; print(sys.prefix)')/lib:$LD_LIBRARY_PATH
./testSolution (macOS):
export DYLD_LIBRARY_PATH=$(python3 -c 'import sys; print(sys.prefix)')/lib:$DYLD_LIBRARY_PATH
./testPossible causes:
-
Python version mismatch: Compiled with one Python version, running with another.
# Check which Python was used for compilation make info | grep Python # Check runtime Python python3 --version
-
Architecture mismatch (macOS): Mixing arm64 and x86_64 binaries.
# Check all binaries are same architecture file ./test file $(brew --prefix)/lib/libgmp.dylib file $(python3 -c 'import sys; print(sys.prefix)')/lib/libpython*.dylib
-
Corrupted build: Try a clean rebuild:
make clean make
Cause: Binary architecture doesn't match library architecture.
Solution: Force native architecture build:
# On Apple Silicon
arch -arm64 make clean
arch -arm64 make
arch -arm64 ./test
# On Intel Mac
arch -x86_64 make clean
arch -x86_64 makeFor detailed debugging information:
make clean
make OPTS="-g -O0 -DDEBUG=1"
# Run with debugger
lldb ./test # macOS
gdb ./test # Linux| File | Description |
|---|---|
charm_embed_api.h |
Header file with full API documentation |
charm_embed_api.c |
Implementation of the embed API |
test.c |
Example program demonstrating ABE usage |
Makefile |
Cross-platform build configuration |
- Charm-Crypto Documentation — Full Python API reference
- PBC Library — Pairing-Based Cryptography library
- Python/C API — Python embedding documentation
The embed API can load any Charm scheme. Common ones include:
| Scheme | Module | Class | Type |
|---|---|---|---|
| CP-ABE (BSW07) | charm.schemes.abenc.abenc_bsw07 |
CPabe_BSW07 |
Ciphertext-Policy ABE |
| KP-ABE (LSW08) | charm.schemes.abenc.abenc_lsw08 |
KPabe |
Key-Policy ABE |
| IBE (Waters09) | charm.schemes.ibenc.ibenc_waters09 |
IBE_N04 |
Identity-Based Encryption |
| BLS Signatures | charm.schemes.pksig.pksig_bls04 |
BLS01 |
Short Signatures |
- GitHub Issues: https://github.com/JHUISI/charm/issues
- Email: support@charm-crypto.com