gfcombinefs combines several "shares" of a secret file previously split using Shamir secret sharing, to produce the original secret, and presents it as a file in a FUSE filesystem.

So far, there have been no releases. Current source code is available.

The contents of the README follow.


######################### WARNING ################################
# So far, gfcombinefs is experimental and mostly untested.       #
# No real release has been made yet. I suggest not trusting it.  #
##################################################################

gfcombinefs combines several "shares" of a secret file previously split using Shamir secret sharing, to produce the original secret, and presents it as a file in a FUSE filesystem.

The idea is that the secret file (probably a GnuPG secret keyring, i.e. secring.gpg) is split into (say) 6 shares (of the same length in bytes as the secret), of which 4 are needed to reconstitute the original secret (that would be a "4-of-6" share). Thanks to the algorithm used, having fewer than the required number of shares gives no information whatsoever about the secret's contents.

For the actual mathematics, gfcombinefs uses libgfshare, written by Daniel Silverstone - see libgfshare's documentation for more about the algorithm.

gfcombinefs is primarily designed for use with GnuPG secret keyrings, which has various consequences:

  • it locks itself into memory to stop the secret from being paged out
  • the secret can be removed, which should be done prior to suspend-to-disk (hibernate), again to prevent it from being paged out
  • when not enough parts have been provided, the file exists but has zero length (this stops gpg from complaining about it)

Requirements

At runtime:

For compilation:

Usage

The filesystem currently has one file in the root directory, called "secret". There's only one secret per mountpoint so far.

The only normal operations supported are:

  • list the root directory (it contains "secret", "." and "..")
  • read from "secret"
  • get the attributes of "secret" (its permissions are 0400, i.e. r--------)
  • unlink "secret", e.g. with rm -f $mountpoint/secret - this doesn't actually remove it, but it discards all its data (by clearing the relevant memory), then truncates it to be an empty file

In addition, you can provide shares. This is done by setting an XFS-style extended attribute on the secret, whose name is "user." plus the share number in ASCII, for instance:

rm -f $mountpoint/secret
attr -q -s user.023 $mountpoint/secret < secring.gpg.023
attr -q -s user.042 $mountpoint/secret < secring.gpg.042
attr -q -s user.111 $mountpoint/secret < /media/usbdisk/secring.gpg.111
attr -q -s user.123 $mountpoint/secret < /media/usbdisk/secring.gpg.123

When enough shares have been provided (currently the filesystem is hard-coded to expect exactly 4 shares), the secret file's size will change from 0 to the real size (which is equal to the size of each share), and its contents will change from empty to the real secret.

Example

For instance, you could do a 4-of-6 share and put two shares on your laptop, two on a USB stick in your pocket, and two backup shares kept in safe places.

If you have both the laptop and the USB stick, you can get your secret keyring and do GnuPG things, but if either your laptop or your USB stick are lost or stolen (but not both devices at the same time!) you don't need to revoke your key: just take the remaining device and the two backup shares, combine them to get the secret, re-split the secret into a new set of shares, and securely destroy the old shares (to guarantee that they cannot be obtained by the same person as the lost device, and used to recover the secret).

The lost device contains only two shares, which isn't enough to recover the secret (you can make sure they're entirely useless by destroying the other shares in that set).

Many other sharing schemes are possible; the author of libgfshare suggests a 3-of-5 share, with two shares on a USB stick and one each on three computers. You can adjust the number of shares, and their distribution between various locations, as necessary, but at the moment the number of shares expected is hard-coded in gfcombinefs.

For shares stored on large storage devices (like the laptop's hard disk), I recommend making a small partition (or LVM logical volume) to contain them - that way, you can wipe the whole partition with wipe(1) without it taking too long, and be reasonably sure that the old shares are unrecoverable.

Limitations

gfcombinefs is a read-only filesystem - you'll have to split the secret yourself, probably using gfsplit(1). Supporting writing would be problematic - splitting the new secret wouldn't be a problem, but it wouldn't be possible to copy the new shares from gfcombinefs to their various locations automatically (the backup shares shouldn't even be present most of the time).

Luckily, gpg can usually operate well with a read-only secret keyring; you'll usually only have to alter the secret keyring (and hence re-split) if you add uids or subkeys to a key, or create a whole new key.

In particular, you can encrypt data, sign data, certify (sign keys), and so on with the secret keyring on a read-only filesystem like gfcombinefs.

gfcombinefs doesn't help you to do the actual splitting. This is probably best done with a temporary GNUPGHOME in a tmpfs, with swap disabled (so the pages of the tmpfs don't get paged out either); copy in the secret and public keyrings, make the changes, split the secret keyring, copy the shares to appropriate places, and copy the public keyring back to the normal GNUPGHOME.

You can only have one secret per filesystem at the moment.

There is no detection of corruption, perhaps from incorrect shares. This could be solved by supplying a hash of the complete secret, perhaps as another extended attribute or as the secret's filename.

The filesystem currently assumes that exactly 4 shares are needed. This could also be solved by supplying a hash of the complete secret - attempting to assemble an incomplete or inconsistent set of shares will yield meaningless contents for the secret, so the filesystem could just try to reassemble the secret after each share is added, then make it available for reading only when the hash matches what was expected.

The unlink() implementation should really be truncate() instead (or perhaps both should work).

There should be scripts for pm-utils (and perhaps other suspend-to-disk implementations) to discard the secret before hibernating.

The executable needs to be setuid root to set a larger mlock limit, and the mlock logic could probably be improved. The Linux default of 64KB is not necessarily sufficient, even if only the security-sensitive parts (the shares, the secret, and any buffers used by libfuse) are mlocked; for the moment it uses root privileges to set a limit of 10MB, drops privileges, and locks the entire process (including libraries) into memory.

Other notes

The access() implementation is necessary to prevent gpg from trying to create lockfiles on the gfcombinefs filesystem (which won't work, and if gpg tries and fails to do so, it will consider this to be an error).

Related links

Copyright/licensing

Copyright © 2009 Simon McVittie http://smcv.pseudorandom.co.uk/

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER IABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.