[] Update RIM details page to display PCRs ()

* This is an update to the display of the Reference Integrity Manifest code base that'll allow a user to upload a swidtag.  This code includes some additions from , slightly modified.

* This code update include changes to import, archive and delete a swidtag into the RIM object.

* Updated the code with additional checks on the uploaded file locations.  Added the number associated with the PCR value to the detail page.

* This change fixes the bug that caused the rim detail page to go blank if the associated event log file associated with the resource file doesn't exist.

Co-authored-by: lareine <lareine@tycho.ncsc.mil>
This commit is contained in:
Cyrus 2020-03-06 07:06:09 -05:00 committed by GitHub
parent 5dbbbafafe
commit 21db725815
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 415 additions and 367 deletions

@ -1,14 +1,22 @@
package hirs.attestationca.portal.page.controllers;
import hirs.data.persist.ReferenceManifest;
import hirs.data.persist.SwidResource;
import hirs.persist.ReferenceManifestManager;
import hirs.tpm.eventlog.TCGEventLogProcessor;
import hirs.attestationca.portal.page.Page;
import hirs.attestationca.portal.page.PageController;
import hirs.attestationca.portal.page.PageMessages;
import hirs.attestationca.portal.page.params.ReferenceManifestDetailsPageParams;
import java.io.IOException;
import java.nio.file.NoSuchFileException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -24,14 +32,15 @@ import org.springframework.web.servlet.ModelAndView;
@Controller
@RequestMapping("/rim-details")
public class ReferenceManifestDetailsPageController
extends PageController<ReferenceManifestDetailsPageParams> {
extends PageController<ReferenceManifestDetailsPageParams> {
private final ReferenceManifestManager referenceManifestManager;
private static final Logger LOGGER =
LogManager.getLogger(ReferenceManifestDetailsPageController.class);
private static final Logger LOGGER
= LogManager.getLogger(ReferenceManifestDetailsPageController.class);
/**
* Constructor providing the Page's display and routing specification.
*
* @param referenceManifestManager the reference manifest manager
*/
@Autowired
@ -92,12 +101,14 @@ extends PageController<ReferenceManifestDetailsPageParams> {
/**
* This method takes the place of an entire class for a string builder.
* Gathers all information and returns it for displays.
*
* @param uuid database reference for the requested RIM.
* @param referenceManifestManager the reference manifest manager.
* @return mapping of the RIM information from the database.
* @throws java.io.IOException error for reading file bytes.
*/
public static HashMap<String, Object> getRimDetailInfo(final UUID uuid,
final ReferenceManifestManager referenceManifestManager) {
final ReferenceManifestManager referenceManifestManager) throws IOException {
HashMap<String, Object> data = new HashMap<>();
ReferenceManifest rim = ReferenceManifest
@ -140,7 +151,33 @@ extends PageController<ReferenceManifestDetailsPageParams> {
// checkout later
data.put("rimType", rim.getRimType());
data.put("swidFiles", rim.parseResource());
List<SwidResource> resources = rim.parseResource();
String resourceFilename = null;
TCGEventLogProcessor logProcessor = new TCGEventLogProcessor();
try {
for (SwidResource swidRes : resources) {
resourceFilename = swidRes.getName();
Path logPath = Paths.get(String.format("%s/%s",
SwidResource.RESOURCE_UPLOAD_FOLDER,
resourceFilename));
if (Files.exists(logPath)) {
logProcessor = new TCGEventLogProcessor(
Files.readAllBytes(logPath));
swidRes.setPcrValues(Arrays.asList(
logProcessor.getExpectedPCRValues()));
} else {
swidRes.setPcrValues(Arrays.asList(
logProcessor.getExpectedPCRValues()));
}
}
} catch (NoSuchFileException nsfEx) {
LOGGER.error(String.format("File Not found!: %s",
resourceFilename));
LOGGER.error(nsfEx);
}
data.put("swidFiles", resources);
} else {
LOGGER.error(String.format("Unable to find Reference Integrity "
+ "Manifest with ID: %s", uuid));

@ -14,6 +14,7 @@ import hirs.persist.DBManagerException;
import hirs.persist.ReferenceManifestManager;
import hirs.persist.CriteriaModifier;
import hirs.data.persist.ReferenceManifest;
import hirs.data.persist.SwidResource;
import hirs.data.persist.certificate.Certificate;
import java.io.IOException;
import java.net.URISyntaxException;
@ -21,9 +22,14 @@ import java.net.URISyntaxException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import javax.servlet.http.HttpServletResponse;
import org.apache.logging.log4j.LogManager;
@ -44,36 +50,36 @@ import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.servlet.view.RedirectView;
/**
* Controller for the Reference Manifest page.
*/
@Controller
@RequestMapping("/reference-manifests")
public class ReferenceManifestPageController
extends PageController<NoPageParams> {
extends PageController<NoPageParams> {
private static final String BIOS_RELEASE_DATE_FORMAT = "yyyy-MM-dd";
private final BiosDateValidator biosValidator;
private final ReferenceManifestManager referenceManifestManager;
private static final Logger LOGGER =
LogManager.getLogger(ReferenceManifestPageController.class);
private static final Logger LOGGER
= LogManager.getLogger(ReferenceManifestPageController.class);
/**
* This class was created for the purposes of avoiding findbugs message:
* As the JavaDoc states, DateFormats are inherently unsafe for
* multi-threaded use. The detector has found a call to an instance
* of DateFormat that has been obtained via a static field.
* This looks suspicious.
* This class was created for the purposes of avoiding findbugs message: As
* the JavaDoc states, DateFormats are inherently unsafe for multi-threaded
* use. The detector has found a call to an instance of DateFormat that has
* been obtained via a static field. This looks suspicious.
*
* This class can have uses elsewhere but for now it will remain here.
*/
private static final class BiosDateValidator {
private final String dateFormat;
/**
* Default constructor that sets the format to parse against.
*
* @param dateFormat
*/
public BiosDateValidator(final String dateFormat) {
@ -82,6 +88,7 @@ extends PageController<NoPageParams> {
/**
* Validates a date by attempting to parse based on format provided.
*
* @param date string of the given date
* @return true if the format matches
*/
@ -102,6 +109,7 @@ extends PageController<NoPageParams> {
/**
* Constructor providing the Page's display and routing specification.
*
* @param referenceManifestManager the reference manifest manager
*/
@Autowired
@ -113,11 +121,12 @@ extends PageController<NoPageParams> {
}
/**
* Returns the path for the view and the data model for the page.
* Returns the filePath for the view and the data model for the page.
*
* @param params The object to map url parameters into.
* @param model The data model for the request. Can contain data from redirect.
* @return the path for the view and data model for the page.
* @param model The data model for the request. Can contain data from
* redirect.
* @return the filePath for the view and data model for the page.
*/
@Override
public ModelAndView initPage(final NoPageParams params,
@ -126,11 +135,12 @@ extends PageController<NoPageParams> {
}
/**
* Returns the list of RIMs using the data table input for paging,
* ordering, and filtering.
* Returns the list of RIMs using the data table input for paging, ordering,
* and filtering.
*
* @param input the data tables input
* @return the data tables response, including the result set
* and paging information
* @return the data tables response, including the result set and paging
* information
*/
@ResponseBody
@RequestMapping(value = "/list",
@ -157,7 +167,6 @@ extends PageController<NoPageParams> {
referenceManifestManager,
input, orderColumnName, criteriaModifier);
LOGGER.debug("Returning list of size: " + records.size());
return new DataTableResponse<>(records, input);
}
@ -177,17 +186,44 @@ extends PageController<NoPageParams> {
final RedirectAttributes attr) throws URISyntaxException, Exception {
Map<String, Object> model = new HashMap<>();
PageMessages messages = new PageMessages();
List<MultipartFile> rims = new ArrayList<>();
String fileName;
Path filePath;
// loop through the files
for (MultipartFile file : files) {
fileName = file.getOriginalFilename();
if (fileName.toLowerCase().endsWith("swidtag")) {
rims.add(file);
} else {
filePath = Paths.get(String.format("%s/%s",
SwidResource.RESOURCE_UPLOAD_FOLDER,
file.getOriginalFilename()));
if (Files.notExists(Paths.get(SwidResource.RESOURCE_UPLOAD_FOLDER))) {
Files.createDirectory(Paths.get(SwidResource.RESOURCE_UPLOAD_FOLDER));
}
if (Files.notExists(filePath)) {
Files.createFile(filePath);
}
Files.write(filePath, file.getBytes());
String uploadCompletedMessage = String.format(
"%s successfully uploaded", file.getOriginalFilename());
messages.addSuccess(uploadCompletedMessage);
LOGGER.info(uploadCompletedMessage);
}
}
for (MultipartFile file : rims) {
//Parse reference manifests
ReferenceManifest rims = parseRIMs(file, messages);
ReferenceManifest rim = parseRIM(file, messages);
//Store only if it was parsed
if (rims != null) {
if (rim != null) {
storeManifest(file.getOriginalFilename(),
messages,
rims,
rim,
referenceManifestManager);
}
}
@ -300,22 +336,23 @@ extends PageController<NoPageParams> {
* @throws IllegalArgumentException
*/
private ReferenceManifest getRimFromDb(final String id) throws IllegalArgumentException {
UUID uuid = UUID.fromString(id);
UUID uuid = UUID.fromString(id);
return ReferenceManifest
.select(referenceManifestManager)
.byEntityId(uuid).getRIM();
return ReferenceManifest
.select(referenceManifestManager)
.byEntityId(uuid).getRIM();
}
/**
* Takes the rim files provided and returns a {@link ReferenceManifest}
* object.
*
* @param file the provide user file via browser.
* @param messages the object that handles displaying information to the
* user.
* @return a single or collection of reference manifest files.
*/
private ReferenceManifest parseRIMs(
private ReferenceManifest parseRIM(
final MultipartFile file,
final PageMessages messages) {
@ -348,6 +385,7 @@ extends PageController<NoPageParams> {
/**
* Stores the {@link ReferenceManifest} objects.
*
* @param fileName name of the file given
* @param messages message object for user display of statuses
* @param referenceManifest the object to store

@ -107,51 +107,73 @@
</div>
<div id="directorycollapse" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="headingOne" aria-expanded="true">
<div class="panel-body">
<div class="panel-heading" role="tab" id="headingThree">
<h3 class="panel-title">
<a role="button" data-toggle="collapse" data-parent="#directorycollapse" class="collapsed"
href="#filescollapse" aria-expanded="false" aria-controls="filescollapse">
Files
</a>
</h3>
</div>
<div id="filescollapse" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingThree" aria-expanded="true">
<c:if test="${not empty initialData.swidFiles}">
<div id="componentIdentifier" class="row">
<c:forEach items="${initialData.swidFiles}" var="resource">
<div class="component col col-md-10" style="padding-left: 20px">
<div class="panel panel-default">
<div class="panel-heading">
<span data-toggle="tooltip" data-placement="top" title="Resource File">${resource.getName()}
</span>
</div>
<div class="component col col-md-10">
<span class="fieldHeader">File Size:</span>
<span class="fieldValue">${resource.getSize()}</span><br/>
<span class="fieldHeader">Hash:</span>
<span class="fieldValue" style="overflow-wrap: break-word">${resource.getHashValue()}</span><br/>
<c:if test="${not empty resource.getRimFormat()}">
<span class="fieldHeader">RIM Format:</span>
<span class="fieldValue">${resource.getRimFormat()}</span><br/>
</c:if>
<c:if test="${not empty resource.getRimType()}">
<span class="fieldHeader">RIM Type:</span>
<span class="fieldValue">${resource.getRimType()}</span><br/>
</c:if>
<c:if test="${not empty resource.getRimUriGlobal()}">
<span class="fieldHeader">URI Global:</span>
<span class="fieldValue">${resource.getRimUriGlobal()}</span><br/>
</c:if>
</div>
</div>
</div>
</c:forEach>
</div>
</c:if>
</div>
<div class="panel-heading" role="tab" id="headingThree">
<h3 class="panel-title">
<a role="button" data-toggle="collapse" data-parent="#directorycollapse" class="collapsed"
href="#filescollapse" aria-expanded="false" aria-controls="filescollapse">
Files
</a>
</h3>
</div>
<div id="filescollapse" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingThree" aria-expanded="true">
<c:if test="${not empty initialData.swidFiles}">
<div id="componentIdentifier" class="row">
<c:forEach items="${initialData.swidFiles}" var="resource">
<div class="component col col-md-10" style="padding-left: 20px">
<div class="panel panel-default">
<div class="panel-heading">
<span data-toggle="tooltip" data-placement="top" title="Resource File">${resource.getName()}
</span>
</div>
<div class="component col col-md-10">
<span class="fieldHeader">File Size:</span>
<span class="fieldValue">${resource.getSize()}</span><br/>
<span class="fieldHeader">Hash:</span>
<span class="fieldValue" style="overflow-wrap: break-word">${resource.getHashValue()}</span><br/>
<c:if test="${not empty resource.getRimFormat()}">
<span class="fieldHeader">RIM Format:</span>
<span class="fieldValue">${resource.getRimFormat()}</span><br/>
</c:if>
<c:if test="${not empty resource.getRimType()}">
<span class="fieldHeader">RIM Type:</span>
<span class="fieldValue">${resource.getRimType()}</span><br/>
</c:if>
<c:if test="${not empty resource.getRimUriGlobal()}">
<span class="fieldHeader">URI Global:</span>
<span class="fieldValue">${resource.getRimUriGlobal()}</span><br/>
</c:if>
<c:if test="${not empty resource.getPcrValues()}">
<div class="panel-body">
<div class="component" role="tab" id="pcrValues">
<a role="button" data-toggle="collapse" data-parent="#directorycollapse" class="collapsed"
href="#pcrscollapse" aria-expanded="false" aria-controls="pcrscollapse">
Expected PCR Values
</a>
</div>
<div id="pcrscollapse" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingThree" aria-expanded="true">
<div>
<c:forEach items="${resource.getPcrMap()}" var="pcrValue">
<div id="componentIdentifier" class="row">
<div>
<span>${pcrValue.key}</span>
<span style="overflow-wrap: break-word">${pcrValue.value}</span>
</div>
</div>
</c:forEach>
</div>
</div>
</div>
</c:if>
</div>
</div>
</div>
</c:forEach>
</div>
</c:if>
</div>
</div>
</div>
</div>
@ -160,4 +182,4 @@
</div>
</div>
</jsp:body>
</my:page>
</my:page>

@ -3,7 +3,11 @@ package hirs.data.persist;
import com.google.common.base.Preconditions;
import hirs.utils.xjc.File;
import java.util.Map;
import java.util.List;
import java.util.LinkedHashMap;
import java.util.Collections;
import java.math.BigInteger;
import java.text.DecimalFormat;
import javax.xml.namespace.QName;
/**
@ -12,9 +16,20 @@ import javax.xml.namespace.QName;
*/
public class SwidResource {
private static final String CATALINA_HOME = System.getProperty("catalina.base");
private static final String TOMCAT_UPLOAD_DIRECTORY
= "/webapps/HIRS_AttestationCAPortal/upload/";
/**
* String holder for location for storing binaries.
*/
public static final String RESOURCE_UPLOAD_FOLDER
= CATALINA_HOME + TOMCAT_UPLOAD_DIRECTORY;
private String name, size;
private String rimFormat, rimType, rimUriGlobal, hashValue;
private List<String> pcrValues;
/**
* Default constructor.
@ -26,6 +41,7 @@ public class SwidResource {
rimType = null;
rimUriGlobal = null;
hashValue = null;
pcrValues = null;
}
/**
@ -112,4 +128,40 @@ public class SwidResource {
public String getHashValue() {
return hashValue;
}
/**
* Getter for the list of PCR Values.
* @return an unmodifiable list
*/
public List<String> getPcrValues() {
return Collections.unmodifiableList(pcrValues);
}
/**
* Setter for the list of associated PCR Values.
* @param pcrValues a collection of PCRs
*/
public void setPcrValues(final List<String> pcrValues) {
this.pcrValues = pcrValues;
}
/**
* Getter for a generated map of the PCR values.
* @return mapping of PCR# to the actual value.
*/
public LinkedHashMap<String, String> getPcrMap() {
LinkedHashMap<String, String> innerMap = new LinkedHashMap<>();
DecimalFormat df = new DecimalFormat("00");
if (!this.pcrValues.isEmpty()) {
long iterate = 0;
String pcrNum;
for (String string : this.pcrValues) {
pcrNum = df.format(iterate++);
innerMap.put(String.format("PCR%s:", pcrNum), string);
}
}
return innerMap;
}
}

@ -1,138 +1,11 @@
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.
* 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<>();
public class CryptoAgileEventLog {
/**
* 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();
}
}

@ -1,134 +1,11 @@
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.
* 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];
public class SHA1EventLog {
/**
* 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();
}
}

@ -1,16 +1,173 @@
package hirs.tpm.eventlog;
import hirs.utils.HexUtils;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* 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
public class TCGEventLog {
private static final Logger LOGGER
= LogManager.getLogger(TCGEventLog.class);
/**
* Init value for SHA 256 values.
*/
String[] getExpectedPCRValues();
/** Retrieves a single expected PCR values.
* @param index the PCR reference
* @return a String holding an expected PCR value
public static final String INIT_SHA256_LIST = "00000000000000000000000000"
+ "00000000000000000000000000000000000000";
/**
* Init value for SHA 1 values.
*/
String getExpectedPCRValue(int index);
public static final String INIT_SHA1_LIST = "0000000000000000000000000000000000000000";
/**
* 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;
/**
* 2 dimensional array holding the PCR values.
*/
private final byte[][] pcrList;
/**
* List of parsed events within the log.
*/
private final ArrayList<TpmPcrEvent> eventList = new ArrayList<>();
private int pcrLength;
private String hashType;
private String initValue;
/**
* Default blank object constructor.
*/
public TCGEventLog() {
this.pcrList = new byte[PCR_COUNT][TpmPcrEvent.SHA1_LENGTH];
initValue = INIT_SHA1_LIST;
pcrLength = TpmPcrEvent.SHA1_LENGTH;
initPcrList();
}
/**
* Default constructor for just the rawlog that'll set up SHA1 Log.
* @param rawlog data for the event log file
* @throws IOException IO Stream for the event log
*/
public TCGEventLog(final byte[] rawlog) throws IOException {
this(rawlog, TpmPcrEvent.SHA1_LENGTH, HASH_STRING, INIT_SHA1_LIST);
}
/**
* Default constructor for specific log.
* @param rawlog data for the event log file
* @param pcrLength determined by SHA1 or 256
* @param hashType the type of algorithm
* @param initValue the default blank value
* @throws IOException IO Stream for the event log
*/
public TCGEventLog(final byte[] rawlog, final int pcrLength,
final String hashType, final String initValue) throws IOException {
this.pcrLength = pcrLength;
this.pcrList = new byte[PCR_COUNT][pcrLength];
this.hashType = hashType;
this.initValue = initValue;
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();
}
/**
* This method puts blank values in the pcrList.
*/
private void initPcrList() {
for (int i = 0; i < PCR_COUNT; i++) { // Initialize the PCRlist1 array
System.arraycopy(HexUtils.hexStringToByteArray(
initValue),
0, pcrList[i], 0, pcrLength);
}
}
/**
* 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) {
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 NoSuchAlgorithmException if hash algorithm not supported
*/
private byte[] extendPCR(final byte[] currentValue, final byte[] newEvent)
throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance(hashType);
md.update(HexUtils.hexStringToByteArray(HexUtils.byteArrayToHexString(currentValue)
+ HexUtils.byteArrayToHexString(newEvent)));
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] = HexUtils.byteArrayToHexString(pcrList[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(pcrList[index]);
}
}

@ -20,10 +20,6 @@ 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.
*/
@ -37,6 +33,13 @@ public class TCGEventLogProcessor {
*/
private static final int SIG_SIZE = 16;
/**
* Default Constructor.
*/
public TCGEventLogProcessor() {
tcgLog = new TCGEventLog();
}
/**
* Constructor.
*
@ -45,9 +48,10 @@ public class TCGEventLogProcessor {
*/
public TCGEventLogProcessor(final byte[] rawLog) throws IOException {
if (isLogCrytoAgile(rawLog)) {
tcgLog = new CryptoAgileEventLog(rawLog);
tcgLog = new TCGEventLog(rawLog, TpmPcrEvent.SHA256_LENGTH,
TCGEventLog.HASH256_STRING, TCGEventLog.INIT_SHA256_LIST);
} else {
tcgLog = new SHA1EventLog(rawLog);
tcgLog = new TCGEventLog(rawLog);
algorithm = "SHA";
}
}
@ -80,8 +84,8 @@ public class TCGEventLogProcessor {
*/
public TpmWhiteListBaseline createTPMBaseline(final String name) {
TpmWhiteListBaseline baseline = new TpmWhiteListBaseline(name);
TPMMeasurementRecord record = null;
String pcrValue = "";
TPMMeasurementRecord record;
String pcrValue;
for (int i = 0; i < TpmPcrEvent.PCR_COUNT; i++) {
if (algorithm.compareToIgnoreCase("SHA1") == 0) { // Log Was SHA1 Format
pcrValue = tcgLog.getExpectedPCRValue(i);
@ -112,15 +116,13 @@ public class TCGEventLogProcessor {
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) {
if (eventID != TCGEventLog.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);
return sig.equals("Spec ID Event03");
}
}

@ -167,16 +167,6 @@ public class TpmPcrEvent {
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.
*

@ -41,7 +41,7 @@ public class TpmPcrEvent1 extends TpmPcrEvent {
setEventType(unit32Data);
byte[] eventDigest = new byte[SHA1_LENGTH];
is.read(eventDigest);
setDigest(eventDigest);
setEventDigest(eventDigest);
is.read(unit32Data);
int eventSize = HexUtils.leReverseInt(unit32Data);
byte[] eventContent = new byte[eventSize];

@ -84,7 +84,7 @@ public class TpmPcrEvent2 extends TpmPcrEvent {
hashAlg = new TcgTpmtHa(is);
hashlist.add(hashAlg);
if (hashAlg.getHashName().compareToIgnoreCase("TPM_ALG_SHA256") == 0) {
setDigest(hashAlg.getDigest());
setEventDigest(hashAlg.getDigest());
}
}
is.read(rawInt);