View Javadoc
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 }