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