summaryrefslogtreecommitdiff
path: root/test/test_securerandom.rb
blob: 765fbc9e0d1b58e764dc23b5cd3ac0ae58454082 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# frozen_string_literal: false
require 'test/unit'
require 'securerandom'

# This testcase does NOT aim to test cryptographically strongness and randomness.
class TestSecureRandom < Test::Unit::TestCase

  def setup
    @it = SecureRandom
  end

  def test_alphanumeric_with_chars
    assert_nothing_raised(ArgumentError) do
      @it.alphanumeric(1, chars: ("0".."9").to_a)
    end
  end

  # This test took 2 minutes on my machine.
  # And 65536 times loop could not be enough for forcing PID recycle.
  # TODO: We should run this test only on GitHub Actions.
  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 if false # ENV["CI"] && RUBY_PLATFORM =~ /darwin/ && `sw_vers -productVersion`.to_i > 13 # for Apple Silicon

  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 * 1.5).to_i.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

  def test_with_openssl
    begin
      require 'openssl'
    rescue LoadError
      return
    end
    assert_equal(Encoding::ASCII_8BIT, @it.send(:gen_random_openssl, 16).encoding)
    65.times do |idx|
      assert_equal(idx, @it.send(:gen_random_openssl, idx).size)
    end
  end

  def test_repeated_gen_random
    assert_nothing_raised NoMethodError, '[ruby-core:92633] [Bug #15847]' do
      @it.gen_random(1)
      @it.gen_random(1)
    end
  end
end