Merge remote-tracking branch 'refs/remotes/origin/tammi-ch4' into tammi-ch4
|
@ -30,5 +30,5 @@ steps:
|
||||||
fi
|
fi
|
||||||
when:
|
when:
|
||||||
event: push
|
event: push
|
||||||
# uncomment when this is proven to be working:
|
# only generate sphinx output for the "draft" branch
|
||||||
#branch: main
|
branch: draft
|
||||||
|
|
|
@ -19,9 +19,8 @@ In OpenPGP, the term "key" may refer to three distinct layers, each serving a un
|
||||||
2. An OpenPGP *component key* includes either an "OpenPGP primary key" or an "OpenPGP subkey." It is a building block of an OpenPGP certificate, consisting of a cryptographic keypair coupled with some invariant metadata, such as key creation time.
|
2. An OpenPGP *component key* includes either an "OpenPGP primary key" or an "OpenPGP subkey." It is a building block of an OpenPGP certificate, consisting of a cryptographic keypair coupled with some invariant metadata, such as key creation time.
|
||||||
3. An "OpenPGP certificate" (or "OpenPGP key") consists of several component keys, identity components, and other elements. These certificates are dynamic, evolving over time as components are added, expire, or are marked as invalid.
|
3. An "OpenPGP certificate" (or "OpenPGP key") consists of several component keys, identity components, and other elements. These certificates are dynamic, evolving over time as components are added, expire, or are marked as invalid.
|
||||||
|
|
||||||
The following section will delve into the OpenPGP-specific layers (2 and 3) to provide a clearer understanding of their roles within OpenPGP certificates.
|
In the following section, we'll look at the two OpenPGP-specific layers (2 and 3).
|
||||||
|
|
||||||
For detailed insights on structure and handling, refer to our chapters on OpenPGP [certificates](certificates_chapter) and [private keys](private_key_chapter). Additionally, managing certificates, and understanding their authentication and trust models are vital topics. While this document briefly touches upon these aspects, they are integral to working proficiently with OpenPGP.
|
|
||||||
|
|
||||||
## Structure of OpenPGP certificates
|
## Structure of OpenPGP certificates
|
||||||
|
|
||||||
|
@ -50,11 +49,11 @@ Every element in an OpenPGP certificate revolves around a central component: the
|
||||||
OpenPGP certificates tend to have a long lifespan, with the potential for modifications (typically by their owner) over time. Components may be added or invalidated throughout a certificate's lifetime.
|
OpenPGP certificates tend to have a long lifespan, with the potential for modifications (typically by their owner) over time. Components may be added or invalidated throughout a certificate's lifetime.
|
||||||
```
|
```
|
||||||
|
|
||||||
### OpenPGP component keys
|
## Component keys
|
||||||
|
|
||||||
An OpenPGP certificate usually contains multiple component keys.
|
An OpenPGP certificate usually contains multiple OpenPGP component keys.
|
||||||
|
|
||||||
OpenPGP component keys consist of an [asymmetric cryptographic keypair](asymmetric_key_pair) and a creation timestamp. Once created, these attributes of a component key remain fixed (for ECDH keys, two additional parameters are part of a component key's constitutive data[^ecdh-parameters]).
|
OpenPGP component keys consist of an [asymmetric cryptographic keypair](asymmetric_key_pair) and a creation timestamp. These attributes of a component key cannot be changed after creation (in the case of ECDH keys, two additional parameters are part of a component key's constituting data[^ecdh-paramters]).
|
||||||
|
|
||||||
[^ecdh-parameters]: For [ECDH](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-algorithm-specific-part-for-ecd) component keys, two additional algorithm parameters are integral to the component key's constitutive and immutable properties. Those parameters specify a hash function and a symmetric encryption algorithm.
|
[^ecdh-parameters]: For [ECDH](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-algorithm-specific-part-for-ecd) component keys, two additional algorithm parameters are integral to the component key's constitutive and immutable properties. Those parameters specify a hash function and a symmetric encryption algorithm.
|
||||||
|
|
||||||
|
@ -63,7 +62,7 @@ OpenPGP component keys consist of an [asymmetric cryptographic keypair](asymmetr
|
||||||
An OpenPGP component key
|
An OpenPGP component key
|
||||||
```
|
```
|
||||||
|
|
||||||
Component keys containing private key material also contain metadata that specifies the password protection scheme for the private key material.
|
Component key representations that include private key material also contain metadata that specifies the password protection scheme for the private key material.
|
||||||
|
|
||||||
For each OpenPGP component key, an *OpenPGP fingerprint* can be generated. This fingerprint is derived from the combination of the public key material and creation timestamp (and ECDH parameters, if applicable).
|
For each OpenPGP component key, an *OpenPGP fingerprint* can be generated. This fingerprint is derived from the combination of the public key material and creation timestamp (and ECDH parameters, if applicable).
|
||||||
|
|
||||||
|
@ -72,20 +71,20 @@ For each OpenPGP component key, an *OpenPGP fingerprint* can be generated. This
|
||||||
Every OpenPGP component key is identifiable by a unique fingerprint.
|
Every OpenPGP component key is identifiable by a unique fingerprint.
|
||||||
```
|
```
|
||||||
|
|
||||||
The fingerprint of our example OpenPGP component key is `AAA1 8CBB 2546 85C5 8358 3205 63FD 37B6 7F33 00F9 FB0E C457 378C D29F 1026 98B3` [^keyid].
|
The fingerprint of our example component OpenPGP key is `AAA1 8CBB 2546 85C5 8358 3205 63FD 37B6 7F33 00F9 FB0E C457 378C D29F 1026 98B3` [^keyid].
|
||||||
|
|
||||||
[^keyid]: In OpenPGP version 4, the rightmost 64 bits were sometimes used as a shorter identifier, called "Key ID."
|
[^keyid]: In OpenPGP version 4, the rightmost 64 bit were sometimes used as a shorter identifier, called "Key ID".
|
||||||
For example, an OpenPGP version 4 certificate with the fingerprint `B3D2 7B09 FBA4 1235 2B41 8972 C8B8 6AC4 2455 4239` might be referenced by the 64-bit Key ID `C8B8 6AC4 2455 4239` or formatted as `0xC8B86AC424554239`.
|
E.g., an OpenPGP version 4 certificate with the fingerprint `B3D2 7B09 FBA4 1235 2B41 8972 C8B8 6AC4 2455 4239` might be referred to by the 64 bit Key ID `C8B8 6AC4 2455 4239` or styled as `0xC8B86AC424554239`.
|
||||||
Historically, even shorter 32-bit identifiers were used, like this: `2455 4239`, or `0x24554239`. Such identifiers still appear in very old documents about PGP. However, [32-bit identifiers have been long deemed unfit for purpose](https://evil32.com/). At one point, 32-bit identifiers were called "short Key ID," while 64-bit identifiers were referred to as "long Key ID."
|
Historically, even shorter 32 bit identifiers have sometimes been used, like this: `2455 4239`, or `0x24554239`. You may still see such identifiers in very old documents about PGP. However, 32 bit identifiers have [been unfit for purpose for a long time](https://evil32.com/). At some point, 32 bit identifiers were called "short Key ID", while 64 bit identifiers were called "long Key ID".
|
||||||
|
|
||||||
Component keys serve in one of two roles: either as an "OpenPGP primary key" or as an "OpenPGP subkey."
|
Component keys are used in one of two roles: either as "OpenPGP primary key," or as an "OpenPGP subkey".
|
||||||
|
|
||||||
#### Primary key
|
#### Primary key
|
||||||
|
|
||||||
The OpenPGP primary key is a distinct component key that serves a central role in an OpenPGP certificate:
|
The OpenPGP primary key is a distinct component key that serves a central role in an OpenPGP certificate:
|
||||||
|
|
||||||
- Its fingerprint acts as the unique identifier for the entire OpenPGP certificate.
|
- Its fingerprint is used as the unique identifier for the full OpenPGP certificate.
|
||||||
- It facilitates lifecycle operations, such as adding or invalidating subkeys or identities within a certificate.
|
- It is used for lifecycle operations, such as adding or invalidating subkeys or identities in a certificate.
|
||||||
|
|
||||||
```{admonition} Terminology
|
```{admonition} Terminology
|
||||||
:class: note
|
:class: note
|
||||||
|
@ -93,11 +92,11 @@ The OpenPGP primary key is a distinct component key that serves a central role i
|
||||||
In the RFC, the OpenPGP primary key is occasionally referred to as "top-level key." Informally, it has also been termed the "master key."
|
In the RFC, the OpenPGP primary key is occasionally referred to as "top-level key." Informally, it has also been termed the "master key."
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Subkeys
|
### Subkeys
|
||||||
|
|
||||||
In addition to the primary key, modern OpenPGP certificates usually contain several subkeys, although they are not technically required.
|
In addition to the primary key, modern OpenPGP certificates usually contain several subkeys, although they are not technically required.
|
||||||
|
|
||||||
Subkeys have the same structural attributes as the primary key but fulfill a different role. Subkeys are cryptographically linked with the primary key (elaborated below).
|
Subkeys have the same structure as the primary key, but they are used in a different role. Subkeys are cryptographically linked with the primary key (more on this below).
|
||||||
|
|
||||||
```{figure} diag/Subkeys.png
|
```{figure} diag/Subkeys.png
|
||||||
:name: Certificate with subkeys
|
:name: Certificate with subkeys
|
||||||
|
@ -134,100 +133,76 @@ It is considered good practice to have separate component keys for each type of
|
||||||
|
|
||||||
[^key-flag-sharing]: With ECC algorithms, it's actually not possible to share encryption functionality with the signing-based functionalities, e.g.: ed25519 used for signing; cv25519 used for encryption.
|
[^key-flag-sharing]: With ECC algorithms, it's actually not possible to share encryption functionality with the signing-based functionalities, e.g.: ed25519 used for signing; cv25519 used for encryption.
|
||||||
|
|
||||||
|
### Component key metadata, including key flags
|
||||||
|
|
||||||
### Identity components
|
The key flags for a component key are actually not defined *inside* that component key itself.
|
||||||
|
|
||||||
#### User IDs
|
Instead, key flags, together with other metadata about that component key (such as the key expiration time), are stored using mechanisms that join components together as an OpenPGP certificate:
|
||||||
|
|
||||||
|
- For the primary key, two different mechanisms can be used to define its key flags (as well as other metadata): That configuration can be associated with the [Primary User ID](primary_user_id), or via a [direct key signature](direct_key_signature).
|
||||||
|
- For subkeys, their key flags (and other metadata) are defined with the mechanism that connects the subkey with the certificate (via the primary key). More on that [below](binding_subkeys).
|
||||||
|
|
||||||
|
|
||||||
|
(identity_components)=
|
||||||
|
## Identity components
|
||||||
|
|
||||||
|
Identity components in an OpenPGP certificate are used by the certificate holder to state that they are known by a certain identifier (like a name, or an email address).
|
||||||
|
|
||||||
|
### User IDs
|
||||||
|
|
||||||
An OpenPGP certificate can contain any number of [User IDs](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-user-id-packet-tag-13). Each User ID associates the certificate with an identity.
|
An OpenPGP certificate can contain any number of [User IDs](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-user-id-packet-tag-13). Each User ID associates the certificate with an identity.
|
||||||
|
|
||||||
Often, identities in a User ID consist of a string that is composed of a name and an email address (this string must be UTF-8 encoded).
|
```{figure} diag/user_ids.png
|
||||||
|
|
||||||
```{figure} diag/user_id.png
|
|
||||||
|
|
||||||
OpenPGP certificates can contain any number of User IDs
|
OpenPGP certificates can contain any number of User IDs
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Primary User ID and its implications
|
Often, identities in a User ID consist of a UTF-8 encoded string that is composed of a name and an email address. By convention, User IDs typically consist of an [RFC2822](https://www.rfc-editor.org/rfc/rfc2822) *name-addr*.
|
||||||
|
|
||||||
|
Also see [draft-dkg-openpgp-userid-conventions-00](https://datatracker.ietf.org/doc/draft-dkg-openpgp-userid-conventions/), 25 August 2023.
|
||||||
|
|
||||||
|
One proposed variant for encoding identities in User ID is to use ["split User IDs"](https://dkg.fifthhorseman.net/blog/2021-dkg-openpgp-transition.html#split-user-ids).
|
||||||
|
|
||||||
|
(primary_user_id)=
|
||||||
|
### Primary User ID and its implications
|
||||||
|
|
||||||
One User ID in a certificate has the special property of being the [Primary User ID](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-primary-user-id).
|
One User ID in a certificate has the special property of being the [Primary User ID](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-primary-user-id).
|
||||||
|
|
||||||
User IDs are associated with preference settings (such as preferred encryption algorithms, more on this below). The preferences associated with the Primary User ID are used by default.
|
User IDs are associated with preference settings (such as preferred encryption algorithms, more on this in {numref}`zooming_in_user_id`). The preferences associated with the Primary User ID are used by default.
|
||||||
|
|
||||||
|
```{admonition} TODO
|
||||||
|
:class: warning
|
||||||
|
|
||||||
#### User attributes
|
i think crypto-refresh suggests that the direct key signature should hold the default preferences?
|
||||||
|
we might need to write a more nuanced text here, about how DKS and primary user id interact in v6, and mention the differences to v4?
|
||||||
|
```
|
||||||
|
|
||||||
|
### User attributes
|
||||||
|
|
||||||
[User attributes](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-user-attribute-packet-tag-1) are similar to User IDs, but less commonly used.
|
[User attributes](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-user-attribute-packet-tag-1) are similar to User IDs, but less commonly used.
|
||||||
|
|
||||||
The OpenPGP standard currently only defines one format to store in User Attributes: an [image](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-the-image-attribute-subpack), "presumably (but not required to be) that of the key owner".
|
The OpenPGP standard currently only defines one format to store in User Attributes: an [image](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-the-image-attribute-subpack), "presumably (but not required to be) that of the key owner".
|
||||||
|
|
||||||
### Linking the components of an OpenPGP certificate
|
## Linking the components
|
||||||
|
|
||||||
So far we've looked at the components in an OpenPGP certificate, but certificates actually contain another set of elements, which bind the components together, and add metadata to them.
|
To form an OpenPGP certificate out of a collection of components, the certificate holder links these components together (using their OpenPGP software).
|
||||||
|
|
||||||
Internally, an OpenPGP certificate consists of a sequence of OpenPGP packets. These packets are just stringed together, one after the other. When a certificate is stored in a file[^tpk], it's easy to remove some of these packets, or add new ones.
|
The OpenPGP term for linking components is "binding," as in: "a subkey is bound to the primary key." The bindings are realized using cryptographic signatures (much more details about this are in {ref}`certifications_chapter`).
|
||||||
|
|
||||||
[^tpk]: When stored in a file, OpenPGP certificates are in a format called [transferable public key](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-transferable-public-keys).
|
In very abstract terms, the primary key of a certificate acts as a root of trust for that certificate (as a kind of "certification authority"):
|
||||||
|
|
||||||
However, the owner of a certificate doesn't want a third party to add subkeys (or add identity claims) to their certificate, pretending that the certificate owner put those components there.
|
The primary key issues signatures that express the certificate holder's intent to use subkeys or identity components. It also performs other lifecycle operations, such as setting expiration times, or marking components as invalidated ("revoked").
|
||||||
|
|
||||||
To prevent malicious addition of components, OpenPGP uses cryptographic signatures. These signatures show that components have been added by the owner of the OpenPGP certificate (these linking signatures are issued by the primary key of the certificate).
|
Binding components together with digital signatures means that recipients of an OpenPGP certificate only need to verify that the primary key is the correct one to use for their communication partner (traditionally, this has often been done by manually verifying the *fingerprint* of the primary key). Once the validity of the primary key is established, the validity of all other components can be automatically determined by the user's OpenPGP software. To a first estimation, components are valid parts of a certificate if there is a statement signed with the certificate's primary key that expresses this validity.
|
||||||
|
|
||||||
So while anyone can still unilaterally store unrelated subkeys and identity claims in an OpenPGP certificate dataset, OpenPGP implementations that read this file should discard components that don't have a valid cryptographic connection with the certificate.
|
## Revocations
|
||||||
|
|
||||||
(Conversely, it's easy for a third party to leave out packets when passing on an OpenPGP certificate. An attacker can, for example, choose to omit revocation packets. The recipient of such a partial copy has no way to notice the omission, without access to a different source for the certificate that contains the revocation packet.)
|
```{admonition} TODO
|
||||||
|
:class: warning
|
||||||
|
|
||||||
Note, though, that there are some cases where third parties legitimately add "unbound" packets to certificates (that is: packets that are not signed by the certificate's owner):
|
This section needs to be written
|
||||||
|
|
||||||
- [Third-party certifications](third_party_cert) are traditionally added to the certificate that they make a statement about (this can cause problems in systems that unconditionally accept and include such certifications[^flooding]),
|
|
||||||
- OpenPGP software may add [unbound identity data](unbound_user_ids), locally.
|
|
||||||
|
|
||||||
[^flooding]: Storing third-party identity certifications in the target OpenPGP certificate is convenient for consumers: it is easy to find all relevant certifications in one central location. However, when third parties can unilaterally add certifications, this opens an avenue for denial-of-service attacks by flooding. The SKS network of OpenPGP key servers [allowed and experienced this problem](https://dkg.fifthhorseman.net/blog/openpgp-certificate-flooding.html).
|
|
||||||
|
|
||||||
#### Binding subkeys to an OpenPGP certificate
|
|
||||||
|
|
||||||
Linking a subkey to an OpenPGP certificate is done with a ["Subkey Binding Signature"](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#sigtype-subkey-binding). Such a signature signals that the "primary key wants to be associated with the subkey".
|
|
||||||
|
|
||||||
The subkey binding signature also adds metadata.
|
|
||||||
|
|
||||||
```{figure} diag/subkey_binding.png
|
|
||||||
|
|
||||||
Linking an OpenPGP subkey to the primary key with a binding signature
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The [Signature packet](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-signature-packet-tag-2) that binds the subkey to the primary key has the signature type [SubkeyBinding](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-subkey-binding-signature-si).
|
|
||||||
|
|
||||||
##### Binding signing subkeys to an OpenPGP certificate
|
|
||||||
|
|
||||||
Binding subkeys with the "signing" key flag is a special case:
|
|
||||||
|
|
||||||
When binding a signing subkey to a primary key, it is not sufficient that the "primary key wants to be associated with the subkey." In addition, the subkey must signal that it "wants to be associated with that primary key."
|
|
||||||
|
|
||||||
Otherwise, Alice could "adopt" Bob's signing subkey and convincingly claim that she made signatures that were in fact issued by Bob.
|
|
||||||
|
|
||||||
```{figure} diag/subkey_binding_backsig.png
|
|
||||||
|
|
||||||
Linking an OpenPGP signing subkey to the primary key with a binding signature, and an embedded primary key binding signature
|
|
||||||
```
|
|
||||||
|
|
||||||
This additional "Primary Key Binding" Signature is informally called a "back signature" (because the subkey uses the signature to point "back" to the primary key).
|
|
||||||
|
|
||||||
|
|
||||||
#### Binding identities with certifying self-signatures
|
|
||||||
|
|
||||||
"User ID" identity components are bound to an OpenPGP certificate by issuing a self-signature ("User Attributes" work analogously).
|
|
||||||
|
|
||||||
For example, the User ID `Alice Adams <alice@example.org>` may be associated with Alice's certificate `AAA1 8CBB 2546 85C5 8358 3205 63FD 37B6 7F33 00F9 FB0E C457 378C D29F 1026 98B3`.
|
|
||||||
|
|
||||||
Alice can link a User ID to her OpenPGP certificate with a cryptographic signature. To link a User ID, a self-signature is created (usually with the signature type [PositiveCertification](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#sigtype-positive-cert)). This signature is issued by the primary key.
|
|
||||||
|
|
||||||
```{figure} diag/user_id_certification.png
|
|
||||||
---
|
|
||||||
---
|
|
||||||
Linking a User ID to an OpenPGP certificate
|
|
||||||
```
|
|
||||||
|
|
||||||
(third_party_cert)=
|
|
||||||
## Third party (identity) certifications
|
## Third party (identity) certifications
|
||||||
|
|
||||||
```{admonition} TODO
|
```{admonition} TODO
|
||||||
|
@ -236,17 +211,10 @@ Linking a User ID to an OpenPGP certificate
|
||||||
This section needs writing
|
This section needs writing
|
||||||
```
|
```
|
||||||
|
|
||||||
## Revocations
|
|
||||||
|
|
||||||
```{admonition} TODO
|
In the past, the SKS keyserver network has accepted third party signatures and added them to certificates without any limitations. This has caused problems: anyone can add a large number of certifications to some certificates, which opens the door to a type of "vandalism", by growing certificates unreasonably, and making them annoying to use[^flooding].
|
||||||
:class: warning
|
|
||||||
|
|
||||||
This section only contains notes and still needs to be written
|
[^flooding]: Storing third-party identity certifications in the target OpenPGP certificate is convenient for consumers: it is easy to find all relevant certifications in one central location. However, when third parties can unilaterally add certifications, this opens an avenue for denial-of-service attacks by flooding. The SKS network of OpenPGP key servers [allowed and experienced this problem](https://dkg.fifthhorseman.net/blog/openpgp-certificate-flooding.html).
|
||||||
```
|
|
||||||
|
|
||||||
Note: certification signatures [can be made irrevocable](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-revocable).
|
|
||||||
|
|
||||||
### Hard vs. soft revocations
|
|
||||||
|
|
||||||
|
|
||||||
## Advanced topics
|
## Advanced topics
|
||||||
|
@ -257,10 +225,20 @@ Note: certification signatures [can be made irrevocable](https://www.ietf.org/ar
|
||||||
This section only contains notes and still needs to be written
|
This section only contains notes and still needs to be written
|
||||||
```
|
```
|
||||||
|
|
||||||
### Certificate Management / Evolution of a certificate over time
|
### Certificate management / Evolution of a certificate over time
|
||||||
|
|
||||||
Minimized versions, merging, effective "append only" semantics, ...
|
Minimized versions, merging, effective "append only" semantics, ...
|
||||||
|
|
||||||
|
### "Naming" a certificate in user-facing contexts - fingerprints and beyond
|
||||||
|
|
||||||
|
```{admonition} TODO
|
||||||
|
:class: warning
|
||||||
|
|
||||||
|
In v4, a 20 byte fingerprint in hex representation was used to name certificates, even in user-facing contexts.
|
||||||
|
|
||||||
|
For v6, this type of approach is discouraged, but a replacement mechanism is still pending.
|
||||||
|
```
|
||||||
|
|
||||||
### Merging
|
### Merging
|
||||||
|
|
||||||
- How to merge two copies of the same certificate?
|
- How to merge two copies of the same certificate?
|
||||||
|
@ -274,7 +252,7 @@ Minimized versions, merging, effective "append only" semantics, ...
|
||||||
- Subkey: Revoked/key expired/binding signature expired
|
- Subkey: Revoked/key expired/binding signature expired
|
||||||
- User ID: revoked, binding expired, ...
|
- User ID: revoked, binding expired, ...
|
||||||
|
|
||||||
### Best Practices regarding Key Freshness
|
### Best practices regarding Key Freshness
|
||||||
|
|
||||||
```{admonition} TODO
|
```{admonition} TODO
|
||||||
:class: warning
|
:class: warning
|
||||||
|
@ -285,547 +263,23 @@ Minimized versions, merging, effective "append only" semantics, ...
|
||||||
Wiktor suggests to check: https://blogs.gentoo.org/mgorny/2018/08/13/openpgp-key-expiration-is-not-a-security-measure/ for important material
|
Wiktor suggests to check: https://blogs.gentoo.org/mgorny/2018/08/13/openpgp-key-expiration-is-not-a-security-measure/ for important material
|
||||||
```
|
```
|
||||||
|
|
||||||
### Metadata Leak of Social Graph
|
### Metadata about the primary key: In Direct Key Signature vs. in Primary User ID, in v4 and v6
|
||||||
|
|
||||||
(unbound_user_ids)=
|
|
||||||
### Adding unbound User IDs to a certificate
|
|
||||||
|
|
||||||
Some OpenPGP subsystems 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" to certificates, in this way).
|
|
||||||
|
|
||||||
## Zooming in: Packet structure
|
|
||||||
|
|
||||||
Now that we've established the concepts of the components that OpenPGP certificates consist of, let's look at the internal details of our example certificate.
|
|
||||||
|
|
||||||
### A very minimal OpenPGP "Transferrable Secret Key"
|
|
||||||
|
|
||||||
We'll start with a very minimal version of [](alice_priv), stored as a "Transferrable Secret Key" (that is, including private key material).
|
|
||||||
|
|
||||||
In this section, we use the Sequoia-PGP tool `sq` to handle and transform our example OpenPGP key, and to inspect internal OpenPGP packet data.
|
|
||||||
|
|
||||||
One way to produce this minimal version of Alice's key is:
|
|
||||||
|
|
||||||
```text
|
|
||||||
$ sq packet split alice.priv
|
|
||||||
```
|
|
||||||
|
|
||||||
With this command, `sq` generates a set of files, one for each packet in `alice.priv`:
|
|
||||||
|
|
||||||
```text
|
|
||||||
alice.priv-0--SecretKey
|
|
||||||
alice.priv-1--Signature
|
|
||||||
alice.priv-2--UserID
|
|
||||||
alice.priv-3--Signature
|
|
||||||
alice.priv-4--SecretSubkey
|
|
||||||
alice.priv-5--Signature
|
|
||||||
alice.priv-6--SecretSubkey
|
|
||||||
alice.priv-7--Signature
|
|
||||||
alice.priv-8--SecretSubkey
|
|
||||||
alice.priv-9--Signature
|
|
||||||
```
|
|
||||||
|
|
||||||
For our first step, we'll use just the first two of these packets, and join them together as a private key:
|
|
||||||
|
|
||||||
```text
|
|
||||||
$ sq packet join alice.priv-0--SecretKey alice.priv-1--Signature --output alice_minimal.priv
|
|
||||||
```
|
|
||||||
|
|
||||||
This version of Alice's key contains just two packets:
|
|
||||||
|
|
||||||
- The [*"Secret-Key Packet"*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-secret-key-packet-formats) for the primary key, and
|
|
||||||
- A [*"Direct Key Signature"*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#sigtype-direct-key) (a self-signature that binds metadata to the primary key).
|
|
||||||
|
|
||||||
In the real world, you won't usually encounter an OpenPGP key that is quite this minimal. However, this is technically a valid OpenPGP key (and we'll add more components to it, later in this section).
|
|
||||||
|
|
||||||
In ASCII-armored representation, this very minimal key looks like this:
|
|
||||||
|
|
||||||
```text
|
|
||||||
-----BEGIN PGP PRIVATE KEY BLOCK-----
|
|
||||||
Comment: AAA1 8CBB 2546 85C5 8358 3205 63FD 37B6 7F33 00F9 FB0E C457 378C D29F 1026 98B3
|
|
||||||
|
|
||||||
xUsGZRbqphsAAAAgUyTpQ6+rFfdu1bUSmHlpzRtdEGXr50Liq0f0hrOuZT4A7+GZ
|
|
||||||
tV8R+6qT6CadO7ItciB9/71C3UvpozaBO6XMz/vCtgYfGwoAAAA9BYJlFuqmBYkF
|
|
||||||
pI+9AwsJBwMVCggCmwECHgEiIQaqoYy7JUaFxYNYMgVj/Te2fzMA+fsOxFc3jNKf
|
|
||||||
ECaYswAAAAoJEKqhjLslRoXFZ0cgouNjgeNr0E9W18g4gAIl6FM5SWuQxg12j0S0
|
|
||||||
7ExCOI5NPRDCrSnAV85mAXOzeIGeiVLPQ40oEal3CX/L+BXIoY2sIEQrLd4TAEEy
|
|
||||||
0BA8aQZTPEmMdiOCM1QB+V+BQZAO
|
|
||||||
=f0GN
|
|
||||||
-----END PGP PRIVATE KEY BLOCK-----
|
|
||||||
```
|
|
||||||
|
|
||||||
We'll now decode this OpenPGP data, and inspect the two packets in detail.
|
|
||||||
|
|
||||||
To generate the output, we run the Sequoia-PGP tool `sq`, using the `packet dump` subcommand. The output of `sq` is one block of text, but we'll break the output up into sections here, to discuss the content of each packet:
|
|
||||||
|
|
||||||
```text
|
|
||||||
$ sq packet dump --hex alice_minimal.priv
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Secret-Key Packet
|
|
||||||
|
|
||||||
The output starts with the (primary) [Secret-Key Packet](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-secret-key-packet-formats) (the file `alice.priv-0--SecretKey` contains this packet):
|
|
||||||
|
|
||||||
```text
|
|
||||||
Secret-Key Packet, new CTB, 2 header bytes + 75 bytes
|
|
||||||
Version: 6
|
|
||||||
Creation time: 2023-09-29 15:17:58 UTC
|
|
||||||
Pk algo: Ed25519
|
|
||||||
Pk size: 256 bits
|
|
||||||
Fingerprint: AAA18CBB254685C58358320563FD37B67F3300F9FB0EC457378CD29F102698B3
|
|
||||||
KeyID: AAA18CBB254685C5
|
|
||||||
|
|
||||||
Secret Key:
|
|
||||||
|
|
||||||
Unencrypted
|
|
||||||
|
|
||||||
00000000 c5 CTB
|
|
||||||
00000001 4b length
|
|
||||||
00000002 06 version
|
|
||||||
00000003 65 16 ea a6 creation_time
|
|
||||||
00000007 1b pk_algo
|
|
||||||
00000008 00 00 00 20 public_len
|
|
||||||
0000000c 53 24 e9 43 ed25519_public
|
|
||||||
00000010 af ab 15 f7 6e d5 b5 12 98 79 69 cd 1b 5d 10 65
|
|
||||||
00000020 eb e7 42 e2 ab 47 f4 86 b3 ae 65 3e
|
|
||||||
0000002c 00 s2k_usage
|
|
||||||
0000002d ef e1 99 ed25519_secret
|
|
||||||
00000030 b5 5f 11 fb aa 93 e8 26 9d 3b b2 2d 72 20 7d ff
|
|
||||||
00000040 bd 42 dd 4b e9 a3 36 81 3b a5 cc cf fb
|
|
||||||
```
|
|
||||||
|
|
||||||
The Secret-Key Packet consists in large part of the actual cryptographic key data. Let's look at the packet field by field:
|
|
||||||
|
|
||||||
- `CTB: 0xc5`[^CTB]: The [Packet Tag](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-packet-headers) for this packet. The binary representation of the value `0xc5` is `11000101`. Bits 7 and 6 show that the packet is in "OpenPGP packet format" (as opposed to in "Legacy packet format"). The remaining 6 bits encode the Tag's value: "5". This is the value for a Secret-Key Packet, as shown in the list of [Packet Tags](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-packet-tags).
|
|
||||||
- `length: 0x4b`: The remaining length of this packet.
|
|
||||||
|
|
||||||
The packet tag defines the semantics of the remaining data in the packet. We're looking at a Secret-Key Packet, which is a kind of [Key Material Packet](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-key-material-packets).
|
|
||||||
|
|
||||||
- `version: 0x06`: The key material is in version 6 format
|
|
||||||
|
|
||||||
This means that the next part of the packet follows the structure of [Version 6 Public Keys](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-version-6-public-keys)
|
|
||||||
|
|
||||||
- `creation_time: 0x6516eaa6`: "The time that the key was created" (also see [Time Fields](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-time-fields))
|
|
||||||
- `pk_algo: 0x1b`: "The public-key algorithm of this key" (decimal value 27, see the list of [Public-Key Algorithms](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-public-key-algorithms))
|
|
||||||
- `public_len: 0x00000020`: "Octet count for the following public key material" (in this case, the length of the following `ed25519_public` field)
|
|
||||||
- `ed25519_public`: [Algorithm-specific representation](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-algorithm-specific-part-for-ed2) of the public key material (the format is based on the value of `pk_algo`), in this case 32 bytes of the Ed25519 public key
|
|
||||||
|
|
||||||
This concludes the Public Key section of the packet. The remaining data follows the [Secret-Key Packet Format](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-secret-key-packet-formats):
|
|
||||||
|
|
||||||
- `s2k_usage: 0x00`: [This "S2K usage" value](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-secret-key-encryption-s2k-u) specifies that the secret-key data is not encrypted
|
|
||||||
- `ed25519_secret`: [Algorithm-specific representation](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-algorithm-specific-part-for-ed2) of the secret key data (the format is based on the value of `pk_algo`)
|
|
||||||
|
|
||||||
[^CTB]: Sequoia uses the term "CTB" (Cipher Type Byte) to refer to the RFC's ["Packet Tag"](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-packet-headers)
|
|
||||||
|
|
||||||
```{tip}
|
|
||||||
|
|
||||||
The overall structure of OpenPGP packets is described in the [Packet Syntax](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-packet-syntax) chapter of the RFC.
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that the "Secret-Key" Packet contains both the private and the public part of the key.
|
|
||||||
|
|
||||||
#### Direct Key Signature
|
|
||||||
|
|
||||||
The next packet is a [*"Direct Key Signature"*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#sigtype-direct-key), which is bound to the primary key (the file `alice.priv-1--Signature` contains this packet).
|
|
||||||
|
|
||||||
This packet "binds the information in the Signature subpackets to the key". Each entry under "Signature Packet -> Hashed area" is one Signature subpacket, for example, including information about algorithm preferences (*"Symmetric algo preferences"* and *"Hash preferences"*).
|
|
||||||
|
|
||||||
```text
|
|
||||||
Signature Packet, new CTB, 2 header bytes + 182 bytes
|
|
||||||
Version: 6
|
|
||||||
Type: DirectKey
|
|
||||||
Pk algo: Ed25519
|
|
||||||
Hash algo: SHA512
|
|
||||||
Hashed area:
|
|
||||||
Signature creation time: 2023-09-29 15:17:58 UTC (critical)
|
|
||||||
Key expiration time: P1095DT62781S (critical)
|
|
||||||
Symmetric algo preferences: AES256, AES128
|
|
||||||
Hash preferences: SHA512, SHA256
|
|
||||||
Key flags: C (critical)
|
|
||||||
Features: MDC
|
|
||||||
Issuer Fingerprint: AAA18CBB254685C58358320563FD37B67F3300F9FB0EC457378CD29F102698B3
|
|
||||||
Unhashed area:
|
|
||||||
Issuer: AAA18CBB254685C5
|
|
||||||
Digest prefix: 6747
|
|
||||||
Level: 0 (signature over data)
|
|
||||||
|
|
||||||
00000000 c2 CTB
|
|
||||||
00000001 b6 length
|
|
||||||
00000002 06 version
|
|
||||||
00000003 1f type
|
|
||||||
00000004 1b pk_algo
|
|
||||||
00000005 0a hash_algo
|
|
||||||
00000006 00 00 00 3d hashed_area_len
|
|
||||||
0000000a 05 subpacket length
|
|
||||||
0000000b 82 subpacket tag
|
|
||||||
0000000c 65 16 ea a6 sig creation time
|
|
||||||
00000010 05 subpacket length
|
|
||||||
00000011 89 subpacket tag
|
|
||||||
00000012 05 a4 8f bd key expiry time
|
|
||||||
00000016 03 subpacket length
|
|
||||||
00000017 0b subpacket tag
|
|
||||||
00000018 09 07 pref sym algos
|
|
||||||
0000001a 03 subpacket length
|
|
||||||
0000001b 15 subpacket tag
|
|
||||||
0000001c 0a 08 pref hash algos
|
|
||||||
0000001e 02 subpacket length
|
|
||||||
0000001f 9b subpacket tag
|
|
||||||
00000020 01 key flags
|
|
||||||
00000021 02 subpacket length
|
|
||||||
00000022 1e subpacket tag
|
|
||||||
00000023 01 features
|
|
||||||
00000024 22 subpacket length
|
|
||||||
00000025 21 subpacket tag
|
|
||||||
00000026 06 version
|
|
||||||
00000027 aa a1 8c bb 25 46 85 c5 83 issuer fp
|
|
||||||
00000030 58 32 05 63 fd 37 b6 7f 33 00 f9 fb 0e c4 57 37
|
|
||||||
00000040 8c d2 9f 10 26 98 b3
|
|
||||||
00000047 00 00 00 0a unhashed_area_len
|
|
||||||
0000004b 09 subpacket length
|
|
||||||
0000004c 10 subpacket tag
|
|
||||||
0000004d aa a1 8c issuer
|
|
||||||
00000050 bb 25 46 85 c5
|
|
||||||
00000055 67 digest_prefix1
|
|
||||||
00000056 47 digest_prefix2
|
|
||||||
00000057 20 salt_len
|
|
||||||
00000058 a2 e3 63 81 e3 6b d0 4f salt
|
|
||||||
00000060 56 d7 c8 38 80 02 25 e8 53 39 49 6b 90 c6 0d 76
|
|
||||||
00000070 8f 44 b4 ec 4c 42 38 8e
|
|
||||||
00000078 4d 3d 10 c2 ad 29 c0 57 ed25519_sig
|
|
||||||
00000080 ce 66 01 73 b3 78 81 9e 89 52 cf 43 8d 28 11 a9
|
|
||||||
00000090 77 09 7f cb f8 15 c8 a1 8d ac 20 44 2b 2d de 13
|
|
||||||
000000a0 00 41 32 d0 10 3c 69 06 53 3c 49 8c 76 23 82 33
|
|
||||||
000000b0 54 01 f9 5f 81 41 90 0e
|
|
||||||
```
|
|
||||||
|
|
||||||
Let’s look at the packet field by field:
|
|
||||||
|
|
||||||
- `CTB: 0xc2`: The Packet Tag for this packet. Bits 7 and 6 show that the packet is in “OpenPGP packet format” (as opposed to in “Legacy packet format”). The remaining 6 bits encode the Tag’s value: “2”. This is the value for a Signature Packet.
|
|
||||||
- `length: 0xb6`: The remaining length of this packet.
|
|
||||||
|
|
||||||
The packet tag defines the semantics of the remaining data in the packet. We're looking at a [Signature Packet](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#signature-packet), so the following data is interpreted accordingly.
|
|
||||||
|
|
||||||
- `version: 0x06`: This is a version 6 signature (some of the following packet format is specific to this signature version).
|
|
||||||
- `type: 0x1f`: The [Signature Type](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-signature-types)
|
|
||||||
- `pk_algo: 0x1b`: Public-key algorithm (decimal 27 corresponds, to [Ed25519](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-public-key-algorithms))
|
|
||||||
- `hash_algo: 0x0a`: Hash algorithm (decimal 10, corresponds to [SHA2-512](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-hash-algorithms))
|
|
||||||
- `hashed_area_len: 0x0000003d`: Length of the following hashed subpacket data
|
|
||||||
|
|
||||||
The next part of this packet contains "hashed subpacket data." A "subpacket data set" in an OpenPGP Signature contains a list of zero or more "Signature subpackets."
|
|
||||||
|
|
||||||
There are two sets of "subpacket data" in a Signature: "hashed," and "unhashed." The difference is that the hashed subpackets are protected by the digital signature of this packet, while the unhashed subpackets are not.
|
|
||||||
|
|
||||||
The following subpacket data consists of sets of "subpacket length, subpacket tag, data." We'll show the information for each subpacket as one line, starting with the [subpacket type description](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-signature-subpacket-specifi) (based on the subpacket tag). Note that bit 7 of the subpacket tag signals if that subpacket is ["critical"](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#section-5.2.3.7-10)[^critical].
|
|
||||||
|
|
||||||
[^critical]: "Critical" here means: the receiver must be able to interpret the subpacket and is expected to fail, otherwise. non-critical subpackets may be ignored by the receiver
|
|
||||||
|
|
||||||
- [Signature Creation Time](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#signature-creation-subpacket) (subpacket type 2) *critical*: `0x6516eaa6`
|
|
||||||
- [Key Expiration Time](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#key-expiration-subpacket) (subpacket type 9) *critical*: `0x05a48fbd`
|
|
||||||
- [Preferred Symmetric Ciphers for v1 SEIPD](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#preferred-v1-seipd) (type 11): `0x09 0x07`. (These values [correspond to](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#symmetric-algos): "AES with 256-bit key" and "AES with 128-bit key")
|
|
||||||
- [Preferred Hash Algorithms](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#preferred-hashes-subpacket) (subpacket type 21): `0x0a 0x08`. (These values [correspond to](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-hash-algorithms): "SHA2-512" and "SHA2-256")
|
|
||||||
- [Key Flags](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#key-flags) (subpacket type 27) *critical*: `0x01`. (This value [corresponds](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-key-flags) to the "certifications" key flag)
|
|
||||||
- [Features](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#features-subpacket) (subpacket type 30): `0x01`. (This value [corresponds](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-features) to: "Symmetrically Encrypted Integrity Protected Data packet version 1")
|
|
||||||
- [Issuer Fingerprint](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#issuer-fingerprint-subpacket) (subpacket type 33): `aaa18cbb254685c58358320563fd37b67f3300f9fb0ec457378cd29f102698b3` (this is the fingerprint of the component key that issued the signature in this packet. Not that here, the value is the primary key fingerprint of the certificate we're looking at.)
|
|
||||||
|
|
||||||
The next part of this packet contains "unhashed subpacket data":
|
|
||||||
|
|
||||||
- `unhashed_area_len: 0x0000000a`: Length of the following unhashed subpacket data.
|
|
||||||
|
|
||||||
As above, the following subpacket data consists of sets of "length, tag, data." In this case, only subpacket follows:
|
|
||||||
|
|
||||||
- [Issuer Key ID](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#issuer-keyid-subpacket) (subpacket type 16): `aaa18cbb254685c5` (this is the shortened version 6 "Key ID" of the fingerprint of this certificate's primary key)
|
|
||||||
|
|
||||||
This concludes the unhashed subpacket data.
|
|
||||||
|
|
||||||
- `digest_prefix: 0x6747`: "The left 16 bits of the signed hash value"
|
|
||||||
- `salt_len, salt`: A random [salt value](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-advantages-of-salted-signat) (the size must be [matching for the hash algorithm](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#hash-algorithms-registry))
|
|
||||||
- `ed25519_sig`: [Algorithm-specific](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-algorithm-specific-fields-for-ed2) representation of the signature (in this case: 64 bytes of Ed25519 signature)
|
|
||||||
|
|
||||||
```{figure} diag/key-minimal.png
|
|
||||||
:width: 40%
|
|
||||||
|
|
||||||
A minimal OpenPGP key, visualized
|
|
||||||
```
|
|
||||||
|
|
||||||
### Seen as an OpenPGP certificate
|
|
||||||
|
|
||||||
Let's now look at a "public key" view of the (very minimal) OpenPGP key above. That is, the same data, but without the private key material parts.
|
|
||||||
|
|
||||||
An OpenPGP user might give such a certificate to a communication partner, or upload it to a key server:
|
|
||||||
|
|
||||||
```text
|
|
||||||
$ sq key extract-cert alice_minimal.priv
|
|
||||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
|
||||||
Comment: AAA1 8CBB 2546 85C5 8358 3205 63FD 37B6 7F33 00F9 FB0E C457 378C D29F 1026 98B3
|
|
||||||
|
|
||||||
xioGZRbqphsAAAAgUyTpQ6+rFfdu1bUSmHlpzRtdEGXr50Liq0f0hrOuZT7CtgYf
|
|
||||||
GwoAAAA9BYJlFuqmBYkFpI+9AwsJBwMVCggCmwECHgEiIQaqoYy7JUaFxYNYMgVj
|
|
||||||
/Te2fzMA+fsOxFc3jNKfECaYswAAAAoJEKqhjLslRoXFZ0cgouNjgeNr0E9W18g4
|
|
||||||
gAIl6FM5SWuQxg12j0S07ExCOI5NPRDCrSnAV85mAXOzeIGeiVLPQ40oEal3CX/L
|
|
||||||
+BXIoY2sIEQrLd4TAEEy0BA8aQZTPEmMdiOCM1QB+V+BQZAO
|
|
||||||
=5nyq
|
|
||||||
-----END PGP PUBLIC KEY BLOCK-----
|
|
||||||
```
|
|
||||||
|
|
||||||
```text
|
|
||||||
$ sq packet dump --hex alice_minimal.pub
|
|
||||||
```
|
|
||||||
|
|
||||||
The output now starts with a (primary) [Public-Key Packet](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-public-key-packet-formats):
|
|
||||||
|
|
||||||
```text
|
|
||||||
Public-Key Packet, new CTB, 2 header bytes + 42 bytes
|
|
||||||
Version: 6
|
|
||||||
Creation time: 2023-09-29 15:17:58 UTC
|
|
||||||
Pk algo: Ed25519
|
|
||||||
Pk size: 256 bits
|
|
||||||
Fingerprint: AAA18CBB254685C58358320563FD37B67F3300F9FB0EC457378CD29F102698B3
|
|
||||||
KeyID: AAA18CBB254685C5
|
|
||||||
|
|
||||||
00000000 c6 CTB
|
|
||||||
00000001 2a length
|
|
||||||
00000002 06 version
|
|
||||||
00000003 65 16 ea a6 creation_time
|
|
||||||
00000007 1b pk_algo
|
|
||||||
00000008 00 00 00 20 public_len
|
|
||||||
0000000c 53 24 e9 43 ed25519_public
|
|
||||||
00000010 af ab 15 f7 6e d5 b5 12 98 79 69 cd 1b 5d 10 65
|
|
||||||
00000020 eb e7 42 e2 ab 47 f4 86 b3 ae 65 3e
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that the packet is almost identical to the Secret Key Packet seen above.
|
|
||||||
|
|
||||||
The packet tag (called `CTB` in the output) has changed to the packet type ["Public-Key Packet"](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-public-key-packet-tag-6) instead of "Secret-Key Packet."
|
|
||||||
|
|
||||||
The two packet types are very similar. Compared to the "Secret-Key Packet" packet shown above, this "Public-Key Packet" just leaves out the last section, which contained the private-key related fields `s2k_usage` and `ed25519_secret`.
|
|
||||||
|
|
||||||
The second packet in the certificate (the Direct Key Signature) is bit-for-bit identical as in the previous section. So we omit showing it again, here.
|
|
||||||
|
|
||||||
```{figure} diag/pubcert-minimal.png
|
|
||||||
:width: 40%
|
|
||||||
|
|
||||||
A minimal OpenPGP public certificate, visualized
|
|
||||||
```
|
|
||||||
|
|
||||||
In the following examples, we will only look at OpenPGP keys that include the private key material. The corresponding "certificate" variants, which only contain the public key material, are easy to imagine: like here, they just leave out the private key material.
|
|
||||||
|
|
||||||
### Subkeys
|
|
||||||
|
|
||||||
```{admonition} TODO
|
```{admonition} TODO
|
||||||
:class: warning
|
:class: warning
|
||||||
|
|
||||||
The following text is unfinished and still needs processing/writing.
|
write
|
||||||
|
|
||||||
**This point marks the end of the material that should be read/edited.**
|
|
||||||
```
|
```
|
||||||
|
|
||||||
From here on, we'll look at the dumps in shorter format (you can see more detail by copying the certificates into the ["Sequoia OpenPGP Packet dumper"](https://dump.sequoia-pgp.org/) and checking the "HexDump" checkbox).
|
### Metadata leak of Social Graph
|
||||||
|
|
||||||
### User IDs
|
(unbound_user_ids)=
|
||||||
|
### Adding unbound User IDs to a certificate
|
||||||
|
|
||||||
User IDs are a mechanism for attaching *identities* to an OpenPGP certificate. Typically, a User ID will contain a name and an email address.
|
```{admonition} TODO
|
||||||
|
:class: warning
|
||||||
|
|
||||||
To look into these, we'll make a certificate that has one [User ID](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#uid). User IDs are *"intended to represent the name and email address of the key holder"*. A certificate can have multiple User IDs associated with it.
|
references/links missing
|
||||||
|
|
||||||
Let's look into the details of this key:
|
|
||||||
|
|
||||||
```text
|
|
||||||
-----BEGIN PGP PRIVATE KEY BLOCK-----
|
|
||||||
Comment: AAA1 8CBB 2546 85C5 8358 3205 63FD 37B6 7F33 00F9 FB0E C457 378C D29F 1026 98B3
|
|
||||||
Comment: <alice@example.org>
|
|
||||||
|
|
||||||
xUsGZRbqphsAAAAgUyTpQ6+rFfdu1bUSmHlpzRtdEGXr50Liq0f0hrOuZT4A7+GZ
|
|
||||||
tV8R+6qT6CadO7ItciB9/71C3UvpozaBO6XMz/vCtgYfGwoAAAA9BYJlFuqmBYkF
|
|
||||||
pI+9AwsJBwMVCggCmwECHgEiIQaqoYy7JUaFxYNYMgVj/Te2fzMA+fsOxFc3jNKf
|
|
||||||
ECaYswAAAAoJEKqhjLslRoXFZ0cgouNjgeNr0E9W18g4gAIl6FM5SWuQxg12j0S0
|
|
||||||
7ExCOI5NPRDCrSnAV85mAXOzeIGeiVLPQ40oEal3CX/L+BXIoY2sIEQrLd4TAEEy
|
|
||||||
0BA8aQZTPEmMdiOCM1QB+V+BQZAOzRM8YWxpY2VAZXhhbXBsZS5vcmc+wrkGExsK
|
|
||||||
AAAAQAWCZRbqpgWJBaSPvQMLCQcDFQoIApkBApsBAh4BIiEGqqGMuyVGhcWDWDIF
|
|
||||||
Y/03tn8zAPn7DsRXN4zSnxAmmLMAAAAKCRCqoYy7JUaFxdu4IIotb9pnNbxdBHe0
|
|
||||||
nWeobsXWiFNf4u/5Zgi/wuDbwFYN69QspRkBD7om0IKiz1zreqly2fOyZgeLsro9
|
|
||||||
t4nkdgRuNSQrJymDvpGceGrMtNVpR3YsKdZUv0MZBP9TmMDVCw==
|
|
||||||
=bgQM
|
|
||||||
-----END PGP PRIVATE KEY BLOCK-----
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```text
|
Some OpenPGP subsystems 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" to certificates, in this way).
|
||||||
$ sq packet dump --hex alice_userid.priv
|
|
||||||
Secret-Key Packet, new CTB, 2 header bytes + 75 bytes
|
|
||||||
Version: 6
|
|
||||||
Creation time: 2023-09-29 15:17:58 UTC
|
|
||||||
Pk algo: Ed25519
|
|
||||||
Pk size: 256 bits
|
|
||||||
Fingerprint: AAA18CBB254685C58358320563FD37B67F3300F9FB0EC457378CD29F102698B3
|
|
||||||
KeyID: AAA18CBB254685C5
|
|
||||||
|
|
||||||
Secret Key:
|
|
||||||
|
|
||||||
Unencrypted
|
|
||||||
|
|
||||||
00000000 c5 CTB
|
|
||||||
00000001 4b length
|
|
||||||
00000002 06 version
|
|
||||||
00000003 65 16 ea a6 creation_time
|
|
||||||
00000007 1b pk_algo
|
|
||||||
00000008 00 00 00 20 public_len
|
|
||||||
0000000c 53 24 e9 43 ed25519_public
|
|
||||||
00000010 af ab 15 f7 6e d5 b5 12 98 79 69 cd 1b 5d 10 65
|
|
||||||
00000020 eb e7 42 e2 ab 47 f4 86 b3 ae 65 3e
|
|
||||||
0000002c 00 s2k_usage
|
|
||||||
0000002d ef e1 99 ed25519_secret
|
|
||||||
00000030 b5 5f 11 fb aa 93 e8 26 9d 3b b2 2d 72 20 7d ff
|
|
||||||
00000040 bd 42 dd 4b e9 a3 36 81 3b a5 cc cf fb
|
|
||||||
|
|
||||||
Signature Packet, new CTB, 2 header bytes + 182 bytes
|
|
||||||
Version: 6
|
|
||||||
Type: DirectKey
|
|
||||||
Pk algo: Ed25519
|
|
||||||
Hash algo: SHA512
|
|
||||||
Hashed area:
|
|
||||||
Signature creation time: 2023-09-29 15:17:58 UTC (critical)
|
|
||||||
Key expiration time: P1095DT62781S (critical)
|
|
||||||
Symmetric algo preferences: AES256, AES128
|
|
||||||
Hash preferences: SHA512, SHA256
|
|
||||||
Key flags: C (critical)
|
|
||||||
Features: MDC
|
|
||||||
Issuer Fingerprint: AAA18CBB254685C58358320563FD37B67F3300F9FB0EC457378CD29F102698B3
|
|
||||||
Unhashed area:
|
|
||||||
Issuer: AAA18CBB254685C5
|
|
||||||
Digest prefix: 6747
|
|
||||||
Level: 0 (signature over data)
|
|
||||||
|
|
||||||
00000000 c2 CTB
|
|
||||||
00000001 b6 length
|
|
||||||
00000002 06 version
|
|
||||||
00000003 1f type
|
|
||||||
00000004 1b pk_algo
|
|
||||||
00000005 0a hash_algo
|
|
||||||
00000006 00 00 00 3d hashed_area_len
|
|
||||||
0000000a 05 subpacket length
|
|
||||||
0000000b 82 subpacket tag
|
|
||||||
0000000c 65 16 ea a6 sig creation time
|
|
||||||
00000010 05 subpacket length
|
|
||||||
00000011 89 subpacket tag
|
|
||||||
00000012 05 a4 8f bd key expiry time
|
|
||||||
00000016 03 subpacket length
|
|
||||||
00000017 0b subpacket tag
|
|
||||||
00000018 09 07 pref sym algos
|
|
||||||
0000001a 03 subpacket length
|
|
||||||
0000001b 15 subpacket tag
|
|
||||||
0000001c 0a 08 pref hash algos
|
|
||||||
0000001e 02 subpacket length
|
|
||||||
0000001f 9b subpacket tag
|
|
||||||
00000020 01 key flags
|
|
||||||
00000021 02 subpacket length
|
|
||||||
00000022 1e subpacket tag
|
|
||||||
00000023 01 features
|
|
||||||
00000024 22 subpacket length
|
|
||||||
00000025 21 subpacket tag
|
|
||||||
00000026 06 version
|
|
||||||
00000027 aa a1 8c bb 25 46 85 c5 83 issuer fp
|
|
||||||
00000030 58 32 05 63 fd 37 b6 7f 33 00 f9 fb 0e c4 57 37
|
|
||||||
00000040 8c d2 9f 10 26 98 b3
|
|
||||||
00000047 00 00 00 0a unhashed_area_len
|
|
||||||
0000004b 09 subpacket length
|
|
||||||
0000004c 10 subpacket tag
|
|
||||||
0000004d aa a1 8c issuer
|
|
||||||
00000050 bb 25 46 85 c5
|
|
||||||
00000055 67 digest_prefix1
|
|
||||||
00000056 47 digest_prefix2
|
|
||||||
00000057 20 salt_len
|
|
||||||
00000058 a2 e3 63 81 e3 6b d0 4f salt
|
|
||||||
00000060 56 d7 c8 38 80 02 25 e8 53 39 49 6b 90 c6 0d 76
|
|
||||||
00000070 8f 44 b4 ec 4c 42 38 8e
|
|
||||||
00000078 4d 3d 10 c2 ad 29 c0 57 ed25519_sig
|
|
||||||
00000080 ce 66 01 73 b3 78 81 9e 89 52 cf 43 8d 28 11 a9
|
|
||||||
00000090 77 09 7f cb f8 15 c8 a1 8d ac 20 44 2b 2d de 13
|
|
||||||
000000a0 00 41 32 d0 10 3c 69 06 53 3c 49 8c 76 23 82 33
|
|
||||||
000000b0 54 01 f9 5f 81 41 90 0e
|
|
||||||
|
|
||||||
User ID Packet, new CTB, 2 header bytes + 19 bytes
|
|
||||||
Value: <alice@example.org>
|
|
||||||
|
|
||||||
00000000 cd CTB
|
|
||||||
00000001 13 length
|
|
||||||
00000002 3c 61 6c 69 63 65 40 65 78 61 6d 70 6c 65 value
|
|
||||||
00000010 2e 6f 72 67 3e
|
|
||||||
|
|
||||||
Signature Packet, new CTB, 2 header bytes + 185 bytes
|
|
||||||
Version: 6
|
|
||||||
Type: PositiveCertification
|
|
||||||
Pk algo: Ed25519
|
|
||||||
Hash algo: SHA512
|
|
||||||
Hashed area:
|
|
||||||
Signature creation time: 2023-09-29 15:17:58 UTC (critical)
|
|
||||||
Key expiration time: P1095DT62781S (critical)
|
|
||||||
Symmetric algo preferences: AES256, AES128
|
|
||||||
Hash preferences: SHA512, SHA256
|
|
||||||
Primary User ID: true (critical)
|
|
||||||
Key flags: C (critical)
|
|
||||||
Features: MDC
|
|
||||||
Issuer Fingerprint: AAA18CBB254685C58358320563FD37B67F3300F9FB0EC457378CD29F102698B3
|
|
||||||
Unhashed area:
|
|
||||||
Issuer: AAA18CBB254685C5
|
|
||||||
Digest prefix: DBB8
|
|
||||||
Level: 0 (signature over data)
|
|
||||||
|
|
||||||
00000000 c2 CTB
|
|
||||||
00000001 b9 length
|
|
||||||
00000002 06 version
|
|
||||||
00000003 13 type
|
|
||||||
00000004 1b pk_algo
|
|
||||||
00000005 0a hash_algo
|
|
||||||
00000006 00 00 00 40 hashed_area_len
|
|
||||||
0000000a 05 subpacket length
|
|
||||||
0000000b 82 subpacket tag
|
|
||||||
0000000c 65 16 ea a6 sig creation time
|
|
||||||
00000010 05 subpacket length
|
|
||||||
00000011 89 subpacket tag
|
|
||||||
00000012 05 a4 8f bd key expiry time
|
|
||||||
00000016 03 subpacket length
|
|
||||||
00000017 0b subpacket tag
|
|
||||||
00000018 09 07 pref sym algos
|
|
||||||
0000001a 03 subpacket length
|
|
||||||
0000001b 15 subpacket tag
|
|
||||||
0000001c 0a 08 pref hash algos
|
|
||||||
0000001e 02 subpacket length
|
|
||||||
0000001f 99 subpacket tag
|
|
||||||
00000020 01 primary user id
|
|
||||||
00000021 02 subpacket length
|
|
||||||
00000022 9b subpacket tag
|
|
||||||
00000023 01 key flags
|
|
||||||
00000024 02 subpacket length
|
|
||||||
00000025 1e subpacket tag
|
|
||||||
00000026 01 features
|
|
||||||
00000027 22 subpacket length
|
|
||||||
00000028 21 subpacket tag
|
|
||||||
00000029 06 version
|
|
||||||
0000002a aa a1 8c bb 25 46 issuer fp
|
|
||||||
00000030 85 c5 83 58 32 05 63 fd 37 b6 7f 33 00 f9 fb 0e
|
|
||||||
00000040 c4 57 37 8c d2 9f 10 26 98 b3
|
|
||||||
0000004a 00 00 00 0a unhashed_area_len
|
|
||||||
0000004e 09 subpacket length
|
|
||||||
0000004f 10 subpacket tag
|
|
||||||
00000050 aa a1 8c bb 25 46 85 c5 issuer
|
|
||||||
00000058 db digest_prefix1
|
|
||||||
00000059 b8 digest_prefix2
|
|
||||||
0000005a 20 salt_len
|
|
||||||
0000005b 8a 2d 6f da 67 salt
|
|
||||||
00000060 35 bc 5d 04 77 b4 9d 67 a8 6e c5 d6 88 53 5f e2
|
|
||||||
00000070 ef f9 66 08 bf c2 e0 db c0 56 0d
|
|
||||||
0000007b eb d4 2c a5 19 ed25519_sig
|
|
||||||
00000080 01 0f ba 26 d0 82 a2 cf 5c eb 7a a9 72 d9 f3 b2
|
|
||||||
00000090 66 07 8b b2 ba 3d b7 89 e4 76 04 6e 35 24 2b 27
|
|
||||||
000000a0 29 83 be 91 9c 78 6a cc b4 d5 69 47 76 2c 29 d6
|
|
||||||
000000b0 54 bf 43 19 04 ff 53 98 c0 d5 0b
|
|
||||||
```
|
|
||||||
|
|
||||||
Instead of two packets, as before, we see four packets in this certificate:
|
|
||||||
|
|
||||||
* First, a "Secret-Key Packet,"
|
|
||||||
* then a "Signature Packet" (these two packets are the same as above).
|
|
||||||
* Third, a [*"User ID Packet"*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#uid), which contains the name and email address we used
|
|
||||||
* Finally, a [*"Positive Certification Signature"*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#sigtype-positive-cert) (type 0x13), *"Positive certification of a User ID and Public-Key packet"*. This is a cryptographic artifact that "binds the User ID packet and the Key packet together", i.e. it certifies that the owner of the key wants this User ID associated with their key. (Only the person who controls the private part of this key can create this signature packet. The signature serves as proof that the owner of the key has added this User ID to the certificate)
|
|
||||||
|
|
||||||
|
|
||||||
### Certifications (Third Party Signatures)
|
|
||||||
|
|
||||||
### Revocations
|
|
||||||
|
|
|
@ -1,15 +1,69 @@
|
||||||
(private_key_chapter)=
|
(private_key_chapter)=
|
||||||
# Private keys
|
# OpenPGP private keys
|
||||||
|
|
||||||
```{admonition} TODO
|
Historically, terminology around OpenPGP certificates and keys has often been used inconsistently. The pair of terms "OpenPGP public key" and "OpenPGP private/secret keys" were commonly used (while the shorthand "OpenPGP key" can refer to both, depending on context).
|
||||||
|
|
||||||
|
## Terms
|
||||||
|
|
||||||
|
In this document, we use the term *OpenPGP certificate* to refer "OpenPGP public keys": The combination of component public keys, identity components and bindings.
|
||||||
|
|
||||||
|
This chapter is about the counterpart to the public material in certificates: Here, we discuss the handling of *private key material* in OpenPGP.
|
||||||
|
|
||||||
|
In this text, we treat the private key material as logically separate from the OpenPGP certificate. Operations that use the private key material are typically handled by a separate subsystem. We think it is useful to think about OpenPGP certificates on one hand, and the associated private key material, on the other, as two related elements, which are usually handled separately[^pkcs11]:
|
||||||
|
|
||||||
|
```{admonition} VISUAL
|
||||||
:class: warning
|
:class: warning
|
||||||
|
|
||||||
- Consistently consider private key material as a separate thing from Certificates? (like in pkcs#11?)
|
- OpenPGP certificate side-by-side with the associated, loose private key material
|
||||||
```
|
```
|
||||||
|
|
||||||
|
[^pkcs11]: This kind of distinction between certificates (which combine public key material and identity information) on the one hand, and private key material on the other, is also applied in the data model of [PKCS #11](https://en.wikipedia.org/wiki/PKCS_11) cryptographic systems.
|
||||||
|
|
||||||
|
However, there is one exception. "OpenPGP private keys" are sometimes handled in a format that combines the certificate and the private key data: [*Transferable secret keys (TSK)*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-11.html#name-transferable-secret-keys).
|
||||||
|
|
||||||
## Transferable secret keys
|
## Transferable secret keys
|
||||||
|
|
||||||
https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-transferable-secret-keys
|
Sometimes users handle "OpenPGP private keys" in the form of *transferable secret keys* (TSK). That is: a serialized format that combines the OpenPGP certificate data with the connected private key material, stored in a single file.
|
||||||
|
|
||||||
|
```{admonition} VISUAL
|
||||||
|
:class: warning
|
||||||
|
|
||||||
|
- OpenPGP certificate with integrated private key material, as TSK
|
||||||
|
```
|
||||||
|
|
||||||
|
The TSK format can be useful for backups of OpenPGP key material, or to move a key to a different computer[^gpg-tsk].
|
||||||
|
|
||||||
|
[^gpg-tsk]: For example, with GnuPG, an OpenPGP key can be exported in (armored) TSK format like this: `gpg --export-secret-key --armor <fingerprint>`
|
||||||
|
|
||||||
|
(encrypted_secrets)=
|
||||||
|
## Protecting secret key material with a passphrase (using S2K)
|
||||||
|
|
||||||
|
In OpenPGP format, private key material can be optionally protected with a [passphrase](https://en.wikipedia.org/wiki/Passphrase). This mechanism applies symmetric encryption to the private key data in component keys.
|
||||||
|
|
||||||
|
The symmetric encryption key is derived from a secret that the user knows (the passphrase).
|
||||||
|
|
||||||
|
Using a passphrase can be useful when a third party can obtain a copy of the OpenPGP key data, but doesn't know the passphrase. In this scenario, an attacker may have obtained a copy of an OpenPGP key, but is unable to use it, because the private key material is encrypted, and the attacker cannot decrypt it.
|
||||||
|
|
||||||
|
OpenPGP defines a mechanism called [string-to-key (S2K)](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-11.html#name-string-to-key-s2k-specifier) that is used to derive (high-entropy) symmetric encryption keys from (lower-entropy) passphrases, using a [key derivation function (KDF)](https://en.wikipedia.org/wiki/Key_derivation_function).
|
||||||
|
|
||||||
|
```{admonition} VISUAL
|
||||||
|
:class: warning
|
||||||
|
|
||||||
|
- passphrase --(S2k mechanism)--> symmetric encryption key
|
||||||
|
```
|
||||||
|
|
||||||
|
Encryption of private key material can be configured independently for each component key. Component keys that are associated with the same certificate can use different mechanisms for passphrase protection, and/or different passphrases.
|
||||||
|
|
||||||
|
### S2K mechanisms for symmetric key generation
|
||||||
|
|
||||||
|
Over time, OpenPGP has specified a series of [S2K mechanisms](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-11.html#name-string-to-key-s2k-types-reg), following the current state of the art. Of the specified S2K mechanisms, two remain relevant today:
|
||||||
|
|
||||||
|
- [Iterated and Salted S2K](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-11.html#name-iterated-and-salted-s2k), which OpenPGP version 4 implementations can handle
|
||||||
|
- [Argon2](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-11.html#name-argon2), which was newly added in OpenPGP version 6, and additionally protects the passphrase against brute-force attacks because it is memory-hard (which reduces the efficiency of attacks with specialised hardware)
|
||||||
|
|
||||||
|
### Mechanisms for secret key encryption with S2K
|
||||||
|
|
||||||
|
Different mechanisms are specified [for the encryption of the secret key data](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-secret-key-encryption).
|
||||||
|
|
||||||
|
|
||||||
## Private key operations
|
## Private key operations
|
||||||
|
|
719
book/source/17-zoom_certificates.md
Normal file
|
@ -0,0 +1,719 @@
|
||||||
|
# Zooming in: Packet structure of certificates and keys
|
||||||
|
|
||||||
|
Now that we've established these concepts, and the components that OpenPGP certificates consist of, let's look at the internal details of an example certificate.
|
||||||
|
|
||||||
|
## A very minimal OpenPGP certificate
|
||||||
|
|
||||||
|
First, we'll look at a very minimal version of a "public key" variant of [](alice_priv). That is, an OpenPGP certificate (which doesn't contain private key material).
|
||||||
|
|
||||||
|
In this section, we use the Sequoia-PGP tool `sq` to handle and transform our example OpenPGP key, and to inspect internal OpenPGP packet data.
|
||||||
|
|
||||||
|
Starting from [Alice's OpenPGP "private key"](alice_priv), we first produce the corresponding "public key", or certificate:
|
||||||
|
|
||||||
|
```text
|
||||||
|
$ sq key extract-cert alice.priv > alice.pub
|
||||||
|
```
|
||||||
|
|
||||||
|
(split_alice)=
|
||||||
|
### Splitting the OpenPGP certificate into packets
|
||||||
|
|
||||||
|
One way to produce a very minimal version of Alice's certificate is to split the data in `alice.pub` into its component packets, and join only the relevant ones back together into a new variant.
|
||||||
|
|
||||||
|
```text
|
||||||
|
$ sq packet split alice.pub
|
||||||
|
```
|
||||||
|
|
||||||
|
With this command, `sq` generates a set of files, each containing an individual OpenPGP packet of the original full certificate in `alice.pub`:
|
||||||
|
|
||||||
|
```text
|
||||||
|
alice.pub-0--PublicKey
|
||||||
|
alice.pub-1--Signature
|
||||||
|
alice.pub-2--UserID
|
||||||
|
alice.pub-3--Signature
|
||||||
|
alice.pub-4--PublicSubkey
|
||||||
|
alice.pub-5--Signature
|
||||||
|
alice.pub-6--PublicSubkey
|
||||||
|
alice.pub-7--Signature
|
||||||
|
alice.pub-8--PublicSubkey
|
||||||
|
alice.pub-9--Signature
|
||||||
|
```
|
||||||
|
|
||||||
|
```{admonition} VISUAL
|
||||||
|
:class: warning
|
||||||
|
|
||||||
|
Show a very abstract diagram of the packets of Alice's OpenPGP certificate (above):
|
||||||
|
- Public-Key packet
|
||||||
|
- Direct Key Signature
|
||||||
|
- User ID
|
||||||
|
- Certifying self-signature for User ID
|
||||||
|
- Public-Subkey packet
|
||||||
|
- Subkey binding signature
|
||||||
|
- Public-Subkey packet
|
||||||
|
- Subkey binding signature
|
||||||
|
- Public-Subkey packet
|
||||||
|
- Subkey binding signature
|
||||||
|
```
|
||||||
|
|
||||||
|
### Joining packets into an OpenPGP certificate
|
||||||
|
|
||||||
|
For our first step, we'll use just the first two of the packets of Alice's certificate, and join them together as a very minimal certificate:
|
||||||
|
|
||||||
|
```text
|
||||||
|
$ sq packet join alice.pub-0--PublicKey alice.pub-1--Signature --output alice_minimal.pub
|
||||||
|
```
|
||||||
|
|
||||||
|
### Inspecting this certificate
|
||||||
|
|
||||||
|
This version of Alice's certificate contains just two packets:
|
||||||
|
|
||||||
|
- The [*Public-Key packet*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-public-key-packet-formats) for the primary key, and
|
||||||
|
- A [*Direct Key Signature*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#sigtype-direct-key) (a self-signature that binds metadata to the primary key).
|
||||||
|
|
||||||
|
This is the shape of the packets we'll be looking at, in the following two sections:
|
||||||
|
|
||||||
|
```{figure} diag/pubcert-minimal.png
|
||||||
|
:width: 40%
|
||||||
|
|
||||||
|
A minimal OpenPGP certificate, visualized
|
||||||
|
```
|
||||||
|
|
||||||
|
```{admonition} VISUAL
|
||||||
|
:class: warning
|
||||||
|
|
||||||
|
This diagram needs adjustments about
|
||||||
|
- what exactly is signed
|
||||||
|
- fix naming of fields?
|
||||||
|
|
||||||
|
We could show repeat-copies of the individual packet visualization again, below for each packet-related section.
|
||||||
|
```
|
||||||
|
|
||||||
|
In the real world, you won't usually encounter an OpenPGP certificate that is quite this minimal. However, this is technically a valid OpenPGP certificate (and we'll add more components to it, later in this section).
|
||||||
|
|
||||||
|
In ASCII-armored representation, this very minimal key looks like this:
|
||||||
|
|
||||||
|
```text
|
||||||
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
||||||
|
xioGZRbqphsAAAAgUyTpQ6+rFfdu1bUSmHlpzRtdEGXr50Liq0f0hrOuZT7CtgYf
|
||||||
|
GwoAAAA9BYJlFuqmBYkFpI+9AwsJBwMVCggCmwECHgEiIQaqoYy7JUaFxYNYMgVj
|
||||||
|
/Te2fzMA+fsOxFc3jNKfECaYswAAAAoJEKqhjLslRoXFZ0cgouNjgeNr0E9W18g4
|
||||||
|
gAIl6FM5SWuQxg12j0S07ExCOI5NPRDCrSnAV85mAXOzeIGeiVLPQ40oEal3CX/L
|
||||||
|
+BXIoY2sIEQrLd4TAEEy0BA8aQZTPEmMdiOCM1QB+V+BQZAO
|
||||||
|
=5nyq
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
|
```
|
||||||
|
|
||||||
|
We'll now decode this OpenPGP data, and inspect the two packets in detail.
|
||||||
|
|
||||||
|
To inspect the internal structure of the OpenPGP data, we run the Sequoia-PGP tool `sq`, using the `packet dump` subcommand. The output of `sq` is one block of text, but to discuss the content of each packet we'll break the output up into sections here:
|
||||||
|
|
||||||
|
```text
|
||||||
|
$ sq packet dump --hex alice_minimal.pub
|
||||||
|
```
|
||||||
|
|
||||||
|
(public_key)=
|
||||||
|
### Public-Key packet
|
||||||
|
|
||||||
|
The output now starts with a (primary) [Public-Key packet](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-public-key-packet-formats):
|
||||||
|
|
||||||
|
```text
|
||||||
|
Public-Key Packet, new CTB, 2 header bytes + 42 bytes
|
||||||
|
Version: 6
|
||||||
|
Creation time: 2023-09-29 15:17:58 UTC
|
||||||
|
Pk algo: Ed25519
|
||||||
|
Pk size: 256 bits
|
||||||
|
Fingerprint: AAA18CBB254685C58358320563FD37B67F3300F9FB0EC457378CD29F102698B3
|
||||||
|
KeyID: AAA18CBB254685C5
|
||||||
|
|
||||||
|
00000000 c6 CTB
|
||||||
|
00000001 2a length
|
||||||
|
00000002 06 version
|
||||||
|
00000003 65 16 ea a6 creation_time
|
||||||
|
00000007 1b pk_algo
|
||||||
|
00000008 00 00 00 20 public_len
|
||||||
|
0000000c 53 24 e9 43 ed25519_public
|
||||||
|
00000010 af ab 15 f7 6e d5 b5 12 98 79 69 cd 1b 5d 10 65
|
||||||
|
00000020 eb e7 42 e2 ab 47 f4 86 b3 ae 65 3e
|
||||||
|
```
|
||||||
|
|
||||||
|
The Public-Key packet consists in large part of the actual cryptographic key data. Let's look at the packet field by field:
|
||||||
|
|
||||||
|
- `CTB: 0xc6`[^CTB]: The [packet type ID](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-packet-headers) for this packet. The binary representation of the value `0xc6` is `11000110`. Bits 7 and 6 show that the packet is in *OpenPGP packet format* (as opposed to in *Legacy packet format*). The remaining 6 bits encode the type ID's value: "6". This is the value for a Public-Key packet, as shown in the list of [packet type IDs](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-packet-tags).
|
||||||
|
- `length: 0x2a`: The remaining length of this packet.
|
||||||
|
|
||||||
|
The packet type id defines the semantics of the remaining data in the packet. We're looking at a Public-Key packet, which is a kind of [Key Material Packet](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-key-material-packets).
|
||||||
|
|
||||||
|
- `version: 0x06`: The key material is in version 6 format
|
||||||
|
|
||||||
|
This means that the next part of the packet follows the structure of [Version 6 Public Keys](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-version-6-public-keys)
|
||||||
|
|
||||||
|
- `creation_time: 0x6516eaa6`: "The time that the key was created" (also see [Time Fields](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-time-fields))
|
||||||
|
- `pk_algo: 0x1b`: "The public-key algorithm ID of this key" (decimal value 27, see the list of [Public-Key Algorithms](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-public-key-algorithms))
|
||||||
|
- `public_len: 0x00000020`: "Octet count for the following public key material" (in this case, the length of the following `ed25519_public` field)
|
||||||
|
- `ed25519_public`: [Algorithm-specific representation](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-algorithm-specific-part-for-ed2) of the public key material (the format is based on the value of `pk_algo`), in this case 32 bytes of Ed25519 public key
|
||||||
|
|
||||||
|
[^CTB]: Sequoia uses the term CTB (Cipher Type Byte) to refer to the RFC's [packet type ID](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-packet-headers). In previous versions, the RFC called this field "Packet Tag".
|
||||||
|
|
||||||
|
```{tip}
|
||||||
|
|
||||||
|
The overall structure of OpenPGP packets is described in the [Packet Syntax](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-packet-syntax) chapter of the RFC.
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that the *Public-Key packet* contains only the public part of the key.
|
||||||
|
|
||||||
|
(zooming_in_dks)=
|
||||||
|
### Direct Key Signature
|
||||||
|
|
||||||
|
The next packet is a [*Direct Key Signature*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#sigtype-direct-key), which is bound to the primary key (the file `alice.pub-1--Signature` contains this packet).
|
||||||
|
|
||||||
|
This packet "binds the information in the signature subpackets to the key". Each entry under "Signature Packet -> Hashed area" is one signature subpacket, for example, including information about algorithm preferences (*symmetric algorithm preference* and *hash algorithm preferences*).
|
||||||
|
|
||||||
|
```text
|
||||||
|
Signature Packet, new CTB, 2 header bytes + 182 bytes
|
||||||
|
Version: 6
|
||||||
|
Type: DirectKey
|
||||||
|
Pk algo: Ed25519
|
||||||
|
Hash algo: SHA512
|
||||||
|
Hashed area:
|
||||||
|
Signature creation time: 2023-09-29 15:17:58 UTC (critical)
|
||||||
|
Key expiration time: P1095DT62781S (critical)
|
||||||
|
Symmetric algo preferences: AES256, AES128
|
||||||
|
Hash preferences: SHA512, SHA256
|
||||||
|
Key flags: C (critical)
|
||||||
|
Features: MDC
|
||||||
|
Issuer Fingerprint: AAA18CBB254685C58358320563FD37B67F3300F9FB0EC457378CD29F102698B3
|
||||||
|
Unhashed area:
|
||||||
|
Issuer: AAA18CBB254685C5
|
||||||
|
Digest prefix: 6747
|
||||||
|
Level: 0 (signature over data)
|
||||||
|
|
||||||
|
00000000 c2 CTB
|
||||||
|
00000001 b6 length
|
||||||
|
00000002 06 version
|
||||||
|
00000003 1f type
|
||||||
|
00000004 1b pk_algo
|
||||||
|
00000005 0a hash_algo
|
||||||
|
00000006 00 00 00 3d hashed_area_len
|
||||||
|
0000000a 05 subpacket length
|
||||||
|
0000000b 82 subpacket tag
|
||||||
|
0000000c 65 16 ea a6 sig creation time
|
||||||
|
00000010 05 subpacket length
|
||||||
|
00000011 89 subpacket tag
|
||||||
|
00000012 05 a4 8f bd key expiry time
|
||||||
|
00000016 03 subpacket length
|
||||||
|
00000017 0b subpacket tag
|
||||||
|
00000018 09 07 pref sym algos
|
||||||
|
0000001a 03 subpacket length
|
||||||
|
0000001b 15 subpacket tag
|
||||||
|
0000001c 0a 08 pref hash algos
|
||||||
|
0000001e 02 subpacket length
|
||||||
|
0000001f 9b subpacket tag
|
||||||
|
00000020 01 key flags
|
||||||
|
00000021 02 subpacket length
|
||||||
|
00000022 1e subpacket tag
|
||||||
|
00000023 01 features
|
||||||
|
00000024 22 subpacket length
|
||||||
|
00000025 21 subpacket tag
|
||||||
|
00000026 06 version
|
||||||
|
00000027 aa a1 8c bb 25 46 85 c5 83 issuer fp
|
||||||
|
00000030 58 32 05 63 fd 37 b6 7f 33 00 f9 fb 0e c4 57 37
|
||||||
|
00000040 8c d2 9f 10 26 98 b3
|
||||||
|
00000047 00 00 00 0a unhashed_area_len
|
||||||
|
0000004b 09 subpacket length
|
||||||
|
0000004c 10 subpacket tag
|
||||||
|
0000004d aa a1 8c issuer
|
||||||
|
00000050 bb 25 46 85 c5
|
||||||
|
00000055 67 digest_prefix1
|
||||||
|
00000056 47 digest_prefix2
|
||||||
|
00000057 20 salt_len
|
||||||
|
00000058 a2 e3 63 81 e3 6b d0 4f salt
|
||||||
|
00000060 56 d7 c8 38 80 02 25 e8 53 39 49 6b 90 c6 0d 76
|
||||||
|
00000070 8f 44 b4 ec 4c 42 38 8e
|
||||||
|
00000078 4d 3d 10 c2 ad 29 c0 57 ed25519_sig
|
||||||
|
00000080 ce 66 01 73 b3 78 81 9e 89 52 cf 43 8d 28 11 a9
|
||||||
|
00000090 77 09 7f cb f8 15 c8 a1 8d ac 20 44 2b 2d de 13
|
||||||
|
000000a0 00 41 32 d0 10 3c 69 06 53 3c 49 8c 76 23 82 33
|
||||||
|
000000b0 54 01 f9 5f 81 41 90 0e
|
||||||
|
```
|
||||||
|
|
||||||
|
Let’s look at the packet field by field:
|
||||||
|
|
||||||
|
- `CTB: 0xc2`: The Packet type ID for this packet. Bits 7 and 6 show that the packet is in “OpenPGP packet format” (as opposed to in “Legacy packet format”). The remaining 6 bits encode the type ID’s value: “2.” This is the value for a Signature packet.
|
||||||
|
- `length: 0xb6`: The remaining length of this packet.
|
||||||
|
|
||||||
|
The packet type ID defines the semantics of the remaining data in the packet. We're looking at a [Signature packet](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#signature-packet), so the following data is interpreted accordingly.
|
||||||
|
|
||||||
|
- `version: 0x06`: This is a version 6 signature (some of the following packet format is specific to this signature version).
|
||||||
|
- `type: 0x1f`: The [Signature Type](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-signature-types)
|
||||||
|
- `pk_algo: 0x1b`: Public-key algorithm ID (decimal 27, corresponds to [Ed25519](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-public-key-algorithms))
|
||||||
|
- `hash_algo: 0x0a`: Hash algorithm ID (decimal 10, corresponds to [SHA2-512](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-hash-algorithms))
|
||||||
|
- `hashed_area_len: 0x0000003d`: Length of the following hashed subpacket data
|
||||||
|
|
||||||
|
The next part of this packet contains hashed subpacket data. A subpacket data set in an OpenPGP Signature contains a list of zero or more Signature subpackets.
|
||||||
|
|
||||||
|
There are two sets of subpacket data in a Signature: hashed, and unhashed. The difference is that the hashed subpackets are protected by the digital signature of this packet, while the unhashed subpackets are not.
|
||||||
|
|
||||||
|
The following subpacket data consists of sets of "subpacket length, subpacket type ID, data." We'll show the information for each subpacket as one line, starting with the [subpacket type description](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-signature-subpacket-specifi) (based on the subpacket type ID). Note that bit 7 of the subpacket type ID signals if that subpacket is ["critical"](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#section-5.2.3.7-10).
|
||||||
|
|
||||||
|
```{note}
|
||||||
|
Critical here means: the receiver must be able to interpret the subpacket and is expected to fail, otherwise. Non-critical subpackets may be ignored by the receiver.
|
||||||
|
```
|
||||||
|
|
||||||
|
- [Signature creation time](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#signature-creation-subpacket) (subpacket type 2, **critical**): `0x6516eaa6` (also see [Time Fields](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-time-fields))
|
||||||
|
- [Key expiration time](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#key-expiration-subpacket) (subpacket type 9, **critical**): `0x05a48fbd` (defined as number of seconds after the key creation time)
|
||||||
|
- [Preferred symmetric ciphers for v1 SEIPD](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#preferred-v1-seipd) (type 11): `0x09 0x07`. (These values [correspond to](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#symmetric-algos): *AES with 256-bit key* and *AES with 128-bit key*)
|
||||||
|
- [Preferred hash algorithms](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#preferred-hashes-subpacket) (subpacket type 21): `0x0a 0x08`. (These values [correspond to](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-hash-algorithms): *SHA2-512* and *SHA2-256*)
|
||||||
|
- [Key flags](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#key-flags) (subpacket type 27, **critical**): `0x01`. (This value [corresponds](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-key-flags) to the *certifications* key flag)
|
||||||
|
- [Features](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#features-subpacket) (subpacket type 30): `0x01`. (This value [corresponds](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-features) to: *Symmetrically Encrypted Integrity Protected Data packet version 1*)
|
||||||
|
- [Issuer fingerprint](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#issuer-fingerprint-subpacket) (subpacket type 33): `aaa18cbb254685c58358320563fd37b67f3300f9fb0ec457378cd29f102698b3` (this is the fingerprint of the component key that issued the signature in this packet. Not that here, the value is the primary key fingerprint of the certificate we're looking at.)
|
||||||
|
|
||||||
|
The next part of this packet contains "unhashed subpacket data":
|
||||||
|
|
||||||
|
- `unhashed_area_len: 0x0000000a`: Length of the following unhashed subpacket data (value: 10 bytes).
|
||||||
|
|
||||||
|
As above, the following subpacket data consists of sets of "subpacket length, subpacket type id, data." In this case, only subpacket follows:
|
||||||
|
|
||||||
|
- [Issuer Key ID](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#issuer-keyid-subpacket) (subpacket type 16): `aaa18cbb254685c5` (this is the shortened version 6 *Key ID* of the fingerprint of this certificate's primary key)
|
||||||
|
|
||||||
|
This concludes the unhashed subpacket data.
|
||||||
|
|
||||||
|
- `digest_prefix: 0x6747`: "The left 16 bits of the signed hash value"
|
||||||
|
- `salt_len, salt`: A random [salt value](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-advantages-of-salted-signat) (the size must be [matching for the hash algorithm](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#hash-algorithms-registry))
|
||||||
|
- `ed25519_sig`: [Algorithm-specific](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-algorithm-specific-fields-for-ed2) representation of the signature (in this case: 64 bytes of Ed25519 signature)
|
||||||
|
|
||||||
|
The signature is calculated over a hash. The hash, in this case, is calculated over the following data (for details, see [Computing Signatures](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-11.html#name-computing-signatures) in the RFC):
|
||||||
|
|
||||||
|
- The signature's salt
|
||||||
|
- A serialized form of the primary key's public data
|
||||||
|
- A serialized form of this direct key signature packet (up to, but excluding the unhashed area)
|
||||||
|
|
||||||
|
|
||||||
|
(zoom_enc_subkey)=
|
||||||
|
## Encryption subkey
|
||||||
|
|
||||||
|
Now we'll look at a subkey in Alice's certificate. An OpenPGP subkey, when it is linked to an OpenPGP certificate (via its primary key), consists of two elements:
|
||||||
|
|
||||||
|
- a key packet that contains the component key itself, and
|
||||||
|
- a signature packet that links this component key to the primary key (and thus implicitly to the full OpenPGP certificate).
|
||||||
|
|
||||||
|
In this section, we'll use the files that contain individual packets of Alice's certificate, which we split apart above. In this split representation of Alice's certificate, the encryption subkey happens to be stored in `alice.pub-4--PublicSubkey`, and the associated binding self-signature for the subkey in `alice.pub-5--Signature`.
|
||||||
|
|
||||||
|
````{note}
|
||||||
|
It's common to look at a packet dump for a full OpenPGP certificate, like this:
|
||||||
|
|
||||||
|
```text
|
||||||
|
$ sq packet dump --hex alice.pub
|
||||||
|
```
|
||||||
|
|
||||||
|
That command shows the details for the full series of packets in an OpenPGP certificate (recall the list of [packets of Alice's certificate](split_alice)). Finding a particular packet in that list can take a bit of focus and practice though.
|
||||||
|
|
||||||
|
In the following sections we're making it a bit easier for ourselves, and directly look at individual packets, from the files we created with `sq packet split`, above.
|
||||||
|
````
|
||||||
|
|
||||||
|
### Public-Subkey packet
|
||||||
|
|
||||||
|
First, we'll look at the *Public-Subkey packet* that contains the component key data of this subkey:
|
||||||
|
|
||||||
|
```text
|
||||||
|
$ sq packet dump --hex alice.pub-4--PublicSubkey
|
||||||
|
Public-Subkey Packet, new CTB, 2 header bytes + 42 bytes
|
||||||
|
Version: 6
|
||||||
|
Creation time: 2023-09-29 15:17:58 UTC
|
||||||
|
Pk algo: X25519
|
||||||
|
Pk size: 256 bits
|
||||||
|
Fingerprint: C0A58384A438E5A14F73712426A4D45DBAEEF4A39E6B30B09D5513F978ACCA94
|
||||||
|
KeyID: C0A58384A438E5A1
|
||||||
|
|
||||||
|
00000000 ce CTB
|
||||||
|
00000001 2a length
|
||||||
|
00000002 06 version
|
||||||
|
00000003 65 16 ea a6 creation_time
|
||||||
|
00000007 19 pk_algo
|
||||||
|
00000008 00 00 00 20 public_len
|
||||||
|
0000000c d1 ae 87 d7 x25519_public
|
||||||
|
00000010 cc 42 af 99 34 c5 c2 5c ca fa b7 4a c8 43 fc 86
|
||||||
|
00000020 35 2a 46 01 f3 cc 00 f5 4a 09 3e 3f
|
||||||
|
```
|
||||||
|
|
||||||
|
Notice that the structure of this *Public-Subkey packet* is the same as the *Public-Key Packet* of the primary key, [above](public_key). Only the content of the two packets differs in some points:
|
||||||
|
|
||||||
|
- The packet type ID (`CTB`) in this packet shows type 14 ([*Public-Subkey packet*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-public-subkey-packet-tag-14)).
|
||||||
|
- The `pk_algo` value is set to `0x19` (decimal 25), which [corresponds to](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-public-key-algorithms) X25519. Note that even though both the primary key and this subkey use a cryptographic mechanism based on Curve25519, this encryption key uses Curve 25519 in a different way (X25519 is a Diffie–Hellman function built out of Curve25519).
|
||||||
|
- Accordingly, the public part of the cryptographic key pair is labeled with the corresponding name: `x25519_public` (however, note that this difference only reflects the semantics of the field, which is implied by the value of `pk_algo`. The actual data consists of just 32 bytes of cryptographic key material, without any type information.)
|
||||||
|
|
||||||
|
### Subkey binding signature
|
||||||
|
|
||||||
|
The subkey packet above by itself is disconnected from the OpenPGP certificate that it is a part of. The link between the subkey and the full OpenPGP key is made with a cryptographic signature, which is issued by the OpenPGP key's primary key.
|
||||||
|
|
||||||
|
The type of signature that is used for this is called a *subkey binding signature*, because it "binds" (as in "connects") the subkey to the rest of the key.
|
||||||
|
|
||||||
|
```{admonition} VISUAL
|
||||||
|
:class: warning
|
||||||
|
|
||||||
|
Add detailed packet diagram analogous to 4.6.1
|
||||||
|
```
|
||||||
|
|
||||||
|
```{admonition} TODO
|
||||||
|
:class: warning
|
||||||
|
|
||||||
|
david points out: "The information on metadata in binding signatures may also make sense in other contexts (direct key signature)?"
|
||||||
|
|
||||||
|
Should this text go elsewhere?
|
||||||
|
- 4.2.3?
|
||||||
|
- ch 6?
|
||||||
|
```
|
||||||
|
In addition to its core purpose of making the connection, this signature also contains additional metadata about the subkey. One reason why this metadata is in a binding signature (and not in the subkey packet) is that it may change over time. The subkey packet itself may not change over time. So metadata about the subkey that can change is stored in self-signatures: if the key holder wants to change some metadata (for example, the key's expiration time), they can issue a newer version of the same kind of signature. Receiving OpenPGP software will then understand that the newer self-signature supersedes the older signature, and that the metadata in the newer signature reflects the most current intent of the key holder.
|
||||||
|
|
||||||
|
Note that this subkey binding signature packet is quite similar to the Direct Key Signature we discussed packet above. Both signatures perform the same function in terms of adding metadata to a component key. In particular, the hashed subpacket data contains many of the same pieces of metadata.
|
||||||
|
|
||||||
|
```text
|
||||||
|
$ sq packet dump --hex alice.pub-5--Signature
|
||||||
|
Signature Packet, new CTB, 2 header bytes + 171 bytes
|
||||||
|
Version: 6
|
||||||
|
Type: SubkeyBinding
|
||||||
|
Pk algo: Ed25519
|
||||||
|
Hash algo: SHA512
|
||||||
|
Hashed area:
|
||||||
|
Signature creation time: 2023-09-29 15:17:58 UTC (critical)
|
||||||
|
Key expiration time: P1095DT62781S (critical)
|
||||||
|
Key flags: EtEr (critical)
|
||||||
|
Issuer Fingerprint: AAA18CBB254685C58358320563FD37B67F3300F9FB0EC457378CD29F102698B3
|
||||||
|
Unhashed area:
|
||||||
|
Issuer: AAA18CBB254685C5
|
||||||
|
Digest prefix: 2289
|
||||||
|
Level: 0 (signature over data)
|
||||||
|
|
||||||
|
00000000 c2 CTB
|
||||||
|
00000001 ab length
|
||||||
|
00000002 06 version
|
||||||
|
00000003 18 type
|
||||||
|
00000004 1b pk_algo
|
||||||
|
00000005 0a hash_algo
|
||||||
|
00000006 00 00 00 32 hashed_area_len
|
||||||
|
0000000a 05 subpacket length
|
||||||
|
0000000b 82 subpacket tag
|
||||||
|
0000000c 65 16 ea a6 sig creation time
|
||||||
|
00000010 05 subpacket length
|
||||||
|
00000011 89 subpacket tag
|
||||||
|
00000012 05 a4 8f bd key expiry time
|
||||||
|
00000016 02 subpacket length
|
||||||
|
00000017 9b subpacket tag
|
||||||
|
00000018 0c key flags
|
||||||
|
00000019 22 subpacket length
|
||||||
|
0000001a 21 subpacket tag
|
||||||
|
0000001b 06 version
|
||||||
|
0000001c aa a1 8c bb issuer fp
|
||||||
|
00000020 25 46 85 c5 83 58 32 05 63 fd 37 b6 7f 33 00 f9
|
||||||
|
00000030 fb 0e c4 57 37 8c d2 9f 10 26 98 b3
|
||||||
|
0000003c 00 00 00 0a unhashed_area_len
|
||||||
|
00000040 09 subpacket length
|
||||||
|
00000041 10 subpacket tag
|
||||||
|
00000042 aa a1 8c bb 25 46 85 c5 issuer
|
||||||
|
0000004a 22 digest_prefix1
|
||||||
|
0000004b 89 digest_prefix2
|
||||||
|
0000004c 20 salt_len
|
||||||
|
0000004d 0b 0c 89 salt
|
||||||
|
00000050 b5 ab 15 e3 7f e4 4d b9 a7 ef 71 48 14 3b ab 26
|
||||||
|
00000060 5f 34 7f 6d 48 2e 9f 78 48 58 6d 9a fb
|
||||||
|
0000006d 6d b2 db ed25519_sig
|
||||||
|
00000070 2f 97 8e c8 12 fc 57 7f 85 aa d1 59 bc 80 40 0b
|
||||||
|
00000080 be 2e f0 e1 23 2d bf 4b 71 7e d0 e4 c0 36 e4 d2
|
||||||
|
00000090 cf b2 9f b4 a8 4f 3e 2a 21 89 74 c2 33 55 af ac
|
||||||
|
000000a0 41 36 1b 2b 60 09 f2 d9 19 f4 41 12 0b
|
||||||
|
```
|
||||||
|
|
||||||
|
We'll go over this packet dump in less detail, since its structure mirrors the *Direct Key Signature* (described above) very closely.
|
||||||
|
|
||||||
|
The first difference is in the `type` field, showing that this signature is of type `0x18` ([Subkey Binding Signature](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-subkey-binding-signature-si)).
|
||||||
|
|
||||||
|
The `pk_algo` of this signature is informed by the algorithm of the primary key (`0x1b`, corresponding to Ed25519). The signature in this packet is issued by the primary key, so by definition it uses the signing algorithm of the primary key (that is: the algorithm used to produce the cryptographic signature in this packet is entire independent of the `pk_algo` of the key material of this subkey itself, which uses the X25519 mechanism).
|
||||||
|
|
||||||
|
As shown in the text at the top of this packet dump, the hashed subpacket data contains four pieces of information:
|
||||||
|
|
||||||
|
- Signature creation time: `2023-09-29 15:17:58 UTC` (**critical**)
|
||||||
|
- Key expiration time: `P1095DT62781S` (**critical**)
|
||||||
|
- Key flags: `EtEr` (**critical**) (encryption for communication, encryption for storage)
|
||||||
|
- Issuer Fingerprint: `AAA18CBB254685C58358320563FD37B67F3300F9FB0EC457378CD29F102698B3`
|
||||||
|
|
||||||
|
The remainder of the packet has the same content as the *Direct Key Signature* above:
|
||||||
|
- A 16 bit digest prefix
|
||||||
|
- A salt value
|
||||||
|
- The cryptographic signature itself
|
||||||
|
|
||||||
|
The signature is calculated over a hash. The hash, in this case, is calculated over the following data (for details, see [Computing Signatures](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-11.html#name-computing-signatures) in the RFC):
|
||||||
|
|
||||||
|
- The signature's salt
|
||||||
|
- A serialized form of the primary key's public data
|
||||||
|
- A serialized form of the subkey's public data
|
||||||
|
- A serialized form of this subkey binding signature packet (up to, but excluding the unhashed area)
|
||||||
|
|
||||||
|
## Signing subkey
|
||||||
|
|
||||||
|
```{admonition} TODO
|
||||||
|
:class: warning
|
||||||
|
|
||||||
|
write
|
||||||
|
```
|
||||||
|
|
||||||
|
```text
|
||||||
|
$ sq packet dump --hex alice.pub-6--PublicSubkey
|
||||||
|
Public-Subkey Packet, new CTB, 2 header bytes + 42 bytes
|
||||||
|
Version: 6
|
||||||
|
Creation time: 2023-09-29 15:17:58 UTC
|
||||||
|
Pk algo: Ed25519
|
||||||
|
Pk size: 256 bits
|
||||||
|
Fingerprint: D07B24EC91A14DD240AC2D53E6C8A9E054949A41222EA738576ED19CAEA3DC99
|
||||||
|
KeyID: D07B24EC91A14DD2
|
||||||
|
|
||||||
|
00000000 ce CTB
|
||||||
|
00000001 2a length
|
||||||
|
00000002 06 version
|
||||||
|
00000003 65 16 ea a6 creation_time
|
||||||
|
00000007 1b pk_algo
|
||||||
|
00000008 00 00 00 20 public_len
|
||||||
|
0000000c 33 8c d4 f5 ed25519_public
|
||||||
|
00000010 1a 73 39 ef ce d6 0f 21 8d a0 58 a2 3c 3d 44 a8
|
||||||
|
00000020 59 e9 13 1f 12 9c 6f 19 d0 3d 40 a0
|
||||||
|
```
|
||||||
|
|
||||||
|
```text
|
||||||
|
$ sq packet dump --hex alice.pub-7--Signature
|
||||||
|
Signature Packet, new CTB, 3 header bytes + 325 bytes
|
||||||
|
Version: 6
|
||||||
|
Type: SubkeyBinding
|
||||||
|
Pk algo: Ed25519
|
||||||
|
Hash algo: SHA512
|
||||||
|
Hashed area:
|
||||||
|
Signature creation time: 2023-09-29 15:17:58 UTC (critical)
|
||||||
|
Key expiration time: P1095DT62781S (critical)
|
||||||
|
Key flags: S (critical)
|
||||||
|
Embedded signature: (critical)
|
||||||
|
Signature Packet
|
||||||
|
Version: 6
|
||||||
|
Type: PrimaryKeyBinding
|
||||||
|
Pk algo: Ed25519
|
||||||
|
Hash algo: SHA512
|
||||||
|
Hashed area:
|
||||||
|
Signature creation time: 2023-09-29 15:17:58 UTC (critical)
|
||||||
|
Issuer Fingerprint: D07B24EC91A14DD240AC2D53E6C8A9E054949A41222EA738576ED19CAEA3DC99
|
||||||
|
Digest prefix: 5365
|
||||||
|
Level: 0 (signature over data)
|
||||||
|
|
||||||
|
Issuer Fingerprint: AAA18CBB254685C58358320563FD37B67F3300F9FB0EC457378CD29F102698B3
|
||||||
|
Unhashed area:
|
||||||
|
Issuer: AAA18CBB254685C5
|
||||||
|
Digest prefix: 841C
|
||||||
|
Level: 0 (signature over data)
|
||||||
|
|
||||||
|
00000000 c2 CTB
|
||||||
|
00000001 c0 85 length
|
||||||
|
00000003 06 version
|
||||||
|
00000004 18 type
|
||||||
|
00000005 1b pk_algo
|
||||||
|
00000006 0a hash_algo
|
||||||
|
00000007 00 00 00 cc hashed_area_len
|
||||||
|
0000000b 05 subpacket length
|
||||||
|
0000000c 82 subpacket tag
|
||||||
|
0000000d 65 16 ea sig creation time
|
||||||
|
00000010 a6
|
||||||
|
00000011 05 subpacket length
|
||||||
|
00000012 89 subpacket tag
|
||||||
|
00000013 05 a4 8f bd key expiry time
|
||||||
|
00000017 02 subpacket length
|
||||||
|
00000018 9b subpacket tag
|
||||||
|
00000019 02 key flags
|
||||||
|
0000001a 99 subpacket length
|
||||||
|
0000001b a0 subpacket tag
|
||||||
|
0000001c 06 19 1b 0a embedded sig
|
||||||
|
00000020 00 00 00 29 05 82 65 16 ea a6 22 21 06 d0 7b 24
|
||||||
|
00000030 ec 91 a1 4d d2 40 ac 2d 53 e6 c8 a9 e0 54 94 9a
|
||||||
|
00000040 41 22 2e a7 38 57 6e d1 9c ae a3 dc 99 00 00 00
|
||||||
|
00000050 00 53 65 20 42 03 ad 0c db fc b5 9a 98 a6 15 27
|
||||||
|
00000060 e4 11 5e f5 f2 a0 3d bc ed 8d 94 27 41 09 f6 3c
|
||||||
|
00000070 4b f8 8a e5 af 73 e1 7d 54 07 40 3f f3 29 34 c2
|
||||||
|
00000080 e7 60 56 a5 e1 43 cb 08 ba 66 fe 8b 26 ce e7 cb
|
||||||
|
00000090 a5 3a 46 bb a5 c8 5d e4 6a de ae 49 e1 3e 07 bf
|
||||||
|
000000a0 c4 9e 98 14 2f 3e c5 f7 01 3e 3e 4f f6 18 2a ac
|
||||||
|
000000b0 bd ed 52 0c
|
||||||
|
000000b4 22 subpacket length
|
||||||
|
000000b5 21 subpacket tag
|
||||||
|
000000b6 06 version
|
||||||
|
000000b7 aa a1 8c bb 25 46 85 c5 83 issuer fp
|
||||||
|
000000c0 58 32 05 63 fd 37 b6 7f 33 00 f9 fb 0e c4 57 37
|
||||||
|
000000d0 8c d2 9f 10 26 98 b3
|
||||||
|
000000d7 00 00 00 0a unhashed_area_len
|
||||||
|
000000db 09 subpacket length
|
||||||
|
000000dc 10 subpacket tag
|
||||||
|
000000dd aa a1 8c issuer
|
||||||
|
000000e0 bb 25 46 85 c5
|
||||||
|
000000e5 84 digest_prefix1
|
||||||
|
000000e6 1c digest_prefix2
|
||||||
|
000000e7 20 salt_len
|
||||||
|
000000e8 23 3d b2 49 f3 02 4b 08 salt
|
||||||
|
000000f0 93 af ba 08 89 f0 e0 91 0f ab 22 26 aa b3 56 57
|
||||||
|
00000100 30 ea 95 29 06 60 6f 00
|
||||||
|
00000108 be 44 a1 95 38 a9 6b 3a ed25519_sig
|
||||||
|
00000110 3e 51 f0 55 09 b1 e2 91 a9 17 86 fa f5 1e 3f d0
|
||||||
|
00000120 28 46 3c ce 6e 88 14 37 32 ec 3d fa c6 01 ca e5
|
||||||
|
00000130 a9 4b b7 63 94 c3 0d 92 ab dc fa 23 50 71 60 31
|
||||||
|
00000140 a6 73 c8 33 5a 9c d9 0a
|
||||||
|
```
|
||||||
|
|
||||||
|
(zooming_in_user_id)=
|
||||||
|
## Adding an identity component
|
||||||
|
|
||||||
|
Now we'll look at an identity that is associated with Alice's certificate.
|
||||||
|
|
||||||
|
User IDs are a mechanism for connecting [identities](identity_components) with an OpenPGP certificate. Traditionally, User IDs contain a string that combines a name and an email address.
|
||||||
|
|
||||||
|
Like [above](zoom_enc_subkey), to look at the internal packet structure of this identity and its connection the OpenPGP certificate, we'll inspect the two individual packets that constitute the identity component, the [User ID packet](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-user-id-packet-tag-13), in the file `alice.pub-2--UserID`, and the certifying self-signature a [Positive certification of a User ID and Public-Key packet](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-positive-certification-of-a) in `alice.pub-3--Signature` (these packets are an excerpt of Alice's full OpenPGP private key).
|
||||||
|
|
||||||
|
### User ID packet
|
||||||
|
|
||||||
|
First, let's look at the User ID packet, which encodes an identity that Alice has connected to her OpenPGP certificate:
|
||||||
|
|
||||||
|
```text
|
||||||
|
$ sq packet dump --hex alice.pub-2--UserID
|
||||||
|
User ID Packet, new CTB, 2 header bytes + 19 bytes
|
||||||
|
Value: <alice@example.org>
|
||||||
|
|
||||||
|
00000000 cd CTB
|
||||||
|
00000001 13 length
|
||||||
|
00000002 3c 61 6c 69 63 65 40 65 78 61 6d 70 6c 65 value
|
||||||
|
00000010 2e 6f 72 67 3e
|
||||||
|
```
|
||||||
|
|
||||||
|
- `CTB: 0xcd`: The Packet type ID for this packet. Bits 7 and 6 show that the packet is in “OpenPGP packet format” (as opposed to in “Legacy packet format”). The remaining 6 bits encode the type ID’s value: “13.” This is the value for a [User ID packet](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-user-id-packet-tag-13).
|
||||||
|
- `length: 0x13`: The remaining length of this packet (here: 19 bytes).
|
||||||
|
- `value`: 19 bytes of data that contain UTF-8 encoded text. The value corresponds to the string `<alice@example.org>`. With this identity component, Alice states that she uses (and has control of) this email address. Note that the email address is enclosed in `<` and `>` characters, following [RFC 2822](https://www.rfc-editor.org/rfc/rfc2822) conventions.
|
||||||
|
|
||||||
|
So, a User ID packet is really just a string, marked as a User ID by the packet type id.
|
||||||
|
|
||||||
|
### Linking the User ID with a certification self-signature
|
||||||
|
|
||||||
|
As above, when [linking a subkey](zoom_enc_subkey) to the OpenPGP certificate, a self-signature is used to connect this new component to the certificate.
|
||||||
|
|
||||||
|
To bind identities to a certificate with a self-signature, one of the signature types `0x10` - `0x13` can be used. Here, the signature type `0x13` (*positive certification*) is used.
|
||||||
|
|
||||||
|
```text
|
||||||
|
$ sq packet dump --hex alice.pub-3--Signature
|
||||||
|
Signature Packet, new CTB, 2 header bytes + 185 bytes
|
||||||
|
Version: 6
|
||||||
|
Type: PositiveCertification
|
||||||
|
Pk algo: Ed25519
|
||||||
|
Hash algo: SHA512
|
||||||
|
Hashed area:
|
||||||
|
Signature creation time: 2023-09-29 15:17:58 UTC (critical)
|
||||||
|
Key expiration time: P1095DT62781S (critical)
|
||||||
|
Symmetric algo preferences: AES256, AES128
|
||||||
|
Hash preferences: SHA512, SHA256
|
||||||
|
Primary User ID: true (critical)
|
||||||
|
Key flags: C (critical)
|
||||||
|
Features: MDC
|
||||||
|
Issuer Fingerprint: AAA18CBB254685C58358320563FD37B67F3300F9FB0EC457378CD29F102698B3
|
||||||
|
Unhashed area:
|
||||||
|
Issuer: AAA18CBB254685C5
|
||||||
|
Digest prefix: DBB8
|
||||||
|
Level: 0 (signature over data)
|
||||||
|
|
||||||
|
00000000 c2 CTB
|
||||||
|
00000001 b9 length
|
||||||
|
00000002 06 version
|
||||||
|
00000003 13 type
|
||||||
|
00000004 1b pk_algo
|
||||||
|
00000005 0a hash_algo
|
||||||
|
00000006 00 00 00 40 hashed_area_len
|
||||||
|
0000000a 05 subpacket length
|
||||||
|
0000000b 82 subpacket tag
|
||||||
|
0000000c 65 16 ea a6 sig creation time
|
||||||
|
00000010 05 subpacket length
|
||||||
|
00000011 89 subpacket tag
|
||||||
|
00000012 05 a4 8f bd key expiry time
|
||||||
|
00000016 03 subpacket length
|
||||||
|
00000017 0b subpacket tag
|
||||||
|
00000018 09 07 pref sym algos
|
||||||
|
0000001a 03 subpacket length
|
||||||
|
0000001b 15 subpacket tag
|
||||||
|
0000001c 0a 08 pref hash algos
|
||||||
|
0000001e 02 subpacket length
|
||||||
|
0000001f 99 subpacket tag
|
||||||
|
00000020 01 primary user id
|
||||||
|
00000021 02 subpacket length
|
||||||
|
00000022 9b subpacket tag
|
||||||
|
00000023 01 key flags
|
||||||
|
00000024 02 subpacket length
|
||||||
|
00000025 1e subpacket tag
|
||||||
|
00000026 01 features
|
||||||
|
00000027 22 subpacket length
|
||||||
|
00000028 21 subpacket tag
|
||||||
|
00000029 06 version
|
||||||
|
0000002a aa a1 8c bb 25 46 issuer fp
|
||||||
|
00000030 85 c5 83 58 32 05 63 fd 37 b6 7f 33 00 f9 fb 0e
|
||||||
|
00000040 c4 57 37 8c d2 9f 10 26 98 b3
|
||||||
|
0000004a 00 00 00 0a unhashed_area_len
|
||||||
|
0000004e 09 subpacket length
|
||||||
|
0000004f 10 subpacket tag
|
||||||
|
00000050 aa a1 8c bb 25 46 85 c5 issuer
|
||||||
|
00000058 db digest_prefix1
|
||||||
|
00000059 b8 digest_prefix2
|
||||||
|
0000005a 20 salt_len
|
||||||
|
0000005b 8a 2d 6f da 67 salt
|
||||||
|
00000060 35 bc 5d 04 77 b4 9d 67 a8 6e c5 d6 88 53 5f e2
|
||||||
|
00000070 ef f9 66 08 bf c2 e0 db c0 56 0d
|
||||||
|
0000007b eb d4 2c a5 19 ed25519_sig
|
||||||
|
00000080 01 0f ba 26 d0 82 a2 cf 5c eb 7a a9 72 d9 f3 b2
|
||||||
|
00000090 66 07 8b b2 ba 3d b7 89 e4 76 04 6e 35 24 2b 27
|
||||||
|
000000a0 29 83 be 91 9c 78 6a cc b4 d5 69 47 76 2c 29 d6
|
||||||
|
000000b0 54 bf 43 19 04 ff 53 98 c0 d5 0b
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
We'll go over this packet dump in less detail, since its structure closely mirrors the [Direct Key Signature](zooming_in_dks) discussed above.
|
||||||
|
|
||||||
|
We're again looking at a Signature packet. Its `type` is `0x13` ([corresponding](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-signature-types) to a *positive certification* signature).
|
||||||
|
|
||||||
|
The public key algorithm and hash function used for this signature are Ed25519 and SHA512.
|
||||||
|
|
||||||
|
As shown in the text at the top of this packet dump, the hashed subpacket data contains the following metadata:
|
||||||
|
|
||||||
|
- Signature creation time: `2023-09-29 15:17:58 UTC` (**critical**)
|
||||||
|
- Key expiration time: `P1095DT62781S` (**critical**)
|
||||||
|
- Symmetric algo preferences: `AES256, AES128`
|
||||||
|
- Hash preferences: `SHA512, SHA256`
|
||||||
|
- Primary User ID: `true` (**critical**)
|
||||||
|
- Key flags: `C` (**critical**)
|
||||||
|
- Features: `MDC`
|
||||||
|
- Issuer Fingerprint: `AAA18CBB254685C58358320563FD37B67F3300F9FB0EC457378CD29F102698B3`
|
||||||
|
|
||||||
|
This is a combination of metadata about the User ID itself (including defining this User ID as the *primary User ID* of this certificate), algorithm preferences that are associated with this identity, and settings that apply to the primary key.
|
||||||
|
|
||||||
|
````{note}
|
||||||
|
For historical reasons, the self-signature that binds the primary User ID to the certificate also contains subpackets that apply not to the User ID, but to the primary key itself.
|
||||||
|
|
||||||
|
Setting key expiration time and key flags on the primary User ID self-signature is one mechanism to configure the primary key.
|
||||||
|
|
||||||
|
The interaction between metadata on direct key signatures and User ID binding self-signatures [is subtle](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-11.html#name-notes-on-self-signatures), and there are changes between version 6 and version 4.
|
||||||
|
|
||||||
|
|
||||||
|
```{admonition} TODO
|
||||||
|
:class: warning
|
||||||
|
|
||||||
|
- link to a section that goes into more depth about "#name-notes-on-self-signatures"?
|
||||||
|
```
|
||||||
|
|
||||||
|
````
|
||||||
|
|
||||||
|
Followed, again, by the (informational) unhashed subpacket area.
|
||||||
|
|
||||||
|
And finally, a salt value for the signature and the signature itself.
|
||||||
|
|
||||||
|
The signature is calculated over a hash. The hash, in this case, is calculated over the following data (for details, see [Computing Signatures](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-11.html#name-computing-signatures) in the RFC):
|
||||||
|
|
||||||
|
- The signature's salt
|
||||||
|
- A serialized form of the primary key's public data
|
||||||
|
- A serialized form of the User ID
|
||||||
|
- A serialized form of this self-signature packet (up to, but excluding the unhashed area)
|
||||||
|
|
||||||
|
## Certifications (Third Party Signatures)
|
||||||
|
|
||||||
|
## Revocations
|
187
book/source/18-zoom_private_keys.md
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
# Zooming in: Packet structure of private key material
|
||||||
|
|
||||||
|
## A look at Alice's (unencrypted) private key packets
|
||||||
|
|
||||||
|
Let's take a look at the key material packets of [Alice's key](alice_priv).
|
||||||
|
|
||||||
|
To inspect the internal structure of Alice's key, we run the Sequoia-PGP tool `sq` (using the `packet dump` subcommand). The output of `sq` is one big block of text. To discuss the relevant content, we'll only show the output for the packets that contain key data, here:
|
||||||
|
|
||||||
|
```text
|
||||||
|
$ sq packet dump --hex alice.priv
|
||||||
|
```
|
||||||
|
|
||||||
|
### Primary Secret-Key packet
|
||||||
|
|
||||||
|
The output starts with the (primary) [Secret-Key packet](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-secret-key-packet-formats):
|
||||||
|
|
||||||
|
```text
|
||||||
|
Secret-Key Packet, new CTB, 2 header bytes + 75 bytes
|
||||||
|
Version: 6
|
||||||
|
Creation time: 2023-09-29 15:17:58 UTC
|
||||||
|
Pk algo: Ed25519
|
||||||
|
Pk size: 256 bits
|
||||||
|
Fingerprint: AAA18CBB254685C58358320563FD37B67F3300F9FB0EC457378CD29F102698B3
|
||||||
|
KeyID: AAA18CBB254685C5
|
||||||
|
|
||||||
|
Secret Key:
|
||||||
|
|
||||||
|
Unencrypted
|
||||||
|
|
||||||
|
00000000 c5 CTB
|
||||||
|
00000001 4b length
|
||||||
|
00000002 06 version
|
||||||
|
00000003 65 16 ea a6 creation_time
|
||||||
|
00000007 1b pk_algo
|
||||||
|
00000008 00 00 00 20 public_len
|
||||||
|
0000000c 53 24 e9 43 ed25519_public
|
||||||
|
00000010 af ab 15 f7 6e d5 b5 12 98 79 69 cd 1b 5d 10 65
|
||||||
|
00000020 eb e7 42 e2 ab 47 f4 86 b3 ae 65 3e
|
||||||
|
0000002c 00 s2k_usage
|
||||||
|
0000002d ef e1 99 ed25519_secret
|
||||||
|
00000030 b5 5f 11 fb aa 93 e8 26 9d 3b b2 2d 72 20 7d ff
|
||||||
|
00000040 bd 42 dd 4b e9 a3 36 81 3b a5 cc cf fb
|
||||||
|
```
|
||||||
|
|
||||||
|
The Secret-Key packet consists in large part of the actual cryptographic key data. Notice that its content is almost entirely the same as the Public-Key packet [seen in the previous chapter](public_key). Let's look at the packet field by field:
|
||||||
|
|
||||||
|
- `CTB: 0xc5`[^CTB]: The [packet type ID](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-packet-headers) for this packet. The binary representation of the value `0xc5` is `11000101`. Bits 7 and 6 show that the packet is in *OpenPGP packet format* (as opposed to in *Legacy packet format*). The remaining 6 bits encode the type ID's value: "5". This is the value for a Secret-Key packet, as shown in the list of [packet type IDs](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-packet-tags).
|
||||||
|
- `length: 0x4b`: The remaining length of this packet.
|
||||||
|
|
||||||
|
[^CTB]: Sequoia uses the term CTB (Cipher Type Byte) to refer to the RFC's [packet type ID](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-packet-headers). In previous versions, the RFC called this field "Packet Tag".
|
||||||
|
|
||||||
|
The packet type id defines the semantics of the remaining data in the packet. We're looking at a Secret-Key packet, which is a kind of [Key Material Packet](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-key-material-packets).
|
||||||
|
|
||||||
|
- `version: 0x06`: The key material is in version 6 format
|
||||||
|
|
||||||
|
This means that the next part of the packet follows the structure of [Version 6 Public Keys](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-version-6-public-keys)
|
||||||
|
|
||||||
|
- `creation_time: 0x6516eaa6`: "The time that the key was created" (also see [Time Fields](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-time-fields))
|
||||||
|
- `pk_algo: 0x1b`: "The public-key algorithm ID of this key" (decimal value 27, see the list of [Public-Key Algorithms](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-public-key-algorithms))
|
||||||
|
- `public_len: 0x00000020`: "Octet count for the following public key material" (in this case, the length of the following `ed25519_public` field)
|
||||||
|
- `ed25519_public`: [Algorithm-specific representation](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-algorithm-specific-part-for-ed2) of the public key material (the format is based on the value of `pk_algo`), in this case 32 bytes of Ed25519 public key
|
||||||
|
|
||||||
|
This concludes the Public Key section of the packet. The remaining data follows the [Secret-Key packet format](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-secret-key-packet-formats):
|
||||||
|
|
||||||
|
- `s2k_usage: 0x00`: The [*S2K usage* value](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-secret-key-encryption-s2k-u) of `0x00` specifies that the secret-key data is not encrypted
|
||||||
|
- `ed25519_secret`: [Algorithm-specific representation](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-algorithm-specific-part-for-ed2) of the secret key data (the format is based on the value of `pk_algo`). Because the private key material in this packet is not encrypted, this field
|
||||||
|
|
||||||
|
```{tip}
|
||||||
|
|
||||||
|
The overall structure of OpenPGP packets is described in the [Packet Syntax](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-packet-syntax) chapter of the RFC.
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that the *Secret-Key packet* contains both the private and the public part of the key.
|
||||||
|
|
||||||
|
### Secret-Subkey packet
|
||||||
|
|
||||||
|
Further down in the "packet dump" of Alice's key, we see the encryption subkey, which we already inspected in its Public-Subkey packet format, [above](zoom_enc_subkey):
|
||||||
|
|
||||||
|
```text
|
||||||
|
Secret-Subkey Packet, new CTB, 2 header bytes + 75 bytes
|
||||||
|
Version: 6
|
||||||
|
Creation time: 2023-09-29 15:17:58 UTC
|
||||||
|
Pk algo: X25519
|
||||||
|
Pk size: 256 bits
|
||||||
|
Fingerprint: C0A58384A438E5A14F73712426A4D45DBAEEF4A39E6B30B09D5513F978ACCA94
|
||||||
|
KeyID: C0A58384A438E5A1
|
||||||
|
|
||||||
|
Secret Key:
|
||||||
|
|
||||||
|
Unencrypted
|
||||||
|
|
||||||
|
00000000 c7 CTB
|
||||||
|
00000001 4b length
|
||||||
|
00000002 06 version
|
||||||
|
00000003 65 16 ea a6 creation_time
|
||||||
|
00000007 19 pk_algo
|
||||||
|
00000008 00 00 00 20 public_len
|
||||||
|
0000000c d1 ae 87 d7 x25519_public
|
||||||
|
00000010 cc 42 af 99 34 c5 c2 5c ca fa b7 4a c8 43 fc 86
|
||||||
|
00000020 35 2a 46 01 f3 cc 00 f5 4a 09 3e 3f
|
||||||
|
0000002c 00 s2k_usage
|
||||||
|
0000002d 28 7d cd x25519_secret
|
||||||
|
00000030 da 26 16 37 8d ea 24 c7 ce e7 70 c7 9b e5 6f 0a
|
||||||
|
00000040 c9 77 fb bd 23 41 73 c9 57 5a bf 7c 4c
|
||||||
|
```
|
||||||
|
|
||||||
|
Again, this packet consists of the same content as its Public-Subkey equivalent, followed by two additional fields:
|
||||||
|
|
||||||
|
- The "S2K usage" field, which indicated whether the private key material is encrypted. Like Alice's primary key (above), this subkey is not encrypted.
|
||||||
|
- The private key material: in this case, the algorithm-specific private key data consists of 32 bytes of `x25519_secret` data.
|
||||||
|
|
||||||
|
As with the public key material, the difference between the format of this subkey packet and the private key packet is minimal: Only the packet type ID differs.
|
||||||
|
|
||||||
|
## Bob's (encrypted) private key material
|
||||||
|
|
||||||
|
Now we look at the primary key material packet of [Bob's key](bob_priv), which uses passphrase protection.
|
||||||
|
|
||||||
|
```text
|
||||||
|
Secret-Key Packet, new CTB, 2 header bytes + 134 bytes
|
||||||
|
Version: 6
|
||||||
|
Creation time: 2023-10-13 14:29:00 UTC
|
||||||
|
Pk algo: Ed25519
|
||||||
|
Pk size: 256 bits
|
||||||
|
Fingerprint: BB289FB7A68DBFA8C384CCCDE2058E02D9C6CD2F3C7C56AE7FB53D971170BA83
|
||||||
|
KeyID: BB289FB7A68DBFA8
|
||||||
|
|
||||||
|
Secret Key:
|
||||||
|
|
||||||
|
Encrypted
|
||||||
|
S2K: Argon2id with t: 1, p: 4, m: 2^21, salt: 3B7F4B0EAC8B39625AB4D4BD690413C7 Sym. algo: AES-256
|
||||||
|
|
||||||
|
00000000 c5 CTB
|
||||||
|
00000001 86 length
|
||||||
|
00000002 06 version
|
||||||
|
00000003 65 29 54 2c creation_time
|
||||||
|
00000007 1b pk_algo
|
||||||
|
00000008 00 00 00 20 public_len
|
||||||
|
0000000c 47 e7 c2 dc ed25519_public
|
||||||
|
00000010 58 8e cb fd f2 49 90 66 ae aa 36 66 ca a9 55 2d
|
||||||
|
00000020 71 88 7c 25 91 c3 75 73 1d 07 60 d6
|
||||||
|
0000002c fe s2k_usage
|
||||||
|
0000002d 16 parameters_len
|
||||||
|
0000002e 09 sym_algo
|
||||||
|
0000002f 14 s2k_len
|
||||||
|
00000030 04 s2k_type
|
||||||
|
00000031 3b 7f 4b 0e ac 8b 39 62 5a b4 d4 bd 69 04 13 argon2_salt
|
||||||
|
00000040 c7
|
||||||
|
00000041 01 argon2_t
|
||||||
|
00000042 04 argon2_p
|
||||||
|
00000043 15 argon2_m
|
||||||
|
00000044 21 ff be fc f1 c5 9c 75 9d 1f d1 f8 encrypted_mpis
|
||||||
|
00000050 19 e7 fd 47 55 e3 69 ff 2f e8 52 48 66 03 d3 37
|
||||||
|
00000060 52 7b 05 cb fa b1 f8 13 f7 f6 20 88 d6 f5 8b c4
|
||||||
|
00000070 b4 51 52 ba 6d f9 7c 1a ee 9f e6 b1 fb 63 d1 ca
|
||||||
|
00000080 4a 3f 33 d9 2c c9 26 46
|
||||||
|
```
|
||||||
|
|
||||||
|
The first portion of Bob's Secret-Key packet has the same structure as Alice's, but beginning at the `s2k_usage`, we see different data. The format of this data is described in [Secret-Key Packet Formats](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-secret-key-packet-formats).
|
||||||
|
|
||||||
|
- `s2k_usage: 0xfe`: [S2K usage](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-secret-key-encryption-s2k-u) is set to `AEAD`, here (decimal value 253).
|
||||||
|
- `parameters_len: 0x16` (decimal value: 22): "Cumulative length of all the following conditionally included string-to-key parameter fields."
|
||||||
|
- `sym_algo: 0x9`: [Symmetric-Key Algorithm](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-symmetric-key-algorithms) specifies that AES 256 is used as the AEAD algorithm
|
||||||
|
- `s2k_len: 0x14` (decimal value 20): "[..] count of the size of the one field following this octet"
|
||||||
|
|
||||||
|
The next set of data is the "string-to-key (S2K) specifier." Its format depends on the type.
|
||||||
|
|
||||||
|
- `s2k_type: 0x04` [String-to-Key (S2K) Specifier Type](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-string-to-key-s2k-specifier-), set to *Argon2* here.
|
||||||
|
|
||||||
|
The next fields are [specific to Argon2](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-11.html#name-argon2):
|
||||||
|
|
||||||
|
- `argon2_salt`: "16-octet salt value"
|
||||||
|
- `argon2_t`: "number of passes t"
|
||||||
|
- `argon2_p`: "degree of parallelism p"
|
||||||
|
- `argon2_m`: "the exponent of the memory size"
|
||||||
|
|
||||||
|
```{admonition} TODO
|
||||||
|
:class: warning
|
||||||
|
|
||||||
|
Where is that IV:
|
||||||
|
|
||||||
|
["If string-to-key usage octet was 253 (AEAD), an initialization vector (IV) of size specified by the AEAD algorithm (see Section 5.13.2), which is used as the nonce for the AEAD algorithm"](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-11.html#section-5.5.3-3.4.2.5)
|
||||||
|
|
||||||
|
Is the example key wrong?!
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
"Plain or encrypted multiprecision integers comprising the secret key data. This is algorithm-specific and described in Section 5.5.5. If the string-to-key usage octet is 253 (AEAD), then an AEAD authentication tag is at the end of that data."
|
1
book/source/19-zoom_signatures.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
# Zooming in: Packet structure of data signatures
|
1
book/source/20-zoom_encyption.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
# Zooming in: Packet structure of encrypted data
|
|
@ -9,6 +9,9 @@ Authentication
|
||||||
Certification
|
Certification
|
||||||
"Third party Signature" on a certificate, making a statement about that certificate, or an identity in the certificate
|
"Third party Signature" on a certificate, making a statement about that certificate, or an identity in the certificate
|
||||||
|
|
||||||
|
CA
|
||||||
|
[Certificate authority](https://en.wikipedia.org/wiki/Certificate_authority) or certification authority. An entity that handles digital certificates, especially by signing or issuing them.
|
||||||
|
|
||||||
Delegation
|
Delegation
|
||||||
See {term}`Trust signature`
|
See {term}`Trust signature`
|
||||||
|
|
|
@ -40,3 +40,51 @@ FcsHp6K860qG87pDlk2saQIwzXPmQcQvLokbeQMB
|
||||||
=DfxN
|
=DfxN
|
||||||
-----END PGP PRIVATE KEY BLOCK-----
|
-----END PGP PRIVATE KEY BLOCK-----
|
||||||
```
|
```
|
||||||
|
|
||||||
|
(bob_priv)=
|
||||||
|
## Bob's OpenPGP key
|
||||||
|
|
||||||
|
Bob uses passphrase-protected secret key packets. His passphrase is `password`:
|
||||||
|
|
||||||
|
```text
|
||||||
|
-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||||
|
Comment: BB28 9FB7 A68D BFA8 C384 CCCD E205 8E02 D9C6 CD2F 3C7C 56AE 7FB5 3D97 1170 BA83
|
||||||
|
Comment: Bob Baker <bob@example.org>
|
||||||
|
|
||||||
|
xYYGZSlULBsAAAAgR+fC3FiOy/3ySZBmrqo2ZsqpVS1xiHwlkcN1cx0HYNb+FgkU
|
||||||
|
BDt/Sw6sizliWrTUvWkEE8cBBBUh/7788cWcdZ0f0fgZ5/1HVeNp/y/oUkhmA9M3
|
||||||
|
UnsFy/qx+BP39iCI1vWLxLRRUrpt+Xwa7p/msftj0cpKPzPZLMkmRsK2Bh8bCgAA
|
||||||
|
AD0FgmUpVCwFiQWkj70DCwkHAxUKCAKbAQIeASIhBrson7emjb+ow4TMzeIFjgLZ
|
||||||
|
xs0vPHxWrn+1PZcRcLqDAAAACgkQuyift6aNv6hmsCBGbcyDxHuoq4DTmpzhwxCo
|
||||||
|
Pq37LydspnltmjW6ZlSTLOc7dt+MiSxAUIqH9i0CQVV9cyQfc0Gi7YzjnvQ9RxcZ
|
||||||
|
raou5c0b126fZ6Rt2vzLHICGw3v7dpCAnR0Y2lUvaAXNG0JvYiBCYWtlciA8Ym9i
|
||||||
|
QGV4YW1wbGUub3JnPsK5BhMbCgAAAEAFgmUpVCwFiQWkj70DCwkHAxUKCAKZAQKb
|
||||||
|
AQIeASIhBrson7emjb+ow4TMzeIFjgLZxs0vPHxWrn+1PZcRcLqDAAAACgkQuyif
|
||||||
|
t6aNv6h76CB+0O5ke9ijamCxuAz9FHaMDN+l+mQrTYFTLCpGpkWIta+yHy3YdGog
|
||||||
|
5o5KzDQPrSART32y2dRKQci/49rafLDEqHfPzhEPwwcKWjJxpEpA+AUR+r0WuAh0
|
||||||
|
dRzT5vjPJwLHhgZlKVQsGQAAACAx8dR3SX4a2pudy0Fkzz8IkVhI+iIICfcKe8FC
|
||||||
|
HBUOFP4WCRQEw0VAyFbfmLAChDiHM714gQEEFcXLCpUyt+CPel5FO0wVibtGYRHr
|
||||||
|
pFEH/iCz7VYAup7lgerjiqTWdla37S+cAra9XduruJUZ3XS+L4bhYZTiCe2Jn8Fd
|
||||||
|
wqsGGBsKAAAAMgWCZSlULAWJBaSPvQKbDCIhBrson7emjb+ow4TMzeIFjgLZxs0v
|
||||||
|
PHxWrn+1PZcRcLqDAAAACgkQuyift6aNv6ggfiDu7s2cKnNx1vn17XV99XFo+DVe
|
||||||
|
Z/MQBOIbZ7bQz2ufS4PIjnC62/oybvC+GeNcnD8kOYfwtxPtl6DQdbpHYyqgNO21
|
||||||
|
RMq9oNvei4tKmh7gg6jXGrmWKT6yOIPZyqpQUg/HhgZlKVQsGwAAACAGelHxA+Uz
|
||||||
|
4M73R7YTo7Xjg3KKoLmc/BYWA/QZ3noQNP4WCRQEG4OjK8N9PVQioBCJ848J8QEE
|
||||||
|
FRjFFaC6UN5LB6wyCqvozRo/e069dUiwlnYssBNPINsXQiBPcxmoSbyxVRF7LR9G
|
||||||
|
BZy9/bVQw9ZyPKtbvBQEQKy4m1qvwsCFBhgbCgAAAMwFgmUpVCwFiQWkj70CmwKZ
|
||||||
|
oAYZGwoAAAApBYJlKVQsIiEGLJuY+NkqRYws6tuzQ2Bff3TWsiqA7IzIPzpNxQjJ
|
||||||
|
+pUAAAAA6LUg9nuvXbKHUCoGMAdiVV/ttYcO583925/m/T3nC/CNNShitGiBRNAp
|
||||||
|
HnGyQKVkROyzYznyA9jCF+Ck1jeOCb5nQ7PwxHxRuP4ZG0uRN23pQh4eM2F7V/2F
|
||||||
|
iOkRF9lAM0AEIiEGuyift6aNv6jDhMzN4gWOAtnGzS88fFauf7U9lxFwuoMAAAAK
|
||||||
|
CRC7KJ+3po2/qBhPIIPtGEG7TzgO0gXQjhlx9hNBdKxAzScMwRT7oAT3RZrG5hGH
|
||||||
|
oyvf2n86URceCnfYSwZSOij7CfD0ZJgDmNmvJ5//yx7I7M4YCCheAd5er3/eaF6O
|
||||||
|
VJt3Ui/pv5VuXLTRC8eGBmUpVCwbAAAAIFnzWg9EBmVGFMUClhrtT5DNdCf+A4OQ
|
||||||
|
90WbiTHnseuR/hYJFAT5Ylewq+lINPw46gwA5Z6eAQQViAjElYeZobbZ+D001l/M
|
||||||
|
QvHaiEbEIXadwP3bbjoM43rFoP+p8cNYYECYAL8sx34uIxeixwrL6aOZ8j6Y1zbP
|
||||||
|
C8jTYNrCqwYYGwoAAAAyBYJlKVQsBYkFpI+9ApsgIiEGuyift6aNv6jDhMzN4gWO
|
||||||
|
AtnGzS88fFauf7U9lxFwuoMAAAAKCRC7KJ+3po2/qLHdID+av7QZ75Fq4v9YVHpc
|
||||||
|
wVXtKDX+MOKJM4xz7RvBWErH2xWyqikNZQVuzz/WqOVH/nT+BcqmLWAe3yjrTE4B
|
||||||
|
hSfrR38Nk23E4Bu4HobVrg7rlMU6SKHRWKeX/iSUmr6GDA==
|
||||||
|
=UZBq
|
||||||
|
-----END PGP PRIVATE KEY BLOCK-----
|
||||||
|
```
|
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 110 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 82 KiB |
|
@ -7,7 +7,7 @@
|
||||||
viewBox="-12 -8 126.419 102.162"
|
viewBox="-12 -8 126.419 102.162"
|
||||||
version="1.1"
|
version="1.1"
|
||||||
id="svg895"
|
id="svg895"
|
||||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
inkscape:version="1.3 (0e150ed6c4, 2023-07-21)"
|
||||||
sodipodi:docname="user_id_certification.svg"
|
sodipodi:docname="user_id_certification.svg"
|
||||||
inkscape:export-filename="user_id_certification.png"
|
inkscape:export-filename="user_id_certification.png"
|
||||||
inkscape:export-xdpi="128"
|
inkscape:export-xdpi="128"
|
||||||
|
@ -29,14 +29,15 @@
|
||||||
showgrid="false"
|
showgrid="false"
|
||||||
inkscape:zoom="2.2417025"
|
inkscape:zoom="2.2417025"
|
||||||
inkscape:cx="238.43485"
|
inkscape:cx="238.43485"
|
||||||
inkscape:cy="211.00034"
|
inkscape:cy="210.7773"
|
||||||
inkscape:window-width="1920"
|
inkscape:window-width="1920"
|
||||||
inkscape:window-height="1001"
|
inkscape:window-height="1121"
|
||||||
inkscape:window-x="0"
|
inkscape:window-x="0"
|
||||||
inkscape:window-y="0"
|
inkscape:window-y="0"
|
||||||
inkscape:window-maximized="1"
|
inkscape:window-maximized="1"
|
||||||
inkscape:current-layer="layer1"
|
inkscape:current-layer="layer1"
|
||||||
showguides="true">
|
showguides="true"
|
||||||
|
inkscape:export-bgcolor="#ffffffff">
|
||||||
<sodipodi:guide
|
<sodipodi:guide
|
||||||
position="53.301136,55.911635"
|
position="53.301136,55.911635"
|
||||||
orientation="1,0"
|
orientation="1,0"
|
||||||
|
@ -104,7 +105,8 @@
|
||||||
id="layer1"
|
id="layer1"
|
||||||
transform="translate(-21.366721,-71.748822)">
|
transform="translate(-21.366721,-71.748822)">
|
||||||
<g
|
<g
|
||||||
id="g19460">
|
id="g19460"
|
||||||
|
transform="translate(-3.7041668)">
|
||||||
<rect
|
<rect
|
||||||
style="fill:#0f6cdb;fill-opacity:0.432801;stroke:none;stroke-width:0.172657;stroke-opacity:0.3512"
|
style="fill:#0f6cdb;fill-opacity:0.432801;stroke:none;stroke-width:0.172657;stroke-opacity:0.3512"
|
||||||
id="rect2546"
|
id="rect2546"
|
||||||
|
@ -294,69 +296,52 @@
|
||||||
<text
|
<text
|
||||||
xml:space="preserve"
|
xml:space="preserve"
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82222px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82222px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
||||||
x="94.823761"
|
x="110.48965"
|
||||||
y="82.180534"
|
y="76.269455"
|
||||||
id="text4813-3-6"><tspan
|
id="text4813-3-6"><tspan
|
||||||
sodipodi:role="line"
|
sodipodi:role="line"
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82222px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82222px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
||||||
x="94.823761"
|
x="110.48965"
|
||||||
y="82.180534"
|
y="76.269455"
|
||||||
id="tspan2143-5-0"> Primary key</tspan><tspan
|
id="tspan2143-5-0"> Primary key</tspan><tspan
|
||||||
sodipodi:role="line"
|
sodipodi:role="line"
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82222px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82222px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
||||||
x="94.823761"
|
x="110.48965"
|
||||||
y="85.708313"
|
y="79.797234"
|
||||||
id="tspan13837"> creates a</tspan><tspan
|
id="tspan13837"> issues</tspan><tspan
|
||||||
sodipodi:role="line"
|
sodipodi:role="line"
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82222px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82222px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
||||||
x="94.823761"
|
x="110.48965"
|
||||||
y="89.236084"
|
y="83.325005"
|
||||||
id="tspan13839"> signature</tspan><tspan
|
id="tspan13839"> signature</tspan><tspan
|
||||||
sodipodi:role="line"
|
sodipodi:role="line"
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82222px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82222px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
||||||
x="94.823761"
|
x="110.48965"
|
||||||
y="92.763863"
|
y="86.852783"
|
||||||
id="tspan13841"> to associate</tspan><tspan
|
id="tspan13841"> to associate</tspan><tspan
|
||||||
sodipodi:role="line"
|
sodipodi:role="line"
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82222px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82222px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
||||||
x="94.823761"
|
x="110.48965"
|
||||||
y="96.291634"
|
y="90.380554"
|
||||||
id="tspan18684"> the User ID</tspan><tspan
|
id="tspan18684"> the User ID</tspan><tspan
|
||||||
sodipodi:role="line"
|
sodipodi:role="line"
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82222px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82222px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
||||||
x="94.823761"
|
x="110.48965"
|
||||||
y="99.819412"
|
y="93.908333"
|
||||||
id="tspan18686"> with itself</tspan></text>
|
id="tspan18686"> with itself</tspan></text>
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82222px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
x="85.173096"
|
|
||||||
y="163.16611"
|
|
||||||
id="text4813-3-7-3"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82222px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
x="85.173096"
|
|
||||||
y="163.16611"
|
|
||||||
id="tspan602-7">Certification for User ID</tspan></text>
|
|
||||||
<path
|
<path
|
||||||
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Lend-0-0)"
|
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Lend-0-0)"
|
||||||
d="m 82.546006,121.30183 c -8.633736,3.32714 -0.18306,0.0128 -9.043454,3.61437"
|
d="m 82.546006,121.30183 c -8.633736,3.32714 -0.18306,0.0128 -9.043454,3.61437"
|
||||||
id="path3976-5-3"
|
id="path3976-5-3"
|
||||||
inkscape:connector-curvature="0"
|
inkscape:connector-curvature="0"
|
||||||
sodipodi:nodetypes="cc" />
|
sodipodi:nodetypes="cc" />
|
||||||
<path
|
|
||||||
style="fill:none;stroke:#fe0700;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Lend-0-59422)"
|
|
||||||
d="M 86.003171,75.510639 C 93.077122,75.7709 98.387082,82.8893 98.039172,109.26913"
|
|
||||||
id="path3976-5-5"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="cc" />
|
|
||||||
<rect
|
<rect
|
||||||
style="font-variation-settings:'wght' 700;fill:#fdfec4;fill-opacity:1;stroke:#000000;stroke-width:0.284444;stroke-dasharray:none"
|
style="font-variation-settings:'wght' 700;fill:#fdfec4;fill-opacity:1;stroke:#000000;stroke-width:0.330729;stroke-dasharray:none"
|
||||||
id="rect481"
|
id="rect481"
|
||||||
width="50.312313"
|
width="50.266029"
|
||||||
height="49.749325"
|
height="67.318695"
|
||||||
x="83.159416"
|
x="83.182564"
|
||||||
y="110.43027" />
|
y="96.4897" />
|
||||||
<text
|
<text
|
||||||
xml:space="preserve"
|
xml:space="preserve"
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82222px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82222px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
@ -376,73 +361,106 @@
|
||||||
<text
|
<text
|
||||||
xml:space="preserve"
|
xml:space="preserve"
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
||||||
x="84.29808"
|
x="83.898354"
|
||||||
y="114.02528"
|
y="100.36494"
|
||||||
id="text4813-35-6"><tspan
|
id="text4813-35-6"><tspan
|
||||||
sodipodi:role="line"
|
sodipodi:role="line"
|
||||||
id="tspan4811-6-2"
|
id="tspan4811-6-2"
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
||||||
x="84.29808"
|
x="83.898354"
|
||||||
y="114.02528">Metadata associated</tspan><tspan
|
y="100.36494">Signature Over:</tspan><tspan
|
||||||
sodipodi:role="line"
|
sodipodi:role="line"
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
||||||
x="84.29808"
|
x="83.898354"
|
||||||
y="118.43501"
|
y="104.77467"
|
||||||
|
id="tspan3">Primary Key,</tspan><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
x="83.898354"
|
||||||
|
y="109.18439"
|
||||||
|
id="tspan4">+ User ID</tspan><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
x="83.898354"
|
||||||
|
y="113.59412"
|
||||||
|
id="tspan1" /><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
x="83.898354"
|
||||||
|
y="118.00385"
|
||||||
|
id="tspan2">+ Metadata associated</tspan><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
x="83.898354"
|
||||||
|
y="122.41357"
|
||||||
id="tspan14771"> with this Signature:</tspan><tspan
|
id="tspan14771"> with this Signature:</tspan><tspan
|
||||||
sodipodi:role="line"
|
sodipodi:role="line"
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
||||||
x="84.29808"
|
x="83.898354"
|
||||||
y="122.84473"
|
y="126.8233"
|
||||||
id="tspan14773" /><tspan
|
id="tspan14773" /><tspan
|
||||||
sodipodi:role="line"
|
sodipodi:role="line"
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
||||||
x="84.29808"
|
x="83.898354"
|
||||||
y="127.25446"
|
y="131.23302"
|
||||||
id="tspan14777">- creation time</tspan><tspan
|
id="tspan14777">- creation time</tspan><tspan
|
||||||
sodipodi:role="line"
|
sodipodi:role="line"
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
||||||
x="84.29808"
|
x="83.898354"
|
||||||
y="131.66418"
|
y="135.64275"
|
||||||
id="tspan19941">- algorithm preferences</tspan><tspan
|
id="tspan19941">- algorithm preferences</tspan><tspan
|
||||||
sodipodi:role="line"
|
sodipodi:role="line"
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
||||||
x="84.29808"
|
x="83.898354"
|
||||||
y="136.07391"
|
y="140.05247"
|
||||||
id="tspan13456">- key expiration time</tspan><tspan
|
id="tspan13456">- key expiration time</tspan><tspan
|
||||||
sodipodi:role="line"
|
sodipodi:role="line"
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
||||||
x="84.29808"
|
x="83.898354"
|
||||||
y="140.48363"
|
y="144.46219"
|
||||||
id="tspan2111-9-1">- "Primary User ID" flag</tspan><tspan
|
id="tspan2111-9-1">- "Primary User ID" flag</tspan><tspan
|
||||||
sodipodi:role="line"
|
sodipodi:role="line"
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
||||||
x="84.29808"
|
x="83.898354"
|
||||||
y="144.89336"
|
y="148.87192"
|
||||||
id="tspan19939" /><tspan
|
id="tspan19939" /><tspan
|
||||||
sodipodi:role="line"
|
sodipodi:role="line"
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
||||||
x="84.29808"
|
x="83.898354"
|
||||||
y="149.30309"
|
y="153.28165"
|
||||||
id="tspan19937">- primary key flags</tspan><tspan
|
id="tspan19937">- primary key flags</tspan><tspan
|
||||||
sodipodi:role="line"
|
sodipodi:role="line"
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
||||||
x="84.29808"
|
x="83.898354"
|
||||||
y="153.71281"
|
y="157.69138"
|
||||||
id="tspan19943">- primary key expiration time</tspan><tspan
|
id="tspan19943">- primary key expiration time</tspan><tspan
|
||||||
sodipodi:role="line"
|
sodipodi:role="line"
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
||||||
x="84.29808"
|
x="83.898354"
|
||||||
y="158.12253"
|
y="162.10109"
|
||||||
id="tspan19935">- ...</tspan><tspan
|
id="tspan19935">- ...</tspan><tspan
|
||||||
sodipodi:role="line"
|
sodipodi:role="line"
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.52778px;font-family:'Courier 10 Pitch';-inkscape-font-specification:'Courier 10 Pitch, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
||||||
x="84.29808"
|
x="83.898354"
|
||||||
y="162.53226"
|
y="166.51082"
|
||||||
id="tspan2137-1-2" /><tspan
|
id="tspan2137-1-2" /><tspan
|
||||||
sodipodi:role="line"
|
sodipodi:role="line"
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82222px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82222px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-dasharray:none;stroke-opacity:1"
|
||||||
x="84.29808"
|
x="83.898354"
|
||||||
y="166.94199"
|
y="170.92055"
|
||||||
id="tspan2143-2-7" /></text>
|
id="tspan2143-2-7" /></text>
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#fe0700;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Lend-0-59422)"
|
||||||
|
d="m 86.003171,75.510639 c 1.150889,0.814439 25.217599,7.208996 31.769709,26.613201"
|
||||||
|
id="path3976-5-5"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<ellipse
|
||||||
|
style="fill:#ff0000;stroke:#000000;stroke-width:0.2"
|
||||||
|
id="path8"
|
||||||
|
cx="119.26576"
|
||||||
|
cy="106.12103"
|
||||||
|
rx="3.9128182"
|
||||||
|
ry="3.7064736" />
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 35 KiB |
BIN
book/source/diag/user_ids.png
Normal file
After Width: | Height: | Size: 45 KiB |
499
book/source/diag/user_ids.svg
Normal file
After Width: | Height: | Size: 46 KiB |
|
@ -1,8 +1,10 @@
|
||||||
# OpenPGP for application developers, an introduction
|
# OpenPGP for application developers
|
||||||
|
|
||||||
|
**{sub-ref}`today`**
|
||||||
|
|
||||||
```{toctree}
|
```{toctree}
|
||||||
:numbered:
|
:numbered:
|
||||||
:maxdepth: 3
|
:maxdepth: 2
|
||||||
:glob:
|
:glob:
|
||||||
|
|
||||||
*
|
*
|
||||||
|
|