/** * Takes the keccak hash (32 bytes) of data and returns the ECDSA signature * * @param messageHash - * @return - * @throws IllegalStateException if this ECKey does not have the private part. */ public ECDSASignature sign(byte[] messageHash) { ECDSASignature sig = doSign(messageHash); // Now we have to work backwards to figure out the recId needed to recover the signature. int recId = -1; byte[] thisKey = this.pub.getEncoded(/* compressed */ false); for (int i = 0; i < 4; i++) { byte[] k = ECKey.recoverPubBytesFromSignature(i, sig, messageHash); if (k != null && Arrays.equals(k, thisKey)) { recId = i; break; } } if (recId == -1) { throw new RuntimeException("Could not construct a recoverable key. This should never happen."); } sig.v = (byte) (recId + 27); return sig; }
@Test(expected = IllegalArgumentException.class) public void testSignIncorrectInputSize() { // Expecting an IllegalArgumentException for a non 32-byte input ECKey key = new ECKey(); String message = "The quick brown fox jumps over the lazy dog."; key.doSign(message.getBytes()); }
@Test(expected = ECKey.MissingPrivateKeyException.class) public void testSignWithPubKeyOnly() { // Expecting an MissingPrivateKeyException for a public only ECKey ECKey key = ECKey.fromPublicOnly(pubKey); String message = "The quick brown fox jumps over the lazy dog."; byte[] input = HashUtil.sha3(message.getBytes()); key.doSign(input); }
@Test @SuppressWarnings("UnstableApiUsage") public void testSValue() 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 byte[] hash = HashUtil.sha3(new byte[] {i}); sigFutures.add(executor.submit(() -> key.doSign(hash))); } List<ECKey.ECDSASignature> sigs = Futures.allAsList(sigFutures).get(); for (ECKey.ECDSASignature signature : sigs) { assertTrue(signature.s.compareTo(ECKey.HALF_CURVE_ORDER) <= 0); } 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()); }
@Test public void keyRecovery() { ECKey key = new ECKey(); String message = "Hello World!"; byte[] hash = HashUtil.sha256(message.getBytes()); ECKey.ECDSASignature sig = key.doSign(hash); key = ECKey.fromPublicOnly(key.getPubKeyPoint()); boolean found = false; for (int i = 0; i < 4; i++) { ECKey key2 = ECKey.recoverFromSignature(i, sig, hash); checkNotNull(key2); if (key.equals(key2)) { found = true; break; } } assertTrue(found); }