changeset 2267:c08f91142c41 vim73

Crypt the swapfile.
author Bram Moolenaar <bram@vim.org>
date Mon, 21 Jun 2010 06:15:46 +0200
parents ae2e615a7320
children aafed4a4866f
files runtime/doc/editing.txt runtime/doc/helphelp.txt runtime/doc/recover.txt runtime/doc/tags runtime/doc/todo.txt runtime/doc/usr_11.txt runtime/syntax/c.vim src/blowfish.c src/fileio.c src/globals.h src/main.c src/memfile.c src/memline.c src/misc2.c src/option.c src/proto/blowfish.pro src/proto/memline.pro src/proto/misc2.pro src/sha256.c src/structs.h src/testdir/test72.in src/undo.c src/workshop.c
diffstat 23 files changed, 825 insertions(+), 156 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/editing.txt
+++ b/runtime/doc/editing.txt
@@ -1332,10 +1332,12 @@ 9. Encryption						*encryption*
 Vim is able to write files encrypted, and read them back.  The encrypted text
 cannot be read without the right key.
 
-Note: The swapfile and text in memory is not encrypted.  A system
-administrator will be able to see your text while you are editing it.
-When filtering text with ":!filter" or using ":w !command" the text is not
-encrypted, this may reveal it to others.
+The text in the swap file and the undo file is also encrypted.
+
+Note: The text in memory is not encrypted.  A system administrator may be able
+to see your text while you are editing it.  When filtering text with
+":!filter" or using ":w !command" the text is not encrypted, this may reveal
+it to others.  The 'viminfo' file is not encrypted.
 
 WARNING: If you make a typo when entering the key and then write the file and
 exit, the text will be lost!
--- a/runtime/doc/helphelp.txt
+++ b/runtime/doc/helphelp.txt
@@ -119,9 +119,9 @@ 1. Help commands					*online-help*
 							*:lh* *:lhelpgrep*
 :lh[elpgrep] {pattern}[@xx]
 			Same as ":helpgrep", except the location list is used
-			instead of the quickfix list. If the help window is
+			instead of the quickfix list.  If the help window is
 			already opened, then the location list for that window
-			is used. Otherwise, a new help window is opened and
+			is used.  Otherwise, a new help window is opened and
 			the location list for that window is set.  The
 			location list for the current window is not changed.
 
@@ -281,9 +281,9 @@ The first line in a help file should hav
 
 *helpfile_name.txt*	For Vim version 7.3	Last change: 2010 June 4
 
-The first field is a link to the help file name. The second field describes
-the applicable Vim version. The last field specifies the last modification
-date of the file. Each field is separated by a tab.
+The first field is a link to the help file name.  The second field describes
+the applicable Vim version.  The last field specifies the last modification
+date of the file.  Each field is separated by a tab.
 
 At the bottom of the help file, place a Vim modeline to set the 'textwidth'
 and 'tabstop' options and the 'filetype' to 'help'.  Never set a global option
@@ -295,30 +295,30 @@ TAGS
 
 To define a help tag, place the name between asterisks (*tag-name*).  The
 tag-name should be different from all the Vim help tag names and ideally
-should begin with the name of the Vim plugin. The tag name is usually right
+should begin with the name of the Vim plugin.  The tag name is usually right
 aligned on a line.
 
 When referring to an existing help tag and to create a hot-link, place the
 name between two bars (|) eg. |help-writing|.
 
 When referring to a Vim option in the help file, place the option name between
-two single quotes. eg. 'statusline'
+two single quotes, eg. 'statusline'
 
 
 HIGHLIGHTING
 
-To define a column heading, use a tilde character at the end of the line. This
-will highlight the column heading in a different color. E.g.
+To define a column heading, use a tilde character at the end of the line.
+This will highlight the column heading in a different color.  E.g.
 
 Column heading~
 
 To separate sections in a help file, place a series of '=' characters in a
-line starting from the first column. The section separator line is highlighted
+line starting from the first column.  The section separator line is highlighted
 differently.
 
 To quote a block of ex-commands verbatim, place a greater than (>) character
 at the end of the line before the block and a less than (<) character as the
-first non-blank on a line following the block. Any line starting in column 1
+first non-blank on a line following the block.  Any line starting in column 1
 also implicitly stops the block of ex-commands before it.  E.g. >
     function Example_Func()
 	echo "Example"
--- a/runtime/doc/recover.txt
+++ b/runtime/doc/recover.txt
@@ -188,4 +188,43 @@ will continue to get warning messages th
 
 {Vi: recovers in another way and sends mail if there is something to recover}
 
+
+ENCRYPTION AND THE SWAP FILE				*:recover-crypt*
+
+When the text file is encrypted the swap file is encrypted as well.  This
+makes recovery a bit more complicated.  When recovering from a swap file and
+encryption has been used, you will be asked to enter one or two crypt keys.
+
+If the text file does not exist you will only be asked to enter the crypt key
+for the swap file.
+
+If the text file does exist, it may be encrypted in a different way than the
+swap file.  You will be asked for the crypt key twice:
+
+	Need encryption key for "/tmp/tt" ~
+	Enter encryption key: ****** ~
+	"/tmp/tt" [crypted] 23200L, 522129C ~
+	Using swap file "/tmp/.tt.swp" ~
+	Original file "/tmp/tt" ~
+	Swap file is encrypted: "/tmp/.tt.swp" ~
+	If you entered a new crypt key but did not write the text file, ~
+	enter the new crypt key. ~
+	If you wrote the text file after changing the crypt key press enter ~
+	to use the same key for text file and swap file ~
+	Enter encryption key:  ~
+
+You can be in one of these two situations:
+
+1. The encryption key was not changed, or after changing the key the text file
+   was written.  You will be prompted for the crypt key twice.  The second
+   time you can simply press Enter.  That means the same key is used for the
+   text file and the swap file.
+2. You entered a new encryption key, but did not save the text file.  Vim will
+   then use the new key for the swap file, and the text file will still be
+   encrypted with the old key.  At the second prompt enter the new key.
+
+Note that after recovery the key of the swap file will be used for the text
+file.  Thus if you write the text file, you need to use that new key.
+
+
  vim:tw=78:ts=8:ft=help:norl:
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -2577,6 +2577,7 @@ 90.5	usr_90.txt	/*90.5*
 :read!	insert.txt	/*:read!*
 :rec	recover.txt	/*:rec*
 :recover	recover.txt	/*:recover*
+:recover-crypt	recover.txt	/*:recover-crypt*
 :red	undo.txt	/*:red*
 :redi	various.txt	/*:redi*
 :redir	various.txt	/*:redir*
@@ -6914,7 +6915,6 @@ os_unix.txt	os_unix.txt	/*os_unix.txt*
 os_vms.txt	os_vms.txt	/*os_vms.txt*
 os_win32.txt	os_win32.txt	/*os_win32.txt*
 other-features	vi_diff.txt	/*other-features*
-ownsyntax	eval.txt	/*ownsyntax*
 p	change.txt	/*p*
 page-down	intro.txt	/*page-down*
 page-up	intro.txt	/*page-up*
@@ -8220,7 +8220,7 @@ vt100-cursor-keys	term.txt	/*vt100-curso
 vt100-function-keys	term.txt	/*vt100-function-keys*
 w	motion.txt	/*w*
 w32-clientserver	remote.txt	/*w32-clientserver*
-w:ownsyntax-variable	eval.txt	/*w:ownsyntax-variable*
+w:current_syntax	syntax.txt	/*w:current_syntax*
 w:var	eval.txt	/*w:var*
 warningmsg-variable	eval.txt	/*warningmsg-variable*
 white-space	pattern.txt	/*white-space*
--- a/runtime/doc/todo.txt
+++ b/runtime/doc/todo.txt
@@ -1088,18 +1088,16 @@ Vim 7.3:
 - using NSIS 2.46: install on Windows 7 works, but no "Edit with Vim" menu.
    Use register_shell_extension()? (George Reilly, 2010 May 26)
    Ron's version: http://dev.ronware.org/p/vim/finfo?name=gvim.nsi
-- Also crypt the swap file, each block separately.  Change mf_write() and
-    mf_read().
-    - How to get b_p_key to these functions?  -> Store buf_T pointer in mfp.
-    - Generate a salt and seed for the swapfile, put it in block 0.
-    - For each block, use password + seed + byte offset to crypt/decrypt.
-    - When changing the password need to read back with the old password and
-      write again with the new one.
-    - Fill the gaps in the block with random bytes, otherwise it's easy to
-      check for correct password by finding NUL bytes.
-    - Verify recovery works.
+- Also crypt the swap file, each block separately:
+    - When changing the password or 'cryptmethod' need to read back with the
+      old password and write again with the new one.
+      Problem: when the file is not written, key differs between text file and
+      swap file!
+- Patch for :ownsyntax completion (Dominique Pelle, 2010 Jun 20)
 - Patch for conceal feature and 'foldcolumn'. (Dominique Pelle, 2010 Jun 10,
   second patch)
+  Also patch from Vince, 2010 Jun 15.  And another June 16.
+  However: more generic patch on the way.
 - patch for conceal feature and 'modifiable'. (Dominique Pelle, 2010 Jun 9)
 - undofile: keep markers where the file was written/read, so that it's easy to
   go back to a saved version of the file:  ":earlier 1f" (f for file)?
@@ -1110,6 +1108,7 @@ Vim 7.3:
   dictionary: {'nr': 2, 'time': 1234, 'saved': 1}
 - Remove support for GTK 1?  Patch by James Vega, Jun 11.
 Patches to include:
+- Patch for X clibboard CurrentTime, (Fries, 2010 Jun 20)
 - Patch for Lisp support with ECL (Mikael Jansson, 2008 Oct 25)
 - Minor patches from Dominique Pelle, 2010 May 15
 - Gvimext patch to support wide file names. (Szabolcs Horvat 2008 Sep 10)
@@ -1117,8 +1116,9 @@ Patches to include:
 - Patch to support clipboard for Mac terminal. (Jjgod Jiang, 2009 Aug 1)
 - Patch to support :browse for more commands. (Lech Lorens, 2009 Jul 18)
 - Patch to improve javascript indenting. (Hari Kumar G, 2010 May 22)
+- Patch to use return value of 'formatexpr'. (James Vega, 2010 Jun 16)
 - Patch to make CTRL-L work better with 'ignorecase' and 'smarcase'. (Martin
-  Toft, 2010 Jun 8)
+  Toft, 2010 Jun 8, Jun 16)
 - Patch to add diff functionality to 2html.vim. (Christian Brabandt, 2009 Dec
   15)
 - Win32: patch for better font scaling. (George Reilly, 2009 Mar 26) 
--- a/runtime/doc/usr_11.txt
+++ b/runtime/doc/usr_11.txt
@@ -283,6 +283,8 @@ machines.  Therefore, don't rely on Vim 
 If you really don't want to see this message, you can add the 'A' flag to the
 'shortmess' option.  But it's very unusual that you need this.
 
+For remarks about encryption and the swap file, see |:recover-crypt|.
+
 ==============================================================================
 *11.4*	Further reading
 
--- a/runtime/syntax/c.vim
+++ b/runtime/syntax/c.vim
@@ -270,7 +270,7 @@ if !exists("c_no_c99") " ISO C99
 endif
 
 " Accept %: for # (C99)
-syn region	cPreCondit	start="^\s*\(%:\|#\)\s*\(if\|ifdef\|ifndef\|elif\)\>" skip="\\$" end="$"  contains=cComment,cCommentL,cCppString,cCharacter,cCppParen,cParenError,cNumbers,cCommentError,cSpaceError
+syn region      cPreCondit      start="^\s*\(%:\|#\)\s*\(if\|ifdef\|ifndef\|elif\)\>" skip="\\$" end="$"  keepend contains=cComment,cCommentL,cCppString,cCharacter,cCppParen,cParenError,cNumbers,cCommentError,cSpaceError
 syn match	cPreCondit	display "^\s*\(%:\|#\)\s*\(else\|endif\)\>"
 if !exists("c_no_if0")
   if !exists("c_no_if0_fold")
--- a/src/blowfish.c
+++ b/src/blowfish.c
@@ -436,13 +436,7 @@ bf_key_init(password, salt, salt_len)
         key[i] = j;
     }
 
-    for (i = 0; i < 256; ++i)
-    {
-	sbx[0][i] = sbi[0][i];
-	sbx[1][i] = sbi[1][i];
-	sbx[2][i] = sbi[2][i];
-	sbx[3][i] = sbi[3][i];
-    }
+    mch_memmove(sbx, sbi, 4 * 4 * 256);
 
     for (i = 0; i < 18; ++i)
     {
@@ -655,6 +649,40 @@ bf_crypt_init_keys(passwd)
     }
 }
 
+static int save_randbyte_offset;
+static int save_update_offset;
+static char_u save_ofb_buffer[BF_OFB_LEN];
+static UINT32_T save_pax[18];
+static UINT32_T save_sbx[4][256];
+
+/*
+ * 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_ofb_buffer, ofb_buffer, BF_OFB_LEN);
+    mch_memmove(save_pax, pax, 4 * 18);
+    mch_memmove(save_sbx, sbx, 4 * 4 * 256);
+}
+
+/*
+ * 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(ofb_buffer, save_ofb_buffer, BF_OFB_LEN);
+    mch_memmove(pax, save_pax, 4 * 18);
+    mch_memmove(sbx, save_sbx, 4 * 4 * 256);
+}
+
 /*
  * Run a test to check if the encryption works as expected.
  * Give an error and return FAIL when not.
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -64,7 +64,7 @@ static void check_marks_read __ARGS((voi
 #endif
 #ifdef FEAT_CRYPT
 static int get_crypt_method __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, int *did_ask));
+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
 static void set_file_time __ARGS((char_u *fname, time_t atime, time_t mtime));
@@ -995,6 +995,13 @@ 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.
@@ -1426,7 +1433,8 @@ retry:
 		 */
 		if (filesize == 0)
 		    cryptkey = check_for_cryptkey(cryptkey, ptr, &size,
-					&filesize, newfile, &did_ask_for_key);
+					&filesize, newfile, sfname,
+					&did_ask_for_key);
 		/*
 		 * Decrypt the read bytes.
 		 */
@@ -2277,8 +2285,14 @@ failed:
 	save_file_ff(curbuf);		/* remember the current file format */
 
 #ifdef FEAT_CRYPT
-    if (cryptkey != curbuf->b_p_key)
-	free_crypt_key(cryptkey);
+    if (cryptkey != 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 */
+    }
 #endif
 
 #ifdef FEAT_MBYTE
@@ -2869,12 +2883,13 @@ get_crypt_method(ptr, len)
  * Return the (new) encryption key, NULL for no encryption.
  */
     static char_u *
-check_for_cryptkey(cryptkey, ptr, sizep, filesizep, newfile, did_ask)
+check_for_cryptkey(cryptkey, ptr, sizep, filesizep, newfile, fname, did_ask)
     char_u	*cryptkey;	/* previous encryption key or NULL */
     char_u	*ptr;		/* pointer to read bytes */
     long	*sizep;		/* length of read bytes */
     off_t	*filesizep;	/* nr of bytes used from file */
     int		newfile;	/* editing a new buffer */
+    char_u	*fname;		/* file name to display */
     int		*did_ask;	/* flag: whether already asked for key */
 {
     int method = get_crypt_method((char *)ptr, *sizep);
@@ -2882,7 +2897,6 @@ check_for_cryptkey(cryptkey, ptr, sizep,
     if (method >= 0)
     {
 	curbuf->b_p_cm = method;
-	use_crypt_method = method;
 	if (method > 0)
 	    (void)blowfish_self_test();
 	if (cryptkey == NULL && !*did_ask)
@@ -2895,6 +2909,8 @@ check_for_cryptkey(cryptkey, ptr, sizep,
 		 * option and don't free it.  bf needs hash of the key saved.
 		 * Don't ask for the key again when first time Enter was hit.
 		 * Happens when retrying to detect encoding. */
+		smsg((char_u *)_(need_key_msg), fname);
+		msg_scroll = TRUE;
 		cryptkey = get_crypt_key(newfile, FALSE);
 		*did_ask = TRUE;
 
@@ -2913,6 +2929,8 @@ check_for_cryptkey(cryptkey, ptr, sizep,
 	    int seed_len = crypt_seed_len[method];
 	    int salt_len = crypt_salt_len[method];
 
+	    crypt_push_state();
+	    use_crypt_method = method;
 	    if (method == 0)
 		crypt_init_keys(cryptkey);
 	    else
@@ -2924,7 +2942,8 @@ check_for_cryptkey(cryptkey, ptr, 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);
+	    mch_memmove(ptr, ptr + CRYPT_MAGIC_LEN + salt_len + seed_len,
+							      (size_t)*sizep);
 	}
     }
     /* When starting to edit a new file which does not have encryption, clear
@@ -2956,6 +2975,7 @@ prepare_crypt_read(fp)
     if (method < 0 || method != curbuf->b_p_cm)
 	return FAIL;
 
+    crypt_push_state();
     if (method == 0)
 	crypt_init_keys(curbuf->b_p_key);
     else
@@ -2974,6 +2994,8 @@ prepare_crypt_read(fp)
 /*
  * 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)
@@ -2990,6 +3012,7 @@ prepare_crypt_write(buf, lenp)
 						    + CRYPT_SEED_LEN_MAX + 2);
     if (header != NULL)
     {
+	crypt_push_state();
 	use_crypt_method = buf->b_p_cm;  /* select pkzip or blowfish */
 	vim_strncpy(header, (char_u *)crypt_magic[use_crypt_method],
 							     CRYPT_MAGIC_LEN);
@@ -4404,7 +4427,7 @@ restore_backup:
     write_info.bw_fd = fd;
 
 #ifdef FEAT_CRYPT
-    if (*buf->b_p_key && !filtering)
+    if (*buf->b_p_key != NUL && !filtering)
     {
 	char_u *header;
 	int    header_len;
@@ -4674,6 +4697,10 @@ restore_backup:
     if (!backup_copy)
 	mch_set_acl(wfname, acl);
 #endif
+#ifdef FEAT_CRYPT
+    if (wb_flags & FIO_ENCRYPTED)
+	crypt_pop_state();
+#endif
 
 
 #if defined(FEAT_MBYTE) && defined(FEAT_EVAL)
--- a/src/globals.h
+++ b/src/globals.h
@@ -1564,6 +1564,10 @@ EXTERN short disallow_gui	INIT(= FALSE);
 EXTERN char top_bot_msg[] INIT(= N_("search hit TOP, continuing at BOTTOM"));
 EXTERN char bot_top_msg[] INIT(= N_("search hit BOTTOM, continuing at TOP"));
 
+#ifdef FEAT_CRYPT
+EXTERN char need_key_msg[] INIT(= N_("Need encryption key for \"%s\""));
+#endif
+
 /*
  * Comms. with the session manager (XSMP)
  */
--- a/src/main.c
+++ b/src/main.c
@@ -595,7 +595,7 @@ main
      */
     if (recoverymode && fname == NULL)
     {
-	recover_names(NULL, TRUE, 0);
+	recover_names(NULL, TRUE, 0, NULL);
 	mch_exit(0);
     }
 
--- a/src/memfile.c
+++ b/src/memfile.c
@@ -85,6 +85,7 @@ static void mf_ins_free __ARGS((memfile_
 static bhdr_T *mf_rem_free __ARGS((memfile_T *));
 static int  mf_read __ARGS((memfile_T *, bhdr_T *));
 static int  mf_write __ARGS((memfile_T *, bhdr_T *));
+static int  mf_write_block __ARGS((memfile_T *mfp, bhdr_T *hp, off_t offset, unsigned size));
 static int  mf_trans_add __ARGS((memfile_T *, bhdr_T *));
 static void mf_do_open __ARGS((memfile_T *, char_u *, int));
 
@@ -161,6 +162,9 @@ mf_open(fname, flags)
 	mfp->mf_trans[i] = NULL;	/* trans lists are empty */
     }
     mfp->mf_page_size = MEMFILE_PAGE_SIZE;
+#ifdef FEAT_CRYPT
+    mfp->mf_old_key = NULL;
+#endif
 
 #ifdef USE_FSTATFS
     /*
@@ -422,7 +426,7 @@ mf_new(mfp, negative, page_count)
 }
 
 /*
- * get existing block 'nr' with 'page_count' pages
+ * Get existing block "nr" with "page_count" pages.
  *
  * Note: The caller should first check a negative nr with mf_trans_del()
  */
@@ -1050,6 +1054,13 @@ mf_read(mfp, hp)
 	PERROR(_("E295: Read error in swap file"));
 	return FAIL;
     }
+
+#ifdef FEAT_CRYPT
+    /* Decrypt if 'key' is set and this is a data block. */
+    if (*mfp->mf_buffer->b_p_key != NUL)
+	ml_decrypt_data(mfp, hp->bh_data, offset, size);
+#endif
+
     return OK;
 }
 
@@ -1107,8 +1118,7 @@ mf_write(mfp, hp)
 	else
 	    page_count = hp2->bh_page_count;
 	size = page_size * page_count;
-	if ((unsigned)vim_write(mfp->mf_fd,
-	     (hp2 == NULL ? hp : hp2)->bh_data, size) != size)
+	if (mf_write_block(mfp, hp2 == NULL ? hp : hp2, offset, size) == FAIL)
 	{
 	    /*
 	     * Avoid repeating the error message, this mostly happens when the
@@ -1134,6 +1144,42 @@ mf_write(mfp, hp)
 }
 
 /*
+ * Write block "hp" with data size "size" to file "mfp->mf_fd".
+ * Takes care of encryption.
+ * Return FAIL or OK.
+ */
+    static int
+mf_write_block(mfp, hp, offset, size)
+    memfile_T	*mfp;
+    bhdr_T	*hp;
+    off_t	offset UNUSED;
+    unsigned	size;
+{
+    char_u	*data = hp->bh_data;
+    int		result = OK;
+
+#ifdef FEAT_CRYPT
+    /* Encrypt if 'key' is set and this is a data block. */
+    if (*mfp->mf_buffer->b_p_key != NUL)
+    {
+	data = ml_encrypt_data(mfp, data, offset, size);
+	if (data == NULL)
+	    return FAIL;
+    }
+#endif
+
+    if ((unsigned)vim_write(mfp->mf_fd, data, size) != size)
+	result = FAIL;
+
+#ifdef FEAT_CRYPT
+    if (data != hp->bh_data)
+	vim_free(data);
+#endif
+
+    return result;
+}
+
+/*
  * Make block number for *hp positive and add it to the translation list
  *
  * Return FAIL for failure, OK otherwise
@@ -1156,7 +1202,7 @@ mf_trans_add(mfp, hp)
 	return FAIL;
 
 /*
- * get a new number for the block.
+ * Get a new number for the block.
  * If the first item in the free list has sufficient pages, use its number
  * Otherwise use mf_blocknr_max.
  */
--- a/src/memline.c
+++ b/src/memline.c
@@ -65,10 +65,12 @@ typedef struct pointer_block	PTR_BL;	   
 typedef struct data_block	DATA_BL;    /* contents of a data block */
 typedef struct pointer_entry	PTR_EN;	    /* block/line-count pair */
 
-#define DATA_ID	    (('d' << 8) + 'a')	    /* data block id */
-#define PTR_ID	    (('p' << 8) + 't')	    /* pointer block id */
-#define BLOCK0_ID0  'b'			    /* block 0 id 0 */
-#define BLOCK0_ID1  '0'			    /* block 0 id 1 */
+#define DATA_ID	       (('d' << 8) + 'a')   /* data block id */
+#define PTR_ID	       (('p' << 8) + 't')   /* pointer block id */
+#define BLOCK0_ID0     'b'		    /* block 0 id 0 */
+#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 */
 
 /*
  * pointer to a block, used in a pointer block
@@ -128,7 +130,8 @@ struct data_block
 #define HEADER_SIZE (sizeof(DATA_BL) - INDEX_SIZE)  /* size of data block header */
 
 #define B0_FNAME_SIZE_ORG	900	/* what it was in older versions */
-#define B0_FNAME_SIZE		898
+#define B0_FNAME_SIZE_NOCRYPT	898	/* 2 bytes used for other things */
+#define B0_FNAME_SIZE_CRYPT	890	/* 10 bytes used for other things */
 #define B0_UNAME_SIZE		40
 #define B0_HNAME_SIZE		40
 /*
@@ -155,7 +158,8 @@ struct data_block
  */
 struct block0
 {
-    char_u	b0_id[2];	/* id for block 0: BLOCK0_ID0 and BLOCK0_ID1 */
+    char_u	b0_id[2];	/* id for block 0: BLOCK0_ID0 and BLOCK0_ID1,
+				 * BLOCK0_ID1_C0, BLOCK0_ID1_C1 */
     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 */
@@ -177,12 +181,18 @@ struct block0
  * when there is room, for very long file names it's omitted.
  */
 #define B0_DIRTY	0x55
-#define b0_dirty	b0_fname[B0_FNAME_SIZE_ORG-1]
+#define b0_dirty	b0_fname[B0_FNAME_SIZE_ORG - 1]
 
 /*
  * The b0_flags field is new in Vim 7.0.
  */
-#define b0_flags	b0_fname[B0_FNAME_SIZE_ORG-2]
+#define b0_flags	b0_fname[B0_FNAME_SIZE_ORG - 2]
+
+/*
+ * Crypt seed goes here, 8 bytes.  New in Vim 7.3.
+ * Without encryption these bytes may be used for 'fenc'.
+ */
+#define b0_seed		b0_fname[B0_FNAME_SIZE_ORG - 2 - MF_SEED_LEN]
 
 /* The lowest two bits contain the fileformat.  Zero means it's not set
  * (compatible with Vim 6.x), otherwise it's EOL_UNIX + 1, EOL_DOS + 1 or
@@ -216,7 +226,18 @@ static linenr_T	lowest_marked = 0;
 #define ML_FLUSH	0x02	    /* flush locked block */
 #define ML_SIMPLE(x)	(x & 0x10)  /* DEL, INS or FIND */
 
-static void ml_upd_block0 __ARGS((buf_T *buf, int set_fname));
+/* argument for ml_upd_block0() */
+typedef enum {
+      UB_FNAME = 0	/* update timestamp and filename */
+    , UB_SAME_DIR       /* update the B0_SAME_DIR flag */
+    , UB_CRYPT		/* update crypt key */
+} upd_block0_T;
+
+#ifdef FEAT_CRYPT
+static void ml_set_b0_crypt __ARGS((buf_T *buf, ZERO_BL *b0p));
+#endif
+static int ml_check_b0_id __ARGS((ZERO_BL *b0p));
+static void ml_upd_block0 __ARGS((buf_T *buf, upd_block0_T what));
 static void set_b0_fname __ARGS((ZERO_BL *, buf_T *buf));
 static void set_b0_dir_flag __ARGS((ZERO_BL *b0p, buf_T *buf));
 #ifdef FEAT_MBYTE
@@ -242,6 +263,9 @@ static long char_to_long __ARGS((char_u 
 #if defined(UNIX) || defined(WIN3264)
 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));
+#endif
 #ifdef FEAT_BYTEOFF
 static void ml_updatechunk __ARGS((buf_T *buf, long line, long len, int updtype));
 #endif
@@ -264,7 +288,7 @@ ml_open(buf)
     /*
      * init fields in memline struct
      */
-    buf->b_ml.ml_stack_size = 0;	/* no stack yet */
+    buf->b_ml.ml_stack_size = 0; /* no stack yet */
     buf->b_ml.ml_stack = NULL;	/* no stack yet */
     buf->b_ml.ml_stack_top = 0;	/* nothing in the stack */
     buf->b_ml.ml_locked = NULL;	/* no cached block */
@@ -289,6 +313,9 @@ ml_open(buf)
 	goto error;
 
     buf->b_ml.ml_mfp = mfp;
+#ifdef FEAT_CRYPT
+    mfp->mf_buffer = buf;
+#endif
     buf->b_ml.ml_flags = ML_EMPTY;
     buf->b_ml.ml_line_count = 1;
 #ifdef FEAT_LINEBREAK
@@ -336,12 +363,16 @@ ml_open(buf)
 	mch_get_host_name(b0p->b0_hname, B0_HNAME_SIZE);
 	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);
+#endif
     }
 
     /*
      * Always sync block number 0 to disk, so we can check the file name in
-     * the swap file in findswapname(). Don't do this for help files though
-     * and spell buffer though.
+     * the swap file in findswapname(). Don't do this for a help files or
+     * a spell buffer though.
      * Only works when there's a swapfile, otherwise it's done when the file
      * is created.
      */
@@ -397,6 +428,165 @@ error:
     return FAIL;
 }
 
+#if defined(FEAT_CRYPT) || defined(PROTO)
+/*
+ * Prepare encryption for "buf" with block 0 "b0p".
+ */
+    static void
+ml_set_b0_crypt(buf, b0p)
+    buf_T	*buf;
+    ZERO_BL	*b0p;
+{
+    if (*buf->b_p_key == NUL)
+	b0p->b0_id[1] = BLOCK0_ID1;
+    else
+    {
+	if (buf->b_p_cm == 0)
+	    b0p->b0_id[1] = BLOCK0_ID1_C0;
+	else
+	{
+	    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);
+	}
+    }
+}
+
+/*
+ * Called after the crypt key or 'cryptmethod' was changed for "buf".
+ * Will apply this to the swapfile.
+ * "old_key" is the previous key.  It is equal to buf->b_p_key when
+ * 'cryptmethod' is changed.
+ * "old_cm" is the previous 'cryptmethod'.  It is equal to buf->b_p_cm when
+ * 'key' is changed.
+ */
+    void
+ml_set_crypt_key(buf, old_key, old_cm)
+    buf_T	*buf;
+    char_u	*old_key;
+    int		old_cm;
+{
+    memfile_T	*mfp = buf->b_ml.ml_mfp;
+    bhdr_T	*hp;
+    int		page_count;
+    int		idx;
+    long	error;
+    infoptr_T	*ip;
+    PTR_BL	*pp;
+    DATA_BL	*dp;
+    blocknr_T	bnum;
+    int		top;
+
+    if (mfp == NULL || mfp->mf_fd < 0)
+	return;  /* no memfile yet, nothing to do */
+
+    /* Set the key, method and seed to be used for reading, these must be the
+     * old values. */
+    mfp->mf_old_key = old_key;
+    mfp->mf_old_cm = old_cm;
+    if (old_cm > 0)
+	mch_memmove(mfp->mf_old_seed, mfp->mf_seed, MF_SEED_LEN);
+
+    /* Update block 0 with the crypt flag and may set a new seed. */
+    ml_upd_block0(buf, UB_CRYPT);
+
+    if (mfp->mf_infile_count > 2)
+    {
+	/*
+	 * Need to read back all data blocks from disk, decrypt them with the
+	 * old key/method and mark them to be written. The algorithm is
+	 * similar to what happens in ml_recover(), but we skip negative block
+	 * numbers.
+	 */
+	ml_flush_line(buf);		    /* flush buffered line */
+	(void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); /* flush locked block */
+
+	hp = NULL;
+	bnum = 1;		/* start with block 1 */
+	page_count = 1;		/* which is 1 page */
+	idx = 0;		/* start with first index in block 1 */
+	error = 0;
+	buf->b_ml.ml_stack_top = 0;
+	buf->b_ml.ml_stack = NULL;
+	buf->b_ml.ml_stack_size = 0;	/* no stack yet */
+
+	for ( ; !got_int; line_breakcheck())
+	{
+	    if (hp != NULL)
+		mf_put(mfp, hp, FALSE, FALSE);	/* release previous block */
+
+	    /* get the block (pointer or data) */
+	    if ((hp = mf_get(mfp, (blocknr_T)bnum, page_count)) == NULL)
+	    {
+		if (bnum == 1)
+		    break;
+		++error;
+	    }
+	    else
+	    {
+		pp = (PTR_BL *)(hp->bh_data);
+		if (pp->pb_id == PTR_ID)	/* it is a pointer block */
+		{
+		    if (pp->pb_count == 0)
+		    {
+			/* empty block? */
+			++error;
+		    }
+		    else if (idx < (int)pp->pb_count)	/* go a block deeper */
+		    {
+			if (pp->pb_pointer[idx].pe_bnum < 0)
+			{
+			    /* Skip data block with negative block number. */
+			    ++idx;    /* get same block again for next index */
+			    continue;
+			}
+
+			/* going one block deeper in the tree, new entry in
+			 * stack */
+			if ((top = ml_add_stack(buf)) < 0)
+			{
+			    ++error;
+			    break;		    /* out of memory */
+			}
+			ip = &(buf->b_ml.ml_stack[top]);
+			ip->ip_bnum = bnum;
+			ip->ip_index = idx;
+
+			bnum = pp->pb_pointer[idx].pe_bnum;
+			page_count = pp->pb_pointer[idx].pe_page_count;
+			continue;
+		    }
+		}
+		else	    /* not a pointer block */
+		{
+		    dp = (DATA_BL *)(hp->bh_data);
+		    if (dp->db_id != DATA_ID)	/* block id wrong */
+			++error;
+		    else
+		    {
+			/* It is a data block, need to write it back to disk. */
+			mf_put(mfp, hp, TRUE, FALSE);
+			hp = NULL;
+		    }
+		}
+	    }
+
+	    if (buf->b_ml.ml_stack_top == 0)	/* finished */
+		break;
+
+	    /* go one block up in the tree */
+	    ip = &(buf->b_ml.ml_stack[--(buf->b_ml.ml_stack_top)]);
+	    bnum = ip->ip_bnum;
+	    idx = ip->ip_index + 1;	    /* go to next index */
+	    page_count = 1;
+	}
+    }
+
+    mfp->mf_old_key = NULL;
+}
+#endif
+
 /*
  * ml_setname() is called when the file name of "buf" has been changed.
  * It may rename the swap file.
@@ -475,7 +665,7 @@ ml_setname(buf)
 #else
 	    mf_set_ffname(mfp);
 #endif
-	    ml_upd_block0(buf, FALSE);
+	    ml_upd_block0(buf, UB_SAME_DIR);
 	    break;
 	}
 	vim_free(fname);	    /* this fname didn't work, try another */
@@ -569,7 +759,7 @@ ml_open_file(buf)
 	     */
 	    mf_fullname(mfp);
 #endif
-	    ml_upd_block0(buf, FALSE);
+	    ml_upd_block0(buf, UB_SAME_DIR);
 
 	    /* Flush block zero, so others can read it */
 	    if (mf_sync(mfp, MFS_ZERO) == OK)
@@ -680,16 +870,32 @@ ml_close_notmod()
 ml_timestamp(buf)
     buf_T	*buf;
 {
-    ml_upd_block0(buf, TRUE);
+    ml_upd_block0(buf, UB_FNAME);
+}
+
+/*
+ * Return FAIL when the ID of "b0p" is wrong.
+ */
+    static int
+ml_check_b0_id(b0p)
+    ZERO_BL	*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)
+	    )
+	return FAIL;
+    return OK;
 }
 
 /*
  * Update the timestamp or the B0_SAME_DIR flag of the .swp file.
  */
     static void
-ml_upd_block0(buf, set_fname)
+ml_upd_block0(buf, what)
     buf_T	*buf;
-    int		set_fname;
+    upd_block0_T what;
 {
     memfile_T	*mfp;
     bhdr_T	*hp;
@@ -699,13 +905,17 @@ ml_upd_block0(buf, set_fname)
     if (mfp == NULL || (hp = mf_get(mfp, (blocknr_T)0, 1)) == NULL)
 	return;
     b0p = (ZERO_BL *)(hp->bh_data);
-    if (b0p->b0_id[0] != BLOCK0_ID0 || b0p->b0_id[1] != BLOCK0_ID1)
+    if (ml_check_b0_id(b0p) == FAIL)
 	EMSG(_("E304: ml_upd_block0(): Didn't get block 0??"));
     else
     {
-	if (set_fname)
+	if (what == UB_FNAME)
 	    set_b0_fname(b0p, buf);
-	else
+#ifdef FEAT_CRYPT
+	else if (what == UB_CRYPT)
+	    ml_set_b0_crypt(buf, b0p);
+#endif
+	else /* what == UB_SAME_DIR */
 	    set_b0_dir_flag(b0p, buf);
     }
     mf_put(mfp, hp, TRUE, FALSE);
@@ -731,7 +941,7 @@ set_b0_fname(b0p, buf)
 	/* Systems that cannot translate "~user" back into a path: copy the
 	 * file name unmodified.  Do use slashes instead of backslashes for
 	 * portability. */
-	vim_strncpy(b0p->b0_fname, buf->b_ffname, B0_FNAME_SIZE - 1);
+	vim_strncpy(b0p->b0_fname, buf->b_ffname, B0_FNAME_SIZE_CRYPT - 1);
 # ifdef BACKSLASH_IN_FILENAME
 	forward_slash(b0p->b0_fname);
 # endif
@@ -746,14 +956,16 @@ set_b0_fname(b0p, buf)
 	 * First replace home dir path with "~/" with home_replace().
 	 * Then insert the user name to get "~user/".
 	 */
-	home_replace(NULL, buf->b_ffname, b0p->b0_fname, B0_FNAME_SIZE, TRUE);
+	home_replace(NULL, buf->b_ffname, b0p->b0_fname,
+						   B0_FNAME_SIZE_CRYPT, TRUE);
 	if (b0p->b0_fname[0] == '~')
 	{
 	    flen = STRLEN(b0p->b0_fname);
 	    /* If there is no user name or it is too long, don't use "~/" */
 	    if (get_user_name(uname, B0_UNAME_SIZE) == FAIL
-			 || (ulen = STRLEN(uname)) + flen > B0_FNAME_SIZE - 1)
-		vim_strncpy(b0p->b0_fname, buf->b_ffname, B0_FNAME_SIZE - 1);
+		   || (ulen = STRLEN(uname)) + flen > B0_FNAME_SIZE_CRYPT - 1)
+		vim_strncpy(b0p->b0_fname, buf->b_ffname,
+						     B0_FNAME_SIZE_CRYPT - 1);
 	    else
 	    {
 		mch_memmove(b0p->b0_fname + ulen + 1, b0p->b0_fname + 1, flen);
@@ -816,15 +1028,24 @@ add_b0_fenc(b0p, buf)
     buf_T	*buf;
 {
     int		n;
+    int		size = B0_FNAME_SIZE_NOCRYPT;
+
+# ifdef FEAT_CRYPT
+    /* Without encryption use the same offset as in Vim 7.2 to be compatible.
+     * With encryption it's OK to move elsewhere, the swap file is not
+     * compatible anyway. */
+    if (*buf->b_p_key != NUL)
+	size = B0_FNAME_SIZE_CRYPT;
+# endif
 
     n = (int)STRLEN(buf->b_p_fenc);
-    if (STRLEN(b0p->b0_fname) + n + 1 > B0_FNAME_SIZE)
+    if ((int)STRLEN(b0p->b0_fname) + n + 1 > size)
 	b0p->b0_flags &= ~B0_HAS_FENC;
     else
     {
-	mch_memmove((char *)b0p->b0_fname + B0_FNAME_SIZE - n,
+	mch_memmove((char *)b0p->b0_fname + size - n,
 					    (char *)buf->b_p_fenc, (size_t)n);
-	*(b0p->b0_fname + B0_FNAME_SIZE - n - 1) = NUL;
+	*(b0p->b0_fname + size - n - 1) = NUL;
 	b0p->b0_flags |= B0_HAS_FENC;
     }
 }
@@ -832,7 +1053,7 @@ add_b0_fenc(b0p, buf)
 
 
 /*
- * try to recover curbuf from the .swp file
+ * Try to recover curbuf from the .swp file.
  */
     void
 ml_recover()
@@ -840,10 +1061,14 @@ ml_recover()
     buf_T	*buf = NULL;
     memfile_T	*mfp = NULL;
     char_u	*fname;
+    char_u	*fname_used = NULL;
     bhdr_T	*hp = NULL;
     ZERO_BL	*b0p;
     int		b0_ff;
     char_u	*b0_fenc = NULL;
+#ifdef FEAT_CRYPT
+    int		b0_cm = -1;
+#endif
     PTR_BL	*pp;
     DATA_BL	*dp;
     infoptr_T	*ip;
@@ -892,14 +1117,14 @@ ml_recover()
 		&& ASCII_ISALPHA(fname[len - 1]))
     {
 	directly = TRUE;
-	fname = vim_strsave(fname); /* make a copy for mf_open() */
+	fname_used = vim_strsave(fname); /* make a copy for mf_open() */
     }
     else
     {
 	directly = FALSE;
 
 	/* count the number of matching swap files */
-	len = recover_names(&fname, FALSE, 0);
+	len = recover_names(fname, FALSE, 0, NULL);
 	if (len == 0)		    /* no swap files found */
 	{
 	    EMSG2(_("E305: No swap file found for %s"), fname);
@@ -910,7 +1135,7 @@ ml_recover()
 	else			    /* several swap files found, choose */
 	{
 	    /* list the names of the swap files */
-	    (void)recover_names(&fname, TRUE, 0);
+	    (void)recover_names(fname, TRUE, 0, NULL);
 	    msg_putchar('\n');
 	    MSG_PUTS(_("Enter number of swap file to use (0 to quit): "));
 	    i = get_number(FALSE, NULL);
@@ -918,28 +1143,26 @@ ml_recover()
 		goto theend;
 	}
 	/* get the swap file name that will be used */
-	(void)recover_names(&fname, FALSE, i);
+	(void)recover_names(fname, FALSE, i, &fname_used);
     }
-    if (fname == NULL)
+    if (fname_used == NULL)
 	goto theend;			/* out of memory */
 
     /* When called from main() still need to initialize storage structure */
     if (called_from_main && ml_open(curbuf) == FAIL)
 	getout(1);
 
-/*
- * allocate a buffer structure (only the memline in it is really used)
- */
+    /*
+     * Allocate a buffer structure for the swap file that is used for recovery.
+     * Only the memline in it is really used.
+     */
     buf = (buf_T *)alloc((unsigned)sizeof(buf_T));
     if (buf == NULL)
-    {
-	vim_free(fname);
 	goto theend;
-    }
-
-/*
- * init fields in memline struct
- */
+
+    /*
+     * init fields in memline struct
+     */
     buf->b_ml.ml_stack_size = 0;	/* no stack yet */
     buf->b_ml.ml_stack = NULL;		/* no stack yet */
     buf->b_ml.ml_stack_top = 0;		/* nothing in the stack */
@@ -947,23 +1170,24 @@ ml_recover()
     buf->b_ml.ml_locked = NULL;		/* no locked block */
     buf->b_ml.ml_flags = 0;
 
-/*
- * open the memfile from the old swap file
- */
-    p = vim_strsave(fname);		/* save fname for the message
-					   (mf_open() may free fname) */
-    mfp = mf_open(fname, O_RDONLY);	/* consumes fname! */
+    /*
+     * open the memfile from the old swap file
+     */
+    p = vim_strsave(fname_used); /* save "fname_used" for the message:
+				    mf_open() will consume "fname_used"! */
+    mfp = mf_open(fname_used, O_RDONLY);
+    fname_used = p;
     if (mfp == NULL || mfp->mf_fd < 0)
     {
-	if (p != NULL)
-	{
-	    EMSG2(_("E306: Cannot open %s"), p);
-	    vim_free(p);
-	}
+	if (fname_used != NULL)
+	    EMSG2(_("E306: Cannot open %s"), fname_used);
 	goto theend;
     }
-    vim_free(p);
     buf->b_ml.ml_mfp = mfp;
+#ifdef FEAT_CRYPT
+    mfp->mf_buffer = buf;
+    buf->b_p_key = empty_option;
+#endif
 
     /*
      * The page size set in mf_open() might be different from the page size
@@ -973,16 +1197,15 @@ ml_recover()
      */
     mfp->mf_page_size = MIN_SWAP_PAGE_SIZE;
 
-/*
- * try to read block 0
- */
+    /*
+     * try to read block 0
+     */
     if ((hp = mf_get(mfp, (blocknr_T)0, 1)) == NULL)
     {
 	msg_start();
 	MSG_PUTS_ATTR(_("Unable to read block 0 from "), attr | MSG_HIST);
 	msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST);
-	MSG_PUTS_ATTR(
-	   _("\nMaybe no changes were made or Vim did not update the swap file."),
+	MSG_PUTS_ATTR(_("\nMaybe no changes were made or Vim did not update the swap file."),
 		attr | MSG_HIST);
 	msg_end();
 	goto theend;
@@ -998,7 +1221,7 @@ ml_recover()
 	msg_end();
 	goto theend;
     }
-    if (b0p->b0_id[0] != BLOCK0_ID0 || b0p->b0_id[1] != BLOCK0_ID1)
+    if (ml_check_b0_id(b0p) == FAIL)
     {
 	EMSG2(_("E307: %s does not look like a Vim swap file"), mfp->mf_fname);
 	goto theend;
@@ -1024,6 +1247,22 @@ ml_recover()
 	goto theend;
     }
 
+#ifdef FEAT_CRYPT
+    if (b0p->b0_id[1] == BLOCK0_ID1_C0)
+	buf->b_p_cm = b0_cm = 0;
+    else if (b0p->b0_id[1] == BLOCK0_ID1_C1)
+    {
+	buf->b_p_cm = b0_cm = 1;
+	mch_memmove(mfp->mf_seed, &b0p->b0_seed, MF_SEED_LEN);
+    }
+#else
+    if (b0p->b0_id[1] != BLOCK0_ID1)
+    {
+	EMSG2(_("E000: %s is encrypted and this version of Vim does not support encryption"), mfp->mf_fname);
+	goto theend;
+    }
+#endif
+
     /*
      * If we guessed the wrong page size, we have to recalculate the
      * highest block number in the file.
@@ -1058,9 +1297,9 @@ ml_recover()
 	b0p = (ZERO_BL *)(hp->bh_data);
     }
 
-/*
- * If .swp file name given directly, use name from swap file for buffer.
- */
+    /*
+     * If .swp file name given directly, use name from swap file for buffer.
+     */
     if (directly)
     {
 	expand_env(b0p->b0_fname, NameBuff, MAXPATHL);
@@ -1078,9 +1317,9 @@ ml_recover()
     smsg((char_u *)_("Original file \"%s\""), NameBuff);
     msg_putchar('\n');
 
-/*
- * check date of swap file and original file
- */
+    /*
+     * check date of swap file and original file
+     */
     mtime = char_to_long(b0p->b0_mtime);
     if (curbuf->b_ffname != NULL
 	    && mch_stat((char *)curbuf->b_ffname, &org_stat) != -1
@@ -1096,10 +1335,16 @@ ml_recover()
     b0_ff = (b0p->b0_flags & B0_FF_MASK);
     if (b0p->b0_flags & B0_HAS_FENC)
     {
-	for (p = b0p->b0_fname + B0_FNAME_SIZE;
-				       p > b0p->b0_fname && p[-1] != NUL; --p)
+	int size = B0_FNAME_SIZE_NOCRYPT;
+
+#ifdef FEAT_CRYPT
+	/* Use the same size as in add_b0_fenc(). */
+	if (b0p->b0_id[1] != BLOCK0_ID1)
+	    size = B0_FNAME_SIZE_CRYPT;
+#endif
+	for (p = b0p->b0_fname + size; p > b0p->b0_fname && p[-1] != NUL; --p)
 	    ;
-	b0_fenc = vim_strnsave(p, (int)(b0p->b0_fname + B0_FNAME_SIZE - p));
+	b0_fenc = vim_strnsave(p, (int)(b0p->b0_fname + size - p));
     }
 
     mf_put(mfp, hp, FALSE, FALSE);	/* release block 0 */
@@ -1115,11 +1360,40 @@ ml_recover()
     /*
      * Try reading the original file to obtain the values of 'fileformat',
      * 'fileencoding', etc.  Ignore errors.  The text itself is not used.
+     * When the file is encrypted the user is asked to enter the key.
      */
     if (curbuf->b_ffname != NULL)
 	orig_file_status = readfile(curbuf->b_ffname, NULL, (linenr_T)0,
 			      (linenr_T)0, (linenr_T)MAXLNUM, NULL, READ_NEW);
 
+#ifdef FEAT_CRYPT
+    if (b0_cm >= 0)
+    {
+	/* Need to ask the user for the crypt key.  If this fails we continue
+	 * without a key, will probably get garbage text. */
+	if (*curbuf->b_p_key != NUL)
+	{
+	    smsg((char_u *)_("Swap file is encrypted: \"%s\""), fname_used);
+	    MSG_PUTS(_("\nIf you entered a new crypt key but did not write the text file,"));
+	    MSG_PUTS(_("\nenter the new crypt key."));
+	    MSG_PUTS(_("\nIf you wrote the text file after changing the crypt key press enter"));
+	    MSG_PUTS(_("\nto use the same key for text file and swap file"));
+	}
+	else
+	    smsg((char_u *)_(need_key_msg), fname_used);
+	buf->b_p_key = get_crypt_key(FALSE, FALSE);
+	if (buf->b_p_key == NULL)
+	    buf->b_p_key = curbuf->b_p_key;
+	else if (*buf->b_p_key == NUL)
+	{
+	    vim_free(buf->b_p_key);
+	    buf->b_p_key = curbuf->b_p_key;
+	}
+	if (buf->b_p_key == NULL)
+	    buf->b_p_key = empty_option;
+    }
+#endif
+
     /* Use the 'fileformat' and 'fileencoding' as stored in the swap file. */
     if (b0_ff != 0)
 	set_fileformat(b0_ff - 1, OPT_LOCAL);
@@ -1386,9 +1660,17 @@ ml_recover()
 	MSG_PUTS(_("\nYou may want to delete the .swp file now.\n\n"));
 	cmdline_row = msg_row;
     }
+#ifdef FEAT_CRYPT
+    if (*buf->b_p_key != NUL && STRCMP(curbuf->b_p_key, buf->b_p_key) != 0)
+    {
+	MSG_PUTS(_("Using crypt key from swap file for the text file.\n"));
+	set_option_value((char_u *)"key", 0L, buf->b_p_key, OPT_LOCAL);
+    }
+#endif
     redraw_curbuf_later(NOT_VALID);
 
 theend:
+    vim_free(fname_used);
     recoverymode = FALSE;
     if (mfp != NULL)
     {
@@ -1398,6 +1680,10 @@ theend:
     }
     if (buf != NULL)
     {
+#ifdef FEAT_CRYPT
+	if (buf->b_p_key != curbuf->b_p_key)
+	    free_string_option(buf->b_p_key);
+#endif
 	vim_free(buf->b_ml.ml_stack);
 	vim_free(buf);
     }
@@ -1424,10 +1710,11 @@ theend:
  * - find the name of the n'th swap file when recovering
  */
     int
-recover_names(fname, list, nr)
-    char_u	**fname;    /* base for swap file name */
-    int		list;	    /* when TRUE, list the swap file names */
-    int		nr;	    /* when non-zero, return nr'th swap file name */
+recover_names(fname, list, nr, fname_out)
+    char_u	*fname;		/* base for swap file name */
+    int		list;		/* when TRUE, list the swap file names */
+    int		nr;		/* when non-zero, return nr'th swap file name */
+    char_u	**fname_out;	/* result when "nr" > 0 */
 {
     int		num_names;
     char_u	*(names[6]);
@@ -1447,13 +1734,13 @@ recover_names(fname, list, nr)
     if (fname != NULL)
     {
 #ifdef HAVE_READLINK
-    /* Expand symlink in the file name, because the swap file is created with
-     * the actual file instead of with the symlink. */
-    if (resolve_symlink(*fname, fname_buf) == OK)
-	fname_res = fname_buf;
-    else
+	/* Expand symlink in the file name, because the swap file is created
+	 * with the actual file instead of with the symlink. */
+	if (resolve_symlink(fname, fname_buf) == OK)
+	    fname_res = fname_buf;
+	else
 #endif
-	fname_res = *fname;
+	    fname_res = fname;
     }
 
     if (list)
@@ -1480,7 +1767,7 @@ recover_names(fname, list, nr)
 
 	if (dir_name[0] == '.' && dir_name[1] == NUL)	/* check current dir */
 	{
-	    if (fname == NULL || *fname == NULL)
+	    if (fname == NULL)
 	    {
 #ifdef VMS
 		names[0] = vim_strsave((char_u *)"*_sw%");
@@ -1511,7 +1798,7 @@ recover_names(fname, list, nr)
 	}
 	else			    /* check directory dir_name */
 	{
-	    if (fname == NULL || *fname == NULL)
+	    if (fname == NULL)
 	    {
 #ifdef VMS
 		names[0] = concat_fnames(dir_name, (char_u *)"*_sw%", TRUE);
@@ -1583,8 +1870,7 @@ recover_names(fname, list, nr)
 	 * not able to execute the shell).
 	 * Try finding a swap file by simply adding ".swp" to the file name.
 	 */
-	if (*dirp == NUL && file_count + num_files == 0
-					   && fname != NULL && *fname != NULL)
+	if (*dirp == NUL && file_count + num_files == 0 && fname != NULL)
 	{
 	    struct stat	    st;
 	    char_u	    *swapname;
@@ -1637,7 +1923,8 @@ recover_names(fname, list, nr)
 	    file_count += num_files;
 	    if (nr <= file_count)
 	    {
-		*fname = vim_strsave(files[nr - 1 + num_files - file_count]);
+		*fname_out = vim_strsave(
+				      files[nr - 1 + num_files - file_count]);
 		dirp = (char_u *)"";		    /* stop searching */
 	    }
 	}
@@ -1645,7 +1932,7 @@ recover_names(fname, list, nr)
 	{
 	    if (dir_name[0] == '.' && dir_name[1] == NUL)
 	    {
-		if (fname == NULL || *fname == NULL)
+		if (fname == NULL)
 		    MSG_PUTS(_("   In current directory:\n"));
 		else
 		    MSG_PUTS(_("   Using specified name:\n"));
@@ -1772,7 +2059,7 @@ swapfile_info(fname)
 	    {
 		MSG_PUTS(_("         [from Vim version 3.0]"));
 	    }
-	    else if (b0.b0_id[0] != BLOCK0_ID0 || b0.b0_id[1] != BLOCK0_ID1)
+	    else if (ml_check_b0_id(&b0) == FAIL)
 	    {
 		MSG_PUTS(_("         [does not look like a Vim swap file]"));
 	    }
@@ -3478,11 +3765,11 @@ ml_find_line(buf, lnum, action)
 error_block:
     mf_put(mfp, hp, FALSE, FALSE);
 error_noblock:
-/*
- * If action is ML_DELETE or ML_INSERT we have to correct the tree for
- * the incremented/decremented line counts, because there won't be a line
- * inserted/deleted after all.
- */
+    /*
+     * If action is ML_DELETE or ML_INSERT we have to correct the tree for
+     * the incremented/decremented line counts, because there won't be a line
+     * inserted/deleted after all.
+     */
     if (action == ML_DELETE)
 	ml_lineadd(buf, 1);
     else if (action == ML_INSERT)
@@ -3505,10 +3792,10 @@ ml_add_stack(buf)
 
     top = buf->b_ml.ml_stack_top;
 
-	/* may have to increase the stack size */
+    /* may have to increase the stack size */
     if (top == buf->b_ml.ml_stack_size)
     {
-	CHECK(top > 0, _("Stack size increases"));	/* more than 5 levels??? */
+	CHECK(top > 0, _("Stack size increases")); /* more than 5 levels??? */
 
 	newstack = (infoptr_T *)alloc((unsigned)sizeof(infoptr_T) *
 					(buf->b_ml.ml_stack_size + STACK_INCR));
@@ -4518,6 +4805,132 @@ ml_setflags(buf)
     }
 }
 
+#if defined(FEAT_CRYPT) || defined(PROTO)
+/*
+ * If "data" points to a data block encrypt the text in it and return a copy
+ * in allocated memory.  Return NULL when out of memory.
+ * Otherwise return "data".
+ */
+    char_u *
+ml_encrypt_data(mfp, data, offset, size)
+    memfile_T	*mfp;
+    char_u	*data;
+    off_t	offset;
+    unsigned	size;
+{
+    DATA_BL	*dp = (DATA_BL *)data;
+    char_u	*head_end;
+    char_u	*text_start;
+    char_u	*new_data;
+    int		text_len;
+
+    if (dp->db_id != DATA_ID)
+	return data;
+
+    new_data = (char_u *)alloc(size);
+    if (new_data == NULL)
+	return NULL;
+    head_end = (char_u *)(&dp->db_index[dp->db_line_count]);
+    text_start = (char_u *)dp + dp->db_txt_start;
+    text_len = size - dp->db_txt_start;
+
+    /* Copy the header and the text. */
+    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();
+
+    /* Clear the gap. */
+    if (head_end < text_start)
+	vim_memset(new_data + (head_end - data), 0, text_start - head_end);
+
+    return new_data;
+}
+
+/*
+ * Decrypt the text in "data" if it points to a data block.
+ */
+    void
+ml_decrypt_data(mfp, data, offset, size)
+    memfile_T	*mfp;
+    char_u	*data;
+    off_t	offset;
+    unsigned	size;
+{
+    DATA_BL	*dp = (DATA_BL *)data;
+    char_u	*head_end;
+    char_u	*text_start;
+    int		text_len;
+
+    if (dp->db_id == DATA_ID)
+    {
+	head_end = (char_u *)(&dp->db_index[dp->db_line_count]);
+	text_start = (char_u *)dp + dp->db_txt_start;
+	text_len = dp->db_txt_end - dp->db_txt_start;
+
+	if (head_end > text_start || dp->db_txt_start > size
+						     || dp->db_txt_end > 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();
+    }
+}
+
+/*
+ * Prepare for encryption/decryption, using the key, seed and offset.
+ */
+    static void
+ml_crypt_prepare(mfp, offset, reading)
+    memfile_T	*mfp;
+    off_t	offset;
+    int		reading;
+{
+    buf_T	*buf = mfp->mf_buffer;
+    char_u	salt[50];
+    int		method;
+    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;
+	key = mfp->mf_old_key;
+	seed = mfp->mf_old_seed;
+    }
+    else
+    {
+	method = buf->b_p_cm;
+	key = buf->b_p_key;
+	seed = mfp->mf_seed;
+    }
+
+    use_crypt_method = method;  /* select pkzip or blowfish */
+    if (method == 0)
+    {
+	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, STRLEN(salt));
+	bf_ofb_init(seed, MF_SEED_LEN);
+    }
+}
+
+#endif
+
+
 #if defined(FEAT_BYTEOFF) || defined(PROTO)
 
 #define MLCS_MAXL 800	/* max no of lines in chunk */
--- a/src/misc2.c
+++ b/src/misc2.c
@@ -3746,6 +3746,59 @@ static ulg keys[3]; /* keys defining the
     keys[2] = CRC32(keys[2], (int)(keys[1] >> 24)); \
 }
 
+static int crypt_busy = 0;
+static ulg saved_keys[3];
+static int saved_crypt_method;
+
+/*
+ * Prepare for initializing encryption.  If already doing encryption then save
+ * the state.
+ * Must always be called symmetrycally 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 symmetrycally 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.
@@ -3894,6 +3947,8 @@ get_crypt_key(store, twice)
 
     /* since the user typed this, no need to wait for return */
     need_wait_return = FALSE;
+    if (msg_didout)
+	msg_putchar('\n');
     msg_didout = FALSE;
 
     free_crypt_key(p2);
--- a/src/option.c
+++ b/src/option.c
@@ -5969,13 +5969,18 @@ did_set_string_option(opt_idx, varp, new
 	}
     }
 
-#if defined(FEAT_CRYPT) && defined(FEAT_CMDHIST)
+#if defined(FEAT_CRYPT)
     /* 'cryptkey' */
     else if (gvarp == &p_key)
     {
+# if defined(FEAT_CMDHIST)
 	/* Make sure the ":set" command doesn't show the new value in the
 	 * history. */
 	remove_key_from_history();
+# endif
+	if (STRCMP(curbuf->b_p_key, oldval) != 0)
+	    /* Need to update the swapfile. */
+	    ml_set_crypt_key(curbuf, oldval, curbuf->b_p_cm);
     }
 #endif
 
@@ -7941,15 +7946,19 @@ set_num_option(opt_idx, varp, value, err
 	if (curbuf->b_p_cm < 0)
 	{
 	    errmsg = e_positive;
-	    curbuf->b_p_cm = 0;
+	    curbuf->b_p_cm = old_value;
 	}
 	if (curbuf->b_p_cm > 1)
 	{
 	    errmsg = e_invarg;
-	    curbuf->b_p_cm = 1;
+	    curbuf->b_p_cm = old_value;
 	}
 	if (curbuf->b_p_cm > 0 && blowfish_self_test() == FAIL)
-	    curbuf->b_p_cm = 0;
+	    curbuf->b_p_cm = old_value;
+
+	if (curbuf->b_p_cm != old_value && *curbuf->b_p_key != NUL)
+	    /* Need to update the swapfile. */
+	    ml_set_crypt_key(curbuf, curbuf->b_p_key, old_value);
     }
 #endif
 
--- a/src/proto/blowfish.pro
+++ b/src/proto/blowfish.pro
@@ -4,5 +4,7 @@ void bf_ofb_init __ARGS((char_u *iv, int
 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));
 int blowfish_self_test __ARGS((void));
 /* vim: set ft=c : */
--- a/src/proto/memline.pro
+++ b/src/proto/memline.pro
@@ -1,5 +1,6 @@
 /* memline.c */
 int ml_open __ARGS((buf_T *buf));
+void ml_set_crypt_key __ARGS((buf_T *buf, char_u *old_key, int old_cm));
 void ml_setname __ARGS((buf_T *buf));
 void ml_open_files __ARGS((void));
 void ml_open_file __ARGS((buf_T *buf));
@@ -9,7 +10,7 @@ void ml_close_all __ARGS((int del_file))
 void ml_close_notmod __ARGS((void));
 void ml_timestamp __ARGS((buf_T *buf));
 void ml_recover __ARGS((void));
-int recover_names __ARGS((char_u **fname, int list, int nr));
+int recover_names __ARGS((char_u *fname, int list, int nr, char_u **fname_out));
 void ml_sync_all __ARGS((int check_file, int check_char));
 void ml_preserve __ARGS((buf_T *buf, int message));
 char_u *ml_get __ARGS((linenr_T lnum));
@@ -29,6 +30,8 @@ int resolve_symlink __ARGS((char_u *fnam
 char_u *makeswapname __ARGS((char_u *fname, char_u *ffname, buf_T *buf, char_u *dir_name));
 char_u *get_file_in_dir __ARGS((char_u *fname, char_u *dname));
 void ml_setflags __ARGS((buf_T *buf));
+char_u *ml_encrypt_data __ARGS((memfile_T *mfp, char_u *data, off_t offset, unsigned size));
+void ml_decrypt_data __ARGS((memfile_T *mfp, char_u *data, off_t offset, unsigned size));
 long ml_find_line_or_offset __ARGS((buf_T *buf, linenr_T lnum, long *offp));
 void goto_byte __ARGS((long cnt));
 /* vim: set ft=c : */
--- a/src/proto/misc2.pro
+++ b/src/proto/misc2.pro
@@ -80,6 +80,8 @@ 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));
+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));
--- a/src/sha256.c
+++ b/src/sha256.c
@@ -402,7 +402,8 @@ get_some_time()
 }
 
 /*
- * set header = sha2_seed(random_data);
+ * Fill "header[header_len]" with random_data.
+ * Also "salt[salt_len]" when "salt" is not NULL.
  */
     void
 sha2_seed(header, header_len, salt, salt_len)
@@ -429,8 +430,9 @@ sha2_seed(header, header_len, salt, salt
 	header[i] = sha256sum[i % sizeof(sha256sum)];
 
     /* put remaining block into salt. */
-    for (i = 0; i < salt_len; i++)
-	salt[i] = sha256sum[(i + header_len) % sizeof(sha256sum)];
+    if (salt != NULL)
+	for (i = 0; i < salt_len; i++)
+	    salt[i] = sha256sum[(i + header_len) % sizeof(sha256sum)];
 }
 
 #endif /* FEAT_CRYPT */
--- a/src/structs.h
+++ b/src/structs.h
@@ -392,7 +392,7 @@ struct block_hdr
     bhdr_T	*bh_prev;	    /* previous block_hdr in used list */
     bhdr_T	*bh_hash_next;	    /* next block_hdr in hash list */
     bhdr_T	*bh_hash_prev;	    /* previous block_hdr in hash list */
-    blocknr_T	bh_bnum;		/* block number */
+    blocknr_T	bh_bnum;	    /* block number */
     char_u	*bh_data;	    /* pointer to memory (for used block) */
     int		bh_page_count;	    /* number of pages in this block */
 
@@ -491,12 +491,15 @@ typedef struct
 # endif
 } cmdmod_T;
 
+typedef struct file_buffer buf_T;  /* forward declaration */
+
 /*
  * Simplistic hashing scheme to quickly locate the blocks in the used list.
  * 64 blocks are found directly (64 * 4K = 256K, most files are smaller).
  */
 #define MEMHASHSIZE	64
 #define MEMHASH(nr)	((nr) & (MEMHASHSIZE - 1))
+#define MF_SEED_LEN	8
 
 struct memfile
 {
@@ -516,6 +519,16 @@ struct memfile
     blocknr_T	mf_infile_count;	/* number of pages in the file */
     unsigned	mf_page_size;		/* number of bytes in a page */
     int		mf_dirty;		/* TRUE if there are dirty blocks */
+#ifdef FEAT_CRYPT
+    buf_T	*mf_buffer;		/* bufer this memfile is for */
+    char_u	mf_seed[MF_SEED_LEN];	/* seed for encryption */
+
+    /* Values for key, method and seed used for reading data blocks when
+     * updating for a newly set key or method. Only when mf_old_key != NULL. */
+    char_u	*mf_old_key;
+    int		mf_old_cm;
+    char_u	mf_old_seed[MF_SEED_LEN];
+#endif
 };
 
 /*
@@ -1229,8 +1242,6 @@ typedef struct {
  * A buffer is new if the associated file has never been loaded yet.
  */
 
-typedef struct file_buffer buf_T;
-
 struct file_buffer
 {
     memline_T	b_ml;		/* associated memline (also contains line
--- a/src/testdir/test72.in
+++ b/src/testdir/test72.in
@@ -6,7 +6,7 @@ STARTTEST
 :so small.vim
 :"
 :" Test 'undofile': first a simple one-line change.
-:set nocp ul=100 undofile
+:set nocp ul=100 undofile nomore
 :e! Xtestfile
 ggdGithis is one line:set ul=100
 :s/one/ONE/
--- a/src/undo.c
+++ b/src/undo.c
@@ -886,7 +886,10 @@ serialize_header(fp, buf, hash)
 	len = (int)fwrite(header, (size_t)header_len, (size_t)1, fp);
 	vim_free(header);
 	if (len != 1)
+	{
+	    crypt_pop_state();
 	    return FAIL;
+	}
     }
     else
 #endif
@@ -1240,6 +1243,9 @@ u_write_undo(name, forceit, buf, hash)
     struct stat	st_old;
     struct stat	st_new;
 #endif
+#ifdef FEAT_CRYPT
+    int		do_crypt = FALSE;
+#endif
 
     if (name == NULL)
     {
@@ -1397,6 +1403,10 @@ u_write_undo(name, forceit, buf, hash)
      */
     if (serialize_header(fp, buf, hash) == FAIL)
 	goto write_error;
+#ifdef FEAT_CRYPT
+    if (*buf->b_p_key)
+	do_crypt = TRUE;
+#endif
 
     /*
      * Iteratively serialize UHPs and their UEPs from the top down.
@@ -1462,6 +1472,10 @@ write_error:
 #endif
 
 theend:
+#ifdef FEAT_CRYPT
+    if (do_crypt)
+	crypt_pop_state();
+#endif
     if (file_name != name)
 	vim_free(file_name);
 }
@@ -1505,6 +1519,9 @@ u_read_undo(name, hash, orig_name)
     struct stat	st_orig;
     struct stat	st_undo;
 #endif
+#ifdef FEAT_CRYPT
+    int		do_decrypt = FALSE;
+#endif
 
     if (name == NULL)
     {
@@ -1572,6 +1589,7 @@ u_read_undo(name, hash, orig_name)
 	    EMSG2(_("E826: Undo file decryption failed: %s"), file_name);
 	    goto error;
 	}
+	do_decrypt = TRUE;
 #else
         EMSG2(_("E827: Undo file is encrypted: %s"), file_name);
         goto error;
@@ -1776,6 +1794,10 @@ error:
     }
 
 theend:
+#ifdef FEAT_CRYPT
+    if (do_decrypt)
+	crypt_pop_state();
+#endif
     if (fp != NULL)
         fclose(fp);
     if (file_name != name)
--- a/src/workshop.c
+++ b/src/workshop.c
@@ -1304,13 +1304,15 @@ load_window(
     }
     else
     {
+#ifdef FEAT_WINDOWS
 	/* buf is in a window */
 	if (win != curwin)
 	{
 	    win_enter(win, False);
-	    /* wsdebug("load_window: window endter %s\n",
+	    /* wsdebug("load_window: window enter %s\n",
 		    win->w_buffer->b_sfname); */
 	}
+#endif
 	if (lnum > 0 && win->w_cursor.lnum != lnum)
 	{
 	    warp_to_pc(lnum);