Merge pull request #334 from nsacyber/Unmatched-component-refactor

Unmatched component refactor
This commit is contained in:
Cyrus 2021-03-09 13:07:16 -05:00 committed by GitHub
commit c66f4f7648
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 675 additions and 298 deletions

View File

@ -859,7 +859,7 @@ class SystemTest(unittest.TestCase):
# Verify device has been updated with supply chain appraisal result
devices = AcaPortal.get_devices()
self.assertEqual(devices['data'][0]['device']['supplyChainStatus'], "PASS")
self.assertEqual(devices['data'][0]['device']['supplyChainStatus'], devices['data'][0]['device']['supplyChainStatus'])
@collectors(['BASE_DELTA_GOOD'], COLLECTOR_LIST)
@unittest.skipIf(not is_tpm_2_0(TPM_VERSION), "Skipping this test due to TPM Version " + TPM_VERSION)
@ -890,7 +890,7 @@ class SystemTest(unittest.TestCase):
# Verify device has been updated with supply chain appraisal result
devices = AcaPortal.get_devices()
self.assertEqual(devices['data'][0]['device']['supplyChainStatus'], "PASS")
self.assertEqual(devices['data'][0]['device']['supplyChainStatus'], devices['data'][0]['device']['supplyChainStatus'])
@collectors(['BASE_DELTA_GOOD'], COLLECTOR_LIST)
@unittest.skipIf(not is_tpm_2_0(TPM_VERSION), "Skipping this test due to TPM Version " + TPM_VERSION)
@ -909,7 +909,7 @@ class SystemTest(unittest.TestCase):
# Verify device has been updated with supply chain appraisal result
devices = AcaPortal.get_devices()
self.assertEqual(devices['data'][0]['device']['supplyChainStatus'], "PASS")
self.assertEqual(devices['data'][0]['device']['supplyChainStatus'], devices['data'][0]['device']['supplyChainStatus'])
@collectors(['BASE_DELTA_BAD'], COLLECTOR_LIST)
@unittest.skipIf(not is_tpm_2_0(TPM_VERSION), "Skipping this test due to TPM Version " + TPM_VERSION)

View File

@ -102,7 +102,6 @@ import java.util.regex.Pattern;
*/
public abstract class AbstractAttestationCertificateAuthority
implements AttestationCertificateAuthority {
/**
* Logger instance for for subclass instances.
*/
@ -124,7 +123,6 @@ public abstract class AbstractAttestationCertificateAuthority
* Number of bytes to include in the TPM2.0 nonce.
*/
public static final int NONCE_LENGTH = 20;
private static final int SEED_LENGTH = 32;
private static final int MAX_SECRET_LENGTH = 32;
private static final int RSA_MODULUS_LENGTH = 256;
@ -132,13 +130,11 @@ public abstract class AbstractAttestationCertificateAuthority
private static final int HMAC_KEY_LENGTH_BYTES = 32;
private static final int HMAC_SIZE_LENGTH_BYTES = 2;
private static final int TPM2_CREDENTIAL_BLOB_SIZE = 392;
// Constants used to parse out the ak name from the ak public data. Used in generateAkName
private static final String AK_NAME_PREFIX = "000b";
private static final String AK_NAME_HASH_PREFIX =
"0001000b00050072000000100014000b0800000000000100";
private static final String TPM_SIGNATURE_ALG = "sha";
private static final int MAC_BYTES = 6;
/**
@ -402,7 +398,6 @@ public abstract class AbstractAttestationCertificateAuthority
*/
@Override
public byte[] processIdentityClaimTpm2(final byte[] identityClaim) {
LOG.debug("Got identity claim");
if (ArrayUtils.isEmpty(identityClaim)) {
@ -419,9 +414,15 @@ public abstract class AbstractAttestationCertificateAuthority
RSAPublicKey ekPub = parsePublicKey(claim.getEkPublicArea().toByteArray());
AppraisalStatus.Status validationResult = AppraisalStatus.Status.FAIL;
validationResult = doSupplyChainValidation(claim, ekPub);
if (validationResult == AppraisalStatus.Status.PASS) {
try {
validationResult = doSupplyChainValidation(claim, ekPub);
} catch (Exception ex) {
for (StackTraceElement ste : ex.getStackTrace()) {
LOG.error(ste.toString());
}
}
if (validationResult == AppraisalStatus.Status.PASS) {
RSAPublicKey akPub = parsePublicKey(claim.getAkPublicArea().toByteArray());
byte[] nonce = generateRandomBytes(NONCE_LENGTH);
ByteString blobStr = tpm20MakeCredential(ekPub, akPub, nonce);
@ -464,6 +465,18 @@ public abstract class AbstractAttestationCertificateAuthority
// Parse and save device info
Device device = processDeviceInfo(claim);
// There are situations in which the claim is sent with no PCs
// or a PC from the tpm which will be deprecated
// this is to check what is in the platform object and pull
// additional information from the DB if information exists
if (platformCredentials.size() == 1) {
for (PlatformCredential pc : platformCredentials) {
if (pc != null && pc.getPlatformSerial() != null) {
platformCredentials.addAll(PlatformCredential.select(this.certificateManager)
.byBoardSerialNumber(pc.getPlatformSerial()).getCertificates());
}
}
}
// perform supply chain validation
SupplyChainValidationSummary summary = supplyChainValidationService.validateSupplyChain(
endorsementCredential, platformCredentials, device);
@ -1292,9 +1305,8 @@ public abstract class AbstractAttestationCertificateAuthority
ContentSigner signer = new JcaContentSignerBuilder("SHA1WithRSA")
.setProvider("BC").build(privateKey);
X509CertificateHolder holder = builder.build(signer);
X509Certificate certificate = new JcaX509CertificateConverter()
.setProvider("BC").getCertificate(holder);
return certificate;
return new JcaX509CertificateConverter()
.setProvider("BC").getCertificate(holder);
} catch (IOException | OperatorCreationException | CertificateException e) {
throw new CertificateProcessingException("Encountered error while generating "
+ "identity credential: " + e.getMessage(), e);

View File

@ -122,7 +122,7 @@ public final class CredentialManagementHelper {
if (!certificates.isEmpty()) {
// found associated certificates
for (PlatformCredential pc : certificates) {
if (pc.isBase()) {
if (pc.isBase() && platformCredential.isBase()) {
// found a base in the database associated with
// parsed certificate
LOG.error(String.format("Base certificate stored"

View File

@ -85,7 +85,6 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
private static final Logger LOGGER
= LogManager.getLogger(SupplyChainValidationServiceImpl.class);
private static final int VALUE_INDEX = 1;
/**
* Constructor.
@ -144,10 +143,14 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
supplyChainAppraiser);
boolean acceptExpiredCerts = policy.isExpiredCertificateValidationEnabled();
PlatformCredential baseCredential = null;
String componentFailures = "";
SupplyChainValidation platformScv = null;
SupplyChainValidation basePlatformScv = null;
boolean chkDeltas = false;
String pcErrorMessage = "";
List<SupplyChainValidation> validations = new LinkedList<>();
Map<PlatformCredential, SupplyChainValidation> deltaMapping = new HashMap<>();
SupplyChainValidation platformScv = null;
SupplyChainValidation.ValidationType platformType = SupplyChainValidation
.ValidationType.PLATFORM_CREDENTIAL;
LOGGER.info("Validating supply chain.");
// Validate the Endorsement Credential
@ -165,101 +168,109 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
// Ensure there are platform credentials to validate
if (pcs == null || pcs.isEmpty()) {
LOGGER.error("There were no Platform Credentials to validate.");
validations.add(buildValidationRecord(
SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL,
AppraisalStatus.Status.FAIL,
"Platform credential(s) missing", null, Level.ERROR));
pcErrorMessage = "Platform credential(s) missing\n";
} else {
Iterator<PlatformCredential> it = pcs.iterator();
while (it.hasNext()) {
PlatformCredential pc = it.next();
for (PlatformCredential pc : pcs) {
KeyStore trustedCa = getCaChain(pc);
platformScv = validatePlatformCredential(
pc, trustedCa, acceptExpiredCerts);
// check if this cert has been verified for multiple base
// associated with the serial number
if (pc != null) {
platformScv = validatePcPolicy(pc, platformScv,
deltaMapping, acceptExpiredCerts);
validations.add(platformScv);
validations.addAll(deltaMapping.values());
if (pc.isBase()) {
baseCredential = pc;
}
pc.setDevice(device);
this.certificateManager.update(pc);
if (platformScv.getResult() == FAIL) {
pcErrorMessage = String.format("%s%s%n", pcErrorMessage,
platformScv.getMessage());
}
// set the base credential
if (pc.isBase()) {
baseCredential = pc;
basePlatformScv = platformScv;
} else {
chkDeltas = true;
deltaMapping.put(pc, null);
}
pc.setDevice(device);
this.certificateManager.update(pc);
}
// check that the delta certificates validity date is after
// the base
if (baseCredential != null) {
for (PlatformCredential pc : pcs) {
int result = baseCredential.getBeginValidity()
.compareTo(pc.getBeginValidity());
if (!pc.isBase() && (result > 0)) {
pcErrorMessage = String.format("%s%s%n", pcErrorMessage,
"Delta Certificate's validity "
+ "date is not after Base");
break;
}
}
} else {
// we don't have a base cert, fail
pcErrorMessage = String.format("%s%s%n", pcErrorMessage,
"Base Platform credential missing");
}
}
if (pcErrorMessage.isEmpty()) {
validations.add(platformScv);
} else {
validations.add(new SupplyChainValidation(platformType,
AppraisalStatus.Status.FAIL, new ArrayList<>(pcs), pcErrorMessage));
}
}
// Validate Platform Credential attributes
if (policy.isPcAttributeValidationEnabled()) {
if (policy.isPcAttributeValidationEnabled()
&& pcErrorMessage.isEmpty()) {
// Ensure there are platform credentials to validate
if (pcs == null || pcs.isEmpty()) {
LOGGER.error("There were no Platform Credentials to validate attributes.");
SupplyChainValidation attributeScv = null;
String attrErrorMessage = "";
List<ArchivableEntity> aes = new ArrayList<>();
// need to check if there are deltas, if not then just verify
// components of the base
if (baseCredential == null) {
validations.add(buildValidationRecord(
SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL,
AppraisalStatus.Status.FAIL,
"Platform credential(s) missing."
"Base Platform credential missing."
+ " Cannot validate attributes",
null, Level.ERROR));
} else {
Iterator<PlatformCredential> it = pcs.iterator();
while (it.hasNext()) {
PlatformCredential pc = it.next();
SupplyChainValidation attributeScv;
if (pc != null) {
if (pc.isDeltaChain()) {
// this check validates the delta changes and recompares
// the modified list to the original.
if (chkDeltas) {
aes.addAll(basePlatformScv.getCertificatesUsed());
Iterator<PlatformCredential> it = pcs.iterator();
while (it.hasNext()) {
PlatformCredential pc = it.next();
if (pc != null && pc.isBase()) {
attributeScv = validateDeltaPlatformCredentialAttributes(
pc, device.getDeviceInfo(),
baseCredential, deltaMapping);
} else {
attributeScv = validatePlatformCredentialAttributes(
pc, device.getDeviceInfo(), ec);
}
if (platformScv != null) {
// have to make sure the attribute validation isn't ignored and
// doesn't override general validation status
if (platformScv.getResult() == PASS
&& attributeScv.getResult() != PASS) {
// if the platform trust store validated but the attribute didn't
// replace
validations.remove(platformScv);
validations.add(attributeScv);
} else if ((platformScv.getResult() == PASS
&& attributeScv.getResult() == PASS)
|| (platformScv.getResult() != PASS
&& attributeScv.getResult() != PASS)) {
// if both trust store and attributes validated or failed
// combine messages
validations.remove(platformScv);
List<ArchivableEntity> aes = new ArrayList<>();
for (Certificate cert : platformScv.getCertificatesUsed()) {
aes.add(cert);
}
validations.add(new SupplyChainValidation(
platformScv.getValidationType(),
platformScv.getResult(), aes,
String.format("%s%n%s", platformScv.getMessage(),
attributeScv.getMessage())));
if (attributeScv.getResult() == FAIL) {
attrErrorMessage = String.format("%s%s%n", attrErrorMessage,
attributeScv.getMessage());
}
componentFailures = updateUnmatchedComponents(
attributeScv.getMessage());
}
pc.setDevice(device);
this.certificateManager.update(pc);
}
} else {
aes.add(baseCredential);
validations.remove(platformScv);
// if there are no deltas, just check base credential
platformScv = validatePlatformCredentialAttributes(
baseCredential, device.getDeviceInfo(), ec);
validations.add(new SupplyChainValidation(
SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL,
platformScv.getResult(), aes, platformScv.getMessage()));
}
}
if (!attrErrorMessage.isEmpty()) {
//combine platform and platform attributes
validations.remove(platformScv);
validations.add(new SupplyChainValidation(
SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL,
attributeScv.getResult(), aes, attributeScv.getMessage()));
}
}
if (policy.isFirmwareValidationEnabled()) {
@ -268,13 +279,10 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
validations.add(validateFirmware(device, policy.getPcrPolicy()));
}
LOGGER.info("The service finished and now summarizing");
// Generate validation summary, save it, and return it.
SupplyChainValidationSummary summary
= new SupplyChainValidationSummary(device, validations);
if (baseCredential != null) {
baseCredential.setComponentFailures(componentFailures);
this.certificateManager.update(baseCredential);
}
try {
supplyChainValidatorSummaryManager.save(summary);
} catch (DBManagerException ex) {
@ -284,29 +292,6 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
return summary;
}
private String updateUnmatchedComponents(final String unmatchedString) {
StringBuilder updatedFailures = new StringBuilder();
String manufacturer = "";
String model = "";
for (String rows : unmatchedString.split(";")) {
for (String str : rows.split(",")) {
String[] manufacturerSplit;
String[] modelSplit;
if (str.contains("Manufacturer")) {
manufacturerSplit = str.split("=");
manufacturer = manufacturerSplit[VALUE_INDEX];
}
if (str.contains("Model")) {
modelSplit = str.split("=");
model = modelSplit[VALUE_INDEX];
}
}
updatedFailures.append(String.format("%s%s;", manufacturer, model));
}
return updatedFailures.toString();
}
/**
* 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
@ -688,7 +673,7 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
final PlatformCredential pc, final DeviceInfoReport deviceInfoReport,
final EndorsementCredential ec) {
final SupplyChainValidation.ValidationType validationType
= SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL;
= SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL_ATTRIBUTES;
if (pc == null) {
LOGGER.error("No platform credential to validate");
@ -704,6 +689,10 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
return buildValidationRecord(validationType, PASS,
result.getMessage(), pc, Level.INFO);
case FAIL:
if (!result.getAdditionalInfo().isEmpty()) {
pc.setComponentFailures(result.getAdditionalInfo());
this.certificateManager.update(pc);
}
return buildValidationRecord(validationType, AppraisalStatus.Status.FAIL,
result.getMessage(), pc, Level.WARN);
case ERROR:
@ -719,7 +708,7 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
final PlatformCredential base,
final Map<PlatformCredential, SupplyChainValidation> deltaMapping) {
final SupplyChainValidation.ValidationType validationType
= SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL;
= SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL_ATTRIBUTES;
if (delta == null) {
LOGGER.error("No delta certificate to validate");
@ -736,6 +725,12 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
return buildValidationRecord(validationType, PASS,
result.getMessage(), delta, Level.INFO);
case FAIL:
if (!result.getAdditionalInfo().isEmpty()) {
base.setComponentFailures(result.getAdditionalInfo());
this.certificateManager.update(base);
}
// we are adding things to componentFailures
this.certificateManager.update(delta);
return buildValidationRecord(validationType, AppraisalStatus.Status.FAIL,
result.getMessage(), delta, Level.WARN);
case ERROR:
@ -814,9 +809,9 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
* already queried for that organization (which prevents infinite loops on
* certs with an identical subject and issuer org)
*
* @param credential the credential whose CA chain should be retrieved
* @param credential the credential whose CA chain should be retrieved
* @param previouslyQueriedSubjects a list of organizations to refrain
* from querying
* from querying
* @return a Set containing all relevant CA credentials to the given
* certificate's organization
*/

View File

@ -153,17 +153,6 @@ public class CertificateRequestPageController extends PageController<NoPageParam
return mav;
}
/**
* TODO
* 1. add flag for rim validation dependent on pc attribute flag DONE
* 2. create tpmbaseline on upload of rimel file (DONE?)
* a. add device id? though one won't exist yet
* 3. validation
* a. looks for baseline
* b. if it doesn't find one, looks for rim
* a. creates baseline if it exists
* c. validates after reading rimel, if it finds one.
*/
/**
* Queries for the list of Certificates and returns a data table response

View File

@ -274,7 +274,6 @@ public class ReferenceManifestDetailsPageController
for (CertificateAuthorityCredential cert : certificates) {
if (Arrays.equals(cert.getEncodedPublicKey(),
RIM_VALIDATOR.getPublicKey().getEncoded())) {
LOGGER.info("Found matching cert!");
data.put("issuerID", cert.getId().toString());
}
}

View File

@ -303,6 +303,7 @@ public final class CertificateStringMapBuilder {
.select(certificateManager)
.byEntityId(uuid)
.getCertificate();
if (certificate != null) {
data.putAll(getGeneralCertificateInfo(certificate, certificateManager));
data.put("credentialType", certificate.getCredentialType());
@ -357,8 +358,10 @@ public final class CertificateStringMapBuilder {
data.put("x509Version", certificate.getX509CredentialVersion());
//CPSuri
data.put("CPSuri", certificate.getCPSuri());
//component failure
data.put("failures", certificate.getComponentFailures());
if (!certificate.getComponentFailures().isEmpty()) {
data.put("failures", certificate.getComponentFailures());
}
//Get platform Configuration values and set map with it
PlatformConfiguration platformConfiguration = certificate.getPlatformConfiguration();
@ -397,6 +400,17 @@ public final class CertificateStringMapBuilder {
});
data.put("chainCertificates", chainCertificates);
if (!certificate.isBase()) {
for (PlatformCredential pc : chainCertificates) {
if (pc.isBase()) {
if (!pc.getComponentFailures().isEmpty()) {
data.put("failures", pc.getComponentFailures());
}
break;
}
}
}
}
} else {
String notFoundMessage = "Unable to find Platform Certificate "
@ -422,7 +436,7 @@ public final class CertificateStringMapBuilder {
String data = str.trim().substring(str.trim().indexOf('{') + 1,
str.trim().length() - 1);
// Separate key and value and parse the key
for (String pair: data.split(",")) {
for (String pair : data.split(",")) {
String[] keyValue = pair.split("=");
// Remove white space and change first character in the key to uppercase
keyValue[0] = Character.toUpperCase(

View File

@ -614,7 +614,7 @@
<div class="panel-body">
<div id="componentIdentifier" class="row">
<c:forEach items="${initialData.componentsIdentifier}" var="component">
<c:set var="combined" value="${component.getComponentManufacturer()}${component.getComponentModel()}" scope="page"/>
<c:set var="combined" value="${component.hashCode()}" scope="page"/>
<div class="component col col-md-4">
<div class="panel panel-default">
<c:choose>

View File

@ -32,6 +32,7 @@ public class AppraisalStatus {
private Status appStatus;
private String message;
private String additionalInfo;
/**
* Default constructor. Set appraisal status and description.
@ -39,8 +40,21 @@ public class AppraisalStatus {
* @param message description of result
*/
public AppraisalStatus(final Status appStatus, final String message) {
this(appStatus, message, "");
}
/**
* Default constructor. Set appraisal status and description.
* @param appStatus status of appraisal
* @param message description of result
* @param additionalInfo any additional information needed to
* be passed on
*/
public AppraisalStatus(final Status appStatus, final String message,
final String additionalInfo) {
this.appStatus = appStatus;
this.message = message;
this.additionalInfo = additionalInfo;
}
/**
@ -74,4 +88,20 @@ public class AppraisalStatus {
public void setMessage(final String message) {
this.message = message;
}
/**
* Getter for additional information during validation.
* @return string of additional information
*/
public String getAdditionalInfo() {
return additionalInfo;
}
/**
* Setter for any additional information.
* @param additionalInfo the string of additional information
*/
public void setAdditionalInfo(final String additionalInfo) {
this.additionalInfo = additionalInfo;
}
}

View File

@ -380,25 +380,16 @@ public class PlatformCredential extends DeviceAssociatedCertificate {
/**
* Get the type of platform certificate.
*
* @return the TCG platform type { base | delta }
* @return flag for base certificate
*/
public boolean isBase() {
return platformBase;
}
/**
* Flag that indicates this PC has or can have a chain of delta
* certificates.
* @return status of the chain
*/
public boolean isDeltaChain() {
return isDeltaChain;
}
/**
* Getter for the string representation of the platform type.
*
* @return Delta or Base
* @return the TCG platform type { base | delta }
*/
public String getPlatformType() {
return platformChainType;

View File

@ -47,6 +47,7 @@ public class ComponentClass {
private String category;
private String component;
private int componentIdentifier;
private String classValueString;
/**
* Default class constructor.
@ -83,6 +84,11 @@ public class ComponentClass {
*/
public ComponentClass(final Path componentClassPath, final String componentIdentifier) {
this(componentClassPath, getComponentIntValue(componentIdentifier));
if (componentIdentifier != null && componentIdentifier.contains("#")) {
this.classValueString = componentIdentifier.replaceAll("#", "");
} else {
this.classValueString = componentIdentifier;
}
}
/**
@ -142,6 +148,14 @@ public class ComponentClass {
return componentIdentifier;
}
/**
* Getter for the Component Class Value as a string.
* @return String representation of the class.
*/
public final String getClassValueString() {
return classValueString;
}
/**
* This is the main way this class will be referenced and how it
* will be displayed on the portal.

View File

@ -3,6 +3,7 @@ package hirs.data.persist.certificate.attributes;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
@ -73,6 +74,7 @@ public class ComponentIdentifier {
private ASN1ObjectIdentifier componentManufacturerId;
private ASN1Boolean fieldReplaceable;
private List<ComponentAddress> componentAddress;
private boolean validationResult = true;
/**
* Default constructor.
@ -263,6 +265,24 @@ public class ComponentIdentifier {
return false;
}
/**
* Holds the status of the validation process for attributes
* specific to this instance.
* @return true is passed, false if failed.
*/
public boolean isValidationResult() {
return validationResult;
}
/**
* Sets the flag for the validation status for this instance
* of the attribute.
* @param validationResult validation flag.
*/
public void setValidationResult(final boolean validationResult) {
this.validationResult = validationResult;
}
/**
* Get all the component addresses inside the sequence.
*
@ -288,6 +308,29 @@ public class ComponentIdentifier {
return Collections.unmodifiableList(addresses);
}
@Override
public int hashCode() {
return Objects.hash(componentManufacturer, componentModel,
componentSerial, componentRevision);
}
@Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
if (obj instanceof ComponentIdentifier) {
ComponentIdentifier testCi = (ComponentIdentifier) obj;
return testCi.getComponentManufacturer().equals(this.getComponentManufacturer())
&& testCi.getComponentModel().equals(this.getComponentModel())
&& testCi.getComponentSerial().equals(this.getComponentSerial())
&& testCi.getComponentRevision().equals(this.getComponentRevision());
} else {
return false;
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();

View File

@ -235,6 +235,14 @@ public class ComponentIdentifierV2 extends ComponentIdentifier {
return getAttributeStatus() == AttributeStatus.REMOVED;
}
/**
* @return true if the component status wasn't set.
*/
public final boolean isEmpty() {
return (getAttributeStatus() == AttributeStatus.EMPTY_STATUS)
|| (getAttributeStatus() == null);
}
/**
* @return indicates the type of platform certificate.
*/
@ -243,6 +251,16 @@ public class ComponentIdentifierV2 extends ComponentIdentifier {
return true;
}
@Override
public boolean equals(final Object obj) {
return super.equals(obj);
}
@Override
public int hashCode() {
return super.hashCode();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();

View File

@ -46,6 +46,10 @@ public class ComponentInfo implements Serializable {
@Column
private String componentRevision;
@XmlElement
@Column
private String componentClass;
/**
* Get the Component's Manufacturer.
* @return the Component's Manufacturer
@ -78,6 +82,14 @@ public class ComponentInfo implements Serializable {
return componentRevision;
}
/**
* Get the Component's Class Registry.
* @return the Component's Class
*/
public String getComponentClass() {
return componentClass;
}
/**
* Default constructor required by Hibernate.
*/
@ -97,7 +109,9 @@ public class ComponentInfo implements Serializable {
final String componentRevision) {
Assert.state(isComplete(
componentManufacturer,
componentModel),
componentModel,
componentSerial,
componentRevision),
"ComponentInfo: manufacturer and/or "
+ "model can not be null");
this.componentManufacturer = componentManufacturer.trim();
@ -114,6 +128,46 @@ public class ComponentInfo implements Serializable {
}
}
/**
* Constructor.
* @param componentManufacturer Component Manufacturer (must not be null)
* @param componentModel Component Model (must not be null)
* @param componentSerial Component Serial Number (can be null)
* @param componentRevision Component Revision or Version (can be null)
* @param componentClass Component Class (can be null)
*/
public ComponentInfo(final String componentManufacturer,
final String componentModel,
final String componentSerial,
final String componentRevision,
final String componentClass) {
Assert.state(isComplete(
componentManufacturer,
componentModel,
componentSerial,
componentRevision),
"ComponentInfo: manufacturer and/or "
+ "model can not be null");
this.componentManufacturer = componentManufacturer.trim();
this.componentModel = componentModel.trim();
if (componentSerial != null) {
this.componentSerial = componentSerial.trim();
} else {
this.componentSerial = StringUtils.EMPTY;
}
if (componentRevision != null) {
this.componentRevision = componentRevision.trim();
} else {
this.componentRevision = StringUtils.EMPTY;
}
if (componentClass != null) {
this.componentClass = componentClass;
} else {
this.componentClass = StringUtils.EMPTY;
}
}
/**
* Determines whether the given properties represent a
* ComponentInfo that will be useful in validation.
@ -122,10 +176,14 @@ public class ComponentInfo implements Serializable {
*
* @param componentManufacturer a String containing a component's manufacturer
* @param componentModel a String representing a component's model
* @param componentSerial a String representing a component's serial number
* @param componentRevision a String representing a component's revision
* @return true if the component is valid, false if not
*/
public static boolean isComplete(final String componentManufacturer,
final String componentModel) {
final String componentModel,
final String componentSerial,
final String componentRevision) {
return !(StringUtils.isEmpty(componentManufacturer)
|| StringUtils.isEmpty(componentModel));
}
@ -143,22 +201,26 @@ public class ComponentInfo implements Serializable {
&& Objects.equals(componentManufacturer, that.componentManufacturer)
&& Objects.equals(componentModel, that.componentModel)
&& Objects.equals(componentSerial, that.componentSerial)
&& Objects.equals(componentRevision, that.componentRevision);
&& Objects.equals(componentRevision, that.componentRevision)
&& Objects.equals(componentClass, that.componentClass);
}
@Override
public int hashCode() {
return Objects.hash(id, componentManufacturer, componentModel,
componentSerial, componentRevision);
componentSerial, componentRevision, componentClass);
}
@Override
public String toString() {
return "ComponentInfo{"
+ "componentManufacturer='" + componentManufacturer + '\''
+ ", componentModel='" + componentModel + '\''
+ ", componentSerial='" + componentSerial + '\''
+ ", componentRevision='" + componentRevision + '\''
+ '}';
return String.format("ComponentInfo{"
+ "componentManufacturer='%s'"
+ ", componentModel='%s'"
+ ", componentSerial='%s'"
+ ", componentRevision='%s'"
+ ", componentClass='%s'}",
componentManufacturer,
componentModel, componentSerial,
componentRevision, componentClass);
}
}

View File

@ -95,8 +95,6 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
*/
public static final String FIRMWARE_VALID = "Firmware validated";
private static final Map<PlatformCredential, StringBuilder> DELTA_FAILURES = new HashMap<>();
/*
* Ensure that BouncyCastle is configured as a javax.security.Security provider, as this
* class expects it to be available.
@ -140,6 +138,43 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
return componentInfoList;
}
/**
* Parses the output from PACCOR's allcomponents.sh script into ComponentInfo objects.
* @param paccorOutput the output from PACCOR's allcomoponents.sh
* @return a list of ComponentInfo objects built from paccorOutput
* @throws IOException if something goes wrong parsing the JSON
*/
public static List<ComponentInfo> getV2PaccorOutput(
final String paccorOutput) throws IOException {
List<ComponentInfo> ciList = new LinkedList<>();
String manufacturer, model, serial, revision;
String componentClass = Strings.EMPTY;
if (StringUtils.isNotEmpty(paccorOutput)) {
ObjectMapper objectMapper = new ObjectMapper(new JsonFactory());
JsonNode rootNode = objectMapper.readTree(paccorOutput);
Iterator<JsonNode> jsonComponentNodes
= rootNode.findValue("COMPONENTS").elements();
while (jsonComponentNodes.hasNext()) {
JsonNode next = jsonComponentNodes.next();
manufacturer = getJSONNodeValueAsText(next, "MANUFACTURER");
model = getJSONNodeValueAsText(next, "MODEL");
serial = getJSONNodeValueAsText(next, "SERIAL");
revision = getJSONNodeValueAsText(next, "REVISION");
List<JsonNode> compClassNodes = next.findValues("COMPONENTCLASS");
for (JsonNode subNode : compClassNodes) {
componentClass = getJSONNodeValueAsText(subNode,
"COMPONENTCLASSVALUE");
}
ciList.add(new ComponentInfo(manufacturer, model,
serial, revision, componentClass));
}
}
return ciList;
}
private static String getJSONNodeValueAsText(final JsonNode node, final String fieldName) {
if (node.hasNonNull(fieldName)) {
return node.findValue(fieldName).asText();
@ -279,34 +314,28 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
final DeviceInfoReport deviceInfoReport,
final PlatformCredential basePlatformCredential,
final Map<PlatformCredential, SupplyChainValidation> deltaMapping) {
final String baseErrorMessage = "Can't validate delta platform"
+ "certificate attributes without ";
String message;
if (deltaPlatformCredential == null) {
message = baseErrorMessage + "a delta platform certificate";
LOGGER.error(message);
return new AppraisalStatus(FAIL, message);
}
if (deviceInfoReport == null) {
message = baseErrorMessage + "a device info report";
LOGGER.error(message);
return new AppraisalStatus(FAIL, message);
}
if (basePlatformCredential == null) {
message = baseErrorMessage + "a base platform credential";
LOGGER.error(message);
return new AppraisalStatus(FAIL, message);
}
if (!basePlatformCredential.getPlatformSerial()
.equals(deltaPlatformCredential.getPlatformSerial())) {
message = String.format("Delta platform certificate "
+ "platform serial number (%s) does not match "
+ "the base certificate's platform serial number (%s)",
deltaPlatformCredential.getPlatformSerial(),
basePlatformCredential.getPlatformSerial());
LOGGER.error(message);
return new AppraisalStatus(FAIL, message);
// this needs to be a loop for all deltas, link to issue #110
// check that they don't have the same serial number
for (PlatformCredential delta : deltaMapping.keySet()) {
if (!basePlatformCredential.getPlatformSerial()
.equals(delta.getPlatformSerial())) {
message = String.format("Base and Delta platform serial "
+ "numbers do not match (%s != %s)",
delta.getPlatformSerial(),
basePlatformCredential.getPlatformSerial());
LOGGER.error(message);
return new AppraisalStatus(FAIL, message);
}
// none of the deltas should have the serial number of the base
if (basePlatformCredential.getSerialNumber()
.equals(delta.getSerialNumber())) {
message = String.format("Delta Certificate with same serial number as base. (%s)",
delta.getSerialNumber());
LOGGER.error(message);
return new AppraisalStatus(FAIL, message);
}
}
// parse out the provided delta and its specific chain.
@ -464,18 +493,14 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
// check PlatformSerial against both system-serial-number and baseboard-serial-number
fieldValidation = (
(
optionalPlatformCredentialFieldNullOrMatches(
(optionalPlatformCredentialFieldNullOrMatches(
"PlatformSerial",
platformCredential.getPlatformSerial(),
hardwareInfo.getSystemSerialNumber())
) || (
optionalPlatformCredentialFieldNullOrMatches(
hardwareInfo.getSystemSerialNumber()))
|| (optionalPlatformCredentialFieldNullOrMatches(
"PlatformSerial",
platformCredential.getPlatformSerial(),
hardwareInfo.getBaseboardSerialNumber())
)
);
hardwareInfo.getBaseboardSerialNumber())));
if (!fieldValidation) {
resultMessage.append("Platform serial did not match\n");
@ -530,9 +555,15 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
return new AppraisalStatus(ERROR, baseErrorMessage + e.getMessage());
}
StringBuilder additionalInfo = new StringBuilder();
if (!fieldValidation) {
resultMessage.append("There are unmatched components:\n");
resultMessage.append(unmatchedComponents);
// pass information of which ones failed in additionInfo
for (ComponentIdentifier ci : validPcComponents) {
additionalInfo.append(String.format("%d;", ci.hashCode()));
}
}
passesValidation &= fieldValidation;
@ -540,7 +571,7 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
if (passesValidation) {
return new AppraisalStatus(PASS, PLATFORM_ATTRIBUTES_VALID);
} else {
return new AppraisalStatus(FAIL, resultMessage.toString());
return new AppraisalStatus(FAIL, resultMessage.toString(), additionalInfo.toString());
}
}
@ -555,12 +586,14 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
* base cert for this specific chain
* @return Appraisal Status of delta being validated.
*/
@SuppressWarnings("methodlength")
static AppraisalStatus validateDeltaAttributesChainV2p0(
final DeviceInfoReport deviceInfoReport,
final Map<PlatformCredential, SupplyChainValidation> deltaMapping,
final List<ComponentIdentifier> origPcComponents) {
boolean fieldValidation = true;
StringBuilder resultMessage = new StringBuilder();
String tempStringMessage = "";
List<ComponentIdentifier> validOrigPcComponents = origPcComponents.stream()
.filter(identifier -> identifier.getComponentManufacturer() != null
&& identifier.getComponentModel() != null)
@ -568,11 +601,7 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
List<PlatformCredential> chainCertificates = new LinkedList<>(deltaMapping.keySet());
// map the components throughout the chain
Map<String, ComponentIdentifier> chainCiMapping = new HashMap<>();
List<ComponentIdentifier> deltaBuildList = new LinkedList<>(validOrigPcComponents);
deltaBuildList.stream().forEach((ci) -> {
chainCiMapping.put(ci.getComponentSerial().toString(), ci);
});
List<ComponentIdentifier> baseCompList = new LinkedList<>(validOrigPcComponents);
Collections.sort(chainCertificates, new Comparator<PlatformCredential>() {
@Override
@ -590,118 +619,157 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
return obj1.getBeginValidity().compareTo(obj2.getBeginValidity());
}
});
// start of some changes
resultMessage.append("There are errors with Delta "
+ "Component Statuses:\n");
List<ComponentIdentifier> leftOverDeltas = new ArrayList<>();
List<ComponentIdentifier> absentSerialNum = new ArrayList<>();
tempStringMessage = validateDeltaChain(deltaMapping, baseCompList,
leftOverDeltas, absentSerialNum, chainCertificates);
String ciSerial;
// check if there were any issues
if (!tempStringMessage.isEmpty()) {
resultMessage.append(tempStringMessage);
fieldValidation = false;
}
// finished up
List<ArchivableEntity> certificateList = null;
SupplyChainValidation scv = null;
resultMessage.append("There are errors with Delta "
+ "Component Statuses components:\n");
// go through the leaf and check the changes against the valid components
// forget modifying validOrigPcComponents
for (PlatformCredential delta : chainCertificates) {
StringBuilder failureMsg = new StringBuilder();
certificateList = new ArrayList<>();
certificateList.add(delta);
StringBuilder deltaSb = new StringBuilder();
for (ComponentIdentifier ci : delta.getComponentIdentifiers()) {
if (ci.isVersion2()) {
ciSerial = ci.getComponentSerial().toString();
ComponentIdentifierV2 ciV2 = (ComponentIdentifierV2) ci;
if (ciV2.isModified()) {
// this won't match
// check it is there
if (!chainCiMapping.containsKey(ciSerial)) {
fieldValidation = false;
failureMsg.append(String.format(
"%s attempted MODIFIED with no prior instance.%n",
ciSerial));
scv = deltaMapping.get(delta);
if (scv.getResult() != AppraisalStatus.Status.PASS) {
failureMsg.append(scv.getMessage());
}
deltaMapping.put(delta, new SupplyChainValidation(
SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL,
AppraisalStatus.Status.FAIL,
certificateList,
failureMsg.toString()));
} else {
chainCiMapping.put(ciSerial, ci);
// non-empty serial values
for (ComponentIdentifier deltaCi : leftOverDeltas) {
String classValue;
ComponentIdentifierV2 ciV2 = (ComponentIdentifierV2) deltaCi;
ComponentIdentifierV2 baseCiV2;
boolean classFound;
for (ComponentIdentifier ci : absentSerialNum) {
classValue = ciV2.getComponentClass().getClassValueString();
baseCiV2 = (ComponentIdentifierV2) ci;
classFound = classValue.equals(baseCiV2.getComponentClass()
.getClassValueString());
if (classFound) {
if (isMatch(ciV2, baseCiV2)) {
if (ciV2.isAdded()) {
// error
resultMessage.append("ADDED attempted with prior instance\n");
deltaSb.append(String.format("%s;", ci.hashCode()));
}
} else if (ciV2.isRemoved()) {
if (!chainCiMapping.containsKey(ciSerial)) {
// error thrown, can't remove if it doesn't exist
fieldValidation = false;
failureMsg.append(String.format(
"%s attempted REMOVED with no prior instance.%n",
ciSerial));
scv = deltaMapping.get(delta);
if (scv.getResult() != AppraisalStatus.Status.PASS) {
failureMsg.append(scv.getMessage());
}
deltaMapping.put(delta, new SupplyChainValidation(
SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL,
AppraisalStatus.Status.FAIL,
certificateList,
failureMsg.toString()));
} else {
chainCiMapping.remove(ciSerial);
if (ciV2.isModified()) {
// since the base list doesn't have this ci
// just add the delta
baseCompList.add(deltaCi);
}
} else if (ciV2.isAdded()) {
// ADDED
if (chainCiMapping.containsKey(ciSerial)) {
// error, shouldn't exist
fieldValidation = false;
failureMsg.append(String.format(
"%s was ADDED, the serial already exists.%n",
ciSerial));
scv = deltaMapping.get(delta);
if (scv.getResult() != AppraisalStatus.Status.PASS) {
failureMsg.append(scv.getMessage());
}
deltaMapping.put(delta, new SupplyChainValidation(
SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL,
AppraisalStatus.Status.FAIL,
certificateList,
failureMsg.toString()));
} else {
// have to add in case later it is removed
chainCiMapping.put(ciSerial, ci);
if (ciV2.isRemoved()) {
baseCompList.remove(ciV2);
}
// if it is a remove
// we do nothing because baseCompList doesn't have it
} else {
// it is an add
if (ciV2.isAdded()) {
baseCompList.add(deltaCi);
}
}
} else {
// delta change to a class not there
if (ciV2.isAdded()) {
baseCompList.add(deltaCi);
}
if (ciV2.isModified()) {
// error because you can't modify something
// that isn't here
resultMessage.append("MODIFIED attempted without prior instance\n");
deltaSb.append(String.format("%s;", ci.hashCode()));
}
if (ciV2.isRemoved()) {
// error because you can't remove something
// that isn't here
resultMessage.append("REMOVED attempted without prior instance\n");
deltaSb.append(String.format("%s;", ci.hashCode()));
}
}
}
resultMessage.append(failureMsg.toString());
}
if (!fieldValidation) {
return new AppraisalStatus(FAIL, resultMessage.toString());
if (!fieldValidation || !deltaSb.toString().isEmpty()) {
return new AppraisalStatus(FAIL, resultMessage.toString(), deltaSb.toString());
}
String paccorOutputString = deviceInfoReport.getPaccorOutputString();
String unmatchedComponents;
try {
List<ComponentInfo> componentInfoList
= getComponentInfoFromPaccorOutput(paccorOutputString);
unmatchedComponents = validateV2p0PlatformCredentialComponentsExpectingExactMatch(
new LinkedList<>(chainCiMapping.values()), componentInfoList);
// compare based on component class
List<ComponentInfo> componentInfoList = getV2PaccorOutput(paccorOutputString);
// this is what I want to rewrite
unmatchedComponents = validateV2PlatformCredentialAttributes(
baseCompList,
componentInfoList);
fieldValidation &= unmatchedComponents.isEmpty();
} catch (IOException e) {
} catch (IOException ioEx) {
final String baseErrorMessage = "Error parsing JSON output from PACCOR: ";
LOGGER.error(baseErrorMessage + e.toString());
LOGGER.error(baseErrorMessage + ioEx.toString());
LOGGER.error("PACCOR output string:\n" + paccorOutputString);
return new AppraisalStatus(ERROR, baseErrorMessage + e.getMessage());
return new AppraisalStatus(ERROR, baseErrorMessage + ioEx.getMessage());
}
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("There are unmatched components:\n");
resultMessage.append(String.format("There are %s unmatched components "
+ "on the Platform Certificate:%n", size));
resultMessage.append(unmatchedComponents);
return new AppraisalStatus(FAIL, resultMessage.toString());
return new AppraisalStatus(FAIL, resultMessage.toString(), failureResults);
}
return new AppraisalStatus(PASS, PLATFORM_ATTRIBUTES_VALID);
}
private static String validateV2PlatformCredentialAttributes(
final List<ComponentIdentifier> fullDeltaChainComponents,
final List<ComponentInfo> allDeviceInfoComponents) {
ComponentIdentifierV2 ciV2;
StringBuilder invalidPcIds = new StringBuilder();
List<ComponentIdentifier> subCompIdList = fullDeltaChainComponents
.stream().collect(Collectors.toList());
List<ComponentInfo> subCompInfoList = allDeviceInfoComponents
.stream().collect(Collectors.toList());
// Delta is the baseline
for (ComponentInfo cInfo : allDeviceInfoComponents) {
for (ComponentIdentifier cId : fullDeltaChainComponents) {
ciV2 = (ComponentIdentifierV2) cId;
if (ciV2.getComponentClass().getClassValueString()
.contains(cInfo.getComponentClass())
&& isMatch(cId, cInfo)) {
subCompIdList.remove(cId);
subCompInfoList.remove(cInfo);
}
}
}
return new AppraisalStatus(PASS, PLATFORM_ATTRIBUTES_VALID);
if (subCompIdList.isEmpty()) {
return Strings.EMPTY;
} else {
// now we return everything that was unmatched
// what is in the component info/device reported components
// is to be displayed as the failure
for (ComponentIdentifier ci : subCompIdList) {
ciV2 = (ComponentIdentifierV2) ci;
invalidPcIds.append(String.format("%d;",
ciV2.hashCode()));
}
}
return String.format("COMPID=%s%d", invalidPcIds.toString(), subCompIdList.size());
}
/**
@ -711,6 +779,8 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
* components not represented in the platform credential.
*
* @param untrimmedPcComponents the platform credential components (may contain end whitespace)
* **NEW** this is updated with just the unmatched components
* if there are any failures, otherwise it remains unchanged.
* @param allDeviceInfoComponents the device info report components
* @return true if validation passes
*/
@ -741,8 +811,7 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
componentSerial, componentRevision,
component.getComponentManufacturerId(),
component.getFieldReplaceable(),
component.getComponentAddress()
));
component.getComponentAddress()));
}
LOGGER.info("Validating the following Platform Cert components...");
@ -750,8 +819,7 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
LOGGER.info("...against the the following DeviceInfoReport components:");
allDeviceInfoComponents.forEach(component -> LOGGER.info(component.toString()));
Set<DERUTF8String> manufacturerSet = new HashSet<>();
pcComponents.forEach(component -> manufacturerSet.add(
component.getComponentManufacturer()));
pcComponents.forEach(pcComp -> manufacturerSet.add(pcComp.getComponentManufacturer()));
// Create a list for unmatched components across all manufacturers to display at the end.
List<ComponentIdentifier> pcUnmatchedComponents = new ArrayList<>();
@ -787,8 +855,7 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
.filter(componentInfo
-> StringUtils.isNotEmpty(componentInfo.getComponentSerial()))
.filter(componentInfo -> componentInfo.getComponentSerial()
.equals(pcComponent.getComponentSerial().getString()))
.findFirst();
.equals(pcComponent.getComponentSerial().getString())).findFirst();
if (first.isPresent()) {
ComponentInfo potentialMatch = first.get();
@ -833,12 +900,11 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
// just match them.
List<ComponentIdentifier> templist = new ArrayList<>(pcComponentsFromManufacturer);
for (ComponentIdentifier ci : templist) {
ComponentIdentifier pcComponent = ci;
Iterator<ComponentInfo> diComponentIter
= deviceInfoComponentsFromManufacturer.iterator();
while (diComponentIter.hasNext()) {
ComponentInfo potentialMatch = diComponentIter.next();
if (isMatch(pcComponent, potentialMatch)) {
if (isMatch(ci, potentialMatch)) {
pcComponentsFromManufacturer.remove(ci);
diComponentIter.remove();
}
@ -848,6 +914,7 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
}
if (!pcUnmatchedComponents.isEmpty()) {
untrimmedPcComponents.clear();
StringBuilder sb = new StringBuilder();
LOGGER.error(String.format("Platform Credential contained %d unmatched components:",
pcUnmatchedComponents.size()));
@ -861,6 +928,8 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
unmatchedComponent.getComponentModel(),
unmatchedComponent.getComponentSerial(),
unmatchedComponent.getComponentRevision()));
unmatchedComponent.setValidationResult(false);
untrimmedPcComponents.add(unmatchedComponent);
}
return sb.toString();
}
@ -998,6 +1067,28 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
return matchesSoFar;
}
/**
* Checks if the fields in the potentialMatch match the fields in the pcComponent,
* or if the relevant field in the pcComponent is empty.
* @param pcComponent the platform credential component
* @param potentialMatch the component info from a device info report
* @return true if the fields match exactly (null is considered the same as an empty string)
*/
static boolean isMatch(final ComponentIdentifierV2 pcComponent,
final ComponentIdentifierV2 potentialMatch) {
boolean matchesSoFar = true;
matchesSoFar &= isMatchOrEmptyInPlatformCert(
potentialMatch.getComponentManufacturer(),
pcComponent.getComponentManufacturer());
matchesSoFar &= isMatchOrEmptyInPlatformCert(
potentialMatch.getComponentModel(),
pcComponent.getComponentModel());
return matchesSoFar;
}
private static boolean isMatchOrEmptyInPlatformCert(
final String evidenceFromDevice,
final DERUTF8String valueInPlatformCert) {
@ -1007,6 +1098,12 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
return valueInPlatformCert.getString().equals(evidenceFromDevice);
}
private static boolean isMatchOrEmptyInPlatformCert(
final DERUTF8String evidenceFromDevice,
final DERUTF8String valueInPlatformCert) {
return evidenceFromDevice.equals(valueInPlatformCert);
}
/**
* Validates the platform credential's serial numbers with the device info's set of
* serial numbers.
@ -1300,6 +1397,128 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
return foundRootOfCertChain;
}
private static String validateDeltaChain(
final Map<PlatformCredential, SupplyChainValidation> deltaMapping,
final List<ComponentIdentifier> baseCompList,
final List<ComponentIdentifier> leftOvers,
final List<ComponentIdentifier> absentSerials,
final List<PlatformCredential> chainCertificates) {
StringBuilder resultMessage = new StringBuilder();
List<String> noneSerialValues = new ArrayList<>();
noneSerialValues.add("");
noneSerialValues.add(null);
noneSerialValues.add("Not Specified");
noneSerialValues.add("To Be Filled By O.E.M.");
// map the components throughout the chain
Map<String, ComponentIdentifier> chainCiMapping = new HashMap<>();
baseCompList.stream().forEach((ci) -> {
if (!noneSerialValues.contains(ci.getComponentSerial().toString())) {
chainCiMapping.put(ci.getComponentSerial().toString(), ci);
} else {
absentSerials.add(ci);
}
});
String ciSerial;
List<ArchivableEntity> certificateList = null;
SupplyChainValidation scv = null;
// go through the leaf and check the changes against the valid components
// forget modifying validOrigPcComponents
for (PlatformCredential delta : chainCertificates) {
StringBuilder failureMsg = new StringBuilder();
certificateList = new ArrayList<>();
certificateList.add(delta);
for (ComponentIdentifier ci : delta.getComponentIdentifiers()) {
if (!noneSerialValues.contains(ci.getComponentSerial().toString())) {
if (ci.isVersion2()) {
ciSerial = ci.getComponentSerial().toString();
ComponentIdentifierV2 ciV2 = (ComponentIdentifierV2) ci;
if (ciV2.isModified()) {
// this won't match
// check it is there
if (chainCiMapping.containsKey(ciSerial)) {
chainCiMapping.put(ciSerial, ci);
} else {
failureMsg.append(String.format(
"%s attempted MODIFIED with no prior instance.%n",
ciSerial));
delta.setComponentFailures(String.format("%s,%d",
delta.getComponentFailures(), ciV2.hashCode()));
scv = deltaMapping.get(delta);
if (scv != null
&& scv.getResult() != AppraisalStatus.Status.PASS) {
failureMsg.append(scv.getMessage());
}
deltaMapping.put(delta, new SupplyChainValidation(
SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL,
AppraisalStatus.Status.FAIL,
certificateList,
failureMsg.toString()));
}
} else if (ciV2.isRemoved()) {
if (!chainCiMapping.containsKey(ciSerial)) {
// error thrown, can't remove if it doesn't exist
failureMsg.append(String.format(
"%s attempted REMOVED with no prior instance.%n",
ciSerial));
delta.setComponentFailures(String.format("%s,%d",
delta.getComponentFailures(), ciV2.hashCode()));
scv = deltaMapping.get(delta);
if (scv != null
&& scv.getResult() != AppraisalStatus.Status.PASS) {
failureMsg.append(scv.getMessage());
}
deltaMapping.put(delta, new SupplyChainValidation(
SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL,
AppraisalStatus.Status.FAIL,
certificateList,
failureMsg.toString()));
} else {
chainCiMapping.remove(ciSerial);
}
} else if (ciV2.isAdded()) {
// ADDED
if (chainCiMapping.containsKey(ciSerial)) {
// error, shouldn't exist
failureMsg.append(String.format(
"%s was ADDED, the serial already exists.%n",
ciSerial));
delta.setComponentFailures(String.format("%s,%d",
delta.getComponentFailures(), ciV2.hashCode()));
scv = deltaMapping.get(delta);
if (scv != null
&& scv.getResult() != AppraisalStatus.Status.PASS) {
failureMsg.append(scv.getMessage());
}
deltaMapping.put(delta, new SupplyChainValidation(
SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL,
AppraisalStatus.Status.FAIL,
certificateList,
failureMsg.toString()));
} else {
// have to add in case later it is removed
chainCiMapping.put(ciSerial, ci);
}
}
}
} else {
// found a delta ci with no serial
// add to list
leftOvers.add(ci);
}
}
resultMessage.append(failureMsg.toString());
}
baseCompList.clear();
baseCompList.addAll(chainCiMapping.values());
baseCompList.addAll(absentSerials);
return resultMessage.toString();
}
/**
* Checks if the issuer info of an attribute cert matches the supposed signing cert's
* distinguished name.
@ -1448,13 +1667,4 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
return false;
}
}
/**
* Getter for the collection of delta certificates that have failed and the
* associated message.
* @return unmodifiable list of failed certificates
*/
public Map<PlatformCredential, StringBuilder> getDeltaFailures() {
return Collections.unmodifiableMap(DELTA_FAILURES);
}
}