mirror of
https://codeberg.org/openpgp/notes.git
synced 2025-09-09 19:29:41 +02:00
initial public release
This commit is contained in:
parent
ecffecc2ce
commit
e913b907d7
129 changed files with 28453 additions and 0 deletions
381
book/source/adv/certificates.md
Normal file
381
book/source/adv/certificates.md
Normal file
|
@ -0,0 +1,381 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# Advanced material: Certificates
|
||||
|
||||
## When are certificates valid?
|
||||
|
||||
Certificates are composites of components that are linked together using [signatures](../signing_components).
|
||||
|
||||
A certificate can be valid or invalid as a whole. However, even when a certificate is valid, individual components (subkeys or identities) of it can be invalid.
|
||||
|
||||
In this section, we discuss the validity of certificates and their components. This discussion is closely related to [signature validity](/verification), and builds on that concept.
|
||||
|
||||
The validity of the signatures that link a certificate is a necessary precondition. Two concepts are particularly central to the validity of certificates and components:
|
||||
|
||||
- Expiration
|
||||
- Revocation
|
||||
|
||||
### Expiration
|
||||
|
||||
Certificates and components can "expire," which renders them invalid. Each component of a certificate can have an expiration time, or be unlimited in its temporal validity.
|
||||
|
||||
The OpenPGP software of a sender will refuse to encrypt email using an expired certificate, or using an encryption component key that is expired. The sender's software rejects encryption to the key, essentially as a courtesy to the certificate owner, respecting the preferences expressed in their certificate metadata.
|
||||
|
||||
The expiration mechanism in OpenPGP is complemented by a mechanism to extend/renew expiration time.
|
||||
|
||||
Using the expiration mechanism is useful for two reasons:
|
||||
|
||||
- Expiration of a certificate means that it cannot be used anymore. This forces users of that certificate (or their OpenPGP software) to poll for updates for it. For example, from a keyserver.
|
||||
- It is a passive way for certificates to "time out," e.g., if their owner loses control over them, or isn't able to broadcast a revocation, for any reason.
|
||||
|
||||
Component keys use *Key Expiration Time* subpackets for expressing the expiration time. Identity components rely on the [*signature expiration time*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#signature-expiration-subpacket) subpacket of their binding signature. If a binding signature expires, the binding becomes invalid, and the component is considered expired.
|
||||
|
||||
### Revocation
|
||||
|
||||
Since OpenPGP certificates act as ["append only" data structures](append-only), existing components or signatures cannot simply be "removed." Instead, they can be marked as invalid by issuing revocation signatures. These additional revocation signatures are added to the certificate.
|
||||
|
||||
Each component, such as User ID and a subkey, can be revoked without affecting the rest of the certificate.
|
||||
|
||||
The *primary User ID* is an exception: when it is revoked, the entire certificate is considered invalid.
|
||||
|
||||
Revoking the primary key with a [*Key revocation signature*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-key-revocation-signature-ty) (type ID `0x20`) also marks the entire certificate, including all of its components, as invalid and unusable.
|
||||
|
||||
### Semantics of Revocations
|
||||
|
||||
In contrast to expiration, revocation is typically final and not withdrawn[^undo-revocations].
|
||||
|
||||
[^undo-revocations]: While some revocations can be reverted, undoing revocations is an uncommon workflow. Unlike expirations, which are commonly undone by extending the expiration time.
|
||||
|
||||
A revocation indicates that the component should not be used. Revocation signatures over components use a [*Reason for Revocation*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#reason-for-revocation) subpacket to specify further details about the reason why the component or certification was revoked. The OpenPGP format specifies a set of distinct [values for *Reasons for Revocation*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#table-10), and additionally provides space for a human-readable free text field for comments about the revocation.
|
||||
|
||||
Some libraries, such as Sequoia PGP, expose these distinct reasons for users, enabling nuanced machine-readable statements by the revoker. Other implementations focus mainly on the distinction between "hard" and "soft" revocations.
|
||||
|
||||
Of the defined revocation types, *Key is superseded*, *Key is retired* and *User ID is no longer valid* are considered "soft" revocations. Any other reason (including a missing *reason for revocation* subpacket) means that the revocation is "hard."
|
||||
|
||||
The distinction between hard and soft revocations plays a role when evaluating the validity of a component or signature at a specified reference time: Hard revocations have unbounded [temporal validity](temporal-validity), they are in effect even before their creation time and therefore invalidate the revoked component or signature at all points in time.
|
||||
|
||||
By contrast, a soft revocation leaves the revoked component or signature valid before the creation time of the revocation signature. A soft revocation can technically be overridden, for example, with a newer binding signature (the new binding signature and its metadata then shadow the revocation and re-connect and re-validate the component).
|
||||
|
||||
Hard revocations address the following problem: If a private key was compromised, then the attacker can issue signatures using that key. This means, the attacker could issue a signature dated before the revocation, impersonating the owner of the key. A recipient of that signature would mistakenly consider this signature valid if the issuing key has been soft revoked. This is a problem.
|
||||
To counteract this problem, it is reasonable to clearly mark compromised keys as suspect at any point in time. That's what hard revocations do.
|
||||
|
||||
On the other hand, if the subkey was merely retired using a soft revocation, and the certificate holder moved to a different subkey, then the signatures in the past, made by the retired key, are still valid.
|
||||
|
||||
(append-only)=
|
||||
## Certificates are effectively append-only data structures
|
||||
|
||||
OpenPGP certificates act as *append-only data structures*, in practice. Packets that are associated with a certificate cannot be "recalled", once they were published. Third parties (such as other users, or keyservers) may keep and/or distribute copies of those packets.
|
||||
|
||||
While it is not possible to *remove* elements, once they were publicly associated with an OpenPGP certificate, it is possible to invalidate them by adding new metadata to the certificate. This new metadata could set an *expiration time* on a component, or explicitly *revoke* that component. In both cases, no packets are removed from the certificate.
|
||||
|
||||
Invalidation resembles removal of a component in a semantical sense. The component is not a valid element of the certificate anymore, at least starting from some point in time. Implementations that handle the certificate may omit the invalid component in their representation.
|
||||
|
||||
We have to distinguish the "packet level" information about a certificate from an application-level view of that certificate. The two may differ.
|
||||
|
||||
### Reasoning about append-only properties in a distributed system
|
||||
|
||||
OpenPGP is a decentral and distributed system. Users can obtain and transmit certificate information about their own, as well as other users', certificates using a broad range of mechanisms. These mechanisms include keyservers, manual handling, [Web Key Directory](https://datatracker.ietf.org/doc/draft-koch-openpgp-webkey-service/) (WKD) and [Autocrypt](https://en.wikipedia.org/wiki/Autocrypt).
|
||||
|
||||
Different users' OpenPGP software may obtain different views of a particular certificate, over time. Individual users' OpenPGP instances have to reconcile and store a combined version of the possibly disparate elements they obtain from different sources.
|
||||
|
||||
In practice, this means that various OpenPGP users may have differing views of any given certificate. For various reasons, not all users will be in possession of a fully up-to date and complete version of a certificate.
|
||||
|
||||
There are various potential problems associated with this fact: Users may not be aware that a component has been invalidated by the certificate holder. Revocations may not have been propagated to some third party. So for example, they may not be aware that the certificate holder has rotated their encryption subkey to a new one, and doesn't want to receive messages encrypted to the previous encryption subkey.
|
||||
|
||||
One mechanism that addresses a part of this issue is *expiration*: By setting their certificates to expire after an appropriate interval, certificate holders can force their communication partners to refresh their certificate, e.g. from a keyserver[^mgorny].
|
||||
|
||||
[^mgorny]: See, for example, [here](https://blogs.gentoo.org/mgorny/2018/08/13/openpgp-key-expiration-is-not-a-security-measure/): "Expiration times really serve two purposes: naturally eliminating unused keys, and enforcing periodical checks on the primary key."
|
||||
|
||||
Good practices, like setting appropriate expiration times, can mitigate the complexity of the inherently distributed nature of certificates.
|
||||
|
||||
However, such mitigations by definition cannot address all possible cases of outdated certificate information in a decentralized, asynchronous system such as OpenPGP. So a defensive approach is generally appropriate when reasoning about the view of certificates that different actors have.
|
||||
|
||||
When thinking about edge cases, it's useful to "assume the worst." For example:
|
||||
|
||||
- Recipients may not obtain updates to a certificate in a timely manner (this could happen for various reasons, including, but not limited to, interference by malicious actors).
|
||||
- Data associated with a certificate may compound, and a certificate can become too large for convenient handling, even in the course of normal operations (for example, a certificate may receive very many legitimate third-party certifications). If such a problem arises, then by definition, the certificate holder cannot address it: remember that the certificate holder cannot "recall" existing packets.
|
||||
|
||||
### Differing "views" of a certificate exist
|
||||
|
||||
Another way to think about this discussion is that different OpenPGP users may have a different view of any certificate. There is a notional "canonical" version of the certificate, but we cannot assume that every user has exactly this copy. Besides propagation of elements that the certificate holder has linked to a certificate, third-party certifications are by design a distributed mechanism. A third-party certification is issued by a third party, and may or may not be distributed widely by them, or by the certificate holder. Not distributing third-party certifications widely is a workflow that may be entirely appropriate for some use cases[^tpc-privacy].
|
||||
|
||||
[^tpc-privacy]: The two parties to a certification (the issuer and the target of the certification) may prefer not to publish their mutual association. Also see {ref}`social-graph-metadata-leak`.
|
||||
|
||||
As a general tendency, it is desirable for OpenPGP users to have the most complete possible view of all certificates that they interact with.
|
||||
|
||||
However, there are contexts in which it is preferable to only use a subset of the available elements of a certificate. We discuss this in the section {ref}`minimization`.
|
||||
|
||||
(certificate-merging)=
|
||||
## Merging
|
||||
|
||||
As described above, OpenPGP certificates are effectively [append-only](append-only) data structures. As part of the practical realization of this fact, OpenPGP software needs to *merge* different copies of a certificate.
|
||||
|
||||
For example, Bob's OpenPGP software may have a local copy of Alice's certificate, and obtain a different version of Alice's certificate from a keyserver. The goal of the implementation is to add new information about Alice's certificate, if any, to the local copy. Alice may have added a new identity, replaced a subkey with a new subkey, or revoked some components of her certificate. Or, Alice may have revoked her certificate, signaling that she doesn't want communication partners to use that certificate anymore. All of these updates could be crucial for Bob to be aware of.
|
||||
|
||||
Merging two versions of a certificate involves making decisions about which packets should be kept. The versions of the certificate will typically contain some packets that are identical. No duplicates of the exact same packet should be stored in the merged version of the certificate. Additionally, if the newly obtained copy contains packets that are in fact entirely unrelated to the certificate, those should not be retained (a third party may have included unrelated packets, either by mistake, or with malicious intent).
|
||||
|
||||
### Handling unauthenticated information
|
||||
|
||||
For information that *is* related to the certificate, but not bound to it by a self-signature, there is no generally correct approach. The receiving implementation must revolve these cases, possibly in a context-specific manner. Such cases include:
|
||||
|
||||
- Third-party certifications. These could be valuable information, where a third party attests that the association of an identity to a certificate is valid. On the other hand, they could also be a type of spam.
|
||||
- Subpackets in the unhashed area of a signature packet. Again, these could contain information that is useful to the recipient. However, the data could also be either useless, or even misleading/harmful.
|
||||
|
||||
(minimization)=
|
||||
## Certificate minimization
|
||||
|
||||
Certificate minimization is the practice of presenting a partial view of a certificate by filtering out some of its components.
|
||||
|
||||
Filtering out some elements of a certificate can serve various purposes:
|
||||
|
||||
- Omitting unnecessary components for specific use-cases. For example, email clients need encryption, signing and certification component keys, but not authentication subkeys, which are used, e.g., for SSH connections.
|
||||
- Omitting third-party certifications if they are not required for a use-case. ["Certificate flooding,"](https://dkg.fifthhorseman.net/blog/openpgp-certificate-flooding.html) for example, can lead to consumer software rejecting a certificate entirely. Filtering out third-party User ID certifications on import can mitigate this.
|
||||
- Sometimes, a certificate organically grows so big that the user software [has problems handling it](https://www.reddit.com/r/GnuPG/comments/bp23p4/my_key_is_too_large/).
|
||||
|
||||
### Elements that can be omitted as part of a minimization process
|
||||
|
||||
There are different types of elements that can be omitted during minimization:
|
||||
|
||||
- Subkeys (along with signatures on those subkeys)
|
||||
- Identity components (along with both their self-signatures and third-party signatures)
|
||||
- Signatures, by themselves:
|
||||
- Self-signatures that have been superseded by newer self-signatures for the same purpose
|
||||
- Third-party certifications
|
||||
|
||||
### Minimization in applications
|
||||
|
||||
#### Hagrid, which runs keys.openpgp.org
|
||||
|
||||
The [hagrid keyserver software](https://gitlab.com/keys.openpgp.org/hagrid) doesn't publish the identity components in certificates by default. This is a central aspect of the [privacy policy](https://keys.openpgp.org/about/privacy) of the service. Certificates can be uploaded to the service by third parties, which is useful. However, identifying information is only distributed by the service on an explicit opt-in basis.
|
||||
|
||||
Separately, third-party certifications are currently filtered out by the service, to avoid flooding attacks.
|
||||
|
||||
#### GnuPG
|
||||
|
||||
GnuPG offers two explicit methods for certificate minimization, described [in the GnuPG manual](https://www.gnupg.org/documentation/manuals/gnupg-devel/OpenPGP-Key-Management.html) as:
|
||||
|
||||
*clean*
|
||||
: *Compact (by removing all signatures except the selfsig) any user ID that is no longer usable (e.g. revoked, or expired). Then, remove any signatures that are not usable by the trust calculations. Specifically, this removes any signature that does not validate, any signature that is superseded by a later signature, revoked signatures, and signatures issued by keys that are not present on the keyring.*
|
||||
|
||||
*minimize*
|
||||
: *Make the key as small as possible. This removes all signatures from each user ID except for the most recent self-signature.*
|
||||
|
||||
`clean` removes third-party signatures by certificates that are not present in current keyring, as well as other stale data. `minimize` removes superseded signatures that are not needed at the point when the command is executed.
|
||||
|
||||
Independently, GnuPG by default [strips some signatures on key import](https://dev.gnupg.org/T4607#127792)[^gpg-default-strip]. However, a number of Linux distributions change this default behavior, and continue to import signatures without minimization by default. e.g. [Debian](https://dev.gnupg.org/T4628#128513) and Arch Linux: stripping third-party certifications on import, by default, is problematic for users who want to leverage authentication based on the [Web of Trust mechanism](wot).
|
||||
|
||||
[^gpg-default-strip]: GnuPG's changes in the default handling of third-party certifications on imports were prompted by the 2019 [keyserver flooding](keyserver-flooding) event.
|
||||
|
||||
### Limitations that can result from stripping historical self-signatures
|
||||
|
||||
Some implementations, such as Sequoia, prefer to rely on the full historical set of self-signatures to construct a view of the certificate over time. This way, signatures can be verified at different reference times. In this model, removing superseded self-signatures can cause problems with the validation of historical signature.
|
||||
|
||||
An example for the tension between minimization and nuanced verification of the [temporal validity](temporal-validity) of signatures can be seen in the case of rpm-sequoia. See [this discussion](https://github.com/rpm-software-management/rpm-sequoia/issues/50#issuecomment-1689642607) for details:
|
||||
|
||||
Initially, when checking the validity of a data signature for a software package, `rpm-sequoia` used the signature's creation time as the reference time. However, the availability of historical self-signatures in certificates is limited. So sometimes only a more recent self-signature for the primary key is available, and there is no evidence that the primary key was valid at the reference time.
|
||||
|
||||
To deal with this reality, the rpm-sequoia implementation was adjusted to accept data signatures that predate the validity of the current primary key self-signature[^primary-self-sig].
|
||||
|
||||
[^primary-self-sig]: Which in OpenPGP version 4 is often a primary User ID binding signature.
|
||||
|
||||
### Autocrypt
|
||||
|
||||
The [Autocrypt](https://autocrypt.org/) project describes itself as:
|
||||
|
||||
> [..] a set of guidelines for developers to achieve convenient end-to-end-encryption of e-mails. It specifies how e-mail programs negotiate encryption capabilities using regular e-mails.
|
||||
|
||||
The Autocrypt Level 1 specification defines a specific [minimal format for OpenPGP certificates](https://autocrypt.org/level1.html#openpgp-based-key-data) that are distributed by the autocrypt mechanism.
|
||||
|
||||
One goal of the Autocrypt mechanism is to distribute certificates widely. To this end, Autocrypt sends certificates in mail headers, where smaller size is greatly preferable.
|
||||
|
||||
Basic encrypted email functionality requires only a small subset of the recipient's certificate, so small certificate size is feasible.
|
||||
|
||||
### Minimization for email
|
||||
|
||||
Note that minimization of certificates isn't generally "right" or "wrong." The benefit or harm depends on the context.
|
||||
|
||||
For example, we might consider minimizing a certificate for distribution via WKD, with the use-case of email in mind.
|
||||
|
||||
Many certificates can be significantly pruned if the only goal of distributing them is to enable encryption and signature verification. For such cases, many components can be dropped, including invalid subkeys and their binding signatures, authentication subkeys (which are irrelevant to email), shadowed self-signatures, and third-party certifications. With many real-world certificates, the space savings of such a minimization are significant[^space-example].
|
||||
|
||||
Such minimization might be appropriate and convenient to enable encrypted communication with a ProtonMail client, which automatically fetches OpenPGP certificates via WKD while composing a message. The ProtonMail use case requires only component keys, not third-party certifications, and it doesn't require historical component keys or self-signatures.
|
||||
|
||||
However, in a different context, the same certificate might be fetched to verify the authenticity of a signature. In that case, third-party certifications may be crucial for the client. Stripping them could prevent the client from performing Web of Trust calculations and validating the authenticity of the certificate.
|
||||
|
||||
[^space-example]: The following fragment processes an example certificate. It drops any subkey that is not valid at the time of export (because of revocation or expiration), authentication subkeys, and any third-party certifications:
|
||||
|
||||
```sh
|
||||
gpg --export-options export-minimal,export-clean,no-export-attributes \
|
||||
--export-filter keep-uid=mbox=wiktor@metacode.biz \
|
||||
--export-filter 'drop-subkey=expired -t || revoked -t || usage =~ a' \
|
||||
--export wiktor@metacode.biz
|
||||
```
|
||||
|
||||
At the time of writing, the original certificate consists of 152322 bytes of data. The filtered variant consists of only 3771 bytes, which is 40x smaller. In some contexts, there are hard constraints on size, and minimization is unavoidable, e.g., when embedding certificate data in email headers.
|
||||
|
||||
### Pitfalls of minimization
|
||||
|
||||
Disadvantages/risks of minimizing certificates:
|
||||
|
||||
- A minimized certificate does not present a full view of how it (and the validity of its components) evolved over time.
|
||||
- As the OpenPGP subsystem on a user's computer learns about more certificates, third-party certifications that were previously unusable may become usable. Dropping third-party certifications by unknown issuers as a part of minimization prevents this mechanism.
|
||||
- An OpenPGP implementation that minimizes a certificate might remove component keys that it cannot use itself (e.g. because it doesn't support the algorithm of that key), even if the *receiving* implementation supports them.
|
||||
- Refreshing certificates from key servers may inflate the certificate again, since OpenPGP certificates tend to act as [append-only structures](append-only).
|
||||
- Some libraries, such as [anonaddy-sequoia](https://gitlab.com/willbrowning/anonaddy-sequoia/-/blob/master/src/sequoia.rs?ref_type=heads#L125) strip unusable encryption subkeys, but retain at least one subkey, even if all subkeys are expired. Although this may leave only an expired encryption subkey in the certificate, this presents a better UX for the end-user who potentially is still in possession of the private key for decryption.
|
||||
|
||||
## Guidelines
|
||||
|
||||
1. Don't minimize certificates unless you have a good reason to.
|
||||
2. When minimizing a certificate, minimize it in a way that suites your use-case. E.g., when minimizing a certificate for distribution alongside a signed software packet, make sure to include enough historical self-signatures as to not break the verification of the signed packet.
|
||||
3. When presenting a minimized view of a certificate to a consumer, consider when that a new version of that view needs to be generated. Ideally, minimized certificates are freshly generated on demand (e.g., an Autocrypt header is constructed while an email is sent or composed). The receiver is expected to typically merge all data it sees locally.
|
||||
|
||||
## Fingerprints and beyond: "Naming" certificates in user-facing contexts
|
||||
|
||||
Certificates in OpenPGP have traditionally often been "named" using hexadecimal strings of varying length.
|
||||
|
||||
For example, a business card might have shown the hexadecimal fingerprint of a person's OpenPGP certificate to facilitate secure communication. Over time, different formats and lengths for these identifiers have been used.
|
||||
|
||||
This section outlines the various ways in which certificates can be named, and their properties.
|
||||
|
||||
### Fingerprints and Key IDs in Version 4
|
||||
|
||||
With OpenPGP version 4 certificates, it was customary that user-facing software used 20 byte (160 bit) *fingerprints* as an identifier for the certificate. Or alternatively, the 8 byte (64 bit) *Key ID* variant of the fingerprint. Both were represented in hexadecimal format, sometimes with whitespace to group the identifier into blocks for easier readability.
|
||||
|
||||
Workflows such as
|
||||
|
||||
- accepting a certificate for a communication partner, or
|
||||
- issuing a third-party certification for an identity,
|
||||
|
||||
required users to manually compare the 40 character long hexadecimal representation of a fingerprint against a reference source for that fingerprint.
|
||||
|
||||
### Fingerprints in Version 6
|
||||
|
||||
The OpenPGP version 6 standard uses 32 byte (256 bit) fingerprints, but explicitly defines no format for displaying those fingerprints in a human-readable form. The standard [recommends strongly against](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-fingerprint-usability) using version 6 fingerprints as identifiers in user-facing workflows.
|
||||
|
||||
Instead, "mechanical fingerprint transfer and comparison" should be preferred, wherever possible. The reasoning is that humans tend to be bad at comparing high-entropy data[^schuermann] (in addition, many users are probably put off by being asked to compare long hexadecimal strings).
|
||||
|
||||
[^schuermann]: See "An Empirical Study of Textual Key-Fingerprint Representations" <https://www.ibr.cs.tu-bs.de/papers/schuermann-usenix2016.pdf>
|
||||
|
||||
### Use of Fingerprints and Key IDs in APIs
|
||||
|
||||
However, both Fingerprints and Key IDs may (and usually *must*) be used, programmatically, by software that handles OpenPGP data, to address specific certificates. This is equally true for OpenPGP version 6.
|
||||
|
||||
Note that regardless of the OpenPGP version, software that relies on 8-byte Key IDs should not assume that Key IDs are unique. It is trivial to generate collisions for 8-byte Key IDs, so applications must be able to handle Key ID collisions gracefully.
|
||||
|
||||
The historical 4-byte "short Key IDs" format should not be used anywhere, anymore (finding collisions in a 32-bit keyspace has been [trivial for a long time](https://evil32.com/)).
|
||||
|
||||
(certificate-lookup-by-email)=
|
||||
### Looking up certificates by email
|
||||
|
||||
Searching OpenPGP certificates by email is a use case that often arises. For example, when composing an email to a new contact, the sender may want to find the OpenPGP certificate for that contact.
|
||||
|
||||
Different mechanisms allow certificate lookup by email, for example:
|
||||
|
||||
- [Web Key Directory](https://datatracker.ietf.org/doc/draft-koch-openpgp-webkey-service/) (WKD)
|
||||
- The [keys.openpgp.org](https://keys.openpgp.org/) "verifying keyserver" (also known as ["hagrid"](https://gitlab.com/keys.openpgp.org/hagrid), the name of the server software it runs)
|
||||
- SKS-style OpenPGP keyservers (today, most of these run the [Hockeypuck](https://github.com/hockeypuck/hockeypuck) software)
|
||||
|
||||
Their properties differ, for more see [](certificate-distribution).
|
||||
|
||||
(certificate-freshness)=
|
||||
## Certificate freshness: Triggering updates with an expiration time
|
||||
|
||||
For a certificate holder, one problem is that their communication partners may not regularly poll for updates of their certificate.
|
||||
|
||||
A certificate holder usually prefers that everyone else regularly obtains updates for their certificate. This way, a third party will, for example, not mistakenly keep using the certificate indefinitely, after it gets revoked. Setting an expiration time on the certificate, ahead of time, limits the worst case scenario: communication partners will at most use a revoked certificate until its expiration time, even if they never learn of the revocation.
|
||||
|
||||
Once the expiration time is reached, third parties, or ideally their OpenPGP software will have to stop using the certificate, and may attempt to obtain an update for it. For example, from a keyserver, or via WKD. Ideally, certificate updates are obtained automatically, by the user's OpenPGP software, without any need for human intervention.
|
||||
|
||||
After the update, the updated copy of the certificate will usually have a fresh expiration time. The same procedure will repeat once that new expiration time has been reached.
|
||||
|
||||
(social-graph-metadata-leak)=
|
||||
## Metadata leak of Social Graph
|
||||
|
||||
Third-party certifications are signatures over identity components made by other users.
|
||||
|
||||
These certifications form the back-bone of the OpenPGP trust-model called the Web of Trust. The name stems from the fact that the collection of certifications forms a unidirectional graph resembling a web. Each edge of the graph connects the signing certificate to the identity component associated with another certificate.
|
||||
|
||||
OpenPGP software can inspect that graph. Based on the certification data in the graph and a set of trust anchors, it can infer whether a target certificate is legitimate.
|
||||
|
||||
The trust anchor is usually the certificate holder's own key, but a user may designate additional certificates of entities they are connected to as trust anchors.
|
||||
|
||||
Third-party certifications can be published as part of the target certificate to facilitate the process of certificate authentication. Unfortunately, a side effect of this approach is that it's feasible to reconstruct the entire social graph of all people issuing certifications. In addition, the signature creation time of certifications can be used to deduce whether the certificate owner attended a Key Signing Party (and if it was public, where it was held) and whom they interacted with.
|
||||
|
||||
So, there is some tension between the goals of
|
||||
|
||||
- a decentralized system where every participant can access certification information and perform analysis on it locally,
|
||||
- privacy related goals (also see {ref}`certificate-lookup-by-email`, for a comparison of certificate distribution mechanisms, which also touches on this theme).
|
||||
|
||||
(unbound-user-ids)=
|
||||
## Adding unbound, local User IDs to a certificate
|
||||
|
||||
Some OpenPGP software may add User IDs to a certificate, which are not bound to the primary key by the certificate's owner. This can be useful to store local identity information (e.g., Sequoia's public store attaches ["pet-names"][PET] to certificates, in this way).
|
||||
|
||||
[PET]: https://sequoia-pgp.org/blog/2023/04/08/sequoia-sq/#an-address-book-style-trust-model
|
||||
|
||||
Sequoia additionally certifies these "local, third party, User IDs" with a local trust anchor to facilitate local authentication decisions.
|
||||
To prevent accidental publication of these local User IDs (e.g. to public keyservers), Sequoia marks these binding signatures as "local" artifacts using [Exportable Certification](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-exportable-certification) subpackets to mark them as non-exportable.
|
||||
|
||||
(certificate-distribution)=
|
||||
## Certificate distribution mechanisms
|
||||
|
||||
Different mechanisms for discovering certificates, and updating certificate data exist in the OpenPGP space:
|
||||
|
||||
- A *Web Key Directory* service is based on a well-known location on a webserver, serving certificates in a specific format. A WKD server is operated by the entity that controls the DNS domain of an email-based identity of a certificate. This means that WKD is inherently decentralized, and the reliability of OpenPGP certificates may vary depending on the organization that operates a particular WKD instance.
|
||||
- The *keys.openpgp.org* service is a "verifying" keyserver: the keyserver software only publishes identity components (which include email addresses) after sending a verification email to that address, and receiving opt-in consent by the user of the email address. This service makes a different tradeoff: it is centralized, and relying on it to correctly perform the verification step requires trust in the operator. The tradeoff allows the service to only list identity information with the consent of the owner of that identity, and to prevent "enumeration" of the certificates and identities it stores (that is: third parties cannot obtain a list of email addresses in the service's database). By design, this service allows easy publication of revocations without requiring publication of any identity components.
|
||||
- *SKS-style keyservers* act as a distributed synchronizing database, which accepts certificate information without verification. The SKS network handles third-party signatures, additional changes to their handling are pending[^hip1].
|
||||
|
||||
[^hip1]: <https://github.com/hockeypuck/hockeypuck/issues/136>
|
||||
|
||||
One central difference between hockeypuck and hagrid (the software that runs the *keys.openpgp.org* service) is that hockeypuck distributes identity packets and third-party certifications that have indeterminate validity, while hagrid does not.
|
||||
|
||||
(keyserver-flooding)=
|
||||
## Third-party certification flooding
|
||||
|
||||
Traditional OpenPGP keyservers are one mechanism for [collection and distribution](certificate-distribution) of certificate information. Their model revolves around receiving certificate information from sources that don't identify themselves to the keyserver network. Traditionally, these keyservers have accepted both components bound to certificates by self-signatures, and third party identity certifications.
|
||||
|
||||
While a convenience for consumers, indiscriminately accepting and integrating third-party identity certifications comes with significant risks.
|
||||
|
||||
Without any restrictions in place, malicious entities can flood a certificate with excessive certifications. Called "certificate flooding," this form of digital vandalism grossly expands the certificate size, making the certificate cumbersome and impractical for users.
|
||||
|
||||
It also opens the door to potential denial-of-service attacks, rendering the certificate non-functional or significantly impeding its operation.
|
||||
|
||||
The popular [SKS keyserver network experienced certificate flooding firsthand](https://dkg.fifthhorseman.net/blog/openpgp-certificate-flooding.html) in 2019, causing significant changes to its operation.
|
||||
|
||||
```{note}
|
||||
The *keys.openpgp.org* (KOO) service performs a similar function as the SKS-style keyservers.
|
||||
However, there are major differences in its design and tradeoffs.
|
||||
|
||||
The KOO keyserver was designed to:
|
||||
|
||||
1. conform to [GDPR regulations](https://en.wikipedia.org/wiki/General_Data_Protection_Regulation), and
|
||||
2. be resistant to flooding-style vandalism.
|
||||
|
||||
To achieve these goals, KOO does not serve identity components at all, unless an explicit opt-in has been performed, using a confirmation process vial email. Third-party certifications are also not served by default, but only under very specific circumstances, which preclude flooding.
|
||||
```
|
||||
|
||||
### Hockeypuck-based keyservers
|
||||
|
||||
Currently, third-party certification flooding can be worked around by users or administrators requesting the removal/re-adding of a certificate. [See here](https://github.com/hockeypuck/hockeypuck/wiki/HIP-1:-Regaining-control-over-public-key-identity-with-authenticated-key-management).
|
||||
|
||||
Additional mechanisms [are upcoming](support-for-1pa3pc).
|
||||
|
||||
## First-Party attested third-party certifications in OpenPGP (1pa3pc)
|
||||
|
||||
[First-Party attested third-party certifications in OpenPGP](https://datatracker.ietf.org/doc/draft-dkg-openpgp-1pa3pc/) are a "mechanism to allow the owner of a certificate to explicitly approve of specific third-party certifications". 1pa3pc was designed to enable flooding-proof distribution of third-part certifications.
|
||||
|
||||
This mechanism uses the *attested certifications* signature subpacket (type ID `37`), which currently only exists as a proposed feature in [draft-ietf-openpgp-rfc4880bis](https://www.ietf.org/archive/id/draft-ietf-openpgp-rfc4880bis-10.html#table-3)[^ac-draft].
|
||||
|
||||
[^ac-draft]: Introducing the *attested certifications* signature subpacket (type ID `37`) was unfortunately not in scope of the chartered topics for the current "crypto-refresh" work of the OpenPGP working group. However, hopefully the working group can handle this feature in future rechartering.
|
||||
|
||||
(support-for-1pa3pc)=
|
||||
### Support
|
||||
|
||||
- The *keys.openpgp.org* (KOO) keyserver [supports *1pa3pc*](https://gitlab.com/keys.openpgp.org/hagrid/-/commit/39c0e12ac64588220d36bada6497d8396f5915b3).
|
||||
|
||||
- The Hockeypuck keyserver software [plans to add support for *1pa3pc*](https://github.com/hockeypuck/hockeypuck/issues/136#issuecomment-1812466084) in version 2.2.0.
|
||||
|
||||
- The Sequoia `sq` commandline tool [allows adding](https://man.archlinux.org/man/sq-key-attest-certifications.1) attested third-party certifications to a certificate.
|
29
book/source/adv/decryption.md
Normal file
29
book/source/adv/decryption.md
Normal file
|
@ -0,0 +1,29 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# Advanced material: Decryption
|
||||
|
||||
(decryption-seipd-quick-check)=
|
||||
## Verify successful session key decryption
|
||||
|
||||
SEIPDv1 packets might make use of a "quick check" mechanism to quickly verify that the correct session key was used without the need to decrypt the whole SEIPD packet.
|
||||
This check consists of 16 random bytes, followed by a copy of the two last bytes, which are prefixed to the plaintext.
|
||||
During decrypting, these 2 bytes can be compared to the 15th and 16th random byte to detect use of the wrong session key.
|
||||
|
||||
Since the chance to accidentally end up with matching quick check bytes albeit the use of the wrong session key is 1:65536, some implementations validate further contents of the plaintext, such as the packet headers.
|
||||
|
||||
The standard [warns against](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-risks-of-a-quick-check-orac) using the quick check mechanism, as it introduces the risk of a decryption oracle. Instead, the use of SEIPDv2 is recommended, as the AEAD mechanism automatically detects use of the wrong session key early on after the first chunk has been decrypted.
|
||||
|
||||
(decryption-anonymous-recipient)=
|
||||
## Anonymous recipients
|
||||
|
||||
Having all recipients keys listed as part of the PKESK packets presents a metadata leakage. An observer can easily enumerate recipients of a message by comparing the PKESKs with certificates of potential recipients.
|
||||
|
||||
To prevent this issue, the sender can decide to add individual recipients as anonymous recipients using a wildcard key-ID / fingerprint.
|
||||
This is done by creating a normal PKESK packet for the recipient, but setting the recipient key field to `0` (as well as omitting the version number of the key for v6 PKESKs).
|
||||
|
||||
A recipient of such a message that does not find a PKESK addressed specifically to any of their keys, can then try to decrypt any anonymous PKESK packets using any of their encryption subkeys.
|
||||
|
||||
To reduce the number of keys to try, the recipient can skip all secret keys which do not share the public-key algorithm stated in the PKESK packet.
|
32
book/source/adv/encryption.md
Normal file
32
book/source/adv/encryption.md
Normal file
|
@ -0,0 +1,32 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# Advanced material: Encryption
|
||||
|
||||
## Encrypt to multiple/single subkey per certificate?
|
||||
|
||||
A recipient's certificate can contain more than one usable encryption subkey.
|
||||
This raises the question, should the message be encrypted for all of them?
|
||||
|
||||
There is the argument that a powerful attacker might have managed to add an attacker-controlled encryption subkey to the victim's certificate.
|
||||
In this case, only encrypting to the "newest" encryption key would help uncover such an attack. However, a powerful attacker could just MitM any sent messages and just add a PKESK for the victim-controlled encryption keys to hide the fact that the sender used a different key.
|
||||
|
||||
On the other hand, a user might have multiple encryption subkeys on purpose.
|
||||
Picture, for example, a scenario where the same certificate is used on multiple devices, but each device has dedicated encryption subkeys to allow for smoother revocation in case of a lost device.
|
||||
In this scenario, it is important that the sender encrypts the message to all available encryption subkeys.
|
||||
|
||||
## "Negotiating" algorithms based on recipients preference subpackets
|
||||
|
||||
### Prevent "downgrade" -> Policy
|
||||
|
||||
Each implementation should define a "minimum" level of security when it comes to algorithms and key lengths.
|
||||
If the lowest common denominator of symmetric encryption algorithms preferred by a set of recipients provides too little security, the implementation should either use a configured fallback algorithm instead, or fail to produce a message at all.
|
||||
|
||||
## AEAD modes in v2 SEIPD: GCM
|
||||
|
||||
```{note}
|
||||
|
||||
This section is still about to be written.
|
||||
```
|
82
book/source/adv/private_keys.md
Normal file
82
book/source/adv/private_keys.md
Normal file
|
@ -0,0 +1,82 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# Advanced material: Private keys
|
||||
|
||||
(key-store-design)=
|
||||
## Private keystores
|
||||
|
||||
This section examines the diverse architectures and operational mechanics of private keystores in OpenPGP. It focuses on the various design choices, their functional implications, and how they contribute to the secure management of private key material.
|
||||
|
||||
### Design variations
|
||||
|
||||
The design of private key subsystems within the OpenPGP framework varies, offering different approaches to cryptographic operations:
|
||||
|
||||
1. **Separate backend operations**: Some designs execute the primitive cryptographic operations in a separate backend, using only the cryptographic key material. This approach is particularly compatible with general purpose hardware cryptographic devices, such as [trusted platform modules (TPMs)](https://en.wikipedia.org/wiki/Trusted_Platform_Module).
|
||||
2. **Component key-based systems**: An OpenPGP private key subsystem may be built around component keys, specifically the content of [Secret-Key packets](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-secret-key-packet-formats). These packets contain metadata that is required for some operations. ECDH operations, in particular, require metadata as KDF parameters.
|
||||
3. **Full transferable secret keys**: Some designs maintain copies of full TSKs in the private key subsystem, leveraging these for private key operations.
|
||||
|
||||
While private keystore operations require component keys, they do not require access to the entire OpenPGP certificate.
|
||||
|
||||
```{note}
|
||||
The third design option, involving the storage of full TSKs in the private key subsystem, can cause "split brain" problems.
|
||||
|
||||
For example, a private keystore might contain a TSK with outdated certificate metadata, marking the certificate as expired, while the updated version in the local public keystore could indicate an extended expiration time.
|
||||
|
||||
This problem was notably present in GnuPG 1.x, which held separate TSK copies in its private store component. Similarly, the current design of Thunderbird's OpenPGP subsystem can lead to users experiencing such issues.
|
||||
```
|
||||
|
||||
### Two-tier architecture
|
||||
|
||||
At its core, an OpenPGP private key subsystem performs operations requiring only the private cryptographic key material, akin to the "separate backend operations" model described above.
|
||||
|
||||
However, the subsystem also supports operations that require additional access to the metadata of the component key. These operations, supplementary to the core keystore operations, do not involve the private key material.
|
||||
|
||||
When implementing a keystore based on hardware cryptographic devices like [OpenPGP card](openpgp-card), its design will consist of two layers:
|
||||
|
||||
- **core layer**: directly handles private key material, and
|
||||
- **supplementary layer**: performs additional cryptographic operations that don't directly use the private key material, such as [AES key wrap](https://www.rfc-editor.org/rfc/rfc3394.html) for ECDH.
|
||||
|
||||
```{note}
|
||||
The decryption process using ECC algorithms, especially ECDH, has multiple steps. The initial step, potentially performed by devices such as OpenPGP cards, directly uses private key material to produce a "shared secret." Following this, operations like ["AES key unwrap"](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-ec-dh-algorithm-ecdh) are conducted in software outside the hardware device.
|
||||
|
||||
Further details on this process can be found in the "Advanced Encryption Standard (AES) Key Wrap Algorithm" [RFC 3394](https://www.rfc-editor.org/rfc/rfc3394.html).
|
||||
```
|
||||
|
||||
### Addressing individual keys
|
||||
|
||||
A critical aspect of private keystore design involves determining how users address individual keys.
|
||||
|
||||
One common method is using the fingerprint of each component key. The availability of these fingerprints, however, depends on the underlying technology of the keystore. For instance, in software-based private keystores or OpenPGP cards, fingerprints of component keys are usually readily available. Keystores relying on generic cryptographic hardware, like TPMs, need to implement their own mechanisms for tracking and managing the fingerprints of each key.
|
||||
|
||||
### Additional keystore duties
|
||||
|
||||
In addition to key management, a keystore often involves various supplementary functions:
|
||||
|
||||
- **Tracking devices**: Keystores may track which devices contain particular component keys.
|
||||
|
||||
- **Handling secrets**: This involves the management of sensitive information such as passphrases for software keys or PINs for OpenPGP cards.
|
||||
|
||||
- **User interaction alerts**: Keystores might also need to prompt users for necessary interactions during certain operations. For example, OpenPGP cards may require user touch confirmation to authorize each cryptographic action.
|
||||
|
||||
## Understanding key overwriting (KO) attacks
|
||||
|
||||
### What they are
|
||||
|
||||
OpenPGP is subject to specific vulnerabilities known as key overwriting (KO) attacks. These attacks exploit weaknesses in how encrypted private keys or their metadata are handled, potentially leading to the leakage of secret data when the key is used. The core issue lies in OpenPGP's handling of Secret-Key packets, where corruption of the non-encrypted fields can cause the unaltered private key material to be used with altered parameters. This mismatch can result in private key leakage.
|
||||
|
||||
Importantly, KO attacks are particularly relevant when an attacker is responsible for storing a user's encrypted private key. By altering the algorithm field in the Secret-Key packet, the attacker may cause the user to perform a cryptographic operation with a different algorithm. E.g., performing a DSA operation with ECC private key material. By observing the output of that attacker-corrupted operation, the attacker can recover the user's unencrypted private key material, even though the attacker had no direct access to it.
|
||||
|
||||
### Mitigation
|
||||
|
||||
Understanding KO attacks is crucial due to their potential to compromise the integrity and confidentiality of encrypted communications, and the risk of complete private key material compromise. KO attacks highlight the necessity for robust key validation procedures and the dangers of storing keys in insecure environments. OpenPGP application developers should consider if this attack class is a concern in their applications.
|
||||
|
||||
Private keys that are protected with [S2K usage mode 253 (AEAD)](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-secret-key-encryption), are not vulnerable to KO attacks. This mode ensures the integrity of the private key by using its unencrypted fields (including the algorithm field) as the *authentication tag* for integrity verification in the decryption process. When an attacker alters the unencrypted part of the packet, then decryption of the private key material will fail, and the user is prevented from e.g. accidentally using the key material with an altered attacker-controlled algorithm.
|
||||
|
||||
Note that while S2K usage mode 253 (AEAD) has been introduced in the OpenPGP version 6 specification, it can also be applied to OpenPGP version 4 key material (also see {ref}`migration-s2k`).
|
||||
|
||||
#### Resources
|
||||
|
||||
For comprehensive information on KO attacks, including background, attack vectors, countermeasures, and technical analyses, visit [KOpenPGP.com](https://www.kopenpgp.com/). It is based on the paper "Victory by KO: Attacking OpenPGP Using Key Overwriting" written by Lara Bruseghini, Daniel Huigens, and Kenneth G. Paterson for the Proceedings of ACM Conference on Computer and Communications Security, Los Angeles, November 2022.
|
55
book/source/adv/signatures.md
Normal file
55
book/source/adv/signatures.md
Normal file
|
@ -0,0 +1,55 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# Advanced material: Signatures
|
||||
|
||||
(notation-signature-subpackets)=
|
||||
## Notation signature subpackets
|
||||
|
||||
[Notation signature subpackets](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#notation-data) can be used to effectively extend the otherwise limited set of {term}`signature subpacket types<OpenPGP Signature Subpacket Type>` in OpenPGP with user-defined {term}`notations<Notation>`. {term}`Issuers<Issuer>` can use these {term}`notations<Notation>` to add name-value pairs to an {term}`OpenPGP signature<OpenPGP Signature Packet>`.
|
||||
|
||||
{term}`Notation` names - strings encoded in UTF-8 - may reside in the "user namespace." This namespace is characterized by a {term}`notation tag`, followed by a DNS domain name, both in UTF-8 format.
|
||||
|
||||
{term}`Notations<Notation>`, as described earlier, allow for user-defined extensions to the {term}`OpenPGP signature subpacket types<OpenPGP Signature Subpacket Type>`. A practical and popular application of this functionality is seen in Keyoxide, a decentralized {term}`identity verification` service. Keyoxide uses {term}`notations<Notation>` in the `ariadne.id` namespace. For the details of this {term}`implementation<OpenPGP Implementation>`, refer to the [Keyoxide documentation](https://docs.keyoxide.org/wiki/ariadne-identity/).
|
||||
|
||||
## Choosing the hash algorithm for a signature
|
||||
|
||||
A central element of signature packets is the hash digest of the input data. Most OpenPGP software supports a set of different hash mechanisms, of which one is chosen for each signature packet (this is one aspect of OpenPGP's *cryptographic agility*), and used to calculate the hash digest.
|
||||
|
||||
Different hash mechanisms offer different trade-offs:
|
||||
|
||||
- *Hash digest size*: Larger hash size tends to correspond with greater strength against cryptanalysis, and hash digests are relatively small: at the time of this writing, typical sizes are 32 to 64 bytes. However, for some use cases - especially where small messages are sent over a bandwidth-limited transport - larger hash sizes may unacceptably increase message size.
|
||||
- *Computational cost*: Different hash algorithms may have different computational costs. Some OpenPGP users may prefer to limit this cost, for example on constrained computing environments.
|
||||
|
||||
The following sections discuss how the hash algorithm is chosen, based on preferences that are associated with the involved OpenPGP certificates.
|
||||
|
||||
### Typically: Local determination
|
||||
|
||||
Often, signature creation isn't targeted at a specific receiver. Many signatures are issued for an indeterminate set of "anyone who receives the signature."
|
||||
|
||||
For example, self-signatures that form a certificate are aimed at everyone who interacts with that certificate. Similarly, when creating a data signature for a software package, this signature is aimed at "anyone who will check the signature," often over a long period of time, easily spanning years.
|
||||
|
||||
In such cases, the issuer of that signature chooses the hash algorithm locally, without following preferences of a third party.
|
||||
|
||||
### With a specific recipient: "Negotiation" based on recipient's preferences
|
||||
|
||||
In contrast, when a message is created for a specific recipient, the sender can - and should - choose the hash algorithm for the signature packet [based on the recipient's hash algorithm preference](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-hash-algorithm-preferences).
|
||||
|
||||
The recipient's hash algorithm preference is defined in metadata of their certificate, see {ref}`preferences-features` for more details.
|
||||
|
||||
In this workflow, the signed hash digest is created with a hash algorithm that follows the recipient's preferences, and its intersection with the sender's capabilities and preferences.
|
||||
|
||||
## Signature versions
|
||||
|
||||
As described in the [RFC](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-signature-packet-type-id-2), the version of a generated signature packet must conform to the version of the key that issues the signature.
|
||||
|
||||
That is:
|
||||
|
||||
- OpenPGP version 6 keys must generate version 6 signature packets
|
||||
- OpenPGP version 4 keys must generate version 4 signature packets
|
||||
|
||||
Note that some historical version 3 signature packets may still be relevant for applications that handle old OpenPGP data[^sig-v3]. These version 3 signature packets will have been generated by version 4 keys.
|
||||
|
||||
[sig-v3]Version 4 signature packets were introduced in [RFC 2440](https://datatracker.ietf.org/doc/html/rfc2440#section-5.2) in 1998, which specifies that applications SHOULD generate v4 signature, however generation of v3 signature packets has remained allowed through [RFC 4880](https://www.rfc-editor.org/rfc/rfc4880.html#section-5.2).
|
145
book/source/adv/signing_components.md
Normal file
145
book/source/adv/signing_components.md
Normal file
|
@ -0,0 +1,145 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# Advanced material: Signatures on components
|
||||
|
||||
## Certification recipes
|
||||
|
||||
Different {term}`signatures<OpenPGP Signature Packet>` in OpenPGP serve various specific purposes. This section provides practical guidance on creating these {term}`signatures<OpenPGP Signature Packet>`, illustrating each with concrete examples.
|
||||
|
||||
(recipe-algorithm-preferences)=
|
||||
### Change algorithm preferences
|
||||
|
||||
To modify the preferred {term}`symmetric<Symmetric Cryptography>`, compression, {term}`hash<Hash Function>`, or {term}`AEAD algorithms<Authenticated Encryption With Associated Data>` for a {term}`key<Transferable Secret Key>`, the {term}`key owner<Certificate Holder>` needs to issue a {term}`direct key signature` ({term}`type ID<Signature Type ID>` `0x1F`) on the {term}`primary key<OpenPGP Primary Key>`.
|
||||
|
||||
This {term}`signature<OpenPGP Signature Packet>` should have the following structure:
|
||||
|
||||
| {term}`Subpacket<OpenPGP Signature Subpacket>` | Area | {term}`Critical<Criticality Flag>` | Mandatory | Notes |
|
||||
|---------------------------------------------------------------------------------------------|-----------------------------|------------------------------------------|----------------------|----------------------------------------------------------------------------------------|
|
||||
| {term}`Signature Creation Time<Signature Creation Time Subpacket>` | {term}`Hashed<Hashed Area>` | True | True | Current time |
|
||||
| {term}`Issuer Fingerprint<Issuer Fingerprint Subpacket>` | {term}`Hashed<Hashed Area>` | True or False | Strongly Recommended | The {term}`primary key` is the {term}`issuer` |
|
||||
| {term}`Key Flags<Key Flag>` | {term}`Hashed<Hashed Area>` | True | False | Retain {term}`key flags<Key Flag>` from the previous {term}`self-signature` |
|
||||
| {term}`Features<Features Subpacket>` | {term}`Hashed<Hashed Area>` | True | False | Retain {term}`features<Features Subpacket>` from the previous {term}`self-signature` |
|
||||
| {term}`Key Expiration Time<Key Expiration Time Subpacket>` | {term}`Hashed<Hashed Area>` | True | False | Retain {term}`expiration time` from the previous {term}`self-signature`, if applicable |
|
||||
| {term}`Hash Algorithm Preferences<Preferred Hash Algorithms Subpacket>` | {term}`Hashed<Hashed Area>` | False | False | New {term}`preferences<Algorithm Preferences>` |
|
||||
| {term}`Compression Algorithm Preferences<Preferred Compression Algorithms Subpacket>` | {term}`Hashed<Hashed Area>` | False | False | New {term}`preferences<Algorithm Preferences>` |
|
||||
| {term}`Symmetric Algorithm Preferences<Preferred Symmetric Ciphers for v1 SEIPD Subpacket>` | {term}`Hashed<Hashed Area>` | False | False | New {term}`preferences<Algorithm Preferences>` |
|
||||
| {term}`AEAD Algorithm Preferences<Preferred AEAD Ciphersuites Subpacket>` | {term}`Hashed<Hashed Area>` | False | False | New {term}`preferences<Algorithm Preferences>` |
|
||||
|
||||
### Change expiration time
|
||||
|
||||
To adjust the {term}`expiration time` of an {term}`OpenPGP certificate`, a new *{term}`DirectKey<Direct Key Signature>`* {term}`signature<OpenPGP Signature Packet>` ({term}`type ID<Signature Type ID>` `0x1F`) with a modified {term}`Key Expiration Time subpacket` must be issued. The structure of this {term}`signature<OpenPGP Signature Packet>` is identical to the one outlined in the previous section on changing {term}`algorithm preferences`.
|
||||
|
||||
Additionally, the {term}`expiration time` can be altered for individual {term}`User IDs<User ID>` (detailed below) or separate {term}`subkeys<OpenPGP Subkey>` (see {numref}`bind-subkey`).
|
||||
|
||||
### Add User ID
|
||||
|
||||
To {term}`bind<Binding>` a {term}`User ID` to an {term}`OpenPGP certificate` a {term}`certification signature<Certification>` ({term}`type ID<Signature Type ID>` `0x10`-`0x13`) is used which should have the following structure:
|
||||
|
||||
| {term}`Subpacket<OpenPGP Signature Subpacket>` | Area | {term}`Critical<Criticality Flag>` | Mandatory | Notes |
|
||||
|------------------------------------------------------------------------|-----------------------------|------------------------------------------|----------------------|-------------------------------------------------------------------------------------|
|
||||
| {term}`Signature Creation Time<Signature Creation Time Subpacket>` | {term}`Hashed<Hashed Area>` | True | True | Current time |
|
||||
| {term}`Issuer Fingerprint<Issuer Fingerprint Subpacket>` | {term}`Hashed<Hashed Area>` | True or False | Strongly Recommended | The {term}`primary key<OpenPGP Primary Key>` is the {term}`issuer` |
|
||||
| {term}`Primary User ID<Primary User ID Subpacket>` | {term}`Hashed<Hashed Area>` | True | False | Optional |
|
||||
| {term}`Signature Expiration Time<Signature Expiration Time Subpacket>` | {term}`Hashed<Hashed Area>` | True | False | Optional |
|
||||
|
||||
In addition to these {term}`subpackets<OpenPGP Signature Subpacket>`, {term}`self-certifications<Self-certification>` for {term}`User IDs<User ID>` can include others – such as {term}`key flags<Key Flag>`, {term}`features<Features Subpacket>`, and {term}`algorithm preferences` – as shown in the previous table. This enables the specification of unique capabilities and {term}`preferences<Algorithm Preferences>` for each {term}`identity` associated with the {term}`certificate<OpenPGP Certificate>`.
|
||||
|
||||
### Remove or revoke a User ID
|
||||
|
||||
Since {term}`OpenPGP certificates<OpenPGP certificate>` are often distributed by the means of {term}`key servers<Key Server>`, new {term}`signatures<OpenPGP Signature Packet>` on a {term}`certificate<OpenPGP Certificate>` are often "[merged](certificate-merging)" into existing copies of the {term}`certificate<OpenPGP Certificate>` locally by the recipient.
|
||||
|
||||
This integration process means it is practically impossible to directly remove {term}`signatures<OpenPGP Signature Packet>` or {term}`User IDs<User ID>` from a {term}`certificate<OpenPGP Certificate>`, as there is no way to communicate the intention of {term}`packet<OpenPGP Signature Packet>` deletion to the recipient.
|
||||
|
||||
To effectively mark a {term}`User ID` as invalid, the user can publish a copy of their {term}`certificate<OpenPGP Certificate>` with a {term}`Certification Revocation signature<Certification Revocation Signature Packet>` ({term}`type ID<Signature Type ID>` `0x30`) attached to the invalidated {term}`User ID`. This {term}`signature<OpenPGP Signature Packet>` signals that the specified {term}`User ID` is no longer valid or associated with the {term}`certificate holder`.
|
||||
|
||||
The structure of a {term}`Certification Revocation signature<Certification Revocation Signature Packet>` is as follows:
|
||||
|
||||
| {term}`Subpacket<OpenPGP Signature Subpacket>` | Area | {term}`Critical<Criticality Flag>` | Mandatory | Notes |
|
||||
|--------------------------------------------------------------------|-----------------------------|------------------------------------------|----------------------|-------------------------------------------------------------------------------------|
|
||||
| {term}`Signature Creation Time<Signature Creation Time Subpacket>` | {term}`Hashed<Hashed Area>` | True | True | Current time |
|
||||
| {term}`Issuer Fingerprint<Issuer Fingerprint Subpacket>` | {term}`Hashed<Hashed Area>` | True or False | Strongly Recommended | The {term}`primary key<OpenPGP Primary Key>` is the {term}`issuer` |
|
||||
| {term}`Reason for Revocation<Reason for Revocation Subpacket>` | {term}`Hashed<Hashed Area>` | True | False | Determines {term}`soft<Soft Revocation>` or {term}`hard revocation` |
|
||||
|
||||
For {term}`User ID` {term}`revocations<Revocation>`, the *{term}`Reason for Revocation<Reason for Revocation Subpacket>`* {term}`subpacket<OpenPGP Signature Subpacket>` is crucial. A value of `0` means no specific reason, leading to a {term}`hard revocation`, while `32` indicates the {term}`User ID` is no longer valid, resulting in a {term}`soft revocation`. Omitting the {term}`reason subpacket<Reason For Revocation Subpacket>` is also equivalent to a {term}`hard revocation`.
|
||||
|
||||
It is generally advisable to use reason code `32` for revoking {term}`User IDs<User ID>`.
|
||||
|
||||
(recipe-binding-subkeys)=
|
||||
### Add a subkey
|
||||
|
||||
As part of {term}`life-cycle management`, users may need to add a new {term}`subkey<OpenPGP Subkey>` to their {term}`OpenPGP certificate`, often for reasons such as upgrading to a {term}`subkey<OpenPGP Subkey>` with more advanced cryptographic algorithms. The process involves creating a specific {term}`signature<OpenPGP Signature Packet>` structure:
|
||||
|
||||
| {term}`Subpacket<OpenPGP Signature Subpacket>` | Area | {term}`Critical<Criticality Flag>` | Mandatory | Notes |
|
||||
|---------------------------------------------------------------------------------------------|-----------------------------|------------------------------------------|-------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| {term}`Signature Creation Time<Signature Creation Time Subpacket>` | {term}`Hashed<Hashed Area>` | True | True | Current time |
|
||||
| {term}`Issuer Fingerprint<Issuer Fingerprint Subpacket>` | {term}`Hashed<Hashed Area>` | True or False | Strongly Recommended | The {term}`primary key<OpenPGP Primary Key>` is the {term}`issuer` |
|
||||
| {term}`Key Flags<Key Flag>` | {term}`Hashed<Hashed Area>` | True | Strongly Recommended | Determine the usage of the {term}`key<OpenPGP Subkey>` |
|
||||
| {term}`Key Expiration Time<Key Expiration Time Subpacket>` | {term}`Hashed<Hashed Area>` | True | False | Specifies the {term}`expiration time` of the {term}`subkey<OpenPGP Subkey>` |
|
||||
| {term}`Embedded Signature<Embedded Signature Subpacket>` | {term}`Hashed<Hashed Area>` | True | If {term}`Key Flags<Key Flag>` contains **{term}`S<Signing Key Flag>`** | {term}`Signing subkeys<OpenPGP Signing Subkey>` require embedded *{term}`Primary Key Binding<Primary Key Binding Signature>`* {term}`signature<OpenPGP Signature Packet>` |
|
||||
| {term}`Hash Algorithm Preferences<Preferred Hash Algorithms Subpacket>` | {term}`Hashed<Hashed Area>` | False | False | Per {term}`key<Component Key>` {term}`preferences<Algorithm Preferences>` |
|
||||
| {term}`Compression Algorithm Preferences<Preferred Compression Algorithms Subpacket>` | {term}`Hashed<Hashed Area>` | False | False | Per {term}`key<Component Key>` {term}`preferences<Algorithm Preferences>` |
|
||||
| {term}`Symmetric Algorithm Preferences<Preferred Symmetric Ciphers for v1 SEIPD Subpacket>` | {term}`Hashed<Hashed Area>` | False | False | Per {term}`key<Component Key>` {term}`preferences<Algorithm Preferences>` |
|
||||
| {term}`AEAD Algorithm Preferences<Preferred AEAD Ciphersuites Subpacket>` | {term}`Hashed<Hashed Area>` | False | False | Per {term}`key<Component Key>` {term}`preferences<Algorithm Preferences>` |
|
||||
|
||||
In addition to these {term}`subpackets<OpenPGP Signature Subpacket>`, users can specify {term}`algorithm preferences` for each {term}`subkey<OpenPGP Subkey>`, distinct from those set in the {term}`certificate<OpenPGP Certificate>`'s *{term}`Direct Key<Direct Key Signature>`* {term}`signature<OpenPGP Signature Packet>`.
|
||||
|
||||
### Revoke a subkey
|
||||
|
||||
{term}`Subkeys<OpenPGP subkey>`, like {term}`User IDs<User ID>`, can be individually revoked in OpenPGP.
|
||||
This is done by issuing a {term}`Subkey Revocation signature<Subkey Revocation Signature Packet>` ({term}`type ID<Signature Type ID>` `0x28`) using the {term}`primary key<OpenPGP Primary Key>`.
|
||||
|
||||
The structure of such a {term}`signature<OpenPGP Signature Packet>` is straightforward:
|
||||
|
||||
| {term}`Subpacket<OpenPGP Signature Subpacket>` | Area | {term}`Critical<Criticality Flag>` | Mandatory | Notes |
|
||||
|--------------------------------------------------------------------|-----------------------------|------------------------------------------|----------------------|-------------------------------------------------------------------------------------|
|
||||
| {term}`Signature Creation Time<Signature Creation Time Subpacket>` | {term}`Hashed<Hashed Area>` | True | True | Current time |
|
||||
| {term}`Issuer Fingerprint<Issuer Fingerprint Subpacket>` | {term}`Hashed<Hashed Area>` | True or False | Strongly Recommended | The {term}`primary key<OpenPGP Primary Key>` is the {term}`issuer` |
|
||||
| {term}`Reason for Revocation<Reason For Revocation Subpacket>` | {term}`Hashed<Hashed Area>` | True | False | Determines {term}`soft<Soft Revocation>` or {term}`hard revocation` |
|
||||
|
||||
|
||||
In {term}`Subkey Revocation signatures<Subkey Revocation Signature Packet>`, the [reason for revocation](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-reason-for-revocation) {term}`subpacket<OpenPGP Signature Subpacket>` can only have values in the range of `0-3`. The values `1` ({term}`key<OpenPGP Subkey>` superseded) and `3` ({term}`key<OpenPGP Subkey>` retired and no longer used) indicate {term}`soft revocations<Soft Revocation>`, whereas values `0` (no reason) and `2` ({term}`key<OpenPGP Subkey>` compromised) indicate {term}`hard revocations<Hard Revocation>`.
|
||||
|
||||
Note that a value of `32` is not applicable in these {term}`signatures<OpenPGP Signature Packet>`.
|
||||
|
||||
### Revoke a certificate
|
||||
|
||||
Users may find themselves needing to revoke their entire {term}`OpenPGP certificate`, rendering it unusable. This could be for various reasons, such as migrating to a new {term}`certificate<OpenPGP certificate>` or in response to a compromise of the {term}`certificate<OpenPGP certificate>`'s {term}`secret key material<Private Key Material>`.
|
||||
|
||||
While a {term}`soft-revoked<Soft Revocation>` {term}`certificate<OpenPGP Certificate>` can be re-validated at a later time with a new {term}`certification`, a {term}`hard revocation` is permanent.
|
||||
|
||||
The recommended way to {term}`revoke<Revocation>` a {term}`certificate<OpenPGP Certificate>` is by issuing a {term}`Key Revocation signature<Key Revocation Signature Packet>` ({term}`type ID<Signature Type ID>` `0x20`). Its structure is similar to that of a {term}`Certification Revocation signature<Certification Revocation Signature Packet>`.
|
||||
|
||||
| {term}`Subpacket<OpenPGP Signature Subpacket>` | Area | {term}`Critical<Criticality Flag>` | Mandatory | Notes |
|
||||
|--------------------------------------------------------------------|-----------------------------|------------------------------------------|----------------------|-------------------------------------------------------------------------------------|
|
||||
| {term}`Signature Creation Time<Signature Creation Time Subpacket>` | {term}`Hashed<Hashed Area>` | True | True | Current time |
|
||||
| {term}`Issuer Fingerprint<Issuer Fingerprint Subpacket>` | {term}`Hashed<Hashed Area>` | True or False | Strongly Recommended | The {term}`primary key<OpenPGP Primary Key>` is the {term}`issuer` |
|
||||
| {term}`Reason for Revocation<Reason For Revocation Subpacket>` | {term}`Hashed<Hashed Area>` | True | False | Determines {term}`soft<Soft Revocation>` or {term}`hard revocation` |
|
||||
|
||||
For {term}`Key Revocation signatures<Key Revocation Signature Packet>`, the guidelines regarding the [*Reason for Revocation* subpacket](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-reason-for-revocation) are the same as those for {term}`Subkey Revocation signatures<Subkey Revocation Signature Packet>`.
|
||||
|
||||
### Common subpackets in OpenPGP signatures
|
||||
|
||||
In OpenPGP, certain {term}`subpackets<OpenPGP Signature Subpacket>` are universally expected across all types of {term}`signatures<OpenPGP Signature Packet>`, serving fundamental roles in the {term}`signature<OpenPGP Signature Packet>`'s structure, {term}`verification` and {term}`validation`:
|
||||
|
||||
* **{term}`Signature Creation Time<Signature Creation Time Subpacket>`**: This is a mandatory {term}`subpacket<OpenPGP Signature Subpacket>` in every {term}`OpenPGP signature<OpenPGP Signature Packet>`. It contains the timestamp of when the {term}`signature<OpenPGP Signature Packet>` was created. For security and integrity, this {term}`subpacket<OpenPGP Signature Subpacket>` must be located in the {term}`hashed area` of the {term}`signature<OpenPGP Signature Packet>` and is recommended to be marked as {term}`critical<Criticality Flag>`.
|
||||
|
||||
* **{term}`Issuer Fingerprint<Issuer Fingerprint Subpacket>`**: Essential for {term}`signature<OpenPGP Signature Packet>` {term}`validation`, this {term}`subpacket<OpenPGP Signature Subpacket>` identifies the {term}`key<OpenPGP Primary Key>` (or {term}`subkey<OpenPGP Subkey>`) that was used to create the {term}`signature<OpenPGP Signature Packet>`. OpenPGP v6 {term}`signatures<OpenPGP Signature Packet>` should include the {term}`Issuer Fingerprint subpacket`, containing the 32-byte {term}`fingerprint<OpenPGP Fingerprint>` of the {term}`key<Component Key>`.
|
||||
|
||||
```{note}
|
||||
The {term}`key<Component Key>` used as the {term}`issuer` in the {term}`signature<OpenPGP Signature Packet>` might be a {term}`subkey<OpenPGP Subkey>` of the {term}`certificate<OpenPGP Certificate>`.
|
||||
```
|
||||
|
||||
These {term}`subpackets<OpenPGP Signature Subpacket>` can be placed in either the {term}`hashed<Hashed Area>` or {term}`unhashed area` due to its self-{term}`authenticating<Authentication>` nature. However, we recommend including them in the {term}`signature<OpenPGP Signature Packet>`'s {term}`hashed area`.
|
||||
|
||||
## Managing subpacket conflicts and duplication
|
||||
|
||||
In {term}`OpenPGP signatures<OpenPGP Signature Packet>`, both the {term}`hashed<Hashed Area>` and {term}`unhashed areas<Unhashed Area>` are composed of lists of {term}`subpackets<OpenPGP Signature Subpacket>`. Inherently, this structure permits the duplication of the same {term}`subpacket<OpenPGP Signature Subpacket>`, which could lead to conflicts. To manage these potential conflicts, the following strategies are used:
|
||||
|
||||
- **Precedence of {term}`hashed area`**: {term}`Subpackets<OpenPGP Signature Subpacket>` within the {term}`hashed area` of a {term}`signature<OpenPGP Signature Packet>` take precedence over those in the {term}`unhashed area`. This hierarchy helps resolve conflicts when the same {term}`subpacket<OpenPGP Signature Subpacket>` appears in both areas.
|
||||
|
||||
- **Handling conflicts within the same area**: Conflicts can still arise within the same area, such as when two {term}`subpackets<OpenPGP Signature Subpacket>` have different {term}`expiration times<Expiration Time>`. In such cases, the [OpenPGP specification](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-notes-on-subpackets) advises that {term}`implementations<OpenPGP Implementation>` should favor the last occurrence of a conflicting {term}`subpacket<OpenPGP Signature Subpacket>` in the {term}`hashed area`.
|
||||
|
||||
In certain scenarios, having duplicate {term}`subpackets<OpenPGP Signature Subpacket>` with conflicting content is logical and even necessary. For example, consider a {term}`signature<OpenPGP Signature Packet>` created by a version 4 {term}`issuer` {term}`key<Component Key>`, which was upgraded from an older OpenPGP version (like v3). Since the {term}`key ID` calculation scheme changed from v3 to v4, the identifiers for the same {term}`key<Component Key>` would differ between these versions. Therefore, a v4 signature might contain two {term}`issuer key ID subpackets<Issuer Fingerprint Subpacket>`, each with different, yet correct values for v3 and v4 {term}`keys<Component Key>`, respectively. This allows for backward compatibility and ensures the {term}`signature<OpenPGP Signature Packet>` can be {term}`validated<Validation>` under both {term}`key ID` calculation schemes.
|
52
book/source/adv/signing_data.md
Normal file
52
book/source/adv/signing_data.md
Normal file
|
@ -0,0 +1,52 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# Advanced material: Signatures over data
|
||||
|
||||
## Nesting of one-pass signatures
|
||||
|
||||
Signing a message using the one-pass mechanism involves prepending a *One-Pass-Signature* (OPS) packet to the message and appending the corresponding signature, sandwiching the signed content.
|
||||
|
||||
An OpenPGP message can contain multiple signatures added that way.
|
||||
|
||||
```{note}
|
||||
One-Pass-Signatures are nested, meaning the outermost One-Pass-Signature packet corresponds to the outermost signature packet.
|
||||
```
|
||||
|
||||
When a message is signed, the signature is always calculated over the contents of the literal data packet, not the literal data packet itself.
|
||||
This means, that if a message, which is compressed using a compressed data packet is wrapped using a one-pass-signature, the signature is still being calculated over the plaintext inside the literal data packet.
|
||||
|
||||
There is one exception though.
|
||||
```{note}
|
||||
Of course there is.
|
||||
```
|
||||
|
||||
The OPS packet has a "nested" flag[^nested-flag], which can either be `1` or `0`.
|
||||
If this flag is set to `0`, it indicates that further OPSs will follow this packet, which are calculated over the same plaintext data as this OPS is. A value of `1` indicates, that either no further OPS packets will follow (this OPS is the last), or that this OPS is calculated over the the usual plaintext data, but wrapped inside any OPS+Signature combinations that follow this OPS.
|
||||
|
||||
[^nested-flag]: See [description of the nested flag](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#section-5.4-3.8.1).
|
||||
|
||||
This mechanism enables attested signatures, where the signer signs an already one-pass-signed message including the already contained signature.
|
||||
|
||||
As a practical example, consider the following notation:
|
||||
* `LIT("Hello World")` represents a literal data packet with the content `Hello World`.
|
||||
* `COMP(XYZ)` represents a compressed data packet over some other packet `XYZ`.
|
||||
* `OPS₁` represents a one-pass-signature packet with the nested flag set to `1`. Analogous, `OPS₀` has the nested flag set to `0`.
|
||||
* `SIG` represents a signature packet.
|
||||
|
||||
A normal, one-pass-signed message looks like this:
|
||||
`OPS₁ LIT("Hello World") SIG`
|
||||
|
||||
Here, the signature is calculated over the plaintext `Hello World`, as is it in a message that has the following form: `OPS₁ COMP(LIT("Hello World")) SIG`.
|
||||
|
||||
A message, where multiple one-pass-signatures are calculated over the same plaintext looks the following:
|
||||
`OPS₀ OPS₀ OPS₁ LIT("Hello World") SIG SIG SIG`
|
||||
|
||||
All three signatures are calculated over the same plaintext `Hello World`.
|
||||
|
||||
Now, a message, where the signer attests an already signed message has the following format:
|
||||
`OPS₁ OPS₁ LIT("Hello World") SIG SIG`
|
||||
|
||||
While the inner signature is calculated over the usual plaintext `Hello World`, the outer signature is instead calculated over `OPS₁ Hello World SIG`.
|
116
book/source/adv/verification.md
Normal file
116
book/source/adv/verification.md
Normal file
|
@ -0,0 +1,116 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
# Advanced material: Signature verification
|
||||
|
||||
## Attribute shadowing
|
||||
|
||||
When determining the preferences of a key, several signatures may have to be inspected.
|
||||
|
||||
For example, when using a signing subkey to generate a data signature, an implementation might want to check for hash algorithm preferences on the subkey binding signature.
|
||||
However, the RFC [states](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#section-5.2.3.10-2) that signature subpackets in a direct key signature (which may also contain preferences) on the OpenPGP certificate's primary key apply to the entire OpenPGP key, and therefore also to the signing subkey.
|
||||
|
||||
In this case, the implementation uses the preferences from the subkey binding signature, but if no such subpacket is found on the latest binding signature, it falls back to the preferences from the direct key signature.
|
||||
This is called attribute shadowing, since direct key signature subpackets apply to all subkeys, but are shadowed by binding signature subpackets.
|
||||
|
||||
```{figure} ../img/drawio/attribute-shadowing.png
|
||||
:name: fig-signature-verification-attribute-shadowing
|
||||
:alt: Depicts a certificate with to dedicated signing subkeys and a subkey binding signature each. The primary key carries a direct-key signature, which specifies SHA-512 and SHA-256 as hash algorithm preferences. The binding signature of the first signing subkey does not specify preferences, while the binding signature of the second subkey defines SHA-384. Signatures made using the first subkey source the hash algorithm preferences from the direct-key signature, due to the absence of a preference subpacket on the binding signature, while for signature made using the second subkey the direct-key signature's preferences are shadowed by the subkey signatures preferences subpacket.
|
||||
|
||||
Inheritance and Shadowing of Attributes
|
||||
```
|
||||
|
||||
```{admonition} Note
|
||||
:class: note
|
||||
|
||||
Attribute shadowing is relatively straightforward to reason about when used for algorithm preferences. For other subpacket types, shadowing may be confusing, and/or the semantics underspecified (e.g. for key expiration time subpackets).
|
||||
```
|
||||
|
||||
## Signature shadowing
|
||||
|
||||
When inspecting signatures on a component of an OpenPGP certificate, of the signatures that are in effect for each function, only the newest is considered.
|
||||
|
||||
In other words:
|
||||
- If there are three binding signatures `A, B, C` for a subkey,
|
||||
- where:
|
||||
- `A` was created at `t0`,
|
||||
- `B` at `t1`, and
|
||||
- `C` at `t3`, with
|
||||
- `t0 < t1 < t2 < t3`.
|
||||
- Then at `t2`, an implementation only needs to consider signature `B`,
|
||||
- because `C` is not yet in effect, and
|
||||
- `A` is shadowed, because it is older than `B`.
|
||||
|
||||
```{figure} ../img/drawio/cert-validity-subkey.png
|
||||
:name: fig-signature-verification-subkey-validity
|
||||
:alt: Depicts a gantt-style diagram visualizing how the validity of a certificates components changes over time, depending on component signatures.
|
||||
|
||||
An example for how certificate validity can change with time.
|
||||
```
|
||||
|
||||
```{note}
|
||||
|
||||
Signature shadowing should not be confused with attribute shadowing.
|
||||
```
|
||||
|
||||
As attribute and signature shadowing can occur in combination, it is not always obvious which properties a key has at a given time.
|
||||
|
||||
```{figure} ../img/drawio/dk-attributes-and-shadowing.png
|
||||
:name: fig-signature-verification-signature-shadowing
|
||||
:alt: Depicts a certificate with a subkey, whose capabilities change over time, due to signature shadowing another.
|
||||
|
||||
Signatures shadow one another, based on reference time.
|
||||
```
|
||||
|
||||
## Which signatures take precedence?
|
||||
|
||||
Multiple signatures can be attached to an OpenPGP certificate or component. These signatures can contain conflicting information.
|
||||
|
||||
When verifying a signature that is not self-qualifying, an implementation needs to inspect self-qualifying signatures in the issuer's certificate for qualification. The certificate may contain multiple signatures for one component.
|
||||
|
||||
For example, there could be multiple subkey binding signatures for one subkey. This could be the case because the expiration time in the original binding signature has expired, and the certificate holder has issued a new binding signature with an extended expiration time.
|
||||
|
||||
In general, for each category of signatures (categories such as binding signatures for one particular subkey), the signature with the latest creation time takes precedence, and only that signature is considered.
|
||||
|
||||
Alternatively, there can be competing qualifying signatures of different types, e.g., a direct key signature and a self-certification signature on a primary User ID. Both of these contain metadata associated with the entire certificate. By default, the direct key signature is preferred[^conflicting-prefs] in OpenPGP version 6.
|
||||
|
||||
[^conflicting-prefs]: However, the semantics of these cases are not currently fully specified, see [this discussion](https://gitlab.com/openpgp-wg/rfc4880bis/-/issues/103).
|
||||
|
||||
Depending on how a certificate is "located," different metadata from possible candidate signatures "shadow" one another. The RFC [states](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-notes-on-self-signatures) that when a certificate is "located" by the OpenPGP software "via an identity", then the metadata associated with that identity takes precedence over more global metadata, such as that associated with the certificate's primary key, with a direct key signature.
|
||||
|
||||
For example, the latest direct key signature could list "SHA512, SHA384" as hash algorithm preferences, while the latest self-certification of the User ID "Bob" could list only "SHA256."
|
||||
For yet another User ID "Bobby," the self-signature could list no hash algorithm preferences at all.
|
||||
If the user wants to compose a signed message using the associated OpenPGP key they need to figure out which preferences to use.
|
||||
|
||||
The specification recommends that implementations decide which signature takes precedence by the way the certificate is "addressed."
|
||||
|
||||
```{figure} ../img/drawio/narrow-interpretation.png
|
||||
|
||||
Preferences are sourced from signatures on different components, depending on how the key is addressed.
|
||||
```
|
||||
|
||||
If the user wants to write an email as "Bob," it should consider the signature on "Bob," so SHA256 should be used as hash algorithm.
|
||||
If instead the user wants to write as "Bobby," the implementation should inspect the self-certification on "Bobby" instead.
|
||||
However, since this signature does not carry any hash algorithm preferences subpacket, the implementation must fall back to the direct key signature instead.
|
||||
The same is true if the certificate is used without any User ID as sender.
|
||||
|
||||
To complicate things further:
|
||||
Algorithm preferences can also be stated on subkey binding signatures, so if the certificate has a dedicated signing subkey, there is yet another signature which could take precedence.
|
||||
Preferences from the subkey binding signature take precedence over the direct key signature, but not over self-certifications on the User ID.
|
||||
|
||||
There can be more than one signature on a component. As an example, there are 3 direct key signatures (e.g., because the key's expiry has been extended two times).
|
||||
In general, for each component, only the newest self-signature is "in effect," and older signatures are "shadowed."
|
||||
For each certificate, there is at most one "active" direct key signature, for each User ID at most one active self-certification and for each subkey exactly one subkey binding.
|
||||
|
||||
## Complexity of the packet format
|
||||
|
||||
OpenPGP certificates can contain complex preference settings. Additionally, the OpenPGP packet format allows a lot of flexibility when storing certificates in TPK format.
|
||||
|
||||
User ID packets, for example, do not have a fixed position in a TPK. This means an attacker (or an implementation-internal certificate canonicalization procedure) can change the order in which User IDs appear in the certificate's packet sequence.
|
||||
|
||||
As a concrete example, consider a certificate with multiple User IDs, all marked as primary. Or similarly, a certificate with multiple User IDs of which none is marked as primary.
|
||||
Clients might apply different heuristics to figure out which User ID actually qualifies as the primary User ID here.
|
||||
|
||||
Such subtle changes to the representation of a certificate can lead to different preference settings being deduced, by different OpenPGP implementations.
|
Loading…
Add table
Add a link
Reference in a new issue