Skip to content

Commit

Permalink
Tests for the 50% Secp problem when creating a public short key
Browse files Browse the repository at this point in the history
  • Loading branch information
stormeye2000 committed Jun 28, 2024
1 parent f0f7874 commit a95d9a9
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 8 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 4 additions & 0 deletions script/debug-test
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion script/docker-run
Original file line number Diff line number Diff line change
@@ -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
2 changes: 0 additions & 2 deletions src/main/java/com/syntifi/crypto/key/Secp256k1PublicKey.java
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
Expand Down
96 changes: 93 additions & 3 deletions src/test/java/com/casper/sdk/service/CasperServiceTestsNctl.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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.*;

Expand Down Expand Up @@ -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();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;


Expand Down
2 changes: 1 addition & 1 deletion src/test/resources/secp256k1/private-padded.pem
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
-----BEGIN EC PRIVATE KEY-----
MC4CAQEEIGbGCLXUsZFyrJe4nAGW+V5Zd7z+AbccZ5SU3LBQi/Q6oAcGBSuBBAAK
MC4CAQEEIF6d87JG1raI7KNzkaXUbtTezTHE7oziYJ04VGZv4YOdoAcGBSuBBAAK
-----END EC PRIVATE KEY-----

0 comments on commit a95d9a9

Please sign in to comment.