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) |
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 1d20ec1af1..772b15c7d6 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1431,6 +1431,8 @@ BASIC_SRC = \ blowfish.c \ buffer.c \ charset.c \ + crypt.c \ + crypt_zip.c \ diff.c \ digraph.c \ edit.c \ @@ -1520,6 +1522,8 @@ OBJ_COMMON = \ objects/buffer.o \ objects/blowfish.o \ objects/charset.o \ + objects/crypt.o \ + objects/crypt_zip.o \ objects/diff.o \ objects/digraph.o \ objects/edit.o \ @@ -1589,6 +1593,8 @@ PRO_AUTO = \ blowfish.pro \ buffer.pro \ charset.pro \ + crypt.pro \ + crypt_zip.pro \ diff.pro \ digraph.pro \ edit.pro \ @@ -1753,10 +1759,11 @@ xxd/xxd$(EXEEXT): xxd/xxd.c languages: @if test -n "$(MAKEMO)" -a -f $(PODIR)/Makefile; then \ cd $(PODIR); \ - CC="$(CC)" $(MAKE) prefix=$(DESTDIR)$(prefix); \ + CC="$(CC)" $(MAKE) prefix=$(DESTDIR)$(prefix); \ fi -@if test -n "$(MAKEMO)" -a -f $(PODIR)/Makefile; then \ - cd $(PODIR); CC="$(CC)" $(MAKE) prefix=$(DESTDIR)$(prefix) converted; \ + cd $(PODIR); \ + CC="$(CC)" $(MAKE) prefix=$(DESTDIR)$(prefix) converted; \ fi # Update the *.po files for changes in the sources. Only run manually. @@ -1883,8 +1890,14 @@ unittest unittests: $(UNITTEST_TARGETS) # Run individual test, assuming that Vim was already compiled. test1 test2 test3 test4 test5 test6 test7 test8 test9 \ test_autoformat_join \ + test_breakindent \ + test_changelist \ test_eval \ + test_insertcount \ + test_listlbr \ + test_listlbr_utf8 \ test_options \ + test_qf_title \ test10 test11 test12 test13 test14 test15 test16 test17 test18 test19 \ test20 test21 test22 test23 test24 test25 test26 test27 test28 test29 \ test30 test31 test32 test33 test34 test35 test36 test37 test38 test39 \ @@ -2506,6 +2519,12 @@ objects/buffer.o: buffer.c objects/charset.o: charset.c $(CCC) -o $@ charset.c +objects/crypt.o: crypt.c + $(CCC) -o $@ crypt.c + +objects/crypt_zip.o: crypt_zip.c + $(CCC) -o $@ crypt_zip.c + objects/diff.o: diff.c $(CCC) -o $@ diff.c @@ -2855,6 +2874,14 @@ objects/charset.o: charset.c vim.h auto/config.h feature.h os_unix.h auto/osdef. ascii.h keymap.h term.h macros.h option.h structs.h regexp.h gui.h \ gui_beval.h proto/gui_beval.pro ex_cmds.h proto.h globals.h farsi.h \ arabic.h +objects/crypt.o: crypt.c vim.h auto/config.h feature.h os_unix.h auto/osdef.h \ + ascii.h keymap.h term.h macros.h option.h structs.h regexp.h gui.h \ + gui_beval.h proto/gui_beval.pro ex_cmds.h proto.h globals.h farsi.h \ + arabic.h +objects/crypt_zip.o: crypt_zip.c vim.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h structs.h \ + regexp.h gui.h gui_beval.h proto/gui_beval.pro ex_cmds.h proto.h \ + globals.h farsi.h arabic.h objects/diff.o: diff.c vim.h auto/config.h feature.h os_unix.h auto/osdef.h \ ascii.h keymap.h term.h macros.h option.h structs.h regexp.h gui.h \ gui_beval.h proto/gui_beval.pro ex_cmds.h proto.h globals.h farsi.h \ diff --git a/src/blowfish.c b/src/blowfish.c index 3d9ba5566a..6bf2482f0e 100644 --- a/src/blowfish.c +++ b/src/blowfish.c @@ -9,17 +9,25 @@ * Blowfish encryption for Vim; in Blowfish cipher feedback mode. * Contributed by Mohsin Ahmed, http://www.cs.albany.edu/~mosh * Based on http://www.schneier.com/blowfish.html by Bruce Schneier. + * + * There are two variants: + * - The old one "blowfish" has a flaw which makes it much easier to crack the + * key. To see this, make a text file with one line of 1000 "x" characters + * and write it encrypted. Use "xxd" to inspect the bytes in the file. You + * will see that a block of 8 bytes repeats 8 times. + * - The new one "blowfish2" is better. It uses an 8 byte CFB to avoid the + * repeats. */ #include "vim.h" -#if defined(FEAT_CRYPT) +#if defined(FEAT_CRYPT) || defined(PROTO) #define ARRAY_LENGTH(A) (sizeof(A)/sizeof(A[0])) #define BF_BLOCK 8 #define BF_BLOCK_MASK 7 -#define BF_CFB_LEN (8*(BF_BLOCK)) +#define BF_MAX_CFB_LEN (8 * BF_BLOCK) typedef union { UINT32_T ul[2]; @@ -37,14 +45,26 @@ typedef union { # endif #endif -static void bf_e_block __ARGS((UINT32_T *p_xl, UINT32_T *p_xr)); -static void bf_e_cblock __ARGS((char_u *block)); -static int bf_check_tables __ARGS((UINT32_T a_ipa[18], UINT32_T a_sbi[4][256], UINT32_T val)); +/* The state of encryption, referenced by cryptstate_T. */ +typedef struct { + UINT32_T pax[18]; /* P-array */ + UINT32_T sbx[4][256]; /* S-boxes */ + int randbyte_offset; + int update_offset; + char_u cfb_buffer[BF_MAX_CFB_LEN]; /* up to 64 bytes used */ + int cfb_len; /* size of cfb_buffer actually used */ +} bf_state_T; + + +static void bf_e_block __ARGS((bf_state_T *state, UINT32_T *p_xl, UINT32_T *p_xr)); +static void bf_e_cblock __ARGS((bf_state_T *state, char_u *block)); +static int bf_check_tables __ARGS((UINT32_T pax[18], UINT32_T sbx[4][256], UINT32_T val)); static int bf_self_test __ARGS((void)); +static void bf_key_init __ARGS((bf_state_T *state, char_u *password, char_u *salt, int salt_len)); +static void bf_cfb_init __ARGS((bf_state_T *state, char_u *seed, int seed_len)); /* Blowfish code */ -static UINT32_T pax[18]; -static UINT32_T ipa[18] = { +static UINT32_T pax_init[18] = { 0x243f6a88u, 0x85a308d3u, 0x13198a2eu, 0x03707344u, 0xa4093822u, 0x299f31d0u, 0x082efa98u, 0xec4e6c89u, 0x452821e6u, @@ -53,8 +73,7 @@ static UINT32_T ipa[18] = { 0xb5470917u, 0x9216d5d9u, 0x8979fb1bu }; -static UINT32_T sbx[4][256]; -static UINT32_T sbi[4][256] = { +static UINT32_T sbx_init[4][256] = { {0xd1310ba6u, 0x98dfb5acu, 0x2ffd72dbu, 0xd01adfb7u, 0xb8e1afedu, 0x6a267e96u, 0xba7c9045u, 0xf12c7f99u, 0x24a19947u, 0xb3916cf7u, 0x0801f2e2u, 0x858efc16u, @@ -314,33 +333,40 @@ static UINT32_T sbi[4][256] = { } }; - #define F1(i) \ - xl ^= pax[i]; \ - xr ^= ((sbx[0][xl >> 24] + \ - sbx[1][(xl & 0xFF0000) >> 16]) ^ \ - sbx[2][(xl & 0xFF00) >> 8]) + \ - sbx[3][xl & 0xFF]; + xl ^= bfs->pax[i]; \ + xr ^= ((bfs->sbx[0][xl >> 24] + \ + bfs->sbx[1][(xl & 0xFF0000) >> 16]) ^ \ + bfs->sbx[2][(xl & 0xFF00) >> 8]) + \ + bfs->sbx[3][xl & 0xFF]; #define F2(i) \ - xr ^= pax[i]; \ - xl ^= ((sbx[0][xr >> 24] + \ - sbx[1][(xr & 0xFF0000) >> 16]) ^ \ - sbx[2][(xr & 0xFF00) >> 8]) + \ - sbx[3][xr & 0xFF]; - + xr ^= bfs->pax[i]; \ + xl ^= ((bfs->sbx[0][xr >> 24] + \ + bfs->sbx[1][(xr & 0xFF0000) >> 16]) ^ \ + bfs->sbx[2][(xr & 0xFF00) >> 8]) + \ + bfs->sbx[3][xr & 0xFF]; static void -bf_e_block(p_xl, p_xr) +bf_e_block(bfs, p_xl, p_xr) + bf_state_T *bfs; UINT32_T *p_xl; UINT32_T *p_xr; { - UINT32_T temp, xl = *p_xl, xr = *p_xr; - - F1(0) F2(1) F1(2) F2(3) F1(4) F2(5) F1(6) F2(7) - F1(8) F2(9) F1(10) F2(11) F1(12) F2(13) F1(14) F2(15) - xl ^= pax[16]; - xr ^= pax[17]; + UINT32_T temp; + UINT32_T xl = *p_xl; + UINT32_T xr = *p_xr; + + F1(0) F2(1) + F1(2) F2(3) + F1(4) F2(5) + F1(6) F2(7) + F1(8) F2(9) + F1(10) F2(11) + F1(12) F2(13) + F1(14) F2(15) + xl ^= bfs->pax[16]; + xr ^= bfs->pax[17]; temp = xl; xl = xr; xr = temp; @@ -348,22 +374,6 @@ bf_e_block(p_xl, p_xr) *p_xr = xr; } -#if 0 /* not used */ - static void -bf_d_block(p_xl, p_xr) - UINT32_T *p_xl; - UINT32_T *p_xr; -{ - UINT32_T temp, xl = *p_xl, xr = *p_xr; - F1(17) F2(16) F1(15) F2(14) F1(13) F2(12) F1(11) F2(10) - F1(9) F2(8) F1(7) F2(6) F1(5) F2(4) F1(3) F2(2) - xl ^= pax[1]; - xr ^= pax[0]; - temp = xl; xl = xr; xr = temp; - *p_xl = xl; *p_xr = xr; -} -#endif - #ifdef WORDS_BIGENDIAN # define htonl2(x) \ @@ -374,7 +384,8 @@ bf_d_block(p_xl, p_xr) #endif static void -bf_e_cblock(block) +bf_e_cblock(bfs, block) + bf_state_T *bfs; char_u *block; { block8 bk; @@ -382,35 +393,22 @@ bf_e_cblock(block) memcpy(bk.uc, block, 8); htonl2(bk.ul[0]); htonl2(bk.ul[1]); - bf_e_block(&bk.ul[0], &bk.ul[1]); + bf_e_block(bfs, &bk.ul[0], &bk.ul[1]); htonl2(bk.ul[0]); htonl2(bk.ul[1]); memcpy(block, bk.uc, 8); } -#if 0 /* not used */ - void -bf_d_cblock(block) - char_u *block; -{ - block8 bk; - memcpy(bk.uc, block, 8); - htonl2(bk.ul[0]); htonl2(bk.ul[1]); - bf_d_block(&bk.ul[0], &bk.ul[1]); - htonl2(bk.ul[0]); htonl2(bk.ul[1]); - memcpy(block, bk.uc, 8); -} -#endif - /* * Initialize the crypt method using "password" as the encryption key and * "salt[salt_len]" as the salt. */ - void -bf_key_init(password, salt, salt_len) - char_u *password; - char_u *salt; - int salt_len; + static void +bf_key_init(bfs, password, salt, salt_len) + bf_state_T *bfs; + char_u *password; + char_u *salt; + int salt_len; { int i, j, keypos = 0; unsigned u; @@ -418,7 +416,7 @@ bf_key_init(password, salt, salt_len) char_u *key; int keylen; - /* Process the key 1000 times. + /* Process the key 1001 times. * See http://en.wikipedia.org/wiki/Key_strengthening. */ key = sha256_key(password, salt, salt_len); for (i = 0; i < 1000; i++) @@ -437,52 +435,54 @@ bf_key_init(password, salt, salt_len) key[i] = u; } - mch_memmove(sbx, sbi, 4 * 4 * 256); + /* Use "key" to initialize the P-array ("pax") and S-boxes ("sbx") of + * Blowfish. */ + mch_memmove(bfs->sbx, sbx_init, 4 * 4 * 256); for (i = 0; i < 18; ++i) { val = 0; for (j = 0; j < 4; ++j) val = (val << 8) | key[keypos++ % keylen]; - pax[i] = ipa[i] ^ val; + bfs->pax[i] = pax_init[i] ^ val; } data_l = data_r = 0; for (i = 0; i < 18; i += 2) { - bf_e_block(&data_l, &data_r); - pax[i + 0] = data_l; - pax[i + 1] = data_r; + bf_e_block(bfs, &data_l, &data_r); + bfs->pax[i + 0] = data_l; + bfs->pax[i + 1] = data_r; } for (i = 0; i < 4; ++i) { for (j = 0; j < 256; j += 2) { - bf_e_block(&data_l, &data_r); - sbx[i][j + 0] = data_l; - sbx[i][j + 1] = data_r; + bf_e_block(bfs, &data_l, &data_r); + bfs->sbx[i][j + 0] = data_l; + bfs->sbx[i][j + 1] = data_r; } } } /* - * BF Self test for corrupted tables or instructions + * Blowfish self-test for corrupted tables or instructions. */ static int -bf_check_tables(a_ipa, a_sbi, val) - UINT32_T a_ipa[18]; - UINT32_T a_sbi[4][256]; +bf_check_tables(pax, sbx, val) + UINT32_T pax[18]; + UINT32_T sbx[4][256]; UINT32_T val; { int i, j; UINT32_T c = 0; for (i = 0; i < 18; i++) - c ^= a_ipa[i]; + c ^= pax[i]; for (i = 0; i < 4; i++) for (j = 0; j < 256; j++) - c ^= a_sbi[i][j]; + c ^= sbx[i][j]; return c == val; } @@ -520,6 +520,10 @@ bf_self_test() int err = 0; block8 bk; UINT32_T ui = 0xffffffffUL; + bf_state_T state; + + vim_memset(&state, 0, sizeof(bf_state_T)); + state.cfb_len = BF_MAX_CFB_LEN; /* We can't simply use sizeof(UINT32_T), it would generate a compiler * warning. */ @@ -528,21 +532,21 @@ bf_self_test() EMSG(_("E820: sizeof(uint32_t) != 4")); } - if (!bf_check_tables(ipa, sbi, 0x6ffa520a)) + if (!bf_check_tables(pax_init, sbx_init, 0x6ffa520a)) err++; bn = ARRAY_LENGTH(bf_test_data); for (i = 0; i < bn; i++) { - bf_key_init((char_u *)(bf_test_data[i].password), + bf_key_init(&state, (char_u *)(bf_test_data[i].password), bf_test_data[i].salt, (int)STRLEN(bf_test_data[i].salt)); - if (!bf_check_tables(pax, sbx, bf_test_data[i].keysum)) + if (!bf_check_tables(state.pax, state.sbx, bf_test_data[i].keysum)) err++; /* Don't modify bf_test_data[i].plaintxt, self test is idempotent. */ memcpy(bk.uc, bf_test_data[i].plaintxt, 8); - bf_e_cblock(bk.uc); + bf_e_cblock(&state, bk.uc); if (memcmp(bk.uc, bf_test_data[i].cryptxt, 8) != 0) { if (err == 0 && memcmp(bk.uc, bf_test_data[i].badcryptxt, 8) == 0) @@ -554,43 +558,43 @@ bf_self_test() return err > 0 ? FAIL : OK; } -/* Cipher feedback mode. */ -static int randbyte_offset = 0; -static int update_offset = 0; -static char_u cfb_buffer[BF_CFB_LEN]; /* 64 bytes */ +/* + * CFB: Cipher Feedback Mode. + */ /* - * Initialize with seed "iv[iv_len]". + * Initialize with seed "seed[seed_len]". */ - void -bf_cfb_init(iv, iv_len) - char_u *iv; - int iv_len; + static void +bf_cfb_init(bfs, seed, seed_len) + bf_state_T *bfs; + char_u *seed; + int seed_len; { int i, mi; - randbyte_offset = update_offset = 0; - vim_memset(cfb_buffer, 0, BF_CFB_LEN); - if (iv_len > 0) + bfs->randbyte_offset = bfs->update_offset = 0; + vim_memset(bfs->cfb_buffer, 0, bfs->cfb_len); + if (seed_len > 0) { - mi = iv_len > BF_CFB_LEN ? iv_len : BF_CFB_LEN; + mi = seed_len > bfs->cfb_len ? seed_len : bfs->cfb_len; for (i = 0; i < mi; i++) - cfb_buffer[i % BF_CFB_LEN] ^= iv[i % iv_len]; + bfs->cfb_buffer[i % bfs->cfb_len] ^= seed[i % seed_len]; } } -#define BF_CFB_UPDATE(c) { \ - cfb_buffer[update_offset] ^= (char_u)c; \ - if (++update_offset == BF_CFB_LEN) \ - update_offset = 0; \ +#define BF_CFB_UPDATE(bfs, c) { \ + bfs->cfb_buffer[bfs->update_offset] ^= (char_u)c; \ + if (++bfs->update_offset == bfs->cfb_len) \ + bfs->update_offset = 0; \ } -#define BF_RANBYTE(t) { \ - if ((randbyte_offset & BF_BLOCK_MASK) == 0) \ - bf_e_cblock(&cfb_buffer[randbyte_offset]); \ - t = cfb_buffer[randbyte_offset]; \ - if (++randbyte_offset == BF_CFB_LEN) \ - randbyte_offset = 0; \ +#define BF_RANBYTE(bfs, t) { \ + if ((bfs->randbyte_offset & BF_BLOCK_MASK) == 0) \ + bf_e_cblock(bfs, &(bfs->cfb_buffer[bfs->randbyte_offset])); \ + t = bfs->cfb_buffer[bfs->randbyte_offset]; \ + if (++bfs->randbyte_offset == bfs->cfb_len) \ + bfs->randbyte_offset = 0; \ } /* @@ -598,90 +602,69 @@ bf_cfb_init(iv, iv_len) * "from" and "to" can be equal to encrypt in place. */ void -bf_crypt_encode(from, len, to) +crypt_blowfish_encode(state, from, len, to) + cryptstate_T *state; char_u *from; size_t len; char_u *to; { + bf_state_T *bfs = state->method_state; size_t i; int ztemp, t; for (i = 0; i < len; ++i) { ztemp = from[i]; - BF_RANBYTE(t); - BF_CFB_UPDATE(ztemp); + BF_RANBYTE(bfs, t); + BF_CFB_UPDATE(bfs, ztemp); to[i] = t ^ ztemp; } } /* - * Decrypt "ptr[len]" in place. + * Decrypt "from[len]" into "to[len]". */ void -bf_crypt_decode(ptr, len) - char_u *ptr; - long len; +crypt_blowfish_decode(state, from, len, to) + cryptstate_T *state; + char_u *from; + size_t len; + char_u *to; { - char_u *p; + bf_state_T *bfs = state->method_state; + size_t i; int t; - for (p = ptr; p < ptr + len; ++p) + for (i = 0; i < len; ++i) { - BF_RANBYTE(t); - *p ^= t; - BF_CFB_UPDATE(*p); + BF_RANBYTE(bfs, t); + to[i] = from[i] ^ t; + BF_CFB_UPDATE(bfs, to[i]); } } -/* - * Initialize the encryption keys and the random header according to - * the given password. - */ void -bf_crypt_init_keys(passwd) - char_u *passwd; /* password string with which to modify keys */ +crypt_blowfish_init(state, key, salt, salt_len, seed, seed_len) + cryptstate_T *state; + char_u* key; + char_u* salt; + int salt_len; + char_u* seed; + int seed_len; { - char_u *p; + bf_state_T *bfs = (bf_state_T *)alloc_clear(sizeof(bf_state_T)); - for (p = passwd; *p != NUL; ++p) - { - BF_CFB_UPDATE(*p); - } -} + state->method_state = bfs; -static int save_randbyte_offset; -static int save_update_offset; -static char_u save_cfb_buffer[BF_CFB_LEN]; -static UINT32_T save_pax[18]; -static UINT32_T save_sbx[4][256]; + /* "blowfish" uses a 64 byte buffer, causing it to repeat 8 byte groups 8 + * times. "blowfish2" uses a 8 byte buffer to avoid repeating. */ + bfs->cfb_len = state->method_nr == CRYPT_M_BF ? BF_MAX_CFB_LEN : BF_BLOCK; -/* - * Save the current crypt state. Can only be used once before - * bf_crypt_restore(). - */ - void -bf_crypt_save() -{ - save_randbyte_offset = randbyte_offset; - save_update_offset = update_offset; - mch_memmove(save_cfb_buffer, cfb_buffer, BF_CFB_LEN); - mch_memmove(save_pax, pax, 4 * 18); - mch_memmove(save_sbx, sbx, 4 * 4 * 256); -} + if (blowfish_self_test() == FAIL) + return; -/* - * Restore the current crypt state. Can only be used after - * bf_crypt_save(). - */ - void -bf_crypt_restore() -{ - randbyte_offset = save_randbyte_offset; - update_offset = save_update_offset; - mch_memmove(cfb_buffer, save_cfb_buffer, BF_CFB_LEN); - mch_memmove(pax, save_pax, 4 * 18); - mch_memmove(sbx, save_sbx, 4 * 4 * 256); + bf_key_init(bfs, key, salt, salt_len); + bf_cfb_init(bfs, seed, seed_len); } /* diff --git a/src/crypt.c b/src/crypt.c new file mode 100644 index 0000000000..758ffb17b2 --- /dev/null +++ b/src/crypt.c @@ -0,0 +1,585 @@ +/* vi:set ts=8 sts=4 sw=4: + * + * VIM - Vi IMproved by Bram Moolenaar + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + * See README.txt for an overview of the Vim source code. + */ + +/* + * crypt.c: Generic encryption support. + */ +#include "vim.h" + +#if defined(FEAT_CRYPT) || defined(PROTO) +/* + * Optional encryption support. + * Mohsin Ahmed, mosh@sasi.com, 1998-09-24 + * Based on zip/crypt sources. + * Refactored by David Leadbeater, 2014. + * + * NOTE FOR USA: Since 2000 exporting this code from the USA is allowed to + * most countries. There are a few exceptions, but that still should not be a + * problem since this code was originally created in Europe and India. + * + * Blowfish addition originally made by Mohsin Ahmed, + * http://www.cs.albany.edu/~mosh 2010-03-14 + * Based on blowfish by Bruce Schneier (http://www.schneier.com/blowfish.html) + * and sha256 by Christophe Devine. + */ + +typedef struct { + char *name; /* encryption name as used in 'cryptmethod' */ + char *magic; /* magic bytes stored in file header */ + int salt_len; /* length of salt, or 0 when not using salt */ + int seed_len; /* length of seed, or 0 when not using salt */ + int works_inplace; /* encryption/decryption can be done in-place */ + int whole_undofile; /* whole undo file is encrypted */ + + /* Optional function pointer for a self-test. */ + int (* self_test_fn)(); + + /* Function pointer for initializing encryption/decription. */ + void (* init_fn)(cryptstate_T *state, char_u *key, + char_u *salt, int salt_len, char_u *seed, int seed_len); + + /* Function pointers for encoding/decoding from one buffer into another. + * Optional, however, these or the _buffer ones should be configured. */ + void (*encode_fn)(cryptstate_T *state, char_u *from, size_t len, + char_u *to); + void (*decode_fn)(cryptstate_T *state, char_u *from, size_t len, + char_u *to); + + /* Function pointers for encoding and decoding, can buffer data if needed. + * Optional (however, these or the above should be configured). */ + long (*encode_buffer_fn)(cryptstate_T *state, char_u *from, size_t len, + char_u **newptr); + long (*decode_buffer_fn)(cryptstate_T *state, char_u *from, size_t len, + char_u **newptr); + + /* Function pointers for in-place encoding and decoding, used for + * crypt_*_inplace(). "from" and "to" arguments will be equal. + * These may be the same as decode_fn and encode_fn above, however an + * algorithm may implement them in a way that is not interchangeable with + * the crypt_(en|de)code() interface (for example because it wishes to add + * padding to files). + * This method is used for swap and undo files which have a rigid format. + */ + void (*encode_inplace_fn)(cryptstate_T *state, char_u *p1, size_t len, + char_u *p2); + void (*decode_inplace_fn)(cryptstate_T *state, char_u *p1, size_t len, + char_u *p2); +} cryptmethod_T; + +/* index is method_nr of cryptstate_T, CRYPT_M_* */ +static cryptmethod_T cryptmethods[CRYPT_M_COUNT] = { + /* PK_Zip; very weak */ + { + "zip", + "VimCrypt~01!", + 0, + 0, + TRUE, + FALSE, + NULL, + crypt_zip_init, + crypt_zip_encode, crypt_zip_decode, + NULL, NULL, + crypt_zip_encode, crypt_zip_decode, + }, + + /* Blowfish/CFB + SHA-256 custom key derivation; implementation issues. */ + { + "blowfish", + "VimCrypt~02!", + 8, + 8, + TRUE, + FALSE, + blowfish_self_test, + crypt_blowfish_init, + crypt_blowfish_encode, crypt_blowfish_decode, + NULL, NULL, + crypt_blowfish_encode, crypt_blowfish_decode, + }, + + /* Blowfish/CFB + SHA-256 custom key derivation; fixed. */ + { + "blowfish2", + "VimCrypt~03!", + 8, + 8, + TRUE, + TRUE, + blowfish_self_test, + crypt_blowfish_init, + crypt_blowfish_encode, crypt_blowfish_decode, + NULL, NULL, + crypt_blowfish_encode, crypt_blowfish_decode, + }, +}; + +#define CRYPT_MAGIC_LEN 12 /* cannot change */ +static char crypt_magic_head[] = "VimCrypt~"; + +/* + * Return int value for crypt method name. + * 0 for "zip", the old method. Also for any non-valid value. + * 1 for "blowfish". + * 2 for "blowfish2". + */ + int +crypt_method_nr_from_name(name) + char_u *name; +{ + int i; + + for (i = 0; i < CRYPT_M_COUNT; ++i) + if (STRCMP(name, cryptmethods[i].name) == 0) + return i; + return 0; +} + +/* + * Get the crypt method used for a file from "ptr[len]", the magic text at the + * start of the file. + * Returns -1 when no encryption used. + */ + int +crypt_method_nr_from_magic(ptr, len) + char *ptr; + int len; +{ + int i; + + if (len < CRYPT_MAGIC_LEN) + return -1; + + for (i = 0; i < CRYPT_M_COUNT; i++) + if (memcmp(ptr, cryptmethods[i].magic, CRYPT_MAGIC_LEN) == 0) + return i; + + i = (int)STRLEN(crypt_magic_head); + if (len >= i && memcmp(ptr, crypt_magic_head, i) == 0) + EMSG(_("E821: File is encrypted with unknown method")); + + return -1; +} + +/* + * Return TRUE if the crypt method for "method_nr" can be done in-place. + */ + int +crypt_works_inplace(state) + cryptstate_T *state; +{ + return cryptmethods[state->method_nr].works_inplace; +} + +/* + * Get the crypt method for buffer "buf" as a number. + */ + int +crypt_get_method_nr(buf) + buf_T *buf; +{ + return crypt_method_nr_from_name(*buf->b_p_cm == NUL ? p_cm : buf->b_p_cm); +} + +/* + * Return TRUE when the buffer uses an encryption method that encrypts the + * whole undo file, not only the text. + */ + int +crypt_whole_undofile(method_nr) + int method_nr; +{ + return cryptmethods[method_nr].whole_undofile; +} + +/* + * Get crypt method specifc length of the file header in bytes. + */ + int +crypt_get_header_len(method_nr) + int method_nr; +{ + return CRYPT_MAGIC_LEN + + cryptmethods[method_nr].salt_len + + cryptmethods[method_nr].seed_len; +} + +/* + * Set the crypt method for buffer "buf" to "method_nr" using the int value as + * returned by crypt_method_nr_from_name(). + */ + void +crypt_set_cm_option(buf, method_nr) + buf_T *buf; + int method_nr; +{ + free_string_option(buf->b_p_cm); + buf->b_p_cm = vim_strsave((char_u *)cryptmethods[method_nr].name); +} + +/* + * If the crypt method for the current buffer has a self-test, run it and + * return OK/FAIL. + */ + int +crypt_self_test() +{ + int method_nr = crypt_get_method_nr(curbuf); + + if (cryptmethods[method_nr].self_test_fn == NULL) + return OK; + return cryptmethods[method_nr].self_test_fn(); +} + +/* + * Allocate a crypt state and initialize it. + */ + cryptstate_T * +crypt_create(method_nr, key, salt, salt_len, seed, seed_len) + int method_nr; + char_u *key; + char_u *salt; + int salt_len; + char_u *seed; + int seed_len; +{ + cryptstate_T *state = (cryptstate_T *)alloc((int)sizeof(cryptstate_T)); + + state->method_nr = method_nr; + cryptmethods[method_nr].init_fn(state, key, salt, salt_len, seed, seed_len); + return state; +} + +/* + * Allocate a crypt state from a file header and initialize it. + * Assumes that header contains at least the number of bytes that + * crypt_get_header_len() returns for "method_nr". + */ + cryptstate_T * +crypt_create_from_header(method_nr, key, header) + int method_nr; + char_u *key; + char_u *header; +{ + char_u *salt = NULL; + char_u *seed = NULL; + int salt_len = cryptmethods[method_nr].salt_len; + int seed_len = cryptmethods[method_nr].seed_len; + + if (salt_len > 0) + salt = header + CRYPT_MAGIC_LEN; + if (seed_len > 0) + seed = header + CRYPT_MAGIC_LEN + salt_len; + + return crypt_create(method_nr, key, salt, salt_len, seed, seed_len); +} + +/* + * Read the crypt method specific header data from "fp". + * Return an allocated cryptstate_T or NULL on error. + */ + cryptstate_T * +crypt_create_from_file(fp, key) + FILE *fp; + char_u *key; +{ + int method_nr; + int header_len; + char magic_buffer[CRYPT_MAGIC_LEN]; + char_u *buffer; + cryptstate_T *state; + + if (fread(magic_buffer, CRYPT_MAGIC_LEN, 1, fp) != 1) + return NULL; + method_nr = crypt_method_nr_from_magic(magic_buffer, CRYPT_MAGIC_LEN); + if (method_nr < 0) + return NULL; + + header_len = crypt_get_header_len(method_nr); + if ((buffer = alloc(header_len)) == NULL) + return NULL; + mch_memmove(buffer, magic_buffer, CRYPT_MAGIC_LEN); + if (header_len > CRYPT_MAGIC_LEN + && fread(buffer + CRYPT_MAGIC_LEN, + header_len - CRYPT_MAGIC_LEN, 1, fp) != 1) + { + vim_free(buffer); + return NULL; + } + + state = crypt_create_from_header(method_nr, key, buffer); + vim_free(buffer); + return state; +} + +/* + * Allocate a cryptstate_T for writing and initialize it with "key". + * Allocates and fills in the header and stores it in "header", setting + * "header_len". The header may include salt and seed, depending on + * cryptmethod. Caller must free header. + * Returns the state or NULL on failure. + */ + cryptstate_T * +crypt_create_for_writing(method_nr, key, header, header_len) + int method_nr; + char_u *key; + char_u **header; + int *header_len; +{ + int len = crypt_get_header_len(method_nr); + char_u *salt = NULL; + char_u *seed = NULL; + int salt_len = cryptmethods[method_nr].salt_len; + int seed_len = cryptmethods[method_nr].seed_len; + cryptstate_T *state; + + *header_len = len; + *header = alloc(len); + if (*header == NULL) + return NULL; + + mch_memmove(*header, cryptmethods[method_nr].magic, CRYPT_MAGIC_LEN); + if (salt_len > 0 || seed_len > 0) + { + if (salt_len > 0) + salt = *header + CRYPT_MAGIC_LEN; + if (seed_len > 0) + seed = *header + CRYPT_MAGIC_LEN + salt_len; + + /* TODO: Should this be crypt method specific? (Probably not worth |