SafeCloset, a Secret Safe - Why and how I made it in Rust
Like everybody, I have small secrets to store, like door codes, some passwords, where I buried the body, etc.
They must be kept away from other eyes but, more importantly, they must be available, even if I'm traveling far from my computers.
And they must be easily backed up without risk.
In the past, I've dealt with such secret storage with various solutions, like having files decrypted, edited with my favorite editor, then encrypted again.
They were full of weaknesses:
- temporary clear files that could be inadvertently backed up, or staying here if I had to leave or in case of crash
- editor weaknesses, like backup files and plugins
- OS specificity making them inaccessible when far from my computers
The temporary clear files where the most dangerous problem. I've had to hunt for my clear files after a badly parameterized backup rule.
Editor plugins were a real threat too, especially as I started using an AI plugin storing whole extracts of my texts for better auto-completion.
So I had to find or build better.
The obvious requirements for my new solution were:
- an encrypted storage file, with a strong algorithm
- a storage format making it possible to keep the file on non secure disks
- a multi-platform application, that can be easily carried too
- totally open-source, so that the program can be fixed or rewritten
- no clear file ever created, no data sent to external API, so the application is an editor too
- pure Rust, to avoid most nasty bugs
I added a few less obvious requirements:
- easy fuzzy key search
- fast opening, instant closing
- auto-closing (dead man switch)
- focus on ergonomics, I want to feel comfortable editing in the application
And in case I inadvertently become a secret agent:
- plausible deniability by putting drawers (storage units) inside other ones
- non observability of deep drawers (having several versions of the file doesn't let you know whether there were changes and in which deep drawer)
Now, let's see the technical choices.
Programming language: Rust
As I said, this was obvious to me. Such program can't really be written today in another language. Rust doesn't prevent all bugs, but it makes it possible to avoid the nasty ones which stay hidden and compromise security.
Cryptographic algorithm and library : AES-GCM
I never considered rolling my own algorithm or using a lightly tested library.
I choose an AEDS crate from the RustCrypto group: AES-GCM in its SIV variant (the SIV variant isn't really needed but it doesn't cost much).
File format
The minimal unit of secret in SafeCloset is an entry, which is made of a name and a value, for example "VISA Card code" and "9875".
Entries are stored together in a drawer.
SafeCloset uses the metaphor of closets and drawers:
A SafeCloset file contains something which is called a closet.
A closet contains several drawers. Each drawer is separately encrypted, with its own passphrase (and nonce).
A drawer also contains a closet, which contains deeper drawers.
To ensure plausible deniability, drawers are automatically created, including deep ones, and nothing distinguishes drawers that you created and you can open from the ones which were automatically created and that you can't open (you could if you knew their password but they aren't displayed on creation and they only contain random bytes anyway).
Drawers are serialized using the serde crate which is kind of standard in the Rust world and is very convenient. For the encoding format, I choose MessagePack which, like JSON, allows field addition but is much more compact. Having optional fields is very important to allow evolution of the file format while ensuring old files will stay compatible with newer versions of the application.
Combining the chosen encryption scheme and the serialization encoding with the list of structures and fields, the complete file format is described in the community page to allow replacement of the application if needed.
Making an UI in the terminal
There are many low level libraries whose features go from the basic (and easy) task of coloring and styling the text you print in the terminal to handling events, terminal size, alternate screen, etc. I personally like Crossterm which is cross platform and well designed.
I combine it with Termimad, a crate I made to manage skins, generate texts without mixing the style and the content, handle text inputs, even with wide characters, and a lot of small TUI related problems.
Termimad allows fancy things like editing texts in small areas of your terminal and fading the view behind the menus or dialogs:
As the author of Termimad I feel comfortable to not recommend you use it for your own TUI. Not that it's bad, I like it, but it's a strange beast covering much more than what a library should do, and at a much lower level than your typical framework. If you're not used to low level TUI libraries and well versed in Rust, I suggest you look at other higher level TUI crates.
If you still hesitate, come to my chat and I'll tell you whether Termimad might be the right choice for your application.
Other libraries
They're very few other crates involved, way less than in common programs of the same complexity. This was a deliberate choice consistent with the global efforts of making it easier to check the source and of reducing the attack surface.
The result: SafeCloset
Here it is: https://dystroy.org/safecloset/
If I may say so, I'd say SafeCloset is convenient and pleasant to use.
It lets me find and read my secrets in a few keystrokes.
And it seems to be the most secure solution I ever used.
I designed it to be intuitive for other users too, and I hope I succeeded.
To better introduce it, I made a website explaining how it works, how to install it, how to use it: https://dystroy.org/safecloset/
The current version is a candidate for a release as 1.0, there's nothing important missing right now, but I'll let some time run to be sure.
I'd welcome your opinion!
And if you have questions, for example on technical details, please ask.