summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2014-08-10 13:38:34 +0200
committerBram Moolenaar <Bram@vim.org>2014-08-10 13:38:34 +0200
commit8f4ac01544b44bdd906d241e4f203de7496e5ac8 (patch)
tree52ee7ff7368d7953f2baa3d7d015c539b11a345e
parent0106b4b89127b043eddf711c750364b487deb794 (diff)
updated for version 7.4.399v7.4.399
Problem: Encryption implementation is messy. Blowfish encryption has a weakness. Solution: Refactor the encryption, store the state in an allocated struct instead of using a save/restore mechanism. Introduce the "blowfish2" method, which does not have the weakness and encrypts the whole undo file. (largely by David Leadbeater)
-rw-r--r--runtime/doc/editing.txt34
-rw-r--r--runtime/doc/options.txt16
-rw-r--r--src/Makefile31
-rw-r--r--src/blowfish.c307
-rw-r--r--src/crypt.c585
-rw-r--r--src/crypt_zip.c158
-rw-r--r--src/ex_docmd.c3
-rw-r--r--src/fileio.c346
-rw-r--r--src/globals.h4
-rw-r--r--src/main.c3
-rw-r--r--src/memline.c87
-rw-r--r--src/misc2.c335
-rw-r--r--src/option.c10
-rw-r--r--src/proto.h2
-rw-r--r--src/proto/blowfish.pro10
-rw-r--r--src/proto/crypt.pro24
-rw-r--r--src/proto/crypt_zip.pro5
-rw-r--r--src/proto/fileio.pro2
-rw-r--r--src/proto/misc2.pro11
-rw-r--r--src/structs.h25
-rw-r--r--src/testdir/test71.in27
-rw-r--r--src/testdir/test71.ok5
-rw-r--r--src/testdir/test71a.in4
-rw-r--r--src/testdir/test72.in27
-rw-r--r--src/testdir/test72.ok4
-rw-r--r--src/undo.c692
-rw-r--r--src/version.c2
27 files changed, 1781 insertions, 978 deletions
diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt
index 0b5f69a7b7..cbb9b90e5f 100644
--- a/runtime/doc/editing.txt
+++ b/runtime/doc/editing.txt
@@ -1,4 +1,4 @@
-*editing.txt* For Vim version 7.4. Last change: 2014 Jul 19
+*editing.txt* For Vim version 7.4. Last change: 2014 Aug 09
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -1370,8 +1370,13 @@ lose your work. The undo file can be disabled without much disadvantage. >
Note: The text in memory is not encrypted. A system administrator may be able
to see your text while you are editing it. When filtering text with
-":!filter" or using ":w !command" the text is not encrypted, this may reveal
-it to others. The 'viminfo' file is not encrypted.
+":!filter" or using ":w !command" the text is also not encrypted, this may
+reveal it to others. The 'viminfo' file is not encrypted.
+
+You could do this to edit very secret text: >
+ :set noundofile viminfo=
+ :noswapfile edit secrets.txt
+Keep in mind that without a swap file you risk loosing your work in a crash.
WARNING: If you make a typo when entering the key and then write the file and
exit, the text will be lost!
@@ -1398,18 +1403,25 @@ To disable the encryption, reset the 'key' option to an empty value: >
:set key=
You can use the 'cryptmethod' option to select the type of encryption, use one
-of these two: >
- :setlocal cm=zip " weak method, backwards compatible
- :setlocal cm=blowfish " strong method
+of these: >
+ :setlocal cm=zip " weak method, backwards compatible
+ :setlocal cm=blowfish " method with flaws
+ :setlocal cm=blowfish2 " medium strong method
+
Do this before writing the file. When reading an encrypted file it will be
set automatically to the method used when that file was written. You can
change 'cryptmethod' before writing that file to change the method.
+
To set the default method, used for new files, use one of these in your
|vimrc| file: >
set cm=zip
- set cm=blowfish
+ set cm=blowfish2
+Use the first one if you need to be compatible with Vim 7.2 and older. Using
+"blowfish2" is highly recommended if you can use a Vim version that supports
+it.
+
The message given for reading and writing a file will show "[crypted]" when
-using zip, "[blowfish]" when using blowfish.
+using zip, "[blowfish]" when using blowfish, etc.
When writing an undo file, the same key and method will be used for the text
in the undo file. |persistent-undo|.
@@ -1444,7 +1456,7 @@ lines to "/etc/magic", "/usr/share/misc/magic" or wherever your system has the
0 string VimCrypt~ Vim encrypted file
>9 string 01 - "zip" cryptmethod
>9 string 02 - "blowfish" cryptmethod
-
+ >9 string 03 - "blowfish2" cryptmethod
Notes:
- Encryption is not possible when doing conversion with 'charconvert'.
@@ -1468,6 +1480,10 @@ Notes:
- Pkzip uses the same encryption as 'cryptmethod' "zip", and US Govt has no
objection to its export. Pkzip's public file APPNOTE.TXT describes this
algorithm in detail.
+- The implmentation of 'cryptmethod' "blowfish" has a flaw. It is possible to
+ crack the first 64 bytes of a file and in some circumstances more of the
+ file. Use of it is not recommended, but it's still the strongest method
+ supported by Vim 7.3 and 7.4. The "zip" method is even weaker.
- Vim originates from the Netherlands. That is where the sources come from.
Thus the encryption code is not exported from the USA.
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index 5f4a06e7ef..9d9cb1f937 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -2229,10 +2229,18 @@ A jump table for the options with a short description can be found at |Q_op|.
zip PkZip compatible method. A weak kind of encryption.
Backwards compatible with Vim 7.2 and older.
*blowfish*
- blowfish Blowfish method. Strong encryption. Requires Vim 7.3
- or later, files can NOT be read by Vim 7.2 and older.
- This adds a "seed" to the file, every time you write
- the file the encrypted bytes will be different.
+ blowfish Blowfish method. Medium strong encryption but it has
+ an implementation flaw. Requires Vim 7.3 or later,
+ files can NOT be read by Vim 7.2 and older. This adds
+ a "seed" to the file, every time you write the file
+ the encrypted bytes will be different.
+ *blowfish2*
+ blowfish2 Blowfish method. Medium strong encryption. Requires
+ Vim 7.4.399 or later, files can NOT be read by Vim 7.3
+ and older. This adds a "seed" to the file, every time
+ you write the file the encrypted bytes will be
+ different. The whole undo file is encrypted, not just
+ the pieces of text.
When reading an encrypted file 'cryptmethod' will be set automatically
to the detected method of the file being read. Thus if you write it
diff --git a/src/Makefile b/src/Makefile
index 1d20ec1af1..772b15c7d6 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1431,6 +1431,8 @@ BASIC_SRC = \
blowfish.c \
buffer.c \
charset.c \
+ crypt.c \
+ crypt_zip.c \
diff.c \
digraph.c \
edit.c \
@@ -1520,6 +1522,8 @@ OBJ_COMMON = \
objects/buffer.o \
objects/blowfish.o \
objects/charset.o \
+ objects/crypt.o \
+ objects/crypt_zip.o \
objects/diff.o \
objects/digraph.o \
objects/edit.o \
@@ -1589,6 +1593,8 @@ PRO_AUTO = \
blowfish.pro \
buffer.pro \
charset.pro \
+ crypt.pro \
+ crypt_zip.pro \
diff.pro \
digraph.pro \
edit.pro \
@@ -1753,10 +1759,11 @@ xxd/xxd$(EXEEXT): xxd/xxd.c
languages:
@if test -n "$(MAKEMO)" -a -f $(PODIR)/Makefile; then \
cd $(PODIR); \
- CC="$(CC)" $(MAKE) prefix=$(DESTDIR)$(prefix); \
+ CC="$(CC)" $(MAKE) prefix=$(DESTDIR)$(prefix); \
fi
-@if test -n "$(MAKEMO)" -a -f $(PODIR)/Makefile; then \
- cd $(PODIR); CC="$(CC)" $(MAKE) prefix=$(DESTDIR)$(prefix) converted; \
+ cd $(PODIR); \
+ CC="$(CC)" $(MAKE) prefix=$(DESTDIR)$(prefix) converted; \
fi
# Update the *.po files for changes in the sources. Only run manually.
@@ -1883,8 +1890,14 @@ unittest unittests: $(UNITTEST_TARGETS)
# Run individual test, assuming that Vim was already compiled.
test1 test2 test3 test4 test5 test6 test7 test8 test9 \
test_autoformat_join \
+ test_breakindent \
+ test_changelist \
test_eval \
+ test_insertcount \
+ test_listlbr \
+ test_listlbr_utf8 \
test_options \
+ test_qf_title \
test10 test11 test12 test13 test14 test15 test16 test17 test18 test19 \
test20 test21 test22 test23 test24 test25 test26 test27 test28 test29 \
test30 test31 test32 test33 test34 test35 test36 test37 test38 test39 \
@@ -2506,6 +2519,12 @@ objects/buffer.o: buffer.c
objects/charset.o: charset.c
$(CCC) -o $@ charset.c
+objects/crypt.o: crypt.c
+ $(CCC) -o $@ crypt.c
+
+objects/crypt_zip.o: crypt_zip.c
+ $(CCC) -o $@ crypt_zip.c
+
objects/diff.o: diff.c
$(CCC) -o $@ diff.c
@@ -2855,6 +2874,14 @@ objects/charset.o: charset.c vim.h auto/config.h feature.h os_unix.h auto/osdef.
ascii.h keymap.h term.h macros.h option.h structs.h regexp.h gui.h \
gui_beval.h proto/gui_beval.pro ex_cmds.h proto.h globals.h farsi.h \
arabic.h
+objects/crypt.o: crypt.c vim.h auto/config.h feature.h os_unix.h auto/osdef.h \
+ ascii.h keymap.h term.h macros.h option.h structs.h regexp.h gui.h \
+ gui_beval.h proto/gui_beval.pro ex_cmds.h proto.h globals.h farsi.h \
+ arabic.h
+objects/crypt_zip.o: crypt_zip.c vim.h auto/config.h feature.h os_unix.h \
+ auto/osdef.h ascii.h keymap.h term.h macros.h option.h structs.h \
+ regexp.h gui.h gui_beval.h proto/gui_beval.pro ex_cmds.h proto.h \
+ globals.h farsi.h arabic.h
objects/diff.o: diff.c vim.h auto/config.h feature.h os_unix.h auto/osdef.h \
ascii.h keymap.h term.h macros.h option.h structs.h regexp.h gui.h \
gui_beval.h proto/gui_beval.pro ex_cmds.h proto.h globals.h farsi.h \
diff --git a/src/blowfish.c b/src/blowfish.c
index 3d9ba5566a..6bf2482f0e 100644
--- a/src/blowfish.c
+++ b/src/blowfish.c
@@ -9,17 +9,25 @@
* Blowfish encryption for Vim; in Blowfish cipher feedback mode.
* Contributed by Mohsin Ahmed, http://www.cs.albany.edu/~mosh
* Based on http://www.schneier.com/blowfish.html by Bruce Schneier.
+ *
+ * There are two variants:
+ * - The old one "blowfish" has a flaw which makes it much easier to crack the
+ * key. To see this, make a text file with one line of 1000 "x" characters
+ * and write it encrypted. Use "xxd" to inspect the bytes in the file. You
+ * will see that a block of 8 bytes repeats 8 times.
+ * - The new one "blowfish2" is better. It uses an 8 byte CFB to avoid the
+ * repeats.
*/
#include "vim.h"
-#if defined(FEAT_CRYPT)
+#if defined(FEAT_CRYPT) || defined(PROTO)
#define ARRAY_LENGTH(A) (sizeof(A)/sizeof(A[0]))
#define BF_BLOCK 8
#define BF_BLOCK_MASK 7
-#define BF_CFB_LEN (8*(BF_BLOCK))
+#define BF_MAX_CFB_LEN (8 * BF_BLOCK)
typedef union {
UINT32_T ul[2];
@@ -37,14 +45,26 @@ typedef union {
# endif
#endif
-static void bf_e_block __ARGS((UINT32_T *p_xl, UINT32_T *p_xr));
-static void bf_e_cblock __ARGS((char_u *block));
-static int bf_check_tables __ARGS((UINT32_T a_ipa[18], UINT32_T a_sbi[4][256], UINT32_T val));
+/* The state of encryption, referenced by cryptstate_T. */
+typedef struct {
+ UINT32_T pax[18]; /* P-array */
+ UINT32_T sbx[4][256]; /* S-boxes */
+ int randbyte_offset;
+ int update_offset;
+ char_u cfb_buffer[BF_MAX_CFB_LEN]; /* up to 64 bytes used */
+ int cfb_len; /* size of cfb_buffer actually used */
+} bf_state_T;
+
+
+static void bf_e_block __ARGS((bf_state_T *state, UINT32_T *p_xl, UINT32_T *p_xr));
+static void bf_e_cblock __ARGS((bf_state_T *state, char_u *block));
+static int bf_check_tables __ARGS((UINT32_T pax[18], UINT32_T sbx[4][256], UINT32_T val));
static int bf_self_test __ARGS((void));
+static void bf_key_init __ARGS((bf_state_T *state, char_u *password, char_u *salt, int salt_len));
+static void bf_cfb_init __ARGS((bf_state_T *state, char_u *seed, int seed_len));
/* Blowfish code */
-static UINT32_T pax[18];
-static UINT32_T ipa[18] = {
+static UINT32_T pax_init[18] = {
0x243f6a88u, 0x85a308d3u, 0x13198a2eu,
0x03707344u, 0xa4093822u, 0x299f31d0u,
0x082efa98u, 0xec4e6c89u, 0x452821e6u,
@@ -53,8 +73,7 @@ static UINT32_T ipa[18] = {
0xb5470917u, 0x9216d5d9u, 0x8979fb1bu
};
-static UINT32_T sbx[4][256];
-static UINT32_T sbi[4][256] = {
+static UINT32_T sbx_init[4][256] = {
{0xd1310ba6u, 0x98dfb5acu, 0x2ffd72dbu, 0xd01adfb7u,
0xb8e1afedu, 0x6a267e96u, 0xba7c9045u, 0xf12c7f99u,
0x24a19947u, 0xb3916cf7u, 0x0801f2e2u, 0x858efc16u,
@@ -314,33 +333,40 @@ static UINT32_T sbi[4][256] = {
}
};
-
#define F1(i) \
- xl ^= pax[i]; \
- xr ^= ((sbx[0][xl >> 24] + \
- sbx[1][(xl & 0xFF0000) >> 16]) ^ \
- sbx[2][(xl & 0xFF00) >> 8]) + \
- sbx[3][xl & 0xFF];
+ xl ^= bfs->pax[i]; \
+ xr ^= ((bfs->sbx[0][xl >> 24] + \
+ bfs->sbx[1][(xl & 0xFF0000) >> 16]) ^ \
+ bfs->sbx[2][(xl & 0xFF00) >> 8]) + \
+ bfs->sbx[3][xl & 0xFF];
#define F2(i) \
- xr ^= pax[i]; \
- xl ^= ((sbx[0][xr >> 24] + \
- sbx[1][(xr & 0xFF0000) >> 16]) ^ \
- sbx[2][(xr & 0xFF00) >> 8]) + \
- sbx[3][xr & 0xFF];
-
+ xr ^= bfs->pax[i]; \
+ xl ^= ((bfs->sbx[0][xr >> 24] + \
+ bfs->sbx[1][(xr & 0xFF0000) >> 16]) ^ \
+ bfs->sbx[2][(xr & 0xFF00) >> 8]) + \
+ bfs->sbx[3][xr & 0xFF];
static void
-bf_e_block(p_xl, p_xr)
+bf_e_block(bfs, p_xl, p_xr)
+ bf_state_T *bfs;
UINT32_T *p_xl;
UINT32_T *p_xr;
{
- UINT32_T temp, xl = *p_xl, xr = *p_xr;
-
- F1(0) F2(1) F1(2) F2(3) F1(4) F2(5) F1(6) F2(7)
- F1(8) F2(9) F1(10) F2(11) F1(12) F2(13) F1(14) F2(15)
- xl ^= pax[16];
- xr ^= pax[17];
+ UINT32_T temp;
+ UINT32_T xl = *p_xl;
+ UINT32_T xr = *p_xr;
+
+ F1(0) F2(1)
+ F1(2) F2(3)
+ F1(4) F2(5)
+ F1(6) F2(7)
+ F1(8) F2(9)
+ F1(10) F2(11)
+ F1(12) F2(13)
+ F1(14) F2(15)
+ xl ^= bfs->pax[16];
+ xr ^= bfs->pax[17];
temp = xl;
xl = xr;
xr = temp;
@@ -348,22 +374,6 @@ bf_e_block(p_xl, p_xr)
*p_xr = xr;
}
-#if 0 /* not used */
- static void
-bf_d_block(p_xl, p_xr)
- UINT32_T *p_xl;
- UINT32_T *p_xr;
-{
- UINT32_T temp, xl = *p_xl, xr = *p_xr;
- F1(17) F2(16) F1(15) F2(14) F1(13) F2(12) F1(11) F2(10)
- F1(9) F2(8) F1(7) F2(6) F1(5) F2(4) F1(3) F2(2)
- xl ^= pax[1];
- xr ^= pax[0];
- temp = xl; xl = xr; xr = temp;
- *p_xl = xl; *p_xr = xr;
-}
-#endif
-
#ifdef WORDS_BIGENDIAN
# define htonl2(x) \
@@ -374,7 +384,8 @@ bf_d_block(p_xl, p_xr)
#endif
static void
-bf_e_cblock(block)
+bf_e_cblock(bfs, block)
+ bf_state_T *bfs;
char_u *block;
{
block8 bk;
@@ -382,35 +393,22 @@ bf_e_cblock(block)
memcpy(bk.uc, block, 8);
htonl2(bk.ul[0]);
htonl2(bk.ul[1]);
- bf_e_block(&bk.ul[0], &bk.ul[1]);
+ bf_e_block(bfs, &bk.ul[0], &bk.ul[1]);
htonl2(bk.ul[0]);
htonl2(bk.ul[1]);
memcpy(block, bk.uc, 8);
}
-#if 0 /* not used */
- void
-bf_d_cblock(block)
- char_u *block;
-{
- block8 bk;
- memcpy(bk.uc, block, 8);
- htonl2(bk.ul[0]); htonl2(bk.ul[1]);
- bf_d_block(&bk.ul[0], &bk.ul[1]);
- htonl2(bk.ul[0]); htonl2(bk.ul[1]);
- memcpy(block, bk.uc, 8);
-}
-#endif
-
/*
* Initialize the crypt method using "password" as the encryption key and
* "salt[salt_len]" as the salt.
*/
- void
-bf_key_init(password, salt, salt_len)
- char_u *password;
- char_u *salt;
- int salt_len;
+ static void
+bf_key_init(bfs, password, salt, salt_len)
+ bf_state_T *bfs;
+ char_u *password;
+ char_u *salt;
+ int salt_len;
{
int i, j, keypos = 0;
unsigned u;
@@ -418,7 +416,7 @@ bf_key_init(password, salt, salt_len)
char_u *key;
int keylen;
- /* Process the key 1000 times.
+ /* Process the key 1001 times.
* See http://en.wikipedia.org/wiki/Key_strengthening. */
key = sha256_key(password, salt, salt_len);
for (i = 0; i < 1000; i++)
@@ -437,52 +435,54 @@ bf_key_init(password, salt, salt_len)
key[i] = u;
}
- mch_memmove(sbx, sbi, 4 * 4 * 256);
+ /* Use "key" to initialize the P-array ("pax") and S-boxes ("sbx") of
+ * Blowfish. */
+ mch_memmove(bfs->sbx, sbx_init, 4 * 4 * 256);
for (i = 0; i < 18; ++i)
{
val = 0;
for (j = 0; j < 4; ++j)
val = (val << 8) | key[keypos++ % keylen];
- pax[i] = ipa[i] ^ val;
+ bfs->pax[i] = pax_init[i] ^ val;
}
data_l = data_r = 0;
for (i = 0; i < 18; i += 2)
{
- bf_e_block(&data_l, &data_r);
- pax[i + 0] = data_l;
- pax[i + 1] = data_r;
+ bf_e_block(bfs, &data_l, &data_r);
+ bfs->pax[i + 0] = data_l;
+ bfs->pax[i + 1] = data_r;
}
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 256; j += 2)
{
- bf_e_block(&data_l, &data_r);
- sbx[i][j + 0] = data_l;
- sbx[i][j + 1] = data_r;
+ bf_e_block(bfs, &data_l, &data_r);
+ bfs->sbx[i][j + 0] = data_l;
+ bfs->sbx[i][j + 1] = data_r;
}
}
}
/*
- * BF Self test for corrupted tables or instructions
+ * Blowfish self-test for corrupted tables or instructions.
*/
static int
-bf_check_tables(a_ipa, a_sbi, val)
- UINT32_T a_ipa[18];
- UINT32_T a_sbi[4][256];
+bf_check_tables(pax, sbx, val)
+ UINT32_T pax[18];
+ UINT32_T sbx[4][256];
UINT32_T val;
{
int i, j;
UINT32_T c = 0;
for (i = 0; i < 18; i++)
- c ^= a_ipa[i];
+ c ^= pax[i];
for (i = 0; i < 4; i++)
for (j = 0; j < 256; j++)
- c ^= a_sbi[i][j];
+ c ^= sbx[i][j];
return c == val;
}
@@ -520,6 +520,10 @@ bf_self_test()
int err = 0;
block8 bk;
UINT32_T ui = 0xffffffffUL;
+ bf_state_T state;
+
+ vim_memset(&state, 0, sizeof(bf_state_T));
+ state.cfb_len = BF_MAX_CFB_LEN;
/* We can't simply use sizeof(UINT32_T), it would generate a compiler
* warning. */
@@ -528,21 +532,21 @@ bf_self_test()
EMSG(_("E820: sizeof(uint32_t) != 4"));
}
- if (!bf_check_tables(ipa, sbi, 0x6ffa520a))
+ if (!bf_check_tables(pax_init, sbx_init, 0x6ffa520a))
err++;
bn = ARRAY_LENGTH(bf_test_data);
for (i = 0; i < bn; i++)
{
- bf_key_init((char_u *)(bf_test_data[i].password),
+ bf_key_init(&state, (char_u *)(bf_test_data[i].password),
bf_test_data[i].salt,
(int)STRLEN(bf_test_data[i].salt));
- if (!bf_check_tables(pax, sbx, bf_test_data[i].keysum))
+ if (!bf_check_tables(state.pax, state.sbx, bf_test_data[i].keysum))
err++;
/* Don't modify bf_test_data[i].plaintxt, self test is idempotent. */
memcpy(bk.uc, bf_test_data[i].plaintxt, 8);
- bf_e_cblock(bk.uc);
+ bf_e_cblock(&state, bk.uc);
if (memcmp(bk.uc, bf_test_data[i].cryptxt, 8) != 0)
{
if (err == 0 && memcmp(bk.uc, bf_test_data[i].badcryptxt, 8) == 0)
@@ -554,43 +558,43 @@ bf_self_test()
return err > 0 ? FAIL : OK;
}
-/* Cipher feedback mode. */
-static int randbyte_offset = 0;
-static int update_offset = 0;
-static char_u cfb_buffer[BF_CFB_LEN]; /* 64 bytes */
+/*
+ * CFB: Cipher Feedback Mode.
+ */
/*
- * Initialize with seed "iv[iv_len]".
+ * Initialize with seed "seed[seed_len]".
*/
- void
-bf_cfb_init(iv, iv_len)
- char_u *iv;
- int iv_len;
+ static void
+bf_cfb_init(bfs, seed, seed_len)
+ bf_state_T *bfs;
+ char_u *seed;
+ int seed_len;
{
int i, mi;
- randbyte_offset = update_offset = 0;
- vim_memset(cfb_buffer, 0, BF_CFB_LEN);
- if (iv_len > 0)
+ bfs->randbyte_offset = bfs->update_offset = 0;
+ vim_memset(bfs->cfb_buffer, 0, bfs->cfb_len);
+ if (seed_len > 0)
{
- mi = iv_len > BF_CFB_LEN ? iv_len : BF_CFB_LEN;
+ mi = seed_len > bfs->cfb_len ? seed_len : bfs->cfb_len;
for (i = 0; i < mi; i++)
- cfb_buffer[i % BF_CFB_LEN] ^= iv[i % iv_len];
+ bfs->cfb_buffer[i % bfs->cfb_len] ^= seed[i % seed_len];
}
}
-#define BF_CFB_UPDATE(c) { \
- cfb_buffer[update_offset] ^= (char_u)c; \
- if (++update_offset == BF_CFB_LEN) \
- update_offset = 0; \
+#define BF_CFB_UPDATE(bfs, c) { \
+ bfs->cfb_buffer[bfs->update_offset] ^= (char_u)c; \
+ if (++bfs->update_offset == bfs->cfb_len) \
+ bfs->update_offset = 0; \
}
-#define BF_RANBYTE(t) { \
- if ((randbyte_offset & BF_BLOCK_MASK) == 0) \
- bf_e_cblock(&cfb_buffer[randbyte_offset]); \
- t = cfb_buffer[randbyte_offset]; \
- if (++randbyte_offset == BF_CFB_LEN) \
- randbyte_offset = 0; \
+#define BF_RANBYTE(bfs, t) { \
+ if ((bfs->randbyte_offset & BF_BLOCK_MASK) == 0) \
+ bf_e_cblock(bfs, &(bfs->cfb_buffer[bfs->randbyte_offset])); \
+ t = bfs->cfb_buffer[bfs->randbyte_offset]; \
+ if (++bfs->randbyte_offset == bfs->cfb_len) \
+ bfs->randbyte_offset = 0; \
}
/*
@@ -598,90 +602,69 @@ bf_cfb_init(iv, iv_len)
* "from" and "to" can be equal to encrypt in place.
*/
void
-bf_crypt_encode(from, len, to)
+crypt_blowfish_encode(state, from, len, to)
+ cryptstate_T *state;
char_u *from;
size_t len;
char_u *to;
{
+ bf_state_T *bfs = state->method_state;
size_t i;
int ztemp, t;
for (i = 0; i < len; ++i)
{
ztemp = from[i];
- BF_RANBYTE(t);
- BF_CFB_UPDATE(ztemp);
+ BF_RANBYTE(bfs, t);
+ BF_CFB_UPDATE(bfs, ztemp);
to[i] = t ^ ztemp;
}
}
/*
- * Decrypt "ptr[len]" in place.
+ * Decrypt "from[len]" into "to[len]".
*/
void
-bf_crypt_decode(ptr, len)
- char_u *ptr;
- long len;
+crypt_blowfish_decode(state, from, len, to)
+ cryptstate_T *state;
+ char_u *from;
+ size_t len;
+ char_u *to;
{
- char_u *p;
+ bf_state_T *bfs = state->method_state;
+ size_t i;
int t;
- for (p = ptr; p < ptr + len; ++p)
+ for (i = 0; i < len; ++i)
{
- BF_RANBYTE(t);
- *p ^= t;
- BF_CFB_UPDATE(*p);
+ BF_RANBYTE(bfs, t);
+ to[i] = from[i] ^ t;
+ BF_CFB_UPDATE(bfs, to[i]);
}
}
-/*
- * Initialize the encryption keys and the random header according to
- * the given password.
- */
void
-bf_crypt_init_keys(passwd)
- char_u *passwd; /* password string with which to modify keys */
+crypt_blowfish_init(state, key, salt, salt_len, seed, seed_len)
+ cryptstate_T *state;
+ char_u* key;
+ char_u* salt;
+ int salt_len;
+ char_u* seed;
+ int seed_len;
{
- char_u *p;
+ bf_state_T *bfs = (bf_state_T *)alloc_clear(sizeof(bf_state_T));
- for (p = passwd; *p != NUL; ++p)
- {
- BF_CFB_UPDATE(*p);
- }
-}
+ state->method_state = bfs;
-static int save_randbyte_offset;
-static int save_update_offset;
-static char_u save_cfb_buffer[BF_CFB_LEN];
-static UINT32_T save_pax[18];
-static UINT32_T save_sbx[4][256];
+ /* "blowfish" uses a 64 byte buffer, causing it to repeat 8 byte groups 8
+ * times. "blowfish2" uses a 8 byte buffer to avoid repeating. */
+ bfs->cfb_len = state->method_nr == CRYPT_M_BF ? BF_MAX_CFB_LEN : BF_BLOCK;
-/*
- * Save the current crypt state. Can only be used once before
- * bf_crypt_restore().
- */
- void
-bf_crypt_save()
-{
- save_randbyte_offset = randbyte_offset;
- save_update_offset = update_offset;
- mch_memmove(save_cfb_buffer, cfb_buffer, BF_CFB_LEN);
- mch_memmove(save_pax, pax, 4 * 18);
- mch_memmove(save_sbx, sbx, 4 * 4 * 256);
-}
+ if (blowfish_self_test() == FAIL)
+ return;
-/*
- * Restore the current crypt state. Can only be used after
- * bf_crypt_save().
- */
- void
-bf_crypt_restore()
-{
- randbyte_offset = save_randbyte_offset;
- update_offset = save_update_offset;
- mch_memmove(cfb_buffer, save_cfb_buffer, BF_CFB_LEN);
- mch_memmove(pax, save_pax, 4 * 18);
- mch_memmove(sbx, save_sbx, 4 * 4 * 256);
+ bf_key_init(bfs, key, salt, salt_len);
+ bf_cfb_init(bfs, seed, seed_len);
}
/*
diff --git a/src/crypt.c b/src/crypt.c
new file mode 100644
index 0000000000..758ffb17b2
--- /dev/null
+++ b/src/crypt.c
@@ -0,0 +1,585 @@
+/* vi:set ts=8 sts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ * See README.txt for an overview of the Vim source code.
+ */
+
+/*
+ * crypt.c: Generic encryption support.
+ */
+#include "vim.h"
+
+#if defined(FEAT_CRYPT) || defined(PROTO)
+/*
+ * Optional encryption support.
+ * Mohsin Ahmed, mosh@sasi.com, 1998-09-24
+ * Based on zip/crypt sources.
+ * Refactored by David Leadbeater, 2014.
+ *
+ * NOTE FOR USA: Since 2000 exporting this code from the USA is allowed to
+ * most countries. There are a few exceptions, but that still should not be a
+ * problem since this code was originally created in Europe and India.
+ *
+ * Blowfish addition originally made by Mohsin Ahmed,
+ * http://www.cs.albany.edu/~mosh 2010-03-14
+ * Based on blowfish by Bruce Schneier (http://www.schneier.com/blowfish.html)
+ * and sha256 by Christophe Devine.
+ */
+
+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 works_inplace; /* encryption/decryption can be done in-place */
+ int whole_undofile; /* whole undo file is encrypted */
+
+ /* Optional function pointer for a self-test. */
+ int (* self_test_fn)();
+
+ /* Function pointer for initializing encryption/decription. */
+ void (* init_fn)(cryptstate_T *state, char_u *key,
+ char_u *salt, int salt_len, char_u *seed, int seed_len);
+
+ /* 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);
+ void (*decode_fn)(cryptstate_T *state, char_u *from, size_t len,
+ char_u *to);
+
+ /* 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);
+ long (*decode_buffer_fn)(cryptstate_T *state, char_u *from, size_t len,
+ char_u **newptr);
+
+ /* Function pointers for in-place encoding and decoding, used for
+ * crypt_*_inplace(). "from" and "to" arguments will be equal.
+ * These may be the same as decode_fn and encode_fn above, however an
+ * algorithm may implement them in a way that is not interchangeable with
+ * the crypt_(en|de)code() interface (for example because it wishes to add
+ * 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);
+ void (*decode_inplace_fn)(cryptstate_T *state, char_u *p1, size_t len,
+ char_u *p2);
+} cryptmethod_T;
+
+/* index is method_nr of cryptstate_T, CRYPT_M_* */
+static cryptmethod_T cryptmethods[CRYPT_M_COUNT] = {
+ /* PK_Zip; very weak */
+ {
+ "zip",
+ "VimCrypt~01!",
+ 0,
+ 0,
+ TRUE,
+ FALSE,
+ NULL,
+ crypt_zip_init,
+ crypt_zip_encode, crypt_zip_decode,
+ NULL, NULL,
+ crypt_zip_encode, crypt_zip_decode,
+ },
+
+ /* Blowfish/CFB + SHA-256 custom key derivation; implementation issues. */
+ {
+ "blowfish",
+ "VimCrypt~02!",
+ 8,
+ 8,
+ TRUE,
+ FALSE,
+ blowfish_self_test,
+ crypt_blowfish_init,
+ crypt_blowfish_encode, crypt_blowfish_decode,
+ NULL, NULL,
+ crypt_blowfish_encode, crypt_blowfish_decode,
+ },
+
+ /* Blowfish/CFB + SHA-256 custom key derivation; fixed. */
+ {
+ "blowfish2",
+ "VimCrypt~03!",
+ 8,
+ 8,
+ TRUE,
+ TRUE,
+ blowfish_self_test,
+ crypt_blowfish_init,
+ crypt_blowfish_encode, crypt_blowfish_decode,
+ NULL, NULL,
+ crypt_blowfish_encode, crypt_blowfish_decode,
+ },
+};
+
+#define CRYPT_MAGIC_LEN 12 /* cannot change */
+static char crypt_magic_head[] = "VimCrypt~";
+
+/*
+ * Return int value for crypt method name.
+ * 0 for "zip", the old method. Also for any non-valid value.
+ * 1 for "blowfish".
+ * 2 for "blowfish2".
+ */
+ int
+crypt_method_nr_from_name(name)
+ char_u *name;
+{
+ int i;
+
+ for (i = 0; i < CRYPT_M_COUNT; ++i)
+ if (STRCMP(name, cryptmethods[i].name) == 0)
+ return i;
+ return 0;
+}
+
+/*
+ * Get the crypt method used for a file from "ptr[len]", the magic text at the
+ * start of the file.
+ * Returns -1 when no encryption used.
+ */
+ int
+crypt_method_nr_from_magic(ptr, len)
+ char *ptr;
+ int len;
+{
+ int i;
+
+ if (len < CRYPT_MAGIC_LEN)
+ return -1;
+
+ for (i = 0; i < CRYPT_M_COUNT; i++)
+ if (memcmp(ptr, cryptmethods[i].magic, CRYPT_MAGIC_LEN) == 0)
+ return i;
+
+ i = (int)STRLEN(crypt_magic_head);
+ if (len >= i && memcmp(ptr, crypt_magic_head, i) == 0)
+ EMSG(_("E821: File is encrypted with unknown method"));
+
+ return -1;
+}
+
+/*
+ * Return TRUE if the crypt method for "method_nr" can be done in-place.
+ */
+ int
+crypt_works_inplace(state)
+ cryptstate_T *state;
+{
+ return cryptmethods[state->method_nr].works_inplace;
+}
+
+/*
+ * Get the crypt method for buffer "buf" as a number.
+ */
+ int
+crypt_get_method_nr(buf)
+ buf_T *buf;
+{
+ return crypt_method_nr_from_name(*buf->b_p_cm == NUL ? p_cm : buf->b_p_cm);
+}
+
+/*
+ * Return TRUE when the buffer uses an encryption method that encrypts the
+ * whole undo file, not only the tex