Skip to content

Commit a4c9ad6

Browse files
committed
Make transactions with signatures with "high" S component violate the "canonical encoding" rule.
This makes those transactions non-standard and risky. By default, they will not be accepted into wallets unless confirmed by the block chain.
1 parent 52b9623 commit a4c9ad6

File tree

2 files changed

+30
-0
lines changed

2 files changed

+30
-0
lines changed

core/src/main/java/org/bitcoinj/wallet/DefaultRiskAnalysis.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,8 @@ public static RuleViolation isInputStandard(TransactionInput input) {
184184
if (signature != null) {
185185
if (!TransactionSignature.isEncodingCanonical(chunk.data))
186186
return RuleViolation.SIGNATURE_CANONICAL_ENCODING;
187+
if (!signature.isCanonical())
188+
return RuleViolation.SIGNATURE_CANONICAL_ENCODING;
187189
}
188190
}
189191
}

core/src/test/java/org/bitcoinj/wallet/DefaultRiskAnalysisTest.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,34 @@ public void canonicalSignature() {
175175
.getProgram())));
176176
}
177177

178+
@Test
179+
public void canonicalSignatureLowS() {
180+
// First, a synthetic test.
181+
TransactionSignature sig = TransactionSignature.dummy();
182+
Script scriptHighS = ScriptBuilder
183+
.createInputScript(new TransactionSignature(sig.r, ECKey.CURVE.getN().subtract(sig.s)));
184+
assertEquals(RuleViolation.SIGNATURE_CANONICAL_ENCODING,
185+
DefaultRiskAnalysis.isInputStandard(new TransactionInput(params, null, scriptHighS.getProgram())));
186+
187+
// This is a real transaction. Its signatures S component is "low".
188+
Transaction tx1 = new Transaction(params, Utils.HEX.decode(
189+
"010000000200a2be4376b7f47250ad9ad3a83b6aa5eb6a6d139a1f50771704d77aeb8ce76c010000006a4730440220055723d363cd2d4fe4e887270ebdf5c4b99eaf233a5c09f9404f888ec8b839350220763c3794d310b384ce86decfb05787e5bfa5d31983db612a2dde5ffec7f396ae012102ef47e27e0c4bdd6dc83915f185d972d5eb8515c34d17bad584a9312e59f4e0bcffffffff52239451d37757eeacb86d32864ec1ee6b6e131d1e3fee6f1cff512703b71014030000006b483045022100ea266ac4f893d98a623a6fc0e6a961cd5a3f32696721e87e7570a68851917e75022056d75c3b767419f6f6cb8189a0ad78d45971523908dc4892f7594b75fd43a8d00121038bb455ca101ebbb0ecf7f5c01fa1dcb7d14fbf6b7d7ea52ee56f0148e72a736cffffffff0630b15a00000000001976a9146ae477b690cf85f21c2c01e2c8639a5c18dc884e88ac4f260d00000000001976a91498d08c02ab92a671590adb726dddb719695ee12e88ac65753b00000000001976a9140b2eb4ba6d364c82092f25775f56bc10cd92c8f188ac65753b00000000001976a914d1cb414e22081c6ba3a935635c0f1d837d3c5d9188ac65753b00000000001976a914df9d137a0d279471a2796291874c29759071340b88ac3d753b00000000001976a91459f5aa4815e3aa8e1720e8b82f4ac8e6e904e47d88ac00000000"));
190+
assertEquals("2a1c8569b2b01ebac647fb94444d1118d4d00e327456a3c518e40d47d72cd5fe", tx1.getHashAsString());
191+
192+
assertEquals(RuleViolation.NONE, DefaultRiskAnalysis.isStandard(tx1));
193+
194+
// This tx is the same as the above, except for a "high" S component on the signature of input 1.
195+
// It was part of the Oct 2015 malleability attack.
196+
Transaction tx2 = new Transaction(params, Utils.HEX.decode(
197+
"010000000200a2be4376b7f47250ad9ad3a83b6aa5eb6a6d139a1f50771704d77aeb8ce76c010000006a4730440220055723d363cd2d4fe4e887270ebdf5c4b99eaf233a5c09f9404f888ec8b839350220763c3794d310b384ce86decfb05787e5bfa5d31983db612a2dde5ffec7f396ae012102ef47e27e0c4bdd6dc83915f185d972d5eb8515c34d17bad584a9312e59f4e0bcffffffff52239451d37757eeacb86d32864ec1ee6b6e131d1e3fee6f1cff512703b71014030000006c493046022100ea266ac4f893d98a623a6fc0e6a961cd5a3f32696721e87e7570a68851917e75022100a928a3c4898be60909347e765f52872a613d8aada66c57a8c8791316d2f298710121038bb455ca101ebbb0ecf7f5c01fa1dcb7d14fbf6b7d7ea52ee56f0148e72a736cffffffff0630b15a00000000001976a9146ae477b690cf85f21c2c01e2c8639a5c18dc884e88ac4f260d00000000001976a91498d08c02ab92a671590adb726dddb719695ee12e88ac65753b00000000001976a9140b2eb4ba6d364c82092f25775f56bc10cd92c8f188ac65753b00000000001976a914d1cb414e22081c6ba3a935635c0f1d837d3c5d9188ac65753b00000000001976a914df9d137a0d279471a2796291874c29759071340b88ac3d753b00000000001976a91459f5aa4815e3aa8e1720e8b82f4ac8e6e904e47d88ac00000000"));
198+
assertEquals("dbe4147cf89b89fd9fa6c8ce6a3e2adecb234db094ec88301ae09073ca17d61d", tx2.getHashAsString());
199+
assertFalse(ECKey.ECDSASignature
200+
.decodeFromDER(new Script(tx2.getInputs().get(1).getScriptBytes()).getChunks().get(0).data)
201+
.isCanonical());
202+
203+
assertEquals(RuleViolation.SIGNATURE_CANONICAL_ENCODING, DefaultRiskAnalysis.isStandard(tx2));
204+
}
205+
178206
@Test
179207
public void standardOutputs() throws Exception {
180208
Transaction tx = new Transaction(params);

0 commit comments

Comments
 (0)