When sending data packets, the encryption currently happens in the NEPacketTunnelFlow's queue. The OpenVPN ping packets are sent from the OpenVPNSession object's queue. This results in encryptions happening in parallel, especially when sending lots of packets in a short time. Since both kinds of packets use the same crypto context, this results in the same crypto context being accessed from two different threads at the same time.
Consider the code for encryptBytes. If two threads are simultaneously executing that method, and both thread have finished EVP_CipherInit, then after one thread exits EVP_CipherFinal_ex, the other thread's EVP_CipherFinal_ex call fails because the same IV can't be reused. (EVP_CipherFinal_ex() calls aes_gcm_cipher() here when using AES-256-GCM cipher). The OpenSSL API is not designed to be used from different threads on the same context.
So the fix is to make the data packets' encryption also happen in the OpenVPNSession object's queue.
Fixes #481.
When sending data packets, the encryption currently happens in the NEPacketTunnelFlow's queue. The OpenVPN ping packets are sent from the OpenVPNSession object's queue. This results in encryptions happening in parallel, especially when sending lots of packets in a short time. Since both kinds of packets use the same crypto context, this results in the same crypto context being accessed from two different threads at the same time.
Consider the code for encryptBytes. If two threads are simultaneously executing that method, and both thread have finished EVP_CipherInit, then after one thread exits EVP_CipherFinal_ex, the other thread's EVP_CipherFinal_ex call fails because the same IV can't be reused. (EVP_CipherFinal_ex() calls aes_gcm_cipher() here when using AES-256-GCM cipher). The OpenSSL API is not designed to be used from different threads on the same context.
So the fix is to make the data packets' encryption also happen in the OpenVPNSession object's queue.