关于php:如何计算Laravel加密方法输出的最大长度?

How to calculate the maximum length of the output of the Laravel encryption method?

本问题已经有最佳答案,请猛点这里访问。

设置

给出以下内容:

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

对于长度为1的字符串是否可以知道$s的可能范围?

语境

数据库存储-需要存储加密的值,并希望设置输入字符串的验证,因此最长长度的输入字符串在加密后可以不截断地插入db中。

基本测试

使用以下代码片段在本地运行一些非常原始的测试:

1
2
3
4
5
6
7
8
9
10
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)),但没有考虑到差异。

输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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可以包含多个'/'吗?


The real problem - can $iv and $value contain more than a single '/'?

好的。 IV的最坏情况是IV FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF(十六进制),其Base64值为/////////////////////w==

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

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

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)/块大小)*块大小,base64是4 * ceil(数据/ 3),多余的* 4是"一切都是斜杠")
  • 再次使用Base64:4 * ceil(sum / 3)

= 4 * ceil((4 * 4 * ceil(16 * ceil((n + 1) / 16) / 3) + 203) / 3)

对于产生400字节的n=1。实际的最大值是(我认为)是388,因为密文公式将24个斜杠视为最坏的情况,而21是最坏的情况。因此,真正的至高点需要将密文称为更复杂的东西,包括底数,上限和减法。


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

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

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

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

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

总共给出188。最大波动是192,这是奇怪的-输入的大小完全没有变化(因为纯文本应始终为16字节,介于0到15之间)。


请注意,我将向@Luke Joshua Park颁发赏金,因为他让我最接近最终成为(最接近a)解决方案的方法。

(不是)解决方案

答案是,没有具体的答案,不是没有未知数和方差。在撰写本文时,三个人(我自己,Luke和bartonjs)都在关注这一点,但对于100%准确的解决方案仍然存在疑问。

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

但是,在最坏的情况下,即使是最小长度的字符串也很长(创建包含许多斜杠的随机$ iv -不可能或不可能)。 n=1的可能加密字符串(可能为400字节长)意味着varchar将永远不是正确的答案。

那么...该怎么办?

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

但是,密码呢?

在短暂的愚蠢时刻,我想,但是密码字段呢?那是一个varchar。但这当然是一个哈希值,而不是一个加密值(当那个想法突然冒出来时,我还没有喝咖啡,好吗?)