Skip to content

Commit 636667a

Browse files
authored
feat(plugins): migrate keystore CLI from FullNode to Toolkit (#6637)
1 parent c977f82 commit 636667a

32 files changed

Lines changed: 5145 additions & 204 deletions

framework/src/main/java/org/tron/keystore/Credentials.java renamed to crypto/src/main/java/org/tron/keystore/Credentials.java

File renamed without changes.

framework/src/main/java/org/tron/keystore/Wallet.java renamed to crypto/src/main/java/org/tron/keystore/Wallet.java

Lines changed: 66 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import org.tron.common.crypto.SignUtils;
2424
import org.tron.common.utils.ByteArray;
2525
import org.tron.common.utils.StringUtil;
26-
import org.tron.core.config.args.Args;
2726
import org.tron.core.exception.CipherException;
2827

2928
/**
@@ -48,7 +47,12 @@
4847
*/
4948
public class Wallet {
5049

51-
protected static final String AES_128_CTR = "pbkdf2";
50+
// KDF identifiers used in the Web3 Secret Storage "kdf" field.
51+
// The old name "AES_128_CTR" was misleading — the value is the PBKDF2 KDF
52+
// identifier, not the cipher (CIPHER below). The inner class name
53+
// `WalletFile.Aes128CtrKdfParams` is kept for wire-format/Jackson-subtype
54+
// backward compatibility even though it also reflects the same history.
55+
protected static final String PBKDF2 = "pbkdf2";
5256
protected static final String SCRYPT = "scrypt";
5357
private static final int N_LIGHT = 1 << 12;
5458
private static final int P_LIGHT = 6;
@@ -168,8 +172,8 @@ private static byte[] generateMac(byte[] derivedKey, byte[] cipherText) {
168172
return Hash.sha3(result);
169173
}
170174

171-
public static SignInterface decrypt(String password, WalletFile walletFile)
172-
throws CipherException {
175+
public static SignInterface decrypt(String password, WalletFile walletFile,
176+
boolean ecKey) throws CipherException {
173177

174178
validate(walletFile);
175179

@@ -205,32 +209,79 @@ public static SignInterface decrypt(String password, WalletFile walletFile)
205209

206210
byte[] derivedMac = generateMac(derivedKey, cipherText);
207211

208-
if (!Arrays.equals(derivedMac, mac)) {
212+
if (!java.security.MessageDigest.isEqual(derivedMac, mac)) {
209213
throw new CipherException("Invalid password provided");
210214
}
211215

212216
byte[] encryptKey = Arrays.copyOfRange(derivedKey, 0, 16);
213217
byte[] privateKey = performCipherOperation(Cipher.DECRYPT_MODE, iv, encryptKey, cipherText);
214218

215-
return SignUtils.fromPrivate(privateKey, Args.getInstance().isECKeyCryptoEngine());
216-
}
219+
SignInterface keyPair = SignUtils.fromPrivate(privateKey, ecKey);
220+
221+
// Enforce address consistency: if the keystore declares an address, it MUST match
222+
// the address derived from the decrypted private key. Prevents address spoofing
223+
// where a crafted keystore displays one address but encrypts a different key.
224+
String declared = walletFile.getAddress();
225+
if (declared != null && !declared.isEmpty()) {
226+
String derived = StringUtil.encode58Check(keyPair.getAddress());
227+
if (!declared.equals(derived)) {
228+
throw new CipherException(
229+
"Keystore address mismatch: file declares " + declared
230+
+ " but private key derives " + derived);
231+
}
232+
}
217233

218-
static void validate(WalletFile walletFile) throws CipherException {
219-
WalletFile.Crypto crypto = walletFile.getCrypto();
234+
return keyPair;
235+
}
220236

237+
/**
238+
* Returns a description of the first schema violation found in
239+
* {@code walletFile}, or {@code null} if the file matches the supported
240+
* V3 keystore shape (current version, known cipher, known KDF).
241+
*
242+
* <p>Shared by {@link #validate(WalletFile)} (which throws the message)
243+
* and {@link #isValidKeystoreFile(WalletFile)} (which returns boolean
244+
* for discovery-style filtering).
245+
*/
246+
private static String validationError(WalletFile walletFile) {
221247
if (walletFile.getVersion() != CURRENT_VERSION) {
222-
throw new CipherException("Wallet version is not supported");
248+
return "Wallet version is not supported";
223249
}
224-
225-
if (!crypto.getCipher().equals(CIPHER)) {
226-
throw new CipherException("Wallet cipher is not supported");
250+
WalletFile.Crypto crypto = walletFile.getCrypto();
251+
if (crypto == null) {
252+
return "Missing crypto section";
253+
}
254+
String cipher = crypto.getCipher();
255+
if (cipher == null || !cipher.equals(CIPHER)) {
256+
return "Wallet cipher is not supported";
227257
}
258+
String kdf = crypto.getKdf();
259+
if (kdf == null || (!kdf.equals(PBKDF2) && !kdf.equals(SCRYPT))) {
260+
return "KDF type is not supported";
261+
}
262+
return null;
263+
}
228264

229-
if (!crypto.getKdf().equals(AES_128_CTR) && !crypto.getKdf().equals(SCRYPT)) {
230-
throw new CipherException("KDF type is not supported");
265+
static void validate(WalletFile walletFile) throws CipherException {
266+
String error = validationError(walletFile);
267+
if (error != null) {
268+
throw new CipherException(error);
231269
}
232270
}
233271

272+
/**
273+
* Returns {@code true} iff {@code walletFile} has the shape of a
274+
* decryptable V3 keystore: non-null address, supported version, non-null
275+
* crypto section with a supported cipher and KDF. Intended for
276+
* discovery-style filtering (e.g. listing or duplicate detection) where
277+
* we want to skip JSON stubs that would later fail {@link #validate}.
278+
*/
279+
public static boolean isValidKeystoreFile(WalletFile walletFile) {
280+
return walletFile != null
281+
&& walletFile.getAddress() != null
282+
&& validationError(walletFile) == null;
283+
}
284+
234285
public static byte[] generateRandomBytes(int size) {
235286
byte[] bytes = new byte[size];
236287
new SecureRandom().nextBytes(bytes);

framework/src/main/java/org/tron/keystore/WalletFile.java renamed to crypto/src/main/java/org/tron/keystore/WalletFile.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ public KdfParams getKdfparams() {
165165
include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
166166
property = "kdf")
167167
@JsonSubTypes({
168-
@JsonSubTypes.Type(value = Aes128CtrKdfParams.class, name = Wallet.AES_128_CTR),
168+
@JsonSubTypes.Type(value = Aes128CtrKdfParams.class, name = Wallet.PBKDF2),
169169
@JsonSubTypes.Type(value = ScryptKdfParams.class, name = Wallet.SCRYPT)
170170
})
171171
// To support my Ether Wallet keys uncomment this annotation & comment out the above

0 commit comments

Comments
 (0)