PHP:
$key = "testtesttest"; $text = "bublijuja"; $iv = openssl_random_pseudo_bytes( openssl_cipher_iv_length("blowfish")); for($i = 0; $i < strlen($iv); $i++) { echo ord($iv[$i])." "; } echo "n"; $et = openssl_encrypt( $text, "blowfish", $key, OPENSSL_RAW_DATA, $iv ); for($i = 0; $i < strlen($et); $i++) { echo ord($et[$i])." "; } echo "n";
This prints(first line is IV, second one is encrypted text:
253 145 220 198 224 78 40 124 208 51 12 30 46 92 13 181 19 210 50 57 174 207 93 130
Copying that IV to golang:
package main import ( "golang.org/x/crypto/blowfish" "crypto/cipher" "fmt" ) func main() { key := "testtesttest" plaintext := "bublijuja" byteSlice := []int{253, 145, 220, 198, 224, 78, 40, 124} iv := make([]byte, len(byteSlice)) for i, b := range byteSlice { iv[i] = byte(b) } fmt.Println(iv) ciphertext := make([]byte, blowfish.BlockSize+len(plaintext)) block, err := blowfish.NewCipher([]byte(key)) if err != nil { fmt.Println(err.Error()) return } mode := cipher.NewCBCEncrypter(block, iv) mode.CryptBlocks(ciphertext, pad([]byte(plaintext))) fmt.Println(ciphertext) } func pad(pt []byte) []byte { // calculate modulus of plaintext to blowfish's cipher block size // if result is not 0, then we need to pad modulus := len(pt) % blowfish.BlockSize if modulus != 0 { // how many bytes do we need to pad to make pt to be a multiple of //blowfish's block size? padlen := blowfish.BlockSize - modulus // let's add the required padding for i := 0; i < padlen; i++ { // add the pad, one at a time pt = append(pt, 0) } } // return the whole-multiple-of-blowfish.BlockSize-sized plaintext // to the calling function return pt }
I get:
[253 145 220 198 224 78 40 124] [61 82 97 183 42 220 119 173 114 107 250 139 174 236 113 91 0]
I Tried with ECB mode as well. I was able to match first 8 bytes once but I messed something up. I’m trying to figure out how the php version handles it so that I can match go implementation, but I’m failing so far.
Advertisement
Answer
The following issues cause the different results:
Blowfish has an 8 bytes block size and a variable key size between 4 and 56 bytes. In PHP there is a bug for Blowfish that pads shorter keys to 16 bytes with 0 values. Since version 7.1.8 there is a flag that prevents this:
OPENSSL_DONT_ZERO_PAD_KEY
. If this flag is additionally set (OPENSSL_RAW_DATA | OPENSSL_DONT_ZERO_PAD_KEY
), the following output results (in the required environment):253 145 220 198 224 78 40 124 61 82 97 183 42 220 119 173 26 156 153 20 152 139 105 237
Here there is an online PHP environment where the flag can be set.
The padding defined in Go is Zero padding while in the PHP code PKCS7 padding is used by openssl (as default). For PKCS7 the following changes are necessary (without comments, using the same names):
func pad(pt []byte) []byte { modulus := len(pt) % blowfish.BlockSize padlen := blowfish.BlockSize - modulus for i := 0; i < padlen; i++ { pt = append(pt, byte(padlen)) } return pt }
With this change the Go – Code gives the same result:
[253 145 220 198 224 78 40 124] [61 82 97 183 42 220 119 173 26 156 153 20 152 139 105 237 0]
The 0 at the end is caused by a too large buffer for the ciphertext. In the Go code the length of the output buffer is calculated with plaintext length plus block size (8 bytes for Blowfish), which ensures that there is enough space for padding, since the maximum padding is one block. With shorter padding the buffer is too large, e.g. in the current case the plaintext has a length of 9 bytes, which results in a buffer of 17 bytes. The ciphertext has a length of 16 bytes, which leads to the 0 at the end. If desired, the exactly required buffer size can be determined as plaintext length plus padding length (the latter being determined analogous to
padlen
in thepad
function).