sequoia-keystore 0.3.0

Sequoia's private key store server.
Documentation
@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.

    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.

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

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

  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.

    list @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: BoolResult);
    # Whether the key is locked.

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

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

  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 {
      unspecified @0 :Void;
      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.
    }
  }

  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;
    }
  }
}