Запутывание удостоверения личности - PullRequest
80 голосов
/ 18 декабря 2011

Я ищу способ зашифровать / скрыть целочисленный идентификатор в другое целое число.Точнее, мне нужна функция int F(int x), чтобы

  • x <-> F (x) было взаимно-однозначным соответствием (если x! = Y, F (x)! =F (y))
  • с учетом F (x), легко найти x - поэтому F не является хеш-функцией
  • с учетом x и F (x) трудно / невозможно найтивне F (y), что-то вроде x ^ 0x1234 не будет работать

Для ясности, я не ищу надежного решения для шифрования, это только запутывание.Представьте себе веб-приложение с URL-адресами, такими как example.com/profile/1, example.com/profile/2 и т. Д. Сами профили не являются секретными, но я бы хотел, чтобы случайные вуайеристы не просматривали / выбирали все профили один за другим, поэтому я бы предпочел скрыть ихчто-то вроде example.com/profile/23423, example.com/profile/80980234 и т. д. Хотя токены, хранящиеся в базе данных, могут выполнять работу довольно легко, мне любопытно, есть ли для этого какая-то простая математика.

Одно важное требование, которое я не поняло том, что результаты должны выглядеть "случайными", то есть, если последовательность x,x+1,...,x+n, F(x),F(x+1)...F(x+n) не должна образовывать прогрессию любого вида.

Ответы [ 11 ]

37 голосов
/ 19 декабря 2011

Запутать его с помощью некоторой комбинации из 2 или 3 простых методов:

  • XOR
  • перемешать отдельные биты
  • преобразовать в модульное представление (D.Knuth, Vol2, глава 4.3.2)
  • выберите 32 (или 64) перекрывающихся подмножества битов и битов XOR в каждом подмножестве (биты четности подмножеств)
  • представляют его в числовой системе переменной длиныи перемешать цифры
  • выбрать пару нечетных целых чисел x и y, которые являются мультипликативными инверсиями друг друга (по модулю 2 32 ), затем умножить на x, чтобы скрытьумножить на y для восстановления, все умножения по модулю 2 32 (источник: "Практическое использование мультипликативных инверсий" Эрика Липперта )

Метод числовой системы переменной длины сам по себе не подчиняется вашему требованию «прогрессии».Он всегда производит короткие арифметические прогрессии.Но в сочетании с другим методом он дает хорошие результаты.

То же самое верно для метода модульного представления.

Вот пример кода C ++ для 3 из этих методов.Пример случайных битов может использовать несколько разных масок и расстояний, чтобы быть более непредсказуемым.Другие 2 примера хороши для небольших чисел (просто чтобы дать идею).Они должны быть расширены, чтобы правильно скрыть все целочисленные значения.

// *** Numberic system base: (4, 3, 5) -> (5, 3, 4)
// In real life all the bases multiplied should be near 2^32
unsigned y = x/15 + ((x/5)%3)*4 + (x%5)*12; // obfuscate
unsigned z = y/12 + ((y/4)%3)*5 + (y%4)*15; // restore

// *** Shuffle bits (method used here is described in D.Knuth's vol.4a chapter 7.1.3)
const unsigned mask1 = 0x00550055; const unsigned d1 = 7;
const unsigned mask2 = 0x0000cccc; const unsigned d2 = 14;

// Obfuscate
unsigned t = (x ^ (x >> d1)) & mask1;
unsigned u = x ^ t ^ (t << d1);
t = (u ^ (u  >> d2)) & mask2;
y = u ^ t ^ (t << d2);

// Restore
t = (y ^ (y >> d2)) & mask2;
u = y ^ t ^ (t << d2);
t = (u ^ (u >> d1)) & mask1;
z = u ^ t ^ (t << d1);

// *** Subset parity
t = (x ^ (x >> 1)) & 0x44444444;
u = (x ^ (x << 2)) & 0xcccccccc;
y = ((x & 0x88888888) >> 3) | (t >> 1) | u; // obfuscate

t = ((y & 0x11111111) << 3) | (((y & 0x11111111) << 2) ^ ((y & 0x22222222) << 1));
z = t | ((t >> 2) ^ ((y >> 2) & 0x33333333)); // restore
9 голосов
/ 19 декабря 2011

Вы хотите, чтобы преобразование было обратимым, а не очевидным.Это похоже на шифрование, которое принимает число в данном диапазоне и производит другое число в том же диапазоне.Если ваш диапазон 64-битных чисел, используйте DES.Если ваш диапазон 128-битных чисел, используйте AES.Если вам нужен другой диапазон, тогда лучше всего делать ставку на Шифр ​​Hasty Pudding , который рассчитан на разные размеры блоков и диапазоны номеров, которые не вписываются в блок, например, от 100 000 до 999 999.

6 голосов
/ 19 декабря 2011

Запутывание на самом деле недостаточно для обеспечения безопасности.

Однако, если вы пытаетесь помешать случайному наблюдателю, я бы порекомендовал комбинацию из двух методов:

  • Закрытый ключ, который вы комбинируете с идентификатором, хранив их вместе
  • Вращение битов на определенную величину как до, так и после ключа был применен

Вот пример (с использованием псевдокода):

  def F(x)
    x = x XOR 31415927       # XOR x with a secret key
    x = rotl(x, 5)           # rotate the bits left 5 times
    x = x XOR 31415927       # XOR x with a secret key again
    x = rotr(x, 5)           # rotate the bits right 5 times
    x = x XOR 31415927       # XOR x with a secret key again
    return x                 # return the value
  end

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

5 голосов
/ 23 декабря 2013

Я нашел этот фрагмент кода Python / PHP очень полезным:

2 голосов
/ 05 апреля 2019

Я написал некоторый JS-код, используя некоторые идеи из этой темы:

const BITS = 32n;
const MAX = 4294967295n;
const COPRIME = 65521n;
const INVERSE = 2166657316n;
const ROT = 6n;
const XOR1 = 10296065n; 
const XOR2 = 2426476569n;


function rotRight(n, bits, size) {
    const mask = (1n << bits) - 1n;
    // console.log('mask',mask.toString(2).padStart(Number(size),'0'));
    const left = n & mask;
    const right = n >> bits;
    return (left << (size - bits)) | right;
}

const pipe = fns => fns.reduce((f, g) => (...args) => g(f(...args)));

function build(...fns) {
    const enc = fns.map(f => Array.isArray(f) ? f[0] : f);
    const dec = fns.map(f => Array.isArray(f) ? f[1] : f).reverse();

    return [
        pipe(enc),
        pipe(dec),
    ]
}

[exports.encode, exports.decode] = build(
    [BigInt, Number],
    [i => (i * COPRIME) % MAX, i => (i * INVERSE) % MAX],
    x => x ^ XOR1,
    [x => rotRight(x, ROT, BITS), x => rotRight(x, BITS-ROT, BITS)],
    x => x ^ XOR2,
);

Это дает хорошие результаты, такие как:

1 1352888202n 1 'mdh37u'
2 480471946n 2 '7y26iy'
3 3634587530n 3 '1o3xtoq'
4 2225300362n 4 '10svwqy'
5 1084456843n 5 'hxno97'
6 212040587n 6 '3i8rkb'
7 3366156171n 7 '1jo4eq3'
8 3030610827n 8 '1e4cia3'
9 1889750920n 9 'v93x54'
10 1017334664n 10 'gtp0g8'
11 4171450248n 11 '1wzknm0'
12 2762163080n 12 '19oiqo8'
13 1621319561n 13 'qtai6h'
14 748903305n 14 'cdvlhl'
15 3903018889n 15 '1sjr8nd'
16 3567473545n 16 '1mzzc7d'
17 2426613641n 17 '144qr2h'
18 1554197390n 18 'ppbudq'
19 413345678n 19 '6u3fke'
20 3299025806n 20 '1ik5klq'
21 2158182286n 21 'zoxc3y'
22 1285766031n 22 'l9iff3'
23 144914319n 23 '2ea0lr'
24 4104336271n 24 '1vvm64v'
25 2963476367n 25 '1d0dkzz'
26 2091060108n 26 'ykyob0'
27 950208396n 27 'fpq9ho'
28 3835888524n 28 '1rfsej0'
29 2695045004n 29 '18kk618'
30 1822628749n 30 'u559cd'
31 681777037n 31 'b9wuj1'
32 346231693n 32 '5q4y31'

Тестирование с помощью:

  const {encode,decode} = require('./obfuscate')

  for(let i = 1; i <= 1000; ++i) {
        const j = encode(i);
        const k = decode(j);
        console.log(i, j, k, j.toString(36));
   }

XOR1 и XOR2 - это просто случайные числа от 0 до MAX.MAX - это 2**32-1;Вы должны установить это значение, как вы думаете, вашим самым высоким ID будет.

COPRIME - это число, взаимно простое с / MAXдумаю, что сами простые числа взаимно просты с любым другим числом (кроме кратных самим себе).

INVERSE - сложная задача для выяснения.Эти сообщения в блоге не дают прямого ответа, но WolframAlpha может понять это для вас .По сути, просто решите уравнение (COPRIME * x) % MAX = 1 для x.

Функция build - это то, что я создал, чтобы упростить создание этих конвейеров кодирования / декодирования.Вы можете кормить столько операций, сколько хотите, [encode, decode] парами.Эти функции должны быть равными и противоположными.Функции XOR являются их собственными комплиментами, поэтому вам здесь не нужна пара.


Вот еще одна забава involution :

function mixHalves(n) {
    const mask = 2n**12n-1n;
    const right = n & mask;
    const left = n >> 12n;
    const mix = left ^ right;
    return (mix << 12n) | right;
}

(предполагает24-разрядные целые числа - просто измените числа на любой другой размер)

2 голосов
/ 19 декабря 2011

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

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

2 голосов
/ 19 декабря 2011

Сделайте что-нибудь с битами идентификатора, которые не уничтожат их.Например:

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

Длярасшифруйте, сделайте все это в обратном порядке.

Создайте программу, которая «зашифрует» некоторые интересные для вас значения и поместит их в таблицу, которую вы можете проверить.Попросите ту же программу ПРОВЕРИТЬ свою подпрограмму шифрования / дешифрования СО всем набором значений, которые вы хотите иметь в своей системе.

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

Для чего-либо еще получите копию The Book .

1 голос
/ 19 декабря 2011

Если xor приемлемо для всего, кроме вывода F(y) с учетом x и F(x), тогда я думаю, что вы можете сделать это с солью .Сначала выберите секретную одностороннюю функцию.Например S(s) = MD5(secret ^ s).Тогда F(x) = (s, S(s) ^ x), где s выбирается случайным образом.Я написал это как кортеж, но вы можете объединить две части в целое число, например F(x) = 10000 * s + S(s) ^ x.Для расшифровки снова извлекается соль s и используется F'(F(x)) = S(extract s) ^ (extract S(s)^x).С учетом x и F(x) вы можете увидеть s (хотя он слегка запутан) и вы можете сделать вывод S(s), но для какого-то другого пользователя y с другой случайной солью t пользователь знает F(x)не могу найти S(t).

1 голос
/ 19 декабря 2011

Создайте закрытый симметричный ключ для использования в вашем приложении и зашифруйте его целым числом. Это удовлетворит все три требования, включая самое сложное # 3: нужно будет угадать ваш ключ, чтобы сломать вашу схему.

1 голос
/ 19 декабря 2011

То, что вы здесь описываете, похоже на противоположность односторонней функции: ее легко инвертировать, но очень сложно применять.Одним из вариантов будет использование стандартного стандартного алгоритма шифрования с открытым ключом, в котором вы фиксируете (секретный, случайно выбранный) открытый ключ, который храните в секрете, и закрытый ключ, которым вы делитесь с миром.Таким образом, вашей функцией F (x) будет шифрование x с использованием открытого ключа.Затем вы можете легко расшифровать F (x) обратно к x, используя закрытый ключ дешифрования.Обратите внимание, что роли открытого и закрытого ключей здесь поменялись местами - вы предоставляете всем закрытый ключ, чтобы они могли расшифровать функцию, но сохранили секретный ключ на вашем сервере.Таким образом:

  1. Функция является биекцией, поэтому она обратима.
  2. При условии F (x), x эффективно вычислима.
  3. При заданных x и F (x), чрезвычайно сложно вычислить F (y) из y, поскольку без открытого ключа (при условии, что вы используете криптографически надежную схему шифрования) невозможно осуществить шифрование данных, даже если известен секретный ключ дешифрования.

Это имеет много преимуществ.Во-первых, вы можете быть уверены, что криптосистема безопасна, поскольку, если вы используете хорошо зарекомендовавший себя алгоритм, такой как RSA, вам не нужно беспокоиться о случайной незащищенности.Во-вторых, для этого уже есть библиотеки, поэтому вам не нужно много кодировать и вы можете быть неуязвимы для атак по побочным каналам.Наконец, вы можете сделать так, чтобы любой мог пойти и инвертировать F (x), при этом никто не мог вычислить F (x).

Одна деталь - вам определенно не следует просто использовать стандартный тип int здесь,Даже с 64-битными целыми числами существует так мало возможных комбинаций, что злоумышленник может просто перебрать все, пытаясь инвертировать все, пока не найдет шифрование F (y) для некоторого y, даже если у него нет ключа.Я бы предложил использовать что-то вроде 512-битного значения, так как даже атака научной фантастики не смогла бы перебором.

Надеюсь, это поможет!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...