From a95d9a9b0d91cfbf8c5532ce73066763fd90a8b8 Mon Sep 17 00:00:00 2001 From: Carl Norburn Date: Fri, 28 Jun 2024 10:19:53 +0100 Subject: [PATCH] Tests for the 50% Secp problem when creating a public short key --- build.gradle | 1 + script/debug-test | 4 + script/docker-run | 2 +- .../crypto/key/Secp256k1PublicKey.java | 2 - .../sdk/service/CasperServiceTestsNctl.java | 96 ++++++++++++++++++- .../crypto/key/Secp256k1PublicKeyTests.java | 2 +- .../resources/secp256k1/private-padded.pem | 2 +- 7 files changed, 101 insertions(+), 8 deletions(-) create mode 100755 script/debug-test diff --git a/build.gradle b/build.gradle index 58f1e4e29..76a17d435 100644 --- a/build.gradle +++ b/build.gradle @@ -49,6 +49,7 @@ dependencies { testImplementation "org.junit.jupiter:junit-jupiter:${jupiterVersion}" // Used to compare json strings while testing testImplementation "org.skyscreamer:jsonassert:${jsonassertVersion}" + testImplementation files('assets') } java { diff --git a/script/debug-test b/script/debug-test new file mode 100755 index 000000000..29412781c --- /dev/null +++ b/script/debug-test @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +cd "$(dirname "$0")/.."|| exit 1 +./gradlew --stop +./gradlew cleanTest test -Dorg.gradle.jvmargs='-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005' --no-daemon --debug-jvm \ No newline at end of file diff --git a/script/docker-run b/script/docker-run index 0987ca2c9..26584abe2 100644 --- a/script/docker-run +++ b/script/docker-run @@ -1,3 +1,3 @@ #!/usr/bin/env bash # run the cspr-nctl container in docker -docker run --rm -it --name cspr-nctl -d -p 25101:25101 -p 11101:11101 -p 14101:14101 -p 18101:18101 stormeye2000/cspr-nctl:linux-1.5.5 +docker run --rm -it --name cspr-nctl -d -p 25101:25101 -p 11101:11101 -p 14101:14101 -p 18101:18101 stormeye2000/cspr-nctl:release-1.5.5 diff --git a/src/main/java/com/syntifi/crypto/key/Secp256k1PublicKey.java b/src/main/java/com/syntifi/crypto/key/Secp256k1PublicKey.java index 42ab48909..9496ebe5e 100644 --- a/src/main/java/com/syntifi/crypto/key/Secp256k1PublicKey.java +++ b/src/main/java/com/syntifi/crypto/key/Secp256k1PublicKey.java @@ -125,9 +125,7 @@ public Boolean verify(byte[] message, byte[] signature) { public static byte[] getShortKey(final byte[] key) { final BigInteger pubKey = new BigInteger(key); final String pubKeyPrefix = pubKey.testBit(0) ? "03" : "02"; - final int startBit = key[0] == (byte) 0 ? 1 : 0; - final byte[] pubKeyBytes = Arrays.copyOfRange(key, startBit, (AlgorithmTag.SECP256K1.getLength() - 1) + startBit); return Hex.decode(pubKeyPrefix + Hex.encode(pubKeyBytes)); } diff --git a/src/test/java/com/casper/sdk/service/CasperServiceTestsNctl.java b/src/test/java/com/casper/sdk/service/CasperServiceTestsNctl.java index 79996ad54..a66d3fe5e 100644 --- a/src/test/java/com/casper/sdk/service/CasperServiceTestsNctl.java +++ b/src/test/java/com/casper/sdk/service/CasperServiceTestsNctl.java @@ -1,6 +1,8 @@ package com.casper.sdk.service; import com.casper.sdk.exception.CasperInvalidStateException; +import com.casper.sdk.exception.NoSuchTypeException; +import com.casper.sdk.helper.CasperTransferHelper; import com.casper.sdk.identifier.block.HashBlockIdentifier; import com.casper.sdk.identifier.block.HeightBlockIdentifier; import com.casper.sdk.identifier.global.GlobalStateIdentifier; @@ -9,22 +11,29 @@ import com.casper.sdk.identifier.purse.PurseIdentifier; import com.casper.sdk.model.balance.QueryBalanceData; import com.casper.sdk.model.block.JsonBlockData; +import com.casper.sdk.model.common.Ttl; +import com.casper.sdk.model.deploy.Deploy; +import com.casper.sdk.model.deploy.DeployData; +import com.casper.sdk.model.deploy.DeployResult; import com.casper.sdk.model.era.EraInfoData; import com.casper.sdk.model.key.PublicKey; import com.casper.sdk.model.status.ChainspecData; import com.casper.sdk.model.status.StatusData; -import com.syntifi.crypto.key.AbstractPrivateKey; -import com.syntifi.crypto.key.Ed25519PrivateKey; -import org.junit.Ignore; +import com.syntifi.crypto.key.*; +import dev.oak3.sbs4j.exception.ValueSerializationException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.io.IOException; import java.math.BigInteger; +import java.net.URISyntaxException; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.GeneralSecurityException; +import java.util.*; import static org.junit.jupiter.api.Assertions.*; @@ -179,5 +188,86 @@ void getGlobalStateAndReadAsString() { } } + /** + * We're testing the 50% Secp problem here + * When a Secp public key is generated from prime numbers, there's a 50% chance it will 65bit or 66bit + * When it's 65% the long public key is padded with zero + * This test uses a Secp private key that will generate a 65bit padded public key + */ + @Test + void testDeployWithPaddedPublicKey() throws URISyntaxException, IOException, NoSuchTypeException, GeneralSecurityException, ValueSerializationException { + + DeployData deploy; + + //First fund the private-padded.pem account from the faucet account + final Ed25519PrivateKey faucetPrivateKey = new Ed25519PrivateKey(); + final URL faucetKey = getClass().getResource("/net-1/faucet/secret_key.pem"); + assert faucetKey != null; + faucetPrivateKey.readPrivateKey(faucetKey.getFile()); + + Secp256k1PrivateKey paddedPrivateKey = new Secp256k1PrivateKey(); + String filePath = getResourcesKeyPath("secp256k1/private-padded.pem"); + paddedPrivateKey.readPrivateKey(filePath); + + + byte[] paddedPublicKeyFull = paddedPrivateKey.getKeyPair().getPublicKey().toByteArray(); + assert paddedPublicKeyFull[0] == (byte) 0; + + DeployResult deployResult = doDeploy(faucetPrivateKey, paddedPrivateKey.derivePublicKey()); + + assert deployResult != null; + + //wait for deploy to be accepted + do { + deploy = casperServiceNctl.getDeploy(deployResult.getDeployHash()); + } while (deploy.getDeploy().getApprovals() == null || deploy.getDeploy().getApprovals().size() <= 0); + + + //Now transfer from private-padded.pem to another user + Secp256k1PrivateKey toPrivateKey = new Secp256k1PrivateKey(); + filePath = getResourcesKeyPath("secp256k1/secret_key.pem"); + toPrivateKey.readPrivateKey(filePath); + + deployResult = doDeploy(paddedPrivateKey, toPrivateKey.derivePublicKey()); + + assert deployResult != null; + + //wait for deploy to be accepted + do { + deploy = casperServiceNctl.getDeploy(deployResult.getDeployHash()); + } while (deploy.getDeploy().getApprovals() == null || deploy.getDeploy().getApprovals().size() <= 0); + + + assert Arrays.equals(deploy.getDeploy().getApprovals().get(0).getSigner().getPubKey().getKey(), paddedPrivateKey.derivePublicKey().getKey()); + + //now verify signature + + Secp256k1PublicKey paddedPublicKey = (Secp256k1PublicKey) paddedPrivateKey.derivePublicKey(); + + assert paddedPublicKey.verify(deploy.getDeploy().getHash().getDigest(), deploy.getDeploy().getApprovals().get(0).getSignature().getKey()); + + } + + private DeployResult doDeploy(final AbstractPrivateKey sk, final AbstractPublicKey pk) throws NoSuchTypeException, GeneralSecurityException, ValueSerializationException { + + final Deploy deploy = CasperTransferHelper.buildTransferDeploy( + sk, + PublicKey.fromAbstractPublicKey(pk), + BigInteger.valueOf(2500000000L), + "casper-net-1", + Math.abs(new Random().nextLong()), + BigInteger.valueOf(100000000L), + 1L, + Ttl.builder().ttl("30m").build(), + new Date(), + new ArrayList<>()); + + return casperServiceNctl.putDeploy(deploy); + } + + + protected String getResourcesKeyPath(String filename) throws URISyntaxException { + return Paths.get(Objects.requireNonNull(getClass().getClassLoader().getResource(filename)).toURI()).toString(); + } } diff --git a/src/test/java/com/syntifi/crypto/key/Secp256k1PublicKeyTests.java b/src/test/java/com/syntifi/crypto/key/Secp256k1PublicKeyTests.java index b02687d47..eccba87ad 100644 --- a/src/test/java/com/syntifi/crypto/key/Secp256k1PublicKeyTests.java +++ b/src/test/java/com/syntifi/crypto/key/Secp256k1PublicKeyTests.java @@ -70,7 +70,7 @@ void signAndRecoverPublicKeyWithPaddedPK() throws URISyntaxException, IOExceptio String filePath = getResourcesKeyPath("secp256k1/private-padded.pem"); privKey.readPrivateKey(filePath); - //Check that the public key is padded with a 0 byte + //Check that the public key is private-padded.pem with a 0 byte assert privKey.getKeyPair().getPublicKey().toByteArray()[0] == (byte) 0; diff --git a/src/test/resources/secp256k1/private-padded.pem b/src/test/resources/secp256k1/private-padded.pem index 4dde15e72..3c034ebb5 100644 --- a/src/test/resources/secp256k1/private-padded.pem +++ b/src/test/resources/secp256k1/private-padded.pem @@ -1,3 +1,3 @@ -----BEGIN EC PRIVATE KEY----- -MC4CAQEEIGbGCLXUsZFyrJe4nAGW+V5Zd7z+AbccZ5SU3LBQi/Q6oAcGBSuBBAAK +MC4CAQEEIF6d87JG1raI7KNzkaXUbtTezTHE7oziYJ04VGZv4YOdoAcGBSuBBAAK -----END EC PRIVATE KEY-----