mirror of
https://github.com/nsacyber/HIRS.git
synced 2024-12-24 07:06:46 +00:00
Added back in HIRS_Utils
This commit is contained in:
parent
7cdbc74506
commit
86ef7d9356
54
HIRS_Utils/build.gradle
Normal file
54
HIRS_Utils/build.gradle
Normal file
@ -0,0 +1,54 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(17)
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
||||
flatDir { dirs "lib" }
|
||||
}
|
||||
|
||||
configurations {
|
||||
compileOnly {
|
||||
extendsFrom annotationProcessor
|
||||
}
|
||||
jaxb
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'org.bouncycastle:bcmail-jdk15on:1.70'
|
||||
implementation 'com.google.guava:guava:31.1-jre'
|
||||
implementation 'commons-codec:commons-codec:1.15'
|
||||
implementation 'org.apache.commons:commons-lang3:3.12.0'
|
||||
implementation 'org.apache.logging.log4j:log4j-core:2.19.0'
|
||||
implementation 'org.apache.logging.log4j:log4j-api:2.19.0'
|
||||
implementation 'jakarta.xml.bind:jakarta.xml.bind-api:4.0.0'
|
||||
implementation 'jakarta.persistence:jakarta.persistence-api:3.1.0'
|
||||
implementation 'com.eclipsesource.minimal-json:minimal-json:0.9.5'
|
||||
implementation 'com.fasterxml.jackson.core:jackson-core:2.14.2'
|
||||
implementation 'com.fasterxml.jackson.core:jackson-databind:2.14.2'
|
||||
implementation 'org.glassfish.jaxb:jaxb-runtime:4.0.1'
|
||||
compileOnly libs.lombok
|
||||
implementation libs.lombok
|
||||
annotationProcessor libs.lombok
|
||||
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0'
|
||||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
//task generateXjcLibrary(type:Exec) {
|
||||
// workingDir 'config'
|
||||
//
|
||||
// commandLine './genXjcLibrary.sh'
|
||||
//}
|
||||
//compileJava.dependsOn generateXjcLibrary
|
130
HIRS_Utils/src/main/java/hirs/utils/BannerConfiguration.java
Normal file
130
HIRS_Utils/src/main/java/hirs/utils/BannerConfiguration.java
Normal file
@ -0,0 +1,130 @@
|
||||
package hirs.utils;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* This class exposes methods to get the banner properties file. Properties are read from
|
||||
* /etc/hirs/banner.properties if it exists. If no value for a property exists in the file,
|
||||
* no change will be applied for that property.
|
||||
*/
|
||||
@Log4j2
|
||||
public class BannerConfiguration {
|
||||
|
||||
private static final Path BANNER_PROPERTIES_PATH = FileSystems.getDefault()
|
||||
.getPath("/opt/tomcat/webapps/HIRS_AttestationCAPortal", "WEB-INF", "classes", "banner.properties");
|
||||
|
||||
private static final String BANNER_COLOR = "banner.color";
|
||||
private static final String BANNER_STRING = "banner.string";
|
||||
private static final String BANNER_DYNAMIC = "banner.dynamic";
|
||||
private static final String LEFT_CONTENT = "left.content";
|
||||
private static final String RIGHT_CONTENT = "right.content";
|
||||
|
||||
@Getter
|
||||
private String bannerColor = "";
|
||||
@Getter
|
||||
private String bannerString = "";
|
||||
@Getter
|
||||
private String bannerDynamic = "";
|
||||
|
||||
private final ArrayList<String> leftContent = new ArrayList<>();
|
||||
private final ArrayList<String> rightContent = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Banner Configuration default constructor.
|
||||
* Verify if the file exist, if it does it will get all the
|
||||
* properties values and save them on the class.
|
||||
*
|
||||
* @throws IOException the banner level for the web site.
|
||||
*/
|
||||
public BannerConfiguration() throws IOException {
|
||||
if (!Files.exists(BANNER_PROPERTIES_PATH)) {
|
||||
log.info(String.format(
|
||||
"No file found at %s. Banner will not display.",
|
||||
BANNER_PROPERTIES_PATH.toString()
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
try (InputStream loggingIs = new FileInputStream(BANNER_PROPERTIES_PATH.toFile())) {
|
||||
Properties bannerProps = new Properties();
|
||||
bannerProps.load(loggingIs);
|
||||
setBannerProperties(bannerProps);
|
||||
} catch (IOException e) {
|
||||
throw new IOException("Could not apply banner configuration", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method applies any dynamically configuration found in the properties file,
|
||||
* if it exists.
|
||||
* @param bannerProps
|
||||
* @return the banner level for the web site.
|
||||
*/
|
||||
private void setBannerProperties(final Properties bannerProps) {
|
||||
|
||||
bannerColor = bannerProps.getProperty(BANNER_COLOR, "").toLowerCase();
|
||||
bannerString = bannerProps.getProperty(BANNER_STRING, "").toUpperCase();
|
||||
bannerDynamic = bannerProps.getProperty(BANNER_DYNAMIC, "").toUpperCase();
|
||||
|
||||
// We don't need these any more
|
||||
bannerProps.remove(BANNER_COLOR);
|
||||
bannerProps.remove(BANNER_STRING);
|
||||
bannerProps.remove(BANNER_DYNAMIC);
|
||||
|
||||
//Get property list and sort it
|
||||
ArrayList<String> propertyList = new ArrayList<>(bannerProps.stringPropertyNames());
|
||||
Collections.sort(propertyList);
|
||||
|
||||
// Set banner information from the property file
|
||||
for (String prop : propertyList) {
|
||||
if (prop.startsWith(LEFT_CONTENT)) {
|
||||
leftContent.add(bannerProps.getProperty(prop));
|
||||
} else if (prop.startsWith(RIGHT_CONTENT)) {
|
||||
rightContent.add(bannerProps.getProperty(prop));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if the banner was set.
|
||||
*
|
||||
* @return if the banner was set.
|
||||
*/
|
||||
public Boolean getHasBanner() {
|
||||
if (!bannerColor.isEmpty() || !bannerString.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the left content.
|
||||
*
|
||||
* @return the left content
|
||||
*/
|
||||
public List<String> getLeftContent() {
|
||||
return Collections.unmodifiableList(leftContent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the right content.
|
||||
*
|
||||
* @return the right content
|
||||
*/
|
||||
public List<String> getRightContent() {
|
||||
return Collections.unmodifiableList(rightContent);
|
||||
}
|
||||
}
|
||||
|
47
HIRS_Utils/src/main/java/hirs/utils/BouncyCastleUtils.java
Normal file
47
HIRS_Utils/src/main/java/hirs/utils/BouncyCastleUtils.java
Normal file
@ -0,0 +1,47 @@
|
||||
package hirs.utils;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.bouncycastle.asn1.x500.X500Name;
|
||||
|
||||
/**
|
||||
* Utilities class specific for additional Bouncy Castle functionality.
|
||||
*/
|
||||
@Log4j2
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public final class BouncyCastleUtils {
|
||||
|
||||
private static final String SEPARATOR_COMMA = ",";
|
||||
private static final String SEPARATOR_PLUS = "+";
|
||||
|
||||
/**
|
||||
* This method can be used to compare the distinguished names given from
|
||||
* certificates. This compare uses X500Name class in bouncy castle, which
|
||||
* compares the RDNs and not the string itself. The method will check for
|
||||
* '+' and replace them, X500Name doesn't do this.
|
||||
*
|
||||
* @param nameValue1 first general name to be used
|
||||
* @param nameValue2 second general name to be used
|
||||
* @return true if the values match based on the RDNs, false if not
|
||||
*/
|
||||
public static boolean x500NameCompare(final String nameValue1, final String nameValue2) {
|
||||
if (nameValue1 == null || nameValue2 == null) {
|
||||
throw new IllegalArgumentException("Provided DN string is null.");
|
||||
}
|
||||
|
||||
boolean result = false;
|
||||
X500Name x500Name1;
|
||||
X500Name x500Name2;
|
||||
|
||||
try {
|
||||
x500Name1 = new X500Name(nameValue1.replace(SEPARATOR_PLUS, SEPARATOR_COMMA));
|
||||
x500Name2 = new X500Name(nameValue2.replace(SEPARATOR_PLUS, SEPARATOR_COMMA));
|
||||
result = x500Name1.equals(x500Name2);
|
||||
} catch (IllegalArgumentException iaEx) {
|
||||
log.error(iaEx.toString());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
115
HIRS_Utils/src/main/java/hirs/utils/HexUtils.java
Normal file
115
HIRS_Utils/src/main/java/hirs/utils/HexUtils.java
Normal file
@ -0,0 +1,115 @@
|
||||
package hirs.utils;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* Utilities for working with hex strings and byte arrays.
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public final class HexUtils {
|
||||
|
||||
/**
|
||||
* The mathematical base for the hexadecimal representation.
|
||||
*/
|
||||
public static final int HEX_BASIS = 16;
|
||||
|
||||
/**
|
||||
* An integer representation of the byte 0xff or 255.
|
||||
*/
|
||||
public static final int FF_BYTE = 0xff;
|
||||
|
||||
/**
|
||||
* Converts a binary hex string to a byte array.
|
||||
* @param s string to convert
|
||||
* @return byte array representation of s
|
||||
*/
|
||||
public static byte[] hexStringToByteArray(final String s) {
|
||||
int sizeInt = s.length() / 2;
|
||||
byte[] returnArray = new byte[sizeInt];
|
||||
String byteVal;
|
||||
for (int i = 0; i < sizeInt; i++) {
|
||||
int index = 2 * i;
|
||||
byteVal = s.substring(index, index + 2);
|
||||
returnArray[i] = (byte) (Integer.parseInt(byteVal, HEX_BASIS));
|
||||
}
|
||||
return returnArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a byte array to a hex represented binary string.
|
||||
* @param b byte array to convert
|
||||
* @return hex string representation of array
|
||||
*/
|
||||
public static String byteArrayToHexString(final byte[] b) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String returnStr = "";
|
||||
for (int i = 0; i < b.length; i++) {
|
||||
String singleByte = Integer.toHexString(b[i] & FF_BYTE);
|
||||
if (singleByte.length() != 2) {
|
||||
singleByte = "0" + singleByte;
|
||||
}
|
||||
returnStr = sb.append(singleByte).toString();
|
||||
}
|
||||
return returnStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an individual hex string to an integer.
|
||||
* @param s an individual hex string
|
||||
* @return an integer representation of a hex string
|
||||
*/
|
||||
public static Integer hexToInt(final String s) {
|
||||
Integer i = Integer.parseInt(s, HEX_BASIS);
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a byte array returns a subset of the array.
|
||||
* @param b the array to take a subset of
|
||||
* @param start the first index to copy
|
||||
* @param end the last index to copy (inclusive)
|
||||
* @return a new array of bytes from start to end
|
||||
*/
|
||||
public static byte[] subarray(final byte[] b, final int start, final int end) {
|
||||
byte[] copy = new byte[end - start + 1];
|
||||
System.arraycopy(b, start, copy, 0, end - start + 1);
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes in a byte array and reverses the order.
|
||||
* @param in byte array to reverse
|
||||
* @return reversed byte array
|
||||
*/
|
||||
public static byte[] leReverseByte(final byte[] in) {
|
||||
byte[] finished = new byte[in.length];
|
||||
for (int i = 0; i < finished.length; i++) {
|
||||
finished[i] = in[(in.length - 1) - i];
|
||||
}
|
||||
return finished;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes in a byte array and reverses the order then converts to an int.
|
||||
* @param in byte array to reverse
|
||||
* @return integer that represents the reversed byte array
|
||||
*/
|
||||
public static int leReverseInt(final byte[] in) {
|
||||
byte[] finished = leReverseByte(in);
|
||||
return new BigInteger(finished).intValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes in a byte array of 4 bytes and returns a long.
|
||||
* @param bytes byte array to convert
|
||||
* @return long representation of the bytes
|
||||
*/
|
||||
public static long bytesToLong(final byte[] bytes) {
|
||||
BigInteger lValue = new BigInteger(bytes);
|
||||
|
||||
return lValue.abs().longValue();
|
||||
}
|
||||
}
|
100
HIRS_Utils/src/main/java/hirs/utils/JsonUtils.java
Normal file
100
HIRS_Utils/src/main/java/hirs/utils/JsonUtils.java
Normal file
@ -0,0 +1,100 @@
|
||||
package hirs.utils;
|
||||
|
||||
import com.eclipsesource.json.Json;
|
||||
import com.eclipsesource.json.JsonObject;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* A utility class for common JSON operations using the {@link com.eclipsesource}
|
||||
* library.
|
||||
*/
|
||||
@Log4j2
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public final class JsonUtils {
|
||||
|
||||
/**
|
||||
* Getter for the JSON Object that is associated with the elementName value
|
||||
* mapped in the associated JSON file.
|
||||
* Default {@link java.nio.charset.Charset} is UTF 8
|
||||
*
|
||||
* @param jsonPath the object holding the location of the file to parse.
|
||||
* @param elementName the specific object to pull from the file
|
||||
* @return a JSON object
|
||||
*/
|
||||
public static JsonObject getSpecificJsonObject(final Path jsonPath, final String elementName) {
|
||||
// find the file and load it
|
||||
return getSpecificJsonObject(jsonPath, elementName, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the JSON Object that is associated with the elementName value
|
||||
* mapped in the associated JSON file.
|
||||
* Default {@link java.nio.charset.Charset} is UTF 8
|
||||
*
|
||||
* @param jsonPath the object holding the location of the file to parse.
|
||||
* @param elementName the specific object to pull from the file
|
||||
* @param charset the character set to use
|
||||
* @return a JSON object
|
||||
*/
|
||||
public static JsonObject getSpecificJsonObject(final Path jsonPath,
|
||||
final String elementName,
|
||||
final Charset charset) {
|
||||
// find the file and load it
|
||||
JsonObject jsonObject = getJsonObject(jsonPath, charset);
|
||||
|
||||
if (jsonObject != null && jsonObject.get(elementName) != null) {
|
||||
return jsonObject.get(elementName).asObject();
|
||||
}
|
||||
|
||||
return new JsonObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the JSON Object that is mapped in the associated JSON file.
|
||||
* Default {@link java.nio.charset.Charset} is UTF 8
|
||||
*
|
||||
* @param jsonPath the object holding the location of the file to parse.
|
||||
* @return a JSON object
|
||||
*/
|
||||
public static JsonObject getJsonObject(final Path jsonPath) {
|
||||
return getJsonObject(jsonPath, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the JSON Object that is mapped in the associated JSON file.
|
||||
*
|
||||
* @param jsonPath the object holding the location of the file to parse.
|
||||
* @param charset the character set to use
|
||||
* @return a JSON object
|
||||
*/
|
||||
public static JsonObject getJsonObject(final Path jsonPath, final Charset charset) {
|
||||
// find the file and load it
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
|
||||
if (Files.notExists(jsonPath)) {
|
||||
log.warn(String.format("No file found at %s.", jsonPath.toString()));
|
||||
} else {
|
||||
try {
|
||||
InputStream inputStream = new FileInputStream(jsonPath.toString());
|
||||
jsonObject = Json.parse(new InputStreamReader(inputStream,
|
||||
charset)).asObject();
|
||||
} catch (IOException ex) {
|
||||
// add log file thing here indication issue with JSON File
|
||||
jsonObject = new JsonObject();
|
||||
}
|
||||
}
|
||||
|
||||
return jsonObject;
|
||||
}
|
||||
}
|
110
HIRS_Utils/src/main/java/hirs/utils/StringValidator.java
Normal file
110
HIRS_Utils/src/main/java/hirs/utils/StringValidator.java
Normal file
@ -0,0 +1,110 @@
|
||||
package hirs.utils;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
/**
|
||||
* A simple utility that exposes a fluent way to validate Strings. Can easily be generalized to
|
||||
* any type of data. See example usage in StringValidationTest.
|
||||
*/
|
||||
@Log4j2
|
||||
public final class StringValidator {
|
||||
|
||||
@Getter
|
||||
private final String value;
|
||||
private final String fieldName;
|
||||
private final Logger logger;
|
||||
|
||||
/**
|
||||
* Begins a validation operation.
|
||||
*
|
||||
* @param value the value to check
|
||||
* @param fieldName the name of the field (to be used in error reporting)
|
||||
* @return a Validation object, upon which validation methods can be called
|
||||
*/
|
||||
public static StringValidator check(final String value, final String fieldName) {
|
||||
return new StringValidator(value, fieldName, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Begins a validation operation.
|
||||
*
|
||||
* @param value the value to check
|
||||
* @param fieldName the name of the field (to be used in error reporting)
|
||||
* @param logger a logger to use in lieu of Validation's logger
|
||||
* @return a Validation object, upon which validation methods can be called
|
||||
*/
|
||||
public static StringValidator check(final String value, final String fieldName,
|
||||
final Logger logger) {
|
||||
return new StringValidator(value, fieldName, logger);
|
||||
}
|
||||
|
||||
private StringValidator(final String value, final String fieldName, final Logger logger) {
|
||||
this.value = value;
|
||||
this.fieldName = fieldName;
|
||||
if (logger == null) {
|
||||
this.logger = log;
|
||||
} else {
|
||||
this.logger = logger;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the given field is not null. Throws an IllegalArgumentException if the value
|
||||
* is indeed null.
|
||||
*
|
||||
* @return this Validation object for further validation
|
||||
*/
|
||||
public StringValidator notNull() {
|
||||
if (value == null) {
|
||||
String message = String.format("Field %s is null", fieldName);
|
||||
logger.error(message);
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the given field is not blank (empty or null.) Throws an IllegalArgumentException
|
||||
* if the value is indeed blank.
|
||||
*
|
||||
* @return this Validation object for further validation
|
||||
*/
|
||||
public StringValidator notBlank() {
|
||||
if (StringUtils.isBlank(value)) {
|
||||
String message = String.format("Field %s is blank (empty or null)", fieldName);
|
||||
logger.error(message);
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the given field is not longer than the given value. Throws an
|
||||
* IllegalArgumentException if the value exceeds this length. A null value will pass
|
||||
* this validation.
|
||||
*
|
||||
* @param maxLength the maximum length of the String
|
||||
* @return this Validation object for further validation
|
||||
*/
|
||||
public StringValidator maxLength(final int maxLength) {
|
||||
if (value == null) {
|
||||
return this;
|
||||
}
|
||||
|
||||
if (value.length() > maxLength) {
|
||||
String message = String.format(
|
||||
"Field %s is too large (%d > %d) with value %s",
|
||||
fieldName, value.length(), maxLength, value
|
||||
);
|
||||
logger.error(message);
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
80
HIRS_Utils/src/main/java/hirs/utils/SwidResource.java
Normal file
80
HIRS_Utils/src/main/java/hirs/utils/SwidResource.java
Normal file
@ -0,0 +1,80 @@
|
||||
package hirs.utils;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import hirs.utils.digest.DigestAlgorithm;
|
||||
import hirs.utils.xjc.File;
|
||||
import lombok.Getter;
|
||||
|
||||
import javax.xml.namespace.QName;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This object is used to represent the content of a Swid Tags Directory
|
||||
* section.
|
||||
*/
|
||||
public class SwidResource {
|
||||
|
||||
@Getter
|
||||
private String name, size;
|
||||
@Getter
|
||||
private String rimFormat, rimType, rimUriGlobal, hashValue;
|
||||
// private TpmWhiteListBaseline tpmWhiteList;
|
||||
private DigestAlgorithm digest = DigestAlgorithm.SHA1;
|
||||
@Getter
|
||||
private boolean validFileSize = false;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public SwidResource() {
|
||||
name = null;
|
||||
size = null;
|
||||
rimFormat = null;
|
||||
rimType = null;
|
||||
rimUriGlobal = null;
|
||||
hashValue = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The main constructor that processes a {@code hirs.utils.xjc.File}.
|
||||
*
|
||||
* @param file {@link File}
|
||||
* @param digest algorithm associated with pcr values
|
||||
*/
|
||||
public SwidResource(final File file, final DigestAlgorithm digest) {
|
||||
Preconditions.checkArgument(file != null,
|
||||
"Cannot construct a RIM Resource from a null File object");
|
||||
|
||||
this.name = file.getName();
|
||||
// at this time, there is a possibility to get an object with
|
||||
// no size even though it is required.
|
||||
if (file.getSize() != null) {
|
||||
this.size = file.getSize().toString();
|
||||
} else {
|
||||
this.size = BigInteger.ZERO.toString();
|
||||
}
|
||||
|
||||
for (Map.Entry<QName, String> entry
|
||||
: file.getOtherAttributes().entrySet()) {
|
||||
switch (entry.getKey().getLocalPart()) {
|
||||
case "supportRIMFormat":
|
||||
this.rimFormat = entry.getValue();
|
||||
break;
|
||||
case "supportRIMType":
|
||||
this.rimType = entry.getValue();
|
||||
break;
|
||||
case "supportRIMURIGlobal":
|
||||
this.rimUriGlobal = entry.getValue();
|
||||
break;
|
||||
case "hash":
|
||||
this.hashValue = entry.getValue();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
this.digest = digest;
|
||||
// tpmWhiteList = new TpmWhiteListBaseline(this.name);
|
||||
}
|
||||
}
|
57
HIRS_Utils/src/main/java/hirs/utils/VersionHelper.java
Normal file
57
HIRS_Utils/src/main/java/hirs/utils/VersionHelper.java
Normal file
@ -0,0 +1,57 @@
|
||||
package hirs.utils;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.io.Resources;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* Utility class to get the current version from the VERSION file.
|
||||
*/
|
||||
public final class VersionHelper {
|
||||
|
||||
private static final String VERSION_FILENAME = "VERSION";
|
||||
|
||||
private VersionHelper() {
|
||||
// intentionally blank, should never be instantiated
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current version of HIRS_Portal that is installed.
|
||||
*
|
||||
* @return A string representing the current version.
|
||||
*/
|
||||
public static String getVersion() {
|
||||
return getVersion(VERSION_FILENAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current version of HIRS_Portal that is installed.
|
||||
*
|
||||
* @param filename
|
||||
* that contains the version
|
||||
* @return A string representing the current version.
|
||||
*/
|
||||
public static String getVersion(final String filename) {
|
||||
String version;
|
||||
try {
|
||||
version = getFileContents(filename);
|
||||
} catch (Exception e) {
|
||||
version = "";
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the symbolic link to VERSION in the top level HIRS directory.
|
||||
* @param filename "VERSION"
|
||||
* @return the version number from the file
|
||||
* @throws IOException
|
||||
*/
|
||||
private static String getFileContents(final String filename) throws IOException {
|
||||
URL url = Resources.getResource(filename);
|
||||
return Resources.toString(url, Charsets.UTF_8).trim();
|
||||
}
|
||||
}
|
||||
|
246
HIRS_Utils/src/main/java/hirs/utils/digest/AbstractDigest.java
Normal file
246
HIRS_Utils/src/main/java/hirs/utils/digest/AbstractDigest.java
Normal file
@ -0,0 +1,246 @@
|
||||
package hirs.utils.digest;
|
||||
|
||||
import jakarta.xml.bind.DatatypeConverter;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.apache.commons.codec.binary.Hex;
|
||||
import org.apache.logging.log4j.core.util.ArrayUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* This abstract class represents a message digest. Extending classes include
|
||||
* {@link hirs.utils.digest.Digest} and {@link hirs.utils.digest.OptionalDigest}.
|
||||
* <p>
|
||||
* Two classes were made to facilitate persisting them with Hibernate in different ways.
|
||||
* To persist non-nullable entries in an embedded collection, use {@link hirs.utils.digest.Digest} (see
|
||||
* {@link TPMBaseline} for reference.) To persist nullable entries, use {@link hirs.utils.digest.OptionalDigest}
|
||||
* (see {@link ImaBlacklistRecord} for reference.)
|
||||
*/
|
||||
@Log4j2
|
||||
public abstract class AbstractDigest {
|
||||
/**
|
||||
* Length of MD2 digest.
|
||||
*/
|
||||
public static final int MD2_DIGEST_LENGTH = 16;
|
||||
/**
|
||||
* Length of MD5 digest.
|
||||
*/
|
||||
public static final int MD5_DIGEST_LENGTH = 16;
|
||||
/**
|
||||
* Length of SHA1 digest.
|
||||
*/
|
||||
public static final int SHA1_DIGEST_LENGTH = 20;
|
||||
/**
|
||||
* Length of SHA256 digest.
|
||||
*/
|
||||
public static final int SHA256_DIGEST_LENGTH = 32;
|
||||
/**
|
||||
* Length of SHA384 digest.
|
||||
*/
|
||||
public static final int SHA384_DIGEST_LENGTH = 48;
|
||||
/**
|
||||
* Length of SHA512 digest.
|
||||
*/
|
||||
public static final int SHA512_DIGEST_LENGTH = 64;
|
||||
|
||||
/**
|
||||
* Ensures the given algorithm type and digest byte array represent a valid digest.
|
||||
* This includes ensuring they are both not null or empty and ensuring that the length of the
|
||||
* digest matches the expected amount of data for the given algorithm.
|
||||
*
|
||||
* @param algorithm a digest algorithm
|
||||
* @param digest the digest computed by this algorithm
|
||||
* @throws IllegalArgumentException if the provided input does not represent a valid digest
|
||||
*/
|
||||
void validateInput(final DigestAlgorithm algorithm, final byte[] digest)
|
||||
throws IllegalArgumentException {
|
||||
if (algorithm == null) {
|
||||
throw new IllegalArgumentException("Algorithm must not be null");
|
||||
}
|
||||
|
||||
if (ArrayUtils.isEmpty(digest)) {
|
||||
throw new IllegalArgumentException("Digest must have at least one byte");
|
||||
}
|
||||
|
||||
if (digest.length != algorithm.getLengthInBytes()) {
|
||||
throw new AbstractDigest.IllegalDigestLength(algorithm, digest);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will help class determine the algorithm associated with the
|
||||
* pcr values given.
|
||||
*
|
||||
* @param digest list of pcr values.
|
||||
* @return the associated algorithm.
|
||||
*/
|
||||
public static final DigestAlgorithm getDigestAlgorithm(final byte[] digest) {
|
||||
if (digest == null || ArrayUtils.isEmpty(digest)) {
|
||||
return DigestAlgorithm.UNSPECIFIED;
|
||||
}
|
||||
|
||||
switch (digest.length) {
|
||||
case MD2_DIGEST_LENGTH:
|
||||
return DigestAlgorithm.MD5;
|
||||
case SHA1_DIGEST_LENGTH:
|
||||
return DigestAlgorithm.SHA1;
|
||||
case SHA256_DIGEST_LENGTH:
|
||||
return DigestAlgorithm.SHA256;
|
||||
case SHA384_DIGEST_LENGTH:
|
||||
return DigestAlgorithm.SHA384;
|
||||
case SHA512_DIGEST_LENGTH:
|
||||
return DigestAlgorithm.SHA512;
|
||||
default:
|
||||
return DigestAlgorithm.UNSPECIFIED;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will help class determine the algorithm associated with the
|
||||
* pcr values given.
|
||||
*
|
||||
* @param digest list of pcr values.
|
||||
* @return the associated algorithm.
|
||||
*/
|
||||
public static final DigestAlgorithm getDigestAlgorithm(final String digest) {
|
||||
try {
|
||||
return getDigestAlgorithm(Hex.decodeHex(digest.toCharArray()));
|
||||
} catch (Exception deEx) {
|
||||
log.error(deEx);
|
||||
}
|
||||
|
||||
return DigestAlgorithm.UNSPECIFIED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the <code>DigestAlgorithm</code> that identifies which hash
|
||||
* function generated the digest.
|
||||
*
|
||||
* @return digest algorithm
|
||||
*/
|
||||
public abstract DigestAlgorithm getAlgorithm();
|
||||
|
||||
/**
|
||||
* Retrieves the digest.
|
||||
*
|
||||
* @return digest
|
||||
*/
|
||||
public abstract byte[] getDigest();
|
||||
|
||||
/**
|
||||
* Returns a hex <code>String</code> representing the binary digest.
|
||||
*
|
||||
* @return hex representation of digest
|
||||
*/
|
||||
public String getDigestString() {
|
||||
return Hex.encodeHexString(getDigest());
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares this digest's hash with another digest's hash.
|
||||
* @param otherDigest a Digest to compare to.
|
||||
* @return the comparison result type.
|
||||
*/
|
||||
public DigestComparisonResultType compare(final Digest otherDigest) {
|
||||
if (null == otherDigest) {
|
||||
return DigestComparisonResultType.UNKNOWN;
|
||||
}
|
||||
|
||||
if (this.equals(otherDigest)) {
|
||||
return DigestComparisonResultType.MATCH;
|
||||
}
|
||||
|
||||
return DigestComparisonResultType.MISMATCH;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a {@link DigestAlgorithm} from a String returned by {@link AbstractDigest#toString()}.
|
||||
*
|
||||
* @param digest the digest string as computed above
|
||||
* @return the DigestAlgorithm component of the String
|
||||
*/
|
||||
static DigestAlgorithm algorithmFromString(final String digest) {
|
||||
return DigestAlgorithm.findByString(matchString(digest).group(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a digest from a String returned by {@link AbstractDigest#toString()}.
|
||||
*
|
||||
* @param digest the digest string as computed above
|
||||
* @return the byte array representing the actual digest
|
||||
*/
|
||||
static byte[] digestFromString(final String digest) {
|
||||
return DatatypeConverter.parseHexBinary(matchString(digest).group(2));
|
||||
}
|
||||
|
||||
private static Matcher matchString(final String digest) {
|
||||
Pattern digestPattern = Pattern.compile("(.*) - 0x(.*)");
|
||||
Matcher matcher = digestPattern.matcher(digest);
|
||||
if (!matcher.matches()) {
|
||||
String message = String.format("String \"%s\" did not match pattern \"%s\"", digest,
|
||||
digestPattern.toString());
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
return matcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + getAlgorithm().hashCode();
|
||||
result = prime * result + Arrays.hashCode(getDigest());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean equals(final Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj == null || !(obj instanceof AbstractDigest)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AbstractDigest other = (AbstractDigest) obj;
|
||||
|
||||
if (getAlgorithm() != other.getAlgorithm()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Arrays.equals(getDigest(), other.getDigest())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the standard algorithm name and a hexadecimal representation of
|
||||
* the bytes.
|
||||
*
|
||||
* @return string representation
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
//NOTE: Any updates here should also be reflected in fromString()
|
||||
return String.format("%s - 0x%s", getAlgorithm().getStandardAlgorithmName(),
|
||||
Hex.encodeHexString(getDigest()));
|
||||
}
|
||||
|
||||
private static final class IllegalDigestLength extends
|
||||
IllegalArgumentException {
|
||||
|
||||
private static final long serialVersionUID = 8782184397041237374L;
|
||||
|
||||
private IllegalDigestLength(final DigestAlgorithm algorithm,
|
||||
final byte[] digest) {
|
||||
super(String.format(
|
||||
"digest length (%d) does not match that of algorithm (%s)",
|
||||
digest.length, algorithm.toString()));
|
||||
}
|
||||
}
|
||||
}
|
136
HIRS_Utils/src/main/java/hirs/utils/digest/Digest.java
Normal file
136
HIRS_Utils/src/main/java/hirs/utils/digest/Digest.java
Normal file
@ -0,0 +1,136 @@
|
||||
package hirs.utils.digest;
|
||||
|
||||
import jakarta.persistence.Access;
|
||||
import jakarta.persistence.AccessType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import jakarta.persistence.EnumType;
|
||||
import jakarta.persistence.Enumerated;
|
||||
import jakarta.xml.bind.annotation.XmlElement;
|
||||
import org.apache.commons.codec.DecoderException;
|
||||
import org.apache.commons.codec.binary.Hex;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* This class represents a message digest. This stores the bytes of a message
|
||||
* digest as computed by a hash function.
|
||||
* <p>
|
||||
* This class differs from Java's provided <code>MessageDigest</code> class by the
|
||||
* fact that it does not compute a digest. This class simply stores the result
|
||||
* of a digest. This is useful for scenarios where the digest is already known.
|
||||
* This is the case for IMA reports that already have the digest computed. The
|
||||
* <code>MessageDigest</code> class does not provide a means to store that value.
|
||||
* The value must be computed.
|
||||
*/
|
||||
@Embeddable
|
||||
@Access(AccessType.FIELD)
|
||||
public final class Digest extends AbstractDigest {
|
||||
/**
|
||||
* A SHA1 digest whose content is all zeros.
|
||||
*/
|
||||
public static final Digest SHA1_ZERO = new Digest(
|
||||
DigestAlgorithm.SHA1,
|
||||
new byte[SHA1_DIGEST_LENGTH]
|
||||
);
|
||||
|
||||
private static final String SHA1_EMPTY_HEX =
|
||||
"da39a3ee5e6b4b0d3255bfef95601890afd80709";
|
||||
|
||||
/**
|
||||
* A SHA1 digest whose content is the hash of an empty buffer.
|
||||
*/
|
||||
public static final Digest SHA1_OF_NO_DATA;
|
||||
|
||||
static {
|
||||
try {
|
||||
SHA1_OF_NO_DATA = new Digest(
|
||||
DigestAlgorithm.SHA1,
|
||||
Hex.decodeHex(SHA1_EMPTY_HEX.toCharArray())
|
||||
);
|
||||
} catch (DecoderException e) {
|
||||
throw new RuntimeException("Could not decode hex value", e);
|
||||
}
|
||||
}
|
||||
|
||||
@XmlElement
|
||||
@Column(nullable = false, name = "digest", length = SHA512_DIGEST_LENGTH,
|
||||
columnDefinition = "varbinary(64)")
|
||||
private final byte[] digest;
|
||||
|
||||
@XmlElement
|
||||
@Column(nullable = false)
|
||||
@Enumerated(EnumType.ORDINAL)
|
||||
private final DigestAlgorithm algorithm;
|
||||
|
||||
/**
|
||||
* Creates a new <code>Digest</code>.
|
||||
*
|
||||
* @param algorithm algorithm used to generate the digest
|
||||
* @param digest digest value
|
||||
* @throws IllegalArgumentException if digest length does not match that of the algorithm
|
||||
*/
|
||||
public Digest(final DigestAlgorithm algorithm, final byte[] digest)
|
||||
throws IllegalArgumentException {
|
||||
validateInput(algorithm, digest);
|
||||
this.algorithm = algorithm;
|
||||
this.digest = Arrays.copyOf(digest, digest.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new <code>Digest</code> when an algorithm isn't specified.
|
||||
* @param digest byte array value
|
||||
*/
|
||||
public Digest(final byte[] digest) {
|
||||
this(AbstractDigest.getDigestAlgorithm(digest), digest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default constructor necessary for Hibernate.
|
||||
*/
|
||||
protected Digest() {
|
||||
this.algorithm = null;
|
||||
this.digest = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the <code>DigestAlgorithm</code> that identifies which hash
|
||||
* function generated the digest.
|
||||
*
|
||||
* @return digest algorithm
|
||||
*/
|
||||
@Override
|
||||
public DigestAlgorithm getAlgorithm() {
|
||||
return this.algorithm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the digest.
|
||||
*
|
||||
* @return digest
|
||||
*/
|
||||
@Override
|
||||
public byte[] getDigest() {
|
||||
return Arrays.copyOf(this.digest, this.digest.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new Digest with the same attributes as this instance.
|
||||
*
|
||||
* @return a new equivalent Digest
|
||||
*/
|
||||
public OptionalDigest asOptionalDigest() {
|
||||
return new OptionalDigest(algorithm, digest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to reverse the toString method. Returns a Digest given a String
|
||||
* that was created using an AbstractDigest's toString method.
|
||||
*
|
||||
* @param digest String representation of an AbstractDigest
|
||||
* @return Digest object recreated from the String passed in
|
||||
*/
|
||||
public static Digest fromString(final String digest) {
|
||||
return new Digest(algorithmFromString(digest), digestFromString(digest));
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
package hirs.utils.digest;
|
||||
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* Enum of digest algorithms. The enum values also provide a standardized
|
||||
* algorithm name. The standardized algorithm name is a String of the algorithm
|
||||
* name as defined by Java.
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum DigestAlgorithm {
|
||||
/**
|
||||
* MD2 digest algorithm.
|
||||
*/
|
||||
MD2("MD2", AbstractDigest.MD2_DIGEST_LENGTH),
|
||||
/**
|
||||
* MD5 digest algorithm.
|
||||
*/
|
||||
MD5("MD5", AbstractDigest.MD5_DIGEST_LENGTH),
|
||||
/**
|
||||
* SHA-1 digest algorithm.
|
||||
*/
|
||||
SHA1("SHA-1", AbstractDigest.SHA1_DIGEST_LENGTH),
|
||||
/**
|
||||
* SHA-256 digest algorithm.
|
||||
*/
|
||||
SHA256("SHA-256", AbstractDigest.SHA256_DIGEST_LENGTH),
|
||||
/**
|
||||
* SHA-384 digest algorithm.
|
||||
*/
|
||||
SHA384("SHA-384", AbstractDigest.SHA384_DIGEST_LENGTH),
|
||||
/**
|
||||
* SHA-512 digest algorithm.
|
||||
*/
|
||||
SHA512("SHA-512", AbstractDigest.SHA512_DIGEST_LENGTH),
|
||||
/**
|
||||
* Condition used when an algorithm is not specified and
|
||||
* the size doesn't match known digests.
|
||||
*/
|
||||
UNSPECIFIED("NOT SPECIFIED", Integer.BYTES);
|
||||
|
||||
private final String standardAlgorithmName;
|
||||
private final int lengthInBytes;
|
||||
|
||||
/**
|
||||
* Returns a DigestAlgorithm object given a String. The String is expected to be one of the
|
||||
* options for standardAlgorithmName. Throws an IllegalArgumentException if no Enum exists with
|
||||
* that value.
|
||||
*
|
||||
* @param standardAlgorithmName
|
||||
* String value of the Enum
|
||||
* @return DigestAlgorithm object
|
||||
*/
|
||||
public static DigestAlgorithm findByString(final String standardAlgorithmName) {
|
||||
for (DigestAlgorithm algorithm: DigestAlgorithm.values()) {
|
||||
if (algorithm.getStandardAlgorithmName().equals(standardAlgorithmName)) {
|
||||
return algorithm;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException(String.format("No constant with text \"%s\" found",
|
||||
standardAlgorithmName));
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package hirs.utils.digest;
|
||||
|
||||
/**
|
||||
* Enumeration identifying the different outcomes of a comparison between
|
||||
* two {@link Digest} objects.
|
||||
*
|
||||
*/
|
||||
public enum DigestComparisonResultType {
|
||||
/**
|
||||
* When one of the Digests compared has a hash that is uninitialized, defaulted, or
|
||||
* is a byte array equal to zero.
|
||||
*/
|
||||
UNKNOWN,
|
||||
|
||||
/**
|
||||
* When the two digest hashes are equal, and are not zeroized / defaulted hash arrays.
|
||||
*/
|
||||
MATCH,
|
||||
|
||||
/**
|
||||
* When the two digest hashes are not equal, and are not zeroized / defaulted hash arrays.
|
||||
*/
|
||||
MISMATCH,
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
package hirs.utils.digest;
|
||||
|
||||
import jakarta.persistence.Access;
|
||||
import jakarta.persistence.AccessType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import jakarta.persistence.EnumType;
|
||||
import jakarta.persistence.Enumerated;
|
||||
import jakarta.xml.bind.annotation.XmlElement;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* This class is identical to {@link Digest} except its fields are nullable. However, in practice,
|
||||
* an instance of this class cannot have null values assigned to its fields. The fields are marked
|
||||
* as nullable to allow Hibernate to set a reference an embedded instance of this class to null
|
||||
* (as there is no way for Hibernate to distinguish between a null reference and completely
|
||||
* null fields on an embedded entity.) Otherwise, there is no operational difference between
|
||||
* this class and {@link Digest}.
|
||||
*/
|
||||
@Embeddable
|
||||
@Access(AccessType.FIELD)
|
||||
public final class OptionalDigest extends AbstractDigest {
|
||||
@XmlElement
|
||||
@Column(nullable = true, name = "digest", length = SHA512_DIGEST_LENGTH,
|
||||
columnDefinition = "varbinary(64)")
|
||||
private final byte[] digest;
|
||||
|
||||
@XmlElement
|
||||
@Column(nullable = true)
|
||||
@Enumerated(EnumType.ORDINAL)
|
||||
private final DigestAlgorithm algorithm;
|
||||
|
||||
/**
|
||||
* Creates a new <code>OptionalDigest</code>.
|
||||
*
|
||||
* @param algorithm algorithm used to generate the digest
|
||||
* @param digest digest value
|
||||
* @throws IllegalArgumentException if digest length does not match that of the algorithm
|
||||
*/
|
||||
public OptionalDigest(final DigestAlgorithm algorithm, final byte[] digest)
|
||||
throws IllegalArgumentException {
|
||||
validateInput(algorithm, digest);
|
||||
this.algorithm = algorithm;
|
||||
this.digest = Arrays.copyOf(digest, digest.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default constructor necessary for Hibernate.
|
||||
*/
|
||||
protected OptionalDigest() {
|
||||
this.algorithm = null;
|
||||
this.digest = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <code>DigestAlgorithm</code> that identifies which hash
|
||||
* function generated the digest.
|
||||
*
|
||||
* @return digest algorithm
|
||||
*/
|
||||
@Override
|
||||
public DigestAlgorithm getAlgorithm() {
|
||||
return algorithm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the digest.
|
||||
*
|
||||
* @return digest
|
||||
*/
|
||||
@Override
|
||||
public byte[] getDigest() {
|
||||
return Arrays.copyOf(this.digest, this.digest.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new Digest with the same attributes as this instance.
|
||||
*
|
||||
* @return a new equivalent Digest
|
||||
*/
|
||||
public Digest asDigest() {
|
||||
return new Digest(algorithm, digest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to reverse the toString method. Returns an OptionalDigest given a String
|
||||
* that was created using an AbstractDigest's toString method.
|
||||
*
|
||||
* @param digest String representation of an AbstractDigest
|
||||
* @return OptionalDigest object recreated from the String passed in
|
||||
*/
|
||||
public static OptionalDigest fromString(final String digest) {
|
||||
return new OptionalDigest(algorithmFromString(digest), digestFromString(digest));
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package hirs.utils.exception;
|
||||
|
||||
/**
|
||||
* This class represents an <code>Exception</code> generated by a
|
||||
* <code>PolicyManager</code>.
|
||||
*/
|
||||
public class PolicyManagerException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 3081536085161873284L;
|
||||
|
||||
/**
|
||||
* Creates a new <code>PolicyManagerException</code> that has the message
|
||||
* <code>msg</code>.
|
||||
*
|
||||
* @param msg
|
||||
* exception message
|
||||
*/
|
||||
public PolicyManagerException(final String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new <code>PolicyManagerException</code> that wraps the given
|
||||
* <code>Throwable</code>.
|
||||
*
|
||||
* @param t
|
||||
* root cause
|
||||
*/
|
||||
public PolicyManagerException(final Throwable t) {
|
||||
super(t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new <code>PolicyManagerException</code> that has the message
|
||||
* <code>msg</code> and wraps the root cause.
|
||||
*
|
||||
* @param msg
|
||||
* exception message
|
||||
* @param t
|
||||
* root cause
|
||||
*/
|
||||
public PolicyManagerException(final String msg, final Throwable t) {
|
||||
super(msg, t);
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package hirs.utils.tpm;
|
||||
|
||||
/**
|
||||
* This class represents an <code>Exception</code> generated by
|
||||
* <code>CreateTPMBaseline</code>.
|
||||
*/
|
||||
public class TPMBaselineGeneratorException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 8850867303391694668L;
|
||||
|
||||
/**
|
||||
* Creates a new <code>CreateTPMBaselineException</code> that has the
|
||||
* message <code>msg</code>.
|
||||
*
|
||||
* @param msg
|
||||
* exception message
|
||||
*/
|
||||
TPMBaselineGeneratorException(final String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new <code>CreateTPMBaselineException</code> that wraps the
|
||||
* given <code>Throwable</code>.
|
||||
*
|
||||
* @param t
|
||||
* root cause
|
||||
*/
|
||||
TPMBaselineGeneratorException(final Throwable t) {
|
||||
super(t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new <code>CreateTPMBaselineException</code> that has the
|
||||
* message <code>msg</code> and wraps the root cause.
|
||||
*
|
||||
* @param msg
|
||||
* exception message
|
||||
* @param t
|
||||
* root cause
|
||||
*/
|
||||
TPMBaselineGeneratorException(final String msg, final Throwable t) {
|
||||
super(msg, t);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,350 @@
|
||||
package hirs.utils.tpm.eventlog;
|
||||
|
||||
import hirs.utils.HexUtils;
|
||||
import hirs.utils.digest.AbstractDigest;
|
||||
import hirs.utils.tpm.eventlog.events.EvConstants;
|
||||
import hirs.utils.tpm.eventlog.uefi.UefiConstants;
|
||||
import lombok.Getter;
|
||||
import org.apache.commons.codec.DecoderException;
|
||||
import org.apache.commons.codec.binary.Hex;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
/**
|
||||
* Class for handling different formats of TCG Event logs.
|
||||
*/
|
||||
public final class TCGEventLog {
|
||||
|
||||
/** Logger. */
|
||||
private static final Logger LOGGER = LogManager.getLogger(TCGEventLog.class);
|
||||
/** Name of the hash algorithm used to process the Event Log, default is SHA256. */
|
||||
@Getter
|
||||
private String algorithm = "TPM_ALG_SHA256";
|
||||
/** Parsed event log array. */
|
||||
private static final int SIG_OFFSET = 32;
|
||||
/** TEV_NO_ACTION signature size. */
|
||||
private static final int SIG_SIZE = 16;
|
||||
/** Initial value for SHA 256 values.*/
|
||||
public static final String INIT_SHA256_LIST = "00000000000000000000000000"
|
||||
+ "00000000000000000000000000000000000000";
|
||||
/** Initial value for SHA 256 values.*/
|
||||
public static final String LOCALITY4_SHA256_LIST = "ffffffffffffffffffffffffff"
|
||||
+ "ffffffffffffffffffffffffffffffffffffff";
|
||||
/** Initial value for SHA 1 values. */
|
||||
public static final String INIT_SHA1_LIST = "0000000000000000000000000000000000000000";
|
||||
/** Initial value for SHA 1 values. */
|
||||
public static final String LOCALITY4_SHA1_LIST = "ffffffffffffffffffffffffffffffffffffffff";
|
||||
/** PFP defined EV_NO_ACTION identifier. */
|
||||
public static final int NO_ACTION_EVENT = 0x00000003;
|
||||
/** String value of SHA1 hash.*/
|
||||
public static final String HASH_STRING = "SHA1";
|
||||
/** String value of SHA256 hash. */
|
||||
public static final String HASH256_STRING = "SHA-256";
|
||||
/** Each PCR bank holds 24 registers. */
|
||||
public static final int PCR_COUNT = 24;
|
||||
/** Locality 4 starts at PCR 17. */
|
||||
public static final int PCR_LOCALITY4_MIN = 17;
|
||||
/** Locality 4 Ends at PCR 23. */
|
||||
public static final int PCR_LOCALITY4_MAX = 23;
|
||||
/** 2 dimensional array holding the PCR values. */
|
||||
private byte[][] pcrList;
|
||||
/** List of parsed events within the log. */
|
||||
private LinkedHashMap<Integer, TpmPcrEvent> eventList = new LinkedHashMap<>();
|
||||
/** Length of PCR. Indicates which hash algorithm is used. */
|
||||
private int pcrLength;
|
||||
/** Name of hash algorithm. */
|
||||
private String hashType;
|
||||
/** Initial PCR Value to use. */
|
||||
private String initValue;
|
||||
/** Initial PcR Value to use for locality 4. */
|
||||
private String initLocalityFourValue;
|
||||
/** Content Output Flag use. */
|
||||
private boolean bContent = false;
|
||||
/** Event Output Flag use. */
|
||||
private boolean bHexEvent = false;
|
||||
/** Event Output Flag use. */
|
||||
private boolean bEvent = false;
|
||||
/** Event Output Flag use. */
|
||||
@Getter
|
||||
private boolean bCryptoAgile = false;
|
||||
|
||||
/**
|
||||
* Default blank object constructor.
|
||||
*/
|
||||
public TCGEventLog() {
|
||||
this.pcrList = new byte[PCR_COUNT][EvConstants.SHA1_LENGTH];
|
||||
initValue = INIT_SHA1_LIST;
|
||||
initLocalityFourValue = LOCALITY4_SHA1_LIST;
|
||||
pcrLength = EvConstants.SHA1_LENGTH;
|
||||
hashType = HASH_STRING;
|
||||
algorithm = "TPM_ALG_SHA1";
|
||||
initPcrList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple constructor for Event Log.
|
||||
* @param rawlog data for the event log file.
|
||||
* @throws java.security.NoSuchAlgorithmException if an unknown algorithm is encountered.
|
||||
* @throws java.security.cert.CertificateException if a certificate in the log cannot be parsed.
|
||||
* @throws java.io.IOException IO Stream if event cannot be parsed.
|
||||
*/
|
||||
public TCGEventLog(final byte[] rawlog)
|
||||
throws CertificateException, NoSuchAlgorithmException, IOException {
|
||||
this(rawlog, false, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default constructor for just the rawlog that'll set up SHA1 Log.
|
||||
* @param rawlog data for the event log file.
|
||||
* @param bEventFlag if true provides human readable event descriptions.
|
||||
* @param bContentFlag if true provides hex output for Content in the description.
|
||||
* @param bHexEventFlag if true provides hex event structure in the description.
|
||||
* @throws java.security.NoSuchAlgorithmException if an unknown algorithm is encountered.
|
||||
* @throws java.security.cert.CertificateException if a certificate in the log cannot be parsed.
|
||||
* @throws java.io.IOException IO Stream if event cannot be parsed.
|
||||
*/
|
||||
public TCGEventLog(final byte[] rawlog, final boolean bEventFlag,
|
||||
final boolean bContentFlag, final boolean bHexEventFlag)
|
||||
throws CertificateException, NoSuchAlgorithmException, IOException {
|
||||
|
||||
bCryptoAgile = isLogCrytoAgile(rawlog);
|
||||
if (bCryptoAgile) {
|
||||
initValue = INIT_SHA256_LIST;
|
||||
initLocalityFourValue = LOCALITY4_SHA256_LIST;
|
||||
algorithm = "TPM_ALG_SHA256";
|
||||
hashType = HASH256_STRING;
|
||||
pcrLength = EvConstants.SHA256_LENGTH;
|
||||
} else {
|
||||
initValue = INIT_SHA1_LIST;
|
||||
initLocalityFourValue = LOCALITY4_SHA1_LIST;
|
||||
hashType = HASH_STRING;
|
||||
algorithm = "TPM_ALG_SHA1";
|
||||
pcrLength = EvConstants.SHA1_LENGTH;
|
||||
}
|
||||
this.pcrList = new byte[PCR_COUNT][pcrLength];
|
||||
int eventNumber = 0;
|
||||
bContent = bContentFlag;
|
||||
bEvent = bEventFlag;
|
||||
bHexEvent = bHexEventFlag;
|
||||
ByteArrayInputStream is = new ByteArrayInputStream(rawlog);
|
||||
// Process the 1st entry as a SHA1 format (per the spec)
|
||||
eventList.put(eventNumber, new TpmPcrEvent1(is, eventNumber++));
|
||||
// put all events into an event list for further processing
|
||||
|
||||
while (is.available() > 0) {
|
||||
if (bCryptoAgile) {
|
||||
eventList.put(eventNumber, new TpmPcrEvent2(is, eventNumber++));
|
||||
} else {
|
||||
eventList.put(eventNumber, new TpmPcrEvent1(is, eventNumber++));
|
||||
}
|
||||
}
|
||||
calculatePcrValues();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method puts blank values in the pcrList.
|
||||
*/
|
||||
private void initPcrList() {
|
||||
try {
|
||||
for (int i = 0; i < PCR_COUNT; i++) {
|
||||
System.arraycopy(Hex.decodeHex(initValue.toCharArray()),
|
||||
0, pcrList[i], 0, pcrLength);
|
||||
}
|
||||
for (int i = PCR_LOCALITY4_MIN; i < PCR_LOCALITY4_MAX; i++) {
|
||||
System.arraycopy(Hex.decodeHex(initLocalityFourValue.toCharArray()),
|
||||
0, pcrList[i], 0, pcrLength);
|
||||
}
|
||||
} catch (DecoderException deEx) {
|
||||
LOGGER.error(deEx);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a TPM baseline using the expected PCR Values.
|
||||
* Expected PCR Values were Calculated from the EventLog (RIM Support file).
|
||||
*
|
||||
* @param name name to call the TPM Baseline
|
||||
* @return whitelist baseline
|
||||
*/
|
||||
// public TpmWhiteListBaseline createTPMBaseline(final String name) {
|
||||
// TpmWhiteListBaseline baseline = new TpmWhiteListBaseline(name);
|
||||
// TPMMeasurementRecord record;
|
||||
// String pcrValue;
|
||||
// for (int i = 0; i < PCR_COUNT; i++) {
|
||||
// if (algorithm.compareToIgnoreCase("TPM_ALG_SHA1") == 0) { // Log Was SHA1 Format
|
||||
// pcrValue = getExpectedPCRValue(i);
|
||||
// byte[] hexValue = HexUtils.hexStringToByteArray(pcrValue);
|
||||
// final Digest hash = new Digest(DigestAlgorithm.SHA1, hexValue);
|
||||
// record = new TPMMeasurementRecord(i, hash);
|
||||
// } else { // Log was Crypto Agile, currently assumes SHA256
|
||||
// pcrValue = getExpectedPCRValue(i);
|
||||
// byte[] hexValue = HexUtils.hexStringToByteArray(pcrValue);
|
||||
// final Digest hash = new Digest(DigestAlgorithm.SHA256, hexValue);
|
||||
// record = new TPMMeasurementRecord(i, hash);
|
||||
// }
|
||||
// baseline.addToBaseline(record);
|
||||
// }
|
||||
// return baseline;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Calculates the "Expected Values for TPM PCRs based upon Event digests in the Event Log.
|
||||
* Uses the algorithm and eventList passed into the constructor,
|
||||
*/
|
||||
private void calculatePcrValues() {
|
||||
byte[] extendedPCR;
|
||||
initPcrList();
|
||||
for (TpmPcrEvent currentEvent : eventList.values()) {
|
||||
if (currentEvent.getPcrIndex() >= 0) { // Ignore NO_EVENTS which can have a PCR=-1
|
||||
try {
|
||||
if (currentEvent.getEventType() != NO_ACTION_EVENT) {
|
||||
// Don't include EV_NO_ACTION event
|
||||
extendedPCR = extendPCR(pcrList[currentEvent.getPcrIndex()],
|
||||
currentEvent.getEventDigest());
|
||||
System.arraycopy(extendedPCR, 0, pcrList[currentEvent.getPcrIndex()],
|
||||
0, currentEvent.getDigestLength());
|
||||
}
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
LOGGER.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends a hash with a hash of new data.
|
||||
*
|
||||
* @param currentValue value to extend
|
||||
* @param newEvent value to extend with
|
||||
* @return new hash resultant hash
|
||||
* @throws java.security.NoSuchAlgorithmException if hash algorithm not supported
|
||||
*/
|
||||
private byte[] extendPCR(final byte[] currentValue, final byte[] newEvent)
|
||||
throws NoSuchAlgorithmException {
|
||||
MessageDigest md = MessageDigest.getInstance(hashType);
|
||||
StringBuilder sb = new StringBuilder(AbstractDigest.SHA512_DIGEST_LENGTH);
|
||||
sb.append(Hex.encodeHexString(currentValue).toCharArray());
|
||||
sb.append(Hex.encodeHexString(newEvent).toCharArray());
|
||||
|
||||
try {
|
||||
md.update(Hex.decodeHex(sb.toString().toCharArray()));
|
||||
} catch (DecoderException deEx) {
|
||||
LOGGER.error(deEx);
|
||||
}
|
||||
return md.digest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all 24 PCR values for display purposes.
|
||||
*
|
||||
* @return Returns an array of strings representing the expected hash values for all 24 PCRs
|
||||
*/
|
||||
public String[] getExpectedPCRValues() {
|
||||
String[] pcrs = new String[PCR_COUNT];
|
||||
for (int i = 0; i < PCR_COUNT; i++) {
|
||||
pcrs[i] = Hex.encodeHexString(pcrList[i]);
|
||||
}
|
||||
return pcrs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of event found in the Event Log.
|
||||
* @return an arraylist of event.
|
||||
*/
|
||||
public Collection<TpmPcrEvent> getEventList() {
|
||||
return eventList.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a specific element of the Event Log that corresponds to the requested
|
||||
* event number.
|
||||
* @param eventNumber specific event to find in the list.
|
||||
* @return TPM Event in the position of the list
|
||||
*/
|
||||
public TpmPcrEvent getEventByNumber(final int eventNumber) {
|
||||
return eventList.get(eventNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a single PCR value given an index (PCR Number).
|
||||
*
|
||||
* @param index pcr index
|
||||
* @return String representing the PCR contents
|
||||
*/
|
||||
public String getExpectedPCRValue(final int index) {
|
||||
return HexUtils.byteArrayToHexString(pcrList[index]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Human readable string representing the contents of the Event Log.
|
||||
* @return Description of the log.
|
||||
*/
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (TpmPcrEvent event : eventList.values()) {
|
||||
sb.append(event.toString(bEvent, bHexEvent, bContent));
|
||||
}
|
||||
sb.append("Event Log processing completed.\n");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Human readable string representing the contents of the Event Log.
|
||||
* @param bEvent flag to set
|
||||
* @param bHexEvent flag to set
|
||||
* @param bContent flag to set
|
||||
* @return Description of the log.
|
||||
*/
|
||||
public String toString(final boolean bEvent,
|
||||
final boolean bHexEvent,
|
||||
final boolean bContent) {
|
||||
this.bEvent = bEvent;
|
||||
this.bHexEvent = bHexEvent;
|
||||
this.bContent = bContent;
|
||||
|
||||
return this.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the TCG Algorithm Registry defined ID for the Digest Algorithm
|
||||
* used in the event log.
|
||||
* @return TCG Defined Algorithm name
|
||||
*/
|
||||
public int getEventLogHashAlgorithmID() {
|
||||
return TcgTpmtHa.tcgAlgStringToId(algorithm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if an event is an EfiSpecIdEvent indicating that the log format is crypto agile.
|
||||
* The EfiSpecIdEvent should be the first event in the TCG TPM Event Log.
|
||||
*
|
||||
* @param log The Event Log
|
||||
* @return true if EfiSpecIDEvent is found and indicates that the format is crypto agile
|
||||
*/
|
||||
private boolean isLogCrytoAgile(final byte[] log) {
|
||||
byte[] eType = new byte[UefiConstants.SIZE_4];
|
||||
System.arraycopy(log, UefiConstants.SIZE_4, eType, 0, UefiConstants.SIZE_4);
|
||||
byte[] eventType = HexUtils.leReverseByte(eType);
|
||||
int eventID = new BigInteger(eventType).intValue();
|
||||
if (eventID != TCGEventLog.NO_ACTION_EVENT) {
|
||||
return false;
|
||||
} // Event Type should be EV_NO_ACTION
|
||||
byte[] signature = new byte[SIG_SIZE];
|
||||
// should be "Spec ID Event03"
|
||||
System.arraycopy(log, SIG_OFFSET, signature, 0, SIG_SIZE);
|
||||
// remove null char
|
||||
String sig = new String(signature, StandardCharsets.UTF_8).substring(0, SIG_SIZE - 1);
|
||||
|
||||
return sig.equals("Spec ID Event03");
|
||||
}
|
||||
}
|
215
HIRS_Utils/src/main/java/hirs/utils/tpm/eventlog/TcgTpmtHa.java
Normal file
215
HIRS_Utils/src/main/java/hirs/utils/tpm/eventlog/TcgTpmtHa.java
Normal file
@ -0,0 +1,215 @@
|
||||
package hirs.utils.tpm.eventlog;
|
||||
|
||||
import hirs.utils.HexUtils;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* Class to for the TCG defined TPMT_HA structure used to support the Crypto Agile Log format.
|
||||
* <p>
|
||||
* typedef struct {
|
||||
* TPMI_ALG_HASH hashAlg;
|
||||
* TPMU_HA digest;
|
||||
* } TPMT_HA;
|
||||
*/
|
||||
public class TcgTpmtHa {
|
||||
/**
|
||||
* TCG Defined Algorithm Identifiers.
|
||||
*/
|
||||
@Getter
|
||||
private int hashAlgId = 0;
|
||||
/**
|
||||
* Length of the hash.
|
||||
*/
|
||||
@Getter
|
||||
private int hashLength = 0;
|
||||
/**
|
||||
* Human readable name of the hash algorithm.
|
||||
*/
|
||||
@Getter
|
||||
private String hashName = "";
|
||||
/**
|
||||
* Hash data.
|
||||
*/
|
||||
@Getter(value = AccessLevel.PROTECTED)
|
||||
private byte[] digest = null;
|
||||
/**
|
||||
* TCG ID for SHA1.
|
||||
*/
|
||||
public static final int TPM_ALG_SHA1 = 0x04;
|
||||
/**
|
||||
* TCG ID for SHA1.
|
||||
*/
|
||||
public static final int TPM_ALG_SHA256 = 0x0B;
|
||||
/**
|
||||
* TCG ID for SHA 384.
|
||||
*/
|
||||
public static final int TPM_ALG_SHA384 = 0x0C;
|
||||
/**
|
||||
* TCG ID for SHA512.
|
||||
*/
|
||||
public static final int TPM_ALG_SHA_512 = 0x0D;
|
||||
/**
|
||||
* TCG ID for Null algorithm.
|
||||
*/
|
||||
public static final int TPM_ALG_NULL = 0x10;
|
||||
/**
|
||||
* TCG ID for SHA1.
|
||||
*/
|
||||
public static final int TPM_ALG_SHA1_LENGTH = 20;
|
||||
/**
|
||||
* TCG ID for SHA1.
|
||||
*/
|
||||
public static final int TPM_ALG_SHA256_LENGTH = 32;
|
||||
/**
|
||||
* TCG ID for SHA 384.
|
||||
*/
|
||||
public static final int TPM_ALG_SHA384_LENGTH = 48;
|
||||
/**
|
||||
* TCG ID for SHA512.
|
||||
*/
|
||||
public static final int TPM_ALG_SHA512_LENGTH = 64;
|
||||
/**
|
||||
* TCG ID for Null algorithm.
|
||||
*/
|
||||
public static final int TPM_ALG_NULL_LENGTH = 0;
|
||||
/**
|
||||
* buffer to hold the structure.
|
||||
*/
|
||||
private byte[] buffer = null;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param is ByteArrayInputStream holding the TcgTPMT_HA structured data
|
||||
* @throws java.io.IOException if TPMT_HA structure cannot be parsed
|
||||
*/
|
||||
public TcgTpmtHa(final ByteArrayInputStream is) throws IOException {
|
||||
byte[] algID = new byte[2];
|
||||
is.read(algID);
|
||||
byte[] rAlgID = HexUtils.leReverseByte(algID);
|
||||
hashAlgId = new BigInteger(rAlgID).intValue();
|
||||
hashName = tcgAlgIdToString(algID[0]);
|
||||
hashLength = tcgAlgLength(algID[0]);
|
||||
digest = new byte[hashLength];
|
||||
is.read(digest);
|
||||
buffer = new byte[algID.length + digest.length];
|
||||
System.arraycopy(algID, 0, buffer, 0, algID.length);
|
||||
System.arraycopy(digest, 0, buffer, algID.length, digest.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the contents of the TPMT_HA structure buffer.
|
||||
*
|
||||
* @return contents of the TPMT_HA structure.
|
||||
*/
|
||||
public byte[] getBuffer() {
|
||||
return java.util.Arrays.copyOf(buffer, buffer.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Readable description of the Algorithm.
|
||||
*
|
||||
* @return Readable Algorithm name
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s hash = %s", hashName, HexUtils.byteArrayToHexString(digest));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hash name via a lookup.
|
||||
* Lookup based upon section 6.3 for the TPM-Rev-2.0-Part-2-Structures.pdf document.
|
||||
* Only hash algorithms found in Table 7 are used.
|
||||
*
|
||||
* @param algId int to convert to string
|
||||
* @return name of the algorithm
|
||||
*/
|
||||
public static String tcgAlgIdToString(final int algId) {
|
||||
String alg;
|
||||
switch (algId) {
|
||||
case TPM_ALG_SHA1:
|
||||
alg = "TPM_ALG_SHA1";
|
||||
break;
|
||||
case TPM_ALG_SHA256:
|
||||
alg = "TPM_ALG_SHA256";
|
||||
break;
|
||||
case TPM_ALG_SHA384:
|
||||
alg = "TPM_ALG_SHA384";
|
||||
break;
|
||||
case TPM_ALG_SHA_512:
|
||||
alg = "TPM_ALG_SHA512";
|
||||
break;
|
||||
case TPM_ALG_NULL:
|
||||
alg = "TPM_ALG_NULL";
|
||||
break;
|
||||
default:
|
||||
alg = "Unknown or invalid Hash";
|
||||
}
|
||||
return alg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the TCG defined ID via a lookup o the TCG Defined Algorithm String.
|
||||
* Lookup based upon section 6.3 for the TPM-Rev-2.0-Part-2-Structures.pdf document.
|
||||
* Only hash algorithms found in Table 7 are used.
|
||||
*
|
||||
* @param algorithm String to convert to an id
|
||||
* @return id of hash algorithm
|
||||
*/
|
||||
public static int tcgAlgStringToId(final String algorithm) {
|
||||
int alg;
|
||||
switch (algorithm) {
|
||||
case "TPM_ALG_SHA1":
|
||||
alg = TPM_ALG_SHA1;
|
||||
break;
|
||||
case "TPM_ALG_SHA256":
|
||||
alg = TPM_ALG_SHA256;
|
||||
break;
|
||||
case "TPM_ALG_SHA384":
|
||||
alg = TPM_ALG_SHA384;
|
||||
break;
|
||||
case "TPM_ALG_SHA512":
|
||||
alg = TPM_ALG_SHA_512;
|
||||
break;
|
||||
case "TPM_ALG_NULL":
|
||||
default:
|
||||
alg = TPM_ALG_NULL;
|
||||
}
|
||||
return alg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the length of a given TPM ALG Identifier.
|
||||
* (lookup based upon section 6.3 for the TPM-Rev-2.0-Part-2-Structures.pdf document)
|
||||
* Only hash algorithms found in Table 7 are used.
|
||||
*
|
||||
* @param algId TCG defined Algorithm identifier
|
||||
* @return length of hash data in bytes
|
||||
*/
|
||||
public static int tcgAlgLength(final int algId) {
|
||||
int length;
|
||||
switch (algId) {
|
||||
case TPM_ALG_SHA1:
|
||||
length = TPM_ALG_SHA1_LENGTH;
|
||||
break;
|
||||
case TPM_ALG_SHA256:
|
||||
length = TPM_ALG_SHA256_LENGTH;
|
||||
break;
|
||||
case TPM_ALG_SHA384:
|
||||
length = TPM_ALG_SHA384_LENGTH;
|
||||
break;
|
||||
case TPM_ALG_SHA_512:
|
||||
length = TPM_ALG_SHA512_LENGTH;
|
||||
break;
|
||||
case TPM_ALG_NULL:
|
||||
default:
|
||||
length = TPM_ALG_NULL_LENGTH;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
}
|
@ -0,0 +1,731 @@
|
||||
package hirs.utils.tpm.eventlog;
|
||||
|
||||
import hirs.utils.HexUtils;
|
||||
import hirs.utils.tpm.eventlog.events.EvCompactHash;
|
||||
import hirs.utils.tpm.eventlog.events.EvConstants;
|
||||
import hirs.utils.tpm.eventlog.events.EvEfiGptPartition;
|
||||
import hirs.utils.tpm.eventlog.events.EvEfiHandoffTable;
|
||||
import hirs.utils.tpm.eventlog.events.EvEfiSpecIdEvent;
|
||||
import hirs.utils.tpm.eventlog.events.EvEventTag;
|
||||
import hirs.utils.tpm.eventlog.events.EvIPL;
|
||||
import hirs.utils.tpm.eventlog.events.EvNoAction;
|
||||
import hirs.utils.tpm.eventlog.events.EvSCrtmContents;
|
||||
import hirs.utils.tpm.eventlog.events.EvSCrtmVersion;
|
||||
import hirs.utils.tpm.eventlog.uefi.UefiConstants;
|
||||
import hirs.utils.tpm.eventlog.events.EvEfiBootServicesApp;
|
||||
import hirs.utils.tpm.eventlog.events.EvPostCode;
|
||||
import hirs.utils.tpm.eventlog.uefi.UefiFirmware;
|
||||
import hirs.utils.tpm.eventlog.uefi.UefiVariable;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.apache.commons.codec.binary.Hex;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Class to process a TCG_PCR_EVENT.
|
||||
* TCG_PCR_EVENT is used when the Event log uses the SHA1 Format as described in the
|
||||
* TCG Platform Firmware Profile (PFP) specification.
|
||||
* typedef struct {
|
||||
* TCG_PCRINDEX PCRIndex; //PCR Index value that either
|
||||
* //matches the PCRIndex of a
|
||||
* //previous extend operation or
|
||||
* //indicates that this Event Log
|
||||
* //entry is not associated with
|
||||
* //an extend operation
|
||||
* TCG_EVENTTYPE EventType; //See Log event types defined in toStrng()
|
||||
* TCG_DIGEST digest; //The hash of the event data
|
||||
* UINT32 EventSize; //Size of the event data
|
||||
* UINT8 Event[EventSize]; //The event data
|
||||
* } TCG_PCR_EVENT;
|
||||
*/
|
||||
public class TpmPcrEvent {
|
||||
private static final Logger LOGGER = LogManager.getLogger(TpmPcrEvent.class);
|
||||
/**
|
||||
* Indent Offset.
|
||||
*/
|
||||
private static final int INDENT_3 = 3;
|
||||
/**
|
||||
* Log format. SHA1=1, Crytpo agile=2.
|
||||
* this can be refactored out
|
||||
*/
|
||||
@Getter @Setter(value = AccessLevel.PROTECTED)
|
||||
private int logFormat = -1;
|
||||
/**
|
||||
* PCR index.
|
||||
*/
|
||||
@Getter
|
||||
private int pcrIndex = -1;
|
||||
/**
|
||||
* Event Type (long).
|
||||
*/
|
||||
@Getter
|
||||
private long eventType = 0;
|
||||
/**
|
||||
* Event digest.
|
||||
*/
|
||||
private byte[] digest = null;
|
||||
/**
|
||||
* Event data (no content).
|
||||
*/
|
||||
private byte[] event;
|
||||
/**
|
||||
* Event content data.
|
||||
*/
|
||||
private byte[] eventContent;
|
||||
/**
|
||||
* TCG Event Log spec version.
|
||||
*/
|
||||
@Getter
|
||||
private String specVersion = "Unknown";
|
||||
/**
|
||||
* TCG Event Log errata version.
|
||||
*/
|
||||
@Getter
|
||||
private String specErrataVersion = "Unknown";
|
||||
/**
|
||||
* Description for toString support.
|
||||
*/
|
||||
private String description = "";
|
||||
/**
|
||||
* Length (in bytes) of a pcr.
|
||||
*/
|
||||
@Setter @Getter
|
||||
private int digestLength = 0;
|
||||
/**
|
||||
* Event hash for SHA1 event logs.
|
||||
*/
|
||||
private byte[] eventDataSha1hash;
|
||||
/**
|
||||
* Event hash for Crypto Agile events.
|
||||
*/
|
||||
private byte[] eventDataSha256hash;
|
||||
private EvPostCode evPostCode;
|
||||
@Setter @Getter
|
||||
private int eventNumber;
|
||||
@Setter @Getter
|
||||
private boolean error = false;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param is ByteArrayInputStream holding the event
|
||||
* @throws java.io.IOException when event can't be parsed
|
||||
*/
|
||||
public TpmPcrEvent(final ByteArrayInputStream is) throws IOException {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the digest from a TCG_PCR_EVENT digest field.
|
||||
* This can be SHA1 for older event structures or any algorithm for newer structure.
|
||||
*
|
||||
* @param digestData cryptographic hash
|
||||
* @param digestLength length of the cryptographic hash
|
||||
*/
|
||||
protected void setEventDigest(final byte[] digestData, final int digestLength) {
|
||||
digest = new byte[digestLength];
|
||||
System.arraycopy(digestData, 0, digest, 0, digestLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the digest from a TCG Event.
|
||||
* This can be SHA1 for older event structures or any algorithm for newer structure.
|
||||
*
|
||||
* @return the digest data for the event
|
||||
*/
|
||||
public byte[] getEventDigest() {
|
||||
byte[] digestCopy = new byte[digestLength];
|
||||
System.arraycopy(digest, 0, digestCopy, 0, this.digestLength);
|
||||
return digestCopy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a hex representation of the event digest.
|
||||
* @return hex string
|
||||
*/
|
||||
public String getEventDigestStr() {
|
||||
return Hex.encodeHexString(this.digest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the event PCR index value from a TCG Event.
|
||||
*
|
||||
* @param eventIndex TCG Event PCR Index as defined in the PFP
|
||||
*/
|
||||
protected void setPcrIndex(final byte[] eventIndex) {
|
||||
pcrIndex = HexUtils.leReverseInt(eventIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the EventType.
|
||||
*
|
||||
* @param type byte array holding the PFP defined log event type
|
||||
*/
|
||||
protected void setEventType(final byte[] type) {
|
||||
eventType = new BigInteger(1, HexUtils.leReverseByte(type)).longValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a formatted string of the type for the event.
|
||||
* @return a string formatted to be human readable
|
||||
*/
|
||||
public String getEventTypeStr() {
|
||||
return String.format("0x%s %s", Long.toHexString(eventType), eventString((int) eventType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a formatted string of the type for the event minus the byte code.
|
||||
* @return a string formatted to be human readable
|
||||
*/
|
||||
public String getEventTypeString() {
|
||||
return eventString((int) eventType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the event data after processing.
|
||||
*
|
||||
* @param eventData The PFP defined event content
|
||||
*/
|
||||
protected void setEventData(final byte[] eventData) {
|
||||
event = new byte[eventData.length];
|
||||
System.arraycopy(eventData, 0, event, 0, eventData.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Event Data (no event content) for the event.
|
||||
* event log format.
|
||||
*
|
||||
* @return byte array holding the event structure.
|
||||
*/
|
||||
public byte[] getEvent() {
|
||||
return Arrays.copyOf(event, event.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the event content after processing.
|
||||
*
|
||||
* @param eventData The PFP defined event content
|
||||
*/
|
||||
protected void setEventContent(final byte[] eventData) {
|
||||
eventContent = new byte[eventData.length];
|
||||
evPostCode = new EvPostCode(eventContent);
|
||||
System.arraycopy(eventData, 0, eventContent, 0, eventData.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the event Content Data (not the entire event structure).
|
||||
*
|
||||
* @return byte array holding the events content field
|
||||
*/
|
||||
public byte[] getEventContent() {
|
||||
return Arrays.copyOf(eventContent, eventContent.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* A getter that parses the content based on the type and returns the proper string
|
||||
* value for the content.
|
||||
* @return an appended string of human readable data
|
||||
*/
|
||||
public String getEventContentStr() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
switch ((int) this.eventType) {
|
||||
case EvConstants.EV_PREBOOT_CERT:
|
||||
sb.append(" EV_PREBOOT_CERT");
|
||||
break;
|
||||
case EvConstants.EV_POST_CODE:
|
||||
sb.append(new EvPostCode(eventContent).toString());
|
||||
break;
|
||||
case EvConstants.EV_UNUSED:
|
||||
break;
|
||||
case EvConstants.EV_NO_ACTION:
|
||||
EvNoAction noAction = null;
|
||||
try {
|
||||
noAction = new EvNoAction(eventContent);
|
||||
sb.append(noAction.toString());
|
||||
if (noAction.isSpecIDEvent()) {
|
||||
// this should be in the constructor
|
||||
EvEfiSpecIdEvent specID = noAction.getSpecIDEvent();
|
||||
specVersion = String.format("%s.%s",
|
||||
specID.getVersionMajor(),
|
||||
specID.getVersionMinor());
|
||||
specErrataVersion = specID.getErrata();
|
||||
}
|
||||
} catch (UnsupportedEncodingException ueEx) {
|
||||
LOGGER.error(ueEx);
|
||||
sb.append(ueEx.toString());
|
||||
}
|
||||
break;
|
||||
case EvConstants.EV_SEPARATOR:
|
||||
if (EvPostCode.isAscii(eventContent)
|
||||
&& !this.isBlank(eventContent)) {
|
||||
sb.append(String.format("Separator event content = %s",
|
||||
new String(eventContent, StandardCharsets.UTF_8)));
|
||||
}
|
||||
break;
|
||||
case EvConstants.EV_EVENT_TAG:
|
||||
sb.append(new EvEventTag(eventContent).toString());
|
||||
break;
|
||||
case EvConstants.EV_S_CRTM_CONTENTS:
|
||||
sb.append(new EvSCrtmContents(eventContent).toString());
|
||||
break;
|
||||
case EvConstants.EV_S_CRTM_VERSION:
|
||||
try {
|
||||
sb.append(new EvSCrtmVersion(eventContent).toString());
|
||||
} catch (UnsupportedEncodingException ueEx) {
|
||||
LOGGER.error(ueEx);
|
||||
sb.append(ueEx.toString());
|
||||
}
|
||||
break;
|
||||
case EvConstants.EV_CPU_MICROCODE:
|
||||
case EvConstants.EV_PLATFORM_CONFIG_FLAGS:
|
||||
case EvConstants.EV_TABLE_OF_DEVICES:
|
||||
break;
|
||||
case EvConstants.EV_COMPACT_HASH:
|
||||
try {
|
||||
sb.append(new EvCompactHash(eventContent).toString());
|
||||
} catch (UnsupportedEncodingException ueEx) {
|
||||
LOGGER.error(ueEx);
|
||||
sb.append(ueEx.toString());
|
||||
}
|
||||
break;
|
||||
case EvConstants.EV_IPL:
|
||||
sb.append(new EvIPL(eventContent).toString());
|
||||
break;
|
||||
case EvConstants.EV_IPL_PARTITION_DATA:
|
||||
case EvConstants.EV_NONHOST_CODE:
|
||||
case EvConstants.EV_NONHOST_CONFIG:
|
||||
case EvConstants.EV_NONHOST_INFO:
|
||||
case EvConstants.EV_EV_OMIT_BOOT_DEVICES_EVENTS:
|
||||
case EvConstants.EV_EFI_EVENT_BASE:
|
||||
break;
|
||||
case EvConstants.EV_EFI_VARIABLE_DRIVER_CONFIG:
|
||||
UefiVariable efiVar = null;
|
||||
try {
|
||||
efiVar = new UefiVariable(eventContent);
|
||||
String efiVarDescription = efiVar.toString().replace("\n", "\n ");
|
||||
sb.append(efiVarDescription.substring(0,
|
||||
efiVarDescription.length() - INDENT_3));
|
||||
} catch (CertificateException cEx) {
|
||||
LOGGER.error(cEx);
|
||||
sb.append(cEx.toString());
|
||||
} catch (NoSuchAlgorithmException noSaEx) {
|
||||
LOGGER.error(noSaEx);
|
||||
sb.append(noSaEx.toString());
|
||||
} catch (IOException ioEx) {
|
||||
LOGGER.error(ioEx);
|
||||
sb.append(ioEx.toString());
|
||||
}
|
||||
break;
|
||||
case EvConstants.EV_EFI_VARIABLE_BOOT:
|
||||
case EvConstants.EV_EFI_VARIABLE_AUTHORITY:
|
||||
try {
|
||||
sb.append(new UefiVariable(eventContent).toString());
|
||||
} catch (CertificateException cEx) {
|
||||
LOGGER.error(cEx);
|
||||
sb.append(cEx.toString());
|
||||
} catch (NoSuchAlgorithmException noSaEx) {
|
||||
LOGGER.error(noSaEx);
|
||||
sb.append(noSaEx.toString());
|
||||
} catch (IOException ioEx) {
|
||||
LOGGER.error(ioEx);
|
||||
sb.append(ioEx.toString());
|
||||
}
|
||||
break;
|
||||
case EvConstants.EV_EFI_BOOT_SERVICES_APPLICATION:
|
||||
case EvConstants.EV_EFI_BOOT_SERVICES_DRIVER: // same as EV_EFI_BOOT_SERVICES_APP
|
||||
try {
|
||||
sb.append(new EvEfiBootServicesApp(eventContent).toString());
|
||||
} catch (UnsupportedEncodingException ueEx) {
|
||||
LOGGER.error(ueEx);
|
||||
sb.append(ueEx.toString());
|
||||
}
|
||||
break;
|
||||
case EvConstants.EV_EFI_RUNTIME_SERVICES_DRIVER:
|
||||
break;
|
||||
case EvConstants.EV_EFI_GPT_EVENT:
|
||||
try {
|
||||
sb.append(new EvEfiGptPartition(eventContent).toString());
|
||||
} catch (UnsupportedEncodingException ueEx) {
|
||||
LOGGER.error(ueEx);
|
||||
sb.append(ueEx.toString());
|
||||
}
|
||||
break;
|
||||
case EvConstants.EV_EFI_ACTION:
|
||||
case EvConstants.EV_ACTION:
|
||||
sb.append(new String(eventContent, StandardCharsets.UTF_8));
|
||||
break;
|
||||
case EvConstants.EV_EFI_PLATFORM_FIRMWARE_BLOB:
|
||||
sb.append(new UefiFirmware(eventContent).toString());
|
||||
break;
|
||||
case EvConstants.EV_EFI_HANDOFF_TABLES:
|
||||
sb.append(new EvEfiHandoffTable(eventContent).toString());
|
||||
break;
|
||||
case EvConstants.EV_EFI_HCRTM_EVENT:
|
||||
break;
|
||||
default:
|
||||
sb.append("Unknown Event found\n");
|
||||
}
|
||||
|
||||
return cleanTextContent(sb.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the event content and creates a human readable description of each event.
|
||||
*
|
||||
* @param event the byte array holding the event data.
|
||||
* @param eventContent the byte array holding the event content.
|
||||
* @param eventNumber event position within the event log.
|
||||
* @param hashName name of the hash algorithm used by the event log
|
||||
* @return String description of the event.
|
||||
* @throws java.security.cert.CertificateException if the event contains an event that cannot be processed.
|
||||
* @throws java.security.NoSuchAlgorithmException if an event contains an unsupported algorithm.
|
||||
* @throws java.io.IOException if the event cannot be parsed.
|
||||
*/
|
||||
public String processEvent(final byte[] event, final byte[] eventContent, final int eventNumber,
|
||||
final String hashName)
|
||||
throws CertificateException, NoSuchAlgorithmException, IOException {
|
||||
int eventID = (int) eventType;
|
||||
this.eventNumber = eventNumber;
|
||||
description += "Event# " + eventNumber + ": ";
|
||||
description += "Index PCR[" + getPcrIndex() + "]\n";
|
||||
description += "Event Type: 0x" + Long.toHexString(eventType) + " " + eventString(eventID);
|
||||
description += "\n";
|
||||
if (hashName.compareToIgnoreCase("TPM_ALG_SHA1") == 0) { // Digest
|
||||
description += "digest (SHA-1): " + Hex.encodeHexString(this.digest);
|
||||
} else if (hashName.compareToIgnoreCase("TPM_ALG_SHA256") == 0) { // Digest
|
||||
description += "digest (SHA256): " + Hex.encodeHexString(this.digest);
|
||||
} else if (hashName.compareToIgnoreCase("TPM_ALG_SHA384") == 0) { // Digest
|
||||
description += "digest (SHA384): " + Hex.encodeHexString(this.digest);
|
||||
} else if (hashName.compareToIgnoreCase("TPM_ALG_SHA512") == 0) { // Digest
|
||||
description += "digest (SHA512): " + Hex.encodeHexString(this.digest);
|
||||
} else {
|
||||
description += "Unsupported Hash Algorithm encoutered";
|
||||
}
|
||||
if (eventID != UefiConstants.SIZE_4) {
|
||||
description += "\n";
|
||||
}
|
||||
// Calculate both the SHA1 and SHA256 on the event since this will equal the digest
|
||||
// field of about half the log messages.
|
||||
MessageDigest md1 = MessageDigest.getInstance("SHA-1");
|
||||
md1.update(event);
|
||||
eventDataSha1hash = md1.digest();
|
||||
MessageDigest md2 = MessageDigest.getInstance("SHA-256");
|
||||
md2.update(event);
|
||||
eventDataSha256hash = md2.digest();
|
||||
|
||||
switch (eventID) {
|
||||
case EvConstants.EV_PREBOOT_CERT:
|
||||
description += " EV_PREBOOT_CERT" + "\n";
|
||||
break;
|
||||
case EvConstants.EV_POST_CODE:
|
||||
EvPostCode postCode = new EvPostCode(eventContent);
|
||||
description += "Event Content:\n" + postCode.toString();
|
||||
break;
|
||||
case EvConstants.EV_UNUSED:
|
||||
break;
|
||||
case EvConstants.EV_NO_ACTION:
|
||||
EvNoAction noAction = new EvNoAction(eventContent);
|
||||
description += "Event Content:\n" + noAction.toString();
|
||||
if (noAction.isSpecIDEvent()) {
|
||||
EvEfiSpecIdEvent specID = noAction.getSpecIDEvent();
|
||||
specVersion = specID.getVersionMajor() + "." + specID.getVersionMinor();
|
||||
specErrataVersion = specID.getErrata();
|
||||
}
|
||||
break;
|
||||
case EvConstants.EV_SEPARATOR:
|
||||
if (EvPostCode.isAscii(eventContent)) {
|
||||
String separatorEventData = new String(eventContent, StandardCharsets.UTF_8);
|
||||
if (!this.isBlank(eventContent)) {
|
||||
description += "Separator event content = " + separatorEventData;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EvConstants.EV_ACTION:
|
||||
description += "Event Content:\n"
|
||||
+ new String(eventContent, StandardCharsets.UTF_8);
|
||||
break;
|
||||
case EvConstants.EV_EVENT_TAG:
|
||||
EvEventTag eventTag = new EvEventTag(eventContent);
|
||||
description += eventTag.toString();
|
||||
break;
|
||||
case EvConstants.EV_S_CRTM_CONTENTS:
|
||||
EvSCrtmContents sCrtmContents = new EvSCrtmContents(eventContent);
|
||||
description += "Event Content:\n " + sCrtmContents.toString();
|
||||
break;
|
||||
case EvConstants.EV_S_CRTM_VERSION:
|
||||
EvSCrtmVersion sCrtmVersion = new EvSCrtmVersion(eventContent);
|
||||
description += "Event Content:\n" + sCrtmVersion.toString();
|
||||
break;
|
||||
case EvConstants.EV_CPU_MICROCODE:
|
||||
break;
|
||||
case EvConstants.EV_PLATFORM_CONFIG_FLAGS:
|
||||
break;
|
||||
case EvConstants.EV_TABLE_OF_DEVICES:
|
||||
break;
|
||||
case EvConstants.EV_COMPACT_HASH:
|
||||
EvCompactHash compactHash = new EvCompactHash(eventContent);
|
||||
description += "Event Content:\n" + compactHash.toString();
|
||||
break;
|
||||
case EvConstants.EV_IPL:
|
||||
EvIPL ipl = new EvIPL(eventContent);
|
||||
description += "Event Content:\n" + ipl.toString();
|
||||
break;
|
||||
case EvConstants.EV_IPL_PARTITION_DATA:
|
||||
break;
|
||||
case EvConstants.EV_NONHOST_CODE:
|
||||
break;
|
||||
case EvConstants.EV_NONHOST_CONFIG:
|
||||
break;
|
||||
case EvConstants.EV_NONHOST_INFO:
|
||||
break;
|
||||
case EvConstants.EV_EV_OMIT_BOOT_DEVICES_EVENTS:
|
||||
break;
|
||||
case EvConstants.EV_EFI_EVENT_BASE:
|
||||
break;
|
||||
case EvConstants.EV_EFI_VARIABLE_DRIVER_CONFIG:
|
||||
UefiVariable efiVar = new UefiVariable(eventContent);
|
||||
String efiVarDescription = efiVar.toString().replace("\n", "\n ");
|
||||
description += "Event Content:\n " + efiVarDescription.substring(0,
|
||||
efiVarDescription.length() - INDENT_3);
|
||||
break;
|
||||
case EvConstants.EV_EFI_VARIABLE_BOOT:
|
||||
description += "Event Content:\n" + new UefiVariable(eventContent).toString();
|
||||
break;
|
||||
case EvConstants.EV_EFI_BOOT_SERVICES_APPLICATION:
|
||||
EvEfiBootServicesApp bootServices = new EvEfiBootServicesApp(eventContent);
|
||||
description += "Event Content:\n" + bootServices.toString();
|
||||
break;
|
||||
case EvConstants.EV_EFI_BOOT_SERVICES_DRIVER: // same as EV_EFI_BOOT_SERVICES_APP
|
||||
EvEfiBootServicesApp bootDriver = new EvEfiBootServicesApp(eventContent);
|
||||
description += "Event Content:\n" + bootDriver.toString();
|
||||
break;
|
||||
case EvConstants.EV_EFI_RUNTIME_SERVICES_DRIVER:
|
||||
break;
|
||||
case EvConstants.EV_EFI_GPT_EVENT:
|
||||
description += "Event Content:\n" + new EvEfiGptPartition(eventContent).toString();
|
||||
break;
|
||||
case EvConstants.EV_EFI_ACTION:
|
||||
description += new String(eventContent, StandardCharsets.UTF_8);
|
||||
break;
|
||||
case EvConstants.EV_EFI_PLATFORM_FIRMWARE_BLOB:
|
||||
description += "Event Content:\n"
|
||||
+ new UefiFirmware(eventContent).toString();
|
||||
break;
|
||||
case EvConstants.EV_EFI_HANDOFF_TABLES:
|
||||
EvEfiHandoffTable efiTable = new EvEfiHandoffTable(eventContent);
|
||||
description += "Event Content:\n" + efiTable.toString();
|
||||
break;
|
||||
case EvConstants.EV_EFI_HCRTM_EVENT:
|
||||
break;
|
||||
case EvConstants.EV_EFI_VARIABLE_AUTHORITY:
|
||||
description += "Event Content:\n" + new UefiVariable(eventContent).toString();
|
||||
break;
|
||||
default:
|
||||
description += " Unknown Event found" + "\n";
|
||||
}
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the Event ID into a String As defined in the TCG PC Client FW Profile.
|
||||
* Event IDs have values larger than an integer,so a Long is used hold the value.
|
||||
*
|
||||
* @param event the event id.
|
||||
* @return TCG defined String that represents the event id
|
||||
*/
|
||||
private static String eventString(final long event) {
|
||||
|
||||
if (event == EvConstants.EV_PREBOOT_CERT) {
|
||||
return "EV_PREBOOT_CERT";
|
||||
} else if (event == EvConstants.EV_POST_CODE) {
|
||||
return "EV_POST_CODE";
|
||||
} else if (event == EvConstants.EV_UNUSED) {
|
||||
return "EV_Unused";
|
||||
} else if (event == EvConstants.EV_NO_ACTION) {
|
||||
return "EV_NO_ACTION";
|
||||
} else if (event == EvConstants.EV_SEPARATOR) {
|
||||
return "EV_SEPARATOR";
|
||||
} else if (event == EvConstants.EV_ACTION) {
|
||||
return "EV_ACTION";
|
||||
} else if (event == EvConstants.EV_EVENT_TAG) {
|
||||
return "EV_EVENT_TAG";
|
||||
} else if (event == EvConstants.EV_S_CRTM_CONTENTS) {
|
||||
return "EV_S_CRTM_CONTENTS";
|
||||
} else if (event == EvConstants.EV_S_CRTM_VERSION) {
|
||||
return "EV_S_CRTM_VERSION";
|
||||
} else if (event == EvConstants.EV_CPU_MICROCODE) {
|
||||
return "EV_CPU_MICROCODE";
|
||||
} else if (event == EvConstants.EV_PLATFORM_CONFIG_FLAGS) {
|
||||
return "EV_PLATFORM_CONFIG_FLAGS ";
|
||||
} else if (event == EvConstants.EV_TABLE_OF_DEVICES) {
|
||||
return "EV_TABLE_OF_DEVICES";
|
||||
} else if (event == EvConstants.EV_COMPACT_HASH) {
|
||||
return "EV_COMPACT_HASH";
|
||||
} else if (event == EvConstants.EV_IPL) {
|
||||
return "EV_IPL";
|
||||
} else if (event == EvConstants.EV_IPL_PARTITION_DATA) {
|
||||
return "EV_IPL_PARTITION_DATA";
|
||||
} else if (event == EvConstants.EV_NONHOST_CODE) {
|
||||
return "EV_NONHOST_CODE";
|
||||
} else if (event == EvConstants.EV_NONHOST_CONFIG) {
|
||||
return "EV_NONHOST_CONFIG";
|
||||
} else if (event == EvConstants.EV_NONHOST_INFO) {
|
||||
return "EV_NONHOST_INFO";
|
||||
} else if (event == EvConstants.EV_EV_OMIT_BOOT_DEVICES_EVENTS) {
|
||||
return "EV_EV_OMIT_BOOT_DEVICES_EVENTS";
|
||||
} else if (event == EvConstants.EV_EFI_EVENT_BASE) {
|
||||
return "EV_EFI_EVENT_BASE";
|
||||
} else if (event == EvConstants.EV_EFI_VARIABLE_DRIVER_CONFIG) {
|
||||
return "EV_EFI_VARIABLE_DRIVER_CONFIG";
|
||||
} else if (event == EvConstants.EV_EFI_VARIABLE_BOOT) {
|
||||
return "EV_EFI_VARIABLE_BOOT";
|
||||
} else if (event == EvConstants.EV_EFI_BOOT_SERVICES_APPLICATION) {
|
||||
return "EV_EFI_BOOT_SERVICES_APPLICATION";
|
||||
} else if (event == EvConstants.EV_EFI_BOOT_SERVICES_DRIVER) {
|
||||
return "EV_EFI_BOOT_SERVICES_DRIVER";
|
||||
} else if (event == EvConstants.EV_EFI_RUNTIME_SERVICES_DRIVER) {
|
||||
return "EV_EFI_RUNTIME_SERVICES_DRIVER";
|
||||
} else if (event == EvConstants.EV_EFI_GPT_EVENT) {
|
||||
return "EV_EFI_GPT_EVENT";
|
||||
} else if (event == EvConstants.EV_EFI_ACTION) {
|
||||
return "EV_EFI_ACTION";
|
||||
} else if (event == EvConstants.EV_EFI_PLATFORM_FIRMWARE_BLOB) {
|
||||
return "EV_EFI_PLATFORM_FIRMWARE_BLOB";
|
||||
} else if (event == EvConstants.EV_EFI_HANDOFF_TABLES) {
|
||||
return "EV_EFI_HANDOFF_TABLES";
|
||||
} else if (event == EvConstants.EV_EFI_HCRTM_EVENT) {
|
||||
return "EV_EFI_HCRTM_EVENT";
|
||||
} else if (event == EvConstants.EV_EFI_VARIABLE_AUTHORITY) {
|
||||
return "EV_EFI_VARIABLE_AUTHORITY";
|
||||
} else {
|
||||
return "Unknown Event ID " + event + " encountered";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Human readable output of a check of input against the current event hash.
|
||||
*
|
||||
* @return human readable string.
|
||||
*/
|
||||
private String eventHashCheck() {
|
||||
String result = "";
|
||||
if (logFormat == 1) {
|
||||
if (Arrays.equals(this.digest, eventDataSha1hash)) {
|
||||
result
|
||||
+= "Event digest matched hash of the event data " + "\n";
|
||||
} else {
|
||||
result += "Event digest DID NOT match the hash of the event data :"
|
||||
+ Hex.encodeHexString(getEventDigest()) + "\n";
|
||||
}
|
||||
} else {
|
||||
if (Arrays.equals(this.digest, eventDataSha256hash)) {
|
||||
result += "Event digest matched hash of the event data " + "\n";
|
||||
} else {
|
||||
result += "Event digest DID NOT match the hash of the event data :"
|
||||
+ Hex.encodeHexString(getEventDigest()) + "\n";
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method takes in an event and compares the hashes to verify that they match.
|
||||
* @param tpmPcrEvent an event to match.
|
||||
* @return true if the event # matches and the hash is correct.
|
||||
*/
|
||||
public boolean eventCompare(final TpmPcrEvent tpmPcrEvent) {
|
||||
if (tpmPcrEvent.getPcrIndex() != this.getPcrIndex()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Arrays.equals(this.digest, tpmPcrEvent.getEventDigest());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a byte array for all zeros.
|
||||
*
|
||||
* @param array holds data to check.
|
||||
* @return true of all zeros are found.
|
||||
*/
|
||||
public boolean isBlank(final byte[] array) {
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
if (array[i] != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Human readable string representing the contents of the Event Log.
|
||||
*
|
||||
* @return Description of the log.
|
||||
*/
|
||||
public String toString() {
|
||||
return description + "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Human readable string representing the contents of the Event Log.
|
||||
*
|
||||
* @param bEvent event Flag.
|
||||
* @param bContent content flag.
|
||||
* @param bHexEvent hex event flag.
|
||||
* @return Description of the log.
|
||||
*/
|
||||
public String toString(final boolean bEvent, final boolean bContent, final boolean bHexEvent) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (bEvent) {
|
||||
sb.append(description);
|
||||
}
|
||||
if (bHexEvent) {
|
||||
if (bEvent || bContent) {
|
||||
sb.append("\n");
|
||||
}
|
||||
byte[] eventData = getEvent();
|
||||
sb.append("Event (Hex no Content) (" + eventData.length + " bytes): "
|
||||
+ Hex.encodeHexString(eventData));
|
||||
}
|
||||
if (bContent) {
|
||||
byte[] evContent = getEventContent();
|
||||
if (bEvent) {
|
||||
sb.append("\n");
|
||||
}
|
||||
sb.append("Event content (Hex) (" + evContent.length + " bytes): "
|
||||
+ Hex.encodeHexString(evContent));
|
||||
}
|
||||
return sb.toString() + "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove bad visual value text.
|
||||
* @param text content to operate over.
|
||||
* @return cleared string
|
||||
*/
|
||||
public String cleanTextContent(final String text) {
|
||||
String result;
|
||||
// strips off all non-ASCII characters
|
||||
result = text.replaceAll("[^\\x00-\\x7F]", "");
|
||||
|
||||
// erases all the ASCII control characters
|
||||
result = result.replaceAll("[\\p{Cntrl}&&[^\r\n\t]]", "");
|
||||
|
||||
// removes non-printable characters from Unicode
|
||||
result = result.replaceAll("\\p{C}", "");
|
||||
|
||||
return result.trim();
|
||||
}
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
package hirs.utils.tpm.eventlog;
|
||||
|
||||
import hirs.utils.HexUtils;
|
||||
import hirs.utils.tpm.eventlog.events.EvConstants;
|
||||
import hirs.utils.tpm.eventlog.uefi.UefiConstants;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateException;
|
||||
|
||||
/**
|
||||
* Class to process a TCG_PCR_EVENT.
|
||||
* TCG_PCR_EVENT is used when the Event log uses the SHA1 Format as described in the
|
||||
* TCG Platform Firmware Profile specification.
|
||||
* typedef struct {
|
||||
* UINT32 PCRIndex; //PCR Index value that either
|
||||
* //matches the PCRIndex of a
|
||||
* //previous extend operation or
|
||||
* //indicates that this Event Log
|
||||
* //entry is not associated with
|
||||
* //an extend operation
|
||||
* UINT32 EventType; //See Log event types
|
||||
* BYTE digest[20]; //The SHA1 hash of the event data
|
||||
* UINT32 EventSize; //Size of the event data
|
||||
* UINT8 Event[1]; //
|
||||
* } TCG_PCR_EVENT; //The event data structure to be added
|
||||
*/
|
||||
public class TpmPcrEvent1 extends TpmPcrEvent {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param is ByteArrayInputStream holding the TCG Log event.
|
||||
* @param eventNumber event position within the event log.
|
||||
* @throws java.io.IOException if an error occurs in parsing the event.
|
||||
* @throws java.security.NoSuchAlgorithmException if an undefined algorithm is encountered.
|
||||
* @throws java.security.cert.CertificateException If a certificate within an event can't be processed.
|
||||
*/
|
||||
public TpmPcrEvent1(final ByteArrayInputStream is, final int eventNumber)
|
||||
throws IOException, CertificateException, NoSuchAlgorithmException {
|
||||
super(is);
|
||||
setDigestLength(EvConstants.SHA1_LENGTH);
|
||||
setLogFormat(1);
|
||||
/** Event data. */
|
||||
byte[] event = null;
|
||||
byte[] rawIndex = new byte[UefiConstants.SIZE_4];
|
||||
byte[] rawType = new byte[UefiConstants.SIZE_4];
|
||||
byte[] rawEventSize = new byte[UefiConstants.SIZE_4];
|
||||
byte[] eventDigest = new byte[EvConstants.SHA1_LENGTH];
|
||||
byte[] eventContent = null;
|
||||
int digestSize = EvConstants.SHA1_LENGTH;
|
||||
int eventSize = 0;
|
||||
String hashName = "TPM_ALG_SHA1";
|
||||
if (is.available() > UefiConstants.SIZE_32) {
|
||||
is.read(rawIndex);
|
||||
setPcrIndex(rawIndex);
|
||||
is.read(rawType);
|
||||
setEventType(rawType);
|
||||
is.read(eventDigest);
|
||||
setEventDigest(eventDigest, digestSize);
|
||||
is.read(rawEventSize);
|
||||
eventSize = HexUtils.leReverseInt(rawEventSize);
|
||||
eventContent = new byte[eventSize];
|
||||
is.read(eventContent);
|
||||
setEventContent(eventContent);
|
||||
// copy entire event into a byte array for processing
|
||||
int eventLength = rawIndex.length + rawType.length + eventDigest.length
|
||||
+ rawEventSize.length;
|
||||
int offset = 0;
|
||||
event = new byte[eventLength];
|
||||
System.arraycopy(rawIndex, 0, event, offset, rawIndex.length);
|
||||
offset += rawIndex.length;
|
||||
System.arraycopy(rawType, 0, event, offset, rawType.length);
|
||||
offset += rawType.length;
|
||||
System.arraycopy(eventDigest, 0, event, offset, eventDigest.length);
|
||||
offset += eventDigest.length;
|
||||
System.arraycopy(rawEventSize, 0, event, offset, rawEventSize.length);
|
||||
offset += rawEventSize.length;
|
||||
setEventData(event);
|
||||
//System.arraycopy(eventContent, 0, event, offset, eventContent.length);
|
||||
this.processEvent(event, eventContent, eventNumber, hashName);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,135 @@
|
||||
package hirs.utils.tpm.eventlog;
|
||||
|
||||
import hirs.utils.HexUtils;
|
||||
import hirs.utils.tpm.eventlog.events.EvConstants;
|
||||
import hirs.utils.tpm.eventlog.uefi.UefiConstants;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Class to process a TCG_PCR_EVENT2 which is used
|
||||
* when the Event log uses the Crypto Agile (SHA256) format as described in the
|
||||
* TCG Platform Firmware Profile specification.
|
||||
* This class will only process SHA-256 digests.
|
||||
* typedef struct {
|
||||
* UINT32 PCRIndex; //PCR Index value that either
|
||||
* //matches the PCRIndex of a
|
||||
* //previous extend operation or
|
||||
* //indicates that this Event Log
|
||||
* //entry is not associated with
|
||||
* //an extend operation
|
||||
* UINT32 EventType; //See Log event types
|
||||
* TPML_DIGEST_VALUES digest; //The hash of the event data
|
||||
* UINT32 EventSize; //Size of the event data
|
||||
* BYTE Event[1]; //The event data
|
||||
* } TCG_PCR_EVENT2; //The event data structure to be added
|
||||
* typedef struct {
|
||||
* UINT32 count;
|
||||
* TPMT_HA digests[HASH_COUNT];
|
||||
* } TPML_DIGEST_VALUES;
|
||||
* typedef struct {
|
||||
* TPMI_ALG_HASH hashAlg;
|
||||
* TPMU_HA digest;
|
||||
* } TPMT_HA;
|
||||
* typedef union {
|
||||
* BYTE sha1[SHA1_DIGEST_SIZE];
|
||||
* BYTE sha256[SHA256_DIGEST_SIZE];
|
||||
* BYTE sha384[SHA384_DIGEST_SIZE];
|
||||
* BYTE sha512[SHA512_DIGEST_SIZE];
|
||||
* } TPMU_HA;
|
||||
* define SHA1_DIGEST_SIZE 20
|
||||
* define SHA256_DIGEST_SIZE 32
|
||||
* define SHA384_DIGEST_SIZE 48
|
||||
* define SHA512_DIGEST_SIZE 64
|
||||
* typedef TPM_ALG_ID TPMI_ALG_HASH;
|
||||
* typedef UINT16 TPM_ALG_ID;
|
||||
* define TPM_ALG_SHA1 (TPM_ALG_ID)(0x0004)
|
||||
* define TPM_ALG_SHA256 (TPM_ALG_ID)(0x000B)
|
||||
* define TPM_ALG_SHA384 (TPM_ALG_ID)(0x000C)
|
||||
* define TPM_ALG_SHA512 (TPM_ALG_ID)(0x000D)
|
||||
*/
|
||||
public class TpmPcrEvent2 extends TpmPcrEvent {
|
||||
/**
|
||||
* algorithms found.
|
||||
*/
|
||||
private int algCount = 0;
|
||||
|
||||
/**
|
||||
* list of digests.
|
||||
*/
|
||||
private ArrayList<TcgTpmtHa> hashList = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param is ByteArrayInputStream holding the TCG Log event
|
||||
* @param eventNumber event position within the event log.
|
||||
* @throws java.io.IOException if an error occurs in parsing the event
|
||||
* @throws java.security.NoSuchAlgorithmException if an undefined algorithm is encountered.
|
||||
* @throws java.security.cert.CertificateException If a certificate within an event can't be processed.
|
||||
*/
|
||||
public TpmPcrEvent2(final ByteArrayInputStream is, final int eventNumber)
|
||||
throws IOException, CertificateException, NoSuchAlgorithmException {
|
||||
super(is);
|
||||
setDigestLength(EvConstants.SHA256_LENGTH);
|
||||
setLogFormat(2);
|
||||
/** Event data. */
|
||||
int eventDigestLength = 0;
|
||||
String hashName = "";
|
||||
byte[] event;
|
||||
byte[] rawIndex = new byte[UefiConstants.SIZE_4];
|
||||
byte[] algCountBytes = new byte[UefiConstants.SIZE_4];
|
||||
byte[] rawType = new byte[UefiConstants.SIZE_4];
|
||||
byte[] rawEventSize = new byte[UefiConstants.SIZE_4];
|
||||
byte[] eventDigest = null;
|
||||
byte[] eventContent = null;
|
||||
TcgTpmtHa hashAlg = null;
|
||||
int eventSize = 0;
|
||||
//TCG_PCR_EVENT2
|
||||
if (is.available() > UefiConstants.SIZE_32) {
|
||||
is.read(rawIndex);
|
||||
setPcrIndex(rawIndex);
|
||||
is.read(rawType);
|
||||
setEventType(rawType);
|
||||
// TPML_DIGEST_VALUES
|
||||
is.read(algCountBytes);
|
||||
algCount = HexUtils.leReverseInt(algCountBytes);
|
||||
// Process TPMT_HA,
|
||||
for (int i = 0; i < algCount; i++) {
|
||||
hashAlg = new TcgTpmtHa(is);
|
||||
hashName = hashAlg.getHashName();
|
||||
hashList.add(hashAlg);
|
||||
eventDigest = new byte[hashAlg.getHashLength()];
|
||||
setEventDigest(hashAlg.getDigest(), hashAlg.getHashLength());
|
||||
}
|
||||
is.read(rawEventSize);
|
||||
eventSize = HexUtils.leReverseInt(rawEventSize);
|
||||
eventContent = new byte[eventSize];
|
||||
is.read(eventContent);
|
||||
setEventContent(eventContent);
|
||||
int eventLength = rawIndex.length + rawType.length + eventDigest.length
|
||||
+ rawEventSize.length;
|
||||
int offset = 0;
|
||||
for (TcgTpmtHa hash : hashList) {
|
||||
eventLength += hash.getBuffer().length;
|
||||
}
|
||||
event = new byte[eventLength];
|
||||
System.arraycopy(rawIndex, 0, event, offset, rawIndex.length);
|
||||
offset += rawIndex.length;
|
||||
System.arraycopy(rawType, 0, event, offset, rawType.length);
|
||||
offset += rawType.length;
|
||||
System.arraycopy(eventDigest, 0, event, offset, eventDigest.length);
|
||||
offset += eventDigest.length;
|
||||
System.arraycopy(rawEventSize, 0, event, offset, rawEventSize.length);
|
||||
offset += rawEventSize.length;
|
||||
//System.arraycopy(eventContent, 0, event, offset, eventContent.length);
|
||||
setEventData(event);
|
||||
//setDigestLength(eventDigestLength);
|
||||
this.processEvent(event, eventContent, eventNumber, hashName);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package hirs.utils.tpm.eventlog.events;
|
||||
|
||||
import hirs.utils.HexUtils;
|
||||
import hirs.utils.tpm.eventlog.uefi.UefiConstants;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* Class to process the EV_COMPACT_HASH event.
|
||||
* The Old 2005 PFP description of EV_COMPACT_HASH which provides 4 byte ESI field (a pointer).
|
||||
* The 2019 PFP description allow the vendor to create event data that is "specified by the caller"
|
||||
* however the for PCR 6 there is a constraint that it contain
|
||||
* "The Event Data field SHALL be a unique string".
|
||||
*/
|
||||
public class EvCompactHash {
|
||||
|
||||
/**
|
||||
* Holds the Compact Hash description.
|
||||
*/
|
||||
private String eventInfo = "";
|
||||
|
||||
/**
|
||||
* Constructor that takes in the event data (hex string) and passes to function below.
|
||||
*
|
||||
* @param event byte array of the Event Compact Hash.
|
||||
* @throws java.io.UnsupportedEncodingException if compact hash has non utf-8 characters.
|
||||
*/
|
||||
public EvCompactHash(final byte[] event) throws UnsupportedEncodingException {
|
||||
hashEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the event data (hex string) converts to readable output.
|
||||
* This may be somewhat limited due to the unpublished nature of vendor specific data.
|
||||
*
|
||||
* @param event data to process.
|
||||
* @return a human readable description.
|
||||
* @throws java.io.UnsupportedEncodingException if compact hash has non utf-8 characters.
|
||||
*/
|
||||
public String hashEvent(final byte[] event) throws UnsupportedEncodingException {
|
||||
// determine if old format is used
|
||||
if (event.length == UefiConstants.SIZE_4) { // older PFP defines as 4 byte ESI pointer.
|
||||
eventInfo = " ESI = " + HexUtils.byteArrayToHexString(event);
|
||||
} else { // otherwise assume the event content is a string
|
||||
eventInfo = " " + new String(event, StandardCharsets.UTF_8);
|
||||
}
|
||||
return eventInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Readable description of the Event Content, however limiting that may be.
|
||||
*
|
||||
* @return Event description.
|
||||
*/
|
||||
public String toString() {
|
||||
return eventInfo;
|
||||
}
|
||||
}
|
@ -0,0 +1,166 @@
|
||||
package hirs.utils.tpm.eventlog.events;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Class for defining constants referenced in the PC Client
|
||||
* Platform Firmware Profile specification.
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public final class EvConstants {
|
||||
|
||||
/**
|
||||
* Type length = 4 bytes.
|
||||
*/
|
||||
public static final int EV_TYPE_SIZE = 4;
|
||||
/**
|
||||
* Event Log spec version.
|
||||
*/
|
||||
public static final int MIN_SIZE = 32;
|
||||
/**
|
||||
* Event Type (byte array).
|
||||
*/
|
||||
public static final int INT_LENGTH = 4;
|
||||
/**
|
||||
* Event Type (byte array).
|
||||
*/
|
||||
public static final int SHA1_LENGTH = 20;
|
||||
/**
|
||||
* Event Type (byte array).
|
||||
*/
|
||||
public static final int SHA256_LENGTH = 32;
|
||||
/**
|
||||
* Event Type (byte array).
|
||||
*/
|
||||
public static final int SHA384_LENGTH = 48;
|
||||
/**
|
||||
* Each PCR bank holds 24 registers.
|
||||
*/
|
||||
public static final int PCR_COUNT = 24;
|
||||
// Event IDs
|
||||
/**
|
||||
* Pre boot cert Event ID.
|
||||
*/
|
||||
public static final int EV_PREBOOT_CERT = 0x00000000;
|
||||
/**
|
||||
* POST Code Event ID.
|
||||
*/
|
||||
public static final int EV_POST_CODE = 0x00000001;
|
||||
/**
|
||||
* Unused Event ID.
|
||||
*/
|
||||
public static final int EV_UNUSED = 0x00000002;
|
||||
/**
|
||||
* NoAction Event ID.
|
||||
*/
|
||||
public static final int EV_NO_ACTION = 0x00000003;
|
||||
/**
|
||||
* NoAction Event ID.
|
||||
*/
|
||||
public static final int EV_SEPARATOR = 0x00000004;
|
||||
/**
|
||||
* Action Event ID.
|
||||
*/
|
||||
public static final int EV_ACTION = 0x00000005;
|
||||
/**
|
||||
* Event ID.
|
||||
*/
|
||||
public static final int EV_EVENT_TAG = 0x00000006;
|
||||
/**
|
||||
* SCRTM Contents Event ID.
|
||||
*/
|
||||
public static final int EV_S_CRTM_CONTENTS = 0x00000007;
|
||||
/**
|
||||
* SCRTM Version Event ID.
|
||||
*/
|
||||
public static final int EV_S_CRTM_VERSION = 0x00000008;
|
||||
/**
|
||||
* CPU Microcode Event ID.
|
||||
*/
|
||||
public static final int EV_CPU_MICROCODE = 0x00000009;
|
||||
/**
|
||||
* Platform Config Flags Event ID.
|
||||
*/
|
||||
public static final int EV_PLATFORM_CONFIG_FLAGS = 0x0000000A;
|
||||
/**
|
||||
* Table of Devices Event ID.
|
||||
*/
|
||||
public static final int EV_TABLE_OF_DEVICES = 0x0000000B;
|
||||
/**
|
||||
* Compact Hash Event ID.
|
||||
*/
|
||||
public static final int EV_COMPACT_HASH = 0x0000000C;
|
||||
/**
|
||||
* IPL Event ID.
|
||||
*/
|
||||
public static final int EV_IPL = 0x0000000D;
|
||||
/**
|
||||
* Partition Data Event ID.
|
||||
*/
|
||||
public static final int EV_IPL_PARTITION_DATA = 0x0000000E;
|
||||
/**
|
||||
* Non Host Event ID.
|
||||
*/
|
||||
public static final int EV_NONHOST_CODE = 0x0000000F;
|
||||
/**
|
||||
* Non Host Config Event ID.
|
||||
*/
|
||||
public static final int EV_NONHOST_CONFIG = 0x00000010;
|
||||
/**
|
||||
* Non Host Info Event ID.
|
||||
*/
|
||||
public static final int EV_NONHOST_INFO = 0x00000011;
|
||||
/**
|
||||
* Omit Boot Device Event ID.
|
||||
*/
|
||||
public static final int EV_EV_OMIT_BOOT_DEVICES_EVENTS = 0x00000012;
|
||||
/**
|
||||
* EFI Event ID.
|
||||
*/
|
||||
public static final int EV_EFI_EVENT_BASE = 0x80000000;
|
||||
/**
|
||||
* EFI Variable Driver Event ID.
|
||||
*/
|
||||
public static final int EV_EFI_VARIABLE_DRIVER_CONFIG = 0x80000001;
|
||||
/**
|
||||
* EFI Variable Boot Driver Event ID.
|
||||
*/
|
||||
public static final int EV_EFI_VARIABLE_BOOT = 0x80000002;
|
||||
/**
|
||||
* EFI Boot Services Application Event ID.
|
||||
*/
|
||||
public static final int EV_EFI_BOOT_SERVICES_APPLICATION = 0x80000003;
|
||||
/**
|
||||
* EFI Boot Services Application Event ID.
|
||||
*/
|
||||
public static final int EV_EFI_BOOT_SERVICES_DRIVER = 0x80000004;
|
||||
/**
|
||||
* EFI Runtime Services Driver Event ID.
|
||||
*/
|
||||
public static final int EV_EFI_RUNTIME_SERVICES_DRIVER = 0x80000005;
|
||||
/**
|
||||
* EFI GPT Event ID.
|
||||
*/
|
||||
public static final int EV_EFI_GPT_EVENT = 0x80000006;
|
||||
/**
|
||||
* EFI GPT Event ID.
|
||||
*/
|
||||
public static final int EV_EFI_ACTION = 0x80000007;
|
||||
/**
|
||||
* Platform Firmware Blob Event ID.
|
||||
*/
|
||||
public static final int EV_EFI_PLATFORM_FIRMWARE_BLOB = 0x80000008;
|
||||
/**
|
||||
* EFI Handoff Tables Event ID.
|
||||
*/
|
||||
public static final int EV_EFI_HANDOFF_TABLES = 0x80000009;
|
||||
/**
|
||||
* HRCTM Event ID.
|
||||
*/
|
||||
public static final int EV_EFI_HCRTM_EVENT = 0x80000010;
|
||||
/**
|
||||
* EFI Variable Authority Event ID.
|
||||
*/
|
||||
public static final int EV_EFI_VARIABLE_AUTHORITY = 0x800000E0;
|
||||
}
|
@ -0,0 +1,132 @@
|
||||
package hirs.utils.tpm.eventlog.events;
|
||||
|
||||
import hirs.utils.HexUtils;
|
||||
import hirs.utils.tpm.eventlog.uefi.UefiConstants;
|
||||
import hirs.utils.tpm.eventlog.uefi.UefiDevicePath;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Class to process the PC Client Firmware profile defined EV_EFI_BOOT_SERVICES_APPLICATION event.
|
||||
* The EV_EFI_BOOT_SERVICES_APPLICATION event data contains the UEFI_IMAGE_LOAD_EVENT structure:
|
||||
* struct tdUEFI_IMAGE_LOAD_EVENT {
|
||||
* UEFI_PHYSICAL_ADDRESS ImageLocationInMemory; // PE/COFF image same as UINT64
|
||||
* UINT64 ImageLengthInMemory;
|
||||
* UINT64 ImageLinkTimeAddress;
|
||||
* UINT64 LengthOfDevicePath;
|
||||
* UEFI_DEVICE_PATH DevicePath[LengthOfDevicePath]; // See UEFI spec for the encodings.
|
||||
* } UEFI_IMAGE_LOAD_EVENT;
|
||||
* <p>
|
||||
* DEVICE_PATH_PROTOCOL from the UEFI spec Section 10.1 page 284 of v2.8
|
||||
* <p>
|
||||
* #define EFI_DEVICE_PATH_PROTOCOL_GUID \09576e91-6d3f-11d2-8e39-00a0c969723b
|
||||
* typedef struct _EFI_DEVICE_PATH_PROTOCOL {
|
||||
* UINT8 Type;
|
||||
* UINT8 SubType;
|
||||
* UINT8 Length[2];
|
||||
* } EFI_DEVICE_PATH_PROTOCOL; // ref page of the UEFI spec
|
||||
* <p>
|
||||
* Where Type and Subtype are defined the UEFI spec section 10.3.1
|
||||
* Type 0x01 Hardware Device Path
|
||||
* Type 0x02 ACPI Device Path
|
||||
* Type 0x03 Messaging Device Path
|
||||
* Type 0x04 Media Device Path
|
||||
* Type 0x05 BIOS Boot Specification Device Path
|
||||
* Type 0x7F End of Hardware Device Path
|
||||
*/
|
||||
public class EvEfiBootServicesApp {
|
||||
/**
|
||||
* UEFI Address.
|
||||
*/
|
||||
private byte[] physicalAddress = null;
|
||||
/**
|
||||
* UEFI Image Length.
|
||||
*/
|
||||
@Getter
|
||||
private int imageLength = 0;
|
||||
/**
|
||||
* UEFI Link Time image address.
|
||||
*/
|
||||
private byte[] linkTimeAddress = null;
|
||||
/**
|
||||
* UEFI Device Path Length.
|
||||
*/
|
||||
@Getter
|
||||
private int devicePathLength = 0;
|
||||
/**
|
||||
* UEFI Device path.
|
||||
*/
|
||||
@Getter
|
||||
private UefiDevicePath devicePath = null;
|
||||
/**
|
||||
* Is the Device Path Valid.
|
||||
*/
|
||||
private boolean devicePathValid = false;
|
||||
|
||||
/**
|
||||
* EvEFIBootServicesApp constructor.
|
||||
*
|
||||
* @param bootServices byte array holding the event data.
|
||||
* @throws java.io.UnsupportedEncodingException if parsing issues exists.
|
||||
*/
|
||||
public EvEfiBootServicesApp(final byte[] bootServices) throws UnsupportedEncodingException {
|
||||
physicalAddress = new byte[UefiConstants.SIZE_8];
|
||||
System.arraycopy(bootServices, 0, physicalAddress, 0, UefiConstants.SIZE_8);
|
||||
byte[] lengthBytes = new byte[UefiConstants.SIZE_8];
|
||||
System.arraycopy(bootServices, UefiConstants.OFFSET_8, lengthBytes, 0, UefiConstants.SIZE_8);
|
||||
imageLength = HexUtils.leReverseInt(lengthBytes);
|
||||
linkTimeAddress = new byte[UefiConstants.SIZE_8];
|
||||
System.arraycopy(bootServices, UefiConstants.OFFSET_16, linkTimeAddress, 0,
|
||||
UefiConstants.SIZE_8);
|
||||
System.arraycopy(bootServices, UefiConstants.SIZE_24, lengthBytes, 0, UefiConstants.SIZE_8);
|
||||
// if (imageLength != 0) {
|
||||
devicePathLength = HexUtils.leReverseInt(lengthBytes);
|
||||
if (devicePathLength != 0) {
|
||||
byte[] devPathBytes = new byte[devicePathLength];
|
||||
System.arraycopy(bootServices, UefiConstants.SIZE_32, devPathBytes,
|
||||
0, devicePathLength);
|
||||
devicePath = new UefiDevicePath(devPathBytes);
|
||||
devicePathValid = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the address of the physical image of the boot services application.
|
||||
*
|
||||
* @return address of the physical image.
|
||||
*/
|
||||
public byte[] getImagePhysicalAddress() {
|
||||
return Arrays.copyOf(physicalAddress, physicalAddress.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length of a link time image referenced by this event.
|
||||
*
|
||||
* @return length of the link time image.
|
||||
*/
|
||||
public byte[] getImageLinkTimeAddress() {
|
||||
return Arrays.copyOf(linkTimeAddress, linkTimeAddress.length);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a human readable string of the Boot Service info.
|
||||
*
|
||||
* @return a human readable string.
|
||||
*/
|
||||
public String toString() {
|
||||
String info = "Image info: ";
|
||||
info += " Image physical address: " + HexUtils.byteArrayToHexString(physicalAddress);
|
||||
info += " Image length = " + imageLength;
|
||||
info += " Image link time address: " + HexUtils.byteArrayToHexString(physicalAddress);
|
||||
info += " Device path length = " + devicePathLength;
|
||||
if (devicePathValid) {
|
||||
info += "\n" + devicePath.toString();
|
||||
} else {
|
||||
info += "\n No uefi device paths were specified";
|
||||
}
|
||||
return info;
|
||||
}
|
||||
}
|
@ -0,0 +1,148 @@
|
||||
package hirs.utils.tpm.eventlog.events;
|
||||
|
||||
import hirs.utils.HexUtils;
|
||||
import hirs.utils.tpm.eventlog.uefi.UefiConstants;
|
||||
import hirs.utils.tpm.eventlog.uefi.UefiPartition;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Class to process the PC Client Firmware profile defined EV_EFI_GPT_EVENT event.
|
||||
* The EV_EFI_GPT_EVENT event data contains the UEFI_GPT_DATA structure as defined in the PFP
|
||||
* line 2860:
|
||||
* <p>
|
||||
* typedef struct {
|
||||
* UEFI_PARTITION_TABLE_HEADER UEFIPartitionHeader; // same as UINT64 for current x86 devices
|
||||
* UINT64 NumberOfPartitions;
|
||||
* UEFI_PARTITION_ENTRY Partitions [NumberOfPartitions];
|
||||
* }UEFI_GPT_DATA;
|
||||
* <p>
|
||||
* The UEFI spec defines the EFI_TABLE_HEADER and EFI_PARTITION_ENTRY
|
||||
* <p>
|
||||
* * typedef struct {
|
||||
* UINT64 Signature; // A 64-bit signature that identifies the type of table that follows.
|
||||
* UINT32 Revision;
|
||||
* UINT32 HeaderSize;
|
||||
* UINT32 CRC32;
|
||||
* UINT32 Reserved;
|
||||
* } EFI_TABLE_HEADER;
|
||||
* <p>
|
||||
* typedef struct {
|
||||
* EFI_GUID PartitionTypeGUID;
|
||||
* EFI_GUID UniquePartitionGUID;
|
||||
* EFI_LBA StartingLBA; // Same as UINT64.
|
||||
* EFI_LBA EndingLBA;
|
||||
* UINT64 Attributes;
|
||||
* CHAR16 PartitionName[36]; // 36 CHAR16 = 72 Bytes
|
||||
* } EFI_PARTITION_ENTRY;
|
||||
* <p>
|
||||
* EFI_SYSTEM_TABLE_SIGNATURE 0x5453595320494249
|
||||
* EFI_BOOT_SERVICES_SIGNATURE 0x56524553544f4f42
|
||||
* EFI_RUNTIME_SERVICES_SIGNATURE 0x56524553544e5552
|
||||
* <p>
|
||||
* UEFI Table 23. Defined GPT Partition Entry - Partition Type GUIDs
|
||||
* Unused Entry 00000000-0000-0000-0000-000000000000
|
||||
* EFI System Partition C12A7328-F81F-11D2-BA4B-00A0C93EC93B
|
||||
* Partition containing a legacy MBR 024DEE41-33E7-11D3-9D69-0008C781F39F
|
||||
*/
|
||||
public class EvEfiGptPartition {
|
||||
/**
|
||||
* Header Size.
|
||||
*/
|
||||
private int headerSize = 0;
|
||||
/**
|
||||
* Header bytes.
|
||||
*/
|
||||
private byte[] header = new byte[UefiConstants.SIZE_8];
|
||||
/**
|
||||
* Number of partitions in this event.
|
||||
*/
|
||||
private int numberOfPartitions;
|
||||
/**
|
||||
* Partition Length.
|
||||
*/
|
||||
private int partitonEntryLength = UefiConstants.SIZE_128;
|
||||
/**
|
||||
* List of Partitions.
|
||||
*/
|
||||
@Getter
|
||||
private ArrayList<UefiPartition> partitionList = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* GPT Partition Event Type constructor.
|
||||
*
|
||||
* @param eventDataBytes GPT Event to process
|
||||
* @throws java.io.UnsupportedEncodingException if Event Data fails to parse
|
||||
*/
|
||||
public EvEfiGptPartition(final byte[] eventDataBytes) throws UnsupportedEncodingException {
|
||||
//byte[] eventDataBytes = event.getEventContent();
|
||||
// Process the partition header
|
||||
System.arraycopy(eventDataBytes, 0, header, 0, UefiConstants.SIZE_8); // Signature
|
||||
byte[] revision = new byte[UefiConstants.SIZE_4];
|
||||
System.arraycopy(eventDataBytes, UefiConstants.SIZE_8, revision, 0, UefiConstants.SIZE_4);
|
||||
byte[] hsize = new byte[UefiConstants.SIZE_4];
|
||||
System.arraycopy(eventDataBytes, UefiConstants.SIZE_12, hsize, 0, UefiConstants.SIZE_4);
|
||||
headerSize = getIntFromBytes(hsize);
|
||||
byte[] partitions = new byte[UefiConstants.SIZE_8];
|
||||
System.arraycopy(eventDataBytes, headerSize, partitions, 0, UefiConstants.SIZE_8);
|
||||
numberOfPartitions = getIntFromBytes(partitions);
|
||||
int partitionLength = numberOfPartitions * partitonEntryLength;
|
||||
byte[] partitionEntries = new byte[partitionLength];
|
||||
System.arraycopy(eventDataBytes, headerSize + UefiConstants.SIZE_8, partitionEntries,
|
||||
0, partitionLength);
|
||||
processesPartitions(partitionEntries, numberOfPartitions);
|
||||
// Mystery Structure get processed here (skipped for now), still part of the header
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes an individual GPT partition entry.
|
||||
*
|
||||
* @param partitions byte array holding partition data.
|
||||
* @param numberOfPartitions number of partitions included in the data.
|
||||
* @throws java.io.UnsupportedEncodingException if partition data fails to parse.
|
||||
*/
|
||||
private void processesPartitions(final byte[] partitions, final int numberOfPartitions)
|
||||
throws UnsupportedEncodingException {
|
||||
byte[] partitionData = new byte[UefiConstants.SIZE_128];
|
||||
for (int i = 0; i < numberOfPartitions; i++) {
|
||||
System.arraycopy(partitions, i * partitonEntryLength, partitionData, 0,
|
||||
partitonEntryLength);
|
||||
partitionList.add(new UefiPartition(partitionData));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a human readable string describing the GPT Partition information.
|
||||
*
|
||||
* @return a human readable string holding the partition information.
|
||||
*/
|
||||
public String toString() {
|
||||
String headerStr = HexUtils.byteArrayToHexString(header);
|
||||
StringBuilder partitionInfo = new StringBuilder();
|
||||
partitionInfo.append("GPT Header Signature = " + headerStr + " : Number of Partitions = "
|
||||
+ numberOfPartitions + "\n");
|
||||
for (int i = 0; i < numberOfPartitions; i++) {
|
||||
if (i > 0) {
|
||||
partitionInfo.append("\n");
|
||||
}
|
||||
partitionInfo.append(" Partition " + i + " information\n");
|
||||
partitionInfo.append(partitionList.get(i).toString());
|
||||
}
|
||||
return partitionInfo.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for converting little Endian byte arrays into Big Endian integers.
|
||||
*
|
||||
* @param data data to convert.
|
||||
* @return an integer.
|
||||
*/
|
||||
public int getIntFromBytes(final byte[] data) {
|
||||
byte[] bigEndData = HexUtils.leReverseByte(data);
|
||||
BigInteger bigInt = new BigInteger(bigEndData);
|
||||
return bigInt.intValue();
|
||||
}
|
||||
}
|
@ -0,0 +1,142 @@
|
||||
package hirs.utils.tpm.eventlog.events;
|
||||
|
||||
import hirs.utils.HexUtils;
|
||||
import hirs.utils.tpm.eventlog.uefi.UefiConstants;
|
||||
import hirs.utils.tpm.eventlog.uefi.UefiGuid;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Class to process the PC Client Firmware profile defined EV_EFI_HANDOFF_TABLES event.
|
||||
* The Event data holds a structure called UEFI_HANDOFF_TABLE_POINTERS:
|
||||
* <p>
|
||||
* tdUEFI_HANDOFF_TABLE_POINTERS {
|
||||
* UINT64 NumberOfTables;
|
||||
* UEFI_CONFIGURATION_TABLE TableEntry[NumberOfTables];
|
||||
* }UEFI_HANDOFF_TABLE_POINTERS;
|
||||
* <p>
|
||||
* The UEFI_CONFIGURATION_TABLE id defined in the UEFI spec as:
|
||||
* <p>
|
||||
* typedef struct{
|
||||
* EFI_GUID VendorGuid;
|
||||
* VOID *VendorTable;
|
||||
* } EFI_CONFIGURATION_TABLE;
|
||||
* Where the defines
|
||||
* VendorGuid: The 128-bit GUID value that uniquely identifies the system configuration table.
|
||||
* VendorTable: A pointer to the table associated with VendorGuid.
|
||||
* Section 4.6 of the UEFI spec has a listing of some of the industry defined
|
||||
* standard that define the particular table.
|
||||
*/
|
||||
public class EvEfiHandoffTable {
|
||||
/**
|
||||
* Number of Tables.
|
||||
*/
|
||||
@Getter
|
||||
private int numberOfTables = 0;
|
||||
/**
|
||||
* List of Vendor GUIDs.
|
||||
*/
|
||||
private ArrayList<UefiGuid> vendorGuids = new ArrayList<>();
|
||||
/**
|
||||
* List of Vendors.
|
||||
*/
|
||||
private ArrayList<byte[]> vendorTables = new ArrayList<>();
|
||||
|
||||
private Path vendorPathString;
|
||||
|
||||
/**
|
||||
* EvEFIHandoffTable constructor.
|
||||
*
|
||||
* @param tpmEventData byte array holding the Handoff table data.
|
||||
*/
|
||||
public EvEfiHandoffTable(final byte[] tpmEventData) {
|
||||
// Get NumberOfTables from the EventData
|
||||
byte[] count = new byte[UefiConstants.SIZE_8];
|
||||
System.arraycopy(tpmEventData, 0, count, 0, UefiConstants.SIZE_8);
|
||||
byte[] bigEndCount = HexUtils.leReverseByte(count);
|
||||
BigInteger countInt = new BigInteger(bigEndCount);
|
||||
numberOfTables = countInt.intValue();
|
||||
// process each UEFI_CONFIGURATION_TABLE table
|
||||
int offset = UefiConstants.OFFSET_8;
|
||||
for (int tables = 0; tables < numberOfTables; tables++) {
|
||||
vendorGuids.add(getNextGUID(tpmEventData, offset));
|
||||
vendorTables.add(getNextTable(tpmEventData, offset + UefiConstants.OFFSET_16));
|
||||
offset += UefiConstants.OFFSET_24;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* EvEFIHandoffTable constructor.
|
||||
*
|
||||
* @param tpmEventData byte array holding the Handoff table data.
|
||||
* @param vendorPathString the string for the vendor file
|
||||
*/
|
||||
public EvEfiHandoffTable(final byte[] tpmEventData, final Path vendorPathString) {
|
||||
// Get NumberOfTables from the EventData
|
||||
byte[] count = new byte[UefiConstants.SIZE_8];
|
||||
System.arraycopy(tpmEventData, 0, count, 0, UefiConstants.SIZE_8);
|
||||
byte[] bigEndCount = HexUtils.leReverseByte(count);
|
||||
BigInteger countInt = new BigInteger(bigEndCount);
|
||||
numberOfTables = countInt.intValue();
|
||||
this.vendorPathString = vendorPathString;
|
||||
// process each UEFI_CONFIGURATION_TABLE table
|
||||
int offset = UefiConstants.OFFSET_8;
|
||||
for (int tables = 0; tables < numberOfTables; tables++) {
|
||||
vendorGuids.add(getNextGUID(tpmEventData, offset));
|
||||
vendorTables.add(getNextTable(tpmEventData, offset + UefiConstants.OFFSET_16));
|
||||
offset += UefiConstants.OFFSET_24;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next GUI in the table.
|
||||
*
|
||||
* @param eventData byte array holding the guids.
|
||||
* @param offset offset to the guid.
|
||||
* @return Vendor Guid
|
||||
*/
|
||||
private UefiGuid getNextGUID(final byte[] eventData, final int offset) {
|
||||
byte[] guid = new byte[UefiConstants.SIZE_16];
|
||||
System.arraycopy(eventData, offset, guid, 0, UefiConstants.SIZE_16);
|
||||
if (vendorPathString == null || vendorPathString.toString().isEmpty()) {
|
||||
return new UefiGuid(guid);
|
||||
} else {
|
||||
return new UefiGuid(guid, vendorPathString);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the next table to a new array.
|
||||
*
|
||||
* @param eventData byte array holding the next table.
|
||||
* @param offset offset within the table to fond the data.
|
||||
* @return a byte array holding the new table.
|
||||
*/
|
||||
private byte[] getNextTable(final byte[] eventData, final int offset) {
|
||||
byte[] table = new byte[UefiConstants.SIZE_8];
|
||||
System.arraycopy(eventData, offset, table, 0, UefiConstants.SIZE_8);
|
||||
return table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a human readable description of the hand off tables.
|
||||
*
|
||||
* @return a human readable description.
|
||||
*/
|
||||
public String toString() {
|
||||
StringBuilder tableInfo = new StringBuilder();
|
||||
tableInfo.append("Number of UEFI_CONFIGURATION_TABLEs = " + numberOfTables + "\n");
|
||||
for (int i = 0; i < numberOfTables; i++) {
|
||||
UefiGuid currentGuid = vendorGuids.get(i);
|
||||
tableInfo.append(" Table " + i + ": " + currentGuid.toString());
|
||||
tableInfo.append(" UEFI industry standard table type = "
|
||||
+ currentGuid.getVendorTableReference() + "\n");
|
||||
tableInfo.append(" VendorTable " + i + " address: "
|
||||
+ HexUtils.byteArrayToHexString(vendorTables.get(i)));
|
||||
}
|
||||
return tableInfo.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,150 @@
|
||||
package hirs.utils.tpm.eventlog.events;
|
||||
|
||||
import hirs.utils.HexUtils;
|
||||
import hirs.utils.tpm.eventlog.TcgTpmtHa;
|
||||
import hirs.utils.tpm.eventlog.uefi.UefiConstants;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Class to process the TCG_EfiSpecIDEvent.
|
||||
* The first 16 bytes of a Event Data MUST be String based identifier (Signature).
|
||||
* The only currently defined Signature is "Spec ID Event03" which implies the data is
|
||||
* a TCG_EfiSpecIDEvent. TCG_EfiSpecIDEvent is the first event in a TPM Event Log
|
||||
* and is used to determine the format of the Log (SHA1 vs Crypt Agile).
|
||||
* <p>
|
||||
* typedef struct tdTCG_EfiSpecIdEvent {
|
||||
* BYTE Signature[16];
|
||||
* UINT32 platformClass;
|
||||
* UINT8 specVersionMinor;
|
||||
* UINT8 specVersionMajor;
|
||||
* UINT8 specErrata;
|
||||
* UINT8 uintnSize;
|
||||
* UINT32 numberOfAlgorithms;
|
||||
* TCG_EfiSpecIdEventAlgorithmSize digestSizes[numberOfAlgorithms];
|
||||
* UINT8 vendorInfoSize;
|
||||
* BYTE vendorInfo[VendorInfoSize];
|
||||
* } TCG_EfiSpecIDEvent;
|
||||
* <p>
|
||||
* typedef struct tdTCG_EfiSpecIdEventAlgorithmSize {
|
||||
* UINT16 algorithmId;
|
||||
* UINT16 digestSize;
|
||||
* } TCG_EfiSpecIdEventAlgorithmSize;
|
||||
* <p>
|
||||
* define TPM_ALG_SHA1 (TPM_ALG_ID)(0x0004)
|
||||
* define TPM_ALG_SHA256 (TPM_ALG_ID)(0x000B)
|
||||
* define TPM_ALG_SHA384 (TPM_ALG_ID)(0x000C)
|
||||
* define TPM_ALG_SHA512 (TPM_ALG_ID)(0x000D)
|
||||
* <p>
|
||||
* Notes: Parses event data for an EfiSpecID per Table 5 TCG_EfiSpecIdEvent Example.
|
||||
* 1. Should be the first Structure in the log
|
||||
* 2. Has an EventType of EV_NO_ACTION (0x00000003)
|
||||
* 3. Digest of 20 bytes of all 0's
|
||||
* 4. Event content defined as TCG_EfiSpecIDEvent Struct.
|
||||
* 5. First 16 bytes of the structure is an ASCII "Spec ID Event03"
|
||||
* 6. The version of the log is used to determine which format the Log
|
||||
* is to use (sha1 or Crypto Agile)
|
||||
*/
|
||||
@Getter
|
||||
public class EvEfiSpecIdEvent {
|
||||
/**
|
||||
* Minor Version.
|
||||
*/
|
||||
private String versionMinor = "";
|
||||
/**
|
||||
* Major Version.
|
||||
*/
|
||||
private String versionMajor = "";
|
||||
/**
|
||||
* Specification errata version.
|
||||
*/
|
||||
private String errata = "";
|
||||
/**
|
||||
* Signature (text) data.
|
||||
*/
|
||||
private String signature = "";
|
||||
/**
|
||||
* Platform class.
|
||||
*/
|
||||
private String platformClass = "";
|
||||
/**
|
||||
* Algorithm count.
|
||||
*/
|
||||
private int numberOfAlg = 0;
|
||||
/**
|
||||
* True if event log uses Crypto Agile format.
|
||||
*/
|
||||
private boolean cryptoAgile = false;
|
||||
/**
|
||||
* Algorithm list.
|
||||
*/
|
||||
private ArrayList<String> algList = new ArrayList<String>();
|
||||
|
||||
/**
|
||||
* EvEfiSpecIdEvent Constructor.
|
||||
*
|
||||
* @param efiSpecId byte array holding the spec ID Event.
|
||||
*/
|
||||
public EvEfiSpecIdEvent(final byte[] efiSpecId) {
|
||||
byte[] signatureBytes = new byte[UefiConstants.SIZE_16];
|
||||
System.arraycopy(efiSpecId, 0, signatureBytes, 0, UefiConstants.SIZE_16);
|
||||
signature = HexUtils.byteArrayToHexString(signatureBytes);
|
||||
signature = new String(signatureBytes, StandardCharsets.UTF_8)
|
||||
.substring(0, UefiConstants.SIZE_15);
|
||||
|
||||
byte[] platformClassBytes = new byte[UefiConstants.SIZE_4];
|
||||
System.arraycopy(efiSpecId, UefiConstants.OFFSET_16, platformClassBytes, 0,
|
||||
UefiConstants.SIZE_4);
|
||||
platformClass = HexUtils.byteArrayToHexString(platformClassBytes);
|
||||
|
||||
byte[] specVersionMinorBytes = new byte[1];
|
||||
System.arraycopy(efiSpecId, UefiConstants.OFFSET_20, specVersionMinorBytes, 0, 1);
|
||||
versionMinor = HexUtils.byteArrayToHexString(specVersionMinorBytes);
|
||||
|
||||
byte[] specVersionMajorBytes = new byte[1];
|
||||
System.arraycopy(efiSpecId, UefiConstants.OFFSET_21, specVersionMajorBytes, 0, 1);
|
||||
versionMajor = HexUtils.byteArrayToHexString(specVersionMajorBytes);
|
||||
|
||||
byte[] specErrataBytes = new byte[1];
|
||||
System.arraycopy(efiSpecId, UefiConstants.OFFSET_22, specErrataBytes, 0, 1);
|
||||
errata = HexUtils.byteArrayToHexString(specErrataBytes);
|
||||
|
||||
byte[] numberOfAlgBytes = new byte[UefiConstants.SIZE_4];
|
||||
System.arraycopy(efiSpecId, UefiConstants.OFFSET_24, numberOfAlgBytes, 0,
|
||||
UefiConstants.SIZE_4);
|
||||
numberOfAlg = HexUtils.leReverseInt(numberOfAlgBytes);
|
||||
|
||||
byte[] algorithmIDBytes = new byte[UefiConstants.SIZE_2];
|
||||
int algLocation = UefiConstants.SIZE_28;
|
||||
for (int i = 0; i < numberOfAlg; i++) {
|
||||
System.arraycopy(efiSpecId, algLocation + UefiConstants.OFFSET_4 * i, algorithmIDBytes,
|
||||
0, UefiConstants.SIZE_2);
|
||||
String alg = TcgTpmtHa.tcgAlgIdToString(HexUtils.leReverseInt(algorithmIDBytes));
|
||||
algList.add(alg);
|
||||
}
|
||||
if ((algList.size() == 1) && (algList.get(0).compareTo("SHA1") == 0)) {
|
||||
cryptoAgile = false;
|
||||
} else {
|
||||
cryptoAgile = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a human readable description of the data within this event.
|
||||
*
|
||||
* @return a description of this event..
|
||||
*/
|
||||
public String toString() {
|
||||
String specInfo = "";
|
||||
if (signature.equals("Spec ID Event#")) {
|
||||
specInfo += "Platform Profile Specification version = " + versionMajor + "." + versionMinor
|
||||
+ " using errata version" + errata;
|
||||
} else {
|
||||
specInfo = "EV_NO_ACTION event named " + signature
|
||||
+ " encountered but support for processing it has not been added to this application";
|
||||
}
|
||||
return specInfo;
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
package hirs.utils.tpm.eventlog.events;
|
||||
|
||||
import hirs.utils.HexUtils;
|
||||
import hirs.utils.tpm.eventlog.uefi.UefiConstants;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* Class for processing the EV_EVENT_TAG.
|
||||
* The structure for the Event Data is defined as:
|
||||
* structure tdTCG_PCClientTaggedEvent{
|
||||
* UINT32 taggedEventID;
|
||||
* UINT32 taggedEventDataSize;
|
||||
* BYTE taggedEventData[taggedEventDataSize];
|
||||
* } TCG_PCClientTaggedEvent;
|
||||
* ToDo: Find lookup of taggedEventID and figure out how to process.
|
||||
*/
|
||||
public class EvEventTag {
|
||||
/**
|
||||
* Event Tag Information.
|
||||
*/
|
||||
private String eventTagInfo = "";
|
||||
/**
|
||||
* Event Tag ID.
|
||||
*/
|
||||
@Getter
|
||||
private int tagEventID = 0;
|
||||
/**
|
||||
* Event ID.
|
||||
*/
|
||||
private int eventID = 0;
|
||||
/**
|
||||
* Data size.
|
||||
*/
|
||||
@Getter
|
||||
private int dataSize = 0;
|
||||
|
||||
/**
|
||||
* Processes event tag.
|
||||
*
|
||||
* @param eventTag byte array holding the eventTag data.
|
||||
*/
|
||||
public EvEventTag(final byte[] eventTag) {
|
||||
if (eventTag.length < UefiConstants.SIZE_8) {
|
||||
eventTagInfo = "Invalid EV Event Tag data";
|
||||
} else {
|
||||
byte[] tagEventIdBytes = new byte[UefiConstants.SIZE_4];
|
||||
System.arraycopy(eventTag, 0, tagEventIdBytes, 0, UefiConstants.SIZE_4);
|
||||
eventID = HexUtils.leReverseInt(tagEventIdBytes);
|
||||
byte[] tagEventDataSize = new byte[UefiConstants.SIZE_4];
|
||||
System.arraycopy(eventTag, UefiConstants.OFFSET_4, tagEventDataSize, 0,
|
||||
UefiConstants.SIZE_4);
|
||||
dataSize = HexUtils.leReverseInt(tagEventDataSize);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a human readable string of the Event Tag.
|
||||
*
|
||||
* @return human readable string.
|
||||
*/
|
||||
public String toString() {
|
||||
if (eventTagInfo.isEmpty()) {
|
||||
eventTagInfo = " Tagged Event ID = " + eventID;
|
||||
eventTagInfo += " Data Size = " + dataSize;
|
||||
}
|
||||
return eventTagInfo;
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package hirs.utils.tpm.eventlog.events;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* Processes event type EV_IPL which is deprecated in the current spec,
|
||||
* but defined in older version of the specification(1.0.0) as contain
|
||||
* "informative information about the IPL code" (ascii strings).
|
||||
*/
|
||||
public class EvIPL {
|
||||
|
||||
private String description = "";
|
||||
|
||||
/**
|
||||
*IPL Event Constructor.
|
||||
* @param event byte array holding the IPL Event data.
|
||||
*/
|
||||
public EvIPL(final byte[] event) {
|
||||
event(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes IPL event.
|
||||
* @param event byte array holding the IPL Event data.
|
||||
* @return a description of the IPl event.
|
||||
*/
|
||||
public String event(final byte[] event) {
|
||||
if (event == null) {
|
||||
description = "Invalid IPL event data";
|
||||
} else {
|
||||
description = " \"" + new String(event, StandardCharsets.UTF_8) + "\"";
|
||||
}
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a human readable description of the IPL Event.
|
||||
* @return human readable description.
|
||||
*/
|
||||
public String toString() {
|
||||
return description;
|
||||
}
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
package hirs.utils.tpm.eventlog.events;
|
||||
|
||||
import hirs.utils.tpm.eventlog.uefi.UefiConstants;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* Class to process the EV_NO_ACTION event using a structure of TCG_EfiSpecIDEvent.
|
||||
* The first 16 bytes of the event data MUST be a String based identifier (Signature).
|
||||
* The only currently defined Signature is "Spec ID Event03"
|
||||
* which implies the data is a TCG_EfiSpecIDEvent.
|
||||
* TCG_EfiSpecIDEvent is the first event in a TPM Event Log and is used to determine
|
||||
* if the format of the Log (SHA1 vs Crypto Agile).
|
||||
* <p>
|
||||
* Notes:
|
||||
* 1. First 16 bytes of the structure is an ASCII with a fixed Length of 16
|
||||
* 2. Add processing of other NoEvent types when new ones get defined
|
||||
*/
|
||||
public class EvNoAction {
|
||||
|
||||
/**
|
||||
* Signature (text) data.
|
||||
*/
|
||||
private String signature = "";
|
||||
/**
|
||||
* True of the event is a SpecIDEvent.
|
||||
*/
|
||||
private boolean bSpecIDEvent = false;
|
||||
/**
|
||||
* EvEfiSpecIdEvent Object.
|
||||
*/
|
||||
@Getter
|
||||
private EvEfiSpecIdEvent specIDEvent = null;
|
||||
|
||||
/**
|
||||
* EvNoAction constructor.
|
||||
*
|
||||
* @param eventData byte array holding the event to process.
|
||||
* @throws java.io.UnsupportedEncodingException if input fails to parse.
|
||||
*/
|
||||
public EvNoAction(final byte[] eventData) throws UnsupportedEncodingException {
|
||||
byte[] signatureBytes = new byte[UefiConstants.SIZE_15];
|
||||
System.arraycopy(eventData, 0, signatureBytes, 0, UefiConstants.SIZE_15);
|
||||
signature = new String(signatureBytes, StandardCharsets.UTF_8);
|
||||
signature = signature.replaceAll("[^\\P{C}\t\r\n]", ""); // remove null characters
|
||||
if (signature.contains("Spec ID Event03")) { // implies CryptAgileFormat
|
||||
specIDEvent = new EvEfiSpecIdEvent(eventData);
|
||||
bSpecIDEvent = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this event is a SpecIDEvent.
|
||||
*
|
||||
* @return true of the event is a SpecIDEvent.
|
||||
*/
|
||||
public boolean isSpecIDEvent() {
|
||||
return bSpecIDEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a description of this event.
|
||||
*
|
||||
* @return Human readable description of this event.
|
||||
*/
|
||||
public String toString() {
|
||||
String specInfo = "";
|
||||
if (bSpecIDEvent) {
|
||||
specInfo += " Signature = Spec ID Event03 : ";
|
||||
if (specIDEvent.isCryptoAgile()) {
|
||||
specInfo += "Log format is Crypto Agile\n";
|
||||
} else {
|
||||
specInfo += "Log format is SHA 1 (NOT Crypto Agile)\n";
|
||||
}
|
||||
specInfo += " Platform Profile Specification version = "
|
||||
+ specIDEvent.getVersionMajor() + "." + specIDEvent.getVersionMinor()
|
||||
+ " using errata version " + specIDEvent.getErrata();
|
||||
} else {
|
||||
specInfo = "EV_NO_ACTION event named " + signature
|
||||
+ " encountered but support for processing it has not been added to this application.\n";
|
||||
}
|
||||
return specInfo;
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
package hirs.utils.tpm.eventlog.events;
|
||||
|
||||
import hirs.utils.tpm.eventlog.uefi.UefiFirmware;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* Class for processing EV_POST_CODE event types
|
||||
*
|
||||
* typedef struct tdUEFI_PLATFORM_FIRMWARE_BLOB {
|
||||
* UEFI_PHYSICAL_ADDRESS BlobBase; // Same as UINT64 for most systems
|
||||
* UINT64 BlobLength;
|
||||
* } UEFI_PLATFORM_FIRMWARE_BLOB;
|
||||
*
|
||||
* However Table 9 of the PC Client Platform firmware profile states that even content is a string
|
||||
* For POST code, the event data SHOULD be POST CODE.
|
||||
* For embedded SMM code, the event data SHOULD be SMM CODE.
|
||||
* For ACPI flash data, the event data SHOULD be ACPI DATA.
|
||||
* For BIS code, the event data SHOULD be BIS CODE.
|
||||
* For embedded option ROMs, the event data SHOULD be Embedded UEFI Driver.
|
||||
*/
|
||||
public class EvPostCode {
|
||||
/** Event Description. */
|
||||
private String codeInfo = "";
|
||||
/** String type flag. */
|
||||
private boolean bisString = false;
|
||||
/** Firmware object. */
|
||||
@Getter
|
||||
private UefiFirmware firmwareBlob = null;
|
||||
|
||||
/**
|
||||
* EcPostCode constructor.
|
||||
* @param postCode byte array holding the post code content.
|
||||
*/
|
||||
public EvPostCode(final byte[] postCode) {
|
||||
// 2 ways post code has been implemented, check for the ascii string first
|
||||
if (isAscii(postCode)) {
|
||||
codeInfo = new String(postCode, StandardCharsets.UTF_8);
|
||||
bisString = true;
|
||||
} else {
|
||||
firmwareBlob = new UefiFirmware(postCode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag set to true if Post Code is a string.
|
||||
* @return true if Post Code is a string.
|
||||
*/
|
||||
public boolean isString() {
|
||||
return bisString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a human readable string of the Post Code information.
|
||||
* @return human readable string.
|
||||
*/
|
||||
public String toString() {
|
||||
if (bisString) {
|
||||
return codeInfo;
|
||||
}
|
||||
return firmwareBlob.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the byte array is a string.
|
||||
* @param postCode byte array input.
|
||||
* @return true if byte array is a string.
|
||||
*/
|
||||
public static boolean isAscii(final byte[] postCode) {
|
||||
for (byte b : postCode) {
|
||||
if (!Character.isDefined(b)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package hirs.utils.tpm.eventlog.events;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* Class to process the PC Client Firmware profile defined EV_S_CRTM_CONTENTS event.
|
||||
*/
|
||||
public class EvSCrtmContents {
|
||||
|
||||
private String description = "";
|
||||
|
||||
/**
|
||||
* Constructor that takes in the event data and waits to be called.
|
||||
* @param event byte array holding the event content data.
|
||||
*/
|
||||
public EvSCrtmContents(final byte[] event) {
|
||||
scrtmContents(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if event data is null and if not it converts to a String.
|
||||
* @param event byte array holding the event data.
|
||||
* @return String contents contained within the event.
|
||||
*/
|
||||
public String scrtmContents(final byte[] event) {
|
||||
if (event == null) {
|
||||
description = "invalid content event data";
|
||||
} else {
|
||||
description = new String(event, StandardCharsets.UTF_8);
|
||||
}
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Human readable string contained within the CRTM Contents event.
|
||||
* @return Human readable string.
|
||||
*/
|
||||
public String toString() {
|
||||
return description;
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package hirs.utils.tpm.eventlog.events;
|
||||
|
||||
import hirs.utils.HexUtils;
|
||||
import hirs.utils.tpm.eventlog.uefi.UefiConstants;
|
||||
import hirs.utils.tpm.eventlog.uefi.UefiGuid;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* Class to process the PC Client Firmware profile defined EV_S_CRTM_VERSION event.
|
||||
*/
|
||||
public class EvSCrtmVersion {
|
||||
|
||||
private String description = "";
|
||||
|
||||
/**
|
||||
* Constructor that takes in the event data and waits to be called.
|
||||
*
|
||||
* @param event byte array holding the event content data.
|
||||
* @throws java.io.UnsupportedEncodingException if parsing issues exist.
|
||||
*/
|
||||
public EvSCrtmVersion(final byte[] event) throws UnsupportedEncodingException {
|
||||
sCrtmVersion(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if event data is null and if not it converts to a String.
|
||||
*
|
||||
* @param data byte array holding the vent content.
|
||||
* @return String representation of the version.
|
||||
*/
|
||||
public String sCrtmVersion(final byte[] data) {
|
||||
UefiGuid guid = null;
|
||||
if (data == null) {
|
||||
description = "invalid content event data";
|
||||
} else {
|
||||
if (data.length == UefiConstants.SIZE_16) {
|
||||
if (UefiGuid.isValidUUID(data)) {
|
||||
guid = new UefiGuid(data);
|
||||
String guidInfo = guid.toStringNoLookup();
|
||||
description = " SCRM Version = " + guidInfo;
|
||||
}
|
||||
} else if (data.length < UefiConstants.SIZE_4) {
|
||||
description = HexUtils.byteArrayToHexString(data);
|
||||
} else if (EvPostCode.isAscii(data)) {
|
||||
description = new String(data, StandardCharsets.UTF_8);
|
||||
} else {
|
||||
description = "Unknown Version format";
|
||||
}
|
||||
}
|
||||
return (description);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return function to send data to the toString.
|
||||
*
|
||||
* @return String representation of the version.
|
||||
*/
|
||||
public String toString() {
|
||||
return description;
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Non-persistant classes related to TGC Event Logs.
|
||||
*/
|
||||
|
||||
package hirs.utils.tpm.eventlog.events;
|
||||
|
@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Non-persistant classes related to TGC Event Logs.
|
||||
*/
|
||||
|
||||
package hirs.utils.tpm.eventlog;
|
||||
|
@ -0,0 +1,41 @@
|
||||
package hirs.utils.tpm.eventlog.uefi;
|
||||
|
||||
/**
|
||||
* Class to process a UEFI BootOrder variable.
|
||||
* UEFI spec version 2.8 section 3.3 on page 83 defines the Boot Order as:
|
||||
* an array of UINT16s that make up an ordered list of the Boot#### options.
|
||||
*/
|
||||
public class UefiBootOrder {
|
||||
/**
|
||||
* list of UINT16 Boot#### numbers.
|
||||
*/
|
||||
private char[] bootOrder = null;
|
||||
|
||||
/**
|
||||
* Process the BootOrder UEFI variable.
|
||||
*
|
||||
* @param order byte array holding the UEFI boot order variable.
|
||||
*/
|
||||
UefiBootOrder(final byte[] order) {
|
||||
bootOrder = new char[order.length / UefiConstants.SIZE_2];
|
||||
for (int i = 0; i < order.length; i += UefiConstants.SIZE_2) {
|
||||
bootOrder[i / UefiConstants.SIZE_2] =
|
||||
(char) (order[i + 1] * UefiConstants.SIZE_256 + order[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a human readable Boot Order list on single line.
|
||||
*
|
||||
* @return A human readable Boot Order
|
||||
*/
|
||||
public String toString() {
|
||||
StringBuilder orderList = new StringBuilder();
|
||||
orderList.append("BootOrder = ");
|
||||
for (int i = 0; i < bootOrder.length; i++) {
|
||||
orderList.append(String.format("Boot %04d", (int) bootOrder[i]));
|
||||
}
|
||||
//orderList.append("\n");
|
||||
return orderList.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
package hirs.utils.tpm.eventlog.uefi;
|
||||
|
||||
import hirs.utils.HexUtils;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Class to process a UEFI Boot#### variable.
|
||||
* Data is defined using the EFI_LOAD_OptionStructure:
|
||||
* typedef struct _EFI_LOAD_OPTION {
|
||||
* UINT32 Attributes;
|
||||
* UINT16 FilePathListLength;
|
||||
* // CHAR16 Description[];
|
||||
* // EFI_DEVICE_PATH_PROTOCOL FilePathList[];
|
||||
* // UINT8 OptionalData[];
|
||||
* } EFI_LOAD_OPTION;
|
||||
* <p>
|
||||
* No length field for the Description is given
|
||||
* so we need to calculate it by search for a null termination on the Description field
|
||||
* Data following the Description should be an EFI Device Path
|
||||
*/
|
||||
public class UefiBootVariable {
|
||||
/**
|
||||
* Human readable description of the variable.
|
||||
*/
|
||||
private String description = "";
|
||||
/**
|
||||
* Variable attributes.
|
||||
*/
|
||||
private byte[] attributes = null;
|
||||
/**
|
||||
* Firmware memory blob.
|
||||
*/
|
||||
private byte[] blob = null;
|
||||
/**
|
||||
* UEFI Device Path.
|
||||
*/
|
||||
private UefiDevicePath efiDevPath = null;
|
||||
|
||||
/**
|
||||
* UefiBootVariable Constructor.
|
||||
*
|
||||
* @param bootVar byte array holding the boot variable.
|
||||
* @throws java.io.UnsupportedEncodingException if the data fails to parse.
|
||||
*/
|
||||
public UefiBootVariable(final byte[] bootVar) throws UnsupportedEncodingException {
|
||||
attributes = new byte[UefiConstants.SIZE_4];
|
||||
System.arraycopy(bootVar, 0, attributes, 0, UefiConstants.SIZE_4);
|
||||
byte[] blobLen = new byte[UefiConstants.SIZE_2];
|
||||
System.arraycopy(bootVar, UefiConstants.OFFSET_4, blobLen, 0, UefiConstants.SIZE_2);
|
||||
int blobLength = HexUtils.leReverseInt(blobLen);
|
||||
if (blobLength % UefiConstants.SIZE_2 == 0) {
|
||||
blob = new byte[blobLength];
|
||||
} else {
|
||||
blob = new byte[blobLength + 1];
|
||||
}
|
||||
System.arraycopy(bootVar, UefiConstants.OFFSET_6, blob, 0, blobLength);
|
||||
int descLength = getChar16ArrayLength(blob);
|
||||
byte[] desc = new byte[descLength * UefiConstants.SIZE_2];
|
||||
System.arraycopy(bootVar, UefiConstants.OFFSET_6, desc, 0, descLength * UefiConstants.SIZE_2);
|
||||
description = new String(UefiDevicePath.convertChar16tobyteArray(desc), StandardCharsets.UTF_8);
|
||||
// Data following the Description should be EFI Partition Data (EFI_DEVICE_PATH_PROTOCOL)
|
||||
int devPathLength = blobLength;
|
||||
int devPathOffset = UefiConstants.OFFSET_6 + descLength; //attributes+bloblength+desc+length+2
|
||||
byte[] devPath = new byte[devPathLength];
|
||||
System.arraycopy(bootVar, devPathOffset, devPath, 0, devPathLength);
|
||||
efiDevPath = new UefiDevicePath(devPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string that represents a UEFI boot variable.
|
||||
* Some devices have not properly terminated the Description filed with null characters
|
||||
* so garbage bytes are appended to the string that we must strip off.
|
||||
* All non-alpha numeric is stripped from the string.
|
||||
*
|
||||
* @return string that represents a UEFI boot variable.
|
||||
*/
|
||||
public String toString() {
|
||||
StringBuilder bootInfo = new StringBuilder("Description = ");
|
||||
String bootVar = description.replaceAll("[^a-zA-Z_0-0\\s]", ""); // remove all non ascii chars
|
||||
bootInfo.append(bootVar + "\n" + efiDevPath.toString());
|
||||
return bootInfo.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for the first char16 based null character (2 bytes of zeros).
|
||||
* Searches in a given byte array and returns the length of data up to that point in bytes.
|
||||
*
|
||||
* @param data a byte array to search for the data.
|
||||
* @return the length of the data in bytes at the beginning of the byte array.
|
||||
* which was terminated by a null character.
|
||||
*/
|
||||
public int getChar16ArrayLength(final byte[] data) {
|
||||
int count = 0;
|
||||
byte[] nullTerminator = new byte[UefiConstants.SIZE_2];
|
||||
byte[] char16 = new byte[UefiConstants.SIZE_2];
|
||||
nullTerminator[0] = 0;
|
||||
nullTerminator[1] = 0;
|
||||
for (int i = 0; i < data.length; i += UefiConstants.SIZE_2) {
|
||||
char16[0] = data[i];
|
||||
char16[1] = data[i + 1];
|
||||
count++;
|
||||
if (Arrays.equals(nullTerminator, char16)) {
|
||||
return count * UefiConstants.SIZE_2;
|
||||
}
|
||||
}
|
||||
return count * UefiConstants.SIZE_2 + 1;
|
||||
}
|
||||
}
|
@ -0,0 +1,274 @@
|
||||
package hirs.utils.tpm.eventlog.uefi;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* This class contains the String constants that are referenced by UEFI.
|
||||
* It is expected that member properties of this class will expand as
|
||||
* more functionality is added.
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public final class UefiConstants {
|
||||
|
||||
/**
|
||||
* 2 byte size.
|
||||
*/
|
||||
public static final int SIZE_2 = 2;
|
||||
/**
|
||||
* 4 byte size.
|
||||
*/
|
||||
public static final int SIZE_4 = 4;
|
||||
/**
|
||||
* 5 byte size.
|
||||
*/
|
||||
public static final int SIZE_5 = 5;
|
||||
/**
|
||||
* 8 byte size.
|
||||
*/
|
||||
public static final int SIZE_8 = 8;
|
||||
/**
|
||||
* 12 byte size.
|
||||
*/
|
||||
public static final int SIZE_12 = 12;
|
||||
/**
|
||||
* 15 byte size.
|
||||
*/
|
||||
public static final int SIZE_15 = 15;
|
||||
/**
|
||||
* 16 byte size.
|
||||
*/
|
||||
public static final int SIZE_16 = 16;
|
||||
/**
|
||||
* 20 byte size.
|
||||
*/
|
||||
public static final int SIZE_20 = 20;
|
||||
/**
|
||||
* 21 byte size.
|
||||
*/
|
||||
public static final int SIZE_21 = 21;
|
||||
/**
|
||||
* 22 byte size.
|
||||
*/
|
||||
public static final int SIZE_22 = 22;
|
||||
/**
|
||||
* 23 byte size.
|
||||
*/
|
||||
public static final int SIZE_23 = 23;
|
||||
/**
|
||||
* 24 byte size.
|
||||
*/
|
||||
public static final int SIZE_24 = 24;
|
||||
/**
|
||||
* 28 byte size.
|
||||
*/
|
||||
public static final int SIZE_28 = 28;
|
||||
/**
|
||||
* 32 byte size.
|
||||
*/
|
||||
public static final int SIZE_32 = 32;
|
||||
/**
|
||||
* 40 byte size.
|
||||
*/
|
||||
public static final int SIZE_40 = 40;
|
||||
/**
|
||||
* 128 byte size.
|
||||
*/
|
||||
public static final int SIZE_128 = 128;
|
||||
/**
|
||||
* 256 byte size.
|
||||
*/
|
||||
public static final int SIZE_256 = 256;
|
||||
/**
|
||||
* 1 byte offset.
|
||||
*/
|
||||
public static final int OFFSET_1 = 1;
|
||||
/**
|
||||
* 2 byte offset.
|
||||
*/
|
||||
public static final int OFFSET_2 = 2;
|
||||
/**
|
||||
* 3 byte offset.
|
||||
*/
|
||||
public static final int OFFSET_3 = 3;
|
||||
/**
|
||||
* 4 byte offset.
|
||||
*/
|
||||
public static final int OFFSET_4 = 4;
|
||||
/**
|
||||
* 5 byte offset.
|
||||
*/
|
||||
public static final int OFFSET_5 = 5;
|
||||
/**
|
||||
* 6 byte offset.
|
||||
*/
|
||||
public static final int OFFSET_6 = 4;
|
||||
/**
|
||||
* 8 byte offset.
|
||||
*/
|
||||
public static final int OFFSET_8 = 8;
|
||||
/**
|
||||
* 16 byte offset.
|
||||
*/
|
||||
public static final int OFFSET_16 = 16;
|
||||
/**
|
||||
* 20 byte offset.
|
||||
*/
|
||||
public static final int OFFSET_20 = 20;
|
||||
/**
|
||||
* 21 byte offset.
|
||||
*/
|
||||
public static final int OFFSET_21 = 21;
|
||||
/**
|
||||
* 22 byte offset.
|
||||
*/
|
||||
public static final int OFFSET_22 = 22;
|
||||
/**
|
||||
* 24 byte offset.
|
||||
*/
|
||||
public static final int OFFSET_24 = 24;
|
||||
/**
|
||||
* 28 byte offset.
|
||||
*/
|
||||
public static final int OFFSET_28 = 28;
|
||||
/**
|
||||
* 28 byte offset.
|
||||
*/
|
||||
public static final int OFFSET_32 = 32;
|
||||
/**
|
||||
* 40 byte offset.
|
||||
*/
|
||||
public static final int OFFSET_40 = 40;
|
||||
/**
|
||||
* 41 byte offset.
|
||||
*/
|
||||
public static final int OFFSET_41 = 41;
|
||||
/**
|
||||
* Device path terminator.
|
||||
*/
|
||||
public static final int TERMINATOR = 0x7f;
|
||||
/**
|
||||
* Device path end flag.
|
||||
*/
|
||||
public static final int END_FLAG = 0xff;
|
||||
/**
|
||||
* Device Type Hardware.
|
||||
*/
|
||||
public static final int DEV_HW = 0x01;
|
||||
/**
|
||||
* Device Type ACPI.
|
||||
*/
|
||||
public static final int DEV_ACPI = 0x02;
|
||||
/**
|
||||
* Device Type Messaging.
|
||||
*/
|
||||
public static final int DEV_MSG = 0x03;
|
||||
/**
|
||||
* Device Type Media.
|
||||
*/
|
||||
public static final int DEV_MEDIA = 0x04;
|
||||
/**
|
||||
* Device Type Hardware.
|
||||
*/
|
||||
public static final int DEV_BIOS = 0x05;
|
||||
/**
|
||||
* Device Sub-Type USV.
|
||||
*/
|
||||
public static final int DEV_SUB_USB = 0x05;
|
||||
/**
|
||||
* Device Sub-Type Sata.
|
||||
*/
|
||||
public static final int DEV_SUB_SATA = 0x12;
|
||||
/**
|
||||
* Device Sub-Type nvm.
|
||||
*/
|
||||
public static final int DEV_SUB_NVM = 0x17;
|
||||
/**
|
||||
* BIOS Device Path reserved.
|
||||
*/
|
||||
public static final int DEVPATH_BIOS_RESERVED = 0x0;
|
||||
/**
|
||||
* BIOS Device Path for Floppy disks.
|
||||
*/
|
||||
public static final int DEVPATH_BIOS_FLOPPY = 0x01;
|
||||
/**
|
||||
* BIOS Device Path Hard drives.
|
||||
*/
|
||||
public static final int DEVPATH_BIOS_HD = 0x02;
|
||||
/**
|
||||
* BIOS Device Path for CD Drives.
|
||||
*/
|
||||
public static final int DEVPATH_BIOS_CD = 0x03;
|
||||
/**
|
||||
* BIOS Device Path for PCM CIA drives.
|
||||
*/
|
||||
public static final int DEVPATH_BIOS_PCM = 0x04;
|
||||
/**
|
||||
* BIOS Device Path for USB Drives.
|
||||
*/
|
||||
public static final int DEVPATH_BIOS_USB = 0x05;
|
||||
/**
|
||||
* BIOS Device Path for embedded network.
|
||||
*/
|
||||
public static final int DEVPATH_BIOS_EN = 0x06;
|
||||
/**
|
||||
* BIOS Device Path for a Bootstrap Entry Vector (BEV) from an option ROM.
|
||||
*/
|
||||
public static final int DEVPATH_BIOS_BEV = 0x80;
|
||||
/**
|
||||
* Hardware Device Path.
|
||||
*/
|
||||
public static final int DEVPATH_HARWARE = 0x1;
|
||||
/**
|
||||
* 2 byte size.
|
||||
*/
|
||||
public static final int DEVPATH_VENDOR = 0x03;
|
||||
/**
|
||||
* 2 byte size.
|
||||
*/
|
||||
public static final int DEVPATH_FILE = 0x04;
|
||||
/**
|
||||
* PIWG File device path type.
|
||||
*/
|
||||
public static final int DEVPATH_PWIG_FILE = 0x06;
|
||||
/**
|
||||
* PIWG Volume device path type.
|
||||
*/
|
||||
public static final int DEVPATH_PWIG_VOL = 0x07;
|
||||
/**
|
||||
* PC-AT compatible legacy MBR.
|
||||
*/
|
||||
public static final int DRIVE_TYPE_PC_AT = 0x01;
|
||||
/**
|
||||
* GUID Partition Table type.
|
||||
*/
|
||||
public static final int DRIVE_TYPE_GPT = 0x02;
|
||||
/**
|
||||
* Drive Signature type.
|
||||
*/
|
||||
public static final int DRIVE_SIG_NONE = 0x00;
|
||||
/**
|
||||
* Drive Signature type.
|
||||
*/
|
||||
public static final int DRIVE_SIG_32BIT = 0x01;
|
||||
/**
|
||||
* Drive Signature type.
|
||||
*/
|
||||
public static final int DRIVE_SIG_GUID = 0x02;
|
||||
/**
|
||||
* standard byte length.
|
||||
*/
|
||||
public static final int BYTE_LENGTH = 8;
|
||||
/**
|
||||
* standard byte length.
|
||||
*/
|
||||
public static final int ATTRIBUTE_LENGTH = 48;
|
||||
/**
|
||||
* standard byte length.
|
||||
*/
|
||||
public static final int PART_NAME_LENGTH = 56;
|
||||
/**
|
||||
* standard UEFI partition table lengh.
|
||||
*/
|
||||
public static final int UEFI_PT_LENGTH = 72;
|
||||
}
|
@ -0,0 +1,488 @@
|
||||
package hirs.utils.tpm.eventlog.uefi;
|
||||
|
||||
import hirs.utils.HexUtils;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* Class to process EFI_DEVICE_PATH_PROTOCOL which is referred to as the UEFI_DEVICE_PATH
|
||||
* <p>
|
||||
* #define EFI_DEVICE_PATH_PROTOCOL_GUID \09576e91-6d3f-11d2-8e39-00a0c969723b
|
||||
* typedef struct _EFI_DEVICE_PATH_PROTOCOL {
|
||||
* UINT8 Type;
|
||||
* UINT8 SubType;
|
||||
* UINT8 Length[2];
|
||||
* } EFI_DEVICE_PATH_PROTOCOL;
|
||||
* <p>
|
||||
* Where Type is defined in the UEFI spec section 10:
|
||||
* Type 0x01 Hardware Device Path
|
||||
* Type 0x02 ACPI Device Path
|
||||
* Type 0x03 Messaging Device Path
|
||||
* Type 0x04 Media Device Path
|
||||
* Type 0x05 BIOS Boot Specification Device Path
|
||||
* Type 0x7F End of Hardware Device Path
|
||||
* Each Type has a sub-type that may or may no be defined in the section
|
||||
* <p>
|
||||
* Only a few of the SubTypes have been implemented as there are many,
|
||||
* but only those that were reported using the test devices at hand.
|
||||
* Without test patterns, the processing may lead to an un-handled exception
|
||||
*/
|
||||
public class UefiDevicePath {
|
||||
/**
|
||||
* UEFI Device path type.
|
||||
*/
|
||||
@Getter
|
||||
private String type = "";
|
||||
/**
|
||||
* UEFI Device path sub-type.
|
||||
*/
|
||||
private String subType = "";
|
||||
/**
|
||||
* UEFI Device path human readable description.
|
||||
*/
|
||||
private String devPathInfo = "";
|
||||
/**
|
||||
* UEFI Device path length.
|
||||
*/
|
||||
@Getter
|
||||
private int length = 0;
|
||||
|
||||
/**
|
||||
* UEFI Device path constructor.
|
||||
*
|
||||
* @param path byte array holding device path data
|
||||
* @throws java.io.UnsupportedEncodingException if path byte array contains unexpected values
|
||||
*/
|
||||
public UefiDevicePath(final byte[] path) throws UnsupportedEncodingException {
|
||||
devPathInfo = processDevPath(path);
|
||||
byte[] lengthBytes = new byte[UefiConstants.SIZE_2];
|
||||
System.arraycopy(path, UefiConstants.OFFSET_2, lengthBytes, 0, UefiConstants.OFFSET_2);
|
||||
length = HexUtils.leReverseInt(lengthBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the UEFI device sub-type.
|
||||
*
|
||||
* @return uefi sub-type
|
||||
*/
|
||||
public String getSubType() {
|
||||
return subType.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the UEFI device path.
|
||||
* UEFI device path is a collection of EFI_DEVICE_PATH_PROTOCOL structures of variable length.
|
||||
* length must be calculated for each device path and used as an offset.
|
||||
* devPath is terminated by 07f and 0xff per the UEFi spec.
|
||||
*
|
||||
* @param path byte array holding the Device path
|
||||
* @return Human readable string containing the device path description.
|
||||
* @throws java.io.UnsupportedEncodingException
|
||||
*/
|
||||
private String processDevPath(final byte[] path) throws UnsupportedEncodingException {
|
||||
StringBuilder pInfo = new StringBuilder();
|
||||
int devLength = 0, pathOffset = 0, devCount = 0;
|
||||
while (true) {
|
||||
Byte devPath = Byte.valueOf(path[pathOffset]);
|
||||
if ((devPath.intValue() == UefiConstants.TERMINATOR)
|
||||
|| (devPath.intValue() == UefiConstants.END_FLAG)) {
|
||||
break;
|
||||
}
|
||||
if (devCount++ > 0) {
|
||||
pInfo.append("\n");
|
||||
}
|
||||
pInfo.append(processDev(path, pathOffset));
|
||||
devLength = path[pathOffset + UefiConstants.OFFSET_3] * UefiConstants.SIZE_256
|
||||
+ path[pathOffset + UefiConstants.OFFSET_2];
|
||||
pathOffset = pathOffset + devLength;
|
||||
if (pathOffset >= path.length) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return pInfo.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a specific UEFI device path, only limited set of types and subtypes are supported.
|
||||
* Current types processed include Hardware Device Path, ACPI Device Path,
|
||||
* Messaging Device Path, and Media Device Path.
|
||||
*
|
||||
* @param path
|
||||
* @param offset
|
||||
* @return human readable string representing the UEFI device path
|
||||
* @throws java.io.UnsupportedEncodingException
|
||||
*/
|
||||
private String processDev(final byte[] path, final int offset)
|
||||
throws UnsupportedEncodingException {
|
||||
String devInfo = " ";
|
||||
int devPath = path[offset];
|
||||
byte unknownSubType = path[offset + UefiConstants.OFFSET_1];
|
||||
switch (path[0 + offset]) {
|
||||
case UefiConstants.DEV_HW:
|
||||
type = "Hardware Device Path";
|
||||
if (devPath == UefiConstants.DEVPATH_HARWARE) {
|
||||
devInfo += type + ": " + pciSubType(path, offset);
|
||||
}
|
||||
break;
|
||||
case UefiConstants.DEV_ACPI:
|
||||
type = "ACPI Device Path";
|
||||
devInfo += type + ": " + acpiSubType(path, offset);
|
||||
break;
|
||||
case UefiConstants.DEV_MSG:
|
||||
type = "Messaging Device Path";
|
||||
if (path[offset + UefiConstants.OFFSET_1] == UefiConstants.DEV_SUB_SATA) {
|
||||
devInfo += type + ": " + sataSubType(path, offset);
|
||||
} else if (path[offset + UefiConstants.OFFSET_1] == UefiConstants.DEV_SUB_NVM) {
|
||||
devInfo += type + ": " + nvmSubType(path, offset);
|
||||
} else if (path[offset + UefiConstants.OFFSET_1] == UefiConstants.DEV_SUB_USB) {
|
||||
devInfo += type + ": " + usbSubType(path, offset);
|
||||
} else {
|
||||
devInfo += "UEFI Messaging Device Path Type " + Integer.valueOf(unknownSubType);
|
||||
}
|
||||
break;
|
||||
case UefiConstants.DEV_MEDIA:
|
||||
type = "Media Device Path";
|
||||
if (path[offset + UefiConstants.OFFSET_1] == 0x01) {
|
||||
devInfo += type + ": " + hardDriveSubType(path, offset);
|
||||
} else if (path[offset + UefiConstants.OFFSET_1] == UefiConstants.DEVPATH_VENDOR) {
|
||||
devInfo += type + ": " + vendorSubType(path, offset);
|
||||
} else if (path[offset + UefiConstants.OFFSET_1] == UefiConstants.DEVPATH_FILE) {
|
||||
devInfo += type + ": " + filePathSubType(path, offset);
|
||||
} else if (path[offset + UefiConstants.OFFSET_1] == UefiConstants.DEVPATH_PWIG_FILE) {
|
||||
devInfo += type + ": " + piwgFirmVolFile(path, offset);
|
||||
} else if (path[offset + UefiConstants.OFFSET_1] == UefiConstants.DEVPATH_PWIG_VOL) {
|
||||
devInfo += type + ": " + piwgFirmVolPath(path, offset);
|
||||
} else {
|
||||
devInfo += "UEFI Media Device Path Type " + Integer.valueOf(unknownSubType);
|
||||
}
|
||||
break;
|
||||
case UefiConstants.DEV_BIOS:
|
||||
type = "BIOS Device Path";
|
||||
devInfo += type + ": " + biosDevicePath(path, offset);
|
||||
break;
|
||||
case UefiConstants.TERMINATOR:
|
||||
devInfo += "End of Hardware Device Path";
|
||||
break;
|
||||
default:
|
||||
devInfo += "UEFI Device Path Type " + Integer.valueOf(unknownSubType);
|
||||
}
|
||||
return devInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* processes the ACPI UEFI device subtype.
|
||||
*
|
||||
* @param path
|
||||
* @param offset
|
||||
* @return acpi device info
|
||||
*/
|
||||
private String acpiSubType(final byte[] path, final int offset) {
|
||||
subType = "";
|
||||
switch (path[offset + UefiConstants.OFFSET_1]) {
|
||||
case 0x01:
|
||||
subType = "(Short): ";
|
||||
subType += acpiShortSubType(path, offset);
|
||||
break;
|
||||
case 0x02:
|
||||
subType = "Expanded ACPI Device Path";
|
||||
break;
|
||||
default:
|
||||
subType = "Invalid ACPI Device Path sub type";
|
||||
}
|
||||
return subType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the ACPI short subtype.
|
||||
*
|
||||
* @param path
|
||||
* @param offset
|
||||
* @return short acpi info.
|
||||
*/
|
||||
private String acpiShortSubType(final byte[] path, final int offset) {
|
||||
subType = "";
|
||||
byte[] hid = new byte[UefiConstants.SIZE_4];
|
||||
System.arraycopy(path, UefiConstants.OFFSET_4 + offset, hid, 0, UefiConstants.SIZE_4);
|
||||
subType += "_HID = " + HexUtils.byteArrayToHexString(hid);
|
||||
System.arraycopy(path, 2 * UefiConstants.SIZE_4 + offset, hid, 0, UefiConstants.SIZE_4);
|
||||
subType += "_UID = " + HexUtils.byteArrayToHexString(hid);
|
||||
return subType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the PCI subType.
|
||||
*
|
||||
* @param path
|
||||
* @param offset
|
||||
* @return pci device info.
|
||||
*/
|
||||
private String pciSubType(final byte[] path, final int offset) {
|
||||
subType = "PCI: PCI Function Number = ";
|
||||
subType += String.format("0x%x", path[offset + UefiConstants.SIZE_4]);
|
||||
subType += " PCI Device Number = ";
|
||||
subType += String.format("0x%x", path[offset + UefiConstants.SIZE_5]);
|
||||
return subType;
|
||||
}
|
||||
|
||||
/**
|
||||
* processes the SATA sub type.
|
||||
*
|
||||
* @param path
|
||||
* @param offset
|
||||
* @return SATA drive info.
|
||||
*/
|
||||
private String sataSubType(final byte[] path, final int offset) {
|
||||
subType = "SATA: HBA Port Number = ";
|
||||
byte[] data = new byte[UefiConstants.SIZE_2];
|
||||
System.arraycopy(path, UefiConstants.OFFSET_4 + offset, data, 0, UefiConstants.SIZE_2);
|
||||
subType += HexUtils.byteArrayToHexString(data);
|
||||
System.arraycopy(path, UefiConstants.OFFSET_6 + offset, data, 0, UefiConstants.SIZE_2);
|
||||
subType += " Port Multiplier = " + HexUtils.byteArrayToHexString(data);
|
||||
System.arraycopy(path, UefiConstants.OFFSET_8 + offset, data, 0, UefiConstants.SIZE_2);
|
||||
subType += " Logical Unit Number = " + HexUtils.byteArrayToHexString(data);
|
||||
return subType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the hard drive sub type.
|
||||
*
|
||||
* @param path
|
||||
* @param offset
|
||||
* @return hard drive info.
|
||||
*/
|
||||
private String hardDriveSubType(final byte[] path, final int offset) {
|
||||
subType = "Partition Number = ";
|
||||
byte[] partnumber = new byte[UefiConstants.SIZE_4];
|
||||
System.arraycopy(path, UefiConstants.OFFSET_4 + offset, partnumber, 0, UefiConstants.SIZE_4);
|
||||
subType += HexUtils.byteArrayToHexString(partnumber);
|
||||
byte[] data = new byte[UefiConstants.SIZE_8];
|
||||
System.arraycopy(path, UefiConstants.OFFSET_8 + offset, data, 0, UefiConstants.SIZE_8);
|
||||
subType += " Partition Start = " + HexUtils.byteArrayToHexString(data);
|
||||
System.arraycopy(path, UefiConstants.OFFSET_16 + offset, data, 0, UefiConstants.SIZE_8);
|
||||
subType += " Partition Size = " + HexUtils.byteArrayToHexString(data);
|
||||
byte[] signature = new byte[UefiConstants.SIZE_16];
|
||||
System.arraycopy(path, UefiConstants.OFFSET_24 + offset, signature, 0, UefiConstants.SIZE_16);
|
||||
subType += "\n Partition Signature = ";
|
||||
if (path[UefiConstants.OFFSET_41 + offset] == UefiConstants.DRIVE_SIG_NONE) {
|
||||
subType += "None";
|
||||
} else if (path[UefiConstants.OFFSET_41 + offset] == UefiConstants.DRIVE_SIG_32BIT) {
|
||||
subType += HexUtils.byteArrayToHexString(signature);
|
||||
} else if (path[UefiConstants.OFFSET_41 + offset] == UefiConstants.DRIVE_SIG_GUID) {
|
||||
UefiGuid guid = new UefiGuid(signature);
|
||||
subType += guid.toString();
|
||||
} else {
|
||||
subType += "invalid partition signature type";
|
||||
}
|
||||
subType += " Partition Format = ";
|
||||
if (path[UefiConstants.OFFSET_40 + offset] == UefiConstants.DRIVE_TYPE_PC_AT) {
|
||||
subType += " PC-AT compatible legacy MBR";
|
||||
} else if (path[UefiConstants.OFFSET_40 + offset] == UefiConstants.DRIVE_TYPE_GPT) {
|
||||
subType += " GUID Partition Table";
|
||||
} else {
|
||||
subType += " Invalid partition table type";
|
||||
}
|
||||
return subType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the File path sub type.
|
||||
*
|
||||
* @param path
|
||||
* @param offset
|
||||
* @return file path info.
|
||||
*/
|
||||
private String filePathSubType(final byte[] path, final int offset) {
|
||||
subType = "File Path = ";
|
||||
byte[] lengthBytes = new byte[UefiConstants.SIZE_2];
|
||||
System.arraycopy(path, 2 + offset, lengthBytes, 0, UefiConstants.SIZE_2);
|
||||
int subTypeLength = HexUtils.leReverseInt(lengthBytes);
|
||||
byte[] filePath = new byte[subTypeLength];
|
||||
System.arraycopy(path, UefiConstants.OFFSET_4 + offset, filePath, 0, subTypeLength);
|
||||
byte[] fileName = convertChar16tobyteArray(filePath);
|
||||
subType += new String(fileName, StandardCharsets.UTF_8);
|
||||
return subType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a vendor sub-type on a Media Type.
|
||||
* Length of this structure in bytes. Length is 20 + n bytes
|
||||
* Vendor-assigned GUID that defines the data that follows.
|
||||
* Vendor-defined variable size data.
|
||||
*
|
||||
* @param path
|
||||
* @param offset
|
||||
* @return vendor device info.
|
||||
*/
|
||||
private String vendorSubType(final byte[] path, final int offset) {
|
||||
subType = "Vendor Subtype GUID = ";
|
||||
byte[] lengthBytes = new byte[UefiConstants.SIZE_2];
|
||||
System.arraycopy(path, UefiConstants.OFFSET_2 + offset, lengthBytes, 0, UefiConstants.SIZE_2);
|
||||
int subTypeLength = HexUtils.leReverseInt(lengthBytes);
|
||||
byte[] guidData = new byte[UefiConstants.SIZE_16];
|
||||
System.arraycopy(path, UefiConstants.OFFSET_4 + offset, guidData, 0, UefiConstants.SIZE_16);
|
||||
UefiGuid guid = new UefiGuid(guidData);
|
||||
subType += guid.toString() + " ";
|
||||
if (subTypeLength - UefiConstants.SIZE_16 > 0) {
|
||||
byte[] vendorData = new byte[subTypeLength - UefiConstants.SIZE_16];
|
||||
System.arraycopy(path, UefiConstants.OFFSET_20
|
||||
+ offset, vendorData, 0, subTypeLength - UefiConstants.SIZE_16);
|
||||
subType += " : Vendor Data = " + HexUtils.byteArrayToHexString(vendorData);
|
||||
} else {
|
||||
subType += " : No Vendor Data pesent";
|
||||
}
|
||||
return subType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns USB device info.
|
||||
* UEFI Specification, Version 2.8.
|
||||
*
|
||||
* @param path
|
||||
* @param offset
|
||||
* @return USB device info.
|
||||
*/
|
||||
private String usbSubType(final byte[] path, final int offset) {
|
||||
subType = " USB ";
|
||||
subType += " port = " + Integer.valueOf(path[offset + UefiConstants.OFFSET_4]);
|
||||
subType += " interface = " + Integer.valueOf(path[offset + UefiConstants.OFFSET_5]);
|
||||
byte[] lengthBytes = new byte[UefiConstants.SIZE_2];
|
||||
System.arraycopy(path, UefiConstants.OFFSET_2 + offset, lengthBytes, 0, UefiConstants.SIZE_2);
|
||||
int subTypeLength = HexUtils.leReverseInt(lengthBytes);
|
||||
byte[] usbData = new byte[subTypeLength];
|
||||
System.arraycopy(path, UefiConstants.OFFSET_4 + offset, usbData, 0, subTypeLength);
|
||||
// Todo add further USB processing ...
|
||||
return subType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns NVM device info.
|
||||
* UEFI Specification, Version 2.8.
|
||||
* Name space Identifier (NSID) and IEEE Extended Unique Identifier (EUI-64):
|
||||
* See Links to UEFI Related Documents
|
||||
* (http://uefi.org/uefi under the headings NVM Express Specification.
|
||||
*
|
||||
* @param path
|
||||
* @param offset
|
||||
* @return NVM device info.
|
||||
*/
|
||||
private String nvmSubType(final byte[] path, final int offset) {
|
||||
subType = "NVM Express Namespace = ";
|
||||
byte[] lengthBytes = new byte[UefiConstants.SIZE_2];
|
||||
System.arraycopy(path, UefiConstants.OFFSET_2 + offset, lengthBytes, 0, UefiConstants.SIZE_2);
|
||||
int subTypeLength = HexUtils.leReverseInt(lengthBytes);
|
||||
byte[] nvmData = new byte[subTypeLength];
|
||||
System.arraycopy(path, UefiConstants.OFFSET_4 + offset, nvmData, 0, subTypeLength);
|
||||
subType += HexUtils.byteArrayToHexString(nvmData);
|
||||
return subType;
|
||||
}
|
||||
|
||||
/**
|
||||
* BIOS Device Type definition.
|
||||
* From Appendix A of the BIOS Boot Specification.
|
||||
* Only processes the Device type.
|
||||
* Status bootHandler pointer, and description String pointer are ignored.
|
||||
*
|
||||
* @param path byte array holding the device path.
|
||||
* @return String that represents the UEFI defined BIOS Device Type.
|
||||
*/
|
||||
private String biosDevicePath(final byte[] path, final int offset) {
|
||||
subType = "Legacy BIOS : Type = ";
|
||||
Byte pathType = Byte.valueOf(path[offset + 1]);
|
||||
switch (pathType.intValue()) {
|
||||
case UefiConstants.DEVPATH_BIOS_RESERVED:
|
||||
subType += "Reserved";
|
||||
break;
|
||||
case UefiConstants.DEVPATH_BIOS_FLOPPY:
|
||||
subType += "Floppy";
|
||||
break;
|
||||
case UefiConstants.DEVPATH_BIOS_HD:
|
||||
subType += "Hard Disk";
|
||||
break;
|
||||
case UefiConstants.DEVPATH_BIOS_CD:
|
||||
subType += "CD-ROM";
|
||||
break;
|
||||
case UefiConstants.DEVPATH_BIOS_PCM:
|
||||
subType += "PCMCIA";
|
||||
break;
|
||||
case UefiConstants.DEVPATH_BIOS_USB:
|
||||
subType += "USB";
|
||||
break;
|
||||
case UefiConstants.DEVPATH_BIOS_EN:
|
||||
subType += "Embedded network";
|
||||
break;
|
||||
case UefiConstants.DEVPATH_BIOS_BEV:
|
||||
subType +=
|
||||
"Bootstrap Entry Vector (BEV) from an Option ROM";
|
||||
break;
|
||||
default:
|
||||
subType += "Unknown";
|
||||
break;
|
||||
}
|
||||
return subType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns PIWG firmware volume info.
|
||||
* UEFI Specification, Version 2.8.
|
||||
* PIWG Firmware File Section 10.3.5.6:
|
||||
* Contents are defined in the UEFI PI Specification.
|
||||
*
|
||||
* @param path
|
||||
* @param offset
|
||||
* @return String that represents the PIWG Firmware Volume Path
|
||||
*/
|
||||
private String piwgFirmVolFile(final byte[] path, final int offset) {
|
||||
subType = "PIWG Firmware File ";
|
||||
byte[] guidData = new byte[UefiConstants.SIZE_16];
|
||||
System.arraycopy(path, UefiConstants.OFFSET_4 + offset, guidData, 0, UefiConstants.SIZE_16);
|
||||
UefiGuid guid = new UefiGuid(guidData);
|
||||
subType += guid.toString();
|
||||
return subType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns PIWG firmware file info.
|
||||
* UEFI Specification, Version 2.8.
|
||||
* PIWG Firmware Volume Section 10.3.5.7:
|
||||
* Contents are defined in the UEFI PI Specification.
|
||||
*
|
||||
* @param path
|
||||
* @param offset
|
||||
* @return String that represents the PIWG Firmware Volume Path
|
||||
*/
|
||||
private String piwgFirmVolPath(final byte[] path, final int offset) {
|
||||
subType = "PIWG Firmware Volume ";
|
||||
byte[] guidData = new byte[UefiConstants.SIZE_16];
|
||||
System.arraycopy(path, UefiConstants.OFFSET_4 + offset, guidData, 0, UefiConstants.SIZE_16);
|
||||
UefiGuid guid = new UefiGuid(guidData);
|
||||
subType += guid.toString();
|
||||
return subType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string that represents the UEFi Device path.
|
||||
*
|
||||
* @return UEFi Device path.
|
||||
*/
|
||||
public String toString() {
|
||||
return devPathInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts from a char array to byte array.
|
||||
* Removes the upper byte (typically set to 0) of each char.
|
||||
*
|
||||
* @param data Character array.
|
||||
* @return byte array.
|
||||
*/
|
||||
public static byte[] convertChar16tobyteArray(final byte[] data) {
|
||||
byte[] hexdata = new byte[data.length];
|
||||
int j = 0;
|
||||
for (int i = 0; i < data.length; i = i + UefiConstants.SIZE_2) {
|
||||
hexdata[j++] = data[i];
|
||||
}
|
||||
return hexdata;
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
package hirs.utils.tpm.eventlog.uefi;
|
||||
|
||||
import hirs.utils.HexUtils;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* Class to process the PFP defined UEFI_PLATFORM_FIRMWARE_BLOB structure.
|
||||
* <p>
|
||||
* typedef struct tdUEFI_PLATFORM_FIRMWARE_BLOB {
|
||||
* UEFI_PHYSICAL_ADDRESS BlobBase;
|
||||
* UINT64 BlobLength;
|
||||
* } UEFI_PLATFORM_FIRMWARE_BLOB;
|
||||
*/
|
||||
public class UefiFirmware {
|
||||
private boolean bError = false;
|
||||
/**
|
||||
* byte array holding the firmwares physical address.
|
||||
*/
|
||||
private byte[] physicalAddress = null;
|
||||
/**
|
||||
* byte array holding the uefi address length.
|
||||
*/
|
||||
private byte[] addressLength = null;
|
||||
/**
|
||||
* uefi physical address.
|
||||
*/
|
||||
@Getter
|
||||
private int physicalBlobAddress = 0;
|
||||
/**
|
||||
* uefi address length.
|
||||
*/
|
||||
@Getter
|
||||
private int blobLength = 0;
|
||||
|
||||
/**
|
||||
* UefiFirmware constructor.
|
||||
*
|
||||
* @param blob byte array holding a Firmware Blob.
|
||||
*/
|
||||
public UefiFirmware(final byte[] blob) {
|
||||
if (blob.length != UefiConstants.SIZE_16) {
|
||||
bError = true;
|
||||
} else {
|
||||
physicalAddress = new byte[UefiConstants.SIZE_8];
|
||||
addressLength = new byte[UefiConstants.SIZE_8];
|
||||
System.arraycopy(blob, 0, physicalAddress, 0, UefiConstants.SIZE_8);
|
||||
System.arraycopy(blob, UefiConstants.SIZE_8, addressLength, 0, UefiConstants.SIZE_8);
|
||||
byte[] lelength = HexUtils.leReverseByte(addressLength);
|
||||
BigInteger bigIntLength = new BigInteger(lelength);
|
||||
blobLength = bigIntLength.intValue();
|
||||
byte[] leAddress = HexUtils.leReverseByte(physicalAddress);
|
||||
BigInteger bigIntAddress = new BigInteger(leAddress);
|
||||
physicalBlobAddress = bigIntAddress.intValue();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a description of the firmware blobs location.
|
||||
*
|
||||
* @return a description of the the firmware blobs location.
|
||||
*/
|
||||
public String toString() {
|
||||
StringBuilder blobInfo = new StringBuilder();
|
||||
if (!bError) {
|
||||
blobInfo.append(String.format(" Platform Firmware Blob Address = %s",
|
||||
Integer.toHexString(physicalBlobAddress)));
|
||||
blobInfo.append(String.format(" length = %d", blobLength));
|
||||
} else {
|
||||
blobInfo.append(" Invalid Firmware Blob event encountered");
|
||||
}
|
||||
return blobInfo.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,193 @@
|
||||
package hirs.utils.tpm.eventlog.uefi;
|
||||
|
||||
import com.eclipsesource.json.JsonObject;
|
||||
import hirs.utils.HexUtils;
|
||||
import hirs.utils.JsonUtils;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Path;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Class to process GUID per the UEFI specification
|
||||
* GUIDs are essentially UUID as defined by RFC-1422, however Microsoft refers to GUIDS.
|
||||
*/
|
||||
public class UefiGuid {
|
||||
/**
|
||||
* number of 100ns intervals since UUID Epoch.
|
||||
*/
|
||||
private static final long UUID_EPOCH_INTERVALS = 0x01b21dd213814000L;
|
||||
/**
|
||||
* used for conversion to uuid time.
|
||||
*/
|
||||
private static final int UUID_EPOCH_DIVISOR = 10000;
|
||||
|
||||
private static final Path JSON_PATH = FileSystems.getDefault().getPath("/opt",
|
||||
"hirs", "default-properties", "vendor-table.json");
|
||||
private JsonObject uefiVendorRef;
|
||||
/**
|
||||
* guid byte array.
|
||||
*/
|
||||
private byte[] guid;
|
||||
/**
|
||||
* UUID object.
|
||||
*/
|
||||
private UUID uuid;
|
||||
|
||||
/**
|
||||
* UefiGUID constructor.
|
||||
*
|
||||
* @param guidBytes byte array holding a valid guid.
|
||||
*/
|
||||
public UefiGuid(final byte[] guidBytes) {
|
||||
guid = new byte[UefiConstants.SIZE_16];
|
||||
System.arraycopy(guidBytes, 0, guid, 0, UefiConstants.SIZE_16);
|
||||
uuid = processGuid(guidBytes);
|
||||
uefiVendorRef = JsonUtils.getSpecificJsonObject(JSON_PATH, "VendorTable");
|
||||
}
|
||||
|
||||
/**
|
||||
* UefiGUID constructor.
|
||||
*
|
||||
* @param guidBytes byte array holding a valid guid.
|
||||
* @param vendorPathString string path for vendor
|
||||
*/
|
||||
public UefiGuid(final byte[] guidBytes, final Path vendorPathString) {
|
||||
guid = new byte[UefiConstants.SIZE_16];
|
||||
System.arraycopy(guidBytes, 0, guid, 0, UefiConstants.SIZE_16);
|
||||
uuid = processGuid(guidBytes);
|
||||
uefiVendorRef = JsonUtils.getSpecificJsonObject(vendorPathString,
|
||||
"VendorTable");
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a GUID with a byte array to a RFC-1422 UUID object.
|
||||
* Assumes a MS format and converts to Big Endian format used by most others , including Linux
|
||||
* Matched uuids found in /sys/firmware/efi/efivars on Centos 7.
|
||||
*/
|
||||
private static UUID processGuid(final byte[] guid) {
|
||||
byte[] msb1 = new byte[UefiConstants.SIZE_4];
|
||||
System.arraycopy(guid, 0, msb1, 0, UefiConstants.SIZE_4);
|
||||
byte[] msb1r = HexUtils.leReverseByte(msb1);
|
||||
byte[] msb2 = new byte[UefiConstants.SIZE_4];
|
||||
System.arraycopy(guid, UefiConstants.OFFSET_4, msb2, 0, UefiConstants.SIZE_4);
|
||||
byte[] msb2r = HexUtils.leReverseByte(msb2);
|
||||
byte[] msb2rs = new byte[UefiConstants.SIZE_4];
|
||||
System.arraycopy(msb2r, 0, msb2rs, UefiConstants.OFFSET_2, UefiConstants.SIZE_2);
|
||||
System.arraycopy(msb2r, UefiConstants.OFFSET_2, msb2rs, 0, UefiConstants.SIZE_2);
|
||||
byte[] msbt = new byte[UefiConstants.SIZE_8];
|
||||
System.arraycopy(msb1r, 0, msbt, 0, UefiConstants.SIZE_4);
|
||||
System.arraycopy(msb2rs, 0, msbt, UefiConstants.OFFSET_4, UefiConstants.SIZE_4);
|
||||
long msbl = new BigInteger(msbt).longValue();
|
||||
byte[] lsb = new byte[UefiConstants.SIZE_8];
|
||||
System.arraycopy(guid, UefiConstants.OFFSET_8, lsb, 0, UefiConstants.SIZE_8);
|
||||
long lsbl = new BigInteger(lsb).longValue();
|
||||
return new UUID(msbl, lsbl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the standard GUID length.
|
||||
*
|
||||
* @return guid length
|
||||
*/
|
||||
public static int getGuidLength() {
|
||||
return UefiConstants.SIZE_16;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a String that represents a specification name referenced by the
|
||||
* EFI_CONFIGURATION_TABLE VendorGUID field. For structure of
|
||||
* EFI_CONFIGURATION_TABLE type, the UEFI specification has set of GUIDs
|
||||
* published that represent standards that one can find further information on
|
||||
* the configuration table being referenced.
|
||||
* Refer to section 4.6 of UEFI spec v 2.8, page 101.
|
||||
*
|
||||
* @return A String of major UUID parameters
|
||||
*/
|
||||
public String getVendorTableReference() {
|
||||
return getVendorTableReference(uuid.toString().toLowerCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a String that represents a specification name referenced by the
|
||||
* EFI_CONFIGURATION_TABLE VendorGUID field. For structure of
|
||||
* EFI_CONFIGURATION_TABLE type, the UEFI specification has set of GUIDs
|
||||
* published that represent standards that one can find further
|
||||
* information on the configuration table being referenced.
|
||||
* Refer to section 4.6 of UEFI spec v 2.8, page 101.
|
||||
*
|
||||
* @param lookupValue specific value to look up
|
||||
* @return A String of major UUID parameters
|
||||
*/
|
||||
public String getVendorTableReference(final String lookupValue) {
|
||||
return uefiVendorRef.getString(lookupValue, "Unknown GUID reference");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string of the entity that the UUID represents.
|
||||
* Contains a Vendor String lookup on the UUID.
|
||||
*
|
||||
* @return UUID description.
|
||||
*/
|
||||
public String toString() {
|
||||
return String.format("%s : %s", uuid.toString(), getVendorTableReference());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string of the entity that the UUID represents.
|
||||
* Does not contain a vendor lookup on the UUID.
|
||||
*
|
||||
* @return UUID description.
|
||||
*/
|
||||
public String toStringNoLookup() {
|
||||
return uuid.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string of the entity that the UUID represents.
|
||||
* Does not contain a vendor lookup on the UUID.
|
||||
*
|
||||
* @param guid byte array holding the guid data.
|
||||
* @return true if the UUID has a valid structure.
|
||||
*/
|
||||
public static boolean isValidUUID(final byte[] guid) {
|
||||
boolean valid = false;
|
||||
UUID tmpUuid = processGuid(guid);
|
||||
if (tmpUuid.toString().length() != 0) {
|
||||
valid = true;
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the uuid is the test or Empty UUID ("00000000-0000-0000-0000-000000000000").
|
||||
*
|
||||
* @return true if the uuid is the Empty UUID, false if not
|
||||
*/
|
||||
public boolean isEmptyUUID() {
|
||||
return uuid.toString().equals("00000000-0000-0000-0000-000000000000");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the uuid is the Empty UUID or an unknown.
|
||||
*
|
||||
* @return true if the uuid is the Empty UUID, false if not
|
||||
*/
|
||||
public boolean isUnknownUUID() {
|
||||
if (getVendorTableReference().equals("Unknown GUID reference")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the timestamp within a time based GUID.
|
||||
*
|
||||
* @param uuid uuid object
|
||||
* @return long representing the time stamp from the GUID
|
||||
*/
|
||||
public long getTimeFromUUID(final UUID uuid) {
|
||||
return (uuid.timestamp() - UUID_EPOCH_INTERVALS) / UUID_EPOCH_DIVISOR;
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
package hirs.utils.tpm.eventlog.uefi;
|
||||
|
||||
import hirs.utils.HexUtils;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* Class to process EFI Partitions for EFI Partition tables defined in UEFI section 5.3.3
|
||||
* typedef struct {
|
||||
* EFI_GUID PartitionTypeGUID;
|
||||
* EFI_GUID UniquePartitionGUID;
|
||||
* EFI_LBA StartingLBA; // Same as UINT64.
|
||||
* EFI_LBA EndingLBA;
|
||||
* UINT64 Attributes;
|
||||
* CHAR16 PartitionName[36]; // 36 CHAR16 = 72 Bytes
|
||||
* } EFI_PARTITION_ENTRY;
|
||||
* <p>
|
||||
* UEFI Table 23. Defined GPT Partition Entry - Partition Type GUIDs (implemented in EFIGui.java)
|
||||
* Examples:
|
||||
* Unused Entry 00000000-0000-0000-0000-000000000000
|
||||
* EFI System Partition C12A7328-F81F-11D2-BA4B-00A0C93EC93B
|
||||
* Partition containing a legacy MBR 024DEE41-33E7-11D3-9D69-0008C781F39F
|
||||
* Linux filesystem data 0FC63DAF-8483-4772-8E79-3D69D8477DE4
|
||||
* Logical Volume Manager (LVM) partition E6D6D379-F507-44C2-A23C-238F2A3DF928
|
||||
* Plain dm-crypt partition 7FFEC5C9-2D00-49B7-8941-3EA10A5586B7
|
||||
* Root partition (x86-64) 4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709
|
||||
* RAID partition A19D880F-05FC-4D3B-A006-743F0F84911E
|
||||
* LUKS partition CA7D7CCB-63ED-4C53-861C-1742536059CC
|
||||
* <p>
|
||||
* linux commands to check uuids:
|
||||
* blkid list //unique parition guids
|
||||
* ls /dev/disk/by-partuuid
|
||||
*/
|
||||
@Getter
|
||||
public class UefiPartition {
|
||||
private UefiGuid partitionTypeGUID = null;
|
||||
private UefiGuid uniquePartitionGUID = null;
|
||||
private String partitionName = "";
|
||||
private String attributes = "";
|
||||
|
||||
/**
|
||||
* Processes a UEFI defined partition entry.
|
||||
*
|
||||
* @param table byte array holding the partition table.
|
||||
*/
|
||||
public UefiPartition(final byte[] table) {
|
||||
byte[] partitionGuidBytes = new byte[UefiConstants.SIZE_16];
|
||||
System.arraycopy(table, 0, partitionGuidBytes, 0, UefiConstants.SIZE_16);
|
||||
partitionTypeGUID = new UefiGuid(partitionGuidBytes);
|
||||
byte[] uniquePartGuidBytes = new byte[UefiConstants.SIZE_16];
|
||||
System.arraycopy(table, UefiConstants.SIZE_16, uniquePartGuidBytes, 0, UefiConstants.SIZE_16);
|
||||
uniquePartitionGUID = new UefiGuid(uniquePartGuidBytes);
|
||||
byte[] attributeBytes = new byte[UefiConstants.SIZE_8];
|
||||
System.arraycopy(table, UefiConstants.ATTRIBUTE_LENGTH, attributeBytes,
|
||||
0, UefiConstants.SIZE_8);
|
||||
attributes = HexUtils.byteArrayToHexString(attributeBytes);
|
||||
byte[] partitionNameBytes = new byte[UefiConstants.UEFI_PT_LENGTH];
|
||||
System.arraycopy(table, UefiConstants.PART_NAME_LENGTH, partitionNameBytes,
|
||||
0, UefiConstants.UEFI_PT_LENGTH);
|
||||
byte[] pName = convertChar16tobyteArray(partitionNameBytes);
|
||||
partitionName = new String(pName, StandardCharsets.UTF_8).trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a description of the partition.
|
||||
*
|
||||
* @return partition description.
|
||||
*/
|
||||
public String toString() {
|
||||
String partitionInfo = "";
|
||||
partitionInfo += " Partition Name : " + partitionName + "\n";
|
||||
partitionInfo += " Partition Type GUID : " + partitionTypeGUID.toString() + "\n";
|
||||
partitionInfo += " Unique Partition GUID : " + uniquePartitionGUID.toStringNoLookup() + "\n";
|
||||
partitionInfo += " Attributes : " + attributes;
|
||||
return partitionInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies character array to a byte by removing upper byte of character array.
|
||||
*
|
||||
* @param data input char array
|
||||
* @return byte array
|
||||
*/
|
||||
private byte[] convertChar16tobyteArray(final byte[] data) {
|
||||
byte[] hexdata = new byte[data.length];
|
||||
int j = 0;
|
||||
for (int i = 0; i < data.length; i += 2) {
|
||||
hexdata[j++] = data[i];
|
||||
}
|
||||
return hexdata;
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package hirs.utils.tpm.eventlog.uefi;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* Class that processes the UEFI defined SecureBoot Variable.
|
||||
* Currently this variable only specifies if SecureBoot is on/off.
|
||||
*/
|
||||
public class UefiSecureBoot {
|
||||
/**
|
||||
* Variable value.
|
||||
*/
|
||||
@Getter
|
||||
private int secureBootVariable = 0;
|
||||
/**
|
||||
* Error flag.
|
||||
*/
|
||||
private boolean berror = false;
|
||||
/**
|
||||
* Human readable description.
|
||||
*/
|
||||
private String info = "";
|
||||
|
||||
/**
|
||||
* Constructor to process the EFI Secure Boot Variable.
|
||||
*
|
||||
* @param data UEFI variable data.
|
||||
*/
|
||||
public UefiSecureBoot(final byte[] data) {
|
||||
if (data.length == 0) {
|
||||
berror = true;
|
||||
info = "Unknown State: Empty Secure Boot variable\n";
|
||||
} else {
|
||||
secureBootVariable = new BigInteger(data).intValue();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a human readable value for the Secure Boot variable.
|
||||
*
|
||||
* @return Human readable description.
|
||||
*/
|
||||
public String toString() {
|
||||
if (!berror) {
|
||||
if (secureBootVariable == 1) {
|
||||
info += " Secure Boot is enabled ";
|
||||
} else if (secureBootVariable == 0) {
|
||||
info += " Secure Boot is NOT enabled ";
|
||||
} else {
|
||||
info += " Unkown State: Secure Variable is undefined ";
|
||||
}
|
||||
}
|
||||
return info;
|
||||
}
|
||||
}
|
@ -0,0 +1,172 @@
|
||||
package hirs.utils.tpm.eventlog.uefi;
|
||||
|
||||
import hirs.utils.HexUtils;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateException;
|
||||
|
||||
/**
|
||||
* Class for processing the contents of a Secure Boot DB or DBX contents.
|
||||
* used for EFIVariables associated with Secure Boot
|
||||
* as defined by Section 32.4.1 Signature Database from the UEFI 2.8 specification
|
||||
* <p>
|
||||
* typedef struct _EFI_SIGNATURE_DATA {
|
||||
* EFI_GUID SignatureOwner;
|
||||
* UINT8 SignatureData[...];
|
||||
* } EFI_SIGNATURE_DATA;
|
||||
* <p>
|
||||
* However page 1729 0f UEFI 2.8 implies that SignatureListType of EFI_CERT_SHA256_GUID
|
||||
* will contain the "the SHA-256 hash of the binary".
|
||||
* So the Signature Data depends upon the Signature Type from the EFI Signature List.
|
||||
*/
|
||||
public class UefiSignatureData {
|
||||
/**
|
||||
* UEFI Certificate GUID.
|
||||
*/
|
||||
private byte[] guid = new byte[UefiConstants.SIZE_16];
|
||||
/**
|
||||
* UEFI Signature data.
|
||||
*/
|
||||
private byte[] sigData = null;
|
||||
/**
|
||||
* UEFI Certificate object .
|
||||
*/
|
||||
@Getter
|
||||
private UefiX509Cert cert = null;
|
||||
/**
|
||||
* UEFI Certificate GUID.
|
||||
*/
|
||||
@Getter
|
||||
private UefiGuid efiVarGuid = null;
|
||||
/**
|
||||
* UEFI Signature type.
|
||||
*/
|
||||
@Getter
|
||||
private UefiGuid signatureType = null;
|
||||
/**
|
||||
* UEFI Signature validity.
|
||||
*/
|
||||
@Getter
|
||||
private boolean valid = false;
|
||||
/**
|
||||
* UEFI Certificate SHA1 hash.
|
||||
*/
|
||||
private byte[] binaryHash = new byte[UefiConstants.SIZE_40];
|
||||
/**
|
||||
* UEFI Signature data status.
|
||||
*/
|
||||
@Getter
|
||||
private String status = "Signature Data contains a valid Certificate";
|
||||
|
||||
/**
|
||||
* UefiSignatureData constructor.
|
||||
*
|
||||
* @param inputStream The Signature data.
|
||||
* @param sigType UEFI defined signature type.
|
||||
* @throws java.io.IOException if there's an problem reading the input stream.
|
||||
* @throws java.security.cert.CertificateException If there a problem parsing the X509 certificate.
|
||||
* @throws java.security.NoSuchAlgorithmException if there's a problem hashing the certificate.
|
||||
*/
|
||||
UefiSignatureData(final ByteArrayInputStream inputStream, final UefiGuid sigType)
|
||||
throws IOException, CertificateException, NoSuchAlgorithmException {
|
||||
signatureType = sigType;
|
||||
// UEFI spec section 32.5.3.3 states that SignatureListType of EFI_CERT_SHA256_GUID
|
||||
// only contains a hash, not a cert
|
||||
if (sigType.getVendorTableReference().equals("EFI_CERT_SHA256_GUID")) {
|
||||
inputStream.read(guid);
|
||||
efiVarGuid = new UefiGuid(guid);
|
||||
// Should be a SHA256 hash of the "binary"
|
||||
inputStream.read(binaryHash);
|
||||
} else if (sigType.getVendorTableReference().equals("EFI_CERT_X509_GUID")) {
|
||||
inputStream.read(guid);
|
||||
efiVarGuid = new UefiGuid(guid);
|
||||
// Read in Type and Length separately so we calculate the rest of the cert size
|
||||
byte[] certType = new byte[UefiConstants.SIZE_2];
|
||||
inputStream.read(certType);
|
||||
byte[] certLength = new byte[UefiConstants.SIZE_2];
|
||||
inputStream.read(certLength);
|
||||
int cLength = new BigInteger(certLength).intValue() + UefiConstants.SIZE_4;
|
||||
byte[] certData = new byte[cLength];
|
||||
inputStream.read(certData);
|
||||
// put the cert back together
|
||||
byte[] certBlob = new byte[cLength + UefiConstants.SIZE_4];
|
||||
System.arraycopy(certType, 0, certBlob, 0, UefiConstants.SIZE_2);
|
||||
System.arraycopy(certLength, 0, certBlob, UefiConstants.OFFSET_2, UefiConstants.SIZE_2);
|
||||
System.arraycopy(certData, 0, certBlob, UefiConstants.OFFSET_4, cLength);
|
||||
cert = new UefiX509Cert(certBlob);
|
||||
} else if (sigType.isUnknownUUID()) {
|
||||
//status = "Signature List Type has an unknown GUID: " + efiGuid.toString();
|
||||
status = "Signature List Type has an unknown GUID";
|
||||
return;
|
||||
} else { // else process as a cert (RH SHIM does this)
|
||||
processC509Cert(inputStream);
|
||||
efiVarGuid = sigType;
|
||||
}
|
||||
valid = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default EFISignatureData Constructor.
|
||||
*
|
||||
* @param data byte array of the EFISignatureData to process
|
||||
* @throws java.security.cert.CertificateException If there a problem parsing the X509 certificate.
|
||||
* @throws java.security.NoSuchAlgorithmException if there's a problem hashing the certificate.
|
||||
*/
|
||||
UefiSignatureData(final byte[] data) throws CertificateException, NoSuchAlgorithmException {
|
||||
System.arraycopy(data, 0, guid, 0, UefiConstants.SIZE_16);
|
||||
sigData = new byte[data.length - UefiConstants.SIZE_16];
|
||||
System.arraycopy(data, UefiConstants.OFFSET_16, sigData, 0, data.length - UefiConstants.SIZE_16);
|
||||
cert = new UefiX509Cert(sigData);
|
||||
efiVarGuid = new UefiGuid(guid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes an x509 Cert used by secure DB or DBx.
|
||||
*
|
||||
* @param inputStream x509 certificate data.
|
||||
* @throws java.io.IOException is there's a problem reading the data.
|
||||
* @throws java.security.cert.CertificateException if there's a problem parsing the certificate.
|
||||
* @throws java.security.NoSuchAlgorithmException if there's a problem creating a hash.
|
||||
*/
|
||||
private void processC509Cert(final ByteArrayInputStream inputStream)
|
||||
throws IOException, CertificateException, NoSuchAlgorithmException {
|
||||
byte[] certType = new byte[UefiConstants.SIZE_2];
|
||||
inputStream.read(certType);
|
||||
byte[] certLength = new byte[UefiConstants.SIZE_2];
|
||||
inputStream.read(certLength);
|
||||
int cLength = new BigInteger(certLength).intValue() + UefiConstants.SIZE_4;
|
||||
byte[] certData = new byte[cLength];
|
||||
inputStream.read(certData);
|
||||
// put the cert back together
|
||||
byte[] certBlob = new byte[cLength + UefiConstants.SIZE_4];
|
||||
System.arraycopy(certType, 0, certBlob, 0, 2);
|
||||
System.arraycopy(certLength, 0, certBlob, 2, 2);
|
||||
System.arraycopy(certData, 0, certBlob, UefiConstants.OFFSET_4, cLength);
|
||||
cert = new UefiX509Cert(certBlob);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a description of the fields within the EFI Signature Data.
|
||||
*
|
||||
* @return X509Cert human readable description.
|
||||
*/
|
||||
public String toString() {
|
||||
String sigInfo = "";
|
||||
if (!valid) {
|
||||
sigInfo = status;
|
||||
} else {
|
||||
if (signatureType.getVendorTableReference().equals("EFI_CERT_SHA256_GUID")) {
|
||||
sigInfo += "UEFI Signature Owner = " + efiVarGuid.toString() + "\n";
|
||||
sigInfo += " Binary Hash = " + HexUtils.byteArrayToHexString(binaryHash) + "\n";
|
||||
} else {
|
||||
sigInfo += "UEFI Signature Owner = " + efiVarGuid.toString() + "\n";
|
||||
sigInfo += cert.toString();
|
||||
}
|
||||
}
|
||||
return sigInfo;
|
||||
}
|
||||
}
|
@ -0,0 +1,225 @@
|
||||
package hirs.utils.tpm.eventlog.uefi;
|
||||
|
||||
import hirs.utils.HexUtils;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Class for processing the contents of a Secure Boot DB or DBX contents.
|
||||
* used for EFIVariables associated with Secure Boot
|
||||
* as defined by Section 32.4.1 Signature Database from the UEFI 2.8 specification.
|
||||
* <p>
|
||||
* An EFI Signature List is actual a list of Certificates used to verify a Signature.
|
||||
* This is mainly found in PCR[7] UEFI variables for the Secure Boot PK, KEK, Db and DBx variables.
|
||||
* <p>
|
||||
* typedef struct _EFI_SIGNATURE_LIST {
|
||||
* EFI_GUID SignatureType;
|
||||
* UINT32 SignatureListSize;
|
||||
* UINT32 SignatureHeaderSize;
|
||||
* UINT32 SignatureSize;
|
||||
* // UINT8 SignatureHeader[SignatureHeaderSize];
|
||||
* // EFI_SIGNATURE_DATA Signatures[...][SignatureSize];
|
||||
* } EFI_SIGNATURE_LIST;
|
||||
*/
|
||||
public class UefiSignatureList {
|
||||
/**
|
||||
* Size of the signature list.
|
||||
*/
|
||||
private int listSize = 0;
|
||||
/**
|
||||
* Size of a signature.
|
||||
*/
|
||||
private int signatureSize = 0;
|
||||
/**
|
||||
* Signature data.
|
||||
*/
|
||||
private byte[] sigData = null;
|
||||
/**
|
||||
* Number of Items in the list.
|
||||
*/
|
||||
@Getter
|
||||
private int numberOfCerts = 0;
|
||||
/**
|
||||
* Signature validity.
|
||||
*/
|
||||
private boolean valid = true;
|
||||
/**
|
||||
* Current status.
|
||||
*/
|
||||
private String status = "Signature List is Valid";
|
||||
/**
|
||||
* Array List of Signature found in the list.
|
||||
*/
|
||||
private ArrayList<UefiSignatureData> sigList = new ArrayList<UefiSignatureData>();
|
||||
/**
|
||||
* Input Stream for processing.
|
||||
*/
|
||||
private ByteArrayInputStream efiSigDataIS = null;
|
||||
/**
|
||||
* Type of signature.
|
||||
*/
|
||||
private UefiGuid signatureType = null;
|
||||
|
||||
/**
|
||||
* UefiSignatureList constructor.
|
||||
*
|
||||
* @param list byte array holding the signature list.
|
||||
* @throws java.security.cert.CertificateException If there a problem parsing the X509 certificate.
|
||||
* @throws java.security.NoSuchAlgorithmException if there's a problem hashing the certificate.
|
||||
* @throws java.io.IOException If there's a problem parsing the signature data.
|
||||
*/
|
||||
UefiSignatureList(final byte[] list)
|
||||
throws CertificateException, NoSuchAlgorithmException, IOException {
|
||||
|
||||
byte[] guid = new byte[UefiConstants.SIZE_16];
|
||||
System.arraycopy(list, 0, guid, 0, UefiConstants.SIZE_16);
|
||||
signatureType = new UefiGuid(guid);
|
||||
|
||||
byte[] lSize = new byte[UefiConstants.SIZE_4];
|
||||
System.arraycopy(list, UefiConstants.OFFSET_16, lSize, 0, UefiConstants.SIZE_4);
|
||||
listSize = HexUtils.leReverseInt(lSize);
|
||||
|
||||
byte[] hSize = new byte[UefiConstants.SIZE_4];
|
||||
System.arraycopy(list, UefiConstants.OFFSET_20, hSize, 0, UefiConstants.SIZE_4);
|
||||
|
||||
byte[] sSize = new byte[UefiConstants.SIZE_4];
|
||||
System.arraycopy(list, UefiConstants.OFFSET_24, sSize, 0, UefiConstants.SIZE_4);
|
||||
signatureSize = HexUtils.leReverseInt(sSize);
|
||||
|
||||
sigData = new byte[signatureSize];
|
||||
System.arraycopy(list, UefiConstants.OFFSET_28, sigData, 0, signatureSize);
|
||||
processSignatureList(sigData);
|
||||
}
|
||||
|
||||
/**
|
||||
* EFI Signature list constructor.
|
||||
*
|
||||
* @param lists ByteArrayInputStream containing an EFI Signature list.
|
||||
* @throws java.io.IOException If there's a problem in reading he input stream.
|
||||
* @throws java.security.cert.CertificateException If there's a problem parsing the X509 certificate.
|
||||
* @throws java.security.NoSuchAlgorithmException if there's a problem hashing the certificate.
|
||||
*/
|
||||
UefiSignatureList(final ByteArrayInputStream lists)
|
||||
throws IOException, CertificateException, NoSuchAlgorithmException {
|
||||
byte[] guid = new byte[UefiConstants.SIZE_16];
|
||||
lists.read(guid);
|
||||
signatureType = new UefiGuid(guid);
|
||||
|
||||
if (!isValidSigListGUID(signatureType)) {
|
||||
processSignatureData(lists);
|
||||
} else { // valid SigData Processing
|
||||
byte[] lSize = new byte[UefiConstants.SIZE_4];
|
||||
lists.read(lSize);
|
||||
listSize = HexUtils.leReverseInt(lSize);
|
||||
|
||||
byte[] hSize = new byte[UefiConstants.SIZE_4];
|
||||
lists.read(hSize);
|
||||
|
||||
byte[] sSize = new byte[UefiConstants.SIZE_4];
|
||||
lists.read(sSize);
|
||||
signatureSize = listSize - UefiConstants.SIZE_28;
|
||||
sigData = new byte[signatureSize];
|
||||
lists.read(sigData);
|
||||
processSignatureList(sigData);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method for processing a set of EFI SignatureList(s).
|
||||
*
|
||||
* @param efiSigData Byte array holding one or more SignatureLists
|
||||
* @throws java.security.cert.CertificateException If there's a problem parsing the X509 certificate.
|
||||
* @throws java.security.NoSuchAlgorithmException if there's a problem hashing the certificate.
|
||||
* @throws java.io.IOException If there's a problem parsing the signature data.
|
||||
*/
|
||||
private void processSignatureList(final byte[] efiSigData)
|
||||
throws CertificateException, NoSuchAlgorithmException, IOException {
|
||||
efiSigDataIS = new ByteArrayInputStream(efiSigData);
|
||||
while (efiSigDataIS.available() > 0) {
|
||||
UefiSignatureData tmpSigData = new UefiSignatureData(efiSigDataIS, signatureType);
|
||||
if (!tmpSigData.isValid()) {
|
||||
valid = false;
|
||||
status = tmpSigData.getStatus();
|
||||
break;
|
||||
}
|
||||
sigList.add(tmpSigData);
|
||||
numberOfCerts++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method for processing a set of EFI SignatureList(s).
|
||||
*
|
||||
* @param sigDataIS Byte array holding one or more SignatureLists.
|
||||
* @throws java.security.cert.CertificateException If there's a problem parsing the X509 certificate.
|
||||
* @throws java.security.NoSuchAlgorithmException if there's a problem hashing the certificate.
|
||||
* @throws java.io.IOException If there's a problem parsing the signature data.
|
||||
*/
|
||||
private void processSignatureData(final ByteArrayInputStream sigDataIS)
|
||||
throws CertificateException, NoSuchAlgorithmException, IOException {
|
||||
while (sigDataIS.available() > 0) {
|
||||
UefiSignatureData tmpigData = new UefiSignatureData(sigDataIS, signatureType);
|
||||
if (!tmpigData.isValid()) {
|
||||
valid = false;
|
||||
status = tmpigData.getStatus();
|
||||
break;
|
||||
}
|
||||
sigList.add(tmpigData);
|
||||
numberOfCerts++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an ArrayList of EFISignatureData objects.
|
||||
*
|
||||
* @return ArrayList of EFISignatureData objects.
|
||||
*/
|
||||
public ArrayList<UefiSignatureData> getSignatureDataList() {
|
||||
return sigList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if GUID is listed on page 1729 of UEFI spec version 2.8.
|
||||
*
|
||||
* @param guid GUID of the has algorithm.
|
||||
* @return true if the GUID is a valid GUID for Signature List Type, false if not.
|
||||
*/
|
||||
public boolean isValidSigListGUID(final UefiGuid guid) {
|
||||
switch (guid.getVendorTableReference()) {
|
||||
case "EFI_CERT_SHA256_GUID":
|
||||
case "EFI_CERT_X509_SHA256":
|
||||
case "EFI_CERT_X509_SHA384":
|
||||
case "EFI_CERT_X509_SHA512":
|
||||
case "EFI_CERT_X509_GUID":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a description of the fields within the EFI Signature Data field.
|
||||
* Which is essentially a list of X509 certificates.
|
||||
*
|
||||
* @return human readable description.
|
||||
*/
|
||||
public String toString() {
|
||||
StringBuilder sigInfo = new StringBuilder();
|
||||
sigInfo.append("UEFI Signature List Type = " + signatureType.toString() + "\n");
|
||||
sigInfo.append("Number if items = " + numberOfCerts + "\n");
|
||||
sigList.iterator();
|
||||
for (int i = 0; i < sigList.size(); i++) {
|
||||
UefiSignatureData certData = sigList.get(i);
|
||||
sigInfo.append(certData.toString());
|
||||
}
|
||||
if (!valid) {
|
||||
sigInfo.append("*** Invalid UEFI Signature data encountered: " + status + "\n");
|
||||
}
|
||||
return sigInfo.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,204 @@
|
||||
package hirs.utils.tpm.eventlog.uefi;
|
||||
|
||||
import hirs.utils.HexUtils;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Class to process a UEFI variable within a TPM Event.
|
||||
* typedef struct tdUEFI_VARIABLE_DATA{
|
||||
* UEFI_GUID VariableName; (16 bytes)
|
||||
* UINT64 UnicodeNameLength; (8 bytes)
|
||||
* UINT64 VariableDataLength; (8 bytes)
|
||||
* CHAR16 UnicodeName[];
|
||||
* INT8 VariableData[];
|
||||
* } UEFI_VARIABLE_DATA
|
||||
*/
|
||||
public class UefiVariable {
|
||||
|
||||
/**
|
||||
* UEFI defined variable identifier GUID.
|
||||
*/
|
||||
@Getter
|
||||
private UefiGuid uefiVarGuid = null;
|
||||
/**
|
||||
* List of Signature lists.
|
||||
*/
|
||||
@Getter
|
||||
private ArrayList<UefiSignatureList> certSuperList = new ArrayList<>();
|
||||
/**
|
||||
* Name of the UEFI variable.
|
||||
*/
|
||||
@Getter
|
||||
private String efiVarName = "";
|
||||
/**
|
||||
* UEFI defined Boot Variable.
|
||||
*/
|
||||
private UefiBootVariable bootv = null;
|
||||
/**
|
||||
* UEFI Defined boot order.
|
||||
*/
|
||||
private UefiBootOrder booto = null;
|
||||
/**
|
||||
* UEFI defined secure boot.
|
||||
*/
|
||||
private UefiSecureBoot sb = null;
|
||||
/**
|
||||
* UEFI variable data.
|
||||
*/
|
||||
private byte[] uefiVariableData = null;
|
||||
|
||||
/**
|
||||
* EFIVariable constructor.
|
||||
* The UEFI_VARIABLE_DATA contains a "VariableName" field which is used to determine
|
||||
* the class used to parse the data within the "VariableData".
|
||||
*
|
||||
* @param variableData byte array holding the UEFI Variable.
|
||||
* @throws java.security.cert.CertificateException If there a problem parsing the X509 certificate.
|
||||
* @throws java.security.NoSuchAlgorithmException if there's a problem hashing the certificate.
|
||||
* @throws java.io.IOException If there's a problem parsing the signature data.
|
||||
*/
|
||||
public UefiVariable(final byte[] variableData)
|
||||
throws CertificateException, NoSuchAlgorithmException, IOException {
|
||||
byte[] guid = new byte[UefiConstants.SIZE_16];
|
||||
byte[] nameLength = new byte[UefiConstants.SIZE_8];
|
||||
byte[] nameTemp = null;
|
||||
byte[] dataLength = new byte[UefiConstants.SIZE_8];
|
||||
byte[] name = null;
|
||||
int variableLength = 0;
|
||||
|
||||
System.arraycopy(variableData, 0, guid, 0, UefiConstants.SIZE_16);
|
||||
uefiVarGuid = new UefiGuid(guid);
|
||||
System.arraycopy(variableData, UefiConstants.SIZE_16, nameLength, 0, UefiConstants.SIZE_8);
|
||||
int nlength = HexUtils.leReverseInt(nameLength);
|
||||
System.arraycopy(variableData, UefiConstants.OFFSET_24, dataLength, 0, UefiConstants.SIZE_8);
|
||||
nameTemp = new byte[nlength * UefiConstants.SIZE_2];
|
||||
|
||||
System.arraycopy(variableData, UefiConstants.OFFSET_32,
|
||||
nameTemp, 0, nlength * UefiConstants.SIZE_2);
|
||||
byte[] name1 = UefiDevicePath.convertChar16tobyteArray(nameTemp);
|
||||
name = new byte[nlength];
|
||||
System.arraycopy(name1, 0, name, 0, nlength);
|
||||
variableLength = HexUtils.leReverseInt(dataLength);
|
||||
uefiVariableData = new byte[variableLength];
|
||||
System.arraycopy(variableData, UefiConstants.OFFSET_32
|
||||
+ nlength * UefiConstants.SIZE_2, uefiVariableData, 0, variableLength);
|
||||
efiVarName = new String(name, StandardCharsets.UTF_8);
|
||||
String tmpName = efiVarName;
|
||||
if (efiVarName.contains("Boot00")) {
|
||||
tmpName = "Boot00";
|
||||
}
|
||||
switch (tmpName) {
|
||||
case "PK":
|
||||
case "KEK":
|
||||
case "db":
|
||||
case "dbx":
|
||||
processSigList(uefiVariableData);
|
||||
break;
|
||||
case "Boot00":
|
||||
bootv = new UefiBootVariable(uefiVariableData);
|
||||
break;
|
||||
case "BootOrder":
|
||||
booto = new UefiBootOrder(uefiVariableData);
|
||||
break;
|
||||
case "SecureBoot":
|
||||
sb = new UefiSecureBoot(uefiVariableData);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the data as a UEFI defined Signature List.
|
||||
*
|
||||
* @param data the bye array holding the Signature List.
|
||||
* @throws java.security.cert.CertificateException If there a problem parsing the X509 certificate.
|
||||
* @throws java.security.NoSuchAlgorithmException if there's a problem hashing the certificate.
|
||||
* @throws java.io.IOException If there's a problem parsing the signature data.
|
||||
*/
|
||||
private void processSigList(final byte[] data)
|
||||
throws CertificateException, NoSuchAlgorithmException, IOException {
|
||||
ByteArrayInputStream certData = new ByteArrayInputStream(data);
|
||||
while (certData.available() > 0) {
|
||||
UefiSignatureList list;
|
||||
list = new UefiSignatureList(certData);
|
||||
certSuperList.add(list);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print out all the interesting characteristics available on this UEFI Variable.
|
||||
*
|
||||
* @return human readable description of the UEFi variable.
|
||||
*/
|
||||
public String toString() {
|
||||
StringBuilder efiVariable = new StringBuilder();
|
||||
efiVariable.append("UEFI Variable Name:" + efiVarName + "\n");
|
||||
efiVariable.append("UEFI_GUID = " + uefiVarGuid.toString() + "\n ");
|
||||
if (efiVarName != "") {
|
||||
efiVariable.append("UEFI Variable Contents => " + "\n ");
|
||||
}
|
||||
String tmpName = efiVarName;
|
||||
if (efiVarName.contains("Boot00")) {
|
||||
tmpName = "Boot00";
|
||||
} else {
|
||||
tmpName = efiVarName;
|
||||
}
|
||||
switch (tmpName) {
|
||||
case "Shim":
|
||||
case "MokList":
|
||||
efiVariable.append(printCert(uefiVariableData, 0));
|
||||
break;
|
||||
case "Boot00":
|
||||
efiVariable.append(bootv.toString());
|
||||
break;
|
||||
case "BootOrder":
|
||||
efiVariable.append(booto.toString());
|
||||
break;
|
||||
case "SecureBoot":
|
||||
efiVariable.append(sb.toString());
|
||||
break;
|
||||
default:
|
||||
if (!tmpName.isEmpty()) {
|
||||
efiVariable.append(String.format("Data not provided for UEFI variable named %s ",
|
||||
tmpName));
|
||||
} else {
|
||||
efiVariable.append("Data not provided ");
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < certSuperList.size(); i++) {
|
||||
efiVariable.append(certSuperList.get(i).toString());
|
||||
}
|
||||
return efiVariable.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves human readable description from a Certificate.
|
||||
*
|
||||
* @param data byte[] holding the certificate.
|
||||
* @param offset offset to start of the certificate within the byte array.
|
||||
* @return human readable description of a certificate.
|
||||
*/
|
||||
public String printCert(final byte[] data, final int offset) {
|
||||
String certInfo = "";
|
||||
byte[] certLength = new byte[UefiConstants.SIZE_2];
|
||||
System.arraycopy(data, offset + UefiConstants.OFFSET_2, certLength, 0, UefiConstants.SIZE_2);
|
||||
int cLength = new BigInteger(certLength).intValue() + UefiConstants.SIZE_4;
|
||||
byte[] certData = new byte[cLength];
|
||||
System.arraycopy(data, offset, certData, 0, cLength);
|
||||
try {
|
||||
UefiX509Cert cert = new UefiX509Cert(certData);
|
||||
certInfo = cert.toString();
|
||||
} catch (Exception e) {
|
||||
certInfo = "Error Processing Certificate : " + e.getMessage();
|
||||
}
|
||||
return (certInfo);
|
||||
}
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
package hirs.utils.tpm.eventlog.uefi;
|
||||
|
||||
import jakarta.xml.bind.DatatypeConverter;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
/**
|
||||
* Class for processing a Secure Boot certificate stored in the UEFI DB or DBX.
|
||||
* X509 certs are used by Secure Boot for validating EFI files.
|
||||
*/
|
||||
public class UefiX509Cert {
|
||||
/**
|
||||
* Certificate object .
|
||||
*/
|
||||
private java.security.cert.Certificate cert = null;
|
||||
|
||||
/**
|
||||
* Constructor for the certificate.
|
||||
*
|
||||
* @param certData byte array holding the certificate.
|
||||
* @throws java.security.cert.CertificateException If the certificate cannot parse.
|
||||
* @throws java.security.NoSuchAlgorithmException if a hash cannot be generated from the cert.
|
||||
*/
|
||||
public UefiX509Cert(final byte[] certData) throws CertificateException, NoSuchAlgorithmException {
|
||||
CertificateFactory cf;
|
||||
cf = CertificateFactory.getInstance("X.509");
|
||||
InputStream targetStream = new ByteArrayInputStream(certData);
|
||||
cert = cf.generateCertificate(targetStream);
|
||||
MessageDigest md = MessageDigest.getInstance("SHA1");
|
||||
md.update(certData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the byte length of the certificate.
|
||||
*
|
||||
* @return the certificate length.
|
||||
* @throws java.security.cert.CertificateEncodingException if the certificate failed to parse.
|
||||
*/
|
||||
public int getLength() throws CertificateEncodingException {
|
||||
int length = 0;
|
||||
X509Certificate x509Cert = (X509Certificate) cert;
|
||||
length = x509Cert.getEncoded().length;
|
||||
return length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the fingerprint per Microsoft's specs using SHA1 and colon based notation.
|
||||
* e.g. "44:d6:41:ca:ca:08:09:00:23:98:b4:87:7b:8e:98:2e:d2:6f:7b:76"
|
||||
*
|
||||
* @return a string representation of the certificate fingerprint
|
||||
*/
|
||||
public String getSHA1FingerPrint() {
|
||||
byte[] der = null;
|
||||
MessageDigest md = null;
|
||||
try {
|
||||
md = MessageDigest.getInstance("SHA-1");
|
||||
der = cert.getEncoded();
|
||||
} catch (Exception e) {
|
||||
return ("Error creating Certificate Fingerprint: " + e.getMessage());
|
||||
}
|
||||
md.update(der);
|
||||
byte[] digest = md.digest();
|
||||
String digestHex = DatatypeConverter.printHexBinary(digest);
|
||||
digestHex = digestHex.replaceAll("..(?!$)", "$0:"); // places : every 2 digits
|
||||
return digestHex.toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a Sting of select fields of the Certificate data.
|
||||
*
|
||||
* @return A string detailing select fields of the certificate.
|
||||
*/
|
||||
public String toString() {
|
||||
X509Certificate x509Cert = (X509Certificate) cert;
|
||||
String certData = "";
|
||||
certData += " Certificate Serial Number = "
|
||||
+ x509Cert.getSerialNumber().toString(UefiConstants.SIZE_16) + "\n";
|
||||
certData += " Subject DN = " + x509Cert.getSubjectX500Principal().getName() + "\n";
|
||||
certData += " Issuer DN = " + x509Cert.getIssuerX500Principal().getName() + "\n";
|
||||
certData += " Not Before Date = " + x509Cert.getNotBefore() + "\n";
|
||||
certData += " Not After Date = " + x509Cert.getNotAfter() + "\n";
|
||||
certData += " Signature Algorithm = " + x509Cert.getSigAlgName() + "\n";
|
||||
certData += " SHA1 Fingerprint = " + getSHA1FingerPrint() + "\n";
|
||||
return certData;
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Non-persistant classes related to TGC Event Logs.
|
||||
*/
|
||||
|
||||
package hirs.utils.tpm.eventlog.uefi;
|
||||
|
@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Non-persistant classes related to TPM.
|
||||
*/
|
||||
|
||||
package hirs.utils.tpm;
|
||||
|
268
build.gradle
268
build.gradle
@ -1,154 +1,120 @@
|
||||
|
||||
allprojects {
|
||||
task addPlugins << {
|
||||
delete './build/plugins'
|
||||
mkdir './build/plugins'
|
||||
if (project.hasProperty('pluginDir')) {
|
||||
if (pluginDir?.trim()) {
|
||||
copy {
|
||||
from "$pluginDir"
|
||||
into 'build/plugins'
|
||||
include '*.jar'
|
||||
include '**/*.jar'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task copyVersion() {
|
||||
doLast {
|
||||
if (project.hasProperty('displayVersion')) {
|
||||
String resourceDir="${buildDir}/resources/main"
|
||||
println "setting app version file contents of: ${displayVersion} to ${resourceDir}"
|
||||
new File(resourceDir, "VERSION").write("$displayVersion")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
group = 'hirs'
|
||||
version = file("$rootDir/VERSION").text.trim() + "-SNAPSHOT"
|
||||
//import java.util.concurrent.TimeUnit
|
||||
//
|
||||
plugins {
|
||||
// Apply the application plugin to add support for building a CLI application in Java.
|
||||
id 'application'
|
||||
// id 'java'
|
||||
// id 'war'
|
||||
// id "nebula.ospackage" version "9.1.1"
|
||||
}
|
||||
|
||||
subprojects {
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'maven-publish'
|
||||
//subprojects {
|
||||
//
|
||||
//}
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" << "-Werror"
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
test {
|
||||
testLogging {
|
||||
exceptionFormat = 'full'
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(Test) {
|
||||
useTestNG() {
|
||||
includeGroups = project.ext.includeGroups.split()
|
||||
excludeGroups = project.ext.excludeGroups.split()
|
||||
}
|
||||
afterSuite { desc, result ->
|
||||
if (desc.parent == null) {
|
||||
logger.lifecycle("${result.successfulTestCount}/${result.testCount} tests passed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(FindBugs) {
|
||||
reports {
|
||||
xml.enabled = false
|
||||
html.enabled = true
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(Pmd) {
|
||||
reports {
|
||||
xml.enabled = false
|
||||
html.enabled = true
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
repositories {
|
||||
if(findProperty("env") != null && findProperty("env") == "CI") {
|
||||
maven {
|
||||
url "$rootDir/librepo"
|
||||
}
|
||||
} else {
|
||||
mavenLocal()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Global checkstyle file
|
||||
ext.checkstyleConfigFile = new File(rootDir, "/config/checkstyle/sun_checks.xml")
|
||||
|
||||
// Version definitions of all of the libraries we're using. They're defined
|
||||
// here to ensure that all projects are using the same versions of common
|
||||
// dependencies:
|
||||
ext.libs = [
|
||||
bouncy_castle: 'org.bouncycastle:bcmail-jdk15on:1.59',
|
||||
checkstyle: 'com.puppycrawl.tools:checkstyle:8.10.1',
|
||||
commons_cli: 'commons-cli:commons-cli:1.2',
|
||||
commons_codec: 'commons-codec:commons-codec:1.9',
|
||||
commons_csv: 'org.apache.commons:commons-csv:1.4',
|
||||
commons_exec: 'org.apache.commons:commons-exec:1.3',
|
||||
commons_http: 'commons-httpclient:commons-httpclient:3.1',
|
||||
commons_io: 'commons-io:commons-io:2.4',
|
||||
commons_lang: 'org.apache.commons:commons-lang3:3.3.2',
|
||||
commons_upload:'commons-fileupload:commons-fileupload:1.3.1',
|
||||
commons_valid: 'commons-validator:commons-validator:1.4.0',
|
||||
findbugs: 'com.google.code.findbugs:findbugs:3.0.0',
|
||||
gson: 'com.google.code.gson:gson:2.2.4',
|
||||
guava: 'com.google.guava:guava:18.0',
|
||||
hibernate: [ 'org.hibernate.common:hibernate-commons-annotations:4.0.4.Final',
|
||||
'org.hibernate:hibernate-core:4.3.11.Final',
|
||||
'org.hibernate:hibernate-hikaricp:4.3.11.Final'],
|
||||
hikari: 'com.zaxxer:HikariCP:2.4.1',
|
||||
hsqldb: 'org.hsqldb:hsqldb:2.3.2',
|
||||
http: 'org.apache.httpcomponents:httpclient:4.5',
|
||||
jackson: [ 'com.fasterxml.jackson.core:jackson-core:2.6.3',
|
||||
'com.fasterxml.jackson.core:jackson-databind:2.6.3',
|
||||
'com.fasterxml.jackson.core:jackson-annotations:2.6.3'],
|
||||
jadira_usertype: 'org.jadira.usertype:usertype.core:4.0.0.GA',
|
||||
jcommander: 'com.beust:jcommander:1.72',
|
||||
joda_time: 'joda-time:joda-time:2.9.4',
|
||||
jstl: [ 'org.apache.taglibs:taglibs-standard-impl:1.2.5',
|
||||
'org.apache.taglibs:taglibs-standard-spec:1.2.5'],
|
||||
log4j2: [ 'org.apache.logging.log4j:log4j-api:2.17.1',
|
||||
'org.apache.logging.log4j:log4j-core:2.17.1',
|
||||
'org.apache.logging.log4j:log4j-slf4j-impl:2.17.1'],
|
||||
log4j2_web: 'org.apache.logging.log4j:log4j-web:2.17.1',
|
||||
log_bridge: 'org.apache.logging.log4j:log4j-jcl:2.17.1',
|
||||
mockito: 'org.mockito:mockito-all:1.10.19',
|
||||
mariadb: 'org.mariadb.jdbc:mariadb-java-client:2.2.1',
|
||||
minimal_json: 'com.eclipsesource.minimal-json:minimal-json:0.9.5',
|
||||
pci_ids: 'com.github.marandus:pci-ids:0.3',
|
||||
pmd: 'net.sourceforge.pmd:pmd:5.1.1',
|
||||
powermock: [ 'org.powermock:powermock-core:1.6.3',
|
||||
'org.powermock:powermock-api-mockito:1.6.3',
|
||||
'org.powermock:powermock-module-testng:1.6.3' ],
|
||||
protobuf_java: 'com.google.protobuf:protobuf-java:3.4.0',
|
||||
reflections: 'org.reflections:reflections:0.9.9-RC1',
|
||||
servlet_api: 'javax.servlet:servlet-api:2.5',
|
||||
slf4j: 'org.slf4j:slf4j-api:1.7.13',
|
||||
spring_core: ['org.springframework:spring-aop:4.3.30.RELEASE',
|
||||
'org.springframework:spring-beans:4.3.30.RELEASE',
|
||||
'org.springframework:spring-context:4.3.30.RELEASE',
|
||||
'org.springframework:spring-expression:4.3.30.RELEASE',
|
||||
'org.springframework:spring-orm:4.3.30.RELEASE'],
|
||||
spring_msg: 'org.springframework:spring-messaging:4.3.30.RELEASE',
|
||||
spring_plugin: 'org.springframework.plugin:spring-plugin-core:1.2.0.RELEASE',
|
||||
spring_retry: 'org.springframework.retry:spring-retry:1.2.2.RELEASE',
|
||||
spring_test: 'org.springframework:spring-test:4.3.30.RELEASE',
|
||||
spring_web: 'org.springframework:spring-web:4.3.30.RELEASE',
|
||||
spring_webmvc: 'org.springframework:spring-webmvc:4.3.30.RELEASE',
|
||||
testng: 'org.testng:testng:6.8.8',
|
||||
xml_rpc_client: 'org.apache.xmlrpc:xmlrpc-client:3.1.3',
|
||||
]
|
||||
}
|
||||
//plugins {
|
||||
// id 'application'
|
||||
// id 'java'
|
||||
// id 'war'
|
||||
// id "nebula.ospackage" version "9.1.1"
|
||||
// id 'org.springframework.boot' version '3.0.1'
|
||||
// id 'io.spring.dependency-management' version '1.1.0'
|
||||
//}
|
||||
//
|
||||
//java {
|
||||
// toolchain {
|
||||
// languageVersion = JavaLanguageVersion.of(17)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//configurations {
|
||||
// compileOnly {
|
||||
// extendsFrom annotationProcessor
|
||||
// }
|
||||
// jaxb
|
||||
//}
|
||||
//
|
||||
//repositories {
|
||||
// flatDir { dirs "lib" }
|
||||
// mavenCentral()
|
||||
//}
|
||||
//
|
||||
//dependencies {
|
||||
// implementation project(':HIRS_Utils')
|
||||
// implementation project(':HIRS_AttestationCA')
|
||||
//
|
||||
// implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||
// implementation 'org.springframework.boot:spring-boot-starter-validation'
|
||||
// implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||
// implementation 'org.projectlombok:lombok:1.18.26'
|
||||
// implementation 'org.bouncycastle:bcmail-jdk15on:1.70'
|
||||
// implementation 'org.springframework.plugin:spring-plugin-core:3.0.0'
|
||||
// implementation 'org.apache.httpcomponents:httpclient:4.5.7'
|
||||
// implementation 'com.google.guava:guava:31.1-jre'
|
||||
// implementation 'org.glassfish.web:jakarta.servlet.jsp.jstl:3.0.0'
|
||||
// implementation 'com.github.marandus:pci-ids:0.3'
|
||||
// implementation 'org.apache.httpcomponents.client5:httpclient5:5.2.1'
|
||||
// implementation 'commons-codec:commons-codec:1.15'
|
||||
// implementation 'org.apache.commons:commons-lang3:3.12.0'
|
||||
// implementation 'org.apache.logging.log4j:log4j-core:2.19.0'
|
||||
// implementation 'org.apache.logging.log4j:log4j-api:2.19.0'
|
||||
// implementation 'com.eclipsesource.minimal-json:minimal-json:0.9.5'
|
||||
// implementation 'com.fasterxml.jackson.core:jackson-core:2.14.2'
|
||||
// implementation 'com.fasterxml.jackson.core:jackson-databind:2.14.2'
|
||||
// implementation "org.glassfish.jaxb:jaxb-runtime:4.0.1"
|
||||
// implementation 'jakarta.xml.bind:jakarta.xml.bind-api:4.0.0'
|
||||
//// implementation 'com.sun.xml.bind:jaxb-impl:4.0.2' //creates duplicate error
|
||||
// compileOnly 'org.projectlombok:lombok:1.18.26'
|
||||
// runtimeOnly 'org.mariadb.jdbc:mariadb-java-client'
|
||||
// annotationProcessor 'org.projectlombok:lombok:1.18.26'
|
||||
// providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
|
||||
//
|
||||
// testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||
//}
|
||||
//
|
||||
//war {
|
||||
// from(buildDir) {
|
||||
// include 'VERSION'
|
||||
// into 'WEB-INF/classes'
|
||||
// }
|
||||
// archiveFileName = 'HIRS_AttestationCAPortal.war'
|
||||
//}
|
||||
//
|
||||
//tasks.register("spring-boot-properties") {
|
||||
// group = 'Introspection'
|
||||
// description = 'Print properties from all BOMs'
|
||||
// doLast {
|
||||
// println dependencyManagement.importedProperties
|
||||
// }
|
||||
//}
|
||||
//
|
||||
////task generateXjcLibrary(type:Exec) {
|
||||
//// workingDir 'config'
|
||||
////
|
||||
//// commandLine './genXjcLibrary.sh'
|
||||
////}
|
||||
////compileJava.dependsOn generateXjcLibrary
|
||||
//
|
||||
////ospackage {
|
||||
//// packageName = 'HIRS_AttestationCA'
|
||||
//// os = LINUX
|
||||
//// arch = NOARCH
|
||||
//// release = '1'
|
||||
////
|
||||
//// user 'root'
|
||||
//// fileMode = 0755
|
||||
////
|
||||
//// addParentDirs = true
|
||||
//// createDirectoryEntry true
|
||||
////
|
||||
//// into ("/opt/tomcat/webapps") {
|
||||
//// from war.outputs.files
|
||||
//// from '../HIRS_AttestationCAPortal/build/libs/HIRS_AttestationCAPortal.war'
|
||||
//// user 'root'
|
||||
//// fileMode = 0755
|
||||
//// }
|
||||
////
|
||||
//// buildRpm {
|
||||
//// arch = X86_64
|
||||
//// }
|
||||
////}
|
@ -1,6 +1,19 @@
|
||||
rootProject.name = 'HIRS'
|
||||
include 'TPM_Utils',
|
||||
'HIRS_AttestationCAPortal',
|
||||
'tpm_module',
|
||||
'tools/tcg_rim_tool',
|
||||
'tools/tcg_eventlog_tool'
|
||||
|
||||
include 'HIRS_Utils',
|
||||
'HIRS_AttestationCA',
|
||||
'HIRS_AttestationCAPortal'
|
||||
|
||||
dependencyResolutionManagement {
|
||||
versionCatalogs {
|
||||
libs {
|
||||
version('springboot', '3.0.1')
|
||||
version('jackson', '2.14.2')
|
||||
library('common-lang3', 'org.apache.commons:commons-lang3:3.12.0')
|
||||
library('jackson-core', 'com.fasterxml.jackson.core', 'jackson-core').version('jackson')
|
||||
library('jackson-databind', 'com.fasterxml.jackson.core', 'jackson-databind').version('jackson')
|
||||
library('spring-datajpa', 'org.springframework.boot', 'spring-boot-starter-data-jpa').version('springboot')
|
||||
library('lombok', 'org.projectlombok:lombok:1.18.26')
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user