Skip to content

Commit

Permalink
Refactor SCP version handling, separating enum from i=
Browse files Browse the repository at this point in the history
  • Loading branch information
martinpaljak committed Dec 31, 2021
1 parent a07c58e commit f5eed37
Show file tree
Hide file tree
Showing 11 changed files with 112 additions and 92 deletions.
2 changes: 1 addition & 1 deletion .mvn/wrapper/maven-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.1/apache-maven-3.8.1-bin.zip
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.3/apache-maven-3.8.3-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
4 changes: 2 additions & 2 deletions library/src/main/java/pro/javacard/gp/GPCardKeys.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public abstract class GPCardKeys {
private static final Logger logger = LoggerFactory.getLogger(GPCardKeys.class);

// Key diversification support.
protected GPSecureChannel scp; // The actual SCP version, to know how to handle DEK
protected GPSecureChannelVersion.SCP scp; // The actual SCP version, to know how to handle DEK
protected byte[] kdd; // The key derivation data that was used to get the keys in question. May be empty (no derivation)
private boolean diversified = false;

Expand Down Expand Up @@ -78,7 +78,7 @@ public static List<KeyPurpose> cardKeys() {
public abstract byte[] kcv(KeyPurpose p);

// Diversify card keys automatically, based on INITIALIZE UPDATE response
public GPCardKeys diversify(GPSecureChannel scp, byte[] kdd) {
public GPCardKeys diversify(GPSecureChannelVersion.SCP scp, byte[] kdd) {
if (diversified)
throw new IllegalStateException("Keys already diversified!");
this.scp = scp; // We know for sure what is the type of the key.
Expand Down
2 changes: 1 addition & 1 deletion library/src/main/java/pro/javacard/gp/GPKeyInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public GPKeyInfo(int version, int id, List<GPKeyInfoElement> elements, int acces
throw new GPDataException("Multiple unsupported elements in key info template");
}
}
this.elements = elements;
this.elements = new ArrayList<>(elements);

// FIXME: handle them as optionals here
this.access = access;
Expand Down
6 changes: 3 additions & 3 deletions library/src/main/java/pro/javacard/gp/GPRegistryEntry.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public class GPRegistryEntry {
HashSet<Integer> implicitContactless = new HashSet<>();

public Set<Privilege> getPrivileges() {
return privileges;
return Collections.unmodifiableSet(privileges);
}

void setPrivileges(Set<Privilege> privs) {
Expand Down Expand Up @@ -215,11 +215,11 @@ public String getLifeCycleString() {
}

public Set<Integer> getImplicitlySelectedContact() {
return implicitContact;
return Collections.unmodifiableSet(implicitContact);
}

public Set<Integer> getImplicitlySelectedContactless() {
return implicitContactless;
return Collections.unmodifiableSet(implicitContactless);
}

public enum Kind {
Expand Down
57 changes: 0 additions & 57 deletions library/src/main/java/pro/javacard/gp/GPSecureChannel.java

This file was deleted.

66 changes: 66 additions & 0 deletions library/src/main/java/pro/javacard/gp/GPSecureChannelVersion.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* GlobalPlatformPro - GlobalPlatform tool
*
* Copyright (C) 2020-present 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 java.util.Optional;

public final class GPSecureChannelVersion {
public final int i;
public final SCP scp;

public enum SCP {
SCP01(1), SCP02(2), SCP03(3);

private final int value;

SCP(int value) {
this.value = value;
}

public byte getValue() {
return (byte) (value & 0xFF);
}

public static Optional<SCP> valueOf(int i) {
for (SCP v : values())
if (v.value == i)
return Optional.of(v);
return Optional.empty();
}
}

public GPSecureChannelVersion(SCP scp, int i) {
this.scp = scp;
this.i = i;
}

public static GPSecureChannelVersion valueOf(int v) {
return valueOf(v, 0);
}

public static GPSecureChannelVersion valueOf(int v, int i) {
SCP scp = SCP.valueOf(v).orElseThrow(() -> new IllegalArgumentException("Unknown SCP version: " + v));
return new GPSecureChannelVersion(scp, i);
}

public String toString() {
return i == 0 ? scp.name() : String.format("%s (i=%02x)", scp.name(), i);
}
}
31 changes: 17 additions & 14 deletions library/src/main/java/pro/javacard/gp/GPSession.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
import java.util.stream.Collectors;

import static pro.javacard.gp.GPCardKeys.KeyPurpose;
import static pro.javacard.gp.GPSecureChannelVersion.SCP.*;

/**
* Represents a connection to a GlobalPlatform Card (BIBO interface)
Expand Down Expand Up @@ -94,7 +95,7 @@ public class GPSession {

// (I)SD AID
private AID sdAID;
private GPSecureChannel scpVersion;
private GPSecureChannelVersion scpVersion;
private int scpKeyVersion = 0; // will be set to the key version reported by card
GPCardProfile profile;
private int blockSize = 255;
Expand Down Expand Up @@ -227,7 +228,7 @@ public AID getAID() {
return new AID(sdAID.getBytes());
}

public GPSecureChannel getSecureChannel() {
public GPSecureChannelVersion getSecureChannel() {
return this.scpVersion;
}

Expand Down Expand Up @@ -361,7 +362,7 @@ private void normalizeSecurityLevel(EnumSet<APDUMode> securityLevel) {
/*
* Establishes a secure channel to the security domain or application
*/
public void openSecureChannel(GPCardKeys keys, GPSecureChannel scp, byte[] host_challenge, EnumSet<APDUMode> securityLevel)
public void openSecureChannel(GPCardKeys keys, GPSecureChannelVersion scp, byte[] host_challenge, EnumSet<APDUMode> securityLevel)
throws IOException, GPException {

normalizeSecurityLevel(securityLevel);
Expand All @@ -376,7 +377,7 @@ public void openSecureChannel(GPCardKeys keys, GPSecureChannel scp, byte[] host_

// P1 key version (all)
// P2 either key ID (SCP01) or 0 (SCP02)
CommandAPDU initUpdate = new CommandAPDU(CLA_GP, INS_INITIALIZE_UPDATE, keys.getKeyInfo().getVersion(), scp == GPSecureChannel.SCP01 ? keys.getKeyInfo().getID() : 0, host_challenge, 256);
CommandAPDU initUpdate = new CommandAPDU(CLA_GP, INS_INITIALIZE_UPDATE, keys.getKeyInfo().getVersion(), scp.scp == GPSecureChannelVersion.SCP.SCP01 ? keys.getKeyInfo().getID() : 0, host_challenge, 256);

ResponseAPDU response = channel.transmit(initUpdate);
int sw = response.getSW();
Expand All @@ -402,13 +403,15 @@ public void openSecureChannel(GPCardKeys keys, GPSecureChannel scp, byte[] host_
scpKeyVersion = update_response[offset] & 0xFF;
offset++;
// Get major SCP version from Key Information field in response
this.scpVersion = GPSecureChannel.valueOf(update_response[offset] & 0xFF).orElseThrow(() -> new GPDataException("Unknown or invalid SCP version", update_response));
int scpv = update_response[offset] & 0xFF;
offset++;

// get the protocol "i" parameter, if SCP03
if (this.scpVersion == GPSecureChannel.SCP03) {
scpVersion.setI(update_response[offset]);
if (scpv == 0x03) {
this.scpVersion = GPSecureChannelVersion.valueOf(scpv, update_response[offset]);
offset++;
} else {
this.scpVersion = GPSecureChannelVersion.valueOf(scpv);
}

// get card challenge
Expand All @@ -421,9 +424,9 @@ public void openSecureChannel(GPCardKeys keys, GPSecureChannel scp, byte[] host_

// Extract ssc
final byte[] seq;
if (this.scpVersion == GPSecureChannel.SCP02) {
if (this.scpVersion.scp == SCP02) {
seq = Arrays.copyOfRange(update_response, 12, 14);
} else if (this.scpVersion == GPSecureChannel.SCP03 && update_response.length == 32) {
} else if (this.scpVersion.scp == SCP03 && update_response.length == 32) {
seq = Arrays.copyOfRange(update_response, offset, 32);
offset += seq.length;
} else {
Expand All @@ -449,17 +452,17 @@ public void openSecureChannel(GPCardKeys keys, GPSecureChannel scp, byte[] host_
}

// This will throw as expected later, to indicate the issue
if (this.scpVersion == GPSecureChannel.SCP01 && securityLevel.contains(APDUMode.RMAC)) {
if (this.scpVersion.scp == GPSecureChannelVersion.SCP.SCP01 && securityLevel.contains(APDUMode.RMAC)) {
logger.warn("SCP01 does not support RMAC, removing.");
}

// Give the card key a chance to be automatically diversifed based on KDD from INITIALIZE UPDATE
cardKeys = keys.diversify(this.scpVersion, diversification_data);
cardKeys = keys.diversify(this.scpVersion.scp, diversification_data);

logger.info("Diversified card keys: {}", cardKeys);

// Derive session keys
if (this.scpVersion == GPSecureChannel.SCP02) {
if (this.scpVersion.scp == GPSecureChannelVersion.SCP.SCP02) {
sessionContext = seq.clone();
} else {
sessionContext = GPUtils.concatenate(host_challenge, card_challenge);
Expand All @@ -473,7 +476,7 @@ public void openSecureChannel(GPCardKeys keys, GPSecureChannel scp, byte[] host_
// Verify card cryptogram
byte[] my_card_cryptogram;
byte[] cntx = GPUtils.concatenate(host_challenge, card_challenge);
if (this.scpVersion == GPSecureChannel.SCP01 || this.scpVersion == GPSecureChannel.SCP02) {
if (this.scpVersion.scp == SCP01 || this.scpVersion.scp == SCP02) {
my_card_cryptogram = GPCrypto.mac_3des_nulliv(encKey, cntx);
} else {
my_card_cryptogram = GPCrypto.scp03_kdf(macKey, (byte) 0x00, cntx, 64);
Expand All @@ -491,7 +494,7 @@ public void openSecureChannel(GPCardKeys keys, GPSecureChannel scp, byte[] host_

// Calculate host cryptogram and initialize SCP wrapper
final byte[] host_cryptogram;
switch (scpVersion) {
switch (scpVersion.scp) {
case SCP01:
host_cryptogram = GPCrypto.mac_3des_nulliv(encKey, GPUtils.concatenate(card_challenge, host_challenge));
wrapper = new SCP01Wrapper(encKey, macKey, blockSize);
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>com.github.martinpaljak</groupId>
<artifactId>metacard</artifactId>
<version>21.04.08</version>
<version>21.10.19</version>
</parent>
<version>20.08.13-SNAPSHOT</version>
<artifactId>gppro</artifactId>
Expand Down
4 changes: 4 additions & 0 deletions spotbugs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,8 @@
<Class name="pro.javacard.gp.HexBytes"/>
<Bug pattern="EI_EXPOSE_REP"/>
</Match>
<Match>
<Class name="pro.javacard.gp.GPSession"/>
<Bug pattern="EI_EXPOSE_REP"/>
</Match>
</FindBugsFilter>
14 changes: 8 additions & 6 deletions tool/src/main/java/pro/javacard/gp/GPTool.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@
import java.util.*;
import java.util.stream.Collectors;

import static pro.javacard.gp.GPSecureChannelVersion.SCP.*;

// Does the CLI parameter parsing and associated execution
@AutoService(SmartCardApp.class)
public final class GPTool extends GPCommandLineInterface implements SimpleSmartCardApp {
Expand Down Expand Up @@ -615,7 +617,7 @@ public int run(BIBO bibo, String[] argv) {
// By default same SCP as current
if (!args.has(OPT_SAD) && !gp.profile.oldStyleSSDParameters()) {
if (parameters != null && parameters.find(new BerTag(0x81)) == null) {
params = GPUtils.concatenate(params, new byte[]{(byte) 0x81, 0x02, gp.getSecureChannel().getValue(), (byte) gp.getSecureChannel().getI()});
params = GPUtils.concatenate(params, new byte[]{(byte) 0x81, 0x02, gp.getSecureChannel().scp.getValue(), (byte) gp.getSecureChannel().i});
} else {
System.err.println("Notice: 0x81 already in parameters or no parameters");
}
Expand Down Expand Up @@ -749,7 +751,7 @@ public int run(BIBO bibo, String[] argv) {
}
PlaintextKeys new_key = PlaintextKeys.defaultKey();
new_key.setVersion(kv);
new_key.diversify(gp.getSecureChannel(), new byte[0]); // Just set the SCP type
new_key.diversify(gp.getSecureChannel().scp, new byte[0]); // Just set the SCP type
gp.putKeys(new_key, replace);
System.out.println("Default " + HexUtils.bin2hex(PlaintextKeys.defaultKeyBytes) + " set as key for " + gp.getAID());
}
Expand Down Expand Up @@ -799,13 +801,13 @@ public int run(BIBO bibo, String[] argv) {

verbose("Looking at key version for diversification method");
if (keyver >= 0x10 && keyver <= 0x1F)
newKeys.diversify(GPSecureChannel.SCP01, kdd);
newKeys.diversify(SCP01, kdd);
else if (keyver >= 0x20 && keyver <= 0x2F)
newKeys.diversify(GPSecureChannel.SCP02, kdd);
newKeys.diversify(SCP02, kdd);
else if (keyver >= 0x30 && keyver <= 0x3F)
newKeys.diversify(GPSecureChannel.SCP03, kdd);
newKeys.diversify(SCP03, kdd);
else
newKeys.diversify(gp.getSecureChannel(), kdd);
newKeys.diversify(gp.getSecureChannel().scp, kdd);

gp.putKeys(newKeys, replace);

Expand Down
Loading

0 comments on commit f5eed37

Please sign in to comment.