changeset 24970:7e9e53a0368f v8.2.3022

patch 8.2.3022: available encryption methods are not strong enough Commit: https://github.com/vim/vim/commit/f573c6e1ed58d46d694c802eaf5ae3662a952744 Author: Christian Brabandt <cb@256bit.org> Date: Sun Jun 20 14:02:16 2021 +0200 patch 8.2.3022: available encryption methods are not strong enough Problem: Available encryption methods are not strong enough. Solution: Add initial support for xchaha20. (Christian Brabandt, closes #8394)
author Bram Moolenaar <Bram@vim.org>
date Sun, 20 Jun 2021 14:15:07 +0200
parents 445ed84ae76a
children f06b8d3bda25
files .github/workflows/ci.yml runtime/doc/eval.txt runtime/doc/options.txt runtime/doc/various.txt src/INSTALLpc.txt src/Make_cyg_ming.mak src/Make_mvc.mak src/auto/configure src/blowfish.c src/bufwrite.c src/config.h.in src/configure.ac src/crypt.c src/crypt_zip.c src/errors.h src/evalfunc.c src/feature.h src/fileio.c src/memline.c src/option.c src/optionstr.c src/proto/blowfish.pro src/proto/crypt.pro src/proto/crypt_zip.pro src/structs.h src/testdir/samples/crypt_sodium_invalid.txt src/testdir/test_crypt.vim src/undo.c src/version.c
diffstat 29 files changed, 821 insertions(+), 65 deletions(-) [+]
line wrap: on
line diff
--- 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'
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -11971,6 +11971,7 @@ scrollbind		Compiled with 'scrollbind' s
 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.
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -2384,6 +2384,23 @@ A jump table for the options with a shor
 			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.
 
--- a/runtime/doc/various.txt
+++ b/runtime/doc/various.txt
@@ -444,6 +444,7 @@ m  *+ruby/dyn*		Ruby interface |ruby-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
--- a/src/INSTALLpc.txt
+++ b/src/INSTALLpc.txt
@@ -322,6 +322,9 @@ MSYS2 has its own git package, and you c
 
     $ 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
 
--- 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 @@ uninstall.exe: uninstall.c dosinst.h ver
 
 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 @@ ifeq ($(VIMDLL),yes)
 	$(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
--- 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
 
 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:lin
 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
--- 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_obj
     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; }
--- 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
--- 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;
--- 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
--- 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)
--- 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_
 	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;
+    }
+
+    if (sod_st->count && len <= crypto_secretstream_xchacha20poly1305_ABYTES)
+    {
+	emsg(e_libsodium_cannot_decrypt_buffer);
+	return;
+    }
+    if (crypto_secretstream_xchacha20poly1305_pull(&sod_st->state,
+			     buf_out, &buf_len, &tag, from, len, NULL, 0) != 0)
+    {
+	emsg(e_libsodium_decription_failed);
+	goto fail;
+    }
+    sod_st->count++;
+
+    if (tag == crypto_secretstream_xchacha20poly1305_TAG_FINAL && !last)
+    {
+	emsg(e_libsodium_decyption_failed_premature);
+	goto fail;
+    }
+    if (p1 == p2)
+	mch_memmove(p2, buf_out, buf_len);
+
+fail:
+    vim_free(buf_out);
+# endif
+}
+
+/*
+ * Encrypt "from[len]" into "to[len]".
+ * "from" and "to" can be equal to encrypt in place.
+ */
+    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 = 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.
+ */
+    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_decription_failed);
+	return -1;
+    }
+
+    if (tag == crypto_secretstream_xchacha20poly1305_TAG_FINAL && !last)
+	emsg(e_libsodium_decyption_failed_premature);
+    return (long) out_len;
+# else
+    return -1;
+# endif
+}
+
 #endif // FEAT_CRYPT
--- a/src/crypt_zip.c
+++ b/src/crypt_zip.c
@@ -114,7 +114,8 @@ crypt_zip_encode(
     cryptstate_T *state,
     char_u	*from,
     size_t	len,
-    char_u	*to)
+    char_u	*to,
+    int		last UNUSED)
 {
     zip_state_T *zs = state->method_state;
     size_t	i;
@@ -137,7 +138,8 @@ crypt_zip_decode(
     cryptstate_T *state,
     char_u	*from,
     size_t	len,
-    char_u	*to)
+    char_u	*to,
+    int		last UNUSED)
 {
     zip_state_T *zs = state->method_state;
     size_t	i;
--- a/src/errors.h
+++ b/src/errors.h
@@ -427,3 +427,22 @@ EXTERN char e_call_to_function_that_fail
 	INIT(= N_("E1191: Call to function that failed to compile: %s"));
 EXTERN char e_empty_function_name[]
 	INIT(= N_("E1192: Empty function name"));
+// libsodium
+EXTERN char e_libsodium_not_built_in[]
+	INIT(= N_("E1193: cryptmethod xchacha20 not built into this Vim"));
+EXTERN char e_libsodium_cannot_encrypt_header[]
+	INIT(= N_("E1194: Cannot encrypt header, not enough space"));
+EXTERN char e_libsodium_cannot_encrypt_buffer[]
+	INIT(= N_("E1195: Cannot encrypt buffer, not enough space"));
+EXTERN char e_libsodium_cannot_decrypt_header[]
+	INIT(= N_("E1196: Cannot decrypt header, not enough space"));
+EXTERN char e_libsodium_cannot_allocate_buffer[]
+	INIT(= N_("E1197: Cannot allocate_buffer for encryption"));
+EXTERN char e_libsodium_decryption_failed_header_incomplete[]
+	INIT(= N_("E1198: Decryption failed: Header incomplete!"));
+EXTERN char e_libsodium_cannot_decrypt_buffer[]
+	INIT(= N_("E1199: Cannot decrypt buffer, not enough space"));
+EXTERN char e_libsodium_decription_failed[]
+	INIT(= N_("E1200: Decryption failed: corrupted chunk!"));
+EXTERN char e_libsodium_decyption_failed_premature[]
+	INIT(= N_("E1201: Decryption failed: pre-mature end of file!"));
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -5072,6 +5072,13 @@ f_has(typval_T *argvars, typval_T *rettv
 		0
 #endif
 		},
+	{"sodium",
+#ifdef FEAT_SODIUM
+		1
+#else
+		0
+#endif
+		},
 	{"sound",
 #ifdef FEAT_SOUND
 		1
--- a/src/feature.h
+++ b/src/feature.h
@@ -593,6 +593,13 @@
 # define FEAT_SOUND_CANBERRA
 #endif
 
+/*
+ * libsodium - add cryptography support
+ */
+#if defined(HAVE_SODIUM) && defined(FEAT_BIG)
+# define FEAT_SODIUM
+#endif
+
 // There are two ways to use XPM.
 #if (defined(HAVE_XM_XPMP_H) && defined(FEAT_GUI_MOTIF)) \
 		|| defined(HAVE_X11_XPM_H)
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -13,6 +13,10 @@
 
 #include "vim.h"
 
+#ifdef FEAT_SODIUM
+# include <sodium.h>
+#endif
+
 #if defined(__TANDEM)
 # include <limits.h>		// for SSIZE_MAX
 #endif
@@ -148,6 +152,8 @@ readfile(
     char_u	*p;
     off_T	filesize = 0;
     int		skip_read = FALSE;
+    off_T       filesize_disk = 0;      // file size read from disk
+    off_T       filesize_count = 0;     // counter
 #ifdef FEAT_CRYPT
     char_u	*cryptkey = NULL;
     int		did_ask_for_key = FALSE;
@@ -215,6 +221,7 @@ readfile(
     int		using_b_ffname;
     int		using_b_fname;
     static char *msg_is_a_directory = N_("is a directory");
+    int         eof;
 
     au_did_filetype = FALSE; // reset before triggering any autocommands
 
@@ -405,6 +412,7 @@ readfile(
 	{
 	    buf_store_time(curbuf, &st, fname);
 	    curbuf->b_mtime_read = curbuf->b_mtime;
+	    filesize_disk = st.st_size;
 #ifdef UNIX
 	    /*
 	     * Use the protection bits of the original file for the swap file.
@@ -1080,6 +1088,7 @@ retry:
     {
 	linerest = 0;
 	filesize = 0;
+	filesize_count = 0;
 	skip_count = lines_to_skip;
 	read_count = lines_to_read;
 	conv_restlen = 0;
@@ -1263,7 +1272,23 @@ retry:
 		    /*
 		     * Read bytes from the file.
 		     */
+# ifdef FEAT_SODIUM
+		    // Let the crypt layer work with a buffer size of 8192
+		    if (filesize == 0)
+			// set size to 8K + Sodium Crypt Metadata
+			size = WRITEBUFSIZE + 36
+		     + crypto_secretstream_xchacha20poly1305_HEADERBYTES
+		     + crypto_secretstream_xchacha20poly1305_ABYTES;
+
+		    else if (filesize > 0 && (curbuf->b_cryptstate != NULL &&
+			 curbuf->b_cryptstate->method_nr == CRYPT_M_SOD))
+			size = WRITEBUFSIZE + crypto_secretstream_xchacha20poly1305_ABYTES;
+# endif
+		    eof = size;
 		    size = read_eintr(fd, ptr, size);
+		    filesize_count += size;
+		    // hit end of file
+		    eof = (size < eof || filesize_count == filesize_disk);
 		}
 
 #ifdef FEAT_CRYPT
@@ -1285,7 +1310,8 @@ retry:
 		    if (crypt_works_inplace(curbuf->b_cryptstate))
 		    {
 # endif
-			crypt_decode_inplace(curbuf->b_cryptstate, ptr, size);
+			crypt_decode_inplace(curbuf->b_cryptstate, ptr,
+								    size, eof);
 # ifdef CRYPT_NOT_INPLACE
 		    }
 		    else
@@ -1294,8 +1320,16 @@ retry:
 			int	decrypted_size;
 
 			decrypted_size = crypt_decode_alloc(
-				    curbuf->b_cryptstate, ptr, size, &newptr);
-
+				    curbuf->b_cryptstate, ptr, size,
+								 &newptr, eof);
+
+			if (decrypted_size < 0)
+			{
+			    // error message already given
+			    error = TRUE;
+			    vim_free(newptr);
+			    break;
+			}
 			// If the crypt layer is buffering, not producing
 			// anything yet, need to read more.
 			if (decrypted_size == 0)
@@ -1325,6 +1359,7 @@ retry:
 			    if (newptr != NULL)
 				mch_memmove(new_buffer + linerest, newptr,
 							      decrypted_size);
+			    vim_free(newptr);
 			}
 
 			if (new_buffer != NULL)
@@ -1334,6 +1369,7 @@ retry:
 			    new_buffer = NULL;
 			    line_start = buffer;
 			    ptr = buffer + linerest;
+			    real_size = size;
 			}
 			size = decrypted_size;
 		    }
--- a/src/memline.c
+++ b/src/memline.c
@@ -48,6 +48,11 @@
 # include <time.h>
 #endif
 
+// for randombytes_buf
+#ifdef FEAT_SODIUM
+# include <sodium.h>
+#endif
+
 #if defined(SASC) || defined(__amigaos4__)
 # include <proto/dos.h>	    // for Open() and Close()
 #endif
@@ -64,12 +69,14 @@ typedef struct pointer_entry	PTR_EN;	   
 #define BLOCK0_ID1_C0  'c'		    // block 0 id 1 'cm' 0
 #define BLOCK0_ID1_C1  'C'		    // block 0 id 1 'cm' 1
 #define BLOCK0_ID1_C2  'd'		    // block 0 id 1 'cm' 2
+#define BLOCK0_ID1_C3  'S'		    // block 0 id 1 'cm' 3 - but not actually used
 
 #if defined(FEAT_CRYPT)
 static int id1_codes[] = {
     BLOCK0_ID1_C0,  // CRYPT_M_ZIP
     BLOCK0_ID1_C1,  // CRYPT_M_BF
     BLOCK0_ID1_C2,  // CRYPT_M_BF2
+    BLOCK0_ID1_C3,  // CRYPT_M_SOD  - Unused!
 };
 #endif
 
@@ -426,11 +433,15 @@ ml_set_mfp_crypt(buf_T *buf)
     {
 	int method_nr = crypt_get_method_nr(buf);
 
-	if (method_nr > CRYPT_M_ZIP)
+	if (method_nr > CRYPT_M_ZIP && method_nr < CRYPT_M_SOD)
 	{
 	    // Generate a seed and store it in the memfile.
 	    sha2_seed(buf->b_ml.ml_mfp->mf_seed, MF_SEED_LEN, NULL, 0);
 	}
+#ifdef FEAT_SODIUM
+	else if (method_nr == CRYPT_M_SOD)
+	    randombytes_buf(buf->b_ml.ml_mfp->mf_seed, MF_SEED_LEN);
+ #endif
     }
 }
 
@@ -447,7 +458,7 @@ ml_set_b0_crypt(buf_T *buf, ZERO_BL *b0p
 	int method_nr = crypt_get_method_nr(buf);
 
 	b0p->b0_id[1] = id1_codes[method_nr];
-	if (method_nr > CRYPT_M_ZIP)
+	if (method_nr > CRYPT_M_ZIP && method_nr < CRYPT_M_SOD)
 	{
 	    // Generate a seed and store it in block 0 and in the memfile.
 	    sha2_seed(&b0p->b0_seed, MF_SEED_LEN, NULL, 0);
@@ -482,10 +493,17 @@ ml_set_crypt_key(
     int		top;
     int		old_method;
 
-    if (mfp == NULL)
+    if (mfp == NULL || mfp->mf_fd < 0)
 	return;  // no memfile yet, nothing to do
     old_method = crypt_method_nr_from_name(old_cm);
 
+    if (old_method == CRYPT_M_SOD || crypt_get_method_nr(buf) == CRYPT_M_SOD)
+    {
+	// close the swapfile
+	mf_close_file(buf, TRUE);
+	buf->b_p_swf = FALSE;
+	return;
+    }
     // First make sure the swapfile is in a consistent state, using the old
     // key and method.
     {
@@ -911,7 +929,8 @@ ml_check_b0_id(ZERO_BL *b0p)
 	    || (b0p->b0_id[1] != BLOCK0_ID1
 		&& b0p->b0_id[1] != BLOCK0_ID1_C0
 		&& b0p->b0_id[1] != BLOCK0_ID1_C1
-		&& b0p->b0_id[1] != BLOCK0_ID1_C2)
+		&& b0p->b0_id[1] != BLOCK0_ID1_C2
+		&& b0p->b0_id[1] != BLOCK0_ID1_C3)
 	    )
 	return FAIL;
     return OK;
@@ -2402,7 +2421,9 @@ ml_sync_all(int check_file, int check_ch
 
     FOR_ALL_BUFFERS(buf)
     {
-	if (buf->b_ml.ml_mfp == NULL || buf->b_ml.ml_mfp->mf_fname == NULL)
+	if (buf->b_ml.ml_mfp == NULL
+		|| buf->b_ml.ml_mfp->mf_fname == NULL
+		|| buf->b_ml.ml_mfp->mf_fd < 0)
 	    continue;			    // no file
 
 	ml_flush_line(buf);		    // flush buffered line
@@ -5320,7 +5341,8 @@ ml_encrypt_data(
     mch_memmove(new_data, dp, head_end - (char_u *)dp);
 
     // Encrypt the text.
-    crypt_encode(state, text_start, text_len, new_data + dp->db_txt_start);
+    crypt_encode(state, text_start, text_len, new_data + dp->db_txt_start,
+									FALSE);
     crypt_free_state(state);
 
     // Clear the gap.
@@ -5360,7 +5382,7 @@ ml_decrypt_data(
 	if (state != NULL)
 	{
 	    // Decrypt the text in place.
-	    crypt_decode_inplace(state, text_start, text_len);
+	    crypt_decode_inplace(state, text_start, text_len, FALSE);
 	    crypt_free_state(state);
 	}
     }
@@ -5407,7 +5429,7 @@ ml_crypt_prepare(memfile_T *mfp, off_T o
     // of the block for the salt.
     vim_snprintf((char *)salt, sizeof(salt), "%ld", (long)offset);
     return crypt_create(method_nr, key, salt, (int)STRLEN(salt),
-							   seed, MF_SEED_LEN);
+							seed, MF_SEED_LEN);
 }
 
 #endif
--- a/src/option.c
+++ b/src/option.c
@@ -2713,6 +2713,10 @@ set_bool_option(
 				|| (opt_flags & OPT_GLOBAL) || opt_flags == 0)
 			&& !curbufIsChanged() && curbuf->b_ml.ml_mfp != NULL)
 		{
+#ifdef FEAT_CRYPT
+		    if (crypt_get_method_nr(curbuf) == CRYPT_M_SOD)
+			continue;
+#endif
 		    u_compute_hash(hash);
 		    u_read_undo(NULL, hash, curbuf->b_fname);
 		}
--- a/src/optionstr.c
+++ b/src/optionstr.c
@@ -24,7 +24,11 @@ static char *(p_bo_values[]) = {"all", "
 static char *(p_nf_values[]) = {"bin", "octal", "hex", "alpha", "unsigned", NULL};
 static char *(p_ff_values[]) = {FF_UNIX, FF_DOS, FF_MAC, NULL};
 #ifdef FEAT_CRYPT
-static char *(p_cm_values[]) = {"zip", "blowfish", "blowfish2", NULL};
+static char *(p_cm_values[]) = {"zip", "blowfish", "blowfish2",
+ # ifdef FEAT_SODIUM
+    "xchacha20",
+ # endif
+    NULL};
 #endif
 static char *(p_cmp_values[]) = {"internal", "keepascii", NULL};
 static char *(p_dy_values[]) = {"lastline", "truncate", "uhex", NULL};
--- a/src/proto/blowfish.pro
+++ b/src/proto/blowfish.pro
@@ -1,6 +1,6 @@
 /* blowfish.c */
-void crypt_blowfish_encode(cryptstate_T *state, char_u *from, size_t len, char_u *to);
-void crypt_blowfish_decode(cryptstate_T *state, char_u *from, size_t len, char_u *to);
+void crypt_blowfish_encode(cryptstate_T *state, char_u *from, size_t len, char_u *to, int last);
+void crypt_blowfish_decode(cryptstate_T *state, char_u *from, size_t len, char_u *to, int last);
 int crypt_blowfish_init(cryptstate_T *state, char_u *key, char_u *salt, int salt_len, char_u *seed, int seed_len);
 int blowfish_self_test(void);
 /* vim: set ft=c : */
--- a/src/proto/crypt.pro
+++ b/src/proto/crypt.pro
@@ -1,6 +1,7 @@
 /* crypt.c */
 int crypt_method_nr_from_name(char_u *name);
 int crypt_method_nr_from_magic(char *ptr, int len);
+int crypt_works_inplace(cryptstate_T *state);
 int crypt_get_method_nr(buf_T *buf);
 int crypt_whole_undofile(int method_nr);
 int crypt_get_header_len(int method_nr);
@@ -11,12 +12,19 @@ cryptstate_T *crypt_create_from_header(i
 cryptstate_T *crypt_create_from_file(FILE *fp, char_u *key);
 cryptstate_T *crypt_create_for_writing(int method_nr, char_u *key, char_u **header, int *header_len);
 void crypt_free_state(cryptstate_T *state);
-void crypt_encode(cryptstate_T *state, char_u *from, size_t len, char_u *to);
-void crypt_encode_inplace(cryptstate_T *state, char_u *buf, size_t len);
-void crypt_decode_inplace(cryptstate_T *state, char_u *buf, size_t len);
+long crypt_encode_alloc(cryptstate_T *state, char_u *from, size_t len, char_u **newptr, int last);
+long crypt_decode_alloc(cryptstate_T *state, char_u *ptr, long len, char_u **newptr, int last);
+void crypt_encode(cryptstate_T *state, char_u *from, size_t len, char_u *to, int last);
+void crypt_encode_inplace(cryptstate_T *state, char_u *buf, size_t len, int last);
+void crypt_decode_inplace(cryptstate_T *state, char_u *buf, size_t len, int last);
 void crypt_free_key(char_u *key);
 void crypt_check_method(int method);
 void crypt_check_current_method(void);
 char_u *crypt_get_key(int store, int twice);
 void crypt_append_msg(buf_T *buf);
+int crypt_sodium_init(cryptstate_T *state, char_u *key, char_u *salt, int salt_len, char_u *seed, int seed_len);
+void crypt_sodium_encode(cryptstate_T *state, char_u *from, size_t len, char_u *to, int last);
+void crypt_sodium_decode(cryptstate_T *state, char_u *from, size_t len, char_u *to, int last);
+long crypt_sodium_buffer_encode(cryptstate_T *state, char_u *from, size_t len, char_u **buf_out, int last);
+long crypt_sodium_buffer_decode(cryptstate_T *state, char_u *from, size_t len, char_u **buf_out, int last);
 /* vim: set ft=c : */
--- a/src/proto/crypt_zip.pro
+++ b/src/proto/crypt_zip.pro
@@ -1,5 +1,5 @@
 /* crypt_zip.c */
 int crypt_zip_init(cryptstate_T *state, char_u *key, char_u *salt, int salt_len, char_u *seed, int seed_len);
-void crypt_zip_encode(cryptstate_T *state, char_u *from, size_t len, char_u *to);
-void crypt_zip_decode(cryptstate_T *state, char_u *from, size_t len, char_u *to);
+void crypt_zip_encode(cryptstate_T *state, char_u *from, size_t len, char_u *to, int last);
+void crypt_zip_decode(cryptstate_T *state, char_u *from, size_t len, char_u *to, int last);
 /* vim: set ft=c : */
--- a/src/structs.h
+++ b/src/structs.h
@@ -2513,11 +2513,12 @@ typedef struct {
 # define CRYPT_M_ZIP	0
 # define CRYPT_M_BF	1
 # define CRYPT_M_BF2	2
-# define CRYPT_M_COUNT	3 // number of crypt methods
+# define CRYPT_M_SOD    3
+# define CRYPT_M_COUNT	4 // number of crypt methods
 
 // Currently all crypt methods work inplace.  If one is added that isn't then
 // define this.
-//  # define CRYPT_NOT_INPLACE 1
+# define CRYPT_NOT_INPLACE 1
 #endif
 
 #ifdef FEAT_PROP_POPUP
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..35e31b5ac9ef929af76c482a7e131fce7611ea73
GIT binary patch
literal 16504
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
--- a/src/testdir/test_crypt.vim
+++ b/src/testdir/test_crypt.vim
@@ -22,6 +22,11 @@ func Test_head_only_3()
   call Common_head_only('VimCrypt~03!abc')
 endfunc
 
+func Test_head_only_4()
+  CheckFeature sodium
+  call Common_head_only('VimCrypt~04!abc')
+endfunc
+
 func Crypt_uncrypt(method)
   exe "set cryptmethod=" . a:method
   " If the blowfish test fails 'cryptmethod' will be 'zip' now.
@@ -55,6 +60,11 @@ func Test_crypt_blowfish2()
   call Crypt_uncrypt('blowfish2')
 endfunc
 
+func Test_crypt_sodium()
+  CheckFeature sodium
+  call Crypt_uncrypt('xchacha20')
+endfunc
+
 func Uncrypt_stable(method, crypted_text, key, uncrypted_text)
   split Xtest.txt
   set bin noeol key= fenc=latin1
@@ -70,6 +80,16 @@ func Uncrypt_stable(method, crypted_text
   set key=
 endfunc
 
+func Uncrypt_stable_xxd(method, hex, key, uncrypted_text)
+  " use xxd to write the binary content
+  call system('xxd -r >Xtest.txt', a:hex)
+  call feedkeys(":split Xtest.txt\<CR>" . a:key . "\<CR>", 'xt')
+  call assert_equal(a:uncrypted_text, getline(1, len(a:uncrypted_text)))
+  bwipe!
+  call delete('Xtest.txt')
+  set key=
+endfunc
+
 func Test_uncrypt_zip()
   call Uncrypt_stable('zip', "VimCrypt~01!\u0006\u001clV'\u00de}Mg\u00a0\u00ea\u00a3V\u00a9\u00e7\u0007E#3\u008e2U\u00e9\u0097", "foofoo", ["1234567890", "aábbccddeëff"])
 endfunc
@@ -78,8 +98,113 @@ func Test_uncrypt_blowfish()
   call Uncrypt_stable('blowfish', "VimCrypt~02!k)\u00be\u0017\u0097#\u0016\u00ddS\u009c\u00f5=\u00ba\u00e0\u00c8#\u00a5M\u00b4\u0086J\u00c3A\u00cd\u00a5M\u00b4\u0086!\u0080\u0015\u009b\u00f5\u000f\u00e1\u00d2\u0019\u0082\u0016\u0098\u00f7\u000d\u00da", "barbar", ["asdfasdfasdf", "0001112223333"])
 endfunc
 
+func Test_uncrypt_blowfish2a()
+  call Uncrypt_stable('blowfish', "VimCrypt~03!\u001e\u00d1N\u00e3;\u00d3\u00c0\u00a0^C)\u0004\u00f7\u007f.\u00b6\u00abF\u000eS\u0019\u00e0\u008b6\u00d2[T\u00cb\u00a7\u0085\u00d8\u00be9\u000b\u00812\u000bQ\u00b3\u00cc@\u0097\u000f\u00df\u009a\u00adIv\u00aa.\u00d8\u00c9\u00ee\u009e`\u00bd$\u00af%\u00d0", "barburp", ["abcdefghijklmnopqrstuvwxyz", "!@#$%^&*()_+=-`~"])
+endfunc
+
 func Test_uncrypt_blowfish2()
-  call Uncrypt_stable('blowfish', "VimCrypt~03!\u001e\u00d1N\u00e3;\u00d3\u00c0\u00a0^C)\u0004\u00f7\u007f.\u00b6\u00abF\u000eS\u0019\u00e0\u008b6\u00d2[T\u00cb\u00a7\u0085\u00d8\u00be9\u000b\u00812\u000bQ\u00b3\u00cc@\u0097\u000f\u00df\u009a\u00adIv\u00aa.\u00d8\u00c9\u00ee\u009e`\u00bd$\u00af%\u00d0", "barburp", ["abcdefghijklmnopqrstuvwxyz", "!@#$%^&*()_+=-`~"])
+  call Uncrypt_stable('blowfish2', "VimCrypt~03!\u001e\u00d1N\u00e3;\u00d3\u00c0\u00a0^C)\u0004\u00f7\u007f.\u00b6\u00abF\u000eS\u0019\u00e0\u008b6\u00d2[T\u00cb\u00a7\u0085\u00d8\u00be9\u000b\u00812\u000bQ\u00b3\u00cc@\u0097\u000f\u00df\u009a\u00adIv\u00aa.\u00d8\u00c9\u00ee\u009e`\u00bd$\u00af%\u00d0", "barburp", ["abcdefghijklmnopqrstuvwxyz", "!@#$%^&*()_+=-`~"])
+endfunc
+
+func Test_uncrypt_xchacha20()
+  CheckFeature sodium
+  let hex=['00000000: 5669 6d43 7279 7074 7e30 3421 6b7d e607  vimCrypt~04!k}..',
+        \  '00000010: 4ea4 e99f 923e f67f 7b59 a80d 3bca 2f06  N....>..{Y..;./.',
+        \  '00000020: fa11 b951 8d09 0dc9 470f e7cf 8b90 4310  ...Q....G.....C.',
+        \  '00000030: 653b b83b e493 378b 0390 0e38 f912 626b  e;.;..7....8..bk',
+        \  '00000040: a02e 4697 0254 2625 2d8e 3a0b 784b e89c  ..F..T&%-.:.xK..',
+        \  '00000050: 0c67 a975 3c17 9319 8ffd 1463 7783 a1f3  .g.u<......cw...',
+        \  '00000060: d917 dcb3 8b3e ecd7 c7d4 086b 6059 7ead  .....>.....k`Y~.',
+        \  '00000070: 9b07 f96b 5c1b 4d08 cd91 f208 5221 7484  ...k\.M.....R!t.',
+        \  '00000080: 72be 0136 84a1 d3                        r..6...']
+  " the file should be in latin1 encoding, this makes sure that readfile()
+  " retries several times converting the multi-byte characters
+  call Uncrypt_stable_xxd('xchacha20', hex, "sodium_crypt", ["abcdefghijklmnopqrstuvwxyzäöü", "ZZZ_äüöÄÜÖ_!@#$%^&*()_+=-`~"])
+endfunc
+
+func Test_uncrypt_xchacha20_invalid()
+  CheckFeature sodium
+  " load an invalid encrypted file and verify it can be decrypted with an
+  " error message
+  try
+    call feedkeys(":split samples/crypt_sodium_invalid.txt\<CR>sodium\<CR>", 'xt')
+    call assert_false(1, 'should not happen')
+  catch
+    call assert_exception('pre-mature')
+  endtry
+  call assert_match("Note: Encryption of swapfile not supported, disabling swap- and undofile", execute(':5messages'))
+
+  call assert_equal(0, &swapfile)
+  call assert_equal("xchacha20", &cryptmethod)
+  call assert_equal('311111111111111111111111', getline('$'))
+  bw!
+endfunc
+
+func Test_uncrypt_xchacha20_2()
+  CheckFeature sodium
+  sp Xcrypt_sodium.txt
+  " Create a larger file, so that Vim will write in several blocks
+  call setline(1, range(1,4000))
+  call assert_equal(1, &swapfile)
+  set cryptmethod=xchacha20
+  call feedkeys(":X\<CR>sodium\<CR>sodium\<CR>", 'xt')
+  " swapfile disabled
+  call assert_equal(0, &swapfile)
+  call assert_match("Note: Encryption of swapfile not supported, disabling swap- and undofile", execute(':messages'))
+  w!
+  " encrypted using xchacha20
+  call assert_match("\[xchacha20\]", execute(':messages'))
+  bw!
+  call feedkeys(":sp Xcrypt_sodium.txt\<CR>sodium\<CR>", 'xt')
+  " successfully decrypted
+  call assert_equal(range(1, 4000)->map( {_, v -> string(v)}), getline(1,'$'))
+  set key=
+  w!
+  " enryption removed
+  call assert_match('"Xcrypt_sodium.txt" 4000L, 18893B written', execute(':message'))
+  bw!
+  call delete('Xcrypt_sodium.txt')
+  set cryptmethod&vim
+endfunc
+
+func Test_uncrypt_xchacha20_3_persistent_undo()
+  CheckFeature sodium
+  CheckFeature persistent_undo
+  sp Xcrypt_sodium_undo.txt
+  set cryptmethod=xchacha20 undofile
+  call feedkeys(":X\<CR>sodium\<CR>sodium\<CR>", 'xt')
+  call assert_equal(0, &undofile)
+  let ufile=undofile(@%)
+  call append(0, ['monday', 'tuesday', 'wednesday', 'thursday', 'friday'])
+  call cursor(1, 1)
+
+  set undolevels=100
+  normal dd
+  set undolevels=100
+  normal dd
+  set undolevels=100
+  normal dd
+  set undolevels=100
+  w!
+  bw!
+  call feedkeys(":sp Xcrypt_sodium_undo.txt\<CR>sodium\<CR>", 'xt')
+  " should fail
+  norm! u
+  call assert_match('Already at oldest change', execute(':1mess'))
+  call assert_fails('verbose rundo' .. fnameescape(ufile), 'E822')
+  bw!
+  set undolevels& cryptmethod& undofile&
+  call delete('Xcrypt_sodium_undo.txt')
+endfunc
+
+func Test_encrypt_xchacha20_missing()
+  if has("sodium")
+    return
+  endif
+  sp Xcrypt_sodium_undo.txt
+  call assert_fails(':set cryptmethod=xchacha20', 'E474')
+  bw!
+  set cm&
 endfunc
 
 func Test_uncrypt_unknown_method()
--- a/src/undo.c
+++ b/src/undo.c
@@ -963,7 +963,9 @@ undo_flush(bufinfo_T *bi)
 {
     if (bi->bi_buffer != NULL && bi->bi_state != NULL && bi->bi_used > 0)
     {
-	crypt_encode_inplace(bi->bi_state, bi->bi_buffer, bi->bi_used);
+	// Last parameter is only used for sodium encryption and that
+	// explicitly disables encryption of undofiles.
+	crypt_encode_inplace(bi->bi_state, bi->bi_buffer, bi->bi_used, FALSE);
 	if (fwrite(bi->bi_buffer, bi->bi_used, (size_t)1, bi->bi_fp) != 1)
 	    return FAIL;
 	bi->bi_used = 0;
@@ -995,7 +997,9 @@ fwrite_crypt(bufinfo_T *bi, char_u *ptr,
 	    if (copy == NULL)
 		return 0;
 	}
-	crypt_encode(bi->bi_state, ptr, len, copy);
+	// Last parameter is only used for sodium encryption and that
+	// explicitly disables encryption of undofiles.
+	crypt_encode(bi->bi_state, ptr, len, copy, TRUE);
 	i = fwrite(copy, len, (size_t)1, bi->bi_fp);
 	if (copy != small_buf)
 	    vim_free(copy);
@@ -1129,7 +1133,7 @@ undo_read(bufinfo_T *bi, char_u *buffer,
 		}
 		bi->bi_avail = n;
 		bi->bi_used = 0;
-		crypt_decode_inplace(bi->bi_state, bi->bi_buffer, bi->bi_avail);
+		crypt_decode_inplace(bi->bi_state, bi->bi_buffer, bi->bi_avail, FALSE);
 	    }
 	    n = size_todo;
 	    if (n > bi->bi_avail - bi->bi_used)
@@ -1176,7 +1180,7 @@ read_string_decrypt(bufinfo_T *bi, int l
 	ptr[len] = NUL;
 #ifdef FEAT_CRYPT
 	if (bi->bi_state != NULL && bi->bi_buffer == NULL)
-	    crypt_decode_inplace(bi->bi_state, ptr, len);
+	    crypt_decode_inplace(bi->bi_state, ptr, len, FALSE);
 #endif
     }
     return ptr;
--- a/src/version.c
+++ b/src/version.c
@@ -553,6 +553,11 @@ static char *(features[]) =
 #else
 	"-smartindent",
 #endif
+#ifdef FEAT_SODIUM
+	"+sodium",
+#else
+	"-sodium",
+#endif
 #ifdef FEAT_SOUND
 	"+sound",
 #else
@@ -751,6 +756,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    3022,
+/**/
     3021,
 /**/
     3020,