super_random 2.0.210126 → 3.0.230113

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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