diff options
| -rw-r--r-- | docs/CURVES.md | 5 | ||||
| -rw-r--r-- | docs/TESTS.md | 12 | ||||
| -rw-r--r-- | src/cz/crcs/ectester/reader/ECTesterReader.java | 7 | ||||
| -rw-r--r-- | src/cz/crcs/ectester/reader/test/CardCofactorTestSuite.java | 60 |
4 files changed, 81 insertions, 3 deletions
diff --git a/docs/CURVES.md b/docs/CURVES.md index a04e82f..a815c5c 100644 --- a/docs/CURVES.md +++ b/docs/CURVES.md @@ -60,4 +60,9 @@ Generated manually. Contains pre-generated points on twists of known named curves from NIST, SECG. These points can be used to attack some implementations. +Generated using [ecgen](https://github.com/J08nY/ecgen). + +### cofactor +Contains curves that are composite order, with points not on the subgroup generated by the generator. + Generated using [ecgen](https://github.com/J08nY/ecgen).
\ No newline at end of file diff --git a/docs/TESTS.md b/docs/TESTS.md index 72c328d..8f8fa2e 100644 --- a/docs/TESTS.md +++ b/docs/TESTS.md @@ -6,8 +6,9 @@ - `composite` - `invalid` - `twist` + - `cofactor` -**NOTE: The `wrong`, `composite`, `invalid` and `twist` test suites caused temporary/permanent DoS of some cards. These test suites prompt you for +**NOTE: The `wrong`, `composite`, `invalid`,`twist` and `cofactor` test suites caused temporary/permanent DoS of some cards. These test suites prompt you for confirmation before running, be cautious.** ## Default @@ -99,4 +100,13 @@ See [SafeCurves on twist security](https://safecurves.cr.yp.to/twist.html) for m For example: ```bash java -jar ECTester.jar -t twist +``` + +## Cofactor +Tests whether the card correctly rejects points that lie on the curve but not on the subgroup generated by the specified generator +during ECDH. + +For example: +```bash +java -jar ECTester.jar -t cofactor ```
\ No newline at end of file diff --git a/src/cz/crcs/ectester/reader/ECTesterReader.java b/src/cz/crcs/ectester/reader/ECTesterReader.java index 9c80bd2..1e8b8a4 100644 --- a/src/cz/crcs/ectester/reader/ECTesterReader.java +++ b/src/cz/crcs/ectester/reader/ECTesterReader.java @@ -246,7 +246,7 @@ public class ECTesterReader { actions.addOption(Option.builder("ln").longOpt("list-named").desc("Print the list of supported named curves and keys.").hasArg().argName("what").optionalArg(true).build()); actions.addOption(Option.builder("e").longOpt("export").desc("Export the defaut curve parameters of the card(if any).").build()); actions.addOption(Option.builder("g").longOpt("generate").desc("Generate [amount] of EC keys.").hasArg().argName("amount").optionalArg(true).build()); - actions.addOption(Option.builder("t").longOpt("test").desc("Test ECC support. [test_suite]:\n- default:\n- invalid:\n- twist:\n- wrong:\n- composite:\n- test-vectors:").hasArg().argName("test_suite").optionalArg(true).build()); + actions.addOption(Option.builder("t").longOpt("test").desc("Test ECC support. [test_suite]:\n- default:\n- invalid:\n- twist:\n- cofactor:\n- wrong:\n- composite:\n- test-vectors:").hasArg().argName("test_suite").optionalArg(true).build()); actions.addOption(Option.builder("dh").longOpt("ecdh").desc("Do EC KeyAgreement (ECDH...), [count] times.").hasArg().argName("count").optionalArg(true).build()); actions.addOption(Option.builder("dsa").longOpt("ecdsa").desc("Sign data with ECDSA, [count] times.").hasArg().argName("count").optionalArg(true).build()); @@ -431,6 +431,9 @@ public class ECTesterReader { case "twist": suite = new CardTwistTestSuite(writer, cfg, cardManager); break; + case "cofactor": + suite = new CardCofactorTestSuite(writer, cfg, cardManager); + break; default: System.err.println("Unknown test suite."); return; @@ -760,7 +763,7 @@ public class ECTesterReader { } testSuite = cli.getOptionValue("test", "default").toLowerCase(); - String[] tests = new String[]{"default", "composite", "invalid", "test-vectors", "wrong", "twist"}; + String[] tests = new String[]{"default", "composite", "invalid", "test-vectors", "wrong", "twist", "cofactor"}; if (!Arrays.asList(tests).contains(testSuite)) { System.err.println("Unknown test suite " + testSuite + ". Should be one of: " + Arrays.toString(tests)); return false; diff --git a/src/cz/crcs/ectester/reader/test/CardCofactorTestSuite.java b/src/cz/crcs/ectester/reader/test/CardCofactorTestSuite.java new file mode 100644 index 0000000..d3aef3a --- /dev/null +++ b/src/cz/crcs/ectester/reader/test/CardCofactorTestSuite.java @@ -0,0 +1,60 @@ +package cz.crcs.ectester.reader.test; + +import cz.crcs.ectester.applet.ECTesterApplet; +import cz.crcs.ectester.applet.EC_Consts; +import cz.crcs.ectester.common.ec.EC_Curve; +import cz.crcs.ectester.common.ec.EC_Key; +import cz.crcs.ectester.common.output.TestWriter; +import cz.crcs.ectester.common.test.CompoundTest; +import cz.crcs.ectester.common.test.Result; +import cz.crcs.ectester.common.test.Test; +import cz.crcs.ectester.data.EC_Store; +import cz.crcs.ectester.reader.CardMngr; +import cz.crcs.ectester.reader.ECTesterReader; +import cz.crcs.ectester.reader.command.Command; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class CardCofactorTestSuite extends CardTestSuite { + public CardCofactorTestSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) { + super(writer, cfg, cardManager, "cofactor", "The cofactor test suite tests whether the card correctly rejects points on the curve but not in the subgroup generated by the generator during ECDH."); + } + + @Override + protected void runTests() throws Exception { + Map<String, EC_Key.Public> pubkeys = EC_Store.getInstance().getObjects(EC_Key.Public.class, "cofactor"); + Map<EC_Curve, List<EC_Key.Public>> curves = new HashMap<>(); + for (EC_Key.Public key : pubkeys.values()) { + EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, key.getCurve()); + List<EC_Key.Public> keys = curves.getOrDefault(curve, new LinkedList<>()); + keys.add(key); + curves.putIfAbsent(curve, keys); + } + for (Map.Entry<EC_Curve, List<EC_Key.Public>> e : curves.entrySet()) { + EC_Curve curve = e.getKey(); + List<EC_Key.Public> keys = e.getValue(); + + Test allocate = CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), Result.ExpectedValue.SUCCESS); + Test set = CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), Result.ExpectedValue.SUCCESS); + Test generate = CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_LOCAL), Result.ExpectedValue.SUCCESS); + + Test prepare = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Prepare and generate keypair on " + curve.getId(), allocate, set, generate); + + List<Test> ecdhTests = new LinkedList<>(); + for (EC_Key.Public pub : keys) { + Command ecdhCommand = new Command.ECDH_direct(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_NONE, ECTesterApplet.KeyAgreement_ALG_EC_SVDP_DH, pub.flatten()); + ecdhTests.add(CommandTest.expect(ecdhCommand, Result.ExpectedValue.FAILURE, "Card correctly rejected point on non-generator subgroup.", "Card incorrectly accepted point on non-generator subgroup.")); + } + Test ecdh = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Perform ECDH with public points on non-generator subgroup", ecdhTests.toArray(new Test[0])); + + doTest(CompoundTest.greedyAll(Result.ExpectedValue.SUCCESS, "Cofactor test of " + curve.getId(), prepare, ecdh)); + new Command.Cleanup(this.card).send(); + } + } +} |
