From 2479167b375d4af0d9cd54c460af86d95b04caa4 Mon Sep 17 00:00:00 2001 From: leitner Date: Thu, 7 May 2015 23:53:05 +0000 Subject: [PATCH] check in some experimental X.509 parsing code --- Makefile | 30 +++++++- fmt_tls_alert.c | 10 +++ fmt_tls_alert_pkt.c | 11 +++ fmt_tls_clienthello.c | 51 +++++++++++++ fmt_tls_packet.c | 13 ++++ fmt_tls_serverhello.c | 163 +++++++++++++++++++++++++++++++++++++++++ init_tls_context.c | 19 +++++ scan_certificate.c | 5 +- tinytls.h | 166 ++++++++++++++++++++++++++++++++++++++++++ tls_cipherprio.c | 13 ++++ x.c | 56 ++++++++++++++ 11 files changed, 535 insertions(+), 2 deletions(-) create mode 100644 fmt_tls_alert.c create mode 100644 fmt_tls_alert_pkt.c create mode 100644 fmt_tls_clienthello.c create mode 100644 fmt_tls_packet.c create mode 100644 fmt_tls_serverhello.c create mode 100644 init_tls_context.c create mode 100644 tinytls.h create mode 100644 tls_cipherprio.c create mode 100644 x.c diff --git a/Makefile b/Makefile index 9f7336b..0bdfca9 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ all: t1 t2 parse dumpidx idx2ldif addindex bindrequest tinyldap \ tinyldap_standalone tinyldap_debug ldapclient ldapclient_str \ -md5password mysql2ldif acl dumpacls ldapdelete asn1dump # t6 # t +md5password mysql2ldif acl dumpacls ldapdelete asn1dump tls.a x # t6 # t asn1.a: fmt_asn1intpayload.o fmt_asn1length.o fmt_asn1tag.o \ fmt_asn1int.o fmt_asn1string.o fmt_asn1transparent.o scan_asn1tag.o \ @@ -37,6 +37,13 @@ mduptab_init.o mduptab_init_reuse.o mduptab_reset.o auth.a: auth.o +tls.a: fmt_tls_clienthello.o init_tls_context.o \ +fmt_tls_serverhello.o fmt_tls_alert.o fmt_tls_packet.o \ +tls_cipherprio.o fmt_tls_alert_pkt.o fmt_tls_handshake_cert.o \ +fmt_tls_handshake_certs_header.o fmt_tls_serverhellodone.o \ +tls_accept.o tls_connect.o tls_doread.o tls_dowrite.o + + DIET=/opt/diet/bin/diet -Os CROSS= #CROSS=i686-mingw32- @@ -171,10 +178,31 @@ scan_asn1generic.o: scan_asn1generic.c asn1.h asn1oid.o: asn1oid.c asn1.h +init_tls_context.o: init_tls_context.c tinytls.h +fmt_tls_clienthello.o: fmt_tls_clienthello.c tinytls.h + ldap_match_sre.o: ldap_match_sre.c ldap.h +x: tls.a + privatekey.pem: openssl genrsa -out $@ windoze: $(MAKE) DIET= CROSS=i686-mingw32- asn1dump + +fmt_tls_alert.o: fmt_tls_alert.c tinytls.h asn1.h +fmt_tls_alert_pkt.o: fmt_tls_alert_pkt.c tinytls.h asn1.h +fmt_tls_clienthello.o: fmt_tls_clienthello.c tinytls.h asn1.h +fmt_tls_handshake_cert.o: fmt_tls_handshake_cert.c tinytls.h asn1.h +fmt_tls_handshake_certs_header.o: fmt_tls_handshake_certs_header.c \ + tinytls.h asn1.h +fmt_tls_packet.o: fmt_tls_packet.c tinytls.h asn1.h +fmt_tls_serverhello.o: fmt_tls_serverhello.c tinytls.h asn1.h +fmt_tls_serverhellodone.o: fmt_tls_serverhellodone.c tinytls.h asn1.h +init_tls_context.o: init_tls_context.c tinytls.h asn1.h +tls_accept.o: tls_accept.c tinytls.h asn1.h +tls_cipherprio.o: tls_cipherprio.c +tls_connect.o: tls_connect.c tinytls.h asn1.h +tls_doread.o: tls_doread.c tinytls.h asn1.h +tls_dowrite.o: tls_dowrite.c tinytls.h asn1.h diff --git a/fmt_tls_alert.c b/fmt_tls_alert.c new file mode 100644 index 0000000..06eeb7b --- /dev/null +++ b/fmt_tls_alert.c @@ -0,0 +1,10 @@ +#include "tinytls.h" + +size_t fmt_tls_alert(char* dest,enum alertlevel level,enum alerttype type) { + if (dest) { + dest[0]=level; + dest[1]=type; + } + return 2; +} + diff --git a/fmt_tls_alert_pkt.c b/fmt_tls_alert_pkt.c new file mode 100644 index 0000000..0b33d0c --- /dev/null +++ b/fmt_tls_alert_pkt.c @@ -0,0 +1,11 @@ +#include +#include "tinytls.h" + +size_t fmt_tls_alert_pkt(char* dest,enum alertlevel level,enum alerttype type) { + if (dest) { + memcpy(dest,"\x15\x03\x03\x00\x02",5); + dest[5]=level; + dest[6]=type; + } + return 7; +} diff --git a/fmt_tls_clienthello.c b/fmt_tls_clienthello.c new file mode 100644 index 0000000..809e4f0 --- /dev/null +++ b/fmt_tls_clienthello.c @@ -0,0 +1,51 @@ +#include "tinytls.h" +#include "uint16.h" +#include "uint32.h" +#include + +size_t fmt_tls_clienthello(char* dest, struct ssl_context* sc) { + size_t hnextlen=sc->servername?strlen(sc->servername)+9:0; + if (hnextlen>0x1000) return 0; + if (sc->session.l>0xff) return 0; + if (dest) { + char* x; + dest[0]=22; // content type: handshake + uint16_pack_big(dest+1,0x303); // tls 1.2 + // uint16_pack_big(dest+3,length); + dest[5]=0x01; // handshake type: client hello + // uint16_pack_big(dest+6,length); + uint16_pack_big(dest+9,0x0303); // tls 1.2 + uint32_pack_big(dest+11,time(0)); + memcpy(dest+15,sc->myrandom,sizeof(sc->myrandom)); + if ((dest[43]=sc->session.l)) + memcpy(dest+44,sc->session.s,sc->session.l); + x=dest+44+sc->session.l; + uint16_pack_big(x,6); + uint16_pack_big(x+2,0x3d); // TLS_RSA_WITH_AES_256_CBC_SHA256 + uint16_pack_big(x+4,0x35); // TLS_RSA_WITH_AES_256_CBC_SHA + uint16_pack_big(x+6,0xff); // "we support renegotiation" + x+=8; +#if 0 + memcpy(x,"\x02\x01\x00",3); // 2 compression methods, deflate and null + x+=3; +#else + memcpy(x,"\x01\x00",2); // only support null compression + x+=2; +#endif + uint16_pack_big(x,hnextlen); + x+=2; + if (hnextlen) { + uint16_pack_big(x,0); // extension id 0 = server_name + uint16_pack_big(x+2,hnextlen-4); // length + uint16_pack_big(x+4,hnextlen-6); // another length + x[6]=0; // hostname type: DNS + uint16_pack_big(x+7,hnextlen-9); // yet another length + memcpy(x+9,sc->servername,hnextlen-9); + x+=hnextlen; + } + uint16_pack_big(dest+3,x-dest-5); + uint16_pack_big(dest+7,x-dest-9); + return x-dest; + } else + return 44+sc->session.l+8+2+2+hnextlen; +} diff --git a/fmt_tls_packet.c b/fmt_tls_packet.c new file mode 100644 index 0000000..c067fd7 --- /dev/null +++ b/fmt_tls_packet.c @@ -0,0 +1,13 @@ +#include "tinytls.h" +#include "uint16.h" + +size_t fmt_tls_packet(char* dest,enum contenttype ct, size_t len) { + if (len>0xffff) return 0; + if (dest) { + dest[0]=ct; + dest[1]=0x03; dest[2]=0x03; // version: TLS 1.2 + dest[3]=len>>8; + dest[4]=len&0xff; + } + return 5; +} diff --git a/fmt_tls_serverhello.c b/fmt_tls_serverhello.c new file mode 100644 index 0000000..fe1a79f --- /dev/null +++ b/fmt_tls_serverhello.c @@ -0,0 +1,163 @@ +#include "uint16.h" +#include "uint32.h" +#include "tinytls.h" +#include +#include +#include +#include + +/* + +This function parses a client helo and writes the server helo into a +buffer. It returns the number of bytes in the server helo message, +so you can send it over the TCP connection. If you pass in NULL as +the destination buffer, the function tells you how much space it +would have needed. So the regular way to use it is to call it twice. + +For efficiency: server helo is around 50 bytes of boilerplate plus +the session data (which comes from the SSL context you are passing +in, field session.l, and its length is limited to 255 bytes). + +The function returns (size_t)-1 if the input buffer does not contain a +full client helo message (i.e. "read more data, then try again") and +it returns (size_t)-2 if the input buffer contained an invalid message +("somebody is trying to hack you; drop connection"). + +If we do not support any of the ciphers or compression methods the other +side wants, this function writes an alert message into the buffer +(length 7). You are then supposed to send that buffer and close the +connection. + +Note that TLS has an encapsulation, so you get an outer message header +and an inner message header. For both input and output we handle both +headers. + +*/ + +size_t fmt_tls_serverhello(char* dest,const char* clienthello,size_t len,struct ssl_context* sc) { + size_t l,i; + int compressionmethod=-1,hostlen=-1; + const char* host; + uint16_t best=0,bestprio=0x7fff; + + /* first check if the clienthello is completely there */ + if (len<5 || len<(l=5+uint16_read_big(clienthello+3))) + return (size_t)-1; + + /* ok, it's complete, now check if it is valid. */ + if (l < 49 || // Minimum length with one cipher suite + clienthello[0]!=22 || // Content Type: handshake + clienthello[1]!=3 || // at least SSL 3.0 + clienthello[5]!=1 || // Handshake Type: Client Hello + clienthello[6]!=0 || // inner length is 3 bytes, outer length is 2 bytes, so first byte of inner length must be 0 + uint16_read_big(clienthello+7)!=l-9) // inner length must fit into outer length +invalid: + return (size_t)-2; + + i=43; + i+=(unsigned char)clienthello[i]+1; // session length + if (i+1>=l) goto invalid; + { + uint16 ciphers=uint16_read_big(clienthello+i); + uint16_t* c; + size_t j; + if (ciphers&1) goto invalid; // must be multiple of two + if (ciphers==0) goto invalid; // must support at least one cipher suite + c=(uint16_t*)(clienthello+i+2); + if (i+ciphers+2>=l) goto invalid; // do the ciphers fit in the packet? + for (j=0; jl) goto invalid; // do the compression methods fit in the packet? + for (j=0; jl) + goto invalid; + if (clienthello[i]==0 && clienthello[i+1]==0) { /* server_name extension */ + hostlen=uint16_read_big(clienthello+i+2); + host=clienthello+i+4; + } + if ((i+=4+uint16_read_big(clienthello+i+2))>l) + goto invalid; + } + /* The client hello validated OK; we can generate a reply now. */ + + /* do we support any of the ciphers and compression methods? */ + if (bestprio==0x7fff || compressionmethod==-1) { /* nope */ + return dest? + fmt_tls_alert_pkt(dest,FATAL,HANDSHAKE_FAILURE) : + 7; + } + + if (!sc->servername) { + /* We have not yet copied the data out of the client hello. + * Do so now. */ + memcpy(sc->theirrandom,clienthello+15,sizeof(sc->theirrandom)); + sc->cipher=best; + sc->compressionmethod=0; + if (hostlen!=-1) { + char* sn; + if (!(sn=malloc(hostlen+1))) { + memcpy(sn,host,hostlen); + sn[hostlen]=0; + sc->servername=sn; + } + } + sc->timestamp=time(0); + } + + if (sc->session.l>0xff) return 0; + + if (dest) { + char* x; + fmt_tls_packet(dest,HANDSHAKE,1+3+2+4+28+1+sc->session.l+2+1+2+5); + dest+=5; + dest[0]=2; // type 2 = server hello + dest[1]=0; + uint16_pack_big(dest+2,2+4+28+1+sc->session.l+2+1+2+5); + uint16_pack_big(dest+4,0x0303); // version: TLS 1.2 + uint32_pack_big(dest+6,sc->timestamp); + memcpy(dest+10,sc->myrandom,28); + x=dest+38; + *x=sc->session.l; + memcpy(x+1,sc->session.s,sc->session.l); + x+=sc->session.l+1; + uint16_pack_big(x,sc->cipher); + x[2]=sc->compressionmethod; + x+=3; + memcpy(x,"\x00\x05\xff\x01\x00\x01\x00",7); // this is the renegotiation extension + } + return 5+1+3+2+4+28+1+sc->session.l+2+1+2+5; +#if 0 + char type = 2; + char length[3]; // big endian + char version[2]; // 0x03, 0x03 + uint32_t gmt_unix_time; // big endian + char random[28]; + char session_id_length; + char session_id[session_id_length]; + char cipher_id[2]; + char compression; // 0 - none, 1 - deflate + char extensions_length[2]; // \x00\x05 + char renegotiation_extension[5]; // \xff\x01\x00\x01\x00 +#endif + +} diff --git a/init_tls_context.c b/init_tls_context.c new file mode 100644 index 0000000..1c5f9a2 --- /dev/null +++ b/init_tls_context.c @@ -0,0 +1,19 @@ +#include "tinytls.h" +#include "open.h" +#include + +void init_tls_context_norandom(struct ssl_context* sc, const char* servername) { + memset(sc,0,sizeof *sc); + sc->servername=servername; +} + +int init_tls_context(struct ssl_context* sc, const char* servername) { + int fd=open_read("/dev/urandom"); + int r; + if (fd==-1) return -1; + init_tls_context_norandom(sc,servername); + r=read(fd,sc->myrandom,sizeof(sc->myrandom)); + close(fd); + if (r!=sizeof(sc->myrandom)) return -1; + return 0; +} diff --git a/scan_certificate.c b/scan_certificate.c index 9153ed3..0f6f3ea 100644 --- a/scan_certificate.c +++ b/scan_certificate.c @@ -32,6 +32,9 @@ struct rsaprivatekey { size_t* freewhendone; }; +struct dsaprivatekey { +}; + void printasn1(const char* buf,const char* max); static int findindn(struct string* dn,enum x509_oid id,struct string* dest) { @@ -379,7 +382,7 @@ int main(int argc,char* argv[]) { printf("failed to parse certificate\n"); free(freewhendone); - buf=mmap_read(argc>1?argv[1]:"privatekey.pem",&l); + buf=mmap_read(argc>2?argv[2]:"privatekey.pem",&l); if (!buf) { puts("privatekey.pem not found"); return 1; } n=scan_rsaprivatekey(buf,l,&k,&freewhendone); diff --git a/tinytls.h b/tinytls.h new file mode 100644 index 0000000..3234a7e --- /dev/null +++ b/tinytls.h @@ -0,0 +1,166 @@ +#ifndef _TINYTLS_H +#define _TINYTLS_H + +#include +#include + +/* for struct string: */ +#include "asn1.h" + +typedef enum { + NONE, + FAIL, // protocol failure, refuse all operations + + READ_CLIENTHELLO, // tls_accept called, trying to read client hello + WRITE_ALERTFAIL, // got something bad, write alert and fail + WRITE_SERVERHELLO, // got client hello, trying to write server hello + + WRITE_CLIENTHELLO, // tls_connect called, trying to write client hello + READ_SERVERHELLO, // trying to read server hello + READ_CERT, // read server hello, trying to read cert + READ_SERVERHELLODONE, // read server hello + cert, trying to read server hello done +} tls_state; + +enum { MAXCERT=4 }; + +struct ssl_context { + tls_state state; + char myrandom[28]; + char theirrandom[28]; + time_t timestamp; /* so we can age out old sessions from the session id cache */ + const char* servername; + struct string session; // a cookie sent during the handshake, so if a client comes back later, we can save work + struct string mycert[MAXCERT]; // my own cert, maybe an intermediate cert, and a ca cert; sent during handshake + struct string theircert[MAXCERT]; + struct string message; // the packet we are currently trying to read or write + size_t ofsinmessage; + char scratch[2048]; + ssize_t (*_read)(uintptr_t handle,char* buf,size_t len); + ssize_t (*_write)(uintptr_t handle,const char* buf,size_t len); + int (*_close)(uintptr_t handle); + int (*readcert)(struct ssl_context* sc); // use sc->servername to read cert into sc->mycert + // return 0 for success, enum alerttype otherwise + uint16_t cipher,compressionmethod; +}; + +/* Put servername into ssl_context, set empty session. */ +void init_tls_context_norandom(struct ssl_context* sc, const char* servername); + +/* Put servername into ssl_context, and fill random bytes from + * /dev/urandom. Return 0 if OK, -1 if /dev/urandom failed */ +int init_tls_context(struct ssl_context* sc, const char* servername); + +/* Generate a client hello inside a handshake packet using the + * servername and session data from the context, return number of bytes + * written to dest. Call with dest=NULL to get needed buffer size. */ +size_t fmt_tls_clienthello(char* dest, struct ssl_context* sc); + +/* The response to a client hello consists of several packets: + 1. server hello + 2. certificate + [3. server key exchange for a DHE cipher suite] + 4. server hello done */ + +/* Generate a server hello from a client hello that came in, return + * number of bytes written to dest. + * Call with dest=NULL to get needed buffer size. + * Returns (size_t)-1 if the client hello is not complete, + * (size_t)-2 if the client hello is invalid + * if the client helo is valid but has no common ciphers, write an alert + * and return length of alert (7) */ +size_t fmt_tls_serverhello(char* dest,const char* clienthelo,size_t len,struct ssl_context* sc); + +/* + * Allocate a buffer that can hold all the X.509 certificates plus 3 + * bytes per certificate plus 12 bytes for the headers. Write the + * certificates to buf+12 using fmt_tls_handshake_cert. Then write + * the header to buf using fmt_tls_handshake_certs_header; give it the + * sum of the return values of fmt_tls_handshake_cert as len. + */ +size_t fmt_tls_handshake_cert(char* dest,const char* cert,size_t len); +size_t fmt_tls_handshake_certs_header(char* dest,size_t len_of_certs); + +size_t fmt_tls_serverhellodone(char* dest); + +size_t scan_tls_serverhello(const char* buf,size_t len,struct ssl_context* sc); + +enum alertlevel { + WARNING=1, + FATAL=2 +}; + +enum alerttype { + CLOSE_NOTIFY=0, + UNEXPECTED_MESSAGE=10, + BAD_RECORD_MAC=20, + DECRYPTION_FAILED=21, + RECORD_OVERFLOW=22, + DECOMPRESSION_FAILURE=30, + HANDSHAKE_FAILURE=40, + NO_CERT=41, + BAD_CERT=42, + UNSUPPORTED_CERT=43, + CERT_REVOKED=44, + CERT_EXPIRED=45, + CERT_UNKNOWN=46, + ILLEGAL_PARAMETER=47, + UNKNOWN_CA=48, + ACCESS_DENIED=49, + DECODE_ERROR=50, + DECRYPT_ERROR=51, + EXPORT_RESTRICTION=60, + PROTOCOL_VERSION=70, + INSUFFICIENT_SECURITY=71, + INTERNAL_ERROR=80, + USER_CANCELED=90, + NO_RENEGOTIATION=100, + UNSUPPORTED_EXT=110 +}; + +/* Generate a TLS alert (only the alert!) */ +size_t fmt_tls_alert(char* dest,enum alertlevel level,enum alerttype type); + +/* Generate a TLS alert with outer header */ +size_t fmt_tls_alert_pkt(char* dest,enum alertlevel level,enum alerttype type); + +enum contenttype { + CHANGE_CIPHER_SPEC=20, + ALERT=21, + HANDSHAKE=22, + APPLICATION_DATA=23 +}; + +size_t fmt_tls_packet(char* dest,enum contenttype ct, size_t len); + +enum ciphers { + TLS_RSA_WITH_AES_256_CBC_SHA256=0x3d, + TLS_RSA_WITH_AES_256_CBC_SHA=0x35, +}; + +/* return the desirability of a cipher (number >= 0) + * or -1 if the cipher is not supported + * the higher the number, the less desirable is the cipher */ +int tls_cipherprio(uint16_t cipher); + +typedef enum { + OK=0, + IOFAIL=-1, // I/O error + WANTREAD=-2, // socket was non-blocking; wait for read event + WANTWRITE=-3, // socket was non-blocking; wait for write event + OOM=-4, // out of memory + PROTOCOLFAIL=-5, // received invalid packets + NEGOTIATIONFAIL=-6, // no common ciphers / compression methods + CRYPTOFAIL=-7, // cryptographic failure (weak key detected or so) + CERTFAIL=-8, // certificate validation failed + YOUSUCK=-42, // user-supplied callbacks violated protocol +} tls_error_code; + +tls_error_code tls_connect(uintptr_t fd,struct ssl_context* sc); +tls_error_code tls_accept(uintptr_t fd,struct ssl_context* sc); + +/* these are internal helpers */ +tls_error_code tls_dowrite(uintptr_t fd,struct ssl_context* sc); +tls_error_code tls_doread(uintptr_t fd,struct ssl_context* sc); +tls_error_code tls_checkalert(struct ssl_context* sc); + +#endif diff --git a/tls_cipherprio.c b/tls_cipherprio.c new file mode 100644 index 0000000..fddbec1 --- /dev/null +++ b/tls_cipherprio.c @@ -0,0 +1,13 @@ +#include + +static uint16_t ciphers[] = { + TLS_RSA_WITH_AES_256_CBC_SHA256, + TLS_RSA_WITH_AES_256_CBC_SHA, +}; + +int tls_cipherprio(uint16_t cipher) { + size_t i; + for (i=0; i + + +char pkt[]= +"\x16\x03\x01\x01\x4e\x01\x00\x01\x4a\x03\x03\x4f\x93\x81\xfb\x57" +"\x72\x4b\x79\x31\x35\x0f\x4e\xb6\xd5\x47\xb7\x0d\xb5\x54\x0e\xd1" +"\x71\xc1\xb6\x9b\x9f\xdb\xa5\xf0\xe4\x43\xac\x00\x00\xa0\xc0\x30" +"\xc0\x2c\xc0\x28\xc0\x24\xc0\x14\xc0\x0a\xc0\x22\xc0\x21\x00\xa3" +"\x00\x9f\x00\x6b\x00\x6a\x00\x39\x00\x38\x00\x88\x00\x87\xc0\x32" +"\xc0\x2e\xc0\x2a\xc0\x26\xc0\x0f\xc0\x05\x00\x9d\x00\x3d\x00\x35" +"\x00\x84\xc0\x12\xc0\x08\xc0\x1c\xc0\x1b\x00\x16\x00\x13\xc0\x0d" +"\xc0\x03\x00\x0a\xc0\x2f\xc0\x2b\xc0\x27\xc0\x23\xc0\x13\xc0\x09" +"\xc0\x1f\xc0\x1e\x00\xa2\x00\x9e\x00\x67\x00\x40\x00\x33\x00\x32" +"\x00\x9a\x00\x99\x00\x45\x00\x44\xc0\x31\xc0\x2d\xc0\x29\xc0\x25" +"\xc0\x0e\xc0\x04\x00\x9c\x00\x3c\x00\x2f\x00\x96\x00\x41\x00\x07" +"\xc0\x11\xc0\x07\xc0\x0c\xc0\x02\x00\x05\x00\x04\x00\x15\x00\x12" +"\x00\x09\x00\x14\x00\x11\x00\x08\x00\x06\x00\x03\x00\xff\x02\x01" +"\x00\x00\x80\x00\x00\x00\x11\x00\x0f\x00\x00\x0c\x62\x6c\x6f\x67" +"\x2e\x66\x65\x66\x65\x2e\x64\x65\x00\x0b\x00\x04\x03\x00\x01\x02" +"\x00\x0a\x00\x34\x00\x32\x00\x0e\x00\x0d\x00\x19\x00\x0b\x00\x0c" +"\x00\x18\x00\x09\x00\x0a\x00\x16\x00\x17\x00\x08\x00\x06\x00\x07" +"\x00\x14\x00\x15\x00\x04\x00\x05\x00\x12\x00\x13\x00\x01\x00\x02" +"\x00\x03\x00\x0f\x00\x10\x00\x11\x00\x0d\x00\x22\x00\x20\x06\x01" +"\x06\x02\x06\x03\x05\x01\x05\x02\x05\x03\x04\x01\x04\x02\x04\x03" +"\x03\x01\x03\x02\x03\x03\x02\x01\x02\x02\x02\x03\x01\x01\x00\x0f" +"\x00\x01\x01\x15\x03\x03\x00\x02\x02\x30"; + +int main() { + char buf[200]; + size_t l; + struct ssl_context sc; + int fd; + + fd=socket_tcp4b(); + if (fd==-1) + diesys(1,"socket"); + if (socket_connect4(fd,ip4loopback,443)==-1) + if (socket_connect4(fd,ip4loopback,4433)==-1) + diesys(1,"connect"); + +#if 0 + init_tls_context_norandom(&sc,NULL); + printf("%zu\n",fmt_tls_serverhello(NULL,pkt,sizeof(pkt),&sc)); + init_tls_context_norandom(&sc,"blog.fefe.de"); + l=fmt_tls_clienthello(buf,&sc); + printf("%zu\n",fmt_tls_serverhello(NULL,buf,l,&sc)); +#endif + + init_tls_context(&sc,"localhost"); + tls_connect(fd,&sc); + return 0; +}