diff options
Diffstat (limited to 'src/crypto')
| -rw-r--r-- | src/crypto/aes_gcm.c | 169 | ||||
| -rw-r--r-- | src/crypto/aes_gcm.h | 51 | ||||
| -rw-r--r-- | src/crypto/rsa.c | 235 | ||||
| -rw-r--r-- | src/crypto/rsa.h | 19 | 
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); +  | 
