$encrypt/$decrypt if password parameter is $null, contains codepoint 128+, or longer than 56 bytes
FYI. The $encrypt/$decrypt pages both say EBC, when they really mean 'ECB' for Electronic Code Book.
The output appears to be the 8_bytes-to-12_text encoding used in FISH, and doesn't offer a binary input/output switch, so until I can make an alias to translate that output to binary, it's hard to verify the test vectors. But I did find edge cases where the password is not handled correctly.
1. $null password crashes:
//echo -a $encrypt(test,$null)
//echo -a $decrypt(test,$null)
2. Codepoints above 127 are used as if $chr(63) "?" is used.
//echo -a #1 $encrypt(test,pass $+ $chr(10004))
//echo -a #2 $encrypt(test,pass $+ $chr(233))
//echo -a #3 $encrypt(test,pass $+ $chr(224))
//echo -a #4 $encrypt(test,pass $+ $chr(63))
//echo -a $decrypt(C9.i6Y5LrozZ,pass $+ $chr(63))
I believe that, when the password is a text string, the correct bytes to use are from UTF-8 encoding the input, or to reject input that isn't 7-bit. If the input is ever binary, such as using a sha1/sha256/sha512 digest, the input should retain the 0x00's and 0x80-0xff's without UTF8 encoding them.
3. Password is expanded to fill the 72-byte internal key incorrectly.
//echo -a $encrypt(test,$str(abcde,1)) input length 5x1=5
//echo -a $encrypt(test,$str(abcde,11)) input length 5x11=55
//echo -a $encrypt(test,$str(abcde,12)) input length 5x12=60
These 3 produce identical outputs. The first two should do that, the 3rd should not. From how it handles the 4 combinations of 71 or 72 a's followed by either 'b' or 'c', it appears $encrypt() is using $left(input,72) then replicating that until length 72 if it's shorter.
The correct method is to use only the first 56 bytes of the input, even if that chops in the middle of a multi-byte UTF8 symbol, then replicate that 1-56 byte string until it has length of 72. Blowfish design requires that at least 16 of the 72 bytes be repeated. It's your call whether to use $left($utfencode(input),56) or to generate an error if the input contains more than 56 bytes (not 56 characters).
Updated by Paul Janson 3 months ago
Edit: I figured out the output's encoding, and it seems to pass the test vectors I've tried so far, though the data is harmlessly transposed on disk. The encoding is 6 text characters being the encoding of a 32-bit value, with the final 4 bits repeated into the last text character. It uses a different Base64 alphabet than the mime alphabet used in $encrypt(text,m).
However of the 2 32-bit encryption variables, ciphertext-left is supposed to be output first, then ciphertext-right is to be last. Instead, the group of 6 bytes are swapped. If this is the 'fish' standard, then it's fine to leave it the way it is, as this is a keyless transposition that doesn't weaken the cipher.
Per the article http://www.drdobbs.com/the-blowfish-encryption-algorithm-one-ye/184409634 the text vector for string=BLOWFISH and password=abcdefghijklmnopqrstuvwxyz
The ciphertext-left should be 0x324ed0fe and ciphertext-right should be 0xf413a203 stored on disk as hex 32 4E D0 FE F4 13 A2 03
//echo -a $encrypt(BLOWFISH, abcdefghijklmnopqrstuvwxyz )
... outputs 16U2OZY1HhM.S60uQZelEEfY where the first 6 characters are the encoding of the 0xf413a203 and the next 6 characters are the encoding of 0x324ed0fe
The S60uQZelEEfY is the encryption of the padding of 8 0x00's padding, which $decrypt throws away since 0x00 is not valid in text strings. If $encrypt is ever changed to accept binary variables as input, the padding needs to be changed to either zero-and-ones or PKCS#5 where the padding is one of the 8 alternatives from having 1 0x01 through having 8 0x08's.
Updated by Per Amundsen 3 months ago
- Category set to Scripting
- Status changed from New to Assigned
- Assignee set to Per Amundsen
Yes I believe it was made to be compatible with fish10, this was before the fish10 plugin was ported to AdiIRC.
I fixed the crashes and updated the wiki page to reflect that.
I'll look into adding &binvar support, but for users wanting to use fish10, I recommend using the plugin instead since it has source available and easier to verify.