diff options
| -rw-r--r-- | src/cz/crcs/ectester/common/util/CardUtil.java | 17 | ||||
| -rw-r--r-- | src/cz/crcs/ectester/common/util/ECUtil.java | 41 | ||||
| -rw-r--r-- | src/cz/crcs/ectester/reader/ECTesterReader.java | 22 | ||||
| -rw-r--r-- | src/cz/crcs/ectester/reader/command/Command.java | 68 |
4 files changed, 108 insertions, 40 deletions
diff --git a/src/cz/crcs/ectester/common/util/CardUtil.java b/src/cz/crcs/ectester/common/util/CardUtil.java index 7483c32..a761949 100644 --- a/src/cz/crcs/ectester/common/util/CardUtil.java +++ b/src/cz/crcs/ectester/common/util/CardUtil.java @@ -31,6 +31,23 @@ public class CardUtil { } } + public static String getSigHashAlgo(byte sigType) { + switch (sigType) { + case EC_Consts.Signature_ALG_ECDSA_SHA: + return "SHA-1"; + case EC_Consts.Signature_ALG_ECDSA_SHA_224: + return "SHA-224"; + case EC_Consts.Signature_ALG_ECDSA_SHA_256: + return "SHA-256"; + case EC_Consts.Signature_ALG_ECDSA_SHA_384: + return "SHA-384"; + case EC_Consts.Signature_ALG_ECDSA_SHA_512: + return "SHA-512"; + default: + return null; + } + } + public static byte getKA(String name) { switch (name) { case "DH": diff --git a/src/cz/crcs/ectester/common/util/ECUtil.java b/src/cz/crcs/ectester/common/util/ECUtil.java index 1706ca0..0260e95 100644 --- a/src/cz/crcs/ectester/common/util/ECUtil.java +++ b/src/cz/crcs/ectester/common/util/ECUtil.java @@ -3,9 +3,16 @@ package cz.crcs.ectester.common.util; import cz.crcs.ectester.applet.EC_Consts; import cz.crcs.ectester.common.ec.*; import cz.crcs.ectester.data.EC_Store; +import org.bouncycastle.asn1.ASN1Integer; +import org.bouncycastle.asn1.ASN1StreamParser; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.DERSequenceParser; +import java.io.IOException; import java.math.BigInteger; import java.security.KeyPair; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.security.interfaces.ECPrivateKey; import java.security.interfaces.ECPublicKey; import java.security.spec.*; @@ -196,7 +203,7 @@ public class ECUtil { public static ECPublicKey toPublicKey(EC_Key.Public pubkey) { EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, pubkey.getCurve()); if (curve == null) { - throw new IllegalArgumentException("pubkey curve nor found: " + pubkey.getCurve()); + throw new IllegalArgumentException("pubkey curve not found: " + pubkey.getCurve()); } return new RawECPublicKey(toPoint(pubkey), curve.toSpec()); } @@ -204,7 +211,7 @@ public class ECUtil { public static ECPrivateKey toPrivateKey(EC_Key.Private privkey) { EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, privkey.getCurve()); if (curve == null) { - throw new IllegalArgumentException("privkey curve nor found: " + privkey.getCurve()); + throw new IllegalArgumentException("privkey curve not found: " + privkey.getCurve()); } return new RawECPrivateKey(toScalar(privkey), curve.toSpec()); } @@ -212,7 +219,7 @@ public class ECUtil { public static KeyPair toKeyPair(EC_Keypair kp) { EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, kp.getCurve()); if (curve == null) { - throw new IllegalArgumentException("keypair curve nor found: " + kp.getCurve()); + throw new IllegalArgumentException("keypair curve not found: " + kp.getCurve()); } ECPublicKey pubkey = new RawECPublicKey(toPoint(kp), curve.toSpec()); ECPrivateKey privkey = new RawECPrivateKey(toScalar(kp), curve.toSpec()); @@ -222,4 +229,32 @@ public class ECUtil { public static byte[] toDERSignature(byte[] r, byte[] s) { return ByteUtil.concatenate(new byte[]{0x30, (byte) (r.length + s.length + 4), 0x02, (byte) r.length}, r, new byte[]{0x02, (byte) s.length}, s); } + + public static BigInteger[] fromDERSignature(byte[] signature) throws IOException { + ASN1StreamParser parser = new ASN1StreamParser(signature); + DERSequence sequence = (DERSequence) ((DERSequenceParser) parser.readObject()).getLoadedObject(); + ASN1Integer r = (ASN1Integer) sequence.getObjectAt(0); + ASN1Integer s = (ASN1Integer) sequence.getObjectAt(1); + return new BigInteger[]{r.getPositiveValue(), s.getPositiveValue()}; + } + + public static BigInteger recoverSignatureNonce(byte[] signature, byte[] data, BigInteger privkey, ECParameterSpec params, String hashType) { + try { + int bitSize = params.getOrder().bitLength(); + MessageDigest md = MessageDigest.getInstance(hashType); + byte[] hash = md.digest(data); + BigInteger hashInt = new BigInteger(1, hash); + hashInt = hashInt.and(BigInteger.ONE.shiftLeft(bitSize + 1).subtract(BigInteger.ONE)); + + BigInteger[] sigPair = fromDERSignature(signature); + BigInteger r = sigPair[0]; + BigInteger s = sigPair[1]; + + BigInteger rd = privkey.multiply(r).mod(params.getOrder()); + BigInteger hrd = hashInt.add(rd).mod(params.getOrder()); + return s.modInverse(params.getOrder()).multiply(hrd).mod(params.getOrder()); + } catch (NoSuchAlgorithmException | IOException nsae) { + return null; + } + } } diff --git a/src/cz/crcs/ectester/reader/ECTesterReader.java b/src/cz/crcs/ectester/reader/ECTesterReader.java index c442ec1..fe44709 100644 --- a/src/cz/crcs/ectester/reader/ECTesterReader.java +++ b/src/cz/crcs/ectester/reader/ECTesterReader.java @@ -26,10 +26,12 @@ import cz.crcs.ectester.applet.ECTesterApplet; import cz.crcs.ectester.applet.EC_Consts; import cz.crcs.ectester.common.cli.CLITools; import cz.crcs.ectester.common.cli.Colors; +import cz.crcs.ectester.common.ec.EC_Curve; import cz.crcs.ectester.common.output.OutputLogger; import cz.crcs.ectester.common.output.TestWriter; 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.common.util.FileUtil; import cz.crcs.ectester.data.EC_Store; import cz.crcs.ectester.reader.command.Command; @@ -40,16 +42,22 @@ import cz.crcs.ectester.reader.test.*; import javacard.framework.ISO7816; import javacard.security.KeyPair; import org.apache.commons.cli.*; +import org.bouncycastle.asn1.ASN1Integer; +import org.bouncycastle.asn1.ASN1StreamParser; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.DERSequenceParser; import org.bouncycastle.jce.provider.BouncyCastleProvider; import javax.smartcardio.CardException; import javax.smartcardio.ResponseAPDU; import javax.xml.parsers.ParserConfigurationException; import java.io.*; +import java.math.BigInteger; import java.net.URL; import java.net.URLClassLoader; import java.nio.file.Files; import java.security.Security; +import java.security.spec.ECParameterSpec; import java.util.*; import java.util.jar.Manifest; @@ -670,7 +678,7 @@ public class ECTesterReader { OutputStreamWriter out = FileUtil.openFiles(cfg.outputs); if (out != null) { - out.write("index;signTime;verifyTime;data;pubW;privS;signature;valid\n"); + out.write("index;signTime;verifyTime;data;pubW;privS;signature;nonce;valid\n"); } Command.Export export = new Command.Export(cardManager, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.KEY_BOTH, EC_Consts.PARAMETERS_KEYPAIR); @@ -719,7 +727,17 @@ public class ECTesterReader { String pub = ByteUtil.bytesToHex(exported.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_W), false); String priv = ByteUtil.bytesToHex(exported.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_S), false); String dataString = (cfg.input != null) ? "" : ByteUtil.bytesToHex(data, false); - out.write(String.format("%d;%d;%d;%s;%s;%s;%s;%d\n", done, sign.getDuration() / 1000000, verify.getDuration() / 1000000, dataString, pub, priv, ByteUtil.bytesToHex(signature, false), verify.successful() ? 1 : 0)); + BigInteger privkey = new BigInteger(1, exported.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_S)); + EC_Curve actualCurve = Command.findCurve(EC_Store.getInstance(), cfg, cfg.bits, keyClass); + String k = ""; + if (actualCurve != null) { + ECParameterSpec params = actualCurve.toSpec(); + BigInteger kValue = ECUtil.recoverSignatureNonce(signature, data, privkey, params, CardUtil.getSigHashAlgo(cfg.ECDSAType)); + if (kValue != null) { + k = ByteUtil.bytesToHex(kValue.toByteArray(), false); + } + } + out.write(String.format("%d;%d;%d;%s;%s;%s;%s;%s;%d\n", done, sign.getDuration() / 1000000, verify.getDuration() / 1000000, dataString, pub, priv, ByteUtil.bytesToHex(signature, false), k,verify.successful() ? 1 : 0)); } ++done; diff --git a/src/cz/crcs/ectester/reader/command/Command.java b/src/cz/crcs/ectester/reader/command/Command.java index 1b1ec33..a92017e 100644 --- a/src/cz/crcs/ectester/reader/command/Command.java +++ b/src/cz/crcs/ectester/reader/command/Command.java @@ -54,23 +54,11 @@ public abstract class Command implements Cloneable { return (Command) super.clone(); } - - /** - * @param keyPair which keyPair/s (local/remote) to set curve domain parameters on - * @param keyLength key length to choose - * @param keyClass key class to choose - * @return a Command to send in order to prepare the curve on the keypairs. - * @throws IOException if curve file cannot be found/opened - */ - public static Command prepareCurve(CardMngr cardManager, EC_Store dataStore, ECTesterReader.Config cfg, byte keyPair, short keyLength, byte keyClass) throws IOException { - + public static EC_Curve findCurve(EC_Store dataStore, ECTesterReader.Config cfg, short keyLength, byte keyClass) throws IOException { if (cfg.customCurve) { - // Set custom curve (one of the SECG curves embedded applet-side) - short domainParams = keyClass == KeyPair.ALG_EC_FP ? EC_Consts.PARAMETERS_DOMAIN_FP : EC_Consts.PARAMETERS_DOMAIN_F2M; - return new Command.Set(cardManager, keyPair, EC_Consts.getCurve(keyLength, keyClass), domainParams, null); + byte curveId = EC_Consts.getCurve(keyLength, keyClass); + return dataStore.getObject(EC_Curve.class, "secg", CardUtil.getCurveName(curveId)); } else if (cfg.namedCurve != null) { - // Set a named curve. - // parse cfg.namedCurve -> cat / id | cat | id EC_Curve curve = dataStore.getObject(EC_Curve.class, cfg.namedCurve); if (curve == null) { throw new IOException("Curve could no be found."); @@ -81,34 +69,44 @@ public abstract class Command implements Cloneable { if (curve.getField() != keyClass) { throw new IOException("Curve field mismatch."); } - - byte[] external = curve.flatten(); - if (external == null) { - throw new IOException("Couldn't read named curve data."); - } - return new Command.Set(cardManager, keyPair, EC_Consts.CURVE_external, curve.getParams(), external); + return curve; } else if (cfg.curveFile != null) { - // Set curve loaded from a file EC_Curve curve = new EC_Curve(null, keyLength, keyClass); FileInputStream in = new FileInputStream(cfg.curveFile); curve.readCSV(in); in.close(); + return curve; + } else { + return null; + } + } - byte[] external = curve.flatten(); - if (external == null) { - throw new IOException("Couldn't read the curve file correctly."); + + /** + * @param keyPair which keyPair/s (local/remote) to set curve domain parameters on + * @param keyLength key length to choose + * @param keyClass key class to choose + * @return a Command to send in order to prepare the curve on the keypairs. + * @throws IOException if curve file cannot be found/opened + */ + public static Command prepareCurve(CardMngr cardManager, EC_Store dataStore, ECTesterReader.Config cfg, byte keyPair, short keyLength, byte keyClass) throws IOException { + if (cfg.customCurve) { + // Set custom curve (one of the SECG curves embedded applet-side) + short domainParams = keyClass == KeyPair.ALG_EC_FP ? EC_Consts.PARAMETERS_DOMAIN_FP : EC_Consts.PARAMETERS_DOMAIN_F2M; + return new Command.Set(cardManager, keyPair, EC_Consts.getCurve(keyLength, keyClass), domainParams, null); + } + + EC_Curve curve = findCurve(dataStore, cfg, keyLength, keyClass); + if ((curve == null || curve.flatten() == null) && (cfg.namedCurve != null || cfg.curveFile != null)) { + if (cfg.namedCurve != null) { + throw new IOException("Couldn't read named curve data."); } - return new Command.Set(cardManager, keyPair, EC_Consts.CURVE_external, curve.getParams(), external); - } else { - // Set default curve - /* This command was generally causing problems for simulating on jcardsim. - * Since there, .clearKey() resets all the keys values, even the domain. - * This might break some other stuff.. But should not. - */ - //commands.add(new Command.Clear(cardManager, keyPair)); + throw new IOException("Couldn't read the curve file correctly."); + } else if (curve == null) { return null; } + return new Command.Set(cardManager, keyPair, EC_Consts.CURVE_external, curve.getParams(), curve.flatten()); } @@ -896,8 +894,8 @@ public abstract class Command implements Cloneable { */ public static class SetDryRunMode extends Command { private byte dryRunMode; + /** - * * @param cardManager * @param dryRunMode */ @@ -918,7 +916,7 @@ public abstract class Command implements Cloneable { @Override public String getDescription() { - return (dryRunMode == ECTesterApplet.MODE_NORMAL ? "Disable" : "Enable") + " dry run mode"; + return (dryRunMode == ECTesterApplet.MODE_NORMAL ? "Disable" : "Enable") + " dry run mode"; } } } |
