home / secrets / src / core / crypt.c

crypt.c



//
//  crypt.c
//  secrets
//
//  Created by Anders on 08/07/2020.
//
#if defined linux || defined __linux__
#include <bsd/stdlib.h>
#endif
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "crypt.h"
#include "../lib/monocypher.h"
#include "../util/require.h"
#include "../util/stringu.h"

static void create_key(uint8_t key[KEY_SIZE], const uint8_t salt[SALT_SIZE], char *password) {
    uint32_t password_size = (uint32_t) strlen(password);
    
    const uint32_t nb_blocks = 100000; /* 100 megabytes   */
    const uint32_t nb_iterations = 3; /* 3 iterations    */
    void *work_area = malloc(nb_blocks * 1024); /* Work area       */
    if (work_area == NULL) {
        /* Handle malloc() failure */
        /* Wipe secrets if they are no longer needed */
        crypto_wipe(password, password_size);
    } else {
        crypto_argon2i(key, KEY_SIZE,
                       work_area, nb_blocks, nb_iterations,
                       (uint8_t *)password, password_size,
                       salt, SALT_SIZE);
        crypto_wipe(password, password_size);
        free(work_area);
    }
}

static void encrypt(Secret *secret, uint8_t key[KEY_SIZE], char *message) {
    uint16_t message_size = (uint16_t) strlen(message);
    
    secret->cipher = malloc(message_size);
    secret->cipher_size = message_size;
    
    arc4random_buf(secret->nonce, NONCE_SIZE);
    crypto_lock(secret->mac, secret->cipher, key, secret->nonce, (uint8_t*) message, message_size);

    crypt_wipe(message);
    crypto_wipe(key, KEY_SIZE);
}


static bool decrypt(FILE* dest, uint8_t key[KEY_SIZE], const Secret *secret) {
    char message[secret->cipher_size + 1];
    message[secret->cipher_size] = '\0';
    
    if (crypto_unlock((uint8_t*) message, key, secret->nonce, secret->mac, secret->cipher, secret->cipher_size)) {
        /* The message is corrupted.
         * Wipe key if it is no longer needed,
         * and abort the decryption.
         */
        crypto_wipe(key, KEY_SIZE);
        return false;
    } else {
        fprintf(dest, "%s", message);
        crypt_wipe(message);
        crypto_wipe(key, KEY_SIZE);
        return true;;
    }
}

Secret* crypt_create_alloc(char *password, char *message) {
    require(!stringu_equals(password, message), "secret and decrypt-password should not be the same\n");
    
    Secret* secret = calloc(1, sizeof(*secret));
    
    arc4random_buf(secret->salt, SALT_SIZE);
    uint8_t key[KEY_SIZE];
    create_key(key, secret->salt, password);
    
    encrypt(secret, key, message);
    
    return secret;
    
}

bool crypt_print(FILE* dest, char *password, const Secret* secret) {
    uint8_t key[KEY_SIZE];
    create_key(key, secret->salt, password);
    return decrypt(dest, key, secret);
}

void crypt_wipe(char *text) {
    crypto_wipe(text, strlen(text) + 1);
}