static int verify_signature(const bytestring_t *source, const bytestring_t *signature)
{
    static const char *pubkey_value = CARDPEEK_PUBLIC_KEY;
    BIO *bp;
    RSA* pubkey;
    int retval;
    unsigned char digest[SHA256_DIGEST_LENGTH];
    SHA256_CTX sha256;

    if ((bp = BIO_new_mem_buf((void*)pubkey_value,strlen(pubkey_value)))==NULL)
    {
        log_printf(LOG_ERROR,"Failed to build BIO for memory object");
        return 0;
    }

    if ((pubkey = PEM_read_bio_RSA_PUBKEY(bp, NULL, NULL, NULL))==NULL)
    {
        log_printf(LOG_ERROR,"Failed to load public in memory");
        BIO_free(bp);
        return 0;
    }
    BIO_free(bp);

    SHA256_Init(&sha256);
    SHA256_Update(&sha256,bytestring_get_data(source),bytestring_get_size(source));
    SHA256_Final(digest,&sha256);

    retval= RSA_verify(NID_sha256,
            digest,
            SHA256_DIGEST_LENGTH,
            (unsigned char *)bytestring_get_data(signature),
            bytestring_get_size(signature),
            pubkey);

    if (retval==0)
    {
        log_printf(LOG_ERROR,
                   "Signature verification failed for update information (%s)\n",
                   ERR_error_string(ERR_get_error(),NULL));
    }

    RSA_free(pubkey);

    return retval;
}
static int update_filter_files(update_t *update)
{
    unsigned char digest[SHA256_DIGEST_LENGTH];
    char* filename;
    update_item_t **prev = &(update->items);
    update_item_t *item = update->items;
    int excluded = 0;

    while (item)
    {
        filename = new_path(PATH_CONFIG_FOLDER_CARDPEEK,item->file,NULL);

        if (sha256sum(filename,digest)!=0)
        {
            if (memcmp(digest,bytestring_get_data(&item->digest),SHA256_DIGEST_LENGTH)!=0)
            {
                log_printf(LOG_INFO,"File %s needs to be updated.",filename);
                prev = &(item->next);
                item = item->next;
            }
            else
            {
                /* log_printf(LOG_DEBUG,"No update needed for %s",filename); */
                *prev = item->next;                    
                update_item_free(item);
                excluded++;
                item = *prev;
                update->item_count--;
            }
        }
        else
        {
            log_printf(LOG_INFO,"File %s is proposed for creation in current script updates.",filename);
            prev = &(item->next);
            item = item->next;
        }

        g_free(filename);
    }
    return excluded;
}
int cardpeek_update_perform(void)
{
    const char* cardpeek_update_file = path_config_get_string(PATH_CONFIG_FILE_CARDPEEK_UPDATE);
    a_string_t *contents;
    update_t *update;
    int remove;
    update_item_t *item;
    time_t now = time(NULL);
    int updated = 0;
    char *url = NULL;
    char *local_file;
    char *local_dnld;
    unsigned char digest[SHA256_DIGEST_LENGTH];
    a_string_t *url_request;
    unsigned first_update;

    first_update = (unsigned)luax_variable_get_integer("cardpeek.updates.first_update");
    
    /* STEP 1: get cardpeek.update file */

    url=luax_variable_get_strdup("cardpeek.updates.url");

    if (url==NULL)
        url = g_strdup(DEFAULT_UPDATE_URL);

    log_printf(LOG_INFO,"Fetching '%s'",url);

    url_request = a_strnew(NULL);
    a_sprintf(url_request,"%s?u=%x&v=%s",url,first_update,VERSION);

    if (http_download(a_strval(url_request),cardpeek_update_file)==0)
    {
        g_free(url);
        return 0;
    }
    g_free(url);
    a_strfree(url_request);


    /* STEP 2: parse file */

    if ((contents=file_get_contents(cardpeek_update_file))==NULL)
    {
        log_printf(LOG_ERROR,"failed to read update file information.");
        unlink(cardpeek_update_file);
        return 0;
    }

    update = update_new();

    if ((update_load(update,a_strval(contents),a_strlen(contents)))==0)
    {
        unlink(cardpeek_update_file);
        a_strfree(contents);
        update_free(update);
        return 0;
    }
    a_strfree(contents);

    /* log_printf(LOG_DEBUG,"Updates correctly loaded from '%s'",cardpeek_update_file); */
    if ((remove = update_filter_version(update,VERSION))>0)
        log_printf(LOG_WARNING,"%d updates will not be installed because they require a newer version of Cardpeek.");
    
    remove = update_filter_files(update);

    if (update->item_count)
        log_printf(LOG_INFO,"A total of %d files will be updated, %d files are kept unchanged.",update->item_count,remove);
    else
        log_printf(LOG_INFO,"No files will be updated, %d files are kept unchanged.",remove);

    item = update->items;

    while (item)
    {
        local_dnld = new_path(PATH_CONFIG_FOLDER_CARDPEEK,item->file,".download");
        local_file = new_path(PATH_CONFIG_FOLDER_CARDPEEK,item->file,NULL);

        if (http_download(item->url,local_dnld)!=0)
        {        
            if (sha256sum(local_dnld,digest))
            {
                if (memcmp(digest,bytestring_get_data(&item->digest),SHA256_DIGEST_LENGTH)==0)
                {
                    unlink(local_file);

                    if (rename(local_dnld,local_file)==0)
                    {
                        log_printf(LOG_INFO,"Successfuly updated %s", local_file);
                        updated++;
                    }
                    else
                    {
                        log_printf(LOG_ERROR,"Failed to copy %s to %s: %s", 
                                local_dnld,
                                local_file, 
                                strerror(errno));
                    }
                }
                else
                {
                    log_printf(LOG_WARNING,"File %s was not updated: authentication failed.",local_file);
                }
            }
            unlink(local_dnld);
        }
        g_free(local_dnld);
        g_free(local_file);
        item =  item->next; 
    }

    if (updated == update->item_count)
    {    
        luax_variable_set_integer("cardpeek.updates.next_update",(int)(now+7*(24*3600)));  
        luax_config_table_save();
    }

    unlink(cardpeek_update_file);
    update_free(update);

    /* STEP 3: finish */

    return 1;

}
static unsigned short pcsc_transmit(cardreader_t* cr,
                                    const bytestring_t* command,
                                    bytestring_t* result)
{
    pcsc_data_t* pcsc = cr->extra_data;
    BYTE REC_DAT[MAX_PCSC_READ_LENGTH];
    DWORD REC_LEN=MAX_PCSC_READ_LENGTH;
    unsigned short SW;

    if (cr->protocol==SCARD_PROTOCOL_T0)
    {
        pcsc->status = SCardTransmit(pcsc->hcard,SCARD_PCI_T0,
                bytestring_get_data(command),
                bytestring_get_size(command),
                SCARD_PCI_NULL,
                REC_DAT,&REC_LEN);
    }
    else if (cr->protocol==SCARD_PROTOCOL_T1)
    {
        pcsc->status = SCardTransmit(pcsc->hcard,SCARD_PCI_T1,
                bytestring_get_data(command),
                bytestring_get_size(command),
                SCARD_PCI_NULL,
                REC_DAT,&REC_LEN);

    }
    else
    {
        log_printf(LOG_ERROR,"Unknown smartcard protocol: %i",cr->protocol);
        return CARDPEEK_ERROR_SW;
    }

    if (pcsc->status!=SCARD_S_SUCCESS)
    {
        log_printf(LOG_ERROR,"Failed to transmit command to card: %s (error 0x%08x).",
                pcsc_stringify_error(pcsc->status),
                pcsc->status );
        return CARDPEEK_ERROR_SW;
    }

    if (REC_LEN>=2)
    {
        bytestring_assign_data(result,REC_LEN-2,REC_DAT);
        SW = (REC_DAT[REC_LEN-2]<<8)|REC_DAT[REC_LEN-1];
    }
    else if (REC_LEN==1)
    {
        bytestring_clear(result);
        SW = REC_DAT[0];
    }
    else
    {
        log_printf(LOG_ERROR,"Transmited %i bytes to the card (%s), but recieved a response of length %i, without any status word included.",
                bytestring_get_size(command),
                pcsc_stringify_protocol(cr->protocol),
                REC_LEN);
        return CARDPEEK_ERROR_SW;
    }

    return SW;
}