Как рассчитать максимальную длину вывода метода шифрования Laravel?

Настроить

Учитывая следующее:

$s = Crypt::encryptString('a');

Можно ли для строки длины 1 знать возможный диапазон длин $s?

контекст

Хранилище базы данных - необходимо хранить зашифрованное значение и хотеть установить проверку входной строки, чтобы входная строка самой длинной длины при шифровании вставлялась в БД без усечения.

Основные тесты

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

Route::get('/test', function() {
    echo '<table>';
    for ($i=0; $i < 100; $i++) { 
        $s = str_repeat('a', $i);
        $l1 = strlen($s);
        $l2 = strlen(Crypt::encryptString($s));
        echo "<tr><td>$l1</td><td>$l2</td></tr>";
    }
    echo '</table>';
});

Я вижу следующее, но оно варьируется между прогонами, например, строка 'a' будет иметь длину либо 188, либо 192 (более длинные значения кажутся между 244 и 248).

Так что должна быть формула. я видел output_size = input_size + (16 - (input_size % 16)) но не учитывает дисперсию.

Выход

0   192
1   188
2   188
3   192
4   188
5   188
6   188
7   192
8   192
9   188
10  188
11  192
12  192
13  192
14  192
15  192
16  220
17  220
18  216
19  216
20  220

редактировать

Итак, после чата с @Luke Joshua Park ниже, разница в длине зависит от функции шифрования Laravel и способа $iv создается, который является случайным байтом, который может содержать /,

$value внутри метода шифрования также может содержать /,

Когда значения, которые содержат / в кодировке JSON, / сбежал в \\\/ добавив дополнительные 3 символа в каждом случае.

Настоящая проблема - может $iv а также $value содержать более одного '/'?

3 ответа

Решение

Заметьте, я собираюсь присудить награду @Luke Joshua Park, так как он дал мне самое близкое к тому, что в итоге оказалось (самым близким к) решением, которому нужно следовать.

(Не а) решение

Ответ таков: конкретного ответа не существует, не обошлось без неизвестности и дисперсии. Три человека, которые смотрели на это во время написания статьи (я, Люк и Бартонж), все еще сомневались в 100% точном решении.

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

Однако даже строки наименьшей длины оказались довольно длинными в худшем случае (где был создан случайный $iv, содержащий много слэшей - вряд ли это было или нет, это было возможно). Возможные зашифрованные строки n=1 возможно, длина 400 байт означает, что varchar никогда не будет правильным ответом.

Так что должно быть сделано?

Таким образом, вместо этого кажется лучшим, наиболее последовательным и наиболее надежным хранить зашифрованные данные в виде текстового поля, а не varchar (на земле mysql), независимо от длины исходной строки. Это удручающе скучный ответ без какой-либо сложной математики. Это не тот ответ, который я хотел бы принять, но он имеет смысл.

Но как насчет паролей?

В недолгую глупость подумал я, а как же поле пароля? Это varchar, Но, конечно, это хешированное значение, а не зашифрованное значение (мне не хватило кофе, когда эта мысль пришла мне в голову, хорошо?)

Реальная проблема - могут ли $iv и $value содержать более одного символа '/'?

Конечно. Ваш худший случай для IV это IV FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF (hex), значение Base64 которого равно /////////////////////w==,

21 косая черта * дополнительные 3 байта каждый = 63 дополнительных байта.

Для HMAC-SHA-2-256 вы можете получить 32 байта 0xFF (наихудший случай), что //////////////////////////////////////////8= в base64.

42 косых черты => 126 дополнительных байтов.

Для зашифрованного текста, опять же, весь вывод может быть (но, скорее всего, нет) FF FF ... FF, Все однобуквенные вводы (независимо от того, какая кодировка) представляют собой один блок зашифрованного текста, что делает вывод /////////////////////w== снова (+63).

Обобщенная формула для максимума представляется

  • IV: 24 + 63 = 87
  • HMAC: 24 + 63 = 87
  • Названия свойств JSON: 10
  • Структура JSON: 19
  • зашифрованный: ceil(ceil((n+1) / 16) * 16 / 3) * 4 * 4 (Я использовал n в байтах. дополненный зашифрованный текст - ceil((n+1) / blockize) * размер блока, base64 - 4 * ceil(data / 3), дополнительный *4 - "все слэши")
  • Base64 это все снова: 4 * ceil(sum / 3)

знак равно 4 * ceil((4 * 4 * ceil(16 * ceil((n + 1) / 16) / 3) + 203) / 3)

За n=1 это производит 400 байтов. Фактический максимум - (я думаю) 388, потому что формула зашифрованного текста считает 24 косых черты как наихудший случай, а 21 - худший. Таким образом, истинный супремум должен называть зашифрованный текст чем-то более сложным, включающим пол, потолок и вычитание.

Просматривая исходный код для Crypt::encryptStringмы видим, что конечным результатом будет объект JSON в кодировке base64, имеющий следующую структуру:

{ "iv": "<128 bits in base64>", "value": "<x bits in base64>", "mac": "<256 bits in hex>" }

Где значение x является ceil(n / 128) * 128 где n это количество бит в исходном текстовом формате.

Это означает, что для входного открытого текста длиной 1 размер вывода должен быть:

  • 24 символа для IV (base64).
  • 24 символа для зашифрованного текста (base64).
  • 64 символа для SHA256 mac (hex).
  • 10 символов для имен полей JSON.
  • 19 символов дополнительных символов JSON, например {, ", :,
  • Последний раунд кодирования base64 всего этого... (ceil(141 / 3) * 4)

Дает в общей сложности 188. Колебания до 192 странные - ваши входные данные вообще не меняются по размеру (поскольку открытый текст всегда должен быть 16 байтов в диапазоне от 0 до 15).

Другие вопросы по тегам