I found a 32-bit install of Perl, but it didn't have any of the CBC/Blowfish modules part of it, and I've not yet figured out how to add these modules. But, based on descriptions of how Perl did things, I've replicated behavior when using the 'i' and 's' switches. The methods used by Perl heavily relies on MD5.

In summary, when not using the literal 'l' switch, the 'c' switch requires you use 1 of 2 hashing methods for generating a 56-byte binary literal key used to encrypt the input string.

When using 'i' or 'r' (or 'ir' to put user-defined IV into the header), users should provide a key string which they have already salted, such as using the 'i' user-defined IV to HMAC the real key and using that hash as the key parameter. Otherwise, the literal 56-byte key is a simplistic expansion of $md5(key parameter) where the 1st 16 bytes are the literal MD5 hash of it, and the same encryption is generated from all key parameter strings having identical MD5 hashes.

When using the 's' switch, the Perl method repeats a MD5 hash 4 times to create the 64 bytes needed to create the literal 56-byte key and the 8-byte salt. The 1st 16 bytes of this is the MD5 hash of the (key $+ salt) parameters.


Below is just detail of how I confirmed that $encode's c/s/i/r switches are doing the same thing as Perl, and describing exactly what that is doing. Both examples use the same key parameter foobar, and use deadbeef as either the salt or IV.

The Perl method of turning -nosalt password into the encryption key is mentioned here:


Based on that, I created an alias which produces a 56-byte binary string.

alias openssl_nosalt_keygen {
  bunset &key | var %pass foobar | noop $nosalt_digest_to_binary($md5(%pass) )
  while ($bvar(&key,0) < 56) { noop $nosalt_digest_to_binary($md5(&key,1)) }
  echo 4 -a $bvar(&key,0) literal key in hex: $regsubex($bvar(&key,1-),/(\d+)/g,$base(\1,10,16,2) )
alias nosalt_digest_to_binary {
  bset &key -1 $regsubex($1,/(..)/g,$base(\1,16,10) $chr(32)) | if ($bvar(&key,0) > 56) bcopy -c &key 56 &key 56 1
  echo 3 -a $bvar(&key,0) of 56 generated: $regsubex($bvar(&key,1-),/(\d+)/g, $base(\t,10,16,2) $chr(32))
alias use_foobar_iv {
  bset -t &v 1 BLOWFISH
  noop $encode(&v,bmcir,foobar,deadbeef) | noop $decode(&v,bm)
  echo 4 -a $regsubex($bvar(&v,1-),/(\d+)/g,$base(\1,10,16,2) $iif(!$calc(\n % 8),.)) $bvar(&v,1-).text

I was able to feed the 56 bytes output from /openssl_nosalt_keygen to an external program which accepted a 56-byte binary string as the encryption key, without UTF-8 encoding byte values 128-255 or excluding 0x00's. This alias produces matching encryption to that of $encode() where it used the 'cir' switches but didn't use the 'l' switch. This key is not affected by the content of the IV.

For example, "/use_foobar_iv" uses key=foobar and iv=deadbeef. Using the 'ir' switches together causes the user-defined IV to be listed in the header as if it's a randomly created IV, which is why this alias shows the header contains an IV of 'deadbeef'. The presence of the IV forces $encode to NOT use a salt and to use the above alias's method to generate the actual key. The encoded output of the "BLOWFISH" string immediately follows the 'deadbeef', and in hex format it's:

"39 17 4D 71 8B 9D 20 82".

"/openssl_nosalt_keygen" shows that using password "foobar" generates a 56 byte binary key string as hex:

38 58 F6 22 30 AC 3C 91 5F 30 0C 66 43 12 C6 3F C4 52 9D C8 56 43 BB 0C 5A 96 E4 65 87 37 77 77 B6 65 DE EB 42 29 7C FF FA B0 E1 4E 7E 78 0E 76 80 46 2D 01 3E 52 94 70

... where the first 16 bytes are the literal MD5 digest for 'foobar', and the remaining 40 of 56 bytes are entirely calculated using only these 16 bytes. i.e. there are only 2^128 possible 56-byte strings. The other program knowing nothing about 'foobar', using these 56 binary bytes as the literal key and using the IV of 'deadbeef', also encrypts the "BLOWFISH" text string to those same "39 17 4D 71 8B 9D 20 82" bytes.

Because /openssl_nosalt_keygen used the key parameter once, as the input to $md5, this limits the password strength to no more than 128 bits, and all password strings having the same MD5 hash generate equivalent encryption.

alias md5_collision {
  bset -t &v1 1 Tclo/w7jXCCVctR3e3IVh9Nvp7Ib3Fa3Sj3AeD57lRivv6IAqChL826OS1WzX0J1k9hJZ22g0VVdg2D7Xwf+og==
  noop $decode(&v1,bm) | bcopy -c &v2 1 &v1 1 -1
  bset &v2 36 $xor($bvar(&v2,36),2)
  bset &v2 56 $xor($bvar(&v2,56),128)
  echo 3 -a $md5(&v1,1) $bvar(&v1,36) $bvar(&v1,56)
  echo 4 -a $md5(&v2,1) $bvar(&v2,36) $bvar(&v2,56)


The switch 's' method for combining the key and salt parameters to generate the salted-key and IV binary strings is described at:


Based on this description, I created another alias to duplicate the way Perl generates the key and salt when using the 's' switch:

alias openssl_salted_keygen {
  bunset &raw | var %pass foobar , %salt deadbeef
  bset -tc &pass+salt 1 %pass $+ %salt
  while ($bvar(&key,0) < $calc(56+8)) { noop $salted_digest_to_binary }
  bcopy -c &pass 1 &key 1 56 | bcopy -c &iv  1 &key 57 8
  echo 4 -a literal key in hex: $regsubex($bvar(&key,1-56) ,/(\d+)/g,$base(\t,10,16,2) $chr(32))
  echo 4 -a literal  iv in hex: $regsubex($bvar(&key,57- ) ,/(\d+)/g,$base(\t,10,16,2) $chr(32))
alias salted_digest_to_binary {
  if ($bvar(&hash,0)) bcopy -c &raw 1 &hash 1 -1 | bcopy &raw -1 &pass+salt 1 -1
  bset -c &hash 1 $regsubex($md5(&raw,1),/(..)/g,$base(\1,16,10) $chr(32)) | bcopy &key -1 &hash 1 -1
  if ($bvar(&key,0) > $calc(56+8)) bcopy -c &key 64 &key 64 1
  echo 3 -a $bvar(&key,0) of 64 generated: $regsubex($bvar(&key,1-) ,/(\d+)/g,$base(\t,10,16,2) $chr(32))
alias use_foobar_salted {
  bset -t &v 1 BLOWFISH
  noop $encode(&v,bmcs,foobar,deadbeef) | echo noop $decode(&v,bm)
  echo 4 -a $regsubex($bvar(&v,1-),/(\d+)/g,$base(\1,10,16,2) $iif(!$calc(\n % 8),.)) $bvar(&v,1-).text

Using /use_foobar_salted shows that, when using password=foobar and a user-provided salt=deadbeef, the encryption of the text "BLOWFISH" follows the 'deadbeef' salt in the header, and in hex is "41 E1 92 D9 AA 51 90 E9".

Using password=foobar and salt=deadbeef as the inputs to /openssl_salted_keygen generates binary values for a 56-byte literal key and an IV, both dependent on the password and salt:

key: 38 58 F6 22 30 AC 3C 91 5F 30 0C 66 43 12 C6 3F 56 83 78 52 96 14 D2 2D DB 49 23 7D 2F 60 BF DF 0E BF 58 78 E8 2A F7 DA 61 8E D5 6F C6 7D 4A B7 AD FF 94 C3 8F B4 45 7A
iv: D4 9F 7A 4F AE 8E DA DE

Using these binary strings as literal input key and iv, the other program encrypted the "BLOWFISH" string to the same 8 bytes "41 E1 92 D9 AA 51 90 E9".