diff options
author | Christian Brabandt <cb@256bit.org> | 2021-06-20 14:02:16 +0200 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2021-06-20 14:02:16 +0200 |
commit | f573c6e1ed58d46d694c802eaf5ae3662a952744 (patch) | |
tree | 9d3ccf4402f322b9d1baf130696a6b0e600ae693 | |
parent | 208f0b48b2c616b29f377a1408290111ed2663f7 (diff) |
patch 8.2.3022: available encryption methods are not strong enoughv8.2.3022
Problem: Available encryption methods are not strong enough.
Solution: Add initial support for xchaha20. (Christian Brabandt,
closes #8394)
-rw-r--r-- | .github/workflows/ci.yml | 3 | ||||
-rw-r--r-- | runtime/doc/eval.txt | 1 | ||||
-rw-r--r-- | runtime/doc/options.txt | 17 | ||||
-rw-r--r-- | runtime/doc/various.txt | 1 | ||||
-rw-r--r-- | src/INSTALLpc.txt | 3 | ||||
-rw-r--r-- | src/Make_cyg_ming.mak | 15 | ||||
-rw-r--r-- | src/Make_mvc.mak | 29 | ||||
-rwxr-xr-x | src/auto/configure | 66 | ||||
-rw-r--r-- | src/blowfish.c | 7 | ||||
-rw-r--r-- | src/bufwrite.c | 19 | ||||
-rw-r--r-- | src/config.h.in | 1 | ||||
-rw-r--r-- | src/configure.ac | 37 | ||||
-rw-r--r-- | src/crypt.c | 385 | ||||
-rw-r--r-- | src/crypt_zip.c | 6 | ||||
-rw-r--r-- | src/errors.h | 19 | ||||
-rw-r--r-- | src/evalfunc.c | 7 | ||||
-rw-r--r-- | src/feature.h | 7 | ||||
-rw-r--r-- | src/fileio.c | 40 | ||||
-rw-r--r-- | src/memline.c | 38 | ||||
-rw-r--r-- | src/option.c | 4 | ||||
-rw-r--r-- | src/optionstr.c | 6 | ||||
-rw-r--r-- | src/proto/blowfish.pro | 4 | ||||
-rw-r--r-- | src/proto/crypt.pro | 14 | ||||
-rw-r--r-- | src/proto/crypt_zip.pro | 4 | ||||
-rw-r--r-- | src/structs.h | 5 | ||||
-rw-r--r-- | src/testdir/samples/crypt_sodium_invalid.txt | bin | 0 -> 16504 bytes | |||
-rw-r--r-- | src/testdir/test_crypt.vim | 127 | ||||
-rw-r--r-- | src/undo.c | 12 | ||||
-rw-r--r-- | src/version.c | 7 |
29 files changed, 820 insertions, 64 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9127455bc1..e569b42397 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -71,7 +71,8 @@ jobs: cscope \ libgtk2.0-dev \ desktop-file-utils \ - libtool-bin + libtool-bin \ + libsodium-dev - name: Install clang-11 if: matrix.compiler == 'clang' diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index dd2d24232a..4620623c63 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -11971,6 +11971,7 @@ scrollbind Compiled with 'scrollbind' support. (always true) showcmd Compiled with 'showcmd' support. signs Compiled with |:sign| support. smartindent Compiled with 'smartindent' support. +sodium Compiled with libsodium for better crypt support sound Compiled with sound support, e.g. `sound_playevent()` spell Compiled with spell checking support |spell|. startuptime Compiled with |--startuptime| support. diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index e600dd91dd..19ddd9cb5f 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -2384,6 +2384,23 @@ A jump table for the options with a short description can be found at |Q_op|. you write the file the encrypted bytes will be different. The whole undo file is encrypted, not just the pieces of text. + *E1193* *E1194* *E1195* *E1196* + *E1197* *E1198* *E1199* *E1200* *E1201* + xchacha20 XChaCha20 Cipher with Poly1305 Message Authentication + Code. Medium strong till strong encryption. + Encryption is provided by the libsodium library, it + requires Vim to be built with |+sodium| + It adds a seed and a message authentication code (MAC) + to the file. This needs at least a Vim 8.2.3022 to + read the encrypted file. + Encryption of swap files is not supported, therefore + no swap file will be used when xchacha20 encryption is + enabled. + Encryption of undo files is not yet supported, + therefore no undo file will currently be written. + CURRENTLY EXPERIMENTAL: Files written with this method + might have to be read back with the same version of + Vim if the binary format changes later. You should use "blowfish2", also to re-encrypt older files. diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt index eb45922ccf..0ad12e8042 100644 --- a/runtime/doc/various.txt +++ b/runtime/doc/various.txt @@ -444,6 +444,7 @@ m *+ruby/dyn* Ruby interface |ruby-dynamic| |/dyn| T *+scrollbind* |'scrollbind'| B *+signs* |:sign| N *+smartindent* |'smartindent'| +B *+sodium* compiled with libsodium for better encryption support B *+sound* |sound_playevent()|, |sound_playfile()| functions, etc. N *+spell* spell checking support, see |spell| N *+startuptime* |--startuptime| argument diff --git a/src/INSTALLpc.txt b/src/INSTALLpc.txt index b5c1a0b58c..d5516c891a 100644 --- a/src/INSTALLpc.txt +++ b/src/INSTALLpc.txt @@ -322,6 +322,9 @@ MSYS2 has its own git package, and you can also install it via pacman: $ pacman -S git +For enabling libsodium support, you also need to install the package + + $ pacman -S mingw-w64-x86_64-libsodium 2.3. Keep the build environment up-to-date diff --git a/src/Make_cyg_ming.mak b/src/Make_cyg_ming.mak index c33200b33e..e93165dbf4 100644 --- a/src/Make_cyg_ming.mak +++ b/src/Make_cyg_ming.mak @@ -41,6 +41,9 @@ DEBUG=no # set to yes to measure code coverage COVERAGE=no +# better encryption support using libsodium +#SODIUM=yes + # set to SIZE for size, SPEED for speed, MAXSPEED for maximum optimization OPTIMIZE=MAXSPEED @@ -517,6 +520,10 @@ CXXFLAGS = -std=gnu++11 WINDRES_FLAGS = EXTRA_LIBS = +ifdef SODIUM +DEFINES += -DHAVE_SODIUM +endif + ifdef GETTEXT DEFINES += -DHAVE_GETTEXT -DHAVE_LOCALE_H GETTEXTINCLUDE = $(GETTEXT)/include @@ -660,6 +667,10 @@ DEFINES += -DFEAT_DIRECTX_COLOR_EMOJI endif endif +ifeq ($(SODIUM),yes) +SODIUMLIB = -lsodium +endif + # Only allow XPM for a GUI build. ifeq (yes, $(GUI)) @@ -1064,7 +1075,7 @@ $(EXEOBJC): | $(OUTDIR) ifeq ($(VIMDLL),yes) $(TARGET): $(OBJ) - $(LINK) $(CFLAGS) $(LFLAGS) -o $@ $(OBJ) $(LIB) -lole32 -luuid -lgdi32 $(LUA_LIB) $(MZSCHEME_LIBDIR) $(MZSCHEME_LIB) $(PYTHONLIB) $(PYTHON3LIB) $(RUBYLIB) + $(LINK) $(CFLAGS) $(LFLAGS) -o $@ $(OBJ) $(LIB) -lole32 -luuid -lgdi32 $(LUA_LIB) $(MZSCHEME_LIBDIR) $(MZSCHEME_LIB) $(PYTHONLIB) $(PYTHON3LIB) $(RUBYLIB) $(SODIUMLIB) $(GVIMEXE): $(EXEOBJG) $(VIMDLLBASE).dll $(CC) -L. $(EXELFLAGS) -mwindows -o $@ $(EXEOBJG) -l$(VIMDLLBASE) @@ -1073,7 +1084,7 @@ $(VIMEXE): $(EXEOBJC) $(VIMDLLBASE).dll $(CC) -L. $(EXELFLAGS) -o $@ $(EXEOBJC) -l$(VIMDLLBASE) else $(TARGET): $(OBJ) - $(LINK) $(CFLAGS) $(LFLAGS) -o $@ $(OBJ) $(LIB) -lole32 -luuid $(LUA_LIB) $(MZSCHEME_LIBDIR) $(MZSCHEME_LIB) $(PYTHONLIB) $(PYTHON3LIB) $(RUBYLIB) + $(LINK) $(CFLAGS) $(LFLAGS) -o $@ $(OBJ) $(LIB) -lole32 -luuid $(LUA_LIB) $(MZSCHEME_LIBDIR) $(MZSCHEME_LIB) $(PYTHONLIB) $(PYTHON3LIB) $(RUBYLIB) $(SODIUMLIB) endif upx: exes diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak index 42b6f8ddf6..c61bb27ab7 100644 --- a/src/Make_mvc.mak +++ b/src/Make_mvc.mak @@ -41,6 +41,9 @@ # # Sound support: SOUND=yes (default is yes) # +# Sodium support: SODIUM=[Path to Sodium directory] +# You need to install the msvc package from https://download.libsodium.org/libsodium/releases/ +# # DLL support (EXPERIMENTAL): VIMDLL=yes (default is no) # Creates vim{32,64}.dll, and stub gvim.exe and vim.exe. # The shared codes between the GUI and the console are built into @@ -372,6 +375,26 @@ SOUND = no ! endif !endif +!ifndef SODIUM +SODIUM = no +!endif + +!if "$(SODIUM)" != "no" +! if "$(CPU)" == "AMD64" +SOD_LIB = $(SODIUM)\x64\Release\v140\dynamic +! elseif "$(CPU)" == "i386" +SOD_LIB = $(SODIUM)\x86\Release\v140\dynamic +! else +SODIUM = no +! endif +!endif + +!if "$(SODIUM)" != "no" +SOD_INC = -I $(SODIUM)\include +SOD_DEFS = -DFEAT_SODIUM +SOD_LIB = $(SOD_LIB)\libsodium.lib +!endif + !ifndef NETBEANS NETBEANS = $(GUI) !endif @@ -491,7 +514,7 @@ CON_LIB = $(CON_LIB) /DELAYLOAD:comdlg32.dll /DELAYLOAD:ole32.dll DelayImp.lib CFLAGS = -c /W3 /GF /nologo $(CVARS) -I. -Iproto -DHAVE_PATHDEF -DWIN32 \ $(CSCOPE_DEFS) $(TERM_DEFS) $(SOUND_DEFS) $(NETBEANS_DEFS) $(CHANNEL_DEFS) \ - $(NBDEBUG_DEFS) $(XPM_DEFS) \ + $(NBDEBUG_DEFS) $(XPM_DEFS) $(SOD_DEFS) \ $(DEFINES) -DWINVER=$(WINVER) -D_WIN32_WINNT=$(WINVER) #>>>>> end of choices @@ -703,7 +726,7 @@ CFLAGS = $(CFLAGS) $(CFLAGS_DEPR) INCL = vim.h alloc.h ascii.h ex_cmds.h feature.h errors.h globals.h \ keymap.h macros.h option.h os_dos.h os_win32.h proto.h regexp.h \ - spell.h structs.h term.h beval.h $(NBDEBUG_INCL) + spell.h structs.h term.h beval.h $(NBDEBUG_INCL) $(SOD_INC) OBJ = \ $(OUTDIR)\arabic.obj \ @@ -1282,7 +1305,7 @@ conflags = $(conflags) /map /mapinfo:lines LINKARGS1 = $(linkdebug) $(conflags) LINKARGS2 = $(CON_LIB) $(GUI_LIB) $(NODEFAULTLIB) $(LIBC) $(OLE_LIB) user32.lib \ $(LUA_LIB) $(MZSCHEME_LIB) $(PERL_LIB) $(PYTHON_LIB) $(PYTHON3_LIB) $(RUBY_LIB) \ - $(TCL_LIB) $(SOUND_LIB) $(NETBEANS_LIB) $(XPM_LIB) $(LINK_PDB) + $(TCL_LIB) $(SOUND_LIB) $(NETBEANS_LIB) $(XPM_LIB) $(SOD_LIB) $(LINK_PDB) # Report link time code generation progress if used. !ifdef NODEBUG diff --git a/src/auto/configure b/src/auto/configure index 7ecc40ca51..cd678fd9f1 100755 --- a/src/auto/configure +++ b/src/auto/configure @@ -839,6 +839,7 @@ with_motif_lib with_tlib enable_largefile enable_canberra +enable_libsodium enable_acl enable_gpm enable_sysmouse @@ -1513,6 +1514,7 @@ Optional Features: --disable-desktop-database-update update disabled --disable-largefile omit support for large files --disable-canberra Do not use libcanberra. + --disable-libsodium Do not use libsodium. --disable-acl No check for ACL support. --disable-gpm Don't use gpm (Linux mouse daemon). --disable-sysmouse Don't use sysmouse (mouse in *BSD console). @@ -13005,6 +13007,70 @@ rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --enable-libsodium argument" >&5 +$as_echo_n "checking --enable-libsodium argument... " >&6; } +# Check whether --enable-libsodium was given. +if test "${enable_libsodium+set}" = set; then : + enableval=$enable_libsodium; +else + enable_libsodium="maybe" +fi + + +if test "$enable_libsodium" = "maybe"; then + if test "$features" = "big" -o "$features" = "huge"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: Defaulting to yes" >&5 +$as_echo "Defaulting to yes" >&6; } + enable_libsodium="yes" + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: Defaulting to no" >&5 +$as_echo "Defaulting to no" >&6; } + enable_libsodium="no" + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_libsodium" >&5 +$as_echo "$enable_libsodium" >&6; } +fi +if test "$enable_libsodium" = "yes"; then + if test "x$PKG_CONFIG" != "xno"; then + libsodium_lib=`$PKG_CONFIG --libs libsodium 2>/dev/null` + libsodium_cflags=`$PKG_CONFIG --cflags libsodium 2>/dev/null` + fi + if test "x$libsodium_lib" = "x"; then + libsodium_lib=-lsodium + libsodium_cflags= + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libcanberra" >&5 +$as_echo_n "checking for libcanberra... " >&6; } + ac_save_CFLAGS="$CFLAGS" + ac_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $libsodium_cflags" + LIBS="$LIBS $libsodium_lib" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + # include <sodium.h> + +int +main () +{ + + printf("%d", sodium_init()); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_SODIUM 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no; try installing libsodium-dev" >&5 +$as_echo "no; try installing libsodium-dev" >&6; }; CFLAGS="$ac_save_CFLAGS"; LIBS="$ac_save_LIBS" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for st_blksize" >&5 $as_echo_n "checking for st_blksize... " >&6; } diff --git a/src/blowfish.c b/src/blowfish.c index 342bcc406e..4502a1c5db 100644 --- a/src/blowfish.c +++ b/src/blowfish.c @@ -596,7 +596,8 @@ crypt_blowfish_encode( cryptstate_T *state, char_u *from, size_t len, - char_u *to) + char_u *to, + int last UNUSED) { bf_state_T *bfs = state->method_state; size_t i; @@ -619,7 +620,8 @@ crypt_blowfish_decode( cryptstate_T *state, char_u *from, size_t len, - char_u *to) + char_u *to, + int last UNUSED) { bf_state_T *bfs = state->method_state; size_t i; @@ -680,5 +682,4 @@ blowfish_self_test(void) } return OK; } - #endif // FEAT_CRYPT diff --git a/src/bufwrite.c b/src/bufwrite.c index c7c832cff7..c91bcd9958 100644 --- a/src/bufwrite.c +++ b/src/bufwrite.c @@ -30,6 +30,7 @@ struct bw_info int bw_flags; // FIO_ flags #ifdef FEAT_CRYPT buf_T *bw_buffer; // buffer being written + int bw_finish; // finish encrypting #endif char_u bw_rest[CONV_RESTLEN]; // not converted bytes int bw_restlen; // nr of bytes in bw_rest[] @@ -493,14 +494,14 @@ buf_write_bytes(struct bw_info *ip) if (crypt_works_inplace(ip->bw_buffer->b_cryptstate)) { # endif - crypt_encode_inplace(ip->bw_buffer->b_cryptstate, buf, len); + crypt_encode_inplace(ip->bw_buffer->b_cryptstate, buf, len, ip->bw_finish); # ifdef CRYPT_NOT_INPLACE } else { char_u *outbuf; - len = crypt_encode_alloc(curbuf->b_cryptstate, buf, len, &outbuf); + len = crypt_encode_alloc(curbuf->b_cryptstate, buf, len, &outbuf, ip->bw_finish); if (len == 0) return OK; // Crypt layer is buffering, will flush later. wlen = write_eintr(ip->bw_fd, outbuf, len); @@ -724,6 +725,7 @@ buf_write( #endif #ifdef FEAT_CRYPT write_info.bw_buffer = buf; + write_info.bw_finish = FALSE; #endif // After writing a file changedtick changes but we don't want to display @@ -2015,6 +2017,13 @@ restore_backup: ++s; if (++len != bufsize) continue; +#ifdef FEAT_CRYPT + if (write_info.bw_fd > 0 && lnum == end + && (write_info.bw_flags & FIO_ENCRYPTED) + && *buf->b_p_key != NUL && !filtering + && *ptr == NUL) + write_info.bw_finish = TRUE; + #endif if (buf_write_bytes(&write_info) == FAIL) { end = 0; // write error: break loop @@ -2118,6 +2127,12 @@ restore_backup: if (len > 0 && end > 0) { write_info.bw_len = len; +#ifdef FEAT_CRYPT + if (write_info.bw_fd > 0 && lnum >= end + && (write_info.bw_flags & FIO_ENCRYPTED) + && *buf->b_p_key != NUL && !filtering) + write_info.bw_finish = TRUE; + #endif if (buf_write_bytes(&write_info) == FAIL) end = 0; // write error nchars += len; diff --git a/src/config.h.in b/src/config.h.in index fbf4b2449c..0808cc3587 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -208,6 +208,7 @@ #undef HAVE_STRPTIME #undef HAVE_STRTOL #undef HAVE_CANBERRA +#undef HAVE_SODIUM #undef HAVE_ST_BLKSIZE #undef HAVE_SYSCONF #undef HAVE_SYSCTL diff --git a/src/configure.ac b/src/configure.ac index 9810ea1fc4..84b54dbf0a 100644 --- a/src/configure.ac +++ b/src/configure.ac @@ -3767,6 +3767,43 @@ if test "$enable_canberra" = "yes"; then AC_MSG_RESULT(no; try installing libcanberra-dev); CFLAGS="$ac_save_CFLAGS"; LIBS="$ac_save_LIBS") fi +AC_MSG_CHECKING(--enable-libsodium argument) +AC_ARG_ENABLE(libsodium, + [ --disable-libsodium Do not use libsodium.], + , [enable_libsodium="maybe"]) + +if test "$enable_libsodium" = "maybe"; then + if test "$features" = "big" -o "$features" = "huge"; then + AC_MSG_RESULT(Defaulting to yes) + enable_libsodium="yes" + else + AC_MSG_RESULT(Defaulting to no) + enable_libsodium="no" + fi +else + AC_MSG_RESULT($enable_libsodium) +fi +if test "$enable_libsodium" = "yes"; then + if test "x$PKG_CONFIG" != "xno"; then + libsodium_lib=`$PKG_CONFIG --libs libsodium 2>/dev/null` + libsodium_cflags=`$PKG_CONFIG --cflags libsodium 2>/dev/null` + fi + if test "x$libsodium_lib" = "x"; then + libsodium_lib=-lsodium + libsodium_cflags= + fi + AC_MSG_CHECKING(for libcanberra) + ac_save_CFLAGS="$CFLAGS" + ac_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $libsodium_cflags" + LIBS="$LIBS $libsodium_lib" + AC_TRY_LINK([ + # include <sodium.h> + ], [ + printf("%d", sodium_init()); ], + AC_MSG_RESULT(yes); AC_DEFINE(HAVE_SODIUM), + AC_MSG_RESULT(no; try installing libsodium-dev); CFLAGS="$ac_save_CFLAGS"; LIBS="$ac_save_LIBS") +fi dnl fstatfs() can take 2 to 4 arguments, try to use st_blksize if possible AC_MSG_CHECKING(for st_blksize) diff --git a/src/crypt.c b/src/crypt.c index 0164f1ce9d..35d4e14dc3 100644 --- a/src/crypt.c +++ b/src/crypt.c @@ -12,6 +12,10 @@ */ #include "vim.h" +#ifdef FEAT_SODIUM +# include <sodium.h> +#endif + #if defined(FEAT_CRYPT) || defined(PROTO) /* * Optional encryption support. @@ -33,7 +37,7 @@ 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 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 @@ -49,16 +53,16 @@ typedef struct { // 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); + char_u *to, int last); void (*decode_fn)(cryptstate_T *state, char_u *from, size_t len, - char_u *to); + 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); + char_u **newptr, int last); long (*decode_buffer_fn)(cryptstate_T *state, char_u *from, size_t len, - char_u **newptr); + char_u **newptr, int last); // Function pointers for in-place encoding and decoding, used for // crypt_*_inplace(). "from" and "to" arguments will be equal. @@ -68,9 +72,9 @@ typedef struct { // 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); + char_u *p2, int last); void (*decode_inplace_fn)(cryptstate_T *state, char_u *p1, size_t len, - char_u *p2); + char_u *p2, int last); } cryptmethod_T; // index is method_nr of cryptstate_T, CRYPT_M_* @@ -126,10 +130,41 @@ static cryptmethod_T cryptmethods[CRYPT_M_COUNT] = { 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, + crypt_sodium_encode, crypt_sodium_decode, + crypt_sodium_buffer_encode, crypt_sodium_buffer_decode, + crypt_sodium_encode, crypt_sodium_decode, + }, + // NOTE: when adding a new method, use some random bytes for the magic key, // to avoid that a text file is recognized as encrypted. }; +#ifdef FEAT_SODIUM +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; +#endif + #define CRYPT_MAGIC_LEN 12 // cannot change static char crypt_magic_head[] = "VimCrypt~"; @@ -260,7 +295,7 @@ crypt_create( state->method_nr = method_nr; if (cryptmethods[method_nr].init_fn( - state, key, salt, salt_len, seed, seed_len) == FAIL) + state, key, salt, salt_len, seed, seed_len) == FAIL) { vim_free(state); return NULL; @@ -365,9 +400,16 @@ crypt_create_for_writing( // 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. - sha2_seed(salt, salt_len, seed, seed_len); +#ifdef FEAT_SODIUM + if (sodium_init() >= 0) + { + randombytes_buf(salt, salt_len); + 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); @@ -380,7 +422,15 @@ crypt_create_for_writing( void crypt_free_state(cryptstate_T *state) { - vim_free(state->method_state); +#ifdef FEAT_SODIUM + if (state->method_nr == CRYPT_M_SOD) + { + sodium_memzero(state->method_state, sizeof(sodium_state_T)); + sodium_free(state->method_state); + } + else +#endif + vim_free(state->method_state); vim_free(state); } @@ -395,21 +445,22 @@ crypt_encode_alloc( cryptstate_T *state, char_u *from, size_t len, - char_u **newptr) + 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); + return method->encode_buffer_fn(state, from, len, newptr, last); if (len == 0) // Not buffering, just return EOF. return (long)len; - *newptr = alloc(len); + *newptr = alloc(len + 50); if (*newptr == NULL) return -1; - method->encode_fn(state, from, len, *newptr); + method->encode_fn(state, from, len, *newptr, last); return (long)len; } @@ -423,13 +474,14 @@ crypt_decode_alloc( cryptstate_T *state, char_u *ptr, long len, - char_u **newptr) + 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); + return method->decode_buffer_fn(state, ptr, len, newptr, last); if (len == 0) // Not buffering, just return EOF. @@ -438,7 +490,7 @@ crypt_decode_alloc( *newptr = alloc(len); if (*newptr == NULL) return -1; - method->decode_fn(state, ptr, len, *newptr); + method->decode_fn(state, ptr, len, *newptr, last); return len; } #endif @@ -451,9 +503,10 @@ crypt_encode( cryptstate_T *state, char_u *from, size_t len, - char_u *to) + char_u *to, + int last) { - cryptmethods[state->method_nr].encode_fn(state, from, len, to); + cryptmethods[state->method_nr].encode_fn(state, from, len, to, last); } #if 0 // unused @@ -465,9 +518,10 @@ crypt_decode( cryptstate_T *state, char_u *from, size_t len, - char_u *to) + char_u *to, + int last) { - cryptmethods[state->method_nr].decode_fn(state, from, len, to); + cryptmethods[state->method_nr].decode_fn(state, from, len, to, last); } #endif @@ -478,9 +532,11 @@ crypt_decode( crypt_encode_inplace( cryptstate_T *state, char_u *buf, - size_t len) + size_t len, + int last) { - cryptmethods[state->method_nr].encode_inplace_fn(state, buf, len, buf); + cryptmethods[state->method_nr].encode_inplace_fn(state, buf, len, + buf, last); } /* @@ -490,9 +546,11 @@ crypt_encode_inplace( crypt_decode_inplace( cryptstate_T *state, char_u *buf, - size_t len) + size_t len, + int last) { - cryptmethods[state->method_nr].decode_inplace_fn(state, buf, len, buf); + cryptmethods[state->method_nr].decode_inplace_fn(state, buf, len, + buf, last); } /* @@ -523,6 +581,19 @@ crypt_check_method(int method) msg_scroll = TRUE; msg(_("Warning: Using a weak encryption method; see :help 'cm'")); } + 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((char_u *)"swf", 0, NULL, OPT_LOCAL); +#ifdef FEAT_PERSISTENT_UNDO + set_option_value((char_u *)"udf", 0, NULL, OPT_LOCAL); +#endif + + msg_scroll = TRUE; + msg(_("Note: Encryption of swapfile not supported, disabling swap- and undofile")); + } } void @@ -610,4 +681,266 @@ crypt_append_msg( } } + 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; + + 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); + 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) + */ + 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 +} + +/* TODO: Unused + * Decrypt "from[len]" into "to[len]". + * "from" and "to" can be equal to encrypt in place. + */ + 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; + } + |