Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: d7d063d66f0f
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 17ff9fee4dd7
Choose a head ref
  • 6 commits
  • 4 files changed
  • 2 contributors

Commits on Jun 6, 2017

  1. Copy the full SHA
    f8b0028 View commit details
  2. Copy the full SHA
    4ed3dd6 View commit details

Commits on Jun 8, 2017

  1. Copy the full SHA
    3d51f62 View commit details
  2. Copy the full SHA
    6039408 View commit details
  3. Copy the full SHA
    112b436 View commit details

Commits on Jun 12, 2017

  1. Merge pull request #4656 from kares/test-securerandom

    SecureRandom update to match Ruby 2.3
    kares authored Jun 12, 2017
    Copy the full SHA
    17ff9fe View commit details
181 changes: 181 additions & 0 deletions core/src/main/java/org/jruby/ext/securerandom/RubySecureRandom.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/*
***** BEGIN LICENSE BLOCK *****
* Version: EPL 1.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Eclipse Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.eclipse.org/legal/epl-v10.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* Copyright (C) 2017 JRuby Community
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the EPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the EPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/
package org.jruby.ext.securerandom;

import org.jruby.RubyBignum;
import org.jruby.RubyFixnum;
import org.jruby.RubyFloat;
import org.jruby.RubyInteger;
import org.jruby.RubyRange;
import org.jruby.RubyString;
import org.jruby.anno.JRubyMethod;
import org.jruby.anno.JRubyModule;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.ConvertBytes;

import java.math.BigInteger;
import java.security.SecureRandom;

/**
* securerandom.rb native parts.
*/
@JRubyModule(name = "SecureRandom")
public class RubySecureRandom {

@JRubyMethod(meta = true, name = "random_bytes")
public static IRubyObject random_bytes(ThreadContext context, IRubyObject self) {
return RubyString.newStringNoCopy(context.runtime, nextBytes(context, 16));
}

@JRubyMethod(meta = true, name = "random_bytes", alias = "gen_random") // gen_random for 'better' compat (not-used)
public static IRubyObject random_bytes(ThreadContext context, IRubyObject self, IRubyObject n) {
return RubyString.newStringNoCopy(context.runtime, nextBytes(context, n));
}

@JRubyMethod(meta = true)
public static IRubyObject hex(ThreadContext context, IRubyObject self) {
return RubyString.newStringNoCopy(context.runtime, ConvertBytes.twosComplementToHexBytes(nextBytes(context, 16), false));
}

@JRubyMethod(meta = true)
public static IRubyObject hex(ThreadContext context, IRubyObject self, IRubyObject n) {
return RubyString.newStringNoCopy(context.runtime, ConvertBytes.twosComplementToHexBytes(nextBytes(context, n), false));
}

@JRubyMethod(meta = true)
public static IRubyObject uuid(ThreadContext context, IRubyObject self) {
return RubyString.newStringNoCopy(context.runtime, ConvertBytes.bytesToUUIDBytes(nextBytes(context, 16), false));
}

private static byte[] nextBytes(ThreadContext context, IRubyObject n) {
return nextBytes(context, n.isNil() ? 16 : n.convertToInteger().getIntValue());
}

private static byte[] nextBytes(ThreadContext context, int size) {
if (size < 0) throw context.runtime.newArgumentError("negative argument: " + size);

byte[] bytes = new byte[size];
getSecureRandom(context).nextBytes(bytes);

return bytes;
}

@JRubyMethod(meta = true)
public static IRubyObject random_number(ThreadContext context, IRubyObject self) {
return randomDouble(context);
}

@JRubyMethod(meta = true)
public static IRubyObject random_number(ThreadContext context, IRubyObject self, IRubyObject n) {
if (n instanceof RubyFixnum) {
final long bound = ((RubyFixnum) n).getLongValue();
return ( bound < 0 ) ? randomDouble(context) : randomFixnum(context, 0, bound - 1);
}
if (n instanceof RubyFloat) {
final double bound = ((RubyFloat) n).getDoubleValue();
return ( bound < 0 ) ? randomDouble(context) : randomDouble(context, 0, bound - Double.MIN_VALUE);
}
if (n instanceof RubyBignum) {
final BigInteger bound = ((RubyBignum) n).getBigIntegerValue();
return ( bound.signum() < 0 ) ? randomDouble(context) : randomBignum(context, 0, bound);
}

if (n instanceof RubyRange) {
final IRubyObject beg = ((RubyRange) n).begin(context);
final IRubyObject end = ((RubyRange) n).end(context);
final boolean exclude = ((RubyRange) n).isExcludeEnd();

if (beg instanceof RubyFixnum && end instanceof RubyFixnum) {
long lower = ((RubyFixnum) beg).getLongValue();
long upper = ((RubyFixnum) end).getLongValue();
if ( lower > upper ) return randomDouble(context);
if ( exclude ) upper--; // rand(2) never returns 2 but rand(0..2) does
return randomFixnum(context, lower, upper);
}
if (beg instanceof RubyInteger && end instanceof RubyInteger) {
BigInteger lower = ((RubyInteger) beg).getBigIntegerValue();
BigInteger upper = ((RubyInteger) end).getBigIntegerValue();
if ( lower.compareTo(upper) > 0 ) return randomDouble(context);
if ( ! exclude ) upper = upper.add(BigInteger.ONE);
return randomBignum(context, lower, upper);
}
if (beg instanceof RubyFloat && end instanceof RubyFloat) {
double lower = ((RubyFloat) beg).getDoubleValue();
double upper = ((RubyFloat) end).getDoubleValue();
if ( lower > upper ) return randomDouble(context);
if ( exclude ) upper = upper - Double.MIN_VALUE;
return randomDouble(context, lower, upper);
}
}

throw context.runtime.newArgumentError("invalid argument - " + n.anyToString());
}

private static RubyFixnum randomFixnum(final ThreadContext context, final long lower, final long upper) {
double rnd = getSecureRandom(context).nextDouble();
rnd = rnd * upper + (1.0 - rnd) * lower + rnd;
return context.runtime.newFixnum((long) Math.floor(rnd));
}

// NOTE: upper is exclusive here compared to others
private static RubyBignum randomBignum(final ThreadContext context, final Number lower, final BigInteger upperExc) {
BigInteger lowerBig = lower instanceof BigInteger ? (BigInteger) lower : BigInteger.valueOf(lower.longValue());
BigInteger bound = upperExc.subtract(lowerBig);
BigInteger rnd = nextBigInteger(getSecureRandom(context), bound, bound.bitLength());
return RubyBignum.newBignum(context.runtime, rnd.add(lowerBig));
}

private static final int BI_ADD_BITS = 96; // 8+4 random bytes 'wasted'

// <0, bound)
private static BigInteger nextBigInteger(final SecureRandom random, final BigInteger bound, final int bits) {
BigInteger val = new BigInteger(bits + BI_ADD_BITS, random);
BigInteger rnd = val.mod(bound);

if (val.add(bound).subtract(rnd).subtract(BigInteger.ONE).bitLength() >= bits + BI_ADD_BITS) {
return nextBigInteger(random, bound, bits); // highly unlikely to recurse at all
}
return rnd;
}

private static RubyFloat randomDouble(final ThreadContext context, final double lower, final double upper) {
double rnd = getSecureRandom(context).nextDouble();
return context.runtime.newFloat( rnd * upper + (1.0 - rnd) * lower );
}

private static RubyFloat randomDouble(final ThreadContext context) {
return context.runtime.newFloat( getSecureRandom(context).nextDouble() );
}

private static SecureRandom getSecureRandom(ThreadContext context) {
return context.getSecureRandom();
}

}
Original file line number Diff line number Diff line change
@@ -1,52 +1,48 @@
package org.jruby.ext.securerandom;

import org.jruby.RubyString;
import org.jruby.anno.JRubyMethod;
import org.jruby.Ruby;
import org.jruby.RubyModule;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.ConvertBytes;
import org.jruby.runtime.load.Library;

/**
* Created by headius on 1/14/14.
*/
public class SecureRandomLibrary {
@JRubyMethod(meta = true)
public class SecureRandomLibrary implements Library {

public static void load(Ruby runtime) {
RubyModule SecureRandom = runtime.defineModule("SecureRandom");
SecureRandom.defineAnnotatedMethods(RubySecureRandom.class);
}

public void load(Ruby runtime, boolean wrap) {
SecureRandomLibrary.load(runtime);
}

@Deprecated
public static IRubyObject random_bytes(ThreadContext context, IRubyObject self) {
return RubyString.newStringNoCopy(context.runtime, nextBytes(context, 16));
return RubySecureRandom.random_bytes(context, self);
}

@JRubyMethod(meta = true)
@Deprecated
public static IRubyObject random_bytes(ThreadContext context, IRubyObject self, IRubyObject n) {
return RubyString.newStringNoCopy(context.runtime, nextBytes(context, n));
return RubySecureRandom.random_bytes(context, self, n);
}

@JRubyMethod(meta = true)
@Deprecated
public static IRubyObject hex(ThreadContext context, IRubyObject self) {
return RubyString.newStringNoCopy(context.runtime, ConvertBytes.twosComplementToHexBytes(nextBytes(context, 16), false));
return RubySecureRandom.hex(context, self);
}

@JRubyMethod(meta = true)
@Deprecated
public static IRubyObject hex(ThreadContext context, IRubyObject self, IRubyObject n) {
return RubyString.newStringNoCopy(context.runtime, ConvertBytes.twosComplementToHexBytes(nextBytes(context, n), false));
return RubySecureRandom.hex(context, self, n);
}

@JRubyMethod(meta = true)
@Deprecated
public static IRubyObject uuid(ThreadContext context, IRubyObject self) {
return RubyString.newStringNoCopy(context.runtime, ConvertBytes.bytesToUUIDBytes(nextBytes(context, 16), false));
return RubySecureRandom.hex(context, self);
}

private static byte[] nextBytes(ThreadContext context, IRubyObject n) {
int size = n.isNil() ? 16 : (int) n.convertToInteger().getLongValue();

return nextBytes(context, size);
}

private static byte[] nextBytes(ThreadContext context, int size) {
if (size < 0) throw context.runtime.newArgumentError("negative argument: " + size);

byte[] bytes = new byte[size];
context.getSecureRandom().nextBytes(bytes);

return bytes;
}
}
229 changes: 108 additions & 121 deletions lib/ruby/stdlib/securerandom.rb
Original file line number Diff line number Diff line change
@@ -1,105 +1,119 @@
# = Secure random number generator interface.
#
# This library is an interface for secure random number generator which is
# suitable for generating session key in HTTP cookies, etc.
#
# It supports following secure random number generators.
#
# * Java's java.security.SecureRandom.
#
# == Example
#
# # random hexadecimal string.
# p SecureRandom.hex(10) #=> "52750b30ffbc7de3b362"
# p SecureRandom.hex(10) #=> "92b15d6c8dc4beb5f559"
# p SecureRandom.hex(11) #=> "6aca1b5c58e4863e6b81b8"
# p SecureRandom.hex(12) #=> "94b2fff3e7fd9b9c391a2306"
# p SecureRandom.hex(13) #=> "39b290146bea6ce975c37cfc23"
# ...
#
# # random base64 string.
# p SecureRandom.base64(10) #=> "EcmTPZwWRAozdA=="
# p SecureRandom.base64(10) #=> "9b0nsevdwNuM/w=="
# p SecureRandom.base64(10) #=> "KO1nIU+p9DKxGg=="
# p SecureRandom.base64(11) #=> "l7XEiFja+8EKEtY="
# p SecureRandom.base64(12) #=> "7kJSM/MzBJI+75j8"
# p SecureRandom.base64(13) #=> "vKLJ0tXBHqQOuIcSIg=="
# ...
#
# # random binary string.
# p SecureRandom.random_bytes(10) #=> "\016\t{\370g\310pbr\301"
# p SecureRandom.random_bytes(10) #=> "\323U\030TO\234\357\020\a\337"
# ...
# -*- coding: us-ascii -*-
# frozen_string_literal: true

require 'jruby'
org.jruby.ext.securerandom.SecureRandomLibrary.load JRuby.runtime

# == Secure random number generator interface.
#
# This library is an interface to secure random number generators which are
# suitable for generating session keys in HTTP cookies, etc.
#
# You can use this library in your application by requiring it:
#
# require 'securerandom'
#
# It supports the following secure random number generators:
#
# * openssl
# * /dev/urandom
# * Win32
#
# === Examples
#
# Generate random hexadecimal strings:
#
# require 'securerandom'
#
# p SecureRandom.hex(10) #=> "52750b30ffbc7de3b362"
# p SecureRandom.hex(10) #=> "92b15d6c8dc4beb5f559"
# p SecureRandom.hex(13) #=> "39b290146bea6ce975c37cfc23"
#
# Generate random base64 strings:
#
# p SecureRandom.base64(10) #=> "EcmTPZwWRAozdA=="
# p SecureRandom.base64(10) #=> "KO1nIU+p9DKxGg=="
# p SecureRandom.base64(12) #=> "7kJSM/MzBJI+75j8"
#
# Generate random binary strings:
#
# p SecureRandom.random_bytes(10) #=> "\016\t{\370g\310pbr\301"
# p SecureRandom.random_bytes(10) #=> "\323U\030TO\234\357\020\a\337"
#
# Generate UUIDs:
#
# p SecureRandom.uuid #=> "2d931510-d99f-494a-8c67-87feb05e1594"
# p SecureRandom.uuid #=> "bad85eb9-0713-4da7-8d36-07a8e4b00eab"
#

module Random::Formatter

module SecureRandom
# SecureRandom.random_bytes generates a random binary string.
#
# The argument n specifies the length of the result string.
# The argument _n_ specifies the length of the result string.
#
# If n is not specified, 16 is assumed.
# If _n_ is not specified or is nil, 16 is assumed.
# It may be larger in future.
#
# The result may contain any byte: "\x00" - "\xff".
#
# p SecureRandom.random_bytes #=> "\xD8\\\xE0\xF4\r\xB2\xFC*WM\xFF\x83\x18\xF45\xB6"
# p SecureRandom.random_bytes #=> "m\xDC\xFC/\a\x00Uf\xB2\xB2P\xBD\xFF6S\x97"
#
# If secure random number generator is not available,
# NotImplementedError is raised.
def self.random_bytes(n=nil)
# replaced below by native version
raise
# If a secure random number generator is not available,
# +NotImplementedError+ is raised.
def random_bytes(n=nil)
n = n ? n.to_int : 16
gen_random(n)
end

# SecureRandom.hex generates a random hex string.
# SecureRandom.hex generates a random hexadecimal string.
#
# The argument n specifies the length of the random length.
# The length of the result string is twice of n.
# The argument _n_ specifies the length, in bytes, of the random number to be generated.
# The length of the resulting hexadecimal string is twice of _n_.
#
# If n is not specified, 16 is assumed.
# It may be larger in future.
# If _n_ is not specified or is nil, 16 is assumed.
# It may be larger in the future.
#
# The result may contain 0-9 and a-f.
#
# p SecureRandom.hex #=> "eb693ec8252cd630102fd0d0fb7c3485"
# p SecureRandom.hex #=> "91dc3bfb4de5b11d029d376634589b61"
#
# If secure random number generator is not available,
# NotImplementedError is raised.
def self.hex(n=nil)
# replaced below by native version
raise
# If a secure random number generator is not available,
# +NotImplementedError+ is raised.
def hex(n=nil)
random_bytes(n).unpack("H*")[0]
end

# SecureRandom.base64 generates a random base64 string.
#
# The argument n specifies the length of the random length.
# The length of the result string is about 4/3 of n.
# The argument _n_ specifies the length, in bytes, of the random number
# to be generated. The length of the result string is about 4/3 of _n_.
#
# If n is not specified, 16 is assumed.
# It may be larger in future.
# If _n_ is not specified or is nil, 16 is assumed.
# It may be larger in the future.
#
# The result may contain A-Z, a-z, 0-9, "+", "/" and "=".
#
# p SecureRandom.base64 #=> "/2BuBuLf3+WfSKyQbRcc/A=="
# p SecureRandom.base64 #=> "6BbW0pxO0YENxn38HMUbcQ=="
#
# If secure random number generator is not available,
# NotImplementedError is raised.
# If a secure random number generator is not available,
# +NotImplementedError+ is raised.
#
# See RFC 3548 for base64.
def self.base64(n=nil)
# See RFC 3548 for the definition of base64.
def base64(n=nil)
[random_bytes(n)].pack("m*").delete("\n")
end

if RUBY_VERSION >= "1.9.0"
# SecureRandom.urlsafe_base64 generates a random URL-safe base64 string.
#
# The argument _n_ specifies the length of the random length.
# The length of the result string is about 4/3 of _n_.
# The argument _n_ specifies the length, in bytes, of the random number
# to be generated. The length of the result string is about 4/3 of _n_.
#
# If _n_ is not specified, 16 is assumed.
# It may be larger in future.
# If _n_ is not specified or is nil, 16 is assumed.
# It may be larger in the future.
#
# The boolean argument _padding_ specifies the padding.
# If it is false or nil, padding is not generated.
@@ -115,96 +129,69 @@ def self.base64(n=nil)
# p SecureRandom.urlsafe_base64(nil, true) #=> "i0XQ-7gglIsHGV2_BNPrdQ=="
# p SecureRandom.urlsafe_base64(nil, true) #=> "-M8rLhr7JEpJlqFGUMmOxg=="
#
# If secure random number generator is not available,
# NotImplementedError is raised.
# If a secure random number generator is not available,
# +NotImplementedError+ is raised.
#
# See RFC 3548 for URL-safe base64.
def self.urlsafe_base64(n=nil, padding=false)
# See RFC 3548 for the definition of URL-safe base64.
def urlsafe_base64(n=nil, padding=false)
s = [random_bytes(n)].pack("m*")
s.delete!("\n")
s.tr!("+/", "-_")
s.delete!("=") if !padding
s.delete!("=") unless padding
s
end
end

# SecureRandom.random_number generates a random number.
#
# If an positive integer is given as n,
# SecureRandom.random_number returns an integer:
# 0 <= SecureRandom.random_number(n) < n.
# If a positive integer is given as _n_,
# +SecureRandom.random_number+ returns an integer, such that:
# +0 <= SecureRandom.random_number(n) < n+.
#
# p SecureRandom.random_number(100) #=> 15
# p SecureRandom.random_number(100) #=> 88
#
# If 0 is given or an argument is not given,
# SecureRandom.random_number returns an float:
# 0.0 <= SecureRandom.random_number() < 1.0.
# +SecureRandom.random_number+ returns a float, such that:
# +0.0 <= SecureRandom.random_number() < 1.0+.
#
# p SecureRandom.random_number #=> 0.596506046187744
# p SecureRandom.random_number #=> 0.350621695741409
#
def self.random_number(n=0)
if 0 < n
hex = n.to_s(16)
hex = '0' + hex if (hex.length & 1) == 1
bin = [hex].pack("H*")
mask = string_ord_of_initial_byte(bin)
mask |= mask >> 1
mask |= mask >> 2
mask |= mask >> 4
begin
rnd = SecureRandom.random_bytes(bin.length)
rnd[0] = (string_ord_of_initial_byte(rnd) & mask).chr
end until rnd < bin
rnd.unpack("H*")[0].hex
else
# assumption: Float::MANT_DIG <= 64
# i64 = SecureRandom.random_bytes(8).unpack("Q")[0]
# Math.ldexp(i64 >> (64-Float::MANT_DIG), -Float::MANT_DIG)
java.security.SecureRandom.new.nextDouble
end
def random_number(n=0)
# implemented natively in JRuby (just like in MRI)
raise NotImplementedError("#{__method__}(#{n})")
end

if RUBY_VERSION >= "1.9.0"
# SecureRandom.uuid generates a v4 random UUID (Universally Unique IDentifier).
# SecureRandom.uuid generates a random v4 UUID (Universally Unique IDentifier).
#
# p SecureRandom.uuid #=> "2d931510-d99f-494a-8c67-87feb05e1594"
# p SecureRandom.uuid #=> "bad85eb9-0713-4da7-8d36-07a8e4b00eab"
# p SecureRandom.uuid #=> "62936e70-1815-439b-bf89-8492855a7e6b"
#
# The version 4 UUID is purely random (except the version).
# It doesn't contain meaningful information such as MAC address, time, etc.
# It doesn't contain meaningful information such as MAC addresses, timestamps, etc.
#
# The result contains 122 random bits (15.25 random bytes).
#
# See RFC 4122 for details of UUID.
#
def self.uuid
# replaced below by native version
raise
def uuid
ary = random_bytes(16).unpack("NnnnnN")
ary[2] = (ary[2] & 0x0fff) | 0x4000
ary[3] = (ary[3] & 0x3fff) | 0x8000
"%08x-%04x-%04x-%04x-%04x%08x" % ary
end

# Following code is based on David Garamond's GUID library for Ruby.
def self.lastWin32ErrorMessage # :nodoc:
get_last_error = Win32API.new("kernel32", "GetLastError", '', 'L')
format_message = Win32API.new("kernel32", "FormatMessageA", 'LPLLPLPPPPPPPP', 'L')
format_message_ignore_inserts = 0x00000200
format_message_from_system = 0x00001000

code = get_last_error.call
msg = "\0" * 1024
len = format_message.call(format_message_ignore_inserts + format_message_from_system, 0, code, 0, msg, 1024, nil, nil, nil, nil, nil, nil, nil, nil)
msg[0, len].tr("\r", '').chomp
private
def gen_random(n)
self.bytes(n)
end
end

# Load the JRuby native ext
JRuby.reference(self).define_annotated_methods(org.jruby.ext.securerandom.SecureRandomLibrary)
SecureRandom.extend(Random::Formatter)

class << self
private
# String[0].ord for both 1.8.7 & 1.9.2 compatible.
def string_ord_of_initial_byte(bin)
bin.bytes.first
end
end
end
# NOTE: JRuby's SecureRandom native part implement some of the methods from Random::Formatter
# - SecureRandom.random_bytes(n=nil)
# - SecureRandom.hex(n=nil)
# - SecureRandom.uuid
# - SecureRandom.random_number(n=0)
46 changes: 46 additions & 0 deletions spec/ruby/library/securerandom/random_number_spec.rb
Original file line number Diff line number Diff line change
@@ -12,6 +12,16 @@
end
end

it "generates a random (potentially bignum) integer value for bignum argument" do
max = 12345678901234567890
11.times do
num = SecureRandom.random_number max
num.should be_kind_of(Integer)
(0 <= num).should == true
(num < max).should == true
end
end

it "generates a random float number between 0.0 and 1.0 if no argument provided" do
64.times do
num = SecureRandom.random_number
@@ -21,13 +31,49 @@
end
end

it "generates a random value in given (integer) range limits" do
64.times do
num = SecureRandom.random_number 11...13
num.should be_kind_of(Integer)
(11 <= num).should == true
(num < 13).should == true
end
end

it "generates a random value in given big (integer) range limits" do
lower = 12345678901234567890
upper = 12345678901234567890 + 5
32.times do
num = SecureRandom.random_number lower..upper
num.should be_kind_of(Integer)
(lower <= num).should == true
(num <= upper).should == true
end
end

it "generates a random value in given (float) range limits" do
64.times do
num = SecureRandom.random_number 0.6..0.9
num.should be_kind_of(Float)
(0.6 <= num).should == true
(num <= 0.9).should == true
end
end

it "generates a random float number between 0.0 and 1.0 if argument is negative" do
num = SecureRandom.random_number(-10)
num.should be_kind_of(Float)
(0.0 <= num).should == true
(num < 1.0).should == true
end

it "generates a random float number between 0.0 and 1.0 if argument is negative float" do
num = SecureRandom.random_number(-11.1)
num.should be_kind_of(Float)
(0.0 <= num).should == true
(num < 1.0).should == true
end

it "generates different float numbers with subsequent invocations" do
# quick and dirty check, but good enough
values = []