Skip to content

Commit 971e8ba

Browse files
authored
Merge pull request #302 from /issues/300
Version bump
2 parents 7ac9899 + e5098f0 commit 971e8ba

8 files changed

Lines changed: 142 additions & 29 deletions

File tree

build.gradle

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ apply plugin: 'java'
1414

1515
group = 'network.casper'
1616
// Version number update for release
17-
version='2.5.8'
17+
version='2.5.9'
1818
sourceCompatibility = 1.8
1919
targetCompatibility = 1.8
2020

@@ -49,6 +49,7 @@ dependencies {
4949
testImplementation "org.junit.jupiter:junit-jupiter:${jupiterVersion}"
5050
// Used to compare json strings while testing
5151
testImplementation "org.skyscreamer:jsonassert:${jsonassertVersion}"
52+
testImplementation files('assets')
5253
}
5354

5455
java {

script/debug-test

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/usr/bin/env bash
2+
cd "$(dirname "$0")/.."|| exit 1
3+
./gradlew --stop
4+
./gradlew cleanTest test -Dorg.gradle.jvmargs='-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005' --no-daemon --debug-jvm

script/docker-run

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
#!/usr/bin/env bash
22
# run the cspr-nctl container in docker
3-
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
3+
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

src/main/java/com/syntifi/crypto/key/Secp256k1PublicKey.java

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.syntifi.crypto.key;
22

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

9998
if (recoveredKey != null) {
10099

101-
final byte[] keyFromSignature = getRecoveredShortKey(recoveredKey.toByteArray());
100+
final byte[] keyFromSignature = getShortKey(recoveredKey.toByteArray());
102101

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

115114
/**
116115
* Gets a short key
117-
*
118-
* @param key the key as a byte array
119-
* @return short key as byte array
120-
*/
121-
public static byte[] getShortKey(final byte[] key) {
122-
final BigInteger pubKey = new BigInteger(key);
123-
final String pubKeyPrefix = pubKey.testBit(0) ? "03" : "02";
124-
final byte[] pubKeyBytes = Arrays.copyOfRange(key, 0, (AlgorithmTag.SECP256K1.getLength() - 1));
125-
return Hex.decode(pubKeyPrefix + Hex.encode(pubKeyBytes));
126-
}
127-
128-
/**
129116
* There's around a 50% chance the elliptical curve algo will generate a 65 byte
130117
* public key instead of 66 byte.
131118
* Luckily the algo pads the first byte as zero when this happens
132-
* Determine this and then return the byte array to be shortened
119+
* startBit determines this
133120
*
134121
* @param key the key as a byte array
135122
* @return short key as byte array
136123
*/
137-
public static byte[] getRecoveredShortKey(final byte[] key){
138-
if (key[0] == (byte) 0) {
139-
return getShortKey(Arrays.copyOfRange(key, 1, (key.length - 1)));
140-
} else {
141-
return getShortKey(key);
142-
}
124+
public static byte[] getShortKey(final byte[] key) {
125+
126+
final int startBit = key[0] == (byte) 0 ? 1 : 0;
127+
128+
final byte[] shortKey = new byte[AlgorithmTag.SECP256K1.getLength()];
129+
shortKey[0] = (byte) (new BigInteger(key).testBit(0) ? 3 : 2);
130+
131+
System.arraycopy(key, startBit, shortKey, 1, AlgorithmTag.SECP256K1.getLength() - 1);
132+
133+
return shortKey;
134+
143135
}
144136

145137
}

src/test/java/com/casper/sdk/service/CasperServiceTestsNctl.java

Lines changed: 93 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.casper.sdk.service;
22

33
import com.casper.sdk.exception.CasperInvalidStateException;
4+
import com.casper.sdk.exception.NoSuchTypeException;
5+
import com.casper.sdk.helper.CasperTransferHelper;
46
import com.casper.sdk.identifier.block.HashBlockIdentifier;
57
import com.casper.sdk.identifier.block.HeightBlockIdentifier;
68
import com.casper.sdk.identifier.global.GlobalStateIdentifier;
@@ -9,22 +11,29 @@
911
import com.casper.sdk.identifier.purse.PurseIdentifier;
1012
import com.casper.sdk.model.balance.QueryBalanceData;
1113
import com.casper.sdk.model.block.JsonBlockData;
14+
import com.casper.sdk.model.common.Ttl;
15+
import com.casper.sdk.model.deploy.Deploy;
16+
import com.casper.sdk.model.deploy.DeployData;
17+
import com.casper.sdk.model.deploy.DeployResult;
1218
import com.casper.sdk.model.era.EraInfoData;
1319
import com.casper.sdk.model.key.PublicKey;
1420
import com.casper.sdk.model.status.ChainspecData;
1521
import com.casper.sdk.model.status.StatusData;
16-
import com.syntifi.crypto.key.AbstractPrivateKey;
17-
import com.syntifi.crypto.key.Ed25519PrivateKey;
18-
import org.junit.Ignore;
22+
import com.syntifi.crypto.key.*;
23+
import dev.oak3.sbs4j.exception.ValueSerializationException;
1924
import org.junit.jupiter.api.Assertions;
2025
import org.junit.jupiter.api.Disabled;
2126
import org.junit.jupiter.api.Test;
2227

2328
import java.io.IOException;
2429
import java.math.BigInteger;
30+
import java.net.URISyntaxException;
2531
import java.net.URL;
2632
import java.nio.file.Files;
2733
import java.nio.file.Path;
34+
import java.nio.file.Paths;
35+
import java.security.GeneralSecurityException;
36+
import java.util.*;
2837

2938
import static org.junit.jupiter.api.Assertions.*;
3039

@@ -179,5 +188,86 @@ void getGlobalStateAndReadAsString() {
179188
}
180189
}
181190

191+
/**
192+
* We're testing the 50% Secp problem here
193+
* When a Secp public key is generated from prime numbers, there's a 50% chance it will 65bit or 66bit
194+
* When it's 65% the long public key is padded with zero
195+
* This test uses a Secp private key that will generate a 65bit padded public key
196+
*/
197+
@Test
198+
void testDeployWithPaddedPublicKey() throws URISyntaxException, IOException, NoSuchTypeException, GeneralSecurityException, ValueSerializationException {
199+
200+
DeployData deploy;
201+
202+
//First fund the private-padded.pem account from the faucet account
203+
final Ed25519PrivateKey faucetPrivateKey = new Ed25519PrivateKey();
204+
final URL faucetKey = getClass().getResource("/net-1/faucet/secret_key.pem");
205+
assert faucetKey != null;
206+
faucetPrivateKey.readPrivateKey(faucetKey.getFile());
207+
208+
Secp256k1PrivateKey paddedPrivateKey = new Secp256k1PrivateKey();
209+
String filePath = getResourcesKeyPath("secp256k1/private-padded.pem");
210+
paddedPrivateKey.readPrivateKey(filePath);
211+
212+
213+
byte[] paddedPublicKeyFull = paddedPrivateKey.getKeyPair().getPublicKey().toByteArray();
214+
assert paddedPublicKeyFull[0] == (byte) 0;
215+
216+
DeployResult deployResult = doDeploy(faucetPrivateKey, paddedPrivateKey.derivePublicKey());
217+
218+
assert deployResult != null;
219+
220+
//wait for deploy to be accepted
221+
do {
222+
deploy = casperServiceNctl.getDeploy(deployResult.getDeployHash());
223+
} while (deploy.getDeploy().getApprovals() == null || deploy.getDeploy().getApprovals().size() <= 0);
224+
225+
226+
//Now transfer from private-padded.pem to another user
227+
Secp256k1PrivateKey toPrivateKey = new Secp256k1PrivateKey();
228+
filePath = getResourcesKeyPath("secp256k1/secret_key.pem");
229+
toPrivateKey.readPrivateKey(filePath);
230+
231+
deployResult = doDeploy(paddedPrivateKey, toPrivateKey.derivePublicKey());
232+
233+
assert deployResult != null;
234+
235+
//wait for deploy to be accepted
236+
do {
237+
deploy = casperServiceNctl.getDeploy(deployResult.getDeployHash());
238+
} while (deploy.getDeploy().getApprovals() == null || deploy.getDeploy().getApprovals().size() <= 0);
239+
240+
241+
assert Arrays.equals(deploy.getDeploy().getApprovals().get(0).getSigner().getPubKey().getKey(), paddedPrivateKey.derivePublicKey().getKey());
242+
243+
//now verify signature
244+
245+
Secp256k1PublicKey paddedPublicKey = (Secp256k1PublicKey) paddedPrivateKey.derivePublicKey();
246+
247+
assert paddedPublicKey.verify(deploy.getDeploy().getHash().getDigest(), deploy.getDeploy().getApprovals().get(0).getSignature().getKey());
248+
249+
}
250+
251+
private DeployResult doDeploy(final AbstractPrivateKey sk, final AbstractPublicKey pk) throws NoSuchTypeException, GeneralSecurityException, ValueSerializationException {
252+
253+
final Deploy deploy = CasperTransferHelper.buildTransferDeploy(
254+
sk,
255+
PublicKey.fromAbstractPublicKey(pk),
256+
BigInteger.valueOf(2500000000L),
257+
"casper-net-1",
258+
Math.abs(new Random().nextLong()),
259+
BigInteger.valueOf(100000000L),
260+
1L,
261+
Ttl.builder().ttl("30m").build(),
262+
new Date(),
263+
new ArrayList<>());
264+
265+
return casperServiceNctl.putDeploy(deploy);
266+
}
267+
268+
269+
protected String getResourcesKeyPath(String filename) throws URISyntaxException {
270+
return Paths.get(Objects.requireNonNull(getClass().getClassLoader().getResource(filename)).toURI()).toString();
271+
}
182272

183273
}

src/test/java/com/syntifi/crypto/key/Secp256k1PrivateKeyTests.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import java.io.File;
99
import java.io.IOException;
1010
import java.net.URISyntaxException;
11-
import java.security.GeneralSecurityException;
1211
import java.text.DateFormat;
1312
import java.text.SimpleDateFormat;
1413
import java.util.Date;
@@ -82,7 +81,7 @@ void sign_should_sign_message() throws URISyntaxException, IOException {
8281
}
8382

8483
@Test
85-
void create_random_key() throws GeneralSecurityException, IOException {
84+
void create_random_key() {
8685
Secp256k1PrivateKey sk = Secp256k1PrivateKey.deriveRandomKey();
8786
Secp256k1PublicKey pk = (Secp256k1PublicKey) sk.derivePublicKey();
8887
LOGGER.info(sk.getKeyPair().getPrivateKey().toString(16));

src/test/java/com/syntifi/crypto/key/Secp256k1PublicKeyTests.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,31 @@ void verify_should_be_ok() throws URISyntaxException, IOException {
6363
}
6464

6565
@Test
66-
void signAndRecoverPublicKey_1() throws URISyntaxException, IOException {
66+
void signAndRecoverPublicKeyWithPaddedPK() throws URISyntaxException, IOException {
67+
68+
//Get the private key
69+
Secp256k1PrivateKey privKey = new Secp256k1PrivateKey();
70+
String filePath = getResourcesKeyPath("secp256k1/private-padded.pem");
71+
privKey.readPrivateKey(filePath);
72+
73+
//Check that the public key is private-padded.pem with a 0 byte
74+
assert privKey.getKeyPair().getPublicKey().toByteArray()[0] == (byte) 0;
75+
76+
77+
//Derive the public key
78+
Secp256k1PublicKey publicKey = (Secp256k1PublicKey) privKey.derivePublicKey();
79+
80+
String message = "bc81ca4de9b3a991a6514eddf0e994e0035c7ba58f333c4d7ba5dd18b4c9c547";
81+
82+
//Generate the signature
83+
byte[] signature = privKey.sign(message.getBytes());
84+
85+
//Test
86+
assert publicKey.verify(message.getBytes(), signature);
87+
88+
}
89+
@Test
90+
void signAndRecoverPublicKey_1() throws URISyntaxException, IOException {
6791

6892
//Get the private key
6993
Secp256k1PrivateKey privKey = new Secp256k1PrivateKey();
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
-----BEGIN EC PRIVATE KEY-----
2+
MC4CAQEEIF6d87JG1raI7KNzkaXUbtTezTHE7oziYJ04VGZv4YOdoAcGBSuBBAAK
3+
-----END EC PRIVATE KEY-----

0 commit comments

Comments
 (0)