On Fri, 1 Apr 2016 23:09:47 +0200
Post by Jonas Schnelli via bitcoin-devPost by Jonas Schnelli via bitcoin-devI have just PRed a draft version of two BIPs I recently wrote.
https://github.com/bitcoin/bips/pull/362
Hi.
I just updated the PR above with another overhaul of the BIP.
It's still under heavy review/work, nevertheless – at this point – any
feedback is highly welcome.
-> Removed AES256-GCM as cipher suite
-> Focusing on Chacha20-Poly1305 (implementation size ~300L)
-> Two symmetric cipher keys must be calculated by HMAC_SHA512 from
the ecdh secret
-> A session-ID (both directions) must be calculated (HMAC_SHA256)
for linking an identity authentication (ecdsa sig of the session-ID)
with the encryption
-> Re-Keying ('=hash(old_key)') can be announced by the responding
peer (after x minutes and/or after x GB, local peer policy but not
shorter then 10mins).
-> AEAD tag is now the last element in the new message format
It is very likely that the encrypted message format performs slightly
better than the current message format (removing the SHA256 checksum).
---
</jonas>
The quotes below are from the BIPs and not the email chain ...
Post by Jonas Schnelli via bitcoin-devRejecting the <code>auth</code> request will not reveal the
responding peers identity that could lead to fingerprinting the node,
however this BIP does not cover protection against fingerprinting the
requesting node from the perspective of the responding node.
In many use cases the requesting node will want to make a connection to
a peer with a specific identity. After encryption initialization, the
requesting node could generate an ECDH secret from the long-term public
key of the expected peer and its own session private-key to encrypt (no
MAC) the signature with the same symmetric cipher agreed upon
previously. The requesting node will not reveal its identity if the
connection has been MitM'ed, while still being the first to provide
authentication. And since this would be "inside" the session-key
crypto, it still has forward-secrecy if the responding-peers longterm
private-key is later compromised.
*Key Revocation*
This is probably too complicated, but an additional public key would
allow for cold-storage key revocation. Spreading the knowledge of such
an event is always painful, but it could be stored in the blockchain. I
think this is likely too complicated, but having these long-term keys
constantly in memory/disk is unfortunate.
Post by Jonas Schnelli via bitcoin-devResponding peers must ignore the requesting peer after a
unsuccessfully authentication initialization to avoid resource
attacks (banning would lead to fingerprinting of peers that support
authentication).
Once the responding peer has read the `auth` message, a TCP ACK can be
sent. From the requesting peer perspective, a TCP ACK of the `auth`
request indicates that it was read by the process or some
intermediary buffer (TOE, proxy, etc) has successfully forwarded it to
the next step. If the requesting peer waits RTT * some constant from
the ACK and gets no response, then either: a failed `auth` occurred,
`auth` is not supported, or the machine was suddenly overloaded. The
requesting peer can then send another message; a response message
indicates the responding peer does not support `auth`, and another no
response wait period indicates an overloaded peer or an `auth` enabled
peer. Initiating a new connection (no banning has occurred) indicates
either `auth` is enabled or a load-balancer re-directed the new
connection to another machine under less load. I think the latter case
is going to be rare, so you should be able to identify with high
probability nodes that support `auth` and what message types require
`auth`. And if this is process repeated multiple times, it will increase
the chances of a correct fingerprint.
Should encryption enabled peers who do _not_ support `auth` ignore all
subsequent messages after an `auth` attempt too? Fingerprinting on
`auth` required message types would still be possible. I do not see a
reliable way to prevent this from occurring.
Post by Jonas Schnelli via bitcoin-devTo request encrypted communication, the requesting peer generates an
EC ephemeral-session-keypair and sends an <code>encinit</code>
message to the responding peer and waits for a <code>encack</code>
message. The responding node must do the same
<code>encinit</code>/<code>encack</code> interaction for the opposite
communication direction.
Why are there two key exchanges? A single shared-secret could be used
to generate keys for each direction. And it would reinforce the single
symmetric cipher rule.
Post by Jonas Schnelli via bitcoin-devPossible symmetric key ciphers types
{|class="wikitable"
! Number !! symmetric key ciphers type !! Comments
|-
| 0 || Chacha20-Poly1305 [3] || encrypted message length must be used
as AAD. |}
Chacha20-Poly1305 defined in an IETF draft [0] and RFC 7539 both
include the ciphertext length in the authentication tag generation. Is
this a unique authentication construction? Or one of the previously
mentioned designs?
*Symmetric Cipher Negotiation*
Should the symmetric cipher choices be removed? I am mainly asking for
the intended use-case. If the intent is to replace a weakened cipher
then leave the cipher negotiation. If the intent is to give
implementations multiple options, then I would remove this negotiation.
Post by Jonas Schnelli via bitcoin-dev<code>K_1</code> must be used to only encrypt the payload size of the
encrypted message to avoid leaking information by revealing the
message size.
<code>K_2</code> must be used in conjunction with poly1305 to build
an AEAD.
Chacha20 is a stream cipher, so only a single encryption key is needed.
The first 32 bytes of the keystream would be used for the Poly1305 key,
the next 4 bytes would be used to encrypt the length field, and the
remaining keystream would be used to encrypt the payload. Poly1305
would then generate a tag over the length and payload. The receiver
would generate the same keystream to decrypt the length which
identifies the length of the message and the MAC offset, then
authenticate the length and payload, then decypt with the remaining
keystream.
Is it safer to define two keys to prevent implementations from screwing
this up? You have to split the decryption and authentication, so the
basic modes of libsodium cannot be used for instance. If a custom tag
generation scheme is being used, then the basic modes are already
unusable ...
*Failed Authentication*
What happens on a failed MAC attempt? Connection closure is the
easiest way to handle the situation.
Post by Jonas Schnelli via bitcoin-devAfter a successful <code>encinit</code>/<code>encack</code>
interaction from both sides, the messages format must use the
"encrypted messages structure". Non-encrypted messages from the
requesting peer must lead to a connection termination (can be
detected by the 4 byte network magic in the unencrypted message
structure).
The magic bytes are at the same offset and size as the encrypted length
field in the encrypted messages structure. So the magic bytes are not a
reliable way to identify unencrypted messages, although the probability
of collision is low.
Post by Jonas Schnelli via bitcoin-dev{|class="wikitable"
! Field Size !! Description !! Data type !! Comments
|-
| 4 || length || uint32_t || Length of ciphertext payload in number
of bytes
|-
| ? || ciphertext payload || ? || One or many ciphertext command &
message data
|-
| 8 || MAC tag || ? || MAC-tag truncated to 8 bytes
|}
Why have a fixed MAC length? I think the MAC length should be inferred
from the cipher + authentication mode. And the Poly1305 tag is 16 bytes.
*Unauthenticated Buffering*
Implementations are unlikely to (i.e. should not) process the payload
until authentication succeeds. Since the length field is 4 bytes, this
means an implementation may have to buffer up to 4 GiB of data _per
connection_ before it can authenticate the length field. If the outter
length field were reduced to 2 or 3 bytes, the unauthenticated
buffering requirements drop to 64 KiB and 16 MiB respectively. Inner
messages already have their own length, so they can span multiple
encrypted blocks without other changes. This will increase the
bandwidth requirements when the size of a single message exceeds 64 KiB
or 16 MiB, since it will require multiple authentication tags for that
message. I think an additional 16 bytes per 16 MiB seems like a good
tradeoff.
Post by Jonas Schnelli via bitcoin-devA responding peer can inform the requesting peer over a re-keying
with a <code>encack</code> message containing 33byte of zeros to
indicate that all encrypted message following after this
<code>encack</code> message will be encrypted with ''the next
symmetric cipher key''.
The new symmetric cipher key will be calculated by
<code>SHA256(SHA256(old_symetric_cipher_key))</code>.
Re-Keying interval is a peer policy with a minimum timespan of 600
seconds.
Should the int64_t message count be reset to 0 on a re-key? Or should
the value reset to zero after 2^63-1? Hopefully the peer re-keys before
that rollover, or keystream reusage will occur. Unlikely that many
messages are sent on a single connection though. And presumably this
only re-keys the senders side? Bi-directional re-keying would be racy.
Lee
[0]https://tools.ietf.org/html/draft-ietf-tls-chacha20-poly1305-04