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