问题描述
我一直试图在PHP中为CBC实现Ciphertext窃取(CTS)。
参考下面的两个链接
和
我困惑,坚持在XOR的最后一个最简单的步骤。
我知道这是愚蠢的,但尝试了所有的组合,我不知道我是什么失踪。
以下代码。
解密第二个到最后一个密文块,使用零作为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.使用第二个到最后一个密文块的块密码解密的最后一个B-M
//位将密文填充到块大小的最接近的倍数。
$ n = 16 - (strlen($ cipher_text)%16);
$ cipher_text。= substr($ second_to_last_plain, - $ n);
// 3.交换最后两个密文块。
$ cipher_block_last = substr($ cipher_text,-16);
$ cipher_block_second_last = substr($ cipher_text,-32,16);
$ cipher_text = substr($ cipher_text,0,-32)。 $ crypto_block_last。 $ cipher_block_second_last;
// 4.使用标准CBC模式解密密文,直到最后一个块。
$ 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.最后一个密文(已经在步骤1中解密)与第二个最后一个密文异或。
// ???
// echo $ ??? ^ $ ???;
我发现具体的用例非常有助于理解算法。这里有两个用例,一步一步的讲解。
两个用例的起点。
这些用例假定您正在解密消息使用AES-256与CBC链模式和密码块窃取的块量化。为了生成这些用例,我使用Delphi 2010编译器和TurboPower LockBox3库(SVN修订版本243)。在下面,我使用像这样的符号...
IV:= [16] 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
...表示某些名为被分配为等于16字节的数组。最左边的字节是数组的最小符号(最低地址)字节的渲染,最右边的字节是最高有效字节。这些字节是以十六进制写的,所以例如如果有...
X:= [2] 03 10
...这意味着LSB是3,MSB是16。
使用案例1
-
让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
With TurboPower LockBox 3,这可以通过将TCodec组件的密码('UTF8Password')属性设置为...来实现。
password = (UTF-8)'你的嘴唇比vasoline更光滑。'
-
Message =(UTF-8)'Leeeeeeeeeroy Jenkins!'
已编码的长度为22个字节。
- 让IV为1.(Aside:)这是一个长度为1到2个区块的区块大小。在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])和last )密文块(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 00 00
-
一般来说,第n个块(除了最后2个块),我们正常的CBC算法是...
m [n]:= Decrypt(c [n])XOR CV;
CV [n + 1]:= c [n]
其中:
- m是输出明文块;
- Decrypt()表示对该块的AES-256 ECB解密;
- CV是我们的Carry-Vector。
-
但是对于第二个最后块(N-1)在使用情况一中,N = 2),转换改为...( )。
m [n]:= Decrypt(c [n])XOR CV;
CV [n + 1]:= CV [n] //不变!
-
申请我们的使用案例:
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
解密(c [0])= [16] 6F 6B 69 6E 73 21 F0 7B 79 F2 AF 27 B1 52 D6 0B
m [0]:=解密(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]:=解密(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
-
发出m [0]的前6个字节
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
这是'Leeeeeeeeeroy Jenkins!'的UTF-8编码。
-
在此用例中,消息精确地为2个字节长。这被称为圆盒。在圆形情况下,不存在要量化的部分块,因此其进行就像是正常的CBC。密码,密钥和IV与用例1中的相同。要解密的密文消息(包括前缀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
其编码为base64为'AQAAAAAAAABwdhJYTjgc4ZLKNPuaN8UKdfILRqHfVmDUXHZLUhnagw =='
这分成了IV,第一块和第二块,就像这样...
[16] 01 00 00 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
-
一般和最后一个方块
解密(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
下一个CV:= 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
m [1] =解密(c [1])XOR CV = [16] 65 65 76 65 72 20 79 6F 75 20 6D 61 79 20 62 65
li>
-
解密过程的最后一步是发送最后两个块。在圆形的情况下,我们不会扭转顺序。我们首先发射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
-
发出整个m
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作为密文的先前块。然而,IMHO,这种使用密文窃取,以这种方式,没有证据的缺乏对加密强度的影响的研究,更不用说增加的实现复杂性。在TurboPower LockBox 3中,当消息是短消息,并且链接模式不是密钥流模式时,链接模式被视为CFB-8位。 CFB-8位是一个密钥流模式。
在零长度消息的情况下,它真的很简单。零长度明文消息映射一对一到零长度密文消息。不需要IV,生成或预置。
PHP实现注意事项
Caveat
我不是一个PHP程序员。我不知道PHP。
字节数组
它看起来像您正在使用PHP字符串来存储字节数组。这对我看起来很危险。如果一个字节值为零怎么办?会缩短字符串吗? strlen()如何在这种情况下行为?如果PHP有一个本地数据类型是一个字节数组,那么这可能会更安全。但我真的不知道。我只是把这一点提请你注意,如果你还没有意识到。可能这不是一个问题。
mcrypt_decrypt库
我不熟悉这个库。它本身是否支持密文窃取?我假设不是。所以有两种可能的策略。
-
使用CBC模式调用库的所有解密,除了最后两个块。处理最后两个块,正如我已经向你描述的。但这需要访问CV。 API暴露了这一点吗?
-
使用ECB模式调用除了最后两个块之外的所有库的解密,然后滚动CBC链接。
如何在PHP中执行XOR
有人发布了此问题的答案,但目前已撤消。但他是对的。它看起来像在一个字节数组上进行XOR操作,遍历字符,一个接一个,并做一个字节级XOR。此技术显示在。
I have been trying to implement Ciphertext Stealing(CTS) in PHP for CBC.
Referring below two links
How can I encrypt/decrypt data using AES CBC+CTS (ciphertext stealing) mode in PHP?
and
http://en.wikipedia.org/wiki/Ciphertext_stealing
I am confused and stuck on the last and simplest step of XOR.I know this is silly but having tried all the combinations, i don't know what am i missing.Code follows.
// 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 $??? ^ $???;
解决方案 I find that concrete use cases are very helpful in understanding algorithms. Here are 2 use cases, and a step-by-step walk-through.
Starting point for both Use Cases.
These Use Cases assume that you are decrypting messages uses AES-256 with CBC chaining mode and ciphertext stealing for block quantisation. To generate these Use Cases, I used Delphi 2010 compiler and the TurboPower LockBox3 library (SVN revision 243). In what follows, I use a notation like so...
IV := [16] 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
... to mean that some variable named 'IV' is assigned to be equal to an array of 16 bytes. The left most byte, is the rendering of the Least Signficant (lowest address) byte of the array, and the right-most byte, the most significant. These bytes are written in hexadecimal, so for example if one puts...
X := [2] 03 10
... it means that the LSB is 3 and the MSB is 16.
Use Case One
Let the AES-256 32 byte compressed key (as defined in the AES standard) be...
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
With TurboPower LockBox 3, this can be achieved by setting the password ('UTF8Password') property of the TCodec component to...
password = (UTF-8) 'Your lips are smoother than vasoline.'
The plaintext message to be sent will be
Message = (UTF-8) 'Leeeeeeeeeroy Jenkins!'
Encoded this is 22 bytes long. AES-256 has a 16 byte block size, so this is some-where between 1 and 2 blocks long.
Let the IV be 1. (Aside: On the Delphi side, this can be achieved by setting
TRandomStream.Instance.Seed := 1;
just before encryption).Thus the ciphertext message to be decrypted by PHP will be (with 8 byte IV prepended a la 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')
Breaking this down into IV, first ciphertext block (c[0]) and last (partial) ciphertext block (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
Now let's walk-through the decryption with ciphertext stealing.
CV := IV
CV = [16] 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
In general, the for n'th block (except for the last 2 blocks), our normal CBC algorithm is...
m[n] := Decrypt( c[n]) XOR CV;
CV[n+1] := c[n]
where:
- m is the output plaintext block;
- Decrypt() means AES-256 ECB decryption on that block;
- CV is our Carry-Vector. The chaining mode defines how this changes from block to block.
but for the second last block (N-1) (N=2 in Use Case One), the transformation changes to ... (This exception is made due to the selection of ciphertext stealing)
m[n] := Decrypt( c[n]) XOR CV;
CV[n+1] := CV[n] // Unchanged!
Applying to our use case:
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
Now to process the last block. It is a partial one, 6 bytes long. In general, the processing of the last block goes like this...
y := c[N-1] | LastBytes( m[N-2], BlockSize-Length(c[N-1]));
m[N-1] := Decrypt( y) XOR CV
Applying to Use Case One:
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
The last step in the decryption process is the emission of the last two blocks. We reverse the order, emitting m[N-1] first, and then emit the first part of m[N-2] (the length of which is equal to the length of c[N-1]). Applying to Use Case One...
Emit m[ 1]
m[1] = [16] 4C 65 65 65 65 65 65 65 65 65 72 6F 79 20 4A 65
Emit the first 6 bytes of m[0]
FirstBytes( m[0], 6) = 6E 6B 69 6E 73 21
Putting it altogether, we get a reconstructed plaintext of ...
[22] 4C 65 65 65 65 65 65 65 65 65 72 6F 79 20 4A 65 6E 6B 69 6E 73 21
which is the UTF-8 encoding of 'Leeeeeeeeeroy Jenkins!'
Use Case Two
In this use case, the message is precisely 2 blocks long. This is called the round case. In round cases, there is no partial block to quantise, so it proceeds as if it were normal CBC. The password, key and IV are the same as in Use Case One. The ciphertext message to be decrypted (included prepended 8 byte IV) is...
Set-up
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=='
This breaks down into IV, first block and second block, like so...
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
General and 2nd last block
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
Last Block:
Our last block is round in this use case.
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
The last step in the decryption process is the emission of the last two blocks. In the round case, we don't reverse the order. We emit m[N-2] first, and then m[N-1]. Applying to Use Case Two...
Emit m[0]
m[0] = [16] 44 61 6E 63 65 20 74 68 65 6E 2C 20 77 68 65 72
Emit the whole of m1
m[1] = [16] 65 65 76 65 72 20 79 6F 75 20 6D 61 79 20 62 65
Putting it altogether, we get a reconstructed plaintext of ...
[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
which is the UTF-8 encoding of 'Dance then, whereever you may be'
Edge Cases to consider.There are two edge cases, not illustrated by the two Use Cases provided here.
Short messages. A short message is a message, whose length in bytes is:
- Not zero; and
- Less than one block;
Zero length messages.
In the case of short messages, technically one could still implement ciphertext stealing by using the the IV as the prior block of ciphertext. However, IMHO, this use of ciphertext stealing, in this way, is not justified by lack of research into the impact on cryptographic strength, not to mention the added implementation complexity. In TurboPower LockBox 3, when the message is a short message, and the chaining mode is not a key-streaming one, then the chaining mode is treated as CFB-8bit. CFB-8 bit is a key-streaming mode.
In the case of zero-length messages, its really simple. Zero-length plaintext message maps one-to-one to zero-length ciphertext messages. No IV is needed, generated nor prepended. This mapping is independent of chaining mode and cipher (in the case of block mode ciphers).
Notes on PHP Implementation
Caveat
I am not a PHP programmer. I don't know PHP. Any thing I say here should be taken with a grain of salt.
Arrays of bytes
It looks like you are using PHP strings to store arrays of bytes. This looks dangerous to me. What if one of the byte values was zero? Would that shorten the string? How would strlen() behave in that case? If PHP has a native data type which was an array of byte, then this probably would be safer. But I don't really know. I am just bringing this point to your attention, if you are not already aware of it. Possibly it is not really an issue.
mcrypt_decrypt library
I am not familiar with this library. Does it natively support ciphertext stealing? I assume not. So there are two possible strategies for you.
Call the library's decrypt for all but the last two blocks with CBC mode. Process the last two blocks as I have described to you. But this requires access to the CV. Does the API expose this? If not, the this strategy is not a viable option for you.
Call the library's decrypt for all but the last two blocks with ECB mode, and roll your CBC chaining. Fairly easy to implement, and be definition, you have access to the CV.
How to do XOR in PHP
Some-one else posted an answer to this question, but has currently withdrawn it. But he was right. It looks like to do an XOR in PHP on an array of bytes, iterate through the characters, one by one, and do a byte level XOR. The technique is shown here.
这篇关于PHP实现CBC的Ciphertext窃取(CTS)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!
在此用例中,消息精确地为2个字节长。这被称为圆盒。在圆形情况下,不存在要量化的部分块,因此其进行就像是正常的CBC。密码,密钥和IV与用例1中的相同。要解密的密文消息(包括前缀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
其编码为base64为'AQAAAAAAAABwdhJYTjgc4ZLKNPuaN8UKdfILRqHfVmDUXHZLUhnagw =='
这分成了IV,第一块和第二块,就像这样...
[16] 01 00 00 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
-
一般和最后一个方块
解密(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
下一个CV:= 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
m [1] =解密(c [1])XOR CV = [16] 65 65 76 65 72 20 79 6F 75 20 6D 61 79 20 62 65
li>
-
解密过程的最后一步是发送最后两个块。在圆形的情况下,我们不会扭转顺序。我们首先发射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
-
发出整个m
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作为密文的先前块。然而,IMHO,这种使用密文窃取,以这种方式,没有证据的缺乏对加密强度的影响的研究,更不用说增加的实现复杂性。在TurboPower LockBox 3中,当消息是短消息,并且链接模式不是密钥流模式时,链接模式被视为CFB-8位。 CFB-8位是一个密钥流模式。
在零长度消息的情况下,它真的很简单。零长度明文消息映射一对一到零长度密文消息。不需要IV,生成或预置。
PHP实现注意事项
Caveat
我不是一个PHP程序员。我不知道PHP。
字节数组
它看起来像您正在使用PHP字符串来存储字节数组。这对我看起来很危险。如果一个字节值为零怎么办?会缩短字符串吗? strlen()如何在这种情况下行为?如果PHP有一个本地数据类型是一个字节数组,那么这可能会更安全。但我真的不知道。我只是把这一点提请你注意,如果你还没有意识到。可能这不是一个问题。
mcrypt_decrypt库
我不熟悉这个库。它本身是否支持密文窃取?我假设不是。所以有两种可能的策略。
-
使用CBC模式调用库的所有解密,除了最后两个块。处理最后两个块,正如我已经向你描述的。但这需要访问CV。 API暴露了这一点吗?
-
使用ECB模式调用除了最后两个块之外的所有库的解密,然后滚动CBC链接。
如何在PHP中执行XOR
有人发布了此问题的答案,但目前已撤消。但他是对的。它看起来像在一个字节数组上进行XOR操作,遍历字符,一个接一个,并做一个字节级XOR。此技术显示在。
I have been trying to implement Ciphertext Stealing(CTS) in PHP for CBC.
Referring below two links
How can I encrypt/decrypt data using AES CBC+CTS (ciphertext stealing) mode in PHP?
and
http://en.wikipedia.org/wiki/Ciphertext_stealing
I am confused and stuck on the last and simplest step of XOR.I know this is silly but having tried all the combinations, i don't know what am i missing.Code follows.
// 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 $??? ^ $???;
解决方案 I find that concrete use cases are very helpful in understanding algorithms. Here are 2 use cases, and a step-by-step walk-through.
Starting point for both Use Cases.
These Use Cases assume that you are decrypting messages uses AES-256 with CBC chaining mode and ciphertext stealing for block quantisation. To generate these Use Cases, I used Delphi 2010 compiler and the TurboPower LockBox3 library (SVN revision 243). In what follows, I use a notation like so...
IV := [16] 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
... to mean that some variable named 'IV' is assigned to be equal to an array of 16 bytes. The left most byte, is the rendering of the Least Signficant (lowest address) byte of the array, and the right-most byte, the most significant. These bytes are written in hexadecimal, so for example if one puts...
X := [2] 03 10
... it means that the LSB is 3 and the MSB is 16.
Use Case One
Let the AES-256 32 byte compressed key (as defined in the AES standard) be...
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
With TurboPower LockBox 3, this can be achieved by setting the password ('UTF8Password') property of the TCodec component to...
password = (UTF-8) 'Your lips are smoother than vasoline.'
The plaintext message to be sent will be
Message = (UTF-8) 'Leeeeeeeeeroy Jenkins!'
Encoded this is 22 bytes long. AES-256 has a 16 byte block size, so this is some-where between 1 and 2 blocks long.
Let the IV be 1. (Aside: On the Delphi side, this can be achieved by setting
TRandomStream.Instance.Seed := 1;
just before encryption).Thus the ciphertext message to be decrypted by PHP will be (with 8 byte IV prepended a la 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')
Breaking this down into IV, first ciphertext block (c[0]) and last (partial) ciphertext block (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
Now let's walk-through the decryption with ciphertext stealing.
CV := IV
CV = [16] 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
In general, the for n'th block (except for the last 2 blocks), our normal CBC algorithm is...
m[n] := Decrypt( c[n]) XOR CV;
CV[n+1] := c[n]
where:
- m is the output plaintext block;
- Decrypt() means AES-256 ECB decryption on that block;
- CV is our Carry-Vector. The chaining mode defines how this changes from block to block.
but for the second last block (N-1) (N=2 in Use Case One), the transformation changes to ... (This exception is made due to the selection of ciphertext stealing)
m[n] := Decrypt( c[n]) XOR CV;
CV[n+1] := CV[n] // Unchanged!
Applying to our use case:
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
Now to process the last block. It is a partial one, 6 bytes long. In general, the processing of the last block goes like this...
y := c[N-1] | LastBytes( m[N-2], BlockSize-Length(c[N-1]));
m[N-1] := Decrypt( y) XOR CV
Applying to Use Case One:
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
The last step in the decryption process is the emission of the last two blocks. We reverse the order, emitting m[N-1] first, and then emit the first part of m[N-2] (the length of which is equal to the length of c[N-1]). Applying to Use Case One...
Emit m[ 1]
m[1] = [16] 4C 65 65 65 65 65 65 65 65 65 72 6F 79 20 4A 65
Emit the first 6 bytes of m[0]
FirstBytes( m[0], 6) = 6E 6B 69 6E 73 21
Putting it altogether, we get a reconstructed plaintext of ...
[22] 4C 65 65 65 65 65 65 65 65 65 72 6F 79 20 4A 65 6E 6B 69 6E 73 21
which is the UTF-8 encoding of 'Leeeeeeeeeroy Jenkins!'
Use Case Two
In this use case, the message is precisely 2 blocks long. This is called the round case. In round cases, there is no partial block to quantise, so it proceeds as if it were normal CBC. The password, key and IV are the same as in Use Case One. The ciphertext message to be decrypted (included prepended 8 byte IV) is...
Set-up
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=='
This breaks down into IV, first block and second block, like so...
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
General and 2nd last block
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
Last Block:
Our last block is round in this use case.
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
The last step in the decryption process is the emission of the last two blocks. In the round case, we don't reverse the order. We emit m[N-2] first, and then m[N-1]. Applying to Use Case Two...
Emit m[0]
m[0] = [16] 44 61 6E 63 65 20 74 68 65 6E 2C 20 77 68 65 72
Emit the whole of m1
m[1] = [16] 65 65 76 65 72 20 79 6F 75 20 6D 61 79 20 62 65
Putting it altogether, we get a reconstructed plaintext of ...
[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
which is the UTF-8 encoding of 'Dance then, whereever you may be'
Edge Cases to consider.There are two edge cases, not illustrated by the two Use Cases provided here.
Short messages. A short message is a message, whose length in bytes is:
- Not zero; and
- Less than one block;
Zero length messages.
In the case of short messages, technically one could still implement ciphertext stealing by using the the IV as the prior block of ciphertext. However, IMHO, this use of ciphertext stealing, in this way, is not justified by lack of research into the impact on cryptographic strength, not to mention the added implementation complexity. In TurboPower LockBox 3, when the message is a short message, and the chaining mode is not a key-streaming one, then the chaining mode is treated as CFB-8bit. CFB-8 bit is a key-streaming mode.
In the case of zero-length messages, its really simple. Zero-length plaintext message maps one-to-one to zero-length ciphertext messages. No IV is needed, generated nor prepended. This mapping is independent of chaining mode and cipher (in the case of block mode ciphers).
Notes on PHP Implementation
Caveat
I am not a PHP programmer. I don't know PHP. Any thing I say here should be taken with a grain of salt.
Arrays of bytes
It looks like you are using PHP strings to store arrays of bytes. This looks dangerous to me. What if one of the byte values was zero? Would that shorten the string? How would strlen() behave in that case? If PHP has a native data type which was an array of byte, then this probably would be safer. But I don't really know. I am just bringing this point to your attention, if you are not already aware of it. Possibly it is not really an issue.
mcrypt_decrypt library
I am not familiar with this library. Does it natively support ciphertext stealing? I assume not. So there are two possible strategies for you.
Call the library's decrypt for all but the last two blocks with CBC mode. Process the last two blocks as I have described to you. But this requires access to the CV. Does the API expose this? If not, the this strategy is not a viable option for you.
Call the library's decrypt for all but the last two blocks with ECB mode, and roll your CBC chaining. Fairly easy to implement, and be definition, you have access to the CV.
How to do XOR in PHP
Some-one else posted an answer to this question, but has currently withdrawn it. But he was right. It looks like to do an XOR in PHP on an array of bytes, iterate through the characters, one by one, and do a byte level XOR. The technique is shown here.
这篇关于PHP实现CBC的Ciphertext窃取(CTS)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!