C bindings for the post-quantum cryptography library hash-sig.
This project provides a C API for working with post-quantum hash-based signature schemes. It implements bindings for the XMSS (eXtended Merkle Signature Scheme) using SHA-3 and Winternitz encoding.
- PQSignatureSchemeSecretKey - wrapper for secret key
- PQSignatureSchemePublicKey - wrapper for public key
- PQSignature - wrapper for signature
- PQRange - epoch range representation
- PQSigningError - error codes
// Generate key pair
enum PQSigningError pq_key_gen(
uintptr_t activation_epoch,
uintptr_t num_active_epochs,
struct PQSignatureSchemePublicKey **pk_out,
struct PQSignatureSchemeSecretKey **sk_out
);
// Free memory
void pq_secret_key_free(struct PQSignatureSchemeSecretKey *key);
void pq_public_key_free(struct PQSignatureSchemePublicKey *key);// Sign message
enum PQSigningError pq_sign(
const struct PQSignatureSchemeSecretKey *sk,
uint32_t epoch,
const uint8_t *message,
uintptr_t message_len,
struct PQSignature **signature_out
);
// Verify signature
int pq_verify(
const struct PQSignatureSchemePublicKey *pk,
uint32_t epoch,
const uint8_t *message,
uintptr_t message_len,
const struct PQSignature *signature
);
// Free memory
void pq_signature_free(struct PQSignature *signature);// Get key activation interval
struct PQRange pq_get_activation_interval(const struct PQSignatureSchemeSecretKey *key);
// Get prepared interval
struct PQRange pq_get_prepared_interval(const struct PQSignatureSchemeSecretKey *key);
// Advance preparation to next interval
void pq_advance_preparation(struct PQSignatureSchemeSecretKey *key);
// Get maximum scheme lifetime
uint64_t pq_get_lifetime(void);// Serialize/deserialize keys and signatures
enum PQSigningError pq_secret_key_serialize(...);
enum PQSigningError pq_secret_key_deserialize(...);
enum PQSigningError pq_public_key_serialize(...);
enum PQSigningError pq_public_key_deserialize(...);
enum PQSigningError pq_signature_serialize(...);
enum PQSigningError pq_signature_deserialize(...);// Get error description
char *pq_error_description(enum PQSigningError error);
// Free string
void pq_string_free(char *s);- Rust 1.87 or higher
- Cargo
# Debug build
cargo build
# Release build (recommended for production)
cargo build --releaseAfter building:
- Library:
target/release/libpq_bindings_c_rust.so(Linux) ortarget/release/libpq_bindings_c_rust.dylib(macOS) ortarget/release/pq_bindings_c_rust.dll(Windows) - Static library:
target/release/libpq_bindings_c_rust.a - Header file:
include/pq-bindings-c-rust.h(automatically generated)
cargo testTest Results: 12/12 tests passed (100% API coverage)
#include <stdio.h>
#include <stdint.h>
#include "include/pq-bindings-c-rust.h"
int main() {
// Generate keys
struct PQSignatureSchemePublicKey *pk = NULL;
struct PQSignatureSchemeSecretKey *sk = NULL;
enum PQSigningError result = pq_key_gen(0, 10000, &pk, &sk);
if (result != Success) {
char *error = pq_error_description(result);
printf("Key generation error: %s\n", error);
pq_string_free(error);
return 1;
}
printf("Keys generated successfully!\n");
// Get key information
struct PQRange activation = pq_get_activation_interval(sk);
printf("Activation interval: %lu - %lu\n", activation.start, activation.end);
struct PQRange prepared = pq_get_prepared_interval(sk);
printf("Prepared interval: %lu - %lu\n", prepared.start, prepared.end);
uint64_t lifetime = pq_get_lifetime();
printf("Maximum scheme lifetime: %lu epochs\n", lifetime);
// Prepare message (must be exactly 32 bytes)
uint8_t message[32] = {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20
};
// Sign message
struct PQSignature *signature = NULL;
uint32_t epoch = 100;
result = pq_sign(sk, epoch, message, 32, &signature);
if (result != Success) {
char *error = pq_error_description(result);
printf("Signing error: %s\n", error);
pq_string_free(error);
pq_secret_key_free(sk);
pq_public_key_free(pk);
return 1;
}
printf("Message signed successfully!\n");
// Verify signature
int verify_result = pq_verify(pk, epoch, message, 32, signature);
if (verify_result == 1) {
printf("Signature is valid!\n");
} else if (verify_result == 0) {
printf("Signature is invalid!\n");
} else {
printf("Verification error (code: %d)\n", verify_result);
}
// Free memory
pq_signature_free(signature);
pq_secret_key_free(sk);
pq_public_key_free(pk);
printf("Done!\n");
return 0;
}# Linux
gcc -o example example.c -I. -L./target/release -lpq_bindings_c_rust -lpthread -ldl -lm
# Run
LD_LIBRARY_PATH=./target/release ./exampleOr using Makefile (recommended):
make run-exampleThis implementation uses a synchronized (stateful) signature scheme where:
- Keys have a fixed lifetime divided into epochs
- Each epoch can be used for signing only once
- Reusing an epoch compromises the security of the scheme
- This model is ideal for consensus protocols where validators sign messages at regular intervals
The secret key at any given time can only sign for a limited interval of epochs (prepared interval). Use pq_advance_preparation() to move this window to the next interval when needed.
All messages must be exactly 32 bytes. To sign longer messages, first use a hash function (e.g., SHA-256 or SHA-3).
- All objects created by the library (keys, signatures, strings) must be freed using the corresponding
*_free()functions - Never free pointers manually via
free()- use only the provided functions - After calling
*_free(), the pointer becomes invalid and should not be used
- Never use the same epoch twice with the same key, even to sign the same message
- Store secret keys securely
- Use serialization to save keys between sessions
- Regularly backup secret keys
Current implementation uses:
- Scheme: Generalized XMSS (Winternitz encoding, w=4)
- Hash function: SHA-3 (SHAKE)
- Lifetime: 2^18 epochs (262,144 epochs)
- Message length: 32 bytes
- Tests: 12/12 passed (100% coverage)
- API functions: 20
- Lines of code: 980 (Rust) + 156 (C example)
- Test execution time: 0.94 sec (release), 79 sec (debug)
This project uses the hash-sig library. See the license of the original project: https://github.com/b-wagn/hash-sig/