Page MenuHome GnuPG

No OneTemporary

diff --git a/misc/w3c/as2/OpenPGPoverActivityStreams.org b/misc/w3c/as2/OpenPGPoverActivityStreams.org
index a4e65c0..1419945 100644
--- a/misc/w3c/as2/OpenPGPoverActivityStreams.org
+++ b/misc/w3c/as2/OpenPGPoverActivityStreams.org
@@ -1,1412 +1,1410 @@
#+TITLE: Active Cryptography: OpenPGP over Activity Streams 2.0
#+STARTUP: showall
#+LATEX_COMPILER: xelatex
#+LATEX_CLASS: article
#+LATEX_CLASS_OPTIONS: [12pt]
#+LATEX_HEADER: \usepackage{xltxtra}
#+LATEX_HEADER: \usepackage[margin=1in]{geometry}
#+LATEX_HEADER: \setmainfont[Ligatures={Common}]{Times New Roman}
#+LATEX_HEADER: \author{Ben McGinnes <ben@gnupg.org>}
* Introduction
:PROPERTIES:
:CUSTOM_ID: intro
:END:
| Version: | 0.0.1-draft-002 |
| Author: | Ben McGinnes <ben@gnupg.org> |
| 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 [[https://www.ietf.org/][IETF]] or [[https://www.w3.org][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.
** Motivation
:PROPERTIES:
:CUSTOM_ID: intro-motive
:END:
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/.
** Contributors and Attribution
:PROPERTIES:
:CUSTOM_ID: intro-contrib
:END:
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, [[https://mastodon.social/@gargron][Eugen Rochko]] (Mastodon) and [[https://pleroma.soykaf.com/users/lain][Lain Soykaf]] (Pleroma),
have been very welcoming of a proposal which 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.
Input and encouragement from [[https://mastodon.social/@david_ross][David Ross]] (Mozilla), [[https://switter.at/@e][Eliza]] (Assembly
Four, Switter), [[https://mastodon.social/@ThisIsMissEm][Emelia]] (Switter) and [[https://switter.at/@vwerner][Vince Werner]] has been a boon.
*** Additional Thanks
:PROPERTIES:
:CUSTOM_ID: intro-thanks
:END:
Additionally 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.
Obviously thanks are also due to [[https://g10code.com][Werner Koch]] for not even batting an
eyelid at what could arguably have been a bit of a tangent from my
work on [[https://www.gnupg.org/][GnuPG]] and [[https://www.gnupg.org/software/gpgme/index.html][GPGME]] in order to devise precisely how to apply
OpenPGP to a whole new transport protocol.
** Approach
:PROPERTIES:
:CUSTOM_ID: intro-approach
:END:
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
of another within the federated networks in use.
This current proposal applies to the W3 Consortium's [[https://www.w3.org/TR/activitystreams-core/][Activity Streams
2.0]] and [[https://www.w3.org/TR/activitypub/][ActivityPub]] protocols; the latter being based upon the former.
** Cryptographic Implementation Choice
:PROPERTIES:
:CUSTOM_ID: intro-crypto-choice
:END:
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.
** Definitions
:PROPERTIES:
:CUSTOM_ID: intro-definitions
:END:
*IMPORTANT:* Everything in this proposal is optional. The definitions
listed here are within the context of the proposal itself.
This document uses the terms defined in [[https://tools.ietf.org/html/rfc4880][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 [[https://tools.ietf.org/html/rfc2119][RFC 2119]].
The following terms have these definitions:
- *AP* means ActivityPub.
- *AS* means Activity Streams.
- *AS2* means Activity Streams 2.0.
- *AC* means Active Cryptography or Activity Cryptography; the
working title for the protocol extension.
The document also draws on the same RFCs cited by both the Activity
Streams [[https://www.w3.org/TR/activitystreams-core/][core]] and [[https://www.w3.org/TR/activitystreams-vocabulary/][vocabulary]] documents, as well as the [[https://www.w3.org/TR/activitypub/][ActivityPub]]
protocol definition.
* Cryptographic Activities
:PROPERTIES:
:CUSTOM_ID: crypto
:END:
This section introduces the new objects, collections, activity types
and properties necessary to implement OpenPGP functions with Activity
Streams 2.0 and ActivityPub.
** Cryptographic protocol
:PROPERTIES:
:CUSTOM_ID: crypto-protocol
:END:
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.
#+begin_src javascript
{"cryptographic-protocol": "openpgp"}
#+end_src
Where the relevant JSON data is already clearly part of a
cryptographic object or activity this proprty *may* be defined as
=protocol=.
#+begin_src javascript
{"protocol": "openpgp"}
#+end_src
*** OpenPGP Protocol
:PROPERTIES:
:CUSTOM_ID: crypto-protocol-openpgp
:END:
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.
** MIME and file types
:PROPERTIES:
:CUSTOM_ID: crypto-mime-types
:END:
The media or content types utilised are adapted from the PGP/MIME
types defined in [[https://tools.ietf.org/html/rfc2015][RFC 2015]] and [[https://tools.ietf.org/html/rfc3156][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 Strams object
or activity type which will need to be processed upon decryption or
signature validation.
** Keys
:PROPERTIES:
:CUSTOM_ID: crypto-keys
:END:
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
[[https://datatracker.ietf.org/doc/draft-koch-openpgp-webkey-service/][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.[fn:1]
*** Public keys and Actors
:PROPERTIES:
:CUSTOM_ID: crypto-actor
:END:
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.
#+BEGIN_SRC javascript
{
"@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",
"preferredUsername": "kenzoishii",
"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",
"primaryKeyID": "3A1222F4BE79DB2AF069FADCF507B8E7E6EF68BF"
} ]
}
#+END_SRC
A slight variation demonstrating how multiple cryptographic
implementations could be utilised along with not specifying a primary
key ID may appear more like this:
#+BEGIN_SRC javascript
{
"@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",
"preferredUsername": "kenzoishii",
"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",
"primaryKeyID": "3A1222F4BE79DB2AF069FADCF507B8E7E6EF68BF"
},
{
"protocol": "openquantum",
"cryptoContext": "https://kenzoishii.example.com/openquantum.json",
"publicKeys": "https://kenzoishii.example.com/openquantumkeys.json"
} ]
}
#+END_SRC
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.[fn:2]
*** Cryptography Context
:PROPERTIES:
:CUSTOM_ID: crypto-context
:END:
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.
In the following examples I use a key created in the name of the same
fictional character I 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 GPG 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.[fn: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=.
#+BEGIN_SRC javascript
{
"@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
},
{
"id": "65DF3A3814D3BADC5E7D68F48D87C4418347F2BB",
"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",
"65DF3A3814D3BADC5E7D68F48D87C4418347F2BB" ]
},
{
"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" ]
}
]
}
#+END_SRC
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.
#+BEGIN_SRC javascript
{
"type": "email-encryption",
"policies": 2,
{
"policy": "Must",
"authorizedKeyIDs": [ "C2FA40FD7A2E6DDB7A4FDFCB1A7425A225C3EF1F",
"681CBF37BE8ED04CB20BD5D0483F423E32DD79A8" ]
},
{
"policy": "May",
"authorizedKeyIDs": [ "DB4724E6FA4286C92B4E55C4321E4E2373590E5D",
"9CBEF6B7E0DF72CF91009AA5C98BAA1862E4484D" ]
}
}
#+END_SRC
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.
*** Serving Public Keys Directly
:PROPERTIES:
:CUSTOM_ID: crypto-keyserv-direct
:END:
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).
#+BEGIN_SRC javascript
{
"@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"
} ]
},
{
"keyblockASCII": "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nmDMEXCaxhxYJKwYBBAHaRw8BAQdAMSVyt57XCqdve8pvgC4BDkj+BYq6xKlsdMua\nIYiKl+y0SURhbmdlciBNb3VzZSAoU29jaWFsOiBAZG1Abm90LnNlY3JldC5leGFt\ncGxlLmNvbSkgPGRtQHNlY3JldC5leGFtcGxlLm5ldD6ImQQTFgoAQRYhBML6QP16\nLm3bek/fyxp0JaIlw+8fBQJcJrGHAhsDDAsKDQkMCAsHBAEDAgcVCgkICwMCBRYC\nAwEAAh4BAheAAAoJEBp0JaIlw+8fR0wBAOLXF7eYegcI4w21BsceE669hpwHBl6b\n5G5/dJQObkSkAP0Vdx7+CyIMJwAqDesQtnUKrxLp1TEsR3FWXmPO5fAvB7g4BFwm\nsgsSCisGAQQBl1UBBQEBB0AyMCTt0r9Gvth5whz4ED8znHo9KqR0AVjftlG86xe0\nLgMBCAeIeAQYFgoAIBYhBML6QP16Lm3bek/fyxp0JaIlw+8fBQJcJrILAhsMAAoJ\nEBp0JaIlw+8fRoQBANjV8BrkS0EzIcQpG8xgsfQESYGsj/B59h9QdL7eS6q5AP9V\nV/6JC2wBwzL38B4accNW/lNPDAMfS3LgqvQnAfgeDbgzBFwmsjUWCSsGAQQB2kcP\nAQEHQLiOaYOPCSEIc1SLk1dM/XbTr5+bIl1DPduKbl2aWsaTiO8EGBYKACAWIQTC\n+kD9ei5t23pP38sadCWiJcPvHwUCXCayNQIbAgCBCRAadCWiJcPvH3YgBBkWCgAd\nFiEEZd86OBTTutxefWj0jYfEQYNH8rsFAlwmsjUACgkQjYfEQYNH8rtiewEAp+wJ\nvMc3Qq8hv372nNVdzE7TySjvJpy05DmtPcbAZ4UBAMrpR6MadshSM7NZvlBAyhPl\n7YmogPQ2N28Ja3kX8l4GIMYBAKRzQtRVH+NlOA0tvPO2wcBYXJhSQF0k/S8EnzZu\nYMmwAP4h+e4ytRt5yUupjFRM+S4OY7rMRTAY0eeu8rJwBeLrAw==\n=0Eei\n-----END PGP PUBLIC KEY BLOCK-----\n"
}
]
}
]
}
#+END_SRC
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.
*** Serving Public Keys Via WebFinger
:PROPERTIES:
:CUSTOM_ID: crypto-keyserv-webfinger
:END:
An alternative approach to using the =openpgpkeys.json= file defined
in the previous section is to instead direct key retrieval traffic to
the existing [[https://webfinger.net/][WebFinger]] service utilised by ActivityPub and [[https://datatracker.ietf.org/doc/rfc7033/][defined]] in
[[https://tools.ietf.org/html/rfc7033][RFC 7033]].
In that case this part of the specification:
#+BEGIN_SRC html
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.
#+END_SRC
Would need to be modified slightly to something more akin to this:
#+BEGIN_SRC html
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.
#+END_SRC
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 [[http://microformats.org/wiki/existing-rel-values][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.
*** Serving Public Keys Via Web Key Directory
:PROPERTIES:
:CUSTOM_ID: crypto-keyserv-wkd
:END:
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:
#+BEGIN_SRC html
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.
#+END_SRC
Would need to be modified slightly to something more like this:
#+BEGIN_SRC html
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.
#+END_SRC
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.
** Signatures
:PROPERTIES:
:CUSTOM_ID: crypto-signing
:END:
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=).
#+BEGIN_SRC javascript
{
"@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",
"signingKeyID": "65DF3A3814D3BADC5E7D68F48D87C4418347F2BB",
"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",
"signingKeyID": "65DF3A3814D3BADC5E7D68F48D87C4418347F2BB",
"pubkeyAlgorithm": "EDDSA",
"hashAlgorithm": "SHA256",
"timestamp": 1546554637
}
}
}
#+END_SRC
** Encryption
:PROPERTIES:
:CUSTOM_ID: crypto-encryption
:END:
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.
*** Encrypted Private Messages
:PROPERTIES:
:CUSTOM_ID: crypto-encryption-privmsg
:END:
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 AP
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.[fn:4]
#+BEGIN_SRC javascript
{
"@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",
"signingKeyID": "65DF3A3814D3BADC5E7D68F48D87C4418347F2BB",
"recipientKeyIDs": [ "681CBF37BE8ED04CB20BD5D0483F423E32DD79A8" ],
"cipher": "TWOFISH",
"encryptionAlgorithm": "EDDSA",
"hash": "SHA512",
"signingAlgorithm": "ECDH",
"timestamp": 1546206334
}
}
#+END_SRC
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.[fn:5]
#+BEGIN_SRC javascript
{
"@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
}
}
#+END_SRC
This second method of encrypting ActivityPub or Activity Streams data
would enable providing signed information without revealing publicly
which key actually signed the that data except to the intended
recipient(s).
*** Encrypted Public Messages
:PROPERTIES:
:CUSTOM_ID: crypto-encryption-pubmsg
:END:
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 GPG. 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.
** Authentication
:PROPERTIES:
:CUSTOM_ID: crypto-auth
:END:
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.
*** Authentication With Signing Keys
:PROPERTIES:
:CUSTOM_ID: crypto-auth-sign
:END:
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.
*** Authentication With Encryption Keys
:PROPERTIES:
:CUSTOM_ID: crypto-auth-encrypt
:END:
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.
*** Authentication With Authentication Keys
:PROPERTIES:
:CUSTOM_ID: crypto-auth-squared
:END:
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.
* Additional Technical Notes
:PROPERTIES:
:CUSTOM_ID: tech
:END:
** Data size limitations
:PROPERTIES:
:CUSTOM_ID: tech-size
:END:
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.
** Metadata and Forward Secrecy
:PROPERTIES:
:CUSTOM_ID: tech-metadata
:END:
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.
* References
:PROPERTIES:
:CUSTOM_ID: refs
:END:
TBA.
** Normative References
:PROPERTIES:
:CUSTOM_ID: refs-norm
:END:
** Non-Normative References
:PROPERTIES:
:CUSTOM_ID: refs-non-norm
:END:
** Informative References
:PROPERTIES:
:CUSTOM_ID: refs-inform
:END:
* Copyright and Licensing
:PROPERTIES:
:CUSTOM_ID: copyright-and-license
:END:
** Copyright
:PROPERTIES:
:CUSTOM_ID: copyright
:END:
-Copyright © Benjamin D. McGinnes, 2018, 2019.
-
-Copyright © The GnuPG Hackers, 2018.
+Copyright © Benjamin D. McGinnes, 2018-2019.
** Licensing
:PROPERTIES:
:CUSTOM_ID: license
:END:
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
[fn: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 [[https://datatracker.ietf.org/doc/draft-koch-openpgp-webkey-service/?include_text=1][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.
[fn: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.
[fn: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.
[fn:4] The session key for the encrypted message in this example is:
10:E877FC8B0CB0B69F15A3397E3A9CD00419A3F47795469A973A841BE0388F8BA0
[fn:5] The session key for the second encrypted message example is:
10:9A4CAAFD053E45B9895C9C882AC24D51C4810E1F6F7834DDFB29DE3EB5ABB083

File Metadata

Mime Type
text/x-diff
Expires
Tue, Jan 20, 1:30 AM (23 h, 20 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
42/45/293f02e00308c5f318cd80594e07

Event Timeline