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.OutputStream;
import java.math.BigInteger;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.*;
public class Utils {
@ -115,6 +111,14 @@ public class Utils {
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. */
public static long readUint32(byte[] bytes, int offset) {
return (bytes[offset] & 0xffl) |

View file

@ -470,7 +470,8 @@ public class ECKey implements EncryptableItem {
}
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
@ -530,14 +531,14 @@ public class ECKey implements EncryptableItem {
}
ECDSASignature signature;
int counter = 0;
Integer counter = null;
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++;
counter = (counter == null ? 1 : counter+1);
} while(!signature.hasLowR());
return signature;

View file

@ -21,7 +21,7 @@ public class HMacDSANonceKCalculator implements DSAKCalculator {
private final HMac hMac;
private final byte[] K;
private final byte[] V;
private final long counter;
private final Long counter;
private BigInteger n;
@ -31,11 +31,11 @@ public class HMacDSANonceKCalculator implements DSAKCalculator {
* @param digest digest to build the HMAC on.
* @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.V = 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()
@ -74,8 +74,12 @@ public class HMacDSANonceKCalculator implements DSAKCalculator {
System.arraycopy(mVal, 0, m, m.length - mVal.length, mVal.length);
byte[] c = null;
if(counter != null) {
BigInteger additional = BigInteger.valueOf(counter);
byte[] aData = Utils.bigIntegerToBytes(additional, size);
c = Utils.bigIntegerToBytes(additional, size);
Utils.reverse(c);
}
hMac.init(new KeyParameter(K));
@ -83,7 +87,9 @@ public class HMacDSANonceKCalculator implements DSAKCalculator {
hMac.update((byte)0x00);
hMac.update(x, 0, x.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);
@ -97,6 +103,9 @@ public class HMacDSANonceKCalculator implements DSAKCalculator {
hMac.update((byte)0x01);
hMac.update(x, 0, x.length);
hMac.update(m, 0, m.length);
if(counter != null) {
hMac.update(c, 0, c.length);
}
hMac.doFinal(K, 0);

View file

@ -83,7 +83,7 @@ public class PSBTEntry {
do {
bb.get(buf);
reverse(buf);
Utils.reverse(buf);
ByteBuffer pbuf = ByteBuffer.wrap(buf);
path.add(new ChildNumber(pbuf.getInt()));
} while(bb.hasRemaining());
@ -202,14 +202,6 @@ public class PSBTEntry {
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 {
if(this.getKey().length != 1) {
throw new PSBTParseException("PSBT key type must be one byte");