mirror of
https://codeberg.org/openpgp/notes.git
synced 2024-11-23 16:12:05 +01:00
187 lines
12 KiB
Markdown
187 lines
12 KiB
Markdown
|
# 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."
|