我一直在努力实现密文窃取(CTS)在PHP中的CBC。
参考以下两个链接
我怎样才能加密/在PHP中使用AES CBC + CTS(密文窃取)模式解密数据?
和
http://en.wikipedia.org/wiki/Ciphertext_stealing
我很困惑,并停留在XOR的最后一个也是最简单的一步。 我知道这是愚蠢的,但已经尝试了所有的组合,我不知道我失去了什么。 代码如下。
// 1. Decrypt the second to last ciphertext block, using zeros as IV.
$second_to_last_cipher_block = substr($cipher_text, strlen($cipher_text) - 32, 16);
$second_to_last_plain = @mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $second_to_last_cipher_block, MCRYPT_MODE_CBC);
// 2. Pad the ciphertext to the nearest multiple of the block size using the last B-M
// bits of block cipher decryption of the second-to-last ciphertext block.
$n = 16 - (strlen($cipher_text) % 16);
$cipher_text .= substr($second_to_last_plain, -$n);
// 3. Swap the last two ciphertext blocks.
$cipher_block_last = substr($cipher_text, -16);
$cipher_block_second_last = substr($cipher_text, -32, 16);
$cipher_text = substr($cipher_text, 0, -32) . $cipher_block_last . $cipher_block_second_last;
// 4. Decrypt the ciphertext using the standard CBC mode up to the last block.
$cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
mcrypt_generic_init($cipher, $key, $iv);
$plain_text = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $cipher_text, MCRYPT_MODE_CBC , $iv);
// 5. Exclusive-OR the last ciphertext (was already decrypted in step 1) with the second last ciphertext.
// ???
// echo $??? ^ $???;
我发现,具体用例是理解算法非常有帮助。 这里有两个用例,并一步一步的演练。
起点为两个用例。
这些用例假定您解密的邮件使用AES-256 CBC链模式和密文偷了块量化。 产生这些用例中,我使用德尔福2010编译器和TurboPower公司LockBox3库(SVN版本243)。 在下文中,我使用了一个符号,像这样......
IV := [16] 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
...意味着一些名为“IV”变量被分配为等于16个字节的阵列。 最左边的字节,是数组的最低Signficant(最低地址)字节的渲染,而最右边的字节,最显著。 这些字节采用十六进制,所以例如,如果一个放...
X := [2] 03 10
......这意味着LSB是3,最高位为16。
用例一
让AES-256 32字节的压缩密钥(如在AES标准中定义)是...
key = [32] 0D EE 8F 9F 8B 0B D4 A1 17 59 FA 05 FA 2B 65 4F 23 00 29 26 0D EE 8F 9F 8B 0B D4 A1 17 59 FA 05
与TurboPower公司加密箱3,这可以通过设置口令(“UTF8Password”)的TCodec组件的属性来实现...
password = (UTF-8) 'Your lips are smoother than vasoline.'
要发送的明文消息会
Message = (UTF-8) 'Leeeeeeeeeroy Jenkins!'
编码,这是22个字节长。 AES-256具有一个16字节的块大小,因此这是一些-其中块1和2之间的长。
让IV为1(除了:在Delphi的侧上,这可以通过设置实现
TRandomStream.Instance.Seed := 1;
只是加密前)。 因此,为了通过PHP解密所述密文的消息将是(8字节IV前置一拉LockBox3)...
ciphertext = [30] 01 00 00 00 00 00 00 00 17 5C C0 97 FF EF 63 5A 88 83 6C 00 62 BF 87 E5 1D 66 DB 97 2E 2C (base64 equivalent ='AQAAAAAAAAAXXMCX/+9jWoiDbABiv4flHWbbly4s')
打破这个向下进入IV,第一密文块(C [0])和最后一个(部分)密文块(C [1])...
IV = [16] 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 c[0] = [16] 17 5C C0 97 FF EF 63 5A 88 83 6C 00 62 BF 87 E5 c[1] = [6] 1D 66 DB 97 2E 2C
现在,让我们走,通过与窃取密文解密。
CV:IV =
CV = [16] 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
在一般情况下,对于第n块(除了最后2块),我们正常的CBC算法是...
m[n] := Decrypt( c[n]) XOR CV; CV[n+1] := c[n]
哪里:
- m是输出明文块;
- 解密()表示块对AES-256 ECB解密;
- CV是我们随身携带的载体。 的链接模式定义了从块这个变化如何阻止。
但对于第二最后的块(N-1)(N = 2在用例1)时,变换更改...( 此异常由于密文窃取的选择制造 )
m[n] := Decrypt( c[n]) XOR CV; CV[n+1] := CV[n] // Unchanged!
适用于我们的使用案例:
CV = [16] 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 c[0] = [16] 17 5C C0 97 FF EF 63 5A 88 83 6C 00 62 BF 87 E5 Decrypt(c[0]) = [16] 6F 6B 69 6E 73 21 F0 7B 79 F2 AF 27 B1 52 D6 0B m[0] := Decrypt(c[0]) XOR CV = [16] 6E 6B 69 6E 73 21 F0 7B 79 F2 AF 27 B1 52 D6 0B
现在要处理的最后一个块。 它是一个局部的一个,6个字节长。 在一般情况下,最后一个块的处理是这样的......
y := c[N-1] | LastBytes( m[N-2], BlockSize-Length(c[N-1])); m[N-1] := Decrypt( y) XOR CV
申请使用案例一:
c[1] = [6] 1D 66 DB 97 2E 2C y := c[1] | LastBytes( m[0], 10) y = [16] 1D 66 DB 97 2E 2C F0 7B 79 F2 AF 27 B1 52 D6 0B Decrypt( y) = [16]= 4D 65 65 65 65 65 65 65 65 65 72 6F 79 20 4A 65 m[1] := Decrypt(y) XOR CV m[1] = [16] 4C 65 65 65 65 65 65 65 65 65 72 6F 79 20 4A 65
在解密过程中的最后一步是最后两个块的发射。 我们顺序颠倒,发光M [N-1]首先,然后发射的M [N-2]的第一部分(其长度等于C的长度[N-1])。 申请使用案例一...
发射M [1]
m[1] = [16] 4C 65 65 65 65 65 65 65 65 65 72 6F 79 20 4A 65
发射前6个字节中的m [0]
FirstBytes( m[0], 6) = 6E 6B 69 6E 73 21
干脆把它,我们得到的重建明文...
[22] 4C 65 65 65 65 65 65 65 65 65 72 6F 79 20 4A 65 6E 6B 69 6E 73 21
这是的UTF-8编码'Leeeeeeeeeroy詹金斯!
用案例二
在这种使用情况下,该消息是精确地2块长。 这就是所谓的圆形表壳。 在圆形的情况下,不存在部分块quantise,所以它前进,好像它是正常CBC。 密码,密钥和IV是一样的用例之一。 要解密密文的消息(包括预谋字节8 IV)是...
设定
Ciphertext = [40] 01 00 00 00 00 00 00 00 70 76 12 58 4E 38 1C E1 92 CA 34 FB 9A 37 C5 0A 75 F2 0B 46 A1 DF 56 60 D4 5C 76 4B 52 19 DA 83 which is encoded base64 as 'AQAAAAAAAABwdhJYTjgc4ZLKNPuaN8UKdfILRqHfVmDUXHZLUhnagw=='
这种分解成四,第一块和第二块,是这样的...
IV = [16] 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 c[0] = [16] 70 76 12 58 4E 38 1C E1 92 CA 34 FB 9A 37 C5 0A c[1] = [16] 75 F2 0B 46 A1 DF 56 60 D4 5C 76 4B 52 19 DA 83
通用和2最后一块
Decrypt(c[0]) = [16] 45 61 6E 63 65 20 74 68 65 6E 2C 20 77 68 65 72 m[0] := Decrypt(c[0]) XOR CV = [16] 44 61 6E 63 65 20 74 68 65 6E 2C 20 77 68 65 72 Next CV := c[0] = [16] 70 76 12 58 4E 38 1C E1 92 CA 34 FB 9A 37 C5 0A
最后块:
我们的最后一块是圆的这种使用情况。
Decrypt(c[1]) = [16] 75 F2 0B 46 A1 DF 56 60 D4 5C 76 4B 52 19 DA 83 m[1] := Decrypt(c[1]) XOR CV = [16] 65 65 76 65 72 20 79 6F 75 20 6D 61 79 20 62 65
在解密过程中的最后一步是最后两个块的发射。 在圆形的情况下,我们不会颠倒顺序。 我们发射M [N-2],然后再M [N-1]。 申请使用案例二...
发射M [0]
m[0] = [16] 44 61 6E 63 65 20 74 68 65 6E 2C 20 77 68 65 72
发射整个的米1
m[1] = [16] 65 65 76 65 72 20 79 6F 75 20 6D 61 79 20 62 65
干脆把它,我们得到的重建明文...
[32] 44 61 6E 63 65 20 74 68 65 6E 2C 20 77 68 65 72 65 65 76 65 72 20 79 6F 75 20 6D 61 79 20 62 65
这是的UTF-8编码舞然后,徘徊无论你可以是“
边缘情况来考虑。 有两种极端情况,而不是由这里提供的两个用例说明。
在短消息的情况下,在技术上人们可以仍然通过使用所述IV作为密文的块之前的密文实施窃取。 但是,恕我直言,这种使用密文偷的,通过这种方式,不会由于缺乏研究正当插入加密强度的影响,更不用说增加实现复杂度。 在TurboPower公司加密箱3中,当消息是短消息,并且链接模式不是关键-流之一,则链接模式被视为CFB-8位。 CFB-8位是一个关键的流模式。
在零长度的消息,它很简单的情况。 零长度的明文消息映射一个-to-one的零长度的密文的消息。 不需要IV,生成也不前缀。 这种映射是独立链接模式和加密(在块模式密码的情况下)的。
关于PHP实现说明
警告
我不是一个PHP程序员。 我不知道PHP。 我在这里说的任何事情应采取与一粒盐。
字节数组
它看起来像你正在使用PHP字符串存储字节数组。 这看起来很危险给我。 如果字节值之一是零? 那会缩短字符串? 如何将strlen的在这种情况下()的行为? 如果PHP这是一个字节数组的本地数据类型,那么这很可能是更安全的。 但我真的不知道。 我只是把这个点给你的注意,如果你是不是已经意识到这一点。 也许它不是一个真正的问题。
mcrypt_decrypt库
我不熟悉这个库。 它原生支持的密文偷了什么? 我认为不是。 因此,有你们两个可能的策略。
调用库的解密所有但CBC模式中的最后两个街区。 过程我已经向你描述的最后两个街区。 但是,这需要访问的简历。 API是否公开此? 如果不是,这种策略是不是你一个可行的选择。
调用库的解密所有但ECB模式中的最后两个街区,并滚动你的CBC链接。 非常容易实现,并定义,你可以访问的简历。
如何做XOR在PHP
有些人都不张贴了这个问题的答案,但目前已收回它。 但他是对的。 它看起来像做在PHP的XOR字节数组上,通过人物,一个个迭代,并做了字节级别XOR。 该技术显示在这里 。
我一直在寻找为Perl了类似的回答。 Perl的库仅限于CBC模式。 这里就是我得到了CTS使用AES 256 CBC模式和CTS方法3.我想这可能是有用的PHP以及工作。
这里的实际NIST文档。 文件ID:NIST800-38A CBC-CS3名称:建议行动的分组密码模式; 三密文的变种偷窃CBC模式来源: http://csrc.nist.gov/publications/nistpubs/800-38a/addendum-to-nist_sp800-38A.pdf
下面的代码...
use Crypt::CBC;
use Crypt::Cipher::AES;
my $key = pack("H*","0000000000000000000000000000000000000000000000000000000000000000");
my $iv = pack("H*","00000000000000000000000000000000");
my $pt = pack("H*","0000000000000000000000000000000000");
my $ct = aes256_cbc_cts_decrypt( $key, $iv, $pt );
#AES 256 CBC with CTS
sub aes256_cbc_cts_decrypt {
my ($key, $iv, $in) = @_;
my $len_in_bytes = length(unpack("H*", $in)) / 2;
my $in_idx = 0;
my $null_iv = pack( "H32", "00000000000000000000000000000000");
my $cipher = Crypt::CBC->new(
-key => $key,
-iv => $null_iv,
-literal_key => '1',
-keysize => 32,
-blocksize => 16,
-header => 'none',
-cipher => 'Crypt::Cipher::AES');
my $out;
while ( $len_in_bytes >= 16 )
{
my $tmp = substr($in, $in_idx, 16);
my $outblock = $cipher->decrypt($tmp);
if ( ( ($len_in_bytes % 16) eq 0 ) || ( $len_in_bytes > 32 ) )
{
$outblock = $outblock ^ $iv;
$iv = $tmp;
}
$out .= $outblock;
$in_idx += 16;
$len_in_bytes -= 16;
}
if ($len_in_bytes) {
my $tmp = substr($in,$in_idx,$len_in_bytes);
my $out_idx = $in_idx - 16;
$tmp .= substr($out,$out_idx + $len_in_bytes, 16 - $len_in_bytes);
$out .= substr($out, $out_idx, $len_in_bytes) ^ substr($tmp, 0, $len_in_bytes);
substr($out,$out_idx,16) = $iv ^ $cipher->decrypt($tmp);
}
return $out;
}