@0xc996c4872a6820ae;

# This file describes the interface between a client of the keystore
# (e.g., sq) and the keystore.  Keystore backends implement the traits
# defined in the sequoia-keystore-backend crate.

interface Keystore {
  backends @0 () -> (result: Result(List(Backend)));
  # Returns the backends.

  decrypt @1 (pkesks: List(Data)) -> (result: Result(DecryptedPkesk));
  # Decrypts a PKESK.
  #
  # If multiple PKESKs are provided, this tries to use the most
  # convenient key from the user's perspective.  In particular, it
  # tries to use an unlocked key before trying a locked key.
  #
  # If no keys could be used, but there is at least one registered key
  # that could be used, this returns `Error::Inaccessible` and the
  # list of keys that could be used, and why they can't be used.

  struct InaccessibleDecryptionKey {
    # Returned by Keystore::decrypt.

    keyDescriptor @0: KeyDescriptor;
    # The inaccessible key.

    pkesk @1: Data;
    # The serialized PKESK that triggered this error.
  }

  struct DecryptedPkesk {
    index @0: UInt32;
    # The index of the PKESK that was decrypted.

    fingerprint @1: Data;
    # The fingerprint of the key used to decrypt the data.

    fingerprintVersion @4: UInt8;
    # The version of the fingerprint of the key used to decrypt the
    # data.

    algo @2: UInt8;
    # The decrypted symmetric algorithm identifier.
    #
    # See https://datatracker.ietf.org/doc/html/rfc4880#section-5.1

    sessionKey @3: Data;
    # The decrypted session key.
    #
    # See https://datatracker.ietf.org/doc/html/rfc4880#section-5.1
  }

  interface Backend {
    id @0 () -> (result: Result(Text));
    # Returns the backend's unique identifier.
    #
    # It should be a well-formed UTF-8 string, which should give a
    # curious user a pretty good idea of what backend this is.

    devices @1 () -> (result: Result(List(Device)));
    # List known devices.

    import @2 (cert: Data) -> (result: Result(List(ImportResult)));
    # Imports secret key material.
    #
    # cert is a serialized TSK.  Keys without secret key material are
    # silently ignored.
    #
    # If a key already exists, it is overwritten.
    #
    # An ImportResult is returned for each secret key.  If the TSK
    # doesn't include any secret keys, then an empty list is returned.
    #
    # Some backends require additional information to import a key.
    # These backends should `Error::externalImportRequired`, and
    # indicate how a user might import a key to this backend.

#    scan @2 () -> (result: VoidResult);
#    # Search for devices.
#
#    register @3 (description: Text) -> (result: Result(Device));
#    # Register a device.
  }

  struct ImportResult {
      key @0: KeyDescriptor;

      status @1: ImportStatus;
  }

  struct ImportStatus {
    union {
      new @0 :Void;
      # The imported object is new.

      updated @1: Void;
      # The imported object updated an existing object.

      unchanged @2: Void;
      # The object already existed, and is unchanged.
    }
  }

  interface Device {
    id @0 () -> (result: Result(Text));
    # Returns a unique device identifier.
    #
    # It should be a well-formed UTF-8 string, which should give a
    # curious user a pretty good idea of what device this is.

    keys @1 () -> (result: Result(List(KeyDescriptor)));
    # List known keys associated with this device.
  }

  struct KeyDescriptor {
    handle @0: Key;

    publicKey @1: Data;
    # The serialized PublicKey or PublicSubkey packet without OpenPGP framing.
    #
    # If the creation time is not known, the backend should set the
    # creation time to 0.
  }

  interface Key {
    id @0 () -> (result: Result(Text));
    # Returns a unique key identifier.
    #
    # It should be a well-formed UTF-8 string, which should give a
    # curious user a pretty good idea of what key this is.

    decryptCiphertext @1 (algo: UInt8, ciphertext: Data, plaintextLen: UInt32)
      -> (result: Result(Data));
    # Decrypts the ciphertext.
    #
    # The semantics are identical to
    # `sequoia_openpgp::crypto::Decryptor::decrypt`.
    #
    # https://docs.sequoia-pgp.org/sequoia_openpgp/crypto/trait.Decryptor.html#tymethod.decrypt

    signMessage @2 (hash_algo: UInt8, digest: Data)
      -> (result: Result(SignedData));
    # Signs the digest.

    unlock @3 (password: Data) -> (result: VoidResult);
    # Unlocks the key.
    #
    # If the key is not available, this first attempts to connect to
    # the device (e.g., bring up an ssh tunnel).
    #
    # If the key is already unlocked, this returns an error.
    #
    # If the password is wrong, thus returns an error.
    #
    # If the key can be unlocked, the key remains unlocked until
    # the cache flushes unlocked key.

    available @4 () -> (result: BoolResult);
    # Whether the key is available.
    #
    # If false, this usually means the device needs to be connected.

    locked @5 () -> (result: Result(Protection));
    # Whether the key is locked, and the type of protection.

    decryptionCapable @6 () -> (result: BoolResult);
    # Whether the key can be used for decryption.

    signingCapable @7 () -> (result: BoolResult);
    # Whether the key can be used for signing.

    export @8 () -> (result: Result(Data));
    # Exports the secret key material.
    #
    # The secret key material is exported as a serialized
    # SecretKey or SecretSubkey packet without OpenPGP framing.

    passwordSource @9 () -> (result: Result(PasswordSource));
    # How the password is obtained.

    changePassword @10 (password: ChangePasswordAction)
        -> (result: VoidResult);
    # Changes the key's password.

    deleteSecretKeyMaterial @11 () -> (result: VoidResult);
    # Deletes the key's secret key material.
  }

  struct Protection {
    union {
      unlocked @0 :Void;
      # The secret key material is unlocked.

      unknownProtection @1 :Text;
      # The backend is not able to determine if the secret key
      # material is protected.
      #
      # It is, however, safe to try a secret key operation (e.g., the
      # retry counter will not be decremented).  Trying an operation
      # may trigger an external event, like a system pin entry dialog.
      #
      # The string is an optional hint for the user.

      password @2 :Text;
      # The secret key material is protected by a password.  It can
      # be unlocked using the unlock interface.
      #
      # The string is an optional hint for the user.

      externalPassword @3 :Text;
      # The secret key material is protected, and can only be unlocked
      # using an external terminal.
      #
      # The string is an optional hint for the user.
      #
      # Note: some devices don't provide a mechanism to determine if
      # the secret key material is currently locked.  For instance,
      # some smart cards can be configured to require the user to
      # enter a pin on an external keypad before their first use, but
      # not require it as long as the smart card remains attached to
      # the host, and also not provide a mechanism for the host to
      # determine the current policy.  Such devices should still
      # report `Protection::ExternalPassword`, and should phrase the
      # hint appropriately.

      externalTouch @4 :Text;
      # The secret key material is protected, and can only be unlocked
      # if the user touches the device.
      #
      # The string is an optional hint for the user.

      externalOther @5 :Text;
      # The secret key material is protected, and can only be unlocked
      # externally.
      #
      # The string is an optional hint for the user, e.g., "Please connect
      # to the VPN."
    }
  }

  struct PasswordSource {
    union {
      inline @0 :Void;
      # The application must provide the password to unlock the key.
      #
      # This means that if a key is locked, then before executing a
      # private key operation, the key must be unlocked using
      # [`KeyHandle::unlock`].

      inlineWithConfirmation @1 :Void;
      # The application must provide the password to unlock the key,
      # but operations must be confirmed externally.
      #
      # This means that if a key is locked, then before executing a
      # private key operation, the key must be unlocked using
      # [`KeyHandle::unlock`].  These operations may require the user
      # externally confirm the operation.

      externalOnDemand @2 :Void;
      # The user must provide the password out of band.
      #
      # The user provides the password using an external PIN pad, or
      # via a trusted user interface so as to not reveal the password
      # to the application or system.
      #
      # This means if the key is locked, the user will be prompted to
      # unlock it as a side effect of a private key operation.
      #
      # The application can use [`KeyHandle::unlock`] to proactively
      # cause the user to be prompted to unlock the key.

      externalSideEffect @3 :Void;
      # The user must provide the password out of band as a side
      # effect of an operation.
      #
      # The user provides the password using an external PIN pad, or
      # via a trusted user interface so as to not reveal the password
      # to the application or system.
      #
      # This means if the key is locked, the user will be prompted to
      # unlock it as a side effect of a private key operation.
      #
      # The application cannot proactively cause the user to be
      # prompted to unlock the key; the prompt is only a side effect
      # of a private key operation.  That is, the key cannot be
      # unlocked using [`KeyHandle::unlock`].
    }
  }

  struct ChangePasswordAction {
    union {
      password @0 :Data;
      # Change the password to the specified password.
      #
      # If the empty string is provided, this removes any password
      # protection.

      ask @1 :Void;
      # Ask the user for a password, and change the password
      # accordingly.
      #
      # This method only works for keys that have an external password
      # source.
    }
  }

  struct SignedData {
    pkAlgo @0 : UInt8;

    mpis @1 : Data;
    # The serialized mpi::Signature, without OpenPGP framing.
  }

  struct Unit {}
  # Unit struct.  Useful with Result.

  struct Error {
    union {
      genericError @0 :Text;
      # A generic error occurred.
      #
      # Text is a description of the error.

      protocol @1: Void;
      eof @2: Void;

      inaccessibleDecryptionKey @3: List(InaccessibleDecryptionKey);
      # Returned by `KeyStore::decrypt`.
      #
      # The operation failed, because no keys that could be used to
      # complete the operation are accessible.  This could be because
      # they are locked, or a device that contains a key is
      # registered, but not connected.

      notDecryptionCapable @4: Text;
      # The key cannot be used for decryption.

      notSigningCapable @5: Text;
      # The key cannot be used for signing.

      internalError @6: Text;
      # An internal server error occurred.

      externalImportRequired @7: Text;
      # The backend doesn't support importing keys using this interface.
      #
      # This error is returned by [`Backend::import`] if the backend
      # requires backend-specific information in order to import a
      # key, which is not provided via this interface.  For instance,
      # when importing a key to a smartcard, the user needs to specify
      # the smartcard, and the slot to use on the smartcard.  Rather
      # than try and model these parameters using this generic
      # interface, backends should just have their own tools.  The
      # text should be a hint that is displayed to the user describing
      # how to find the tool.

      secretKeyMaterialSealed @8: Text;
      # The secret key material cannot be exported.
      #
      # This error is returned by [`Key::export`] if the secret key
      # material cannot be exported.  Many backends do not allow
      # exporting secret key material.  Some may only allow exporting
      # secret key material in admin mode.  In particular in the
      # latter case, the text should describe what the user has to do
      # to export the secret key material.

      noInlinePassword @9: Text;
      # The key doesn't support inline passwords.
      #
      # Password entry is taken care of by the device managing the
      # key.  For instance, the password may be obtained using an
      # external PIN pad.

      noExternalPassword @10: Text;
      # The key doesn't support getting passwords.
      #
      # The password must be provided inline.  The password cannot be
      # obtained using something like an external PIN pad.
    }
  }

  struct Result(T) {
    union {
      ok @0 :T;
      err @1 :Error;
    }
  }

  struct VoidResult {
    # Generic types can only be pointers.  So to return Result<()>, we
    # need a different type.  See:
    #
    #   'Kenton Varda' via Cap'n Proto Thu, 30 Apr 2020 15:19:13 -0700
    #   Subject: Re: [capnproto] Why "Sorry, only pointer types can be used as generic parameters."?
    #
    #   https://www.mail-archive.com/capnproto@googlegroups.com/msg01286.html
    union {
      ok @0 :Void;
      err @1 :Error;
    }
  }

  struct BoolResult {
    # Generic types can only be pointers.  So to return Result<Bool>, we
    # need a different type.  See:
    #
    #   'Kenton Varda' via Cap'n Proto Thu, 30 Apr 2020 15:19:13 -0700
    #   Subject: Re: [capnproto] Why "Sorry, only pointer types can be used as generic parameters."?
    #
    #   https://www.mail-archive.com/capnproto@googlegroups.com/msg01286.html
    union {
      ok @0 :Bool;
      err @1 :Error;
    }
  }
}
