about summary refs log tree commit diff stats
path: root/src/omemo/crypto.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/omemo/crypto.c')
-rw-r--r--src/omemo/crypto.c111
1 files changed, 110 insertions, 1 deletions
diff --git a/src/omemo/crypto.c b/src/omemo/crypto.c
index 380551ad..a4a2d5fc 100644
--- a/src/omemo/crypto.c
+++ b/src/omemo/crypto.c
@@ -35,12 +35,14 @@
 #include <assert.h>
 #include <signal/signal_protocol.h>
 #include <signal/signal_protocol_types.h>
-#include <gcrypt.h>
 
 #include "log.h"
 #include "omemo/omemo.h"
 #include "omemo/crypto.h"
 
+#define AES256_GCM_TAG_LENGTH  16
+#define AES256_GCM_BUFFER_SIZE 1024
+
 int
 omemo_crypto_init(void)
 {
@@ -373,3 +375,110 @@ out:
     gcry_cipher_close(hd);
     return res;
 }
+
+gcry_error_t
+aes256gcm_crypt_file(FILE* in, FILE* out, off_t file_size,
+                     unsigned char key[], unsigned char nonce[], bool encrypt)
+{
+
+    if (!gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) {
+        fputs("libgcrypt has not been initialized\n", stderr);
+        abort();
+    }
+
+    if (!encrypt) {
+        file_size -= AES256_GCM_TAG_LENGTH;
+    }
+
+    gcry_error_t res;
+    gcry_cipher_hd_t hd;
+
+    res = gcry_cipher_open(&hd, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_GCM,
+                           GCRY_CIPHER_SECURE);
+    if (res != GPG_ERR_NO_ERROR) {
+        goto out;
+    }
+
+    res = gcry_cipher_setkey(hd, key, OMEMO_AESGCM_KEY_LENGTH);
+    if (res != GPG_ERR_NO_ERROR) {
+        goto out;
+    }
+
+    res = gcry_cipher_setiv(hd, nonce, OMEMO_AESGCM_NONCE_LENGTH);
+    if (res != GPG_ERR_NO_ERROR) {
+        goto out;
+    }
+
+    unsigned char buffer[AES256_GCM_BUFFER_SIZE];
+
+    int bytes = 0;
+    off_t bytes_read = 0, bytes_available = 0, read_size = 0;
+    while (bytes_read < file_size) {
+        bytes_available = file_size - bytes_read;
+        if (!bytes_available || ferror(in) != 0) {
+            break;
+        }
+
+        if (bytes_available < AES256_GCM_BUFFER_SIZE) {
+            read_size = bytes_available;
+            gcry_cipher_final(hd); // Signal last round of bytes.
+        } else {
+            read_size = AES256_GCM_BUFFER_SIZE;
+        }
+
+        bytes = fread(buffer, 1, read_size, in);
+        bytes_read += bytes;
+
+        if (encrypt) {
+            res = gcry_cipher_encrypt(hd, buffer, bytes, NULL, 0);
+        } else {
+            res = gcry_cipher_decrypt(hd, buffer, bytes, NULL, 0);
+        }
+
+        if (res != GPG_ERR_NO_ERROR) {
+            goto out;
+        }
+
+        fwrite(buffer, 1, bytes, out);
+    }
+
+    unsigned char tag[AES256_GCM_TAG_LENGTH];
+
+    if (encrypt) {
+        // Append authentication tag at the end of the file.
+        res = gcry_cipher_gettag(hd, tag, AES256_GCM_TAG_LENGTH);
+        if (res != GPG_ERR_NO_ERROR) {
+            goto out;
+        }
+
+        fwrite(tag, 1, AES256_GCM_TAG_LENGTH, out);
+
+    } else {
+        // Read and verify authentication tag stored at the end of the file.
+        bytes = fread(tag, 1, AES256_GCM_TAG_LENGTH, in);
+        res = gcry_cipher_checktag(hd, tag, bytes);
+    }
+
+out:
+    gcry_cipher_close(hd);
+    return res;
+}
+
+char*
+aes256gcm_create_secure_fragment(unsigned char* key, unsigned char* nonce)
+{
+    int key_size = OMEMO_AESGCM_KEY_LENGTH;
+    int nonce_size = OMEMO_AESGCM_NONCE_LENGTH;
+
+    char* fragment = gcry_malloc_secure((nonce_size + key_size) * 2 + 1);
+
+    for (int i = 0; i < nonce_size; i++) {
+        sprintf(&(fragment[i * 2]), "%02x", nonce[i]);
+    }
+
+    for (int i = 0; i < key_size; i++) {
+        sprintf(&(fragment[(i + nonce_size) * 2]), "%02x", key[i]);
+    }
+
+    return fragment;
+}