Added back in HIRS_Utils

This commit is contained in:
Cyrus 2023-03-06 20:48:51 -05:00
parent 7cdbc74506
commit 86ef7d9356
50 changed files with 6228 additions and 156 deletions

54
HIRS_Utils/build.gradle Normal file
View 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

View 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);
}
}

View 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;
}
}

View 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();
}
}

View 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;
}
}

View 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;
}
}

View 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);
}
}

View 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();
}
}

View 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()));
}
}
}

View 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));
}
}

View File

@ -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));
}
}

View File

@ -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,
}

View File

@ -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));
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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");
}
}

View 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;
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,6 @@
/**
* Non-persistant classes related to TGC Event Logs.
*/
package hirs.utils.tpm.eventlog.events;

View File

@ -0,0 +1,6 @@
/**
* Non-persistant classes related to TGC Event Logs.
*/
package hirs.utils.tpm.eventlog;

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,6 @@
/**
* Non-persistant classes related to TGC Event Logs.
*/
package hirs.utils.tpm.eventlog.uefi;

View File

@ -0,0 +1,6 @@
/**
* Non-persistant classes related to TPM.
*/
package hirs.utils.tpm;

View File

@ -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'
}
}
}
//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"
}
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")
}
}
}
//subprojects {
//
//}
group = 'hirs'
version = file("$rootDir/VERSION").text.trim() + "-SNAPSHOT"
}
subprojects {
apply plugin: 'java'
apply plugin: 'maven-publish'
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
//// }
////}

View File

@ -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')
}
}
}