27. Zooming in: Packet structure of private key material

27.1. A look at Alice’s (unencrypted) private key packets

Let’s take a look at the key material packets of Alice’s key.

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:

$ sq packet dump --hex alice.priv

27.1.1. Primary Secret-Key packet

The output starts with the (primary) Secret-Key packet.

This is the structure of the Secret-Key packet we will now look at.

Depicts a box with white background and title "Secret-Key packet". In the center a box with white background and red frame is shown. Inside it several items are listed, separated by red dotted horizontal lines. The first three are "Version", "Creation Time", "Public-Key Algorithm" written in black. The fourth one is written in green and reads "Public Key Material" and has the green public key symbol at its right side. The fifth one is again written in black and reads "S2K Usage (Secret Key Encryption)". The sixth item reads "Secret Key Material", written in red and has the red private key symbol at its right side.

Fig. 44 Structure of a Secret-Key packet.

The output of Sequoia’s sq packet dump for this packet:

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:


    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. Let’s look at the packet field by field:

  • CTB: 0xc5[1]: The packet type ID 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.

  • length: 0x4b: 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 Secret-Key packet, which is a kind of Key Material Packet.

  • 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

  • creation_time: 0x6516eaa6: “The time that the key was created” (also see Time Fields)

  • pk_algo: 0x1b: “The public-key algorithm ID of this key” (decimal value 27, see the list of 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 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:

  • s2k_usage: 0x00: The S2K usage value of 0x00 specifies that the secret-key data is not encrypted

  • ed25519_secret: Algorithm-specific representation 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


The overall structure of OpenPGP packets is described in the Packet Syntax chapter of the RFC.

Note that the Secret-Key packet contains both the private and the public part of the key.

27.1.2. 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:

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:


  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.

27.2. Bob’s (encrypted) private key material

Now we look at the primary key material packet of Bob’s key, which uses passphrase protection.

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:

        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.

  • s2k_usage: 0xfe: S2K usage 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 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.

The next fields are specific to 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”

“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.”