| 1 |
|
|---|
| 2 |
|
|---|
| 3 |
|
|---|
| 4 |
|
|---|
| 5 |
|
|---|
| 6 |
|
|---|
| 7 |
|
|---|
| 8 |
module Rails |
|---|
| 9 |
class SecretKeyGenerator |
|---|
| 10 |
GENERATORS = [ :secure_random, :win32_api, :urandom, :openssl, :prng ].freeze |
|---|
| 11 |
|
|---|
| 12 |
def initialize(identifier) |
|---|
| 13 |
@identifier = identifier |
|---|
| 14 |
end |
|---|
| 15 |
|
|---|
| 16 |
|
|---|
| 17 |
|
|---|
| 18 |
def generate_secret |
|---|
| 19 |
generator = GENERATORS.find do |g| |
|---|
| 20 |
self.class.send("supports_#{g}?") |
|---|
| 21 |
end |
|---|
| 22 |
send("generate_secret_with_#{generator}") |
|---|
| 23 |
end |
|---|
| 24 |
|
|---|
| 25 |
|
|---|
| 26 |
|
|---|
| 27 |
|
|---|
| 28 |
def generate_secret_with_win32_api |
|---|
| 29 |
|
|---|
| 30 |
require 'Win32API' |
|---|
| 31 |
|
|---|
| 32 |
crypt_acquire_context = Win32API.new("advapi32", "CryptAcquireContext", |
|---|
| 33 |
'PPPII', 'L') |
|---|
| 34 |
crypt_gen_random = Win32API.new("advapi32", "CryptGenRandom", |
|---|
| 35 |
'LIP', 'L') |
|---|
| 36 |
crypt_release_context = Win32API.new("advapi32", "CryptReleaseContext", |
|---|
| 37 |
'LI', 'L') |
|---|
| 38 |
prov_rsa_full = 1 |
|---|
| 39 |
crypt_verifycontext = 0xF0000000 |
|---|
| 40 |
|
|---|
| 41 |
hProvStr = " " * 4 |
|---|
| 42 |
if crypt_acquire_context.call(hProvStr, nil, nil, prov_rsa_full, |
|---|
| 43 |
crypt_verifycontext) == 0 |
|---|
| 44 |
raise SystemCallError, "CryptAcquireContext failed: #{lastWin32ErrorMessage}" |
|---|
| 45 |
end |
|---|
| 46 |
hProv, = hProvStr.unpack('L') |
|---|
| 47 |
bytes = " " * 64 |
|---|
| 48 |
if crypt_gen_random.call(hProv, bytes.size, bytes) == 0 |
|---|
| 49 |
raise SystemCallError, "CryptGenRandom failed: #{lastWin32ErrorMessage}" |
|---|
| 50 |
end |
|---|
| 51 |
if crypt_release_context.call(hProv, 0) == 0 |
|---|
| 52 |
raise SystemCallError, "CryptReleaseContext failed: #{lastWin32ErrorMessage}" |
|---|
| 53 |
end |
|---|
| 54 |
bytes.unpack("H*")[0] |
|---|
| 55 |
end |
|---|
| 56 |
|
|---|
| 57 |
|
|---|
| 58 |
|
|---|
| 59 |
|
|---|
| 60 |
def generate_secret_with_secure_random |
|---|
| 61 |
require 'securerandom' |
|---|
| 62 |
return SecureRandom.hex(64) |
|---|
| 63 |
end |
|---|
| 64 |
|
|---|
| 65 |
|
|---|
| 66 |
|
|---|
| 67 |
|
|---|
| 68 |
def generate_secret_with_openssl |
|---|
| 69 |
require 'openssl' |
|---|
| 70 |
if !File.exist?("/dev/urandom") |
|---|
| 71 |
|
|---|
| 72 |
|
|---|
| 73 |
|
|---|
| 74 |
|
|---|
| 75 |
|
|---|
| 76 |
|
|---|
| 77 |
OpenSSL::Random.seed(rand(0).to_s + Time.now.usec.to_s) |
|---|
| 78 |
end |
|---|
| 79 |
data = OpenSSL::BN.rand(2048, -1, false).to_s |
|---|
| 80 |
return OpenSSL::Digest::SHA512.new(data).hexdigest |
|---|
| 81 |
end |
|---|
| 82 |
|
|---|
| 83 |
|
|---|
| 84 |
|
|---|
| 85 |
def generate_secret_with_urandom |
|---|
| 86 |
return File.read("/dev/urandom", 64).unpack("H*")[0] |
|---|
| 87 |
end |
|---|
| 88 |
|
|---|
| 89 |
|
|---|
| 90 |
|
|---|
| 91 |
|
|---|
| 92 |
|
|---|
| 93 |
|
|---|
| 94 |
def generate_secret_with_prng |
|---|
| 95 |
require 'digest/sha2' |
|---|
| 96 |
sha = Digest::SHA2.new(512) |
|---|
| 97 |
now = Time.now |
|---|
| 98 |
sha << now.to_s |
|---|
| 99 |
sha << String(now.usec) |
|---|
| 100 |
sha << String(rand(0)) |
|---|
| 101 |
sha << String($$) |
|---|
| 102 |
sha << @identifier |
|---|
| 103 |
return sha.hexdigest |
|---|
| 104 |
end |
|---|
| 105 |
|
|---|
| 106 |
private |
|---|
| 107 |
def lastWin32ErrorMessage |
|---|
| 108 |
|
|---|
| 109 |
get_last_error = Win32API.new("kernel32", "GetLastError", '', 'L') |
|---|
| 110 |
format_message = Win32API.new("kernel32", "FormatMessageA", |
|---|
| 111 |
'LPLLPLPPPPPPPP', 'L') |
|---|
| 112 |
format_message_ignore_inserts = 0x00000200 |
|---|
| 113 |
format_message_from_system = 0x00001000 |
|---|
| 114 |
|
|---|
| 115 |
code = get_last_error.call |
|---|
| 116 |
msg = "\0" * 1024 |
|---|
| 117 |
len = format_message.call(format_message_ignore_inserts + |
|---|
| 118 |
format_message_from_system, 0, |
|---|
| 119 |
code, 0, msg, 1024, nil, nil, |
|---|
| 120 |
nil, nil, nil, nil, nil, nil) |
|---|
| 121 |
msg[0, len].tr("\r", '').chomp |
|---|
| 122 |
end |
|---|
| 123 |
|
|---|
| 124 |
def self.supports_secure_random? |
|---|
| 125 |
begin |
|---|
| 126 |
require 'securerandom' |
|---|
| 127 |
true |
|---|
| 128 |
rescue LoadError |
|---|
| 129 |
false |
|---|
| 130 |
end |
|---|
| 131 |
end |
|---|
| 132 |
|
|---|
| 133 |
def self.supports_win32_api? |
|---|
| 134 |
return false unless RUBY_PLATFORM =~ /(:?mswin|mingw)/ |
|---|
| 135 |
begin |
|---|
| 136 |
require 'Win32API' |
|---|
| 137 |
true |
|---|
| 138 |
rescue LoadError |
|---|
| 139 |
false |
|---|
| 140 |
end |
|---|
| 141 |
end |
|---|
| 142 |
|
|---|
| 143 |
def self.supports_urandom? |
|---|
| 144 |
File.exist?('/dev/urandom') |
|---|
| 145 |
end |
|---|
| 146 |
|
|---|
| 147 |
def self.supports_openssl? |
|---|
| 148 |
begin |
|---|
| 149 |
require 'openssl' |
|---|
| 150 |
true |
|---|
| 151 |
rescue LoadError |
|---|
| 152 |
false |
|---|
| 153 |
end |
|---|
| 154 |
end |
|---|
| 155 |
|
|---|
| 156 |
def self.supports_prng? |
|---|
| 157 |
true |
|---|
| 158 |
end |
|---|
| 159 |
end |
|---|
| 160 |
end |
|---|