2022-11-30 / 2022-12-12

Full-featured password managers are convenient, and I don't rule out using one in the future. For now, I persist in my 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.

This approach might have little appeal beyond minimalism. Moreover, there is a security caveat. I've decided to share it anyway, and document the caveat. If I've overlooked anything important, please let me know.

To continue with the 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 an offsite 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 a plaintext file on disk in order to update it.

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

The 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 is persisted to disk – in plaintext form, unless swap is encrypted.

I've put Apocryphon on Codeberg, as my first trial of that platform.


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. Nevertheless, 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 here) 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. I left the Apocryphon code unaltered.

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 before allocating them to another process. It is possible to configure it not to do that, but that would be highly unusual. Conversely, it is possible to compile a kernel which sanitizes a program's memory as soon as it is freed. See the Grsecurity wiki.

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