Skip to main content

OpenPGP Prompting and Caching
draft-dkg-openpgp-prompting-caching-00

Document Type Active Internet-Draft (individual)
Author Daniel Kahn Gillmor
Last updated 2024-08-08
RFC stream (None)
Intended RFC status (None)
Formats
Additional resources GitLab Repository
Mailing List
Issue Tracker
Stream Stream state (No stream defined)
Consensus boilerplate Unknown
RFC Editor Note (None)
IESG IESG state I-D Exists
Telechat date (None)
Responsible AD (None)
Send notices to (None)
draft-dkg-openpgp-prompting-caching-00
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,
              <https://www.rfc-editor.org/rfc/rfc2119>.

   [RFC8174]  Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
              2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,
              May 2017, <https://www.rfc-editor.org/rfc/rfc8174>.

   [RFC9580]  Wouters, P., Ed., Huigens, D., Winter, J., and Y. Niibe,
              "OpenPGP", RFC 9580, DOI 10.17487/RFC9580, July 2024,
              <https://www.rfc-editor.org/rfc/rfc9580>.

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,
              <https://datatracker.ietf.org/doc/html/draft-dkg-openpgp-
              external-secrets-01>.

   [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,
              <https://datatracker.ietf.org/doc/html/draft-dkg-openpgp-
              stateless-cli-10>.

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, <https://semver.org/>.

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 <<EOF
   [opac]
   cache_duration=session
   prompt_timeout=0
   EOF
   export OPAC_SUBSESSION_CONFIG=$(pwd)/opac.config
   printf correct horse battery staple | opac cache-password ./bob.key

   # ... run test suite that uses the locked bob.key...

   opac drop-cache
   unset -v OPAC_SUBSESSION OPAC_SUBSESSION_CONFIG

A.3.  Single-Application Subsession Use

   An OPAC subsession can also be used by a single application, to
   isolate its prompting and caching state from other applications also
   running in the session.

   For example, an application might be split across multiple processes,
   and each process should be able to use the same shared cache as the
   other processes in the application.  But the application might not
   want its own OpenPGP material to be available for other applications
   running in the same user session.

   An application in this situation might set up an OPAC subsession upon
   application start, exporting it to all child processes.  The two
   examples in this subsection are in Python-like pseudocode.

   def app_init():
       ...
       if use_subsession:
           opac_subsession = base64(get_random(12))
           subprocess_environment["OPAC_SUBSESSION"] = opac_subsession
       ...

   And on application shutdown, it could flush the cache:

   def app_cleanup():
       ...
       if "OPAC_SUBSESSION" in subprocess_environemnt:
          subprocess("opac", "drop-cache")
       ...

Gillmor                  Expires 9 February 2025               [Page 25]
Internet-Draft        OpenPGP Prompting and Caching          August 2024

A.4.  OpenPGP Internal Use

   An application that uses OpenPGP in a more fine-grained way than just
   interacting with a SOP instance might also want to cache some
   intermediate/internal values to free up resources in future
   processing.

   For example, if an S2K operation takes a gigabyte of RAM and 3
   seconds on the current processor, the application might want to cache
   the result of the S2K operation, rather than just caching the
   password.

   The application can use OPAC for this kind of caching as well, but it
   uses a different interface than the password interface, because the
   user is never prompted to supply the value itself.  Depending on the
   configuration, the user may be prompted to confirm the use of the
   value, though.

   In Pythonic pseudocode, that might look something like:

   def unlock_key(key, explanation):
       label = get_internal_label(key)
       value = subprocess("opac", "get-internal",
                          "--message", explanation,
                          key.filename, label)
       if value is None:
           password = subprocess("opac", "get-password",
                                 "--message", explanation,
                                 key.filename).get_stdout()
           value = OpenPGP_string_to_key(key.s2kparams, password)
       if unlock_key_with_intermediate(key, value):
           subprocess("opac", "cache-internal",
                      key.filename, label).write_stdin(value)
       else:
           subprocess("opac", "drop-cache", key.filename)

A.5.  Notifying the User of External Requirements

   In some cases, an OpenPGP-using application might need access to
   external secret key material, like a smartcard, USB hardware token,
   or some other device (see [I-D.dkg-openpgp-external-secrets]).  In
   such a case, the application may need to notify the user that they
   need to take some additional action, like pressing a button on a USB
   device, or scanning their fingerprint.

Gillmor                  Expires 9 February 2025               [Page 26]
Internet-Draft        OpenPGP Prompting and Caching          August 2024

   When the application is aware of this need, it might want to send the
   user a generic notification.  Once the user presses the button, the
   application might want to dismiss the notification.  In Pythonic
   pseudocode, that might look like:

   def handle_external_key():
       handle = subprocess("opac", "notify", "--message",
                           "Please press the USB button")
       ... # wait on external mechadimfor operation to complete
       subprocess("opac", "notify", "--clear", handle)

Appendix B.  Future Work

   This document describes a command-line interface, as that is a widely
   interoperable interface that anyone can implement.

   It might be useful to describe more nuanced, alternate interfaces
   that would be easier to work with for some implementations, like a C
   library API, or even a D-Bus interface for those platforms that use
   D-Bus.

   One of the tricks for this is that the interface -- in particular
   opac get-password or opac-get-internal-value -- can be quite high-
   latency, particularly when awaiting a response from the user.  This
   probably means that a more nuanced interfce will require some kind of
   asynchronous interface functionality to avoid blocking the main
   application.

Appendix C.  Acknowledgements

   Much appreciation to the people who gave feedback on this document,
   including Jameson Rollins and Heiko Schäfer.

Appendix D.  Document History

D.1.  draft-dkg-openpgp-prompting-caching-00

   Initial sketch of the interface.

Author's Address

   Daniel Kahn Gillmor
   American Civil Liberties Union
   125 Broad St.
   New York, NY,  10004
   United States of America
   Email: dkg@fifthhorseman.net

Gillmor                  Expires 9 February 2025               [Page 27]