Tls picotls

https://github.com/h2o/picotls/blob/master/t/picotls.c

This is an implementation of the generic TLS interface based on the picotls 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,hextens,exten_ser,lower,upper) = {
Link the gnutls library

    attribute libspec = "picotls-core,picotls-minicrypto,picotls-openssl,ssl,crypto,dl,picotls.lib,libcrypto.lib,libssl.lib,cifra.lib,bcrypt.lib"
These empty objects are used to hold C++ values.

    object cb = {}          # struct holding the callbacks
    object cid_map = {}     # map from cid's to connections
This code goes in the C++ header file, ahead of the ivy object declaration. Here, we put declarations (perhaps forward) of any auxiliary classes we need). We need to be careful that the names of these don't clash with other modules. However, duplicates are removed, so we don't have to worry about multiple instances of this module clashing.

<<< header

    extern "C" {
ifdef _WIN32 include "picotls/wincompat.h" endif include "picotls.h"
    //#include "picotls.c"
include "picotls/openssl.h" include "picotls/minicrypto.h"
    //#include <fmt/core.h>
    }
include include

    // TODO: put any forward class definitions here

    class tls_callbacks;
    class picotls_connection;


>>>
This code goes in the C++ implementation file. Here, we put implementations of the classes declared in the header, and auxiliary functions.

<<< impl


    // 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.keys_established` use;
    tls_callbacks(
        const %`lower.send` ls,
        const %`upper.recv` ur,
        const %`upper.alert` ua,
        const %`upper.keys_established` use
    )
    : ls(ls), ur(ur), ua(ua), use(use) {}
    };

    bool is_rtt = false;
    bool is_client_test = false;

    /* Per epoch crypto context. There are four such contexts:
     * 0: Initial context, with encryption based on a version dependent key,
     * 1: 0-RTT context
     * 2: Handshake context
     * 3: Application data
     */
    struct quic_crypto_context_t {
        void* aead_encrypt;
        void* aead_decrypt;
        void* pn_enc; /* Used for PN encryption */
        void* pn_dec; /* Used for PN decryption */
        quic_crypto_context_t() {
           aead_encrypt = aead_decrypt = pn_enc = pn_dec = 0;
        }
    };

    // Structure to hold state of a tls session

    struct picotls_connection {
        `cid` id;
        ptls_t *gs;
        ptls_context_t *cctx;
        tls_callbacks cb;
        ptls_handshake_properties_t *hsp;
        std::vector<char> input;
        int handshake_status;
        std::vector<quic_crypto_context_t> crypto_context;
        bool is_server;
        uint8_t* session_ticket; //TOODO

        st_ptls_buffer_t message;

        void * ticket_aead_encrypt;
        void * ticket_aead_decrypt;

        picotls_connection(`cid` id, ptls_t *gs, ptls_context_t *cctx , tls_callbacks cb,
            ptls_handshake_properties_t *hsp, bool is_server)
            : id(id),gs(gs),cctx(cctx),cb(cb),hsp(hsp),is_server(is_server) {
            handshake_status = 0;
            session_ticket = NULL;
            crypto_context.resize(4);
            ticket_aead_encrypt = 0;
            ticket_aead_decrypt = 0;
        }
    };

    static int tls_collect_extensions_cb(ptls_t* tls, struct st_ptls_handshake_properties_t* properties, uint16_t type)
    {
        std::cerr << "tls_collect_extensions_cb " << type << "\n";
        return (type & 0xff00) == 0xff00;
    }

    /*
    * The client signals its willingness to receive session resume tickets by providing
    * the "save ticket" callback in the client's quic context.
    * from picoquic
    */

    static int tls_client_save_ticket_cb(ptls_save_ticket_t* save_ticket_ctx,ptls_t* tls, ptls_iovec_t input)
    {
        int ret = 0;
        std::cerr << "tls_client_save_ticket_cb\n" << input.len << "\n" << input.base  << "\n";
        // for(int i = 0; i < input.len; i++)
        //     std::cerr << input.base[i];
        // std::cerr << "\n";
        const char* env_p = std::getenv("TEST_IMPL");
        int m = 0;
        bool is_quiche = false;
        const char * session_file = getenv("SESSION_TICKET_FILE");
        FILE *fp;
        if(session_file != NULL) {
            fp = fopen(session_file,"w");
            for(int j = 0; j < input.len-m && fp != NULL; j++) {
                fprintf(fp, "%02x",  input.base[j]);
            }
            fclose(fp);
        }
        return ret;
    }

    static uint64_t public_random_seed[16] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
    static int public_random_index = 0;
    static const uint64_t public_random_multiplier = 1181783497276652981ull;
    static uint64_t public_random_obfuscator = 0x5555555555555555ull;

    static uint64_t quic_public_random_step(void)
    {
        uint64_t s1;
        const uint64_t s0 = public_random_seed[public_random_index++];
        public_random_index &= 15;
        s1 = public_random_seed[public_random_index];
        s1 ^= (s1 << 31); // a
        s1 ^= (s1 >> 11); // b
        s1 ^= (s0 ^ (s0 >> 30)); // c
        public_random_seed[public_random_index] = s1;
        return s1;
    }

    uint64_t quic_public_random_64(void)
    {
        uint64_t s1 = quic_public_random_step();
        s1 *= public_random_multiplier;
        s1 ^= public_random_obfuscator;
        return s1;
    }

    /* Integer parsing macros */
define PICOPARSE_16(b) ((((uint16_t)(b)[0]) << 8) | (uint16_t)((b)[1])) define PICOPARSE_24(b) ((((uint32_t)PICOPARSE_16(b)) << 8) | (uint32_t)((b)[2])) define PICOPARSE_32(b) ((((uint32_t)PICOPARSE_16(b)) << 16) | (uint32_t)PICOPARSE_16((b) + 2)) define PICOPARSE_64(b) ((((uint64_t)PICOPARSE_32(b)) << 32) | (uint64_t)PICOPARSE_32((b) + 4))

    void picoformat_64(uint8_t* bytes, uint64_t n64)
    {
        bytes[0] = (uint8_t)(n64 >> 56);
        bytes[1] = (uint8_t)(n64 >> 48);
        bytes[2] = (uint8_t)(n64 >> 40);
        bytes[3] = (uint8_t)(n64 >> 32);
        bytes[4] = (uint8_t)(n64 >> 24);
        bytes[5] = (uint8_t)(n64 >> 16);
        bytes[6] = (uint8_t)(n64 >> 8);
        bytes[7] = (uint8_t)(n64);
    }

    void picoformat_32(uint8_t* bytes, uint32_t n32)
    {
        bytes[0] = (uint8_t)(n32 >> 24);
        bytes[1] = (uint8_t)(n32 >> 16);
        bytes[2] = (uint8_t)(n32 >> 8);
        bytes[3] = (uint8_t)(n32);
    }


    static int tls_server_encrypt_ticket_cb(ptls_encrypt_ticket_t* encrypt_ticket_ctx,
        ptls_t* tls, int is_encrypt, ptls_buffer_t* dst, ptls_iovec_t src)
    {
        std::cerr << "tls_server_encrypt_ticket_cb - ENTRY (is_encrypt=" << is_encrypt << ", src.len=" << src.len << ") \n";
        int ret = 0;

        // Add NULL pointer check to prevent segfault
        void** data_ptr = ptls_get_data_ptr(tls);
        if (!data_ptr || !*data_ptr) {
            std::cerr << "ERROR: tls_server_encrypt_ticket_cb - TLS data pointer is NULL" << "\n";
            return -1;
        }
        picotls_connection *s = (picotls_connection*)*data_ptr;
        std::cerr << "tls_server_encrypt_ticket_cb - Successfully accessed data pointer, connection id=" << s->id << "\n";

        if (is_encrypt != 0) {
            const char * session_file = getenv("ENCRYPT_TICKET_FILE");
            FILE *fp;
            if(session_file != NULL) {
                fp = fopen(session_file,"w");
                for(int j = 0; j < src.len; j++) {
                    fprintf(fp, "%02x",  src.base[j]); //length field
                }
                fclose(fp);
            }
            ptls_aead_context_t *aead_enc = (ptls_aead_context_t *)(s->ticket_aead_encrypt);
            std::cerr << "tls_server_encrypt_ticket_cb 3a ticket_aead_encrypt " << s->ticket_aead_encrypt << "\n";
            //ptls_aead_context_t* aead_enc = (ptls_aead_context_t*)quic->aead_encrypt_ticket_ctx;
            /* Encoding*/
            if (aead_enc == NULL) {
                ret = -1;
            } else if ((ret = ptls_buffer_reserve(dst, 8 + src.len + aead_enc->algo->tag_size)) == 0) {
                /* Create and store the ticket sequence number */
                //uint32_t version_number = 0x00000001; //TODO
                uint64_t seq_num = quic_public_random_64();
                std::cerr << "tls_server_encrypt_ticket_cb 4a " << seq_num << "\n";
                size_t start_off;
                size_t data_length;

                picoformat_64(dst->base + dst->off, seq_num);
                dst->off += 8;

                start_off = dst->off;
                /* Copy initial ticket to dst field before encryption. */
                memcpy(dst->base + dst->off, src.base, src.len);
                data_length = src.len;

                /* Add the version number */
               // picoformat_32(dst->base + dst->off + data_length, version_number);
               // data_length += 4;

                /* Run AEAD encryption */
                dst->off += ptls_aead_encrypt(aead_enc, dst->base + dst->off,
                    dst->base + start_off, data_length, seq_num, NULL, 0);

                /* Remember issued ticket ID in connection context */
                //quic->cnx_in_progress->issued_ticket_id = seq_num;
            }
        } else {
            std::cerr << "tls_server_encrypt_ticket_cb 2b\n";
            ptls_aead_context_t *aead_dec = (ptls_aead_context_t *)(s->ticket_aead_decrypt);
            //ptls_aead_context_t* aead_dec = (ptls_aead_context_t*)quic->aead_decrypt_ticket_ctx;
            /* Encoding*/
            if (aead_dec == NULL) {
                ret = -1;
            } else if (src.len < 8 + aead_dec->algo->tag_size) {
                ret = -1;
            } else if ((ret = ptls_buffer_reserve(dst, src.len)) == 0) {
                /* Decode the ticket sequence number */
                uint64_t seq_num = PICOPARSE_64(src.base);
                std::cerr << "tls_server_encrypt_ticket_cb 4b " << seq_num << "\n";
                /* Decrypt */
                size_t decrypted = ptls_aead_decrypt(aead_dec, dst->base + dst->off,
                                                     src.base + 8, src.len - 8, seq_num, NULL, 0);

                if (decrypted > src.len - 8) {
                    std::cerr << "tls_server_encrypt_ticket_cb 5b decryption ERROR "  << decrypted << "\n";
                    /* decryption error */
                    ret = -1;
                } else {
                    dst->off += decrypted;
                    /* Remember resumed ticket ID in connection context */
                    //quic->cnx_in_progress->resumed_ticket_id = seq_num;
                    const char * session_file = getenv("ENCRYPT_TICKET_FILE");
                    std::cerr << "tls_server_encrypt_ticket_cb 5b " << src.len << "\n";
                    FILE *fp;
                    if(session_file != NULL) {
                        fp = fopen(session_file,"w");
                        for(int j = 0; j < src.len; j++) {
                            fprintf(fp, "%02x",  dst->base[j]); //length field
                        }
                        fclose(fp);
                    }
                }
            }
        }
        return ret;
    }

    static int tls_collected_extensions_cb(ptls_t* tls, ptls_handshake_properties_t* properties,
    ptls_raw_extension_t* slots)
    {
        // TODO: do something with incoming extensions
        std::cerr << "tls_collected_extensions_cb " << slots->type << "\n";
        return 0;
    }

    static int tls_on_extension_cb(ptls_on_extension_t* ext, ptls_t* tls, uint8_t type_tls,
    uint16_t type, ptls_iovec_t input)
    {
        // TODO: do something with incoming extensions
        std::cerr << "tls_on_extension_cb " << type_tls << "\n";
        std::cerr << "tls_on_extension_cb " << type << "\n";
        std::cerr << "tls_on_extension_cb " << input.len << "\n\n";
        return 0;
    }


    // These are some parameters defined byt the QUIC-TLS standard. They
    // don't really belong here, but then again, QUIC is currently the only
    // existing client of this TLS interface.
define QUIC_LABEL_QUIC_BASE "tls13 quic " define QUIC_LABEL_PN "pn" define QUIC_LABEL_INITIAL_CLIENT "client in" define QUIC_LABEL_INITIAL_SERVER "server in"

define QUIC_LABEL_RETRY1 "quic key" define QUIC_LABEL_RETRY2 "quic iv"

define PTLS_HELLO_RANDOM_SIZE 32

define PTLS_HANDSHAKE_HEADER_SIZE 4

    //SSL: ptls_get_client_random(), quic_update_traffic_key_cb
    // The following mess is to get the traffic keys from picotls

    void *aead_from_secret(ptls_cipher_suite_t * cipher, int is_enc, const void *secret)
    {
        return ptls_aead_new(cipher->aead, cipher->hash, is_enc, secret, QUIC_LABEL_QUIC_BASE);
    }

    static int quic_set_aead_from_secret(void ** v_aead,ptls_cipher_suite_t * cipher, int is_enc, const void *secret, const char *prefix_label)
    {
        int ret = 0;
        std::cerr << "quic_set_aead_from_secret 1 " << *v_aead << "\n";
        if (*v_aead != NULL) {
            ptls_aead_free((ptls_aead_context_t*)*v_aead);
        }
        std::cerr << "quic_set_aead_from_secret 2" <<"\n";
        if ((*v_aead = ptls_aead_new(cipher->aead, cipher->hash, is_enc, secret, prefix_label)) == NULL) {
            ret = PTLS_ERROR_NO_MEMORY;
        }
        std::cerr << "quic_set_aead_from_secret 3 "  << *v_aead << "\n";
        return ret;
    }

    void *pn_enc_from_secret(ptls_cipher_suite_t * cipher, int is_enc, const void *secret)
    {
        uint8_t key[PTLS_MAX_SECRET_SIZE];

        ptls_hkdf_expand_label(cipher->hash, key,
                               cipher->aead->ctr_cipher->key_size,
                               ptls_iovec_init(secret, cipher->hash->digest_size),
                               "quic hp",
                               ptls_iovec_init(NULL, 0),
                               NULL);

        return ptls_cipher_new(cipher->aead->ctr_cipher, is_enc, key);
    }

    void quic_set_key_from_secret(ptls_cipher_suite_t * cipher, int is_enc, quic_crypto_context_t * ctx, const void *secret)
    {
        std::cerr << "quic_set_key_from_secret 1" <<"\n";
        if (is_enc) {
            std::cerr << "quic_set_key_from_secret 2q" <<"\n";
            ctx->aead_encrypt = aead_from_secret(cipher, is_enc, secret);
            std::cerr << "quic_set_key_from_secret 3q" <<"\n";
            ctx->pn_enc =  pn_enc_from_secret(cipher, is_enc, secret);
        } else {
            std::cerr << "quic_set_key_from_secret 2v" <<"\n";
            ctx->aead_decrypt = aead_from_secret(cipher, is_enc, secret);
            std::cerr << "quic_set_key_from_secret 3v" <<"\n";
            ctx->pn_dec = pn_enc_from_secret(cipher, is_enc, secret);
        }
    }


    static int quic_update_traffic_key_cb(ptls_update_traffic_key_t * self, ptls_t *tls, int is_enc, size_t epoch, const void *secret)
    {
        std::cerr << "quic_update_traffic_key_cb - ENTRY (is_enc=" << is_enc << ", epoch=" << epoch << ", secret=" << secret << ")\n";
        static const char *log_labels[2][4] = {
            {NULL, "CLIENT_EARLY_TRAFFIC_SECRET", "CLIENT_HANDSHAKE_TRAFFIC_SECRET", "CLIENT_TRAFFIC_SECRET_0"},
            {NULL, NULL, "SERVER_HANDSHAKE_TRAFFIC_SECRET", "SERVER_TRAFFIC_SECRET_0"}};
        const char * keylogfile = getenv("SSLKEYLOGFILE");
        const char * keylogfile_0rtt = getenv("ZRTT_SSLKEYLOG_FILE");
        //const char * is_zrtt = getenv("ZERORTT_TEST");
        FILE *fp;
        FILE *fp_0rtt;
        const char *secret_label = log_labels[ptls_is_server(tls) == is_enc][epoch];
        std::cerr << "quic_update_traffic_key_cb "<< secret_label <<"\n";
        if(keylogfile != NULL) {
            fp = fopen(keylogfile,"a");
            fprintf(fp, "%s ", secret_label);
            ptls_iovec_t crandom = ptls_get_client_random(tls);
            for (int i = 0 ; i < crandom.len ; i++) {
                fprintf(fp, "%02hhx", crandom.base[i]);
            }
            fprintf(fp, " ");
        }
        if(keylogfile_0rtt != NULL && (!is_rtt || strcmp(secret_label, "CLIENT_EARLY_TRAFFIC_SECRET") == 0 || is_client_test)) {
            const char *secret_label = log_labels[ptls_is_server(tls) == is_enc][epoch];
            if(strcmp(secret_label, "SERVER_HANDSHAKE_TRAFFIC_SECRET") == 0) {
                fp_0rtt = fopen(keylogfile_0rtt,"w"); //erase
                fprintf(fp_0rtt, "%s", "");
                fclose(fp_0rtt);
            }
            fp_0rtt = fopen(keylogfile_0rtt,"a");
            fprintf(fp_0rtt, "%s ", secret_label);
            ptls_iovec_t crandom = ptls_get_client_random(tls);
            for (int i = 0 ; i < crandom.len ; i++) {
                fprintf(fp_0rtt, "%02hhx", crandom.base[i]);
            }
            fprintf(fp_0rtt, " ");
        }
        // Add NULL pointer check to prevent segfault
        void** data_ptr = ptls_get_data_ptr(tls);
        if (!data_ptr || !*data_ptr) {
            std::cerr << "ERROR: quic_update_traffic_key_cb - TLS data pointer is NULL" << "\n";
            if(keylogfile != NULL) fclose(fp);
            if(keylogfile_0rtt != NULL) fclose(fp_0rtt);
            return -1;
        }
        picotls_connection* cnx = (picotls_connection*)*data_ptr;
        std::cerr << "quic_update_traffic_key_cb - Successfully accessed data pointer, connection id=" << cnx->id << "\n";
        ptls_cipher_suite_t * cipher = ptls_get_cipher(tls);

        if(keylogfile != NULL) {
            for (int i = 0 ; i < cipher->hash->digest_size ; i++) {
                fprintf(fp, "%02hhx", ((uint8_t *) secret)[i]);
            }
            fprintf(fp, "\n");
        }
        if(keylogfile_0rtt != NULL && (!is_rtt || strcmp(secret_label, "CLIENT_EARLY_TRAFFIC_SECRET") == 0)) {
            for (int i = 0 ; i < cipher->hash->digest_size ; i++) {
                fprintf(fp_0rtt, "%02hhx", ((uint8_t *) secret)[i]);
            }
            fprintf(fp_0rtt, "\n");
        }

        quic_set_key_from_secret(cipher, is_enc, &cnx->crypto_context[epoch], secret);
        if (cnx->crypto_context[epoch].aead_encrypt && cnx->crypto_context[epoch].aead_decrypt) {
            cnx->cb.use(cnx->id,epoch);
        }

        if(keylogfile != NULL) {
            fclose(fp);
        }
        if(keylogfile_0rtt != NULL &&  (!is_rtt || strcmp(secret_label, "CLIENT_EARLY_TRAFFIC_SECRET") == 0)) {
            fclose(fp_0rtt);
        }
        return 0;
    }

    int convert(const char *hex_str, uint8_t *byte_array, int byte_array_max)
    {
        std::cerr << "convert 1" <<"\n";
        int hex_str_len = strnlen(hex_str,byte_array_max);
        int i = 0, j = 0;

        // The output array size is half the hex_str length (rounded up)
        int byte_array_size = (hex_str_len)/2;
        fprintf(stderr,"pipi %d\n",byte_array_size);
        std::cerr << "convert 2" <<"\n";

        if (byte_array_size > byte_array_max)
        {
            // Too big for the output array
            return -1;
        }

        if (hex_str_len % 2 == 1)
        {
            // hex_str is an odd length, so assume an implicit "0" prefix
            if (sscanf(&(hex_str[0]), "%1hhx", &(byte_array[0])) != 1)
            {
                return -1;
            }

            i = j = 1;
        }

        for (; i < hex_str_len; i+=2, j++)
        {
            if (sscanf(&(hex_str[i]), "%2hhx", &(byte_array[j])) != 1)
            {
                return -1;
            }
        }

        return byte_array_size;
    }

    void vec_to_ptls_iovec(ptls_iovec_t &res, const `bytes` &vec) {
        res.base = new uint8_t[vec.size()];
        std::copy(vec.begin(),vec.end(),res.base);
        res.len = vec.size();
    }

    void vec_to_ptls_iovec(ptls_iovec_t &res, uint8_t* vec, int size) {
        res.base = new uint8_t[size];
        std::cerr << "vec_to_ptls_iovec " << std::dec << size << std::endl;
        for(int i = 0; i < size; i++) {
            res.base[i] = vec[i];
            std::cerr << res.base[i];
        }
        std::cerr <<  std::endl;
        //std::copy(std::begin(vec),std::end(vec),res.base);
        res.len = size;
    }

    // This additional mess sets the initial traffic keys

    void bail(int ret, const char *msg) {
        if (ret) {
            std::cerr << msg;
            exit(1);
        }
    }


    /**
     * @brief from https://gitlab.com/wireshark/wireshark/-/blob/master/wsutil/str_util.c
     *
     * @param ch
     * @return int
     */
    int
    ws_xton(char ch)
    {
        switch (ch) {
            case '0': return 0;
            case '1': return 1;
            case '2': return 2;
            case '3': return 3;
            case '4': return 4;
            case '5': return 5;
            case '6': return 6;
            case '7': return 7;
            case '8': return 8;
            case '9': return 9;
            case 'a':  case 'A': return 10;
            case 'b':  case 'B': return 11;
            case 'c':  case 'C': return 12;
            case 'd':  case 'D': return 13;
            case 'e':  case 'E': return 14;
            case 'f':  case 'F': return 15;
            default: return -1;
        }
    }

    /**
     * @brief
     *      Derive-Secret(Secret, Label, Messages) =
            HKDF-Expand-Label(Secret, Label,
                              Transcript-Hash(Messages), Hash.length)
     */
    int setup_initial_traffic_keys(picotls_connection *session,
                                   const `bytes` &salt_vec,
                                   const `bytes` &ikm_vec)
    {
        std::cerr << "setup_initial_traffic_keys\n";
        std::cerr << salt_vec;
        std::cerr << ikm_vec;
        int ret = 0;
        uint8_t master_secret[256]; /* secret_max */
        ptls_cipher_suite_t cipher = { 0, &ptls_openssl_aes128gcm, &ptls_openssl_sha256 };
        ptls_iovec_t salt;
        ptls_iovec_t ikm;
        ptls_iovec_t prk;
        uint8_t client_secret[256];
        uint8_t server_secret[256];
        uint8_t *secret1, *secret2;

        std::cerr << "setup_initial_traffic_keys 2 \n";

        vec_to_ptls_iovec(salt,salt_vec);
        vec_to_ptls_iovec(ikm,ikm_vec);

        std::cerr << "setup_initial_traffic_keys 3\n";
        ret = ptls_hkdf_extract(cipher.hash, master_secret, salt, ikm);
        bail(ret,"tls: failed to set up initial master secret\n");

        std::cerr << "setup_initial_traffic_keys 4\n";
        prk.base = master_secret;
        prk.len = cipher.hash->digest_size;
        ret = ptls_hkdf_expand_label(cipher.hash, client_secret, cipher.hash->digest_size,
            prk, QUIC_LABEL_INITIAL_CLIENT, ptls_iovec_init(NULL, 0),NULL);
            // QUIC_LABEL_QUIC_BASE);
        bail(ret,"tls: failed to set up initial client secret\n");
        ret = ptls_hkdf_expand_label(cipher.hash, server_secret, cipher.hash->digest_size,
            prk, QUIC_LABEL_INITIAL_SERVER, ptls_iovec_init(NULL, 0),NULL);
            // QUIC_LABEL_QUIC_BASE);
        bail(ret,"tls: failed to set up initial server secret\n");
         std::cerr << "setup_initial_traffic_keys 5\n";
        quic_set_key_from_secret(&cipher, session->is_server, &session->crypto_context[0], server_secret);
         std::cerr << "setup_initial_traffic_keys 6\n";
        quic_set_key_from_secret(&cipher, !session->is_server, &session->crypto_context[0], client_secret);
        return ret;
    }



    void encrypt_symm(ptls_cipher_context_t *pn_enc, const `bytes` &clear, const `bytes` &iv, `bytes` &cipher) {
        std::vector<uint8_t> bytes;
        bytes.resize(iv.size());
        std::copy(iv.begin(),iv.end(),bytes.begin());
        ptls_cipher_init(pn_enc, &bytes[0]);
        std::vector<uint8_t> input, output;
        input.resize(clear.size());
        std::copy(clear.begin(),clear.end(),input.begin());
        output.resize(clear.size());
        ptls_cipher_encrypt(pn_enc, &output[0], &input[0], input.size());
        cipher.resize(output.size());
        std::copy(output.begin(),output.end(),cipher.begin());
    }

    // Some parameters for picotls as used by picoquic.

    //mvfst -> No HelloRetry  &ptls_openssl_x25519,&ptls_openssl_secp256r1
    ptls_key_exchange_algorithm_t *picotls_key_exchanges[] =
        {  &ptls_openssl_x25519,&ptls_openssl_secp256r1, NULL };

    // ptls_key_exchange_algorithm_t *picotls_key_exchanges[] =
    //     {  &ptls_openssl_secp256r1, &ptls_openssl_x25519, NULL };

    ptls_cipher_suite_t *picotls_cipher_suites[] = {
        &ptls_openssl_aes128gcmsha256, &ptls_openssl_aes256gcmsha384,
        &ptls_minicrypto_chacha20poly1305sha256, NULL };

    // Here we process handshake data on a TLS connection. The in_epoch is a picotls
    // epoch. For the initial client handshake, input is null.


    int picotls_do_handshake(picotls_connection *s, size_t in_epoch, void *input, size_t inlen ) {
        if(ptls_is_psk_handshake(s->gs)) { //segfault without ??
            std::cerr << "ptls_is_psk_handshake= "  << ptls_is_psk_handshake(s->gs) << std::endl;
        } else {
            std::cerr <<  "ptls_is_psk_handshake= "  <<  ptls_is_psk_handshake(s->gs) << std::endl;
        }

        size_t epoch_offsets[5] = { 0, 0, 0, 0, 0 };
        struct st_ptls_buffer_t sendbuf;
        ptls_buffer_init(&sendbuf, (void *)"", 0);
        int ret = ptls_handle_message(s->gs, &sendbuf, epoch_offsets, in_epoch, input, inlen, s->hsp);
        std::cerr << "picotls_do_handshake ptls_handle_message end " << "\n";
        if (ret == PTLS_ERROR_IN_PROGRESS) {
            std::cerr << "PICOTLS RETURNED PTLS_ERROR_IN_PROGRESS " << ret << "\n";
        } else if (ret != 0) {
            std::cerr << "PICOTLS RETURNED ERROR: " << ret << "\n";
        }

        // Any generated bytes go to the lower send callback (even in case of error).

        if (sendbuf.off > 0) {
            for (size_t ep = 0; ep < 4; ep++) {
                size_t num_bytes = epoch_offsets[ep+1] - epoch_offsets[ep];
                if (num_bytes) {
                    `bytes` bytes;
                    bytes.resize(num_bytes);
                    std::copy(sendbuf.base+epoch_offsets[ep],sendbuf.base+epoch_offsets[ep+1],bytes.begin());
                    s->cb.ls(s->id,bytes,ep);
                }
            }
        }
        std::cerr << "PICOTLS handshake handle message finish" << "\n";
        return ret;
    }

    static int set_sign_certificate_from_key(EVP_PKEY* pkey, ptls_context_t* ctx)
    {
        int ret = 0;
        ptls_openssl_sign_certificate_t* signer;

        signer = (ptls_openssl_sign_certificate_t*)malloc(sizeof(ptls_openssl_sign_certificate_t));

        if (signer == NULL || pkey == NULL) {
            ret = -1;
        } else {
            ret = ptls_openssl_init_sign_certificate(signer, pkey);
            ctx->sign_certificate = &signer->super;
        }

        if (pkey != NULL) {
            EVP_PKEY_free(pkey);
        }

        if (ret != 0 && signer != NULL) {
            free(signer);
        }

        return ret;
    }

    static int set_sign_certificate_from_key_file(char const* keypem, ptls_context_t* ctx)
    {
        int ret = 0;
        BIO* bio = BIO_new_file(keypem, "rb");
        EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
        if (pkey == NULL) {
            ret = -1;
        }
        else {
            ret = set_sign_certificate_from_key(pkey, ctx);
        }
        BIO_free(bio);
        return ret;
    }


    void printt(unsigned char *byte_array, int byte_array_size)
    {
        int i = 0;
        fprintf(stderr,"%s","0x");
        for(; i < byte_array_size; i++)
        {
            fprintf(stderr,"%02x ", byte_array[i]);
        }
        fprintf(stderr,"%s","\n");
    }

    void get_session_ticket(uint8_t* secret, int size)  {
            const char * session_file = getenv("SESSION_TICKET_FILE");
            FILE *fp;
            size_t len = 0;
            ssize_t read;
            char * line = NULL;
            if(session_file != NULL) {
                fp = fopen(session_file,"r");
                read = getline(&line, &len, fp);
                fclose(fp);
                bool is_error = false;
                if(line != NULL) {
                    std::cerr << "get_session_ticket 1 " << line << std::endl;
                    std::cerr << "get_session_ticket hex_len " << size << std::endl;
                    int sizze = convert(line, secret, size*2+1);
                    if (sizze < 0)
                    {
                        fprintf(stderr,"Failed to convert '%s'\n", line);
                        printt(secret, sizze);
                    }
                    else if (sizze == 0)
                    {
                        fprintf(stderr,"Nothing to convert for '%s'\n", line);
                        printt(secret, sizze);
                    }
                    else
                    {
                        fprintf(stderr,"%s","OK OK\n");
                        printt(secret, sizze);
                    }
                }
            }
    }

    int get_session_ticket_size()  {
            const char * session_file = getenv("SESSION_TICKET_FILE");
            FILE *fp;
            size_t len = 0;
            ssize_t read;
            char * line = NULL;
            if(session_file != NULL) {
                fp = fopen(session_file,"r");
                read = getline(&line, &len, fp);
                fclose(fp);
                if(line != NULL) {
                    std::cerr << "get_session_ticket_size " << line << std::endl;
                    int hex_len = (strnlen(line,2000))/2; //strnlen(line,300)-1
                    std::cerr << "get_session_ticket_size " << hex_len << std::endl;
                    return hex_len;
                }
            }
            return 0;
    }


>>>
Here we put any new members of the ivy C++ class. If we have allocated a per-instance object, we declared it here anti-quoted. The plugs in the actual member name, which may be any array if this is a parameterized instance.

<<< member

    hash_space::hash_map<`cid`,picotls_connection *> `cid_map`; // maps cid's to connections
    tls_callbacks *`cb`;             // the callbacks to ivy

>>>
Here, we put code to go in the initializer. If this is a parameterized instance, then this code will be run in a loop, so we have to be careful that any initialization of common objects is idempotent.

<<< 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.keys_established`);

>>>


    object impl = {
These are the implementations of the interface calls. These operations are synchronous.

    implement eavesdrop_client(c:cid) {
        <<< impure

        std::cerr << "eavesdrop_client create cid = " << c << std::endl;

        ptls_context_t* ctx;
        ctx = new ptls_context_t;
        memset(ctx, 0, sizeof(ptls_context_t));

        ctx->random_bytes = ptls_openssl_random_bytes;
        ctx->key_exchanges = picotls_key_exchanges;
        ctx->cipher_suites = picotls_cipher_suites;
        ctx->send_change_cipher_spec = 0;
        // ctx->hkdf_label_prefix = QUIC_LABEL_QUIC_BASE;

        ctx->hkdf_label_prefix__obsolete = NULL;
        ctx->update_traffic_key = (ptls_update_traffic_key_t *)malloc(sizeof(ptls_update_traffic_key_t));
        memset(ctx->update_traffic_key, 0, sizeof(ptls_update_traffic_key_t));
        //ctx->update_traffic_key = new ptls_update_traffic_key_t;
        ctx->update_traffic_key->cb = quic_update_traffic_key_cb;

        ctx->get_time = &ptls_get_time;

        //ctx->on_extension = new ptls_on_extension_t;
        ctx->on_extension = (ptls_on_extension_t *)malloc(sizeof(ptls_on_extension_t));
        memset(ctx->on_extension, 0, sizeof(ptls_on_extension_t));
        ctx->on_extension->cb = tls_on_extension_cb;

        ptls_t *session;
        session = ptls_new(ctx, 0);

        // Initialize data pointer to NULL to prevent callbacks from accessing garbage
        *ptls_get_data_ptr(session) = NULL;
        std::cerr << "PICOTLS: Initialized session data pointer to NULL for safety\n";

        ptls_set_server_name(session, "servername", strlen("servername"));

        ptls_handshake_properties_t *handshake_properties = new ptls_handshake_properties_t;
        memset(handshake_properties, 0, sizeof(ptls_handshake_properties_t));
        handshake_properties->collect_extension = tls_collect_extensions_cb;
        handshake_properties->collected_extensions = tls_collected_extensions_cb;

        ptls_iovec_t *alpn_vec = new(ptls_iovec_t);  // TODO: will leak this

        // h3-x == HTTP/3 over QUIC
        // hq-x == HTTP/0.9 over QUIC
        alpn_vec->base = (uint8_t*) "hq-interop";
        if(const char* env_p = std::getenv("TEST_ALPN")) {
            std::cerr << "TEST_ALPN " << env_p << std::endl;
            alpn_vec->base = (uint8_t*) env_p;
        }
        alpn_vec->len = strlen((char *)(alpn_vec->base));
        handshake_properties->client.negotiated_protocols.count = 1;
        handshake_properties->client.negotiated_protocols.list  = alpn_vec;

        std::cerr << "ptls_set_negotiated_protocol"  << std::endl;
        ptls_set_negotiated_protocol(session,
            (const char*) handshake_properties->client.negotiated_protocols.list[0].base,
            handshake_properties->client.negotiated_protocols.list[0].len);

        picotls_connection *s = new picotls_connection(c, session, ctx, *`cb`, handshake_properties, false);
        *ptls_get_data_ptr(session) = s;
        std::cerr << "PICOTLS: Set session data pointer to connection object (id=" << c << ")\n";

        `cid_map`[c] = s;

        std::cerr << "cid_map[c] = s;"  << std::endl;
        >>>
    }

    implement eavesdrop_server(c:cid) {
        <<< impure

        std::cerr << "eavesdrop_server create cid = " << c << std::endl;

        ptls_context_t* ctx;
        ctx = new ptls_context_t;
        memset(ctx, 0, sizeof(ptls_context_t));

        ctx->random_bytes = ptls_openssl_random_bytes;
        ctx->key_exchanges = picotls_key_exchanges;
        ctx->cipher_suites = picotls_cipher_suites;
        ctx->send_change_cipher_spec = 0;
        // ctx->hkdf_label_prefix = QUIC_LABEL_QUIC_BASE;

        ctx->hkdf_label_prefix__obsolete = NULL;
        ctx->update_traffic_key = (ptls_update_traffic_key_t *)malloc(sizeof(ptls_update_traffic_key_t));
        memset(ctx->update_traffic_key, 0, sizeof(ptls_update_traffic_key_t));
        //ctx->update_traffic_key = new ptls_update_traffic_key_t;
        ctx->update_traffic_key->cb = quic_update_traffic_key_cb;

        ctx->get_time = &ptls_get_time;

        //ctx->on_extension = new ptls_on_extension_t;
        ctx->on_extension = (ptls_on_extension_t *)malloc(sizeof(ptls_on_extension_t));
        memset(ctx->on_extension, 0, sizeof(ptls_on_extension_t));
        ctx->on_extension->cb = tls_on_extension_cb;

        const char* cert_path = getenv("PANTHER_IVY_CERT_PATH");
        if (!cert_path) {
            const char* base_dir = getenv("PANTHER_IVY_BASE_DIR");
            if (base_dir) {
                static std::string cert_path_str = std::string(base_dir) + "/quic/leaf_cert.pem";
                cert_path = cert_path_str.c_str();
            } else {
                cert_path = "/opt/panther_ivy/protocol-testing/quic/quic/leaf_cert.pem";
            }
        }
        int r = ptls_load_certificates(ctx, cert_path);
        std::cerr << "ptls_load_certificates "  << r << std::endl;
        /* Read the certificate file */
        if (r != 0) {
            std::cerr << "could not load certificate file hardcoded leaf_cert.pem\n";
            exit(1);
        } else if(const char* key_path = getenv("PANTHER_IVY_KEY_PATH")) {
            std::cerr << "PANTHER_IVY_KEY_PATH " << key_path << std::endl;
            if (!key_path) {
                const char* base_dir = getenv("PANTHER_IVY_BASE_DIR");
                if (base_dir) {
                    static std::string key_path_str = std::string(base_dir) + "/quic/leaf_cert.key";
                    key_path = key_path_str.c_str();
                } else {
                    key_path = "/opt/panther_ivy/protocol-testing/quic/quic/leaf_cert.key";
                }
            }
        } else {
            std::cerr << "PANTHER_IVY_KEY_PATH not set, using hardcoded leaf_cert.key\n";
            key_path = "/opt/panther_ivy/protocol-testing/quic/quic/leaf_cert.key";
        }
        if (set_sign_certificate_from_key_file(key_path, ctx)) {
            std::cerr << "could not load key file leaf_cert.key\n";
            exit(1);
        }

        ptls_t *session;
        session = ptls_new(ctx, 1);

        // Initialize data pointer to NULL to prevent callbacks from accessing garbage
        *ptls_get_data_ptr(session) = NULL;
        std::cerr << "PICOTLS: Initialized session data pointer to NULL for safety\n";

        ptls_set_server_name(session, "servername", strlen("servername"));

        ptls_handshake_properties_t *handshake_properties = new ptls_handshake_properties_t;
        memset(handshake_properties, 0, sizeof(ptls_handshake_properties_t));
        handshake_properties->collect_extension = tls_collect_extensions_cb;
        handshake_properties->collected_extensions = tls_collected_extensions_cb;

        ptls_iovec_t *alpn_vec = new(ptls_iovec_t);  // TODO: will leak this

        // h3-x == HTTP/3 over QUIC
        // hq-x == HTTP/0.9 over QUIC
        alpn_vec->base = (uint8_t*) "hq-interop";
        if(const char* env_p = std::getenv("TEST_ALPN")) {
            std::cerr << "TEST_ALPN " << env_p << std::endl;
            alpn_vec->base = (uint8_t*) env_p;
        }
        alpn_vec->len = strlen((char *)(alpn_vec->base));
        handshake_properties->client.negotiated_protocols.count = 1;
        handshake_properties->client.negotiated_protocols.list  = alpn_vec;

        std::cerr << "ptls_set_negotiated_protocol"  << std::endl;
        ptls_set_negotiated_protocol(session,
            (const char*) handshake_properties->client.negotiated_protocols.list[0].base,
            handshake_properties->client.negotiated_protocols.list[0].len);

        picotls_connection *s = new picotls_connection(c, session, ctx, *`cb`, handshake_properties, true);
        *ptls_get_data_ptr(session) = s;
        std::cerr << "PICOTLS: Set session data pointer to connection object (id=" << c << ")\n";

        `cid_map`[c] = s;

        std::cerr << "cid_map[c] = s;"  << std::endl;
        >>>
    }
close the socket

    implement create(c:cid, is_server:bool, e:extens) {
        <<< impure

        //is_rtt = true; //todo
        if(is_server)
            is_client_test = true;

        std::cerr << "create cid = " << c << std::endl;
        std::cerr << "is_server  = " << is_server << std::endl;

        // We create a new picootls session, and add an entry in the cid_map
        // for it.

        ptls_context_t* ctx;
        ctx = new ptls_context_t;
        memset(ctx, 0, sizeof(ptls_context_t));

        /*
        HelloRetryRequest random:
            CF 21 AD 74 E5 9A 61 11 BE 1D 8C 02 1E 65 B8 91
            C2 A2 11 16 7A BB 8C 5E 07 9E 09 E2 C8 A8 33 9C
        */
        ctx->random_bytes = ptls_openssl_random_bytes;
        ctx->key_exchanges = picotls_key_exchanges;
        ctx->cipher_suites = picotls_cipher_suites;
        ctx->send_change_cipher_spec = 0;
        // ctx->hkdf_label_prefix = QUIC_LABEL_QUIC_BASE;

        ctx->hkdf_label_prefix__obsolete = NULL;
        ctx->update_traffic_key = (ptls_update_traffic_key_t *)malloc(sizeof(ptls_update_traffic_key_t));
        memset(ctx->update_traffic_key, 0, sizeof(ptls_update_traffic_key_t));
        //ctx->update_traffic_key = new ptls_update_traffic_key_t;
        ctx->update_traffic_key->cb = quic_update_traffic_key_cb;

        //ctx->on_extension = new ptls_on_extension_t;
        ctx->on_extension = (ptls_on_extension_t *)malloc(sizeof(ptls_on_extension_t));
        memset(ctx->on_extension, 0, sizeof(ptls_on_extension_t));
        ctx->on_extension->cb = tls_on_extension_cb;

        ctx->get_time = &ptls_get_time;
        const char * is_zrtt = getenv("ZERORTT_TEST");

       // ctx->client_mode = !is_server;

        ctx->max_early_data_size = 0xFFFFFFFF;
        ctx->omit_end_of_early_data = 0;

        std::cerr << "PROUT 3 "  << std::endl;

        //ctx->use_exporter = 1; // master secrets should be recorded

        ptls_handshake_properties_t *handshake_properties = new ptls_handshake_properties_t;
        memset(handshake_properties, 0, sizeof(ptls_handshake_properties_t));
        handshake_properties->collect_extension = tls_collect_extensions_cb;
        handshake_properties->collected_extensions = tls_collected_extensions_cb;

        std::cerr << "PROUT 4 " << std::endl;

        ptls_iovec_t *alpn_vec = new(ptls_iovec_t);  // TODO: will leak this

        // h3-x == HTTP/3 over QUIC
        // hq-x == HTTP/0.9 over QUIC
        alpn_vec->base = (uint8_t*) "hq-interop";
        if(const char* env_p = std::getenv("TEST_ALPN")) {
            std::cerr << "TEST_ALPN " << env_p << std::endl;
            alpn_vec->base = (uint8_t*) env_p;
        }

        alpn_vec->len = strlen((char *)(alpn_vec->base));
        handshake_properties->client.negotiated_protocols.count = 1;
        handshake_properties->client.negotiated_protocols.list  = alpn_vec;

        // add the extensions
        ptls_raw_extension_t *ptls_exts = new ptls_raw_extension_t[e.size()+1];
        for (unsigned i = 0; i < e.size(); i++) {
            `exten_ser` ser;
            std::cerr << "etype =" << std::endl;
            __ser(ser,e[i]);

            // Validate extension data has minimum required size
            if (ser.res.size() < 4) {
                std::cerr << "Extension data too small: " << ser.res.size() << " bytes, skipping" << std::endl;
                continue;
            }

            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]));

            // Validate extension length matches available data
            if (len > ser.res.size() - 4) {
                std::cerr << "Extension length mismatch: claimed=" << len << " available=" << (ser.res.size()-4) << ", skipping" << std::endl;
                continue;
            }

            // Special handling for certificate_authorities extension (type 57)
            if (etype == 57) {
                std::cerr << "Processing certificate_authorities extension (type 57) with length " << len << std::endl;
            }

            unsigned char *data = new unsigned char[ser.res.size()-4];
            std::copy(ser.res.begin()+4,ser.res.end(),data);
            ptls_exts[i].type = etype;
            std::cerr << "etype =" << etype << std::endl;
            ptls_exts[i].data.base = data;
            //std::cerr << "data =" << data << std::endl;
            ptls_exts[i].data.len = len;
            std::cerr << "len =" << len << std::endl;
            // 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);
            // }
        }
        ptls_exts[e.size()].type = 0xffff;
        ptls_exts[e.size()].data.base = NULL;
        ptls_exts[e.size()].data.len = 0;

        for (unsigned i = 0; i < e.size(); i++) {
            std::cerr << "ptls_exts[i].type = " << ptls_exts[i].type << std::endl;
            //std::cerr << "ptls_exts[i].base = " << ptls_exts[i].data.base << std::endl;
            std::cerr << "ptls_exts[i].len = " << ptls_exts[i].data.len << std::endl;
        }
        handshake_properties->additional_extensions = ptls_exts;

        // Read the certificate, if we are a server

        if (is_server) { //segfault ??
            const char* cert_path = getenv("PANTHER_IVY_CERT_PATH");
            std::cerr << "PANTHER_IVY_CERT_PATH " << cert_path << std::endl;
            if (!cert_path) {
                const char* base_dir = getenv("PANTHER_IVY_BASE_DIR");
                std::cerr << "PANTHER_IVY_BASE_DIR " << base_dir << std::endl;
                if (base_dir) {
                    static std::string cert_path_str = std::string(base_dir) + "/quic/leaf_cert.pem";
                    cert_path = cert_path_str.c_str();
                } else {
                    // Fixed path: removed extra /quic directory
                    cert_path = "/opt/panther_ivy/protocol-testing/quic/leaf_cert.pem";
                }
            }

            // Check if certificate file exists before loading
            std::ifstream cert_file(cert_path);
            if (!cert_file.good()) {
                std::cerr << "Certificate file does not exist: " << cert_path << std::endl;
                // Try alternative certificate path
                cert_path = "/opt/panther_ivy/protocol-testing/quic/cert.pem";
                std::cerr << "Trying alternative certificate: " << cert_path << std::endl;
            }

            int r = ptls_load_certificates(ctx, cert_path);
            std::cerr << "ptls_load_certificates result: " << r << " for path: " << cert_path << std::endl;
            /* Read the certificate file */
            if (r != 0) {
                std::cerr << "could not load certificate file: " << cert_path << std::endl;
                std::cerr << "This may cause certificate_authorities extension (type 57) validation to fail" << std::endl;
                exit(1);
            }
            const char* key_path = getenv("PANTHER_IVY_KEY_PATH");
            if (key_path) {
                std::cerr << "PANTHER_IVY_KEY_PATH " << key_path << std::endl;
            } else {
                const char* base_dir = getenv("PANTHER_IVY_BASE_DIR");
                if (base_dir) {
                    const static std::string key_path_str = std::string(base_dir) + "/quic/leaf_cert.key";
                    key_path = key_path_str.c_str();
                } else {
                    key_path = "/opt/panther_ivy/protocol-testing/quic/quic/leaf_cert.key";
                }
            }
            if (key_path) {
                std::cerr << "PANTHER_IVY_KEY_PATH " << key_path << std::endl;
            } else {
                std::cerr << "PANTHER_IVY_KEY_PATH not set, using hardcoded leaf_cert.key\n";
                key_path = "/opt/panther_ivy/protocol-testing/quic/quic/leaf_cert.key";
            }
            if (set_sign_certificate_from_key_file(key_path, ctx)) {
                std::cerr << "could not load key file leaf_cert.key\n";
                exit(1);
            }
        }

        ptls_t *session;
        session = ptls_new(ctx,is_server ? 1 : 0);

        // Initialize data pointer to NULL to prevent callbacks from accessing garbage
        *ptls_get_data_ptr(session) = NULL;
        std::cerr << "PICOTLS: Initialized session data pointer to NULL for safety\n";

        ptls_set_server_name(session, "servername", strlen("servername"));

        //if(const char* env_p = std::getenv("TEST_IMPL")) {
        //    std::cerr << "TEST_IMPL " << env_p << std::endl;
        //    std::cerr << "is_server " << is_server << std::endl;
            // if(strncmp(env_p,"quic-go",10) == 0
            //     || strncmp(env_p,"quiche",10) == 0
            //        || strncmp(env_p,"lsquic",10) == 0
            //           || strncmp(env_p,"quinn",10) == 0
            //              || strncmp(env_p,"aioquic",10) == 0) //TODO ask why sometime ALPN needed
            //     if (is_server) { // && false
                        std::cerr << "ptls_set_negotiated_protocol"  << std::endl;
                        ptls_set_negotiated_protocol(session,
                            (const char*) handshake_properties->client.negotiated_protocols.list[0].base,
                            handshake_properties->client.negotiated_protocols.list[0].len);
                // }
        //}

        picotls_connection *s = new picotls_connection(c, session, ctx, *`cb`, handshake_properties, is_server);
        *ptls_get_data_ptr(session) = s;
        std::cerr << "PICOTLS: Set session data pointer to connection object (id=" << c << ", is_server=" << is_server << ")\n";

        if(is_zrtt != NULL){ //is_server &&
            /**
            * @brief picoquic_server_setup_ticket_aead_contexts
            */
            uint8_t temp_secret[256]; /* secret_max */
            int ret = 0;
            std::cerr << "picoquic_server_setup_ticket_aead_contexts 1 " << std::endl;
            ptls_cipher_suite_t cipher_ticket ={ 0, &ptls_openssl_aes128gcm, &ptls_openssl_sha256 };
            //  { 0,  &ptls_openssl_aes256gcmsha384, &ptls_openssl_aes128gcmsha256, &ptls_minicrypto_chacha20poly1305sha256 };
            //{ 0, &ptls_openssl_aes128gcm, &ptls_openssl_sha256, &ptls_openssl_aes128gcm };
            if (cipher_ticket.hash->digest_size > sizeof(temp_secret)) {
                ret = -1;
            } else {
                std::cerr << "picoquic_server_setup_ticket_aead_contexts 2 " << std::endl;
                //ctx->random_bytes(temp_secret, cipher_ticket.hash->digest_size);
                for(int i = 0; i < cipher_ticket.hash->digest_size; i++)
                    temp_secret[i] = 1;
                std::cerr << "picoquic_server_setup_ticket_aead_contexts 3 " << std::endl;
                /* Create the AEAD contexts */
                ret = quic_set_aead_from_secret(&s->ticket_aead_encrypt, &cipher_ticket, 1, temp_secret, "random label");
                if (ret == 0) {
                    std::cerr << "picoquic_server_setup_ticket_aead_contexts 4 " << std::endl;
                    ret = quic_set_aead_from_secret(&s->ticket_aead_decrypt, &cipher_ticket, 0, temp_secret, "random label");
                }
                std::cerr << "picoquic_server_setup_ticket_aead_contexts 5 " << std::endl;
                /* erase the temporary secret */
                ptls_clear_memory(temp_secret, cipher_ticket.hash->digest_size);
            }

            ptls_iovec_t ticket_psk = ptls_iovec_init(NULL, 0);
            std::cerr << "ptls_encrypt_ticket_t " << is_zrtt << std::endl;
            ptls_encrypt_ticket_t* encrypt_ticket = (ptls_encrypt_ticket_t*) malloc(sizeof(ptls_encrypt_ticket_t)+ sizeof(ptls_iovec_t*));
            if (encrypt_ticket != NULL) {
                encrypt_ticket->cb = tls_server_encrypt_ticket_cb;
                ptls_iovec_t** ppreceiver = (ptls_iovec_t**)(((char*)encrypt_ticket) + sizeof(ptls_encrypt_ticket_t));
                *ppreceiver = &ticket_psk;
                ctx->encrypt_ticket = encrypt_ticket;
            }

            ctx->ticket_lifetime = 100000; /* 100,000 seconds, a bit more than one day */
            ctx->max_early_data_size = 0xFFFFFFFF;
            ctx->require_dhe_on_psk = 1;

            ptls_iovec_t ticket_psk2 = ptls_iovec_init(NULL, 0);
            std::cerr << "ptls_save_ticket_t " << is_zrtt << std::endl;
            ptls_save_ticket_t* save_ticket = (ptls_save_ticket_t*) malloc(sizeof(ptls_save_ticket_t)+ sizeof(ptls_iovec_t*)); // + sizeof(ptls_iovec_t*)
            if (save_ticket != NULL) {
                save_ticket->cb = tls_client_save_ticket_cb; //memory error
                ptls_iovec_t** ppreceiver2 = (ptls_iovec_t**)(((char*)save_ticket) + sizeof(ptls_save_ticket_t));
                *ppreceiver2 = &ticket_psk2;
                ctx->save_ticket = save_ticket;
            }
        } else {
            ctx->ticket_lifetime = 0; // no ticket? was: 100000; /* 100,000 seconds, a bit more than one day */
            ctx->require_dhe_on_psk = 1;
        }


        `cid_map`[c] = s;

        // Start the handshake if we are the client. The in_epoch is zero for "initial".

        if (!is_server)
            picotls_do_handshake(s,0,0,0);
        >>>
    }

    implement create_0rtt_client(c:cid, is_server:bool, e:extens) {
        <<< impure

        //is_rtt = true; //todo
        if(is_server)
            is_client_test = true;

        std::cerr << "create cid = " << c << std::endl;

        // We create a new picootls session, and add an entry in the cid_map
        // for it.

        ptls_context_t* ctx;
        ctx = new ptls_context_t;
        memset(ctx, 0, sizeof(ptls_context_t));

        /*
        HelloRetryRequest random:
            CF 21 AD 74 E5 9A 61 11 BE 1D 8C 02 1E 65 B8 91
            C2 A2 11 16 7A BB 8C 5E 07 9E 09 E2 C8 A8 33 9C
        */
        ctx->random_bytes = ptls_openssl_random_bytes;
        ctx->key_exchanges = picotls_key_exchanges;
        ctx->cipher_suites = picotls_cipher_suites;
        ctx->send_change_cipher_spec = 0;
        // ctx->hkdf_label_prefix = QUIC_LABEL_QUIC_BASE;

        ctx->hkdf_label_prefix__obsolete = NULL;
        ctx->update_traffic_key = (ptls_update_traffic_key_t *)malloc(sizeof(ptls_update_traffic_key_t));
        memset(ctx->update_traffic_key, 0, sizeof(ptls_update_traffic_key_t));
        //ctx->update_traffic_key = new ptls_update_traffic_key_t;
        ctx->update_traffic_key->cb = quic_update_traffic_key_cb;

        //ctx->on_extension = new ptls_on_extension_t;
        ctx->on_extension = (ptls_on_extension_t *)malloc(sizeof(ptls_on_extension_t));
        memset(ctx->on_extension, 0, sizeof(ptls_on_extension_t));
        ctx->on_extension->cb = tls_on_extension_cb;

        ctx->get_time = &ptls_get_time;
        const char * is_zrtt = getenv("ZERORTT_TEST");

        ctx->max_early_data_size = 0xFFFFFFFF;

        std::cerr << "PROUT 3 "  << std::endl;

        ptls_handshake_properties_t *handshake_properties = new ptls_handshake_properties_t;
        memset(handshake_properties, 0, sizeof(ptls_handshake_properties_t));
        handshake_properties->collect_extension = tls_collect_extensions_cb;
        handshake_properties->collected_extensions = tls_collected_extensions_cb;

        std::cerr << "PROUT 4 " << std::endl;

        ptls_iovec_t *alpn_vec = new(ptls_iovec_t);  // TODO: will leak this

        // h3-x == HTTP/3 over QUIC
        // hq-x == HTTP/0.9 over QUIC
        alpn_vec->base = (uint8_t*) "hq-interop";
        if(const char* env_p = std::getenv("TEST_ALPN")) {
            std::cerr << "TEST_ALPN " << env_p << std::endl;
            alpn_vec->base = (uint8_t*) env_p;
        }

        alpn_vec->len = strlen((char *)(alpn_vec->base));
        handshake_properties->client.negotiated_protocols.count = 1;
        handshake_properties->client.negotiated_protocols.list  = alpn_vec;

        // add the extensions
        ptls_raw_extension_t *ptls_exts = new ptls_raw_extension_t[e.size()+1];
        for (unsigned i = 0; i < e.size(); i++) {
            `exten_ser` ser;
            std::cerr << "etype =" << std::endl;
            __ser(ser,e[i]);

            // Validate extension data has minimum required size
            if (ser.res.size() < 4) {
                std::cerr << "Extension data too small: " << ser.res.size() << " bytes, skipping" << std::endl;
                continue;
            }

            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]));

            // Validate extension length matches available data
            if (len > ser.res.size() - 4) {
                std::cerr << "Extension length mismatch: claimed=" << len << " available=" << (ser.res.size()-4) << ", skipping" << std::endl;
                continue;
            }

            // Special handling for certificate_authorities extension (type 57)
            if (etype == 57) {
                std::cerr << "Processing certificate_authorities extension (type 57) with length " << len << std::endl;
            }

            unsigned char *data = new unsigned char[ser.res.size()-4];
            std::copy(ser.res.begin()+4,ser.res.end(),data);
            ptls_exts[i].type = etype;
            std::cerr << "etype =" << etype << std::endl;
            ptls_exts[i].data.base = data;
            //std::cerr << "data =" << data << std::endl;
            ptls_exts[i].data.len = len;
            std::cerr << "len =" << len << std::endl;
            // 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);
            // }
        }
        ptls_exts[e.size()].type = 0xffff;
        ptls_exts[e.size()].data.base = NULL;
        ptls_exts[e.size()].data.len = 0;
        handshake_properties->additional_extensions = ptls_exts;

        // Read the certificate, if we are a server

        if (is_server) { //segfault ??
            const char* cert_path_alt = getenv("PANTHER_IVY_CERT_PATH");
            if (!cert_path_alt) cert_path_alt = "/opt/panther_ivy/protocol-testing/quic/leaf_cert.pem";
            int r = ptls_load_certificates(ctx, cert_path_alt);
            std::cerr << "ptls_load_certificates "  << r << std::endl;
            /* Read the certificate file */
            if (r != 0) {
                std::cerr << "could not load certificate file leaf_cert.pem\n";
                exit(1);
            } else if(set_sign_certificate_from_key_file("/opt/panther_ivy/protocol-testing/quic/leaf_cert.key", ctx)) {
                std::cerr << "could not load key file leaf_cert.key\n";
                exit(1);
            }
        }

        ptls_t *session;
        session = ptls_new(ctx,is_server ? 1 : 0);

        // Initialize data pointer to NULL to prevent callbacks from accessing garbage
        *ptls_get_data_ptr(session) = NULL;
        std::cerr << "PICOTLS: Initialized session data pointer to NULL for safety \n";

        ptls_set_server_name(session, "servername", strlen("servername"));

        if(const char* env_p = std::getenv("TEST_IMPL")) {
            std::cerr << "TEST_IMPL " << env_p << std::endl;
            std::cerr << "is_server " << is_server << std::endl;
            if(strncmp(env_p,"quic-go",10) == 0
                || strncmp(env_p,"quiche",10) == 0
                   || strncmp(env_p,"lsquic",10) == 0
                   || strncmp(env_p,"quinn",10) == 0
                      || strncmp(env_p,"aioquic",10) == 0) //TODO ask why sometime ALPN needed
                if (is_server) { // && false
                        std::cerr << "ptls_set_negotiated_protocol"  << std::endl;
                        ptls_set_negotiated_protocol(session,
                            (const char*) handshake_properties->client.negotiated_protocols.list[0].base,
                            handshake_properties->client.negotiated_protocols.list[0].len);
                }
        }

        picotls_connection *s = new picotls_connection(c, session, ctx, *`cb`, handshake_properties, is_server);
        *ptls_get_data_ptr(session) = s;
        std::cerr << "PICOTLS: Set session data pointer to connection object (id=" << c << ", is_server=" << is_server << ")\n";

        if(is_zrtt != NULL){ //is_server &&
            /**
            * @brief picoquic_server_setup_ticket_aead_contexts
            */
            uint8_t temp_secret[256]; /* secret_max */
            int ret = 0;
            std::cerr << "picoquic_server_setup_ticket_aead_contexts 1 " << std::endl;
            ptls_cipher_suite_t cipher_ticket = { 0, &ptls_openssl_aes128gcm, &ptls_openssl_sha256 };
            if (cipher_ticket.hash->digest_size > sizeof(temp_secret)) {
                ret = -1;
            } else {
                std::cerr << "picoquic_server_setup_ticket_aead_contexts 2 " << std::endl;
                //ctx->random_bytes(temp_secret, cipher_ticket.hash->digest_size);
                for(int i = 0; i < cipher_ticket.hash->digest_size; i++)
                    temp_secret[i] = 1;
                std::cerr << "picoquic_server_setup_ticket_aead_contexts 3 " << std::endl;
                /* Create the AEAD contexts */
                ret = quic_set_aead_from_secret(&s->ticket_aead_encrypt, &cipher_ticket, 1, temp_secret, "random label");
                if (ret == 0) {
                    std::cerr << "picoquic_server_setup_ticket_aead_contexts 4 " << std::endl;
                    ret = quic_set_aead_from_secret(&s->ticket_aead_decrypt, &cipher_ticket, 0, temp_secret, "random label");
                }
                std::cerr << "picoquic_server_setup_ticket_aead_contexts 5 " << std::endl;
                /* erase the temporary secret */
                ptls_clear_memory(temp_secret, cipher_ticket.hash->digest_size);
            }

            ptls_iovec_t ticket_psk = ptls_iovec_init(NULL, 0);
                std::cerr << "ptls_encrypt_ticket_t " << is_zrtt << std::endl;
                ptls_encrypt_ticket_t* encrypt_ticket = (ptls_encrypt_ticket_t*) malloc(sizeof(ptls_encrypt_ticket_t)+ sizeof(ptls_iovec_t*));
                if (encrypt_ticket != NULL) {
                    encrypt_ticket->cb = tls_server_encrypt_ticket_cb;
                    ptls_iovec_t** ppreceiver = (ptls_iovec_t**)(((char*)encrypt_ticket) + sizeof(ptls_encrypt_ticket_t));
                    *ppreceiver = &ticket_psk;
                    ctx->encrypt_ticket = encrypt_ticket;
                }

            ctx->ticket_lifetime = 100000; /* 100,000 seconds, a bit more than one day */
            ctx->max_early_data_size = 0xFFFFFFFF;
            ctx->require_dhe_on_psk = 1;
            ctx->omit_end_of_early_data = 1;

            ptls_iovec_t ticket_psk2 = ptls_iovec_init(NULL, 0);
                std::cerr << "ptls_save_ticket_t " << is_zrtt << std::endl;
                ptls_save_ticket_t* save_ticket = (ptls_save_ticket_t*) malloc(sizeof(ptls_save_ticket_t)+ sizeof(ptls_iovec_t*)); // + sizeof(ptls_iovec_t*)
                if (save_ticket != NULL) {
                    save_ticket->cb = tls_client_save_ticket_cb; //memory error
                    ptls_iovec_t** ppreceiver2 = (ptls_iovec_t**)(((char*)save_ticket) + sizeof(ptls_save_ticket_t));
                    *ppreceiver2 = &ticket_psk2;
                    ctx->save_ticket = save_ticket;
                }
        }

        if(is_zrtt == NULL){
            std::cerr << "PROUT 2 " << std::endl;
            ctx->ticket_lifetime = 0; // no ticket? was: 100000; /* 100,000 seconds, a bit more than one day */
            ctx->require_dhe_on_psk = 1;
            ctx->omit_end_of_early_data = 0;
        }

        std::cerr << "ptls_get_data_ptr"  << std::endl;

        `cid_map`[c] = s;

        std::cerr << "cid_map[c] = s;"  << std::endl;

        // Start the handshake if we are the client. The in_epoch is zero for "initial".

        if (!is_server)
            picotls_do_handshake(s,0,0,0);
        >>>
    }

    implement create_0rtt(c:cid, is_server:bool, e:extens, he:hextens) {
        <<< impure

        is_rtt = true;
        uint8_t *bytes;
        int bbsize = 0;
        std::cerr << "XXXXXXXXXXXX"  << "\n";
        bbsize = get_session_ticket_size();
        bytes = (uint8_t*) malloc(bbsize * sizeof(uint8_t));
        get_session_ticket(bytes,bbsize);
        ptls_iovec_t ticket_psk;
        vec_to_ptls_iovec(ticket_psk,bytes,bbsize);

        ptls_context_t* ctx;
        ctx = new ptls_context_t;
        memset(ctx, 0, sizeof(ptls_context_t));
        ctx->random_bytes = ptls_openssl_random_bytes;
        ctx->key_exchanges = picotls_key_exchanges;
        ctx->cipher_suites = picotls_cipher_suites;
        ctx->send_change_cipher_spec = 0;
        // ctx->hkdf_label_prefix = QUIC_LABEL_QUIC_BASE;
        ctx->hkdf_label_prefix__obsolete = NULL;

        //ctx->update_traffic_key = new ptls_update_traffic_key_t;
        ctx->update_traffic_key = (ptls_update_traffic_key_t *)malloc(sizeof(ptls_update_traffic_key_t));
        memset(ctx->update_traffic_key, 0, sizeof(ptls_update_traffic_key_t));
        ctx->update_traffic_key->cb = quic_update_traffic_key_cb;

        //ctx->on_extension = new ptls_on_extension_t;
        ctx->on_extension = (ptls_on_extension_t *)malloc(sizeof(ptls_on_extension_t));
        memset(ctx->on_extension, 0, sizeof(ptls_on_extension_t));
        ctx->on_extension->cb = tls_on_extension_cb;

        //ctx->client_mode = !is_server;

        ctx->require_client_authentication = 0;

        ctx->get_time = &ptls_get_time;

        const char * is_zrtt = getenv("ZERORTT_TEST");
        if(is_zrtt == NULL){
            ctx->ticket_lifetime = 0; // no ticket? was: 100000; /* 100,000 seconds, a bit more than one day */
            ctx->require_dhe_on_psk = 1;
            ctx->max_early_data_size = 0xFFFFFFFF;
            ctx->omit_end_of_early_data = 1;
        }
        else {
            std::cerr << "PROUT " << is_zrtt << std::endl;
            ctx->ticket_lifetime = 60 * 60 * 24;
            ctx->require_dhe_on_psk = 1;
            ctx->max_early_data_size = 0xFFFFFFFF;
            ctx->omit_end_of_early_data = 1;
        }

       // ctx->use_exporter = 1; // master secrets should be recorded

        ptls_handshake_properties_t *handshake_properties = new ptls_handshake_properties_t;
        memset(handshake_properties, 0, sizeof(ptls_handshake_properties_t));
        handshake_properties->collect_extension = tls_collect_extensions_cb;
        handshake_properties->collected_extensions = tls_collected_extensions_cb;

        ptls_iovec_t *alpn_vec = new(ptls_iovec_t);  // TODO: will leak this

        // h3-x == HTTP/3 over QUIC
        // hq-x == HTTP/0.9 over QUIC
        alpn_vec->base = (uint8_t*) "hq-interop";
        if(const char* env_p = std::getenv("TEST_ALPN")) {
            std::cerr << "TEST_ALPN " << env_p << std::endl;
            alpn_vec->base = (uint8_t*) env_p;
        }

        alpn_vec->len = strlen((char *)(alpn_vec->base));
        handshake_properties->client.negotiated_protocols.count = 1;
        handshake_properties->client.negotiated_protocols.list  = alpn_vec;

        ptls_raw_extension_t *ptls_exts = new ptls_raw_extension_t[e.size()+he.size()];
        for (unsigned i = 0; i < e.size(); i++) {
            `exten_ser` ser;
            std::cerr << "etype =" << std::endl;
            __ser(ser,e[i]);

            // Validate extension data has minimum required size
            if (ser.res.size() < 4) {
                std::cerr << "Extension data too small: " << ser.res.size() << " bytes, skipping" << std::endl;
                continue;
            }

            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]));

            // Validate extension length matches available data
            if (len > ser.res.size() - 4) {
                std::cerr << "Extension length mismatch: claimed=" << len << " available=" << (ser.res.size()-4) << ", skipping" << std::endl;
                continue;
            }

            // Special handling for certificate_authorities extension (type 57)
            if (etype == 57) {
                std::cerr << "Processing certificate_authorities extension (type 57) with length " << len << std::endl;
            }

            unsigned char *data = new unsigned char[ser.res.size()-4];
            std::copy(ser.res.begin()+4,ser.res.end(),data);
            ptls_exts[i].type = etype;
            std::cerr << "etype =" << etype << std::endl;
            ptls_exts[i].data.base = data;
            //std::cerr << "data =" << data << std::endl;
            ptls_exts[i].data.len = len;
            std::cerr << "len =" << len << std::endl;
            // 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);
            // }
        }
        for (unsigned i = 0; i <  he.size(); i++) {
            `exten_ser` ser;
            std::cerr << "etype =" << std::endl;
            __ser(ser,he[i]);
            // unsigned char etype = (((unsigned char)(ser.res[0])));
            // unsigned len = (((unsigned char)(ser.res[1])) << 16) + (((unsigned char)(ser.res[2])) << 8) + ((unsigned char)(ser.res[3]));
            // unsigned char *data = new unsigned char[ser.res.size()-4];
            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]));
            unsigned char *data = new unsigned char[ser.res.size()-4];
            std::copy(ser.res.begin()+4,ser.res.end(),data);
            ptls_exts[e.size()+i].type = etype;
            std::cerr << "etype =" << etype << std::endl;
            ptls_exts[e.size()+i].data.base = data;
            std::cerr << "data =" << data << std::endl;
            ptls_exts[e.size()+i].data.len = len;
            std::cerr << "len =" << len << std::endl;
            // 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);
            // }
        }
        ptls_exts[e.size()+he.size()].type = 0xffff;
        ptls_exts[e.size()+he.size()].data.base = NULL;
        ptls_exts[e.size()+he.size()].data.len = 0;

        handshake_properties->additional_extensions = ptls_exts;

        handshake_properties->client.negotiate_before_key_exchange = 0; // 1 = HELLORETRY

        if (ticket_psk.base != NULL && !is_server) {
            std::cerr << "ticket_psk "  <<  ticket_psk.len << "\n";
            //std::cerr << "ticket_psk "  <<  ticket_psk.base << "\n";
            handshake_properties->client.session_ticket.base = ticket_psk.base;
            handshake_properties->client.session_ticket.len = ticket_psk.len;
            size_t m = 0xFFFFFFFF;  //0xFFFFFFFF
            handshake_properties->client.max_early_data_size = &m;
        }


        // Read the certificate, if we are a server
        if (is_server) {
            /* Read the certificate file */
            if (const char* cert_path_alt = getenv("PANTHER_IVY_CERT_PATH")) {
                if (!cert_path_alt) cert_path_alt = "/opt/panther_ivy/protocol-testing/quic/leaf_cert.pem";
                int r = ptls_load_certificates(ctx, cert_path_alt) != 0) {
                 std::cerr << "could not load certificate file leaf_cert.pem\n";
                 exit(1);
            } else if(set_sign_certificate_from_key_file("/opt/panther_ivy/protocol-testing/quic/leaf_cert.key", ctx)) {
                std::cerr << "could not load key file leaf_cert.key\n";
                exit(1);
            }
        }

        ptls_t *session;
        session = ptls_new(ctx,is_server ? 1 : 0);

        // Initialize data pointer to NULL to prevent callbacks from accessing garbage
        *ptls_get_data_ptr(session) = NULL;
        std::cerr << "PICOTLS: Initialized session data pointer to NULL for safety \n";

        ptls_set_server_name(session, "servername", strlen("servername"));


        if(const char* env_p = std::getenv("TEST_IMPL")) {
            std::cerr << "TEST_IMPL " << env_p << std::endl;
            std::cerr << "TEST_IMPL " << env_p << std::endl;
            std::cerr << "is_server " << is_server << std::endl;
            if(strncmp(env_p,"quic-go",10) == 0
                || strncmp(env_p,"quiche",10) == 0
                   || strncmp(env_p,"lsquic",10) == 0
                   || strncmp(env_p,"quinn",10) == 0
                      || strncmp(env_p,"aioquic",10) == 0 || true) //TODO ask why sometime ALPN needed
                if (is_server) {
                        std::cerr << "ptls_set_negotiated_protocol"  << std::endl;
                        std::cerr << "ptls_set_negotiated_protocol"  << std::endl;
                        ptls_set_negotiated_protocol(session,
                            (const char*) handshake_properties->client.negotiated_protocols.list[0].base,
                            handshake_properties->client.negotiated_protocols.list[0].len);
                }

        }

        picotls_connection *s = new picotls_connection(c,session,ctx,*`cb`,handshake_properties,is_server);
        *ptls_get_data_ptr(session) = s;
        std::cerr << "PICOTLS: Set session data pointer to connection object (id=" << c << ", is_server=" << is_server << ")\n";


        `cid_map`[c] = s;

        if (!is_server)
            picotls_do_handshake(s,0,0,0);
        std::cerr << "create 0rtt finish"  << "\n";
        >>>
    }

    implement get_old_retry_token returns (b:stream_data) {
        <<< impure

            const char * session_file = getenv("RETRY_TOKEN_FILE");
            FILE *fp;
            size_t len = 0;
            ssize_t read;
            char * line = NULL;
            if(session_file != NULL) {
                fp = fopen(session_file,"r");
                read = getline(&line, &len, fp);
                fclose(fp);
                bool is_error = false;
                if(line != NULL) {
                    std::cerr << "RETRY_TOKEN_FILE 1 " << line << std::endl;
                    int hex_len = strlen(line);
                    std::cerr << "RETRY_TOKEN_FILE hex_len " << hex_len << std::endl;
                    uint8_t secret[hex_len/2];
                    for (int j = 0; j < hex_len/2; j++) {
                        int a = ws_xton(line[j*2]);
                        int b = ws_xton(line[j*2 + 1]);
                        if (a == -1 || b == -1) {
                            is_error = true;
                            std::cerr << "a ERROR " << line[j*2] << std::endl;
                            std::cerr << "b ERROR " << line[j*2 + 1] << std::endl;
                            std::cerr << "RETRY_TOKEN_FILE ERROR " << std::endl;
                            break;
                        }
                        secret[j] = a << 4 | b;
                        // std::cerr << "RETRY_TOKEN_FILE a " << a << std::endl;
                        // std::cerr << "RETRY_TOKEN_FILE b " << b << std::endl;
                        // std::cerr << "RETRY_TOKEN_FILE secret[j] " << secret[j] << std::endl;
                    }
                    std::cerr << "TEST_ST 2 " << secret << std::endl;
                    /*for(int i = (hex_len/2)-1; i >=0 && !is_error; i--)
                        b.push_back(secret[i]);*/
                    for(int i = 0; i < hex_len/2 && !is_error; i++)
                        b.push_back(secret[i]);
                }
            }
        >>>
    }


    implement get_old_new_token returns (b:stream_data) {
        <<< impure
            const char * session_file = getenv("NEW_TOKEN_FILE");
            FILE *fp;
            size_t len = 0;
            ssize_t read;
            char * line = NULL;
            if(session_file != NULL) {
                fp = fopen(session_file,"r");
                read = getline(&line, &len, fp);
                fclose(fp);
                bool is_error = false;
                if(line != NULL) {
                    // std::cerr << "NEW_TOKEN_FILE 1 " << line << std::endl;
                    int hex_len = strlen(line);
                    // std::cerr << "NEW_TOKEN_FILE hex_len " << hex_len << std::endl;
                    uint8_t secret[hex_len/2];
                    for (int j = 0; j < hex_len/2; j++) {
                        int a = ws_xton(line[j*2]);
                        int b = ws_xton(line[j*2 + 1]);
                        if (a == -1 || b == -1) {
                            is_error = true;
                            std::cerr << "a ERROR " << line[j*2] << std::endl;
                            std::cerr << "b ERROR " << line[j*2 + 1] << std::endl;
                            std::cerr << "NEW_TOKEN_FILE ERROR " << std::endl;
                            break;
                        }
                        secret[j] = a << 4 | b;
                        // std::cerr << "NEW_TOKEN_FILE a " << a << std::endl;
                        // std::cerr << "NEW_TOKEN_FILE b " << b << std::endl;
                        // std::cerr << "NEW_TOKEN_FILE secret[j] " << secret[j] << std::endl;
                    }
                    //std::cerr << "NEW_TOKEN_FILE 2 " << secret << std::endl;
                    /*for(int i = (hex_len/2)-1; i >=0 && !is_error; i--)
                        b.push_back(secret[i]);*/
                    for(int i = 0; i < hex_len/2 && !is_error; i++)
                        b.push_back(secret[i]);
                }
            }
        >>>
    }
Destroy frees a cid

    implement destroy(c:cid) {
        <<< impure

        // TODO: actually delete everything here
        picotls_connection *s = `cid_map`[c];
        ptls_free(s->gs);
        free(s->cctx);
        free(s->hsp);
        free(s->session_ticket);
        `cid_map`.erase(c);


        >>>
    }
Set the initial key material.

    implement set_initial_keys(c:cid,salt:bytes,ikm:bytes) {
        <<< impure

        picotls_connection *s = `cid_map`[c];
        std::cerr << "setup_initial_traffic_keys cid: " << c << "\n";
        std::cerr << "setup_initial_traffic_keys session: " << s->is_server << "\n";
        setup_initial_traffic_keys(s,salt,ikm);
        std::cerr << "setup_initial_traffic_keys salt: " << salt << "\n";
        std::cerr << "setup_initial_traffic_keys ikm: " << ikm << "\n";
        >>>
    }
Get the cipher initial value size

    implement iv_size(c:cid,l:level) returns (sz:index) {
        <<< impure
        std::cerr << "iv_size start " << "\n";
        std::cerr << "iv_size cid " <<  c << "\n";
        std::cerr << "iv_size level " << l <<  "\n";
        // Check if c exists in cid_map
        if (`cid_map`.find(c) == `cid_map`.end()) {
            std::cerr << "cid " << c << " not found in cid_map\n";
            return 0;
        }
        picotls_connection *s = `cid_map`[c];
        ptls_cipher_context_t *pn_enc = (ptls_cipher_context_t *)(s->crypto_context[l].pn_enc);
        if (!pn_enc)
            pn_enc = (ptls_cipher_context_t *)(s->crypto_context[l].pn_dec);
        if (!pn_enc) {
            std::cerr << "cipher for level " << l << " is not set\n";
            sz = 0;
        }
        else sz = pn_enc->algo->iv_size;
        std::cerr << "iv_size end " << sz << "\n";
        return sz;
        >>>
    }

    implement encrypt_cipher(c:cid,l:level,clear:bytes,iv:bytes,recv:bool) returns (cipher:bytes) {
        <<< impure
        std::cerr << "encrypt_cipher start " << "\n";
        std::cerr << "encrypt_cipher c " << c << "\n";
        std::cerr << "encrypt_cipher iv " << iv << "\n";
        picotls_connection *s = `cid_map`[c];
        ptls_cipher_context_t *pn_enc;
        if (recv) {
            pn_enc = (ptls_cipher_context_t *)(s->crypto_context[l].pn_dec);
            std::cerr << "encrypt_cipher pn_dec " << pn_enc << "\n";
        }
        else {
            pn_enc = (ptls_cipher_context_t *)(s->crypto_context[l].pn_enc);
            std::cerr << "encrypt_cipher pn_enc " << pn_enc << "\n";
        }
        if(pn_enc != 0)
            encrypt_symm(pn_enc,clear,iv,cipher);
        std::cerr << "encrypt_cipher end " << cipher << "\n";
        return cipher;
        >>>
    }

    implement decrypt_cipher(c:cid,l:level,cipher:bytes,iv:bytes) returns (clear:bytes) {
        <<< impure

        std::cerr << "decrypt_cipher start " << "\n";
        picotls_connection *s = `cid_map`[c];
        ptls_cipher_context_t *pn_enc = (ptls_cipher_context_t *)(s->crypto_context[l].pn_dec);
        encrypt_symm(pn_enc,cipher,iv,clear);
        std::cerr << "decrypt_cipher end " << "\n";

        >>>
    }

    implement save_token(token:stream_data) {
        <<< impure

        unsigned str_d[token.size()];
        for (int i = 0; i < token.size(); i++)
            str_d[i] = token[i];
        const char * session_file = getenv("NEW_TOKEN_FILE");
        FILE *fp;
        if(session_file != NULL) {
            fp = fopen(session_file,"w");
            for (int i = 0; i < token.size(); i++) {
                //std::cerr <<  "str_d[i] "  << str_d[i] << std::endl;
                //std::cerr <<  "str_d[i] == 0 "  << (str_d[i] == 0) << std::endl;
                if(str_d[i] < 16)
                    fprintf(fp, "0%x", str_d[i]);
                else
                    fprintf(fp, "%x", str_d[i]);
            }
            fclose(fp);
        }

        >>>
    }

    implement save_initial_max_stream_data_uni(i:stream_pos) {
        <<< impure

        const char * session_file = getenv("initial_max_stream_data_uni");
        FILE *fp;
        if(session_file != NULL) {
            fp = fopen(session_file,"w");
            if (fp != NULL) {
                fprintf(fp, "%llu", i);
                fclose(fp);
            } else {
                fprintf(stderr, "Warning: Could not open file %s for writing\n", session_file);
            }
        }

        >>>
    }

    implement get_initial_max_stream_data_uni  returns (i:stream_pos) {
        <<< impure

            const char * session_file = getenv("initial_max_stream_data_uni");
            FILE *fp;
            size_t len = 0;
            ssize_t read;
            char * line = NULL;
            if(session_file != NULL) {
                fp = fopen(session_file,"r");
                read = getline(&line, &len, fp);
                fclose(fp);
                bool is_error = false;
                if(line != NULL) {
                    i = (uint32_t) std::stoll(line);
                }
            }
        >>>
    }

    implement save_initial_max_stream_data_bidi_remote(i:stream_pos) {
        <<< impure

        const char * session_file = getenv("initial_max_stream_data_bidi_remote");
        FILE *fp;
        if(session_file != NULL) {
            fp = fopen(session_file,"w");
            if (fp != NULL) {
                fprintf(fp, "%llu", i);
                fclose(fp);
            } else {
                fprintf(stderr, "Warning: Could not open file %s for writing\n", session_file);
            }
        }

        >>>
    }

    implement get_initial_max_stream_data_bidi_remote  returns (i:stream_pos) {
        <<< impure

            const char * session_file = getenv("initial_max_stream_data_bidi_remote");
            FILE *fp;
            size_t len = 0;
            ssize_t read;
            char * line = NULL;
            if(session_file != NULL) {
                fp = fopen(session_file,"r");
                read = getline(&line, &len, fp);
                fclose(fp);
                bool is_error = false;
                if(line != NULL) {
                    i = (uint32_t) std::stoll(line);
                }
            }
        >>>
    }

    implement save_initial_max_data(i:stream_pos) {
        <<< impure

        const char * session_file = getenv("initial_max_data");
        FILE *fp;
        if(session_file != NULL) {
            fp = fopen(session_file,"w");
            if (fp != NULL) {
                fprintf(fp, "%llu", i);
                fclose(fp);
            } else {
                fprintf(stderr, "Warning: Could not open file %s for writing\n", session_file);
            }
        }

        >>>
    }

    implement get_initial_max_data  returns (i:stream_pos) {
        <<< impure

            const char * session_file = getenv("initial_max_data");
            FILE *fp;
            size_t len = 0;
            ssize_t read;
            char * line = NULL;
            if(session_file != NULL) {
                fp = fopen(session_file,"r");
                read = getline(&line, &len, fp);
                fclose(fp);
                bool is_error = false;
                if(line != NULL) {
                    i = (uint32_t) std::stoll(line);
                }
            }
        >>>
    }


    implement save_initial_max_stream_data_bidi_local(i:stream_pos) {
        <<< impure

        const char * session_file = getenv("initial_max_stream_data_bidi_local");
        FILE *fp;
        if(session_file != NULL) {
            fp = fopen(session_file,"w");
            if (fp != NULL) {
                fprintf(fp, "%llu", i);
                fclose(fp);
            } else {
                fprintf(stderr, "Warning: Could not open file %s for writing\n", session_file);
            }
        }

        >>>
    }

    implement get_initial_max_stream_data_bidi_local  returns (i:stream_pos) {
        <<< impure

            const char * session_file = getenv("initial_max_stream_data_bidi_local");
            FILE *fp;
            size_t len = 0;
            ssize_t read;
            char * line = NULL;
            if(session_file != NULL) {
                fp = fopen(session_file,"r");
                read = getline(&line, &len, fp);
                fclose(fp);
                bool is_error = false;
                if(line != NULL) {
                    i = (uint32_t) std::stoll(line);
                }
            }
        >>>
    }

    implement save_initial_max_stream_id_bidi(i:stream_id) {
        <<< impure

        const char * session_file = getenv("initial_max_stream_id_bidi");
        FILE *fp;
        if(session_file != NULL) {
            fp = fopen(session_file,"w");
            if (fp != NULL) {
                if(i < 16)
                        fprintf(fp, "0%x", i);
                else
                        fprintf(fp, "%x", i);
                fclose(fp);
            } else {
                fprintf(stderr, "Warning: Could not open file %s for writing\n", session_file);
            }
        }

        >>>
    }

    implement get_initial_max_stream_id_bidi  returns (i:stream_id) {
        <<< impure

            const char * session_file = getenv("initial_max_stream_id_bidi");
            FILE *fp;
            size_t len = 0;
            ssize_t read;
            char * line = NULL;
            if(session_file != NULL) {
                fp = fopen(session_file,"r");
                read = getline(&line, &len, fp);
                fclose(fp);
                bool is_error = false;
                if(line != NULL) {
                    i = (uint16_t) std::stoll(line);
                }
            }
        >>>
    }

    implement save_active_connection_id_limit(i:stream_pos) {
        <<< impure
        const char * session_file = getenv("active_connection_id_limit");
        FILE *fp;
        if(session_file != NULL) {
            fp = fopen(session_file,"w");
            if (fp != NULL) {
                fprintf(fp, "%llu", i);
                fclose(fp);
            } else {
                fprintf(stderr, "Warning: Could not open file %s for writing\n", session_file);
            }
        }

        >>>
    }

    implement get_active_connection_id_limit  returns (i:stream_pos) {
        <<< impure

            const char * session_file = getenv("active_connection_id_limit");
            FILE *fp;
            size_t len = 0;
            ssize_t read;
            char * line = NULL;
            if(session_file != NULL) {
                fp = fopen(session_file,"r");
                read = getline(&line, &len, fp);
                fclose(fp);
                bool is_error = false;
                if(line != NULL) {
                    i = (uint32_t) std::stoll(line);
                }
            }
        >>>
    }

    implement compute_retry_integrity_tag(odcil: stream_pos, odcid: cid,
                                       pversion: version,
                                       dcil: stream_pos, dcid: cid,
                                       scil: stream_pos, scid: cid,
                                       token : stream_data,
                                       seq:seq_num, h: stream_pos,is_recv: bool) returns (cipher:bytes) {
    <<< impure

        const char * session_file = getenv("RETRY_TOKEN_FILE");
        FILE *fp;
        if(session_file != NULL && is_recv) {
            unsigned str_d[token.size()];
            for (int i = 0; i < token.size(); i++)
                str_d[i] = token[i];
            fp = fopen(session_file,"w");
            for (int i = 0; i < token.size(); i++) {
                //std::cerr <<  "str_d[i] "  << str_d[i] << std::endl;
                //std::cerr <<  "str_d[i] == 0 "  << (str_d[i] == 0) << std::endl;
                if(str_d[i] < 16)
                    fprintf(fp, "0%x", str_d[i]);
                else
                    fprintf(fp, "%x", str_d[i]);
            }
            fclose(fp);
        }


        uint8_t plaintext[] = "";
        //0xbe 0c 69 0b 9f 66 57 5a 1d 76 6b 54 e3 68 c8 4e : rfc9001
        /*uint8_t key[16] = { 0xbe, 0x0c, 0x69, 0x0b, 0x9f, 0x66, 0x57,
                            0x5a, 0x1d, 0x76, 0x6b, 0x54, 0xe3, 0x68,
                            0xc8, 0x4e };*/

        //0xcc ce 18 7e d0 9a 09 d0 57 28 15 5a 6c b9 6b e1
        //0xcc 0xce 0x18 0x7e 0xd0 0x9a 0x09 0xd0 0x57 0x28 0x15 0x5a 0x6c 0xb9 0x6b 0xe1 : draft 29
        uint8_t key[16];

        //0x4d 0x32 0xec 0xdb 0x2a 0x21 0x33 0xc8 0x41 0xe4 0x04 0x3d 0xf2 0x7d 0x44 0x30
        if(pversion == 0xff00001c) {
            uint8_t key2[16] = { 0x4d, 0x32, 0xec, 0xdb, 0x2a,
                                 0x21, 0x33, 0xc8, 0x41, 0xe4,
                                 0x04, 0x3d, 0xf2, 0x7d, 0x44,
                                 0x30 };
            for(int i = 0; i < 16;i++)
                key[i] = key2[i];
        }
        else if(pversion == 0xff00001d) {
            uint8_t key2[16] = { 0xcc, 0xce, 0x18, 0x7e, 0xd0,
                                 0x9a, 0x09, 0xd0, 0x57, 0x28,
                                 0x15, 0x5a, 0x6c, 0xb9, 0x6b,
                                 0xe1 };
            for(int i = 0; i < 16;i++)
                key[i] = key2[i];
        }
        else if(pversion == 0x00000001) {
            //0xbe 0x0c 0x69 0x0b 0x9f 0x66 0x57 0x5a 0x1d 0x76 0x6b 0x54 0xe3 0x68 0xc8 0x4e
            uint8_t key2[16] = { 0xbe, 0x0c, 0x69, 0x0b, 0x9f,
                                 0x66, 0x57, 0x5a, 0x1d, 0x76,
                                 0x6b, 0x54, 0xe3, 0x68, 0xc8,
                                 0x4e };
            for(int i = 0; i < 16;i++)
                key[i] = key2[i];
        }

        //0x461599d35d632bf2239825bb
        /*uint8_t nonce[12] = { 0x46, 0x15, 0x99, 0xd3, 0x5d, 0x63, 0x2b,
                              0xf2, 0x23, 0x98, 0x25, 0xbb };*/

        //0xe5 0x49 0x30 0xf9 0x7f 0x21 0x36 0xf0 0x53 0x0a 0x8c 0x1c : draft 29
        uint8_t nonce[12];
        //0x4d 0x16 0x11 0xd0 0x55 0x13 0xa5 0x52 0xc5 0x87 0xd5 0x75
        if(pversion == 0xff00001c) {
            uint8_t nonce2[12] = { 0x4d, 0x16, 0x11, 0xd0, 0x55,
                                   0x13, 0xa5, 0x52, 0xc5, 0x87,
                                   0xd5, 0x75 }; //nonce[12]
            for(int i = 0; i < 12; i++)
                nonce[i] = nonce2[i];
        }
        else if(pversion == 0xff00001d) {
            uint8_t nonce2[12] = { 0xe5, 0x49, 0x30, 0xf9, 0x7f,
                                   0x21, 0x36, 0xf0, 0x53, 0x0a,
                                   0x8c, 0x1c }; //nonce[12]
            for(int i = 0; i < 12; i++)
                nonce[i] = nonce2[i];
        }
        else if(pversion == 0x00000001) {
            //0x46 0x15 0x99 0xd3 0x5d 0x63 0x2b 0xf2 0x23 0x98 0x25 0xbb
            uint8_t nonce2[12] = { 0x46, 0x15, 0x99, 0xd3, 0x5d,
                                   0x63, 0x2b, 0xf2, 0x23, 0x98,
                                   0x25, 0xbb }; //nonce[12]
            for(int i = 0; i < 12; i++)
                nonce[i] = nonce2[i];
        }

        ptls_aead_context_t *aead = ptls_aead_new_direct(&ptls_openssl_aes128gcm, 1, key, nonce);
        std::cerr << "compute_retry_integrity_tag aead " << aead <<  "\n";


        uint32_t checksum_length = aead->algo->tag_size;
        std::cerr << "c 3 \n";
        std::cerr << "compute_retry_integrity_tag ptls_aead_context_t " << "\n";

        std::vector<uint8_t> ad;
        //ad.resize(sizeof(odcid));
        std::cerr << "compute_retry_integrity_tag odcil " << odcil << "\n";
        std::cerr << "compute_retry_integrity_tag odcid " << std::hex << odcid << "\n";
        std::cerr << "compute_retry_integrity_tag h " << h << "\n";
        std::cerr << "compute_retry_integrity_tag pversion " << std::hex << pversion << "\n";
        std::cerr << "compute_retry_integrity_tag dcil " << dcil << "\n";
        std::cerr << "compute_retry_integrity_tag dcid " << std::hex << dcid << "\n";
        std::cerr << "compute_retry_integrity_tag scil " <<  scil << "\n";
        std::cerr << "compute_retry_integrity_tag scid " << std::hex << scid << "\n";
        std::cerr << "compute_retry_integrity_tag seq " << std::dec << seq << "\n";

        uint8_t pushed;
        std::cerr << "compute_retry_integrity_tag odcil \n";
        ad.push_back(odcil);
        for (int i = odcil-1; i >= 0; --i) {
            pushed = (odcid.val >> (8*i)) & 0xff;
            std::cerr << std::to_string(pushed) << " ";
            ad.push_back(pushed);
        }
        std::cerr << "\n";
        ad.push_back(h);
        std::cerr << "compute_retry_integrity_tag version \n";
        for (int i = 4-1; i >= 0; --i) {
            pushed = (pversion >> (8*i)) & 0xff;
            std::cerr << std::to_string(pushed) << " ";
            ad.push_back(pushed);
        }
        std::cerr << "\n";
        std::cerr << "compute_retry_integrity_tag dcid \n";
        ad.push_back(dcil);
        for (int i = dcil-1; i >= 0; --i) {
            pushed = (dcid.val >> (8*i)) & 0xff;
            std::cerr << std::to_string(pushed) << " ";
            ad.push_back(pushed);
        }
        std::cerr << "\n";
        std::cerr << "compute_retry_integrity_tag scid \n";
        ad.push_back(scil); //scil
        for (int i = scil-1; i >= 0; --i) {
            pushed = (scid.val >> (8*i)) & 0xff;
            std::cerr << std::to_string(pushed) << " ";
            ad.push_back(pushed);
        }

        std::cerr << "\n";
        std::cerr << "compute_retry_integrity_tag token size: " << token.size()  << "\n";
        for (unsigned i = 0; i < token.size(); i++) {
            std::cerr << std::hex << token[i] << ' ';
            ad.push_back(token[i]);
        }

        std::cerr << "\n";
        //std::copy(token.begin(),token.end(),ad.end());

        std::vector<uint8_t> bytes;
        bytes.resize(ad.size());

        std::cerr << "compute_retry_integrity_tag ad size: " << std::dec << ad.size()  << "\n";
        std::cerr << "\n";

        std::copy(ad.begin(),ad.end(),bytes.begin());
        std::cerr << "compute_retry_integrity_tag bytes size: " << std::dec << bytes.size()  << "\n";
        std::cerr << "\n";

        std::vector<uint8_t> output;
        output.resize(16);

        std::cerr << "compute_retry_integrity_tag output size: " << std::dec << output.size()  << "\n";
        size_t encrypted = ptls_aead_encrypt(aead,
          &output[0], &plaintext[0], 0 , seq, //seq, strnlen ?strlen(plaintext)
          &bytes[0], bytes.size());

        cipher.resize(output.size());
        std::copy(output.begin(),output.end(),cipher.begin());

        std::cerr << "compute_retry_integrity_tag encrypted size: " << std::dec << encrypted  << "\n";
        std::cerr << "compute_retry_integrity_tag output size: " << std::dec << output.size()  << "\n";
        std::cerr << "\n";

        std::cerr << "compute_retry_integrity_tag finish " << "\n";
    >>>
    }

    implement encrypt_aead(c:cid,l:level,clear:bytes,seq:seq_num,ad:bytes) returns (cipher:bytes) {
        <<< impure
        std::cerr << "encrypt_aead start l " << l << "\n";
        std::cerr << "encrypt_aead start c " << c << "\n";
        std::cerr << "encrypt_aead start seq " << seq << "\n";
        picotls_connection *s = `cid_map`[c];
        ptls_aead_context_t *aead = (ptls_aead_context_t *)(s->crypto_context[l].aead_encrypt);
        std::cerr << "encrypt_aead aead " << aead << "\n";
        uint32_t checksum_length = aead->algo->tag_size;
        std::vector<uint8_t> bytes;
        bytes.resize(ad.size());
        std::copy(ad.begin(),ad.end(),bytes.begin());
        std::vector<uint8_t> input, output;
        input.resize(clear.size());
        std::copy(clear.begin(),clear.end(),input.begin());
        output.resize(clear.size() + checksum_length);
        std::cerr << "encrypt_aead encrypted  1" << "\n";
        size_t encrypted = ptls_aead_encrypt(aead,
          &output[0], &input[0], input.size(), seq,
          &bytes[0], bytes.size());
        std::cerr << "encrypt_aead encrypted " << encrypted << "\n";
        cipher.resize(output.size());
        std::copy(output.begin(),output.end(),cipher.begin());
        std::cerr << "encrypt_aead finish " << "\n";
        return cipher;
        >>>
    }

    implement decrypt_aead(c:cid,l:level,cipher:bytes,seq:seq_num,ad:bytes)
    returns (res:decrypt_result) {
        <<< impure
        std::cerr << "decrypt_aead start " << "\n";
        std::cerr << "decrypt_aead ad " << ad.size() <<  "\n";
        picotls_connection *s = `cid_map`[c];
        ptls_aead_context_t *aead = (ptls_aead_context_t *)(s->crypto_context[l].aead_decrypt);
        std::cerr << "aead == " << aead << "\n";
        if(aead != 0) {
            uint32_t checksum_length = aead->algo->tag_size;
            std::cerr << "checksum_length == " << checksum_length << "\n";
            std::vector<uint8_t> bytes;
            bytes.resize(ad.size());
            std::copy(ad.begin(),ad.end(),bytes.begin());

            std::cerr << "seq == " << seq << "\n";
            std::cerr << "bytes.size() == " << bytes.size() << "\n";

            std::vector<uint8_t> input, output;
            input.resize(cipher.size());
            std::copy(cipher.begin(),cipher.end(),input.begin());
            output.resize(cipher.size() - checksum_length);
            size_t decrypted = ptls_aead_decrypt(aead,
                                                &output[0],
                                                &input[0],
                                                input.size(),
                                                seq,
                                                &bytes[0], bytes.size());
            res.ok = decrypted <= input.size();
            std::cerr << "decrypted <= input.size() == " << (decrypted <= input.size()) << "\n";
            if (res.ok) {
                res.data.resize(output.size());
                std::copy(output.begin(),output.end(),res.data.begin());
            }
        } else {
            std::cerr << "decrypt_aead failure " << "\n";
            res.ok = false;
        }
        std::cerr << "decrypt_aead finish " << "\n";
        >>>
    }
upper.send is called with application data to be transmitted to the peer.

    implement upper.send(c:cid,data:bytes) {
        <<< impure
        // TODO: implement this
        //picotls_connection *s = `cid_map`[c];
        std::cerr << "upper.send s "   << "\n";
        >>>
    }
lower.recv is called with data received from the peer on the lower interface.

    implement lower.recv(c:cid,data:bytes,lev:lower.level) {
        <<< impure
        std::cerr << "lower.recv c "   << c << "\n";
        picotls_connection *s = `cid_map`[c];
        std::cerr << "lower.recv s "   << s << "\n";
        std::vector<char> &input = s->input;
        for (unsigned i = 0; i < data.size(); i++) {
            input.push_back(data[i]);
        }
        // TODO: make the epoch a parameter of this call
        // For now, we assume that the epochs come in order
        size_t in_epoch = ptls_get_read_epoch(s->gs);
        const char * test_type = getenv("TEST_TYPE");
        if(test_type != NULL && strcmp(test_type,"mim") != 0) {
            picotls_do_handshake(s,in_epoch,&(s->input[0]),s->input.size());
            std::cerr << "before clear"   << std::endl;
            s->input.clear();
            std::cerr << "after clear"   << std::endl;
        } else {
            std::cerr << "mim test -> no handshake"   << std::endl;
        }

        >>>
    }


    trusted isolate iso = this

    attribute test = impl
}
}