LearnBitcoin

Bitcoin Dev Archive 2016

p2p authentication and encryption BIPs

23 messages, 23 Mar to 25 May 2016. Started by Jonas Schnelli.

Jonas Schnelli (9), Tom Zander (4), Lee Clagett (3), James MacWhyte (2), Tier Nolan, Eric Voskuil, Sergio Demian Lerner, Luke Dashjr, +1 more

Permalink Original thread on gnusha.org

Jonas Schnelli permalink original

Hi

I have just PRed a draft version of two BIPs I recently wrote. https://github.com/bitcoin/bips/pull/362

Two BIPs that addresses the problem of decoupling wallets/clients from nodes while assuming a user (or a group) know the remote peer.

Authentication would be necessary to selective allow bloom filtering of transactions, encryption or any other node service that might lead to fingerprinting or resource attacks. Authentication would also be a pre-requirement for certificate free encryption-handshakes that is (enough?) resistant to MITM attacks.

Encryption is highly recommended if you connect a SPV node to a trusted node.

Authentication would allow accessing private p2p extensions from a remote SPV peer (example: fee estimation).

I'm aware of other methods to increase privacy and integrity (tor, VPN, stunnel, etc.), however I think authentication and a basic communication encryption should be part of the protocol and its setup should be complete hassle-free.

Thanks for your feeback.

/jonas

Tier Nolan in reply to Jonas Schnelli permalink original

There is probably not much loss due to per message encryption. Even if a MITM determined that a message was an inv message (or bloom filter message), it wouldn't be able to extract much information. Since the hashes in those messages are fixed size, there is very little leakage.

You could make it so that the the encryption messages effectively create a second data stream and break/weaken the link between message size and wrapped message size. This requires state though, so there is a complexity tradeoff.

There is no real need to include an IV, since you are including a 32 byte context hash. The first 16 bytes of the context hash could be used as IV.

In terms of generating the context hash, it would be easier to make it linear.

context_hash_n = SHA256(context_hash_(n-1) | message_(n-1))

As the session gets longer, both nodes would have to do more and more hashing to compute the hash of the entire conversation.

Tom Zander in reply to Jonas Schnelli permalink original

On Wednesday 23 Mar 2016 16:24:12 Jonas Schnelli via bitcoin-dev wrote:

Quoted text (4 lines)
> Hi
>
> I have just PRed a draft version of two BIPs I recently wrote.
> https://github.com/bitcoin/bips/pull/362

I suggest running a spellchecker ;)

Some questions;

* why would you not allow encryption on non-pre-approved connections? * we just removed (ssl) encryption from the JSON interface, how do you suggest this encryption to be implemented without openSSL? * What is the reason for using the p2p code to connect a wallet to a node? I suggest using one of the other connection methods to connect to the node. This avoids a change in the bitcoin protocol for a very specific usecase. * Why do you want to do a per-message encryption (wrapping the original)? Smaller messages that contain predictable content and are able to be matched to the unencrypted versions on the wire send to other nodes will open this scheme up to various old statistical attacks.

Quoted text (1 line)
> Responding peers must ignore (banning would lead to fingerprinting) the

requesting peer after 5 unsuccessfully authentication tries to avoid resource attacks.

Any implementation of that kind would itself again be open to resource attacks. Why 5? Do you want to allow a node to make a typo?

Quoted text (1 line)
> To ensure that no message was dropped or blocked, the complete communication

must be hashed (sha256). Both peers keep the SHA256 context of the encryption session. The complete <code>enc</code> message (leaving out the hash itself) must be added to the hash-context by both parties. Before sending a <code>enc</code> command, the sha256 context will be copied and finalized.

You write "the complete communication must be hashed" and every message has a hash of the state until it is at that point. I think you need to explain how that works specifically.

Eric Voskuil in reply to Tom Zander permalink original

On 03/23/2016 01:36 PM, Tom via bitcoin-dev wrote:

Quoted text (2 lines)
> On Wednesday 23 Mar 2016 16:24:12 Jonas Schnelli via bitcoin-dev wrote:
> * why would you not allow encryption on non-pre-approved connections?

Agree

Quoted text (2 lines)
> * we just removed (ssl) encryption from the JSON interface, how do you suggest
> this encryption to be implemented without openSSL?

CurveCP

Quoted text (3 lines)
> * What is the reason for using the p2p code to connect a wallet to a node?
> I suggest using one of the other connection methods to connect to the node.
> This avoids a change in the bitcoin protocol for a very specific usecase.

Agree, P2P and client-server protocols are distinct use-cases. Missing this distinction is the root cause of problems with the bloom filters feature.

Quoted text (4 lines)
> * Why do you want to do a per-message encryption (wrapping the original)?
> Smaller messages that contain predictable content and are able to be matched
> to the unencrypted versions on the wire send to other nodes will open this
> scheme up to various old statistical attacks.

Privacy cannot currently be achieved unless the server is trusted. In most wallet scenarios that's not a reasonable assumption unless one controls the full node. So this is only useful in the case where the wallet is trusting a remote server, and as you point out - message encryption is weak in this case. In a trustless server scenario encryption would be unnecessary overhead.

Quoted text (7 lines)
>> Responding peers must ignore (banning would lead to fingerprinting) the
> requesting peer after 5 unsuccessfully authentication tries to avoid resource
> attacks.
>
> Any implementation of that kind would itself again be open to resource
> attacks.
> Why 5? Do you want to allow a node to make a typo?

Agree, denial of service protection can and should be much more flexible than this. It's not necessary to incorporate DoS protection into a protocol. I think maybe this stems from the ill-advised attempt at messaging reliability.

Quoted text (9 lines)
>> To ensure that no message was dropped or blocked, the complete communication
> must be hashed (sha256). Both peers keep the SHA256 context of the encryption
> session. The complete <code>enc</code> message (leaving out the hash itself)
> must be added to the hash-context by both parties. Before sending a
> <code>enc</code> command, the sha256 context will be copied and finalized.
>
> You write "the complete communication must be hashed" and every message has a
> hash of the state until it is at that point.
> I think you need to explain how that works specifically.

Also, this gets into the area of messaging reliability. This is certainly not something I would recommend for a P2P protocol optimized for maintaining a cache of public data.

e

Jonas Schnelli in reply to Tom Zander permalink original
Quoted text (4 lines)
>> I have just PRed a draft version of two BIPs I recently wrote.
>> https://github.com/bitcoin/bips/pull/362
>
> I suggest running a spellchecker ;)

Thanks. Will do.

Quoted text (1 line)
> * why would you not allow encryption on non-pre-approved connections?

The encryption should be optional. The proposed authentication scheme does take care of the identity-management and therefor prevent MITM attacks. Without the identity management, you might not detect sending/receiving encrypted data from/to a MITM.

Quoted text (2 lines)
> * we just removed (ssl) encryption from the JSON interface, how do you suggest
> this encryption to be implemented without openSSL?

The proposed encryption schema is based on ECDSA/ECDH (implemented in libsecp256k1) and AES256CBC (implementation is on the way see https://github.com/bitcoin/bitcoin/pull/7689). OpenSSL is not required.

Quoted text (3 lines)
> * What is the reason for using the p2p code to connect a wallet to a node?
> I suggest using one of the other connection methods to connect to the node.
> This avoids a change in the bitcoin protocol for a very specific usecase.

Most known use-case: SPV.

Quoted text (4 lines)
> * Why do you want to do a per-message encryption (wrapping the original)?
> Smaller messages that contain predictable content and are able to be matched
> to the unencrypted versions on the wire send to other nodes will open this
> scheme up to various old statistical attacks.

It's probably extremely inefficient to create a constant time stream. Even most SSL/SSH application leak information because of the communication message characteristics.

The current wrapping message proposal is not very efficient. I will change it so that the p2p message header will contain the encryption metadata. This should lead to a tiny overhead.

Quoted text (8 lines)
>
>> Responding peers must ignore (banning would lead to fingerprinting) the
> requesting peer after 5 unsuccessfully authentication tries to avoid resource
> attacks.
>
> Any implementation of that kind would itself again be open to resource
> attacks.
> Why 5? Do you want to allow a node to make a typo?

Good point. Maybe one false try should lead to ignoring the peer.

Quoted text (11 lines)
>
>
>> To ensure that no message was dropped or blocked, the complete communication
> must be hashed (sha256). Both peers keep the SHA256 context of the encryption
> session. The complete <code>enc</code> message (leaving out the hash itself)
> must be added to the hash-context by both parties. Before sending a
> <code>enc</code> command, the sha256 context will be copied and finalized.
>
> You write "the complete communication must be hashed" and every message has a
> hash of the state until it is at that point.
> I think you need to explain how that works specifically.

This is a relative simple concept and does not require rehashing the whole communication. You just append the "new data".

Some pseudocode:

SHA256CTX ctx;

// first com SHA256CTX_Update(ctx, 1stmessage);

// copy context SHA256CTX ctxnew = ctx;

// finalize the copied context sha256hash = SHA256CTX_Finalize(ctxnew); //use as checksum hash

//////// next message SHA256CTX_Update(ctx, 2ndmessage);

// copy context SHA256CTX ctxnew = ctx;

// finalize the copied context sha256hash = SHA256CTX_Finalize(ctxnew); //use as checksum hash

... etc.

</jonas>

Sergio Demian Lerner in reply to Tom Zander permalink original

It seems that every message must be signed (the protocols lacks MACs). This can be very resource consuming in terms of CPU and bandwidth since most p2p messages are small.

On Wed, Mar 23, 2016 at 5:36 PM, Tom via bitcoin-dev < bitcoin-dev@lists•linuxfoundation•org> wrote:

Quoted text (53 lines)
> On Wednesday 23 Mar 2016 16:24:12 Jonas Schnelli via bitcoin-dev wrote:
> > Hi
> >
> > I have just PRed a draft version of two BIPs I recently wrote.
> > https://github.com/bitcoin/bips/pull/362
>
> I suggest running a spellchecker ;)
>
> Some questions;
>
> * why would you not allow encryption on non-pre-approved connections?
> * we just removed (ssl) encryption from the JSON interface, how do you
> suggest
> this encryption to be implemented without openSSL?
> * What is the reason for using the p2p code to connect a wallet to a node?
> I suggest using one of the other connection methods to connect to the node.
> This avoids a change in the bitcoin protocol for a very specific usecase.
> * Why do you want to do a per-message encryption (wrapping the original)?
> Smaller messages that contain predictable content and are able to be
> matched
> to the unencrypted versions on the wire send to other nodes will open this
> scheme up to various old statistical attacks.
>
> > Responding peers must ignore (banning would lead to fingerprinting) the
> requesting peer after 5 unsuccessfully authentication tries to avoid
> resource
> attacks.
>
> Any implementation of that kind would itself again be open to resource
> attacks.
> Why 5? Do you want to allow a node to make a typo?
>
>
> > To ensure that no message was dropped or blocked, the complete
> communication
> must be hashed (sha256). Both peers keep the SHA256 context of the
> encryption
> session. The complete <code>enc</code> message (leaving out the hash
> itself)
> must be added to the hash-context by both parties. Before sending a
> <code>enc</code> command, the sha256 context will be copied and finalized.
>
> You write "the complete communication must be hashed" and every message
> has a
> hash of the state until it is at that point.
> I think you need to explain how that works specifically.
>
>
> _______________________________________________
> bitcoin-dev mailing list
> bitcoin-dev@lists•linuxfoundation•org
> https://lists.linuxfoundation.org/mailman/listinfo/bitcoin-dev
>
Luke Dashjr in reply to Jonas Schnelli permalink original

On Wednesday, March 23, 2016 3:24:12 PM Jonas Schnelli via bitcoin-dev wrote:

Quoted text (2 lines)
> I have just PRed a draft version of two BIPs I recently wrote.
> https://github.com/bitcoin/bips/pull/362

In the future, please submit BIP drafts to the mailing list for comment and initial peer review before opening a pull request (or requesting a BIP number assignment), per BIP 1.

Quoted text (2 lines)
> Each peer that supports p2p authentication, must provide two user editable
> databases (can be a simple record-per-line file).

As long as the format of these databases is not standardised, it seems inappropriate to define *any* of this implementation detail in a BIP.

Quoted text (2 lines)
> A peer can send an authenticate message by wrapping the desired message into
> an <code>auth</code>-message-wrapper to the remote peer.

How does a peer know what messages the other peer requires to be authenticated?

Quoted text (2 lines)
> 33bytes || identity-pubkey || comp.-pubkey || The identity pubkey of the
> requesting peer

Seems a waste to include this with every single [authenticated] message...

Quoted text (1 line)
> 8bytes || auth-msg-id || int64 || up-counting auth-msg-id (0 to INT64MAX)

Is this required to persist across connections/restarts/possibly complete reinstalls?

Can the same auth-msg-id be used for multiple peers, so a message can be signed once and sent to all N peers?

Quoted text (3 lines)
> Responding peers must ignore (banning would lead to fingerprinting) the
> requesting peer after 5 unsuccessfully authentication tries to avoid
> resource attacks.

How does banning in this specific case enable fingerprinting as opposed to any other banning?

Quoted text (2 lines)
> The peers should display the identity-pubkey as a identity-address to the
> users, which is a base58-check encoded ripemd160(sha256) hash.

If this is going to become a general-purpose identity system, I think more is needed than a simple EC keypair. At the very least, it should probably use a HD chain and use a new key for every signature (notice you already have auth- msg-id to use with this!).

Quoted text (2 lines)
> This proposal is backward compatible. Non supporting peers will ignore the
> <code>auth</code> message.

... and not process it at all? How is that backward compatible?

Quoted text (4 lines)
> Encrypting traffic between peers is already possible with VPN, tor, stunnel,
> curveCP or any other encryption mechanism on a deeper OSI level, however,
> most mechanism are not practical for SPV or other DHCP/NAT environment and
> will require significant knowhow in how to setup a secure channel.

I don't see how Tor fails this criteria...

Quoted text (2 lines)
> The responding peer will set a session timeout time-interval. The default
> must be 1'800 seconds.

What default? Is the timeout field optional? Why not simply require it?

Quoted text (2 lines)
> This proposal is backward compatible. Non supporting peers will ignore the
> <code>enc*</code> messages.

How should the supporting peer handle the message being ignored?

Luke

Chris Pacia in reply to Jonas Schnelli permalink original

Thanks for doing some work on this Jonas. It's something I've been interested in for a while. I haven't had an opportunity to read the bips but I will do so soon and comment.

As far as the use cases others mentioned, connecting and SPV wallet to your full node is certainly one. It would make it easy to, say, connect the android bitcoin-wallet to your own node. I've hacked on that wallet to make it connect to my .onion node, but it's very slow border-line unusable. Basic encryption and authentication would make that viable.

Also, while bloom filtering in bitcoinj is broken, it could be fixed by just creating a single filter and filling it with 1000 addresses and persisting it to disk. The main issue is you can't restore from seed that way and would have to revert to what bitcoinj does now and blow your privacy. If you had the ability to make an encrypted connection to a trusted node just for restoring from seed, you could save your privacy during a restore.

On 03/23/2016 11:24 AM, Jonas Schnelli via bitcoin-dev wrote:

Quoted text (6 lines)
>
>
> _______________________________________________
> bitcoin-dev mailing list
> bitcoin-dev@lists•linuxfoundation•org
> https://lists.linuxfoundation.org/mailman/listinfo/bitcoin-dev
Lee Clagett in reply to Jonas Schnelli permalink original

On Wed, 23 Mar 2016 16:24:12 +0100 Jonas Schnelli via bitcoin-dev <bitcoin-dev@lists•linuxfoundation•org> wrote:

Quoted text (29 lines)
> Hi
>
> I have just PRed a draft version of two BIPs I recently wrote.
> https://github.com/bitcoin/bips/pull/362
>
> Two BIPs that addresses the problem of decoupling wallets/clients from
> nodes while assuming a user (or a group) know the remote peer.
>
> Authentication would be necessary to selective allow bloom filtering
> of transactions, encryption or any other node service that might lead
> to fingerprinting or resource attacks. Authentication would also be a
> pre-requirement for certificate free encryption-handshakes that is
> (enough?) resistant to MITM attacks.
>
> Encryption is highly recommended if you connect a SPV node to a
> trusted node.
>
> Authentication would allow accessing private p2p extensions from a
> remote SPV peer (example: fee estimation).
>
> I'm aware of other methods to increase privacy and integrity (tor,
> VPN, stunnel, etc.), however I think authentication and a basic
> communication encryption should be part of the protocol and its setup
> should be complete hassle-free.
>
> Thanks for your feeback.
>
> /jonas
>
- The motivation sections seem weak. Why not use SSH? It would have
  similar setup requirements, and is already a deployed solution. If
  there are additional setup simplicities (compared to SSH),
  consider listing them. And if one of the motivating factors is
  complexity reduction from the various "do everything you could
  possible want" protocols/implementations, then add this to the
  motivation.
- ECDSA and "ec pubkey" are mentioned, but not the specific curve.
- The hash algorithm for ECDSA is not explicitly mentioned.
- There is no way to change the cryptographic primitives being used or
  to update to a new protocol version. Would it be done with a new
  message type `auth2` ... ?
- The following seems to be contradictory:
Quoted text (9 lines)
> If the responding peer could not lookup the requesting peer's
> identity-pubkey in the local authorized-peers database or if the
> responding peer could not verify the signature, the requested auth
> message must be ignored to avoid fingerprinting of peers with
> authentication support.
>
> Responding peers must ignore (banning would lead to fingerprinting)
> the requesting peer after 5 unsuccessfully authentication tries to
> avoid resource attacks.
  If I connect to a peer, send 5 auth messages followed by another type
  of message that gets no response, this could indicate auth support.
  Or is this supposed to say ignore further auth messages, but not
  other types of messages? The wording seems to suggest an ignore-all.
- The pubkey from the requester is sent in cleartext, which can be used
  as an identifier across connections (similar to a MAC, except it can
  be seen across every network hop and correlated across connection
  types). Hiding this will likely require encryption, and the protocol
  will start to look similar to CurveCP. If the additional complexity
  is not worth fixing this issue, a section in the encryption BIP
  should be added to explain the identifier leakage.
- The known-peers has an IP and port section. Should the requester limit
  signatures based on this information? This algorithm or process needs
  to be better defined than the vague paragraph about verifying the
  integrity of the remote peer; if an implementation uses the
  any-one-of server approach the known-peers file becomes more like a
  SSL CA list, which does not seem like the intent. However, the example
  at the bottom says "Requesting peer does a lookup of (F) in
  known-peers database (B)".
- The encryption portion does not mention the pubkey pairs in use for
  ECDH (this needs to be described), so I am assuming the pairs from
  authentication are re-used. This increases the chances of data
  exposure since a single botched k selection (re-use) for ECDSA would
  allow for forged authentications, and the decryption of all
  historical data. Adding a temporary key exchange would add slight
  complexity and one RTT from the requesters perspective, but it
  provides forward-secrecy and protection against ECDSA implementation
  failures.
- Can the responding peer set a different cipher in the `ecinit`
  response, or should/must it be the same?
- What happens if the responding peer does not support the cipher?
  Presumably, a rejection?
- The contents of the IV field are unspecified, and should be
  specified to contain new output from a CSPRNG for each message.
- Should `enc` messages be wrapped in `auth` messages (presumably so
  since there is no MAC)? `encinit` have this restriction, but nothing
  is specified for `enc`.
- Is the context hash unique in each direction? There seems to be one,
  which would be racy - what if the client wanted to pipeline messages?
  Or is the intent a single open request/response style? I think this
  _adds_ a restriction to the Bitcoin protocol.
- Instead of a hash, what about a counter in each direction for the
  `enc` stream? The auth portion verifies integrity, authenticity, and
  completeness of each message (including this counter). Missing
  messages (through TCP injection?) would still detected. Using TCP
  injection to forcefully teardown a connection is possible in both
  designs.

Lee

Jonas Schnelli in reply to Jonas Schnelli permalink original
Quoted text (2 lines)
> I have just PRed a draft version of two BIPs I recently wrote.
> https://github.com/bitcoin/bips/pull/362

Thanks for the feedback and IRC discussions.

I have overhauled both BIPs. https://github.com/bitcoin/bips/pull/362/files#diff

Main changes for the encryption BIP: * No message wrapping. Once encryption is established, everything is encrypted. No timeout. * Added MAC: proposed AEAD is now ChaCha20-Poly1305 with an alternative for AES256-GCH * Independent ECDH negotiation and independent secrets for the symmetric cipher for both communication directions * Optimized message format and message-batch-option for encrypted data

It could be that the p2p performance for Chacha20-poly1305 encrypted message is slightly better then the current plaintext message format (dropping the network magic and the sha256 per message).

P2p authentication BIP: * No message wrapping. Peers keep the state once authenticated. * Simplified and auth now requires encrypted channels.

Some answers...

Quoted text (1 line)
> How does a peer know what messages the other peer requires to be

authenticated?

This is not covered by the auth BIP. Peers could agree on a protocol extension outside of any BIP. Once auth is possible, new BIPs could be written. Things like only allowing filtering (or other services) to authenticated peers (and disabling NODE_BLOOM).

Quoted text (1 line)
> How does banning in this specific case enable fingerprinting as

opposed to any other banning?

Current nodes ignore a unknown message with a command like "auth". Banning would allow a requesting peer to identify nodes that support auth and attack them over different channels ("ah, ... this guy supports auth, they must have some secret data, lets attack over SSH).

Quoted text (1 line)
>> This proposal is backward compatible. Non supporting peers will

ignore the <code>auth</code> message.

Quoted text (1 line)
> ... and not process it at all? How is that backward compatible?

Depends how we define backward compatibility. :-) Peers supporting this "extension" can still interact with older peers.

Quoted text (1 line)
> This proposal is backward compatible. Non supporting peers will ignore

the > <code>enc*</code> messages.

Current p2p implementation ignores any unknown command.

</jonas>

Tom Zander in reply to Jonas Schnelli permalink original

On Wednesday 23 Mar 2016 22:55:34 Jonas Schnelli via bitcoin-dev wrote:

Quoted text (8 lines)
> >> I have just PRed a draft version of two BIPs I recently wrote.
> > * why would you not allow encryption on non-pre-approved connections?
>
> The encryption should be optional.
> The proposed authentication scheme does take care of the
> identity-management and therefor prevent MITM attacks.
> Without the identity management, you might not detect sending/receiving
> encrypted data from/to a MITM.

If you want to extend the Bitcoin protocol itself, you will have to resolve that. Which many other solutions do (ssh for instance).

It would not be Ok to have an peer-to-peer encryption system that doesn't allow non-pre-approved connections.

Quoted text (6 lines)
> > * What is the reason for using the p2p code to connect a wallet to a node?
> > I suggest using one of the other connection methods to connect to the
> > node.
> > This avoids a change in the bitcoin protocol for a very specific usecase.
>
> Most known use-case: SPV.

You didn't answer the question.

Quoted text (6 lines)
> > * Why do you want to do a per-message encryption (wrapping the original)?
> > Smaller messages that contain predictable content and are able to be
> > matched to the unencrypted versions on the wire send to other nodes will
> > open this scheme up to various old statistical attacks.
>
> It's probably extremely inefficient to create a constant time stream.

Your use of "probably" makes me wonder if you already have an implementation. Doing any encryption and handshaking design *without* actually having it coded and gone though testing yet makes no sense. I do not belief Bitcoin will benefit from "design by committee" where a specification is drawn before an implementation is written.

Also, you didn't actually address the attack-vector.

Quoted text (10 lines)
> >> Responding peers must ignore (banning would lead to fingerprinting) the
> >
> > requesting peer after 5 unsuccessfully authentication tries to avoid
> > resource attacks.
> >
> > Any implementation of that kind would itself again be open to resource
> > attacks.
> > Why 5? Do you want to allow a node to make a typo?
>
> Good point. Maybe one false try should lead to ignoring the peer.

That doesn't take away the resource attack at all.

Quoted text (14 lines)
> >> To ensure that no message was dropped or blocked, the complete
> >> communication>
> > must be hashed (sha256). Both peers keep the SHA256 context of the
> > encryption session. The complete <code>enc</code> message (leaving out
> > the hash itself) must be added to the hash-context by both parties.
> > Before sending a <code>enc</code> command, the sha256 context will be
> > copied and finalized.
> >
> > You write "the complete communication must be hashed" and every message
> > has a hash of the state until it is at that point.
> > I think you need to explain how that works specifically.
>
> This is a relative simple concept and does not require rehashing the
> whole communication.

Apologies, I should have been more clear; the BIP should specify the actual algorithm, otherwise you can't create an implementation from just reading the BIP.

Also, this may be a good time to ask why you want to have a per-message encryption? Practically every single popular end-to-end encryption uses one approach or another were it just encrypts as another layer. (the L in ssl). You are mixing layers, and unless you do that for a very good reason, or have a very good reason why everyone else is doing it wrong, I suggest using a layered encryption approach.

Tom Zander in reply to Chris Pacia permalink original

On Thursday 24 Mar 2016 13:20:48 Chris via bitcoin-dev wrote:

Quoted text (5 lines)
> As far as the use cases others mentioned, connecting and SPV wallet to
> your full node is certainly one. It would make it easy to, say, connect
> the android bitcoin-wallet to your own node. I've hacked on that wallet
> to make it connect to my .onion node, but it's very slow border-line
> unusable. Basic encryption and authentication would make that viable.

What about using some interface, much like the JSON one (but more likely the zeroMQ one) instead? Would that not solve the problem?

I'm thinking that would not be a replacement for a full-node-connection but in addition.

Which means that some questions can be asked over that channel that you need authentication for. It would be a much better separation of concerns.

Jonas Schnelli in reply to Tom Zander permalink original

Hi Tom

Quoted text (8 lines)
>> The encryption should be optional.
>> The proposed authentication scheme does take care of the
>> identity-management and therefor prevent MITM attacks.
>> Without the identity management, you might not detect sending/receiving
>> encrypted data from/to a MITM.
>
> If you want to extend the Bitcoin protocol itself, you will have to resolve
> that. Which many other solutions do (ssh for instance).

Please check the newest auth BIP (it solves MITM).

The encryption BIP itself does not cover peer authentication. Encryption without authentication of peers can also be valuable.

Quoted text (8 lines)
>>> * What is the reason for using the p2p code to connect a wallet to a node?
>>> I suggest using one of the other connection methods to connect to the
>>> node.
>>> This avoids a change in the bitcoin protocol for a very specific usecase.
>>
>> Most known use-case: SPV.
>
> You didn't answer the question.

I hope you see the today's problem with SPV. You fully reveal to your ISP / WiFi provider most of your wallet controlled addresses (when using BF). The ISP/WiFi provider can link your bitcoin usage to other inet traffic and/or they could sell information to statistics company like google.

Also, an attacker controlling a WiFi router or any other network peer between your SPV node and the remote full node could censorship transactions.

Etc. etc.

An encrypted channel together with a trusted full node would finally allow to have a secure and save SPV communication.

Quoted text (14 lines)
>>> * Why do you want to do a per-message encryption (wrapping the original)?
>>> Smaller messages that contain predictable content and are able to be
>>> matched to the unencrypted versions on the wire send to other nodes will
>>> open this scheme up to various old statistical attacks.
>>
>> It's probably extremely inefficient to create a constant time stream.
>
> Your use of "probably" makes me wonder if you already have an implementation.
> Doing any encryption and handshaking design *without* actually having it coded
> and gone though testing yet makes no sense.
> I do not belief Bitcoin will benefit from "design by committee" where a
> specification is drawn before an implementation is written.
>
> Also, you didn't actually address the attack-vector.

Which attack-vector? MITM? Is conceptual solved with the auth BIP (that requires encryption).

There is no implementation done yet. It would be a waste of time to start writing a such implementation _before_ having this discusses and improved by the community.

But the encryption BIP now recommends Chacha20-Polay1305 as AEAD which is widely used.

I'm ready to write an implementation as soon as I have some signs that the BIP does make sense.

Also, auth and enc is not something we will have in the next couple of weeks. This might require a couple of months until its stable and ready for production.

Quoted text (34 lines)
>
>
>>>> Responding peers must ignore (banning would lead to fingerprinting) the
>>>
>>> requesting peer after 5 unsuccessfully authentication tries to avoid
>>> resource attacks.
>>>
>>> Any implementation of that kind would itself again be open to resource
>>> attacks.
>>> Why 5? Do you want to allow a node to make a typo?
>>
>> Good point. Maybe one false try should lead to ignoring the peer.
>
> That doesn't take away the resource attack at all.
>
>
>>>> To ensure that no message was dropped or blocked, the complete
>>>> communication>
>>> must be hashed (sha256). Both peers keep the SHA256 context of the
>>> encryption session. The complete <code>enc</code> message (leaving out
>>> the hash itself) must be added to the hash-context by both parties.
>>> Before sending a <code>enc</code> command, the sha256 context will be
>>> copied and finalized.
>>>
>>> You write "the complete communication must be hashed" and every message
>>> has a hash of the state until it is at that point.
>>> I think you need to explain how that works specifically.
>>
>> This is a relative simple concept and does not require rehashing the
>> whole communication.
>
> Apologies, I should have been more clear; the BIP should specify the actual
> algorithm, otherwise you can't create an implementation from just reading the
> BIP.

The sha256 context is gone now and replaced by a proper MAC.

Quoted text (8 lines)
>
> Also, this may be a good time to ask why you want to have a per-message
> encryption?
> Practically every single popular end-to-end encryption uses one approach or
> another were it just encrypts as another layer. (the  L in ssl). You are
> mixing layers, and unless you do that for a very good reason, or have a very
> good reason why everyone else is doing it wrong, I suggest using a layered
> encryption approach.

Like most other encryption layers, we would still use messages. But we call them "encrypted messages", the have a tiny header of plaintext data (message length, AEAD-tag) and they will contain <n> plaintext p2p messages _after_ decrypting. The plaintext messages have a much simpler header (removed the 4 bytes sha256 checksum, removed the 4byte network)

</jonas>

Tom Zander in reply to Jonas Schnelli permalink original

On Friday 25 Mar 2016 19:43:00 Jonas Schnelli via bitcoin-dev wrote:

Quoted text (2 lines)
> An encrypted channel together with a trusted full node would finally
> allow to have a secure and save SPV communication.

I guess my question didn't get across.

Why would you want to make your usecase do connections over the peer2peer (net.cpp) connection at all?

Mixing messages that are being sent to everyone and encrypted messages is asking for trouble. Making your private connection out-of-band would work much better.

Quoted text (3 lines)
> > Also, you didn't actually address the attack-vector.
>
> Which attack-vector?

The statistical attack I mentioned earlier. Which comes from knowing which plain text messages are being sent over the encrypted channel, So as long as you keep saying you want to encrypt data that identical copies of are being sent to other nodes at practically the same time, you will keep being vulnerable to that.

Jonas Schnelli in reply to Tom Zander permalink original
Quoted text (4 lines)
> I guess my question didn't get across.
>
> Why would you want to make your usecase do connections over the peer2peer
> (net.cpp) connection at all?

First, because there _are_ a hight amount of SPV wallets in the field. SPV wallets are "dumb-clients" with only a tiny value for the bitcoin network (they don't validate, they don't relay). They already are decoupled wallets. We need solution that offers higher privacy and higher traffic analysis resistance.

Using the p2p channel for communication between full validation peers and wallet-only-peers makes sense IMO because wallet-only-peers can slowly validate the chain and create a UTXO set in the background (could take a couple of weeks) or solve other purposes that increases the security and/or serving something back to the bitcoin network.

Sure, you can always use client/server wallets (Coinbase / Copay, etc.) that offers SSL. But I strongly recommend to improve the communication and interface possibilities between wallet-nodes (SPV) and full-validation-nodes.

Otherwise we will very likely see centralization regarding end-user wallets (with all the large risks of disrupting the community in case of attacks/thefts, etc.).

_If we think Bitcoin should scale, we also need to scale and improve at the point where users enter the network and start using Bitcoin._

Quoted text (3 lines)
> Mixing messages that are being sent to everyone and encrypted messages is
> asking for trouble.
> Making your private connection out-of-band would work much better.

The current encryption BIP requires to encrypt the complete traffic. Having an option to do analysis resistant communication with a remote peer within the protocol itself is something that is very valuable IMO.

Quoted text (9 lines)
>>> Also, you didn't actually address the attack-vector.
>>
>> Which attack-vector?
>
> The statistical attack I mentioned earlier.  Which comes from knowing which
> plain text messages are being sent over the encrypted channel, So as long as
> you keep saying you want to encrypt data that identical copies of are being
> sent to other nodes at practically the same time, you will keep being
> vulnerable to that.

The encryption BIP recommends Chacha20-Poly1305 as encryption AEAD. This is a very broad used encryption scheme (Google uses it to connect Android phones with their cloud services).

Completely avoiding side channel on data analysis would probably require extremely inefficient constant time encrypted datastreams.

Also, the BIP allows combining of multiple plaintext message in one encrypted message.

Additionally we could extend the enc. BIP by allowing random padding of encrypted messages or other techniques to reduce side channel analysis.

</jonas>

James MacWhyte in reply to Tom Zander permalink original

On Sat, Mar 26, 2016 at 1:34 AM Tom via bitcoin-dev < bitcoin-dev@lists•linuxfoundation•org> wrote:

Quoted text (14 lines)
> On Friday 25 Mar 2016 19:43:00 Jonas Schnelli via bitcoin-dev wrote:
> > An encrypted channel together with a trusted full node would finally
> > allow to have a secure and save SPV communication.
>
> I guess my question didn't get across.
>
> Why would you want to make your usecase do connections over the peer2peer
> (net.cpp) connection at all?
>
> Mixing messages that are being sent to everyone and encrypted messages is
> asking for trouble.
> Making your private connection out-of-band would work much better.
>
>

I agree doing it out-of-band is the easiest solution for people who need this privacy right now, but I do like the idea of adding this feature as the number of SPV wallets is going to increase. I think the best way to organize things would be to give encrypted messages their own port number, similar to how http vs. https works.

We don't want two networks to develop, separated by which nodes support encryption and which don't, so ideally nodes would rebroadcast messages they receive on both (encrypted and non-encrypted) channels. This would essentially double the required bandwidth of the network, which is something to think about.

Quoted text (17 lines)
> > > Also, you didn't actually address the attack-vector.
> >
> > Which attack-vector?
>
> The statistical attack I mentioned earlier.  Which comes from knowing which
> plain text messages are being sent over the encrypted channel, So as long
> as
> you keep saying you want to encrypt data that identical copies of are being
> sent to other nodes at practically the same time, you will keep being
> vulnerable to that.
>
>
> _______________________________________________
> bitcoin-dev mailing list
> bitcoin-dev@lists•linuxfoundation•org
> https://lists.linuxfoundation.org/mailman/listinfo/bitcoin-dev
>
Jonas Schnelli in reply to James MacWhyte permalink original
Quoted text (17 lines)
>     I guess my question didn't get across.
>
>     Why would you want to make your usecase do connections over the
>     peer2peer
>     (net.cpp) connection at all?
>
>     Mixing messages that are being sent to everyone and encrypted
>     messages is
>     asking for trouble.
>     Making your private connection out-of-band would work much better.
>
>
> I agree doing it out-of-band is the easiest solution for people who need
> this privacy right now, but I do like the idea of adding this feature as
> the number of SPV wallets is going to increase. I think the best way to
> organize things would be to give encrypted messages their own port
> number, similar to how http vs. https works.

I'm not sure if different ports would make sense. I can't see a benefit (happy if someone can convince me). How would this affect p2p address management (address relay)? Wouldn't this require to extend the current address message to support two port numbers?

Quoted text (5 lines)
> We don't want two networks to develop, separated by which nodes support
> encryption and which don't, so ideally nodes would rebroadcast messages
> they receive on both (encrypted and non-encrypted) channels. This would
> essentially double the required bandwidth of the network, which is
> something to think about.

It can be the same "p2p network". The only difference would be, that once two peers has negotiated encryption, the whole traffic between _these two peers_, and _only_ these two pears, would be encrypted (would _not_ affect traffic to/from other peers).

A simplified example: 1. Peer Alice connects to peer Bob 2. Alice asks Bob: "lets do encrypted communication, here is my session pubkey" 3. Bob also supports encryption and answers "Yes, let's do this, here is my session pubkey" 4. Alice tells Bob (encrypted now): "Perfect. Here I prove that I'm Alice by signing the session ID with my identity pubkey" 5. Bob checks his "authorized-peers" database and look-up Alices pubkey and verifies the signatures. 6. Bob tells Alice: "Good! I trust you now Alice, here is my identity pubkey with a signature of our session-ID" 7. Alice looks up Bobs pubkey in her "known-peers" database and verifies the signature. 8. Alice response to bob: "Perfect. Indeed, you are Bob!" --- At this point, the communication is encrypted and the identities has been verified (MITM protection).

(simplified negotiation [only one-way, missing dh explanation, missing KDF, session-ID, cipher suite nego., missing re-keying, etc.])

</jonas>

James MacWhyte in reply to Jonas Schnelli permalink original

On Sun, Mar 27, 2016 at 5:49 AM Jonas Schnelli via bitcoin-dev < bitcoin-dev@lists•linuxfoundation•org> wrote:

Quoted text (26 lines)
>
> >     I guess my question didn't get across.
> >
> >     Why would you want to make your usecase do connections over the
> >     peer2peer
> >     (net.cpp) connection at all?
> >
> >     Mixing messages that are being sent to everyone and encrypted
> >     messages is
> >     asking for trouble.
> >     Making your private connection out-of-band would work much better.
> >
> >
> > I agree doing it out-of-band is the easiest solution for people who need
> > this privacy right now, but I do like the idea of adding this feature as
> > the number of SPV wallets is going to increase. I think the best way to
> > organize things would be to give encrypted messages their own port
> > number, similar to how http vs. https works.
>
> I'm not sure if different ports would make sense. I can't see a benefit
> (happy if someone can convince me).
> How would this affect p2p address management (address relay)? Wouldn't
> this require to extend the current address message to support two port
> numbers?
>
> I'm assuming clients that connect with encryption don't want to use

unencrypted connections, and are only interested in other peers that support encryption. From their perspective, it is quite inefficient to get a generic list of peers and then have to connect to each one searching for those that accept encryption. If we use port numbers, we can assume any connection that comes on the encrypted port is only interested in encrypted communication, so a getaddr to an encrypted port would only return a list of other encryption-capable peers.

This isn't an issue if the plan is to require all peers to support encryption, and we assume the majority of the network will upgrade before too long.

Quoted text (13 lines)
>
> > We don't want two networks to develop, separated by which nodes support
> > encryption and which don't, so ideally nodes would rebroadcast messages
> > they receive on both (encrypted and non-encrypted) channels. This would
> > essentially double the required bandwidth of the network, which is
> > something to think about.
>
> It can be the same "p2p network". The only difference would be, that
> once two peers has negotiated encryption, the whole traffic between
> _these two peers_, and _only_ these two pears, would be encrypted (would
> _not_ affect traffic to/from other peers).
>
>

You're right, there would not be an increase in bandwidth. Please forget I said that :) But following the logic I wrote above, it would be possible for peers to become segregated (those who require encryption would only connect to each other). It wouldn't be a problem as long as there are enough peers that provide both encrypted and non-encrypted connections; or, as I said above, if we can assume every peer will support it. Maybe the issues I'm thinking of are just growing pains that will be solved once the majority of people upgrade?

Quoted text (31 lines)
> A simplified example:
> 1. Peer Alice connects to peer Bob
> 2. Alice asks Bob: "lets do encrypted communication, here is my session
> pubkey"
> 3. Bob also supports encryption and answers "Yes, let's do this, here is
> my session pubkey"
> 4. Alice tells Bob (encrypted now): "Perfect. Here I prove that I'm
> Alice by signing the session ID with my identity pubkey"
> 5. Bob checks his "authorized-peers" database and look-up Alices pubkey
> and verifies the signatures.
> 6. Bob tells Alice: "Good! I trust you now Alice, here is my identity
> pubkey with a signature of our session-ID"
> 7. Alice looks up Bobs pubkey in her "known-peers" database and verifies
> the signature.
> 8. Alice response to bob: "Perfect. Indeed, you are Bob!"
> ---
> At this point, the communication is encrypted and the identities has
> been verified (MITM protection).
>
>
> (simplified negotiation [only one-way, missing dh explanation, missing
> KDF, session-ID, cipher suite nego., missing re-keying, etc.])
>
>
> </jonas>
>
> _______________________________________________
> bitcoin-dev mailing list
> bitcoin-dev@lists•linuxfoundation•org
> https://lists.linuxfoundation.org/mailman/listinfo/bitcoin-dev
>
Jonas Schnelli in reply to Jonas Schnelli permalink original
Quoted text (2 lines)
> I 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.

Changes since last update: -> 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>

Lee Clagett in reply to Jonas Schnelli permalink original

On Fri, 1 Apr 2016 23:09:47 +0200 Jonas Schnelli via bitcoin-dev <bitcoin-dev@lists•linuxfoundation•org> wrote:

Quoted text (27 lines)
> > I 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.
>
> Changes since last update:
> -> 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 ...

Quoted text (4 lines)
> Rejecting 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.

Quoted text (4 lines)
> Responding 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.

Quoted text (6 lines)
> To 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.

Quoted text (7 lines)
> Possible 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.

Quoted text (6 lines)
> <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.

Quoted text (6 lines)
> After 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.

Quoted text (11 lines)
> {|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.

Quoted text (11 lines)
> A 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

Jonas Schnelli in reply to Lee Clagett permalink original

Hi Lee

Thank you very much for the valuable input. I'm still processing your feedback....

Quoted text (8 lines)
>
> *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.
>

Yes. This could be something that could be extended once the BIP is stable and/or implemented.

Quoted text (17 lines)
>> <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.
>

Right. The AEAD construct I though of is probably called chacha20-poly1305@openssh•com and specified in https://github.com/openssh/openssh-portable/blob/05855bf2ce7d5cd0a6db18bc0b4214ed5ef7516d/PROTOCOL.chacha20poly1305#L34

I think this construct has already serval implementations and is widely used.

I have updated the BIP to mention the chacha20-poly1305@openssh•com specification.

Quoted text (9 lines)
> 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.

Yes. I think closing would make sense.

Quoted text (11 lines)
>> After 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.

Yes. This is a good point. The implementation should probably also accept messages that contain the 4 byte network magic from unencrypted messages (to avoid possible collisions). If the message is unencrypted, the length check or the unsuccessful authentication check will lead to a disconnect.

Quoted text (29 lines)
>> {|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.
>

Good point. I have mentioned this now in the BIP but I think the BIP should allow message > 16 MiB. I leave the max. message length up to the implementation while keeping the 4 byte length on the protocol level.

Quoted text (18 lines)
>
>> A 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.

I just added the RFC4253 recommendation as a must (re-key after every 1GB of data sent or received).

</jonas>

Lee Clagett in reply to Jonas Schnelli permalink original

On Wed, 18 May 2016 10:00:44 +0200 Jonas Schnelli via bitcoin-dev <bitcoin-dev@lists•linuxfoundation•org> wrote:

Quoted text (4 lines)
> Hi Lee
>
> Thank you very much for the valuable input.
> I'm still processing your feedback....

[...]

Quoted text (24 lines)
> > 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.
> >
>
> Good point.
> I have mentioned this now in the BIP but I think the BIP should allow
> message > 16 MiB.
> I leave the max. message length up to the implementation while keeping
> the 4 byte length on the protocol level.

I expect the implementation defined max size to work (SSH 2.0 does this after all), but I want to make sure my suggestion is understood completely.

There is a length field for the encrypted data, and length field(s) inside of the encrypted data to indicate the length of the plaintext Bitcoin messages. I am suggesting that the outter (encrypted) length field be reduced, which will _not limit_ the length of Bitcoin messages. For example, if a 1 GiB Bitcoin message needed to be sent and the encrypted length field was 3 bytes - the sender is forced to send a minimum of 64 MACs for this message. The tradeoff is allowing the receiver to detect malformed data sooner and have a lower max buffering window **against** slightly higher bandwidth and CPU requirements due to the additional headers+MACs (the CPU requirements should primarily be in "finalizing each Poly1305").

An alternative way to think about the suggestion is tunnelling Bitcoin messages over TLS or SSH. TLS 1.2 has a 2-byte length field and SSH 2.0 a 4-byte length field, but neither prevents larger Bitcoin messages from being tunnelled; the lengths are independent.

[...]

Quoted text (3 lines)
>
> </jonas>
>

Lee

Jonas Schnelli in reply to Lee Clagett permalink original
Quoted text (21 lines)
>> Good point.
>> I have mentioned this now in the BIP but I think the BIP should allow
>> message > 16 MiB.
>> I leave the max. message length up to the implementation while keeping
>> the 4 byte length on the protocol level.
>
> I expect the implementation defined max size to work (SSH 2.0 does this
> after all), but I want to make sure my suggestion is understood
> completely.
>
> There is a length field for the encrypted data, and length field(s)
> inside of the encrypted data to indicate the length of the plaintext
> Bitcoin messages. I am suggesting that the outter (encrypted) length
> field be reduced, which will _not limit_ the length of Bitcoin
> messages. For example, if a 1 GiB Bitcoin message needed to be sent
> and the encrypted length field was 3 bytes - the sender is forced to
> send a minimum of 64 MACs for this message. The tradeoff is allowing
> the receiver to detect malformed data sooner and have a lower max
> buffering window **against** slightly higher bandwidth and CPU
> requirements due to the additional headers+MACs (the CPU requirements
> should primarily be in "finalizing each Poly1305").

Okay. Got your point. The current BIPs assumption is that an encrypted package/message can contain 1..n bitcoin messages (a single bitcoin message distributed over multiple encrypted messages/packages was not specified).

But right, this could make sense. Let me think this through....

Quoted text (4 lines)
> An alternative way to think about the suggestion is tunnelling Bitcoin
> messages over TLS or SSH. TLS 1.2 has a 2-byte length field and SSH 2.0
> a 4-byte length field, but neither prevents larger Bitcoin messages from
> being tunnelled; the lengths are independent.

TLS/SSH tunneling is already possible with third party software like stunnel. Also there is promising projects that would encrypt the traffic "on a deeper layer" (see CurveCP).

I think what we want is a simple, openssl-independent traffic encryption built into the core p2p layer.

IMO the risk of screwing up the implementation is moderate.

The implementation is not utterly-complex: OpenSSH chacha20: https://github.com/openssh/openssh-portable/blob/0235a5fa67fcac51adb564cba69011a535f86f6b/chacha.c

Chacha20-Poly1305: https://github.com/openssh/openssh-portable/blob/0235a5fa67fcac51adb564cba69011a535f86f6b/cipher-chachapoly.c

Sure. Before an implementation will be deployed to the endusers it will require intense cryptoanalysis first.

</jonas>