# Active Cryptography: OpenPGP over Activity Streams 2.0

## 1 Introduction

 Version: 0.0.1-draft-003 Author: Ben McGinnes Author GPG Key: DB4724E6FA4286C92B4E55C4321E4E2373590E5D Language: Australian English, British English Language code: en-AU, en-GB, en

This document provides a specification for using OpenPGP cryptography with the Activity Streams 2.0 transport method. It was devised with particular attention towards providing end-user encryption and verification on federated ActivityPub based instances (e.g. Mastodon and Pleroma).

This proposal is not an official part of the W3C's protocols, but is offered as an optional means of addressing some of the security issues identified as lacking or missing in those protocols. As such it is offered under the same terms as any IETF or W3 Consortium standards or proposals as free for any use. Example code, however, may be released under the same terms as the GnuPG Project or some other license as relevant. Example code will be provided separately from this document.

### 1.1 Motivation

The current Presidential Administration in the USA has diverged considerably from the policies of his predecessors with certain legislative and regulatory changes which are set to enable a far greater implementation of authoritarian policies and agendas, as well as enabling those policies to be enforced beyond the territory of the United States of America. This sets a dangerous precedent with regards to the freedom of all people around the globe to communicate freely and privately, particularly when they may become subject to matters which are entirely legal where they live, but which the United States legislates against.

The legislative and regulatory changes in the United States of most concern to the rest of the world at the present time are: the removal of Net Neutrality provisions by the FCC, the CLOUD Act and the SESTA-FOSTA Act. The latter being the attempt to enforce American laws regarding adult content, primarily of a sexual nature, globally. They also remove the “safe harbour” provisions which previously permitted hosting providers to ignore what their customers were doing, in turn making those providers tools of the state who must police the actions of end users and actively censor them.

Since the first draft of this protocol extension was written, the types of wholesale legislative threats to privacy have been exacerbated by Australia's efforts to “double down” on the arbitrary powers overreach in passing the Telecommunications and Other Legislation Amendment (Assistance and Access) Bill 2018.

Obviously the creators of the ActivityStreams 2.0 and ActivityPub protocols have already contributed significantly with those protocols. Those people are: Christopher Lemmer Webber, Jessica Tallon, Evan Prodromou, James M. Snell, Amy Guy and Erin Shepherd.

Additionally the lead developers of the two most popular ActivityPub implementations, Eugen Rochko (Mastodon) and Lain Soykaf (Pleroma), have been very welcoming of a proposal seeking to supply a comprehensive solution to a number of the frequently requested features in their two projects.

Feedback on the first draft has been very valuable; especially from Wiktor Kwapisiewicz. Particularly with regards to the discussions around the WebFinger protocol and related matters.

Feedback on the second draft has also been valuable, particularly the input from Peter Gutmann and Stephen Farrell via the IETF OpenPGP Working Group and mailing list.

Input and encouragement from David Ross (Mozilla), Eliza (Assembly Four, Switter), Emelia (Unobvious Technology) and Vince Werner has been a boon.

The following people have provided non-technical support of various types during the course of designing and writing this protocol extension: Chamara “Mara” Caldera, Trindy Oakley and Michael “Elgy” Eldritch.

Thanks are also due to Werner Koch for not even batting an eyelid at what could arguably have been a bit of a tangent from my work on GnuPG and GPGME in early 2018 in order to devise precisely how to apply OpenPGP to a whole new transport protocol.

### 1.3 Approach

Over the course of the last decade or a little more, a great deal of communication online has shifted towards using social media networks. Email is still good for many things, but it is not good for everything and various types of social networks fill that need.

Modifying the underlying protocols or specifications of proprietary networks, such as Twitter and Facebook, is generally not possible. It is also clear that these networks will act against some or even all of their own user base in order to achieve the goals of those running the companies in question.

In the case of Facebook that is in the form of surveillance of large populations and subsequent manipulation of them. In the case of Twitter it is in the form of banning those who vehemently oppose Nazism or who discuss or promote adult entertainment of various types, primarily pornography and sex work, regardless of the jurisdiction to which the end user is actually subject.

Open standards and protocols, however, can be leveraged freely and as necessary. This is what Phil Zimmermann did back in 1991 when he released the first version of Pretty Good Privacy for use with Email, USENET and, very likely, FidoNet (or FidoNet style) BBS networks. The same approach may be utilised now with social networks which themselves provide an open specification and where that specification provides the means for extending or advancing itself.

There is clearly grounds for social network users to have access to the tools to send and receive end-to-end encrypted private messages via their social network accounts and identities. Likewise there is a need for end users to be able to prove, should they wish to do so, that a message was not modified in transit; either by their own server or by another server within the federated networks in use.

This current proposal applies to the W3 Consortium's Activity Streams 2.0 and ActivityPub protocols; the latter being based upon the former.

### 1.4 Cryptographic Implementation Choice

The cryptographic choice with regards to the GnuPG Project was limited to the two engines which GnuPG currently supports: OpenPGP and S/MIME. Since the intended outcome of this proposal is to provide end users with a means of securing content or preventing content manipulation, the OpenPGP model was selected.

It would, however, be possible to switch the security focus to the server level in order to utilise some future advancement. This may necessitate or simply favour utilising a different cryptographic implementation or method. As a consequence this proposal is designed to more easily enable swapping one method for another.

Note that this is separate from and in addition to the use of a PEM key by ActivityPub servers for each of their users. In those cases the private key is generated by the ActivityPub server when the user account is created. As a consequence it is inherently flawed from a user security perspective. It does, however, move the complexity out of the user level and back to the server level. Whereas this proposal does not.

### 1.5 Definitions

This document uses the terms defined in RFC 4880 and in the same way.

The key words: "must", "must not", "required", "shall", "shall not", "should", "should not", "recommended", "may", and "optional" to be interpreted as defined in RFC 2119.

The document also draws on the same RFCs cited by both the Activity Streams core and vocabulary documents, as well as the ActivityPub protocol definition.

## 2 Cryptographic Activities

This section introduces the new objects, collections, activity types and properties necessary to implement OpenPGP functions with Activity Streams 2.0 and ActivityPub.

### 2.1 Cryptographic protocol

In order to handle any situations in which servers and/or clients may implement multiple cryptographic protocols, a property must be set for any cryptographic object or activity.

{"cryptographic-protocol": "openpgp"}


Where the relevant JSON data is already clearly part of a cryptographic object or activity this proprty may be defined as protocol.

{"protocol": "openpgp"}


#### 2.1.1 OpenPGP Protocol

When integrating OpenPGP with Activities or Objects, consideration must be given to both the versions in use throughout the network and setting sensible minimum requirements so as not to adversely affect the rest of the network.

For this reason the current standards defined in RFC 4880 must be implemented, while the recommendations of RFC 4880bis should be available. Though a number of older versions of the standard may be available with any given implementation, any older standard for which existing recommendations state not to use them due to security related issues then those older standards must not be used.

### 2.2 MIME and file types

The media or content types utilised are adapted from the PGP/MIME types defined in RFC 2015 and RFC 3156. Specifically this covers the pgp-keys, application/pgp-encrypted and application/pgp-signed MIME types.

In addition to these an implementation may utilise application/pgp-encrypted+activitystreams and may utilise application/pgp-signed+activitystreams to indicate an Activity Stream object (i.e. an application/activity+json object) is either entirely affected by the cryptographic function or the object is OpenPGP data which contains an ActivityPub or Activity Streams object or activity type which will need to be processed upon decryption or signature validation.

### 2.3 Keys

Unlike the PEM key included with ActivityPub instances, OpenPGP keys are always intended to be generated by the end user(s) controlling a given actor's account and not controlled or accessed by the server, even when that server is controlled by a single user.

There are also valid reasons or use cases for assigning multiple keys to an actor or using the same key with multiple actors. This is particularly the case if proof of OpenPGP key control was adopted as an alternative means of providing authentication between a client and server, in addition to OAuth methods.

Though there is already a well established network of public keyservers, the SKS keyserver pool, and from GnuPG 2.1 there is the OpenPGP Web Key Directory (WKD); there are also valid reasons for not using these methods of providing access to a public key used with activities.

Likewise, there is a need for serving key information with actor information and referencing it with objects and activities where necessary. This would effectively turn an ActivityPub instance into a limited public keyserver for the keys assigned to actors under its purview, though it may not maintain or serve copies of those keys containing full web-of-trust signatures, particularly if there are size constraints or bandwidth limitations.1

#### 2.3.1 Public keys and Actors

In order to enable access to cryptographic information controlled at the user level we need to add an optional property to actors; one where the absence of it equates to a value of null.

Since it is theoretically possible for multiple cryptographic protocols to be in use, in addition to the Linked Data and HTTP Signatures referenced in the ActivityPub specification, this optional property must contain an array of JSON data listing the protocol or cryptographic-protocol, the cryptoContext for a URI of a collection containing more relevant data, the publicKeys for an additional URI just for checking public key data and may contain a primaryKeyID referencing the preferred key ID used with the actor.

Here is an example using the same actor example in the ActivityPub specification. Note that the key ID or fingerprint used here does not exist on the keyservers and is really just a SHA1 sum of the actor's name.

{
"@context": ["https://www.w3.org/ns/activitystreams",
{"@language": "ja"}],
"type": "Person",
"id": "https://kenzoishii.example.com/",
"following": "https://kenzoishii.example.com/following.json",
"followers": "https://kenzoishii.example.com/followers.json",
"liked": "https://kenzoishii.example.com/liked.json",
"inbox": "https://kenzoishii.example.com/inbox.json",
"outbox": "https://kenzoishii.example.com/feed.json",
"name": "石井健蔵",
"summary": "この方はただの例です",
"icon": [
"https://kenzoishii.example.com/image/165987aklre4"
],
"cryptoProtocols": [ {
"protocol": "openpgp",
"cryptoContext": "https://kenzoishii.example.com/openpgp.json",
"publicKeys": "https://kenzoishii.example.com/openpgpkeys.json",
}   ]
}


A slight variation demonstrating how multiple cryptographic implementations could be utilised along with not specifying a primary key ID may appear more like this:

{
"@context": ["https://www.w3.org/ns/activitystreams",
{"@language": "ja"}],
"type": "Person",
"id": "https://kenzoishii.example.com/",
"following": "https://kenzoishii.example.com/following.json",
"followers": "https://kenzoishii.example.com/followers.json",
"liked": "https://kenzoishii.example.com/liked.json",
"inbox": "https://kenzoishii.example.com/inbox.json",
"outbox": "https://kenzoishii.example.com/feed.json",
"name": "石井健蔵",
"summary": "この方はただの例です",
"icon": [
"https://kenzoishii.example.com/image/165987aklre4"
],
"cryptoProtocols": [ {
"protocol": "openpgp",
"cryptoContext": "https://kenzoishii.example.com/openpgp.json",
"publicKeys": "https://kenzoishii.example.com/openpgpkeys.json",
},
{
"protocol": "openquantum",
"cryptoContext": "https://kenzoishii.example.com/openquantum.json",
"publicKeys": "https://kenzoishii.example.com/openquantumkeys.json"
}   ]
}


In this example of the near-ish future OpenPGP usage is complemented by advances in Quantum Cryptography and the development of the FOSS Quantum Privacy Guard (QPG) with the standard being developed right along side it.2

#### 2.3.2 Cryptography Context

The cryptography contexts referenced from the actor define all the ways in which any key or keys are used in relation to actions and objects by or for that actor. First by identifying the keys and subkeys and then by defining which type of objects they're used in relation to. As well as whether the account is configured to always use them, as may be the case with signatures or not.

The Cryptography Context is a collection of nested collections and objects dealing with each key or subkey type and the ways they're used in regards to activities or other objects.

The following examples use a key created in the name of the same fictional character used in the GPGME Python Bindings HOWTO in conjunction with an imaginary ActivityPub instance on an example domain with a thematically related subdomain, not.secret.example.com.

The keys item must contain a keyinfo item for each public key associated with the actor account.

The keyinfo item must contain keyIDs data for the primary key and all enabled subkeys of the key.

The keyinfo item must contain a type property which indicates both the key's cryptographic protocol and version number of that protocol. Most current OpenPGP keys are version 4 keys.

The keyinfo item may contain keyIDs data for revoked or disabled keys previously used with the actor or revoked subkeys of an active key. Where this data is included the keyID item must contain an enabled property with a boolean value of true or false. Additionally a revoked property may be included, also with a boolean value of true or false.

Where the enabled and revoked properties are not included, the default values are assumed to be that enabled is true and revoked is false.

The keyinfo item may contain userIDs data for some or all of the userIDs listed on the key itself.

The keyinfo item may contain a keyfiles property with direct links to either or both of the GnuPG or PGP binary key formats or the ASCII armored key file format.

The keyinfo item must contain the publicKeys property pointing to a JSON encoded URL containing at least the minimised version of the public key. Alternatively the publicKeys property may point to an array in which the first item is the JSON encoded URL containing key material. The subsequent items in such an array may point to either or both of URLs or URIs for accessing the keys via WebFinger or via the Web Key Directory.

A keyID item must contain an id property of the full key ID which is the hexadecimal key fingerprint without spaces. The id property must not be either the short or long key ID formats.

A keyID item must contain a type property with a value indicating whether the key is the primary (certification) key or a subkey.

A keyID item may contain a fingerprint property with the full key ID in a human readable format. This is the fingerprint format which most OpenPGP users will be familiar with and normally presents the fingerprint with spaces between hexadecimal groupings of four characters each.

A keyID item must contain an algorithm property with a value indicating which asymmetric cryptographic algorithm or whether the key utilised elliptic curve cryptography (as ECC).

If the algorithm property has a value of ECC then the keyID item must also include a curve property with a value of the specific elliptic curve in use. If the algorithm property contains a value specifying an asymmetric cryptographic algorithm then the curve property may be omitted. If the curve property is not omitted, but the algorithm property contains an asymmetric algorithm then the curve property must be null.

A keyID item must contain a size property with an integer value of the bit size of the key or subkey.

A keyID item must contain properties for each of the four capabilities a key or subkey may possess: certification, encryption, signing and authentication. The values for each property are boolean strings; true or false.

A keyID item must contain a timestamp property with an integer value of the number of seconds since the epoch since the key or subkey was last modified. This will usually be the timestamp of the key's creation, but may indicate some other modification such as changing an expiration date or revoking the key or subkey.

The remaining items address the three basic functions for which OpenPGP keys can be used with Activity Streams: signing, encryption and authentication. In addition to those three functions and policies, additional use case policies may be appended: refreshing a key from the keyservers, encrypting email notifications regarding activities to the relevant email address for the actor account.3

Each of these items must include a policy property which stipulates whether or not that function is available and the consistency of that use. Possible policy values are must, may and never. Recommended default values are may unless the relevant key or subkey type is unavailable, in which case the correct value is never.

If the policy value for an item is either must or may then the authorizedKeyIDs property must include an array with all full key IDs of the primary key and relevant subkeys to perform that task. If the policy value is never then the authorizedKeyIDs may be null.

{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://not.secret.example.com/openpgp.json",
"summary": "OpenPGP use and keys with this stream",
"type": "openpgpCollection",
"cryptographic-protocol": "openpgp",
"totalItems": 6,
"items": [
{
"type": "openpgpKeys",
"totalItems": 1,
"items": [
{
"id": "keyinfo",
"type": "openpgpKeyV4",
"timestamp": 1546043571,
"lastUpdated": 1546039861,
"keyIDs": [
{
"id": "C2FA40FD7A2E6DDB7A4FDFCB1A7425A225C3EF1F",
"type": "primary",
"fingerprint": "C2FA 40FD 7A2E 6DDB 7A4F  DFCB 1A74 25A2 25C3 EF1F",
"algorithm": "ECC",
"curve": "ed25519",
"size": 256,
"certification": true,
"signing": true,
"encryption": false,
"authentication": false,
"timestamp": 1546039687
},
{
"id": "681CBF37BE8ED04CB20BD5D0483F423E32DD79A8",
"type": "subkey",
"fingerprint": "681C BF37 BE8E D04C B20B  D5D0 483F 423E 32DD 79A8",
"algorithm": "ECC",
"curve": "cv25519",
"size": 256,
"certification": false,
"signing": false,
"encryption": true,
"authentication": false,
"timestamp": 1546039819
},
{
"type": "subkey",
"fingerprint": "65DF 3A38 14D3 BADC 5E7D  68F4 8D87 C441 8347 F2BB",
"algorithm": "ECC",
"curve": "ed25519",
"size": 256,
"certification": false,
"signing": true,
"encryption": false,
"authentication": false,
"timestamp": 1546039861
}
],
"userIDs": [
{
"name": "Danger Mouse",
"comment": "Social: @dm@not.secret.example.com",
"email": "dm@secret.example.net"
}
],
"keyfiles": [
{
"url": "https://agents.secret.example.net/dm-key.asc",
"Content-Type", "application/pgp-signature",
"summary": "ASCII armored openpgp keyfile, full key"
},
{
"url": "https://agents.secret.example.net/dm-key.gpg",
"Content-Type", "application/pgp-keys",
"summary": "Binary openpgp keyfile, full key"
},
{
"url": "https://agents.secret.example.net/dm-key-clean.asc",
"Content-Type", "application/pgp-signature",
"summary": "ASCII armored openpgp keyfile, clean key"
},
{
"url": "https://agents.secret.example.net/dm-key-clean.gpg",
"Content-Type", "application/pgp-keys",
"summary": "Binary openpgp keyfile, clean key"
},
{
"url": "https://agents.secret.example.net/dm-key-min.asc",
"Content-Type", "application/pgp-signature",
"summary": "ASCII armored openpgp keyfile, minimised key"
},
{
"url": "https://agents.secret.example.net/dm-key-min.gpg",
"Content-Type", "application/pgp-keys",
"summary": "Binary openpgp keyfile, minimised key"
}   ],
"publicKeys": "https://not.secret.example.com/openpgpkeys.json"
}
]
},
{
"type": "content-signing",
"policy": "May",
"authorizedKeyIDs": [ "C2FA40FD7A2E6DDB7A4FDFCB1A7425A225C3EF1F",
},
{
"type": "encryption",
"policy": "May",
"authorizedKeyIDs": [ "C2FA40FD7A2E6DDB7A4FDFCB1A7425A225C3EF1F",
"681CBF37BE8ED04CB20BD5D0483F423E32DD79A8" ]
},
{
"type": "authentication",
"policy": "Never",
"authorizedKeyIDs": null
},
{
"type": "refresh"
"policy": "May",
"authorizedKeyIDs": [ "C2FA40FD7A2E6DDB7A4FDFCB1A7425A225C3EF1F" ]
},
{
"type": "email-encryption",
"policy": "Must",
"authorizedKeyIDs": [ "C2FA40FD7A2E6DDB7A4FDFCB1A7425A225C3EF1F",
"681CBF37BE8ED04CB20BD5D0483F423E32DD79A8" ]
}
]
}


There are numerous ways in which OpenPGP may be leveraged by a server to provide authentication mechanisms for an actor utilising either signatures, encrypted tokens to be decrypted and used like OAuth or even using the authentication subkey type in a manner similar to TLS or SSH. For this example these possibilities are disregarded in order to demonstrate how a policy may be set to not use one possible function.

A server might also use the public keys in a more traditional manner for OpenPGP if end users receive email notifications of activites. In that circumstance the server could, if the public key had a subkey with the encryption capability and the relevant matching policy, encrypt those emailed notifications.

Also note that while default and recommended key generation stipulates that OpenPGP primary (certification) keys should not have the encryption capability, it is still advisable to include that primary key ID as authorized for any function granted to any of its subkeys. The reason being that not every OpenPGP implementation correctly interprets the relationship between the primary key and those subkeys (e.g. some of the JavaScript implementations). By explicitly including the primary as authorized, even for those tasks for which it does not have the capability we avoid unnecessary false error reports with certain OpenPGP implementations.

If an actor has multiple keys assigned to it, it should be permitted to extend the policy section to provide for different policies for each key.

For instance it may be preferred to have one main key which is always refreshed from the keyservers, but a backup key which is only updated manually by an end user. The following example demonstrates how a single type can be expanded to cover multiple policies. Where there is only one policy, as in the larger example above it is assumed that the policies property has a value of 1 and may be omitted.

{
"type": "email-encryption",
"policies": 2,
{
"policy": "Must",
"authorizedKeyIDs": [ "C2FA40FD7A2E6DDB7A4FDFCB1A7425A225C3EF1F",
"681CBF37BE8ED04CB20BD5D0483F423E32DD79A8" ]
},
{
"policy": "May",
"authorizedKeyIDs": [ "DB4724E6FA4286C92B4E55C4321E4E2373590E5D",
"9CBEF6B7E0DF72CF91009AA5C98BAA1862E4484D" ]
}
}


Note that the second key listed here is that of the principal author of this proposal and thus secret key material for that key will never be provided; unlike the example key for “The Greatest Secret Agent in the World: Danger Mouse.”

While the secret key and paassphrase for these examples will be published in supplemental files. This document will also contain copies of the session keys used with encrypted examples; in case this document is distributed separately from the supplemental files.

#### 2.3.3 Serving Public Keys Directly

The openpgpKeys.json file contains a lot of matching data to the main context file by necessity since both need to include the key ID data and both will usually include some user ID data. Both of which being data about the public key which is available from the public key itself. The main differences, however, are that the context file provides the information on the circumstances under which the public key either can, should or must be used; but does not include a copy of the public key itself. While the other file only has data about the key itself and a copy of at least the minimised key (or keys if there are multiple keys assigned to an actor or stream).

{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://not.secret.example.com/openpgpkeys.json",
"stream": "https://not.secret.example.com/",
"summary": "OpenPGP public keys for this stream.",
"type": "openpgpKeys",
"cryptographic-protocol": "openpgp",
"totalItems": 1,
"items": [
{
"type": "openpgpKey",
"keyVersion": 4,
"totalItems": 2,
"lastUpdated": 1524951377,
"items": [
{
"type": "openpgpKeyData",
"timestamp": 1514332912,
"keyIDs": [
{
"id": "C2FA40FD7A2E6DDB7A4FDFCB1A7425A225C3EF1F",
"type": "primary",
"fingerprint": "DB47 24E6 FA42 86C9 2B4E  55C4 321E 4E23 7359 0E5D",
"cipher": "RSA",
"curve": null,
"size": 4096,
"certification": true,
"signing": true,
"encryption": false,
"authentication": false,
"timestamp": 1343480251
},
{
"id": "B7F0FE759387430DD0C58BDB7FF2D37135C7553C",
"type": "subkey",
"fingerprint": "B7F0 FE75 9387 430D D0C5  8BDB 7FF2 D371 35C7 553C",
"cipher": "RSA",
"curve": null,
"size": 3072,
"certification": false,
"signing": true,
"encryption": false,
"authentication": false,
"timestamp": 1343480419
},
{
"id": "681CBF37BE8ED04CB20BD5D0483F423E32DD79A8",
"type": "subkey",
"fingerprint": "681C BF37 BE8E D04C B20B  D5D0 483F 423E 32DD 79A8",
"cipher": "ECC",
"curve": "cv25519",
"size": 256,
"certification": false,
"signing": false,
"encryption": true,
"authentication": false,
"timestamp": 1343480559
}   ],
"userIDs": [
{
"name": "Danger Mouse",
"comment": "Social: @dm@not.secret.example.com",
"email": "dm@secret.example.net"
}   ],
"keyfiles": [
{
"url": "https://agents.secret.example.net/dm-key.asc",
"Content-Type", "application/pgp-signature",
"summary": "ASCII armored openpgp keyfile, full key"
},
{
"url": "https://agents.secret.example.net/dm-key.gpg",
"Content-Type", "application/pgp-keys",
"summary": "Binary openpgp keyfile, full key"
},
{
"url": "https://agents.secret.example.net/dm-key-clean.asc",
"Content-Type", "application/pgp-signature",
"summary": "ASCII armored openpgp keyfile, clean key"
},
{
"url": "https://agents.secret.example.net/dm-key-clean.gpg",
"Content-Type", "application/pgp-keys",
"summary": "Binary openpgp keyfile, clean key"
},
{
"url": "https://agents.secret.example.net/dm-key-min.asc",
"Content-Type", "application/pgp-signature",
"summary": "ASCII armored openpgp keyfile, minimised key"
},
{
"url": "https://agents.secret.example.net/dm-key-min.gpg",
"Content-Type", "application/pgp-keys",
"summary": "Binary openpgp keyfile, minimised key"
}   ]
},
{
}
]
}
]
}


Note the main timestamp is the date the key itself was last modified and will usually match the timestamp of the last subkey to be added or the timestamp of the most recent self-certification of a key. Whereas the lastUpdated property notes the last time the copy of the public key was updated on the server serving that data. Such an update should normally be the result of a client uploading the key to the actor account, but may be the result of the server refreshing key data from the SKS keyserver network or the Web Key Directory service.

#### 2.3.4 Serving Public Keys Via WebFinger

An alternative approach to using the openpgpkeys.json file defined in the previous section is to instead direct key retrieval traffic to the existing WebFinger service utilised by ActivityPub and defined in RFC 7033.

In that case this part of the specification:

The <code>keyinfo</code> item <b>must</b> contain
the <code>publicKeys</code> property pointing to a JSON encoded URL
containing at least the minimised version of the public key.
Alternatively the <code>publicKeys</code> property <b>may</b> point to
an array in which the first item is the JSON encoded URL containing
key material.  The subsequent items in such an array <b>may</b> point
to either or both of URLs or URIs for accessing the keys via WebFinger
or via the Web Key Directory.


Would need to be modified slightly to something more akin to this:

The <code>keyinfo</code> item <b>must</b> contain
the <code>publicKeys</code> property pointing to a JSON encoded
WebFinger URL containing links to the relevant OpenPGP key or keys
associated with the account.  Alternatively the <code>publicKeys</code>
property <b>may</b> point to an array in which the first item is the
WebFinger URL.  The subsequent items in such an array <b>may</b> point
to either or both of URLs or URIs for accessing the keys directly or
via the Web Key Directory.


As this transport protocol is ultimately driven by HTTP/S traffic, it does not matter so much if the distribution of key data occurs in a binary format (e.g. .pgp, .gpg and .sig files). As a consequence there is less need to serve Radix64 encoded versions of those binary formats (e.g. .asc files) for key distribution.

As the WebFinger specification already utilises existing rel values and as there is already a pgpkey value specifically serving OpenPGP key data, leveraging this here ought to be fairly straight forward.

All the WebFinger service requires is a means of identifying each key and possibly subkeys, along with accessing those keys. Both of which are simple enough to deliver with a URL for a key file and the fingerprint(s) of the keys and subkeys.

Utilising the WebFinger specification here provides the additional advantage that other, otherwise unrelated, services or software may benefit from accessing these OpenPGP keys and subsequently enhancing privacy features.

#### 2.3.5 Serving Public Keys Via Web Key Directory

An alternative to both serving key data directly and linking to keys via the WebFinger service is to incorporate the Web Key Directory service into the specification.

In that case this part of the specification:

The <code>keyinfo</code> item <b>must</b> contain
the <code>publicKeys</code> property pointing to a JSON encoded URL
containing at least the minimised version of the public key.
Alternatively the <code>publicKeys</code> property <b>may</b> point to
an array in which the first item is the JSON encoded URL containing
key material.  The subsequent items in such an array <b>may</b> point
to either or both of URLs or URIs for accessing the keys via WebFinger
or via the Web Key Directory.


Would need to be modified slightly to something more like this:

The <code>keyinfo</code> item <b>must</b> contain
the <code>publicKeys</code> property pointing to a Well Known URI
matching the Web Key Directory format and which links to a key or keys
associated with the account.  Alternatively
the <code>publicKeys</code> property <b>may</b> point to an array in
which the first item is the Web Key Directory URI.  The subsequent
items in such an array <b>may</b> point to either or both of URLs or
URIs for accessing the keys directly, or the URL of a JSON encoded
WebFinger file.


Though the Web Key Directory service may very well prove to be the ultimate replacement for the SKS keyserver network, it is not yet a finalised specification. As a consequence it is currently recommended as an optional supplementary key discovery method.

### 2.4 Signatures

Signing activities as a means of providing assurance that they genuinely originate with the client and have not been modified in transit will probably be one of the most common uses of these functions.

There are, however, issues with the possibility that a server may render the content differently to the author's system or sanitize the content in an unexpected manner. Also the author might use another content format (e.g. Markdown) which is intended to be rendered into HTML by the server.

The solution to this problem is a new object type, the Signed Note.

A Signed Note must contain a source property containing the original data transmitted, even if the mediaType is text/html as the server might still render it differently.

A Signed Note must contain a signatures property which must specify the protocol and must include a detached signature file for the source data.

The scope property specifies which source properties were signed, usually this should only be the subject and content or just the content.

The signatures property may include a signature for the expected rendered output. As with the source signature, the scope property specifies which rendered output properties were signed.

Since the order will matter with regards to the scope a signedData property must be included with with each signature.

This is followed by the detached signature in ASCII armored (radix64) format and some additional data pertaining to the key or subkey used to sign the data as signingKeyID, the algorithms used as the pubkeyAlgorithm and the digital hashAlgorithm, and the timestamp of the signature.

It should be possible for anyone with the Signed Note object to take the signedData and the detached signature, save them both to files and then manually verify them with OpenPGP compliant software (e.g. gpg or gpg.exe).

 {
"@context": ["https://www.w3.org/ns/activitystreams",
{ "@language": "en" } ],
"type": "Signed Note",
"id": "https://not.secret.example.com/agents/dm/posted/thing",
"subject": "GnuPG rocks",
"content": "<p>So, what <em>should</em> be signed, what was written or what was rendered?</p>",
"source": {
encryption       "subject": "GnuPG rocks",
"content": "So, what *should* be signed, what was written or what was rendered?",
"mediaType": "text/markdown"
},
"signatures": {
"cryptographic-protocol": "openpgp",
{
"scope": { "source": ["subject", "content"] },
"signedData": "GnuPG rocksSo, what *should* be signed, what was written or what was rendered?",
"signature": "-----BEGIN PGP SIGNATURE-----\n\niHUEABYIAB0WIQRl3zo4FNO63F59aPSNh8RBg0fyuwUCXCl4HgAKCRCNh8RBg0fy\nu6fZAQDqCKlaQRmIBdZgoHmMHDBU6KO/vw6iW5q/PYKChBM5dwEAv5UPYNY33mKh\n/CFvwLnZ0j+pVGgsuEidp5J1zk5JgA0=\n=xHh0\n-----END PGP SIGNATURE-----\n",
"pubkeyAlgorithm": "EDDSA",
"hashAlgorithm": "SHA256",
"timestamp": 1546221598
},
{
"scope": { "expectedRender": ["subject", "content"] },
"signedData": "GnuPG rocks<p>So, what <em>should</em> be signed, what was written or what was rendered?</p>",
"signature": "-----BEGIN PGP SIGNATURE-----\n\niHUEABYIAB0WIQRl3zo4FNO63F59aPSNh8RBg0fyuwUCXC6NDQAKCRCNh8RBg0fy\nuyH6AQDOD7QfcYfPx6xpHKRsv6SzDijNXOS3vq1qaIYRkY/a/AEAyvn6uwRSJ1L5\nKEEKIvWhFsJoFJf0RCIraxoNlyWnvQ0=\n=Qea9\n-----END PGP SIGNATURE-----\n",
"pubkeyAlgorithm": "EDDSA",
"hashAlgorithm": "SHA256",
"timestamp": 1546554637
}
}
}


### 2.5 Encryption

Encrypting activity content or content and subjects will meet the needs of many feature requests on numerous instances. There are, however, some variations of methods which may be worth examining, along with issues pertaining to availability of metadata and what options, if any, exist for providing any measure of forward secrecy.

There are multiple issues to be addressed when dealing with encrypted activities, objects or portions of either. Some of these issues relate to whether the ciphertext contains additional embedded JSON data to be interpreted or rendered by the recipient upon decryption, while others relate more to the addressing or total number of recipients or how to treat data when not all the intended recipients have a public ky available.

Still, one problem it readily solves is in providing end-to-end encrypted messages between two single actors.

#### 2.5.1 Encrypted Private Messages

There are essentially two methods of sending an encrypted private message: one in which the encrypted content is just the message being sent, which may contain content or markup intended to be parsed or rendered at the recipient's end; and the other being when the encrypted content contains embedded JSON data matching the Activity Streams 2.0 specification and possibly the ActivityPub specification to be interpreted by software at the recipient's end.

Regardless of which it is, the sending of it requires another new ActivityPub object, the Encrypted Note.

The Encrypted Note must contain an encrypted property.

The encrypted property may contain a subject property.

The encrypted property must contain a content property in which the encrypted data is inserted in radix64 ASCII armored format.

The encrypted property should contain a mediaType property with a value of application/pgp-encrypted or application/pgp-encrypted+activitystreams.

The encrypted property may contain a signingKeyID property containing the id of the key used to sign the encrypted content, if any. Alternatively the signingKeyID property may be an array of multiple keys or subkeys if more than one key was used to sign the data.

The encrypted property may contain a recipientKeyIDs property containing an array of the key IDs to which the encrypted data has been encrypted. If the recipients have been hidden then the recipientKeyIDs property may be excluded or explicitly set to either null or hidden.

The encrypted property must contain a cipher property with a value of the symmetric cipher used to encrypt the content data.

The encrypted property must contain an encryptedAlgorithm property containing a value of the asymmetric encryption or elliptic curve algorithms of the recipientKeyIDs. If there multiple algorithms then this data must be included in an array. This requirement remains even if the recipientKeyIDs property is null or hidden.

The encrypted property may contain a hash property with a value of the hash digest algorithm used to sign the content data, if any.

The encrypted property may contain a signingAlgorithm property with a value of the digital signature algorithm of the key used to sign the content data. If multiple keys were used to sign the data and those keys used different signing algorithms then this may be an array containing each algorithm.

The encrypted property should contain a timestamp, except where enough of the data regarding the encrypted content does not include an actual timestamp.

The following example is about as simple as it gets. The content is encrypted and signed, in this case simply containing a small Markdown text file.4

{
"@context": ["https://www.w3.org/ns/activitystreams",
{ "@language": "en-GB" } ],
"type": "Encrypted Note",
"id": "https://not.secret.example.com/agents/dm/posted/encrypted-thing",
"to": "https://not.secret.example.com/agents/dm/inbox",
"subject": "Secret Message",
"cryptographic-protocol": "openpgp",
"encrypted": {
"subject": "Secret Message",
"content": "-----BEGIN PGP MESSAGE-----\n\nhF4DSD9CPjLdeagSAQdAqdWMriKCydTELA/6Rn0V6v0iCx2tTz4qFzvl0iutjWMw\nl8OJnLw+5xy0aUEr17PujJCnrcI8hUVxarZHZSOILLjLLVtWjI5LB3YuSepP0Iav\n0sEsARd02MNCp32Eyj8X1vFEsf8pvWxPe0ojrZ9afwjWF6ZIYpOHoiYPZc/za3Gf\nJGeyDyZ+FJMDkP5TnJsME9K6vqF+fZnwP4m2K1HoPOMH1pCqH4jI54IMy06c4ZUx\nLh7zPrOmfcdFMSBQ4jVxw/hDaeLUaPw7J1bE21jd9dTuK8Nn6q1zteI0hmw9d6t7\nQYHw7CwNI3dsrU5y1YiHs7PoEZO2W1qqoykvOFeNzkx8RmkbNUPy1LULFiDED+Y+\nDrFYPH9Xpfaqp4SqV+kE/zL7T/edftL/ZCDmRNwzoCUcvUkg6MMTfmiTZglZ5O/k\nzFn74RTmrGjXDnQv7iikP+urs41bJvOzBKYRGfRFQ08GRgZR6HJS19NrdLiB8M9I\njHQZG2fpDpNKNByx3gfXwSCXEhpurYh7m4ssK80KFXdWKRpECTN0qXj5B9LFcok8\nD1GSdX0WvKIarvtyKDxaaruAS6gVD59QODELpDnK6sKHuP4mkX34D9zKpV/yJqMb\nMNiNlNnBvQF/9cp+wyVpA5BW5WlkqWKOgev+V7z0DuPkBHrsilAZOCFplaiVU//m\nmErPTT6FeHSP9U5iPXTKq6vkDnDnUkNEHLIR8LgUvVvQLvGHZXWqxQpMXOcMmiyd\n7rlVFL4CRXYaLlhfYH2c\n=OIPC\n-----END PGP MESSAGE-----\n",
"mediaType": "application/pgp-encrypted",
"recipientKeyIDs": [ "681CBF37BE8ED04CB20BD5D0483F423E32DD79A8" ],
"cipher": "TWOFISH",
"encryptionAlgorithm": "EDDSA",
"hash": "SHA512",
"signingAlgorithm": "ECDH",
"timestamp": 1546206334
}
}


A more complete and possibly more effective method, however, is in the following example. Like the preceding one, the Encrypted Note object contains OpenPGP encrypted data in the content property. A summary is optional and may indicate that the content is encrypted if the Encrypted Note is being posted publicly (see next section).

The encrypted data, however is an entire ActivityPub object including source format which may be rendered by a recipient's software and which may include a Signed Note as described above.5

{
"@context": ["https://www.w3.org/ns/activitystreams",
{ "@language": "en-GB" } ],
"type": "Encrypted Note",
"id": "https://not.secret.example.com/agents/dm/posted/encrypted-thing",
"to": "https://not.secret.example.com/agents/dm/inbox",
"cryptographic-protocol": "openpgp",
"encrypted": {
"content": "-----BEGIN PGP MESSAGE-----\n\nhF4DSD9CPjLdeagSAQdAaR4vYSrOenqGK3sM0V3rGLJtRCcPb3NTpf1/yuNQLy0w\nHRirczb52+WarwgcbJXpnslVOyFNJnHnJ8fi6G++w98ZNycf7UrOPTbu/EoINPom\n0ukB1CC4aelHjhE90SosjP/wosrn7YzZxm3QUDu/kR2y6um1v/gIghpBlTHWovK9\nXJMd7c0JGSxtEqHoJAUlTXsRZn7CYMGHTJ1W+In4uc1rZb5aHNv+iHzKLBCylfsO\n0VsHJ6MET74FO40iWYjlfReoPP08n9x8Q2J/6RuuCDLbYKPX3W2VD2Gv4tASRCW8\nXJuv9knMCnbV4yUD0EAn2ZmJSH9LbBSiJUT5yBEmUqgme09EiuPxP8/uRmCf03+n\nab5S1yjR5xKUfGHhSs2MJZqKLP0xKmClgZIA3PYnPyHLtlzASn6EhZ9PZ0d2DFrA\n5uIKdTpuly0esfCWjCjMH+S7W85Zk3ne7Qk4ZOsuejj8Z+HHAjKMVBAlAZ0gFU4G\nJGvM9U0Hs84vFLcNShdY+KixTL1yxMT4nom9ch9vKZszT9KBFfTxFZP9JAeO2Xam\n1hbKCL7uo+xKLdGCD38X3FTOtQNAFohpffzb5aQqLRb5+GSO720Dkhn6/RwjCfpB\n4+PJiz8jnlVzdMPOb2QumfjF4BOAGK3L9L0wIdszelwP7WrIFrUHh0BwwHOM7F0e\nPHTFj5nxE+BZ7KO3EHcqR0oTokUjQY/oNm2W3rZr8ZtZCsWNMr5BDGN4yMBxOrQq\nnwe+kzys9bAR+u683DzPE6K7e8uyy4Fs+irZQO1AKC7Z1QA=\n=Kxtt\n-----END PGP MESSAGE-----\n",
"mediaType": "application/pgp-encrypted+activitystreams",
"signingKeyID": null,
"recipientKeyIDs": [ "C2FA40FD7A2E6DDB7A4FDFCB1A7425A225C3EF1F",
"681CBF37BE8ED04CB20BD5D0483F423E32DD79A8" ],
"cipher": "TWOFISH",
"encryptionAlgorithm": "ECDH",
"timestamp": null
}
}


This second method of encrypting ActivityPub or Activity Streams data would enable providing signed information without revealing publicly which key actually signed that data except to the intended recipient(s).

#### 2.5.2 Encrypted Public Messages

It would be possible to post an encrypted message publicly, but in which the recipients' key IDs were hidden using any of the hidden-recipient (-R), hidden-encrypt-to or throw-keyids options available when using GnuPG. For such messages the second of the two options in the previous section is likely to be the most useful, but it could be used with the first.

This would enable the use of a public stream of objects and activities as a “dead drop” as a means of providing anonymous or pseudonymous communication with any other party and without requiring a means by which that party might be directly identified by others.

### 2.6 Authentication

There are multiple methods by which OpenPGP keys could be employed to provide authentication services between a client and server, In particular as an alternative to using passwords or two-factor authentication when used in conjunction with OAuth tokens for sessions.

These methods have the additional advantage of providing a means by which a remote server could confirm the identity of a user of another server without requiring the transfer of any sensitive or secure data between the two servers. For the most part this advantage stems from confirming a status is signed by the same key as used on that remote server, but it could also be used to directly authenticate in order to access any private messages of a local user intended for that user and in the local user's ActivityPub outbox.

#### 2.6.1 Authentication With Signing Keys

Utilising signing keys or subkeys would enable a means of authentication with a server without requiring an ongoing session between the client and the server. This could be used to facilitate a secure update or activity even across an insecure connection without compromising the security of the account itself as the server would be able to determine the authenticity of the activity and any relevant objects by verifying the signature alone.

#### 2.6.2 Authentication With Encryption Keys

Utilising encryption subkeys would enable a means of establishing a secure session's token exchange which does not rely on the transmission of a password, two-factor authentication or other API key, as is most commonly utilised. Instead the server simply issues the token for that session in an encrypted format. Since only an authorised user or client with control of the OpenPGP key could decrypt the data and obtain the token.

#### 2.6.3 Authentication With Authentication Keys

OpenPGP authentication keys or subkeys are intended for use with protocols like SSH or other remote access. In spite of the name they may be less useful in this use case. Nevertheless, it would be possible to configure a server to accept connections utilising an authentication key or subkey to establish an authorised connection from the client to the server.

### 3.1 Data size limitations

Since the conversion of encrypted binary data in the OpenPGP format to radix64 encoded ASCII text generally adds to the size of the output data, determined according to both the size of the original input data and the size of the keys to which that data is encrypted, the maximum message size should not be arbitrarily limited in the same way that many ActivityPub objects are limited. The common limitation of five hundred characters per status to be found with many Mastodon servers, for instance, would severely hamper the ability to usefully employ any of these options.

### 3.2 Metadata and Forward Secrecy

The nature of ActivityPub and Activity Streams 2.0 data is such that there is an inherent leakage of metadata with each object and activity posted to a stream. As a consequence there are certain limitations on what can or should be concealed. There are, however, methods of mitigating that leakage. A good example being the second message encryption method described above.

Forward secrecy is a little more difficult with a messaging format like this, even where it appears to be a stream to an end user. This is due to each object being separate packages in that stream rather than the data being transmitted as a single encrypted session originating with the author and ending with the recipient in real time. Even in those circumstances in which the overall communication (e.g. a conversation) does occur in real time or near real time.

Nevertheless, between using OpenPGP keys with pseudonymous identifiers linked to the ActivityPub stream end points and minimising the amount of data revealed by encrypted content, there are points which can facilitate this process. In many respects this could be done in a manner not too dissimilar to the use of anonymous remailers and posts to the old alt.anonymous.messages USENET news group.

## 4 References

TBA.

### 5.2 Licensing

This file is free software; as a special exception the author gives unlimited permission to copy and/or distribute it, with or without modifications, as long as this notice is preserved.

This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY, to the extent permitted by law; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

## Footnotes:

1

As a point of comparison, the author's current public key in ASCII armored format with all the web-of-trust signatures included is approximately 100KB in size, whereas the same key exported in its most minimal and concise form is approximately 13KB. Most keys will be smaller than that (the author's key is a 4Kb RSA certification and signing primary key with a 3Kb RSA signing subkey, a 4Kb El-Gamal encryption subkey and a 3Kb DSA2 signing subkey).

Note that a future draft of this protocol extension may shift the key distribution method to utilise the proposed OpenPGP Web Key Directory protocol; which would meet all the requirements of this protocol along with very fine tuned user ID control with key distribution. At the current time, however, adoption of the Web Key Directory service is limited and its protocol design is not finalised.

2

As the example suggests, the example is heavily based on the current state of the GnuPG Project. As this is a fictional thing which may become real in the future, it's necessary to stress that such a project must be both free and permissive in its licensing.

To choose only free (e.g. GPL and/or Affero GPL only) means to sacrifice other people's security/lives for one's own political beliefs, while to choose only permissive (e.g. BSD and/or Apache and/or proprietary only) means to sacrifice other people's security/lives for profit.

If any reading this have ever wondered why the GnuPG Project hasn't moved away from its dual licensing under the GPLv2+ (free) and the LGPLv2.1+ (permissive), this is why.

3

Since an actor contact email address may be different from any of the user IDs listed on the public key, servers should be configured with their own means of matching key IDs to email addresses. In GnuPG this is what the group option is used for and various MUAs have their own solutions (e.g. Enigmail's Per-Recipient Record and Mutt's crypt-hook). It is also recommended that servers automatically encrypt such notifications with the trust model set to always, otherwise the server will need to be configured with its own key which signs or locally signs all the keys uploaded by clients.

4

The session key for the encrypted message in this example is: 10:E877FC8B0CB0B69F15A3397E3A9CD00419A3F47795469A973A841BE0388F8BA0

5

The session key for the second encrypted message example is: 10:9A4CAAFD053E45B9895C9C882AC24D51C4810E1F6F7834DDFB29DE3EB5ABB083

Created: 2019-09-17 Tue 01:32

Validate