Можете ли вы «клонировать» объект java SecureRandom? - Как получить клонируемый сильный ГСЧ? - PullRequest
1 голос
/ 01 февраля 2020

У меня есть приложение, которому нужны длинные случайные битовые последовательности.

Поскольку я полагаюсь, что эти последовательности "действительно случайны", я хочу использовать SecureRandom.

К сожалению, мне нужна каждая такая последовательность дважды.

Я не могу держать их в оперативной памяти, потому что они слишком длинные.

Я не знаю не хочу записывать их на жесткий диск, потому что запись и чтение с жесткого диска требует времени и места на жестком диске и делает код излишне сложным.

Однако, поскольку "случайная" последовательность битов генерируется некоторый Генератор детерминированно зависит от состояния, в котором Генератор находился, когда он был создан, один (может быть) может быть в состоянии сохранить и воссоздать состояние, в котором был недавно сгенерированный объект SecureRandom, что позволяет воссоздать битовую последовательность.

Основной вопрос:

(Как) Можно ли сохранить все состояние генератора случайных чисел SecureRandom? Достаточно ли клонирования? (могут быть некоторые состояния ОС, которые также играют роль, например, текущее время). Существуют ли другие сильные ГСЧ, которые вы можете клонировать, чтобы оригинал и клон создавали одинаковые выходные данные?

Кроме того, меня интересует, какие типы шаблонов создают обычные генераторы случайных чисел, делают их небезопасными, и можно ли сохранять и восстанавливать их состояние, как описано выше.

Редактировать:

I для этой цели создал LCG.

Однако я думаю, что он не работает должным образом.

Создает ли он "сильную" псевдослучайную битовую последовательность?

Это стало подозрительным, когда это дало одно и то же значение несколько раз подряд. Например:

ложь ложь истина истина истина ложь истина ложь ложь ложь истина истина истина истина истина истина истина истина истинно истина истина истина истина истинно истина истинно истинно истина истина истинно истина истина истинно истинно истинно истинно истина истинно истина истинно истинно истинно истинно истинно истина истинно истинно истинно истинно истинно верно истинно истинно истинно истинно верно Истинный Истинный Истинный Истинный Истинный Истинный Истинный Истинный Истинный Истинный Истинный Истинный Истинный Истинный Истинный Истинный Истинный Истинный Истинный

public class DeterministicRandom{
    private BigInteger state;
    private BigInteger a;
    private BigInteger c;
    private static final int o=32;
    private static final BigInteger m=BigInteger.ONE.shiftLeft(o*8);
    public DeterministicRandom(){
        a=RandomUtils.randomBigInt(o);
        c=RandomUtils.randomBigInt(o);
        state=RandomUtils.randomBigInt(o);
    }
    public DeterministicRandom(DeterministicRandom r){
        a=r.a;
        c=r.c;
        state=r.state;
    }
    public boolean nextBoolean(){
        boolean r=state.testBit(o*8-7);
        state=state.multiply(a).add(c).mod(m);
        return r;
    }
}
public class RandomUtils {
    private static Random random = new Random();
    public static byte[] nextBytes(byte[] d){
        random.nextBytes(d);
        return d;
    }
    public boolean randomBit(){
        return random.nextBoolean();
    }
    public static BigInteger randomBigInt(int bytes){
        byte[] d=new byte[bytes+1];
        random.nextBytes(d);
        d[0]=0;
        BigInteger x=new BigInteger(d);
        return x;
    }
}

Ответы [ 2 ]

3 голосов
/ 02 февраля 2020

Кажется, вы хотите случайные числа для машинного обучения или выборки Монте-Карло. Также кажется, что вы также сочли java.util.Random и нашли его неподходящим для ваших целей. В этом случае SecureRandom далеко не лучший выбор. Вместо этого вам нужен высококачественный PRNG , и доступно множество высококачественных алгоритмов RNG .

In Java, два примеров включают it.unimi.dsi/dsiutils и org.apache.commons/commons-rng-simple артефакты, которые включают в себя реализации посеянных PRNG, таких как xoroshiro128++ и xoshiro256**.

2 голосов
/ 02 февраля 2020

Не думаю, что в целом можно полагаться на то, что экземпляр SecureRandom может быть клонирован, или на клон, обеспечивающий ту же последовательность, что и оригинал.

Проблема заключается в том, что типичная платформа Java предоставляет несколько реализаций безопасного случайного выбора, и когда вы вызываете new SecureRandom(), вы получаете реализацию по умолчанию. Это может быть PRNG или RNG, который использует источник «реальной» случайности. В последнем случае, даже если clone() поддерживается классом, объект-клон не будет выдавать ту же случайную последовательность, что и оригинал.

Я могу придумать пару решений (в общем).


Первое решение - создать собственный класс Random, который обёртывает экземпляр SecureRandom. Особый соус заключается в том, что вашему классу-обертке нужно записать поток случайных чисел, которые он раздает. Затем вам нужен метод reset(), который заставляет PRNG переключаться на использование записанных чисел.

Недостатком является то, что вам нужно достаточно памяти для хранения всех чисел. (В зависимости от представления, это может быть много памяти. Например, если вы записываете N целых чисел в ArrayList<Integer>, наихудший случай - N x 24 байта для объектов Integer и пик 3 x N x 8 байтов для ArrayList. int[] гораздо более плотный, но более сложный в управлении.)

Я вижу, что вы сказали, что последовательности будут слишком длинными, чтобы держать их в памяти при вашем использовании. дело. Вы могли бы записать их в файл, но даже это не масштабируется бесконечно.


Второе решение - использовать возможность классов SecureRandom для выбора алгоритма и повторного заполнения себя.

SecureRandom seeder = new SecureRandom();
byte[] seed = seeder.generateSeed(NOS_BYTES);

SecureRandom prng = SecureRandom.getInstance("SHA1PRNG");
prng.setSeed(seed);

SecureRandom prng2 = SecureRandom.getInstance("SHA1PRNG");
prng2.setSeed(seed);

// prng1 and prng2 created as above should generate the same sequences

Установите NOS_BYTES на количество байтов начального числа, которое вы хотите использовать. Или вы можете сгенерировать начальное число «вручную» или прочитать его из файла.

Обратите внимание, что вы не можете надежно установить начальное значение для экземпляра, полученного с помощью new SecureRandom(), и вы не можете надежно восстановить заполнить существующий экземпляр 1 .

Могут быть и другие алгоритмы PRNG, которые вы можете использовать, в зависимости от ваших Java платформ, настроенных провайдерами безопасности.

Пожалуйста, обратитесь к SecureRandom javado c для получения дополнительной информации.

Я протестировал описанный выше подход и на моей машине с Java 11 объекты prng и prng2 генерировали одинаковую последовательность значений int ... до тех пор, пока я не нажму ^ C.

1 - Суть проблемы в том, что javado c для setSeed говорит так: "Данное семя дополняет, а не заменяет существующее семена. ". В другом месте говорится, что конструкторы возвращают объект, который был засеян. К счастью, в нем также говорится, что методы getInstance возвращают объекты, которые не были заполнены.


Наконец, из вашего обновленного вопроса я вижу, что вы рассматриваете возможность использования "roll-your- собственный "PRNG" реализован с использованием BigInteger.

Осторожно!

Очень легко реализовать PRNG, который не так хорош, как вы думаете. Я бы не стал этого делать. Но если вы решите go пойти по этому пути, вам следует ознакомиться с теорией PRNG и их свойствами и выполнить серию (соответствующих) статистических тестов вашей реализации.

(И если вы намерены опубликовать sh свои результаты, в вашей статье должно быть упомянуто, что вы внедрили собственный PRNG, и вы должны предоставить исходный код PRNG и результаты статистического теста для читателей, чтобы проверить. Ради открытости и научной * воспроизводимости c. На мой взгляд.)

...