/** * 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 * the same message. However, we dislike the ability to modify the bits of a Bitcoin transaction after it's * been signed, as that violates various assumed invariants. Thus in future only one of those forms will be * considered legal and the other will be banned. */ public ECDSASignature toCanonicalised() { 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 // s = 8, so (-8 % 10 == 2) thus both (r, 8) and (r, 2) are valid solutions. // 10 - 8 == 2, giving us always the latter solution, which is canonical. return new ECDSASignature(r, CURVE.getN().subtract(s)); } else { return this; } }
/** * 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 * the same message. However, we dislike the ability to modify the bits of a Bitcoin transaction after it's * been signed, as that violates various assumed invariants. Thus in future only one of those forms will be * considered legal and the other will be banned. */ public ECDSASignature toCanonicalised() { 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 // s = 8, so (-8 % 10 == 2) thus both (r, 8) and (r, 2) are valid solutions. // 10 - 8 == 2, giving us always the latter solution, which is canonical. return new ECDSASignature(r, CURVE.getN().subtract(s)); } else { return this; } }
/** * 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 * the same message. However, we dislike the ability to modify the bits of a Bitcoin transaction after it's * been signed, as that violates various assumed invariants. Thus in future only one of those forms will be * considered legal and the other will be banned. */ public ECDSASignature toCanonicalised() { 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 // s = 8, so (-8 % 10 == 2) thus both (r, 8) and (r, 2) are valid solutions. // 10 - 8 == 2, giving us always the latter solution, which is canonical. return new ECDSASignature(r, CURVE.getN().subtract(s)); } else { return this; } }
/** * 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]); } }
/** * 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]); } }
/** * 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 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()); }
/** * 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]); } }
/** Checks if the given input passes some of the AreInputsStandard checks. Not complete. */ public static RuleViolation isInputStandard(TransactionInput input) { for (ScriptChunk chunk : input.getScriptSig().getChunks()) { if (chunk.data != null && !chunk.isShortestPossiblePushData()) return RuleViolation.SHORTEST_POSSIBLE_PUSHDATA; if (chunk.isPushData()) { ECDSASignature signature; try { signature = ECKey.ECDSASignature.decodeFromDER(chunk.data); } catch (RuntimeException x) { // Doesn't look like a signature. signature = null; } if (signature != null) { if (!TransactionSignature.isEncodingCanonical(chunk.data)) return RuleViolation.SIGNATURE_CANONICAL_ENCODING; if (!signature.isCanonical()) return RuleViolation.SIGNATURE_CANONICAL_ENCODING; } } } return RuleViolation.NONE; }
/** Checks if the given input passes some of the AreInputsStandard checks. Not complete. */ public static RuleViolation isInputStandard(TransactionInput input) { for (ScriptChunk chunk : input.getScriptSig().getChunks()) { if (chunk.data != null && !chunk.isShortestPossiblePushData()) return RuleViolation.SHORTEST_POSSIBLE_PUSHDATA; if (chunk.isPushData()) { ECDSASignature signature; try { signature = ECKey.ECDSASignature.decodeFromDER(chunk.data); } catch (IllegalArgumentException x) { // Doesn't look like a signature. signature = null; } if (signature != null) { if (!TransactionSignature.isEncodingCanonical(chunk.data)) return RuleViolation.SIGNATURE_CANONICAL_ENCODING; if (!signature.isCanonical()) return RuleViolation.SIGNATURE_CANONICAL_ENCODING; } } } return RuleViolation.NONE; }
@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)); }
/** Checks if the given input passes some of the AreInputsStandard checks. Not complete. */ public static RuleViolation isInputStandard(TransactionInput input) { for (ScriptChunk chunk : input.getScriptSig().getChunks()) { if (chunk.data != null && !chunk.isShortestPossiblePushData()) return RuleViolation.SHORTEST_POSSIBLE_PUSHDATA; if (chunk.isPushData()) { ECDSASignature signature; try { signature = ECKey.ECDSASignature.decodeFromDER(chunk.data); } catch (RuntimeException x) { // Doesn't look like a signature. signature = null; } if (signature != null) { if (!TransactionSignature.isEncodingCanonical(chunk.data)) return RuleViolation.SIGNATURE_CANONICAL_ENCODING; if (!signature.isCanonical()) return RuleViolation.SIGNATURE_CANONICAL_ENCODING; } } } return RuleViolation.NONE; }
/** Checks if the given input passes some of the AreInputsStandard checks. Not complete. */ public static RuleViolation isInputStandard(TransactionInput input) { for (ScriptChunk chunk : input.getScriptSig().getChunks()) { if (chunk.data != null && !chunk.isShortestPossiblePushData()) return RuleViolation.SHORTEST_POSSIBLE_PUSHDATA; if (chunk.isPushData()) { ECDSASignature signature; try { signature = ECKey.ECDSASignature.decodeFromDER(chunk.data); } catch (RuntimeException x) { // Doesn't look like a signature. signature = null; } if (signature != null) { if (!TransactionSignature.isEncodingCanonical(chunk.data)) return RuleViolation.SIGNATURE_CANONICAL_ENCODING; if (!signature.isCanonical()) return RuleViolation.SIGNATURE_CANONICAL_ENCODING; } } } return RuleViolation.NONE; }
/** * 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 * the same message. However, we dislike the ability to modify the bits of a Bitcoin transaction after it's * been signed, as that violates various assumed invariants. Thus in future only one of those forms will be * considered legal and the other will be banned. */ public ECDSASignature toCanonicalised() { 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 // s = 8, so (-8 % 10 == 2) thus both (r, 8) and (r, 2) are valid solutions. // 10 - 8 == 2, giving us always the latter solution, which is canonical. return new ECDSASignature(r, CURVE.getN().subtract(s)); } else { return this; } }