Fixes Age of Empires III not being able to find its servers / multiplayer games. That regressed with the following commit: ``` commit 745df5915cf4c7411d69b4fd2ba5d05a333f3947 Author: Hans Leidekker hans@codeweavers.com Date: Wed Aug 31 09:42:11 2022 +0200
bcrypt: Force symmetric key reset if necessary.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=52457 ```
The core problem is not in this commit though. The bit of the commit which regressed AoE is removing ```!is_zero_vector( vector, vector_len )``` condition in key_symmetric_set_vector() which was previously also forcing key reset (which was resetting key to the set value whenever the key is nonempty). That was apparently breaking other things, that's why it was probably removed.
The actual culprit is that BCryptEncrypt() / BCryptEncrypt() modify init vector on output in AES CBC and CFB mode on Windows. So, to properly sync our gnutls key we need to return the value in IV parameter like Windows does and decide whether the key needs update or not based on comparison with the actual dynamic key state, not with the value set initially. The game sets the IV data over multiple encrypt or decrypt call (sets it with its own fixed value overwriting what Windows returns in those). Before the blamed commit that worked because key_symmetric_set_vector() was always pushing the given key to gnutls. But that was breaking apps which worked differently (which way is probably a more expected usage) which set IV with their only once initially and then either: - pass the same buffer, which on Windows has the updated IV and in Wine was always the same, thus the changed in blamed commit fixed the thing by keeping the internal state of gnutls key untouched; - pass NULL buffer; this case I suppose didn't work correctly neither before nor after the blamed commit but should be also fixed by this MR.
Besides newly added tests (which cover IV output data plus some additional cases of encryption with auto-modified state) the MR is fixing a few rather unfortunate existing todo's.
Now, the implementation. My first instinct was to try to extract that updated state from gnutls, but: 1. It seems like there is no exported function which would give this updated state. There is _gnutls_cipher_get_iv() used in tests but it doesn't even seem to work for AES / CBC; 2. The comment above _gnutls_cipher_get_iv() says: ``` * This is solely for validation purposes of our crypto * implementation. For other purposes, the IV can be typically * calculated from the initial IV value and the subsequent ciphertext * values. ``` So it doesn't look like gnutls is planning to export anything like that. Also, it is not apparent that "init vector" is generally equivalent to the dynamic cipher hash, and the fact that Windows returns that in the IV vector back look rather implementation specific. So, also given that the updated state hash value is the simply taken from the last encrypted data block (input on decrypt and output on encrypt) I hope it is ok to just code that.