package com.greenaddress.greenapi;
import com.blockstream.libwally.Wally;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.Sha256Hash;
import org.bitcoinj.crypto.DeterministicKey;
public abstract class HWWallet extends ISigningWallet {
@Override
public boolean requiresPrevoutRawTxs() { return true; }
@Override
public DeterministicKey getSubAccountPublicKey(final int subAccount) {
return getMyKey(subAccount).getPubKey();
}
@Override
public String[] signChallenge(final String challengeString, final String[] challengePath) {
// Generate a path for the challenge.
// We use "GA" + 0xB11E as the child path as this allows btchip to skip HID auth.
final HWWallet child = this.derive(0x4741b11e); // 0x4741 = Ascii G << 8 + A
// Generate a message to sign from the challenge
final String challenge = "greenaddress.it login " + challengeString;
final byte[] rawHash = Wally.format_bitcoin_message(challenge.getBytes(),
Wally.BITCOIN_MESSAGE_FLAG_HASH);
final Sha256Hash hash = Sha256Hash.wrap(rawHash);
// Return the path to the caller for them to pass in the server RPC call
challengePath[0] = "GA";
// Compute and return the challenge signatures
final ECKey.ECDSASignature signature = child.signMessage(challenge);
int recId;
for (recId = 0; recId < 4; ++recId) {
final ECKey recovered = ECKey.recoverFromSignature(recId, signature, hash, true);
if (recovered != null && recovered.equals(child.getPubKey()))
break;
}
return new String[]{signature.r.toString(), signature.s.toString(), String.valueOf(recId)};
}
private HWWallet getMyKey(final int subAccount) {
HWWallet parent = this;
if (subAccount != 0)
parent = parent.derive(ISigningWallet.HARDENED | 3)
.derive(ISigningWallet.HARDENED | subAccount);
return parent;
}
public byte[] getLocalEncryptionPassword() {
final byte[] pubkey = this.derive(PASSWORD_PATH).getPubKey().getPubKey();
return CryptoHelper.pbkdf2_hmac_sha512(pubkey, PASSWORD_SALT);
}
protected Object[] getChallengeArguments(final boolean isTrezor) {
final byte[] id = getPubKey().toAddress(Network.NETWORK).getHash160();
final Address addr = new Address(Network.NETWORK, id);
return new Object[]{ "login.get_trezor_challenge", addr.toString(), !isTrezor };
}
protected abstract DeterministicKey getPubKey();
protected abstract HWWallet derive(Integer childNumber);
protected abstract ECKey.ECDSASignature signMessage(String message);
}