Tls gnutls
This is an implementation of the generic TLS interface based on the gnutls library. The parameters are:
cid : the type of connection ids
index : an unbounded sequence type
bytes : a type of byte arrays indexed by <code>index</code>
extens : a type of lists of extensions
lower : the lower interface
upper : the upper interface
module tls_gnutls(cid,index,bytes,extens,exten_ser,lower,upper) = {
attribute libspec = "gnutls"
object cb = {} # struct holding the callbacks
object cid_map = {} # map from cid's to connections
<<< header
// TODO: put any forward class definitions here
class tls_callbacks;
class gnutls_connection;
>>>
<<< impl
// HACK HACK HACK HACK HACK
// Here we have stolen some internal struct definitions from gnutls to allow
// us to go in and hack the record encryption. This is needed for QUIC version 13
// and later. Eventually it would be good to have gnutls API calls to directly
// read and write handshake messages.
typedef struct {
unsigned int entity; /* GNUTLS_SERVER or GNUTLS_CLIENT */
/* The epoch used to read and write */
uint16_t epoch_read;
uint16_t epoch_write;
/* The epoch that the next handshake will initialize. */
uint16_t epoch_next;
/* The epoch at index 0 of record_parameters. */
uint16_t epoch_min;
} security_parameters_st;
struct gnutls_session_int {
security_parameters_st security_parameters;
};
// This structure holds all the callbacks. These are functions
// that are called synchronously.
struct tls_callbacks {
%`lower.send` ls;
%`upper.recv` ur;
%`upper.alert` ua;
%`upper.session_established` use;
tls_callbacks(
const %`lower.send` ls,
const %`upper.recv` ur,
const %`upper.alert` ua,
const %`upper.session_established` use
)
: ls(ls), ur(ur), ua(ua), use(use) {}
};
// Structure to hold state of a tls session
struct gnutls_connection {
`cid` id;
gnutls_session_t gs;
tls_callbacks cb;
std::vector<char> input;
int handshake_status;
gnutls_connection(`cid` id, gnutls_session_t gs, tls_callbacks cb) : id(id),gs(gs),cb(cb) {
handshake_status = 0;
}
};
// Handle callback from gnutls to send data
ssize_t gnutls_session_push_func(gnutls_transport_ptr_t ptr, const void* data, size_t len) {
gnutls_connection *s = (gnutls_connection *) ptr;
`cid` c = s->id;
unsigned char *d = (unsigned char *)data;
`bytes` bytes;
bytes.resize(len);
std::copy(d,d+len,bytes.begin());
s->cb.ls(c,bytes);
return len;
}
// Handle callback from gnutls to recv data
ssize_t gnutls_session_pull_func(gnutls_transport_ptr_t ptr, void* data, size_t len){
gnutls_connection *s = (gnutls_connection *) ptr;
`cid` c = s->id;
std::vector<char> &input = s->input;
size_t res = input.size();
if (len < res)
res = len;
if (res == 0) {
errno = EAGAIN;
return -1;
}
char *d = (char *)data;
for (size_t i = 0; i < res; i++) {
d[i] = input[i];
}
input.erase(input.begin(),input.begin()+res);
return res;
}
// Start or resume handshake
void gnutls_do_handshake(gnutls_connection *s) {
// HACK HACK HACK HACK
// set the read and write epochs to zero to hopefully disable encryption of records
// s->gs->security_parameters.epoch_read = 0;
// s->gs->security_parameters.epoch_write = 0;
s->handshake_status = gnutls_handshake(s->gs);
if (s->handshake_status != GNUTLS_E_SUCCESS &&
s->handshake_status != GNUTLS_E_AGAIN) {
std::cerr << "gnutls_handshake returned error\n";
exit(1);
}
}
// This is horrible, but it can't be helped, because the gnutls
// callbacks for external extensions don't have parameters.
hash_space::hash_map<gnutls_session_t,std::vector<std::vector<unsigned char> > > gnutls_ext_data_map;
// Send extension data
int gnutls_ext_supp_recv_params(gnutls_session_t session, const unsigned char *data, size_t _data_size) {
const unsigned char *d = (const unsigned char *) data;
std::vector<unsigned char> v;
v.resize(_data_size);
std::copy(d,d+_data_size,v.begin());
gnutls_ext_data_map[session].push_back(v);
return _data_size;
}
// Receive extension data
int gnutls_ext_supp_send_params(gnutls_session_t session, gnutls_buffer_t buf) {
std::vector<std::vector<unsigned char> > &datas = gnutls_ext_data_map[session];
std::vector<unsigned char> v = datas.front();
int len = v.size();
if (gnutls_buffer_append_data(buf, &v[0], len) < 0) {
std::cerr << "gnutls_buffer_append_data returned error\n";
exit(1);
}
datas.erase(datas.begin());
return len;
}
>>>
<<< member
hash_space::hash_map<`cid`,gnutls_connection *> `cid_map`; // maps cid's to connections
tls_callbacks *`cb`; // the callbacks to ivy
>>>
<<< init
// Create the callbacks. When you put an
// action in anti-quotes it creates a function object (a "thunk")
// that captures the instance environment, in this case including
// the instance's endpoint id "me".
`cb` = new tls_callbacks(`lower.send`,`upper.recv`,`upper.alert`,`upper.session_established`);
>>>
object impl = {
close the socket
implement create(c:cid, is_server:bool, e:extens) {
<<< impure
// We create a new gnutls session, and add an entry in the cid_map
// for it.
gnutls_session_t session;
if (gnutls_init (& session, (is_server ? GNUTLS_SERVER : GNUTLS_CLIENT) | GNUTLS_NONBLOCK) != GNUTLS_E_SUCCESS) {
std::cerr << "gnutls_init failed\n";
exit(1);
}
gnutls_connection *s = new gnutls_connection(c,session,*`cb`);
gnutls_transport_ptr_t ptr = (gnutls_transport_ptr_t) s;
gnutls_transport_set_ptr (session, ptr);
gnutls_transport_set_push_function (session, gnutls_session_push_func);
gnutls_transport_set_pull_function (session, gnutls_session_pull_func);
const char *errpos;
int pres = gnutls_priority_set_direct (session,"NORMAL:-VERS-ALL:+VERS-TLS1.3:+VERS-TLS1.2:+ANON-ECDH:+ANON-DH:+AES-128-GCM:+AES-256-GCM:+AES-128-CCM:+AES-256-CCM:+SHA256",&errpos);
if (pres != GNUTLS_E_SUCCESS) {
std::cerr << "set_default_priority returned error\n";
exit(1);
}
if (is_server) {
gnutls_anon_server_credentials_t scred;
int crres = gnutls_anon_allocate_server_credentials (&scred);
if (crres != GNUTLS_E_SUCCESS) {
std::cerr << "anon_allocate_server_credentials returned error\n";
exit(1);
}
int pres = gnutls_anon_set_server_known_dh_params (scred, GNUTLS_SEC_PARAM_HIGH);
if (pres != GNUTLS_E_SUCCESS) {
std::cerr << "gnutls_anon_set_server_known_dh_params returned error\n";
exit(1);
}
int cres = gnutls_credentials_set (session, GNUTLS_CRD_ANON, scred);
if (cres != GNUTLS_E_SUCCESS) {
std::cerr << "gnutls_credentials_set returned error\n";
exit(1);
}
}
else {
gnutls_anon_client_credentials_t cred;
int crres = gnutls_anon_allocate_client_credentials (&cred);
if (crres != GNUTLS_E_SUCCESS) {
std::cerr << "anon_allocate_client_credentials returned error\n";
exit(1);
}
int cres = gnutls_credentials_set (session, GNUTLS_CRD_ANON, cred);
if (cres != GNUTLS_E_SUCCESS) {
std::cerr << "gnutls_credentials_set returned error\n";
exit(1);
}
gnutls_certificate_credentials_t cred2;
crres = gnutls_certificate_allocate_credentials(&cred2);
if (crres != GNUTLS_E_SUCCESS) {
std::cerr << "gnutls_certificate_allocate_credentials returned error\n";
exit(1);
}
crres = gnutls_certificate_set_x509_key_file2 (cred2, "leaf_cert.pem", "leaf_cert.key", GNUTLS_X509_FMT_PEM, 0, 0);
if (crres != GNUTLS_E_SUCCESS) {
std::cerr << "gnutls_certificate_set_x509_key_file2 returned error\n";
exit(1);
}
cres = gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, cred2);
if (cres != GNUTLS_E_SUCCESS) {
std::cerr << "gnutls_credentials_set returned error\n";
exit(1);
}
cres = gnutls_server_name_set (session, GNUTLS_NAME_DNS, "localhost", 9);
if (cres != GNUTLS_E_SUCCESS) {
std::cerr << "gnutls_server_name_set returned error\n";
exit(1);
}
}
// Register the extensions
for (unsigned i = 0; i < e.size(); i++) {
`exten_ser` ser;
__ser(ser,e[i]);
unsigned etype = (((unsigned char)(ser.res[0])) << 8) + ((unsigned char)(ser.res[1]));
unsigned len = (((unsigned char)(ser.res[2])) << 8) + ((unsigned char)(ser.res[3]));
std::vector< unsigned char > data;
data.resize(ser.res.size()-4);
std::copy(ser.res.begin()+4,ser.res.end(),data.begin());
gnutls_ext_data_map[session].push_back(data);
int eres = gnutls_session_ext_register (session, "ext_name", etype,
GNUTLS_EXT_APPLICATION, gnutls_ext_supp_recv_params,
gnutls_ext_supp_send_params, 0,0,0,0);
if (eres != GNUTLS_E_SUCCESS) {
std::cerr << "gnutls_session_ext_register\n";
exit(1);
}
}
`cid_map`[c] = s;
// Start the handshake. This returns, sitting status EAGAIN when input
// would block.
gnutls_do_handshake(s);
>>>
}
implement destroy(c:cid) {
<<< impure
// TODO: actually delete everything here
`cid_map`.erase(c);
>>>
}
implement upper.send(c:cid,data:bytes) {
<<< impure
// TODO: implement this
>>>
}
implement lower.recv(c:cid,data:bytes) {
<<< impure
gnutls_connection *s = `cid_map`[c];
std::vector<char> &input = s->input;
for (unsigned i = 0; i < data.size(); i++) {
input.push_back(data[i]);
}
// Resume handshake if needed
if (s->handshake_status == GNUTLS_E_AGAIN) {
gnutls_do_handshake(s);
}
>>>
}
trusted isolate iso = this
attribute test = impl
}
}