about summary refs log tree commit diff stats
path: root/src/omemo/omemo.c
diff options
context:
space:
mode:
authorWilliam Wennerström <william@wstrm.dev>2020-07-20 22:49:50 +0200
committerWilliam Wennerström <william@wstrm.dev>2020-11-16 21:58:09 +0100
commit73f313b9212d652fecb13bcb82a0f162abb897a0 (patch)
tree79556fb956301732e66c58196e4b685e87718792 /src/omemo/omemo.c
parentfb002a59b6dd0e2656987e9bdb72f6d3478d4e97 (diff)
downloadprofani-tty-73f313b9212d652fecb13bcb82a0f162abb897a0.tar.gz
Refactor OMEMO download into AESGCMDownload tool
Diffstat (limited to 'src/omemo/omemo.c')
-rw-r--r--src/omemo/omemo.c106
1 files changed, 99 insertions, 7 deletions
diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c
index e08d3f06..e19b724e 100644
--- a/src/omemo/omemo.c
+++ b/src/omemo/omemo.c
@@ -62,6 +62,9 @@
 #include "xmpp/roster_list.h"
 #include "xmpp/xmpp.h"
 
+#define AESGCM_URL_NONCE_LEN (2 * OMEMO_AESGCM_NONCE_LENGTH)
+#define AESGCM_URL_KEY_LEN   (2 * OMEMO_AESGCM_KEY_LENGTH)
+
 static gboolean loaded;
 
 static void _generate_pre_keys(int count);
@@ -1664,12 +1667,12 @@ char*
 omemo_encrypt_file(FILE* in, FILE* out, off_t file_size, int* gcry_res)
 {
     unsigned char* key = gcry_random_bytes_secure(
-        AES256_GCM_KEY_LENGTH,
+        OMEMO_AESGCM_KEY_LENGTH,
         GCRY_VERY_STRONG_RANDOM);
 
     // Create nonce/IV with random bytes.
-    unsigned char nonce[AES256_GCM_NONCE_LENGTH];
-    gcry_create_nonce(nonce, AES256_GCM_NONCE_LENGTH);
+    unsigned char nonce[OMEMO_AESGCM_NONCE_LENGTH];
+    gcry_create_nonce(nonce, OMEMO_AESGCM_NONCE_LENGTH);
 
     char* fragment = aes256gcm_create_secure_fragment(key, nonce);
     *gcry_res = aes256gcm_crypt_file(in, out, file_size, key, nonce, true);
@@ -1684,7 +1687,96 @@ omemo_encrypt_file(FILE* in, FILE* out, off_t file_size, int* gcry_res)
     return fragment;
 }
 
-//int omemo_decrypt_file(FILE *in, FILE *out, off_t file_size,
-//    unsigned char key[], unsigned char nonce[]) {
-//    return aes256gcm_crypt_file(in, out, file_size, key, nonce, false);
-//}
+void
+_bytes_from_hex(const char* hex, size_t hex_size,
+                unsigned char* bytes, size_t bytes_size)
+{
+    const unsigned char ht[] = {
+        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 01234567
+        0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 89:;<=>?
+        0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, // @ABCDEFG
+    };
+    const size_t ht_size = sizeof(ht);
+
+    unsigned char b0;
+    unsigned char b1;
+
+    memset(bytes, 0, bytes_size);
+
+    for (int i = 0; (i < hex_size) && (i / 2 < bytes_size); i += 2) {
+        b0 = ((unsigned char)hex[i + 0] & 0x1f) ^ 0x10;
+        b1 = ((unsigned char)hex[i + 1] & 0x1f) ^ 0x10;
+
+        if (b0 <= ht_size && b1 <= ht_size) {
+            bytes[i / 2] = (unsigned char)(ht[b0] << 4) | ht[b1];
+        }
+    }
+}
+
+int
+omemo_decrypt_file(FILE* in, FILE* out, off_t file_size, const char* fragment)
+{
+    char nonce_hex[AESGCM_URL_NONCE_LEN];
+    char key_hex[AESGCM_URL_KEY_LEN];
+
+    const int nonce_pos = 0;
+    const int key_pos = AESGCM_URL_NONCE_LEN;
+
+    memcpy(nonce_hex, &(fragment[nonce_pos]), AESGCM_URL_NONCE_LEN);
+    memcpy(key_hex, &(fragment[key_pos]), AESGCM_URL_KEY_LEN);
+
+    unsigned char nonce[OMEMO_AESGCM_NONCE_LENGTH];
+    unsigned char* key = gcry_malloc_secure(OMEMO_AESGCM_KEY_LENGTH);
+
+    _bytes_from_hex(nonce_hex, AESGCM_URL_NONCE_LEN,
+                    nonce, OMEMO_AESGCM_NONCE_LENGTH);
+    _bytes_from_hex(key_hex, AESGCM_URL_KEY_LEN,
+                    key, OMEMO_AESGCM_KEY_LENGTH);
+
+    int crypt_res = aes256gcm_crypt_file(in, out, file_size, key, nonce, false);
+
+    gcry_free(key);
+
+    return crypt_res;
+}
+
+int
+omemo_parse_aesgcm_url(const char* aesgcm_url,
+                       char** https_url,
+                       char** fragment)
+{
+    CURLUcode ret;
+    CURLU* url = curl_url();
+
+    // Required to allow for the "aesgcm://" scheme that OMEMO Media Sharing
+    // uses.
+    unsigned int curl_flags = CURLU_NON_SUPPORT_SCHEME;
+
+    ret = curl_url_set(url, CURLUPART_URL, aesgcm_url, curl_flags);
+    if (ret) {
+        goto out;
+    }
+
+    ret = curl_url_get(url, CURLUPART_FRAGMENT, fragment, curl_flags);
+    if (ret) {
+        goto out;
+    }
+
+    if (strlen(*fragment) != AESGCM_URL_NONCE_LEN + AESGCM_URL_KEY_LEN) {
+        goto out;
+    }
+
+    ret = curl_url_set(url, CURLUPART_SCHEME, "https", curl_flags);
+    if (ret) {
+        goto out;
+    }
+
+    ret = curl_url_get(url, CURLUPART_URL, https_url, curl_flags);
+    if (ret) {
+        goto out;
+    }
+
+out:
+    curl_url_cleanup(url);
+    return ret;
+}