package com.ripple.crypto.ecdsa;
import com.ripple.utils.Sha512;
import com.ripple.utils.Utils;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import static com.ripple.config.Config.getB58IdentiferCodecs;
public class Seed {
// See https://wiki.ripple.com/Account_Family
final byte[] seedBytes;
public Seed(byte[] seedBytes) {
this.seedBytes = seedBytes;
}
@Override
public String toString() {
return getB58IdentiferCodecs().encodeFamilySeed(seedBytes);
}
public byte[] getBytes() {
return seedBytes;
}
public IKeyPair keyPair() {
return createKeyPair(seedBytes, 0);
}
public IKeyPair rootKeyPair() {
return createKeyPair(seedBytes, -1);
}
public IKeyPair keyPair(int account) {
return createKeyPair(seedBytes, account);
}
public static Seed fromBase58(String b58) {
return new Seed(getB58IdentiferCodecs().decodeFamilySeed(b58));
}
public static Seed fromPassPhrase(String passPhrase) {
return new Seed(passPhraseToSeedBytes(passPhrase));
}
public static byte[] passPhraseToSeedBytes(String phrase) {
try {
return new Sha512(phrase.getBytes("utf-8")).finish128();
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
public static IKeyPair createKeyPair(byte[] seedBytes) {
return createKeyPair(seedBytes, 0);
}
public static IKeyPair createKeyPair(byte[] seedBytes, int accountNumber) {
BigInteger secret, pub, privateGen;
// The private generator (aka root private key, master private key)
privateGen = computePrivateGen(seedBytes);
byte[] publicGenBytes = computePublicGenerator(privateGen);
if (accountNumber == -1) {
// The root keyPair
return new KeyPair(privateGen, Utils.uBigInt(publicGenBytes));
}
else {
secret = computeSecretKey(privateGen, publicGenBytes, accountNumber);
pub = computePublicKey(secret);
return new KeyPair(secret, pub);
}
}
/**
*
* @param secretKey secret point on the curve as BigInteger
* @return corresponding public point
*/
public static byte[] getPublic(BigInteger secretKey) {
return SECP256K1.basePointMultipliedBy(secretKey);
}
/**
*
* @param privateGen secret point on the curve as BigInteger
* @return the corresponding public key is the public generator
* (aka public root key, master public key).
* return as byte[] for convenience.
*/
public static byte[] computePublicGenerator(BigInteger privateGen) {
return getPublic(privateGen);
}
public static BigInteger computePublicKey(BigInteger secret) {
return Utils.uBigInt(getPublic(secret));
}
public static BigInteger computePrivateGen(byte[] seedBytes) {
byte[] privateGenBytes;
BigInteger privateGen;
int i = 0;
while (true) {
privateGenBytes = new Sha512().add(seedBytes)
.add32(i++)
.finish256();
privateGen = Utils.uBigInt(privateGenBytes);
if (privateGen.compareTo(SECP256K1.order()) == -1) {
break;
}
}
return privateGen;
}
public static BigInteger computeSecretKey(BigInteger privateGen, byte[] publicGenBytes, int accountNumber) {
BigInteger secret;
int i;
i=0;
while (true) {
byte[] secretBytes = new Sha512().add(publicGenBytes)
.add32(accountNumber)
.add32(i++)
.finish256();
secret = Utils.uBigInt(secretBytes);
if (secret.compareTo(SECP256K1.order()) == -1) {
break;
}
}
secret = secret.add(privateGen).mod(SECP256K1.order());
return secret;
}
public static IKeyPair getKeyPair(byte[] seedBytes) {
return createKeyPair(seedBytes, 0);
}
public static IKeyPair getKeyPair(String b58) {
return getKeyPair(getB58IdentiferCodecs().decodeFamilySeed(b58));
}
}