MikeMirzayanov's blog

By MikeMirzayanov, 10 years ago, In Russian

Был такой пост Timus и BigInteger.isProbablePrime. В нем описывалась проблема и ее решение от команды Тимуса.

Напомню суть. Класс SecureRandom для своей инициализации должен взять откуда-то неожиданные данные, которые злоумышленнику тяжело угадать. JRE для этого использует /dev/random. В Linux это такой специальный поток, в который драйверы выводят шум от разных устройств. В результате значения там в самом деле удачно подходят для инициализации криптографического генератора случайных чисел.

На Windows происходит всё примерно так же, но шум берется из разных мест. В результате, если запустить процесс из-под ограниченного пользователя, то чтобы взять этот шум процесс начинает инициализировать профиль пользователя в системе, что требует времени. В зависимости от характера песочницы, вероятно, из некоторых мест процесс и не сможет взять энтропию. Например, на Timus это приводило к вердикту Restricted Function.

На Codeforces это приводило до недавнего времени скорее к Time Limit Exceeded, так как загрузка профиля требовала дополнительного времени.

Олимпиадникам обычно этот SecureRandom и даром не нужен, но стандартная реализация BigInteger.isProbablePrime(BigInteger) использует его.

Команда Timus предложила такое решение. Они похачили стандартную библиотеку, заменив реализацию BigInteger и, видимо, подложив её в rt.jar. Короче, теперь пользовательские решения запускаются у них с измененной стандартной библиотекой.

Мне кажется это так себе решение:

  • возможно, что SecureRandom используется еще в каком-то полезном месте (или будет использоваться после апдейта);
  • тяжело обновлять JRE на тестирующих машинах — ведь нельзя просто так подложить rt.jar из прошлой версии, придется хачить новый rt.jar;
  • решение требует дополнительной настройки окружения, что всегда плохо (например, какой-нибудь portable timus tester либо не будет делать песочницу вообще, либо будет иметь эту проблему, либо большую инструкцию в стиле «не забудьте пропатчить Java!»).

Сегодня я нашел значительно лучшее решение, которое и накатил на Codeforces. Для JRE существует системное свойство, которое указывает откуда надо читать энтропию: -Djava.security.egd, которое по-умолчанию равно в Windows значению file:/dev/random

Так вот оказывается возможно брать энтропию из обычного файла, для этого достаточно передать -Djava.security.egd=file:имя-файла. Проблема решена. (Конечно, я сначала попробовал передать file:/dev/urandom, но это не помогло)

Напоследок, напомню, что на Timus для того, чтобы добиться повторяемости при rejudges пошли на беспрецедентный шаг и в пропатченной версии BigInteger используют Random c фиксированным randseed. В данном случае, надо просто в качестве -Djava.security.egd указывать какой-то фиксированный файл. Если хочется иметь каждый раз разное значение, то можно создать случайный файл в директории запуска решения и использовать его.

Конечно, этот трюк полностью разрушает криптостойкость SecureRandom, но в случае запуска решений участников это допустимо.

  • Vote: I like it
  • +43
  • Vote: I do not like it