Add satochip support: initial commit with debug traces

todo: clean code
This commit is contained in:
Toporin 2023-09-06 09:07:40 +01:00
parent 2b7b650fae
commit 2f1644ee16
3 changed files with 202 additions and 1 deletions

View file

@ -307,6 +307,11 @@ public class ECKey {
return pub.getEncoded();
}
// SATOCHIP
public byte[] getPubKey(Boolean compressed) {
return pub.getEncoded(compressed);
}
/**
* Gets the x coordinate of the raw public key value. This appears in transaction scriptPubKeys for Taproot outputs.
*/
@ -428,14 +433,119 @@ public class ECKey {
return verify(sigHash.getBytes(), signature);
}
public ECKey getTweakedOutputKey() {
log.debug("SATOCHIP ECKey getTweakedOutputKey START");
TaprootPubKey taprootPubKey = liftX(getPubKeyXCoord());
log.debug("SATOCHIP ECKey getTweakedOutputKey taprootPubKey: " + taprootPubKey);
log.debug("SATOCHIP ECKey getTweakedOutputKey taprootPubKey.ecPoint: " + taprootPubKey.ecPoint);
ECPoint internalKey = taprootPubKey.ecPoint;
log.debug("SATOCHIP ECKey getTweakedOutputKey internalKey: " + internalKey);
//debug
ECKey tmp2 = ECKey.fromPublicOnly(internalKey, true);
log.debug("SATOCHIP ECKey getTweakedOutputKey internalKey: " + Utils.bytesToHex(tmp2.getPubKey()));
//endbug
byte[] taggedHash = Utils.taggedHash("TapTweak", internalKey.getXCoord().getEncoded());
ECKey tweakValue = ECKey.fromPrivate(taggedHash);
log.debug("SATOCHIP ECKey getTweakedOutputKey tweakValue: " + Utils.bytesToHex(tweakValue.getPubKey()));
ECPoint outputKey = internalKey.add(tweakValue.getPubKeyPoint());
log.debug("SATOCHIP ECKey getTweakedOutputKey outputKey: " + outputKey);
//debug
ECKey tmp = ECKey.fromPublicOnly(outputKey, true);
log.debug("SATOCHIP ECKey getTweakedOutputKey outputKey: " + Utils.bytesToHex(tmp.getPubKey()));
//endbug
if(hasPrivKey()) {
log.debug("SATOCHIP ECKey getTweakedOutputKey PRIVKEY NEW VERSION");
// isEven => used to determine private key for tweaking
Boolean isEven = (getPubKey()[0] == 0x02);
log.debug("SATOCHIP ECKey getTweakedOutputKey getPubKey(): " + Utils.bytesToHex(getPubKey()));
log.debug("SATOCHIP ECKey getTweakedOutputKey isEven getPubKey()[0]: " + getPubKey()[0]);
log.debug("SATOCHIP ECKey getTweakedOutputKey isEven: " + isEven);
BigInteger taprootPriv;
if (isEven){
taprootPriv = priv;
} else {
taprootPriv = CURVE_PARAMS.getCurve().getOrder().subtract(priv);
}
BigInteger tweakedPrivKey = taprootPriv.add(tweakValue.getPrivKey()).mod(CURVE_PARAMS.getCurve().getOrder());
//debug
ECKey tmp3 = new ECKey(tweakedPrivKey, outputKey, true);
log.debug("SATOCHIP ECKey getTweakedOutputKey outputKey with private: " + Utils.bytesToHex(tmp3.getPubKey()));
log.debug("SATOCHIP ECKey getTweakedOutputKey outputKey private: " + tmp3.getPrivKey());
//endbug
/* log.debug("SATOCHIP ECKey getTweakedOutputKey PRIVKEY NEW VERSION SWITCH EVENNESS");
if (isEven){
//taprootPriv = priv;
taprootPriv = CURVE_PARAMS.getCurve().getOrder().subtract(priv);
} else {
taprootPriv = priv;
//taprootPriv = CURVE_PARAMS.getCurve().getOrder().subtract(priv);
}
tweakedPrivKey = taprootPriv.add(tweakValue.getPrivKey()).mod(CURVE_PARAMS.getCurve().getOrder());
//debug
ECKey tmp5 = new ECKey(tweakedPrivKey, outputKey, true);
log.debug("SATOCHIP ECKey getTweakedOutputKey outputKey with private: " + Utils.bytesToHex(tmp5.getPubKey()));
log.debug("SATOCHIP ECKey getTweakedOutputKey outputKey private: " + tmp5.getPrivKey());
//endbug*/
/* // ORIGNAL VERSION
log.debug("SATOCHIP ECKey getTweakedOutputKey PRIVKEY OLD VERSION");
taprootPriv = priv;
tweakedPrivKey = taprootPriv.add(tweakValue.getPrivKey()).mod(CURVE_PARAMS.getCurve().getOrder());
//TODO: Improve on this hack. How do we know whether to negate the private key before tweaking it?
if(!ECKey.fromPrivate(tweakedPrivKey).getPubKeyPoint().equals(outputKey)) {
taprootPriv = CURVE_PARAMS.getCurve().getOrder().subtract(priv);
tweakedPrivKey = taprootPriv.add(tweakValue.getPrivKey()).mod(CURVE_PARAMS.getCurve().getOrder());
}
//debug
ECKey tmp4 = new ECKey(tweakedPrivKey, outputKey, true);
log.debug("SATOCHIP ECKey getTweakedOutputKey outputKey with private: " + Utils.bytesToHex(tmp4.getPubKey()));
log.debug("SATOCHIP ECKey getTweakedOutputKey outputKey private: " + tmp4.getPrivKey());
//endbug*/
return new ECKey(tweakedPrivKey, outputKey, true);
}
return ECKey.fromPublicOnly(outputKey, true);
}
public ECKey getTweakedOutputKeyOLD() {
log.debug("SATOCHIP ECKey getTweakedOutputKey START");
TaprootPubKey taprootPubKey = liftX(getPubKeyXCoord());
log.debug("SATOCHIP ECKey getTweakedOutputKey taprootPubKey: " + taprootPubKey);
log.debug("SATOCHIP ECKey getTweakedOutputKey taprootPubKey.ecPoint: " + taprootPubKey.ecPoint);
ECPoint internalKey = taprootPubKey.ecPoint;
log.debug("SATOCHIP ECKey getTweakedOutputKey internalKey: " + internalKey);
//debug
ECKey tmp2 = ECKey.fromPublicOnly(internalKey, true);
log.debug("SATOCHIP ECKey getTweakedOutputKey outputKey: " + Utils.bytesToHex(tmp2.getPubKey()));
//endbug
byte[] taggedHash = Utils.taggedHash("TapTweak", internalKey.getXCoord().getEncoded());
ECKey tweakValue = ECKey.fromPrivate(taggedHash);
log.debug("SATOCHIP ECKey getTweakedOutputKey tweakValue: " + Utils.bytesToHex(tweakValue.getPubKey()));
ECPoint outputKey = internalKey.add(tweakValue.getPubKeyPoint());
log.debug("SATOCHIP ECKey getTweakedOutputKey outputKey: " + outputKey);
//debug
ECKey tmp = ECKey.fromPublicOnly(outputKey, true);
log.debug("SATOCHIP ECKey getTweakedOutputKey outputKey: " + Utils.bytesToHex(tmp.getPubKey()));
//endbug
if(hasPrivKey()) {
log.debug("SATOCHIP ECKey getTweakedOutputKey hasPrivKey(): true");
BigInteger taprootPriv = priv;
BigInteger tweakedPrivKey = taprootPriv.add(tweakValue.getPrivKey()).mod(CURVE_PARAMS.getCurve().getOrder());
//TODO: Improve on this hack. How do we know whether to negate the private key before tweaking it?

View file

@ -250,6 +250,75 @@ public class PSBTInput {
this.index = index;
}
// satochip debug
public void printDebugInfo(){
log.debug("SATOCHIP PSBTInput printDebugInfo() START");
// partialSignatures
try{
log.debug("SATOCHIP PSBTInput printDebugInfo() partialSignatures: Map<ECKey, TransactionSignature>");
log.debug("SATOCHIP PSBTInput printDebugInfo() partialSignatures.size(): " + partialSignatures.size());
log.debug("SATOCHIP PSBTInput printDebugInfo() partialSignatures: " + partialSignatures);
for (Map.Entry<ECKey, TransactionSignature> entry : partialSignatures.entrySet()) {
ECKey key = entry.getKey();
TransactionSignature value = entry.getValue();
log.debug("SATOCHIP PSBTInput printDebugInfo() partialSignatures pubkey: " + Utils.bytesToHex(key.getPubKey()));
log.debug("SATOCHIP PSBTInput printDebugInfo() partialSignatures sig: " + Utils.bytesToHex(value.encodeToBitcoin()));
}
log.debug("SATOCHIP PSBTInput printDebugInfo() partialSignatures END");
} catch(Exception e) {
log.debug("SATOCHIP PSBTInput printDebugInfo() partialSignatures exception: " + e);
}
// derivedPublicKeys
try{
log.debug("SATOCHIP PSBTInput printDebugInfo() derivedPublicKeys: Map<ECKey, KeyDerivation>");
log.debug("SATOCHIP PSBTInput printDebugInfo() derivedPublicKeys.size(): " + derivedPublicKeys.size());
log.debug("SATOCHIP PSBTInput printDebugInfo() derivedPublicKeys: " + derivedPublicKeys);
for (Map.Entry<ECKey, KeyDerivation> entry : derivedPublicKeys.entrySet()) {
ECKey key = entry.getKey();
KeyDerivation value = entry.getValue();
log.debug("SATOCHIP PSBTInput printDebugInfo() derivedPublicKeys pubkey: " + Utils.bytesToHex(key.getPubKey()));
log.debug("SATOCHIP PSBTInput printDebugInfo() derivedPublicKeys derivation: " + value.getDerivationPath());
}
log.debug("SATOCHIP PSBTInput printDebugInfo() derivedPublicKeys END");
} catch(Exception e) {
log.debug("SATOCHIP PSBTInput printDebugInfo() derivedPublicKeys exception: " + e);
}
// TransactionSignature tapKeyPathSignature;
if (tapKeyPathSignature!=null) {
log.debug("SATOCHIP PSBTInput printDebugInfo() tapKeyPathSignature: " + Utils.bytesToHex(tapKeyPathSignature.encodeToBitcoin()));
} else {
log.debug("SATOCHIP PSBTInput printDebugInfo() tapKeyPathSignature: null");
}
// ECKey tapInternalKey
if (tapInternalKey!=null) {
log.debug("SATOCHIP PSBTInput printDebugInfo() tapInternalKey: " + Utils.bytesToHex(tapInternalKey.getPubKey()));
} else {
log.debug("SATOCHIP PSBTInput printDebugInfo() tapInternalKey: null");
}
// tapDerivedPublicKeys
try {
log.debug("SATOCHIP PSBTInput printDebugInfo() tapDerivedPublicKeys: Map<ECKey, Map<KeyDerivation, List<Sha256Hash>>>");
log.debug("SATOCHIP PSBTInput printDebugInfo() tapDerivedPublicKeys.size(): " + tapDerivedPublicKeys.size());
log.debug("SATOCHIP PSBTInput printDebugInfo() tapDerivedPublicKeys: " + tapDerivedPublicKeys);
for (Map.Entry<ECKey, Map<KeyDerivation, List<Sha256Hash>>> entry : tapDerivedPublicKeys.entrySet()) {
ECKey key = entry.getKey();
Map<KeyDerivation, List<Sha256Hash>> value = entry.getValue();
log.debug("SATOCHIP PSBTInput printDebugInfo() tapDerivedPublicKeys pubkey: " + Utils.bytesToHex(key.getPubKey()));
log.debug("SATOCHIP PSBTInput printDebugInfo() tapDerivedPublicKeys map<keyderivation, listhash>: " + value);
}
log.debug("SATOCHIP PSBTInput printDebugInfo() tapDerivedPublicKeys END");
} catch(Exception e) {
log.debug("SATOCHIP PSBTInput printDebugInfo() tapDerivedPublicKeys exception: " + e);
}
log.debug("SATOCHIP PSBTInput printDebugInfo() END");
}
// endbug
public List<PSBTEntry> getInputEntries() {
List<PSBTEntry> entries = new ArrayList<>();
@ -573,6 +642,15 @@ public class PSBTInput {
if(isTaproot() && tapKeyPathSignature != null) {
ECKey outputKey = P2TR.getPublicKeyFromScript(getUtxo().getScript());
if(!outputKey.verify(hash, tapKeyPathSignature)) {
log.error("SATOCHIP PSBTInput verifySignatures error: " + "Tweaked internal key does not verify against provided taproot keypath signature");
log.error("SATOCHIP PSBTInput verifySignatures error: psbtinput: " + this);
log.error("SATOCHIP PSBTInput verifySignatures error: outputKey: " + Utils.bytesToHex(outputKey.getPubKey()));
log.error("SATOCHIP PSBTInput verifySignatures error: hash: " + Utils.bytesToHex(hash.getBytes()));
log.error("SATOCHIP PSBTInput verifySignatures error: tapKeyPathSignature: " + Utils.bytesToHex(tapKeyPathSignature.encodeToBitcoin()));
log.error("SATOCHIP PSBTInput verifySignatures error: getUtxo().getScript(): " + getUtxo().getScript());
log.error("SATOCHIP PSBTInput verifySignatures error: getUtxo(): " + getUtxo());
log.error("SATOCHIP PSBTInput verifySignatures error: getUtxo().getHash(): " + getUtxo().getHash());
log.error("SATOCHIP PSBTInput verifySignatures error: getUtxo().getIndex(): " + getUtxo().getIndex());
throw new PSBTSignatureException("Tweaked internal key does not verify against provided taproot keypath signature");
}
} else {

View file

@ -4,7 +4,7 @@ import java.util.Locale;
public enum WalletModel {
SEED, SPARROW, BITCOIN_CORE, ELECTRUM, TREZOR_1, TREZOR_T, COLDCARD, LEDGER_NANO_S, LEDGER_NANO_X, DIGITALBITBOX_01, KEEPKEY, SPECTER_DESKTOP, COBO_VAULT,
BITBOX_02, SPECTER_DIY, PASSPORT, BLUE_WALLET, KEYSTONE, SEEDSIGNER, CARAVAN, GORDIAN_SEED_TOOL, JADE, LEDGER_NANO_S_PLUS, EPS, TAPSIGNER, SATSCARD, LABELS, BSMS;
BITBOX_02, SPECTER_DIY, PASSPORT, BLUE_WALLET, KEYSTONE, SEEDSIGNER, CARAVAN, GORDIAN_SEED_TOOL, JADE, LEDGER_NANO_S_PLUS, EPS, TAPSIGNER, SATSCARD, SATOCHIP, LABELS, BSMS;
public static WalletModel getModel(String model) {
return valueOf(model.toUpperCase(Locale.ROOT));
@ -70,6 +70,19 @@ public enum WalletModel {
return (this == TAPSIGNER || this == SATSCARD);
}
// for card devices that require a PIN code, returns the minimum size of valid PIN code
public int getMinPinLength() {
if (this == TAPSIGNER || this == SATSCARD){
return 6;
}
else if (this == SATOCHIP){
return 4;
}
else {
return 0; // default?
}
}
public static WalletModel fromType(String type) {
for(WalletModel model : values()) {
if(model.getType().equalsIgnoreCase(type)) {