package org.bouncycastle.crypto.prng;

import org.bouncycastle.crypto.BlockCipher;

public class X931RNG
{
    private static final long       BLOCK64_RESEED_MAX = 1L << (16 - 1);
    private static final long       BLOCK128_RESEED_MAX = 1L << (24 - 1);
    private static final int        BLOCK64_MAX_BITS_REQUEST = 1 << (13 - 1);
    private static final int        BLOCK128_MAX_BITS_REQUEST = 1 << (19 - 1);
    
    private final BlockCipher engine;
    private final EntropySource entropySource;

    private final byte[] DT;
    private final byte[] I;
    private final byte[] R;;

    private byte[] V;

    private long reseedCounter = 1;

    
Params:
  • engine –
  • entropySource –
/** * * @param engine * @param entropySource */
public X931RNG(BlockCipher engine, byte[] dateTimeVector, EntropySource entropySource) { this.engine = engine; this.entropySource = entropySource; this.DT = new byte[engine.getBlockSize()]; System.arraycopy(dateTimeVector, 0, DT, 0, DT.length); this.I = new byte[engine.getBlockSize()]; this.R = new byte[engine.getBlockSize()]; }
Populate a passed in array with random data.
Params:
  • output – output array for generated bits.
  • predictionResistant – true if a reseed should be forced, false otherwise.
Returns:number of bits generated, -1 if a reseed required.
/** * Populate a passed in array with random data. * * @param output output array for generated bits. * @param predictionResistant true if a reseed should be forced, false otherwise. * * @return number of bits generated, -1 if a reseed required. */
int generate(byte[] output, boolean predictionResistant) { if (R.length == 8) // 64 bit block size { if (reseedCounter > BLOCK64_RESEED_MAX) { return -1; } if (isTooLarge(output, BLOCK64_MAX_BITS_REQUEST / 8)) { throw new IllegalArgumentException("Number of bits per request limited to " + BLOCK64_MAX_BITS_REQUEST); } } else { if (reseedCounter > BLOCK128_RESEED_MAX) { return -1; } if (isTooLarge(output, BLOCK128_MAX_BITS_REQUEST / 8)) { throw new IllegalArgumentException("Number of bits per request limited to " + BLOCK128_MAX_BITS_REQUEST); } } if (predictionResistant || V == null) { V = entropySource.getEntropy(); if (V.length != engine.getBlockSize()) { throw new IllegalStateException("Insufficient entropy returned"); } } int m = output.length / R.length; for (int i = 0; i < m; i++) { engine.processBlock(DT, 0, I, 0); process(R, I, V); process(V, R, I); System.arraycopy(R, 0, output, i * R.length, R.length); increment(DT); } int bytesToCopy = (output.length - m * R.length); if (bytesToCopy > 0) { engine.processBlock(DT, 0, I, 0); process(R, I, V); process(V, R, I); System.arraycopy(R, 0, output, m * R.length, bytesToCopy); increment(DT); } reseedCounter++; return output.length; }
Reseed the RNG.
/** * Reseed the RNG. */
void reseed() { V = entropySource.getEntropy(); if (V.length != engine.getBlockSize()) { throw new IllegalStateException("Insufficient entropy returned"); } reseedCounter = 1; } EntropySource getEntropySource() { return entropySource; } private void process(byte[] res, byte[] a, byte[] b) { for (int i = 0; i != res.length; i++) { res[i] = (byte)(a[i] ^ b[i]); } engine.processBlock(res, 0, res, 0); } private void increment(byte[] val) { for (int i = val.length - 1; i >= 0; i--) { if (++val[i] != 0) { break; } } } private static boolean isTooLarge(byte[] bytes, int maxBytes) { return bytes != null && bytes.length > maxBytes; } }