Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replacing the static references of SECP256K1 by an interface #1933

Merged

Conversation

daniel-iobuilders
Copy link
Contributor

Signed-off-by: Daniel Lehrner daniel@io.builders

PR description

At the moment Besu only supports the Ethereum standard SECP256K1 key algorithm. Due to NIST compliance, we would like to add support for SECP256R1 keys.
In this first PR all the static reference calls to SECP256K1 are replaced by an interface. This is a first step to allow different elliptic curves to be added by future PRs.

Fixed Issue(s)

Changelog

Signed-off-by: Daniel Lehrner <daniel@io.builders>
@atoulme
Copy link
Contributor

atoulme commented Feb 22, 2021

This is an intriguing proposal, do you want to change the key wholesale across the whole system? Would you want to allow some compatibility for example at the network layer?

@lucassaldanha
Copy link
Member

This is an intriguing proposal, do you want to change the key wholesale across the whole system?

The plan is to support different key algorithms across the board. We aren't aiming for interop between nodes using different key algorithms.

Would you want to allow some compatibility for example at the network layer?

I don't think I follow. Would you mind elaborating on this?

Copy link
Contributor

@shemnon shemnon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Partial review, I focused on the main classes and skimmed through the unavoidably long list of import renames.

import java.util.Optional;
import java.util.function.UnaryOperator;

public interface EllipticCurveSignature {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel we should go a bit further than simply pulling out the interface methods. I think we should reconsider all the APIs being pulled through and look to see if better homes exist (calculateECDHKeyAgreement, getHalfCureOrder, enableNarive, etc).

A higher level goal may be to provide SPI classes to make it work with javax.crypto and friends, rather than a single use interface.

I also think we can do better than the EllipticCurveSignature name. Account sigs may not always be ECDSA (DSA and RSA are also listed in FIPS). So perhaps SignatureAlgorithm? Or make it role based AccountSignatures. Also the noun implies it is the signature itself, not the class that does the signing, verification, and key management.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel we should go a bit further than simply pulling out the interface methods. I think we should reconsider all the APIs being pulled through and look to see if better homes exist (calculateECDHKeyAgreement, getHalfCureOrder, enableNarive, etc).

I believe the idea is to make incremental changes. And because this class is used pretty much everywhere, pulling out the interface already introduces a bunch of changes to review. Do you feel like it is better to have all changes done at once?

I also think we can do better than the EllipticCurveSignature name. Account sigs may not always be ECDSA (DSA and RSA are also listed in FIPS)

I think supporting "non-EC" have bigger implications that are out of the scope of this PR. I guess this is a small step towards something like https://github.com/EntEthAlliance/client-spec/issues/110. (a really small step)

import static com.google.common.base.Preconditions.checkNotNull;

public class KeyPair {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we just use javax.security.KeyPair and move the static method elsewhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking through the code that would require a lot of casting from javax.security.PublicKey -> SECPPublicKey, because SECPPublicKey.getEncodedBytes is used in a lot of places and not part of the interface. Considering this I would leave the class for right now, as it is.

package org.hyperledger.besu.crypto;

public class EllipticCurveSignatureFactory {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm presuming this is a placeholder for future work to add secp256r1. How will the choice of curve be determined?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct. We are considering this being a config in the genesis file. Mostly because it makes it easier for other people deploying nodes in the network (they don't have to remember the right config, just use the same genesis). Thoughts?


public class PublicKey implements java.security.PublicKey {

public static final int BYTE_LENGTH = 64;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't presume a key length. What if secp256r1 is un-recommened and we have to go to secp384r1, or secp521r1? or any of the sect series?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest to do that in a seperate PR. I've looked a bit into it and there are some calculation, e.g. to recover the public key, which are affected by that too. I think a seperate (and much smaller) PR is much easier to review such a bit more difficult change.
I will do that after this one is merged.


public class Signature {

public static final int BYTES_REQUIRED = 65;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, we shouldn't lock in the signature length. It is reasonable to prepare for chains that may require stronger EC cures.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the impact of supporting stronger keys might add extra work that won't provide much value atm. One of the assumptions for this is that secp256k1 and secp256r1 use keys of the same size. If we make the size dynamic, we might need to handle this across different layers of the system.

I'm not saying that we shouldn't do it, but maybe not in this first iteration.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can expose the key length as a signature API and the initial batch of factory methods can presume it. But I'm wary of baking in 65 bytes too deeply into the API.

…. Fixing all tests

Signed-off-by: Daniel Lehrner <daniel@io.builders>
Renamed PublicKey -> SECPPublicKey
Renamed Signature -> SECPSignature
Renamed EllipticCurveSignature -> SignatureAlgorithm
Refactored call to get SignatureAlgorithm during class loading, to use com.google.common.base.Suppliers#memoize

Signed-off-by: Daniel Lehrner <daniel@io.builders>
Signed-off-by: Daniel Lehrner <daniel@io.builders>
@daniel-iobuilders daniel-iobuilders marked this pull request as ready for review February 25, 2021 10:01
Signed-off-by: Daniel Lehrner <daniel@io.builders>
Copy link
Member

@lucassaldanha lucassaldanha left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can merge this as a first step. This PR is only getting rid of the static references. However, there were some good suggestions that I'd like to see done as well. Let's make sure we work on those next.

@lucassaldanha lucassaldanha merged commit 8315ba6 into hyperledger:master Mar 1, 2021
RichardH92 pushed a commit to RichardH92/besu that referenced this pull request Mar 29, 2021
…dger#1933)

* Replacing the static references of SECP256K1 by an interface

Signed-off-by: Daniel Lehrner <daniel@io.builders>
Signed-off-by: Richard Hart <richardhart92@gmail.com>
@matkt
Copy link
Contributor

matkt commented Apr 13, 2021

I have quite a bit of error message while synchronizing my nodes. I got it on Goerli, Ropsten, Mordor.

The error does not seem to prevent the synchronization from doing well but my logs are full of this messsage.

Looking at the stacktrace this seems related to this modification. Any idea ?

{"timestamp":"2021-04-13T09:35:38,122","level":"ERROR","thread":"vert.x-eventloop-thread-2","class":"VertxPeerDiscoveryAgent","message":"Encountered error while handling packet","throwable":" java.lang.IllegalArgumentException: Could not recover public key\n\tat org.hyperledger.besu.crypto.SECP256K1.recoverFromSignatureNative(SECP256K1.java:495)\n\tat org.hyperledger.besu.crypto.SECP256K1.recoverPublicKeyFromSignature(SECP256K1.java:370)\n\tat org.hyperledger.besu.ethereum.p2p.discovery.internal.Packet.(Packet.java:90)\n\tat org.hyperledger.besu.ethereum.p2p.discovery.internal.Packet.decode(Packet.java:126)\n\tat org.hyperledger.besu.ethereum.p2p.discovery.VertxPeerDiscoveryAgent.lambda$handlePacket$6(VertxPeerDiscoveryAgent.java:221)\n\tat io.vertx.core.impl.ContextImpl.lambda$executeBlocking$2(ContextImpl.java:313)\n\tat io.vertx.core.impl.TaskQueue.run(TaskQueue.java:76)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)\n\tat io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)\n\tat java.base/java.lang.Thread.run(Thread.java:829)\n"}

@daniel-iobuilders
Copy link
Contributor Author

daniel-iobuilders commented Apr 13, 2021

@matkt Thank you for reporting it. Do you use any non-default parameters for starting Besu? Or do you use the default ones, like for example:

besu --network=ropsten

@matkt
Copy link
Contributor

matkt commented Apr 13, 2021

@matkt Thank you for reporting it. Do you use any non-default parameters for starting Besu? Or do you use the default ones, like for example:

besu --network=ropsten

I use the default parameters. When I run Besu the error does not appear directly we have to wait (<5 minutes) for the synchronization to progress and then we have the error messages. If I restart Besu it stop and after some time it reappears

@daniel-iobuilders
Copy link
Contributor Author

@matkt Thank you! I could reproduce the error. I look into it and will let you know when I have found the solution.

@3nprob
Copy link
Contributor

3nprob commented Apr 14, 2021

@daniel-iobuilders Thanks for looking into it. We're getting the same on fast sync nodes on rinkeby/goerli/mainnet, let me know if there's any particular part of config that's relevant to share

Sample trace from besu:21.1.4-openjdk-latest (appearing ~10-30 times per minute):

2021-04-12 18:34:42.924+00:00 | vert.x-eventloop-thread-1 | ERROR | VertxPeerDiscoveryAgent | Encountered error while handling packet
java.lang.IllegalArgumentException: Could not recover public key
	at org.hyperledger.besu.crypto.SECP256K1.recoverFromSignatureNative(SECP256K1.java:420)
	at org.hyperledger.besu.crypto.SECP256K1$PublicKey.recoverFromSignature(SECP256K1.java:539)
	at org.hyperledger.besu.ethereum.p2p.discovery.internal.Packet.<init>(Packet.java:83)
	at org.hyperledger.besu.ethereum.p2p.discovery.internal.Packet.decode(Packet.java:119)
	at org.hyperledger.besu.ethereum.p2p.discovery.VertxPeerDiscoveryAgent.lambda$handlePacket$6(VertxPeerDiscoveryAgent.java:221)
	at io.vertx.core.impl.ContextImpl.lambda$executeBlocking$2(ContextImpl.java:313)
	at io.vertx.core.impl.TaskQueue.run(TaskQueue.java:76)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Thread.java:832)

@daniel-iobuilders
Copy link
Contributor Author

@matkt @3nprob The problem is caused by apparently invalid signatures of some packets that the discovery agent receives. I captured some of these packets and created a test to reproduce the error and tried it with different releases of Besu. This showed that this error is not related to this PR, but also occurs in Besu versions that were released before it, like 21.1.0 or 20.10.4. This leads to the conclusion that the packets are indeed invalid and they don't affect the correct functionality of Besu.

You can verify this by simply running one of the mentioned releases. After a few minutes you will see the same error messages in the logs.

To summarize: This error message appears when a packet of the discovery protocol has an invalid signature. Besu tries to verify it, which throws the exception in question. This is not really an error, because in an open network you can always receive invalid packets and it does not affect any functionality. So what needs to be changed is, how Besu handles those packets. I would suggest that instead of throwing an error a simply debug message in the logs is sufficient. Like this the logs don't get cluttered by it. I will open an issue, share my findings and propose this solution to avoid those messages in the future.

eum602 pushed a commit to lacchain/besu that referenced this pull request Nov 3, 2023
…dger#1933)

* Replacing the static references of SECP256K1 by an interface

Signed-off-by: Daniel Lehrner <daniel@io.builders>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants