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)
This commit is contained in:
Christian Brabandt
2021-06-20 14:02:16 +02:00
committed by Bram Moolenaar
parent 208f0b48b2
commit f573c6e1ed
29 changed files with 820 additions and 64 deletions

View 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'

View 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.

View 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.

View 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

View 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

View 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

View 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

66
src/auto/configure vendored
View File

@ -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; }

View 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

View 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;

View File

@ -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

View 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)

View File

@ -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);
# 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

View 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;

View 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!"));

View 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",

View File

@ -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)

View File

@ -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;
}

View File

@ -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; // 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

View 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);
}

View 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};

View 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 : */

View 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 : */

View 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 : */

View 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

Binary file not shown.

View 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

View 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;

View 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,
/**/