diff options
author | Bram Moolenaar <Bram@vim.org> | 2014-08-10 13:38:34 +0200 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2014-08-10 13:38:34 +0200 |
commit | 8f4ac01544b44bdd906d241e4f203de7496e5ac8 (patch) | |
tree | 52ee7ff7368d7953f2baa3d7d015c539b11a345e /src | |
parent | 0106b4b89127b043eddf711c750364b487deb794 (diff) | |
download | vim-git-8f4ac01544b44bdd906d241e4f203de7496e5ac8.tar.gz |
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/Makefile | 31 | ||||
-rw-r--r-- | src/blowfish.c | 307 | ||||
-rw-r--r-- | src/crypt.c | 585 | ||||
-rw-r--r-- | src/crypt_zip.c | 158 | ||||
-rw-r--r-- | src/ex_docmd.c | 3 | ||||
-rw-r--r-- | src/fileio.c | 346 | ||||
-rw-r--r-- | src/globals.h | 4 | ||||
-rw-r--r-- | src/main.c | 3 | ||||
-rw-r--r-- | src/memline.c | 87 | ||||
-rw-r--r-- | src/misc2.c | 335 | ||||
-rw-r--r-- | src/option.c | 10 | ||||
-rw-r--r-- | src/proto.h | 2 | ||||
-rw-r--r-- | src/proto/blowfish.pro | 10 | ||||
-rw-r--r-- | src/proto/crypt.pro | 24 | ||||
-rw-r--r-- | src/proto/crypt_zip.pro | 5 | ||||
-rw-r--r-- | src/proto/fileio.pro | 2 | ||||
-rw-r--r-- | src/proto/misc2.pro | 11 | ||||
-rw-r--r-- | src/structs.h | 25 | ||||
-rw-r--r-- | src/testdir/test71.in | 27 | ||||
-rw-r--r-- | src/testdir/test71.ok | 5 | ||||
-rw-r--r-- | src/testdir/test71a.in | 4 | ||||
-rw-r--r-- | src/testdir/test72.in | 27 | ||||
-rw-r--r-- | src/testdir/test72.ok | 4 | ||||
-rw-r--r-- | src/undo.c | 692 | ||||
-rw-r--r-- | src/version.c | 2 |
25 files changed, 1744 insertions, 965 deletions
diff --git a/src/Makefile b/src/Makefile index 1d20ec1af..772b15c7d 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 3d9ba5566..6bf2482f0 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 000000000..758ffb17b --- /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 + * 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); + } + + state = crypt_create(method_nr, key, salt, salt_len, seed, seed_len); + if (state == NULL) + { + vim_free(*header); + *header = NULL; + } + return state; +} + +/* + * Free the crypt state. + */ + void +crypt_free_state(state) + cryptstate_T *state; +{ + vim_free(state->method_state); + vim_free(state); +} + +/* + * Encode "from[len]" and store the result in a newly allocated buffer, which + * is stored in "newptr". + * Return number of bytes in "newptr", 0 for need more or -1 on error. + */ + long +crypt_encode_alloc(state, from, len, newptr) + cryptstate_T *state; + char_u *from; + size_t len; + char_u **newptr; +{ + 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); + if (len == 0) + /* Not buffering, just return EOF. */ + return len; + + *newptr = alloc(len); + if (*newptr == NULL) + return -1; + method->encode_fn(state, from, len, *newptr); + return len; +} + +/* + * Decrypt "ptr[len]" and store the result in a newly allocated buffer, which + * is stored in "newptr". + * Return number of bytes in "newptr", 0 for need more or -1 on error. + */ + long +crypt_decode_alloc(state, ptr, len, newptr) + cryptstate_T *state; + char_u *ptr; + long len; + char_u **newptr; +{ + 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); + + if (len == 0) + /* Not buffering, just return EOF. */ + return len; + + *newptr = alloc(len); + if (*newptr == NULL) + return -1; + method->decode_fn(state, ptr, len, *newptr); + return len; +} + +/* + * Encrypting "from[len]" into "to[len]". + */ + void +crypt_encode(state, from, len, to) + cryptstate_T *state; + char_u *from; + size_t len; + char_u *to; +{ + cryptmethods[state->method_nr].encode_fn(state, from, len, to); +} + +/* + * decrypting "from[len]" into "to[len]". + */ + void +crypt_decode(state, from, len, to) + cryptstate_T *state; + char_u *from; + size_t len; + char_u *to; +{ + cryptmethods[state->method_nr].decode_fn(state, from, len, to); +} + +/* + * Simple inplace encryption, modifies "buf[len]" in place. + */ + void +crypt_encode_inplace(state, buf, len) + cryptstate_T *state; + char_u *buf; + size_t len; +{ + cryptmethods[state->method_nr].encode_inplace_fn(state, buf, len, buf); +} + +/* + * Simple inplace decryption, modifies "buf[len]" in place. + */ + void +crypt_decode_inplace(state, buf, len) + cryptstate_T *state; + char_u *buf; + size_t len; +{ + cryptmethods[state->method_nr].decode_inplace_fn(state, buf, len, buf); +} + +/* + * Free an allocated crypt key. Clear the text to make sure it doesn't stay + * in memory anywhere. + */ + void +crypt_free_key(key) + char_u *key; +{ + char_u *p; + + if (key != NULL) + { + for (p = key; *p != NUL; ++p) + *p = 0; + vim_free(key); + } +} + +/* + * Ask the user for a crypt key. + * When "store" is TRUE, the new key is stored in the 'key' option, and the + * 'key' option value is returned: Don't free it. + * When "store" is FALSE, the typed key is returned in allocated memory. + * Returns NULL on failure. + */ + char_u * +crypt_get_key(store, twice) + int store; + int twice; /* Ask for the key twice. */ +{ + char_u *p1, *p2 = NULL; + int round; + + for (round = 0; ; ++round) + { + cmdline_star = TRUE; + cmdline_row = msg_row; + p1 = getcmdline_prompt(NUL, round == 0 + ? (char_u *)_("Enter encryption key: ") + : (char_u *)_("Enter same key again: "), 0, EXPAND_NOTHING, + NULL); + cmdline_star = FALSE; + + if (p1 == NULL) + break; + + if (round == twice) + { + if (p2 != NULL && STRCMP(p1, p2) != 0) + { + MSG(_("Keys don't match!")); + crypt_free_key(p1); + crypt_free_key(p2); + p2 = NULL; + round = -1; /* do it again */ + continue; + } + + if (store) + { + set_option_value((char_u *)"key", 0L, p1, OPT_LOCAL); + crypt_free_key(p1); + p1 = curbuf->b_p_key; + } + break; + } + p2 = p1; + } + + /* since the user typed this, no need to wait for return */ + if (msg_didout) + msg_putchar('\n'); + need_wait_return = FALSE; + msg_didout = FALSE; + + crypt_free_key(p2); + return p1; +} + + +/* + * Append a message to IObuff for the encryption/decryption method being used. + */ + void +crypt_append_msg(buf) + buf_T *buf; +{ + if (crypt_get_method_nr(buf) == 0) + STRCAT(IObuff, _("[crypted]")); + else + { + STRCAT(IObuff, "["); + STRCAT(IObuff, *buf->b_p_cm == NUL ? p_cm : buf->b_p_cm); + STRCAT(IObuff, "]"); + } +} + +#endif /* FEAT_CRYPT */ diff --git a/src/crypt_zip.c b/src/crypt_zip.c new file mode 100644 index 000000000..571f3ec74 --- /dev/null +++ b/src/crypt_zip.c @@ -0,0 +1,158 @@ +/* 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_zip.c: Zip encryption support. + */ +#include "vim.h" + +#if defined(FEAT_CRYPT) || defined(PROTO) +/* + * Optional encryption support. + * Mohsin Ahmed, mosh@sasi.com, 98-09-24 + * Based on zip/crypt sources. + * + * 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. + */ + +/* Need a type that should be 32 bits. 64 also works but wastes space. */ +# if VIM_SIZEOF_INT >= 4 +typedef unsigned int u32_T; /* int is at least 32 bits */ +# else +typedef unsigned long u32_T; /* long should be 32 bits or more */ +# endif + +/* The state of encryption, referenced by cryptstate_T. */ +typedef struct { + u32_T keys[3]; +} zip_state_T; + + +static void make_crc_tab __ARGS((void)); + +static u32_T crc_32_table[256]; + +/* + * Fill the CRC table, if not done already. + */ + static void +make_crc_tab() +{ + u32_T s, t, v; + static int done = FALSE; + + if (done) + return; + for (t = 0; t < 256; t++) + { + v = t; + for (s = 0; s < 8; s++) + v = (v >> 1) ^ ((v & 1) * (u32_T)0xedb88320L); + crc_32_table[t] = v; + } + done = TRUE; +} + +#define CRC32(c, b) (crc_32_table[((int)(c) ^ (b)) & 0xff] ^ ((c) >> 8)) + +/* + * Return the next byte in the pseudo-random sequence. + */ +#define DECRYPT_BYTE_ZIP(keys, t) { \ + short_u temp = (short_u)keys[2] | 2; \ + t = (int)(((unsigned)(temp * (temp ^ 1U)) >> 8) & 0xff); \ +} + +/* + * Update the encryption keys with the next byte of plain text. + */ +#define UPDATE_KEYS_ZIP(keys, c) { \ + keys[0] = CRC32(keys[0], (c)); \ + keys[1] += keys[0] & 0xff; \ + keys[1] = keys[1] * 134775813L + 1; \ + keys[2] = CRC32(keys[2], (int)(keys[1] >> 24)); \ +} + +/* + * Initialize for encryption/decryption. + */ + void +crypt_zip_init(state, key, salt, salt_len, seed, seed_len) + cryptstate_T *state; + char_u *key; + char_u *salt UNUSED; + int salt_len UNUSED; + char_u *seed UNUSED; + int seed_len UNUSED; +{ + char_u *p; + zip_state_T *zs; + + zs = (zip_state_T *)alloc(sizeof(zip_state_T)); + state->method_state = zs; + + make_crc_tab(); + zs->keys[0] = 305419896L; + zs->keys[1] = 591751049L; + zs->keys[2] = 878082192L; + for (p = key; *p != NUL; ++p) + { + UPDATE_KEYS_ZIP(zs->keys, (int)*p); + } +} + +/* + * Encrypt "from[len]" into "to[len]". + * "from" and "to" can be equal to encrypt in place. + */ + void +crypt_zip_encode(state, from, len, to) + cryptstate_T *state; + char_u *from; + size_t len; + char_u *to; +{ + zip_state_T *zs = state->method_state; + size_t i; + int ztemp, t; + + for (i = 0; i < len; ++i) + { + ztemp = from[i]; + DECRYPT_BYTE_ZIP(zs->keys, t); + UPDATE_KEYS_ZIP(zs->keys, ztemp); + to[i] = t ^ ztemp; + } +} + +/* + * Decrypt "from[len]" into "to[len]". + */ + void +crypt_zip_decode(state, from, len, to) + cryptstate_T *state; + char_u *from; + size_t len; + char_u *to; +{ + zip_state_T *zs = state->method_state; + size_t i; + short_u temp; + + for (i = 0; i < len; ++i) + { + temp = (short_u)zs->keys[2] | 2; + temp = (int)(((unsigned)(temp * (temp ^ 1U)) >> 8) & 0xff); + UPDATE_KEYS_ZIP(zs->keys, to[i] = from[i] ^ temp); + } +} + +#endif /* FEAT_CRYPT */ diff --git a/src/ex_docmd.c b/src/ex_docmd.c index ba764bfcf..fecb653b5 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -11506,8 +11506,7 @@ ex_match(eap) ex_X(eap) exarg_T *eap UNUSED; { - if (get_crypt_method(curbuf) == 0 || blowfish_self_test() == OK) - (void)get_crypt_key(TRUE, TRUE); + (void)crypt_get_key(TRUE, TRUE); } #endif diff --git a/src/fileio.c b/src/fileio.c index 38dc2597d..a028b22a5 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -24,20 +24,6 @@ #define BUFSIZE 8192 /* size of normal write buffer */ #define SMBUFSIZE 256 /* size of emergency write buffer */ -#ifdef FEAT_CRYPT -/* crypt_magic[0] is pkzip crypt, crypt_magic[1] is sha2+blowfish */ -static char *crypt_magic[] = {"VimCrypt~01!", "VimCrypt~02!"}; -static char crypt_magic_head[] = "VimCrypt~"; -# define CRYPT_MAGIC_LEN 12 /* must be multiple of 4! */ - -/* For blowfish, after the magic header, we store 8 bytes of salt and then 8 - * bytes of seed (initialisation vector). */ -static int crypt_salt_len[] = {0, 8}; -static int crypt_seed_len[] = {0, 8}; -#define CRYPT_SALT_LEN_MAX 8 -#define CRYPT_SEED_LEN_MAX 8 -#endif - /* Is there any system that doesn't have access()? */ #define USE_MCH_ACCESS @@ -55,7 +41,6 @@ static char_u *readfile_charconvert __ARGS((char_u *fname, char_u *fenc, int *fd static void check_marks_read __ARGS((void)); #endif #ifdef FEAT_CRYPT -static int crypt_method_from_magic __ARGS((char *ptr, int len)); static char_u *check_for_cryptkey __ARGS((char_u *cryptkey, char_u *ptr, long *sizep, off_t *filesizep, int newfile, char_u *fname, int *did_ask)); #endif #ifdef UNIX @@ -116,6 +101,9 @@ struct bw_info #ifdef HAS_BW_FLAGS int bw_flags; /* FIO_ flags */ #endif +#ifdef FEAT_CRYPT + buf_T *bw_buffer; /* buffer being written */ +#endif #ifdef FEAT_MBYTE char_u bw_rest[CONV_RESTLEN]; /* not converted bytes */ int bw_restlen; /* nr of bytes in bw_rest[] */ @@ -250,7 +238,6 @@ readfile(fname, sfname, from, lines_to_skip, lines_to_read, eap, flags) #ifdef FEAT_CRYPT char_u *cryptkey = NULL; int did_ask_for_key = FALSE; - int crypt_method_used; #endif #ifdef FEAT_PERSISTENT_UNDO context_sha256_T sha_ctx; @@ -966,13 +953,6 @@ retry: #endif } -#ifdef FEAT_CRYPT - if (cryptkey != NULL) - /* Need to reset the state, but keep the key, don't want to ask for it - * again. */ - crypt_pop_state(); -#endif - /* * When retrying with another "fenc" and the first time "fileformat" * will be reset. @@ -1175,6 +1155,15 @@ retry: if (read_undo_file) sha256_start(&sha_ctx); #endif +#ifdef FEAT_CRYPT + if (curbuf->b_cryptstate != NULL) + { + /* Need to free the state, but keep the key, don't want to ask for + * it again. */ + crypt_free_state(curbuf->b_cryptstate); + curbuf->b_cryptstate = NULL; + } +#endif } while (!error && !got_int) @@ -1339,6 +1328,76 @@ retry: size = read_eintr(fd, ptr, size); } +#ifdef FEAT_CRYPT + /* + * At start of file: Check for magic number of encryption. + */ + if (filesize == 0 && size > 0) + cryptkey = check_for_cryptkey(cryptkey, ptr, &size, + &filesize, newfile, sfname, + &did_ask_for_key); + /* + * Decrypt the read bytes. This is done before checking for + * EOF because the crypt layer may be buffering. + */ + if (cryptkey != NULL && size > 0) + { + if (crypt_works_inplace(curbuf->b_cryptstate)) + { + crypt_decode_inplace(curbuf->b_cryptstate, ptr, size); + } + else + { + char_u *newptr = NULL; + int decrypted_size; + + decrypted_size = crypt_decode_alloc( + curbuf->b_cryptstate, ptr, size, &newptr); + + /* If the crypt layer is buffering, not producing + * anything yet, need to read more. */ + if (size > 0 && decrypted_size == 0) + continue; + + if (linerest == 0) + { + /* Simple case: reuse returned buffer (may be + * NULL, checked later). */ + new_buffer = newptr; + } + else + { + long_u new_size; + + /* Need new buffer to add bytes carried over. */ + new_size = (long_u)(decrypted_size + linerest + 1); + new_buffer = lalloc(new_size, FALSE); + if (new_buffer == NULL) + { + do_outofmem_msg(new_size); + error = TRUE; + break; + } + + mch_memmove(new_buffer, buffer, linerest); + if (newptr != NULL) + mch_memmove(new_buffer + linerest, newptr, + decrypted_size); + } + + if (new_buffer != NULL) + { + vim_free(buffer); + buffer = new_buffer; + new_buffer = NULL; + line_start = buffer; + ptr = buffer + linerest; + } + size = decrypted_size; + } + } +#endif + if (size <= 0) { if (size < 0) /* read error */ @@ -1403,21 +1462,6 @@ retry: } #endif } - -#ifdef FEAT_CRYPT - /* - * At start of file: Check for magic number of encryption. - */ - if (filesize == 0) - cryptkey = check_for_cryptkey(cryptkey, ptr, &size, - &filesize, newfile, sfname, - &did_ask_for_key); - /* - * Decrypt the read bytes. - */ - if (cryptkey != NULL && size > 0) - crypt_decode(ptr, size); -#endif } skip_read = FALSE; @@ -1430,10 +1474,9 @@ retry: */ if ((filesize == 0 # ifdef FEAT_CRYPT - || (filesize == (CRYPT_MAGIC_LEN - + crypt_salt_len[use_crypt_method] - + crypt_seed_len[use_crypt_method]) - && cryptkey != NULL) + || (cryptkey != NULL + && filesize == crypt_get_header_len( + crypt_get_method_nr(curbuf))) # endif ) && (fio_flags == FIO_UCSBOM @@ -2262,15 +2305,15 @@ failed: save_file_ff(curbuf); /* remember the current file format */ #ifdef FEAT_CRYPT - crypt_method_used = use_crypt_method; - if (cryptkey != NULL) + if (curbuf->b_cryptstate != NULL) { - crypt_pop_state(); - if (cryptkey != curbuf->b_p_key) - free_crypt_key(cryptkey); - /* don't set cryptkey to NULL, it's used below as a flag that - * encryption was used */ + crypt_free_state(curbuf->b_cryptstate); + curbuf->b_cryptstate = NULL; } + if (cryptkey != NULL && cryptkey != curbuf->b_p_key) + crypt_free_key(cryptkey); + /* Don't set cryptkey to NULL, it's used below as a flag that + * encryption was used. */ #endif #ifdef FEAT_MBYTE @@ -2457,10 +2500,7 @@ failed: #ifdef FEAT_CRYPT if (cryptkey != NULL) { - if (crypt_method_used == 1) - STRCAT(IObuff, _("[blowfish]")); - else - STRCAT(IObuff, _("[crypted]")); + crypt_append_msg(curbuf); c = TRUE; } #endif @@ -2489,9 +2529,7 @@ failed: #ifdef FEAT_CRYPT if (cryptkey != NULL) msg_add_lines(c, (long)linecnt, filesize - - CRYPT_MAGIC_LEN - - crypt_salt_len[use_crypt_method] - - crypt_seed_len[use_crypt_method]); + - crypt_get_header_len(crypt_get_method_nr(curbuf))); else #endif msg_add_lines(c, (long)linecnt, filesize); @@ -2882,33 +2920,6 @@ check_marks_read() #if defined(FEAT_CRYPT) || defined(PROTO) /* - * 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. - */ - static int -crypt_method_from_magic(ptr, len) - char *ptr; - int len; -{ - int i; - - for (i = 0; i < (int)(sizeof(crypt_magic) / sizeof(crypt_magic[0])); i++) - { - if (len < (CRYPT_MAGIC_LEN + crypt_salt_len[i] + crypt_seed_len[i])) - continue; - if (memcmp(ptr, crypt_magic[i], 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; -} - -/* * Check for magic number used for encryption. Applies to the current buffer. * If found, the magic number is removed from ptr[*sizep] and *sizep and * *filesizep are updated. @@ -2924,7 +2935,7 @@ check_for_cryptkey(cryptkey, ptr, sizep, filesizep, newfile, fname, did_ask) char_u *fname; /* file name to display */ int *did_ask; /* flag: whether already asked for key */ { - int method = crypt_method_from_magic((char *)ptr, *sizep); + int method = crypt_method_nr_from_magic((char *)ptr, *sizep); int b_p_ro = curbuf->b_p_ro; if (method >= 0) @@ -2933,9 +2944,7 @@ check_for_cryptkey(cryptkey, ptr, sizep, filesizep, newfile, fname, did_ask) * Avoids accidentally overwriting the file with garbage. */ curbuf->b_p_ro = TRUE; - set_crypt_method(curbuf, method); - if (method > 0) - (void)blowfish_self_test(); + crypt_set_cm_option(curbuf, method); if (cryptkey == NULL && !*did_ask) { if (*curbuf->b_p_key) @@ -2948,7 +2957,7 @@ check_for_cryptkey(cryptkey, ptr, sizep, filesizep, newfile, fname, did_ask) * Happens when retrying to detect encoding. */ smsg((char_u *)_(need_key_msg), fname); msg_scroll = TRUE; - cryptkey = get_crypt_key(newfile, FALSE); + cryptkey = crypt_get_key(newfile, FALSE); *did_ask = TRUE; /* check if empty key entered */ @@ -2963,24 +2972,18 @@ check_for_cryptkey(cryptkey, ptr, sizep, filesizep, newfile, fname, did_ask) if (cryptkey != NULL) { - int seed_len = crypt_seed_len[method]; - int salt_len = crypt_salt_len[method]; + int header_len; - crypt_push_state(); - use_crypt_method = method; - if (method == 0) - crypt_init_keys(cryptkey); - else - { - bf_key_init(cryptkey, ptr + CRYPT_MAGIC_LEN, salt_len); - bf_cfb_init(ptr + CRYPT_MAGIC_LEN + salt_len, seed_len); - } + curbuf->b_cryptstate = crypt_create_from_header( + method, cryptkey, ptr); + crypt_set_cm_option(curbuf, method); + + /* Remove cryptmethod specific header from the text. */ + header_len = crypt_get_header_len(method); + *filesizep += header_len; + *sizep -= header_len; + mch_memmove(ptr, ptr + header_len, (size_t)*sizep); - /* Remove magic number from the text */ - *filesizep += CRYPT_MAGIC_LEN + salt_len + seed_len; - *sizep -= CRYPT_MAGIC_LEN + salt_len + seed_len; - mch_memmove(ptr, ptr + CRYPT_MAGIC_LEN + salt_len + seed_len, - (size_t)*sizep); /* Restore the read-only flag. */ curbuf->b_p_ro = b_p_ro; } @@ -2992,85 +2995,6 @@ check_for_cryptkey(cryptkey, ptr, sizep, filesizep, newfile, fname, did_ask) return cryptkey; } - -/* - * Check for magic number used for encryption. Applies to the current buffer. - * If found and decryption is possible returns OK; - */ - int -prepare_crypt_read(fp) - FILE *fp; -{ - int method; - char_u buffer[CRYPT_MAGIC_LEN + CRYPT_SALT_LEN_MAX - + CRYPT_SEED_LEN_MAX + 2]; - - if (fread(buffer, CRYPT_MAGIC_LEN, 1, fp) != 1) - return FAIL; - method = crypt_method_from_magic((char *)buffer, - CRYPT_MAGIC_LEN + - CRYPT_SEED_LEN_MAX + - CRYPT_SALT_LEN_MAX); - if (method < 0 || method != get_crypt_method(curbuf)) - return FAIL; - - crypt_push_state(); - if (method == 0) - crypt_init_keys(curbuf->b_p_key); - else - { - int salt_len = crypt_salt_len[method]; - int seed_len = crypt_seed_len[method]; - - if (fread(buffer, salt_len + seed_len, 1, fp) != 1) - return FAIL; - bf_key_init(curbuf->b_p_key, buffer, salt_len); - bf_cfb_init(buffer + salt_len, seed_len); - } - return OK; -} - -/* - * Prepare for writing encrypted bytes for buffer "buf". - * Returns a pointer to an allocated header of length "*lenp". - * When out of memory returns NULL. - * Otherwise calls crypt_push_state(), call crypt_pop_state() later. - */ - char_u * -prepare_crypt_write(buf, lenp) - buf_T *buf; - int *lenp; -{ - char_u *header; - int seed_len = crypt_seed_len[get_crypt_method(buf)]; - int salt_len = crypt_salt_len[get_crypt_method(buf)]; - char_u *salt; - char_u *seed; - - header = alloc_clear(CRYPT_MAGIC_LEN + CRYPT_SALT_LEN_MAX - + CRYPT_SEED_LEN_MAX + 2); - if (header != NULL) - { - crypt_push_state(); - use_crypt_method = get_crypt_method(buf); /* select zip or blowfish */ - vim_strncpy(header, (char_u *)crypt_magic[use_crypt_method], - CRYPT_MAGIC_LEN); - if (use_crypt_method == 0) - crypt_init_keys(buf->b_p_key); - else - { - /* Using blowfish, add salt and seed. */ - salt = header + CRYPT_MAGIC_LEN; - seed = salt + salt_len; - sha2_seed(salt, salt_len, seed, seed_len); - bf_key_init(buf->b_p_key, salt, salt_len); - bf_cfb_init(seed, seed_len); - } - } - *lenp = CRYPT_MAGIC_LEN + salt_len + seed_len; - return header; -} - #endif /* FEAT_CRYPT */ #ifdef UNIX @@ -3224,9 +3148,6 @@ buf_write(buf, fname, sfname, start, end, eap, append, forceit, int write_undo_file = FALSE; context_sha256_T sha_ctx; #endif -#ifdef FEAT_CRYPT - int crypt_method_used; -#endif if (fname == NULL || *fname == NUL) /* safety check */ return FAIL; @@ -3262,6 +3183,9 @@ buf_write(buf, fname, sfname, start, end, eap, append, forceit, write_info.bw_iconv_fd = (iconv_t)-1; # endif #endif +#ifdef FEAT_CRYPT + write_info.bw_buffer = buf; +#endif /* After writing a file changedtick changes but we don't want to display * the line. */ @@ -4505,17 +4429,17 @@ restore_backup: #ifdef FEAT_CRYPT if (*buf->b_p_key != NUL && !filtering) { - char_u *header; - int header_len; + char_u *header; + int header_len; - header = prepare_crypt_write(buf, &header_len); - if (header == NULL) + buf->b_cryptstate = crypt_create_for_writing(crypt_get_method_nr(buf), + buf->b_p_key, &header, &header_len); + if (buf->b_cryptstate == NULL || header == NULL) end = 0; else { - /* Write magic number, so that Vim knows that this file is - * encrypted when reading it again. This also undergoes utf-8 to - * ucs-2/4 conversion when needed. */ + /* Write magic number, so that Vim knows how this file is + * encrypted when reading it back. */ write_info.bw_buf = header; write_info.bw_len = header_len; write_info.bw_flags = FIO_NOCONVERT; @@ -4769,12 +4693,13 @@ restore_backup: mch_set_acl(wfname, acl); #endif #ifdef FEAT_CRYPT - crypt_method_used = use_crypt_method; - if (wb_flags & FIO_ENCRYPTED) - crypt_pop_state(); + if (buf->b_cryptstate != NULL) + { + crypt_free_state(buf->b_cryptstate); + buf->b_cryptstate = NULL; + } #endif - #if defined(FEAT_MBYTE) && defined(FEAT_EVAL) if (wfname != fname) { @@ -4924,10 +4849,7 @@ restore_backup: #ifdef FEAT_CRYPT if (wb_flags & FIO_ENCRYPTED) { - if (crypt_method_used == 1) - STRCAT(IObuff, _("[blowfish]")); - else - STRCAT(IObuff, _("[crypted]")); + crypt_append_msg(buf); c = TRUE; } #endif @@ -5740,8 +5662,26 @@ buf_write_bytes(ip) #endif /* FEAT_MBYTE */ #ifdef FEAT_CRYPT - if (flags & FIO_ENCRYPTED) /* encrypt the data */ - crypt_encode(buf, len, buf); + if (flags & FIO_ENCRYPTED) + { + /* Encrypt the data. Do it in-place if possible, otherwise use an + * allocated buffer. */ + if (crypt_works_inplace(ip->bw_buffer->b_cryptstate)) + { + crypt_encode_inplace(ip->bw_buffer->b_cryptstate, buf, len); + } + else + { + char_u *outbuf; + + len = crypt_encode_alloc(curbuf->b_cryptstate, buf, len, &outbuf); + if (len == 0) + return OK; /* Crypt layer is buffering, will flush later. */ + wlen = write_eintr(ip->bw_fd, outbuf, len); + vim_free(outbuf); + return (wlen < len) ? FAIL : OK; + } + } #endif wlen = write_eintr(ip->bw_fd, buf, len); diff --git a/src/globals.h b/src/globals.h index ce831f33d..d1fdc33ac 100644 --- a/src/globals.h +++ b/src/globals.h @@ -105,10 +105,6 @@ EXTERN int exec_from_reg INIT(= FALSE); /* executing register */ EXTERN int screen_cleared INIT(= FALSE); /* screen has been cleared */ -#ifdef FEAT_CRYPT -EXTERN int use_crypt_method INIT(= 0); -#endif - /* * When '$' is included in 'cpoptions' option set: * When a change command is given that deletes only part of a line, a dollar diff --git a/src/main.c b/src/main.c index 9c92f7a84..2fb2edcb5 100644 --- a/src/main.c +++ b/src/main.c @@ -846,8 +846,7 @@ vim_main2(int argc UNUSED, char **argv UNUSED) #ifdef FEAT_CRYPT if (params.ask_for_key) { - (void)blowfish_self_test(); - (void)get_crypt_key(TRUE, TRUE); + (void)crypt_get_key(TRUE, TRUE); TIME_MSG("getting crypt key"); } #endif diff --git a/src/memline.c b/src/memline.c index fb438d273..a5053f023 100644 --- a/src/memline.c +++ b/src/memline.c @@ -63,6 +63,15 @@ typedef struct pointer_entry PTR_EN; /* block/line-count pair */ #define BLOCK0_ID1 '0' /* block 0 id 1 */ #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 */ + +#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 */ +}; +#endif /* * pointer to a block, used in a pointer block @@ -151,7 +160,7 @@ struct data_block struct block0 { char_u b0_id[2]; /* id for block 0: BLOCK0_ID0 and BLOCK0_ID1, - * BLOCK0_ID1_C0, BLOCK0_ID1_C1 */ + * BLOCK0_ID1_C0, BLOCK0_ID1_C1, etc. */ char_u b0_version[10]; /* Vim version string */ char_u b0_page_size[4];/* number of bytes per page */ char_u b0_mtime[4]; /* last modification time of file */ @@ -256,7 +265,7 @@ static long char_to_long __ARGS((char_u *)); static char_u *make_percent_swname __ARGS((char_u *dir, char_u *name)); #endif #ifdef FEAT_CRYPT -static void ml_crypt_prepare __ARGS((memfile_T *mfp, off_t offset, int reading)); +static cryptstate_T *ml_crypt_prepare __ARGS((memfile_T *mfp, off_t offset, int reading)); #endif #ifdef FEAT_BYTEOFF static void ml_updatechunk __ARGS((buf_T *buf, long line, long len, int updtype)); @@ -359,8 +368,7 @@ ml_open(buf) b0p->b0_hname[B0_HNAME_SIZE - 1] = NUL; long_to_char(mch_get_pid(), b0p->b0_pid); #ifdef FEAT_CRYPT - if (*buf->b_p_key != NUL) - ml_set_b0_crypt(buf, b0p); + ml_set_b0_crypt(buf, b0p); #endif } @@ -436,11 +444,11 @@ ml_set_b0_crypt(buf, b0p) b0p->b0_id[1] = BLOCK0_ID1; else { - if (get_crypt_method(buf) == 0) - b0p->b0_id[1] = BLOCK0_ID1_C0; - else + int method_nr = crypt_get_method_nr(buf); + + b0p->b0_id[1] = id1_codes[method_nr]; + if (method_nr > CRYPT_M_ZIP) { - b0p->b0_id[1] = BLOCK0_ID1_C1; /* Generate a seed and store it in block 0 and in the memfile. */ sha2_seed(&b0p->b0_seed, MF_SEED_LEN, NULL, 0); mch_memmove(buf->b_ml.ml_mfp->mf_seed, &b0p->b0_seed, MF_SEED_LEN); @@ -887,7 +895,8 @@ ml_check_b0_id(b0p) if (b0p->b0_id[0] != BLOCK0_ID0 || (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_C1 + && b0p->b0_id[1] != BLOCK0_ID1_C2) ) return FAIL; return OK; @@ -1255,14 +1264,12 @@ ml_recover() } #ifdef FEAT_CRYPT - if (b0p->b0_id[1] == BLOCK0_ID1_C0) - b0_cm = 0; - else if (b0p->b0_id[1] == BLOCK0_ID1_C1) - { - b0_cm = 1; + for (i = 0; i < (int)(sizeof(id1_codes) / sizeof(int)); ++i) + if (id1_codes[i] == b0p->b0_id[1]) + b0_cm = i; + if (b0_cm > 0) mch_memmove(mfp->mf_seed, &b0p->b0_seed, MF_SEED_LEN); - } - set_crypt_method(buf, b0_cm); + crypt_set_cm_option(buf, b0_cm < 0 ? 0 : b0_cm); #else if (b0p->b0_id[1] != BLOCK0_ID1) { @@ -1389,7 +1396,7 @@ ml_recover() } else smsg((char_u *)_(need_key_msg), fname_used); - buf->b_p_key = get_crypt_key(FALSE, FALSE); + buf->b_p_key = crypt_get_key(FALSE, FALSE); if (buf->b_p_key == NULL) buf->b_p_key = curbuf->b_p_key; else if (*buf->b_p_key == NUL) @@ -4816,6 +4823,7 @@ ml_encrypt_data(mfp, data, offset, size) char_u *text_start; char_u *new_data; int text_len; + cryptstate_T *state; if (dp->db_id != DATA_ID) return data; @@ -4831,10 +4839,9 @@ ml_encrypt_data(mfp, data, offset, size) mch_memmove(new_data, dp, head_end - (char_u *)dp); /* Encrypt the text. */ - crypt_push_state(); - ml_crypt_prepare(mfp, offset, FALSE); - crypt_encode(text_start, text_len, new_data + dp->db_txt_start); - crypt_pop_state(); + state = ml_crypt_prepare(mfp, offset, FALSE); + crypt_encode(state, text_start, text_len, new_data + dp->db_txt_start); + crypt_free_state(state); /* Clear the gap. */ if (head_end < text_start) @@ -4857,6 +4864,7 @@ ml_decrypt_data(mfp, data, offset, size) char_u *head_end; char_u *text_start; int text_len; + cryptstate_T *state; if (dp->db_id == DATA_ID) { @@ -4869,17 +4877,17 @@ ml_decrypt_data(mfp, data, offset, size) return; /* data was messed up */ /* Decrypt the text in place. */ - crypt_push_state(); - ml_crypt_prepare(mfp, offset, TRUE); - crypt_decode(text_start, text_len); - crypt_pop_state(); + state = ml_crypt_prepare(mfp, offset, TRUE); + crypt_decode_inplace(state, text_start, text_len); + crypt_free_state(state); } } /* * Prepare for encryption/decryption, using the key, seed and offset. + * Return an allocated cryptstate_T *. */ - static void + static cryptstate_T * ml_crypt_prepare(mfp, offset, reading) memfile_T *mfp; off_t offset; @@ -4887,38 +4895,37 @@ ml_crypt_prepare(mfp, offset, reading) { buf_T *buf = mfp->mf_buffer; char_u salt[50]; - int method; + int method_nr; char_u *key; char_u *seed; if (reading && mfp->mf_old_key != NULL) { /* Reading back blocks with the previous key/method/seed. */ - method = mfp->mf_old_cm; + method_nr = mfp->mf_old_cm; key = mfp->mf_old_key; seed = mfp->mf_old_seed; } else { - method = get_crypt_method(buf); + method_nr = crypt_get_method_nr(buf); key = buf->b_p_key; seed = mfp->mf_seed; } - use_crypt_method = method; /* select pkzip or blowfish */ - if (method == 0) + if (method_nr == CRYPT_M_ZIP) { + /* For PKzip: Append the offset to the key, so that we use a different + * key for every block. */ vim_snprintf((char *)salt, sizeof(salt), "%s%ld", key, (long)offset); - crypt_init_keys(salt); - } - else - { - /* Using blowfish, add salt and seed. We use the byte offset of the - * block for the salt. */ - vim_snprintf((char *)salt, sizeof(salt), "%ld", (long)offset); - bf_key_init(key, salt, (int)STRLEN(salt)); - bf_cfb_init(seed, MF_SEED_LEN); + return crypt_create(method_nr, salt, NULL, 0, NULL, 0); } + + /* Using blowfish or better: add salt and seed. We use the byte offset + * 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); } #endif diff --git a/src/misc2.c b/src/misc2.c index 1a5b370ba..1f8878f67 100644 --- a/src/misc2.c +++ b/src/misc2.c @@ -3803,322 +3803,6 @@ update_mouseshape(shape_idx) #endif /* CURSOR_SHAPE */ -#ifdef FEAT_CRYPT -/* - * Optional encryption support. - * Mohsin Ahmed, mosh@sasi.com, 98-09-24 - * Based on zip/crypt sources. - * - * 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. - */ - -/* from zip.h */ - -typedef unsigned short ush; /* unsigned 16-bit value */ -typedef unsigned long ulg; /* unsigned 32-bit value */ - -static void make_crc_tab __ARGS((void)); - -static ulg crc_32_tab[256]; - -/* - * Fill the CRC table. - */ - static void -make_crc_tab() -{ - ulg s,t,v; - static int done = FALSE; - - if (done) - return; - for (t = 0; t < 256; t++) - { - v = t; - for (s = 0; s < 8; s++) - v = (v >> 1) ^ ((v & 1) * (ulg)0xedb88320L); - crc_32_tab[t] = v; - } - done = TRUE; -} - -#define CRC32(c, b) (crc_32_tab[((int)(c) ^ (b)) & 0xff] ^ ((c) >> 8)) - -static ulg keys[3]; /* keys defining the pseudo-random sequence */ - -/* - * Return the next byte in the pseudo-random sequence. - */ -#define DECRYPT_BYTE_ZIP(t) { \ - ush temp; \ - \ - temp = (ush)keys[2] | 2; \ - t = (int)(((unsigned)(temp * (temp ^ 1U)) >> 8) & 0xff); \ -} - -/* - * Update the encryption keys with the next byte of plain text. - */ -#define UPDATE_KEYS_ZIP(c) { \ - keys[0] = CRC32(keys[0], (c)); \ - keys[1] += keys[0] & 0xff; \ - keys[1] = keys[1] * 134775813L + 1; \ - keys[2] = CRC32(keys[2], (int)(keys[1] >> 24)); \ -} - -static int crypt_busy = 0; -static ulg saved_keys[3]; -static int saved_crypt_method; - -/* - * Return int value for crypt method string: - * 0 for "zip", the old method. Also for any non-valid value. - * 1 for "blowfish". - */ - int -crypt_method_from_string(s) - char_u *s; -{ - return *s == 'b' ? 1 : 0; -} - -/* - * Get the crypt method for buffer "buf" as a number. - */ - int -get_crypt_method(buf) - buf_T *buf; -{ - return crypt_method_from_string(*buf->b_p_cm == NUL ? p_cm : buf->b_p_cm); -} - -/* - * Set the crypt method for buffer "buf" to "method" using the int value as - * returned by crypt_method_from_string(). - */ - void -set_crypt_method(buf, method) - buf_T *buf; - int method; -{ - free_string_option(buf->b_p_cm); - buf->b_p_cm = vim_strsave((char_u *)(method == 0 ? "zip" : "blowfish")); -} - -/* - * Prepare for initializing encryption. If already doing encryption then save - * the state. - * Must always be called symmetrically with crypt_pop_state(). - */ - void -crypt_push_state() -{ - if (crypt_busy == 1) - { - /* save the state */ - if (use_crypt_method == 0) - { - saved_keys[0] = keys[0]; - saved_keys[1] = keys[1]; - saved_keys[2] = keys[2]; - } - else - bf_crypt_save(); - saved_crypt_method = use_crypt_method; - } - else if (crypt_busy > 1) - EMSG2(_(e_intern2), "crypt_push_state()"); - ++crypt_busy; -} - -/* - * End encryption. If doing encryption before crypt_push_state() then restore - * the saved state. - * Must always be called symmetrically with crypt_push_state(). - */ - void -crypt_pop_state() -{ - --crypt_busy; - if (crypt_busy == 1) - { - use_crypt_method = saved_crypt_method; - if (use_crypt_method == 0) - { - keys[0] = saved_keys[0]; - keys[1] = saved_keys[1]; - keys[2] = saved_keys[2]; - } - else - bf_crypt_restore(); - } -} - -/* - * Encrypt "from[len]" into "to[len]". - * "from" and "to" can be equal to encrypt in place. - */ - void -crypt_encode(from, len, to) - char_u *from; - size_t len; - char_u *to; -{ - size_t i; - int ztemp, t; - - if (use_crypt_method == 0) - for (i = 0; i < len; ++i) - { - ztemp = from[i]; - DECRYPT_BYTE_ZIP(t); - UPDATE_KEYS_ZIP(ztemp); - to[i] = t ^ ztemp; - } - else - bf_crypt_encode(from, len, to); -} - -/* - * Decrypt "ptr[len]" in place. - */ - void -crypt_decode(ptr, len) - char_u *ptr; - long len; -{ - char_u *p; - - if (use_crypt_method == 0) - for (p = ptr; p < ptr + len; ++p) - { - ush temp; - - temp = (ush)keys[2] | 2; - temp = (int)(((unsigned)(temp * (temp ^ 1U)) >> 8) & 0xff); - UPDATE_KEYS_ZIP(*p ^= temp); - } - else - bf_crypt_decode(ptr, len); -} - -/* - * Initialize the encryption keys and the random header according to - * the given password. - * If "passwd" is NULL or empty, don't do anything. - */ - void -crypt_init_keys(passwd) - char_u *passwd; /* password string with which to modify keys */ -{ - if (passwd != NULL && *passwd != NUL) - { - if (use_crypt_method == 0) - { - char_u *p; - - make_crc_tab(); - keys[0] = 305419896L; - keys[1] = 591751049L; - keys[2] = 878082192L; - for (p = passwd; *p!= NUL; ++p) - { - UPDATE_KEYS_ZIP((int)*p); - } - } - else - bf_crypt_init_keys(passwd); - } -} - -/* - * Free an allocated crypt key. Clear the text to make sure it doesn't stay - * in memory anywhere. - */ - void -free_crypt_key(key) - char_u *key; -{ - char_u *p; - - if (key != NULL) - { - for (p = key; *p != NUL; ++p) - *p = 0; - vim_free(key); - } -} - -/* - * Ask the user for a crypt key. - * When "store" is TRUE, the new key is stored in the 'key' option, and the - * 'key' option value is returned: Don't free it. - * When "store" is FALSE, the typed key is returned in allocated memory. - * Returns NULL on failure. - */ - char_u * -get_crypt_key(store, twice) - int store; - int twice; /* Ask for the key twice. */ -{ - char_u *p1, *p2 = NULL; - int round; - - for (round = 0; ; ++round) - { - cmdline_star = TRUE; - cmdline_row = msg_row; - p1 = getcmdline_prompt(NUL, round == 0 - ? (char_u *)_("Enter encryption key: ") - : (char_u *)_("Enter same key again: "), 0, EXPAND_NOTHING, - NULL); - cmdline_star = FALSE; - - if (p1 == NULL) - break; - - if (round == twice) - { - if (p2 != NULL && STRCMP(p1, p2) != 0) - { - MSG(_("Keys don't match!")); - free_crypt_key(p1); - free_crypt_key(p2); - p2 = NULL; - round = -1; /* do it again */ - continue; - } - - if (store) - { - set_option_value((char_u *)"key", 0L, p1, OPT_LOCAL); - free_crypt_key(p1); - p1 = curbuf->b_p_key; - } - break; - } - p2 = p1; - } - - /* since the user typed this, no need to wait for return */ - if (msg_didout) - msg_putchar('\n'); - need_wait_return = FALSE; - msg_didout = FALSE; - - free_crypt_key(p2); - return p1; -} - -#endif /* FEAT_CRYPT */ - /* TODO: make some #ifdef for this */ /*--------[ file searching ]-------------------------------------------------*/ /* @@ -6588,8 +6272,23 @@ put_time(fd, the_time) FILE *fd; time_t the_time; { + char_u buf[8]; + + time_to_bytes(the_time, buf); + fwrite(buf, (size_t)8, (size_t)1, fd); +} + +/* + * Write time_t to "buf[8]". + */ + void +time_to_bytes(the_time, buf) + time_t the_time; + char_u *buf; +{ int c; int i; + int bi = 0; time_t wtime = the_time; /* time_t can be up to 8 bytes in size, more than long_u, thus we @@ -6603,7 +6302,7 @@ put_time(fd, the_time) { if (i + 1 > (int)sizeof(time_t)) /* ">>" doesn't work well when shifting more bits than avail */ - putc(0, fd); + buf[bi++] = 0; else { #if defined(SIZEOF_TIME_T) && SIZEOF_TIME_T > 4 @@ -6611,7 +6310,7 @@ put_time(fd, the_time) #else c = (int)((long_u)wtime >> (i * 8)); #endif - putc(c, fd); + buf[bi++] = c; } } } diff --git a/src/option.c b/src/option.c index 3e6164e5a..62556b69c 100644 --- a/src/option.c +++ b/src/option.c @@ -2989,7 +2989,7 @@ static char *(p_bg_values[]) = {"light", "dark", NULL}; static char *(p_nf_values[]) = {"octal", "hex", "alpha", NULL}; static char *(p_ff_values[]) = {FF_UNIX, FF_DOS, FF_MAC, NULL}; #ifdef FEAT_CRYPT -static char *(p_cm_values[]) = {"zip", "blowfish", NULL}; +static char *(p_cm_values[]) = {"zip", "blowfish", "blowfish2", NULL}; #endif #ifdef FEAT_CMDL_COMPL static char *(p_wop_values[]) = {"tagfile", NULL}; @@ -6140,7 +6140,7 @@ did_set_string_option(opt_idx, varp, new_value_alloced, oldval, errbuf, # endif if (STRCMP(curbuf->b_p_key, oldval) != 0) /* Need to update the swapfile. */ - ml_set_crypt_key(curbuf, oldval, get_crypt_method(curbuf)); + ml_set_crypt_key(curbuf, oldval, crypt_get_method_nr(curbuf)); } else if (gvarp == &p_cm) @@ -6151,7 +6151,7 @@ did_set_string_option(opt_idx, varp, new_value_alloced, oldval, errbuf, p = p_cm; if (check_opt_strings(p, p_cm_values, TRUE) != OK) errmsg = e_invarg; - else if (get_crypt_method(curbuf) > 0 && blowfish_self_test() == FAIL) + else if (crypt_self_test() == FAIL) errmsg = e_invarg; else { @@ -6177,7 +6177,7 @@ did_set_string_option(opt_idx, varp, new_value_alloced, oldval, errbuf, p = curbuf->b_p_cm; if (STRCMP(s, p) != 0) ml_set_crypt_key(curbuf, curbuf->b_p_key, - crypt_method_from_string(s)); + crypt_method_nr_from_name(s)); /* If the global value changes need to update the swapfile for all * buffers using that value. */ @@ -6188,7 +6188,7 @@ did_set_string_option(opt_idx, varp, new_value_alloced, oldval, errbuf, for (buf = firstbuf; buf != NULL; buf = buf->b_next) if (buf != curbuf && *buf->b_p_cm == NUL) ml_set_crypt_key(buf, buf->b_p_key, - crypt_method_from_string(oldval)); + crypt_method_nr_from_name(oldval)); } } } diff --git a/src/proto.h b/src/proto.h index 191ecd8fd..2b08eb38f 100644 --- a/src/proto.h +++ b/src/proto.h @@ -70,6 +70,8 @@ extern int _stricoll __ARGS((char *a, char *b)); # ifdef FEAT_CRYPT # include "blowfish.pro" +# include "crypt.pro" +# include "crypt_zip.pro" # endif # include "buffer.pro" # include "charset.pro" diff --git a/src/proto/blowfish.pro b/src/proto/blowfish.pro index 4d64e10e8..66d029cd8 100644 --- a/src/proto/blowfish.pro +++ b/src/proto/blowfish.pro @@ -1,10 +1,6 @@ /* blowfish.c */ -void bf_key_init __ARGS((char_u *password, char_u *salt, int salt_len)); -void bf_cfb_init __ARGS((char_u *iv, int iv_len)); -void bf_crypt_encode __ARGS((char_u *from, size_t len, char_u *to)); -void bf_crypt_decode __ARGS((char_u *ptr, long len)); -void bf_crypt_init_keys __ARGS((char_u *passwd)); -void bf_crypt_save __ARGS((void)); -void bf_crypt_restore __ARGS((void)); +void crypt_blowfish_encode __ARGS((cryptstate_T *state, char_u *from, size_t len, char_u *to)); +void crypt_blowfish_decode __ARGS((cryptstate_T *state, char_u *from, size_t len, char_u *to)); +void crypt_blowfish_init __ARGS((cryptstate_T *state, char_u *key, char_u *salt, int salt_len, char_u *seed, int seed_len)); int blowfish_self_test __ARGS((void)); /* vim: set ft=c : */ diff --git a/src/proto/crypt.pro b/src/proto/crypt.pro new file mode 100644 index 000000000..d61df718b --- /dev/null +++ b/src/proto/crypt.pro @@ -0,0 +1,24 @@ +/* crypt.c */ +int crypt_method_nr_from_name __ARGS((char_u *name)); +int crypt_method_nr_from_magic __ARGS((char *ptr, int len)); +int crypt_works_inplace __ARGS((cryptstate_T *state)); +int crypt_get_method_nr __ARGS((buf_T *buf)); +int crypt_whole_undofile __ARGS((int method_nr)); +int crypt_get_header_len __ARGS((int method_nr)); +void crypt_set_cm_option __ARGS((buf_T *buf, int method_nr)); +int crypt_self_test __ARGS((void)); +cryptstate_T *crypt_create __ARGS((int method_nr, char_u *key, char_u *salt, int salt_len, char_u *seed, int seed_len)); +cryptstate_T *crypt_create_from_header __ARGS((int method_nr, char_u *key, char_u *header)); +cryptstate_T *crypt_create_from_file __ARGS((FILE *fp, char_u *key)); +cryptstate_T *crypt_create_for_writing __ARGS((int method_nr, char_u *key, char_u **header, int *header_len)); +void crypt_free_state __ARGS((cryptstate_T *state)); +long crypt_encode_alloc __ARGS((cryptstate_T *state, char_u *from, size_t len, char_u **newptr)); +long crypt_decode_alloc __ARGS((cryptstate_T *state, char_u *ptr, long len, char_u **newptr)); +void crypt_encode __ARGS((cryptstate_T *state, char_u *from, size_t len, char_u *to)); +void crypt_decode __ARGS((cryptstate_T *state, char_u *from, size_t len, char_u *to)); +void crypt_encode_inplace __ARGS((cryptstate_T *state, char_u *buf, size_t len)); +void crypt_decode_inplace __ARGS((cryptstate_T *state, char_u *buf, size_t len)); +void crypt_free_key __ARGS((char_u *key)); +char_u *crypt_get_key __ARGS((int store, int twice)); +void crypt_append_msg __ARGS((buf_T *buf)); +/* vim: set ft=c : */ diff --git a/src/proto/crypt_zip.pro b/src/proto/crypt_zip.pro new file mode 100644 index 000000000..5f4e13771 --- /dev/null +++ b/src/proto/crypt_zip.pro @@ -0,0 +1,5 @@ +/* crypt_zip.c */ +void crypt_zip_init __ARGS((cryptstate_T *state, char_u *key, char_u *salt, int salt_len, char_u *seed, int seed_len)); +void crypt_zip_encode __ARGS((cryptstate_T *state, char_u *from, size_t len, char_u *to)); +void crypt_zip_decode __ARGS((cryptstate_T *state, char_u *from, size_t len, char_u *to)); +/* vim: set ft=c : */ diff --git a/src/proto/fileio.pro b/src/proto/fileio.pro index 7aa99cfa8..0d532f4ce 100644 --- a/src/proto/fileio.pro +++ b/src/proto/fileio.pro @@ -4,8 +4,6 @@ int readfile __ARGS((char_u *fname, char_u *sfname, linenr_T from, linenr_T line int prep_exarg __ARGS((exarg_T *eap, buf_T *buf)); void set_file_options __ARGS((int set_options, exarg_T *eap)); void set_forced_fenc __ARGS((exarg_T *eap)); -int prepare_crypt_read __ARGS((FILE *fp)); -char_u *prepare_crypt_write __ARGS((buf_T *buf, int *lenp)); int check_file_readonly __ARGS((char_u *fname, int perm)); int buf_write __ARGS((buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_T end, exarg_T *eap, int append, int forceit, int reset_changed, int filtering)); void msg_add_fname __ARGS((buf_T *buf, char_u *fname)); diff --git a/src/proto/misc2.pro b/src/proto/misc2.pro index 4fd457382..b2f72d864 100644 --- a/src/proto/misc2.pro +++ b/src/proto/misc2.pro @@ -84,16 +84,6 @@ int illegal_slash __ARGS((char *name)); char_u *parse_shape_opt __ARGS((int what)); int get_shape_idx __ARGS((int mouse)); void update_mouseshape __ARGS((int shape_idx)); -int crypt_method_from_string __ARGS((char_u *s)); -int get_crypt_method __ARGS((buf_T *buf)); -void set_crypt_method __ARGS((buf_T *buf, int method)); -void crypt_push_state __ARGS((void)); -void crypt_pop_state __ARGS((void)); -void crypt_encode __ARGS((char_u *from, size_t len, char_u *to)); -void crypt_decode __ARGS((char_u *ptr, long len)); -void crypt_init_keys __ARGS((char_u *passwd)); -void free_crypt_key __ARGS((char_u *key)); -char_u *get_crypt_key __ARGS((int store, int twice)); void *vim_findfile_init __ARGS((char_u *path, char_u *filename, char_u *stopdirs, int level, int free_visited, int find_what, void *search_ctx_arg, int tagfile, char_u *rel_fname)); char_u *vim_findfile_stopdir __ARGS((char_u *buf)); void vim_findfile_cleanup __ARGS((void *ctx)); @@ -116,5 +106,6 @@ time_t get8ctime __ARGS((FILE *fd)); char_u *read_string __ARGS((FILE *fd, int cnt)); int put_bytes __ARGS((FILE *fd, long_u nr, int len)); void put_time __ARGS((FILE *fd, time_t the_time)); +void time_to_bytes __ARGS((time_t the_time, char_u *buf)); int has_non_ascii __ARGS((char_u *s)); /* vim: set ft=c : */ diff --git a/src/structs.h b/src/structs.h index 138238055..441fcaa1c 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1251,6 +1251,24 @@ typedef struct { } syn_time_T; #endif +#ifdef FEAT_CRYPT +/* + * Structure to hold the type of encryption and the state of encryption or + * decryption. + */ +typedef struct { + int method_nr; + void *method_state; /* method-specific state information */ +} cryptstate_T; + +/* values for method_nr */ +# define CRYPT_M_ZIP 0 +# define CRYPT_M_BF 1 +# define CRYPT_M_BF2 2 +# define CRYPT_M_COUNT 3 /* number of crypt methods */ +#endif + + /* * These are items normally related to a buffer. But when using ":ownsyntax" * a window may have its own instance. @@ -1778,7 +1796,12 @@ struct file_buffer int b_was_netbeans_file;/* TRUE if b_netbeans_file was once set */ #endif -}; +#ifdef FEAT_CRYPT + cryptstate_T *b_cryptstate; /* Encryption state while reading or writing + * the file. NULL when not using encryption. */ +#endif + +}; /* file_buffer */ #ifdef FEAT_DIFF diff --git a/src/testdir/test71.in b/src/testdir/test71.in index 155fd413b..944b69dc5 100644 --- a/src/testdir/test71.in +++ b/src/testdir/test71.in @@ -13,6 +13,8 @@ STARTTEST :let cm0_bytes = getline('.', '.') :/^start of cm=blowfish bytes/+1 :let cm1_bytes = getline('.', '.') +:/^start of cm=blowfish2 bytes/+1 +:let cm2_bytes = getline('.', '.') :bwipe! :call append(0, text_lines) :$d @@ -36,6 +38,18 @@ barfoo :e Xtestfile barfoo :let cm1_read_back = getline('.', '$') +:set key= +:set cryptmethod=blowfish2 +:" If the blowfish test fails 'cryptmethod' will be 'zip' now. +:%s/^/\=&cryptmethod == 'blowfish2' ? "OK " : "blowfish test failed "/ +:X +bar2foo +bar2foo +:w! Xtestfile +:bwipe! +:e Xtestfile +bar2foo +:let cm2_read_back = getline('.', '$') :bwipe! :set bin noeol key= :call append(0, cm0_bytes) @@ -57,7 +71,20 @@ foofoo :set nobin :e Xtestfile barbar +:let cm1_read_bin = getline('.', '$') +:bwipe! +:set bin noeol key= +:call append(0, cm2_bytes) +:$d +:set fenc=latin1 +:w! Xtestfile +:bwipe! +:set nobin +:e Xtestfile +barburp +:call append(0, cm1_read_bin) :call append(0, cm0_read_bin) +:call append(0, cm2_read_back) :call append(0, cm1_read_back) :call append(0, cm0_read_back) :set key= fenc=latin1 diff --git a/src/testdir/test71.ok b/src/testdir/test71.ok index 24652c438..de1b0ab83 100644 --- a/src/testdir/test71.ok +++ b/src/testdir/test71.ok @@ -4,7 +4,12 @@ line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx OK 01234567890123456789012345678901234567 OK line 2 foo bar blah OK line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +OK OK 01234567890123456789012345678901234567 +OK OK line 2 foo bar blah +OK OK line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 1234567890 abbccddeff asdfasdfasdf 0001112223333 +abcdefghijklmnopqrstuvwxyz +!@#$%^&*()_+=-`~ diff --git a/src/testdir/test71a.in b/src/testdir/test71a.in index 85bd22cd0..e79a39895 100644 --- a/src/testdir/test71a.in +++ b/src/testdir/test71a.in @@ -12,3 +12,7 @@ end of cm=zip bytes start of cm=blowfish bytes VimCrypt~02!k)#S=#MJAͥM!
end of cm=blowfish bytes + +start of cm=blowfish2 bytes +VimCrypt~03!N;^C).FS6[T˧ؾ92Q@ߚIv.`$% +end of cm=blowfish2 bytes diff --git a/src/testdir/test72.in b/src/testdir/test72.in index 3b3a6a432..031edbf14 100644 --- a/src/testdir/test72.in +++ b/src/testdir/test72.in @@ -81,6 +81,7 @@ uu:w >>test.out :" :" With encryption, cryptmethod=blowfish :e! Xtestfile +rubbish :set undofile cm=blowfish ggdGijan feb @@ -104,6 +105,32 @@ u:.w >>test.out u:.w >>test.out u:.w >>test.out :" +:" With encryption, cryptmethod=blowfish2 +:e! Xtestfile +rubbish +:set undofile cm=blowfish2 +ggdGijan +feb +mar +apr +jun:set ul=100 +kk0ifoo :set ul=100 +dd:set ul=100 +ibar :set ul=100 +:X +foo2bar +foo2bar +:w! +:bwipe! +:e Xtestfile +foo2bar +:set key= +/bar +:.w >>test.out +u:.w >>test.out +u:.w >>test.out +u:.w >>test.out +:" :" Rename the undo file so that it gets cleaned up. :if has("vms") : call rename("_un_Xtestfile", "Xtestundo") diff --git a/src/testdir/test72.ok b/src/testdir/test72.ok index bb267d0d8..8d30ba10b 100644 --- a/src/testdir/test72.ok +++ b/src/testdir/test72.ok @@ -25,3 +25,7 @@ bar apr apr foo mar mar +bar apr +apr +foo mar +mar diff --git a/src/undo.c b/src/undo.c index 5db25f04e..1661c8074 100644 --- a/src/undo.c +++ b/src/undo.c @@ -81,8 +81,25 @@ #define UH_MAGIC 0x18dade /* value for uh_magic when in use */ #define UE_MAGIC 0xabc123 /* value for ue_magic when in use */ +/* Size of buffer used for encryption. */ +#define CRYPT_BUF_SIZE 8192 + #include "vim.h" +/* Structure passed around between functions. + * Avoids passing cryptstate_T when encryption not available. */ +typedef struct { + buf_T *bi_buf; + FILE *bi_fp; +#ifdef FEAT_CRYPT + cryptstate_T *bi_state; + char_u *bi_buffer; /* CRYPT_BUF_SIZE, NULL when not buffering */ + size_t bi_used; /* bytes written to/read from bi_buffer */ + size_t bi_avail; /* bytes available in bi_buffer */ +#endif +} bufinfo_T; + + static long get_undolevel __ARGS((void)); static void u_unch_branch __ARGS((u_header_T *uhp)); static u_entry_T *u_get_headentry __ARGS((void)); @@ -98,18 +115,26 @@ static void u_freeentry __ARGS((u_entry_T *, long)); #ifdef FEAT_PERSISTENT_UNDO static void corruption_error __ARGS((char *mesg, char_u *file_name)); static void u_free_uhp __ARGS((u_header_T *uhp)); -static size_t fwrite_crypt __ARGS((buf_T *buf UNUSED, char_u *ptr, size_t len, FILE *fp)); -static char_u *read_string_decrypt __ARGS((buf_T *buf UNUSED, FILE *fd, int len)); -static int serialize_header __ARGS((FILE *fp, buf_T *buf, char_u *hash)); -static int serialize_uhp __ARGS((FILE *fp, buf_T *buf, u_header_T *uhp)); -static u_header_T *unserialize_uhp __ARGS((FILE *fp, char_u *file_name)); -static int serialize_uep __ARGS((FILE *fp, buf_T *buf, u_entry_T *uep)); -static u_entry_T *unserialize_uep __ARGS((FILE *fp, int *error, char_u *file_name)); -static void serialize_pos __ARGS((pos_T pos, FILE *fp)); -static void unserialize_pos __ARGS((pos_T *pos, FILE *fp)); -static void serialize_visualinfo __ARGS((visualinfo_T *info, FILE *fp)); -static void unserialize_visualinfo __ARGS((visualinfo_T *info, FILE *fp)); -static void put_header_ptr __ARGS((FILE *fp, u_header_T *uhp)); +static int undo_write __ARGS((bufinfo_T *bi, char_u *ptr, size_t len)); +static int undo_flush __ARGS((bufinfo_T *bi)); +static int fwrite_crypt __ARGS((bufinfo_T *bi, char_u *ptr, size_t len)); +static int undo_write_bytes __ARGS((bufinfo_T *bi, long_u nr, int len)); +static void put_header_ptr __ARGS((bufinfo_T *bi, u_header_T *uhp)); +static int undo_read_4c __ARGS((bufinfo_T *bi)); +static int undo_read_2c __ARGS((bufinfo_T *bi)); +static int undo_read_byte __ARGS((bufinfo_T *bi)); +static time_t undo_read_time __ARGS((bufinfo_T *bi)); +static int undo_read __ARGS((bufinfo_T *bi, char_u *buffer, size_t size)); +static char_u *read_string_decrypt __ARGS((bufinfo_T *bi, int len)); +static int serialize_header __ARGS((bufinfo_T *bi, char_u *hash)); +static int serialize_uhp __ARGS((bufinfo_T *bi, u_header_T *uhp)); +static u_header_T *unserialize_uhp __ARGS((bufinfo_T *bi, char_u *file_name)); +static int serialize_uep __ARGS((bufinfo_T *bi, u_entry_T *uep)); +static u_entry_T *unserialize_uep __ARGS((bufinfo_T *bi, int *error, char_u *file_name)); +static void serialize_pos __ARGS((bufinfo_T *bi, pos_T pos)); +static void unserialize_pos __ARGS((bufinfo_T *bi, pos_T *pos)); +static void serialize_visualinfo __ARGS((bufinfo_T *bi, visualinfo_T *info)); +static void unserialize_visualinfo __ARGS((bufinfo_T *bi, visualinfo_T *info)); #endif #define U_ALLOC_LINE(size) lalloc((long_u)(size), FALSE) @@ -859,68 +884,294 @@ u_free_uhp(uhp) } /* - * Like fwrite() but crypt the bytes when 'key' is set. - * Returns 1 if successful. + * Write a sequence of bytes to the undo file. + * Buffers and encrypts as needed. + * Returns OK or FAIL. */ - static size_t -fwrite_crypt(buf, ptr, len, fp) - buf_T *buf UNUSED; + static int +undo_write(bi, ptr, len) + bufinfo_T *bi; + char_u *ptr; + size_t len; +{ +#ifdef FEAT_CRYPT + if (bi->bi_buffer != NULL) + { + size_t len_todo = len; + char_u *p = ptr; + + while (bi->bi_used + len_todo >= CRYPT_BUF_SIZE) + { + size_t n = CRYPT_BUF_SIZE - bi->bi_used; + + mch_memmove(bi->bi_buffer + bi->bi_used, p, n); + len_todo -= n; + p += n; + bi->bi_used = CRYPT_BUF_SIZE; + if (undo_flush(bi) == FAIL) + return FAIL; + } + if (len_todo > 0) + { + mch_memmove(bi->bi_buffer + bi->bi_used, p, len_todo); + bi->bi_used += len_todo; + } + return OK; + } +#endif + if (fwrite(ptr, len, (size_t)1, bi->bi_fp) != 1) + return FAIL; + return OK; +} + +#ifdef FEAT_CRYPT + static int +undo_flush(bi) + bufinfo_T *bi; +{ + if (bi->bi_used > 0) + { + crypt_encode_inplace(bi->bi_state, bi->bi_buffer, bi->bi_used); + if (fwrite(bi->bi_buffer, bi->bi_used, (size_t)1, bi->bi_fp) != 1) + return FAIL; + bi->bi_used = 0; + } + return OK; +} +#endif + +/* + * Write "ptr[len]" and crypt the bytes when needed. + * Returns OK or FAIL. + */ + static int +fwrite_crypt(bi, ptr, len) + bufinfo_T *bi; char_u *ptr; size_t len; - FILE *fp; { #ifdef FEAT_CRYPT char_u *copy; char_u small_buf[100]; size_t i; - if (*buf->b_p_key == NUL) - return fwrite(ptr, len, (size_t)1, fp); - if (len < 100) - copy = small_buf; /* no malloc()/free() for short strings */ - else + if (bi->bi_state != NULL && bi->bi_buffer == NULL) { - copy = lalloc(len, FALSE); - if (copy == NULL) - return 0; + /* crypting every piece of text separately */ + if (len < 100) + copy = small_buf; /* no malloc()/free() for short strings */ + else + { + copy = lalloc(len, FALSE); + if (copy == NULL) + return 0; + } + crypt_encode(bi->bi_state, ptr, len, copy); + i = fwrite(copy, len, (size_t)1, bi->bi_fp); + if (copy != small_buf) + vim_free(copy); + return i == 1 ? OK : FAIL; } - crypt_encode(ptr, len, copy); - i = fwrite(copy, len, (size_t)1, fp); - if (copy != small_buf) - vim_free(copy); - return i; -#else - return fwrite(ptr, len, (size_t)1, fp); #endif + return undo_write(bi, ptr, len); } /* - * Read a string of length "len" from "fd". - * When 'key' is set decrypt the bytes. + * Write a number, MSB first, in "len" bytes. + * Must match with undo_read_?c() functions. + * Returns OK or FAIL. */ - static char_u * -read_string_decrypt(buf, fd, len) - buf_T *buf UNUSED; - FILE *fd; + static int +undo_write_bytes(bi, nr, len) + bufinfo_T *bi; + long_u nr; int len; { - char_u *ptr; + char_u buf[8]; + int i; + int bufi = 0; + + for (i = len - 1; i >= 0; --i) + buf[bufi++] = nr >> (i * 8); + return undo_write(bi, buf, (size_t)len); +} + +/* + * Write the pointer to an undo header. Instead of writing the pointer itself + * we use the sequence number of the header. This is converted back to + * pointers when reading. */ + static void +put_header_ptr(bi, uhp) + bufinfo_T *bi; + u_header_T *uhp; +{ + undo_write_bytes(bi, (long_u)(uhp != NULL ? uhp->uh_seq : 0), 4); +} + + static int +undo_read_4c(bi) + bufinfo_T *bi; +{ +#ifdef FEAT_CRYPT + if (bi->bi_buffer != NULL) + { + char_u buf[4]; + int n; + + undo_read(bi, buf, (size_t)4); + n = (buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3]; + return n; + } +#endif + return get4c(bi->bi_fp); +} + + static int +undo_read_2c(bi) + bufinfo_T *bi; +{ +#ifdef FEAT_CRYPT + if (bi->bi_buffer != NULL) + { + char_u buf[2]; + int n; + + undo_read(bi, buf, (size_t)2); + n = (buf[0] << 8) + buf[1]; + return n; + } +#endif + return get2c(bi->bi_fp); +} + + static int +undo_read_byte(bi) + bufinfo_T *bi; +{ +#ifdef FEAT_CRYPT + if (bi->bi_buffer != NULL) + { + char_u buf[1]; + + undo_read(bi, buf, (size_t)1); + return buf[0]; + } +#endif + return getc(bi->bi_fp); +} + + static time_t +undo_read_time(bi) + bufinfo_T *bi; +{ +#ifdef FEAT_CRYPT + if (bi->bi_buffer != NULL) + { + char_u buf[8]; + time_t n = 0; + int i; + + undo_read(bi, buf, (size_t)8); + for (i = 0; i < 8; ++i) + n = (n << 8) + buf[i]; + return n; + } +#endif + return get8ctime(bi->bi_fp); +} + +/* + * Read "buffer[size]" from the undo file. + * Return OK or FAIL. + */ + static int +undo_read(bi, buffer, size) + bufinfo_T *bi; + char_u *buffer; + size_t size; +{ +#ifdef FEAT_CRYPT + if (bi->bi_buffer != NULL) + { + int size_todo = size; + char_u *p = buffer; + + while (size_todo > 0) + { + size_t n; + + if (bi->bi_used >= bi->bi_avail) + { + n = fread(bi->bi_buffer, 1, (size_t)CRYPT_BUF_SIZE, bi->bi_fp); + if (n <= 0) + { + /* Error may be checked for only later. Fill with zeros, + * so that the reader won't use garbage. */ + vim_memset(p, 0, size_todo); + return FAIL; + } + bi->bi_avail = n; + bi->bi_used = 0; + crypt_decode_inplace(bi->bi_state, bi->bi_buffer, bi->bi_avail); + } + n = size_todo; + if (n > bi->bi_avail - bi->bi_used) + n = bi->bi_avail - bi->bi_used; + mch_memmove(p, bi->bi_buffer + bi->bi_used, n); + bi->bi_used += n; + size_todo -= n; + p += n; + } + return OK; + } +#endif + if (fread(buffer, (size_t)size, 1, bi->bi_fp) != 1) + return FAIL; + return OK; +} + +/* + * Read a string of length "len" from "bi->bi_fd". + * "len" can be zero to allocate an empty line. + * Decrypt the bytes if needed. + * Append a NUL. + * Returns a pointer to allocated memory or NULL for failure. + */ + static char_u * +read_string_decrypt(bi, len) + bufinfo_T *bi; + int len; +{ + char_u *ptr = alloc((unsigned)len + 1); - ptr = read_string(fd, len); + if (ptr != NULL) + { + if (len > 0 && undo_read(bi, ptr, len) == FAIL) + { + vim_free(ptr); + return NULL; + } + ptr[len] = NUL; #ifdef FEAT_CRYPT - if (ptr != NULL && *buf->b_p_key != NUL) - crypt_decode(ptr, len); + if (bi->bi_state != NULL && bi->bi_buffer == NULL) + crypt_decode_inplace(bi->bi_state, ptr, len); #endif + } return ptr; } +/* + * Writes the (not encrypted) header and initializes encryption if needed. + */ static int -serialize_header(fp, buf, hash) - FILE *fp; - buf_T *buf; +serialize_header(bi, hash) + bufinfo_T *bi; char_u *hash; { - int len; + int len; + buf_T *buf = bi->bi_buf; + FILE *fp = bi->bi_fp; + char_u time_buf[8]; /* Start writing, first the magic marker and undo info version. */ if (fwrite(UF_START_MAGIC, (size_t)UF_START_MAGIC_LEN, (size_t)1, fp) != 1) @@ -934,108 +1185,124 @@ serialize_header(fp, buf, hash) char_u *header; int header_len; - put_bytes(fp, (long_u)UF_VERSION_CRYPT, 2); - header = prepare_crypt_write(buf, &header_len); - if (header == NULL) + undo_write_bytes(bi, (long_u)UF_VERSION_CRYPT, 2); + bi->bi_state = crypt_create_for_writing(crypt_get_method_nr(buf), + buf->b_p_key, &header, &header_len); + if (bi->bi_state == NULL) return FAIL; len = (int)fwrite(header, (size_t)header_len, (size_t)1, fp); vim_free(header); if (len != 1) { - crypt_pop_state(); + crypt_free_state(bi->bi_state); + bi->bi_state = NULL; return FAIL; } + + if (crypt_whole_undofile(crypt_get_method_nr(buf))) + { + bi->bi_buffer = alloc(CRYPT_BUF_SIZE); + if (bi->bi_buffer == NULL) + { + crypt_free_state(bi->bi_state); + bi->bi_state = NULL; + return FAIL; + } + bi->bi_used = 0; + } } else #endif - put_bytes(fp, (long_u)UF_VERSION, 2); + undo_write_bytes(bi, (long_u)UF_VERSION, 2); /* Write a hash of the buffer text, so that we can verify it is still the * same when reading the buffer text. */ - if (fwrite(hash, (size_t)UNDO_HASH_SIZE, (size_t)1, fp) != 1) + if (undo_write(bi, hash, (size_t)UNDO_HASH_SIZE) == FAIL) return FAIL; /* buffer-specific data */ - put_bytes(fp, (long_u)buf->b_ml.ml_line_count, 4); + undo_write_bytes(bi, (long_u)buf->b_ml.ml_line_count, 4); len = buf->b_u_line_ptr != NULL ? (int)STRLEN(buf->b_u_line_ptr) : 0; - put_bytes(fp, (long_u)len, 4); - if (len > 0 && fwrite_crypt(buf, buf->b_u_line_ptr, (size_t)len, fp) != 1) + undo_write_bytes(bi, (long_u)len, 4); + if (len > 0 && fwrite_crypt(bi, buf->b_u_line_ptr, (size_t)len) == FAIL) return FAIL; - put_bytes(fp, (long_u)buf->b_u_line_lnum, 4); - put_bytes(fp, (long_u)buf->b_u_line_colnr, 4); + undo_write_bytes(bi, (long_u)buf->b_u_line_lnum, 4); + undo_write_bytes(bi, (long_u)buf->b_u_line_colnr, 4); /* Undo structures header data */ - put_header_ptr(fp, buf->b_u_oldhead); - put_header_ptr(fp, buf->b_u_newhead); - put_header_ptr(fp, buf->b_u_curhead); + put_header_ptr(bi, buf->b_u_oldhead); + put_header_ptr(bi, buf->b_u_newhead); + put_header_ptr(bi, buf->b_u_curhead); - put_bytes(fp, (long_u)buf->b_u_numhead, 4); - put_bytes(fp, (long_u)buf->b_u_seq_last, 4); - put_bytes(fp, (long_u)buf->b_u_seq_cur, 4); - put_time(fp, buf->b_u_time_cur); + undo_write_bytes(bi, (long_u)buf->b_u_numhead, 4); + undo_write_bytes(bi, (long_u)buf->b_u_seq_last, 4); + undo_write_bytes(bi, (long_u)buf->b_u_seq_cur, 4); + time_to_bytes(buf->b_u_time_cur, time_buf); + undo_write(bi, time_buf, 8); /* Optional fields. */ - putc(4, fp); - putc(UF_LAST_SAVE_NR, fp); - put_bytes(fp, (long_u)buf->b_u_save_nr_last, 4); + undo_write_bytes(bi, 4, 1); + undo_write_bytes(bi, UF_LAST_SAVE_NR, 1); + undo_write_bytes(bi, (long_u)buf->b_u_save_nr_last, 4); - putc(0, fp); /* end marker */ + undo_write_bytes(bi, 0, 1); /* end marker */ return OK; } static int -serialize_uhp(fp, buf, uhp) - FILE *fp; - buf_T *buf; +serialize_uhp(bi, uhp) + bufinfo_T *bi; u_header_T *uhp; { int i; u_entry_T *uep; + char_u time_buf[8]; - if (put_bytes(fp, (long_u)UF_HEADER_MAGIC, 2) == FAIL) + if (undo_write_bytes(bi, (long_u)UF_HEADER_MAGIC, 2) == FAIL) return FAIL; - put_header_ptr(fp, uhp->uh_next.ptr); - put_header_ptr(fp, uhp->uh_prev.ptr); - put_header_ptr(fp, uhp->uh_alt_next.ptr); - put_header_ptr(fp, uhp->uh_alt_prev.ptr); - put_bytes(fp, uhp->uh_seq, 4); - serialize_pos(uhp->uh_cursor, fp); + put_header_ptr(bi, uhp->uh_next.ptr); + put_header_ptr(bi, uhp->uh_prev.ptr); + put_header_ptr(bi, uhp->uh_alt_next.ptr); + put_header_ptr(bi, uhp->uh_alt_prev.ptr); + undo_write_bytes(bi, uhp->uh_seq, 4); + serialize_pos(bi, uhp->uh_cursor); #ifdef FEAT_VIRTUALEDIT - put_bytes(fp, (long_u)uhp->uh_cursor_vcol, 4); + undo_write_bytes(bi, (long_u)uhp->uh_cursor_vcol, 4); #else - put_bytes(fp, (long_u)0, 4); + undo_write_bytes(bi, (long_u)0, 4); #endif - put_bytes(fp, (long_u)uhp->uh_flags, 2); + undo_write_bytes(bi, (long_u)uhp->uh_flags, 2); /* Assume NMARKS will stay the same. */ for (i = 0; i < NMARKS; ++i) - serialize_pos(uhp->uh_namedm[i], fp); - serialize_visualinfo(&uhp->uh_visual, fp); - put_time(fp, uhp->uh_time); + serialize_pos(bi, uhp->uh_namedm[i]); + serialize_visualinfo(bi, &uhp->uh_visual); + time_to_bytes(uhp->uh_time, time_buf); + undo_write(bi, time_buf, 8); /* Optional fields. */ - putc(4, fp); - putc(UHP_SAVE_NR, fp); - put_bytes(fp, (long_u)uhp->uh_save_nr, 4); + undo_write_bytes(bi, 4, 1); + undo_write_bytes(bi, UHP_SAVE_NR, 1); + undo_write_bytes(bi, (long_u)uhp->uh_save_nr, 4); - putc(0, fp); /* end marker */ + undo_write_bytes(bi, 0, 1); /* end marker */ /* Write all the entries. */ for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next) { - put_bytes(fp, (long_u)UF_ENTRY_MAGIC, 2); - if (serialize_uep(fp, buf, uep) == FAIL) + undo_write_bytes(bi, (long_u)UF_ENTRY_MAGIC, 2); + if (serialize_uep(bi, uep) == FAIL) return FAIL; } - put_bytes(fp, (long_u)UF_ENTRY_END_MAGIC, 2); + undo_write_bytes(bi, (long_u)UF_ENTRY_END_MAGIC, 2); return OK; } static u_header_T * -unserialize_uhp(fp, file_name) - FILE *fp; +unserialize_uhp(bi, file_name) + bufinfo_T *bi; char_u *file_name; { u_header_T *uhp; @@ -1051,56 +1318,56 @@ unserialize_uhp(fp, file_name) #ifdef U_DEBUG uhp->uh_magic = UH_MAGIC; #endif - uhp->uh_next.seq = get4c(fp); - uhp->uh_prev.seq = get4c(fp); - uhp->uh_alt_next.seq = get4c(fp); - uhp->uh_alt_prev.seq = get4c(fp); - uhp->uh_seq = get4c(fp); + uhp->uh_next.seq = undo_read_4c(bi); + uhp->uh_prev.seq = undo_read_4c(bi); + uhp->uh_alt_next.seq = undo_read_4c(bi); + uhp->uh_alt_prev.seq = undo_read_4c(bi); + uhp->uh_seq = undo_read_4c(bi); if (uhp->uh_seq <= 0) { corruption_error("uh_seq", file_name); vim_free(uhp); return NULL; } - unserialize_pos(&uhp->uh_cursor, fp); + unserialize_pos(bi, &uhp->uh_cursor); #ifdef FEAT_VIRTUALEDIT - uhp->uh_cursor_vcol = get4c(fp); + uhp->uh_cursor_vcol = undo_read_4c(bi); #else - (void)get4c(fp); + (void)undo_read_4c(bi); #endif - uhp->uh_flags = get2c(fp); + uhp->uh_flags = undo_read_2c(bi); for (i = 0; i < NMARKS; ++i) - unserialize_pos(&uhp->uh_namedm[i], fp); - unserialize_visualinfo(&uhp->uh_visual, fp); - uhp->uh_time = get8ctime(fp); + unserialize_pos(bi, &uhp->uh_namedm[i]); + unserialize_visualinfo(bi, &uhp->uh_visual); + uhp->uh_time = undo_read_time(bi); /* Optional fields. */ for (;;) { - int len = getc(fp); + int len = undo_read_byte(bi); int what; if (len == 0) break; - what = getc(fp); + what = undo_read_byte(bi); switch (what) { case UHP_SAVE_NR: - uhp->uh_save_nr = get4c(fp); + uhp->uh_save_nr = undo_read_4c(bi); break; default: /* field not supported, skip */ while (--len >= 0) - (void)getc(fp); + (void)undo_read_byte(bi); } } /* Unserialize the uep list. */ last_uep = NULL; - while ((c = get2c(fp)) == UF_ENTRY_MAGIC) + while ((c = undo_read_2c(bi)) == UF_ENTRY_MAGIC) { error = FALSE; - uep = unserialize_uep(fp, &error, file_name); + uep = unserialize_uep(bi, &error, file_name); if (last_uep == NULL) uhp->uh_entry = uep; else @@ -1123,35 +1390,34 @@ unserialize_uhp(fp, file_name) } /* - * Serialize "uep" to "fp". + * Serialize "uep". */ static int -serialize_uep(fp, buf, uep) - FILE *fp; - buf_T *buf; +serialize_uep(bi, uep) + bufinfo_T *bi; u_entry_T *uep; { int i; size_t len; - put_bytes(fp, (long_u)uep->ue_top, 4); - put_bytes(fp, (long_u)uep->ue_bot, 4); - put_bytes(fp, (long_u)uep->ue_lcount, 4); - put_bytes(fp, (long_u)uep->ue_size, 4); + undo_write_bytes(bi, (long_u)uep->ue_top, 4); + undo_write_bytes(bi, (long_u)uep->ue_bot, 4); + undo_write_bytes(bi, (long_u)uep->ue_lcount, 4); + undo_write_bytes(bi, (long_u)uep->ue_size, 4); for (i = 0; i < uep->ue_size; ++i) { len = STRLEN(uep->ue_array[i]); - if (put_bytes(fp, (long_u)len, 4) == FAIL) + if (undo_write_bytes(bi, (long_u)len, 4) == FAIL) return FAIL; - if (len > 0 && fwrite_crypt(buf, uep->ue_array[i], len, fp) != 1) + if (len > 0 && fwrite_crypt(bi, uep->ue_array[i], len) == FAIL) return FAIL; } return OK; } static u_entry_T * -unserialize_uep(fp, error, file_name) - FILE *fp; +unserialize_uep(bi, error, file_name) + bufinfo_T *bi; int *error; char_u *file_name; { @@ -1168,10 +1434,10 @@ unserialize_uep(fp, error, file_name) #ifdef U_DEBUG uep->ue_magic = UE_MAGIC; #endif - uep->ue_top = get4c(fp); - uep->ue_bot = get4c(fp); - uep->ue_lcount = get4c(fp); - uep->ue_size = get4c(fp); + uep->ue_top = undo_read_4c(bi); + uep->ue_bot = undo_read_4c(bi); + uep->ue_lcount = undo_read_4c(bi); + uep->ue_size = undo_read_4c(bi); if (uep->ue_size > 0) { array = (char_u **)U_ALLOC_LINE(sizeof(char_u *) * uep->ue_size); @@ -1188,9 +1454,9 @@ unserialize_uep(fp, error, file_name) for (i = 0; i < uep->ue_size; ++i) { - line_len = get4c(fp); + line_len = undo_read_4c(bi); if (line_len >= 0) - line = read_string_decrypt(curbuf, fp, line_len); + line = read_string_decrypt(bi, line_len); else { line = NULL; @@ -1207,83 +1473,71 @@ unserialize_uep(fp, error, file_name) } /* - * Serialize "pos" to "fp". + * Serialize "pos". */ static void -serialize_pos(pos, fp) +serialize_pos(bi, pos) + bufinfo_T *bi; pos_T pos; - FILE *fp; { - put_bytes(fp, (long_u)pos.lnum, 4); - put_bytes(fp, (long_u)pos.col, 4); + undo_write_bytes(bi, (long_u)pos.lnum, 4); + undo_write_bytes(bi, (long_u)pos.col, 4); #ifdef FEAT_VIRTUALEDIT - put_bytes(fp, (long_u)pos.coladd, 4); + undo_write_bytes(bi, (long_u)pos.coladd, 4); #else - put_bytes(fp, (long_u)0, 4); + undo_write_bytes(bi, (long_u)0, 4); #endif } /* - * Unserialize the pos_T at the current position in fp. + * Unserialize the pos_T at the current position. */ static void -unserialize_pos(pos, fp) +unserialize_pos(bi, pos) + bufinfo_T *bi; pos_T *pos; - FILE *fp; { - pos->lnum = get4c(fp); + pos->lnum = undo_read_4c(bi); if (pos->lnum < 0) pos->lnum = 0; - pos->col = get4c(fp); + pos->col = undo_read_4c(bi); if (pos->col < 0) pos->col = 0; #ifdef FEAT_VIRTUALEDIT - pos->coladd = get4c(fp); + pos->coladd = undo_read_4c(bi); if (pos->coladd < 0) pos->coladd = 0; #else - (void)get4c(fp); + (void)undo_read_4c(bi); #endif } /* - * Serialize "info" to "fp". + * Serialize "info". */ static void -serialize_visualinfo(info, fp) +serialize_visualinfo(bi, info) + bufinfo_T *bi; visualinfo_T *info; - FILE *fp; { - serialize_pos(info->vi_start, fp); - serialize_pos(info->vi_end, fp); - put_bytes(fp, (long_u)info->vi_mode, 4); - put_bytes(fp, (long_u)info->vi_curswant, 4); + serialize_pos(bi, info->vi_start); + serialize_pos(bi, info->vi_end); + undo_write_bytes(bi, (long_u)info->vi_mode, 4); + undo_write_bytes(bi, (long_u)info->vi_curswant, 4); } /* - * Unserialize the visualinfo_T at the current position in fp. + * Unserialize the visualinfo_T at the current position. */ static void -unserialize_visualinfo(info, fp) +unserialize_visualinfo(bi, info) + bufinfo_T *bi; visualinfo_T *info; - FILE *fp; -{ - unserialize_pos(&info->vi_start, fp); - unserialize_pos(&info->vi_end, fp); - info->vi_mode = get4c(fp); - info->vi_curswant = get4c(fp); -} - -/* - * Write the pointer to an undo header. Instead of writing the pointer itself - * we use the sequence number of the header. This is converted back to - * pointers when reading. */ - static void -put_header_ptr(fp, uhp) - FILE *fp; - u_header_T *uhp; { - put_bytes(fp, (long_u)(uhp != NULL ? uhp->uh_seq : 0), 4); + unserialize_pos(bi, &info->vi_start); + unserialize_pos(bi, &info->vi_end); + info->vi_mode = undo_read_4c(bi); + info->vi_curswant = undo_read_4c(bi); } /* @@ -1317,8 +1571,11 @@ u_write_undo(name, forceit, buf, hash) struct stat st_old; struct stat st_new; #endif + bufinfo_T bi; + #ifdef FEAT_CRYPT - int do_crypt = FALSE; + bi.bi_state = NULL; + bi.bi_buffer = NULL; #endif if (name == NULL) @@ -1474,14 +1731,12 @@ u_write_undo(name, forceit, buf, hash) u_sync(TRUE); /* - * Write the header. + * Write the header. Initializes encryption, if enabled. */ - if (serialize_header(fp, buf, hash) == FAIL) + bi.bi_buf = buf; + bi.bi_fp = fp; + if (serialize_header(&bi, hash) == FAIL) goto write_error; -#ifdef FEAT_CRYPT - if (*buf->b_p_key != NUL) - do_crypt = TRUE; -#endif /* * Iteratively serialize UHPs and their UEPs from the top down. @@ -1497,7 +1752,7 @@ u_write_undo(name, forceit, buf, hash) #ifdef U_DEBUG ++headers_written; #endif - if (serialize_uhp(fp, buf, uhp) == FAIL) + if (serialize_uhp(&bi, uhp) == FAIL) goto write_error; } @@ -1516,7 +1771,7 @@ u_write_undo(name, forceit, buf, hash) uhp = uhp->uh_next.ptr; } - if (put_bytes(fp, (long_u)UF_HEADER_END_MAGIC, 2) == OK) + if (undo_write_bytes(&bi, (long_u)UF_HEADER_END_MAGIC, 2) == OK) write_ok = TRUE; #ifdef U_DEBUG if (headers_written != buf->b_u_numhead) @@ -1526,6 +1781,11 @@ u_write_undo(name, forceit, buf, hash) } #endif +#ifdef FEAT_CRYPT + if (bi.bi_state != NULL && undo_flush(&bi) == FAIL) + write_ok = FALSE; +#endif + write_error: fclose(fp); if (!write_ok) @@ -1551,8 +1811,9 @@ write_error: theend: #ifdef FEAT_CRYPT - if (do_crypt) - crypt_pop_state(); + if (bi.bi_state != NULL) + crypt_free_state(bi.bi_state); + vim_free(bi.bi_buffer); #endif if (file_name != name) vim_free(file_name); @@ -1598,9 +1859,7 @@ u_read_undo(name, hash, orig_name) struct stat st_orig; struct stat st_undo; #endif -#ifdef FEAT_CRYPT - int do_decrypt = FALSE; -#endif + bufinfo_T bi; if (name == NULL) { @@ -1644,6 +1903,12 @@ u_read_undo(name, hash, orig_name) EMSG2(_("E822: Cannot open undo file for reading: %s"), file_name); goto error; } + bi.bi_buf = curbuf; + bi.bi_fp = fp; +#ifdef FEAT_CRYPT + bi.bi_state = NULL; + bi.bi_buffer = NULL; +#endif /* * Read the undo file header. @@ -1664,12 +1929,24 @@ u_read_undo(name, hash, orig_name) file_name); goto error; } - if (prepare_crypt_read(fp) == FAIL) + bi.bi_state = crypt_create_from_file(fp, curbuf->b_p_key); + if (bi.bi_state == NULL) { EMSG2(_("E826: Undo file decryption failed: %s"), file_name); goto error; } - do_decrypt = TRUE; + if (crypt_whole_undofile(bi.bi_state->method_nr)) + { + bi.bi_buffer = alloc(CRYPT_BUF_SIZE); + if (bi.bi_buffer == NULL) + { + crypt_free_state(bi.bi_state); + bi.bi_state = NULL; + goto error; + } + bi.bi_avail = 0; + bi.bi_used = 0; + } #else EMSG2(_("E827: Undo file is encrypted: %s"), file_name); goto error; @@ -1681,12 +1958,12 @@ u_read_undo(name, hash, orig_name) goto error; } - if (fread(read_hash, UNDO_HASH_SIZE, 1, fp) != 1) + if (undo_read(&bi, read_hash, (size_t)UNDO_HASH_SIZE) == FAIL) { corruption_error("hash", file_name); goto error; } - line_count = (linenr_T)get4c(fp); + line_count = (linenr_T)undo_read_4c(&bi); if (memcmp(hash, read_hash, UNDO_HASH_SIZE) != 0 || line_count != curbuf->b_ml.ml_line_count) { @@ -1703,13 +1980,13 @@ u_read_undo(name, hash, orig_name) } /* Read undo data for "U" command. */ - str_len = get4c(fp); + str_len = undo_read_4c(&bi); if (str_len < 0) goto error; if (str_len > 0) - line_ptr = read_string_decrypt(curbuf, fp, str_len); - line_lnum = (linenr_T)get4c(fp); - line_colnr = (colnr_T)get4c(fp); + line_ptr = read_string_decrypt(&bi, str_len); + line_lnum = (linenr_T)undo_read_4c(&bi); + line_colnr = (colnr_T)undo_read_4c(&bi); if (line_lnum < 0 || line_colnr < 0) { corruption_error("line lnum/col", file_name); @@ -1717,32 +1994,32 @@ u_read_undo(name, hash, orig_name) } /* Begin general undo data */ - old_header_seq = get4c(fp); - new_header_seq = get4c(fp); - cur_header_seq = get4c(fp); - num_head = get4c(fp); - seq_last = get4c(fp); - seq_cur = get4c(fp); - seq_time = get8ctime(fp); + old_header_seq = undo_read_4c(&bi); + new_header_seq = undo_read_4c(&bi); + cur_header_seq = undo_read_4c(&bi); + num_head = undo_read_4c(&bi); + seq_last = undo_read_4c(&bi); + seq_cur = undo_read_4c(&bi); + seq_time = undo_read_time(&bi); /* Optional header fields. */ for (;;) { - int len = getc(fp); + int len = undo_read_byte(&bi); int what; if (len == 0 || len == EOF) break; - what = getc(fp); + what = undo_read_byte(&bi); switch (what) { case UF_LAST_SAVE_NR: - last_save_nr = get4c(fp); + last_save_nr = undo_read_4c(&bi); break; default: /* field not supported, skip */ while (--len >= 0) - (void)getc(fp); + (void)undo_read_byte(&bi); } } @@ -1758,7 +2035,7 @@ u_read_undo(name, hash, orig_name) goto error; } - while ((c = get2c(fp)) == UF_HEADER_MAGIC) + while ((c = undo_read_2c(&bi)) == UF_HEADER_MAGIC) { if (num_read_uhps >= num_head) { @@ -1766,7 +2043,7 @@ u_read_undo(name, hash, orig_name) goto error; } - uhp = unserialize_uhp(fp, file_name); + uhp = unserialize_uhp(&bi, file_name); if (uhp == NULL) goto error; uhp_table[num_read_uhps++] = uhp; @@ -1898,8 +2175,9 @@ error: theend: #ifdef FEAT_CRYPT - if (do_decrypt) - crypt_pop_state(); + if (bi.bi_state != NULL) + crypt_free_state(bi.bi_state); + vim_free(bi.bi_buffer); #endif if (fp != NULL) fclose(fp); diff --git a/src/version.c b/src/version.c index 19183fdac..30bcb2e0f 100644 --- a/src/version.c +++ b/src/version.c @@ -742,6 +742,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 399, +/**/ 398, /**/ 397, |