packages feed

second-transfer-0.10.0.1: cbits/enable_tls.cpp

#ifdef INCLUDE_BOTAN_ALL_H
#include "botan_all.h"
#else

#include <string>
#include <sstream>

#include <botan/botan.h>
#include <botan/tls_session.h>
#include <botan/tls_alert.h>
#include <botan/tls_policy.h>
#include <botan/tls_exceptn.h>
#include <botan/exceptn.h>
#include <botan/credentials_manager.h>
#include <botan/tls_channel.h>
#include <botan/pkcs8.h>
#include <botan/tls_session_manager.h>
#include <botan/tls_server.h>
#include <botan/data_src.h>
#endif

#include "Botan_stub.h"
#include <functional>

#include <cstdlib>

// TODO:
//
// Consider marking these functions "safe" in Haskell code.
// For that to work, you need first to change them in such a way that no lock
// takes place, that is, you shouldn't call back to Haskell code from here, but
// instead have the callbacks to simply write that in a provided buffer.
//
// This is also a good point to write about the "TLS overhead."

namespace second_transfer {

// Just because of the conversions, but it may be a handy place for
// other stuff later.
void output_dn_cb (void* botan_pad_ref, const unsigned char a[], size_t sz)
{
    iocba_push(botan_pad_ref, (char*)a, sz);
}

void data_cb (void* botan_pad_ref, const unsigned char a[], size_t sz)
{
    iocba_data_cb(botan_pad_ref, (char*)a, sz);
}

void alert_cb (void* botan_pad_ref, Botan::TLS::Alert const& alert, const unsigned char a[], size_t sz) 
{
    // printf("BOTAN WAS TO DELIVER ALERT: %d \n", alert.type_string().c_str());
    if (alert.is_valid() && alert.is_fatal() )
    {
        // TODO: Propagate this softly.
        iocba_alert_cb(botan_pad_ref, -1);
    } else {
        // printf("Ignore an alert!!\n");
    }
}

bool handshake_cb(void* botan_pad_ref, const Botan::TLS::Session&)
{
    iocba_handshake_cb(botan_pad_ref);
    // TODO: Implement cache management
    return false;
}

// TODO: We can use stronger ciphers here. For now let's go simple
class HereTLSPolicy: public Botan::TLS::Policy {
public:
    virtual bool acceptable_protocol_version(const Botan::TLS::Protocol_Version& v)
    {
        return v == Botan::TLS::Protocol_Version::TLS_V12;
    }

    virtual std::vector<std::string> allowed_macs() const
    {
        std::vector<std::string> result;
        result.push_back("AEAD");
        result.push_back("SHA-384");
        result.push_back("SHA-256");
        //
        result.push_back("SHA-1");
        return result;
    }

    virtual std::vector<std::string> allowed_ciphers() const
    {
        std::vector<std::string> result;
        result.push_back("ChaCha20Poly1305");
        result.push_back("AES-256/GCM");
        result.push_back("AES-128/GCM");
        result.push_back("Camellia-256/GCM");
        result.push_back("AES-256/OCB(12)");
        result.push_back("AES-256/CCM");
        // For use with old browsers....
        result.push_back("AES-256");
        result.push_back("3DES");
        return result;
    }

    virtual std::vector<std::string> allowed_key_exchange_methods() const
    {
        std::vector<std::string> result;
        result.push_back("ECDH");
        result.push_back("DH");
        result.push_back("RSA");
        result.push_back("EDCHE");
        return result;
    }

    virtual std::vector<std::string> allowed_signature_hashes() const
    {
        std::vector<std::string> result;
        result.push_back("SHA-256");
        return result;
    }

    virtual std::vector<std::string> allowed_signature_methods() const
    {
        std::vector<std::string> result;
        result.push_back("RSA");
        return result;
    }

    // This is experimental, may need to change.
    virtual bool server_uses_own_ciphersuite_preferences() const
    {
        return true;
    }

};

// TODO: We can use different certificates if needs come....
class HereCredentialsManager: public Botan::Credentials_Manager {
    std::vector<Botan::X509_Certificate> certs;
    Botan::Private_Key* privkey;
public:
    HereCredentialsManager(
        const char* cert_filename,
        const char* privkey_filename,
        Botan::AutoSeeded_RNG& rng
        )
    {
        // In addition to  the certificate itself, build a chain
        // of certificates
        Botan::DataSource_Stream dss(cert_filename);
        int i = 0;
        while ( not dss.end_of_data() )
        {
            try {
                certs.push_back(Botan::X509_Certificate(dss));
            } catch (Botan::Decoding_Error const& err)
            {
                if ( i == 0)
                {
                    throw;
                } else
                {
                    break;
                }
            }
            i ++;
        }
        privkey = Botan::PKCS8::load_key(
            privkey_filename, rng
        );
    }

    HereCredentialsManager(
        const uint8_t* cert_data,
        uint32_t cert_data_len,
        const uint8_t* privkey_data,
        uint32_t privkey_data_len,
        Botan::AutoSeeded_RNG& rng
        )
    {
        std::string cert_string((char*)cert_data, cert_data_len);
        std::stringstream certs_source(cert_string);
        std::string key_string((char*)privkey_data, privkey_data_len);
        std::stringstream key_source(key_string);
        // In addition to  the certificate itself, build a chain
        // of certificates
        Botan::DataSource_Stream certs_source_(certs_source);
        Botan::DataSource_Stream key_source_(key_source);
        int i = 0;
        while ( not certs_source_.end_of_data() )
        {
            try {
                certs.push_back(Botan::X509_Certificate(certs_source_));
            } catch (Botan::Decoding_Error const& err)
            {
                if ( i == 0)
                {
                    throw;
                } else
                {
                    break;
                }
            }
            i ++;
        }
        privkey = Botan::PKCS8::load_key(
            key_source_, rng
        );
    }

    virtual std::vector<
        Botan::X509_Certificate > cert_chain(
            const std::vector< std::string >&,
            const std::string&,
            const std::string& )
    {
        return certs;
    }

    virtual Botan::Private_Key*  private_key_for(const Botan::X509_Certificate &cert, const std::string & type, const std::string &context)
    {
        return privkey;
    }

    ~HereCredentialsManager()
    {
        delete privkey;
    }
};

std::string defaultProtocolSelector(void* botan_pad_ref, std::vector<std::string> const& prots)
{
    std::string pass_to_haskell;
    bool is_first = true;
    for (int i=0; i < prots.size(); i++ )
    {
        if ( is_first )
        {
            is_first=false;
        } else
        {
            pass_to_haskell += '\0';
        }
        pass_to_haskell += prots[i];
        //printf("Prot: %s \n", prots[i].c_str());
    }
    int idx = iocba_select_protocol_cb( botan_pad_ref, (void*)pass_to_haskell.c_str(), pass_to_haskell.size());
    //printf("Prot selected: %d \n", idx);
    if ( idx >= 0 )
        return prots[idx];
    else
        throw Botan::TLS::TLS_Exception( Botan::TLS::Alert::NO_APPLICATION_PROTOCOL, "ShimmerCat:NoApplicationProtocol" );
}

} // namespace

extern "C" int iocba_receive_data(
    void* tls_channel,
    char* data,
    int length )
{
    Botan::TLS::Channel* channel = (Botan::TLS::Channel*) tls_channel;
    try {
        //printf("Before taking data=%p \n", channel);
        size_t more_data_required = channel->received_data( (const unsigned char*) data, length);
        //printf("More data required %d \n", more_data_required);
        //printf("After taking data=%p \n", channel);
    } catch (std::exception const& e)
    {
        // TODO: control messages
        printf("BotanTLS engine instance crashed (normal if ALPN didn't go well): %s \n", e.what());
        return -1;
    }
    return 0;
}

extern "C" void iocba_cleartext_push(
    void* tls_channel,
    char* data,
    int length )
{
    // TODO: Check for "can send"!!!!!
    // OTHERWISE THIS WON'T WORK
    try {
        Botan::TLS::Channel* channel = (Botan::TLS::Channel*) tls_channel;
        // printf("Before send channel=%p \n", channel);
        channel->send( (const unsigned char*) data, length);
        // printf("After send channel=%p \n", channel);
    } catch (...)
    {
        printf("BotanTLS engine raised exception on send\n");
    }
}


extern "C" void iocba_close(
    void* tls_channel
    )
{
    Botan::TLS::Channel* channel = (Botan::TLS::Channel*) tls_channel;
    try{
        channel->close();
    } catch (...)
    {
        printf("BotanTLS engine raised exception on close\n");
    }
}


struct botan_tls_context_t {
    second_transfer::HereCredentialsManager credentials_manager;
    Botan::TLS::Session_Manager_In_Memory* session_manager;
    Botan::AutoSeeded_RNG* rng;
    std::vector<std::string> protocols;
    second_transfer::HereTLSPolicy here_tls_policty;
};

extern "C" botan_tls_context_t* iocba_make_tls_context(
    const char* cert_filename,
    const char* privkey_filename
    )
{
    Botan::AutoSeeded_RNG* rng=new Botan::AutoSeeded_RNG();
    std::vector< std::string > protocols;

    // Not sure what is this good for....
    protocols.push_back("h2");
    protocols.push_back("http/1.1");

    return new botan_tls_context_t{
        second_transfer::HereCredentialsManager(cert_filename, privkey_filename, *rng),
        new Botan::TLS::Session_Manager_In_Memory(*rng),
        rng,
        protocols,
        second_transfer::HereTLSPolicy()
    };
}

// Oh Gods, forgive me
pthread_mutex_t new_ctx_mutex = PTHREAD_MUTEX_INITIALIZER ;
pthread_mutex_t new_channel_mutex = PTHREAD_MUTEX_INITIALIZER ;

extern "C" botan_tls_context_t* iocba_make_tls_context_from_memory(
    const uint8_t* cert_data,
    uint32_t cert_data_length,
    const uint8_t* key_data,
    uint32_t key_data_length
)
{
    // printf("New tls context from memory\n");
    pthread_mutex_lock(&new_ctx_mutex);
    Botan::AutoSeeded_RNG* rng=new Botan::AutoSeeded_RNG();
    std::vector< std::string > protocols;

    // Not sure what is this good for....
    protocols.push_back("h2");
    protocols.push_back("http/1.1");

    botan_tls_context_t* result = new botan_tls_context_t{
        second_transfer::HereCredentialsManager(
            cert_data,
            cert_data_length,
            key_data,
            key_data_length,
            *rng),
        new Botan::TLS::Session_Manager_In_Memory(*rng),
        rng,
        protocols,
        second_transfer::HereTLSPolicy()
    };
    pthread_mutex_unlock(&new_ctx_mutex);
    return result;
}


extern "C" void iocba_delete_tls_context(botan_tls_context_t* ctx)
{
    delete ctx->session_manager;
    delete ctx->rng;
    delete ctx;
}


extern "C" void* iocba_new_tls_server_channel (
       void* botan_pad_ref,
       botan_tls_context_t* ctx)
{
    //printf("New tls server channel\n");
    pthread_mutex_lock(&new_channel_mutex);
    auto* server =
        new Botan::TLS::Server(
            std::bind(second_transfer::output_dn_cb, botan_pad_ref, std::placeholders::_1, std::placeholders::_2),
            std::bind(second_transfer::data_cb, botan_pad_ref, std::placeholders::_1, std::placeholders::_2),
            std::bind(second_transfer::alert_cb, botan_pad_ref, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3),
            std::bind(second_transfer::handshake_cb, botan_pad_ref, std::placeholders::_1),
            *(ctx->session_manager),
            ctx->credentials_manager,
            ctx->here_tls_policty,
            *(ctx->rng),
            std::bind(second_transfer::defaultProtocolSelector, botan_pad_ref, std::placeholders::_1)
        );
    pthread_mutex_unlock(&new_channel_mutex);
    return server;
}

extern "C" void iocba_delete_tls_server_channel (
    Botan::TLS::Server * srv
    )
{
    delete srv;
}