Class: Crypt::ISAAC

Inherits:
Object
  • Object
show all
Defined in:
lib/crypt/isaac.rb

Overview

ISAAC is a fast, strong random number generator. Details on the algorithm can be found here: burtleburtle.net/bob/rand/isaac.html This provides a consistent and capable algorithm for producing independent streams of quality random numbers.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(seed = true) ⇒ ISAAC

When a Crypt::ISAAC object is created, it needs to be seeded for random number generation. If the system has a /dev/urandom file, that will be used to do the seeding by default. If false is explictly passed when creating the object, it will instead use /dev/random to generate its seeds. Be warned that this may make for SLOW initialization. If the requested source (/dev/urandom or /dev/random) do not exist, the system will fall back to a simplistic initialization mechanism using the builtin Mersenne Twister PRNG.



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/crypt/isaac.rb', line 23

def initialize(seed = true)
  @mm = []

  if Integer === seed || Random === seed
    self.srand(seed)
  else
    @seed = seed
    rnd_source = ( ( seed == true ) || ( seed == false ) ) ?
      ( seed ? '/dev/urandom' : '/dev/random' ) :
      seed
    if (FileTest.exist? rnd_source)
      @randrsl = []
      File.open(rnd_source,'r') do |r|
        256.times do |t|
          z = r.read(4)
          x = z.unpack('V')[0]
          @randrsl[t] = x
        end
      end
      randinit(true)
    else
      raise "Entropy source (#{rnd_source}) doesn't exist. The ISAAC algorithm can not be seeded."
    end
  end
end

Instance Attribute Details

#aaObject

Returns the value of attribute aa.



11
12
13
# File 'lib/crypt/isaac.rb', line 11

def aa
  @aa
end

#bbObject

Returns the value of attribute bb.



11
12
13
# File 'lib/crypt/isaac.rb', line 11

def bb
  @bb
end

#ccObject

Returns the value of attribute cc.



11
12
13
# File 'lib/crypt/isaac.rb', line 11

def cc
  @cc
end

#mmObject

Returns the value of attribute mm.



11
12
13
# File 'lib/crypt/isaac.rb', line 11

def mm
  @mm
end

#randcntObject

Returns the value of attribute randcnt.



10
11
12
# File 'lib/crypt/isaac.rb', line 10

def randcnt
  @randcnt
end

#randrslObject

Returns the value of attribute randrsl.



10
11
12
# File 'lib/crypt/isaac.rb', line 10

def randrsl
  @randrsl
end

Instance Method Details

#==(gen) ⇒ Object



93
94
95
# File 'lib/crypt/isaac.rb', line 93

def ==(gen)
  self.state == gen.state
end

#bytes(size) ⇒ Object



97
98
99
100
101
102
103
104
105
106
# File 'lib/crypt/isaac.rb', line 97

def bytes(size)
  buffer = ""
  ( size / 4 ).times { buffer << [rand(4294967295)].pack("L").unpack("aaaa").join }

  if size % 4 != 0
    buffer << [rand(4294967295)].pack("L").unpack("aaaa")[0..(size % 4 - 1)].join
  end

  buffer
end

#isaacObject



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/crypt/isaac.rb', line 108

def isaac
  i = 0
  x = 0
  y = 0

  @cc += 1
  @bb += @cc
  @bb & 0xffffffff

  while (i < 256) do 
    x = @mm[i]
    @aa = (@mm[(i + 128) & 255] + (@aa^(@aa << 13)) ) & 0xffffffff
    @mm[i] = y = (@mm[(x>>2)&255] + @aa + @bb ) & 0xffffffff
    @randrsl[i] = @bb = (@mm[(y>>10)&255] + x ) & 0xffffffff
    i += 1

    x = @mm[i]
    @aa = (@mm[(i+128)&255] + (@aa^(0x03ffffff & (@aa >> 6))) ) & 0xffffffff
    @mm[i] = y = (@mm[(x>>2)&255] + @aa + @bb ) & 0xffffffff
    @randrsl[i] = @bb = (@mm[(y>>10)&255] + x ) & 0xffffffff
    i += 1

    x = @mm[i]
    @aa = (@mm[(i + 128)&255] + (@aa^(@aa << 2)) ) & 0xffffffff
    @mm[i] = y = (@mm[(x>>2)&255] + @aa + @bb ) & 0xffffffff
    @randrsl[i] = @bb = (@mm[(y>>10)&255] + x ) & 0xffffffff
    i += 1

    x = @mm[i]
    @aa = (@mm[(i+128)&255] + (@aa^(0x0000ffff & (@aa >> 16))) ) & 0xffffffff
    @mm[i] = y = (@mm[(x>>2)&255] + @aa + @bb ) & 0xffffffff
    @randrsl[i] = @bb = (@mm[(y>>10)&255] + x ) & 0xffffffff
    i += 1
  end
end

#rand(arg = nil) ⇒ Object

Works just like the standard rand() function. If called with an integer argument, rand() will return positive random number in the range of 0 to (argument - 1). If called without an integer argument, rand() returns a positive floating point number less than 1. If called with a Range, returns a number that is in the range.



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/crypt/isaac.rb', line 68

def rand(arg = nil)
  if (@randcnt == 1)
    isaac
    @randcnt = 256
  end
  @randcnt -= 1
  if arg.nil?
    ( @randrsl[@randcnt] / 536870912.0 ) % 1
  elsif Integer === arg || Float === arg
    @randrsl[@randcnt] % arg
  elsif Range === arg
    arg.min + @randrsl[@randcnt] % (arg.max - arg.min)
  else
    @randrsl[@randcnt] % Integer(arg)
  end
end

#randinit(flag) ⇒ Object



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/crypt/isaac.rb', line 144

def randinit(flag)
  i = 0
  a = 0
  b = 0
  c = 0
  d = 0
  e = 0
  f = 0
  g = 0
  @aa = @bb = @cc = 0
  a = b = c = d = e = f = g = h = 0x9e3779b9

  while (i < 4) do
    a ^= b<<1; d += a; b += c
    b ^= 0x3fffffff & (c>>2); e += b; c += d
    c ^= d << 8; f += c; d += e
    d ^= 0x0000ffff & (e >> 16); g += d; e += f
    e ^= f << 10; h += e; f += g
    f ^= 0x0fffffff & (g >> 4); a += f; g += h
    g ^= h << 8; b += g; h += a
    h ^= 0x007fffff & (a >> 9); c += h; a += b
    i += 1
  end

  i = 0
  while (i < 256) do
    if (flag)
      a+=@randrsl[i  ].to_i; b+=@randrsl[i+1].to_i;
      c+=@randrsl[i+2]; d+=@randrsl[i+3];
      e+=@randrsl[i+4]; f+=@randrsl[i+5];
      g+=@randrsl[i+6]; h+=@randrsl[i+7];
    end

    a^=b<<11; d+=a; b+=c;
    b^=0x3fffffff & (c>>2);  e+=b; c+=d;
    c^=d<<8;  f+=c; d+=e;
    d^=0x0000ffff & (e>>16); g+=d; e+=f;
    e^=f<<10; h+=e; f+=g;
    f^=0x0fffffff & (g>>4);  a+=f; g+=h;
    g^=h<<8;  b+=g; h+=a;
    h^=0x007fffff & (a>>9);  c+=h; a+=b;
    @mm[i]=a;@mm[i+1]=b; @mm[i+2]=c; @mm[i+3]=d;
    @mm[i+4]=e; @mm[i+5]=f; @mm[i+6]=g; @mm[i+7]=h;
    i += 8
  end

  if flag
    i = 0
    while (i < 256)
      a+=@mm[i  ]; b+=@mm[i+1]; c+=@mm[i+2]; d+=@mm[i+3];
      e+=@mm[i+4]; f+=@mm[i+5]; g+=@mm[i+6]; h+=@mm[i+7];
      a^=b<<11; d+=a; b+=c;
      b^=0x3fffffff & (c>>2);  e+=b; c+=d;
      c^=d<<8;  f+=c; d+=e;
      d^=0x0000ffff & (e>>16); g+=d; e+=f;
      e^=f<<10; h+=e; f+=g;
      f^=0x0fffffff & (g>>4);  a+=f; g+=h;
      g^=h<<8;  b+=g; h+=a;
      h^=0x007fffff & (a>>9);  c+=h; a+=b;
      @mm[i  ]=a; @mm[i+1]=b; @mm[i+2]=c; @mm[i+3]=d;
      @mm[i+4]=e; @mm[i+5]=f; @mm[i+6]=g; @mm[i+7]=h;
      i += 8
    end
  end

  isaac()
  @randcnt=256;        # /* prepare to use the first set of results */
end

#seedObject



85
86
87
# File 'lib/crypt/isaac.rb', line 85

def seed
  Random === @seed ? @seed.seed : @seed
end

#srand(seed) ⇒ Object

If seeded with an integer, use that to seed a standard Ruby Mersenne Twister PRNG, and then use that to generate seed value for ISAAC. This is mostly useful for producing repeated, deterministic results, which may be needed for testing.



52
53
54
55
56
57
58
59
60
# File 'lib/crypt/isaac.rb', line 52

def srand(seed)
  @seed = seed
  @randrsl = []
  seed_prng = ( Random === seed ) ? seed : Random.new(seed)
  256.times do |t|
    @randrsl[t] = seed_prng.rand(4294967295)
  end
  randinit(true)
end

#stateObject



89
90
91
# File 'lib/crypt/isaac.rb', line 89

def state
  @randrsl + [@randcnt]
end