This library does not authenticate ciphertexts, and there is a padding oracle vulnerability. I am procrastinating some math homework, so I'll write out a full description of the attack.
Suppose Mallory has an N-block ciphertext (IV, C1, C2, ..., CN) she wants to decrypt. Suppose also that she can choose ciphertexts to be decrypted, but all she can see is whether the substr() in unpad() returns false or not. This is a reasonable scenario, for example a web site might try to encrypt its cookies with this library to keep their contents secret from the user, and error out when decrypt() returns false, or might otherwise be distinguishable by treating false as the empty string.
Mallory can use this to find out the last byte of every plaintext block. Suppose Mallory wants to find the last byte of the plaintext block Pj corresponding to the ciphertext block Cj (call the IV C0).
If Mallory sends the ciphertext (C(j-1), Cj), i.e. the previous block as the IV, and the to-be-decrypted block as the only block in the ciphertext, then substr() will return false exactly when the last byte of Pj is greater than 16. We will call the two results either FALSE (substr() returned false) or SUCCESS (substr() returned a string). Here is how Mallory gets the last byte:
Input: C[j-1], C[j], byte arrays of length 16.
For X := 0 to 255:
V := C[j-1]
// XOR the last byte of plaintext with X
V := V ^ X
Send (V, C[j])
// If XORing with X makes the last byte <= 16...
// Check if the last byte is 16 by "adding one"
V := V ^ 1
Send (V, C[j])
// The last byte now decrypts to 17.
Output X ^ 1 ^ 17
tl;dr: We alter the previous block until the last byte of the plaintext block is known to be 17. We know how it got changed to 17 (by XORing X^1), so undoing that (XORing 17 by X^1) gives Mallory the value of the last byte.
This attack highlights the danger of not authenticating the ciphertext, but in practice it is probably much easier to break, since the client will probably behave differently depending on the contents of the returned string, and thus Mallory would be able to decrypt entire blocks (it will depend on how this library gets used in the specific application).
By the way, I am the author of the similarly-named php-encryption. I put a lot of time into making that one secure, and it has had a decent amount of review, so you may want to just replace this code with mine.
The text was updated successfully, but these errors were encountered:
After the discussion we decided that we will no longer support this library. Instead we will switch to php-encryption and this library is also suggested now in readme and on Packagist.
It doesn't make sense now to continue this library when the more mature library exists now.
This library is now marked as Abandoned on packagist https://packagist.org/packages/keboola/php-encryption
Thanks @defuse for your work.