| author | Stephen Paul Weber
<singpolyma@singpolyma.net> 2026-03-10 18:13:10 UTC |
| committer | Stephen Paul Weber
<singpolyma@singpolyma.net> 2026-03-10 18:13:10 UTC |
| parent | 77298b34440f108039e06b6d35465beaca8e26a2 |
| cpp/alt/SSL-openssl.cpp | +802 | -0 |
diff --git a/cpp/alt/SSL-openssl.cpp b/cpp/alt/SSL-openssl.cpp new file mode 100644 index 0000000..b7a3d5a --- /dev/null +++ b/cpp/alt/SSL-openssl.cpp @@ -0,0 +1,802 @@ +#include <string.h> + +#ifdef HX_WINDOWS +# include <winsock2.h> +# include <wincrypt.h> +#else +# include <sys/socket.h> +# include <strings.h> +# include <errno.h> +typedef int SOCKET; +#endif + +#include <hxcpp.h> +#include <hx/OS.h> + +#if defined(NEKO_MAC) && !defined(IPHONE) && !defined(APPLETV) +#include <Security/Security.h> +#endif + +typedef size_t socket_int; + +#define SOCKET_ERROR (-1) +#define NRETRYS 20 + +// --- OpenSSL Headers --- +#include <openssl/ssl.h> +#include <openssl/err.h> +#include <openssl/x509.h> +#include <openssl/x509v3.h> +#include <openssl/pem.h> +#include <openssl/evp.h> +#include <openssl/rand.h> + +#define val_ssl(o) ((sslctx*)o.mPtr) +#define val_conf(o) ((sslconf*)o.mPtr) +#define val_cert(o) ((sslcert*)o.mPtr) +#define val_pkey(o) ((sslpkey*)o.mPtr) + +struct SocketWrapper : public hx::Object +{ + HX_IS_INSTANCE_OF enum { _hx_ClassId = hx::clsIdSocket }; + SOCKET socket; +}; + +struct sslctx : public hx::Object +{ + HX_IS_INSTANCE_OF enum { _hx_ClassId = hx::clsIdSsl }; + + SSL *s; + + void create(SSL_CTX *ctx) + { + s = SSL_new(ctx); + _hx_set_finalizer(this, finalize); + } + + void destroy() + { + if( s ) + { + SSL_free(s); + s = NULL; + } + } + + static void finalize(Dynamic obj) + { + ((sslctx *)(obj.mPtr))->destroy(); + } + + String toString() { return HX_CSTRING("sslctx"); } +}; + +struct sslconf : public hx::Object +{ + HX_IS_INSTANCE_OF enum { _hx_ClassId = hx::clsIdSslConf }; + + SSL_CTX *c; + + void create(bool server) + { + // Use general purpose TLS methods + const SSL_METHOD *method = server ? TLS_server_method() : TLS_client_method(); + c = SSL_CTX_new(method); + _hx_set_finalizer(this, finalize); + } + + void destroy() + { + if( c ) + { + SSL_CTX_free(c); + c = NULL; + } + } + + static void finalize(Dynamic obj) + { + ((sslconf *)(obj.mPtr))->destroy(); + } + + String toString() { return HX_CSTRING("sslconfig"); } +}; + +struct sslcert : public hx::Object +{ + HX_IS_INSTANCE_OF enum { _hx_ClassId = hx::clsIdSslCert }; + + STACK_OF(X509) *chain; + int current_index; + bool head; + + void create(X509 *inC, STACK_OF(X509) *inChain = NULL, int index = 0) + { + current_index = index; + if( inChain ) { + chain = inChain; + head = false; + } else { + chain = sk_X509_new_null(); + if (inC) sk_X509_push(chain, inC); + head = true; + } + _hx_set_finalizer(this, finalize); + } + + void destroy() + { + if( chain && head ) + { + sk_X509_pop_free(chain, X509_free); + chain = NULL; + } + } + + X509* get_current() { + if (!chain || current_index >= sk_X509_num(chain)) return NULL; + return sk_X509_value(chain, current_index); + } + + static void finalize(Dynamic obj) + { + ((sslcert *)(obj.mPtr))->destroy(); + } + + String toString() { return HX_CSTRING("sslcert"); } +}; + +struct sslpkey : public hx::Object +{ + HX_IS_INSTANCE_OF enum { _hx_ClassId = hx::clsIdSslKey }; + + EVP_PKEY *k; + + void create(EVP_PKEY *inK = NULL) + { + k = inK; + _hx_set_finalizer(this, finalize); + } + + void destroy() + { + if( k ) + { + EVP_PKEY_free(k); + k = NULL; + } + } + + static void finalize(Dynamic obj) + { + ((sslpkey *)(obj.mPtr))->destroy(); + } + + String toString() { return HX_CSTRING("sslpkey"); } +}; + +static void ssl_error(const char* prefix = NULL){ + unsigned long errCode = ERR_get_error(); + char buf[256]; + ERR_error_string_n(errCode, buf, sizeof(buf)); + String msg = String(buf); + if (prefix) msg = String(prefix) + HX_CSTRING(": ") + msg; + hx::Throw( msg ); +} + +static bool is_ssl_blocking( SSL* ssl, int r ) { + int err = SSL_get_error(ssl, r); + return err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE; +} + +Dynamic _hx_ssl_new( Dynamic hconf ) { + sslctx *ssl = new sslctx(); + sslconf *conf = val_conf(hconf); + ssl->create(conf->c); + if (!ssl->s) { + ssl->destroy(); + ssl_error("SSL_new failed"); + } + return ssl; +} + +void _hx_ssl_close( Dynamic hssl ) { + sslctx *ssl = val_ssl(hssl); + ssl->destroy(); +} + +void _hx_ssl_debug_set (int i) { + // OpenSSL debug is typically handled differently, often compiled in. + // For standard builds, we leave this as a no-op or wire to tracing. +} + +void _hx_ssl_handshake( Dynamic hssl ) { + int r; + sslctx *ssl = val_ssl(hssl); + POSIX_LABEL(handshake_again); + + hx::EnterGCFreeZone(); + r = SSL_do_handshake( ssl->s ); + hx::ExitGCFreeZone(); + + if (r <= 0) { + if ( is_ssl_blocking(ssl->s, r) ) { + HANDLE_EINTR(handshake_again); + hx::Throw(HX_CSTRING("Blocking")); + } else { + ssl_error("Handshake failed"); + } + } +} + +void _hx_ssl_set_socket( Dynamic hssl, Dynamic hsocket ) { + sslctx *ssl = val_ssl(hssl); + SocketWrapper *socket = (SocketWrapper *)hsocket.mPtr; + // OpenSSL native file descriptor binding + SSL_set_fd( ssl->s, (int)socket->socket ); +} + +void _hx_ssl_set_hostname( Dynamic hssl, String hostname ){ + sslctx *ssl = val_ssl(hssl); + hx::strbuf buf; + if( SSL_set_tlsext_host_name(ssl->s, hostname.utf8_str(&buf)) != 1 ) + ssl_error("Failed setting hostname SNI"); +} + +Dynamic _hx_ssl_get_peer_certificate( Dynamic hssl ){ + sslctx *ssl = val_ssl(hssl); + X509 *crt = SSL_get_peer_certificate(ssl->s); + if( crt == NULL ) + return null(); + sslcert *cert = new sslcert(); + cert->create( crt ); + return cert; +} + +bool _hx_ssl_get_verify_result( Dynamic hssl ){ + sslctx *ssl = val_ssl(hssl); + long r = SSL_get_verify_result( ssl->s ); + if( r == X509_V_OK ) + return true; + return false; +} + +void _hx_ssl_send_char( Dynamic hssl, int c ) { + if( c < 0 || c > 255 ) + hx::Throw( HX_CSTRING("invalid char") ); + sslctx *ssl = val_ssl(hssl); + const unsigned char cc = c; + int r = SSL_write( ssl->s, &cc, 1 ); + if (r <= 0) ssl_error("Send char failed"); +} + +int _hx_ssl_send( Dynamic hssl, Array<unsigned char> buf, int p, int l ) { + sslctx *ssl = val_ssl(hssl); + int dlen = buf->length; + if( p < 0 || l < 0 || p > dlen || p + l > dlen ) + hx::Throw( HX_CSTRING("ssl_send bounds") ); + + POSIX_LABEL(send_again); + const unsigned char *base = (const unsigned char *)&buf[0]; + + hx::EnterGCFreeZone(); + dlen = SSL_write( ssl->s, base + p, l ); + hx::ExitGCFreeZone(); + + if (dlen <= 0) { + if ( is_ssl_blocking(ssl->s, dlen) ) { + HANDLE_EINTR(send_again); + hx::Throw(HX_CSTRING("Blocking")); + } else { + hx::Throw(HX_CSTRING("ssl network error")); + } + } + return dlen; +} + +void _hx_ssl_write( Dynamic hssl, Array<unsigned char> buf ) { + sslctx *ssl = val_ssl(hssl); + int len = buf->length; + unsigned char *cdata = &buf[0]; + while( len > 0 ) { + POSIX_LABEL( write_again ); + + hx::EnterGCFreeZone(); + int slen = SSL_write( ssl->s, cdata, len ); + hx::ExitGCFreeZone(); + + if (slen <= 0) { + if ( is_ssl_blocking(ssl->s, slen) ) { + HANDLE_EINTR( write_again ); + hx::Throw(HX_CSTRING("Blocking")); + } else { + hx::Throw(HX_CSTRING("ssl network error")); + } + } + cdata += slen; + len -= slen; + } +} + +int _hx_ssl_recv_char( Dynamic hssl ) { + sslctx *ssl = val_ssl(hssl); + unsigned char cc; + int r = SSL_read( ssl->s, &cc, 1 ); + if( r <= 0 ) + hx::Throw( HX_CSTRING("ssl_recv_char") ); + return (int)cc; +} + +int _hx_ssl_recv( Dynamic hssl, Array<unsigned char> buf, int p, int l ) { + sslctx *ssl = val_ssl(hssl); + int dlen = buf->length; + if( p < 0 || l < 0 || p > dlen || p + l > dlen ) + hx::Throw( HX_CSTRING("ssl_recv bounds") ); + + unsigned char *base = &buf[0]; + POSIX_LABEL(recv_again); + + hx::EnterGCFreeZone(); + dlen = SSL_read( ssl->s, base + p, l ); + hx::ExitGCFreeZone(); + + if (dlen <= 0) { + int err = SSL_get_error(ssl->s, dlen); + if (err == SSL_ERROR_ZERO_RETURN) { // Peer closed connection + return 0; + } else if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { + HANDLE_EINTR(recv_again); + hx::Throw(HX_CSTRING("Blocking")); + } else { + hx::Throw(HX_CSTRING("ssl network error")); + } + } + return dlen; +} + +Array<unsigned char> _hx_ssl_read( Dynamic hssl ) { + sslctx *ssl = val_ssl(hssl); + Array<unsigned char> result = Array_obj<unsigned char>::__new(); + unsigned char buf[256]; + + while( true ) { + POSIX_LABEL(read_again); + + hx::EnterGCFreeZone(); + int len = SSL_read( ssl->s, buf, 256 ); + hx::ExitGCFreeZone(); + + if (len <= 0) { + int err = SSL_get_error(ssl->s, len); + if (err == SSL_ERROR_ZERO_RETURN) { + break; // Graceful shutdown + } else if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { + HANDLE_EINTR(read_again); + hx::Throw(HX_CSTRING("Blocking")); + } else { + hx::Throw(HX_CSTRING("ssl network error")); + } + } + result->memcpy(result->length, buf, len); + } + return result; +} + +Dynamic _hx_ssl_conf_new( bool server ) { + sslconf *conf = new sslconf(); + conf->create(server); + if( !conf->c ){ + conf->destroy(); + ssl_error("SSL_CTX_new failed"); + } + return conf; +} + +void _hx_ssl_conf_close( Dynamic hconf ) { + sslconf *conf = val_conf(hconf); + conf->destroy(); +} + +void _hx_ssl_conf_set_ca( Dynamic hconf, Dynamic hcert ) { + sslconf *conf = val_conf(hconf); + if( hcert.mPtr ){ + sslcert *cert = val_cert(hcert); + X509_STORE *store = SSL_CTX_get_cert_store(conf->c); + for(int i=0; i < sk_X509_num(cert->chain); i++) { + X509_STORE_add_cert(store, sk_X509_value(cert->chain, i)); + } + } +} + +void _hx_ssl_conf_set_verify( Dynamic hconf, int mode ) { + sslconf *conf = val_conf(hconf); + if( mode == 2 ) // Optional + SSL_CTX_set_verify(conf->c, SSL_VERIFY_PEER, NULL); + else if( mode == 1 ) // Required + SSL_CTX_set_verify(conf->c, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); + else + SSL_CTX_set_verify(conf->c, SSL_VERIFY_NONE, NULL); +} + +void _hx_ssl_conf_set_cert( Dynamic hconf, Dynamic hcert, Dynamic hpkey ) { + sslconf *conf = val_conf(hconf); + sslcert *cert = val_cert(hcert); + sslpkey *pkey = val_pkey(hpkey); + + if (SSL_CTX_use_certificate(conf->c, cert->get_current()) != 1) + ssl_error("Failed to use certificate"); + + if (SSL_CTX_use_PrivateKey(conf->c, pkey->k) != 1) + ssl_error("Failed to use private key"); + + if (SSL_CTX_check_private_key(conf->c) != 1) + ssl_error("Private key does not match certificate"); +} + +// Callback for Server Name Indication (SNI) +static int sni_callback(SSL *s, int *al, void *arg) { + const char *servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name); + if( servername && arg ){ + Dynamic cb = new Dynamic(); + cb.mPtr = (hx::Object*)arg; + Dynamic ret = cb->__run( String(servername) ); + if( ret != null() ){ + Dynamic hcert = ret->__Field(HX_CSTRING("cert"), hx::paccDynamic); + Dynamic hpkey = ret->__Field(HX_CSTRING("key"), hx::paccDynamic); + sslcert *cert = val_cert(hcert); + sslpkey *pk = val_pkey(hpkey); + + SSL_use_certificate(s, cert->get_current()); + SSL_use_PrivateKey(s, pk->k); + return SSL_TLSEXT_ERR_OK; + } + } + return SSL_TLSEXT_ERR_NOACK; +} + +void _hx_ssl_conf_set_servername_callback( Dynamic hconf, Dynamic cb ){ + sslconf *conf = val_conf(hconf); + SSL_CTX_set_tlsext_servername_callback(conf->c, sni_callback); + SSL_CTX_set_tlsext_servername_arg(conf->c, (void*)cb.mPtr); +} + +Dynamic _hx_ssl_cert_load_defaults(){ +#if defined(NEKO_WINDOWS) + HCERTSTORE store; + PCCERT_CONTEXT cert = NULL; + sslcert *chain = NULL; + if( store = CertOpenSystemStore(0, (LPCSTR)"Root") ){ + while( cert = CertEnumCertificatesInStore(store, cert) ){ + if( chain == NULL ){ + chain = new sslcert(); + chain->create( NULL ); + } + const unsigned char *p = (const unsigned char *)cert->pbCertEncoded; + X509 *x = d2i_X509(NULL, &p, cert->cbCertEncoded); + if (x) sk_X509_push(chain->chain, x); + } + CertCloseStore(store, 0); + } + if( chain != NULL ) + return chain; +#elif defined(NEKO_MAC) && !defined(IPHONE) && !defined(APPLETV) + CFMutableDictionaryRef search; + CFArrayRef result; + SecKeychainRef keychain; + SecCertificateRef item; + CFDataRef dat; + sslcert *chain = NULL; + + if( SecKeychainOpen("/System/Library/Keychains/SystemRootCertificates.keychain",&keychain) != errSecSuccess ) + return null(); + + search = CFDictionaryCreateMutable( NULL, 0, NULL, NULL ); + CFDictionarySetValue( search, kSecClass, kSecClassCertificate ); + CFDictionarySetValue( search, kSecMatchLimit, kSecMatchLimitAll ); + CFDictionarySetValue( search, kSecReturnRef, kCFBooleanTrue ); + CFDictionarySetValue( search, kSecMatchSearchList, CFArrayCreate(NULL, (const void **)&keychain, 1, NULL) ); + + if( SecItemCopyMatching( search, (CFTypeRef *)&result ) == errSecSuccess ){ + CFIndex n = CFArrayGetCount( result ); + for( CFIndex i = 0; i < n; i++ ){ + item = (SecCertificateRef)CFArrayGetValueAtIndex( result, i ); + dat = SecCertificateCopyData( item ); + if( dat ){ + if( chain == NULL ){ + chain = new sslcert(); + chain->create( NULL ); + } + const unsigned char *p = (const unsigned char *)CFDataGetBytePtr(dat); + X509 *x = d2i_X509(NULL, &p, CFDataGetLength(dat)); + if(x) sk_X509_push(chain->chain, x); + CFRelease( dat ); + } + } + } + CFRelease(keychain); + if( chain != NULL ) + return chain; +#endif + return null(); +} + +Dynamic _hx_ssl_cert_load_file( String file ){ + sslcert *cert = new sslcert(); + cert->create( NULL ); + hx::strbuf buf; + FILE *fp = fopen(file.utf8_str(&buf), "r"); + if (!fp) { + cert->destroy(); + hx::Throw(HX_CSTRING("File not found")); + } + + X509 *x; + while ((x = PEM_read_X509(fp, NULL, NULL, NULL)) != NULL) { + sk_X509_push(cert->chain, x); + } + fclose(fp); + + if(sk_X509_num(cert->chain) == 0) { + cert->destroy(); + ssl_error("Failed reading cert file"); + } + return cert; +} + +Dynamic _hx_ssl_cert_load_path( String path ){ + // Usually implemented by loading all certs in a directory. OpenSSL handles this differently. + // For exact match of functionality, you could use X509_LOOKUP_hash_dir + hx::Throw( HX_CSTRING("Path loading needs explicit implementation in OpenSSL") ); + return null(); +} + +String _hx_ssl_cert_get_subject( Dynamic hcert, String objname ){ + sslcert *cert = val_cert(hcert); + X509 *c = cert->get_current(); + if (!c) hx::Throw( HX_CSTRING("cert_get_subject") ); + + hx::strbuf buf; + int nid = OBJ_txt2nid(objname.utf8_str(&buf)); + X509_NAME *subj = X509_get_subject_name(c); + int loc = X509_NAME_get_index_by_NID(subj, nid, -1); + if (loc == -1) return String(); + + X509_NAME_ENTRY *entry = X509_NAME_get_entry(subj, loc); + ASN1_STRING *data = X509_NAME_ENTRY_get_data(entry); + + return String((const char*)ASN1_STRING_get0_data(data), ASN1_STRING_length(data)); +} + +String _hx_ssl_cert_get_issuer( Dynamic hcert, String objname ){ + sslcert *cert = val_cert(hcert); + X509 *c = cert->get_current(); + if (!c) hx::Throw( HX_CSTRING("cert_get_issuer") ); + + hx::strbuf buf; + int nid = OBJ_txt2nid(objname.utf8_str(&buf)); + X509_NAME *issuer = X509_get_issuer_name(c); + int loc = X509_NAME_get_index_by_NID(issuer, nid, -1); + if (loc == -1) return String(); + + X509_NAME_ENTRY *entry = X509_NAME_get_entry(issuer, loc); + ASN1_STRING *data = X509_NAME_ENTRY_get_data(entry); + + return String((const char*)ASN1_STRING_get0_data(data), ASN1_STRING_length(data)); +} + +Array<String> _hx_ssl_cert_get_altnames( Dynamic hcert ){ + sslcert *cert = val_cert(hcert); + X509 *c = cert->get_current(); + Array<String> result(0,1); + + STACK_OF(GENERAL_NAME) *altnames = (STACK_OF(GENERAL_NAME)*)X509_get_ext_d2i(c, NID_subject_alt_name, NULL, NULL); + if (altnames) { + int num = sk_GENERAL_NAME_num(altnames); + for (int i = 0; i < num; ++i) { + GENERAL_NAME *val = sk_GENERAL_NAME_value(altnames, i); + if (val->type == GEN_DNS) { + result.Add(String((const char*)ASN1_STRING_get0_data(val->d.dNSName), ASN1_STRING_length(val->d.dNSName))); + } + } + sk_GENERAL_NAME_pop_free(altnames, GENERAL_NAME_free); + } + return result; +} + +static Array<int> asn1_time_to_array( const ASN1_TIME *time ){ + Array<int> result(6,6); + struct tm tm_info; + + if (ASN1_TIME_to_tm(time, &tm_info)) { + result[0] = tm_info.tm_year + 1900; + result[1] = tm_info.tm_mon + 1; + result[2] = tm_info.tm_mday; + result[3] = tm_info.tm_hour; + result[4] = tm_info.tm_min; + result[5] = tm_info.tm_sec; + } + return result; +} + +Array<int> _hx_ssl_cert_get_notbefore( Dynamic hcert ){ + sslcert *cert = val_cert(hcert); + X509 *c = cert->get_current(); + if( !c ) hx::Throw( HX_CSTRING("cert_get_notbefore") ); + return asn1_time_to_array( X509_get0_notBefore(c) ); +} + +Array<int> _hx_ssl_cert_get_notafter( Dynamic hcert ){ + sslcert *cert = val_cert(hcert); + X509 *c = cert->get_current(); + if( !c ) hx::Throw( HX_CSTRING("cert_get_notafter") ); + return asn1_time_to_array( X509_get0_notAfter(c) ); +} + +Dynamic _hx_ssl_cert_get_next( Dynamic hcert ){ + sslcert *cert = val_cert(hcert); + if (cert->current_index + 1 >= sk_X509_num(cert->chain)) { + return null(); + } + sslcert *next_cert = new sslcert(); + next_cert->create(NULL, cert->chain, cert->current_index + 1); + return next_cert; +} + +Dynamic _hx_ssl_cert_add_pem( Dynamic hcert, String data ){ + #ifdef HX_SMART_STRINGS + if (data.isUTF16Encoded()) + hx::Throw( HX_CSTRING("Invalid data encoding") ); + #endif + sslcert *cert = val_cert(hcert); + if( !cert ){ + cert = new sslcert(); + cert->create( NULL ); + } + + BIO *bio = BIO_new_mem_buf(data.raw_ptr(), data.length); + X509 *x; + int added = 0; + while ((x = PEM_read_bio_X509(bio, NULL, NULL, NULL)) != NULL) { + sk_X509_push(cert->chain, x); + added++; + } + BIO_free(bio); + + if( added == 0 ){ + if (cert->current_index == 0) cert->destroy(); + ssl_error("Failed to parse PEM cert"); + } + return cert; +} + +Dynamic _hx_ssl_cert_add_der( Dynamic hcert, Array<unsigned char> buf ){ + sslcert *cert = val_cert(hcert); + if( !cert ){ + cert = new sslcert(); + cert->create( NULL ); + } + + const unsigned char *p = &buf[0]; + X509 *x = d2i_X509(NULL, &p, buf->length); + if( !x ){ + if (cert->current_index == 0) cert->destroy(); + ssl_error("Failed to parse DER cert"); + } + sk_X509_push(cert->chain, x); + return cert; +} + +Dynamic _hx_ssl_key_from_der( Array<unsigned char> buf, bool pub ){ + sslpkey *pk = new sslpkey(); + + const unsigned char *p = &buf[0]; + EVP_PKEY *key = NULL; + if (pub) { + key = d2i_PUBKEY(NULL, &p, buf->length); + } else { + key = d2i_AutoPrivateKey(NULL, &p, buf->length); + } + + if( !key ){ + ssl_error("Failed parsing DER key"); + } + pk->create(key); + return pk; +} + +Dynamic _hx_ssl_key_from_pem( String data, bool pub, String pass ){ + #ifdef HX_SMART_STRINGS + if (data.isUTF16Encoded()) + hx::Throw( HX_CSTRING("Invalid data encoding") ); + #endif + sslpkey *pk = new sslpkey(); + + BIO *bio = BIO_new_mem_buf(data.raw_ptr(), data.length); + EVP_PKEY *key = NULL; + + if (pub) { + key = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL); + } else { + if (pass == null()) { + key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); + } else { + Array<unsigned char> pbytes(0,0); + __hxcpp_bytes_of_string(pbytes,pass); + key = PEM_read_bio_PrivateKey(bio, NULL, NULL, (void*)pbytes->GetBase()); + } + } + BIO_free(bio); + + if( !key ){ + ssl_error("Failed parsing PEM key"); + } + pk->create(key); + return pk; +} + +Array<unsigned char> _hx_ssl_dgst_make( Array<unsigned char> buf, String alg ){ + hx::strbuf ubuf; + const EVP_MD *md = EVP_get_digestbyname(alg.utf8_str(&ubuf)); + if( md == NULL ) hx::Throw( HX_CSTRING("Invalid hash algorithm") ); + + unsigned int size = EVP_MD_size(md); + Array<unsigned char> out = Array_obj<unsigned char>::__new(size,size); + + EVP_MD_CTX *ctx = EVP_MD_CTX_new(); + EVP_DigestInit_ex(ctx, md, NULL); + EVP_DigestUpdate(ctx, &buf[0], buf->length); + EVP_DigestFinal_ex(ctx, &out[0], &size); + EVP_MD_CTX_free(ctx); + + return out; +} + +Array<unsigned char> _hx_ssl_dgst_sign( Array<unsigned char> buf, Dynamic hpkey, String alg ){ + sslpkey *pk = val_pkey(hpkey); + hx::strbuf ubuf; + const EVP_MD *md = EVP_get_digestbyname( alg.utf8_str(&ubuf) ); + if( md == NULL ) hx::Throw( HX_CSTRING("Invalid hash algorithm") ); + + EVP_MD_CTX *ctx = EVP_MD_CTX_new(); + if( EVP_DigestSignInit(ctx, NULL, md, NULL, pk->k) <= 0 ) { + EVP_MD_CTX_free(ctx); + ssl_error("DigestSignInit failed"); + } + + size_t req_len = 0; + EVP_DigestSign(ctx, NULL, &req_len, &buf[0], buf->length); + + Array<unsigned char> result = Array_obj<unsigned char>::__new(req_len, req_len); + if( EVP_DigestSign(ctx, &result[0], &req_len, &buf[0], buf->length) <= 0 ) { + EVP_MD_CTX_free(ctx); + ssl_error("DigestSign failed"); + } + + EVP_MD_CTX_free(ctx); + result->__SetSize(req_len); + return result; +} + +bool _hx_ssl_dgst_verify( Array<unsigned char> buf, Array<unsigned char> sign, Dynamic hpkey, String alg ){ + sslpkey *pk = val_pkey(hpkey); + hx::strbuf ubuf; + const EVP_MD *md = EVP_get_digestbyname( alg.utf8_str(&ubuf) ); + if( md == NULL ) hx::Throw( HX_CSTRING("Invalid hash algorithm") ); + + EVP_MD_CTX *ctx = EVP_MD_CTX_new(); + if( EVP_DigestVerifyInit(ctx, NULL, md, NULL, pk->k) <= 0 ) { + EVP_MD_CTX_free(ctx); + return false; + } + + int r = EVP_DigestVerify(ctx, &sign[0], sign->length, &buf[0], buf->length); + EVP_MD_CTX_free(ctx); + + return r == 1; +} + +static bool _hx_ssl_inited = false; +void _hx_ssl_init() { + if (_hx_ssl_inited) return; + _hx_ssl_inited = true; +}