All bugs are fixed. The SupplyChainValidationSummary wasn't getting pulled from the DB.

This commit is contained in:
Cyrus 2020-08-27 12:11:12 -04:00
parent 0f3eb1b5d0
commit 0ab91b9b41
5 changed files with 209 additions and 164 deletions

View File

@ -62,14 +62,11 @@ import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
@ -167,8 +164,9 @@ public abstract class AbstractAttestationCertificateAuthority
private final DeviceManager deviceManager;
private final DBManager<TPM2ProvisionerState> tpm2ProvisionerStateDBManager;
private String tpmQuoteHash = "";
private String tpmSignatureHash = "";
private String tpmQuoteSignature = "";
private String pcrValues;
private SupplyChainValidationSummary savedSummary;
/**
* Constructor.
@ -455,7 +453,7 @@ public abstract class AbstractAttestationCertificateAuthority
// perform supply chain validation
SupplyChainValidationSummary summary = supplyChainValidationService.validateSupplyChain(
endorsementCredential, platformCredentials, device);
savedSummary = summary;
// update the validation result in the device
AppraisalStatus.Status validationResult = summary.getOverallValidationResult();
device.setSupplyChainStatus(validationResult);
@ -472,7 +470,8 @@ public abstract class AbstractAttestationCertificateAuthority
*/
private AppraisalStatus.Status doQuoteValidation(final Device device) {
// perform supply chain validation
SupplyChainValidationSummary scvs = supplyChainValidationService.validateQuote(device);
SupplyChainValidationSummary scvs = supplyChainValidationService.validateQuote(
device, savedSummary);
AppraisalStatus.Status validationResult;
// either validation wasn't enabled or device already failed
@ -536,37 +535,63 @@ public abstract class AbstractAttestationCertificateAuthority
Set<PlatformCredential> platformCredentials = parsePcsFromIdentityClaim(claim,
endorsementCredential);
// Parse through the Provisioner supplied TPM Quote and pcr values
// these fields are optional
if (request.getQuote() != null && !request.getQuote().isEmpty()) {
parseTPMQuote(request.getQuote().toStringUtf8());
}
// if (request.getPcrslist() != null && !request.getPcrslist().isEmpty()) {
// this.pcrValues = request.getPcrslist().toStringUtf8();
// }
// Get device name and device
String deviceName = claim.getDv().getNw().getHostname();
Device device = deviceManager.getDevice(deviceName);
// Create signed, attestation certificate
X509Certificate attestationCertificate = generateCredential(akPub,
endorsementCredential, platformCredentials, deviceName);
byte[] derEncodedAttestationCertificate = getDerEncodedCertificate(
attestationCertificate);
// Parse through the Provisioner supplied TPM Quote and pcr values
// these fields are optional
if (request.getQuote() != null && !request.getQuote().isEmpty()) {
parseTPMQuote(request.getQuote().toStringUtf8());
TPMInfo savedInfo = device.getDeviceInfo().getTPMInfo();
TPMInfo tpmInfo = null;
try {
tpmInfo = new TPMInfo(savedInfo.getTPMMake(),
savedInfo.getTPMVersionMajor(),
savedInfo.getTPMVersionMinor(),
savedInfo.getTPMVersionRevMajor(),
savedInfo.getTPMVersionRevMinor(),
savedInfo.getPcrValues(),
this.tpmQuoteHash.getBytes("UTF-8"),
this.tpmQuoteSignature.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
LOG.error(e);
}
DeviceInfoReport dvReport = new DeviceInfoReport(
device.getDeviceInfo().getNetworkInfo(),
device.getDeviceInfo().getOSInfo(),
device.getDeviceInfo().getFirmwareInfo(),
device.getDeviceInfo().getHardwareInfo(), tpmInfo,
claim.getClientVersion());
device = this.deviceRegister.saveOrUpdateDevice(dvReport);
}
// We validated the nonce and made use of the identity claim so state can be deleted
tpm2ProvisionerStateDBManager.delete(tpm2ProvisionerState);
AppraisalStatus.Status validationResult = doQuoteValidation(device);
if (validationResult == AppraisalStatus.Status.PASS) {
// Create signed, attestation certificate
X509Certificate attestationCertificate = generateCredential(akPub,
endorsementCredential, platformCredentials, deviceName);
byte[] derEncodedAttestationCertificate = getDerEncodedCertificate(
attestationCertificate);
// Package the signed certificate into a response
ByteString certificateBytes = ByteString.copyFrom(derEncodedAttestationCertificate);
ProvisionerTpm2.CertificateResponse response = ProvisionerTpm2.CertificateResponse
.newBuilder().setCertificate(certificateBytes).build();
// We validated the nonce and made use of the identity claim so state can be deleted
tpm2ProvisionerStateDBManager.delete(tpm2ProvisionerState);
saveAttestationCertificate(derEncodedAttestationCertificate, endorsementCredential,
platformCredentials, device);
// Package the signed certificate into a response
ByteString certificateBytes = ByteString.copyFrom(derEncodedAttestationCertificate);
ProvisionerTpm2.CertificateResponse response = ProvisionerTpm2.CertificateResponse
.newBuilder().setCertificate(certificateBytes).build();
return response.toByteArray();
saveAttestationCertificate(derEncodedAttestationCertificate, endorsementCredential,
platformCredentials, device);
return response.toByteArray();
} else {
LOG.error("Supply chain validation did not succeed. "
+ "Firmware Quote Validation failed. Result is: "
+ validationResult);
return new byte[]{};
}
} else {
LOG.error("Could not process credential request. Invalid nonce provided: "
+ request.getNonce().toString());
@ -579,7 +604,8 @@ public abstract class AbstractAttestationCertificateAuthority
* quote and the signature hash.
* @param tpmQuote contains hash values for the quote and the signature
*/
private void parseTPMQuote(final String tpmQuote) {
private boolean parseTPMQuote(final String tpmQuote) {
boolean success = false;
if (tpmQuote != null) {
String[] lines = tpmQuote.split(":");
if (lines[1].contains("signature")) {
@ -587,8 +613,11 @@ public abstract class AbstractAttestationCertificateAuthority
} else {
this.tpmQuoteHash = lines[1].trim();
}
this.tpmSignatureHash = lines[2].trim();
this.tpmQuoteSignature = lines[2].trim();
success = true;
}
return success;
}
/**
@ -689,9 +718,24 @@ public abstract class AbstractAttestationCertificateAuthority
hwProto.getProductVersion(), hwProto.getSystemSerialNumber(),
firstChassisSerialNumber, firstBaseboardSerialNumber);
if (dv.getPcrslist() != null && !dv.getPcrslist().isEmpty()) {
this.pcrValues = dv.getPcrslist().toStringUtf8();
}
// Get TPM info, currently unimplemented
TPMInfo tpm = new TPMInfo();
try {
tpm = new TPMInfo(DeviceInfoReport.NOT_SPECIFIED,
(short) 0,
(short) 0,
(short) 0,
(short) 0,
this.pcrValues.getBytes("UTF-8"),
this.tpmQuoteHash.getBytes("UTF-8"),
this.tpmQuoteSignature.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
tpm = new TPMInfo();
}
// Create final report
DeviceInfoReport dvReport = new DeviceInfoReport(nw, os, fw, hw, tpm,
@ -1511,29 +1555,4 @@ public abstract class AbstractAttestationCertificateAuthority
+ e.getMessage(), e);
}
}
private String savePcrValues(final String pcrValues, final String deviceName) {
if (pcrValues != null && !pcrValues.isEmpty()) {
try {
if (Files.notExists(Paths.get(PCR_UPLOAD_FOLDER))) {
Files.createDirectory(Paths.get(PCR_UPLOAD_FOLDER));
}
Path pcrPath = Paths.get(String.format("%s/%s",
PCR_UPLOAD_FOLDER, deviceName));
if (Files.notExists(pcrPath)) {
Files.createFile(pcrPath);
}
Files.write(pcrPath, pcrValues.getBytes("UTF8"));
return pcrPath.toString();
} catch (NoSuchFileException nsfEx) {
LOG.error(String.format("File Not found!: %s",
deviceName));
LOG.error(nsfEx);
} catch (IOException ioEx) {
LOG.error(ioEx);
}
}
return "empty";
}
}

View File

@ -30,7 +30,9 @@ public interface SupplyChainValidationService {
* A supplemental method that handles validating just the quote post main validation.
*
* @param device the associated device.
* @param summary the associated device summary
* @return True if validation is successful, false otherwise.
*/
SupplyChainValidationSummary validateQuote(Device device);
SupplyChainValidationSummary validateQuote(Device device,
SupplyChainValidationSummary summary);
}

View File

@ -257,79 +257,6 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
return summary;
}
/**
* A supplemental method that handles validating just the quote post main validation.
*
* @param device the associated device.
* @return True if validation is successful, false otherwise.
*/
@Override
public SupplyChainValidationSummary validateQuote(final Device device) {
final Appraiser supplyChainAppraiser = appraiserManager.getAppraiser(
SupplyChainAppraiser.NAME);
SupplyChainPolicy policy = (SupplyChainPolicy) policyManager.getDefaultPolicy(
supplyChainAppraiser);
SupplyChainValidation quoteScv = null;
SupplyChainValidationSummary summary = supplyChainValidatorSummaryManager
.get(device.getId());
Level level = Level.ERROR;
AppraisalStatus fwStatus = new AppraisalStatus(FAIL,
SupplyChainCredentialValidator.FIRMWARE_VALID);
// If the device already failed, then ignore
if (summary.getOverallValidationResult() == PASS) {
// check if the policy is enabled
if (policy.isFirmwareValidationEnabled()) {
String[] baseline = new String[Integer.SIZE];
String manufacturer = device.getDeviceInfo()
.getHardwareInfo().getManufacturer();
// need to get pcrs
ReferenceManifest rim = ReferenceManifest.select(
this.referenceManifestManager)
.byManufacturer(manufacturer)
.getRIM();
if (rim == null) {
fwStatus = new AppraisalStatus(FAIL,
String.format("Firmware Quote validation failed: "
+ "No associated RIM file could be found for %s",
manufacturer));
} else {
List<SwidResource> swids = rim.parseResource();
for (SwidResource swid : swids) {
baseline = swid.getPcrValues()
.toArray(new String[swid.getPcrValues().size()]);
}
PCRPolicy pcrPolicy = policy.getPcrPolicy();
pcrPolicy.setBaselinePcrs(baseline);
// grab the quote
// byte[] hash = device.getDeviceInfo().getTPMInfo().getTpmQuoteHash();
// byte[] signature = device.getDeviceInfo().getTPMInfo().getTpmQuoteHash();
//
// if (!pcrPolicy.validateQuote(hash)) {
// quoteScv = buildValidationRecord(SupplyChainValidation
// .ValidationType.FIRMWARE,
// fwStatus.getAppStatus(),
// "Firmware validation of TPM Quote failed.", rim, level);
// }
}
}
}
// Generate validation summary, save it, and return it.
summary.getValidations().add(quoteScv); //verify
try {
supplyChainValidatorSummaryManager.save(summary);
} catch (DBManagerException ex) {
LOGGER.error("Failed to save Supply Chain summary", ex);
}
return summary;
}
/**
* This method is a sub set of the validate supply chain method and focuses
* on the specific multibase validation check for a delta chain. This method
@ -422,9 +349,6 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
} catch (NullPointerException npEx) {
LOGGER.error(npEx);
}
String[] pcrSet = null;
String[] storedPcrs = null;
int algorithmLength = baseline[0].length();
if (pcrContent.isEmpty()) {
fwStatus = new AppraisalStatus(FAIL,
@ -435,26 +359,8 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
+ "provide pcr values.", device.getName()));
} else {
// we have a full set of PCR values
pcrSet = pcrContent.split("\\n");
storedPcrs = new String[TPMMeasurementRecord.MAX_PCR_ID + 1];
// we need to scroll through the entire list until we find
// a matching hash length
int offset = 1;
for (int i = 0; i < pcrSet.length; i++) {
if (pcrSet[i].contains("sha")) {
// entered a new set, check size
if (pcrSet[i + offset].split(":")[1].trim().length()
== algorithmLength) {
// found the matching set
for (int j = 0; j <= TPMMeasurementRecord.MAX_PCR_ID; j++) {
storedPcrs[j] = pcrSet[++i].split(":")[1].trim();
}
break;
}
}
}
int algorithmLength = baseline[0].length();
String[] storedPcrs = buildStoredPcrs(pcrContent, algorithmLength);
if (storedPcrs[0].isEmpty()) {
// validation fail
@ -483,6 +389,81 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
fwStatus.getAppStatus(), fwStatus.getMessage(), rim, level);
}
/**
* A supplemental method that handles validating just the quote post main validation.
*
* @param device the associated device.
* @param summary the associated device summary
* @return True if validation is successful, false otherwise.
*/
@Override
public SupplyChainValidationSummary validateQuote(final Device device,
final SupplyChainValidationSummary summary) {
final Appraiser supplyChainAppraiser = appraiserManager.getAppraiser(
SupplyChainAppraiser.NAME);
SupplyChainPolicy policy = (SupplyChainPolicy) policyManager.getDefaultPolicy(
supplyChainAppraiser);
SupplyChainValidation quoteScv = null;
SupplyChainValidationSummary newSummary = null;
Level level = Level.ERROR;
AppraisalStatus fwStatus = new AppraisalStatus(FAIL,
SupplyChainCredentialValidator.FIRMWARE_VALID);
// check if the policy is enabled
if (policy.isFirmwareValidationEnabled()) {
String[] baseline = new String[Integer.SIZE];
String manufacturer = device.getDeviceInfo()
.getHardwareInfo().getManufacturer();
// need to get pcrs
ReferenceManifest rim = ReferenceManifest.select(
this.referenceManifestManager)
.byManufacturer(manufacturer)
.getRIM();
if (rim == null) {
fwStatus = new AppraisalStatus(FAIL,
String.format("Firmware Quote validation failed: "
+ "No associated RIM file could be found for %s",
manufacturer));
} else {
List<SwidResource> swids = rim.parseResource();
for (SwidResource swid : swids) {
baseline = swid.getPcrValues()
.toArray(new String[swid.getPcrValues().size()]);
}
int algorithmLength = baseline[0].length();
String pcrContent = new String(device.getDeviceInfo().getTPMInfo().getPcrValues());
String[] storedPcrs = buildStoredPcrs(pcrContent, algorithmLength);
PCRPolicy pcrPolicy = policy.getPcrPolicy();
pcrPolicy.setBaselinePcrs(baseline);
// grab the quote
byte[] hash = device.getDeviceInfo().getTPMInfo().getTpmQuoteHash();
byte[] signature = device.getDeviceInfo().getTPMInfo().getTpmQuoteHash();
if (!pcrPolicy.validateQuote(hash, storedPcrs)) {
quoteScv = buildValidationRecord(SupplyChainValidation
.ValidationType.FIRMWARE,
fwStatus.getAppStatus(),
"Firmware validation of TPM Quote failed.", rim, level);
}
}
// Generate validation summary, save it, and return it.
List<SupplyChainValidation> validations = new ArrayList<>();
validations.addAll(summary.getValidations());
validations.add(quoteScv);
newSummary = new SupplyChainValidationSummary(device, validations);
try {
supplyChainValidatorSummaryManager.update(newSummary);
} catch (DBManagerException ex) {
LOGGER.error("Failed to save Supply Chain summary", ex);
}
}
return newSummary;
}
private SupplyChainValidation validateEndorsementCredential(final EndorsementCredential ec,
final boolean acceptExpiredCerts) {
final SupplyChainValidation.ValidationType validationType
@ -617,7 +598,6 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
final SupplyChainValidation.ValidationType validationType,
final AppraisalStatus.Status result, final String message,
final ArchivableEntity archivableEntity, final Level logLevel) {
List<ArchivableEntity> aeList = new ArrayList<>();
if (archivableEntity != null) {
aeList.add(archivableEntity);
@ -735,4 +715,30 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
return multiple;
}
private String[] buildStoredPcrs(final String pcrContent, final int algorithmLength) {
// we have a full set of PCR values
String[] pcrSet = pcrContent.split("\\n");
String[] storedPcrs = new String[TPMMeasurementRecord.MAX_PCR_ID + 1];
// we need to scroll through the entire list until we find
// a matching hash length
int offset = 1;
for (int i = 0; i < pcrSet.length; i++) {
if (pcrSet[i].contains("sha")) {
// entered a new set, check size
if (pcrSet[i + offset].split(":")[1].trim().length()
== algorithmLength) {
// found the matching set
for (int j = 0; j <= TPMMeasurementRecord.MAX_PCR_ID; j++) {
storedPcrs[j] = pcrSet[++i].split(":")[1].trim();
}
break;
}
}
}
return storedPcrs;
}
}

View File

@ -2,16 +2,19 @@ package hirs.data.persist;
import javax.persistence.Column;
import javax.persistence.Entity;
import static org.apache.logging.log4j.LogManager.getLogger;
import hirs.data.persist.tpm.PcrComposite;
import hirs.data.persist.tpm.PcrInfoShort;
import hirs.data.persist.tpm.PcrSelection;
import org.apache.commons.codec.DecoderException;
import org.apache.logging.log4j.Logger;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Base64;
/**
* The class handles the flags that ignore certain PCRs for validation.
@ -90,13 +93,22 @@ public final class PCRPolicy extends Policy {
* Compares hashs to validate the quote from the client.
*
* @param tpmQuote the provided quote
* @param storedPcrs values from the RIM file
* @return true if validated, false if not
*/
public boolean validateQuote(final byte[] tpmQuote) {
public boolean validateQuote(final byte[] tpmQuote, final String[] storedPcrs) {
LOGGER.info("Validating quote from associated device.");
boolean validated = false;
short localityAtRelease = 0;
TPMMeasurementRecord[] measurements = new TPMMeasurementRecord[baselinePcrs.length];
try {
for (int i = 0; i <= TPMMeasurementRecord.MAX_PCR_ID; i++) {
measurements[i] = new TPMMeasurementRecord(i, storedPcrs[i]);
}
} catch (DecoderException deEx) {
LOGGER.error(deEx);
}
PcrSelection pcrSelection = new PcrSelection(PcrSelection.ALL_PCRS_ON);
PcrComposite pcrComposite = new PcrComposite(
pcrSelection,
@ -106,20 +118,25 @@ public final class PCRPolicy extends Policy {
tpmQuote, pcrComposite);
try {
if (!Arrays.equals(pcrInfoShort.getCalculatedDigest(),
pcrInfoShort.getCompositeHash())) {
LOGGER.error("This is NOT matching: ");
LOGGER.error(new String(pcrInfoShort.getCalculatedDigest(), "UTF-8"));
validated = Arrays.equals(pcrInfoShort.getCalculatedDigest(),
pcrInfoShort.getCompositeHash());
if (validated) {
LOGGER.error("This is matching: ");
String value = Base64.getEncoder().encodeToString(pcrInfoShort
.getCalculatedDigest());
LOGGER.error(value);
LOGGER.error(new String(pcrInfoShort.getCompositeHash(), "UTF-8"));
} else {
LOGGER.error("This is matching: ");
LOGGER.error(new String(pcrInfoShort.getCalculatedDigest(), "UTF-8"));
LOGGER.error("This is NOT matching: ");
String value = new String(pcrInfoShort
.getCalculatedDigest(), "UTF-8");
LOGGER.error(value);
LOGGER.error(new String(pcrInfoShort.getCompositeHash(), "UTF-8"));
}
} catch (NoSuchAlgorithmException naEx) {
LOGGER.error(naEx);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException ueEx) {
LOGGER.error(ueEx);
}
return validated;

View File

@ -270,6 +270,7 @@ public class PcrInfoShort {
while (iter.hasNext()) {
TPMMeasurementRecord record = (TPMMeasurementRecord) iter.next();
LOGGER.error(record.getHash());
byteBuffer.put(record.getHash().getDigest());
}