From 8520beb51f0640db1814a91634d9b0056872da1f Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Fri, 19 Jul 2019 12:39:50 +0000 Subject: [PATCH] proposal 152 updates --- i2p2www/spec/proposals/152-ecies-tunnels.rst | 238 +++++++++---------- 1 file changed, 108 insertions(+), 130 deletions(-) diff --git a/i2p2www/spec/proposals/152-ecies-tunnels.rst b/i2p2www/spec/proposals/152-ecies-tunnels.rst index 0bb55f8e2..bcc5184ca 100644 --- a/i2p2www/spec/proposals/152-ecies-tunnels.rst +++ b/i2p2www/spec/proposals/152-ecies-tunnels.rst @@ -6,7 +6,7 @@ ECIES Tunnels :author: chisana :created: 2019-07-04 :thread: http://zzz.i2p/topics/2737 - :lastupdated: 2019-07-18 + :lastupdated: 2019-07-19 :status: Open .. contents:: @@ -24,7 +24,8 @@ Specifications for how to handle mixed tunnel hops are provided. No changes will be made to the format, processing, or encryption of ElGamal hops. -All ElGamal routers are treated as though running without changes from this document. +ElGamal tunnel creators will need to create ephemeral X25519 keypairs per-hop, and +follow this spec for creating tunnels containing ECIES hops. The proposal is split into two sections: @@ -90,9 +91,12 @@ bytes 0-3: tunnel ID to receive messages as, nonzero byte 72: flags bytes 73-76: request time (in hours since the epoch, rounded down) bytes 77-80: next message ID + bytes 81-464: tunnel build options / random padding {% endhighlight %} +The tunnel build options block will be defined by [Tunnel-Build-Options]_. + Request Record Spec Encrypted (ECIES) ````````````````````````````````````` @@ -102,8 +106,7 @@ Request Record Spec Encrypted (ECIES) bytes 0-15: hop's truncated identity hash bytes 16-47: sender's ephemeral public key - bytes 48-128: ChaChaPoly AEAD encrypted build request record - bytes 129-511: Random padding + bytes 48-511: ChaChaPoly AEAD encrypted BuildRequestRecord bytes 512-527: Poly1305 MAC {% endhighlight %} @@ -123,11 +126,8 @@ Bit order: 76543210 (bit 7 is MSB) bit 7: if set, allow messages from anyone bit 6: if set, allow messages to anyone, and send the reply to the specified next hop in a Tunnel Build Reply Message - bit 5: if set, ChaCha20 reply encryption selected (ECIES build record), - also indicates next hop is ECIES - AES256/CBC (ElGamal) otherwise - bit 4: if set, only ECIES hops in the tunnel, use Blowfish+ChaCha20 layer encryption - bits 3-0: Undefined, must set to 0 for compatibility with future options + bit 5: if set, only ECIES hops in the tunnel, use Blowfish+ChaCha20 layer encryption + bits 4-0: Undefined, must set to 0 for compatibility with future options {% endhighlight %} @@ -142,10 +142,12 @@ Reply Record Spec Unencrypted (ECIES) {% highlight lang='dataspec' %} bytes 0: Reply byte - bytes 1-511: Random padding + bytes 1-511: Tunnel Build Options / Random padding {% endhighlight %} +For options formatting refer to the [Tunnel-Build-Options]_ spec. + Reply flags for ECIES reply records should use the following values to avoid fingerprinting: - 0x00 (accept) @@ -189,17 +191,11 @@ Symmetric cryptography preprocessing will run in the same way: - once by the hop - once on final reply processing -When mixed routers are hops in the same tunnel, and the current hop is ECIES, -it will check if reply encryption flag is set (indicating ChaCha20). - -If the current hop is an ECIES hop, and ChaCha20 reply encryption is selected, -the reply key is used to ChaCha20 "decrypt" its reply and other records. +When mixed tunnels are used, tunnel creators will need to base the symmetric encryption +of BuildRequestRecord on the current and previous hop's encryption type. -If the current hop is an ElGamal hop, the reply encryption bit is ignored, -and the reply key is used to AES256/CBC "decrypt" its reply and other records. - -This means later hops in the tunnel are preprocessed using a mix of ChaCha20 -and AES256/CBC, using the reply key of preceding hops. +Each hop will use it's own encryption type for encrypting BuildReplyRecords, and the other +records in the VariableTunnelBuildMessage (VTBM). On the reply path, the endpoint (sender) will need to undo the [Multiple-Encryption]_, using each hop's reply key. @@ -207,15 +203,17 @@ As a clarifying example, let's look at an outbound tunnel w/ ECIES surrounded by - Sender (OBGW) -> ElGamal (H1) -> ECIES (H2) -> ElGamal (H3) -All records are in their encrypted state (using ElGamal or ECIES). +All BuildRequestRecords are in their encrypted state (using ElGamal or ECIES). AES256/CBC cipher, when used, is still used for each record, without chaining across multiple records. +Likewise, ChaCha20 will be used to encrypt each record, not streaming across the entire VTBM. + The request records are preprocessed by the Sender (OBGW): - H3's record is "encrypted" using: - - H2's reply key (AES256/CBC) + - H2's reply key (ChaCha20) - H1's reply key (AES256/CBC) - H2's record is "encrypted" using: @@ -226,8 +224,6 @@ The request records are preprocessed by the Sender (OBGW): Only H2 checks the reply encryption flag, and sees its followed by AES256/CBC. -H3 checks the flags, sees it is an OBEP (bit 6 set), and ignores the reply encryption bit. - After being processed by each hop, the records are in a "decrypted" state: - H3's record is "decrypted" using: @@ -237,15 +233,15 @@ After being processed by each hop, the records are in a "decrypted" state: - H2's record is "decrypted" using: - H3's reply key (AES256/CBC) - - H2's reply key (AES256/CBC) + - H2's reply key (ChaCha20-Poly1305) - H1's record is "decrypted" using: - H3's reply key (AES256/CBC) - - H2's reply key (AES256/CBC) + - H2's reply key (ChaCha20) - H1's reply key (AES256/CBC) -When there are no inbound tunnels at startup, the Sender (IBEP) postprocesses the reply: +The tunnel creator, a.k.a. Inbound Endpoint (IBEP), postprocesses the reply: - H3's record is "encrypted" using: @@ -254,76 +250,19 @@ When there are no inbound tunnels at startup, the Sender (IBEP) postprocesses th - H2's record is "encrypted" using: - H3's reply key (AES256/CBC) - - H2's reply key (AES256/CBC) + - H2's reply key (ChaCha20-Poly1305) - H1's record is "encrypted" using: - H3's reply key (AES256/CBC) - - H2's reply key (AES256/CBC) - - H1's reply key (AES256/CBC) - -If H3 (OBEP) is an ECIES hop, it checks the reply encryption flag for -ChaCha20 (bit 5 set) or AES256/CBC (bit 5 unset). - -H2 would also see that the reply encryption flag is set, and "decrypt" its reply -and other records using ChaCha20. - -So our example changes to the following hops: - -- Sender (OBGW) -> ElGamal (H1) -> ECIES (H2) -> ECIES (H3) - -The request records are preprocessed by the Sender (OBGW): - -- H3's record is "encrypted" using: - - - H2's reply key (ChaCha20) - - H1's reply key (AES256/CBC) - -- H2's record is "encrypted" using: - - - H1's reply key (AES256/CBC) - -- H1's record goes out without symmetric encryption - -After being processed by each hop, the records are in a "decrypted" state: - -- H3's record is "decrypted" using: - - - H3's reply key (ChaCha20) - -- H2's record is "decrypted" using: - - - H3's reply key (ChaCha20) - - H2's reply key (ChaCha20) - -- H1's record is "decrypted" using: - - - H3's reply key (ChaCha20) - - H2's reply key (ChaCha20) - - H1's reply key (AES256/CBC) - -When there are no inbound tunnels at startup, the Sender (IBEP) postprocesses the reply: - -- H3's record is "encrypted" using: - - - H3's reply key (ChaCha20) - -- H2's record is "encrypted" using: - - - H3's reply key (ChaCha20) - - H2's reply key (ChaCha20) - -- H1's record is "encrypted" using: - - - H3's reply key (ChaCha20) - H2's reply key (ChaCha20) - H1's reply key (AES256/CBC) Request Record Key, Reply Key, Tunnel Layer Key, and IV Key KDF (ECIES) ---------------------------------------------------------------------------------- +----------------------------------------------------------------------- The ``recordKey`` takes the place of the product of the ElGamal exchange. It is used -to AEAD encrypt request and reply records for ECIES hops. +to AEAD encrypt request records for ECIES hops. Below is a description of how to derive the keys previously transmitted in request records. @@ -331,7 +270,7 @@ Below is a description of how to derive the keys previously transmitted in reque {% highlight lang='dataspec' %} -// Sender generates an X25519 ephemeral keypair per VTBM (sesk, sepk) +// Sender generates an X25519 ephemeral keypair per ECIES hop in the VTBM (sesk, sepk) sesk = GENERATE_PRIVATE() sepk = DERIVE_PUBLIC(sesk) @@ -360,42 +299,58 @@ Below is a description of how to derive the keys previously transmitted in reque {% endhighlight %} ``replyKey``, ``layerKey`` and ``IVKey`` must still be included inside ElGamal records, -and can be generated randomly. For ElGamal, the ``recordKey`` is just the result of ElGamal multiplication. +and can be generated randomly. For ElGamal, the ``recordKey`` is not needed, since the +tunnel creator can directly encrypt to an ElGamal hop's public key. Keys are omitted from ECIES records (since they can be derived at the hop). -Request Record Preprocessing for ECIES Hops -------------------------------------------- +BuildRequestRecord Encryption for ECIES Hops +-------------------------------------------- .. raw:: html {% highlight lang='dataspec' %} // See record key KDF for key generation - (ciphertext, mac) = ChaCha20-Poly1305(msg = unencrypted record, nonce = 0, AD = Sha256(recordKey), key = recordKey) + // Repeat for each ECIES hop record in the VTBM + (ciphertext, mac) = ChaCha20-Poly1305(msg = unencrypted record, nonce = 0, AD = Sha256(hop's recordKey), key = hop's recordKey) + encryptedRecord = ciphertext \|\| MAC - // For subsequent records past the initial hop - // nonce = one \+ zero-indexed order of record in the TunnelBuildMessage - symCiphertext = ChaCha20(msg = ciphertext \|\| MAC, nonce, key = replyKey of preceding hop) + For subsequent records past the initial hop, pre-emptively decrypt for each preceding hop in the tunnel -{% endhighlight %} + // If the preceding hop is ECIES: + nonce = one \+ zero-indexed order of record in the VariableTunnelBuildMessage + key = replyKey of preceding hop + symCiphertext = ChaCha20(msg = encryptedRecord, nonce, key) -Request Record Encryption from ElGamal Tunnel Creators ------------------------------------------------------- + // If the preceding hop is ElGamal: + IV = reply IV of preceding hop + key = reply key of preceding hop + symCiphertext = AES256/CBC-Decrypt(msg = encryptedRecord, IV, key) -No changes are made for how ElGamal routers preprocess and encrypt request records. +{% endhighlight %} + +Request Record Encryption from ElGamal + ECIES Tunnel Creators +-------------------------------------------------------------- -This means ECIES hops will behave like ElGamal hops in ElGamal created tunnels. +ElGamal tunnel creators will need to generate an ephemeral X25519 keypair for each +ECIES hop in the tunnel, and use scheme above for encrypting their BuildRequestRecord. +ElGamal tunnel creators will use the scheme prior to this spec for encrypting to ElGamal hops. -For ECIES hops to detect ElGamal tunnel creators, trial-decryption is needed. +ECIES tunnel creators will need to encrypt to the ElGamal hop's public key using the +scheme prior to this spec. ECIES tunnel creators will use the above scheme for encrypting +to ECIES hops. -It will be necessary to first try decrypting the request record as though it came from an ECIES router. +This means that tunnel hops will only see encrypted records from their same encryption type. -If trial-decryption fails, attempt decryption as though from an ElGamal router. +For ElGamal and ECIES tunnel creators, they will generate unique ephemeral X25519 keypairs +per-hop for encrypting to ECIES hops. -If the record includes expected fields (keys + IV, flags, etc, and valid Sha256 of preceding data), ElGamal decryption was succesful. +**WARNING**: if the same ephemeral keypair is used for more than one hop, it can only be +used for at most **two** hops, and the hops must be **consecutive**. -If ElGamal decryption fails, drop the message without reply, or forwarding to next hop. +**WARNING**: Using the same ephemeral keys for non-consecutive hops, or more than two hops, +allows colluding hops to know they're in the same tunnel, **VERY BAD**!!! Reply Record Encryption for ECIES Hops -------------------------------------- @@ -409,27 +364,14 @@ See [RFC-7539-S4]_ Security Considerations for more information. {% highlight lang='dataspec' %} // See reply key KDF for key generation - (ciphertext, MAC) = ChaCha20-Poly1305(msg = reply byte, nonce = 0, AD = Sha256(replyKey), key = replyKey) - - If ChaCha20 reply encryption is set in the request record (flags bit 5 set): - - // Use a unique nonce per-record - nonce = one \+ zero-indexed order of record in the VariableTunnelBuildMessage - symCiphertext = ChaCha20(msg = ciphertext \|\| MAC \|\| random padding, nonce, key = replyKey) + msg = reply byte \|\| build options \|\| random padding + (ciphertext, MAC) = ChaCha20-Poly1305(msg, nonce = 0, AD = Sha256(replyKey), key = replyKey) // Other request/reply record encryption // Use a unique nonce per-record nonce = one \+ number of records \+ zero-indexed order of record in the VariableTunnelBuildMessage symCiphertext = ChaCha20(msg = multiple encrypted record, nonce, key = replyKey) - If AES256/CBC reply encryption is set in the request record (flag bit 5 unset): - - // Other request/reply record encryption - msg = multiple encrypted record - key = replyKey - IV = Sha256(replyKey \|\| hop static public key) - symCiphertext = AES256-CBC-Encrypt(msg, key, IV) - {% endhighlight %} While mixed tunnels are used, reply records are the same size, though the format is different. @@ -439,7 +381,9 @@ After full transition to ECIES, random padding can be a range of included paddin When ranged padding is used, random padding will be formatted using the Padding block structure from [ECIES-X25519]_ and [NTCP2]_. For symmetric encryption by other hops, it's necessary to know full record length (w/ padding) without asymmetric decryption. + When/if records become variable-length, it may become necessary to include an unencrypted Data block header before each record, TBD. + BuildReplyRecord may or may not need to match BuildRequestRecord length if both are preceded by Data block header, TBD. Reply Record Encryption for ElGamal Hops @@ -516,7 +460,7 @@ Inbound tunnels: - Use 8-byte ``tunnelNonce`` given the lifetime of tunnels - Destroy tunnel before 2^(64/2 - 1) messages: 2^31 = 2,147,483,648 - - Nonce limit in place to avoid Sweet32 attack on Blowfish + - Nonce limit in place to avoid [Sweet32]_ attack on [Blowfish]_ - Nonce limit unlikely to ever be reached, given this would be ~3,579,139 msgs/second for 10 minute tunnels - Nonce cannot be truncated. For shorter nonce, a different method must be used with smaller state space. @@ -602,7 +546,7 @@ To validate received ``tunnelNonce``, the participant checks against its Bloom f After validation, the participant: -- Blowfish encrypts the ``tunnelNonce`` with its ``IVKey`` +- [Blowfish]_ encrypts the ``tunnelNonce`` with its ``IVKey`` - Uses the encrypted ``tunnelNonce`` & its ``layerKey`` to ChaCha20 encrypt the tunnel message(s) - Sends the tuple {``tunnelId``, encrypted ``tunnelNonce``, ciphertext} to the next hop. @@ -634,7 +578,7 @@ For ECIES-only tunnels, the following scheme will be used: - Validate the received ``tunnelNonce`` against the Bloom filter - ChaCha20 decrypt the encrypted data using the received ``tunnelNonce`` & the hop's ``layerKey`` -- Blowfish decrypt the ``tunnelNonce`` using the hop's ``IVKey`` to get the preceding ``tunnelNonce`` +- [Blowfish]_ decrypt the ``tunnelNonce`` using the hop's ``IVKey`` to get the preceding ``tunnelNonce`` - ChaCha20 decrypt the encrypted data using the decrypted ``tunnelNonce`` & the preceding hop's ``layerKey`` - Repeat for each hop in the tunnel, back to the IBGW @@ -663,25 +607,25 @@ Security Analysis for Blowfish+ChaCha20 Tunnel Layer Encryption Switching from AES256/ECB to ChaCha20 has a number of advantages, and new security considerations. The biggest security considerations to account for, are that ChaCha20 nonces must be unique per-message, -for the life of the key being used, and Blowfish is susceptible to Sweet32 birthday attacks. +for the life of the key being used, and [Blowfish]_ is susceptible to [Sweet32]_ birthday attacks. Failing to use unique nonces with the same key on different messages breaks ChaCha20. -Nonce uniqueness is main reason for using an Blowfish, see [RFC-7539-S4]_. +Nonce uniqueness is main reason for using an [Blowfish]_, see [RFC-7539-S4]_. Simple counters cannot be used, since they require syncing for proper decryption. Syncing the counter can't be guaranteed at the IBEP, without further changes to tunnel protocols. -Blowfish is only used for nonce encryption to guarantee unique nonces, and prevent non-consecutive +[Blowfish]_ is only used for nonce encryption to guarantee unique nonces, and prevent non-consecutive hops in the same tunnel from colluding to know they are in the same tunnel. -The tunnel lifetime of ten minutes and nonce limit of 2^31 messages guarantees that Sweet32 attacks +The tunnel lifetime of ten minutes and nonce limit of 2^31 messages guarantees that [Sweet32]_ attacks are ineffective against Blowfish. Exceeding the limit would require over ~3,579,139 messages/second in each tunnel. Even if 2^31 messages proves to not be a strict enough limit, we can safely reduce the limit by another power of two, without ever realistically reaching the limit. -Even if a Sweet32 attack were successful, an attacker would only gain access to the ``tunnelNonce`` +Even if a [Sweet32]_ attack were successful, an attacker would only gain access to the ``tunnelNonce`` for the colliding message, which doesn't break the ChaCha20 encryption. Non-consecutive hops would only be able to confirm they are participants in the same tunnel. @@ -698,6 +642,34 @@ double-encryption. The chosen-plaintext producing a recovered IV cannot be used to perform a padding-oracle attack against AES256/CBC layer encryption, since duplicate IVs are rejected. +Justification for Blowfish +-------------------------- + +[Blowfish]_ is needed to symmetrically encrypt ChaCha20 nonces used in tunnel layer encryption. It was chosen for +its 64-bit block size, and ability to symmetrically encrypt without using a nonce. + +A 64-bit block size is needed to generate unique nonces for ChaCha20. ChaCha20 has a maximum nonce size of 96-bits, +using the IETF variant. Nonces of smaller sizes can be used, and are padded with zeroes. However, larger nonces, like +the 128-bit AES256/CBC IV cannot safely be truncated, as 96-bit segments may be identical for two otherwise +unique IVs. + +HKDF and other hashing functions cannot be used to safely truncate the received IV, since it must be possible +for Inbound Endpoints to recover the IV of preceding hops in the tunnel. + +Of the 64-bit ciphers, [Blowfish]_ is the most secure, with the widest support in well-audited cryptography libraries. + +Other alternatives like DES and 3DES, are more cumbersome, and weaker in comparison. Despite its comparative strength, +[Blowfish]_ is still vulnerable to [Sweet32]_ attacks. This means that in ~2^32 blocks, there will be a block collision, +allowing for recovery of the nonce from that block. Given the lifetime of tunnels, the restriction on unique received +nonces, and the limit of 2^31 messages, [Blowfish]_ would not vulnerable to [Sweet32]_ in the I2P ECIES Tunnels context. + +Realistically, much fewer than 2^29 nonces will ever be seen by any tunnel, since this would be over 894,784 msgs/sec. + +For comparison, Google receives ~76,000 searches per second. Even assuming 10 messages per search, a tunnel would have +to be over 100,000 msgs/sec busier than Google. Vanishingly unlikely. + +Statistics from: https://www.internetlivestats.com/one-second/ + Tunnel Message Overhead for ECIES ================================= @@ -760,6 +732,9 @@ References .. [ECIES-X25519] {{ proposal_url('144') }} +.. [Tunnel-Build-Options] + {{ proposal_url('143') }} + .. [NTCP2] https://geti2p.net/spec/ntcp2 @@ -775,5 +750,8 @@ References .. [RFC-7539-S4] https://tools.ietf.org/html/rfc7539#section-4 -.. [SplitMix64] - http://xoshiro.di.unimi.it/splitmix64.c +.. [Blowfish] + https://www.schneier.com/academic/blowfish/ + +.. [Sweet32] + https://sweet32.info/ -- GitLab