/* * ruby bindings for xyssl * Yoann Guillot - 01/2007 * * compile with: * XXX ugly hack * ruby extconf.rb * vim Makefile => add xyssl.a to LDFLAGS * C_INCLUDE_DIR=/path/to/xyssl/src make * * usage: * * require 'xyssl' * puts XYSSL::MD5.hexdigest('foobar') * * require 'socket' * TCPSocket.open('www.google.com', 443) { |s| * ssl = XYSSL::sslize(s) * ssl.puts 'GET / HTTP/1.1', 'Host: www.google.com:443', '' * puts ssl.read * ssl.close * } */ #include #include #ifndef _CRT_SECURE_NO_DEPRECATE #define _CRT_SECURE_NO_DEPRECATE 1 #endif #include #include "md5.h" #include "havege.h" #include "testcert.h" #include "ssl_v3.h" #include "x509.h" static VALUE rb_md5_digest(VALUE self, VALUE str) { uchar digest[16]; md5_csum((uchar*)RSTRING(str)->ptr, RSTRING(str)->len, digest); return rb_str_new((char*)digest, 16); } static VALUE rb_md5_hexdigest(VALUE self, VALUE str) { int i; uchar digest[16]; char hexdigest[33]; md5_csum((uchar*)RSTRING(str)->ptr, RSTRING(str)->len, digest); for (i=0 ; i < 16 ; i++) snprintf(hexdigest+2*i, 3, "%02X", digest[i]); return rb_str_new(hexdigest, 32); } #define WRAP(strct, val) VALUE val; do { strct foobar; val = rb_str_new((char *)&foobar, sizeof(foobar)); } while(0) #define WRAPED(strct, val) ((strct*)RSTRING(val)->ptr) static VALUE rb_xysslread(VALUE self) { int ret; unsigned len; uchar buf[1024]; ssl_context *ctx = WRAPED(ssl_context, rb_iv_get(self, "@xyssl_ctx")); len = sizeof(buf)-1; ret = ssl_read(ctx, buf, &len, 0); if (ret) { ssl_close(ctx); rb_raise(rb_eRuntimeError, "XYSSL: ssl_read failed with error %08x", ret); } return rb_str_new((char*)buf, len); } static VALUE rb_xysslwrite(VALUE self, VALUE data) { int ret; ssl_context *ctx = WRAPED(ssl_context, rb_iv_get(self, "@xyssl_ctx")); ret = ssl_write(ctx, (uchar*)RSTRING(data)->ptr, RSTRING(data)->len); if (ret) { ssl_close(ctx); rb_raise(rb_eRuntimeError, "XYSSL: ssl_write failed with error %08x", ret); } return INT2FIX(ret); } static VALUE rb_xysslclose(VALUE self) { ssl_close(WRAPED(ssl_context, rb_iv_get(self, "@xyssl_ctx"))); return Qnil; } static uint ciphers[] = { TLS1_RSA_AES_256_SHA, SSL3_RSA_DES_192_SHA, SSL3_RSA_RC4_128_SHA, 0 }; static x509_cert cacert; static VALUE rb_sslize(VALUE self, VALUE targetio) { int ret; int fd; VALUE sslio = rb_obj_dup(targetio); VALUE metaclass = rb_singleton_class(sslio); WRAP(havege_state, val_hs); WRAP(ssl_context, val_ctx); ssl_context *ctx = WRAPED(ssl_context, val_ctx); memset(&cacert, 0, sizeof(cacert)); if ((ret = x509_add_certs(&cacert, (uchar*)xyssl_ca_crt, strlen(xyssl_ca_crt)))) rb_raise(rb_eRuntimeError, "XYSSL: x509_add_certs failed with error %08x", ret); havege_init(WRAPED(havege_state, val_hs)); if ((ret = ssl_init(ctx, ciphers, havege_rand, WRAPED(havege_state, val_hs)))) rb_raise(rb_eRuntimeError, "XYSSL: ssl_init failed with error %08x", ret); OpenFile *fptr; GetOpenFile(sslio, fptr); fd = fileno(fptr->f); ssl_set_io_files(ctx, fd, fd); ssl_set_ca_chain(ctx, &cacert, "www.google.com"); if ((ret = ssl_client_start(ctx, SSL_VERIFY_OPTIONAL))) { ssl_close(ctx); rb_raise(rb_eRuntimeError, "XYSSL: ssl_client_start failed with error %08x", ret); } // check x509 rb_iv_set(sslio, "@xyssl_ctx", val_ctx); rb_iv_set(sslio, "@xyssl_hs", val_hs); rb_define_method(metaclass, "read", rb_xysslread, 1); rb_define_method(metaclass, "write", rb_xysslwrite, 1); rb_define_method(metaclass, "close", rb_xysslclose, 1); return ret; } void Init_xyssl(void) { VALUE rb_mXYSSL, rb_mSSL; VALUE rb_cMD5, rb_cSSLIO; rb_mXYSSL = rb_define_module("XYSSL"); rb_mSSL = rb_define_module_under(rb_mXYSSL, "SSL"); rb_cMD5 = rb_define_class_under(rb_mXYSSL, "MD5", rb_cObject); rb_cSSLIO = rb_define_class_under(rb_mSSL, "IO", rb_cObject); rb_define_singleton_method(rb_cMD5, "digest", rb_md5_digest, 1); rb_define_singleton_method(rb_cMD5, "hexdigest", rb_md5_hexdigest, 1); rb_define_singleton_method(rb_mXYSSL, "sslize", rb_sslize, 1); }