Skip to content

Read and write encrypted GNOME keyring files in a straightforward way

License

Notifications You must be signed in to change notification settings

radian-software/gnome-keyloop

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

GNOME KeyLoop

GNOME keyring has a problem. I don't know why but there is absolutely no utility or library for reading and writing from a keyring file. You would think secret-tool would be able to do that, but lmao no it can't (it doesn't even have a way to list all the entries in the keyring, you can only look up a specific one by filter). You would think that libsecret would do if you write some C code, but lmao no that doesn't even work either (it doesn't actually know how to parse the keyring file, it only knows how to make a dbus request). The only thing that knows how to read and write keyring files directly is apparently gnome-keyring-daemon. So you would be stuck with setting up some kind of isolated environment where you can run a second gnome-keyring-daemon pointed at the keyring file of interest, and then talk to the daemon in that environment.

In principle you can inspect the files yourself, because in principle all that's needed to decrypt them is your passphrase, but in practice this page that could only with great generosity be called """documentation""" is the only extant reference for what format those files are in and what the cryptography is, aside from the source code of gnome-keyring-daemon itself.

This is all a giant mess, and I concluded somebody needed to do the public service of making a simple utility that can read and write these files from the command line in a no-nonsense way with zero dbus or environment dependencies.

You can use this project to:

  • Access the data in keyring files that you have on a backup drive (not installed in your live session).
  • Make changes to keyring file entries that aren't supported by secret-tool or the GNOME user interface.
  • Merge two keyring files together.
  • Discover how the keyring files are formatted and encrypted since there is no documentation on it anywhere else.

Installation

This project is a single command-line script gnome_keyloop.py written in Python. It has a single Python dependency, cryptography. You can set that up yourself or use the provided Poetry configuration to set up a suitable virtual environment.

Functionality and workflow

The utility is a converter between two formats for the keyring data: binary and JSON. The binary format is what is used on disk by GNOME, it has the file extension .keyring. The JSON format is something I made up and nothing else uses it, but it is human-readable and lossless. You can convert the binary format into the JSON format and back, and get a byte-for-byte identical keyring (unless there's a bug in my code).

The way that you make changes to your keyring (or merge two keyrings together, etc) is by converting the keyring from binary to JSON, making whatever changes you want to make to the JSON files (there are many existing tools for this, see e.g. jq), and finally converting the JSON back into the binary format. My understanding is that after installing a new keyring file into ~/.local/share/keyrings, you have to log out and back in again for it to be used.

Constraints on your changes

At present I do not bother to strictly validate JSON inputs, so while all JSON outputs are valid JSON inputs, if you make changes, you could trigger unexpected errors or generate broken keyring files.

Here are interesting notes about what you can change. When in doubt, refer to the source code; I promise it's more readable than the gnome-keyring-daemon source that I had to reverse engineer.

  • You can change the salt or hash_iterations freely (salt must be exactly 8 bytes and represented in base64). The new values will be used for encryption when converting the keyring back to binary format.
  • There is support for sub-second resolution on all the timestamps in the keyring data format, but my system does not make use of this, so I have not tested what happens if a keyring actually has timestamps that do not fall on even seconds.
  • The acl field is optional. Again my system is not using it, so while I implemented support for this, I don't know if it works.
  • All strings can support binary data, not just the salt that I encoded explicitly to base64 (since it is always binary). On my system none of the strings contained non-UTF-8 data, but it should work, I guess.
  • I would assume that if you are merging two keyring files then you should adjust the id fields on each item so that there are no duplicates.
  • There is only support for version 0 of the keyring format with AES encryption and MD5 hash. This corresponds to a (0, 0, 0) header, and a keyring that does not conform will be rejected with an error. Are there other allowable values? Maybe I would know if there was any documentation.
  • Every item and attribute has a type which is a 32-bit integer that is always zero on my system, so I omitted it from the JSON and had my converter throw an error if it is ever non-zero. I guess that could be adjusted if needed. The """documentation""" for the binary file format suggests that sometimes MD5 hashes might be represented as 32-bit integers instead of strings, with no explanation of why or when this would happen, so maybe that happens when there is a non-zero type.

Command-line interface

Provide two positional arguments to the script: input file and output file. The output file can be - to print to stdout. The input file can be either JSON or binary format. Files that start with { are assumed to be JSON, otherwise they are assumed to be binary. Yes, I know that's rather silly, but it works well enough.

Set the environment variable KEYRING_PASSPHRASE to the passphrase that decrypts your keyring. If this variable is not set when converting a binary keyring to JSON, then the resulting JSON will say "decrypted": false and will be missing some properties including the actual secrets and attribute values for each item. Non-decrypted JSON cannot be converted back into binary format because the data is not all there. Also, the environment variable is mandatory when converting back into binary format, since there is no way to construct the binary format without using encryption (for which a passphrase is needed).

Testing

When I was searching for a tool to extract GNOME keyring entries from a backup drive and rescue my live system, I found a GitHub repo that said it did exactly what I needed, and even had full documentation about the process. Only, once I compiled and ran it, I found that no code was ever actually written, it was just a glorified "Hello, world". I will not be naming names, but the experience stuck with me.

So, just to prove that this thing actually is functional at least for some keyrings, I have included a sample GNOME keyring file with fake data, which can be converted from the binary format to JSON and back and verified that it is byte for byte identical.

Verify that JSON converts to the binary format as shown:

% rm test/example.keyring
% KEYRING_PASSPHRASE="$(< test/example.pass)" poetry run ./gnome_keyloop.py test/example.json test/example.keyring
% git status

Verify that binary converts to the JSON format as shown:

% rm test/example.json
% KEYRING_PASSPHRASE="$(< test/example.pass)" poetry run ./gnome_keyloop.py test/example.keyring test/example.json
% git status

About

Read and write encrypted GNOME keyring files in a straightforward way

Resources

License

Stars

Watchers

Forks

Languages