diff --git a/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiBootOrder.java b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiBootOrder.java new file mode 100644 index 00000000..27f04858 --- /dev/null +++ b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiBootOrder.java @@ -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 UINT16’s 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(); + } +} diff --git a/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiBootVariable.java b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiBootVariable.java new file mode 100644 index 00000000..9f7b1092 --- /dev/null +++ b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiBootVariable.java @@ -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; + } +} diff --git a/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiConstants.java b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiConstants.java new file mode 100644 index 00000000..33906100 --- /dev/null +++ b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiConstants.java @@ -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; +} diff --git a/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiDevicePath.java b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiDevicePath.java new file mode 100644 index 00000000..1a13e0aa --- /dev/null +++ b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiDevicePath.java @@ -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; + } +} diff --git a/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiFirmware.java b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiFirmware.java index 3cbeec9e..e286a0d7 100644 --- a/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiFirmware.java +++ b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiFirmware.java @@ -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"; } diff --git a/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiGuid.java b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiGuid.java index 2d088d1d..43e4b318 100644 --- a/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiGuid.java +++ b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiGuid.java @@ -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 diff --git a/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UEFIPartition.java b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiPartition.java similarity index 78% rename from HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UEFIPartition.java rename to HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiPartition.java index 06dfbddc..509f1acf 100644 --- a/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UEFIPartition.java +++ b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiPartition.java @@ -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 diff --git a/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiSecureBoot.java b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiSecureBoot.java new file mode 100644 index 00000000..017968e6 --- /dev/null +++ b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiSecureBoot.java @@ -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; + } +} diff --git a/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiSignatureData.java b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiSignatureData.java new file mode 100644 index 00000000..4db7a1f5 --- /dev/null +++ b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiSignatureData.java @@ -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; + } +} diff --git a/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiSignatureList.java b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiSignatureList.java new file mode 100644 index 00000000..d2686461 --- /dev/null +++ b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiSignatureList.java @@ -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 sigList = new ArrayList(); + /** 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 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(); + } +} diff --git a/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiVariable.java b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiVariable.java new file mode 100644 index 00000000..b354e149 --- /dev/null +++ b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiVariable.java @@ -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 certSuperList = new ArrayList(); + /** 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 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); + } +} diff --git a/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiX509Cert.java b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiX509Cert.java new file mode 100644 index 00000000..61e556fe --- /dev/null +++ b/HIRS_Utils/src/main/java/hirs/tpm/eventlog/uefi/UefiX509Cert.java @@ -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; + } +}