[#166] Validation icon swap (#173)

* This pull request contains 2 main changes, the first is transferring the status text from the attributes failure to the icon specifically for platform trust chain validation.  Then this removes the third column on the validation page that singles out the icons for the attribute status.  In addition, this status is also rolled up to the summary status icon and displays the text there as well for all that have failed.  This last change meant a change to the sizes of the columns in the database.

The validation of a single base certificate with an error was not handled in the code base.  Due to the changes with the introduction of delta certifications, the validation was modified and only handled changes presented by the deltas and ignored errors in the base certificate.  This commit modifies the code that if there is just a single base certificate that is bad and error is thrown.
This commit is contained in:
Cyrus 2019-08-02 09:41:44 -04:00 committed by GitHub
parent aa707b8665
commit 7cfabe756d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 245 additions and 173 deletions

View File

@ -12,14 +12,13 @@ import org.springframework.context.annotation.Import;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.stream.Collectors;
import java.util.HashMap;
import org.apache.logging.log4j.Level;
import hirs.appraiser.Appraiser;
import hirs.appraiser.SupplyChainAppraiser;
@ -41,6 +40,8 @@ import hirs.persist.DBManagerException;
import hirs.persist.PersistenceConfiguration;
import hirs.persist.PolicyManager;
import hirs.validation.CredentialValidator;
import java.util.HashMap;
import java.util.Map;
/**
* The main executor of supply chain verification tasks. The AbstractAttestationCertificateAuthority
@ -102,17 +103,16 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
SupplyChainPolicy policy = (SupplyChainPolicy) policyManager.getDefaultPolicy(
supplyChainAppraiser);
boolean acceptExpiredCerts = policy.isExpiredCertificateValidationEnabled();
HashMap<PlatformCredential, SupplyChainValidation> credentialMap = new HashMap<>();
PlatformCredential baseCredential = null;
List<SupplyChainValidation> validations = new LinkedList<>();
List<SupplyChainValidation> deltaValidations = new LinkedList<>();
// validate all supply chain pieces. Potentially, a policy setting could be made
// to dictate stopping after the first validation failure.
Map<SupplyChainValidation.ValidationType,
SupplyChainValidation> validationTypeMap = new HashMap<>();
Map<PlatformCredential, SupplyChainValidation> deltaMapping = new HashMap<>();
// Validate the Endorsement Credential
if (policy.isEcValidationEnabled()) {
validations.add(validateEndorsementCredential(ec, acceptExpiredCerts));
validationTypeMap.put(SupplyChainValidation.ValidationType.ENDORSEMENT_CREDENTIAL,
validateEndorsementCredential(ec, acceptExpiredCerts));
// store the device with the credential
if (null != ec) {
ec.setDevice(device);
@ -125,7 +125,9 @@ 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(
validationTypeMap.put(SupplyChainValidation
.ValidationType.PLATFORM_CREDENTIAL,
buildValidationRecord(
SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL,
AppraisalStatus.Status.FAIL,
"Platform credential(s) missing", null, Level.ERROR));
@ -137,20 +139,17 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
SupplyChainValidation platformScv = validatePlatformCredential(
pc, trustedCa, acceptExpiredCerts);
// check if this cert has been verified for multiple base associated
// with the serial number
// check if this cert has been verified for multiple base
// associated with the serial number
if (pc != null) {
platformScv = validatePcPolicy(pc, platformScv,
deltaValidations, acceptExpiredCerts);
}
validations.add(platformScv);
if (!deltaValidations.isEmpty()) {
validations.addAll(deltaValidations);
}
if (pc != null) {
deltaMapping, acceptExpiredCerts);
validationTypeMap.put(SupplyChainValidation
.ValidationType.PLATFORM_CREDENTIAL,
platformScv);
pc.setDevice(device);
this.certificateManager.update(pc);
credentialMap.put(pc, platformScv);
if (pc.isBase()) {
baseCredential = pc;
}
@ -164,53 +163,30 @@ 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 attributes.");
validations.add(buildValidationRecord(
SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL_ATTRIBUTES,
validationTypeMap.put(SupplyChainValidation
.ValidationType.PLATFORM_CREDENTIAL,
buildValidationRecord(
SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL,
AppraisalStatus.Status.FAIL,
"Platform credential(s) missing. Cannot validate attributes",
"Platform credential(s) missing.\nPlatform credential(s) missing."
+ " Cannot validate attributes",
null, Level.ERROR));
} else {
Iterator<PlatformCredential> it = pcs.iterator();
while (it.hasNext()) {
PlatformCredential pc = it.next();
SupplyChainValidation attributeScv;
if (baseCredential == null || pc == baseCredential && !pc.isDeltaChain()) {
if (baseCredential == null || pc == baseCredential) {
attributeScv = validatePlatformCredentialAttributes(
pc, device.getDeviceInfo(), ec);
} else {
List<PlatformCredential> chainCertificates = PlatformCredential
.select(certificateManager)
.byBoardSerialNumber(pc.getPlatformSerial())
.getCertificates().stream().collect(Collectors.toList());
Collections.sort(chainCertificates,
new Comparator<PlatformCredential>() {
@Override
public int compare(final PlatformCredential obj1,
final PlatformCredential obj2) {
return obj1.getBeginValidity()
.compareTo(obj2.getBeginValidity());
}
});
attributeScv = validateDeltaPlatformCredentialAttributes(
pc, device.getDeviceInfo(), baseCredential, chainCertificates);
validationTypeMap.put(SupplyChainValidation
.ValidationType.PLATFORM_CREDENTIAL,
attributeScv);
}
SupplyChainValidation platformScv = credentialMap.get(pc);
if (platformScv != null) {
if (platformScv.getResult() == AppraisalStatus.Status.FAIL
|| platformScv.getResult() == AppraisalStatus.Status.ERROR) {
if (attributeScv != null
&& attributeScv.getResult() == AppraisalStatus.Status.PASS) {
validations.add(buildValidationRecord(
SupplyChainValidation.ValidationType
.PLATFORM_CREDENTIAL_ATTRIBUTES,
AppraisalStatus.Status.FAIL,
platformScv.getMessage(), pc, Level.WARN));
}
} else {
validations.add(attributeScv);
}
if (pc != null && pc.isDeltaChain()) {
validateDeltaPlatformCredentialAttributes(
pc, device.getDeviceInfo(), baseCredential, deltaMapping);
}
if (pc != null) {
@ -221,6 +197,14 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
}
}
if (!validationTypeMap.isEmpty()) {
validations.addAll(validationTypeMap.values());
}
if (!deltaMapping.isEmpty()) {
validations.addAll(deltaMapping.values());
}
// Generate validation summary, save it, and return it.
SupplyChainValidationSummary summary =
new SupplyChainValidationSummary(device, validations);
@ -244,7 +228,7 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
private SupplyChainValidation validatePcPolicy(
final PlatformCredential pc,
final SupplyChainValidation platformScv,
final List<SupplyChainValidation> deltaValidations,
final Map<PlatformCredential, SupplyChainValidation> deltaMapping,
final boolean acceptExpiredCerts) {
SupplyChainValidation subPlatformScv = platformScv;
@ -263,26 +247,34 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
AppraisalStatus.Status.FAIL,
message, pc, Level.ERROR);
}
}
// Grab all certs associated with this platform chain
List<PlatformCredential> chainCertificates = PlatformCredential
.select(certificateManager)
.byBoardSerialNumber(pc.getPlatformSerial())
.getCertificates().stream().collect(Collectors.toList());
// Grab all certs associated with this platform chain
List<PlatformCredential> chainCertificates = PlatformCredential
.select(certificateManager)
.byBoardSerialNumber(pc.getPlatformSerial())
.getCertificates().stream().collect(Collectors.toList());
Collections.sort(chainCertificates,
new Comparator<PlatformCredential>() {
@Override
public int compare(final PlatformCredential obj1,
final PlatformCredential obj2) {
return obj1.getBeginValidity()
.compareTo(obj2.getBeginValidity());
}
});
SupplyChainValidation deltaScv;
KeyStore trustedCa;
// verify that the deltas trust chain is valid.
for (PlatformCredential delta : chainCertificates) {
if (delta != null && !delta.isBase()) {
trustedCa = getCaChain(delta);
deltaScv = validatePlatformCredential(
delta, trustedCa, acceptExpiredCerts);
deltaValidations.add(deltaScv);
SupplyChainValidation deltaScv;
KeyStore trustedCa;
// verify that the deltas trust chain is valid.
for (PlatformCredential delta : chainCertificates) {
if (delta != null && !delta.isBase()) {
trustedCa = getCaChain(delta);
deltaScv = validatePlatformCredential(
delta, trustedCa, acceptExpiredCerts);
deltaMapping.put(delta, deltaScv);
}
}
}
return subPlatformScv;
}
@ -352,7 +344,7 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
final DeviceInfoReport deviceInfoReport,
final EndorsementCredential ec) {
final SupplyChainValidation.ValidationType validationType
= SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL_ATTRIBUTES;
= SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL;
if (pc == null) {
LOGGER.error("No platform credential to validate");
@ -383,7 +375,7 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
final PlatformCredential delta,
final DeviceInfoReport deviceInfoReport,
final PlatformCredential base,
final List<PlatformCredential> chainCertificates) {
final Map<PlatformCredential, SupplyChainValidation> deltaMapping) {
final SupplyChainValidation.ValidationType validationType =
SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL_ATTRIBUTES;
@ -396,7 +388,7 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
LOGGER.info("Validating delta platform certificate attributes");
AppraisalStatus result = supplyChainCredentialValidator.
validateDeltaPlatformCredentialAttributes(delta, deviceInfoReport,
base, chainCertificates);
base, deltaMapping);
switch (result.getAppStatus()) {
case PASS:
return buildValidationRecord(validationType, AppraisalStatus.Status.PASS,

View File

@ -144,7 +144,6 @@ public class SupplyChainValidationServiceImplTest extends SpringPersistenceTest
//when(delta.getSerialNumber()).thenReturn(BigInteger.ONE);
when(delta.getIssuerOrganization()).thenReturn("STMicroelectronics NV");
when(delta.getSubjectOrganization()).thenReturn("STMicroelectronics NV");
//pcs.add(delta);
Set<Certificate> resultPcs = new HashSet<>();
resultPcs.add(pc);
@ -242,7 +241,7 @@ public class SupplyChainValidationServiceImplTest extends SpringPersistenceTest
validateEndorsementCredential(eq(ec), any(KeyStore.class), eq(true));
doReturn(new AppraisalStatus(FAIL, "")).when(supplyChainCredentialValidator).
validatePlatformCredential(eq(pc), any(KeyStore.class), eq(true));
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator)
doReturn(new AppraisalStatus(FAIL, "")).when(supplyChainCredentialValidator)
.validatePlatformCredentialAttributes(eq(pc), any(DeviceInfoReport.class),
any(EndorsementCredential.class));
Assert.assertEquals(service.validateSupplyChain(ec, pcs,

View File

@ -616,12 +616,12 @@
<div class="panel panel-default">
<div class="panel-heading">
<c:choose>
<c:when test="${component.isVersion2()=='TRUE'}">
<span data-toggle="tooltip" data-placement="top" title="Component Class">${component.getComponentClass()}</span>
</c:when>
<c:otherwise>
<span data-toggle="tooltip" data-placement="top" title="Component Class">Platform Components</span>
</c:otherwise>
<c:when test="${component.isVersion2()=='TRUE'}">
<span data-toggle="tooltip" data-placement="top" title="Component Class">${component.getComponentClass()}</span>
</c:when>
<c:otherwise>
<span data-toggle="tooltip" data-placement="top" title="Component Class">Platform Components</span>
</c:otherwise>
</c:choose>
</div>
<div class="panel-body">
@ -649,22 +649,22 @@
<span class="label label-danger">Irreplaceable</span><br/>
</c:otherwise>
</c:choose>
<c:if test="${component.isVersion2()}">
<c:if test="${not empty component.getCertificateIdentifier()}">
<span class="fieldHeader">Platform Certificate Issuer:</span>
<span class="fieldValue">${component.getCertificateIdentifier().getIssuerDN()}</span><br />
<span class="fieldHeader">Platform Certificate Serial Number:</span>
<span class="fieldValue">${component.getCertificateIdentifier().getCertificateSerialNumber()}</span><br />
<span class="fieldHeader">Platform Certificate URI:</span>
</c:if>
<span class="fieldValue">
<a href="${component.getComponentPlatformUri().getUniformResourceIdentifier()}">
${component.getComponentPlatformUri().getUniformResourceIdentifier()}
</a>
</span><br />
<span class="fieldHeader">Status:</span>
<span class="fieldValue">${component.getAttributeStatus()}</span><br/>
<c:if test="${component.isVersion2()}">
<c:if test="${not empty component.getCertificateIdentifier()}">
<span class="fieldHeader">Platform Certificate Issuer:</span>
<span class="fieldValue">${component.getCertificateIdentifier().getIssuerDN()}</span><br />
<span class="fieldHeader">Platform Certificate Serial Number:</span>
<span class="fieldValue">${component.getCertificateIdentifier().getCertificateSerialNumber()}</span><br />
<span class="fieldHeader">Platform Certificate URI:</span>
</c:if>
<span class="fieldValue">
<a href="${component.getComponentPlatformUri().getUniformResourceIdentifier()}">
${component.getComponentPlatformUri().getUniformResourceIdentifier()}
</a>
</span><br />
<span class="fieldHeader">Status:</span>
<span class="fieldValue">${component.getAttributeStatus()}</span><br/>
</c:if>
</div>
</div>
</div>
@ -825,11 +825,11 @@
$("#authorityKeyIdentifier").html("Not Specified");
</c:otherwise>
</c:choose>
<c:if test="${not empty initialData.authSerialNumber}">
//Convert string to serial String
$("#authSerialNumber").html(parseSerialNumber(authoritySerialNumber));
</c:if>
<c:if test="${not empty initialData.authSerialNumber}">
//Convert string to serial String
$("#authSerialNumber").html(parseSerialNumber(authoritySerialNumber));
</c:if>
<c:choose>
<c:when test="${not empty initialData.publicKeyValue}">
var publicKey = '${initialData.publicKeyValue}';

View File

@ -32,12 +32,12 @@
<th rowspan="2">Result</th>
<th rowspan="2">Timestamp</th>
<th rowspan="2">Device</th>
<th colspan="3">Credential Validations</th>
<th colspan="2">Credential Validations</th>
</tr>
<tr>
<th>Endorsement</th>
<th>Platform</th>
<th>Platform Attributes</th>
<th style="text-align:center">Endorsement</th>
<th style="text-align:center">Platform</th>
<th></th>
</tr>
</thead>
</table>
@ -55,16 +55,17 @@
// create status icon
var result = full.overallValidationResult;
var ovallMessage = full.message;
if (result) {
switch (result) {
case "PASS":
html += '<img src="${passIcon}" title="${passText}"/>';
break;
case "FAIL":
html += '<img src="${failIcon}" title="${failText}"/>';
html += '<img src="${failIcon}" title="' + ovallMessage + '"/>';
break;
case "ERROR":
html += '<img src="${errorIcon}" title="${errorText}"/>';
html += '<img src="${errorIcon}" title="' + ovallMessage + '"/>';
break;
default:
html += unknownStatus;
@ -161,7 +162,7 @@
html += '<a href="${portal}/certificate-details?id='
+ curValidation.certificatesUsed[0].id
+ '&type=' + certType + '">'
+ '&type=' + certType + '">';
}
switch (curResult) {

View File

@ -15,6 +15,13 @@ import org.hibernate.annotations.Type;
*/
@MappedSuperclass
public abstract class AbstractEntity {
/**
* static value for the length of a status message for objects that
* can have extremely long values, potentially.
*/
protected static final int RESULT_MESSAGE_LENGTH = 1000000;
@Id
@Column(name = "id")
@GeneratedValue(generator = "uuid2")

View File

@ -29,8 +29,6 @@ import javax.xml.bind.annotation.XmlTransient;
@JsonSerialize(using = AppraisalResultSerializer.class)
public class AppraisalResult extends AbstractEntity {
private static final int RESULT_MESSAGE_LENGTH = 1000000;
/**
* Corresponding <code>Appraiser</code>. Can be NULL.
*/

View File

@ -22,22 +22,24 @@ public class NetworkInfo implements Serializable {
private static final Logger LOGGER = LogManager
.getLogger(NetworkInfo.class);
private static final int LONG_STRING_LENGTH = 255;
private static final int SHORT_STRING_LENGTH = 32;
private static final int NUM_MAC_ADDRESS_BYTES = 6;
@XmlElement
@Column(length = 255, nullable = true)
@Column(length = LONG_STRING_LENGTH, nullable = true)
@SuppressWarnings("checkstyle:magicnumber")
private String hostname;
@XmlElement
@XmlJavaTypeAdapter(value = InetAddressXmlAdapter.class)
@SuppressWarnings("checkstyle:magicnumber")
@Column(length = 32, nullable = true)
@Column(length = SHORT_STRING_LENGTH, nullable = true)
@Type(type = "hirs.data.persist.type.InetAddressType")
private InetAddress ipAddress;
@XmlElement
@Column(length = 6, nullable = true)
@Column(length = NUM_MAC_ADDRESS_BYTES, nullable = true)
@SuppressWarnings("checkstyle:magicnumber")
private byte[] macAddress;

View File

@ -25,6 +25,7 @@ import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.apache.logging.log4j.util.Strings;
/**
* A container class to group multiple related {@link SupplyChainValidation} instances
@ -42,6 +43,9 @@ public class SupplyChainValidationSummary extends ArchivableEntity {
@Enumerated(EnumType.STRING)
private final AppraisalStatus.Status overallValidationResult;
@Column(length = RESULT_MESSAGE_LENGTH)
private final String message;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER,
targetEntity = SupplyChainValidation.class, orphanRemoval = true)
private final Set<SupplyChainValidation> validations;
@ -53,6 +57,7 @@ public class SupplyChainValidationSummary extends ArchivableEntity {
this.device = null;
overallValidationResult = AppraisalStatus.Status.FAIL;
validations = Collections.emptySet();
this.message = Strings.EMPTY;
}
/**
@ -183,9 +188,10 @@ public class SupplyChainValidationSummary extends ArchivableEntity {
this.device = device;
this.overallValidationResult = calculateValidationResult(validations);
AppraisalStatus status = calculateValidationResult(validations);
this.overallValidationResult = status.getAppStatus();
this.validations = new HashSet<>(validations);
this.message = status.getMessage();
}
/**
@ -204,6 +210,13 @@ public class SupplyChainValidationSummary extends ArchivableEntity {
return overallValidationResult;
}
/**
* @return the fail message if there is a failure.
*/
public String getMessage() {
return message;
}
/**
* @return the validations that this summary contains
*/
@ -217,17 +230,20 @@ public class SupplyChainValidationSummary extends ArchivableEntity {
* @param validations the validations to evaluate
* @return the overall appraisal result
*/
private AppraisalStatus.Status calculateValidationResult(
private AppraisalStatus calculateValidationResult(
final Collection<SupplyChainValidation> validations) {
boolean hasAnyFailures = false;
StringBuilder failureMsg = new StringBuilder();
for (SupplyChainValidation validation : validations) {
switch (validation.getResult()) {
// if any error, then process overall as error immediately.
case ERROR:
return AppraisalStatus.Status.ERROR;
return new AppraisalStatus(AppraisalStatus.Status.ERROR,
validation.getMessage());
case FAIL:
hasAnyFailures = true;
failureMsg.append(validation.getMessage());
break;
default:
break;
@ -236,8 +252,10 @@ public class SupplyChainValidationSummary extends ArchivableEntity {
}
// if failures, but no error, indicate failure result.
if (hasAnyFailures) {
return AppraisalStatus.Status.FAIL;
return new AppraisalStatus(AppraisalStatus.Status.FAIL,
failureMsg.toString());
}
return AppraisalStatus.Status.PASS;
return new AppraisalStatus(AppraisalStatus.Status.PASS,
Strings.EMPTY);
}
}

View File

@ -2,11 +2,12 @@ package hirs.validation;
import hirs.data.persist.AppraisalStatus;
import hirs.data.persist.DeviceInfoReport;
import hirs.data.persist.SupplyChainValidation;
import hirs.data.persist.certificate.EndorsementCredential;
import hirs.data.persist.certificate.PlatformCredential;
import java.security.KeyStore;
import java.util.List;
import java.util.Map;
/**
* A class used to support supply chain validation by performing the actual
@ -44,14 +45,15 @@ public interface CredentialValidator {
* serial number of the platform to be validated.
* @param base the base credential from the same identity request
* as the delta credential.
* @param chainCertificates base and delta certificates associated with the
* delta being validated.
* @param deltaMapping delta certificates associated with the
* delta supply validation.
* @return the result of the validation.
*/
AppraisalStatus validateDeltaPlatformCredentialAttributes(PlatformCredential delta,
DeviceInfoReport deviceInfoReport,
PlatformCredential base,
List<PlatformCredential> chainCertificates);
Map<PlatformCredential,
SupplyChainValidation> deltaMapping);
/**
* Checks if the endorsement credential is valid.
*

View File

@ -55,6 +55,8 @@ import java.util.stream.Collectors;
import static hirs.data.persist.AppraisalStatus.Status.ERROR;
import static hirs.data.persist.AppraisalStatus.Status.FAIL;
import static hirs.data.persist.AppraisalStatus.Status.PASS;
import hirs.data.persist.SupplyChainValidation;
import hirs.data.persist.certificate.Certificate;
import hirs.data.persist.certificate.attributes.V2.ComponentIdentifierV2;
import java.util.Collections;
import java.util.LinkedList;
@ -87,6 +89,8 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
public static final String PLATFORM_ATTRIBUTES_VALID =
"Platform credential attributes 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.
@ -135,7 +139,6 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
return node.findValue(fieldName).asText();
}
return null;
}
/**
@ -153,16 +156,15 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
final String baseErrorMessage = "Can't validate platform credential without ";
String message;
if (pc == null) {
message = baseErrorMessage + "a platform credential";
message = baseErrorMessage + "a platform credential\n";
LOGGER.error(message);
return new AppraisalStatus(FAIL, message);
}
try {
if (trustStore == null || trustStore.size() == 0) {
message = baseErrorMessage + "a trust store";
message = baseErrorMessage + "a trust store\n";
LOGGER.error(message);
return new AppraisalStatus(FAIL, message);
}
} catch (KeyStoreException e) {
message = baseErrorMessage + "an intitialized trust store";
@ -261,8 +263,8 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
* serial number of the platform to be validated.
* @param basePlatformCredential the base credential from the same identity request
* as the delta credential.
* @param chainCertificates base and delta certificates associated with the
* delta being validated.
* @param deltaMapping delta certificates associated with the
* delta supply validation.
* @return the result of the validation.
*/
@Override
@ -270,7 +272,7 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
final PlatformCredential deltaPlatformCredential,
final DeviceInfoReport deviceInfoReport,
final PlatformCredential basePlatformCredential,
final List<PlatformCredential> chainCertificates) {
final Map<PlatformCredential, SupplyChainValidation> deltaMapping) {
final String baseErrorMessage = "Can't validate delta platform"
+ "certificate attributes without ";
String message;
@ -302,30 +304,11 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
}
// parse out the provided delta and its specific chain.
Collections.reverse(chainCertificates);
List<PlatformCredential> leafChain = new LinkedList<>();
List<ComponentIdentifier> origPcComponents = new LinkedList<>();
for (PlatformCredential pc : chainCertificates) {
if (pc.isBase()) {
if (basePlatformCredential.getSerialNumber()
.equals(pc.getSerialNumber())) {
// get original component list
origPcComponents.addAll(pc.getComponentIdentifiers());
}
} else {
leafChain.add(pc);
}
}
// map the deltas to their holder serial numbers
Map<String, PlatformCredential> leafMap = new HashMap<>();
leafChain.stream().forEach((delta) -> {
leafMap.put(delta.getHolderSerialNumber().toString(), delta);
});
List<ComponentIdentifier> origPcComponents
= new LinkedList<>(basePlatformCredential.getComponentIdentifiers());
return validateDeltaAttributesChainV2p0(deviceInfoReport,
leafChain, origPcComponents);
deltaMapping, origPcComponents);
}
private static AppraisalStatus validatePlatformCredentialAttributesV1p2(
@ -561,19 +544,17 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
* are valid.
*
* @param deviceInfoReport The paccor profile of device being validated against.
* @param leafChain The specific chain associated with delta cert being
* validated.
* @param deltaMapping map of delta certificates to their validated status
* @param origPcComponents The component identifier list associated with the
* base cert for this specific chain
* @return Appraisal Status of delta being validated.
*/
static AppraisalStatus validateDeltaAttributesChainV2p0(
final DeviceInfoReport deviceInfoReport,
final List<PlatformCredential> leafChain,
final Map<PlatformCredential, SupplyChainValidation> deltaMapping,
final List<ComponentIdentifier> origPcComponents) {
boolean fieldValidation = true;
StringBuilder resultMessage = new StringBuilder();
StringBuilder deltaMessage = new StringBuilder();
List<ComponentIdentifier> validOrigPcComponents = origPcComponents.stream()
.filter(identifier -> identifier.getComponentManufacturer() != null
&& identifier.getComponentModel() != null)
@ -587,9 +568,20 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
});
String ciSerial;
List<Certificate> certificateList = null;
PlatformCredential delta = 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 : leafChain) {
for (Map.Entry<PlatformCredential, SupplyChainValidation> deltaEntry
: deltaMapping.entrySet()) {
StringBuilder failureMsg = new StringBuilder();
delta = deltaEntry.getKey();
certificateList = new ArrayList<>();
certificateList.add(delta);
for (ComponentIdentifier ci : delta.getComponentIdentifiers()) {
if (ci.isVersion2()) {
ciSerial = ci.getComponentSerial().toString();
@ -599,9 +591,18 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
// check it is there
if (!chainCiMapping.containsKey(ciSerial)) {
fieldValidation = false;
deltaMessage.append(String.format(
failureMsg.append(String.format(
"%s attempted MODIFIED with no prior instance.%n",
ciSerial));
scv = deltaEntry.getValue();
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(ci.getComponentSerial().toString(), ci);
}
@ -609,9 +610,18 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
if (!chainCiMapping.containsKey(ciSerial)) {
// error thrown, can't remove if it doesn't exist
fieldValidation = false;
deltaMessage.append(String.format(
failureMsg.append(String.format(
"%s attempted REMOVED with no prior instance.%n",
ciSerial));
scv = deltaEntry.getValue();
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(ci.getComponentSerial().toString());
}
@ -620,9 +630,18 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
if (chainCiMapping.containsKey(ciSerial)) {
// error, shouldn't exist
fieldValidation = false;
deltaMessage.append(String.format(
failureMsg.append(String.format(
"%s was ADDED, the serial already exists.%n",
ciSerial));
scv = deltaEntry.getValue();
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);
@ -630,11 +649,11 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
}
}
}
resultMessage.append(failureMsg.toString());
}
if (!fieldValidation) {
resultMessage.append("There are errors with Delta Component Statuses components:\n");
resultMessage.append(deltaMessage.toString());
return new AppraisalStatus(FAIL, resultMessage.toString());
}
@ -654,6 +673,7 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
}
if (!fieldValidation) {
resultMessage = new StringBuilder();
resultMessage.append("There are unmatched components:\n");
resultMessage.append(unmatchedComponents);
@ -1407,4 +1427,13 @@ 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);
}
}

View File

@ -9,6 +9,7 @@ import hirs.data.persist.HardwareInfo;
import hirs.data.persist.NICComponentInfo;
import hirs.data.persist.NetworkInfo;
import hirs.data.persist.OSInfo;
import hirs.data.persist.SupplyChainValidation;
import hirs.data.persist.TPMInfo;
import hirs.data.persist.certificate.Certificate;
import hirs.data.persist.certificate.CertificateAuthorityCredential;
@ -79,8 +80,10 @@ import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.powermock.api.mockito.PowerMockito.mock;
@ -1158,7 +1161,8 @@ public class SupplyChainCredentialValidatorTest {
*/
@Test
public final void verifyPlatformCredentialNullCredentialPath() {
String expectedMessage = "Can't validate platform credential without a platform credential";
String expectedMessage = "Can't validate platform credential without "
+ "a platform credential\n";
AppraisalStatus result = supplyChainCredentialValidator.validatePlatformCredential(
null, keyStore, true);
@ -1182,7 +1186,7 @@ public class SupplyChainCredentialValidatorTest {
PlatformCredential pc = new PlatformCredential(certBytes);
String expectedMessage = "Can't validate platform credential without a "
+ "trust store";
+ "trust store\n";
AppraisalStatus result = supplyChainCredentialValidator.validatePlatformCredential(pc, null,
true);
@ -2092,10 +2096,22 @@ public class SupplyChainCredentialValidatorTest {
when(delta1.getComponentIdentifiers()).thenReturn(delta1List);
when(delta2.getComponentIdentifiers()).thenReturn(delta2List);
List<PlatformCredential> chainCredentials = new ArrayList<>(0);
chainCredentials.add(base);
chainCredentials.add(delta1);
chainCredentials.add(delta2);
Map<PlatformCredential, SupplyChainValidation> chainCredentials = new HashMap<>(0);
List<Certificate> certsUsed = new ArrayList<>();
certsUsed.add(base);
chainCredentials.put(base, new SupplyChainValidation(
SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL,
AppraisalStatus.Status.PASS, certsUsed, ""));
certsUsed.clear();
certsUsed.add(delta1);
chainCredentials.put(delta1, new SupplyChainValidation(
SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL,
AppraisalStatus.Status.PASS, certsUsed, ""));
certsUsed.clear();
certsUsed.add(delta2);
chainCredentials.put(delta2, new SupplyChainValidation(
SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL,
AppraisalStatus.Status.PASS, certsUsed, ""));
AppraisalStatus result = supplyChainCredentialValidator
.validateDeltaPlatformCredentialAttributes(delta2,
@ -2185,9 +2201,17 @@ public class SupplyChainCredentialValidatorTest {
when(base.getComponentIdentifiers()).thenReturn(compList);
when(delta1.getComponentIdentifiers()).thenReturn(delta1List);
List<PlatformCredential> chainCredentials = new ArrayList<>(0);
chainCredentials.add(base);
chainCredentials.add(delta1);
Map<PlatformCredential, SupplyChainValidation> chainCredentials = new HashMap<>(0);
List<Certificate> certsUsed = new ArrayList<>();
certsUsed.add(base);
chainCredentials.put(base, new SupplyChainValidation(
SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL,
AppraisalStatus.Status.PASS, certsUsed, ""));
certsUsed.clear();
certsUsed.add(delta1);
chainCredentials.put(delta1, new SupplyChainValidation(
SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL,
AppraisalStatus.Status.PASS, certsUsed, ""));
AppraisalStatus result = supplyChainCredentialValidator
.validateDeltaPlatformCredentialAttributes(delta1,