Introducing gpg-tui, a Terminal User Interface for GnuPG

7 minute read Published: 2021-05-29

gpg-tui is a TUI for managing the GnuPG keys πŸ” In this post, I'm giving a brief introduction to the project as well as describing the thought process and main development challenges behind it.

Logo

https://github.com/orhun/gpg-tui

Background

GnuPG is a well-known implementation of the OpenPGP standard which is been used for years in various communities and projects. It's also known as "GPG", which is the name of the command line tool that makes it easier to integrate with other applications. It is battle-tested over the years and has a wealth of frontend applications and libraries.

My past with GnuPG is roughly the same with an ordinary developer who is into programming, open source, and related concepts. So I cannot say that I was using it for more than encryption/decryption/verification of files/emails and different types of authentication. It is a tool basically for securing the communication for me.

But some things about gpg, the CLI tool, were bugging me. I remember times that I have to run multiple commands to get fully informed about a key. Or I have to process its output to get the information that I needed. (using grep, sed, etc.) Not to mention that some of the output of gpg is quite confusing if you're unfamiliar with the GnuPG terms. So for years, I was thinking that it'd be nice to have a more user-friendly and interactive tool to handle the tasks that gpg is trying to accomplish. These tasks include key management operations such as listing (showing subkeys, user ids, signatures), signing, deleting keys, etc.

Plan

So I decided to write this tool in Rust, using tui-rs and bindings for the GPGME library.

GPGME uses GnuPG's OpenPGP backend as default to provide a high-level crypto API for various operations including key management, which was the thing I needed. And I have written a TUI program using tui-rs before, so why not use it again?

Implementation

I'd like to point out the 2 main issues that I've dealt with during the development process.

Listing Format

I started with the very basic: listing keys. Thankfully GPGME had an example for it, so it was supposed to be easy to implement.

Output from GPGME example:

keyid   : E19F76D037BD65B6
fpr     : B14085A20355B74DE0CE0FA1E19F76D037BD65B6
caps    : esc
flags   :
userid 0: Example Key <example@key>
valid  0: Validity::Ultimate(5)
userid 1: Other User ID <example@key>
valid  1: Validity::Ultimate(5)

Ta-da! It's done.

Well, no. It was good and well until I reached the point where I realized I should come up with my own listing format. If I use this format or the format that gpg -k uses, I'm afraid I can't extend it for my needs. So I played around with different formats a little bit and found an optimal one that can expand/shrink to show more/fewer details about an individual key. It consists of two columns which are key information and user information:

[sc--] rsa3072/B14085A20355B74DE0CE0FA1E19F76D037BD65B6  β”‚  [u] Example Key <example@key>
|      └─(2021-05-14)                                    β”‚   β”‚  └─[13] selfsig (2021-05-16)
[--e-] rsa3072/E56CAC142AE5A979BEECB00FB4F68595CAD4E7E5  β”‚   └─[u] Other User ID <example@key>
       └─(2021-05-14)                                              β”œβ”€[13] selfsig (2021-05-16)
                                                                   └─[10] 84C39331F6F85326 Other Signer Key <example@signer> (2021-05-16)

Thanks to tree-like output, everything was clearer and it was possible to show more detail without worrying about making the output look complicated.

Here's the gpg -k output of the same key:

pub   rsa3072 2021-05-14 [SC]
      B14085A20355B74DE0CE0FA1E19F76D037BD65B6
uid           [ultimate] Example Key <example@key>
uid           [ultimate] Other User ID <example@key>
sub   rsa3072 2021-05-14 [E]

I explain this custom format in detail in the project's documentation.

After figuring out the format that I think the most suitable for the key listing, I made it possible to toggle the detail level that list entries shows by assigning a key to it. So it will be not only possible to interactively select/scroll these keys, the user was also supposed to be able to see the other user ids and signatures if they want. That was a plus one.

Listing Keys

GPG Fallback

My next step was figuring out which operations that I wanted to support. I knew that gpg manpage had a section called "How to manage your keys", so I decided to go along with it. The plan is to support all of these features, with the extensions/flexibility that having a TUI provides.

After implementing a couple of those features using GPGME, I realized it was not possible to do it all due to various reasons.

well, whatever.

I thought I can get help from gpg for these cases. It's already a powerful tool, so why not use it instead of designing custom tabs/widgets/layouts for each feature? I can pause the TUI during the execution of a gpg command and resume it afterward. Seems like a good idea.

So I did. And I'm pretty happy with the result. Until I decide to extend the TUI for these operations, I think it's better to get help from 3-4 external gpg commands.

GPG Fallback

"GPG on steroids"

And so my journey continued and I implemented the commonly used gpg operations for my interface. I also added some extra features to use the real power of having a TUI. And since I'm designing a TUI for GPG, I named the project gpg-tui. Just simple.

Here's the end result:

Demo

As of the initial release, gpg-tui can do the following:

With lots of additional TUI features like copy/paste mode, options menu, and command prompt!

See README.md for more information.

Endnote

My goal for the next releases is to implement new features that will make key management even easier. I wish a project that is born solely from my needs will also be useful for other people.

Also if you liked gpg-tui and/or my other projects, consider consider supporting me on GitHub Sponsors or Patreon.

(I recently got my first ever patron so I'd like to thank Ethem for their nice surprise and support :D)

Have a good day!