Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issues/293 #297

Merged
merged 3 commits into from
Jun 13, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
70 changes: 53 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,56 @@ 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) {

boolean keyFound = false;

//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++) {
meywood marked this conversation as resolved.
Show resolved Hide resolved

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)) {
keyFound = true;
break;
}
}
}

if (keyFound) break;
}

return keyFound;

}

/**
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);

}

}
Loading