ensure grinding for low R is done in the same way as libsecp256k1 so signatures match

This commit is contained in:
Craig Raw 2021-04-25 12:22:38 +02:00
parent db05e09fe5
commit 173743ba0d
4 changed files with 28 additions and 22 deletions

View file

@ -12,10 +12,6 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.math.BigInteger; import java.math.BigInteger;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.*; import java.util.*;
public class Utils { public class Utils {
@ -115,6 +111,14 @@ public class Utils {
return dest; return dest;
} }
public static void reverse(byte[] array) {
for (int i = 0; i < array.length / 2; i++) {
byte temp = array[i];
array[i] = array[array.length - i - 1];
array[array.length - i - 1] = temp;
}
}
/** Parse 4 bytes from the byte array (starting at the offset) as unsigned 32-bit integer in little endian format. */ /** Parse 4 bytes from the byte array (starting at the offset) as unsigned 32-bit integer in little endian format. */
public static long readUint32(byte[] bytes, int offset) { public static long readUint32(byte[] bytes, int offset) {
return (bytes[offset] & 0xffl) | return (bytes[offset] & 0xffl) |

View file

@ -470,7 +470,8 @@ public class ECKey implements EncryptableItem {
} }
protected boolean hasLowR() { protected boolean hasLowR() {
return r.toByteArray().length <= 32; //A low R signature will have less than 71 bytes when encoded to DER
return toCanonicalised().encodeToDER().length < 71;
} }
@Override @Override
@ -530,14 +531,14 @@ public class ECKey implements EncryptableItem {
} }
ECDSASignature signature; ECDSASignature signature;
int counter = 0; Integer counter = null;
do { do {
ECDSASigner signer = new ECDSASigner(new HMacDSANonceKCalculator(new SHA256Digest(), counter)); ECDSASigner signer = new ECDSASigner(new HMacDSANonceKCalculator(new SHA256Digest(), counter));
ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(privateKeyForSigning, CURVE); ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(privateKeyForSigning, CURVE);
signer.init(true, privKey); signer.init(true, privKey);
BigInteger[] components = signer.generateSignature(input.getBytes()); BigInteger[] components = signer.generateSignature(input.getBytes());
signature = new ECDSASignature(components[0], components[1]).toCanonicalised(); signature = new ECDSASignature(components[0], components[1]).toCanonicalised();
counter++; counter = (counter == null ? 1 : counter+1);
} while(!signature.hasLowR()); } while(!signature.hasLowR());
return signature; return signature;

View file

@ -21,7 +21,7 @@ public class HMacDSANonceKCalculator implements DSAKCalculator {
private final HMac hMac; private final HMac hMac;
private final byte[] K; private final byte[] K;
private final byte[] V; private final byte[] V;
private final long counter; private final Long counter;
private BigInteger n; private BigInteger n;
@ -31,11 +31,11 @@ public class HMacDSANonceKCalculator implements DSAKCalculator {
* @param digest digest to build the HMAC on. * @param digest digest to build the HMAC on.
* @param counter additional data as per RFC 6979 3.6 * @param counter additional data as per RFC 6979 3.6
*/ */
public HMacDSANonceKCalculator(Digest digest, int counter) { public HMacDSANonceKCalculator(Digest digest, Integer counter) {
this.hMac = new HMac(digest); this.hMac = new HMac(digest);
this.V = new byte[hMac.getMacSize()]; this.V = new byte[hMac.getMacSize()];
this.K = new byte[hMac.getMacSize()]; this.K = new byte[hMac.getMacSize()];
this.counter = Integer.toUnsignedLong(counter); this.counter = (counter == null ? null : Integer.toUnsignedLong(counter));
} }
public boolean isDeterministic() public boolean isDeterministic()
@ -74,8 +74,12 @@ public class HMacDSANonceKCalculator implements DSAKCalculator {
System.arraycopy(mVal, 0, m, m.length - mVal.length, mVal.length); System.arraycopy(mVal, 0, m, m.length - mVal.length, mVal.length);
byte[] c = null;
if(counter != null) {
BigInteger additional = BigInteger.valueOf(counter); BigInteger additional = BigInteger.valueOf(counter);
byte[] aData = Utils.bigIntegerToBytes(additional, size); c = Utils.bigIntegerToBytes(additional, size);
Utils.reverse(c);
}
hMac.init(new KeyParameter(K)); hMac.init(new KeyParameter(K));
@ -83,7 +87,9 @@ public class HMacDSANonceKCalculator implements DSAKCalculator {
hMac.update((byte)0x00); hMac.update((byte)0x00);
hMac.update(x, 0, x.length); hMac.update(x, 0, x.length);
hMac.update(m, 0, m.length); hMac.update(m, 0, m.length);
hMac.update(aData, 0, aData.length); if(c != null) {
hMac.update(c, 0, c.length);
}
hMac.doFinal(K, 0); hMac.doFinal(K, 0);
@ -97,6 +103,9 @@ public class HMacDSANonceKCalculator implements DSAKCalculator {
hMac.update((byte)0x01); hMac.update((byte)0x01);
hMac.update(x, 0, x.length); hMac.update(x, 0, x.length);
hMac.update(m, 0, m.length); hMac.update(m, 0, m.length);
if(counter != null) {
hMac.update(c, 0, c.length);
}
hMac.doFinal(K, 0); hMac.doFinal(K, 0);

View file

@ -83,7 +83,7 @@ public class PSBTEntry {
do { do {
bb.get(buf); bb.get(buf);
reverse(buf); Utils.reverse(buf);
ByteBuffer pbuf = ByteBuffer.wrap(buf); ByteBuffer pbuf = ByteBuffer.wrap(buf);
path.add(new ChildNumber(pbuf.getInt())); path.add(new ChildNumber(pbuf.getInt()));
} while(bb.hasRemaining()); } while(bb.hasRemaining());
@ -202,14 +202,6 @@ public class PSBTEntry {
return bb.array(); return bb.array();
} }
private static void reverse(byte[] array) {
for (int i = 0; i < array.length / 2; i++) {
byte temp = array[i];
array[i] = array[array.length - i - 1];
array[array.length - i - 1] = temp;
}
}
public void checkOneByteKey() throws PSBTParseException { public void checkOneByteKey() throws PSBTParseException {
if(this.getKey().length != 1) { if(this.getKey().length != 1) {
throw new PSBTParseException("PSBT key type must be one byte"); throw new PSBTParseException("PSBT key type must be one byte");