1 | /* |
2 | * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, |
3 | * and the EPL 1.0 (http://h2database.com/html/license.html). |
4 | * Initial Developer: H2 Group |
5 | */ |
6 | package org.h2.util; |
7 | |
8 | import java.io.ByteArrayOutputStream; |
9 | import java.io.DataOutputStream; |
10 | import java.io.IOException; |
11 | import java.lang.reflect.Method; |
12 | import java.security.SecureRandom; |
13 | import java.util.Random; |
14 | |
15 | /** |
16 | * This is a utility class with mathematical helper functions. |
17 | */ |
18 | public class MathUtils { |
19 | |
20 | /** |
21 | * The secure random object. |
22 | */ |
23 | static SecureRandom cachedSecureRandom; |
24 | |
25 | /** |
26 | * True if the secure random object is seeded. |
27 | */ |
28 | static volatile boolean seeded; |
29 | |
30 | private static final Random RANDOM = new Random(); |
31 | |
32 | private MathUtils() { |
33 | // utility class |
34 | } |
35 | |
36 | |
37 | /** |
38 | * Round the value up to the next block size. The block size must be a power |
39 | * of two. As an example, using the block size of 8, the following rounding |
40 | * operations are done: 0 stays 0; values 1..8 results in 8, 9..16 results |
41 | * in 16, and so on. |
42 | * |
43 | * @param x the value to be rounded |
44 | * @param blockSizePowerOf2 the block size |
45 | * @return the rounded value |
46 | */ |
47 | public static int roundUpInt(int x, int blockSizePowerOf2) { |
48 | return (x + blockSizePowerOf2 - 1) & (-blockSizePowerOf2); |
49 | } |
50 | |
51 | /** |
52 | * Round the value up to the next block size. The block size must be a power |
53 | * of two. As an example, using the block size of 8, the following rounding |
54 | * operations are done: 0 stays 0; values 1..8 results in 8, 9..16 results |
55 | * in 16, and so on. |
56 | * |
57 | * @param x the value to be rounded |
58 | * @param blockSizePowerOf2 the block size |
59 | * @return the rounded value |
60 | */ |
61 | public static long roundUpLong(long x, long blockSizePowerOf2) { |
62 | return (x + blockSizePowerOf2 - 1) & (-blockSizePowerOf2); |
63 | } |
64 | |
65 | private static synchronized SecureRandom getSecureRandom() { |
66 | if (cachedSecureRandom != null) { |
67 | return cachedSecureRandom; |
68 | } |
69 | // Workaround for SecureRandom problem as described in |
70 | // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6202721 |
71 | // Can not do that in a static initializer block, because |
72 | // threads are not started until after the initializer block exits |
73 | try { |
74 | cachedSecureRandom = SecureRandom.getInstance("SHA1PRNG"); |
75 | // On some systems, secureRandom.generateSeed() is very slow. |
76 | // In this case it is initialized using our own seed implementation |
77 | // and afterwards (in the thread) using the regular algorithm. |
78 | Runnable runnable = new Runnable() { |
79 | @Override |
80 | public void run() { |
81 | try { |
82 | SecureRandom sr = SecureRandom.getInstance("SHA1PRNG"); |
83 | byte[] seed = sr.generateSeed(20); |
84 | synchronized (cachedSecureRandom) { |
85 | cachedSecureRandom.setSeed(seed); |
86 | seeded = true; |
87 | } |
88 | } catch (Exception e) { |
89 | // NoSuchAlgorithmException |
90 | warn("SecureRandom", e); |
91 | } |
92 | } |
93 | }; |
94 | |
95 | try { |
96 | Thread t = new Thread(runnable, "Generate Seed"); |
97 | // let the process terminate even if generating the seed is |
98 | // really slow |
99 | t.setDaemon(true); |
100 | t.start(); |
101 | Thread.yield(); |
102 | try { |
103 | // normally, generateSeed takes less than 200 ms |
104 | t.join(400); |
105 | } catch (InterruptedException e) { |
106 | warn("InterruptedException", e); |
107 | } |
108 | if (!seeded) { |
109 | byte[] seed = generateAlternativeSeed(); |
110 | // this never reduces randomness |
111 | synchronized (cachedSecureRandom) { |
112 | cachedSecureRandom.setSeed(seed); |
113 | } |
114 | } |
115 | } catch (SecurityException e) { |
116 | // workaround for the Google App Engine: don't use a thread |
117 | runnable.run(); |
118 | generateAlternativeSeed(); |
119 | } |
120 | |
121 | } catch (Exception e) { |
122 | // NoSuchAlgorithmException |
123 | warn("SecureRandom", e); |
124 | cachedSecureRandom = new SecureRandom(); |
125 | } |
126 | return cachedSecureRandom; |
127 | } |
128 | |
129 | /** |
130 | * Generate a seed value, using as much unpredictable data as possible. |
131 | * |
132 | * @return the seed |
133 | */ |
134 | public static byte[] generateAlternativeSeed() { |
135 | try { |
136 | ByteArrayOutputStream bout = new ByteArrayOutputStream(); |
137 | DataOutputStream out = new DataOutputStream(bout); |
138 | |
139 | // milliseconds and nanoseconds |
140 | out.writeLong(System.currentTimeMillis()); |
141 | out.writeLong(System.nanoTime()); |
142 | |
143 | // memory |
144 | out.writeInt(new Object().hashCode()); |
145 | Runtime runtime = Runtime.getRuntime(); |
146 | out.writeLong(runtime.freeMemory()); |
147 | out.writeLong(runtime.maxMemory()); |
148 | out.writeLong(runtime.totalMemory()); |
149 | |
150 | // environment |
151 | try { |
152 | String s = System.getProperties().toString(); |
153 | // can't use writeUTF, as the string |
154 | // might be larger than 64 KB |
155 | out.writeInt(s.length()); |
156 | out.write(s.getBytes("UTF-8")); |
157 | } catch (Exception e) { |
158 | warn("generateAlternativeSeed", e); |
159 | } |
160 | |
161 | // host name and ip addresses (if any) |
162 | try { |
163 | // workaround for the Google App Engine: don't use InetAddress |
164 | Class<?> inetAddressClass = Class.forName( |
165 | "java.net.InetAddress"); |
166 | Object localHost = inetAddressClass.getMethod( |
167 | "getLocalHost").invoke(null); |
168 | String hostName = inetAddressClass.getMethod( |
169 | "getHostName").invoke(localHost).toString(); |
170 | out.writeUTF(hostName); |
171 | Object[] list = (Object[]) inetAddressClass.getMethod( |
172 | "getAllByName", String.class).invoke(null, hostName); |
173 | Method getAddress = inetAddressClass.getMethod( |
174 | "getAddress"); |
175 | for (Object o : list) { |
176 | out.write((byte[]) getAddress.invoke(o)); |
177 | } |
178 | } catch (Throwable e) { |
179 | // on some system, InetAddress is not supported |
180 | // on some system, InetAddress.getLocalHost() doesn't work |
181 | // for some reason (incorrect configuration) |
182 | } |
183 | |
184 | // timing (a second thread is already running usually) |
185 | for (int j = 0; j < 16; j++) { |
186 | int i = 0; |
187 | long end = System.currentTimeMillis(); |
188 | while (end == System.currentTimeMillis()) { |
189 | i++; |
190 | } |
191 | out.writeInt(i); |
192 | } |
193 | |
194 | out.close(); |
195 | return bout.toByteArray(); |
196 | } catch (IOException e) { |
197 | warn("generateAlternativeSeed", e); |
198 | return new byte[1]; |
199 | } |
200 | } |
201 | |
202 | /** |
203 | * Print a message to system output if there was a problem initializing the |
204 | * random number generator. |
205 | * |
206 | * @param s the message to print |
207 | * @param t the stack trace |
208 | */ |
209 | static void warn(String s, Throwable t) { |
210 | // not a fatal problem, but maybe reduced security |
211 | System.out.println("Warning: " + s); |
212 | if (t != null) { |
213 | t.printStackTrace(); |
214 | } |
215 | } |
216 | |
217 | /** |
218 | * Get the value that is equal or higher than this value, and that is a |
219 | * power of two. |
220 | * |
221 | * @param x the original value |
222 | * @return the next power of two value |
223 | */ |
224 | public static int nextPowerOf2(int x) { |
225 | long i = 1; |
226 | while (i < x && i < (Integer.MAX_VALUE / 2)) { |
227 | i += i; |
228 | } |
229 | return (int) i; |
230 | } |
231 | |
232 | /** |
233 | * Convert a long value to an int value. Values larger than the biggest int |
234 | * value is converted to the biggest int value, and values smaller than the |
235 | * smallest int value are converted to the smallest int value. |
236 | * |
237 | * @param l the value to convert |
238 | * @return the converted int value |
239 | */ |
240 | public static int convertLongToInt(long l) { |
241 | if (l <= Integer.MIN_VALUE) { |
242 | return Integer.MIN_VALUE; |
243 | } else if (l >= Integer.MAX_VALUE) { |
244 | return Integer.MAX_VALUE; |
245 | } else { |
246 | return (int) l; |
247 | } |
248 | } |
249 | |
250 | /** |
251 | * Compare two values. Returns -1 if the first value is smaller, 1 if |
252 | * bigger, and 0 if equal. |
253 | * |
254 | * @param a the first value |
255 | * @param b the second value |
256 | * @return the result |
257 | */ |
258 | public static int compareInt(int a, int b) { |
259 | return a == b ? 0 : a < b ? -1 : 1; |
260 | } |
261 | |
262 | /** |
263 | * Compare two values. Returns -1 if the first value is smaller, 1 if |
264 | * bigger, and 0 if equal. |
265 | * |
266 | * @param a the first value |
267 | * @param b the second value |
268 | * @return the result |
269 | */ |
270 | public static int compareLong(long a, long b) { |
271 | return a == b ? 0 : a < b ? -1 : 1; |
272 | } |
273 | |
274 | /** |
275 | * Get a cryptographically secure pseudo random long value. |
276 | * |
277 | * @return the random long value |
278 | */ |
279 | public static long secureRandomLong() { |
280 | SecureRandom sr = getSecureRandom(); |
281 | synchronized (sr) { |
282 | return sr.nextLong(); |
283 | } |
284 | } |
285 | |
286 | /** |
287 | * Get a number of pseudo random bytes. |
288 | * |
289 | * @param bytes the target array |
290 | */ |
291 | public static void randomBytes(byte[] bytes) { |
292 | RANDOM.nextBytes(bytes); |
293 | } |
294 | |
295 | /** |
296 | * Get a number of cryptographically secure pseudo random bytes. |
297 | * |
298 | * @param len the number of bytes |
299 | * @return the random bytes |
300 | */ |
301 | public static byte[] secureRandomBytes(int len) { |
302 | if (len <= 0) { |
303 | len = 1; |
304 | } |
305 | byte[] buff = new byte[len]; |
306 | SecureRandom sr = getSecureRandom(); |
307 | synchronized (sr) { |
308 | sr.nextBytes(buff); |
309 | } |
310 | return buff; |
311 | } |
312 | |
313 | /** |
314 | * Get a pseudo random int value between 0 (including and the given value |
315 | * (excluding). The value is not cryptographically secure. |
316 | * |
317 | * @param lowerThan the value returned will be lower than this value |
318 | * @return the random long value |
319 | */ |
320 | public static int randomInt(int lowerThan) { |
321 | return RANDOM.nextInt(lowerThan); |
322 | } |
323 | |
324 | /** |
325 | * Get a cryptographically secure pseudo random int value between 0 |
326 | * (including and the given value (excluding). |
327 | * |
328 | * @param lowerThan the value returned will be lower than this value |
329 | * @return the random long value |
330 | */ |
331 | public static int secureRandomInt(int lowerThan) { |
332 | SecureRandom sr = getSecureRandom(); |
333 | synchronized (sr) { |
334 | return sr.nextInt(lowerThan); |
335 | } |
336 | } |
337 | |
338 | } |