diff options
| author | J08nY | 2018-01-23 17:31:15 +0100 |
|---|---|---|
| committer | J08nY | 2018-01-23 17:31:15 +0100 |
| commit | cb6c6b8b1274fe5a340c4317a4b015ea0ef15396 (patch) | |
| tree | 864a54dcdf07da33cd139312c8b0ee693e1a0eff /src/cz/crcs/ectester/common/util | |
| parent | 6c46a27a52854aee24f7a37e74002bd6f4485723 (diff) | |
| parent | c581e39e539e6dadb49d9f83f563ab2b375f6e0b (diff) | |
| download | ECTester-0.2.0.tar.gz ECTester-0.2.0.tar.zst ECTester-0.2.0.zip | |
Diffstat (limited to 'src/cz/crcs/ectester/common/util')
| -rw-r--r-- | src/cz/crcs/ectester/common/util/ByteUtil.java | 128 | ||||
| -rw-r--r-- | src/cz/crcs/ectester/common/util/CardUtil.java | 272 | ||||
| -rw-r--r-- | src/cz/crcs/ectester/common/util/ECUtil.java | 172 |
3 files changed, 572 insertions, 0 deletions
diff --git a/src/cz/crcs/ectester/common/util/ByteUtil.java b/src/cz/crcs/ectester/common/util/ByteUtil.java new file mode 100644 index 0000000..90c6eaa --- /dev/null +++ b/src/cz/crcs/ectester/common/util/ByteUtil.java @@ -0,0 +1,128 @@ +package cz.crcs.ectester.common.util; + +/** + * Utility class, some byte/hex manipulation, convenient byte[] methods. + * + * @author Petr Svenda petr@svenda.com + * @author Jan Jancar johny@neuromancer.sk + */ +public class ByteUtil { + public static short getShort(byte[] array, int offset) { + return (short) (((array[offset] & 0xFF) << 8) | (array[offset + 1] & 0xFF)); + } + + public static void setShort(byte[] array, int offset, short value) { + array[offset + 1] = (byte) (value & 0xFF); + array[offset] = (byte) ((value >> 8) & 0xFF); + } + + public static int diffBytes(byte[] one, int oneOffset, byte[] other, int otherOffset, int length) { + for (int i = 0; i < length; ++i) { + byte a = one[i + oneOffset]; + byte b = other[i + otherOffset]; + if (a != b) { + return i; + } + } + return length; + } + + public static boolean compareBytes(byte[] one, int oneOffset, byte[] other, int otherOffset, int length) { + return diffBytes(one, oneOffset, other, otherOffset, length) == length; + } + + public static boolean allValue(byte[] array, byte value) { + for (byte a : array) { + if (a != value) + return false; + } + return true; + } + + public static byte[] hexToBytes(String hex) { + return hexToBytes(hex, true); + } + + public static byte[] hexToBytes(String hex, boolean bigEndian) { + hex = hex.replace(" ", ""); + int len = hex.length(); + StringBuilder sb = new StringBuilder(); + + if (len % 2 == 1) { + sb.append("0"); + ++len; + } + + if (bigEndian) { + sb.append(hex); + } else { + for (int i = 0; i < len / 2; ++i) { + if (sb.length() >= 2) { + sb.insert(sb.length() - 2, hex.substring(2 * i, 2 * i + 2)); + } else { + sb.append(hex.substring(2 * i, 2 * i + 2)); + } + + } + } + + String data = sb.toString(); + byte[] result = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + result[i / 2] = (byte) ((Character.digit(data.charAt(i), 16) << 4) + + (Character.digit(data.charAt(i + 1), 16))); + } + return result; + } + + public static String byteToHex(byte data) { + return String.format("%02x", data); + } + + public static String bytesToHex(byte[] data) { + return bytesToHex(data, true); + } + + public static String bytesToHex(byte[] data, boolean addSpace) { + if (data == null) { + return ""; + } + return bytesToHex(data, 0, data.length, addSpace); + } + + public static String bytesToHex(byte[] data, int offset, int len) { + return bytesToHex(data, offset, len, true); + } + + public static String bytesToHex(byte[] data, int offset, int len, boolean addSpace) { + if (data == null) { + return ""; + } + StringBuilder buf = new StringBuilder(); + for (int i = offset; i < (offset + len); i++) { + buf.append(byteToHex(data[i])); + if (addSpace && i != (offset + len - 1)) { + buf.append(" "); + } + } + return (buf.toString()); + } + + public static byte[] concatenate(byte[]... arrays) { + int len = 0; + for (byte[] array : arrays) { + if (array == null) + continue; + len += array.length; + } + byte[] out = new byte[len]; + int offset = 0; + for (byte[] array : arrays) { + if (array == null || array.length == 0) + continue; + System.arraycopy(array, 0, out, offset, array.length); + offset += array.length; + } + return out; + } +} diff --git a/src/cz/crcs/ectester/common/util/CardUtil.java b/src/cz/crcs/ectester/common/util/CardUtil.java new file mode 100644 index 0000000..8285d8b --- /dev/null +++ b/src/cz/crcs/ectester/common/util/CardUtil.java @@ -0,0 +1,272 @@ +package cz.crcs.ectester.common.util; + +import cz.crcs.ectester.applet.ECTesterApplet; +import cz.crcs.ectester.applet.EC_Consts; +import javacard.framework.ISO7816; +import javacard.security.CryptoException; + +import static cz.crcs.ectester.applet.ECTesterApplet.*; + +/** + * @author Petr Svenda petr@svenda.com + * @author Jan Jancar johny@neuromancer.sk + */ +public class CardUtil { + public static byte getKA(String name) { + switch (name) { + case "DH": + case "ECDH": + return ECTesterApplet.KeyAgreement_ALG_EC_SVDP_DH; + case "DHC": + case "ECDHC": + return ECTesterApplet.KeyAgreement_ALG_EC_SVDP_DHC; + default: + return ECTesterApplet.KeyAgreement_ALG_EC_SVDP_DH; + } + } + + public static String getSWSource(short sw) { + switch (sw) { + case ISO7816.SW_NO_ERROR: + case ISO7816.SW_APPLET_SELECT_FAILED: + case ISO7816.SW_BYTES_REMAINING_00: + case ISO7816.SW_CLA_NOT_SUPPORTED: + case ISO7816.SW_COMMAND_NOT_ALLOWED: + case ISO7816.SW_CONDITIONS_NOT_SATISFIED: + case ISO7816.SW_CORRECT_LENGTH_00: + case ISO7816.SW_DATA_INVALID: + case ISO7816.SW_FILE_FULL: + case ISO7816.SW_FILE_INVALID: + case ISO7816.SW_FILE_NOT_FOUND: + case ISO7816.SW_FUNC_NOT_SUPPORTED: + case ISO7816.SW_INCORRECT_P1P2: + case ISO7816.SW_INS_NOT_SUPPORTED: + case ISO7816.SW_LOGICAL_CHANNEL_NOT_SUPPORTED: + case ISO7816.SW_RECORD_NOT_FOUND: + case ISO7816.SW_SECURE_MESSAGING_NOT_SUPPORTED: + case ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED: + case ISO7816.SW_UNKNOWN: + case ISO7816.SW_WARNING_STATE_UNCHANGED: + case ISO7816.SW_WRONG_DATA: + case ISO7816.SW_WRONG_LENGTH: + case ISO7816.SW_WRONG_P1P2: + return "ISO"; + case CryptoException.ILLEGAL_VALUE: + case CryptoException.UNINITIALIZED_KEY: + case CryptoException.NO_SUCH_ALGORITHM: + case CryptoException.INVALID_INIT: + case CryptoException.ILLEGAL_USE: + return "CryptoException"; + case ECTesterApplet.SW_SIG_VERIFY_FAIL: + case ECTesterApplet.SW_DH_DHC_MISMATCH: + case ECTesterApplet.SW_KEYPAIR_NULL: + case ECTesterApplet.SW_KA_NULL: + case ECTesterApplet.SW_SIGNATURE_NULL: + case ECTesterApplet.SW_OBJECT_NULL: + return "ECTesterApplet"; + default: + return "?"; + } + } + + public static String getSW(short sw) { + switch (sw) { + case ISO7816.SW_APPLET_SELECT_FAILED: + return "APPLET_SELECT_FAILED"; + case ISO7816.SW_BYTES_REMAINING_00: + return "BYTES_REMAINING"; + case ISO7816.SW_CLA_NOT_SUPPORTED: + return "CLA_NOT_SUPPORTED"; + case ISO7816.SW_COMMAND_NOT_ALLOWED: + return "COMMAND_NOT_ALLOWED"; + case ISO7816.SW_CONDITIONS_NOT_SATISFIED: + return "CONDITIONS_NOT_SATISFIED"; + case ISO7816.SW_CORRECT_LENGTH_00: + return "CORRECT_LENGTH"; + case ISO7816.SW_DATA_INVALID: + return "DATA_INVALID"; + case ISO7816.SW_FILE_FULL: + return "FILE_FULL"; + case ISO7816.SW_FILE_INVALID: + return "FILE_INVALID"; + case ISO7816.SW_FILE_NOT_FOUND: + return "FILE_NOT_FOUND"; + case ISO7816.SW_FUNC_NOT_SUPPORTED: + return "FUNC_NOT_SUPPORTED"; + case ISO7816.SW_INCORRECT_P1P2: + return "INCORRECT_P1P2"; + case ISO7816.SW_INS_NOT_SUPPORTED: + return "INS_NOT_SUPPORTED"; + case ISO7816.SW_LOGICAL_CHANNEL_NOT_SUPPORTED: + return "LOGICAL_CHANNEL_NOT_SUPPORTED"; + case ISO7816.SW_RECORD_NOT_FOUND: + return "RECORD_NOT_FOUND"; + case ISO7816.SW_SECURE_MESSAGING_NOT_SUPPORTED: + return "SECURE_MESSAGING_NOT_SUPPORTED"; + case ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED: + return "SECURITY_STATUS_NOT_SATISFIED"; + case ISO7816.SW_UNKNOWN: + return "UNKNOWN"; + case ISO7816.SW_WARNING_STATE_UNCHANGED: + return "WARNING_STATE_UNCHANGED"; + case ISO7816.SW_WRONG_DATA: + return "WRONG_DATA"; + case ISO7816.SW_WRONG_LENGTH: + return "WRONG_LENGTH"; + case ISO7816.SW_WRONG_P1P2: + return "WRONG_P1P2"; + case CryptoException.ILLEGAL_VALUE: + return "ILLEGAL_VALUE"; + case CryptoException.UNINITIALIZED_KEY: + return "UNINITIALIZED_KEY"; + case CryptoException.NO_SUCH_ALGORITHM: + return "NO_SUCH_ALG"; + case CryptoException.INVALID_INIT: + return "INVALID_INIT"; + case CryptoException.ILLEGAL_USE: + return "ILLEGAL_USE"; + case ECTesterApplet.SW_SIG_VERIFY_FAIL: + return "SIG_VERIFY_FAIL"; + case ECTesterApplet.SW_DH_DHC_MISMATCH: + return "DH_DHC_MISMATCH"; + case ECTesterApplet.SW_KEYPAIR_NULL: + return "KEYPAIR_NULL"; + case ECTesterApplet.SW_KA_NULL: + return "KA_NULL"; + case ECTesterApplet.SW_SIGNATURE_NULL: + return "SIGNATURE_NULL"; + case ECTesterApplet.SW_OBJECT_NULL: + return "OBJECT_NULL"; + default: + return "unknown"; + } + } + + public static String getSWString(short sw) { + if (sw == ISO7816.SW_NO_ERROR) { + return "OK (0x9000)"; + } else { + String str = getSW(sw); + return String.format("fail (%s, 0x%04x)", str, sw); + } + } + + public static String getCorruption(short corruptionType) { + switch (corruptionType) { + case EC_Consts.CORRUPTION_NONE: + return "NONE"; + case EC_Consts.CORRUPTION_FIXED: + return "FIXED"; + case EC_Consts.CORRUPTION_ONE: + return "ONE"; + case EC_Consts.CORRUPTION_ZERO: + return "ZERO"; + case EC_Consts.CORRUPTION_ONEBYTERANDOM: + return "ONE_BYTE_RANDOM"; + case EC_Consts.CORRUPTION_FULLRANDOM: + return "FULL_RANDOM"; + case EC_Consts.CORRUPTION_INCREMENT: + return "INCREMENT"; + case EC_Consts.CORRUPTION_INFINITY: + return "INFINITY"; + case EC_Consts.CORRUPTION_COMPRESS: + return "COMPRESSED"; + case EC_Consts.CORRUPTION_MAX: + return "MAX"; + default: + return "unknown"; + } + } + + public static String getKATypeString(byte kaType) { + switch (kaType) { + case KeyAgreement_ALG_EC_SVDP_DH: + return "ALG_EC_SVDP_DH"; + case KeyAgreement_ALG_EC_SVDP_DH_PLAIN: + return "ALG_EC_SVDP_DH_PLAIN"; + case KeyAgreement_ALG_EC_PACE_GM: + return "ALG_EC_PACE_GM"; + case KeyAgreement_ALG_EC_SVDP_DH_PLAIN_XY: + return "ALG_EC_SVDP_DH_PLAIN_XY"; + case KeyAgreement_ALG_EC_SVDP_DHC: + return "ALG_EC_SVDP_DHC"; + case KeyAgreement_ALG_EC_SVDP_DHC_PLAIN: + return "ALG_EC_SVDP_DHC_PLAIN"; + default: + return "unknown"; + } + } + + public static byte getKAType(String kaTypeString) { + switch (kaTypeString) { + case "ALG_EC_SVDP_DH": + return KeyAgreement_ALG_EC_SVDP_DH; + case "ALG_EC_SVDP_DH_PLAIN": + return KeyAgreement_ALG_EC_SVDP_DH_PLAIN; + case "ALG_EC_PACE_GM": + return KeyAgreement_ALG_EC_PACE_GM; + case "ALG_EC_SVDP_DH_PLAIN_XY": + return KeyAgreement_ALG_EC_SVDP_DH_PLAIN_XY; + case "ALG_EC_SVDP_DHC": + return KeyAgreement_ALG_EC_SVDP_DHC; + case "ALG_EC_SVDP_DHC_PLAIN": + return KeyAgreement_ALG_EC_SVDP_DHC_PLAIN; + default: + return 0; + } + } + + public static byte parseKAType(String kaTypeString) { + byte kaType; + try { + kaType = Byte.parseByte(kaTypeString); + } catch (NumberFormatException nfex) { + kaType = getKAType(kaTypeString); + } + return kaType; + } + + public static String getSigTypeString(byte sigType) { + switch (sigType) { + case Signature_ALG_ECDSA_SHA: + return "ALG_ECDSA_SHA"; + case Signature_ALG_ECDSA_SHA_224: + return "ALG_ECDSA_SHA_224"; + case Signature_ALG_ECDSA_SHA_256: + return "ALG_ECDSA_SHA_256"; + case Signature_ALG_ECDSA_SHA_384: + return "ALG_ECDSA_SHA_384"; + case Signature_ALG_ECDSA_SHA_512: + return "ALG_ECDSA_SHA_512"; + default: + return "unknown"; + } + } + + public static byte getSigType(String sigTypeString) { + switch (sigTypeString) { + case "ALG_ECDSA_SHA": + return Signature_ALG_ECDSA_SHA; + case "ALG_ECDSA_SHA_224": + return Signature_ALG_ECDSA_SHA_224; + case "ALG_ECDSA_SHA_256": + return Signature_ALG_ECDSA_SHA_256; + case "ALG_ECDSA_SHA_384": + return Signature_ALG_ECDSA_SHA_384; + case "ALG_ECDSA_SHA_512": + return Signature_ALG_ECDSA_SHA_512; + default: + return 0; + } + } + + public static byte parseSigType(String sigTypeString) { + byte sigType; + try { + sigType = Byte.parseByte(sigTypeString); + } catch (NumberFormatException nfex) { + sigType = getSigType(sigTypeString); + } + return sigType; + } +} diff --git a/src/cz/crcs/ectester/common/util/ECUtil.java b/src/cz/crcs/ectester/common/util/ECUtil.java new file mode 100644 index 0000000..973b813 --- /dev/null +++ b/src/cz/crcs/ectester/common/util/ECUtil.java @@ -0,0 +1,172 @@ +package cz.crcs.ectester.common.util; + +import java.math.BigInteger; +import java.security.spec.*; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class ECUtil { + + public static byte[] toByteArray(BigInteger what, int bits) { + byte[] raw = what.toByteArray(); + int bytes = (bits + 7) / 8; + if (raw.length < bytes) { + byte[] result = new byte[bytes]; + System.arraycopy(raw, 0, result, bytes - raw.length, raw.length); + return result; + } + if (bytes < raw.length) { + byte[] result = new byte[bytes]; + System.arraycopy(raw, raw.length - bytes, result, 0, bytes); + return result; + } + return raw; + } + + public static byte[] toX962Compressed(ECPoint point, int bits) { + if (point.equals(ECPoint.POINT_INFINITY)) { + return new byte[]{0}; + } + byte[] x = toByteArray(point.getAffineX(), bits); + byte marker = (byte) (0x02 | point.getAffineY().mod(BigInteger.valueOf(2)).byteValue()); + return ByteUtil.concatenate(new byte[]{marker}, x); + } + + public static byte[] toX962Compressed(ECPoint point, EllipticCurve curve) { + return toX962Compressed(point, curve.getField().getFieldSize()); + } + + public static byte[] toX962Compressed(ECPoint point, ECParameterSpec spec) { + return toX962Compressed(point, spec.getCurve()); + } + + public static byte[] toX962Uncompressed(ECPoint point, int bits) { + if (point.equals(ECPoint.POINT_INFINITY)) { + return new byte[]{0}; + } + byte[] x = toByteArray(point.getAffineX(), bits); + byte[] y = toByteArray(point.getAffineY(), bits); + return ByteUtil.concatenate(new byte[]{0x04}, x, y); + } + + public static byte[] toX962Uncompressed(ECPoint point, EllipticCurve curve) { + return toX962Uncompressed(point, curve.getField().getFieldSize()); + } + + public static byte[] toX962Uncompressed(ECPoint point, ECParameterSpec spec) { + return toX962Uncompressed(point, spec.getCurve()); + } + + public static byte[] toX962Hybrid(ECPoint point, int bits) { + if (point.equals(ECPoint.POINT_INFINITY)) { + return new byte[]{0}; + } + byte[] x = toByteArray(point.getAffineX(), bits); + byte[] y = toByteArray(point.getAffineY(), bits); + byte marker = (byte) (0x06 | point.getAffineY().mod(BigInteger.valueOf(2)).byteValue()); + return ByteUtil.concatenate(new byte[]{marker}, x, y); + } + + public static byte[] toX962Hybrid(ECPoint point, EllipticCurve curve) { + return toX962Hybrid(point, curve.getField().getFieldSize()); + } + + public static byte[] toX962Hybrid(ECPoint point, ECParameterSpec spec) { + return toX962Hybrid(point, spec.getCurve()); + } + + private static boolean isResidue(BigInteger a, BigInteger p) { + BigInteger exponent = p.subtract(BigInteger.ONE).divide(BigInteger.valueOf(2)); + BigInteger result = a.modPow(exponent, p); + return result.intValueExact() == 1; + } + + private static BigInteger modSqrt(BigInteger a, BigInteger p) { + BigInteger q = p.subtract(BigInteger.ONE); + int s = 0; + while (q.mod(BigInteger.valueOf(2)).equals(BigInteger.ZERO)) { + q = q.divide(BigInteger.valueOf(2)); + s++; + } + + BigInteger z = BigInteger.ONE; + do { + z = z.add(BigInteger.ONE); + } while (isResidue(z, p)); + + BigInteger m = BigInteger.valueOf(s); + BigInteger c = z.modPow(q, p); + BigInteger t = a.modPow(q, p); + BigInteger rExponent = q.add(BigInteger.ONE).divide(BigInteger.valueOf(2)); + BigInteger r = a.modPow(rExponent, p); + + while (!t.equals(BigInteger.ONE)) { + int i = 0; + BigInteger exponent; + do { + exponent = BigInteger.valueOf(2).pow(++i); + } while (!t.modPow(exponent, p).equals(BigInteger.ONE)); + + BigInteger twoExponent = m.subtract(BigInteger.valueOf(i + 1)); + BigInteger b = c.modPow(BigInteger.valueOf(2).modPow(twoExponent, p), p); + m = BigInteger.valueOf(i); + c = b.modPow(BigInteger.valueOf(2), p); + t = t.multiply(c).mod(p); + r = r.multiply(b).mod(p); + } + return r; + } + + public static ECPoint fromX962(byte[] data, EllipticCurve curve) { + if (data == null) { + return null; + } + if (data[0] == 0x04 || data[0] == 0x06 || data[0] == 0x07) { + int len = (data.length - 1) / 2; + byte[] xbytes = new byte[len]; + System.arraycopy(data, 1, xbytes, 0, len); + byte[] ybytes = new byte[len]; + System.arraycopy(data, 1 + len, ybytes, 0, len); + return new ECPoint(new BigInteger(1, xbytes), new BigInteger(1, ybytes)); + } else if (data[0] == 0x02 || data[0] == 0x03) { + if (curve == null) { + throw new IllegalArgumentException(); + } + byte[] xbytes = new byte[data.length - 1]; + System.arraycopy(data, 1, xbytes, 0, data.length - 1); + BigInteger x = new BigInteger(1, xbytes); + BigInteger a = curve.getA(); + BigInteger b = curve.getB(); + + ECField field = curve.getField(); + if (field instanceof ECFieldFp) { + BigInteger p = ((ECFieldFp) field).getP(); + BigInteger alpha = x.modPow(BigInteger.valueOf(3), p); + alpha = alpha.add(x.multiply(a)); + alpha = alpha.add(b); + + BigInteger beta = modSqrt(alpha, p); + if (beta.getLowestSetBit() == 0) { + // rightmost bit is one + if (data[0] == 0x02) { + beta = beta.negate(); + } + } else { + // rightmost bit is zero + if (data[0] == 0x03) { + beta = beta.negate(); + } + } + + return new ECPoint(x, beta); + } else if (field instanceof ECFieldF2m) { + //TODO + throw new UnsupportedOperationException(); + } + return null; + } else { + throw new IllegalArgumentException(); + } + } +} |
