From 8673e3c65797a72671fdf00932a0f13e43b62405 Mon Sep 17 00:00:00 2001 From: Heiko Schaefer Date: Sat, 18 Nov 2023 22:54:24 +0100 Subject: [PATCH 01/33] packet dump of seipd v2 encrypted message --- book/source/21-zoom_encyption.md | 72 ++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/book/source/21-zoom_encyption.md b/book/source/21-zoom_encyption.md index 79880f2..831e57d 100644 --- a/book/source/21-zoom_encyption.md +++ b/book/source/21-zoom_encyption.md @@ -4,3 +4,75 @@ SPDX-License-Identifier: CC-BY-SA-4.0 --> # Zooming in: Packet structure of encrypted data + +## SEIPD v2 + +We encrypt a short message to Alice, using a public certificate version of {ref}`alice_priv`: + +```text +$ echo "hello world" | sq encrypt --recipient-file alice.pub +``` + +This produces an ASCII armored encrypted message: + +```text +-----BEGIN PGP MESSAGE----- + +wV0GIQbApYOEpDjloU9zcSQmpNRduu70o55rMLCdVRP5eKzKlBkxYaQzuusD78oj +AdiGwQ8MI8sSAXAV4AEMKIcbINqhIBgSm5EV9h+Yl/XV3fEZ1JOaBnrtso2ZAS7S +cgIHAQaWc/Ip4Thq0EZDZwlpRUk/TUL+TWEpsGdQs8ifDyFAk7t3+3XvvLr5dUg3 ++Ot+sESkCSjhrZk50HIjwjBVZ6Y159yfaOqttMT6cXqWaxIishPaJ+OR1q2bZS1N +2jFbaROOcbASK6AVzqCWneqkIA== +=WFpq +-----END PGP MESSAGE----- +``` + +Inspecting the packets of this message, we see: + +```text +$ sq packet dump --hex enc.pgp +Public-Key Encrypted Session Key Packet, new CTB, 2 header bytes + 93 bytes + Version: 6 + Recipient: C0A58384A438E5A14F73712426A4D45DBAEEF4A39E6B30B09D5513F978ACCA94 + Pk algo: X25519 + + 00000000 c1 CTB + 00000001 5d length + 00000002 06 version + 00000003 21 recipient_len + 00000004 06 recipient_version + 00000005 c0 a5 83 84 a4 38 e5 a1 4f 73 71 recipient + 00000010 24 26 a4 d4 5d ba ee f4 a3 9e 6b 30 b0 9d 55 13 + 00000020 f9 78 ac ca 94 + 00000025 19 pk_algo + 00000026 31 61 a4 33 ba eb 03 ef ca 23 x25519_e + 00000030 01 d8 86 c1 0f 0c 23 cb 12 01 70 15 e0 01 0c 28 + 00000040 87 1b 20 da a1 20 + 00000046 18 x25519_esk_len + 00000047 12 9b 91 15 f6 1f 98 97 f5 x25519_esk + 00000050 d5 dd f1 19 d4 93 9a 06 7a ed b2 8d 99 01 2e + +Sym. Encrypted and Integrity Protected Data Packet, new CTB, 2 header bytes + 114 bytes + Version: 2 + Symmetric algo: AES-128 + AEAD algo: EAX + Chunk size: 4096 + Salt: 9673F229E1386AD0464367096945493F4D42FE4D6129B06750B3C89F0F214093 + No session key supplied + + 00000000 d2 CTB + 00000001 72 length + 00000002 02 version + 00000003 07 sym_algo + 00000004 01 aead_algo + 00000005 06 chunk_size + 00000006 96 73 f2 29 e1 38 6a d0 46 43 salt + 00000010 67 09 69 45 49 3f 4d 42 fe 4d 61 29 b0 67 50 b3 + 00000020 c8 9f 0f 21 40 93 + 00000026 bb 77 fb 75 ef bc ba f9 75 48 .w.u....uH + 00000030 37 f8 eb 7e b0 44 a4 09 28 e1 ad 99 39 d0 72 23 7..~.D..(...9.r# + 00000040 c2 30 55 67 a6 35 e7 dc 9f 68 ea ad b4 c4 fa 71 .0Ug.5...h.....q + 00000050 7a 96 6b 12 22 b2 13 da 27 e3 91 d6 ad 9b 65 2d z.k."...'.....e- + 00000060 4d da 31 5b 69 13 8e 71 b0 12 2b a0 15 ce a0 96 M.1[i..q..+..... + 00000070 9d ea a4 20 ... +``` \ No newline at end of file From 7d7a2914151e56119c74966f75fcff2f1ab5e93d Mon Sep 17 00:00:00 2001 From: Heiko Schaefer Date: Sat, 18 Nov 2023 22:49:02 +0100 Subject: [PATCH 02/33] write ch10 --- book/source/10-encryption.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/book/source/10-encryption.md b/book/source/10-encryption.md index 2b48280..843ec89 100644 --- a/book/source/10-encryption.md +++ b/book/source/10-encryption.md @@ -10,18 +10,24 @@ SPDX-License-Identifier: CC-BY-SA-4.0 ## High-Level overview of the message encryption process -Core concept: +Encryption in OpenPGP uses a [hybrid cryptosystem](hybrid_cryptosystems). -- The plaintext is encrypted with a symmetric "session key." -- The "session key" itself is stored in encrypted form, possibly multiple times: - - The session key is encrypted to the encryption keys of each intended recipient of the message. - - Alternatively, or additionally, the session key may be encrypted using a passphrase (this mode of operation doesn't require any OpenPGP certificates.) +This means that two distinct steps are performed: + +- Encryption of the plaintext with a (secret) symmetric key. The (potentially large) payload only needs to be stored once: all recipients use the same symmetric key, and all of them can decrypt the same symmetrically encrypted ciphertext. +- This symmetric key is then stored in encrypted form, possibly multiple time, once for each recipient. + - Usually, the symmetric key is encrypted to a public encryption component key of the recipient. + - Alternatively - or additionally - the secret symmetric key may also be encrypted using a passphrase, in place of an asymmetric key. This is a specialized and less commonly used mode of operation that doesn't require OpenPGP certificates. ## Generations of encryption +There are two generations of OpenPGP's encryption mechanism that the RFC allows for producing new encrypted messages, see [Packet Versions in Encrypted Messages](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#section-10.3.2.1). + (SEIPDv2)= ### SEIPD w/ AEAD (v2) +New in OpenPGP version 6. + ### SEIPD (v1) ## Advanced topics From 01739af17ce409b705dae6649445d1bafed01ad3 Mon Sep 17 00:00:00 2001 From: Heiko Schaefer Date: Sat, 18 Nov 2023 23:13:09 +0100 Subject: [PATCH 03/33] write ch10 --- book/source/10-encryption.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/source/10-encryption.md b/book/source/10-encryption.md index 843ec89..250dd1c 100644 --- a/book/source/10-encryption.md +++ b/book/source/10-encryption.md @@ -14,8 +14,8 @@ Encryption in OpenPGP uses a [hybrid cryptosystem](hybrid_cryptosystems). This means that two distinct steps are performed: -- Encryption of the plaintext with a (secret) symmetric key. The (potentially large) payload only needs to be stored once: all recipients use the same symmetric key, and all of them can decrypt the same symmetrically encrypted ciphertext. -- This symmetric key is then stored in encrypted form, possibly multiple time, once for each recipient. +- Encryption of the plaintext with a (secret) symmetric key, the *message key*. The (potentially large) payload only needs to be stored once: all recipients can decrypt the same symmetrically encrypted ciphertext, using this single message key. +- This symmetric message key is then stored in encrypted form, possibly multiple times, once for each recipient. - Usually, the symmetric key is encrypted to a public encryption component key of the recipient. - Alternatively - or additionally - the secret symmetric key may also be encrypted using a passphrase, in place of an asymmetric key. This is a specialized and less commonly used mode of operation that doesn't require OpenPGP certificates. From 1152dc20202a6d292b49366d03728a15e6e217fb Mon Sep 17 00:00:00 2001 From: Heiko Schaefer Date: Sun, 19 Nov 2023 15:16:12 +0100 Subject: [PATCH 04/33] write ch10 --- book/source/10-encryption.md | 39 +++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/book/source/10-encryption.md b/book/source/10-encryption.md index 250dd1c..9ca7a55 100644 --- a/book/source/10-encryption.md +++ b/book/source/10-encryption.md @@ -10,25 +10,46 @@ SPDX-License-Identifier: CC-BY-SA-4.0 ## High-Level overview of the message encryption process -Encryption in OpenPGP uses a [hybrid cryptosystem](hybrid_cryptosystems). +OpenPGP uses a [hybrid cryptosystem](hybrid_cryptosystems). Encryption is performed in two distinct steps: -This means that two distinct steps are performed: - -- Encryption of the plaintext with a (secret) symmetric key, the *message key*. The (potentially large) payload only needs to be stored once: all recipients can decrypt the same symmetrically encrypted ciphertext, using this single message key. -- This symmetric message key is then stored in encrypted form, possibly multiple times, once for each recipient. - - Usually, the symmetric key is encrypted to a public encryption component key of the recipient. +- The plaintext is encrypted with a (secret) symmetric key, the [*message key*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-confidentiality-via-encrypt). The (potentially large) payload only needs to be stored once in its encrypted form, even if it is encrypted to multiple recipients. +- For each recipient of the message, a packet with information about the message key is generated. + - Usually, the information that allows retrieval of the message key is encrypted to a public encryption component key of the recipient. - Alternatively - or additionally - the secret symmetric key may also be encrypted using a passphrase, in place of an asymmetric key. This is a specialized and less commonly used mode of operation that doesn't require OpenPGP certificates. ## Generations of encryption There are two generations of OpenPGP's encryption mechanism that the RFC allows for producing new encrypted messages, see [Packet Versions in Encrypted Messages](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#section-10.3.2.1). +The main difference between these two generations lies in the symmetric part of the mechanism, abbreviated as "SEIPD", for *Symmetrically Encrypted and Integrity Protected Data*. + +Older encryption mechanisms existed in OpenPGP, but those must not be used for encryption anymore. More information about these legacy encryption mechanisms can be found in the [decryption](decryption_chapter) chapter. + (SEIPDv2)= -### SEIPD w/ AEAD (v2) +### v2 SEIPD, based on AEAD -New in OpenPGP version 6. +This mechanism is new in OpenPGP version 6, and only supported by OpenPGP version 6 implementations. Consequently, ut can only be used when all recipients support OpenPGP version 6. -### SEIPD (v1) +v2 SEIPD can only be combined with either v6 PKESK or v6 SKESK. + +### v1 SEIPD, based on MDC + +This mechanism is supported by modern OpenPGP version 4 implementations. It was introduced in [RFC 4880](https://www.rfc-editor.org/rfc/rfc4880.html#section-5.13). + +It can only be combined with either v4 PKESK or v3 SKESK. + +When communicating with a mix of recipients, some of whose OpenPGP software only supports OpenPGP version 4, then this mechanism must be used. + +## Symmetric encryption, SEIPD + +This symmetric aspect of OpenPGP's encryption is entirely independent of OpenPGP keys. It only deals with symmetric keys (*session keys* and *message keys*) + +## Handling session keys with "ESK" + +This is a family of mechanisms for dealing with symmetric key material. It has two branches: + +- one that deals with asymmetric OpenPGP key material, and +- (a less commonly used) one that doesn't use OpenPGP asymmetric key material, but instead uses passphrases to protect the symmetric key material. ## Advanced topics From 09c6424a76d863a0ce1df712efbbd24729547bc7 Mon Sep 17 00:00:00 2001 From: Heiko Schaefer Date: Sun, 19 Nov 2023 21:25:05 +0100 Subject: [PATCH 05/33] write ch10 --- book/source/10-encryption.md | 44 +++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/book/source/10-encryption.md b/book/source/10-encryption.md index 9ca7a55..9fe231f 100644 --- a/book/source/10-encryption.md +++ b/book/source/10-encryption.md @@ -17,39 +17,51 @@ OpenPGP uses a [hybrid cryptosystem](hybrid_cryptosystems). Encryption is perfor - Usually, the information that allows retrieval of the message key is encrypted to a public encryption component key of the recipient. - Alternatively - or additionally - the secret symmetric key may also be encrypted using a passphrase, in place of an asymmetric key. This is a specialized and less commonly used mode of operation that doesn't require OpenPGP certificates. -## Generations of encryption +## Generations of encryption mechanisms in OpenPGP -There are two generations of OpenPGP's encryption mechanism that the RFC allows for producing new encrypted messages, see [Packet Versions in Encrypted Messages](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#section-10.3.2.1). +OpenPGP's encryption mechanisms have evolved over time. The RFC shows an [overview of encryption mechanisms](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#section-10.3.2.1), and how they may be combined. -The main difference between these two generations lies in the symmetric part of the mechanism, abbreviated as "SEIPD", for *Symmetrically Encrypted and Integrity Protected Data*. +Two generations of encryption mechanisms are currently relevant in OpenPGP, and will co-exist for the foreseeable future. The main difference between these lies in the symmetric part of the encryption mechanism, represented by versions 1 and 2 of the *Symmetrically Encrypted and Integrity Protected Data* packets (abbreviated as "SEIPD"). More on these below. -Older encryption mechanisms existed in OpenPGP, but those must not be used for encryption anymore. More information about these legacy encryption mechanisms can be found in the [decryption](decryption_chapter) chapter. +Older, legacy encryption mechanisms exist in OpenPGP. However, those must not be used for encryption anymore. Messages encrypted using these legacy mechanisms may still be decrypted, for more information see the [decryption](decryption_chapter) chapter. + +## Symmetric encryption of data, SEIPD + +*Symmetrically Encrypted Integrity Protected Data* (SEIPD) packets represent the symmetric aspect of OpenPGP's encryption mechanism. The function of these packets is entirely independent of (asymmetric) OpenPGP keys. The SEIPD mechanisms only deal with symmetric cryptography. + +```{note} +SEIPD packets are the successor to the [Symmetrically Encrypted Data](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-symmetrically-encrypted-dat) packet, which is obsolete. +``` + +When decrypted, the data contained in a SEIPD packet forms an OpenPGP message. That is, the decrypted data consists of a series of OpenPGP packets. + +In both versions of SEIPD, the decryptor has obtained a *session key* in a previous step - before processing the SEIPD packet. Using this session key, the decryptor can decrypt the SEIPD packet and process the contained plaintext data. + +Both versions of SEIPD can be used in combination with two mechanisms that provide *session keys*: +- [Public-Key Encrypted Session Key](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-public-key-encrypted-sessio) packets (PKESK) and +- [Symmetric-Key Encrypted Session Key](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#skesk) packets (SKESK) (SEIPDv2)= ### v2 SEIPD, based on AEAD -This mechanism is new in OpenPGP version 6, and only supported by OpenPGP version 6 implementations. Consequently, ut can only be used when all recipients support OpenPGP version 6. +The [version 2 SEIPD](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#version-two-seipd) mechanism is new in OpenPGP version 6, and only supported by OpenPGP version 6 implementations. Consequently, it can only be used for encryption when all recipients support OpenPGP version 6. v2 SEIPD can only be combined with either [version 6 PKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#v6-pkesk) or [version 6 SKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#v6-skesk). -v2 SEIPD can only be combined with either v6 PKESK or v6 SKESK. +In version 2 SEIPD, the *session key* is transformed into a *message key*, based on a salt value in the v2 SEIPD packet. ### v1 SEIPD, based on MDC -This mechanism is supported by modern OpenPGP version 4 implementations. It was introduced in [RFC 4880](https://www.rfc-editor.org/rfc/rfc4880.html#section-5.13). +The [version 1 SEIPD](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#version-one-seipd) mechanism is supported by all modern OpenPGP version 4 implementations. It was introduced in [RFC 4880](https://www.rfc-editor.org/rfc/rfc4880.html#section-5.13). -It can only be combined with either v4 PKESK or v3 SKESK. +Version 1 SEIPD can only be combined with either [version 3 PKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#v3-pkesk) or [version 4 SKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#v4-skesk). When communicating with a mix of recipients, some of whose OpenPGP software only supports OpenPGP version 4, then this mechanism must be used. -## Symmetric encryption, SEIPD +## Handling session keys with *ESK packets -This symmetric aspect of OpenPGP's encryption is entirely independent of OpenPGP keys. It only deals with symmetric keys (*session keys* and *message keys*) +"ESK" is a family of mechanisms for dealing with symmetric key material. It has two branches: -## Handling session keys with "ESK" - -This is a family of mechanisms for dealing with symmetric key material. It has two branches: - -- one that deals with asymmetric OpenPGP key material, and -- (a less commonly used) one that doesn't use OpenPGP asymmetric key material, but instead uses passphrases to protect the symmetric key material. +- [PKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-public-key-encrypted-sessio): Uses asymmetric OpenPGP key material to protect a session key, and +- [SKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-symmetric-key-encrypted-ses): Uses passphrases to protect the symmetric key material, instead of OpenPGP asymmetric key material (this is less commonly used). ## Advanced topics From 05b6e46d25ea956196559713ca2f769c89ebef8b Mon Sep 17 00:00:00 2001 From: Heiko Schaefer Date: Sun, 19 Nov 2023 21:34:55 +0100 Subject: [PATCH 06/33] fixes --- book/source/10-encryption.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/source/10-encryption.md b/book/source/10-encryption.md index 9fe231f..5bc9352 100644 --- a/book/source/10-encryption.md +++ b/book/source/10-encryption.md @@ -23,7 +23,7 @@ OpenPGP's encryption mechanisms have evolved over time. The RFC shows an [overvi Two generations of encryption mechanisms are currently relevant in OpenPGP, and will co-exist for the foreseeable future. The main difference between these lies in the symmetric part of the encryption mechanism, represented by versions 1 and 2 of the *Symmetrically Encrypted and Integrity Protected Data* packets (abbreviated as "SEIPD"). More on these below. -Older, legacy encryption mechanisms exist in OpenPGP. However, those must not be used for encryption anymore. Messages encrypted using these legacy mechanisms may still be decrypted, for more information see the [decryption](decryption_chapter) chapter. +Older, legacy encryption mechanisms exist in OpenPGP. However, those must not be used for encryption anymore. Messages encrypted using these legacy mechanisms may still be decrypted. For more information see the [decryption](decryption_chapter) chapter. ## Symmetric encryption of data, SEIPD @@ -33,7 +33,7 @@ Older, legacy encryption mechanisms exist in OpenPGP. However, those must not be SEIPD packets are the successor to the [Symmetrically Encrypted Data](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-symmetrically-encrypted-dat) packet, which is obsolete. ``` -When decrypted, the data contained in a SEIPD packet forms an OpenPGP message. That is, the decrypted data consists of a series of OpenPGP packets. +When decrypted, the data contained in a SEIPD packet forms an [OpenPGP message](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-openpgp-messages). That is, the decrypted data consists of a series of OpenPGP packets. In both versions of SEIPD, the decryptor has obtained a *session key* in a previous step - before processing the SEIPD packet. Using this session key, the decryptor can decrypt the SEIPD packet and process the contained plaintext data. From bfcbbee8aef3b4d460bd8019c80553d1f85a269d Mon Sep 17 00:00:00 2001 From: Heiko Schaefer Date: Sun, 19 Nov 2023 22:36:56 +0100 Subject: [PATCH 07/33] write ch10 --- book/source/10-encryption.md | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/book/source/10-encryption.md b/book/source/10-encryption.md index 5bc9352..5102f6a 100644 --- a/book/source/10-encryption.md +++ b/book/source/10-encryption.md @@ -10,12 +10,19 @@ SPDX-License-Identifier: CC-BY-SA-4.0 ## High-Level overview of the message encryption process -OpenPGP uses a [hybrid cryptosystem](hybrid_cryptosystems). Encryption is performed in two distinct steps: +Encryption in OpenPGP is performed in two distinct steps: -- The plaintext is encrypted with a (secret) symmetric key, the [*message key*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-confidentiality-via-encrypt). The (potentially large) payload only needs to be stored once in its encrypted form, even if it is encrypted to multiple recipients. -- For each recipient of the message, a packet with information about the message key is generated. - - Usually, the information that allows retrieval of the message key is encrypted to a public encryption component key of the recipient. - - Alternatively - or additionally - the secret symmetric key may also be encrypted using a passphrase, in place of an asymmetric key. This is a specialized and less commonly used mode of operation that doesn't require OpenPGP certificates. +1. The plaintext is encrypted with a (secret) symmetric key, the [*message key*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-confidentiality-via-encrypt). The (potentially large) ciphertext only needs to be stored once, even if it is sent to multiple recipients. All recipients get access to the same shared symmetric key for this message. +2. For each recipient of the message, a packet with information about the message key is generated. + - Usually, the information that allows retrieval of the message key is encrypted to a public encryption component key of the recipient. + - Alternatively - or additionally - the secret symmetric key may also be encrypted using a passphrase. This is a specialized and less commonly used mode of operation that doesn't require OpenPGP certificates. + + +```{admonition} TODO +:class: warning + +This text could center either the message, or the session key. Both are awkward, in different ways. +``` ## Generations of encryption mechanisms in OpenPGP @@ -29,17 +36,22 @@ Older, legacy encryption mechanisms exist in OpenPGP. However, those must not be *Symmetrically Encrypted Integrity Protected Data* (SEIPD) packets represent the symmetric aspect of OpenPGP's encryption mechanism. The function of these packets is entirely independent of (asymmetric) OpenPGP keys. The SEIPD mechanisms only deal with symmetric cryptography. +A SEIPD packet contains the actual payload: the ciphertext of the encrypted message. For a large encrypted message, the SEIPD packet will also be large. + ```{note} SEIPD packets are the successor to the [Symmetrically Encrypted Data](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-symmetrically-encrypted-dat) packet, which is obsolete. ``` When decrypted, the data contained in a SEIPD packet forms an [OpenPGP message](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-openpgp-messages). That is, the decrypted data consists of a series of OpenPGP packets. -In both versions of SEIPD, the decryptor has obtained a *session key* in a previous step - before processing the SEIPD packet. Using this session key, the decryptor can decrypt the SEIPD packet and process the contained plaintext data. +In both versions of SEIPD, the decryptor must have obtained a *session key* in a previous step, before processing the SEIPD packet. Using this session key, the decryptor can decrypt the SEIPD packet and process the plaintext data that it contains. Both versions of SEIPD can be used in combination with two mechanisms that provide *session keys*: -- [Public-Key Encrypted Session Key](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-public-key-encrypted-sessio) packets (PKESK) and -- [Symmetric-Key Encrypted Session Key](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#skesk) packets (SKESK) + +- [Public-Key Encrypted Session Key](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-public-key-encrypted-sessio) (PKESK) packets and +- [Symmetric-Key Encrypted Session Key](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#skesk) (SKESK) packets. + +The typical combination of mechanisms for encryption in OpenPGP is a [hybrid cryptosystem](hybrid_cryptosystems), consisting of [Public-Key Encrypted Session Key](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-public-key-encrypted-sessio) packets (PKESK), and a *Symmetrically Encrypted Integrity Protected Data* (SEIPD) packet. In this combination, an asymmetric cryptographic mechanism is used to protect a *session key*, with PKESK packets, and SEIPD packet is used to symmetrically encrypt the plaintext. (SEIPDv2)= ### v2 SEIPD, based on AEAD From 9310f0178f29e6f6311f113d5c52faf90cbea328 Mon Sep 17 00:00:00 2001 From: Heiko Schaefer Date: Sun, 19 Nov 2023 22:42:02 +0100 Subject: [PATCH 08/33] ch20: decrypt --- book/source/21-zoom_encyption.md | 45 +++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/book/source/21-zoom_encyption.md b/book/source/21-zoom_encyption.md index 831e57d..a729631 100644 --- a/book/source/21-zoom_encyption.md +++ b/book/source/21-zoom_encyption.md @@ -7,6 +7,8 @@ SPDX-License-Identifier: CC-BY-SA-4.0 ## SEIPD v2 +### Encrypt + We encrypt a short message to Alice, using a public certificate version of {ref}`alice_priv`: ```text @@ -27,6 +29,8 @@ cgIHAQaWc/Ip4Thq0EZDZwlpRUk/TUL+TWEpsGdQs8ifDyFAk7t3+3XvvLr5dUg3 -----END PGP MESSAGE----- ``` +### Inspect the packet dump of the encrypted message + Inspecting the packets of this message, we see: ```text @@ -75,4 +79,43 @@ Sym. Encrypted and Integrity Protected Data Packet, new CTB, 2 header bytes + 11 00000050 7a 96 6b 12 22 b2 13 da 27 e3 91 d6 ad 9b 65 2d z.k."...'.....e- 00000060 4d da 31 5b 69 13 8e 71 b0 12 2b a0 15 ce a0 96 M.1[i..q..+..... 00000070 9d ea a4 20 ... -``` \ No newline at end of file +``` + +### Decrypt + +```text +$ sq decrypt --dump-session-key --recipient-file alice.sec enc.pgp +Session key: 8DDA27B9B000BD84D0A39DFF66780111 +Encrypted using AES-128 +Compressed using ZIP +hello world +``` + +Inspecting the packets inside the SEIPD container: + +```text +$ sq decrypt --dump --recipient-file alice.sec enc.pgp +Public-Key Encrypted Session Key Packet, new CTB, 93 bytes + Version: 6 + Recipient: C0A58384A438E5A14F73712426A4D45DBAEEF4A39E6B30B09D5513F978ACCA94 + Pk algo: X25519 + +Encrypted using AES-128 +Compressed using ZIP +hello world +Sym. Encrypted and Integrity Protected Data Packet, new CTB, 114 bytes +│ Version: 2 +│ Symmetric algo: AES-128 +│ AEAD algo: EAX +│ Chunk size: 4096 +│ Salt: 9673F229E1386AD0464367096945493F4D42FE4D6129B06750B3C89F0F214093 +│ +└── Compressed Data Packet, new CTB, 44 bytes + │ Algorithm: ZIP + │ + ├── Literal Data Packet, new CTB, 18 bytes + │ Format: Binary data + │ + └── Padding Packet, new CTB, 14 bytes + Unknown variant +``` From 33bda5f443fd5dd24833fea9ff96c01b72799e28 Mon Sep 17 00:00:00 2001 From: Heiko Schaefer Date: Sun, 19 Nov 2023 23:10:49 +0100 Subject: [PATCH 09/33] write ch10 --- book/source/10-encryption.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/book/source/10-encryption.md b/book/source/10-encryption.md index 5102f6a..84fe481 100644 --- a/book/source/10-encryption.md +++ b/book/source/10-encryption.md @@ -68,13 +68,17 @@ Version 1 SEIPD can only be combined with either [version 3 PKESK](https://www.i When communicating with a mix of recipients, some of whose OpenPGP software only supports OpenPGP version 4, then this mechanism must be used. -## Handling session keys with *ESK packets +## Handling encrypted session keys: PKESK, SKESK -"ESK" is a family of mechanisms for dealing with symmetric key material. It has two branches: +"*ESK" is a family of mechanisms for dealing with symmetric key material. It has two branches: - [PKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-public-key-encrypted-sessio): Uses asymmetric OpenPGP key material to protect a session key, and - [SKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-symmetric-key-encrypted-ses): Uses passphrases to protect the symmetric key material, instead of OpenPGP asymmetric key material (this is less commonly used). +### PKESK: Session key encrypted to an asymmetric OpenPGP key + +### SKESK: Session key encrypted to a passphrase + ## Advanced topics ### Encrypt for multiple/single subkey per certificate? From f0b610f53f2cf43f3ea19e471f77d3a2ef505543 Mon Sep 17 00:00:00 2001 From: Heiko Schaefer Date: Sun, 19 Nov 2023 23:17:31 +0100 Subject: [PATCH 10/33] write ch10 --- book/source/10-encryption.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/source/10-encryption.md b/book/source/10-encryption.md index 84fe481..6ab287e 100644 --- a/book/source/10-encryption.md +++ b/book/source/10-encryption.md @@ -81,7 +81,7 @@ When communicating with a mix of recipients, some of whose OpenPGP software only ## Advanced topics -### Encrypt for multiple/single subkey per certificate? +### Encrypt to multiple/single subkey per certificate? ### "Negotiating" algorithms based on recipients preference subpackets @@ -89,7 +89,7 @@ When communicating with a mix of recipients, some of whose OpenPGP software only ### Implications of how a recipient cert is "addressed" (fingerprint/key-ID vs. user-ID) (preferences, expiration, revocation) -### AEAD modes: GCM +### AEAD modes in v2 SEIPD: GCM ```{admonition} TODO :class: warning From 3cd95f3300e792473ccd86d21a43d282b66f8c69 Mon Sep 17 00:00:00 2001 From: Heiko Schaefer Date: Sun, 19 Nov 2023 23:20:25 +0100 Subject: [PATCH 11/33] ch10: link to zooming in --- book/source/10-encryption.md | 13 ++++--------- book/source/21-zoom_encyption.md | 1 + 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/book/source/10-encryption.md b/book/source/10-encryption.md index 6ab287e..796ab87 100644 --- a/book/source/10-encryption.md +++ b/book/source/10-encryption.md @@ -8,6 +8,8 @@ SPDX-License-Identifier: CC-BY-SA-4.0 [Encryption](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#section-2.1) is one of the core facilities of OpenPGP. It provides confidentiality. +For an in-depth, packet-level view of encrypted data in OpenPGP, see our chapter {ref}`zoom_enc`. + ## High-Level overview of the message encryption process Encryption in OpenPGP is performed in two distinct steps: @@ -17,7 +19,6 @@ Encryption in OpenPGP is performed in two distinct steps: - Usually, the information that allows retrieval of the message key is encrypted to a public encryption component key of the recipient. - Alternatively - or additionally - the secret symmetric key may also be encrypted using a passphrase. This is a specialized and less commonly used mode of operation that doesn't require OpenPGP certificates. - ```{admonition} TODO :class: warning @@ -79,6 +80,8 @@ When communicating with a mix of recipients, some of whose OpenPGP software only ### SKESK: Session key encrypted to a passphrase +Also see https://flowcrypt.com/docs/guide/send-and-receive/send-password-protected-emails.html + ## Advanced topics ### Encrypt to multiple/single subkey per certificate? @@ -96,11 +99,3 @@ When communicating with a mix of recipients, some of whose OpenPGP software only Produce text around discussion: https://mailarchive.ietf.org/arch/msg/openpgp/ZTYD5VJsG1k2jJBbn5zIAf5o7d4/ ``` - -## Zooming in: Packet structure - -### Encryption yields a 'wrapped' openpgp packet stream - -### SKESK - -Also see https://flowcrypt.com/docs/guide/send-and-receive/send-password-protected-emails.html diff --git a/book/source/21-zoom_encyption.md b/book/source/21-zoom_encyption.md index a729631..e863962 100644 --- a/book/source/21-zoom_encyption.md +++ b/book/source/21-zoom_encyption.md @@ -3,6 +3,7 @@ SPDX-FileCopyrightText: 2023 The "Notes on OpenPGP" project SPDX-License-Identifier: CC-BY-SA-4.0 --> +(zoom_enc)= # Zooming in: Packet structure of encrypted data ## SEIPD v2 From 3d8bd5eab37af2453f35d37f498c39de7304059f Mon Sep 17 00:00:00 2001 From: Heiko Schaefer Date: Mon, 20 Nov 2023 15:24:42 +0100 Subject: [PATCH 12/33] ch10: edits --- book/source/10-encryption.md | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/book/source/10-encryption.md b/book/source/10-encryption.md index 796ab87..52c98df 100644 --- a/book/source/10-encryption.md +++ b/book/source/10-encryption.md @@ -8,22 +8,16 @@ SPDX-License-Identifier: CC-BY-SA-4.0 [Encryption](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#section-2.1) is one of the core facilities of OpenPGP. It provides confidentiality. -For an in-depth, packet-level view of encrypted data in OpenPGP, see our chapter {ref}`zoom_enc`. +For an in-depth, packet-level view of encrypted data in OpenPGP, see {ref}`zoom_enc`. ## High-Level overview of the message encryption process Encryption in OpenPGP is performed in two distinct steps: -1. The plaintext is encrypted with a (secret) symmetric key, the [*message key*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-confidentiality-via-encrypt). The (potentially large) ciphertext only needs to be stored once, even if it is sent to multiple recipients. All recipients get access to the same shared symmetric key for this message. -2. For each recipient of the message, a packet with information about the message key is generated. - - Usually, the information that allows retrieval of the message key is encrypted to a public encryption component key of the recipient. - - Alternatively - or additionally - the secret symmetric key may also be encrypted using a passphrase. This is a specialized and less commonly used mode of operation that doesn't require OpenPGP certificates. - -```{admonition} TODO -:class: warning - -This text could center either the message, or the session key. Both are awkward, in different ways. -``` +1. The plaintext is encrypted based on a (secret) symmetric key, the [*session key*](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-confidentiality-via-encrypt). The (potentially large) ciphertext only needs to be stored once, even if it is sent to multiple recipients. All recipients get access to the same shared session key to decrypt the message. +2. For each recipient of the message, a packet that contains the session key is generated. + - Usually, the session key is encrypted to a public encryption component key of the recipient. + - Alternatively - or additionally - the session key may also be encrypted using a passphrase. This is a specialized and less commonly used mode of operation that doesn't require OpenPGP certificates. ## Generations of encryption mechanisms in OpenPGP From a4969aa98b6aad891a1fb842b4ea3d5289318fd0 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 27 Nov 2023 22:47:29 +0100 Subject: [PATCH 13/33] ch10 feedback --- book/source/10-encryption.md | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/book/source/10-encryption.md b/book/source/10-encryption.md index 52c98df..ce7bbff 100644 --- a/book/source/10-encryption.md +++ b/book/source/10-encryption.md @@ -10,6 +10,16 @@ SPDX-License-Identifier: CC-BY-SA-4.0 For an in-depth, packet-level view of encrypted data in OpenPGP, see {ref}`zoom_enc`. +## Terminology + +| Term | Description | +|------|-------------| +| SEIPD Packet | *Symmetrically Encrypted, Integrity Protected Data* packet; contains the encrypted message payload | +| SKESK Packet | *Symmetric-Key-Encrypted Session-Key* packet; contains or provides a passphrase-encrypted Session-Key | +| PKESK Packet | *Public-Key-Encrypted Session-Key* packet; contains a session-key encrypted using an asymmetric public-key | +| Session-Key | Symmetric encryption key, which is either used directly as - or to derive - the Message-Key | +| Message-Key | Symmetric encryption key used to encrypt the contents of the SEIPD packet | + ## High-Level overview of the message encryption process Encryption in OpenPGP is performed in two distinct steps: @@ -19,13 +29,24 @@ Encryption in OpenPGP is performed in two distinct steps: - Usually, the session key is encrypted to a public encryption component key of the recipient. - Alternatively - or additionally - the session key may also be encrypted using a passphrase. This is a specialized and less commonly used mode of operation that doesn't require OpenPGP certificates. +```{note} +Above, "plaintext" either means a *Literal Data* packet, *Compressed Data* packet or a *signed message*. +A *signed message* on the other hand is a packet sequence that either resembles an *inline-signed message* (a *Literal Data* packet sandwhiched between one or more *One-Pass-Signature* and their respective *Signature* packets), or a *prefixed-signed* message (one or more *Signature* packets followed by a single *Literal Data* packet). +``` + ## Generations of encryption mechanisms in OpenPGP +```{admonition} TODO +:class: warning + +"Generations" here may be confused with the substantive of "generate" upon first reading. Perhaps we can find a better title? +``` + OpenPGP's encryption mechanisms have evolved over time. The RFC shows an [overview of encryption mechanisms](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#section-10.3.2.1), and how they may be combined. Two generations of encryption mechanisms are currently relevant in OpenPGP, and will co-exist for the foreseeable future. The main difference between these lies in the symmetric part of the encryption mechanism, represented by versions 1 and 2 of the *Symmetrically Encrypted and Integrity Protected Data* packets (abbreviated as "SEIPD"). More on these below. -Older, legacy encryption mechanisms exist in OpenPGP. However, those must not be used for encryption anymore. Messages encrypted using these legacy mechanisms may still be decrypted. For more information see the [decryption](decryption_chapter) chapter. +Older, legacy encryption mechanisms exist in OpenPGP. However, those must not be used for encryption anymore. Messages encrypted using these legacy mechanisms may still be decrypted, although with caution. For more information see the [decryption](decryption_chapter) chapter. ## Symmetric encryption of data, SEIPD @@ -51,7 +72,7 @@ The typical combination of mechanisms for encryption in OpenPGP is a [hybrid cry (SEIPDv2)= ### v2 SEIPD, based on AEAD -The [version 2 SEIPD](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#version-two-seipd) mechanism is new in OpenPGP version 6, and only supported by OpenPGP version 6 implementations. Consequently, it can only be used for encryption when all recipients support OpenPGP version 6. v2 SEIPD can only be combined with either [version 6 PKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#v6-pkesk) or [version 6 SKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#v6-skesk). +The [version 2 SEIPD](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#version-two-seipd) mechanism was introduced in OpenPGP version 6, and is only supported by OpenPGP version 6 implementations. Consequently, it can only be used for encryption when all recipients support OpenPGP version 6. v2 SEIPD can only be combined with either [version 6 PKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#v6-pkesk) and/or [version 6 SKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#v6-skesk) packets. In version 2 SEIPD, the *session key* is transformed into a *message key*, based on a salt value in the v2 SEIPD packet. @@ -59,7 +80,7 @@ In version 2 SEIPD, the *session key* is transformed into a *message key*, based The [version 1 SEIPD](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#version-one-seipd) mechanism is supported by all modern OpenPGP version 4 implementations. It was introduced in [RFC 4880](https://www.rfc-editor.org/rfc/rfc4880.html#section-5.13). -Version 1 SEIPD can only be combined with either [version 3 PKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#v3-pkesk) or [version 4 SKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#v4-skesk). +Version 1 SEIPD can only be combined with either [version 3 PKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#v3-pkesk) and/or [version 4 SKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#v4-skesk) packets. When communicating with a mix of recipients, some of whose OpenPGP software only supports OpenPGP version 4, then this mechanism must be used. From 9951bcd9b23557e8f4788d1169deff8a415a7227 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 27 Nov 2023 23:22:52 +0100 Subject: [PATCH 14/33] Add diagram for SEIPDv1 --- book/source/10-encryption.md | 7 +++ book/source/drawio/SEIPDv1-PKESK.drawio | 70 ++++++++++++++++++++++++ book/source/drawio/SEIPDv1-PKESK.png | Bin 0 -> 33990 bytes 3 files changed, 77 insertions(+) create mode 100644 book/source/drawio/SEIPDv1-PKESK.drawio create mode 100644 book/source/drawio/SEIPDv1-PKESK.png diff --git a/book/source/10-encryption.md b/book/source/10-encryption.md index ce7bbff..b21cc27 100644 --- a/book/source/10-encryption.md +++ b/book/source/10-encryption.md @@ -84,6 +84,13 @@ Version 1 SEIPD can only be combined with either [version 3 PKESK](https://www.i When communicating with a mix of recipients, some of whose OpenPGP software only supports OpenPGP version 4, then this mechanism must be used. +```{figure} drawio/SEIPDv1-PKESK.png +:name: fig-encryption-seipdv1-pkesk +:alt: Depicts a dotted hexagon labeled "Plaintext", from which a curved arrow passes another dotted hexagon "Session Key" and finally points to a "SEIPDv1" packet. Two more curved arrows originate from the session key and pass Alice' and Bob's encryption key, ending in two PKESK packets. + +With SEIPDv1, the session-key is directly used as message-key to encrypt the payload +``` + ## Handling encrypted session keys: PKESK, SKESK "*ESK" is a family of mechanisms for dealing with symmetric key material. It has two branches: diff --git a/book/source/drawio/SEIPDv1-PKESK.drawio b/book/source/drawio/SEIPDv1-PKESK.drawio new file mode 100644 index 0000000..ff0732b --- /dev/null +++ b/book/source/drawio/SEIPDv1-PKESK.drawio @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/book/source/drawio/SEIPDv1-PKESK.png b/book/source/drawio/SEIPDv1-PKESK.png new file mode 100644 index 0000000000000000000000000000000000000000..cb8d0407ef9ccb5cfafb710ee2d5e7091c9c5103 GIT binary patch literal 33990 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 Date: Mon, 27 Nov 2023 23:39:26 +0100 Subject: [PATCH 15/33] Add diagram for SEIPDv2 --- book/source/10-encryption.md | 7 ++ book/source/drawio/SEIPDv2-PKESK.drawio | 94 ++++++++++++++++++++++++ book/source/drawio/SEIPDv2-PKESK.png | Bin 0 -> 44278 bytes 3 files changed, 101 insertions(+) create mode 100644 book/source/drawio/SEIPDv2-PKESK.drawio create mode 100644 book/source/drawio/SEIPDv2-PKESK.png diff --git a/book/source/10-encryption.md b/book/source/10-encryption.md index b21cc27..34d5174 100644 --- a/book/source/10-encryption.md +++ b/book/source/10-encryption.md @@ -76,6 +76,13 @@ The [version 2 SEIPD](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto- In version 2 SEIPD, the *session key* is transformed into a *message key*, based on a salt value in the v2 SEIPD packet. +```{figure} drawio/SEIPDv2-PKESK.png +:name: fig-encryption-seipdv2-pkesk +:alt: TODO + +With SEIPDv2, the message-key is derived from the session-key in an extra step. +``` + ### v1 SEIPD, based on MDC The [version 1 SEIPD](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#version-one-seipd) mechanism is supported by all modern OpenPGP version 4 implementations. It was introduced in [RFC 4880](https://www.rfc-editor.org/rfc/rfc4880.html#section-5.13). diff --git a/book/source/drawio/SEIPDv2-PKESK.drawio b/book/source/drawio/SEIPDv2-PKESK.drawio new file mode 100644 index 0000000..9a4a400 --- /dev/null +++ b/book/source/drawio/SEIPDv2-PKESK.drawio @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/book/source/drawio/SEIPDv2-PKESK.png b/book/source/drawio/SEIPDv2-PKESK.png new file mode 100644 index 0000000000000000000000000000000000000000..f3151ca530b6d081547073c89eaae6cad2fd7b16 GIT binary patch literal 44278 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 Date: Tue, 28 Nov 2023 23:19:59 +0100 Subject: [PATCH 16/33] Some progress. Swapped SEIPDv2 and v1 to improve readability --- book/source/10-encryption.md | 80 ++++++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 31 deletions(-) diff --git a/book/source/10-encryption.md b/book/source/10-encryption.md index 34d5174..37318dc 100644 --- a/book/source/10-encryption.md +++ b/book/source/10-encryption.md @@ -48,9 +48,37 @@ Two generations of encryption mechanisms are currently relevant in OpenPGP, and Older, legacy encryption mechanisms exist in OpenPGP. However, those must not be used for encryption anymore. Messages encrypted using these legacy mechanisms may still be decrypted, although with caution. For more information see the [decryption](decryption_chapter) chapter. +SEIPD packets are typically used in combination with two mechanisms that provide *session keys*: + +- [Public-Key Encrypted Session Key](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-public-key-encrypted-sessio) (PKESK) packets and +- [Symmetric-Key Encrypted Session Key](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#skesk) (SKESK) packets. + +The typical combination of mechanisms for encryption in OpenPGP is a [hybrid cryptosystem](hybrid_cryptosystems), consisting of [Public-Key Encrypted Session Key](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-public-key-encrypted-sessio) packets (PKESK), and a *Symmetrically Encrypted Integrity Protected Data* (SEIPD) packet. In this combination, an asymmetric cryptographic mechanism is used to protect a *session key*, with PKESK packets, and SEIPD packet is used to symmetrically encrypt the plaintext. + +## Encapsulating session keys: PKESK, SKESK + +"*ESK" (encrypted session-key) is a family of mechanisms for encapsulation of symmetric key material. It has two branches: + +- [PKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-public-key-encrypted-sessio): Uses asymmetric OpenPGP key material to protect a session key, and +- [SKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-symmetric-key-encrypted-ses): Uses passphrases to protect the symmetric key material, instead of OpenPGP asymmetric key material (this is less commonly used). + +An arbitrary number of PKESKs and SKESKs can be used for the same message. + +### PKESK: Session key encrypted to an asymmetric OpenPGP key + +To encrypt an OpenPGP message for a recipient, the session-key is encrypted to the recipients public key. The resulting encrypted session key is packed into a PKESK packet, which holds essential metadata, like an identifier of the recipients encryption (sub)-key. + +This procedure is repeated for each recipient of the message, and all resulting PKESK packets are prepended to the SEIPD packet (see below) containing the actual message. + +### SKESK: Session key encrypted to a passphrase + +As an alternative (or augmentation) to PKESK packets, a message can also be encrypted to a symmetric passphrase. This is done using a SKESK packet, which basically uses an S2K mechanism to derive a symmetric key from a passphrase, which is then either used directly as the session-key, or more commonly, used as a key-encapsulation-key (KEK) to encrypt the session-key. + +Also see https://flowcrypt.com/docs/guide/send-and-receive/send-password-protected-emails.html + ## Symmetric encryption of data, SEIPD -*Symmetrically Encrypted Integrity Protected Data* (SEIPD) packets represent the symmetric aspect of OpenPGP's encryption mechanism. The function of these packets is entirely independent of (asymmetric) OpenPGP keys. The SEIPD mechanisms only deal with symmetric cryptography. +*Symmetrically Encrypted Integrity Protected Data* (SEIPD) packets represent the symmetric aspect of OpenPGP's encryption mechanism. The function of these packets is entirely independent of (asymmetric) OpenPGP keys. A SEIPD packet contains the actual payload: the ciphertext of the encrypted message. For a large encrypted message, the SEIPD packet will also be large. @@ -58,36 +86,19 @@ A SEIPD packet contains the actual payload: the ciphertext of the encrypted mess SEIPD packets are the successor to the [Symmetrically Encrypted Data](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-symmetrically-encrypted-dat) packet, which is obsolete. ``` +Two versions of the SEIPD packet (separated by the version number) have been specified. Version 1, introduced in RFC4880, is used in OpenPGP v4 (and can be used with v6) while SEIPD version 2 was introduced with OpenPGP v6 and is not backwards compatible to OpenPGP v4. + When decrypted, the data contained in a SEIPD packet forms an [OpenPGP message](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-openpgp-messages). That is, the decrypted data consists of a series of OpenPGP packets. In both versions of SEIPD, the decryptor must have obtained a *session key* in a previous step, before processing the SEIPD packet. Using this session key, the decryptor can decrypt the SEIPD packet and process the plaintext data that it contains. -Both versions of SEIPD can be used in combination with two mechanisms that provide *session keys*: - -- [Public-Key Encrypted Session Key](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-public-key-encrypted-sessio) (PKESK) packets and -- [Symmetric-Key Encrypted Session Key](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#skesk) (SKESK) packets. - -The typical combination of mechanisms for encryption in OpenPGP is a [hybrid cryptosystem](hybrid_cryptosystems), consisting of [Public-Key Encrypted Session Key](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-public-key-encrypted-sessio) packets (PKESK), and a *Symmetrically Encrypted Integrity Protected Data* (SEIPD) packet. In this combination, an asymmetric cryptographic mechanism is used to protect a *session key*, with PKESK packets, and SEIPD packet is used to symmetrically encrypt the plaintext. - -(SEIPDv2)= -### v2 SEIPD, based on AEAD - -The [version 2 SEIPD](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#version-two-seipd) mechanism was introduced in OpenPGP version 6, and is only supported by OpenPGP version 6 implementations. Consequently, it can only be used for encryption when all recipients support OpenPGP version 6. v2 SEIPD can only be combined with either [version 6 PKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#v6-pkesk) and/or [version 6 SKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#v6-skesk) packets. - -In version 2 SEIPD, the *session key* is transformed into a *message key*, based on a salt value in the v2 SEIPD packet. - -```{figure} drawio/SEIPDv2-PKESK.png -:name: fig-encryption-seipdv2-pkesk -:alt: TODO - -With SEIPDv2, the message-key is derived from the session-key in an extra step. -``` - ### v1 SEIPD, based on MDC -The [version 1 SEIPD](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#version-one-seipd) mechanism is supported by all modern OpenPGP version 4 implementations. It was introduced in [RFC 4880](https://www.rfc-editor.org/rfc/rfc4880.html#section-5.13). +The [version 1 SEIPD](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#version-one-seipd) mechanism is supported by all modern OpenPGP version 4 implementations. It was introduced in [RFC 4880](https://www.rfc-editor.org/rfc/rfc4880.html#section-5.13) as a replacement for the *SED* (Symmetricaly Encrypted Data) packet. SEIPDv1 provides integrity protection of the ciphertext using a SHA-1 checksum of the plaintext as modification detection code. -Version 1 SEIPD can only be combined with either [version 3 PKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#v3-pkesk) and/or [version 4 SKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#v4-skesk) packets. +Version 1 SEIPD can only be combined with [version 3 PKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#v3-pkesk) and/or [version 4 SKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#v4-skesk) packets. + +In this version of the SEIPD packet, the session-key is used directly as message-key, meaning the payload is encrypted symmetrically using the session-key. When communicating with a mix of recipients, some of whose OpenPGP software only supports OpenPGP version 4, then this mechanism must be used. @@ -98,18 +109,25 @@ When communicating with a mix of recipients, some of whose OpenPGP software only With SEIPDv1, the session-key is directly used as message-key to encrypt the payload ``` -## Handling encrypted session keys: PKESK, SKESK +(SEIPDv2)= +### v2 SEIPD, based on AEAD -"*ESK" is a family of mechanisms for dealing with symmetric key material. It has two branches: +The [version 2 SEIPD](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#version-two-seipd) mechanism was introduced in OpenPGP version 6, and is only supported by OpenPGP version 6 implementations. Consequently, it can only be used for encryption when all recipients support OpenPGP version 6. +It provides integrity protection of the ciphertext using *AEAD* (authenticated encryption with additional data). +v2 SEIPD can only be combined with either [version 6 PKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#v6-pkesk) and/or [version 6 SKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#v6-skesk) packets. -- [PKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-public-key-encrypted-sessio): Uses asymmetric OpenPGP key material to protect a session key, and -- [SKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-symmetric-key-encrypted-ses): Uses passphrases to protect the symmetric key material, instead of OpenPGP asymmetric key material (this is less commonly used). +In version 2 SEIPD, the *session key* is transformed into a *message key*, based on a per-message salt value stored separately in the v2 SEIPD packet. The message key is then used in an AEAD scheme to encrypt the message payload. -### PKESK: Session key encrypted to an asymmetric OpenPGP key +```{note} +The session-key can use a different symmetric algorithm than the message-key. +``` -### SKESK: Session key encrypted to a passphrase +```{figure} drawio/SEIPDv2-PKESK.png +:name: fig-encryption-seipdv2-pkesk +:alt: TODO -Also see https://flowcrypt.com/docs/guide/send-and-receive/send-password-protected-emails.html +With SEIPDv2, the message-key is derived from the session-key in an extra step. +``` ## Advanced topics From 217f1ed50772aaa0c7719a4a71433774cb64ce05 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 28 Nov 2023 23:26:37 +0100 Subject: [PATCH 17/33] Add TODO about session-key reuse in SEIPDv2 --- book/source/10-encryption.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/book/source/10-encryption.md b/book/source/10-encryption.md index 37318dc..42b95eb 100644 --- a/book/source/10-encryption.md +++ b/book/source/10-encryption.md @@ -129,6 +129,14 @@ The session-key can use a different symmetric algorithm than the message-key. With SEIPDv2, the message-key is derived from the session-key in an extra step. ``` +```{admonition} TODO +:class: warning + +Explain, that with SEIPDv2, a session-key can essentially protect more than one message by reusing the same session-key and *ESK packets with a fresh, per-message salt. + +This might very well go into the advanced topics section though. +``` + ## Advanced topics ### Encrypt to multiple/single subkey per certificate? From 20c0cb8b91dde40e22f569546480ffe3cf08a68a Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Wed, 6 Dec 2023 21:15:24 +0100 Subject: [PATCH 18/33] Remove some TODOs from ch10 and add some more --- book/source/10-encryption.md | 51 +++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/book/source/10-encryption.md b/book/source/10-encryption.md index 42b95eb..fd555e9 100644 --- a/book/source/10-encryption.md +++ b/book/source/10-encryption.md @@ -34,17 +34,11 @@ Above, "plaintext" either means a *Literal Data* packet, *Compressed Data* packe A *signed message* on the other hand is a packet sequence that either resembles an *inline-signed message* (a *Literal Data* packet sandwhiched between one or more *One-Pass-Signature* and their respective *Signature* packets), or a *prefixed-signed* message (one or more *Signature* packets followed by a single *Literal Data* packet). ``` -## Generations of encryption mechanisms in OpenPGP - -```{admonition} TODO -:class: warning - -"Generations" here may be confused with the substantive of "generate" upon first reading. Perhaps we can find a better title? -``` +## History of encryption mechanisms in OpenPGP OpenPGP's encryption mechanisms have evolved over time. The RFC shows an [overview of encryption mechanisms](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#section-10.3.2.1), and how they may be combined. -Two generations of encryption mechanisms are currently relevant in OpenPGP, and will co-exist for the foreseeable future. The main difference between these lies in the symmetric part of the encryption mechanism, represented by versions 1 and 2 of the *Symmetrically Encrypted and Integrity Protected Data* packets (abbreviated as "SEIPD"). More on these below. +Two generations of encryption mechanisms are currently relevant in OpenPGP, and will co-exist for the foreseeable future. The main difference between these lies in the symmetric part of the encryption mechanism, represented by versions 1 and 2 of the *Symmetrically Encrypted and Integrity Protected Data* packets (abbreviated as "SEIPD"), as they make use of different techniques to provide non-malleability. More on these below. Older, legacy encryption mechanisms exist in OpenPGP. However, those must not be used for encryption anymore. Messages encrypted using these legacy mechanisms may still be decrypted, although with caution. For more information see the [decryption](decryption_chapter) chapter. @@ -53,7 +47,7 @@ SEIPD packets are typically used in combination with two mechanisms that provide - [Public-Key Encrypted Session Key](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-public-key-encrypted-sessio) (PKESK) packets and - [Symmetric-Key Encrypted Session Key](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#skesk) (SKESK) packets. -The typical combination of mechanisms for encryption in OpenPGP is a [hybrid cryptosystem](hybrid_cryptosystems), consisting of [Public-Key Encrypted Session Key](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-public-key-encrypted-sessio) packets (PKESK), and a *Symmetrically Encrypted Integrity Protected Data* (SEIPD) packet. In this combination, an asymmetric cryptographic mechanism is used to protect a *session key*, with PKESK packets, and SEIPD packet is used to symmetrically encrypt the plaintext. +The typical combination of mechanisms for encryption in OpenPGP is a [hybrid cryptosystem](hybrid_cryptosystems), consisting of one or more [Public-Key Encrypted Session Key](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-public-key-encrypted-sessio) packets (PKESK), followed by a [Symmetrically Encrypted Integrity Protected Data* (SEIPD) packet. In this combination, an asymmetric cryptographic mechanism is used to protect a *session key* inside PKESK packets, which is used to protect the plaintext using symmetric-key encryption in a SEIPD packet. ## Encapsulating session keys: PKESK, SKESK @@ -62,7 +56,7 @@ The typical combination of mechanisms for encryption in OpenPGP is a [hybrid cry - [PKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-public-key-encrypted-sessio): Uses asymmetric OpenPGP key material to protect a session key, and - [SKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-symmetric-key-encrypted-ses): Uses passphrases to protect the symmetric key material, instead of OpenPGP asymmetric key material (this is less commonly used). -An arbitrary number of PKESKs and SKESKs can be used for the same message. +An arbitrary number of PKESKs and SKESKs can be used for the same message. It is also possible to mix those, resulting in a message which can be decrypted using either one of the designated OpenPGP keys or any of the passwords used to encrypt the message. This is useful to make a message available to a number of known recipients, with the option to provide the password to future recipients. ### PKESK: Session key encrypted to an asymmetric OpenPGP key @@ -70,12 +64,23 @@ To encrypt an OpenPGP message for a recipient, the session-key is encrypted to t This procedure is repeated for each recipient of the message, and all resulting PKESK packets are prepended to the SEIPD packet (see below) containing the actual message. +Typically, the sender would also include themselves as a recipient, in order to be able to decrypt the sent message at a later point in time. + ### SKESK: Session key encrypted to a passphrase As an alternative (or augmentation) to PKESK packets, a message can also be encrypted to a symmetric passphrase. This is done using a SKESK packet, which basically uses an S2K mechanism to derive a symmetric key from a passphrase, which is then either used directly as the session-key, or more commonly, used as a key-encapsulation-key (KEK) to encrypt the session-key. Also see https://flowcrypt.com/docs/guide/send-and-receive/send-password-protected-emails.html +As for protection of secret key material, it is important to chose appropriate S2K parameters when generating an SKESK packet. +The specification currently recommends to use either *Iterated and Salted S2K* or *Argon2*. + +```{admonition} TODO: +:class: warning + +Add further guidance for recommended S2K parameters, like iteration count or Argon2 configuration. Perhaps in a dedicated "S2K Parameters" section, which can be reused for the encryption chapter and when we talk about secret key encryption in TSKs. +``` + ## Symmetric encryption of data, SEIPD *Symmetrically Encrypted Integrity Protected Data* (SEIPD) packets represent the symmetric aspect of OpenPGP's encryption mechanism. The function of these packets is entirely independent of (asymmetric) OpenPGP keys. @@ -86,7 +91,7 @@ A SEIPD packet contains the actual payload: the ciphertext of the encrypted mess SEIPD packets are the successor to the [Symmetrically Encrypted Data](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-symmetrically-encrypted-dat) packet, which is obsolete. ``` -Two versions of the SEIPD packet (separated by the version number) have been specified. Version 1, introduced in RFC4880, is used in OpenPGP v4 (and can be used with v6) while SEIPD version 2 was introduced with OpenPGP v6 and is not backwards compatible to OpenPGP v4. +Two versions of the SEIPD packet (differentiated by the version number) have been specified. Version 1, introduced in RFC4880, is used in OpenPGP v4 while SEIPD version 2 was introduced with OpenPGP v6. Both versions can be used with either OpenPGP v4 or v6 keys, although OpenPGP v4 keys need to announce support for SEIPD version 2 via the *Feature* signature subpacket. When decrypted, the data contained in a SEIPD packet forms an [OpenPGP message](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-openpgp-messages). That is, the decrypted data consists of a series of OpenPGP packets. @@ -112,7 +117,7 @@ With SEIPDv1, the session-key is directly used as message-key to encrypt the pay (SEIPDv2)= ### v2 SEIPD, based on AEAD -The [version 2 SEIPD](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#version-two-seipd) mechanism was introduced in OpenPGP version 6, and is only supported by OpenPGP version 6 implementations. Consequently, it can only be used for encryption when all recipients support OpenPGP version 6. +The [version 2 SEIPD](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#version-two-seipd) mechanism was introduced in OpenPGP version 6. Consequently, it can only be used for encryption when all recipients explicitly announce support for it using a *Feature* signature subpacket. It provides integrity protection of the ciphertext using *AEAD* (authenticated encryption with additional data). v2 SEIPD can only be combined with either [version 6 PKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#v6-pkesk) and/or [version 6 SKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#v6-skesk) packets. @@ -129,6 +134,8 @@ The session-key can use a different symmetric algorithm than the message-key. With SEIPDv2, the message-key is derived from the session-key in an extra step. ``` +This additional step introduces key-separation into the protocol, which protects against certain attacks, such as an [OpenPGP SEIP downgrade attack](https://www.metzdowd.com/pipermail/cryptography/2015-October/026685.html). + ```{admonition} TODO :class: warning @@ -141,12 +148,32 @@ This might very well go into the advanced topics section though. ### Encrypt to multiple/single subkey per certificate? +A recipients certificate may possibly contain more than one usable encryption subkey. +This raises the question, should the message be encrypted for all of them? + +There is the argument, that a powerful attacker might have managed to add an attacker-controlled encryption subkey to the victims certificate. +In this case, only encrypting to the "newest" encryption key would help uncovering such an attack, although a powerful attacker could just MitM any sent messages and just add a PKESK for the victim-controlled encryption keys to hide the fact that the sender used a different key. + +On the other hand, a user might have multiple encryption subkeys on purpose. +Picture for example a scenario where the same certificate is used on multiple devices, but each devices has dedicated encryption subkeys to allow for smoother revocation in case of a lost device. +In this scenario, it is important that the sender encrypts the message to all available encryption subkeys. + ### "Negotiating" algorithms based on recipients preference subpackets #### Prevent "downgrade" -> Policy +Each implementation should define a "minimum" level of security when it comes to algorithms. +If the lowest common denominator of symmetric encryption algorithms preferred by a set of recipients provides too little security, the implementation should either use a fallback algorithm instead, or fail to produce a message at all. + ### Implications of how a recipient cert is "addressed" (fingerprint/key-ID vs. user-ID) (preferences, expiration, revocation) +```{admonition} TODO +:class: warning + +This has been described elsewhere already. +See 9.7.3 +``` + ### AEAD modes in v2 SEIPD: GCM ```{admonition} TODO From acc1548b4036b2bd7596c3000cbd16308b2de4dd Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Thu, 7 Dec 2023 23:52:03 +0100 Subject: [PATCH 19/33] Big commit, should have split into smaller commits, but haven't --- book/source/11-decryption.md | 179 +++++++++++- book/source/drawio/PKESKv3-decryption.drawio | 57 ++++ book/source/drawio/PKESKv3-decryption.svg | 4 + book/source/drawio/PKESKv6-decryption.drawio | 66 +++++ book/source/drawio/PKESKv6-decryption.svg | 4 + book/source/drawio/SEIPDv1-decryption.drawio | 69 +++++ book/source/drawio/SEIPDv1-decryption.svg | 4 + .../drawio/SEIPDv2-decryption-chunks.drawio | 257 ++++++++++++++++++ .../drawio/SEIPDv2-decryption-chunks.svg | 4 + .../SEIPDv2-decryption-mk-derivation.drawio | 119 ++++++++ .../SEIPDv2-decryption-mk-derivation.svg | 4 + book/source/drawio/SKESKv4-decryption.drawio | 114 ++++++++ book/source/drawio/SKESKv4-decryption.svg | 4 + book/source/drawio/SKESKv6-decryption.drawio | 220 +++++++++++++++ book/source/drawio/SKESKv6-decryption.svg | 4 + 15 files changed, 1108 insertions(+), 1 deletion(-) create mode 100644 book/source/drawio/PKESKv3-decryption.drawio create mode 100644 book/source/drawio/PKESKv3-decryption.svg create mode 100644 book/source/drawio/PKESKv6-decryption.drawio create mode 100644 book/source/drawio/PKESKv6-decryption.svg create mode 100644 book/source/drawio/SEIPDv1-decryption.drawio create mode 100644 book/source/drawio/SEIPDv1-decryption.svg create mode 100644 book/source/drawio/SEIPDv2-decryption-chunks.drawio create mode 100644 book/source/drawio/SEIPDv2-decryption-chunks.svg create mode 100644 book/source/drawio/SEIPDv2-decryption-mk-derivation.drawio create mode 100644 book/source/drawio/SEIPDv2-decryption-mk-derivation.svg create mode 100644 book/source/drawio/SKESKv4-decryption.drawio create mode 100644 book/source/drawio/SKESKv4-decryption.svg create mode 100644 book/source/drawio/SKESKv6-decryption.drawio create mode 100644 book/source/drawio/SKESKv6-decryption.svg diff --git a/book/source/11-decryption.md b/book/source/11-decryption.md index a3e39dd..31ee3ea 100644 --- a/book/source/11-decryption.md +++ b/book/source/11-decryption.md @@ -6,6 +6,22 @@ SPDX-License-Identifier: CC-BY-SA-4.0 (decryption_chapter)= # Decryption +Message decryption is the process of taking an encrypted message and recovering its plaintext. +This involves multiple steps. + +Implementations typically first process the PKESK and SKESK packets leading the SEIPD packet to identify \*ESK packets suitable for decryption. +A PKESK packet is suitable, if it contains a recipient-keyID matching a decryption (sub-) key of the users certificate. +Typically, all \*ESK packets leading a SEIPD packet contain the same *session-key* once decrypted. + +```{note} + +Anonymous-recipient PKESK packets contain a recipient-keyID of `0`, so if no suitable non-anonymous PKESK was found, any anonymous PKESKs are tried with any available decryption (sub-) keys (see [](decryption_anonymous_recipient)). +``` + +If no suitable PKESK packets were found, SKESK packets are tried next, meaning the user is typically prompted to enter a decryption passphrase. + +Once any of these methods succeeded, the resulting *session-key* is used to decrypt the SEIPD packet. + ```{admonition} TODO :class: warning @@ -15,10 +31,171 @@ SPDX-License-Identifier: CC-BY-SA-4.0 - using revoked subkey? ``` -## SEIPD w/ AEAD (v2) +## Symmetric decryption of the session-key (SKESK) + +Decrypting a SKESK packet to recover the *session-key* is done by performing the encryption steps in reverse, based on a user-provided passphrase. + +In both version 4 and version 6 of the SKESK packet, the user is prompted to enter a passphrase, which is passed through the S2K function described by the SKESK packet. +However, the subsequent steps of the procedure are different: + +### SKESK v4 + +Here, the result of the S2K function is a symmetric key, which is either used to decrypt the encrypted session-key contained in the SKESK packet, or - less commonly - used as session-key directly. + +```{note} + +The "direct method" where the result of the S2K function is directly used as session key is only applicable if only one SKESK packet is present. +``` + +```{figure} drawio/SKESKv4-decryption.svg +:name: fig-skeskv4-decryption +:alt: Diagram depicting how the S2K function is used to derive key symmetric key from the user-provided passphrase. This key is then either used directly as session-key, or used to decrypt the encrypted session-key. + +Decrypting the session-key from a version 4 SKESK packet. +``` + +With version 4 SKESK packets, which are only used with version 1 SEIPD packets, the *session-key* is used as *message-key* without an intermediate derivation. +The symmetric cipher algorithm tag of the SKESK packet dictates the cipher algorithm used to decrypt the plaintext from the SEIPD packet. + +### SKESK v6 + +With version 6 SKESK packets, the result of the passing the passphrase through the S2K function is used as *initial keying material* (IKM) to derive a symmetric *key encryption key* using HKDF as a key derivation function. The HKDF function doesn't use any salt in this step and the *info* parameter is assembled from parameters of the SKESK packet. + +In the next step, this symmetric key is used to decrypt the *session-key* using AEAD. +The AEAD function uses information from the associated SEIPDv2 packet as *additional data*. +The function is also salted using the SEIPDv2's salt. +The *AEAD Auth Tag* of the SKESK packet is used as authentication tag. + +The result is the *session-key*. + +```{figure} drawio/SKESKv6-decryption.svg +:name: fig-skeskv6-decryption +:alt: Diagram depicting the complicated process of deriving the session-key from a SKESK version 6 packet. + +Decrypting the session-key from a version 6 SKESK packet. +``` + +## Asymmetric decryption of the session key via PKESK + +More common than SKESK packets are PKESK packets which are used for asymmetric encryption of the session-key. +Here, the recipients secret key is used to decrypt the session-key. + +### PKESK v3 + +With version 3 PKESKs, the recipients secret encryption (sub-) key is directly used to decrypt the encrypted *session key*. +The key ID of the subkey to be used is recorded in the PKESKs key-id field. A value of `0` indicates an anonymous recipient (see [](decryption_anonymous_recipient)). + +To detect, which symmetric cipher is used to decrypt the SEIPDv1 packet later on, each public key algorithm uses a slightly different encoding to unpack the symmetric algorithm tag from the decrypted session key. See sections 5.1.3 through 5.1.7 of the OpenPGP specification. Typically, the cipher algorithm ID is prefixed the the actual session key. + +```{admonition} TODO +:class: warning + +Link those sections directly +``` + +```{figure} drawio/PKESKv3-decryption.svg +:name: fig-decryption-pkesk3 +:alt: Depicts, how the the secret-key component of the users encryption subkey is directly used to decrypt the encrypted session-key. + +Decrypting the session-key from a version 3 PKESK packet. +``` + +### PKESK v6 + +The decryption of version 6 PKESK packets works quite similar to version 3. + +```{figure} drawio/PKESKv6-decryption.svg +:name: fig-decryption-pkesk6 +:alt: Depicts, how the the secret-key component of the users encryption subkey is directly used to decrypt the encrypted session-key. + +Decrypting the session-key from a version 6 PKESK packet. +``` + +Contrary to the version 3 PKESK, the encrypted session-key within the version 6 PKESK does not contain the symmetric cipher algorithm used to decrypt the SEIPD packet. +Instead, this cipher algorithm ID is encoded inside the SEIPDv2 packet directly. + +(decryption_anonymous_recipient)= +### Anonymous recipients + +Having all recipients keys listed as part of the PKESK packets presents a metadata leakage. An observer can easily enumerate recipients of a message by comparing the PKESKs with certificates of potential recipients. + +To prevent this issue, the sender can decide to add individual recipients as anonymous recipients using a wildcard key-ID / fingerprint. +This is done by creating a normal PKESK packet for the recipient, but setting the recipient key field to `0` (as well as omitting the version number of the key for v6 PKESKs). + +A recipient of such a message that does not find a PKESK addressed specifically to any of their keys, can then try to decrypt any anonymous PKESK packets using any of their encryption subkeys. + +```{admonition} TODO +:class: warning + +When did the decryption succeed? Describe quick check of the check sum and decryption of first few bytes of the SEIPD as test strategies. +``` ## SEIPD (v1) +Version 1 SEIPD packets MUST only be used with version 3 PKESK packets and/or version 4 SKESK packets. +Any other combinations are not allowed and MUST result in a broken message. + +```{note} +Since SEIPD version 1 is susceptible to downgrade attacks under certain scenarios, it is recommended to use SEIPD version 2 wherever possible. +``` + +To decrypt the contents of a version 1 SEIPD packet, the session-key obtained in the previous step is used. +The cipher algorithm is determined by TODO. + +```{adminonition} TODO +:class: warning + +Describe in detail, how the cipher algorithm is obtained. +``` + +Once the cipher is initialized, the whole encrypted data from the SEIPD packet is decrypted. + +```{admonition} TODO +:class: warning + +Describe the MDC which is used for modification detection. +``` + +```{figure} drawio/SEIPDv1-decryption.svg +:name: fig-decryption-seipd1 +:alt: Depicts how the session-key is used directly to decrypt the contents of the SEIPD packet. + +The contents of the SEIPD packet are decrypted using the session-key as message-key. +``` + + +## SEIPD w/ AEAD (v2) + +Preferred mode. +Version 2 SEIPD packets MUST only be used with version 6 PKESK packets and/or version 6 SKESK packets. +Any other combinations are not allowed and MUST result in a broken message. + +Once the session-key was obtained from a PKESK or SKESK, it is used to derive a *message-key* and an IV. This is done by passing the session-key through a salted HKDF function, where the salt is unique per message and obtained from the SEIPD packet. + +The result is split into the message key and first half of the IV. + +```{figure} drawio/SEIPDv2-decryption-mk-derivation.svg +:name: fig-decryption-seipd2-mk-derivation +:alt: Depicts how the session-key is fed into a salted HKDF to derive both the message-key and the first half of an IV. + +In a first step, a message-key and half of an IV is derived from the session-key. +``` + +Then, the contens of the SEIPDs encrypted data are split into chunks, which are processed sequentially. Each chunk is decrypted using AEAD with parameters from the SEIPD packet as *additional data*. +For each chunk, the chunk index starting at `0` is passed into the function as second half of the IV. + +All decrypted plaintext blocks are appended to form the result of the decryption process. + +After all blocks have been processed, in a last AEAD step, the total number of plaintext octets gets appended to the *additional data* and the final AEAD auth tag from the SEIPD packet is processed. + +```{figure} drawio/SEIPDv2-decryption-chunks.svg +:name: fig-decryption-seipd2-chunks +:alt: Depicts, how the message-key and index-postfixed IV are used to decrypt each individual chunk of plaintext. + +Each chunk is decrypted using AEAD using the message-key and an IV with appended chunk index. +``` + + ## SED Legacy mode, may be decrypted, but not produced. diff --git a/book/source/drawio/PKESKv3-decryption.drawio b/book/source/drawio/PKESKv3-decryption.drawio new file mode 100644 index 0000000..235b4f1 --- /dev/null +++ b/book/source/drawio/PKESKv3-decryption.drawio @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/book/source/drawio/PKESKv3-decryption.svg b/book/source/drawio/PKESKv3-decryption.svg new file mode 100644 index 0000000..28e9267 --- /dev/null +++ b/book/source/drawio/PKESKv3-decryption.svg @@ -0,0 +1,4 @@ + + + +
Secret Key
Key-ID: 0xB0B
Secret KeyKey-ID: 0x...
Asymmetric
Decryption
Asymmetric...
Symmetric Key
Symmetric Key
PKESKv3
PKESKv3
Key-ID: 0xB0B
Key-ID: 0xB0B
Asymmetric Algo.
Asymmetric Algo.
ciphertext
ciphertext
Enc. Session-Key
Enc. Session-Key
Text is not SVG - cannot display
\ No newline at end of file diff --git a/book/source/drawio/PKESKv6-decryption.drawio b/book/source/drawio/PKESKv6-decryption.drawio new file mode 100644 index 0000000..26142f7 --- /dev/null +++ b/book/source/drawio/PKESKv6-decryption.drawio @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/book/source/drawio/PKESKv6-decryption.svg b/book/source/drawio/PKESKv6-decryption.svg new file mode 100644 index 0000000..0a0dd87 --- /dev/null +++ b/book/source/drawio/PKESKv6-decryption.svg @@ -0,0 +1,4 @@ + + + +
Secret Key
Key-ID: 0xB0B
Version: 6
Secret Key...
Asymmetric
Decryption
Asymmetric...
Session-Key
Session-Key
PKESKv6
PKESKv6
Fingerprint: 0xB0B
Fingerprint: 0xB...
Asymmetric Algo.
Asymmetric Algo.
ciphertext
ciphertext
Enc. Session-Key
Enc. Session-Key
Key-Version: 6
Key-Version: 6
Text is not SVG - cannot display
\ No newline at end of file diff --git a/book/source/drawio/SEIPDv1-decryption.drawio b/book/source/drawio/SEIPDv1-decryption.drawio new file mode 100644 index 0000000..4e86af7 --- /dev/null +++ b/book/source/drawio/SEIPDv1-decryption.drawio @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/book/source/drawio/SEIPDv1-decryption.svg b/book/source/drawio/SEIPDv1-decryption.svg new file mode 100644 index 0000000..09c1962 --- /dev/null +++ b/book/source/drawio/SEIPDv1-decryption.svg @@ -0,0 +1,4 @@ + + + +
key
key
Message-Key
(Session-Key)
Message-Key(Session-...
SEIPDv1
SEIPDv1
Encrypted Data
Encrypted Data
Symmetric
Decryption
Symmetric...
algorithm
algorithm
Extract
Cipher
Algorithm
Extract...
ciphertext
ciphertext
Plaintext
Plaintext
Text is not SVG - cannot display
\ No newline at end of file diff --git a/book/source/drawio/SEIPDv2-decryption-chunks.drawio b/book/source/drawio/SEIPDv2-decryption-chunks.drawio new file mode 100644 index 0000000..e38b132 --- /dev/null +++ b/book/source/drawio/SEIPDv2-decryption-chunks.drawio @@ -0,0 +1,257 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/book/source/drawio/SEIPDv2-decryption-chunks.svg b/book/source/drawio/SEIPDv2-decryption-chunks.svg new file mode 100644 index 0000000..7376d21 --- /dev/null +++ b/book/source/drawio/SEIPDv2-decryption-chunks.svg @@ -0,0 +1,4 @@ + + + +
key
key
key
key
Message-Key
Message-Key
SEIPDv2
SEIPDv2
Cipher Algo.
Cipher Algo.
AEAD Algo.
AEAD Algo.
Chunk Size
Chunk Size
Salt
Salt
Encrypted Data
Encrypted Data
ciphertext
ciphertext
Final AEAD
Auth Tag
Final AEAD...
IV
IV
AD
AD
Packet Type ID,
Version Number,
Cipher Algo,
AEAD Algo,
Chunk Size
Packet Type ID,...
Packet Type, Version
Packet Type, Version
+
+
toChunk(i)
toChunk(i)
append
chunk
index(i)
append...
Chunk #i
Chunk #i
nonce
nonce
nonce
nonce
Nonce #i
Nonce #i
ciphertext
ciphertext
Tag #i
Tag #i
AEAD
AEAD
Plaintext Block #i
Plaintext Block #i
Final
AEAD
Step
Final...
AD
AD
AD,
#Plaintext Octets
AD,...
AD
AD
""
""
Text is not SVG - cannot display
\ No newline at end of file diff --git a/book/source/drawio/SEIPDv2-decryption-mk-derivation.drawio b/book/source/drawio/SEIPDv2-decryption-mk-derivation.drawio new file mode 100644 index 0000000..5f7dba1 --- /dev/null +++ b/book/source/drawio/SEIPDv2-decryption-mk-derivation.drawio @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/book/source/drawio/SEIPDv2-decryption-mk-derivation.svg b/book/source/drawio/SEIPDv2-decryption-mk-derivation.svg new file mode 100644 index 0000000..7491d2d --- /dev/null +++ b/book/source/drawio/SEIPDv2-decryption-mk-derivation.svg @@ -0,0 +1,4 @@ + + + +
Message-Key
Message-Key
SEIPDv2
SEIPDv2
Cipher Algo.
Cipher Algo.
AEAD Algo.
AEAD Algo.
Chunk Size
Chunk Size
salt
salt
Salt
Salt
Encrypted Data
Encrypted Data
Final AEAD
Auth Tag
Final AEAD...
IKM
IKM
Session-Key
(decrypted from PKESK/SKESK)
Session-Key...
HKDF
HKDF
IV
IV
info
info
Packet Type ID,
Version Number,
Cipher Algo,
AEAD Algo,
Chunk Size
Packet Type ID,...
Packet Type, Version
Packet Type, Version
+
+
Text is not SVG - cannot display
\ No newline at end of file diff --git a/book/source/drawio/SKESKv4-decryption.drawio b/book/source/drawio/SKESKv4-decryption.drawio new file mode 100644 index 0000000..0a8c28f --- /dev/null +++ b/book/source/drawio/SKESKv4-decryption.drawio @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/book/source/drawio/SKESKv4-decryption.svg b/book/source/drawio/SKESKv4-decryption.svg new file mode 100644 index 0000000..879f6bc --- /dev/null +++ b/book/source/drawio/SKESKv4-decryption.svg @@ -0,0 +1,4 @@ + + + +
Passphrase
Passphrase
S2K Function
S2K Function
Session Key
Session Key
Symmetric Key
Symmetric Key
SKESKv4
SKESKv4
Cipher Algo.
Cipher Algo.
S2K Identifier
S2K Identifier
ciphertext
ciphertext
Enc. Session-Key
Enc. Session-Key
yes
yes
key
key
no
no

Is
Encrypted
Session-Key
present
Is...
Symmetric
Decryption
Symmetric...
Text is not SVG - cannot display
\ No newline at end of file diff --git a/book/source/drawio/SKESKv6-decryption.drawio b/book/source/drawio/SKESKv6-decryption.drawio new file mode 100644 index 0000000..5bdafa0 --- /dev/null +++ b/book/source/drawio/SKESKv6-decryption.drawio @@ -0,0 +1,220 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/book/source/drawio/SKESKv6-decryption.svg b/book/source/drawio/SKESKv6-decryption.svg new file mode 100644 index 0000000..898a5fc --- /dev/null +++ b/book/source/drawio/SKESKv6-decryption.svg @@ -0,0 +1,4 @@ + + + +
Encrypted Message
Encrypted Message
SEIPDv2
SEIPDv2
Salt: 49f8edc3
Salt: 49f8edc3
Ciphertext
Ciphertext
Cipher Algo.
Cipher Algo.
AEAD Mode
AEAD Mode
Chunk Size
Chunk Size
AEAD Auth Tag
AEAD Auth Tag
Passphrase
Passphrase
SKESKv6
SKESKv6
Cipher Algo.
Cipher Algo.
AEAD Mode
AEAD Mode
S2K Identifier
S2K Identifier
IV: 0xC0FFEE
IV: 0xC0FFEE
Enc. Session-Key
Enc. Session-Key
AEAD Auth Tag
AEAD Auth Tag
S2K Function
S2K Function
HKDF
(no salt)
HKDF(no salt)
IKM
IKM
Packet Type ID,
Packet Version,
Cipher Algo,
AEAD Mode
Packet Type ID,...
Packet Type and Verison
Packet Type and Verison
Key Encryption Key
Key Encryption Key
Info
Info
AEAD
AEAD
Packet Type ID,
Packet Version,
Cipher Algo,
AEAD Mode
Packet Type ID,...
Packet Type and Verison
Packet Type and Verison
Salt
Salt
AD
AD
Key
Key
Ciphertext
Ciphertext
Auth Tag
Auth Tag
Session Key
Session Key
Text is not SVG - cannot display
\ No newline at end of file From 6e1b616f9996fbbcd2c162a02710ef4a2b11a3fd Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Thu, 7 Dec 2023 23:55:10 +0100 Subject: [PATCH 20/33] Fix admonition --- book/source/11-decryption.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/source/11-decryption.md b/book/source/11-decryption.md index 31ee3ea..fdfa4ba 100644 --- a/book/source/11-decryption.md +++ b/book/source/11-decryption.md @@ -142,7 +142,7 @@ Since SEIPD version 1 is susceptible to downgrade attacks under certain scenario To decrypt the contents of a version 1 SEIPD packet, the session-key obtained in the previous step is used. The cipher algorithm is determined by TODO. -```{adminonition} TODO +```{admonition} TODO :class: warning Describe in detail, how the cipher algorithm is obtained. From 41100cfad51d452def2d90cc0f4950308bfe3ead Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Fri, 8 Dec 2023 00:16:03 +0100 Subject: [PATCH 21/33] Replace ch10 diags with svgs --- book/source/10-encryption.md | 4 ++-- book/source/drawio/SEIPDv1-PKESK.png | Bin 33990 -> 0 bytes book/source/drawio/SEIPDv1-PKESK.svg | 4 ++++ book/source/drawio/SEIPDv2-PKESK.png | Bin 44278 -> 0 bytes book/source/drawio/SEIPDv2-PKESK.svg | 4 ++++ 5 files changed, 10 insertions(+), 2 deletions(-) delete mode 100644 book/source/drawio/SEIPDv1-PKESK.png create mode 100644 book/source/drawio/SEIPDv1-PKESK.svg delete mode 100644 book/source/drawio/SEIPDv2-PKESK.png create mode 100644 book/source/drawio/SEIPDv2-PKESK.svg diff --git a/book/source/10-encryption.md b/book/source/10-encryption.md index fd555e9..cdde41e 100644 --- a/book/source/10-encryption.md +++ b/book/source/10-encryption.md @@ -107,7 +107,7 @@ In this version of the SEIPD packet, the session-key is used directly as message When communicating with a mix of recipients, some of whose OpenPGP software only supports OpenPGP version 4, then this mechanism must be used. -```{figure} drawio/SEIPDv1-PKESK.png +```{figure} drawio/SEIPDv1-PKESK.svg :name: fig-encryption-seipdv1-pkesk :alt: Depicts a dotted hexagon labeled "Plaintext", from which a curved arrow passes another dotted hexagon "Session Key" and finally points to a "SEIPDv1" packet. Two more curved arrows originate from the session key and pass Alice' and Bob's encryption key, ending in two PKESK packets. @@ -127,7 +127,7 @@ In version 2 SEIPD, the *session key* is transformed into a *message key*, based The session-key can use a different symmetric algorithm than the message-key. ``` -```{figure} drawio/SEIPDv2-PKESK.png +```{figure} drawio/SEIPDv2-PKESK.svg :name: fig-encryption-seipdv2-pkesk :alt: TODO diff --git a/book/source/drawio/SEIPDv1-PKESK.png b/book/source/drawio/SEIPDv1-PKESK.png deleted file mode 100644 index cb8d0407ef9ccb5cfafb710ee2d5e7091c9c5103..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33990 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 + + +
Session Key
Session Key
PKESK #1
0xBBBB...
PKESK #10xBBBB...
PKESK #0
0xAAAA...
PKESK #00xAAAA...
Alice'
Encryption Key
0xAAAA...
Alice'...
Bob's
Encryption Key
0xBBBB...
Bob's...
Plaintext
Plaintext
SEIPDv1
SEIPDv1
Encrypted Message
Encrypted Message
Text is not SVG - cannot display
\ No newline at end of file diff --git a/book/source/drawio/SEIPDv2-PKESK.png b/book/source/drawio/SEIPDv2-PKESK.png deleted file mode 100644 index f3151ca530b6d081547073c89eaae6cad2fd7b16..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44278 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 + + +
Session Key
Session Key
PKESK #1
0xBBBB...
PKESK #10xBBBB...
PKESK #0
0xAAAA...
PKESK #00xAAAA...
Alice'
Encryption Key
0xAAAA...
Alice'...
Bob's
Encryption Key
0xBBBB...
Bob's...
Plaintext
Plaintext
Encrypted Message
Encrypted Message
Message Key
Message Key
SEIPDv2
SEIPDv2
Salt: 49f8edc3
Salt: 49f8edc3
Ciphertext
Ciphertext
Text is not SVG - cannot display
\ No newline at end of file From 558801eca574bbf0df546e691daaf10c77dcf7b5 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Fri, 8 Dec 2023 15:43:03 +0100 Subject: [PATCH 22/33] Link to algorithm-specific session key encryption sections --- book/source/11-decryption.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/book/source/11-decryption.md b/book/source/11-decryption.md index fdfa4ba..7760cc5 100644 --- a/book/source/11-decryption.md +++ b/book/source/11-decryption.md @@ -85,13 +85,13 @@ Here, the recipients secret key is used to decrypt the session-key. With version 3 PKESKs, the recipients secret encryption (sub-) key is directly used to decrypt the encrypted *session key*. The key ID of the subkey to be used is recorded in the PKESKs key-id field. A value of `0` indicates an anonymous recipient (see [](decryption_anonymous_recipient)). -To detect, which symmetric cipher is used to decrypt the SEIPDv1 packet later on, each public key algorithm uses a slightly different encoding to unpack the symmetric algorithm tag from the decrypted session key. See sections 5.1.3 through 5.1.7 of the OpenPGP specification. Typically, the cipher algorithm ID is prefixed the the actual session key. +To detect, which symmetric cipher is used to decrypt the SEIPDv1 packet later on, each public key algorithm uses a slightly different encoding to unpack the symmetric algorithm tag from the decrypted session key. See the respective sections[^rsa-spec] [^elgamal-spec] [^ecdh-spec] [^x25519-spec] [^x448-spec] of the standard. Typically, the cipher algorithm ID is prefixed the the actual session key. -```{admonition} TODO -:class: warning - -Link those sections directly -``` +[^rsa-spec]: [Algorithm-Specific Fields for RSA encryption](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-algorithm-specific-fields-f) +[^elgamal-spec]: [Algorithm-Specific Fields for Elgamal encryption](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-algorithm-specific-fields-fo) +[^ecdh-spec]: [Algorithm-Specific Fields for ECDH encryption](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-algorithm-specific-fields-for) +[^x25519-spec]: [Algorithm-Specific Fields for X25519 encryption](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-algorithm-specific-fields-for-) +[^x448-spec]: [Algorithm-Specific Fields for X448 encryption](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-algorithm-specific-fields-for-x) ```{figure} drawio/PKESKv3-decryption.svg :name: fig-decryption-pkesk3 From 6371b6051277ba11d4f16232068d8a21234bff45 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Fri, 8 Dec 2023 15:44:18 +0100 Subject: [PATCH 23/33] Move anonymous recipient section to advanced topics --- book/source/11-decryption.md | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/book/source/11-decryption.md b/book/source/11-decryption.md index 7760cc5..3e8b318 100644 --- a/book/source/11-decryption.md +++ b/book/source/11-decryption.md @@ -114,22 +114,6 @@ Decrypting the session-key from a version 6 PKESK packet. Contrary to the version 3 PKESK, the encrypted session-key within the version 6 PKESK does not contain the symmetric cipher algorithm used to decrypt the SEIPD packet. Instead, this cipher algorithm ID is encoded inside the SEIPDv2 packet directly. -(decryption_anonymous_recipient)= -### Anonymous recipients - -Having all recipients keys listed as part of the PKESK packets presents a metadata leakage. An observer can easily enumerate recipients of a message by comparing the PKESKs with certificates of potential recipients. - -To prevent this issue, the sender can decide to add individual recipients as anonymous recipients using a wildcard key-ID / fingerprint. -This is done by creating a normal PKESK packet for the recipient, but setting the recipient key field to `0` (as well as omitting the version number of the key for v6 PKESKs). - -A recipient of such a message that does not find a PKESK addressed specifically to any of their keys, can then try to decrypt any anonymous PKESK packets using any of their encryption subkeys. - -```{admonition} TODO -:class: warning - -When did the decryption succeed? Describe quick check of the check sum and decryption of first few bytes of the SEIPD as test strategies. -``` - ## SEIPD (v1) Version 1 SEIPD packets MUST only be used with version 3 PKESK packets and/or version 4 SKESK packets. @@ -213,4 +197,21 @@ also see: https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#pkesk-notes -> An implementation MAY accept or use a Key ID of all zeros, or an omitted key fingerprint, to hide the intended decryption key \ No newline at end of file +(decryption_anonymous_recipient)= +> An implementation MAY accept or use a Key ID of all zeros, or an omitted key fingerprint, to hide the intended decryption key(decryption_anonymous_recipient)= +### Anonymous recipients + +Having all recipients keys listed as part of the PKESK packets presents a metadata leakage. An observer can easily enumerate recipients of a message by comparing the PKESKs with certificates of potential recipients. + +To prevent this issue, the sender can decide to add individual recipients as anonymous recipients using a wildcard key-ID / fingerprint. +This is done by creating a normal PKESK packet for the recipient, but setting the recipient key field to `0` (as well as omitting the version number of the key for v6 PKESKs). + +A recipient of such a message that does not find a PKESK addressed specifically to any of their keys, can then try to decrypt any anonymous PKESK packets using any of their encryption subkeys. + +To reduce the number of keys to try, the recipient can skip all secret keys which do not share the public-key algorithm stated in the PKESK packet. + +```{admonition} TODO +:class: warning + +When did the decryption succeed? Describe quick check of the check sum and decryption of first few bytes of the SEIPD as test strategies. +``` From 39ed1873d0e0ca6cea62d6c03d6a906de3f72b22 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Fri, 8 Dec 2023 15:44:50 +0100 Subject: [PATCH 24/33] Add more info about SEIPDv1's quick check mechanism --- book/source/11-decryption.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/book/source/11-decryption.md b/book/source/11-decryption.md index 3e8b318..a14b3df 100644 --- a/book/source/11-decryption.md +++ b/book/source/11-decryption.md @@ -186,19 +186,17 @@ Legacy mode, may be decrypted, but not produced. ## Advanced topics -### Selecting decryption key +### Verify successful session-key decryption -- Trying PKESKs until one works out -- consider "smart" strategies +SEIPDv1 packets might make use of a "quick check" mechanism to quickly verify that the correct session key was used without the need to decrypt the whole SEIPD packet. +This check consists of 16 random bytes, followed a copy of the two last bytes, which are prefixed to the plaintext. +During decrypting, these 2 bytes can be compared to the 15th and 16th random byte to detect use of the wrong session key. -additional wrinkle: hidden intended decryption key (`gnupg --throw-keyid`) - -also see: +Since the chance to accidentally end up with matching quick check bytes albeit the use of the wrong session key is 1:65536, some implementations validate further contents of the plaintext, such as the packet headers. -https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#pkesk-notes +The standard [warns against](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-risks-of-a-quick-check-orac) using the quick check mechanism, as it introduces the risk of a decryption oracle. Instead, the use of SEIPDv2 is recommended, as the AEAD mechanism automatically detects use of the wrong session-key early on after the first chunk has been decrypted. (decryption_anonymous_recipient)= -> An implementation MAY accept or use a Key ID of all zeros, or an omitted key fingerprint, to hide the intended decryption key(decryption_anonymous_recipient)= ### Anonymous recipients Having all recipients keys listed as part of the PKESK packets presents a metadata leakage. An observer can easily enumerate recipients of a message by comparing the PKESKs with certificates of potential recipients. From d2def8cb89b9acbf618ca2ddef43e418090e9370 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Fri, 8 Dec 2023 15:52:54 +0100 Subject: [PATCH 25/33] Improve section on symmetric ciphers used for SKESKv4+SEIPDv1 --- book/source/11-decryption.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/book/source/11-decryption.md b/book/source/11-decryption.md index a14b3df..778c19a 100644 --- a/book/source/11-decryption.md +++ b/book/source/11-decryption.md @@ -55,7 +55,11 @@ Decrypting the session-key from a version 4 SKESK packet. ``` With version 4 SKESK packets, which are only used with version 1 SEIPD packets, the *session-key* is used as *message-key* without an intermediate derivation. -The symmetric cipher algorithm tag of the SKESK packet dictates the cipher algorithm used to decrypt the plaintext from the SEIPD packet. +When the direct method is used, meaning no encrypted session-key was contained in the SKESK packet, the symmetric cipher algorithm ID of the SKESK packet dictates the cipher algorithm used to decrypt the plaintext from the SEIPD packet. + +Otherwise, the cipher algorithm ID to decrypt the SEIPD packet was prefixed to the decrypted session key. + +Sanitizing this first byte acts as a very early quick check to verify that the used passphrase was correct. For further validation of the session-key, see [](decryption_seipd_quick_check). ### SKESK v6 @@ -186,6 +190,7 @@ Legacy mode, may be decrypted, but not produced. ## Advanced topics +(decryption_seipd_quick_check)= ### Verify successful session-key decryption SEIPDv1 packets might make use of a "quick check" mechanism to quickly verify that the correct session key was used without the need to decrypt the whole SEIPD packet. From 65cf7fbcf932a8406f3aa6e2a11aa4b1b7a692b7 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Fri, 8 Dec 2023 16:21:45 +0100 Subject: [PATCH 26/33] Improve diagrams --- book/source/11-decryption.md | 8 +-- book/source/drawio/PKESKv3-decryption.drawio | 30 ++++---- book/source/drawio/PKESKv3-decryption.svg | 2 +- book/source/drawio/SEIPDv1-decryption.drawio | 48 +++++++------ book/source/drawio/SEIPDv1-decryption.svg | 2 +- .../SEIPDv2-decryption-mk-derivation.drawio | 63 ++++++++-------- .../SEIPDv2-decryption-mk-derivation.svg | 2 +- book/source/drawio/SKESKv4-decryption.drawio | 71 ++++++++++--------- book/source/drawio/SKESKv4-decryption.svg | 2 +- 9 files changed, 115 insertions(+), 113 deletions(-) diff --git a/book/source/11-decryption.md b/book/source/11-decryption.md index 778c19a..88d7099 100644 --- a/book/source/11-decryption.md +++ b/book/source/11-decryption.md @@ -128,13 +128,7 @@ Since SEIPD version 1 is susceptible to downgrade attacks under certain scenario ``` To decrypt the contents of a version 1 SEIPD packet, the session-key obtained in the previous step is used. -The cipher algorithm is determined by TODO. - -```{admonition} TODO -:class: warning - -Describe in detail, how the cipher algorithm is obtained. -``` +The cipher algorithm is either extracted from the decrypted session-key (the algorithm ID is typically prefixed to the decrypted session-key), or - in case of a SKESK packet using the direct-method - taken from the SKESKs cipher algorithm field. Once the cipher is initialized, the whole encrypted data from the SEIPD packet is decrypted. diff --git a/book/source/drawio/PKESKv3-decryption.drawio b/book/source/drawio/PKESKv3-decryption.drawio index 235b4f1..b6e37fe 100644 --- a/book/source/drawio/PKESKv3-decryption.drawio +++ b/book/source/drawio/PKESKv3-decryption.drawio @@ -1,28 +1,28 @@ - + - + - + - + - + - + - + - + - + @@ -31,24 +31,24 @@ - + - + - + - + - + - + diff --git a/book/source/drawio/PKESKv3-decryption.svg b/book/source/drawio/PKESKv3-decryption.svg index 28e9267..da7e8d6 100644 --- a/book/source/drawio/PKESKv3-decryption.svg +++ b/book/source/drawio/PKESKv3-decryption.svg @@ -1,4 +1,4 @@ -
Secret Key
Key-ID: 0xB0B
Secret KeyKey-ID: 0x...
Asymmetric
Decryption
Asymmetric...
Symmetric Key
Symmetric Key
PKESKv3
PKESKv3
Key-ID: 0xB0B
Key-ID: 0xB0B
Asymmetric Algo.
Asymmetric Algo.
ciphertext
ciphertext
Enc. Session-Key
Enc. Session-Key
Text is not SVG - cannot display
\ No newline at end of file +
Secret Key
Key-ID: 0xB0B
Secret KeyKey-ID: 0x...
Asymmetric
Decryption
Asymmetric...
Cipher Algorithm
+
Session Key
Cipher Algorithm...
PKESKv3
PKESKv3
Key-ID: 0xB0B
Key-ID: 0xB0B
Asymmetric Algo.
Asymmetric Algo.
ciphertext
ciphertext
Enc. Session-Key
Enc. Session-Key
Text is not SVG - cannot display
\ No newline at end of file diff --git a/book/source/drawio/SEIPDv1-decryption.drawio b/book/source/drawio/SEIPDv1-decryption.drawio index 4e86af7..adf2f4c 100644 --- a/book/source/drawio/SEIPDv1-decryption.drawio +++ b/book/source/drawio/SEIPDv1-decryption.drawio @@ -1,53 +1,49 @@ - + - + - + - + - - - - + - + - + - + - + - + - + - - + + + + - + - - - - + @@ -55,14 +51,20 @@ - + - + + + + + + + diff --git a/book/source/drawio/SEIPDv1-decryption.svg b/book/source/drawio/SEIPDv1-decryption.svg index 09c1962..708593e 100644 --- a/book/source/drawio/SEIPDv1-decryption.svg +++ b/book/source/drawio/SEIPDv1-decryption.svg @@ -1,4 +1,4 @@ -
key
key
Message-Key
(Session-Key)
Message-Key(Session-...
SEIPDv1
SEIPDv1
Encrypted Data
Encrypted Data
Symmetric
Decryption
Symmetric...
algorithm
algorithm
Extract
Cipher
Algorithm
Extract...
ciphertext
ciphertext
Plaintext
Plaintext
Text is not SVG - cannot display
\ No newline at end of file +
key
key
Message-Key
(Session-Key)
Message-Key(Session-...
SEIPDv1
SEIPDv1
Encrypted Data
Encrypted Data
Symmetric
Decryption
Symmetric...
algorithm
algorithm
ciphertext
ciphertext
Plaintext
Plaintext
Cipher Algorithm
Cipher Algorithm
(obtained from PKESK / SKESK)
(obtained from PKESK / SKESK)
Text is not SVG - cannot display
\ No newline at end of file diff --git a/book/source/drawio/SEIPDv2-decryption-mk-derivation.drawio b/book/source/drawio/SEIPDv2-decryption-mk-derivation.drawio index 5f7dba1..24dfd8d 100644 --- a/book/source/drawio/SEIPDv2-decryption-mk-derivation.drawio +++ b/book/source/drawio/SEIPDv2-decryption-mk-derivation.drawio @@ -1,13 +1,13 @@ - + - + - + - + @@ -15,31 +15,31 @@ - + - + - + - + - + - + - + - + - + @@ -49,32 +49,32 @@ - + - + - + - + - + - + - - + + - + @@ -82,7 +82,7 @@ - + @@ -90,29 +90,32 @@ - + - + - + - + - + - + - + + + + diff --git a/book/source/drawio/SEIPDv2-decryption-mk-derivation.svg b/book/source/drawio/SEIPDv2-decryption-mk-derivation.svg index 7491d2d..0619eda 100644 --- a/book/source/drawio/SEIPDv2-decryption-mk-derivation.svg +++ b/book/source/drawio/SEIPDv2-decryption-mk-derivation.svg @@ -1,4 +1,4 @@ -
Message-Key
Message-Key
SEIPDv2
SEIPDv2
Cipher Algo.
Cipher Algo.
AEAD Algo.
AEAD Algo.
Chunk Size
Chunk Size
salt
salt
Salt
Salt
Encrypted Data
Encrypted Data
Final AEAD
Auth Tag
Final AEAD...
IKM
IKM
Session-Key
(decrypted from PKESK/SKESK)
Session-Key...
HKDF
HKDF
IV
IV
info
info
Packet Type ID,
Version Number,
Cipher Algo,
AEAD Algo,
Chunk Size
Packet Type ID,...
Packet Type, Version
Packet Type, Version
+
+
Text is not SVG - cannot display
\ No newline at end of file +
Message-Key
Message-Key
SEIPDv2
SEIPDv2
Cipher Algo.
Cipher Algo.
AEAD Algo.
AEAD Algo.
Chunk Size
Chunk Size
salt
salt
Salt
Salt
Encrypted Data
Encrypted Data
Final AEAD
Auth Tag
Final AEAD...
IKM
IKM
Session-Key
Session-Key
HKDF
HKDF
IV
IV
info
info
Packet Type ID,
Version Number,
Cipher Algo,
AEAD Algo,
Chunk Size
Packet Type ID,...
Packet Type, Version
Packet Type, Version
+
+
(obtained from PKESK/SKESK)
(obtained from PKESK/SKESK)
Text is not SVG - cannot display
\ No newline at end of file diff --git a/book/source/drawio/SKESKv4-decryption.drawio b/book/source/drawio/SKESKv4-decryption.drawio index 0a8c28f..d7c71b5 100644 --- a/book/source/drawio/SKESKv4-decryption.drawio +++ b/book/source/drawio/SKESKv4-decryption.drawio @@ -1,34 +1,34 @@ - + - + - + - + - + - + - - + + - + - + - + - + @@ -38,19 +38,19 @@ - + - + - + - + - + @@ -61,53 +61,56 @@ - + - + - + - + - + - - + + + + + + + - + - + - - - - - - - + + - + + + + diff --git a/book/source/drawio/SKESKv4-decryption.svg b/book/source/drawio/SKESKv4-decryption.svg index 879f6bc..c168a78 100644 --- a/book/source/drawio/SKESKv4-decryption.svg +++ b/book/source/drawio/SKESKv4-decryption.svg @@ -1,4 +1,4 @@ -
Passphrase
Passphrase
S2K Function
S2K Function
Session Key
Session Key
Symmetric Key
Symmetric Key
SKESKv4
SKESKv4
Cipher Algo.
Cipher Algo.
S2K Identifier
S2K Identifier
ciphertext
ciphertext
Enc. Session-Key
Enc. Session-Key
yes
yes
key
key
no
no

Is
Encrypted
Session-Key
present
Is...
Symmetric
Decryption
Symmetric...
Text is not SVG - cannot display
\ No newline at end of file +
Passphrase
Passphrase
S2K Function
S2K Function
Session Key
Session Key
Symmetric Key
Symmetric Key
SKESKv4
SKESKv4
Cipher Algo.
Cipher Algo.
S2K Identifier
S2K Identifier
ciphertext
ciphertext
Enc. Session-Key
Enc. Session-Key
yes
yes
key
key
no
no

Is
Encrypted
Session-Key
present
Is...
Symmetric
Decryption
Symmetric...
Cipher Algorithm
+
Session Key
Cipher Algorithm...
Text is not SVG - cannot display
\ No newline at end of file From 8b01fbe131cb927a027a8deaa0c793da31ea7824 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Fri, 8 Dec 2023 16:33:03 +0100 Subject: [PATCH 27/33] Change topic titles and move direct-method into own subsubsection --- book/source/11-decryption.md | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/book/source/11-decryption.md b/book/source/11-decryption.md index 88d7099..7eb7034 100644 --- a/book/source/11-decryption.md +++ b/book/source/11-decryption.md @@ -31,16 +31,16 @@ Once any of these methods succeeded, the resulting *session-key* is used to decr - using revoked subkey? ``` -## Symmetric decryption of the session-key (SKESK) +## Password-protected session-key (SKESK) Decrypting a SKESK packet to recover the *session-key* is done by performing the encryption steps in reverse, based on a user-provided passphrase. In both version 4 and version 6 of the SKESK packet, the user is prompted to enter a passphrase, which is passed through the S2K function described by the SKESK packet. -However, the subsequent steps of the procedure are different: +However, the subsequent steps of the procedure are different, as described in the following sections. ### SKESK v4 -Here, the result of the S2K function is a symmetric key, which is either used to decrypt the encrypted session-key contained in the SKESK packet, or - less commonly - used as session-key directly. +Here, the result of the S2K function is a symmetric key, which is either used to decrypt the encrypted session-key contained in the SKESK packet, or - less commonly - used as session-key directly (see [](decryption-skesk4-direct-method)). ```{note} @@ -55,11 +55,18 @@ Decrypting the session-key from a version 4 SKESK packet. ``` With version 4 SKESK packets, which are only used with version 1 SEIPD packets, the *session-key* is used as *message-key* without an intermediate derivation. -When the direct method is used, meaning no encrypted session-key was contained in the SKESK packet, the symmetric cipher algorithm ID of the SKESK packet dictates the cipher algorithm used to decrypt the plaintext from the SEIPD packet. + +(decryption-skesk4-direct-method)= +#### Direct-Method + +In version 4 of the SKESK packet, the encrypted session-key is optional. A missing encrypted session-key signals the use of the "direct-method", which means, the result of passing the passphrase through the S2K function is directly used as the session-key/message-key. + +When the direct method is used, the symmetric cipher algorithm ID of the SKESK packet dictates the cipher algorithm used to decrypt the plaintext from the SEIPD packet. Otherwise, the cipher algorithm ID to decrypt the SEIPD packet was prefixed to the decrypted session key. -Sanitizing this first byte acts as a very early quick check to verify that the used passphrase was correct. For further validation of the session-key, see [](decryption_seipd_quick_check). +Sanitizing this algorithm ID of the decrypted session-key acts as a very early quick check to verify that the used passphrase was correct. For further validation of the session-key, see [](decryption_seipd_quick_check). + ### SKESK v6 @@ -79,10 +86,9 @@ The result is the *session-key*. Decrypting the session-key from a version 6 SKESK packet. ``` -## Asymmetric decryption of the session key via PKESK +## Key-protected session key (PKESK) -More common than SKESK packets are PKESK packets which are used for asymmetric encryption of the session-key. -Here, the recipients secret key is used to decrypt the session-key. +More common than SKESK packets are PKESK packets which are used to protect the session-key using an encryption key of the recipient. ### PKESK v3 From d8b4eb16a11db6b84b6ebf6e1b021900c8bd6ba7 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Fri, 8 Dec 2023 16:38:20 +0100 Subject: [PATCH 28/33] Wording --- book/source/10-encryption.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/source/10-encryption.md b/book/source/10-encryption.md index cdde41e..e3ce2b6 100644 --- a/book/source/10-encryption.md +++ b/book/source/10-encryption.md @@ -51,7 +51,7 @@ The typical combination of mechanisms for encryption in OpenPGP is a [hybrid cry ## Encapsulating session keys: PKESK, SKESK -"*ESK" (encrypted session-key) is a family of mechanisms for encapsulation of symmetric key material. It has two branches: +"*ESK" (encrypted session-key) packets are a family of mechanisms for encapsulation of symmetric key material. There are two branches: - [PKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-public-key-encrypted-sessio): Uses asymmetric OpenPGP key material to protect a session key, and - [SKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-symmetric-key-encrypted-ses): Uses passphrases to protect the symmetric key material, instead of OpenPGP asymmetric key material (this is less commonly used). From e35c334d487213f3c4a893366073487b66daf210 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Fri, 8 Dec 2023 16:43:38 +0100 Subject: [PATCH 29/33] More wording --- book/source/10-encryption.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/source/10-encryption.md b/book/source/10-encryption.md index e3ce2b6..21456c7 100644 --- a/book/source/10-encryption.md +++ b/book/source/10-encryption.md @@ -162,8 +162,8 @@ In this scenario, it is important that the sender encrypts the message to all av #### Prevent "downgrade" -> Policy -Each implementation should define a "minimum" level of security when it comes to algorithms. -If the lowest common denominator of symmetric encryption algorithms preferred by a set of recipients provides too little security, the implementation should either use a fallback algorithm instead, or fail to produce a message at all. +Each implementation should define a "minimum" level of security when it comes to algorithms and key lengths. +If the lowest common denominator of symmetric encryption algorithms preferred by a set of recipients provides too little security, the implementation should either use a configured fallback algorithm instead, or fail to produce a message at all. ### Implications of how a recipient cert is "addressed" (fingerprint/key-ID vs. user-ID) (preferences, expiration, revocation) From 787af30af88e204f8db6036b587003280a70ad77 Mon Sep 17 00:00:00 2001 From: Heiko Schaefer Date: Sat, 9 Dec 2023 02:07:05 +0100 Subject: [PATCH 30/33] fix codespell complaints --- book/source/10-encryption.md | 2 +- book/source/11-decryption.md | 2 +- book/source/drawio/SKESKv6-decryption.drawio | 4 ++-- book/source/drawio/SKESKv6-decryption.svg | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/book/source/10-encryption.md b/book/source/10-encryption.md index 21456c7..87adc37 100644 --- a/book/source/10-encryption.md +++ b/book/source/10-encryption.md @@ -99,7 +99,7 @@ In both versions of SEIPD, the decryptor must have obtained a *session key* in a ### v1 SEIPD, based on MDC -The [version 1 SEIPD](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#version-one-seipd) mechanism is supported by all modern OpenPGP version 4 implementations. It was introduced in [RFC 4880](https://www.rfc-editor.org/rfc/rfc4880.html#section-5.13) as a replacement for the *SED* (Symmetricaly Encrypted Data) packet. SEIPDv1 provides integrity protection of the ciphertext using a SHA-1 checksum of the plaintext as modification detection code. +The [version 1 SEIPD](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#version-one-seipd) mechanism is supported by all modern OpenPGP version 4 implementations. It was introduced in [RFC 4880](https://www.rfc-editor.org/rfc/rfc4880.html#section-5.13) as a replacement for the *SED* (Symmetrically Encrypted Data) packet. SEIPDv1 provides integrity protection of the ciphertext using a SHA-1 checksum of the plaintext as modification detection code. Version 1 SEIPD can only be combined with [version 3 PKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#v3-pkesk) and/or [version 4 SKESK](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#v4-skesk) packets. diff --git a/book/source/11-decryption.md b/book/source/11-decryption.md index 7eb7034..030c1f0 100644 --- a/book/source/11-decryption.md +++ b/book/source/11-decryption.md @@ -169,7 +169,7 @@ The result is split into the message key and first half of the IV. In a first step, a message-key and half of an IV is derived from the session-key. ``` -Then, the contens of the SEIPDs encrypted data are split into chunks, which are processed sequentially. Each chunk is decrypted using AEAD with parameters from the SEIPD packet as *additional data*. +Then, the contents of the SEIPDs encrypted data are split into chunks, which are processed sequentially. Each chunk is decrypted using AEAD with parameters from the SEIPD packet as *additional data*. For each chunk, the chunk index starting at `0` is passed into the function as second half of the IV. All decrypted plaintext blocks are appended to form the result of the decryption process. diff --git a/book/source/drawio/SKESKv6-decryption.drawio b/book/source/drawio/SKESKv6-decryption.drawio index 5bdafa0..a9ec75b 100644 --- a/book/source/drawio/SKESKv6-decryption.drawio +++ b/book/source/drawio/SKESKv6-decryption.drawio @@ -122,7 +122,7 @@ - + @@ -154,7 +154,7 @@ - + diff --git a/book/source/drawio/SKESKv6-decryption.svg b/book/source/drawio/SKESKv6-decryption.svg index 898a5fc..02e79a2 100644 --- a/book/source/drawio/SKESKv6-decryption.svg +++ b/book/source/drawio/SKESKv6-decryption.svg @@ -1,4 +1,4 @@ -
Encrypted Message
Encrypted Message
SEIPDv2
SEIPDv2
Salt: 49f8edc3
Salt: 49f8edc3
Ciphertext
Ciphertext
Cipher Algo.
Cipher Algo.
AEAD Mode
AEAD Mode
Chunk Size
Chunk Size
AEAD Auth Tag
AEAD Auth Tag
Passphrase
Passphrase
SKESKv6
SKESKv6
Cipher Algo.
Cipher Algo.
AEAD Mode
AEAD Mode
S2K Identifier
S2K Identifier
IV: 0xC0FFEE
IV: 0xC0FFEE
Enc. Session-Key
Enc. Session-Key
AEAD Auth Tag
AEAD Auth Tag
S2K Function
S2K Function
HKDF
(no salt)
HKDF(no salt)
IKM
IKM
Packet Type ID,
Packet Version,
Cipher Algo,
AEAD Mode
Packet Type ID,...
Packet Type and Verison
Packet Type and Verison
Key Encryption Key
Key Encryption Key
Info
Info
AEAD
AEAD
Packet Type ID,
Packet Version,
Cipher Algo,
AEAD Mode
Packet Type ID,...
Packet Type and Verison
Packet Type and Verison
Salt
Salt
AD
AD
Key
Key
Ciphertext
Ciphertext
Auth Tag
Auth Tag
Session Key
Session Key
Text is not SVG - cannot display
\ No newline at end of file +
Encrypted Message
Encrypted Message
SEIPDv2
SEIPDv2
Salt: 49f8edc3
Salt: 49f8edc3
Ciphertext
Ciphertext
Cipher Algo.
Cipher Algo.
AEAD Mode
AEAD Mode
Chunk Size
Chunk Size
AEAD Auth Tag
AEAD Auth Tag
Passphrase
Passphrase
SKESKv6
SKESKv6
Cipher Algo.
Cipher Algo.
AEAD Mode
AEAD Mode
S2K Identifier
S2K Identifier
IV: 0xC0FFEE
IV: 0xC0FFEE
Enc. Session-Key
Enc. Session-Key
AEAD Auth Tag
AEAD Auth Tag
S2K Function
S2K Function
HKDF
(no salt)
HKDF(no salt)
IKM
IKM
Packet Type ID,
Packet Version,
Cipher Algo,
AEAD Mode
Packet Type ID,...
Packet Type and Version
Packet Type and Version
Key Encryption Key
Key Encryption Key
Info
Info
AEAD
AEAD
Packet Type ID,
Packet Version,
Cipher Algo,
AEAD Mode
Packet Type ID,...
Packet Type and Version
Packet Type and Version
Salt
Salt
AD
AD
Key
Key
Ciphertext
Ciphertext
Auth Tag
Auth Tag
Session Key
Session Key
Text is not SVG - cannot display
\ No newline at end of file From a3a32f556d5c22568a413465ad7a0e35a3862f62 Mon Sep 17 00:00:00 2001 From: David Runge Date: Sat, 9 Dec 2023 11:46:08 +0100 Subject: [PATCH 31/33] Modify codespell to not check example files Example files may contain crypto gibberish and will trip codespell. Signed-off-by: David Runge --- book/.codespellrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/.codespellrc b/book/.codespellrc index 3fe0661..0ca6c79 100644 --- a/book/.codespellrc +++ b/book/.codespellrc @@ -2,4 +2,4 @@ # SPDX-License-Identifier: CC0-1.0 [codespell] -skip = ./build,./input,./source/diag/*.svg +skip = ./build,./input,./source/diag/*.svg,./source/examples/* From 90cca801ce4ed3c4d998bf26255feea459efe7af Mon Sep 17 00:00:00 2001 From: David Runge Date: Sat, 9 Dec 2023 11:57:11 +0100 Subject: [PATCH 32/33] Move ASCII armored encrypted message in ch21 to example file Signed-off-by: David Runge --- book/source/21-zoom_encyption.md | 12 ++---------- .../examples/ascii_armored_encrypted_message.asc | 9 +++++++++ 2 files changed, 11 insertions(+), 10 deletions(-) create mode 100644 book/source/examples/ascii_armored_encrypted_message.asc diff --git a/book/source/21-zoom_encyption.md b/book/source/21-zoom_encyption.md index e863962..f48b73b 100644 --- a/book/source/21-zoom_encyption.md +++ b/book/source/21-zoom_encyption.md @@ -18,16 +18,8 @@ $ echo "hello world" | sq encrypt --recipient-file alice.pub This produces an ASCII armored encrypted message: -```text ------BEGIN PGP MESSAGE----- - -wV0GIQbApYOEpDjloU9zcSQmpNRduu70o55rMLCdVRP5eKzKlBkxYaQzuusD78oj -AdiGwQ8MI8sSAXAV4AEMKIcbINqhIBgSm5EV9h+Yl/XV3fEZ1JOaBnrtso2ZAS7S -cgIHAQaWc/Ip4Thq0EZDZwlpRUk/TUL+TWEpsGdQs8ifDyFAk7t3+3XvvLr5dUg3 -+Ot+sESkCSjhrZk50HIjwjBVZ6Y159yfaOqttMT6cXqWaxIishPaJ+OR1q2bZS1N -2jFbaROOcbASK6AVzqCWneqkIA== -=WFpq ------END PGP MESSAGE----- +```{literalinclude} examples/ascii_armored_encrypted_message.asc +:language: text ``` ### Inspect the packet dump of the encrypted message diff --git a/book/source/examples/ascii_armored_encrypted_message.asc b/book/source/examples/ascii_armored_encrypted_message.asc new file mode 100644 index 0000000..68bfa3f --- /dev/null +++ b/book/source/examples/ascii_armored_encrypted_message.asc @@ -0,0 +1,9 @@ +-----BEGIN PGP MESSAGE----- + +wV0GIQbApYOEpDjloU9zcSQmpNRduu70o55rMLCdVRP5eKzKlBkxYaQzuusD78oj +AdiGwQ8MI8sSAXAV4AEMKIcbINqhIBgSm5EV9h+Yl/XV3fEZ1JOaBnrtso2ZAS7S +cgIHAQaWc/Ip4Thq0EZDZwlpRUk/TUL+TWEpsGdQs8ifDyFAk7t3+3XvvLr5dUg3 ++Ot+sESkCSjhrZk50HIjwjBVZ6Y159yfaOqttMT6cXqWaxIishPaJ+OR1q2bZS1N +2jFbaROOcbASK6AVzqCWneqkIA== +=WFpq +-----END PGP MESSAGE----- From 4903214c7a1a6e4b356db7e0e9601af0502fa7cd Mon Sep 17 00:00:00 2001 From: David Runge Date: Sat, 9 Dec 2023 12:16:52 +0100 Subject: [PATCH 33/33] Add contributing guideline on including external files Signed-off-by: David Runge --- CONTRIBUTING.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c01e66e..bfb5e07 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -56,6 +56,27 @@ The syntax follows what is available in [MyST-parser]. --- +#### Including files + +It is advisable to [include external files](https://myst-parser.readthedocs.io/en/latest/syntax/code_and_apis.html#including-code-from-files) with data, instead of using a code block to display the data. + +--- +**NOTE**: The reason for this is, that `codespell` is used to check for common spelling mistakes and output from other programs may contain text, that will trigger it to flag it as an error. + +To work around this issue, `codespell` is configured to ignore example files, so that not entire chapter files have to be ignored. + +--- + +Use the `{literalcode}` directive to include files: + +```` + +```{literalinclude} examples/my-example.txt +:language: text +``` + +```` + #### Cross-referencing There are a few guidelines when it comes to cross-referencing, which work around oddities with [sphinx] and [MyST-parser]: