-
Notifications
You must be signed in to change notification settings - Fork 5.6k
/
SecureRandom.java
335 lines (304 loc) · 11.5 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
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
/*
* Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.provider;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.security.MessageDigest;
import java.security.SecureRandomSpi;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.Arrays;
/**
* <p>This class provides a cryptographically strong pseudo-random number
* generator based on the SHA-1 hash algorithm.
*
* <p>Note that if a seed is not provided, we attempt to provide sufficient
* seed bytes to completely randomize the internal state of the generator
* (20 bytes). However, our seed generation algorithm has not been thoroughly
* studied or widely deployed.
*
* <p>Also note that when a random object is deserialized,
* <a href="#engineNextBytes(byte[])">engineNextBytes</a> invoked on the
* restored random object will yield the exact same (random) bytes as the
* original object. If this behaviour is not desired, the restored random
* object should be seeded, using
* <a href="#engineSetSeed(byte[])">engineSetSeed</a>.
*
* @author Benjamin Renaud
* @author Josh Bloch
* @author Gadi Guy
*/
public final class SecureRandom extends SecureRandomSpi
implements java.io.Serializable {
@java.io.Serial
private static final long serialVersionUID = 3581829991155417889L;
private static final int DIGEST_SIZE = 20;
private transient MessageDigest digest;
private byte[] state;
private byte[] remainder;
private int remCount;
/**
* An empty constructor that creates an unseeded SecureRandom object.
* <p>
* Unless the user calls setSeed(), the first call to engineGetBytes()
* will have the SeedGenerator provide sufficient seed bytes to
* completely randomize the internal state of the generator (20 bytes).
* Note that the old threaded seed generation algorithm is provided
* only as a fallback, and has not been thoroughly studied or widely
* deployed.
* <p>
* The SeedGenerator relies on a VM-wide entropy pool to generate
* seed bytes for these objects. The first time the SeedGenerator is
* called, it may take several seconds of CPU time to initialize,
* depending on the underlying hardware. Successive calls run
* quickly because they rely on the same (internal) pseudo-random
* number generator for their seed bits.
*/
public SecureRandom() {
init(null);
}
/**
* This constructor is used to instantiate the private seeder object
* with a given seed from the SeedGenerator.
*
* @param seed the seed.
*/
private SecureRandom(byte[] seed) {
init(seed);
}
/**
* This call, used by the constructors, instantiates the SHA digest
* and sets the seed, if given.
*/
private void init(byte[] seed) {
try {
/*
* Use the local SUN implementation to avoid native
* performance overhead.
*/
digest = MessageDigest.getInstance("SHA", "SUN");
} catch (NoSuchProviderException | NoSuchAlgorithmException e) {
// Fallback to any available.
try {
digest = MessageDigest.getInstance("SHA");
} catch (NoSuchAlgorithmException exc) {
throw new InternalError(
"internal error: SHA-1 not available.", exc);
}
}
if (seed != null) {
engineSetSeed(seed);
}
}
/**
* 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.
*/
@Override
public byte[] engineGenerateSeed(int numBytes) {
// Neither of the SeedGenerator implementations require
// locking, so no sync needed here.
byte[] b = new byte[numBytes];
SeedGenerator.generateSeed(b);
return b;
}
/**
* 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.
*/
@Override
public synchronized void engineSetSeed(byte[] seed) {
if (state != null) {
digest.update(state);
Arrays.fill(state, (byte) 0);
}
state = digest.digest(seed);
remCount = 0;
}
private static void updateState(byte[] state, byte[] output) {
int last = 1;
int v;
byte t;
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]++;
}
}
/**
* This static object will be seeded by SeedGenerator, and used
* to seed future instances of SHA1PRNG SecureRandoms.
* <p>
* Bloch, Effective Java Second Edition: Item 71
*/
private static class SeederHolder {
private static final SecureRandom seeder;
static {
/*
* Call to SeedGenerator.generateSeed() to add additional
* seed material (likely from the Native implementation).
*/
seeder = new SecureRandom(SeedGenerator.getSystemEntropy());
byte [] b = new byte[DIGEST_SIZE];
SeedGenerator.generateSeed(b);
seeder.engineSetSeed(b);
}
}
/**
* Generates a user-specified number of random bytes.
*
* @param result the array to be filled in with random bytes.
*/
@Override
public synchronized void engineNextBytes(byte[] result) {
int index = 0;
int todo;
byte[] output = remainder;
if (state == null) {
byte[] seed = new byte[DIGEST_SIZE];
SeederHolder.seeder.engineNextBytes(seed);
state = digest.digest(seed);
}
// Use remainder from last time
int r = remCount;
if (r > 0) {
// How many bytes?
todo = Math.min(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 = Math.min((result.length - index), DIGEST_SIZE);
// 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;
}
/*
* This method is called to restore the state of the random object from
* a stream.
* <p>
* We have to create a new instance of {@code MessageDigest}, because
* it is not included in the stream (it is marked "transient").
* <p>
* Note that the {@code engineNextBytes()} method invoked on the restored
* random object will yield the exact same (random) bytes as the original.
* If you do not want this behaviour, you should re-seed the restored
* random object, using {@code engineSetSeed()}.
*
* @param s the {@code ObjectInputStream} from which data is read
* @throws IOException if an I/O error occurs
* @throws ClassNotFoundException if a serialized class cannot be loaded
*/
@java.io.Serial
private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException {
s.defaultReadObject ();
try {
/*
* Use the local SUN implementation to avoid native
* performance overhead.
*/
digest = MessageDigest.getInstance("SHA", "SUN");
} catch (NoSuchProviderException | NoSuchAlgorithmException e) {
// Fallback to any available.
try {
digest = MessageDigest.getInstance("SHA");
} catch (NoSuchAlgorithmException exc) {
throw new InternalError(
"internal error: SHA-1 not available.", exc);
}
}
// Various consistency checks
if ((remainder == null) && (remCount > 0)) {
throw new InvalidObjectException(
"Remainder indicated, but no data available");
}
// Not yet allocated state
if (state == null) {
if (remainder == null) {
return;
} else {
throw new InvalidObjectException(
"Inconsistent buffer allocations");
}
}
// Sanity check on sizes/pointer
if ((state.length != DIGEST_SIZE) ||
((remainder != null) && (remainder.length != DIGEST_SIZE)) ||
(remCount < 0 ) || (remCount >= DIGEST_SIZE)) {
throw new InvalidObjectException(
"Inconsistent buffer sizes/state");
}
state = state.clone();
if (remainder != null) {
remainder = remainder.clone();
}
}
}