Skip to content
This repository has been archived by the owner on Mar 15, 2024. It is now read-only.

Commit

Permalink
Add support for SDK 2.1.1
Browse files Browse the repository at this point in the history
- sanitize cap file dumping
- document -sha256 CLI option
- some API visibility adjustments
- get rid of the maven parent project brainfart, that does not work
- update version to today
  • Loading branch information
martinpaljak committed Mar 4, 2019
1 parent fab4a5b commit e021e0d
Show file tree
Hide file tree
Showing 7 changed files with 192 additions and 57 deletions.
128 changes: 116 additions & 12 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.github.martinpaljak</groupId>
<artifactId>javacard</artifactId>
<version>18.10.04</version>
</parent>
<groupId>com.github.martinpaljak</groupId>
<artifactId>capfile</artifactId>
<packaging>jar</packaging>
<name>CAP file library</name>
<version>18.10.18</version>
<!-- launch4j requires version without leading zero -->
<version>19.03.04</version>
<url>https://github.com/martinpaljak/capfile</url>
<description>JavaCard CAP parsing</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- launch4j requires a version without leading zeros and 4 components -->
<revision.windows>19.3.4.0</revision.windows>
</properties>

<licenses>
<license>
<name>MIT</name>
Expand All @@ -24,20 +28,119 @@
<developerConnection>scm:git:git@github.com:martinpaljak/capfile.git</developerConnection>
</scm>
<dependencies>
<!-- For PEM parsing in the signing tool -->
<!-- For PEM parsing in the CLI tool -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>${bc.version}</version>
<version>1.60</version>
<!-- Optional, because only used in CLI -->
<optional>true</optional>
</dependency>
</dependencies>

<!-- Maven central -->
<distributionManagement>
<repository>
<id>ossrh</id>
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
</repository>
</distributionManagement>

<profiles>
<profile>
<id>release</id>
<build>
<plugins>
<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
<version>1.6.8</version>
<extensions>true</extensions>
<configuration>
<serverId>ossrh-martinpaljak</serverId>
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
<autoReleaseAfterClose>true</autoReleaseAfterClose>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.0.1</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<build>
<plugins>
<!-- Compiler -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<compilerArgs>
<arg>-Xlint</arg>
</compilerArgs>
</configuration>
</plugin>
<!-- Findbugs -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
<version>3.0.5</version>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>

<!-- CLI package -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.0.0</version>
<version>3.2.1</version>
<executions>
<execution>
<phase>package</phase>
Expand All @@ -47,7 +150,8 @@
<configuration>
<finalName>capfile</finalName>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>pro.javacard.CAPFileTool</mainClass>
</transformer>
</transformers>
Expand Down Expand Up @@ -91,7 +195,7 @@
<fileVersion>${revision.windows}</fileVersion>
<txtFileVersion>${project.version}</txtFileVersion>
<fileDescription>Parse JavaCard CAP files</fileDescription>
<copyright>(C) 2018 Martin Paljak (MIT License)</copyright>
<copyright>(C) 2019 Martin Paljak (MIT License)</copyright>
<productVersion>${revision.windows}</productVersion>
<txtProductVersion>${project.version}</txtProductVersion>
<productName>capfile</productName>
Expand Down
43 changes: 30 additions & 13 deletions src/main/java/pro/javacard/CAPFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ protected CAPFile(InputStream in) throws IOException {
int offset = 4;
for (int j = 0; j < (imps[3] & 0xFF); j++) {
AID aid = new AID(imps, offset + 3, imps[offset + 2]);
CAPPackage p = new CAPPackage(aid, imps[offset + 1], imps[offset], WellKnownAID.getName(aid).orElse(""));
CAPPackage p = new CAPPackage(aid, imps[offset + 1], imps[offset]);
imports.add(p);
offset += imps[offset + 2] + 3;
}
Expand Down Expand Up @@ -212,7 +212,16 @@ public String getPackageName() {
return pkg.getName().orElseThrow(() -> new IllegalStateException("No package name"));
}

public byte[] getCode() {
return _getCode(false);
}

@Deprecated
public byte[] getCode(boolean includeDebug) {
return _getCode(includeDebug);
}

byte[] _getCode(boolean includeDebug) {
byte[] result = new byte[0];
for (String name : componentNames) {
byte[] c = getComponent(name);
Expand All @@ -225,9 +234,18 @@ public byte[] getCode(boolean includeDebug) {
return result;
}

public byte[] getLoadFileDataHash(String hash) {
try {
return MessageDigest.getInstance(hash).digest(getCode());
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Not possible", e);
}
}

@Deprecated
public byte[] getLoadFileDataHash(String hash, boolean includeDebug) {
try {
return MessageDigest.getInstance(hash).digest(getCode(includeDebug));
return MessageDigest.getInstance(hash).digest(_getCode(includeDebug));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Not possible", e);
}
Expand All @@ -239,22 +257,21 @@ public void dump(PrintStream out) {
String gpversion = gpv.isPresent() ? "/GlobalPlatform " + gpv.get() : "";

out.println("CAP file (v" + cap_version + "), contains: " + String.join(", ", getFlags()) + " for JavaCard " + jcv.orElse("2.1.1?") + gpversion);
out.println("Package: " + pkg);
for (CAPPackage imp : getImports()) {
out.println("Import: " + imp);
}
out.printf("Package: %s %s v%s%n", pkg.getName().get(), pkg.getAid().toString(), pkg.getVersionString());
for (Map.Entry<AID, String> applet : getApplets().entrySet()) {
out.println("Applet: " + (applet.getValue() == null ? "" : applet.getValue() + " ") + applet.getKey());
out.println("Applet: " + (applet.getValue() == null ? "" : applet.getValue() + " ") + applet.getKey());
}
for (CAPPackage imp : getImports()) {
out.println("Import: " + imp);
}

// Check manifest for metadata
if (manifest != null) {
Attributes mains = manifest.getMainAttributes();

// iterate all packages
Map<String, Attributes> ent = manifest.getEntries();
if (ent.keySet().size() > 1) {
throw new IllegalArgumentException("Too many elments in CAP manifest");
throw new IllegalArgumentException("Too many elements in CAP manifest");
}
Attributes caps = ent.get(ent.keySet().toArray()[0]);
// Generic
Expand All @@ -267,12 +284,12 @@ public void dump(PrintStream out) {
out.println("Generated by " + converter_provider + " converter " + converter_version);
out.println("On " + cap_creation_time + " with JDK " + jdk_name);
}
out.println("Code size: " + getCode(false).length + " bytes (" + getCode(true).length + " with debug)");
out.println("SHA-256 (code): " + HexUtils.bin2hex(getLoadFileDataHash("SHA-256", false)).toLowerCase());
out.println("SHA-1 (code): " + HexUtils.bin2hex(getLoadFileDataHash("SHA-1", false)).toLowerCase());
out.println("Code size " + getCode().length + " bytes (" + getCode(true).length + " with debug)");
out.println("SHA-256 " + HexUtils.bin2hex(getLoadFileDataHash("SHA-256")).toLowerCase());
out.println("SHA-1 " + HexUtils.bin2hex(getLoadFileDataHash("SHA-1")).toLowerCase());
}

private List<String> getFlags() {
public List<String> getFlags() {
ArrayList<String> result = new ArrayList<>();
// Table 6-3: CAP File Package Flags
if ((flags & 0x01) == 0x01) {
Expand Down
8 changes: 4 additions & 4 deletions src/main/java/pro/javacard/CAPFileSigner.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@ public static void addSignature(CAPFile cap, PrivateKey key) throws GeneralSecur
if ((rkey.getModulus().bitLength() + 7) / 8 == 128) {
Signature signer = Signature.getInstance("SHA1withRSA");
signer.initSign(key);
signer.update(cap.getLoadFileDataHash("SHA1", false));
signer.update(cap.getLoadFileDataHash("SHA1"));
byte[] dap = signer.sign();
cap.entries.put("META-INF/" + CAPFile.DAP_RSA_V1_SHA1_FILE, dap);
signer.initSign(key);
signer.update(cap.getLoadFileDataHash("SHA-256", false));
signer.update(cap.getLoadFileDataHash("SHA-256"));
dap = signer.sign();
cap.entries.put("META-INF/" + CAPFile.DAP_RSA_V1_SHA256_FILE, dap);
return;
Expand All @@ -71,11 +71,11 @@ public static void addSignature(CAPFile cap, PrivateKey key) throws GeneralSecur
if (ekey.getParams().equals(secp256r1)) {
Signature signer = Signature.getInstance("SHA256withECDSA");
signer.initSign(key);
signer.update(cap.getLoadFileDataHash("SHA-1", false));
signer.update(cap.getLoadFileDataHash("SHA-1"));
byte[] dap = signer.sign();
cap.entries.put("META-INF/" + CAPFile.DAP_P256_SHA1_FILE, dap);
signer.initSign(key);
signer.update(cap.getLoadFileDataHash("SHA-256", false));
signer.update(cap.getLoadFileDataHash("SHA-256"));
dap = signer.sign();
cap.entries.put("META-INF/" + CAPFile.DAP_P256_SHA256_FILE, dap);
return;
Expand Down
20 changes: 13 additions & 7 deletions src/main/java/pro/javacard/CAPFileTool.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,20 @@
import java.nio.file.StandardCopyOption;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Vector;
import java.util.stream.Collectors;

public class CAPFileTool {

private final static ArrayList<String> help = new ArrayList<>();
static {
help.add(" dump: capfile <capfile>");
help.add(" verify: capfile -v <sdkpath> [<targetsdkpath>] <capfile> [<expfiles...>]");
help.add(" sign: capfile -s <keyfile> <capfile>");
help.add(" lfdbh: capfile -sha256 <capfile>");
}
private static boolean has(Vector<String> args, String v) {
for (String s : args) {
if (s.equalsIgnoreCase(v)) {
Expand All @@ -31,16 +39,14 @@ public static void main(String[] argv) {

if (args.size() < 1 || has(args, "-h")) {
System.err.println("Usage:");
System.err.println(" dump: capfile <capfile>");
System.err.println(" verify: capfile -v <sdkpath> [<targetsdkpath>] <capfile> [<expfiles...>]");
System.err.println(" sign: capfile -s <keyfile> <capfile>");
help.stream().forEach(s -> System.err.println(s));
System.exit(1);
}

try {
if (has(args, "-s")) {
if (args.size() < 2)
fail("Usage:\n capfile -s <keyfile> <capfile>");
fail("Usage:\n" + help.get(2));
String keyfile = args.remove(0);
Path capfile = Paths.get(args.remove(0));
CAPFile cap = CAPFile.fromBytes(Files.readAllBytes(capfile));
Expand All @@ -61,7 +67,7 @@ public static void main(String[] argv) {

} else if (has(args, "-v")) {
if (args.size() < 2)
fail("Usage:\n capfile -v <sdkpath> [<targetsdkpath>] <capfile> [<expfiles...>]");
fail("Usage:\n" + help.get(1));
final String sdkpath = args.remove(0);
final String targetsdkpath;
final String capfile;
Expand All @@ -88,10 +94,10 @@ public static void main(String[] argv) {
}
} else if (has(args, "-sha256")) {
if (args.size() < 1)
fail("Usage:\n capfile -sha256 <capfile>");
fail("Usage:\n" + help.get(3));
String capfile = args.remove(0);
CAPFile cap = CAPFile.fromBytes(Files.readAllBytes(Paths.get(capfile)));
System.out.println(Hex.toHexString(cap.getLoadFileDataHash("SHA-256", false)));
System.out.println(Hex.toHexString(cap.getLoadFileDataHash("SHA-256")));
} else {
String capfile = args.remove(0);
CAPFile cap = CAPFile.fromBytes(Files.readAllBytes(Paths.get(capfile)));
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/pro/javacard/CAPPackage.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public int hashCode() {

@Override
public String toString() {
return String.format("%s %s v%d.%d", getName().orElse("(unknown)"), aid, major, minor);
return String.format("%-32s v%d.%d %s", aid, major, minor, getName().orElse(WellKnownAID.getName(aid).orElse("(unknown)")));
}

public String getVersionString() {
Expand Down
Loading

0 comments on commit e021e0d

Please sign in to comment.