Как создать случайный хэш SHA1 для использования в качестве идентификатора в узле.Джей?



Я использую эту строку для создания идентификатора sha1 для узла.js:



crypto.createHash('sha1').digest('hex');


проблема в том, что он возвращает один и тот же идентификатор каждый раз.



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

804   3  

3 ответов:

посмотреть здесь: Как я могу использовать узел.JS Crypto для создания хэша HMAC-SHA1? Я бы создал хэш текущей метки времени + случайное число, чтобы обеспечить уникальность хэша:

var current_date = (new Date()).valueOf().toString();
var random = Math.random().toString();
crypto.createHash('sha1').update(current_date + random).digest('hex');

243,583,606,221,817,150,598,111,409 x больше энтропии

я бы посоветовал использовать крипто.randomBytes. Это не sha1, но для целей id, это быстрее, и так же, как "случайный".

var id = crypto.randomBytes(20).toString('hex');
//=> f26d60305dae929ef8640a75e70dd78ab809cfe9

результирующая строка будет в два раза длиннее случайных байтов, которые вы генерируете; каждый байт, закодированный в hex, состоит из 2 символов. 20 байт будет 40 символов шестнадцатеричного кода.

используя 20 байт, мы имеем 256^20 или 1,461,501,637,330,902,918,203,684,832,716,283,019,655,932,542,976 уникальные значения выходного. Это одинаковых к 160-битным (20-байтовым) возможным выходам SHA1.

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


почему это лучше?

чтобы понять, почему это лучше, мы сначала должны понять, как работают функции хеширования. Функции хэширования (включая SHA1) всегда будут генерировать один и тот же выход, если задан один и тот же вход.

скажем, мы хотим генерировать идентификаторы, но наш случайный вход генерируется подбрасыванием монеты. У нас есть "heads" или "tails"

% echo -n "heads" | shasum
c25dda249cdece9d908cc33adcd16aa05e20290f  -

% echo -n "tails" | shasum
71ac9eed6a76a285ae035fe84a251d56ae9485a4  -

если "heads" появляется снова, выход SHA1 будет то же самое как это было в первый раз время

% echo -n "heads" | shasum
c25dda249cdece9d908cc33adcd16aa05e20290f  -

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

если мы используем стандартный 6-сторонний штамп, у нас есть 6 возможных входов. Угадайте, сколько возможных выходов SHA1? 6!

input => (sha1) => output
1 => 356a192b7913b04c54574d18c28d46e6395428ab
2 => da4b9237bacccdf19c0760cab7aec4a8359010b0
3 => 77de68daecd823babbb58edb1c8e14d7106e83bb
4 => 1b6453892473a467d07372d45eb05abc2031647a
5 => ac3478d69a3c81fa62e60f5c3696165a4e5e6ac4
6 => c1dfd96eea8cc2b62785275bca38ac261256e278

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

мы оба согласны, что бросок монеты или 6-сторонний штамп сделал бы плохой генератор случайных идентификаторов, потому что наши возможные результаты SHA1 (значение, которое мы используем для идентификатора) очень малы. Но что, если мы используем что-то, что имеет гораздо больше выходов? Как отметка времени с миллисекундами? Или JavaScript Math.random? Или даже сочетание из этих двух?!

давайте посчитаем, сколько уникальных идентификаторов мы получим ...


уникальность метки времени с миллисекундами

когда используя (new Date()).valueOf().toString(), вы получаете 13-символьный номер (например,1375369309741). Однако, поскольку это последовательно обновляемое число (один раз в миллисекунду), выходы почти всегда одинаковы. Давайте посмотрим

for (var i=0; i<10; i++) {
  console.log((new Date()).valueOf().toString());
}
console.log("OMG so not random");

// 1375369431838
// 1375369431839
// 1375369431839
// 1375369431839
// 1375369431839
// 1375369431839
// 1375369431839
// 1375369431839
// 1375369431840
// 1375369431840
// OMG so not random

чтобы быть справедливым, для сравнения,в данную минуту (щедрое время выполнения операции), вы будете иметь 60*1000 или 60000 uniques.


уникальность Math.random

теперь, когда используя Math.random, из-за того, как JavaScript представляет 64-разрядные числа с плавающей запятой, вы получите число длиной от 13 до 24 символов. Более длинный результат означает больше цифр, что означает больше энтропии. Во-первых, нам нужно выяснить, какая длина наиболее вероятна.

скрипт ниже определит, какая длина наиболее вероятна. Мы делаем это, генерируя 1 миллион случайных чисел и увеличивая счетчик на основе .length каждого число.

// get distribution
var counts = [], rand, len;
for (var i=0; i<1000000; i++) {
  rand = Math.random();
  len  = String(rand).length;
  if (counts[len] === undefined) counts[len] = 0;
  counts[len] += 1;
}

// calculate % frequency
var freq = counts.map(function(n) { return n/1000000 *100 });

разделив каждый счетчик на 1 миллион, мы получаем вероятность длины числа, возвращаемого из Math.random.

len   frequency(%)
------------------
13    0.0004  
14    0.0066  
15    0.0654  
16    0.6768  
17    6.6703  
18    61.133  <- highest probability
19    28.089  <- second highest probability
20    3.0287  
21    0.2989  
22    0.0262
23    0.0040
24    0.0004

Итак, даже если это не совсем правда, давайте будем щедрыми и скажем, что вы получаете 19-символьный случайный выход;0.1234567890123456789. Первые символы всегда будут 0 и ., так что на самом деле мы получаем только 17 случайных символов. Это оставляет нас с 10^17+1 (по возможности 0; см. Примечания ниже) или 100,000,000,000,000,001 uniques.


Итак, сколько случайных входов мы можем генерировать?

хорошо, мы рассчитали количество результатов для миллисекундной метки времени и Math.random

      100,000,000,000,000,001 (Math.random)
*                      60,000 (timestamp)
-----------------------------
6,000,000,000,000,000,060,000

это один 6,000,000,000,000,000,060,000-сторонний умереть. Или, чтобы сделать это число более удобоваримым для человека, это примерно то же число, что и

input                                            outputs
------------------------------------------------------------------------------
( 1×) 6,000,000,000,000,000,060,000-sided die    6,000,000,000,000,000,060,000
(28×) 6-sided die                                6,140,942,214,464,815,497,21
(72×) 2-sided coins                              4,722,366,482,869,645,213,696

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

SHA1 выдает 20-байтовое значение, с возможными 256^20 исходами. Так что мы действительно не используем SHA1, чтобы это был полный потенциал. Ну сколько мы используем?

node> 6000000000000000060000 / Math.pow(256,20) * 100

миллисекундная метка времени и математика.random использует только 4,11 e-27 процентов 160-битного потенциала SHA1!

generator               sha1 potential used
-----------------------------------------------------------------------------
crypto.randomBytes(20)  100%
Date() + Math.random()    0.00000000000000000000000000411%
6-sided die               0.000000000000000000000000000000000000000000000411%
A coin                    0.000000000000000000000000000000000000000000000137%

елки-палки, мужик! Посмотри на все эти нули. Так насколько же лучше crypto.randomBytes(20)? 243,583,606,221,817,150,598,111,409 раз лучше.


Примечания +1 и частота нулей

Если вам интересно о +1, возможно Math.random возвратить 0 это значит, что есть еще 1 возможный уникальный результат, который мы должны учитывать.

основываясь на обсуждении, которое произошло ниже, мне было любопытно о частоте a 0 придумали бы. Вот небольшой сценарий,random_zero.js, я сделал, чтобы получить некоторые данные

#!/usr/bin/env node
var count = 0;
while (Math.random() !== 0) count++;
console.log(count);

затем я запустил его в 4 потоках (у меня есть 4-ядерный процессор), добавив вывод в файл

$ yes | xargs -n 1 -P 4 node random_zero.js >> zeroes.txt

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

1 in 3,164,854,823 randoms - это 0

круто! Потребуется больше исследований, чтобы узнать, соответствует ли это число равномерному распределению v8 Math.random реализация

сделать это в браузере, тоже !

EDIT: это действительно не вписывается в поток моего предыдущего ответа. Я оставляю его здесь в качестве второго ответа для людей, которые хотели бы сделать это в браузере.

вы можете сделать это на стороне клиента в современных браузерах, если вы хотите

// str byteToHex(uint8 byte)
//   converts a single byte to a hex string 
function byteToHex(byte) {
  return ('0' + byte.toString(16)).slice(-2);
}

// str generateId(int len);
//   len - must be an even number (default: 40)
function generateId(len) {
  var arr = new Uint8Array((len || 40) / 2);
  window.crypto.getRandomValues(arr);
  return [].map.call(arr, byteToHex).join("");
}

Хорошо, давайте проверим это !

generateId();
// "1e6ef8d5c851a3b5c5ad78f96dd086e4a77da800"

generateId(20);
// "d2180620d8f781178840"

требования к браузеру

Browser    Minimum Version
--------------------------
Chrome     11.0
Firefox    21.0
IE         11.0
Opera      15.0
Safari     5.1

Comments

    Ничего не найдено.