Рейтинг:0

можете ли вы сгенерировать идентификационный номер быстро, без коллизий и без раскрытия информации идентификаторами?

флаг ru

Есть ли стандартный способ генерировать идентификационные номера один за другим, чтобы:

  • Вы можете гарантировать или почти гарантировать, что вы избежите столкновений. (Под «почти гарантией» я подразумеваю, например, если вы сгенерировали совершенно случайные 24-значные числа, а вы «только» сгенерировали 1 миллион из них, то даже с учетом парадокса дня рождения вероятность столкновения будет небольшой.)
  • Вы хотите, чтобы идентификационные номера были короткими, а не громоздкими — в частности, вы не хотите полагаться на длину идентификационного номера (и выбор случайных значений), чтобы избежать коллизий, как описано в предыдущем пункте. Вы должны сделать это как-то по-другому.
  • Вы не хотите избегать коллизий каждый раз, когда создаете новое значение, просматривая все ранее существовавшие значения, чтобы увидеть, не использовалось ли оно уже. Это будет только поиск log(n) каждый раз в отсортированном списке, но предположим, что я все равно хочу этого избежать.
  • Вы не хотите, чтобы идентификационный номер раскрывал какую-либо информацию о том, когда он был сгенерирован, или о том, сколько идентификационных номеров было сгенерировано между идентификационным номером X и идентификационным номером Y. Без этого условия проблема тривиальна; вы можете просто использовать время по часам (плюс некоторое случайное значение, достаточно большое, чтобы избежать коллизий между числами, сгенерированными в одном и том же значении времени по часам), или вы можете просто использовать последовательные целые числа (за исключением того, что теперь злоумышленник знает, что если кто-то сгенерирует значение идентификатора 5000 на 1 марта и значение ID 6000 1 апреля, между ними было сгенерировано 1000 других значений).

Я пытался найти тривиальный ответ, но ни один из тех, что я пробовал, не работал. Вы можете просто взять хэш SHA-256 чисел 1, 2, 3 и т. д. (плюс некоторый секретный ключ), но это та же проблема, что и выбор случайных чисел из доступного пространства — если вы полагаетесь на длина хэша (например, SHA-256), чтобы избежать коллизий, результирующие идентификационные номера будут длинными и громоздкими, и если вы сделаете хеш короче, вы увеличите вероятность коллизий.

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

Единственное решение, которое я смог найти, заключается в следующем: во-первых, заранее проведите некоторую подготовительную работу. Для любого количества значений ID, которые вы ожидаете сгенерировать (скажем, 1 миллион), возьмите все целые числа от 1 до 1 миллиона и по порядку начните вычислять хэш каждого целого числа плюс секретный ключ. Обрежьте хеш до любого значения, которое вы считаете достаточно коротким. Но при достаточно коротком усечении вы ожидаете увидеть коллизии. Таким образом, каждый раз, когда вы генерируете новый усеченный хэш для заданного целого числа, проверяйте его на соответствие ранее сгенерированным значениям и, если есть коллизия, добавляйте это целое число в список L целых чисел, где хэш этого целого числа сталкивается с хэшем меньшего целого числа. (На самом деле, если вы планируете сгенерировать 1 миллион идентификаторов, во время его подготовительной работы вам придется пройтись немного последним 1 миллионом целых чисел, чтобы компенсировать те, которые вы пропустили.)

Затем во время выполнения, когда вы генерируете свои идентификаторы, вы просто начинаете с целочисленного счетчика. Каждый раз, когда вы создаете новый идентификатор, вы увеличиваете целое число и проверяете, есть ли оно в вашем списке L, и если да, вы пропускаете и переходите к следующему целому числу. (Это включает в себя «поиск в журнале n», что, по-видимому, нарушает одно из моих заявленных правил, но что я действительно хотел сделать, так это избежать необходимости сверять каждое новое значение идентификатора с каждым сгенерированным до сих пор значением; проверка L должна быть намного быстрее.) И вы можете настроить это для компромиссов (чем длиннее вы делаете усеченные хэши, тем короче будет L и, следовательно, тем короче будет проверка каждый раз, когда вы создаете новый идентификатор; но более длинные значения идентификатора могут быть нежелательными).

Но это похоже на взлом. Есть стандартный способ? Если нет, можете ли вы придумать лучший способ?

kelalaka avatar
флаг in
Для фиксированного ключа шифруйте в режиме AES-ECB. AES представляет собой семейство перестановок, и ключ выбирает одну из них. Кроме того, я бы выбрал SHA-512, SHAKE или BLAKE-512 и т. д. Не ожидается столкновения, однако, как только вы его найдете, вы станете известным!
poncho avatar
флаг my
@kelalaka: SHA-512? Итак, вы предлагаете 512-битный (154-значный) идентификатор???
kelalaka avatar
флаг in
Как вы думаете, можно ли иметь более $2^{100}$ идентификаторов?
poncho avatar
флаг my
@kelalaka: если вы полагаетесь на устойчивость к коллизиям SHA-512, вам нужно вывести полный хеш - усеченный хэш не будет означать коллизию в исходной хеш-функции.
Bennett avatar
флаг ru
Проблема с использованием любого вида хэша для генерации идентификатора — это та же проблема, что и с использованием случайных чисел, как описано в постановке задачи — если вы избегаете коллизий, просто делая их длинными, это слишком громоздко, и если вы усекаете их до сделать их короче, вы увеличиваете вероятность столкновения. Я ищу способ избежать столкновений, не используя просто очень длинные значения.
Рейтинг:2
флаг my

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

Использовать его было бы просто: вы бы выбрали случайный ключ и сохранили его; вы также сохраните порядковый номер (в том же формате, что и вывод, например, вы можете начать с 0000000000). Затем, когда приходит время генерировать следующий идентификатор, вы увеличиваете порядковый номер и отправляете его через алгоритм FPE; это ваш следующий идентификатор [1].

Поскольку алгоритм FPE представляет собой перестановку, вы никогда не сгенерируете один и тот же идентификатор дважды, пока не произойдет перенос порядкового номера; следовательно, никаких столкновений. Вы можете сделать идентификатор настолько коротким, насколько это необходимо (текущие алгоритмы FPE имеют проблемы с очень маленькими пробелами; если вы сохраните идентификатор, скажем, не менее 6 цифр, вы будете в безопасности). А поскольку алгоритмы FPE безопасны, любой идентификатор не предоставляет никакой информации о любом другом идентификаторе (включая относительный порядок генерации).

Недостаток: нет (насколько мне известно) общедоступных библиотек FPE. Для использования я бы предложил FF1 из этого документа; реализовать это с нуля было бы немного работы (но удовлетворило бы ваши потребности).


Менее эффективным, но более простым в реализации методом было бы сделать вариант того, что вы предложили: сохранить список идентификаторов, которые вы еще не назначили.

Здесь во время установки вы должны инициализировать список всех возможных идентификаторов в последовательном порядке, скажем, от 000000 до 999999. Кроме того, вы должны установить значение N для самого высокого неназначенного идентификатора (в этом примере N = 999999).

Затем, когда придет время выдавать новый идентификатор, вы должны выбрать случайное число x от 0 до N (включительно). Затем вы должны поменять местами идентификаторы с индексами N и x (и если x=N, то эта операция обмена ничего не делает); затем вы должны вывести значение, которого нет в индексе N (а затем уменьшить N).

Вот и все; это на самом деле Перетасовка Фишера-Йейтса, вы можете сделать это по запросу (как я написал) или вы можете перетасовать все во время установки (и просто читать что-то из списка при создании идентификаторов).

Это менее эффективно, чем идея FPE — как из-за более сложной настройки, так и из-за того, что вам нужно иметь большой список идентификаторов. С другой стороны, это намного проще реализовать, чем пытаться реализовать FF1 с нуля...


[1]: Алгоритмы шифрования с сохранением формата также требуют «настройки»; вы хотели бы сохранить это фиксированное значение (например, пустая строка); настройка предоставляет услугу, которая не требуется для вашего конкретного использования.

Ответить или комментировать

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