super_random 2.0.210126 → 3.0.230113

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2480d12416526288677a0a9203d7e63ae666eb70b59fc792cc1a67bc2c35978a
4
- data.tar.gz: 52d9c2e334770c1498f6afffc1bc967db38686a1d5098882f2b1290bbf5f1e60
3
+ metadata.gz: 313ffa9d968e2c7b511fc80ec9a0b6c3de755fe0dd9ed51531448e9f8768d393
4
+ data.tar.gz: 43cae93ca8d339bd61018fa3b93806426ad7a8bc77161a52309e23b99f0aeedc
5
5
  SHA512:
6
- metadata.gz: 3dcb0cb30658abd969e2930cc60bc9d0ff33bca7b4dc7ee0e418c43ba6f9815d52c7af4c21346320ff7941422e2ff8fbbb75a3e0453c9172edf3f05e5a0609ff
7
- data.tar.gz: c0b24a58b9a236992b615a3bad666af0d5377575dabed3fd760cc3b2857904f9bec41ed3ee0f186c11820979c0bea50cf4f52de580ff6a576d3dc253a2644f43
6
+ metadata.gz: 262c9131cc290b6df1cb1b68b54aaa2fb0cd60b6288c33facd07761e1f80802747a0555db7ec7edb9dd5146ecd088c463b5347a5dce2f2da263518964740e3a0
7
+ data.tar.gz: 352bcace20e9b6f2250c55000a69d2fa51c5f8db3b8a0f7c7474be068d2698d0622d7759c9acfd3cb2e4fa2c2b0fb3a81bfd60fee7390562c1b0078f001f4c84
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # SuperRandom
2
2
 
3
- * [VERSION 2.0.210126](https://github.com/carlosjhr64/super_random/releases)
3
+ * [VERSION 3.0.230113](https://github.com/carlosjhr64/super_random/releases)
4
4
  * [github](https://github.com/carlosjhr64/super_random)
5
5
  * [rubygems](https://rubygems.org/gems/super_random)
6
6
 
@@ -8,51 +8,79 @@
8
8
 
9
9
  You can't get more random than random, but you can try really, really, really hard.
10
10
 
11
- SuperRandom combines three online real random services to create a more perfect random byte.
11
+ `SuperRandom` combines sources of entropy to generate super-random bytes!
12
12
 
13
13
  ## INSTALL:
14
14
 
15
15
  $ gem install super_random
16
16
 
17
17
  ## SYNOPSIS:
18
-
19
18
  ```ruby
20
19
  require 'super_random'
20
+ SuperRandom::VERSION #=> "3.0.230113"
21
+ SuperRandom::DEFAULT_SOURCES #~> www.random.org
21
22
  super_random = SuperRandom.new
23
+ super_random.sources #~> www.random.org
24
+ super_random.bytes #~> ^\[\d+(, \d+){63}\]$
25
+ # The source_count attribute gives the number of sources successfully used.
26
+ super_random.source_count #=> 1
27
+ # The byte_count attribute gives the total bytes digested from sources.
28
+ super_random.byte_count #=> 210
29
+ super_random.hexadecimal #~> ^\h{128}$
30
+ super_random.random_number(100.0) #~> ^\d{1,2}\.\d+$
31
+ super_random.random_number(100) #~> ^\d{1,2}$
32
+ # Because of a 1 minute rate limit, subsequent source counts are zero:
33
+ super_random.source_count #=> 0
34
+ super_random.byte_count #=> 0
35
+ # Snapshots from your webcam can be a good entropy source:
36
+ super_random = SuperRandom.new('/var/lib/motion/lastsnap.jpg')
37
+ super_random.sources #=> ["/var/lib/motion/lastsnap.jpg"]
38
+ ```
39
+ ## METHODOLOGY:
22
40
 
23
- super_random.bytes(32) #~> ^\[\d+(, \d+){31}\]$
24
- # Example:
25
- # [142, 36, 107, 199, 1, 222, 69, 238, 130, 159, 236, 201, 199,
26
- # 33, 237, 166, 189, 166, 95, 246, 111, 103, 113, 126, 27, 31,
27
- # 244, 215, 200, 60, 255, 184]
41
+ `SuperRandom` uses `OpenUri` to read your sources, which
42
+ can be http, https, or ftp URLs.
43
+ `Digest::SHA2.new(512)` is used to digest your sources.
44
+ Finally, `SecureRandom.bytes` are fed to the digest as a fail safe.
45
+ This generates the final 64 random bytes.
28
46
 
29
- sleep 1 # rate limit to be nice
30
- super_random.hexadecimal(32) #~> ^\h{64}$
31
- # Example:
32
- # "3e0dffe42c08b849dc3c1290e7aa87dff4ad3037b29694136786a4db1e3efab8"
47
+ ## SOURCES:
33
48
 
34
- sleep 1
35
- super_random.random_number(100.0) #~> ^\d{1,2}\.\d+$
36
- # Example:
37
- # 16.882225652425537
38
-
39
- sleep 1
40
- super_random.random_number(100) #~> ^\d{1,2}$
41
- # Example:
42
- # 85
43
-
44
- # The "services" attribute gives the number of online services used.
45
- # It's possible for a service to fail.
46
- # Ultimately, SuperRandom uses SecureRandom as a failsafe.
47
- super_random.services #=> 3
48
- super_random.randomness #=> 3.0
49
- ```
49
+ I could only find one good source expressly for this purpose, `www.random.org`.
50
+ Another good one is `qrng.anu.edu.au`, but you'll need an API key.
51
+ Consider using:
50
52
 
53
+ * Snapshots from your webcam
54
+ * List of market spot prices
55
+ * Weather forecasts
56
+ * News or micro-logging feeds
57
+
58
+ Be very nice about your calls,
59
+ specially when you're not using the source as intended.
60
+ `SuperRandom` enforces a one minute rate limit in the use of sources.
61
+ Here are ways to set custom sources:
62
+ ```ruby
63
+ # Sources specified in the constructor:
64
+ super_random = SuperRandom.new('/var/lib/motion/lastsnap.jpg', 'https://wttr.in')
65
+ super_random.sources
66
+ #=> ["/var/lib/motion/lastsnap.jpg", "https://wttr.in"]
67
+
68
+ # Sources appended on the instance:
69
+ super_random.sources.append 'https://text.npr.org'
70
+ super_random.sources
71
+ #=> ["/var/lib/motion/lastsnap.jpg", "https://wttr.in", "https://text.npr.org"]
72
+
73
+ # Sources appended to the DEFAULT_SOURCES
74
+ SuperRandom::DEFAULT_SOURCES.append 'https://coinmarketcap.com'
75
+ super_random = SuperRandom.new
76
+ super_random.sources
77
+ #~> random.org.*coinmarketcap.com
78
+ ```
51
79
  ## LICENSE:
52
80
 
53
81
  (The MIT License)
54
82
 
55
- Copyright (c) 2021 carlosjhr64
83
+ Copyright (c) 2023 carlosjhr64
56
84
 
57
85
  Permission is hereby granted, free of charge, to any person obtaining
58
86
  a copy of this software and associated documentation files (the
data/lib/super_random.rb CHANGED
@@ -1,15 +1,95 @@
1
- # Standard Libraries
2
1
  require 'timeout'
3
2
  require 'securerandom'
4
- require 'net/http'
5
- require 'json'
3
+ require 'open-uri'
4
+ require 'digest'
5
+ # Requires:
6
+ #`ruby`
6
7
 
7
8
  class SuperRandom
8
- VERSION = '2.0.210126'
9
- # This Gem
10
- require 'super_random/services'
11
- require 'super_random/generator'
12
- end
9
+ VERSION = '3.0.230113'
10
+ DEFAULT_SOURCES = [
11
+ 'https://www.random.org/strings/?num=10&len=20&digits=on&upperalpha=on&loweralpha=on&unique=on&format=plain&rnd=new',
12
+ ]
13
+ MUTEX = Mutex.new
14
+ DIV = 128.times.inject(''){|h,_|h<<'f'}.to_i(16)
15
+ RATE_LIMIT = 60 # One minute, be nice!
16
+ @@LAST_TIME = Time.now.to_i - RATE_LIMIT - 1
13
17
 
14
- # Requires:
15
- #`ruby`
18
+ attr_accessor :first_timeout, :second_timeout, :nevermind
19
+ attr_reader :sources, :source_count, :byte_count
20
+ def initialize(*sources)
21
+ @sources = sources.empty? ? DEFAULT_SOURCES.dup : sources
22
+ @byte_count,@source_count = 0,0
23
+ @first_timeout,@second_timeout,@nevermind = 3,6,true
24
+ end
25
+
26
+ # Returns 64 random bytes(at least from SecureRandom's)
27
+ def bytes
28
+ @byte_count,@source_count = 0,0
29
+ _do_updates if Time.now.to_i - @@LAST_TIME > RATE_LIMIT
30
+ _get_bytes
31
+ end
32
+
33
+ def hexadecimal
34
+ bytes.map{_1.to_s(16).rjust(2,'0')}.join
35
+ end
36
+
37
+ def random_number(scale=1.0)
38
+ case scale
39
+ when Float
40
+ return scale * Rational(hexadecimal.to_i(16), DIV)
41
+ when Integer
42
+ return ((hexadecimal.to_i(16) + SecureRandom.random_number(scale)) % scale)
43
+ end
44
+ raise "rand(scale Integer|Float)"
45
+ end
46
+ alias rand random_number
47
+
48
+ private
49
+
50
+ SHASUM = Digest::SHA2.new(512)
51
+ def _get_bytes
52
+ MUTEX.synchronize do
53
+ SHASUM.update SecureRandom.bytes(64)
54
+ SHASUM.digest.chars.map{_1.ord}
55
+ end
56
+ ensure
57
+ MUTEX.synchronize{SHASUM.update SecureRandom.bytes(64)}
58
+ end
59
+
60
+ def _update_with(source)
61
+ URI.open(source) do |tmp|
62
+ MUTEX.synchronize do
63
+ tmp.each_line do |line|
64
+ SHASUM.update line
65
+ @byte_count += line.bytesize
66
+ end
67
+ @source_count += 1
68
+ end
69
+ end
70
+ rescue
71
+ $stderr.puts $!
72
+ end
73
+
74
+ def _do_updates
75
+ @@LAST_TIME = Time.now.to_i
76
+ threads = @sources.inject([]){|t,s|t.push Thread.new{_update_with s}}
77
+ begin
78
+ # Initially, would like to get them all.
79
+ Timeout.timeout(@first_timeout){threads.each{_1.join}}
80
+ rescue Timeout::Error
81
+ begin
82
+ Timeout.timeout(@second_timeout) do
83
+ # But at this point,
84
+ # would like to get at least one.
85
+ while @services<1 and threads.any?{_1.alive?}
86
+ Thread.pass
87
+ end
88
+ end
89
+ rescue Timeout::Error
90
+ Mutex.sychronize{threads.each{_1.exit}} # Exit each thread
91
+ raise $! unless @nevermind # Go on if never-minding
92
+ end
93
+ end
94
+ end
95
+ end
metadata CHANGED
@@ -1,19 +1,19 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: super_random
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.210126
4
+ version: 3.0.230113
5
5
  platform: ruby
6
6
  authors:
7
- - carlosjhr64
8
- autorequire:
7
+ - CarlosJHR64
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-01-26 00:00:00.000000000 Z
11
+ date: 2023-01-13 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
14
  You can't get more random than random, but you can try really, really, really hard.
15
15
 
16
- SuperRandom combines three online real random services to create a more perfect random byte.
16
+ `SuperRandom` combines sources of entropy to generate super-random bytes!
17
17
  email: carlosjhr64@gmail.com
18
18
  executables: []
19
19
  extensions: []
@@ -21,13 +21,11 @@ extra_rdoc_files: []
21
21
  files:
22
22
  - README.md
23
23
  - lib/super_random.rb
24
- - lib/super_random/generator.rb
25
- - lib/super_random/services.rb
26
24
  homepage: https://github.com/carlosjhr64/super_random
27
25
  licenses:
28
26
  - MIT
29
27
  metadata: {}
30
- post_install_message:
28
+ post_install_message:
31
29
  rdoc_options: []
32
30
  require_paths:
33
31
  - lib
@@ -42,9 +40,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
42
40
  - !ruby/object:Gem::Version
43
41
  version: '0'
44
42
  requirements:
45
- - 'ruby: ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-linux]'
46
- rubygems_version: 3.2.3
47
- signing_key:
43
+ - 'ruby: ruby 3.2.0 (2022-12-25 revision a528908271) [aarch64-linux]'
44
+ rubygems_version: 3.4.3
45
+ signing_key:
48
46
  specification_version: 4
49
47
  summary: You can't get more random than random, but you can try really, really, really
50
48
  hard.
@@ -1,99 +0,0 @@
1
- class SuperRandom
2
- DEFAULT_BYTES = 32
3
-
4
- attr_accessor :first_timeout, :second_timeout, :nevermind
5
- attr_reader :randomness, :services
6
-
7
- def initialize
8
- @first_timeout = 3
9
- @second_timeout = 6
10
- @nevermind = true
11
- @randomness = 0.0
12
- @services = 0
13
- end
14
-
15
- def bytes(n=DEFAULT_BYTES)
16
- @randomness = 0.0
17
- @services = 0
18
-
19
- m = SuperRandom.services
20
- a = Array.new m.length
21
- t = Array.new m.length
22
- m.each_with_index do |k,i|
23
- t[i] = Thread.new{ a[i] = SuperRandom.send(k, n) }
24
- end
25
-
26
- begin
27
- Timeout.timeout(@first_timeout) do
28
- # Initially, would like to get them all.
29
- t.each{_1.join}
30
- end
31
- rescue Timeout::Error
32
- begin
33
- Timeout.timeout(@second_timeout) do
34
- # But at this point,
35
- # would like to get at least one.
36
- while a.all?{_1.nil?} and t.any?{_1.alive?}
37
- Thread.pass
38
- end
39
- end
40
- rescue Timeout::Error
41
- # If we don't care that we got nothing, go on.
42
- raise $! unless @nevermind
43
- end
44
- end
45
-
46
- r = Array.new n
47
- n.times{|i| r[i] = SecureRandom.random_number(256)}
48
-
49
- a.each do |b|
50
- next if b.nil?
51
- l = b.length
52
- @randomness += l.to_f/n.to_f
53
- @services += 1
54
- n.times{|i|r[i]=(r[i]+b[i%l])%256}
55
- end
56
-
57
- return r
58
- end
59
-
60
- def hexadecimal(n=DEFAULT_BYTES)
61
- bytes(n).map{|i|i.to_s(16).rjust(2,'0')}.join
62
- end
63
-
64
- def random_number(scale=1.0, minbytes=6, maxbytes=[minbytes,DEFAULT_BYTES].max)
65
- case scale
66
- when Float
67
- div = minbytes.times.inject(''){|s,i| s+'FF'}.to_i(16)
68
- den = hexadecimal(minbytes).to_i(16)
69
- return scale * den.to_f / div.to_f
70
- when Integer
71
- n = n0 = Math.log(scale, 256).ceil
72
- e = e0 = 256**n
73
- r = r0 = e0 % scale
74
- while r > 0
75
- n0 += 1
76
- e0 *= 256
77
- r0 = e0 % scale
78
- if r0 <= r
79
- # break if repeating pattern with big enough integer
80
- break if r0 == r and n0 > minbytes
81
- r,n,e = r0,n0,e0
82
- end
83
- break if n0 >= maxbytes
84
- end
85
- max = (e/scale)*scale
86
- loop do
87
- number = hexadecimal(n).to_i(16)
88
- return number % scale if number < max
89
- # On a relatively small chance that we're above max...
90
- if @nevermind
91
- warn "using SecureRandom.random_number(#{scale})"
92
- return SecureRandom.random_number(scale)
93
- end
94
- end
95
- end
96
- raise "rand(scale Integer|Float)"
97
- end
98
- alias rand random_number
99
- end
@@ -1,45 +0,0 @@
1
- class SuperRandom
2
- def self.services
3
- [:quantum, :atmospheric, :hotbits]
4
- end
5
-
6
- # https://qrng.anu.edu.au/
7
- # https://qrng.anu.edu.au/contact/api-documentation/
8
- def self.quantum(n)
9
- s = Net::HTTP.get(URI(
10
- "https://qrng.anu.edu.au/API/jsonI.php?length=#{n}&type=uint8"))
11
- a = JSON.parse(s)['data']
12
- raise unless a.is_a?(Array) and a.length==n
13
- raise unless a.all?{|i| i.is_a?(Integer) and i.between?(0,255)}
14
- return a
15
- rescue StandardError
16
- warn "quantum (qrng.anu.edu.au) failed."
17
- return nil
18
- end
19
-
20
- # https://www.random.org/
21
- # https://www.random.org/integers/
22
- def self.atmospheric(n)
23
- s = Net::HTTP.get(URI(
24
- "https://www.random.org/integers/?num=#{n}&min=0&max=255&col=1&base=10&format=plain&rnd=new"))
25
- a = s.strip.split(/\s+/).map{|j|j.to_i}
26
- raise unless a.length==n
27
- raise unless a.all?{|i| i.between?(0,255)}
28
- return a
29
- rescue StandardError
30
- warn "atmospheric (www.random.org) failed."
31
- return nil
32
- end
33
-
34
- # https://www.fourmilab.ch/hotbits/
35
- def self.hotbits(n, k='Pseudorandom')
36
- s = Net::HTTP.get(URI(
37
- "https://www.fourmilab.ch/cgi-bin/Hotbits.api?nbytes=#{n}&fmt=bin&apikey=#{k}"))
38
- a = s.bytes
39
- raise unless a.length==n
40
- return a
41
- rescue StandardError
42
- warn "hotbits (www.fourmilab.ch) failed."
43
- return nil
44
- end
45
- end