From a8ffcbbf5d6070380e41b3d0841c3944396a27c0 Mon Sep 17 00:00:00 2001
From: Bram Moolenaar <Bram@vim.org>
Date: Mon, 21 Jun 2010 06:15:46 +0200
Subject: [PATCH] Crypt the swapfile.

---
 runtime/doc/editing.txt  |  10 +-
 runtime/doc/helphelp.txt |  22 +-
 runtime/doc/recover.txt  |  39 +++
 runtime/doc/tags         |   4 +-
 runtime/doc/todo.txt     |  22 +-
 runtime/doc/usr_11.txt   |   2 +
 runtime/syntax/c.vim     |   2 +-
 src/blowfish.c           |  42 ++-
 src/fileio.c             |  43 ++-
 src/globals.h            |   4 +
 src/main.c               |   2 +-
 src/memfile.c            |  54 +++-
 src/memline.c            | 599 +++++++++++++++++++++++++++++++++------
 src/misc2.c              |  55 ++++
 src/option.c             |  17 +-
 src/proto/blowfish.pro   |   2 +
 src/proto/memline.pro    |   5 +-
 src/proto/misc2.pro      |   2 +
 src/sha256.c             |   8 +-
 src/structs.h            |  17 +-
 src/testdir/test72.in    |   2 +-
 src/undo.c               |  22 ++
 src/workshop.c           |   4 +-
 23 files changed, 824 insertions(+), 155 deletions(-)

diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt
index a10e8220a7..a89d8bb8de 100644
--- a/runtime/doc/editing.txt
+++ b/runtime/doc/editing.txt
@@ -1332,10 +1332,12 @@ There are a few things to remember when editing binary files:
 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!
diff --git a/runtime/doc/helphelp.txt b/runtime/doc/helphelp.txt
index eea639fd21..8fa0a2ddc4 100644
--- a/runtime/doc/helphelp.txt
+++ b/runtime/doc/helphelp.txt
@@ -119,9 +119,9 @@ Help on help files					*helphelp*
 							*: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 have the following format:
 
 *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"
diff --git a/runtime/doc/recover.txt b/runtime/doc/recover.txt
index fd960eb121..f088c0824c 100644
--- a/runtime/doc/recover.txt
+++ b/runtime/doc/recover.txt
@@ -188,4 +188,43 @@ will continue to get warning messages that the ".swp" file already exists.
 
 {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:
diff --git a/runtime/doc/tags b/runtime/doc/tags
index ec4c8abc96..936cb5e386 100644
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -2577,6 +2577,7 @@ $VIMRUNTIME	starting.txt	/*$VIMRUNTIME*
 :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-cursor-keys*
 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*
diff --git a/runtime/doc/todo.txt b/runtime/doc/todo.txt
index 73f4d51d14..78ad818aec 100644
--- 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) 
diff --git a/runtime/doc/usr_11.txt b/runtime/doc/usr_11.txt
index c6c75b1bf9..4c40408057 100644
--- a/runtime/doc/usr_11.txt
+++ b/runtime/doc/usr_11.txt
@@ -283,6 +283,8 @@ machines.  Therefore, don't rely on Vim always warning you.
 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
 
diff --git a/runtime/syntax/c.vim b/runtime/syntax/c.vim
index 94562127c2..a0b94ae973 100644
--- 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")
diff --git a/src/blowfish.c b/src/blowfish.c
index f0b97b7b90..c8e68d2244 100644
--- 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.
diff --git a/src/fileio.c b/src/fileio.c
index aad76170ef..b099461675 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -64,7 +64,7 @@ static void check_marks_read __ARGS((void));
 #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, filesizep, newfile, did_ask)
     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, filesizep, newfile, did_ask)
 		 * 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, filesizep, newfile, did_ask)
 	    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, filesizep, newfile, did_ask)
 	    /* 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)
diff --git a/src/globals.h b/src/globals.h
index f4ec25713c..c9bfc1fe87 100644
--- 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)
  */
diff --git a/src/main.c b/src/main.c
index f4e1fde195..0f29255864 100644
--- 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);
     }
 
diff --git a/src/memfile.c b/src/memfile.c
index 5412a61456..c6f5fdf8d0 100644
--- a/src/memfile.c
+++ b/src/memfile.c
@@ -85,6 +85,7 @@ static void mf_ins_free __ARGS((memfile_T *, bhdr_T *));
 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
@@ -1133,6 +1143,42 @@ mf_write(mfp, hp)
     return OK;
 }
 
+/*
+ * 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
  *
@@ -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.
  */
diff --git a/src/memline.c b/src/memline.c
index d9043dce63..75800b15bd 100644
--- a/src/memline.c
+++ b/src/memline.c
@@ -65,10 +65,12 @@ typedef struct pointer_block	PTR_BL;	    /* contents of a pointer block */
 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 */
diff --git a/src/misc2.c b/src/misc2.c
index e0eea68b52..1eb4e8a160 100644
--- a/src/misc2.c
+++ b/src/misc2.c
@@ -3746,6 +3746,59 @@ static ulg keys[3]; /* keys defining the pseudo-random sequence */
     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);
diff --git a/src/option.c b/src/option.c
index d06381cb84..face9fde99 100644
--- a/src/option.c
+++ b/src/option.c
@@ -5969,13 +5969,18 @@ did_set_string_option(opt_idx, varp, new_value_alloced, oldval, errbuf,
 	}
     }
 
-#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, errbuf, errbuflen, opt_flags)
 	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
 
diff --git a/src/proto/blowfish.pro b/src/proto/blowfish.pro
index ba18176fbc..51e4fa9ec5 100644
--- a/src/proto/blowfish.pro
+++ b/src/proto/blowfish.pro
@@ -4,5 +4,7 @@ void bf_ofb_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));
 int blowfish_self_test __ARGS((void));
 /* vim: set ft=c : */
diff --git a/src/proto/memline.pro b/src/proto/memline.pro
index 4d50671298..62a3ce6408 100644
--- 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 *fname, char_u *buf));
 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 : */
diff --git a/src/proto/misc2.pro b/src/proto/misc2.pro
index 6b292bb81f..8d3397fc12 100644
--- 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));
diff --git a/src/sha256.c b/src/sha256.c
index c24e8b8f83..3379620f38 100644
--- 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_len)
 	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 */
diff --git a/src/structs.h b/src/structs.h
index 4f72f998b3..a1c7e9d9a8 100644
--- 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
diff --git a/src/testdir/test72.in b/src/testdir/test72.in
index b6c6bfab3c..ada2654b26 100644
--- 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/
diff --git a/src/undo.c b/src/undo.c
index 07412acc5c..45a6a23078 100644
--- 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)
diff --git a/src/workshop.c b/src/workshop.c
index cd98914bcf..334e6afff8 100644
--- 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);
-- 
GitLab