package org.ripple.power;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import org.ripple.power.utils.CollectionUtils;
import org.ripple.bouncycastle.asn1.sec.SECNamedCurves;
import org.ripple.bouncycastle.asn1.x9.X9ECParameters;
import org.ripple.bouncycastle.crypto.params.ECDomainParameters;
import org.ripple.bouncycastle.math.ec.ECPoint;
public class RippleGenerator {
public static ECDomainParameters SECP256K1_PARAMS;
protected byte[] seedBytes;
static {
X9ECParameters params = SECNamedCurves.getByName("secp256k1");
SECP256K1_PARAMS = new ECDomainParameters(params.getCurve(),
params.getG(), params.getN(), params.getH());
}
public RippleGenerator(RippleSeedAddress secret) {
this(secret.getBytes());
}
public RippleGenerator(byte[] bytesSeed) {
if (bytesSeed.length != 16) {
throw new RuntimeException("The seed size should be 128 bit, was "
+ bytesSeed.length * 8);
}
this.seedBytes = CollectionUtils.copyOf(bytesSeed);
}
protected byte[] getPrivateRootKeyBytes() {
for (int seq = 0;; seq++) {
byte[] seqBytes = ByteBuffer.allocate(4).putInt(seq).array();
byte[] seedAndSeqBytes = Helper.concatenate(seedBytes, seqBytes);
byte[] privateGeneratorBytes = Helper.halfSHA512(seedAndSeqBytes);
BigInteger privateGeneratorBI = new BigInteger(1,
privateGeneratorBytes);
if (privateGeneratorBI.compareTo(SECP256K1_PARAMS.getN()) == -1) {
return privateGeneratorBytes;
}
}
}
protected ECPoint getPublicGeneratorPoint() {
byte[] privateGeneratorBytes = getPrivateRootKeyBytes();
ECPoint publicGenerator = new RipplePrivateKey(privateGeneratorBytes)
.getPublicKey().getPublicPoint();
return publicGenerator;
}
public RipplePrivateKey getAccountPrivateKey(int accountNumber) {
BigInteger privateRootKeyBI = new BigInteger(1,
getPrivateRootKeyBytes());
ECPoint publicGeneratorPoint = getPublicGeneratorPoint();
byte[] publicGeneratorBytes = publicGeneratorPoint.getEncoded();
byte[] accountNumberBytes = ByteBuffer.allocate(4)
.putInt(accountNumber).array();
BigInteger pubGenSeqSubSeqHashBI;
for (int subSequence = 0;; subSequence++) {
byte[] subSequenceBytes = ByteBuffer.allocate(4)
.putInt(subSequence).array();
byte[] pubGenAccountSubSeqBytes = Helper.concatenate(
publicGeneratorBytes, accountNumberBytes, subSequenceBytes);
byte[] publicGeneratorAccountSeqHashBytes = Helper
.halfSHA512(pubGenAccountSubSeqBytes);
pubGenSeqSubSeqHashBI = new BigInteger(1,
publicGeneratorAccountSeqHashBytes);
if (pubGenSeqSubSeqHashBI.compareTo(SECP256K1_PARAMS.getN()) == -1
&& !pubGenSeqSubSeqHashBI.equals(BigInteger.ZERO)) {
break;
}
}
BigInteger privateKeyForAccount = privateRootKeyBI.add(
pubGenSeqSubSeqHashBI).mod(SECP256K1_PARAMS.getN());
return new RipplePrivateKey(privateKeyForAccount);
}
public RipplePublicKey getAccountPublicKey(int accountNumber) {
ECPoint publicGeneratorPoint = getPublicGeneratorPoint();
byte[] publicGeneratorBytes = publicGeneratorPoint.getEncoded();
byte[] accountNumberBytes = ByteBuffer.allocate(4)
.putInt(accountNumber).array();
byte[] publicGeneratorAccountSeqHashBytes;
for (int subSequence = 0;; subSequence++) {
byte[] subSequenceBytes = ByteBuffer.allocate(4)
.putInt(subSequence).array();
byte[] pubGenAccountSubSeqBytes = Helper.concatenate(
publicGeneratorBytes, accountNumberBytes, subSequenceBytes);
publicGeneratorAccountSeqHashBytes = Helper
.halfSHA512(pubGenAccountSubSeqBytes);
BigInteger pubGenSeqSubSeqHashBI = new BigInteger(1,
publicGeneratorAccountSeqHashBytes);
if (pubGenSeqSubSeqHashBI.compareTo(SECP256K1_PARAMS.getN()) == -1) {
break;
}
}
ECPoint temporaryPublicPoint = new RipplePrivateKey(
publicGeneratorAccountSeqHashBytes).getPublicKey()
.getPublicPoint();
ECPoint accountPublicKeyPoint = publicGeneratorPoint
.add(temporaryPublicPoint);
byte[] publicKeyBytes = accountPublicKeyPoint.getEncoded();
return new RipplePublicKey(publicKeyBytes);
}
public RipplePublicGeneratorAddress getPublicGeneratorFamily()
throws Exception {
byte[] publicGeneratorBytes = getPublicGeneratorPoint().getEncoded();
return new RipplePublicGeneratorAddress(publicGeneratorBytes);
}
}