Merge pull request #407 from nsacyber/issue-381

[#381] Update RIM validation in ACA
This commit is contained in:
iadgovuser26 2021-12-16 10:39:58 -05:00 committed by GitHub
commit bf8ef387c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 586 additions and 239 deletions

View File

@ -126,6 +126,8 @@ public abstract class AbstractAttestationCertificateAuthority
= "/webapps/HIRS_AttestationCA/upload/";
private static final String PCR_UPLOAD_FOLDER
= CATALINA_HOME + TOMCAT_UPLOAD_DIRECTORY;
private static final String PCR_QUOTE_MASK = "0,1,2,3,4,5,6,7,8,9,10,11,12,13,"
+ "14,15,16,17,18,19,20,21,22,23";
/**
* Number of bytes to include in the TPM2.0 nonce.
@ -441,6 +443,8 @@ public abstract class AbstractAttestationCertificateAuthority
RSAPublicKey akPub = parsePublicKey(claim.getAkPublicArea().toByteArray());
byte[] nonce = generateRandomBytes(NONCE_LENGTH);
ByteString blobStr = tpm20MakeCredential(ekPub, akPub, nonce);
SupplyChainPolicy scp = this.supplyChainValidationService.getPolicy();
String pcrQuoteMask = PCR_QUOTE_MASK;
String strNonce = HexUtils.byteArrayToHexString(nonce);
LOG.info("Sending nonce: " + strNonce);
@ -448,10 +452,14 @@ public abstract class AbstractAttestationCertificateAuthority
tpm2ProvisionerStateDBManager.save(new TPM2ProvisionerState(nonce, identityClaim));
if (scp != null && scp.isIgnoreImaEnabled()) {
pcrQuoteMask = PCR_QUOTE_MASK.replace("10,", "");
}
// Package response
ProvisionerTpm2.IdentityClaimResponse response
= ProvisionerTpm2.IdentityClaimResponse.newBuilder()
.setCredentialBlob(blobStr).build();
.setCredentialBlob(blobStr).setPcrMask(pcrQuoteMask)
.build();
return response.toByteArray();
} else {
@ -622,9 +630,11 @@ public abstract class AbstractAttestationCertificateAuthority
tpm2ProvisionerStateDBManager.delete(tpm2ProvisionerState);
// Package the signed certificate into a response
ByteString certificateBytes = ByteString.copyFrom(derEncodedAttestationCertificate);
ByteString certificateBytes = ByteString
.copyFrom(derEncodedAttestationCertificate);
ProvisionerTpm2.CertificateResponse response = ProvisionerTpm2.CertificateResponse
.newBuilder().setCertificate(certificateBytes).build();
.newBuilder().setCertificate(certificateBytes)
.build();
saveAttestationCertificate(derEncodedAttestationCertificate, endorsementCredential,
platformCredentials, device);
@ -1017,23 +1027,25 @@ public abstract class AbstractAttestationCertificateAuthority
}
}
} else if (dbSupport.isSwidSupplemental() && !dbSupport.isProcessed()) {
try {
TCGEventLog logProcessor = new TCGEventLog(dbSupport.getRimBytes());
ReferenceDigestValue rdv;
for (TpmPcrEvent tpe : logProcessor.getEventList()) {
rdv = new ReferenceDigestValue(rdr.getId(), tpe.getPcrIndex(),
tpe.getEventDigestStr(), tpe.getEventTypeStr(),
false, false);
this.referenceEventManager.saveValue(rdv);
if (rdr != null) {
try {
TCGEventLog logProcessor = new TCGEventLog(dbSupport.getRimBytes());
ReferenceDigestValue rdv;
for (TpmPcrEvent tpe : logProcessor.getEventList()) {
rdv = new ReferenceDigestValue(rdr.getId(), tpe.getPcrIndex(),
tpe.getEventDigestStr(), tpe.getEventTypeStr(),
false, false);
this.referenceEventManager.saveValue(rdv);
}
dbSupport.setProcessed(true);
this.referenceManifestManager.update(dbSupport);
} catch (CertificateException cEx) {
LOG.error(cEx);
} catch (NoSuchAlgorithmException noSaEx) {
LOG.error(noSaEx);
} catch (IOException ioEx) {
LOG.error(ioEx);
}
dbSupport.setProcessed(true);
this.referenceManifestManager.update(dbSupport);
} catch (CertificateException cEx) {
LOG.error(cEx);
} catch (NoSuchAlgorithmException noSaEx) {
LOG.error(noSaEx);
} catch (IOException ioEx) {
LOG.error(ioEx);
}
}
}

View File

@ -46,15 +46,12 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Import;
import org.springframework.stereotype.Service;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@ -95,7 +92,8 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
/**
* Constructor to set just the CertificateManager, so that cert chain validating
* methods can be called from outside classes.
* @param certificateManager the cert manager
*
* @param certificateManager the cert manager
*/
public SupplyChainValidationServiceImpl(final CertificateManager certificateManager) {
this.certificateManager = certificateManager;
@ -135,6 +133,7 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
/**
* Allows other service access to the policy information.
*
* @return supply chain policy
*/
public SupplyChainPolicy getPolicy() {
@ -243,7 +242,7 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
} else {
validations.add(new SupplyChainValidation(platformType,
AppraisalStatus.Status.FAIL, new ArrayList<>(pcs), pcErrorMessage));
}
}
}
}
@ -425,33 +424,20 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
// verify signatures
ReferenceManifestValidator referenceManifestValidator =
new ReferenceManifestValidator(
new ByteArrayInputStream(baseReferenceManifest.getRimBytes()));
new ReferenceManifestValidator();
referenceManifestValidator.setRim(baseReferenceManifest);
for (SwidResource swidRes : resources) {
supportReferenceManifest = SupportReferenceManifest.select(referenceManifestManager)
.byHexDecHash(swidRes.getHashValue()).getRIM();
if (supportReferenceManifest != null
&& swidRes.getName().equals(supportReferenceManifest.getFileName())) {
referenceManifestValidator.validateSupportRimHash(
supportReferenceManifest.getRimBytes(), swidRes.getHashValue());
} else {
supportReferenceManifest = null;
}
}
//Validate signing cert
Set<CertificateAuthorityCredential> allCerts =
CertificateAuthorityCredential.select(certificateManager).getCertificates();
CertificateAuthorityCredential signingCert = null;
for (CertificateAuthorityCredential cert : allCerts) {
if (Arrays.equals(cert.getEncodedPublicKey(),
referenceManifestValidator.getPublicKey().getEncoded())) {
signingCert = cert;
KeyStore keyStore = getCaChain(signingCert);
signingCert = cert;
KeyStore keyStore = getCaChain(signingCert);
if (referenceManifestValidator.validateXmlSignature(signingCert)) {
try {
X509Certificate x509Cert = signingCert.getX509Certificate();
if (!SupplyChainCredentialValidator.verifyCertificate(x509Cert, keyStore)) {
if (!SupplyChainCredentialValidator.verifyCertificate(
signingCert.getX509Certificate(), keyStore)) {
passed = false;
fwStatus = new AppraisalStatus(FAIL,
"Firmware validation failed: invalid certificate path.");
@ -467,6 +453,18 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
}
}
for (SwidResource swidRes : resources) {
supportReferenceManifest = SupportReferenceManifest.select(referenceManifestManager)
.byHexDecHash(swidRes.getHashValue()).getRIM();
if (supportReferenceManifest != null
&& swidRes.getName().equals(supportReferenceManifest.getFileName())) {
referenceManifestValidator.validateSupportRimHash(
supportReferenceManifest.getRimBytes(), swidRes.getHashValue());
} else {
supportReferenceManifest = null;
}
}
if (signingCert == null) {
passed = false;
fwStatus = new AppraisalStatus(FAIL,
@ -522,9 +520,9 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
+ "provide pcr values.", device.getName()));
} else {
// we have a full set of PCR values
int algorithmLength = baseline[0].length();
String[] storedPcrs = buildStoredPcrs(pcrContent, algorithmLength);
pcrPolicy.validatePcrs(storedPcrs);
//int algorithmLength = baseline[0].length();
//String[] storedPcrs = buildStoredPcrs(pcrContent, algorithmLength);
//pcrPolicy.validatePcrs(storedPcrs);
// part 2 of firmware validation check: bios measurements
// vs baseline tcg event log
@ -583,6 +581,9 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
} else {
fwStatus = new AppraisalStatus(FAIL, String.format("Firmware Validation failed: "
+ "%s for %s can not be found", failedString, manufacturer));
EventLogMeasurements eventLog = (EventLogMeasurements) measurement;
eventLog.setOverallValidationResult(fwStatus.getAppStatus());
this.referenceManifestManager.update(eventLog);
}
return buildValidationRecord(SupplyChainValidation.ValidationType.FIRMWARE,
@ -605,7 +606,7 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
SupplyChainValidationSummary summary = null;
Level level = Level.ERROR;
AppraisalStatus fwStatus = new AppraisalStatus(FAIL,
SupplyChainCredentialValidator.FIRMWARE_VALID);
"Unknown exception caught during quote validation.");
SupportReferenceManifest sRim = null;
EventLogMeasurements eventLog = null;

View File

@ -31,12 +31,10 @@ import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@ -68,9 +66,9 @@ public class ReferenceManifestDetailsPageController
* Constructor providing the Page's display and routing specification.
*
* @param referenceManifestManager the reference manifest manager.
* @param referenceDigestManager the reference digest manager.
* @param referenceEventManager the reference event manager.
* @param certificateManager the certificate manager.
* @param referenceDigestManager the reference digest manager.
* @param referenceEventManager the reference event manager.
* @param certificateManager the certificate manager.
*/
@Autowired
public ReferenceManifestDetailsPageController(
@ -144,19 +142,20 @@ public class ReferenceManifestDetailsPageController
*
* @param uuid database reference for the requested RIM.
* @param referenceManifestManager the reference manifest manager.
* @param referenceDigestManager the reference digest manager.
* @param referenceEventManager the reference event manager.
* @param certificateManager the certificate manager.
* @param referenceDigestManager the reference digest manager.
* @param referenceEventManager the reference event manager.
* @param certificateManager the certificate manager.
* @return mapping of the RIM information from the database.
* @throws java.io.IOException error for reading file bytes.
* @throws NoSuchAlgorithmException If an unknown Algorithm is encountered.
* @throws CertificateException if a certificate doesn't parse.
*/
public static HashMap<String, Object> getRimDetailInfo(final UUID uuid,
final ReferenceManifestManager referenceManifestManager,
final ReferenceDigestManager referenceDigestManager,
final ReferenceEventManager referenceEventManager,
final CertificateManager certificateManager) throws IOException,
final ReferenceManifestManager referenceManifestManager,
final ReferenceDigestManager referenceDigestManager,
final ReferenceEventManager referenceEventManager,
final CertificateManager certificateManager)
throws IOException,
CertificateException, NoSuchAlgorithmException {
HashMap<String, Object> data = new HashMap<>();
@ -189,9 +188,9 @@ public class ReferenceManifestDetailsPageController
* This method takes the place of an entire class for a string builder.
* Gathers all information and returns it for displays.
*
* @param baseRim established ReferenceManifest Type.
* @param baseRim established ReferenceManifest Type.
* @param referenceManifestManager the reference manifest manager.
* @param certificateManager the certificate manager.
* @param certificateManager the certificate manager.
* @return mapping of the RIM information from the database.
* @throws java.io.IOException error for reading file bytes.
* @throws NoSuchAlgorithmException If an unknown Algorithm is encountered.
@ -282,6 +281,7 @@ public class ReferenceManifestDetailsPageController
}
// going to have to pull the filename and grab that from the DB
// to get the id to make the link
RIM_VALIDATOR.setRim(baseRim);
for (SwidResource swidRes : resources) {
if (support != null && swidRes.getHashValue()
.equalsIgnoreCase(support.getHexDecHash())) {
@ -303,29 +303,24 @@ public class ReferenceManifestDetailsPageController
data.put("pcrList", support.getExpectedPCRList());
}
RIM_VALIDATOR.validateXmlSignature(new ByteArrayInputStream(baseRim.getRimBytes()));
Set<CertificateAuthorityCredential> certificates =
CertificateAuthorityCredential.select(certificateManager)
.getCertificates();
//Report invalid signature unless RIM_VALIDATOR validates it and cert path is valid
data.put("signatureValid", false);
if (RIM_VALIDATOR.isSignatureValid()) {
for (CertificateAuthorityCredential cert : certificates) {
if (Arrays.equals(cert.getEncodedPublicKey(),
RIM_VALIDATOR.getPublicKey().getEncoded())) {
SupplyChainValidationServiceImpl scvsImpl =
new SupplyChainValidationServiceImpl(certificateManager);
KeyStore keystore = scvsImpl.getCaChain(cert);
X509Certificate signingCert = cert.getX509Certificate();
try {
if (SupplyChainCredentialValidator.verifyCertificate(signingCert,
keystore)) {
data.replace("signatureValid", true);
}
} catch (SupplyChainValidatorException e) {
LOGGER.error("Error verifying cert chain: " + e.getMessage());
for (CertificateAuthorityCredential cert : certificates) {
SupplyChainValidationServiceImpl scvsImpl =
new SupplyChainValidationServiceImpl(certificateManager);
KeyStore keystore = scvsImpl.getCaChain(cert);
if (RIM_VALIDATOR.validateXmlSignature(cert)) {
try {
if (SupplyChainCredentialValidator.verifyCertificate(
cert.getX509Certificate(), keystore)) {
data.replace("signatureValid", true);
break;
}
break;
} catch (SupplyChainValidatorException e) {
LOGGER.error("Error verifying cert chain: " + e.getMessage());
}
}
}
@ -347,7 +342,7 @@ public class ReferenceManifestDetailsPageController
* This method takes the place of an entire class for a string builder.
* Gathers all information and returns it for displays.
*
* @param support established ReferenceManifest Type.
* @param support established ReferenceManifest Type.
* @param referenceManifestManager the reference manifest manager.
* @return mapping of the RIM information from the database.
* @throws java.io.IOException error for reading file bytes.
@ -412,8 +407,8 @@ public class ReferenceManifestDetailsPageController
digestMap.put(tpe.getEventDigestStr(), tpe);
if (!support.isSwidSupplemental()
&& !tpe.eventCompare(
measurementsProcess.getEventByNumber(
tpe.getEventNumber()))) {
measurementsProcess.getEventByNumber(
tpe.getEventNumber()))) {
tpe.setError(true);
}
tpmPcrEvents.add(tpe);
@ -507,10 +502,10 @@ public class ReferenceManifestDetailsPageController
* This method takes the place of an entire class for a string builder.
* Gathers all information and returns it for displays.
*
* @param measurements established ReferenceManifest Type.
* @param measurements established ReferenceManifest Type.
* @param referenceManifestManager the reference manifest manager.
* @param referenceDigestManager the reference digest manager.
* @param referenceEventManager the reference event manager.
* @param referenceDigestManager the reference digest manager.
* @param referenceEventManager the reference event manager.
* @return mapping of the RIM information from the database.
* @throws java.io.IOException error for reading file bytes.
* @throws NoSuchAlgorithmException If an unknown Algorithm is encountered.

View File

@ -30,6 +30,7 @@ public final class PciIds {
{
add("/usr/share/hwdata/pci.ids");
add("/usr/share/misc/pci.ids");
add("/tmp/pci.ids");
}
});

View File

@ -151,6 +151,8 @@ namespace string_utils {
std::string trimWhitespaceFromRight(std::string str);
std::string trimWhitespaceFromBothEnds(std::string str);
std::vector<std::string> split(const std::string& str, char delim);
} // namespace string_utils
} // namespace hirs

View File

@ -79,6 +79,7 @@ message TpmQuote {
message IdentityClaimResponse {
required bytes credential_blob = 1;
optional string pcr_mask = 2;
}
message CertificateRequest {

View File

@ -81,18 +81,25 @@ string RestfulClientProvisioner::sendIdentityClaim(
}
IdentityClaimResponse response;
response.ParseFromString(r.text);
{
// Convert the nonce blob to hex for logging
string blobHex = binaryToHex(response.credential_blob());
stringstream logStream;
logStream << "Received nonce blob: " << blobHex;
LOGGER.info(logStream.str());
try {
response.ParseFromString(r.text);
} catch (const google::protobuf::FatalException& e) {
LOGGER.error(e.what());
stringstream errormsg;
errormsg << "Provisioning failed. IdentityClaimResponse "
<< "did not contain credential_blob.";
throw HirsRuntimeException(errormsg.str(),
"RestfulClientProvisioner::sendIdentityClaim");
}
// Return the wrapped nonce blob
return response.credential_blob();
// Convert the nonce blob to hex for logging
string blobHex = binaryToHex(response.credential_blob());
stringstream logStream;
logStream << "Received nonce blob: " << blobHex;
LOGGER.info(logStream.str());
// Return the response
return response.SerializeAsString();
} else {
stringstream errormsg;

View File

@ -39,6 +39,7 @@ using std::cerr;
using std::endl;
using std::string;
using std::stringstream;
using std::vector;
int provision() {
Logger logger = Logger::getDefaultLogger();
@ -67,7 +68,10 @@ int provision() {
// if platformCredential is empty, not in TPM
// pull from properties file
if (platformCredential.empty()) {
const std::string& cert_dir = props.get("tcg.cert.dir", "");
const std::string& cert_dir =
props.get(
"tcg.cert.dir",
"/boot/tcg/cert/platform/");
try {
platformCredentials =
hirs::file_utils::search_directory(cert_dir);
@ -85,9 +89,18 @@ int provision() {
// collect TCG Boot files
std::vector<string> rim_files;
std::vector<string> swidtag_files;
const std::string& rim_dir = props.get("tcg.rim.dir", "");
const std::string& swid_dir = props.get("tcg.swidtag.dir", "");
const std::string& live_log_file = props.get("tcg.event.file", "");
const std::string& rim_dir =
props.get(
"tcg.rim.dir",
"/boot/tcg/manifest/rim/");
const std::string& swid_dir =
props.get(
"tcg.swidtag.dir",
"/boot/tcg/manifest/swidtag/");
const std::string& live_log_file =
props.get(
"tcg.event.file",
"/sys/kernel/security/tpm0/binary_bios_measurements");
try {
rim_files = hirs::file_utils::search_directory(rim_dir);
@ -128,13 +141,16 @@ int provision() {
"TPM2_Provisioner.cpp", __LINE__);
identityClaim.set_paccoroutput(paccorOutputString);
RestfulClientProvisioner provisioner;
string nonceBlob = provisioner.sendIdentityClaim(identityClaim);
if (nonceBlob == "") {
string response = provisioner.sendIdentityClaim(identityClaim);
hirs::pb::IdentityClaimResponse icr;
if (!icr.ParseFromString(response) || !icr.has_credential_blob()) {
cout << "----> Provisioning failed." << endl;
cout << "Please refer to the Attestation CA for details." << endl;
cout << "The ACA did not send make credential information." << endl;
return 0;
}
string nonceBlob = icr.credential_blob();
// activateIdentity requires we read makeCredential output from a file
cout << "----> Received response. Attempting to decrypt nonce" << endl;
try {
@ -152,9 +168,10 @@ int provision() {
hirs::pb::CertificateRequest certificateRequest;
certificateRequest.set_nonce(decryptedNonce);
certificateRequest.set_quote(tpm2.getQuote(
"0,1,2,3,4,5,6,7,8,9,10,11,12,13,"
"14,15,16,17,18,19,20,21,22,23",
decryptedNonce));
icr.has_pcr_mask()
? icr.pcr_mask()
: "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23",
decryptedNonce));
const string& akCertificateByteString
= provisioner.sendAttestationCertificateRequest(certificateRequest);

View File

@ -293,6 +293,18 @@ namespace string_utils {
return trimWhitespaceFromRight(trimWhitespaceFromLeft(str));
}
vector<string> split(const string &str, char delim) {
vector<string> result;
stringstream ss(str);
string item;
while (getline(ss, item, delim)) {
result.push_back(item);
}
return result;
}
} // namespace string_utils
} // namespace hirs

View File

@ -39,6 +39,7 @@ dependencies {
compile libs.joda_time
compile libs.log4j2
compile libs.mariadb
compile libs.pci_ids
compile libs.reflections
compile libs.guava
compile libs.spring_core
@ -48,6 +49,7 @@ dependencies {
exclude group: 'junit'
}
compile 'org.jboss.logging:jboss-logging:3.2.0.Final'
compile 'org.apache.commons:commons-text:1.9'
// add spring plugin, but do not pull transitive dependencies (causes conflicts)
compile(libs.spring_plugin) {

View File

@ -13,7 +13,7 @@ import javax.persistence.Column;
import javax.persistence.Entity;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@ -40,6 +40,7 @@ public final class PCRPolicy extends Policy {
private static final int TBOOT_PCR_END = 19;
// PCR 5
private static final int GPT_PCR = 5;
private static final int IMA_MASK = 0xfffbff;
// Event Log Event Types
private static final String EVT_EFI_BOOT = "EV_EFI_BOOT_SERVICES_APPLICATION";
@ -169,20 +170,29 @@ public final class PCRPolicy extends Policy {
boolean validated = false;
short localityAtRelease = 0;
String quoteString = new String(tpmQuote, StandardCharsets.UTF_8);
int pcrMaskSelection = PcrSelection.ALL_PCRS_ON;
if (enableIgnoreIma) {
pcrMaskSelection = IMA_MASK;
}
ArrayList<TPMMeasurementRecord> measurements = new ArrayList<>();
TPMMeasurementRecord[] measurements = new TPMMeasurementRecord[baselinePcrs.length];
try {
for (int i = 0; i <= TPMMeasurementRecord.MAX_PCR_ID; i++) {
measurements[i] = new TPMMeasurementRecord(i, storedPcrs[i]);
for (int i = 0; i < storedPcrs.length; i++) {
if (i == IMA_PCR && enableIgnoreIma) {
LOGGER.info("Ignore IMA PCR policy is enabled.");
} else {
measurements.add(new TPMMeasurementRecord(i, storedPcrs[i]));
}
}
} catch (DecoderException deEx) {
LOGGER.error(deEx);
}
PcrSelection pcrSelection = new PcrSelection(PcrSelection.ALL_PCRS_ON);
PcrSelection pcrSelection = new PcrSelection(pcrMaskSelection);
PcrComposite pcrComposite = new PcrComposite(
pcrSelection,
Arrays.asList(measurements));
pcrSelection, measurements);
PcrInfoShort pcrInfoShort = new PcrInfoShort(pcrSelection,
localityAtRelease,
tpmQuote, pcrComposite);
@ -197,6 +207,9 @@ public final class PCRPolicy extends Policy {
String calculatedString = Hex.encodeHexString(
pcrInfoShort.getCalculatedDigest());
validated = quoteString.contains(calculatedString);
if (!validated) {
LOGGER.warn(calculatedString + " not found in " + quoteString);
}
} catch (NoSuchAlgorithmException naEx) {
LOGGER.error(naEx);
}

View File

@ -15,7 +15,7 @@ import hirs.data.persist.TPMReport;
import hirs.data.persist.baseline.TpmBlackListBaseline;
import hirs.data.persist.baseline.TpmWhiteListBaseline;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.text.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

View File

@ -0,0 +1,194 @@
package hirs.utils;
import com.github.marandus.pciid.model.Device;
import com.github.marandus.pciid.model.Vendor;
import com.github.marandus.pciid.service.PciIdsDatabase;
import com.google.common.base.Strings;
import hirs.data.persist.certificate.attributes.ComponentIdentifier;
import hirs.data.persist.certificate.attributes.V2.ComponentIdentifierV2;
import org.bouncycastle.asn1.DERUTF8String;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.List;
import java.util.Vector;
/**
* Provide Java access to PCI IDs.
*/
public final class PciIds {
/**
* This pci ids file can be in different places on different distributions.
*/
public static final List<String> PCI_IDS_PATH =
Collections.unmodifiableList(new Vector<String>() {
private static final long serialVersionUID = 1L;
{
add("/usr/share/hwdata/pci.ids");
add("/usr/share/misc/pci.ids");
add("/tmp/pci.ids");
}
});
/**
* The PCI IDs Database object.
*
* This only needs to be loaded one time.
*
* The pci ids library protects the data inside the object by making it immutable.
*/
public static final PciIdsDatabase DB = new PciIdsDatabase();
static {
if (!DB.isReady()) {
String dbFile = null;
for (final String path : PCI_IDS_PATH) {
if ((new File(path)).exists()) {
dbFile = path;
break;
}
}
if (dbFile != null) {
InputStream is = null;
try {
is = new FileInputStream(new File(dbFile));
DB.loadStream(is);
} catch (IOException e) {
// DB will not be ready, hardware IDs will not be translated
dbFile = null;
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
dbFile = null;
}
}
}
}
}
}
/**
* Utility class.
*/
private PciIds() {
}
/**
* The Component Class TCG Registry OID.
*/
public static final String COMPCLASS_TCG_OID = "2.23.133.18.3.1";
/**
* The Component Class Value mask for NICs.
*/
public static final String COMPCLASS_TCG_CAT_NIC = "00090000";
/**
* The Component Class Value mask for GFX cards.
*/
public static final String COMPCLASS_TCG_CAT_GFX = "00050000";
/**
* Iterate through all components and translate PCI hardware IDs as necessary. It will only
* translate ComponentIdentifierV2+ objects as it relies on Component Class information.
* @param components List of ComponentIdentifiers.
* @return the translated list of ComponentIdentifiers.
*/
public static List<ComponentIdentifier> translate(
final List<ComponentIdentifier> components) {
Vector<ComponentIdentifier> newList = new Vector<ComponentIdentifier>();
if (components != null && !components.isEmpty()) {
for (final ComponentIdentifier component : components) {
// V2 components should not be found alongside V1 components
// they pass through just in case
if (component.isVersion2()) {
newList.add(translate((ComponentIdentifierV2) component));
} else {
newList.add(component);
}
}
}
return newList;
}
/**
* Translate Vendor and Device IDs, if found, in ComponentIdentifierV2 objects.
* It will only translate ID values, any other value will pass through.
* @param component ComponentIdentifierV2 object.
* @return the translated ComponentIdentifierV2 object.
*/
public static ComponentIdentifierV2 translate(final ComponentIdentifierV2 component) {
ComponentIdentifierV2 newComponent = null;
if (component != null) {
newComponent = component;
// This can be updated as we get more accurate component class registries and values
// Component Class Registry not accessible: TCG assumed
final String compClassValue = component.getComponentClass().getCategoryValue();
if (compClassValue.equals(COMPCLASS_TCG_CAT_NIC)
|| compClassValue.equals(COMPCLASS_TCG_CAT_GFX)) {
DERUTF8String manufacturer = translateVendor(component.getComponentManufacturer());
DERUTF8String model = translateDevice(component.getComponentManufacturer(),
component.getComponentModel());
newComponent = new ComponentIdentifierV2(component.getComponentClass(),
manufacturer,
model,
component.getComponentSerial(),
component.getComponentRevision(),
component.getComponentManufacturerId(),
component.getFieldReplaceable(),
component.getComponentAddress(),
component.getCertificateIdentifier(),
component.getComponentPlatformUri(),
component.getAttributeStatus());
}
}
return newComponent;
}
/**
* Look up the vendor name from the PCI IDs list, if the input string contains an ID.
* If any part of this fails, return the original manufacturer value.
* @param refManufacturer DERUTF8String, likely from a ComponentIdentifier
* @return DERUTF8String with the discovered vendor name, or the original manufacturer value.
*/
public static DERUTF8String translateVendor(final DERUTF8String refManufacturer) {
DERUTF8String manufacturer = refManufacturer;
if (manufacturer != null && manufacturer.getString().trim().matches("^[0-9A-Fa-f]{4}$")) {
Vendor ven = DB.findVendor(manufacturer.getString().toLowerCase());
if (ven != null && !Strings.isNullOrEmpty(ven.getName())) {
manufacturer = new DERUTF8String(ven.getName());
}
}
return manufacturer;
}
/**
* Look up the device name from the PCI IDs list, if the input strings contain IDs.
* The Device lookup requires the Vendor ID AND the Device ID to be valid values.
* If any part of this fails, return the original model value.
* @param refManufacturer DERUTF8String, likely from a ComponentIdentifier
* @param refModel DERUTF8String, likely from a ComponentIdentifier
* @return DERUTF8String with the discovered device name, or the original model value.
*/
public static DERUTF8String translateDevice(final DERUTF8String refManufacturer,
final DERUTF8String refModel) {
DERUTF8String manufacturer = refManufacturer;
DERUTF8String model = refModel;
if (manufacturer != null
&& model != null
&& manufacturer.getString().trim().matches("^[0-9A-Fa-f]{4}$")
&& model.getString().trim().matches("^[0-9A-Fa-f]{4}$")) {
Device dev = DB.findDevice(manufacturer.getString().toLowerCase(),
model.getString().toLowerCase());
if (dev != null && !Strings.isNullOrEmpty(dev.getName())) {
model = new DERUTF8String(dev.getName());
}
}
return model;
}
}

View File

@ -1,7 +1,11 @@
package hirs.utils;
import hirs.data.persist.ReferenceManifest;
import hirs.data.persist.certificate.CertificateAuthorityCredential;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
@ -23,7 +27,6 @@ import javax.xml.crypto.dsig.XMLSignatureException;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.dom.DOMValidateContext;
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
import javax.xml.crypto.dsig.keyinfo.KeyValue;
import javax.xml.crypto.dsig.keyinfo.X509Data;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
@ -34,14 +37,18 @@ import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.Key;
import java.security.KeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Iterator;
@ -52,7 +59,7 @@ import java.util.Iterator;
*/
public class ReferenceManifestValidator {
private static final String SIGNATURE_ALGORITHM_RSA_SHA256 =
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
private static final String SCHEMA_PACKAGE = "hirs.utils.xjc";
private static final String SCHEMA_URL = "swid_schema.xsd";
private static final String SCHEMA_LANGUAGE = XMLConstants.W3C_XML_SCHEMA_NS_URI;
@ -63,12 +70,29 @@ public class ReferenceManifestValidator {
private static final int RADIX = 16;
private static final Logger LOGGER = LogManager.getLogger(ReferenceManifestValidator.class);
private Document rim;
private Unmarshaller unmarshaller;
private PublicKey publicKey;
private Schema schema;
private String subjectKeyIdentifier;
private boolean signatureValid, supportRimValid;
/**
* Setter for the RIM to be validated. The ReferenceManifest object is converted into a
* Document for processing.
*
* @param rim ReferenceManifest object
*/
public void setRim(final ReferenceManifest rim) {
try {
Document doc = validateSwidtagSchema(removeXMLWhitespace(new StreamSource(
new ByteArrayInputStream(rim.getRimBytes()))));
this.rim = doc;
} catch (IOException e) {
LOGGER.error("Error while unmarshalling rim bytes: " + e.getMessage());
}
}
/**
* Getter for signatureValid.
*
@ -98,6 +122,7 @@ public class ReferenceManifestValidator {
/**
* Getter for subjectKeyIdentifier.
*
* @return subjectKeyIdentifier
*/
public String getSubjectKeyIdentifier() {
@ -114,62 +139,87 @@ public class ReferenceManifestValidator {
.getClassLoader().getResourceAsStream(SCHEMA_URL);
SchemaFactory schemaFactory = SchemaFactory.newInstance(SCHEMA_LANGUAGE);
schema = schemaFactory.newSchema(new StreamSource(is));
rim = null;
signatureValid = false;
supportRimValid = false;
publicKey = null;
subjectKeyIdentifier = "";
subjectKeyIdentifier = "(not found)";
} catch (SAXException e) {
LOGGER.warn("Error setting schema for validation!");
}
}
/**
* This constructor is used for a quick signature check which bypasses the loading of
* the schema url into memory. As a result the full stream is not validated against the schema.
* This method attempts to validate the signature element of the instance's RIM
* using a given cert. The cert is compared to either the RIM's embedded certificate
* or the RIM's subject key identifier. If the cert is matched then validation proceeds,
* otherwise validation ends.
*
* @param input xml data byte array.
* @param cert the cert to be checked against the RIM
* @return true if the signature element is validated, false otherwise
*/
public ReferenceManifestValidator(final InputStream input) {
@SuppressWarnings("magicnumber")
public boolean validateXmlSignature(final CertificateAuthorityCredential cert) {
DOMValidateContext context = null;
try {
signatureValid = validateSignedXMLDocument(
removeXMLWhitespace(new StreamSource(input)));
NodeList nodes = rim.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
if (nodes.getLength() == 0) {
LOGGER.error("Cannot validate RIM, signature element not found!");
return false;
}
NodeList certElement = rim.getElementsByTagName("X509Certificate");
if (certElement.getLength() > 0) {
X509Certificate embeddedCert = parseCertFromPEMString(
certElement.item(0).getTextContent());
if (embeddedCert != null) {
subjectKeyIdentifier = getCertificateSubjectKeyIdentifier(embeddedCert);
if (Arrays.equals(embeddedCert.getPublicKey().getEncoded(),
cert.getEncodedPublicKey())) {
context = new DOMValidateContext(new X509KeySelector(), nodes.item(0));
}
}
} else {
subjectKeyIdentifier = getKeyName(rim);
if (subjectKeyIdentifier.equals(cert.getSubjectKeyIdString().substring(8))) {
context = new DOMValidateContext(cert.getX509Certificate().getPublicKey(),
nodes.item(0));
}
}
if (context != null) {
publicKey = cert.getX509Certificate().getPublicKey();
signatureValid = validateSignedXMLDocument(context);
return signatureValid;
}
} catch (IOException e) {
LOGGER.warn("Error during unmarshal: " + e.getMessage());
LOGGER.warn("Error while parsing certificate data: " + e.getMessage());
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
/**
* This method calculates the SHA256 hash of the input byte array and compares it against
* the value passed in.
*
* @param input byte array to hash.
* @param input byte array to hash.
* @param expected value to compare against.
*/
public void validateSupportRimHash(final byte[] input, final String expected) {
String calculatedHash = getHashValue(input, SHA256);
LOGGER.info("Calculated hash: " + calculatedHash + ", actual: " + expected);
supportRimValid = calculatedHash.equals(expected);
}
/**
* This method validates the xml signature in the stream and stores the
* result for public access.
*
* @param input the xml data stream.
*/
public void validateXmlSignature(final InputStream input) {
try {
Document doc = validateSwidtagSchema(removeXMLWhitespace(new StreamSource(input)));
signatureValid = validateSignedXMLDocument(doc);
} catch (IOException e) {
LOGGER.warn("Error during unmarshal: " + e.getMessage());
if (!supportRimValid) {
LOGGER.info("Unmatched support RIM hash! Expected: " + expected
+ ", actual: " + calculatedHash);
}
}
/**
* This method calculates the digest of a byte array based on the hashing algorithm passed in.
*
* @param input byte array.
* @param sha hash algorithm.
* @param sha hash algorithm.
* @return String digest.
*/
private String getHashValue(final byte[] input, final String sha) {
@ -181,7 +231,7 @@ public class ReferenceManifestValidator {
for (int i = 0; i < bytes.length; i++) {
sb.append(Integer.toString((bytes[i] & EIGHT_BIT_MASK)
+ LEFT_SHIFT, RADIX).substring(1));
+ LEFT_SHIFT, RADIX).substring(1));
}
resultString = sb.toString();
} catch (NoSuchAlgorithmException grex) {
@ -191,37 +241,18 @@ public class ReferenceManifestValidator {
return resultString;
}
/**
* This method validates a Document with a signature element.
*
* @param doc
*/
private boolean validateSignedXMLDocument(final Document doc) {
DOMValidateContext context;
boolean isValid = false;
private boolean validateSignedXMLDocument(final DOMValidateContext context) {
try {
NodeList nodes = doc.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
if (nodes.getLength() == 0) {
throw new Exception("Signature element not found!");
}
X509KeySelector keySelector = new ReferenceManifestValidator.X509KeySelector();
context = new DOMValidateContext(keySelector, nodes.item(0));
XMLSignatureFactory sigFactory = XMLSignatureFactory.getInstance("DOM");
XMLSignature signature = sigFactory.unmarshalXMLSignature(context);
isValid = signature.validate(context);
publicKey = keySelector.getPublicKey();
subjectKeyIdentifier = getKeyName(doc);
return signature.validate(context);
} catch (MarshalException e) {
LOGGER.warn("Error while unmarshalling XML signature: " + e.getMessage());
} catch (XMLSignatureException e) {
LOGGER.warn("Error while validating XML signature: " + e.getMessage());
} catch (KeySelectorException e) {
LOGGER.warn("Public key not found in XML signature: " + e.getMessage());
} catch (Exception e) {
LOGGER.warn(e.getMessage());
}
return isValid;
return false;
}
/**
@ -230,8 +261,6 @@ public class ReferenceManifestValidator {
* an XML signature.
*/
public static class X509KeySelector extends KeySelector {
private PublicKey publicKey;
/**
* This method selects a public key for validation.
* PKs are parsed preferentially from the following elements:
@ -240,10 +269,10 @@ public class ReferenceManifestValidator {
* The parsed PK is then verified based on the provided algorithm before
* being returned in a KeySelectorResult.
*
* @param keyinfo object containing the cert.
* @param purpose purpose.
* @param keyinfo object containing the cert.
* @param purpose purpose.
* @param algorithm algorithm.
* @param context XMLCryptoContext.
* @param context XMLCryptoContext.
* @return KeySelectorResult holding the PublicKey.
* @throws KeySelectorException exception.
*/
@ -251,7 +280,7 @@ public class ReferenceManifestValidator {
final KeySelector.Purpose purpose,
final AlgorithmMethod algorithm,
final XMLCryptoContext context)
throws KeySelectorException {
throws KeySelectorException {
Iterator keyinfoItr = keyinfo.getContent().iterator();
while (keyinfoItr.hasNext()) {
XMLStructure element = (XMLStructure) keyinfoItr.next();
@ -261,32 +290,23 @@ public class ReferenceManifestValidator {
while (dataItr.hasNext()) {
Object object = dataItr.next();
if (object instanceof X509Certificate) {
publicKey = ((X509Certificate) object).getPublicKey();
break;
final PublicKey publicKey = ((X509Certificate) object).getPublicKey();
if (areAlgorithmsEqual(algorithm.getAlgorithm(),
publicKey.getAlgorithm())) {
return new ReferenceManifestValidator.X509KeySelector
.RIMKeySelectorResult(publicKey);
}
}
}
} else if (element instanceof KeyValue) {
try {
publicKey = ((KeyValue) element).getPublicKey();
} catch (KeyException e) {
LOGGER.warn("KeyException thrown while getting PK from KeyValue: "
+ e.getMessage());
}
}
}
if (areAlgorithmsEqual(algorithm.getAlgorithm(),
publicKey.getAlgorithm())) {
return new ReferenceManifestValidator.X509KeySelector
.RIMKeySelectorResult(publicKey);
}
throw new KeySelectorException("No key found!");
}
/**
* This method checks if two strings refer to the same algorithm.
*
* @param uri string 1
* @param uri string 1
* @param name string 2
* @return true if equal, false if not
*/
@ -294,15 +314,6 @@ public class ReferenceManifestValidator {
return uri.equals(SIGNATURE_ALGORITHM_RSA_SHA256) && name.equalsIgnoreCase("RSA");
}
/**
* Getter for the public key that is parsed in the select() method above.
*
* @return PublicKey encoded in the X509 cert.
*/
public PublicKey getPublicKey() {
return publicKey;
}
/**
* This internal class creates a KeySelectorResult from the public key.
*/
@ -319,6 +330,54 @@ public class ReferenceManifestValidator {
}
}
/**
* This method extracts certificate bytes from a string. The bytes are assumed to be
* PEM format, and a header and footer are concatenated with the input string to
* facilitate proper parsing.
*
* @param pemString the input string
* @return an X509Certificate created from the string, or null
* @throws Exception if certificate cannot be successfully parsed
*/
private X509Certificate parseCertFromPEMString(final String pemString) throws Exception {
String certificateHeader = "-----BEGIN CERTIFICATE-----";
String certificateFooter = "-----END CERTIFICATE-----";
try {
CertificateFactory factory = CertificateFactory.getInstance("X.509");
InputStream inputStream = new ByteArrayInputStream((certificateHeader
+ System.lineSeparator()
+ pemString
+ System.lineSeparator()
+ certificateFooter).getBytes("UTF-8"));
return (X509Certificate) factory.generateCertificate(inputStream);
} catch (CertificateException e) {
LOGGER.warn("Error creating CertificateFactory instance: " + e.getMessage());
} catch (UnsupportedEncodingException e) {
LOGGER.warn("Error while parsing cert from PEM string: " + e.getMessage());
}
return null;
}
/**
* This method returns the subjectKeyIdentifier from a given X509Certificate.
*
* @param certificate the cert to pull the subjectKeyIdentifier from
* @return the String representation of the subjectKeyIdentifier
* @throws IOException
*/
private String getCertificateSubjectKeyIdentifier(final X509Certificate certificate)
throws IOException {
String decodedValue;
byte[] extension = certificate.getExtensionValue(Extension.subjectKeyIdentifier.getId());
if (extension != null && extension.length > 0) {
decodedValue = JcaX509ExtensionUtils.parseExtensionValue(extension).toString();
} else {
decodedValue = " "; //Unlikely that a proper X509Certificate does not have a skid
}
return decodedValue.substring(1); //Drop the # at the beginning of the string
}
/**
* This method parses the subject key identifier from the KeyName element of a signature.
*
@ -368,7 +427,7 @@ public class ReferenceManifestValidator {
TransformerFactory tf = TransformerFactory.newInstance();
Source identitySource = new StreamSource(
ReferenceManifestValidator.class.getClassLoader()
.getResourceAsStream(IDENTITY_TRANSFORM));
.getResourceAsStream(IDENTITY_TRANSFORM));
Document doc = null;
try {
Transformer transformer = tf.newTransformer(identitySource);

View File

@ -13,6 +13,7 @@ import hirs.data.persist.certificate.attributes.ComponentIdentifier;
import hirs.data.persist.certificate.attributes.V2.ComponentIdentifierV2;
import hirs.data.persist.info.ComponentInfo;
import hirs.data.persist.info.HardwareInfo;
import hirs.utils.PciIds;
import org.apache.commons.codec.Charsets;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
@ -91,7 +92,7 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
"Platform credential attributes validated";
/**
* AppraisalStatus message for a valid platform credential appraisal.
* AppraisalStatus message for a valid firmware appraisal.
*/
public static final String FIRMWARE_VALID = "Firmware validated";
@ -564,9 +565,15 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
resultMessage.append(unmatchedComponents);
// pass information of which ones failed in additionInfo
int counter = 0;
for (ComponentIdentifier ci : validPcComponents) {
counter++;
additionalInfo.append(String.format("%d;", ci.hashCode()));
}
if (counter > 0) {
additionalInfo.insert(0, "COMPID=");
additionalInfo.append(counter);
}
}
passesValidation &= fieldValidation;
@ -697,6 +704,7 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
}
if (!fieldValidation || !deltaSb.toString().isEmpty()) {
deltaSb.insert(0, "COMPID=");
return new AppraisalStatus(FAIL, resultMessage.toString(), deltaSb.toString());
}
@ -716,21 +724,29 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
LOGGER.error("PACCOR output string:\n" + paccorOutputString);
return new AppraisalStatus(ERROR, baseErrorMessage + ioEx.getMessage());
}
StringBuilder additionalInfo = new StringBuilder();
if (!fieldValidation) {
// instead of listing all unmatched, just print the #. The failure
// will link to the platform certificate that'll display them.
String failureResults = unmatchedComponents.substring(0,
unmatchedComponents.length() - 1);
String size = unmatchedComponents.substring(unmatchedComponents.length() - 1);
resultMessage = new StringBuilder();
resultMessage.append(String.format("There are %s unmatched components "
+ "on the Platform Certificate:%n", size));
resultMessage.append("There are unmatched components:\n");
resultMessage.append(unmatchedComponents);
return new AppraisalStatus(FAIL, resultMessage.toString(), failureResults);
// pass information of which ones failed in additionInfo
int counter = 0;
for (ComponentIdentifier ci : baseCompList) {
counter++;
additionalInfo.append(String.format("%d;", ci.hashCode()));
}
if (counter > 0) {
additionalInfo.insert(0, "COMPID=");
additionalInfo.append(counter);
}
}
if (fieldValidation) {
return new AppraisalStatus(PASS, PLATFORM_ATTRIBUTES_VALID);
} else {
return new AppraisalStatus(FAIL, resultMessage.toString(), additionalInfo.toString());
}
return new AppraisalStatus(PASS, PLATFORM_ATTRIBUTES_VALID);
}
private static String validateV2PlatformCredentialAttributes(
@ -762,14 +778,23 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
// now we return everything that was unmatched
// what is in the component info/device reported components
// is to be displayed as the failure
fullDeltaChainComponents.clear();
for (ComponentIdentifier ci : subCompIdList) {
ciV2 = (ComponentIdentifierV2) ci;
invalidPcIds.append(String.format("%d;",
ciV2.hashCode()));
if (ci.isVersion2() && PciIds.DB.isReady()) {
ci = PciIds.translate((ComponentIdentifierV2) ci);
}
LOGGER.error("Unmatched component: " + ci);
fullDeltaChainComponents.add(ci);
invalidPcIds.append(String.format(
"Manufacturer=%s, Model=%s, Serial=%s, Revision=%s;%n",
ci.getComponentManufacturer(),
ci.getComponentModel(),
ci.getComponentSerial(),
ci.getComponentRevision()));
}
}
return String.format("COMPID=%s%d", invalidPcIds.toString(), subCompIdList.size());
return invalidPcIds.toString();
}
/**
@ -794,24 +819,23 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
// on the leftovers in the lists and the policy in place.
final List<ComponentIdentifier> pcComponents = new ArrayList<>();
for (ComponentIdentifier component : untrimmedPcComponents) {
DERUTF8String componentSerial = new DERUTF8String("");
DERUTF8String componentRevision = new DERUTF8String("");
if (component.getComponentManufacturer() != null) {
component.setComponentManufacturer(new DERUTF8String(
component.getComponentManufacturer().getString().trim()));
}
if (component.getComponentModel() != null) {
component.setComponentModel(new DERUTF8String(
component.getComponentModel().getString().trim()));
}
if (component.getComponentSerial() != null) {
componentSerial = new DERUTF8String(
component.getComponentSerial().getString().trim());
component.setComponentSerial(new DERUTF8String(
component.getComponentSerial().getString().trim()));
}
if (component.getComponentRevision() != null) {
componentRevision = new DERUTF8String(
component.getComponentRevision().getString().trim());
component.setComponentRevision(new DERUTF8String(
component.getComponentRevision().getString().trim()));
}
pcComponents.add(
new ComponentIdentifier(
new DERUTF8String(component.getComponentManufacturer().getString().trim()),
new DERUTF8String(component.getComponentModel().getString().trim()),
componentSerial, componentRevision,
component.getComponentManufacturerId(),
component.getFieldReplaceable(),
component.getComponentAddress()));
pcComponents.add(component);
}
LOGGER.info("Validating the following Platform Cert components...");
@ -835,7 +859,6 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
= allDeviceInfoComponents.stream().filter(componentInfo
-> componentInfo.getComponentManufacturer().equals(pcManufacturer))
.collect(Collectors.toList());
// For each component listed in the platform credential from this manufacturer
// find the ones that specify a serial number so we can match the most specific ones
// first.
@ -844,7 +867,6 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
-> compIdentifier.getComponentSerial() != null
&& StringUtils.isNotEmpty(compIdentifier.getComponentSerial().getString()))
.collect(Collectors.toList());
// Now match up the components from the device info that are from the same
// manufacturer and have a serial number. As matches are found, remove them from
// both lists.
@ -865,7 +887,6 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
}
}
}
// For each component listed in the platform credential from this manufacturer
// find the ones that specify value for the revision field so we can match the most
// specific ones first.
@ -874,7 +895,6 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
-> compIdentifier.getComponentRevision() != null
&& StringUtils.isNotEmpty(compIdentifier.getComponentRevision().getString()))
.collect(Collectors.toList());
// Now match up the components from the device info that are from the same
// manufacturer and specify a value for the revision field. As matches are found,
// remove them from both lists.
@ -895,7 +915,6 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
}
}
}
// The remaining components from the manufacturer have only the 2 required fields so
// just match them.
List<ComponentIdentifier> templist = new ArrayList<>(pcComponentsFromManufacturer);
@ -921,6 +940,10 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
int unmatchedComponentCounter = 1;
for (ComponentIdentifier unmatchedComponent : pcUnmatchedComponents) {
if (unmatchedComponent.isVersion2() && PciIds.DB.isReady()) {
unmatchedComponent =
PciIds.translate((ComponentIdentifierV2) unmatchedComponent);
}
LOGGER.error("Unmatched component " + unmatchedComponentCounter++ + ": "
+ unmatchedComponent);
sb.append(String.format("Manufacturer=%s, Model=%s, Serial=%s, Revision=%s;%n",
@ -1432,7 +1455,7 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
}
} while (foundRootOfCertChain.equals(intCAError));
LOGGER.error(foundRootOfCertChain);
LOGGER.warn(foundRootOfCertChain);
return foundRootOfCertChain;
}
@ -1660,11 +1683,19 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
try {
cert.verify(signingCert.getPublicKey(), BouncyCastleProvider.PROVIDER_NAME);
return true;
} catch (InvalidKeyException | CertificateException | NoSuchAlgorithmException
| NoSuchProviderException | SignatureException e) {
LOGGER.error("Exception thrown while verifying certificate", e);
return false;
} catch (InvalidKeyException e) {
LOGGER.info("Incorrect key given to validate this cert's signature");
} catch (CertificateException e) {
LOGGER.info("Encoding error while validating this cert's signature");
} catch (NoSuchAlgorithmException e) {
LOGGER.info("Unsupported signature algorithm found during validation");
} catch (NoSuchProviderException e) {
LOGGER.info("Incorrect provider for cert signature validation");
} catch (SignatureException e) {
LOGGER.info(String.format("%s.verify(%s)", cert.getSubjectDN(),
signingCert.getSubjectDN()));
}
return false;
}
@ -1708,7 +1739,9 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
new JcaContentVerifierProviderBuilder().setProvider("BC").build(signingKey);
return cert.isSignatureValid(contentVerifierProvider);
} catch (OperatorCreationException | CertException e) {
LOGGER.error("Exception thrown while verifying certificate", e);
LOGGER.info("Exception thrown while verifying certificate", e);
LOGGER.info(String.format("%s.isSignatureValid(%s)", cert.getSerialNumber(),
signingKey.getFormat()));
return false;
}
}

View File

@ -2256,9 +2256,7 @@ public class SupplyChainCredentialValidatorTest {
.validateDeltaPlatformCredentialAttributes(delta1,
deviceInfoReport, base, chainCredentials);
Assert.assertEquals(result.getAppStatus(), AppraisalStatus.Status.FAIL);
Assert.assertEquals(result.getMessage(),
"There are 1 unmatched components on the Platform Certificate:\n"
+ "COMPID=370101885;1");
Assert.assertEquals(result.getAdditionalInfo(), "COMPID=370101885;1");
}
/**