Subject: [openpgp] v5 Secret-Key Packet Formats

From: Werner Koch <[email-masked]>
Date: Fri, 12 Jan 2018 11:29:12 +0100

Hi!

The protection of secret keys usning AEAD has this requirement:

   If the string-to-key usage octet is 253, the encrypted MPI values are
   encrypted as one combined plaintext exactly as an AEAD Encrypted Data
   packet with a chunk size octet of 10 would be.  This implicit chunk
                 ^^^^^^^^^^^^^^^^^^^^^^
   size octet is included in the normal calculations of additional data.

Given chunk_size = ((uint64_t)1 << (c + 6)) this works only for keys
requiring less than 64 KiByte for the secret parameters.  This may not
be enough in the future and thus I suggest to increase the default chunk
size.  A value of 20 would allow for 64 MiByte which should be enough,
but 25, resulting in 2^31 would be more logical.

However, there is a practical problem using chunks for secret key (and
also with Symmetric-Key Encrypted Session Key Packets): we need to run
two AEAD encryptions for those commonly short key packets because our
description of the AEAD says:

   After the final chunk, the AEAD algorithm is used to produce a final
   authentication tag encrypting the empty string.  This AEAD instance
   is given the additional data specified above, plus an eight-octet,
   big-endian value specifying the total number of plaintext octets
   encrypted.  This allows detection of a truncated ciphertext.

This is overhead in the implementation and the specification (question
of the default chunk size).  I would prefer that we remove the use of
the chunked AEAD encryption from Secret-Key and Symmetric-Key Encrypted
Session Key Packets.  Thus instead of

  For each chunk, the AEAD construction is given the packet header,
  version number, cipher algorithm octet, AEAD algorithm octet, chunk
  size octet, and an eight-octet, big-endian chunk index as additional
  data.  The index of the first chunk is zero.

we make use of the parameters already in the Secret-Key Packet:

  * [Optional] If string-to-key usage octet was 255, 254, or 253, a one-
    octet symmetric encryption algorithm.

  * [Optional] If string-to-key usage octet was 253, a one-octet AEAD
    algorithm.

  [...]

  * [Optional] If secret data is encrypted (string-to-key usage
    octet not zero), an Initial Vector (IV) of the same length as
    the cipher's block size.

The IV already matches our requirement for EAX and for OCB we would set
its LSByte to zero.  The description of the AEAD mode needs to be
changed to explain the chunked and the non-chunked mode.

What do you think?

Salam-Shalom,

   Werner

-- 
Die Gedanken sind frei.  Ausnahmen regelt ein Bundesgesetz.

---

From: Tom Ritter <[email-masked]>
Date: Fri, 12 Jan 2018 09:22:44 -0600

> What do you think?

Would this be adding a new mode that would have to be implemented?
That is in addition to adding chunked AEAD we're now also adding
non-chunked AEAD?

-tom

---

From: Werner Koch <[email-masked]>
Date: Fri, 12 Jan 2018 17:22:45 +0100

> That is in addition to adding chunked AEAD we're now also adding

No.  Like the current CFB mode, AEAD will be used at 3 places:

 1. Bulk data encryption

 2. Encryption used by the secret-key session key packet (which makes it
    possible to encrypt to several passphrases)

 3. Encryption of the secret key.

My claim is that the chunked mode is only used for 1.  For 2 and 3 we
can avoid any chunked mode and thus do not need to assume a certain
chunk size.

Sure, we could also keep on using CFB for 2 and 3 but that would require
a minimalist implementation to implement CFB and AEAD(EAX).

Shalom-Salam,

   Werner

---

From: "brian m. carlson" <[email-masked]>
Date: Sat, 13 Jan 2018 00:37:57 +0000

> a minimalist implementation to implement CFB and AEAD(EAX).

I don't have a strong preference between non-chunked or chunked,
although I do quite want to avoid relying on CFB for the future.

I wrote it the way I did for a couple of reasons:

* We have various uses of CFB throughout the document, some using an
  all-zero IV and random prefix and some using a real IV.  I wanted to
  provide one consistent technique for encrypting to reduce complexity.
* The chunk size was large enough to cover most current keys, and it
  could be arbitrarily extended (using multiple chunks) to any future
  keys.
* The chunk size was small enough to be practical for low-resource
  devices without having to release ciphertext before tag verification,
  even if we have large keys.  (I know the non-chunked nature of the MDC
  packet has had DoS implications in GnuPG in the past.)
* The extra block at the end is required to avoid truncation attacks in
  chunked mode, and the cost of 16 bytes in the secret key packets
  didn't seem like a huge cost given the other constraints.

This is just a rationale, and if implementers or other folks think a
non-chunked approach for key material is better, I'm happy to go along.
-- 
brian m. carlson / brian with sandals: Houston, Texas, US
https://www.crustytoothpaste.net/~bmc | My opinion only
OpenPGP: https://keybase.io/bk2204

---

From: Werner Koch <[email-masked]>
Date: Thu, 18 Jan 2018 10:41:47 +0100

Hello!

> although I do quite want to avoid relying on CFB for the future.

I fully agree.

>   provide one consistent technique for encrypting to reduce complexity.

I understand this.  I am right now implementing this and figured a
couple of minor problems.  See below.

>   could be arbitrarily extended (using multiple chunks) to any future

Okay.  But that makes simple processing of secret key material more
complicated than needed.  I can imagine application which do not
implement encryption but want to handle protected secret keys.  A
non-chunked AEAD encryption makes this much easier.

>   didn't seem like a huge cost given the other constraints.

This is not a problem for bulk encryption , but blows up secret ECC keys
protecion.  But that is really minor.

> non-chunked approach for key material is better, I'm happy to go along.

I have not seen other comments but let me prepare a patch to use
non-chunked AEAD for non-bulk data.

What I noticed in the AEAD mode:

1. There is a problem including the entire packet header in the
   additional data: When using partial length encoding we would need to
   use the first partial length here.  gpg uses a a pipeline of
   functions as data structure.  Thus the encryption layer does not know
   how the next function down the line will block the data to emit the
   partial length encoding.  Sure this can be hacked around but it would
   be ugly.  Thus I propose this change:

--8<---------------cut here---------------start------------->8---
-For each chunk, the AEAD construction is given the packet header,
-version number, cipher algorithm octet, AEAD algorithm octet, chunk size
-octet, and an eight-octet, big-endian chunk index as additional
-data.  The index of the first chunk is zero.
+For each chunk, the AEAD construction is given the Packet Tag, version
+number, cipher algorithm octet, AEAD algorithm octet, chunk size
+octet, and an eight-octet, big-endian chunk index as additional data.
+The index of the first chunk is zero.  Note that the Packet Tag has
+the value 0xd4 and that the length octets are not included because
+they may vary in case of a partial body length.
--8<---------------cut here---------------end--------------->8---

2. The above immediately raises the question why we need the Packet Tag
   at all.  After all it will be a constant (the AEAD packet requires a
   new style CTB and thus there is only one encoding).  I assume you did
   this to prepare against a future rollback attack in case we ever
   introduce a new style AEAD encryption.  But then, why do we have the
   version number as first item in the AEAD packet?  It can serve the
   same purpose.  Or is this to distinguish between AEAD used for bulk
   encryption and secret key encryption?  What packet tag would we use
   for the latter (with old-style CTB we can have several encodings)?

   Shouldn't we either drop the packet header from the additional data
   or can we define fixed values depending on the use like:

     0xd4 := AEAD Encrypted Data Packet  (tag 20)
     0xc3 := SKESK (Symmetric-Key Encrypted Session Key Packet, tag 3)
     0xc5 := Secret-Key Packet (tag 5) and Secret-Subkey Packet (tag 7)

3. The AEAD Encrypted Data Packet specifies the cipher algorithm to use.
   The old encryption modes had no way to specify the algorithm.
   Instead they take the algorithm either from the SKESK or the PKESK
   (Public-Key Encryption Session Key Packet, tag 1).  For simple
   encryption without even an SKESK it is implementation defined how to
   get the algorithm.

   I like that algorithm id in the AEAD packet.  But should we specify
   what to do on a conflict and whether it is allowed that the SKESK
   uses a different algorithm than that in the AEAD packet?  I currently
   print a warning and continue but I am not sure whether this is really
   needed.  The potential for a conflict has always been in OpenPGP: A
   faulty implementation may encrypt to several keys giving different
   algorithms in each PKESK and thus not all recipients would be able to
   decrypt the message.  I am not ware of such bug, though.

Shalom-Salam,

   Werner

---

From: Nickolay Olshevsky <[email-masked]>
Date: Thu, 18 Jan 2018 14:38:43 +0300

Hello!

> On Jan 18, 2018, at 12:41, Werner Koch <[email-masked]> wrote:
> 

Would like to add that I got the same problem that you described during implementation of the AEAD.
With partial packet lengths you’d need to cache first partial chunk on the encryption layer to set the additional data.
It can be worked around but removing packet’s length from ad will make things easier without loosing any security properties.
So agree with the proposed changes.

>     0xc5 := Secret-Key Packet (tag 5) and Secret-Subkey Packet (tag 7)

Also agree - this will make things more clear. And should not make an impact on the security as well.

>   decrypt the message.  I am not ware of such bug, though.

Yep, some statement like ‘symmetric algorithm in previous v4 SKESK/v3 PKESK packets should be ignored for AEAD packet’ will make things more clear.
However, should it be allowed to use v4 SKESK with AEAD encrypted data packet?

Best regards,
	Nickolay Olshevsky
	[email-masked]

---

From: Werner Koch <[email-masked]>
Date: Thu, 18 Jan 2018 14:15:04 +0100

> However, should it be allowed to use v4 SKESK with AEAD encrypted data packet?

It won't harm but it is useless to allow this because implementations
supporting AEAD should not have any problem to support a v5 SKESK.

The only reason I can imagine that this is useful is to allow
re-encryption of existing data to an existing session key.  For example
to move from CFB to AEAD.  So a SHOULD use v5 SKESK would be
appropriate.

Shalom-Salam,

   Werner

---

From: "brian m. carlson" <[email-masked]>
Date: Fri, 19 Jan 2018 23:08:21 +0000

> --8<---------------cut here---------------end--------------->8---

That should be fine.  We detect truncation elsewhere anyway.

>      0xc5 := Secret-Key Packet (tag 5) and Secret-Subkey Packet (tag 7)

It's a good idea to use domain separation to ensure that packets can't
be copied and pasted between packet types.  It prevents cases where
someone accidentally creates a decryption oracle and reuses keys or
passphrases.  The packet tag should be enough for this, since the
authentication tag will flag the failure immediately if it changes.

I tried to model the AEAD authenticated data after TLS, since that has
generally been a successful model that's been resistant to attacks
involving the MAC.  The TLS model is "authenticate the entire packet".

>    decrypt the message.  I am not ware of such bug, though.

I would just say a hard error is the right thing to do.  Clearly if
there's disagreement, we can't be sure what the sender intended.  It
would be better to fail than produce a bunch of junk data.
-- 
brian m. carlson / brian with sandals: Houston, Texas, US
https://www.crustytoothpaste.net/~bmc | My opinion only
OpenPGP: https://keybase.io/bk2204

---

From: Werner Koch <[email-masked]>
Date: Mon, 22 Jan 2018 10:17:16 +0100

> involving the MAC.  The TLS model is "authenticate the entire packet".

Well, we do basically the same except that we need to leave out the
packet lengths because OpenPGP different here form TLS.  Find below my
changes on using non-chunked AEAD for secret keys and the SKESK.

> would be better to fail than produce a bunch of junk data.

Well, it won't be junk data if the implementaion supports all required
algorithms.  For example the SKESK uses AES-256 to encrypt the session
key and the AEAD Encrypted Data Packet uses AES-128.  I can't see a
security problem here.

Salam-Shalom,

   Werner

--8<---------------cut here---------------start------------->8---
    Do not used chunked AEAD encryption for secret keys and SKESK.
    
    This also gives examples on how to encode the additional data.

	Modified   middle.mkd
diff --git a/middle.mkd b/middle.mkd
index a48a6fb..b7be5c6 100644
--- a/middle.mkd
+++ b/middle.mkd
@@ -1801,11 +1801,17 @@ ## {5.3} Symmetric-Key Encrypted Session Key Packets (Tag 3)
   * The encrypted session key itself, which is decrypted with the
     string-to-key object using the given cipher and AEAD mode.
 
-  * A final, summary authentication tag for the AEAD mode.
+  * An authentication tag for the AEAD mode.
+
+The encrypted session key is encrypted using one of the AEAD
+algorithms specified for the AEAD Encrypted Packet.  Note that no
+chunks are used and that there is only one authentication tag.  The
+Packet Tag in new format encoding (bits 7 and 6 set, bits 5-0 carry
+the packet tag), the packet version number, the cipher algorithm
+octet, and the AEAD algorithm octet are given as additional data.  For
+example, the additional data used with EAX and AES-128 consists of the
+octets 0xC3, 0x05, 0x07, and 0x01.
 
-The encrypted session key is encrypted exactly as an AEAD Encrypted Data
-packet with a chunk size octet of 10 would be.  This implicit chunk size
-octet is included in the normal calculations of additional data.
 
 ## {5.4} One-Pass Signature Packets (Tag 4)
 
@@ -1990,9 +1996,12 @@ ### {5.5.3} Secret-Key Packet Formats
     string-to-key specifier.  The length of the string-to-key
     specifier is implied by its type, as described above.
 
-  * [Optional] If secret data is encrypted (string-to-key usage
-    octet not zero), an Initial Vector (IV) of the same length as
-    the cipher's block size.
+  * [Optional] If secret data is encrypted (string-to-key usage octet
+    not zero), an Initial Vector (IV) of the same length as the
+    cipher's block size.  If string-to-key usage octet was 253 the IV
+    is used as the nonce for the AEAD algorithm.  If the AEAD
+    algorithm requires a shorter nonce, the high-order bits of the IV
+    are used and the remaining bits MUST be zero.
 
   * Only for a version 5 packet, a four-octet scalar octet count for
     the following key material.
@@ -2037,9 +2046,15 @@ ### {5.5.3} Secret-Key Packet Formats
 are encrypted, including the MPI bitcount prefix.
 
 If the string-to-key usage octet is 253, the encrypted MPI values are
-encrypted as one combined plaintext exactly as an AEAD Encrypted Data
-packet with a chunk size octet of 10 would be.  This implicit chunk size
-octet is included in the normal calculations of additional data.
+encrypted as one combined plaintext using one of the AEAD algorithms
+specified for the AEAD Encrypted Packet.  Note that no chunks are used
+and that there is only one authentication tag.  The Packet Tag in new
+format encoding (bits 7 and 6 set, bits 5-0 carry the packet tag), the
+packet version number, the cipher algorithm octet, and the AEAD
+algorithm octet are given as additional data.  For example, the
+additional data used with EAX and AES-128 in a Secret-Key Packet of
+version 4 consists of the octets 0xC5, 0x04, 0x07, and 0x01; in a
+Secret-Subkey Packet the first octet would be 0xC7.
 
 The two-octet checksum that follows the algorithm-specific portion is
 the algebraic sum, mod 65536, of the plaintext of all the algorithm-
@@ -2050,7 +2065,9 @@ ### {5.5.3} Secret-Key Packet Formats
 deprecated; an implementation SHOULD NOT use it, but should rather use
 the SHA-1 hash denoted with a usage octet of 254.  The reason for this
 is that there are some attacks that involve undetectably modifying the
-secret key.
+secret key.  If the string-to-key usage octet is 253 no checksum or
+SHA-1 hash is used but the authentication tag of the AEAD algorithm
+follows.

 ## Algorithm-specific Parts of Keys
@@ -2640,7 +2657,10 @@ ## AEAD Encrypted Data Packet (Tag 20)
 format encoding (bits 7 and 6 set, bits 5-0 carry the packet tag),
 version number, cipher algorithm octet, AEAD algorithm octet, chunk
 size octet, and an eight-octet, big-endian chunk index as additional
-data.  The index of the first chunk is zero.
+data.  The index of the first chunk is zero.  For example, the
+additional data of the first chunk using EAX and AES-128 with a chunk
+size of 64 kiByte consists of the octets 0xD4, 0x01, 0x07, 0x01, 0x10,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, and 0x00.
 
 After the final chunk, the AEAD algorithm is used to produce a final
 authentication tag encrypting the empty string.  This AEAD instance is
--8<---------------cut here---------------end--------------->8---