Why do I have to implement the go aes-cbc-256 encryption and decryption function?
The previous project was implemented using PHP. Now we are going to use go for reconstruction. This function is required. This common feature allows us to search for a ready-made example on the Internet, so you can implement a pair of encryption and decryption functions in minutes based on the existing go API. If you think right, it will fail when you run it. Well, there is no nonsense. The AES-CBC Implementation of Go has two restrictions:
1: Two Problems
1: the length of the go key must be 16/24/32
The Go source code is as follows. Our key length is 29, which does not match
// NewCipher creates and returns a new cipher.Block. // The key argument should be the AES key, // either 16, 24, or 32 bytes to select // AES-128, AES-192, or AES-256. func NewCipher(key []byte) (cipher.Block, error) { k := len(key) switch k { default : return nil, KeySizeError(k) case 16 , 24 , 32 : break } return newCipher(key) } |
2: Go does not support 256-bit aes-CBC encryption and decryption.
I once again posted the relevant source code of go, and wrote that const blocksize = 16, and his mother is a constant. That is to say, go can only encrypt 16*8 = 128 bits at a time, how to migrate my php256 bit
const BlockSize = 16 // You can see me at a glance type aesCipherAsm struct { aesCipher } var useAsm = cipherhw.AESGCMSupport() func newCipher(key []byte) (cipher.Block, error) { if !useAsm { return newCipherGeneric(key) } n := len(key) + 28 c := aesCipherAsm{aesCipher{ make ([]uint32, n), make ([]uint32, n)}} rounds := 10 switch len(key) { case 128 / 8 : rounds = 10 case 192 / 8 : rounds = 12 case 256 / 8 : rounds = 14 } expandKeyAsm(rounds, &key[ 0 ], &c.enc[ 0 ], &c.dec[ 0 ]) if hasGCMAsm() { return &aesCipherGCM{c}, nil } return &c, nil } func (c *aesCipherAsm) BlockSize() int { return BlockSize } func (c *aesCipherAsm) Encrypt(dst, src []byte) { if len(src) < BlockSize { panic( "crypto/aes: input not full block" ) } if len(dst) < BlockSize { panic( "crypto/aes: output not full block" ) } encryptBlockAsm(len(c.enc)/ 4 - 1 , &c.enc[ 0 ], &dst[ 0 ], &src[ 0 ]) } func (c *aesCipherAsm) Decrypt(dst, src []byte) { if len(src) < BlockSize { panic( "crypto/aes: input not full block" ) } if len(dst) < BlockSize { panic( "crypto/aes: output not full block" ) } decryptBlockAsm(len(c.dec)/ 4 - 1 , &c.dec[ 0 ], &dst[ 0 ], &src[ 0 ]) } |
2: brother started thinking.
The problem is broken one by one, and I want to see if I can bypass it. Because the necessary length is limited during newcipher, I can't do it myself. I am dumpfounded, only the interface is public and the implemented objects are private. to instantiate an object, you can only use newcipher. If you want to instantiate the object, you can only copy the source code, modify it yourself, and run the go source code again, and copied it out to show the first bit
// Encryption implementation TEXT ·encryptBlockAsm(SB),NOSPLIT,$ 0 MOVQ nr+ 0 (FP), CX 666 ... Lenc256: MOVUPS 0 (AX), X1 666 ... Lenc196: MOVUPS 0 (AX), X1 666 ... Lenc128: MOVUPS 0 (AX), X1 666 .. RET // Decryption Implementation // func decryptBlockAsm(nr int, xk *uint32, dst, src *byte) TEXT ·decryptBlockAsm(SB),NOSPLIT,$ 0 MOVQ nr+ 0 (FP), CX 666 ... Ldec256: MOVUPS 0 (AX), X1 666 ... Ldec196: MOVUPS 0 (AX), X1 666 ... Ldec128: MOVUPS 0 (AX), X1 666 ... RET // Initialize the data structure required for encryption and decryption through key and IV // func expandKeyAsm(nr int, key *byte, enc, dec *uint32) { // Note that round keys are stored in uint128 format, not uint32 TEXT ·expandKeyAsm(SB),NOSPLIT,$ 0 MOVQ nr+ 0 (FP), CX JE Lexp_enc196 // I have no idea what the two Ghost commands are. JB Lexp_enc128 // Same as above, so it is two Ghost commands Lexp_enc256: MOVUPS 16 (AX), X2 666 ... |
Head: comrades, shout with me: source code in hand, I have it in the world, 666...
Younger brother: Big Brother, this source code seems a bit wrong.
I am going to: Assembly, strong calm, first look for relevant information, for a long time did not study the compilation of me, again studied the compilation, found some information: https://juejin.im/entry/5a39d646f265da431a435476, Information
Copy part of the code and try to modify the assembly code. After running the code, it failed.
In fact, go also has non-assembly-implemented go code, but it also encrypts 16 bytes each time. It does not meet the requirements. I need to process 32 bytes of source code each time, later, I tried to layer the object package produced by newcipher so that blocksize () would return 32. Naturally, it would not work either.
The first stage ends with a failure.
3: I want to use go to call PHP.
How bold people are, Go tune PHP, a search on the Internet really has such a bold God, to achieve the go tune php: the moment when the world was getting, a burst of tears, the solution is so fixed: Go calls PHP to implement aes-cbc-256 encryption and decryption
The above success is just an illusion. In fact, it's just that go has been successfully tuned to C. It's not excited. I didn't call PHP. I didn't find libphp on my computer. so, this lib library was not generated when PHP was compiled. Go calls PHP to compile PHP into your program and let you call it. So I started to look for information, find this: Success. So I asked the gods with a thick face. The gods said that they do not recommend using go to tune PHP. This road is not very reliable. We recommend that you call RPC directly. When I tell the gods my needs and the status quo of go, great God suggestion: copy the code from the standard library, both sides compare debugging, you are a great god or I am a great god, let me use go to C aes-cbc-256 from the new implementation, how can I do this! Of course you are a great god.
4: golang implemented aes-cbc-256 encryption and decryption officially started
The first step is to look at the PHP source code. Take a step-by-step look at the following functions:
mcrypt_module_open mcrypt_generic_init mcrypt_generic mdecrypt_generic |
All implementations are in the PHP extension module mcrypt. This module is only an encapsulation of another standard library. Address: encrypt (in fact, it took more than two hours ), translation soon was relative to the debug phase. I found that the encryption was incorrect, and I didn't know where the error was. The code was easy to read and spam, and no problem was found, so I had to compare and debug the C language for more than two years according to what the great gods said. So I installed clion and studied it briefly. Because I used to work on windows, I have never done it on Mac, but it is quite helpful. I started to compile it, So I simply reviewed the C language and finally ran it. Because the implementation of libmcrypt is full of pointers, A lot of data is invisible and can only be printed. Later, I found that the length of the key is incorrect. I passed it to 32. In fact, the length of the key is calculated in this way.
// Obtain the length of the encryption key func getKeySize(size int) int { for _, val := range keySizes { if size <= val { return val } } return BLOCK_SIZE } |
After a long time, I finally solved the encryption problem. I was so happy (if I could not understand it, it would be a dialect). I really felt a sense of accomplishment. Then I began to decrypt it and found it wrong, I couldn't find the cause for half a day. In this process, I found another database: encrypt (half encrypted). You asked me to give up. Why should we limit the key length in this library? The standard library itself has no restrictions.
5. Solve the problem with tears
When I realized the encryption, I brag to the great gods, saying that I have implemented the encryption, but the decryption is not solved now. What should I do?
I watched the code for a while and didn't find any problems, so I had to make the ultimate killer: single-step comparison and debugging. In fact, I have found static word32 rtable [256]; Initialization is incorrect, why can't encryption be decrypted? This variable is only used for decryption. synchronization, comparison, and debugging have finally found a problem. A Go language is different from a C language. See the following function:
// C language implementation static byte bmul(byte x, byte y) { if (x && y) return ptab[(ltab[x] + ltab[y]) % 255]; else return 0; } bmul(200,200) == 145 // Go language implementation func bmul(x, y byte) byte { if x > 0 && y > 0 { return ptab[(ltab[x]+ltab[y])%255] } return 0 } bmul(200,200) == 144 |
Dear friends, there is no difference. As mentioned above, I directly translated the C language into the go language, but the C language is different from the go language. Two identical functions are actually different, C language 400% 255 = 145 good understanding, how does go become 144? 200 + 200 = 144. Let's take a look at the 400 binary representation of 110010000, remove the first 1, that is, 010010000, that is to say, if the number of bytes in the C language exceeds 144, it does not matter. If the number of bytes in the go language exceeds 255, it is cut off. What about the C language in the Internet Age!
6: github.com
Https://github.com/chentaihan/aesCbc
Golang implements aes-cbc-256 encryption and decryption process record