6

设置

鉴于以下情况:

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

是否有可能知道,对于长度为 1 的字符串,可能的长度范围为 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加密方法里面还可以包含一个/.

当包含 a 的值/是 JSON 编码时,将/转义为\\\/每次出现额外添加 3 个字符。

真正的问题 - 可以$iv包含$value多个“/”吗?

4

3 回答 3

3

真正的问题 - $iv 和 $value 可以包含多个“/”吗?

当然。IV 的最坏情况是 IV FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF(十六进制),它的 Base64 值为/////////////////////w==.

21 个正斜杠 * 每个额外 3 个字节 = 63 个额外字节。

对于 HMAC-SHA-2-256,您可以获得 32 个字节的 0xFF(最坏情况),即//////////////////////////////////////////8=base64。

42 个正斜杠 => 126 个额外字节。

同样,对于密文,整个输出可能是(但可能不是)FF FF ... FF。所有一个字母输入(无论何种编码)都是一个密文块,使输出/////////////////////w==再次成为(+63)。

最大值的广义公式似乎是

  • 四:24 + 63 = 87
  • HMAC:24 + 63 = 87
  • JSON 属性名称:10
  • JSON结构:19
  • 密文:(ceil(ceil((n+1) / 16) * 16 / 3) * 4 * 4我用作n字节。填充密文是 ceil((n+1) / blocksize) * blocksize,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,因为当 21 是最坏情况时,密文公式将 24 个斜杠计算为最坏情况。因此,真正的上确界需要将密文称为更复杂的东西,包括地板、天花板和减法。

于 2019-02-20T16:32:56.610 回答
3

查看的源代码Crypt::encryptString我们可以看到最终结果将是一个 base64 编码的 JSON 对象,具有以下结构:

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

其中的值x是原始明文中的位数。ceil(n / 128) * 128n

这意味着,对于长度为 1 的输入明文,输出的大小应为:

  • IV (base64) 为 24 个字符。
  • 24 个字符的密文 (base64)。
  • SHA256 mac(十六进制)为 64 个字符。
  • JSON 字段的名称为 10 个字符。
  • 19 个额外的 JSON 字符,例如{, ", :.
  • 整个事物的最后一轮base64编码......(ceil(141 / 3) * 4

总共给出188个。高达 192 的波动是奇怪的 - 您的输入的大小根本没有变化(因为明文应该始终是 0 - 15 长度之间的 16 个字节)。

于 2019-02-18T03:30:21.137 回答
2

请注意,我将把赏金奖励给@Luke Joshua Park,因为他让我最接近最终成为(最接近的)解决方案,这是要遵循的。

(不是)解决方案

答案是,没有具体的答案,并非没有未知数和方差。在撰写本文时正在研究这个问题的三个人(我自己、Luke 和 bartonjs)仍然对 100% 准确的解决方案存在疑问。

提出这个问题是为了找出一种可靠的类型和大小来存储加密数据,理想情况下以独立于数据库的方式(我不想指定特定的数据库,因为我想知道并理解如何计算长度而不管它被坚持的方式)。

然而,在最坏的情况下,即使是最小长度的字符串也会变得很长(其中创建了一个包含许多斜线的随机 $iv - 不太可能或不可能,这是可能的)。可能是 400 字节长的可能加密字符串n=1意味着 avarchar永远不会是正确的答案。

那么……应该怎么办?

因此,无论原始字符串的长度如何,将加密数据存储为文本字段而不是 varchar(在 mysql 领域)似乎是最好、最一致和最可靠的。这是一个令人失望的无聊答案,没有涉及任何花哨的数学。这不是我想接受的答案,但最有意义。

但是,密码呢?

在短暂的愚蠢时刻,我想,但是密码字段呢?那是一个varchar. 但当然这是一个散列值,而不是加密值(当我脑子里冒出这个想法时,我还没有喝足够的咖啡,好吗?)

于 2019-02-24T10:49:51.773 回答