diff --git a/src/main/java/com/sparrowwallet/drongo/crypto/ECKey.java b/src/main/java/com/sparrowwallet/drongo/crypto/ECKey.java index d26c0de..e5b8141 100644 --- a/src/main/java/com/sparrowwallet/drongo/crypto/ECKey.java +++ b/src/main/java/com/sparrowwallet/drongo/crypto/ECKey.java @@ -461,6 +461,10 @@ public class ECKey implements EncryptableItem { return bos; } + protected boolean hasLowR() { + return r.toByteArray().length <= 32; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -517,11 +521,18 @@ public class ECKey implements EncryptableItem { throw new IllegalArgumentException("Private key cannot be null"); } - ECDSASigner signer = new ECDSASigner(new HMacDSAKCalculator(new SHA256Digest())); - ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(privateKeyForSigning, CURVE); - signer.init(true, privKey); - BigInteger[] components = signer.generateSignature(input.getBytes()); - return new ECDSASignature(components[0], components[1]).toCanonicalised(); + ECDSASignature signature; + int counter = 0; + do { + ECDSASigner signer = new ECDSASigner(new HMacDSANonceKCalculator(new SHA256Digest(), counter)); + ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(privateKeyForSigning, CURVE); + signer.init(true, privKey); + BigInteger[] components = signer.generateSignature(input.getBytes()); + signature = new ECDSASignature(components[0], components[1]).toCanonicalised(); + counter++; + } while(!signature.hasLowR()); + + return signature; } /** diff --git a/src/main/java/com/sparrowwallet/drongo/crypto/HMacDSANonceKCalculator.java b/src/main/java/com/sparrowwallet/drongo/crypto/HMacDSANonceKCalculator.java new file mode 100644 index 0000000..90aa721 --- /dev/null +++ b/src/main/java/com/sparrowwallet/drongo/crypto/HMacDSANonceKCalculator.java @@ -0,0 +1,160 @@ +package com.sparrowwallet.drongo.crypto; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import com.sparrowwallet.drongo.Utils; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.macs.HMac; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.signers.DSAKCalculator; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.BigIntegers; + +/** + * A deterministic K calculator based on the algorithm in section 3.2 of RFC 6979. + * This is a copy of org.bouncycastle.crypto.signers.HMacDSAKCalculator, with support for section 3.6 of RFC 6979 (additional data) + */ +public class HMacDSANonceKCalculator implements DSAKCalculator { + private static final BigInteger ZERO = BigInteger.valueOf(0); + + private final HMac hMac; + private final byte[] K; + private final byte[] V; + private final long counter; + + private BigInteger n; + + /** + * Base constructor. + * + * @param digest digest to build the HMAC on. + * @param counter additional data as per RFC 6979 3.6 + */ + public HMacDSANonceKCalculator(Digest digest, int counter) { + this.hMac = new HMac(digest); + this.V = new byte[hMac.getMacSize()]; + this.K = new byte[hMac.getMacSize()]; + this.counter = Integer.toUnsignedLong(counter); + } + + public boolean isDeterministic() + { + return true; + } + + public void init(BigInteger n, SecureRandom random) + { + throw new IllegalStateException("Operation not supported"); + } + + public void init(BigInteger n, BigInteger d, byte[] message) + { + this.n = n; + + Arrays.fill(V, (byte)0x01); + Arrays.fill(K, (byte)0); + + int size = BigIntegers.getUnsignedByteLength(n); + byte[] x = new byte[size]; + byte[] dVal = BigIntegers.asUnsignedByteArray(d); + + System.arraycopy(dVal, 0, x, x.length - dVal.length, dVal.length); + + byte[] m = new byte[size]; + + BigInteger mInt = bitsToInt(message); + + if (mInt.compareTo(n) >= 0) + { + mInt = mInt.subtract(n); + } + + byte[] mVal = BigIntegers.asUnsignedByteArray(mInt); + + System.arraycopy(mVal, 0, m, m.length - mVal.length, mVal.length); + + BigInteger additional = BigInteger.valueOf(counter); + byte[] aData = Utils.bigIntegerToBytes(additional, size); + + hMac.init(new KeyParameter(K)); + + hMac.update(V, 0, V.length); + hMac.update((byte)0x00); + hMac.update(x, 0, x.length); + hMac.update(m, 0, m.length); + hMac.update(aData, 0, aData.length); + + hMac.doFinal(K, 0); + + hMac.init(new KeyParameter(K)); + + hMac.update(V, 0, V.length); + + hMac.doFinal(V, 0); + + hMac.update(V, 0, V.length); + hMac.update((byte)0x01); + hMac.update(x, 0, x.length); + hMac.update(m, 0, m.length); + + hMac.doFinal(K, 0); + + hMac.init(new KeyParameter(K)); + + hMac.update(V, 0, V.length); + + hMac.doFinal(V, 0); + } + + public BigInteger nextK() + { + byte[] t = new byte[BigIntegers.getUnsignedByteLength(n)]; + + for (;;) + { + int tOff = 0; + + while (tOff < t.length) + { + hMac.update(V, 0, V.length); + + hMac.doFinal(V, 0); + + int len = Math.min(t.length - tOff, V.length); + System.arraycopy(V, 0, t, tOff, len); + tOff += len; + } + + BigInteger k = bitsToInt(t); + + if (k.compareTo(ZERO) > 0 && k.compareTo(n) < 0) + { + return k; + } + + hMac.update(V, 0, V.length); + hMac.update((byte)0x00); + + hMac.doFinal(K, 0); + + hMac.init(new KeyParameter(K)); + + hMac.update(V, 0, V.length); + + hMac.doFinal(V, 0); + } + } + + private BigInteger bitsToInt(byte[] t) + { + BigInteger v = new BigInteger(1, t); + + if (t.length * 8 > n.bitLength()) + { + v = v.shiftRight(t.length * 8 - n.bitLength()); + } + + return v; + } +} diff --git a/src/main/java/com/sparrowwallet/drongo/psbt/PSBTInput.java b/src/main/java/com/sparrowwallet/drongo/psbt/PSBTInput.java index 74aa4ef..db03ef9 100644 --- a/src/main/java/com/sparrowwallet/drongo/psbt/PSBTInput.java +++ b/src/main/java/com/sparrowwallet/drongo/psbt/PSBTInput.java @@ -334,7 +334,7 @@ public class PSBTInput { public Script getSigningScript() { int vout = (int)transaction.getInputs().get(index).getOutpoint().getIndex(); - Script signingScript = getNonWitnessUtxo() != null ? getNonWitnessUtxo().getOutputs().get(vout).getScript() : getWitnessUtxo().getScript(); + Script signingScript = getWitnessUtxo() != null ? getWitnessUtxo().getScript() : getNonWitnessUtxo().getOutputs().get(vout).getScript(); if(P2SH.isScriptType(signingScript)) { if(getRedeemScript() != null) { @@ -363,11 +363,11 @@ public class PSBTInput { private Sha256Hash getHashForSignature(Script connectedScript, SigHash localSigHash) { Sha256Hash hash; - if (getNonWitnessUtxo() != null) { - hash = transaction.hashForSignature(index, connectedScript, localSigHash); - } else { + if(getWitnessUtxo() != null) { long prevValue = getWitnessUtxo().getValue(); hash = transaction.hashForWitnessSignature(index, connectedScript, prevValue, localSigHash); + } else { + hash = transaction.hashForSignature(index, connectedScript, localSigHash); } return hash;