OpenPGP Secret Key Transfer
This document describes a mechanism for securely transferring OpenPGP secret key material between two devices. The devices must be able to communicate on a local network, and at least one of the devices must be able to scan a QR-Code displayed by the other. These requirements are typically fulfilled in a scenario where key material should be transferred from a desktop machine to a phone or vice versa.
This mechanism relies on two techniques: First, a QR-Code is used for out-of-band communication of authentication and network discovery information. The actual connection then relies on TLS-PSK for mutually authenticated, forward-secure transport encryption.
Session Setup
A secure session between two devices is set up as follows:
- One device generates a preshared key, then opens a TCP port to listen for a single incoming TLS-PSK session. This device is the "server" device.
- While in the listening state, the server device displays a QR code that encodes the necessary data to connect to it (see below).
- Another device scans the QR code, then establishes a TLS-PSK connection to the server device. This device is the "client" device.
Connection Details
The choice of port the server device listens on is left to the implementation, it is RECOMMENDED to use an automatically assigned one. The connection MUST be secured with the TLS-PSK protocol. It MUST also use one of the PSK-authenticated Diffie-Hellman ciphersuites (TLS_DHE_PSK_* or TLS_ECDHE_PSK_*). The preshared key consists of 128 bits of random data, which MUST be obtained from a CSPRNG. For compatibility, the client SHOULD use the identity hint provided by the server during the TLS handshake as their client identity, or empty string if none was provided.
The connection details displayed in the QR-Code are encoded as a URI, as such:
uri = "OPENPGP+SKT://" hex-psk "@" address ":" port hex-psk = 32HEXDIG address = IPv4address / "[" IPv6address "]" port = *DIGIT
The IPv4address and IPv6address grammars are defined as in https://tools.ietf.org/html/rfc3986#section-3.2.2 For efficient encoding, all letters (and hexadecimal numbers) in the URI SHOULD be uppercase.
Transmitting Data
The only data that may be sent through an SKT session are ASCII armored secret key blocks, terminated by two newlines. Both the client and server device are allowed to send data first; the device that first sends data becomes the /active/ device of the session, the other becomes the /passive/ one. The active device may send further key blocks after the first one, the passive device MUST NOT send any. The session SHOULD be kept open by the passive device, until dismissed by the user. Note that the race condition in this behavior is not an issue in practice, since data sent by the then-active device is triggered directly by user input (see below).
This way of handling sessions enables two distinct user flows:
- Establish a session, then wait for user input to select keys to send. When keys are received, switch to list of received keys and allow user to import.
- Ask for user input before the connection is established, then send keys immediately after session has been established.
The former user flow yields an (arguably) better user experience: Establishing a session first not only makes it more transparent to the user how the transfer mechanism works, but also moves most modes of failure to a point in time /before/ the user selects their keys to send. It also makes expectations more clear in the scenario where the passive device is the server device, since scanning a QR code is typically perceived as an action that receives data, rather than sends. An implementation that follows the former approach is automatically compatible with the second. It is RECOMMENDED that implementations take the first approach, unless implementation constraints prescribe the second.
Security Considerations
- transfer is secure, still: designed for usability and compatibility
- only TLS_DHE_* or TLS_ECDHE_*
- doesn't work (by definition) on airgapped devices