diff options
| author | J08nY | 2018-07-29 18:34:58 +0200 |
|---|---|---|
| committer | J08nY | 2018-07-29 18:34:58 +0200 |
| commit | d24630d759bb16f715564ab80a5d4447f57d03f2 (patch) | |
| tree | c30699e723a8ed31ea354be7e76eb16c1b681f04 /src/cz/crcs/ectester/reader/test | |
| parent | cb6c6b8b1274fe5a340c4317a4b015ea0ef15396 (diff) | |
| parent | 07d0c8947ef0d0f4c0ae01c1d8699d24a892752d (diff) | |
| download | ECTester-d24630d759bb16f715564ab80a5d4447f57d03f2.tar.gz ECTester-d24630d759bb16f715564ab80a5d4447f57d03f2.tar.zst ECTester-d24630d759bb16f715564ab80a5d4447f57d03f2.zip | |
Merge branch 'devel'
Diffstat (limited to 'src/cz/crcs/ectester/reader/test')
19 files changed, 1109 insertions, 295 deletions
diff --git a/src/cz/crcs/ectester/reader/test/CardCofactorSuite.java b/src/cz/crcs/ectester/reader/test/CardCofactorSuite.java new file mode 100644 index 0000000..39024b8 --- /dev/null +++ b/src/cz/crcs/ectester/reader/test/CardCofactorSuite.java @@ -0,0 +1,77 @@ +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.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.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Random; + +import static cz.crcs.ectester.common.test.Result.ExpectedValue; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class CardCofactorSuite extends CardTestSuite { + public CardCofactorSuite(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"); + List<Map.Entry<EC_Curve, List<EC_Key.Public>>> curveList = EC_Store.mapKeyToCurve(pubkeys.values()); + for (Map.Entry<EC_Curve, List<EC_Key.Public>> e : curveList) { + 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()), ExpectedValue.SUCCESS); + Test set = CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), ExpectedValue.SUCCESS); + Test generate = CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_LOCAL), ExpectedValue.SUCCESS); + + Test prepare = CompoundTest.all(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.TRANSFORMATION_NONE, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH, pub.flatten()); + ecdhTests.add(CommandTest.expect(ecdhCommand, ExpectedValue.FAILURE, "Card correctly rejected point on non-generator subgroup.", "Card incorrectly accepted point on non-generator subgroup.")); + } + Test ecdh = CompoundTest.all(ExpectedValue.SUCCESS, "Perform ECDH with public points on non-generator subgroup.", ecdhTests.toArray(new Test[0])); + + Random r = new Random(); + byte[] raw = new byte[128]; + byte[] sig = new byte[40]; + r.nextBytes(raw); + r.nextBytes(sig); + + List<Test> ecdsaTests = new LinkedList<>(); + for (EC_Key.Public pub : keys) { + Command setCommand = new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, pub.getParams(), pub.flatten()); + Test setTest = CommandTest.expect(setCommand, ExpectedValue.ANY); + Command ecdsaCommand = new Command.ECDSA_verify(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.Signature_ALG_ECDSA_SHA, raw, sig); + Test ecdsaTest = CommandTest.expect(ecdsaCommand, ExpectedValue.FAILURE); + ecdsaTests.add(CompoundTest.all(ExpectedValue.SUCCESS, "Verify random ECDSA signature by " + pub.getId() + ".", setTest, ecdsaTest)); + } + Test ecdsa = CompoundTest.all(ExpectedValue.SUCCESS, "Verify random ECDSA signature by public points on non-generator subgroup.", ecdsaTests.toArray(new Test[0])); + + Test tests = CompoundTest.all(ExpectedValue.SUCCESS, "Perform ECDH and ECDSA tests.", ecdh, ecdsa); + + if (cfg.cleanup) { + Test cleanup = CommandTest.expect(new Command.Cleanup(this.card), ExpectedValue.SUCCESS); + doTest(CompoundTest.greedyAllTry(ExpectedValue.SUCCESS, "Cofactor test of " + curve.getId() + ".", prepare, tests, cleanup)); + } else { + doTest(CompoundTest.greedyAllTry(ExpectedValue.SUCCESS, "Cofactor test of " + curve.getId() + ".", prepare, tests)); + } + } + } +} diff --git a/src/cz/crcs/ectester/reader/test/CardCompositeCurvesSuite.java b/src/cz/crcs/ectester/reader/test/CardCompositeCurvesSuite.java deleted file mode 100644 index a53806c..0000000 --- a/src/cz/crcs/ectester/reader/test/CardCompositeCurvesSuite.java +++ /dev/null @@ -1,51 +0,0 @@ -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.data.EC_Store; -import cz.crcs.ectester.reader.CardMngr; -import cz.crcs.ectester.reader.ECTesterReader; -import cz.crcs.ectester.reader.command.Command; -import javacard.security.KeyPair; - -import java.util.Map; - -import static cz.crcs.ectester.common.test.Result.ExpectedValue; - -/** - * @author Jan Jancar johny@neuromancer.sk - */ -public class CardCompositeCurvesSuite extends CardTestSuite { - - public CardCompositeCurvesSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) { - super(writer, cfg, cardManager, "composite", "The composite suite run ECDH over curves with composite order. This should generally fail, as using such a curve is unsafe."); - } - - @Override - protected void runTests() throws Exception { - /* Do the default run with the public keys set to provided smallorder keys - * over composite order curves. Essentially small subgroup attacks. - * These should fail, the curves aren't safe so that if the computation with - * a small order public key succeeds the private key modulo the public key order - * is revealed. - */ - Map<String, EC_Key> keys = EC_Store.getInstance().getObjects(EC_Key.class, "composite"); - for (EC_Key key : keys.values()) { - EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, key.getCurve()); - if (curve.getField() == KeyPair.ALG_EC_FP && !cfg.primeField || curve.getField() == KeyPair.ALG_EC_F2M && !cfg.binaryField) { - continue; - } - if ((curve.getBits() == cfg.bits || cfg.all)) { - doTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), ExpectedValue.SUCCESS)); - doTest(CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), ExpectedValue.ANY)); - doTest(CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_LOCAL), ExpectedValue.ANY)); - Command ecdhCommand = new Command.ECDH_direct(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_NONE, ECTesterApplet.KeyAgreement_ALG_EC_SVDP_DH, key.flatten()); - doTest(CommandTest.expect(ecdhCommand, ExpectedValue.FAILURE, "Card correctly rejected to do ECDH over a composite order curve.", "Card incorrectly does ECDH over a composite order curve, leaks bits of private key.")); - new Command.Cleanup(this.card).send(); - } - } - } -} diff --git a/src/cz/crcs/ectester/reader/test/CardCompositeSuite.java b/src/cz/crcs/ectester/reader/test/CardCompositeSuite.java new file mode 100644 index 0000000..ec56901 --- /dev/null +++ b/src/cz/crcs/ectester/reader/test/CardCompositeSuite.java @@ -0,0 +1,116 @@ +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.Test; +import cz.crcs.ectester.common.util.CardUtil; +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.LinkedList; +import java.util.List; +import java.util.Map; + +import static cz.crcs.ectester.common.test.Result.ExpectedValue; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class CardCompositeSuite extends CardTestSuite { + + public CardCompositeSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) { + super(writer, cfg, cardManager, "composite", "The composite suite runs ECDH over curves with composite order. This should generally fail, as using such a curve is unsafe."); + } + + @Override + protected void runTests() throws Exception { + /* Do the default run with the public keys set to provided smallorder keys + * over composite order curves. Essentially small subgroup attacks. + * These should fail, the curves aren't safe so that if the computation with + * a small order public key succeeds the private key modulo the public key order + * is revealed. + */ + Map<String, EC_Key> keys = EC_Store.getInstance().getObjects(EC_Key.class, "composite"); + List<Map.Entry<EC_Curve, List<EC_Key>>> mappedKeys = EC_Store.mapKeyToCurve(keys.values()); + for (Map.Entry<EC_Curve, List<EC_Key>> curveKeys : mappedKeys) { + EC_Curve curve = curveKeys.getKey(); + List<Test> tests = new LinkedList<>(); + Test allocate = runTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_LOCAL, curve.getBits(), curve.getField()), ExpectedValue.SUCCESS)); + if (!allocate.ok()) { + doTest(CompoundTest.all(ExpectedValue.SUCCESS, "No support for " + curve.getBits() + "b " + CardUtil.getKeyTypeString(curve.getField()) + ".", allocate)); + continue; + } + tests.add(allocate); + tests.add(CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), ExpectedValue.ANY)); + tests.add(CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_LOCAL), ExpectedValue.ANY)); + for (EC_Key key : curveKeys.getValue()) { + Command ecdhCommand = new Command.ECDH_direct(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH, key.flatten()); + Test ecdh = CommandTest.expect(ecdhCommand, ExpectedValue.FAILURE, "Card correctly rejected to do ECDH over a composite order curve.", "Card incorrectly does ECDH over a composite order curve, leaks bits of private key."); + tests.add(CompoundTest.greedyAllTry(ExpectedValue.SUCCESS, "Composite test of " + curve.getId() + ", " + key.getDesc(), ecdh)); + } + doTest(CompoundTest.all(ExpectedValue.SUCCESS, "Composite test of " + curve.getId() + ".", tests.toArray(new Test[0]))); + } + + + Map<String, EC_Curve> results = EC_Store.getInstance().getObjects(EC_Curve.class, "composite"); + List<Map.Entry<String, List<EC_Curve>>> groupList = EC_Store.mapToPrefix(results.values()); + /* Test the whole curves with both keypairs generated on card(no small-order public points provided). + */ + List<EC_Curve> wholeCurves = groupList.stream().filter((e) -> e.getKey().equals("whole")).findFirst().get().getValue(); + testGroup(wholeCurves, "Composite generator order", ExpectedValue.FAILURE, "Card rejected to do ECDH with composite order generator.", "Card did not reject to do ECDH with composite order generator."); + + /* Also test having a G of small order, so small R. + */ + List<EC_Curve> smallRCurves = groupList.stream().filter((e) -> e.getKey().equals("small")).findFirst().get().getValue(); + testGroup(smallRCurves, "Small generator order", ExpectedValue.FAILURE, "Card correctly rejected to do ECDH over a small order generator.", "Card incorrectly does ECDH over a small order generator."); + + /* Test increasingly larger prime R, to determine where/if card behavior changes. + */ + List<EC_Curve> varyingCurves = groupList.stream().filter((e) -> e.getKey().equals("varying")).findFirst().get().getValue(); + testGroup(varyingCurves, null, ExpectedValue.ANY, "", ""); + + /* Also test having a G of large but composite order, R = p * q, + */ + List<EC_Curve> pqCurves = groupList.stream().filter((e) -> e.getKey().equals("pq")).findFirst().get().getValue(); + testGroup(pqCurves, null, ExpectedValue.ANY, "", ""); + + /* Also test having G or large order being a Carmichael pseudoprime, R = p * q * r, + */ + List<EC_Curve> ppCurves = groupList.stream().filter((e) -> e.getKey().equals("pp")).findFirst().get().getValue(); + testGroup(ppCurves, "Generator order = Carmichael pseudoprime", ExpectedValue.ANY, "", ""); + + /* Also test rg0 curves. + */ + List<EC_Curve> rg0Curves = groupList.stream().filter((e) -> e.getKey().equals("rg0")).findFirst().get().getValue(); + testGroup(rg0Curves, null, ExpectedValue.ANY, "", ""); + } + + private void testGroup(List<EC_Curve> curves, String testName, ExpectedValue dhValue, String ok, String nok) throws Exception { + for (EC_Curve curve : curves) { + Test allocate = CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), ExpectedValue.SUCCESS); + Test set = CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), ExpectedValue.ANY); + Test generate = CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_BOTH), ExpectedValue.ANY); + Test ecdh = CommandTest.expect(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH), dhValue, ok, nok); + + String description; + if (testName == null) { + description = curve.getDesc() + " test of " + curve.getId() + "."; + } else { + description = testName + " test of " + curve.getId() + "."; + } + if (cfg.cleanup) { + Test cleanup = CommandTest.expect(new Command.Cleanup(this.card), ExpectedValue.SUCCESS); + doTest(CompoundTest.greedyAllTry(ExpectedValue.SUCCESS, description, allocate, set, generate, ecdh, cleanup)); + } else { + doTest(CompoundTest.greedyAllTry(ExpectedValue.SUCCESS, description, allocate, set, generate, ecdh)); + } + } + + } +} diff --git a/src/cz/crcs/ectester/reader/test/CardCompressionSuite.java b/src/cz/crcs/ectester/reader/test/CardCompressionSuite.java new file mode 100644 index 0000000..5e8f600 --- /dev/null +++ b/src/cz/crcs/ectester/reader/test/CardCompressionSuite.java @@ -0,0 +1,122 @@ +package cz.crcs.ectester.reader.test; + +import cz.crcs.ectester.applet.ECTesterApplet; +import cz.crcs.ectester.applet.EC_Consts; +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.common.util.ByteUtil; +import cz.crcs.ectester.common.util.CardUtil; +import cz.crcs.ectester.common.util.ECUtil; +import cz.crcs.ectester.reader.CardMngr; +import cz.crcs.ectester.reader.ECTesterReader; +import cz.crcs.ectester.reader.command.Command; +import cz.crcs.ectester.reader.response.Response; +import javacard.security.KeyPair; + +import java.security.spec.ECPoint; +import java.util.LinkedList; +import java.util.List; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class CardCompressionSuite extends CardTestSuite { + public CardCompressionSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) { + super(writer, cfg, cardManager, "compression", "The compression test suite tests cards support for compressed points in ECDH (as per ANSI X9.62).", + "It also tests for handling of bogus input by using the point at infinity and a hybrid point with the y coordinate corrupted."); + } + + @Override + protected void runTests() throws Exception { + //iterate over default curve sizes + // for Fp + // - allocate, set custom curve, generate keypairs, -> export generated. + // - test ecdh with local and remote simply(no compression) + // - test local privkey, remote pubkey (compressed) + // - test local privkey, remote pubkey (hybrid) + // - test local privkey, remote pubkey (hybrid with wrong y) + // - test local privkey, remote pubkey (point at infinity) + if (cfg.primeField) { + runCompression(KeyPair.ALG_EC_FP); + } + // for F2m + // - allocate, set custom curve, generate keypairs, -> export generated. + // - test ecdh with local and remote simply(no compression) + // - test local privkey, remote pubkey (compressed) + // - test local privkey, remote pubkey (hybrid) + // - test local privkey, remote pubkey (hybrid with wrong y) + // - test local privkey, remote pubkey (point at infinity) + if (cfg.binaryField) { + runCompression(KeyPair.ALG_EC_F2M); + } + } + + private void runCompression(byte field) throws Exception { + short[] keySizes = field == KeyPair.ALG_EC_FP ? EC_Consts.FP_SIZES : EC_Consts.F2M_SIZES; + short domain = field == KeyPair.ALG_EC_FP ? EC_Consts.PARAMETERS_DOMAIN_FP : EC_Consts.PARAMETERS_DOMAIN_F2M; + + for (short keyLength : keySizes) { + String spec = keyLength + "b " + CardUtil.getKeyTypeString(field); + + Test allocateFirst = runTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, keyLength, field), Result.ExpectedValue.SUCCESS)); + if (!allocateFirst.ok()) { + doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "No support for " + spec + ".", allocateFirst)); + continue; + } + + List<Test> compressionTests = new LinkedList<>(); + compressionTests.add(allocateFirst); + Test setCustom = runTest(CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.getCurve(keyLength, field), domain, null), Result.ExpectedValue.SUCCESS)); + Test genCustom = runTest(CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_BOTH), Result.ExpectedValue.SUCCESS)); + compressionTests.add(setCustom); + compressionTests.add(genCustom); + + Response.Export key = new Command.Export(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.KEY_PUBLIC, EC_Consts.PARAMETER_W).send(); + byte[] pubkey = key.getParameter(ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.KEY_PUBLIC); + ECPoint pub; + try { + pub = ECUtil.fromX962(pubkey, null); + } catch (IllegalArgumentException iae) { + // TODO: use external SECG curves so we have them here. + doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "", compressionTests.toArray(new Test[0]))); + continue; + } + + List<Test> kaTests = new LinkedList<>(); + for (byte kaType : EC_Consts.KA_TYPES) { + List<Test> thisTests = new LinkedList<>(); + Test allocate = runTest(CommandTest.expect(new Command.AllocateKeyAgreement(this.card, kaType), Result.ExpectedValue.SUCCESS)); + if (allocate.ok()) { + Test ka = runTest(CommandTest.expect(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, kaType), Result.ExpectedValue.SUCCESS)); + + thisTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, "KeyAgreement setup and basic test.", allocate, ka)); + if (ka.ok()) { + // tests of the good stuff + Test kaCompressed = CommandTest.expect(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_COMPRESS, kaType), Result.ExpectedValue.SUCCESS); + Test kaHybrid = CommandTest.expect(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_COMPRESS_HYBRID, kaType), Result.ExpectedValue.SUCCESS); + thisTests.add(CompoundTest.any(Result.ExpectedValue.SUCCESS, "Tests of compressed and hybrid form.", kaCompressed, kaHybrid)); + + // tests the bad stuff here + byte[] pubHybrid = ECUtil.toX962Hybrid(pub, keyLength); + pubHybrid[pubHybrid.length - 1] ^= 1; + byte[] pubHybridEncoded = ByteUtil.prependLength(pubHybrid); + Test kaBadHybrid = CommandTest.expect(new Command.ECDH_direct(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, kaType, pubHybridEncoded), Result.ExpectedValue.FAILURE); + + byte[] pubInfinityEncoded = {0x01, 0x00}; + Test kaBadInfinity = CommandTest.expect(new Command.ECDH_direct(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, kaType, pubInfinityEncoded), Result.ExpectedValue.FAILURE); + thisTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Tests of corrupted hybrid form and infinity.", kaBadHybrid, kaBadInfinity)); + } + kaTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, "KeyAgreement tests of " + CardUtil.getKATypeString(kaType) + ".", thisTests.toArray(new Test[0]))); + } + } + compressionTests.addAll(kaTests); + if (cfg.cleanup) { + compressionTests.add(CommandTest.expect(new Command.Cleanup(this.card), Result.ExpectedValue.SUCCESS)); + } + + doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Compression test of " + spec + ".", compressionTests.toArray(new Test[0]))); + } + } +} diff --git a/src/cz/crcs/ectester/reader/test/CardDefaultSuite.java b/src/cz/crcs/ectester/reader/test/CardDefaultSuite.java index c3bd9c8..fa9bfd0 100644 --- a/src/cz/crcs/ectester/reader/test/CardDefaultSuite.java +++ b/src/cz/crcs/ectester/reader/test/CardDefaultSuite.java @@ -4,6 +4,7 @@ import cz.crcs.ectester.applet.ECTesterApplet; import cz.crcs.ectester.applet.EC_Consts; 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.common.util.CardUtil; import cz.crcs.ectester.reader.CardMngr; @@ -13,8 +14,13 @@ import javacard.security.KeyPair; import java.util.LinkedList; import java.util.List; +import java.util.Random; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; import static cz.crcs.ectester.common.test.Result.ExpectedValue; +import static cz.crcs.ectester.common.test.Result.Value; /** * @author Jan Jancar johny@neuromancer.sk @@ -22,7 +28,7 @@ import static cz.crcs.ectester.common.test.Result.ExpectedValue; public class CardDefaultSuite extends CardTestSuite { public CardDefaultSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) { - super(writer, cfg, cardManager, "default", "The default test suite run basic support of ECDH and ECDSA."); + super(writer, cfg, cardManager, "default", "The default test suite tests basic support of ECDH and ECDSA."); } @Override @@ -37,58 +43,99 @@ public class CardDefaultSuite extends CardTestSuite { private void runDefault(byte field) throws Exception { short[] keySizes = field == KeyPair.ALG_EC_FP ? EC_Consts.FP_SIZES : EC_Consts.F2M_SIZES; + short domain = field == KeyPair.ALG_EC_FP ? EC_Consts.PARAMETERS_DOMAIN_FP : EC_Consts.PARAMETERS_DOMAIN_F2M; for (short keyLength : keySizes) { - String description = "Tests of " + keyLength + "b " + (field == KeyPair.ALG_EC_FP ? "ALG_EC_FP" : "ALG_EC_F2M") + " support."; List<Test> supportTests = new LinkedList<>(); - Test key = runTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, keyLength, field), ExpectedValue.SUCCESS)); - if (!key.ok()) { - doTest(CompoundTest.all(ExpectedValue.SUCCESS, description + " None.", key)); + Test allocateFirst = runTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, keyLength, field), ExpectedValue.SUCCESS)); + if (!allocateFirst.ok()) { + doTest(CompoundTest.all(ExpectedValue.SUCCESS, "No support for " + keyLength + "b " + CardUtil.getKeyTypeString(field) + ".", allocateFirst)); continue; } - supportTests.add(key); + supportTests.add(allocateFirst); Test genDefault = runTest(CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_BOTH), ExpectedValue.SUCCESS)); - Test setCustom = runTest(CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.getCurve(keyLength, field), EC_Consts.PARAMETERS_DOMAIN_FP, null), ExpectedValue.SUCCESS)); + Test allocateSecond = runTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, keyLength, field), ExpectedValue.SUCCESS)); + Test setCustom = runTest(CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.getCurve(keyLength, field), domain, null), ExpectedValue.SUCCESS)); Test genCustom = runTest(CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_BOTH), ExpectedValue.SUCCESS)); supportTests.add(genDefault); + supportTests.add(allocateSecond); supportTests.add(setCustom); supportTests.add(genCustom); + List<Test> kaTests = new LinkedList<>(); for (byte kaType : EC_Consts.KA_TYPES) { Test allocate = runTest(CommandTest.expect(new Command.AllocateKeyAgreement(this.card, kaType), ExpectedValue.SUCCESS)); if (allocate.ok()) { - Command ecdh = new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_NONE, kaType); + Command ecdh = new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, kaType); Test ka = runTest(CommandTest.expect(ecdh, ExpectedValue.SUCCESS)); - Test kaCompressed = runTest(CommandTest.expect(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_COMPRESS, kaType), ExpectedValue.SUCCESS)); - Test perfTest = null; + Test kaCompressed = runTest(CommandTest.expect(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_COMPRESS, kaType), ExpectedValue.SUCCESS)); + + String kaDesc = "Test of the " + CardUtil.getKATypeString(kaType) + " KeyAgreement."; + Function<Test[], Result> kaCallback = (tests) -> { + if (tests[1].ok() || tests[2].ok()) { + return new Result(Value.SUCCESS, "Some ECDH is supported."); + } else { + return new Result(Value.FAILURE, "ECDH failed."); + } + }; + + Test compound; if (ka.ok()) { - perfTest = runTest(PerformanceTest.repeat(ecdh, 10)); + Test perfTest = runTest(PerformanceTest.repeat(ecdh, 10)); + compound = runTest(CompoundTest.function(kaCallback, kaDesc, allocate, ka, kaCompressed, perfTest)); + } else { + compound = runTest(CompoundTest.function(kaCallback, kaDesc, allocate, ka, kaCompressed)); } - Test compound = runTest(CompoundTest.all(ExpectedValue.SUCCESS, "Test of the " + CardUtil.getKATypeString(kaType) + " KeyAgreement.", allocate, ka, kaCompressed, perfTest)); - supportTests.add(compound); + + kaTests.add(compound); } else { runTest(allocate); - supportTests.add(allocate); + kaTests.add(allocate); } } + Test kaTest = runTest(CompoundTest.any(ExpectedValue.SUCCESS, "KeyAgreement tests.", kaTests.toArray(new Test[0]))); + supportTests.add(kaTest); + + List<Test> signTests = new LinkedList<>(); for (byte sigType : EC_Consts.SIG_TYPES) { Test allocate = runTest(CommandTest.expect(new Command.AllocateSignature(this.card, sigType), ExpectedValue.SUCCESS)); if (allocate.ok()) { Command ecdsa = new Command.ECDSA(this.card, ECTesterApplet.KEYPAIR_LOCAL, sigType, ECTesterApplet.EXPORT_FALSE, null); Test expect = runTest(CommandTest.expect(ecdsa, ExpectedValue.SUCCESS)); - Test perfTest = null; + + String signDesc = "Test of the " + CardUtil.getSigTypeString(sigType) + " signature."; + + Random rand = new Random(); + byte[] sigData = new byte[64]; + rand.nextBytes(sigData); + + Test compound; if (expect.ok()) { - perfTest = runTest(PerformanceTest.repeat(ecdsa, 10)); + Command ecdsaSign = new Command.ECDSA_sign(this.card, ECTesterApplet.KEYPAIR_LOCAL, sigType, ECTesterApplet.EXPORT_TRUE, sigData); + PerformanceTest signTest = runTest(PerformanceTest.repeat("Sign", ecdsaSign, 10)); + byte[] signature = signTest.getResponses()[0].getParam(0); + Command ecdsaVerify = new Command.ECDSA_verify(this.card, ECTesterApplet.KEYPAIR_LOCAL, sigType, sigData, signature); + PerformanceTest verifyTest = runTest(PerformanceTest.repeat("Verify", ecdsaVerify, 10)); + compound = runTest(CompoundTest.all(ExpectedValue.SUCCESS, signDesc, allocate, expect, signTest, verifyTest)); + } else { + compound = runTest(CompoundTest.all(ExpectedValue.SUCCESS, signDesc, allocate, expect)); } - Test compound = runTest(CompoundTest.all(ExpectedValue.SUCCESS, "Test of the " + CardUtil.getSigTypeString(sigType) + " signature.", allocate, expect, perfTest)); - supportTests.add(compound); + signTests.add(compound); } else { - supportTests.add(allocate); + signTests.add(allocate); } } - doTest(CompoundTest.all(ExpectedValue.SUCCESS, description + " Some.", supportTests.toArray(new Test[0]))); - new Command.Cleanup(this.card).send(); + Test signTest = runTest(CompoundTest.any(ExpectedValue.SUCCESS, "Signature tests.", signTests.toArray(new Test[0]))); + supportTests.add(signTest); + ExpectedValue[] testExpects = {ExpectedValue.SUCCESS, ExpectedValue.ANY, ExpectedValue.SUCCESS, ExpectedValue.SUCCESS, ExpectedValue.SUCCESS, ExpectedValue.SUCCESS, ExpectedValue.SUCCESS}; + List<ExpectedValue> expects = Stream.of(testExpects).collect(Collectors.toList()); + if (cfg.cleanup) { + supportTests.add(CommandTest.expect(new Command.Cleanup(this.card), Result.ExpectedValue.SUCCESS)); + expects.add(ExpectedValue.ANY); + } + + doTest(CompoundTest.mask(expects.toArray(new ExpectedValue[0]), "Tests of " + keyLength + "b " + CardUtil.getKeyTypeString(field) + " support.", supportTests.toArray(new Test[0]))); } } } diff --git a/src/cz/crcs/ectester/reader/test/CardDegenerateSuite.java b/src/cz/crcs/ectester/reader/test/CardDegenerateSuite.java new file mode 100644 index 0000000..064c6cb --- /dev/null +++ b/src/cz/crcs/ectester/reader/test/CardDegenerateSuite.java @@ -0,0 +1,59 @@ +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.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class CardDegenerateSuite extends CardTestSuite { + + public CardDegenerateSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) { + super(writer, cfg, cardManager, "degenerate", "The degenerate suite tests whether the card rejects points outside of the curve during ECDH.", + "The tested points lie on a part of the plane for which some Edwards, Hessian and Huff form addition formulas work."); + } + + @Override + protected void runTests() throws Exception { + Map<String, EC_Key.Public> pubkeys = EC_Store.getInstance().getObjects(EC_Key.Public.class, "degenerate"); + List<Map.Entry<EC_Curve, List<EC_Key.Public>>> curveList = EC_Store.mapKeyToCurve(pubkeys.values()); + for (Map.Entry<EC_Curve, List<EC_Key.Public>> e : curveList) { + 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.TRANSFORMATION_NONE, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH, pub.flatten()); + ecdhTests.add(CommandTest.expect(ecdhCommand, Result.ExpectedValue.FAILURE, "Card correctly rejected point on degenerate curve.", "Card incorrectly accepted point on degenerate curve.")); + } + Test ecdh = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Perform ECDH with degenerate public points", ecdhTests.toArray(new Test[0])); + if (cfg.cleanup) { + Test cleanup = CommandTest.expect(new Command.Cleanup(this.card), Result.ExpectedValue.SUCCESS); + doTest(CompoundTest.greedyAllTry(Result.ExpectedValue.SUCCESS, "Degenerate curve test of " + curve.getId(), prepare, ecdh, cleanup)); + } else { + doTest(CompoundTest.greedyAllTry(Result.ExpectedValue.SUCCESS, "Degenerate curve test of " + curve.getId(), prepare, ecdh)); + } + + } + } +} diff --git a/src/cz/crcs/ectester/reader/test/CardEdgeCasesSuite.java b/src/cz/crcs/ectester/reader/test/CardEdgeCasesSuite.java new file mode 100644 index 0000000..dc489a0 --- /dev/null +++ b/src/cz/crcs/ectester/reader/test/CardEdgeCasesSuite.java @@ -0,0 +1,189 @@ +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_KAResult; +import cz.crcs.ectester.common.ec.EC_Key; +import cz.crcs.ectester.common.ec.EC_Params; +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.common.test.TestCallback; +import cz.crcs.ectester.common.util.ByteUtil; +import cz.crcs.ectester.common.util.ECUtil; +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 cz.crcs.ectester.reader.response.Response; +import javacard.security.CryptoException; +import javacard.security.KeyPair; + +import java.math.BigInteger; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.stream.Collectors; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class CardEdgeCasesSuite extends CardTestSuite { + public CardEdgeCasesSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) { + super(writer, cfg, cardManager, "edge-cases", "The edge-cases test suite tests various inputs to ECDH which may cause an implementation to achieve a certain edge-case state during it.", + "Some of the data is from the google/Wycheproof project. Tests include CVE-2017-10176 and CVE-2017-8932.", + "Various edge private key values are also tested."); + } + + @Override + protected void runTests() throws Exception { + Map<String, EC_KAResult> results = EC_Store.getInstance().getObjects(EC_KAResult.class, "wycheproof"); + List<Map.Entry<String, List<EC_KAResult>>> groupList = EC_Store.mapToPrefix(results.values()); + for (Map.Entry<String, List<EC_KAResult>> e : groupList) { + String description = null; + switch (e.getKey()) { + case "addsub": + description = "Tests for addition-subtraction chains."; + break; + case "cve_2017_10176": + description = "Tests for CVE-2017-10176."; + break; + case "cve_2017_8932": + description = "Tests for CVE-2017-8932."; + break; + } + + List<Test> groupTests = new LinkedList<>(); + List<Map.Entry<EC_Curve, List<EC_KAResult>>> curveList = EC_Store.mapResultToCurve(e.getValue()); + for (Map.Entry<EC_Curve, List<EC_KAResult>> c : curveList) { + EC_Curve curve = c.getKey(); + + List<Test> curveTests = new LinkedList<>(); + 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 prepareCurve = CompoundTest.greedyAll(Result.ExpectedValue.SUCCESS, "Prepare curve", allocate, set); + + List<EC_KAResult> values = c.getValue(); + for (EC_KAResult value : values) { + String id = value.getId(); + String privkeyId = value.getOneKey(); + String pubkeyId = value.getOtherKey(); + + EC_Key.Private privkey = EC_Store.getInstance().getObject(EC_Key.Private.class, privkeyId); + EC_Key.Public pubkey = EC_Store.getInstance().getObject(EC_Key.Public.class, pubkeyId); + + Test setPrivkey = CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.CURVE_external, privkey.getParams(), privkey.flatten()), Result.ExpectedValue.SUCCESS); + Test setPubkey = CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, pubkey.getParams(), pubkey.flatten()), Result.ExpectedValue.SUCCESS); + Test ecdhPreTest = CommandTest.expect(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH), Result.ExpectedValue.SUCCESS); + Test ecdh = CommandTest.function(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_TRUE, EC_Consts.TRANSFORMATION_NONE, value.getJavaCardKA()), new TestCallback<CommandTestable>() { + @Override + public Result apply(CommandTestable testable) { + Response.ECDH dh = (Response.ECDH) testable.getResponse(); + if (dh.getSW(0) == CryptoException.NO_SUCH_ALGORITHM) { + return new Result(Result.Value.SUCCESS, "ECDH algorithm unsupported."); + } + if (!dh.successful()) + return new Result(Result.Value.FAILURE, "ECDH was unsuccessful."); + if (!dh.hasSecret()) + return new Result(Result.Value.FAILURE, "ECDH response did not contain the derived secret."); + if (!ByteUtil.compareBytes(dh.getSecret(), 0, value.getData(0), 0, dh.secretLength())) { + int firstDiff = ByteUtil.diffBytes(dh.getSecret(), 0, value.getData(0), 0, dh.secretLength()); + System.err.println(ByteUtil.bytesToHex(dh.getSecret())); + System.err.println(ByteUtil.bytesToHex(value.getData(0))); + return new Result(Result.Value.FAILURE, "ECDH derived secret does not match the test-vector, first difference was at byte " + String.valueOf(firstDiff) + "."); + } + return new Result(Result.Value.SUCCESS); + } + }); + + Test prepare = CompoundTest.greedyAll(Result.ExpectedValue.SUCCESS, "Prepare", setPrivkey, setPubkey); + Test ka = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Do", ecdhPreTest, ecdh); + + Test one = CompoundTest.greedyAllTry(Result.ExpectedValue.SUCCESS, "Test " + id + ".", prepare, ka); + curveTests.add(one); + } + + Test curveTest = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Tests", curveTests.toArray(new Test[0])); + groupTests.add(CompoundTest.greedyAllTry(Result.ExpectedValue.SUCCESS, "Tests on " + curve.getId() + ".", prepareCurve, curveTest)); + } + doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, description, groupTests.toArray(new Test[0]))); + } + + Map<String, EC_Curve> curveMap = EC_Store.getInstance().getObjects(EC_Curve.class, "secg"); + List<EC_Curve> curves = curveMap.entrySet().stream().filter((e) -> e.getKey().endsWith("r1") && e.getValue().getField() == KeyPair.ALG_EC_FP).map(Map.Entry::getValue).collect(Collectors.toList()); + Random rand = new Random(); + for (EC_Curve curve : curves) { + Test key = runTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), KeyPair.ALG_EC_FP), Result.ExpectedValue.SUCCESS)); + if (!key.ok()) { + doTest(CompoundTest.all(Result.ExpectedValue.FAILURE, "No support for " + curve.getBits() + "b ALG_EC_FP.", key)); + continue; + } + 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 setup = CompoundTest.all(Result.ExpectedValue.SUCCESS, "KeyPair setup.", key, set, generate); + + Test zeroS = ecdhTest(new Command.Transform(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, EC_Consts.PARAMETER_S, EC_Consts.TRANSFORMATION_ZERO), "ECDH with S = 0.", Result.ExpectedValue.FAILURE, Result.ExpectedValue.FAILURE); + Test oneS = ecdhTest(new Command.Transform(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, EC_Consts.PARAMETER_S, EC_Consts.TRANSFORMATION_ONE), "ECDH with S = 1.", Result.ExpectedValue.FAILURE, Result.ExpectedValue.FAILURE); + + byte[] r = curve.getParam(EC_Consts.PARAMETER_R)[0]; + BigInteger R = new BigInteger(1, r); + BigInteger smaller = new BigInteger(curve.getBits(), rand).mod(R); + BigInteger diff = R.divide(BigInteger.valueOf(10)); + BigInteger randDiff = new BigInteger(diff.bitLength(), rand).mod(diff); + BigInteger larger = R.add(randDiff); + + EC_Params smallerParams = makeParams(smaller); + Test smallerS = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, smallerParams.getParams(), smallerParams.flatten()), "ECDH with S < r.", Result.ExpectedValue.SUCCESS, Result.ExpectedValue.SUCCESS); + + EC_Params exactParams = makeParams(R); + Test exactS = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, exactParams.getParams(), exactParams.flatten()), "ECDH with S = r.", Result.ExpectedValue.FAILURE, Result.ExpectedValue.FAILURE); + + EC_Params largerParams = makeParams(larger); + Test largerS = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, largerParams.getParams(), largerParams.flatten()), "ECDH with S > r.", Result.ExpectedValue.ANY, Result.ExpectedValue.ANY); + + BigInteger rm1 = R.subtract(BigInteger.ONE); + BigInteger rp1 = R.add(BigInteger.ONE); + + EC_Params rm1Params = makeParams(rm1); + Test rm1S = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, rm1Params.getParams(), rm1Params.flatten()), "ECDH with S = r - 1.", Result.ExpectedValue.SUCCESS, Result.ExpectedValue.SUCCESS); + + EC_Params rp1Params = makeParams(rp1); + Test rp1S = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, rp1Params.getParams(), rp1Params.flatten()), "ECDH with S = r + 1.", Result.ExpectedValue.ANY, Result.ExpectedValue.ANY); + + byte[] k = curve.getParam(EC_Consts.PARAMETER_K)[0]; + BigInteger K = new BigInteger(1, k); + BigInteger kr = K.multiply(R); + BigInteger krm1 = kr.subtract(BigInteger.ONE); + BigInteger krp1 = kr.add(BigInteger.ONE); + + EC_Params krParams = makeParams(kr); + Test krS /*ONE!*/ = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, krParams.getParams(), krParams.flatten()), "ECDH with S = k * r.", Result.ExpectedValue.FAILURE, Result.ExpectedValue.FAILURE); + + EC_Params krm1Params = makeParams(krm1); + Test krm1S = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, krm1Params.getParams(), krm1Params.flatten()), "ECDH with S = (k * r) - 1.", Result.ExpectedValue.FAILURE, Result.ExpectedValue.FAILURE); + + EC_Params krp1Params = makeParams(krp1); + Test krp1S = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, krp1Params.getParams(), krp1Params.flatten()), "ECDH with S = (k * r) + 1.", Result.ExpectedValue.FAILURE, Result.ExpectedValue.FAILURE); + + doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Tests with edge-case private key values over " + curve.getId() + ".", setup, zeroS, oneS, smallerS, exactS, largerS, rm1S, rp1S, krS, krm1S, krp1S)); + } + } + + private Test ecdhTest(Command setPriv, String desc, Result.ExpectedValue setExpect, Result.ExpectedValue ecdhExpect) { + Test set = CommandTest.expect(setPriv, setExpect); + Test ecdh = CommandTest.expect(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_TRUE, EC_Consts.TRANSFORMATION_NONE, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH), ecdhExpect); + + return CompoundTest.any(Result.ExpectedValue.SUCCESS, desc, set, ecdh); + } + + private EC_Params makeParams(BigInteger s) { + return makeParams(ECUtil.toByteArray(s, s.bitLength())); + } + + private EC_Params makeParams(byte[] s) { + return new EC_Params(EC_Consts.PARAMETER_S, new byte[][]{s}); + } +} diff --git a/src/cz/crcs/ectester/reader/test/CardInvalidCurvesSuite.java b/src/cz/crcs/ectester/reader/test/CardInvalidCurvesSuite.java deleted file mode 100644 index 8424d45..0000000 --- a/src/cz/crcs/ectester/reader/test/CardInvalidCurvesSuite.java +++ /dev/null @@ -1,67 +0,0 @@ -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.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 javacard.security.KeyPair; - -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -import static cz.crcs.ectester.common.test.Result.ExpectedValue; - -/** - * @author Jan Jancar johny@neuromancer.sk - */ -public class CardInvalidCurvesSuite extends CardTestSuite { - - public CardInvalidCurvesSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) { - super(writer, cfg, cardManager, "invalid", "The invalid curve suite tests whether the card rejects points outside of the curve during ECDH."); - } - - @Override - protected void runTests() throws Exception { - /* Set original curves (secg/nist/brainpool). Generate local. - * Try ECDH with invalid public keys of increasing (or decreasing) order. - */ - Map<String, EC_Key.Public> pubkeys = EC_Store.getInstance().getObjects(EC_Key.Public.class, "invalid"); - 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()); - if (curve.getBits() != cfg.bits && !cfg.all) { - continue; - } - if (curve.getField() == KeyPair.ALG_EC_FP && !cfg.primeField || curve.getField() == KeyPair.ALG_EC_F2M && !cfg.binaryField) { - continue; - } - 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(); - - doTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), ExpectedValue.SUCCESS)); - doTest(CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), ExpectedValue.SUCCESS)); - doTest(CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_LOCAL), ExpectedValue.SUCCESS)); - 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, ExpectedValue.FAILURE, "Card correctly rejected point on invalid curve.", "Card incorrectly accepted point on invalid curve.")); - } - doTest(CompoundTest.all(ExpectedValue.SUCCESS, "Invalid curve test of " + curve.getId(), ecdhTests.toArray(new Test[0]))); - new Command.Cleanup(this.card).send(); - } - } -} diff --git a/src/cz/crcs/ectester/reader/test/CardInvalidSuite.java b/src/cz/crcs/ectester/reader/test/CardInvalidSuite.java new file mode 100644 index 0000000..59a427f --- /dev/null +++ b/src/cz/crcs/ectester/reader/test/CardInvalidSuite.java @@ -0,0 +1,81 @@ +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.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Random; + +import static cz.crcs.ectester.common.test.Result.ExpectedValue; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class CardInvalidSuite extends CardTestSuite { + + public CardInvalidSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) { + super(writer, cfg, cardManager, "invalid", "The invalid curve suite tests whether the card rejects points outside of the curve during ECDH."); + } + + @Override + protected void runTests() throws Exception { + /* Set original curves (secg/nist/brainpool). Generate local. + * Try ECDH with invalid public keys of increasing order. + */ + Map<String, EC_Key.Public> pubkeys = EC_Store.getInstance().getObjects(EC_Key.Public.class, "invalid"); + List<Map.Entry<EC_Curve, List<EC_Key.Public>>> curveList = EC_Store.mapKeyToCurve(pubkeys.values()); + for (Map.Entry<EC_Curve, List<EC_Key.Public>> e : curveList) { + 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()), ExpectedValue.SUCCESS); + Test set = CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), ExpectedValue.SUCCESS); + Test generate = CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_LOCAL), ExpectedValue.SUCCESS); + + Test prepare = CompoundTest.all(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.TRANSFORMATION_NONE, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH, pub.flatten()); + ecdhTests.add(CommandTest.expect(ecdhCommand, ExpectedValue.FAILURE, "Card correctly rejected point on invalid curve.", "Card incorrectly accepted point on invalid curve.")); + } + Test ecdh = CompoundTest.all(ExpectedValue.SUCCESS, "Perform ECDH with invalid public points", ecdhTests.toArray(new Test[0])); + + Random r = new Random(); + byte[] raw = new byte[128]; + byte[] sig = new byte[40]; + r.nextBytes(raw); + r.nextBytes(sig); + + List<Test> ecdsaTests = new LinkedList<>(); + for (EC_Key.Public pub : keys) { + Command setCommand = new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, pub.getParams(), pub.flatten()); + Test setTest = CommandTest.expect(setCommand, Result.ExpectedValue.ANY); + Command ecdsaCommand = new Command.ECDSA_verify(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.Signature_ALG_ECDSA_SHA, raw, sig); + Test ecdsaTest = CommandTest.expect(ecdsaCommand, Result.ExpectedValue.FAILURE); + ecdsaTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Verify random ECDSA signature by " + pub.getId(), setTest, ecdsaTest)); + } + Test ecdsa = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Verify random ECDSA signature by invalid public points", ecdsaTests.toArray(new Test[0])); + + Test tests = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Test ECDH and ECDSA with points on invalid curves.", ecdh, ecdsa); + if (cfg.cleanup) { + Test cleanup = CommandTest.expect(new Command.Cleanup(this.card), ExpectedValue.SUCCESS); + doTest(CompoundTest.greedyAllTry(ExpectedValue.SUCCESS, "Invalid curve test of " + curve.getId(), prepare, tests, cleanup)); + } else { + doTest(CompoundTest.greedyAllTry(ExpectedValue.SUCCESS, "Invalid curve test of " + curve.getId(), prepare, tests)); + } + } + } +} diff --git a/src/cz/crcs/ectester/reader/test/CardMiscSuite.java b/src/cz/crcs/ectester/reader/test/CardMiscSuite.java new file mode 100644 index 0000000..e568f67 --- /dev/null +++ b/src/cz/crcs/ectester/reader/test/CardMiscSuite.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.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.common.util.CardUtil; +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.Map; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class CardMiscSuite extends CardTestSuite { + + public CardMiscSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) { + super(writer, cfg, cardManager, "miscellaneous", "Some miscellaneous tests, tries ECDH and ECDSA over supersingular curves, anomalous curves and some Barreto-Naehrig curves with small embedding degree and CM discriminant."); + } + + @Override + protected void runTests() throws Exception { + Map<String, EC_Curve> anCurves = EC_Store.getInstance().getObjects(EC_Curve.class, "anomalous"); + Map<String, EC_Curve> ssCurves = EC_Store.getInstance().getObjects(EC_Curve.class, "supersingular"); + Map<String, EC_Curve> bnCurves = EC_Store.getInstance().getObjects(EC_Curve.class, "Barreto-Naehrig"); + + testCurves(anCurves, "anomalous", Result.ExpectedValue.FAILURE); + testCurves(ssCurves, "supersingular", Result.ExpectedValue.FAILURE); + testCurves(bnCurves, "Barreto-Naehrig", Result.ExpectedValue.ANY); + } + + private void testCurves(Map<String, EC_Curve> curves, String catName, Result.ExpectedValue expected) throws Exception { + for (EC_Curve curve : curves.values()) { + Test allocateFirst = runTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), Result.ExpectedValue.SUCCESS)); + if (!allocateFirst.ok()) { + doTest(CompoundTest.all(Result.ExpectedValue.FAILURE, "No support for " + curve.getBits() + "b " + CardUtil.getKeyTypeString(curve.getField()) + ".", allocateFirst)); + continue; + } + + 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_BOTH), Result.ExpectedValue.ANY); + Test ka = CommandTest.expect(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH), expected); + Test sig = CommandTest.expect(new Command.ECDSA(this.card, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.Signature_ALG_ECDSA_SHA, ECTesterApplet.EXPORT_FALSE, null), expected); + Test perform = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Perform ECDH and ECDSA", ka, sig); + + if (cfg.cleanup) { + Test cleanup = CommandTest.expect(new Command.Cleanup(this.card), Result.ExpectedValue.SUCCESS); + doTest(CompoundTest.greedyAll(Result.ExpectedValue.SUCCESS, "Tests over " + curve.getBits() + " " + catName + " curve: " + curve.getId() + ".", allocateFirst, set, generate, perform, cleanup)); + } else { + doTest(CompoundTest.greedyAll(Result.ExpectedValue.SUCCESS, "Tests over " + curve.getBits() + " " + catName + " curve: " + curve.getId() + ".", allocateFirst, set, generate, perform)); + } + } + } +} diff --git a/src/cz/crcs/ectester/reader/test/CardTestSuite.java b/src/cz/crcs/ectester/reader/test/CardTestSuite.java index 0eccd16..3578f9c 100644 --- a/src/cz/crcs/ectester/reader/test/CardTestSuite.java +++ b/src/cz/crcs/ectester/reader/test/CardTestSuite.java @@ -12,7 +12,7 @@ public abstract class CardTestSuite extends TestSuite { ECTesterReader.Config cfg; CardMngr card; - CardTestSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager, String name, String description) { + CardTestSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager, String name, String... description) { super(writer, name, description); this.card = cardManager; this.cfg = cfg; diff --git a/src/cz/crcs/ectester/reader/test/CardTestVectorSuite.java b/src/cz/crcs/ectester/reader/test/CardTestVectorSuite.java index 73c6621..052e480 100644 --- a/src/cz/crcs/ectester/reader/test/CardTestVectorSuite.java +++ b/src/cz/crcs/ectester/reader/test/CardTestVectorSuite.java @@ -4,14 +4,16 @@ import cz.crcs.ectester.applet.ECTesterApplet; import cz.crcs.ectester.applet.EC_Consts; import cz.crcs.ectester.common.ec.*; import cz.crcs.ectester.common.output.TestWriter; -import cz.crcs.ectester.common.test.*; +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.common.test.TestCallback; import cz.crcs.ectester.common.util.ByteUtil; 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 cz.crcs.ectester.reader.response.Response; -import javacard.security.KeyPair; import java.io.IOException; import java.util.LinkedList; @@ -38,12 +40,6 @@ public class CardTestVectorSuite extends CardTestSuite { Map<String, EC_KAResult> results = EC_Store.getInstance().getObjects(EC_KAResult.class, "test"); for (EC_KAResult result : results.values()) { EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, result.getCurve()); - if (curve.getBits() != cfg.bits && !cfg.all) { - continue; - } - if (curve.getField() == KeyPair.ALG_EC_FP && !cfg.primeField || curve.getField() == KeyPair.ALG_EC_F2M && !cfg.binaryField) { - continue; - } EC_Params onekey = EC_Store.getInstance().getObject(EC_Keypair.class, result.getOneKey()); if (onekey == null) { onekey = EC_Store.getInstance().getObject(EC_Key.Private.class, result.getOneKey()); @@ -61,7 +57,7 @@ public class CardTestVectorSuite extends CardTestSuite { testVector.add(CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), ExpectedValue.SUCCESS)); testVector.add(CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.CURVE_external, EC_Consts.PARAMETER_S, onekey.flatten(EC_Consts.PARAMETER_S)), ExpectedValue.SUCCESS)); testVector.add(CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, EC_Consts.PARAMETER_W, otherkey.flatten(EC_Consts.PARAMETER_W)), ExpectedValue.SUCCESS)); - testVector.add(CommandTest.function(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_TRUE, EC_Consts.CORRUPTION_NONE, result.getJavaCardKA()), new TestCallback<CommandTestable>() { + testVector.add(CommandTest.function(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.EXPORT_TRUE, EC_Consts.TRANSFORMATION_NONE, result.getJavaCardKA()), new TestCallback<CommandTestable>() { @Override public Result apply(CommandTestable testable) { Response.ECDH dh = (Response.ECDH) testable.getResponse(); @@ -76,8 +72,10 @@ public class CardTestVectorSuite extends CardTestSuite { return new Result(Value.SUCCESS); } })); - doTest(CompoundTest.all(ExpectedValue.SUCCESS, "Test vector " + result.getId(), testVector.toArray(new Test[0]))); - new Command.Cleanup(this.card).send(); + if (cfg.cleanup) { + testVector.add(CommandTest.expect(new Command.Cleanup(this.card), ExpectedValue.SUCCESS)); + } + doTest(CompoundTest.greedyAll(ExpectedValue.SUCCESS, "Test vector " + result.getId(), testVector.toArray(new Test[0]))); } } } diff --git a/src/cz/crcs/ectester/reader/test/CardTwistSuite.java b/src/cz/crcs/ectester/reader/test/CardTwistSuite.java new file mode 100644 index 0000000..1e1f5f3 --- /dev/null +++ b/src/cz/crcs/ectester/reader/test/CardTwistSuite.java @@ -0,0 +1,75 @@ +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.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Random; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class CardTwistSuite extends CardTestSuite { + public CardTwistSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) { + super(writer, cfg, cardManager, "twist", "The twist test suite tests whether the card correctly rejects points on the quadratic twist of the curve during ECDH."); + } + + @Override + protected void runTests() throws Exception { + Map<String, EC_Key.Public> pubkeys = EC_Store.getInstance().getObjects(EC_Key.Public.class, "twist"); + List<Map.Entry<EC_Curve, List<EC_Key.Public>>> curveList = EC_Store.mapKeyToCurve(pubkeys.values()); + for (Map.Entry<EC_Curve, List<EC_Key.Public>> e : curveList) { + 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.TRANSFORMATION_NONE, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH, pub.flatten()); + ecdhTests.add(CommandTest.expect(ecdhCommand, Result.ExpectedValue.FAILURE, "Card correctly rejected point on twist.", "Card incorrectly accepted point on twist.")); + } + Test ecdh = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Perform ECDH with public points on twist", ecdhTests.toArray(new Test[0])); + + Random r = new Random(); + byte[] raw = new byte[128]; + byte[] sig = new byte[40]; + r.nextBytes(raw); + r.nextBytes(sig); + + List<Test> ecdsaTests = new LinkedList<>(); + for (EC_Key.Public pub : keys) { + Command setCommand = new Command.Set(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.CURVE_external, pub.getParams(), pub.flatten()); + Test setTest = CommandTest.expect(setCommand, Result.ExpectedValue.ANY); + Command ecdsaCommand = new Command.ECDSA_verify(this.card, ECTesterApplet.KEYPAIR_REMOTE, EC_Consts.Signature_ALG_ECDSA_SHA, raw, sig); + Test ecdsaTest = CommandTest.expect(ecdsaCommand, Result.ExpectedValue.FAILURE); + ecdsaTests.add(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Verify random ECDSA signature by " + pub.getId(), setTest, ecdsaTest)); + } + Test ecdsa = CompoundTest.all(Result.ExpectedValue.SUCCESS, "Verify random ECDSA signature by public points on twist", ecdsaTests.toArray(new Test[0])); + + Test tests = CompoundTest.all(Result.ExpectedValue.SUCCESS, ecdh, ecdsa); + if (cfg.cleanup) { + Test cleanup = CommandTest.expect(new Command.Cleanup(this.card), Result.ExpectedValue.SUCCESS); + doTest(CompoundTest.greedyAllTry(Result.ExpectedValue.SUCCESS, "Twist test of " + curve.getId(), prepare, tests, cleanup)); + } else { + doTest(CompoundTest.greedyAllTry(Result.ExpectedValue.SUCCESS, "Twist test of " + curve.getId(), prepare, tests)); + } + } + } +} diff --git a/src/cz/crcs/ectester/reader/test/CardTwistTestSuite.java b/src/cz/crcs/ectester/reader/test/CardTwistTestSuite.java deleted file mode 100644 index c43b234..0000000 --- a/src/cz/crcs/ectester/reader/test/CardTwistTestSuite.java +++ /dev/null @@ -1,62 +0,0 @@ -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 javacard.security.KeyPair; - -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -/** - * @author Jan Jancar johny@neuromancer.sk - */ -public class CardTwistTestSuite extends CardTestSuite { - public CardTwistTestSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) { - super(writer, cfg, cardManager, "twist", "The twist test suite tests whether the card correctly rejects points on the quadratic twist of the curve during ECDH."); - } - - @Override - protected void runTests() throws Exception { - Map<String, EC_Key.Public> pubkeys = EC_Store.getInstance().getObjects(EC_Key.Public.class, "twist"); - 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()); - if (curve.getBits() != cfg.bits && !cfg.all) { - continue; - } - if (curve.getField() == KeyPair.ALG_EC_FP && !cfg.primeField || curve.getField() == KeyPair.ALG_EC_F2M && !cfg.binaryField) { - continue; - } - 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(); - - doTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), Result.ExpectedValue.SUCCESS)); - doTest(CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), Result.ExpectedValue.SUCCESS)); - doTest(CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_LOCAL), Result.ExpectedValue.SUCCESS)); - 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 twist.", "Card incorrectly accepted point on twist.")); - } - doTest(CompoundTest.all(Result.ExpectedValue.SUCCESS, "Twist test of " + curve.getId(), ecdhTests.toArray(new Test[0]))); - new Command.Cleanup(this.card).send(); - } - } -} diff --git a/src/cz/crcs/ectester/reader/test/CardWrongCurvesSuite.java b/src/cz/crcs/ectester/reader/test/CardWrongCurvesSuite.java deleted file mode 100644 index cac8fab..0000000 --- a/src/cz/crcs/ectester/reader/test/CardWrongCurvesSuite.java +++ /dev/null @@ -1,58 +0,0 @@ -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.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 javacard.security.KeyPair; - -import java.util.Map; - -/** - * @author Jan Jancar johny@neuromancer.sk - */ -public class CardWrongCurvesSuite extends CardTestSuite { - - public CardWrongCurvesSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) { - super(writer, cfg, cardManager, "wrong", "The wrong curve suite run whether the card rejects domain parameters which are not curves."); - } - - @Override - protected void runTests() throws Exception { - /* Just do the default run on the wrong curves. - * These should generally fail, the curves aren't curves. - */ - Map<String, EC_Curve> curves = EC_Store.getInstance().getObjects(EC_Curve.class, "wrong"); - for (Map.Entry<String, EC_Curve> e : curves.entrySet()) { - EC_Curve curve = e.getValue(); - if (curve.getBits() != cfg.bits && !cfg.all) { - continue; - } - if (curve.getField() == KeyPair.ALG_EC_FP && !cfg.primeField || curve.getField() == KeyPair.ALG_EC_F2M && !cfg.binaryField) { - continue; - } - Test key = doTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), Result.ExpectedValue.SUCCESS)); - if (!key.ok()) { - continue; - } - Test set = runTest(CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), Result.ExpectedValue.SUCCESS)); - Test generate = runTest(CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_BOTH), Result.ExpectedValue.SUCCESS)); - doTest(CompoundTest.any(Result.ExpectedValue.FAILURE, "Set wrong curve and generate keypairs, should fail." ,set, generate)); - - for (byte kaType : EC_Consts.KA_TYPES) { - Test allocate = runTest(CommandTest.expect(new Command.AllocateKeyAgreement(this.card, kaType), Result.ExpectedValue.SUCCESS)); - if (allocate.ok()) { - Test ka = runTest(CommandTest.expect(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.CORRUPTION_NONE, kaType), Result.ExpectedValue.FAILURE)); - doTest(CompoundTest.any(Result.ExpectedValue.FAILURE, "Allocate and perform KA, should fail.", allocate, ka)); - } - } - } - } -} diff --git a/src/cz/crcs/ectester/reader/test/CardWrongSuite.java b/src/cz/crcs/ectester/reader/test/CardWrongSuite.java new file mode 100644 index 0000000..8bc7c90 --- /dev/null +++ b/src/cz/crcs/ectester/reader/test/CardWrongSuite.java @@ -0,0 +1,227 @@ +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_Params; +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.common.util.ByteUtil; +import cz.crcs.ectester.common.util.CardUtil; +import cz.crcs.ectester.common.util.ECUtil; +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 javacard.security.KeyPair; + +import java.math.BigInteger; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Random; + +import static cz.crcs.ectester.common.test.Result.ExpectedValue; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class CardWrongSuite extends CardTestSuite { + + public CardWrongSuite(TestWriter writer, ECTesterReader.Config cfg, CardMngr cardManager) { + super(writer, cfg, cardManager, "wrong", "The wrong curve suite tests whether the card rejects domain parameters which are not curves."); + } + + @Override + protected void runTests() throws Exception { + /* Just do the default run on the wrong curves. + * These should generally fail, the curves aren't curves. + */ + Map<String, EC_Curve> curves = EC_Store.getInstance().getObjects(EC_Curve.class, "wrong"); + for (Map.Entry<String, EC_Curve> e : curves.entrySet()) { + EC_Curve curve = e.getValue(); + List<Test> tests = new LinkedList<>(); + Test key = runTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, curve.getBits(), curve.getField()), ExpectedValue.SUCCESS)); + if (!key.ok()) { + doTest(CompoundTest.all(ExpectedValue.FAILURE, "No support for " + curve.getBits() + "b " + CardUtil.getKeyTypeString(curve.getField()), key)); + continue; + } + tests.add(key); + Test set = runTest(CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()), ExpectedValue.FAILURE)); + Test generate = runTest(CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_BOTH), ExpectedValue.FAILURE)); + Test setup = runTest(CompoundTest.any(ExpectedValue.SUCCESS, "Set wrong curve and generate keypairs.", set, generate)); + tests.add(setup); + + for (byte kaType : EC_Consts.KA_TYPES) { + Test allocate = runTest(CommandTest.expect(new Command.AllocateKeyAgreement(this.card, kaType), ExpectedValue.SUCCESS)); + if (allocate.ok()) { + Test ka = runTest(CommandTest.expect(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, kaType), ExpectedValue.FAILURE)); + Test kaTest = runTest(CompoundTest.all(ExpectedValue.SUCCESS, "Allocate and perform KA.", allocate, ka)); + tests.add(kaTest); + } + } + doTest(CompoundTest.function((tsts) -> { + for (int i = 0; i < tsts.length; ++i) { + if (i != 1 && !tsts[i].ok()) { + return new Result(Result.Value.FAILURE, "Some tests did not have the expected result."); + } + } + return new Result(Result.Value.SUCCESS, "All tests had the expected result."); + }, "Wrong curve test of " + curve.getBits() + "b " + CardUtil.getKeyTypeString(curve.getField()), tests.toArray(new Test[0]))); + } + /* + * Do some interesting tests with corrupting the custom curves. + * For prime field: + * - p = 0 + * - p = 1 + * - p is a square of a prime + * - p is a composite q * s with q, s primes + * - TODO: p divides discriminant + */ + Random r = new Random(); + for (short keyLength : EC_Consts.FP_SIZES) { + byte curve = EC_Consts.getCurve(keyLength, KeyPair.ALG_EC_FP); + Test key = runTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, keyLength, KeyPair.ALG_EC_FP), ExpectedValue.SUCCESS)); + if (!key.ok()) { + doTest(CompoundTest.all(ExpectedValue.FAILURE, "No support for " + keyLength + "b ALG_EC_FP.", key)); + continue; + } + Test set = CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, curve, EC_Consts.PARAMETERS_DOMAIN_FP, null), ExpectedValue.SUCCESS); + Test setup = CompoundTest.all(ExpectedValue.SUCCESS, "KeyPair setup.", key, set); + + Test prime0 = ecdhTest(new Command.Transform(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.KEY_BOTH, EC_Consts.PARAMETER_FP, EC_Consts.TRANSFORMATION_ZERO), "Set p = 0.", "ECDH with p = 0."); + Test prime1 = ecdhTest(new Command.Transform(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.KEY_BOTH, EC_Consts.PARAMETER_FP, EC_Consts.TRANSFORMATION_ONE), "Set p = 1.", "ECDH with p = 1."); + + short keyHalf = (short) (keyLength / 2); + BigInteger prime = new BigInteger(keyHalf, 50, r); + BigInteger primePow = prime.pow(2); + byte[] primePowBytes = ECUtil.toByteArray(primePow, keyLength); + EC_Params primePowData = new EC_Params(EC_Consts.PARAMETER_FP, new byte[][]{primePowBytes}); + Test primePower = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, primePowData.getParams(), primePowData.flatten()), "Set p = square of a prime.", "ECDH with p = q^2."); + + BigInteger q = new BigInteger(keyHalf, r); + BigInteger s = new BigInteger(keyHalf, r); + BigInteger compositeValue = q.multiply(s); + byte[] compositeBytes = ECUtil.toByteArray(compositeValue, keyLength); + EC_Params compositeData = new EC_Params(EC_Consts.PARAMETER_FP, new byte[][]{compositeBytes}); + Test composite = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, compositeData.getParams(), compositeData.flatten()), "Set p = product of two primes.", "ECDH with p = q * s."); + + Test wrongPrime = CompoundTest.all(ExpectedValue.SUCCESS, "Tests with corrupted prime parameter.", prime0, prime1, primePower, composite); + + Test resetSetup = CompoundTest.all(ExpectedValue.SUCCESS, "Reset keypair.", set.clone()); + + Test randomG = ecdhTest(new Command.Transform(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.KEY_BOTH, EC_Consts.PARAMETER_G, (short) (EC_Consts.TRANSFORMATION_FULLRANDOM | EC_Consts.TRANSFORMATION_04_MASK)), "Set G = random non-point/point-like.", "ECDH with non-point G."); + Test fullRandomG = ecdhTest(new Command.Transform(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.KEY_BOTH, EC_Consts.PARAMETER_G, EC_Consts.TRANSFORMATION_FULLRANDOM), "Set G = random data.", "ECDH with G = random data."); + Test zeroG = ecdhTest(new Command.Transform(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.KEY_BOTH, EC_Consts.PARAMETER_G, EC_Consts.TRANSFORMATION_INFINITY), "Set G = inifnity.", "ECDH with G = infinity."); + Test wrongG = CompoundTest.all(ExpectedValue.SUCCESS, "Tests with corrupted G parameter.", randomG, fullRandomG, zeroG); + + byte[] originalR = new byte[keyLength]; + EC_Consts.getCurveParameter(curve, EC_Consts.PARAMETER_R, originalR, (short) 0); + BigInteger originalBigR = new BigInteger(1, originalR); + + Test zeroR = ecdhTest(new Command.Transform(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, EC_Consts.PARAMETER_R, EC_Consts.TRANSFORMATION_ZERO), "Set R = 0.", "ECDH with R = 0."); + Test oneR = ecdhTest(new Command.Transform(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, EC_Consts.PARAMETER_R, EC_Consts.TRANSFORMATION_ONE), "Set R = 1.", "ECDH with R = 1."); + + BigInteger prevPrimeR; + do { + prevPrimeR = BigInteger.probablePrime(originalBigR.bitLength() - 1, r); + } while (prevPrimeR.compareTo(originalBigR) >= 0); + byte[] prevRBytes = ECUtil.toByteArray(prevPrimeR, keyLength); + EC_Params prevRData = new EC_Params(EC_Consts.PARAMETER_R, new byte[][]{prevRBytes}); + Test prevprimeWrongR = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, prevRData.getParams(), prevRData.flatten()), "Set R = some prime (but [r]G != infinity) smaller than original R.", "ECDH with wrong R, prevprime."); + + BigInteger nextPrimeR = originalBigR.nextProbablePrime(); + byte[] nextRBytes = ECUtil.toByteArray(nextPrimeR, keyLength); + EC_Params nextRData = new EC_Params(EC_Consts.PARAMETER_R, new byte[][]{nextRBytes}); + Test nextprimeWrongR = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, nextRData.getParams(), nextRData.flatten()), "Set R = some prime (but [r]G != infinity) larger than original R.", "ECDH with wrong R, nextprime."); + + byte[] nonprimeRBytes = nextRBytes.clone(); + nonprimeRBytes[0] ^= 1; + EC_Params nonprimeWrongRData = new EC_Params(EC_Consts.PARAMETER_R, new byte[][]{nonprimeRBytes}); + Test nonprimeWrongR = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, nonprimeWrongRData.getParams(), nonprimeWrongRData.flatten()), "Set R = some composite (but [r]G != infinity).", "ECDH with wrong R, composite."); + + Test wrongR = CompoundTest.all(ExpectedValue.SUCCESS, "Tests with corrupted R parameter.", zeroR, oneR, prevprimeWrongR, nextprimeWrongR, nonprimeWrongR); + + byte[] kRaw = new byte[]{(byte) 0xff}; + EC_Params kData = new EC_Params(EC_Consts.PARAMETER_K, new byte[][]{kRaw}); + Test bigK = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, kData.getParams(), kData.flatten()), "", ""); + + byte[] kZero = new byte[]{(byte) 0}; + EC_Params kZeroData = new EC_Params(EC_Consts.PARAMETER_K, new byte[][]{kZero}); + Test zeroK = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, kZeroData.getParams(), kZeroData.flatten()), "", ""); + + Test wrongK = CompoundTest.all(ExpectedValue.SUCCESS, "Tests with corrupted K parameter.", bigK, zeroK); + + doTest(CompoundTest.all(ExpectedValue.SUCCESS, "Tests of " + keyLength + "b " + CardUtil.getKeyTypeString(KeyPair.ALG_EC_FP), setup, wrongPrime, resetSetup, wrongG, resetSetup.clone(), wrongR, resetSetup.clone(), wrongK, resetSetup.clone())); + } + + /* + * For binary field: + * - e1, e2 or e3 is larger than m. + * - e1 = e2 = e3 = 0 + */ + for (short keyLength : EC_Consts.F2M_SIZES) { + byte curve = EC_Consts.getCurve(keyLength, KeyPair.ALG_EC_F2M); + Test key = runTest(CommandTest.expect(new Command.Allocate(this.card, ECTesterApplet.KEYPAIR_BOTH, keyLength, KeyPair.ALG_EC_F2M), ExpectedValue.SUCCESS)); + if (!key.ok()) { + doTest(CompoundTest.all(ExpectedValue.FAILURE, "No support for " + keyLength + "b ALG_EC_F2M.", key)); + continue; + } + Test set = CommandTest.expect(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, curve, EC_Consts.PARAMETERS_DOMAIN_F2M, null), ExpectedValue.SUCCESS); + Test setup = CompoundTest.all(ExpectedValue.SUCCESS, "KeyPair setup.", key, set); + + Test coeff0 = ecdhTest(new Command.Transform(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.KEY_BOTH, EC_Consts.PARAMETER_F2M, EC_Consts.TRANSFORMATION_ZERO), "Set e1 = e2 = e3 = 0.", "ECDH with wrong field polynomial: x^" + keyLength); + + short e1 = (short) (2 * keyLength); + short e2 = (short) (3 * keyLength); + short e3 = (short) (4 * keyLength); + byte[][] coeffBytes = new byte[][]{ + ByteUtil.shortToBytes(keyLength), + ByteUtil.shortToBytes(e1), + ByteUtil.shortToBytes(e2), + ByteUtil.shortToBytes(e3)}; + EC_Params coeffParams = new EC_Params(EC_Consts.PARAMETER_F2M, coeffBytes); + Test coeffLarger = ecdhTest(new Command.Set(this.card, ECTesterApplet.KEYPAIR_BOTH, EC_Consts.CURVE_external, coeffParams.getParams(), coeffParams.flatten()), "Set e1=" + e1 + ", e2=" + e2 + ", e3=" + e3, "ECDH with wrong field poly, powers larger than " + keyLength); + + Test wrong = CompoundTest.all(ExpectedValue.SUCCESS, "Tests with corrupted field polynomial parameter.", coeff0, coeffLarger); + doTest(CompoundTest.all(ExpectedValue.SUCCESS, "Tests of " + keyLength + "b " + CardUtil.getKeyTypeString(KeyPair.ALG_EC_F2M), setup, wrong)); + } + + /* + * TODO: tests for both Fp and F2m: + * - generator not on curve, + * - generator not on proper subgroup of curve(as specified by order/cofactor), + * - wrong order, + * - wrong cofactor. + */ + } + + private Test ecdhTest(Command setupCmd, String prepareDesc, String fullDesc) { + Test setup = CommandTest.expect(setupCmd, ExpectedValue.FAILURE); + Test generate = CommandTest.expect(new Command.Generate(this.card, ECTesterApplet.KEYPAIR_BOTH), ExpectedValue.FAILURE); + Test preparePhase = CompoundTest.any(ExpectedValue.SUCCESS, prepareDesc, setup, generate); + Test allocateECDH = CommandTest.expect(new Command.AllocateKeyAgreement(this.card, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH), ExpectedValue.SUCCESS); + Test ecdh = CommandTest.expect(new Command.ECDH(this.card, ECTesterApplet.KEYPAIR_LOCAL, ECTesterApplet.KEYPAIR_REMOTE, ECTesterApplet.EXPORT_FALSE, EC_Consts.TRANSFORMATION_NONE, EC_Consts.KeyAgreement_ALG_EC_SVDP_DH), ExpectedValue.FAILURE); + + + return CompoundTest.function((tests) -> { + if (preparePhase.ok() | !allocateECDH.ok() | ecdh.ok()) { + return new Result(Result.Value.SUCCESS, "All tests had the expected result."); + } else { + return new Result(Result.Value.FAILURE, "Some tests did not have the expected result."); + } + }, (tests) -> { + preparePhase.run(); + if (preparePhase.ok()) { + return; + } + allocateECDH.run(); + if (!allocateECDH.ok()) { + return; + } + ecdh.run(); + }, fullDesc, preparePhase, allocateECDH, ecdh); + } +} diff --git a/src/cz/crcs/ectester/reader/test/CommandTest.java b/src/cz/crcs/ectester/reader/test/CommandTest.java index a08d820..d57dc17 100644 --- a/src/cz/crcs/ectester/reader/test/CommandTest.java +++ b/src/cz/crcs/ectester/reader/test/CommandTest.java @@ -3,13 +3,14 @@ package cz.crcs.ectester.reader.test; import cz.crcs.ectester.common.test.Result; import cz.crcs.ectester.common.test.SimpleTest; import cz.crcs.ectester.common.test.TestCallback; -import cz.crcs.ectester.common.test.TestException; import cz.crcs.ectester.reader.command.Command; import cz.crcs.ectester.reader.response.Response; /** * A simple test that runs one Command to get and evaluate one Response * to get a Result and compare it with the expected one. + * + * @author Jan Jancar johny@neuromancer.sk */ public class CommandTest extends SimpleTest<CommandTestable> { private CommandTest(CommandTestable command, TestCallback<CommandTestable> callback) { @@ -28,8 +29,7 @@ public class CommandTest extends SimpleTest<CommandTestable> { return new CommandTest(command, new TestCallback<CommandTestable>() { @Override public Result apply(CommandTestable commandTestable) { - Response resp = commandTestable.getResponse(); - Result.Value resultValue = Result.Value.fromExpected(expected, resp.successful(), resp.error()); + Result.Value resultValue = Result.Value.fromExpected(expected, commandTestable.ok(), commandTestable.error()); return new Result(resultValue, resultValue.ok() ? ok : nok); } }); @@ -56,21 +56,11 @@ public class CommandTest extends SimpleTest<CommandTestable> { } @Override - public void run() throws TestException { - if (hasRun) - return; - - testable.run(); - result = callback.apply(testable); - hasRun = true; - } - - @Override public String getDescription() { if (hasRun) { return testable.getResponse().getDescription(); } else { - return testable.getCommand().toString(); + return testable.getCommand().getDescription(); } } } diff --git a/src/cz/crcs/ectester/reader/test/CommandTestable.java b/src/cz/crcs/ectester/reader/test/CommandTestable.java index 3bb55bf..f670534 100644 --- a/src/cz/crcs/ectester/reader/test/CommandTestable.java +++ b/src/cz/crcs/ectester/reader/test/CommandTestable.java @@ -27,7 +27,7 @@ public class CommandTestable extends BaseTestable { } @Override - public void run() throws TestException { + public void run() { try { response = command.send(); } catch (CardException e) { diff --git a/src/cz/crcs/ectester/reader/test/PerformanceTest.java b/src/cz/crcs/ectester/reader/test/PerformanceTest.java index 4a27bad..9abaadc 100644 --- a/src/cz/crcs/ectester/reader/test/PerformanceTest.java +++ b/src/cz/crcs/ectester/reader/test/PerformanceTest.java @@ -5,6 +5,7 @@ import cz.crcs.ectester.common.test.SimpleTest; import cz.crcs.ectester.common.test.TestCallback; import cz.crcs.ectester.common.test.TestException; import cz.crcs.ectester.reader.command.Command; +import cz.crcs.ectester.reader.response.Response; import java.util.Arrays; @@ -13,12 +14,14 @@ import java.util.Arrays; */ public class PerformanceTest extends SimpleTest<CommandTestable> { private long[] times; + private Response[] responses; private long mean; private long median; private long mode; private int count; + private String desc; - private PerformanceTest(CommandTestable testable, int count) { + private PerformanceTest(CommandTestable testable, int count, String desc) { super(testable, new TestCallback<CommandTestable>() { @Override public Result apply(CommandTestable testable) { @@ -26,26 +29,31 @@ public class PerformanceTest extends SimpleTest<CommandTestable> { } }); this.count = count; + this.desc = desc; } public static PerformanceTest repeat(Command cmd, int count) { - return new PerformanceTest(new CommandTestable(cmd), count); + return new PerformanceTest(new CommandTestable(cmd), count, null); + } + + public static PerformanceTest repeat(String desc, Command cmd, int count) { + return new PerformanceTest(new CommandTestable(cmd), count, desc); } @Override public String getDescription() { - return String.format("Mean = %d ns, Median = %d ns, Mode = %d ns", mean, median, mode); + String rest = String.format("Mean = %d ns, Median = %d ns, Mode = %d ns", mean, median, mode); + return (desc == null ? rest : desc + " (" + rest + ")"); } @Override - public void run() throws TestException { - if (hasRun) - return; - + protected void runSelf() { times = new long[count]; + responses = new Response[count]; for (int i = 0; i < count; ++i) { testable.run(); - times[i] = testable.getResponse().getDuration(); + responses[i] = testable.getResponse(); + times[i] = responses[i].getDuration(); testable.reset(); } @@ -73,7 +81,6 @@ public class PerformanceTest extends SimpleTest<CommandTestable> { mode = current_value; } } - hasRun = true; result = callback.apply(testable); } @@ -85,6 +92,10 @@ public class PerformanceTest extends SimpleTest<CommandTestable> { return testable.getCommand(); } + public Response[] getResponses() { + return responses; + } + public long[] getTimes() { return times; } |
