openpgp D. K. Gillmor Internet-Draft ACLU Intended status: Informational 8 August 2024 Expires: 9 February 2025 OpenPGP Prompting and Caching draft-dkg-openpgp-prompting-caching-00 Abstract Some OpenPGP secret keys and messages are locked with a passphrase. An OpenPGP-using application might want to prompt the user for the passphrase, or might want to permit the user to only enter the passphrase once while keeping the material unlocked for future use. This document describes a simple interface that can be used for prompting the user and for caching the results of such a prompt. It is designed to interoperate well with the Stateless OpenPGP Interface, and to facilitate its use both in test suite operations and in common patterns of interactive operation. About This Document This note is to be removed before publishing as an RFC. The latest revision of this draft can be found at https://dkg.gitlab.io/openpgp-prompting-caching/. Status information for this document may be found at https://datatracker.ietf.org/doc/ draft-dkg-openpgp-prompting-caching/. Discussion of this document takes place on the OpenPGP Working Group mailing list (mailto:openpgp@ietf.org), which is archived at https://mailarchive.ietf.org/arch/browse/openpgp/. Subscribe at https://www.ietf.org/mailman/listinfo/openpgp/. Source for this draft and an issue tracker can be found at https://gitlab.com/dkg/openpgp-prompting-caching/. Status of This Memo This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79. Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet- Drafts is at https://datatracker.ietf.org/drafts/current/. Gillmor Expires 9 February 2025 [Page 1] Internet-Draft OpenPGP Prompting and Caching August 2024 Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress." This Internet-Draft will expire on 9 February 2025. Copyright Notice Copyright (c) 2024 IETF Trust and the persons identified as the document authors. All rights reserved. This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/ license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License. Table of Contents 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 3 1.1. Requirements Language . . . . . . . . . . . . . . . . . . 4 1.2. Terminology . . . . . . . . . . . . . . . . . . . . . . . 4 1.3. Theory of Operation . . . . . . . . . . . . . . . . . . . 5 2. Specification . . . . . . . . . . . . . . . . . . . . . . . . 5 2.1. version: Version information . . . . . . . . . . . . . . 5 2.2. get-password: Request a Password . . . . . . . . . . . . 6 2.3. cache-password: Cache a Password . . . . . . . . . . . . 7 2.4. cache-internal: Cache an Intermediate Value From a Calculation . . . . . . . . . . . . . . . . . . . . . . . 8 2.5. get-internal: Retrieve an Intermediate Value From the Cache . . . . . . . . . . . . . . . . . . . . . . . . . . 9 2.6. drop-cache: Dropping Cached Data . . . . . . . . . . . . 10 2.7. notify: Notify the User of Additional Steps . . . . . . . 10 2.8. dismiss: Dismiss a Notification . . . . . . . . . . . . . 11 3. Error Codes . . . . . . . . . . . . . . . . . . . . . . . . . 12 4. Configuration . . . . . . . . . . . . . . . . . . . . . . . . 13 4.1. Configuration Example . . . . . . . . . . . . . . . . . . 13 4.2. Configuration File Format . . . . . . . . . . . . . . . . 14 4.3. What are the configuration values? . . . . . . . . . . . 14 4.3.1. cache_duration: Time to Cache . . . . . . . . . . . . 14 4.3.2. prompt_timeout: Time to Wait For User Feedback . . . 15 4.3.3. on_reuse: Action on Cache Retrieval . . . . . . . . . 15 5. Subsessions . . . . . . . . . . . . . . . . . . . . . . . . . 15 5.1. Subsession Identifiers . . . . . . . . . . . . . . . . . 15 Gillmor Expires 9 February 2025 [Page 2] Internet-Draft OpenPGP Prompting and Caching August 2024 5.2. Subsession Configuration . . . . . . . . . . . . . . . . 15 6. Data Types . . . . . . . . . . . . . . . . . . . . . . . . . 16 6.1. PROMPT_TIMEOUT . . . . . . . . . . . . . . . . . . . . . 16 6.2. CACHE_DURATION . . . . . . . . . . . . . . . . . . . . . 16 6.3. REUSE_DIRECTIVE . . . . . . . . . . . . . . . . . . . . . 17 6.4. NOTIFICATION_HANDLE . . . . . . . . . . . . . . . . . . . 17 6.5. INTERNAL_LABEL . . . . . . . . . . . . . . . . . . . . . 18 6.6. INTERNAL_VALUE . . . . . . . . . . . . . . . . . . . . . 18 6.7. MESSAGE . . . . . . . . . . . . . . . . . . . . . . . . . 18 6.8. PASSWORD . . . . . . . . . . . . . . . . . . . . . . . . 18 7. Subtleties and Nuance . . . . . . . . . . . . . . . . . . . . 19 7.1. The OPAC Cache . . . . . . . . . . . . . . . . . . . . . 19 7.1.1. Cache Lifecycle . . . . . . . . . . . . . . . . . . . 20 7.1.2. Subsession Cache Structure . . . . . . . . . . . . . 20 7.1.3. Cache Insertion vs. Cache Retrieval . . . . . . . . . 20 7.2. OPAC Prompting . . . . . . . . . . . . . . . . . . . . . 20 7.3. OPAC Subsessions . . . . . . . . . . . . . . . . . . . . 21 7.3.1. Subsession Configuration . . . . . . . . . . . . . . 21 7.4. Passwords vs. Internal Values . . . . . . . . . . . . . . 22 8. References . . . . . . . . . . . . . . . . . . . . . . . . . 22 8.1. Normative References . . . . . . . . . . . . . . . . . . 22 8.2. Informative References . . . . . . . . . . . . . . . . . 22 Appendix A. Example Uses . . . . . . . . . . . . . . . . . . . . 23 A.1. Integration with the Stateless OpenPGP Command-Line Interface (SOP) . . . . . . . . . . . . . . . . . . . . . 23 A.1.1. OPAC + SOP: Prompting . . . . . . . . . . . . . . . . 23 A.1.2. OPAC + SOP: Prompting and Caching . . . . . . . . . . 23 A.2. Test Suite Isolation . . . . . . . . . . . . . . . . . . 24 A.3. Single-Application Subsession Use . . . . . . . . . . . . 25 A.4. OpenPGP Internal Use . . . . . . . . . . . . . . . . . . 26 A.5. Notifying the User of External Requirements . . . . . . . 26 Appendix B. Future Work . . . . . . . . . . . . . . . . . . . . 27 Appendix C. Acknowledgements . . . . . . . . . . . . . . . . . . 27 Appendix D. Document History . . . . . . . . . . . . . . . . . . 27 D.1. draft-dkg-openpgp-prompting-caching-00 . . . . . . . . . 27 Author's Address . . . . . . . . . . . . . . . . . . . . . . . . 27 1. Introduction Some OpenPGP secret keys and messages are locked with a passphrase. An application that uses OpenPGP might want to prompt the user for the passphrase, or might want to permit the user to only enter the passphrase once while keeping the material unlocked for future use. Gillmor Expires 9 February 2025 [Page 3] Internet-Draft OpenPGP Prompting and Caching August 2024 This document describes a simple interface that can be used for prompting the user and for caching the results of such a prompt across an interactive session. The OpenPGP Prompting and Caching interface described here can be referred to as OPAC. A specific command-line utility offering this interface is referred to as opac. It is intended to interoperate well with the Stateless OpenPGP Interface (see [I-D.dkg-openpgp-stateless-cli]), and to facilitate the use of OpenPGP both in test suite operations and in common patterns of interactive operation. 1.1. Requirements Language The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here. 1.2. Terminology This document tries to use certain terms with specificity: * "Certificate" refers to an OpenPGP Certificate, or Transferable Public Key, as defined in [RFC9580] * "Secret Key" refers to an OpenPGP Transferable Secret Key, as defined in [RFC9580]. * "S2K" refers to the OpenPGP String-to-Key process for deriving high-quality cryptographic symmetric keys from a human-memorable string of text, also defined in [RFC9580]. * "Session" refers to an interactive user session at a computer. Different human-computer interfaces may have different conceptions of a session, and this document is largely agnostic about those details. The salient features of a "session", for the purposes of this document, are that it has a defined time limit (often bounded by operations like "logging in" and "logging out"), a way of requesting and gathering feedback from the user ("prompting"), and is intentionally under some overarching administrative authority or control by the system operator ("the logged in user"). * "Fails with XXX" means that the invoked command terminates and yields a specific value associated with XXX (see Table 1) as its return code. Gillmor Expires 9 February 2025 [Page 4] Internet-Draft OpenPGP Prompting and Caching August 2024 1.3. Theory of Operation OPAC is a command-line interface to facilitate the use of OpenPGP, that binds together three things: * The user's interactive session. * An ephemeral cache of OpenPGP-related data that can "unlock" OpenPGP objects in the filesystem. * A session-specific way to notify the user or elicit feedback from them ("prompting"). The ephemeral cache may be backed by a variety of different mechanisms. For example, it could use memory of an ephemeral session-bound process, or it could store data in a kernel-level keyring. Critically, the cache lasts no more than the duration of the user's session, and the material in the cache is never deliberately placed in non-ephemeral storage. Since the cached material is associated with objects in the filesystem, OPAC tends to assume that all processes in the session will have the same view of the filesystem as each other, or at the very least that any process invoking OPAC will have the same view as any session-bound process that backs the OPAC cache. There are unusual computing scenarios or configurations where OPAC will not work correctly, but it is intended to work with the overwhelming majority of interactive computing environments. See Section 7 for more details about OPAC's applicability. 2. Specification opac uses a command-line interface, with a set of standardized subcommands. 2.1. version: Version information opac version [--extended|--opac] * Standard Input: ignored * Standard Output: version information This subcommand emits version information as UTF-8-encoded text. Gillmor Expires 9 February 2025 [Page 5] Internet-Draft OpenPGP Prompting and Caching August 2024 With no arguments, the version string emitted should contain the name of the opac implementation, followed by a single space, followed by the version number. An opac implementation should use a version number that respects an established standard that is easily comparable and parsable, like [SEMVER]. If --extended is supplied, the implementation may emit multiple lines of version information. The first line MUST match the information produced by a simple invocation, but the rest of the text has no defined structure. If --opac is supplied, the implementations should produce a single line with the implemented [SEMVER] opac interface that this implementation specifies. For this draft, that version is 0.1. Example: $ opac version ExampleOpac 0.3 $ opac version --extended ExampleOpac 0.3 LibDBus 0.11.4 See https://pgp.example/opac/ for more information $ opac version --opac 0.1 $ 2.2. get-password: Request a Password opac get-password [--message MESSAGE] [--ignore-cache] [--prompt-timeout TIMEOUT] FILENAME * Standard Input: ignored * Standard Output: PASSWORD When successful, this invocation emits the human-readable password the user expects to be associated with the OpenPGP material found in FILENAME on standard output, encoded as UTF-8 text. If any failure happens, it emits nothing on standard output. If --ignore-cache is supplied, or if FILENAME is the special value -, opac MUST NOT look in its cache. If an associated password is found in the cache, opac MAY ask the user to confirm its use before emitting it. Gillmor Expires 9 February 2025 [Page 6] Internet-Draft OpenPGP Prompting and Caching August 2024 If no associated password is found in the cache, opac asks the user for the password associated with FILENAME. The optional argument MESSAGE MUST be well-formed according to Section 6.7. If MESSAGE is not well-formed, opac get-password fails with MALFORMED_MESSAGE. If the user declines to offer a password, or declines to permit use of the password from its cache, opac get-password fails with USER_DECLINED. When the user's provided password is emitted, opac get-password MUST NOT emit any other data to standard output before terminating successfully. In particular, it must not pad the output with LINE FEED (U+000A) or other similar characters. If the user fails to respond to a prompt after sufficient time has elapsed, opac get-password fails with PROMPT_TIMED_OUT. If --prompt- timeout is supplied, the specified timeout for the prompt is used. If it is not supplied, the prompt timeout is taken from the configuration (see Section 4.3.2). opac get-password MUST NOT insert anything into the cache, or update the expiration date of anything in the cache. 2.3. cache-password: Cache a Password opac cache-password [--on-reuse REUSE_DIRECTIVE] [--duration CACHE_DURATION] FILENAME * Standard Input: PASSWORD * Standard Output: nothing This invocation reads the supplied password on standard input, and injects it in the cache associated with the FILENAME OpenPGP object in the filesystem. Standard input MUST contain only a single UTF-8 encoded password, with no other information. However, it MAY include trailing whitespace characters, which opac strips before storing. If opac cache-password does cache the password in association with FILENAME, it succeeds. Gillmor Expires 9 February 2025 [Page 7] Internet-Draft OpenPGP Prompting and Caching August 2024 opac cache-password MAY decline to cache the supplied password for any reason. If it declines to cache, it SHOULD emit the reason for declining to stderr and fail with CACHE_DECLINED or some other more specific non-zero return code. If FILENAME is - in this invocation, it MUST NOT insert anything in the cache, and fails with INVALID_FILENAME. When associating the password with FILENAME in the cache, it chooses the duration from the cache by looking at the --duration argument and in its configuration (see Section 4.3.1). It chooses the shorter of those two durations to be the default cache. When a cached password is requested for use, opac follows the associated REUSE_DIRECTIVE, either from the command line or from the configuration (see Section 4.3.3). If the REUSE_DIRECTIVE appears in both the configuration and the command line, opac cache-password uses the stricter of the two choices. When inserting a password in the cache associated with FILENAME, if any other password is in the cache associated with FILENAME, the old value is removed from the cache. 2.4. cache-internal: Cache an Intermediate Value From a Calculation opac cache-internal [--on-reuse REUSE_DIRECTIVE] [--duration CACHE_DURATION] FILENAME INTERNAL_LABEL * Standard Input: Internal Value (octet stream) * Standard Output: nothing This invocation reads the supplied password on standard input, and injects it in the cache associated with the FILENAME OpenPGP object in the filesystem and the supplied INTERNAL_LABEL. If the INTERNAL_LABEL is malformed (see Section 6.5), it MUST NOT insert anything in the cache, and fails with MALFORMED_INTERNAL_LABEL. If FILENAME is -, it MUST NOT insert anything in the cache, and fails with INVALID_FILENAME. Standard input contains an arbitrary stream of octets, but it MUST be at least 1 octet long. If the input is entirely empty, opac cache- internal fails with MALFORMED_INTERNAL_VALUE If opac cache-internal does cache the internal value in association with FILENAME and INTERNAL_LABEL, it succeeds. Gillmor Expires 9 February 2025 [Page 8] Internet-Draft OpenPGP Prompting and Caching August 2024 opac cache-internal MAY decline to cache the supplied internal value for any reason. If it declines to cache, it SHOULD emit the reason for declining to stderr and fail with CACHE_DECLINED or some other more specific non-zero return code. --on-reuse and --duration apply in the same way as opac cache- password (see Section 2.3). When inserting an internal value in the cache associated with FILENAME and INTERNAL_LABEL, if any other password is in the cache associated with both FILENAME and INTERNAL_LABEL, the old value is removed from the cache. Note that multiple internal values can be held in the cache associated with a given FILENAME as long as they each have a distinct INTERNAL_LABEL. 2.5. get-internal: Retrieve an Intermediate Value From the Cache opac get-internal [--message MESSAGE] [--prompt-timeout PROMPT_TIMEOUT] FILENAME INTERNAL_LABEL * Standard Input: ignored * Standard Output: Internal Value (octet stream) This invocation retrieves a previously cached internal value associated with the FILENAME OpenPGP object in the filesystem and the supplied INTERNAL_LABEL. If an associated internal value is found in the cache, opac get- internal MAY ask the user to confirm its use before emitting it. If no associated internal value is found in the cache, opac get- internal fails with NO_ASSOCIATED_VALUE. The optional argument MESSAGE MUST be well-formed according to Section 6.7. If MESSAGE is not well-formed, opac get-internal fails with MALFORMED_MESSAGE. If the user declines to permit use of the internal value from its cache, opac get-internal fails with USER_DECLINED. When returning a value from the cache, opac get-internal MUST emit exactly the same data to standard output that was provided on standard input from the corresponding call to opac cache-internal (see Section 2.4). Gillmor Expires 9 February 2025 [Page 9] Internet-Draft OpenPGP Prompting and Caching August 2024 If the user fails to respond to a prompt after sufficient time has elapsed, opac get-internal fails with PROMPT_TIMED_OUT. If --prompt- timeout is supplied, the specified timeout for the prompt is used. If it is not supplied, the prompt timeout is taken from the configuration (see Section 4.3.2). opac get-internal MUST NOT insert anything into the cache, or update the expiration date of anything in the cache. 2.6. drop-cache: Dropping Cached Data opac drop-cache [--all-subsessions|FILENAME] * Standard Input: ignored * Standard Output: nothing If FILENAME is supplied, this invocation empties the cache of all data (passwords and internal values) associated with FILENAME. If FILENAME is not supplied, it empties the entire cache. If any passwords or internal values were removed from the cache, and they were all successfully removed, the process succeeds. If opac drop-cache failed to remove any item from the cache that it was instructed to remove, it fails with COULD_NOT_REMOVE_FROM_CACHE. In this case, it SHOULD emit an explanation to standard error. If OPAC_SUBSESSION is set, it only removes items from the subsession- specific cache. If OPAC_SUBSESSION is not set, it only removes items from the main cache. If --all-subsessions is present, and either OPAC_SUBSESSION is set of FILENAME is also present, then opac drop-cache fails with INCOMPATIBLE_OPTIONS. Otherwise, if --all-subsessions is present, it removes all items from the main cache and from every subsession cache as well. 2.7. notify: Notify the User of Additional Steps opac notify FILENAME MESSAGE * Standard Input: ignored * Standard Output: NOTIFICATION_HANDLE Gillmor Expires 9 February 2025 [Page 10] Internet-Draft OpenPGP Prompting and Caching August 2024 This invocation causes opac to emit a non-interactive notification to the user. This is intended specifically for the case where the user needs to take an action for the OpenPGP operation to succeed that the application cannot trigger on the user's behalf. For example, if the implementation needs the user to press a button on a USB hardware token to permit the use of secret key material, it might use opac notify so that the user knows that the application is awaiting action from them. If MESSAGE is malformed (see Section 6.7), it fails with MESSAGE_MALFORMED, and emits nothing on standard output. If it was unable to produce a notification, it fails with COULD_NOT_NOTIFY, and emits nothing on standard output. If it successfully notifies the user, it emits a NOTIFICATION_HANDLE (see Section 6.4) on standard output. This NOTIFICATION_HANDLE can be used to dismiss the notification (see Section 2.8) if the application determines it is no longer needed (for example, when the button has been pressed, and the USB hardware token returned a response). opac notify ignores the cache, and uses no configuration variables, so it ignores OPAC_SUBSESSION. 2.8. dismiss: Dismiss a Notification opac dismiss NOTIFICATION_HANDLE * Standard Input: ignored * Standard Output: nothing This invocation is used solely to dismiss a notification that had been created with opac notify (see Section 2.7). The only additional argument is a NOTIFICATION_HANDLE (see Section 6.4), which should match a value emitted from opac notify (see Section 2.7). It always succeeds, even if the NOTIFICATION_HANDLE could not be dismissed. opac dismiss ignores the cache, and uses no configuration variables, so it ignores OPAC_SUBSESSION. Gillmor Expires 9 February 2025 [Page 11] Internet-Draft OpenPGP Prompting and Caching August 2024 3. Error Codes opac, like any other process, returns an error code. The following values are defined for specific errors. +=======+=============================+=========================+ | Value | Name | Description | +=======+=============================+=========================+ | 0 | SUCCESS | Success | +-------+-----------------------------+-------------------------+ | 1 | UNSUPPORTED_SUBCOMMAND | Unsupported subcommand | +-------+-----------------------------+-------------------------+ | 2 | UNSUPPORTED_OPTION | Unsupported option | +-------+-----------------------------+-------------------------+ | 3 | MALFORMED_MESSAGE | Message is malformed | +-------+-----------------------------+-------------------------+ | 4 | USER_DECLINED | User declined to | | | | provide a password or | | | | permit use of the cache | +-------+-----------------------------+-------------------------+ | 5 | PROMPT_TIMED_OUT | User failed to take | | | | action in response to a | | | | prompt | +-------+-----------------------------+-------------------------+ | 6 | CACHE_DECLINED | Cache entry was not | | | | added | +-------+-----------------------------+-------------------------+ | 7 | INVALID_FILENAME | An inappropriate | | | | filename was requested | | | | for caching | +-------+-----------------------------+-------------------------+ | 8 | COULD_NOT_REMOVE_FROM_CACHE | An entry remains in the | | | | cache that should have | | | | been dropped | +-------+-----------------------------+-------------------------+ | 9 | NO_ASSOCIATED_VALUE | A request was made to | | | | drop a password or | | | | internal cache entry | | | | that did not exist | +-------+-----------------------------+-------------------------+ | 10 | INVALID_CONFIGURATION_FILE | The config file was not | | | | well-formed | +-------+-----------------------------+-------------------------+ | 11 | MALFORMED_PASSWORD | Password format is not | | | | supported | +-------+-----------------------------+-------------------------+ | 12 | MALFORMED_INTERNAL_LABEL | The Internal contextual | | | | label was malformed | Gillmor Expires 9 February 2025 [Page 12] Internet-Draft OpenPGP Prompting and Caching August 2024 +-------+-----------------------------+-------------------------+ | 13 | MALFORMED_INTERNAL_VALUE | The Internal value was | | | | too short | +-------+-----------------------------+-------------------------+ | 14 | INCOMPATIBLE_OPTIONS | The command line | | | | options and environment | | | | variables supplied are | | | | mutually incompatible | +-------+-----------------------------+-------------------------+ | 15 | COULD_NOT_NOTIFY | A notification was | | | | requested, but could | | | | not be produced | +-------+-----------------------------+-------------------------+ | 16 | MALFORMED_SUBSESSION_ID | A malformed subsession | | | | identifier was supplied | +-------+-----------------------------+-------------------------+ Table 1: Return Codes for opac 4. Configuration opac should behave sensibly without any configuration file, or with an empty configuration file. It always looks for its configuration as an "inifile" in exactly one location: * If $OPAC_SUBSESSION is set to a non-empty string, and $OPAC_SUBSESSION_CONFIG is set, it looks for the file in $OPAC_SUBSESSION_CONFIG. FIXME: if $OPAC_SUBSESSION_CONFIG is unset, should it really fall through to reading the main session config? * Otherwise, if $XDG_CONFIG_HOME is set, it looks in $XDG_CONFIG_HOME/opac/config * Otherwise, it looks in ~/.config/opac/config opac does not attempt to coalesce multiple configuration files. 4.1. Configuration Example [opac] cache_duration=10s [opac "~/.private/bob.key"] on_reuse=confirm Gillmor Expires 9 February 2025 [Page 13] Internet-Draft OpenPGP Prompting and Caching August 2024 4.2. Configuration File Format The OPAC configuration file is an "inifile". It has one optional base section, simply named opac, which contains configuration values that supersede the built-in defaults, and take effect when no override is present. It has any number of file-specific subsections, which contain configuration overrides for specific objects in the filesystem. A file-specific subsection (within opac) is named with a representation of a path in the filesystem. The path MUST start with either / (indicating an absolute path to a file) or ~/ (indicating a filename relative to the user's home directory). opac MUST ignore any unexpected or unknown sections or values. If opac ever updates its own configuration, it MUST NOT alter any unexpected or unknown sections or values, reorder sections, or modify comments. When looking up a configuration option for a prompt or a cache for a password or internal value associated with FILENAME, opac looks up such a key in the following places: 1. In the [opac "FILENAME"] subsection of the config file 2. In [opac] section of the config file 3. In the application's built-in settings Each configuration option is looked up separately, so in the example above, when using ~/.private/bob.key, on_reuse is set to confirm and cache_duration is set to 10s. 4.3. What are the configuration values? There are three configuration directives available. 4.3.1. cache_duration: Time to Cache The cache_duration directive accepts a CACHE_DURATION value (see Section 6.2). A reasonable default value for cache_duration is 1h. Gillmor Expires 9 February 2025 [Page 14] Internet-Draft OpenPGP Prompting and Caching August 2024 4.3.2. prompt_timeout: Time to Wait For User Feedback The prompt_timeout directive accepts a PROMPT_TIMEOUT value (see Section 6.1). A reasonable default value for prompt_timeout is 1m. 4.3.3. on_reuse: Action on Cache Retrieval The on_reuse directive accepts a REUSE_DIRECTIVE value (see Section 6.3). A reasonable default value for on_reuse is notify. 5. Subsessions OPAC is typically used directly in association with the user's session. However, it also supports the idea of a "subsession", to support an isolated cache for the use cases of test suites (see Appendix A.2) or isolated applications (see Appendix A.3). A subsession shares the same prompting mechanism as the main session, but uses a completely distinct cache and configuration. opac knows to use a subsession when the environment variable OPAC_SUBSESSION is set to a non-empty string. The value of OPAC_SUBSESSION is the subsession identifier. 5.1. Subsession Identifiers The subsession is identified by an ASCII string consisting of printable (non-whitespace) characters. The subsession identifier must be at least one octet long, and no more than 64 octets. If opac is using the cache, and OPAC_SUBSESSION is non-empty and it is not conformant to this specification, opac fails with MALFORMED_SUBSESSION_ID. 5.2. Subsession Configuration When using a subsession (that is, when OPAC_SUBSESSION is set), if the environment variable OPAC_SUBSESSION_CONFIG is set, opac reads its the configuration file from the location named in OPAC_SUBSESSION_CONFIG. Gillmor Expires 9 February 2025 [Page 15] Internet-Draft OpenPGP Prompting and Caching August 2024 6. Data Types 6.1. PROMPT_TIMEOUT This represents a length of time that a prompt should be displayed to the user before it is dismissed as having been ignored. It is represented in an integer number of hours, minutes, and seconds, using letter suffixes. Hours, if present, come first and are suffixed with h. Minutes, if present, appear in the middle, and are suffixed with m. Seconds, if present, appear at the end, and are suffixed with s. If no letter suffix is present, a completely numeric value is read as seconds. For example, 1m30s means the same timeout as 90, and 1h2s means the same timeout as 3602 Using the value 0 means that where prompting is necessary, opac MUST NOT prompt, but should instead immediately fail with PROMPT_TIMED_OUT. Using the special value never means to never deliberately time out the prompt. An OPAC implementation may nevertheless fail with PROMPT_TIMED_OUT due to other compelling circumstances. For example, if the prompt is a confirmation prompt for the reuse of a cached value, and that cached value is removed from the cache, or if the user's session is ending and the OPAC implementation is trying to clean up, it MAY fail with PROMPT_TIMED_OUT. 6.2. CACHE_DURATION This represents the maximum amount of time that a value will persist in its position in the cache. It is represented in an integer number of days, hours, minutes, and seconds, using letter suffixes. Days, if present, come first and are suffixed with d. Hours, if present, follow days and are suffixed with h. Minutes, if present, follow hours, and are suffixed with m. Seconds, if present, appear at the end, and are suffixed with s. If no letter suffix is present, a completely numeric value is read as seconds. For example, 1m30s means the same timeout as 90, and 1h2s means the same timeout as 3602 Gillmor Expires 9 February 2025 [Page 16] Internet-Draft OpenPGP Prompting and Caching August 2024 Using the special value never is the same as 0, meaning that OPAC should never cache this value. (This is particularly useful in the configuration file; from the command line, the easier thing would be to simply never ask for caching in the first place) This value may be supplied from the command line during cache insertion (see Section 2.3 and Section 2.4) and from the configuration file (see Section 4.3.1). If it is specified in both the command line and the configuration file, the shorter of the two values provided is preferred. 6.3. REUSE_DIRECTIVE This form describes what an OPAC implementation should do when a value is elicited from the cache, and the cache can supply the value. It can be one of three possible values, in increasing order of strictness: ok, notify, or confirm. ok permits the use of the cached value without any interaction with the user. notify emits a standard notification while permitting the use of the cached value. confirm asks the user to confirm the use of the cached value. In this case, the user may decline its use. This value may be supplied from the command line during cache insertion (see Section 2.3 and Section 2.4) and from the configuration file (see Section 4.3.3). If it is specified in both the command line and the configuration file, the stricter of the two values provided is preferred. For example, if the configuration file indicates that a given value can be replayed from the cache as long as the user is notified (notify), then a command-line option of --on- reuse=confirm will ask the user to confirm the use, but a command- line option of --on-reuse=ok will not suppress the notification. 6.4. NOTIFICATION_HANDLE This is a unique string that can be used to dismiss a previous notification when that notification is no longer relevant. It is produced by opac notify (see Section 2.7) and can be used to dismiss a specific notification with opac dismiss (see Section 2.8). It consists only of printable ASCII characters (no whitespace), and will be no more than 64 characters in length. Gillmor Expires 9 February 2025 [Page 17] Internet-Draft OpenPGP Prompting and Caching August 2024 6.5. INTERNAL_LABEL This is a label used by an OpenPGP implementation to associate the output of a specific cryptographic process associated with a file. It is implementation-specific, but must be a UTF-8 encoded string of no more than 128 octets in length. It is used, with the filename itself, as an index into the OPAC cache in opac cache-internal (see Section 2.4) and opac get-internal (see Section 2.5). As a simple example, an OpenPGP implementation might choose to base64-encode the S2K Usage Octet and the accompanying parameters from a Secret Key Packet, and then prefix that with a short identifying label. 6.6. INTERNAL_VALUE An internal value is simply a non-empty octet string of no more than 1000 octets. Beyond the length limits, there are no constraints on its content. 6.7. MESSAGE This is a limited textual string that is presented to the user when the user is prompted or notified. It is a way for an application requesting data from the user to provide some additional context for what is being requested or why it is being requested. It MUST be UTF-8-encoded, no more than 120 characters, with no more than two internal LINE FEED (U+000A) characters. Leading and trailing whitespace will be ignored. 6.8. PASSWORD OPAC expects a password to be a UTF-8-encoded, non-empty string. The password MUST NOT contain any LINE FEED (U+000A) characters, and leading or trailing whitespace may be stripped. A password MUST NOT be more than 1000 octets in length in its UTF-8 form. If a password is supplied that cannot be brought into conformance with these specifications, opac will fail with MALFORMED_PASSWORD. The OpenPGP standard permits the use of a password as an arbitrary bytestream, so it is possible that some OpenPGP material may be encrypted with a password that does not conform to these constraints. It is recommended to use an OpenPGP tool that does not use OPAC change to such a password to a conformant string. Gillmor Expires 9 February 2025 [Page 18] Internet-Draft OpenPGP Prompting and Caching August 2024 7. Subtleties and Nuance The design of OPAC is intended to make normal, straightforward use cases easy and simple. It cannot handle all possible scenarios. This section describes some details about the underlying assumptions in its design, including some descriptions of scenarios where it will not work as expected. 7.1. The OPAC Cache The OPAC main cache is structured as a two-level nested tree. The top level index is by filename. Each filename can have zero or one PASSWORDs (see Section 6.8) associated with it. Additionally, each filename can have zero or more INTERNAL_LABELs (see Section 6.5) associated with it. If the (filename, INTERNAL_LABEL) tuple exists, it has an INTERNAL_VALUE associated with it. Here is an example populated cache showing the indexing and the types of stored values: ├┬╴/tmp/secret-message.pgp │└─╴"BarPGP:23e1b0a7cbe322bf5c096cb3d1bc" → INTERNAL_VALUE ├─╴/home/bob/src/mysoft/distribution.key → PASSWORD └┬╴/home/bob/.private/bob.key → PASSWORD ├─╴"FooPGP:YXdsO2loeXFhaXdlZ2xhd2Vwa2Ewd2l0MjI" → INTERNAL_VALUE └─╴"FooPGP:Z3E0Mzl2bTtqOWZvd2x3" → INTERNAL_VALUE Some observations about this example: * No password was cached for /tmp/secret-message.pgp, but an internal value was cached, probably by the BarPGP implementation. * Only a password was cached for the distribution.key. * bob.key has cached a password and multiple internal values. One internal value might correspond to the output of some stage of the S2K process for unlocking the primary key, while the other might be for unlocking a subkey. * The choice of the INTERNAL_LABELs present in the cache are arbitrary and up to the implementations. In this example, FooPGP and BarPGP have used a namespacing approach within labels to ensure that they don't accidentally collide with each other's labels. The cache depends on a view of the filesystem that aligns with the view seen by the process invoking opac. Gillmor Expires 9 February 2025 [Page 19] Internet-Draft OpenPGP Prompting and Caching August 2024 7.1.1. Cache Lifecycle When the user's sessions starts, the OPAC cache is empty. As elements are added to the cache, they should be marked clearly with expected expiration times, and removed (any associated memory wiped if possible) promptly when the stored password or internal value expires from the cache. FIXME: configuration might change between insertion and retrieval from the cache; if they differ, which one should take precedence at retrieval time? 7.1.2. Subsession Cache Structure Each subsession, identified by a subsession ID, has an independent cache that is shaped just like the main cache. If OPAC gets a cache retrieval request for a previously unknown subsession ID, it treats it as a cache miss. If OPAC gets a cache insertion request for a previously unknown subsession ID, it initializes a new, empty cache for the subsession, and inserts the password or internal value in the new cache. When a cache insertion or retrieval is performed under a subsession, it works only with that subsession cache, and does not look in the main cache. When an insertion or retrieval is done outside of a subsession, it only uses the main cache, and does not look in any subsession cache. See also Section 7.3 for more about subsessions. 7.1.3. Cache Insertion vs. Cache Retrieval Note that the opac interface completely separates cache insertion from cache retrieval. In particular, opac get-password retrieves a password from the cache or from the user, but does not insert a user-provided password in the cache. This is because opac is not expected to directly interact with the OpenPGP objects, so it cannot know whether the user's supplied password works or not. 7.2. OPAC Prompting OPAC supports three types of user interaction, collectively known as "prompting". Gillmor Expires 9 February 2025 [Page 20] Internet-Draft OpenPGP Prompting and Caching August 2024 * Notification -- this might happen during cache retrieval, depending on the configuration, or explicitly via opac notify. * Confirmation (boolean approval) -- this might happen during cache retrieval, depending on the configuration. * Elicit a string of text -- this might happen during opac get- password, if the requested password is not found in the cache. Additionally, a user of OPAC may dismiss a prior notification (when it is no longer relevant) -- but only notifications from opac notify can be dismissed in this way. Each of these forms of prompting MAY be accompanied by an application-supplied MESSAGE, which will be clearly delimited from any OPAC-specific metadata, such as the path to the associated file, or any information about the caller of opac. 7.3. OPAC Subsessions OPAC needs subsessions because there are circumstances where the desired scope of an OpenPGP unlocking step isn't contiguous with the user's interactive session as a whole. In particular, a software test suite is likely to want to lock and unlock various OpenPGP objects independently of the rest of the session (see Appendix A.2). And an application that spans multiple processes want to lock and unlock various OpenPGP objects without interference from the rest of the session (see Appendix A.3). 7.3.1. Subsession Configuration Note that the user's configuration values can't necessarily override subsession configuration, because subsession config might come from somewhere else, via the OPAC_SUBSESSION_CONFIG environment variable. The two main use cases for subsessions are test suites and isolated apps. A test suite wants to use its own dedicated configuration, and for reproducibility it shouldn't be influenced by configuration that happens to live outside its own workspace, even if run within the user's session. Similarly, an isolated app might want the user to be able to configure how the app handles OpenPGP prompting and caching, but without asking the user to manage some system-wide configuration. Gillmor Expires 9 February 2025 [Page 21] Internet-Draft OpenPGP Prompting and Caching August 2024 7.4. Passwords vs. Internal Values OPAC handles both passwords and internal values. Note the differences between how it handles each of these things: * passwords are human-readable UTF-8 strings, internal values are octet strings. * one password per FILENAME (in the main cache) * arbitrary numbers of internal values per FILENAME * nonetheless, they are all bound together at the FILENAME level for things like configuration and drop-cache. 8. References 8.1. Normative References [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10.17487/RFC2119, March 1997, . [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, May 2017, . [RFC9580] Wouters, P., Ed., Huigens, D., Winter, J., and Y. Niibe, "OpenPGP", RFC 9580, DOI 10.17487/RFC9580, July 2024, . 8.2. Informative References [I-D.dkg-openpgp-external-secrets] Gillmor, D. K., "OpenPGP External Secret Keys", Work in Progress, Internet-Draft, draft-dkg-openpgp-external- secrets-01, 2 August 2024, . [I-D.dkg-openpgp-stateless-cli] Gillmor, D. K., "Stateless OpenPGP Command Line Interface", Work in Progress, Internet-Draft, draft-dkg- openpgp-stateless-cli-10, 1 March 2024, . Gillmor Expires 9 February 2025 [Page 22] Internet-Draft OpenPGP Prompting and Caching August 2024 [SEMVER] Preston-Werner, T., "Semantic Versioning 2.0.0", 18 June 2013, . Appendix A. Example Uses opac offers a minimal interface that can be used for a variety of common use cases. This section provides a few simple motivational examples of how it can be integrated to support these cases. These examples are not necessarily complete, but should give a flavor of the supported scenarios. A.1. Integration with the Stateless OpenPGP Command-Line Interface (SOP) It should be easy to integrate OPAC into SOP, the Stateless OpenPGP command-line interface described in [I-D.dkg-openpgp-stateless-cli]. The integration can make use of prompting on its own, or it may also make use of the caching capabilities for a more usable, session- oriented approach. A.1.1. OPAC + SOP: Prompting Using POSIX-compliant shell, an OpenPGP application can combine opac and sop to decrypt a message with a locked Secret Key like this: BOBPW=$(opac get-password bob.key) \ sop decrypt --with-key-password @ENV:BOBPW bob.key < msg.txt Alternately, using slightly fancier shells like bash or zsh that offer command substitution: sop decrypt --with-key-password <(opac get-password bob.key) \ bob.key < msg.txt Other similar combinations (such as file descriptor redirection) should be straightforward from a wide range of programming environments. A.1.2. OPAC + SOP: Prompting and Caching If the OpenPGP application also wants to make use of opac's cache, it can do something like the following POSIX shell: Gillmor Expires 9 February 2025 [Page 23] Internet-Draft OpenPGP Prompting and Caching August 2024 # from SOP's standard return codes: KEY_IS_PROTECTED=67 BOBPW=$(opac get-password bob.key) PASS=$BOBPW sop decrypt --with-key-password @ENV:PASS \ bob.key < msg.txt > msg.decrypted; SOP_RESULT=$? if [ 0 -eq $SOP_RESULT ]; then printf '%s' "$PASS" | opac cache-password bob.key elif [ $KEY_IS_PROTECTED -eq $SOP_RESULT ]; then opac drop-cache bob.key fi This process shows the use of the password cache: when a secret is successfully unlocked, the password is retained in the cache; if it does not work, the cache for that object is deliberately cleared so that the user would be prompted in the future. A.2. Test Suite Isolation A test suite that includes locked OpenPGP material typically wants to run code that would normally interact directly with the user, but should not require actual user interaction. Furthermore, a test suite typically does not want to interact with the caching or prompting of the interactive session of the developer running the test suite. OPAC aims to support this use case with subsessions (see Section 5). An OPAC subsession is identified with a unique string, and a subsession indicated to opac through an environment variable. Here is a comprehensive example in POSIX shell, wrapping a test suite so that it uses an OPAC subsession with a randomly-derived subsession identifier. It uses a dedicated configuration file to discourage prompting, and to cache everything for the entire session. It pre- emptively seeds the password cache with the password for the locked key. After the test suite is run, it cleans up after itself. Gillmor Expires 9 February 2025 [Page 24] Internet-Draft OpenPGP Prompting and Caching August 2024 export OPAC_SUBSESSION=$(head -c12 < /dev/urandom | base64) cat > opac.config <