]> granicus.if.org Git - vim/commitdiff
patch 8.2.3022: available encryption methods are not strong enough v8.2.3022
authorChristian Brabandt <cb@256bit.org>
Sun, 20 Jun 2021 12:02:16 +0000 (14:02 +0200)
committerBram Moolenaar <Bram@vim.org>
Sun, 20 Jun 2021 12:02:16 +0000 (14:02 +0200)
Problem:    Available encryption methods are not strong enough.
Solution:   Add initial support for xchaha20. (Christian Brabandt,
            closes #8394)

29 files changed:
.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 [new file with mode: 0644]
src/testdir/test_crypt.vim
src/undo.c
src/version.c

index 9127455bc1120fa317f467265cb27ef3248daee5..e569b42397f3e15c5f69b7a2313808a8be096678 100644 (file)
@@ -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'
index dd2d24232a94514b9e71739163ff04ee0923cc1d..4620623c631839c27b26c3fb97b7b29515ecee20 100644 (file)
@@ -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.
index e600dd91dd016d8324df26e7dd25c3eccdad37e6..19ddd9cb5f8bc85e13e4dad0a7ef38abbd1fecf3 100644 (file)
@@ -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.
 
index eb45922ccf75e13651e29080244fa1b8e71f1878..0ad12e80424e453436f94b446ef7fac3734c2487 100644 (file)
@@ -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
index b5c1a0b58c9c1c78ee88cc4fc772ee4a1e7653cc..d5516c891a2e8f10aa2f7689d1c1b0849e48c940 100644 (file)
@@ -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
 
index c33200b33e8343d05851701d7a9cf05ea5d70ae8..e93165dbf4e139e3dea2618810ee898c9c0baa7d 100644 (file)
@@ -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
index 42b6f8ddf6fa13f20c53bea15d58523261f04b2b..c61bb27ab7232285c633e3bd305db118b8849a25 100644 (file)
@@ -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
index 7ecc40ca5120a11c9206ed0e5847d323c59a68cc..cd678fd9f156203bd8f40e7e2b82187f7bd97197 100755 (executable)
@@ -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; }
index 342bcc406ea1041f97b3e8ab360a486e7f81cc3d..4502a1c5db8dc8bcdb522775dcf9b47346fa72c2 100644 (file)
@@ -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
index c7c832cff7006a7eba00d09ae9a4e74eea7a78f6..c91bcd9958f513861d957310336164f02aa14b0d 100644 (file)
@@ -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;
index fbf4b2449c3a056a64ba45fb6b1b8ccd67ef55a2..0808cc3587a19af04a7e4d652d37cb20d3861b40 100644 (file)
 #undef HAVE_STRPTIME
 #undef HAVE_STRTOL
 #undef HAVE_CANBERRA
+#undef HAVE_SODIUM
 #undef HAVE_ST_BLKSIZE
 #undef HAVE_SYSCONF
 #undef HAVE_SYSCTL
index 9810ea1fc4655a49ad28eed2d76ce3d2831baad8..84b54dbf0a4ce73be615876531491686fbb85ae7 100644 (file)
@@ -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)
index 0164f1ce9d07716536b2c4b65b674686839d41a9..35d4e14dc3b7767db2186dd23c3363a28b9dbc5c 100644 (file)
  */
 #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;
+    }
+
+    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
index 42abe9350b7bedfb2db8027eb126657308b2e7b5..b11d7a329fd5a0add7c09ecebd5f0177e3e17de1 100644 (file)
@@ -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;
index 63e2659f73337476597f9389ab3408cae61a5d88..cb93efd9079faf69d1268999a1a3576660f400f1 100644 (file)
@@ -427,3 +427,22 @@ EXTERN char e_call_to_function_that_failed_to_compile_str[]
        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!"));
index 97f7b61070388c1efc1e437dd52fe35afd27e479..9267117daa70f80fd4d0ef300964b445ddb8d9a7 100644 (file)
@@ -5070,6 +5070,13 @@ f_has(typval_T *argvars, typval_T *rettv)
                1
 #else
                0
+#endif
+               },
+       {"sodium",
+#ifdef FEAT_SODIUM
+               1
+#else
+               0
 #endif
                },
        {"sound",
index 4df73d1cb4f7ae9a3dd721754996547cd626044b..2ae739aa82a6276c545348d3bce54e3191d6f2f3 100644 (file)
 # 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)
index 91c02bfcd047cceaccabc881252736b0cc4c7b0f..e05dfa3a5ddc88936934adcf3a43beec3b925013 100644 (file)
 
 #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;
                    }
index 58582c9d69db079dab39c86f9c5054ac98e6a6be..3b09a7dd4975a0f05ed97252daa0f919b04e0a70 100644 (file)
 # 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;     // block/line-count pair
 #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_char)
 
     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 offset, int reading)
     // 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
index 535213c19df50de4f8561d5b2c295b7a8bb1a033..07bb71e4d6b46f455e1d8a333d92c491d605695a 100644 (file)
@@ -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);
                }
index f332294ed83714605dcae23bc8fe4d6552f7d4b7..47aee35be340f750f2c70ad21733e6abcb7f4d9b 100644 (file)
@@ -24,7 +24,11 @@ static char *(p_bo_values[]) = {"all", "backspace", "cursor", "complete",
 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};
index 5998fb35810057fb161e42f2062426dc14bffe31..6b2c45459a705b09701296176f76472758ecb227 100644 (file)
@@ -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 : */
index e8ba18897ce2101fafac6e8d90d5d68544377624..2e580392091e0ee4714b8080c175098073a9c568 100644 (file)
@@ -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(int method_nr, char_u *key, char_u *heade
 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 : */
index 868b131f0f2e67778f287d77d23c77468cc6348a..626d9855bf16985d081d065bcacb76ba6010a903 100644 (file)
@@ -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 : */
index 26ab315febdaa0ea2f97a09c8f4d1c3ba1357981..d62b10f3719047d104413d497b505773590de8ef 100644 (file)
@@ -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
diff --git a/src/testdir/samples/crypt_sodium_invalid.txt b/src/testdir/samples/crypt_sodium_invalid.txt
new file mode 100644 (file)
index 0000000..35e31b5
Binary files /dev/null and b/src/testdir/samples/crypt_sodium_invalid.txt differ
index f386593cec884780850db46be70eed223157e4b3..63bf8a5b2559891a1fb349e5c1a169a6bfb4d0e7 100644 (file)
@@ -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, key, uncrypted_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,10 +98,115 @@ 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_blowfish2()
+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('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()
   split Xuncrypt_unknown.txt
   set bin noeol key= fenc=latin1
index 12f3e968f396df0c7e5c1c39e8ec546ef6e6b626..4b1158a69e77296edecdc0c43f2d6d77e82055c9 100644 (file)
@@ -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, size_t len)
            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, size_t size)
                }
                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 len)
        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;
index d217fe615b4992100d3436f39cd20ce6923cf949..64648161216e897b22dc9985b8b5583d07f4997f 100644 (file)
@@ -553,6 +553,11 @@ static char *(features[]) =
 #else
        "-smartindent",
 #endif
+#ifdef FEAT_SODIUM
+       "+sodium",
+#else
+       "-sodium",
+#endif
 #ifdef FEAT_SOUND
        "+sound",
 #else
@@ -750,6 +755,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    3022,
 /**/
     3021,
 /**/