This document proposes two paths to Bitcoin seed node authentication to facilitate conversation about the pros and cons of each.
Reading:
BIP324 enables end-to-end authenticated encryption between bitcoin nodes. Optionally, if the node operators are able to compare the session ids over a secure channel, BIP324 can also make man-in-the-middle attacks observable and provide authentication.
BIP150 enables node operators (with access to secure channels) to exchange public identity-keys and introduces new p2p messages using which a node can challenge a peer to prove its identity and provide directional authentication.
As of July 2021, Bitcoin Core nodes that do not have a populated addrman use dns seeds and fixed seeds to bootstrap a database of peer addrs. DNS seeding is susceptible to DNS hijacks and both techniques are susceptible to BGP hijacks.
Encrypting connections to seeds will help increase the cost of censoring new nodes. Authenticating connections to seeds will help increase the cost of co-opting new nodes into alternate, dishonest networks.
Both approaches outlined in this document assume BIP324 e2e encryption is available for use on the Bitcoin network.
In this approach we implement BIP150 authentication, and the authorized-peers
file comes preloaded with the identity-public-keys(s) and domain names for all the seed nodes. Once the BIP324 encrypted session is available, the new node uses the session-id and the identity-public-key for the seed node to initiate a BIP150 AUTHCHALLENGE
<-> AUTHREPLY
exchange. If the seed node responds with a valid signature for the challenge hash, the node considers the seed authenticated(note, we only need unidirectional authentication for this use case) and proceeds to send the getaddr
message to the seed to populate addrman
.
Pros:
- Authentication for seeds would work the same as authentication for nodes
Cons:
- Dependency on BIP150 implementation
- Introduction on new p2p message types is a slower and more deliberate community process (as opposed to approach #2)
In this approach, the seed node implements BIP324 with a small change. The keypair it uses in ECDH is not ephemeral. Each dns seed in chainparams.cpp
, also has a secp256k1
public key attached to it and the seed node is considered the initiating node as described in the BIP324 handshake. The node that is trying to seed addrman generates an ephemeral keypair, and uses the seed's public key to non-interactively compute the ECDH secret. It then performs the second half of the ECDH handshake with the seed providing its public key which the seed node can use to generate the shared ECDH secret.
Once the handshake is complete, bi-directional encryption and unidirectional authentication is available. The node seeking seeding services can now proceed to send a getaddr
message to the seed node.
This approach provides the same discrete-log security and authentication as approach 1.
Pros:
- No BIP150 dependency
- No new p2p message types
Cons:
- If/when BIP150 is implemented, that is probably a cleaner and more uniform approach to authentication.
- Doing half of the ECDH handshake over a secure, offline channel is not a commonly deployed approach. While it is cryptographically secure, we need to be more careful in choosing this option.
In both approaches, we need to allow for the seed node operators to invalidate old keys in case the private key used for identity is compromised. In general, this does not seem to be a problem though:
- The authentication is both approaches is opportunistic as BIP324 itself is opportunistic. So unauthenticated seeding can always be made available as a fallback.
- Seeding is done by 9 different domains and it is unlikely all keys are compromised at the same time.
Key rotation by a node operator is easier in approach #1 as it's an update to the human-readable authorized-peers
. In approach #2, the node operator would need to make a change to chainparams.cpp
and re-compile. This can be addressed by having the keys in user accessible files for approach #2 as well.
Approach 1
This seems reasonable. I am not sure what the motivation was for the particulars of BIP150 were. IMO signing the session id derived from the DH key would be enough. It would be interesting to hear justifications for:
In considering the above perhaps the protocol could be further simplified to just one new message since both parties already have the session ids. Just adding a single "auth" message should be enough. After establishing an encrypted session which you expect to be authenticated just wait for the auth message and if it doesn't come first drop the connection. This seems to achieve your goal for authenticating seeds. I may be missing wider design considerations since this is the first time I've seen/heard of BIP150.
To enable non-seed auth at a later date you could then introduce more messages to tell the peer to authenticate themselves. Whether this makes sense depends on the goals of the authentication and whether you want TOFU to be possible but I guess this can be figured out at a later date.
Approach 2
The following comes off the top of my head. When you use a static keypair for DH in this way you provide a Decisional Diffie-Hellman (DDH) oracle. Consider that I have a DDH challenge (X,Y,Z) where my task is to determine whether Z is the DH secret of X and Y. Let's say that X is the seed node's public key. I can solve the challenge by using Y as my ephemeral key and seeing if Z works as a shared secret for the session.
Now is giving a DDH oracle such a big deal? I don't think so. In the random oracle model (ROM) this kind of hashed DH key exchange is secure under the Computational Diffie-Hellman assumption so DDH doesn't matter. Theoretically without ROM I think it does need DDH to guarantee semantic security of the resulting encrypted session. This also means we couldn't securely use the node's key to do any other protocol that does need DDH (not that I can think of any reason why we would).
So the downside here is that it makes security convoluted to argue and would require more scrutiny/research. I think that it's not worth going this route it if you can just solve the problem in the protocol layer (Approach 1). Consider that the noise protocol has set the standard for the design of doing DH with static keys for authentications for all types of scenarios so if you wanted to go this way then I would copy what they do: http://www.noiseprotocol.org/noise.html. IMO it's better to keep BIP324 simple and then do approach 1 here.