added uefi variable processing

This commit is contained in:
lareine 2020-03-24 13:12:44 -04:00
parent b5867e0cf6
commit 828e6d4e65
12 changed files with 1433 additions and 54 deletions

View File

@ -0,0 +1,37 @@
package hirs.tpm.eventlog.uefi;
/**
* Class to process a UEFI BootOrder variable.
* UEFI spec version 2.8 section 3.3 on page 83 defines the Boot Order as:
* an array of UINT16s that make up an ordered list of the Boot#### options.
*/
public class UefiBootOrder {
/** list of UINT16 Boot#### numbers.*/
private char[] bootOrder = null;
/**
* Process the BootOrder UEFI variable.
* @param order byte array holding the UEFI boot order variable.
*/
UefiBootOrder(final byte[] order) {
bootOrder = new char[order.length / UefiConstants.SIZE_2];
for (int i = 0; i < order.length; i = i + UefiConstants.SIZE_2) {
bootOrder[i / UefiConstants.SIZE_2] =
(char) (order[i + 1] * UefiConstants.SIZE_256 + order[i]);
}
}
/**
* Provides a human readable Boot Order list on single line.
* @return A human readable Boot Order
*/
public String toString() {
StringBuffer orderList = new StringBuffer();
orderList.append("BootOrder = ");
for (int i = 0; i < bootOrder.length; i++) {
int order = bootOrder[i];
orderList.append(" Boot" + String.format("%04d", order));
}
return orderList.toString();
}
}

View File

@ -0,0 +1,99 @@
package hirs.tpm.eventlog.uefi;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import hirs.utils.HexUtils;
/**
* Class to process a UEFI Boot#### variable.
* Data is defined using the EFI_LOAD_OptionStructure:
* typedef struct _EFI_LOAD_OPTION {
* UINT32 Attributes;
* UINT16 FilePathListLength;
* // CHAR16 Description[];
* // EFI_DEVICE_PATH_PROTOCOL FilePathList[];
* // UINT8 OptionalData[];
* } EFI_LOAD_OPTION;
*
* No length field for the Description is given
* so we need to calculate it by search for a null termination on the Description field
* Data following the Description should be an EFI Device Path
*/
public class UefiBootVariable {
/** Human readable description of the variable. */
private String description = "";
/** Variable attributes. */
private byte[] attributes = null;
/** Firmware memory blob.*/
private byte[] blob = null;
/** UEFI Device Path.*/
private UefiDevicePath efiDevPath = null;
/**
* UefiBootVariable Constructor.
* @param bootVar byte array holding the boot variable.
* @throws UnsupportedEncodingException if the data fails to parse.
*/
public UefiBootVariable(final byte[] bootVar) throws UnsupportedEncodingException {
attributes = new byte[UefiConstants.SIZE_4];
System.arraycopy(bootVar, 0, attributes, 0, UefiConstants.SIZE_4);
byte[] blobLen = new byte[UefiConstants.SIZE_2];
System.arraycopy(bootVar, UefiConstants.OFFSET_4, blobLen, 0, UefiConstants.SIZE_2);
int blobLength = HexUtils.leReverseInt(blobLen);
if (blobLength % UefiConstants.SIZE_2 == 0) {
blob = new byte[blobLength];
} else {
blob = new byte[blobLength + 1];
}
System.arraycopy(bootVar, UefiConstants.OFFSET_6, blob, 0, blobLength);
int descLength = getChar16ArrayLength(blob);
byte[] desc = new byte[descLength * UefiConstants.SIZE_2];
System.arraycopy(bootVar, UefiConstants.OFFSET_6, desc, 0, descLength * UefiConstants.SIZE_2);
description = new String(UefiDevicePath.convertChar16tobyteArray(desc), "UTF-8");
// Data following the Description should be EFI Partition Data (EFI_DEVICE_PATH_PROTOCOL)
int devPathLength = blobLength;
int devPathOffset = UefiConstants.OFFSET_6 + descLength; //attributes+bloblength+desc+length+2
byte[] devPath = new byte[devPathLength];
System.arraycopy(bootVar, devPathOffset, devPath, 0, devPathLength);
efiDevPath = new UefiDevicePath(devPath);
}
/**
* Returns a string that represents a UEFI boot variable.
* Some devices have not properly terminated the Description filed with null characters
* so garbage bytes are appended to the string that we must strip off.
* All non-alpha numeric is stripped from the string.
* @return string that represents a UEFI boot variable.
*/
public String toString() {
String bootInfo = "";
String bootvar = description.replaceAll("[^a-zA-Z_0-0\\s]", ""); // remove all non ascii chars
bootInfo += "Description = " + bootvar + "\n";
bootInfo += efiDevPath.toString();
return bootInfo;
}
/**
* Searches for the first char16 based null character (2 bytes of zeros).
* Searches in a given byte array and returns the length of data up to that point in bytes.
* @param data a byte array to search for the data.
* @return the length of the data in bytes at the beginning of the byte array.
* which was terminated by a null character.
*/
public int getChar16ArrayLength(final byte[] data) {
int count = 0;
byte[] nullTerminitor = new byte[UefiConstants.SIZE_2];
byte[] char16 = new byte[UefiConstants.SIZE_2];
nullTerminitor[0] = 0;
nullTerminitor[1] = 0;
for (int i = 0; i < data.length; i = i + UefiConstants.SIZE_2) {
char16[0] = data[i];
char16[1] = data[i + 1];
count++;
if (Arrays.equals(nullTerminitor, char16)) {
return count * UefiConstants.SIZE_2;
}
}
return count * UefiConstants.SIZE_2 + 1;
}
}

View File

@ -0,0 +1,122 @@
package hirs.tpm.eventlog.uefi;
/**
* This class contains the String constants that are referenced by UEFI.
* It is expected that member properties of this class will expand as
* more functionality is added.
*/
public final class UefiConstants {
/**
* Constructor.
*/
private UefiConstants() {
}
/** 2 byte size. */
public static final int SIZE_2 = 2;
/** 4 byte size. */
public static final int SIZE_4 = 4;
/** 5 byte size. */
public static final int SIZE_5 = 5;
/** 8 byte size. */
public static final int SIZE_8 = 8;
/** 16 byte size. */
public static final int SIZE_16 = 16;
/** 20 byte size. */
public static final int SIZE_20 = 20;
/** 28 byte size. */
public static final int SIZE_28 = 28;
/** 32 byte size. */
public static final int SIZE_32 = 32;
/** 40 byte size. */
public static final int SIZE_40 = 40;
/** 256 byte size. */
public static final int SIZE_256 = 256;
/** 1 byte offset. */
public static final int OFFSET_1 = 1;
/** 2 byte offset. */
public static final int OFFSET_2 = 2;
/** 3 byte offset. */
public static final int OFFSET_3 = 3;
/** 4 byte offset. */
public static final int OFFSET_4 = 4;
/** 6 byte offset. */
public static final int OFFSET_6 = 4;
/** 8 byte offset. */
public static final int OFFSET_8 = 8;
/** 16 byte offset. */
public static final int OFFSET_16 = 16;
/** 20 byte offset. */
public static final int OFFSET_20 = 20;
/** 24 byte offset. */
public static final int OFFSET_24 = 24;
/** 28 byte offset. */
public static final int OFFSET_28 = 28;
/** 28 byte offset. */
public static final int OFFSET_32 = 32;
/** 40 byte offset. */
public static final int OFFSET_40 = 40;
/** 41 byte offset. */
public static final int OFFSET_41 = 41;
/** Device path terminator. */
public static final int TERMINATOR = 0x7f;
/** Device path end flag. */
public static final int END_FLAG = 0xff;
/** Device Type Hardware. */
public static final int DEV_HW = 0x01;
/** Device Type ACPI. */
public static final int DEV_ACPI = 0x02;
/** Device Type Messaging. */
public static final int DEV_MSG = 0x03;
/** Device Type Media. */
public static final int DEV_MEDIA = 0x04;
/** Device Type Hardware. */
public static final int DEV_BIOS = 0x05;
/** Device Sub-Type Sata. */
public static final int DEV_SUB_SATA = 0x12;
/** Device Sub-Type nvm. */
public static final int DEV_SUB_NVM = 0x17;
/** BIOS Device Path reserved. */
public static final int DEVPATH_BIOS_RESERVED = 0x0;
/** BIOS Device Path for Floppy disks. */
public static final int DEVPATH_BIOS_FLOPPY = 0x01;
/** BIOS Device Path Hard drives. */
public static final int DEVPATH_BIOS_HD = 0x02;
/** BIOS Device Path for CD Drives. */
public static final int DEVPATH_BIOS_CD = 0x03;
/** BIOS Device Path for PCM CIA drives. */
public static final int DEVPATH_BIOS_PCM = 0x04;
/** BIOS Device Path for USB Drives. */
public static final int DEVPATH_BIOS_USB = 0x05;
/** BIOS Device Path for embedded network. */
public static final int DEVPATH_BIOS_EN = 0x06;
/** BIOS Device Path for a Bootstrap Entry Vector (BEV) from an option ROM. */
public static final int DEVPATH_BIOS_BEV = 0x80;
/** Hardware Device Path. */
public static final int DEVPATH_HARWARE = 0x1;
/** 2 byte size. */
public static final int DEVPATH_VENDOR = 0x03;
/** 2 byte size. */
public static final int DEVPATH_FILE = 0x04;
/** PIWG File device path type. */
public static final int DEVPATH_PWIG_FILE = 0x06;
/** PIWG Volume device path type. */
public static final int DEVPATH_PWIG_VOL = 0x07;
/** PC-AT compatible legacy MBR. */
public static final int DRIVE_TYPE_PC_AT = 0x01;
/** GUID Partition Table type. */
public static final int DRIVE_TYPE_GPT = 0x02;
/** Drive Signature type. */
public static final int DRIVE_SIG_NONE = 0x00;
/** Drive Signature type. */
public static final int DRIVE_SIG_32BIT = 0x01;
/** Drive Signature type. */
public static final int DRIVE_SIG_GUID = 0x02;
/** standard byte length. */
public static final int BYTE_LENGTH = 8;
/** standard byte length. */
public static final int ATTRIBUTE_LENGTH = 48;
/** standard byte length. */
public static final int PART_NAME_LENGTH = 56;
/** standard UEFI partition table lengh. */
public static final int UEFI_PT_LENGTH = 72;
}

View File

@ -0,0 +1,430 @@
package hirs.tpm.eventlog.uefi;
import java.io.UnsupportedEncodingException;
import hirs.utils.HexUtils;
/**
* Class to process EFI_DEVICE_PATH_PROTOCOL which is referred to as the UEFI_DEVICE_PATH
*
* #define EFI_DEVICE_PATH_PROTOCOL_GUID \09576e91-6d3f-11d2-8e39-00a0c969723b
* typedef struct _EFI_DEVICE_PATH_PROTOCOL {
* UINT8 Type;
* UINT8 SubType;
* UINT8 Length[2];
* } EFI_DEVICE_PATH_PROTOCOL;
*
* Where Type is defined in the UEFI spec section 10:
* Type 0x01 Hardware Device Path
* Type 0x02 ACPI Device Path
* Type 0x03 Messaging Device Path
* Type 0x04 Media Device Path
* Type 0x05 BIOS Boot Specification Device Path
* Type 0x7F End of Hardware Device Path
* Each Type has a sub-type that may or may no be defined in the section
*
* Only a few of the SubTypes have been implemented as there are many,
* but only those that were reported using the test devices at hand.
* Without test patterns, the processing may lead to an un-handled exception
*/
public class UefiDevicePath {
/** UEFI Device path type. */
private String type = "";
/** UEFI Device path sub-type. */
private String subType = "";
/** UEFI Device path human readable description. */
private String devPathInfo = "";
/** UEFI Device path length. */
private int length = 0;
/**
* UEFI Device path constructor.
* @param path byte array holding device path data
* @throws UnsupportedEncodingException if path byte array contains unexpected values
*/
public UefiDevicePath(final byte[] path) throws UnsupportedEncodingException {
devPathInfo = processDevPath(path);
byte[] lengthBytes = new byte[UefiConstants.SIZE_2];
System.arraycopy(path, UefiConstants.OFFSET_2, lengthBytes, 0, UefiConstants.OFFSET_2);
length = HexUtils.leReverseInt(lengthBytes);
}
/**
* Returns the UEFI device type.
* @return uefi type
*/
public String getType() {
return type;
}
/**
* Returns the UEFI device sub-type.
* @return uefi sub-type
*/
public String getSubType() {
return subType;
}
/**
* Returns the UEFI device structure length.
* @return uefi device structure length
*/
public int getLegth() {
return length;
}
/**
* Processes the UEFI device path.
* UEFI device path is a collection of EFI_DEVICE_PATH_PROTOCOL structures of variable length.
* length must be calculated for each device path and used as an offset.
* devPath is terminated by 07f and 0xff per the UEFi spec.
* @param path byte array holding the Device path
* @return Human readable string containing the device path description.
* @throws UnsupportedEncodingException
*/
private String processDevPath(final byte[] path) throws UnsupportedEncodingException {
StringBuffer pInfo = new StringBuffer();
String devicePathInfo = "";
int devLength = 0, pathOffset = 0;
boolean moreDev = true;
while (moreDev) {
Byte devPath = Byte.valueOf(path[pathOffset]);
if ((devPath.intValue() == UefiConstants.TERMINATOR)
|| (devPath.intValue() == UefiConstants.END_FLAG)) {
moreDev = false;
break;
}
devicePathInfo = processDev(path, pathOffset);
if (devicePathInfo.contains("Unknown Device Path")) {
moreDev = false;
}
pInfo.append(devicePathInfo);
devLength = path[pathOffset + UefiConstants.OFFSET_3] * UefiConstants.SIZE_256
+ path[pathOffset + UefiConstants.OFFSET_2];
pathOffset = pathOffset + devLength;
if (pathOffset >= path.length) {
moreDev = false;
}
}
return pInfo.toString();
}
/**
* Processes a specific UEFI device path, only limited set of types and subtypes are supported.
* Current types processed include Hardware Device Path, ACPI Device Path,
* Messaging Device Path, and Media Device Path.
* @param path
* @param offset
* @return human readable string representing the UEFI device path
* @throws UnsupportedEncodingException
*/
private String processDev(final byte[] path, final int offset)
throws UnsupportedEncodingException {
String devInfo = " ";
int devPath = path[offset];
switch (path[0 + offset]) {
case UefiConstants.DEV_HW: devInfo += "Hardware Device Path: ";
if (devPath == UefiConstants.DEVPATH_HARWARE) {
devInfo += pciSubType(path, offset);
}
break;
case UefiConstants.DEV_ACPI: devInfo += "ACPI Device Path: ";
devInfo += acpiSubType(path, offset);
break;
case UefiConstants.DEV_MSG: devInfo += "Messaging Device Path:";
if (path[offset + UefiConstants.OFFSET_1] == UefiConstants.DEV_SUB_SATA) {
devInfo += sataSubType(path, offset);
}
if (path[offset + UefiConstants.OFFSET_1] == UefiConstants.DEV_SUB_NVM) {
devInfo += nvmSubType(path, offset);
}
break;
case UefiConstants.DEV_MEDIA: devInfo += "Media Device Path: ";
if (path[offset + UefiConstants.OFFSET_1] == 0x01) {
devInfo += hardDriveSubType(path, offset);
} else if (path[offset + UefiConstants.OFFSET_1] == UefiConstants.DEVPATH_VENDOR) {
devInfo += vendorSubType(path, offset);
} else if (path[offset + UefiConstants.OFFSET_1] == UefiConstants.DEVPATH_FILE) {
devInfo += filePathSubType(path, offset);
} else if (path[offset + UefiConstants.OFFSET_1] == UefiConstants.DEVPATH_PWIG_FILE) {
devInfo += piwgFirmVolFile(path, offset);
} else if (path[offset + UefiConstants.OFFSET_1] == UefiConstants.DEVPATH_PWIG_VOL) {
devInfo += piwgFirmVolPath(path, offset);
}
break;
case UefiConstants.DEV_BIOS: devInfo += "BIOS Device Path: ";
devInfo += biosDevicePath(path, offset);
break;
case UefiConstants.TERMINATOR: devInfo += "End of Hardware Device Path: ";
break;
default: devInfo = " Unknown Device Path";
}
devInfo += "\n";
return devInfo;
}
/**
* processes the ACPI UEFI device subtype.
* @param path
* @param offset
* @return acpi device info
*/
private String acpiSubType(final byte[] path, final int offset) {
String tmpType = "";
switch (path[offset + UefiConstants.OFFSET_1]) {
case 0x01: tmpType = "(Short): ";
tmpType += acpiShortSubType(path, offset);
break;
case 0x02: tmpType = "Expanded ACPI Device Path"; break;
default: tmpType = "Invalid ACPI Device Path sub type";
}
return tmpType;
}
/**
* Processes the ACPI short subtype.
* @param path
* @param offset
* @return short acpi info.
*/
private String acpiShortSubType(final byte[] path, final int offset) {
String tmpType = "";
byte[] hid = new byte[UefiConstants.SIZE_4];
System.arraycopy(path, UefiConstants.OFFSET_4 + offset, hid, 0, UefiConstants.SIZE_4);
tmpType += "_HID = " + HexUtils.byteArrayToHexString(hid);
System.arraycopy(path, 2 * UefiConstants.SIZE_4 + offset, hid, 0, UefiConstants.SIZE_4);
tmpType += "_UID = " + HexUtils.byteArrayToHexString(hid);
return tmpType;
}
/**
* Processes the PCI subType.
* @param path
* @param offset
* @return pci device info.
*/
private String pciSubType(final byte[] path, final int offset) {
String tmpType = "PCI: PCI Function Number = ";
tmpType += String.format("0x%x", path[offset + UefiConstants.SIZE_4]);
tmpType += " PCI Device Number = ";
tmpType += String.format("0x%x", path[offset + UefiConstants.SIZE_5]);
return tmpType;
}
/**
* processes the SATA sub type.
* @param path
* @param offset
* @return SATA drive info.
*/
private String sataSubType(final byte[] path, final int offset) {
String tmpType = "SATA: HBA Port Number = ";
byte[] data = new byte[UefiConstants.SIZE_2];
System.arraycopy(path, UefiConstants.OFFSET_4 + offset, data, 0, UefiConstants.SIZE_2);
tmpType += HexUtils.byteArrayToHexString(data);
System.arraycopy(path, UefiConstants.OFFSET_6 + offset, data, 0, UefiConstants.SIZE_2);
tmpType += " Port Multiplier = " + HexUtils.byteArrayToHexString(data);
System.arraycopy(path, UefiConstants.OFFSET_8 + offset, data, 0, UefiConstants.SIZE_2);
tmpType += " Logical Unit Number = " + HexUtils.byteArrayToHexString(data);
return tmpType;
}
/**
* Processes the hard drive sub type.
* @param path
* @param offset
* @return hard drive info.
*/
private String hardDriveSubType(final byte[] path, final int offset) {
String tmpType = " Partition Number = ";
byte[] partnumber = new byte[UefiConstants.SIZE_4];
System.arraycopy(path, UefiConstants.OFFSET_4 + offset, partnumber, 0, UefiConstants.SIZE_4);
tmpType += HexUtils.byteArrayToHexString(partnumber);
byte[] data = new byte[UefiConstants.SIZE_8];
System.arraycopy(path, UefiConstants.OFFSET_8 + offset, data, 0, UefiConstants.SIZE_8);
tmpType += " Partition Start = " + HexUtils.byteArrayToHexString(data);
System.arraycopy(path, UefiConstants.OFFSET_16 + offset, data, 0, UefiConstants.SIZE_8);
tmpType += " Partition Size = " + HexUtils.byteArrayToHexString(data);
byte[] signature = new byte[UefiConstants.SIZE_16];
System.arraycopy(path, UefiConstants.OFFSET_24 + offset, signature, 0, UefiConstants.SIZE_16);
tmpType += " Partition Signature = ";
if (path[UefiConstants.OFFSET_41 + offset] == UefiConstants.DRIVE_SIG_NONE) {
tmpType += "None";
} else if (path[UefiConstants.OFFSET_41 + offset] == UefiConstants.DRIVE_SIG_32BIT) {
tmpType += HexUtils.byteArrayToHexString(signature);
} else if (path[UefiConstants.OFFSET_41 + offset] == UefiConstants.DRIVE_SIG_GUID) {
UefiGuid guid = new UefiGuid(signature);
tmpType += guid.toString();
} else {
tmpType += "invalid partition signature type";
}
tmpType += " Partition Format = ";
if (path[UefiConstants.OFFSET_40 + offset] == UefiConstants.DRIVE_TYPE_PC_AT) {
tmpType += "PC-AT compatible legacy MBR";
} else if (path[UefiConstants.OFFSET_40 + offset] == UefiConstants.DRIVE_TYPE_GPT) {
tmpType += "GUID Partition Table";
} else {
tmpType += "Invalid partition table type";
}
return tmpType;
}
/**
* Process the File path sub type.
* @param path
* @param offset
* @return file path info.
* @throws UnsupportedEncodingException
*/
private String filePathSubType(final byte[] path, final int offset)
throws UnsupportedEncodingException {
String tmpType = " File Path = ";
byte[] lengthBytes = new byte[UefiConstants.SIZE_2];
System.arraycopy(path, 2 + offset, lengthBytes, 0, UefiConstants.SIZE_2);
int subTypeLength = HexUtils.leReverseInt(lengthBytes);
byte[] filePath = new byte[subTypeLength];
System.arraycopy(path, UefiConstants.OFFSET_4 + offset, filePath, 0, subTypeLength);
byte[] fileName = convertChar16tobyteArray(filePath);
tmpType += new String(fileName, "UTF-8");
return tmpType;
}
/**
* Process a vendor sub-type on a Media Type.
* Length of this structure in bytes. Length is 20 + n bytes
* Vendor-assigned GUID that defines the data that follows.
* Vendor-defined variable size data.
* @param path
* @param offset
* @return vendor device info.
*/
private String vendorSubType(final byte[] path, final int offset) {
String tmpType = " Vendor Subtype GUID = ";
byte[] lengthBytes = new byte[UefiConstants.SIZE_2];
System.arraycopy(path, UefiConstants.OFFSET_2 + offset, lengthBytes, 0, UefiConstants.SIZE_2);
int subTypeLength = HexUtils.leReverseInt(lengthBytes);
byte[] guidData = new byte[UefiConstants.SIZE_16];
System.arraycopy(path, UefiConstants.OFFSET_4 + offset, guidData, 0, UefiConstants.SIZE_16);
UefiGuid guid = new UefiGuid(guidData);
tmpType += guid.toString() + " ";
if (subTypeLength - UefiConstants.SIZE_16 > 0) {
byte[] vendorData = new byte[subTypeLength - UefiConstants.SIZE_16];
System.arraycopy(path, UefiConstants.OFFSET_20
+ offset, vendorData, 0, subTypeLength - UefiConstants.SIZE_16);
tmpType += " : Vendor Data = " + HexUtils.byteArrayToHexString(vendorData);
} else {
tmpType += " : No Vendor Data pesent";
}
return tmpType;
}
/**
* Returns nvm device info.
* UEFI Specification, Version 2.8.
* Name space Identifier (NSID) and IEEE Extended Unique Identifier (EUI-64):
* See Links to UEFI-Related Documents
* (http://uefi.org/uefi under the headings NVM Express Specification.
* @param path
* @param offset
* @return NVM device info.
*/
private String nvmSubType(final byte[] path, final int offset) {
String tmpType = " NVM Express Namespace = ";
byte[] lengthBytes = new byte[UefiConstants.SIZE_2];
System.arraycopy(path, UefiConstants.OFFSET_2 + offset, lengthBytes, 0, UefiConstants.SIZE_2);
int subTypeLength = HexUtils.leReverseInt(lengthBytes);
byte[] nvmData = new byte[subTypeLength];
System.arraycopy(path, UefiConstants.OFFSET_4 + offset, nvmData, 0, subTypeLength);
tmpType += HexUtils.byteArrayToHexString(nvmData);
return tmpType;
}
/**
* BIOS Device Type definition.
* From Appendix A of the BIOS Boot Specification.
* Only process the Device type.
* Status bootHandler pointer, and description String pointer are ignored.
* @param path byte array holding the device path.
* @return String that represents the UEFI defined BIOS Device Type.
*/
private String biosDevicePath(final byte[] path, final int offset) {
String devPath = " Legacy BIOS : Type = ";
byte devPathType = path[offset + 1];
Byte pathType = Byte.valueOf(devPathType);
switch (pathType.intValue()) {
case UefiConstants.DEVPATH_BIOS_RESERVED: devPath += "Reserved"; break;
case UefiConstants.DEVPATH_BIOS_FLOPPY: devPath += "Floppy"; break;
case UefiConstants.DEVPATH_BIOS_HD: devPath += "Hard Disk"; break;
case UefiConstants.DEVPATH_BIOS_CD: devPath += "CD-ROM"; break;
case UefiConstants.DEVPATH_BIOS_PCM: devPath += "PCMCIA"; break;
case UefiConstants.DEVPATH_BIOS_USB: devPath += "USB"; break;
case UefiConstants.DEVPATH_BIOS_EN: devPath += "Embedded network"; break;
case UefiConstants.DEVPATH_BIOS_BEV: devPath +=
"Bootstrap Entry Vector (BEV) from an Option ROM";
break;
default: devPath += "Reserved";
break;
}
return devPath;
}
/**
* Returns PIWG firmware volume info.
* UEFI Specification, Version 2.8.
* PIWG Firmware File Section 10.3.5.6:
* Contents are defined in the UEFI PI Specification.
* @param path
* @param offset
* @return String that represents the PIWG Firmware Volume Path
*/
private String piwgFirmVolFile(final byte[] path, final int offset) {
String fWPath = " PIWG Firmware File ";
byte[] guidData = new byte[UefiConstants.SIZE_16];
System.arraycopy(path, UefiConstants.OFFSET_4 + offset, guidData, 0, UefiConstants.SIZE_16);
UefiGuid guid = new UefiGuid(guidData);
fWPath += guid.toString();
return fWPath;
}
/**
* Returns PIWG firmware file info.
* UEFI Specification, Version 2.8.
* PIWG Firmware Volume Section 10.3.5.7:
* Contents are defined in the UEFI PI Specification.
* @param path
* @param offset
* @return String that represents the PIWG Firmware Volume Path
*/
private String piwgFirmVolPath(final byte[] path, final int offset) {
String fWPath = " PIWG Firmware Volume ";
byte[] guidData = new byte[UefiConstants.SIZE_16];
System.arraycopy(path, UefiConstants.OFFSET_4 + offset, guidData, 0, UefiConstants.SIZE_16);
UefiGuid guid = new UefiGuid(guidData);
fWPath += guid.toString();
return fWPath;
}
/**
* Returns a string that represents the UEFi Device path.
* @return UEFi Device path.
*/
public String toString() {
return (devPathInfo);
}
/**
* Converts from a char array to byte array.
* Removes the upper byte (typically set to 0) of each char.
* @param data Character array.
* @return byte array.
*/
public static byte[] convertChar16tobyteArray(final byte[] data) {
byte[] hexdata = new byte[data.length];
int j = 0;
for (int i = 0; i < data.length; i = i + UefiConstants.SIZE_2) {
hexdata[j++] = data[i];
}
return hexdata;
}
}

View File

@ -12,9 +12,6 @@ import hirs.utils.HexUtils;
* } UEFI_PLATFORM_FIRMWARE_BLOB;
*/
public class UefiFirmware {
/** standard uefi address length. */
private static final int ADDRESS_LENGTH = 8;
/** error flag. */
private boolean berror = false;
/** byte array holding the firmwares physical address. */
private byte[] physicalAddress = null;
@ -24,19 +21,19 @@ public class UefiFirmware {
private int blobAddress = 0;
/** uefi address length. */
private int blobLength = 0;
/** standard uefi address length. */
/**
* UefiFirmware constructor.
* @param blob byte array holding a Firmware Blob.
*/
public UefiFirmware(final byte[] blob) {
if (blob.length != 16) {
if (blob.length != UefiConstants.SIZE_16) {
berror = true;
} else {
physicalAddress = new byte[ADDRESS_LENGTH];
System.arraycopy(blob, 0, physicalAddress, 0, ADDRESS_LENGTH);
System.arraycopy(blob, ADDRESS_LENGTH, addressLength, 0, ADDRESS_LENGTH);
physicalAddress = new byte[UefiConstants.SIZE_8];
addressLength = new byte[UefiConstants.SIZE_8];
System.arraycopy(blob, 0, physicalAddress, 0, UefiConstants.SIZE_8);
System.arraycopy(blob, UefiConstants.SIZE_8, addressLength, 0, UefiConstants.SIZE_8);
byte[] lelength = HexUtils.leReverseByte(addressLength);
BigInteger bigIntLength = new BigInteger(lelength);
blobLength = bigIntLength.intValue();
@ -61,13 +58,13 @@ public int getBlobLength() {
}
/**
* Returns a description of the firmware blobs location.
* @returns a descritpion of the the firmware blobs location.
* @return a description of the the firmware blobs location.
*/
public String toString() {
String blobInfo = "";
if(!berror) {
blobInfo += " Platform Firwmare Blob Address = "+blobAddress;
blobInfo += " length = "+ blobLength;
if (!berror) {
blobInfo += " Platform Firwmare Blob Address = " + blobAddress;
blobInfo += " length = " + blobLength;
} else {
blobInfo += " Invalid Firmware Blob event encountered";
}

View File

@ -10,20 +10,12 @@ import hirs.utils.HexUtils;
* GUIDs are essentially UUID as defined by RFC-1422, however Microsoft refers to GUIDS.
*/
public class UefiGuid {
/** standard GUID length. */
private static final int GUID_LENGTH = 16;
/** standard short length. */
private static final int SHORT_LENGTH = 2;
/** standard int length. */
private static final int INT_LENGTH = 4;
/** standard long length. */
private static final int LONG_LENGTH = 8;
/** number of 100ns intervals since UUID Epoch. */
private static final long UUID_EPOCH_INTERVALS = 0x01b21dd213814000L;
/** used for conversion to uuid time. */
private static final int UUID_EPOCH_DIVISOR = 10000;
/** guid byte array. */
private byte[] guid = new byte[GUID_LENGTH ];
private byte[] guid = new byte[UefiConstants.SIZE_16 ];
/** UUID object. */
private UUID uuid;
@ -32,7 +24,7 @@ public class UefiGuid {
* @param guidBytes byte array holding a valid guid.
*/
public UefiGuid(final byte[] guidBytes) {
System.arraycopy(guidBytes, 0, guid, 0, GUID_LENGTH);
System.arraycopy(guidBytes, 0, guid, 0, UefiConstants.SIZE_16);
uuid = processGuid(guidBytes);
}
@ -41,21 +33,21 @@ public class UefiGuid {
* Matched uuids found in /sys/firmware/efi/efivars on Centos 7.
*/
private static UUID processGuid(final byte[] guid) {
byte[] msb1 = new byte[INT_LENGTH];
System.arraycopy(guid, 0, msb1, 0, INT_LENGTH);
byte[] msb1 = new byte[UefiConstants.SIZE_4];
System.arraycopy(guid, 0, msb1, 0, UefiConstants.SIZE_4);
byte[] msb1r = HexUtils.leReverseByte(msb1);
byte[] msb2 = new byte[INT_LENGTH];
System.arraycopy(guid, INT_LENGTH, msb2, 0, INT_LENGTH);
byte[] msb2 = new byte[UefiConstants.SIZE_4];
System.arraycopy(guid, UefiConstants.OFFSET_4, msb2, 0, UefiConstants.SIZE_4);
byte[] msb2r = HexUtils.leReverseByte(msb2);
byte[] msb2rs = new byte[INT_LENGTH];
System.arraycopy(msb2r, 0, msb2rs, SHORT_LENGTH, SHORT_LENGTH);
System.arraycopy(msb2r, SHORT_LENGTH, msb2rs, 0, SHORT_LENGTH);
byte[] msbt = new byte[LONG_LENGTH];
System.arraycopy(msb1r, 0, msbt, 0, INT_LENGTH);
System.arraycopy(msb2rs, 0, msbt, INT_LENGTH, INT_LENGTH);
byte[] msb2rs = new byte[UefiConstants.SIZE_4];
System.arraycopy(msb2r, 0, msb2rs, UefiConstants.OFFSET_2, UefiConstants.SIZE_2);
System.arraycopy(msb2r, UefiConstants.OFFSET_2, msb2rs, 0, UefiConstants.SIZE_2);
byte[] msbt = new byte[UefiConstants.SIZE_8];
System.arraycopy(msb1r, 0, msbt, 0, UefiConstants.SIZE_4);
System.arraycopy(msb2rs, 0, msbt, UefiConstants.OFFSET_4, UefiConstants.SIZE_4);
long msbl = new BigInteger(msbt).longValue();
byte[] lsb = new byte[LONG_LENGTH];
System.arraycopy(guid, LONG_LENGTH, lsb, 0, LONG_LENGTH);
byte[] lsb = new byte[UefiConstants.SIZE_8];
System.arraycopy(guid, UefiConstants.OFFSET_8, lsb, 0, UefiConstants.SIZE_8);
long lsbl = new BigInteger(lsb).longValue();
UUID tmpUuid = new UUID(msbl, lsbl);
return tmpUuid;
@ -65,7 +57,7 @@ private static UUID processGuid(final byte[] guid) {
* @return guid length
*/
public static int getGuidLength() {
return GUID_LENGTH;
return UefiConstants.SIZE_16;
}
/**
* Returns a String that represents a specification name referenced by the EFI_CONFIGURATION_TABLE

View File

@ -13,54 +13,44 @@ import hirs.utils.HexUtils;
* } EFI_PARTITION_ENTRY;
*
* UEFI Table 23. Defined GPT Partition Entry - Partition Type GUIDs (implemented in EFIGui.java)
* Examples:
* Unused Entry 00000000-0000-0000-0000-000000000000
* EFI System Partition C12A7328-F81F-11D2-BA4B-00A0C93EC93B
* Partition containing a legacy MBR 024DEE41-33E7-11D3-9D69-0008C781F39F
*
* A list of type Partition GUIDs from: https://en.wikipedia.org/wiki/GUID_Partition_Table
* Linux filesystem data 0FC63DAF-8483-4772-8E79-3D69D8477DE4
* Logical Volume Manager (LVM) partition E6D6D379-F507-44C2-A23C-238F2A3DF928
* Plain dm-crypt partition 7FFEC5C9-2D00-49B7-8941-3EA10A5586B7
* Root partition (x86-64) 4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709
* RAID partition A19D880F-05FC-4D3B-A006-743F0F84911E
* LUKS partition CA7D7CCB-63ED-4C53-861C-1742536059CC
* Swap partition 0657FD6D-A4AB-43C4-84E5-0933C84B4F4F
* /home partition 933AC7E1-2EB4-4F13-B844-0E14E2AEF915
* /srv (server data) partition 3B8F8425-20E0-4F3B-907F-1A25A76F98E8
*
* linux commands to check uuids:
* blkid list //unique parition guids
* ls /dev/disk/by-partuuid
*/
public class UEFIPartition {
public class UefiPartition {
private UefiGuid partitionTypeGUID = null;
private UefiGuid uniquePartitionGUID = null;
private String partitionName = "";
private String attributes = "";
/** standard byte length. */
private static final int BYTE_LENGTH = 8;
/** standard byte length. */
private static final int ATTRIBUTE_LENGTH = 48;
/** standard byte length. */
private static final int PART_NAME_LENGTH = 56;
/** standard UEFI partition table lengh. */
private static final int UEFI_PT_LENGTH = 72;
/**
* Processes a UEFI defined partition entry.
* @param table byte array holding the partition table.
*/
public UEFIPartition(final byte[] table) {
public UefiPartition(final byte[] table) {
byte[] partitionGUID = new byte[UefiGuid.getGuidLength()];
System.arraycopy(table, 0, partitionGUID, 0, UefiGuid.getGuidLength());
partitionTypeGUID = new UefiGuid(partitionGUID);
byte[] uniquePartGUID = new byte[UefiGuid.getGuidLength()];
System.arraycopy(table, UefiGuid.getGuidLength(), uniquePartGUID, 0, UefiGuid.getGuidLength());
uniquePartitionGUID = new UefiGuid(uniquePartGUID);
byte[] attribute = new byte[BYTE_LENGTH];
System.arraycopy(table, ATTRIBUTE_LENGTH, attribute, 0, BYTE_LENGTH);
byte[] attribute = new byte[UefiConstants.SIZE_8];
System.arraycopy(table, UefiConstants.ATTRIBUTE_LENGTH, attribute, 0, UefiConstants.SIZE_8);
attributes = HexUtils.byteArrayToHexString(attribute);
byte[] partitionname = new byte[UEFI_PT_LENGTH];
System.arraycopy(table, PART_NAME_LENGTH, partitionname, 0, UEFI_PT_LENGTH);
byte[] partitionname = new byte[UefiConstants.UEFI_PT_LENGTH];
System.arraycopy(table, UefiConstants.PART_NAME_LENGTH, partitionname,
0, UefiConstants.UEFI_PT_LENGTH);
byte[] pName = convertChar16tobyteArray(partitionname);
partitionName = HexUtils.byteArrayToHexString(pName);
String[] parts = partitionName.split("[^\\x00-\\x7F]"); // remove any non ASCII

View File

@ -0,0 +1,55 @@
package hirs.tpm.eventlog.uefi;
import java.math.BigInteger;
/**
* Class that processes the UEFI defined SecureBoot Variable.
* Currently this variable only specifies if SecureBoot is on/off.
*/
public class UefiSecureBoot {
/** Variable value. */
private int secureBootVar = 0;
/** Error flag.*/
private boolean berror = false;
/** Human readable description. */
private String info = "";
/**
* Constructor to process the EFI Secure Boot Variable.
* @param data UEFI variable data.
*/
public UefiSecureBoot(final byte[] data) {
if (data.length == 0) {
berror = true;
info = "Unkown State: Empty Secure Boot variable\n";
} else {
secureBootVar = new BigInteger(data).intValue();
}
}
/**
* Return the value of the Secure Boot Variable.
* Current defined values are 1 = On and 0=off.
* @return Integer value of the Secure Boot Variable.
*/
public int getSecurBootVariable() {
return secureBootVar;
}
/**
* Provides a human readable value for the Secure Boot variable.
* @return Human readable description.
*/
public String toString() {
if (!berror) {
if (secureBootVar == 1) {
info += " Secure Boot is enabled \n";
} else if (secureBootVar == 0) {
info += " Secure Boot is NOT enabled \n";
} else {
info += " Unkown State: Secure Variable is undefined \n";
}
}
return info;
}
}

View File

@ -0,0 +1,186 @@
package hirs.tpm.eventlog.uefi;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import hirs.utils.HexUtils;
/**
* Class for processing the contents of a Secure Boot DB or DBX contents.
* used for EFIVariables associated with Secure Boot
* as defined by Section 32.4.1 Signature Database from the UEFI 2.8 specification
*
* typedef struct _EFI_SIGNATURE_DATA {
* EFI_GUID SignatureOwner;
* UINT8 SignatureData[...];
* } EFI_SIGNATURE_DATA;
*
* However page 1729 0f UEFI 2.8 implies that SignatureListType of EFI_CERT_SHA256_GUID
* will contain the "the SHA-256 hash of the binary".
* So the Signature Data depends upon the Signature Type from the EFI Signature List.
*/
public class UefiSignatureData {
/** UEFI Certificate GUID.*/
private byte[] guid = new byte[UefiConstants.SIZE_16];
/** UEFI Signature data.*/
private byte[] sigData = null;
/** UEFI Certificate object .*/
private UefiX509Cert cert = null;
/** UEFI Certificate GUID.*/
private UefiGuid efiGuid = null;
/** UEFI Signature type.*/
private UefiGuid signatureType = null;
/** UEFI Signature validity.*/
private boolean valid = false;
/** UEFI Certificate SHA1 hash.*/
private byte[] binaryHash = new byte[UefiConstants.SIZE_40];
/** UEFI Signature data status.*/
private String status = "Signature Data contians a valid Certificate";
/**
* UefiSignatureData constructor.
* @param inputStream The Signature data.
* @param sigType UEFI defined signature type.
* @throws IOException if there's an problem reading the input stream.
* @throws CertificateException If there a problem parsing the X509 certificate.
* @throws NoSuchAlgorithmException if there's a problem hashing the certificate.
*/
UefiSignatureData(final ByteArrayInputStream inputStream, final UefiGuid sigType)
throws IOException, CertificateException, NoSuchAlgorithmException {
signatureType = sigType;
// UEFI spec section 32.5.3.3 states that SignatureListType of EFI_CERT_SHA256_GUID
// only contains a hash, not a cert
if (sigType.getVendorTableReference().equals("EFI_CERT_SHA256_GUID")) {
inputStream.read(guid);
efiGuid = new UefiGuid(guid);
// Should be a SHA256 hash of the "binary"
inputStream.read(binaryHash);
} else if (sigType.getVendorTableReference().equals("EFI_CERT_X509_GUID")) {
inputStream.read(guid);
efiGuid = new UefiGuid(guid);
// Read in Type and Length separately so we calculate the rest of the cert size
byte[] certType = new byte[UefiConstants.SIZE_2];
inputStream.read(certType);
byte[] certLength = new byte[UefiConstants.SIZE_2];
inputStream.read(certLength);
int cLength = new BigInteger(certLength).intValue() + UefiConstants.SIZE_4;
byte[] certData = new byte[cLength];
inputStream.read(certData);
// put the cert back together
byte[] certBlob = new byte[cLength + UefiConstants.SIZE_4];
System.arraycopy(certType, 0, certBlob, 0, UefiConstants.SIZE_2);
System.arraycopy(certLength, 0, certBlob, UefiConstants.OFFSET_2, UefiConstants.SIZE_2);
System.arraycopy(certData, 0, certBlob, UefiConstants.OFFSET_4, cLength);
cert = new UefiX509Cert(certBlob);
} else if (sigType.isUnknownUUID()) {
//status = "Signature List Type has an unknown GUID: " + efiGuid.toString();
status = "Signature List Type has an unknown GUID";
return;
} else { // else process as a cert (RH SHIM does this)
processC509Cert(inputStream);
efiGuid = sigType;
}
valid = true;
}
/**
* Default EFISignatureData Constructor.
* @param data byte array of the EFISignatureData to process
* @throws CertificateException If there a problem parsing the X509 certificate.
* @throws NoSuchAlgorithmException if there's a problem hashing the certificate.
*/
UefiSignatureData(final byte[] data) throws CertificateException, NoSuchAlgorithmException {
System.arraycopy(data, 0, guid, 0, UefiConstants.SIZE_16);
sigData = new byte[data.length - UefiConstants.SIZE_16];
System.arraycopy(data, UefiConstants.OFFSET_16, sigData, 0, data.length - UefiConstants.SIZE_16);
cert = new UefiX509Cert(sigData);
efiGuid = new UefiGuid(guid);
}
/**
* Processes an x509 Cert used by secure DB or DBx.
* @param inputStream x509 certificate data.
* @throws IOException is there's a problem reading the data.
* @throws CertificateException if there's a problem parsing the certificate.
* @throws NoSuchAlgorithmException if there's a problem creating a hash.
*/
private void processC509Cert(final ByteArrayInputStream inputStream)
throws IOException, CertificateException, NoSuchAlgorithmException {
byte[] certType = new byte[UefiConstants.SIZE_2];
inputStream.read(certType);
byte[] certLength = new byte[UefiConstants.SIZE_2];
inputStream.read(certLength);
int cLength = new BigInteger(certLength).intValue() + UefiConstants.SIZE_4;
byte[] certData = new byte[cLength];
inputStream.read(certData);
// put the cert back together
byte[] certBlob = new byte[cLength + UefiConstants.SIZE_4];
System.arraycopy(certType, 0, certBlob, 0, 2);
System.arraycopy(certLength, 0, certBlob, 2, 2);
System.arraycopy(certData, 0, certBlob, UefiConstants.OFFSET_4, cLength);
cert = new UefiX509Cert(certBlob);
}
/**
* Efi GUID of the signature owner.
* @return EFIGuid object
*/
public UefiGuid getEfiVarGuid() {
return efiGuid;
}
/**
* Returns the signature type.
* @return Guid: either EFI_CERT_SHA256_GUID or EFI_SHA256_GUID
*/
public UefiGuid getSignatureType() {
return signatureType;
}
/**
* Returns a X509 Certificate object which is created from the sign data.
* @return X509Cert Object
*/
public UefiX509Cert getCert() {
return cert;
}
/**
* Checks if EFI Signature/Certificate is valid.
* @return true if EFI Signature is valid
*/
public boolean isValid() {
return valid;
}
/**
* Retrieves the last status of the object.
* @return String with the latest status on this object.
*/
public String getStatus() {
return status;
}
/**
* Provides a description of the fields within the EFI Signature Data.
* @return X509Cert human readable description.
*/
public String toString() {
String sigInfo = "";
if (!valid) {
sigInfo = status;
} else {
if (signatureType.getVendorTableReference().equals("EFI_CERT_SHA256_GUID")) {
sigInfo += "UEFI Signature Owner = " + efiGuid.toString() + "\n";
sigInfo += "Binary Hash = " + HexUtils.byteArrayToHexString(binaryHash) + "\n";
} else {
sigInfo += "UEFI Signature Owner = " + efiGuid.toString() + "\n";
sigInfo += cert.toString();
}
}
return sigInfo;
}
}

View File

@ -0,0 +1,204 @@
package hirs.tpm.eventlog.uefi;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import hirs.utils.HexUtils;
/**
* Class for processing the contents of a Secure Boot DB or DBX contents.
* used for EFIVariables associated with Secure Boot
* as defined by Section 32.4.1 Signature Database from the UEFI 2.8 specification.
*
* An EFI Signature List is actual a list of Certificates used to verify a Signature.
* This is mainly found in PCR[7] UEFI variables for the Secure Boot PK, KEK, Db and DBx variables.
*
* typedef struct _EFI_SIGNATURE_LIST {
* EFI_GUID SignatureType;
* UINT32 SignatureListSize;
* UINT32 SignatureHeaderSize;
* UINT32 SignatureSize;
* // UINT8 SignatureHeader[SignatureHeaderSize];
* // EFI_SIGNATURE_DATA Signatures[...][SignatureSize];
* } EFI_SIGNATURE_LIST;
*
*/
public class UefiSignatureList {
/** Size of the signature list.*/
private int listSize = 0;
/** Size of a signature. */
private int signatureSize = 0;
/** Signature data. */
private byte[]sigData = null;
/** Number of Items in the list. */
private int numberOfItems = 0;
/** Signature validity. */
private boolean valid = true;
/** Current status. */
private String status = "Signature List is Valid";
/** Array List of Signature found in the list. */
private ArrayList<UefiSignatureData> sigList = new ArrayList<UefiSignatureData>();
/** Input Stream for processing. */
private ByteArrayInputStream efiSigDataIS = null;
/** Type of signature. */
private UefiGuid signatureType = null;
/**
* UefiSignatureList constructor.
* @param list byte array holding the signature list.
* @throws CertificateException If there a problem parsing the X509 certificate.
* @throws NoSuchAlgorithmException if there's a problem hashing the certificate.
* @throws IOException If there's a problem parsing the signature data.
*/
UefiSignatureList(final byte[] list)
throws CertificateException, NoSuchAlgorithmException, IOException {
byte[] guid = new byte[UefiConstants.SIZE_16];
System.arraycopy(list, 0, guid, 0, UefiConstants.SIZE_16);
signatureType = new UefiGuid(guid);
byte[] lSize = new byte[UefiConstants.SIZE_4];
System.arraycopy(list, UefiConstants.OFFSET_16, lSize, 0, UefiConstants.SIZE_4);
listSize = HexUtils.leReverseInt(lSize);
byte[] hSize = new byte[UefiConstants.SIZE_4];
System.arraycopy(list, UefiConstants.OFFSET_20, hSize, 0, UefiConstants.SIZE_4);
byte[] sSize = new byte[UefiConstants.SIZE_4];
System.arraycopy(list, UefiConstants.OFFSET_24, sSize, 0, UefiConstants.SIZE_4);
signatureSize = HexUtils.leReverseInt(sSize);
sigData = new byte[signatureSize];
System.arraycopy(list, UefiConstants.OFFSET_28, sigData, 0, signatureSize);
processSignatureList(sigData);
}
/**
* EFI Signature list constructor.
* @param lists ByteArrayInputStream containing an EFI Signature list.
* @throws IOException If there's a problem in reading he input stream.
* @throws CertificateException If there's a problem parsing the X509 certificate.
* @throws NoSuchAlgorithmException if there's a problem hashing the certificate.
*/
UefiSignatureList(final ByteArrayInputStream lists)
throws IOException, CertificateException, NoSuchAlgorithmException {
byte[] guid = new byte[UefiConstants.SIZE_16];
lists.read(guid);
signatureType = new UefiGuid(guid);
if (!isValidSigListGUID(signatureType)) {
processSignatureData(lists);
} else { // valid SigData Processing
byte[] lSize = new byte[UefiConstants.SIZE_4];
lists.read(lSize);
listSize = HexUtils.leReverseInt(lSize);
byte[] hSize = new byte[UefiConstants.SIZE_4];
lists.read(hSize);
byte[] sSize = new byte[UefiConstants.SIZE_4];
lists.read(sSize);
signatureSize = listSize - UefiConstants.SIZE_28;
sigData = new byte[signatureSize];
lists.read(sigData);
processSignatureList(sigData);
}
}
/**
* Method for processing a set of EFI SignatureList(s).
* @param sigData Byte array holding one or more SignatureLists
* @throws CertificateException If there's a problem parsing the X509 certificate.
* @throws NoSuchAlgorithmException if there's a problem hashing the certificate.
* @throws IOException If there's a problem parsing the signature data.
*/
private void processSignatureList(final byte[] efiSigData)
throws CertificateException, NoSuchAlgorithmException, IOException {
efiSigDataIS = new ByteArrayInputStream(efiSigData);
while (efiSigDataIS.available() > 0) {
UefiSignatureData tmpSigData = new UefiSignatureData(efiSigDataIS, signatureType);
if (!tmpSigData.isValid()) {
valid = false;
status = tmpSigData.getStatus();
break;
}
sigList.add(tmpSigData);
numberOfItems++;
}
}
/**
* Method for processing a set of EFI SignatureList(s).
* @param sigData Byte array holding one or more SignatureLists.
* @throws CertificateException If there's a problem parsing the X509 certificate.
* @throws NoSuchAlgorithmException if there's a problem hashing the certificate.
* @throws IOException If there's a problem parsing the signature data.
*/
private void processSignatureData(final ByteArrayInputStream sigDataIS)
throws CertificateException, NoSuchAlgorithmException, IOException {
while (sigDataIS.available() > 0) {
UefiSignatureData tmpigData = new UefiSignatureData(sigDataIS, signatureType);
if (!tmpigData.isValid()) {
valid = false;
status = tmpigData.getStatus();
break;
}
sigList.add(tmpigData);
numberOfItems++;
}
}
/**
* Returns an ArrayList of EFISignatureData objects.
* @return ArrayList of EFISignatureData objects.
*/
public ArrayList<UefiSignatureData> getSigatureDataList() {
return sigList;
}
/**
* Return the number of certificates found within a certificate list.
* @return int Number of certs.
*/
public int getNumberOfCerts() {
return numberOfItems;
}
/**
* Checks to see if GUID is listed on page 1729 of UEFI spec version 2.8.
* @param guid GUID of the has algorithm.
* @return true if the GUID is a valid GUID for Signature List Type, false if not.
*/
public boolean isValidSigListGUID(final UefiGuid guid) {
switch (guid.getVendorTableReference()) {
case "EFI_CERT_SHA256_GUID": return true;
case "EFI_CERT_X509_SHA256": return true;
case "EFI_CERT_X509_SHA384": return true;
case "EFI_CERT_X509_SHA512": return true;
case "EFI_CERT_X509_GUID": return true;
default: return false;
}
}
/**
* Provides a description of the fields within the EFI Signature Data field.
* Which is essentially a list of X509 certificates.
* @return human readable description.
*/
public String toString() {
StringBuffer sigInfo = new StringBuffer();
sigInfo.append("UEFI Signature List Type = " + signatureType.toString() + "\n");
sigInfo.append("Number if items = " + numberOfItems + "\n");
sigList.iterator();
for (int i = 0; i < sigList.size(); i++) {
UefiSignatureData certData = (UefiSignatureData) sigList.get(i);
sigInfo.append(certData.toString());
}
if (!valid) {
sigInfo.append("*** Invalid UEFI Signature data encountered: " + status + "\n");
}
return sigInfo.toString();
}
}

View File

@ -0,0 +1,180 @@
package hirs.tpm.eventlog.uefi;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import hirs.utils.HexUtils;
/**
* Class to process a UEFI variable within a TPM Event.
* typedef struct tdUEFI_VARIABLE_DATA{
* UEFI_GUID VariableName; (16 bytes)
* UINT64 UnicodeNameLength; (8 bytes)
* UINT64 VariableDataLength; (8 bytes)
* CHAR16 UnicodeName[];
* INT8 VariableData[];
* } UEFI_VARIABLE_DATA
*/
public class UefiVariable {
/** UEFI defined variable identifier GUID. */
private UefiGuid uefiGuid = null;
/** List of Signature lists. */
private ArrayList<UefiSignatureList> certSuperList = new ArrayList<UefiSignatureList>();
/** Name of the UEFI variable. */
private String varName = "";
/** UEFI defined Boot Variable. */
private UefiBootVariable bootv = null;
/** UEFI Defined boot order. */
private UefiBootOrder booto = null;
/** UEFI defined secure boot. */
private UefiSecureBoot sb = null;
/** UEFI variable data. */
private byte[] uefiVaribelData = null;
/**
* EFIVariable constructor.
* The UEFI_VARIABLE_DATA contains a "VariableName" field which is used to determine
* the class used to parse the data within the "VariableData".
* @param varibaleData byte array holding the UEFI Variable.
* @throws CertificateException If there a problem parsing the X509 certificate.
* @throws NoSuchAlgorithmException if there's a problem hashing the certificate.
* @throws IOException If there's a problem parsing the signature data.
*/
public UefiVariable(final byte[] varibaleData)
throws CertificateException, NoSuchAlgorithmException, IOException {
byte[] guid = new byte[UefiConstants.SIZE_16];
byte[] nameLength = new byte[UefiConstants.SIZE_8];
byte[] nameTemp = null;
byte[] dataLength = new byte[UefiConstants.SIZE_8];
byte[] name = null;
int variableLength = 0;
uefiGuid = new UefiGuid(guid);
System.arraycopy(varibaleData, 0, guid, 0, UefiConstants.SIZE_16);
System.arraycopy(varibaleData, UefiConstants.SIZE_16, nameLength, 0, UefiConstants.SIZE_8);
int nlength = HexUtils.leReverseInt(nameLength);
System.arraycopy(varibaleData, UefiConstants.OFFSET_24, dataLength, 0, UefiConstants.SIZE_8);
nameTemp = new byte[nlength * UefiConstants.SIZE_2];
System.arraycopy(varibaleData, UefiConstants.OFFSET_32,
nameTemp, 0, nlength * UefiConstants.SIZE_2);
byte[] name1 = UefiDevicePath.convertChar16tobyteArray(nameTemp);
name = new byte[nlength];
System.arraycopy(name1, 0, name, 0, nlength);
variableLength = HexUtils.leReverseInt(dataLength);
uefiVaribelData = new byte[variableLength];
System.arraycopy(varibaleData, UefiConstants.OFFSET_32
+ nlength * UefiConstants.SIZE_2, uefiVaribelData, 0, variableLength);
varName = new String(name, "UTF-8");
String tmpName = varName;
if (varName.contains("Boot00")) {
tmpName = "Boot00";
}
switch (tmpName) {
case "PK": processSigList(uefiVaribelData); break;
case "KEK": processSigList(uefiVaribelData); break;
case "db": processSigList(uefiVaribelData); break;
case "dbx": processSigList(uefiVaribelData); break;
case "Boot00": bootv = new UefiBootVariable(uefiVaribelData); break;
case "BootOrder": booto = new UefiBootOrder(uefiVaribelData); break;
case "SecureBoot": sb = new UefiSecureBoot(uefiVaribelData); break;
default:
}
}
/**
* The GUID is a globally unique identifier assigned to this UEFI variable.
* UEFI variable specific GUIDs are specified in the UEFI specification.
* @return UEFI Variable GUID.
*/
public UefiGuid getEfiVarGuid() {
return uefiGuid;
}
/**
* Returns a human readable Name for this UEFI Variable (e.g. SecureBoot).
* @return the UEFI Variable name assigned to this variable.
*/
public String getEfiVarName() {
return varName;
}
/**
* Returns a arrayList of UefiSignatureList (Certificates or hashes) held in the Event Log.
* @return and array list of UEFI defined Signature Lists.
*/
public ArrayList<UefiSignatureList> getEFISignatureList() {
return certSuperList;
}
/**
* Processes the data as a UEFI defined Signature List.
* @param data the bye array holding the Signature List.
* @throws CertificateException If there a problem parsing the X509 certificate.
* @throws NoSuchAlgorithmException if there's a problem hashing the certificate.
* @throws IOException If there's a problem parsing the signature data.
*/
private void processSigList(final byte[] data)
throws CertificateException, NoSuchAlgorithmException, IOException {
ByteArrayInputStream certData = new ByteArrayInputStream(data);
while (certData.available() > 0) {
UefiSignatureList list;
list = new UefiSignatureList(certData);
certSuperList.add(list);
}
}
/**
* Print out all the interesting characteristics available on this UEFI Variable.
* @return human readable description of the UEFi variable.
*/
public String toString() {
StringBuffer efiVariable = new StringBuffer();
efiVariable.append("UEFI Variable Name:" + varName + "\n");
efiVariable.append("UEFI_GUID = " + getEfiVarGuid().toString() + "\n");
efiVariable.append("UEFI Variable Contents => " + "\n");
String tmpName = varName;
if (varName.contains("Boot00")) {
tmpName = "Boot00";
}
switch (tmpName) {
case "Shim": efiVariable.append(printCert(uefiVaribelData, 0)); break;
case "MokList": efiVariable.append(printCert(uefiVaribelData, 0)); break;
case "Boot00": efiVariable.append(bootv.toString()); break;
case "BootOrder": efiVariable.append(booto.toString()); break;
case "SecureBoot": efiVariable.append(sb.toString()); break;
default:
}
for (int i = 0; i < certSuperList.size(); i++) {
efiVariable.append(certSuperList.get(i).toString());
}
return efiVariable.toString();
}
/**
* Retrieves human readable description from a Certificate.
* @param data byte[] holding the certificate.
* @param offset offset to start of the certificate within the byte array.
* @return human readable description of a certificate.
*/
public String printCert(final byte[] data, final int offset) {
String certInfo = "";
byte[] certLength = new byte[UefiConstants.SIZE_2];
System.arraycopy(data, offset + UefiConstants.OFFSET_2, certLength, 0, UefiConstants.SIZE_2);
int cLength = new BigInteger(certLength).intValue() + UefiConstants.SIZE_4;
byte[] certData = new byte[cLength];
System.arraycopy(data, offset, certData, 0, cLength);
try {
UefiX509Cert cert = new UefiX509Cert(certData);
certInfo = cert.toString();
} catch (Exception e) {
certInfo = "Error Processing Certificate : " + e.getMessage() + "\n";
}
return (certInfo);
}
}

View File

@ -0,0 +1,87 @@
package hirs.tpm.eventlog.uefi;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import javax.xml.bind.DatatypeConverter;
/**
* Class for processing a Secure Boot certificate stored in the UEFI DB or DBX.
* X509 certs are used by Secure Boot for validating EFI files.
*/
public class UefiX509Cert {
/** Certificate object .*/
private java.security.cert.Certificate cert = null;
/**
* Constructor for the certificate.
* @param certData byte array holding the certificate.
* @throws CertificateException If the certificate cannot parse.
* @throws NoSuchAlgorithmException if a hash cannot be generated from the cert.
*/
public UefiX509Cert(final byte[] certData) throws CertificateException, NoSuchAlgorithmException {
CertificateFactory cf;
cf = CertificateFactory.getInstance("X.509");
InputStream targetStream = new ByteArrayInputStream(certData);
cert = cf.generateCertificate(targetStream);
MessageDigest md = MessageDigest.getInstance("SHA1");
md.update(certData);
}
/**
* Finds the byte length of the certificate.
* @return the certificate length.
* @throws CertificateEncodingException if the certificate failed to parse.
*/
public int getLength() throws CertificateEncodingException {
int length = 0;
X509Certificate x509Cert = (X509Certificate) cert;
length = x509Cert.getEncoded().length;
return length;
}
/**
* Calculates the fingerprint per Microsoft's specs using SHA1 and colon based notation.
* e.g. "44:d6:41:ca:ca:08:09:00:23:98:b4:87:7b:8e:98:2e:d2:6f:7b:76"
* @return a string representation of the certificate fingerprint
*/
public String getSHA1FingerPrint() {
byte[] der = null;
MessageDigest md = null;
try {
md = MessageDigest.getInstance("SHA-1");
der = cert.getEncoded();
} catch (Exception e) {
return ("Error creating Certificate Fingerprint: " + e.getMessage());
}
md.update(der);
byte[] digest = md.digest();
String digestHex = DatatypeConverter.printHexBinary(digest);
digestHex = digestHex.replaceAll("..(?!$)", "$0:"); // places : every 2 digits
return digestHex.toLowerCase();
}
/**
* Provides a Sting of select fields of the Certificate data.
* @return A string detailing select fields of the certificate.
*/
public String toString() {
X509Certificate x509Cert = (X509Certificate) cert;
String certData = "";
certData += "Certificate Serial Number = "
+ x509Cert.getSerialNumber().toString(UefiConstants.SIZE_16) + "\n";
certData += "Subject DN = " + x509Cert.getSubjectDN() + "\n";
certData += "Issuer DN = " + x509Cert.getIssuerDN() + "\n";
certData += "Not Before Date = " + x509Cert.getNotBefore() + "\n";
certData += "Not After Date = " + x509Cert.getNotAfter() + "\n";
certData += "Signature Algorithm = " + x509Cert.getSigAlgName() + "\n";
certData += "SHA1 Fingerprint = " + getSHA1FingerPrint() + "\n";
return certData;
}
}