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

feat(mnemonics):add mnemonics #775

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions Mnemonic/TMdrg9hq5u1jKoWAKNASSxyKuzc6ajQRgv.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"address":"TMdrg9hq5u1jKoWAKNASSxyKuzc6ajQRgv","id":"1b752214-16f7-45b3-bd65-d064c91f59a0","version":3,"crypto":{"cipher":"aes-128-ctr","ciphertext":"1a3cb9216008ebec745ad08ea8a62db0b71a0fcd14a3909bbf8fb0ba8715e20cc31224a8418310069d8ab1b17074c541c9797a057b1c66470ae60012a4ea35d54b95de83654bbca77c","cipherparams":{"iv":"0b3e63cb83813eedf396a0bf0a40f01f"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"e66a12cdf0132003c316a51e35866a3859b6a1fe66a47d7e7b4375546e0defb5"},"mac":"ac7e80c2d3f47860a916fee65ad8cce0650f8fa0d2deb9fff0a76c365cdfb9ec"}}
29 changes: 28 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ For more information on a specific command, just type the command on terminal wh
| [WithdrawExpireUnfreeze](#How-to-freezev2) | [CancelAllUnfreezeV2](#How-to-freezev2) |[GetDelegatedResourceV2](#How-to-freezev2) |
| [GetDelegatedResourceAccountIndexV2](#How-to-freezev2) | [GetCanDelegatedMaxSize](#How-to-freezev2) | [GetAvailableUnfreezeCount](#How-to-freezev2) |
| [GetCanWithdrawUnfreezeAmount](#How-to-freezev2) |[GetBandwidthPrices](#Get-resource-prices-and-memo-fee) | [GetEnergyPrices](#Get-resource-prices-and-memo-fee)|
| [GetMemoFee](#Get-resource-prices-and-memo-fee) |||
| [GetMemoFee](#Get-resource-prices-and-memo-fee) |[ImportWalletByMnemonic](#import-and-export-mnemonic) | [ExportWalletMnemonic](#import-and-export-mnemonic) |


Type any one of the listed commands, to display how-to tips.

Expand Down Expand Up @@ -1506,6 +1507,32 @@ wallet> getMemoFee
"prices": "0:0,1675492680000:1000000"
}
```
## import and export mnemonic
>ImportWalletByMnemonic
>Import wallet, you need to set a password, mnemonic

Example:
```console
wallet> ImportWalletByMnemonic
Please input password.
password:
Please input password again.
password:
Please enter 12 words (separated by spaces) [Attempt 1/3]:
```

>ExportWalletMnemonic
>export mnemonic of the address in the wallet

Example:
```console
wallet> ExportWalletMnemonic
Please input your password.
password:
exportWalletMnemonic successful !!
a*ert tw*st co*rect mat*er pa*s g*ther p*t p*sition s*op em*ty coc*nut aband*n
```



## Wallet related commands
Expand Down
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ dependencies {

implementation group: 'org.jline', name: 'jline', version: '3.25.0'
implementation group: 'io.github.tronprotocol', name: 'zksnark-java-sdk', version: '1.0.0'

implementation group: 'org.bitcoinj', name: 'bitcoinj-core', version: '0.16.2'
implementation group: 'org.web3j', name: 'crypto', version: '4.5.0'
}

protobuf {
Expand Down
10 changes: 9 additions & 1 deletion src/main/java/org/tron/common/utils/AbiUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,15 @@ public static void main(String[] args) {
System.out.println(parseMethod(byteMethod1, bytesValue1));
}


public static String generateOccupationConstantPrivateKey() {
StringBuilder privateKey = new StringBuilder();
String baseKey = "1234567890";
for (int i = 0; i < 6; i++) {
privateKey.append(baseKey);
}
privateKey.append("1234");
return privateKey.toString();
}


}
3 changes: 2 additions & 1 deletion src/main/java/org/tron/demo/ECKeyDemo.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.tron.common.crypto.ECKey;
import org.tron.common.crypto.Hash;
import org.tron.common.crypto.Sha256Sm3Hash;
import org.tron.common.utils.AbiUtil;
import org.tron.common.utils.Base58;
import org.tron.common.utils.ByteArray;
import org.tron.common.utils.Utils;
Expand Down Expand Up @@ -80,7 +81,7 @@ private static String private2Address(byte[] privateKey) throws CipherException
}

public static void main(String[] args) throws CipherException {
String privateKey = "F43EBCC94E6C257EDBE559183D1A8778B2D5A08040902C0F0A77A3343A1D0EA5";
String privateKey = AbiUtil.generateOccupationConstantPrivateKey();
String address = private2Address(ByteArray.fromHexString(privateKey));
System.out.println("base58Address: " + address);

Expand Down
211 changes: 211 additions & 0 deletions src/main/java/org/tron/mnemonic/Mnemonic.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
package org.tron.mnemonic;

import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator;
import org.bouncycastle.crypto.generators.SCrypt;
import org.bouncycastle.crypto.params.KeyParameter;
import org.tron.common.crypto.Hash;
import org.tron.common.crypto.SignInterface;
import org.tron.common.utils.ByteArray;
import org.tron.core.exception.CipherException;
import org.tron.keystore.StringUtils;
import org.tron.walletserver.WalletApi;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;

public class Mnemonic {

private static final int N_LIGHT = 1 << 12;
private static final int P_LIGHT = 6;

private static final int N_STANDARD = 1 << 18;
private static final int P_STANDARD = 1;

private static final int R = 8;
private static final int DKLEN = 32;

private static final int CURRENT_VERSION = 3;

private static final String CIPHER = "aes-128-ctr";
static final String AES_128_CTR = "pbkdf2";
static final String SCRYPT = "scrypt";

public static MnemonicFile create(byte[] password, SignInterface ecKeySm2Pair,
String mnemonicWords, int n, int p)
throws CipherException {
byte[] salt = generateRandomBytes(32);
byte[] derivedKey = generateDerivedScryptKey(password, salt, n, R, p, DKLEN);
byte[] encryptKey = Arrays.copyOfRange(derivedKey, 0, 16);
byte[] iv = generateRandomBytes(16);
byte[] mnemonicWordsBytes = mnemonicWords.getBytes();
byte[] cipherText = performCipherOperation(Cipher.ENCRYPT_MODE, iv, encryptKey,
mnemonicWordsBytes);
byte[] mac = generateMac(derivedKey, cipherText);
return createMnemonicFile(ecKeySm2Pair, cipherText, iv, salt, mac, n, p);
}

public static MnemonicFile createStandard(byte[] password, SignInterface ecKeySm2Pair, List<String> mnemonicWords)
throws CipherException {
return create(password, ecKeySm2Pair, MnemonicUtils.mnemonicWordsToString(mnemonicWords), N_STANDARD, P_STANDARD);
}

public static MnemonicFile createLight(byte[] password, SignInterface ecKeySm2Pair, List<String> mnemonicWords)
throws CipherException {
return create(password, ecKeySm2Pair, MnemonicUtils.mnemonicWordsToString(mnemonicWords), N_LIGHT, P_LIGHT);
}

private static MnemonicFile createMnemonicFile(SignInterface ecKeySm2Pair,
byte[] cipherText, byte[] iv, byte[] salt,
byte[] mac, int n, int p) {
MnemonicFile MnemonicFile = new MnemonicFile();
MnemonicFile.setAddress(WalletApi.encode58Check(ecKeySm2Pair.getAddress()));

MnemonicFile.Crypto crypto = new MnemonicFile.Crypto();
crypto.setCipher(CIPHER);
crypto.setCiphertext(ByteArray.toHexString(cipherText));
MnemonicFile.setCrypto(crypto);

MnemonicFile.CipherParams cipherParams = new MnemonicFile.CipherParams();
cipherParams.setIv(ByteArray.toHexString(iv));
crypto.setCipherparams(cipherParams);

crypto.setKdf(SCRYPT);
MnemonicFile.ScryptKdfParams kdfParams = new MnemonicFile.ScryptKdfParams();
kdfParams.setDklen(DKLEN);
kdfParams.setN(n);
kdfParams.setP(p);
kdfParams.setR(R);
kdfParams.setSalt(ByteArray.toHexString(salt));
crypto.setKdfparams(kdfParams);

crypto.setMac(ByteArray.toHexString(mac));
MnemonicFile.setCrypto(crypto);
MnemonicFile.setId(UUID.randomUUID().toString());
MnemonicFile.setVersion(CURRENT_VERSION);

return MnemonicFile;
}

private static byte[] generateDerivedScryptKey(
byte[] password, byte[] salt, int n, int r, int p, int dkLen) throws CipherException {
return SCrypt.generate(password, salt, n, r, p, dkLen);
}

private static byte[] generateAes128CtrDerivedKey(
byte[] password, byte[] salt, int c, String prf) throws CipherException {

if (!prf.equals("hmac-sha256")) {
throw new CipherException("Unsupported prf:" + prf);
}

PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(new SHA256Digest());
gen.init(password, salt, c);
return ((KeyParameter) gen.generateDerivedParameters(256)).getKey();
}

private static byte[] performCipherOperation(
int mode, byte[] iv, byte[] encryptKey, byte[] text) throws CipherException {

try {
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");

SecretKeySpec secretKeySpec = new SecretKeySpec(encryptKey, "AES");
cipher.init(mode, secretKeySpec, ivParameterSpec);
return cipher.doFinal(text);
} catch (NoSuchPaddingException | NoSuchAlgorithmException
| InvalidAlgorithmParameterException | InvalidKeyException
| BadPaddingException | IllegalBlockSizeException e) {
throw new CipherException("Error performing cipher operation", e);
}
}

private static byte[] generateMac(byte[] derivedKey, byte[] cipherText) {
byte[] result = new byte[16 + cipherText.length];

System.arraycopy(derivedKey, 16, result, 0, 16);
System.arraycopy(cipherText, 0, result, 16, cipherText.length);

return Hash.sha3(result);
}

public static byte[] decrypt2MnemonicWordsBytes(byte[] password, MnemonicFile MnemonicFile)
throws CipherException {
validate(MnemonicFile);
MnemonicFile.Crypto crypto = MnemonicFile.getCrypto();

byte[] mac = ByteArray.fromHexString(crypto.getMac());
byte[] iv = ByteArray.fromHexString(crypto.getCipherparams().getIv());
byte[] cipherText = ByteArray.fromHexString(crypto.getCiphertext());
byte[] derivedKey;

MnemonicFile.KdfParams kdfParams = crypto.getKdfparams();
if (kdfParams instanceof MnemonicFile.ScryptKdfParams) {
MnemonicFile.ScryptKdfParams scryptKdfParams =
(MnemonicFile.ScryptKdfParams) crypto.getKdfparams();
int dklen = scryptKdfParams.getDklen();
int n = scryptKdfParams.getN();
int p = scryptKdfParams.getP();
int r = scryptKdfParams.getR();
byte[] salt = ByteArray.fromHexString(scryptKdfParams.getSalt());
derivedKey = generateDerivedScryptKey(password, salt, n, r, p, dklen);
} else if (kdfParams instanceof MnemonicFile.Aes128CtrKdfParams) {
MnemonicFile.Aes128CtrKdfParams aes128CtrKdfParams =
(MnemonicFile.Aes128CtrKdfParams) crypto.getKdfparams();
int c = aes128CtrKdfParams.getC();
String prf = aes128CtrKdfParams.getPrf();
byte[] salt = ByteArray.fromHexString(aes128CtrKdfParams.getSalt());

derivedKey = generateAes128CtrDerivedKey(password, salt, c, prf);
} else {
throw new CipherException("Unable to deserialize params: " + crypto.getKdf());
}

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

if (!Arrays.equals(derivedMac, mac)) {
throw new CipherException("Invalid password provided");
}

byte[] encryptKey = Arrays.copyOfRange(derivedKey, 0, 16);
StringUtils.clear(derivedKey);
byte[] mnemonicWords = performCipherOperation(Cipher.DECRYPT_MODE, iv, encryptKey, cipherText);
StringUtils.clear(encryptKey);

return mnemonicWords;
}

static void validate(MnemonicFile MnemonicFile) throws CipherException {
MnemonicFile.Crypto crypto = MnemonicFile.getCrypto();
if (MnemonicFile.getVersion() != CURRENT_VERSION) {
throw new CipherException("Mnemonic version is not supported");
}

if (!crypto.getCipher().equals(CIPHER)) {
throw new CipherException("Mnemonic cipher is not supported");
}

if (!crypto.getKdf().equals(AES_128_CTR) && !crypto.getKdf().equals(SCRYPT)) {
throw new CipherException("KDF type is not supported");
}
}

public static byte[] generateRandomBytes(int size) {
byte[] bytes = new byte[size];
new SecureRandom().nextBytes(bytes);
return bytes;
}

}
Loading