forked from blue/squawk
feat(omemo): add signal protocol wrappers
This commit is contained in:
parent
12ffe8e8e6
commit
2654e38665
28 changed files with 874 additions and 27 deletions
10
qomemo/signal/crypto/CMakeLists.txt
Normal file
10
qomemo/signal/crypto/CMakeLists.txt
Normal 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
|
||||
)
|
187
qomemo/signal/crypto/aes_openssl.cpp
Normal file
187
qomemo/signal/crypto/aes_openssl.cpp
Normal 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;
|
||||
}
|
17
qomemo/signal/crypto/aes_openssl.h
Normal file
17
qomemo/signal/crypto/aes_openssl.h
Normal 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
|
40
qomemo/signal/crypto/crypto.cpp
Normal file
40
qomemo/signal/crypto/crypto.cpp
Normal 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;
|
||||
}
|
13
qomemo/signal/crypto/crypto.h
Normal file
13
qomemo/signal/crypto/crypto.h
Normal 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();
|
||||
|
||||
}
|
71
qomemo/signal/crypto/hmac_sha256_openssl.cpp
Normal file
71
qomemo/signal/crypto/hmac_sha256_openssl.cpp
Normal 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
|
||||
}
|
||||
}
|
16
qomemo/signal/crypto/hmac_sha256_openssl.h
Normal file
16
qomemo/signal/crypto/hmac_sha256_openssl.h
Normal 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
|
67
qomemo/signal/crypto/sha512_digest_openssl.cpp
Normal file
67
qomemo/signal/crypto/sha512_digest_openssl.cpp
Normal 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);
|
||||
}
|
16
qomemo/signal/crypto/sha512_digest_openssl.h
Normal file
16
qomemo/signal/crypto/sha512_digest_openssl.h
Normal 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
|
Loading…
Add table
Add a link
Reference in a new issue