Skip to content

Commit 11fa656

Browse files
committed
tool: Rework command classes
This allows waiting for cards and splits things like SCP more nicely. Disadvantage is that we now have an inheritance chain going on that is hard to compose with.
1 parent 779d092 commit 11fa656

28 files changed

+440
-387
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package org.openjavacard.tool.command.base;
2+
3+
import com.beust.jcommander.Parameter;
4+
5+
import javax.smartcardio.Card;
6+
import javax.smartcardio.CardChannel;
7+
8+
public abstract class BasicCardCommand extends BasicTerminalCommand {
9+
10+
@Parameter(
11+
names = "--wait-for-card", order = 100,
12+
description = "Wait for card presence before start"
13+
)
14+
protected boolean aWaitForCard;
15+
16+
protected Card mCard;
17+
protected CardChannel mBasicChannel;
18+
19+
@Override
20+
protected void prepare() throws Exception {
21+
// super will prepare the terminal
22+
super.prepare();
23+
// wait for card presence if requested
24+
if(aWaitForCard) {
25+
while(!mTerminal.isCardPresent()) {
26+
LOG.debug("Waiting for card...");
27+
mTerminal.waitForCardPresent(5000);
28+
}
29+
}
30+
// final check for card presence
31+
if(!mTerminal.isCardPresent()) {
32+
throw new Error("No card present in terminal");
33+
}
34+
// connect to the card
35+
LOG.debug("Connecting to card");
36+
mCard = mTerminal.connect("*");
37+
mBasicChannel = mCard.getBasicChannel();
38+
}
39+
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package org.openjavacard.tool.command.base;
2+
3+
import com.beust.jcommander.Parameter;
4+
import org.slf4j.Logger;
5+
import org.slf4j.LoggerFactory;
6+
7+
public abstract class BasicCommand implements Runnable {
8+
9+
@Parameter(
10+
names = {"--help", "-h"}, order = 50,
11+
description = "Show help"
12+
)
13+
protected boolean aHelp;
14+
15+
protected final Logger LOG;
16+
17+
public BasicCommand() {
18+
LOG = LoggerFactory.getLogger(getClass());
19+
}
20+
21+
@Override
22+
public void run() {
23+
LOG.trace("run()");
24+
try {
25+
prepare();
26+
beforeExecute();
27+
execute();
28+
afterExecute();
29+
} catch (HelpException e) {
30+
throw new Error("Help not implemented");
31+
} catch (Exception e) {
32+
e.printStackTrace();
33+
}
34+
}
35+
36+
protected void prepare() throws Exception {
37+
LOG.trace("prepare()");
38+
// abort and show help if requested
39+
if(aHelp) {
40+
throw new HelpException(this);
41+
}
42+
}
43+
44+
protected void beforeExecute() throws Exception {
45+
LOG.trace("beforeExecute()");
46+
}
47+
48+
protected void execute() throws Exception {
49+
LOG.trace("execute()");
50+
}
51+
52+
protected void afterExecute() throws Exception {
53+
LOG.trace("afterExecute()");
54+
}
55+
56+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package org.openjavacard.tool.command.base;
2+
3+
import com.beust.jcommander.Parameter;
4+
import org.openjavacard.gp.client.GPCard;
5+
import org.openjavacard.gp.client.GPContext;
6+
import org.openjavacard.gp.keys.GPKeySet;
7+
import org.openjavacard.gp.scp.SCPProtocolPolicy;
8+
import org.openjavacard.iso.AID;
9+
import org.openjavacard.util.HexUtil;
10+
11+
import javax.smartcardio.CardException;
12+
import java.io.PrintStream;
13+
14+
public abstract class BasicGPCommand extends BasicSCPCommand {
15+
16+
@Parameter(
17+
names = "--isd", order = 200,
18+
description = "AID of the issuer security domain"
19+
)
20+
protected AID isd;
21+
22+
@Parameter(
23+
names = "--force-protected", order = 800,
24+
description = "Force operation on protected object"
25+
)
26+
protected boolean forceProtected = false;
27+
28+
@Parameter(
29+
names = "--log-keys", order = 900,
30+
description = "Allow writing keys into the debug log"
31+
)
32+
protected boolean logKeys = false;
33+
34+
GPContext mContext;
35+
GPCard mCard;
36+
37+
protected abstract void performOperation(GPContext context, GPCard card) throws CardException;
38+
39+
@Override
40+
protected void beforeExecute() throws Exception {
41+
super.beforeExecute();
42+
43+
PrintStream os = System.out;
44+
45+
mContext = new GPContext();
46+
mCard = findSingleGPCard(isd, getKeySet());
47+
48+
AID isdConf = mCard.getISD();
49+
os.println("Host GP configuration:");
50+
os.println(" ISD " + ((isdConf==null)?"auto":isdConf));
51+
int protocol = HexUtil.unsigned8(scpProtocol);
52+
int parameters = HexUtil.unsigned8(scpParameters);
53+
SCPProtocolPolicy protocolPolicy = new SCPProtocolPolicy(protocol, parameters);
54+
os.println(" Key diversification " + scpDiversification);
55+
mCard.setDiversification(scpDiversification);
56+
os.println(" Protocol policy " + protocolPolicy);
57+
mCard.setProtocolPolicy(protocolPolicy);
58+
os.println(" Security policy " + scpSecurity);
59+
mCard.setSecurityPolicy(scpSecurity);
60+
61+
mCard.connect();
62+
}
63+
64+
@Override
65+
protected void execute() throws Exception {
66+
super.execute();
67+
performOperation(mContext, mCard);
68+
}
69+
70+
@Override
71+
protected void afterExecute() throws Exception {
72+
super.afterExecute();
73+
PrintStream os = System.out;
74+
75+
os.println("DISCONNECTING");
76+
mCard.disconnect();
77+
os.println();
78+
}
79+
80+
public GPCard findSingleGPCard(AID sd, GPKeySet keys) {
81+
GPCard card;
82+
try {
83+
// check card presence
84+
if (!mTerminal.isCardPresent()) {
85+
throw new Error("No card present in terminal");
86+
}
87+
// create GP client
88+
card = new GPCard(mContext, mTerminal);
89+
// tell the client if we know the SD AID
90+
if(sd != null) {
91+
card.setISD(sd);
92+
}
93+
if(keys != null) {
94+
card.setKeys(keys);
95+
}
96+
// detect GP applet
97+
//boolean detected = card.detect();
98+
//if (!detected) {
99+
// throw new Error("Could not find a GlobalPlatform applet on the card");
100+
//}
101+
} catch (CardException e) {
102+
throw new Error("Error detecting card", e);
103+
}
104+
return card;
105+
}
106+
107+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package org.openjavacard.tool.command.base;
2+
3+
import com.beust.jcommander.Parameter;
4+
import com.beust.jcommander.validators.PositiveInteger;
5+
import org.openjavacard.gp.keys.GPKey;
6+
import org.openjavacard.gp.keys.GPKeyCipher;
7+
import org.openjavacard.gp.keys.GPKeyDiversification;
8+
import org.openjavacard.gp.keys.GPKeyId;
9+
import org.openjavacard.gp.keys.GPKeySet;
10+
import org.openjavacard.gp.keys.GPKeyUsage;
11+
import org.openjavacard.gp.keys.GPKeyVersion;
12+
import org.openjavacard.gp.scp.SCPSecurityPolicy;
13+
import org.openjavacard.util.HexUtil;
14+
15+
public class BasicSCPCommand extends BasicCardCommand {
16+
17+
@Parameter(
18+
names = "--scp-diversification", order = 300,
19+
description = "Use specified key diversification"
20+
)
21+
protected GPKeyDiversification scpDiversification = GPKeyDiversification.NONE;
22+
23+
@Parameter(
24+
names = "--scp-protocol", order = 300,
25+
description = "Require specified SCP protocol"
26+
)
27+
protected String scpProtocol = "00";
28+
29+
@Parameter(
30+
names = "--scp-parameters", order = 300,
31+
description = "Require specified SCP parameters"
32+
)
33+
protected String scpParameters = "00";
34+
35+
@Parameter(
36+
names = "--scp-security", order = 300,
37+
description = "Require specified SCP security level"
38+
)
39+
protected SCPSecurityPolicy scpSecurity = SCPSecurityPolicy.CMAC;
40+
41+
@Parameter(
42+
names = "--key-version",
43+
description = "User-specified keys: key version (0 means any)",
44+
validateWith = PositiveInteger.class
45+
)
46+
private int scpKeyVersion = 0;
47+
48+
@Parameter(
49+
names = "--key-id",
50+
description = "User-specified keys: first key ID (0 means any)",
51+
validateWith = PositiveInteger.class
52+
)
53+
private int scpKeyId = 0;
54+
55+
@Parameter(
56+
names = "--key-cipher",
57+
description = "User-specified keys: key cipher"
58+
)
59+
private GPKeyCipher scpKeyCipher = GPKeyCipher.GENERIC;
60+
61+
@Parameter(
62+
names = "--key-types",
63+
description = "User-specified keys: key types (colon-separated)"
64+
)
65+
private String scpKeyTypes = "MASTER";
66+
67+
@Parameter(
68+
names = "--key-secrets",
69+
description = "User-specified keys: secrets (colon-separated)"
70+
)
71+
private String scpKeySecrets = null;
72+
73+
74+
public GPKeySet getKeySet() {
75+
GPKeySet keys = GPKeySet.GLOBALPLATFORM;
76+
77+
if(scpKeySecrets != null) {
78+
keys = buildKeysFromParameters(scpKeyId, scpKeyVersion, scpKeyCipher, scpKeyTypes, scpKeySecrets);
79+
}
80+
81+
return keys;
82+
}
83+
84+
public static GPKeySet buildKeysFromParameters(int keyId, int keyVersion, GPKeyCipher cipher, String types, String secrets) {
85+
// XXX this is not comprehensive because of the loop and protocol variations
86+
GPKeyId.checkKeyId(keyId);
87+
GPKeyVersion.checkKeyVersion(keyVersion);
88+
// build a key set
89+
GPKeySet keys = new GPKeySet("commandline", keyVersion);
90+
// split arguments
91+
String[] typeStrings = types.split(":");
92+
String[] secretStrings = secrets.split(":");
93+
// check lengths of provided arrays
94+
if(typeStrings.length != secretStrings.length) {
95+
throw new Error("Must provide an equal number of key types and secrets");
96+
}
97+
// assume number of keys from number of types
98+
int numKeys = typeStrings.length;
99+
for(int i = 0; i < numKeys; i++) {
100+
GPKeyUsage usage = GPKeyUsage.valueOf(typeStrings[i]);
101+
byte[] secret = HexUtil.hexToBytes(secretStrings[i]);
102+
byte id = (byte)(keyId + i);
103+
if(usage == GPKeyUsage.MASTER) {
104+
id = 0;
105+
}
106+
GPKey key = new GPKey(id, usage, cipher, secret);
107+
keys.putKey(key);
108+
}
109+
return keys;
110+
}
111+
112+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package org.openjavacard.tool.command.base;
2+
3+
import com.beust.jcommander.Parameter;
4+
5+
import javax.smartcardio.CardException;
6+
import javax.smartcardio.CardTerminal;
7+
import javax.smartcardio.CardTerminals;
8+
import javax.smartcardio.TerminalFactory;
9+
import java.util.ArrayList;
10+
import java.util.List;
11+
12+
public abstract class BasicTerminalCommand extends BasicCommand {
13+
14+
@Parameter(
15+
names = "--terminal", order = 100,
16+
description = "Terminal to use for the operation (unique prefix)"
17+
)
18+
protected String aTerminal = null;
19+
20+
protected CardTerminal mTerminal = null;
21+
22+
@Override
23+
protected void prepare() throws Exception {
24+
super.prepare();
25+
mTerminal = findTerminal(aTerminal);
26+
}
27+
28+
private CardTerminal findTerminal(String prefix) {
29+
LOG.trace("findTerminal()");
30+
List<CardTerminal> terminals = findTerminals(prefix);
31+
if (terminals.isEmpty()) {
32+
throw new Error("No terminals found");
33+
} else if (terminals.size() > 1) {
34+
if (prefix == null) {
35+
throw new Error("More than one terminal found");
36+
} else {
37+
throw new Error("More than one terminal found matching \"" + prefix + "\"");
38+
}
39+
}
40+
return terminals.get(0);
41+
}
42+
43+
private List<CardTerminal> findTerminals(String prefix) {
44+
LOG.trace("findTerminals()");
45+
ArrayList<CardTerminal> found = new ArrayList<>();
46+
TerminalFactory tf = TerminalFactory.getDefault();
47+
CardTerminals ts = tf.terminals();
48+
try {
49+
List<CardTerminal> terminals = ts.list();
50+
for (CardTerminal terminal : terminals) {
51+
String name = terminal.getName();
52+
LOG.trace("terminal \"" + name + "\"");
53+
if (prefix == null || name.startsWith(prefix)) {
54+
found.add(terminal);
55+
}
56+
}
57+
} catch (CardException e) {
58+
throw new Error("Error detecting terminals", e);
59+
}
60+
return found;
61+
}
62+
63+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package org.openjavacard.tool.command.base;
2+
3+
public class HelpException extends Exception {
4+
5+
private BasicCommand mCommand;
6+
7+
public HelpException(BasicCommand command) {
8+
}
9+
10+
}

0 commit comments

Comments
 (0)