diff options
author | hrxi <hrrrxi@gmail.com> | 2023-06-19 14:08:57 +0200 |
---|---|---|
committer | fiaxh <fiaxh@users.noreply.github.com> | 2023-10-06 15:25:12 +0200 |
commit | 6eb1b53e60a12f82c8d47a5824bf9cee954ccdc2 (patch) | |
tree | 13a13ef08fcd74bc1685454730b72693806b76f0 /plugins/omemo/src/signal/signal_helper.c | |
parent | e2d801b5f74b60c38a75310066c48468c8a4bc93 (diff) | |
download | dino-6eb1b53e60a12f82c8d47a5824bf9cee954ccdc2.tar.gz dino-6eb1b53e60a12f82c8d47a5824bf9cee954ccdc2.zip |
Merge `signal-protocol` into `omemo` plugin
Same reasoning as for the `openpgp` plugin.
Diffstat (limited to 'plugins/omemo/src/signal/signal_helper.c')
-rw-r--r-- | plugins/omemo/src/signal/signal_helper.c | 377 |
1 files changed, 377 insertions, 0 deletions
diff --git a/plugins/omemo/src/signal/signal_helper.c b/plugins/omemo/src/signal/signal_helper.c new file mode 100644 index 00000000..17682929 --- /dev/null +++ b/plugins/omemo/src/signal/signal_helper.c @@ -0,0 +1,377 @@ +#include "signal_helper.h" + +#include <gcrypt.h> + +signal_type_base* signal_type_ref_vapi(void* instance) { + g_return_val_if_fail(instance != NULL, NULL); + signal_type_ref(instance); + return instance; +} + +signal_type_base* signal_type_unref_vapi(void* instance) { + g_return_val_if_fail(instance != NULL, NULL); + signal_type_unref(instance); + return NULL; +} + +signal_protocol_address* signal_protocol_address_new(const gchar* name, int32_t device_id) { + g_return_val_if_fail(name != NULL, NULL); + signal_protocol_address* address = malloc(sizeof(signal_protocol_address)); + address->device_id = -1; + address->name = NULL; + signal_protocol_address_set_name(address, name); + signal_protocol_address_set_device_id(address, device_id); + return address; +} + +void signal_protocol_address_free(signal_protocol_address* ptr) { + g_return_if_fail(ptr != NULL); + if (ptr->name) { + g_free((void*)ptr->name); + } + return free(ptr); +} + +void signal_protocol_address_set_name(signal_protocol_address* self, const gchar* name) { + g_return_if_fail(self != NULL); + g_return_if_fail(name != NULL); + gchar* n = g_malloc(strlen(name)+1); + memcpy(n, name, strlen(name)); + n[strlen(name)] = 0; + if (self->name) { + g_free((void*)self->name); + } + self->name = n; + self->name_len = strlen(n); +} + +gchar* signal_protocol_address_get_name(signal_protocol_address* self) { + g_return_val_if_fail(self != NULL, NULL); + g_return_val_if_fail(self->name != NULL, 0); + gchar* res = g_malloc(sizeof(char) * (self->name_len + 1)); + memcpy(res, self->name, self->name_len); + res[self->name_len] = 0; + return res; +} + +int32_t signal_protocol_address_get_device_id(signal_protocol_address* self) { + g_return_val_if_fail(self != NULL, -1); + return self->device_id; +} + +void signal_protocol_address_set_device_id(signal_protocol_address* self, int32_t device_id) { + g_return_if_fail(self != NULL); + self->device_id = device_id; +} + +int signal_vala_randomize(uint8_t *data, size_t len) { + gcry_randomize(data, len, GCRY_STRONG_RANDOM); + return SG_SUCCESS; +} + +int signal_vala_random_generator(uint8_t *data, size_t len, void *user_data) { + gcry_randomize(data, len, GCRY_STRONG_RANDOM); + return SG_SUCCESS; +} + +int signal_vala_hmac_sha256_init(void **hmac_context, const uint8_t *key, size_t key_len, void *user_data) { + gcry_mac_hd_t* ctx = malloc(sizeof(gcry_mac_hd_t)); + if (!ctx) return SG_ERR_NOMEM; + + if (gcry_mac_open(ctx, GCRY_MAC_HMAC_SHA256, 0, 0)) { + free(ctx); + return SG_ERR_UNKNOWN; + } + + if (gcry_mac_setkey(*ctx, key, key_len)) { + free(ctx); + return SG_ERR_UNKNOWN; + } + + *hmac_context = ctx; + + return SG_SUCCESS; +} + +int signal_vala_hmac_sha256_update(void *hmac_context, const uint8_t *data, size_t data_len, void *user_data) { + gcry_mac_hd_t* ctx = hmac_context; + + if (gcry_mac_write(*ctx, data, data_len)) return SG_ERR_UNKNOWN; + + return SG_SUCCESS; +} + +int signal_vala_hmac_sha256_final(void *hmac_context, signal_buffer **output, void *user_data) { + size_t len = gcry_mac_get_algo_maclen(GCRY_MAC_HMAC_SHA256); + uint8_t md[len]; + gcry_mac_hd_t* ctx = hmac_context; + + if (gcry_mac_read(*ctx, md, &len)) 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 signal_vala_hmac_sha256_cleanup(void *hmac_context, void *user_data) { + gcry_mac_hd_t* ctx = hmac_context; + if (ctx) { + gcry_mac_close(*ctx); + free(ctx); + } +} + +int signal_vala_sha512_digest_init(void **digest_context, void *user_data) { + gcry_md_hd_t* ctx = malloc(sizeof(gcry_mac_hd_t)); + if (!ctx) return SG_ERR_NOMEM; + + if (gcry_md_open(ctx, GCRY_MD_SHA512, 0)) { + free(ctx); + return SG_ERR_UNKNOWN; + } + + *digest_context = ctx; + + return SG_SUCCESS; +} + +int signal_vala_sha512_digest_update(void *digest_context, const uint8_t *data, size_t data_len, void *user_data) { + gcry_md_hd_t* ctx = digest_context; + + gcry_md_write(*ctx, data, data_len); + + return SG_SUCCESS; +} + +int signal_vala_sha512_digest_final(void *digest_context, signal_buffer **output, void *user_data) { + size_t len = gcry_md_get_algo_dlen(GCRY_MD_SHA512); + gcry_md_hd_t* ctx = digest_context; + + uint8_t* md = gcry_md_read(*ctx, GCRY_MD_SHA512); + if (!md) return SG_ERR_UNKNOWN; + + gcry_md_reset(*ctx); + + signal_buffer *output_buffer = signal_buffer_create(md, len); + free(md); + if (!output_buffer) return SG_ERR_NOMEM; + + *output = output_buffer; + + return SG_SUCCESS; +} + +void signal_vala_sha512_digest_cleanup(void *digest_context, void *user_data) { + gcry_md_hd_t* ctx = digest_context; + if (ctx) { + gcry_md_close(*ctx); + free(ctx); + } +} + +const int aes_cipher(int cipher, size_t key_len, int* algo, int* mode) { + switch (key_len) { + case 16: + *algo = GCRY_CIPHER_AES128; + break; + case 24: + *algo = GCRY_CIPHER_AES192; + break; + case 32: + *algo = GCRY_CIPHER_AES256; + break; + default: + return SG_ERR_UNKNOWN; + } + switch (cipher) { + case SG_CIPHER_AES_CBC_PKCS5: + *mode = GCRY_CIPHER_MODE_CBC; + break; + case SG_CIPHER_AES_CTR_NOPADDING: + *mode = GCRY_CIPHER_MODE_CTR; + break; + case SG_CIPHER_AES_GCM_NOPADDING: + *mode = GCRY_CIPHER_MODE_GCM; + break; + default: + return SG_ERR_UNKNOWN; + } + return SG_SUCCESS; +} + +int signal_vala_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 algo, mode, error_code = SG_ERR_UNKNOWN; + if (aes_cipher(cipher, key_len, &algo, &mode)) return SG_ERR_INVAL; + + gcry_cipher_hd_t ctx = {0}; + + if (gcry_cipher_open(&ctx, algo, mode, 0)) return SG_ERR_NOMEM; + + signal_buffer* padded = 0; + signal_buffer* out_buf = 0; + goto no_error; +error: + gcry_cipher_close(ctx); + if (padded != 0) { + signal_buffer_bzero_free(padded); + } + if (out_buf != 0) { + signal_buffer_free(out_buf); + } + return error_code; +no_error: + + if (gcry_cipher_setkey(ctx, key, key_len)) goto error; + + uint8_t tag_len = 0, pad_len = 0; + switch (cipher) { + case SG_CIPHER_AES_CBC_PKCS5: + if (gcry_cipher_setiv(ctx, iv, iv_len)) goto error; + pad_len = 16 - (plaintext_len % 16); + if (pad_len == 0) pad_len = 16; + break; + case SG_CIPHER_AES_CTR_NOPADDING: + if (gcry_cipher_setctr(ctx, iv, iv_len)) goto error; + break; + case SG_CIPHER_AES_GCM_NOPADDING: + if (gcry_cipher_setiv(ctx, iv, iv_len)) goto error; + tag_len = 16; + break; + default: + return SG_ERR_UNKNOWN; + } + + size_t padded_len = plaintext_len + pad_len; + padded = signal_buffer_alloc(padded_len); + if (padded == 0) { + error_code = SG_ERR_NOMEM; + goto error; + } + + memset(signal_buffer_data(padded) + plaintext_len, pad_len, pad_len); + memcpy(signal_buffer_data(padded), plaintext, plaintext_len); + + out_buf = signal_buffer_alloc(padded_len + tag_len); + if (out_buf == 0) { + error_code = SG_ERR_NOMEM; + goto error; + } + + if (gcry_cipher_encrypt(ctx, signal_buffer_data(out_buf), padded_len, signal_buffer_data(padded), padded_len)) goto error; + + if (tag_len > 0) { + if (gcry_cipher_gettag(ctx, signal_buffer_data(out_buf) + padded_len, tag_len)) goto error; + } + + *output = out_buf; + out_buf = 0; + + signal_buffer_bzero_free(padded); + padded = 0; + + gcry_cipher_close(ctx); + return SG_SUCCESS; +} + +int signal_vala_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) { + int algo, mode, error_code = SG_ERR_UNKNOWN; + *output = 0; + if (aes_cipher(cipher, key_len, &algo, &mode)) return SG_ERR_INVAL; + if (ciphertext_len == 0) return SG_ERR_INVAL; + + gcry_cipher_hd_t ctx = {0}; + + if (gcry_cipher_open(&ctx, algo, mode, 0)) return SG_ERR_NOMEM; + + signal_buffer* out_buf = 0; + goto no_error; +error: + gcry_cipher_close(ctx); + if (out_buf != 0) { + signal_buffer_bzero_free(out_buf); + } + return error_code; +no_error: + + if (gcry_cipher_setkey(ctx, key, key_len)) goto error; + + uint8_t tag_len = 0, pkcs_pad = FALSE; + switch (cipher) { + case SG_CIPHER_AES_CBC_PKCS5: + if (gcry_cipher_setiv(ctx, iv, iv_len)) goto error; + pkcs_pad = TRUE; + break; + case SG_CIPHER_AES_CTR_NOPADDING: + if (gcry_cipher_setctr(ctx, iv, iv_len)) goto error; + break; + case SG_CIPHER_AES_GCM_NOPADDING: + if (gcry_cipher_setiv(ctx, iv, iv_len)) goto error; + if (ciphertext_len < 16) goto error; + tag_len = 16; + break; + default: + goto error; + } + + size_t padded_len = ciphertext_len - tag_len; + out_buf = signal_buffer_alloc(padded_len); + if (out_buf == 0) { + error_code = SG_ERR_NOMEM; + goto error; + } + + if (gcry_cipher_decrypt(ctx, signal_buffer_data(out_buf), signal_buffer_len(out_buf), ciphertext, padded_len)) goto error; + + if (tag_len > 0) { + if (gcry_cipher_checktag(ctx, ciphertext + padded_len, tag_len)) goto error; + } + + if (pkcs_pad) { + uint8_t pad_len = signal_buffer_data(out_buf)[padded_len - 1]; + if (pad_len > 16 || pad_len > padded_len) goto error; + *output = signal_buffer_create(signal_buffer_data(out_buf), padded_len - pad_len); + signal_buffer_bzero_free(out_buf); + out_buf = 0; + } else { + *output = out_buf; + out_buf = 0; + } + + gcry_cipher_close(ctx); + return SG_SUCCESS; +} + +void setup_signal_vala_crypto_provider(signal_context *context) +{ + gcry_check_version(NULL); + + signal_crypto_provider provider = { + .random_func = signal_vala_random_generator, + .hmac_sha256_init_func = signal_vala_hmac_sha256_init, + .hmac_sha256_update_func = signal_vala_hmac_sha256_update, + .hmac_sha256_final_func = signal_vala_hmac_sha256_final, + .hmac_sha256_cleanup_func = signal_vala_hmac_sha256_cleanup, + .sha512_digest_init_func = signal_vala_sha512_digest_init, + .sha512_digest_update_func = signal_vala_sha512_digest_update, + .sha512_digest_final_func = signal_vala_sha512_digest_final, + .sha512_digest_cleanup_func = signal_vala_sha512_digest_cleanup, + .encrypt_func = signal_vala_encrypt, + .decrypt_func = signal_vala_decrypt, + .user_data = 0 + }; + + signal_context_set_crypto_provider(context, &provider); +} |