securerandom 0.3.2 → 0.4.0

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: 6f14853630e9280f98e432e1da48fd1eb894a7376c4e3678dc97639e28929c3d
4
- data.tar.gz: 79846163a70d7b0198271a61ce63a7320c9a141dd59d0912a91e9a5259353999
3
+ metadata.gz: 52ec261823c8ca03d85bd23789c1e3aa4c6ac287d28d8d950d355b6405c7668f
4
+ data.tar.gz: 89010c35a3d7bd4125e9b1893ad07a3f8333d547584e26e29b5bf218d6941efa
5
5
  SHA512:
6
- metadata.gz: 7d448c439592c67f07e543c855fa4c06e6da72ce84d31ade33ea97d76d5ee90952b575c938a34bf8e11453353920582d0d5311cf9212b6ba7397fa3d038bc2e5
7
- data.tar.gz: c3080d88c1a11a88ca3a28ddc6e173ab5afbf6027d60c353263ed9f78b8982a55dcc15c4a88cc3f2a44ba6e8a5933d7283af91b2f38965f67ebd68e636d78922
6
+ metadata.gz: 55d0ab1eb4c1018c954cd6c87578ae7a06d7dabec151b3e3f319db2e514517c68051858c53d62e418005020a1789d36dc1e0412163c4cb5f797acd374a0c7b61
7
+ data.tar.gz: eda8bb6f8e502e8aca5586a9878896c598d6b5ce1b3a4dc45956ee3fafad767a943730e5d4e92c4a2292169123abc8b155c066e4cee1f90df6ed2015250407cb
data/lib/securerandom.rb CHANGED
@@ -41,7 +41,7 @@ require 'random/formatter'
41
41
  module SecureRandom
42
42
 
43
43
  # The version
44
- VERSION = "0.3.2"
44
+ VERSION = "0.4.0"
45
45
 
46
46
  class << self
47
47
  # Returns a random binary string containing +size+ bytes.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: securerandom
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tanaka Akira
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-11-11 00:00:00.000000000 Z
11
+ date: 2024-12-02 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Interface for secure random number generator.
14
14
  email:
@@ -20,7 +20,6 @@ files:
20
20
  - BSDL
21
21
  - COPYING
22
22
  - README.md
23
- - lib/random/formatter.rb
24
23
  - lib/securerandom.rb
25
24
  homepage: https://github.com/ruby/securerandom
26
25
  licenses:
@@ -37,7 +36,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
37
36
  requirements:
38
37
  - - ">="
39
38
  - !ruby/object:Gem::Version
40
- version: 2.6.0
39
+ version: 3.1.0
41
40
  required_rubygems_version: !ruby/object:Gem::Requirement
42
41
  requirements:
43
42
  - - ">="
@@ -1,372 +0,0 @@
1
- # -*- coding: us-ascii -*-
2
- # frozen_string_literal: true
3
-
4
- # == \Random number formatter.
5
- #
6
- # Formats generated random numbers in many manners. When <tt>'random/formatter'</tt>
7
- # is required, several methods are added to empty core module <tt>Random::Formatter</tt>,
8
- # making them available as Random's instance and module methods.
9
- #
10
- # Standard library SecureRandom is also extended with the module, and the methods
11
- # described below are available as a module methods in it.
12
- #
13
- # === Examples
14
- #
15
- # Generate random hexadecimal strings:
16
- #
17
- # require 'random/formatter'
18
- #
19
- # prng = Random.new
20
- # prng.hex(10) #=> "52750b30ffbc7de3b362"
21
- # prng.hex(10) #=> "92b15d6c8dc4beb5f559"
22
- # prng.hex(13) #=> "39b290146bea6ce975c37cfc23"
23
- # # or just
24
- # Random.hex #=> "1aed0c631e41be7f77365415541052ee"
25
- #
26
- # Generate random base64 strings:
27
- #
28
- # prng.base64(10) #=> "EcmTPZwWRAozdA=="
29
- # prng.base64(10) #=> "KO1nIU+p9DKxGg=="
30
- # prng.base64(12) #=> "7kJSM/MzBJI+75j8"
31
- # Random.base64(4) #=> "bsQ3fQ=="
32
- #
33
- # Generate random binary strings:
34
- #
35
- # prng.random_bytes(10) #=> "\016\t{\370g\310pbr\301"
36
- # prng.random_bytes(10) #=> "\323U\030TO\234\357\020\a\337"
37
- # Random.random_bytes(6) #=> "\xA1\xE6Lr\xC43"
38
- #
39
- # Generate alphanumeric strings:
40
- #
41
- # prng.alphanumeric(10) #=> "S8baxMJnPl"
42
- # prng.alphanumeric(10) #=> "aOxAg8BAJe"
43
- # Random.alphanumeric #=> "TmP9OsJHJLtaZYhP"
44
- #
45
- # Generate UUIDs:
46
- #
47
- # prng.uuid #=> "2d931510-d99f-494a-8c67-87feb05e1594"
48
- # prng.uuid #=> "bad85eb9-0713-4da7-8d36-07a8e4b00eab"
49
- # Random.uuid #=> "f14e0271-de96-45cc-8911-8910292a42cd"
50
- #
51
- # All methods are available in the standard library SecureRandom, too:
52
- #
53
- # SecureRandom.hex #=> "05b45376a30c67238eb93b16499e50cf"
54
-
55
- module Random::Formatter
56
-
57
- # Generate a random binary string.
58
- #
59
- # The argument _n_ specifies the length of the result string.
60
- #
61
- # If _n_ is not specified or is nil, 16 is assumed.
62
- # It may be larger in future.
63
- #
64
- # The result may contain any byte: "\x00" - "\xff".
65
- #
66
- # require 'random/formatter'
67
- #
68
- # Random.random_bytes #=> "\xD8\\\xE0\xF4\r\xB2\xFC*WM\xFF\x83\x18\xF45\xB6"
69
- # # or
70
- # prng = Random.new
71
- # prng.random_bytes #=> "m\xDC\xFC/\a\x00Uf\xB2\xB2P\xBD\xFF6S\x97"
72
- def random_bytes(n=nil)
73
- n = n ? n.to_int : 16
74
- gen_random(n)
75
- end
76
-
77
- # Generate a random hexadecimal string.
78
- #
79
- # The argument _n_ specifies the length, in bytes, of the random number to be generated.
80
- # The length of the resulting hexadecimal string is twice of _n_.
81
- #
82
- # If _n_ is not specified or is nil, 16 is assumed.
83
- # It may be larger in the future.
84
- #
85
- # The result may contain 0-9 and a-f.
86
- #
87
- # require 'random/formatter'
88
- #
89
- # Random.hex #=> "eb693ec8252cd630102fd0d0fb7c3485"
90
- # # or
91
- # prng = Random.new
92
- # prng.hex #=> "91dc3bfb4de5b11d029d376634589b61"
93
- def hex(n=nil)
94
- random_bytes(n).unpack1("H*")
95
- end
96
-
97
- # Generate a random base64 string.
98
- #
99
- # The argument _n_ specifies the length, in bytes, of the random number
100
- # to be generated. The length of the result string is about 4/3 of _n_.
101
- #
102
- # If _n_ is not specified or is nil, 16 is assumed.
103
- # It may be larger in the future.
104
- #
105
- # The result may contain A-Z, a-z, 0-9, "+", "/" and "=".
106
- #
107
- # require 'random/formatter'
108
- #
109
- # Random.base64 #=> "/2BuBuLf3+WfSKyQbRcc/A=="
110
- # # or
111
- # prng = Random.new
112
- # prng.base64 #=> "6BbW0pxO0YENxn38HMUbcQ=="
113
- #
114
- # See RFC 3548 for the definition of base64.
115
- def base64(n=nil)
116
- [random_bytes(n)].pack("m0")
117
- end
118
-
119
- # Generate a random URL-safe base64 string.
120
- #
121
- # The argument _n_ specifies the length, in bytes, of the random number
122
- # to be generated. The length of the result string is about 4/3 of _n_.
123
- #
124
- # If _n_ is not specified or is nil, 16 is assumed.
125
- # It may be larger in the future.
126
- #
127
- # The boolean argument _padding_ specifies the padding.
128
- # If it is false or nil, padding is not generated.
129
- # Otherwise padding is generated.
130
- # By default, padding is not generated because "=" may be used as a URL delimiter.
131
- #
132
- # The result may contain A-Z, a-z, 0-9, "-" and "_".
133
- # "=" is also used if _padding_ is true.
134
- #
135
- # require 'random/formatter'
136
- #
137
- # Random.urlsafe_base64 #=> "b4GOKm4pOYU_-BOXcrUGDg"
138
- # # or
139
- # prng = Random.new
140
- # prng.urlsafe_base64 #=> "UZLdOkzop70Ddx-IJR0ABg"
141
- #
142
- # prng.urlsafe_base64(nil, true) #=> "i0XQ-7gglIsHGV2_BNPrdQ=="
143
- # prng.urlsafe_base64(nil, true) #=> "-M8rLhr7JEpJlqFGUMmOxg=="
144
- #
145
- # See RFC 3548 for the definition of URL-safe base64.
146
- def urlsafe_base64(n=nil, padding=false)
147
- s = [random_bytes(n)].pack("m0")
148
- s.tr!("+/", "-_")
149
- s.delete!("=") unless padding
150
- s
151
- end
152
-
153
- # Generate a random v4 UUID (Universally Unique IDentifier).
154
- #
155
- # require 'random/formatter'
156
- #
157
- # Random.uuid #=> "2d931510-d99f-494a-8c67-87feb05e1594"
158
- # Random.uuid #=> "bad85eb9-0713-4da7-8d36-07a8e4b00eab"
159
- # # or
160
- # prng = Random.new
161
- # prng.uuid #=> "62936e70-1815-439b-bf89-8492855a7e6b"
162
- #
163
- # The version 4 UUID is purely random (except the version).
164
- # It doesn't contain meaningful information such as MAC addresses, timestamps, etc.
165
- #
166
- # The result contains 122 random bits (15.25 random bytes).
167
- #
168
- # See RFC9562[https://www.rfc-editor.org/rfc/rfc9562] for details of UUIDv4.
169
- #
170
- def uuid
171
- ary = random_bytes(16)
172
- ary.setbyte(6, (ary.getbyte(6) & 0x0f) | 0x40)
173
- ary.setbyte(8, (ary.getbyte(8) & 0x3f) | 0x80)
174
- ary.unpack("H8H4H4H4H12").join(?-)
175
- end
176
-
177
- alias uuid_v4 uuid
178
-
179
- # Generate a random v7 UUID (Universally Unique IDentifier).
180
- #
181
- # require 'random/formatter'
182
- #
183
- # Random.uuid_v7 # => "0188d4c3-1311-7f96-85c7-242a7aa58f1e"
184
- # Random.uuid_v7 # => "0188d4c3-16fe-744f-86af-38fa04c62bb5"
185
- # Random.uuid_v7 # => "0188d4c3-1af8-764f-b049-c204ce0afa23"
186
- # Random.uuid_v7 # => "0188d4c3-1e74-7085-b14f-ef6415dc6f31"
187
- # # |<--sorted-->| |<----- random ---->|
188
- #
189
- # # or
190
- # prng = Random.new
191
- # prng.uuid_v7 # => "0188ca51-5e72-7950-a11d-def7ff977c98"
192
- #
193
- # The version 7 UUID starts with the least significant 48 bits of a 64 bit
194
- # Unix timestamp (milliseconds since the epoch) and fills the remaining bits
195
- # with random data, excluding the version and variant bits.
196
- #
197
- # This allows version 7 UUIDs to be sorted by creation time. Time ordered
198
- # UUIDs can be used for better database index locality of newly inserted
199
- # records, which may have a significant performance benefit compared to random
200
- # data inserts.
201
- #
202
- # The result contains 74 random bits (9.25 random bytes).
203
- #
204
- # Note that this method cannot be made reproducible because its output
205
- # includes not only random bits but also timestamp.
206
- #
207
- # See RFC9562[https://www.rfc-editor.org/rfc/rfc9562] for details of UUIDv7.
208
- #
209
- # ==== Monotonicity
210
- #
211
- # UUIDv7 has millisecond precision by default, so multiple UUIDs created
212
- # within the same millisecond are not issued in monotonically increasing
213
- # order. To create UUIDs that are time-ordered with sub-millisecond
214
- # precision, up to 12 bits of additional timestamp may added with
215
- # +extra_timestamp_bits+. The extra timestamp precision comes at the expense
216
- # of random bits. Setting <tt>extra_timestamp_bits: 12</tt> provides ~244ns
217
- # of precision, but only 62 random bits (7.75 random bytes).
218
- #
219
- # prng = Random.new
220
- # Array.new(4) { prng.uuid_v7(extra_timestamp_bits: 12) }
221
- # # =>
222
- # ["0188d4c7-13da-74f9-8b53-22a786ffdd5a",
223
- # "0188d4c7-13da-753b-83a5-7fb9b2afaeea",
224
- # "0188d4c7-13da-754a-88ea-ac0baeedd8db",
225
- # "0188d4c7-13da-7557-83e1-7cad9cda0d8d"]
226
- # # |<--- sorted --->| |<-- random --->|
227
- #
228
- # Array.new(4) { prng.uuid_v7(extra_timestamp_bits: 8) }
229
- # # =>
230
- # ["0188d4c7-3333-7a95-850a-de6edb858f7e",
231
- # "0188d4c7-3333-7ae8-842e-bc3a8b7d0cf9", # <- out of order
232
- # "0188d4c7-3333-7ae2-995a-9f135dc44ead", # <- out of order
233
- # "0188d4c7-3333-7af9-87c3-8f612edac82e"]
234
- # # |<--- sorted -->||<---- random --->|
235
- #
236
- # Any rollbacks of the system clock will break monotonicity. UUIDv7 is based
237
- # on UTC, which excludes leap seconds and can rollback the clock. To avoid
238
- # this, the system clock can synchronize with an NTP server configured to use
239
- # a "leap smear" approach. NTP or PTP will also be needed to synchronize
240
- # across distributed nodes.
241
- #
242
- # Counters and other mechanisms for stronger guarantees of monotonicity are
243
- # not implemented. Applications with stricter requirements should follow
244
- # {Section 6.2}[https://www.rfc-editor.org/rfc/rfc9562.html#name-monotonicity-and-counters]
245
- # of the specification.
246
- #
247
- def uuid_v7(extra_timestamp_bits: 0)
248
- case (extra_timestamp_bits = Integer(extra_timestamp_bits))
249
- when 0 # min timestamp precision
250
- ms = Process.clock_gettime(Process::CLOCK_REALTIME, :millisecond)
251
- rand = random_bytes(10)
252
- rand.setbyte(0, rand.getbyte(0) & 0x0f | 0x70) # version
253
- rand.setbyte(2, rand.getbyte(2) & 0x3f | 0x80) # variant
254
- "%08x-%04x-%s" % [
255
- (ms & 0x0000_ffff_ffff_0000) >> 16,
256
- (ms & 0x0000_0000_0000_ffff),
257
- rand.unpack("H4H4H12").join("-")
258
- ]
259
-
260
- when 12 # max timestamp precision
261
- ms, ns = Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond)
262
- .divmod(1_000_000)
263
- extra_bits = ns * 4096 / 1_000_000
264
- rand = random_bytes(8)
265
- rand.setbyte(0, rand.getbyte(0) & 0x3f | 0x80) # variant
266
- "%08x-%04x-7%03x-%s" % [
267
- (ms & 0x0000_ffff_ffff_0000) >> 16,
268
- (ms & 0x0000_0000_0000_ffff),
269
- extra_bits,
270
- rand.unpack("H4H12").join("-")
271
- ]
272
-
273
- when (0..12) # the generic version is slower than the special cases above
274
- rand_a, rand_b1, rand_b2, rand_b3 = random_bytes(10).unpack("nnnN")
275
- rand_mask_bits = 12 - extra_timestamp_bits
276
- ms, ns = Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond)
277
- .divmod(1_000_000)
278
- "%08x-%04x-%04x-%04x-%04x%08x" % [
279
- (ms & 0x0000_ffff_ffff_0000) >> 16,
280
- (ms & 0x0000_0000_0000_ffff),
281
- 0x7000 |
282
- ((ns * (1 << extra_timestamp_bits) / 1_000_000) << rand_mask_bits) |
283
- rand_a & ((1 << rand_mask_bits) - 1),
284
- 0x8000 | (rand_b1 & 0x3fff),
285
- rand_b2,
286
- rand_b3
287
- ]
288
-
289
- else
290
- raise ArgumentError, "extra_timestamp_bits must be in 0..12"
291
- end
292
- end
293
-
294
- # Internal interface to Random; Generate random data _n_ bytes.
295
- private def gen_random(n)
296
- self.bytes(n)
297
- end
298
-
299
- # Generate a string that randomly draws from a
300
- # source array of characters.
301
- #
302
- # The argument _source_ specifies the array of characters from which
303
- # to generate the string.
304
- # The argument _n_ specifies the length, in characters, of the string to be
305
- # generated.
306
- #
307
- # The result may contain whatever characters are in the source array.
308
- #
309
- # require 'random/formatter'
310
- #
311
- # prng.choose([*'l'..'r'], 16) #=> "lmrqpoonmmlqlron"
312
- # prng.choose([*'0'..'9'], 5) #=> "27309"
313
- private def choose(source, n)
314
- size = source.size
315
- m = 1
316
- limit = size
317
- while limit * size <= 0x100000000
318
- limit *= size
319
- m += 1
320
- end
321
- result = ''.dup
322
- while m <= n
323
- rs = random_number(limit)
324
- is = rs.digits(size)
325
- (m-is.length).times { is << 0 }
326
- result << source.values_at(*is).join('')
327
- n -= m
328
- end
329
- if 0 < n
330
- rs = random_number(limit)
331
- is = rs.digits(size)
332
- if is.length < n
333
- (n-is.length).times { is << 0 }
334
- else
335
- is.pop while n < is.length
336
- end
337
- result.concat source.values_at(*is).join('')
338
- end
339
- result
340
- end
341
-
342
- # The default character list for #alphanumeric.
343
- ALPHANUMERIC = [*'A'..'Z', *'a'..'z', *'0'..'9']
344
-
345
- # Generate a random alphanumeric string.
346
- #
347
- # The argument _n_ specifies the length, in characters, of the alphanumeric
348
- # string to be generated.
349
- # The argument _chars_ specifies the character list which the result is
350
- # consist of.
351
- #
352
- # If _n_ is not specified or is nil, 16 is assumed.
353
- # It may be larger in the future.
354
- #
355
- # The result may contain A-Z, a-z and 0-9, unless _chars_ is specified.
356
- #
357
- # require 'random/formatter'
358
- #
359
- # Random.alphanumeric #=> "2BuBuLf3WfSKyQbR"
360
- # # or
361
- # prng = Random.new
362
- # prng.alphanumeric(10) #=> "i6K93NdqiH"
363
- #
364
- # Random.alphanumeric(4, chars: [*"0".."9"]) #=> "2952"
365
- # # or
366
- # prng = Random.new
367
- # prng.alphanumeric(10, chars: [*"!".."/"]) #=> ",.,++%/''."
368
- def alphanumeric(n = nil, chars: ALPHANUMERIC)
369
- n = 16 if n.nil?
370
- choose(chars, n)
371
- end
372
- end