Skip to content

Commit 3223936

Browse files
authored
Merge pull request #297 from /issues/293
Issues/293
2 parents cf70d59 + a8df826 commit 3223936

3 files changed

Lines changed: 139 additions & 21 deletions

File tree

build.gradle

Lines changed: 1 addition & 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.7'
17+
version='2.5.8'
1818
sourceCompatibility = 1.8
1919
targetCompatibility = 1.8
2020

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

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44
import lombok.EqualsAndHashCode;
55
import lombok.NoArgsConstructor;
66
import org.bouncycastle.asn1.*;
7+
import org.web3j.crypto.ECDSASignature;
78
import org.web3j.crypto.Hash;
89
import org.web3j.crypto.Sign;
9-
import org.web3j.crypto.Sign.SignatureData;
1010

11-
import java.io.*;
11+
import java.io.IOException;
12+
import java.io.Reader;
13+
import java.io.Writer;
1214
import java.math.BigInteger;
13-
import java.security.GeneralSecurityException;
1415
import java.util.Arrays;
1516

1617
/**
@@ -62,21 +63,52 @@ public void writePublicKey(final Writer writer) throws IOException {
6263
PemFileHelper.writePemFile(writer, derKey.getEncoded(), ASN1Identifiers.PUBLIC_KEY_DER_HEADER);
6364
}
6465

66+
/**
67+
* Iterates possible signature combinations and possible recovery id's
68+
* Casper does not use signature.v so we have to iterate v
69+
* We don't know v so we have to iterate the possible recover id's
70+
* Converts to short public key for comparison
71+
*
72+
* @param message the signed message
73+
* @param signature the signature to check against
74+
* @return true|false public key found
75+
*/
6576
@Override
66-
public Boolean verify(final byte[] message, final byte[] signature) throws GeneralSecurityException {
67-
//TODO: Double check the issue the getV(), for now we are trying with both (27 and 28)
68-
final SignatureData signatureData1 = new SignatureData(
69-
(byte) 27,
70-
Arrays.copyOfRange(signature, 0, 32),
71-
Arrays.copyOfRange(signature, 32, 64));
72-
final BigInteger derivedKey1 = Sign.signedMessageHashToKey(Hash.sha256(message), signatureData1);
73-
final SignatureData signatureData2 = new SignatureData(
74-
(byte) 28,
75-
Arrays.copyOfRange(signature, 0, 32),
76-
Arrays.copyOfRange(signature, 32, 64));
77-
final BigInteger derivedKey2 = Sign.signedMessageHashToKey(Hash.sha256(message), signatureData2);
78-
return Arrays.equals(Secp256k1PublicKey.getShortKey(derivedKey1.toByteArray()), getKey()) ||
79-
Arrays.equals(Secp256k1PublicKey.getShortKey(derivedKey2.toByteArray()), getKey());
77+
public Boolean verify(byte[] message, byte[] signature) {
78+
79+
//We need the Public key's short key
80+
byte[] keyToFind = (getKey().length > 33) ? getShortKey(getKey()) : getKey();
81+
82+
//Looping possible v's of the signature
83+
for (int i = 27; i <= 34; i++) {
84+
85+
final Sign.SignatureData signatureData =
86+
new Sign.SignatureData(
87+
(byte) (i),
88+
Arrays.copyOfRange(signature, 0, 32),
89+
Arrays.copyOfRange(signature, 32, 64));
90+
91+
//iterate the recovery id's
92+
for (int j = 0; j < 4; j++) {
93+
94+
final ECDSASignature ecdsaSignature = new ECDSASignature(new BigInteger(1, signatureData.getR()),
95+
new BigInteger(1, signatureData.getS()));
96+
final BigInteger recoveredKey = Sign.recoverFromSignature((byte) j, ecdsaSignature, Hash.sha256(message));
97+
98+
if (recoveredKey != null) {
99+
100+
final byte[] keyFromSignature = getShortKey(recoveredKey.toByteArray());
101+
102+
if (Arrays.equals(keyFromSignature, keyToFind)) {
103+
return true;
104+
}
105+
}
106+
}
107+
108+
}
109+
110+
return false;
111+
80112
}
81113

82114
/**

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

Lines changed: 89 additions & 3 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;
@@ -50,7 +49,7 @@ void writePublicKey_should_equal_source_file() throws URISyntaxException, IOExce
5049
}
5150

5251
@Test
53-
void verify_should_be_ok() throws URISyntaxException, IOException, GeneralSecurityException {
52+
void verify_should_be_ok() throws URISyntaxException, IOException {
5453
String hexSignature = "ea5b38fd0db5fb3d871c47fde1fa4c4db75d1a9e1c0ac54d826e178ee0e63707176b4e63b4f838bd031f007fffd6a4f71d920a10c48ea53dd1573fa2b58a829e";
5554

5655
Secp256k1PublicKey pubKey = new Secp256k1PublicKey();
@@ -60,4 +59,91 @@ void verify_should_be_ok() throws URISyntaxException, IOException, GeneralSecuri
6059

6160
assertTrue(pubKey.verify("Test message".getBytes(), Hex.decode(hexSignature)));
6261
}
63-
}
62+
63+
@Test
64+
void signAndRecoverPublicKey_1() throws URISyntaxException, IOException {
65+
66+
//Get the private key
67+
Secp256k1PrivateKey privKey = new Secp256k1PrivateKey();
68+
String filePath = getResourcesKeyPath("secp256k1/secret_key.pem");
69+
privKey.readPrivateKey(filePath);
70+
71+
//Derive the public key
72+
Secp256k1PublicKey publicKey = (Secp256k1PublicKey) privKey.derivePublicKey();
73+
74+
String message = "bc81ca4de9b3a991a6514eddf0e994e0035c7ba58f333c4d7ba5dd18b4c9c547";
75+
76+
//Generate the signature
77+
byte[] signature = privKey.sign(message.getBytes());
78+
79+
//Test
80+
assert publicKey.verify(message.getBytes(), signature);
81+
82+
}
83+
84+
@Test
85+
void signAndRecoverPublicKey_2() throws URISyntaxException, IOException {
86+
87+
//Get the private key
88+
Secp256k1PrivateKey privKey = new Secp256k1PrivateKey();
89+
90+
String filePath = getResourcesKeyPath("secp256k1/secret_key.pem");
91+
privKey.readPrivateKey(filePath);
92+
93+
//Derive the public key
94+
Secp256k1PublicKey publicKey = (Secp256k1PublicKey) privKey.derivePublicKey();
95+
96+
String message = "1df13c9aaa8217657b7e5ec2442594735eeb4ca7e764877b3d2b593c3909d15f";
97+
98+
//Generate the signature
99+
byte[] signature = privKey.sign(message.getBytes());
100+
101+
//Test
102+
assert publicKey.verify(message.getBytes(), signature);
103+
104+
}
105+
106+
@Test
107+
void signAndRecoverPublicKey_3() throws URISyntaxException, IOException {
108+
109+
//Get the private key
110+
Secp256k1PrivateKey privKey = new Secp256k1PrivateKey();
111+
String filePath = getResourcesKeyPath("secp256k1/secret_key.pem");
112+
privKey.readPrivateKey(filePath);
113+
114+
//Derive the public key
115+
Secp256k1PublicKey publicKey = (Secp256k1PublicKey) privKey.derivePublicKey();
116+
117+
String message = "Test message";
118+
119+
//Generate the signature
120+
byte[] signature = privKey.sign(message.getBytes());
121+
122+
//Test
123+
assert publicKey.verify(message.getBytes(), signature);
124+
125+
}
126+
127+
@Test
128+
void signAndRecoverPublicKey_4() throws URISyntaxException, IOException {
129+
130+
//Get the private key
131+
Secp256k1PrivateKey privKey = new Secp256k1PrivateKey();
132+
String filePath = getResourcesKeyPath("secp256k1/secret_key.pem");
133+
privKey.readPrivateKey(filePath);
134+
135+
//Derive the public key
136+
Secp256k1PublicKey publicKey = (Secp256k1PublicKey) privKey.derivePublicKey();
137+
138+
String message = "Test message";
139+
140+
//Generate the signature
141+
byte[] signature = privKey.sign(message.getBytes());
142+
143+
//Test
144+
assert publicKey.verify(message.getBytes(), signature);
145+
assert !publicKey.verify("Not test message".getBytes(), signature);
146+
147+
}
148+
149+
}

0 commit comments

Comments
 (0)