aboutsummaryrefslogtreecommitdiff
path: root/src/crypto
diff options
context:
space:
mode:
authorfschildt <florian.schildt@protonmail.com>2025-08-22 15:23:11 +0200
committerfschildt <florian.schildt@protonmail.com>2025-08-22 15:23:11 +0200
commit2050c0e0576f05156f192aa4caf48834d2f28b14 (patch)
treeee58bd35b0df0a1bacfbc9700ed99ce80c99294e /src/crypto
first commitHEADmaster
Diffstat (limited to 'src/crypto')
-rw-r--r--src/crypto/aes_gcm.c169
-rw-r--r--src/crypto/aes_gcm.h51
-rw-r--r--src/crypto/rsa.c235
-rw-r--r--src/crypto/rsa.h19
4 files changed, 474 insertions, 0 deletions
diff --git a/src/crypto/aes_gcm.c b/src/crypto/aes_gcm.c
new file mode 100644
index 0000000..dbcf731
--- /dev/null
+++ b/src/crypto/aes_gcm.c
@@ -0,0 +1,169 @@
+#include <crypto/aes_gcm.h>
+
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+#include <openssl/err.h>
+#include <string.h>
+
+b32
+aes_gcm_key_init_random(AesGcmKey *key)
+{
+ int success = RAND_priv_bytes(key->buff, sizeof(key->buff));
+ return success == 1;
+}
+
+b32
+aes_gcm_iv_init(AesGcmIv *iv)
+{
+ if (RAND_priv_bytes(iv->salt, sizeof(iv->salt)) < 0) {
+ return false;
+ }
+
+ u32 *counter = (u32*)iv->counter;
+ assert(sizeof(*counter) == sizeof(iv->counter));
+ *counter = 0;
+
+ return true;
+}
+
+void
+aes_gcm_iv_advance(AesGcmIv *iv)
+{
+ u32 *counter = (u32*)iv->counter;
+ assert(sizeof(*counter) == sizeof(iv->counter));
+
+ *counter += 1;
+ if (*counter == U32_MAX) {
+ exit(0); // Todo: make new keys
+ }
+}
+
+b32
+aes_gcm_decrypt(AesGcmKey *key, AesGcmIv *iv,
+ u8 *plaintext, u8 *ciphertext, i32 ciphertext_len,
+ u8 *tag, i32 tag_len)
+{
+ EVP_CIPHER_CTX *ctx;
+ i32 plaintext_len = 0;
+ i32 len = 0;
+
+ ctx = EVP_CIPHER_CTX_new();
+ if (!ctx) {
+ printf("EVP_CIPHER_CTX_new() failed\n");
+ return false;
+ }
+
+ if (EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), 0, 0, 0) != 1) {
+ printf("EVP_DecryptInit_ex() failed\n");
+ EVP_CIPHER_CTX_free(ctx);
+ return false;
+ }
+
+ if(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, 0) != 1) {
+ printf("EVP_CIPHER_CTX_ctrl setting iv length failed\n");
+ EVP_CIPHER_CTX_free(ctx);
+ return false;
+ }
+
+ if(EVP_DecryptInit_ex(ctx, 0, 0, key->buff, (u8*)iv) != 1) {
+ printf("EVP_DecryptInit_ex with key and iv failed\n");
+ EVP_CIPHER_CTX_free(ctx);
+ return false;
+ }
+
+ if (EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len) != 1) {
+ printf("EVP_DecryptUpdate() failed\n");
+ EVP_CIPHER_CTX_free(ctx);
+ return false;
+ }
+ plaintext_len += len;
+
+ if(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, tag_len, tag) != 1) {
+ printf("EVP_CIPHER_CTX_ctrl setting tag failed\n");
+ EVP_CIPHER_CTX_free(ctx);
+ return false;
+ }
+
+ if (EVP_DecryptFinal_ex(ctx, plaintext + len, &len) != 1) {
+ ERR_print_errors_fp(stderr);
+ printf("EVP_DecryptFinal_ex failed\n");
+ EVP_CIPHER_CTX_free(ctx);
+ return false;
+ }
+ plaintext_len += len;
+
+ if (plaintext_len != ciphertext_len) {
+ printf("aes_gcm_decrypt failed: decrypted_len != len\n");
+ EVP_CIPHER_CTX_free(ctx);
+ return false;
+ }
+
+ EVP_CIPHER_CTX_free(ctx);
+ aes_gcm_iv_advance(iv);
+ return true;
+}
+
+b32
+aes_gcm_encrypt(AesGcmKey *key, AesGcmIv *iv,
+ u8 *ciphertext, u8 *plaintext, i32 plaintext_len,
+ u8 *tag_out, i32 tag_out_len)
+{
+ EVP_CIPHER_CTX *ctx;
+ i32 ciphertext_len = 0;
+ i32 len = 0;
+
+ ctx = EVP_CIPHER_CTX_new();
+ if (!ctx) {
+ printf("EVP_CIPHER_CTX_new() failed\n");
+ return false;
+ }
+
+ if (EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), 0, 0, 0) != 1) {
+ printf("EVP_EncryptInit_ex() failed\n");
+ EVP_CIPHER_CTX_free(ctx);
+ return false;
+ }
+
+ if(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, 0) != 1) {
+ printf("EVP_CIPHER_CTX_ctrl setting iv length failed\n");
+ EVP_CIPHER_CTX_free(ctx);
+ return false;
+ }
+
+ if (EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), 0, key->buff, (u8*)iv) != 1) {
+ printf("EVP_EncryptInit_ex() failed\n");
+ EVP_CIPHER_CTX_free(ctx);
+ return false;
+ }
+
+ if (EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len) != 1) {
+ printf("EVP_EncryptUpdate failed\n");
+ EVP_CIPHER_CTX_free(ctx);
+ return false;
+ }
+ ciphertext_len += len;
+
+ if (EVP_EncryptFinal_ex(ctx, plaintext + len, &len) != 1) {
+ printf("EVP_EncryptFinal_ex failed\n");
+ EVP_CIPHER_CTX_free(ctx);
+ return false;
+ }
+ ciphertext_len += len;
+
+ if (ciphertext_len != plaintext_len) {
+ printf("aes_gcm_encrypt failed: ciphertext_len = %d, plaintext_len = %d\n", ciphertext_len, plaintext_len);
+ EVP_CIPHER_CTX_free(ctx);
+ return false;
+ }
+
+ if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, tag_out_len, tag_out) != 1) {
+ printf("EVP_CIPHER_CTX_ctrl failed\n");
+ EVP_CIPHER_CTX_free(ctx);
+ return false;
+ }
+
+ EVP_CIPHER_CTX_free(ctx);
+ aes_gcm_iv_advance(iv);
+ return true;
+}
+
diff --git a/src/crypto/aes_gcm.h b/src/crypto/aes_gcm.h
new file mode 100644
index 0000000..6c30171
--- /dev/null
+++ b/src/crypto/aes_gcm.h
@@ -0,0 +1,51 @@
+#ifndef AES_GCM_H
+#define AES_GCM_H
+
+#include <basic/basic.h>
+
+// Note: Aes-256
+// - https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
+// - DANGER: max. 2^32 invocations!!!
+
+// Note: Aes Key
+// - for aes-256 always 32 bytes
+
+// Note: Initialization Vector
+// - for aes-256 is always 12 bytes for optimal usage
+// - https://www.rfc-editor.org/rfc/rfc5288#section-3
+
+// Note: Authentication Tag
+// - is sent with every message
+// - is calculated after whole message is encrypted
+// - len is preferred 16 bytes for max security
+
+
+typedef struct {
+ u8 buff[32];
+} AesGcmKey;
+
+typedef struct {
+ u8 salt[8];
+ u8 counter[4];
+} AesGcmIv;
+
+typedef struct {
+ u32 payload_size;
+ u8 tag[16];
+} AesGcmHeader;
+
+
+b32 aes_gcm_key_init_random(AesGcmKey *key);
+
+b32 aes_gcm_iv_init(AesGcmIv *iv);
+void aes_gcm_iv_advance(AesGcmIv *iv);
+
+b32 aes_gcm_encrypt(AesGcmKey *key, AesGcmIv *iv,
+ u8 *ciphertext, u8 *plaintext, i32 plaintext_len,
+ u8 *tag_out, i32 tag_out_len);
+b32 aes_gcm_decrypt(AesGcmKey *key, AesGcmIv *iv,
+ u8 *plaintext, u8 *ciphertext, i32 ciphertext_len,
+ u8 *tag, i32 tag_len);
+
+
+#endif // AES_GCM_H
diff --git a/src/crypto/rsa.c b/src/crypto/rsa.c
new file mode 100644
index 0000000..e1994ef
--- /dev/null
+++ b/src/crypto/rsa.c
@@ -0,0 +1,235 @@
+#include <crypto/rsa.h>
+#include <os/os.h>
+
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/rand.h>
+
+b32
+rsa_get_pem(EVP_PKEY *key, char *pem, size_t pem_len)
+{
+ const char *pem_in_bio = 0;
+
+ BIO *bio = BIO_new(BIO_s_mem());
+ if (!bio) {
+ printf("BIO_new failed\n");
+ BIO_free(bio);
+ return false;
+ }
+
+ if (!PEM_write_bio_PUBKEY(bio, key)) {
+ printf("PEM_write_bio_PUBKEY failed\n");
+ BIO_free(bio);
+ return false;
+ }
+
+ long data_size = BIO_get_mem_data(bio, &pem_in_bio);
+ if (data_size > pem_len) {
+ BIO_free(bio);
+ return false;
+ }
+
+ memcpy(pem, pem_in_bio, data_size);
+
+ BIO_free(bio);
+ return true;
+}
+
+b32
+rsa_decrypt(EVP_PKEY *key, void *dest, void *src, size_t dest_size)
+{
+ EVP_PKEY_CTX *ctx;
+
+ ctx = EVP_PKEY_CTX_new(key, 0);
+ if (!ctx) {
+ printf("EVP_PKEY_CTX_new failed\n");
+ return false;
+ }
+
+ if (EVP_PKEY_decrypt_init(ctx) <= 0) {
+ printf("EVP_PKEY_decrypt_init failed\n");
+ EVP_PKEY_CTX_free(ctx);
+ return false;
+ }
+
+ if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0) {
+ printf("EVP_PKEY_CTX_set_rsa_padding failed\n");
+ EVP_PKEY_CTX_free(ctx);
+ return false;
+ }
+
+ if (EVP_PKEY_CTX_set_rsa_oaep_md(ctx, EVP_sha512()) <= 0) {
+ printf("EVP_PKEY_CTX_set_rsa_oaep_md failed\n");
+ EVP_PKEY_CTX_free(ctx);
+ return false;
+ }
+
+ // get decrypt output size
+ size_t decrypted_len;
+ if (EVP_PKEY_decrypt(ctx, 0, &decrypted_len, src, 512) <= 0) {
+ printf("EVP_PKEY_decrypt failed\n");
+ EVP_PKEY_CTX_free(ctx);
+ return false;
+ }
+
+ if (decrypted_len != dest_size) {
+ printf("rsa error: decrypt_len != dest_size\n");
+ EVP_PKEY_CTX_free(ctx);
+ return false;
+ }
+
+ // decrypt
+ if (EVP_PKEY_decrypt(ctx, dest, &decrypted_len, src, 512) <= 0) {
+ printf("EVP_PKEY_decrypt failed\n");
+ EVP_PKEY_CTX_free(ctx);
+ return false;
+ }
+
+ EVP_PKEY_CTX_free(ctx);
+ return true;
+}
+
+b32
+rsa_encrypt(EVP_PKEY *key, void *dest, void *src, size_t src_size)
+{
+ EVP_PKEY_CTX *ctx;
+
+ ctx = EVP_PKEY_CTX_new(key, 0);
+ if (!ctx) {
+ printf("EVP_PKEY_CTX_new failed\n");
+ return false;
+ }
+
+ if (EVP_PKEY_encrypt_init(ctx) <= 0) {
+ printf("EVP_PKEY_encrypt_init failed\n");
+ EVP_PKEY_CTX_free(ctx);
+ return false;
+ }
+
+ if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0) {
+ printf("EVP_PKEY_CTX_set_rsa_padding failed\n");
+ EVP_PKEY_CTX_free(ctx);
+ return false;
+ }
+
+ if (EVP_PKEY_CTX_set_rsa_oaep_md(ctx, EVP_sha512()) <= 0) {
+ printf("EVP_PKEY_CTX_set_rsa_oaep_md failed\n");
+ EVP_PKEY_CTX_free(ctx);
+ return false;
+ }
+
+ // get encrypt output size
+ size_t output_len;
+ if (EVP_PKEY_encrypt(ctx, 0, &output_len, src, src_size) <= 0) {
+ printf("EVP_PKEY_encrypt to get size failed\n");
+ EVP_PKEY_CTX_free(ctx);
+ return false;
+ }
+
+ if (output_len != 512) {
+ printf("rsa encryption error: expected encrypt_len 512 but got %zu\n", output_len);
+ EVP_PKEY_CTX_free(ctx);
+ return false;
+ }
+
+ // encrypt
+ if (EVP_PKEY_encrypt(ctx, dest, &output_len, src, src_size) <= 0) {
+ printf("EVP_PKEY_encrypt to encrypt failed\n");
+ EVP_PKEY_CTX_free(ctx);
+ return false;
+ }
+
+ EVP_PKEY_CTX_free(ctx);
+ return true;
+}
+
+void
+rsa_destroy(EVP_PKEY *key)
+{
+ EVP_PKEY_free(key);
+}
+
+EVP_PKEY *
+rsa_create_via_gen(i32 keysize_in_bits)
+{
+ EVP_PKEY *key = 0;
+ EVP_PKEY_CTX *context;
+
+ context = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, 0);
+ if (!context) {
+ printf("EVP_PKEY_CTX_new_id failed\n");
+ goto end;
+ }
+
+ if (EVP_PKEY_keygen_init(context) <= 0) {
+ printf("EVP_PKEY_keygen_init failed\n");
+ goto end;
+ }
+
+ if (EVP_PKEY_CTX_set_rsa_keygen_bits(context, keysize_in_bits) <= 0) {
+ printf("EVP_PKEY_CTX_set_rsa_keygen_bits failed\n");
+ goto end;
+ }
+
+ if (EVP_PKEY_keygen(context, &key) <= 0) {
+ printf("EVP_PKEY_keygen failed\n");
+ goto end;
+ }
+
+end:
+ EVP_PKEY_CTX_free(context);
+ return key;
+}
+
+EVP_PKEY *
+rsa_create_via_pem(char *pem, size_t pem_len, b32 is_public)
+{
+ BIO *bio;
+ bio = BIO_new(BIO_s_mem());
+ if (!bio) {
+ printf("BIO_new failed\n");
+ return 0;
+ }
+
+ if (BIO_write(bio, pem, pem_len) <= 0) {
+ printf("BIO_write failed\n");
+ BIO_free(bio);
+ return 0;
+ }
+
+ EVP_PKEY *key;
+ if (is_public) {
+ key = PEM_read_bio_PUBKEY(bio, 0, 0, 0);
+ if (!key) {
+ printf("PEM_read_bio_PUBKEY failed\n");
+ }
+ } else {
+ key = PEM_read_bio_PrivateKey(bio, 0, 0, 0);
+ if (!key) {
+ printf("PEM_read_bio_PrivateKey failed\n");
+ }
+ }
+
+ BIO_free(bio);
+ return key;
+}
+
+EVP_PKEY *
+rsa_create_via_file(Arena *arena, char *filepath, b32 is_public)
+{
+ EVP_PKEY *key = 0;
+
+ ArenaTmp tmp_arena = arena_tmp_begin(arena);
+
+ size_t pem_len;
+ char *pem = (char*)os_file_read_as_string(arena, filepath, &pem_len);
+ if (!pem) {
+ printf("rsa_create_via_file failed, could not read %s\n", filepath);
+ }
+ key = rsa_create_via_pem(pem, pem_len, is_public);
+
+ arena_tmp_end(tmp_arena);
+
+ return key;
+}
+
diff --git a/src/crypto/rsa.h b/src/crypto/rsa.h
new file mode 100644
index 0000000..cace9dc
--- /dev/null
+++ b/src/crypto/rsa.h
@@ -0,0 +1,19 @@
+#include <basic/basic.h>
+#include <basic/arena.h>
+#include <openssl/evp.h>
+
+// Note: The rsa input size is calculated by modulus_size - 2*hash_len - 2
+// The rsa input size for 4096bit key and SHA-512 = 512-(2*64)-2 = 382 bytes
+// The rsa output size is always equal to the modulus_size (key_size)
+
+// Note: The arena in rsa_create_via_file only uses it temporarily and cleans it up.
+
+EVP_PKEY *rsa_create_via_file(Arena *arena, char *filepath, b32 is_public);
+EVP_PKEY *rsa_create_via_gen(i32 keysize_in_bits);
+EVP_PKEY *rsa_create_via_pem(char *pem, size_t pem_len, b32 is_public);
+void rsa_destroy(EVP_PKEY *key);
+
+b32 rsa_get_pem(EVP_PKEY *key, char *dest, size_t dest_size);
+b32 rsa_encrypt(EVP_PKEY *key, void *dest, void *src, size_t size);
+b32 rsa_decrypt(EVP_PKEY *key, void *dest, void *src, size_t size);
+