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

时间:2019-02-18 03:04:39

标签: php laravel encryption aes laravel-encryption

设置

给出以下内容:

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

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

上下文

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

基本测试

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

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 个答案:

答案 0 :(得分:3)

the source code中查找Crypt::encryptString,我们可以看到最终结果将是具有以下结构的base64编码的JSON对象:

{ "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之间)。

答案 1 :(得分:3)

  

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

好的。 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(data / 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是最坏的情况。因此,真正的至高点需要将密文称为更复杂的东西,包括底数,上限和减法。

答案 2 :(得分:2)

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

(不是)解决方案

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

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

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

那...该怎么办?

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

但是,密码呢?

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