parsing SPDM cert chain

This commit is contained in:
iadgovuser58 2024-08-02 18:35:08 -04:00
parent f2f5c52053
commit e91517c77c
7 changed files with 214 additions and 85 deletions

View File

@ -76,14 +76,14 @@ public class DeviceSecurityEventData2 extends DeviceSecurityEvent {
}
}
else if (subHeaderType == DeviceSecurityEventDataSubHeader.SUBHEADERTYPE_CERT_CHAIN) {
subHeaderInfo += "\n Cert chain to be implemented ";
// subHeaderInfo += "\n Cert chain to be implemented ";
try {
dsedSubHeader =
new DeviceSecurityEventDataSubHeaderCertChain(dsedSubHeaderBytes);
subHeaderInfo += dsedSubHeader.toString();
}
catch(NullPointerException e) {
subHeaderInfo = "\n Could not interpret Sub header info for SPDM measurement block";
subHeaderInfo = "\n Could not interpret Sub header info for SPDM cert chain";
}
}
else {

View File

@ -1,8 +1,8 @@
package hirs.utils.tpm.eventlog.events;
import hirs.utils.HexUtils;
import hirs.utils.tpm.eventlog.spdm.SpdmCertificateChain;
import hirs.utils.tpm.eventlog.spdm.SpdmHa;
import hirs.utils.tpm.eventlog.spdm.SpdmMeasurementBlock;
import lombok.Getter;
import java.io.ByteArrayInputStream;
@ -20,24 +20,33 @@ import java.util.ArrayList;
* SPDM_CERT_CHAIN SpdmCertChain;
* } DEVICE_SECURITY_EVENT_DATA_SUB_HEADER_SPDM_CERT_CHAIN;
* <p>
* SpdmVersion: SpdmBaseHashAlgo
* SpdmSlotId: SlotId associated with this SPDM Certificate Chain
* SpdmBaseHashAlgo: SPDM Base Hash Algorithm for the root certificate in the SPDM Certificate chain
* SpdmCertChain: SPDM Certificate Chain
*/
public class DeviceSecurityEventDataSubHeaderCertChain extends DeviceSecurityEventDataSubHeader{
/**
* SPDM version.
*/
@Getter
private int spdmVersion = 0;
/**
* SPDM slot ID.
*/
@Getter
private int spdmLotId = 0;
private int spdmSlotId = 0;
/**
* SPDM base hash algorithm.
*/
@Getter
private int spdmBaseHashAlgo = -1;
/**
* SPDM cert chain.
*/
private SpdmCertificateChain spdmCertChain = null;
/**
* Human-readable description of any error associated with SPDM base hash alg.
*/
String spdmBaseHashAlgoError = "";
/**
* DeviceSecurityEventDataSubHeaderCertChain Constructor.
@ -52,7 +61,7 @@ public class DeviceSecurityEventDataSubHeaderCertChain extends DeviceSecurityEve
byte[] spdmLotIdBytes = new byte[1];
System.arraycopy(dsedSubHBytes, 2, spdmLotIdBytes, 0, 1);
spdmLotId = HexUtils.leReverseInt(spdmLotIdBytes);
spdmSlotId = HexUtils.leReverseInt(spdmLotIdBytes);
// byte[] reserved[Bytes]: 1 byte
@ -68,13 +77,15 @@ public class DeviceSecurityEventDataSubHeaderCertChain extends DeviceSecurityEve
System.arraycopy(dsedSubHBytes, 8, spdmCertChainBytes, 0,
spdmCertChainSize);
// ByteArrayInputStream spdmMeasurementBlockListData =
// new ByteArrayInputStream(spdmMeasurementBlockListBytes);
// while (spdmMeasurementBlockListData.available() > 0) {
// SpdmMeasurementBlock spdmMeasurementBlock;
// spdmMeasurementBlock = new SpdmMeasurementBlock(spdmMeasurementBlockListData);
// spdmMeasurementBlockList.add(spdmMeasurementBlock);
// }
int spdmBaseHashAlgoSize = SpdmHa.tcgAlgIdToByteSize(spdmBaseHashAlgo);
if(spdmBaseHashAlgoSize > 0) {
spdmCertChain = new SpdmCertificateChain(spdmCertChainBytes, spdmBaseHashAlgoSize);
}
else {
spdmBaseHashAlgoError += "SPDM base hash algorithm size is not >0";
}
}
@ -85,18 +96,13 @@ public class DeviceSecurityEventDataSubHeaderCertChain extends DeviceSecurityEve
*/
public String toString() {
String dsedSubHeaderInfo = "";
// dsedSubHeaderInfo += "\n SPDM Version: " + spdmVersion;
// String spdmHashAlgoStr = SpdmHa.tcgAlgIdToString(spdmMeasurementHashAlgo);
// dsedSubHeaderInfo += "\n SPDM Hash Algorithm = " + spdmHashAlgoStr;
//
// // SPDM Measurement Block List output
// dsedSubHeaderInfo += "\n Number of SPDM Measurement Blocks = " + spdmMeasurementBlockList.size();
// int spdmMeasBlockCnt = 1;
// for (SpdmMeasurementBlock spdmMeasBlock : spdmMeasurementBlockList) {
// dsedSubHeaderInfo += "\n SPDM Measurement Block # " + spdmMeasBlockCnt++ + " of " +
// spdmMeasurementBlockList.size();
// dsedSubHeaderInfo += spdmMeasBlock.toString();
// }
dsedSubHeaderInfo += "\n SPDM Version = " + spdmVersion;
dsedSubHeaderInfo += "\n SPDM Slot ID = " + spdmSlotId;
String spdmBaseHashAlgoStr = SpdmHa.tcgAlgIdToString(spdmBaseHashAlgo);
dsedSubHeaderInfo += "\n SPDM Base Hash Algorithm = " + spdmBaseHashAlgoStr;
// SPDM Certificate Chain output
dsedSubHeaderInfo += spdmCertChain.toString();
return dsedSubHeaderInfo;
}

View File

@ -21,6 +21,14 @@ import java.util.List;
* SPDM_MEASUREMENT_BLOCK SpdmMeasurementBlock[SpdmMeasurementBlockCount];
* } DEVICE_SECURITY_EVENT_DATA_SUB_HEADER_SPDM_MEASUREMENT_BLOCK;
* <p>
*
* SpdmMeasurementBlock is an array of SPDM_MEASUREMENT_BLOCKs
* The size of each block is the same and can be found by either:
* 1) 4 + SpdmMeasurementBlock MeasurementSize
* OR
* 2) 4 + hash length of the hash algorithm found in
* DEVICE_SECURITY_EVENT_DATA_SUB_HEADER_SPDM_MEASUREMENT_BLOCK SpdmMeasurementHashAlgo
* where 4 is the size of the SpdmMeasurementBlock header
*/
public class DeviceSecurityEventDataSubHeaderSpdmMeasurementBlock extends DeviceSecurityEventDataSubHeader {
@ -68,7 +76,7 @@ public class DeviceSecurityEventDataSubHeaderSpdmMeasurementBlock extends Device
System.arraycopy(dsedSubHBytes, 4, spdmMeasurementHashAlgoBytes, 0, 4);
spdmMeasurementHashAlgo = HexUtils.leReverseInt(spdmMeasurementHashAlgoBytes);
// get the size of the SPDM Measurement Block List
// get the total size of the SPDM Measurement Block List
int spdmMeasurementBlockListSize = dsedSubHBytes.length - 8;
// extract the bytes that comprise the SPDM Measurement Block List
@ -92,7 +100,7 @@ public class DeviceSecurityEventDataSubHeaderSpdmMeasurementBlock extends Device
*/
public String toString() {
String dsedSubHeaderInfo = "";
dsedSubHeaderInfo += "\n SPDM Version: " + spdmVersion;
dsedSubHeaderInfo += "\n SPDM Version = " + spdmVersion;
String spdmHashAlgoStr = SpdmHa.tcgAlgIdToString(spdmMeasurementHashAlgo);
dsedSubHeaderInfo += "\n SPDM Hash Algorithm = " + spdmHashAlgoStr;

View File

@ -1,8 +1,10 @@
package hirs.utils.tpm.eventlog.spdm;
import hirs.utils.HexUtils;
import hirs.utils.tpm.eventlog.events.DeviceSecurityEventDataSubHeaderSpdmMeasurementBlock;
import hirs.utils.tpm.eventlog.uefi.UefiConstants;
import hirs.utils.tpm.eventlog.uefi.UefiSignatureData;
import hirs.utils.tpm.eventlog.uefi.UefiSignatureList;
import hirs.utils.tpm.eventlog.uefi.UefiX509Cert;
import lombok.Getter;
@ -31,6 +33,8 @@ import java.util.ArrayList;
* hash algorithm is included in the DEVICE_SECURITY_EVENT_DATA_SUB_HEADER_SPDM_CERT_CHAIN
* structure as the member "SpdmBaseHashAlg"
* RootHash: the digest of the Root Certificate.
* size is determined by hash algorithm selected by the most recent SPDM ALGORITHMS response;
* the hash algorithm is the DEVICE_SECURITY_EVENT_DATA_SUB_HEADER_SPDM_CERT_CHAIN SpdmBaseHashAlgo
* Certificates: Complete cert chain consisting of 1 or more ASN.1 DER-encoded X.509 v3 certs
* this field shall be in Encoded ASN.1 byte order
*/
@ -44,75 +48,99 @@ public class SpdmCertificateChain {
* Root hash.
*/
private byte[] rootHash = null;
/**
* Number of certs in the SPDM cert chain.
*/
@Getter
private int numberOfCerts = 0;
/**
* Array List of certs found in the chain.
*/
// private ArrayList<X509Certificate> certList = new ArrayList<X509Certificate>();
private ArrayList<UefiX509Cert> certList = new ArrayList<UefiX509Cert>();
/**
* Human-readable description of any error associated with SPDM base hash alg.
*/
String spdmBaseHashAlgoError = "";
/**
* Human-readable description of any error associated with parsing the X509 certs.
*/
String certProcessingError = "";
/**
* SpdmCertificateChain Constructor.
*
* @param spdmCertChainBytes byte array holding the SPDM Cert Chain bytes.
*/
public SpdmCertificateChain(final byte[] spdmCertChainBytes, final int rootHashLength) throws CertificateException, NoSuchAlgorithmException, IOException {
public SpdmCertificateChain(final byte[] spdmCertChainBytes, final int rootHashLength) {
byte[] lengthBytes = new byte[2];
System.arraycopy(spdmCertChainBytes, 0, lengthBytes, 0, 2);
length = HexUtils.leReverseInt(lengthBytes);
if(rootHashLength <= 0) {
spdmBaseHashAlgoError = "SPDM base hash algorithm size is not >0";
}
else {
byte[] lengthBytes = new byte[2];
System.arraycopy(spdmCertChainBytes, 0, lengthBytes, 0, 2);
length = HexUtils.leReverseInt(lengthBytes);
// Reserved: 2 bytes
// Reserved: 2 bytes
rootHash = new byte[rootHashLength];
System.arraycopy(spdmCertChainBytes, 4, rootHash, 0, rootHashLength);
rootHash = new byte[rootHashLength];
System.arraycopy(spdmCertChainBytes, 4, rootHash, 0, rootHashLength);
int certChainStartPos = 4 + rootHashLength;
int certChainLength = spdmCertChainBytes.length - certChainStartPos;
byte[] certChainBytes = new byte[certChainLength];
System.arraycopy(spdmCertChainBytes, certChainStartPos, certChainBytes, 0, certChainLength);
int certChainStartPos = 4 + rootHashLength;
int certChainLength = spdmCertChainBytes.length - certChainStartPos;
byte[] certChainBytes = new byte[certChainLength];
System.arraycopy(spdmCertChainBytes, certChainStartPos, certChainBytes, 0, certChainLength);
processCertChain(certChainBytes);
processCertChain(certChainBytes);
}
}
//TODO possily get rid of exceptions
/**
* Method for processing the data in an EFI SignatureList (ex. can be one or more X509 certs)
*
* @param certChainData Byte array holding the cert chain data
* @throws java.security.cert.CertificateException If there's a problem parsing the X509 certificate.
* @throws java.security.NoSuchAlgorithmException if there's a problem hashing the certificate.
* @throws java.io.IOException If there's a problem parsing the signature data.
*/
private void processCertChain(final byte[] certChainData)
throws CertificateException, NoSuchAlgorithmException, IOException {
private void processCertChain(final byte[] certChainData) {
UefiX509Cert cert = null;
ByteArrayInputStream certChainDataIS = new ByteArrayInputStream(certChainData);
while (certChainDataIS.available() > 0) {
byte[] certType = new byte[UefiConstants.SIZE_2];
certChainDataIS.read(certType);
byte[] certLength = new byte[UefiConstants.SIZE_2];
certChainDataIS.read(certLength);
int cLength = new BigInteger(certLength).intValue() + UefiConstants.SIZE_4;
byte[] certData = new byte[cLength];
certChainDataIS.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);
// java.io.IOException If there's a problem parsing the cert chain data.
// java.security.cert.CertificateException if there's a problem parsing the X509 certificate.
// java.security.NoSuchAlgorithmException if there's a problem hashing the certificate.
try {
byte[] certType = new byte[UefiConstants.SIZE_2];
certChainDataIS.read(certType);
byte[] certLength = new byte[UefiConstants.SIZE_2];
certChainDataIS.read(certLength);
int cLength = new BigInteger(certLength).intValue() + UefiConstants.SIZE_4;
byte[] certData = new byte[cLength];
certChainDataIS.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);
// cert = new X509Certificate(certBlob);
certList.add(cert);
numberOfCerts++;
certList.add(cert);
numberOfCerts++;
} catch (IOException e) {
certProcessingError += "Error with Cert # " + (numberOfCerts+1)
+ ": IOException (error reading cert data)";
break;
} catch (CertificateException e) {
certProcessingError += "Error with Cert # " + (numberOfCerts+1)
+ ": CertificateException";
break;
} catch (NoSuchAlgorithmException e) {
certProcessingError += "Error with Cert # " + numberOfCerts+1
+ ": CNoSuchAlgorithmException";
break;
}
}
}
@ -123,17 +151,30 @@ public class SpdmCertificateChain {
*/
public String toString() {
String spdmMeasBlockInfo = "";
//
// if(spdmMeasurementBlockReadError) {
// spdmMeasBlockInfo += "\n Error reading SPDM Measurement Block";
// }
// else {
// spdmMeasBlockInfo += "\n Index = " + index;
// spdmMeasBlockInfo += "\n MeasurementSpec = " + measurementSpec;
// spdmMeasBlockInfo += spdmMeasurement.toString();
// }
//
return spdmMeasBlockInfo;
String spdmCertChainInfo = "";
if(spdmBaseHashAlgoError != "") {
spdmCertChainInfo += "\n *** ERROR with SPDM base hash algorithm size ***";
spdmCertChainInfo += "\n " + spdmBaseHashAlgoError;
spdmCertChainInfo += "\n Stopping processing of this cert chain";
}
else {
spdmCertChainInfo += "\n Root hash = " + rootHash.toString();
spdmCertChainInfo += "\n Number of certs in chain = " + numberOfCerts + "\n";
int certCnt = 1;
for (UefiX509Cert cert : certList) {
spdmCertChainInfo += " Cert # " + certCnt++ + " of " +
numberOfCerts + ": ------------------\n";
spdmCertChainInfo += cert.toString();
}
if (certProcessingError != "") {
spdmCertChainInfo += " *** ERROR processing cert ***";
spdmCertChainInfo += "\n " + certProcessingError;
spdmCertChainInfo += "\n Stopping processing of this cert chain";
}
}
return spdmCertChainInfo;
}
}

View File

@ -75,4 +75,42 @@ public class SpdmHa {
}
return alg;
}
/**
* Returns the hash name via a lookup.
* Lookup based upon SPDM Spec v1.03 section 10.4.
*
* @param algId int to convert to string
* @return name of the algorithm
*/
public static int tcgAlgIdToByteSize(final int algId) {
int byteSize;
switch (algId) {
//case TPM_ALG_RAW: // add this when have more test data
// byteSize = ;
// break;
case TPM_ALG_SHA_256:
byteSize = 32;
break;
case TPM_ALG_SHA_384:
byteSize = 48;
break;
case TPM_ALG_SHA_512:
byteSize = 64;
break;
case TPM_ALG_SHA3_256:
byteSize = 32;
break;
case TPM_ALG_SHA3_384:
byteSize = 48;
break;
case TPM_ALG_SHA3_512:
byteSize = 64;
break;
default:
byteSize = -1;
}
return byteSize;
}
}

View File

@ -10,9 +10,15 @@ import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
/**
* Class for processing the contents of a Secure Boot PK, KEK, 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
* Class for processing either
* 1) the contents of a Secure Boot PK, KEK, 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
* 2) the contents of an SPDM devdb,
* used for SPDM Device Policy or Device Authority, whose data is an EFIVariable
* EFIVariable data for SPDM Device Policy: UefiSignatureList
* EFIVariable data for SPDM Device: UefiSignatureData only
* as defined by PFP v1.06 Rev52, Section 10.4
* <p>
* typedef struct _EFI_SIGNATURE_DATA {
* EFI_GUID SignatureOwner;

View File

@ -12,12 +12,18 @@ import java.util.ArrayList;
import static hirs.utils.tpm.eventlog.uefi.UefiConstants.FILESTATUS_NOT_ACCESSIBLE;
/**
* 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.
* Class for processing either
* 1) the contents of a Secure Boot PK, KEK, 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
* 2) the contents of an SPDM devdb,
* used for SPDM Device Policy, whose data is an EFIVariable
* as defined by PFP v1.06 Rev52, Section 10.4
* <p>
* 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.
* An EFI Signature List is actually a list of Certificates used to verify a Signature.
* This is mainly found in PCR[7] UEFI variables for either the
* Secure Boot PK, KEK, Db and DBx variables
* or the SPDM devdb variable (under EV_EFI_SPDM_DEVICE_POLICY).
* <p>
* typedef struct _EFI_SIGNATURE_LIST {
* EFI_GUID SignatureType;
@ -27,6 +33,27 @@ import static hirs.utils.tpm.eventlog.uefi.UefiConstants.FILESTATUS_NOT_ACCESSIB
* // UINT8 SignatureHeader[SignatureHeaderSize];
* // EFI_SIGNATURE_DATA Signatures[...][SignatureSize];
* } EFI_SIGNATURE_LIST;
*
* Signatures[][] is an array of signatures.
* - Each signature is SignatureSize bytes in length.
* - The format of the signature is defined by SignatureType (SHA256, X509)
*
* / |-------------------------| ------- SignatureType
* / | Signature List Header | SignatureListSize
* |---------------------| / |-------------------------|\ SignatureHeaderSize
* | Signature List #0 | / | Signature Header | \ _____ SignatureSize
* | | / |-------------------------|
* |---------------------| / | Signature #0 |
* | Signature List #1 | / |-------------------------|
* |---------------------|/ | Signature #1 | --> each Signature is
* | Signature List #2 | |-------------------------| 1 UefiSignatureData
* | | | Signature #2 | (1 cert or hash)
* | | |-------------------------|
* |---------------------| | ... |
* \ | |
* \ |-------------------------|
* \ | Signature #n |
* \ |-------------------------|
*/
public class UefiSignatureList {
/**
@ -125,7 +152,7 @@ public class UefiSignatureList {
vendorTableFileStatus = signatureType.getVendorTableFileStatus();
// if signatureType is invalid, don't even process any of the data
// however, if signatureTYpe is valid, but some of the data later on is invalid, that will
// however, if signatureType is valid, but some of the data later on is invalid, that will
// be caught when UefiSignatureData is processed
if (!isValidSigListGUID(signatureType)) {
//processSignatureData(lists);
@ -230,7 +257,10 @@ public class UefiSignatureList {
sigInfo.append(" UEFI Signature List Type = " + signatureType.toString() + "\n");
sigInfo.append(" Number of Certs or Hashes in UEFI Signature List = " + numberOfCerts + "\n");
int certOrHashCnt = 1;
for (int i = 0; i < sigList.size(); i++) {
sigInfo.append(" Cert or Hash # " + certOrHashCnt++ + " of " +
numberOfCerts + ": ------------------\n");
UefiSignatureData certData = sigList.get(i);
sigInfo.append(certData.toString());
}