feat(omemo): add signal protocol wrappers

This commit is contained in:
vae 2021-05-13 17:50:29 +03:00
parent 12ffe8e8e6
commit 2654e38665
Signed by: vae
GPG key ID: A9A33351400E00E5
28 changed files with 874 additions and 27 deletions

View file

@ -0,0 +1,10 @@
target_sources(squawk PRIVATE
aes_openssl.cpp
aes_openssl.h
crypto.cpp
crypto.h
hmac_sha256_openssl.cpp
hmac_sha256_openssl.h
sha512_digest_openssl.cpp
sha512_digest_openssl.h
)

View file

@ -0,0 +1,187 @@
/*
* 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;
}

View file

@ -0,0 +1,17 @@
/*
* Created by victoria on 2021-05-13.
*/
#pragma once
#include <signal/signal_protocol.h>
namespace Signal::Crypto::Aes {
int 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 *user_data);
int 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 *user_data);
} // namespace Signal::Crypto::Aes

View file

@ -0,0 +1,40 @@
/*
* Created by victoria on 2021-05-13.
*/
#include "crypto.h"
extern "C" {
#include <openssl/rand.h>
}
#include "aes_openssl.h"
#include "hmac_sha256_openssl.h"
#include "sha512_digest_openssl.h"
int random_func(uint8_t *data, size_t len, void *) {
if (RAND_bytes(data, len)) {
return 0;
} else {
return SG_ERR_UNKNOWN;
}
}
signal_crypto_provider Signal::Crypto::createProvider() {
signal_crypto_provider result{};
result.random_func = random_func;
result.hmac_sha256_init_func = HmacSha256::init;
result.hmac_sha256_update_func = HmacSha256::update;
result.hmac_sha256_final_func = HmacSha256::final;
result.hmac_sha256_cleanup_func = HmacSha256::cleanup;
result.sha512_digest_init_func = Sha512::init;
result.sha512_digest_update_func = Sha512::update;
result.sha512_digest_final_func = Sha512::final;
result.sha512_digest_cleanup_func = Sha512::cleanup;
result.encrypt_func = Aes::encrypt;
result.decrypt_func = Aes::decrypt;
result.user_data = nullptr;
return result;
}

View file

@ -0,0 +1,13 @@
/*
* Created by victoria on 2021-05-13.
*/
#pragma once
#include <signal/signal_protocol.h>
namespace Signal::Crypto {
signal_crypto_provider createProvider();
}

View file

@ -0,0 +1,71 @@
/*
* Created by victoria on 2021-05-13.
*/
#include "hmac_sha256_openssl.h"
extern "C" {
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <openssl/sha.h>
}
using namespace Signal::Crypto;
int HmacSha256::init(void **hmac_context, const uint8_t *key, size_t key_len, void *) {
#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
HMAC_CTX *ctx = HMAC_CTX_new();
if (!ctx) {
return SG_ERR_NOMEM;
}
#else
auto ctx = new HMAC_CTX;
HMAC_CTX_init(ctx);
#endif
*hmac_context = ctx;
if (HMAC_Init_ex(ctx, key, key_len, EVP_sha256(), nullptr) != 1) {
return SG_ERR_UNKNOWN;
}
return SG_SUCCESS;
}
int HmacSha256::update(void *hmac_context, const uint8_t *data, size_t data_len, void *) {
auto ctx = static_cast<HMAC_CTX *>(hmac_context);
int result = HMAC_Update(ctx, data, data_len);
return (result == 1) ? SG_SUCCESS : SG_ERR_UNKNOWN;
}
int HmacSha256::final(void *hmac_context, signal_buffer **output, void *) {
auto ctx = static_cast<HMAC_CTX *>(hmac_context);
unsigned char md[EVP_MAX_MD_SIZE];
unsigned int len = 0;
if (HMAC_Final(ctx, md, &len) != 1) {
return SG_ERR_UNKNOWN;
}
signal_buffer *output_buffer = signal_buffer_create(md, len);
if (!output_buffer) {
return SG_ERR_NOMEM;
}
*output = output_buffer;
return SG_SUCCESS;
}
void HmacSha256::cleanup(void *hmac_context, void *) {
if (hmac_context) {
auto ctx = static_cast<HMAC_CTX *>(hmac_context);
#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
HMAC_CTX_free(ctx);
#else
HMAC_CTX_cleanup(ctx);
delete ctx;
#endif
}
}

View file

@ -0,0 +1,16 @@
/*
* Created by victoria on 2021-05-13.
*/
#pragma once
#include <signal/signal_protocol.h>
namespace Signal::Crypto::HmacSha256 {
int init(void **hmac_context, const uint8_t *key, size_t key_len, void *);
int update(void *hmac_context, const uint8_t *data, size_t data_len, void *);
int final(void *hmac_context, signal_buffer **output, void *);
void cleanup(void *hmac_context, void *);
} // namespace Signal::Crypto::HmacSha256

View file

@ -0,0 +1,67 @@
/*
* Created by victoria on 2021-05-13.
*/
#include "sha512_digest_openssl.h"
extern "C" {
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/sha.h>
}
using namespace Signal::Crypto;
int Sha512::init(void **digest_context, void *) {
auto ctx = EVP_MD_CTX_create();
if (!ctx) {
return SG_ERR_NOMEM;
}
auto result = EVP_DigestInit_ex(ctx, EVP_sha512(), nullptr);
if (result == 1) {
*digest_context = ctx;
return SG_SUCCESS;
}
EVP_MD_CTX_destroy(ctx);
return SG_ERR_UNKNOWN;
}
int Sha512::update(void *digest_context, const uint8_t *data, size_t data_len, void *) {
auto ctx = static_cast<EVP_MD_CTX *>(digest_context);
auto result = EVP_DigestUpdate(ctx, data, data_len);
return (result == 1) ? SG_SUCCESS : SG_ERR_UNKNOWN;
}
int Sha512::final(void *digest_context, signal_buffer **output, void *) {
auto ctx = static_cast<EVP_MD_CTX *>(digest_context);
unsigned char md[EVP_MAX_MD_SIZE];
unsigned int len = 0;
auto result = EVP_DigestFinal_ex(ctx, md, &len);
if (result != 1) {
return SG_ERR_UNKNOWN;
}
result = EVP_DigestInit_ex(ctx, EVP_sha512(), nullptr);
if (result != 1) {
return SG_ERR_UNKNOWN;
}
signal_buffer *output_buffer = signal_buffer_create(md, len);
if (!output_buffer) {
return SG_ERR_NOMEM;
}
*output = output_buffer;
return SG_SUCCESS;
}
void Sha512::cleanup(void *digest_context, void *) {
auto ctx = static_cast<EVP_MD_CTX *>(digest_context);
EVP_MD_CTX_destroy(ctx);
}

View file

@ -0,0 +1,16 @@
/*
* Created by victoria on 2021-05-13.
*/
#pragma once
#include <signal/signal_protocol.h>
namespace Signal::Crypto::Sha512 {
int init(void **digest_context, void *);
int update(void *digest_context, const uint8_t *data, size_t data_len, void *);
int final(void *digest_context, signal_buffer **output, void *);
void cleanup(void *digest_context, void *);
} // namespace Signal::Crypto::Sha512