diff --git a/tools/tcg_rim_tool/src/main/java/hirs/swid/Main.java b/tools/tcg_rim_tool/src/main/java/hirs/swid/Main.java index b791ea9e..6356f0cd 100644 --- a/tools/tcg_rim_tool/src/main/java/hirs/swid/Main.java +++ b/tools/tcg_rim_tool/src/main/java/hirs/swid/Main.java @@ -2,6 +2,9 @@ package hirs.swid; import hirs.swid.utils.Commander; import com.beust.jcommander.JCommander; +import hirs.swid.utils.TimestampArgumentValidator; + +import java.util.List; public class Main { @@ -79,16 +82,16 @@ public class Main { } else { gateway.setRimEventLog(rimEventLog); } - String filename = commander.getRfc3852Filename(); - if (!filename.isEmpty() && commander.getRfc3339() != null) { - System.out.println("Only one timestamp format can be specified"); - System.exit(1); - } else if (!filename.isEmpty()) { - gateway.setTimestampFormat("RFC3852"); - gateway.setTimestampArgument(filename); - } else if (commander.getRfc3339() != null) { - gateway.setTimestampFormat("RFC3339"); - gateway.setTimestampArgument(commander.getRfc3339()); + List timestampArguments = commander.getTimestampArguments(); + if (timestampArguments.size() > 0) { + if (new TimestampArgumentValidator(timestampArguments).isValid()) { + gateway.setTimestampFormat(timestampArguments.get(0)); + if (timestampArguments.size() > 1) { + gateway.setTimestampArgument(timestampArguments.get(1)); + } + } else { + System.exit(1); + } } gateway.generateSwidTag(commander.getOutFile()); break; diff --git a/tools/tcg_rim_tool/src/main/java/hirs/swid/SwidTagGateway.java b/tools/tcg_rim_tool/src/main/java/hirs/swid/SwidTagGateway.java index 6493ea3c..9c9af605 100644 --- a/tools/tcg_rim_tool/src/main/java/hirs/swid/SwidTagGateway.java +++ b/tools/tcg_rim_tool/src/main/java/hirs/swid/SwidTagGateway.java @@ -652,7 +652,7 @@ public class SwidTagGateway { */ private XMLObject createXmlTimestamp(Document doc, XMLSignatureFactory sigFactory) { Element timeStampElement = doc.createElement("TimeStamp"); - switch (timestampFormat) { + switch (timestampFormat.toUpperCase()) { case "RFC3852": try { byte[] counterSignature = Base64.getEncoder().encode( diff --git a/tools/tcg_rim_tool/src/main/java/hirs/swid/utils/Commander.java b/tools/tcg_rim_tool/src/main/java/hirs/swid/utils/Commander.java index d18c4f35..d84f4dbf 100644 --- a/tools/tcg_rim_tool/src/main/java/hirs/swid/utils/Commander.java +++ b/tools/tcg_rim_tool/src/main/java/hirs/swid/utils/Commander.java @@ -3,6 +3,9 @@ package hirs.swid.utils; import com.beust.jcommander.Parameter; import hirs.swid.SwidTagConstants; +import java.util.ArrayList; +import java.util.List; + /** * Commander is a class that handles the command line arguments for the SWID * Tags gateway by implementing the JCommander package. @@ -45,15 +48,11 @@ public class Commander { @Parameter(names = {"-l", "--rimel "}, order = 9, description = "The TCG eventlog file to use as a support RIM.") private String rimEventLog = ""; - @Parameter(names = {"--rfc3852"}, order = 10, - description = "Add a placeholder for a base 64-encoded RFC3852 countersignature.") - private String rfc3852Filename = ""; - @Parameter(names = {"--rfc3339"}, order = 11, - description = "Add a timestamp to the signature that is compliant with RFC3339.") - private boolean rfc3852 = false; - @Parameter(names = {"--rfc3339"}, order = 11, validateWith = Rfc3339Format.class, - description = "Add a timestamp to the signature that is compliant with RFC3339.") - private String rfc3339 = ""; + @Parameter(names = {"--timestamp"}, order = 10, variableArity = true, + description = "Add a timestamp to the signature. " + + "Currently only RFC3339 and RFC3852 are supported:\n" + + "\tRFC3339 [yyyy-MM-ddThh:mm:ssZ]\n\tRFC3852 ") + private List timestampArguments = new ArrayList(2); public boolean isHelp() { return help; @@ -91,11 +90,9 @@ public class Commander { public String getRimEventLog() { return rimEventLog; } - public String getRfc3852Filename() { return rfc3852Filename; } - - public boolean isRfc3852() { return rfc3852; } - - public String getRfc3339() { return rfc3339; } + public List getTimestampArguments() { + return timestampArguments; + } public String printHelpExamples() { StringBuilder sb = new StringBuilder(); @@ -108,6 +105,11 @@ public class Commander { sb.append("sign it using privateKey.pem; embed cert.pem in the signature block; "); sb.append("and write the data to console output:\n\n"); sb.append("\t\t-c base -l support_rim.bin -k privateKey.pem -p cert.pem -e\n\n\n"); + sb.append("Create a base RIM using the values in attributes.json; " + + "sign it with the default keystore; add a RFC3852 timestamp; "); + sb.append("and write the data to base_rim.swidtag:\n\n"); + sb.append("\t\t-c base -a attributes.json -d -l support_rim.bin " + + "--timestamp RFC3852 counterSignature.bin -o base_rim.swidtag\n\n\n"); sb.append("Validate a base RIM using an external support RIM to override the "); sb.append("payload file:\n\n"); sb.append("\t\t-v base_rim.swidtag -l support_rim.bin\n\n\n"); @@ -138,12 +140,12 @@ public class Commander { sb.append("Signing credential: (none given)" + System.lineSeparator()); } sb.append("Event log support RIM: " + this.getRimEventLog() + System.lineSeparator()); - if (!this.getRfc3852Filename().isEmpty()) { - sb.append("Timestamp format: RFC3852, " + this.getRfc3852Filename()); - } else if (getRfc3339().isEmpty()) { - sb.append("Timestamp format: RFC3339 with generated timestamp"); - } else if (!getRfc3339().isEmpty()) { - sb.append("Timestamp format: RFC3339 with timestamp input"); + List timestampArguments = this.getTimestampArguments(); + if (timestampArguments.size() > 0) { + sb.append("Timestamp format: " + timestampArguments.get(0)); + if (timestampArguments.size() == 2) { + sb.append(", " + timestampArguments.get(1)); + } } else { sb.append("No timestamp included"); } diff --git a/tools/tcg_rim_tool/src/main/java/hirs/swid/utils/Rfc3339Format.java b/tools/tcg_rim_tool/src/main/java/hirs/swid/utils/Rfc3339Format.java deleted file mode 100644 index 9e189606..00000000 --- a/tools/tcg_rim_tool/src/main/java/hirs/swid/utils/Rfc3339Format.java +++ /dev/null @@ -1,24 +0,0 @@ -package hirs.swid.utils; - -import com.beust.jcommander.IParameterValidator; -import com.beust.jcommander.ParameterException; - -import java.time.Instant; -import java.time.format.DateTimeParseException; - -public class Rfc3339Format implements IParameterValidator { - public void validate(String name, String value) throws ParameterException { - if (value != null) { - try { - Instant instant = Instant.parse(value); - } catch (DateTimeParseException e) { - e.printStackTrace(); - throw new ParameterException("Parameter " + name + "=" + value + - " is not in valid RFC3339 format; " + - "expected format is yyyy-MM-dd'T'hh:mm:ss'Z'"); - } - } else { - return; - } - } -} diff --git a/tools/tcg_rim_tool/src/main/java/hirs/swid/utils/TimestampArgumentValidator.java b/tools/tcg_rim_tool/src/main/java/hirs/swid/utils/TimestampArgumentValidator.java new file mode 100644 index 00000000..96966580 --- /dev/null +++ b/tools/tcg_rim_tool/src/main/java/hirs/swid/utils/TimestampArgumentValidator.java @@ -0,0 +1,123 @@ +package hirs.swid.utils; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.time.Instant; +import java.time.format.DateTimeParseException; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class TimestampArgumentValidator { + List args; + /** + * This class handles validation of the --timestamp commandline parameter. + * Currently only RFC3339 and RFC3852 formats are supported. + * + * @param args list of arguments from command line + */ + public TimestampArgumentValidator(List args) { + this.args = args; + } + + /** + * This is the public access method through which all other methods are called. + * + * @return true if all arguments are valid, false otherwise + */ + public boolean isValid() { + if (isExactlyOneFormat(args)) { + if (args.get(0).equalsIgnoreCase("RFC3852")) { + if (args.size() == 2) { + if (isRfc3852FileValid(args.get(1))) { + return true; + } else { + return false; + } + } else if (args.size() == 1){ + System.out.println("Countersignature file is required for RFC3852 timestamps"); + return false; + } + } else if (args.get(0).equalsIgnoreCase("RFC3339")) { + if (args.size() == 2) { + if (isRfc3339Format(args.get(1))) { + return true; + } else { + return false; + } + } else if (args.size() == 1) { + return true; + } + } else { + System.out.println("Unsupported timestamp format specified"); + return false; + } + } + return false; + } + + /** + * This method ensures that exactly one of RFC3339 and RFC3852 are specified. + * + * @param args list of command line arguments + * @return true if exactly one format is specified, false otherwise + */ + private boolean isExactlyOneFormat(List args) { + Pattern pattern = Pattern.compile("(R|r)(F|f)(C|c)(3339|3852)"); + String format = args.get(0); + Matcher formatMatcher = pattern.matcher(format); + + if (!formatMatcher.matches()) { + System.out.println("Invalid timestamp format specified, expected RFC3339 or RFC3852."); + return false; + } + if (args.size() == 2) { + String argument = args.get(1); + Matcher argumentMatcher = pattern.matcher(argument); + if (argumentMatcher.matches()) { + System.out.println("Exactly one timestamp format must be specified."); + return false; + } + } + + return true; + } + + /** + * This method verifies a user-given RFC3339 timestamp + * + * @param timestamp the timestamp string + * @return true if valid RFC3339 format, false otherwise + */ + private boolean isRfc3339Format(String timestamp) { + try { + Instant instant = Instant.parse(timestamp); + } catch (DateTimeParseException e) { + System.out.println("Invalid RFC3339 timestamp given: " + timestamp); + return false; + } + return true; + } + + /** + * This method verifies the counter signature file + * + * @param file the counter signature + * @return true if file exists and is valid, false otherwise + */ + private boolean isRfc3852FileValid(String file) { + if (file != null && !file.isEmpty()) { + try { + Files.readAllBytes(Paths.get(file)); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + } else { + System.out.println("RFC3852 requires a filename input of the countersignature file."); + return false; + } + return true; + } +}