KpqC-0.1.0: cbits/third_party/AIMer/sign.c
// SPDX-License-Identifier: MIT
#include "api.h"
#include "aim3.h"
#include "field.h"
#include "hash.h"
#include "params.h"
#include "sign.h"
#include "tree.h"
#include "common/crypto_declassify.h"
#include "common/rng.h"
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
static void commit_and_expand_tape(tape_t *tape, uint8_t *commit,
const uint8_t *salt,
size_t rep, size_t party,
const uint8_t *seed)
{
hash_instance ctx;
uint8_t rep_byte = (uint8_t)rep;
uint8_t party_byte = (uint8_t)party;
hash_init_prefix(&ctx, HASH_PREFIX_5);
hash_update(&ctx, salt, AIMER_SALT_SIZE);
hash_update(&ctx, &rep_byte, sizeof(rep_byte));
hash_update(&ctx, &party_byte, sizeof(party_byte));
hash_update(&ctx, seed, AIMER_SEED_SIZE);
hash_final(&ctx);
hash_squeeze(&ctx, commit, AIMER_COMMIT_SIZE);
hash_squeeze(&ctx, (uint8_t *)tape, sizeof(tape_t));
hash_ctx_release(&ctx);
}
static inline void adjust_last_share(
proof_t *proof, tape_t *delta, tape_t *tape,
const gf pt_gf, const gf *sbox_outputs)
{
gf_add(delta->pt_share, delta->pt_share, pt_gf);
gf_to_bytes(proof->delta_pt_bytes, delta->pt_share);
gf_add(tape->pt_share, delta->pt_share, tape->pt_share);
for (size_t i = 0; i < AIMER_L; i++)
{
gf_add(delta->y_shares[i], delta->y_shares[i], sbox_outputs[i]);
gf_to_bytes(proof->delta_ys_bytes[i], delta->y_shares[i]);
gf_add(tape->y_shares[i], delta->y_shares[i], tape->y_shares[i]);
}
// c = sum a[i] * y[i] where y = sbox outs
for (size_t i = 0; i < AIMER_L + 1; i++)
{
gf_mul_add(delta->c_share, delta->a_shares[i], sbox_outputs[i]);
}
gf_to_bytes(proof->delta_c_bytes, delta->c_share);
gf_add(tape->c_share, delta->c_share, tape->c_share);
}
// committing to the seeds and the execution views of the parties
// Returns 0 on success or -1 on allocation failure.
static int run_phase_1(signature_t *sign,
uint8_t commits[AIMER_T][AIMER_N][AIMER_COMMIT_SIZE],
uint8_t nodes[AIMER_T][2 * AIMER_N - 1][AIMER_SEED_SIZE],
mult_chk_t mult_chk[AIMER_T][AIMER_N],
const uint8_t *sk, const uint8_t *rnd,
const uint8_t *m, size_t mlen,
const uint8_t *pre, size_t prelen)
{
gf pt_gf = {0,}, ct_gf = {0,};
gf_from_bytes(pt_gf, sk);
gf_from_bytes(ct_gf, sk + AIM3_NUM_BYTES_FIELD + AIM3_IV_SIZE);
// message pre-hashing
hash_instance ctx;
hash_init_prefix(&ctx, HASH_PREFIX_0);
hash_update(&ctx, sk + AIM3_NUM_BYTES_FIELD,
AIM3_IV_SIZE + AIM3_NUM_BYTES_FIELD);
hash_update(&ctx, pre, prelen);
hash_update(&ctx, m, mlen);
hash_final(&ctx);
uint8_t mu[AIMER_COMMIT_SIZE];
hash_squeeze(&ctx, mu, AIMER_COMMIT_SIZE);
hash_ctx_release(&ctx);
// derive the binary matrix and the vector from the initial vector
aim_lin_t *lin = malloc(sizeof(aim_lin_t));
if (lin == NULL)
{
return -2;
}
aim3_generate_linear(lin, sk + AIM3_NUM_BYTES_FIELD);
// compute sboxes' outputs
gf sbox_outputs[AIMER_L + 1];
aim3_sbox_outputs(sbox_outputs, lin, pt_gf, ct_gf);
// generate salt
hash_init_prefix(&ctx, HASH_PREFIX_3);
hash_update(&ctx, sk, AIM3_NUM_BYTES_FIELD);
hash_update(&ctx, mu, AIMER_COMMIT_SIZE);
hash_update(&ctx, rnd, SECURITY_BYTES);
hash_final(&ctx);
hash_squeeze(&ctx, sign->salt, AIMER_SALT_SIZE);
// generate root seeds
uint8_t root_seeds[AIMER_T][AIMER_SEED_SIZE];
hash_squeeze(&ctx, root_seeds[0], sizeof(root_seeds));
hash_ctx_release(&ctx);
// hash_instance for h_1
hash_init_prefix(&ctx, HASH_PREFIX_1);
hash_update(&ctx, mu, AIMER_COMMIT_SIZE);
hash_update(&ctx, sign->salt, AIMER_SALT_SIZE);
// ct_gf is part of the public key, so it is public throughout the MPC
crypto_declassify(ct_gf, sizeof(ct_gf));
for (size_t rep = 0; rep < AIMER_T; rep++)
{
// compute parties' seeds using binary tree
expand_tree(nodes[rep], sign->salt, rep, root_seeds[rep]);
// initialize adjustment values
tape_t delta, tape;
memset(&delta, 0, sizeof(tape_t));
for (size_t party = 0; party < AIMER_N; party++)
{
// generate execution views and commitments
commit_and_expand_tape(&tape, commits[rep][party], sign->salt, rep, party,
nodes[rep][party + AIMER_N - 1]);
// compute offsets
gf_add(delta.pt_share, delta.pt_share, tape.pt_share);
for (size_t i = 0; i < AIMER_L; i++)
{
gf_add(delta.y_shares[i], delta.y_shares[i], tape.y_shares[i]);
}
for (size_t i = 0; i < AIMER_L + 1; i++)
{
gf_add(delta.a_shares[i], delta.a_shares[i], tape.a_shares[i]);
}
gf_add(delta.c_share, delta.c_share, tape.c_share);
// adjust the last share and prepare the proof and h_1
if (party == AIMER_N - 1)
{
adjust_last_share(&sign->proofs[rep], &delta, &tape, pt_gf,
sbox_outputs);
}
// run the MPC simulation and prepare the mult check inputs
aim3_mpc(&mult_chk[rep][party], lin, &tape, ct_gf, party);
}
hash_update(&ctx, (const uint8_t*)commits[rep], AIMER_COMMIT_SIZE * AIMER_N);
// NOTE: depend on the order of values in proof_t
hash_update(&ctx, sign->proofs[rep].delta_pt_bytes,
AIM3_NUM_BYTES_FIELD * (AIMER_L + 2));
}
// commit to salt, (all commitments of parties' seeds,
// delta_pt, delta_t, delta_c) for all repetitions
hash_final(&ctx);
hash_squeeze(&ctx, sign->h_1, AIMER_COMMIT_SIZE);
hash_ctx_release(&ctx);
free(lin);
return 0;
}
static void run_phase_2_and_3(signature_t *sign,
gf alpha_shares[AIMER_T][AIMER_N][AIMER_L + 1],
gf v_share[AIMER_T][AIMER_N],
const mult_chk_t mult_chk[AIMER_T][AIMER_N])
{
gf epsilons[AIMER_L + 1];
gf alpha[AIMER_L + 1];
hash_instance ctx_e;
hash_init(&ctx_e);
hash_update(&ctx_e, sign->h_1, AIMER_COMMIT_SIZE);
hash_final(&ctx_e);
hash_instance ctx;
hash_init_prefix(&ctx, HASH_PREFIX_2);
hash_update(&ctx, sign->h_1, AIMER_COMMIT_SIZE);
hash_update(&ctx, sign->salt, AIMER_SALT_SIZE);
for (size_t rep = 0; rep < AIMER_T; rep++)
{
hash_squeeze(&ctx_e, (uint8_t *)epsilons, sizeof(epsilons));
crypto_declassify(epsilons, sizeof(epsilons));
for (size_t ell = 0; ell < AIMER_L + 1; ell++)
{
gf_set0(alpha[ell]);
}
for (size_t party = 0; party < AIMER_N; party++)
{
// alpha_share[ell] = a_share[ell] + x_share[ell] * eps[ell]
// v_share = c_share - sum alpha[ell] * b_share[ell] + sum z_share[ell] * eps[ell]
gf_copy(v_share[rep][party], mult_chk[rep][party].c_share);
for (size_t ell = 0; ell < AIMER_L + 1; ell++)
{
gf_copy(alpha_shares[rep][party][ell],
mult_chk[rep][party].a_shares[ell]);
gf_mul_add(alpha_shares[rep][party][ell],
mult_chk[rep][party].x_shares[ell], epsilons[ell]);
gf_add(alpha[ell], alpha[ell], alpha_shares[rep][party][ell]);
gf_mul_add(v_share[rep][party],
mult_chk[rep][party].z_shares[ell], epsilons[ell]);
}
}
// alpha is opened, so we can finish calculating v_share
crypto_declassify(alpha, sizeof(alpha));
for (size_t party = 0; party < AIMER_N; party++)
{
for (size_t ell = 0; ell < AIMER_L + 1; ell++)
{
gf_mul_add(v_share[rep][party],
mult_chk[rep][party].b_shares[ell], alpha[ell]);
}
}
hash_update(&ctx, (const uint8_t *)alpha_shares[rep],
AIM3_NUM_BYTES_FIELD * AIMER_N * (AIMER_L + 1));
hash_update(&ctx, (const uint8_t *)v_share[rep],
AIM3_NUM_BYTES_FIELD * AIMER_N);
}
hash_final(&ctx);
hash_squeeze(&ctx, sign->h_2, AIMER_COMMIT_SIZE);
hash_ctx_release(&ctx);
hash_ctx_release(&ctx_e);
}
////////////////////////////////////////////////////////////////////////////////
// Returns 0 on success, -1 when a zero S-box input requires a retry with fresh
// randomness, and a value < -1 for a fatal error such as an allocation failure.
static int crypto_sign_keypair_internal(uint8_t *ct,
const uint8_t *pt, const uint8_t *iv)
{
return aim3_nozeroinpsb(ct, pt, iv);
}
int crypto_sign_keypair(uint8_t *pk, uint8_t *sk)
{
if (!pk || !sk)
{
return -1;
}
uint8_t pt[AIM3_NUM_BYTES_FIELD];
uint8_t iv[AIM3_IV_SIZE];
uint8_t ct[AIM3_NUM_BYTES_FIELD];
while (true)
{
if (randombytes(pt, AIM3_NUM_BYTES_FIELD) != 0 ||
randombytes(iv, AIM3_IV_SIZE) != 0)
{
return -1;
}
int ret = crypto_sign_keypair_internal(ct, pt, iv);
if (ret == 0)
{
break;
}
if (ret != -1)
{
// allocation failure: do not retry
return ret;
}
}
memcpy(pk, iv, AIM3_IV_SIZE);
memcpy(pk + AIM3_IV_SIZE, ct, AIM3_NUM_BYTES_FIELD);
memcpy(sk, pt, AIM3_NUM_BYTES_FIELD);
memcpy(sk + AIM3_NUM_BYTES_FIELD, pk, AIM3_IV_SIZE + AIM3_NUM_BYTES_FIELD);
return 0;
}
static int crypto_sign_signature_internal(uint8_t *sig, size_t *siglen,
const uint8_t *m, size_t mlen,
const uint8_t *pre, size_t prelen,
const uint8_t *rnd, const uint8_t *sk)
{
signature_t *sign = (signature_t *)sig;
//////////////////////////////////////////////////////////////////////////
// Phase 1: Committing to the seeds and the execution views of parties. //
//////////////////////////////////////////////////////////////////////////
// nodes for seed trees
uint8_t (*nodes)[2 * AIMER_N - 1][AIMER_SEED_SIZE] =
malloc(sizeof(*nodes) * AIMER_T);
// commitments for seeds
uint8_t (*commits)[AIMER_N][AIMER_COMMIT_SIZE] =
malloc(sizeof(*commits) * AIMER_T);
// multiplication check inputs
mult_chk_t (*mult_chk)[AIMER_N] = malloc(sizeof(*mult_chk) * AIMER_T);
// multiplication check outputs
gf (*alpha_shares)[AIMER_N][AIMER_L + 1] =
malloc(sizeof(*alpha_shares) * AIMER_T);
gf (*v_share)[AIMER_N] = malloc(sizeof(*v_share) * AIMER_T);
if (!nodes || !commits || !mult_chk || !alpha_shares || !v_share)
{
free(nodes);
free(commits);
free(mult_chk);
free(alpha_shares);
free(v_share);
return -2;
}
memset(mult_chk, 0, AIMER_T * sizeof(*mult_chk));
memset(alpha_shares, 0, AIMER_T * sizeof(*alpha_shares));
memset(v_share, 0, AIMER_T * sizeof(*v_share));
// commitments for phase 1
int status = run_phase_1(sign, commits, nodes, mult_chk, sk, rnd, m, mlen,
pre, prelen);
if (status != 0)
{
free(nodes);
free(commits);
free(mult_chk);
free(alpha_shares);
free(v_share);
return status;
}
/////////////////////////////////////////////////////////////////
// Phase 2, 3: Challenging and committing to the simulation of //
// the multiplication checking protocol. //
/////////////////////////////////////////////////////////////////
// compute the commitment of phase 3
run_phase_2_and_3(sign, alpha_shares, v_share,
(const mult_chk_t (*)[AIMER_N])mult_chk);
//////////////////////////////////////////////////////
// Phase 4: Challenging views of the MPC protocols. //
//////////////////////////////////////////////////////
hash_instance ctx;
hash_init(&ctx);
hash_update(&ctx, sign->h_2, AIMER_COMMIT_SIZE);
hash_final(&ctx);
uint8_t indices[AIMER_T]; // AIMER_N <= 256
hash_squeeze(&ctx, indices, AIMER_T);
hash_ctx_release(&ctx);
for (size_t rep = 0; rep < AIMER_T; rep++)
{
indices[rep] &= (1 << AIMER_LOGN) - 1;
}
//////////////////////////////////////////////////////
// Phase 5: Opening the views of the MPC protocols. //
//////////////////////////////////////////////////////
crypto_declassify(indices, sizeof(indices));
for (size_t rep = 0; rep < AIMER_T; rep++)
{
size_t i_bar = indices[rep];
reveal_all_but(sign->proofs[rep].reveal_path,
(const uint8_t (*)[AIMER_SEED_SIZE])nodes[rep], i_bar);
memcpy(sign->proofs[rep].missing_commitment, commits[rep][i_bar],
AIMER_COMMIT_SIZE);
for (size_t ell = 0; ell < AIMER_L + 1; ell++)
{
gf_to_bytes(sign->proofs[rep].missing_alpha_share_bytes[ell],
alpha_shares[rep][i_bar][ell]);
}
}
*siglen = CRYPTO_BYTES;
free(nodes);
free(commits);
free(mult_chk);
free(alpha_shares);
free(v_share);
return 0;
}
int crypto_sign_signature(uint8_t *sig, size_t *siglen,
const uint8_t *m, size_t mlen,
const uint8_t *ctx, size_t ctxlen, const uint8_t *sk)
{
if (ctxlen > 255)
{
return -1;
}
uint8_t prefix[256];
uint8_t randomness[SECURITY_BYTES];
prefix[0] = ctxlen;
for (size_t i = 0; i < ctxlen; i++)
{
prefix[1 + i] = ctx[i];
}
#ifdef RANDOMIZED_SIGNING
if (randombytes(randomness, SECURITY_BYTES) != 0)
{
return -1;
}
#else
for (size_t i = 0; i < SECURITY_BYTES; i++)
{
randomness[i] = 0;
}
#endif
return crypto_sign_signature_internal(sig, siglen, m, mlen, prefix,
1 + ctxlen, randomness, sk);
}
int crypto_sign(uint8_t *sm, size_t *smlen, const uint8_t *m, size_t mlen,
const uint8_t *ctx, size_t ctxlen, const uint8_t *sk)
{
int ret = crypto_sign_signature(sm + mlen, smlen, m, mlen, ctx, ctxlen, sk);
if (ret != 0)
{
return ret;
}
memcpy(sm, m, mlen);
*smlen += mlen;
return 0;
}
static inline void recompute_last_share(const proof_t *proof, tape_t *tape)
{
gf temp = {0,};
gf_from_bytes(temp, proof->delta_pt_bytes);
gf_add(tape->pt_share, tape->pt_share, temp);
for (size_t ell = 0; ell < AIMER_L; ell++)
{
gf_from_bytes(temp, proof->delta_ys_bytes[ell]);
gf_add(tape->y_shares[ell], tape->y_shares[ell], temp);
}
gf_from_bytes(temp, proof->delta_c_bytes);
gf_add(tape->c_share, tape->c_share, temp);
}
static inline void recompute_missing_alpha(const proof_t *proof,
gf *alpha_shares, gf *alpha)
{
for (size_t ell = 0; ell < AIMER_L + 1; ell++)
{
gf_from_bytes(alpha_shares[ell],
proof->missing_alpha_share_bytes[ell]);
gf_add(alpha[ell], alpha[ell], alpha_shares[ell]);
}
}
static inline void recompute_v_shares(gf *v_shares,
gf (*b_shares)[AIMER_L + 1],
gf *alpha, size_t i_bar)
{
gf_set0(v_shares[i_bar]);
for (size_t party = 0; party < AIMER_N; party++)
{
if (party == i_bar)
{
continue;
}
for (size_t ell = 0; ell < AIMER_L + 1; ell++)
{
gf_mul_add(v_shares[party], b_shares[party][ell], alpha[ell]);
}
gf_add(v_shares[i_bar], v_shares[i_bar], v_shares[party]);
}
}
static int crypto_sign_verify_internal(const uint8_t *sig, size_t siglen,
const uint8_t *m, size_t mlen,
const uint8_t *pre, size_t prelen,
const uint8_t *pk)
{
if (siglen != CRYPTO_BYTES)
{
return -1;
}
const signature_t *sign = (const signature_t *)sig;
gf ct_gf = {0,};
gf_from_bytes(ct_gf, pk + AIM3_IV_SIZE);
// derive the binary matrix and the vector from the initial vector
aim_lin_t *lin = malloc(sizeof(aim_lin_t));
if (lin == NULL)
{
return -2;
}
aim3_generate_linear(lin, pk);
hash_instance ctx_e, ctx_h1, ctx_h2;
// indices = Expand(h_2)
hash_init(&ctx_e);
hash_update(&ctx_e, sign->h_2, AIMER_COMMIT_SIZE);
hash_final(&ctx_e);
uint8_t indices[AIMER_T]; // AIMER_N <= 256
hash_squeeze(&ctx_e, indices, AIMER_T);
hash_ctx_release(&ctx_e);
for (size_t rep = 0; rep < AIMER_T; rep++)
{
indices[rep] &= (1 << AIMER_LOGN) - 1;
}
// epsilons = Expand(h_1)
hash_init(&ctx_e);
hash_update(&ctx_e, sign->h_1, AIMER_COMMIT_SIZE);
hash_final(&ctx_e);
// message pre-hashing
uint8_t mu[AIMER_COMMIT_SIZE];
hash_init_prefix(&ctx_h1, HASH_PREFIX_0);
hash_update(&ctx_h1, pk, AIM3_IV_SIZE + AIM3_NUM_BYTES_FIELD);
hash_update(&ctx_h1, pre, prelen);
hash_update(&ctx_h1, m, mlen);
hash_final(&ctx_h1);
hash_squeeze(&ctx_h1, mu, AIMER_COMMIT_SIZE);
hash_ctx_release(&ctx_h1);
// ready for computing h_1' and h_2'
hash_init_prefix(&ctx_h1, HASH_PREFIX_1);
hash_update(&ctx_h1, mu, AIMER_COMMIT_SIZE);
hash_update(&ctx_h1, sign->salt, AIMER_SALT_SIZE);
hash_init_prefix(&ctx_h2, HASH_PREFIX_2);
hash_update(&ctx_h2, sign->h_1, AIMER_COMMIT_SIZE);
hash_update(&ctx_h2, sign->salt, AIMER_SALT_SIZE);
uint8_t (*nodes)[AIMER_SEED_SIZE] =
malloc(sizeof(*nodes) * (2 * AIMER_N - 2));
gf (*b_shares)[AIMER_L + 1] = malloc(AIMER_N * sizeof(*b_shares));
gf (*alpha_shares)[AIMER_L + 1] = malloc(AIMER_N * sizeof(*alpha_shares));
gf *v_shares = malloc(AIMER_N * sizeof(gf));
if (!nodes || !b_shares || !alpha_shares || !v_shares)
{
free(lin);
free(nodes);
free(b_shares);
free(alpha_shares);
free(v_shares);
return -2;
}
for (size_t rep = 0; rep < AIMER_T; rep++)
{
size_t i_bar = indices[rep];
reconstruct_tree(nodes, sign->salt, sign->proofs[rep].reveal_path,
rep, i_bar);
gf epsilons[AIMER_L + 1];
hash_squeeze(&ctx_e, (uint8_t *)epsilons, sizeof(epsilons));
gf alpha[AIMER_L + 1];
memset(alpha, 0, sizeof(alpha));
for (size_t party = 0; party < AIMER_N; party++)
{
if (party == i_bar)
{
hash_update(&ctx_h1, sign->proofs[rep].missing_commitment,
AIMER_COMMIT_SIZE);
recompute_missing_alpha(&sign->proofs[rep], alpha_shares[i_bar], alpha);
continue;
}
tape_t tape;
uint8_t commit[AIMER_COMMIT_SIZE];
commit_and_expand_tape(&tape, commit, sign->salt, rep, party,
nodes[AIMER_N + party - 2]);
hash_update(&ctx_h1, commit, AIMER_COMMIT_SIZE);
// adjust last shares
mult_chk_t mult_chk;
memset(&mult_chk, 0, sizeof(mult_chk_t));
if (party == AIMER_N - 1)
{
recompute_last_share(&sign->proofs[rep], &tape);
}
// run the MPC simulation and prepare the mult check inputs
aim3_mpc(&mult_chk, lin, &tape, ct_gf, party);
gf_copy(v_shares[party], mult_chk.c_share);
for (size_t ell = 0; ell < AIMER_L + 1; ell++)
{
gf_copy(alpha_shares[party][ell], mult_chk.a_shares[ell]);
gf_mul_add(alpha_shares[party][ell], mult_chk.x_shares[ell],
epsilons[ell]);
gf_add(alpha[ell], alpha[ell], alpha_shares[party][ell]);
gf_copy(b_shares[party][ell], mult_chk.b_shares[ell]);
gf_mul_add(v_shares[party], mult_chk.z_shares[ell], epsilons[ell]);
}
}
// alpha is opened, so we can finish calculating v_share
recompute_v_shares(v_shares, b_shares, alpha, i_bar);
// v is opened
hash_update(&ctx_h2, (const uint8_t *)alpha_shares,
AIM3_NUM_BYTES_FIELD * AIMER_N * (AIMER_L + 1));
hash_update(&ctx_h2, (const uint8_t *)v_shares,
AIM3_NUM_BYTES_FIELD * AIMER_N);
// NOTE: depend on the order of values in proof_t
hash_update(&ctx_h1, sign->proofs[rep].delta_pt_bytes,
AIM3_NUM_BYTES_FIELD * (AIMER_L + 2));
}
hash_ctx_release(&ctx_e);
uint8_t h_1_prime[AIMER_COMMIT_SIZE];
hash_final(&ctx_h1);
hash_squeeze(&ctx_h1, h_1_prime, AIMER_COMMIT_SIZE);
hash_ctx_release(&ctx_h1);
uint8_t h_2_prime[AIMER_COMMIT_SIZE];
hash_final(&ctx_h2);
hash_squeeze(&ctx_h2, h_2_prime, AIMER_COMMIT_SIZE);
hash_ctx_release(&ctx_h2);
free(lin);
free(nodes);
free(b_shares);
free(alpha_shares);
free(v_shares);
if (memcmp(h_1_prime, sign->h_1, AIMER_COMMIT_SIZE) != 0 ||
memcmp(h_2_prime, sign->h_2, AIMER_COMMIT_SIZE) != 0)
{
return -1;
}
return 0;
}
int crypto_sign_verify(const uint8_t *sig, size_t siglen,
const uint8_t *m, size_t mlen,
const uint8_t *ctx, size_t ctxlen, const uint8_t *pk)
{
if (ctxlen > 255)
{
return -1;
}
uint8_t prefix[256];
prefix[0] = ctxlen;
for (size_t i = 0; i < ctxlen; i++)
{
prefix[1 + i] = ctx[i];
}
return crypto_sign_verify_internal(sig, siglen, m, mlen, prefix, 1 + ctxlen,
pk);
}
int crypto_sign_open(uint8_t *m, size_t *mlen, const uint8_t *sm, size_t smlen,
const uint8_t *ctx, size_t ctxlen, const uint8_t *pk)
{
if (smlen < CRYPTO_BYTES)
{
return -1;
}
const size_t message_len = smlen - CRYPTO_BYTES;
const uint8_t *message = sm;
const uint8_t *signature = sm + message_len;
if (crypto_sign_verify(signature, CRYPTO_BYTES, message, message_len,
ctx, ctxlen, pk))
{
return -1;
}
memmove(m, message, message_len);
*mlen = message_len;
return 0;
}