OsCryptoRandom.java

  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. import java.io.File;
  20. import java.io.FileInputStream;
  21. import java.io.IOException;
  22. import java.util.Properties;

  23. import org.apache.commons.crypto.utils.IoUtils;

  24. /**
  25.  * A Random implementation that uses random bytes sourced from the operating system.
  26.  * <p>
  27.  * This class is not public/protected so does not appear in the main Javadoc Please ensure that property use is documented in the enum
  28.  * CryptoRandomFactory.RandomProvider
  29.  * </p>
  30.  */
  31. final class OsCryptoRandom implements CryptoRandom {

  32.     private static final int RESERVOIR_LENGTH = 8192;

  33.     private transient FileInputStream stream;

  34.     private final byte[] reservoir = new byte[RESERVOIR_LENGTH];

  35.     private int pos = reservoir.length;

  36.     /**
  37.      * Constructs a {@link OsCryptoRandom}.
  38.      *
  39.      * @param props the configuration properties.
  40.      * Uses {@link CryptoRandomFactory#DEVICE_FILE_PATH_KEY} to determine the
  41.      * path to the random device, default is
  42.      * {@link CryptoRandomFactory#DEVICE_FILE_PATH_DEFAULT}
  43.      */
  44.     public OsCryptoRandom(final Properties props) {
  45.         final File randomDevFile = new File(props.getProperty(CryptoRandomFactory.DEVICE_FILE_PATH_KEY, CryptoRandomFactory.DEVICE_FILE_PATH_DEFAULT));

  46.         try {
  47.             close();
  48.             this.stream = new FileInputStream(randomDevFile);
  49.         } catch (final IOException e) {
  50.             throw new IllegalArgumentException(e);
  51.         }

  52.         try {
  53.             fillReservoir(0);
  54.         } catch (final IllegalStateException e) {
  55.             close();
  56.             throw e;
  57.         }
  58.     }

  59.     /**
  60.      * Overrides {@link java.lang.AutoCloseable#close()}. Closes the OS stream.
  61.      */
  62.     @Override
  63.     synchronized public void close() {
  64.         if (stream != null) {
  65.             IoUtils.closeQuietly(stream);
  66.             stream = null;
  67.         }
  68.     }

  69.     /**
  70.      * Fills the reservoir.
  71.      *
  72.      * @param min the length.
  73.      */
  74.     private void fillReservoir(final int min) {
  75.         if (pos >= reservoir.length - min) {
  76.             try {
  77.                 IoUtils.readFully(stream, reservoir, 0, reservoir.length);
  78.             } catch (final IOException e) {
  79.                 throw new IllegalStateException("failed to fill reservoir", e);
  80.             }
  81.             pos = 0;
  82.         }
  83.     }

  84.     /**
  85.      * Overrides {@link CryptoRandom#nextBytes(byte[])}. Generates random bytes
  86.      * and places them into a user-supplied byte array. The number of random
  87.      * bytes produced is equal to the length of the byte array.
  88.      *
  89.      * @param bytes the array to be filled in with random bytes.
  90.      */
  91.     @Override
  92.     synchronized public void nextBytes(final byte[] bytes) {
  93.         int off = 0;
  94.         int n = 0;
  95.         while (off < bytes.length) {
  96.             fillReservoir(0);
  97.             n = Math.min(bytes.length - off, reservoir.length - pos);
  98.             System.arraycopy(reservoir, pos, bytes, off, n);
  99.             off += n;
  100.             pos += n;
  101.         }
  102.     }

  103. }