Skip to content

Commit

Permalink
Merge pull request #297 from /issues/293
Browse files Browse the repository at this point in the history
Issues/293
  • Loading branch information
meywood authored Jun 13, 2024
2 parents cf70d59 + a8df826 commit 3223936
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 21 deletions.
2 changes: 1 addition & 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.7'
version='2.5.8'
sourceCompatibility = 1.8
targetCompatibility = 1.8

Expand Down
66 changes: 49 additions & 17 deletions src/main/java/com/syntifi/crypto/key/Secp256k1PublicKey.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.bouncycastle.asn1.*;
import org.web3j.crypto.ECDSASignature;
import org.web3j.crypto.Hash;
import org.web3j.crypto.Sign;
import org.web3j.crypto.Sign.SignatureData;

import java.io.*;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.util.Arrays;

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

/**
* Iterates possible signature combinations and possible recovery id's
* Casper does not use signature.v so we have to iterate v
* We don't know v so we have to iterate the possible recover id's
* Converts to short public key for comparison
*
* @param message the signed message
* @param signature the signature to check against
* @return true|false public key found
*/
@Override
public Boolean verify(final byte[] message, final byte[] signature) throws GeneralSecurityException {
//TODO: Double check the issue the getV(), for now we are trying with both (27 and 28)
final SignatureData signatureData1 = new SignatureData(
(byte) 27,
Arrays.copyOfRange(signature, 0, 32),
Arrays.copyOfRange(signature, 32, 64));
final BigInteger derivedKey1 = Sign.signedMessageHashToKey(Hash.sha256(message), signatureData1);
final SignatureData signatureData2 = new SignatureData(
(byte) 28,
Arrays.copyOfRange(signature, 0, 32),
Arrays.copyOfRange(signature, 32, 64));
final BigInteger derivedKey2 = Sign.signedMessageHashToKey(Hash.sha256(message), signatureData2);
return Arrays.equals(Secp256k1PublicKey.getShortKey(derivedKey1.toByteArray()), getKey()) ||
Arrays.equals(Secp256k1PublicKey.getShortKey(derivedKey2.toByteArray()), getKey());
public Boolean verify(byte[] message, byte[] signature) {

//We need the Public key's short key
byte[] keyToFind = (getKey().length > 33) ? getShortKey(getKey()) : getKey();

//Looping possible v's of the signature
for (int i = 27; i <= 34; i++) {

final Sign.SignatureData signatureData =
new Sign.SignatureData(
(byte) (i),
Arrays.copyOfRange(signature, 0, 32),
Arrays.copyOfRange(signature, 32, 64));

//iterate the recovery id's
for (int j = 0; j < 4; j++) {

final ECDSASignature ecdsaSignature = new ECDSASignature(new BigInteger(1, signatureData.getR()),
new BigInteger(1, signatureData.getS()));
final BigInteger recoveredKey = Sign.recoverFromSignature((byte) j, ecdsaSignature, Hash.sha256(message));

if (recoveredKey != null) {

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

if (Arrays.equals(keyFromSignature, keyToFind)) {
return true;
}
}
}

}

return false;

}

/**
Expand Down
92 changes: 89 additions & 3 deletions src/test/java/com/syntifi/crypto/key/Secp256k1PublicKeyTests.java
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 @@ -50,7 +49,7 @@ void writePublicKey_should_equal_source_file() throws URISyntaxException, IOExce
}

@Test
void verify_should_be_ok() throws URISyntaxException, IOException, GeneralSecurityException {
void verify_should_be_ok() throws URISyntaxException, IOException {
String hexSignature = "ea5b38fd0db5fb3d871c47fde1fa4c4db75d1a9e1c0ac54d826e178ee0e63707176b4e63b4f838bd031f007fffd6a4f71d920a10c48ea53dd1573fa2b58a829e";

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

assertTrue(pubKey.verify("Test message".getBytes(), Hex.decode(hexSignature)));
}
}

@Test
void signAndRecoverPublicKey_1() throws URISyntaxException, IOException {

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

//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_2() throws URISyntaxException, IOException {

//Get the private key
Secp256k1PrivateKey privKey = new Secp256k1PrivateKey();

String filePath = getResourcesKeyPath("secp256k1/secret_key.pem");
privKey.readPrivateKey(filePath);

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

String message = "1df13c9aaa8217657b7e5ec2442594735eeb4ca7e764877b3d2b593c3909d15f";

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

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

}

@Test
void signAndRecoverPublicKey_3() throws URISyntaxException, IOException {

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

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

String message = "Test message";

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

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

}

@Test
void signAndRecoverPublicKey_4() throws URISyntaxException, IOException {

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

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

String message = "Test message";

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

//Test
assert publicKey.verify(message.getBytes(), signature);
assert !publicKey.verify("Not test message".getBytes(), signature);

}

}

0 comments on commit 3223936

Please sign in to comment.