From a15dfa6b4a0c8afc0fd47a5d1b6c7d12191e38fa Mon Sep 17 00:00:00 2001 From: nahi Date: Thu, 16 Jun 2011 10:32:51 +0000 Subject: [PATCH] * test/test_securerandom.rb: Add testcase. This testcase does NOT aim to test cryptographically strongness and randomness. It includes the test for PID recycle issue of OpenSSL described in #4579 but it's disabled by default. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@32122 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 7 ++ test/test_securerandom.rb | 175 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 182 insertions(+) create mode 100644 test/test_securerandom.rb diff --git a/ChangeLog b/ChangeLog index db5b697323..ae16952f39 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +Thu Jun 16 19:27:12 2011 Hiroshi Nakamura + + * test/test_securerandom.rb: Add testcase. This testcase does NOT aim + to test cryptographically strongness and randomness. It includes + the test for PID recycle issue of OpenSSL described in #4579 but + it's disabled by default. + Thu Jun 16 17:55:07 2011 KOSAKI Motohiro * test/ruby/test_io.rb (TestIO#test_copy_stream_socket): fix diff --git a/test/test_securerandom.rb b/test/test_securerandom.rb new file mode 100644 index 0000000000..be951ea722 --- /dev/null +++ b/test/test_securerandom.rb @@ -0,0 +1,175 @@ +require 'test/unit' +require 'securerandom' +require 'tempfile' + +# This testcase does NOT aim to test cryptographically strongness and randomness. +class TestSecureRandom < Test::Unit::TestCase + def setup + @it = SecureRandom + end + + def test_s_random_bytes + assert_equal(16, @it.random_bytes.size) + assert_equal(Encoding::ASCII_8BIT, @it.random_bytes.encoding) + 65.times do |idx| + assert_equal(idx, @it.random_bytes(idx).size) + end + end + +# This test took 2 minutes on my machine. +# And 65536 times loop could not be enough for forcing PID recycle. +if false + def test_s_random_bytes_is_fork_safe + begin + require 'openssl' + rescue LoadError + return + end + SecureRandom.random_bytes(8) + pid, v1 = forking_random_bytes + assert(check_forking_random_bytes(pid, v1), 'Process ID not recycled?') + end + + def forking_random_bytes + r, w = IO.pipe + pid = fork { + r.close + w.write SecureRandom.random_bytes(8) + w.close + } + w.close + v = r.read(8) + r.close + Process.waitpid2(pid) + [pid, v] + end + + def check_forking_random_bytes(target_pid, target) + 65536.times do + pid = fork { + if $$ == target_pid + v2 = SecureRandom.random_bytes(8) + if v2 == target + exit(1) + else + exit(2) + end + end + exit(3) + } + pid, status = Process.waitpid2(pid) + case status.exitstatus + when 1 + raise 'returned same sequence for same PID' + when 2 + return true + end + end + false # not recycled? + end +end + + def test_s_random_bytes_without_openssl + begin + require 'openssl' + rescue LoadError + return + end + begin + load_path = $LOAD_PATH.dup + loaded_features = $LOADED_FEATURES.dup + openssl = Object.instance_eval { remove_const(:OpenSSL) } + + remove_feature('securerandom.rb') + remove_feature('openssl.rb') + Dir.mktmpdir do |dir| + open(File.join(dir, 'openssl.rb'), 'w') { |f| + f << 'raise LoadError' + } + $LOAD_PATH.unshift(dir) + require 'securerandom' + test_s_random_bytes + end + ensure + $LOADED_FEATURES.replace(loaded_features) + $LOAD_PATH.replace(load_path) + Object.const_set(:OpenSSL, openssl) + end + end + + def test_s_hex + assert_equal(16 * 2, @it.hex.size) + 33.times do |idx| + assert_equal(idx * 2, @it.hex(idx).size) + assert_equal(idx, @it.hex(idx).gsub(/(..)/) { [$1].pack('H*') }.size) + end + end + + def test_s_base64 + assert_equal(16, @it.base64.unpack('m*')[0].size) + 17.times do |idx| + assert_equal(idx, @it.base64(idx).unpack('m*')[0].size) + end + end + + def test_s_urlsafe_base64 + safe = /[\n+\/]/ + 65.times do |idx| + assert_not_match(safe, @it.urlsafe_base64(idx)) + end + # base64 can include unsafe byte + 10001.times do |idx| + return if safe =~ @it.base64(idx) + end + flunk + end + + def test_s_random_number_float + 101.times do + v = @it.random_number + assert(0.0 <= v && v < 1.0) + end + end + + def test_s_random_number_float_by_zero + 101.times do + v = @it.random_number(0) + assert(0.0 <= v && v < 1.0) + end + end + + def test_s_random_number_int + 101.times do |idx| + next if idx.zero? + v = @it.random_number(idx) + assert(0 <= v && v < idx) + end + end + + def test_uuid + uuid = @it.uuid + assert_equal(36, uuid.size) + uuid.unpack('a8xa4xa4xa4xa12').each do |e| + assert_match(/^[0-9a-f]+$/, e) + end + end + + def protect + begin + yield + rescue NotImplementedError + # ignore + end + end + + def remove_feature(basename) + $LOADED_FEATURES.delete_if { |path| + if File.basename(path) == basename + $LOAD_PATH.any? { |dir| + File.exists?(File.join(dir, basename)) + } + end + } + end + +end