about summary refs log tree commit diff stats
path: root/src/omemo/crypto.c
diff options
context:
space:
mode:
authorWilliam Wennerström <william@wstrm.dev>2020-06-11 22:50:36 +0200
committerWilliam Wennerström <william@wstrm.dev>2020-11-16 21:58:07 +0100
commit3370418d71de255c832da97113543e554ec0e86b (patch)
tree0a364790895088cb0415b4ff31bfd40a588735bc /src/omemo/crypto.c
parent35aecd425fad6697e9cf72832cb287a156ec7942 (diff)
downloadprofani-tty-3370418d71de255c832da97113543e554ec0e86b.tar.gz
Initial /sendfile OMEMO encryption
Diffstat (limited to 'src/omemo/crypto.c')
-rw-r--r--src/omemo/crypto.c99
1 files changed, 99 insertions, 0 deletions
diff --git a/src/omemo/crypto.c b/src/omemo/crypto.c
index 380551ad..6d6ba519 100644
--- a/src/omemo/crypto.c
+++ b/src/omemo/crypto.c
@@ -41,6 +41,9 @@
 #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 +376,99 @@ out:
     gcry_cipher_close(hd);
     return res;
 }
+
+int 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, AES256_GCM_KEY_LENGTH);
+    if (res != GPG_ERR_NO_ERROR) {
+        goto out;
+    }
+
+    res = gcry_cipher_setiv(hd, nonce, AES256_GCM_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) {
+            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;
+}
+
+int aes256gcm_encrypt_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, true);
+}
+
+int aes256gcm_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);
+}