6 releases (breaking)
0.6.0 | Aug 27, 2024 |
---|---|
0.5.0 | Jun 4, 2024 |
0.4.0 | May 19, 2024 |
0.3.0 | Mar 24, 2024 |
0.1.0 | Jan 21, 2024 |
#2273 in Cryptography
1,487 downloads per month
Used in 8 crates
(5 directly)
93KB
1K
SLoC
The sequoia-keystore
crate implements a server that manages secret
key material. Secret key material can be stored in files, on hardware
devices like smartcards, or accessed via the network.
sequoia-keystore
doesn't implement these access methods. This is
taken care of by various backends. The backends implement a common
interface, which is defined by this crate.
lib.rs
:
Defines the traits that keystore backends need to implement.
Sequoia's keystore is a service, which manages and multiplexes access to secret key material. Conceptually, keys live on devices, and devices are managed by backends. A device may be as simple as an on-disk file, it may be a smartcard, or it could be another keystore server that is accessed over the network.
A backend implements the traits defined in this crate. The traits abstract away the details of the various devices. They are mostly concerned with enumerating keys, and executing the low-level decrypt and sign operations. The backend interfaces are different from, and more low level than the general-purpose interface exposed to applications.
The following figure illustrates the architecture. The squares represent different address spaces.
+---------------+ +---------------+
| Application | | Application |
+---------------+ +---------------+
\ /
+----------------------------------------------+
| Keystore |
| / \ |
| soft key openpgp card |
| backend backend |
+----------------------------------------------+
The keystore does not have to run as a server; it is also possible to co-locate the keystore in an application, as shown here:
+----------------------------------------------+
| Application |
| | |
| Keystore |
| / \ |
| soft key openpgp card |
+----------------------------------------------+
Using a daemon instead of a library or a sub-process, which is spawned once per application and is terminated when the application terminates, offers several advantages.
The main user-visible advantage is that the daemon is able to hold
state. In the case of soft keys, the daemon can cache an
unencrypted key in memory so that the user doesn't have to unlock
the key as frequently. This is particularly helpful when a
command-line tool like sq
is executed multiple times in a row
and each time accesses the same password-protected key. Likewise,
a daemon can cache a PIN needed to access an HSM. It can also
keep the HSM open thereby avoiding the initialization overhead.
This also applies to remote keys: an ssh tunnel, for instance, can
be held open, and reused as required.
A separate daemon also simplifies an important non-functional security property: process separation. Since soft keys aren't managed by the application, but by the daemon, an attacker is not able to use a heartbleed-style attack to exfiltrate secret key material.
The traits model backends as collections of devices each of which contains zero or more keys. The following figure illustrates a possible configuration. The keystore uses two backends, the softkey backend, and the openpgp card backend, and each backend has two devices. The softkey backend models certificates as devices; the openpgp card backend has one device for each physical device. Each device contains between 1 and 3 keys. The interface does not impose a limit on the number of devices per backend, or the number of keys per device. As such, a TPM managing thousands of keys is conceivable, and in scope.
+----------------------------------------------------+
| Keystore |
| / \ |
| soft key openpgp card |
| / \ / \ |
| 0x1234 0xABCE Gnuk Nitro Key |
| / \ | #123456 #234567 |
| 0x10 0x23 0x34 / | \ / | \ |
| 0x31 0x49 0x5A 0x64 0x71 0x88 |
+----------------------------------------------------+
The different devices may or may not be connected at any given time. For instance, the user may remove a smartcard, but if the backend has recorded the configuration, the keystore still knows about the
When the keystore starts, it eagerly initializes the various backends that it knows about. At this time, backends are statically linked to the keystore, and have to be explicitly listed in the keystore initialization function.
When a backend is initialized, the initialization function is passed a directory. The backend should read any required state from a subdirectory, which is named after the backend. For instance, the soft keys backend uses the "softkeys" subdirectory.
A backend must be extremely careful when using state stored somewhere else. If a user selects a different home directory, then they usually want a different configuration, which is isolated from the main configuration. This is not entirely possible in the case where a backend uses a physical resource, for example.
Keys and Devices
At its simplest, a device contains zero or more OpenPGP keys. A device may also be locked or unlocked, registered or not registered, and available or unavailable.
If a device is locked, it first has to be unlocked before it can
be used. Sometimes a device can be unlocked by supplying a
password via the DeviceHandle::unlock
interface. Other times,
the device has to be manually unlocked by the user. If a device
is locked, it may or may not be possible to enumerate the keys
stored on the device.
If a device is registered, then the device's configuration has been cached locally. In this case, the keys on the device can be enumerated even if the device is not connected to the host. For instance, when an OpenPGP card is registered, the OpenPGP card backend records the serial number of device, the list of keys that are stored on the smartcard, and their attributes. When the user enumerates the keys managed by the key store, these keys are returned, even if the smartcard is not attached. The user cannot, of course, use the keys.
If a device is registered, but is not attached to the system, then it is considered unavailable. If the user attempts to use a key on an unavailable device, then an error is returned. In this case, the application could normally prompt the user to make the corresponding device available.
These states are documented in more detail in the documentation
for DeviceHandle
.
Whether a key is registered or available is purely a function of the device. If a device contains multiple keys, and they can be registered, or available independent of the other keys, then the backend must model the keys as separate devices.
Dependencies
~16–30MB
~417K SLoC