Skip to content

Commit f7a6748

Browse files
authored
fix(crypto): resolve the invalid witness (#6368)
* feat(crypto): upgrade the bouncycastle denpendency * fix(crypto): optimize the null localwitness * fix(metric): process the null witness address * feat(crypto): optimize the code * feat(doc): add the comment * feat(test): add the nullWitnessAddress test * add witness config test * strengthen the private key validation * increase the test coverage * optimiz the witnessAddress initialization * simplify the localWitness * refactor the code * add the test unit
1 parent 00d6b77 commit f7a6748

File tree

22 files changed

+658
-131
lines changed

22 files changed

+658
-131
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ subprojects {
8484
implementation group: 'org.apache.commons', name: 'commons-math', version: '2.2'
8585
implementation "org.apache.commons:commons-collections4:4.1"
8686
implementation group: 'joda-time', name: 'joda-time', version: '2.3'
87-
implementation group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.69'
87+
implementation group: 'org.bouncycastle', name: 'bcprov-jdk18on', version: '1.79'
8888

8989
compileOnly 'org.projectlombok:lombok:1.18.34'
9090
annotationProcessor 'org.projectlombok:lombok:1.18.34'

chainbase/src/main/java/org/tron/common/utils/LocalWitnesses.java

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public class LocalWitnesses {
3232
@Getter
3333
private List<String> privateKeys = Lists.newArrayList();
3434

35+
@Getter
3536
private byte[] witnessAccountAddress;
3637

3738
public LocalWitnesses() {
@@ -45,21 +46,11 @@ public LocalWitnesses(List<String> privateKeys) {
4546
setPrivateKeys(privateKeys);
4647
}
4748

48-
public byte[] getWitnessAccountAddress(boolean isECKeyCryptoEngine) {
49-
if (witnessAccountAddress == null) {
50-
byte[] privateKey = ByteArray.fromHexString(getPrivateKey());
51-
final SignInterface cryptoEngine = SignUtils.fromPrivate(privateKey, isECKeyCryptoEngine);
52-
this.witnessAccountAddress = cryptoEngine.getAddress();
53-
}
54-
return witnessAccountAddress;
55-
}
56-
57-
public void setWitnessAccountAddress(final byte[] localWitnessAccountAddress) {
58-
this.witnessAccountAddress = localWitnessAccountAddress;
59-
}
60-
61-
public void initWitnessAccountAddress(boolean isECKeyCryptoEngine) {
62-
if (witnessAccountAddress == null) {
49+
public void initWitnessAccountAddress(final byte[] witnessAddress,
50+
boolean isECKeyCryptoEngine) {
51+
if (witnessAddress != null) {
52+
this.witnessAccountAddress = witnessAddress;
53+
} else if (!CollectionUtils.isEmpty(privateKeys)) {
6354
byte[] privateKey = ByteArray.fromHexString(getPrivateKey());
6455
final SignInterface ecKey = SignUtils.fromPrivate(privateKey,
6556
isECKeyCryptoEngine);
@@ -85,11 +76,15 @@ private void validate(String privateKey) {
8576
privateKey = privateKey.substring(2);
8677
}
8778

88-
if (StringUtils.isNotBlank(privateKey)
89-
&& privateKey.length() != ChainConstant.PRIVATE_KEY_LENGTH) {
79+
if (StringUtils.isBlank(privateKey)
80+
|| privateKey.length() != ChainConstant.PRIVATE_KEY_LENGTH) {
9081
throw new IllegalArgumentException(
91-
String.format("private key must be %d-bits hex string, actual: %d",
92-
ChainConstant.PRIVATE_KEY_LENGTH, privateKey.length()));
82+
String.format("private key must be %d hex string, actual: %d",
83+
ChainConstant.PRIVATE_KEY_LENGTH,
84+
StringUtils.isBlank(privateKey) ? 0 : privateKey.length()));
85+
}
86+
if (!StringUtil.isHexadecimal(privateKey)) {
87+
throw new IllegalArgumentException("private key must be hex string");
9388
}
9489
}
9590

common/src/main/java/org/tron/common/utils/StringUtil.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,21 @@ public static String createReadableString(ByteString string) {
4444
public static ByteString hexString2ByteString(String hexString) {
4545
return ByteString.copyFrom(ByteArray.fromHexString(hexString));
4646
}
47+
48+
public static boolean isHexadecimal(String str) {
49+
if (str == null || str.isEmpty()) {
50+
return false;
51+
}
52+
if (str.length() % 2 != 0) {
53+
return false;
54+
}
55+
56+
for (int i = 0; i < str.length(); i++) {
57+
char c = str.charAt(i);
58+
if (Character.digit(c, 16) == -1) {
59+
return false;
60+
}
61+
}
62+
return true;
63+
}
4764
}

crypto/src/main/java/org/tron/common/crypto/ECKey.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import org.tron.common.crypto.jce.ECKeyPairGenerator;
5656
import org.tron.common.crypto.jce.TronCastleProvider;
5757
import org.tron.common.utils.BIUtil;
58+
import org.tron.common.utils.ByteArray;
5859
import org.tron.common.utils.ByteUtil;
5960

6061
@Slf4j(topic = "crypto")
@@ -285,7 +286,7 @@ public static ECKey fromPrivate(BigInteger privKey) {
285286
* @return -
286287
*/
287288
public static ECKey fromPrivate(byte[] privKeyBytes) {
288-
if (Objects.isNull(privKeyBytes)) {
289+
if (ByteArray.isEmpty(privKeyBytes)) {
289290
return null;
290291
}
291292
return fromPrivate(new BigInteger(1, privKeyBytes));

crypto/src/main/java/org/tron/common/crypto/sm2/SM2.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import org.tron.common.crypto.SignatureInterface;
4242
import org.tron.common.crypto.jce.ECKeyFactory;
4343
import org.tron.common.crypto.jce.TronCastleProvider;
44+
import org.tron.common.utils.ByteArray;
4445
import org.tron.common.utils.ByteUtil;
4546

4647
/**
@@ -247,7 +248,7 @@ public static SM2 fromPrivate(BigInteger privKey) {
247248
* @return -
248249
*/
249250
public static SM2 fromPrivate(byte[] privKeyBytes) {
250-
if (Objects.isNull(privKeyBytes)) {
251+
if (ByteArray.isEmpty(privKeyBytes)) {
251252
return null;
252253
}
253254
return fromPrivate(new BigInteger(1, privKeyBytes));

framework/src/main/java/org/tron/core/config/args/Args.java

Lines changed: 1 addition & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@
5353
import org.tron.common.args.Witness;
5454
import org.tron.common.config.DbBackupConfig;
5555
import org.tron.common.cron.CronExpression;
56-
import org.tron.common.crypto.SignInterface;
5756
import org.tron.common.logsfilter.EventPluginConfig;
5857
import org.tron.common.logsfilter.FilterQuery;
5958
import org.tron.common.logsfilter.TriggerConfig;
@@ -62,19 +61,15 @@
6261
import org.tron.common.parameter.CommonParameter;
6362
import org.tron.common.parameter.RateLimiterInitialization;
6463
import org.tron.common.setting.RocksDbSettings;
65-
import org.tron.common.utils.ByteArray;
6664
import org.tron.common.utils.Commons;
6765
import org.tron.common.utils.LocalWitnesses;
6866
import org.tron.core.Constant;
6967
import org.tron.core.Wallet;
7068
import org.tron.core.config.Configuration;
7169
import org.tron.core.config.Parameter.NetConstants;
7270
import org.tron.core.config.Parameter.NodeConstant;
73-
import org.tron.core.exception.CipherException;
7471
import org.tron.core.exception.TronError;
7572
import org.tron.core.store.AccountStore;
76-
import org.tron.keystore.Credentials;
77-
import org.tron.keystore.WalletUtils;
7873
import org.tron.p2p.P2pConfig;
7974
import org.tron.p2p.dns.update.DnsType;
8075
import org.tron.p2p.dns.update.PublishConfig;
@@ -412,61 +407,7 @@ public static void setParam(final Config config) {
412407
PARAMETER.cryptoEngine = config.hasPath(Constant.CRYPTO_ENGINE) ? config
413408
.getString(Constant.CRYPTO_ENGINE) : Constant.ECKey_ENGINE;
414409

415-
if (StringUtils.isNoneBlank(PARAMETER.privateKey)) {
416-
localWitnesses = (new LocalWitnesses(PARAMETER.privateKey));
417-
if (StringUtils.isNoneBlank(PARAMETER.witnessAddress)) {
418-
byte[] bytes = Commons.decodeFromBase58Check(PARAMETER.witnessAddress);
419-
if (bytes != null) {
420-
localWitnesses.setWitnessAccountAddress(bytes);
421-
logger.debug("Got localWitnessAccountAddress from cmd");
422-
} else {
423-
PARAMETER.witnessAddress = "";
424-
logger.warn(IGNORE_WRONG_WITNESS_ADDRESS_FORMAT);
425-
}
426-
}
427-
localWitnesses.initWitnessAccountAddress(PARAMETER.isECKeyCryptoEngine());
428-
logger.debug("Got privateKey from cmd");
429-
} else if (config.hasPath(Constant.LOCAL_WITNESS)) {
430-
localWitnesses = new LocalWitnesses();
431-
List<String> localwitness = config.getStringList(Constant.LOCAL_WITNESS);
432-
localWitnesses.setPrivateKeys(localwitness);
433-
witnessAddressCheck(config);
434-
localWitnesses.initWitnessAccountAddress(PARAMETER.isECKeyCryptoEngine());
435-
logger.debug("Got privateKey from config.conf");
436-
} else if (config.hasPath(Constant.LOCAL_WITNESS_KEYSTORE)) {
437-
localWitnesses = new LocalWitnesses();
438-
List<String> privateKeys = new ArrayList<String>();
439-
if (PARAMETER.isWitness()) {
440-
List<String> localwitness = config.getStringList(Constant.LOCAL_WITNESS_KEYSTORE);
441-
if (localwitness.size() > 0) {
442-
String fileName = System.getProperty("user.dir") + "/" + localwitness.get(0);
443-
String password;
444-
if (StringUtils.isEmpty(PARAMETER.password)) {
445-
System.out.println("Please input your password.");
446-
password = WalletUtils.inputPassword();
447-
} else {
448-
password = PARAMETER.password;
449-
PARAMETER.password = null;
450-
}
451-
452-
try {
453-
Credentials credentials = WalletUtils
454-
.loadCredentials(password, new File(fileName));
455-
SignInterface sign = credentials.getSignInterface();
456-
String prikey = ByteArray.toHexString(sign.getPrivateKey());
457-
privateKeys.add(prikey);
458-
} catch (IOException | CipherException e) {
459-
logger.error("Witness node start failed!");
460-
throw new TronError(e, TronError.ErrCode.WITNESS_KEYSTORE_LOAD);
461-
}
462-
}
463-
}
464-
localWitnesses.setPrivateKeys(privateKeys);
465-
witnessAddressCheck(config);
466-
localWitnesses.initWitnessAccountAddress(PARAMETER.isECKeyCryptoEngine());
467-
logger.debug("Got privateKey from keystore");
468-
}
469-
410+
localWitnesses = new WitnessInitializer(config).initLocalWitnesses();
470411
if (PARAMETER.isWitness()
471412
&& CollectionUtils.isEmpty(localWitnesses.getPrivateKeys())) {
472413
throw new TronError("This is a witness node, but localWitnesses is null",
@@ -1817,19 +1758,6 @@ public static void logConfig() {
18171758
logger.info("\n");
18181759
}
18191760

1820-
private static void witnessAddressCheck(Config config) {
1821-
if (config.hasPath(Constant.LOCAL_WITNESS_ACCOUNT_ADDRESS)) {
1822-
byte[] bytes = Commons
1823-
.decodeFromBase58Check(config.getString(Constant.LOCAL_WITNESS_ACCOUNT_ADDRESS));
1824-
if (bytes != null) {
1825-
localWitnesses.setWitnessAccountAddress(bytes);
1826-
logger.debug("Got localWitnessAccountAddress from config.conf");
1827-
} else {
1828-
logger.warn(IGNORE_WRONG_WITNESS_ADDRESS_FORMAT);
1829-
}
1830-
}
1831-
}
1832-
18331761
/**
18341762
* get output directory.
18351763
*/
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
package org.tron.core.config.args;
2+
3+
import com.typesafe.config.Config;
4+
import java.io.File;
5+
import java.io.IOException;
6+
import java.util.ArrayList;
7+
import java.util.List;
8+
import lombok.extern.slf4j.Slf4j;
9+
import org.apache.commons.lang3.StringUtils;
10+
import org.tron.common.crypto.SignInterface;
11+
import org.tron.common.utils.ByteArray;
12+
import org.tron.common.utils.Commons;
13+
import org.tron.common.utils.LocalWitnesses;
14+
import org.tron.core.Constant;
15+
import org.tron.core.exception.CipherException;
16+
import org.tron.core.exception.TronError;
17+
import org.tron.keystore.Credentials;
18+
import org.tron.keystore.WalletUtils;
19+
20+
@Slf4j
21+
public class WitnessInitializer {
22+
23+
private final Config config;
24+
25+
private LocalWitnesses localWitnesses;
26+
27+
public WitnessInitializer(Config config) {
28+
this.config = config;
29+
this.localWitnesses = new LocalWitnesses();
30+
}
31+
32+
public LocalWitnesses initLocalWitnesses() {
33+
if (!Args.PARAMETER.isWitness()) {
34+
return localWitnesses;
35+
}
36+
37+
if (tryInitFromCommandLine()) {
38+
return localWitnesses;
39+
}
40+
41+
if (tryInitFromConfig()) {
42+
return localWitnesses;
43+
}
44+
45+
tryInitFromKeystore();
46+
47+
return localWitnesses;
48+
}
49+
50+
private boolean tryInitFromCommandLine() {
51+
if (StringUtils.isBlank(Args.PARAMETER.privateKey)) {
52+
return false;
53+
}
54+
55+
byte[] witnessAddress = null;
56+
this.localWitnesses = new LocalWitnesses(Args.PARAMETER.privateKey);
57+
if (StringUtils.isNotEmpty(Args.PARAMETER.witnessAddress)) {
58+
witnessAddress = Commons.decodeFromBase58Check(Args.PARAMETER.witnessAddress);
59+
if (witnessAddress == null) {
60+
throw new TronError("LocalWitnessAccountAddress format from cmd is incorrect",
61+
TronError.ErrCode.WITNESS_INIT);
62+
}
63+
logger.debug("Got localWitnessAccountAddress from cmd");
64+
}
65+
66+
this.localWitnesses.initWitnessAccountAddress(witnessAddress,
67+
Args.PARAMETER.isECKeyCryptoEngine());
68+
logger.debug("Got privateKey from cmd");
69+
return true;
70+
}
71+
72+
private boolean tryInitFromConfig() {
73+
if (!config.hasPath(Constant.LOCAL_WITNESS) || config.getStringList(Constant.LOCAL_WITNESS)
74+
.isEmpty()) {
75+
return false;
76+
}
77+
78+
List<String> localWitness = config.getStringList(Constant.LOCAL_WITNESS);
79+
this.localWitnesses.setPrivateKeys(localWitness);
80+
logger.debug("Got privateKey from config.conf");
81+
byte[] witnessAddress = getWitnessAddress();
82+
this.localWitnesses.initWitnessAccountAddress(witnessAddress,
83+
Args.PARAMETER.isECKeyCryptoEngine());
84+
return true;
85+
}
86+
87+
private void tryInitFromKeystore() {
88+
if (!config.hasPath(Constant.LOCAL_WITNESS_KEYSTORE)
89+
|| config.getStringList(Constant.LOCAL_WITNESS_KEYSTORE).isEmpty()) {
90+
return;
91+
}
92+
93+
List<String> localWitness = config.getStringList(Constant.LOCAL_WITNESS_KEYSTORE);
94+
if (localWitness.size() > 1) {
95+
logger.warn(
96+
"Multiple keystores detected. Only the first keystore will be used as witness, all "
97+
+ "others will be ignored.");
98+
}
99+
100+
List<String> privateKeys = new ArrayList<>();
101+
String fileName = System.getProperty("user.dir") + "/" + localWitness.get(0);
102+
String password;
103+
if (StringUtils.isEmpty(Args.PARAMETER.password)) {
104+
System.out.println("Please input your password.");
105+
password = WalletUtils.inputPassword();
106+
} else {
107+
password = Args.PARAMETER.password;
108+
Args.PARAMETER.password = null;
109+
}
110+
111+
try {
112+
Credentials credentials = WalletUtils
113+
.loadCredentials(password, new File(fileName));
114+
SignInterface sign = credentials.getSignInterface();
115+
String prikey = ByteArray.toHexString(sign.getPrivateKey());
116+
privateKeys.add(prikey);
117+
} catch (IOException | CipherException e) {
118+
logger.error("Witness node start failed!");
119+
throw new TronError(e, TronError.ErrCode.WITNESS_KEYSTORE_LOAD);
120+
}
121+
122+
this.localWitnesses.setPrivateKeys(privateKeys);
123+
byte[] witnessAddress = getWitnessAddress();
124+
this.localWitnesses.initWitnessAccountAddress(witnessAddress,
125+
Args.PARAMETER.isECKeyCryptoEngine());
126+
logger.debug("Got privateKey from keystore");
127+
}
128+
129+
private byte[] getWitnessAddress() {
130+
if (!config.hasPath(Constant.LOCAL_WITNESS_ACCOUNT_ADDRESS)) {
131+
return null;
132+
}
133+
134+
if (localWitnesses.getPrivateKeys().size() != 1) {
135+
throw new TronError(
136+
"LocalWitnessAccountAddress can only be set when there is only one private key",
137+
TronError.ErrCode.WITNESS_INIT);
138+
}
139+
byte[] witnessAddress = Commons
140+
.decodeFromBase58Check(config.getString(Constant.LOCAL_WITNESS_ACCOUNT_ADDRESS));
141+
if (witnessAddress != null) {
142+
logger.debug("Got localWitnessAccountAddress from config.conf");
143+
} else {
144+
throw new TronError("LocalWitnessAccountAddress format from config is incorrect",
145+
TronError.ErrCode.WITNESS_INIT);
146+
}
147+
return witnessAddress;
148+
}
149+
}

0 commit comments

Comments
 (0)