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

Extension Signing request endpoint #982

Merged
merged 34 commits into from
May 9, 2024
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
a9e8973
feat!: Generic signing extension handler
usmansaleem Mar 20, 2024
e4bf550
cq: Remove unnecessary LOG variable
usmansaleem Mar 20, 2024
91cfe43
feat: Use base64 encoding of payload in response
usmansaleem Mar 20, 2024
03ced53
tests - Add acceptance tests
usmansaleem Mar 21, 2024
2e99ece
changelog
usmansaleem Mar 21, 2024
b3d5707
fix: Use specific body for extension signing
usmansaleem Mar 25, 2024
c12c749
test: Update Acceptance tests
usmansaleem Mar 25, 2024
12e5b40
Update cli option
usmansaleem Mar 26, 2024
423e042
Deleting Eth1 AT
usmansaleem Mar 26, 2024
ee024bc
Refactor signing data to use header.payload.sig format
usmansaleem Mar 26, 2024
8d08221
Refactor signing extension to eth2 mode. Fix acceptance test
usmansaleem Mar 27, 2024
87ea174
spotless fix
usmansaleem Mar 27, 2024
d413431
Merge remote-tracking branch 'upstream/master' into sign_extension
usmansaleem Apr 2, 2024
4cd29c6
changelog
usmansaleem Apr 2, 2024
9422720
changing json response format
usmansaleem Apr 2, 2024
bf3dada
Merge remote-tracking branch 'upstream/master' into sign_extension
usmansaleem Apr 5, 2024
a1fea94
Merge remote-tracking branch 'upstream/master' into sign_extension
usmansaleem Apr 8, 2024
5a2903f
fix: Update bouncycastle libraries
usmansaleem Apr 15, 2024
f7cb63e
fix: Update transitive dependency threetenbp and google cloud secretm…
usmansaleem Apr 15, 2024
0c11f44
build: assign dependency scan nvd api key from env variable
usmansaleem Apr 15, 2024
9aa7ecc
changelog
usmansaleem Apr 15, 2024
11c1dd6
fix: Update guava and commons-logging libraries
usmansaleem Apr 15, 2024
458975c
Merge branch 'vuln_scan_fix' into sign_extension
usmansaleem Apr 15, 2024
23984b2
fix: Update response to return hex encoded signature
usmansaleem Apr 15, 2024
7f06899
Merge remote-tracking branch 'upstream/master' into sign_extension
usmansaleem Apr 15, 2024
17692cf
Enforce strict fields parsing for SigningExtensionHandler
usmansaleem May 1, 2024
3c60ee6
Send Base64 encoded payload in the response
usmansaleem May 1, 2024
e58d150
Use appropriate type for tinestamp
usmansaleem May 1, 2024
c4fe46f
Merge remote-tracking branch 'upstream/master' into sign_extension
usmansaleem May 1, 2024
6cbae83
Merge remote-tracking branch 'upstream/master' into sign_extension
usmansaleem May 2, 2024
38a6db7
fix: Always use application/json as Content-Type header
usmansaleem May 8, 2024
a6eb923
build - debug information in docker test.sh
usmansaleem May 8, 2024
acf0714
build - fix docker goss test
usmansaleem May 8, 2024
e11ae2b
build - store docker dgoss test results
usmansaleem May 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Next Version

### Features Added
- Added endpoint `/api/v1/eth2/ext/sign/:identifier` which is enabled using cli option `--Xsigning-ext-enabled=true`. This endpoint allows signing of additional data not covered by the remoting API specs. [#982](https://github.com/Consensys/web3signer/pull/982)
Copy link
Contributor

Choose a reason for hiding this comment

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

Wonder if we should put this under a web3signer-specific namespace? e.g. /w3s/v1/eth2/sign, /w3s/v1/sign or/v1/eth2/w3s/sign.

There's precedent in teku for spec vs teku-specific https://consensys.github.io/teku/#tag/Teku and I think there's some merit for web3signer to follow that pattern.
Other CLs too: https://lighthouse-book.sigmaprime.io/api-lighthouse.html


### Bugs fixed
- Update transitive dependency threetenbp and google cloud secretmanager library to fix CVE-2024-23082, CVE-2024-23081
- Update bouncycastle libraries to fix CVE-2024-29857, CVE-2024-30171, CVE-2024-30172
Expand All @@ -11,6 +14,7 @@
- Update Postgresql JDBC driver to fix CVE-2024-1597
- Fix cached gvr to be thread-safe during first boot. [#978](https://github.com/Consensys/web3signer/issues/978)

---
## 24.2.0

This is a required update for Mainnet users containing the configuration for the Deneb upgrade on March 13th. This update is required for Gnosis Deneb network upgrade on March 11th. For all other networks, this update is optional.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ public class Signer extends FilecoinJsonRpcEndpoint {
public static final String ETH2_PUBLIC_KEYS = "/api/v1/eth2/publicKeys"; // bls keys
public static final String RELOAD_ENDPOINT = "/reload";

public static final String SIGN_EXT_ENDPOINT = "/api/v1/eth2/ext/sign/{identifier}";

public static final ObjectMapper ETH_2_INTERFACE_OBJECT_MAPPER =
SigningObjectMapperFactory.createObjectMapper().setSerializationInclusion(Include.NON_NULL);
private static final String METRICS_ENDPOINT = "/metrics";
Expand Down Expand Up @@ -175,6 +177,17 @@ public Response eth2Sign(
.post(signPath(BLS));
}

public Response signExtensionPayload(
final String publicKey, final String payload, final ContentType acceptMediaType) {
return given()
.baseUri(getUrl())
.contentType(ContentType.JSON)
.accept(acceptMediaType)
.pathParam("identifier", publicKey)
.body(payload)
.post(SIGN_EXT_ENDPOINT);
}

public Response callApiPublicKeys(final KeyType keyType) {
return given().baseUri(getUrl()).get(publicKeysPath(keyType));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ public class SignerConfiguration {
private final ChainIdProvider chainIdProvider;
private final Optional<KeystoresParameters> v3KeystoresBulkloadParameters;

private final boolean signingExtEnabled;

public SignerConfiguration(
final String hostname,
final int httpRpcPort,
Expand Down Expand Up @@ -123,7 +125,8 @@ public SignerConfiguration(
final int downstreamHttpPort,
final Optional<ClientTlsOptions> downstreamTlsOptions,
final ChainIdProvider chainIdProvider,
final Optional<KeystoresParameters> v3KeystoresBulkloadParameters) {
final Optional<KeystoresParameters> v3KeystoresBulkloadParameters,
final boolean signingExtEnabled) {
this.hostname = hostname;
this.logLevel = logLevel;
this.httpRpcPort = httpRpcPort;
Expand Down Expand Up @@ -168,6 +171,7 @@ public SignerConfiguration(
this.downstreamTlsOptions = downstreamTlsOptions;
this.chainIdProvider = chainIdProvider;
this.v3KeystoresBulkloadParameters = v3KeystoresBulkloadParameters;
this.signingExtEnabled = signingExtEnabled;
}

public String hostname() {
Expand Down Expand Up @@ -353,4 +357,8 @@ public ChainIdProvider getChainIdProvider() {
public Optional<KeystoresParameters> getV3KeystoresBulkloadParameters() {
return v3KeystoresBulkloadParameters;
}

public boolean isSigningExtEnabled() {
return signingExtEnabled;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ public class SignerConfigurationBuilder {

private KeystoresParameters v3KeystoresBulkloadParameters;

private boolean signingExtEnabled;

public SignerConfigurationBuilder withLogLevel(final Level logLevel) {
this.logLevel = logLevel;
return this;
Expand Down Expand Up @@ -318,6 +320,11 @@ public SignerConfigurationBuilder withV3KeystoresBulkloadParameters(
return this;
}

public SignerConfigurationBuilder withSigningExtEnabled(final boolean signingExtEnabled) {
this.signingExtEnabled = signingExtEnabled;
return this;
}

public SignerConfiguration build() {
if (mode == null) {
throw new IllegalArgumentException("Mode cannot be null");
Expand Down Expand Up @@ -366,6 +373,7 @@ public SignerConfiguration build() {
downstreamHttpPort,
Optional.ofNullable(downstreamTlsOptions),
chainIdProvider,
Optional.ofNullable(v3KeystoresBulkloadParameters));
Optional.ofNullable(v3KeystoresBulkloadParameters),
signingExtEnabled);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,11 @@ public List<String> createCmdLineParams() {
.getGcpParameters()
.ifPresent(gcpParameters -> yamlConfig.append(gcpBulkLoadingOptions(gcpParameters)));

if (signerConfig.isSigningExtEnabled()) {
yamlConfig.append(
String.format(YAML_BOOLEAN_FMT, "eth2.Xsigning-ext-enabled", Boolean.TRUE));
}

final CommandArgs subCommandArgs = createSubCommandArgs();
params.addAll(subCommandArgs.params);
yamlConfig.append(subCommandArgs.yamlConfig);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ public List<String> createCmdLineParams() {
signerConfig
.getGcpParameters()
.ifPresent(gcpParams -> params.addAll(gcpSecretManagerBulkLoadingOptions(gcpParams)));

if (signerConfig.isSigningExtEnabled()) {
params.add("--Xsigning-ext-enabled=true");
}
} else if (signerConfig.getMode().equals("eth1")) {
params.add("--downstream-http-port");
params.add(Integer.toString(signerConfig.getDownstreamHttpPort()));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*
* Copyright 2024 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.web3signer.tests.signing;

import static io.restassured.http.ContentType.JSON;
import static io.restassured.http.ContentType.TEXT;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;
import static tech.pegasys.teku.spec.SpecMilestone.DENEB;

import tech.pegasys.teku.bls.BLS;
import tech.pegasys.teku.bls.BLSKeyPair;
import tech.pegasys.teku.bls.BLSPublicKey;
import tech.pegasys.teku.bls.BLSSecretKey;
import tech.pegasys.teku.bls.BLSSignature;
import tech.pegasys.teku.spec.networks.Eth2Network;
import tech.pegasys.web3signer.core.service.http.SigningObjectMapperFactory;
import tech.pegasys.web3signer.core.service.http.handlers.signing.ProofOfValidationBody;
import tech.pegasys.web3signer.core.service.http.handlers.signing.SigningExtensionType;
import tech.pegasys.web3signer.dsl.signer.SignerConfigurationBuilder;
import tech.pegasys.web3signer.dsl.utils.MetadataFileHelpers;
import tech.pegasys.web3signer.signing.KeyType;

import java.nio.file.Path;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.restassured.http.ContentType;
import io.vertx.core.json.JsonObject;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.junit.jupiter.params.provider.ValueSource;

public class ProofOfValidationSigningExtAcceptanceTest extends SigningAcceptanceTestBase {

private static final String PRIVATE_KEY =
"3ee2224386c82ffea477e2adf28a2929f5c349165a4196158c7f3a2ecca40f35";
private static final MetadataFileHelpers METADATA_FILE_HELPERS = new MetadataFileHelpers();
private static final BLSSecretKey KEY =
BLSSecretKey.fromBytes(Bytes32.fromHexString(PRIVATE_KEY));
private static final BLSKeyPair KEY_PAIR = new BLSKeyPair(KEY);
private static final BLSPublicKey PUBLIC_KEY = KEY_PAIR.getPublicKey();
private static final ObjectMapper JSON_MAPPER = SigningObjectMapperFactory.createObjectMapper();

@BeforeEach
void setup() {
final String configFilename = PUBLIC_KEY.toString().substring(2);
final Path keyConfigFile = testDirectory.resolve(configFilename + ".yaml");
METADATA_FILE_HELPERS.createUnencryptedYamlFileAt(keyConfigFile, PRIVATE_KEY, KeyType.BLS);

setForkEpochsAndStartSigner(
new SignerConfigurationBuilder()
.withKeyStoreDirectory(testDirectory)
.withMode("eth2")
.withNetwork(Eth2Network.MINIMAL.configName())
.withSigningExtEnabled(true),
DENEB);
}

@ParameterizedTest(name = "{index} - Testing Accept Media Type: {0}")
@EnumSource(
value = ContentType.class,
names = {"ANY", "JSON", "TEXT"})
void extensionSigningData(ContentType acceptMediaType) throws Exception {
final var signingExtensionBody =
new ProofOfValidationBody(
SigningExtensionType.PROOF_OF_VALIDATION,
"AT",
String.valueOf(System.currentTimeMillis()));
final var payload = JSON_MAPPER.writeValueAsString(signingExtensionBody);

final var response =
signer.signExtensionPayload(PUBLIC_KEY.toString(), payload, acceptMediaType);

final var hexEncodedSignature =
switch (acceptMediaType) {
case TEXT -> {
response.then().statusCode(200).contentType(TEXT);
yield response.body().print();
}
case JSON, ANY -> {
response.then().statusCode(200).contentType(JSON);
yield new JsonObject(response.body().print()).getString("signature");
}
default -> throw new IllegalStateException("Unexpected value: " + acceptMediaType);
};

final var blsSignature =
BLSSignature.fromBytesCompressed(Bytes.fromHexString(hexEncodedSignature));

final var isValidBLSSig =
BLS.verify(PUBLIC_KEY, Bytes.wrap(payload.getBytes(UTF_8)), blsSignature);
assertThat(isValidBLSSig).isTrue();
}

@Test
void invalidIdentifierCausesNotFound() throws Exception {
final ProofOfValidationBody proofOfValidationBody =
new ProofOfValidationBody(
SigningExtensionType.PROOF_OF_VALIDATION,
"AT",
String.valueOf(System.currentTimeMillis()));
final String data = JSON_MAPPER.writeValueAsString(proofOfValidationBody);

signer.signExtensionPayload("0x1234", data, JSON).then().statusCode(404);
}

@ParameterizedTest(name = "{index} - Testing Invalid Body: {0}")
@ValueSource(strings = {"", "invalid", "{}", "{\"data\": \"invalid\"}"})
void invalidBodyCausesBadRequestStatusCode(final String data) {
signer.signExtensionPayload(PUBLIC_KEY.toString(), data, JSON).then().statusCode(400);
}

@Test
void invalidSignExtensionTypeCausesBadRequestStatusCode() throws Exception {
final ProofOfValidationBody proofOfValidationBody =
new ProofOfValidationBody(
SigningExtensionType.PROOF_OF_VALIDATION,
"AT",
String.valueOf(System.currentTimeMillis()));
var payload = JSON_MAPPER.writeValueAsString(proofOfValidationBody);
payload = payload.replace("PROOF_OF_VALIDATION", "INVALID_TYPE");

signer.signExtensionPayload(PUBLIC_KEY.toString(), payload, JSON).then().statusCode(400);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,19 +49,13 @@ protected void setupEth2Signer(final Eth2Network eth2Network, final SpecMileston
.withKeyStoreDirectory(testDirectory)
.withMode("eth2")
.withNetwork(eth2Network.configName());

setForkEpochs(specMilestone, builder);

startSigner(builder.build());
setForkEpochsAndStartSigner(builder, specMilestone);
}

protected void setupEth2Signer(final Path networkConfigFile, final SpecMilestone specMilestone) {
final SignerConfigurationBuilder builder = new SignerConfigurationBuilder();
builder.withKeyStoreDirectory(testDirectory).withMode("eth2").withNetwork(networkConfigFile);

setForkEpochs(specMilestone, builder);

startSigner(builder.build());
setForkEpochsAndStartSigner(builder, specMilestone);
}

protected void setupEth2SignerWithCustomNetworkConfig(final Path networkConfigFile) {
Expand All @@ -73,6 +67,12 @@ protected void setupEth2SignerWithCustomNetworkConfig(final Path networkConfigFi
startSigner(builder.build());
}

protected void setForkEpochsAndStartSigner(
final SignerConfigurationBuilder builder, final SpecMilestone specMilestone) {
setForkEpochs(specMilestone, builder);
startSigner(builder.build());
}

private void setForkEpochs(
final SpecMilestone specMilestone, final SignerConfigurationBuilder builder) {
switch (specMilestone) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ public class Web3SignerBaseCommand implements BaseConfig, Runnable {
paramLabel = INTEGER_FORMAT_HELP)
private Integer vertxWorkerPoolSize = null;

@Deprecated
@Deprecated(forRemoval = true)
@Option(names = "--Xworker-pool-size", hidden = true)
private Integer deprecatedWorkerPoolSize = null;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,14 @@ private static class NetworkCliCompletionCandidates extends ArrayList<String> {
arity = "1")
private boolean isKeyManagerApiEnabled = false;

@CommandLine.Option(
names = "--Xsigning-ext-enabled",
description = "Set to true to enable signing extensions.",
paramLabel = "<BOOL>",
arity = "1",
hidden = true)
private boolean signingExtEnabled = false;

@Mixin private PicoCliSlashingProtectionParameters slashingProtectionParameters;
@Mixin private PicoCliEth2AzureKeyVaultParameters azureKeyVaultParameters;
@Mixin private PicoKeystoresParameters keystoreParameters;
Expand All @@ -156,7 +164,8 @@ public Runner createRunner() {
awsSecretsManagerParameters,
gcpSecretManagerParameters,
eth2Spec,
isKeyManagerApiEnabled);
isKeyManagerApiEnabled,
signingExtEnabled);
}

private void logNetworkSpecInformation() {
Expand Down
Loading
Loading