From 94fdd87fddb977ba4a9075a05476bc6fc294b746 Mon Sep 17 00:00:00 2001 From: akr Date: Fri, 8 Jun 2007 05:26:20 +0000 Subject: [PATCH] * lib/secrand.rb: new file for secure random interface. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12475 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 4 +++ lib/secrand.rb | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 lib/secrand.rb diff --git a/ChangeLog b/ChangeLog index babbe7b2ee..6479093f35 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +Fri Jun 8 14:26:18 2007 Tanaka Akira + + * lib/secrand.rb: new file for secure random interface. + Fri Jun 8 12:44:37 2007 NAKAMURA Usaku * {win32,wince}/Makefile.sub: add lex.c rule. diff --git a/lib/secrand.rb b/lib/secrand.rb new file mode 100644 index 0000000000..8f7bbc6007 --- /dev/null +++ b/lib/secrand.rb @@ -0,0 +1,66 @@ +begin + require 'openssl' +rescue LoadError +end + +module SecRand + def self.random_bytes(n=nil) + n ||= 16 + if defined? OpenSSL::Random + return OpenSSL::Random.random_bytes(n) + end + if !defined?(@has_urandom) || @has_urandom + @has_urandom = false + flags = File::RDONLY + flags |= File::NONBLOCK if defined? File::NONBLOCK + flags |= File::NOCTTY if defined? File::NOCTTY + flags |= File::NOFOLLOW if defined? File::NOFOLLOW + begin + File.open("/dev/urandom", flags) {|f| + unless f.stat.chardev? + raise Errno::ENOENT + end + @has_urandom = true + ret = f.readpartial(n) + if ret.length != n + raise NotImplementedError, "Unexpected partial read from random device" + end + return ret + } + rescue Errno::ENOENT + raise NotImplementedError, "No random device" + end + end + raise NotImplementedError, "No random device" + end + + def self.hex(n=nil) + random_bytes(n).unpack("H*")[0] + end + + def self.base64(n=nil) + [random_bytes(n)].pack("m*").delete("\n") + end + +end + +def SecRand(n=0) + if 0 < n + hex = n.to_s(16) + hex = '0' + hex if (hex.length & 1) == 1 + bin = [hex].pack("H*") + mask = bin[0].ord + mask |= mask >> 1 + mask |= mask >> 2 + mask |= mask >> 4 + begin + rnd = SecRand.random_bytes(bin.length) + rnd[0] = (rnd[0].ord & mask).chr + end until rnd < bin + rnd.unpack("H*")[0].hex + else + # assumption: Float::MANT_DIG <= 64 + i64 = SecRand.random_bytes(8).unpack("Q")[0] + Math.ldexp(i64 >> (64-Float::MANT_DIG), -Float::MANT_DIG) + end +end