diff --git a/HIRS_Utils/src/main/java/hirs/tpm/eventlog/CryptoAgileEventLog.java b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/CryptoAgileEventLog.java new file mode 100644 index 00000000..2f5200d5 --- /dev/null +++ b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/CryptoAgileEventLog.java @@ -0,0 +1,138 @@ +package hirs.tpm.eventlog; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; + +import hirs.utils.HexUtils; + +/** + * Class to handle the "Crypto Agile" Format for TCG Event Logs as defined in the + * TCG Platform Firmware Profile (PFP). + * The Format can provide multiple digests with different algorithm, + * however currently on SHA256 is supported. + * All other are currently ignored. + */ +public class CryptoAgileEventLog implements TCGEventLog { + /** + * SHA256 length = 24 bytes. + */ + private static final int PCR_LENGTH = TpmPcrEvent.SHA256_LENGTH; + /** + * Each PCR bank holds 24 registers. + */ + private static final int PCR_COUNT = 24; + /** + * PFP defined EV_NO_ACTION identifier. + */ + private static final int NO_ACTION_EVENT = 0x00000003; + /** + * 2 dimensional array holding the PCR values. + */ + private byte[][] pcrList = new byte[PCR_COUNT][PCR_LENGTH]; + /** + * List of parsed events within the log. + */ + private ArrayList eventList = new ArrayList<>(); + + /** + * Constructor. + * + * @param rawlog the entire tcg log + * @throws IOException if the input stream cannot access the log data + */ + public CryptoAgileEventLog(final byte[] rawlog) throws IOException { + ByteArrayInputStream is = new ByteArrayInputStream(rawlog); + // process the EfiSpecId Event in SHA1 format per the PFP + TpmPcrEvent1 idEvent = new TpmPcrEvent1(is); + eventList.add(idEvent); + // put all events into an event list for further processing + while (is.available() > 0) { // All other events should be Crypto agile + eventList.add(new TpmPcrEvent2(is)); + } + calculatePCRValues(); + } + + /** + * Returns a single PCR value given an index (PCR Number). + */ + @Override + public String[] getExpectedPCRValues() { + String[] pcrs = new String[PCR_COUNT]; + for (int i = 0; i < PCR_COUNT; i++) { + pcrs[i] = HexUtils.byteArrayToHexString(pcrList[i]); + } + return pcrs; + } + + /** + * Returns all 24 PCR values for display purposes. + * + * @param index pcr index + * @return Returns an array of strings for all 24 PCRs + */ + @Override + public String getExpectedPCRValue(final int index) { + return HexUtils.byteArrayToHexString(pcrList[index]); + } + + /** + * Calculates the "Expected Values for TPM PCRs based upon Event digests in the Event Log. + * Uses the algorithm and eventList passed into the constructor. + * + * @return a 2 dimensional bye array holding the hashes of the PCRs. + */ + private void calculatePCRValues() { + byte[] extendedPCR = null; + for (int i = 0; i < PCR_COUNT; i++) { // Initialize the PCRlist array + System.arraycopy(HexUtils.hexStringToByteArray( + "0000000000000000000000000000000000000000000000000000000000000000"), + 0, pcrList[i], 0, PCR_LENGTH); + } + for (TpmPcrEvent currentEvent : eventList) { + 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 + extendedPCR = extendPCRsha256(pcrList[currentEvent.getPcrIndex()], + currentEvent.getEventDigest()); + System.arraycopy(extendedPCR, 0, pcrList[currentEvent.getPcrIndex()], + 0, PCR_LENGTH); + } + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + } + } + } + + /** + * Extends a sha256 hash with a hash of new data. + * + * @param currentValue byte array holding the current hash value + * @param newEvent byte array holding the value to extend + * @return new hash value + * @throws NoSuchAlgorithmException if hash algorithm is not supported. + */ + private byte[] extendPCRsha256(final byte[] currentValue, final byte[] newEvent) + throws NoSuchAlgorithmException { + return sha256hash( + HexUtils.hexStringToByteArray(HexUtils.byteArrayToHexString(currentValue) + + HexUtils.byteArrayToHexString(newEvent))); + } + + /** + * Creates a sha356 hash of a given byte array. + * + * @param blob byte array hold the value to hash. + * @return byte array holding the hash of the input array. + * @throws NoSuchAlgorithmException if hash algorithm is not supported. + */ + private byte[] sha256hash(final byte[] blob) throws NoSuchAlgorithmException { + MessageDigest md = MessageDigest.getInstance("SHA-256"); + md.update(blob); + return md.digest(); + } +} diff --git a/HIRS_Utils/src/main/java/hirs/tpm/eventlog/SHA1EventLog.java b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/SHA1EventLog.java new file mode 100644 index 00000000..ced29ee4 --- /dev/null +++ b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/SHA1EventLog.java @@ -0,0 +1,134 @@ +package hirs.tpm.eventlog; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; + +import hirs.utils.HexUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/** + * Class to handle the "SHA1" Format for TCG Event Logs. + * "SHA1" Format is defined in the TCG Platform Firmware Profile (PFP). + * This is to support older versions of UEFI Firmware or OS that create logs with SHA1. + */ +public class SHA1EventLog implements TCGEventLog { + private static final Logger LOGGER + = LogManager.getLogger(TCGEventLog.class); + /** + * SHA256 length = 24 bytes. + */ + private static final int PCR_LENGTH = TpmPcrEvent.SHA1_LENGTH; + /** + * Each PCR bank holds 24 registers. + */ + private static final int PCR_COUNT = 24; + /** + * PFP defined EV_NO_ACTION identifier. + */ + private static final int NO_ACTION_EVENT = 0x00000003; + /** + * List of parsed events within the log. + */ + private final ArrayList eventList = new ArrayList(); + /** + * 2 dimensional array holding the PCR values. + */ + private final byte[][] pcrList1 = new byte[PCR_COUNT][PCR_LENGTH]; + + /** + * Constructor. + * + * @param rawlog the entire tcg log + * @throws IOException if the inspur stream cannot access the log data + */ + public SHA1EventLog(final byte[] rawlog) throws IOException { + ByteArrayInputStream is = new ByteArrayInputStream(rawlog); + // put all events into an event list for further processing + while (is.available() > 0) { + eventList.add(new TpmPcrEvent1(is)); + } + calculatePcrValues(); + } + + /** + * 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] = HexUtils.byteArrayToHexString(pcrList1[i]); + } + return pcrs; + } + + /** + * 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(pcrList1[index]); + } + + /** + * 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 = null; + for (int i = 0; i < PCR_COUNT; i++) { // Initialize the PCRlist1 array + System.arraycopy(HexUtils.hexStringToByteArray( + "0000000000000000000000000000000000000000"), + 0, pcrList1[i], 0, PCR_LENGTH); + } + for (TpmPcrEvent currentEvent : eventList) { + 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 = extendPCRsha1(pcrList1[currentEvent.getPcrIndex()], + currentEvent.getEventDigest()); + System.arraycopy(extendedPCR, 0, pcrList1[currentEvent.getPcrIndex()], + 0, currentEvent.getDigestLength()); + } + } catch (NoSuchAlgorithmException e) { + LOGGER.error(e); + } + } + } + } + + /** + * Extends a sha1 hash with a hash of new data. + * + * @param currentValue value to extend + * @param newEvent value to extend with + * @return new hash resultant hash + * @throws NoSuchAlgorithmException if hash algorithm not supported + */ + private byte[] extendPCRsha1(final byte[] currentValue, final byte[] newEvent) + throws NoSuchAlgorithmException { + return sha1hash(HexUtils.hexStringToByteArray(HexUtils.byteArrayToHexString(currentValue) + + HexUtils.byteArrayToHexString(newEvent))); + } + + /** + * Creates a sha1 hash of a given byte array. + * + * @param blob byte array of data to hash + * @return byte array holding the hash of the input array + * @throws NoSuchAlgorithmException id hash algorithm not supported + */ + private byte[] sha1hash(final byte[] blob) throws NoSuchAlgorithmException { + MessageDigest md = MessageDigest.getInstance("SHA1"); + md.update(blob); + return md.digest(); + } +} diff --git a/HIRS_Utils/src/main/java/hirs/tpm/eventlog/TCGEventLog.java b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/TCGEventLog.java new file mode 100644 index 00000000..87d740ac --- /dev/null +++ b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/TCGEventLog.java @@ -0,0 +1,16 @@ +package hirs.tpm.eventlog; + +/** + * Interface for handling different formats of TCG Event logs. + */ +public interface TCGEventLog { + /** Retrieves a all expected PCR values. + * @return String array holding all PCR Values + */ + String[] getExpectedPCRValues(); + /** Retrieves a single expected PCR values. + * @param index the PCR reference + * @return a String holding an expected PCR value + */ + String getExpectedPCRValue(int index); +} diff --git a/HIRS_Utils/src/main/java/hirs/tpm/eventlog/TCGEventLogProcessor.java b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/TCGEventLogProcessor.java new file mode 100644 index 00000000..a38e26b3 --- /dev/null +++ b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/TCGEventLogProcessor.java @@ -0,0 +1,126 @@ +package hirs.tpm.eventlog; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; + +import hirs.data.persist.TPMMeasurementRecord; +import hirs.data.persist.TpmWhiteListBaseline; +import hirs.utils.HexUtils; +import hirs.data.persist.Digest; +import hirs.data.persist.DigestAlgorithm;; + +/** + * Class for parsing a TCG EventLogs (both SHA1 and Crypto Agile Formats). + * Also produces a TPM Baseline using he digests within the event log. + * Constructor parses the input byte array into a List of TpmPcrEvents. + */ +public class TCGEventLogProcessor { + /** + * Name of the hash algorithm used to process the Event Log, default is SHA256. + */ + private String algorithm = "SHA256"; + /** + * PFP defined EV_NO_ACTION identifier. + */ + private static final int NO_ACTION_EVENT = 0x00000003; + /** + * Parsed event log array. + */ + private TCGEventLog tcgLog = null; + /** + * EV_NO_ACTION signature offset. + */ + private static final int SIG_OFFSET = 32; + /** + * TEV_NO_ACTION signature size. + */ + private static final int SIG_SIZE = 16; + + /** + * Constructor. + * + * @param rawLog the byte array holding the contents of the TCG Event Log + * @throws IOException if there is a parsing error + */ + public TCGEventLogProcessor(final byte[] rawLog) throws IOException { + if (isLogCrytoAgile(rawLog)) { + tcgLog = new CryptoAgileEventLog(rawLog); + } else { + tcgLog = new SHA1EventLog(rawLog); + algorithm = "SHA"; + } + } + + /** + * 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() { + return tcgLog.getExpectedPCRValues(); + } + + /** + * Returns a single PCR value given an index (PCR Number). + * + * @param index the PCR index + * @return String representing the PCR contents + */ + public String getExpectedPCRValue(final int index) { + return tcgLog.getExpectedPCRValue(index); + } + + /** + * 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 = null; + String pcrValue = ""; + for (int i = 0; i < TpmPcrEvent.PCR_COUNT; i++) { + if (algorithm.compareToIgnoreCase("SHA1") == 0) { // Log Was SHA1 Format + pcrValue = tcgLog.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 = tcgLog.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; + } + + /** + * 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 + * @throws UnsupportedEncodingException + */ + private boolean isLogCrytoAgile(final byte[] log) throws UnsupportedEncodingException { + byte[] eType = new byte[TpmPcrEvent.INT_LENGTH]; + System.arraycopy(log, TpmPcrEvent.INT_LENGTH, eType, 0, TpmPcrEvent.INT_LENGTH); + byte[] eventType = HexUtils.leReverseByte(eType); + int eventID = new BigInteger(eventType).intValue(); + if (eventID != NO_ACTION_EVENT) { + return false; + } // Event Type should be EV_NO_ACTION + byte[] signature = new byte[SIG_SIZE]; + System.arraycopy(log, SIG_OFFSET, signature, 0, SIG_SIZE); // should be "Spec ID Event03" + String sig = new String(signature, "UTF-8").substring(0, SIG_SIZE - 1); // remove null char + if (sig.equals("Spec ID Event03")) { + return true; + } + return (false); + } +} diff --git a/HIRS_Utils/src/main/java/hirs/tpm/eventlog/TcgTpmtHa.java b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/TcgTpmtHa.java new file mode 100644 index 00000000..d0d5a72b --- /dev/null +++ b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/TcgTpmtHa.java @@ -0,0 +1,196 @@ +package hirs.tpm.eventlog; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.math.BigInteger; + +import hirs.utils.HexUtils; + +/** + * Class to for the TCG defined TPMT_HA structure used to support the Crypto Agile Log format. + *

+ * typedef struct { + * TPMI_ALG_HASH hashAlg; + * TPMU_HA digest; + * } TPMT_HA; + */ +public class TcgTpmtHa { + /** + * TCG Defined Algorithm Identifiers . + */ + private int hashAlgId = 0; + /** + * Length of the hash. + */ + private int hashLength = 0; + /** + * Human readable name of the hash algorithm. + */ + private String hashName = ""; + /** + * Hash data. + */ + private byte[] digest = null; + /** + * TCG ID for SHA1. + */ + private static final int TPM_ALG_SHA1 = 0x04; + /** + * TCG ID for SHA1. + */ + private static final int TPM_ALG_SHA256 = 0x0B; + /** + * TCG ID for SHA 384. + */ + private static final int TPM_ALG_SHA384 = 0x0C; + /** + * TCG ID for SHA512. + */ + private static final int TPM_ALG_SHA_512 = 0x0D; + /** + * TCG ID for Null algorithm. + */ + private static final int TPM_ALG_NULL = 0x10; + /** + * TCG ID for SHA1. + */ + private static final int TPM_ALG_SHA1_LENGTH = 20; + /** + * TCG ID for SHA1. + */ + private static final int TPM_ALG_SHA256_LENGH = 32; + /** + * TCG ID for SHA 384. + */ + private static final int TPM_ALG_SHA384_LENGTH = 48; + /** + * TCG ID for SHA512. + */ + private static final int TPM_ALG_SHA512_LENGTH = 64; + /** + * TCG ID for Null algorithm. + */ + private static final int TPM_ALG_NULL_LENGTH = 0; + + /** + * Constructor. + * + * @param is ByteArrayInputStream holding the TcgTPMT_HA structured data + * @throws 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); + } + + /** + * Returns the TCG defined algorithm identifier. + * + * @return integer that specifies the algorithm as defined by the TCG + */ + public int getAlgId() { + return hashAlgId; + } + + /** + * Return the length of the Hash. + * + * @return the Hash length + */ + public int getHashLength() { + return hashLength; + } + + /** + * Readable name of the algorithm. + * + * @return Hash algorithm name + */ + public String getHashName() { + return hashName; + } + + /** + * @return digest held by the event + */ + protected byte[] getDigest() { + return digest; + } + + /** + * 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 + */ + private 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; + } + + /** + * 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 + */ + private 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_LENGH; + 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; + } +} diff --git a/HIRS_Utils/src/main/java/hirs/tpm/eventlog/TpmPcrEvent.java b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/TpmPcrEvent.java new file mode 100644 index 00000000..93724934 --- /dev/null +++ b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/TpmPcrEvent.java @@ -0,0 +1,218 @@ +package hirs.tpm.eventlog; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.math.BigInteger; + +import hirs.utils.HexUtils; + +/** + * 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 { + /** + * 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; + /** + * Each PCR bank holds 24 registers. + */ + public static final int PCR_COUNT = 24; + /** + * PCR index. + */ + private int pcrIndex = -1; + /** + * Event Type (long). + */ + private long eventType = 0; + /** + * Event digest. + */ + private byte[] digest = null; + /** + * Even data. + */ + private byte[] eventContent; + /** + * TCG Event Log spec version. + */ + private static String version = "Unknown"; + /** + * TCG Event Log errata version. + */ + private static String errata = "Unknown"; + /** + * Length (in bytes) of a pcr. + */ + private int digestLength = 0; + + /** + * Constructor. + * + * @param is ByteArrayInputStream holding the event + * @throws 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 + */ + protected void setEventDigest(final byte[] digestData) { + digest = new byte[digestLength]; + System.arraycopy(digestData, 0, digest, 0, this.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; + } + + /** + * 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); + } + + /** + * Gets the event index value from a TCG Event. + * + * @return eventIndex TCG Event Index as defined in the PFP + */ + public int getPcrIndex() { + return pcrIndex; + } + + /** + * Sets the EventType. + * + * @param type byte array holding the PFP defined log event type + */ + protected void setEventType(final byte[] type) { + byte[] evType = HexUtils.leReverseByte(type); + eventType = new BigInteger(evType).longValue(); + } + + /** + * Returns the EventType for the Event. + * + * @return event type + */ + public long getEventType() { + return eventType; + } + + /** + * Returns the version of the TCG Log Event specification pertaining to the log. + * only updated if the event is a TCG_EfiSpecIdEvent. + * + * @return specification version + */ + public String getSpecVersion() { + return version; + } + + /** + * Returns the Errata version of the TCG Log Event specification pertaining to the log. + * only updated if the event is a TCG_EfiSpecIdEvent). + * + * @return Errata version + */ + public String getSpecErrataVersion() { + return errata; + } + + /** + * Sets the event digest value after processing. + * + * @param digestData SHA1 or SHA256 digest to set + */ + protected void setDigest(final byte[] digestData) { + digest = new byte[digestLength]; + System.arraycopy(digestData, 0, digest, 0, digestLength); + } + + /** + * Sets the event content after processing. + * + * @param eventData The PFP defined event content + */ + protected void setEventContent(final byte[] eventData) { + eventContent = new byte[eventData.length]; + System.arraycopy(eventContent, 0, eventData, 0, eventData.length); + } + + /** + * Gets the length of number of bytes in a PCR for the event. + * event log format. + * + * @return byte array holding the events content field + */ + protected byte[] getEventContent() { + return eventContent; + } + + /** + * Sets the Digest Length. + * Also the number of bytes expected within each PCR. + * + * @param length number of bytes in a PCR for the event. + */ + public void setDigestLength(final int length) { + digestLength = length; + } + + /** + * Gets the length of number of bytes in a PCR for the event. + * + * @return Byte Array containing the PFP defined event content + */ + public int getDigestLength() { + return digestLength; + } +} diff --git a/HIRS_Utils/src/main/java/hirs/tpm/eventlog/TpmPcrEvent1.java b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/TpmPcrEvent1.java new file mode 100644 index 00000000..11868ba7 --- /dev/null +++ b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/TpmPcrEvent1.java @@ -0,0 +1,51 @@ +package hirs.tpm.eventlog; + +import hirs.utils.HexUtils; + +import java.io.ByteArrayInputStream; +import java.io.IOException; + +/** + * 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 + * @throws IOException if an error occurs in parsing the event + */ + public TpmPcrEvent1(final ByteArrayInputStream is) throws IOException { + super(is); + setDigestLength(SHA1_LENGTH); + byte[] unit32Data = new byte[INT_LENGTH]; + if (is.available() > MIN_SIZE) { + is.read(unit32Data); + setPcrIndex(unit32Data); + is.read(unit32Data); + setEventType(unit32Data); + byte[] eventDigest = new byte[SHA1_LENGTH]; + is.read(eventDigest); + setDigest(eventDigest); + is.read(unit32Data); + int eventSize = HexUtils.leReverseInt(unit32Data); + byte[] eventContent = new byte[eventSize]; + is.read(eventContent); + } + } +} diff --git a/HIRS_Utils/src/main/java/hirs/tpm/eventlog/TpmPcrEvent2.java b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/TpmPcrEvent2.java new file mode 100644 index 00000000..ad8c1f20 --- /dev/null +++ b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/TpmPcrEvent2.java @@ -0,0 +1,97 @@ +package hirs.tpm.eventlog; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.ArrayList; + +import hirs.utils.HexUtils; + +/** + * 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 hashlist = new ArrayList<>(); + + /** + * Constructor. + * + * @param is ByteArrayInputStream holding the TCG Log event + * @throws IOException if an error occurs in parsing the event + */ + public TpmPcrEvent2(final ByteArrayInputStream is) throws IOException { + super(is); + setDigestLength(SHA256_LENGTH); + //TCG_PCR_EVENT2 + byte[] rawInt = new byte[INT_LENGTH]; + if (is.available() > MIN_SIZE) { + is.read(rawInt); + setPcrIndex(rawInt); + is.read(rawInt); + setEventType(rawInt); + // TPML_DIGEST_VALUES + is.read(rawInt); + algCount = HexUtils.leReverseInt(rawInt); + TcgTpmtHa hashAlg = null; + // Process TPMT_HA, + for (int i = 0; i < algCount; i++) { + hashAlg = new TcgTpmtHa(is); + hashlist.add(hashAlg); + if (hashAlg.getHashName().compareToIgnoreCase("TPM_ALG_SHA256") == 0) { + setDigest(hashAlg.getDigest()); + } + } + is.read(rawInt); + int eventSize = HexUtils.leReverseInt(rawInt); + byte[] eventContent = new byte[eventSize]; + is.read(eventContent); + setEventContent(eventContent); + } + } +} diff --git a/HIRS_Utils/src/main/java/hirs/tpm/eventlog/package-info.java b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/package-info.java new file mode 100644 index 00000000..e20b170f --- /dev/null +++ b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/package-info.java @@ -0,0 +1,6 @@ +/** + * Non-persistant classes related to TGC Event Logs. + */ + +package hirs.tpm.eventlog; + diff --git a/HIRS_Utils/src/main/java/hirs/utils/HexUtils.java b/HIRS_Utils/src/main/java/hirs/utils/HexUtils.java index 97516ced..98a1175c 100644 --- a/HIRS_Utils/src/main/java/hirs/utils/HexUtils.java +++ b/HIRS_Utils/src/main/java/hirs/utils/HexUtils.java @@ -1,5 +1,7 @@ package hirs.utils; +import java.math.BigInteger; + /** * Utilities for working with hex strings and byte arrays. */ @@ -40,7 +42,7 @@ public final class HexUtils { * @return hex string representation of array */ public static String byteArrayToHexString(final byte[] b) { - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); String returnStr = ""; for (int i = 0; i < b.length; i++) { String singleByte = Integer.toHexString(b[i] & FF_BYTE); @@ -74,4 +76,38 @@ public final class HexUtils { 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(); + } } diff --git a/HIRS_Utils/src/test/java/hirs/tpm/eventlog/TCGEventLogProcessorTest.java b/HIRS_Utils/src/test/java/hirs/tpm/eventlog/TCGEventLogProcessorTest.java new file mode 100644 index 00000000..288aa2a6 --- /dev/null +++ b/HIRS_Utils/src/test/java/hirs/tpm/eventlog/TCGEventLogProcessorTest.java @@ -0,0 +1,175 @@ +package hirs.tpm.eventlog; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +import org.apache.commons.io.IOUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.hibernate.Session; + + +import org.testng.Assert; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +/* +import org.junit.Test; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +*/ + +import hirs.data.persist.Baseline; +import hirs.data.persist.Digest; +import hirs.data.persist.SpringPersistenceTest; +import hirs.data.persist.TpmWhiteListBaseline; +import hirs.utils.HexUtils; + +/** + * Class for testing TCG Event Log processing. + */ +public class TCGEventLogProcessorTest extends SpringPersistenceTest { + private static final String DEFAULT_EVENT_LOG = "/tcgeventlog/TpmLog.bin"; + private static final String DEFAULT_EXPECTED_PCRS = "/tcgeventlog/TpmLogExpectedPcrs.txt"; + private static final String SHA1_EVENT_LOG = "/tcgeventlog/TpmLogSHA1.bin"; + private static final String SHA1_EXPECTED_PCRS = "/tcgeventlog/TpmLogSHA1ExpectedPcrs.txt"; + private static final Logger LOGGER + = LogManager.getLogger(TCGEventLogProcessorTest.class); + + /** + * Initializes a SessionFactory. The factory is used for an in-memory database that + * is used for testing. + */ + @BeforeClass + public static final void setup() { + LOGGER.debug("retrieving session factory"); + + } + + /** + * Closes the SessionFactory from setup. + */ + @AfterClass + public static final void tearDown() { + LOGGER.debug("closing session factory"); + } + + /** + * Resets the test state to a known good state. This currently only resets the database by + * removing all Baseline objects. + */ + // @AfterMethod + public final void resetTestState() { + LOGGER.debug("reset test state"); + LOGGER.debug("deleting all baselines"); + Session session = sessionFactory.getCurrentSession(); + session.beginTransaction(); + final List baselines = session.createCriteria(Baseline.class).list(); + for (Object o : baselines) { + LOGGER.debug("deleting baseline: {}", o); + session.delete(o); + } + LOGGER.debug("all baselines removed"); + session.getTransaction().commit(); + } + + /** + * Tests the processing of a cryto agile event log. + * @throws IOException when processing the test fails + */ + @Test + public final void testCryptoAgileTCGEventLog() throws IOException { + LOGGER.debug("Testing the parsing of a Crypto Agile formatted TCG Event Log"); + InputStream log, pcrs; + boolean testPass = true; + log = this.getClass().getResourceAsStream(DEFAULT_EVENT_LOG); + byte[] rawLogBytes = IOUtils.toByteArray(log); + TCGEventLogProcessor tlp = new TCGEventLogProcessor(rawLogBytes); + String[] pcrFromLog = tlp.getExpectedPCRValues(); + pcrs = this.getClass().getResourceAsStream(DEFAULT_EXPECTED_PCRS); + Object[] pcrObj = IOUtils.readLines(pcrs).toArray(); + String[] pcrTxt = Arrays.copyOf(pcrObj, pcrObj.length, String[].class); + // Test 1 get all PCRs + for (int i = 0; i < 24; i++) { + if (pcrFromLog[i].compareToIgnoreCase(pcrTxt[i]) != 0) { + testPass = false; + LOGGER.error("\ntestTCGEventLogProcessorParser error with PCR " + i); + } + } + Assert.assertTrue(testPass); + // Test 2 get an individual PCR + String pcr3 = tlp.getExpectedPCRValue(3); + Assert.assertEquals(pcr3, pcrFromLog[3]); + LOGGER.debug("OK. Parsing of a Crypto Agile Format Success"); + } + + /** + * Tests the processing of a SHA1 formatted Event log. + * @throws IOException when processing the test fails + */ + @Test + public final void testSHA1TCGEventLog() throws IOException { + LOGGER.debug("Testing the parsing of a SHA1 formated TCG Event Log"); + InputStream log, pcrs; + boolean testPass = true; + log = this.getClass().getResourceAsStream(SHA1_EVENT_LOG); + byte[] rawLogBytes = IOUtils.toByteArray(log); + TCGEventLogProcessor tlp = new TCGEventLogProcessor(rawLogBytes); + String[] pcrFromLog = tlp.getExpectedPCRValues(); + pcrs = this.getClass().getResourceAsStream(SHA1_EXPECTED_PCRS); + Object[] pcrObj = IOUtils.readLines(pcrs).toArray(); + String[] pcrTxt = Arrays.copyOf(pcrObj, pcrObj.length, String[].class); + // Test 1 get all PCRs + for (int i = 0; i < 24; i++) { + if (pcrFromLog[i].compareToIgnoreCase(pcrTxt[i]) != 0) { + testPass = false; + LOGGER.error("\ntestTCGEventLogProcessorParser error with PCR " + i); + } + } + Assert.assertTrue(testPass); + // Test 2 get an individual PCR + String pcr0 = tlp.getExpectedPCRValue(0); + Assert.assertEquals(pcr0, pcrFromLog[0]); + LOGGER.debug("OK. Parsing of a SHA1 formatted TCG Event Log Success"); + } + + /** + * Tests TPM Baseline creation from a EventLog. + * @throws IOException when processing the test fails + */ + @Test + public final void testTPMBaselineCreate() throws IOException { + LOGGER.debug("Create and save TPM baseline from TCG Event Log test started"); + InputStream log; + boolean testPass = true; + log = this.getClass().getResourceAsStream(DEFAULT_EVENT_LOG); + byte[] rawLogBytes = IOUtils.toByteArray(log); + TCGEventLogProcessor tlp = new TCGEventLogProcessor(rawLogBytes); + String[] pcrFromLog = tlp.getExpectedPCRValues(); + // save it to the test db + LOGGER.debug("Creating and saving a TPM baseline from TCG Event Log"); + Session session = sessionFactory.getCurrentSession(); + session.beginTransaction(); + final TpmWhiteListBaseline b = tlp.createTPMBaseline("TcgEventLogTestBaseline"); + session.save(b); + session.getTransaction().commit(); + // Check that the TPM Baseline contains he correct info + for (int i = 0; i < 24; i++) { + Set records = b.getPCRHashes(i); + for (Digest digest:records) { + String pcrValue = HexUtils.byteArrayToHexString(digest.getDigest()); + if (pcrFromLog[i].compareToIgnoreCase(pcrValue) != 0) { + testPass = false; + LOGGER.error("\testTPMBaselineCreate error with PCR " + i); + } + } + } + Assert.assertTrue(testPass); + LOGGER.debug("OK. Create and save TPM baseline from TCG Event Log was a success"); + } +} diff --git a/HIRS_Utils/src/test/java/hirs/tpm/eventlog/package-info.java b/HIRS_Utils/src/test/java/hirs/tpm/eventlog/package-info.java new file mode 100644 index 00000000..3aab200e --- /dev/null +++ b/HIRS_Utils/src/test/java/hirs/tpm/eventlog/package-info.java @@ -0,0 +1,5 @@ +/** + * Test classes for the hirs.tpm package. + */ + +package hirs.tpm.eventlog; diff --git a/HIRS_Utils/src/test/resources/tcgeventlog/TpmLog.bin b/HIRS_Utils/src/test/resources/tcgeventlog/TpmLog.bin new file mode 100644 index 00000000..0b8f1f39 Binary files /dev/null and b/HIRS_Utils/src/test/resources/tcgeventlog/TpmLog.bin differ diff --git a/HIRS_Utils/src/test/resources/tcgeventlog/TpmLogExpectedPcrs.txt b/HIRS_Utils/src/test/resources/tcgeventlog/TpmLogExpectedPcrs.txt new file mode 100644 index 00000000..2e8fdd9b --- /dev/null +++ b/HIRS_Utils/src/test/resources/tcgeventlog/TpmLogExpectedPcrs.txt @@ -0,0 +1,24 @@ +5ef6c69a589a96b5ade6a09e960eb341e6f68a8239df66be34e5e991ddde97a8 +0f16d93fe0cbe7114fd9fefeb1d98a0802b184b6077f05275269aa90ebb8a993 +966eb0b055e5b656f81c08ed1b2107cdea5740f321382d07a0eade7d014addee +3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969 +c919e77702cb066016b575c008659ba7d758b0b4c3f9df29658e1770699823d1 +45f6dd68feb493ec2f371f2fbd2f904181a20e9491102304f239745f6fd1eaf6 +3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969 +65caf8dd1e0ea7a6347b635d2b379c93b9a1351edc2afc3ecda700e534eb3068 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000 diff --git a/HIRS_Utils/src/test/resources/tcgeventlog/TpmLogSHA1.bin b/HIRS_Utils/src/test/resources/tcgeventlog/TpmLogSHA1.bin new file mode 100644 index 00000000..95b74c38 Binary files /dev/null and b/HIRS_Utils/src/test/resources/tcgeventlog/TpmLogSHA1.bin differ diff --git a/HIRS_Utils/src/test/resources/tcgeventlog/TpmLogSHA1ExpectedPcrs.txt b/HIRS_Utils/src/test/resources/tcgeventlog/TpmLogSHA1ExpectedPcrs.txt new file mode 100644 index 00000000..d404746a --- /dev/null +++ b/HIRS_Utils/src/test/resources/tcgeventlog/TpmLogSHA1ExpectedPcrs.txt @@ -0,0 +1,24 @@ +1f1e9bf7dea0be1c37c999c4233b0164ed577607 +46f041010f19e5e74aa33e04467c59759af3fca4 +b2a83b0ebf2f8374299a5b2bdfc31ea955ad7236 +b2a83b0ebf2f8374299a5b2bdfc31ea955ad7236 +f36f2acdb5134d2560e7784002f606573bac99d5 +ed6db334e4e0f3811c18b9e79601b0c16d5a5b2b +b2a83b0ebf2f8374299a5b2bdfc31ea955ad7236 +54f675801f2f654bf53fc61c36837198fddd7a85 +0000000000000000000000000000000000000000 +0000000000000000000000000000000000000000 +0000000000000000000000000000000000000000 +0000000000000000000000000000000000000000 +0000000000000000000000000000000000000000 +0000000000000000000000000000000000000000 +0000000000000000000000000000000000000000 +0000000000000000000000000000000000000000 +0000000000000000000000000000000000000000 +0000000000000000000000000000000000000000 +0000000000000000000000000000000000000000 +0000000000000000000000000000000000000000 +0000000000000000000000000000000000000000 +0000000000000000000000000000000000000000 +0000000000000000000000000000000000000000 +0000000000000000000000000000000000000000