crypt-isaac 1.0.0 → 1.1.0

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
  SHA1:
3
- metadata.gz: eeff4541c06d7907ba4eb56a0784cb908fd1c039
4
- data.tar.gz: fc61b38f750e2e3a805580b424aafc057aff01bb
3
+ metadata.gz: 32721d59c293f47c17f554ec6d614a3dd6d9c223
4
+ data.tar.gz: 3b0bf2597824aa9e730ee2d314e96fbe028d489f
5
5
  SHA512:
6
- metadata.gz: 7181904366b11ae6ce0be343a720e0e0e56f847579675e0ef00680888e9dce6413dadaff21e0d3cd297b4b591cc718dc77d00fdef4e41a135f8be816138b2610
7
- data.tar.gz: b937c669132250c3cbc4d7fc4cfbadbfebfed56211f6a252fb4d0dbb088e94713f9f30c9bb6121318060dd72e6a3d567754f7e95fde6dff1d3f3bb55f43e6b23
6
+ metadata.gz: b43a59b1dffaf4f1326081fe7b4f935ad4ecb478c17e23d8f637d8e03f2d9a7166964f750eb93953e298ecb0d57754d0ad98d2524c316f0d20d09b2d0d66e7db
7
+ data.tar.gz: 9313d7ac00934839290713cef9b2c6a5cb13da38b1c21eaf7a0bfc5f4838860777516c8416f086fb7ec0d05c83d99c75f60cd27b3bd5319f47c11137f2d3b803
data/README.md CHANGED
@@ -4,11 +4,25 @@ ISAAC is a cryptographically secure PRNG for generating high quality random numb
4
4
 
5
5
  http://burtleburtle.net/bob/rand/isaac.html
6
6
 
7
- This is a pure Ruby implementation of the algorithm, but it is reasonably fast.
7
+ This library combines a pure Ruby implementation with a C implementation bound to ruby as an extension. The C extension implementation currently runs many times faster than the pure ruby implementation.
8
8
 
9
- When originally written, running on Ruby 1.8.2 under a venerable 800Mhz PIII Linux system, it could do 15000 to 16000 numbers per second. On Ruby 2.2.3, running on a basic Digital Ocean VM, it can generate almost a million random integers or floats per second.
9
+ When originally written, running on Ruby 1.8.2 under a venerable 800Mhz PIII Linux system, it could do 15000 to 16000 numbers per second. On Ruby 2.3.1, testing via an Ubuntu shell session on a Windows 10 system running a 2.3Ghz Intel i5 processor, the library will generate over ten million floats per second, or almost nine million integers per second:
10
10
 
11
- Ruby uses the Mersenne Twister as its PRNG, and while this algorithm is a fast PRNG that produces highly random numbers with good stastical properties, it is not cryptographically strong. ISAAC is very fast, also has good statistical properties, and is cryptographically strong.
11
+ ```
12
+ Benchmark integer prng generation
13
+ user system total real
14
+ 1.130000 0.000000 1.130000 ( 1.138785)
15
+ 10000000 numbers generated in 1.1393164 seconds; 8777193 per second
16
+
17
+ Benchmark float prng generation
18
+ user system total real
19
+ 0.950000 0.000000 0.950000 ( 0.967220)
20
+ 10000000 numbers generated in 0.967398 seconds; 10337007 per second
21
+ ```
22
+
23
+ Ruby uses the Mersenne Twister as its PRNG. This algorithm is used by many languages because it is relatively fast, and has a long period. It, however, is not cryptographically strong; observing a window of as few as 624 generated values is enough to establish the state of the generator and to predict all future numbers. Nor is it well suited to Monte Carlo type simulations unless the seeds are quite different (generators with similar keys tend to produce number sequences that are the same for quite a long time before diverging), and the generators are ran for a while to ensure strong divergence.
24
+
25
+ ISAAC is very fast. This implementation is currently very comparable to Random's performance for both floats and for integers. ISAAC has strong statistical properties, like the Mersenne Twister, but it is also cryptographically strong, and different generators produce completely unique streams of numbers, even if seeded with similar seeds (though the seed size is substantial, so good seeding should make it difficult for two generators to be similarly seeded).
12
26
 
13
27
  ## Installation
14
28
 
@@ -31,9 +45,35 @@ Or install it yourself as:
31
45
  ```ruby
32
46
  require 'crypt/isaac'
33
47
 
34
- rng = Crypt::ISAAC.new # New ISAAC object, seeded using /dev/urandom or /dev/random, if available.
48
+ rng = Crypt::ISAAC.new
49
+ ```
50
+ ISAAC will seed itself, by default, using /dev/urandom, if the device exists on the system being used. This is the same as:
51
+
52
+ ```ruby
53
+ rng = Crypt::ISAAC.new( true )
54
+ ```
55
+
56
+ If one wants to use /dev/random instead (though there is really no reason to do so), pass false instead:
57
+
58
+ ```ruby
59
+ rng = Crypt::ISAAC.new( false )
60
+ ```
61
+
62
+ If /dev/urandom is not available, or nil is specifically passed, then Crypt::ISAAC will use Crypt::Xorshift, if it is installed, or a bundled microimplementation of Crypt::Xorshift64Star to generate a series of high quality pseudorandom numbers which will in turn be used to seed the ISAAC generator, as it produces better streams of numbers than the Mersenne Twister, in a very simple implementation.
63
+
64
+ ```ruby
65
+ rng = Crypt::ISAAC.new( nil )
66
+ ```
67
+
68
+ Finally, ISAAC may be seeded deterministically by passing an integer seed into it. Any ISAAC generator seeded with the same value will produce the same sequence of random numbers.
69
+
70
+ ```ruby
35
71
  rng = Crypt::ISAAC.new(17773845992) # New ISAAC object, seeded from a deterministic point.
72
+ ```
73
+
74
+ TODO: One should be able to seed an ISAAC PRNG with an array of seed values, as well.
36
75
 
76
+ ```ruby
37
77
  r1 = rng.rand() # returns a floating point number between 0 and 1
38
78
  r2 = rng.rand(10.57) # returns a floating point number between 0 and 10.57
39
79
  r2 = rnd.rand(1000) # returns an integer between 0 and 999
@@ -43,6 +83,8 @@ noise = rng.bytes(1024) # return a 1k string of random bytes
43
83
 
44
84
  Crypt::ISAAC should provide the same API as the Ruby 2.2 version of Random.
45
85
 
86
+ This implementation returns 32 bit values. TODO is to add support for the 64 bit version of ISAAC.
87
+
46
88
  Enjoy it. Let me know if you find anything that can be improved or that
47
89
  needs to be fixed.
48
90
 
data/Rakefile CHANGED
@@ -1,5 +1,14 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rake/testtask"
3
+ require "rake/extensiontask"
4
+
5
+ Rake::ExtensionTask.new "isaac" do |ext|
6
+ ext.lib_dir = "lib/crypt/"
7
+ end
8
+
9
+ Rake::ExtensionTask.new "xorshift" do |ext|
10
+ ext.lib_dir = "lib/crypt/xorshift"
11
+ end
3
12
 
4
13
  Rake::TestTask.new(:test) do |t|
5
14
  t.libs << "test"
data/crypt-isaac.gemspec CHANGED
@@ -9,8 +9,8 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ["Kirk Haines"]
10
10
  spec.email = ["wyhaines@gmail.com"]
11
11
 
12
- spec.summary = %q{An implementation of the ISAAC PRNG}
13
- spec.description = %q{ISAAC is a cryptgraphically secure pseudorandom number generator. This is a Ruby implementation of the algorithm.}
12
+ spec.summary = %q{An implementation of the fast, cryptographically secure ISAAC PRNG}
13
+ spec.description = %q{ISAAC is a fast, cryptographically secure pseudo random number generator with strong statistical properties. This gem provides both a pure Ruby and a C extension based implementation which conforms to the Ruby 2 api for Random, with some enhancements. So, you should be able to use it as a drop in replacement for Ruby's (Mersenne Twister based) PRNG. }
14
14
  spec.homepage = "http://github.com/wyhaines/crypt-isaac"
15
15
  spec.license = "MIT"
16
16
 
@@ -25,6 +25,7 @@ Gem::Specification.new do |spec|
25
25
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
26
  spec.bindir = "exe"
27
27
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
+ spec.extensions = %w[ext/crypt/isaac/xorshift/extconf.rb ext/crypt/isaac/extconf.rb]
28
29
  spec.require_paths = ["lib"]
29
30
 
30
31
  spec.add_development_dependency "bundler", "~> 1.10"
@@ -0,0 +1,260 @@
1
+
2
+ SHELL = /bin/sh
3
+
4
+ # V=0 quiet, V=1 verbose. other values don't work.
5
+ V = 0
6
+ Q1 = $(V:1=)
7
+ Q = $(Q1:0=@)
8
+ ECHO1 = $(V:1=@:)
9
+ ECHO = $(ECHO1:0=@echo)
10
+ NULLCMD = :
11
+
12
+ #### Start of system configuration section. ####
13
+
14
+ srcdir = .
15
+ topdir = /home/wyhaines/.rvm/rubies/ruby-2.3.1/include/ruby-2.3.0
16
+ hdrdir = $(topdir)
17
+ arch_hdrdir = /home/wyhaines/.rvm/rubies/ruby-2.3.1/include/ruby-2.3.0/x86_64-linux
18
+ PATH_SEPARATOR = :
19
+ VPATH = $(srcdir):$(arch_hdrdir)/ruby:$(hdrdir)/ruby
20
+ prefix = $(DESTDIR)/home/wyhaines/.rvm/rubies/ruby-2.3.1
21
+ rubysitearchprefix = $(rubylibprefix)/$(sitearch)
22
+ rubyarchprefix = $(rubylibprefix)/$(arch)
23
+ rubylibprefix = $(libdir)/$(RUBY_BASE_NAME)
24
+ exec_prefix = $(prefix)
25
+ vendorarchhdrdir = $(vendorhdrdir)/$(sitearch)
26
+ sitearchhdrdir = $(sitehdrdir)/$(sitearch)
27
+ rubyarchhdrdir = $(rubyhdrdir)/$(arch)
28
+ vendorhdrdir = $(rubyhdrdir)/vendor_ruby
29
+ sitehdrdir = $(rubyhdrdir)/site_ruby
30
+ rubyhdrdir = $(includedir)/$(RUBY_VERSION_NAME)
31
+ vendorarchdir = $(vendorlibdir)/$(sitearch)
32
+ vendorlibdir = $(vendordir)/$(ruby_version)
33
+ vendordir = $(rubylibprefix)/vendor_ruby
34
+ sitearchdir = $(sitelibdir)/$(sitearch)
35
+ sitelibdir = $(sitedir)/$(ruby_version)
36
+ sitedir = $(rubylibprefix)/site_ruby
37
+ rubyarchdir = $(rubylibdir)/$(arch)
38
+ rubylibdir = $(rubylibprefix)/$(ruby_version)
39
+ sitearchincludedir = $(includedir)/$(sitearch)
40
+ archincludedir = $(includedir)/$(arch)
41
+ sitearchlibdir = $(libdir)/$(sitearch)
42
+ archlibdir = $(libdir)/$(arch)
43
+ ridir = $(datarootdir)/$(RI_BASE_NAME)
44
+ mandir = $(datarootdir)/man
45
+ localedir = $(datarootdir)/locale
46
+ libdir = $(exec_prefix)/lib
47
+ psdir = $(docdir)
48
+ pdfdir = $(docdir)
49
+ dvidir = $(docdir)
50
+ htmldir = $(docdir)
51
+ infodir = $(datarootdir)/info
52
+ docdir = $(datarootdir)/doc/$(PACKAGE)
53
+ oldincludedir = $(DESTDIR)/usr/include
54
+ includedir = $(prefix)/include
55
+ localstatedir = $(prefix)/var
56
+ sharedstatedir = $(prefix)/com
57
+ sysconfdir = $(DESTDIR)/etc
58
+ datadir = $(datarootdir)
59
+ datarootdir = $(prefix)/share
60
+ libexecdir = $(exec_prefix)/libexec
61
+ sbindir = $(exec_prefix)/sbin
62
+ bindir = $(exec_prefix)/bin
63
+ archdir = $(rubyarchdir)
64
+
65
+
66
+ CC = gcc
67
+ CXX = g++
68
+ LIBRUBY = $(LIBRUBY_SO)
69
+ LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a
70
+ LIBRUBYARG_SHARED = -Wl,-rpath,'$${ORIGIN}/../lib' -Wl,-R'$${ORIGIN}/../lib' -l$(RUBY_SO_NAME)
71
+ LIBRUBYARG_STATIC = -Wl,-rpath,'$${ORIGIN}/../lib' -Wl,-R'$${ORIGIN}/../lib' -l$(RUBY_SO_NAME)-static
72
+ empty =
73
+ OUTFLAG = -o $(empty)
74
+ COUTFLAG = -o $(empty)
75
+
76
+ RUBY_EXTCONF_H =
77
+ cflags = $(optflags) $(debugflags) $(warnflags)
78
+ cxxflags = $(optflags) $(debugflags) $(warnflags)
79
+ optflags = -O3 -fno-fast-math
80
+ debugflags = -ggdb3
81
+ warnflags = -Wall -Wextra -Wno-unused-parameter -Wno-parentheses -Wno-long-long -Wno-missing-field-initializers -Wunused-variable -Wpointer-arith -Wwrite-strings -Wdeclaration-after-statement -Wimplicit-function-declaration -Wdeprecated-declarations -Wno-packed-bitfield-compat
82
+ CCDLFLAGS = -fPIC
83
+ CFLAGS = $(CCDLFLAGS) $(cflags) -fPIC $(ARCH_FLAG)
84
+ INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir)
85
+ DEFS =
86
+ CPPFLAGS = $(DEFS) $(cppflags)
87
+ CXXFLAGS = $(CCDLFLAGS) $(cxxflags) $(ARCH_FLAG)
88
+ ldflags = -L. -fstack-protector -rdynamic -Wl,-export-dynamic
89
+ dldflags =
90
+ ARCH_FLAG =
91
+ DLDFLAGS = $(ldflags) $(dldflags) $(ARCH_FLAG)
92
+ LDSHARED = $(CC) -shared
93
+ LDSHAREDXX = $(CXX) -shared
94
+ AR = ar
95
+ EXEEXT =
96
+
97
+ RUBY_INSTALL_NAME = $(RUBY_BASE_NAME)
98
+ RUBY_SO_NAME = ruby
99
+ RUBYW_INSTALL_NAME =
100
+ RUBY_VERSION_NAME = $(RUBY_BASE_NAME)-$(ruby_version)
101
+ RUBYW_BASE_NAME = rubyw
102
+ RUBY_BASE_NAME = ruby
103
+
104
+ arch = x86_64-linux
105
+ sitearch = $(arch)
106
+ ruby_version = 2.3.0
107
+ ruby = $(bindir)/$(RUBY_BASE_NAME)
108
+ RUBY = $(ruby)
109
+ ruby_headers = $(hdrdir)/ruby.h $(hdrdir)/ruby/ruby.h $(hdrdir)/ruby/defines.h $(hdrdir)/ruby/missing.h $(hdrdir)/ruby/intern.h $(hdrdir)/ruby/st.h $(hdrdir)/ruby/subst.h $(arch_hdrdir)/ruby/config.h
110
+
111
+ RM = rm -f
112
+ RM_RF = $(RUBY) -run -e rm -- -rf
113
+ RMDIRS = rmdir --ignore-fail-on-non-empty -p
114
+ MAKEDIRS = /bin/mkdir -p
115
+ INSTALL = /usr/bin/install
116
+ INSTALL_PROG = $(INSTALL) -m 0755
117
+ INSTALL_DATA = $(INSTALL) -m 644
118
+ COPY = cp
119
+ TOUCH = exit >
120
+
121
+ #### End of system configuration section. ####
122
+
123
+ preload =
124
+
125
+ libpath = . $(libdir)
126
+ LIBPATH = -L. -L$(libdir) -Wl,-R$(libdir)
127
+ DEFFILE =
128
+
129
+ CLEANFILES = mkmf.log
130
+ DISTCLEANFILES =
131
+ DISTCLEANDIRS =
132
+
133
+ extout =
134
+ extout_prefix =
135
+ target_prefix = /crypt/isaac
136
+ LOCAL_LIBS =
137
+ LIBS = $(LIBRUBYARG_SHARED) -lpthread -ldl -lcrypt -lm -lc
138
+ ORIG_SRCS = isaac.c rand.c
139
+ SRCS = $(ORIG_SRCS)
140
+ OBJS = isaac.o rand.o
141
+ HDRS = $(srcdir)/rand.h $(srcdir)/standard.h
142
+ TARGET = ext
143
+ TARGET_NAME = ext
144
+ TARGET_ENTRY = Init_$(TARGET_NAME)
145
+ DLLIB = $(TARGET).so
146
+ EXTSTATIC =
147
+ STATIC_LIB =
148
+
149
+ TIMESTAMP_DIR = .
150
+ BINDIR = $(bindir)
151
+ RUBYCOMMONDIR = $(sitedir)$(target_prefix)
152
+ RUBYLIBDIR = $(sitelibdir)$(target_prefix)
153
+ RUBYARCHDIR = $(sitearchdir)$(target_prefix)
154
+ HDRDIR = $(rubyhdrdir)/ruby$(target_prefix)
155
+ ARCHHDRDIR = $(rubyhdrdir)/$(arch)/ruby$(target_prefix)
156
+
157
+ TARGET_SO = $(DLLIB)
158
+ CLEANLIBS = $(TARGET).so
159
+ CLEANOBJS = *.o *.bak
160
+
161
+ all: $(DLLIB)
162
+ static: $(STATIC_LIB) install-rb
163
+ .PHONY: all install static install-so install-rb
164
+ .PHONY: clean clean-so clean-static clean-rb
165
+
166
+ clean-static::
167
+ clean-rb-default::
168
+ clean-rb::
169
+ clean-so::
170
+ clean: clean-so clean-static clean-rb-default clean-rb
171
+ -$(Q)$(RM) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES) .*.time
172
+
173
+ distclean-rb-default::
174
+ distclean-rb::
175
+ distclean-so::
176
+ distclean-static::
177
+ distclean: clean distclean-so distclean-static distclean-rb-default distclean-rb
178
+ -$(Q)$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
179
+ -$(Q)$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES)
180
+ -$(Q)$(RMDIRS) $(DISTCLEANDIRS) 2> /dev/null || true
181
+
182
+ realclean: distclean
183
+ install: install-so install-rb
184
+
185
+ install-so: $(DLLIB) $(TIMESTAMP_DIR)/.RUBYARCHDIR.-.crypt.-.isaac.time
186
+ $(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
187
+ clean-static::
188
+ -$(Q)$(RM) $(STATIC_LIB)
189
+ install-rb: pre-install-rb install-rb-default
190
+ install-rb-default: pre-install-rb-default
191
+ pre-install-rb: Makefile
192
+ pre-install-rb-default: Makefile
193
+ pre-install-rb-default:
194
+ @$(NULLCMD)
195
+ $(TIMESTAMP_DIR)/.RUBYARCHDIR.-.crypt.-.isaac.time:
196
+ $(Q) $(MAKEDIRS) $(@D) $(RUBYARCHDIR)
197
+ $(Q) $(TOUCH) $@
198
+
199
+ site-install: site-install-so site-install-rb
200
+ site-install-so: install-so
201
+ site-install-rb: install-rb
202
+
203
+ .SUFFIXES: .c .m .cc .mm .cxx .cpp .o .S
204
+
205
+ .cc.o:
206
+ $(ECHO) compiling $(<)
207
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
208
+
209
+ .cc.S:
210
+ $(ECHO) translating $(<)
211
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $<
212
+
213
+ .mm.o:
214
+ $(ECHO) compiling $(<)
215
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
216
+
217
+ .mm.S:
218
+ $(ECHO) translating $(<)
219
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $<
220
+
221
+ .cxx.o:
222
+ $(ECHO) compiling $(<)
223
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
224
+
225
+ .cxx.S:
226
+ $(ECHO) translating $(<)
227
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $<
228
+
229
+ .cpp.o:
230
+ $(ECHO) compiling $(<)
231
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
232
+
233
+ .cpp.S:
234
+ $(ECHO) translating $(<)
235
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $<
236
+
237
+ .c.o:
238
+ $(ECHO) compiling $(<)
239
+ $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $<
240
+
241
+ .c.S:
242
+ $(ECHO) translating $(<)
243
+ $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $<
244
+
245
+ .m.o:
246
+ $(ECHO) compiling $(<)
247
+ $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $<
248
+
249
+ .m.S:
250
+ $(ECHO) translating $(<)
251
+ $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $<
252
+
253
+ $(DLLIB): $(OBJS) Makefile
254
+ $(ECHO) linking shared-object crypt/isaac/$(DLLIB)
255
+ -$(Q)$(RM) $(@)
256
+ $(Q) $(LDSHARED) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)
257
+
258
+
259
+
260
+ $(OBJS): $(HDRS) $(ruby_headers)
@@ -0,0 +1,3 @@
1
+ require 'mkmf'
2
+
3
+ create_makefile("crypt/isaac/ext")
@@ -0,0 +1,232 @@
1
+ #include "ruby.h"
2
+ #include "rand.h"
3
+
4
+ static VALUE CryptModule;
5
+ static VALUE ISAACClass;
6
+
7
+ static void ISAAC_free( randctx* ctx ) {
8
+ if ( ctx ) {
9
+ xfree( ctx );
10
+ }
11
+ }
12
+
13
+ static VALUE ISAAC_alloc( VALUE klass ) {
14
+ randctx *ctx;
15
+
16
+ return Data_Make_Struct( klass, randctx, NULL, ISAAC_free, ctx );
17
+ }
18
+
19
+ static VALUE ISAAC_initialize( VALUE self, VALUE args ) {
20
+ VALUE _seed ;
21
+ long len = RARRAY_LEN( args );
22
+ if ( len == 0 ) {
23
+ _seed = Qtrue;
24
+ } else {
25
+ _seed = rb_ary_entry( args, 0 );
26
+ }
27
+
28
+ return rb_funcall( self, rb_intern( "srand" ), 1, _seed );
29
+ }
30
+
31
+ static VALUE ISAAC_srand( VALUE self, VALUE args ) {
32
+ FILE *fh;
33
+ uint32_t num = 0;
34
+ size_t nread;
35
+ VALUE _seed;
36
+ VALUE old_seed;
37
+ VALUE seed_prng;
38
+ VALUE random_argv[1];
39
+ VALUE rnd_source = Qnil;
40
+ int x;
41
+ long len = RARRAY_LEN( args );
42
+ randctx *ctx;
43
+
44
+ if ( len == 0 ) {
45
+ _seed = Qtrue;
46
+ } else {
47
+ _seed = rb_ary_entry( args, 0 );
48
+ }
49
+
50
+ if ( ( _seed == Qtrue ) || ( _seed == Qfalse ) ) {
51
+ rnd_source = ( _seed == Qtrue ) ? rb_str_new2("/dev/urandom") : rb_str_new2("/dev/random");
52
+ }
53
+
54
+ Data_Get_Struct( self, randctx, ctx );
55
+ MEMZERO( ctx, randctx, 1 );
56
+
57
+ if ( ( rnd_source != Qnil ) && ( rb_funcall( rb_mFileTest, rb_intern("exist?"), 1, rnd_source ) == Qtrue ) ) {
58
+ fh = fopen("/dev/urandom","r");
59
+ for ( x = RANDSIZ - 1; x >= 0; x-- ) {
60
+ nread = fread(&num, sizeof(uint32_t), 1, fh);
61
+ if ( nread == 0 ) {
62
+ x++;
63
+ continue;
64
+ }
65
+ ctx->randrsl[x] = num;
66
+ }
67
+ fclose(fh);
68
+ } else {
69
+ if ( rnd_source != Qnil ) {
70
+ _seed = Qnil;
71
+ }
72
+ if ( rb_funcall( _seed, rb_intern( "respond_to?" ), 1, rb_str_new2("rand") ) == Qtrue ) {
73
+ seed_prng = _seed;
74
+ } else {
75
+ random_argv[0] = _seed;
76
+ seed_prng = rb_class_new_instance( 1, random_argv, rb_const_get( rb_const_get( rb_cObject, rb_intern( "Crypt" ) ), rb_intern( "Xorshift64Star" ) ) );
77
+ _seed = rb_funcall( seed_prng, rb_intern( "seed" ), 0 );
78
+ rb_iv_set( self, "@seed_prng", seed_prng );
79
+ }
80
+ for ( x = RANDSIZ - 1; x >= 0; x-- ) {
81
+ ctx->randrsl[x] = FIX2ULONG( rb_funcall( seed_prng, rb_intern( "rand" ), 1, ULONG2NUM(4294967296) ) );
82
+ }
83
+ }
84
+ randinit(ctx, 1);
85
+
86
+ rb_iv_set( self, "@seed", ( ( rnd_source == Qtrue ) || ( rnd_source == Qfalse ) ) ? rnd_source : _seed );
87
+ old_seed = rb_iv_get( self, "@seed");
88
+
89
+ return old_seed;
90
+ }
91
+
92
+ static VALUE ISAAC_rand( VALUE self, VALUE args ) {
93
+ uint32_t limit;
94
+ long len = RARRAY_LEN( args );
95
+ VALUE arg;
96
+ short arg_is_a_range = 0;
97
+ ID id_min;
98
+ VALUE val_min = 0;
99
+ ID id_max;
100
+ randctx *ctx;
101
+
102
+ arg = rb_ary_entry( args, 0 );
103
+
104
+ if ( len == 0 ) {
105
+ limit = 0;
106
+ } else if ( rb_obj_class( arg ) == rb_cRange ) {
107
+ arg_is_a_range = 1;
108
+ id_min = rb_intern("min");
109
+ id_max = rb_intern("max");
110
+ val_min = rb_funcall( arg, id_min, 0 );
111
+ limit = NUM2ULONG( rb_funcall( arg, id_max, 0 ) ) - NUM2ULONG( val_min );
112
+ } else {
113
+ limit = NUM2ULONG( rb_ary_entry( args, 0 ) );
114
+ }
115
+
116
+ Data_Get_Struct( self, randctx, ctx );
117
+ if ( !ctx->randcnt-- ) {
118
+ isaac( ctx );
119
+ ctx->randcnt = RANDSIZ - 1;
120
+ }
121
+
122
+ if ( limit == 0 ) {
123
+ return rb_float_new( ctx->randrsl[ctx->randcnt] / 4294967296.0 );
124
+ } else if ( arg_is_a_range ) {
125
+ return ULONG2NUM( NUM2ULONG( val_min ) + ( ctx->randrsl[ctx->randcnt] % limit ) );
126
+ } else {
127
+ return ULONG2NUM( ctx->randrsl[ctx->randcnt] % limit );
128
+ }
129
+ }
130
+
131
+ static VALUE ISAAC_marshal_dump( VALUE self ) {
132
+ randctx *ctx;
133
+ int i;
134
+ int ary_size = sizeof( randctx ) / sizeof( ub4 );
135
+ VALUE ary;
136
+
137
+ Data_Get_Struct( self, randctx, ctx );
138
+
139
+ ary = rb_ary_new2( ary_size );
140
+ for ( i = 0; i < ary_size; i++ ) {
141
+ rb_ary_push( ary, ULONG2NUM(((ub4 *)ctx)[i]));
142
+ }
143
+
144
+ return ary;
145
+ }
146
+
147
+ static VALUE ISAAC_marshal_load( VALUE self, VALUE ary ) {
148
+ randctx *ctx;
149
+ int i;
150
+ int ary_size = sizeof( randctx ) / sizeof( ub4 );
151
+
152
+ Data_Get_Struct( self, randctx, ctx );
153
+
154
+ if ( RARRAY_LEN(ary) != ary_size )
155
+ rb_raise( rb_eArgError, "bad length in loaded ISAAC data" );
156
+
157
+ for ( i = 0; i < ary_size; i++ ) {
158
+ ((ub4 *)ctx)[i] = NUM2ULONG(RARRAY_PTR(ary)[i] );
159
+ }
160
+
161
+ return self;
162
+ }
163
+
164
+ static VALUE ISAAC_seed( VALUE self ) {
165
+ return rb_iv_get( self, "@seed");
166
+ }
167
+
168
+ static VALUE ISAAC_new_seed( VALUE self ) {
169
+ return rb_funcall( rb_iv_get( self, "@seed_prng" ), rb_intern("new_seed"), 0 );
170
+ }
171
+
172
+ int compare_ctx( randctx* ctx1, randctx* ctx2 ) {
173
+ int x;
174
+ for ( x = RANDSIZ - 1; x >= 0; x-- ) {
175
+ if ( ctx1->randrsl[x] != ctx2->randrsl[x] ) return 0;
176
+ }
177
+
178
+ return 1;
179
+ }
180
+
181
+ static VALUE ISAAC_eq( VALUE self, VALUE v ) {
182
+ randctx* ctx1;
183
+ randctx* ctx2;
184
+
185
+ Data_Get_Struct( self, randctx, ctx1 );
186
+ Data_Get_Struct( v, randctx, ctx2 );
187
+
188
+ return ( ( rb_obj_classname( self ) == rb_obj_classname( v ) ) && ( compare_ctx( ctx1, ctx2 ) ) ) ? Qtrue : Qfalse ;
189
+ }
190
+
191
+ static VALUE ISAAC_bytes( VALUE self, VALUE count ) {
192
+ uint64_t bytes = NUM2ULL( count );
193
+ char buf[ bytes + 5 ];
194
+ uint64_t i = 0;
195
+ randctx* ctx;
196
+
197
+ Data_Get_Struct( self, randctx, ctx );
198
+ for( i = 0; i < bytes; i = i + 4 ) {
199
+
200
+ if ( !ctx->randcnt-- ) {
201
+ isaac( ctx );
202
+ ctx->randcnt = RANDSIZ - 1;
203
+ }
204
+
205
+ // snprintf( &buf[ i ], 5, "%lu", ctx->randrsl[ctx->randcnt] );
206
+ buf[ i ] = ctx->randrsl[ctx->randcnt];
207
+ buf[ i + 1 ] = ctx->randrsl[ctx->randcnt] >> 8;
208
+ buf[ i + 2 ] = ctx->randrsl[ctx->randcnt] >> 16;
209
+ buf[ i + 3 ] = ctx->randrsl[ctx->randcnt] >> 24;
210
+ }
211
+
212
+ return rb_str_new( buf, bytes );
213
+ }
214
+
215
+ void Init_ext() {
216
+ CryptModule = rb_define_module( "Crypt" );
217
+ ISAACClass = rb_define_class_under( CryptModule, "ISAAC", rb_cObject );
218
+
219
+ rb_define_alloc_func( ISAACClass, ISAAC_alloc );
220
+ rb_define_method( ISAACClass, "initialize", ISAAC_initialize, -2 );
221
+ rb_define_method( ISAACClass, "srand", ISAAC_srand, -2 );
222
+ rb_define_method( ISAACClass, "rand", ISAAC_rand, -2 );
223
+ rb_define_method( ISAACClass, "seed", ISAAC_seed, 0 );
224
+ rb_define_method( ISAACClass, "new_seed", ISAAC_new_seed, 0 );
225
+ rb_define_method( ISAACClass, "==", ISAAC_eq, 1 );
226
+ rb_define_method( ISAACClass, "bytes", ISAAC_bytes, 1 );
227
+ rb_define_method( ISAACClass, "marshal_dump", ISAAC_marshal_dump, 0 );
228
+ rb_define_method( ISAACClass, "marshal_load", ISAAC_marshal_load, 1 );
229
+
230
+ rb_const_set( ISAACClass, rb_intern("RANDSIZ"), ULONG2NUM(RANDSIZ) );
231
+ rb_require( "crypt/isaac/version.rb" );
232
+ }