Merge pull request #299 from nsacyber/firmware-quote-validation

[#236] TPM Quote validation update
This commit is contained in:
iadgovuser26 2020-09-21 10:11:07 -04:00 committed by GitHub
commit 13f956732e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 495 additions and 150 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;
@ -166,8 +163,8 @@ public abstract class AbstractAttestationCertificateAuthority
private final DeviceRegister deviceRegister;
private final DeviceManager deviceManager;
private final DBManager<TPM2ProvisionerState> tpm2ProvisionerStateDBManager;
private String tpmQuoteHash;
private String tpmSignatureHash;
private String tpmQuoteHash = "";
private String tpmQuoteSignature = "";
private String pcrValues;
/**
@ -305,7 +302,7 @@ public abstract class AbstractAttestationCertificateAuthority
// update the validation result in the device
device.setSupplyChainStatus(summary.getOverallValidationResult());
deviceManager.updateDevice(device);
LOG.error("This is the device id? {} ", device.getId());
// check if supply chain validation succeeded.
// If it did not, do not provide the IdentityResponseEnvelope
if (summary.getOverallValidationResult() == AppraisalStatus.Status.PASS) {
@ -455,7 +452,7 @@ public abstract class AbstractAttestationCertificateAuthority
// perform supply chain validation
SupplyChainValidationSummary summary = supplyChainValidationService.validateSupplyChain(
endorsementCredential, platformCredentials, device);
device.setSummaryId(summary.getId().toString());
// update the validation result in the device
AppraisalStatus.Status validationResult = summary.getOverallValidationResult();
device.setSupplyChainStatus(validationResult);
@ -463,6 +460,34 @@ public abstract class AbstractAttestationCertificateAuthority
return validationResult;
}
/**
* Performs supply chain validation for just the quote under Firmware validation.
* Performed after main supply chain validation and a certificate request.
*
* @param device associated device to validate.
* @return the {@link AppraisalStatus} of the supply chain validation
*/
private AppraisalStatus.Status doQuoteValidation(final Device device) {
// perform supply chain validation
SupplyChainValidationSummary scvs = supplyChainValidationService.validateQuote(
device);
AppraisalStatus.Status validationResult;
// either validation wasn't enabled or device already failed
if (scvs == null) {
// this will just allow for the certificate to be saved.
validationResult = AppraisalStatus.Status.PASS;
} else {
device.setSummaryId(scvs.getId().toString());
// update the validation result in the device
validationResult = scvs.getOverallValidationResult();
device.setSupplyChainStatus(validationResult);
deviceManager.updateDevice(device);
}
return validationResult;
}
/**
* Basic implementation of the ACA processCertificateRequest method.
* Parses the nonce, validates its correctness, generates the signed,
@ -510,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());
@ -553,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")) {
@ -561,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;
}
/**
@ -663,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,
@ -1477,7 +1547,6 @@ public abstract class AbstractAttestationCertificateAuthority
IssuedAttestationCertificate attCert = new IssuedAttestationCertificate(
derEncodedAttestationCertificate, endorsementCredential, platformCredentials);
attCert.setDevice(device);
attCert.setPcrValues(savePcrValues(pcrValues, device.getName()));
certificateManager.save(attCert);
} catch (Exception e) {
LOG.error("Error saving generated Attestation Certificate to database.", e);
@ -1486,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

@ -1,6 +1,7 @@
package hirs.attestationca.service;
import java.util.Set;
import hirs.data.persist.Device;
import hirs.data.persist.SupplyChainValidationSummary;
import hirs.data.persist.certificate.EndorsementCredential;
@ -25,4 +26,12 @@ public interface SupplyChainValidationService {
SupplyChainValidationSummary validateSupplyChain(EndorsementCredential ec,
Set<PlatformCredential> pc,
Device device);
/**
* 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.
*/
SupplyChainValidationSummary validateQuote(Device device);
}

View File

@ -1,9 +1,6 @@
package hirs.attestationca.service;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
@ -26,6 +23,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.LinkedList;
import java.util.UUID;
import java.util.stream.Collectors;
import org.apache.logging.log4j.Level;
import hirs.appraiser.Appraiser;
@ -40,7 +38,6 @@ import hirs.data.persist.certificate.Certificate;
import hirs.data.persist.certificate.CertificateAuthorityCredential;
import hirs.data.persist.certificate.EndorsementCredential;
import hirs.data.persist.certificate.PlatformCredential;
import hirs.data.persist.certificate.IssuedAttestationCertificate;
import hirs.data.persist.ReferenceManifest;
import hirs.persist.AppraiserManager;
import hirs.persist.CertificateManager;
@ -51,6 +48,7 @@ import hirs.persist.DBManagerException;
import hirs.persist.PersistenceConfiguration;
import hirs.persist.PolicyManager;
import hirs.validation.CredentialValidator;
import java.util.HashMap;
import java.util.Map;
@ -127,6 +125,7 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
List<SupplyChainValidation> validations = new LinkedList<>();
Map<PlatformCredential, SupplyChainValidation> deltaMapping = new HashMap<>();
SupplyChainValidation platformScv = null;
LOGGER.info("Validating supply chain.");
// Validate the Endorsement Credential
if (policy.isEcValidationEnabled()) {
@ -257,6 +256,7 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
} catch (DBManagerException ex) {
LOGGER.error("Failed to save Supply Chain summary", ex);
}
return summary;
}
@ -325,9 +325,6 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
String manufacturer = device.getDeviceInfo()
.getHardwareInfo().getManufacturer();
IssuedAttestationCertificate attCert = IssuedAttestationCertificate
.select(this.certificateManager)
.byDeviceId(device.getId()).getCertificate();
ReferenceManifest rim = ReferenceManifest.select(
this.referenceManifestManager)
.byManufacturer(manufacturer)
@ -348,19 +345,13 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
}
pcrPolicy.setBaselinePcrs(baseline);
if (attCert != null) {
Path pcrPath = Paths.get(attCert.getPcrValues());
if (device != null) {
String pcrContent = "";
if (Files.exists(pcrPath)) {
try {
pcrContent = new String(Files.readAllBytes(pcrPath), "UTF8");
} catch (IOException ioEx) {
LOGGER.error(ioEx);
}
try {
pcrContent = new String(device.getDeviceInfo().getTPMInfo().getPcrValues());
} catch (NullPointerException npEx) {
LOGGER.error(npEx);
}
String[] pcrSet = null;
String[] quote = null;
int algorithmLength = baseline[0].length();
if (pcrContent.isEmpty()) {
fwStatus = new AppraisalStatus(FAIL,
@ -368,31 +359,13 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
+ "provide pcr values.");
LOGGER.warn(String.format(
"Firmware validation failed: Client (%s) did not "
+ "provide pcr values.", attCert.getDevice().getName()));
+ "provide pcr values.", device.getName()));
} else {
// we have a full set of PCR values
pcrSet = pcrContent.split("\\n");
quote = new String[TPMMeasurementRecord.MAX_PCR_ID + 1];
int algorithmLength = baseline[0].length();
String[] storedPcrs = buildStoredPcrs(pcrContent, algorithmLength);
// 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++) {
quote[j] = pcrSet[++i].split(":")[1].trim();
}
break;
}
}
}
if (quote[0].isEmpty()) {
if (storedPcrs[0].isEmpty()) {
// validation fail
fwStatus = new AppraisalStatus(FAIL,
"Firmware validation failed: "
@ -400,7 +373,7 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
+ "values are not the same algorithm "
+ "as associated RIM.");
} else {
StringBuilder sb = pcrPolicy.validatePcrs(quote);
StringBuilder sb = pcrPolicy.validatePcrs(storedPcrs);
if (sb.length() > 0) {
level = Level.ERROR;
fwStatus = new AppraisalStatus(FAIL, sb.toString());
@ -419,6 +392,96 @@ 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.
* @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 = 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()]);
}
String pcrContent = new String(device.getDeviceInfo().getTPMInfo().getPcrValues());
String[] storedPcrs = buildStoredPcrs(pcrContent, baseline[0].length());
PCRPolicy pcrPolicy = policy.getPcrPolicy();
pcrPolicy.setBaselinePcrs(baseline);
// grab the quote
byte[] hash = device.getDeviceInfo().getTPMInfo().getTpmQuoteHash();
if (pcrPolicy.validateQuote(hash, storedPcrs)) {
level = Level.INFO;
fwStatus = new AppraisalStatus(PASS,
SupplyChainCredentialValidator.FIRMWARE_VALID);
fwStatus.setMessage("Firmware validation of TPM Quote successful.");
} else {
fwStatus.setMessage("Firmware validation of TPM Quote failed.");
}
}
quoteScv = buildValidationRecord(SupplyChainValidation
.ValidationType.FIRMWARE,
fwStatus.getAppStatus(), fwStatus.getMessage(), rim, level);
// Generate validation summary, save it, and return it.
List<SupplyChainValidation> validations = new ArrayList<>();
SupplyChainValidationSummary previous
= this.supplyChainValidatorSummaryManager.get(
UUID.fromString(device.getSummaryId()));
for (SupplyChainValidation scv : previous.getValidations()) {
if (scv.getValidationType() != SupplyChainValidation.ValidationType.FIRMWARE) {
validations.add(buildValidationRecord(scv.getValidationType(),
scv.getResult(), scv.getMessage(),
scv.getCertificatesUsed().get(0), Level.INFO));
}
}
validations.add(quoteScv);
previous.archive();
supplyChainValidatorSummaryManager.update(previous);
summary = new SupplyChainValidationSummary(device, validations);
// try removing the supply chain validation as well and resaving that
try {
supplyChainValidatorSummaryManager.save(summary);
} catch (DBManagerException ex) {
LOGGER.error("Failed to save Supply Chain Summary", ex);
}
}
return summary;
}
private SupplyChainValidation validateEndorsementCredential(final EndorsementCredential ec,
final boolean acceptExpiredCerts) {
final SupplyChainValidation.ValidationType validationType
@ -553,7 +616,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);
@ -671,4 +733,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

@ -5,9 +5,11 @@ import hirs.attestationca.portal.datatables.DataTableResponse;
import hirs.attestationca.portal.datatables.OrderedListQueryDataTableAdapter;
import hirs.attestationca.portal.page.PageController;
import hirs.attestationca.portal.page.params.NoPageParams;
import hirs.data.persist.certificate.Certificate;
import org.apache.logging.log4j.Logger;
import static org.apache.logging.log4j.LogManager.getLogger;
import org.hibernate.Criteria;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
@ -79,16 +81,19 @@ public class ValidationReportsPageController extends PageController<NoPageParams
// define an alias so the composite object, device, can be used by the
// datatables / query. This is necessary so the device.name property can
// be used.
CriteriaModifier modifier = new CriteriaModifier() {
CriteriaModifier criteriaModifier = new CriteriaModifier() {
@Override
public void modify(final Criteria criteria) {
criteria.add(Restrictions.isNull(Certificate.ARCHIVE_FIELD));
criteria.createAlias("device", "device");
}
};
FilteredRecordsList<SupplyChainValidationSummary> records =
OrderedListQueryDataTableAdapter.getOrderedList(SupplyChainValidationSummary.class,
supplyChainValidatorSummaryManager, input, orderColumnName, modifier);
OrderedListQueryDataTableAdapter.getOrderedList(
SupplyChainValidationSummary.class,
supplyChainValidatorSummaryManager, input, orderColumnName,
criteriaModifier);
return new DataTableResponse<>(records, input);
}

View File

@ -137,7 +137,7 @@ class CommandTpm2 {
std::string getQuote(const std::string& pcr_selection,
const std::string& nonce);
std::string getPcrsList();
std::string getPcrList();
};
} // namespace tpm2

View File

@ -558,7 +558,7 @@ string CommandTpm2::getQuote(const string& pcr_selection,
* Method to get the full list of pcrs from the TPM.
*
*/
string CommandTpm2::getPcrsList() {
string CommandTpm2::getPcrList() {
string pcrslist;
stringstream argsStream;

View File

@ -57,6 +57,7 @@ message DeviceInfo {
required HardwareInfo hw = 2;
required NetworkInfo nw = 3;
required OsInfo os = 4;
optional bytes pcrslist = 5;
}
message IdentityClaim {
@ -80,7 +81,6 @@ message IdentityClaimResponse {
message CertificateRequest {
required bytes nonce = 1;
optional bytes quote = 2;
optional bytes pcrslist = 3;
}
message CertificateResponse {

View File

@ -98,7 +98,7 @@ string RestfulClientProvisioner::sendIdentityClaim(
stringstream errormsg;
errormsg << "Error communicating with ACA server. "
<< "Received response code: " << to_string(r.status_code)
<< "\n\nError message fom ACA was: "
<< "\n\nError message from ACA was: "
<< JSONFieldParser::parseJsonStringField(r.text,
ACA_ERROR_FIELDNAME);
throw HirsRuntimeException(errormsg.str(),

View File

@ -64,6 +64,7 @@ int provision() {
// collect device info
cout << "----> Collecting device information" << endl;
hirs::pb::DeviceInfo dv = DeviceInfoCollector::collectDeviceInfo();
dv.set_pcrslist(tpm2.getPcrList());
// send identity claim
cout << "----> Sending identity claim to Attestation CA" << endl;
@ -106,10 +107,14 @@ int provision() {
"14,15,16,17,18,19,20,21,22,23",
decryptedNonce));
certificateRequest.set_pcrslist(tpm2.getPcrsList());
const string& akCertificateByteString
= provisioner.sendAttestationCertificateRequest(certificateRequest);
if (akCertificateByteString == "") {
cout << "----> Provisioning failed.";
cout << "Please refer to the Attestation CA for details." << endl;
return 0;
}
cout << "----> Storing attestation key certificate" << endl;
tpm2.storeAKCertificate(akCertificateByteString);
return 1;

View File

@ -95,6 +95,9 @@ public class Device extends AbstractEntity {
@Column(name = "state_override_reason")
private String overrideReason;
@Column(name = "summary_id")
private String summaryId;
/**
* Default constructor required by Hibernate.
*/
@ -358,6 +361,22 @@ public class Device extends AbstractEntity {
this.supplyChainValidationStatus = supplyChainValidationStatus;
}
/**
* Getter for the last summary id.
* @return UUID for the summary
*/
public String getSummaryId() {
return summaryId;
}
/**
* Setter for the last summary id.
* @param summaryId UUID
*/
public void setSummaryId(final String summaryId) {
this.summaryId = summaryId;
}
/**
* Returns a hash code for this <code>Device</code>. The hash code is
* determined from the name of the <code>Device</code>.

View File

@ -2,9 +2,20 @@ 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.commons.codec.binary.Hex;
import org.apache.logging.log4j.Logger;
import java.nio.charset.Charset;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
/**
* The class handles the flags that ignore certain PCRs for validation.
*/
@ -52,10 +63,10 @@ public final class PCRPolicy extends Policy {
* Compares the baseline pcr list and the quote pcr list. If the
* ignore flags are set, 10 and 17-19 will be skipped for comparison.
*
* @param quotePcrs non-baseline pcr list
* @param storedPcrs non-baseline pcr list
* @return a StringBuilder that is empty if everything passes.
*/
public StringBuilder validatePcrs(final String[] quotePcrs) {
public StringBuilder validatePcrs(final String[] storedPcrs) {
StringBuilder sb = new StringBuilder();
String failureMsg = "PCR %d does not match%n";
@ -70,7 +81,7 @@ public final class PCRPolicy extends Policy {
i += NUM_OF_TBOOT_PCR;
}
if (!baselinePcrs[i].equals(quotePcrs[i])) {
if (!baselinePcrs[i].equals(storedPcrs[i])) {
sb.append(String.format(failureMsg, i));
}
}
@ -78,6 +89,47 @@ public final class PCRPolicy extends Policy {
return sb;
}
/**
* 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, final String[] storedPcrs) {
LOGGER.info("Validating quote from associated device.");
boolean validated = false;
short localityAtRelease = 0;
Charset charset = Charset.forName("UTF-8");
String quoteString = new String(tpmQuote, charset);
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,
Arrays.asList(measurements));
PcrInfoShort pcrInfoShort = new PcrInfoShort(pcrSelection,
localityAtRelease,
tpmQuote, pcrComposite);
try {
String calculatedString = Hex.encodeHexString(
pcrInfoShort.getCalculatedDigest());
validated = quoteString.contains(calculatedString);
} catch (NoSuchAlgorithmException naEx) {
LOGGER.error(naEx);
}
return validated;
}
/**
* Getter for the array of baseline PCRs.
* @return instance of the PCRs.

View File

@ -48,6 +48,7 @@ public class SwidResource {
private List<String> pcrValues;
private TpmWhiteListBaseline tpmWhiteList;
private DigestAlgorithm digest = DigestAlgorithm.SHA1;
private boolean validFileSize = false;
/**
* Default constructor.
@ -192,6 +193,14 @@ public class SwidResource {
this.pcrValues = pcrValues;
}
/**
* flag for if the file sizes match with the swidtag.
* @return true if they match
*/
public boolean isValidFileSize() {
return validFileSize;
}
/**
* Getter for a generated map of the PCR values.
*

View File

@ -10,7 +10,6 @@ import java.util.Set;
import java.util.UUID;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Column;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
@ -21,8 +20,6 @@ import javax.persistence.ManyToOne;
@Entity
public class IssuedAttestationCertificate extends DeviceAssociatedCertificate {
private static final int MAX_CERT_LENGTH_BYTES = 1024;
/**
* AIC label that must be used.
*/
@ -36,9 +33,6 @@ public class IssuedAttestationCertificate extends DeviceAssociatedCertificate {
@JoinColumn(name = "pc_id")
private Set<PlatformCredential> platformCredentials;
@Column(nullable = true, length = MAX_CERT_LENGTH_BYTES)
private String pcrValues;
/**
* This class enables the retrieval of IssuedAttestationCertificate by their attributes.
*/
@ -129,20 +123,4 @@ public class IssuedAttestationCertificate extends DeviceAssociatedCertificate {
public Set<PlatformCredential> getPlatformCredentials() {
return Collections.unmodifiableSet(platformCredentials);
}
/**
* Getter for the pcrValues passed up by the client.
* @return a string blob of pcrs
*/
public String getPcrValues() {
return pcrValues;
}
/**
* Setter for the pcrValues passed up by the client.
* @param pcrValues to be stored.
*/
public void setPcrValues(final String pcrValues) {
this.pcrValues = pcrValues;
}
}

View File

@ -24,6 +24,7 @@ import org.hibernate.annotations.Type;
@Embeddable
public class TPMInfo implements Serializable {
private static final Logger LOGGER = LogManager.getLogger(TPMInfo.class);
private static final int MAX_BLOB_SIZE = 65535;
@XmlElement
@Column(length = DeviceInfoReport.MED_STRING_LENGTH, nullable = true)
@ -52,6 +53,15 @@ public class TPMInfo implements Serializable {
@JsonIgnore
private X509Certificate identityCertificate;
@Column(nullable = true, length = MAX_BLOB_SIZE)
private byte[] pcrValues;
@Column(nullable = true, length = MAX_BLOB_SIZE)
private byte[] tpmQuoteHash;
@Column(nullable = true, length = MAX_BLOB_SIZE)
private byte[] tpmQuoteSignature;
/**
* Constructor used to create a TPMInfo object.
*
@ -68,17 +78,65 @@ public class TPMInfo implements Serializable {
* short representing the minor revision number for the TPM
* @param identityCertificate
* byte array with the value of the identity certificate
* @param pcrValues
* short representing the major revision number for the TPM
* @param tpmQuoteHash
* short representing the minor revision number for the TPM
* @param tpmQuoteSignature
* byte array with the value of the identity certificate
*/
@SuppressWarnings("parameternumber")
public TPMInfo(final String tpmMake, final short tpmVersionMajor,
final short tpmVersionMinor, final short tpmVersionRevMajor,
final short tpmVersionRevMinor,
final X509Certificate identityCertificate) {
final X509Certificate identityCertificate, final byte[] pcrValues,
final byte[] tpmQuoteHash, final byte[] tpmQuoteSignature) {
setTPMMake(tpmMake);
setTPMVersionMajor(tpmVersionMajor);
setTPMVersionMinor(tpmVersionMinor);
setTPMVersionRevMajor(tpmVersionRevMajor);
setTPMVersionRevMinor(tpmVersionRevMinor);
setIdentityCertificate(identityCertificate);
setPcrValues(pcrValues);
setTpmQuoteHash(tpmQuoteHash);
setTpmQuoteSignature(tpmQuoteSignature);
}
/**
* Constructor used to create a TPMInfo object without an identity
* certificate.
*
* @param tpmMake
* String representing the make information for the TPM,
* NullPointerException thrown if null
* @param tpmVersionMajor
* short representing the major version number for the TPM
* @param tpmVersionMinor
* short representing the minor version number for the TPM
* @param tpmVersionRevMajor
* short representing the major revision number for the TPM
* @param tpmVersionRevMinor
* short representing the minor revision number for the TPM
* @param pcrValues
* short representing the major revision number for the TPM
* @param tpmQuoteHash
* short representing the minor revision number for the TPM
* @param tpmQuoteSignature
* byte array with the value of the identity certificate
*/
@SuppressWarnings("parameternumber")
public TPMInfo(final String tpmMake, final short tpmVersionMajor,
final short tpmVersionMinor, final short tpmVersionRevMajor,
final short tpmVersionRevMinor, final byte[] pcrValues,
final byte[] tpmQuoteHash, final byte[] tpmQuoteSignature) {
setTPMMake(tpmMake);
setTPMVersionMajor(tpmVersionMajor);
setTPMVersionMinor(tpmVersionMinor);
setTPMVersionRevMajor(tpmVersionRevMajor);
setTPMVersionRevMinor(tpmVersionRevMinor);
setPcrValues(pcrValues);
setTpmQuoteHash(tpmQuoteHash);
setTpmQuoteSignature(tpmQuoteSignature);
}
/**
@ -98,13 +156,38 @@ public class TPMInfo implements Serializable {
* short representing the minor revision number for the TPM
*/
public TPMInfo(final String tpmMake, final short tpmVersionMajor,
final short tpmVersionMinor, final short tpmVersionRevMajor,
final short tpmVersionRevMinor) {
setTPMMake(tpmMake);
setTPMVersionMajor(tpmVersionMajor);
setTPMVersionMinor(tpmVersionMinor);
setTPMVersionRevMajor(tpmVersionRevMajor);
setTPMVersionRevMinor(tpmVersionRevMinor);
final short tpmVersionMinor, final short tpmVersionRevMajor,
final short tpmVersionRevMinor) {
this(tpmMake, tpmVersionMajor, tpmVersionMinor, tpmVersionRevMajor,
tpmVersionRevMinor, null,
new byte[0], new byte[0], new byte[0]);
}
/**
* Constructor used to create a TPMInfo object without an identity
* certificate.
*
* @param tpmMake
* String representing the make information for the TPM,
* NullPointerException thrown if null
* @param tpmVersionMajor
* short representing the major version number for the TPM
* @param tpmVersionMinor
* short representing the minor version number for the TPM
* @param tpmVersionRevMajor
* short representing the major revision number for the TPM
* @param tpmVersionRevMinor
* short representing the minor revision number for the TPM
* @param identityCertificate
* byte array with the value of the identity certificate
*/
public TPMInfo(final String tpmMake, final short tpmVersionMajor,
final short tpmVersionMinor, final short tpmVersionRevMajor,
final short tpmVersionRevMinor,
final X509Certificate identityCertificate) {
this(tpmMake, tpmVersionMajor, tpmVersionMinor, tpmVersionRevMajor,
tpmVersionRevMinor, identityCertificate,
new byte[0], new byte[0], new byte[0]);
}
/**
@ -112,10 +195,13 @@ public class TPMInfo implements Serializable {
*/
public TPMInfo() {
this(DeviceInfoReport.NOT_SPECIFIED,
(short) 0,
(short) 0,
(short) 0,
(short) 0);
(short) 0,
(short) 0,
(short) 0,
(short) 0,
new byte[0],
new byte[0],
new byte[0]);
identityCertificate = null;
}
@ -175,6 +261,30 @@ public class TPMInfo implements Serializable {
return identityCertificate;
}
/**
* Getter for the tpmQuote passed up by the client.
* @return a byte blob of quote
*/
public final byte[] getTpmQuoteHash() {
return tpmQuoteHash.clone();
}
/**
* Getter for the quote signature.
* @return a byte blob.
*/
public final byte[] getTpmQuoteSignature() {
return tpmQuoteSignature.clone();
}
/**
* Getter for the pcr values.
* @return a byte blob for the pcrValues.
*/
public final byte[] getPcrValues() {
return pcrValues.clone();
}
@Override
public final int hashCode() {
final int prime = 31;
@ -285,4 +395,28 @@ public class TPMInfo implements Serializable {
LOGGER.debug("setting identity certificate");
this.identityCertificate = identityCertificate;
}
private void setPcrValues(final byte[] pcrValues) {
if (pcrValues == null) {
this.pcrValues = new byte[0];
} else {
this.pcrValues = pcrValues.clone();
}
}
private void setTpmQuoteHash(final byte[] tpmQuoteHash) {
if (tpmQuoteHash == null) {
this.tpmQuoteHash = new byte[0];
} else {
this.tpmQuoteHash = tpmQuoteHash.clone();
}
}
private void setTpmQuoteSignature(final byte[] tpmQuoteSignature) {
if (tpmQuoteSignature == null) {
this.tpmQuoteSignature = new byte[0];
} else {
this.tpmQuoteSignature = tpmQuoteSignature.clone();
}
}
}

View File

@ -115,7 +115,7 @@ public class PcrComposite {
@XmlElement(name = "ValueSize", required = true)
public final int getValueSize() {
int valueSize = 0;
for (TPMMeasurementRecord record: this.pcrValueList) {
for (TPMMeasurementRecord record : this.pcrValueList) {
valueSize += record.getHash().getDigest().length;
}
return valueSize;

View File

@ -287,7 +287,6 @@ public class PcrInfoShort {
* @return byte array representing the PcrInfoShort object
*/
public final byte[] getValue() {
ByteBuffer byteBuffer = ByteBuffer.allocate(getLength());
byteBuffer.put(pcrSelection.getValue());
byteBuffer.put((byte) localityAtRelease);

View File

@ -30,6 +30,10 @@ public class PcrSelection {
private static final Logger LOGGER = LogManager
.getLogger(PcrSelection.class);
private static final int MAX_SIZE_PCR_ARRAY = 3;
/**
* All PCRs are on.
*/
public static final int ALL_PCRS_ON = 0xffffff;
@XmlAttribute(name = "PcrSelect", required = true)
private final byte[] pcrSelect;
@ -76,8 +80,7 @@ public class PcrSelection {
* long value representing the bits to be selected
*/
public PcrSelection(final long pcrSelectLong) {
final int allPCRsOn = 0xffffff;
if (pcrSelectLong > allPCRsOn) {
if (pcrSelectLong > ALL_PCRS_ON) {
LOGGER.error("pcrSelect long value must be less than 3 bytes");
throw new InvalidParameterException("pcrSelect");
}