secubox-openwrt/package/secubox/zkp-hamiltonian/tools/zkp_prover.c
CyberMind-FR 6553936886 feat(zkp-hamiltonian): Add Zero-Knowledge Proof library based on Hamiltonian Cycle
Implements NIZK (Non-Interactive Zero-Knowledge) proof protocol using
Blum's Hamiltonian Cycle construction with Fiat-Shamir transformation.

Features:
- Complete C99 library with SHA3-256 commitments (via OpenSSL)
- Graph generation with embedded trapdoor (Hamiltonian cycle)
- NIZK proof generation and verification
- Binary serialization for proofs, graphs, and cycles
- CLI tools: zkp_keygen, zkp_prover, zkp_verifier
- Comprehensive test suite (41 tests)

Security properties:
- Completeness: honest prover always convinces verifier
- Soundness: cheater fails with probability >= 1 - 2^(-128)
- Zero-Knowledge: verifier learns nothing about the secret cycle

Target: OpenWrt ARM (SecuBox authentication module)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-24 09:59:16 +01:00

226 lines
5.8 KiB
C

/**
* @file zkp_prover.c
* @brief CLI tool for generating ZKP proofs
* @version 1.0
*
* Usage: zkp_prover -g <graph_file> -k <key_file> -o <proof_file>
*
* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright (C) 2026 CyberMind.FR / SecuBox
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include "zkp_hamiltonian.h"
#include "zkp_graph.h"
#include "zkp_crypto.h"
static void print_usage(const char *prog)
{
fprintf(stderr, "Usage: %s -g <graph_file> -k <key_file> -o <proof_file>\n", prog);
fprintf(stderr, "\n");
fprintf(stderr, "Options:\n");
fprintf(stderr, " -g, --graph <file> Path to public graph file (.graph)\n");
fprintf(stderr, " -k, --key <file> Path to secret key file (.key)\n");
fprintf(stderr, " -o, --output <file> Path to output proof file (.proof)\n");
fprintf(stderr, " -h, --help Show this help\n");
fprintf(stderr, "\n");
fprintf(stderr, "Example:\n");
fprintf(stderr, " %s -g identity.graph -k identity.key -o auth.proof\n", prog);
}
static int read_file(const char *path, uint8_t **data, size_t *len)
{
FILE *f = fopen(path, "rb");
if (f == NULL) {
perror("fopen");
return -1;
}
fseek(f, 0, SEEK_END);
long size = ftell(f);
fseek(f, 0, SEEK_SET);
if (size <= 0 || size > 16 * 1024 * 1024) { /* Max 16MB */
fprintf(stderr, "Error: invalid file size\n");
fclose(f);
return -1;
}
*data = malloc((size_t)size);
if (*data == NULL) {
fprintf(stderr, "Error: out of memory\n");
fclose(f);
return -1;
}
size_t read = fread(*data, 1, (size_t)size, f);
fclose(f);
if (read != (size_t)size) {
free(*data);
*data = NULL;
return -1;
}
*len = (size_t)size;
return 0;
}
static int write_file(const char *path, const uint8_t *data, size_t len)
{
FILE *f = fopen(path, "wb");
if (f == NULL) {
perror("fopen");
return -1;
}
size_t written = fwrite(data, 1, len, f);
fclose(f);
if (written != len) {
fprintf(stderr, "Error: only wrote %zu of %zu bytes\n", written, len);
return -1;
}
return 0;
}
int main(int argc, char *argv[])
{
const char *graph_path = NULL;
const char *key_path = NULL;
const char *output_path = NULL;
static struct option long_options[] = {
{"graph", required_argument, 0, 'g'},
{"key", required_argument, 0, 'k'},
{"output", required_argument, 0, 'o'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
};
int opt;
while ((opt = getopt_long(argc, argv, "g:k:o:h", long_options, NULL)) != -1) {
switch (opt) {
case 'g':
graph_path = optarg;
break;
case 'k':
key_path = optarg;
break;
case 'o':
output_path = optarg;
break;
case 'h':
print_usage(argv[0]);
return 0;
default:
print_usage(argv[0]);
return 2;
}
}
if (graph_path == NULL || key_path == NULL || output_path == NULL) {
fprintf(stderr, "Error: all options are required\n");
print_usage(argv[0]);
return 2;
}
int ret = 1;
uint8_t *graph_data = NULL;
uint8_t *key_data = NULL;
uint8_t *proof_buf = NULL;
size_t graph_len = 0, key_len = 0;
/* Read graph */
printf("Reading graph from %s...\n", graph_path);
if (read_file(graph_path, &graph_data, &graph_len) != 0) {
fprintf(stderr, "Error: failed to read graph file\n");
goto cleanup;
}
/* Read key */
printf("Reading key from %s...\n", key_path);
if (read_file(key_path, &key_data, &key_len) != 0) {
fprintf(stderr, "Error: failed to read key file\n");
goto cleanup;
}
/* Deserialize graph */
Graph G;
ZKPResult res = zkp_graph_deserialize(graph_data, graph_len, &G);
if (res != ZKP_OK) {
fprintf(stderr, "Error: invalid graph file format\n");
goto cleanup;
}
printf("Graph loaded: n=%u, edges=%u\n", G.n, zkp_graph_edge_count(&G));
/* Deserialize cycle */
HamiltonianCycle H;
res = zkp_cycle_deserialize(key_data, key_len, &H);
if (res != ZKP_OK) {
fprintf(stderr, "Error: invalid key file format\n");
goto cleanup;
}
/* Validate cycle */
if (!zkp_validate_hamiltonian_cycle(&G, &H)) {
fprintf(stderr, "Error: key does not match graph (invalid cycle)\n");
goto cleanup;
}
/* Generate proof */
printf("Generating NIZK proof...\n");
NIZKProof proof;
res = zkp_prove(&G, &H, &proof);
if (res != ZKP_OK) {
fprintf(stderr, "Error: proof generation failed (code %d)\n", res);
goto cleanup;
}
printf("Proof generated: challenge=%u (%s)\n",
proof.challenge,
proof.challenge == 0 ? "isomorphism" : "hamiltonian");
/* Serialize proof */
size_t proof_len = 0;
zkp_proof_serialize(&proof, NULL, &proof_len); /* Get size */
proof_buf = malloc(proof_len);
if (proof_buf == NULL) {
fprintf(stderr, "Error: out of memory\n");
goto cleanup;
}
res = zkp_proof_serialize(&proof, proof_buf, &proof_len);
if (res != ZKP_OK) {
fprintf(stderr, "Error: proof serialization failed\n");
goto cleanup;
}
/* Write proof */
if (write_file(output_path, proof_buf, proof_len) != 0) {
fprintf(stderr, "Error: failed to write proof file\n");
goto cleanup;
}
printf("Wrote proof: %s (%zu bytes)\n", output_path, proof_len);
ret = 0;
cleanup:
/* Zero sensitive data */
if (key_data != NULL) {
zkp_secure_zero(key_data, key_len);
free(key_data);
}
zkp_secure_zero(&H, sizeof(H));
free(graph_data);
free(proof_buf);
return ret;
}