API Sketch


Update 2015-04-22

Update 2014-04-07

Dominik has updated the API to reflect the proposed changes and is getting them merged into K9:

Updated 2013-12-04

After several iterations with feedback from K9 developers (summary of issues) we propose the following changes.

1. All input/output will be performed with ParcelFileDescriptor (this makes the current OpenPgpData class obsolete)
2. All user interaction will be initiated via the consumer/client program via use of PendingIntent

For all operations, the consumer should check to see if a PendingIntent was returned, indicating user interaction is required. If so, then the consumer must explicitly start the intent to launch the UI and perform the original operation. In the case where user interaction is not required, the operation will be performed and returned asynchronously via the callback.

Example operation: 'sign'

Take a sign() operation for example, the public method signature would be:

oneway void sign(in ParcelFileDescriptor input, in ParcelFileDescriptor output, in IOpenPgpCallback callback);

Case 1: passphrase cached
When called, if the passphrase is cached, and therefore user interaction is not required, the sign operation occurs asynchronously and the result is returned via the provided callback with the data being written to the output ParcelFileDescriptor.

Case 2: passphrase not cached
When called, if the passphrase is not cached, and therefore user interaction is required, then the openpgp provider service will return a PendingIntent via the provided callback. With this PendingIntent the consumer application can call startActivity() when appropriate. This will launch the necessary Activity to prompt the passphrase from the user, then the original sign() operation will be performed. Again, the result is returned via the provided callback with the data being written to the output ParcelFileDescriptor.

Open Questions

  1. Should the consumer/client pass the ParcelFileDescriptor or should the provider return one in the callback?
  2. Is a ParcelFileDescriptor really the correct choice, always? What if the client just wants to pass a byte[] or simple InputStream?
  3. According to the answer to this SO question, we can't pass ParcelFileDescriptors in a service method..?


Intent API

  • @function@, arguments, return value
  • SELECT_KEYS, send name/email/keyid/etc., search then receive all matching private/public keyids or null (maybe SELECT_KEYS should use the standard ACTION_GET_CONTENT, should understand EXTRA_EMAIL)
  • FIND_KEYS_OR_PROMPT, send name/email/keyid/etc., search then receive matching keys (unless there is a single match, this will prompt with an Activity to choose the key, or try another search, then return whichever key was chosen by the user, should understand EXTRA_EMAIL. maybe it should use the standard ACTION_SEARCH or ACTION_PICK)
  • SIGN, send data/text/fileUri, receive signature blob
  • VEFIFY, send data/text/fileUri, receive verified status
  • ENCRYPT, send data/text/fileUri and optional signing key, receive encrypted, signed data/text/fileUri
  • DECRYPT, send encrypted data/text/fileUri, receive unencrypted data and verified status
  • QUERY_TRUST, send keyid, receive trust info (or maybe trust should only be sent back as part of verify, encrypt, etc, or maybe SELECT_KEYS or FIND_KEYS_OR_PROMPT)

Return Values

  • userID - a 3 element string array of Name, Email, Comment
  • keyID - a long of the last 64-bits of the key fingerprint (aka the last 16 hex chars)

General Ideas

  • ASCII or binary return is specified by an EXTRA flag
  • should use standard EXTRA_STREAM, EXTRA_TEXT, EXTRA_UID, etc.
  • respect EXTRA_LOCAL_ONLY to prevent searches on keyservers, etc.

AIDL vs Intents

In this comparison, we are assuming the same user experience. So for example, we expect the OpenPGP app to handle the passphrase prompting entirely, and the app using the crypto API is entirely ignore of it. The OpenPGP app should also provide the key selection and management.


+ Java-ish method calls and event handling
+ private data transfer
- AIDL talks to Service so natural Activity flow must be manually coded


+ preserves natural Activity flow when GPGapp handles pinentry, etc.
- world readable data transfer


Permissions Considerations

There might need to be a permission for controlling access to the keyring, along the lines of android.permission.READ_CONTACTS, otherwise we'll be leaking the contacts info, in effect. Apps would not need to use that permission for encrypting and signing if the exact key id is provided, but that would probably be of limited use.

  • READ_KEYS - read/search the public key information from the entire keyring

Key Data ContentProvider

Inspired by the APG ContentProvider, there is a ContentProvider included in gpgcli. You can either select a specific key using its 16 hex char KeyID (e.g. DEADBEEFDECAFBAD or 978234D82FFF34AD). This will return a single key or null:

  • content://info.guardianproject.gpg/keys/secret/key_id/
  • content://info.guardianproject.gpg/keys/public/key_id/

Or you can search by email address, which will return an array of all matches, or null if there are none:

  • content://info.guardianproject.gpg/keys/secret/email/
  • content://info.guardianproject.gpg/keys/public/email/

The data available for selecting with the projection map are:

  • key_id
  • name
  • email
  • fingerprint
  • comment

MIME Types

These are the MIME Types of the data returned from the Cursor:

  • PUBLIC_KEY_CONTENT_DIR_TYPE = "vnd.android.cursor.dir/vnd.info.guardianproject.gpg.public_key";
  • PUBLIC_KEY_CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.info.guardianproject.gpg.public_key";
  • SECRET_KEY_CONTENT_DIR_TYPE = "vnd.android.cursor.dir/vnd.info.guardianproject.gpg.secret_key";
  • SECRET_KEY_CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.info.guardianproject.gpg.secret_key";

Other methods for doing things:

  • import keys via file associations (.asc, .gpg, etc.)
  • import keys via GPG UI via QR, NFC, file import, etc.

AIDL Remote Service

  • expose gpg-agent key agent functionality for X.509, HTTPS client certs, etc.


APG Intent API [1]

  • org.thialfihar.android.apg.intent.SELECT_PUBLIC_KEYS
    • EXTRA_SELECTION, a long[] of keyIds to be pre-selected
    • returns Apg.EXTRA_SELECTION, a long[] of keyIds of the selected public keys
    • returns Apg.EXTRA_USER_IDS, a String[] of userIds, including name, email, and comment
  • org.thialfihar.android.apg.intent.SELECT_SECRET_KEY
    • returns Apg.EXTRA_KEY_ID, a long keyId of the selected secret key
    • returns Apg.EXTRA_USER_ID, a String userId, including name, email, and comment
  • org.thialfihar.android.apg.intent.ENCRYPT
  • org.thialfihar.android.apg.intent.ENCRYPT_FILE
  • org.thialfihar.android.apg.intent.ENCRYPT_AND_RETURN
  • org.thialfihar.android.apg.intent.GENERATE_SIGNATURE
    • Intent data contains the Uri to the file to be encrypted or signed (only used for ENCRYPT_FILE)
    • Apg.EXTRA_SEND_TO, a String containing the intended recipient(s)
    • Apg.EXTRA_SUBJECT, a String containing the subject of the included message
    • Apg.EXTRA_SIGNATURE_KEY_ID, the long keyId of the key used for signing
    • Apg.EXTRA_ENCRYPTION_KEY_IDS, the array of long keyIds of the keys to encrypt to
    • Apg.EXTRA_ASCII_ARMOUR, a boolean whether the data should be ASCII-armored or not
    • Apg.EXTRA_DATA, the byte[] data to be encrypted and/or signed
    • Apg.EXTRA_TEXT if Apg.EXTRA_DATA is null, then Apg.EXTRA_TEXT is the String data to be encrypted and/or signed
    • returns Apg.EXTRA_SIGNATURE_TEXT, ASCII data for the PGP signature
    • returns Apg.EXTRA_SIGNATURE_DATA, binary data for the PGP signature
    • returns Apg.EXTRA_ENCRYPTED_MESSAGE, ASCII data for the encrypted contents
    • returns Apg.EXTRA_ENCRYPTED_DATA, binary data for the encrypted contents
    • returns Apg.EXTRA_RESULT_URI, a Uri to the encrypted file
    • returns Apg.EXTRA_ERROR, the text of any errors that happened during the process
  • org.thialfihar.android.apg.intent.DECRYPT
  • org.thialfihar.android.apg.intent.DECRYPT_FILE
  • org.thialfihar.android.apg.intent.DECRYPT_AND_RETURN


  • content://org.thialfihar.android.apg.provider/key_rings/public/key_id/
  • content://org.thialfihar.android.apg.provider/key_rings/public/emails/
  • content://org.thialfihar.android.apg.provider/key_rings/secret/key_id/
  • content://org.thialfihar.android.apg.provider/key_rings/secret/emails/

Access to these ContentProviders is controlled by this permission:

  • org.thialfihar.android.apg.permission.READ_KEY_DETAILS

OpenPGP-Keychain Intent API [2] [3]

  • org.sufficientlysecure.keychain.action.CREATE_KEYRING
  • org.sufficientlysecure.keychain.action.DECRYPT
  • org.sufficientlysecure.keychain.action.DECRYPT_AND_RETURN
  • org.sufficientlysecure.keychain.action.DECRYPT_FILE
  • org.sufficientlysecure.keychain.action.DECRYPT_STREAM_AND_RETURN
  • org.sufficientlysecure.keychain.action.EDIT_KEYRING
  • org.sufficientlysecure.keychain.action.ENCRYPT
  • org.sufficientlysecure.keychain.action.ENCRYPT_AND_RETURN
  • org.sufficientlysecure.keychain.action.ENCRYPT_FILE
  • org.sufficientlysecure.keychain.action.ENCRYPT_STREAM_AND_RETURN
  • org.sufficientlysecure.keychain.action.GENERATE_SIGNATURE_AND_RETURN
  • org.sufficientlysecure.keychain.action.IMPORT
  • org.sufficientlysecure.keychain.action.IMPORT_FROM_NFC
  • org.sufficientlysecure.keychain.action.IMPORT_FROM_QR_CODE
  • org.sufficientlysecure.keychain.action.SELECT_PUBLIC_KEYRINGS
  • org.sufficientlysecure.keychain.action.SELECT_SECRET_KEYRING
  • org.sufficientlysecure.keychain.action.SHARE_KEYRING
  • org.sufficientlysecure.keychain.action.SHARE_KEYRING_WITH_NFC
  • org.sufficientlysecure.keychain.action.SHARE_KEYRING_WITH_QR_CODE


  1. https://github.com/thialfihar/apg/blob/master/AndroidManifest.xml
  2. https://github.com/dschuermann/openpgp-keychain/blob/master/OpenPGP-Keychain/AndroidManifest.xml
  3. https://github.com/dschuermann/openpgp-keychain#intents
  4. Re: [k-9-mail] Rejected new OpenPGP/Crypto Providers

Also available in: PDF HTML TXT