From 231247c010acba191b78ed2d1310c935e63ad919 Mon Sep 17 00:00:00 2001 From: gotoyuzo Date: Wed, 23 Jul 2003 16:12:24 +0000 Subject: * ext/openssl: imported. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@4128 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ext/openssl/MANIFEST | 62 +++ ext/openssl/extconf.rb | 132 ++++++ ext/openssl/lib/net/ftptls.rb | 43 ++ ext/openssl/lib/net/https.rb | 179 ++++++++ ext/openssl/lib/net/protocols.rb | 57 +++ ext/openssl/lib/net/telnets.rb | 250 +++++++++++ ext/openssl/lib/openssl.rb | 24 ++ ext/openssl/lib/openssl/bn.rb | 35 ++ ext/openssl/lib/openssl/buffering.rb | 189 +++++++++ ext/openssl/lib/openssl/cipher.rb | 52 +++ ext/openssl/lib/openssl/digest.rb | 44 ++ ext/openssl/lib/openssl/ssl.rb | 38 ++ ext/openssl/lib/openssl/x509.rb | 132 ++++++ ext/openssl/openssl_missing.c | 279 +++++++++++++ ext/openssl/openssl_missing.h | 105 +++++ ext/openssl/ossl.c | 567 +++++++++++++++++++++++++ ext/openssl/ossl.h | 195 +++++++++ ext/openssl/ossl_bn.c | 734 +++++++++++++++++++++++++++++++++ ext/openssl/ossl_bn.h | 22 + ext/openssl/ossl_cipher.c | 377 +++++++++++++++++ ext/openssl/ossl_cipher.h | 22 + ext/openssl/ossl_config.c | 152 +++++++ ext/openssl/ossl_config.h | 20 + ext/openssl/ossl_digest.c | 289 +++++++++++++ ext/openssl/ossl_digest.h | 22 + ext/openssl/ossl_hmac.c | 222 ++++++++++ ext/openssl/ossl_hmac.h | 19 + ext/openssl/ossl_ns_spki.c | 232 +++++++++++ ext/openssl/ossl_ns_spki.h | 21 + ext/openssl/ossl_ocsp.c | 765 ++++++++++++++++++++++++++++++++++ ext/openssl/ossl_ocsp.h | 24 ++ ext/openssl/ossl_pkcs7.c | 775 +++++++++++++++++++++++++++++++++++ ext/openssl/ossl_pkcs7.h | 22 + ext/openssl/ossl_pkey.c | 238 +++++++++++ ext/openssl/ossl_pkey.h | 111 +++++ ext/openssl/ossl_pkey_dh.c | 386 +++++++++++++++++ ext/openssl/ossl_pkey_dsa.c | 404 ++++++++++++++++++ ext/openssl/ossl_pkey_rsa.c | 515 +++++++++++++++++++++++ ext/openssl/ossl_rand.c | 142 +++++++ ext/openssl/ossl_rand.h | 20 + ext/openssl/ossl_ssl.c | 678 ++++++++++++++++++++++++++++++ ext/openssl/ossl_ssl.h | 21 + ext/openssl/ossl_version.h | 16 + ext/openssl/ossl_x509.c | 95 +++++ ext/openssl/ossl_x509.h | 113 +++++ ext/openssl/ossl_x509attr.c | 152 +++++++ ext/openssl/ossl_x509cert.c | 691 +++++++++++++++++++++++++++++++ ext/openssl/ossl_x509crl.c | 551 +++++++++++++++++++++++++ ext/openssl/ossl_x509ext.c | 345 ++++++++++++++++ ext/openssl/ossl_x509name.c | 234 +++++++++++ ext/openssl/ossl_x509req.c | 449 ++++++++++++++++++++ ext/openssl/ossl_x509revoked.c | 230 +++++++++++ ext/openssl/ossl_x509store.c | 561 +++++++++++++++++++++++++ ext/openssl/ruby_missing.h | 70 ++++ ext/openssl/sample/c_rehash.rb | 174 ++++++++ ext/openssl/sample/cipher.rb | 29 ++ ext/openssl/sample/echo_cli.rb | 36 ++ ext/openssl/sample/echo_svr.rb | 64 +++ ext/openssl/sample/gen_csr.rb | 52 +++ ext/openssl/sample/smime_read.rb | 23 ++ ext/openssl/sample/smime_write.rb | 23 ++ ext/openssl/sample/wget.rb | 33 ++ 62 files changed, 12557 insertions(+) create mode 100644 ext/openssl/MANIFEST create mode 100644 ext/openssl/extconf.rb create mode 100644 ext/openssl/lib/net/ftptls.rb create mode 100644 ext/openssl/lib/net/https.rb create mode 100644 ext/openssl/lib/net/protocols.rb create mode 100644 ext/openssl/lib/net/telnets.rb create mode 100644 ext/openssl/lib/openssl.rb create mode 100644 ext/openssl/lib/openssl/bn.rb create mode 100644 ext/openssl/lib/openssl/buffering.rb create mode 100644 ext/openssl/lib/openssl/cipher.rb create mode 100644 ext/openssl/lib/openssl/digest.rb create mode 100644 ext/openssl/lib/openssl/ssl.rb create mode 100644 ext/openssl/lib/openssl/x509.rb create mode 100644 ext/openssl/openssl_missing.c create mode 100644 ext/openssl/openssl_missing.h create mode 100644 ext/openssl/ossl.c create mode 100644 ext/openssl/ossl.h create mode 100644 ext/openssl/ossl_bn.c create mode 100644 ext/openssl/ossl_bn.h create mode 100644 ext/openssl/ossl_cipher.c create mode 100644 ext/openssl/ossl_cipher.h create mode 100644 ext/openssl/ossl_config.c create mode 100644 ext/openssl/ossl_config.h create mode 100644 ext/openssl/ossl_digest.c create mode 100644 ext/openssl/ossl_digest.h create mode 100644 ext/openssl/ossl_hmac.c create mode 100644 ext/openssl/ossl_hmac.h create mode 100644 ext/openssl/ossl_ns_spki.c create mode 100644 ext/openssl/ossl_ns_spki.h create mode 100644 ext/openssl/ossl_ocsp.c create mode 100644 ext/openssl/ossl_ocsp.h create mode 100644 ext/openssl/ossl_pkcs7.c create mode 100644 ext/openssl/ossl_pkcs7.h create mode 100644 ext/openssl/ossl_pkey.c create mode 100644 ext/openssl/ossl_pkey.h create mode 100644 ext/openssl/ossl_pkey_dh.c create mode 100644 ext/openssl/ossl_pkey_dsa.c create mode 100644 ext/openssl/ossl_pkey_rsa.c create mode 100644 ext/openssl/ossl_rand.c create mode 100644 ext/openssl/ossl_rand.h create mode 100644 ext/openssl/ossl_ssl.c create mode 100644 ext/openssl/ossl_ssl.h create mode 100644 ext/openssl/ossl_version.h create mode 100644 ext/openssl/ossl_x509.c create mode 100644 ext/openssl/ossl_x509.h create mode 100644 ext/openssl/ossl_x509attr.c create mode 100644 ext/openssl/ossl_x509cert.c create mode 100644 ext/openssl/ossl_x509crl.c create mode 100644 ext/openssl/ossl_x509ext.c create mode 100644 ext/openssl/ossl_x509name.c create mode 100644 ext/openssl/ossl_x509req.c create mode 100644 ext/openssl/ossl_x509revoked.c create mode 100644 ext/openssl/ossl_x509store.c create mode 100644 ext/openssl/ruby_missing.h create mode 100644 ext/openssl/sample/c_rehash.rb create mode 100644 ext/openssl/sample/cipher.rb create mode 100644 ext/openssl/sample/echo_cli.rb create mode 100644 ext/openssl/sample/echo_svr.rb create mode 100644 ext/openssl/sample/gen_csr.rb create mode 100644 ext/openssl/sample/smime_read.rb create mode 100644 ext/openssl/sample/smime_write.rb create mode 100644 ext/openssl/sample/wget.rb (limited to 'ext/openssl') diff --git a/ext/openssl/MANIFEST b/ext/openssl/MANIFEST new file mode 100644 index 0000000000..e19bf9d91e --- /dev/null +++ b/ext/openssl/MANIFEST @@ -0,0 +1,62 @@ +MANIFEST +extconf.rb +lib/net/ftptls.rb +lib/net/https.rb +lib/net/protocols.rb +lib/net/telnets.rb +lib/openssl.rb +lib/openssl/bn.rb +lib/openssl/buffering.rb +lib/openssl/cipher.rb +lib/openssl/digest.rb +lib/openssl/ssl.rb +lib/openssl/x509.rb +openssl_missing.c +openssl_missing.h +ossl.c +ossl.h +ossl_bn.c +ossl_bn.h +ossl_cipher.c +ossl_cipher.h +ossl_config.c +ossl_config.h +ossl_digest.c +ossl_digest.h +ossl_hmac.c +ossl_hmac.h +ossl_ns_spki.c +ossl_ns_spki.h +ossl_ocsp.c +ossl_ocsp.h +ossl_pkcs7.c +ossl_pkcs7.h +ossl_pkey.c +ossl_pkey.h +ossl_pkey_dh.c +ossl_pkey_dsa.c +ossl_pkey_rsa.c +ossl_rand.c +ossl_rand.h +ossl_ssl.c +ossl_ssl.h +ossl_version.h +ossl_x509.c +ossl_x509.h +ossl_x509attr.c +ossl_x509cert.c +ossl_x509crl.c +ossl_x509ext.c +ossl_x509name.c +ossl_x509req.c +ossl_x509revoked.c +ossl_x509store.c +ruby_missing.h +sample/c_rehash.rb +sample/cipher.rb +sample/echo_cli.rb +sample/echo_svr.rb +sample/gen_csr.rb +sample/smime_read.rb +sample/smime_write.rb +sample/wget.rb diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb new file mode 100644 index 0000000000..0b028b7b8a --- /dev/null +++ b/ext/openssl/extconf.rb @@ -0,0 +1,132 @@ +=begin += $RCSfile$ -- Generator for Makefile + += Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2002 Michal Rokos + All rights reserved. + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + += Version + $Id$ +=end + +require "mkmf" + +if RUBY_PLATFORM =~ /mswin32/ + CRYPTOLIB="libeay32" + SSLLIB="ssleay32" +else + CRYPTOLIB="crypto" + SSLLIB="ssl" +end + +if !defined? message + def message(*s) + printf(*s) + Logging::message(*s) + end +end + +includes, = dir_config("openssl") +includes ||= "/usr/include" + +message "=== OpenSSL for Ruby configurator ===\n" + +## +# Adds -Wall -DOSSL_DEBUG for compilation and some more targets when GCC is used +# To turn it on, use: --with-debug or --enable-debug +# +if with_config("debug") or enable_config("debug") + $defs.push("-DOSSL_DEBUG") unless $defs.include? "-DOSSL_DEBUG" + $CPPFLAGS += " -Wall" unless $CPPFLAGS.split.include? "-Wall" + + if CONFIG["CC"] =~ /gcc/ + srcs = [] + for f in Dir[File.join($srcdir, "*.c")] + srcs.push File.basename(f) + end + srcs = srcs.join(" ") + + $distcleanfiles << "dep" if defined? $distcleanfiles + + File.open(File.join($srcdir, "depend"), "w") {|f| + f.print < dep + +include dep +EOD + } + File.open(File.join($srcdir, "dep"), "w").close + end +end + + +def have_openssl_097(inc_dir) +# FIXME: +# checking_for("OpenSSL >= 0.9.7") do + printf "checking for OpenSSL version... " + File.open(inc_dir+"/openssl/opensslv.h") {|f| + txt = f.read + puts (txt.grep(/#define SHLIB_VERSION_NUMBER/)[0].split '"')[1] + true + } +end + +message "=== Checking for required stuff... ===\n" + +result = have_header("openssl/crypto.h") +result &= have_library(CRYPTOLIB, "OpenSSL_add_all_digests") +result &= have_library(SSLLIB, "SSL_library_init") + +if !result + message "=== Checking for required stuff failed. ===\n" + message "Makefile wasn't created. Fix the errors above.\n" + exit 1 +end + +message "=== Checking for system dependent stuff... ===\n" +have_header("unistd.h") +have_header("sys/time.h") + +message "=== Checking for OpenSSL features... ===\n" +have_openssl_097(includes) +have_func("HMAC_CTX_copy") +have_func("X509_STORE_get_ex_data") +have_func("X509_STORE_set_ex_data") +have_func("EVP_MD_CTX_create") +have_func("EVP_MD_CTX_cleanup") +have_func("EVP_MD_CTX_destroy") +have_func("PEM_def_callback") +have_func("EVP_MD_CTX_init") +have_func("HMAC_CTX_init") +have_func("HMAC_CTX_cleanup") +have_func("X509_CRL_set_version") +have_func("X509_CRL_set_issuer_name") +have_func("X509_CRL_sort") +have_func("X509_CRL_add0_revoked") +have_func("CONF_get1_default_config_file") +have_func("BN_mod_sqr") +have_func("BN_mod_add") +have_func("BN_mod_sub") +have_func("CONF_get1_default_config_file") +have_header("openssl/ocsp.h") +have_struct_member("EVP_CIPHER_CTX", "flags", "openssl/evp.h") + +message "=== Checking for Ruby features... ===\n" +have_func("rb_obj_init_copy", "ruby.h") + +message "=== Checking done. ===\n" +create_makefile("openssl") +message "Done.\n" diff --git a/ext/openssl/lib/net/ftptls.rb b/ext/openssl/lib/net/ftptls.rb new file mode 100644 index 0000000000..f433457923 --- /dev/null +++ b/ext/openssl/lib/net/ftptls.rb @@ -0,0 +1,43 @@ +=begin += $RCSfile$ -- SSL/TLS enhancement for Net::HTTP. + += Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2003 Blaz Grilc + All rights reserved. + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + += Requirements + += Version + $Id$ + += Notes + Tested on FreeBSD 5-CURRENT and 4-STABLE + - ruby 1.6.8 (2003-01-17) [i386-freebsd5] + - OpenSSL 0.9.7a Feb 19 2003 + - ruby-openssl-0.2.0.p0 + tested on ftp server: glftpd 1.30 +=end + +require 'socket' +require 'openssl' +require 'net/ftp' + +module Net + class FTPTLS < FTP + def login(user = "anonymous", passwd = nil, acct = nil) + ctx = OpenSSL::SSL::SSLContext.new('SSLv23') + ctx.key = nil + ctx.cert = nil + voidcmd("AUTH TLS") + @sock = OpenSSL::SSL::SSLSocket.new(@sock, ctx) + @sock.connect + super(user, passwd, acct) + voidcmd("PBSZ 0") + end + end +end diff --git a/ext/openssl/lib/net/https.rb b/ext/openssl/lib/net/https.rb new file mode 100644 index 0000000000..0f5cb13407 --- /dev/null +++ b/ext/openssl/lib/net/https.rb @@ -0,0 +1,179 @@ +=begin += $RCSfile$ -- SSL/TLS enhancement for Net::HTTP. + += Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2001 GOTOU Yuuzou + All rights reserved. + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + += Requirements + This program requires Net 1.2.0 or higher version. + You can get it from RAA or Ruby's CVS repository. + += Version + $Id$ + + 2001/11/06: Contiributed to Ruby/OpenSSL project. + +== class Net::HTTP + +== Example + +Simple HTTP client is here: + + require 'net/http' + host, port, path = "localhost", 80, "/" + if %r!http://(.*?)(?::(\d+))?(/.*)! =~ ARGV[0] + host = $1 + port = $2.to_i if $2 + path = $3 + end + h = Net::HTTP.new(host, port) + h.get2(path){ |resp| print resp.body } + +It can be replaced by follow one: + + require 'net/https' + host, port, path = "localhost", 80, "/" + if %r!(https?)://(.*?)(?::(\d+))?(/.*)! =~ ARGV[0] + scheme = $1 + host = $2 + port = $3 ? $3.to_i : ((scheme == "http") ? 80 : 443) + path = $4 + end + h = Net::HTTP.new(host, port) + h.use_ssl = true if scheme == "https" # enable SSL/TLS + h.get2(path){ |resp| print resp.body } + +=== Instance Methods + +: use_ssl + returns ture if use SSL/TLS with HTTP. + +: use_ssl=((|true_or_false|)) + sets use_ssl. + +: peer_cert + return the X.509 certificates the server presented. + +: key=((|key|)) + Sets an OpenSSL::PKey::RSA or OpenSSL::PKey::DSA object. + (This method is appeared in Michal Rokos's OpenSSL extention.) + +: key_file=((|path|)) + Sets a private key file to use in PEM format. + +: cert=((|cert|)) + Sets an OpenSSL::X509::Certificate object as client certificate. + (This method is appeared in Michal Rokos's OpenSSL extention.) + +: cert_file=((|path|)) + Sets pathname of a X.509 certification file in PEM format. + +: ca_file=((|path|)) + Sets path of a CA certification file in PEM format. + The file can contrain several CA certificats. + +: ca_path=((|path|)) + Sets path of a CA certification directory containing certifications + in PEM format. + +: verify_mode=((|mode|)) + Sets the flags for server the certification verification at + begining of SSL/TLS session. + OpenSSL::SSL::VERIFY_NONE or OpenSSL::SSL::VERIFY_PEER is acceptable. + +: verify_callback=((|proc|)) + Sets the verify callback for the server certification verification. + +: verify_depth=((|num|)) + Sets the maximum depth for the certificate chain verification. + +: cert_store=((|store|)) + Sets the X509::Store to verify peer certificate. + +=end + +require 'net/protocols' +require 'net/http' + +module Net + class HTTP + class Conn < HTTPRequest + REQUEST_HAS_BODY=false + RESPONSE_HAS_BODY=false + METHOD="connect" + + def initialize + super nil, nil + end + + def exec( sock, addr, port, ver ) + @socket = sock + request(addr, port, ver) + end + + def request( addr, port, ver ) + @socket.writeline sprintf('CONNECT %s:%s HTTP/%s', addr, port, ver) + @socket.writeline '' + end + end + + module ProxyMod + def edit_path( path ) + if use_ssl + 'https://' + addr_port + path + else + 'http://' + addr_port + path + end + end + end + + def self.socket_type + SSLIO + end + + attr_accessor :use_ssl + attr_writer :key, :cert + attr_writer :ca_file, :ca_path + attr_writer :verify_mode, :verify_callback, :verify_depth + attr_writer :cert_store, :timeout + attr_reader :peer_cert + + alias :default_initialize :initialize + + def initialize(*args) + default_initialize(*args) + @key = @cert = @ca_file = @ca_path = @verify_mode = + @verify_callback = @verify_depth = @timeout = @cert_store = nil + end + + def on_connect + if use_ssl + if proxy? + Conn.new.exec(@socket, @address, @port, "1.0") + resp = HTTPResponse.read_new(@socket) + if resp.code != '200' + raise resp.message + end + end + @socket.key = @key if @key + @socket.cert = @cert if @cert + @socket.ca_file = @ca_file + @socket.ca_path = @ca_path + @socket.verify_mode = @verify_mode + @socket.verify_callback = @verify_callback + @socket.verify_depth = @verify_depth + @socket.timeout = @timeout + @socket.cert_store = @cert_store + @socket.ssl_connect + @peer_cert = @socket.peer_cert + end + end + + end +end diff --git a/ext/openssl/lib/net/protocols.rb b/ext/openssl/lib/net/protocols.rb new file mode 100644 index 0000000000..5897716f3d --- /dev/null +++ b/ext/openssl/lib/net/protocols.rb @@ -0,0 +1,57 @@ +=begin += $RCSfile$ -- SSL/TLS enhancement for Net. + += Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2001 GOTOU YUUZOU + All rights reserved. + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + += Requirements + This program requires Net 1.2.0 or higher version. + You can get it from RAA or Ruby's CVS repository. + += Version + $Id$ + + 2001/11/06: Contiributed to Ruby/OpenSSL project. +=end + +require 'net/protocol' +require 'forwardable' +require 'openssl' + +module Net + class SSLIO < InternetMessageIO + extend Forwardable + + def_delegators(:@ssl_context, + :key=, :cert=, :key_file=, :cert_file=, + :ca_file=, :ca_path=, + :verify_mode=, :verify_callback=, :verify_depth=, + :timeout=, :cert_store=) + + def initialize(addr, port, otime = nil, rtime = nil, dout = nil) + super + @ssl_context = OpenSSL::SSL::SSLContext.new() + end + + def ssl_connect() + @raw_socket = @socket + @socket = OpenSSL::SSL::SSLSocket.new(@raw_socket, @ssl_context) + @socket.connect + end + + def close + super + @raw_socket.close if @raw_socket + end + + def peer_cert + @socket.peer_cert + end + end +end diff --git a/ext/openssl/lib/net/telnets.rb b/ext/openssl/lib/net/telnets.rb new file mode 100644 index 0000000000..c7ecbd717a --- /dev/null +++ b/ext/openssl/lib/net/telnets.rb @@ -0,0 +1,250 @@ +=begin += $RCSfile$ -- SSL/TLS enhancement for Net::Telnet. + += Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2001 GOTOU YUUZOU + All rights reserved. + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + += Version + $Id$ + + 2001/11/06: Contiributed to Ruby/OpenSSL project. + +== class Net::Telnet + +This class will initiate SSL/TLS session automaticaly if the server +sent OPT_STARTTLS. Some options are added for SSL/TLS. + + host = Net::Telnet::new({ + "Host" => "localhost", + "Port" => "telnets", + ## follows are new options. + 'CertFile' => "user.crt", + 'KeyFile' => "user.key", + 'CAFile' => "/some/where/certs/casert.pem", + 'CAPath' => "/some/where/caserts", + 'VerifyMode' => SSL::VERIFY_PEER, + 'VerifyCallback' => verify_proc + }) + +Or, the new options ('Cert', 'Key' and 'CACert') are available from +Michal Rokos's OpenSSL module. + + cert_data = File.open("user.crt"){|io| io.read } + pkey_data = File.open("user.key"){|io| io.read } + cacert_data = File.open("your_ca.pem"){|io| io.read } + host = Net::Telnet::new({ + "Host" => "localhost", + "Port" => "telnets", + 'Cert' => OpenSSL::X509::Certificate.new(cert_data) + 'Key' => OpenSSL::PKey::RSA.new(pkey_data) + 'CACert' => OpenSSL::X509::Certificate.new(cacert_data) + 'CAFile' => "/some/where/certs/casert.pem", + 'CAPath' => "/some/where/caserts", + 'VerifyMode' => SSL::VERIFY_PEER, + 'VerifyCallback' => verify_proc + }) + +This class is expected to be a superset of usual Net::Telnet. +=end + +require "net/telnet" +require "openssl" + +module Net + class Telnet + attr_reader :ssl + + OPT_STARTTLS = 46.chr # "\056" # "\x2e" # Start TLS + TLS_FOLLOWS = 1.chr # "\001" # "\x01" # FOLLOWS (for STARTTLS) + + alias preprocess_orig preprocess + + def ssl?; @ssl; end + + def preprocess(string) + # combine CR+NULL into CR + string = string.gsub(/#{CR}#{NULL}/no, CR) if @options["Telnetmode"] + + # combine EOL into "\n" + string = string.gsub(/#{EOL}/no, "\n") unless @options["Binmode"] + + string.gsub(/#{IAC}( + [#{IAC}#{AO}#{AYT}#{DM}#{IP}#{NOP}]| + [#{DO}#{DONT}#{WILL}#{WONT}][#{OPT_BINARY}-#{OPT_EXOPL}]| + #{SB}[#{OPT_BINARY}-#{OPT_EXOPL}] + (#{IAC}#{IAC}|[^#{IAC}])+#{IAC}#{SE} + )/xno) do + if IAC == $1 # handle escaped IAC characters + IAC + elsif AYT == $1 # respond to "IAC AYT" (are you there) + self.write("nobody here but us pigeons" + EOL) + '' + elsif DO[0] == $1[0] # respond to "IAC DO x" + if OPT_BINARY[0] == $1[1] + @telnet_option["BINARY"] = true + self.write(IAC + WILL + OPT_BINARY) + elsif OPT_STARTTLS[0] == $1[1] + self.write(IAC + WILL + OPT_STARTTLS) + self.write(IAC + SB + OPT_STARTTLS + TLS_FOLLOWS + IAC + SE) + else + self.write(IAC + WONT + $1[1..1]) + end + '' + elsif DONT[0] == $1[0] # respond to "IAC DON'T x" with "IAC WON'T x" + self.write(IAC + WONT + $1[1..1]) + '' + elsif WILL[0] == $1[0] # respond to "IAC WILL x" + if OPT_BINARY[0] == $1[1] + self.write(IAC + DO + OPT_BINARY) + elsif OPT_ECHO[0] == $1[1] + self.write(IAC + DO + OPT_ECHO) + elsif OPT_SGA[0] == $1[1] + @telnet_option["SGA"] = true + self.write(IAC + DO + OPT_SGA) + else + self.write(IAC + DONT + $1[1..1]) + end + '' + elsif WONT[0] == $1[0] # respond to "IAC WON'T x" + if OPT_ECHO[0] == $1[1] + self.write(IAC + DONT + OPT_ECHO) + elsif OPT_SGA[0] == $1[1] + @telnet_option["SGA"] = false + self.write(IAC + DONT + OPT_SGA) + else + self.write(IAC + DONT + $1[1..1]) + end + '' + elsif SB[0] == $1[0] # respond to "IAC SB xxx IAC SE" + if OPT_STARTTLS[0] == $1[1] && TLS_FOLLOWS[0] == $2[0] + @sock = OpenSSL::SSL::SSLSocket.new(@sock) + @sock.cert_file = @options['CertFile'] + @sock.cert = @options['Cert'] unless @sock.cert + @sock.key_file = @options['KeyFile'] + @sock.key = @options['Key'] unless @sock.key + @sock.ca_cert = @options['CACert'] + @sock.ca_file = @options['CAFile'] + @sock.ca_path = @options['CAPath'] + @sock.timeout = @options['Timeout'] + @sock.verify_mode = @options['VerifyMode'] + @sock.verify_callback = @options['VerifyCallback'] + @sock.verify_depth = @options['VerifyDepth'] + @sock.connect + @ssl = true + end + '' + else + '' + end + end + end # preprocess + + alias waitfor_org waitfor + + def waitfor(options) + time_out = @options["Timeout"] + waittime = @options["Waittime"] + + if options.kind_of?(Hash) + prompt = if options.has_key?("Match") + options["Match"] + elsif options.has_key?("Prompt") + options["Prompt"] + elsif options.has_key?("String") + Regexp.new( Regexp.quote(options["String"]) ) + end + time_out = options["Timeout"] if options.has_key?("Timeout") + waittime = options["Waittime"] if options.has_key?("Waittime") + else + prompt = options + end + + if time_out == false + time_out = nil + end + + line = '' + buf = '' + @rest = '' unless @rest + + until(prompt === line and not IO::select([@sock], nil, nil, waittime)) + unless IO::select([@sock], nil, nil, time_out) + raise TimeoutError, "timed-out; wait for the next data" + end + begin + c = @rest + @sock.sysread(1024 * 1024) + @dumplog.log_dump('<', c) if @options.has_key?("Dump_log") + if @options["Telnetmode"] + pos = 0 + catch(:next){ + while true + case c[pos] + when IAC[0] + case c[pos+1] + when DO[0], DONT[0], WILL[0], WONT[0] + throw :next unless c[pos+2] + pos += 3 + when SB[0] + ret = detect_sub_negotiation(c, pos) + throw :next unless ret + pos = ret + when nil + throw :next + else + pos += 2 + end + when nil + throw :next + else + pos += 1 + end + end + } + + buf = preprocess(c[0...pos]) + @rest = c[pos..-1] + end + @log.print(buf) if @options.has_key?("Output_log") + line.concat(buf) + yield buf if block_given? + rescue EOFError # End of file reached + if line == '' + line = nil + yield nil if block_given? + end + break + end + end + line + end + + private + + def detect_sub_negotiation(data, pos) + return nil if data.length < pos+6 # IAC SB x param IAC SE + pos += 3 + while true + case data[pos] + when IAC[0] + if data[pos+1] == SE[0] + pos += 2 + return pos + else + pos += 2 + end + when nil + return nil + else + pos += 1 + end + end + end + + end +end diff --git a/ext/openssl/lib/openssl.rb b/ext/openssl/lib/openssl.rb new file mode 100644 index 0000000000..24a9eed136 --- /dev/null +++ b/ext/openssl/lib/openssl.rb @@ -0,0 +1,24 @@ +=begin += $RCSfile$ -- Loader for all OpenSSL C-space and Ruby-space definitions + += Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2002 Michal Rokos + All rights reserved. + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + += Version + $Id$ +=end + +require 'openssl.so' + +require 'openssl/bn' +require 'openssl/cipher' +require 'openssl/digest' +require 'openssl/ssl' +require 'openssl/x509' + diff --git a/ext/openssl/lib/openssl/bn.rb b/ext/openssl/lib/openssl/bn.rb new file mode 100644 index 0000000000..e7cbf2cfaf --- /dev/null +++ b/ext/openssl/lib/openssl/bn.rb @@ -0,0 +1,35 @@ +=begin += $RCSfile$ -- Ruby-space definitions that completes C-space funcs for BN + += Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2002 Michal Rokos + All rights reserved. + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + += Version + $Id$ +=end + +## +# Should we care what if somebody require this file directly? +#require 'openssl' + +module OpenSSL + class BN + include Comparable + end # BN +end # OpenSSL + +## +# Add double dispatch to Integer +# +class Integer + def to_bn + OpenSSL::BN::new(self) + end +end # Integer + diff --git a/ext/openssl/lib/openssl/buffering.rb b/ext/openssl/lib/openssl/buffering.rb new file mode 100644 index 0000000000..61eb9dcc04 --- /dev/null +++ b/ext/openssl/lib/openssl/buffering.rb @@ -0,0 +1,189 @@ +=begin += $RCSfile$ -- Buffering mix-in module. + += Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2001 GOTOU YUUZOU + All rights reserved. + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + += Version + $Id$ +=end + +module Buffering + include Enumerable + attr_accessor :sync + BLOCK_SIZE = 1024 + + # + # for reading. + # + private + + def fill_rbuff + @rbuffer = "" unless defined? @rbuffer + begin + if self.respond_to?(:to_io) + IO.select([self.to_io], nil, nil) + end + @rbuffer << self.sysread(BLOCK_SIZE) + rescue EOFError + @eof = true + end + end + + def consume_rbuff(size=nil) + if @rbuffer.size == 0 + @eof = nil + nil + else + size = @rbuffer.size unless size + ret = @rbuffer[0, size] + @rbuffer[0, size] = "" + ret + end + end + + public + + def read(size=nil) + fill_rbuff unless defined? @rbuffer + @eof ||= nil + until @eof + break if size && size <= @rbuffer.size + fill_rbuff + end + consume_rbuff(size) + end + + def gets(eol=$/) + fill_rbuff unless defined? @rbuffer + idx = @rbuffer.index(eol) + @eof ||= nil + until @eof + break if idx + fill_rbuff + idx = @rbuffer.index(eol) + end + if eol.is_a?(Regexp) + size = idx ? idx+$&.size : nil + else + size = idx ? idx+eol.size : nil + end + consume_rbuff(size) + end + + def each(eol=$/) + while line = self.gets(eol?) + yield line + end + end + alias each_line each + + def readlines(eol=$/) + ary = [] + while line = self.gets(eol) + ary << line + end + ary + end + + def readline(eol=$/) + raise EOFErorr if eof? + gets(eol) + end + + def getc + c = read(1) + c ? c.to_i : nil + end + + def each_byte + while c = getc + yield(c) + end + end + + def readchar + raise EOFErorr if eof? + getc + end + + def ungetc(c) + @rbuffer[0,0] = c.chr + end + + def eof? + @eof ||= nil + @eof && @rbuffer.size == 0 + end + alias eof eof? + + # + # for writing. + # + private + + def do_write(s) + @wbuffer = "" unless defined? @wbuffer + @wbuffer << s + @sync ||= false + if @sync or @wbuffer.size > BLOCK_SIZE or idx = @wbuffer.rindex($/) + remain = idx ? idx + $/.size : @wbuffer.length + nwritten = 0 + while remain > 0 + nwrote = syswrite(@wbuffer[nwritten,remain]) + remain -= nwrote + nwritten += nwrote + end + @wbuffer = "" + end + end + + public + + def write(s) + do_write(s) + s.length + end + + def << (s) + do_write(s) + self + end + + def puts(*args) + s = "" + args.each{ |arg| s << arg.to_s + $/ } + do_write(s) + nil + end + + def print(*args) + s = "" + args.each{ |arg| s << arg.to_s } + do_write(s) + nil + end + + def printf(s, *args) + do_write(s % args) + nil + end + + def flush + osync = @sync + @sync = true + do_write "" + @sync = osync + end + + def close + flush + sysclose + end +end diff --git a/ext/openssl/lib/openssl/cipher.rb b/ext/openssl/lib/openssl/cipher.rb new file mode 100644 index 0000000000..11153104ee --- /dev/null +++ b/ext/openssl/lib/openssl/cipher.rb @@ -0,0 +1,52 @@ +=begin += $RCSfile$ -- Ruby-space predefined Cipher subclasses + += Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2002 Michal Rokos + All rights reserved. + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + += Version + $Id$ +=end + +## +# Should we care what if somebody require this file directly? +#require 'openssl' + +module OpenSSL + module Cipher + %w(AES Cast5 BF DES Idea RC2 RC4 RC5).each{|cipher| + eval(<<-EOD) + class #{cipher} < Cipher + def initialize(*args) + args = args.join('-') + if args.size == 0 + super(\"#{cipher}\") + else + super(\"#{cipher}-#\{args\}\") + end + end + end + EOD + } + + class Cipher + def random_key + str = OpenSSL::Random.random_bytes(self.key_len) + self.key = str + return str + end + + def random_iv + str = OpenSSL::Random.random_bytes(self.iv_len) + self.iv = str + return str + end + end + end # Cipher +end # OpenSSL diff --git a/ext/openssl/lib/openssl/digest.rb b/ext/openssl/lib/openssl/digest.rb new file mode 100644 index 0000000000..58622f543e --- /dev/null +++ b/ext/openssl/lib/openssl/digest.rb @@ -0,0 +1,44 @@ +=begin += $RCSfile$ -- Ruby-space predefined Digest subclasses + += Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2002 Michal Rokos + All rights reserved. + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + += Version + $Id$ +=end + +## +# Should we care what if somebody require this file directly? +#require 'openssl' + +module OpenSSL + module Digest + + %w(DSS DSS1 MD2 MD4 MD5 MDC2 RIPEMD160 SHA SHA1).each{|digest| + eval(<<-EOD) + class #{digest} < Digest + def initialize(data=nil) + super(\"#{digest}\", data) + end + + def #{digest}::digest(data) + Digest::digest(\"#{digest}\", data) + end + + def #{digest}::hexdigest(data) + Digest::hexdigest(\"#{digest}\", data) + end + end + EOD + } + + end # Digest +end # OpenSSL + diff --git a/ext/openssl/lib/openssl/ssl.rb b/ext/openssl/lib/openssl/ssl.rb new file mode 100644 index 0000000000..e434941913 --- /dev/null +++ b/ext/openssl/lib/openssl/ssl.rb @@ -0,0 +1,38 @@ +=begin += $RCSfile$ -- Ruby-space definitions that completes C-space funcs for SSL + += Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2001 GOTOU YUUZOU + All rights reserved. + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + += Version + $Id$ +=end + +require 'openssl/buffering' + +module OpenSSL + module SSL + class SSLSocket + include Buffering + + def addr + @io.addr + end + + def peeraddr + @io.peeraddr + end + + def closed? + @io.closed? + end + end + end +end + diff --git a/ext/openssl/lib/openssl/x509.rb b/ext/openssl/lib/openssl/x509.rb new file mode 100644 index 0000000000..f7df597acb --- /dev/null +++ b/ext/openssl/lib/openssl/x509.rb @@ -0,0 +1,132 @@ +=begin += $RCSfile$ -- Ruby-space definitions that completes C-space funcs for X509 and subclasses + += Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2002 Michal Rokos + All rights reserved. + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + += Version + $Id$ +=end + +## +# Should we care what if somebody require this file directly? +#require 'openssl' + +module OpenSSL + module X509 + + class ExtensionFactory + def create_extension(*arg) + if arg.size == 1 then arg = arg[0] end + type = arg.class + while type + method = "create_ext_from_#{type.name.downcase}".intern + return send(method, arg) if respond_to? method + type = type.superclass + end + raise TypeError, "Don't how to create ext from #{arg.class}" + ###send("create_ext_from_#{arg.class.name.downcase}", arg) + end + + # + # create_ext_from_array is built-in + # + def create_ext_from_string(str) # "oid = critical, value" + unless str =~ /\s*=\s*/ + raise ArgumentError, "string in format \"oid = value\" expected" + end + ary = [] + ary << $`.sub(/^\s*/,"") # delete whitespaces from the beginning + rest = $'.sub(/\s*$/,"") # delete them from the end + if rest =~ /^critical,\s*/ # handle 'critical' option + ary << $' + ary << true + else + ary << rest + end + create_ext_from_array(ary) + end + + # + # Create an extention from Hash + # {"oid"=>sn|ln, "value"=>value, "critical"=>true|false} + # + def create_ext_from_hash(hash) + unless (hash.has_key? "oid" and hash.has_key? "value") + raise ArgumentError, + "hash in format {\"oid\"=>..., \"value\"=>...} expected" + end + ary = [] + ary << hash["oid"] + ary << hash["value"] + ary << hash["critical"] if hash.has_key? "critical" + create_ext_from_array(ary) + end + end # ExtensionFactory + + class Extension + def to_s # "oid = critical, value" + str = self.oid + str << " = " + str << "critical, " if self.critical? + str << self.value.gsub(/\n/, ", ") + end + + def to_h # {"oid"=>sn|ln, "value"=>value, "critical"=>true|false} + {"oid"=>self.oid,"value"=>self.value,"critical"=>self.critical?} + end + + def to_a + [ self.oid, self.value, self.critical? ] + end + end # Extension + + class Attribute + def Attribute::new(arg) + type = arg.class + while type + method = "new_from_#{type.name.downcase}".intern + return Attribute::send(method, arg) if Attribute::respond_to? method + type = type.superclass + end + raise "Don't how to make new #{self} from #{arg.class}" + ###Attribute::send("new_from_#{arg.class.name.downcase}", arg) + end + + # + # Attribute::new_from_array(ary) is built-in method + # + def Attribute::new_from_string(str) # "oid = value" + unless str =~ /\s*=\s*/ + raise ArgumentError, "string in format \"oid = value\" expected" + end + ary = [] + ary << $`.sub(/^\s*/,"") # delete whitespaces from the beginning + ary << $'.sub(/\s*$/,"") # delete them from the end + Attribute::new_from_array(ary) + end + + # + # Create an attribute from Hash + # {"oid"=>sn|ln, "value"=>value, "critical"=>true|false} + # + def Attribute::new_from_hash(hash) # {"oid"=>"...", "value"=>"..."} + unless (hash.has_key? "oid" and hash.has_key? "value") + raise ArgumentError, + "hash in format {\"oid\"=>..., \"value\"=>...} expected" + end + ary = [] + ary << hash["oid"] + ary << hash["value"] + Attribute::new_from_array(ary) + end + end # Attribute + + end # X509 +end # OpenSSL diff --git a/ext/openssl/openssl_missing.c b/ext/openssl/openssl_missing.c new file mode 100644 index 0000000000..e201e750c9 --- /dev/null +++ b/ext/openssl/openssl_missing.c @@ -0,0 +1,279 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ + +#if !defined(OPENSSL_NO_HMAC) +#include /* memcpy() */ +#include + +#if !defined(HAVE_HMAC_CTX_COPY) +int +HMAC_CTX_copy(HMAC_CTX *out, HMAC_CTX *in) +{ + if (!out || !in) { + /* HMACerr(HMAC_CTX_COPY,HMAC_R_INPUT_NOT_INITIALIZED); */ + return 0; + } + memcpy(out, in, sizeof(HMAC_CTX)); + + if (!EVP_MD_CTX_copy(&out->md_ctx, &in->md_ctx)) { + return 0; + } + if (!EVP_MD_CTX_copy(&out->i_ctx, &in->i_ctx)) { + return 0; + } + if (!EVP_MD_CTX_copy(&out->o_ctx, &in->o_ctx)) { + return 0; + } + return 1; +} +#endif /* HAVE_HMAC_CTX_COPY */ +#endif /* NO_HMAC */ + +#if !defined(HAVE_X509_STORE_SET_EX_DATA) +#include + +int X509_STORE_set_ex_data(X509_STORE *str, int idx, void *data) +{ + return CRYPTO_set_ex_data(&str->ex_data,idx,data); +} + +void *X509_STORE_get_ex_data(X509_STORE *str, int idx) +{ + return CRYPTO_get_ex_data(&str->ex_data,idx); +} +#endif + +#if !defined(HAVE_EVP_MD_CTX_CREATE) +EVP_MD_CTX * +EVP_MD_CTX_create(void) +{ + EVP_MD_CTX *ctx = OPENSSL_malloc(sizeof *ctx); + + memset(ctx, '\0', sizeof *ctx); + + return ctx; +} +#endif + +#if !defined(HAVE_EVP_MD_CTX_CLEANUP) +int +EVP_MD_CTX_cleanup(EVP_MD_CTX *ctx) +{ + /* FIXME!!! */ + memset(ctx, '\0', sizeof *ctx); + + return 1; +} +#endif + +#if !defined(HAVE_EVP_MD_CTX_DESTROY) +void +EVP_MD_CTX_destroy(EVP_MD_CTX *ctx) +{ + EVP_MD_CTX_cleanup(ctx); + OPENSSL_free(ctx); +} +#endif + +#if !defined(HAVE_EVP_MD_CTX_INIT) +void +EVP_MD_CTX_init(EVP_MD_CTX *ctx) +{ + memset(ctx,'\0',sizeof *ctx); +} +#endif + +#if !defined(HAVE_HMAC_CTX_INIT) +void +HMAC_CTX_init(HMAC_CTX *ctx) +{ + EVP_MD_CTX_init(&ctx->i_ctx); + EVP_MD_CTX_init(&ctx->o_ctx); + EVP_MD_CTX_init(&ctx->md_ctx); +} +#endif + +#if !defined(HAVE_HMAC_CTX_CLEANUP) +void +HMAC_CTX_cleanup(HMAC_CTX *ctx) +{ + EVP_MD_CTX_cleanup(&ctx->i_ctx); + EVP_MD_CTX_cleanup(&ctx->o_ctx); + EVP_MD_CTX_cleanup(&ctx->md_ctx); + memset(ctx,0,sizeof *ctx); +} +#endif + +#if !defined(HAVE_X509_CRL_SET_VERSION) +int +X509_CRL_set_version(X509_CRL *x, long version) +{ + if (x == NULL) return(0); + if (x->crl->version == NULL) + { + if ((x->crl->version=M_ASN1_INTEGER_new()) == NULL) + return(0); + } + return(ASN1_INTEGER_set(x->crl->version,version)); +} +#endif + +#if !defined(HAVE_X509_CRL_SET_ISSUER_NAME) +int +X509_CRL_set_issuer_name(X509_CRL *x, X509_NAME *name) +{ + if ((x == NULL) || (x->crl == NULL)) return(0); + return(X509_NAME_set(&x->crl->issuer,name)); +} +#endif + +#if !defined(HAVE_X509_CRL_SORT) +int +X509_CRL_sort(X509_CRL *c) +{ + int i; + X509_REVOKED *r; + /* sort the data so it will be written in serial + * number order */ + sk_X509_REVOKED_sort(c->crl->revoked); + for (i=0; icrl->revoked); i++){ + r=sk_X509_REVOKED_value(c->crl->revoked,i); + r->sequence=i; + } + return 1; +} +#endif + +#if !defined(HAVE_X509_CRL_ADD0_REVOKED) +static int +OSSL_X509_REVOKED_cmp(const X509_REVOKED * const *a, const X509_REVOKED * const *b) +{ + return(ASN1_STRING_cmp( + (ASN1_STRING *)(*a)->serialNumber, + (ASN1_STRING *)(*b)->serialNumber)); +} + +int +X509_CRL_add0_revoked(X509_CRL *crl, X509_REVOKED *rev) +{ + X509_CRL_INFO *inf; + inf = crl->crl; + if(!inf->revoked) + inf->revoked = sk_X509_REVOKED_new(OSSL_X509_REVOKED_cmp); + if(!inf->revoked || !sk_X509_REVOKED_push(inf->revoked, rev)) { + /* ASN1err(ASN1_F_X509_CRL_ADD0_REVOKED, ERR_R_MALLOC_FAILURE); */ + return 0; + } + return 1; +} +#endif + +#if !defined(HAVE_BN_MOD_SQR) +int +BN_mod_sqr(BIGNUM *r, const BIGNUM *a, const BIGNUM *m, BN_CTX *ctx) +{ + if (!BN_sqr(r, (BIGNUM*)a, ctx)) return 0; + /* r->neg == 0, thus we don't need BN_nnmod */ + return BN_mod(r, r, m, ctx); +} +#endif + +#if !defined(HAVE_BN_MOD_ADD) || !defined(HAVE_BN_MOD_SUB) +int BN_nnmod(BIGNUM *r, const BIGNUM *m, const BIGNUM *d, BN_CTX *ctx) +{ + /* like BN_mod, but returns non-negative remainder + * (i.e., 0 <= r < |d| always holds) */ + if (!(BN_mod(r,m,d,ctx))) return 0; + if (!r->neg) return 1; + /* now -|d| < r < 0, so we have to set r := r + |d| */ + return (d->neg ? BN_sub : BN_add)(r, r, d); +} +#endif + +#if !defined(HAVE_BN_MOD_ADD) +int +BN_mod_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, const BIGNUM *m, BN_CTX *ctx) +{ + if (!BN_add(r, a, b)) return 0; + return BN_nnmod(r, r, m, ctx); +} +#endif + +#if !defined(HAVE_BN_MOD_SUB) +int +BN_mod_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, const BIGNUM *m, BN_CTX *ctx) +{ + if (!BN_sub(r, a, b)) return 0; + return BN_nnmod(r, r, m, ctx); +} +#endif + +#if !defined(HAVE_CONF_GET1_DEFAULT_CONFIG_FILE) +#define OPENSSL_CONF "openssl.cnf" +char * +CONF_get1_default_config_file(void) +{ + char *file; + int len; + + file = getenv("OPENSSL_CONF"); + if (file) return BUF_strdup(file); + len = strlen(X509_get_default_cert_area()); +#ifndef OPENSSL_SYS_VMS + len++; +#endif + len += strlen(OPENSSL_CONF); + file = OPENSSL_malloc(len + 1); + if (!file) return NULL; + strcpy(file,X509_get_default_cert_area()); +#ifndef OPENSSL_SYS_VMS + strcat(file,"/"); +#endif + strcat(file,OPENSSL_CONF); + + return file; +} +#endif + +#if !defined(HAVE_PEM_DEF_CALLBACK) +#define OSSL_PASS_MIN_LENGTH 4 +int +PEM_def_callback(char *buf, int num, int w, void *key) +{ + int i,j; + const char *prompt; + if(key){ + i = strlen(key); + i = (i > num) ? num : i; + memcpy(buf, key, i); + return(i); + } + + prompt = EVP_get_pw_prompt(); + if (prompt == NULL) prompt= "Enter PEM pass phrase:"; + for(;;){ + i = EVP_read_pw_string(buf, num, prompt, w); + if(i != 0){ + memset(buf,0,(unsigned int)num); + return(-1); + } + j = strlen(buf); + if(j < OSSL_PASS_MIN_LENGTH){ + fprintf(stderr, + "phrase is too short, needs to be at least %d chars\n", + OSSL_PASS_MIN_LENGTH); + } + else break; + } + return(j); +} +#endif + diff --git a/ext/openssl/openssl_missing.h b/ext/openssl/openssl_missing.h new file mode 100644 index 0000000000..33b15f6aa6 --- /dev/null +++ b/ext/openssl/openssl_missing.h @@ -0,0 +1,105 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#if !defined(_OSSL_OPENSSL_MISSING_H_) +#define _OSSL_OPENSSL_MISSING_H_ + +#if defined(__cplusplus) +extern "C" { +#endif + +/* + * These functions are not included in headers of OPENSSL <= 0.9.6b + */ + +#if !defined(PEM_read_bio_DSAPublicKey) +# define PEM_read_bio_DSAPublicKey(bp,x,cb,u) (DSA *)PEM_ASN1_read_bio( \ + (char *(*)())d2i_DSAPublicKey,PEM_STRING_DSA_PUBLIC,bp,(char **)x,cb,u) +#endif + +#if !defined(PEM_write_bio_DSAPublicKey) +# define PEM_write_bio_DSAPublicKey(bp,x) \ + PEM_ASN1_write_bio((int (*)())i2d_DSAPublicKey,\ + PEM_STRING_DSA_PUBLIC,\ + bp,(char *)x, NULL, NULL, 0, NULL, NULL) +#endif + +#if !defined(DSAPrivateKey_dup) +# define DSAPrivateKey_dup(dsa) (DSA *)ASN1_dup((int (*)())i2d_DSAPrivateKey, \ + (char *(*)())d2i_DSAPrivateKey,(char *)dsa) +#endif + +#if !defined(DSAPublicKey_dup) +# define DSAPublicKey_dup(dsa) (DSA *)ASN1_dup((int (*)())i2d_DSAPublicKey, \ + (char *(*)())d2i_DSAPublicKey,(char *)dsa) +#endif + +#if !defined(X509_REVOKED_dup) +# define X509_REVOKED_dup(rev) (X509_REVOKED *)ASN1_dup((int (*)())i2d_X509_REVOKED, \ + (char *(*)())d2i_X509_REVOKED, (char *)rev) +#endif + +#if !defined(PKCS7_SIGNER_INFO_dup) +# define PKCS7_SIGNER_INFO_dup(si) (PKCS7_SIGNER_INFO *)ASN1_dup((int (*)())i2d_PKCS7_SIGNER_INFO, \ + (char *(*)())d2i_PKCS7_SIGNER_INFO, (char *)si) +#endif + +#if !defined(PKCS7_RECIP_INFO_dup) +# define PKCS7_RECIP_INFO_dup(ri) (PKCS7_RECIP_INFO *)ASN1_dup((int (*)())i2d_PKCS7_RECIP_INFO, \ + (char *(*)())d2i_PKCS7_RECIP_INFO, (char *)ri) +#endif + +int HMAC_CTX_copy(HMAC_CTX *out, HMAC_CTX *in); +void *X509_STORE_get_ex_data(X509_STORE *str, int idx); +int X509_STORE_set_ex_data(X509_STORE *str, int idx, void *data); +EVP_MD_CTX *EVP_MD_CTX_create(void); +int EVP_MD_CTX_cleanup(EVP_MD_CTX *ctx); +void EVP_MD_CTX_destroy(EVP_MD_CTX *ctx); + +#if !defined(EVP_CIPHER_name) +# define EVP_CIPHER_name(e) OBJ_nid2sn(EVP_CIPHER_nid(e)) +#endif + +#if !defined(EVP_MD_name) +# define EVP_MD_name(e) OBJ_nid2sn(EVP_MD_type(e)) +#endif + +void EVP_MD_CTX_init(EVP_MD_CTX *ctx); +void HMAC_CTX_init(HMAC_CTX *ctx); +void HMAC_CTX_cleanup(HMAC_CTX *ctx); + +#if !defined(PKCS7_is_detached) +# define PKCS7_is_detached(p7) (PKCS7_type_is_signed(p7) && PKCS7_get_detached(p7)) +#endif + +#if !defined(PKCS7_type_is_encrypted) +# define PKCS7_type_is_encrypted(a) (OBJ_obj2nid((a)->type) == NID_pkcs7_encrypted) +#endif + +int X509_CRL_set_version(X509_CRL *x, long version); +int X509_CRL_set_issuer_name(X509_CRL *x, X509_NAME *name); +int X509_CRL_sort(X509_CRL *c); +int X509_CRL_add0_revoked(X509_CRL *crl, X509_REVOKED *rev); +int BN_mod_sqr(BIGNUM *r, const BIGNUM *a, const BIGNUM *m, BN_CTX *ctx); +int BN_mod_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, const BIGNUM *m, BN_CTX *ctx); +int BN_mod_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, const BIGNUM *m, BN_CTX *ctx); +char *CONF_get1_default_config_file(void); + +#if !defined(HAVE_PEM_DEF_CALLBACK) +int PEM_def_callback(char *buf, int num, int w, void *key); +#endif + +#if defined(__cplusplus) +} +#endif + + +#endif /* _OSSL_OPENSSL_MISSING_H_ */ + diff --git a/ext/openssl/ossl.c b/ext/openssl/ossl.c new file mode 100644 index 0000000000..ab4572d2cf --- /dev/null +++ b/ext/openssl/ossl.c @@ -0,0 +1,567 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" +#include /* for ossl_raise */ + +#if defined(HAVE_SYS_TIME_H) +# include +#elif !defined(NT) && !defined(_WIN32) +struct timeval { + long tv_sec; /* seconds */ + long tv_usec; /* and microseconds */ +}; +#endif + +/* + * DATE conversion + */ +VALUE +asn1time_to_time(ASN1_TIME *time) +{ + struct tm tm; + VALUE argv[6]; + + if (!time) { + ossl_raise(rb_eTypeError, "ASN1_TIME is NULL!"); + } + memset(&tm, 0, sizeof(struct tm)); + + switch (time->type) { + case V_ASN1_UTCTIME: + if (sscanf(time->data, "%2d%2d%2d%2d%2d%2dZ", &tm.tm_year, &tm.tm_mon, + &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) { + ossl_raise(rb_eTypeError, "bad UTCTIME format"); + } + if (tm.tm_year < 69) { + tm.tm_year += 2000; + } else { + tm.tm_year += 1900; + } + tm.tm_mon -= 1; + break; + case V_ASN1_GENERALIZEDTIME: + if (sscanf(time->data, "%4d%2d%2d%2d%2d%2dZ", &tm.tm_year, &tm.tm_mon, + &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) { + ossl_raise(rb_eTypeError, "bad GENERALIZEDTIME format" ); + } + tm.tm_mon -= 1; + break; + default: + rb_warning("unknown time format"); + return Qnil; + } + argv[0] = INT2NUM(tm.tm_year); + argv[1] = INT2NUM(tm.tm_mon+1); + argv[2] = INT2NUM(tm.tm_mday); + argv[3] = INT2NUM(tm.tm_hour); + argv[4] = INT2NUM(tm.tm_min); + argv[5] = INT2NUM(tm.tm_sec); + + return rb_funcall2(rb_cTime, rb_intern("utc"), 6, argv); +} + +/* + * This function is not exported in Ruby's *.h + */ +extern struct timeval rb_time_timeval(VALUE); + +time_t +time_to_time_t(VALUE time) +{ + struct timeval t = rb_time_timeval(time); + return t.tv_sec; +} + +/* + * ASN1_INTEGER conversions + * TODO: Make a decision what's the right way to do this. + */ +#define DO_IT_VIA_RUBY 0 +VALUE +asn1integer_to_num(ASN1_INTEGER *ai) +{ + BIGNUM *bn; +#if DO_IT_VIA_RUBY + char *txt; +#endif + VALUE num; + + if (!ai) { + ossl_raise(rb_eTypeError, "ASN1_INTEGER is NULL!"); + } + if (!(bn = ASN1_INTEGER_to_BN(ai, NULL))) { + ossl_raise(eOSSLError, NULL); + } +#if DO_IT_VIA_RUBY + if (!(txt = BN_bn2dec(bn))) { + BN_free(bn); + ossl_raise(eOSSLError, NULL); + } + num = rb_cstr_to_inum(txt, 10, Qtrue); + OPENSSL_free(txt); +#else + num = ossl_bn_new(bn); +#endif + BN_free(bn); + + return num; +} + +#if DO_IT_VIA_RUBY +ASN1_INTEGER *num_to_asn1integer(VALUE obj, ASN1_INTEGER *ai) +{ + BIGNUM *bn = NULL; + + if (RTEST(rb_obj_is_kind_of(obj, cBN))) { + bn = GetBNPtr(obj); + } else { + obj = rb_String(obj); + if (!BN_dec2bn(&bn, StringValuePtr(obj))) { + ossl_raise(eOSSLError, NULL); + } + } + if (!(ai = BN_to_ASN1_INTEGER(bn, ai))) { + BN_free(bn); + ossl_raise(eOSSLError, NULL); + } + BN_free(bn); + return ai; +} +#else +ASN1_INTEGER *num_to_asn1integer(VALUE obj, ASN1_INTEGER *ai) +{ + BIGNUM *bn = GetBNPtr(obj); + + if (!(ai = BN_to_ASN1_INTEGER(bn, ai))) { + ossl_raise(eOSSLError, NULL); + } + return ai; +} +#endif + +/* + * String to HEXString conversion + */ +int +string2hex(char *buf, int buf_len, char **hexbuf, int *hexbuf_len) +{ + static const char hex[]="0123456789abcdef"; + int i, len = 2 * buf_len; + + if (buf_len < 0 || len < buf_len) { /* PARANOIA? */ + return -1; + } + if (!hexbuf) { /* if no buf, return calculated len */ + if (hexbuf_len) { + *hexbuf_len = len; + } + return len; + } + if (!(*hexbuf = OPENSSL_malloc(len + 1))) { + return -1; + } + for (i = 0; i < buf_len; i++) { + (*hexbuf)[2 * i] = hex[((unsigned char)buf[i]) >> 4]; + (*hexbuf)[2 * i + 1] = hex[buf[i] & 0x0f]; + } + (*hexbuf)[2 * i] = '\0'; + + if (hexbuf_len) { + *hexbuf_len = len; + } + return len; +} + +/* + * Data Conversion + */ +BIO * +ossl_obj2bio(VALUE obj) +{ + BIO *bio; + + if (TYPE(obj) == T_FILE) { + OpenFile *fptr; + GetOpenFile(obj, fptr); + rb_io_check_readable(fptr); + bio = BIO_new_fp(fptr->f, BIO_NOCLOSE); + } + else { + StringValue(obj); + bio = BIO_new_mem_buf(RSTRING(obj)->ptr, RSTRING(obj)->len); + } + if (!bio) ossl_raise(eOSSLError, NULL); + + return bio; +} + +BIO * +ossl_protect_obj2bio(VALUE obj, int *status) +{ + BIO *ret = NULL; + ret = (BIO*)rb_protect((VALUE(*)())ossl_obj2bio, obj, status); + return ret; +} + +VALUE +ossl_membio2str(BIO *bio) +{ + VALUE ret; + BUF_MEM *buf; + + BIO_get_mem_ptr(bio, &buf); + ret = rb_str_new(buf->data, buf->length); + + return ret; +} + +VALUE +ossl_protect_membio2str(BIO *bio, int *status) +{ + return rb_protect((VALUE(*)())ossl_membio2str, (VALUE)bio, status); +} + +STACK_OF(X509) * +ossl_x509_ary2sk(VALUE ary) +{ + STACK_OF(X509) *sk; + VALUE val; + X509 *x509; + int i; + + Check_Type(ary, T_ARRAY); + sk = sk_X509_new_null(); + if (!sk) ossl_raise(eOSSLError, NULL); + + for (i = 0; i < RARRAY(ary)->len; i++) { + val = rb_ary_entry(ary, i); + if (!rb_obj_is_kind_of(val, cX509Cert)) { + sk_X509_pop_free(sk, X509_free); + ossl_raise(eOSSLError, "object except X509 cert is in array"); + } + x509 = DupX509CertPtr(val); /* NEED TO DUP */ + sk_X509_push(sk, x509); + } + return sk; +} + +STACK_OF(X509) * +ossl_protect_x509_ary2sk(VALUE ary, int *status) +{ + return (STACK_OF(X509)*)rb_protect((VALUE(*)())ossl_x509_ary2sk, ary, status); +} + +#if 0 +#define OSSL_SK2ARY(name, type) \ +VALUE \ +ossl_##name##_sk2ary(STACK *sk) \ +{ \ + type *t; \ + int i, num; \ + VALUE ary; \ + \ + if (!sk) { \ + OSSL_Debug("empty sk!"); \ + return rb_ary_new(); \ + } \ + num = sk_num(sk); \ + if (num < 0) { \ + OSSL_Debug("items in sk < -1???"); \ + return rb_ary_new(); \ + } \ + ary = rb_ary_new2(num); \ + \ + for (i=0; ilen; + if (len < 4) { /* 4 is OpenSSL hardcoded limit */ + rb_warning("password must be longer than 4 bytes"); + continue; + } + if (len > max_len) { + rb_warning("password must be shorter then %d bytes", max_len-1); + continue; + } + memcpy(buf, RSTRING(pass)->ptr, len); + break; + } + return len; +} + +/* + * Verify callback + */ +int ossl_verify_cb_idx; + +VALUE +ossl_call_verify_cb_proc(struct ossl_verify_cb_args *args) +{ + return rb_funcall(args->proc, rb_intern("call"), 2, + args->preverify_ok, args->store_ctx); +} + +int +ossl_verify_cb(int ok, X509_STORE_CTX *ctx) +{ + VALUE proc, rctx, ret; + struct ossl_verify_cb_args args; + int state = 0; + + proc = (VALUE)X509_STORE_CTX_get_ex_data(ctx, ossl_verify_cb_idx); + if ((void*)proc == 0) + proc = (VALUE)X509_STORE_get_ex_data(ctx->ctx, ossl_verify_cb_idx); + if ((void*)proc == 0) + return ok; + if (!NIL_P(proc)) { + rctx = rb_protect((VALUE(*)(VALUE))ossl_x509stctx_new, + (VALUE)ctx, &state); + ret = Qfalse; + if (!state) { + args.proc = proc; + args.preverify_ok = ok ? Qtrue : Qfalse; + args.store_ctx = rctx; + ret = rb_ensure(ossl_call_verify_cb_proc, (VALUE)&args, + ossl_x509stctx_clear_ptr, rctx); + } + if (ret == Qtrue) { + X509_STORE_CTX_set_error(ctx, X509_V_OK); + ok = 1; + } + else{ + if (X509_STORE_CTX_get_error(ctx) == X509_V_OK) { + X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REJECTED); + } + ok = 0; + } + } + + return ok; +} + +/* + * main module + */ +VALUE mOSSL; + +/* + * OpenSSLError < StandardError + */ +VALUE eOSSLError; + +/* + * Errors + */ +void +ossl_raise(VALUE exc, const char *fmt, ...) +{ + va_list args; + char buf[BUFSIZ]; + const char *msg; + long e = ERR_get_error(); + int len = 0; + + if (fmt) { + va_start(args, fmt); + len = vsnprintf(buf, BUFSIZ, fmt, args); + va_end(args); + len += snprintf(buf+len, BUFSIZ-len, ": "); + } + if (e) { + if (dOSSL == Qtrue) /* FULL INFO */ + msg = ERR_error_string(e, NULL); + else + msg = ERR_reason_error_string(e); + ERR_clear_error(); + len += snprintf(buf+len, BUFSIZ-len, "%s", msg); + } + + rb_exc_raise(rb_exc_new(exc, buf, len)); +} + +/* + * Debug + */ +VALUE dOSSL; + +#if defined(NT) || defined(_WIN32) +void ossl_debug(const char *fmt, ...) +{ + va_list args; + + if (dOSSL == Qtrue) { + fprintf(stderr, "OSSL_DEBUG: "); + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + fprintf(stderr, " [CONTEXT N/A]\n"); + } +} +#endif + +static VALUE +ossl_debug_get(VALUE self) +{ + return dOSSL; +} + +static VALUE +ossl_debug_set(VALUE self, VALUE val) +{ + VALUE old = dOSSL; + dOSSL = val; + + if (old != dOSSL) { + if (dOSSL == Qtrue) { + CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON); + fprintf(stderr, "OSSL_DEBUG: IS NOW ON!\n"); + } else if (old == Qtrue) { + CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_OFF); + fprintf(stderr, "OSSL_DEBUG: IS NOW OFF!\n"); + } + } + return val; +} + +/* + * OSSL library init + */ +void +Init_openssl() +{ + /* + * Init timezone info + */ +#if 0 + tzset(); +#endif + + /* + * Init all digests, ciphers + */ + /* CRYPTO_malloc_init(); */ + /* ENGINE_load_builtin_engines(); */ + OpenSSL_add_all_algorithms(); + ERR_load_crypto_strings(); + SSL_load_error_strings(); + + /* + * FIXME: + * On unload do: + */ +#if 0 + CONF_modules_unload(1); + destroy_ui_method(); + EVP_cleanup(); + ENGINE_cleanup(); + CRYPTO_cleanup_all_ex_data(); + ERR_remove_state(0); + ERR_free_strings(); +#endif + + /* + * Init main module + */ + mOSSL = rb_define_module("OpenSSL"); + + /* + * Constants + */ + rb_define_const(mOSSL, "VERSION", rb_str_new2(OSSL_VERSION)); + rb_define_const(mOSSL, "OPENSSL_VERSION", rb_str_new2(OPENSSL_VERSION_TEXT)); + rb_define_const(mOSSL, "OPENSSL_VERSION_NUMBER", INT2NUM(OPENSSL_VERSION_NUMBER)); + + /* + * Generic error, + * common for all classes under OpenSSL module + */ + eOSSLError = rb_define_class_under(mOSSL,"OpenSSLError",rb_eStandardError); + + /* + * Verify callback Proc index for ext-data + */ + ossl_verify_cb_idx = + X509_STORE_CTX_get_ex_new_index(0, "ossl_verify_cb_idx", 0, 0, 0); + + /* + * Init debug core + */ + dOSSL = Qfalse; + rb_define_module_function(mOSSL, "debug", ossl_debug_get, 0); + rb_define_module_function(mOSSL, "debug=", ossl_debug_set, 1); + + /* + * Init components + */ + Init_ossl_bn(); + Init_ossl_cipher(); + Init_ossl_config(); + Init_ossl_digest(); + Init_ossl_hmac(); + Init_ossl_ns_spki(); + Init_ossl_pkcs7(); + Init_ossl_pkey(); + Init_ossl_rand(); + Init_ossl_ssl(); + Init_ossl_x509(); + Init_ossl_ocsp(); +} + +#if defined(OSSL_DEBUG) +/* + * Check if all symbols are OK with 'make LDSHARED=gcc all' + */ +int +main(int argc, char *argv[], char *env[]) +{ + return 0; +} +#endif /* OSSL_DEBUG */ + diff --git a/ext/openssl/ossl.h b/ext/openssl/ossl.h new file mode 100644 index 0000000000..e1cd09ce4a --- /dev/null +++ b/ext/openssl/ossl.h @@ -0,0 +1,195 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#if !defined(_OSSL_H_) +#define _OSSL_H_ + +#if defined(__cplusplus) +extern "C" { +#endif + +/* + * Check the Ruby version and OpenSSL + * The only supported are: + * Ruby >= 1.7.2 + * OpenSSL >= 0.9.7 + */ +#include +#include + +#if defined(NT) || defined(_WIN32) +# define OpenFile WINAPI_OpenFile +#endif +#include +#include +#include +#include +#include +#include +#include +#if defined(HAVE_OPENSSL_OCSP_H) +# define OSSL_OCSP_ENABLED +# include +#endif +#if defined(NT) || defined(_WIN32) +# undef OpenFile +#endif + +/* + * OpenSSL has defined RFILE and Ruby has defined RFILE - so undef it! + */ +#if defined(RFILE) /*&& !defined(OSSL_DEBUG)*/ +# undef RFILE +#endif +#include +#include + +/* + * Common Module + */ +extern VALUE mOSSL; + +/* + * Common Error Class + */ +extern VALUE eOSSLError; + +/* + * CheckTypes + */ +#define OSSL_Check_Kind(obj, klass) do {\ + if (!rb_obj_is_kind_of(obj, klass)) {\ + ossl_raise(rb_eTypeError, "wrong argument (%s)! (Expected kind of %s)",\ + rb_obj_classname(obj), rb_class2name(klass));\ + }\ +} while (0) + +#define OSSL_Check_Instance(obj, klass) do {\ + if (!rb_obj_is_instance_of(obj, klass)) {\ + ossl_raise(rb_eTypeError, "wrong argument (%s)! (Expected instance of %s)",\ + rb_obj_classname(obj), rb_class2name(klass));\ + }\ +} while (0) + +#define OSSL_Check_Same_Class(obj1, obj2) do {\ + if (!rb_obj_is_instance_of(obj1, rb_obj_class(obj2))) {\ + ossl_raise(rb_eTypeError, "wrong argument type");\ + }\ +} while (0) + +/* + * ASN1_DATE conversions + */ +VALUE asn1time_to_time(ASN1_TIME *); +time_t time_to_time_t(VALUE); + +/* + * ASN1_INTEGER conversions + */ +VALUE asn1integer_to_num(ASN1_INTEGER *); +ASN1_INTEGER *num_to_asn1integer(VALUE, ASN1_INTEGER *); + +/* + * String to HEXString conversion + */ +int string2hex(char *, int, char **, int *); + +/* + * Data Conversion + */ +BIO *ossl_obj2bio(VALUE); +BIO *ossl_protect_obj2bio(VALUE,int*); +VALUE ossl_membio2str(BIO*); +VALUE ossl_protect_membio2str(BIO*,int*); +STACK_OF(X509) *ossl_x509_ary2sk(VALUE); +STACK_OF(X509) *ossl_protect_x509_ary2sk(VALUE,int*); + +/* + * our default PEM callback + */ +int ossl_pem_passwd_cb(char *, int, int, void *); + +/* + * ERRor messages + */ +#define OSSL_ErrMsg() ERR_reason_error_string(ERR_get_error()) +NORETURN(void ossl_raise(VALUE, const char *, ...)); + +/* + * Verify callback + */ +extern int ossl_verify_cb_idx; + +struct ossl_verify_cb_args { + VALUE proc; + VALUE preverify_ok; + VALUE store_ctx; +}; + +VALUE ossl_call_verify_cb_proc(struct ossl_verify_cb_args *); +int ossl_verify_cb(int, X509_STORE_CTX *); + +/* + * Debug + */ +extern VALUE dOSSL; + +#if defined(__GNUC__) || __STDC_VERSION__ >= 199901L +#define OSSL_Debug(fmt, ...) do { \ + if (dOSSL == Qtrue) { \ + fprintf(stderr, "OSSL_DEBUG: "); \ + fprintf(stderr, fmt, ##__VA_ARGS__); \ + fprintf(stderr, " [in %s (%s:%d)]\n", __func__, __FILE__, __LINE__); \ + } \ +} while (0) + +#define OSSL_Warning(fmt, ...) do { \ + OSSL_Debug(fmt, ##__VA_ARGS__); \ + rb_warning(fmt, ##__VA_ARGS__); \ +} while (0) + +#define OSSL_Warn(fmt, ...) do { \ + OSSL_Debug(fmt, ##__VA_ARGS__); \ + rb_warn(fmt, ##__VA_ARGS__); \ +} while (0) +#else +void ossl_debug(const char *, ...); +#define OSSL_Debug ossl_debug +#define OSSL_Warning rb_warning +#define OSSL_Warn rb_warn +#endif /* __GNUC__ || _STDC_VERSION__ >= 199901L */ + +/* + * Include all parts + */ +#include "openssl_missing.h" +#include "ruby_missing.h" +#include "ossl_bn.h" +#include "ossl_cipher.h" +#include "ossl_config.h" +#include "ossl_digest.h" +#include "ossl_hmac.h" +#include "ossl_ns_spki.h" +#include "ossl_pkcs7.h" +#include "ossl_pkey.h" +#include "ossl_rand.h" +#include "ossl_ssl.h" +#include "ossl_version.h" +#include "ossl_x509.h" +#include "ossl_ocsp.h" + +void Init_openssl(void); + +#if defined(__cplusplus) +} +#endif + +#endif /* _OSSL_H_ */ + diff --git a/ext/openssl/ossl_bn.c b/ext/openssl/ossl_bn.c new file mode 100644 index 0000000000..92b43bd5d1 --- /dev/null +++ b/ext/openssl/ossl_bn.c @@ -0,0 +1,734 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Technorama team + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +/* modified by Michal Rokos */ +#include "ossl.h" + +#define WrapBN(klass, obj, bn) do { \ + if (!bn) { \ + ossl_raise(rb_eRuntimeError, "BN wasn't initialized!"); \ + } \ + obj = Data_Wrap_Struct(klass, 0, BN_clear_free, bn); \ +} while (0) + +#define GetBN(obj, bn) do { \ + Data_Get_Struct(obj, BIGNUM, bn); \ + if (!bn) { \ + ossl_raise(rb_eRuntimeError, "BN wasn't initialized!"); \ + } \ +} while (0) + +#define SafeGetBN(obj, bn) do { \ + OSSL_Check_Kind(obj, cBN); \ + GetBN(obj, bn); \ +} while (0) + +/* + * Classes + */ +VALUE cBN; +VALUE eBNError; + +/* + * Public + */ +VALUE +ossl_bn_new(BIGNUM *bn) +{ + BIGNUM *newbn; + VALUE obj; + + newbn = bn ? BN_dup(bn) : BN_new(); + if (!newbn) { + ossl_raise(eBNError, NULL); + } + WrapBN(cBN, obj, newbn); + + return obj; +} + +BIGNUM * +GetBNPtr(VALUE obj) +{ + BIGNUM *bn = NULL; + + if (RTEST(rb_obj_is_kind_of(obj, cBN))) { + GetBN(obj, bn); + } else switch (TYPE(obj)) { + case T_FIXNUM: + case T_BIGNUM: + obj = rb_String(obj); + if (!BN_dec2bn(&bn, StringValuePtr(obj))) { + ossl_raise(eBNError, NULL); + } + WrapBN(cBN, obj, bn); /* Handle potencial mem leaks */ + break; + default: + ossl_raise(rb_eTypeError, "Cannot convert into OpenSSL::BN"); + } + return bn; +} + +/* + * Private + */ +/* + * BN_CTX - is used in more difficult math. ops + * (Why just 1? Because Ruby itself isn't thread safe, + * we don't need to care about threads) + */ +BN_CTX *ossl_bn_ctx; + +static VALUE +ossl_bn_alloc(VALUE klass) +{ + BIGNUM *bn; + VALUE obj; + + if (!(bn = BN_new())) { + ossl_raise(eBNError, NULL); + } + WrapBN(klass, obj, bn); + + return obj; +} +DEFINE_ALLOC_WRAPPER(ossl_bn_alloc) + +static VALUE +ossl_bn_initialize(int argc, VALUE *argv, VALUE self) +{ + BIGNUM *bn; + VALUE str, bs; + int base = 10; + + GetBN(self, bn); + + if (rb_scan_args(argc, argv, "11", &str, &bs) == 2) { + base = NUM2INT(bs); + } + if (RTEST(rb_obj_is_kind_of(str, cBN))) { + BIGNUM *other; + + GetBN(str, other); /* Safe - we checked kind_of? above */ + if (!BN_copy(bn, other)) { + ossl_raise(eBNError, NULL); + } + return self; + } + str = rb_String(str); + StringValue(str); + + switch (base) { + case 0: + if (!BN_mpi2bn(RSTRING(str)->ptr, RSTRING(str)->len, bn)) { + ossl_raise(eBNError, NULL); + } + break; + case 2: + if (!BN_bin2bn(RSTRING(str)->ptr, RSTRING(str)->len, bn)) { + ossl_raise(eBNError, NULL); + } + break; + case 10: + if (!BN_dec2bn(&bn, RSTRING(str)->ptr)) { + ossl_raise(eBNError, NULL); + } + break; + case 16: + if (!BN_hex2bn(&bn, RSTRING(str)->ptr)) { + ossl_raise(eBNError, NULL); + } + break; + default: + ossl_raise(rb_eArgError, "illegal radix %d", base); + } + return self; +} + +static VALUE +ossl_bn_to_s(int argc, VALUE *argv, VALUE self) +{ + BIGNUM *bn; + VALUE str, bs; + int base = 10, len; + char *buf; + + GetBN(self, bn); + + if (rb_scan_args(argc, argv, "01", &bs) == 1) { + base = NUM2INT(bs); + } + switch (base) { + case 0: + len = BN_bn2mpi(bn, NULL); + if (!(buf = OPENSSL_malloc(len))) { + ossl_raise(eBNError, "Cannot allocate mem for BN"); + } + if (BN_bn2mpi(bn, buf) != len) { + OPENSSL_free(buf); + ossl_raise(eBNError, NULL); + } + break; + case 2: + len = BN_num_bytes(bn); + if (!(buf = OPENSSL_malloc(len))) { + ossl_raise(eBNError, "Cannot allocate mem for BN"); + } + if (BN_bn2bin(bn, buf) != len) { + OPENSSL_free(buf); + ossl_raise(eBNError, NULL); + } + break; + case 10: + if (!(buf = BN_bn2dec(bn))) { + ossl_raise(eBNError, NULL); + } + len = strlen(buf); + break; + case 16: + if (!(buf = BN_bn2hex(bn))) { + ossl_raise(eBNError, NULL); + } + len = strlen(buf); + break; + default: + ossl_raise(rb_eArgError, "illegal radix %d", base); + } + str = rb_str_new(buf, len); + OPENSSL_free(buf); + + return str; +} + +static VALUE +ossl_bn_to_i(VALUE self) +{ + BIGNUM *bn; + char *txt; + VALUE num; + + GetBN(self, bn); + + if (!(txt = BN_bn2dec(bn))) { + ossl_raise(eBNError, NULL); + } + num = rb_cstr_to_inum(txt, 10, Qtrue); + OPENSSL_free(txt); + + return num; +} + +static VALUE +ossl_bn_to_bn(VALUE self) +{ + return self; +} + +static VALUE +ossl_bn_coerce(VALUE self, VALUE other) +{ + switch(TYPE(other)) { + case T_STRING: + self = ossl_bn_to_s(0, NULL, self); + break; + case T_FIXNUM: + case T_BIGNUM: + self = ossl_bn_to_i(self); + break; + default: + if (!RTEST(rb_obj_is_kind_of(other, cBN))) { + ossl_raise(rb_eTypeError, "Don't know how to coerce"); + } + } + return rb_assoc_new(other, self); +} + +#define BIGNUM_BOOL1(func) \ + static VALUE \ + ossl_bn_##func(VALUE self) \ + { \ + BIGNUM *bn; \ + GetBN(self, bn); \ + if (BN_##func(bn)) { \ + return Qtrue; \ + } \ + return Qfalse; \ + } +BIGNUM_BOOL1(is_zero); +BIGNUM_BOOL1(is_one); +BIGNUM_BOOL1(is_odd); + +#define BIGNUM_1c(func) \ + static VALUE \ + ossl_bn_##func(VALUE self) \ + { \ + BIGNUM *bn, *result; \ + VALUE obj; \ + GetBN(self, bn); \ + if (!(result = BN_new())) { \ + ossl_raise(eBNError, NULL); \ + } \ + if (!BN_##func(result, bn, ossl_bn_ctx)) { \ + BN_free(result); \ + ossl_raise(eBNError, NULL); \ + } \ + WrapBN(CLASS_OF(self), obj, result); \ + return obj; \ + } +BIGNUM_1c(sqr); + +#define BIGNUM_2(func) \ + static VALUE \ + ossl_bn_##func(VALUE self, VALUE other) \ + { \ + BIGNUM *bn1, *bn2 = GetBNPtr(other), *result; \ + VALUE obj; \ + GetBN(self, bn1); \ + if (!(result = BN_new())) { \ + ossl_raise(eBNError, NULL); \ + } \ + if (!BN_##func(result, bn1, bn2)) { \ + BN_free(result); \ + ossl_raise(eBNError, NULL); \ + } \ + WrapBN(CLASS_OF(self), obj, result); \ + return obj; \ + } +BIGNUM_2(add); +BIGNUM_2(sub); + +#define BIGNUM_2c(func) \ + static VALUE \ + ossl_bn_##func(VALUE self, VALUE other) \ + { \ + BIGNUM *bn1, *bn2 = GetBNPtr(other), *result; \ + VALUE obj; \ + GetBN(self, bn1); \ + if (!(result = BN_new())) { \ + ossl_raise(eBNError, NULL); \ + } \ + if (!BN_##func(result, bn1, bn2, ossl_bn_ctx)) { \ + BN_free(result); \ + ossl_raise(eBNError, NULL); \ + } \ + WrapBN(CLASS_OF(self), obj, result); \ + return obj; \ + } +BIGNUM_2c(mul); +BIGNUM_2c(mod); +BIGNUM_2c(exp); +BIGNUM_2c(gcd); +BIGNUM_2c(mod_sqr); +BIGNUM_2c(mod_inverse); + +static VALUE +ossl_bn_div(VALUE self, VALUE other) +{ + BIGNUM *bn1, *bn2 = GetBNPtr(other), *r1, *r2; + VALUE obj1, obj2; + + GetBN(self, bn1); + + if (!(r1 = BN_new())) { + ossl_raise(eBNError, NULL); + } + if (!(r2 = BN_new())) { + BN_free(r1); + ossl_raise(eBNError, NULL); + } + if (!BN_div(r1, r2, bn1, bn2, ossl_bn_ctx)) { + BN_free(r1); + BN_free(r2); + ossl_raise(eBNError, NULL); + } + WrapBN(CLASS_OF(self), obj1, r1); + WrapBN(CLASS_OF(self), obj2, r2); + + return rb_ary_new3(2, obj1, obj2); +} + +#define BIGNUM_3c(func) \ + static VALUE \ + ossl_bn_##func(VALUE self, VALUE other1, VALUE other2) \ + { \ + BIGNUM *bn1, *bn2 = GetBNPtr(other1); \ + BIGNUM *bn3 = GetBNPtr(other2), *result; \ + VALUE obj; \ + GetBN(self, bn1); \ + if (!(result = BN_new())) { \ + ossl_raise(eBNError, NULL); \ + } \ + if (!BN_##func(result, bn1, bn2, bn3, ossl_bn_ctx)) { \ + BN_free(result); \ + ossl_raise(eBNError, NULL); \ + } \ + WrapBN(CLASS_OF(self), obj, result); \ + return obj; \ + } +BIGNUM_3c(mod_add); +BIGNUM_3c(mod_sub); +BIGNUM_3c(mod_mul); +BIGNUM_3c(mod_exp); + +#define BIGNUM_BIT(func) \ + static VALUE \ + ossl_bn_##func(VALUE self, VALUE bit) \ + { \ + BIGNUM *bn; \ + GetBN(self, bn); \ + if (!BN_##func(bn, NUM2INT(bit))) { \ + ossl_raise(eBNError, NULL); \ + } \ + return self; \ + } +BIGNUM_BIT(set_bit); +BIGNUM_BIT(clear_bit); +BIGNUM_BIT(mask_bits); + +static VALUE +ossl_bn_is_bit_set(VALUE self, VALUE bit) +{ + BIGNUM *bn; + + GetBN(self, bn); + + if (BN_is_bit_set(bn, NUM2INT(bit))) { + return Qtrue; + } + return Qfalse; +} + +#define BIGNUM_SHIFT(func) \ + static VALUE \ + ossl_bn_##func(VALUE self, VALUE bits) \ + { \ + BIGNUM *bn, *result; \ + int b; \ + VALUE obj; \ + GetBN(self, bn); \ + b = NUM2INT(bits); \ + if (!(result = BN_new())) { \ + ossl_raise(eBNError, NULL); \ + } \ + if (!BN_##func(result, bn, b)) { \ + BN_free(result); \ + ossl_raise(eBNError, NULL); \ + } \ + WrapBN(CLASS_OF(self), obj, result); \ + return obj; \ + } +BIGNUM_SHIFT(lshift); +BIGNUM_SHIFT(rshift); + +#define BIGNUM_RAND(func) \ + static VALUE \ + ossl_bn_s_##func(int argc, VALUE *argv, VALUE klass) \ + { \ + BIGNUM *result; \ + int bottom = 0, top = 0, b; \ + VALUE bits, fill, odd, obj; \ + \ + switch (rb_scan_args(argc, argv, "12", &bits, &fill, &odd)) { \ + case 3: \ + bottom = (odd == Qtrue) ? 1 : 0; \ + /* FALLTHROUGH */ \ + case 2: \ + top = FIX2INT(fill); \ + } \ + b = NUM2INT(bits); \ + if (!(result = BN_new())) { \ + ossl_raise(eBNError, NULL); \ + } \ + if (!BN_##func(result, b, top, bottom)) { \ + BN_free(result); \ + ossl_raise(eBNError, NULL); \ + } \ + WrapBN(klass, obj, result); \ + return obj; \ + } +BIGNUM_RAND(rand); +BIGNUM_RAND(pseudo_rand); + +#define BIGNUM_RAND_RANGE(func) \ + static VALUE \ + ossl_bn_s_##func##_range(VALUE klass, VALUE range) \ + { \ + BIGNUM *bn = GetBNPtr(range), *result; \ + VALUE obj; \ + if (!(result = BN_new())) { \ + ossl_raise(eBNError, NULL); \ + } \ + if (!BN_##func##_range(result, bn)) { \ + BN_free(result); \ + ossl_raise(eBNError, NULL); \ + } \ + WrapBN(klass, obj, result); \ + return obj; \ + } +BIGNUM_RAND_RANGE(rand); +BIGNUM_RAND_RANGE(pseudo_rand); + +static VALUE +ossl_bn_s_generate_prime(int argc, VALUE *argv, VALUE klass) +{ + BIGNUM *add = NULL, *rem = NULL, *result; + int safe = 1, num; + VALUE vnum, vsafe, vadd, vrem, obj; + + rb_scan_args(argc, argv, "13", &vnum, &vsafe, &vadd, &vrem); + + num = NUM2INT(vnum); + + if (vsafe == Qfalse) { + safe = 0; + } + if (!NIL_P(vadd)) { + if (NIL_P(vrem)) { + ossl_raise(rb_eArgError, + "if ADD is specified, REM must be also given"); + } + add = GetBNPtr(vadd); + rem = GetBNPtr(vrem); + } + if (!(result = BN_new())) { + ossl_raise(eBNError, NULL); + } + if (!BN_generate_prime(result, num, safe, add, rem, NULL, NULL)) { + BN_free(result); + ossl_raise(eBNError, NULL); + } + WrapBN(klass, obj, result); + + return obj; +} + +#define BIGNUM_NUM(func) \ + static VALUE \ + ossl_bn_##func(VALUE self) \ + { \ + BIGNUM *bn; \ + GetBN(self, bn); \ + return INT2FIX(BN_##func(bn)); \ + } +BIGNUM_NUM(num_bytes); +BIGNUM_NUM(num_bits); + +static VALUE +ossl_bn_copy(VALUE self, VALUE other) +{ + BIGNUM *bn1, *bn2; + + rb_check_frozen(self); + + if (self == other) return self; + + GetBN(self, bn1); + bn2 = GetBNPtr(other); + + if (!BN_copy(bn1, bn2)) { + ossl_raise(eBNError, NULL); + } + return self; +} + +#define BIGNUM_CMP(func) \ + static VALUE \ + ossl_bn_##func(VALUE self, VALUE other) \ + { \ + BIGNUM *bn1, *bn2 = GetBNPtr(other); \ + GetBN(self, bn1); \ + return INT2FIX(BN_##func(bn1, bn2)); \ + } +BIGNUM_CMP(cmp); +BIGNUM_CMP(ucmp); + +static VALUE +ossl_bn_eql(VALUE self, VALUE other) +{ + if (ossl_bn_cmp(self, other) == INT2FIX(0)) { + return Qtrue; + } + return Qfalse; +} + +static VALUE +ossl_bn_is_prime(int argc, VALUE *argv, VALUE self) +{ + BIGNUM *bn; + VALUE vchecks; + int checks = BN_prime_checks; + + GetBN(self, bn); + + if (rb_scan_args(argc, argv, "01", &vchecks) == 0) { + checks = NUM2INT(vchecks); + } + switch (BN_is_prime(bn, checks, NULL, ossl_bn_ctx, NULL)) { + case 1: + return Qtrue; + case 0: + return Qfalse; + default: + ossl_raise(eBNError, NULL); + } + /* not reachable */ + return Qnil; +} + +static VALUE +ossl_bn_is_prime_fasttest(int argc, VALUE *argv, VALUE self) +{ + BIGNUM *bn; + VALUE vchecks, vtrivdiv; + int checks = BN_prime_checks, do_trial_division = 1; + + GetBN(self, bn); + + rb_scan_args(argc, argv, "02", &vchecks, &vtrivdiv); + + if (!NIL_P(vchecks)) { + checks = NUM2INT(vchecks); + } + /* handle true/false */ + if (vtrivdiv == Qfalse) { + do_trial_division = 0; + } + switch (BN_is_prime_fasttest(bn, checks, NULL, ossl_bn_ctx, NULL, do_trial_division)) { + case 1: + return Qtrue; + case 0: + return Qfalse; + default: + ossl_raise(eBNError, NULL); + } + /* not reachable */ + return Qnil; +} + +/* + * INIT + * (NOTE: ordering of methods is the same as in 'man bn') + */ +void +Init_ossl_bn() +{ + if (!(ossl_bn_ctx = BN_CTX_new())) { + ossl_raise(rb_eRuntimeError, "Cannot init BN_CTX"); + } + + eBNError = rb_define_class_under(mOSSL, "BNError", eOSSLError); + + cBN = rb_define_class_under(mOSSL, "BN", rb_cObject); + + rb_define_alloc_func(cBN, ossl_bn_alloc); + rb_define_method(cBN, "initialize", ossl_bn_initialize, -1); + + rb_define_copy_func(cBN, ossl_bn_copy); + rb_define_method(cBN, "copy", ossl_bn_copy, 1); + + /* swap (=coerce?) */ + + rb_define_method(cBN, "num_bytes", ossl_bn_num_bytes, 0); + rb_define_method(cBN, "num_bits", ossl_bn_num_bits, 0); + /* num_bits_word */ + + rb_define_method(cBN, "+", ossl_bn_add, 1); + rb_define_method(cBN, "-", ossl_bn_sub, 1); + rb_define_method(cBN, "*", ossl_bn_mul, 1); + rb_define_method(cBN, "sqr", ossl_bn_sqr, 0); + rb_define_method(cBN, "/", ossl_bn_div, 1); + rb_define_method(cBN, "%", ossl_bn_mod, 1); + /* nnmod */ + + rb_define_method(cBN, "mod_add", ossl_bn_mod_add, 2); + rb_define_method(cBN, "mod_sub", ossl_bn_mod_sub, 2); + rb_define_method(cBN, "mod_mul", ossl_bn_mod_mul, 2); + rb_define_method(cBN, "mod_sqr", ossl_bn_mod_sqr, 1); + rb_define_method(cBN, "**", ossl_bn_exp, 1); + rb_define_method(cBN, "mod_exp", ossl_bn_mod_exp, 2); + rb_define_method(cBN, "gcd", ossl_bn_gcd, 1); + + /* add_word + * sub_word + * mul_word + * div_word + * mod_word */ + + rb_define_method(cBN, "cmp", ossl_bn_cmp, 1); + rb_define_alias(cBN, "<=>", "cmp"); + rb_define_method(cBN, "ucmp", ossl_bn_ucmp, 1); + rb_define_method(cBN, "eql?", ossl_bn_eql, 1); + rb_define_alias(cBN, "==", "eql?"); + rb_define_alias(cBN, "===", "eql?"); + rb_define_method(cBN, "zero?", ossl_bn_is_zero, 0); + rb_define_method(cBN, "one?", ossl_bn_is_one, 0); + /* is_word */ + rb_define_method(cBN, "odd?", ossl_bn_is_odd, 0); + + /* zero + * one + * value_one - DON'T IMPL. + * set_word + * get_word */ + + rb_define_singleton_method(cBN, "rand", ossl_bn_s_rand, -1); + rb_define_singleton_method(cBN, "pseudo_rand", ossl_bn_s_pseudo_rand, -1); + rb_define_singleton_method(cBN, "rand_range", ossl_bn_s_rand_range, 1); + rb_define_singleton_method(cBN, "pseudo_rand_range", ossl_bn_s_pseudo_rand_range, 1); + + rb_define_singleton_method(cBN, "generate_prime", ossl_bn_s_generate_prime, -1); + rb_define_method(cBN, "prime?", ossl_bn_is_prime, -1); + + rb_define_method(cBN, "set_bit!", ossl_bn_set_bit, 1); + rb_define_method(cBN, "clear_bit!", ossl_bn_clear_bit, 1); + rb_define_method(cBN, "bit_set?", ossl_bn_is_bit_set, 1); + rb_define_method(cBN, "mask_bits!", ossl_bn_mask_bits, 1); + rb_define_method(cBN, "<<", ossl_bn_lshift, 1); + /* lshift1 - DON'T IMPL. */ + rb_define_method(cBN, ">>", ossl_bn_rshift, 1); + /* rshift1 - DON'T IMPL. */ + + /* + * bn2bin + * bin2bn + * bn2hex + * bn2dec + * hex2bn + * dec2bn - all these are implemented in ossl_bn_initialize, and ossl_bn_to_s + * print - NOT IMPL. + * print_fp - NOT IMPL. + * bn2mpi + * mpi2bn + */ + rb_define_method(cBN, "to_s", ossl_bn_to_s, -1); + rb_define_method(cBN, "to_i", ossl_bn_to_i, 0); + rb_define_alias(cBN, "to_int", "to_i"); + rb_define_method(cBN, "to_bn", ossl_bn_to_bn, 0); + rb_define_method(cBN, "coerce", ossl_bn_coerce, 1); + + /* + * TODO: + * But how to: from_bin, from_mpi? PACK? + * to_bin + * to_mpi + */ + + rb_define_method(cBN, "mod_inverse", ossl_bn_mod_inverse, 1); + + /* RECiProcal + * MONTgomery */ + + /* + * TODO: + * Where to belong these? + */ + rb_define_method(cBN, "prime_fasttest?", ossl_bn_is_prime_fasttest, -1); +} + diff --git a/ext/openssl/ossl_bn.h b/ext/openssl/ossl_bn.h new file mode 100644 index 0000000000..12aa484873 --- /dev/null +++ b/ext/openssl/ossl_bn.h @@ -0,0 +1,22 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#if !defined(_OSSL_BN_H_) +#define _OSSL_BN_H_ + +extern VALUE cBN; +extern VALUE eBNError; + +VALUE ossl_bn_new(BIGNUM *); +BIGNUM *GetBNPtr(VALUE); +void Init_ossl_bn(void); + +#endif /* _OSS_BN_H_ */ + diff --git a/ext/openssl/ossl_cipher.c b/ext/openssl/ossl_cipher.c new file mode 100644 index 0000000000..3d63a2a038 --- /dev/null +++ b/ext/openssl/ossl_cipher.c @@ -0,0 +1,377 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" + +#define MakeCipher(obj, klass, ctx) \ + obj = Data_Make_Struct(klass, EVP_CIPHER_CTX, 0, ossl_cipher_free, ctx) +#define GetCipher(obj, ctx) do { \ + Data_Get_Struct(obj, EVP_CIPHER_CTX, ctx); \ + if (!ctx) { \ + ossl_raise(rb_eRuntimeError, "Cipher not inititalized!"); \ + } \ +} while (0) +#define SafeGetCipher(obj, ctx) do { \ + OSSL_Check_Kind(obj, cCipher); \ + GetCipher(obj, ctx); \ +} while (0) + +/* + * Classes + */ +VALUE mCipher; +VALUE cCipher; +VALUE eCipherError; + +/* + * PUBLIC + */ +const EVP_CIPHER * +GetCipherPtr(VALUE obj) +{ + EVP_CIPHER_CTX *ctx; + + SafeGetCipher(obj, ctx); + + return EVP_CIPHER_CTX_cipher(ctx); +} + +/* + * PRIVATE + */ +static void +ossl_cipher_free(EVP_CIPHER_CTX *ctx) +{ + if (ctx) { + EVP_CIPHER_CTX_cleanup(ctx); + free(ctx); + } +} + +static VALUE +ossl_cipher_alloc(VALUE klass) +{ + EVP_CIPHER_CTX *ctx; + VALUE obj; + + MakeCipher(obj, klass, ctx); + + return obj; +} +DEFINE_ALLOC_WRAPPER(ossl_cipher_alloc) + +static VALUE +ossl_cipher_initialize(VALUE self, VALUE str) +{ + EVP_CIPHER_CTX *ctx; + const EVP_CIPHER *cipher; + char *name; + + GetCipher(self, ctx); + + name = StringValuePtr(str); + + if (!(cipher = EVP_get_cipherbyname(name))) { + ossl_raise(rb_eRuntimeError, "Unsupported cipher algorithm (%s).", name); + } + EVP_CIPHER_CTX_init(ctx); + if (EVP_CipherInit(ctx, cipher, NULL, NULL, -1) != 1) + ossl_raise(eCipherError, NULL); + + return self; +} +static VALUE +ossl_cipher_copy(VALUE self, VALUE other) +{ + EVP_CIPHER_CTX *ctx1, *ctx2; + + rb_check_frozen(self); + if (self == other) return self; + + GetCipher(self, ctx1); + SafeGetCipher(other, ctx2); + + memcpy(ctx1, ctx2, sizeof(EVP_CIPHER_CTX)); + + return self; +} + +static VALUE +ossl_cipher_reset(VALUE self) +{ + EVP_CIPHER_CTX *ctx; + + GetCipher(self, ctx); + if (EVP_CipherInit(ctx, NULL, NULL, NULL, -1) != 1) + ossl_raise(eCipherError, NULL); + + return self; +} + +static VALUE +ossl_cipher_encrypt(int argc, VALUE *argv, VALUE self) +{ + EVP_CIPHER_CTX *ctx; + unsigned char iv[EVP_MAX_IV_LENGTH], key[EVP_MAX_KEY_LENGTH]; + VALUE pass, init_v; + + GetCipher(self, ctx); + + rb_scan_args(argc, argv, "02", &pass, &init_v); + + if (NIL_P(init_v)) { + /* + * TODO: + * random IV generation! + */ + memcpy(iv, "OpenSSL for Ruby rulez!", sizeof(iv)); + /* + RAND_add(data,i,0); where from take data? + if (RAND_pseudo_bytes(iv, 8) < 0) { + ossl_raise(eCipherError, NULL); + } + */ + } + else { + init_v = rb_obj_as_string(init_v); + if (EVP_MAX_IV_LENGTH > RSTRING(init_v)->len) { + memset(iv, 0, EVP_MAX_IV_LENGTH); + memcpy(iv, RSTRING(init_v)->ptr, RSTRING(init_v)->len); + } + else { + memcpy(iv, RSTRING(init_v)->ptr, sizeof(iv)); + } + } + + if (EVP_CipherInit(ctx, NULL, NULL, NULL, 1) != 1) { + ossl_raise(eCipherError, NULL); + } + + if (!NIL_P(pass)) { + StringValue(pass); + + EVP_BytesToKey(EVP_CIPHER_CTX_cipher(ctx), EVP_md5(), iv, + RSTRING(pass)->ptr, RSTRING(pass)->len, 1, key, NULL); + if (EVP_CipherInit(ctx, NULL, key, iv, -1) != 1) { + ossl_raise(eCipherError, NULL); + } + } + + return self; +} + +static VALUE +ossl_cipher_decrypt(int argc, VALUE *argv, VALUE self) +{ + EVP_CIPHER_CTX *ctx; + unsigned char iv[EVP_MAX_IV_LENGTH], key[EVP_MAX_KEY_LENGTH]; + VALUE pass, init_v; + + GetCipher(self, ctx); + rb_scan_args(argc, argv, "02", &pass, &init_v); + + if (NIL_P(init_v)) { + /* + * TODO: + * random IV generation! + */ + memcpy(iv, "OpenSSL for Ruby rulez!", EVP_MAX_IV_LENGTH); + } + else { + init_v = rb_obj_as_string(init_v); + if (EVP_MAX_IV_LENGTH > RSTRING(init_v)->len) { + memset(iv, 0, EVP_MAX_IV_LENGTH); + memcpy(iv, RSTRING(init_v)->ptr, RSTRING(init_v)->len); + } + else { + memcpy(iv, RSTRING(init_v)->ptr, EVP_MAX_IV_LENGTH); + } + } + + if (EVP_CipherInit(ctx, NULL, NULL, NULL, 0) != 1) { + ossl_raise(eCipherError, NULL); + } + + if (!NIL_P(pass)) { + StringValue(pass); + + EVP_BytesToKey(EVP_CIPHER_CTX_cipher(ctx), EVP_md5(), iv, + RSTRING(pass)->ptr, RSTRING(pass)->len, 1, key, NULL); + if (EVP_CipherInit(ctx, NULL, key, iv, -1) != 1) { + ossl_raise(eCipherError, NULL); + } + } + + return self; +} + +static VALUE +ossl_cipher_update(VALUE self, VALUE data) +{ + EVP_CIPHER_CTX *ctx; + char *in, *out; + int in_len, out_len; + VALUE str; + + GetCipher(self, ctx); + + StringValue(data); + in = RSTRING(data)->ptr; + in_len = RSTRING(data)->len; + + if (!(out = OPENSSL_malloc(in_len+EVP_CIPHER_CTX_block_size(ctx)))) { + ossl_raise(eCipherError, NULL); + } + if (!EVP_CipherUpdate(ctx, out, &out_len, in, in_len)) { + OPENSSL_free(out); + ossl_raise(eCipherError, NULL); + } + str = rb_str_new(out, out_len); + OPENSSL_free(out); + + return str; +} + +static VALUE +ossl_cipher_final(VALUE self) +{ + EVP_CIPHER_CTX *ctx; + char *out; + int out_len; + VALUE str; + + GetCipher(self, ctx); + + if (!(out = OPENSSL_malloc(EVP_CIPHER_CTX_block_size(ctx)))) { + ossl_raise(eCipherError, NULL); + } + if (!EVP_CipherFinal(ctx, out, &out_len)) { + OPENSSL_free(out); + ossl_raise(eCipherError, NULL); + } + + str = rb_str_new(out, out_len); + OPENSSL_free(out); + + return str; +} + +static VALUE +ossl_cipher_name(VALUE self) +{ + EVP_CIPHER_CTX *ctx; + + GetCipher(self, ctx); + + return rb_str_new2(EVP_CIPHER_name(EVP_CIPHER_CTX_cipher(ctx))); +} + +static VALUE +ossl_cipher_set_key(VALUE self, VALUE key) +{ + EVP_CIPHER_CTX *ctx; + + StringValue(key); + GetCipher(self, ctx); + + if (RSTRING(key)->len < EVP_CIPHER_CTX_key_length(ctx)) + ossl_raise(eCipherError, "key length too short"); + + if (EVP_CipherInit(ctx, NULL, RSTRING(key)->ptr, NULL, -1) != 1) + ossl_raise(eCipherError, NULL); + + return key; +} + +static VALUE +ossl_cipher_set_iv(VALUE self, VALUE iv) +{ + EVP_CIPHER_CTX *ctx; + + StringValue(iv); + GetCipher(self, ctx); + + if (RSTRING(iv)->len < EVP_CIPHER_CTX_iv_length(ctx)) + ossl_raise(eCipherError, "iv length too short"); + + if (EVP_CipherInit(ctx, NULL, NULL, RSTRING(iv)->ptr, -1) != 1) + ossl_raise(eCipherError, NULL); + + return iv; +} + +static VALUE +ossl_cipher_set_padding(VALUE self, VALUE padding) +{ +#if defined(HAVE_ST_FLAGS) + EVP_CIPHER_CTX *ctx; + + GetCipher(self, ctx); + + if (EVP_CIPHER_CTX_set_padding(ctx, NUM2INT(padding)) != 1) + ossl_raise(eCipherError, NULL); +#else + rb_notimplement(); +#endif + return padding; +} + +#define CIPHER_0ARG_INT(func) \ + static VALUE \ + ossl_cipher_##func(VALUE self) \ + { \ + EVP_CIPHER_CTX *ctx; \ + GetCipher(self, ctx); \ + return INT2NUM(EVP_CIPHER_##func(EVP_CIPHER_CTX_cipher(ctx))); \ + } +CIPHER_0ARG_INT(key_length) +CIPHER_0ARG_INT(iv_length) +CIPHER_0ARG_INT(block_size) + +/* + * INIT + */ +void +Init_ossl_cipher(void) +{ + mCipher = rb_define_module_under(mOSSL, "Cipher"); + eCipherError = rb_define_class_under(mOSSL, "CipherError", eOSSLError); + cCipher = rb_define_class_under(mCipher, "Cipher", rb_cObject); + + rb_define_alloc_func(cCipher, ossl_cipher_alloc); + rb_define_method(cCipher, "initialize", ossl_cipher_initialize, 1); + + rb_define_copy_func(cCipher, ossl_cipher_copy); + + rb_define_method(cCipher, "reset", ossl_cipher_reset, 0); + + rb_define_method(cCipher, "encrypt", ossl_cipher_encrypt, -1); + rb_define_method(cCipher, "decrypt", ossl_cipher_decrypt, -1); + rb_define_method(cCipher, "update", ossl_cipher_update, 1); + rb_define_alias(cCipher, "<<", "update"); + rb_define_method(cCipher, "final", ossl_cipher_final, 0); + + rb_define_method(cCipher, "name", ossl_cipher_name, 0); + + rb_define_method(cCipher, "key=", ossl_cipher_set_key, 1); + rb_define_method(cCipher, "key_len", ossl_cipher_key_length, 0); +/* + * TODO + * int EVP_CIPHER_CTX_set_key_length(EVP_CIPHER_CTX *x, int keylen); + */ + rb_define_method(cCipher, "iv=", ossl_cipher_set_iv, 1); + rb_define_method(cCipher, "iv_len", ossl_cipher_iv_length, 0); + + rb_define_method(cCipher, "block_size", ossl_cipher_block_size, 0); + + rb_define_method(cCipher, "padding=", ossl_cipher_set_padding, 1); + +} /* Init_ossl_cipher */ + diff --git a/ext/openssl/ossl_cipher.h b/ext/openssl/ossl_cipher.h new file mode 100644 index 0000000000..4dcbd4fd89 --- /dev/null +++ b/ext/openssl/ossl_cipher.h @@ -0,0 +1,22 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#if !defined(_OSSL_CIPHER_H_) +#define _OSSL_CIPHER_H_ + +extern VALUE mCipher; +extern VALUE cCipher; +extern VALUE eCipherError; + +const EVP_CIPHER *GetCipherPtr(VALUE); +void Init_ossl_cipher(void); + +#endif /* _OSSL_CIPHER_H_ */ + diff --git a/ext/openssl/ossl_config.c b/ext/openssl/ossl_config.c new file mode 100644 index 0000000000..f721bb90b1 --- /dev/null +++ b/ext/openssl/ossl_config.c @@ -0,0 +1,152 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" + +#define WrapConfig(klass, obj, conf) do { \ + if (!conf) { \ + ossl_raise(rb_eRuntimeError, "Config wasn't intitialized!"); \ + } \ + obj = Data_Wrap_Struct(klass, 0, NCONF_free, conf); \ +} while (0) + +#define GetConfig(obj, conf) do { \ + Data_Get_Struct(obj, CONF, conf); \ + if (!conf) { \ + ossl_raise(rb_eRuntimeError, "Config wasn't intitialized!"); \ + } \ +} while (0) + +/* + * Classes + */ +VALUE cConfig; +VALUE eConfigError; + +/* + * Public + */ + +/* + * Private + */ +static VALUE +ossl_config_s_load(int argc, VALUE *argv, VALUE klass) +{ + CONF *conf; + long err_line = -1; + char *filename; + VALUE path, obj; + + if (rb_scan_args(argc, argv, "01", &path) == 1) { + SafeStringValue(path); + filename = BUF_strdup(RSTRING(path)->ptr); + } + else { + if (!(filename = CONF_get1_default_config_file())) { + ossl_raise(eConfigError, NULL); + } + } + if (!(conf = NCONF_new(NULL))) { + OPENSSL_free(filename); + ossl_raise(eConfigError, NULL); + } + OSSL_Debug("Loading file: %s", filename); + + if (!NCONF_load(conf, filename, &err_line)) { + char tmp[255]; + + memcpy(tmp, filename, strlen(filename)>=sizeof(tmp)?sizeof(tmp):strlen(filename)); + tmp[sizeof(tmp)-1] = '\0'; + OPENSSL_free(filename); + + if (err_line <= 0) { + ossl_raise(eConfigError, "wrong config file (%s)", tmp); + } else { + ossl_raise(eConfigError, "error on line %ld in config file \"%s\"", + err_line, tmp); + } + } + OPENSSL_free(filename); + WrapConfig(klass, obj, conf); + + return obj; +} + +static VALUE +ossl_config_get_value(int argc, VALUE *argv, VALUE self) +{ + CONF *conf; + VALUE section, item; + char *sect = NULL, *str; + + GetConfig(self, conf); + + if (rb_scan_args(argc, argv, "11", §ion, &item) == 1) { + item = section; + } else if (!NIL_P(section)) { + sect = StringValuePtr(section); + } + if (!(str = NCONF_get_string(conf, sect, StringValuePtr(item)))) { + ossl_raise(eConfigError, NULL); + } + return rb_str_new2(str); +} + +/* + * Get all numbers as strings - use str.to_i to convert + * long number = CONF_get_number(confp->config, sect, StringValuePtr(item)); + */ + +static VALUE +ossl_config_get_section(VALUE self, VALUE section) +{ + CONF *conf; + STACK_OF(CONF_VALUE) *sk; + CONF_VALUE *entry; + int i, entries; + VALUE hash; + + GetConfig(self, conf); + + if (!(sk = NCONF_get_section(conf, StringValuePtr(section)))) { + ossl_raise(eConfigError, NULL); + } + hash = rb_hash_new(); + + if ((entries = sk_CONF_VALUE_num(sk)) < 0) { + OSSL_Debug("# of items in section is < 0?!?"); + return hash; + } + for (i=0; iname), rb_str_new2(entry->value)); + } + return hash; +} + +/* + * INIT + */ +void +Init_ossl_config() +{ + eConfigError = rb_define_class_under(mOSSL, "ConfigError", eOSSLError); + + cConfig = rb_define_class_under(mOSSL, "Config", rb_cObject); + + rb_define_singleton_method(cConfig, "load", ossl_config_s_load, -1); + rb_define_alias(CLASS_OF(cConfig), "new", "load"); + + rb_define_method(cConfig, "value", ossl_config_get_value, -1); + rb_define_method(cConfig, "section", ossl_config_get_section, 1); + rb_define_alias(cConfig, "[]", "section"); +} + diff --git a/ext/openssl/ossl_config.h b/ext/openssl/ossl_config.h new file mode 100644 index 0000000000..c45619dc1b --- /dev/null +++ b/ext/openssl/ossl_config.h @@ -0,0 +1,20 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#if !defined(_OSSL_CONFIG_H_) +#define _OSSL_CONFIG_H_ + +extern VALUE cConfig; +extern VALUE eConfigError; + +void Init_ossl_config(void); + +#endif /* _OSSL_CONFIG_H_ */ + diff --git a/ext/openssl/ossl_digest.c b/ext/openssl/ossl_digest.c new file mode 100644 index 0000000000..e04b145889 --- /dev/null +++ b/ext/openssl/ossl_digest.c @@ -0,0 +1,289 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" + +#define GetDigest(obj, ctx) do { \ + Data_Get_Struct(obj, EVP_MD_CTX, ctx); \ + if (!ctx) { \ + ossl_raise(rb_eRuntimeError, "Digest CTX wasn't initialized!"); \ + } \ +} while (0) +#define SafeGetDigest(obj, ctx) do { \ + OSSL_Check_Kind(obj, cDigest); \ + GetDigest(obj, ctx); \ +} while (0) + +/* + * Classes + */ +VALUE mDigest; +VALUE cDigest; +VALUE eDigestError; + +/* + * Public + */ +const EVP_MD * +GetDigestPtr(VALUE obj) +{ + EVP_MD_CTX *ctx; + + SafeGetDigest(obj, ctx); + + return EVP_MD_CTX_md(ctx); /*== ctx->digest*/ +} + +/* + * Private + */ +static VALUE +ossl_digest_alloc(VALUE klass) +{ + EVP_MD_CTX *ctx; + VALUE obj; + + ctx = EVP_MD_CTX_create(); + if (ctx == NULL) + ossl_raise(rb_eRuntimeError, "EVP_MD_CTX_create() failed"); + obj = Data_Wrap_Struct(klass, 0, EVP_MD_CTX_destroy, ctx); + + return obj; +} +DEFINE_ALLOC_WRAPPER(ossl_digest_alloc) + +VALUE ossl_digest_update(VALUE, VALUE); + +static VALUE +ossl_digest_initialize(int argc, VALUE *argv, VALUE self) +{ + EVP_MD_CTX *ctx; + const EVP_MD *md; + char *name; + VALUE type, data; + + GetDigest(self, ctx); + + rb_scan_args(argc, argv, "11", &type, &data); + name = StringValuePtr(type); + if (!NIL_P(data)) StringValue(data); + + md = EVP_get_digestbyname(name); + if (!md) { + ossl_raise(rb_eRuntimeError, "Unsupported digest algorithm (%s).", name); + } + EVP_DigestInit(ctx, md); + + if (!NIL_P(data)) return ossl_digest_update(self, data); + return self; +} + +static VALUE +ossl_digest_copy(VALUE self, VALUE other) +{ + EVP_MD_CTX *ctx1, *ctx2; + + rb_check_frozen(self); + if (self == other) return self; + + GetDigest(self, ctx1); + SafeGetDigest(other, ctx2); + + if (!EVP_MD_CTX_copy(ctx1, ctx2)) { + ossl_raise(eDigestError, NULL); + } + return self; +} + +static VALUE +ossl_digest_reset(VALUE self) +{ + EVP_MD_CTX *ctx; + + GetDigest(self, ctx); + EVP_DigestInit(ctx, EVP_MD_CTX_md(ctx)); + + return self; +} + +VALUE +ossl_digest_update(VALUE self, VALUE data) +{ + EVP_MD_CTX *ctx; + + GetDigest(self, ctx); + StringValue(data); + EVP_DigestUpdate(ctx, RSTRING(data)->ptr, RSTRING(data)->len); + + return self; +} + +static void +digest_final(EVP_MD_CTX *ctx, char **buf, int *buf_len) +{ + EVP_MD_CTX final; + + if (!EVP_MD_CTX_copy(&final, ctx)) { + ossl_raise(eDigestError, NULL); + } + if (!(*buf = OPENSSL_malloc(EVP_MD_CTX_size(&final)))) { + ossl_raise(eDigestError, "Cannot allocate mem for digest"); + } + EVP_DigestFinal(&final, *buf, buf_len); + EVP_MD_CTX_cleanup(&final); +} + +static VALUE +ossl_digest_digest(VALUE self) +{ + EVP_MD_CTX *ctx; + char *buf; + int buf_len; + VALUE digest; + + GetDigest(self, ctx); + digest_final(ctx, &buf, &buf_len); + digest = rb_str_new(buf, buf_len); + OPENSSL_free(buf); + + return digest; +} + +static VALUE +ossl_digest_hexdigest(VALUE self) +{ + EVP_MD_CTX *ctx; + char *buf, *hexbuf; + int buf_len; + VALUE hexdigest; + + GetDigest(self, ctx); + digest_final(ctx, &buf, &buf_len); + if (string2hex(buf, buf_len, &hexbuf, NULL) != 2 * buf_len) { + OPENSSL_free(buf); + ossl_raise(eDigestError, "Memory alloc error"); + } + hexdigest = rb_str_new(hexbuf, 2 * buf_len); + OPENSSL_free(buf); + OPENSSL_free(hexbuf); + + return hexdigest; +} + +static VALUE +ossl_digest_s_digest(VALUE klass, VALUE str, VALUE data) +{ + VALUE obj = +#if HAVE_RB_DEFINE_ALLOC_FUNC + rb_class_new_instance(1, &str, klass); +#else + ossl_digest_alloc_wrapper(1, &str, klass); +#endif + + ossl_digest_update(obj, data); + + return ossl_digest_digest(obj); +} + +static VALUE +ossl_digest_s_hexdigest(VALUE klass, VALUE str, VALUE data) +{ + VALUE obj = +#if HAVE_RB_DEFINE_ALLOC_FUNC + rb_class_new_instance(1, &str, klass); +#else + ossl_digest_alloc_wrapper(1, &str, klass); +#endif + + ossl_digest_update(obj, data); + + return ossl_digest_hexdigest(obj); +} + +static VALUE +ossl_digest_equal(VALUE self, VALUE other) +{ + EVP_MD_CTX *ctx; + VALUE str1, str2; + + GetDigest(self, ctx); + if (rb_obj_is_kind_of(other, cDigest) == Qtrue) { + str2 = ossl_digest_digest(other); + } else { + StringValue(other); + str2 = other; + } + if (RSTRING(str2)->len == EVP_MD_CTX_size(ctx)) { + str1 = ossl_digest_digest(self); + } else { + str1 = ossl_digest_hexdigest(self); + } + if (RSTRING(str1)->len == RSTRING(str2)->len + && rb_str_cmp(str1, str2) == 0) { + return Qtrue; + } + + return Qfalse; +} + +static VALUE +ossl_digest_name(VALUE self) +{ + EVP_MD_CTX *ctx; + + GetDigest(self, ctx); + + return rb_str_new2(EVP_MD_name(EVP_MD_CTX_md(ctx))); +} + +static VALUE +ossl_digest_size(VALUE self) +{ + EVP_MD_CTX *ctx; + + GetDigest(self, ctx); + + return INT2NUM(EVP_MD_CTX_size(ctx)); +} + +/* + * INIT + */ +void +Init_ossl_digest() +{ + mDigest = rb_define_module_under(mOSSL, "Digest"); + + eDigestError = rb_define_class_under(mDigest, "DigestError", eOSSLError); + + cDigest = rb_define_class_under(mDigest, "Digest", rb_cObject); + + rb_define_alloc_func(cDigest, ossl_digest_alloc); + rb_define_singleton_method(cDigest, "digest", ossl_digest_s_digest, 2); + rb_define_singleton_method(cDigest, "hexdigest", ossl_digest_s_hexdigest, 2); + + rb_define_method(cDigest, "initialize", ossl_digest_initialize, -1); + rb_define_method(cDigest, "reset", ossl_digest_reset, 0); + + rb_define_copy_func(cDigest, ossl_digest_copy); + + rb_define_method(cDigest, "digest", ossl_digest_digest, 0); + rb_define_method(cDigest, "hexdigest", ossl_digest_hexdigest, 0); + rb_define_alias(cDigest, "inspect", "hexdigest"); + rb_define_alias(cDigest, "to_s", "hexdigest"); + + rb_define_method(cDigest, "update", ossl_digest_update, 1); + rb_define_alias(cDigest, "<<", "update"); + + rb_define_method(cDigest, "==", ossl_digest_equal, 1); + + rb_define_method(cDigest, "name", ossl_digest_name, 0); + rb_define_method(cDigest, "size", ossl_digest_size, 0); +} diff --git a/ext/openssl/ossl_digest.h b/ext/openssl/ossl_digest.h new file mode 100644 index 0000000000..5aa1c42f05 --- /dev/null +++ b/ext/openssl/ossl_digest.h @@ -0,0 +1,22 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#if !defined(_OSSL_DIGEST_H_) +#define _OSSL_DIGEST_H_ + +extern VALUE mDigest; +extern VALUE cDigest; +extern VALUE eDigestError; + +const EVP_MD *GetDigestPtr(VALUE); +void Init_ossl_digest(void); + +#endif /* _OSSL_DIGEST_H_ */ + diff --git a/ext/openssl/ossl_hmac.c b/ext/openssl/ossl_hmac.c new file mode 100644 index 0000000000..05b6e81202 --- /dev/null +++ b/ext/openssl/ossl_hmac.c @@ -0,0 +1,222 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#if !defined(OPENSSL_NO_HMAC) + +#include "ossl.h" + +#define MakeHMAC(obj, klass, ctx) \ + obj = Data_Make_Struct(klass, HMAC_CTX, 0, ossl_hmac_free, ctx) +#define GetHMAC(obj, ctx) do { \ + Data_Get_Struct(obj, HMAC_CTX, ctx); \ + if (!ctx) { \ + ossl_raise(rb_eRuntimeError, "HMAC wasn't initialized"); \ + } \ +} while (0) +#define SafeGetHMAC(obj, ctx) do { \ + OSSL_Check_Kind(obj, cHMAC); \ + GetHMAC(obj, ctx); \ +} while (0) + +/* + * Classes + */ +VALUE cHMAC; +VALUE eHMACError; + +/* + * Public + */ + +/* + * Private + */ +static void +ossl_hmac_free(HMAC_CTX *ctx) +{ + HMAC_CTX_cleanup(ctx); + free(ctx); +} + +static VALUE +ossl_hmac_alloc(VALUE klass) +{ + HMAC_CTX *ctx; + VALUE obj; + + MakeHMAC(obj, klass, ctx); + + return obj; +} +DEFINE_ALLOC_WRAPPER(ossl_hmac_alloc) + +static VALUE +ossl_hmac_initialize(VALUE self, VALUE key, VALUE digest) +{ + HMAC_CTX *ctx; + + GetHMAC(self, ctx); + StringValue(key); + HMAC_CTX_init(ctx); + HMAC_Init(ctx, RSTRING(key)->ptr, RSTRING(key)->len, GetDigestPtr(digest)); + + return self; +} + +static VALUE +ossl_hmac_copy(VALUE self, VALUE other) +{ + HMAC_CTX *ctx1, *ctx2; + + rb_check_frozen(self); + if (self == other) return self; + + GetHMAC(self, ctx1); + SafeGetHMAC(other, ctx2); + + if (!HMAC_CTX_copy(ctx1, ctx2)) { + ossl_raise(eHMACError, NULL); + } + return self; +} + +static VALUE +ossl_hmac_update(VALUE self, VALUE data) +{ + HMAC_CTX *ctx; + + GetHMAC(self, ctx); + StringValue(data); + HMAC_Update(ctx, RSTRING(data)->ptr, RSTRING(data)->len); + + return self; +} + +static void +hmac_final(HMAC_CTX *ctx, char **buf, int *buf_len) +{ + HMAC_CTX final; + + if (!HMAC_CTX_copy(&final, ctx)) { + ossl_raise(eHMACError, NULL); + } + if (!(*buf = OPENSSL_malloc(HMAC_size(&final)))) { + OSSL_Debug("Allocating %d mem", HMAC_size(&final)); + ossl_raise(eHMACError, "Cannot allocate memory for hmac"); + } + HMAC_Final(&final, *buf, buf_len); + HMAC_CTX_cleanup(&final); +} + +static VALUE +ossl_hmac_digest(VALUE self) +{ + HMAC_CTX *ctx; + char *buf; + int buf_len; + VALUE digest; + + GetHMAC(self, ctx); + hmac_final(ctx, &buf, &buf_len); + digest = rb_str_new(buf, buf_len); + OPENSSL_free(buf); + + return digest; +} + +static VALUE +ossl_hmac_hexdigest(VALUE self) +{ + HMAC_CTX *ctx; + char *buf, *hexbuf; + int buf_len; + VALUE hexdigest; + + GetHMAC(self, ctx); + hmac_final(ctx, &buf, &buf_len); + if (string2hex(buf, buf_len, &hexbuf, NULL) != 2 * buf_len) { + OPENSSL_free(buf); + ossl_raise(eHMACError, "Memory alloc error"); + } + hexdigest = rb_str_new(hexbuf, 2 * buf_len); + OPENSSL_free(buf); + OPENSSL_free(hexbuf); + + return hexdigest; +} + +static VALUE +ossl_hmac_s_digest(VALUE klass, VALUE digest, VALUE key, VALUE data) +{ + char *buf; + int buf_len; + + StringValue(key); + StringValue(data); + buf = HMAC(GetDigestPtr(digest), RSTRING(key)->ptr, RSTRING(key)->len, + RSTRING(data)->ptr, RSTRING(data)->len, NULL, &buf_len); + + return rb_str_new(buf, buf_len); +} + +static VALUE +ossl_hmac_s_hexdigest(VALUE klass, VALUE digest, VALUE key, VALUE data) +{ + char *buf, *hexbuf; + int buf_len; + VALUE hexdigest; + + StringValue(key); + StringValue(data); + + buf = HMAC(GetDigestPtr(digest), RSTRING(key)->ptr, RSTRING(key)->len, + RSTRING(data)->ptr, RSTRING(data)->len, NULL, &buf_len); + if (string2hex(buf, buf_len, &hexbuf, NULL) != 2 * buf_len) { + ossl_raise(eHMACError, "Cannot convert buf to hexbuf"); + } + hexdigest = rb_str_new(hexbuf, 2 * buf_len); + OPENSSL_free(hexbuf); + + return hexdigest; +} + +/* + * INIT + */ +void +Init_ossl_hmac() +{ + eHMACError = rb_define_class_under(mOSSL, "HMACError", eOSSLError); + + cHMAC = rb_define_class_under(mOSSL, "HMAC", rb_cObject); + + rb_define_alloc_func(cHMAC, ossl_hmac_alloc); + rb_define_singleton_method(cHMAC, "digest", ossl_hmac_s_digest, 3); + rb_define_singleton_method(cHMAC, "hexdigest", ossl_hmac_s_hexdigest, 3); + + rb_define_method(cHMAC, "initialize", ossl_hmac_initialize, 2); + rb_define_copy_func(cHMAC, ossl_hmac_copy); + + rb_define_method(cHMAC, "update", ossl_hmac_update, 1); + rb_define_alias(cHMAC, "<<", "update"); + rb_define_method(cHMAC, "digest", ossl_hmac_digest, 0); + rb_define_method(cHMAC, "hexdigest", ossl_hmac_hexdigest, 0); + rb_define_alias(cHMAC, "inspect", "hexdigest"); + rb_define_alias(cHMAC, "to_s", "hexdigest"); +} + +#else /* NO_HMAC */ +# warning >>> OpenSSL is compiled without HMAC support <<< +void +Init_ossl_hmac() +{ + rb_warning("HMAC will NOT be avaible: OpenSSL is compiled without HMAC."); +} +#endif /* NO_HMAC */ diff --git a/ext/openssl/ossl_hmac.h b/ext/openssl/ossl_hmac.h new file mode 100644 index 0000000000..1a2978b39a --- /dev/null +++ b/ext/openssl/ossl_hmac.h @@ -0,0 +1,19 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#if !defined(_OSSL_HMAC_H_) +#define _OSSL_HMAC_H_ + +extern VALUE cHMAC; +extern VALUE eHMACError; + +void Init_ossl_hmac(void); + +#endif /* _OSSL_HMAC_H_ */ diff --git a/ext/openssl/ossl_ns_spki.c b/ext/openssl/ossl_ns_spki.c new file mode 100644 index 0000000000..bcefcbdfde --- /dev/null +++ b/ext/openssl/ossl_ns_spki.c @@ -0,0 +1,232 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" + +#define WrapSPKI(klass, obj, spki) do { \ + if (!spki) { \ + ossl_raise(rb_eRuntimeError, "SPKI wasn't initialized!"); \ + } \ + obj = Data_Wrap_Struct(klass, 0, NETSCAPE_SPKI_free, spki); \ +} while (0) +#define GetSPKI(obj, spki) do { \ + Data_Get_Struct(obj, NETSCAPE_SPKI, spki); \ + if (!spki) { \ + ossl_raise(rb_eRuntimeError, "SPKI wasn't initialized!"); \ + } \ +} while (0) + +/* + * Classes + */ +VALUE mNetscape; +VALUE cSPKI; +VALUE eSPKIError; + +/* + * Public functions + */ + +/* + * Private functions + */ +static VALUE +ossl_spki_alloc(VALUE klass) +{ + NETSCAPE_SPKI *spki; + VALUE obj; + + if (!(spki = NETSCAPE_SPKI_new())) { + ossl_raise(eSPKIError, NULL); + } + WrapSPKI(klass, obj, spki); + + return obj; +} +DEFINE_ALLOC_WRAPPER(ossl_spki_alloc) + +static VALUE +ossl_spki_initialize(int argc, VALUE *argv, VALUE self) +{ + NETSCAPE_SPKI *spki; + VALUE buffer; + + if (rb_scan_args(argc, argv, "01", &buffer) == 0) { + return self; + } + if (!(spki = NETSCAPE_SPKI_b64_decode(StringValuePtr(buffer), -1))) { + ossl_raise(eSPKIError, NULL); + } + NETSCAPE_SPKI_free(DATA_PTR(self)); + DATA_PTR(self) = spki; + + return self; +} + +static VALUE +ossl_spki_to_pem(VALUE self) +{ + NETSCAPE_SPKI *spki; + char *data; + VALUE str; + + GetSPKI(self, spki); + if (!(data = NETSCAPE_SPKI_b64_encode(spki))) { + ossl_raise(eSPKIError, NULL); + } + str = rb_str_new2(data); + OPENSSL_free(data); + + return str; +} + +static VALUE +ossl_spki_print(VALUE self) +{ + NETSCAPE_SPKI *spki; + BIO *out; + BUF_MEM *buf; + VALUE str; + + GetSPKI(self, spki); + if (!(out = BIO_new(BIO_s_mem()))) { + ossl_raise(eSPKIError, NULL); + } + if (!NETSCAPE_SPKI_print(out, spki)) { + BIO_free(out); + ossl_raise(eSPKIError, NULL); + } + BIO_get_mem_ptr(out, &buf); + str = rb_str_new(buf->data, buf->length); + BIO_free(out); + + return str; +} + +static VALUE +ossl_spki_get_public_key(VALUE self) +{ + NETSCAPE_SPKI *spki; + EVP_PKEY *pkey; + + GetSPKI(self, spki); + if (!(pkey = NETSCAPE_SPKI_get_pubkey(spki))) { /* adds an reference */ + ossl_raise(eSPKIError, NULL); + } + + return ossl_pkey_new(pkey); /* NO DUP - OK */ +} + +static VALUE +ossl_spki_set_public_key(VALUE self, VALUE key) +{ + NETSCAPE_SPKI *spki; + + GetSPKI(self, spki); + if (!NETSCAPE_SPKI_set_pubkey(spki, GetPKeyPtr(key))) { /* NO NEED TO DUP */ + ossl_raise(eSPKIError, NULL); + } + + return key; +} + +static VALUE +ossl_spki_get_challenge(VALUE self) +{ + NETSCAPE_SPKI *spki; + + GetSPKI(self, spki); + if (spki->spkac->challenge->length <= 0) { + OSSL_Debug("Challenge.length <= 0?"); + return rb_str_new2(NULL); + } + + return rb_str_new(spki->spkac->challenge->data, + spki->spkac->challenge->length); +} + +static VALUE +ossl_spki_set_challenge(VALUE self, VALUE str) +{ + NETSCAPE_SPKI *spki; + + GetSPKI(self, spki); + StringValue(str); + if (!ASN1_STRING_set(spki->spkac->challenge, RSTRING(str)->ptr, + RSTRING(str)->len)) { + ossl_raise(eSPKIError, NULL); + } + + return str; +} + +static VALUE +ossl_spki_sign(VALUE self, VALUE key, VALUE digest) +{ + NETSCAPE_SPKI *spki; + EVP_PKEY *pkey; + const EVP_MD *md; + + GetSPKI(self, spki); + pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */ + md = GetDigestPtr(digest); + if (!NETSCAPE_SPKI_sign(spki, pkey, md)) { + ossl_raise(eSPKIError, NULL); + } + + return self; +} + +/* + * Checks that cert signature is made with PRIVversion of this PUBLIC 'key' + */ +static VALUE +ossl_spki_verify(VALUE self, VALUE key) +{ + NETSCAPE_SPKI *spki; + + GetSPKI(self, spki); + switch (NETSCAPE_SPKI_verify(spki, GetPKeyPtr(key))) { /* NO NEED TO DUP */ + case 0: + return Qfalse; + case 1: + return Qtrue; + default: + ossl_raise(eSPKIError, NULL); + } + return Qnil; /* dummy */ +} + +/* + * NETSCAPE_SPKI init + */ +void +Init_ossl_ns_spki() +{ + mNetscape = rb_define_module_under(mOSSL, "Netscape"); + + eSPKIError = rb_define_class_under(mNetscape, "SPKIError", eOSSLError); + + cSPKI = rb_define_class_under(mNetscape, "SPKI", rb_cObject); + + rb_define_alloc_func(cSPKI, ossl_spki_alloc); + rb_define_method(cSPKI, "initialize", ossl_spki_initialize, -1); + + rb_define_method(cSPKI, "to_pem", ossl_spki_to_pem, 0); + rb_define_alias(cSPKI, "to_s", "to_pem"); + rb_define_method(cSPKI, "to_text", ossl_spki_print, 0); + rb_define_method(cSPKI, "public_key", ossl_spki_get_public_key, 0); + rb_define_method(cSPKI, "public_key=", ossl_spki_set_public_key, 1); + rb_define_method(cSPKI, "sign", ossl_spki_sign, 2); + rb_define_method(cSPKI, "verify", ossl_spki_verify, 1); + rb_define_method(cSPKI, "challenge", ossl_spki_get_challenge, 0); + rb_define_method(cSPKI, "challenge=", ossl_spki_set_challenge, 1); +} + diff --git a/ext/openssl/ossl_ns_spki.h b/ext/openssl/ossl_ns_spki.h new file mode 100644 index 0000000000..9977035a9c --- /dev/null +++ b/ext/openssl/ossl_ns_spki.h @@ -0,0 +1,21 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#if !defined(_OSSL_NS_SPKI_H_) +#define _OSSL_NS_SPKI_H_ + +extern VALUE mNetscape; +extern VALUE cSPKI; +extern VALUE eSPKIError; + +void Init_ossl_ns_spki(void); + +#endif /* _OSSL_NS_SPKI_H_ */ + diff --git a/ext/openssl/ossl_ocsp.c b/ext/openssl/ossl_ocsp.c new file mode 100644 index 0000000000..3ec5c39472 --- /dev/null +++ b/ext/openssl/ossl_ocsp.c @@ -0,0 +1,765 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2003 Michal Rokos + * Copyright (C) 2003 GOTOU Yuuzou + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" + +#if defined(OSSL_OCSP_ENABLED) + +#define WrapOCSPReq(klass, obj, req) do { \ + if(!req) ossl_raise(rb_eRuntimeError, "Request wasn't initialized!"); \ + obj = Data_Wrap_Struct(klass, 0, OCSP_REQUEST_free, req); \ +} while (0) +#define GetOCSPReq(obj, req) do { \ + Data_Get_Struct(obj, OCSP_REQUEST, req); \ + if(!req) ossl_raise(rb_eRuntimeError, "Request wasn't initialized!"); \ +} while (0) +#define SafeGetOCSPReq(obj, req) do { \ + OSSL_Check_Kind(obj, cOCSPReq); \ + GetOCSPReq(obj, req); \ +} while (0) + +#define WrapOCSPRes(klass, obj, res) do { \ + if(!res) ossl_raise(rb_eRuntimeError, "Response wasn't initialized!"); \ + obj = Data_Wrap_Struct(klass, 0, OCSP_RESPONSE_free, res); \ +} while (0) +#define GetOCSPRes(obj, res) do { \ + Data_Get_Struct(obj, OCSP_RESPONSE, res); \ + if(!res) ossl_raise(rb_eRuntimeError, "Response wasn't initialized!"); \ +} while (0) +#define SafeGetOCSPRes(obj, res) do { \ + OSSL_Check_Kind(obj, cOCSPRes); \ + GetOCSPRes(obj, res); \ +} while (0) + +#define WrapOCSPBasicRes(klass, obj, res) do { \ + if(!res) ossl_raise(rb_eRuntimeError, "Response wasn't initialized!"); \ + obj = Data_Wrap_Struct(klass, 0, OCSP_BASICRESP_free, res); \ +} while (0) +#define GetOCSPBasicRes(obj, res) do { \ + Data_Get_Struct(obj, OCSP_BASICRESP, res); \ + if(!res) ossl_raise(rb_eRuntimeError, "Response wasn't initialized!"); \ +} while (0) +#define SafeGetOCSPBasicRes(obj, res) do { \ + OSSL_Check_Kind(obj, cOCSPBasicRes); \ + GetOCSPBasicRes(obj, res); \ +} while (0) + +#define WrapOCSPCertId(klass, obj, cid) do { \ + if(!cid) ossl_raise(rb_eRuntimeError, "Cert ID wasn't initialized!"); \ + obj = Data_Wrap_Struct(klass, 0, OCSP_CERTID_free, cid); \ +} while (0) +#define GetOCSPCertId(obj, cid) do { \ + Data_Get_Struct(obj, OCSP_CERTID, cid); \ + if(!cid) ossl_raise(rb_eRuntimeError, "Cert ID wasn't initialized!"); \ +} while (0) +#define SafeGetOCSPCertId(obj, cid) do { \ + OSSL_Check_Kind(obj, cOCSPCertId); \ + GetOCSPCertId(obj, cid); \ +} while (0) + +VALUE mOCSP; +VALUE eOCSPError; +VALUE cOCSPReq; +VALUE cOCSPRes; +VALUE cOCSPBasicRes; +VALUE cOCSPCertId; + +/* + * Public + */ +static VALUE +ossl_ocspcertid_new(OCSP_CERTID *cid) +{ + VALUE obj; + WrapOCSPCertId(cOCSPCertId, obj, cid); + return obj; +} + +/* + * OCSP::Resquest + */ +static VALUE +ossl_ocspreq_alloc(VALUE klass) +{ + OCSP_REQUEST *req; + VALUE obj; + + if (!(req = OCSP_REQUEST_new())) + ossl_raise(eOCSPError, NULL); + WrapOCSPReq(klass, obj, req); + + return obj; +} +DEFINE_ALLOC_WRAPPER(ossl_ocspreq_alloc) + +static VALUE +ossl_ocspreq_initialize(int argc, VALUE *argv, VALUE self) +{ + VALUE arg; + BIO *bio; + + rb_scan_args(argc, argv, "01", &arg); + if(!NIL_P(arg)){ + bio = ossl_obj2bio(arg); + if(!d2i_OCSP_REQUEST_bio(bio, (OCSP_REQUEST**)&DATA_PTR(self))){ + BIO_free(bio); + ossl_raise(eOCSPError, "cannot load DER encoded request"); + } + BIO_free(bio); + } + + return self; +} + +static VALUE +ossl_ocspreq_add_nonce(int argc, VALUE *argv, VALUE self) +{ + OCSP_REQUEST *req; + VALUE val; + int ret; + + rb_scan_args(argc, argv, "01", &val); + GetOCSPReq(self, req); + if(NIL_P(val)) + ret = OCSP_request_add1_nonce(req, NULL, -1); + else{ + StringValue(val); + ret = OCSP_request_add1_nonce(req, RSTRING(val)->ptr, RSTRING(val)->len); + } + if(!ret) ossl_raise(eOCSPError, NULL); + + return self; +} + +/* Check nonce validity in a request and response. + * Return value reflects result: + * 1: nonces present and equal. + * 2: nonces both absent. + * 3: nonce present in response only. + * 0: nonces both present and not equal. + * -1: nonce in request only. + * + * For most responders clients can check return > 0. + * If responder doesn't handle nonces return != 0 may be + * necessary. return == 0 is always an error. + */ +static VALUE +ossl_ocspreq_check_nonce(VALUE self, VALUE basic_resp) +{ + OCSP_REQUEST *req; + OCSP_BASICRESP *bs; + int res; + + GetOCSPReq(self, req); + SafeGetOCSPBasicRes(basic_resp, bs); + res = OCSP_check_nonce(req, bs); + + return INT2NUM(res); +} + +static VALUE +ossl_ocspreq_add_certid(VALUE self, VALUE certid) +{ + OCSP_REQUEST *req; + OCSP_CERTID *id; + + GetOCSPReq(self, req); + GetOCSPCertId(certid, id); + if(!OCSP_request_add0_id(req, OCSP_CERTID_dup(id))) + ossl_raise(eOCSPError, NULL); + + return self; +} + +static VALUE +ossl_ocspreq_get_certid(VALUE self) +{ + OCSP_REQUEST *req; + OCSP_ONEREQ *one; + OCSP_CERTID *id; + VALUE ary, tmp; + int i, count; + + GetOCSPReq(self, req); + count = OCSP_request_onereq_count(req); + ary = (count > 0) ? rb_ary_new() : Qnil; + for(i = 0; i < count; i++){ + one = OCSP_request_onereq_get0(req, i); + if(!(id = OCSP_CERTID_dup(OCSP_onereq_get0_id(one)))) + ossl_raise(eOCSPError, NULL); + WrapOCSPCertId(cOCSPCertId, tmp, id); + rb_ary_push(ary, tmp); + } + + return ary; +} + +static VALUE +ossl_ocspreq_sign(int argc, VALUE *argv, VALUE self) +{ + VALUE signer_cert, signer_key, certs, flags; + OCSP_REQUEST *req; + X509 *signer; + EVP_PKEY *key; + STACK_OF(X509) *x509s; + unsigned long flg; + int ret, status = 0; + + rb_scan_args(argc, argv, "22", &signer_cert, &signer_key, &certs, &flags); + GetOCSPReq(self, req); + signer = GetX509CertPtr(signer_cert); + key = GetPrivPKeyPtr(signer_key); + flg = NIL_P(flags) ? 0 : NUM2INT(flags); + if(NIL_P(certs)){ + x509s = sk_X509_new_null(); + flags |= OCSP_NOCERTS; + } + else x509s = ossl_protect_x509_ary2sk(certs, &status); + if(status){ + sk_X509_pop_free(x509s, X509_free); + rb_jump_tag(status); + } + ret = OCSP_request_sign(req, signer, key, EVP_sha1(), x509s, flg); + sk_X509_pop_free(x509s, X509_free); + if(!ret) ossl_raise(eOCSPError, NULL); + + return self; +} + +static VALUE +ossl_ocspreq_verify(int argc, VALUE *argv, VALUE self) +{ + VALUE certs, store, flags; + OCSP_REQUEST *req; + STACK_OF(X509) *x509s; + X509_STORE *x509st; + int flg, result; + + rb_scan_args(argc, argv, "21", &certs, &store, &flags); + GetOCSPReq(self, req); + x509st = GetX509StorePtr(store); + flg = NIL_P(flags) ? 0 : INT2NUM(flags); + x509s = ossl_x509_ary2sk(certs); + result = OCSP_request_verify(req, x509s, x509st, flg); + sk_X509_pop_free(x509s, X509_free); + if(!result) rb_warn("%s", ERR_error_string(ERR_peek_error(), NULL)); + + return result ? Qtrue : Qfalse; +} + +static VALUE +ossl_ocspreq_to_der(VALUE self) +{ + OCSP_REQUEST *req; + BIO *bio; + VALUE str; + int status = 0; + + GetOCSPReq(self, req); + if(!(bio = BIO_new(BIO_s_mem()))) rb_raise(eOCSPError, NULL); + i2d_OCSP_REQUEST_bio(bio, req); + str = ossl_protect_membio2str(bio, &status); + BIO_free(bio); + if(status) rb_jump_tag(status); + + return str; +} + +/* + * OCSP::Response + */ +static VALUE +ossl_ocspres_s_create(VALUE klass, VALUE status, VALUE basic_resp) +{ + OCSP_BASICRESP *bs; + OCSP_RESPONSE *res; + VALUE obj; + + if(NIL_P(basic_resp)) bs = NULL; + else GetOCSPBasicRes(basic_resp, bs); /* NO NEED TO DUP */ + if(!(res = OCSP_response_create(NUM2INT(status), bs))) + ossl_raise(eOCSPError, NULL); + WrapOCSPRes(klass, obj, res); + + return obj; +} + +static VALUE +ossl_ocspres_alloc(VALUE klass) +{ + OCSP_RESPONSE *res; + VALUE obj; + + if(!(res = OCSP_RESPONSE_new())) + ossl_raise(eOCSPError, NULL); + WrapOCSPRes(klass, obj, res); + + return obj; +} +DEFINE_ALLOC_WRAPPER(ossl_ocspreq_alloc) + +static VALUE +ossl_ocspres_initialize(int argc, VALUE *argv, VALUE self) +{ + VALUE arg; + BIO *bio; + + rb_scan_args(argc, argv, "01", &arg); + bio = ossl_obj2bio(arg); + if(!d2i_OCSP_RESPONSE_bio(bio, (OCSP_RESPONSE**)&DATA_PTR(self))){ + BIO_free(bio); + ossl_raise(eOCSPError, "cannot load DER encoded response"); + } + BIO_free(bio); + + return self; +} + +static VALUE +ossl_ocspres_status(VALUE self) +{ + OCSP_RESPONSE *res; + int st; + + GetOCSPRes(self, res); + st = OCSP_response_status(res); + + return INT2NUM(st); +} + +static VALUE +ossl_ocspres_status_string(VALUE self) +{ + OCSP_RESPONSE *res; + int st; + + GetOCSPRes(self, res); + st = OCSP_response_status(res); + + return rb_str_new2(OCSP_response_status_str(st)); +} + +static VALUE +ossl_ocspres_get_basic(VALUE self) +{ + OCSP_RESPONSE *res; + OCSP_BASICRESP *bs; + VALUE ret; + + GetOCSPRes(self, res); + if(!(bs = OCSP_response_get1_basic(res))) + return Qnil; + WrapOCSPBasicRes(cOCSPBasicRes, ret, bs); + + return ret; +} + +static VALUE +ossl_ocspres_to_der(VALUE self) +{ + OCSP_RESPONSE *res; + BIO *bio; + VALUE str; + int status = 0; + + GetOCSPRes(self, res); + if(!(bio = BIO_new(BIO_s_mem()))) rb_raise(eOCSPError, NULL); + i2d_OCSP_RESPONSE_bio(bio, res); + str = ossl_protect_membio2str(bio, &status); + BIO_free(bio); + if(status) rb_jump_tag(status); + + return str; +} + +/* + * OCSP::BasicResponse + */ +static VALUE +ossl_ocspbres_alloc(VALUE klass) +{ + OCSP_BASICRESP *bs; + VALUE obj; + + if(!(bs = OCSP_BASICRESP_new())) + ossl_raise(eOCSPError, NULL); + WrapOCSPBasicRes(klass, obj, bs); + + return obj; +} +DEFINE_ALLOC_WRAPPER(ossl_ocspbres_alloc) + +static VALUE +ossl_ocspbres_initialize(int argc, VALUE *argv, VALUE self) +{ + return self; +} + +static VALUE +ossl_ocspbres_copy_nonce(VALUE self, VALUE request) +{ + OCSP_BASICRESP *bs; + OCSP_REQUEST *req; + int ret; + + GetOCSPBasicRes(self, bs); + SafeGetOCSPReq(request, req); + ret = OCSP_copy_nonce(bs, req); + + return INT2NUM(ret); +} + +static VALUE +ossl_ocspbres_add_nonce(int argc, VALUE *argv, VALUE self) +{ + OCSP_BASICRESP *bs; + VALUE val; + int ret; + + GetOCSPBasicRes(self, bs); + rb_scan_args(argc, argv, "01", &val); + if(NIL_P(val)) + ret = OCSP_basic_add1_nonce(bs, NULL, -1); + else{ + StringValue(val); + ret = OCSP_basic_add1_nonce(bs, RSTRING(val)->ptr, RSTRING(val)->len); + } + if(!ret) ossl_raise(eOCSPError, NULL); + + return self; +} + +static VALUE +ossl_ocspbres_add_status(VALUE self, VALUE cid, VALUE status, + VALUE reason, VALUE revtime, + VALUE thisupd, VALUE nextupd, VALUE ext) +{ + OCSP_BASICRESP *bs; + OCSP_SINGLERESP *single; + OCSP_CERTID *id; + int st, rsn; + ASN1_TIME *ths, *nxt, *rev; + int error, i, rstatus = 0; + VALUE tmp; + + GetOCSPBasicRes(self, bs); + SafeGetOCSPCertId(cid, id); + st = NUM2INT(status); + rsn = NIL_P(status) ? 0 : NUM2INT(reason); + if(!NIL_P(ext)){ + /* All ary's members should be X509Extension */ + Check_Type(ext, T_ARRAY); + for (i = 0; i < RARRAY(ext)->len; i++) + OSSL_Check_Kind(RARRAY(ext)->ptr[i], cX509Ext); + } + + error = 0; + ths = nxt = rev = NULL; + if(!NIL_P(revtime)){ + tmp = rb_protect(rb_Integer, revtime, &rstatus); + if(rstatus) goto err; + rev = X509_gmtime_adj(NULL, NUM2INT(tmp)); + } + tmp = rb_protect(rb_Integer, thisupd, &rstatus); + if(rstatus) goto err; + ths = X509_gmtime_adj(NULL, NUM2INT(tmp)); + tmp = rb_protect(rb_Integer, nextupd, &rstatus); + if(rstatus) goto err; + nxt = X509_gmtime_adj(NULL, NUM2INT(tmp)); + + if(!(single = OCSP_basic_add1_status(bs, id, st, rsn, rev, ths, nxt))){ + error = 1; + goto err; + } + + if(!NIL_P(ext)){ + X509_EXTENSION *x509ext; + sk_X509_EXTENSION_pop_free(single->singleExtensions, X509_EXTENSION_free); + single->singleExtensions = NULL; + for(i = 0; i < RARRAY(ext)->len; i++){ + x509ext = DupX509ExtPtr(RARRAY(ext)->ptr[i]); + if(!OCSP_SINGLERESP_add_ext(single, x509ext, -1)){ + X509_EXTENSION_free(x509ext); + error = 1; + goto err; + } + X509_EXTENSION_free(x509ext); + } + } + + err: + ASN1_TIME_free(ths); + ASN1_TIME_free(nxt); + ASN1_TIME_free(rev); + if(error) ossl_raise(eOCSPError, NULL); + if(rstatus) rb_jump_tag(rstatus); + + return self; +} + +static VALUE +ossl_ocspbres_get_status(VALUE self) +{ + OCSP_BASICRESP *bs; + OCSP_SINGLERESP *single; + OCSP_CERTID *cid; + ASN1_TIME *revtime, *thisupd, *nextupd; + int status, reason; + X509_EXTENSION *x509ext; + VALUE ret, ary, ext; + int count, ext_count, i, j; + + GetOCSPBasicRes(self, bs); + ret = rb_ary_new(); + count = OCSP_resp_count(bs); + for(i = 0; i < count; i++){ + single = OCSP_resp_get0(bs, i); + if(!single) continue; + + revtime = thisupd = nextupd = NULL; + status = OCSP_single_get0_status(single, &reason, &revtime, + &thisupd, &nextupd); + if(status < 0) continue; + if(!(cid = OCSP_CERTID_dup(single->certId))) + ossl_raise(eOCSPError, NULL); + ary = rb_ary_new(); + rb_ary_push(ary, ossl_ocspcertid_new(cid)); + rb_ary_push(ary, INT2NUM(status)); + rb_ary_push(ary, INT2NUM(reason)); + rb_ary_push(ary, revtime ? asn1time_to_time(revtime) : Qnil); + rb_ary_push(ary, thisupd ? asn1time_to_time(thisupd) : Qnil); + rb_ary_push(ary, nextupd ? asn1time_to_time(nextupd) : Qnil); + ext = rb_ary_new(); + ext_count = OCSP_SINGLERESP_get_ext_count(single); + for(j = 0; j < ext_count; j++){ + x509ext = OCSP_SINGLERESP_get_ext(single, j); + rb_ary_push(ext, ossl_x509ext_new(x509ext)); + } + rb_ary_push(ary, ext); + rb_ary_push(ret, ary); + } + + return ret; +} + +static VALUE +ossl_ocspbres_sign(int argc, VALUE *argv, VALUE self) +{ + VALUE signer_cert, signer_key, certs, flags; + OCSP_BASICRESP *bs; + X509 *signer; + EVP_PKEY *key; + STACK_OF(X509) *x509s; + unsigned long flg; + int ret, status = 0; + + rb_scan_args(argc, argv, "22", &signer_cert, &signer_key, &certs, &flags); + GetOCSPBasicRes(self, bs); + signer = GetX509CertPtr(signer_cert); + key = GetPrivPKeyPtr(signer_key); + flg = NIL_P(flags) ? 0 : NUM2INT(flags); + if(NIL_P(certs)){ + x509s = sk_X509_new_null(); + flg |= OCSP_NOCERTS; + } + else{ + x509s = ossl_protect_x509_ary2sk(certs, &status); + if(status) rb_jump_tag(status); + } + ret = OCSP_basic_sign(bs, signer, key, EVP_sha1(), x509s, flg); + sk_X509_pop_free(x509s, X509_free); + if(!ret) ossl_raise(eOCSPError, NULL); + + return self; +} + +static VALUE +ossl_ocspbres_verify(int argc, VALUE *argv, VALUE self) +{ + VALUE certs, store, flags; + OCSP_BASICRESP *bs; + STACK_OF(X509) *x509s; + X509_STORE *x509st; + int flg, result; + + rb_scan_args(argc, argv, "21", &certs, &store, &flags); + GetOCSPBasicRes(self, bs); + x509st = GetX509StorePtr(store); + flg = NIL_P(flags) ? 0 : INT2NUM(flags); + x509s = ossl_x509_ary2sk(certs); + result = OCSP_basic_verify(bs, x509s, x509st, flg); + sk_X509_pop_free(x509s, X509_free); + if(!result) rb_warn("%s", ERR_error_string(ERR_peek_error(), NULL)); + + return result ? Qtrue : Qfalse; +} + +/* + * OCSP::CertificateId + */ +static VALUE +ossl_ocspcid_alloc(VALUE klass) +{ + OCSP_CERTID *id; + VALUE obj; + + if(!(id = OCSP_CERTID_new())) + ossl_raise(eOCSPError, NULL); + WrapOCSPCertId(klass, obj, id); + + return obj; +} +DEFINE_ALLOC_WRAPPER(ossl_ocspcid_alloc) + +static VALUE +ossl_ocspcid_initialize(VALUE self, VALUE subject, VALUE issuer) +{ + OCSP_CERTID *id, *newid; + X509 *x509s, *x509i; + + GetOCSPCertId(self, id); + x509s = GetX509CertPtr(subject); /* NO NEED TO DUP */ + x509i = GetX509CertPtr(issuer); /* NO NEED TO DUP */ + if(!(newid = OCSP_cert_to_id(NULL, x509s, x509i))) + ossl_raise(eOCSPError, NULL); + OCSP_CERTID_free(id); + RDATA(self)->data = newid; + + return self; +} + +static VALUE +ossl_ocspcid_cmp(VALUE self, VALUE other) +{ + OCSP_CERTID *id, *id2; + int result; + + GetOCSPCertId(self, id); + SafeGetOCSPCertId(other, id2); + result = OCSP_id_cmp(id, id2); + + return (result == 0) ? Qtrue : Qfalse; +} + +static VALUE +ossl_ocspcid_cmp_issuer(VALUE self, VALUE other) +{ + OCSP_CERTID *id, *id2; + int result; + + GetOCSPCertId(self, id); + SafeGetOCSPCertId(other, id2); + result = OCSP_id_issuer_cmp(id, id2); + + return (result == 0) ? Qtrue : Qfalse; +} + +static VALUE +ossl_ocspcid_get_serial(VALUE self) +{ + OCSP_CERTID *id; + + GetOCSPCertId(self, id); + + return asn1integer_to_num(id->serialNumber); +} + +void +Init_ossl_ocsp() +{ + mOCSP = rb_define_module_under(mOSSL, "OCSP"); + + eOCSPError = rb_define_class_under(mOCSP, "OCSPError", rb_cObject); + + cOCSPReq = rb_define_class_under(mOCSP, "Request", rb_cObject); + rb_define_alloc_func(cOCSPReq, ossl_ocspreq_alloc); + rb_define_method(cOCSPReq, "initialize", ossl_ocspreq_initialize, -1); + rb_define_method(cOCSPReq, "add_nonce", ossl_ocspreq_add_nonce, -1); + rb_define_method(cOCSPReq, "check_nonce", ossl_ocspreq_check_nonce, 1); + rb_define_method(cOCSPReq, "add_certid", ossl_ocspreq_add_certid, 1); + rb_define_method(cOCSPReq, "certid", ossl_ocspreq_get_certid, 0); + rb_define_method(cOCSPReq, "sign", ossl_ocspreq_sign, -1); + rb_define_method(cOCSPReq, "verify", ossl_ocspreq_verify, -1); + rb_define_method(cOCSPReq, "to_der", ossl_ocspreq_to_der, 0); + + cOCSPRes = rb_define_class_under(mOCSP, "Response", rb_cObject); + rb_define_singleton_method(cOCSPRes, "create", ossl_ocspres_s_create, 2); + rb_define_alloc_func(cOCSPRes, ossl_ocspres_alloc); + rb_define_method(cOCSPRes, "initialize", ossl_ocspres_initialize, -1); + rb_define_method(cOCSPRes, "status", ossl_ocspres_status, 0); + rb_define_method(cOCSPRes, "status_string", ossl_ocspres_status_string, 0); + rb_define_method(cOCSPRes, "basic", ossl_ocspres_get_basic, 0); + rb_define_method(cOCSPRes, "to_der", ossl_ocspres_to_der, 0); + + cOCSPBasicRes = rb_define_class_under(mOCSP, "BasicResponse", rb_cObject); + rb_define_alloc_func(cOCSPBasicRes, ossl_ocspbres_alloc); + rb_define_method(cOCSPBasicRes, "initialize", ossl_ocspbres_initialize, -1); + rb_define_method(cOCSPBasicRes, "copy_nonce", ossl_ocspbres_copy_nonce, 1); + rb_define_method(cOCSPBasicRes, "add_nonce", ossl_ocspbres_add_nonce, -1); + rb_define_method(cOCSPBasicRes, "add_status", ossl_ocspbres_add_status, 7); + rb_define_method(cOCSPBasicRes, "status", ossl_ocspbres_get_status, 0); + rb_define_method(cOCSPBasicRes, "sign", ossl_ocspbres_sign, -1); + rb_define_method(cOCSPBasicRes, "verify", ossl_ocspbres_verify, -1); + + cOCSPCertId = rb_define_class_under(mOCSP, "CertificateId", rb_cObject); + rb_define_alloc_func(cOCSPCertId, ossl_ocspcid_alloc); + rb_define_method(cOCSPCertId, "initialize", ossl_ocspcid_initialize, 2); + rb_define_method(cOCSPCertId, "cmp", ossl_ocspcid_cmp, 1); + rb_define_method(cOCSPCertId, "cmp_issuer", ossl_ocspcid_cmp_issuer, 1); + rb_define_method(cOCSPCertId, "serial", ossl_ocspcid_get_serial, 0); + +#define DefOCSPConst(x) rb_define_const(mOCSP, #x, INT2NUM(OCSP_##x)) + + DefOCSPConst(RESPONSE_STATUS_SUCCESSFUL); + DefOCSPConst(RESPONSE_STATUS_MALFORMEDREQUEST); + DefOCSPConst(RESPONSE_STATUS_INTERNALERROR); + DefOCSPConst(RESPONSE_STATUS_TRYLATER); + DefOCSPConst(RESPONSE_STATUS_SIGREQUIRED); + DefOCSPConst(RESPONSE_STATUS_UNAUTHORIZED); + + DefOCSPConst(REVOKED_STATUS_NOSTATUS); + DefOCSPConst(REVOKED_STATUS_UNSPECIFIED); + DefOCSPConst(REVOKED_STATUS_KEYCOMPROMISE); + DefOCSPConst(REVOKED_STATUS_CACOMPROMISE); + DefOCSPConst(REVOKED_STATUS_AFFILIATIONCHANGED); + DefOCSPConst(REVOKED_STATUS_SUPERSEDED); + DefOCSPConst(REVOKED_STATUS_CESSATIONOFOPERATION); + DefOCSPConst(REVOKED_STATUS_CERTIFICATEHOLD); + DefOCSPConst(REVOKED_STATUS_REMOVEFROMCRL); + + DefOCSPConst(NOCERTS); + DefOCSPConst(NOINTERN); + DefOCSPConst(NOSIGS); + DefOCSPConst(NOCHAIN); + DefOCSPConst(NOVERIFY); + DefOCSPConst(NOEXPLICIT); + DefOCSPConst(NOCASIGN); + DefOCSPConst(NODELEGATED); + DefOCSPConst(NOCHECKS); + DefOCSPConst(TRUSTOTHER); + DefOCSPConst(RESPID_KEY); + DefOCSPConst(NOTIME); + +#define DefOCSPVConst(x) rb_define_const(mOCSP, "V_" #x, INT2NUM(V_OCSP_##x)) + + DefOCSPVConst(CERTSTATUS_GOOD); + DefOCSPVConst(CERTSTATUS_REVOKED); + DefOCSPVConst(CERTSTATUS_UNKNOWN); + DefOCSPVConst(RESPID_NAME); + DefOCSPVConst(RESPID_KEY); +} + +#else /* ! OSSL_OCSP_ENABLED */ +void +Init_ossl_ocsp() +{ +} +#endif diff --git a/ext/openssl/ossl_ocsp.h b/ext/openssl/ossl_ocsp.h new file mode 100644 index 0000000000..65b4f2e23f --- /dev/null +++ b/ext/openssl/ossl_ocsp.h @@ -0,0 +1,24 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2003 Michal Rokos + * Copyright (C) 2003 GOTOU Yuuzou + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#if !defined(_OSSL_OCSP_H_) +#define _OSSL_OCSP_H_ + +#if defined(OSSL_OCSP_ENABLED) +extern VALUE mOCSP; +extern VALUE cOPCSReq; +extern VALUE cOPCSRes; +extern VALUE cOPCSBasicRes; +#endif + +void Init_ossl_ocsp(void); + +#endif /* _OSSL_OCSP_H_ */ diff --git a/ext/openssl/ossl_pkcs7.c b/ext/openssl/ossl_pkcs7.c new file mode 100644 index 0000000000..e8efdf4b69 --- /dev/null +++ b/ext/openssl/ossl_pkcs7.c @@ -0,0 +1,775 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" + +#define WrapPKCS7(klass, obj, pkcs7) do { \ + if (!pkcs7) { \ + ossl_raise(rb_eRuntimeError, "PKCS7 wasn't initialized."); \ + } \ + obj = Data_Wrap_Struct(klass, 0, PKCS7_free, pkcs7); \ +} while (0) +#define GetPKCS7(obj, pkcs7) do { \ + Data_Get_Struct(obj, PKCS7, pkcs7); \ + if (!pkcs7) { \ + ossl_raise(rb_eRuntimeError, "PKCS7 wasn't initialized."); \ + } \ +} while (0) +#define SafeGetPKCS7(obj, pkcs7) do { \ + OSSL_Check_Kind(obj, cPKCS7); \ + GetPKCS7(obj, pkcs7); \ +} while (0) + +#define WrapPKCS7si(klass, obj, p7si) do { \ + if (!p7si) { \ + ossl_raise(rb_eRuntimeError, "PKCS7si wasn't initialized."); \ + } \ + obj = Data_Wrap_Struct(klass, 0, PKCS7_SIGNER_INFO_free, p7si); \ +} while (0) +#define GetPKCS7si(obj, p7si) do { \ + Data_Get_Struct(obj, PKCS7_SIGNER_INFO, p7si); \ + if (!p7si) { \ + ossl_raise(rb_eRuntimeError, "PKCS7si wasn't initialized."); \ + } \ +} while (0) +#define SafeGetPKCS7si(obj, p7si) do { \ + OSSL_Check_Kind(obj, cPKCS7Signer); \ + GetPKCS7si(obj, p7si); \ +} while (0) + +#define numberof(ary) (sizeof(ary)/sizeof(ary[0])) + +#define ossl_pkcs7_set_data(o,v) rb_iv_set((o), "@data", (v)) +#define ossl_pkcs7_get_data(o) rb_iv_get((o), "@data") +#define ossl_pkcs7_set_err_string(o,v) rb_iv_set((o), "@error_string", (v)) +#define ossl_pkcs7_get_err_string(o) rb_iv_get((o), "@error_string") + +/* + * Classes + */ +VALUE mPKCS7; +VALUE cPKCS7; +VALUE cPKCS7Signer; +VALUE ePKCS7Error; + +/* + * Public + * (MADE PRIVATE UNTIL SOMEBODY WILL NEED THEM) + */ +static VALUE +ossl_pkcs7si_new(PKCS7_SIGNER_INFO *p7si) +{ + PKCS7_SIGNER_INFO *pkcs7; + VALUE obj; + + pkcs7 = p7si ? PKCS7_SIGNER_INFO_dup(p7si) : PKCS7_SIGNER_INFO_new(); + if (!pkcs7) ossl_raise(ePKCS7Error, NULL); + WrapPKCS7si(cPKCS7Signer, obj, pkcs7); + + return obj; +} + +static PKCS7_SIGNER_INFO * +DupPKCS7SignerPtr(VALUE obj) +{ + PKCS7_SIGNER_INFO *p7si, *pkcs7; + + SafeGetPKCS7si(obj, p7si); + if (!(pkcs7 = PKCS7_SIGNER_INFO_dup(p7si))) { + ossl_raise(ePKCS7Error, NULL); + } + + return pkcs7; +} + +/* + * Private + */ +static VALUE +ossl_pkcs7_s_read_smime(VALUE klass, VALUE arg) +{ + BIO *in, *out; + PKCS7 *pkcs7; + VALUE ret, data; + int status = 0; + + in = ossl_obj2bio(arg); + out = NULL; + if((pkcs7 = SMIME_read_PKCS7(in, &out)) == NULL){ + BIO_free(in); + BIO_free(out); + ossl_raise(ePKCS7Error, NULL); + } + if(out) data = ossl_protect_membio2str(out, &status); + else data = Qnil; + BIO_free(in); + BIO_free(out); + if(status) rb_jump_tag(status); + WrapPKCS7(cPKCS7, ret, pkcs7); + ossl_pkcs7_set_data(ret, data); + ossl_pkcs7_set_err_string(ret, Qnil); + + return ret; +} + +static VALUE +ossl_pkcs7_s_write_smime(int argc, VALUE *argv, VALUE klass) +{ + VALUE pkcs7, data, flags; + BIO *out; + BIO *in; + PKCS7 *p7; + VALUE str; + int flg, status = 0; + + rb_scan_args(argc, argv, "12", &pkcs7, &data, &flags); + SafeGetPKCS7(pkcs7, p7); + flg = NIL_P(flags) ? 0 : NUM2INT(flags); + if(NIL_P(data)) data = ossl_pkcs7_get_data(pkcs7); + if(!NIL_P(data) && PKCS7_is_detached(p7)) + flg |= PKCS7_DETACHED; + in = NIL_P(data) ? NULL : ossl_obj2bio(data); + if(!(out = BIO_new(BIO_s_mem()))){ + BIO_free(in); + ossl_raise(ePKCS7Error, NULL); + } + if(!SMIME_write_PKCS7(out, p7, in, flg)){ + BIO_free(out); + BIO_free(in); + ossl_raise(ePKCS7Error, NULL); + } + str = ossl_protect_membio2str(out, &status); + BIO_free(in); + BIO_free(out); + if(status) rb_jump_tag(status); + + return str; +} + +static VALUE +ossl_pkcs7_s_sign(int argc, VALUE *argv, VALUE klass) +{ + VALUE cert, key, data, certs, flags; + X509 *x509; + EVP_PKEY *pkey; + BIO *in; + STACK_OF(X509) *x509s; + int flg, status = 0; + PKCS7 *pkcs7; + VALUE ret; + + rb_scan_args(argc, argv, "32", &cert, &key, &data, &certs, &flags); + x509 = GetX509CertPtr(cert); /* NO NEED TO DUP */ + pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */ + flg = NIL_P(flags) ? 0 : NUM2INT(flags); + in = ossl_obj2bio(data); + if(NIL_P(certs)) x509s = NULL; + else{ + x509s = ossl_protect_x509_ary2sk(certs, &status); + if(status){ + BIO_free(in); + rb_jump_tag(status); + } + } + if(!(pkcs7 = PKCS7_sign(x509, pkey, x509s, in, flg))){ + BIO_free(in); + sk_X509_pop_free(x509s, X509_free); + ossl_raise(ePKCS7Error, NULL); + } + WrapPKCS7(cPKCS7, ret, pkcs7); + ossl_pkcs7_set_data(ret, data); + ossl_pkcs7_set_err_string(ret, Qnil); + BIO_free(in); + sk_X509_pop_free(x509s, X509_free); + + return ret; +} + +static VALUE +ossl_pkcs7_s_encrypt(int argc, VALUE *argv, VALUE klass) +{ + VALUE certs, data, cipher, flags; + STACK_OF(X509) *x509s; + BIO *in; + const EVP_CIPHER *ciph; + int flg, status = 0; + VALUE ret; + PKCS7 *p7; + + rb_scan_args(argc, argv, "22", &certs, &data, &cipher, &flags); + if(NIL_P(cipher)){ +#if !defined(OPENSSL_NO_RC2) + ciph = EVP_rc2_40_cbc(); +#elif !defined(OPENSSL_NO_DES) + ciph = EVP_des_ede3_cbc(); +#elif !defined(OPENSSL_NO_RC2) + ciph = EVP_rc2_40_cbc(); +#elif !defined(OPENSSL_NO_AES) + ciph = EVP_EVP_aes_128_cbc(); +#else + ossl_raise(ePKCS7Error, "Must specify cipher"); +#endif + + } + else ciph = GetCipherPtr(cipher); /* NO NEED TO DUP */ + flg = NIL_P(flags) ? 0 : NUM2INT(flags); + in = ossl_obj2bio(data); + x509s = ossl_protect_x509_ary2sk(certs, &status); + if(status){ + BIO_free(in); + rb_jump_tag(status); + } + if(!(p7 = PKCS7_encrypt(x509s, in, ciph, flg))){ + BIO_free(in); + sk_X509_pop_free(x509s, X509_free); + ossl_raise(ePKCS7Error, NULL); + } + WrapPKCS7(cPKCS7, ret, p7); + ossl_pkcs7_set_data(ret, data); + BIO_free(in); + sk_X509_pop_free(x509s, X509_free); + + return ret; +} + +static VALUE +ossl_pkcs7_alloc(VALUE klass) +{ + PKCS7 *pkcs7; + VALUE obj; + + if (!(pkcs7 = PKCS7_new())) { + ossl_raise(ePKCS7Error, NULL); + } + WrapPKCS7(klass, obj, pkcs7); + + return obj; +} +DEFINE_ALLOC_WRAPPER(ossl_pkcs7_alloc) + +static VALUE +ossl_pkcs7_initialize(int argc, VALUE *argv, VALUE self) +{ + BIO *in; + VALUE s; + + if(rb_scan_args(argc, argv, "01", &s) == 0) + return self; + in = ossl_obj2bio(s); + + if (!PEM_read_bio_PKCS7(in, (PKCS7 **)&DATA_PTR(self), NULL, NULL)) { + BIO_free(in); + ossl_raise(ePKCS7Error, NULL); + } + BIO_free(in); + ossl_pkcs7_set_data(self, Qnil); + ossl_pkcs7_set_err_string(self, Qnil); + + return self; +} + +static VALUE +ossl_pkcs7_copy(VALUE self, VALUE other) +{ + PKCS7 *a, *b, *pkcs7; + + rb_check_frozen(self); + if (self == other) return self; + + GetPKCS7(self, a); + SafeGetPKCS7(other, b); + + pkcs7 = PKCS7_dup(b); + if (!pkcs7) { + ossl_raise(ePKCS7Error, NULL); + } + DATA_PTR(self) = pkcs7; + PKCS7_free(a); + + return self; +} + +static int +ossl_pkcs7_sym2typeid(VALUE sym) +{ + int i, ret = Qnil; + char *s; + + static struct { + const char *name; + int nid; + } p7_type_tab[] = { + { "signed", NID_pkcs7_signed }, + { "data", NID_pkcs7_data }, + { "signedAndEnveloped", NID_pkcs7_signedAndEnveloped }, + { "enveloped", NID_pkcs7_enveloped }, + { "encrypted", NID_pkcs7_encrypted }, + { "digest", NID_pkcs7_digest }, + { NULL, 0 }, + }; + + if(TYPE(sym) == T_SYMBOL) s = rb_id2name(SYM2ID(sym)); + else s = StringValuePtr(sym); + for(i = 0; i < numberof(p7_type_tab); i++){ + if(p7_type_tab[i].name == NULL) + ossl_raise(ePKCS7Error, "unknown type \"%s\"", s); + if(strcmp(p7_type_tab[i].name, s) == 0){ + ret = p7_type_tab[i].nid; + break; + } + } + + return ret; +} + +static VALUE +ossl_pkcs7_set_type(VALUE self, VALUE type) +{ + PKCS7 *p7; + + GetPKCS7(self, p7); + if(!PKCS7_set_type(p7, ossl_pkcs7_sym2typeid(type))) + ossl_raise(ePKCS7Error, NULL); + + return type; +} + +static VALUE +ossl_pkcs7_get_type(VALUE self) +{ + PKCS7 *p7; + + GetPKCS7(self, p7); + if(PKCS7_type_is_signed(p7)) + return ID2SYM(rb_intern("signed")); + if(PKCS7_type_is_encrypted(p7)) + return ID2SYM(rb_intern("encrypted")); + if(PKCS7_type_is_enveloped(p7)) + return ID2SYM(rb_intern("enveloped")); + if(PKCS7_type_is_signedAndEnveloped(p7)) + return ID2SYM(rb_intern("signedAndEnveloped")); + if(PKCS7_type_is_data(p7)) + return ID2SYM(rb_intern("data")); + return Qnil; +} + +static VALUE +ossl_pkcs7_set_detached(VALUE self, VALUE flag) +{ + PKCS7 *p7; + + GetPKCS7(self, p7); + if(flag != Qtrue && flag != Qfalse) + ossl_raise(ePKCS7Error, "must secify a boolean"); + if(!PKCS7_set_detached(p7, flag == Qtrue ? 1 : 0)) + ossl_raise(ePKCS7Error, NULL); + + return flag; +} + +static VALUE +ossl_pkcs7_get_detached(VALUE self) +{ + PKCS7 *p7; + GetPKCS7(self, p7); + return PKCS7_get_detached(p7) ? Qtrue : Qfalse; +} + +static VALUE +ossl_pkcs7_detached_p(VALUE self) +{ + PKCS7 *p7; + GetPKCS7(self, p7); + return PKCS7_is_detached(p7) ? Qtrue : Qfalse; +} + +static VALUE +ossl_pkcs7_set_cipher(VALUE self, VALUE cipher) +{ + PKCS7 *pkcs7; + + GetPKCS7(self, pkcs7); + if (!PKCS7_set_cipher(pkcs7, GetCipherPtr(cipher))) { + ossl_raise(ePKCS7Error, NULL); + } + + return cipher; +} + +static VALUE +ossl_pkcs7_add_signer(VALUE self, VALUE signer) +{ + PKCS7 *pkcs7; + PKCS7_SIGNER_INFO *p7si; + + GetPKCS7(self, pkcs7); + p7si = DupPKCS7SignerPtr(signer); /* NEED TO DUP */ + if (!PKCS7_add_signer(pkcs7, p7si)) { + PKCS7_SIGNER_INFO_free(p7si); + ossl_raise(ePKCS7Error, "Could not add signer."); + } + if (PKCS7_type_is_signed(pkcs7)){ + PKCS7_add_signed_attribute(p7si, NID_pkcs9_contentType, + V_ASN1_OBJECT, OBJ_nid2obj(NID_pkcs7_data)); + } + + return self; +} + +static VALUE +ossl_pkcs7_get_signer(VALUE self) +{ + PKCS7 *pkcs7; + STACK_OF(PKCS7_SIGNER_INFO) *sk; + PKCS7_SIGNER_INFO *si; + int num, i; + VALUE ary; + + GetPKCS7(self, pkcs7); + if (!(sk = PKCS7_get_signer_info(pkcs7))) { + OSSL_Debug("OpenSSL::PKCS7#get_signer_info == NULL!"); + return rb_ary_new(); + } + if ((num = sk_PKCS7_SIGNER_INFO_num(sk)) < 0) { + ossl_raise(ePKCS7Error, "Negative number of signers!"); + } + ary = rb_ary_new2(num); + for (i=0; iissuer_and_serial->issuer); +} + +static VALUE +ossl_pkcs7si_get_serial(VALUE self) +{ + PKCS7_SIGNER_INFO *p7si; + + GetPKCS7si(self, p7si); + + return asn1integer_to_num(p7si->issuer_and_serial->serial); +} + +static VALUE +ossl_pkcs7si_get_signed_time(VALUE self) +{ + PKCS7_SIGNER_INFO *p7si; + ASN1_TYPE *asn1obj; + + GetPKCS7si(self, p7si); + + if (!(asn1obj = PKCS7_get_signed_attribute(p7si, NID_pkcs9_signingTime))) { + ossl_raise(ePKCS7Error, NULL); + } + if (asn1obj->type == V_ASN1_UTCTIME) { + return asn1time_to_time(asn1obj->value.utctime); + } + /* + * OR + * ossl_raise(ePKCS7Error, "..."); + * ? + */ + + return Qnil; +} + +/* + * INIT + */ +void +Init_ossl_pkcs7() +{ + mPKCS7 = rb_define_module_under(mOSSL, "PKCS7"); + + ePKCS7Error = rb_define_class_under(mPKCS7, "PKCS7Error", eOSSLError); + + cPKCS7 = rb_define_class_under(mPKCS7, "PKCS7", rb_cObject); + rb_define_singleton_method(mPKCS7, "read_smime", ossl_pkcs7_s_read_smime, 1); + rb_define_singleton_method(mPKCS7, "write_smime", ossl_pkcs7_s_write_smime, -1); + rb_define_singleton_method(mPKCS7, "sign", ossl_pkcs7_s_sign, -1); + rb_define_singleton_method(mPKCS7, "encrypt", ossl_pkcs7_s_encrypt, -1); + rb_attr(cPKCS7, rb_intern("data"), 1, 0, Qfalse); + rb_attr(cPKCS7, rb_intern("error_string"), 1, 1, Qfalse); + rb_define_alloc_func(cPKCS7, ossl_pkcs7_alloc); + rb_define_copy_func(cPKCS7, ossl_pkcs7_copy); + rb_define_method(cPKCS7, "initialize", ossl_pkcs7_initialize, -1); + rb_define_method(cPKCS7, "type=", ossl_pkcs7_set_type, 1); + rb_define_method(cPKCS7, "type", ossl_pkcs7_get_type, 0); + rb_define_method(cPKCS7, "detached=", ossl_pkcs7_set_detached, 1); + rb_define_method(cPKCS7, "detached", ossl_pkcs7_get_detached, 0); + rb_define_method(cPKCS7, "detached?", ossl_pkcs7_detached_p, 0); + rb_define_method(cPKCS7, "cipher=", ossl_pkcs7_set_cipher, 1); + rb_define_method(cPKCS7, "add_signer", ossl_pkcs7_add_signer, 1); + rb_define_method(cPKCS7, "signers", ossl_pkcs7_get_signer, 0); + rb_define_method(cPKCS7, "add_recipient", ossl_pkcs7_add_recipient, 1); + rb_define_method(cPKCS7, "add_certificate", ossl_pkcs7_add_certificate, 1); + rb_define_method(cPKCS7, "add_crl", ossl_pkcs7_add_crl, 1); + rb_define_method(cPKCS7, "add_data", ossl_pkcs7_add_data, 1); + rb_define_alias(cPKCS7, "data=", "add_data"); + rb_define_method(cPKCS7, "verify", ossl_pkcs7_verify, -1); + rb_define_method(cPKCS7, "decrypt", ossl_pkcs7_decrypt, -1); + rb_define_method(cPKCS7, "to_pem", ossl_pkcs7_to_pem, 0); + rb_define_alias(cPKCS7, "to_s", "to_pem"); + + cPKCS7Signer = rb_define_class_under(mPKCS7, "Signer", rb_cObject); + rb_define_alloc_func(cPKCS7Signer, ossl_pkcs7si_alloc); + rb_define_method(cPKCS7Signer, "initialize", ossl_pkcs7si_initialize,3); + rb_define_method(cPKCS7Signer, "name", ossl_pkcs7si_get_name,0); + rb_define_method(cPKCS7Signer, "serial", ossl_pkcs7si_get_serial,0); + rb_define_method(cPKCS7Signer, "signed_time", ossl_pkcs7si_get_signed_time,0); + +#define DefPKCS7Const(x) rb_define_const(mPKCS7, #x, INT2NUM(PKCS7_##x)) + + DefPKCS7Const(TEXT); + DefPKCS7Const(NOCERTS); + DefPKCS7Const(NOSIGS); + DefPKCS7Const(NOCHAIN); + DefPKCS7Const(NOINTERN); + DefPKCS7Const(NOVERIFY); + DefPKCS7Const(DETACHED); + DefPKCS7Const(BINARY); + DefPKCS7Const(NOATTR); + DefPKCS7Const(NOSMIMECAP); +} diff --git a/ext/openssl/ossl_pkcs7.h b/ext/openssl/ossl_pkcs7.h new file mode 100644 index 0000000000..9378397f25 --- /dev/null +++ b/ext/openssl/ossl_pkcs7.h @@ -0,0 +1,22 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#if !defined(_OSSL_PKCS7_H_) +#define _OSSL_PKCS7_H_ + +extern VALUE mPKCS7; +extern VALUE cPKCS7; +extern VALUE cPKCS7SignerInfo; +extern VALUE ePKCS7Error; + +void Init_ossl_pkcs7(void); + +#endif /* _OSSL_PKCS7_H_ */ + diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c new file mode 100644 index 0000000000..cd73355d66 --- /dev/null +++ b/ext/openssl/ossl_pkey.c @@ -0,0 +1,238 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" + +/* + * Classes + */ +VALUE mPKey; +VALUE cPKey; +VALUE ePKeyError; +ID id_private_q; + +/* + * Public + */ +VALUE +ossl_pkey_new(EVP_PKEY *pkey) +{ + if (!pkey) { + ossl_raise(ePKeyError, "Cannot make new key from NULL."); + } + switch (EVP_PKEY_type(pkey->type)) { +#if !defined(OPENSSL_NO_RSA) + case EVP_PKEY_RSA: + return ossl_rsa_new(pkey); +#endif +#if !defined(OPENSSL_NO_DSA) + case EVP_PKEY_DSA: + return ossl_dsa_new(pkey); +#endif +#if !defined(OPENSSL_NO_DH) + case EVP_PKEY_DH: + return ossl_dh_new(pkey); +#endif + default: + ossl_raise(ePKeyError, "unsupported key type"); + } + return Qnil; /* not reached */ +} + +VALUE +ossl_pkey_new_from_file(VALUE filename) +{ + FILE *fp; + EVP_PKEY *pkey; + + SafeStringValue(filename); + if (!(fp = fopen(RSTRING(filename)->ptr, "r"))) { + ossl_raise(ePKeyError, "%s", strerror(errno)); + } + + pkey = PEM_read_PrivateKey(fp, NULL, ossl_pem_passwd_cb, NULL); + fclose(fp); + if (!pkey) { + ossl_raise(ePKeyError, NULL); + } + + return ossl_pkey_new(pkey); +} + +EVP_PKEY * +GetPKeyPtr(VALUE obj) +{ + EVP_PKEY *pkey; + + SafeGetPKey(obj, pkey); + + return pkey; +} + +EVP_PKEY * +GetPrivPKeyPtr(VALUE obj) +{ + EVP_PKEY *pkey; + + SafeGetPKey(obj, pkey); + if (rb_funcall(obj, id_private_q, 0, NULL) != Qtrue) { /* returns Qtrue */ + ossl_raise(rb_eArgError, "Private key is needed."); + } + + return pkey; +} + +EVP_PKEY * +DupPrivPKeyPtr(VALUE obj) +{ + EVP_PKEY *pkey; + + SafeGetPKey(obj, pkey); + if (rb_funcall(obj, id_private_q, 0, NULL) != Qtrue) { /* returns Qtrue */ + ossl_raise(rb_eArgError, "Private key is needed."); + } + CRYPTO_add(&pkey->references, 1, CRYPTO_LOCK_EVP_PKEY); + + return pkey; +} + +/* + * Private + */ +static VALUE +ossl_pkey_alloc(VALUE klass) +{ + EVP_PKEY *pkey; + VALUE obj; + + if (!(pkey = EVP_PKEY_new())) { + ossl_raise(ePKeyError, NULL); + } + WrapPKey(klass, obj, pkey); + + return obj; +} +DEFINE_ALLOC_WRAPPER(ossl_pkey_alloc) + +static VALUE +ossl_pkey_initialize(VALUE self) +{ + if (rb_obj_is_instance_of(self, cPKey)) { + ossl_raise(rb_eNotImpError, "OpenSSL::PKey::PKey is an abstract class."); + } + return self; +} + +static VALUE +ossl_pkey_to_der(VALUE self) +{ + EVP_PKEY *pkey; + VALUE str; + BIO *out; + BUF_MEM *buf; + + GetPKey(self, pkey); + + out = BIO_new(BIO_s_mem()); + if (!out) ossl_raise(ePKeyError, NULL); + + if (!i2d_PUBKEY_bio(out, pkey)) { + BIO_free(out); + ossl_raise(ePKeyError, NULL); + } + + BIO_get_mem_ptr(out, &buf); + str = rb_str_new(buf->data, buf->length); + + BIO_free(out); + + return str; +} + +static VALUE +ossl_pkey_sign(VALUE self, VALUE digest, VALUE data) +{ + EVP_PKEY *pkey; + EVP_MD_CTX ctx; + char *buf; + int buf_len; + VALUE str; + + GetPKey(self, pkey); + if (rb_funcall(self, id_private_q, 0, NULL) != Qtrue) { + ossl_raise(rb_eArgError, "Private key is needed."); + } + EVP_SignInit(&ctx, GetDigestPtr(digest)); + StringValue(data); + EVP_SignUpdate(&ctx, RSTRING(data)->ptr, RSTRING(data)->len); + if (!(buf = OPENSSL_malloc(EVP_PKEY_size(pkey) + 16))) { + ossl_raise(ePKeyError, NULL); + } + if (!EVP_SignFinal(&ctx, buf, &buf_len, pkey)) { + OPENSSL_free(buf); + ossl_raise(ePKeyError, NULL); + } + str = rb_str_new(buf, buf_len); + OPENSSL_free(buf); + + return str; +} + +static VALUE +ossl_pkey_verify(VALUE self, VALUE digest, VALUE sig, VALUE data) +{ + EVP_PKEY *pkey; + EVP_MD_CTX ctx; + + GetPKey(self, pkey); + EVP_VerifyInit(&ctx, GetDigestPtr(digest)); + StringValue(sig); + StringValue(data); + EVP_VerifyUpdate(&ctx, RSTRING(data)->ptr, RSTRING(data)->len); + switch (EVP_VerifyFinal(&ctx, RSTRING(sig)->ptr, RSTRING(sig)->len, pkey)) { + case 0: + return Qfalse; + case 1: + return Qtrue; + default: + ossl_raise(ePKeyError, NULL); + } + return Qnil; /* dummy */ +} + +/* + * INIT + */ +void +Init_ossl_pkey() +{ + mPKey = rb_define_module_under(mOSSL, "PKey"); + + ePKeyError = rb_define_class_under(mPKey, "PKeyError", eOSSLError); + + cPKey = rb_define_class_under(mPKey, "PKey", rb_cObject); + + rb_define_alloc_func(cPKey, ossl_pkey_alloc); + rb_define_method(cPKey, "initialize", ossl_pkey_initialize, 0); + + rb_define_method(cPKey, "to_der", ossl_pkey_to_der, 0); + rb_define_method(cPKey, "sign", ossl_pkey_sign, 2); + rb_define_method(cPKey, "verify", ossl_pkey_verify, 3); + + id_private_q = rb_intern("private?"); + + /* + * INIT rsa, dsa + */ + Init_ossl_rsa(); + Init_ossl_dsa(); + Init_ossl_dh(); +} + diff --git a/ext/openssl/ossl_pkey.h b/ext/openssl/ossl_pkey.h new file mode 100644 index 0000000000..f3cf20c5d3 --- /dev/null +++ b/ext/openssl/ossl_pkey.h @@ -0,0 +1,111 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#if !defined(_OSSL_PKEY_H_) +#define _OSSL_PKEY_H_ + +extern VALUE mPKey; +extern VALUE cPKey; +extern VALUE ePKeyError; +extern ID id_private_q; + +#define WrapPKey(klass, obj, pkey) do { \ + if (!pkey) { \ + rb_raise(rb_eRuntimeError, "PKEY wasn't initialized!"); \ + } \ + obj = Data_Wrap_Struct(klass, 0, EVP_PKEY_free, pkey); \ +} while (0) +#define GetPKey(obj, pkey) do {\ + Data_Get_Struct(obj, EVP_PKEY, pkey);\ + if (!pkey) { \ + rb_raise(rb_eRuntimeError, "PKEY wasn't initialized!");\ + } \ +} while (0) +#define SafeGetPKey(obj, pkey) do { \ + OSSL_Check_Kind(obj, cPKey); \ + GetPKey(obj, pkey); \ +} while (0) + +VALUE ossl_pkey_new(EVP_PKEY *); +VALUE ossl_pkey_new_from_file(VALUE); +EVP_PKEY *GetPKeyPtr(VALUE); +/*EVP_PKEY *DupPKeyPtr(VALUE);*/ +EVP_PKEY *GetPrivPKeyPtr(VALUE); +EVP_PKEY *DupPrivPKeyPtr(VALUE); +void Init_ossl_pkey(void); + +/* + * RSA + */ +extern VALUE cRSA; +extern VALUE eRSAError; + +VALUE ossl_rsa_new(EVP_PKEY *); +void Init_ossl_rsa(void); + +/* + * DSA + */ +extern VALUE cDSA; +extern VALUE eDSAError; + +VALUE ossl_dsa_new(EVP_PKEY *); +void Init_ossl_dsa(void); + +/* + * DH + */ +extern VALUE cDH; +extern VALUE eDHError; + +VALUE ossl_dh_new(EVP_PKEY *); +void Init_ossl_dh(void); + +#define OSSL_PKEY_BN(keytype, name) \ +static VALUE ossl_##keytype##_get_##name(VALUE self) \ +{ \ + EVP_PKEY *pkey; \ + BIGNUM *bn; \ + \ + GetPKey(self, pkey); \ + bn = pkey->pkey.keytype->name; \ + if (bn == NULL) \ + return Qnil; \ + return ossl_bn_new(bn); \ +} \ +static VALUE ossl_##keytype##_set_##name(VALUE self, VALUE bignum) \ +{ \ + EVP_PKEY *pkey; \ + BIGNUM *bn; \ + \ + GetPKey(self, pkey); \ + if (NIL_P(bignum)) { \ + BN_clear_free(pkey->pkey.keytype->name); \ + pkey->pkey.keytype->name = NULL; \ + return Qnil; \ + } \ + \ + bn = GetBNPtr(bignum); \ + if (pkey->pkey.keytype->name == NULL) \ + pkey->pkey.keytype->name = BN_new(); \ + if (pkey->pkey.keytype->name == NULL) \ + ossl_raise(eBNError, NULL); \ + if (BN_copy(pkey->pkey.keytype->name, bn) == NULL) \ + ossl_raise(eBNError, NULL); \ + return bignum; \ +} + +#define DEF_OSSL_PKEY_BN(class, keytype, name) \ +do { \ + rb_define_method(class, #name, ossl_##keytype##_get_##name, 0); \ + rb_define_method(class, #name "=", ossl_##keytype##_set_##name, 1); \ +} while (0) + +#endif /* _OSSL_PKEY_H_ */ diff --git a/ext/openssl/ossl_pkey_dh.c b/ext/openssl/ossl_pkey_dh.c new file mode 100644 index 0000000000..9a6bec15eb --- /dev/null +++ b/ext/openssl/ossl_pkey_dh.c @@ -0,0 +1,386 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#if !defined(OPENSSL_NO_DH) + +#include "ossl.h" + +#define GetPKeyDH(obj, pkey) do { \ + GetPKey(obj, pkey); \ + if (EVP_PKEY_type(pkey->type) != EVP_PKEY_DH) { /* PARANOIA? */ \ + ossl_raise(rb_eRuntimeError, "THIS IS NOT A DH!") ; \ + } \ +} while (0) + +#define DH_PRIVATE(dh) ((dh)->priv_key) + +/* + * Classes + */ +VALUE cDH; +VALUE eDHError; + +/* + * Public + */ +static VALUE +dh_instance(VALUE klass, DH *dh) +{ + EVP_PKEY *pkey; + VALUE obj; + + if (!dh) { + return Qfalse; + } + if (!(pkey = EVP_PKEY_new())) { + return Qfalse; + } + if (!EVP_PKEY_assign_DH(pkey, dh)) { + EVP_PKEY_free(pkey); + return Qfalse; + } + WrapPKey(klass, obj, pkey); + + return obj; +} + +VALUE +ossl_dh_new(EVP_PKEY *pkey) +{ + VALUE obj; + + if (!pkey) { + obj = dh_instance(cDH, DH_new()); + } else { + if (EVP_PKEY_type(pkey->type) != EVP_PKEY_DH) { + ossl_raise(rb_eTypeError, "Not a DH key!"); + } + WrapPKey(cDH, obj, pkey); + } + if (obj == Qfalse) { + ossl_raise(eDHError, NULL); + } + + return obj; +} + +/* + * Private + */ +/* + * CB for yielding when generating DH params + */ +static void +ossl_dh_generate_cb(int p, int n, void *arg) +{ + VALUE ary; + + ary = rb_ary_new2(2); + rb_ary_store(ary, 0, INT2NUM(p)); + rb_ary_store(ary, 1, INT2NUM(n)); + + rb_yield(ary); +} + +static DH * +dh_generate(int size, int gen) +{ + DH *dh; + void (*cb)(int, int, void *) = NULL; + + if (rb_block_given_p()) { + cb = ossl_dh_generate_cb; + } + /* arg to cb = NULL */ + if (!(dh = DH_generate_parameters(size, gen, cb, NULL))) { + return 0; + } + if (!DH_generate_key(dh)) { + DH_free(dh); + return 0; + } + + return dh; +} + +static VALUE +ossl_dh_s_generate(int argc, VALUE *argv, VALUE klass) +{ + DH *dh ; + int g = 2; + VALUE size, gen, obj; + + if (rb_scan_args(argc, argv, "11", &size, &gen) == 2) { + g = FIX2INT(gen); + } + dh = dh_generate(FIX2INT(size), g); + obj = dh_instance(klass, dh); + if (obj == Qfalse) { + DH_free(dh); + ossl_raise(eDHError, NULL); + } + + return obj; +} + +static VALUE +ossl_dh_initialize(int argc, VALUE *argv, VALUE self) +{ + EVP_PKEY *pkey; + DH *dh; + int g = 2; + BIO *in; + VALUE buffer, gen; + + GetPKey(self, pkey); + rb_scan_args(argc, argv, "11", &buffer, &gen); + if (FIXNUM_P(buffer)) { + if (!NIL_P(gen)) { + g = FIX2INT(gen); + } + if (!(dh = dh_generate(FIX2INT(buffer), g))) { + ossl_raise(eDHError, NULL); + } + } else { + StringValue(buffer); + in = BIO_new_mem_buf(RSTRING(buffer)->ptr, RSTRING(buffer)->len); + if (!in){ + ossl_raise(eDHError, NULL); + } + if (!(dh = PEM_read_bio_DHparams(in, NULL, NULL, NULL))) { + BIO_free(in); + ossl_raise(eDHError, NULL); + } + BIO_free(in); + } + if (!EVP_PKEY_assign_DH(pkey, dh)) { + DH_free(dh); + ossl_raise(eRSAError, NULL); + } + return self; +} + +static VALUE +ossl_dh_is_public(VALUE self) +{ + EVP_PKEY *pkey; + + GetPKeyDH(self, pkey); + /* + * Do we need to check dhp->dh->public_pkey? + * return Qtrue; + */ + return (pkey->pkey.dh->pub_key) ? Qtrue : Qfalse; +} + +static VALUE +ossl_dh_is_private(VALUE self) +{ + EVP_PKEY *pkey; + + GetPKeyDH(self, pkey); + + return (DH_PRIVATE(pkey->pkey.dh)) ? Qtrue : Qfalse; +} + +static VALUE +ossl_dh_export(VALUE self) +{ + EVP_PKEY *pkey; + BIO *out; + BUF_MEM *buf; + VALUE str; + + GetPKeyDH(self, pkey); + if (!(out = BIO_new(BIO_s_mem()))) { + ossl_raise(eDHError, NULL); + } + if (!PEM_write_bio_DHparams(out, pkey->pkey.dh)) { + BIO_free(out); + ossl_raise(eDHError, NULL); + } + BIO_get_mem_ptr(out, &buf); + str = rb_str_new(buf->data, buf->length); + BIO_free(out); + + return str; +} + +/* + * Stores all parameters of key to the hash + * INSECURE: PRIVATE INFORMATIONS CAN LEAK OUT!!! + * Don't use :-)) (I's up to you) + */ +static VALUE +ossl_dh_get_params(VALUE self) +{ + EVP_PKEY *pkey; + VALUE hash; + + GetPKeyDH(self, pkey); + + hash = rb_hash_new(); + + rb_hash_aset(hash, rb_str_new2("p"), ossl_bn_new(pkey->pkey.dh->p)); + rb_hash_aset(hash, rb_str_new2("g"), ossl_bn_new(pkey->pkey.dh->g)); + rb_hash_aset(hash, rb_str_new2("pub_key"), ossl_bn_new(pkey->pkey.dh->pub_key)); + rb_hash_aset(hash, rb_str_new2("priv_key"), ossl_bn_new(pkey->pkey.dh->priv_key)); + + return hash; +} + +/* + * Prints all parameters of key to buffer + * INSECURE: PRIVATE INFORMATIONS CAN LEAK OUT!!! + * Don't use :-)) (I's up to you) + */ +static VALUE +ossl_dh_to_text(VALUE self) +{ + EVP_PKEY *pkey; + BIO *out; + BUF_MEM *buf; + VALUE str; + + GetPKeyDH(self, pkey); + if (!(out = BIO_new(BIO_s_mem()))) { + ossl_raise(eDHError, NULL); + } + if (!DHparams_print(out, pkey->pkey.dh)) { + BIO_free(out); + ossl_raise(eDHError, NULL); + } + BIO_get_mem_ptr(out, &buf); + str = rb_str_new(buf->data, buf->length); + BIO_free(out); + + return str; +} + +/* + * Makes new instance DH PUBLIC_KEY from PRIVATE_KEY + */ +static VALUE +ossl_dh_to_public_key(VALUE self) +{ + EVP_PKEY *pkey; + DH *dh; + VALUE obj; + + GetPKeyDH(self, pkey); + dh = DHparams_dup(pkey->pkey.dh); /* err check perfomed by dh_instance */ + obj = dh_instance(CLASS_OF(self), dh); + if (obj == Qfalse) { + DH_free(dh); + ossl_raise(eDHError, NULL); + } + + return obj; +} + +static VALUE +ossl_dh_check_params(VALUE self) +{ + DH *dh; + EVP_PKEY *pkey; + int codes; + + GetPKeyDH(self, pkey); + dh = pkey->pkey.dh; + + if (!DH_check(dh, &codes)) { + return Qfalse; + } + + return codes == 0 ? Qtrue : Qfalse; +} + +static VALUE +ossl_dh_generate_key(VALUE self) +{ + DH *dh; + EVP_PKEY *pkey; + + GetPKeyDH(self, pkey); + dh = pkey->pkey.dh; + + if (!DH_generate_key(dh)) + ossl_raise(eDHError, "Failed to generate key"); + return self; +} + +static VALUE +ossl_dh_compute_key(VALUE self, VALUE pub) +{ + DH *dh; + EVP_PKEY *pkey; + BIGNUM *pub_key; + VALUE str; + int len; + char *buf; + + GetPKeyDH(self, pkey); + dh = pkey->pkey.dh; + pub_key = GetBNPtr(pub); + + len = DH_size(dh); + if (!(buf = OPENSSL_malloc(len))) { + ossl_raise(eDHError, "Cannot allocate mem for shared secret"); + } + + if ((len = DH_compute_key(buf, pub_key, dh)) < 0) { + OPENSSL_free(buf); + ossl_raise(eDHError, NULL); + } + + str = rb_str_new(buf, len); + OPENSSL_free(buf); + + return str; +} + +/* + * INIT + */ +void +Init_ossl_dh() +{ + eDHError = rb_define_class_under(mPKey, "DHError", ePKeyError); + + cDH = rb_define_class_under(mPKey, "DH", cPKey); + + rb_define_singleton_method(cDH, "generate", ossl_dh_s_generate, -1); + rb_define_method(cDH, "initialize", ossl_dh_initialize, -1); + + rb_define_method(cDH, "public?", ossl_dh_is_public, 0); + rb_define_method(cDH, "private?", ossl_dh_is_private, 0); + rb_define_method(cDH, "to_text", ossl_dh_to_text, 0); + rb_define_method(cDH, "export", ossl_dh_export, 0); + rb_define_alias(cDH, "to_pem", "export"); + rb_define_alias(cDH, "to_s", "export"); + rb_define_method(cDH, "public_key", ossl_dh_to_public_key, 0); + + rb_define_method(cDH, "params_ok?", ossl_dh_check_params, 0); + rb_define_method(cDH, "generate_key!", ossl_dh_generate_key, 0); + rb_define_method(cDH, "compute_key", ossl_dh_compute_key, 1); + + rb_define_method(cDH, "params", ossl_dh_get_params, 0); +} + +#else /* defined NO_DH */ +# warning >>> OpenSSL is compiled without DH support <<< + +void +Init_ossl_dh() +{ + rb_warning("OpenSSL is compiled without DH support"); +} +#endif /* NO_DH */ + diff --git a/ext/openssl/ossl_pkey_dsa.c b/ext/openssl/ossl_pkey_dsa.c new file mode 100644 index 0000000000..73ab47b2c9 --- /dev/null +++ b/ext/openssl/ossl_pkey_dsa.c @@ -0,0 +1,404 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#if !defined(OPENSSL_NO_DSA) + +#include "ossl.h" + +#define GetPKeyDSA(obj, pkey) do { \ + GetPKey(obj, pkey); \ + if (EVP_PKEY_type(pkey->type) != EVP_PKEY_DSA) { /* PARANOIA? */ \ + ossl_raise(rb_eRuntimeError, "THIS IS NOT A DSA!"); \ + } \ +} while (0) + +#define DSA_PRIVATE(dsa) ((dsa)->priv_key) + +/* + * Classes + */ +VALUE cDSA; +VALUE eDSAError; + +/* + * Public + */ +static VALUE +dsa_instance(VALUE klass, DSA *dsa) +{ + EVP_PKEY *pkey; + VALUE obj; + + if (!dsa) { + return Qfalse; + } + if (!(pkey = EVP_PKEY_new())) { + return Qfalse; + } + if (!EVP_PKEY_assign_DSA(pkey, dsa)) { + EVP_PKEY_free(pkey); + return Qfalse; + } + WrapPKey(klass, obj, pkey); + + return obj; +} + +VALUE +ossl_dsa_new(EVP_PKEY *pkey) +{ + VALUE obj; + + if (!pkey) { + obj = dsa_instance(cDSA, DSA_new()); + } else { + if (EVP_PKEY_type(pkey->type) != EVP_PKEY_DSA) { + ossl_raise(rb_eTypeError, "Not a DSA key!"); + } + WrapPKey(cDSA, obj, pkey); + } + if (obj == Qfalse) { + ossl_raise(eDSAError, NULL); + } + + return obj; +} + +/* + * Private + */ +/* + * CB for yielding when generating DSA params + */ +static void +ossl_dsa_generate_cb(int p, int n, void *arg) +{ + VALUE ary; + + ary = rb_ary_new2(2); + rb_ary_store(ary, 0, INT2NUM(p)); + rb_ary_store(ary, 1, INT2NUM(n)); + + rb_yield(ary); +} + +static DSA * +dsa_generate(int size) +{ + DSA *dsa; + unsigned char seed[20]; + int seed_len = 20, counter; + unsigned long h; + void (*cb)(int, int, void *) = NULL; + + if (!RAND_bytes(seed, seed_len)) { + return 0; + } + if (rb_block_given_p()) { + cb = ossl_dsa_generate_cb; + } + dsa = DSA_generate_parameters(size, seed, seed_len, &counter, &h, cb, NULL); + if(!dsa) { /* arg to cb = NULL */ + return 0; + } + if (!DSA_generate_key(dsa)) { + DSA_free(dsa); + return 0; + } + + return dsa; +} + +static VALUE +ossl_dsa_s_generate(VALUE klass, VALUE size) +{ + DSA *dsa = dsa_generate(FIX2INT(size)); /* err handled by dsa_instance */ + VALUE obj = dsa_instance(klass, dsa); + + if (obj == Qfalse) { + DSA_free(dsa); + ossl_raise(eDSAError, NULL); + } + + return obj; +} + +static VALUE +ossl_dsa_initialize(int argc, VALUE *argv, VALUE self) +{ + EVP_PKEY *pkey; + DSA *dsa; + BIO *in; + char *passwd = NULL; + VALUE buffer, pass; + + GetPKey(self, pkey); + rb_scan_args(argc, argv, "11", &buffer, &pass); + if (FIXNUM_P(buffer)) { + if (!(dsa = dsa_generate(FIX2INT(buffer)))) { + ossl_raise(eDSAError, NULL); + } + } else { + StringValue(buffer); + if (!NIL_P(pass)) { + passwd = StringValuePtr(pass); + } + in = BIO_new_mem_buf(RSTRING(buffer)->ptr, RSTRING(buffer)->len); + if (!in){ + ossl_raise(eDSAError, NULL); + } + + dsa = PEM_read_bio_DSAPrivateKey(in, NULL, ossl_pem_passwd_cb, passwd); + if (!dsa) { + BIO_reset(in); + + dsa = PEM_read_bio_DSAPublicKey(in, NULL, NULL, NULL); + } + if (!dsa) { + BIO_reset(in); + + dsa = PEM_read_bio_DSA_PUBKEY(in, NULL, NULL, NULL); + } + if (!dsa) { + BIO_free(in); + ossl_raise(eDSAError, "Neither PUB key nor PRIV key:"); + } + BIO_free(in); + } + if (!EVP_PKEY_assign_DSA(pkey, dsa)) { + DSA_free(dsa); + ossl_raise(eDSAError, NULL); + } + + return self; +} + +static VALUE +ossl_dsa_is_public(VALUE self) +{ + EVP_PKEY *pkey; + + GetPKeyDSA(self, pkey); + + /* + * Do we need to check dsap->dsa->public_pkey? + * return Qtrue; + */ + return (pkey->pkey.dsa->pub_key) ? Qtrue : Qfalse; +} + +static VALUE +ossl_dsa_is_private(VALUE self) +{ + EVP_PKEY *pkey; + + GetPKeyDSA(self, pkey); + + return (DSA_PRIVATE(pkey->pkey.dsa)) ? Qtrue : Qfalse; +} + +static VALUE +ossl_dsa_export(int argc, VALUE *argv, VALUE self) +{ + EVP_PKEY *pkey; + BIO *out; + BUF_MEM *buf; + const EVP_CIPHER *ciph = NULL; + char *passwd = NULL; + VALUE cipher, pass, str; + + GetPKeyDSA(self, pkey); + rb_scan_args(argc, argv, "02", &cipher, &pass); + if (!NIL_P(cipher)) { + ciph = GetCipherPtr(cipher); + if (!NIL_P(pass)) { + passwd = StringValuePtr(pass); + } + } + if (!(out = BIO_new(BIO_s_mem()))) { + ossl_raise(eDSAError, NULL); + } + if (DSA_PRIVATE(pkey->pkey.dsa)) { + if (!PEM_write_bio_DSAPrivateKey(out, pkey->pkey.dsa, ciph, + NULL, 0, ossl_pem_passwd_cb, passwd)){ + BIO_free(out); + ossl_raise(eDSAError, NULL); + } + } else { + if (!PEM_write_bio_DSAPublicKey(out, pkey->pkey.dsa)) { + BIO_free(out); + ossl_raise(eDSAError, NULL); + } + } + BIO_get_mem_ptr(out, &buf); + str = rb_str_new(buf->data, buf->length); + BIO_free(out); + + return str; +} + +/* + * Stores all parameters of key to the hash + * INSECURE: PRIVATE INFORMATIONS CAN LEAK OUT!!! + * Don't use :-)) (I's up to you) + */ +static VALUE +ossl_dsa_get_params(VALUE self) +{ + EVP_PKEY *pkey; + VALUE hash; + + GetPKeyDSA(self, pkey); + + hash = rb_hash_new(); + + rb_hash_aset(hash, rb_str_new2("p"), ossl_bn_new(pkey->pkey.dsa->p)); + rb_hash_aset(hash, rb_str_new2("q"), ossl_bn_new(pkey->pkey.dsa->q)); + rb_hash_aset(hash, rb_str_new2("g"), ossl_bn_new(pkey->pkey.dsa->g)); + rb_hash_aset(hash, rb_str_new2("pub_key"), ossl_bn_new(pkey->pkey.dsa->pub_key)); + rb_hash_aset(hash, rb_str_new2("priv_key"), ossl_bn_new(pkey->pkey.dsa->priv_key)); + + return hash; +} + +/* + * Prints all parameters of key to buffer + * INSECURE: PRIVATE INFORMATIONS CAN LEAK OUT!!! + * Don't use :-)) (I's up to you) + */ +static VALUE +ossl_dsa_to_text(VALUE self) +{ + EVP_PKEY *pkey; + BIO *out; + BUF_MEM *buf; + VALUE str; + + GetPKeyDSA(self, pkey); + if (!(out = BIO_new(BIO_s_mem()))) { + ossl_raise(eDSAError, NULL); + } + if (!DSA_print(out, pkey->pkey.dsa, 0)) { //offset = 0 + BIO_free(out); + ossl_raise(eDSAError, NULL); + } + BIO_get_mem_ptr(out, &buf); + str = rb_str_new(buf->data, buf->length); + BIO_free(out); + + return str; +} + +/* + * Makes new instance DSA PUBLIC_KEY from PRIVATE_KEY + */ +static VALUE +ossl_dsa_to_public_key(VALUE self) +{ + EVP_PKEY *pkey; + DSA *dsa; + VALUE obj; + + GetPKeyDSA(self, pkey); + /* err check performed by dsa_instance */ + dsa = DSAPublicKey_dup(pkey->pkey.dsa); + obj = dsa_instance(CLASS_OF(self), dsa); + if (obj == Qfalse) { + DSA_free(dsa); + ossl_raise(eDSAError, NULL); + } + return obj; +} + +static VALUE +ossl_dsa_sign(VALUE self, VALUE data) +{ + EVP_PKEY *pkey; + char *buf; + int buf_len; + VALUE str; + + GetPKeyDSA(self, pkey); + StringValue(data); + if (!DSA_PRIVATE(pkey->pkey.dsa)) { + ossl_raise(eDSAError, "Private DSA key needed!"); + } + if (!(buf = OPENSSL_malloc(DSA_size(pkey->pkey.dsa) + 16))) { + ossl_raise(eDSAError, NULL); + } + if (!DSA_sign(0, RSTRING(data)->ptr, RSTRING(data)->len, buf, + &buf_len, pkey->pkey.dsa)) { /* type is ignored (0) */ + OPENSSL_free(buf); + ossl_raise(eDSAError, NULL); + } + str = rb_str_new(buf, buf_len); + OPENSSL_free(buf); + + return str; +} + +static VALUE +ossl_dsa_verify(VALUE self, VALUE digest, VALUE sig) +{ + EVP_PKEY *pkey; + int ret; + + GetPKeyDSA(self, pkey); + StringValue(digest); + StringValue(sig); + /* type is ignored (0) */ + ret = DSA_verify(0, RSTRING(digest)->ptr, RSTRING(digest)->len, + RSTRING(sig)->ptr, RSTRING(sig)->len, pkey->pkey.dsa); + if (ret < 0) { + ossl_raise(eDSAError, NULL); + } + else if (ret == 1) { + return Qtrue; + } + + return Qfalse; +} + +/* + * INIT + */ +void +Init_ossl_dsa() +{ + eDSAError = rb_define_class_under(mPKey, "DSAError", ePKeyError); + + cDSA = rb_define_class_under(mPKey, "DSA", cPKey); + + rb_define_singleton_method(cDSA, "generate", ossl_dsa_s_generate, 1); + rb_define_method(cDSA, "initialize", ossl_dsa_initialize, -1); + + rb_define_method(cDSA, "public?", ossl_dsa_is_public, 0); + rb_define_method(cDSA, "private?", ossl_dsa_is_private, 0); + rb_define_method(cDSA, "to_text", ossl_dsa_to_text, 0); + rb_define_method(cDSA, "export", ossl_dsa_export, -1); + rb_define_alias(cDSA, "to_pem", "export"); + rb_define_alias(cDSA, "to_s", "export"); + rb_define_method(cDSA, "public_key", ossl_dsa_to_public_key, 0); + rb_define_method(cDSA, "syssign", ossl_dsa_sign, 1); + rb_define_method(cDSA, "sysverify", ossl_dsa_verify, 2); + + rb_define_method(cDSA, "params", ossl_dsa_get_params, 0); +} + +#else /* defined NO_DSA */ +# warning >>> OpenSSL is compiled without DSA support <<< + +void +Init_ossl_dsa() +{ + rb_warning("OpenSSL is compiled without DSA support"); +} + +#endif /* NO_DSA */ diff --git a/ext/openssl/ossl_pkey_rsa.c b/ext/openssl/ossl_pkey_rsa.c new file mode 100644 index 0000000000..c3d23666c9 --- /dev/null +++ b/ext/openssl/ossl_pkey_rsa.c @@ -0,0 +1,515 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#if !defined(OPENSSL_NO_RSA) + +#include "ossl.h" + +#define GetPKeyRSA(obj, pkey) do { \ + GetPKey(obj, pkey); \ + if (EVP_PKEY_type(pkey->type) != EVP_PKEY_RSA) { /* PARANOIA? */ \ + ossl_raise(rb_eRuntimeError, "THIS IS NOT A RSA!") ; \ + } \ +} while (0) + +#define RSA_PRIVATE(rsa) ((rsa)->p && (rsa)->q) + +/* + * Classes + */ +VALUE cRSA; +VALUE eRSAError; + +/* + * Public + */ +static VALUE +rsa_instance(VALUE klass, RSA *rsa) +{ + EVP_PKEY *pkey; + VALUE obj; + + if (!rsa) { + return Qfalse; + } + if (!(pkey = EVP_PKEY_new())) { + return Qfalse; + } + if (!EVP_PKEY_assign_RSA(pkey, rsa)) { + EVP_PKEY_free(pkey); + return Qfalse; + } + WrapPKey(klass, obj, pkey); + + return obj; +} + +VALUE +ossl_rsa_new(EVP_PKEY *pkey) +{ + VALUE obj; + + if (!pkey) { + obj = rsa_instance(cRSA, RSA_new()); + } + else { + if (EVP_PKEY_type(pkey->type) != EVP_PKEY_RSA) { + ossl_raise(rb_eTypeError, "Not a RSA key!"); + } + WrapPKey(cRSA, obj, pkey); + } + if (obj == Qfalse) { + ossl_raise(eRSAError, NULL); + } + + return obj; +} + +/* + * Private + */ +/* + * CB for yielding when generating RSA data + */ +static void +ossl_rsa_generate_cb(int p, int n, void *arg) +{ + VALUE ary; + + ary = rb_ary_new2(2); + rb_ary_store(ary, 0, INT2NUM(p)); + rb_ary_store(ary, 1, INT2NUM(n)); + + rb_yield(ary); +} + +static RSA * +rsa_generate(int size, int exp) +{ + void (*cb)(int, int, void *) = NULL; + + if (rb_block_given_p()) { + cb = ossl_rsa_generate_cb; + } + return RSA_generate_key(size, exp, cb, NULL); +} + +static VALUE +ossl_rsa_s_generate(int argc, VALUE *argv, VALUE klass) +{ + RSA *rsa; + VALUE size, exp; + VALUE obj; + + rb_scan_args(argc, argv, "11", &size, &exp); + + rsa = rsa_generate(NUM2INT(size), NIL_P(exp) ? RSA_F4 : NUM2INT(exp)); /* err handled by rsa_instance */ + obj = rsa_instance(klass, rsa); + + if (obj == Qfalse) { + RSA_free(rsa); + ossl_raise(eRSAError, NULL); + } + + return obj; +} + +static VALUE +ossl_rsa_initialize(int argc, VALUE *argv, VALUE self) +{ + EVP_PKEY *pkey; + RSA *rsa; + BIO *in; + char *passwd = NULL; + VALUE buffer, pass; + + GetPKey(self, pkey); + + rb_scan_args(argc, argv, "11", &buffer, &pass); + + if (FIXNUM_P(buffer)) { + rsa = rsa_generate(FIX2INT(buffer), NIL_P(pass) ? RSA_F4 : NUM2INT(pass)); + if (!rsa) { + ossl_raise(eRSAError, NULL); + } + } + else { + StringValue(buffer); + if (!NIL_P(pass)) { + passwd = StringValuePtr(pass); + } + if (!(in = BIO_new_mem_buf(RSTRING(buffer)->ptr, RSTRING(buffer)->len))){ + ossl_raise(eRSAError, NULL); + } + + rsa = PEM_read_bio_RSAPrivateKey(in, NULL, ossl_pem_passwd_cb, passwd); + if (!rsa) { + BIO_reset(in); + + rsa = PEM_read_bio_RSAPublicKey(in, NULL, NULL, NULL); + } + if (!rsa) { + BIO_reset(in); + + rsa = PEM_read_bio_RSA_PUBKEY(in, NULL, NULL, NULL); + } + BIO_free(in); + + if (!rsa) { + ossl_raise(eRSAError, "Neither PUB key nor PRIV key:"); + } + } + if (!EVP_PKEY_assign_RSA(pkey, rsa)) { + RSA_free(rsa); + ossl_raise(eRSAError, NULL); + } + + return self; +} + +static VALUE +ossl_rsa_is_public(VALUE self) +{ + EVP_PKEY *pkey; + + GetPKeyRSA(self, pkey); + /* + * SURPRISE! :-)) + * Every key is public at the same time! + */ + return Qtrue; +} + +static VALUE +ossl_rsa_is_private(VALUE self) +{ + EVP_PKEY *pkey; + + GetPKeyRSA(self, pkey); + + return (RSA_PRIVATE(pkey->pkey.rsa)) ? Qtrue : Qfalse; +} + +static VALUE +ossl_rsa_export(int argc, VALUE *argv, VALUE self) +{ + EVP_PKEY *pkey; + BIO *out; + BUF_MEM *buf; + const EVP_CIPHER *ciph = NULL; + char *passwd = NULL; + VALUE cipher, pass, str; + + GetPKeyRSA(self, pkey); + + rb_scan_args(argc, argv, "02", &cipher, &pass); + + if (!NIL_P(cipher)) { + ciph = GetCipherPtr(cipher); + if (!NIL_P(pass)) { + passwd = StringValuePtr(pass); + } + } + if (!(out = BIO_new(BIO_s_mem()))) { + ossl_raise(eRSAError, NULL); + } + if (RSA_PRIVATE(pkey->pkey.rsa)) { + if (!PEM_write_bio_RSAPrivateKey(out, pkey->pkey.rsa, ciph, + NULL, 0, ossl_pem_passwd_cb, passwd)) { + BIO_free(out); + ossl_raise(eRSAError, NULL); + } + } else { + if (!PEM_write_bio_RSAPublicKey(out, pkey->pkey.rsa)) { + BIO_free(out); + ossl_raise(eRSAError, NULL); + } + } + BIO_get_mem_ptr(out, &buf); + str = rb_str_new(buf->data, buf->length); + BIO_free(out); + + return str; +} + +static VALUE +ossl_rsa_public_encrypt(VALUE self, VALUE buffer) +{ + EVP_PKEY *pkey; + char *buf; + int buf_len; + VALUE str; + + GetPKeyRSA(self, pkey); + + StringValue(buffer); + + if (!(buf = OPENSSL_malloc(RSA_size(pkey->pkey.rsa) + 16))) { + ossl_raise(eRSAError, NULL); + } + buf_len = RSA_public_encrypt(RSTRING(buffer)->len, RSTRING(buffer)->ptr, + buf, pkey->pkey.rsa, RSA_PKCS1_PADDING); + if (buf_len < 0){ + OPENSSL_free(buf); + ossl_raise(eRSAError, NULL); + } + str = rb_str_new(buf, buf_len); + OPENSSL_free(buf); + + return str; +} + +static VALUE +ossl_rsa_public_decrypt(VALUE self, VALUE buffer) +{ + EVP_PKEY *pkey; + char *buf; + int buf_len; + VALUE str; + + GetPKeyRSA(self, pkey); + StringValue(buffer); + if (!(buf = OPENSSL_malloc(RSA_size(pkey->pkey.rsa) + 16))) { + ossl_raise(eRSAError, NULL); + } + buf_len = RSA_public_decrypt(RSTRING(buffer)->len, RSTRING(buffer)->ptr, + buf, pkey->pkey.rsa, RSA_PKCS1_PADDING); + if(buf_len < 0) { + OPENSSL_free(buf); + ossl_raise(eRSAError, NULL); + } + str = rb_str_new(buf, buf_len); + OPENSSL_free(buf); + + return str; +} + +static VALUE +ossl_rsa_private_encrypt(VALUE self, VALUE buffer) +{ + EVP_PKEY *pkey; + char *buf; + int buf_len; + VALUE str; + + GetPKeyRSA(self, pkey); + if (!RSA_PRIVATE(pkey->pkey.rsa)) { + ossl_raise(eRSAError, "PRIVATE key needed for this operation!"); + } + StringValue(buffer); + if (!(buf = OPENSSL_malloc(RSA_size(pkey->pkey.rsa) + 16))) { + ossl_raise(eRSAError, "Memory alloc error"); + } + buf_len = RSA_private_encrypt(RSTRING(buffer)->len, RSTRING(buffer)->ptr, + buf, pkey->pkey.rsa, RSA_PKCS1_PADDING); + if (buf_len < 0){ + OPENSSL_free(buf); + ossl_raise(eRSAError, NULL); + } + str = rb_str_new(buf, buf_len); + OPENSSL_free(buf); + + return str; +} + +static VALUE +ossl_rsa_private_decrypt(VALUE self, VALUE buffer) +{ + EVP_PKEY *pkey; + char *buf; + int buf_len; + VALUE str; + + GetPKeyRSA(self, pkey); + if (!RSA_PRIVATE(pkey->pkey.rsa)) { + ossl_raise(eRSAError, "Private RSA key needed!"); + } + StringValue(buffer); + if (!(buf = OPENSSL_malloc(RSA_size(pkey->pkey.rsa) + 16))) { + ossl_raise(eRSAError, "Memory alloc error"); + } + buf_len = RSA_private_decrypt(RSTRING(buffer)->len, RSTRING(buffer)->ptr, + buf, pkey->pkey.rsa, RSA_PKCS1_PADDING); + if(buf_len < 0) { + OPENSSL_free(buf); + ossl_raise(eRSAError, NULL); + } + str = rb_str_new(buf, buf_len); + OPENSSL_free(buf); + + return str; +} + +/* + * Stores all parameters of key to the hash + * INSECURE: PRIVATE INFORMATIONS CAN LEAK OUT!!! + * Don't use :-)) (I's up to you) + */ +static VALUE +ossl_rsa_get_params(VALUE self) +{ + EVP_PKEY *pkey; + VALUE hash; + + GetPKeyRSA(self, pkey); + + hash = rb_hash_new(); + + rb_hash_aset(hash, rb_str_new2("n"), ossl_bn_new(pkey->pkey.rsa->n)); + rb_hash_aset(hash, rb_str_new2("e"), ossl_bn_new(pkey->pkey.rsa->e)); + rb_hash_aset(hash, rb_str_new2("d"), ossl_bn_new(pkey->pkey.rsa->d)); + rb_hash_aset(hash, rb_str_new2("p"), ossl_bn_new(pkey->pkey.rsa->p)); + rb_hash_aset(hash, rb_str_new2("q"), ossl_bn_new(pkey->pkey.rsa->q)); + rb_hash_aset(hash, rb_str_new2("dmp1"), ossl_bn_new(pkey->pkey.rsa->dmp1)); + rb_hash_aset(hash, rb_str_new2("dmq1"), ossl_bn_new(pkey->pkey.rsa->dmq1)); + rb_hash_aset(hash, rb_str_new2("iqmp"), ossl_bn_new(pkey->pkey.rsa->iqmp)); + + return hash; +} + +/* + * Prints all parameters of key to buffer + * INSECURE: PRIVATE INFORMATIONS CAN LEAK OUT!!! + * Don't use :-)) (I's up to you) + */ +static VALUE +ossl_rsa_to_text(VALUE self) +{ + EVP_PKEY *pkey; + BIO *out; + BUF_MEM *buf; + VALUE str; + + GetPKeyRSA(self, pkey); + if (!(out = BIO_new(BIO_s_mem()))) { + ossl_raise(eRSAError, NULL); + } + if (!RSA_print(out, pkey->pkey.rsa, 0)) { //offset = 0 + BIO_free(out); + ossl_raise(eRSAError, NULL); + } + BIO_get_mem_ptr(out, &buf); + str = rb_str_new(buf->data, buf->length); + BIO_free(out); + + return str; +} + +/* + * Makes new instance RSA PUBLIC_KEY from PRIVATE_KEY + */ +static VALUE +ossl_rsa_to_public_key(VALUE self) +{ + EVP_PKEY *pkey; + RSA *rsa; + VALUE obj; + + GetPKeyRSA(self, pkey); + /* err check performed by rsa_instance */ + rsa = RSAPublicKey_dup(pkey->pkey.rsa); + obj = rsa_instance(CLASS_OF(self), rsa); + if (obj == Qfalse) { + RSA_free(rsa); + ossl_raise(eRSAError, NULL); + } + return obj; +} + +/* + * TODO: Test me +extern BN_CTX *ossl_bn_ctx; + +static VALUE +ossl_rsa_blinding_on(VALUE self) +{ + EVP_PKEY *pkey; + + GetPKeyRSA(self, pkey); + + if (RSA_blinding_on(pkey->pkey.rsa, ossl_bn_ctx) != 1) { + ossl_raise(eRSAError, NULL); + } + return self; +} + +static VALUE +ossl_rsa_blinding_off(VALUE self) +{ + EVP_PKEY *pkey; + + GetPKeyRSA(self, pkey); + RSA_blinding_off(pkey->pkey.rsa); + + return self; +} + */ + +OSSL_PKEY_BN(rsa, n); +OSSL_PKEY_BN(rsa, e); +OSSL_PKEY_BN(rsa, d); +OSSL_PKEY_BN(rsa, p); +OSSL_PKEY_BN(rsa, q); +OSSL_PKEY_BN(rsa, dmp1); +OSSL_PKEY_BN(rsa, dmq1); +OSSL_PKEY_BN(rsa, iqmp); + +/* + * INIT + */ +void +Init_ossl_rsa() +{ + eRSAError = rb_define_class_under(mPKey, "RSAError", ePKeyError); + + cRSA = rb_define_class_under(mPKey, "RSA", cPKey); + + rb_define_singleton_method(cRSA, "generate", ossl_rsa_s_generate, -1); + rb_define_method(cRSA, "initialize", ossl_rsa_initialize, -1); + + rb_define_method(cRSA, "public?", ossl_rsa_is_public, 0); + rb_define_method(cRSA, "private?", ossl_rsa_is_private, 0); + rb_define_method(cRSA, "to_text", ossl_rsa_to_text, 0); + rb_define_method(cRSA, "export", ossl_rsa_export, -1); + rb_define_alias(cRSA, "to_pem", "export"); + rb_define_alias(cRSA, "to_s", "export"); + rb_define_method(cRSA, "public_key", ossl_rsa_to_public_key, 0); + rb_define_method(cRSA, "public_encrypt", ossl_rsa_public_encrypt, 1); + rb_define_method(cRSA, "public_decrypt", ossl_rsa_public_decrypt, 1); + rb_define_method(cRSA, "private_encrypt", ossl_rsa_private_encrypt, 1); + rb_define_method(cRSA, "private_decrypt", ossl_rsa_private_decrypt, 1); + + DEF_OSSL_PKEY_BN(cRSA, rsa, n); + DEF_OSSL_PKEY_BN(cRSA, rsa, e); + DEF_OSSL_PKEY_BN(cRSA, rsa, d); + DEF_OSSL_PKEY_BN(cRSA, rsa, p); + DEF_OSSL_PKEY_BN(cRSA, rsa, q); + DEF_OSSL_PKEY_BN(cRSA, rsa, dmp1); + DEF_OSSL_PKEY_BN(cRSA, rsa, dmq1); + DEF_OSSL_PKEY_BN(cRSA, rsa, iqmp); + + rb_define_method(cRSA, "params", ossl_rsa_get_params, 0); + +/* + * TODO: Test it + rb_define_method(cRSA, "blinding_on!", ossl_rsa_blinding_on, 0); + rb_define_method(cRSA, "blinding_off!", ossl_rsa_blinding_off, 0); + */ +} + +#else /* defined NO_RSA */ +# warning >>> OpenSSL is compiled without RSA support <<< +void +Init_ossl_rsa() +{ + rb_warning("OpenSSL is compiled without RSA support"); +} +#endif /* NO_RSA */ + diff --git a/ext/openssl/ossl_rand.c b/ext/openssl/ossl_rand.c new file mode 100644 index 0000000000..c30889221e --- /dev/null +++ b/ext/openssl/ossl_rand.c @@ -0,0 +1,142 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" + +/* + * Classes + */ +VALUE mRandom; +VALUE eRandomError; + +/* + * Struct + */ + +/* + * Public + */ + +/* + * Private + */ +static VALUE +ossl_rand_seed(VALUE self, VALUE str) +{ + StringValue(str); + RAND_seed(RSTRING(str)->ptr, RSTRING(str)->len); + + return str; +} + +static VALUE +ossl_rand_load_file(VALUE self, VALUE filename) +{ + SafeStringValue(filename); + + if(!RAND_load_file(RSTRING(filename)->ptr, -1)) { + ossl_raise(eRandomError, NULL); + } + return Qtrue; +} + +static VALUE +ossl_rand_write_file(VALUE self, VALUE filename) +{ + SafeStringValue(filename); + if (RAND_write_file(RSTRING(filename)->ptr) == -1) { + ossl_raise(eRandomError, NULL); + } + return Qtrue; +} + +static VALUE +ossl_rand_bytes(VALUE self, VALUE len) +{ + unsigned char *buffer = NULL; + VALUE str; + + if (!(buffer = OPENSSL_malloc(FIX2INT(len) + 1))) { + ossl_raise(eRandomError, NULL); + } + if (!RAND_bytes(buffer, FIX2INT(len))) { + OPENSSL_free(buffer); + ossl_raise(eRandomError, NULL); + } + str = rb_str_new(buffer, FIX2INT(len)); + OPENSSL_free(buffer); + + return str; +} + +static VALUE +ossl_rand_pseudo_bytes(VALUE self, VALUE len) +{ + unsigned char *buffer = NULL; + VALUE str; + + if (!(buffer = OPENSSL_malloc(FIX2INT(len) + 1))) { + ossl_raise(eRandomError, NULL); + } + if (!RAND_pseudo_bytes(buffer, FIX2INT(len))) { + OPENSSL_free(buffer); + ossl_raise(eRandomError, NULL); + } + str = rb_str_new(buffer, FIX2INT(len)); + OPENSSL_free(buffer); + + return str; +} + +static VALUE +ossl_rand_egd(VALUE self, VALUE filename) +{ + SafeStringValue(filename); + + if(!RAND_egd(RSTRING(filename)->ptr)) { + ossl_raise(eRandomError, NULL); + } + return Qtrue; +} + +static VALUE +ossl_rand_egd_bytes(VALUE self, VALUE filename, VALUE len) +{ + SafeStringValue(filename); + + if (!RAND_egd_bytes(RSTRING(filename)->ptr, FIX2INT(len))) { + ossl_raise(eRandomError, NULL); + } + return Qtrue; +} + +#define DEFMETH(class, name, func, argc) \ + rb_define_method(class, name, func, argc); \ + rb_define_singleton_method(class, name, func, argc); + +/* + * INIT + */ +void +Init_ossl_rand() +{ + mRandom = rb_define_module_under(mOSSL, "Random"); + + eRandomError = rb_define_class_under(mRandom, "RandomError", eOSSLError); + + DEFMETH(mRandom, "seed", ossl_rand_seed, 1); + DEFMETH(mRandom, "load_random_file", ossl_rand_load_file, 1); + DEFMETH(mRandom, "write_random_file", ossl_rand_write_file, 1); + DEFMETH(mRandom, "random_bytes", ossl_rand_bytes, 1); + DEFMETH(mRandom, "pseudo_bytes", ossl_rand_pseudo_bytes, 1); + DEFMETH(mRandom, "egd", ossl_rand_egd, 1); + DEFMETH(mRandom, "egd_bytes", ossl_rand_egd_bytes, 2); +} + diff --git a/ext/openssl/ossl_rand.h b/ext/openssl/ossl_rand.h new file mode 100644 index 0000000000..ce2ae0d129 --- /dev/null +++ b/ext/openssl/ossl_rand.h @@ -0,0 +1,20 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#if !defined(_OSSL_RAND_H_) +#define _OSSL_RAND_H_ + +extern VALUE mRandom; +extern VALUE eRandomError; + +void Init_ossl_rand(void); + +#endif /* _OSSL_RAND_H_ */ + diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c new file mode 100644 index 0000000000..e4fb0bf78c --- /dev/null +++ b/ext/openssl/ossl_ssl.c @@ -0,0 +1,678 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2000-2002 GOTOU Yuuzou + * Copyright (C) 2001-2002 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" +#include +#include + +#if defined(HAVE_UNISTD_H) +# include /* for read(), and write() */ +#endif + +#define numberof(ary) (sizeof(ary)/sizeof(ary[0])) + +VALUE mSSL; +VALUE eSSLError; +VALUE cSSLContext; +VALUE cSSLSocket; + +/* + * SSLContext class + */ +#define ossl_sslctx_set_cert(o,v) rb_iv_set((o),"@cert",(v)) +#define ossl_sslctx_set_key(o,v) rb_iv_set((o),"@key",(v)) +#define ossl_sslctx_set_client_ca(o,v) rb_iv_set((o),"@client_ca",(v)) +#define ossl_sslctx_set_ca_file(o,v) rb_iv_set((o),"@ca_file",(v)) +#define ossl_sslctx_set_ca_path(o,v) rb_iv_set((o),"@ca_path",(v)) +#define ossl_sslctx_set_timeout(o,v) rb_iv_set((o),"@timeout",(v)) +#define ossl_sslctx_set_verify_mode(o,v) rb_iv_set((o),"@verify_mode",(v)) +#define ossl_sslctx_set_verify_dep(o,v) rb_iv_set((o),"@verify_depth",(v)) +#define ossl_sslctx_set_verify_cb(o,v) rb_iv_set((o),"@verify_callback",(v)) +#define ossl_sslctx_set_options(o,v) rb_iv_set((o),"@options",(v)) +#define ossl_sslctx_set_cert_store(o,v) rb_iv_set((o),"@cert_store",(v)) + +#define ossl_sslctx_get_cert(o) rb_iv_get((o),"@cert") +#define ossl_sslctx_get_key(o) rb_iv_get((o),"@key") +#define ossl_sslctx_get_client_ca(o) rb_iv_get((o),"@client_ca") +#define ossl_sslctx_get_ca_file(o) rb_iv_get((o),"@ca_file") +#define ossl_sslctx_get_ca_path(o) rb_iv_get((o),"@ca_path") +#define ossl_sslctx_get_timeout(o) rb_iv_get((o),"@timeout") +#define ossl_sslctx_get_verify_mode(o) rb_iv_get((o),"@verify_mode") +#define ossl_sslctx_get_verify_dep(o) rb_iv_get((o),"@verify_depth") +#define ossl_sslctx_get_verify_cb(o) rb_iv_get((o),"@verify_callback") +#define ossl_sslctx_get_options(o) rb_iv_get((o),"@options") +#define ossl_sslctx_get_cert_store(o) rb_iv_get((o),"@cert_store") + +static char *ossl_sslctx_attrs[] = { + "cert", "key", "client_ca", "ca_file", "ca_path", + "timeout", "verify_mode", "verify_depth", + "verify_callback", "options", "cert_store", +}; + +struct { + const char *name; + SSL_METHOD *(*func)(void); +} ossl_ssl_method_tab[] = { +#define OSSL_SSL_METHOD_ENTRY(name) { #name, name##_method } + OSSL_SSL_METHOD_ENTRY(TLSv1), + OSSL_SSL_METHOD_ENTRY(TLSv1_server), + OSSL_SSL_METHOD_ENTRY(TLSv1_client), + OSSL_SSL_METHOD_ENTRY(SSLv2), + OSSL_SSL_METHOD_ENTRY(SSLv2_server), + OSSL_SSL_METHOD_ENTRY(SSLv2_client), + OSSL_SSL_METHOD_ENTRY(SSLv3), + OSSL_SSL_METHOD_ENTRY(SSLv3_server), + OSSL_SSL_METHOD_ENTRY(SSLv3_client), + OSSL_SSL_METHOD_ENTRY(SSLv23), + OSSL_SSL_METHOD_ENTRY(SSLv23_server), + OSSL_SSL_METHOD_ENTRY(SSLv23_client), +#undef OSSL_SSL_METHOD_ENTRY +}; + +int ossl_ssl_ex_vcb_idx; +int ossl_ssl_ex_store_p; + +static void +ossl_sslctx_free(SSL_CTX *ctx) +{ + if(ctx && SSL_CTX_get_ex_data(ctx, ossl_ssl_ex_store_p)== (void*)1) + ctx->cert_store = NULL; + SSL_CTX_free(ctx); +} + +static VALUE +ossl_sslctx_s_alloc(VALUE klass) +{ + SSL_CTX *ctx; + + ctx = SSL_CTX_new(SSLv23_method()); + if (!ctx) { + ossl_raise(eSSLError, "SSL_CTX_new:"); + } + SSL_CTX_set_options(ctx, SSL_OP_ALL); + return Data_Wrap_Struct(klass, 0, ossl_sslctx_free, ctx); +} +DEFINE_ALLOC_WRAPPER(ossl_sslctx_s_alloc) + +static VALUE +ossl_sslctx_initialize(int argc, VALUE *argv, VALUE self) +{ + VALUE ssl_method; + SSL_METHOD *method = NULL; + SSL_CTX *ctx; + int i; + char *s; + + Data_Get_Struct(self, SSL_CTX, ctx); + + if (rb_scan_args(argc, argv, "01", &ssl_method) == 0){ + return self; + } + if(TYPE(ssl_method) == T_SYMBOL) + s = rb_id2name(SYM2ID(ssl_method)); + else + s = StringValuePtr(ssl_method); + for (i = 0; i < numberof(ossl_ssl_method_tab); i++) { + if (strcmp(ossl_ssl_method_tab[i].name, s) == 0) { + method = ossl_ssl_method_tab[i].func(); + break; + } + } + if (!method) { + ossl_raise(rb_eArgError, "unknown SSL method `%s'.", s); + } + if (SSL_CTX_set_ssl_version(ctx, method) != 1) { + ossl_raise(eSSLError, "SSL_CTX_set_ssl_version:"); + } + + return self; +} + +static int +ossl_ssl_verify_callback(int preverify_ok, X509_STORE_CTX *ctx) +{ + VALUE cb; + SSL *ssl; + + ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); + cb = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_vcb_idx); + X509_STORE_CTX_set_ex_data(ctx, ossl_verify_cb_idx, (void*)cb); + return ossl_verify_cb(preverify_ok, ctx); +} + +static VALUE +ossl_sslctx_setup(VALUE self) +{ + SSL_CTX *ctx; + X509 *cert = NULL, *client_ca = NULL; + X509_STORE *store; + EVP_PKEY *key = NULL; + char *ca_path = NULL, *ca_file = NULL; + int i, verify_mode; + VALUE val; + + if(OBJ_FROZEN(self)) return Qnil; + Data_Get_Struct(self, SSL_CTX, ctx); + + val = ossl_sslctx_get_cert_store(self); + if(!NIL_P(val)){ + /* + * WORKAROUND: + * X509_STORE can count references, but + * X509_STORE_free() doesn't care it. + * So we won't increment it but mark it by ex_data. + */ + store = GetX509StorePtr(val); /* NO NEED TO DUP */ + SSL_CTX_set_cert_store(ctx, store); + SSL_CTX_set_ex_data(ctx, ossl_ssl_ex_store_p, (void*)1); + } + + /* private key may be bundled in certificate file. */ + val = ossl_sslctx_get_cert(self); + cert = NIL_P(val) ? NULL : GetX509CertPtr(val); /* NO DUP NEEDED */ + val = ossl_sslctx_get_key(self); + key = NIL_P(val) ? NULL : GetPKeyPtr(val); /* NO DUP NEEDED */ + if (cert && key) { + if (!SSL_CTX_use_certificate(ctx, cert)) { + /* Adds a ref => Safe to FREE */ + ossl_raise(eSSLError, "SSL_CTX_use_certificate:"); + } + if (!SSL_CTX_use_PrivateKey(ctx, key)) { + /* Adds a ref => Safe to FREE */ + ossl_raise(eSSLError, "SSL_CTX_use_PrivateKey:"); + } + if (!SSL_CTX_check_private_key(ctx)) { + ossl_raise(eSSLError, "SSL_CTX_check_private_key:"); + } + } + + val = ossl_sslctx_get_client_ca(self); + if(!NIL_P(val)){ + if(TYPE(val) == T_ARRAY){ + for(i = 0; i < RARRAY(val)->len; i++){ + client_ca = GetX509CertPtr(RARRAY(val)->ptr[i]); + if (!SSL_CTX_add_client_CA(ctx, client_ca)){ + /* Copies X509_NAME => FREE it. */ + ossl_raise(eSSLError, "SSL_CTX_add_client_CA"); + } + } + } + else{ + client_ca = GetX509CertPtr(val); /* NO DUP NEEDED. */ + if (!SSL_CTX_add_client_CA(ctx, client_ca)){ + /* Copies X509_NAME => FREE it. */ + ossl_raise(eSSLError, "SSL_CTX_add_client_CA"); + } + } + } + + val = ossl_sslctx_get_ca_file(self); + ca_file = NIL_P(val) ? NULL : StringValuePtr(val); + val = ossl_sslctx_get_ca_path(self); + ca_path = NIL_P(val) ? NULL : StringValuePtr(val); + if(ca_file || ca_path){ + if (!SSL_CTX_load_verify_locations(ctx, ca_file, ca_path)) + rb_warning("can't set verify locations"); + } + + val = ossl_sslctx_get_verify_mode(self); + verify_mode = NIL_P(val) ? SSL_VERIFY_NONE : NUM2INT(val); + SSL_CTX_set_verify(ctx, verify_mode, ossl_ssl_verify_callback); + + val = ossl_sslctx_get_timeout(self); + if(!NIL_P(val)) SSL_CTX_set_timeout(ctx, NUM2LONG(val)); + + val = ossl_sslctx_get_verify_dep(self); + if(!NIL_P(val)) SSL_CTX_set_verify_depth(ctx, NUM2LONG(val)); + + val = ossl_sslctx_get_options(self); + if(!NIL_P(val)) SSL_CTX_set_options(ctx, NUM2LONG(val)); + rb_obj_freeze(self); + + return Qtrue; +} + +static VALUE +ossl_ssl_cipher_to_ary(SSL_CIPHER *cipher) +{ + VALUE ary; + int bits, alg_bits; + + ary = rb_ary_new2(4); + rb_ary_push(ary, rb_str_new2(SSL_CIPHER_get_name(cipher))); + rb_ary_push(ary, rb_str_new2(SSL_CIPHER_get_version(cipher))); + bits = SSL_CIPHER_get_bits(cipher, &alg_bits); + rb_ary_push(ary, INT2FIX(bits)); + rb_ary_push(ary, INT2FIX(alg_bits)); + + return ary; +} + +static VALUE +ossl_sslctx_get_ciphers(VALUE self) +{ + SSL_CTX *ctx; + STACK_OF(SSL_CIPHER) *ciphers; + SSL_CIPHER *cipher; + VALUE ary; + int i, num; + + Data_Get_Struct(self, SSL_CTX, ctx); + if(!ctx){ + rb_warning("SSL_CTX is not initialized."); + return Qnil; + } + ciphers = ctx->cipher_list; + + if (!ciphers) + return rb_ary_new(); + + num = sk_num((STACK*)ciphers); + ary = rb_ary_new2(num); + for(i = 0; i < num; i++){ + cipher = (SSL_CIPHER*)sk_value((STACK*)ciphers, i); + rb_ary_push(ary, ossl_ssl_cipher_to_ary(cipher)); + } + return ary; +} + +static VALUE +ossl_sslctx_set_ciphers(VALUE self, VALUE v) +{ + SSL_CTX *ctx; + VALUE str, elem; + int i; + + rb_check_frozen(self); + Data_Get_Struct(self, SSL_CTX, ctx); + if(!ctx){ + ossl_raise(eSSLError, "SSL_CTX is not initialized."); + return Qnil; + } + + if (TYPE(v) == T_ARRAY) { + str = rb_str_new2(NULL); + for (i = 0; i < RARRAY(v)->len; i++) { + elem = rb_ary_entry(v, i); + if (TYPE(elem) == T_ARRAY) elem = rb_ary_entry(elem, 0); + elem = rb_String(elem); + rb_str_append(str, elem); + if (i < RARRAY(v)->len-1) rb_str_cat2(str, ":"); + } + } else { + str = v; + StringValue(str); + } + + if (!SSL_CTX_set_cipher_list(ctx, RSTRING(str)->ptr)) { + ossl_raise(eSSLError, "SSL_CTX_set_ciphers:"); + } + return Qnil; +} + +/* + * SSLSocket class + */ +#define ossl_ssl_get_io(o) rb_iv_get((o),"@io") +#define ossl_ssl_get_ctx(o) rb_iv_get((o),"@context") + +#define ossl_ssl_set_io(o,v) rb_iv_set((o),"@io",(v)) +#define ossl_ssl_set_ctx(o,v) rb_iv_set((o),"@context",(v)) + +static char *ossl_ssl_attrs[] = { "io", "context", }; + +static void +ossl_ssl_shutdown(SSL *ssl) +{ + if (ssl) { + SSL_shutdown(ssl); + SSL_clear(ssl); + } +} + +static void +ossl_ssl_free(SSL *ssl) +{ + ossl_ssl_shutdown(ssl); + SSL_free(ssl); +} + +static VALUE +ossl_ssl_s_alloc(VALUE klass) +{ + return Data_Wrap_Struct(klass, 0, ossl_ssl_free, NULL); +} +DEFINE_ALLOC_WRAPPER(ossl_ssl_s_alloc) + +static VALUE +ossl_ssl_initialize(int argc, VALUE *argv, VALUE self) +{ + VALUE io, ctx; + + if (rb_scan_args(argc, argv, "11", &io, &ctx) == 1) { + ctx = rb_funcall(cSSLContext, rb_intern("new"), 0); + } + OSSL_Check_Kind(ctx, cSSLContext); + Check_Type(io, T_FILE); + ossl_ssl_set_io(self, io); + ossl_ssl_set_ctx(self, ctx); + ossl_sslctx_setup(ctx); + + return self; +} + +static VALUE +ossl_ssl_setup(VALUE self) +{ + VALUE io, v_ctx; + SSL_CTX *ctx; + SSL *ssl; + OpenFile *fptr; + + Data_Get_Struct(self, SSL, ssl); + if(!ssl){ + v_ctx = ossl_ssl_get_ctx(self); + Data_Get_Struct(v_ctx, SSL_CTX, ctx); + + ssl = SSL_new(ctx); + if (!ssl) { + ossl_raise(eSSLError, "SSL_new:"); + } + DATA_PTR(self) = ssl; + + io = ossl_ssl_get_io(self); + GetOpenFile(io, fptr); + rb_io_check_readable(fptr); + rb_io_check_writable(fptr); + SSL_set_fd(ssl, fileno(fptr->f)); + } + + return Qtrue; +} + +static VALUE +ossl_ssl_connect(VALUE self) +{ + SSL *ssl; + VALUE cb; + + ossl_ssl_setup(self); + Data_Get_Struct(self, SSL, ssl); + cb = ossl_sslctx_get_verify_cb(ossl_ssl_get_ctx(self)); + SSL_set_ex_data(ssl, ossl_ssl_ex_vcb_idx, (void *)cb); + if (SSL_connect(ssl) <= 0) { + ossl_raise(eSSLError, "SSL_connect:"); + } + + return self; +} + +static VALUE +ossl_ssl_accept(VALUE self) +{ + SSL *ssl; + VALUE cb; + + ossl_ssl_setup(self); + Data_Get_Struct(self, SSL, ssl); + cb = ossl_sslctx_get_verify_cb(ossl_ssl_get_ctx(self)); + SSL_set_ex_data(ssl, ossl_ssl_ex_vcb_idx, (void *)cb); + if (SSL_accept(ssl) <= 0) { + ossl_raise(eSSLError, "SSL_accept:"); + } + + return self; +} + +static VALUE +ossl_ssl_read(VALUE self, VALUE len) +{ + SSL *ssl; + int ilen, nread = 0; + VALUE str; + OpenFile *fptr; + + Data_Get_Struct(self, SSL, ssl); + ilen = NUM2INT(len); + str = rb_str_new(0, ilen); + + if (ssl) { + nread = SSL_read(ssl, RSTRING(str)->ptr, RSTRING(str)->len); + if (nread < 0) { + ossl_raise(eSSLError, "SSL_read:"); + } + } + else { + rb_warning("SSL session is not started yet."); + GetOpenFile(ossl_ssl_get_io(self), fptr); + rb_io_check_readable(fptr); + TRAP_BEG; + nread = read(fileno(fptr->f), RSTRING(str)->ptr, RSTRING(str)->len); + TRAP_END; + if(nread < 0) { + ossl_raise(eSSLError, "read:%s", strerror(errno)); + } + } + + if (nread == 0) { + ossl_raise(rb_eEOFError, "End of file reached"); + } + + RSTRING(str)->len = nread; + RSTRING(str)->ptr[nread] = 0; + OBJ_TAINT(str); + + return str; +} + +static VALUE +ossl_ssl_write(VALUE self, VALUE str) +{ + SSL *ssl; + int nwrite = 0; + OpenFile *fptr; + FILE *fp; + + Data_Get_Struct(self, SSL, ssl); + StringValue(str); + + if (ssl) { + nwrite = SSL_write(ssl, RSTRING(str)->ptr, RSTRING(str)->len); + if (nwrite <= 0) { + ossl_raise(eSSLError, "SSL_write:"); + } + } + else { + rb_warning("SSL session is not started yet."); + GetOpenFile(ossl_ssl_get_io(self), fptr); + rb_io_check_writable(fptr); + fp = GetWriteFile(fptr); + nwrite = write(fileno(fp), RSTRING(str)->ptr, RSTRING(str)->len); + if (nwrite < 0) { + ossl_raise(eSSLError, "write:%s", strerror(errno)); + } + } + + return INT2NUM(nwrite); +} + +static VALUE +ossl_ssl_close(VALUE self) +{ + SSL *ssl; + + Data_Get_Struct(self, SSL, ssl); + + ossl_ssl_shutdown(ssl); + + return Qnil; +} + +static VALUE +ossl_ssl_get_cert(VALUE self) +{ + SSL *ssl; + X509 *cert = NULL; + + Data_Get_Struct(self, SSL, ssl); + if (ssl) { + rb_warning("SSL session is not started yet."); + return Qnil; + } + + /* + * Is this OpenSSL bug? Should add a ref? + * TODO: Ask for. + */ + cert = SSL_get_certificate(ssl); /* NO DUPs => DON'T FREE. */ + + if (!cert) { + return Qnil; + } + return ossl_x509_new(cert); +} + +static VALUE +ossl_ssl_get_peer_cert(VALUE self) +{ + SSL *ssl; + X509 *cert = NULL; + VALUE obj; + + Data_Get_Struct(self, SSL, ssl); + + if (!ssl){ + rb_warning("SSL session is not started yet."); + return Qnil; + } + + cert = SSL_get_peer_certificate(ssl); /* Adds a ref => Safe to FREE. */ + + if (!cert) { + return Qnil; + } + obj = ossl_x509_new(cert); + X509_free(cert); + + return obj; +} + +static VALUE +ossl_ssl_get_cipher(VALUE self) +{ + SSL *ssl; + SSL_CIPHER *cipher; + + Data_Get_Struct(self, SSL, ssl); + if (!ssl) { + rb_warning("SSL session is not started yet."); + return Qnil; + } + cipher = SSL_get_current_cipher(ssl); + + return ossl_ssl_cipher_to_ary(cipher); +} + +static VALUE +ossl_ssl_get_state(VALUE self) +{ + SSL *ssl; + VALUE ret; + + Data_Get_Struct(self, SSL, ssl); + if (!ssl) { + rb_warning("SSL session is not started yet."); + return Qnil; + } + ret = rb_str_new2(SSL_state_string(ssl)); + if (ruby_verbose) { + rb_str_cat2(ret, ": "); + rb_str_cat2(ret, SSL_state_string_long(ssl)); + } + return ret; +} + +void +Init_ossl_ssl() +{ + int i; + + ossl_ssl_ex_vcb_idx = SSL_get_ex_new_index(0,"ossl_ssl_ex_vcb_idx",0,0,0); + ossl_ssl_ex_store_p = SSL_get_ex_new_index(0,"ossl_ssl_ex_store_p",0,0,0); + + mSSL = rb_define_module_under(mOSSL, "SSL"); + eSSLError = rb_define_class_under(mSSL, "SSLError", eOSSLError); + + /* class SSLContext */ + cSSLContext = rb_define_class_under(mSSL, "SSLContext", rb_cObject); + rb_define_alloc_func(cSSLContext, ossl_sslctx_s_alloc); + for(i = 0; i < numberof(ossl_sslctx_attrs); i++) + rb_attr(cSSLContext, rb_intern(ossl_sslctx_attrs[i]), 1, 1, Qfalse); + rb_define_method(cSSLContext, "initialize", ossl_sslctx_initialize, -1); + rb_define_method(cSSLContext, "ciphers", ossl_sslctx_get_ciphers, 0); + rb_define_method(cSSLContext, "ciphers=", ossl_sslctx_set_ciphers, 1); + + /* class SSLSocket */ + cSSLSocket = rb_define_class_under(mSSL, "SSLSocket", rb_cObject); + rb_define_alloc_func(cSSLSocket, ossl_ssl_s_alloc); + for(i = 0; i < numberof(ossl_ssl_attrs); i++) + rb_attr(cSSLSocket, rb_intern(ossl_ssl_attrs[i]), 1, 0, Qfalse); + rb_define_alias(cSSLSocket, "to_io", "io"); + rb_define_method(cSSLSocket, "initialize", ossl_ssl_initialize, -1); + rb_define_method(cSSLSocket, "connect", ossl_ssl_connect, 0); + rb_define_method(cSSLSocket, "accept", ossl_ssl_accept, 0); + rb_define_method(cSSLSocket, "sysread", ossl_ssl_read, 1); + rb_define_method(cSSLSocket, "syswrite", ossl_ssl_write, 1); + rb_define_method(cSSLSocket, "sysclose", ossl_ssl_close, 0); + rb_define_method(cSSLSocket, "cert", ossl_ssl_get_cert, 0); + rb_define_method(cSSLSocket, "peer_cert", ossl_ssl_get_peer_cert, 0); + rb_define_method(cSSLSocket, "cipher", ossl_ssl_get_cipher, 0); + rb_define_method(cSSLSocket, "state", ossl_ssl_get_state, 0); + +#define ossl_ssl_def_const(x) rb_define_const(mSSL, #x, INT2FIX(SSL_##x)) + + ossl_ssl_def_const(VERIFY_NONE); + ossl_ssl_def_const(VERIFY_PEER); + ossl_ssl_def_const(VERIFY_FAIL_IF_NO_PEER_CERT); + ossl_ssl_def_const(VERIFY_CLIENT_ONCE); + /* Not introduce constants included in OP_ALL such as... + * ossl_ssl_def_const(OP_MICROSOFT_SESS_ID_BUG); + * ossl_ssl_def_const(OP_NETSCAPE_CHALLENGE_BUG); + * ossl_ssl_def_const(OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG); + * ossl_ssl_def_const(OP_SSLREF2_REUSE_CERT_TYPE_BUG); + * ossl_ssl_def_const(OP_MICROSOFT_BIG_SSLV3_BUFFER); + * ossl_ssl_def_const(OP_MSIE_SSLV2_RSA_PADDING); + * ossl_ssl_def_const(OP_SSLEAY_080_CLIENT_DH_BUG); + * ossl_ssl_def_const(OP_TLS_D5_BUG); + * ossl_ssl_def_const(OP_TLS_BLOCK_PADDING_BUG); + * ossl_ssl_def_const(OP_DONT_INSERT_EMPTY_FRAGMENTS); + */ + ossl_ssl_def_const(OP_ALL); +#if defined(SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION) + ossl_ssl_def_const(OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); +#endif +#if defined(SSL_OP_SINGLE_ECDH_USE) + ossl_ssl_def_const(OP_SINGLE_ECDH_USE); +#endif + ossl_ssl_def_const(OP_SINGLE_DH_USE); + ossl_ssl_def_const(OP_EPHEMERAL_RSA); +#if defined(SSL_OP_CIPHER_SERVER_PREFERENCE) + ossl_ssl_def_const(OP_CIPHER_SERVER_PREFERENCE); +#endif + ossl_ssl_def_const(OP_TLS_ROLLBACK_BUG); + ossl_ssl_def_const(OP_NO_SSLv2); + ossl_ssl_def_const(OP_NO_SSLv3); + ossl_ssl_def_const(OP_NO_TLSv1); + ossl_ssl_def_const(OP_PKCS1_CHECK_1); + ossl_ssl_def_const(OP_PKCS1_CHECK_2); + ossl_ssl_def_const(OP_NETSCAPE_CA_DN_BUG); + ossl_ssl_def_const(OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG); +} diff --git a/ext/openssl/ossl_ssl.h b/ext/openssl/ossl_ssl.h new file mode 100644 index 0000000000..5929eef856 --- /dev/null +++ b/ext/openssl/ossl_ssl.h @@ -0,0 +1,21 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#if !defined(_OSSL_SSL_H_) +#define _OSSL_SSL_H_ + +extern VALUE mSSL; +extern VALUE eSSLError; +extern VALUE cSSLSocket; +extern VALUE cSSLContext; + +void Init_ossl_ssl(void); + +#endif /* _OSSL_SSL_H_ */ diff --git a/ext/openssl/ossl_version.h b/ext/openssl/ossl_version.h new file mode 100644 index 0000000000..63878e0d8e --- /dev/null +++ b/ext/openssl/ossl_version.h @@ -0,0 +1,16 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#if !defined(_OSSL_VERSION_H_) +#define _OSSL_VERSION_H_ + +#define OSSL_VERSION "1.0.0" + +#endif /* _OSSL_VERSION_H_ */ diff --git a/ext/openssl/ossl_x509.c b/ext/openssl/ossl_x509.c new file mode 100644 index 0000000000..1fe50abf2c --- /dev/null +++ b/ext/openssl/ossl_x509.c @@ -0,0 +1,95 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" + +VALUE mX509; + +#define DefX509Const(x) rb_define_const(mX509, #x,INT2FIX(X509_##x)) + +void +Init_ossl_x509() +{ + mX509 = rb_define_module_under(mOSSL, "X509"); + + Init_ossl_x509attr(); + Init_ossl_x509cert(); + Init_ossl_x509crl(); + Init_ossl_x509ext(); + Init_ossl_x509name(); + Init_ossl_x509req(); + Init_ossl_x509revoked(); + Init_ossl_x509store(); + + DefX509Const(V_OK); + DefX509Const(V_ERR_UNABLE_TO_GET_ISSUER_CERT); + DefX509Const(V_ERR_UNABLE_TO_GET_CRL); + DefX509Const(V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE); + DefX509Const(V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE); + DefX509Const(V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY); + DefX509Const(V_ERR_CERT_SIGNATURE_FAILURE); + DefX509Const(V_ERR_CRL_SIGNATURE_FAILURE); + DefX509Const(V_ERR_CERT_NOT_YET_VALID); + DefX509Const(V_ERR_CERT_HAS_EXPIRED); + DefX509Const(V_ERR_CRL_NOT_YET_VALID); + DefX509Const(V_ERR_CRL_HAS_EXPIRED); + DefX509Const(V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD); + DefX509Const(V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD); + DefX509Const(V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD); + DefX509Const(V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD); + DefX509Const(V_ERR_OUT_OF_MEM); + DefX509Const(V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT); + DefX509Const(V_ERR_SELF_SIGNED_CERT_IN_CHAIN); + DefX509Const(V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY); + DefX509Const(V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE); + DefX509Const(V_ERR_CERT_CHAIN_TOO_LONG); + DefX509Const(V_ERR_CERT_REVOKED); + DefX509Const(V_ERR_INVALID_CA); + DefX509Const(V_ERR_PATH_LENGTH_EXCEEDED); + DefX509Const(V_ERR_INVALID_PURPOSE); + DefX509Const(V_ERR_CERT_UNTRUSTED); + DefX509Const(V_ERR_CERT_REJECTED); + DefX509Const(V_ERR_SUBJECT_ISSUER_MISMATCH); + DefX509Const(V_ERR_AKID_SKID_MISMATCH); + DefX509Const(V_ERR_AKID_ISSUER_SERIAL_MISMATCH); + DefX509Const(V_ERR_KEYUSAGE_NO_CERTSIGN); + DefX509Const(V_ERR_APPLICATION_VERIFICATION); + +#if defined(X509_V_FLAG_CRL_CHECK) + DefX509Const(V_FLAG_CRL_CHECK); +#endif +#if defined(X509_V_FLAG_CRL_CHECK_ALL) + DefX509Const(V_FLAG_CRL_CHECK_ALL); +#endif + + DefX509Const(PURPOSE_SSL_CLIENT); + DefX509Const(PURPOSE_SSL_SERVER); + DefX509Const(PURPOSE_NS_SSL_SERVER); + DefX509Const(PURPOSE_SMIME_SIGN); + DefX509Const(PURPOSE_SMIME_ENCRYPT); + DefX509Const(PURPOSE_CRL_SIGN); + DefX509Const(PURPOSE_ANY); +#if defined(X509_PURPOSE_OCSP_HELPER) + DefX509Const(PURPOSE_OCSP_HELPER); +#endif + + DefX509Const(TRUST_COMPAT); + DefX509Const(TRUST_SSL_CLIENT); + DefX509Const(TRUST_SSL_SERVER); + DefX509Const(TRUST_EMAIL); + DefX509Const(TRUST_OBJECT_SIGN); +#if defined(X509_TRUST_OCSP_SIGN) + DefX509Const(TRUST_OCSP_SIGN); +#endif +#if defined(X509_TRUST_OCSP_REQUEST) + DefX509Const(TRUST_OCSP_REQUEST); +#endif +} + diff --git a/ext/openssl/ossl_x509.h b/ext/openssl/ossl_x509.h new file mode 100644 index 0000000000..196ce06848 --- /dev/null +++ b/ext/openssl/ossl_x509.h @@ -0,0 +1,113 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#if !defined(_OSSL_X509_H_) +#define _OSSL_X509_H_ + +/* + * X509 main module + */ +extern VALUE mX509; + +void Init_ossl_x509(void); + +/* + * X509Attr + */ +extern VALUE cX509Attr; +extern VALUE eX509AttrError; + +VALUE ossl_x509attr_new(X509_ATTRIBUTE *); +X509_ATTRIBUTE *DupX509AttrPtr(VALUE); +void Init_ossl_x509attr(void); + +/* + * X509Cert + */ +extern VALUE cX509Cert; +extern VALUE eX509CertError; + +VALUE ossl_x509_new(X509 *); +VALUE ossl_x509_new_from_file(VALUE); +X509 *GetX509CertPtr(VALUE); +X509 *DupX509CertPtr(VALUE); +void Init_ossl_x509cert(void); + +/* + * X509CRL + */ +extern VALUE cX509CRL; +extern VALUE eX509CRLError; + +VALUE ossl_x509crl_new(X509_CRL *); +X509_CRL *GetX509CRLPtr(VALUE); +X509_CRL *DupX509CRLPtr(VALUE); +void Init_ossl_x509crl(void); + +/* + * X509Extension + */ +extern VALUE cX509Ext; +extern VALUE cX509ExtFactory; +extern VALUE eX509ExtError; + +VALUE ossl_x509ext_new(X509_EXTENSION *); +X509_EXTENSION *GetX509ExtPtr(VALUE); +X509_EXTENSION *DupX509ExtPtr(VALUE); +void Init_ossl_x509ext(void); + +/* + * X509Name + */ +extern VALUE cX509Name; +extern VALUE eX509NameError; + +VALUE ossl_x509name_new(X509_NAME *); +X509_NAME *GetX509NamePtr(VALUE); +void Init_ossl_x509name(void); + +/* + * X509Request + */ +extern VALUE cX509Req; +extern VALUE eX509ReqError; + +VALUE ossl_x509req_new(X509_REQ *); +X509_REQ *DupX509ReqPtr(VALUE); +void Init_ossl_x509req(void); + +/* + * X509Revoked + */ +extern VALUE cX509Rev; +extern VALUE eX509RevError; + +VALUE ossl_x509revoked_new(X509_REVOKED *); +X509_REVOKED *DupX509RevokedPtr(VALUE); +void Init_ossl_x509revoked(void); + +/* + * X509Store and X509StoreContext + */ +extern VALUE cX509Store; +extern VALUE cX509StoreContext; +extern VALUE eX509StoreError; + +VALUE ossl_x509store_new(X509_STORE *); +X509_STORE *GetX509StorePtr(VALUE); +X509_STORE *DupX509StorePtr(VALUE); + +VALUE ossl_x509stctx_new(X509_STORE_CTX *); +VALUE ossl_x509stctx_clear_ptr(VALUE); +X509_STORE_CTX *GetX509StCtxtPtr(VALUE); + +void Init_ossl_x509store(void); + +#endif /* _OSSL_X509_H_ */ diff --git a/ext/openssl/ossl_x509attr.c b/ext/openssl/ossl_x509attr.c new file mode 100644 index 0000000000..6d5df51126 --- /dev/null +++ b/ext/openssl/ossl_x509attr.c @@ -0,0 +1,152 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" + +#define WrapX509Attr(klass, obj, attr) do { \ + if (!attr) { \ + ossl_raise(rb_eRuntimeError, "ATTR wasn't initialized!"); \ + } \ + obj = Data_Wrap_Struct(klass, 0, X509_ATTRIBUTE_free, attr); \ +} while (0) +#define GetX509Attr(obj, attr) do { \ + Data_Get_Struct(obj, X509_ATTRIBUTE, attr); \ + if (!attr) { \ + ossl_raise(rb_eRuntimeError, "ATTR wasn't initialized!"); \ + } \ +} while (0) +#define SafeGetX509Attr(obj, attr) do { \ + OSSL_Check_Kind(obj, cX509Attr); \ + GetX509Attr(obj, attr); \ +} while (0) + +/* + * Classes + */ +VALUE cX509Attr; +VALUE eX509AttrError; + +/* + * Public + */ +VALUE +ossl_x509attr_new(X509_ATTRIBUTE *attr) +{ + X509_ATTRIBUTE *new; + VALUE obj; + + if (!attr) { + new = X509_ATTRIBUTE_new(); + } else { + new = X509_ATTRIBUTE_dup(attr); + } + if (!new) { + ossl_raise(eX509AttrError, NULL); + } + WrapX509Attr(cX509Attr, obj, new); + + return obj; +} + +X509_ATTRIBUTE * +DupX509AttrPtr(VALUE obj) +{ + X509_ATTRIBUTE *attr, *new; + + SafeGetX509Attr(obj, attr); + if (!(new = X509_ATTRIBUTE_dup(attr))) { + ossl_raise(eX509AttrError, NULL); + } + + return new; +} + +/* + * Private + */ +static VALUE +ossl_x509attr_s_new_from_array(VALUE klass, VALUE ary) +{ + X509_ATTRIBUTE *attr; + int nid = NID_undef; + VALUE item, obj; + + Check_Type(ary, T_ARRAY); + if (RARRAY(ary)->len != 2) { + ossl_raise(eX509AttrError, "unsupported ary structure"); + } + /* key [0] */ + item = RARRAY(ary)->ptr[0]; + StringValue(item); + if (!(nid = OBJ_ln2nid(RSTRING(item)->ptr))) { + if (!(nid = OBJ_sn2nid(RSTRING(item)->ptr))) { + ossl_raise(eX509AttrError, NULL); + } + } + /* data [1] */ + item = RARRAY(ary)->ptr[1]; + StringValuePtr(item); + if (!(attr = X509_ATTRIBUTE_create(nid, MBSTRING_ASC, RSTRING(item)->ptr))) { + ossl_raise(eX509AttrError, NULL); + } + WrapX509Attr(klass, obj, attr); + + return obj; +} + +#if 0 +/* + * is there any print for attribute? + * (NO, but check t_req.c in crypto/asn1) + */ +static VALUE +ossl_x509attr_to_a(VALUE self) +{ + ossl_x509attr *attrp = NULL; + BIO *out = NULL; + BUF_MEM *buf = NULL; + int nid = NID_undef; + VALUE ary, value; + + GetX509Attr(obj, attrp); + ary = rb_ary_new2(2); + nid = OBJ_obj2nid(X509_ATTRIBUTE_get0_object(attrp->attribute)); + rb_ary_push(ary, rb_str_new2(OBJ_nid2sn(nid))); + if (!(out = BIO_new(BIO_s_mem()))) + ossl_raise(eX509ExtensionError, NULL); + if (!X509V3_???_print(out, extp->extension, 0, 0)) { + BIO_free(out); + ossl_raise(eX509ExtensionError, NULL); + } + BIO_get_mem_ptr(out, &buf); + value = rb_str_new(buf->data, buf->length); + BIO_free(out); + rb_funcall(value, rb_intern("tr!"), 2, rb_str_new2("\n"), rb_str_new2(",")); + rb_ary_push(ary, value); + + return ary; +} +#endif + +/* + * X509_ATTRIBUTE init + */ +void +Init_ossl_x509attr() +{ + eX509AttrError = rb_define_class_under(mX509, "AttributeError", eOSSLError); + + cX509Attr = rb_define_class_under(mX509, "Attribute", rb_cObject); + rb_define_singleton_method(cX509Attr, "new_from_array", ossl_x509attr_s_new_from_array, 1); +/* + * TODO: + rb_define_method(cX509Attr, "to_a", ossl_x509attr_to_a, 0); + */ +} diff --git a/ext/openssl/ossl_x509cert.c b/ext/openssl/ossl_x509cert.c new file mode 100644 index 0000000000..b6ed438e0f --- /dev/null +++ b/ext/openssl/ossl_x509cert.c @@ -0,0 +1,691 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" + +#define WrapX509(klass, obj, x509) do { \ + if (!x509) { \ + ossl_raise(rb_eRuntimeError, "CERT wasn't initialized!"); \ + } \ + obj = Data_Wrap_Struct(klass, 0, X509_free, x509); \ +} while (0) +#define GetX509(obj, x509) do { \ + Data_Get_Struct(obj, X509, x509); \ + if (!x509) { \ + ossl_raise(rb_eRuntimeError, "CERT wasn't initialized!"); \ + } \ +} while (0) +#define SafeGetX509(obj, x509) do { \ + OSSL_Check_Kind(obj, cX509Cert); \ + GetX509(obj, x509); \ +} while (0) + +/* + * Classes + */ +VALUE cX509Cert; +VALUE eX509CertError; + +/* + * Public + */ +VALUE +ossl_x509_new(X509 *x509) +{ + X509 *new; + VALUE obj; + + if (!x509) { + new = X509_new(); + } else { + new = X509_dup(x509); + } + if (!new) { + ossl_raise(eX509CertError, NULL); + } + WrapX509(cX509Cert, obj, new); + + return obj; +} + +VALUE +ossl_x509_new_from_file(VALUE filename) +{ + X509 *x509; + FILE *fp; + VALUE obj; + + SafeStringValue(filename); + if (!(fp = fopen(RSTRING(filename)->ptr, "r"))) { + ossl_raise(eX509CertError, "%s", strerror(errno)); + } + x509 = PEM_read_X509(fp, NULL, NULL, NULL); + /* + * prepare for DER... +#if !defined(OPENSSL_NO_FP_API) + if (!x509) { + rewind(fp); + + x509 = d2i_X509_fp(fp, NULL); + } +#endif + */ + fclose(fp); + if (!x509) { + ossl_raise(eX509CertError, NULL); + } + WrapX509(cX509Cert, obj, x509); + + return obj; +} + +X509 * +GetX509CertPtr(VALUE obj) +{ + X509 *x509; + + SafeGetX509(obj, x509); + + return x509; +} + +X509 * +DupX509CertPtr(VALUE obj) +{ + X509 *x509; + + SafeGetX509(obj, x509); + + CRYPTO_add(&x509->references, 1, CRYPTO_LOCK_X509); + + return x509; +} + +/* + * Private + */ +static VALUE +ossl_x509_alloc(VALUE klass) +{ + X509 *x509; + VALUE obj; + + x509 = X509_new(); + if (!x509) ossl_raise(eX509CertError, NULL); + + WrapX509(klass, obj, x509); + + return obj; +} +DEFINE_ALLOC_WRAPPER(ossl_x509_alloc) + +static VALUE +ossl_x509_initialize(int argc, VALUE *argv, VALUE self) +{ + BIO *in; + X509 *x509; + VALUE arg; + + if (rb_scan_args(argc, argv, "01", &arg) == 0) { + /* create just empty X509Cert */ + return self; + } + in = ossl_obj2bio(arg); + + /* + * TODO: + * Check if we could free old X509 + X509_free(DATA_PTR(self)); + */ + x509 = PEM_read_bio_X509(in, (X509 **)&DATA_PTR(self), NULL, NULL); + if (!x509) { + BIO_reset(in); + x509 = d2i_X509_bio(in, (X509 **)&DATA_PTR(self)); + } + BIO_free(in); + if (!x509) ossl_raise(eX509CertError, NULL); + + return self; +} + +static VALUE +ossl_x509_copy(VALUE self, VALUE other) +{ + X509 *a, *b, *x509; + + rb_check_frozen(self); + if (self == other) return self; + + GetX509(self, a); + SafeGetX509(other, b); + + x509 = X509_dup(b); + if (!x509) ossl_raise(eX509CertError, NULL); + + DATA_PTR(self) = x509; + X509_free(a); + + return self; +} + +static VALUE +ossl_x509_to_der(VALUE self) +{ + X509 *x509; + BIO *out; + VALUE str; + int status=0; + + GetX509(self, x509); + + out = BIO_new(BIO_s_mem()); + if (!out) ossl_raise(eX509CertError, NULL); + + if (!i2d_X509_bio(out, x509)) { + BIO_free(out); + ossl_raise(eX509CertError, NULL); + } + str = ossl_protect_membio2str(out, &status); + BIO_free(out); + if (status) rb_jump_tag(status); + + return str; +} + +static VALUE +ossl_x509_to_pem(VALUE self) +{ + X509 *x509; + BIO *out; + VALUE str; + int status=0; + + GetX509(self, x509); + out = BIO_new(BIO_s_mem()); + if (!out) ossl_raise(eX509CertError, NULL); + + if (!PEM_write_bio_X509(out, x509)) { + BIO_free(out); + ossl_raise(eX509CertError, NULL); + } + str = ossl_protect_membio2str(out, &status); + BIO_free(out); + if (status) rb_jump_tag(status); + + return str; +} + +static VALUE +ossl_x509_to_text(VALUE self) +{ + X509 *x509; + BIO *out; + VALUE str; + int status=0; + + GetX509(self, x509); + + out = BIO_new(BIO_s_mem()); + if (!out) ossl_raise(eX509CertError, NULL); + + if (!X509_print(out, x509)) { + BIO_free(out); + ossl_raise(eX509CertError, NULL); + } + str = ossl_protect_membio2str(out, &status); + BIO_free(out); + if (status) rb_jump_tag(status); + + return str; +} + +#if 0 +/* + * Makes from X509 X509_REQuest + */ +static VALUE +ossl_x509_to_req(VALUE self) +{ + X509 *x509; + X509_REQ *req; + VALUE obj; + + GetX509(self, x509); + if (!(req = X509_to_X509_REQ(x509, NULL, EVP_md5()))) { + ossl_raise(eX509CertError, NULL); + } + obj = ossl_x509req_new(req); + X509_REQ_free(req); + + return obj; +} +#endif + +static VALUE +ossl_x509_get_version(VALUE self) +{ + X509 *x509; + + GetX509(self, x509); + + return LONG2NUM(X509_get_version(x509)); +} + +static VALUE +ossl_x509_set_version(VALUE self, VALUE version) +{ + X509 *x509; + long ver; + + GetX509(self, x509); + if ((ver = NUM2LONG(version)) < 0) { + ossl_raise(eX509CertError, "version must be >= 0!"); + } + if (!X509_set_version(x509, ver)) { + ossl_raise(eX509CertError, NULL); + } + + return version; +} + +static VALUE +ossl_x509_get_serial(VALUE self) +{ + X509 *x509; + + GetX509(self, x509); + + return asn1integer_to_num(X509_get_serialNumber(x509)); +} + +static VALUE +ossl_x509_set_serial(VALUE self, VALUE num) +{ + X509 *x509; + + GetX509(self, x509); + + x509->cert_info->serialNumber = + num_to_asn1integer(num, X509_get_serialNumber(x509)); + + return num; +} + +static VALUE +ossl_x509_get_signature_algorithm(VALUE self) +{ + X509 *x509; + BIO *out; + VALUE str; + int status=0; + + GetX509(self, x509); + + out = BIO_new(BIO_s_mem()); + if (!out) ossl_raise(eX509CertError, NULL); + + if (!i2a_ASN1_OBJECT(out, x509->cert_info->signature->algorithm)) { + BIO_free(out); + ossl_raise(eX509CertError, NULL); + } + str = ossl_protect_membio2str(out, &status); + BIO_free(out); + if (status) rb_jump_tag(status); + + return str; +} + +static VALUE +ossl_x509_get_subject(VALUE self) +{ + X509 *x509; + X509_NAME *name; + + GetX509(self, x509); + if (!(name = X509_get_subject_name(x509))) { /* NO DUP - don't free! */ + ossl_raise(eX509CertError, NULL); + } + + return ossl_x509name_new(name); +} + +static VALUE +ossl_x509_set_subject(VALUE self, VALUE subject) +{ + X509 *x509; + + GetX509(self, x509); + if (!X509_set_subject_name(x509, GetX509NamePtr(subject))) { /* DUPs name */ + ossl_raise(eX509CertError, NULL); + } + + return subject; +} + +static VALUE +ossl_x509_get_issuer(VALUE self) +{ + X509 *x509; + X509_NAME *name; + + GetX509(self, x509); + if(!(name = X509_get_issuer_name(x509))) { /* NO DUP - don't free! */ + ossl_raise(eX509CertError, NULL); + } + + return ossl_x509name_new(name); +} + +static VALUE +ossl_x509_set_issuer(VALUE self, VALUE issuer) +{ + X509 *x509; + + GetX509(self, x509); + if (!X509_set_issuer_name(x509, GetX509NamePtr(issuer))) { /* DUPs name */ + ossl_raise(eX509CertError, NULL); + } + + return issuer; +} + +static VALUE +ossl_x509_get_not_before(VALUE self) +{ + X509 *x509; + ASN1_UTCTIME *asn1time; + + GetX509(self, x509); + if (!(asn1time = X509_get_notBefore(x509))) { /* NO DUP - don't free! */ + ossl_raise(eX509CertError, NULL); + } + + return asn1time_to_time(asn1time); +} + +static VALUE +ossl_x509_set_not_before(VALUE self, VALUE time) +{ + X509 *x509; + time_t sec; + + GetX509(self, x509); + sec = time_to_time_t(time); + if (!X509_time_adj(X509_get_notBefore(x509), 0, &sec)) { + ossl_raise(eX509CertError, NULL); + } + + return time; +} + +static VALUE +ossl_x509_get_not_after(VALUE self) +{ + X509 *x509; + ASN1_TIME *asn1time; + + GetX509(self, x509); + if (!(asn1time = X509_get_notAfter(x509))) { /* NO DUP - don't free! */ + ossl_raise(eX509CertError, NULL); + } + + return asn1time_to_time(asn1time); +} + +static VALUE +ossl_x509_set_not_after(VALUE self, VALUE time) +{ + X509 *x509; + time_t sec; + + GetX509(self, x509); + sec = time_to_time_t(time); + if (!X509_time_adj(X509_get_notAfter(x509), 0, &sec)) { + ossl_raise(eX509CertError, NULL); + } + + return time; +} + +static VALUE +ossl_x509_get_public_key(VALUE self) +{ + X509 *x509; + EVP_PKEY *pkey; + + GetX509(self, x509); + if (!(pkey = X509_get_pubkey(x509))) { /* adds an reference */ + ossl_raise(eX509CertError, NULL); + } + + return ossl_pkey_new(pkey); /* NO DUP - OK */ +} + +static VALUE +ossl_x509_set_public_key(VALUE self, VALUE key) +{ + X509 *x509; + + GetX509(self, x509); + if (!X509_set_pubkey(x509, GetPKeyPtr(key))) { /* DUPs pkey */ + ossl_raise(eX509CertError, NULL); + } + + return key; +} + +static VALUE +ossl_x509_sign(VALUE self, VALUE key, VALUE digest) +{ + X509 *x509; + EVP_PKEY *pkey; + const EVP_MD *md; + + GetX509(self, x509); + pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */ + md = GetDigestPtr(digest); + if (!X509_sign(x509, pkey, md)) { + ossl_raise(eX509CertError, NULL); + } + + return self; +} + +/* + * Checks that cert signature is made with PRIVversion of this PUBLIC 'key' + */ +static VALUE +ossl_x509_verify(VALUE self, VALUE key) +{ + X509 *x509; + EVP_PKEY *pkey; + int i; + + GetX509(self, x509); + pkey = GetPKeyPtr(key); /* NO NEED TO DUP */ + if ((i = X509_verify(x509, pkey)) < 0) { + ossl_raise(eX509CertError, NULL); + } + if (i > 0) { + return Qtrue; + } + + return Qfalse; +} + +/* + * Checks if 'key' is PRIV key for this cert + */ +static VALUE +ossl_x509_check_private_key(VALUE self, VALUE key) +{ + X509 *x509; + EVP_PKEY *pkey; + + GetX509(self, x509); + /* not needed private key, but should be */ + pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */ + if (!X509_check_private_key(x509, pkey)) { + OSSL_Warning("Check private key:%s", OSSL_ErrMsg()); + return Qfalse; + } + + return Qtrue; +} + +/* + * Gets X509v3 extensions as array of X509Ext objects + */ +static VALUE +ossl_x509_get_extensions(VALUE self) +{ + X509 *x509; + int count, i; + X509_EXTENSION *ext; + VALUE ary; + + GetX509(self, x509); + count = X509_get_ext_count(x509); + if (count < 0) { + return rb_ary_new(); + } + ary = rb_ary_new2(count); + for (i=0; ilen; i++) { + OSSL_Check_Kind(RARRAY(ary)->ptr[i], cX509Ext); + } + sk_X509_EXTENSION_pop_free(x509->cert_info->extensions, X509_EXTENSION_free); + x509->cert_info->extensions = NULL; + for (i=0; ilen; i++) { + ext = DupX509ExtPtr(RARRAY(ary)->ptr[i]); + + if (!X509_add_ext(x509, ext, -1)) { /* DUPs ext - FREE it */ + X509_EXTENSION_free(ext); + ossl_raise(eX509CertError, NULL); + } + X509_EXTENSION_free(ext); + } + + return ary; +} + +static VALUE +ossl_x509_add_extension(VALUE self, VALUE extension) +{ + X509 *x509; + X509_EXTENSION *ext; + + GetX509(self, x509); + ext = DupX509ExtPtr(extension); + if (!X509_add_ext(x509, ext, -1)) { /* DUPs ext - FREE it */ + X509_EXTENSION_free(ext); + ossl_raise(eX509CertError, NULL); + } + X509_EXTENSION_free(ext); + + return extension; +} + +static VALUE +ossl_x509_inspect(VALUE self) +{ + VALUE str; + char *cname = rb_class2name(rb_obj_class(self)); + + str = rb_str_new2("#<"); + rb_str_cat2(str, cname); + rb_str_cat2(str, " "); + + rb_str_cat2(str, "subject="); + rb_str_append(str, rb_inspect(ossl_x509_get_subject(self))); + rb_str_cat2(str, ", "); + + rb_str_cat2(str, "issuer="); + rb_str_append(str, rb_inspect(ossl_x509_get_issuer(self))); + rb_str_cat2(str, ", "); + + rb_str_cat2(str, "serial="); + rb_str_append(str, rb_inspect(ossl_x509_get_serial(self))); + rb_str_cat2(str, ", "); + + rb_str_cat2(str, "not_before="); + rb_str_append(str, rb_inspect(ossl_x509_get_not_before(self))); + rb_str_cat2(str, ", "); + + rb_str_cat2(str, "not_after="); + rb_str_append(str, rb_inspect(ossl_x509_get_not_after(self))); + + str = rb_str_cat2(str, ">"); + + return str; +} + +/* + * INIT + */ +void +Init_ossl_x509cert() +{ + eX509CertError = rb_define_class_under(mX509, "CertificateError", eOSSLError); + + cX509Cert = rb_define_class_under(mX509, "Certificate", rb_cObject); + + rb_define_alloc_func(cX509Cert, ossl_x509_alloc); + rb_define_method(cX509Cert, "initialize", ossl_x509_initialize, -1); + rb_define_copy_func(cX509Cert, ossl_x509_copy); + + rb_define_method(cX509Cert, "to_der", ossl_x509_to_der, 0); + rb_define_method(cX509Cert, "to_pem", ossl_x509_to_pem, 0); + rb_define_alias(cX509Cert, "to_s", "to_pem"); + rb_define_method(cX509Cert, "to_text", ossl_x509_to_text, 0); + rb_define_method(cX509Cert, "version", ossl_x509_get_version, 0); + rb_define_method(cX509Cert, "version=", ossl_x509_set_version, 1); + rb_define_method(cX509Cert, "signature_algorithm", ossl_x509_get_signature_algorithm, 0); + rb_define_method(cX509Cert, "serial", ossl_x509_get_serial, 0); + rb_define_method(cX509Cert, "serial=", ossl_x509_set_serial, 1); + rb_define_method(cX509Cert, "subject", ossl_x509_get_subject, 0); + rb_define_method(cX509Cert, "subject=", ossl_x509_set_subject, 1); + rb_define_method(cX509Cert, "issuer", ossl_x509_get_issuer, 0); + rb_define_method(cX509Cert, "issuer=", ossl_x509_set_issuer, 1); + rb_define_method(cX509Cert, "not_before", ossl_x509_get_not_before, 0); + rb_define_method(cX509Cert, "not_before=", ossl_x509_set_not_before, 1); + rb_define_method(cX509Cert, "not_after", ossl_x509_get_not_after, 0); + rb_define_method(cX509Cert, "not_after=", ossl_x509_set_not_after, 1); + rb_define_method(cX509Cert, "public_key", ossl_x509_get_public_key, 0); + rb_define_method(cX509Cert, "public_key=", ossl_x509_set_public_key, 1); + rb_define_method(cX509Cert, "sign", ossl_x509_sign, 2); + rb_define_method(cX509Cert, "verify", ossl_x509_verify, 1); + rb_define_method(cX509Cert, "check_private_key", ossl_x509_check_private_key, 1); + rb_define_method(cX509Cert, "extensions", ossl_x509_get_extensions, 0); + rb_define_method(cX509Cert, "extensions=", ossl_x509_set_extensions, 1); + rb_define_method(cX509Cert, "add_extension", ossl_x509_add_extension, 1); + rb_define_method(cX509Cert, "inspect", ossl_x509_inspect, 0); +} + diff --git a/ext/openssl/ossl_x509crl.c b/ext/openssl/ossl_x509crl.c new file mode 100644 index 0000000000..2c1f9e0cdb --- /dev/null +++ b/ext/openssl/ossl_x509crl.c @@ -0,0 +1,551 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" + +#define WrapX509CRL(klass, obj, crl) do { \ + if (!crl) { \ + ossl_raise(rb_eRuntimeError, "CRL wasn't initialized!"); \ + } \ + obj = Data_Wrap_Struct(klass, 0, X509_CRL_free, crl); \ +} while (0) +#define GetX509CRL(obj, crl) do { \ + Data_Get_Struct(obj, X509_CRL, crl); \ + if (!crl) { \ + ossl_raise(rb_eRuntimeError, "CRL wasn't initialized!"); \ + } \ +} while (0) +#define SafeGetX509CRL(obj, crl) do { \ + OSSL_Check_Kind(obj, cX509CRL); \ + GetX509CRL(obj, crl); \ +} while (0) + +/* + * Classes + */ +VALUE cX509CRL; +VALUE eX509CRLError; + +/* + * PUBLIC + */ +X509_CRL * +GetX509CRLPtr(VALUE obj) +{ + X509_CRL *crl; + + SafeGetX509CRL(obj, crl); + + return crl; +} + +X509_CRL * +DupX509CRLPtr(VALUE obj) +{ + X509_CRL *crl; + + SafeGetX509CRL(obj, crl); + CRYPTO_add(&crl->references, 1, CRYPTO_LOCK_X509_CRL); + + return crl; +} + +VALUE +ossl_x509crl_new(X509_CRL *crl) +{ + X509_CRL *tmp; + VALUE obj; + + tmp = crl ? X509_CRL_dup(crl) : X509_CRL_new(); + if(!tmp) ossl_raise(eX509CRLError, NULL); + WrapX509CRL(cX509CRL, obj, tmp); + + return obj; +} + +/* + * PRIVATE + */ +static VALUE +ossl_x509crl_alloc(VALUE klass) +{ + X509_CRL *crl; + VALUE obj; + + if (!(crl = X509_CRL_new())) { + ossl_raise(eX509CRLError, NULL); + } + WrapX509CRL(klass, obj, crl); + + return obj; +} +DEFINE_ALLOC_WRAPPER(ossl_x509crl_alloc) + +static VALUE +ossl_x509crl_initialize(int argc, VALUE *argv, VALUE self) +{ + BIO *in; + X509_CRL *crl; + VALUE buffer; + + if (rb_scan_args(argc, argv, "01", &buffer) == 0) { + return self; + } + StringValue(buffer); + + in = BIO_new_mem_buf(RSTRING(buffer)->ptr, RSTRING(buffer)->len); + if (!in) { + ossl_raise(eX509CRLError, NULL); + } + /* + * TODO: + * Check if we should free CRL + X509_CRL_free(DATA_PTR(self)); + */ + crl = PEM_read_bio_X509_CRL(in, (X509_CRL **)&DATA_PTR(self), NULL, NULL); + if (!crl) { + BIO_reset(in); + + crl = d2i_X509_CRL_bio(in, (X509_CRL **)&DATA_PTR(self)); + } + if (!crl) { + BIO_free(in); + ossl_raise(eX509CRLError, NULL); + } + BIO_free(in); + + return self; +} + +static VALUE +ossl_x509crl_copy(VALUE self, VALUE other) +{ + X509_CRL *a, *b, *crl; + + rb_check_frozen(self); + if (self == other) return self; + GetX509CRL(self, a); + SafeGetX509CRL(other, b); + if (!(crl = X509_CRL_dup(b))) { + ossl_raise(eX509CRLError, NULL); + } + X509_CRL_free(a); + DATA_PTR(self) = crl; + + return self; +} + +static VALUE +ossl_x509crl_get_version(VALUE self) +{ + X509_CRL *crl; + long ver; + + GetX509CRL(self, crl); + ver = X509_CRL_get_version(crl); + + return LONG2NUM(ver); +} + +static VALUE +ossl_x509crl_set_version(VALUE self, VALUE version) +{ + X509_CRL *crl; + long ver; + + GetX509CRL(self, crl); + + if ((ver = NUM2LONG(version)) < 0) { + ossl_raise(eX509CRLError, "version must be >= 0!"); + } + if (!X509_CRL_set_version(crl, ver)) { + ossl_raise(eX509CRLError, NULL); + } + + return version; +} + +static VALUE +ossl_x509crl_get_signature_algorithm(VALUE self) +{ + X509_CRL *crl; + BIO *out; + BUF_MEM *buf; + VALUE str; + + GetX509CRL(self, crl); + + if (!(out = BIO_new(BIO_s_mem()))) { + ossl_raise(eX509CRLError, NULL); + } + if (!i2a_ASN1_OBJECT(out, crl->sig_alg->algorithm)) { + BIO_free(out); + ossl_raise(eX509CRLError, NULL); + } + BIO_get_mem_ptr(out, &buf); + str = rb_str_new(buf->data, buf->length); + BIO_free(out); + return str; +} + +static VALUE +ossl_x509crl_get_issuer(VALUE self) +{ + X509_CRL *crl; + + GetX509CRL(self, crl); + + return ossl_x509name_new(X509_CRL_get_issuer(crl)); /* NO DUP - don't free */ +} + +static VALUE +ossl_x509crl_set_issuer(VALUE self, VALUE issuer) +{ + X509_CRL *crl; + + GetX509CRL(self, crl); + + if (!X509_CRL_set_issuer_name(crl, GetX509NamePtr(issuer))) { /* DUPs name */ + ossl_raise(eX509CRLError, NULL); + } + return issuer; +} + +static VALUE +ossl_x509crl_get_last_update(VALUE self) +{ + X509_CRL *crl; + + GetX509CRL(self, crl); + + return asn1time_to_time(X509_CRL_get_lastUpdate(crl)); +} + +static VALUE +ossl_x509crl_set_last_update(VALUE self, VALUE time) +{ + X509_CRL *crl; + time_t sec; + + GetX509CRL(self, crl); + sec = time_to_time_t(time); + if (!X509_time_adj(crl->crl->lastUpdate, 0, &sec)) { + ossl_raise(eX509CRLError, NULL); + } + + return time; +} + +static VALUE +ossl_x509crl_get_next_update(VALUE self) +{ + X509_CRL *crl; + + GetX509CRL(self, crl); + + return asn1time_to_time(X509_CRL_get_nextUpdate(crl)); +} + +static VALUE +ossl_x509crl_set_next_update(VALUE self, VALUE time) +{ + X509_CRL *crl; + time_t sec; + + GetX509CRL(self, crl); + sec = time_to_time_t(time); + /* This must be some thinko in OpenSSL */ + if (!(crl->crl->nextUpdate = X509_time_adj(crl->crl->nextUpdate, 0, &sec))){ + ossl_raise(eX509CRLError, NULL); + } + + return time; +} + +static VALUE +ossl_x509crl_get_revoked(VALUE self) +{ + X509_CRL *crl; + int i, num; + X509_REVOKED *rev; + VALUE ary, revoked; + + GetX509CRL(self, crl); + num = sk_X509_CRL_num(X509_CRL_get_REVOKED(crl)); + if (num < 0) { + OSSL_Debug("num < 0???"); + return rb_ary_new(); + } + ary = rb_ary_new2(num); + for(i=0; ilen; i++) { + OSSL_Check_Kind(RARRAY(ary)->ptr[i], cX509Rev); + } + sk_X509_REVOKED_pop_free(crl->crl->revoked, X509_REVOKED_free); + crl->crl->revoked = NULL; + for (i=0; ilen; i++) { + rev = DupX509RevokedPtr(RARRAY(ary)->ptr[i]); + if (!X509_CRL_add0_revoked(crl, rev)) { /* NO DUP - don't free! */ + ossl_raise(eX509CRLError, NULL); + } + } + X509_CRL_sort(crl); + + return ary; +} + +static VALUE +ossl_x509crl_add_revoked(VALUE self, VALUE revoked) +{ + X509_CRL *crl; + X509_REVOKED *rev; + + GetX509CRL(self, crl); + rev = DupX509RevokedPtr(revoked); + if (!X509_CRL_add0_revoked(crl, rev)) { /* NO DUP - don't free! */ + ossl_raise(eX509CRLError, NULL); + } + X509_CRL_sort(crl); + + return revoked; +} + +static VALUE +ossl_x509crl_sign(VALUE self, VALUE key, VALUE digest) +{ + X509_CRL *crl; + EVP_PKEY *pkey; + const EVP_MD *md; + + GetX509CRL(self, crl); + pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */ + md = GetDigestPtr(digest); + if (!X509_CRL_sign(crl, pkey, md)) { + ossl_raise(eX509CRLError, NULL); + } + + return self; +} + +static VALUE +ossl_x509crl_verify(VALUE self, VALUE key) +{ + X509_CRL *crl; + int ret; + + GetX509CRL(self, crl); + if ((ret = X509_CRL_verify(crl, GetPKeyPtr(key))) < 0) { + ossl_raise(eX509CRLError, NULL); + } + if (ret == 1) { + return Qtrue; + } + + return Qfalse; +} + +static VALUE +ossl_x509crl_to_der(VALUE self) +{ + X509_CRL *crl; + BIO *out; + BUF_MEM *buf; + VALUE str; + + GetX509CRL(self, crl); + if (!(out = BIO_new(BIO_s_mem()))) { + ossl_raise(eX509CRLError, NULL); + } + if (!i2d_X509_CRL_bio(out, crl)) { + BIO_free(out); + ossl_raise(eX509CRLError, NULL); + } + BIO_get_mem_ptr(out, &buf); + str = rb_str_new(buf->data, buf->length); + BIO_free(out); + + return str; +} + +static VALUE +ossl_x509crl_to_pem(VALUE self) +{ + X509_CRL *crl; + BIO *out; + BUF_MEM *buf; + VALUE str; + + GetX509CRL(self, crl); + if (!(out = BIO_new(BIO_s_mem()))) { + ossl_raise(eX509CRLError, NULL); + } + if (!PEM_write_bio_X509_CRL(out, crl)) { + BIO_free(out); + ossl_raise(eX509CRLError, NULL); + } + BIO_get_mem_ptr(out, &buf); + str = rb_str_new(buf->data, buf->length); + BIO_free(out); + + return str; +} + +static VALUE +ossl_x509crl_to_text(VALUE self) +{ + X509_CRL *crl; + BIO *out; + BUF_MEM *buf; + VALUE str; + + GetX509CRL(self, crl); + if (!(out = BIO_new(BIO_s_mem()))) { + ossl_raise(eX509CRLError, NULL); + } + if (!X509_CRL_print(out, crl)) { + BIO_free(out); + ossl_raise(eX509CRLError, NULL); + } + BIO_get_mem_ptr(out, &buf); + str = rb_str_new(buf->data, buf->length); + BIO_free(out); + + return str; +} + +/* + * Gets X509v3 extensions as array of X509Ext objects + */ +static VALUE +ossl_x509crl_get_extensions(VALUE self) +{ + X509_CRL *crl; + int count, i; + X509_EXTENSION *ext; + VALUE ary; + + GetX509CRL(self, crl); + count = X509_CRL_get_ext_count(crl); + if (count < 0) { + OSSL_Debug("count < 0???"); + return rb_ary_new(); + } + ary = rb_ary_new2(count); + for (i=0; ilen; i++) { + OSSL_Check_Kind(RARRAY(ary)->ptr[i], cX509Ext); + } + sk_X509_EXTENSION_pop_free(crl->crl->extensions, X509_EXTENSION_free); + crl->crl->extensions = NULL; + for (i=0; ilen; i++) { + ext = DupX509ExtPtr(RARRAY(ary)->ptr[i]); + if(!X509_CRL_add_ext(crl, ext, -1)) { /* DUPs ext - FREE it */ + X509_EXTENSION_free(ext); + ossl_raise(eX509CRLError, NULL); + } + X509_EXTENSION_free(ext); + } + + return ary; +} + +static VALUE +ossl_x509crl_add_extension(VALUE self, VALUE extension) +{ + X509_CRL *crl; + X509_EXTENSION *ext; + + GetX509CRL(self, crl); + ext = DupX509ExtPtr(extension); + if (!X509_CRL_add_ext(crl, ext, -1)) { /* DUPs ext - FREE it */ + X509_EXTENSION_free(ext); + ossl_raise(eX509CRLError, NULL); + } + X509_EXTENSION_free(ext); + + return extension; +} + +/* + * INIT + */ +void +Init_ossl_x509crl() +{ + eX509CRLError = rb_define_class_under(mX509, "CRLError", eOSSLError); + + cX509CRL = rb_define_class_under(mX509, "CRL", rb_cObject); + + rb_define_alloc_func(cX509CRL, ossl_x509crl_alloc); + rb_define_method(cX509CRL, "initialize", ossl_x509crl_initialize, -1); + rb_define_copy_func(cX509CRL, ossl_x509crl_copy); + + rb_define_method(cX509CRL, "version", ossl_x509crl_get_version, 0); + rb_define_method(cX509CRL, "version=", ossl_x509crl_set_version, 1); + rb_define_method(cX509CRL, "signature_algorithm", ossl_x509crl_get_signature_algorithm, 0); + rb_define_method(cX509CRL, "issuer", ossl_x509crl_get_issuer, 0); + rb_define_method(cX509CRL, "issuer=", ossl_x509crl_set_issuer, 1); + rb_define_method(cX509CRL, "last_update", ossl_x509crl_get_last_update, 0); + rb_define_method(cX509CRL, "last_update=", ossl_x509crl_set_last_update, 1); + rb_define_method(cX509CRL, "next_update", ossl_x509crl_get_next_update, 0); + rb_define_method(cX509CRL, "next_update=", ossl_x509crl_set_next_update, 1); + rb_define_method(cX509CRL, "revoked", ossl_x509crl_get_revoked, 0); + rb_define_method(cX509CRL, "revoked=", ossl_x509crl_set_revoked, 1); + rb_define_method(cX509CRL, "add_revoked", ossl_x509crl_add_revoked, 1); + rb_define_method(cX509CRL, "sign", ossl_x509crl_sign, 2); + rb_define_method(cX509CRL, "verify", ossl_x509crl_verify, 1); + rb_define_method(cX509CRL, "to_der", ossl_x509crl_to_der, 0); + rb_define_method(cX509CRL, "to_pem", ossl_x509crl_to_pem, 0); + rb_define_alias(cX509CRL, "to_s", "to_pem"); + rb_define_method(cX509CRL, "to_text", ossl_x509crl_to_text, 0); + rb_define_method(cX509CRL, "extensions", ossl_x509crl_get_extensions, 0); + rb_define_method(cX509CRL, "extensions=", ossl_x509crl_set_extensions, 1); + rb_define_method(cX509CRL, "add_extension", ossl_x509crl_add_extension, 1); +} + diff --git a/ext/openssl/ossl_x509ext.c b/ext/openssl/ossl_x509ext.c new file mode 100644 index 0000000000..4d981ed938 --- /dev/null +++ b/ext/openssl/ossl_x509ext.c @@ -0,0 +1,345 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" + +#define WrapX509Ext(klass, obj, ext) do { \ + if (!ext) { \ + ossl_raise(rb_eRuntimeError, "EXT wasn't initialized!"); \ + } \ + obj = Data_Wrap_Struct(klass, 0, X509_EXTENSION_free, ext); \ +} while (0) +#define GetX509Ext(obj, ext) do { \ + Data_Get_Struct(obj, X509_EXTENSION, ext); \ + if (!ext) { \ + ossl_raise(rb_eRuntimeError, "EXT wasn't initialized!"); \ + } \ +} while (0) +#define SafeGetX509Ext(obj, ext) do { \ + OSSL_Check_Kind(obj, cX509Ext); \ + GetX509Ext(obj, ext); \ +} while (0) + +#define MakeX509ExtFactory(klass, obj, ctx) \ + obj = Data_Make_Struct(klass, X509V3_CTX, 0, ossl_x509extfactory_free, ctx) +#define GetX509ExtFactory(obj, ctx) do { \ + Data_Get_Struct(obj, X509V3_CTX, ctx); \ + if (!ctx) { \ + ossl_raise(rb_eRuntimeError, "CTX wasn't initialized!"); \ + } \ +} while (0) + +/* + * Classes + */ +VALUE cX509Ext; +VALUE cX509ExtFactory; +VALUE eX509ExtError; + +/* + * Public + */ +VALUE +ossl_x509ext_new(X509_EXTENSION *ext) +{ + X509_EXTENSION *new; + VALUE obj; + + if (!ext) { + new = X509_EXTENSION_new(); + } else { + new = X509_EXTENSION_dup(ext); + } + if (!new) { + ossl_raise(eX509ExtError, NULL); + } + WrapX509Ext(cX509Ext, obj, new); + + return obj; +} + +X509_EXTENSION * +GetX509ExtPtr(VALUE obj) +{ + X509_EXTENSION *ext; + + SafeGetX509Ext(obj, ext); + + return ext; +} + +X509_EXTENSION * +DupX509ExtPtr(VALUE obj) +{ + X509_EXTENSION *ext, *new; + + SafeGetX509Ext(obj, ext); + if (!(new = X509_EXTENSION_dup(ext))) { + ossl_raise(eX509ExtError, NULL); + } + + return new; +} + +/* + * Private + */ +/* + * Ext factory + */ +static void +ossl_x509extfactory_free(X509V3_CTX *ctx) +{ + if (ctx) { + if (ctx->issuer_cert) X509_free(ctx->issuer_cert); + if (ctx->subject_cert) X509_free(ctx->subject_cert); + if (ctx->crl) X509_CRL_free(ctx->crl); + if (ctx->subject_req) X509_REQ_free(ctx->subject_req); + OPENSSL_free(ctx); + } +} + +static VALUE +ossl_x509extfactory_alloc(VALUE klass) +{ + X509V3_CTX *ctx; + VALUE obj; + + MakeX509ExtFactory(klass, obj, ctx); + + return obj; +} +DEFINE_ALLOC_WRAPPER(ossl_x509extfactory_alloc) + +static VALUE +ossl_x509extfactory_set_issuer_cert(VALUE self, VALUE cert) +{ + X509V3_CTX *ctx; + + GetX509ExtFactory(self, ctx); + ctx->issuer_cert = DupX509CertPtr(cert); /* DUP NEEDED */ + + return cert; +} + +static VALUE +ossl_x509extfactory_set_subject_cert(VALUE self, VALUE cert) +{ + X509V3_CTX *ctx; + + GetX509ExtFactory(self, ctx); + ctx->subject_cert = DupX509CertPtr(cert); /* DUP NEEDED */ + + return cert; +} + +static VALUE +ossl_x509extfactory_set_subject_req(VALUE self, VALUE req) +{ + X509V3_CTX *ctx; + + GetX509ExtFactory(self, ctx); + ctx->subject_req = DupX509ReqPtr(req); + + return req; +} + +static VALUE +ossl_x509extfactory_set_crl(VALUE self, VALUE crl) +{ + X509V3_CTX *ctx; + + GetX509ExtFactory(self, ctx); + ctx->crl = DupX509CRLPtr(crl); + + return crl; +} + +static VALUE +ossl_x509extfactory_initialize(int argc, VALUE *argv, VALUE self) +{ + /*X509V3_CTX *ctx;*/ + VALUE issuer_cert, subject_cert, subject_req, crl; + + /*GetX509ExtFactory(self, ctx);*/ + + rb_scan_args(argc, argv, "04", &issuer_cert, &subject_cert, &subject_req, &crl); + + if (!NIL_P(issuer_cert)) { + ossl_x509extfactory_set_issuer_cert(self, issuer_cert); + } + if (!NIL_P(subject_cert)) { + ossl_x509extfactory_set_subject_cert(self, subject_cert); + } + if (!NIL_P(subject_req)) { + ossl_x509extfactory_set_subject_req(self, subject_req); + } + if (!NIL_P(crl)) { + ossl_x509extfactory_set_crl(self, crl); + } + return self; +} + +/* + * Array to X509_EXTENSION + * Structure: + * ["ln", "value", bool_critical] or + * ["sn", "value", bool_critical] or + * ["ln", "critical,value"] or the same for sn + * ["ln", "value"] => not critical + */ +static VALUE +ossl_x509extfactory_create_ext_from_array(VALUE self, VALUE ary) +{ + X509V3_CTX *ctx; + X509_EXTENSION *ext; + int nid; + char *value; + VALUE item, obj; + + GetX509ExtFactory(self, ctx); + Check_Type(ary, T_ARRAY); + if ((RARRAY(ary)->len) < 2 || (RARRAY(ary)->len > 3)) { /*2 or 3 allowed*/ + ossl_raise(eX509ExtError, "unsupported structure"); + } + /* key [0] */ + item = RARRAY(ary)->ptr[0]; + StringValue(item); + if (!(nid = OBJ_ln2nid(RSTRING(item)->ptr))) { + if (!(nid = OBJ_sn2nid(RSTRING(item)->ptr))) { + ossl_raise(eX509ExtError, NULL); + } + } + /* data [1] */ + item = RARRAY(ary)->ptr[1]; + StringValue(item); + /* (optional) critical [2] */ + if (RARRAY(ary)->len == 3 && RARRAY(ary)->ptr[2] == Qtrue) { + if (!(value = OPENSSL_malloc(strlen("critical,") + + (RSTRING(item)->len) + 1))) { + ossl_raise(eX509ExtError, "malloc error"); + } + strcpy(value, "critical,"); + strncat(value, RSTRING(item)->ptr, RSTRING(item)->len); + } else { + value = strdup(StringValuePtr(item)); + } + if (!(ext = X509V3_EXT_conf_nid(NULL, ctx, nid, value))) { + OPENSSL_free(value); + ossl_raise(eX509ExtError, NULL); + } + OPENSSL_free(value); + WrapX509Ext(cX509Ext, obj, ext); + + return obj; +} + +/* + * Ext + */ +static VALUE +ossl_x509ext_get_oid(VALUE obj) +{ + X509_EXTENSION *ext; + ASN1_OBJECT *extobj; + BIO *out; + VALUE ret; + int nid, status = 0; + + GetX509Ext(obj, ext); + extobj = X509_EXTENSION_get_object(ext); + if ((nid = OBJ_obj2nid(extobj)) != NID_undef) + ret = rb_str_new2(OBJ_nid2sn(nid)); + else{ + if (!(out = BIO_new(BIO_s_mem()))) + ossl_raise(eX509ExtError, NULL); + i2a_ASN1_OBJECT(out, extobj); + ret = ossl_protect_membio2str(out, &status); + BIO_free(out); + if(status) rb_jump_tag(status); + } + + return ret; +} + +static VALUE +ossl_x509ext_get_value(VALUE obj) +{ + X509_EXTENSION *ext; + BIO *out; + VALUE ret; + int status = 0; + + GetX509Ext(obj, ext); + if (!(out = BIO_new(BIO_s_mem()))) + ossl_raise(eX509ExtError, NULL); + if (!X509V3_EXT_print(out, ext, 0, 0)) + M_ASN1_OCTET_STRING_print(out, ext->value); + ret = ossl_protect_membio2str(out, &status); + BIO_free(out); + if(status) rb_jump_tag(status); + + return ret; +} + +static VALUE +ossl_x509ext_get_critical(VALUE obj) +{ + X509_EXTENSION *ext; + GetX509Ext(obj, ext); + return X509_EXTENSION_get_critical(ext) ? Qtrue : Qfalse; +} + +static VALUE +ossl_x509ext_to_der(VALUE obj) +{ + X509_EXTENSION *ext; + unsigned char *p; + int len; + VALUE str; + + GetX509Ext(obj, ext); + p = NULL; + if((len = i2d_X509_EXTENSION(ext, &p)) < 0) + ossl_raise(eX509ExtError, NULL); + str = rb_str_new(p, len); + free(p); + + return str; +} + +/* + * INIT + */ +void +Init_ossl_x509ext() +{ + eX509ExtError = rb_define_class_under(mX509, "ExtensionError", eOSSLError); + + cX509ExtFactory = rb_define_class_under(mX509, "ExtensionFactory", rb_cObject); + + rb_define_alloc_func(cX509ExtFactory, ossl_x509extfactory_alloc); + rb_define_method(cX509ExtFactory, "initialize", ossl_x509extfactory_initialize, -1); + + rb_define_method(cX509ExtFactory, "issuer_certificate=", ossl_x509extfactory_set_issuer_cert, 1); + rb_define_method(cX509ExtFactory, "subject_certificate=", ossl_x509extfactory_set_subject_cert, 1); + rb_define_method(cX509ExtFactory, "subject_request=", ossl_x509extfactory_set_subject_req, 1); + rb_define_method(cX509ExtFactory, "crl=", ossl_x509extfactory_set_crl, 1); + rb_define_method(cX509ExtFactory, "create_ext_from_array", ossl_x509extfactory_create_ext_from_array, 1); + + cX509Ext = rb_define_class_under(mX509, "Extension", rb_cObject); + rb_undef_method(CLASS_OF(cX509Ext), "new"); +/* rb_define_alloc_func(cX509Ext, ossl_x509ext_alloc); */ +/* rb_define_method(cX509Ext, "initialize", ossl_x509ext_initialize, -1); */ + rb_define_method(cX509Ext, "oid", ossl_x509ext_get_oid, 0); + rb_define_method(cX509Ext, "value", ossl_x509ext_get_value, 0); + rb_define_method(cX509Ext, "critical?", ossl_x509ext_get_critical, 0); + rb_define_method(cX509Ext, "to_der", ossl_x509ext_to_der, 0); +} diff --git a/ext/openssl/ossl_x509name.c b/ext/openssl/ossl_x509name.c new file mode 100644 index 0000000000..a1e5cba1b4 --- /dev/null +++ b/ext/openssl/ossl_x509name.c @@ -0,0 +1,234 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" +#include "st.h" /* For st_foreach -- ST_CONTINUE */ + +#define WrapX509Name(klass, obj, name) do { \ + if (!name) { \ + ossl_raise(rb_eRuntimeError, "Name wasn't initialized."); \ + } \ + obj = Data_Wrap_Struct(klass, 0, X509_NAME_free, name); \ +} while (0) +#define GetX509Name(obj, name) do { \ + Data_Get_Struct(obj, X509_NAME, name); \ + if (!name) { \ + ossl_raise(rb_eRuntimeError, "Name wasn't initialized."); \ + } \ +} while (0) +#define SafeGetX509Name(obj, name) do { \ + OSSL_Check_Kind(obj, cX509Name); \ + GetX509Name(obj, name); \ +} while (0) + +/* + * Classes + */ +VALUE cX509Name; +VALUE eX509NameError; + +/* + * Public + */ +VALUE +ossl_x509name_new(X509_NAME *name) +{ + X509_NAME *new; + VALUE obj; + + if (!name) { + new = X509_NAME_new(); + } else { + new = X509_NAME_dup(name); + } + if (!new) { + ossl_raise(eX509NameError, NULL); + } + WrapX509Name(cX509Name, obj, new); + + return obj; +} + +X509_NAME * +GetX509NamePtr(VALUE obj) +{ + X509_NAME *name; + + SafeGetX509Name(obj, name); + + return name; +} + +/* + * Private + */ +static VALUE +ossl_x509name_alloc(VALUE klass) +{ + X509_NAME *name; + VALUE obj; + + if (!(name = X509_NAME_new())) { + ossl_raise(eX509NameError, NULL); + } + WrapX509Name(klass, obj, name); + + return obj; +} +DEFINE_ALLOC_WRAPPER(ossl_x509name_alloc) + +static VALUE +ossl_x509name_initialize(int argc, VALUE *argv, VALUE self) +{ + X509_NAME *name; + int i, type; + VALUE arg, item, key, value; + + GetX509Name(self, name); + if (rb_scan_args(argc, argv, "01", &arg) == 0) { + return self; + } + Check_Type(arg, T_ARRAY); + for (i=0; ilen; i++) { + item = RARRAY(arg)->ptr[i]; + Check_Type(item, T_ARRAY); + if (RARRAY(item)->len != 2) { + ossl_raise(rb_eArgError, "Unsupported structure."); + } + key = RARRAY(item)->ptr[0]; + value = RARRAY(item)->ptr[1]; + StringValue(key); + StringValue(value); + type = ASN1_PRINTABLE_type(RSTRING(value)->ptr, -1); + if (!X509_NAME_add_entry_by_txt(name, RSTRING(key)->ptr, type, + RSTRING(value)->ptr, RSTRING(value)->len, -1, 0)) { + ossl_raise(eX509NameError, NULL); + } + } + + return self; +} + +static VALUE +ossl_x509name_to_s(VALUE self) +{ + X509_NAME *name; + char *buf; + VALUE str; + + GetX509Name(self, name); + buf = X509_NAME_oneline(name, NULL, 0); + str = rb_str_new2(buf); + OPENSSL_free(buf); + + return str; +} + +static VALUE +ossl_x509name_to_a(VALUE self) +{ + X509_NAME *name; + X509_NAME_ENTRY *entry; + int i,entries; + char long_name[512]; + const char *short_name; + VALUE ary; + + GetX509Name(self, name); + entries = X509_NAME_entry_count(name); + if (entries < 0) { + OSSL_Debug("name entries < 0!"); + return rb_ary_new(); + } + ary = rb_ary_new2(entries); + for (i=0; iobject)) { + ossl_raise(eX509NameError, NULL); + } + short_name = OBJ_nid2sn(OBJ_ln2nid(long_name)); + + rb_ary_push(ary, rb_assoc_new(rb_str_new2(short_name), + rb_str_new(entry->value->data, entry->value->length))); + } + return ary; +} + +static int +ossl_x509name_cmp0(VALUE self, VALUE other) +{ + X509_NAME *name1, *name2; + + GetX509Name(self, name1); + SafeGetX509Name(other, name2); + + return X509_NAME_cmp(name1, name2); +} + +static VALUE +ossl_x509name_cmp(VALUE self, VALUE other) +{ + int result; + + result = ossl_x509name_cmp0(self, other); + if (result < 0) return INT2FIX(-1); + if (result > 1) return INT2FIX(1); + + return INT2FIX(0); +} + +static VALUE +ossl_x509name_eql(VALUE self, VALUE other) +{ + int result; + + if(CLASS_OF(other) != cX509Name) return Qfalse; + result = ossl_x509name_cmp0(self, other); + + return (result == 0) ? Qtrue : Qfalse; +} + +static VALUE +ossl_x509name_hash(VALUE self) +{ + X509_NAME *name; + unsigned long hash; + + GetX509Name(self, name); + + hash = X509_NAME_hash(name); + + return ULONG2NUM(hash); +} + +/* + * INIT + */ +void +Init_ossl_x509name() +{ + eX509NameError = rb_define_class_under(mX509, "NameError", eOSSLError); + + cX509Name = rb_define_class_under(mX509, "Name", rb_cObject); + + rb_define_alloc_func(cX509Name, ossl_x509name_alloc); + rb_define_method(cX509Name, "initialize", ossl_x509name_initialize, -1); + + rb_define_method(cX509Name, "to_s", ossl_x509name_to_s, 0); + rb_define_method(cX509Name, "to_a", ossl_x509name_to_a, 0); + + rb_define_method(cX509Name, "cmp", ossl_x509name_cmp, 1); + rb_define_alias(cX509Name, "<=>", "cmp"); + rb_define_method(cX509Name, "eql?", ossl_x509name_eql, 1); + + rb_define_method(cX509Name, "hash", ossl_x509name_hash, 0); +} diff --git a/ext/openssl/ossl_x509req.c b/ext/openssl/ossl_x509req.c new file mode 100644 index 0000000000..e7329d158b --- /dev/null +++ b/ext/openssl/ossl_x509req.c @@ -0,0 +1,449 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" + +#define WrapX509Req(klass, obj, req) do { \ + if (!req) { \ + ossl_raise(rb_eRuntimeError, "Req wasn't initialized!"); \ + } \ + obj = Data_Wrap_Struct(klass, 0, X509_REQ_free, req); \ +} while (0) +#define GetX509Req(obj, req) do { \ + Data_Get_Struct(obj, X509_REQ, req); \ + if (!req) { \ + ossl_raise(rb_eRuntimeError, "Req wasn't initialized!"); \ + } \ +} while (0) +#define SafeGetX509Req(obj, req) do { \ + OSSL_Check_Kind(obj, cX509Req); \ + GetX509Req(obj, req); \ +} while (0) + +/* + * Classes + */ +VALUE cX509Req; +VALUE eX509ReqError; + +/* + * Public functions + */ +VALUE +ossl_x509req_new(X509_REQ *req) +{ + X509_REQ *new; + VALUE obj; + + if (!req) { + new = X509_REQ_new(); + } else { + new = X509_REQ_dup(req); + } + if (!new) { + ossl_raise(eX509ReqError, NULL); + } + WrapX509Req(cX509Req, obj, new); + + return obj; +} + +X509_REQ * +DupX509ReqPtr(VALUE obj) +{ + X509_REQ *req, *new; + + SafeGetX509Req(obj, req); + if (!(new = X509_REQ_dup(req))) { + ossl_raise(eX509ReqError, NULL); + } + + return new; +} + +/* + * Private functions + */ +static VALUE +ossl_x509req_alloc(VALUE klass) +{ + X509_REQ *req; + VALUE obj; + + if (!(req = X509_REQ_new())) { + ossl_raise(eX509ReqError, NULL); + } + WrapX509Req(klass, obj, req); + + return obj; +} +DEFINE_ALLOC_WRAPPER(ossl_x509req_alloc) + +static VALUE +ossl_x509req_initialize(int argc, VALUE *argv, VALUE self) +{ + BIO *in; + X509_REQ *req; + VALUE buffer; + + if (rb_scan_args(argc, argv, "01", &buffer) == 0) { + return self; + } + StringValue(buffer); + + in = BIO_new_mem_buf(RSTRING(buffer)->ptr, RSTRING(buffer)->len); + if (!in) { + ossl_raise(eX509ReqError, NULL); + } + /* + * TODO: + * Check if we should + X509_REQ_free(DATA_PTR(self)); + */ + req = PEM_read_bio_X509_REQ(in, (X509_REQ **)&DATA_PTR(self), NULL, NULL); + if (!req) { + BIO_reset(in); + + req = d2i_X509_REQ_bio(in, (X509_REQ **)&DATA_PTR(self)); + } + if (!req) { + BIO_free(in); + ossl_raise(eX509ReqError, NULL); + } + BIO_free(in); + + return self; +} + +static VALUE +ossl_x509req_copy(VALUE self, VALUE other) +{ + X509_REQ *a, *b, *req; + + rb_check_frozen(self); + if (self == other) return self; + GetX509Req(self, a); + SafeGetX509Req(other, b); + if (!(req = X509_REQ_dup(b))) { + ossl_raise(eX509ReqError, NULL); + } + X509_REQ_free(a); + DATA_PTR(self) = req; + + return self; +} + +static VALUE +ossl_x509req_to_pem(VALUE self) +{ + X509_REQ *req; + BIO *out; + BUF_MEM *buf; + VALUE str; + + GetX509Req(self, req); + if (!(out = BIO_new(BIO_s_mem()))) { + ossl_raise(eX509ReqError, NULL); + } + if (!PEM_write_bio_X509_REQ(out, req)) { + BIO_free(out); + ossl_raise(eX509ReqError, NULL); + } + BIO_get_mem_ptr(out, &buf); + str = rb_str_new(buf->data, buf->length); + BIO_free(out); + + return str; +} + +static VALUE +ossl_x509req_to_text(VALUE self) +{ + X509_REQ *req; + BIO *out; + BUF_MEM *buf; + VALUE str; + + GetX509Req(self, req); + if (!(out = BIO_new(BIO_s_mem()))) { + ossl_raise(eX509ReqError, NULL); + } + if (!X509_REQ_print(out, req)) { + BIO_free(out); + ossl_raise(eX509ReqError, NULL); + } + BIO_get_mem_ptr(out, &buf); + str = rb_str_new(buf->data, buf->length); + BIO_free(out); + + return str; +} + +#if 0 +/* + * Makes X509 from X509_REQuest + */ +static VALUE +ossl_x509req_to_x509(VALUE self, VALUE days, VALUE key) +{ + X509_REQ *req; + X509 *x509; + + GetX509Req(self, req); + ... + if (!(x509 = X509_REQ_to_X509(req, d, pkey))) { + ossl_raise(eX509ReqError, NULL); + } + + return ossl_x509_new(x509); +} +#endif + +static VALUE +ossl_x509req_get_version(VALUE self) +{ + X509_REQ *req; + long version; + + GetX509Req(self, req); + version = X509_REQ_get_version(req); + + return LONG2FIX(version); +} + +static VALUE +ossl_x509req_set_version(VALUE self, VALUE version) +{ + X509_REQ *req; + long ver; + + GetX509Req(self, req); + if ((ver = FIX2LONG(version)) < 0) { + ossl_raise(eX509ReqError, "version must be >= 0!"); + } + if (!X509_REQ_set_version(req, ver)) { + ossl_raise(eX509ReqError, NULL); + } + + return version; +} + +static VALUE +ossl_x509req_get_subject(VALUE self) +{ + X509_REQ *req; + X509_NAME *name; + + GetX509Req(self, req); + if (!(name = X509_REQ_get_subject_name(req))) { /* NO DUP - don't free */ + ossl_raise(eX509ReqError, NULL); + } + + return ossl_x509name_new(name); +} + +static VALUE +ossl_x509req_set_subject(VALUE self, VALUE subject) +{ + X509_REQ *req; + + GetX509Req(self, req); + /* DUPs name */ + if (!X509_REQ_set_subject_name(req, GetX509NamePtr(subject))) { + ossl_raise(eX509ReqError, NULL); + } + + return subject; +} + +static VALUE +ossl_x509req_get_signature_algorithm(VALUE self) +{ + X509_REQ *req; + BIO *out; + BUF_MEM *buf; + VALUE str; + + GetX509Req(self, req); + + if (!(out = BIO_new(BIO_s_mem()))) { + ossl_raise(eX509ReqError, NULL); + } + if (!i2a_ASN1_OBJECT(out, req->sig_alg->algorithm)) { + BIO_free(out); + ossl_raise(eX509ReqError, NULL); + } + BIO_get_mem_ptr(out, &buf); + str = rb_str_new(buf->data, buf->length); + BIO_free(out); + return str; +} + +static VALUE +ossl_x509req_get_public_key(VALUE self) +{ + X509_REQ *req; + EVP_PKEY *pkey; + + GetX509Req(self, req); + if (!(pkey = X509_REQ_get_pubkey(req))) { /* adds reference */ + ossl_raise(eX509ReqError, NULL); + } + + return ossl_pkey_new(pkey); /* NO DUP - OK */ +} + +static VALUE +ossl_x509req_set_public_key(VALUE self, VALUE key) +{ + X509_REQ *req; + EVP_PKEY *pkey; + + GetX509Req(self, req); + pkey = GetPKeyPtr(key); /* NO NEED TO DUP */ + if (!X509_REQ_set_pubkey(req, pkey)) { + ossl_raise(eX509ReqError, NULL); + } + + return key; +} + +static VALUE +ossl_x509req_sign(VALUE self, VALUE key, VALUE digest) +{ + X509_REQ *req; + EVP_PKEY *pkey; + const EVP_MD *md; + + GetX509Req(self, req); + pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */ + md = GetDigestPtr(digest); + if (!X509_REQ_sign(req, pkey, md)) { + ossl_raise(eX509ReqError, NULL); + } + + return self; +} + +/* + * Checks that cert signature is made with PRIVversion of this PUBLIC 'key' + */ +static VALUE +ossl_x509req_verify(VALUE self, VALUE key) +{ + X509_REQ *req; + EVP_PKEY *pkey; + int i; + + GetX509Req(self, req); + pkey = GetPKeyPtr(key); /* NO NEED TO DUP */ + if ((i = X509_REQ_verify(req, pkey)) < 0) { + ossl_raise(eX509ReqError, NULL); + } + if (i > 0) { + return Qtrue; + } + + return Qfalse; +} + +static VALUE +ossl_x509req_get_attributes(VALUE self) +{ + X509_REQ *req; + int count, i; + X509_ATTRIBUTE *attr; + VALUE ary; + + GetX509Req(self, req); + + count = X509_REQ_get_attr_count(req); + if (count < 0) { + OSSL_Debug("count < 0???"); + return rb_ary_new(); + } + ary = rb_ary_new2(count); + for (i=0; ilen; i++) { + OSSL_Check_Kind(RARRAY(ary)->ptr[i], cX509Attr); + } + sk_X509_ATTRIBUTE_pop_free(req->req_info->attributes, X509_ATTRIBUTE_free); + req->req_info->attributes = NULL; + for (i=0;ilen; i++) { + item = RARRAY(ary)->ptr[i]; + attr = DupX509AttrPtr(item); + if (!X509_REQ_add1_attr(req, attr)) { + ossl_raise(eX509ReqError, NULL); + } + } + return ary; +} + +static VALUE +ossl_x509req_add_attribute(VALUE self, VALUE attr) +{ + X509_REQ *req; + + GetX509Req(self, req); + if (!X509_REQ_add1_attr(req, DupX509AttrPtr(attr))) { + ossl_raise(eX509ReqError, NULL); + } + + return attr; +} + +/* + * X509_REQUEST init + */ +void +Init_ossl_x509req() +{ + eX509ReqError = rb_define_class_under(mX509, "RequestError", eOSSLError); + + cX509Req = rb_define_class_under(mX509, "Request", rb_cObject); + + rb_define_alloc_func(cX509Req, ossl_x509req_alloc); + rb_define_method(cX509Req, "initialize", ossl_x509req_initialize, -1); + rb_define_copy_func(cX509Req, ossl_x509req_copy); + + rb_define_method(cX509Req, "to_pem", ossl_x509req_to_pem, 0); + rb_define_alias(cX509Req, "to_s", "to_pem"); + rb_define_method(cX509Req, "to_text", ossl_x509req_to_text, 0); + rb_define_method(cX509Req, "version", ossl_x509req_get_version, 0); + rb_define_method(cX509Req, "version=", ossl_x509req_set_version, 1); + rb_define_method(cX509Req, "subject", ossl_x509req_get_subject, 0); + rb_define_method(cX509Req, "subject=", ossl_x509req_set_subject, 1); + rb_define_method(cX509Req, "signature_algorithm", ossl_x509req_get_signature_algorithm, 0); + rb_define_method(cX509Req, "public_key", ossl_x509req_get_public_key, 0); + rb_define_method(cX509Req, "public_key=", ossl_x509req_set_public_key, 1); + rb_define_method(cX509Req, "sign", ossl_x509req_sign, 2); + rb_define_method(cX509Req, "verify", ossl_x509req_verify, 1); + rb_define_method(cX509Req, "attributes", ossl_x509req_get_attributes, 0); + rb_define_method(cX509Req, "attributes=", ossl_x509req_set_attributes, 1); + rb_define_method(cX509Req, "add_attribute", ossl_x509req_add_attribute, 1); +} + diff --git a/ext/openssl/ossl_x509revoked.c b/ext/openssl/ossl_x509revoked.c new file mode 100644 index 0000000000..1cd04eaa25 --- /dev/null +++ b/ext/openssl/ossl_x509revoked.c @@ -0,0 +1,230 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" + +#define WrapX509Rev(klass, obj, rev) do { \ + if (!rev) { \ + ossl_raise(rb_eRuntimeError, "REV wasn't initialized!"); \ + } \ + obj = Data_Wrap_Struct(klass, 0, X509_REVOKED_free, rev); \ +} while (0) +#define GetX509Rev(obj, rev) do { \ + Data_Get_Struct(obj, X509_REVOKED, rev); \ + if (!rev) { \ + ossl_raise(rb_eRuntimeError, "REV wasn't initialized!"); \ + } \ +} while (0) +#define SafeGetX509Rev(obj, rev) do { \ + OSSL_Check_Kind(obj, cX509Rev); \ + GetX509Rev(obj, rev); \ +} while (0) + +/* + * Classes + */ +VALUE cX509Rev; +VALUE eX509RevError; + +/* + * PUBLIC + */ +VALUE +ossl_x509revoked_new(X509_REVOKED *rev) +{ + X509_REVOKED *new; + VALUE obj; + + if (!rev) { + new = X509_REVOKED_new(); + } else { + new = X509_REVOKED_dup(rev); + } + if (!new) { + ossl_raise(eX509RevError, NULL); + } + WrapX509Rev(cX509Rev, obj, new); + + return obj; +} + +X509_REVOKED * +DupX509RevokedPtr(VALUE obj) +{ + X509_REVOKED *rev, *new; + + SafeGetX509Rev(obj, rev); + if (!(new = X509_REVOKED_dup(rev))) { + ossl_raise(eX509RevError, NULL); + } + + return new; +} + +/* + * PRIVATE + */ +static VALUE +ossl_x509revoked_alloc(VALUE klass) +{ + X509_REVOKED *rev; + VALUE obj; + + if (!(rev = X509_REVOKED_new())) { + ossl_raise(eX509RevError, NULL); + } + WrapX509Rev(klass, obj, rev); + + return obj; +} +DEFINE_ALLOC_WRAPPER(ossl_x509revoked_alloc) + +static VALUE +ossl_x509revoked_initialize(int argc, VALUE *argv, VALUE self) +{ + /* EMPTY */ + return self; +} + +static VALUE +ossl_x509revoked_get_serial(VALUE self) +{ + X509_REVOKED *rev; + + GetX509Rev(self, rev); + + return asn1integer_to_num(rev->serialNumber); +} + +static VALUE +ossl_x509revoked_set_serial(VALUE self, VALUE num) +{ + X509_REVOKED *rev; + + GetX509Rev(self, rev); + rev->serialNumber = num_to_asn1integer(num, rev->serialNumber); + + return num; +} + +static VALUE +ossl_x509revoked_get_time(VALUE self) +{ + X509_REVOKED *rev; + + GetX509Rev(self, rev); + + return asn1time_to_time(rev->revocationDate); +} + +static VALUE +ossl_x509revoked_set_time(VALUE self, VALUE time) +{ + X509_REVOKED *rev; + time_t sec; + + GetX509Rev(self, rev); + sec = time_to_time_t(time); + if (!X509_time_adj(rev->revocationDate, 0, &sec)) { + ossl_raise(eX509RevError, NULL); + } + + return time; +} +/* + * Gets X509v3 extensions as array of X509Ext objects + */ +static VALUE +ossl_x509revoked_get_extensions(VALUE self) +{ + X509_REVOKED *rev; + int count, i; + X509_EXTENSION *ext; + VALUE ary; + + GetX509Rev(self, rev); + count = X509_REVOKED_get_ext_count(rev); + if (count < 0) { + OSSL_Debug("count < 0???"); + return rb_ary_new(); + } + ary = rb_ary_new2(count); + for (i=0; ilen; i++) { + OSSL_Check_Kind(RARRAY(ary)->ptr[i], cX509Ext); + } + sk_X509_EXTENSION_pop_free(rev->extensions, X509_EXTENSION_free); + rev->extensions = NULL; + for (i=0; ilen; i++) { + item = RARRAY(ary)->ptr[i]; + ext = DupX509ExtPtr(item); + if(!X509_REVOKED_add_ext(rev, ext, -1)) { + ossl_raise(eX509RevError, NULL); + } + } + + return ary; +} + +static VALUE +ossl_x509revoked_add_extension(VALUE self, VALUE ext) +{ + X509_REVOKED *rev; + + GetX509Rev(self, rev); + if(!X509_REVOKED_add_ext(rev, DupX509ExtPtr(ext), -1)) { + ossl_raise(eX509RevError, NULL); + } + + return ext; +} + +/* + * INIT + */ +void +Init_ossl_x509revoked() +{ + eX509RevError = rb_define_class_under(mX509, "RevokedError", eOSSLError); + + cX509Rev = rb_define_class_under(mX509, "Revoked", rb_cObject); + + rb_define_alloc_func(cX509Rev, ossl_x509revoked_alloc); + rb_define_method(cX509Rev, "initialize", ossl_x509revoked_initialize, -1); + + rb_define_method(cX509Rev, "serial", ossl_x509revoked_get_serial, 0); + rb_define_method(cX509Rev, "serial=", ossl_x509revoked_set_serial, 1); + rb_define_method(cX509Rev, "time", ossl_x509revoked_get_time, 0); + rb_define_method(cX509Rev, "time=", ossl_x509revoked_set_time, 1); + rb_define_method(cX509Rev, "extensions", ossl_x509revoked_get_extensions, 0); + rb_define_method(cX509Rev, "extensions=", ossl_x509revoked_set_extensions, 1); + rb_define_method(cX509Rev, "add_extension", ossl_x509revoked_add_extension, 1); +} + diff --git a/ext/openssl/ossl_x509store.c b/ext/openssl/ossl_x509store.c new file mode 100644 index 0000000000..d001a02787 --- /dev/null +++ b/ext/openssl/ossl_x509store.c @@ -0,0 +1,561 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2002 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" +#include + +#define WrapX509Store(klass, obj, st) do { \ + if (!st) { \ + ossl_raise(rb_eRuntimeError, "STORE wasn't initialized!"); \ + } \ + obj = Data_Wrap_Struct(klass, 0, X509_STORE_free, st); \ +} while (0) +#define GetX509Store(obj, st) do { \ + Data_Get_Struct(obj, X509_STORE, st); \ + if (!st) { \ + ossl_raise(rb_eRuntimeError, "STORE wasn't initialized!"); \ + } \ +} while (0) +#define SafeGetX509Store(obj, st) do { \ + OSSL_Check_Kind(obj, cX509Store); \ + GetX509Store(obj, st); \ +} while (0) + +#define WrapX509StCtx(klass, obj, ctx) do { \ + if (!ctx) { \ + ossl_raise(rb_eRuntimeError, "STORE_CTX wasn't initialized!"); \ + } \ + obj = Data_Wrap_Struct(klass, 0, ossl_x509stctx_free, ctx); \ +} while (0) +#define GetX509StCtx(obj, ctx) do { \ + Data_Get_Struct(obj, X509_STORE_CTX, ctx); \ + if (!ctx) { \ + ossl_raise(rb_eRuntimeError, "STORE_CTX is out of scope!"); \ + } \ +} while (0) +#define SafeGetX509StCtx(obj, storep) do { \ + OSSL_Check_Kind(obj, cX509StoreContext); \ + GetX509Store(obj, ctx); \ +} while (0) + +/* + * Classes + */ +VALUE cX509Store; +VALUE cX509StoreContext; +VALUE eX509StoreError; + +/* + * Public functions + */ +VALUE +ossl_x509store_new(X509_STORE *store) +{ + VALUE obj; + + WrapX509Store(cX509Store, obj, store); + + return obj; +} + +X509_STORE * +GetX509StorePtr(VALUE obj) +{ + X509_STORE *store; + + SafeGetX509Store(obj, store); + + return store; +} + +X509_STORE * +DupX509StorePtr(VALUE obj) +{ + X509_STORE *store; + + SafeGetX509Store(obj, store); + CRYPTO_add(&store->references, 1, CRYPTO_LOCK_X509_STORE); + + return store; +} + +/* + * Private functions + */ +static VALUE +ossl_x509store_alloc(VALUE klass) +{ + X509_STORE *store; + VALUE obj; + + if((store = X509_STORE_new()) == NULL){ + ossl_raise(eX509StoreError, NULL); + } + WrapX509Store(klass, obj, store); + + return obj; +} +DEFINE_ALLOC_WRAPPER(ossl_x509store_alloc) + +/* + * General callback for OpenSSL verify + */ +static VALUE +ossl_x509store_set_vfy_cb(VALUE self, VALUE cb) +{ + X509_STORE *store; + + GetX509Store(self, store); + X509_STORE_set_ex_data(store, ossl_verify_cb_idx, (void*)cb); + rb_iv_set(self, "@verify_callback", cb); + + return cb; +} + +static VALUE +ossl_x509store_initialize(int argc, VALUE *argv, VALUE self) +{ + X509_STORE *store; + + GetX509Store(self, store); + X509_STORE_set_verify_cb_func(store, ossl_verify_cb); + ossl_x509store_set_vfy_cb(self, Qnil); + +#if (OPENSSL_VERSION_NUMBER < 0x00907000L) + rb_iv_set(self, "@flags", INT2NUM(0)); + rb_iv_set(self, "@purpose", INT2NUM(0)); + rb_iv_set(self, "@trust", INT2NUM(0)); +#endif + + /* last verification status */ + rb_iv_set(self, "@error", Qnil); + rb_iv_set(self, "@error_string", Qnil); + rb_iv_set(self, "@chain", Qnil); + + return self; +} + +static VALUE +ossl_x509store_set_flags(VALUE self, VALUE flags) +{ +#if (OPENSSL_VERSION_NUMBER >= 0x00907000L) + X509_STORE *store; + + GetX509Store(self, store); + X509_STORE_set_flags(store, NUM2LONG(flags)); +#else + rb_iv_set(self, "@flags", flags); +#endif + + return flags; +} + +static VALUE +ossl_x509store_set_purpose(VALUE self, VALUE purpose) +{ +#if (OPENSSL_VERSION_NUMBER >= 0x00907000L) + X509_STORE *store; + + GetX509Store(self, store); + X509_STORE_set_purpose(store, NUM2LONG(purpose)); +#else + rb_iv_set(self, "@purpose", purpose); +#endif + + return purpose; +} + +static VALUE +ossl_x509store_set_trust(VALUE self, VALUE trust) +{ +#if (OPENSSL_VERSION_NUMBER >= 0x00907000L) + X509_STORE *store; + + GetX509Store(self, store); + X509_STORE_set_trust(store, NUM2LONG(trust)); +#else + rb_iv_set(self, "@trust", trust); +#endif + + return trust; +} + +static VALUE +ossl_x509store_add_file(VALUE self, VALUE file) +{ + X509_STORE *store; + X509_LOOKUP *lookup; + char *path = NULL; + + if(file != Qnil){ + Check_SafeStr(file); + path = RSTRING(file)->ptr; + } + GetX509Store(self, store); + lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); + if(lookup == NULL) ossl_raise(eX509StoreError, NULL); + if(X509_LOOKUP_load_file(lookup, path, X509_FILETYPE_PEM) != 1){ + ossl_raise(eX509StoreError, NULL); + } + + return self; +} + +static VALUE +ossl_x509store_add_path(VALUE self, VALUE dir) +{ + X509_STORE *store; + X509_LOOKUP *lookup; + char *path = NULL; + + if(dir != Qnil){ + Check_SafeStr(dir); + path = RSTRING(dir)->ptr; + } + GetX509Store(self, store); + lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir()); + if(lookup == NULL) ossl_raise(eX509StoreError, NULL); + if(X509_LOOKUP_add_dir(lookup, path, X509_FILETYPE_PEM) != 1){ + ossl_raise(eX509StoreError, NULL); + } + + return self; +} + +static VALUE +ossl_x509store_add_cert(VALUE self, VALUE arg) +{ + X509_STORE *store; + X509 *cert; + + cert = GetX509CertPtr(arg); /* NO NEED TO DUP */ + GetX509Store(self, store); + X509_STORE_add_cert(store, cert); + + return self; +} + +static VALUE +ossl_x509store_add_crl(VALUE self, VALUE arg) +{ + X509_STORE *store; + X509_CRL *crl; + + crl = GetX509CRLPtr(arg); /* NO NEED TO DUP */ + GetX509Store(self, store); + X509_STORE_add_crl(store, crl); + + return self; +} + +static VALUE ossl_x509stctx_get_err(VALUE); +static VALUE ossl_x509stctx_get_err_string(VALUE); +static VALUE ossl_x509stctx_get_chain(VALUE); + +static VALUE +ossl_x509store_verify(int argc, VALUE *argv, VALUE self) +{ + VALUE cert, chain; + VALUE ctx, proc, result; + + rb_scan_args(argc, argv, "11", &cert, &chain); + ctx = rb_funcall(cX509StoreContext, rb_intern("new"), 3, self, cert, chain); + proc = rb_block_given_p() ? rb_block_proc() : + rb_iv_get(self, "@verify_callback"); + rb_iv_set(ctx, "@verify_callback", proc); + result = rb_funcall(ctx, rb_intern("verify"), 0); + + rb_iv_set(self, "@error", ossl_x509stctx_get_err(ctx)); + rb_iv_set(self, "@error_string", ossl_x509stctx_get_err_string(ctx)); + rb_iv_set(self, "@chain", ossl_x509stctx_get_chain(ctx)); + + return result; +} + +/* + * Public Functions + */ +static void ossl_x509stctx_free(X509_STORE_CTX*); + +VALUE +ossl_x509stctx_new(X509_STORE_CTX *ctx) +{ + VALUE obj; + + WrapX509StCtx(cX509StoreContext, obj, ctx); + + return obj; +} + +VALUE +ossl_x509stctx_clear_ptr(VALUE obj) +{ + OSSL_Check_Kind(obj, cX509StoreContext); + RDATA(obj)->data = NULL; + + return obj; +} + +/* + * Private functions + */ +static void +ossl_x509stctx_free(X509_STORE_CTX *ctx) +{ + if(ctx->untrusted) + sk_X509_pop_free(ctx->untrusted, X509_free); + if(ctx->cert) + X509_free(ctx->cert); + X509_STORE_CTX_free(ctx); +} + +static VALUE +ossl_x509stctx_alloc(VALUE klass) +{ + X509_STORE_CTX *ctx; + VALUE obj; + + if((ctx = X509_STORE_CTX_new()) == NULL){ + ossl_raise(eX509StoreError, NULL); + } + WrapX509StCtx(klass, obj, ctx); + + return obj; +} +DEFINE_ALLOC_WRAPPER(ossl_x509stctx_alloc) + +static VALUE +ossl_x509stctx_initialize(int argc, VALUE *argv, VALUE self) +{ + VALUE store, cert, chain; + X509_STORE_CTX *ctx; + X509_STORE *x509st; + X509 *x509 = NULL; + STACK_OF(X509) *x509s = NULL; + + GetX509StCtx(self, ctx); + rb_scan_args(argc, argv, "12", &store, &cert, &chain); + SafeGetX509Store(store, x509st); + if(!NIL_P(cert)) x509 = DupX509CertPtr(cert); /* NEED TO DUP */ + if(!NIL_P(chain)) x509s = ossl_x509_ary2sk(chain); +#if (OPENSSL_VERSION_NUMBER >= 0x00907000L) + if(X509_STORE_CTX_init(ctx, x509st, x509, x509s) != 1){ + sk_X509_pop_free(x509s, X509_free); + ossl_raise(eX509StoreError, NULL); + } +#else + X509_STORE_CTX_init(ctx, x509st, x509, x509s); + X509_STORE_CTX_set_flags(ctx, NUM2INT(rb_iv_get(store, "@flags"))); + X509_STORE_CTX_set_purpose(ctx, NUM2INT(rb_iv_get(store, "@purpose"))); + X509_STORE_CTX_set_trust(ctx, NUM2INT(rb_iv_get(store, "@trust"))); +#endif + rb_iv_set(self, "@verify_callback", rb_iv_get(store, "@verify_callback")); + rb_iv_set(self, "@cert", cert); + + return self; +} + +static VALUE +ossl_x509stctx_verify(VALUE self) +{ + X509_STORE_CTX *ctx; + int result; + + GetX509StCtx(self, ctx); + X509_STORE_CTX_set_ex_data(ctx, ossl_verify_cb_idx, + (void*)rb_iv_get(self, "@verify_callback")); + result = X509_verify_cert(ctx); + + return result ? Qtrue : Qfalse; +} + +static VALUE +ossl_x509stctx_get_chain(VALUE self) +{ + X509_STORE_CTX *ctx; + STACK_OF(X509) *chain; + X509 *x509; + int i, num; + VALUE ary; + + GetX509StCtx(self, ctx); + if((chain = X509_STORE_CTX_get_chain(ctx)) == NULL){ + return Qnil; + } + if((num = sk_X509_num(chain)) < 0){ + OSSL_Debug("certs in chain < 0???"); + return rb_ary_new(); + } + ary = rb_ary_new2(num); + for(i = 0; i < num; i++) { + x509 = sk_X509_value(chain, i); + rb_ary_push(ary, ossl_x509_new(x509)); + } + + return ary; +} + +static VALUE +ossl_x509stctx_get_err(VALUE self) +{ + X509_STORE_CTX *ctx; + + GetX509StCtx(self, ctx); + + return INT2FIX(X509_STORE_CTX_get_error(ctx)); +} + +static VALUE +ossl_x509stctx_set_error(VALUE self, VALUE err) +{ + X509_STORE_CTX *ctx; + + GetX509StCtx(self, ctx); + X509_STORE_CTX_set_error(ctx, FIX2INT(err)); + + return err; +} + +static VALUE +ossl_x509stctx_get_err_string(VALUE self) +{ + X509_STORE_CTX *ctx; + long err; + + GetX509StCtx(self, ctx); + err = X509_STORE_CTX_get_error(ctx); + + return rb_str_new2(X509_verify_cert_error_string(err)); +} + +static VALUE +ossl_x509stctx_get_err_depth(VALUE self) +{ + X509_STORE_CTX *ctx; + + GetX509StCtx(self, ctx); + + return INT2FIX(X509_STORE_CTX_get_error_depth(ctx)); +} + +static VALUE +ossl_x509stctx_get_curr_cert(VALUE self) +{ + X509_STORE_CTX *ctx; + + GetX509StCtx(self, ctx); + + return ossl_x509_new(X509_STORE_CTX_get_current_cert(ctx)); +} + +static VALUE +ossl_x509stctx_get_curr_crl(VALUE self) +{ +#if (OPENSSL_VERSION_NUMBER >= 0x00907000L) + X509_STORE_CTX *ctx; + + GetX509StCtx(self, ctx); + if(!ctx->current_crl) return Qnil; + + return ossl_x509crl_new(ctx->current_crl); +#else + return Qnil; +#endif +} + +static VALUE +ossl_x509stctx_cleanup(VALUE self) +{ + X509_STORE_CTX *ctx; + + GetX509StCtx(self, ctx); + X509_STORE_CTX_cleanup(ctx); + + return self; +} + +static VALUE +ossl_x509stctx_set_flags(VALUE self, VALUE flags) +{ + X509_STORE_CTX *store; + + GetX509StCtx(self, store); + X509_STORE_CTX_set_flags(store, NUM2LONG(flags)); + + return flags; +} + +static VALUE +ossl_x509stctx_set_purpose(VALUE self, VALUE purpose) +{ + X509_STORE_CTX *store; + + GetX509StCtx(self, store); + X509_STORE_CTX_set_purpose(store, NUM2LONG(purpose)); + + return purpose; +} + +static VALUE +ossl_x509stctx_set_trust(VALUE self, VALUE trust) +{ + X509_STORE_CTX *store; + + GetX509StCtx(self, store); + X509_STORE_CTX_set_trust(store, NUM2LONG(trust)); + + return trust; +} + +/* + * INIT + */ +void +Init_ossl_x509store() +{ + VALUE x509stctx; + + eX509StoreError = rb_define_class_under(mX509, "StoreError", eOSSLError); + + cX509Store = rb_define_class_under(mX509, "Store", rb_cObject); + rb_attr(cX509Store, rb_intern("verify_callback"), 1, 0, Qfalse); + rb_attr(cX509Store, rb_intern("error"), 1, 0, Qfalse); + rb_attr(cX509Store, rb_intern("error_string"), 1, 0, Qfalse); + rb_attr(cX509Store, rb_intern("chain"), 1, 0, Qfalse); + rb_define_alloc_func(cX509Store, ossl_x509store_alloc); + rb_define_method(cX509Store, "initialize", ossl_x509store_initialize, -1); + rb_define_method(cX509Store, "verify_callback=", ossl_x509store_set_vfy_cb, 1); + rb_define_method(cX509Store, "flags=", ossl_x509store_set_flags, 1); + rb_define_method(cX509Store, "purpose=", ossl_x509store_set_purpose, 1); + rb_define_method(cX509Store, "trust=", ossl_x509store_set_trust, 1); + rb_define_method(cX509Store, "add_path", ossl_x509store_add_path, 1); + rb_define_method(cX509Store, "add_file", ossl_x509store_add_file, 1); + rb_define_method(cX509Store, "add_cert", ossl_x509store_add_cert, 1); + rb_define_method(cX509Store, "add_crl", ossl_x509store_add_crl, 1); + rb_define_method(cX509Store, "verify", ossl_x509store_verify, -1); + + cX509StoreContext = rb_define_class_under(mX509,"StoreContext",rb_cObject); + x509stctx = cX509StoreContext; + rb_define_alloc_func(cX509StoreContext, ossl_x509stctx_alloc); + rb_define_method(x509stctx,"initialize", ossl_x509stctx_initialize, -1); + rb_define_method(x509stctx,"verify", ossl_x509stctx_verify, 0); + rb_define_method(x509stctx,"chain", ossl_x509stctx_get_chain,0); + rb_define_method(x509stctx,"error", ossl_x509stctx_get_err, 0); + rb_define_method(x509stctx,"error=", ossl_x509stctx_set_error, 1); + rb_define_method(x509stctx,"error_string",ossl_x509stctx_get_err_string,0); + rb_define_method(x509stctx,"error_depth", ossl_x509stctx_get_err_depth, 0); + rb_define_method(x509stctx,"current_cert",ossl_x509stctx_get_curr_cert, 0); + rb_define_method(x509stctx,"current_crl", ossl_x509stctx_get_curr_crl, 0); + rb_define_method(x509stctx,"cleanup", ossl_x509stctx_cleanup, 0); + rb_define_method(x509stctx,"flags=", ossl_x509stctx_set_flags, 1); + rb_define_method(x509stctx,"purpose=", ossl_x509stctx_set_purpose, 1); + rb_define_method(x509stctx,"trust=", ossl_x509stctx_set_trust, 1); + +} diff --git a/ext/openssl/ruby_missing.h b/ext/openssl/ruby_missing.h new file mode 100644 index 0000000000..bdb152b08e --- /dev/null +++ b/ext/openssl/ruby_missing.h @@ -0,0 +1,70 @@ +/* + * $Id$ + * 'OpenSSL for Ruby' project + * Copyright (C) 2001-2003 Michal Rokos + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#if !defined(_OSSL_RUBY_MISSING_H_) +#define _OSS_RUBY_MISSING_H_ + +#if !defined(StringValue) +# define StringValue(v) \ + if (TYPE(v) != T_STRING) v = rb_str_to_str(v) +#endif + +#if !defined(StringValuePtr) +# define StringValuePtr(v) \ + RSTRING((TYPE(v) == T_STRING) ? (v) : rb_str_to_str(v))->ptr +#endif + +#if !defined(SafeStringValue) +# define SafeStringValue(v) do {\ + StringValue(v);\ + rb_check_safe_str(v);\ +} while (0) +#endif + +#if RUBY_VERSION_CODE < 180 +# define rb_cstr_to_inum(a,b,c) \ + rb_cstr2inum(a,b) +# define rb_check_frozen(obj) \ + if (OBJ_FROZEN(obj)) rb_error_frozen(rb_obj_classname(obj)) +# define rb_obj_classname(obj) \ + rb_class2name(CLASS_OF(obj)) +#endif + +#if HAVE_RB_DEFINE_ALLOC_FUNC +# define DEFINE_ALLOC_WRAPPER(func) +#else +# define DEFINE_ALLOC_WRAPPER(func) \ + static VALUE \ + func##_wrapper(int argc, VALUE *argv, VALUE klass) \ + { \ + VALUE obj; \ + \ + obj = func(klass); \ + \ + rb_obj_call_init(obj, argc, argv); \ + \ + return obj; \ + } +# define rb_define_alloc_func(klass, func) \ + rb_define_singleton_method(klass, "new", func##_wrapper, -1) +#endif + +#if RUBY_VERSION_CODE >= 180 +# if !defined(HAVE_RB_OBJ_INIT_COPY) +# define rb_define_copy_func(klass, func) \ + rb_define_method(klass, "copy_object", func, 1) +# else +# define rb_define_copy_func(klass, func) \ + rb_define_method(klass, "initialize_copy", func, 1) +# endif +#endif + +#endif /* _OSS_RUBY_MISSING_H_ */ + diff --git a/ext/openssl/sample/c_rehash.rb b/ext/openssl/sample/c_rehash.rb new file mode 100644 index 0000000000..386eef5f24 --- /dev/null +++ b/ext/openssl/sample/c_rehash.rb @@ -0,0 +1,174 @@ +#!/usr/bin/env ruby + +require 'openssl' +require 'md5' + +class CHashDir + include Enumerable + + def initialize(dirpath) + @dirpath = dirpath + @fingerprint_cache = @cert_cache = @crl_cache = nil + end + + def hash_dir(silent = false) + # ToDo: Should lock the directory... + @silent = silent + @fingerprint_cache = Hash.new + @cert_cache = Hash.new + @crl_cache = Hash.new + do_hash_dir + end + + def get_certs(name = nil) + if name + @cert_cache[hash_name(name)] + else + @cert_cache.values.flatten + end + end + + def get_crls(name = nil) + if name + @crl_cache[hash_name(name)] + else + @crl_cache.values.flatten + end + end + + def delete_crl(crl) + File.unlink(crl_filename(crl)) + hash_dir(true) + end + + def add_crl(crl) + File.open(crl_filename(crl), "w") do |f| + f << crl.to_pem + end + hash_dir(true) + end + + def load_pem_file(filepath) + str = File.read(filepath) + begin + OpenSSL::X509::Certificate.new(str) + rescue + begin + OpenSSL::X509::CRL.new(str) + rescue + begin + OpenSSL::X509::Request.new(str) + rescue + nil + end + end + end + end + +private + + def crl_filename(crl) + path(hash_name(crl.issuer)) + '.pem' + end + + def do_hash_dir + Dir.chdir(@dirpath) do + delete_symlink + Dir.glob('*.pem') do |pemfile| + cert = load_pem_file(pemfile) + case cert + when OpenSSL::X509::Certificate + link_hash_cert(pemfile, cert) + when OpenSSL::X509::CRL + link_hash_crl(pemfile, cert) + else + STDERR.puts("WARNING: #{pemfile} does not contain a certificate or CRL: skipping") unless @silent + end + end + end + end + + def delete_symlink + Dir.entries(".").each do |entry| + next unless /^[\da-f]+\.r{0,1}\d+$/ =~ entry + File.unlink(entry) if FileTest.symlink?(entry) + end + end + + def link_hash_cert(org_filename, cert) + name_hash = hash_name(cert.subject) + fingerprint = fingerprint(cert.to_der) + filepath = link_hash(org_filename, name_hash, fingerprint) { |idx| + "#{name_hash}.#{idx}" + } + unless filepath + unless @silent + STDERR.puts("WARNING: Skipping duplicate certificate #{org_filename}") + end + else + (@cert_cache[name_hash] ||= []) << path(filepath) + end + end + + def link_hash_crl(org_filename, crl) + name_hash = hash_name(crl.issuer) + fingerprint = fingerprint(crl.to_der) + filepath = link_hash(org_filename, name_hash, fingerprint) { |idx| + "#{name_hash}.r#{idx}" + } + unless filepath + unless @silent + STDERR.puts("WARNING: Skipping duplicate CRL #{org_filename}") + end + else + (@crl_cache[name_hash] ||= []) << path(filepath) + end + end + + def link_hash(org_filename, name, fingerprint) + idx = 0 + filepath = nil + while true + filepath = yield(idx) + break unless FileTest.symlink?(filepath) or FileTest.exist?(filepath) + if @fingerprint_cache[filepath] == fingerprint + return false + end + idx += 1 + end + STDOUT.puts("#{org_filename} => #{filepath}") unless @silent + symlink(org_filename, filepath) + @fingerprint_cache[filepath] = fingerprint + filepath + end + + def symlink(from, to) + begin + File.symlink(from, to) + rescue + File.open(to, "w") do |f| + f << File.read(from) + end + end + end + + def path(filename) + File.join(@dirpath, filename) + end + + def hash_name(name) + sprintf("%x", name.hash) + end + + def fingerprint(der) + MD5.hexdigest(der).upcase + end +end + +if $0 == __FILE__ + dirlist = ARGV + dirlist << '/usr/ssl/certs' if dirlist.empty? + dirlist.each do |dir| + CHashDir.new(dir).hash_dir + end +end diff --git a/ext/openssl/sample/cipher.rb b/ext/openssl/sample/cipher.rb new file mode 100644 index 0000000000..844b6eea4e --- /dev/null +++ b/ext/openssl/sample/cipher.rb @@ -0,0 +1,29 @@ +#!/usr/bin/env ruby +require 'openssl' + +text = "abcdefghijklmnopqrstuvwxyz" +key = "key" +alg = "DES-EDE3-CBC" +#alg = "AES-128-CBC" + +puts "--Setup--" +puts %(clear text: "#{text}") +puts %(symmetric key: "#{key}") +puts %(cipher alg: "#{alg}") +puts + +puts "--Encrypting--" +des = OpenSSL::Cipher::Cipher.new(alg) +des.encrypt(key) #, "iv12345678") +cipher = des.update(text) +cipher << des.final +puts %(encrypted text: #{cipher.inspect}) +puts + +puts "--Decrypting--" +des = OpenSSL::Cipher::Cipher.new(alg) +des.decrypt(key) #, "iv12345678") +out = des.update(cipher) +out << des.final +puts %(decrypted text: "#{out}") +puts diff --git a/ext/openssl/sample/echo_cli.rb b/ext/openssl/sample/echo_cli.rb new file mode 100644 index 0000000000..87dacaf545 --- /dev/null +++ b/ext/openssl/sample/echo_cli.rb @@ -0,0 +1,36 @@ +#!/usr/bin/env ruby + +require 'socket' +require 'openssl' +require 'getopts' + +getopts nil, "p:2000", "c:", "k:", "C:" + +host = ARGV[0] || "localhost" +port = $OPT_p +cert_file = $OPT_c +key_file = $OPT_k +ca_path = $OPT_C + +ctx = OpenSSL::SSL::SSLContext.new() +if cert_file && key_file + ctx.cert = OpenSSL::X509::Certificate.new(File::read(cert_file)) + ctx.key = OpenSSL::PKey::RSA.new(File::read(key_file)) +end +if ca_path + ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER + ctx.ca_path = ca_path +else + $stderr.puts "!!! WARNING: PEER CERTIFICATE WON'T BE VERIFIED !!!" +end + +s = TCPSocket.new(host, port) +ssl = OpenSSL::SSL::SSLSocket.new(s, ctx) +ssl.connect +while line = $stdin.gets + ssl.write line + print ssl.gets +end + +ssl.close +s.close diff --git a/ext/openssl/sample/echo_svr.rb b/ext/openssl/sample/echo_svr.rb new file mode 100644 index 0000000000..e35ad12a19 --- /dev/null +++ b/ext/openssl/sample/echo_svr.rb @@ -0,0 +1,64 @@ +#!/usr/bin/env ruby + +require 'socket' +require 'openssl' +require 'getopts' + +getopts nil, "p:2000", "c:", "k:", "C:" + +port = $OPT_p +cert_file = $OPT_c +key_file = $OPT_k +ca_path = $OPT_C + +if cert_file && key_file + cert = OpenSSL::X509::Certificate.new(File::read(cert_file)) + key = OpenSSL::PKey::RSA.new(File::read(key_file)) +else + key = OpenSSL::PKey::RSA.new(512){ print "." } + puts + cert = OpenSSL::X509::Certificate.new + cert.version = 2 + cert.serial = 0 + name = OpenSSL::X509::Name.new([["C","JP"],["O","TEST"],["CN","localhost"]]) + cert.subject = name + cert.issuer = name + cert.not_before = Time.now + cert.not_after = Time.now + 3600 + cert.public_key = key.public_key + ef = OpenSSL::X509::ExtensionFactory.new(nil,cert) + cert.extensions = [ + ef.create_extension("basicConstraints","CA:FALSE"), + ef.create_extension("subjectKeyIdentifier","hash"), + ef.create_extension("extendedKeyUsage","serverAuth"), + ef.create_extension("keyUsage", + "keyEncipherment,dataEncipherment,digitalSignature") + ] + ef.issuer_certificate = cert + cert.add_extension ef.create_extension("authorityKeyIdentifier", + "keyid:always,issuer:always") + cert.sign(key, OpenSSL::Digest::SHA1.new) +end + +ctx = OpenSSL::SSL::SSLContext.new() +ctx.key = key +ctx.cert = cert +if ca_path + ctx.verify_mode = + OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT + ctx.ca_path = ca_path +else + $stderr.puts "!!! WARNING: PEER CERTIFICATE WON'T BE VERIFIED !!!" +end + +svr = TCPServer.new(port) +loop do + ns = svr.accept + ssl = OpenSSL::SSL::SSLSocket.new(ns, ctx) + ssl.accept + while line = ssl.gets + ssl.write line + end + ssl.close + ns.close +end diff --git a/ext/openssl/sample/gen_csr.rb b/ext/openssl/sample/gen_csr.rb new file mode 100644 index 0000000000..c22073b9b9 --- /dev/null +++ b/ext/openssl/sample/gen_csr.rb @@ -0,0 +1,52 @@ +#!/usr/bin/env ruby + +require 'getopts' +require 'openssl' + +include OpenSSL + +def usage + myname = File::basename($0) + $stderr.puts <