summaryrefslogtreecommitdiffstats
path: root/src
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 /src
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)
Diffstat (limited to 'src')
-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
25 files changed, 1744 insertions, 965 deletions
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 text.
+ */
+ int
+crypt_whole_undofile(method_nr)
+ int method_nr;
+{
+ return cryptmethods[method_nr].whole_undofile;
+}
+
+/*
+ * Get crypt method specifc length of the file header in bytes.
+ */
+ int
+crypt_get_header_len(method_nr)
+ int method_nr;
+{
+ return CRYPT_MAGIC_LEN
+ + cryptmethods[method_nr].salt_len
+ + cryptmethods[method_nr].seed_len;
+}
+
+/*
+ * Set the crypt method for buffer "buf" to "method_nr" using the int value as
+ * returned by crypt_method_nr_from_name().
+ */
+ void
+crypt_set_cm_option(buf, method_nr)
+ buf_T *buf;
+ int method_nr;
+{
+ free_string_option(buf->b_p_cm);
+ buf->b_p_cm = vim_strsave((char_u *)cryptmethods[method_nr].name);
+}
+
+/*
+ * If the crypt method for the current buffer has a self-test, run it and
+ * return OK/FAIL.
+ */
+ int
+crypt_self_test()
+{
+ int method_nr = crypt_get_method_nr(curbuf);
+
+ if (cryptmethods[method_nr].self_test_fn == NULL)
+ return OK;
+ return cryptmethods[method_nr].self_test_fn();
+}
+
+/*
+ * Allocate a crypt state and initialize it.
+ */
+ cryptstate_T *
+crypt_create(method_nr, key, salt, salt_len, seed, seed_len)
+ int method_nr;
+ char_u *key;
+ char_u *salt;
+ int salt_len;
+ char_u *seed;
+ int seed_len;
+{
+ cryptstate_T *state = (cryptstate_T *)alloc((int)sizeof(cryptstate_T));
+
+ state->method_nr = method_nr;
+ cryptmethods[method_nr].init_fn(state, key, salt, salt_len, seed, seed_len);
+ return state;
+}
+
+/*
+ * Allocate a crypt state from a file header and initialize it.
+ * Assumes that header contains at least the number of bytes that
+ * crypt_get_header_len() returns for "method_nr".
+ */
+ cryptstate_T *
+crypt_create_from_header(method_nr, key, header)
+ int method_nr;
+ char_u *key;
+ char_u *header;
+{
+ char_u *salt = NULL;
+ char_u *seed = NULL;
+ int salt_len = cryptmethods[method_nr].salt_len;
+ int seed_len = cryptmethods[method_nr].seed_len;
+
+ if (salt_len > 0)
+ salt = header + CRYPT_MAGIC_LEN;
+ if (seed_len > 0)
+ seed = header + CRYPT_MAGIC_LEN + salt_len;
+
+ return crypt_create(method_nr, key, salt, salt_len, seed, seed_len);
+}
+
+/*
+ * Read the crypt method specific header data from "fp".
+ * Return an allocated cryptstate_T or NULL on error.
+ */
+ cryptstate_T *
+crypt_create_from_file(fp, key)
+ FILE *fp;
+ char_u *key;
+{
+ int method_nr;
+ int header_len;
+ char magic_buffer[CRYPT_MAGIC_LEN];
+ char_u *buffer;
+ cryptstate_T *state;
+
+ if (fread(magic_buffer, CRYPT_MAGIC_LEN, 1, fp) != 1)
+ return NULL;
+ method_nr = crypt_method_nr_from_magic(magic_buffer, CRYPT_MAGIC_LEN);
+ if (method_nr < 0)
+ return NULL;
+
+ header_len = crypt_get_header_len(method_nr);
+ if ((buffer = alloc(header_len)) == NULL)
+ return NULL;
+ mch_memmove(buffer, magic_buffer, CRYPT_MAGIC_LEN);
+ if (header_len > CRYPT_MAGIC_LEN
+ && fread(buffer + CRYPT_MAGIC_LEN,
+ header_len - CRYPT_MAGIC_LEN, 1, fp) != 1)
+ {
+ vim_free(buffer);
+ return NULL;
+ }
+
+ state = crypt_create_from_header(method_nr, key, buffer);
+ vim_free(buffer);
+ return state;
+}
+
+/*
+ * Allocate a cryptstate_T for writing and initialize it with "key".
+ * Allocates and fills in the header and stores it in "header", setting
+ * "header_len". The header may include salt and seed, depending on
+ * cryptmethod. Caller must free header.
+ * Returns the state or NULL on failure.
+ */
+ cryptstate_T *
+crypt_create_for_writing(method_nr, key, header, header_len)
+ int method_nr;
+ char_u *key;
+ char_u **header;
+ int *header_len;
+{
+ int len = crypt_get_header_len(method_nr);
+ char_u *salt = NULL;
+ char_u *seed = NULL;
+ int salt_len = cryptmethods[method_nr].salt_len;
+ int seed_len = cryptmethods[method_nr].seed_len;
+ cryptstate_T *state;
+
+ *header_len = len;
+ *header = alloc(len);
+ if (*header == NULL)
+ return NULL;
+
+ mch_memmove(*header, cryptmethods[method_nr].magic, CRYPT_MAGIC_LEN);
+ if (salt_len > 0 || seed_len > 0)
+ {
+ if (salt_len > 0)
+ salt = *header + CRYPT_MAGIC_LEN;
+ if (seed_len > 0)
+ seed = *header + CRYPT_MAGIC_LEN + salt_len;
+
+ /* TODO: Should this be crypt method specific? (Probably not worth</