/** * Returns a dummy invalid signature whose R/S values are set such that they will take up the same number of * encoded bytes as a real signature. This can be useful when you want to fill out a transaction to be of the * right size (e.g. for fee calculations) but don't have the requisite signing key yet and will fill out the * real signature later. */ public static TransactionSignature dummy() { BigInteger val = ECKey.HALF_CURVE_ORDER; return new TransactionSignature(val, val); }
/** Create a program that satisfies an OP_CHECKMULTISIG program. */ public static Script createMultiSigInputScript(List<TransactionSignature> signatures) { List<byte[]> sigs = new ArrayList<byte[]>(signatures.size()); for (TransactionSignature signature : signatures) { sigs.add(signature.encodeToBitcoin()); } return createMultiSigInputScriptBytes(sigs, null); }
public TransactionSignature getChannelSignature () { return TransactionSignature.decodeFromBitcoin(channelSignature, true); }
@Override public ECKey.ECDSASignature toCanonicalised() { return new TransactionSignature(super.toCanonicalised(), sigHashMode(), anyoneCanPay(), useForkId()); }
@Override public ECKey.ECDSASignature toCanonicalised() { return new TransactionSignature(super.toCanonicalised(), sigHashMode(), anyoneCanPay()); }
checkNotNull(theirSignature); stateMachine.checkState(State.WAITING_FOR_SIGNED_REFUND); TransactionSignature theirSig = TransactionSignature.decodeFromBitcoin(theirSignature, true); if (theirSig.sigHashMode() != Transaction.SigHash.NONE || !theirSig.anyoneCanPay()) throw new VerificationException("Refund signature was not SIGHASH_NONE|SIGHASH_ANYONECANPAY");
@Test public void completeTxPartiallySignedWithDummySigs() throws Exception { byte[] dummySig = TransactionSignature.dummy().encodeToBitcoin(); completeTxPartiallySigned(Wallet.MissingSigsMode.USE_DUMMY_SIG, dummySig); }
/** * Returns a decoded signature. * * @param requireCanonicalEncoding if the encoding of the signature must * be canonical. * @param requireCanonicalSValue if the S-value must be canonical (below half * the order of the curve). * @throws RuntimeException if the signature is invalid or unparseable in some way. */ public static TransactionSignature decodeFromBitcoin(byte[] bytes, boolean requireCanonicalEncoding, boolean requireCanonicalSValue) throws VerificationException { // Bitcoin encoding is DER signature + sighash byte. if (requireCanonicalEncoding && !isEncodingCanonical(bytes)) throw new VerificationException("Signature encoding is not canonical."); ECKey.ECDSASignature sig; try { sig = ECKey.ECDSASignature.decodeFromDER(bytes); } catch (IllegalArgumentException e) { throw new VerificationException("Could not decode DER", e); } if (requireCanonicalSValue && !sig.isCanonical()) throw new VerificationException("S-value is not canonical."); // In Bitcoin, any value of the final byte is valid, but not necessarily canonical. See javadocs for // isEncodingCanonical to learn more about this. So we must store the exact byte found. return new TransactionSignature(sig.r, sig.s, bytes[bytes.length - 1]); } }
@Test public void testCanonicalSigs() throws Exception { // Tests the canonical sigs from Bitcoin Core unit tests InputStream in = getClass().getResourceAsStream("sig_canonical.json"); // Poor man's JSON parser (because pulling in a lib for this is overkill) while (in.available() > 0) { while (in.available() > 0 && in.read() != '"') ; if (in.available() < 1) break; StringBuilder sig = new StringBuilder(); int c; while (in.available() > 0 && (c = in.read()) != '"') sig.append((char)c); assertTrue(TransactionSignature.isEncodingCanonical(HEX.decode(sig.toString()))); } in.close(); }
@SuppressWarnings("ConstantConditions") public void completeTxPartiallySigned(Wallet.MissingSigsMode missSigMode, byte[] expectedSig) throws Exception { // Check the wallet will write dummy scriptSigs for inputs that we have only pubkeys for without the privkey. ECKey priv = new ECKey(); ECKey pub = ECKey.fromPublicOnly(priv.getPubKeyPoint()); wallet.importKey(pub); ECKey priv2 = wallet.freshReceiveKey(); // Send three transactions, with one being an address type and the other being a raw CHECKSIG type pubkey only, // and the final one being a key we do have. We expect the first two inputs to be dummy values and the last // to be signed correctly. Transaction t1 = sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, CENT, pub.toAddress(PARAMS)); Transaction t2 = sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, CENT, pub); Transaction t3 = sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, CENT, priv2); SendRequest req = SendRequest.emptyWallet(OTHER_ADDRESS); req.missingSigsMode = missSigMode; wallet.completeTx(req); byte[] dummySig = TransactionSignature.dummy().encodeToBitcoin(); // Selected inputs can be in any order. for (int i = 0; i < req.tx.getInputs().size(); i++) { TransactionInput input = req.tx.getInput(i); if (input.getConnectedOutput().getParentTransaction().equals(t1)) { assertArrayEquals(expectedSig, input.getScriptSig().getChunks().get(0).data); } else if (input.getConnectedOutput().getParentTransaction().equals(t2)) { assertArrayEquals(expectedSig, input.getScriptSig().getChunks().get(0).data); } else if (input.getConnectedOutput().getParentTransaction().equals(t3)) { input.getScriptSig().correctlySpends(req.tx, i, t3.getOutput(0).getScriptPubKey()); } } assertTrue(TransactionSignature.isEncodingCanonical(dummySig)); }
TransactionSignature sig = TransactionSignature.decodeFromBitcoin(sigBytes, requireCanonical, verifyFlags.contains(VerifyFlag.LOW_S)); Sha256Hash hash = sig.useForkId() ? txContainingThis.hashForSignatureWitness(index, connectedScript, value, sig.sigHashMode(), sig.anyoneCanPay()) : txContainingThis.hashForSignature(index, connectedScript, (byte) sig.sighashFlags); sigValid = ECKey.verify(hash.getBytes(), sig, pubKey);
/** Constructs a transaction signature based on the ECDSA signature. */ public TransactionSignature(ECKey.ECDSASignature signature, Transaction.SigHash mode, boolean anyoneCanPay) { super(signature.r, signature.s); sighashFlags = calcSigHashValue(mode, anyoneCanPay); } public TransactionSignature(ECKey.ECDSASignature signature, Transaction.SigHash mode, boolean anyoneCanPay, boolean useForkId) {
@Test public void canonicalSignatureLowS() { // First, a synthetic test. TransactionSignature sig = TransactionSignature.dummy(); Script scriptHighS = ScriptBuilder .createInputScript(new TransactionSignature(sig.r, ECKey.CURVE.getN().subtract(sig.s))); assertEquals(RuleViolation.SIGNATURE_CANONICAL_ENCODING, DefaultRiskAnalysis.isInputStandard(new TransactionInput(PARAMS, null, scriptHighS.getProgram()))); // This is a real transaction. Its signatures S component is "low". Transaction tx1 = new Transaction(PARAMS, Utils.HEX.decode( "010000000200a2be4376b7f47250ad9ad3a83b6aa5eb6a6d139a1f50771704d77aeb8ce76c010000006a4730440220055723d363cd2d4fe4e887270ebdf5c4b99eaf233a5c09f9404f888ec8b839350220763c3794d310b384ce86decfb05787e5bfa5d31983db612a2dde5ffec7f396ae012102ef47e27e0c4bdd6dc83915f185d972d5eb8515c34d17bad584a9312e59f4e0bcffffffff52239451d37757eeacb86d32864ec1ee6b6e131d1e3fee6f1cff512703b71014030000006b483045022100ea266ac4f893d98a623a6fc0e6a961cd5a3f32696721e87e7570a68851917e75022056d75c3b767419f6f6cb8189a0ad78d45971523908dc4892f7594b75fd43a8d00121038bb455ca101ebbb0ecf7f5c01fa1dcb7d14fbf6b7d7ea52ee56f0148e72a736cffffffff0630b15a00000000001976a9146ae477b690cf85f21c2c01e2c8639a5c18dc884e88ac4f260d00000000001976a91498d08c02ab92a671590adb726dddb719695ee12e88ac65753b00000000001976a9140b2eb4ba6d364c82092f25775f56bc10cd92c8f188ac65753b00000000001976a914d1cb414e22081c6ba3a935635c0f1d837d3c5d9188ac65753b00000000001976a914df9d137a0d279471a2796291874c29759071340b88ac3d753b00000000001976a91459f5aa4815e3aa8e1720e8b82f4ac8e6e904e47d88ac00000000")); assertEquals("2a1c8569b2b01ebac647fb94444d1118d4d00e327456a3c518e40d47d72cd5fe", tx1.getHashAsString()); assertEquals(RuleViolation.NONE, DefaultRiskAnalysis.isStandard(tx1)); // This tx is the same as the above, except for a "high" S component on the signature of input 1. // It was part of the Oct 2015 malleability attack. Transaction tx2 = new Transaction(PARAMS, Utils.HEX.decode( "010000000200a2be4376b7f47250ad9ad3a83b6aa5eb6a6d139a1f50771704d77aeb8ce76c010000006a4730440220055723d363cd2d4fe4e887270ebdf5c4b99eaf233a5c09f9404f888ec8b839350220763c3794d310b384ce86decfb05787e5bfa5d31983db612a2dde5ffec7f396ae012102ef47e27e0c4bdd6dc83915f185d972d5eb8515c34d17bad584a9312e59f4e0bcffffffff52239451d37757eeacb86d32864ec1ee6b6e131d1e3fee6f1cff512703b71014030000006c493046022100ea266ac4f893d98a623a6fc0e6a961cd5a3f32696721e87e7570a68851917e75022100a928a3c4898be60909347e765f52872a613d8aada66c57a8c8791316d2f298710121038bb455ca101ebbb0ecf7f5c01fa1dcb7d14fbf6b7d7ea52ee56f0148e72a736cffffffff0630b15a00000000001976a9146ae477b690cf85f21c2c01e2c8639a5c18dc884e88ac4f260d00000000001976a91498d08c02ab92a671590adb726dddb719695ee12e88ac65753b00000000001976a9140b2eb4ba6d364c82092f25775f56bc10cd92c8f188ac65753b00000000001976a914d1cb414e22081c6ba3a935635c0f1d837d3c5d9188ac65753b00000000001976a914df9d137a0d279471a2796291874c29759071340b88ac3d753b00000000001976a91459f5aa4815e3aa8e1720e8b82f4ac8e6e904e47d88ac00000000")); assertEquals("dbe4147cf89b89fd9fa6c8ce6a3e2adecb234db094ec88301ae09073ca17d61d", tx2.getHashAsString()); assertFalse(ECKey.ECDSASignature .decodeFromDER(new Script(tx2.getInputs().get(1).getScriptBytes()).getChunks().get(0).data) .isCanonical()); assertEquals(RuleViolation.SIGNATURE_CANONICAL_ENCODING, DefaultRiskAnalysis.isStandard(tx2)); }
protected ECDSASignature doSign(Sha256Hash input, BigInteger privateKeyForSigning) { if (Secp256k1Context.isEnabled()) { try { byte[] signature = NativeSecp256k1.sign( input.getBytes(), Utils.bigIntegerToBytes(privateKeyForSigning, 32) ); return ECDSASignature.decodeFromDER(signature); } catch (NativeSecp256k1Util.AssertFailException e) { log.error("Caught AssertFailException inside secp256k1", e); throw new RuntimeException(e); } } if (FAKE_SIGNATURES) return TransactionSignature.dummy(); checkNotNull(privateKeyForSigning); ECDSASigner signer = new ECDSASigner(new HMacDSAKCalculator(new SHA256Digest())); ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(privateKeyForSigning, CURVE); signer.init(true, privKey); BigInteger[] components = signer.generateSignature(input.getBytes()); return new ECDSASignature(components[0], components[1]).toCanonicalised(); }
/** * What we get back from the signer are the two components of a signature, r and s. To get a flat byte stream * of the type used by Bitcoin we have to encode them using DER encoding, which is just a way to pack the two * components into a structure, and then we append a byte to the end for the sighash flags. */ public byte[] encodeToBitcoin() { try { ByteArrayOutputStream bos = derByteStream(); bos.write(sighashFlags); return bos.toByteArray(); } catch (IOException e) { throw new RuntimeException(e); // Cannot happen. } }
checkNotNull(theirSignature); stateMachine.checkState(State.WAITING_FOR_SIGNED_REFUND); TransactionSignature theirSig = TransactionSignature.decodeFromBitcoin(theirSignature, true); if (theirSig.sigHashMode() != Transaction.SigHash.NONE || !theirSig.anyoneCanPay()) throw new VerificationException("Refund signature was not SIGHASH_NONE|SIGHASH_ANYONECANPAY");
@Test public void completeTxPartiallySignedMarriedWithDummySigs() throws Exception { byte[] dummySig = TransactionSignature.dummy().encodeToBitcoin(); completeTxPartiallySignedMarried(Wallet.MissingSigsMode.USE_DUMMY_SIG, dummySig); }
/** * Returns a decoded signature. * * @param requireCanonicalEncoding if the encoding of the signature must * be canonical. * @param requireCanonicalSValue if the S-value must be canonical (below half * the order of the curve). * @throws RuntimeException if the signature is invalid or unparseable in some way. */ public static TransactionSignature decodeFromBitcoin(byte[] bytes, boolean requireCanonicalEncoding, boolean requireCanonicalSValue) throws VerificationException { // Bitcoin encoding is DER signature + sighash byte. if (requireCanonicalEncoding && !isEncodingCanonical(bytes)) throw new VerificationException("Signature encoding is not canonical."); ECKey.ECDSASignature sig; try { sig = ECKey.ECDSASignature.decodeFromDER(bytes); } catch (IllegalArgumentException e) { throw new VerificationException("Could not decode DER", e); } if (requireCanonicalSValue && !sig.isCanonical()) throw new VerificationException("S-value is not canonical."); // In Bitcoin, any value of the final byte is valid, but not necessarily canonical. See javadocs for // isEncodingCanonical to learn more about this. So we must store the exact byte found. return new TransactionSignature(sig.r, sig.s, bytes[bytes.length - 1]); } }
@Override public ECKey.ECDSASignature toCanonicalised() { return new TransactionSignature(super.toCanonicalised(), sigHashMode(), anyoneCanPay()); }
@Test public void testNonCanonicalSigs() throws Exception { // Tests the noncanonical sigs from Bitcoin Core unit tests InputStream in = getClass().getResourceAsStream("sig_noncanonical.json"); // Poor man's JSON parser (because pulling in a lib for this is overkill) while (in.available() > 0) { while (in.available() > 0 && in.read() != '"') ; if (in.available() < 1) break; StringBuilder sig = new StringBuilder(); int c; while (in.available() > 0 && (c = in.read()) != '"') sig.append((char)c); try { final String sigStr = sig.toString(); assertFalse(TransactionSignature.isEncodingCanonical(HEX.decode(sigStr))); } catch (IllegalArgumentException e) { // Expected for non-hex strings in the JSON that we should ignore } } in.close(); }