diff --git a/core/src/main/java/org/bitcoinj/core/ECKey.java b/core/src/main/java/org/bitcoinj/core/ECKey.java
index 682cd36a..fb0b4acf 100644
--- a/core/src/main/java/org/bitcoinj/core/ECKey.java
+++ b/core/src/main/java/org/bitcoinj/core/ECKey.java
@@ -495,6 +495,14 @@ public class ECKey implements EncryptableItem, Serializable {
this.s = s;
}
+ /**
+ * Returns true if the S component is "low", that means it is below {@link ECKey#HALF_CURVE_ORDER}. See BIP62.
+ */
+ public boolean isCanonical() {
+ return s.compareTo(HALF_CURVE_ORDER) <= 0;
+ }
+
/**
* Will automatically adjust the S component to be less than or equal to half the curve order, if necessary.
* This is required because for every signature (r,s) the signature (r, -s (mod N)) is a valid signature of
@@ -503,7 +511,7 @@ public class ECKey implements EncryptableItem, Serializable {
* considered legal and the other will be banned.
*/
public ECDSASignature toCanonicalised() {
- if (s.compareTo(HALF_CURVE_ORDER) > 0) {
+ if (!isCanonical()) {
// The order of the curve is the number of valid points that exist on that curve. If S is in the upper
// half of the number of valid points, then bring it back to the lower half. Otherwise, imagine that
// N = 10
diff --git a/core/src/test/java/org/bitcoinj/core/ECKeyTest.java b/core/src/test/java/org/bitcoinj/core/ECKeyTest.java
index 27b3cfe0..6febe6bc 100644
--- a/core/src/test/java/org/bitcoinj/core/ECKeyTest.java
+++ b/core/src/test/java/org/bitcoinj/core/ECKeyTest.java
@@ -17,6 +17,7 @@
package org.bitcoinj.core;
+import org.bitcoinj.core.ECKey.ECDSASignature;
import org.bitcoinj.crypto.EncryptedData;
import org.bitcoinj.crypto.KeyCrypter;
import org.bitcoinj.crypto.KeyCrypterScrypt;
@@ -96,11 +97,15 @@ public class ECKeyTest {
}
List sigs = Futures.allAsList(sigFutures).get();
for (ECKey.ECDSASignature signature : sigs) {
- assertTrue(signature.s.compareTo(ECKey.HALF_CURVE_ORDER) <= 0);
+ assertTrue(signature.isCanonical());
}
- final ECKey.ECDSASignature duplicate = new ECKey.ECDSASignature(sigs.get(0).r, sigs.get(0).s);
- assertEquals(sigs.get(0), duplicate);
- assertEquals(sigs.get(0).hashCode(), duplicate.hashCode());
+ final ECDSASignature first = sigs.get(0);
+ final ECKey.ECDSASignature duplicate = new ECKey.ECDSASignature(first.r, first.s);
+ assertEquals(first, duplicate);
+ assertEquals(first.hashCode(), duplicate.hashCode());
+
+ final ECKey.ECDSASignature highS = new ECKey.ECDSASignature(first.r, ECKey.CURVE.getN().subtract(first.s));
+ assertFalse(highS.isCanonical());
}
@Test