-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathSecureRandom.java
296 lines (263 loc) · 9 KB
/
SecureRandom.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
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
143
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
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
/*
* @(#)SecureRandom.java 1.19 98/08/06
*
* Copyright 1995-1998 by Sun Microsystems, Inc.,
* 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
* All rights reserved.
*
* This software is the confidential and proprietary information
* of Sun Microsystems, Inc. ("Confidential Information"). You
* shall not disclose such Confidential Information and shall use
* it only in accordance with the terms of the license agreement
* you entered into with Sun.
*/
package java.security;
import java.util.*;
/**
* <p>This class provides a crytpographically strong pseudo-random number
* generator based on the SHA-1 hash algorithm.
*
* <p>The calls inherited from Random will be implemented in terms of the
* strengthened functionality.
*
* @see java.util.Random
*
* @version 1.2 96/09/15
* @author Benjamin Renaud
* @author Josh Bloch
* @author Gadi Guy
*/
public class SecureRandom extends Random {
/**
* Used by the empty constructor to seed the SecureRandom under
* construction.
*/
private static SecureRandom seeder;
private static final int DIGEST_SIZE = 20;
private transient MessageDigest digest;
private byte[] state;
private byte[] remainder;
private int remCount;
/**
* This empty constructor automatically seeds the generator. We attempt
* to provide sufficient seed bytes to completely randomize the internal
* state of the generator (20 bytes). Note, however, that our seed
* generation algorithm has not been thoroughly studied or widely deployed.
*
* <p>The first time this constructor is called in a given Virtual Machine,
* it may take several seconds of CPU time to seed the generator, depending
* on the underlying hardware. Successive calls run quickly because they
* rely on the same (internal) pseudo-random number generator for their
* seed bits.
*
* <p>The seeding procedure implemented by this constructor ensures that
* the sequence of pseudo-random bytes produced by each SecureRandom
* instance yields no useful information about the byte-sequence produced
* by any other instance. If however, the user wishes to produce multiple
* instances with truly unrelated seeds, the following code yields
* the desired result (at substantial CPU cost per instance!):<p>
*
* <pre>
* SecureRandom rnd = new SecureRandom(SecureRandom.getSeed(20));
* </pre>
*/
public SecureRandom() {
this(nextSeed());
}
/**
* This call, used exclusively by the empty constructor, returns 20 seed
* bytes to seed the SecureRandom instance under construction. The first
* time this method is called, creates a class-wide generator-generator.
* This involves generating 20 "real-random" bytes with getSeed, which is
* very time consuming!
*/
private synchronized static byte[] nextSeed() {
if (seeder == null) {
seeder = new SecureRandom(getSeed(20));
seeder.setSeed(SeedGenerator.getSystemEntropy());
}
byte seed[] = new byte[20];
seeder.nextBytes(seed);
return seed;
}
/**
* This constructor uses a user-provided seed in preference to the
* self-seeding algorithm referred to in the empty constructor
* description. It may be preferable to the empty constructor if the
* caller has access to high-quality random bytes from some physical
* device (for example, a radiation detector or a noisy diode).
*
* @param seed the seed.
*/
public SecureRandom(byte seed[]) {
/*
* This call to our superclass constructor will result in a call
* to our own setSeed method, which will return immediately when
* it is passed zero.
*/
super(0);
try {
digest = MessageDigest.getInstance("SHA");
} catch (NoSuchAlgorithmException e) {
throw new InternalError("internal error: SHA-1 not available.");
}
setSeed(seed);
}
/**
* Reseeds this random object. The given seed supplements, rather than
* replaces, the existing seed. Thus, repeated calls are guaranteed
* never to reduce randomness.
*
* @param seed the seed.
*/
synchronized public void setSeed(byte[] seed) {
if (state != null) {
digest.update(state);
for (int i = 0; i < state.length; i++)
state[i] = 0;
}
state = digest.digest(seed);
}
/**
* Reseeds this random object, using the eight bytes contained
* in the given <code>long seed</code>. The given seed supplements,
* rather than replaces, the existing seed. Thus, repeated calls
* are guaranteed never to reduce randomness.
*
* <p>This method is defined for compatibility with
* <code>java.util.Random</code>.
*
* @param seed the seed.
*/
public void setSeed(long seed) {
/*
* Ignore call from super constructor (as well as any other calls
* unfortunate enough to be passing 0). It's critical that we
* ignore call from superclass constructor, as digest has not
* yet been initialized at that point.
*/
if (seed != 0)
setSeed(longToByteArray(seed));
}
private static void updateState(byte[] state, byte[] output) {
int last = 1;
int v = 0;
byte t = 0;
boolean zf = false;
// state(n + 1) = (state(n) + output(n) + 1) % 2^160;
for (int i = 0; i < state.length; i++) {
// Add two bytes
v = (int)state[i] + (int)output[i] + last;
// Result is lower 8 bits
t = (byte)v;
// Store result. Check for state collision.
zf = zf | (state[i] != t);
state[i] = t;
// High 8 bits are carry. Store for next iteration.
last = v >> 8;
}
// Make sure at least one bit changes!
if (!zf)
state[0]++;
}
/**
* Generates a user-specified number of random bytes. This method is
* used as the basis of all random entities returned by this class
* (except seed bytes). Thus, it may be overridden to change the
* behavior of the class.
*
* @param bytes the array to be filled in with random bytes.
*/
synchronized public void nextBytes(byte[] result) {
int index = 0;
int todo;
byte[] output = remainder;
// Use remainder from last time
int r = remCount;
if (r > 0) {
// How many bytes?
todo = (result.length - index) < (DIGEST_SIZE - r) ?
(result.length - index) : (DIGEST_SIZE - r);
// Copy the bytes, zero the buffer
for (int i = 0; i < todo; i++) {
result[i] = output[r];
output[r++] = 0;
}
remCount += todo;
index += todo;
}
// If we need more bytes, make them.
while (index < result.length) {
// Step the state
digest.update(state);
output = digest.digest();
updateState(state, output);
// How many bytes?
todo = (result.length - index) > DIGEST_SIZE ?
DIGEST_SIZE : result.length - index;
// Copy the bytes, zero the buffer
for (int i = 0; i < todo; i++) {
result[index++] = output[i];
output[i] = 0;
}
remCount += todo;
}
// Store remainder for next time
remainder = output;
remCount %= DIGEST_SIZE;
}
/**
* Generates an integer containing the user-specified number of
* pseudo-random bits (right justified, with leading zeros). This
* method overrides a <code>java.util.Random</code> method, and serves
* to provide a source of random bits to all of the methods inherited
* from that class (for example, <code>nextInt</code>,
* <code>nextLong</code>, and <code>nextFloat</code>).
*
* @param numBits number of pseudo-random bits to be generated, where
* 0 <= <code>numBits</code> <= 32.
*/
final protected int next(int numBits) {
int numBytes = (numBits+7)/8;
byte b[] = new byte[numBytes];
int next = 0;
nextBytes(b);
for (int i=0; i<numBytes; i++)
next = (next << 8) + (b[i] & 0xFF);
return next >>> (numBytes*8 - numBits);
}
/**
* Returns the given number of seed bytes, computed using the seed
* generation algorithm that this class uses to seed itself. This
* call may be used to seed other random number generators. While
* we attempt to return a "truly random" sequence of bytes, we do not
* know exactly how random the bytes returned by this call are. (See
* the empty constructor <a href = "#SecureRandom">SecureRandom</a>
* for a brief description of the underlying algorithm.)
* The prudent user will err on the side of caution and get extra
* seed bytes, although it should be noted that seed generation is
* somewhat costly.
*
* @param numBytes the number of seed bytes to generate.
*
* @return the seed bytes.
*/
public static byte[] getSeed(int numBytes) {
byte[] retVal = new byte[numBytes];
for (int i=0; i<numBytes; i++)
retVal[i] = (byte) SeedGenerator.getByte();
return retVal;
}
/**
* Helper function to convert a long into a byte array (least significant
* byte first).
*/
private static byte[] longToByteArray(long l) {
byte[] retVal = new byte[8];
for (int i=0; i<8; i++) {
retVal[i] = (byte) l;
l >>= 8;
}
return retVal;
}
}