Sunday, February 17, 2008

Interfacing VIM with GnuPG Encrypted Files

Over the years the editor I have become the most familiar with is vim. It provides features that allow the software development process to go smoothly, such as split screens, syntax highlighting, integration with cscope tags, function folding, and more. By applying configuration directives to your ~/.vimrc file, you can instruct vim to perform some nice preprocessing functions against files. This blog post illustrates a ~/.vimrc tweak (originally from Wouter Hanegraaff) that allows vim to leverage GnuPG to decrypt a previously encrypted file, allow edits to be made, and then re-encrypted before it is written back to disk. Here is the section of the .vimrc file to allow such transparent encryption and editing:

" Transparent editing of gpg encrypted files.
" By Wouter Hanegraaff <>
augroup encrypted
" First make sure nothing is written to ~/.viminfo while editing
" an encrypted file.
autocmd BufReadPre,FileReadPre *.gpg set viminfo=
" We don't want a swap file, as it writes unencrypted data to disk
autocmd BufReadPre,FileReadPre *.gpg set noswapfile
" Switch to binary mode to read the encrypted file
autocmd BufReadPre,FileReadPre *.gpg set bin
autocmd BufReadPre,FileReadPre *.gpg let ch_save = &ch|set ch=2
autocmd BufReadPost,FileReadPost *.gpg '[,']!gpg --decrypt 2> /dev/null
" Switch to normal mode for editing
autocmd BufReadPost,FileReadPost *.gpg set nobin
autocmd BufReadPost,FileReadPost *.gpg let &ch = ch_save|unlet ch_save
autocmd BufReadPost,FileReadPost *.gpg execute ":doautocmd BufReadPost " . expand("%:r")
" Convert all text to encrypted text before writing
autocmd BufWritePre,FileWritePre *.gpg '[,']!gpg --default-recipient-self -ae 2>/dev/null
" Undo the encryption so we are back in the normal text, directly
" after the file has been written.
autocmd BufWritePost,FileWritePost *.gpg u

You can combine this vim tweak with gpgdir to maintain recursively encrypted directories, and just edit the files directly. For example, the following sequence of commands shows the creation of an encrypted file and how vim then interfaces with GnuPG to allow transparent editing:

$ cat > somefile
private data
more private data
$ gpg -e somefile
$ wipe somefile
Okay to WIPE 1 regular file ? (Yes/No) yes
Operation finished.
1 file wiped and 0 special files ignored in 0 directories, 0 symlinks removed \
but not followed, 0 errors occured.
$ ls -l somefile.gpg
-rw-r--r-- 1 mbr mbr 618 2008-02-17 01:52 somefile.gpg
$ vim somefile.gpg
"somefile.gpg" [noeol] 3L, 618C

You need a passphrase to unlock the secret key for
user: "Michael Rash <>"
2048-bit ELG-E key, ID 1234ABCD, created 2007-05-01 (main key ID ABCD1234)

Enter passphrase:

<apply edits now, and hit :wq >

$ ls -l somefile.gpg
-rw-r--r-- 1 mbr mbr 932 2008-02-17 01:55 somefile.gpg

As you can see from the output above, the file was modified (and the original file somefile was deleted using wipe).

Finally, the 1.6 release of gpgdir is ready for download. This is a bugfix release that restores the exclusion of previously encrypted files from the file selection process, and includes a few minor enhancements as well. Here is the ChangeLog:

  • Bugfix to not include previously encrypted files (i.e. those with a .gpg extension) in the encryption/decryption file list. This bug was introduced in gpgdir-1.5 when a change was made to ignore ascii-armored files.

  • Added added LC_ALL=C locale setting for the script (this should help to ensure gpgdir is properly installed on most systems). Two new command line arguments --LC_ALL and --no-LC_ALL also allow the locale setting to be changed or not used at all.

  • Added --Exclude-mod-regex option to the script so that it is possible to force the exclusion of perl modules that gpgdir would normally install. This is useful for ensuring that gpgdir references perl modules that are already installed in the system perl library tree instead of using those that are installed in /usr/lib/gpgdir.

  • Updated to display command line usage warnings without automatically displaying the entire usage() page (which is quite long).

No comments: