Merge pull request #762 from nsacyber/v3_issue_747-spdm

Process SPDM Event EV_EFI_SPDM_FIRMWARE_BLOB
This commit is contained in:
D2B8CA1B27286366A8607B6858C0565962613D18D0546480078B520CD7AD705A 2024-05-01 18:30:46 +00:00 committed by GitHub
commit a418d11293
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 1129 additions and 0 deletions

View File

@ -5,6 +5,7 @@ import hirs.utils.tpm.eventlog.events.EvCompactHash;
import hirs.utils.tpm.eventlog.events.EvConstants;
import hirs.utils.tpm.eventlog.events.EvEfiGptPartition;
import hirs.utils.tpm.eventlog.events.EvEfiHandoffTable;
import hirs.utils.tpm.eventlog.events.EvEfiSpdmFirmwareBlob;
import hirs.utils.tpm.eventlog.events.EvEfiSpecIdEvent;
import hirs.utils.tpm.eventlog.events.EvEventTag;
import hirs.utils.tpm.eventlog.events.EvIPL;
@ -374,6 +375,14 @@ public class TpmPcrEvent {
break;
case EvConstants.EV_EFI_HCRTM_EVENT:
break;
case EvConstants.EV_EFI_SPDM_FIRMWARE_BLOB:
try {
sb.append(new EvEfiSpdmFirmwareBlob(eventContent).toString());
} catch (UnsupportedEncodingException ueEx) {
log.error(ueEx);
sb.append(ueEx.toString());
}
break;
default:
sb.append("Unknown Event found\n");
}
@ -532,6 +541,9 @@ public class TpmPcrEvent {
case EvConstants.EV_EFI_VARIABLE_AUTHORITY:
description += "Event Content:\n" + new UefiVariable(content).toString();
break;
case EvConstants.EV_EFI_SPDM_FIRMWARE_BLOB:
description += "Event Content:\n" + new EvEfiSpdmFirmwareBlob(content).toString();
break;
default:
description += " Unknown Event found" + "\n";
}
@ -609,6 +621,8 @@ public class TpmPcrEvent {
return "EV_EFI_HCRTM_EVENT";
} else if (event == EvConstants.EV_EFI_VARIABLE_AUTHORITY) {
return "EV_EFI_VARIABLE_AUTHORITY";
} else if (event == EvConstants.EV_EFI_SPDM_FIRMWARE_BLOB) {
return "EV_EFI_SPDM_FIRMWARE_BLOB";
} else {
return "Unknown Event ID " + event + " encountered";
}

View File

@ -0,0 +1,103 @@
package hirs.utils.tpm.eventlog.events;
import lombok.Getter;
/**
* Abstract base class to process the DEVICE_SECURITY_EVENT_DATA or ..DATA2 event.
* Parses event data per PFP v1.06 Rev52 Tables 20 and 26.
* The event data comes in 2 forms:
* 1) DEVICE_SECURITY_EVENT_DATA or
* 2) DEVICE_SECURITY_EVENT_DATA2
* The first 2 fields of the respective headers are the same in both ..DATA and ..DATA2.
* Field 1:
* The first 16 bytes of the event data header MUST be a String based identifier (Signature),
* NUL-terminated, per PFP. The only currently defined Signature is "SPDM Device Sec", which
* implies the data is a DEVICE_SECURITY_EVENT_DATA or ..DATA2.
* Field 2:
* The Version field indicates whether the Device Security Event is ..DATA or ..DATA2.
*
* DEVICE SECURITY EVENT structures defined by PFP v1.06 Rev 52:
* <p>
* typedef struct tdDEVICE_SECURITY_EVENT_DATA {
* DEVICE_SECURITY_EVENT_DATA_HEADER EventDataHeader;
* DEVICE_SECURITY_EVENT_DATA_DEVICE_CONTEXT DeviceContext;
* } DEVICE_SECURITY_EVENT_DATA;
* <p>
* typedef struct tdDEVICE_SECURITY_EVENT_DATA2 {
* DEVICE_SECURITY_EVENT_DATA_HEADER2 EventDataHeader;
* DEVICE_SECURITY_EVENT_DATA_SUB_HEADER EventDataSubHeader;
* DEVICE_SECURITY_EVENT_DATA_DEVICE_CONTEXT DeviceContext;
* } DEVICE_SECURITY_EVENT_DATA2;
* <p>
* typedef struct tdDEVICE_SECURITY_EVENT_DATA_HEADER or HEADER2 {
* UINT8 Signature[16];
* UINT16 Version;
* ... ...
* }
* <p>
* Notes:
* 1. Has an EventType of EV_EFI_SPDM_FIRMWARE_BLOB (0x800000E1)
* 2. Event content defined as DEVICE_SECURITY_EVENT_DATA Struct.
* 3. First 16 bytes of the structure header is an ASCII "SPDM Device Sec"
* <p>
* Only a few of the Device Security Event Data events have been implemented as there are many,
* but only those that were reported using the test devices at hand.
* Without test patterns, the processing may lead to an un-handled exception.
* For now, the only test pattern uses ..DeviceContext with PCI only, without USB -> assume only 1
* even though the spec says both are in the data structure. If it is only 1, though, there's no
* method to tell them apart.
*/
public abstract class DeviceSecurityEvent {
/**
* Human readable description of the data within the
* DEVICE_SECURITY_EVENT_DATA_DEVICE_CONTEXT. DEVICE can be either PCI or USB.
*/
@Getter
String deviceContextInfo = "";
/**
* DeviceSecurityEventData Default Constructor.
*
*/
public DeviceSecurityEvent() {
}
/**
* Parse the Device Context structure, can be PCI or USB based on device type field.
*
* @param dSEDbytes byte array holding the DeviceSecurityEventData.
* @param startByte starting byte of the device structure (depends on length of header).
* @param deviceType device type either PCI or USB.
*
*/
public void parseDeviceContext(final byte[] dSEDbytes, int startByte, int deviceType) {
int deviceContextLength = dSEDbytes.length - startByte;
// get the device context bytes
byte[] deviceContextBytes = new byte[deviceContextLength];
System.arraycopy(dSEDbytes, startByte, deviceContextBytes, 0,
deviceContextLength);
if (deviceType == 0) {
deviceContextInfo = "No Device Context (indicated by device type value of 0";
}
else if (deviceType == 1) {
DeviceSecurityEventDataPciContext dSEDpciContext
= new DeviceSecurityEventDataPciContext(deviceContextBytes);
deviceContextInfo = dSEDpciContext.toString();
}
//else if (deviceType == 2) {
//DeviceSecurityEventDataUsbContext dSEDusbContext
// = new DeviceSecurityEventDataUsbContext(deviceContextBytes);
//deviceContextInfo = dSEDusbContext.toString();
//deviceContextInfo = "Device type is USB - to be implemented in future";
//}
else {
deviceContextInfo = " Unknown device type; cannot process device context";
}
}
}

View File

@ -0,0 +1,45 @@
package hirs.utils.tpm.eventlog.events;
import lombok.Getter;
import java.io.UnsupportedEncodingException;
/**
* Class to process DEVICE_SECURITY_EVENT_DATA.
* Parses event data per PFP v1.06 Rev52 Table 20.
* <p>
* typedef struct tdDEVICE_SECURITY_EVENT_DATA {
* DEVICE_SECURITY_EVENT_DATA_HEADER EventDataHeader;
* DEVICE_SECURITY_EVENT_DATA_DEVICE_CONTEXT DeviceContext;
* } DEVICE_SECURITY_EVENT_DATA;
* <p>
*/
public class DeviceSecurityEventData extends DeviceSecurityEvent {
/**
* DeviceSecurityEventDataHeader Object.
*/
@Getter
private DeviceSecurityEventDataHeader dsedHeader = null;
/**
* DeviceSecurityEventData Constructor.
*
* @param dSEDbytes byte array holding the DeviceSecurityEventData.
*/
public DeviceSecurityEventData(final byte[] dSEDbytes) throws UnsupportedEncodingException {
dsedHeader = new DeviceSecurityEventDataHeader(dSEDbytes);
parseDeviceContext(dSEDbytes, dsedHeader.getDSEDheaderByteSize(), dsedHeader.getDeviceType());
}
/**
* Returns a human readable description of the data within this structure.
*
* @return a description of this structure.
*/
public String toString() {
String dsedInfo = "";
dsedInfo += dsedHeader.toString();
dsedInfo += getDeviceContextInfo();
return dsedInfo;
}
}

View File

@ -0,0 +1,46 @@
package hirs.utils.tpm.eventlog.events;
import lombok.Getter;
// TODO Placeholder class to be implemented upon getting test pattern
/**
* Class to process DEVICE_SECURITY_EVENT_DATA2.
* Parses event data per PFP v1.06 Rev52 Table 26.
* <p>
* typedef struct tdDEVICE_SECURITY_EVENT_DATA2 {
* DEVICE_SECURITY_EVENT_DATA_HEADER2 EventDataHeader;
* DEVICE_SECURITY_EVENT_DATA_SUB_HEADER EventDataSubHeader;
* DEVICE_SECURITY_EVENT_DATA_DEVICE_CONTEXT DeviceContext;
* } DEVICE_SECURITY_EVENT_DATA2;
* <p>
*/
public class DeviceSecurityEventData2 extends DeviceSecurityEvent {
/**
* DeviceSecurityEventDataHeader Object.
*/
@Getter
private DeviceSecurityEventDataHeader2 dsedHeader2 = null;
/**
* DeviceSecurityEventData2 Constructor.
*
* @param dSEDbytes byte array holding the DeviceSecurityEventData2.
*/
public DeviceSecurityEventData2(final byte[] dSEDbytes) {
dsedHeader2 = new DeviceSecurityEventDataHeader2(dSEDbytes);
// get subheader
parseDeviceContext(dSEDbytes, dsedHeader2.getDSEDheaderByteSize(), dsedHeader2.getDeviceType());
}
/**
* Returns a human readable description of the data within this structure.
*
* @return a description of this structure.
*/
public String toString() {
String dsedInfo = "";
return dsedInfo;
}
}

View File

@ -0,0 +1,62 @@
package hirs.utils.tpm.eventlog.events;
import hirs.utils.HexUtils;
import lombok.Getter;
/**
* Class to process the DEVICE_SECURITY_EVENT_DATA_DEVICE_CONTEXT event per PFP.
* DEVICE_SECURITY_EVENT_DATA_DEVICE_CONTEXT is a common SPDM structure which includes the
* identification of the device, device vendor, subsystem, etc. Device can be either a PCI
* or USB connection.
* <p>
* typedef union tdDEVICE_SECURITY_EVENT_DATA_DEVICE_CONTEXT {
* DEVICE_SECURITY_EVENT_DATA_PCI_CONTEXT PciContext;
* DEVICE_SECURITY_EVENT_DATA_USB_CONTEXT UsbContext;
* } DEVICE_SECURITY_EVENT_DATA_DEVICE_CONTEXT;
* <p>
*/
public abstract class DeviceSecurityEventDataDeviceContext {
/**
* PCI Version.
*/
@Getter
private int version = 0;
/**
* PCI Length.
*/
@Getter
private int length = 0;
/**
* DeviceSecurityEventDataDeviceContext Constructor.
*
* @param dSEDdeviceContextBytes byte array holding the DeviceSecurityEventData.
*/
public DeviceSecurityEventDataDeviceContext(final byte[] dSEDdeviceContextBytes) {
byte[] pciVersionBytes = new byte[2];
System.arraycopy(dSEDdeviceContextBytes, 0, pciVersionBytes, 0, 2);
version = HexUtils.leReverseInt(pciVersionBytes);
byte[] pciLengthBytes = new byte[2];
System.arraycopy(dSEDdeviceContextBytes, 2, pciLengthBytes, 0, 2);
length = HexUtils.leReverseInt(pciLengthBytes);
}
/**
* Returns a human readable description of the data common to device context structures.
*
* @return a description of this structure..
*/
public String toString() {
String dSEDdeviceContextCommonInfo = "";
dSEDdeviceContextCommonInfo += "\n DeviceSecurityEventData Device Info:";
dSEDdeviceContextCommonInfo += "\n Device Structure Version = " + version;
return dSEDdeviceContextCommonInfo;
}
}

View File

@ -0,0 +1,103 @@
package hirs.utils.tpm.eventlog.events;
import hirs.utils.HexUtils;
import hirs.utils.tpm.eventlog.spdm.SpdmHa;
import hirs.utils.tpm.eventlog.spdm.SpdmMeasurementBlock;
import hirs.utils.tpm.eventlog.uefi.UefiConstants;
import lombok.Getter;
import java.io.UnsupportedEncodingException;
/**
* Class to process the DEVICE_SECURITY_EVENT_DATA_HEADER.
* DEVICE_SECURITY_EVENT_DATA_HEADER contains the measurement(s) and hash algorithm identifier
* returned by the SPDM "GET_MEASUREMENTS" function.
*
* HEADERS defined by PFP v1.06 Rev 52:
* <p>
* typedef struct tdDEVICE_SECURITY_EVENT_DATA_HEADER {
* UINT8 Signature[16];
* UINT16 Version;
* UINT16 Length;
* UINT32 SpdmHashAlg;
* UINT32 DeviceType;
* SPDM_MEASUREMENT_BLOCK SpdmMeasurementBlock;
* UINT64 DevicePathLength;
* UNIT8 DevicePath[DevicePathLength]
* } DEVICE_SECURITY_EVENT_DATA_HEADER;
* <p>
* Assumption: there is only 1 SpdmMeasurementBlock per event. Need more test patterns to verify.
*/
public class DeviceSecurityEventDataHeader extends DeviceSecurityEventHeader {
/**
* Event data length.
*/
@Getter
private int length = 0;
/**
* SPDM hash algorithm.
*/
@Getter
private int spdmHashAlgo = -1;
/**
* SPDM Measurement Block.
*/
private SpdmMeasurementBlock spdmMeasurementBlock = null;
/**
* DeviceSecurityEventDataHeader Constructor.
*
* @param dSEDbytes byte array holding the DeviceSecurityEventData.
*/
public DeviceSecurityEventDataHeader(final byte[] dSEDbytes) throws UnsupportedEncodingException {
super(dSEDbytes);
byte[] lengthBytes = new byte[UefiConstants.SIZE_2];
System.arraycopy(dSEDbytes, 18, lengthBytes, 0,
UefiConstants.SIZE_2);
length = HexUtils.leReverseInt(lengthBytes);
byte[] spdmHashAlgoBytes = new byte[UefiConstants.SIZE_4];
System.arraycopy(dSEDbytes, UefiConstants.OFFSET_20, spdmHashAlgoBytes, 0,
UefiConstants.SIZE_4);
spdmHashAlgo = HexUtils.leReverseInt(spdmHashAlgoBytes);
extractDeviceType(dSEDbytes, 24);
// get the size of the SPDM Measurement Block
byte[] sizeOfSpdmMeasBlockBytes = new byte[UefiConstants.SIZE_2];
System.arraycopy(dSEDbytes, 30, sizeOfSpdmMeasBlockBytes, 0,
UefiConstants.SIZE_2);
int sizeOfSpdmMeas = HexUtils.leReverseInt(sizeOfSpdmMeasBlockBytes);
int sizeOfSpdmMeasBlock = sizeOfSpdmMeas + 4; // header is 4 bytes
// extract the bytes from the SPDM Measurement Block
byte[] spdmMeasBlockBytes = new byte[sizeOfSpdmMeasBlock];
System.arraycopy(dSEDbytes, 28, spdmMeasBlockBytes, 0,
sizeOfSpdmMeasBlock);
spdmMeasurementBlock = new SpdmMeasurementBlock(spdmMeasBlockBytes);
int devPathLenStartByte = 28 + sizeOfSpdmMeasBlock;
extractDevicePathAndFinalSize(dSEDbytes, devPathLenStartByte);
}
/**
* Returns a human readable description of the data within this structure.
*
* @return a description of this structure.
*/
public String toString() {
String dsedHeaderInfo = "";
dsedHeaderInfo += super.toString();
String spdmHashAlgoStr = SpdmHa.tcgAlgIdToString(spdmHashAlgo);
dsedHeaderInfo += "\n SPDM Hash Algorithm = " + spdmHashAlgoStr;
dsedHeaderInfo += "\n SPDM Measurement Block:";
dsedHeaderInfo += spdmMeasurementBlock.toString();
return dsedHeaderInfo;
}
}

View File

@ -0,0 +1,22 @@
package hirs.utils.tpm.eventlog.events;
import hirs.utils.tpm.eventlog.spdm.SpdmHa;
// Placeholder for Header2 data structure.
public class DeviceSecurityEventDataHeader2 extends DeviceSecurityEventHeader {
public DeviceSecurityEventDataHeader2(final byte[] dSEDbytes) {
}
/**
* Returns a human readable description of the data within this structure.
*
* @return a description of this structure.
*/
public String toString() {
String dsedHeader2Info = "";
return dsedHeader2Info;
}
}

View File

@ -0,0 +1,120 @@
package hirs.utils.tpm.eventlog.events;
import hirs.utils.HexUtils;
import lombok.Getter;
/**
* Class to process the DEVICE_SECURITY_EVENT_DATA_PCI_CONTEXT event per PFP.
* <p>
* typedef struct tdDEVICE_SECURITY_EVENT_DATA_PCI_CONTEXT {
* UINT16 Version;
* UINT16 Length;
* UINT16 VendorId;
* UINT16 DeviceId;
* UINT16 RevisionId;
* UINT16 ClassCode[3];
* UINT16 SubsystemVendorId;
* UINT16 SubsystemId;
* <p>
* The following fields are defined by the PCI Express Base Specification rev4.0 v1.0.
* VendorId
* DeviceId
* RevisionId
* ClassCode
* SubsystemVendorId
* SubsystemId
* Vendor id and device id are registered to specific manufacturers.
* https://admin.pci-ids.ucw.cz/read/PC/
* Ex. vendor id 8086 and device id 0b60: https://admin.pci-ids.ucw.cz/read/PC/8086/0b60
* Class code can be looked up on the web.
* https://admin.pci-ids.ucw.cz/read/PD/
* The revision ID is controlled by the vendor and cannot be looked up.
*/
public class DeviceSecurityEventDataPciContext extends DeviceSecurityEventDataDeviceContext {
/**
* PCI Vendor ID.
*/
@Getter
private String vendorId = "";
/**
* PCI Device ID.
*/
@Getter
private String deviceId = "";
/**
* PCI Revision ID.
*/
@Getter
private String revisionId = "";
/**
* PCI Class Code.
*/
@Getter
private String classCode = "";
/**
* PCI Subsystem Vendor ID.
*/
@Getter
private String subsystemVendorId = "";
/**
* PCI Subsystem ID.
*/
@Getter
private String subsystemId = "";
/**
* DeviceSecurityEventDataPciContext Constructor.
*
* @param dSEDpciContextBytes byte array holding the DeviceSecurityEventDataPciContext.
*/
public DeviceSecurityEventDataPciContext(final byte[] dSEDpciContextBytes) {
super(dSEDpciContextBytes);
byte[] pciVendorIdBytes = new byte[2];
System.arraycopy(dSEDpciContextBytes, 4, pciVendorIdBytes, 0, 2);
vendorId = HexUtils.byteArrayToHexString(HexUtils.leReverseByte(pciVendorIdBytes));
byte[] pciDeviceIdBytes = new byte[2];
System.arraycopy(dSEDpciContextBytes, 6, pciDeviceIdBytes, 0, 2);
deviceId = HexUtils.byteArrayToHexString(HexUtils.leReverseByte(pciDeviceIdBytes));
byte[] pciRevisionIdBytes = new byte[1];
System.arraycopy(dSEDpciContextBytes, 8, pciRevisionIdBytes, 0, 1);
revisionId = HexUtils.byteArrayToHexString(HexUtils.leReverseByte(pciRevisionIdBytes));
byte[] pciClassCodeBytes = new byte[3];
System.arraycopy(dSEDpciContextBytes, 9, pciClassCodeBytes, 0, 3);
classCode = HexUtils.byteArrayToHexString(HexUtils.leReverseByte(pciClassCodeBytes));
byte[] pciSubsystemVendorIdBytes = new byte[2];
System.arraycopy(dSEDpciContextBytes, 12, pciSubsystemVendorIdBytes, 0, 2);
subsystemVendorId = HexUtils.byteArrayToHexString(HexUtils.leReverseByte(pciSubsystemVendorIdBytes));
byte[] pciSubsystemIdBytes = new byte[2];
System.arraycopy(dSEDpciContextBytes, 14, pciSubsystemIdBytes, 0, 2);
subsystemId = HexUtils.byteArrayToHexString(HexUtils.leReverseByte(pciSubsystemIdBytes));
}
/**
* Returns a human readable description of the data within this structure.
*
* @return a description of this structure..
*/
public String toString() {
String dSEDpciContextInfo = "";
dSEDpciContextInfo += super.toString();
dSEDpciContextInfo += "\n Device Type = PCI";
dSEDpciContextInfo += "\n VendorID = 0x" + vendorId;
dSEDpciContextInfo += "\n DeviceID = 0x" + deviceId;
dSEDpciContextInfo += "\n RevisionID = 0x" + revisionId;
dSEDpciContextInfo += "\n ClassCode = 0x" + classCode;
dSEDpciContextInfo += "\n SubsystemVendorID = 0x" + subsystemVendorId;
dSEDpciContextInfo += "\n SubsystemID = 0x" + subsystemId;
return dSEDpciContextInfo;
}
}

View File

@ -0,0 +1,220 @@
package hirs.utils.tpm.eventlog.events;
import hirs.utils.HexUtils;
import hirs.utils.tpm.eventlog.uefi.UefiConstants;
import hirs.utils.tpm.eventlog.uefi.UefiDevicePath;
import lombok.Getter;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
/**
* Abstract class to process the DEVICE_SECURITY_EVENT_DATA_HEADER or ..HEADER2 per PFP.
* The first 16 bytes of the event data header MUST be a String based identifier (Signature),
* NUL-terminated, per PFP. The only currently defined Signature is "SPDM Device Sec",
* which implies the data is a DEVICE_SECURITY_EVENT_DATA or ..DATA2.
*
* HEADERS defined by PFP v1.06 Rev 52.
* Certain fields are common to both ..HEADER and ..HEADER2, and are noted below the structures.
* <p>
* typedef struct tdDEVICE_SECURITY_EVENT_DATA_HEADER {
* UINT8 Signature[16];
* UINT16 Version;
* UINT16 Length;
* UINT32 SpdmHashAlg;
* UINT32 DeviceType;
* SPDM_MEASUREMENT_BLOCK SpdmMeasurementBlock;
* UINT64 DevicePathLength;
* UNIT8 DevicePath[DevicePathLength]
* } DEVICE_SECURITY_EVENT_DATA_HEADER;
* <p>
* typedef struct tdDEVICE_SECURITY_EVENT_DATA_HEADER2 { - NOT IMPLEMENTED YET
* UINT8 Signature[16];
* UINT16 Version;
* UINT8 AuthState;
* UINT8 Reserved;
* UINT32 Length;
* UINT32 DeviceType;
* UINT32 SubHeaderType;
* UINT32 SubHeaderLength;
* UINT32 SubHeaderUID;
* UINT64 DevicePathLength;
* UNIT8 DevicePath[DevicePathLength]
* } DEVICE_SECURITY_EVENT_DATA_HEADER2;
* <p>
* Fields common to both ..HEADER and ..HEADER2:
* Signature
* Version
* DeviceType
* DevicePathLength
* DevicePath
* <p>
*/
public abstract class DeviceSecurityEventHeader {
/**
* Contains the size (in bytes) of the header.
*/
@Getter
private Integer dSEDheaderByteSize = 0;
/**
* Signature (text) data.
*/
@Getter
private String signature = "";
/**
* Version determines data structure used (..DATA or ..DATA2),
* which determines whether ..HEADER or ..HEADER2 is used
*/
@Getter
private String version = "";
/**
* Device type.
*/
@Getter
private int deviceType = -1;
/**
* UEFI Device Path Length.
*/
@Getter
private int devicePathLength = 0;
/**
* UEFI Device path.
*/
@Getter
private UefiDevicePath devicePath = null;
/**
* Is the Device Path Valid.
*/
private boolean devicePathValid = false;
/**
* Device Security Event Data Device Type = no device type.
*/
public static final int DEVICE_TYPE_NONE = 0;
/**
* Device Security Event Data Device Type = DEVICE_TYPE_PCI.
*/
public static final int DEVICE_TYPE_PCI = 1;
/**
* Device Security Event Data Device Type = DEVICE_TYPE_USB.
*/
public static final int DEVICE_TYPE_USB = 2;
/**
* DeviceSecurityEventDataHeaderBase Default Constructor.
*/
public DeviceSecurityEventHeader() {
}
/**
* DeviceSecurityEventDataHeaderBase Constructor.
*
* @param dSEDbytes byte array holding the DeviceSecurityEventData.
*/
public DeviceSecurityEventHeader(final byte[] dSEDbytes) {
byte[] signatureBytes = new byte[UefiConstants.SIZE_16];
System.arraycopy(dSEDbytes, 0, signatureBytes, 0, UefiConstants.SIZE_16);
signature = new String(signatureBytes, StandardCharsets.UTF_8)
.substring(0, UefiConstants.SIZE_15);
byte[] versionBytes = new byte[UefiConstants.SIZE_2];
System.arraycopy(dSEDbytes, UefiConstants.OFFSET_16, versionBytes, 0,
UefiConstants.SIZE_2);
version = HexUtils.byteArrayToHexString(versionBytes);
}
/**
* Parse the device type from the Device Security Event Data Header/Header2.
*
* @param dSEDbytes byte array holding the DeviceSecurityEventData/Data2.
* @param startByte starting byte of device type (depends on header fields before it).
*/
public void extractDeviceType(final byte[] dSEDbytes, int startByte) {
// get the device type ID
byte[] deviceTypeBytes = new byte[UefiConstants.SIZE_4];
System.arraycopy(dSEDbytes, startByte, deviceTypeBytes, 0,
UefiConstants.SIZE_4);
deviceType = HexUtils.leReverseInt(deviceTypeBytes);
}
/**
* Parse the device path from the Device Security Event Data Header/Header2.
* Also, determine final length of header (will be used to extract the next data structure).
*
* @param dSEDbytes byte array holding the DeviceSecurityEventData/Data2.
* @param startByte starting byte of device path (depends on header fields before it).
*/
public void extractDevicePathAndFinalSize(final byte[] dSEDbytes, int startByte)
throws UnsupportedEncodingException {
// get the device path length
byte[] devicePathLengthBytes = new byte[UefiConstants.SIZE_8];
System.arraycopy(dSEDbytes, startByte, devicePathLengthBytes, 0,
UefiConstants.SIZE_8);
int devicePathLength = HexUtils.leReverseInt(devicePathLengthBytes);
// get the device path
if (devicePathLength != 0) {
startByte = startByte + UefiConstants.SIZE_8;
byte[] devPathBytes = new byte[devicePathLength];
System.arraycopy(dSEDbytes, startByte, devPathBytes,
0, devicePathLength);
devicePath = new UefiDevicePath(devPathBytes);
devicePathValid = true;
}
// header total size
dSEDheaderByteSize = startByte + devicePathLength;
}
/**
* Returns the device type via a lookup.
* Lookup based upon section 10.2.7.2, Table 19, in the PFP 1.06 v52 spec.
*
* @param deviceTypeInt int to convert to string
* @return name of the device type
*/
public String deviceTypeToString(final int deviceTypeInt) {
String deviceTypeStr;
switch (deviceTypeInt) {
case DEVICE_TYPE_NONE:
deviceTypeStr = "No device type";
break;
case DEVICE_TYPE_PCI:
deviceTypeStr = "PCI";
break;
case DEVICE_TYPE_USB:
deviceTypeStr = "USB";
break;
default:
deviceTypeStr = "Unknown or invalid Device Type";
}
return deviceTypeStr;
}
/**
* Returns a human readable description of the data common to header structures.
*
* @return a description of this structure.
*/
public String toString() {
String dsedHeaderCommonInfo = "";
dsedHeaderCommonInfo += "\n SPDM Device Type = " + deviceTypeToString(deviceType);
if (devicePathValid) {
dsedHeaderCommonInfo += "\n SPDM Device Path =\n";
dsedHeaderCommonInfo += devicePath;
}
else {
dsedHeaderCommonInfo += "\n SPDM Device Path = Unknown or invalid";
}
return dsedHeaderCommonInfo;
}
}

View File

@ -163,4 +163,8 @@ public final class EvConstants {
* EFI Variable Authority Event ID.
*/
public static final int EV_EFI_VARIABLE_AUTHORITY = 0x800000E0;
/**
* EFI SPDM Firmware Blob Event ID.
*/
public static final int EV_EFI_SPDM_FIRMWARE_BLOB = 0x800000E1;
}

View File

@ -0,0 +1,104 @@
package hirs.utils.tpm.eventlog.events;
import hirs.utils.HexUtils;
import hirs.utils.tpm.eventlog.uefi.UefiConstants;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
/**
* Class to process the EV_EFI_SPDM_FIRMWARE_BLOB event. The event field MUST be a
* 1) DEVICE_SECURITY_EVENT_DATA or
* 2) DEVICE_SECURITY_EVENT_DATA2
* DEVICE_SECURITY_EVENT_DATA has 2 structures:
* 1) DEVICE_SECURITY_EVENT_DATA_HEADER
* 2) DEVICE_SECURITY_EVENT_DATA_DEVICE_CONTEXT, which has 2 structures
* a) DEVICE_SECURITY_EVENT_DATA_PCI_CONTEXT
* b) DEVICE_SECURITY_EVENT_DATA_USB_CONTEXT
* DEVICE_SECURITY_EVENT_DATA2 has 3 structures:
* 1) DEVICE_SECURITY_EVENT_DATA_HEADER2
* 2) DEVICE_SECURITY_EVENT_DATA_SUB_HEADER
* 3) DEVICE_SECURITY_EVENT_DATA_DEVICE_CONTEXT, which has 2 structures (see above)
* The first 16 bytes of the event data header MUST be a String based identifier (Signature),
* NUL-terminated, per PFP. The only currently defined Signature is "SPDM Device Sec",
* which implies the data is a DEVICE_SECURITY_EVENT_DATA or ..DATA2.
* The EV_EFI_SPDM_FIRMWARE_BLOB event is used to record an extended digest for the firmware of
* an embedded component or an add-in device that supports SPDM GET_MEASUREMENTS functionality.
* This event records extended digests of SPDM GET_MEASUREMENT responses that correspond to
* firmware, such as immutable ROM, mutable firmware, firmware version, firmware secure version
* number, etc.
*/
public class EvEfiSpdmFirmwareBlob {
/**
* Signature (text) data.
*/
private String signature = "";
/**
* True if the event is a DEVICE_SECURITY_EVENT_DATA or ..DATA2.
*/
private boolean bSpdmDeviceSecurityEventData = false;
/**
* Human readable description of the data within this DEVICE_SECURITY_EVENT_DATA/..DATA2 event.
*/
String spdmInfo = "";
/**
* EvEfiSpdmFirmwareBlob constructor.
*
* @param eventData byte array holding the event to process.
* @throws java.io.UnsupportedEncodingException if input fails to parse.
*/
public EvEfiSpdmFirmwareBlob(final byte[] eventData) throws UnsupportedEncodingException {
byte[] signatureBytes = new byte[UefiConstants.SIZE_15];
System.arraycopy(eventData, 0, signatureBytes, 0, UefiConstants.SIZE_15);
signature = new String(signatureBytes, StandardCharsets.UTF_8);
signature = signature.replaceAll("[^\\P{C}\t\r\n]", ""); // remove null characters
if (signature.contains("SPDM Device Sec")) { // implies Device Security event
bSpdmDeviceSecurityEventData = true;
byte[] versionBytes = new byte[UefiConstants.SIZE_2];
System.arraycopy(eventData, UefiConstants.OFFSET_16, versionBytes, 0,
UefiConstants.SIZE_2);
String version = HexUtils.byteArrayToHexString(versionBytes);
if (version.equals("0100")) {
DeviceSecurityEventData dSED = new DeviceSecurityEventData(eventData);
spdmInfo = dSED.toString();
}
else if (version.equals("0200")) {
DeviceSecurityEventData2 dSED2 = new DeviceSecurityEventData2(eventData);
spdmInfo = dSED2.toString();
}
else {
spdmInfo = " Unknown version of DeviceSecurityEventData structure";
}
}
}
/**
* Determines if this event is a DeviceSecurityEventData.
*
* @return true of the event is a DeviceSecurityEventData.
*/
public boolean isSpdmDeviceSecurityEventData() {
return bSpdmDeviceSecurityEventData;
}
/**
* Returns a description of this event.
*
* @return Human readable description of this event.
*/
public String toString() {
if (bSpdmDeviceSecurityEventData) {
spdmInfo = " Signature = SPDM Device Sec" + spdmInfo;
} else {
spdmInfo = "EV_EFI_SPDM_FIRMWARE_BLOB event named " + signature
+ " encountered but support for processing it has not been added to this application.\n";
}
return spdmInfo;
}
}

View File

@ -0,0 +1,78 @@
package hirs.utils.tpm.eventlog.spdm;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
/**
* Class for defining hash algorithms referenced in the DMTF SPDM specification.
* SPDM 1.3.0, Table 21, MeasurementHashAlgo.
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class SpdmHa {
/**
* Spdm Hash Alg = Raw bit stream
*/
public static final int TPM_ALG_RAW = 1;
/**
* Spdm Hash Alg = TPM_ALG_SHA_256.
*/
public static final int TPM_ALG_SHA_256 = 2;
/**
* Spdm Hash Alg = TPM_ALG_SHA_384.
*/
public static final int TPM_ALG_SHA_384 = 4;
/**
* Spdm Hash Alg = TPM_ALG_SHA_512.
*/
public static final int TPM_ALG_SHA_512 = 8;
/**
* Spdm Hash Alg = TPM_ALG_SHA3_256.
*/
public static final int TPM_ALG_SHA3_256 = 16;
/**
* Spdm Hash Alg = TPM_ALG_SHA3_384.
*/
public static final int TPM_ALG_SHA3_384 = 32;
/**
* Spdm Hash Alg = TPM_ALG_SHA3_512.
*/
public static final int TPM_ALG_SHA3_512 = 64;
/**
* Returns the hash name via a lookup.
* Lookup based upon section 10.4 for the SPDM v1.03 document.
*
* @param algId int to convert to string
* @return name of the algorithm
*/
public static String tcgAlgIdToString(final int algId) {
String alg;
switch (algId) {
case TPM_ALG_RAW:
alg = "Raw Bit Stream";
break;
case TPM_ALG_SHA_256:
alg = "TPM_ALG_SHA_256";
break;
case TPM_ALG_SHA_384:
alg = "TPM_ALG_SHA_384";
break;
case TPM_ALG_SHA_512:
alg = "TPM_ALG_SHA_512";
break;
case TPM_ALG_SHA3_256:
alg = "TPM_ALG_SHA3_256";
break;
case TPM_ALG_SHA3_384:
alg = "TPM_ALG_SHA3_384";
break;
case TPM_ALG_SHA3_512:
alg = "TPM_ALG_SHA3_512";
break;
default:
alg = "Unknown or invalid Hash";
}
return alg;
}
}

View File

@ -0,0 +1,122 @@
package hirs.utils.tpm.eventlog.spdm;
import hirs.utils.HexUtils;
import lombok.AccessLevel;
import lombok.Getter;
/**
* Class to process the SpdmMeasurement.
* <p>
* Measurement, defined by SPDM v1.03, Sect 10.11.1, Table 54:
* DMTF measurement spec format {
* DMTFSpecMeasurementValueType 1 byte;
* DMTFSpecMeasurementValueSize 2 bytes;
* DMTFSpecMeasurementValue <DMTFSpecMeasurementValueSize> bytes;
* }
* <p>
* DMTFSpecMeasurementValueType[7]
* Indicates how bits [0:6] are represented
* Bit = 0: Digest
* Bit = 1: Raw bit stream
* DMTFSpecMeasurementValueType[6:0] (see SPDM Spec, Table 55 "DMTFSpecMeasurementValueType[6:0]")
* Immutable ROM 0x0
* Mutable firmware 0x1
* Hardware configuration 0x2
* Firmware configuration 0x3
* etc.
* <p>
*/
public class SpdmMeasurement {
/**
* Measurement value type (such as mutable firmware, etc).
*/
@Getter
private int dmtfSpecMeasurementValueType = 0;
/**
* Measurement value (digest).
*/
private byte[] dmtfSpecMeasurementValue = null;
/**
* SpdmMeasurement Constructor.
*
* @param spdmMeasBytes byte array holding the SPDM Measurement bytes.
*/
public SpdmMeasurement(final byte[] spdmMeasBytes) {
byte[] dmtfSpecMeasurementValueTypeBytes = new byte[1];
System.arraycopy(spdmMeasBytes, 0, dmtfSpecMeasurementValueTypeBytes, 0,
1);
dmtfSpecMeasurementValueType = HexUtils.leReverseInt(dmtfSpecMeasurementValueTypeBytes);
// in future, can crosscheck this value size + 3 with the spdm block MeasurementSize size
byte[] dmtfSpecMeasurementValueSizeBytes = new byte[2];
System.arraycopy(spdmMeasBytes, 1, dmtfSpecMeasurementValueSizeBytes, 0,
2);
int dmtfSpecMeasurementValueSize = HexUtils.leReverseInt(dmtfSpecMeasurementValueSizeBytes);
dmtfSpecMeasurementValue = new byte[dmtfSpecMeasurementValueSize];
System.arraycopy(spdmMeasBytes, 3, dmtfSpecMeasurementValue, 0,
dmtfSpecMeasurementValueSize);
}
/**
* Returns a human readable description of the data within this structure.
*
* @return a description of this structure..
*/
public String dmtfSpecMeasurementValueTypeToString(final int measValType) {
String measValTypeStr;
switch (measValType) {
case 0:
measValTypeStr = "Immutable ROM";
break;
case 1:
measValTypeStr = "Mutable firmware";
break;
case 2:
measValTypeStr = "Hardware configuration";
break;
case 3:
measValTypeStr = "Firmware configuration";
break;
case 4:
measValTypeStr = "Freeform measurement manifest";
break;
case 5:
measValTypeStr = "Structured representation of debug and device mode";
break;
case 6:
measValTypeStr = "Mutable firmware's version number";
break;
case 7:
measValTypeStr = "Mutable firmware's security verison number";
break;
case 8:
measValTypeStr = "Hash-extended measurement";
break;
case 9:
measValTypeStr = "Informational";
break;
case 10:
measValTypeStr = "Structured measurement manifest";
break;
default:
measValTypeStr = "Unknown or invalid DMTF Spec Measurement Value Type";
}
return measValTypeStr;
}
public String toString() {
String spdmMeasInfo = "";
spdmMeasInfo += "\n SPDM Measurement Value Type = " +
dmtfSpecMeasurementValueTypeToString(dmtfSpecMeasurementValueType);
spdmMeasInfo += "\n SPDM Measurement Value = " +
HexUtils.byteArrayToHexString(dmtfSpecMeasurementValue);
return spdmMeasInfo;
}
}

View File

@ -0,0 +1,86 @@
package hirs.utils.tpm.eventlog.spdm;
import hirs.utils.HexUtils;
import hirs.utils.tpm.eventlog.uefi.UefiConstants;
import lombok.Getter;
/**
* Class to process the SpdmMeasurementBlock.
* <p>
* Measurement block format, defined by SPDM v1.03, Sect 10.11.1, Table 53:
* Measurement block format {
* Index 1 byte;
* MeasurementSpec 1 byte;
* MeasurementSize 2 bytes;
* Measurement <MeasurementSize> bytes;
* }
* <p>
* Index: index of the measurement block, as there can be more than one
* MeasurementSpec: bit mask; the measurement specification that the requested Measurement follows
* See "MeasurementSpecificationSel" in Table 21. See Tables 29, 53, 54
* Bit 0: DMTFmeasSpec, per Table 54
* Bit 1-7: Reserved
* Measurement: the digest
*/
public class SpdmMeasurementBlock {
/**
* Measurement block index, as an SPDM measurement exchange can contain several measurements.
*/
@Getter
private int index = 0;
/**
* Measurement Spec.
*/
@Getter
private int measurementSpec = 0;
/**
* SPDM Measurement.
*/
private SpdmMeasurement spdmMeasurement;
/**
* SpdmMeasurementBlock Constructor.
*
* @param spdmMeasBlockBytes byte array holding the SPDM Measurement Block bytes.
*/
public SpdmMeasurementBlock(final byte[] spdmMeasBlockBytes) {
byte[] indexBytes = new byte[1];
System.arraycopy(spdmMeasBlockBytes, 0, indexBytes, 0,
1);
index = HexUtils.leReverseInt(indexBytes);
byte[] measurementSpecBytes = new byte[1];
System.arraycopy(spdmMeasBlockBytes, 1, measurementSpecBytes, 0,
1);
measurementSpec = HexUtils.leReverseInt(measurementSpecBytes);
// in future, can crosscheck this measurement size with the MeasurementSpec hash alg size
byte[] measurementSizeBytes = new byte[2];
System.arraycopy(spdmMeasBlockBytes, 2, measurementSizeBytes, 0,
2);
int measurementSize = HexUtils.leReverseInt(measurementSizeBytes);
byte[] measurementBytes = new byte[measurementSize];
System.arraycopy(spdmMeasBlockBytes, 4, measurementBytes, 0,
measurementSize);
spdmMeasurement = new SpdmMeasurement(measurementBytes);
}
/**
* Returns a human readable description of the data within this structure.
*
* @return a description of this structure..
*/
public String toString() {
String spdmMeasBlockInfo = "";
spdmMeasBlockInfo += "\n Index = " + index;
spdmMeasBlockInfo += "\n MeasurementSpec = " + measurementSpec;
spdmMeasBlockInfo += spdmMeasurement.toString();
return spdmMeasBlockInfo;
}
}