1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18 package org.apache.commons.crypto.random;
19
20 import java.io.File;
21 import java.io.FileInputStream;
22 import java.io.IOException;
23 import java.util.Properties;
24
25 import org.apache.commons.crypto.utils.IoUtils;
26
27 /**
28 * A Random implementation that uses random bytes sourced from the operating system.
29 * <p>
30 * This class is not public/protected so does not appear in the main Javadoc Please ensure that property use is documented in the enum
31 * CryptoRandomFactory.RandomProvider
32 * </p>
33 */
34 final class OsCryptoRandom implements CryptoRandom {
35
36 private static final int RESERVOIR_LENGTH = 8192;
37
38 private transient FileInputStream stream;
39
40 private final byte[] reservoir = new byte[RESERVOIR_LENGTH];
41
42 private int pos = reservoir.length;
43
44 /**
45 * Constructs a {@link OsCryptoRandom}.
46 *
47 * @param props the configuration properties.
48 * Uses {@link CryptoRandomFactory#DEVICE_FILE_PATH_KEY} to determine the
49 * path to the random device, default is
50 * {@link CryptoRandomFactory#DEVICE_FILE_PATH_DEFAULT}
51 */
52 public OsCryptoRandom(final Properties props) {
53 final File randomDevFile = new File(props.getProperty(CryptoRandomFactory.DEVICE_FILE_PATH_KEY, CryptoRandomFactory.DEVICE_FILE_PATH_DEFAULT));
54
55 try {
56 close();
57 this.stream = new FileInputStream(randomDevFile);
58 } catch (final IOException e) {
59 throw new IllegalArgumentException(e);
60 }
61
62 try {
63 fillReservoir(0);
64 } catch (final IllegalStateException e) {
65 close();
66 throw e;
67 }
68 }
69
70 /**
71 * Overrides {@link java.lang.AutoCloseable#close()}. Closes the OS stream.
72 */
73 @Override
74 synchronized public void close() {
75 if (stream != null) {
76 IoUtils.closeQuietly(stream);
77 stream = null;
78 }
79 }
80
81 /**
82 * Fills the reservoir.
83 *
84 * @param min the length.
85 */
86 private void fillReservoir(final int min) {
87 if (pos >= reservoir.length - min) {
88 try {
89 IoUtils.readFully(stream, reservoir, 0, reservoir.length);
90 } catch (final IOException e) {
91 throw new IllegalStateException("failed to fill reservoir", e);
92 }
93 pos = 0;
94 }
95 }
96
97 /**
98 * Overrides {@link CryptoRandom#nextBytes(byte[])}. Generates random bytes
99 * and places them into a user-supplied byte array. The number of random
100 * bytes produced is equal to the length of the byte array.
101 *
102 * @param bytes the array to be filled in with random bytes.
103 */
104 @Override
105 synchronized public void nextBytes(final byte[] bytes) {
106 int off = 0;
107 int n = 0;
108 while (off < bytes.length) {
109 fillReservoir(0);
110 n = Math.min(bytes.length - off, reservoir.length - pos);
111 System.arraycopy(reservoir, pos, bytes, off, n);
112 off += n;
113 pos += n;
114 }
115 }
116
117 }