Apocryphon

2022-11-30 / 2023-09-25

Wile full-featured password managers may be convenient, I persist in my minimalistic Luddite ways, keeping important passwords in an encrypted text file. Apocryphon is a generalization of some script code I use to make that a little easier: a way to create a file to which private notes can be added and which decrypts itself to stdout.

To continue with the particular example of a password store, my passwords, were they ever to exist as a plaintext file, would be one-per-line with tab-separated fields, something like:

blankety-bank.com   account=135798642   PIN=5229
recklessprogrammers.io  user=crazy7 password=hello123
...

This notional file is append-only: if I change the password for blankety-bank.com then I add another line with the new password. The history of passwords is therefore retained without the need for something like git, with the current password being the last one in the file for a given account. The append-only approach avoids editing accidents. It also allows me to add a line like

blankety-bank.com   account=135798642   PIN=7198

before I make the change at blankety-bank.com: later, perhaps after a backup exists which includes the updated password store, I can make the change to match the added line; until then, I use the password of the second-last blankety-bank line in the store.

To support this, I need only an encrypted text file: anything else, such as a command to generate a random password, is a separate tool, Unix-style. However, I prefer that file to be as self-contained as possible, requiring no unusual program or private key or any separate note of encryption parameters. Also, I don't want to have to decrypt it to disk, even temporarily, in order to update it.

The solution is an apocryphon, composed of the short Apocryphon script code, followed in the same file by the encrypted data encoded as base-64 text.

A security caveat is that a script has no control over the memory it uses to hold a passphrase, and no reliable way to zero that memory when it terminates. The latter limitation means that a passphrase may remain in RAM for an indefinite time after the program has been run, which means it is more exposed to certain kinds of attack. In normal circumstances, this probably isn't a concern, and that memory will be cleared by Linux before it is reallocated to another process.

However, if you have swap enabled and the program is swapped out or you hibernate your system while it is running then your apocryphon's passphrase may be persisted to disk – in plaintext form, unless swap is encrypted.

Notes

Memory security relies on locking memory to prevent it being swapped (but be aware that hibernation will override that) and clearing memory of sensitive data when finished. Neither of these is possible for a bash program. Also, Apocryphon uses Openssl, and I have not investigated how that program handles passwords in memory, so I don't know how much value there would be in "hardening" the script that calls it, were that possible.

Merely out of curiosity, I did some experiments with altered versions of the code, looking for instances of the passphrase in the process's heap and stack at program exit. I found, for example, that storing a long passphrase only in variables declared 'local' in bash was (note multiple qualifications) partially effective in allowing it to be overwritten in the virtual memory pages of the main process (not subshells). If you have a simple script then you can probably reduce the likelihood of sensitive data remaining in memory by the way you write it, but that will never be something you can have confidence in.

The popular password manager "pass" (another bash script) delegates handling of its passphrase to GnuPG Agent, which can lock its memory. GPG also uses AES key wrap to make exfiltration of the passphrase from memory more difficult – but not impossible: see Kudelski Security's GPG Memory Forensics. The GPG documentation warns against hibernation.

Above, I wrote that Linux clears memory pages only before allocating them to another process. If you're concerned about memory threats, it is possible to make it clear memory as soon as it is freed using the init_on_free option.

As I write this, the domain names blankety-bank.com and recklessprogrammers.io are still available. Who said all the good domain names were taken?