diff --git a/.project b/.project
deleted file mode 100644
index 2da04efa..00000000
--- a/.project
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
- GlobalPlatformPro
-
-
-
-
-
- org.eclipse.jdt.core.javabuilder
-
-
-
-
-
- org.eclipse.jdt.core.javanature
-
-
diff --git a/build.xml b/build.xml
deleted file mode 100644
index fa553c0e..00000000
--- a/build.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-
-
- ANT build file for GlobalPlatformPro (javacard.pro)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Not releasing ${release.version}.
-
-
-
-
-
-
diff --git a/ext/README.md b/ext/README.md
deleted file mode 100644
index b104695c..00000000
--- a/ext/README.md
+++ /dev/null
@@ -1,4 +0,0 @@
-# Windows binary
-To make the Windows .exe, download launch4j to this folder, for example:
-
-https://downloads.sourceforge.net/project/launch4j/launch4j-3/3.11/launch4j-3.11-linux-x64.tgz
diff --git a/library/pom.xml b/library/pom.xml
index d0e046cb..72e5504e 100644
--- a/library/pom.xml
+++ b/library/pom.xml
@@ -18,13 +18,13 @@
com.github.martinpaljak
apdu4j-core
- 19.05.08
+ 2020b1
com.github.martinpaljak
capfile
- 19.03.04
+ 20.02.19
@@ -32,12 +32,6 @@
slf4j-api
1.7.30
-
-
- com.google.code.gson
- gson
- 2.8.6
-
org.bouncycastle
@@ -48,13 +42,13 @@
com.payneteasy
ber-tlv
- 1.0-9
+ 1.0-10
-
+
org.testng
testng
- 6.14.3
+ 7.1.0
test
diff --git a/pom.xml b/pom.xml
index f7635aee..23547ad9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
com.github.martinpaljak
metacard
- 20.01.14
+ 20.02.19
20.01.23
gppro
@@ -25,11 +25,16 @@
https://github.com/martinpaljak/GlobalPlatformPro
scm:git:git@github.com:martinpaljak/globalplatformpro.git
+
+
+ javacard-pro
+ https://javacard.pro/maven/
+
+
tool
library
-
diff --git a/tool/pom.xml b/tool/pom.xml
index 92c24af3..47b5c877 100644
--- a/tool/pom.xml
+++ b/tool/pom.xml
@@ -11,32 +11,21 @@
gptool
+ GlobalPlatformPro CLI tool
com.github.martinpaljak
apdu4j-pcsc
- 19.05.08
+ 20.01.01
-
+
com.github.martinpaljak
globalplatformpro
20.01.23
-
-
- com.google.code.gson
- gson
- 2.8.5
-
-
-
- org.apache.httpcomponents
- httpclient
- 4.5.10
-
org.slf4j
@@ -44,23 +33,24 @@
1.7.30
true
-
-
- org.slf4j
- slf4j-api
- 1.7.30
-
net.sf.jopt-simple
jopt-simple
5.0.4
+
+
+ com.google.auto.service
+ auto-service
+ 1.0-rc6
+ true
+
org.testng
testng
- 6.14.3
+ 7.1.0
test
@@ -122,11 +112,15 @@
1.8.0
- ${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}.${parsedVersion.buildNumber}
+
+ ${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}.${parsedVersion.buildNumber}
+
${project.version}
GlobalPlatformPro
(C) 2015 - 2019 Martin Paljak and contributors (LGPL+MIT)
- ${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}.${parsedVersion.buildNumber}
+
+ ${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}.${parsedVersion.buildNumber}
+
${project.version}
GlobalPlatformPro
gppro
diff --git a/tool/src/main/java/pro/javacard/gp/GP.java b/tool/src/main/java/pro/javacard/gp/GP.java
new file mode 100644
index 00000000..9365fa0d
--- /dev/null
+++ b/tool/src/main/java/pro/javacard/gp/GP.java
@@ -0,0 +1,41 @@
+/*
+ * GlobalPlatformPro - GlobalPlatform tool
+ *
+ * Copyright (C) 2015-2020 Martin Paljak, martin@martinpaljak.net
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3.0 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+package pro.javacard.gp;
+
+import apdu4j.BIBO;
+import apdu4j.i.SmartCardApp;
+import com.google.auto.service.AutoService;
+
+/**
+ * Provides an apdu4j compatible SmartCardApp interface
+ */
+@AutoService(SmartCardApp.class)
+public final class GP implements SmartCardApp {
+
+ // Public constructor for service initialization
+ public GP() {
+ }
+
+ @Override
+ public int run(BIBO bibo, String[] args) {
+ return new GPTool().run(bibo, args);
+ }
+}
diff --git a/tool/src/main/java/pro/javacard/gp/GPCommandLineInterface.java b/tool/src/main/java/pro/javacard/gp/GPCommandLineInterface.java
index 37378259..1578db9c 100644
--- a/tool/src/main/java/pro/javacard/gp/GPCommandLineInterface.java
+++ b/tool/src/main/java/pro/javacard/gp/GPCommandLineInterface.java
@@ -43,7 +43,6 @@ abstract class GPCommandLineInterface {
protected final static String OPT_DELETE_KEY = "delete-key";
protected final static String OPT_DOMAIN = "domain";
protected final static String OPT_MOVE = "move";
- protected final static String OPT_DUMP = "dump";
protected final static String OPT_EMV = "emv";
protected final static String OPT_FORCE = "force";
protected final static String OPT_INFO = "info";
@@ -79,7 +78,6 @@ abstract class GPCommandLineInterface {
protected final static String OPT_PUT_KEY = "put-key";
protected final static String OPT_READER = "reader";
protected final static String OPT_RENAME_ISD = "rename-isd";
- protected final static String OPT_REPLAY = "replay";
protected final static String OPT_SC_MODE = "mode";
protected final static String OPT_SDAID = "sdaid";
protected final static String OPT_SECURE_APDU = "secure-apdu";
@@ -89,7 +87,6 @@ abstract class GPCommandLineInterface {
protected final static String OPT_SHA256 = "sha256";
protected final static String OPT_STORE_DATA = "store-data";
protected final static String OPT_STORE_DATA_CHUNK = "store-data-chunk";
- protected final static String OPT_TERMINALS = "terminals";
protected final static String OPT_TERMINATE = "terminate";
protected final static String OPT_TODAY = "today";
protected final static String OPT_TO = "to";
@@ -100,6 +97,7 @@ abstract class GPCommandLineInterface {
protected final static String OPT_VERBOSE = "verbose";
protected final static String OPT_VERSION = "version";
protected final static String OPT_VISA2 = "visa2";
+ // TODO: extract SEAC to a separate utility
protected final static String OPT_ACR_LIST = "acr-list";
protected final static String OPT_ACR_LIST_ARAM = "acr-list-aram";
protected final static String OPT_ACR_ADD = "acr-add";
@@ -125,11 +123,6 @@ protected static OptionSet parseArguments(String[] argv) throws IOException {
parser.acceptsAll(Arrays.asList("a", OPT_APDU), "Send raw APDU (hex)").withRequiredArg().describedAs("APDU");
parser.acceptsAll(Arrays.asList("s", OPT_SECURE_APDU), "Send raw APDU (hex) via SCP").withRequiredArg().describedAs("APDU");
parser.acceptsAll(Arrays.asList("f", OPT_FORCE), "Force operation");
- parser.accepts(OPT_DUMP, "Dump APDU communication to ").withRequiredArg().ofType(File.class);
- parser.accepts(OPT_REPLAY, "Replay APDU responses from ").withRequiredArg().ofType(File.class);
-
- // Special options
- parser.accepts(OPT_TERMINALS, "Use PC/SC provider from ").withRequiredArg();
// Applet operation options
parser.accepts(OPT_CAP, "Use a CAP file as source").withRequiredArg().ofType(File.class);
diff --git a/tool/src/main/java/pro/javacard/gp/GPTool.java b/tool/src/main/java/pro/javacard/gp/GPTool.java
index 2e992bad..1733e767 100644
--- a/tool/src/main/java/pro/javacard/gp/GPTool.java
+++ b/tool/src/main/java/pro/javacard/gp/GPTool.java
@@ -20,9 +20,7 @@
*/
package pro.javacard.gp;
-import apdu4j.CommandAPDU;
import apdu4j.*;
-import apdu4j.providers.APDUReplayProvider;
import apdu4j.terminals.LoggingCardTerminal;
import joptsimple.OptionSet;
import pro.javacard.AID;
@@ -34,9 +32,12 @@
import pro.javacard.gp.PlaintextKeys.Diversification;
import javax.crypto.Cipher;
-import javax.smartcardio.*;
-import javax.smartcardio.CardTerminals.State;
-import java.io.*;
+import javax.smartcardio.CardTerminal;
+import javax.smartcardio.TerminalFactory;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
@@ -44,785 +45,689 @@
import java.util.*;
import java.util.stream.Collectors;
+// Does the CLI parameter parsing and associated execution
public final class GPTool extends GPCommandLineInterface {
- private static boolean isVerbose = false;
+ private boolean isVerbose = false;
- public static void main(String[] argv) throws Exception {
- OptionSet args = parseArguments(argv);
-
- // Set up slf4j simple in a way that pleases us
- System.setProperty("org.slf4j.simpleLogger.showThreadName", "false");
- System.setProperty("org.slf4j.simpleLogger.levelInBrackets", "true");
- System.setProperty("org.slf4j.simpleLogger.showShortLogName", "true");
-
- if (args.has(OPT_VERBOSE)) {
- System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "debug");
- isVerbose = true;
- } else {
- System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "warn");
+ // To keep basic gp.jar together with apdu4j app, this is just a minimalist wrapper
+ public static void main(String[] argv) {
+ try {
+ OptionSet args = parseArguments(argv);
+ TerminalFactory tf = TerminalManager.getTerminalFactory();
+ String reader = (String) args.valueOf(OPT_READER);
+ if (reader == null)
+ reader = System.getenv("GP_READER");
+ Optional t = TerminalManager.getInstance(tf.terminals()).dwim(reader, System.getenv("GP_READER_IGNORE"), Collections.emptyList());
+ if (!t.isPresent()) {
+ System.err.println("Specify reader with -r/$GP_READER");
+ System.exit(1);
+ }
+ t = t.map(e -> args.has(OPT_DEBUG) ? LoggingCardTerminal.getInstance(e) : e);
+ int ret = new GPTool().run(CardBIBO.wrap(t.get().connect("*")), argv);
+ System.exit(ret);
+ } catch (Exception e) {
+ System.err.println("Error: " + e.getMessage());
+ System.exit(1);
}
+ }
- if (args.has(OPT_DEBUG)) {
- System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "trace");
- }
+ public int run(BIBO bibo, String[] argv) {
+ try {
+ OptionSet args = parseArguments(argv);
- if (args.has(OPT_VERSION) || args.has(OPT_VERBOSE) || args.has(OPT_DEBUG) || args.has(OPT_INFO)) {
- String version = GPSession.getVersion();
- // Append host information
- version += "\nRunning on " + System.getProperty("os.name");
- version += " " + System.getProperty("os.version");
- version += " " + System.getProperty("os.arch");
- version += ", Java " + System.getProperty("java.version");
- version += " by " + System.getProperty("java.vendor");
- System.out.println("GlobalPlatformPro " + version);
-
- // Test for unlimited crypto
- if (Cipher.getMaxAllowedKeyLength("AES") == 128) {
- System.out.println("Unlimited crypto policy is NOT installed!");
+ // Set up slf4j simple in a way that pleases us
+ System.setProperty("org.slf4j.simpleLogger.showThreadName", "false");
+ System.setProperty("org.slf4j.simpleLogger.levelInBrackets", "true");
+ System.setProperty("org.slf4j.simpleLogger.showShortLogName", "true");
+
+ if (args.has(OPT_VERBOSE)) {
+ System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "debug");
+ isVerbose = true;
+ } else {
+ System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "warn");
}
- }
- // Load a CAP file, if specified
- CAPFile cap = null;
- if (args.has(OPT_CAP)) {
- File capfile = (File) args.valueOf(OPT_CAP);
- try (FileInputStream fin = new FileInputStream(capfile)) {
- cap = CAPFile.fromStream(fin);
+ if (args.has(OPT_DEBUG)) {
+ System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "trace");
}
- if (args.has(OPT_INFO)) {
- System.out.println("**** CAP info of " + capfile.getName());
- cap.dump(System.out);
- if (args.specs().size() == 2) {
- // Exit after --cap --info
- System.exit(0);
+
+ if (args.has(OPT_VERSION) || args.has(OPT_VERBOSE) || args.has(OPT_DEBUG) || args.has(OPT_INFO)) {
+ String version = GPSession.getVersion();
+ // Append host information
+ version += "\nRunning on " + System.getProperty("os.name");
+ version += " " + System.getProperty("os.version");
+ version += " " + System.getProperty("os.arch");
+ version += ", Java " + System.getProperty("java.version");
+ version += " by " + System.getProperty("java.vendor");
+ System.out.println("GlobalPlatformPro " + version);
+
+ // Test for unlimited crypto
+ if (Cipher.getMaxAllowedKeyLength("AES") == 128) {
+ System.out.println("Unlimited crypto policy is NOT installed!");
}
}
- }
- if (args.has(OPT_LIST_PRIVS)) {
- System.out.println("# Known privileges:");
- System.out.println(Arrays.asList(Privilege.values()).stream().map(i -> i.toString()).collect(Collectors.joining("\n")));
- }
+ // Load a CAP file, if specified
+ CAPFile cap = null;
+ if (args.has(OPT_CAP)) {
+ File capfile = (File) args.valueOf(OPT_CAP);
+ try (FileInputStream fin = new FileInputStream(capfile)) {
+ cap = CAPFile.fromStream(fin);
+ }
+ if (args.has(OPT_INFO)) {
+ System.out.println("**** CAP info of " + capfile.getName());
+ cap.dump(System.out);
+ if (args.specs().size() == 2) {
+ // Exit after --cap --info
+ return 0;
+ }
+ }
+ }
- // Now actually talk to possible terminals
- try {
- final TerminalFactory tf;
+ if (args.has(OPT_LIST_PRIVS)) {
+ System.out.println("# Known privileges:");
+ System.out.println(Arrays.asList(Privilege.values()).stream().map(i -> i.toString()).collect(Collectors.joining("\n")));
+ }
- if (args.has(OPT_REPLAY)) {
- // Replay responses from a file
- // FIXME: use the generic provider interface and drop command line options
- File f = (File) args.valueOf(OPT_REPLAY);
- try (FileInputStream fin = new FileInputStream(f)) {
- tf = TerminalFactory.getInstance("PC/SC", fin, new APDUReplayProvider());
+ // Now actually talk to possible terminals
+ APDUBIBO channel = new APDUBIBO(bibo);
+ // Send all raw APDU-s to the default-selected application of the card
+ if (args.has(OPT_APDU)) {
+ // Select the application, if present
+ AID target = null;
+ if (args.has(OPT_APPLET)) {
+ target = AID.fromString(args.valueOf(OPT_APPLET));
+ } else if (cap != null) {
+ target = cap.getAppletAIDs().get(0); // FIXME: generalize and only work if one
+ }
+ if (target != null) {
+ verbose("Selecting " + target);
+ channel.transmit(new CommandAPDU(0x00, ISO7816.INS_SELECT, 0x04, 0x00, target.getBytes()));
}
+ for (Object s : args.valuesOf(OPT_APDU)) {
+ CommandAPDU c = new CommandAPDU(HexUtils.stringToBin((String) s));
+ channel.transmit(c);
+ }
+ }
+
+ Map env = System.getenv();
+
+ // GlobalPlatform specific
+ final GPSession gp;
+ if (args.has(OPT_SDAID)) {
+ gp = GPSession.connect(channel, AID.fromString(args.valueOf(OPT_SDAID)));
+ } else if (env.containsKey("GP_AID")) {
+ gp = GPSession.connect(channel, AID.fromString(env.get("GP_AID")));
} else {
- tf = TerminalManager.getTerminalFactory((String) args.valueOf(OPT_TERMINALS));
+ gp = GPSession.discover(channel);
}
- CardTerminals terminals = tf.terminals();
+ if (args.has(OPT_TOKEN_KEY)) {
+ gp.setDMTokenGenerator(new DMTokenGenerator(getRSAPrivateKey(args.valueOf(OPT_TOKEN_KEY).toString())));
+ } else {
+ gp.setDMTokenGenerator(new DMTokenGenerator(null));
+ }
- // List terminals if needed
- if (args.has(OPT_DEBUG)) {
- System.out.println("# Detected readers from " + tf.getProvider().getName());
- for (CardTerminal term : terminals.list()) {
- String c = " ";
- if (term.isCardPresent()) {
- c = "*";
- if (ignoreReader(term.getName())) {
- c = "I";
- }
- }
- System.out.println("[" + c + "] " + term.getName());
- }
+ // Don't do sanity checks, just run asked commands
+ if (args.has(OPT_FORCE))
+ gp.setStrict(false);
+
+ // Extract information
+ if (args.has(OPT_INFO)) {
+ GPData.dump(channel);
}
- // Select terminal(s) to work on
- List do_readers;
- if (args.has(OPT_READER) || System.getenv().containsKey("GP_READER")) {
- String reader = System.getenv("GP_READER");
- if (args.has(OPT_READER))
- reader = (String) args.valueOf(OPT_READER);
- CardTerminal t = terminals.getTerminal(reader);
- if (t == null) {
- fail("Reader \"" + reader + "\" not found.");
- }
- do_readers = Arrays.asList(t);
+ // Normally assume a single master key
+ final GPCardKeys keys;
+
+ if (args.has(OPT_KEYS)) {
+ // keys come from custom provider
+ fail("Not yet implemented");
+ keys = PlaintextKeys.defaultKey();
} else {
- List tmp = terminals.list(State.CARD_PRESENT);
- do_readers = new ArrayList<>();
- for (CardTerminal t : tmp) {
- if (!ignoreReader(t.getName())) {
- do_readers.add(t);
+ PlaintextKeys keyz;
+ if (args.has(OPT_KEY)) {
+ byte[] k = HexUtils.stringToBin((String) args.valueOf(OPT_KEY));
+ byte[] kcv = null;
+
+ if (args.has(OPT_KCV)) {
+ kcv = HexUtils.stringToBin((String) args.valueOf(OPT_KCV));
+ }
+
+ keyz = PlaintextKeys.fromMasterKey(k, kcv);
+ } else {
+ Optional params = SecureChannelParameters.fromEnvironment();
+ // XXX: better checks for exclusive key options
+ if (args.has(OPT_KEY_MAC) && args.has(OPT_KEY_ENC) && args.has(OPT_KEY_DEK)) {
+ byte[] enc = HexUtils.stringToBin((String) args.valueOf(OPT_KEY_ENC));
+ byte[] mac = HexUtils.stringToBin((String) args.valueOf(OPT_KEY_MAC));
+ byte[] dek = HexUtils.stringToBin((String) args.valueOf(OPT_KEY_DEK));
+ keyz = PlaintextKeys.fromKeys(enc, mac, dek);
+ } else if (params.isPresent()) {
+ keyz = (PlaintextKeys) params.get().getCardKeys();
} else {
- if (args.has(OPT_VERBOSE)) {
- System.out.println("# Ignoring " + t.getName());
+ if (needsAuthentication(args)) {
+ System.out.println("Warning: no keys given, using default test key " + HexUtils.bin2hex(PlaintextKeys.defaultKeyBytes));
}
+ keyz = PlaintextKeys.defaultKey();
}
}
- }
- if (do_readers.size() == 0) {
- fail("No smart card readers with a card found");
+ // "gp -l -emv" should still work
+ if (args.has(OPT_VISA2)) {
+ keyz.setDiversifier(Diversification.VISA2);
+ } else if (args.has(OPT_EMV)) {
+ keyz.setDiversifier(Diversification.EMV);
+ } else if (args.has(OPT_KDF3)) {
+ keyz.setDiversifier(Diversification.KDF3);
+ } else if (args.has(OPT_KDF)) {
+ keyz.setDiversifier(getDiversificationOrFail(args, OPT_KDF));
+ }
+
+ if (args.has(OPT_KEY_VERSION)) {
+ keyz.setVersion(GPUtils.intValue((String) args.valueOf(OPT_KEY_VERSION)));
+ }
+ keys = keyz;
}
- // Work all readers
- for (CardTerminal reader : do_readers) {
- if (do_readers.size() > 1) {
- System.out.println("# " + reader.getName());
- }
- // Wrap with logging if requested
- if (args.has(OPT_DEBUG)) {
- // And with APDU dumping
- OutputStream o = null;
- if (args.has(OPT_DUMP)) {
- File f = (File) args.valueOf(OPT_DUMP);
- o = new FileOutputStream(f);
- }
- reader = LoggingCardTerminal.getInstance(reader, o);
- }
-
- Card card = null;
- APDUBIBO channel = null;
- try {
- // Establish connection
- try {
- card = reader.connect("*");
- // We use apdu4j which by default uses jnasmartcardio
- // which uses real SCardBeginTransaction
- card.beginExclusive();
- channel = CardChannelBIBO.getBIBO(card.getBasicChannel());
- } catch (CardException e) {
- System.err.println("Could not connect to " + reader.getName() + ": " + TerminalManager.getExceptionMessage(e));
- continue;
- }
-
- if (args.has(OPT_INFO) || args.has(OPT_VERBOSE)) {
- System.out.println("Reader: " + reader.getName());
- System.out.println("ATR: " + HexUtils.bin2hex(card.getATR().getBytes()));
- System.out.println("More information about your card:");
- System.out.println(" http://smartcard-atr.appspot.com/parse?ATR=" + HexUtils.bin2hex(card.getATR().getBytes()));
- System.out.println();
- }
-
- // Send all raw APDU-s to the default-selected application of the card
- if (args.has(OPT_APDU)) {
- // Select the application, if present
- AID target = null;
- if (args.has(OPT_APPLET)) {
- target = AID.fromString(args.valueOf(OPT_APPLET));
- } else if (cap != null) {
- target = cap.getAppletAIDs().get(0); // FIXME: generalize and only work if one
- }
- if (target != null) {
- verbose("Selecting " + target);
- channel.transmit(new CommandAPDU(0x00, ISO7816.INS_SELECT, 0x04, 0x00, target.getBytes()));
- }
- for (Object s : args.valuesOf(OPT_APDU)) {
- CommandAPDU c = new CommandAPDU(HexUtils.stringToBin((String) s));
- channel.transmit(c);
- }
- }
+ // XXX: leftover
+ if (args.has(OPT_OP201)) {
+ gp.setSpec(GPSpec.OP201);
+ }
- Map env = System.getenv();
+ // Override block size for stupidly broken readers.
+ // See https://github.com/martinpaljak/GlobalPlatformPro/issues/32
+ // The name of the option comes from a common abbreviation as well as dd utility
+ if (args.has(OPT_BS)) {
+ gp.setBlockSize((int) args.valueOf(OPT_BS));
+ }
- // GlobalPlatform specific
- final GPSession gp;
- if (args.has(OPT_SDAID)) {
- gp = GPSession.connect(channel, AID.fromString(args.valueOf(OPT_SDAID)));
- } else if (env.containsKey("GP_AID")) {
- gp = GPSession.connect(channel, AID.fromString(env.get("GP_AID")));
- } else {
- // Oracle only applies if no other arguments given
- gp = GPSession.discover(channel);
- // FIXME: would like to get AID from oracle as well.
- }
+ // list access rules from ARA-M TODO: move to separate util
+ if (args.has(OPT_ACR_LIST_ARAM)) {
+ SEAccessControlUtility.acrList(gp);
+ }
- if (args.has(OPT_TOKEN_KEY)) {
- gp.setDMTokenGenerator(new DMTokenGenerator(getRSAPrivateKey(args.valueOf(OPT_TOKEN_KEY).toString())));
- } else {
- gp.setDMTokenGenerator(new DMTokenGenerator(null));
+ // Authenticate, only if needed
+ if (needsAuthentication(args)) {
+ EnumSet mode = GPSession.defaultMode.clone();
+ // Override default mode if needed.
+ if (args.has(OPT_SC_MODE)) {
+ mode.clear();
+ for (Object s : args.valuesOf(OPT_SC_MODE)) {
+ mode.add(APDUMode.fromString((String) s));
}
+ }
- // Don't do sanity checks, just run asked commands
- if (args.has(OPT_FORCE))
- gp.setStrict(false);
+ // IMPORTANT PLACE. Possibly brick the card now, if keys don't match.
+ gp.openSecureChannel(keys, null, null, mode);
- // Extract information
- if (args.has(OPT_INFO)) {
- GPData.dump(channel);
+ // --secure-apdu or -s
+ if (args.has(OPT_SECURE_APDU)) {
+ for (Object s : args.valuesOf(OPT_SECURE_APDU)) {
+ CommandAPDU c = new CommandAPDU(HexUtils.stringToBin((String) s));
+ gp.transmit(c);
}
+ }
- // Normally assume a single master key
- final GPCardKeys keys;
+ // list access rules from ARA-* via STORE DATA
+ if (args.has(OPT_ACR_LIST)) {
+ SEAccessControl.AcrListFetcher fetcher = new SEAccessControl.AcrListFetcher(gp);
+ byte[] r = fetcher.get(args.has(OPT_ACR_AID) ? AID.fromString(args.valueOf(OPT_ACR_AID)) : null);
+ SEAccessControl.AcrListResponse resp = SEAccessControl.AcrListResponse.fromBytes(r);
+ SEAccessControl.printList(resp.acrList);
+ }
- if (args.has(OPT_KEYS)) {
- // keys come from custom provider
- fail("Not yet implemented");
- keys = PlaintextKeys.defaultKey();
- } else {
- PlaintextKeys keyz;
- if (args.has(OPT_KEY)) {
- byte[] k = HexUtils.stringToBin((String) args.valueOf(OPT_KEY));
- byte[] kcv = null;
+ // --delete or --delete --default
+ if (args.has(OPT_DELETE)) {
+ GPRegistry reg = gp.getRegistry();
- if (args.has(OPT_KCV)) {
- kcv = HexUtils.stringToBin((String) args.valueOf(OPT_KCV));
- }
+ // DWIM: assume that default selected is the one to be deleted
+ if (args.has(OPT_DEFAULT)) {
+ Optional def = reg.getDefaultSelectedAID();
- keyz = PlaintextKeys.fromMasterKey(k, kcv);
+ if (def.isPresent()) {
+ gp.deleteAID(def.get(), false);
} else {
- Optional params = SecureChannelParameters.fromEnvironment();
- // XXX: better checks for exclusive key options
- if (args.has(OPT_KEY_MAC) && args.has(OPT_KEY_ENC) && args.has(OPT_KEY_DEK)) {
- byte[] enc = HexUtils.stringToBin((String) args.valueOf(OPT_KEY_ENC));
- byte[] mac = HexUtils.stringToBin((String) args.valueOf(OPT_KEY_MAC));
- byte[] dek = HexUtils.stringToBin((String) args.valueOf(OPT_KEY_DEK));
- keyz = PlaintextKeys.fromKeys(enc, mac, dek);
- } else if (params.isPresent()) {
- keyz = (PlaintextKeys) params.get().getCardKeys();
+ System.err.println("Could not identify default selected application!");
+ }
+ }
+ List aids = args.valuesOf(OPT_DELETE).stream().map(a -> AID.fromString(a)).collect(Collectors.toList());
+ for (AID aid : aids) {
+ try {
+ // If the AID represents a package or otherwise force is enabled.
+ boolean deleteDeps = reg.allPackageAIDs().contains(aid) || args.has(OPT_FORCE);
+ gp.deleteAID(aid, deleteDeps);
+ } catch (GPException e) {
+ if (!gp.getRegistry().allAIDs().contains(aid)) {
+ System.err.println("Could not delete AID (not present on card): " + aid);
} else {
- if (needsAuthentication(args)) {
- System.out.println("Warning: no keys given, using default test key " + HexUtils.bin2hex(PlaintextKeys.defaultKeyBytes));
+ System.err.println("Could not delete AID: " + aid);
+ if (e.sw == 0x6985) {
+ System.err.println("Deletion not allowed. Some app still active?");
+ } else {
+ throw e;
}
- keyz = PlaintextKeys.defaultKey();
}
}
+ }
+ }
- // "gp -l -emv" should still work
- if (args.has(OPT_VISA2)) {
- keyz.setDiversifier(Diversification.VISA2);
- } else if (args.has(OPT_EMV)) {
- keyz.setDiversifier(Diversification.EMV);
- } else if (args.has(OPT_KDF3)) {
- keyz.setDiversifier(Diversification.KDF3);
- } else if (args.has(OPT_KDF)) {
- keyz.setDiversifier(getDiversificationOrFail(args, OPT_KDF));
- }
-
- if (args.has(OPT_KEY_VERSION)) {
- keyz.setVersion(GPUtils.intValue((String) args.valueOf(OPT_KEY_VERSION)));
+ // --uninstall
+ if (args.has(OPT_UNINSTALL)) {
+ List caps = getCapFileList(args, OPT_UNINSTALL);
+ for (CAPFile instcap : caps) {
+ AID aid = instcap.getPackageAID();
+ if (!gp.getRegistry().allAIDs().contains(aid)) {
+ System.out.println(aid + " is not present on card!");
+ } else {
+ gp.deleteAID(aid, true);
+ System.out.println(aid + " deleted.");
}
- keys = keyz;
}
+ }
- // XXX: leftover
- if (args.has(OPT_OP201)) {
- gp.setSpec(GPSpec.OP201);
- }
+ // --load
+ if (args.has(OPT_LOAD)) {
+ List caps = getCapFileList(args, OPT_LOAD);
+ for (CAPFile loadcap : caps) {
+ if (isVerbose) {
+ loadcap.dump(System.out);
+ }
- // Override block size for stupidly broken readers.
- // See https://github.com/martinpaljak/GlobalPlatformPro/issues/32
- // The name of the option comes from a common abbreviation as well as dd utility
- if (args.has(OPT_BS)) {
- gp.setBlockSize((int) args.valueOf(OPT_BS));
+ calculateDapPropertiesAndLoadCap(args, gp, loadcap);
}
+ }
- // list access rules from ARA-M
- if (args.has(OPT_ACR_LIST_ARAM)) {
- SEAccessControlUtility.acrList(gp);
+ // --put-key
+ // Load a RSA public key (for DAP purposes)
+ if (args.has(OPT_PUT_KEY)) {
+ int keyVersion = 0x73; // Default DAP version
+ if (args.has(OPT_NEW_KEY_VERSION)) {
+ keyVersion = GPUtils.intValue(args.valueOf(OPT_NEW_KEY_VERSION).toString());
}
- // Authenticate, only if needed
- if (needsAuthentication(args)) {
- EnumSet mode = GPSession.defaultMode.clone();
- // Override default mode if needed.
- if (args.has(OPT_SC_MODE)) {
- mode.clear();
- for (Object s : args.valuesOf(OPT_SC_MODE)) {
- mode.add(APDUMode.fromString((String) s));
- }
+ try (FileInputStream fin = new FileInputStream(new File(args.valueOf(OPT_PUT_KEY).toString()))) {
+ // Get public key
+ PublicKey key = GPCrypto.pem2PublicKey(fin);
+ if (key instanceof RSAPublicKey) {
+ gp.putKey((RSAPublicKey) key, keyVersion);
}
+ }
+ }
- // IMPORTANT PLACE. Possibly brick the card now, if keys don't match.
- gp.openSecureChannel(keys, null, null, mode);
+ // --install (--applet --create --privs --params )
+ if (args.has(OPT_INSTALL)) {
+ final File capfile;
+ capfile = (File) args.valueOf(OPT_INSTALL);
- // --secure-apdu or -s
- if (args.has(OPT_SECURE_APDU)) {
- for (Object s : args.valuesOf(OPT_SECURE_APDU)) {
- CommandAPDU c = new CommandAPDU(HexUtils.stringToBin((String) s));
- gp.transmit(c);
- }
- }
+ final CAPFile instcap;
+ try (FileInputStream fin = new FileInputStream(capfile)) {
+ instcap = CAPFile.fromStream(fin);
+ }
- // list access rules from ARA-* via STORE DATA
- if (args.has(OPT_ACR_LIST)) {
- SEAccessControl.AcrListFetcher fetcher = new SEAccessControl.AcrListFetcher(gp);
- byte[] r = fetcher.get(args.has(OPT_ACR_AID) ? AID.fromString(args.valueOf(OPT_ACR_AID)) : null);
- SEAccessControl.AcrListResponse resp = SEAccessControl.AcrListResponse.fromBytes(r);
- SEAccessControl.printList(resp.acrList);
- }
+ if (args.has(OPT_VERBOSE)) {
+ instcap.dump(System.out);
+ }
- // --delete or --delete --default
- if (args.has(OPT_DELETE)) {
- GPRegistry reg = gp.getRegistry();
+ GPRegistry reg = gp.getRegistry();
- // DWIM: assume that default selected is the one to be deleted
- if (args.has(OPT_DEFAULT)) {
- Optional def = reg.getDefaultSelectedAID();
+ // Remove existing load file
+ if (args.has(OPT_FORCE) && reg.allPackageAIDs().contains(instcap.getPackageAID())) {
+ gp.deleteAID(instcap.getPackageAID(), true);
+ }
- if (def.isPresent()) {
- gp.deleteAID(def.get(), false);
- } else {
- System.err.println("Could not identify default selected application!");
- }
- }
- List aids = args.valuesOf(OPT_DELETE).stream().map(a -> AID.fromString(a)).collect(Collectors.toList());
- for (AID aid : aids) {
- try {
- // If the AID represents a package or otherwise force is enabled.
- boolean deleteDeps = reg.allPackageAIDs().contains(aid) || args.has(OPT_FORCE);
- gp.deleteAID(aid, deleteDeps);
- } catch (GPException e) {
- if (!gp.getRegistry().allAIDs().contains(aid)) {
- System.err.println("Could not delete AID (not present on card): " + aid);
- } else {
- System.err.println("Could not delete AID: " + aid);
- if (e.sw == 0x6985) {
- System.err.println("Deletion not allowed. Some app still active?");
- } else {
- throw e;
- }
- }
- }
- }
- }
+ // Load
+ if (instcap.getAppletAIDs().size() <= 1) {
+ calculateDapPropertiesAndLoadCap(args, gp, instcap);
+ }
- // --uninstall
- if (args.has(OPT_UNINSTALL)) {
- List caps = getCapFileList(args, OPT_UNINSTALL);
- for (CAPFile instcap : caps) {
- AID aid = instcap.getPackageAID();
- if (!gp.getRegistry().allAIDs().contains(aid)) {
- System.out.println(aid + " is not present on card!");
- } else {
- gp.deleteAID(aid, true);
- System.out.println(aid + " deleted.");
- }
- }
+ // Install
+ final AID appaid;
+ final AID instanceaid;
+ if (instcap.getAppletAIDs().size() == 0) {
+ return 1;
+ } else if (instcap.getAppletAIDs().size() > 1) {
+ if (args.has(OPT_APPLET)) {
+ appaid = AID.fromString(args.valueOf(OPT_APPLET));
+ } else {
+ fail("CAP contains more than one applet, specify the right one with --" + OPT_APPLET);
+ return 1;
}
+ } else {
+ appaid = instcap.getAppletAIDs().get(0);
+ }
- // --load
- if (args.has(OPT_LOAD)) {
- List caps = getCapFileList(args, OPT_LOAD);
- for (CAPFile loadcap : caps) {
- if (isVerbose) {
- loadcap.dump(System.out);
- }
+ // override
+ if (args.has(OPT_CREATE)) {
+ instanceaid = AID.fromString(args.valueOf(OPT_CREATE));
+ } else {
+ instanceaid = appaid;
+ }
- calculateDapPropertiesAndLoadCap(args, gp, loadcap);
- }
- }
+ Privileges privs = getInstPrivs(args);
- // --put-key
- // Load a RSA public key (for DAP purposes)
- if (args.has(OPT_PUT_KEY)) {
- int keyVersion = 0x73; // Default DAP version
- if (args.has(OPT_NEW_KEY_VERSION)) {
- keyVersion = GPUtils.intValue(args.valueOf(OPT_NEW_KEY_VERSION).toString());
- }
+ // Remove existing default app
+ if (args.has(OPT_FORCE) && (reg.getDefaultSelectedAID().isPresent() && privs.has(Privilege.CardReset))) {
+ gp.deleteAID(reg.getDefaultSelectedAID().get(), false);
+ }
- try (FileInputStream fin = new FileInputStream(new File(args.valueOf(OPT_PUT_KEY).toString()))) {
- // Get public key
- PublicKey key = GPCrypto.pem2PublicKey(fin);
- if (key instanceof RSAPublicKey) {
- gp.putKey((RSAPublicKey) key, keyVersion);
- }
- }
- }
+ // warn
+ if (gp.getRegistry().allAppletAIDs().contains(instanceaid)) {
+ System.err.println("WARNING: Applet " + instanceaid + " already present on card");
+ }
- // --install (--applet --create --privs --params )
- if (args.has(OPT_INSTALL)) {
- final File capfile;
- capfile = (File) args.valueOf(OPT_INSTALL);
+ // shoot
+ gp.installAndMakeSelectable(instcap.getPackageAID(), appaid, instanceaid, privs, getInstParams(args));
+ }
- final CAPFile instcap;
- try (FileInputStream fin = new FileInputStream(capfile)) {
- instcap = CAPFile.fromStream(fin);
- }
+ // --create (--applet --package or --cap )
+ if (args.has(OPT_CREATE) && !args.has(OPT_INSTALL)) {
+ AID packageAID = null;
+ AID appletAID = null;
- if (args.has(OPT_VERBOSE)) {
- instcap.dump(System.out);
- }
+ // Load AID-s from cap if present
+ if (cap != null) {
+ packageAID = cap.getPackageAID();
+ if (cap.getAppletAIDs().size() != 1) {
+ throw new IllegalArgumentException("There should be only one applet in CAP. Use --" + OPT_APPLET + " instead.");
+ }
+ appletAID = cap.getAppletAIDs().get(0);
+ }
- GPRegistry reg = gp.getRegistry();
+ // override
+ if (args.has(OPT_PACKAGE)) {
+ packageAID = AID.fromString(args.valueOf(OPT_PACKAGE));
+ }
+ if (args.has(OPT_APPLET)) {
+ appletAID = AID.fromString(args.valueOf(OPT_APPLET));
+ }
- // Remove existing load file
- if (args.has(OPT_FORCE) && reg.allPackageAIDs().contains(instcap.getPackageAID())) {
- gp.deleteAID(instcap.getPackageAID(), true);
- }
+ // check
+ if (packageAID == null || appletAID == null)
+ throw new IllegalArgumentException("Need --" + OPT_PACKAGE + " and --" + OPT_APPLET + " or --" + OPT_CAP);
- // Load
- if (instcap.getAppletAIDs().size() <= 1) {
- calculateDapPropertiesAndLoadCap(args, gp, instcap);
- }
+ // warn
+ if (gp.getRegistry().allAIDs().contains(appletAID)) {
+ System.err.println("WARNING: Applet " + appletAID + " already present on card");
+ }
- // Install
- final AID appaid;
- final AID instanceaid;
- if (instcap.getAppletAIDs().size() == 0) {
- return;
- } else if (instcap.getAppletAIDs().size() > 1) {
- if (args.has(OPT_APPLET)) {
- appaid = AID.fromString(args.valueOf(OPT_APPLET));
- } else {
- fail("CAP contains more than one applet, specify the right one with --" + OPT_APPLET);
- return;
- }
- } else {
- appaid = instcap.getAppletAIDs().get(0);
- }
+ // shoot
+ AID instanceAID = AID.fromString(args.valueOf(OPT_CREATE));
+ gp.installAndMakeSelectable(packageAID, appletAID, instanceAID, getInstPrivs(args), getInstParams(args));
+ }
- // override
- if (args.has(OPT_CREATE)) {
- instanceaid = AID.fromString(args.valueOf(OPT_CREATE));
- } else {
- instanceaid = appaid;
- }
+ // --domain
+ if (args.has(OPT_DOMAIN)) {
+ // Arguments check
+ if ((args.has(OPT_ALLOW_FROM) || args.has(OPT_ALLOW_TO)) && args.has(OPT_PARAMS)) {
+ fail("SSD extradition options can't be used with SSD installation parameters");
+ }
- Privileges privs = getInstPrivs(args);
+ // Default AID-s
+ AID packageAID = new AID("A0000001515350");
+ AID appletAID = new AID("A000000151535041");
- // Remove existing default app
- if (args.has(OPT_FORCE) && (reg.getDefaultSelectedAID().isPresent() && privs.has(Privilege.CardReset))) {
- gp.deleteAID(reg.getDefaultSelectedAID().get(), false);
- }
+ // Override if necessary
+ if (args.has(OPT_PACKAGE) && args.has(OPT_APPLET)) {
+ packageAID = AID.fromString(args.valueOf(OPT_PACKAGE));
+ appletAID = AID.fromString(args.valueOf(OPT_APPLET));
+ } else {
+ System.out.println("Note: using default AID-s for SSD instantiation: " + appletAID + " from " + packageAID);
+ }
+ AID instanceAID = AID.fromString(args.valueOf(OPT_DOMAIN));
- // warn
- if (gp.getRegistry().allAppletAIDs().contains(instanceaid)) {
- System.err.println("WARNING: Applet " + instanceaid + " already present on card");
- }
+ // Extra privileges
+ Privileges privs = getInstPrivs(args);
+ privs.add(Privilege.SecurityDomain);
- // shoot
- gp.installAndMakeSelectable(instcap.getPackageAID(), appaid, instanceaid, privs, getInstParams(args));
+ // Extradition rules
+ byte[] params = new byte[0];
+ if (args.has(OPT_PARAMS)) {
+ params = getInstParams(args);
+ } else {
+ if (args.has(OPT_ALLOW_TO)) {
+ params = GPUtils.concatenate(params, new byte[]{(byte) 0x82, 0x01, 0x20});
}
-
- // --create (--applet --package or --cap )
- if (args.has(OPT_CREATE) && !args.has(OPT_INSTALL)) {
- AID packageAID = null;
- AID appletAID = null;
-
- // Load AID-s from cap if present
- if (cap != null) {
- packageAID = cap.getPackageAID();
- if (cap.getAppletAIDs().size() != 1) {
- throw new IllegalArgumentException("There should be only one applet in CAP. Use --" + OPT_APPLET + " instead.");
- }
- appletAID = cap.getAppletAIDs().get(0);
- }
-
- // override
- if (args.has(OPT_PACKAGE)) {
- packageAID = AID.fromString(args.valueOf(OPT_PACKAGE));
- }
- if (args.has(OPT_APPLET)) {
- appletAID = AID.fromString(args.valueOf(OPT_APPLET));
- }
-
- // check
- if (packageAID == null || appletAID == null)
- throw new IllegalArgumentException("Need --" + OPT_PACKAGE + " and --" + OPT_APPLET + " or --" + OPT_CAP);
-
- // warn
- if (gp.getRegistry().allAIDs().contains(appletAID)) {
- System.err.println("WARNING: Applet " + appletAID + " already present on card");
- }
-
- // shoot
- AID instanceAID = AID.fromString(args.valueOf(OPT_CREATE));
- gp.installAndMakeSelectable(packageAID, appletAID, instanceAID, getInstPrivs(args), getInstParams(args));
+ if (args.has(OPT_ALLOW_FROM)) {
+ params = GPUtils.concatenate(params, new byte[]{(byte) 0x87, 0x01, 0x20});
}
+ }
- // --domain
- if (args.has(OPT_DOMAIN)) {
- // Arguments check
- if ((args.has(OPT_ALLOW_FROM) || args.has(OPT_ALLOW_TO)) && args.has(OPT_PARAMS)) {
- fail("SSD extradition options can't be used with SSD installation parameters");
- }
-
- // Default AID-s
- AID packageAID = new AID("A0000001515350");
- AID appletAID = new AID("A000000151535041");
-
- // Override if necessary
- if (args.has(OPT_PACKAGE) && args.has(OPT_APPLET)) {
- packageAID = AID.fromString(args.valueOf(OPT_PACKAGE));
- appletAID = AID.fromString(args.valueOf(OPT_APPLET));
- } else {
- System.out.println("Note: using default AID-s for SSD instantiation: " + appletAID + " from " + packageAID);
- }
- AID instanceAID = AID.fromString(args.valueOf(OPT_DOMAIN));
-
- // Extra privileges
- Privileges privs = getInstPrivs(args);
- privs.add(Privilege.SecurityDomain);
-
- // Extradition rules
- byte[] params = new byte[0];
- if (args.has(OPT_PARAMS)) {
- params = getInstParams(args);
- } else {
- if (args.has(OPT_ALLOW_TO)) {
- params = GPUtils.concatenate(params, new byte[]{(byte) 0x82, 0x01, 0x20});
- }
- if (args.has(OPT_ALLOW_FROM)) {
- params = GPUtils.concatenate(params, new byte[]{(byte) 0x87, 0x01, 0x20});
- }
- }
-
- // shoot
- gp.installAndMakeSelectable(packageAID, appletAID, instanceAID, privs, params);
- }
+ // shoot
+ gp.installAndMakeSelectable(packageAID, appletAID, instanceAID, privs, params);
+ }
- // --move
- if (args.has(OPT_MOVE)) {
- if (!args.has(OPT_TO)) {
- fail("Specify extradition target with --" + OPT_TO);
- }
- AID what = AID.fromString(args.valueOf(OPT_MOVE));
- AID to = AID.fromString(args.valueOf(OPT_TO));
- gp.extradite(what, to);
- }
+ // --move
+ if (args.has(OPT_MOVE)) {
+ if (!args.has(OPT_TO)) {
+ fail("Specify extradition target with --" + OPT_TO);
+ }
+ AID what = AID.fromString(args.valueOf(OPT_MOVE));
+ AID to = AID.fromString(args.valueOf(OPT_TO));
+ gp.extradite(what, to);
+ }
- // --store-data
- // This will split the data, if necessary
- if (args.has(OPT_STORE_DATA)) {
- List blobs = args.valuesOf(OPT_STORE_DATA).stream().map(e -> HexUtils.stringToBin((String) e)).collect(Collectors.toList());
- for (byte[] blob : blobs) {
- if (args.has(OPT_APPLET)) {
- gp.personalize(AID.fromString(args.valueOf(OPT_APPLET)), blob, 0x01);
- } else {
- gp.storeData(blob, 0x1);
- }
- }
+ // --store-data
+ // This will split the data, if necessary
+ if (args.has(OPT_STORE_DATA)) {
+ List blobs = args.valuesOf(OPT_STORE_DATA).stream().map(e -> HexUtils.stringToBin((String) e)).collect(Collectors.toList());
+ for (byte[] blob : blobs) {
+ if (args.has(OPT_APPLET)) {
+ gp.personalize(AID.fromString(args.valueOf(OPT_APPLET)), blob, 0x01);
+ } else {
+ gp.storeData(blob, 0x1);
}
+ }
+ }
- // --store-data-chunk
- // This will collect the chunks and send them one by one
- if (args.has(OPT_STORE_DATA_CHUNK)) {
- List blobs = args.valuesOf(OPT_STORE_DATA_CHUNK).stream().map(e -> HexUtils.stringToBin((String) e)).collect(Collectors.toList());
- if (args.has(OPT_APPLET)) {
- gp.personalize(AID.fromString(args.valueOf(OPT_APPLET)), blobs, 0x01);
- } else {
- gp.storeData(blobs, 0x1);
- }
- }
+ // --store-data-chunk
+ // This will collect the chunks and send them one by one
+ if (args.has(OPT_STORE_DATA_CHUNK)) {
+ List blobs = args.valuesOf(OPT_STORE_DATA_CHUNK).stream().map(e -> HexUtils.stringToBin((String) e)).collect(Collectors.toList());
+ if (args.has(OPT_APPLET)) {
+ gp.personalize(AID.fromString(args.valueOf(OPT_APPLET)), blobs, 0x01);
+ } else {
+ gp.storeData(blobs, 0x1);
+ }
+ }
- if (args.has(OPT_ACR_ADD)) {
- AID aid = null;
- byte[] hash = null;
- AID araAid = SEAccessControl.ACR_AID;
- if (args.has(OPT_APPLET))
- aid = AID.fromString(args.valueOf(OPT_APPLET));
- if (args.has(OPT_ACR_CERT_HASH))
- hash = HexUtils.stringToBin((String) args.valueOf(OPT_ACR_CERT_HASH));
- if (args.has(OPT_ACR_AID))
- araAid = AID.fromString((String) args.valueOf(OPT_ACR_AID));
- if (!args.has(OPT_ACR_RULE)) {
- System.err.println("Must specify an access rule with -" + OPT_ACR_RULE + " (00, 01 or an apdu filter)");
- }
- if (hash != null && hash.length != 20) {
- fail("certificate hash must be 20 bytes");
- }
- SEAccessControlUtility.acrAdd(gp, araAid, aid, hash, HexUtils.stringToBin((String) args.valueOf(OPT_ACR_RULE)));
- }
+ if (args.has(OPT_ACR_ADD)) {
+ AID aid = null;
+ byte[] hash = null;
+ AID araAid = SEAccessControl.ACR_AID;
+ if (args.has(OPT_APPLET))
+ aid = AID.fromString(args.valueOf(OPT_APPLET));
+ if (args.has(OPT_ACR_CERT_HASH))
+ hash = HexUtils.stringToBin((String) args.valueOf(OPT_ACR_CERT_HASH));
+ if (args.has(OPT_ACR_AID))
+ araAid = AID.fromString((String) args.valueOf(OPT_ACR_AID));
+ if (!args.has(OPT_ACR_RULE)) {
+ System.err.println("Must specify an access rule with -" + OPT_ACR_RULE + " (00, 01 or an apdu filter)");
+ }
+ if (hash != null && hash.length != 20) {
+ fail("certificate hash must be 20 bytes");
+ }
+ SEAccessControlUtility.acrAdd(gp, araAid, aid, hash, HexUtils.stringToBin((String) args.valueOf(OPT_ACR_RULE)));
+ }
- // --acr-delete
- if (args.has(OPT_ACR_DELETE)) {
- AID araAid = SEAccessControl.ACR_AID;
- if (args.has(OPT_ACR_AID))
- araAid = AID.fromString(args.valueOf(OPT_ACR_AID));
+ // --acr-delete
+ if (args.has(OPT_ACR_DELETE)) {
+ AID araAid = SEAccessControl.ACR_AID;
+ if (args.has(OPT_ACR_AID))
+ araAid = AID.fromString(args.valueOf(OPT_ACR_AID));
- AID aid = null;
- if (args.has(OPT_APPLET)) {
- aid = AID.fromString(OPT_APPLET);
- }
+ AID aid = null;
+ if (args.has(OPT_APPLET)) {
+ aid = AID.fromString(OPT_APPLET);
+ }
- byte[] hash = null;
- if (args.has(OPT_ACR_CERT_HASH)) {
- hash = HexUtils.stringToBin((String) args.valueOf(OPT_ACR_CERT_HASH));
- if (hash.length != 20)
- fail("certificate hash must be 20 bytes");
- }
+ byte[] hash = null;
+ if (args.has(OPT_ACR_CERT_HASH)) {
+ hash = HexUtils.stringToBin((String) args.valueOf(OPT_ACR_CERT_HASH));
+ if (hash.length != 20)
+ fail("certificate hash must be 20 bytes");
+ }
- SEAccessControlUtility.acrDelete(gp, araAid, aid, hash);
- }
+ SEAccessControlUtility.acrDelete(gp, araAid, aid, hash);
+ }
- // --lock-card
- if (args.has(OPT_LOCK_CARD)) {
- gp.setCardStatus(GPData.lockedStatus);
- }
- // --unlock-card
- if (args.has(OPT_UNLOCK_CARD)) {
- gp.setCardStatus(GPData.securedStatus);
- }
- // --initialize-card
- if (args.has(OPT_INITIALIZE_CARD)) {
+ // --lock-card
+ if (args.has(OPT_LOCK_CARD)) {
+ gp.setCardStatus(GPData.lockedStatus);
+ }
+ // --unlock-card
+ if (args.has(OPT_UNLOCK_CARD)) {
+ gp.setCardStatus(GPData.securedStatus);
+ }
+ // --initialize-card
+ if (args.has(OPT_INITIALIZE_CARD)) {
+ gp.setCardStatus(GPData.initializedStatus);
+ }
+ // --secure-card
+ if (args.has(OPT_SECURE_CARD)) {
+ // Skip INITIALIZED
+ GPRegistryEntry isd = gp.getRegistry().getISD().orElseThrow(() -> new GPException("ISD is null"));
+ if (isd.getLifeCycle() != GPData.initializedStatus) {
+ if (args.has(OPT_FORCE)) {
+ System.out.println("Note: forcing status to INITIALIZED");
gp.setCardStatus(GPData.initializedStatus);
}
- // --secure-card
- if (args.has(OPT_SECURE_CARD)) {
- // Skip INITIALIZED
- GPRegistryEntry isd = gp.getRegistry().getISD().orElseThrow(() -> new GPException("ISD is null"));
- if (isd.getLifeCycle() != GPData.initializedStatus) {
- if (args.has(OPT_FORCE)) {
- System.out.println("Note: forcing status to INITIALIZED");
- gp.setCardStatus(GPData.initializedStatus);
- }
- }
- gp.setCardStatus(GPData.securedStatus);
- }
+ }
+ gp.setCardStatus(GPData.securedStatus);
+ }
- // --lock-applet
- if (args.has(OPT_LOCK_APPLET)) {
- gp.lockUnlockApplet(AID.fromString(args.valueOf(OPT_LOCK_APPLET)), true);
- }
+ // --lock-applet
+ if (args.has(OPT_LOCK_APPLET)) {
+ gp.lockUnlockApplet(AID.fromString(args.valueOf(OPT_LOCK_APPLET)), true);
+ }
- // --unlock-applet
- if (args.has(OPT_UNLOCK_APPLET)) {
- gp.lockUnlockApplet(AID.fromString(args.valueOf(OPT_UNLOCK_APPLET)), false);
- }
+ // --unlock-applet
+ if (args.has(OPT_UNLOCK_APPLET)) {
+ gp.lockUnlockApplet(AID.fromString(args.valueOf(OPT_UNLOCK_APPLET)), false);
+ }
- // --list
- if (args.has(OPT_LIST)) {
- GPCommands.listRegistry(gp.getRegistry(), System.out, args.has(OPT_VERBOSE));
- }
+ // --list
+ if (args.has(OPT_LIST)) {
+ GPCommands.listRegistry(gp.getRegistry(), System.out, args.has(OPT_VERBOSE));
+ }
- // --delete-key
- // TODO: make --delete smart enough
- if (args.has(OPT_DELETE_KEY)) {
- int keyver = GPUtils.intValue((String) args.valueOf(OPT_DELETE_KEY));
- System.out.println("Deleting key " + keyver);
- gp.deleteKey(keyver);
- }
+ // --delete-key
+ // TODO: make --delete smart enough
+ if (args.has(OPT_DELETE_KEY)) {
+ int keyver = GPUtils.intValue((String) args.valueOf(OPT_DELETE_KEY));
+ System.out.println("Deleting key " + keyver);
+ gp.deleteKey(keyver);
+ }
- // TODO: Move to GPCommands
- // --unlock
- if (args.has(OPT_UNLOCK)) {
- // Write default keys
- final boolean replace;
- final int kv;
- // Factory keys
- if (gp.getScpKeyVersion() == 255) {
- replace = false;
- kv = 1;
- } else {
- // Replace current key
- kv = gp.getScpKeyVersion();
- replace = true;
- }
+ // TODO: Move to GPCommands
+ // --unlock
+ if (args.has(OPT_UNLOCK)) {
+ // Write default keys
+ final boolean replace;
+ final int kv;
+ // Factory keys
+ if (gp.getScpKeyVersion() == 255) {
+ replace = false;
+ kv = 1;
+ } else {
+ // Replace current key
+ kv = gp.getScpKeyVersion();
+ replace = true;
+ }
- PlaintextKeys new_key = PlaintextKeys.defaultKey();
- new_key.setVersion(kv);
- gp.putKeys(new_key, replace);
- System.out.println("Default " + HexUtils.bin2hex(PlaintextKeys.defaultKeyBytes) + " set as master key for " + gp.getAID());
- }
+ PlaintextKeys new_key = PlaintextKeys.defaultKey();
+ new_key.setVersion(kv);
+ gp.putKeys(new_key, replace);
+ System.out.println("Default " + HexUtils.bin2hex(PlaintextKeys.defaultKeyBytes) + " set as master key for " + gp.getAID());
+ }
- // --lock
- if (args.has(OPT_LOCK) || (args.has(OPT_LOCK_ENC) && args.has(OPT_LOCK_MAC) && args.has(OPT_LOCK_DEK))) {
- // By default we try to change an existing key
- boolean replace = true;
- List current = gp.getKeyInfoTemplate();
-
- // By default use key version 1
- int new_version = 1;
- // If there are keys present, check the existing version
- if (current.size() > 0) {
- if (current.get(0).getVersion() == 255) {
- // Factory keys, add keyset with version one.
- replace = false;
- } else {
- // Existing keys, change the present version
- new_version = current.get(0).getVersion();
- }
- }
+ // --lock
+ if (args.has(OPT_LOCK) || (args.has(OPT_LOCK_ENC) && args.has(OPT_LOCK_MAC) && args.has(OPT_LOCK_DEK))) {
+ // By default we try to change an existing key
+ boolean replace = true;
+ List current = gp.getKeyInfoTemplate();
+
+ // By default use key version 1
+ int new_version = 1;
+ // If there are keys present, check the existing version
+ if (current.size() > 0) {
+ if (current.get(0).getVersion() == 255) {
+ // Factory keys, add keyset with version one.
+ replace = false;
+ } else {
+ // Existing keys, change the present version
+ new_version = current.get(0).getVersion();
+ }
+ }
- // Get key value or values
- PlaintextKeys newKeys;
- if (args.has(OPT_LOCK_ENC) && args.has(OPT_LOCK_MAC) && args.has(OPT_LOCK_DEK)) {
- byte[] enc = HexUtils.stringToBin((String) args.valueOf(OPT_LOCK_ENC));
- byte[] mac = HexUtils.stringToBin((String) args.valueOf(OPT_LOCK_MAC));
- byte[] dek = HexUtils.stringToBin((String) args.valueOf(OPT_LOCK_DEK));
- newKeys = PlaintextKeys.fromKeys(enc, mac, dek);
- } else {
- newKeys = PlaintextKeys.fromMasterKey(HexUtils.stringToBin((String) args.valueOf(OPT_LOCK)));
- if (args.has(OPT_LOCK_KDF)) {
- // XXX: should do diversification here explicitly. Expose card keys and kdd
- newKeys.setDiversifier(getDiversificationOrFail(args, OPT_LOCK_KDF));
- }
- }
+ // Get key value or values
+ PlaintextKeys newKeys;
+ if (args.has(OPT_LOCK_ENC) && args.has(OPT_LOCK_MAC) && args.has(OPT_LOCK_DEK)) {
+ byte[] enc = HexUtils.stringToBin((String) args.valueOf(OPT_LOCK_ENC));
+ byte[] mac = HexUtils.stringToBin((String) args.valueOf(OPT_LOCK_MAC));
+ byte[] dek = HexUtils.stringToBin((String) args.valueOf(OPT_LOCK_DEK));
+ newKeys = PlaintextKeys.fromKeys(enc, mac, dek);
+ } else {
+ newKeys = PlaintextKeys.fromMasterKey(HexUtils.stringToBin((String) args.valueOf(OPT_LOCK)));
+ if (args.has(OPT_LOCK_KDF)) {
+ // XXX: should do diversification here explicitly. Expose card keys and kdd
+ newKeys.setDiversifier(getDiversificationOrFail(args, OPT_LOCK_KDF));
+ }
+ }
- // If a specific new key version is specified, use that instead.
- if (args.has(OPT_NEW_KEY_VERSION)) {
- new_version = GPUtils.intValue((String) args.valueOf(OPT_NEW_KEY_VERSION));
- replace = false;
- System.out.println("New version: " + new_version);
- }
- newKeys.setVersion(new_version);
+ // If a specific new key version is specified, use that instead.
+ if (args.has(OPT_NEW_KEY_VERSION)) {
+ new_version = GPUtils.intValue((String) args.valueOf(OPT_NEW_KEY_VERSION));
+ replace = false;
+ System.out.println("New version: " + new_version);
+ }
+ newKeys.setVersion(new_version);
- gp.putKeys(newKeys, replace);
+ gp.putKeys(newKeys, replace);
- if (args.has(OPT_LOCK)) {
- System.out.println("Card locked with: " + HexUtils.bin2hex(HexUtils.stringToBin((String) args.valueOf(OPT_LOCK))));
- System.out.println("Write this down, DO NOT FORGET/LOSE IT!");
- } else {
- System.out.println("Card locked with new keys.");
- System.out.println("Write them down, DO NOT FORGET/LOSE THEM!");
- }
- }
+ if (args.has(OPT_LOCK)) {
+ System.out.println("Card locked with: " + HexUtils.bin2hex(HexUtils.stringToBin((String) args.valueOf(OPT_LOCK))));
+ System.out.println("Write this down, DO NOT FORGET/LOSE IT!");
+ } else {
+ System.out.println("Card locked with new keys.");
+ System.out.println("Write them down, DO NOT FORGET/LOSE THEM!");
+ }
+ }
- // --make-default
- if (args.has(OPT_MAKE_DEFAULT)) {
- gp.makeDefaultSelected(AID.fromString(args.valueOf(OPT_MAKE_DEFAULT)));
- }
+ // --make-default
+ if (args.has(OPT_MAKE_DEFAULT)) {
+ gp.makeDefaultSelected(AID.fromString(args.valueOf(OPT_MAKE_DEFAULT)));
+ }
- // --rename-isd
- if (args.has(OPT_RENAME_ISD)) {
- gp.renameISD(AID.fromString(args.valueOf(OPT_RENAME_ISD)));
- }
- // --set-pre-perso
- if (args.has(OPT_SET_PRE_PERSO)) {
- byte[] payload = HexUtils.stringToBin((String) args.valueOf(OPT_SET_PRE_PERSO));
- if (args.has(OPT_TODAY)) {
- System.arraycopy(GPData.CPLC.today(), 0, payload, 2, 2);
- }
- GPCommands.setPrePerso(gp, payload);
- }
- // --set-perso
- if (args.has(OPT_SET_PERSO)) {
- byte[] payload = HexUtils.stringToBin((String) args.valueOf(OPT_SET_PERSO));
- if (args.has(OPT_TODAY)) {
- System.arraycopy(GPData.CPLC.today(), 0, payload, 2, 2);
- }
- GPCommands.setPerso(gp, payload);
- }
- }
- } catch (GPException e) {
- // All unhandled GP exceptions halt the program unless it is run with -force
- if (!args.has(OPT_FORCE)) {
- fail(e.getMessage());
+ // --rename-isd
+ if (args.has(OPT_RENAME_ISD)) {
+ gp.renameISD(AID.fromString(args.valueOf(OPT_RENAME_ISD)));
+ }
+ // --set-pre-perso
+ if (args.has(OPT_SET_PRE_PERSO)) {
+ byte[] payload = HexUtils.stringToBin((String) args.valueOf(OPT_SET_PRE_PERSO));
+ if (args.has(OPT_TODAY)) {
+ System.arraycopy(GPData.CPLC.today(), 0, payload, 2, 2);
}
- } catch (IOException e) {
- System.out.println("Failed to communicate with card in " + reader + ": " + e.getMessage());
- // Card exceptions skip to the next reader, if available and allowed FIXME broken logic
- continue;
- } finally {
- if (card != null) {
- card.endExclusive();
- card.disconnect(true);
+ GPCommands.setPrePerso(gp, payload);
+ }
+ // --set-perso
+ if (args.has(OPT_SET_PERSO)) {
+ byte[] payload = HexUtils.stringToBin((String) args.valueOf(OPT_SET_PERSO));
+ if (args.has(OPT_TODAY)) {
+ System.arraycopy(GPData.CPLC.today(), 0, payload, 2, 2);
}
+ GPCommands.setPerso(gp, payload);
}
}
- } catch (CardException e) {
- // Sensible wrapper for the different PC/SC exceptions
- if (TerminalManager.getExceptionMessage(e) != null) {
- System.out.println("PC/SC failure: " + TerminalManager.getExceptionMessage(e));
- } else {
- e.printStackTrace(); // TODO: remove
- fail("CardException, terminating");
- }
+ // Other exceptions escape. fin.
+ return 0;
+ } catch (NoSuchAlgorithmException | IOException e) {
+ // FIXME: deal with it
+ e.printStackTrace();
}
- // Other exceptions escape. fin.
- System.exit(0);
+ return 1;
}
private static void calculateDapPropertiesAndLoadCap(OptionSet args, GPSession gp, CAPFile capFile) throws GPException, IOException {
@@ -954,7 +859,7 @@ public static void fail(String msg) {
System.exit(1);
}
- private static void verbose(String s) {
+ private void verbose(String s) {
if (isVerbose) {
System.out.println("# " + s);
}