@Test public void sValue() throws Exception { // Check that we never generate an S value that is larger than half the curve order. This avoids a malleability // issue that can allow someone to change a transaction [hash] without invalidating the signature. final int ITERATIONS = 10; ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(ITERATIONS)); List<ListenableFuture<ECKey.ECDSASignature>> sigFutures = Lists.newArrayList(); final ECKey key = new ECKey(); for (byte i = 0; i < ITERATIONS; i++) { final Sha256Hash hash = Sha256Hash.of(new byte[]{i}); sigFutures.add(executor.submit(new Callable<ECKey.ECDSASignature>() { @Override public ECKey.ECDSASignature call() throws Exception { return key.sign(hash); } })); } List<ECKey.ECDSASignature> sigs = Futures.allAsList(sigFutures).get(); for (ECKey.ECDSASignature signature : sigs) { assertTrue(signature.isCanonical()); } 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()); }