-
Notifications
You must be signed in to change notification settings - Fork 0
fix(crypto): harden ECKey and signature validation #13
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
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,10 +3,12 @@ | |
| import java.io.File; | ||
| import java.io.IOException; | ||
| import java.util.ArrayList; | ||
| import java.util.Arrays; | ||
| import java.util.List; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.apache.commons.lang3.StringUtils; | ||
| import org.tron.common.crypto.SignInterface; | ||
| import org.tron.common.crypto.SignUtils; | ||
| import org.tron.common.utils.ByteArray; | ||
| import org.tron.common.utils.Commons; | ||
| import org.tron.common.utils.LocalWitnesses; | ||
|
|
@@ -78,14 +80,27 @@ public static LocalWitnesses initFromKeystore( | |
| } | ||
|
|
||
| List<String> privateKeys = new ArrayList<>(); | ||
| byte[] privKeyBytes = null; | ||
| try { | ||
| Credentials credentials = WalletUtils.loadCredentials(pwd, new File(fileName)); | ||
| SignInterface sign = credentials.getSignInterface(); | ||
| String prikey = ByteArray.toHexString(sign.getPrivateKey()); | ||
| privateKeys.add(prikey); | ||
| privKeyBytes = sign.getPrivateKey(); | ||
| if (privKeyBytes == null | ||
| || !SignUtils.isValidPrivateKey(privKeyBytes, Args.getInstance().isECKeyCryptoEngine())) { | ||
| throw new TronError( | ||
| "Keystore contains an invalid private key", | ||
| TronError.ErrCode.WITNESS_KEYSTORE_LOAD); | ||
| } | ||
| privateKeys.add(ByteArray.toHexString(privKeyBytes)); | ||
| } catch (IOException | CipherException e) { | ||
| logger.error("Witness node start failed!"); | ||
| throw new TronError(e, TronError.ErrCode.WITNESS_KEYSTORE_LOAD); | ||
| } finally { | ||
| if (privKeyBytes != null) { | ||
| // Zeroize the raw key bytes. Note: the hex String already added to | ||
| // privateKeys is immutable and cannot be zeroed — a known JVM limitation. | ||
| Arrays.fill(privKeyBytes, (byte) 0); | ||
| } | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [SHOULD]
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed: added a comment above the |
||
| } | ||
|
|
||
| LocalWitnesses witnesses = new LocalWitnesses(); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,7 +5,7 @@ | |
| import static org.junit.Assert.assertFalse; | ||
| import static org.junit.Assert.assertNotEquals; | ||
| import static org.junit.Assert.assertNotNull; | ||
| import static org.junit.Assert.assertNull; | ||
| import static org.junit.Assert.assertThrows; | ||
| import static org.junit.Assert.assertTrue; | ||
| import static org.junit.Assert.fail; | ||
| import static org.tron.common.utils.client.utils.AbiUtil.generateOccupationConstantPrivateKey; | ||
|
|
@@ -19,12 +19,12 @@ | |
| import org.bouncycastle.util.encoders.Hex; | ||
| import org.junit.Test; | ||
| import org.tron.common.crypto.ECKey.ECDSASignature; | ||
| import org.tron.common.utils.ByteUtil; | ||
| import org.tron.core.Wallet; | ||
|
|
||
| /** | ||
| * The reason the test case uses the private key plaintext is to ensure that, | ||
| * after the ECkey tool or algorithm is upgraded, | ||
| * the upgraded differences can be verified. | ||
| * The reason the test case uses the private key plaintext is to ensure that, after the ECkey tool | ||
| * or algorithm is upgraded, the upgraded differences can be verified. | ||
| */ | ||
| @Slf4j | ||
| public class ECKeyTest { | ||
|
|
@@ -69,10 +69,49 @@ public void testFromPrivateKey() { | |
| assertTrue(key.hasPrivKey()); | ||
| assertArrayEquals(pubKey, key.getPubKey()); | ||
|
|
||
| key = ECKey.fromPrivate((byte[]) null); | ||
| assertNull(key); | ||
| key = ECKey.fromPrivate(new byte[0]); | ||
| assertNull(key); | ||
| assertThrows(IllegalArgumentException.class, () -> ECKey.fromPrivate((byte[]) null)); | ||
| assertThrows(IllegalArgumentException.class, () -> ECKey.fromPrivate(new byte[0])); | ||
| } | ||
|
|
||
| @Test | ||
| public void testFromPrivateKeyRejectsZeroValue() { | ||
| byte[] zeroPrivateKey = new byte[32]; | ||
|
|
||
| IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, | ||
| () -> ECKey.fromPrivate(zeroPrivateKey)); | ||
| assertEquals("Invalid private key.", exception.getMessage()); | ||
| } | ||
|
|
||
| @Test | ||
| public void testFromPrivateKeyRejectsCurveOrder() { | ||
| byte[] curveOrder = ByteUtil.bigIntegerToBytes(ECKey.CURVE.getN(), 32); | ||
|
|
||
| IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, | ||
| () -> ECKey.fromPrivate(curveOrder)); | ||
| assertEquals("Invalid private key.", exception.getMessage()); | ||
| } | ||
|
|
||
| @Test | ||
| public void testInvalidPublicKeyEncoding() { | ||
| byte[] invalidPublicKey = new byte[33]; | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [NIT]
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed: added |
||
| invalidPublicKey[0] = 0x02; | ||
|
|
||
| IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, | ||
| () -> new ECKey(invalidPublicKey, false)); | ||
| assertEquals("Invalid public key.", exception.getMessage()); | ||
| } | ||
|
|
||
| @Test | ||
| public void testIsValidPublicKey() { | ||
| assertFalse(ECKey.isValidPublicKey(null)); | ||
| assertFalse(ECKey.isValidPublicKey(new byte[0])); | ||
| // 0x02 prefix + zero x-coordinate: valid compressed format but no point on secp256k1 | ||
| byte[] invalidPoint = new byte[33]; | ||
| invalidPoint[0] = 0x02; | ||
| assertFalse(ECKey.isValidPublicKey(invalidPoint)); | ||
| // valid uncompressed and compressed public keys | ||
| assertTrue(ECKey.isValidPublicKey(pubKey)); | ||
| assertTrue(ECKey.isValidPublicKey(compressedPubKey)); | ||
| } | ||
|
|
||
| @Test(expected = IllegalArgumentException.class) | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.