Skip to content

Commit

Permalink
Merge pull request #302 from /issues/300
Browse files Browse the repository at this point in the history
Version bump
  • Loading branch information
meywood authored Jun 28, 2024
2 parents 7ac9899 + e5098f0 commit 971e8ba
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 29 deletions.
3 changes: 2 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ apply plugin: 'java'

group = 'network.casper'
// Version number update for release
version='2.5.8'
version='2.5.9'
sourceCompatibility = 1.8
targetCompatibility = 1.8

Expand Down 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
34 changes: 13 additions & 21 deletions src/main/java/com/syntifi/crypto/key/Secp256k1PublicKey.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.syntifi.crypto.key;

import com.casper.sdk.model.key.AlgorithmTag;
import com.syntifi.crypto.key.encdec.Hex;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.bouncycastle.asn1.*;
Expand Down Expand Up @@ -98,7 +97,7 @@ public Boolean verify(byte[] message, byte[] signature) {

if (recoveredKey != null) {

final byte[] keyFromSignature = getRecoveredShortKey(recoveredKey.toByteArray());
final byte[] keyFromSignature = getShortKey(recoveredKey.toByteArray());

if (Arrays.equals(keyFromSignature, keyToFind)) {
return true;
Expand All @@ -114,32 +113,25 @@ public Boolean verify(byte[] message, byte[] signature) {

/**
* Gets a short key
*
* @param key the key as a byte array
* @return short key as byte array
*/
public static byte[] getShortKey(final byte[] key) {
final BigInteger pubKey = new BigInteger(key);
final String pubKeyPrefix = pubKey.testBit(0) ? "03" : "02";
final byte[] pubKeyBytes = Arrays.copyOfRange(key, 0, (AlgorithmTag.SECP256K1.getLength() - 1));
return Hex.decode(pubKeyPrefix + Hex.encode(pubKeyBytes));
}

/**
* There's around a 50% chance the elliptical curve algo will generate a 65 byte
* public key instead of 66 byte.
* Luckily the algo pads the first byte as zero when this happens
* Determine this and then return the byte array to be shortened
* startBit determines this
*
* @param key the key as a byte array
* @return short key as byte array
*/
public static byte[] getRecoveredShortKey(final byte[] key){
if (key[0] == (byte) 0) {
return getShortKey(Arrays.copyOfRange(key, 1, (key.length - 1)));
} else {
return getShortKey(key);
}
public static byte[] getShortKey(final byte[] key) {

final int startBit = key[0] == (byte) 0 ? 1 : 0;

final byte[] shortKey = new byte[AlgorithmTag.SECP256K1.getLength()];
shortKey[0] = (byte) (new BigInteger(key).testBit(0) ? 3 : 2);

System.arraycopy(key, startBit, shortKey, 1, AlgorithmTag.SECP256K1.getLength() - 1);

return shortKey;

}

}
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 @@ -8,7 +8,6 @@
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.security.GeneralSecurityException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
Expand Down Expand Up @@ -82,7 +81,7 @@ void sign_should_sign_message() throws URISyntaxException, IOException {
}

@Test
void create_random_key() throws GeneralSecurityException, IOException {
void create_random_key() {
Secp256k1PrivateKey sk = Secp256k1PrivateKey.deriveRandomKey();
Secp256k1PublicKey pk = (Secp256k1PublicKey) sk.derivePublicKey();
LOGGER.info(sk.getKeyPair().getPrivateKey().toString(16));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,31 @@ void verify_should_be_ok() throws URISyntaxException, IOException {
}

@Test
void signAndRecoverPublicKey_1() throws URISyntaxException, IOException {
void signAndRecoverPublicKeyWithPaddedPK() throws URISyntaxException, IOException {

//Get the private key
Secp256k1PrivateKey privKey = new Secp256k1PrivateKey();
String filePath = getResourcesKeyPath("secp256k1/private-padded.pem");
privKey.readPrivateKey(filePath);

//Check that the public key is private-padded.pem with a 0 byte
assert privKey.getKeyPair().getPublicKey().toByteArray()[0] == (byte) 0;


//Derive the public key
Secp256k1PublicKey publicKey = (Secp256k1PublicKey) privKey.derivePublicKey();

String message = "bc81ca4de9b3a991a6514eddf0e994e0035c7ba58f333c4d7ba5dd18b4c9c547";

//Generate the signature
byte[] signature = privKey.sign(message.getBytes());

//Test
assert publicKey.verify(message.getBytes(), signature);

}
@Test
void signAndRecoverPublicKey_1() throws URISyntaxException, IOException {

//Get the private key
Secp256k1PrivateKey privKey = new Secp256k1PrivateKey();
Expand Down
3 changes: 3 additions & 0 deletions src/test/resources/secp256k1/private-padded.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-----BEGIN EC PRIVATE KEY-----
MC4CAQEEIF6d87JG1raI7KNzkaXUbtTezTHE7oziYJ04VGZv4YOdoAcGBSuBBAAK
-----END EC PRIVATE KEY-----

0 comments on commit 971e8ba

Please sign in to comment.