[222] Added TCG Event Log Processing that converts TCG Event Logs to HIRS T… (#223)

* Added TCG Event Log Processing that converts TCG Event Logs to HIRS TPM Baselines

* Some minor formating, syntax and code refactoring updates.

* Updated checkstyle failures.

* String format was missing additional %s.

Co-authored-by: Cyrus <24922493+cyrus-dev@users.noreply.github.com>
This commit is contained in:
iadgovuser26 2020-02-21 06:37:43 -05:00 committed by GitHub
parent 6838a38fbc
commit 9a835d8923
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 1247 additions and 1 deletions

View File

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

View File

@ -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<TpmPcrEvent> eventList = new ArrayList<TpmPcrEvent>();
/**
* 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();
}
}

View File

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

View File

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

View File

@ -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.
* <p>
* 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;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 <code>SessionFactory</code>. 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 <code>SessionFactory</code> 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 <code>Baseline</code> 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<Digest> 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");
}
}

View File

@ -0,0 +1,5 @@
/**
* Test classes for the hirs.tpm package.
*/
package hirs.tpm.eventlog;

Binary file not shown.

View File

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

View File

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