From 8f7acf49d55b45dbc448f40547d9087762263ddb Mon Sep 17 00:00:00 2001 From: "Hongli Lai (Phusion)" Date: Wed, 5 Aug 2009 05:37:35 +0800 Subject: [PATCH] Finish JRuby support. Signed-off-by: Coda Hale --- Rakefile | 33 +++++++++++++++++++++++++++------ ext/extconf.rb | 23 ++++++++++++++++++----- lib/bcrypt.rb | 44 +++++++++++++++++++++++++++++++++----------- 3 files changed, 78 insertions(+), 22 deletions(-) diff --git a/Rakefile b/Rakefile index 22f3be5..19c7b6c 100644 --- a/Rakefile +++ b/Rakefile @@ -15,7 +15,9 @@ PKG_FILES = FileList[ 'spec/**/*.rb', 'ext/*.c', 'ext/*.h', - 'ext/*.rb' + 'ext/*.rb', + 'ext/jruby/bcrypt_jruby/BCrypt.java', + 'ext/jruby/bcrypt_jruby/BCrypt.class' ] CLEAN.include( "ext/*.o", @@ -81,11 +83,30 @@ Rake::GemPackageTask.new(spec) do |pkg| pkg.need_tar = true end -desc "Clean, then compile the extension." -task :compile => [:clean] do - Dir.chdir('ext') do - ruby "extconf.rb" - sh "make" +desc "Clean, then compile the extension that's native to the current Ruby compiler." +if RUBY_PLATFORM == "java" + task :compile => 'compile:jruby' +else + task :compile => 'compile:mri' +end + +namespace :compile do + desc "CLean, then compile all extensions" + task :all => [:mri, :jruby] + + desc "Clean, then compile the MRI extension" + task :mri => :clean do + Dir.chdir('ext') do + ruby "extconf.rb" + sh "make" + end + end + + desc "Clean, then compile the JRuby extension" + task :jruby => :clean do + Dir.chdir('ext/jruby/bcrypt_jruby') do + sh "javac BCrypt.java" + end end end diff --git a/ext/extconf.rb b/ext/extconf.rb index c8a1e3e..9454581 100644 --- a/ext/extconf.rb +++ b/ext/extconf.rb @@ -1,5 +1,18 @@ -require "mkmf" -dir_config("bcrypt_ext") -# enable this when we're feeling nitpicky -# CONFIG['CC'] << " -Wall " -create_makefile("bcrypt_ext") \ No newline at end of file +if RUBY_PLATFORM == "java" + # Don't do anything when run in JRuby; this allows gem installation to pass. + # We need to write a dummy Makefile so that RubyGems doesn't think compilation + # failed. + File.open('Makefile', 'w') do |f| + f.puts "all:" + f.puts "\t@true" + f.puts "install:" + f.puts "\t@true" + end + exit 0 +else + require "mkmf" + dir_config("bcrypt_ext") + # enable this when we're feeling nitpicky + # CONFIG['CC'] << " -Wall " + create_makefile("bcrypt_ext") +end \ No newline at end of file diff --git a/lib/bcrypt.rb b/lib/bcrypt.rb index cc1a01f..f4d28aa 100644 --- a/lib/bcrypt.rb +++ b/lib/bcrypt.rb @@ -1,8 +1,14 @@ # A wrapper for OpenBSD's bcrypt/crypt_blowfish password-hashing algorithm. -$: << "ext" -require "bcrypt_ext" -require "openssl" +if RUBY_PLATFORM == "java" + require 'java' + $CLASSPATH << File.expand_path(File.join(File.dirname(__FILE__), "..", "ext", "jruby")) +else + $LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "ext"))) + puts $LOAD_PATH + require "bcrypt_ext" + require "openssl" +end # A Ruby library implementing OpenBSD's bcrypt()/crypt_blowfish algorithm for # hashing passwords. @@ -14,24 +20,32 @@ class InvalidCost < StandardError; end # The cost parameter provided to bcryp class InvalidSecret < StandardError; end # The secret parameter provided to bcrypt() is invalid. end - # A Ruby wrapper for the bcrypt() extension calls. + # A Ruby wrapper for the bcrypt() C extension calls and the Java calls. class Engine # The default computational expense parameter. DEFAULT_COST = 10 + # The minimum cost supported by the algorithm. + MIN_COST = 4 # Maximum possible size of bcrypt() salts. MAX_SALT_LENGTH = 16 - # C-level routines which, if they don't get the right input, will crash the - # hell out of the Ruby process. - private_class_method :__bc_salt - private_class_method :__bc_crypt + if RUBY_PLATFORM != "java" + # C-level routines which, if they don't get the right input, will crash the + # hell out of the Ruby process. + private_class_method :__bc_salt + private_class_method :__bc_crypt + end # Given a secret and a valid salt (see BCrypt::Engine.generate_salt) calculates # a bcrypt() password hash. def self.hash_secret(secret, salt) if valid_secret?(secret) if valid_salt?(salt) - __bc_crypt(secret.to_s, salt) + if RUBY_PLATFORM == "java" + Java.bcrypt_jruby.BCrypt.hashpw(secret.to_s, salt.to_s) + else + __bc_crypt(secret.to_s, salt) + end else raise Errors::InvalidSalt.new("invalid salt") end @@ -42,8 +56,16 @@ def self.hash_secret(secret, salt) # Generates a random salt with a given computational cost. def self.generate_salt(cost = DEFAULT_COST) - if cost.to_i > 0 - __bc_salt(cost, OpenSSL::Random.random_bytes(MAX_SALT_LENGTH)) + cost = cost.to_i + if cost > 0 + if cost < MIN_COST + cost = MIN_COST + end + if RUBY_PLATFORM == "java" + Java.bcrypt_jruby.BCrypt.gensalt(cost) + else + __bc_salt(cost, OpenSSL::Random.random_bytes(MAX_SALT_LENGTH)) + end else raise Errors::InvalidCost.new("cost must be numeric and > 0") end