diff options
Diffstat (limited to '')
-rw-r--r-- | src/crypt.c | 1163 |
1 files changed, 1163 insertions, 0 deletions
diff --git a/src/crypt.c b/src/crypt.c new file mode 100644 index 0000000..b4b48c8 --- /dev/null +++ b/src/crypt.c @@ -0,0 +1,1163 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * VIM - Vi IMproved by Bram Moolenaar + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + * See README.txt for an overview of the Vim source code. + */ + +/* + * crypt.c: Generic encryption support. + */ +#include "vim.h" + +#if defined(FEAT_CRYPT) || defined(PROTO) +/* + * Optional encryption support. + * Mohsin Ahmed, mosh@sasi.com, 1998-09-24 + * Based on zip/crypt sources. + * Refactored by David Leadbeater, 2014. + * + * NOTE FOR USA: Since 2000 exporting this code from the USA is allowed to + * most countries. There are a few exceptions, but that still should not be a + * problem since this code was originally created in Europe and India. + * + * Blowfish addition originally made by Mohsin Ahmed, + * http://www.cs.albany.edu/~mosh 2010-03-14 + * Based on blowfish by Bruce Schneier (http://www.schneier.com/blowfish.html) + * and sha256 by Christophe Devine. + */ + +typedef struct { + char *name; // encryption name as used in 'cryptmethod' + char *magic; // magic bytes stored in file header + int salt_len; // length of salt, or 0 when not using salt + int seed_len; // length of seed, or 0 when not using seed +#ifdef CRYPT_NOT_INPLACE + int works_inplace; // encryption/decryption can be done in-place +#endif + int whole_undofile; // whole undo file is encrypted + + // Optional function pointer for a self-test. + int (* self_test_fn)(); + + // Function pointer for initializing encryption/decryption. + int (* init_fn)(cryptstate_T *state, char_u *key, + char_u *salt, int salt_len, char_u *seed, int seed_len); + + // Function pointers for encoding/decoding from one buffer into another. + // Optional, however, these or the _buffer ones should be configured. + void (*encode_fn)(cryptstate_T *state, char_u *from, size_t len, + char_u *to, int last); + void (*decode_fn)(cryptstate_T *state, char_u *from, size_t len, + char_u *to, int last); + + // Function pointers for encoding and decoding, can buffer data if needed. + // Optional (however, these or the above should be configured). + long (*encode_buffer_fn)(cryptstate_T *state, char_u *from, size_t len, + char_u **newptr, int last); + long (*decode_buffer_fn)(cryptstate_T *state, char_u *from, size_t len, + char_u **newptr, int last); + + // Function pointers for in-place encoding and decoding, used for + // crypt_*_inplace(). "from" and "to" arguments will be equal. + // These may be the same as decode_fn and encode_fn above, however an + // algorithm may implement them in a way that is not interchangeable with + // the crypt_(en|de)code() interface (for example because it wishes to add + // padding to files). + // This method is used for swap and undo files which have a rigid format. + void (*encode_inplace_fn)(cryptstate_T *state, char_u *p1, size_t len, + char_u *p2, int last); + void (*decode_inplace_fn)(cryptstate_T *state, char_u *p1, size_t len, + char_u *p2, int last); +} cryptmethod_T; + +static int crypt_sodium_init_(cryptstate_T *state, char_u *key, char_u *salt, int salt_len, char_u *seed, int seed_len); +static long crypt_sodium_buffer_decode(cryptstate_T *state, char_u *from, size_t len, char_u **buf_out, int last); +static long crypt_sodium_buffer_encode(cryptstate_T *state, char_u *from, size_t len, char_u **buf_out, int last); + +// index is method_nr of cryptstate_T, CRYPT_M_* +static cryptmethod_T cryptmethods[CRYPT_M_COUNT] = { + // PK_Zip; very weak + { + "zip", + "VimCrypt~01!", + 0, + 0, +#ifdef CRYPT_NOT_INPLACE + TRUE, +#endif + FALSE, + NULL, + crypt_zip_init, + crypt_zip_encode, crypt_zip_decode, + NULL, NULL, + crypt_zip_encode, crypt_zip_decode, + }, + + // Blowfish/CFB + SHA-256 custom key derivation; implementation issues. + { + "blowfish", + "VimCrypt~02!", + 8, + 8, +#ifdef CRYPT_NOT_INPLACE + TRUE, +#endif + FALSE, + blowfish_self_test, + crypt_blowfish_init, + crypt_blowfish_encode, crypt_blowfish_decode, + NULL, NULL, + crypt_blowfish_encode, crypt_blowfish_decode, + }, + + // Blowfish/CFB + SHA-256 custom key derivation; fixed. + { + "blowfish2", + "VimCrypt~03!", + 8, + 8, +#ifdef CRYPT_NOT_INPLACE + TRUE, +#endif + TRUE, + blowfish_self_test, + crypt_blowfish_init, + crypt_blowfish_encode, crypt_blowfish_decode, + NULL, NULL, + crypt_blowfish_encode, crypt_blowfish_decode, + }, + + // XChaCha20 using libsodium + { + "xchacha20", + "VimCrypt~04!", +#ifdef FEAT_SODIUM + crypto_pwhash_argon2id_SALTBYTES, // 16 +#else + 16, +#endif + 8, +#ifdef CRYPT_NOT_INPLACE + FALSE, +#endif + FALSE, + NULL, + crypt_sodium_init_, + NULL, NULL, + crypt_sodium_buffer_encode, crypt_sodium_buffer_decode, + NULL, NULL, + }, + + // NOTE: when adding a new method, use some random bytes for the magic key, + // to avoid that a text file is recognized as encrypted. +}; + +#if defined(FEAT_SODIUM) || defined(PROTO) +typedef struct { + size_t count; + unsigned char key[crypto_box_SEEDBYTES]; + // 32, same as crypto_secretstream_xchacha20poly1305_KEYBYTES + crypto_secretstream_xchacha20poly1305_state + state; +} sodium_state_T; + + +# ifdef DYNAMIC_SODIUM +# ifdef MSWIN +# define SODIUM_PROC FARPROC +# define load_dll vimLoadLib +# define symbol_from_dll GetProcAddress +# define close_dll FreeLibrary +# define load_dll_error GetWin32Error +# else +# error Dynamic loading of libsodium is not supported for now. +//# define HINSTANCE void* +//# define SODIUM_PROC void* +//# define load_dll(n) dlopen((n), RTLD_LAZY|RTLD_GLOBAL) +//# define symbol_from_dll dlsym +//# define close_dll dlclose +//# define load_dll_error dlerror +# endif + +# define sodium_init load_sodium +# define sodium_free dll_sodium_free +# define sodium_malloc dll_sodium_malloc +# define sodium_memzero dll_sodium_memzero +# define sodium_mlock dll_sodium_mlock +# define sodium_munlock dll_sodium_munlock +# define crypto_secretstream_xchacha20poly1305_init_push \ + dll_crypto_secretstream_xchacha20poly1305_init_push +# define crypto_secretstream_xchacha20poly1305_push \ + dll_crypto_secretstream_xchacha20poly1305_push +# define crypto_secretstream_xchacha20poly1305_init_pull \ + dll_crypto_secretstream_xchacha20poly1305_init_pull +# define crypto_secretstream_xchacha20poly1305_pull \ + dll_crypto_secretstream_xchacha20poly1305_pull +# define crypto_pwhash dll_crypto_pwhash +# define randombytes_buf dll_randombytes_buf +# define randombytes_random dll_randombytes_random + +static int (*dll_sodium_init)(void) = NULL; +static void (*dll_sodium_free)(void *) = NULL; +static void *(*dll_sodium_malloc)(const size_t) = NULL; +static void (*dll_sodium_memzero)(void * const, const size_t) = NULL; +static int (*dll_sodium_mlock)(void * const, const size_t) = NULL; +static int (*dll_sodium_munlock)(void * const, const size_t) = NULL; +static int (*dll_crypto_secretstream_xchacha20poly1305_init_push) + (crypto_secretstream_xchacha20poly1305_state *state, + unsigned char [], + const unsigned char []) = NULL; +static int (*dll_crypto_secretstream_xchacha20poly1305_push) + (crypto_secretstream_xchacha20poly1305_state *state, + unsigned char *c, unsigned long long *clen_p, + const unsigned char *m, unsigned long long mlen, + const unsigned char *ad, unsigned long long adlen, unsigned char tag) + = NULL; +static int (*dll_crypto_secretstream_xchacha20poly1305_init_pull) + (crypto_secretstream_xchacha20poly1305_state *state, + const unsigned char [], + const unsigned char []) = NULL; +static int (*dll_crypto_secretstream_xchacha20poly1305_pull) + (crypto_secretstream_xchacha20poly1305_state *state, + unsigned char *m, unsigned long long *mlen_p, unsigned char *tag_p, + const unsigned char *c, unsigned long long clen, + const unsigned char *ad, unsigned long long adlen) = NULL; +static int (*dll_crypto_pwhash)(unsigned char * const out, + unsigned long long outlen, + const char * const passwd, unsigned long long passwdlen, + const unsigned char * const salt, + unsigned long long opslimit, size_t memlimit, int alg) + = NULL; +static void (*dll_randombytes_buf)(void * const buf, const size_t size); +static uint32_t (*dll_randombytes_random)(void); + +static struct { + const char *name; + SODIUM_PROC *ptr; +} sodium_funcname_table[] = { + {"sodium_init", (SODIUM_PROC*)&dll_sodium_init}, + {"sodium_free", (SODIUM_PROC*)&dll_sodium_free}, + {"sodium_malloc", (SODIUM_PROC*)&dll_sodium_malloc}, + {"sodium_memzero", (SODIUM_PROC*)&dll_sodium_memzero}, + {"sodium_mlock", (SODIUM_PROC*)&dll_sodium_mlock}, + {"sodium_munlock", (SODIUM_PROC*)&dll_sodium_munlock}, + {"crypto_secretstream_xchacha20poly1305_init_push", (SODIUM_PROC*)&dll_crypto_secretstream_xchacha20poly1305_init_push}, + {"crypto_secretstream_xchacha20poly1305_push", (SODIUM_PROC*)&dll_crypto_secretstream_xchacha20poly1305_push}, + {"crypto_secretstream_xchacha20poly1305_init_pull", (SODIUM_PROC*)&dll_crypto_secretstream_xchacha20poly1305_init_pull}, + {"crypto_secretstream_xchacha20poly1305_pull", (SODIUM_PROC*)&dll_crypto_secretstream_xchacha20poly1305_pull}, + {"crypto_pwhash", (SODIUM_PROC*)&dll_crypto_pwhash}, + {"randombytes_buf", (SODIUM_PROC*)&dll_randombytes_buf}, + {"randombytes_random", (SODIUM_PROC*)&dll_randombytes_random}, + {NULL, NULL} +}; + + static int +sodium_runtime_link_init(int verbose) +{ + static HINSTANCE hsodium = NULL; + const char *libname = DYNAMIC_SODIUM_DLL; + int i; + + if (hsodium != NULL) + return OK; + + hsodium = load_dll(libname); + if (hsodium == NULL) + { + if (verbose) + semsg(_(e_could_not_load_library_str_str), libname, load_dll_error()); + return FAIL; + } + + for (i = 0; sodium_funcname_table[i].ptr; ++i) + { + if ((*sodium_funcname_table[i].ptr = symbol_from_dll(hsodium, + sodium_funcname_table[i].name)) == NULL) + { + close_dll(hsodium); + hsodium = NULL; + if (verbose) + semsg(_(e_could_not_load_library_function_str), sodium_funcname_table[i].name); + return FAIL; + } + } + return OK; +} + + static int +load_sodium(void) +{ + if (sodium_runtime_link_init(TRUE) == FAIL) + return -1; + return dll_sodium_init(); +} +# endif + +# if defined(DYNAMIC_SODIUM) || defined(PROTO) + int +sodium_enabled(int verbose) +{ + return sodium_runtime_link_init(verbose) == OK; +} +# endif +#endif + +#define CRYPT_MAGIC_LEN 12 // cannot change +static char crypt_magic_head[] = "VimCrypt~"; + +/* + * Return int value for crypt method name. + * 0 for "zip", the old method. Also for any non-valid value. + * 1 for "blowfish". + * 2 for "blowfish2". + */ + int +crypt_method_nr_from_name(char_u *name) +{ + int i; + + for (i = 0; i < CRYPT_M_COUNT; ++i) + if (STRCMP(name, cryptmethods[i].name) == 0) + return i; + return 0; +} + +/* + * Get the crypt method used for a file from "ptr[len]", the magic text at the + * start of the file. + * Returns -1 when no encryption used. + */ + int +crypt_method_nr_from_magic(char *ptr, int len) +{ + int i; + + if (len < CRYPT_MAGIC_LEN) + return -1; + + for (i = 0; i < CRYPT_M_COUNT; i++) + if (memcmp(ptr, cryptmethods[i].magic, CRYPT_MAGIC_LEN) == 0) + return i; + + i = (int)STRLEN(crypt_magic_head); + if (len >= i && memcmp(ptr, crypt_magic_head, i) == 0) + emsg(_(e_file_is_encrypted_with_unknown_method)); + + return -1; +} + +#ifdef CRYPT_NOT_INPLACE +/* + * Return TRUE if the crypt method for "method_nr" can be done in-place. + */ + int +crypt_works_inplace(cryptstate_T *state) +{ + return cryptmethods[state->method_nr].works_inplace; +} +#endif + +/* + * Get the crypt method for buffer "buf" as a number. + */ + int +crypt_get_method_nr(buf_T *buf) +{ + return crypt_method_nr_from_name(*buf->b_p_cm == NUL ? p_cm : buf->b_p_cm); +} + +/* + * Return TRUE when the buffer uses an encryption method that encrypts the + * whole undo file, not only the text. + */ + int +crypt_whole_undofile(int method_nr) +{ + return cryptmethods[method_nr].whole_undofile; +} + +/* + * Get crypt method specific length of the file header in bytes. + */ + int +crypt_get_header_len(int method_nr) +{ + return CRYPT_MAGIC_LEN + + cryptmethods[method_nr].salt_len + + cryptmethods[method_nr].seed_len; +} + + +#if defined(FEAT_SODIUM) || defined(PROTO) +/* + * Get maximum crypt method specific length of the file header in bytes. + */ + int +crypt_get_max_header_len(void) +{ + int i; + int max = 0; + int temp = 0; + + for (i = 0; i < CRYPT_M_COUNT; ++i) + { + temp = crypt_get_header_len(i); + if (temp > max) + max = temp; + } + return max; +} +#endif + +/* + * Set the crypt method for buffer "buf" to "method_nr" using the int value as + * returned by crypt_method_nr_from_name(). + */ + void +crypt_set_cm_option(buf_T *buf, int method_nr) +{ + free_string_option(buf->b_p_cm); + buf->b_p_cm = vim_strsave((char_u *)cryptmethods[method_nr].name); +} + +/* + * If the crypt method for the current buffer has a self-test, run it and + * return OK/FAIL. + */ + int +crypt_self_test(void) +{ + int method_nr = crypt_get_method_nr(curbuf); + + if (cryptmethods[method_nr].self_test_fn == NULL) + return OK; + return cryptmethods[method_nr].self_test_fn(); +} + +/* + * Allocate a crypt state and initialize it. + * Return NULL for failure. + */ + cryptstate_T * +crypt_create( + int method_nr, + char_u *key, + char_u *salt, + int salt_len, + char_u *seed, + int seed_len) +{ + cryptstate_T *state = ALLOC_ONE(cryptstate_T); + + if (state == NULL) + return state; + + state->method_nr = method_nr; + if (cryptmethods[method_nr].init_fn( + state, key, salt, salt_len, seed, seed_len) == FAIL) + { + vim_free(state); + return NULL; + } + return state; +} + +/* + * Allocate a crypt state from a file header and initialize it. + * Assumes that header contains at least the number of bytes that + * crypt_get_header_len() returns for "method_nr". + */ + cryptstate_T * +crypt_create_from_header( + int method_nr, + char_u *key, + char_u *header) +{ + char_u *salt = NULL; + char_u *seed = NULL; + int salt_len = cryptmethods[method_nr].salt_len; + int seed_len = cryptmethods[method_nr].seed_len; + + if (salt_len > 0) + salt = header + CRYPT_MAGIC_LEN; + if (seed_len > 0) + seed = header + CRYPT_MAGIC_LEN + salt_len; + + return crypt_create(method_nr, key, salt, salt_len, seed, seed_len); +} + +/* + * Read the crypt method specific header data from "fp". + * Return an allocated cryptstate_T or NULL on error. + */ + cryptstate_T * +crypt_create_from_file(FILE *fp, char_u *key) +{ + int method_nr; + int header_len; + char magic_buffer[CRYPT_MAGIC_LEN]; + char_u *buffer; + cryptstate_T *state; + + if (fread(magic_buffer, CRYPT_MAGIC_LEN, 1, fp) != 1) + return NULL; + method_nr = crypt_method_nr_from_magic(magic_buffer, CRYPT_MAGIC_LEN); + if (method_nr < 0) + return NULL; + + header_len = crypt_get_header_len(method_nr); + if ((buffer = alloc(header_len)) == NULL) + return NULL; + mch_memmove(buffer, magic_buffer, CRYPT_MAGIC_LEN); + if (header_len > CRYPT_MAGIC_LEN + && fread(buffer + CRYPT_MAGIC_LEN, + header_len - CRYPT_MAGIC_LEN, 1, fp) != 1) + { + vim_free(buffer); + return NULL; + } + + state = crypt_create_from_header(method_nr, key, buffer); + vim_free(buffer); + return state; +} + +/* + * Allocate a cryptstate_T for writing and initialize it with "key". + * Allocates and fills in the header and stores it in "header", setting + * "header_len". The header may include salt and seed, depending on + * cryptmethod. Caller must free header. + * Returns the state or NULL on failure. + */ + cryptstate_T * +crypt_create_for_writing( + int method_nr, + char_u *key, + char_u **header, + int *header_len) +{ + int len = crypt_get_header_len(method_nr); + char_u *salt = NULL; + char_u *seed = NULL; + int salt_len = cryptmethods[method_nr].salt_len; + int seed_len = cryptmethods[method_nr].seed_len; + cryptstate_T *state; + + *header_len = len; + *header = alloc(len); + if (*header == NULL) + return NULL; + + mch_memmove(*header, cryptmethods[method_nr].magic, CRYPT_MAGIC_LEN); + if (salt_len > 0 || seed_len > 0) + { + if (salt_len > 0) + salt = *header + CRYPT_MAGIC_LEN; + if (seed_len > 0) + seed = *header + CRYPT_MAGIC_LEN + salt_len; + + // TODO: Should this be crypt method specific? (Probably not worth + // it). sha2_seed is pretty bad for large amounts of entropy, so make + // that into something which is suitable for anything. +#ifdef FEAT_SODIUM + if (sodium_init() >= 0) + { + if (salt_len > 0) + randombytes_buf(salt, salt_len); + if (seed_len > 0) + randombytes_buf(seed, seed_len); + } + else +#endif + sha2_seed(salt, salt_len, seed, seed_len); + } + state = crypt_create(method_nr, key, salt, salt_len, seed, seed_len); + if (state == NULL) + VIM_CLEAR(*header); + return state; +} + +/* + * Free the crypt state. + */ + void +crypt_free_state(cryptstate_T *state) +{ +#ifdef FEAT_SODIUM + if (state->method_nr == CRYPT_M_SOD) + { + sodium_munlock(((sodium_state_T *)state->method_state)->key, + crypto_box_SEEDBYTES); + sodium_memzero(state->method_state, sizeof(sodium_state_T)); + sodium_free(state->method_state); + } + else +#endif + vim_free(state->method_state); + vim_free(state); +} + +#ifdef CRYPT_NOT_INPLACE +/* + * Encode "from[len]" and store the result in a newly allocated buffer, which + * is stored in "newptr". + * Return number of bytes in "newptr", 0 for need more or -1 on error. + */ + long +crypt_encode_alloc( + cryptstate_T *state, + char_u *from, + size_t len, + char_u **newptr, + int last) +{ + cryptmethod_T *method = &cryptmethods[state->method_nr]; + + if (method->encode_buffer_fn != NULL) + // Has buffer function, pass through. + return method->encode_buffer_fn(state, from, len, newptr, last); + if (len == 0) + // Not buffering, just return EOF. + return (long)len; + + *newptr = alloc(len + 50); + if (*newptr == NULL) + return -1; + method->encode_fn(state, from, len, *newptr, last); + return (long)len; +} + +/* + * Decrypt "ptr[len]" and store the result in a newly allocated buffer, which + * is stored in "newptr". + * Return number of bytes in "newptr", 0 for need more or -1 on error. + */ + long +crypt_decode_alloc( + cryptstate_T *state, + char_u *ptr, + long len, + char_u **newptr, + int last) +{ + cryptmethod_T *method = &cryptmethods[state->method_nr]; + + if (method->decode_buffer_fn != NULL) + // Has buffer function, pass through. + return method->decode_buffer_fn(state, ptr, len, newptr, last); + + if (len == 0) + // Not buffering, just return EOF. + return len; + + *newptr = alloc(len); + if (*newptr == NULL) + return -1; + method->decode_fn(state, ptr, len, *newptr, last); + return len; +} +#endif + +/* + * Encrypting "from[len]" into "to[len]". + */ + void +crypt_encode( + cryptstate_T *state, + char_u *from, + size_t len, + char_u *to, + int last) +{ + cryptmethods[state->method_nr].encode_fn(state, from, len, to, last); +} + +#if 0 // unused +/* + * decrypting "from[len]" into "to[len]". + */ + void +crypt_decode( + cryptstate_T *state, + char_u *from, + size_t len, + char_u *to, + int last) +{ + cryptmethods[state->method_nr].decode_fn(state, from, len, to, last); +} +#endif + +/* + * Simple inplace encryption, modifies "buf[len]" in place. + */ + void +crypt_encode_inplace( + cryptstate_T *state, + char_u *buf, + size_t len, + int last) +{ + cryptmethods[state->method_nr].encode_inplace_fn(state, buf, len, + buf, last); +} + +/* + * Simple inplace decryption, modifies "buf[len]" in place. + */ + void +crypt_decode_inplace( + cryptstate_T *state, + char_u *buf, + size_t len, + int last) +{ + cryptmethods[state->method_nr].decode_inplace_fn(state, buf, len, + buf, last); +} + +/* + * Free an allocated crypt key. Clear the text to make sure it doesn't stay + * in memory anywhere. + */ + void +crypt_free_key(char_u *key) +{ + char_u *p; + + if (key != NULL) + { + for (p = key; *p != NUL; ++p) + *p = 0; + vim_free(key); + } +} + +/* + * Check the crypt method and give a warning if it's outdated. + */ + void +crypt_check_method(int method) +{ + if (method < CRYPT_M_BF2) + { + msg_scroll = TRUE; + msg(_("Warning: Using a weak encryption method; see :help 'cm'")); + } +} + +#ifdef FEAT_SODIUM + static void +crypt_check_swapfile_curbuf(void) +{ + int method = crypt_get_method_nr(curbuf); + if (method == CRYPT_M_SOD) + { + // encryption uses padding and MAC, that does not work very well with + // swap and undo files, so disable them + mf_close_file(curbuf, TRUE); // remove the swap file + set_option_value_give_err((char_u *)"swf", 0, NULL, OPT_LOCAL); + msg_scroll = TRUE; + msg(_("Note: Encryption of swapfile not supported, disabling swap file")); + } +} +#endif + + void +crypt_check_current_method(void) +{ + crypt_check_method(crypt_get_method_nr(curbuf)); +} + +/* + * Ask the user for a crypt key. + * When "store" is TRUE, the new key is stored in the 'key' option, and the + * 'key' option value is returned: Don't free it. + * When "store" is FALSE, the typed key is returned in allocated memory. + * Returns NULL on failure. + */ + char_u * +crypt_get_key( + int store, + int twice) // Ask for the key twice. +{ + char_u *p1, *p2 = NULL; + int round; + + for (round = 0; ; ++round) + { + cmdline_star = TRUE; + cmdline_row = msg_row; + p1 = getcmdline_prompt(NUL, round == 0 + ? (char_u *)_("Enter encryption key: ") + : (char_u *)_("Enter same key again: "), 0, EXPAND_NOTHING, + NULL); + cmdline_star = FALSE; + + if (p1 == NULL) + break; + + if (round == twice) + { + if (p2 != NULL && STRCMP(p1, p2) != 0) + { + msg(_("Keys don't match!")); + crypt_free_key(p1); + crypt_free_key(p2); + p2 = NULL; + round = -1; // do it again + continue; + } + + if (store) + { + set_option_value_give_err((char_u *)"key", 0L, p1, OPT_LOCAL); + crypt_free_key(p1); + p1 = curbuf->b_p_key; +#ifdef FEAT_SODIUM + crypt_check_swapfile_curbuf(); +#endif + } + break; + } + p2 = p1; + } + + // since the user typed this, no need to wait for return + if (crypt_get_method_nr(curbuf) != CRYPT_M_SOD) + { + if (msg_didout) + msg_putchar('\n'); + need_wait_return = FALSE; + msg_didout = FALSE; + } + + crypt_free_key(p2); + return p1; +} + + +/* + * Append a message to IObuff for the encryption/decryption method being used. + */ + void +crypt_append_msg( + buf_T *buf) +{ + if (crypt_get_method_nr(buf) == 0) + STRCAT(IObuff, _("[crypted]")); + else + { + STRCAT(IObuff, "["); + STRCAT(IObuff, *buf->b_p_cm == NUL ? p_cm : buf->b_p_cm); + STRCAT(IObuff, "]"); + } +} + + static int +crypt_sodium_init_( + cryptstate_T *state UNUSED, + char_u *key UNUSED, + char_u *salt UNUSED, + int salt_len UNUSED, + char_u *seed UNUSED, + int seed_len UNUSED) +{ +# ifdef FEAT_SODIUM + // crypto_box_SEEDBYTES == crypto_secretstream_xchacha20poly1305_KEYBYTES + unsigned char dkey[crypto_box_SEEDBYTES]; // 32 + sodium_state_T *sd_state; + int retval = 0; + + if (sodium_init() < 0) + return FAIL; + + sd_state = (sodium_state_T *)sodium_malloc(sizeof(sodium_state_T)); + sodium_memzero(sd_state, sizeof(sodium_state_T)); + + // derive a key from the password + if (crypto_pwhash(dkey, sizeof(dkey), (const char *)key, STRLEN(key), salt, + crypto_pwhash_OPSLIMIT_INTERACTIVE, crypto_pwhash_MEMLIMIT_INTERACTIVE, + crypto_pwhash_ALG_DEFAULT) != 0) + { + // out of memory + sodium_free(sd_state); + return FAIL; + } + memcpy(sd_state->key, dkey, crypto_box_SEEDBYTES); + + retval += sodium_mlock(sd_state->key, crypto_box_SEEDBYTES); + retval += sodium_mlock(key, STRLEN(key)); + + if (retval < 0) + { + emsg(_(e_encryption_sodium_mlock_failed)); + sodium_free(sd_state); + return FAIL; + } + sd_state->count = 0; + state->method_state = sd_state; + + return OK; +# else + emsg(e_libsodium_not_built_in); + return FAIL; +# endif +} + +/* + * Encrypt "from[len]" into "to[len]". + * "from" and "to" can be equal to encrypt in place. + * Call needs to ensure that there is enough space in to (for the header) + */ +#if 0 // Currently unused + void +crypt_sodium_encode( + cryptstate_T *state UNUSED, + char_u *from UNUSED, + size_t len UNUSED, + char_u *to UNUSED, + int last UNUSED) +{ +# ifdef FEAT_SODIUM + // crypto_box_SEEDBYTES == crypto_secretstream_xchacha20poly1305_KEYBYTES + sodium_state_T *sod_st = state->method_state; + unsigned char tag = last + ? crypto_secretstream_xchacha20poly1305_TAG_FINAL : 0; + + if (sod_st->count == 0) + { + if (len <= crypto_secretstream_xchacha20poly1305_HEADERBYTES) + { + emsg(e_libsodium_cannot_encrypt_header); + return; + } + crypto_secretstream_xchacha20poly1305_init_push(&sod_st->state, + to, sod_st->key); + to += crypto_secretstream_xchacha20poly1305_HEADERBYTES; + } + + if (sod_st->count && len <= crypto_secretstream_xchacha20poly1305_ABYTES) + { + emsg(e_libsodium_cannot_encrypt_buffer); + return; + } + + crypto_secretstream_xchacha20poly1305_push(&sod_st->state, to, NULL, + from, len, NULL, 0, tag); + + sod_st->count++; +# endif +} +#endif + +/* + * Decrypt "from[len]" into "to[len]". + * "from" and "to" can be equal to encrypt in place. + */ +#if 0 // Currently unused + void +crypt_sodium_decode( + cryptstate_T *state UNUSED, + char_u *from UNUSED, + size_t len UNUSED, + char_u *to UNUSED, + int last UNUSED) +{ +# ifdef FEAT_SODIUM + // crypto_box_SEEDBYTES == crypto_secretstream_xchacha20poly1305_KEYBYTES + sodium_state_T *sod_st = state->method_state; + unsigned char tag; + unsigned long long buf_len; + char_u *p1 = from; + char_u *p2 = to; + char_u *buf_out; + + if (sod_st->count == 0 + && len <= crypto_secretstream_xchacha20poly1305_HEADERBYTES) + { + emsg(e_libsodium_cannot_decrypt_header); + return; + } + + buf_out = (char_u *)alloc(len); + + if (buf_out == NULL) + { + emsg(e_libsodium_cannot_allocate_buffer); + return; + } + if (sod_st->count == 0) + { + if (crypto_secretstream_xchacha20poly1305_init_pull( + &sod_st->state, from, sod_st->key) != 0) + { + emsg(e_libsodium_decryption_failed_header_incomplete); + goto fail; + } + + from += crypto_secretstream_xchacha20poly1305_HEADERBYTES; + len -= crypto_secretstream_xchacha20poly1305_HEADERBYTES; + + if (p1 == p2) + to += crypto_secretstream_xchacha20poly1305_HEADERBYTES; + } + + if (sod_st->count && len <= crypto_secretstream_xchacha20poly1305_ABYTES) + { + emsg(e_libsodium_cannot_decrypt_buffer); + goto fail; + } + if (crypto_secretstream_xchacha20poly1305_pull(&sod_st->state, + buf_out, &buf_len, &tag, from, len, NULL, 0) != 0) + { + emsg(e_libsodium_decryption_failed); + goto fail; + } + sod_st->count++; + + if (tag == crypto_secretstream_xchacha20poly1305_TAG_FINAL && !last) + { + emsg(e_libsodium_decryption_failed_premature); + goto fail; + } + if (p1 == p2) + mch_memmove(p2, buf_out, buf_len); + +fail: + vim_free(buf_out); +# endif +} +#endif + +/* + * Encrypt "from[len]" into "to[len]". + * "from" and "to" can be equal to encrypt in place. + */ + static long +crypt_sodium_buffer_encode( + cryptstate_T *state UNUSED, + char_u *from UNUSED, + size_t len UNUSED, + char_u **buf_out UNUSED, + int last UNUSED) +{ +# ifdef FEAT_SODIUM + // crypto_box_SEEDBYTES == crypto_secretstream_xchacha20poly1305_KEYBYTES + unsigned long long out_len; + char_u *ptr; + unsigned char tag = last + ? crypto_secretstream_xchacha20poly1305_TAG_FINAL : 0; + int length; + sodium_state_T *sod_st = state->method_state; + int first = (sod_st->count == 0); + + length = (int)len + crypto_secretstream_xchacha20poly1305_ABYTES + + (first ? crypto_secretstream_xchacha20poly1305_HEADERBYTES : 0); + *buf_out = alloc_clear(length); + if (*buf_out == NULL) + { + emsg(e_libsodium_cannot_allocate_buffer); + return -1; + } + ptr = *buf_out; + + if (first) + { + crypto_secretstream_xchacha20poly1305_init_push(&sod_st->state, + ptr, sod_st->key); + ptr += crypto_secretstream_xchacha20poly1305_HEADERBYTES; + } + + crypto_secretstream_xchacha20poly1305_push(&sod_st->state, ptr, + &out_len, from, len, NULL, 0, tag); + + sod_st->count++; + return out_len + (first + ? crypto_secretstream_xchacha20poly1305_HEADERBYTES : 0); +# else + return -1; +# endif +} + +/* + * Decrypt "from[len]" into "to[len]". + * "from" and "to" can be equal to encrypt in place. + */ + static long +crypt_sodium_buffer_decode( + cryptstate_T *state UNUSED, + char_u *from UNUSED, + size_t len UNUSED, + char_u **buf_out UNUSED, + int last UNUSED) +{ +# ifdef FEAT_SODIUM + // crypto_box_SEEDBYTES == crypto_secretstream_xchacha20poly1305_KEYBYTES + sodium_state_T *sod_st = state->method_state; + unsigned char tag; + unsigned long long out_len; + *buf_out = alloc_clear(len); + if (*buf_out == NULL) + { + emsg(e_libsodium_cannot_allocate_buffer); + return -1; + } + + if (sod_st->count == 0) + { + if (crypto_secretstream_xchacha20poly1305_init_pull(&sod_st->state, + from, sod_st->key) != 0) + { + emsg(e_libsodium_decryption_failed_header_incomplete); + return -1; + } + from += crypto_secretstream_xchacha20poly1305_HEADERBYTES; + len -= crypto_secretstream_xchacha20poly1305_HEADERBYTES; + sod_st->count++; + } + if (crypto_secretstream_xchacha20poly1305_pull(&sod_st->state, + *buf_out, &out_len, &tag, from, len, NULL, 0) != 0) + { + emsg(e_libsodium_decryption_failed); + return -1; + } + + if (tag == crypto_secretstream_xchacha20poly1305_TAG_FINAL && !last) + emsg(e_libsodium_decryption_failed_premature); + return (long) out_len; +# else + return -1; +# endif +} + +# if defined(FEAT_SODIUM) || defined(PROTO) + int +crypt_sodium_munlock(void *const addr, const size_t len) +{ + return sodium_munlock(addr, len); +} + + void +crypt_sodium_randombytes_buf(void *const buf, const size_t size) +{ + randombytes_buf(buf, size); +} + + int +crypt_sodium_init(void) +{ + return sodium_init(); +} + + uint32_t +crypt_sodium_randombytes_random(void) +{ + return randombytes_random(); +} +# endif + +#endif // FEAT_CRYPT |