Skip to content
Snippets Groups Projects
dap_sign.c 22.7 KiB
Newer Older
/*
 * Authors:
 * Dmitriy A. Gearasimov <gerasimov.dmitriy@demlabs.net>
 * DeM Labs Inc.   https://demlabs.net    https:/gitlab.com/demlabs
 * Kelvin Project https://github.com/kelvinblockchain
 * Copyright  (c) 2017-2018
 * All rights reserved.

 This file is part of DAP (Deus Applications Prototypes) the open source project

    DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    DAP is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with any DAP based project.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <string.h>

#include "dap_common.h"
#include "dap_strfuncs.h"
#include "dap_hash.h"
#include "dap_sign.h"
#include "dap_enc_bliss.h"
#include "dap_enc_tesla.h"
#include "dap_enc_picnic.h"
#include "dap_enc_dilithium.h"

#define LOG_TAG "dap_sign"

//static dap_sign_t * s_sign_null = NULL;
static bliss_signature_t s_sign_bliss_null = {0};

// calc signature size
size_t dap_sign_create_output_unserialized_calc_size(dap_enc_key_t * a_key, size_t a_output_wish_size )
{
    (void)a_output_wish_size;

    if(!a_key)
        return 0;
    size_t l_sign_size = 0;
    switch (a_key->type){
        case DAP_ENC_KEY_TYPE_SIG_BLISS: l_sign_size = sizeof(s_sign_bliss_null); break;
        case DAP_ENC_KEY_TYPE_SIG_PICNIC: l_sign_size = dap_enc_picnic_calc_signature_size(a_key); break;
        case DAP_ENC_KEY_TYPE_SIG_TESLA: l_sign_size = dap_enc_tesla_calc_signature_size(); break;
        case DAP_ENC_KEY_TYPE_SIG_DILITHIUM: l_sign_size = dap_enc_dilithium_calc_signature_unserialized_size(); break;
        default : return 0;

    }
    return l_sign_size;
    //return sizeof(s_sign_null->header)+ a_key->pub_key_data_size + l_sign_size;
}


/**
 * @brief dap_sign_type_from_key_type
 * @param a_key_type
 * @return
 */
dap_sign_type_t dap_sign_type_from_key_type( dap_enc_key_type_t a_key_type)
{
    dap_sign_type_t l_sign_type;
    switch (a_key_type){
        case DAP_ENC_KEY_TYPE_SIG_BLISS: l_sign_type.type = SIG_TYPE_BLISS; break;
        case DAP_ENC_KEY_TYPE_SIG_PICNIC: l_sign_type.type = SIG_TYPE_PICNIC; break;
        case DAP_ENC_KEY_TYPE_SIG_TESLA: l_sign_type.type = SIG_TYPE_TESLA; break;
        case DAP_ENC_KEY_TYPE_SIG_DILITHIUM: l_sign_type.type = SIG_TYPE_DILITHIUM; break;
        default: l_sign_type.raw = 0;
    }
    return l_sign_type;
}

/**
 * @brief dap_sign_type_to_key_type
 * @param a_chain_sign_type
 * @return
 */
dap_enc_key_type_t  dap_sign_type_to_key_type(dap_sign_type_t  a_chain_sign_type)
{
    switch (a_chain_sign_type.type) {
        case SIG_TYPE_BLISS: return DAP_ENC_KEY_TYPE_SIG_BLISS;
        case SIG_TYPE_TESLA: return DAP_ENC_KEY_TYPE_SIG_TESLA;
        case SIG_TYPE_PICNIC: return DAP_ENC_KEY_TYPE_SIG_PICNIC;
        case SIG_TYPE_DILITHIUM: return DAP_ENC_KEY_TYPE_SIG_DILITHIUM;
        default: return DAP_ENC_KEY_TYPE_NULL;
    }
}



/**
 * @brief dap_sign_type_to_str
 * @param a_chain_sign_type
 * @return
 */
const char * dap_sign_type_to_str(dap_sign_type_t a_chain_sign_type)
{
    switch (a_chain_sign_type.type) {
        case SIG_TYPE_BLISS: return "sig_bliss";
        case SIG_TYPE_TESLA: return "sig_tesla";
        case SIG_TYPE_PICNIC: return "sig_picnic";
        case SIG_TYPE_DILITHIUM: return "sig_dil";
        case SIG_TYPE_MULTI_COMBINED: return "sig_multi2";
        case SIG_TYPE_MULTI_CHAINED: return "sig_multi";
        default: return "UNDEFINED";//DAP_ENC_KEY_TYPE_NULL;
    }

}

/**
 * @brief dap_pkey_type_from_sign
 * @param a_pkey_type
 * @return
 */
dap_sign_type_t dap_pkey_type_from_sign( dap_pkey_type_t a_pkey_type)
{
    dap_sign_type_t l_sign_type={0};
    switch (a_pkey_type.type){
        case PKEY_TYPE_SIGN_BLISS: l_sign_type.type = SIG_TYPE_BLISS; break;
        case PKEY_TYPE_SIGN_PICNIC: l_sign_type.type = SIG_TYPE_PICNIC; break;
        case PKEY_TYPE_SIGN_TESLA: l_sign_type.type = SIG_TYPE_TESLA; break;
        case PKEY_TYPE_SIGN_DILITHIUM : l_sign_type.type = SIG_TYPE_DILITHIUM; break;
        case PKEY_TYPE_MULTI: l_sign_type.type = SIG_TYPE_MULTI_CHAINED; break;
        case PKEY_TYPE_NULL: l_sign_type.type = SIG_TYPE_NULL; break;
    }
    return l_sign_type;
}


/**
 * @brief dap_sign_type_from_str
 * @param a_type_str
 * @return
 */
dap_sign_type_t dap_sign_type_from_str(const char * a_type_str)
{
    dap_sign_type_t l_sign_type = {0};
    if ( dap_strcmp (a_type_str,"sig_bliss") == 0 ){
        l_sign_type.type = SIG_TYPE_BLISS;
    } else if ( dap_strcmp (a_type_str,"sig_tesla") == 0){
        l_sign_type.type = SIG_TYPE_TESLA;
    } else if ( dap_strcmp (a_type_str,"sig_picnic") == 0){
        l_sign_type.type = SIG_TYPE_PICNIC;
    }else if ( dap_strcmp (a_type_str,"sig_dil") == 0){
        l_sign_type.type = SIG_TYPE_DILITHIUM;
    }else if ( dap_strcmp (a_type_str,"sig_multi") == 0){
        l_sign_type.type = SIG_TYPE_MULTI_CHAINED;
    }else if ( dap_strcmp (a_type_str,"sig_multi2") == 0){
        l_sign_type.type = SIG_TYPE_MULTI_COMBINED;
        log_it(L_WARNING, "Wrong sign type string \"%s\"", a_type_str ? a_type_str : "(null)");
    }
    return l_sign_type;
}

/**
 * @brief dap_sign_create_output
 * @param a_key
 * @param a_data
 * @param a_data_size
 * @param a_output [in/out]
 * @return
 */
static int dap_sign_create_output(dap_enc_key_t *a_key, const void * a_data, const size_t a_data_size,
        void * a_output, size_t *a_output_size)
{
    if(!a_key || !a_key->priv_key_data || !a_key->priv_key_data_size){
        log_it (L_ERROR, "Can't find the private key to create signature");
        return -1;
    }
    switch (a_key->type) {
        case DAP_ENC_KEY_TYPE_SIG_TESLA:
        case DAP_ENC_KEY_TYPE_SIG_PICNIC:
        case DAP_ENC_KEY_TYPE_SIG_DILITHIUM:
                // For PICNIC a_output_size should decrease
            //*a_output_size = dap_enc_sig_dilithium_get_sign(a_key,a_data,a_data_size,a_output,sizeof(dilithium_signature_t));
            a_key->enc_na(a_key, a_data, a_data_size, a_output, *a_output_size);
            return (*a_output_size > 0) ? 0 : -1;

        case DAP_ENC_KEY_TYPE_SIG_BLISS:
            return (dap_enc_sig_bliss_get_sign(a_key, a_data, a_data_size, a_output, *a_output_size) == BLISS_B_NO_ERROR)
                   ? 0 : -1;
        default:
            return -1;
    }
}

/**
 * @brief dap_sign_create
 * @param a_key
 * @param a_data
 * @param a_data_size
 * @param a_output_wish_size
 * @return
 */
dap_sign_t * dap_sign_create(dap_enc_key_t *a_key, const void * a_data,
        const size_t a_data_size, size_t a_output_wish_size)
{
    // calculate max signature size
    size_t l_sign_unserialized_size = dap_sign_create_output_unserialized_calc_size(a_key, a_output_wish_size);
    if(l_sign_unserialized_size > 0) {
        size_t l_pub_key_size = 0;
        uint8_t *l_pub_key = dap_enc_key_serealize_pub_key(a_key, &l_pub_key_size);
        if(!l_pub_key)
            return NULL;
        uint8_t* l_sign_unserialized = DAP_NEW_Z_SIZE(uint8_t, l_sign_unserialized_size);
        // calc signature [sign_size may decrease slightly]
        if( dap_sign_create_output(a_key, a_data, a_data_size,
                                         l_sign_unserialized, &l_sign_unserialized_size) != 0) {
            dap_enc_key_signature_delete(a_key->type, l_sign_unserialized);
            DAP_DELETE(l_pub_key);
            return NULL;
        } else {
            size_t l_sign_ser_size = l_sign_unserialized_size;
            uint8_t *l_sign_ser = dap_enc_key_serealize_sign(a_key->type, l_sign_unserialized, &l_sign_ser_size);
            if ( l_sign_ser ){
                dap_sign_t * l_ret = DAP_NEW_Z_SIZE(dap_sign_t,
                        sizeof(dap_sign_hdr_t) + l_sign_ser_size + l_pub_key_size);
                // write serialized public key to dap_sign_t
                memcpy(l_ret->pkey_n_sign, l_pub_key, l_pub_key_size);
                l_ret->header.type = dap_sign_type_from_key_type(a_key->type);
                // write serialized signature to dap_sign_t
                memcpy(l_ret->pkey_n_sign + l_pub_key_size, l_sign_ser, l_sign_ser_size);
                l_ret->header.sign_pkey_size =(uint32_t) l_pub_key_size;
                l_ret->header.sign_size = (uint32_t) l_sign_ser_size;
                DAP_DELETE(l_sign_ser);
                dap_enc_key_signature_delete(a_key->type, l_sign_unserialized);
                DAP_DELETE(l_pub_key);
                return l_ret;
            }else {
                log_it(L_WARNING,"Can't serialize signature: NULL returned");
                return NULL;
            }
        }
    }
    return NULL;
}

/**
 * @brief dap_sign_get_sign
 * @param a_sign
 * @param a_sign_out
 * @return
 */
uint8_t* dap_sign_get_sign(dap_sign_t *a_sign, size_t *a_sign_out)
{
    if(!a_sign)
        return NULL;
    if(a_sign_out)
    *a_sign_out = a_sign->header.sign_size;
    return a_sign->pkey_n_sign + a_sign->header.sign_pkey_size;
}

/**
 * @brief dap_sign_get_pkey
 * @param a_sign
 * @param a_pub_key_out
 * @return
 */
uint8_t* dap_sign_get_pkey(dap_sign_t *a_sign, size_t *a_pub_key_out)
{
    if(!a_sign)
        return NULL;
    if(a_pub_key_out)
        *a_pub_key_out = a_sign->header.sign_pkey_size;
    return a_sign->pkey_n_sign;
}

/**
 * @brief dap_sign_get_pkey_hash
 * @param a_sign
 * @param a_sign_hash
 * @return
 */
bool dap_sign_get_pkey_hash(dap_sign_t *a_sign, dap_chain_hash_fast_t * a_sign_hash)
{
    if(!a_sign){
        log_it( L_WARNING, "Sign is NULL on enter");
        return false;
    }
    if( ! a_sign->header.sign_pkey_size ){
        log_it( L_WARNING, "Sign public key's size is 0");
        return false;
    }
    return dap_hash_fast( a_sign->pkey_n_sign,a_sign->header.sign_pkey_size,a_sign_hash );
}


/**
 * @brief dap_sign_to_enc_key
 * @param a_chain_sign
 * @return
 */
dap_enc_key_t *dap_sign_to_enc_key(dap_sign_t * a_chain_sign)
{
    dap_enc_key_t * l_ret =  dap_enc_key_new( dap_sign_type_to_key_type( a_chain_sign->header.type  ) );
    size_t l_pkey_size = 0;
    uint8_t *l_pkey = dap_sign_get_pkey(a_chain_sign, &l_pkey_size);
    // deserialize public key
    dap_enc_key_deserealize_pub_key(l_ret, l_pkey, l_pkey_size);
    return l_ret;
}

/**
 * @brief dap_sign_verify
 * @param a_chain_sign
 * @param a_data
 * @param a_data_size
 * @return 1 valid signature, 0 invalid signature, -1 unsupported sign type
 */
int dap_sign_verify(dap_sign_t * a_chain_sign, const void * a_data, const size_t a_data_size)
{
    int l_ret;
    if (!a_chain_sign || !a_data)
Dmitriy A. Gerasimov's avatar
Dmitriy A. Gerasimov committed
        return -2;
    dap_enc_key_t * l_key = dap_sign_to_enc_key(a_chain_sign);
Dmitriy A. Gerasimov's avatar
Dmitriy A. Gerasimov committed

    if ( ! l_key ){
        log_it(L_WARNING,"Incorrect signature, can't extract key");
        return -3;
    }
    size_t l_sign_data_ser_size;
    uint8_t *l_sign_data_ser = dap_sign_get_sign(a_chain_sign, &l_sign_data_ser_size);

    if ( ! l_sign_data_ser ){
        log_it(L_WARNING,"Incorrect signature, can't extract serialized signature's data ");
        return -4;
    }

    size_t l_sign_data_size = a_chain_sign->header.sign_size;
    // deserialize signature
Dmitriy A. Gerasimov's avatar
Dmitriy A. Gerasimov committed
    uint8_t * l_sign_data = dap_enc_key_deserealize_sign(l_key->type, l_sign_data_ser, &l_sign_data_size);

    if ( ! l_sign_data ){
        log_it(L_WARNING,"Incorrect signature, can't deserialize signature's data");
        return -5;
    }

    //uint8_t * l_sign = a_chain_sign->pkey_n_sign + a_chain_sign->header.sign_pkey_size;
    switch (l_key->type) {
Dmitriy A. Gerasimov's avatar
Dmitriy A. Gerasimov committed
        case DAP_ENC_KEY_TYPE_SIG_TESLA:
        case DAP_ENC_KEY_TYPE_SIG_PICNIC:
        case DAP_ENC_KEY_TYPE_SIG_DILITHIUM:
            if(l_key->dec_na(l_key, a_data, a_data_size, l_sign_data, l_sign_data_size) > 0)
                l_ret = 0;
            else
                l_ret = 1;
            break;
        case DAP_ENC_KEY_TYPE_SIG_BLISS:
            if(dap_enc_sig_bliss_verify_sign(l_key, a_data, a_data_size, l_sign_data, l_sign_data_size) != BLISS_B_NO_ERROR)
                l_ret = 0;
            else
                l_ret = 1;
            break;
        default:
            l_ret = -6;
Dmitriy A. Gerasimov's avatar
Dmitriy A. Gerasimov committed
    dap_enc_key_signature_delete(l_key->type, l_sign_data);
    dap_enc_key_delete(l_key);
    return l_ret;
}

/**
 * Get size of struct dap_sign_t
 */
size_t dap_sign_get_size(dap_sign_t * a_chain_sign)
{
    if(!a_chain_sign)
        return 0;
    return (sizeof(dap_sign_t) + a_chain_sign->header.sign_size + a_chain_sign->header.sign_pkey_size);
}

/**
 * @brief dap_multi_sign_params_make Auxiliary function which helps fill multi-signature params structure
 * @param type Type of multi-signature
 * @param total_count Number of total key count
 * @param sign_count Number of keys participating in multi-signing algorithm
 * @param key[1 .. total_count] Set of keys
 * @param num[1 .. sign_count] Signing keys sequence
 * @return Pointer to multi-signature params structure
 */
dap_multi_sign_params_t *dap_multi_sign_params_make(dap_sign_type_enum_t type, uint8_t total_count, uint8_t sign_count, dap_enc_key_t *key1, ...)
{
    dap_multi_sign_params_t *params = DAP_NEW(dap_multi_sign_params_t);
    params->type = type;
    params->total_count = total_count;
    params->keys = DAP_NEW_SIZE(dap_enc_key_t *, total_count * sizeof(dap_enc_key_t *));
    params->sign_count = sign_count;
    params->key_seq = DAP_NEW_SIZE(uint8_t, sign_count);
    params->keys[0] = key1;
    va_list list;
    va_start(list, key1);
    for (int i = 1; i < total_count; i++) {
        params->keys[i] = va_arg(list, dap_enc_key_t *);
    }
    for (int i = 0; i < sign_count; i++) {
        params->key_seq[i] = va_arg(list, int) - 1;
    }
    va_end(list);
    return params;
}

/**
 * @brief dap_multi_sign_delete Destroy multi-signature params structure
 * @param sign Pointer to multi-signature params structure to destroy
 * @return None
 */
void dap_multi_sign_params_delete(dap_multi_sign_params_t *params)
{
    if (!params)
        return;
    if (params->key_seq) {
        DAP_DELETE(params->key_seq);
    }
    if (params->keys) {
        DAP_DELETE(params->keys);
    }
    DAP_DELETE(params);
}

/**
 * @brief dap_multi_sign_hash_data Make multi-signature hash for specified message
 * @param sign Pointer to multi-signature structure
 * @param data Pointer to message to be signed with this multi-signature
 * @param data_size Message size
 * @param hash OUT Pointer to calculated hash
 * @return True if success, overwise return false
 */
bool dap_multi_sign_hash_data(dap_multi_sign_t *sign, const void *data, const size_t data_size, dap_chain_hash_fast_t *hash)
{
    uint8_t *concatenated_hash = DAP_NEW_SIZE(uint8_t, 3 * sizeof(dap_chain_hash_fast_t));
    if (!dap_hash_fast(data, data_size, hash)) {
        DAP_DELETE(concatenated_hash);
        return false;
    }
    memcpy(concatenated_hash, hash, sizeof(dap_chain_hash_fast_t));
    uint32_t meta_data_size = sizeof(dap_sign_type_enum_t) + 2 * sizeof(uint8_t) + sign->sign_count * sizeof(dap_multi_sign_keys_t);
    uint8_t *meta_data = DAP_NEW_SIZE(uint8_t, meta_data_size);
    int meta_data_mem_shift = 0;
    memcpy(meta_data, &sign->type, sizeof(dap_sign_type_enum_t));
    meta_data_mem_shift += sizeof(dap_sign_type_enum_t);
    meta_data[meta_data_mem_shift++] = sign->total_count;
    meta_data[meta_data_mem_shift++] = sign->sign_count;
    memcpy(&meta_data[meta_data_mem_shift], sign->key_seq, sign->sign_count * sizeof(dap_multi_sign_keys_t));
    if (!dap_hash_fast(meta_data, meta_data_size, hash)) {
        DAP_DELETE(meta_data);
        DAP_DELETE(concatenated_hash);
        return false;
    }
    DAP_DELETE(meta_data);
    memcpy(concatenated_hash + sizeof(dap_chain_hash_fast_t), hash, sizeof(dap_chain_hash_fast_t));
    if (!dap_hash_fast(sign->key_hashes, sign->total_count * sizeof(dap_chain_hash_fast_t), hash)) {
        DAP_DELETE(concatenated_hash);
        return false;
    }
    memcpy(concatenated_hash + 2 * sizeof(dap_chain_hash_fast_t), hash, sizeof(dap_chain_hash_fast_t));
    if (!dap_hash_fast(concatenated_hash, 3 * sizeof(dap_chain_hash_fast_t), hash)) {
        DAP_DELETE(concatenated_hash);
        return false;
    }
    DAP_DELETE(concatenated_hash);
    return true;
}

/**
 * @brief dap_multi_sign_create Make multi-signature for specified message
 * @param params Pointer to multi-signature params structure
 * @param data Pointer to message to be signed with this multi-signature
 * @param data_size Message size
 * @return Pointer to multi-signature structure for specified message
 */
dap_multi_sign_t *dap_multi_sign_create(dap_multi_sign_params_t *params, const void *data, const size_t data_size)
{
    if (params->type != SIG_TYPE_MULTI_CHAINED) {
        log_it (L_ERROR, "Unsupported multi-signature type");
        return NULL;
    }
    if (!params || !params->total_count) {
        log_it (L_ERROR, "Can't create multi-signature");
        return NULL;
    }
    dap_multi_sign_t *sign = DAP_NEW_Z(dap_multi_sign_t);
    sign->type = params->type;
    sign->total_count = params->total_count;
    sign->key_hashes = DAP_NEW_SIZE(dap_chain_hash_fast_t, params->total_count * sizeof(dap_chain_hash_fast_t));
    for (int i = 0; i < params->total_count; i++) {
        if (!dap_hash_fast(params->keys[i]->pub_key_data, params->keys[i]->pub_key_data_size, &sign->key_hashes[i])) {
            log_it (L_ERROR, "Can't create multi-signature hash");
            dap_multi_sign_delete(sign);
            return NULL;
        }
    }
    sign->sign_count = params->sign_count;
    sign->key_seq = DAP_NEW_SIZE(dap_multi_sign_keys_t, params->sign_count * sizeof(dap_multi_sign_keys_t));
    sign->meta = DAP_NEW_SIZE(dap_multi_sign_meta_t, params->sign_count * sizeof(dap_multi_sign_meta_t));
    for (int i = 0; i < sign->sign_count; i++) {
        uint8_t num = params->key_seq[i];
        sign->key_seq[i].num = num;
        sign->key_seq[i].type = dap_sign_type_from_key_type(params->keys[num]->type);
    }
    uint32_t pkeys_mem_shift = 0, signs_mem_shift = 0;
    size_t pkey_size, sign_size;
    dap_chain_hash_fast_t data_hash;
    bool hashed;
    for (int i = 0; i < sign->sign_count; i++) {
        if (i == 0) {
             hashed = dap_multi_sign_hash_data(sign, data, data_size, &data_hash);
        } else {
             hashed = dap_hash_fast(&sign->sign_data[signs_mem_shift], sign_size, &data_hash);
             signs_mem_shift += sign_size;
        }
        if (!hashed) {
            log_it (L_ERROR, "Can't create multi-signature hash");
            dap_multi_sign_delete(sign);
            return NULL;
        }
        int num = sign->key_seq[i].num;
        dap_sign_t *dap_sign_step = dap_sign_create(params->keys[num], &data_hash, sizeof(dap_chain_hash_fast_t), 0);
        if (!dap_sign_step) {
            log_it (L_ERROR, "Can't create multi-signature step signature");
            dap_multi_sign_delete(sign);
            return NULL;
        }
        uint8_t *pkey = dap_sign_get_pkey(dap_sign_step, &pkey_size);
        sign->meta[i].pkey_size = pkey_size;
        if (pkeys_mem_shift == 0) {
            sign->pub_keys = DAP_NEW_SIZE(uint8_t, pkey_size);
        } else {
            sign->pub_keys = DAP_REALLOC(sign->pub_keys, pkeys_mem_shift + pkey_size);
        }
        memcpy(&sign->pub_keys[pkeys_mem_shift], pkey, pkey_size);
        pkeys_mem_shift += pkey_size;
        uint8_t *sign_step = dap_sign_get_sign(dap_sign_step, &sign_size);
        sign->meta[i].sign_size = sign_size;
        if (signs_mem_shift == 0) {
            sign->sign_data = DAP_NEW_SIZE(uint8_t, sign_size);
        } else {
            sign->sign_data = DAP_REALLOC(sign->sign_data, signs_mem_shift + sign_size);
        }
        memcpy(&sign->sign_data[signs_mem_shift], sign_step, sign_size);
        DAP_DELETE(dap_sign_step);
    }
    return sign;
}

/**
 * @brief dap_multi_sign_verify Make verification test for multi-signed message
 * @param sign Pointer to multi-signature structure
 * @param data Pointer to message signed with this multi-signature
 * @param data_size Signed message size
 * @return 1 valid signature, 0 invalid signature, -1 verification error
 */
int dap_multi_sign_verify(dap_multi_sign_t *sign, const void *data, const size_t data_size)
{
    if (!sign || !data)
        return -1;
    if (sign->type != SIG_TYPE_MULTI_CHAINED) {
        log_it (L_ERROR, "Unsupported multi-signature type");
        return -1;
    }
    if (!sign->pub_keys || !sign->sign_data || !sign->key_hashes || !sign->meta || !sign->key_seq) {
        log_it (L_ERROR, "Invalid multi-signature format");
        return -1;
    }
    uint32_t pkeys_mem_shift = 0, signs_mem_shift = 0;
    for (int i = 0; i < sign->sign_count - 1; i++) {
        pkeys_mem_shift += sign->meta[i].pkey_size;
        signs_mem_shift += sign->meta[i].sign_size;
    }
    dap_chain_hash_fast_t data_hash;
    bool hashed;
    int verified = 0;
    for (int i = sign->sign_count - 1; i >= 0; i--) {
        size_t pkey_size = sign->meta[i].pkey_size;
        size_t sign_size = sign->meta[i].sign_size;
        dap_sign_t *step_sign = DAP_NEW_Z_SIZE(dap_sign_t,
                sizeof(dap_sign_hdr_t) + pkey_size + sign_size);
        step_sign->header.type = sign->key_seq[i].type;
        step_sign->header.sign_pkey_size = pkey_size;
        step_sign->header.sign_size = sign_size;
        memcpy(step_sign->pkey_n_sign, &sign->pub_keys[pkeys_mem_shift], pkey_size);
        if (i > 0) {
            pkeys_mem_shift -= sign->meta[i - 1].pkey_size;
        }
        memcpy(&step_sign->pkey_n_sign[pkey_size], &sign->sign_data[signs_mem_shift], sign_size);
        if (i > 0) {
            signs_mem_shift -= sign->meta[i - 1].sign_size;
        }
        if (i ==0) {
            hashed = dap_multi_sign_hash_data(sign, data, data_size, &data_hash);
        } else {
            hashed = dap_hash_fast(&sign->sign_data[signs_mem_shift], sign->meta[i - 1].sign_size, &data_hash);
        }
        if (!hashed) {
            log_it (L_ERROR, "Can't create multi-signature hash");
            return -1;
        }
        verified = dap_sign_verify(step_sign, &data_hash, sizeof(dap_chain_hash_fast_t));
        DAP_DELETE(step_sign);
        if (verified != 1) {
            return verified;
        }
    }
    return verified;
}

/**
 * @brief dap_multi_sign_delete Destroy multi-signature structure
 * @param sign Pointer to multi-signature structure to destroy
 * @return None
 */
void dap_multi_sign_delete(dap_multi_sign_t *sign)
{
    if (!sign)
        return;
    if (sign->sign_data) {
        DAP_DELETE(sign->sign_data);
    }
    if (sign->pub_keys) {
        DAP_DELETE(sign->pub_keys);
    }
    if (sign->key_hashes) {
        DAP_DELETE(sign->key_hashes);
    }
    if (sign->meta) {
        DAP_DELETE(sign->meta);
    }
    if (sign->key_seq) {
        DAP_DELETE(sign->key_seq);
    }
    DAP_DELETE(sign);
}