From 7cfabe756dfd096cb24bf21adab02d545b5aa66d Mon Sep 17 00:00:00 2001 From: Cyrus <24922493+cyrus-dev@users.noreply.github.com> Date: Fri, 2 Aug 2019 09:41:44 -0400 Subject: [PATCH] [#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. --- .../SupplyChainValidationServiceImpl.java | 142 +++++++++--------- .../SupplyChainValidationServiceImplTest.java | 3 +- .../WEB-INF/jsp/certificate-details.jsp | 52 +++---- .../webapp/WEB-INF/jsp/validation-reports.jsp | 15 +- .../hirs/data/persist/AbstractEntity.java | 7 + .../hirs/data/persist/AppraisalResult.java | 2 - .../java/hirs/data/persist/NetworkInfo.java | 8 +- .../persist/SupplyChainValidationSummary.java | 30 +++- .../hirs/validation/CredentialValidator.java | 10 +- .../SupplyChainCredentialValidator.java | 107 ++++++++----- .../SupplyChainCredentialValidatorTest.java | 42 ++++-- 11 files changed, 245 insertions(+), 173 deletions(-) diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/service/SupplyChainValidationServiceImpl.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/service/SupplyChainValidationServiceImpl.java index 4feba80d..6b1bc935 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/service/SupplyChainValidationServiceImpl.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/service/SupplyChainValidationServiceImpl.java @@ -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 credentialMap = new HashMap<>(); PlatformCredential baseCredential = null; List validations = new LinkedList<>(); - List deltaValidations = new LinkedList<>(); - - // validate all supply chain pieces. Potentially, a policy setting could be made - // to dictate stopping after the first validation failure. + Map validationTypeMap = new HashMap<>(); + Map 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 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 chainCertificates = PlatformCredential - .select(certificateManager) - .byBoardSerialNumber(pc.getPlatformSerial()) - .getCertificates().stream().collect(Collectors.toList()); - Collections.sort(chainCertificates, - new Comparator() { - @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 deltaValidations, + final Map 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 chainCertificates = PlatformCredential - .select(certificateManager) - .byBoardSerialNumber(pc.getPlatformSerial()) - .getCertificates().stream().collect(Collectors.toList()); + // Grab all certs associated with this platform chain + List chainCertificates = PlatformCredential + .select(certificateManager) + .byBoardSerialNumber(pc.getPlatformSerial()) + .getCertificates().stream().collect(Collectors.toList()); + Collections.sort(chainCertificates, + new Comparator() { + @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 chainCertificates) { + final Map 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, diff --git a/HIRS_AttestationCA/src/test/java/hirs/attestationca/service/SupplyChainValidationServiceImplTest.java b/HIRS_AttestationCA/src/test/java/hirs/attestationca/service/SupplyChainValidationServiceImplTest.java index 5b2633b5..5b3f32cd 100644 --- a/HIRS_AttestationCA/src/test/java/hirs/attestationca/service/SupplyChainValidationServiceImplTest.java +++ b/HIRS_AttestationCA/src/test/java/hirs/attestationca/service/SupplyChainValidationServiceImplTest.java @@ -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 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, diff --git a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/certificate-details.jsp b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/certificate-details.jsp index 21a57ea5..7d9cc152 100644 --- a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/certificate-details.jsp +++ b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/certificate-details.jsp @@ -616,12 +616,12 @@
- - ${component.getComponentClass()} - - - Platform Components - + + ${component.getComponentClass()} + + + Platform Components +
@@ -649,22 +649,22 @@ Irreplaceable
- - - Platform Certificate Issuer: - ${component.getCertificateIdentifier().getIssuerDN()}
- Platform Certificate Serial Number: - ${component.getCertificateIdentifier().getCertificateSerialNumber()}
- Platform Certificate URI: -
- - - ${component.getComponentPlatformUri().getUniformResourceIdentifier()} - -
- Status: - ${component.getAttributeStatus()}
+ + + Platform Certificate Issuer: + ${component.getCertificateIdentifier().getIssuerDN()}
+ Platform Certificate Serial Number: + ${component.getCertificateIdentifier().getCertificateSerialNumber()}
+ Platform Certificate URI:
+ + + ${component.getComponentPlatformUri().getUniformResourceIdentifier()} + +
+ Status: + ${component.getAttributeStatus()}
+
@@ -825,11 +825,11 @@ $("#authorityKeyIdentifier").html("Not Specified"); - - - //Convert string to serial String - $("#authSerialNumber").html(parseSerialNumber(authoritySerialNumber)); - + + + //Convert string to serial String + $("#authSerialNumber").html(parseSerialNumber(authoritySerialNumber)); + var publicKey = '${initialData.publicKeyValue}'; diff --git a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/validation-reports.jsp b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/validation-reports.jsp index 9d07a4ad..012d6352 100644 --- a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/validation-reports.jsp +++ b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/validation-reports.jsp @@ -32,12 +32,12 @@ Result Timestamp Device - Credential Validations + Credential Validations - Endorsement - Platform - Platform Attributes + Endorsement + Platform + @@ -55,16 +55,17 @@ // create status icon var result = full.overallValidationResult; + var ovallMessage = full.message; if (result) { switch (result) { case "PASS": html += ''; break; case "FAIL": - html += ''; + html += ''; break; case "ERROR": - html += ''; + html += ''; break; default: html += unknownStatus; @@ -161,7 +162,7 @@ html += '' + + '&type=' + certType + '">'; } switch (curResult) { diff --git a/HIRS_Utils/src/main/java/hirs/data/persist/AbstractEntity.java b/HIRS_Utils/src/main/java/hirs/data/persist/AbstractEntity.java index e57a400e..4f04e042 100644 --- a/HIRS_Utils/src/main/java/hirs/data/persist/AbstractEntity.java +++ b/HIRS_Utils/src/main/java/hirs/data/persist/AbstractEntity.java @@ -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") diff --git a/HIRS_Utils/src/main/java/hirs/data/persist/AppraisalResult.java b/HIRS_Utils/src/main/java/hirs/data/persist/AppraisalResult.java index fad1b931..9e8d78b3 100644 --- a/HIRS_Utils/src/main/java/hirs/data/persist/AppraisalResult.java +++ b/HIRS_Utils/src/main/java/hirs/data/persist/AppraisalResult.java @@ -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 Appraiser. Can be NULL. */ diff --git a/HIRS_Utils/src/main/java/hirs/data/persist/NetworkInfo.java b/HIRS_Utils/src/main/java/hirs/data/persist/NetworkInfo.java index 78496c01..d1f15c24 100644 --- a/HIRS_Utils/src/main/java/hirs/data/persist/NetworkInfo.java +++ b/HIRS_Utils/src/main/java/hirs/data/persist/NetworkInfo.java @@ -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; diff --git a/HIRS_Utils/src/main/java/hirs/data/persist/SupplyChainValidationSummary.java b/HIRS_Utils/src/main/java/hirs/data/persist/SupplyChainValidationSummary.java index d60c618b..c0a69489 100644 --- a/HIRS_Utils/src/main/java/hirs/data/persist/SupplyChainValidationSummary.java +++ b/HIRS_Utils/src/main/java/hirs/data/persist/SupplyChainValidationSummary.java @@ -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 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 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); } } diff --git a/HIRS_Utils/src/main/java/hirs/validation/CredentialValidator.java b/HIRS_Utils/src/main/java/hirs/validation/CredentialValidator.java index 4f40e93b..df8048e7 100644 --- a/HIRS_Utils/src/main/java/hirs/validation/CredentialValidator.java +++ b/HIRS_Utils/src/main/java/hirs/validation/CredentialValidator.java @@ -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 chainCertificates); + Map deltaMapping); /** * Checks if the endorsement credential is valid. * diff --git a/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java b/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java index 32e665cd..3fe4309f 100644 --- a/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java +++ b/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java @@ -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 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 chainCertificates) { + final Map 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 leafChain = new LinkedList<>(); - List 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 leafMap = new HashMap<>(); - leafChain.stream().forEach((delta) -> { - leafMap.put(delta.getHolderSerialNumber().toString(), delta); - }); + List 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 leafChain, + final Map deltaMapping, final List origPcComponents) { boolean fieldValidation = true; StringBuilder resultMessage = new StringBuilder(); - StringBuilder deltaMessage = new StringBuilder(); List validOrigPcComponents = origPcComponents.stream() .filter(identifier -> identifier.getComponentManufacturer() != null && identifier.getComponentModel() != null) @@ -587,9 +568,20 @@ public final class SupplyChainCredentialValidator implements CredentialValidator }); String ciSerial; + List 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 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 getDeltaFailures() { + return Collections.unmodifiableMap(DELTA_FAILURES); + } } diff --git a/HIRS_Utils/src/test/java/hirs/validation/SupplyChainCredentialValidatorTest.java b/HIRS_Utils/src/test/java/hirs/validation/SupplyChainCredentialValidatorTest.java index 134ac8b6..7d7e1c8a 100644 --- a/HIRS_Utils/src/test/java/hirs/validation/SupplyChainCredentialValidatorTest.java +++ b/HIRS_Utils/src/test/java/hirs/validation/SupplyChainCredentialValidatorTest.java @@ -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 chainCredentials = new ArrayList<>(0); - chainCredentials.add(base); - chainCredentials.add(delta1); - chainCredentials.add(delta2); + Map chainCredentials = new HashMap<>(0); + List 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 chainCredentials = new ArrayList<>(0); - chainCredentials.add(base); - chainCredentials.add(delta1); + Map chainCredentials = new HashMap<>(0); + List 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,