diff --git a/book/source/04-certificates.md b/book/source/04-certificates.md index d0ec862..e4c587b 100644 --- a/book/source/04-certificates.md +++ b/book/source/04-certificates.md @@ -244,30 +244,49 @@ Minimized versions, merging, effective "append only" semantics, ... 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). -## Zooming in: Package structure +## Zooming in: Packet structure -To use OpenPGP, we need "(OpenPGP) keys." +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. -There is an ongoing effort to establish new terminology around "keys." In particular, to use the term "certificate" instead of "(OpenPGP) public key." +### A very minimal OpenPGP "Transferrable Secret Key" -Note: there is also the related, but distinct, concept of [cryptographic "keys"](https://en.wikipedia.org/wiki/Key_(cryptography)). OpenPGP certificates/keys contain one or more cryptographic key(s), among many other components. +We'll start with a very minimal version of [](alice_priv), stored as a "Transferrable Secret Key" (that is, including private key material). -An OpenPGP certificate/key consists of a number of elements, many of them optional. OpenPGP certificates/keys always make use of [Public-key cryptography (asymmetric cryptography)](https://en.wikipedia.org/wiki/Public-key_cryptography). +One way to produce this minimal version of Alice's key is: -As a consequence, some elements of OpenPGP certificates/keys represent "private" (sometimes referred to as "secret") key material, while other elements represent "public" key material. Yet other elements contain metadata, and finally there are elements that serve as glue ("binding") between the various other elements of a certificate. +```text +$ sq packet split alice.priv +``` -To hand out copies of one's OpenPGP key to third parties, implementations can generate a "certificate" / "public key" representation ([Transferable Public Keys](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#transferable-public-keys) in the RFC), which consists of all the elements of the certificate, except for the private key material (and the optional [S2K configuration](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-string-to-key-s2k-specifier)). +With this command, `sq` generates a set of files, one for each packet in `alice.priv`: -The counterpart is called [Transferable Secret Keys](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#transferable-secret-keys) in the RFC. That is, an OpenPGP certificate that includes private key material. +```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: -### A minimal OpenPGP key +```text +$ sq packet join alice.priv-0--SecretKey alice.priv-1--Signature --output alice_minimal.priv +``` -A minimal OpenPGP key consists of the Secret-Key Packet for the primary key, and a self-certification (the Direct Key Signature). +This version of Alice's key contains just two packets: -#### Seen as a private OpenPGP key - -A minimal version of [Alice's private key](alice_priv) (in ASCII-armored representation) looks like this: +- 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----- @@ -283,10 +302,19 @@ ECaYswAAAAoJEKqhjLslRoXFZ0cgouNjgeNr0E9W18g4gAIl6FM5SWuQxg12j0S0 -----END PGP PRIVATE KEY BLOCK----- ``` -If we decode this OpenPGP data, we see that the key is made up of a sequence of two packets: +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 @@ -312,7 +340,45 @@ KeyID: AAA18CBB254685C5 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 this value 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" +- `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`) + +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`: Specified 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" + +```{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 @@ -380,20 +446,61 @@ Level: 0 (signature over data) 000000b0 54 01 f9 5f 81 41 90 0e ``` -* First, a [*"Secret-Key Packet"*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#seckey), which contains the actual cryptographic key data. Note: the "Secret-Key" Packet contains both the private and the public part of the key. We also see in the output that this packet is "Unencrypted" (i.e. not password-protected). -* Second, a [*"Direct Key Signature"*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#sigtype-direct-key) (type 0x1F), *"Signature directly on a key"*. This packet *"binds the information in the Signature subpackets to the key"*. Each entry under "Signature Packet -> Hashed area" is one Signature subpacket, including for example information about algorithm preferences (*"Symmetric algo preferences"* and *"Hash preferences"*). +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) + +- [Signature Creation Time](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#signature-creation-subpacket) (type 2) *critical*: `0x6516eaa6` +- [Key Expiration Time](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#key-expiration-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` +- [Preferred Hash Algorithms](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#preferred-hashes-subpacket) (type 21): `0x0a 0x08` +- [Key Flags](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#key-flags) (type 27) *critical*: `0x01` +- [Features](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#features-subpacket) (type 30): `0x01` +- [Issuer Fingerprint](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#issuer-fingerprint-subpacket) (type 33): `aaa18cbb254685c58358320563fd37b67f3300f9fb0ec457378cd29f102698b3` + +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) (type 16): `aaa18cbb254685c5` + +This concludes the unhashed subpacket data. + +- `digest_prefix: 0x6747`: "The left 16 bits of the signed hash value" +- `salt_len, salt`: A random value with [matching size](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#hash-algorithms-registry) for the hash algorithm +- `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 ```{figure} diag/key-minimal.png ---- ---- +:width: 40% + A minimal OpenPGP key, visualized ``` -#### Seen as a public certificate +### Seen as an OpenPGP certificate -Let's compare this with the same certificate seen as an armored OpenPGP certificate (that is, a "public key" variant of the key above, but without the private key material. An OpenPGP user might give such a certificate to a communication partner, so that the remote party could send encrypted messages to the user): +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 bits. + +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 @@ -502,13 +609,16 @@ The public certificate uses the packet type "Public-Key Packet" instead of "Secr s2k is used when the secret key material is password-protected. ```{figure} diag/pubcert-minimal.png ---- ---- +:width: 40% + A minimal OpenPGP public certificate, visualized ``` In the following examples, we will look at OpenPGP private keys only. The corresponding public certificates are easy to imagine (just leave out the private key material). +### Subkeys + +From here on, we'll look at the dumps in shorter format (you can see more detail by copying the certificates into the web-dumper at https://dump.sequoia-pgp.org/ and checking the "HexDump" checkbox). ### User IDs @@ -717,10 +827,6 @@ Instead of two packets, as before, we see four packets in this certificate: * 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) -### Subkeys - -From here on, we'll look at the dumps in shorter format (you can see more detail by copying the certificates into the web-dumper at https://dump.sequoia-pgp.org/ and checking the "HexDump" checkbox). - ### Certifications (Third Party Signatures) ### Revocations