forked from blue/squawk
187 lines
5.4 KiB
C++
187 lines
5.4 KiB
C++
/*
|
|
* Created by victoria on 2021-05-13.
|
|
*/
|
|
|
|
#include "aes_openssl.h"
|
|
#include <memory>
|
|
|
|
extern "C" {
|
|
#include <openssl/evp.h>
|
|
#include <openssl/opensslv.h>
|
|
}
|
|
|
|
using namespace Signal::Crypto;
|
|
|
|
class EVPCipherCtxWrapper {
|
|
public:
|
|
EVPCipherCtxWrapper() {
|
|
#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
|
|
ctx = EVP_CIPHER_CTX_new();
|
|
#else
|
|
ctx = new EVP_CIPHER_CTX;
|
|
EVP_CIPHER_CTX_init(ctx);
|
|
#endif
|
|
}
|
|
|
|
~EVPCipherCtxWrapper() {
|
|
if (good()) {
|
|
#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
#else
|
|
EVP_CIPHER_CTX_cleanup(ctx);
|
|
delete ctx;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
EVPCipherCtxWrapper(const EVPCipherCtxWrapper &) = delete;
|
|
EVPCipherCtxWrapper(EVPCipherCtxWrapper &&) = delete;
|
|
EVPCipherCtxWrapper &operator=(const EVPCipherCtxWrapper &) = delete;
|
|
|
|
[[nodiscard]] bool good() const { return ctx != nullptr; }
|
|
|
|
[[nodiscard]] EVP_CIPHER_CTX *operator*() const { return ctx; }
|
|
|
|
private:
|
|
EVP_CIPHER_CTX *ctx{nullptr};
|
|
};
|
|
|
|
static const EVP_CIPHER *aes_cipher(int cipher, size_t key_len) {
|
|
if (cipher == SG_CIPHER_AES_CBC_PKCS5) {
|
|
if (key_len == 16) {
|
|
return EVP_aes_128_cbc();
|
|
} else if (key_len == 24) {
|
|
return EVP_aes_192_cbc();
|
|
} else if (key_len == 32) {
|
|
return EVP_aes_256_cbc();
|
|
}
|
|
} else if (cipher == SG_CIPHER_AES_CTR_NOPADDING) {
|
|
if (key_len == 16) {
|
|
return EVP_aes_128_ctr();
|
|
} else if (key_len == 24) {
|
|
return EVP_aes_192_ctr();
|
|
} else if (key_len == 32) {
|
|
return EVP_aes_256_ctr();
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
int Aes::encrypt(signal_buffer **output, int cipher, const uint8_t *key, size_t key_len, const uint8_t *iv,
|
|
size_t iv_len, const uint8_t *plaintext, size_t plaintext_len, void *) {
|
|
const EVP_CIPHER *evp_cipher = aes_cipher(cipher, key_len);
|
|
if (!evp_cipher) {
|
|
fprintf(stderr, "invalid AES mode or key size: %zu\n", key_len);
|
|
return SG_ERR_UNKNOWN;
|
|
}
|
|
|
|
if (iv_len != 16) {
|
|
fprintf(stderr, "invalid AES IV size: %zu\n", iv_len);
|
|
return SG_ERR_UNKNOWN;
|
|
}
|
|
|
|
if (plaintext_len > INT_MAX - EVP_CIPHER_block_size(evp_cipher)) {
|
|
fprintf(stderr, "invalid plaintext length: %zu\n", plaintext_len);
|
|
return SG_ERR_UNKNOWN;
|
|
}
|
|
|
|
EVPCipherCtxWrapper ctx{};
|
|
if (!ctx.good()) {
|
|
fprintf(stderr, "could not create context\n");
|
|
return SG_ERR_UNKNOWN;
|
|
}
|
|
|
|
auto result = EVP_EncryptInit_ex(*ctx, evp_cipher, nullptr, key, iv);
|
|
if (!result) {
|
|
fprintf(stderr, "cannot initialize cipher\n");
|
|
return SG_ERR_UNKNOWN;
|
|
}
|
|
|
|
if (cipher == SG_CIPHER_AES_CTR_NOPADDING) {
|
|
result = EVP_CIPHER_CTX_set_padding(*ctx, 0);
|
|
if (!result) {
|
|
fprintf(stderr, "cannot set padding\n");
|
|
return SG_ERR_UNKNOWN;
|
|
}
|
|
}
|
|
|
|
auto out_buf = std::make_unique<uint8_t>(plaintext_len + EVP_CIPHER_block_size(evp_cipher));
|
|
|
|
int out_len = 0;
|
|
result = EVP_EncryptUpdate(*ctx, out_buf.get(), &out_len, plaintext,
|
|
plaintext_len);
|
|
if (!result) {
|
|
fprintf(stderr, "cannot encrypt plaintext\n");
|
|
return SG_ERR_UNKNOWN;
|
|
}
|
|
|
|
int final_len = 0;
|
|
result = EVP_EncryptFinal_ex(*ctx, out_buf.get() + out_len, &final_len);
|
|
if (!result) {
|
|
fprintf(stderr, "cannot finish encrypting plaintext\n");
|
|
return SG_ERR_UNKNOWN;
|
|
}
|
|
|
|
*output = signal_buffer_create(out_buf.get(), out_len + final_len);
|
|
|
|
return result;
|
|
}
|
|
|
|
int Aes::decrypt(signal_buffer **output, int cipher, const uint8_t *key, size_t key_len, const uint8_t *iv,
|
|
size_t iv_len, const uint8_t *ciphertext, size_t ciphertext_len, void *) {
|
|
const EVP_CIPHER *evp_cipher = aes_cipher(cipher, key_len);
|
|
if (!evp_cipher) {
|
|
fprintf(stderr, "invalid AES mode or key size: %zu\n", key_len);
|
|
return SG_ERR_INVAL;
|
|
}
|
|
|
|
if (iv_len != 16) {
|
|
fprintf(stderr, "invalid AES IV size: %zu\n", iv_len);
|
|
return SG_ERR_INVAL;
|
|
}
|
|
|
|
if (ciphertext_len > INT_MAX - EVP_CIPHER_block_size(evp_cipher)) {
|
|
fprintf(stderr, "invalid ciphertext length: %zu\n", ciphertext_len);
|
|
return SG_ERR_UNKNOWN;
|
|
}
|
|
|
|
EVPCipherCtxWrapper ctx{};
|
|
if (!ctx.good()) {
|
|
fprintf(stderr, "could not create context\n");
|
|
return SG_ERR_UNKNOWN;
|
|
}
|
|
|
|
auto result = EVP_DecryptInit_ex(*ctx, evp_cipher, nullptr, key, iv);
|
|
if (!result) {
|
|
fprintf(stderr, "cannot initialize cipher\n");
|
|
return SG_ERR_UNKNOWN;
|
|
}
|
|
|
|
if (cipher == SG_CIPHER_AES_CTR_NOPADDING) {
|
|
result = EVP_CIPHER_CTX_set_padding(*ctx, 0);
|
|
if (!result) {
|
|
fprintf(stderr, "cannot set padding\n");
|
|
return SG_ERR_UNKNOWN;
|
|
}
|
|
}
|
|
|
|
auto out_buf = std::make_unique<uint8_t>(ciphertext_len + EVP_CIPHER_block_size(evp_cipher));
|
|
|
|
int out_len = 0;
|
|
result = EVP_DecryptUpdate(*ctx, out_buf.get(), &out_len, ciphertext, ciphertext_len);
|
|
if (!result) {
|
|
fprintf(stderr, "cannot decrypt ciphertext\n");
|
|
return SG_ERR_UNKNOWN;
|
|
}
|
|
|
|
int final_len = 0;
|
|
result = EVP_DecryptFinal_ex(*ctx, out_buf.get() + out_len, &final_len);
|
|
if (!result) {
|
|
fprintf(stderr, "cannot finish decrypting ciphertext\n");
|
|
return SG_ERR_UNKNOWN;
|
|
}
|
|
|
|
*output = signal_buffer_create(out_buf.get(), out_len + final_len);
|
|
|
|
return result;
|
|
} |