Давайте поиграем в компьютерный покер, только вы, я и сервер, которому мы оба доверяем. Сервер использует генератор псевдослучайных чисел, который инициализируется 32-битным начальным числом непосредственно перед началом игры. Таким образом, существует около четырех миллиардов возможных колод.
У меня в руке пять карт - очевидно, мы не играем в Техасский Холдем. Предположим, карты раздаются одному мне, одному вам, одному мне, одному вам и так далее. Итак, у меня в колоде первая, третья, пятая, седьмая и девятая карты.
Ранее я запускал генератор псевдослучайных чисел четыре миллиарда раз, по одному разу с каждым начальным числом, и записывал первую карту, сгенерированную для каждого, в базу данных. Предположим, моя первая карта - Пиковая дама. Это показывает только одну карту как первую в каждой из 52 возможных колод, поэтому мы сократили количество возможных колод с четырех миллиардов до примерно 80 миллионов или около того.
Предположим, моя вторая карта - это три сердца. Теперь я использую свой RNG еще 80 миллионов раз, используя 80 миллионов семян, которые в качестве первого числа дают пиковую даму. Это займет у меня пару секунд. Я записываю все колоды, которые производят три червы, в качестве третьей карты - второй карты в моей руке. Это опять-таки только около 2% колод, так что теперь мы сократили до 2 миллионов колод.
Предположим, что третья карта в моей руке - это 7 треф. У меня есть база данных с 2 миллионами семян, которые раздают мои две карты; Я провожу свой RNG еще 2 миллиона раз, чтобы найти 2% из тех колод, которые производят 7 треф в качестве третьей карты, и у нас осталось всего 40 тысяч колод.
Вы видите, как это происходит. Я запускаю свой RNG 40000 больше раз, чтобы найти все семена, которые производят мою четвертую карту, и это приводит нас к 800 колодам, а затем запускаю его еще 800 раз, чтобы получить ~ 20 семян, которые дают мою пятую карту, и теперь я просто сгенерируйте эти двадцать колод карт, и я знаю, что у вас есть одна из двадцати возможных рук. Более того, я очень хорошо представляю, что буду рисовать дальше.
Теперь вы понимаете, почему важна истинная случайность? Как вы описываете это, вы думаете, что распределение важно, но распределение не делает процесс случайным. Непредсказуемость - это то, что делает процесс случайным.
ОБНОВИТЬ
Исходя из (в настоящее время удаленных из-за их неконструктивного характера) комментариев, по крайней мере 0,3% людей, которые читали это, смущены моей точкой зрения. Когда люди выступают против точек я не сделал, или хуже, утверждают , для точек , которые я сделал на том , что я не делал их, то я знаю , что мне нужно более четко и тщательно объяснить.
Похоже, что в распространении слов возникает определенная путаница, поэтому я хочу осторожно назвать употребления.
Вопросы под рукой:
- Чем отличаются псевдослучайные числа и действительно случайные числа?
- Почему разница важна?
- Различия имеют какое-то отношение к распределению выхода PRNG?
Давайте начнем с рассмотрения идеального способа создания случайной колоды карт для игры в покер. Затем мы увидим, как другие методы генерирования колод различны, и если возможно, использовать эту разницу.
Давайте начнем с предположения, что у нас есть волшебная коробка с надписью TRNG
. В качестве входных данных мы даем ему целое число n, большее или равное единице, а в качестве выходных данных оно дает нам действительно случайное число от одного до n включительно. Вывод поля совершенно непредсказуем (если ему дано число, отличное от одного), и любое число от одного до n столь же вероятно, как и другое; то есть распределение равномерное . (Существуют и другие более сложные статистические проверки случайности, которые мы могли бы выполнить; я игнорирую этот пункт, поскольку он не соответствует моему аргументу. ТРНГ является совершенно статистически случайным по предположению.)
Начнем с колоды карт без перетасовки. Мы просим в графе число от одного до 52, то есть TRNG(52)
. Какое бы число оно не вернуло, мы отсчитываем столько карт из нашей отсортированной колоды и удаляем эту карту. Он становится первой картой в перетасованной колоде. Затем мы просим TRNG(51)
и делаем то же самое, чтобы выбрать вторую карту, и так далее.
Еще один способ взглянуть на это: есть 52! = 52 x 51 x 50 ... x 2 x 1 возможных колод, что примерно равно 2 226. Мы выбрали один из них поистине наугад.
Теперь мы сдаем карты. Когда я смотрю на свои карты, я понятия не имею, какие у вас карты. (Помимо очевидного факта, что у вас нет ни одной из моих карт.) Это могут быть любые карты с равной вероятностью.
Итак, позвольте мне убедиться, что я объясню это ясно. У нас есть равномерное распределение каждого отдельного выхода TRNG(n)
; каждый выбирает число от 1 до n с вероятностью 1/n. Кроме того, результатом этого процесса является то, что мы выбрали один из 52! возможные колоды с вероятностью 1/52 !, поэтому распределение по множеству возможных колод также равномерно.
Отлично.
Теперь давайте предположим, что у нас есть менее волшебная коробка с надписью PRNG
. Прежде чем вы сможете использовать его, он должен быть заполнен 32-битным беззнаковым номером.
В сторону: почему 32? Разве это не может быть заполнено с 64 или 256 или 10000 бит? Конечно. Но (1) на практике большинство готовых PRNG сеяно с 32-битным числом, и (2) если у вас есть 10000 бит случайности для создания начального числа, тогда зачем вообще использовать PRNG? У вас уже есть источник 10000 бит случайности!
В любом случае, вернемся к тому, как работает PRNG: после того, как он посеян, вы можете использовать его так же, как и TRNG
. То есть вы передаете ему число n, и оно возвращает вам число от 1 до n включительно. Кроме того, распределение этого выхода более или менее равномерно . То есть, когда мы просим PRNG
число от 1 до 6, мы получаем 1, 2, 3, 4, 5 или 6 примерно одну шестую часть времени, независимо от того, каким было семя.
Я хочу подчеркнуть этот момент несколько раз, потому что он, похоже, сбивает с толку некоторых комментаторов. Распределение PRNG является равномерным, по крайней мере, двумя способами. Сначала предположим, что мы выбрали какое-то конкретное семя. Мы ожидаем, что последовательности PRNG(6), PRNG(6), PRNG(6)...
миллион раз дадут равномерное распределение чисел между 1 и 6. И, во-вторых, если мы выбрали миллион различных семян и назвали PRNG(6)
один раз для каждого семени, мы снова ожидаем равномерного распределения чисел от 1 до 6. Однородность PRNG в любой из этих операций не имеет отношения к описываемой мной атаке.
Этот процесс называется псевдослучайным, поскольку поведение блока на самом деле полностью детерминировано; он выбирает один из 2 32 возможных вариантов поведения на основе начального числа. То есть, после того, как он будет посеян, PRNG(6), PRNG(6), PRNG(6), ...
создает последовательность чисел с равномерным распределением, но эта последовательность полностью определяется начальным числом. Для данной последовательности вызовов, скажем, PRNG(52), PRNG(51) ... и т.д., Существует только 2 32 возможных последовательности. Семя по сути выбирает, какое мы получим.
Для создания колоды сервер теперь генерирует начальное число. (Как? Мы вернемся к этому вопросу.) Затем они вызывают PRNG(52)
, PRNG(51)
и так далее, чтобы сгенерировать колоду, аналогично предыдущему.
Эта система подвержена атаке, которую я описал. Чтобы атаковать сервер, мы сначала заблаговременно заполняем нашу собственную копию поля 0, запрашиваем PRNG(52)
и записываем это. Затем мы снова посеем 1, попросим PRNG(52)
и запишем это до 2 32 -1.
Теперь покерный сервер, который использует PRNG для генерации колод, должен каким-то образом генерировать начальное число. Неважно, как они это делают. Они могли бы вызвать TRNG(2^32)
чтобы получить действительно случайное семя. Или они могли бы взять текущее время как семя, которое вряд ли случайно; Я знаю, сколько сейчас времени, столько же, сколько и тебе. Суть моей атаки в том, что это не имеет значения, потому что у меня есть база данных. Когда я вижу свою первую карту, я могу уничтожить 98% возможных семян. Когда я вижу свою вторую карту, я могу убрать на 98% больше и так далее, пока в конечном итоге не смогу добраться до горстки возможных семян и с высокой вероятностью узнать, что находится в вашей руке.
Теперь, опять же, я хочу подчеркнуть, что предположение здесь состоит в том, что если бы мы вызывали PRNG(6)
миллион раз, мы получали бы каждое число примерно одну шестую времени. Это распределение (более или менее) равномерное, и если однородность этого распределения - это все, что вас волнует, это нормально. Суть вопроса заключалась в том, есть ли что-то еще, кроме распределения PRNG(6)
о котором мы заботимся? и ответ да. Мы также заботимся о непредсказуемости.
Другой способ взглянуть на проблему состоит в том, что, хотя распределение миллиона вызовов в PRNG(6)
может быть хорошим, поскольку PRNG выбирает только из 32 возможных вариантов поведения, он не может генерировать все возможные колоды. Он может генерировать только 2 32 из 2 226 возможных колод; крошечная фракция. Так что распределение по множеству всех колод очень плохое. Но опять же, фундаментальная атака здесь основана на том, что мы можем успешно предсказать прошлое и будущее поведение PRNG
на основе небольшой выборки его результатов.
Позвольте мне сказать это в третий или четыре раза, чтобы убедиться, что это впитывается. Здесь есть три распределения. Во-первых, распределение процесса, который производит случайное 32-разрядное начальное число. Это может быть совершенно случайно, непредсказуемо и равномерно, и атака все равно будет работать. Во-вторых, раздача миллиона звонков в PRNG(6)
. Это может быть совершенно одинаково, и атака все равно будет работать. В-третьих, распределение колод, выбранных псевдослучайным процессом, который я описал. Это распределение крайне плохое; только небольшая часть возможных колод IRL может быть выбрана. Атака зависит от предсказуемости поведения PRNG, основанного на частичном знании его выхода.
ASIDE: эта атака требует, чтобы злоумышленник знал или мог угадать, какой именно алгоритм используется PRNG. Реалистично это или нет, остается открытым вопросом. Однако при разработке системы безопасности вы должны спроектировать ее защищенной от атак, даже если злоумышленник знает все алгоритмы в программе. Другими словами, часть системы безопасности, которая должна оставаться секретной, чтобы система была защищенной, называется "ключом". Если ваша система в своей безопасности зависит от алгоритмов, которые вы используете в качестве секрета, тогда ваш ключ содержит эти алгоритмы. Это чрезвычайно слабая позиция, чтобы быть в!
Двигаемся дальше.
Теперь давайте предположим, что у нас есть третий волшебный ящик, помеченный CPRNG
. Это крипто-версия PRNG
. Требуется 256-разрядное начальное число, а не 32-разрядное. Он разделяет с PRNG
свойство, которое семя выбирает из одного из 2 256 возможных вариантов поведения. И, как и у других наших машин, он обладает тем свойством, что большое количество вызовов CPRNG(n)
приводит к равномерному распределению результатов между 1 и n: каждый происходит 1/n времени. Можем ли мы провести нашу атаку против него?
Наша первоначальная атака требует, чтобы мы сохранили 2 32 отображения из семян в PRNG(52)
. Но 2 256 - намного большее число; совершенно невозможно запускать CPRNG(52)
столько раз и сохранять результаты.
Но предположим, что есть какой-то другой способ получить значение CPRNG(52)
и из этого вывести факт о семени? До сих пор мы были довольно глупы, просто перебирая все возможные комбинации. Можем ли мы заглянуть внутрь волшебной коробки, выяснить, как она работает, и вывести факты о семени на основе результатов?
Нет. Детали слишком сложны для объяснения, но CPRNG разработаны с умом, так что невозможно вывести какой-либо полезный факт о начальном значении из первого выхода CPRNG(52)
или из любого подмножества выходных данных, независимо от их размера.
Хорошо, теперь давайте предположим, что сервер использует CPRNG
для генерации колод. Это нуждается в 256-битном семени. Как он выбирает это семя? Если он выбирает какое-либо значение, которое злоумышленник может предсказать, то внезапно атака снова становится жизнеспособной. Если мы сможем определить, что из 2 256 возможных семян, только четыре миллиарда из них будут выбраны сервером, то мы вернемся к делу. Мы можем провести эту атаку снова, обращая внимание только на небольшое количество семян, которые могут быть получены.
Поэтому сервер должен выполнить работу, чтобы обеспечить равномерное распределение 256-битного числа, то есть каждое возможное начальное число выбирается с вероятностью 1/2 256. По сути, сервер должен вызывать TRNG(2^256)-1
для генерации начального числа для CPRNG
.
Что если я смогу взломать сервер и заглянуть в него, чтобы увидеть, какое семя было выбрано? В этом случае атакующий знает полное прошлое и будущее CPRNG. Автор сервера должен остерегаться этой атаки! (Конечно, если я смогу успешно провести эту атаку, то, возможно, я также могу просто перевести деньги на свой банковский счет напрямую, так что, возможно, это не так интересно. Дело в том, что начальное число должно быть трудно угадываемым секретом, а действительно случайное 256-битное число чертовски трудно угадать.)
Возвращаясь к моему предыдущему замечанию о глубокой защите: 256-битное начальное число является ключом к этой системе безопасности. Идея CPRNG заключается в том, что система защищена, пока ключ защищен; даже если все остальные факты об алгоритме известны, пока вы можете держать ключ в секрете, карты противника непредсказуемы.
Итак, зерно должно быть как секретным, так и равномерно распределенным, потому что, если это не так, мы можем провести атаку. Предполагается, что распределение выходов CPRNG(n)
является равномерным. Как насчет распределения по множеству всех возможных колод?
Вы можете сказать: есть 2 256 возможных последовательностей, выведенных CPRNG, но есть только 2 226 возможных колод. Поэтому существует больше возможных последовательностей, чем колод, так что мы в порядке; каждая возможная колода IRL теперь (с высокой вероятностью) возможна в этой системе. И это хороший аргумент, кроме ...
2 226 является всего лишь приближение 52 !. Разделите это. 2 256/52 ! не может быть целым числом, потому что, с одной стороны, 52! делится на 3, но нет степени двойки! Так как это не целое число , теперь мы имеем ситуацию , когда все палубы возможны, но некоторые палубы более вероятны, чем другие.
Если это не ясно, рассмотрите ситуацию с меньшими числами. Предположим, у нас есть три карты, A, B и C. Предположим, что мы используем PRNG с 8-битным начальным числом, поэтому существует 256 возможных начальных чисел. Есть 256 возможных выходов PRNG(3)
зависимости от начального числа; невозможно, чтобы одна треть из них была A, треть из них - B, а треть - C, потому что 256 не делится поровну на 3. Должен быть небольшой уклон к одному из них.
Аналогично, 52 не делится поровну на 2 256, поэтому должен быть некоторый уклон в сторону некоторых карт в качестве первой выбранной карты и уклон в сторону от других.
В нашей оригинальной системе с 32-битным начальным числом было огромное смещение, и подавляющее большинство возможных колод никогда не создавалось. В этой системе могут быть изготовлены все колоды, но распределение колод все еще некорректно. Некоторые колоды чуть более вероятны, чем другие.
Теперь вопрос: у нас есть атака, основанная на этом недостатке? и ответ на практике, вероятно, нет. CPRNG разработаны так, что если начальное число действительно случайное, то в вычислительном отношении невозможно определить разницу между CPRNG
и TRNG
.
Хорошо, давайте подведем итоги.
Чем отличаются псевдослучайные числа и действительно случайные числа?
Они отличаются уровнем предсказуемости, которую они демонстрируют.
- Истинно случайные числа не предсказуемы.
- Все псевдослучайные числа предсказуемы, если начальное число может быть определено или угадано.
Почему разница важна?
Потому что есть приложения, в которых безопасность системы зависит от непредсказуемости.
- Если для выбора каждой карты используется TRNG, то система недоступна.
- Если для выбора каждой карты используется CPRNG, то система безопасна, если начальное число непредсказуемо и неизвестно.
- Если используется обычный PRNG с небольшим начальным пространством, то система не защищена независимо от того, является ли начальное число непредсказуемым или неизвестным; достаточно малое начальное пространство подвержено атакам грубой силы, которые я описал.
Имеет ли разница какое-то отношение к распределению выхода PRNG?
Равномерность распределения или отсутствие такового для отдельных вызовов RNG(n)
не имеет отношения к описанным мною атакам.
Как мы уже видели, и PRNG
и CPRNG
дают плохое распределение вероятности выбора любой отдельной колоды из всех возможных колод. PRNG
значительно хуже, но у обоих есть проблемы.
Еще один вопрос:
Если TRNG намного лучше, чем CPRNG, что, в свою очередь, намного лучше, чем PRNG, почему кто-то использует CPRNG или PRNG?
Две причины.
Первый: расход. TRNG стоит дорого. Генерировать действительно случайные числа сложно. CPRNG дают хорошие результаты для произвольно большого количества вызовов с одним вызовом TRNG для начального числа. Недостатком является то, что вы должны держать это семя в секрете.
Второе: иногда нам нужна предсказуемость, и все, что нас волнует, это хорошее распределение. Если вы генерируете "случайные" данные в качестве входных данных программы для набора тестов, и это показывает ошибку, было бы хорошо, если запуск набора тестов снова приведет к ошибке!
Я надеюсь, что теперь это намного яснее.
Наконец, если вам понравилось это, то вам может понравиться дальнейшее чтение на тему случайности и перестановок: