From ec5b64a0944d0a00c2f4826619fd07e1d3453e0c Mon Sep 17 00:00:00 2001 From: Carl Norburn Date: Wed, 26 Jun 2024 12:38:36 +0100 Subject: [PATCH 1/2] Fixed 65bit public key issue --- .../crypto/key/Secp256k1PublicKey.java | 23 +++++- .../crypto/key/Secp256k1PublicKeyTests.java | 72 +++++++++++++++++++ 2 files changed, 92 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/syntifi/crypto/key/Secp256k1PublicKey.java b/src/main/java/com/syntifi/crypto/key/Secp256k1PublicKey.java index b9960900..2d43ed39 100644 --- a/src/main/java/com/syntifi/crypto/key/Secp256k1PublicKey.java +++ b/src/main/java/com/syntifi/crypto/key/Secp256k1PublicKey.java @@ -1,5 +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; @@ -77,7 +78,7 @@ public void writePublicKey(final Writer writer) throws IOException { public Boolean verify(byte[] message, byte[] signature) { //We need the Public key's short key - byte[] keyToFind = (getKey().length > 33) ? getShortKey(getKey()) : getKey(); + byte[] keyToFind = (getKey().length > AlgorithmTag.SECP256K1.getLength()) ? getShortKey(getKey()) : getKey(); //Looping possible v's of the signature for (int i = 27; i <= 34; i++) { @@ -97,7 +98,7 @@ public Boolean verify(byte[] message, byte[] signature) { if (recoveredKey != null) { - final byte[] keyFromSignature = getShortKey(recoveredKey.toByteArray()); + final byte[] keyFromSignature = getRecoveredShortKey(recoveredKey.toByteArray()); if (Arrays.equals(keyFromSignature, keyToFind)) { return true; @@ -120,7 +121,23 @@ public Boolean verify(byte[] message, byte[] signature) { 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, 32); + 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 + * + * @param key the key as a byte array + * @return short key as byte array + */ + public static byte[] getRecoveredShortKey(final byte[] key){ + return (key[0] == (byte) 0) + ? getShortKey(Arrays.copyOfRange(key, 1, (key.length - 1))) + : getShortKey(key); + } + } diff --git a/src/test/java/com/syntifi/crypto/key/Secp256k1PublicKeyTests.java b/src/test/java/com/syntifi/crypto/key/Secp256k1PublicKeyTests.java index 1d0f0fc1..a9b1a59f 100644 --- a/src/test/java/com/syntifi/crypto/key/Secp256k1PublicKeyTests.java +++ b/src/test/java/com/syntifi/crypto/key/Secp256k1PublicKeyTests.java @@ -1,5 +1,6 @@ package com.syntifi.crypto.key; +import com.casper.sdk.model.key.PublicKey; import com.syntifi.crypto.key.encdec.Hex; import org.junit.jupiter.api.Test; import org.slf4j.Logger; @@ -8,6 +9,7 @@ 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; @@ -146,4 +148,74 @@ void signAndRecoverPublicKey_4() throws URISyntaxException, IOException { } + @Test + void signAndRecoverPublicKey_5() throws GeneralSecurityException { + + String publicKey = "02027fec2d969dd0779358c40abe2a772f309408348c2a1f413fddfe684a4287ba1a"; + String signature = "2f26d3a896e5ad74bafc381455fc9bec808cea64b083b4a05e3dae43d80478820f59128c97bb50b52d5eba90eb29ec8dbd1b39fe6488bbc8c44a799e9a631878"; + String hash = "92c983f3bb205b4f642f532dff837ebed56b19f11351da9181f6512e75a54a9b"; + + PublicKey key = new PublicKey(); + key.createPublicKey(publicKey); + + assert key.getPubKey().verify(Hex.decode(hash), Hex.decode(signature)); + + } + + @Test + void signAndRecoverPublicKey_6() throws GeneralSecurityException { + + String publicKey = "02024c5e3ba7b1da49cda950319aec914cd3c720fbec3dcf25aa4add631e28f70aa9"; + String signature = "ade1d9902ec5520e0f52052b329cc62620ac5e84294729c559ce979fe0cf22aa29a8cef7a9d985851da01f68b4049ad9db3d310440cafcb3b5fe40ed6040aa1e"; + String hash = "57a2fc930ab83f6bf5b3f1d7d5d1bd05ccd20dcbe3135f7c21a0553650d220e6"; + + PublicKey key = new PublicKey(); + key.createPublicKey(publicKey); + + assert key.getPubKey().verify(Hex.decode(hash), Hex.decode(signature)); + + } + + @Test + void signAndRecoverPublicKey_7() throws GeneralSecurityException { + + String publicKey = "0203b34ffdb855f8654f7f3805257844fe08d4055bcf75bbbb19b78463e34ba8b932"; + String signature = "29d7ea92f1193c13c7ab5d90dfdfab75f52fd49047b9bb4fafc9962a7a1e0d2a36828d93d0d95080f53c625a2f2111dd86c37391f9a05a7ea5a1f2abba579476"; + String hash = "3fa94f85c55a44a8afc2944914eb38720cc85d4b130809f8409a8c7e3e677298"; + + PublicKey key = new PublicKey(); + key.createPublicKey(publicKey); + + assert key.getPubKey().verify(Hex.decode(hash), Hex.decode(signature)); + + } + + @Test + void signAndRecoverPublicKey_8() throws GeneralSecurityException { + + String publicKey = "020287a38f11ac7aab689205912b981c21b80cf1efdd7541bd09cbe1e6ad6e8f188c"; + String signature = "d1a796f1221e2256dbe85904e2c3d07be52c1482f8b3cfdce0bef0b6d4c5fc5537e3d8720054888eba9ddcb4c6ff388fabb5c09143000980e6af9096ee163524"; + String hash = "1df13c9aaa8217657b7e5ec2442594735eeb4ca7e764877b3d2b593c3909d15f"; + + PublicKey key = new PublicKey(); + key.createPublicKey(publicKey); + + assert key.getPubKey().verify(Hex.decode(hash), Hex.decode(signature)); + + } + + @Test + void signAndRecoverPublicKey_9() throws GeneralSecurityException { + String publicKey = "0202ea6c7f7f281078af87e79a3856d500da39d35743d8253c9bc1fce7b1da0a3536"; + String signature = "c808c81f8db9a3a7364b0d884968c74427ace3fbd66cef9a8f0235e570b2774407071b63094446992e37c8f0e7d154bacd882d583fa2bfb043c884e445da56aa"; + String hash = "e96cb427900710e89e39f3a69b8f576c8b5cb3085702f3c33c46fcbb115f1775"; + + PublicKey key = new PublicKey(); + key.createPublicKey(publicKey); + + assert key.getPubKey().verify(Hex.decode(hash), Hex.decode(signature)); + + } + + } From 8726ebcdd01edb513f39cd26c8c1687c1473771b Mon Sep 17 00:00:00 2001 From: Carl Norburn Date: Wed, 26 Jun 2024 12:47:02 +0100 Subject: [PATCH 2/2] PR changes --- .../java/com/syntifi/crypto/key/Secp256k1PublicKey.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/syntifi/crypto/key/Secp256k1PublicKey.java b/src/main/java/com/syntifi/crypto/key/Secp256k1PublicKey.java index 2d43ed39..f63120c7 100644 --- a/src/main/java/com/syntifi/crypto/key/Secp256k1PublicKey.java +++ b/src/main/java/com/syntifi/crypto/key/Secp256k1PublicKey.java @@ -135,9 +135,11 @@ public static byte[] getShortKey(final byte[] key) { * @return short key as byte array */ public static byte[] getRecoveredShortKey(final byte[] key){ - return (key[0] == (byte) 0) - ? getShortKey(Arrays.copyOfRange(key, 1, (key.length - 1))) - : getShortKey(key); + if (key[0] == (byte) 0) { + return getShortKey(Arrays.copyOfRange(key, 1, (key.length - 1))); + } else { + return getShortKey(key); + } } }